How Worker‑Shell Feature Groups Are Tree‑Shaken from the Bundle

Tree‑shaking in the Worker‑Shell backend removes optional feature groups (e.g., curl, git, sqlite) from the final bundle unless the consumer explicitly imports them, using a three‑stage partition system that analyzes module reachability at build time.

The @cloudflare/computer repository provides a Worker‑Shell backend that builds self‑contained JavaScript bundles for Cloudflare Workers. Keeping these bundles small is critical for cold‑start performance and the 128 MB script size limit. To achieve this, the backend implements a sophisticated tree‑shaking mechanism for optional feature groups—modules that provide shell‑like utilities but should only be included when actually used.


How Feature Group Tree‑Shaking Works

The tree‑shaking process operates in three coordinated stages, each handled by a specific source file in the backend.

Stage 1: Generate the Feature‑to‑Module Map

At build time, shell-modules.ts scans the @cloudflare/computer/shell/<feature> packages and constructs a map of feature → source strings. This map distinguishes between:

  • A core group (always included, containing shared infrastructure)
  • One group per optional feature (e.g., curl, git, sqlite)

The source file packages/computer/src/backends/worker-shell/shell-modules.ts exports this map, which downstream scripts consume to determine what code belongs to which feature.


Stage 2: Partition the Module Graph

The core tree‑shaking logic lives in script/partition.mjs. This script receives the full module graph from the bundler along with the OPTIONAL_FEATURES map, then performs four key operations:

  1. Resolve feature roots (resolveFeatureRoots): Maps each optional feature to the command chunks it directly depends on
  2. Compute reachability closures (featureReach): Determines every chunk reachable from each feature's entry points
  3. Classify chunks: Labels each chunk as core, exclusive to a single feature, or shared across multiple features
  4. Build the partition object: Populates partition[feature] with chunks that belong exclusively to that feature
// Conceptual flow in partition.mjs
const partition = partitionModules({
  graph,           // Full module graph from bundler
  registry,        // Chunk metadata
  optionalFeatures // Map of feature names to entry points
});

// Result: partition.core, partition.curl, partition.git, etc.

The partitioner guarantees safety: if an optional feature resolves to zero command chunks, it throws immediately. This prevents silent failures from typos or package restructuring. The test throws when a feature resolves to no chunk in partition.test.ts verifies this behavior.


Stage 3: Build the Final Bundle

script/build-bundle.mjs orchestrates the final bundling step. It calls partitionModules and passes the resulting partition to the bundler with specific instructions:

  • Always include core chunks
  • Include exclusive chunks only for features actually imported by the consumer
  • Omit any feature whose chunks are unreachable from the entry point

This effectively tree‑shakes unused features out of the bundle.

// From build-bundle.mjs
const { core, ...featurePartitions } = partitionModules({
  graph,
  registry,
  optionalFeatures: OPTIONAL_FEATURES
});

// Bundler receives: core + selected feature partitions
const bundle = await bundler.build({
  entryPoints: [mainEntry],
  inject: [
    ...core,
    ...(userImportedCurl ? featurePartitions.curl : []),
    ...(userImportedGit ? featurePartitions.git : []),
    // ... other selected features
  ]
});

Why This Tree‑Shaking Approach Is Safe

The partition system ensures correctness through two key properties of the module graph analysis.

Exclusive Chunks Enable Safe Removal

When a chunk is reachable only from one optional feature, dropping that feature guarantees the chunk is unreferenced. The partitioner identifies these exclusive chunks by comparing reachability sets: if featureReach(curl) ∩ featureReach(git) = ∅ for a given chunk, that chunk is exclusive.

Shared Chunks Stay in Core

When multiple features depend on the same chunk, the partitioner promotes it to the core group. This ensures the chunk remains available regardless of which specific features the consumer imports. No feature can "claim" shared infrastructure exclusively.


Practical Examples

Importing a Feature Preserves Its Code

// src/index.ts (consumer's worker)
import { curl } from '@cloudflare/computer/shell/curl';

// This import causes the build pipeline to:
// 1. Mark 'curl' as referenced in OPTIONAL_FEATURES
// 2. Include partition.curl chunks in the final bundle
await curl('https://api.example.com/data');

The OPTIONAL_FEATURES map contains curl → ['curl']. During partitioning, the curl group's exclusive chunks are retained.

Omitting a Feature Removes Its Code

// src/index.ts (consumer's worker)
import { echo } from '@cloudflare/computer/shell/echo';

// No import of '@cloudflare/computer/shell/curl'
// The curl feature group is never added to partition imports

Because curl never appears in the module graph reachable from the entry point, partition.curl is excluded from the bundle. The resulting size reduction is approximately 150 KB for the curl implementation alone.

Debugging the Partition Result

import { partitionModules } from '@cloudflare/computer/src/backends/worker-shell/script/partition.mjs';
import { graph, registry } from './bundle-graph'; // Generated by bundler

const part = partitionModules({
  graph,
  registry,
  optionalFeatures: OPTIONAL_FEATURES
});

console.log('Core chunks:', part.core.length);
console.log('Curl chunks:', part.curl?.length ?? 0);
console.log('Git chunks:', part.git?.length ?? 0);
console.log('SQLite chunks:', part.sqlite?.length ?? 0);

Running this during a CI build enables verification that tree‑shaking behaved as expected for specific feature combinations.


Key Source Files

File Role
packages/computer/src/backends/worker-shell/worker-shell.ts Runtime entry point that loads the generated bundle
packages/computer/src/backends/worker-shell/shell-modules.ts Generates feature‑to‑module mapping
packages/computer/src/backends/worker-shell/shell-modules.test.ts Validates feature group construction
packages/computer/src/backends/worker-shell/script/partition.mjs Core tree‑shaking algorithm: reachability analysis and chunk classification
packages/computer/src/backends/worker-shell/script/partition.test.ts Unit tests for partition correctness and error handling
packages/computer/src/backends/worker-shell/script/build-bundle.mjs Orchestrates bundling with partitioned modules

Summary

  • Tree‑shaking in Worker‑Shell operates through explicit partition analysis rather than relying solely on bundler heuristics
  • Three stages: feature map generation (shell-modules.ts), module graph partitioning (partition.mjs), and selective bundle construction (build-bundle.mjs)
  • Exclusive chunks per feature enable safe removal; shared chunks are promoted to core to prevent breakage
  • Safety guarantees: The partitioner throws on misconfigured features, preventing silent code loss
  • Consumer control: Import statements directly determine which feature groups survive tree‑shaking

Frequently Asked Questions

What happens if two features share a common dependency?

Shared dependencies are classified as core chunks during partitioning. They remain in the bundle regardless of which specific optional features are imported, ensuring no feature can break another by its absence.

Can tree‑shaking remove features that are dynamically imported?

The current implementation analyzes static imports at build time. Dynamic imports of shell features would need explicit inclusion in the OPTIONAL_FEATURES map or manual chunk configuration to guarantee availability.

How large are typical feature groups?

Based on the repository structure, individual feature groups range from ~50 KB to ~200 KB uncompressed. The curl implementation specifically contributes approximately 150 KB, making its removal significant for bundle optimization.

What errors indicate a broken feature configuration?

The partitioner throws with a descriptive message when a feature name in OPTIONAL_FEATURES resolves to zero command chunks. This typically indicates a package rename, missing export, or typo in the feature map. The error is caught by tests in partition.test.ts and during CI builds.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →