v1.0 — Stable Release

Track every import. Instantly.

When a file changes, you need to know what else is affected. importree builds the full import dependency tree for any TypeScript or JavaScript entry point — with zero dependencies and zero AST overhead.

Built for CI pipelines, build tools, monorepo task runners, and test selectors.

npm install importree Copied!
Get Started
index.ts app.ts router.ts auth.ts db.ts api.ts views.ts utils.ts
0
Runtime dependencies
<9kb
Minified bundle
ESM+CJS
Dual module output
100%
Test coverage

Everything you need.
Nothing you don't.

One job, done right. Resolve every import across your entire codebase with a single function call.

Zero Dependencies

Built entirely on Node.js built-ins. No native binaries, no WASM, no transitive dependency tree of its own. Just pure TypeScript.

Fast Scanning

Regex-based import extraction with concurrent async file traversal. No AST parsing overhead — just the specifiers you need, as fast as reading the file.

Path Alias Support

Resolve @/components, ~/utils, or any custom alias. Longest-prefix matching with automatic extension probing and index file resolution.

Cache Invalidation

Someone edits utils.ts — which test suites, which pages, which build targets need to re-run? The pre-computed reverse graph answers that instantly with getAffectedFiles().

Benchmarks

Compared with excellent tools like madge, dependency-tree, and the TypeScript compiler on a synthetic project with realistic import patterns.

500 files synthetic project
importree
12.7 ms
glob+regex
26.5 ms
madge
43.3 ms
dep-tree
44.4 ms
ts compiler
50.9 ms
12.7 ms
full dependency tree for 500 files
Concurrent async traversal + resolver cache
661K ops/s
scanImports throughput on typical source files
Regex-based extraction · zero allocation hot path
26.4 ms
full tree build for 1,000 files
~38 ops/s · scales linearly with project size
0 deps
built entirely on Node.js built-ins
No native binaries · no WASM · no AST parsers

Two functions.
That's the whole API.

Build the tree. Query it. Done.

1Build the tree

build-tree.ts
import { importree } from 'importree';

const tree = await importree('./src/index.ts', {
  aliases: { '@': './src' },
});

console.log(tree.files);
// ['/abs/src/index.ts', '/abs/src/app.ts', ...]

console.log(tree.externals);
// ['react', 'lodash', 'node:path']

console.log(tree.graph);
// { '/abs/src/index.ts': ['/abs/src/app.ts', ...] }

2Find affected files

invalidate.ts
import { importree, getAffectedFiles } from 'importree';

const tree = await importree('./src/index.ts');

// When utils.ts changes, what needs rebuilding?
const affected = getAffectedFiles(
  tree,
  './src/utils.ts'
);

console.log(affected);
// ['/abs/src/app.ts', '/abs/src/index.ts']
// ^ every file that transitively depends on utils.ts

The complete API.

Two functions, five output fields — fully typed with JSDoc. Nothing hidden, nothing undocumented.

importree(entry: string, options?: ImportreeOptions): Promise<ImportTree>

Recursively resolves all static imports, dynamic imports, require() calls, and re-exports starting from the entry file. Returns the full dependency graph.

getAffectedFiles(tree: ImportTree, changedFile: string): string[]

BFS traversal of the reverse dependency graph. Returns all files that transitively depend on the changed file — sorted, deterministic, and without the changed file itself.

ImportTree

entrypoint : string

Absolute path of the entry file.

files : string[]

Sorted absolute paths of all local files in the dependency tree.

externals : string[]

Sorted unique bare import specifiers — packages like react, lodash, node:fs.

graph : Record<string, string[]>

Forward adjacency list. Each file maps to its direct local imports.

reverseGraph : Record<string, string[]>

Reverse adjacency list. Each file maps to files that import it. Pre-computed for fast cache invalidation.