Jesse A. Green / notebook

Live demo: the DocCompiler guide library as a static, read-only snapshot (October 2026). No backend, no accounts, nothing to submit.

Parcel Admin & Architecture Guide

Generated June 2026 from parcel-bundler/parcel at 5948485

AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it.

Audience: architects, platform engineers, plugin authors, and contributors who need to understand how Parcel is built, configure it deeply, embed it, operate it at scale, or extend it. For day-to-day usage and the full CLI option reference, see the User Guide.

Note

This guide documents Parcel v2.16.4 (parcel on npm). It is a Yarn-workspaces monorepo of ~80 published packages plus a set of Rust crates that provide native-speed implementations of the hottest code paths. Minimum runtime: Node.js ≥ 16.


Table of Contents

  1. Executive Summary
  2. Architecture Overview
  3. Repository Structure
  4. The Plugin Pipeline
  5. The Build Orchestration Model
  6. System Requirements
  7. Configuration Reference
  8. The Plugin System (Authoring)
  9. The Native (Rust) Layer
  10. Caching & Incrementality
  11. Concurrency: The Worker Farm
  12. Programmatic API & Embedding
  13. Security Analysis
  14. Operations & Observability
  15. Troubleshooting (Operational)
  16. Code Quality & Conventions
  17. Potential Improvements
  18. Contributing
  19. Quick Reference

1. Executive Summary

Parcel is a web application bundler whose defining trait is zero configuration: a single default config (@parcel/config-default) maps every supported file type to a compiler, so most projects ship without a config file. Its architecture is a plugin pipeline — resolvers, transformers, bundlers, namers, runtimes, packagers, optimizers, compressors, reporters, and validators — orchestrated by a content-addressable request graph that makes rebuilds incremental and cache-persistent across restarts.

Three engineering decisions define the system:

  • Plugin pipeline over a monolith. Every stage of the build is a swappable plugin selected by glob in .parcelrc. The core orchestrates; the plugins do the work.
  • Native hot paths in Rust. The JavaScript/TypeScript compiler (SWC), the HTML transformer, and the module resolver are implemented in Rust and exposed to Node via N-API bindings.
  • Aggressive, persistent caching with parallelism. A request graph tracks fine-grained dependencies; a worker farm fans transformations across all CPU cores; results persist in .parcel-cache (LMDB or filesystem).
