Skip to content

rocketman-code/chainsaw

Repository files navigation

chainsaw

Trace the transitive import weight of any TypeScript or Python entry point. Point it at a file, get back what gets pulled in at startup and how much code it costs.

chainsaw demo

Only static imports count toward the weight. Dynamic import() and type-only imports are tracked separately.

Install

cargo install chainsaw-cli

Requires Rust 1.91+.

Usage

Python

Works the same way. Resolution matches importlib behavior -- source roots, virtualenv, .pth files, sys.path modifications, C extensions.

$ chainsaw trace app/main.py

app/main.py
Static transitive weight: 2.1 MB (608 modules)

Heavy dependencies (static):
  botocore                            1.2 MB  340 files
    -> app/main.py -> ... -> boto3 -> botocore

Why is this package in my tree?

$ chainsaw trace src/index.ts --chain zod

3 chains to "zod" (3 hops):

  1. src/index.ts -> src/api/routes.ts -> src/api/validation.ts -> zod
  2. src/index.ts -> src/api/middleware.ts -> src/api/validation.ts -> zod
  3. src/index.ts -> src/config/env.ts -> src/api/validation.ts -> zod

Where to cut

All three chains pass through src/api/validation.ts. Chainsaw finds that:

$ chainsaw trace src/index.ts --cut zod

1 cut point to sever all 3 chains to "zod":

  src/api/validation.ts                                   (breaks 3/3 chains)

Make that import dynamic and zod drops out of your startup path.

When no single file can break all chains:

No single cut point can sever all 3 chains to "chalk".
Each chain takes a different path — multiple fixes needed.

Diff

Compare two entry points:

$ chainsaw trace src/index.ts --diff src/worker.ts

Diff: src/index.ts vs src/worker.ts

  src/index.ts                               8.2 MB
  src/worker.ts                              2.4 MB
  Delta                                      -5.8 MB

Only in src/index.ts:
  - undici                              1.1 MB
  - date-fns                            619 KB
  - zod                                 537 KB
Only in src/worker.ts:
  + html-rewriter                        84 KB
Shared: 12 packages

Or save a snapshot and compare before/after:

$ chainsaw trace src/index.ts --save before.json
# ... make changes ...
$ chainsaw trace src/index.ts --diff-from before.json

The diff subcommand compares two saved snapshots directly: chainsaw diff before.json after.json

Compare against a git ref to see how weight changed over time:

$ chainsaw diff HEAD~10 --entry src/index.ts

In a monorepo, the diff target can be in a different package -- chainsaw builds a separate graph from that package's root automatically.

Interactive mode

Run multiple queries against a cached graph without rebuilding:

$ chainsaw repl src/index.ts

chainsaw> trace
chainsaw> chain zod
chainsaw> cut zod
chainsaw> packages

CI gating

$ chainsaw trace src/index.ts --max-weight 5MB --quiet --top 0 --top-modules 0

error: static transitive weight 8.2 MB (2140 modules) exceeds --max-weight threshold 5.0 MB

Exits non-zero when static weight exceeds the threshold. Accepts 5MB, 500KB, 100B.

JSON

$ chainsaw trace src/index.ts --json --quiet | jq .static_weight_bytes

Shell completions

chainsaw completions zsh > ~/.zfunc/_chainsaw
chainsaw completions bash > /etc/bash_completion.d/chainsaw
chainsaw completions fish > ~/.config/fish/completions/chainsaw.fish

Run chainsaw --help for the full flag reference.

Development

git clone https://github.com/rocketman-code/chainsaw.git
cd chainsaw
cargo xtask install-hooks
cargo test --workspace

See CONTRIBUTING.md for the full development guide.

License

GPLv3. See LICENSE.

About

Trace transitive import weight in TypeScript and Python codebases

Topics

Resources

License

Contributing

Stars

Watchers

Forks

Packages

 
 
 

Contributors