# Parcel Admin & Architecture Guide

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](#1-executive-summary)
2. [Architecture Overview](#2-architecture-overview)
3. [Repository Structure](#3-repository-structure)
4. [The Plugin Pipeline](#4-the-plugin-pipeline)
5. [The Build Orchestration Model](#5-the-build-orchestration-model)
6. [System Requirements](#6-system-requirements)
7. [Configuration Reference](#7-configuration-reference)
8. [The Plugin System (Authoring)](#8-the-plugin-system-authoring)
9. [The Native (Rust) Layer](#9-the-native-rust-layer)
10. [Caching & Incrementality](#10-caching--incrementality)
11. [Concurrency: The Worker Farm](#11-concurrency-the-worker-farm)
12. [Programmatic API & Embedding](#12-programmatic-api--embedding)
13. [Security Analysis](#13-security-analysis)
14. [Operations & Observability](#14-operations--observability)
15. [Troubleshooting (Operational)](#15-troubleshooting-operational)
16. [Code Quality & Conventions](#16-code-quality--conventions)
17. [Potential Improvements](#17-potential-improvements)
18. [Contributing](#18-contributing)
19. [Quick Reference](#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

```text
                    ┌─────────────────────────────────────────────────────────┐
 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

```json
"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:

```json
{
  "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

```bash
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` |

```bash
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)

```js
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`:

```json
{
  "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:

```text
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`):

```text
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.

```js
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`.

```bash
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|-canary|-wasm]`) |
| 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:
   ```bash
   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.

```bash
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

---

_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. Unofficial; not affiliated with or reviewed by the project._
