CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-rollup

Next-generation ES module bundler that compiles small pieces of code into larger, more complex applications or libraries

Pending
Overview
Eval results
Files

watch-mode.mddocs/

Watch Mode

Development-focused file watching system that automatically rebuilds when source files change, with comprehensive event handling and error recovery. Rollup's watch mode provides efficient incremental builds and detailed progress reporting.

Capabilities

Watch Function

Main watch function that creates a file watcher for automatic rebuilding during development.

/**
 * Creates a file watcher for automatic rebuilding
 * @param configs - Watch configuration(s)
 * @returns RollupWatcher event emitter
 */
function watch(configs: RollupWatchOptions | RollupWatchOptions[]): RollupWatcher;

interface RollupWatchOptions extends InputOptions {
  /** Output configuration */
  output?: OutputOptions | OutputOptions[];
  /** Watch-specific options */
  watch?: WatcherOptions | false;
}

Usage Examples:

import { watch } from "rollup";

// Basic watch setup
const watcher = watch({
  input: "src/main.js",
  output: {
    file: "dist/bundle.js",
    format: "esm"
  }
});

// Watch with specific options
const watcher = watch({
  input: "src/main.js",
  output: {
    file: "dist/bundle.js",
    format: "esm"
  },
  watch: {
    exclude: "node_modules/**",
    clearScreen: false
  }
});

// Multiple configurations
const watcher = watch([
  {
    input: "src/main.js",
    output: { file: "dist/esm.js", format: "esm" }
  },
  {
    input: "src/main.js",
    output: { file: "dist/cjs.js", format: "cjs" }
  }
]);

RollupWatcher Interface

Event emitter interface for handling watch events and controlling the watcher lifecycle.

/**
 * Watcher instance for rebuild-on-change functionality
 */
interface RollupWatcher extends AwaitingEventEmitter<{
  change: (id: string, change: { event: ChangeEvent }) => void;
  close: () => void;
  event: (event: RollupWatcherEvent) => void;
  restart: () => void;
}> {
  /** Close the watcher and stop watching */
  close(): Promise<void>;
  /** Emit an event to all listeners */
  emit<K extends keyof T>(event: K, ...parameters: Parameters<T[K]>): Promise<unknown>;
  /** Register an event listener */
  on<K extends keyof T>(event: K, listener: AwaitedEventListener<T, K>): this;
  /** Register a one-time event listener */
  onCurrentRun<K extends keyof T>(event: K, listener: AwaitedEventListener<T, K>): this;
  /** Remove an event listener */
  off<K extends keyof T>(event: K, listener: AwaitedEventListener<T, K>): this;
  /** Remove all listeners */
  removeAllListeners(): this;
  /** Remove listeners for current run only */
  removeListenersForCurrentRun(): this;
}

type ChangeEvent = 'create' | 'update' | 'delete';
type AwaitedEventListener<T extends Record<string, (...args: any) => any>, K extends keyof T> = 
  (...parameters: Parameters<T[K]>) => void | Promise<void>;

Event Handling

Comprehensive event handling for monitoring build progress and responding to file changes.

/**
 * Event types emitted by the watcher
 */
type RollupWatcherEvent = 
  | { code: 'START' }
  | { 
      code: 'BUNDLE_START';
      input?: InputOption;
      output: readonly string[];
    }
  | {
      code: 'BUNDLE_END';
      duration: number;
      input?: InputOption;
      output: readonly string[];
      result: RollupBuild;
    }
  | { code: 'END' }
  | { 
      code: 'ERROR';
      error: RollupError;
      result: RollupBuild | null;
    };

Usage Examples:

import { watch } from "rollup";

const watcher = watch(config);

// Handle build events
watcher.on('event', (event) => {
  switch (event.code) {
    case 'START':
      console.log('Starting build...');
      break;
    case 'BUNDLE_START':
      console.log(`Building ${event.input}...`);
      break;
    case 'BUNDLE_END':
      console.log(`Built in ${event.duration}ms`);
      break;
    case 'END':
      console.log('Build complete');
      break;
    case 'ERROR':
      console.error('Build error:', event.error);
      break;
  }
});

// Handle file changes
watcher.on('change', (id, { event }) => {
  console.log(`File ${event}: ${id}`);
});

// Graceful shutdown
process.on('SIGINT', async () => {
  await watcher.close();
  process.exit(0);
});

Watcher Control

Methods for controlling watcher behavior and lifecycle.

/**
 * Close the watcher and stop watching files
 * @returns Promise that resolves when watcher is closed
 */
close(): Promise<void>;

/**
 * Restart the watcher with current configuration
 */
restart(): void;

Usage Examples:

// Programmatic control
const watcher = watch(config);

// Restart on command
process.on('SIGUSR2', () => {
  console.log('Restarting watcher...');
  watcher.restart();
});

// Close watcher
setTimeout(async () => {
  console.log('Closing watcher...');
  await watcher.close();
}, 30000);

Watch Configuration

WatcherOptions

Detailed configuration options for controlling watch behavior.

