rollup/rollup
github.com/rollup/rollup ↗ · 4000 files · 3.2 MB · scan capped
Rollup Overview Rollup is a module bundler for JavaScript which compiles small pieces of code into something larger and more complex, such as a library or application.
Overview
Rollup 4.63.6 bundles JavaScript modules into distributable libraries and applications through a CLI and a programmatic API. Its TypeScript core builds a dependency graph, removes unused code, and renders multiple module formats; Rust supplies parsing and hashing through native Node bindings or WebAssembly. This brief examines commit a77225f4b9ef88f20d26a32a3dc70954241f3fb6 through bounded source inspection, including CLI builds, API output generation, and watch rebuilds.
System map
Rollup exposes a Node API, a CLI, and a browser API around the same bundling core. Graph owns build state, while Bundle owns one output generation. Parsing and hashing cross a Rust boundary with separate native and WASM adapters. Distribution consists of npm artifacts and platform binaries rather than a running service.
- The Node entry exports VERSION, rollup, defineConfig, and watch; the browser entry exports the first three and omits watch.
- cli/cli.ts parses argv with yargs-parser and commandAliases, handles help/version, and delegates other invocations to run.
- Graph constructs PluginDriver, ModuleLoader, and a maxParallelFileOps queue; its modulesById map holds internal and external modules.
- Each generate/write invocation creates a new Bundle around the existing Graph, allowing output generation without rebuilding the input graph.
- Rust's parse_ast crate uses SWC parsing and AstConverter; NAPI and wasm-bindgen expose parser and xxhash operations to JavaScript.
- native.js selects a platform/architecture package, distinguishes musl where supported, and loads a local .node binary or @rollup/rollup-${packageBase}.
- rollup.config.ts produces CommonJS and ESM Node artifacts plus UMD and ESM browser artifacts; configTest selects only CommonJS and configIsBuildNode selects both Node builds.
- npm run build combines WASM, generated AST converters, release NAPI, JavaScript, and native copying; CI publication depends on lint/tests and gates on v4 refs plus release-shaped commit messages.
Feature catalog
The principal features are module bundling, tree-shaking, configurable chunking, and output generation. Users can invoke them through configuration files, CLI arguments, or the JavaScript API. Plugins supply resolution, transformation, virtual modules, and emitted assets. Watch mode and browser execution expose different environment capabilities.
- The build API exposes generate, write, cache, watchFiles, close, and asynchronous disposal; perf-enabled builds also expose getTimings.
- Output finalisers support AMD, CommonJS, ES modules, IIFE, SystemJS, and UMD.
- Tree-shaking iterates module inclusion until no additional pass is requested; disabled tree-shaking includes all modules, and no-treeshake modules bypass selective inclusion.
- Chunk generation supports inlineDynamicImports, preserveModules, automatic assignments, and manualChunks; manual assignment can use module introspection.
- The CLI supports configuration arrays, sequential builds, environment-variable assignments, watch mode, and failAfterWarnings.
- CLI stdin is represented by '-' or a suffixed virtual ID; builds without an output destination generate to stdout and allow only inline sourcemaps there.
- The browser package can obtain modules through resolveId/load plugins or an injected filesystem; its WASM artifact must be served beside the JavaScript bundle.
- Output options expose asset/chunk naming, content-hash alphabets, sourcemaps, source exclusion, ignore-list callbacks, and sourcemap path transformation.
Data flow
Input options become a normalized build configuration and then a graph of resolved modules. Loaded source passes through transform hooks before parsing and inclusion analysis. Rendering constructs output chunks, resolves content hashes, and returns files or writes them through a filesystem adapter. Incremental state is represented by module and plugin caches retained by callers or watch tasks.
- getInputOptions first processes plugin options hooks, then normalizes input options and assigns anonymous plugin names.
- resolveId asks plugins first; fallback resolution handles entry/relative/absolute paths, tries .mjs and .js, and optionally dereferences symlinks.
- addModuleSource obtains the first non-null load result or reads UTF-8 through options.fs, validates its code shape, and strips the BOM.
- transform reduces source through transform hooks while recording sourcemaps, watched transform dependencies, emitted files, and custom-cache usage.
- Module.setSource stores code and maps, initializes MagicString and AST context, then uses a provided AST or parseAsync plus convertProgram.
- ModuleLoader recursively fetches static/dynamic dependencies, links imports, and awaits moduleParsed; Graph then orders modules, binds references, and includes statements.
- renderChunks renders chunks, transforms them and obtains content-hash inputs, computes final dependency-sensitive hashes, and adds resulting chunks to the output bundle.
- Graph.getCache serializes modules and plugin cache entries with expiry eviction; writeOutputFile resolves the destination and persists asset sources or chunk code through injected mkdir/writeFile.
Behavior traces
Three concrete flows were followed through their implementation: CLI configuration builds, API build-to-output generation, and watch invalidation-to-rebuild. The CLI adds executable configuration loading and stdout handling around the API. The API separates graph construction from rendering and disk writes. Watch mode retains cache and coordinates asynchronous change notifications before another build.
- CLI startup runs argParser → runRollup → getConfigs; config mode resolves a configuration path and calls loadConfigFile, otherwise it loads options from the command.
- Configuration loading runs getConfigFileExport → getConfigList → mergeOptions → addCommandPluginsToInputOptions; exports may be objects, arrays, promises, or functions receiving command options.
- With configPlugin or bundleConfigAsCjs, configuration loading builds the config → generates ES/CJS code → writes a sibling temporary config → imports its default export → schedules unlink in finally.
- The CLI builds configurations sequentially; each build obtains an await-using bundle, generates the first output to stdout when destinationless, or writes configured outputs concurrently.
- API startup runs rollup → rollupInternal → getInputOptions → initWasm → Graph construction → buildStart → Graph.build → buildEnd; failures call buildEnd with the error and closeBundle.
- Output generation runs handleGenerateWrite → output option/plugin-driver setup → Bundle.generate → renderStart → generateChunks → renderChunks → generateBundle → asset finalization → filename validation.
- write additionally requires file or dir, queues writeOutputFile for generated files, and runs writeBundle after writes resolve; generate returns without this persistence branch.
- Watch startup merges/filter configs and constructs Watcher; filesystem events run Task.invalidate → Watcher.invalidate → pending-change announcements → restart → Task.run → rollupInternal → optional output writes → BUNDLE_END.
Testing signals
Tests and CI were inspected, not executed; their existence establishes intended checks, not passing results. Mocha suites exercise built artifacts through runtime assertions, output comparisons, cache reruns, CLI invocation, and filesystem watching. Browser tests use Node substitutes rather than an actual browser. Inspection was bounded and does not establish exhaustive coverage or whole-repository absences.
- test:only invokes Mocha on test/test.js, which loads misc, function, form, chunking, hashes, sourcemaps, incremental, hooks, config, CLI, and watch suites.
- Function tests import dist/rollup, generate CommonJS, execute outputs with injected assert, and support build/generation/runtime error expectations.
- Form tests compare generated output across six formats and rerun from bundle.cache; repository guidance distinguishes them from tests that execute generated code.
- Browser tests load browser/dist/rollup.browser.js under Node, mock fetch with filesystem reads, and compare output fixtures; they do not run in an actual browser.
- Watch tests assert rebuild event sequences and output values, reject overlapping asynchronous listener handling, and check closeWatcher after a failed run with a pending rerun.
- test:all includes browser, TypeScript, package, and option checks; ci:coverage adds leak checks and covered Mocha execution alongside browser/type checks.
- CI configures Node 18.20.0 and 24 across selected Linux/macOS/Windows targets, excluding Node 18 on Windows ARM; it tests tarball installation and runs test:only.
- CI checks generated converter drift and uploads JavaScript/Rust coverage; a final failure step preserves test failure even though the coverage execution step uses continue-on-error.
Risk map
The inspected security-sensitive boundaries are executable configuration/plugins, watch shell commands, filesystem access, native loading, and CI privilege transitions. These are source observations, not confirmed vulnerabilities. Filename validation provides a concrete output-path guard, while custom filesystem adapters determine actual I/O authority. Native/WASM parity and asynchronous watch ordering are broad correctness hotspots.
- CLI plugin arguments can execute new Function for inline plugin objects and argument expressions, then require/import packages; these inputs enter executable code paths.
- Configuration files execute through dynamic import; transpilation writes code beside the config and cleanup errors are logged rather than awaited or rethrown.
- CLI watch hooks execute user-supplied command strings with execSync; command failures are printed and suppressed.
- Fallback resolution accepts absolute paths and follows symlinks unless preserveSymlinks is set; loading and writing use configured filesystem methods without a repository-root confinement check in those functions.
- After generateBundle, Bundle validates bundle keys and differing entry filenames, rejecting normalized parent escapes, '.', and absolute paths before core writes.
- NAPI parseAsync accepts AbortSignal, but browser and Node-WASM adapters ignore it and call synchronous parse; lint-native-js checks export-name presence in only one direction.
- Hypothesis: changing watch running/rerun state or invalidation batching could lose changes or overlap asynchronous listeners; those exact boundaries have sequencing comments and dedicated regressions. (inferred)
- The performance workflow uses pull_request_target and executes PR-head code, including in a report job with pull-requests:write; REPL upload uses a separate workflow_run job with OIDC and resolves PR identity through GitHub API.
Extension points
Plugins are the primary public extension mechanism and receive graph, parsing, filesystem, cache, and file-emission capabilities. Hook execution categories and ordering are explicit contracts. Internal extension surfaces include option normalization, finalisers, AST schemas, and browser replacement registries. New native functions require coordinated adapters across environments.
- Plugins are objects with properties and build/output hooks; documented examples implement resolveId and load to supply virtual modules.
- PluginDriver distinguishes first-result hooks, sequential argument reduction, and parallel hooks; sequential-marked parallel hooks wait for preceding parallel work.
- Hook descriptors support pre/post ordering; validated plugins are returned as pre, normal, then post groups.
- PluginContext exposes addWatchFile, cache, fs, emitFile, getFileName, module introspection, load, parse, resolve, setAssetSource, and structured logging.
- Output plugins are appended to the base driver's plugins with shared emission state; input hooks in output-only plugins produce warnings.
- Input fs is an injectable capability used by core I/O and exposed as this.fs; browser defaults are throwing stubs checked against RollupFsModule.
- scripts/ast-types.js defines generated Rust constants, JSON decoders, internal AST parsers, and child keys; format finalisers have a separate shared function interface and registry.
- Browser substitutions are registered in JS_REPLACED_MODULES and wasmModulesMap; additional binding functions must synchronize both Rust crates and all three JavaScript handover points.
Contributor handoff
Start with architecture and scoped instructions, then follow the affected phase and environment adapters. Preserve generated-file ownership and conservative tree-shaking contracts. Tests and CI were inspected, not executed, and this bounded brief supplies no passing-test or coverage guarantee. The commands below describe future authorized implementation checks; none were run during this audit.
- Read AGENTS.md and scoped src/rust/test instructions before changes; production code uses descriptive names, localized behavior, and short comments only for non-obvious information.
- Respect LOAD_AND_PARSE → ANALYSE → GENERATE layering; AST nodes access module capabilities through astContext, and user-facing diagnostics use log factories plus URL constants.
- Tree-shaking changes must retain pessimistic defaults, schedule new passes after invalidating inclusion decisions, and invalidate memoized dependent values after reassignment.
- Edit AST schema/generators instead of generated decoders; preserve buffer alignment, word-index references, endianness, UTF-16 positions, and error-node protocols.
- Verify browser capability changes against code: custom fs is supported despite the narrower ARCHITECTURE.md description; default fs methods throw and replacements are explicitly registered.
- For future authorized checks, JavaScript changes use update:js and Rust changes use update:napi; build:quick builds NAPI plus CommonJS and omits browser/ESM artifacts needed by some tests.
- Prefer function regression samples for executable behavior, form for removed code, and chunking-form for multiple outputs; watch tests live in index.js, and new plugin tests should avoid the legacy hooks suite.
- Rebuild artifacts before testing, remove solo selections, and remember fixture runners change cwd; tests and CI here were inspected rather than executed, so configured checks provide no observed pass result.
Languages
| Language | Files | Share of source |
|---|---|---|
| JavaScript | 12408 | 96% |
| TypeScript | 381 | 3% |
| Rust | 113 | 1% |
| JSON | 92 | docs/config |
| Markdown | 68 | docs/config |
| HTML | 24 | docs/config |
| YAML | 15 | docs/config |
| Vue | 11 | 0% |
Start reading here
Probable source entrypoint: src/ — inferred from the directory tree and manifest, not the upstream docs.
Source roots
src/ (274 files)
Repository structure
| Top-level path | Files |
|---|---|
test/ | 3320 |
src/ | 274 |
rust/ | 124 |
docs/ | 97 |
npm/ | 50 |
scripts/ | 32 |
.github/ | 18 |
cli/ | 17 |
Docs found: AGENTS.mdARCHITECTURE.mdCLAUDE.mdCONTRIBUTING.mdLICENSE.mdREADME.mddocs/AGENTS.mddocs/CLAUDE.mddocs/browser/index.mddocs/command-line-interface/index.md
Package
package.json — rollup v4.63.6, 80 declared dependencies.
@codemirror/commands@codemirror/lang-javascript@codemirror/language@codemirror/search@codemirror/state@codemirror/view@emnapi/core@emnapi/runtime@eslint/js@inquirer/prompts@jridgewell/sourcemap-codec@mermaid-js/mermaid-cli
CI & config entrypoints
README.md— Repository readmeAGENTS.md— Agent instructionsCLAUDE.md— Claude instructions.github/workflows/build-and-tests.yml— GitHub Actions workflow.github/workflows/clean-cache.yml— GitHub Actions workflow.github/workflows/copilot-review.yml— GitHub Actions workflow.github/workflows/performance-report.yml— GitHub Actions workflow.github/workflows/repl-artefacts-upload.yml— GitHub Actions workflow.github/workflows/repl-artefacts.yml— GitHub Actions workflowpackage.json— npm scripts: build, build:ast-converters, build:bootstrap, build:bootstrap:cjs, build:cjs, build:copy-native, build:docs, build:js
Structural health signals
Average score 7.7/10 across 96 analyzed source files; 4 hotspots surfaced.
scripts/ast-types.js— 783 lines, score 5.5 (Large file, Deep nesting)src/Chunk.ts— 1640 lines, score 6.2 (Large file, Long function/block)src/Module.ts— 1519 lines, score 6.2 (Large file, Long function/block)
These are deterministic review leads — size, churn, and structural signals — not proof of a bug in this project.
Recent history
a77225f4b9ef2026-10-02 — Update msys2/setup-msys2 digest to ec48f7c (#6544)ec48a7d87be22026-10-02 — Update dependency @mermaid-js/mermaid-cli to v12 (#6546)06afd977bac42026-10-02 — Update Rust crate swc_compiler_base to v65 (#6547)06f64263b5562026-10-01 — Update minor/patch updates (#6545)f2e3c9d8febf2026-10-01 — Run additional tests as part of the coverage job (#6542)