dependency-tree 11.4.3 → 11.5.0
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 +49 -61
- package/bin/cli.js +14 -2
- package/index.js +55 -53
- package/lib/config.js +1 -0
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -10,18 +10,17 @@
|
|
|
10
10
|
npm install dependency-tree
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
-
*
|
|
14
|
-
-
|
|
15
|
-
-
|
|
16
|
-
|
|
17
|
-
* All core Node modules (assert, path, fs, etc) are removed from the dependency list by default
|
|
13
|
+
* Supports JS (AMD, CommonJS, ES6), TypeScript, and CSS preprocessors (PostCSS, Sass, Stylus, Less) - any type handled by [precinct](https://github.com/dependents/node-precinct)
|
|
14
|
+
- CommonJS: third-party (npm) dependencies are included by default
|
|
15
|
+
- Path resolution is handled by [filing-cabinet](https://github.com/dependents/node-filing-cabinet); RequireJS and webpack loaders are supported
|
|
16
|
+
* Core Node built-ins (assert, path, fs, etc.) are excluded by default
|
|
18
17
|
|
|
19
18
|
## Usage
|
|
20
19
|
|
|
21
20
|
```js
|
|
22
21
|
const dependencyTree = require('dependency-tree');
|
|
23
22
|
|
|
24
|
-
// Returns a dependency tree object for the given file
|
|
23
|
+
// Returns a nested dependency tree object for the given file
|
|
25
24
|
const tree = dependencyTree({
|
|
26
25
|
filename: 'path/to/a/file',
|
|
27
26
|
directory: 'path/to/all/files',
|
|
@@ -31,14 +30,13 @@ const tree = dependencyTree({
|
|
|
31
30
|
nodeModulesConfig: {
|
|
32
31
|
entry: 'module'
|
|
33
32
|
}, // optional
|
|
34
|
-
filter: path => path.
|
|
33
|
+
filter: path => !path.includes('node_modules'), // optional
|
|
35
34
|
nonExistent: [], // optional
|
|
36
35
|
noTypeDefinitions: false // optional
|
|
37
36
|
});
|
|
38
37
|
|
|
39
|
-
// Returns a post-order
|
|
40
|
-
//
|
|
41
|
-
// Note: you can pass the same arguments as you would to dependencyTree()
|
|
38
|
+
// Returns a post-order flat list of absolute paths (dependencies before dependents).
|
|
39
|
+
// Useful as a concatenation order for bundling.
|
|
42
40
|
const list = dependencyTree.toList({
|
|
43
41
|
filename: 'path/to/a/file',
|
|
44
42
|
directory: 'path/to/all/files'
|
|
@@ -47,78 +45,68 @@ const list = dependencyTree.toList({
|
|
|
47
45
|
|
|
48
46
|
### Options
|
|
49
47
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
where every key is an absolute
|
|
68
|
-
|
|
69
|
-
Example:
|
|
48
|
+
| Option | Type | Default | Description |
|
|
49
|
+
|---|---|---|---|
|
|
50
|
+
| `filename` | `string` | - | **Required.** Absolute path to the entry file |
|
|
51
|
+
| `directory` | `string` | - | **Required.** Root directory used to resolve relative paths |
|
|
52
|
+
| `requireConfig` | `string` | `undefined` | Path to a RequireJS config for AMD modules (resolves aliased paths) |
|
|
53
|
+
| `webpackConfig` | `string` | `undefined` | Path to a webpack config for aliased modules |
|
|
54
|
+
| `tsConfig` | `string \| object` | `undefined` | Path to a TypeScript config file, or a preloaded config object |
|
|
55
|
+
| `tsConfigPath` | `string` | `undefined` | Virtual path for the TypeScript config when `tsConfig` is an object. Required for [Path Mapping](https://www.typescriptlang.org/docs/handbook/module-resolution.html#path-mapping); ignored when `tsConfig` is a string path |
|
|
56
|
+
| `nodeModulesConfig` | `object` | `undefined` | Config for resolving `node_modules` entry files (e.g. `{ entry: 'module' }`) |
|
|
57
|
+
| `visited` | `object` | `{}` | Memoization cache (`filename → subtree`) to skip already-processed files |
|
|
58
|
+
| `nonExistent` | `string[]` | `[]` | Array populated with partial paths that could not be resolved |
|
|
59
|
+
| `filter` | `(depPath, parentPath) => boolean` | `undefined` | Return `true` to include a dependency (and its subtree) in the tree |
|
|
60
|
+
| `detective` | `object` | `undefined` | Detector options passed to [precinct](https://github.com/dependents/node-precinct#usage) - e.g. `{ amd: { skipLazyLoaded: true } }` |
|
|
61
|
+
| `noTypeDefinitions` | `boolean` | `false` | Resolve TypeScript imports to `*.js` instead of `*.d.ts` |
|
|
62
|
+
|
|
63
|
+
### Output format
|
|
64
|
+
|
|
65
|
+
The default output is a nested object where every key is an absolute file path and the value is its own subtree:
|
|
70
66
|
|
|
71
67
|
```js
|
|
72
68
|
{
|
|
73
|
-
'/
|
|
74
|
-
'/
|
|
75
|
-
'/
|
|
76
|
-
'/
|
|
69
|
+
'/path/to/a.js': {
|
|
70
|
+
'/path/to/b.js': {
|
|
71
|
+
'/path/to/d.js': {},
|
|
72
|
+
'/path/to/e.js': {}
|
|
77
73
|
},
|
|
78
|
-
'/
|
|
79
|
-
'/
|
|
80
|
-
'/
|
|
74
|
+
'/path/to/c.js': {
|
|
75
|
+
'/path/to/f.js': {},
|
|
76
|
+
'/path/to/g.js': {}
|
|
81
77
|
}
|
|
82
78
|
}
|
|
83
79
|
}
|
|
84
80
|
```
|
|
85
81
|
|
|
86
|
-
This
|
|
87
|
-
for use in the [Dependents](https://github.com/mrjoelkemp/sublime-dependents) plugin.
|
|
82
|
+
This format was designed for visual representation in the [Dependents](https://github.com/mrjoelkemp/sublime-dependents) plugin.
|
|
88
83
|
|
|
89
|
-
### CLI
|
|
84
|
+
### CLI
|
|
90
85
|
|
|
91
|
-
|
|
86
|
+
Requires a global install: `npm install -g dependency-tree`
|
|
92
87
|
|
|
93
88
|
```
|
|
94
|
-
dependency-tree --directory=path/to/
|
|
89
|
+
dependency-tree --directory=path/to/files [--list-form] [--es6-mixed-imports] [-c path/to/require/config] [-w path/to/webpack/config] filename
|
|
95
90
|
```
|
|
96
91
|
|
|
97
|
-
Prints the dependency tree
|
|
98
|
-
|
|
99
|
-
* You can alternatively print out the list form one element per line using the `--list-form` option.
|
|
100
|
-
|
|
101
|
-
## How does this work?
|
|
92
|
+
Prints the dependency tree as JSON. Use `--list-form` to print one path per line instead.
|
|
102
93
|
|
|
103
|
-
|
|
94
|
+
## How it works
|
|
104
95
|
|
|
105
|
-
|
|
96
|
+
dependency-tree passes the entry file to [precinct](https://github.com/dependents/node-precinct/) to extract raw dependency strings, then passes each to [filing-cabinet](https://github.com/dependents/node-filing-cabinet) to resolve them to real filesystem paths, and recurses until the full tree is built.
|
|
106
97
|
|
|
107
|
-
|
|
98
|
+
Precinct generates an AST via [node-source-walk](https://github.com/dependents/node-source-walk), detects the module format (CommonJS, AMD, or ES6), and delegates to the appropriate detective to extract dependency declarations.
|
|
108
99
|
|
|
109
|
-
Filing-cabinet reuses
|
|
110
|
-
|
|
111
|
-
So after the appropriate resolver finds the file on the filesystem, filing-cabinet has successfully mapped a raw dependency name to a file on the filesystem. Now, dependency-tree has a file that it can also traverse (repeating exactly what was done for the starting file).
|
|
112
|
-
|
|
113
|
-
At the end of traversing every file (in a depth-first fashion), we have a fully populated dependency tree. :dancers:
|
|
100
|
+
Filing-cabinet reuses that AST to detect the module format again, then delegates to the right resolver - necessary because AMD supports path aliasing via a RequireJS config, while CommonJS has its own resolution algorithm. The resolver returns an absolute path, which dependency-tree then recurses into.
|
|
114
101
|
|
|
115
102
|
## FAQ
|
|
116
103
|
|
|
117
|
-
### Why aren't some
|
|
104
|
+
### Why aren't some dependencies being detected?
|
|
105
|
+
|
|
106
|
+
Bugs in [precinct](https://github.com/dependents/node-precinct) or an incomplete `requireConfig`/`webpackConfig`/`tsConfig` are the most common causes. Any path that could not be resolved is appended to the array passed as `nonExistent`.
|
|
118
107
|
|
|
119
|
-
|
|
120
|
-
some dependencies may not be resolved. The optional array passed to the `nonExistent` option will be populated with paths
|
|
121
|
-
that could not be resolved. You can check this array to see where problems might exist.
|
|
108
|
+
For detailed resolution logs, set `NODE_DEBUG=*` when using the CLI:
|
|
122
109
|
|
|
123
|
-
|
|
124
|
-
|
|
110
|
+
```sh
|
|
111
|
+
NODE_DEBUG=* dependency-tree -w path/to/webpack.config.json path/to/a/file
|
|
112
|
+
```
|
package/bin/cli.js
CHANGED
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
'use strict';
|
|
4
4
|
|
|
5
|
+
const process = require('node:process');
|
|
5
6
|
const { program } = require('commander');
|
|
7
|
+
const { stringifyChunked } = require('@discoveryjs/json-ext');
|
|
6
8
|
const dependencyTree = require('../index.js');
|
|
7
9
|
const { name, description, version } = require('../package.json');
|
|
8
10
|
|
|
@@ -16,6 +18,7 @@ program
|
|
|
16
18
|
.option('-c, --require-config <path>', 'path to a requirejs config')
|
|
17
19
|
.option('-w, --webpack-config <path>', 'path to a webpack config')
|
|
18
20
|
.option('-t, --ts-config <path>', 'path to a typescript config')
|
|
21
|
+
.option('--es6-mixed-imports', 'detect dependencies from files that mix ES6 imports and CJS require() calls')
|
|
19
22
|
.option('--list-form', 'output the list form of the tree (one element per line)')
|
|
20
23
|
.showHelpAfterError()
|
|
21
24
|
.parse();
|
|
@@ -26,7 +29,12 @@ const options = {
|
|
|
26
29
|
root: cliOptions.directory,
|
|
27
30
|
config: cliOptions.requireConfig,
|
|
28
31
|
webpackConfig: cliOptions.webpackConfig,
|
|
29
|
-
tsConfig: cliOptions.tsConfig
|
|
32
|
+
tsConfig: cliOptions.tsConfig,
|
|
33
|
+
detective: {
|
|
34
|
+
es6: {
|
|
35
|
+
mixedImports: Boolean(cliOptions.es6MixedImports)
|
|
36
|
+
}
|
|
37
|
+
}
|
|
30
38
|
};
|
|
31
39
|
|
|
32
40
|
if (cliOptions.listForm) {
|
|
@@ -38,5 +46,9 @@ if (cliOptions.listForm) {
|
|
|
38
46
|
} else {
|
|
39
47
|
const tree = dependencyTree(options);
|
|
40
48
|
|
|
41
|
-
|
|
49
|
+
for (const chunk of stringifyChunked(tree)) {
|
|
50
|
+
process.stdout.write(chunk);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
process.stdout.write('\n');
|
|
42
54
|
}
|
package/index.js
CHANGED
|
@@ -10,22 +10,21 @@ const Config = require('./lib/config.js');
|
|
|
10
10
|
const debug = debuglog('tree');
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
|
-
*
|
|
14
|
-
* and returns a flat list of all unique, visited nodes
|
|
13
|
+
* Returns the dependency tree of a module as a nested object
|
|
15
14
|
*
|
|
16
15
|
* @param {Object} options
|
|
17
|
-
* @param {
|
|
18
|
-
* @param {
|
|
19
|
-
* @param {
|
|
20
|
-
* @param {
|
|
21
|
-
* @param {
|
|
22
|
-
* @param {Object} [options.visited] -
|
|
23
|
-
*
|
|
24
|
-
* @param {
|
|
25
|
-
* @param {
|
|
26
|
-
* @param {
|
|
27
|
-
* @param {
|
|
28
|
-
* @
|
|
16
|
+
* @param {string} options.filename - Entry module path
|
|
17
|
+
* @param {string} options.directory - Root directory containing all files
|
|
18
|
+
* @param {string} [options.requireConfig] - Path to a RequireJS config
|
|
19
|
+
* @param {string} [options.webpackConfig] - Path to a webpack config
|
|
20
|
+
* @param {string} [options.nodeModulesConfig] - Config for resolving node_modules entry files
|
|
21
|
+
* @param {Object} [options.visited] - Memoization cache: filename ? subtree
|
|
22
|
+
* @param {Array} [options.nonExistent] - Accumulator for unresolvable partials
|
|
23
|
+
* @param {boolean} [options.isListForm=false] - Return a flat list instead of a tree
|
|
24
|
+
* @param {string|Object} [options.tsConfig] - Path to (or preloaded) TypeScript config
|
|
25
|
+
* @param {string} [options.tsConfigPath] - (Virtual) path to tsconfig when tsConfig is an object; needed for Path Mapping
|
|
26
|
+
* @param {boolean} [options.noTypeDefinitions] - Resolve TS imports to `*.js` instead of `*.d.ts`
|
|
27
|
+
* @returns {Object}
|
|
29
28
|
*/
|
|
30
29
|
module.exports = function(options = {}) {
|
|
31
30
|
const config = new Config(options);
|
|
@@ -56,30 +55,23 @@ module.exports = function(options = {}) {
|
|
|
56
55
|
};
|
|
57
56
|
|
|
58
57
|
/**
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
58
|
+
* Returns a post-order flat list of absolute file paths (dependencies before dependents).
|
|
59
|
+
* Every file's dependencies appear at lower indices, so the root entry point is last.
|
|
60
|
+
* The list contains no duplicates. Accepts the same options as the default export.
|
|
62
61
|
*
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
* The list will not contain duplicates.
|
|
67
|
-
*
|
|
68
|
-
* Params are those of module.exports
|
|
62
|
+
* @param {Object} options - Same as the default export
|
|
63
|
+
* @returns {Array<string>}
|
|
69
64
|
*/
|
|
70
65
|
module.exports.toList = function(options = {}) {
|
|
71
|
-
options
|
|
72
|
-
|
|
73
|
-
return module.exports(options);
|
|
66
|
+
return module.exports({ ...options, isListForm: true });
|
|
74
67
|
};
|
|
75
68
|
|
|
76
69
|
/**
|
|
77
|
-
* Returns
|
|
78
|
-
*
|
|
79
|
-
* Protected for testing
|
|
70
|
+
* Returns resolved dependency paths for the file described by `config`.
|
|
71
|
+
* Exposed for testing.
|
|
80
72
|
*
|
|
81
|
-
* @param
|
|
82
|
-
* @
|
|
73
|
+
* @param {Config} config
|
|
74
|
+
* @returns {Array<string>}
|
|
83
75
|
*/
|
|
84
76
|
module.exports._getDependencies = function(config = {}) {
|
|
85
77
|
const precinctOptions = config.detectiveConfig;
|
|
@@ -132,8 +124,8 @@ module.exports._getDependencies = function(config = {}) {
|
|
|
132
124
|
};
|
|
133
125
|
|
|
134
126
|
/**
|
|
135
|
-
* @param
|
|
136
|
-
* @
|
|
127
|
+
* @param {Config} config
|
|
128
|
+
* @returns {Object|Set}
|
|
137
129
|
*/
|
|
138
130
|
function traverse(config = {}) {
|
|
139
131
|
const subTree = config.isListForm ? new Set() : {};
|
|
@@ -148,8 +140,7 @@ function traverse(config = {}) {
|
|
|
148
140
|
let dependencies = module.exports._getDependencies(config);
|
|
149
141
|
|
|
150
142
|
debug('cabinet-resolved all dependencies: ', dependencies);
|
|
151
|
-
//
|
|
152
|
-
// so that any dependent dependencies exit
|
|
143
|
+
// Eagerly mark the current file before recursing so any re-entrant visit exits early
|
|
153
144
|
config.visited[config.filename] = config.isListForm ? [] : {};
|
|
154
145
|
|
|
155
146
|
if (config.filter) {
|
|
@@ -163,7 +154,7 @@ function traverse(config = {}) {
|
|
|
163
154
|
for (const dependency of dependencies) {
|
|
164
155
|
const localConfig = config.clone();
|
|
165
156
|
localConfig.filename = dependency;
|
|
166
|
-
localConfig.directory =
|
|
157
|
+
localConfig.directory = getLocalConfigDirectory(localConfig);
|
|
167
158
|
|
|
168
159
|
if (localConfig.isListForm) {
|
|
169
160
|
for (const item of traverse(localConfig)) {
|
|
@@ -184,7 +175,7 @@ function traverse(config = {}) {
|
|
|
184
175
|
return subTree;
|
|
185
176
|
}
|
|
186
177
|
|
|
187
|
-
//
|
|
178
|
+
// Dedupe in-place so the caller's array reference stays valid
|
|
188
179
|
function dedupeNonExistent(nonExistent) {
|
|
189
180
|
const deduped = new Set(nonExistent);
|
|
190
181
|
nonExistent.length = deduped.size;
|
|
@@ -196,24 +187,35 @@ function dedupeNonExistent(nonExistent) {
|
|
|
196
187
|
}
|
|
197
188
|
}
|
|
198
189
|
|
|
199
|
-
// If the file is in a
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
190
|
+
// If the file is in a node_modules directory, we want to resolve the root of the package,
|
|
191
|
+
// not the file itself, since the file may be buried in a subdirectory and not contain all
|
|
192
|
+
// of the package's dependencies
|
|
193
|
+
function getLocalConfigDirectory(localConfig) {
|
|
194
|
+
const { filename, directory } = localConfig;
|
|
195
|
+
|
|
196
|
+
if (!filename.includes('node_modules')) {
|
|
197
|
+
return directory;
|
|
203
198
|
}
|
|
204
199
|
|
|
205
|
-
|
|
206
|
-
|
|
200
|
+
const dir = path.dirname(filename);
|
|
201
|
+
const parts = dir.split('node_modules');
|
|
207
202
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
const nodeModuleParts = filename.split('node_modules');
|
|
211
|
-
const packageSubPathPath = nodeModuleParts.pop().split(path.sep).filter(Boolean);
|
|
212
|
-
const packageName = packageSubPathPath[0].startsWith('@') ? `${packageSubPathPath[0]}${path.sep}${packageSubPathPath[1]}` : packageSubPathPath[0];
|
|
213
|
-
|
|
214
|
-
return path.normalize([...nodeModuleParts, `${path.sep}${packageName}`].join('node_modules'));
|
|
215
|
-
} catch {
|
|
216
|
-
debug(`Could not determine the root directory of package file ${filename}. Using default`);
|
|
217
|
-
return null;
|
|
203
|
+
if (parts.length < 2) {
|
|
204
|
+
return directory;
|
|
218
205
|
}
|
|
206
|
+
|
|
207
|
+
const afterNodeModules = parts.pop();
|
|
208
|
+
if (!afterNodeModules) {
|
|
209
|
+
return directory;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const segments = afterNodeModules.split(path.sep).filter(Boolean);
|
|
213
|
+
if (segments.length === 0) {
|
|
214
|
+
return directory;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
const packageName = segments[0].startsWith('@') ? `${segments[0]}${path.sep}${segments[1]}` : segments[0];
|
|
218
|
+
const projectPath = path.normalize(`${parts.join('node_modules')}node_modules${path.sep}${packageName}`);
|
|
219
|
+
|
|
220
|
+
return projectPath;
|
|
219
221
|
}
|
package/lib/config.js
CHANGED
|
@@ -27,6 +27,7 @@ module.exports = class Config {
|
|
|
27
27
|
if (this.filter && typeof this.filter !== 'function') throw new Error('filter must be a function');
|
|
28
28
|
|
|
29
29
|
if (typeof this.tsConfig === 'string') {
|
|
30
|
+
// Pre-parse once so all recursive clones share the object form
|
|
30
31
|
debug('preparsing the ts config into an object for performance');
|
|
31
32
|
const ts = require('typescript');
|
|
32
33
|
const tsParsedConfig = ts.readJsonConfigFile(this.tsConfig, ts.sys.readFile);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dependency-tree",
|
|
3
|
-
"version": "11.
|
|
3
|
+
"version": "11.5.0",
|
|
4
4
|
"description": "Get the dependency tree of a module",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "index.d.ts",
|
|
@@ -49,9 +49,10 @@
|
|
|
49
49
|
"node": ">=18"
|
|
50
50
|
},
|
|
51
51
|
"dependencies": {
|
|
52
|
+
"@discoveryjs/json-ext": "^1.1.0",
|
|
52
53
|
"commander": "^12.1.0",
|
|
53
|
-
"filing-cabinet": "^5.
|
|
54
|
-
"precinct": "^12.3.
|
|
54
|
+
"filing-cabinet": "^5.5.1",
|
|
55
|
+
"precinct": "^12.3.2",
|
|
55
56
|
"typescript": "^5.9.3"
|
|
56
57
|
},
|
|
57
58
|
"devDependencies": {
|