foresthouse 1.0.0-dev.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +72 -0
- package/dist/cli.d.mts +1 -0
- package/dist/cli.mjs +100 -0
- package/dist/cli.mjs.map +1 -0
- package/dist/index.d.mts +38 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +2 -0
- package/dist/tree-BiiNljTI.mjs +276 -0
- package/dist/tree-BiiNljTI.mjs.map +1 -0
- package/package.json +80 -0
package/README.md
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# foresthouse
|
|
2
|
+
|
|
3
|
+
`foresthouse` is a modern TypeScript-first Node.js CLI that starts from an entry file, follows local JavaScript and TypeScript imports, and prints the result as a dependency tree.
|
|
4
|
+
|
|
5
|
+
## Stack
|
|
6
|
+
|
|
7
|
+
- Node.js 24.14.0 LTS
|
|
8
|
+
- TypeScript 5.9
|
|
9
|
+
- `tsx` for fast TypeScript execution in development
|
|
10
|
+
- `tsdown` for fast ESM builds
|
|
11
|
+
- `Biome` for linting and formatting
|
|
12
|
+
- `Vitest` for tests
|
|
13
|
+
- `semantic-release` for automated pre-releases and releases
|
|
14
|
+
- npm publishing through `semantic-release` on GitHub Actions
|
|
15
|
+
|
|
16
|
+
## What it does
|
|
17
|
+
|
|
18
|
+
- Reads a JavaScript/TypeScript entry file (`.js`, `.jsx`, `.ts`, `.tsx`, `.mjs`, `.cjs`, `.mts`, `.cts`)
|
|
19
|
+
- Resolves local imports, re-exports, `require()`, and string-literal dynamic `import()`
|
|
20
|
+
- Honors the nearest `tsconfig.json` or `jsconfig.json`, including `baseUrl` and `paths`
|
|
21
|
+
- Prints a tree by default, or JSON with `--json`
|
|
22
|
+
|
|
23
|
+
## Usage
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
corepack enable
|
|
27
|
+
pnpm install
|
|
28
|
+
pnpm run build
|
|
29
|
+
node dist/cli.mjs src/index.ts
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### Example
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
node dist/cli.mjs src/main.ts --cwd test/fixtures/basic
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Output:
|
|
39
|
+
|
|
40
|
+
```text
|
|
41
|
+
src/main.ts
|
|
42
|
+
├─ src/app.tsx
|
|
43
|
+
│ ├─ src/shared/util.ts
|
|
44
|
+
│ └─ src/components/button.tsx
|
|
45
|
+
├─ src/widget-loader.ts
|
|
46
|
+
│ └─ [dynamic] src/widgets/chart.ts
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Options
|
|
50
|
+
|
|
51
|
+
- `--cwd <path>`: working directory for resolving the entry file and config
|
|
52
|
+
- `--config <path>`: use a specific `tsconfig.json` or `jsconfig.json`
|
|
53
|
+
- `--include-externals`: include packages and Node built-ins in the output
|
|
54
|
+
- `--json`: print a JSON tree instead of ASCII output
|
|
55
|
+
|
|
56
|
+
## Development
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
corepack enable
|
|
60
|
+
pnpm install
|
|
61
|
+
pnpm run check
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Collaboration And Release Flow
|
|
65
|
+
|
|
66
|
+
- `dev` is the pre-release branch and publishes `-dev.N` builds through `semantic-release`
|
|
67
|
+
- `main` is the stable release branch
|
|
68
|
+
- every code change starts from a GitHub issue and lands through a pull request
|
|
69
|
+
- all commits must follow Conventional Commits
|
|
70
|
+
- Biome is the only formatter and linter in this repository
|
|
71
|
+
- CI runs lint, typecheck, build, and release automation
|
|
72
|
+
- npm publishing is configured through `semantic-release` with `NPM_TOKEN` and GitHub Actions provenance
|
package/dist/cli.d.mts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { };
|
package/dist/cli.mjs
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { n as analyzeDependencies, r as graphToSerializableTree, t as printDependencyTree } from "./tree-BiiNljTI.mjs";
|
|
3
|
+
import { createRequire } from "node:module";
|
|
4
|
+
import process from "node:process";
|
|
5
|
+
//#region src/cli.ts
|
|
6
|
+
const { version } = createRequire(import.meta.url)("../package.json");
|
|
7
|
+
function main() {
|
|
8
|
+
try {
|
|
9
|
+
const options = parseArgs(process.argv.slice(2));
|
|
10
|
+
const graph = analyzeDependencies(options.entryFile, {
|
|
11
|
+
...options.cwd === void 0 ? {} : { cwd: options.cwd },
|
|
12
|
+
...options.configPath === void 0 ? {} : { configPath: options.configPath }
|
|
13
|
+
});
|
|
14
|
+
if (options.json) {
|
|
15
|
+
process.stdout.write(`${JSON.stringify(graphToSerializableTree(graph), null, 2)}\n`);
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
process.stdout.write(`${printDependencyTree(graph, {
|
|
19
|
+
cwd: options.cwd ?? graph.cwd,
|
|
20
|
+
includeExternals: options.includeExternals
|
|
21
|
+
})}\n`);
|
|
22
|
+
} catch (error) {
|
|
23
|
+
const message = error instanceof Error ? error.message : "Unknown error occurred.";
|
|
24
|
+
process.stderr.write(`foresthouse: ${message}\n`);
|
|
25
|
+
process.exitCode = 1;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
function parseArgs(argv) {
|
|
29
|
+
if (argv.includes("--version") || argv.includes("-v")) {
|
|
30
|
+
process.stdout.write(`${version}\n`);
|
|
31
|
+
process.exit(0);
|
|
32
|
+
}
|
|
33
|
+
if (argv.length === 0 || argv.includes("--help") || argv.includes("-h")) {
|
|
34
|
+
printHelp();
|
|
35
|
+
process.exit(0);
|
|
36
|
+
}
|
|
37
|
+
let entryFile;
|
|
38
|
+
let cwd;
|
|
39
|
+
let configPath;
|
|
40
|
+
let includeExternals = false;
|
|
41
|
+
let json = false;
|
|
42
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
43
|
+
const argument = argv[index];
|
|
44
|
+
if (argument === void 0) continue;
|
|
45
|
+
if (argument === "--cwd") {
|
|
46
|
+
cwd = readOptionValue(argv, index, "--cwd");
|
|
47
|
+
index += 1;
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
if (argument === "--config") {
|
|
51
|
+
configPath = readOptionValue(argv, index, "--config");
|
|
52
|
+
index += 1;
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
if (argument === "--include-externals") {
|
|
56
|
+
includeExternals = true;
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
if (argument === "--json") {
|
|
60
|
+
json = true;
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
if (argument.startsWith("-")) throw new Error(`Unknown option: ${argument}`);
|
|
64
|
+
if (entryFile !== void 0) throw new Error("Only one entry file can be provided.");
|
|
65
|
+
entryFile = argument;
|
|
66
|
+
}
|
|
67
|
+
if (entryFile === void 0) throw new Error("An entry file is required.");
|
|
68
|
+
return {
|
|
69
|
+
entryFile,
|
|
70
|
+
cwd,
|
|
71
|
+
configPath,
|
|
72
|
+
includeExternals,
|
|
73
|
+
json
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
function readOptionValue(argv, index, optionName) {
|
|
77
|
+
const value = argv[index + 1];
|
|
78
|
+
if (value === void 0 || value.startsWith("-")) throw new Error(`Missing value for ${optionName}`);
|
|
79
|
+
return value;
|
|
80
|
+
}
|
|
81
|
+
function printHelp() {
|
|
82
|
+
process.stdout.write(`foresthouse v${version}
|
|
83
|
+
|
|
84
|
+
Usage:
|
|
85
|
+
foresthouse <entry-file> [options]
|
|
86
|
+
|
|
87
|
+
Options:
|
|
88
|
+
--cwd <path> Working directory used for relative paths.
|
|
89
|
+
--config <path> Explicit tsconfig.json or jsconfig.json path.
|
|
90
|
+
--include-externals Include packages and Node built-ins in the tree.
|
|
91
|
+
--json Print the dependency tree as JSON.
|
|
92
|
+
-v, --version Show the current version.
|
|
93
|
+
-h, --help Show this help message.
|
|
94
|
+
`);
|
|
95
|
+
}
|
|
96
|
+
main();
|
|
97
|
+
//#endregion
|
|
98
|
+
export {};
|
|
99
|
+
|
|
100
|
+
//# sourceMappingURL=cli.mjs.map
|
package/dist/cli.mjs.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.mjs","names":[],"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { createRequire } from 'node:module'\nimport process from 'node:process'\n\nimport { analyzeDependencies, graphToSerializableTree } from './analyzer.js'\nimport { printDependencyTree } from './tree.js'\n\nconst require = createRequire(import.meta.url)\nconst { version } = require('../package.json') as { version: string }\n\ninterface CliOptions {\n readonly entryFile: string\n readonly cwd: string | undefined\n readonly configPath: string | undefined\n readonly includeExternals: boolean\n readonly json: boolean\n}\n\nfunction main(): void {\n try {\n const options = parseArgs(process.argv.slice(2))\n const graph = analyzeDependencies(options.entryFile, {\n ...(options.cwd === undefined ? {} : { cwd: options.cwd }),\n ...(options.configPath === undefined\n ? {}\n : { configPath: options.configPath }),\n })\n\n if (options.json) {\n process.stdout.write(\n `${JSON.stringify(graphToSerializableTree(graph), null, 2)}\\n`,\n )\n return\n }\n\n process.stdout.write(\n `${printDependencyTree(graph, {\n cwd: options.cwd ?? graph.cwd,\n includeExternals: options.includeExternals,\n })}\\n`,\n )\n } catch (error) {\n const message =\n error instanceof Error ? error.message : 'Unknown error occurred.'\n process.stderr.write(`foresthouse: ${message}\\n`)\n process.exitCode = 1\n }\n}\n\nfunction parseArgs(argv: string[]): CliOptions {\n if (argv.includes('--version') || argv.includes('-v')) {\n process.stdout.write(`${version}\\n`)\n process.exit(0)\n }\n\n if (argv.length === 0 || argv.includes('--help') || argv.includes('-h')) {\n printHelp()\n process.exit(0)\n }\n\n let entryFile: string | undefined\n let cwd: string | undefined\n let configPath: string | undefined\n let includeExternals = false\n let json = false\n\n for (let index = 0; index < argv.length; index += 1) {\n const argument = argv[index]\n if (argument === undefined) {\n continue\n }\n\n if (argument === '--cwd') {\n cwd = readOptionValue(argv, index, '--cwd')\n index += 1\n continue\n }\n\n if (argument === '--config') {\n configPath = readOptionValue(argv, index, '--config')\n index += 1\n continue\n }\n\n if (argument === '--include-externals') {\n includeExternals = true\n continue\n }\n\n if (argument === '--json') {\n json = true\n continue\n }\n\n if (argument.startsWith('-')) {\n throw new Error(`Unknown option: ${argument}`)\n }\n\n if (entryFile !== undefined) {\n throw new Error('Only one entry file can be provided.')\n }\n\n entryFile = argument\n }\n\n if (entryFile === undefined) {\n throw new Error('An entry file is required.')\n }\n\n return {\n entryFile,\n cwd,\n configPath,\n includeExternals,\n json,\n }\n}\n\nfunction readOptionValue(\n argv: string[],\n index: number,\n optionName: string,\n): string {\n const value = argv[index + 1]\n if (value === undefined || value.startsWith('-')) {\n throw new Error(`Missing value for ${optionName}`)\n }\n\n return value\n}\n\nfunction printHelp(): void {\n process.stdout.write(`foresthouse v${version}\n\nUsage:\n foresthouse <entry-file> [options]\n\nOptions:\n --cwd <path> Working directory used for relative paths.\n --config <path> Explicit tsconfig.json or jsconfig.json path.\n --include-externals Include packages and Node built-ins in the tree.\n --json Print the dependency tree as JSON.\n -v, --version Show the current version.\n -h, --help Show this help message.\n`)\n}\n\nmain()\n"],"mappings":";;;;;AASA,MAAM,EAAE,YADQ,cAAc,OAAO,KAAK,IAAI,CAClB,kBAAkB;AAU9C,SAAS,OAAa;AACpB,KAAI;EACF,MAAM,UAAU,UAAU,QAAQ,KAAK,MAAM,EAAE,CAAC;EAChD,MAAM,QAAQ,oBAAoB,QAAQ,WAAW;GACnD,GAAI,QAAQ,QAAQ,KAAA,IAAY,EAAE,GAAG,EAAE,KAAK,QAAQ,KAAK;GACzD,GAAI,QAAQ,eAAe,KAAA,IACvB,EAAE,GACF,EAAE,YAAY,QAAQ,YAAY;GACvC,CAAC;AAEF,MAAI,QAAQ,MAAM;AAChB,WAAQ,OAAO,MACb,GAAG,KAAK,UAAU,wBAAwB,MAAM,EAAE,MAAM,EAAE,CAAC,IAC5D;AACD;;AAGF,UAAQ,OAAO,MACb,GAAG,oBAAoB,OAAO;GAC5B,KAAK,QAAQ,OAAO,MAAM;GAC1B,kBAAkB,QAAQ;GAC3B,CAAC,CAAC,IACJ;UACM,OAAO;EACd,MAAM,UACJ,iBAAiB,QAAQ,MAAM,UAAU;AAC3C,UAAQ,OAAO,MAAM,gBAAgB,QAAQ,IAAI;AACjD,UAAQ,WAAW;;;AAIvB,SAAS,UAAU,MAA4B;AAC7C,KAAI,KAAK,SAAS,YAAY,IAAI,KAAK,SAAS,KAAK,EAAE;AACrD,UAAQ,OAAO,MAAM,GAAG,QAAQ,IAAI;AACpC,UAAQ,KAAK,EAAE;;AAGjB,KAAI,KAAK,WAAW,KAAK,KAAK,SAAS,SAAS,IAAI,KAAK,SAAS,KAAK,EAAE;AACvE,aAAW;AACX,UAAQ,KAAK,EAAE;;CAGjB,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI,mBAAmB;CACvB,IAAI,OAAO;AAEX,MAAK,IAAI,QAAQ,GAAG,QAAQ,KAAK,QAAQ,SAAS,GAAG;EACnD,MAAM,WAAW,KAAK;AACtB,MAAI,aAAa,KAAA,EACf;AAGF,MAAI,aAAa,SAAS;AACxB,SAAM,gBAAgB,MAAM,OAAO,QAAQ;AAC3C,YAAS;AACT;;AAGF,MAAI,aAAa,YAAY;AAC3B,gBAAa,gBAAgB,MAAM,OAAO,WAAW;AACrD,YAAS;AACT;;AAGF,MAAI,aAAa,uBAAuB;AACtC,sBAAmB;AACnB;;AAGF,MAAI,aAAa,UAAU;AACzB,UAAO;AACP;;AAGF,MAAI,SAAS,WAAW,IAAI,CAC1B,OAAM,IAAI,MAAM,mBAAmB,WAAW;AAGhD,MAAI,cAAc,KAAA,EAChB,OAAM,IAAI,MAAM,uCAAuC;AAGzD,cAAY;;AAGd,KAAI,cAAc,KAAA,EAChB,OAAM,IAAI,MAAM,6BAA6B;AAG/C,QAAO;EACL;EACA;EACA;EACA;EACA;EACD;;AAGH,SAAS,gBACP,MACA,OACA,YACQ;CACR,MAAM,QAAQ,KAAK,QAAQ;AAC3B,KAAI,UAAU,KAAA,KAAa,MAAM,WAAW,IAAI,CAC9C,OAAM,IAAI,MAAM,qBAAqB,aAAa;AAGpD,QAAO;;AAGT,SAAS,YAAkB;AACzB,SAAQ,OAAO,MAAM,gBAAgB,QAAQ;;;;;;;;;;;;EAY7C;;AAGF,MAAM"}
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
//#region src/types.d.ts
|
|
2
|
+
type DependencyKind = 'source' | 'external' | 'builtin' | 'missing';
|
|
3
|
+
type ReferenceKind = 'import' | 'export' | 'require' | 'dynamic-import' | 'import-equals';
|
|
4
|
+
interface DependencyEdge {
|
|
5
|
+
readonly specifier: string;
|
|
6
|
+
readonly referenceKind: ReferenceKind;
|
|
7
|
+
readonly isTypeOnly: boolean;
|
|
8
|
+
readonly kind: DependencyKind;
|
|
9
|
+
readonly target: string;
|
|
10
|
+
}
|
|
11
|
+
interface SourceModuleNode {
|
|
12
|
+
readonly id: string;
|
|
13
|
+
readonly dependencies: readonly DependencyEdge[];
|
|
14
|
+
}
|
|
15
|
+
interface DependencyGraph {
|
|
16
|
+
readonly cwd: string;
|
|
17
|
+
readonly entryId: string;
|
|
18
|
+
readonly nodes: ReadonlyMap<string, SourceModuleNode>;
|
|
19
|
+
readonly configPath?: string;
|
|
20
|
+
}
|
|
21
|
+
interface AnalyzeOptions {
|
|
22
|
+
readonly cwd?: string;
|
|
23
|
+
readonly configPath?: string;
|
|
24
|
+
}
|
|
25
|
+
interface PrintTreeOptions {
|
|
26
|
+
readonly cwd?: string;
|
|
27
|
+
readonly includeExternals?: boolean;
|
|
28
|
+
}
|
|
29
|
+
//#endregion
|
|
30
|
+
//#region src/analyzer.d.ts
|
|
31
|
+
declare function analyzeDependencies(entryFile: string, options?: AnalyzeOptions): DependencyGraph;
|
|
32
|
+
declare function graphToSerializableTree(graph: DependencyGraph): object;
|
|
33
|
+
//#endregion
|
|
34
|
+
//#region src/tree.d.ts
|
|
35
|
+
declare function printDependencyTree(graph: DependencyGraph, options?: PrintTreeOptions): string;
|
|
36
|
+
//#endregion
|
|
37
|
+
export { type AnalyzeOptions, type DependencyEdge, type DependencyGraph, type DependencyKind, type PrintTreeOptions, type ReferenceKind, type SourceModuleNode, analyzeDependencies, graphToSerializableTree, printDependencyTree };
|
|
38
|
+
//# sourceMappingURL=index.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/types.ts","../src/analyzer.ts","../src/tree.ts"],"mappings":";KAAY,cAAA;AAAA,KAEA,aAAA;AAAA,UAOK,cAAA;EAAA,SACN,SAAA;EAAA,SACA,aAAA,EAAe,aAAA;EAAA,SACf,UAAA;EAAA,SACA,IAAA,EAAM,cAAA;EAAA,SACN,MAAA;AAAA;AAAA,UAGM,gBAAA;EAAA,SACN,EAAA;EAAA,SACA,YAAA,WAAuB,cAAA;AAAA;AAAA,UAGjB,eAAA;EAAA,SACN,GAAA;EAAA,SACA,OAAA;EAAA,SACA,KAAA,EAAO,WAAA,SAAoB,gBAAA;EAAA,SAC3B,UAAA;AAAA;AAAA,UAGM,cAAA;EAAA,SACN,GAAA;EAAA,SACA,UAAA;AAAA;AAAA,UAGM,gBAAA;EAAA,SACN,GAAA;EAAA,SACA,gBAAA;AAAA;;;iBCNK,mBAAA,CACd,SAAA,UACA,OAAA,GAAS,cAAA,GACR,eAAA;AAAA,iBA4Ba,uBAAA,CAAwB,KAAA,EAAO,eAAA;;;iBCtD/B,mBAAA,CACd,KAAA,EAAO,eAAA,EACP,OAAA,GAAS,gBAAA"}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
import { builtinModules } from "node:module";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import ts from "typescript";
|
|
5
|
+
//#region src/config.ts
|
|
6
|
+
function loadCompilerOptions(searchFrom, explicitConfigPath) {
|
|
7
|
+
const configPath = explicitConfigPath === void 0 ? findNearestConfig(searchFrom) : path.resolve(searchFrom, explicitConfigPath);
|
|
8
|
+
if (configPath === void 0) return { compilerOptions: defaultCompilerOptions() };
|
|
9
|
+
const readResult = ts.readConfigFile(configPath, ts.sys.readFile);
|
|
10
|
+
if (readResult.error !== void 0) throw new Error(`Failed to read TypeScript config at ${configPath}: ${formatDiagnostic(readResult.error)}`);
|
|
11
|
+
const parsed = ts.parseJsonConfigFileContent(readResult.config, ts.sys, path.dirname(configPath), defaultCompilerOptions(), configPath);
|
|
12
|
+
if (parsed.errors.length > 0) {
|
|
13
|
+
const [firstError] = parsed.errors;
|
|
14
|
+
if (firstError === void 0) throw new Error(`Failed to parse TypeScript config at ${configPath}.`);
|
|
15
|
+
throw new Error(`Failed to parse TypeScript config at ${configPath}: ${formatDiagnostic(firstError)}`);
|
|
16
|
+
}
|
|
17
|
+
return {
|
|
18
|
+
path: configPath,
|
|
19
|
+
compilerOptions: parsed.options
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
function findNearestConfig(searchFrom) {
|
|
23
|
+
let currentDirectory = path.resolve(searchFrom);
|
|
24
|
+
while (true) {
|
|
25
|
+
const tsconfigPath = path.join(currentDirectory, "tsconfig.json");
|
|
26
|
+
if (ts.sys.fileExists(tsconfigPath)) return tsconfigPath;
|
|
27
|
+
const jsconfigPath = path.join(currentDirectory, "jsconfig.json");
|
|
28
|
+
if (ts.sys.fileExists(jsconfigPath)) return jsconfigPath;
|
|
29
|
+
const parentDirectory = path.dirname(currentDirectory);
|
|
30
|
+
if (parentDirectory === currentDirectory) return;
|
|
31
|
+
currentDirectory = parentDirectory;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
function defaultCompilerOptions() {
|
|
35
|
+
return {
|
|
36
|
+
allowJs: true,
|
|
37
|
+
jsx: ts.JsxEmit.ReactJSX,
|
|
38
|
+
module: ts.ModuleKind.NodeNext,
|
|
39
|
+
moduleResolution: ts.ModuleResolutionKind.NodeNext,
|
|
40
|
+
target: ts.ScriptTarget.ESNext,
|
|
41
|
+
resolveJsonModule: true,
|
|
42
|
+
esModuleInterop: true
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
function formatDiagnostic(diagnostic) {
|
|
46
|
+
return ts.flattenDiagnosticMessageText(diagnostic.messageText, "\n");
|
|
47
|
+
}
|
|
48
|
+
//#endregion
|
|
49
|
+
//#region src/path-utils.ts
|
|
50
|
+
const SOURCE_EXTENSIONS = new Set([
|
|
51
|
+
".js",
|
|
52
|
+
".jsx",
|
|
53
|
+
".ts",
|
|
54
|
+
".tsx",
|
|
55
|
+
".mjs",
|
|
56
|
+
".cjs",
|
|
57
|
+
".mts",
|
|
58
|
+
".cts"
|
|
59
|
+
]);
|
|
60
|
+
function normalizeFilePath(filePath) {
|
|
61
|
+
return path.normalize(filePath);
|
|
62
|
+
}
|
|
63
|
+
function toDisplayPath(filePath, cwd) {
|
|
64
|
+
const relativePath = path.relative(cwd, filePath);
|
|
65
|
+
if (relativePath === "") return ".";
|
|
66
|
+
const normalizedPath = relativePath.split(path.sep).join("/");
|
|
67
|
+
return normalizedPath.startsWith("..") ? filePath : normalizedPath;
|
|
68
|
+
}
|
|
69
|
+
function isSourceCodeFile(filePath) {
|
|
70
|
+
return SOURCE_EXTENSIONS.has(path.extname(filePath).toLowerCase());
|
|
71
|
+
}
|
|
72
|
+
//#endregion
|
|
73
|
+
//#region src/analyzer.ts
|
|
74
|
+
const BUILTIN_MODULES = new Set(builtinModules.flatMap((name) => [name, `node:${name}`]));
|
|
75
|
+
function analyzeDependencies(entryFile, options = {}) {
|
|
76
|
+
const cwd = path.resolve(options.cwd ?? process.cwd());
|
|
77
|
+
const resolvedEntryPath = resolveExistingPath(cwd, entryFile);
|
|
78
|
+
const { compilerOptions, path: configPath } = loadCompilerOptions(path.dirname(resolvedEntryPath), options.configPath);
|
|
79
|
+
const host = {
|
|
80
|
+
fileExists: ts.sys.fileExists,
|
|
81
|
+
readFile: ts.sys.readFile,
|
|
82
|
+
directoryExists: ts.sys.directoryExists,
|
|
83
|
+
getCurrentDirectory: () => cwd,
|
|
84
|
+
getDirectories: ts.sys.getDirectories,
|
|
85
|
+
...ts.sys.realpath === void 0 ? {} : { realpath: ts.sys.realpath }
|
|
86
|
+
};
|
|
87
|
+
const nodes = /* @__PURE__ */ new Map();
|
|
88
|
+
visitFile(resolvedEntryPath, compilerOptions, host, nodes);
|
|
89
|
+
return {
|
|
90
|
+
cwd,
|
|
91
|
+
entryId: resolvedEntryPath,
|
|
92
|
+
nodes,
|
|
93
|
+
...configPath === void 0 ? {} : { configPath }
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
function graphToSerializableTree(graph) {
|
|
97
|
+
const visited = /* @__PURE__ */ new Set();
|
|
98
|
+
return serializeNode(graph.entryId, graph, visited);
|
|
99
|
+
}
|
|
100
|
+
function serializeNode(filePath, graph, visited) {
|
|
101
|
+
const node = graph.nodes.get(filePath);
|
|
102
|
+
const displayPath = toDisplayPath(filePath, graph.cwd);
|
|
103
|
+
if (node === void 0) return {
|
|
104
|
+
path: displayPath,
|
|
105
|
+
kind: "missing",
|
|
106
|
+
dependencies: []
|
|
107
|
+
};
|
|
108
|
+
if (visited.has(filePath)) return {
|
|
109
|
+
path: displayPath,
|
|
110
|
+
kind: "circular",
|
|
111
|
+
dependencies: []
|
|
112
|
+
};
|
|
113
|
+
visited.add(filePath);
|
|
114
|
+
const dependencies = node.dependencies.map((dependency) => {
|
|
115
|
+
if (dependency.kind !== "source") return {
|
|
116
|
+
specifier: dependency.specifier,
|
|
117
|
+
referenceKind: dependency.referenceKind,
|
|
118
|
+
isTypeOnly: dependency.isTypeOnly,
|
|
119
|
+
kind: dependency.kind,
|
|
120
|
+
target: dependency.kind === "missing" ? dependency.target : toDisplayPath(dependency.target, graph.cwd)
|
|
121
|
+
};
|
|
122
|
+
return {
|
|
123
|
+
specifier: dependency.specifier,
|
|
124
|
+
referenceKind: dependency.referenceKind,
|
|
125
|
+
isTypeOnly: dependency.isTypeOnly,
|
|
126
|
+
kind: dependency.kind,
|
|
127
|
+
target: toDisplayPath(dependency.target, graph.cwd),
|
|
128
|
+
node: serializeNode(dependency.target, graph, new Set(visited))
|
|
129
|
+
};
|
|
130
|
+
});
|
|
131
|
+
return {
|
|
132
|
+
path: displayPath,
|
|
133
|
+
kind: filePath === graph.entryId ? "entry" : "source",
|
|
134
|
+
dependencies
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
function visitFile(filePath, compilerOptions, host, nodes) {
|
|
138
|
+
const normalizedPath = normalizeFilePath(filePath);
|
|
139
|
+
if (nodes.has(normalizedPath)) return;
|
|
140
|
+
const sourceText = fs.readFileSync(normalizedPath, "utf8");
|
|
141
|
+
const dependencies = collectModuleReferences(ts.createSourceFile(normalizedPath, sourceText, ts.ScriptTarget.Latest, true, getScriptKind(normalizedPath))).map((reference) => resolveDependency(reference, normalizedPath, compilerOptions, host));
|
|
142
|
+
nodes.set(normalizedPath, {
|
|
143
|
+
id: normalizedPath,
|
|
144
|
+
dependencies
|
|
145
|
+
});
|
|
146
|
+
for (const dependency of dependencies) if (dependency.kind === "source") visitFile(dependency.target, compilerOptions, host, nodes);
|
|
147
|
+
}
|
|
148
|
+
function collectModuleReferences(sourceFile) {
|
|
149
|
+
const references = [];
|
|
150
|
+
const seen = /* @__PURE__ */ new Set();
|
|
151
|
+
function addReference(specifier, referenceKind, isTypeOnly) {
|
|
152
|
+
const key = `${referenceKind}:${isTypeOnly ? "type" : "value"}:${specifier}`;
|
|
153
|
+
if (seen.has(key)) return;
|
|
154
|
+
seen.add(key);
|
|
155
|
+
references.push({
|
|
156
|
+
specifier,
|
|
157
|
+
referenceKind,
|
|
158
|
+
isTypeOnly
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
function visit(node) {
|
|
162
|
+
if (ts.isImportDeclaration(node) && ts.isStringLiteralLike(node.moduleSpecifier)) addReference(node.moduleSpecifier.text, "import", node.importClause?.isTypeOnly ?? false);
|
|
163
|
+
else if (ts.isExportDeclaration(node) && node.moduleSpecifier !== void 0 && ts.isStringLiteralLike(node.moduleSpecifier)) addReference(node.moduleSpecifier.text, "export", node.isTypeOnly ?? false);
|
|
164
|
+
else if (ts.isImportEqualsDeclaration(node)) {
|
|
165
|
+
const moduleReference = node.moduleReference;
|
|
166
|
+
if (ts.isExternalModuleReference(moduleReference) && moduleReference.expression !== void 0 && ts.isStringLiteralLike(moduleReference.expression)) addReference(moduleReference.expression.text, "import-equals", false);
|
|
167
|
+
} else if (ts.isCallExpression(node)) {
|
|
168
|
+
if (node.expression.kind === ts.SyntaxKind.ImportKeyword && node.arguments.length === 1) {
|
|
169
|
+
const [argument] = node.arguments;
|
|
170
|
+
if (argument !== void 0 && ts.isStringLiteralLike(argument)) addReference(argument.text, "dynamic-import", false);
|
|
171
|
+
}
|
|
172
|
+
if (ts.isIdentifier(node.expression) && node.expression.text === "require" && node.arguments.length === 1) {
|
|
173
|
+
const [argument] = node.arguments;
|
|
174
|
+
if (argument !== void 0 && ts.isStringLiteralLike(argument)) addReference(argument.text, "require", false);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
ts.forEachChild(node, visit);
|
|
178
|
+
}
|
|
179
|
+
visit(sourceFile);
|
|
180
|
+
return references;
|
|
181
|
+
}
|
|
182
|
+
function resolveDependency(reference, containingFile, compilerOptions, host) {
|
|
183
|
+
const specifier = reference.specifier;
|
|
184
|
+
if (BUILTIN_MODULES.has(specifier)) return createEdge(reference, "builtin", specifier);
|
|
185
|
+
const resolution = ts.resolveModuleName(specifier, containingFile, compilerOptions, host).resolvedModule;
|
|
186
|
+
if (resolution !== void 0) {
|
|
187
|
+
const resolvedPath = normalizeFilePath(resolution.resolvedFileName);
|
|
188
|
+
if (resolution.isExternalLibraryImport || resolvedPath.includes(`${path.sep}node_modules${path.sep}`)) return createEdge(reference, "external", specifier);
|
|
189
|
+
if (isSourceCodeFile(resolvedPath) && !resolvedPath.endsWith(".d.ts")) return createEdge(reference, "source", resolvedPath);
|
|
190
|
+
}
|
|
191
|
+
if (!specifier.startsWith(".") && !path.isAbsolute(specifier)) return createEdge(reference, "external", specifier);
|
|
192
|
+
return createEdge(reference, "missing", specifier);
|
|
193
|
+
}
|
|
194
|
+
function createEdge(reference, kind, target) {
|
|
195
|
+
return {
|
|
196
|
+
specifier: reference.specifier,
|
|
197
|
+
referenceKind: reference.referenceKind,
|
|
198
|
+
isTypeOnly: reference.isTypeOnly,
|
|
199
|
+
kind,
|
|
200
|
+
target
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
function getScriptKind(filePath) {
|
|
204
|
+
switch (path.extname(filePath).toLowerCase()) {
|
|
205
|
+
case ".js":
|
|
206
|
+
case ".mjs":
|
|
207
|
+
case ".cjs": return ts.ScriptKind.JS;
|
|
208
|
+
case ".jsx": return ts.ScriptKind.JSX;
|
|
209
|
+
case ".tsx": return ts.ScriptKind.TSX;
|
|
210
|
+
case ".json": return ts.ScriptKind.JSON;
|
|
211
|
+
default: return ts.ScriptKind.TS;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
function resolveExistingPath(cwd, entryFile) {
|
|
215
|
+
const normalizedPath = normalizeFilePath(path.resolve(cwd, entryFile));
|
|
216
|
+
if (!fs.existsSync(normalizedPath)) throw new Error(`Entry file not found: ${entryFile}`);
|
|
217
|
+
if (!isSourceCodeFile(normalizedPath)) throw new Error(`Entry file must be a JS/TS source file: ${entryFile}`);
|
|
218
|
+
return normalizedPath;
|
|
219
|
+
}
|
|
220
|
+
//#endregion
|
|
221
|
+
//#region src/tree.ts
|
|
222
|
+
function printDependencyTree(graph, options = {}) {
|
|
223
|
+
const cwd = options.cwd ?? graph.cwd;
|
|
224
|
+
const includeExternals = options.includeExternals ?? false;
|
|
225
|
+
const rootLines = [toDisplayPath(graph.entryId, cwd)];
|
|
226
|
+
const visited = new Set([graph.entryId]);
|
|
227
|
+
const entryNode = graph.nodes.get(graph.entryId);
|
|
228
|
+
if (entryNode === void 0) return rootLines.join("\n");
|
|
229
|
+
const rootDependencies = filterDependencies(entryNode.dependencies, includeExternals);
|
|
230
|
+
rootDependencies.forEach((dependency, index) => {
|
|
231
|
+
const lines = renderDependency(dependency, graph, visited, "", index === rootDependencies.length - 1, includeExternals, cwd);
|
|
232
|
+
rootLines.push(...lines);
|
|
233
|
+
});
|
|
234
|
+
return rootLines.join("\n");
|
|
235
|
+
}
|
|
236
|
+
function renderDependency(dependency, graph, visited, prefix, isLast, includeExternals, cwd) {
|
|
237
|
+
const branch = `${prefix}${isLast ? "└─ " : "├─ "}`;
|
|
238
|
+
const label = formatDependencyLabel(dependency, graph, cwd);
|
|
239
|
+
if (dependency.kind !== "source") return [`${branch}${label}`];
|
|
240
|
+
if (visited.has(dependency.target)) return [`${branch}${label} (circular)`];
|
|
241
|
+
const childNode = graph.nodes.get(dependency.target);
|
|
242
|
+
if (childNode === void 0) return [`${branch}${label}`];
|
|
243
|
+
const nextPrefix = `${prefix}${isLast ? " " : "│ "}`;
|
|
244
|
+
const nextVisited = new Set(visited);
|
|
245
|
+
nextVisited.add(dependency.target);
|
|
246
|
+
const childLines = [`${branch}${label}`];
|
|
247
|
+
const childDependencies = filterDependencies(childNode.dependencies, includeExternals);
|
|
248
|
+
childDependencies.forEach((childDependency, index) => {
|
|
249
|
+
const isChildLast = index === childDependencies.length - 1;
|
|
250
|
+
childLines.push(...renderDependency(childDependency, graph, nextVisited, nextPrefix, isChildLast, includeExternals, cwd));
|
|
251
|
+
});
|
|
252
|
+
return childLines;
|
|
253
|
+
}
|
|
254
|
+
function filterDependencies(dependencies, includeExternals) {
|
|
255
|
+
return dependencies.filter((dependency) => {
|
|
256
|
+
if (dependency.kind === "source" || dependency.kind === "missing") return true;
|
|
257
|
+
return includeExternals;
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
function formatDependencyLabel(dependency, _graph, cwd) {
|
|
261
|
+
const prefixes = [];
|
|
262
|
+
if (dependency.isTypeOnly) prefixes.push("type");
|
|
263
|
+
if (dependency.referenceKind === "require") prefixes.push("require");
|
|
264
|
+
else if (dependency.referenceKind === "dynamic-import") prefixes.push("dynamic");
|
|
265
|
+
else if (dependency.referenceKind === "export") prefixes.push("re-export");
|
|
266
|
+
else if (dependency.referenceKind === "import-equals") prefixes.push("import=");
|
|
267
|
+
const annotation = prefixes.length > 0 ? `[${prefixes.join(", ")}] ` : "";
|
|
268
|
+
if (dependency.kind === "source") return `${annotation}${toDisplayPath(dependency.target, cwd)}`;
|
|
269
|
+
if (dependency.kind === "missing") return `${annotation}${dependency.specifier} [missing]`;
|
|
270
|
+
if (dependency.kind === "builtin") return `${annotation}${dependency.target} [builtin]`;
|
|
271
|
+
return `${annotation}${dependency.target} [external]`;
|
|
272
|
+
}
|
|
273
|
+
//#endregion
|
|
274
|
+
export { analyzeDependencies as n, graphToSerializableTree as r, printDependencyTree as t };
|
|
275
|
+
|
|
276
|
+
//# sourceMappingURL=tree-BiiNljTI.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tree-BiiNljTI.mjs","names":[],"sources":["../src/config.ts","../src/path-utils.ts","../src/analyzer.ts","../src/tree.ts"],"sourcesContent":["import path from 'node:path'\nimport ts from 'typescript'\n\nexport interface LoadedConfig {\n readonly path?: string\n readonly compilerOptions: ts.CompilerOptions\n}\n\nexport function loadCompilerOptions(\n searchFrom: string,\n explicitConfigPath?: string,\n): LoadedConfig {\n const configPath =\n explicitConfigPath === undefined\n ? findNearestConfig(searchFrom)\n : path.resolve(searchFrom, explicitConfigPath)\n\n if (configPath === undefined) {\n return {\n compilerOptions: defaultCompilerOptions(),\n }\n }\n\n const readResult = ts.readConfigFile(configPath, ts.sys.readFile)\n if (readResult.error !== undefined) {\n throw new Error(\n `Failed to read TypeScript config at ${configPath}: ${formatDiagnostic(\n readResult.error,\n )}`,\n )\n }\n\n const parsed = ts.parseJsonConfigFileContent(\n readResult.config,\n ts.sys,\n path.dirname(configPath),\n defaultCompilerOptions(),\n configPath,\n )\n\n if (parsed.errors.length > 0) {\n const [firstError] = parsed.errors\n if (firstError === undefined) {\n throw new Error(`Failed to parse TypeScript config at ${configPath}.`)\n }\n\n throw new Error(\n `Failed to parse TypeScript config at ${configPath}: ${formatDiagnostic(\n firstError,\n )}`,\n )\n }\n\n return {\n path: configPath,\n compilerOptions: parsed.options,\n }\n}\n\nfunction findNearestConfig(searchFrom: string): string | undefined {\n let currentDirectory = path.resolve(searchFrom)\n\n while (true) {\n const tsconfigPath = path.join(currentDirectory, 'tsconfig.json')\n if (ts.sys.fileExists(tsconfigPath)) {\n return tsconfigPath\n }\n\n const jsconfigPath = path.join(currentDirectory, 'jsconfig.json')\n if (ts.sys.fileExists(jsconfigPath)) {\n return jsconfigPath\n }\n\n const parentDirectory = path.dirname(currentDirectory)\n if (parentDirectory === currentDirectory) {\n return undefined\n }\n\n currentDirectory = parentDirectory\n }\n}\n\nfunction defaultCompilerOptions(): ts.CompilerOptions {\n return {\n allowJs: true,\n jsx: ts.JsxEmit.ReactJSX,\n module: ts.ModuleKind.NodeNext,\n moduleResolution: ts.ModuleResolutionKind.NodeNext,\n target: ts.ScriptTarget.ESNext,\n resolveJsonModule: true,\n esModuleInterop: true,\n }\n}\n\nfunction formatDiagnostic(diagnostic: ts.Diagnostic): string {\n return ts.flattenDiagnosticMessageText(diagnostic.messageText, '\\n')\n}\n","import path from 'node:path'\n\nexport const SOURCE_EXTENSIONS = new Set([\n '.js',\n '.jsx',\n '.ts',\n '.tsx',\n '.mjs',\n '.cjs',\n '.mts',\n '.cts',\n])\n\nexport function normalizeFilePath(filePath: string): string {\n return path.normalize(filePath)\n}\n\nexport function toDisplayPath(filePath: string, cwd: string): string {\n const relativePath = path.relative(cwd, filePath)\n if (relativePath === '') {\n return '.'\n }\n\n const normalizedPath = relativePath.split(path.sep).join('/')\n return normalizedPath.startsWith('..') ? filePath : normalizedPath\n}\n\nexport function isSourceCodeFile(filePath: string): boolean {\n return SOURCE_EXTENSIONS.has(path.extname(filePath).toLowerCase())\n}\n","import fs from 'node:fs'\nimport { builtinModules } from 'node:module'\nimport path from 'node:path'\nimport ts from 'typescript'\n\nimport { loadCompilerOptions } from './config.js'\nimport {\n isSourceCodeFile,\n normalizeFilePath,\n toDisplayPath,\n} from './path-utils.js'\nimport type {\n AnalyzeOptions,\n DependencyEdge,\n DependencyGraph,\n DependencyKind,\n ReferenceKind,\n SourceModuleNode,\n} from './types.js'\n\ninterface ModuleReference {\n readonly specifier: string\n readonly referenceKind: ReferenceKind\n readonly isTypeOnly: boolean\n}\n\nconst BUILTIN_MODULES = new Set(\n builtinModules.flatMap((name) => [name, `node:${name}`]),\n)\n\nexport function analyzeDependencies(\n entryFile: string,\n options: AnalyzeOptions = {},\n): DependencyGraph {\n const cwd = path.resolve(options.cwd ?? process.cwd())\n const resolvedEntryPath = resolveExistingPath(cwd, entryFile)\n const { compilerOptions, path: configPath } = loadCompilerOptions(\n path.dirname(resolvedEntryPath),\n options.configPath,\n )\n\n const host: ts.ModuleResolutionHost = {\n fileExists: ts.sys.fileExists,\n readFile: ts.sys.readFile,\n directoryExists: ts.sys.directoryExists,\n getCurrentDirectory: () => cwd,\n getDirectories: ts.sys.getDirectories,\n ...(ts.sys.realpath === undefined ? {} : { realpath: ts.sys.realpath }),\n }\n\n const nodes = new Map<string, SourceModuleNode>()\n visitFile(resolvedEntryPath, compilerOptions, host, nodes)\n\n return {\n cwd,\n entryId: resolvedEntryPath,\n nodes,\n ...(configPath === undefined ? {} : { configPath }),\n }\n}\n\nexport function graphToSerializableTree(graph: DependencyGraph): object {\n const visited = new Set<string>()\n return serializeNode(graph.entryId, graph, visited)\n}\n\nfunction serializeNode(\n filePath: string,\n graph: DependencyGraph,\n visited: Set<string>,\n): object {\n const node = graph.nodes.get(filePath)\n const displayPath = toDisplayPath(filePath, graph.cwd)\n\n if (node === undefined) {\n return {\n path: displayPath,\n kind: 'missing',\n dependencies: [],\n }\n }\n\n if (visited.has(filePath)) {\n return {\n path: displayPath,\n kind: 'circular',\n dependencies: [],\n }\n }\n\n visited.add(filePath)\n\n const dependencies = node.dependencies.map((dependency) => {\n if (dependency.kind !== 'source') {\n return {\n specifier: dependency.specifier,\n referenceKind: dependency.referenceKind,\n isTypeOnly: dependency.isTypeOnly,\n kind: dependency.kind,\n target:\n dependency.kind === 'missing'\n ? dependency.target\n : toDisplayPath(dependency.target, graph.cwd),\n }\n }\n\n return {\n specifier: dependency.specifier,\n referenceKind: dependency.referenceKind,\n isTypeOnly: dependency.isTypeOnly,\n kind: dependency.kind,\n target: toDisplayPath(dependency.target, graph.cwd),\n node: serializeNode(dependency.target, graph, new Set(visited)),\n }\n })\n\n return {\n path: displayPath,\n kind: filePath === graph.entryId ? 'entry' : 'source',\n dependencies,\n }\n}\n\nfunction visitFile(\n filePath: string,\n compilerOptions: ts.CompilerOptions,\n host: ts.ModuleResolutionHost,\n nodes: Map<string, SourceModuleNode>,\n): void {\n const normalizedPath = normalizeFilePath(filePath)\n if (nodes.has(normalizedPath)) {\n return\n }\n\n const sourceText = fs.readFileSync(normalizedPath, 'utf8')\n const sourceFile = ts.createSourceFile(\n normalizedPath,\n sourceText,\n ts.ScriptTarget.Latest,\n true,\n getScriptKind(normalizedPath),\n )\n\n const references = collectModuleReferences(sourceFile)\n const dependencies = references.map((reference) =>\n resolveDependency(reference, normalizedPath, compilerOptions, host),\n )\n\n nodes.set(normalizedPath, {\n id: normalizedPath,\n dependencies,\n })\n\n for (const dependency of dependencies) {\n if (dependency.kind === 'source') {\n visitFile(dependency.target, compilerOptions, host, nodes)\n }\n }\n}\n\nfunction collectModuleReferences(sourceFile: ts.SourceFile): ModuleReference[] {\n const references: ModuleReference[] = []\n const seen = new Set<string>()\n\n function addReference(\n specifier: string,\n referenceKind: ReferenceKind,\n isTypeOnly: boolean,\n ): void {\n const key = `${referenceKind}:${isTypeOnly ? 'type' : 'value'}:${specifier}`\n if (seen.has(key)) {\n return\n }\n\n seen.add(key)\n references.push({\n specifier,\n referenceKind,\n isTypeOnly,\n })\n }\n\n function visit(node: ts.Node): void {\n if (\n ts.isImportDeclaration(node) &&\n ts.isStringLiteralLike(node.moduleSpecifier)\n ) {\n addReference(\n node.moduleSpecifier.text,\n 'import',\n node.importClause?.isTypeOnly ?? false,\n )\n } else if (\n ts.isExportDeclaration(node) &&\n node.moduleSpecifier !== undefined &&\n ts.isStringLiteralLike(node.moduleSpecifier)\n ) {\n addReference(\n node.moduleSpecifier.text,\n 'export',\n node.isTypeOnly ?? false,\n )\n } else if (ts.isImportEqualsDeclaration(node)) {\n const moduleReference = node.moduleReference\n if (\n ts.isExternalModuleReference(moduleReference) &&\n moduleReference.expression !== undefined &&\n ts.isStringLiteralLike(moduleReference.expression)\n ) {\n addReference(moduleReference.expression.text, 'import-equals', false)\n }\n } else if (ts.isCallExpression(node)) {\n if (\n node.expression.kind === ts.SyntaxKind.ImportKeyword &&\n node.arguments.length === 1\n ) {\n const [argument] = node.arguments\n if (argument !== undefined && ts.isStringLiteralLike(argument)) {\n addReference(argument.text, 'dynamic-import', false)\n }\n }\n\n if (\n ts.isIdentifier(node.expression) &&\n node.expression.text === 'require' &&\n node.arguments.length === 1\n ) {\n const [argument] = node.arguments\n if (argument !== undefined && ts.isStringLiteralLike(argument)) {\n addReference(argument.text, 'require', false)\n }\n }\n }\n\n ts.forEachChild(node, visit)\n }\n\n visit(sourceFile)\n return references\n}\n\nfunction resolveDependency(\n reference: ModuleReference,\n containingFile: string,\n compilerOptions: ts.CompilerOptions,\n host: ts.ModuleResolutionHost,\n): DependencyEdge {\n const specifier = reference.specifier\n if (BUILTIN_MODULES.has(specifier)) {\n return createEdge(reference, 'builtin', specifier)\n }\n\n const resolution = ts.resolveModuleName(\n specifier,\n containingFile,\n compilerOptions,\n host,\n ).resolvedModule\n\n if (resolution !== undefined) {\n const resolvedPath = normalizeFilePath(resolution.resolvedFileName)\n if (\n resolution.isExternalLibraryImport ||\n resolvedPath.includes(`${path.sep}node_modules${path.sep}`)\n ) {\n return createEdge(reference, 'external', specifier)\n }\n\n if (isSourceCodeFile(resolvedPath) && !resolvedPath.endsWith('.d.ts')) {\n return createEdge(reference, 'source', resolvedPath)\n }\n }\n\n if (!specifier.startsWith('.') && !path.isAbsolute(specifier)) {\n return createEdge(reference, 'external', specifier)\n }\n\n return createEdge(reference, 'missing', specifier)\n}\n\nfunction createEdge(\n reference: ModuleReference,\n kind: DependencyKind,\n target: string,\n): DependencyEdge {\n return {\n specifier: reference.specifier,\n referenceKind: reference.referenceKind,\n isTypeOnly: reference.isTypeOnly,\n kind,\n target,\n }\n}\n\nfunction getScriptKind(filePath: string): ts.ScriptKind {\n switch (path.extname(filePath).toLowerCase()) {\n case '.js':\n case '.mjs':\n case '.cjs':\n return ts.ScriptKind.JS\n case '.jsx':\n return ts.ScriptKind.JSX\n case '.tsx':\n return ts.ScriptKind.TSX\n case '.json':\n return ts.ScriptKind.JSON\n default:\n return ts.ScriptKind.TS\n }\n}\n\nfunction resolveExistingPath(cwd: string, entryFile: string): string {\n const absolutePath = path.resolve(cwd, entryFile)\n const normalizedPath = normalizeFilePath(absolutePath)\n\n if (!fs.existsSync(normalizedPath)) {\n throw new Error(`Entry file not found: ${entryFile}`)\n }\n\n if (!isSourceCodeFile(normalizedPath)) {\n throw new Error(`Entry file must be a JS/TS source file: ${entryFile}`)\n }\n\n return normalizedPath\n}\n","import { toDisplayPath } from './path-utils.js'\nimport type {\n DependencyEdge,\n DependencyGraph,\n PrintTreeOptions,\n} from './types.js'\n\nexport function printDependencyTree(\n graph: DependencyGraph,\n options: PrintTreeOptions = {},\n): string {\n const cwd = options.cwd ?? graph.cwd\n const includeExternals = options.includeExternals ?? false\n const rootLines = [toDisplayPath(graph.entryId, cwd)]\n const visited = new Set<string>([graph.entryId])\n const entryNode = graph.nodes.get(graph.entryId)\n\n if (entryNode === undefined) {\n return rootLines.join('\\n')\n }\n\n const rootDependencies = filterDependencies(\n entryNode.dependencies,\n includeExternals,\n )\n\n rootDependencies.forEach((dependency, index) => {\n const isLast = index === rootDependencies.length - 1\n const lines = renderDependency(\n dependency,\n graph,\n visited,\n '',\n isLast,\n includeExternals,\n cwd,\n )\n rootLines.push(...lines)\n })\n\n return rootLines.join('\\n')\n}\n\nfunction renderDependency(\n dependency: DependencyEdge,\n graph: DependencyGraph,\n visited: ReadonlySet<string>,\n prefix: string,\n isLast: boolean,\n includeExternals: boolean,\n cwd: string,\n): string[] {\n const branch = `${prefix}${isLast ? '└─ ' : '├─ '}`\n const label = formatDependencyLabel(dependency, graph, cwd)\n\n if (dependency.kind !== 'source') {\n return [`${branch}${label}`]\n }\n\n if (visited.has(dependency.target)) {\n return [`${branch}${label} (circular)`]\n }\n\n const childNode = graph.nodes.get(dependency.target)\n if (childNode === undefined) {\n return [`${branch}${label}`]\n }\n\n const nextPrefix = `${prefix}${isLast ? ' ' : '│ '}`\n const nextVisited = new Set(visited)\n nextVisited.add(dependency.target)\n\n const childLines = [`${branch}${label}`]\n const childDependencies = filterDependencies(\n childNode.dependencies,\n includeExternals,\n )\n\n childDependencies.forEach((childDependency, index) => {\n const isChildLast = index === childDependencies.length - 1\n childLines.push(\n ...renderDependency(\n childDependency,\n graph,\n nextVisited,\n nextPrefix,\n isChildLast,\n includeExternals,\n cwd,\n ),\n )\n })\n\n return childLines\n}\n\nfunction filterDependencies(\n dependencies: readonly DependencyEdge[],\n includeExternals: boolean,\n): DependencyEdge[] {\n return dependencies.filter((dependency) => {\n if (dependency.kind === 'source' || dependency.kind === 'missing') {\n return true\n }\n\n return includeExternals\n })\n}\n\nfunction formatDependencyLabel(\n dependency: DependencyEdge,\n _graph: DependencyGraph,\n cwd: string,\n): string {\n const prefixes: string[] = []\n if (dependency.isTypeOnly) {\n prefixes.push('type')\n }\n\n if (dependency.referenceKind === 'require') {\n prefixes.push('require')\n } else if (dependency.referenceKind === 'dynamic-import') {\n prefixes.push('dynamic')\n } else if (dependency.referenceKind === 'export') {\n prefixes.push('re-export')\n } else if (dependency.referenceKind === 'import-equals') {\n prefixes.push('import=')\n }\n\n const annotation = prefixes.length > 0 ? `[${prefixes.join(', ')}] ` : ''\n\n if (dependency.kind === 'source') {\n return `${annotation}${toDisplayPath(dependency.target, cwd)}`\n }\n\n if (dependency.kind === 'missing') {\n return `${annotation}${dependency.specifier} [missing]`\n }\n\n if (dependency.kind === 'builtin') {\n return `${annotation}${dependency.target} [builtin]`\n }\n\n return `${annotation}${dependency.target} [external]`\n}\n"],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "foresthouse",
|
|
3
|
+
"version": "1.0.0-dev.1",
|
|
4
|
+
"description": "A Node.js CLI that reads JavaScript and TypeScript entry files and prints an import tree.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"packageManager": "pnpm@10.32.1",
|
|
7
|
+
"bin": {
|
|
8
|
+
"foresthouse": "dist/cli.mjs"
|
|
9
|
+
},
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"types": "./dist/index.d.mts",
|
|
13
|
+
"import": "./dist/index.mjs"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"types": "./dist/index.d.mts",
|
|
17
|
+
"files": [
|
|
18
|
+
"dist"
|
|
19
|
+
],
|
|
20
|
+
"repository": {
|
|
21
|
+
"type": "git",
|
|
22
|
+
"url": "git+https://github.com/async3619/foresthouse.git"
|
|
23
|
+
},
|
|
24
|
+
"homepage": "https://github.com/async3619/foresthouse#readme",
|
|
25
|
+
"bugs": {
|
|
26
|
+
"url": "https://github.com/async3619/foresthouse/issues"
|
|
27
|
+
},
|
|
28
|
+
"publishConfig": {
|
|
29
|
+
"access": "public",
|
|
30
|
+
"registry": "https://registry.npmjs.org/",
|
|
31
|
+
"provenance": true
|
|
32
|
+
},
|
|
33
|
+
"engines": {
|
|
34
|
+
"node": ">=24.14.0"
|
|
35
|
+
},
|
|
36
|
+
"scripts": {
|
|
37
|
+
"build": "tsdown",
|
|
38
|
+
"dev": "tsx src/cli.ts",
|
|
39
|
+
"lint": "biome check .",
|
|
40
|
+
"lint:fix": "biome check --write .",
|
|
41
|
+
"format": "biome format --write .",
|
|
42
|
+
"test": "vitest run",
|
|
43
|
+
"typecheck": "tsc --noEmit",
|
|
44
|
+
"check": "pnpm run lint && pnpm run typecheck && pnpm run test && pnpm run build",
|
|
45
|
+
"commitlint": "commitlint --from HEAD~1 --to HEAD --verbose",
|
|
46
|
+
"prepare": "husky",
|
|
47
|
+
"release": "semantic-release",
|
|
48
|
+
"release:dry-run": "semantic-release --dry-run"
|
|
49
|
+
},
|
|
50
|
+
"keywords": [
|
|
51
|
+
"cli",
|
|
52
|
+
"typescript",
|
|
53
|
+
"dependency-graph",
|
|
54
|
+
"import-tree",
|
|
55
|
+
"nodejs"
|
|
56
|
+
],
|
|
57
|
+
"author": "Sophia (Turner) <me@sophia-dev.io>",
|
|
58
|
+
"license": "MIT",
|
|
59
|
+
"dependencies": {
|
|
60
|
+
"typescript": "5.9.3"
|
|
61
|
+
},
|
|
62
|
+
"devDependencies": {
|
|
63
|
+
"@biomejs/biome": "2.4.7",
|
|
64
|
+
"@commitlint/cli": "20.4.4",
|
|
65
|
+
"@commitlint/config-conventional": "20.4.4",
|
|
66
|
+
"@types/node": "24.6.2",
|
|
67
|
+
"@semantic-release/changelog": "6.0.3",
|
|
68
|
+
"@semantic-release/commit-analyzer": "13.0.1",
|
|
69
|
+
"@semantic-release/git": "10.0.1",
|
|
70
|
+
"@semantic-release/github": "12.0.6",
|
|
71
|
+
"@semantic-release/npm": "13.1.5",
|
|
72
|
+
"@semantic-release/release-notes-generator": "14.1.0",
|
|
73
|
+
"conventional-changelog-conventionalcommits": "9.3.0",
|
|
74
|
+
"husky": "9.1.7",
|
|
75
|
+
"semantic-release": "25.0.3",
|
|
76
|
+
"tsdown": "0.21.2",
|
|
77
|
+
"tsx": "4.21.0",
|
|
78
|
+
"vitest": "4.1.0"
|
|
79
|
+
}
|
|
80
|
+
}
|