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

> Discover how Worker Shell backend tree shakes feature groups like curl, git, and sqlite from your bundle. Learn how this process optimizes your code by analyzing module reachability at build time.

- Repository: [Cloudflare/computer](https://github.com/cloudflare/computer)
- Tags: internals
- Published: 2026-08-14

---

**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`](https://github.com/cloudflare/computer/blob/main/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`](https://github.com/cloudflare/computer/blob/main/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

```javascript
// 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`](https://github.com/cloudflare/computer/blob/main/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**.

```javascript
// 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

```typescript
// 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

```typescript
// 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

```typescript
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`](https://github.com/cloudflare/computer/blob/main/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`](https://github.com/cloudflare/computer/blob/main/packages/computer/src/backends/worker-shell/shell-modules.ts) | Generates feature‑to‑module mapping |
| [`packages/computer/src/backends/worker-shell/shell-modules.test.ts`](https://github.com/cloudflare/computer/blob/main/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`](https://github.com/cloudflare/computer/blob/main/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`](https://github.com/cloudflare/computer/blob/main/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`](https://github.com/cloudflare/computer/blob/main/partition.test.ts) and during CI builds.