Attribute Value
Package name parcel
Version 2.16.4
License MIT
Language(s) JavaScript (Flow-typed), Rust
Runtime Node.js ≥ 16
Repo model Yarn 4 workspaces monorepo (packages/*/*) + Cargo workspace (crates/*)
Release tooling Lerna
CLI entry parcel → packages/core/parcel/src/bin.js → cli.js
Core engine @parcel/core → packages/core/core/src/Parcel.js

2. Architecture Overview

The asset pipeline

                    ┌─────────────────────────────────────────────────────────┐
 entry points  ───► │  RESOLVER(s)      specifier  →  file path                │
                    │  TRANSFORMER(s)   compile each asset (per file-type)     │
                    │  BUNDLER          group assets into bundles              │
                    │  NAMER(s)         assign output filenames                │
                    │  RUNTIME(s)       inject runtime code (HMR, loaders, …)  │
                    │  PACKAGER(s)      combine a bundle's assets into output  │
                    │  OPTIMIZER(s)     minify / transform final output        │
                    │  COMPRESSOR(s)    gzip/brotli/raw variants               │
                    └─────────────────────────────────────────────────────────┘
                                          │
                                          ▼
                              dist/  +  REPORTER(s) (dev server, CLI, tracer)
                                                     VALIDATOR(s) run in parallel (type-check, lint)

Each stage is a plugin category. The default mapping of file globs to plugins lives in packages/configs/default/index.json (see §4).

The orchestration core (@parcel/core)

The Parcel class (packages/core/core/src/Parcel.js) is the conductor. It does not transform files itself; instead it:

  1. Resolves options (resolveOptions.js) and loads the merged config (loadParcelConfig.js).
  2. Starts a worker farm and a shared options reference.
  3. Opens the cache and initializes a RequestTracker.
  4. Runs a ParcelBuildRequest through the request tracker, producing a BundleGraph.
  5. Emits build events to reporters (the dev server and CLI are reporters).

Graph data structures (@parcel/graph)

Parcel models the entire build as graphs. The packages/core/graph/src package provides the primitives:

Structure Role
Graph / ContentGraph Generic directed graph with typed edges; content-keyed nodes
AdjacencyList Cache-friendly, compact edge storage (typed-array backed)
Asset graph All source assets and their dependencies
Bundle graph The asset graph grouped into bundles and their relationships
Request graph The dependency graph of build requests — the basis of incrementality

Note

The AdjacencyList is a hand-optimized, typed-array-backed edge store (see docs/AdjacencyList.md in the repo). It exists because the build graphs can contain hundreds of thousands of edges; a naive object-of-arrays representation would dominate memory and GC time.

Native acceleration (Rust)

Performance-critical work is delegated to Rust crates compiled to native Node addons (and WASM for the browser REPL). See §9.


3. Repository Structure

Parcel is a monorepo. The Yarn workspaces glob is packages/*/*, so every package lives two levels deep under packages/<category>/<name>.

Path Category What lives here
packages/core/* Core The engine: core (orchestrator), parcel (CLI), graph, cache, fs, workers, package-manager, diagnostic, codeframe, logger, profiler, types/types-internal, plugin, register, feature-flags, utils
packages/resolvers/* Resolvers default, glob, repl-runtimes
packages/transformers/* Transformers js, babel, css, postcss, html, posthtml, sass, less, stylus, typescript-types, typescript-tsc, vue, elm, image, svg, json, toml, yaml, graphql, glsl, webextension, …
packages/bundlers/* Bundlers default, library
packages/namers/* Namers default
packages/runtimes/* Runtimes js, hmr, service-worker, webextension, rsc
packages/packagers/* Packagers js, css, html, svg, xml, ts, wasm, raw, raw-url, react-static, webextension
packages/optimizers/* Optimizers swc, css, cssnano, htmlnano, html, image, svg, svgo, terser, esbuild, data-url, blob-url, inline-requires
packages/compressors/* Compressors raw, gzip, brotli
packages/validators/* Validators typescript, eslint
packages/reporters/* Reporters cli, dev-server, dev-server-sw, json, tracer, bundle-analyzer, bundle-stats, bundle-buddy, build-metrics, lsp-reporter, sourcemap-visualiser
packages/configs/* Configs default (the zero-config .parcelrc), repl, webextension
packages/utils/* Utilities create-parcel, create-react-app, node-resolver-core, node-resolver-rs, parcel-lsp, babel-preset-env, rsc, error-overlay, …
packages/dev/* Dev tooling repl, parcel-link, bundle-stats-cli, babel-register, eslint-*, esm-fuzzer
packages/examples/* Examples Reference apps (html, react-hmr, react-server-components, ts-example, kitchen-sink, …)
crates/* Rust core, parcel-resolver, html, macros, node-bindings, dev-dep-resolver
packages/transformers/js/core Rust (SWC) The Rust source of the JS/TS transformer

Tip

When tracing a bug, start at the package that owns the stage where it manifests. A wrong output filename → namers/default; a broken minified JS bundle → optimizers/swc or packagers/js; a resolution failure → resolvers/default + crates/parcel-resolver.

Important

The JS source is type-checked with Flow (// @flow headers), not TypeScript. Run yarn check (flow check) when modifying core. Public plugin types are published as TypeScript declarations from @parcel/types.


4. The Plugin Pipeline

The zero-config behavior is entirely data: the default config (packages/configs/default/index.json) is a .parcelrc that wires every plugin. This is the single most important architecture artifact — it is the pipeline.

Default transformer map (by glob)

File glob Transformer chain
*.{js,mjs,jsm,jsx,es6,cjs,ts,tsx} @parcel/transformer-babel → @parcel/transformer-js → @parcel/transformer-react-refresh-wrap
*.mdx @parcel/transformer-mdx (if installed) → @parcel/transformer-js
*.{css,pcss} @parcel/transformer-postcss → @parcel/transformer-css
*.{sass,scss} @parcel/transformer-sass
*.less @parcel/transformer-less
*.{styl,stylus} @parcel/transformer-stylus
*.sss @parcel/transformer-sugarss
*.{htm,html,xhtml} @parcel/transformer-posthtml → @parcel/transformer-html
*.pug @parcel/transformer-pug
*.coffee @parcel/transformer-coffeescript
*.elm @parcel/transformer-elm
*.vue (+ template:/script:/style:/custom:) @parcel/transformer-vue
*.{json,json5} @parcel/transformer-json
*.jsonld @parcel/transformer-jsonld
*.toml @parcel/transformer-toml
*.{yaml,yml} @parcel/transformer-yaml
*.{glsl,vert,frag} @parcel/transformer-glsl
*.{gql,graphql} @parcel/transformer-graphql
*.{png,jpg,jpeg,webp,gif,tiff,avif,heic,heif} @parcel/transformer-image
*.svg @parcel/transformer-svg
*.{xml,rss,atom} @parcel/transformer-xml
*.{webmanifest} / webmanifest:* @parcel/transformer-webmanifest
*.node @parcel/transformer-node
types:*.{ts,tsx} @parcel/transformer-typescript-types
bundle-text:*, data-url:* (...) → @parcel/transformer-inline-string
worklet:*.{…} @parcel/transformer-worklet → (...)
react-static:* @parcel/transformer-react-static → (...)
url:* (...) → @parcel/transformer-raw

The "..." token means "splice in the default pipeline for this glob here" — it's how named pipelines extend rather than replace the defaults.

Default remaining stages

"namers":      ["@parcel/namer-default"],
"runtimes":    ["@parcel/runtime-rsc", "@parcel/runtime-js",
                "@parcel/runtime-browser-hmr", "@parcel/runtime-service-worker"],
"optimizers":  { "*.css": ["@parcel/optimizer-css"],
                 "*.{html,xhtml}": ["@parcel/optimizer-html"],
                 "*.{js,mjs,cjs}": ["@parcel/optimizer-swc"],
                 "*.svg": ["@parcel/optimizer-svg"],
                 "*.{jpg,jpeg,png}": ["@parcel/optimizer-image"],
                 "data-url:*": ["...", "@parcel/optimizer-data-url"] },
"packagers":   { "*.{html,xhtml}": "@parcel/packager-html", "*.css": "@parcel/packager-css",
                 "*.{js,mjs,cjs}": "@parcel/packager-js", "*.svg": "@parcel/packager-svg",
                 "*.{xml,rss,atom}": "@parcel/packager-xml", "*.ts": "@parcel/packager-ts",
                 "*.wasm": "@parcel/packager-wasm",
                 "react-static:*.html": "@parcel/packager-react-static",
                 "*.{jsonld,svg,webmanifest}": "@parcel/packager-raw-url", "*": "@parcel/packager-raw" },
"compressors": { "*": ["@parcel/compressor-raw"] },
"resolvers":   ["@parcel/resolver-default"],
"reporters":   ["@parcel/reporter-dev-server"]

Customizing the pipeline

Create a .parcelrc in your project root that extends the default and adds/overrides only what you need:

{
  "extends": "@parcel/config-default",
  "transformers": {
    "*.{md}": ["my-markdown-transformer"]
  },
  "reporters": ["...", "@parcel/reporter-bundle-analyzer"]
}

Caution

Omitting "extends": "@parcel/config-default" replaces the entire zero-config pipeline with whatever you list — almost never what you want. Always extend the default unless you are deliberately building a fully custom pipeline.

The CLI flags --config <path> and --reporter <name> let you point at an alternate config or add a reporter for a single run without editing .parcelrc.


5. The Build Orchestration Model

Lifecycle of Parcel.run()

packages/core/core/src/Parcel.js:

  1. _init() (lazy, runs once): init sourcemaps & Rust → resolveOptions() → loadParcelConfig() → create/adopt WorkerFarm → cache.ensure() → create a shared options reference → build the ReporterRunner → RequestTracker.init().
  2. _build(): enable profiling/tracing if requested → emit buildStart → invalidate build-time nodes → create a ParcelBuildRequest → requestTracker.runRequest(request, {force: true}) → receive {bundleGraph, bundleInfo, changedAssets, assetRequests} → assemble a buildSuccess event carrying a BundleGraph<IPackagedBundle>.
  3. _end(): requestTracker.writeToCache() then dispose resources.
  4. On buildFailure, throw a BuildError carrying diagnostics.

Watch mode

watch(cb) initializes once, subscribes to the filesystem watcher, kicks off a first build through a single-concurrency PromiseQueue, and returns an AsyncSubscription. Each change aborts the in-flight build via an AbortController and starts the next. unsubscribe() tears down the watcher, runs watchEnd, and ends the instance when the last watcher detaches.

Incrementality via the request graph

The RequestTracker records every build request (resolve, transform, bundle, package, …) and the file/option/env invalidations each depends on. On the next run it re-executes only requests whose inputs changed. Because the request graph is serialized to the cache, this incrementality survives process restarts — the source of Parcel's "second build is instant" behavior.

Note

Error: Lazy bundling does not work with content hashing (resolveOptions.js:145) is enforced here: lazily-built async bundles can't have stable content hashes, so the two features are mutually exclusive by design.


6. System Requirements

Component Requirement Notes
Node.js ≥ 16.0.0 Enforced via engines.node. Use an LTS release.
CPU Multi-core recommended Parcel parallelizes across cores via the worker farm
Native binaries OS/arch must be supported Prebuilt Rust addons ship as optional deps for macOS, Linux, Windows (and FreeBSD/OpenBSD watcher support)
Disk Space for .parcel-cache Cache grows with project size; safe to delete
Watchman (optional) Recommended on Linux Improves file-watching reliability (see User Guide §6)

For contributors (building Parcel itself)

Per CONTRIBUTING.md, you additionally need:

  • Yarn v4 (the repo pins packageManager: yarn@4.9.1; Corepack-enabled)
  • Rust (stable) — rust-toolchain pins the channel; native builds via yarn build-native
  • Flow — for type-checking the JS source
yarn                 # install workspaces
yarn build-native    # compile the Rust crates to native addons
yarn build           # build JS bundles + run gulp pipeline

7. Configuration Reference

Parcel reads configuration from three places, in order of locality: CLI flags (highest), .parcelrc (pipeline), and package.json (targets/source/browser support).

.parcelrc

The plugin pipeline (see §4). Supports extends, per-glob plugin arrays, and the "..." splice token.

package.json fields

Field Purpose
source Entry source file(s) when not passed on the CLI
main / module / browser / types Library output targets (each becomes a build target)
targets Explicit target definitions: distDir, publicUrl, context, engines, outputFormat, isLibrary, optimize, sourceMap, scopeHoist
browserslist Browser support matrix driving JS/CSS down-compilation
@parcel/* keys Some plugins read scoped config keys from package.json

Default target options (set by CLI)

normalizeOptions() in cli.js derives defaultTargetOptions from flags:

Option Default Source flag
shouldOptimize true in build, else false --no-optimize
sourceMaps true --no-source-maps
shouldScopeHoist enabled in build --no-scope-hoist
publicUrl / --public-url
distDir dist/ --dist-dir

Resolved-option defaults (resolveOptions.js)

Setting Default
Cache directory .parcel-cache (DEFAULT_CACHE_DIRNAME)
Cache backend LMDBCache when available, else FSCache
Public URL /
Dist directory <cwd>/dist

Environment variables

Variable Effect Read at
NODE_ENV Build mode default + exposed to user code/config cli.js
CI Disables auto-install in build mode cli.js
PORT / HMR_PORT / HMR_HOST Dev-server & HMR defaults cli.js
PARCEL_WORKERS Number of worker threads WorkerFarm.js
PARCEL_MAX_CONCURRENT_CALLS Max concurrent calls per worker WorkerFarm.js
PARCEL_WORKER_BACKEND threads or process workers/backend.js
PARCEL_INCREMENTAL_BUNDLING false disables incremental bundling cli.js
NODE_OPTIONS Forwarded to workers workers/Worker.js
PARCEL_BUILD_ENV / PARCEL_SELF_BUILD Internal: control Babel register on the CLI bin bin.js
PARCEL_DUMP_GRAPHVIZ / PARCEL_BUILD_REPL Debug: dump build graphs to GraphViz dumpGraphToGraphViz.js
SKIP_PLUGIN_COMPATIBILITY_CHECK Skip plugin/version compat check (used by canary releases) loadParcelPlugin.js
PARCEL_DEBUG_CACHE_FILEPATH Debug cache path test-utils

Feature flags

--feature-flag <name=value> toggles experimental behavior. The defaults (packages/core/feature-flags) are:

Flag Default
exampleFeature false
useWatchmanWatcher false
parcel build src/index.html --feature-flag useWatchmanWatcher=true

Unknown flag names are ignored with a warning.


8. The Plugin System (Authoring)

Every plugin is an npm package whose default export is an instance of one of the wrapper classes from @parcel/plugin (packages/core/plugin/src/PluginAPI.js). You construct the wrapper with an options object containing your hook methods; the wrapper stashes it under the Symbol.for('parcel-plugin-config') key, which the core reads when loading the plugin (loadParcelPlugin.js).

Plugin categories

Wrapper class Responsibility
Resolver Map an import specifier to a resolved file (or exclude it)
Transformer Compile/transform a single asset; declare its dependencies
Bundler Decide how assets are grouped into bundles
Namer Produce the output filename for a bundle
Runtime Inject extra assets/code into bundles (loaders, HMR client, …)
Packager Combine a bundle's assets into final serialized output
Optimizer Post-process packaged output (minify, transform)
Compressor Emit compressed variants (gzip, brotli)
Reporter React to build lifecycle events (progress, dev server, metrics)
Validator Validate assets in parallel (type-check, lint) without blocking output

Authoring example (a Transformer)

import {Transformer} from '@parcel/plugin';

export default new Transformer({
  async loadConfig({config}) {
    // optionally read project config; result is cached & invalidation-tracked
  },
  async transform({asset, options, logger}) {
    let source = await asset.getCode();
    let compiled = doSomething(source);
    asset.setCode(compiled);
    asset.type = 'js';
    return [asset]; // may return multiple assets
  },
});

Register it in .parcelrc:

{
  "extends": "@parcel/config-default",
  "transformers": { "*.foo": ["parcel-transformer-foo"] }
}

Important

Parcel checks plugin/engine compatibility at load time. The Error: Plugin ${pluginName} has no exports. (loadParcelPlugin.js:232) means your package's default export is not a @parcel/plugin wrapper instance. The SKIP_PLUGIN_COMPATIBILITY_CHECK env var bypasses the version check and exists for canary releases — do not set it in normal use.

Tip

Plugin hooks receive an options bag, a scoped logger, and config helpers. Declaring file/config dependencies inside hooks is what lets Parcel invalidate and rebuild only what changed — always declare dependencies rather than reading files out of band.


9. The Native (Rust) Layer

Parcel implements its hottest paths in Rust, compiled to native Node addons via N-API (crates/node-bindings) and to WASM for the browser REPL.

Crate Role
crates/node-bindings N-API bridge exposing Rust functions to Node (resolver, transformer, …)
crates/parcel-resolver The module resolution algorithm (largest crate, ~207 KB of source)
crates/html HTML parsing/serialization/optimization (~231 KB; backs the HTML transformer/optimizer)
crates/macros Build-time macro evaluation
crates/core Shared Rust core types
crates/dev-dep-resolver Dev-dependency resolution
packages/transformers/js/core The SWC-based JS/TS transformer (Rust; ~364 KB of source)

Native binaries are loaded by packages/core/rust/index.js, which selects the correct prebuilt binary per platform/arch and throws a clear diagnostic when none matches:

Unsupported architecture on Linux: ${arch}
Failed to load native binding

Warning

Native addons are delivered as optional dependencies. Installing with --no-optional / --omit=optional, or building images for an unsupported platform, yields Failed to load native binding. Always allow optional dependencies to install. WASM builds are available for the REPL but resolveAsync() / getInvalidations() are explicitly unsupported in WASM (crates/node-bindings/src/resolver.rs).


10. Caching & Incrementality

  • Location: .parcel-cache (override with --cache-dir; disable with --no-cache).
  • Backend: LMDBCache (memory-mapped key-value store) when available, otherwise FSCache (filesystem). Selected in resolveOptions.js.
  • Granularity: Per-request results keyed by content + invalidations, stored via the request graph and the serializer (serializer.js, registerCoreWithSerializer.js).
  • Persistence: The request graph is written to the cache on _end()/writeToCache(), so incrementality survives restarts.
  • Invalidation: File changes, env-var changes, and option changes invalidate dependent requests only.

Operational guidance:

Situation Action
Suspected stale build rm -rf .parcel-cache or build once with --no-cache
Reproducible CI builds Keep cache out of CI artifacts; rely on lockfile + content hashing
Disk pressure .parcel-cache is safe to delete at any time

Tip

Add .parcel-cache and dist to .gitignore. Never commit the cache — it's machine- and content-specific.


11. Concurrency: The Worker Farm

@parcel/workers (packages/core/workers) spreads transformation work across CPU cores.

Concern Mechanism
Worker count Auto-detected CPU count, override with PARCEL_WORKERS
Backend Worker threads or child processes (PARCEL_WORKER_BACKEND; backend.js)
Concurrency cap PARCEL_MAX_CONCURRENT_CALLS per worker
Shared state createSharedReference() shares the resolved options across workers without copying
Reuse createWorkerFarm() (exported from @parcel/core) lets you reuse one farm across multiple Parcel instances

Relevant guard rails (from WorkerFarm.js):

Please provide a worker path!
Cannot add a worker call if workerfarm is ending.
Could not detect cpu count!

Note

The ERR_WORKER_INVALID_EXEC_ARGV crash reported on newer Node releases originates in worker-thread execArgv handling. Pin to a supported Node LTS; if you must run newer Node, ensure NODE_OPTIONS carries no worker-incompatible flags.


12. Programmatic API & Embedding

Embed Parcel via @parcel/core. The package exports Parcel (default + named), BuildError, createWorkerFarm, and serializer helpers.

import {Parcel, createWorkerFarm} from '@parcel/core';
import {MemoryFS} from '@parcel/fs';

// Reuse one worker farm across many builds (e.g. a test runner or a CI matrix):
const workerFarm = createWorkerFarm();

const bundler = new Parcel({
  entries: ['src/index.html'],
  defaultConfig: '@parcel/config-default',
  workerFarm,
  mode: 'production',
  defaultTargetOptions: {distDir: 'build', shouldOptimize: true},
});

const {bundleGraph, buildTime} = await bundler.run();
console.log(`Built ${bundleGraph.getBundles().length} bundles in ${buildTime}ms`);

await workerFarm.end(); // you own the farm, so you end it

Key option fields (InitialParcelOptions): entries, defaultConfig, config, mode, env, hmrOptions, serveOptions, targets, shouldDisableCache, cacheDir, watchDir, watchBackend, watchIgnore, shouldAutoInstall, logLevel, shouldProfile, shouldTrace, shouldBuildLazily, lazyIncludes/lazyExcludes, shouldBundleIncrementally, detailedReport, additionalReporters, defaultTargetOptions, featureFlags, workerFarm, shouldPatchConsole.

Important

If you pass your own workerFarm, Parcel disposes only the references it created and leaves the farm running — you are responsible for workerFarm.end(). If you let Parcel create the farm, it ends it for you on _end(). Passing a farm that is already ending throws Supplied WorkerFarm is ending.

Note

MemoryFS (from @parcel/fs) lets you build entirely in memory — useful for tests and serverless contexts. Note MemoryFS is reportedly slower than the default filesystem for large inputs.


13. Security Analysis

Parcel is a build tool that executes on developer machines and CI, not a network service. Its security surface is the supply chain and the dev server.

Area Behavior Guidance
Auto-install By default Parcel installs missing plugins/transitive build deps via the detected package manager (@parcel/package-manager). Disable in untrusted contexts with --no-autoinstall. Auto-install is already disabled in CI builds (via the CI env var).
Install scripts The package runs postinstall: patch-package. A reported issue (npm RFC #868) notes install scripts may be blocked by default in future npm. Audit patches/ and lockfiles in security-sensitive setups.
Native binaries Prebuilt Rust addons are fetched as optional deps. Verify integrity via your lockfile; prefer pinned versions and a private registry mirror in regulated environments.
Dev server parcel serve binds to all interfaces by default and serves your project. On shared networks, bind to localhost (--host 127.0.0.1) or use --https. CORS can be disabled with --no-cors.
HTTPS Auto-generates a self-signed certificate, or accepts --cert/--key. Use a trusted cert for anything beyond local dev. Certificate and/or key not found (getCertificate.js) indicates a misconfigured cert path.
Plugin trust Plugins are arbitrary code executed at build time. Only install plugins you trust; pin versions; review .parcelrc.
Secrets NODE_ENV and other env vars are exposed to bundled code. Never put secrets in client-bundled env vars; only public values belong in browser bundles.

Caution

Because the dev server listens on all interfaces by default, anyone on your local network can reach your in-development app. Bind to 127.0.0.1 when working on sensitive features or untrusted networks.


14. Operations & Observability

Profiling & tracing

Tool How Output
Sampling profiler --profile or Ctrl+E while watching CPU sampling profile
Build tracer --trace Adds @parcel/reporter-tracer; emits a trace of build phases
Heap snapshot Ctrl+Y while watching V8 heap snapshot
Detailed report --detailed-report [count] Per-asset timings and sizes
Phase times PARCEL_SHOW_PHASE_TIMES env (CLIReporter.js) Per-phase timing output

Diagnostic reporters available

@parcel/reporter-bundle-analyzer, -bundle-stats, -bundle-buddy, -build-metrics, -json, -sourcemap-visualiser, and the LSP reporter (@parcel/reporter-lsp) for editor integration. Add any of them via --reporter <name> or .parcelrc.

parcel build src/index.html --reporter @parcel/reporter-bundle-analyzer

Editor / IDE integration

@parcel/parcel-lsp + @parcel/reporter-lsp-reporter provide a Language Server Protocol surface (and packages/utils/parcelforvscode) so editors can show Parcel diagnostics inline.

Logging

--log-level none|error|warn|info|verbose. The diagnostics system (@parcel/diagnostic, @parcel/codeframe, @parcel/markdown-ansi) renders rich, code-framed errors with hints and documentation links.


15. Troubleshooting (Operational)

Failed to load native binding in Docker/CI

Your image lacks the platform's prebuilt addon. Ensure optional dependencies install and the base image matches a supported platform/arch. Don't use --no-optional/--omit=optional.

Supplied WorkerFarm is ending

(Programmatic API.) You passed a workerFarm to new Parcel() after calling workerFarm.end(). Create a fresh farm or don't end it until all builds finish.

Could not detect cpu count!

The environment doesn't expose CPU info to Node. Set PARCEL_WORKERS explicitly.

Plugin ${pluginName} has no exports.

A .parcelrc references a package whose default export isn't a @parcel/plugin wrapper. Fix the plugin's export, or remove it from .parcelrc.

Invalid backend: ${backend}

PARCEL_WORKER_BACKEND is set to an unrecognized value. Use threads or process.

Scope hoisting is not supported with SSG

The React-static packager can't scope-hoist. Build that target with --no-scope-hoist or adjust its target config.

Build OOMs on a huge graph (Node capacity overflow! / Edge capacity overflow!)

The graph's AdjacencyList hit its capacity. Reduce the build's scope, split into multiple targets, or raise Node's heap (NODE_OPTIONS=--max-old-space-size=...).

Dev server reload broken on Linux

Install Watchman or pass --watch-backend watchman (also see feature flag useWatchmanWatcher).


16. Code Quality & Conventions

Aspect Convention
Types Flow for JS source (// @flow); TypeScript declarations for the published @parcel/types API
Lint/format ESLint + Prettier for JS/JSON/MD; cargo fmt (nightly) for Rust (yarn lint, yarn format)
Tests Mocha unit tests + @parcel/integration-tests; cargo test for Rust (yarn test)
Monorepo mgmt Yarn 4 workspaces + Lerna for versioning/publishing
Native build scripts/build-native.js (`yarn build-native[-release
Self-hosting Parcel builds itself: build-bundles runs parcel build on selected packages
Error handling Centralized ThrowableDiagnostic + code frames; build failures become BuildError with structured diagnostics
Plugin contract Single symbol (parcel-plugin-config) decouples the wrapper from the loader
Releases Canary/dev/release flows via Lerna (dev:release, canary:release, release) with --dist-tag channels

Architectural strengths: clean plugin boundaries; graph-based incrementality; native acceleration isolated behind N-API; rich diagnostics. Complexity hotspots: the core orchestrator and graph packages (Parcel.js, BundleGraph.js, RequestTracker.js, graph/*) carry most of the system's intrinsic complexity and are the densest reading.


17. Potential Improvements

Derived from code structure and community-reported issues (these are observations, not commitments):

  • Node compatibility: the ERR_WORKER_INVALID_EXEC_ARGV crash on Node 24.7.0+ (#10238) suggests the worker execArgv handling needs forward-compat hardening.
  • RSC maturity: multiple open issues tag [RSC] (hydration error from HTML minification #10205, invalid entrypoint with multiple targets #10310, duplicate stylesheet refs #10309, asset URL creation #10305) — the React Server Components path is still stabilizing.
  • Web-extension service workers: Bundles conflicting names (#10223) and import-maps breakage (#10240) indicate the webextension target needs naming/manifest fixes.
  • Node polyfills: the url polyfill is reportedly not a faithful stand-in for Node's native url (#7801).
  • MemoryFS performance: reportedly much slower than the default FS for large inputs (#9413) — a candidate for optimization or documentation.
  • CSS modules / packaging edge cases: Got unexpected null with composes (#9635) and CSS-as-JS misclassification from inline module scripts (#8572).
  • Build-mode mode switch: community request to add a mode to target config and the build CLI (#10226).

18. Contributing

From CONTRIBUTING.md:

  1. Install prerequisites: git, Node (LTS), Yarn v4 (or Corepack), Rust (stable), Flow.
  2. Fork, branch, then:
    yarn                  # install
    yarn build-native     # compile Rust addons
    
  3. Learn the internals starting at packages/core/core/src/Parcel.js and the docs/ folder (DefaultBundler.md, Scopehoisting*.md, Symbol Propagation.md, AdjacencyList.md, …).
  4. Add tests for fixes/features. Commit yarn.lock if it changed.
  5. Ensure CI checks pass locally: yarn check (Flow), yarn lint, yarn test.
  6. For significant changes, propose first via GitHub Discussions.
yarn test:unit          # mocha + cargo test
yarn test:integration   # integration-tests workspace
yarn lint               # eslint + prettier + cargo fmt --check
yarn check              # flow check

19. Quick Reference

Key source locations

Concern File
CLI definition packages/core/parcel/src/cli.js
Orchestrator packages/core/core/src/Parcel.js
Option resolution packages/core/core/src/resolveOptions.js
Config loading packages/core/core/src/loadParcelConfig.js, loadParcelPlugin.js
Default pipeline packages/configs/default/index.json
Plugin author API packages/core/plugin/src/PluginAPI.js
Graph primitives packages/core/graph/src/{Graph,ContentGraph,AdjacencyList}.js
Worker farm packages/core/workers/src/WorkerFarm.js
Native loader packages/core/rust/index.js

Build tooling commands (repo)

Command Action
yarn build Build JS bundles + gulp pipeline
yarn build-native Compile Rust crates to native addons
yarn build-native-wasm Compile WASM (for the REPL)
yarn test Unit + integration tests
yarn check / yarn lint / yarn format Flow / lint / format
yarn clean Clean caches, lib output, reinstall

Operational env vars

PARCEL_WORKERS, PARCEL_MAX_CONCURRENT_CALLS, PARCEL_WORKER_BACKEND, PARCEL_INCREMENTAL_BUNDLING, NODE_OPTIONS, NODE_ENV, CI, SKIP_PLUGIN_COMPATIBILITY_CHECK.

Plugin categories (10)

Resolver · Transformer · Bundler · Namer · Runtime · Packager · Optimizer · Compressor · Reporter · Validator