Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
name: CI

on:
push:
branches: [main]
pull_request:
branches: [main]

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
ci:
name: Node ${{ matrix.node-version }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
node-version: [20, 22]

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Install pnpm
uses: pnpm/action-setup@v4
with:
version: 10

- name: Setup Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: pnpm

- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Lint
run: pnpm lint

- name: Format check
run: pnpm fmt:check

- name: Typecheck
run: pnpm tsc --noEmit

- name: Build
run: pnpm build

- name: Test
run: pnpm test:coverage
82 changes: 41 additions & 41 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,27 +29,27 @@ Measured with [Vitest bench](https://vitest.dev/guide/features.html#benchmarking

Each tool brings different strengths — [dependency-tree](https://github.com/dependents/node-dependency-tree) offers robust AST-based analysis via detective, [madge](https://github.com/pahen/madge) supports multiple languages and provides circular dependency detection with visualization. importree trades those features for raw speed through regex-based extraction.

| Scenario | importree | [dependency-tree](https://github.com/dependents/node-dependency-tree) | [madge](https://github.com/pahen/madge) | Manual glob+regex | ts.createProgram |
|----------|-----------|-----------------|-------|-------------------|------------------|
| Small (10 files) | **0.4 ms** | 3.1 ms | 3.7 ms | 0.6 ms | 49.9 ms |
| Medium (100 files) | **2.1 ms** | 14.3 ms | 15.1 ms | 5.2 ms | 48.4 ms |
| Large (500 files) | **12.7 ms** | 44.4 ms | 43.3 ms | 26.5 ms | 50.9 ms |
| Scenario | importree | [dependency-tree](https://github.com/dependents/node-dependency-tree) | [madge](https://github.com/pahen/madge) | Manual glob+regex | ts.createProgram |
| ------------------ | ----------- | --------------------------------------------------------------------- | --------------------------------------- | ----------------- | ---------------- |
| Small (10 files) | **0.4 ms** | 3.1 ms | 3.7 ms | 0.6 ms | 49.9 ms |
| Medium (100 files) | **2.1 ms** | 14.3 ms | 15.1 ms | 5.2 ms | 48.4 ms |
| Large (500 files) | **12.7 ms** | 44.4 ms | 43.3 ms | 26.5 ms | 50.9 ms |

### Full tree build

| Project size | Mean time | Throughput |
|-------------|-----------|------------|
| 10 files | 0.4 ms | ~2,548 ops/s |
| 100 files | 2.5 ms | ~406 ops/s |
| 500 files | 12.1 ms | ~83 ops/s |
| 1,000 files | 26.4 ms | ~38 ops/s |
| Project size | Mean time | Throughput |
| ------------ | --------- | ------------ |
| 10 files | 0.4 ms | ~2,548 ops/s |
| 100 files | 2.5 ms | ~406 ops/s |
| 500 files | 12.1 ms | ~83 ops/s |
| 1,000 files | 26.4 ms | ~38 ops/s |

### Scanner throughput

| Operation | Throughput |
|-----------|-----------|
| `scanImports` (3 imports) | ~661K ops/s |
| `scanImports` (50 imports) | ~41K ops/s |
| Operation | Throughput |
| ----------------------------- | ------------ |
| `scanImports` (3 imports) | ~661K ops/s |
| `scanImports` (50 imports) | ~41K ops/s |
| `stripComments` (1,000 lines) | ~2,497 ops/s |

> Run `pnpm bench:run` to reproduce locally.
Expand All @@ -73,10 +73,10 @@ Requires Node.js >= 18.
### Build the tree

```ts
import { importree } from 'importree';
import { importree } from "importree";

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

console.log(tree.files);
Expand All @@ -92,12 +92,12 @@ console.log(tree.graph);
### Find affected files

```ts
import { importree, getAffectedFiles } from 'importree';
import { importree, getAffectedFiles } from "importree";

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

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

console.log(affected);
// ['/abs/src/app.ts', '/abs/src/index.ts']
Expand All @@ -116,18 +116,18 @@ importree(entry: string, options?: ImportreeOptions): Promise<ImportTree>

#### Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `entry` | `string` | Yes | Path to the entry file (resolved against `cwd`) |
| `options` | `ImportreeOptions` | No | Configuration for resolution behavior |
| Parameter | Type | Required | Description |
| --------- | ------------------ | -------- | ----------------------------------------------- |
| `entry` | `string` | Yes | Path to the entry file (resolved against `cwd`) |
| `options` | `ImportreeOptions` | No | Configuration for resolution behavior |

#### `ImportreeOptions`

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `rootDir` | `string` | `process.cwd()` | Root directory for resolving relative alias paths |
| `aliases` | `Record<string, string>` | `{}` | Path alias mappings (e.g., `{ '@': './src' }`) |
| `extensions` | `string[]` | `['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs']` | File extensions to try when resolving extensionless imports |
| Option | Type | Default | Description |
| ------------ | ------------------------ | ------------------------------------------------ | ----------------------------------------------------------- |
| `rootDir` | `string` | `process.cwd()` | Root directory for resolving relative alias paths |
| `aliases` | `Record<string, string>` | `{}` | Path alias mappings (e.g., `{ '@': './src' }`) |
| `extensions` | `string[]` | `['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs']` | File extensions to try when resolving extensionless imports |

#### Returns

Expand All @@ -145,10 +145,10 @@ getAffectedFiles(tree: ImportTree, changedFile: string): string[]

#### Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `tree` | `ImportTree` | Yes | A tree previously returned by `importree()` |
| `changedFile` | `string` | Yes | Path to the file that changed (resolved to absolute) |
| Parameter | Type | Required | Description |
| ------------- | ------------ | -------- | ---------------------------------------------------- |
| `tree` | `ImportTree` | Yes | A tree previously returned by `importree()` |
| `changedFile` | `string` | Yes | Path to the file that changed (resolved to absolute) |

#### Returns

Expand All @@ -160,13 +160,13 @@ getAffectedFiles(tree: ImportTree, changedFile: string): string[]

The result object returned by `importree()`.

| Field | Type | Description |
|-------|------|-------------|
| `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. |
| Field | Type | Description |
| -------------- | -------------------------- | --------------------------------------------------------------------------------- |
| `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. |

## What gets detected

Expand Down
Loading
Loading