interface WatcherOptions {
  /** Allow input files inside output directory */
  allowInputInsideOutputPath?: boolean;
  /** Debounce delay for rebuilds (ms) */
  buildDelay?: number;
  /** Chokidar file watcher options */
  chokidar?: ChokidarOptions;
  /** Clear screen on each rebuild */
  clearScreen?: boolean;
  /** Files/patterns to exclude from watching */
  exclude?: string | RegExp | (string | RegExp)[];
  /** Files/patterns to include in watching */
  include?: string | RegExp | (string | RegExp)[];
  /** Skip writing files (generate only) */
  skipWrite?: boolean;
  /** Callback when files are invalidated */
  onInvalidate?: (id: string) => void;
}

Chokidar Integration

Advanced file watching options via Chokidar integration.

interface ChokidarOptions {
  /** Files/paths to ignore */
  ignored?: any;
  /** Don't emit events for initially added files */
  ignoreInitial?: boolean;
  /** Follow symbolic links */
  followSymlinks?: boolean;
  /** Base directory for relative paths */
  cwd?: string;
  /** Disable globbing */
  disableGlobbing?: boolean;
  /** Use polling instead of native events */
  usePolling?: boolean;
  /** Polling interval (ms) */
  interval?: number;
  /** Binary file polling interval (ms) */
  binaryInterval?: number;
  /** Always stat files */
  alwaysStat?: boolean;
  /** Maximum depth for recursive watching */
  depth?: number;
  /** Wait for write completion */
  awaitWriteFinish?: boolean | {
    /** Stability threshold (ms) */
    stabilityThreshold?: number;
    /** Poll interval (ms) */
    pollInterval?: number;
  };
  /** Ignore permission errors */
  ignorePermissionErrors?: boolean;
  /** Atomic file moves */
  atomic?: boolean | number;
  /** Keep process alive */
  persistent?: boolean;
  /** Use fsevents on macOS */
  useFsEvents?: boolean;
}

Advanced Watch Patterns

Development Server Integration

import { watch } from "rollup";
import express from "express";

const app = express();
const watcher = watch({
  input: "src/main.js",
  output: {
    file: "dist/bundle.js",
    format: "esm"
  },
  watch: {
    clearScreen: false,
    exclude: ["node_modules/**", "dist/**"]
  }
});

// Serve files
app.use(express.static("dist"));

// Handle build events
watcher.on('event', (event) => {
  if (event.code === 'BUNDLE_END') {
    console.log('✓ Build complete, files updated');
  } else if (event.code === 'ERROR') {
    console.error('✗ Build error:', event.error.message);
  }
});

app.listen(3000, () => {
  console.log('Dev server running on http://localhost:3000');
});

Conditional Rebuilding

import { watch } from "rollup";

const watcher = watch({
  input: "src/main.js",
  output: {
    file: "dist/bundle.js",
    format: "esm"
  },
  watch: {
    buildDelay: 1000, // Wait 1s before rebuilding
    exclude: [
      "node_modules/**",
      "**/*.test.js",
      "docs/**"
    ]
  }
});

// Custom file change handling
watcher.on('change', (id, { event }) => {
  if (id.includes('.test.')) {
    console.log('Test file changed, skipping rebuild');
    return;
  }
  
  if (event === 'delete') {
    console.log(`File deleted: ${id}`);
  } else {
    console.log(`File ${event}: ${id}`);
  }
});

// Error recovery
watcher.on('event', (event) => {
  if (event.code === 'ERROR') {
    console.error('Build failed:', event.error.message);
    console.log('Waiting for file changes to retry...');
  }
});

Multi-Config Watch

import { watch } from "rollup";

// Watch multiple build targets
const watcher = watch([
  // Main application
  {
    input: "src/main.js",
    output: {
      file: "dist/app.js",
      format: "esm"
    },
    watch: {
      include: "src/**"
    }
  },
  // Service worker
  {
    input: "src/sw.js",
    output: {
      file: "dist/sw.js",
      format: "iife"
    },
    watch: {
      include: "src/sw/**"
    }
  },
  // Styles (with different plugin)
  {
    input: "src/styles/main.css",
    output: {
      file: "dist/styles.css",
      format: "es"
    },
    plugins: [postcss()],
    watch: {
      include: "src/styles/**"
    }
  }
]);

// Track which configs are building
const buildingConfigs = new Set();

watcher.on('event', (event) => {
  switch (event.code) {
    case 'BUNDLE_START':
      const input = Array.isArray(event.input) ? event.input[0] : event.input;
      buildingConfigs.add(input);
      console.log(`Building ${input}... (${buildingConfigs.size} active)`);
      break;
    
    case 'BUNDLE_END':
      const builtInput = Array.isArray(event.input) ? event.input[0] : event.input;
      buildingConfigs.delete(builtInput);
      console.log(`✓ Built ${builtInput} in ${event.duration}ms`);
      break;
  }
});

Install with Tessl CLI

npx tessl i tessl/npm-rollup

docs

configuration.md

core-bundling.md

index.md

plugin-system.md

utilities.md

watch-mode.md

tile.json