tdepend 0.2.0 → 0.3.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.
Files changed (115) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +212 -116
  3. package/dist/api/analyze.d.ts +8 -68
  4. package/dist/api/analyze.js +33 -108
  5. package/dist/api/analyze.js.map +1 -1
  6. package/dist/api/explain.d.ts +22 -0
  7. package/dist/api/explain.js +38 -0
  8. package/dist/api/explain.js.map +1 -0
  9. package/dist/api/export.d.ts +4 -47
  10. package/dist/api/export.js +6 -45
  11. package/dist/api/export.js.map +1 -1
  12. package/dist/api/serialization.d.ts +6 -26
  13. package/dist/api/serialization.js +68 -58
  14. package/dist/api/serialization.js.map +1 -1
  15. package/dist/cli/index.js +11 -129
  16. package/dist/cli/index.js.map +1 -1
  17. package/dist/cli/run.d.ts +12 -0
  18. package/dist/cli/run.js +112 -0
  19. package/dist/cli/run.js.map +1 -0
  20. package/dist/config/loadConfig.d.ts +10 -10
  21. package/dist/config/loadConfig.js +73 -36
  22. package/dist/config/loadConfig.js.map +1 -1
  23. package/dist/config/schema.d.ts +59 -29
  24. package/dist/config/schema.js +45 -51
  25. package/dist/config/schema.js.map +1 -1
  26. package/dist/errors.d.ts +9 -0
  27. package/dist/errors.js +21 -0
  28. package/dist/errors.js.map +1 -0
  29. package/dist/files/collectFiles.d.ts +21 -0
  30. package/dist/files/collectFiles.js +189 -0
  31. package/dist/files/collectFiles.js.map +1 -0
  32. package/dist/fs.d.ts +16 -0
  33. package/dist/fs.js +50 -0
  34. package/dist/fs.js.map +1 -0
  35. package/dist/graph/buildFileGraph.d.ts +33 -0
  36. package/dist/graph/buildFileGraph.js +163 -0
  37. package/dist/graph/buildFileGraph.js.map +1 -0
  38. package/dist/graph/fileGraph.d.ts +20 -0
  39. package/dist/graph/fileGraph.js +75 -0
  40. package/dist/graph/fileGraph.js.map +1 -0
  41. package/dist/index.d.ts +10 -21
  42. package/dist/index.js +6 -29
  43. package/dist/index.js.map +1 -1
  44. package/dist/output/analyzeText.d.ts +7 -0
  45. package/dist/output/analyzeText.js +91 -0
  46. package/dist/output/analyzeText.js.map +1 -0
  47. package/dist/parser/extractSites.d.ts +3 -0
  48. package/dist/parser/extractSites.js +704 -0
  49. package/dist/parser/extractSites.js.map +1 -0
  50. package/dist/parser/loadsTarget.d.ts +10 -0
  51. package/dist/parser/loadsTarget.js +304 -0
  52. package/dist/parser/loadsTarget.js.map +1 -0
  53. package/dist/parser/types.d.ts +30 -0
  54. package/dist/{types/graph.js → parser/types.js} +1 -1
  55. package/dist/parser/types.js.map +1 -0
  56. package/dist/project/discoverProject.d.ts +2 -0
  57. package/dist/project/discoverProject.js +477 -0
  58. package/dist/project/discoverProject.js.map +1 -0
  59. package/dist/project/gitignore.d.ts +13 -0
  60. package/dist/project/gitignore.js +138 -0
  61. package/dist/project/gitignore.js.map +1 -0
  62. package/dist/project/types.d.ts +44 -0
  63. package/dist/{types/parser.js → project/types.js} +1 -1
  64. package/dist/project/types.js.map +1 -0
  65. package/dist/resolver/createResolver.d.ts +4 -0
  66. package/dist/resolver/createResolver.js +276 -0
  67. package/dist/resolver/createResolver.js.map +1 -0
  68. package/dist/resolver/types.d.ts +28 -0
  69. package/dist/resolver/types.js +3 -0
  70. package/dist/resolver/types.js.map +1 -0
  71. package/dist/resolver/workspaceHost.d.ts +12 -0
  72. package/dist/resolver/workspaceHost.js +253 -0
  73. package/dist/resolver/workspaceHost.js.map +1 -0
  74. package/dist/types/api.d.ts +71 -64
  75. package/package.json +24 -25
  76. package/dist/analysis/reporter.d.ts +0 -22
  77. package/dist/analysis/reporter.js +0 -116
  78. package/dist/analysis/reporter.js.map +0 -1
  79. package/dist/graph/cycleDetector.d.ts +0 -2
  80. package/dist/graph/cycleDetector.js +0 -56
  81. package/dist/graph/cycleDetector.js.map +0 -1
  82. package/dist/graph/dependencyGraph.d.ts +0 -10
  83. package/dist/graph/dependencyGraph.js +0 -41
  84. package/dist/graph/dependencyGraph.js.map +0 -1
  85. package/dist/metrics/abstractness.d.ts +0 -2
  86. package/dist/metrics/abstractness.js +0 -10
  87. package/dist/metrics/abstractness.js.map +0 -1
  88. package/dist/metrics/distance.d.ts +0 -1
  89. package/dist/metrics/distance.js +0 -7
  90. package/dist/metrics/distance.js.map +0 -1
  91. package/dist/metrics/index.d.ts +0 -4
  92. package/dist/metrics/index.js +0 -30
  93. package/dist/metrics/index.js.map +0 -1
  94. package/dist/metrics/instability.d.ts +0 -1
  95. package/dist/metrics/instability.js +0 -8
  96. package/dist/metrics/instability.js.map +0 -1
  97. package/dist/parser/fileScanner.d.ts +0 -8
  98. package/dist/parser/fileScanner.js +0 -22
  99. package/dist/parser/fileScanner.js.map +0 -1
  100. package/dist/parser/tsParser.d.ts +0 -18
  101. package/dist/parser/tsParser.js +0 -158
  102. package/dist/parser/tsParser.js.map +0 -1
  103. package/dist/types/graph.d.ts +0 -14
  104. package/dist/types/graph.js.map +0 -1
  105. package/dist/types/parser.d.ts +0 -26
  106. package/dist/types/parser.js.map +0 -1
  107. package/dist/utils/deepMerge.d.ts +0 -1
  108. package/dist/utils/deepMerge.js +0 -23
  109. package/dist/utils/deepMerge.js.map +0 -1
  110. package/dist/utils/log.d.ts +0 -2
  111. package/dist/utils/log.js +0 -9
  112. package/dist/utils/log.js.map +0 -1
  113. package/dist/utils/pathUtils.d.ts +0 -12
  114. package/dist/utils/pathUtils.js +0 -27
  115. package/dist/utils/pathUtils.js.map +0 -1
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025-2026 Mohamed Saad
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,167 +1,263 @@
1
- # TDepend
2
-
3
- TDepend is a JDepend-inspired dependency analysis tool for the TypeScript ecosystem.
1
+ # tdepend
4
2
 
