CLI & library to verify packages are tree-shakeable by Rollup

@wolfcola/treeshake-check

Verify that your npm packages are properly tree-shakeable. Uses Rollup to bundle a synthetic consumer that imports your package entry point, then analyzes which modules survive tree-shaking and why.

Installation

npm install -D @wolfcola/treeshake-check

CLI Usage

npx treeshake-check [options]

By default, the CLI reads package.json from the current directory, resolves the entry point from the module, main, or exports field, and runs the analysis.

Options

OptionAliasDescription
--cwd-CDirectory containing the package.json to analyze (defaults to cwd)
--entry-eAnalyze a specific entry file directly, skipping package.json resolution
--jsonEmit machine-readable JSON instead of human-readable output
--quiet-qSuppress all output; rely on exit code only (exit 1 = not fully tree-shakeable)
--topShow only the N modules with the largest surviving byte count

Examples

# Check the package in the current directory
npx treeshake-check

# Check a specific directory
npx treeshake-check --cwd packages/my-lib

# Analyze a specific file
npx treeshake-check --entry dist/index.mjs

# CI gate (fails with exit code 1 if not tree-shakeable)
npx treeshake-check --quiet

# Machine-readable output
npx treeshake-check --json

# Show top 5 worst offenders
npx treeshake-check --top 5

Programmatic API

checkPackage(cwd?)

Full pipeline: reads package.json from cwd (defaults to process.cwd()), resolves the entry point, and analyzes tree-shakeability. Returns Effect.Effect<TreeshakeResult, PackageJsonNotFound | MissingEntryPoint | BundleFailed | ParseError, FileSystem | Path>.

import { checkPackage } from '@wolfcola/treeshake-check';
import { Effect } from 'effect';
import { NodeContext } from '@effect/platform-node';

const result = await Effect.runPromise(
  checkPackage('./packages/my-lib').pipe(Effect.provide(NodeContext.layer)),
);

analyzeTreeshakeability(entry, pkg?)

Analyzes a single entry file. Creates a virtual Rollup bundle that imports the entry, enables tree-shaking, and inspects what survives. Returns Effect.Effect<TreeshakeResult, BundleFailed, Path>.

getEntryFromPackageJson(cwd?)

Reads and validates package.json, resolving the entry point from module, main, or exports fields. Returns the entry path and parsed package metadata.

Result Types

TreeshakeResult

A discriminated union (Effect Schema.Union of TaggedStruct):

  • FullyTreeshakeable -- All modules rendered to zero bytes. Includes hints with package-level recommendations.
  • HasSideEffects -- Some modules survived tree-shaking. Includes totalOriginalBytes, totalRenderedBytes, modules (array of per-module analysis), warnings (Rollup warnings), hints, and unshakenCode.

Each module in the HasSideEffects result includes id, originalLength, renderedLength, shakingRatio, renderedExports, removedExports, survivingCode, and suspectedCauses.

Suspected Causes

The analyzer classifies surviving code into categories: TopLevelSideEffect, PrototypeMutation, GlobalAssignment, CommonJsContamination, UnannotatedCall, EnumPattern, Unknown. The CLI provides explanations and fix suggestions for each.

Error Types

ErrorDescription
PackageJsonNotFoundNo package.json found at the specified path
MissingEntryPointpackage.json has no module, main, or exports entry
BundleFailedRollup bundling failed (wraps the underlying cause)

This tool uses Rollup internally. Rollup is bundled as a dependency.