5
3
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
6
4
 
7
- ## Features
5
+ tdepend's goal is to make TypeScript architecture contracts executable. Version 0.3 delivers the foundation those contracts need: an import graph that matches the TypeScript compiler.
6
+
7
+ It finds your projects, workspace packages and `tsconfig` files, then resolves every import through TypeScript's own resolver. For each import it records:
8
8
 
9
- - 📊 Compute architectural metrics (Ca, Ce, Abstractness, Instability, Distance)
10
- - 🔄 Detect circular dependencies
11
- - 📏 Threshold-based quality gates for CI/CD
12
- - 🎯 Scoped analysis (module/namespace/class)
13
- - ⚙️ Fully config-driven with sensible defaults
14
- - 🚀 CI mode with JSON output
15
- - 📦 Zero runtime dependencies for analysis
9
+ - where it is, by file, line and column;
10
+ - what form it takes, such as a static import, a `require` or a type-only import;
11
+ - what it resolves to, in one of ten classes;
12
+ - whether it really loads its target at runtime, or the compiler erases it.
13
+
14
+ Architecture rules, metrics, cycles and baselines are not in this version. See [Migrating from 0.2](#migrating-from-02).
16
15
 
17
16
  ## Installation
18
17
 
18
+ tdepend needs Node 22.12 or later.
19
+
19
20
  ```bash
20
- npm install -g tdepend
21
+ npm install --save-dev tdepend
21
22
  # or
22
- pnpm add -g tdepend
23
- # or
24
- yarn global add tdepend
23
+ pnpm add --save-dev tdepend
25
24
  ```
26
25
 
27
- ## Quick Start
28
-
29
- ### CLI Usage
26
+ ## Usage
30
27
 
31
28
  ```bash
32
- # Analyze your TypeScript project
33
- tdepend analyze
29
+ tdepend analyze # report on the current directory
30
+ tdepend analyze --root path/to/repo # report on another directory
31
+ tdepend explain src/main.tsx # show every import in one file
32
+ ```
34
33
 
35
- # Use custom config file
36
- tdepend analyze --config my-config.json
34
+ Both commands take `--root <dir>` and `--config <path>`. `tdepend --version` prints the version and `tdepend --help` lists the commands.
37
35
 
38
- # CI mode with JSON output
39
- tdepend analyze --ci
36
+ tdepend reads the checkout as it is, so it works before a build and usually before `pnpm install`. The exception is a `tsconfig` whose `extends` names a package or a generated file. That target must exist, or tdepend exits 2 and names the config. tdepend never writes to the analysed repo unless you point `--export` there.
40
37
 
41
- # Export full analysis to JSON file
42
- tdepend analyze --export analysis-result.json
43
- ```
38
+ ### `tdepend analyze`
44
39
 
45
- ### Library Usage
40
+ | Option | Meaning |
41
+ |---|---|
42
+ | `-f, --format <text\|json>` | Output format, `text` by default |
43
+ | `-e, --export <path>` | Also write the full result to a JSON file |
44
+ | `--strict-resolution` | Exit 2 when any import is unresolved |
46
45
 
47
- TDepend can also be used programmatically as a library:
46
+ The exit code is 0, or 2 on a config or parse error, or when the analysis root doesn't exist. With `--strict-resolution` it is also 2 when an import is unresolved, which makes that flag the one gate this version offers for CI.
48
47
 
49
- ```typescript
50
- import { analyze, exportToFile } from 'tdepend';
48
+ The text report looks like this:
51
49
 
52
- // Analyze a project
53
- const result = await analyze({
54
- rootDir: 'src',
55
- include: ['src/**/*.ts'],
56
- failOnCycle: true
57
- });
50
+ ```text
51
+ Files: 6
52
+ Import sites: 10
53
+ Edges: 2
58
54
 
59
- console.log(`Analyzed ${result.modules.length} modules`);
60
- console.log(`Found ${result.cycles.length} cycles`);
55
+ Sites by class:
56
+ builtin: 1
57
+ asset: 2
58
+ virtual: 1
59
+ internal: 2
60
+ workspace: 0
61
+ excluded: 2
62
+ external: 0
63
+ ignored: 0
64
+ unsupported: 0
65
+ unresolved: 2
61
66
 
62
- // Export results to JSON
63
- await exportToFile(result, 'architecture-snapshot.json');
67
+ Unresolved imports (2):
68
+ src/main.tsx:6 ./missing
69
+ src/main.tsx:9 left-pad
64
70
 
65
- // Access detailed metrics
66
- for (const metric of result.metrics) {
67
- if (metric.distance > 0.8) {
68
- console.log(`${metric.filePath}: D=${metric.distance.toFixed(2)}`);
69
- }
70
- }
71
+ Ignored (gitignored targets): 0
72
+
73
+ Unsupported imports: 0
74
+
75
+ Self-imports: 0
76
+ Files outside every tsconfig: 2
71
77
  ```
72
78
 
73
- #### API Reference
79
+ **Files** is the analysis set. **Import sites** counts every import statement or call. **Edges** counts the file pairs connected by at least one site.
74
80
 
75
- **Main Functions:**
76
- - `analyze(options?)` - Analyze a TypeScript project
77
- - `analyzeWithConfig(config)` - Analyze using a full config object
78
- - `exportToFile(result, filePath, options?)` - Export analysis to JSON file
79
- - `exportToJson(result, options?)` - Convert analysis to JSON string
81
+ Every site gets exactly one class:
80
82
 
81
- **Core Building Blocks:**
82
- - `DependencyGraph` - Graph data structure
83
- - `detectCycles(graph)` - Cycle detection
84
- - `computeAllMetrics(graph, modules, cycles)` - Metric computation
85
- - `parseProject(files)` - Parse TypeScript files
86
- - `scanFiles(config)` - Scan files matching patterns
83
+ | Class | Meaning | Adds a graph edge |
84
+ |---|---|---|
85
+ | `builtin` | A Node built-in; `fs` and `node:fs` count as the same module | No |
86
+ | `virtual` | A `virtual:` or `~icons/` module, or a `resolve.virtual` pattern | No |
87
+ | `asset` | A stylesheet, image, font, JSON or Markdown file, or anything with a `?query` | No |
88
+ | `internal` | A file in the analysis set, in the same package | Yes |
89
+ | `workspace` | A file in the analysis set, in a different workspace package | Yes |
90
+ | `excluded` | A repo file outside the analysis set, such as a test or generated file | No |
91
+ | `external` | A package from `node_modules`, or one declared in `package.json` but not installed | No |
92
+ | `ignored` | An unresolved import that matches `resolve.ignoreUnresolved`, or whose target is gitignored | No |
93
+ | `unsupported` | A form tdepend can't follow, such as `import(name)`, a shadowed `require`, or a JSDoc import type | No |
94
+ | `unresolved` | None of the above; this is usually a broken import | No |
87
95
 
88
- See the [examples](./examples) directory for more usage patterns.
96
+ The report also counts self-imports and files that no `tsconfig` covers. When a package is declared but missing, it adds a line asking whether `node_modules` is installed. Discovery warnings, such as a compiler option TypeScript 5.9 doesn't know, are listed at the end.
89
97
 
90
- ## Configuration
98
+ ### `tdepend explain <file>`
91
99
 
92
- TDepend looks for `tdepend.config.json` in the current directory.
100
+ `explain` is the debugging tool. It prints one row per import: position, kind, class, target and the runtime verdict with its reason.
93
101
 
94
- ### Config File Example
102
+ ```text
103
+ 1:8 side-effect internal '@/components/button' -> src/components/button.tsx loads (retained): a side-effect import is evaluated and every compiler keeps it
104
+ 3:8 side-effect asset './styles.css' -> - loads (retained): a side-effect import is evaluated and every compiler keeps it
105
+ 7:27 static excluded './routeTree.gen' -> src/routeTree.gen.ts loads (retained): at least one binding is used as a value
106
+ 10:16 static builtin 'node:fs' -> node:fs loads (retained): at least one binding is used as a value
107
+ ```
108
+
109
+ The verdict rule is conservative: an import loads its target unless `tsc`, esbuild and swc would all drop it. A reported runtime edge may therefore be one a single compiler would erase, but an import reported as erased is erased by every listed compiler.
110
+
111
+ The word in brackets is the kind of evidence. `syntax` means the form alone decides, as with `import type`. `usage` means no binding is used as a value. `retained` means a compiler keeps the import.
112
+
113
+ ### JSON export
114
+
115
+ `--export <path>` and `--format json` write the same versioned document. It has no timestamp and every list is sorted, so two runs on the same inputs produce byte-identical output.
95
116
 
96
117
  ```json
97
118
  {
98
- "rootDir": "src",
99
- "include": ["src/**/*.ts", "src/**/*.tsx"],
100
- "exclude": ["**/*.test.ts", "**/*.spec.ts", "dist"],
101
- "metrics": {
102
- "enabled": ["coupling", "abstractness", "distance", "cycles"],
103
- "thresholds": {
104
- "distance": 0.6
119
+ "version": 1,
120
+ "files": ["src/main.ts", "src/util.ts"],
121
+ "edges": [
122
+ { "from": "src/main.ts", "to": "src/util.ts", "runtime": true, "mapped": false, "kinds": ["static"] }
123
+ ],
124
+ "sites": [
125
+ {
126
+ "file": "src/main.ts",
127
+ "specifier": "./util",
128
+ "line": 1,
129
+ "column": 22,
130
+ "kind": "static",
131
+ "form": "declaration",
132
+ "deferred": false,
133
+ "unsupported": null,
134
+ "loadsTarget": true,
135
+ "loadsTargetBy": "retained",
136
+ "class": "internal",
137
+ "reason": null,
138
+ "target": { "file": "src/util.ts" },
139
+ "mapped": null,
140
+ "requested": null
105
141
  }
106
- },
107
- "analysis": {
108
- "target": null,
109
- "value": null
110
- },
111
- "ci": {
112
- "failOnThreshold": true,
113
- "outputFormat": "json"
114
- }
142
+ ],
143
+ "report": { "...": "class counts, unresolved, ignored and unsupported sites, warnings" }
115
144
  }
116
145
  ```
117
146
 
118
- ### Configuration Options
147
+ Paths are relative to the analysis root and use `/` on every platform. Lines and columns start at 1, and the column points at the opening quote of the specifier. An edge is `runtime` when at least one of its sites loads the target, and `mapped` when a declared output path such as `dist/index.d.ts` was mapped back to its source file.
119
148
 
120
- - **rootDir**: Root directory for analysis (default: `"src"`)
121
- - **include**: Glob patterns for files to include (default: `["src/**/*.ts", "src/**/*.tsx"]`)
122
- - **exclude**: Glob patterns for files to exclude (default: `["**/*.test.ts", "**/*.spec.ts", "dist"]`)
123
- - **metrics.enabled**: Array of metrics to compute (options: `"coupling"`, `"abstractness"`, `"distance"`, `"cycles"`)
124
- - **metrics.thresholds.distance**: Maximum allowed distance from main sequence (default: `0.6`)
125
- - **analysis.target**: Scope analysis to `"module"`, `"class"`, or `"namespace"` (default: `null`)
126
- - **analysis.value**: Value for the target scope (default: `null`)
127
- - **ci.failOnThreshold**: Exit with code 1 on threshold violations (default: `true`)
128
- - **ci.failOnCycle**: Exit with code 1 when cycles are detected (default: `false`)
129
- - **ci.outputFormat**: Output format - `"console"` or `"json"` (default: `"console"`)
149
+ ## Library API
130
150
 
131
- ## Development
151
+ The package exports four functions, plus the types of the values they return.
132
152
 
133
- ```bash
134
- # Install dependencies
135
- pnpm install
153
+ ```ts
154
+ import { analyze, explainFile, exportToFile, toSerializable } from 'tdepend';
155
+
156
+ const result = await analyze({ root: '.' });
136
157
 
137
- # Build
138
- pnpm run build
158
+ const { edges, report } = toSerializable(result);
159
+ console.log(`${edges.length} edges, ${report.unresolved.length} unresolved imports`);
139
160
 
140
- # Run tests
141
- pnpm test
161
+ await exportToFile(result, 'graph.json');
142
162
 
143
- # Format code
144
- pnpm format
163
+ const rows = await explainFile({ root: '.', file: 'src/main.tsx' });
164
+ for (const row of rows) {
165
+ console.log(row.specifier, row.class, row.loadsTarget);
166
+ }
145
167
  ```
146
168
 
147
- ## Project Structure
169
+ - `analyze(options?)` runs the whole pipeline. Its options are `root`, `config` and `typeOnly`.
170
+ - `explainFile(options)` takes the same options plus `file`, and returns one row per import.
171
+ - `toSerializable(result)` returns the JSON document above.
172
+ - `exportToFile(result, path)` writes that document to a file.
173
+
174
+ ## Configuration
175
+
176
+ tdepend looks for `tdepend.config.json` in the analysis root, or uses `--config <path>`. A config is optional. These are the keys that work in 0.3, with their defaults:
177
+
178
+ ```json
179
+ {
180
+ "root": ".",
181
+ "include": [],
182
+ "exclude": [],
183
+ "typeOnly": "usage",
184
+ "compilers": ["tsc", "esbuild", "swc"],
185
+ "resolve": { "aliases": {}, "virtual": [], "ignoreUnresolved": [] }
186
+ }
187
+ ```
188
+
189
+ - **`root`** is the analysis root, relative to `--root` or the working directory.
190
+ - **`include`** replaces discovery when it isn't empty. It takes root-relative globs, and `exclude` still applies.
191
+ - **`exclude`** adds to the built-in skips below. An entry starting with `!` removes an earlier pattern, so `"!**/public/**"` keeps `public/`.
192
+ - **`typeOnly`** is `usage` or `syntax`. With `syntax`, every static import counts as loading its target, however its bindings are used.
193
+ - **`compilers`** names the compilers you really use. In 0.3 it only changes empty `import {} from` statements, which `tsc` erases and swc keeps.
194
+ - **`resolve.aliases`** is for aliases that exist only in a bundler config, as a prefix map relative to the importing file's project, such as `{ "@/": "src/" }`. Aliases in `tsconfig` `paths` need no config.
195
+ - **`resolve.virtual`** lists extra patterns to treat as `virtual`.
196
+ - **`resolve.ignoreUnresolved`** lists unresolved imports to class as `ignored`.
197
+
198
+ The built-in skips are test and spec files, `__tests__`, stories, declaration files, `*.min.js`, `public` and a root `e2e` folder. Gitignored files are skipped too. Directories named `node_modules`, `dist`, `build`, `out` and `coverage`, and dot directories such as `.storybook`, are never walked, so `include` can't reach them. A `tsconfig` that lists a file in a dot directory still brings that file in.
199
+
200
+ `exclude` also applies to `tsconfig` files, so a deliberately broken one under a fixtures folder can be left out.
201
+
202
+ The keys `presets` and `metrics.abstractness` are accepted and have no effect yet. An unknown key exits 2, and so do `rules`, `components`, `baseline` and `metrics.thresholds`, which belong to later releases.
203
+
204
+ ## Migrating from 0.2
205
+
206
+ Version 0.3 is a rewrite, with no compatibility layer. The old graph missed aliases, re-exports, dynamic imports and workspace packages, so it was replaced rather than patched.
207
+
208
+ - **Metrics and cycles are gone for now.** 0.3 reports the graph only. Coupling metrics, cycles and rules arrive in 0.4, through a new `check` command with baselines. Comparing a pull request against its base arrives in 0.5.
209
+ - **Removed exports:** `analyzeWithConfig`, `exportToJson`, `DependencyGraph`, `detectCycles`, `computeAllMetrics`, `parseProject` and `scanFiles`. The public API is now the four functions above.
210
+ - **Removed CLI option:** `--ci`. Use `--format json` for machine output and `--strict-resolution` to fail a build.
211
+ - **Removed config keys:** `rootDir`, `ci`, `analysis` and `metrics.enabled`. `metrics.thresholds` returns in 0.4.
212
+ - **Old configs are rejected.** A config with `rootDir`, `ci` or `analysis` exits 2 with this message:
213
+
214
+ ```text
215
+ This looks like a tdepend 0.2 config. See 'Migrating from 0.2' in the README.
216
+ ```
217
+
218
+ Start again from the keys in [Configuration](#configuration).
219
+ - **Node 22.12 or later** is now required.
220
+
221
+ ## Support levels
222
+
223
+ | Level | Layouts |
224
+ |---|---|
225
+ | **Supported**, covered by a fixture | NestJS with CommonJS or NodeNext, NestJS monorepos using `paths`, Next.js apps, Vite with TanStack Router, TanStack Start, Express in CommonJS JavaScript, Fastify in ESM, plain Node libraries, feature-based frontends, pnpm and npm workspaces including unbuilt and bundler-built packages, several apps without a workspace config, nested projects |
226
+ | **Configured manually** | Aliases that exist only in a bundler config, through `resolve.aliases`; unusual layouts, through `include` |
227
+ | **Experimental** | Yarn workspaces without Plug'n'Play, which use the same `package.json` form as npm but have no fixture of their own |
228
+ | **Unsupported** | Vue, Svelte and Astro single-file components, Yarn Plug'n'Play, non-literal dynamic loading |
229
+
230
+ ## Development
231
+
232
+ ```bash
233
+ pnpm install
234
+ pnpm run clean # remove dist
235
+ pnpm run build # clean, then compile
236
+ pnpm test # run the test suite
237
+ pnpm run typecheck # check the sources and the tests
148
238
  ```
239
+
240
+ ## Project structure
241
+
242
+ ```text
149
243
  src/
150
- api/ # Public library API
151
- cli/ # CLI entry point
152
- config/ # Configuration loading and validation
153
- parser/ # TypeScript file parsing
154
- graph/ # Dependency graph and cycle detection
155
- metrics/ # Metric computation
156
- analysis/ # Reporting
157
- utils/ # Utilities
158
- tests/ # Test files
159
- types/ # Type definitions
160
- index.ts # Library entry point
161
-
162
- dist/ # Compiled output
163
- examples/ # Usage examples
244
+ api/ analyze, explainFile, exportToFile, toSerializable
245
+ cli/ command line entry point
246
+ config/ config schema and loading
247
+ files/ the analysis set
248
+ graph/ the file graph and resolution report
249
+ output/ the text report
250
+ parser/ import sites and runtime-load classification
251
+ project/ projects, workspace packages, tsconfigs, gitignore rules
252
+ resolver/ TypeScript resolution, workspace host, classification
253
+ tests/ tests and fixtures
254
+ types/ public result types
255
+ fs.ts file system port
256
+ errors.ts errors that exit 2
257
+ index.ts library entry point
258
+ scripts/ regenerates the compiler fixture expectations
164
259
  ```
165
260
 
166
261
  ## License
167
- MIT
262
+
263
+ [MIT](LICENSE)
@@ -1,75 +1,15 @@
1
- import type { TDependConfig } from '../config/schema';
2
1
  import type { AnalysisResult } from '../types/api';
3
- /**
4
- * Options for the analyze() function.
5
- * These options are merged with config file settings (if any).
6
- */
7
2
  export interface AnalyzeOptions {
8
- /** Root directory to analyze (default: 'src') */
9
- rootDir?: string;
10
- /** Glob patterns for files to include (default: ['src/**\/*.ts', 'src/**\/*.tsx']) */
11
- include?: string[];
12
- /** Glob patterns for files to exclude (default: ['**\/*.test.ts', '**\/*.spec.ts', 'dist']) */
13
- exclude?: string[];
14
- /** Path to config file. If not provided, looks for tdepend.config.json */
3
+ /** Root directory to analyse. Defaults to the current working directory. */
4
+ root?: string;
5
+ /** Path to the tdepend config file. Discovered under the root when omitted. */
15
6
  config?: string;
16
- /** Fail analysis if cycles are detected (default: false) */
17
- failOnCycle?: boolean;
18
- /** Fail analysis if distance threshold is exceeded (default: true) */
19
- failOnThreshold?: boolean;
7
+ /** Overrides the config's type-only detection for this run. */
8
+ typeOnly?: 'usage' | 'syntax';
20
9
  }
21
10
  /**
22
- * Analyzes a TypeScript project and returns complete dependency analysis.
23
- *
24
- * This is the main entry point for programmatic usage of tdepend.
25
- * It orchestrates the entire analysis pipeline:
26
- * 1. Load and merge configuration
27
- * 2. Scan files matching include/exclude patterns
28
- * 3. Parse TypeScript modules
29
- * 4. Build dependency graph
30
- * 5. Detect cycles
31
- * 6. Compute metrics (coupling, abstractness, instability, distance)
32
- * 7. Generate report with violations
33
- *
34
- * @param options - Analysis options (merged with config file if present)
35
- * @returns Complete analysis result with modules, graph, metrics, and report
36
- *
37
- * @example
38
- * ```typescript
39
- * import { analyze } from 'tdepend';
40
- *
41
- * const result = await analyze({
42
- * rootDir: 'src',
43
- * include: ['src/**\/*.ts'],
44
- * failOnCycle: true
45
- * });
46
- *
47
- * console.log(`Analyzed ${result.modules.length} modules`);
48
- * console.log(`Found ${result.cycles.length} cycles`);
49
- * ```
11
+ * Runs the whole pipeline once: discover projects, packages and tsconfigs,
12
+ * collect the analysis set, parse every file's import sites, resolve each site
13
+ * through TypeScript with a workspace host, and build the file graph.
50
14
  */
51
15
  export declare function analyze(options?: AnalyzeOptions): Promise<AnalysisResult>;
52
- /**
53
- * Analyzes a TypeScript project using a provided configuration object.
54
- *
55
- * This is useful when you want full control over the configuration
56
- * without loading from a file or merging with defaults.
57
- *
58
- * @param config - Complete TDependConfig object
59
- * @returns Complete analysis result with modules, graph, metrics, and report
60
- *
61
- * @example
62
- * ```typescript
63
- * import { analyzeWithConfig, defaultConfig } from 'tdepend';
64
- *
65
- * const result = await analyzeWithConfig({
66
- * ...defaultConfig,
67
- * rootDir: 'src',
68
- * ci: {
69
- * ...defaultConfig.ci,
70
- * failOnCycle: true
71
- * }
72
- * });
73
- * ```
74
- */
75
- export declare function analyzeWithConfig(config: TDependConfig): Promise<AnalysisResult>;
@@ -1,120 +1,45 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.analyze = analyze;
4
- exports.analyzeWithConfig = analyzeWithConfig;
4
+ const node_path_1 = require("node:path");
5
5
  const loadConfig_1 = require("../config/loadConfig");
6
- const fileScanner_1 = require("../parser/fileScanner");
7
- const tsParser_1 = require("../parser/tsParser");
8
- const dependencyGraph_1 = require("../graph/dependencyGraph");
9
- const cycleDetector_1 = require("../graph/cycleDetector");
10
- const metrics_1 = require("../metrics");
11
- const reporter_1 = require("../analysis/reporter");
12
- const deepMerge_1 = require("../utils/deepMerge");
6
+ const errors_1 = require("../errors");
7
+ const collectFiles_1 = require("../files/collectFiles");
8
+ const fs_1 = require("../fs");
9
+ const buildFileGraph_1 = require("../graph/buildFileGraph");
10
+ const discoverProject_1 = require("../project/discoverProject");
11
+ const createResolver_1 = require("../resolver/createResolver");
13
12
  /**
14
- * Analyzes a TypeScript project and returns complete dependency analysis.
15
- *
16
- * This is the main entry point for programmatic usage of tdepend.
17
- * It orchestrates the entire analysis pipeline:
18
- * 1. Load and merge configuration
19
- * 2. Scan files matching include/exclude patterns
20
- * 3. Parse TypeScript modules
21
- * 4. Build dependency graph
22
- * 5. Detect cycles
23
- * 6. Compute metrics (coupling, abstractness, instability, distance)
24
- * 7. Generate report with violations
25
- *
26
- * @param options - Analysis options (merged with config file if present)
27
- * @returns Complete analysis result with modules, graph, metrics, and report
28
- *
29
- * @example
30
- * ```typescript
31
- * import { analyze } from 'tdepend';
32
- *
33
- * const result = await analyze({
34
- * rootDir: 'src',
35
- * include: ['src/**\/*.ts'],
36
- * failOnCycle: true
37
- * });
38
- *
39
- * console.log(`Analyzed ${result.modules.length} modules`);
40
- * console.log(`Found ${result.cycles.length} cycles`);
41
- * ```
13
+ * Runs the whole pipeline once: discover projects, packages and tsconfigs,
14
+ * collect the analysis set, parse every file's import sites, resolve each site
15
+ * through TypeScript with a workspace host, and build the file graph.
42
16
  */
43
17
  async function analyze(options = {}) {
44
- // Load config from file (or use defaults if no file exists)
45
- const baseConfig = options.config ? (0, loadConfig_1.loadConfig)(options.config) : (0, loadConfig_1.loadConfig)();
46
- // Create partial config from options
47
- const optionsConfig = {};
48
- if (options.rootDir !== undefined) {
49
- optionsConfig.rootDir = options.rootDir;
18
+ const baseDir = (0, node_path_1.resolve)(options.root ?? process.cwd());
19
+ const { config } = (0, loadConfig_1.loadConfig)({ cwd: baseDir, configPath: options.config });
20
+ const rootDir = config.root === '.' ? baseDir : (0, node_path_1.resolve)(baseDir, config.root);
21
+ if (!(0, fs_1.isDir)((0, fs_1.nodeFileSystem)(), rootDir)) {
22
+ throw new errors_1.ConfigError(`Root directory not found: ${rootDir}`);
50
23
  }
51
- if (options.include !== undefined) {
52
- optionsConfig.include = options.include;
53
- }
54
- if (options.exclude !== undefined) {
55
- optionsConfig.exclude = options.exclude;
56
- }
57
- if (options.failOnCycle !== undefined || options.failOnThreshold !== undefined) {
58
- optionsConfig.ci = {
59
- ...baseConfig.ci,
60
- ...(options.failOnCycle !== undefined && { failOnCycle: options.failOnCycle }),
61
- ...(options.failOnThreshold !== undefined && { failOnThreshold: options.failOnThreshold }),
62
- };
63
- }
64
- // Merge configs (options override file config)
65
- const config = (0, deepMerge_1.deepMerge)(baseConfig, optionsConfig);
66
- // Run analysis pipeline
67
- const files = await (0, fileScanner_1.scanFiles)(config);
68
- const modules = (0, tsParser_1.parseProject)(files);
69
- const graph = new dependencyGraph_1.DependencyGraph(modules);
70
- const cycles = (0, cycleDetector_1.detectCycles)(graph);
71
- const metrics = (0, metrics_1.computeAllMetrics)(graph, modules, cycles);
72
- const report = (0, reporter_1.generateReport)(modules, metrics, cycles, config);
73
- return {
74
- modules,
75
- graph,
76
- cycles,
77
- metrics,
78
- report,
79
- config,
80
- };
81
- }
82
- /**
83
- * Analyzes a TypeScript project using a provided configuration object.
84
- *
85
- * This is useful when you want full control over the configuration
86
- * without loading from a file or merging with defaults.
87
- *
88
- * @param config - Complete TDependConfig object
89
- * @returns Complete analysis result with modules, graph, metrics, and report
90
- *
91
- * @example
92
- * ```typescript
93
- * import { analyzeWithConfig, defaultConfig } from 'tdepend';
94
- *
95
- * const result = await analyzeWithConfig({
96
- * ...defaultConfig,
97
- * rootDir: 'src',
98
- * ci: {
99
- * ...defaultConfig.ci,
100
- * failOnCycle: true
101
- * }
102
- * });
103
- * ```
104
- */
105
- async function analyzeWithConfig(config) {
106
- const files = await (0, fileScanner_1.scanFiles)(config);
107
- const modules = (0, tsParser_1.parseProject)(files);
108
- const graph = new dependencyGraph_1.DependencyGraph(modules);
109
- const cycles = (0, cycleDetector_1.detectCycles)(graph);
110
- const metrics = (0, metrics_1.computeAllMetrics)(graph, modules, cycles);
111
- const report = (0, reporter_1.generateReport)(modules, metrics, cycles, config);
24
+ const ctx = (0, discoverProject_1.discoverProject)(rootDir, {
25
+ isExcluded: (0, collectFiles_1.createExcludeMatcher)(collectFiles_1.DEFAULT_SKIPS, config.exclude),
26
+ isConfigExcluded: (0, collectFiles_1.createExcludeMatcher)([], config.exclude),
27
+ });
28
+ const files = (0, collectFiles_1.collectFiles)(ctx, {
29
+ include: config.include,
30
+ exclude: config.exclude,
31
+ presetSkips: collectFiles_1.DEFAULT_SKIPS,
32
+ });
33
+ const resolver = (0, createResolver_1.createResolver)(ctx, new Set(files.files), config.resolve);
34
+ const built = (0, buildFileGraph_1.buildFileGraph)(ctx, files, resolver, options.typeOnly ?? config.typeOnly, {
35
+ compilers: config.compilers,
36
+ });
112
37
  return {
113
- modules,
114
- graph,
115
- cycles,
116
- metrics,
117
- report,
38
+ ctx,
39
+ files,
40
+ graph: built.graph,
41
+ sites: built.sites,
42
+ report: built.report,
118
43
  config,
119
44
  };
120
45
  }