rev-dep 0.3.0 → 1.0.0-alpha.2
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 +93 -5
- package/babel.js +1 -0
- package/bin.js +2 -0
- package/dist/babel/index.js +277 -0
- package/dist/babel/reexport-rewire.js +277 -0
- package/dist/cli/commonOptions.js +24 -0
- package/dist/cli/createCommands.js +17 -0
- package/dist/cli/docs/generate.js +90 -0
- package/dist/cli/docs/index.js +18 -0
- package/dist/cli/docs/template.js +49 -0
- package/dist/cli/entryPoints/index.js +50 -0
- package/dist/cli/entryPoints/types.js +2 -0
- package/dist/cli/files/index.js +33 -0
- package/dist/cli/files/types.js +2 -0
- package/dist/cli/index.js +10 -0
- package/dist/cli/resolve/formatResults.js +67 -0
- package/dist/cli/resolve/index.js +42 -0
- package/dist/cli/resolve/types.js +2 -0
- package/dist/lib/buildDepsGraph.js +43 -0
- package/dist/lib/cleanupDpdmDeps.js +24 -0
- package/dist/lib/find.js +45 -0
- package/dist/lib/getDepsSetWebpack.js +44 -0
- package/dist/lib/getDepsTree.js +23 -0
- package/dist/lib/getEntryPoints.js +71 -0
- package/dist/lib/getMaxDepthInGraph.js +21 -0
- package/dist/lib/getMaxDepthInGrapth.js +21 -0
- package/dist/lib/types.js +2 -0
- package/dist/lib/utils.js +44 -0
- package/package.json +31 -15
- package/cli.js +0 -69
- package/find.js +0 -119
- package/getDepsSet.js +0 -21
package/README.md
CHANGED
|
@@ -81,14 +81,14 @@ Available options are
|
|
|
81
81
|
#### `find` Function
|
|
82
82
|
|
|
83
83
|
```js
|
|
84
|
-
import { find } from
|
|
84
|
+
import { find } from "rev-dep";
|
|
85
85
|
|
|
86
86
|
const path = find({
|
|
87
|
-
entryPoints: [
|
|
88
|
-
filePath:
|
|
89
|
-
})
|
|
87
|
+
entryPoints: ["index.js"],
|
|
88
|
+
filePath: "utils.js",
|
|
89
|
+
});
|
|
90
90
|
|
|
91
|
-
console.log(path)
|
|
91
|
+
console.log(path);
|
|
92
92
|
```
|
|
93
93
|
|
|
94
94
|
#### `find` Options
|
|
@@ -111,6 +111,94 @@ If you installed `rev-dep` **globally**, you will have appropriate compiler inst
|
|
|
111
111
|
|
|
112
112
|
For example, to support `*.ts` and `*.tsx` implicit extensions in globally installed `rev-dep`, you have to also install globally `typescript` package (see [source](https://github.com/sverweij/dependency-cruiser/blob/96e34d0cf158034f2b7c8cafe9cec72dd74d8c45/src/extract/transpile/typescript-wrap.js))
|
|
113
113
|
|
|
114
|
+
## CLI reference
|
|
115
|
+
|
|
116
|
+
<!-- cli-docs-start -->
|
|
117
|
+
|
|
118
|
+
### Command `resolve`
|
|
119
|
+
|
|
120
|
+
Checks if a filePath is required from entryPoint(s) and prints the resolution path
|
|
121
|
+
|
|
122
|
+
#### Usage
|
|
123
|
+
|
|
124
|
+
```sh
|
|
125
|
+
rev-dep resolve <filePath> [entryPoints...] [options]
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
#### Arguments
|
|
129
|
+
|
|
130
|
+
- `filePath` - Path to a file that should be resolved in entry points (**required**),\* `entryPoints...` - List of entry points to look for file (_optional_)
|
|
131
|
+
|
|
132
|
+
#### Options
|
|
133
|
+
|
|
134
|
+
- `-wc, --webpackConfig <path>` - path to webpack config to enable webpack aliases support (_optional_)
|
|
135
|
+
- `--cwd <path>` - path to a directory that should be used as a resolution root (_optional_)
|
|
136
|
+
- `--rr reexportRewire <value>` - resolve actual dependencies for "export \* from" statements (_optional_)
|
|
137
|
+
- `-i include <globs...>` - A list of globs to determine files included in entry points search (_optional_)
|
|
138
|
+
- `-e exclude <globs...>` - A list of globs to determine files excluded in entry points search (_optional_)
|
|
139
|
+
- `-cs, --compactSummary` - print a compact summary of reverse resolution with a count of found paths (_optional_)
|
|
140
|
+
- `-a, --all` - finds all paths combination of a given dependency. Might work very slow or crash for some projects due to heavy usage of RAM (_optional_)
|
|
141
|
+
|
|
142
|
+
### Command `entry-points`
|
|
143
|
+
|
|
144
|
+
Print list of entry points in current directory
|
|
145
|
+
|
|
146
|
+
#### Usage
|
|
147
|
+
|
|
148
|
+
```sh
|
|
149
|
+
rev-dep entry-points [options]
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
#### Options
|
|
153
|
+
|
|
154
|
+
- `-wc, --webpackConfig <path>` - path to webpack config to enable webpack aliases support (_optional_)
|
|
155
|
+
- `--cwd <path>` - path to a directory that should be used as a resolution root (_optional_)
|
|
156
|
+
- `--rr reexportRewire <value>` - resolve actual dependencies for "export \* from" statements (_optional_)
|
|
157
|
+
- `-i include <globs...>` - A list of globs to determine files included in entry points search (_optional_)
|
|
158
|
+
- `-e exclude <globs...>` - A list of globs to determine files excluded in entry points search (_optional_)
|
|
159
|
+
- `-pdc, --printDependenciesCount` - print count of entry point dependencies (_optional_)
|
|
160
|
+
- `-c, --count` - print just count of found entry points (_optional_)
|
|
161
|
+
|
|
162
|
+
### Command `files`
|
|
163
|
+
|
|
164
|
+
Get list of files required by entry point
|
|
165
|
+
|
|
166
|
+
#### Usage
|
|
167
|
+
|
|
168
|
+
```sh
|
|
169
|
+
rev-dep files <entryPoint> [options]
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
#### Arguments
|
|
173
|
+
|
|
174
|
+
- `entryPoint` - Path to entry point (**required**)
|
|
175
|
+
|
|
176
|
+
#### Options
|
|
177
|
+
|
|
178
|
+
- `-wc, --webpackConfig <path>` - path to webpack config to enable webpack aliases support (_optional_)
|
|
179
|
+
- `--cwd <path>` - path to a directory that should be used as a resolution root (_optional_)
|
|
180
|
+
- `--rr reexportRewire <value>` - resolve actual dependencies for "export \* from" statements (_optional_)
|
|
181
|
+
- `-c, --count` - print only count of entry point dependencies (_optional_)
|
|
182
|
+
|
|
183
|
+
### Command `docs`
|
|
184
|
+
|
|
185
|
+
Generate documentation of available commands into md file.
|
|
186
|
+
|
|
187
|
+
#### Usage
|
|
188
|
+
|
|
189
|
+
```sh
|
|
190
|
+
rev-dep docs <outputPath> [options]
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
#### Arguments
|
|
194
|
+
|
|
195
|
+
- `outputPath` - path to output \*.md file (**required**)
|
|
196
|
+
|
|
197
|
+
#### Options
|
|
198
|
+
|
|
199
|
+
- `-hl, --headerLevel <value>` - Initial header level (_optional_)
|
|
200
|
+
<!-- cli-docs-end -->
|
|
201
|
+
|
|
114
202
|
## Contributing
|
|
115
203
|
|
|
116
204
|
Project is open to contributions, just rise an issue if you have some ideas about features or you noticed a bug. After discussion we can approach implementation :)
|
package/babel.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
module.exports = require('./dist/babel/index.js')
|
package/bin.js
ADDED
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*eslint-disable @typescript-eslint/no-var-requires */
|
|
3
|
+
const node_path = require('path');
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const parser = require('@babel/parser');
|
|
6
|
+
const template = require('@babel/template').default;
|
|
7
|
+
const SKIP = Symbol('SKIP');
|
|
8
|
+
module.exports = function plugin({ types }, { tsConfigPath }) {
|
|
9
|
+
const tsConfig = require(tsConfigPath);
|
|
10
|
+
const aliases = tsConfig.compilerOptions.paths;
|
|
11
|
+
const aliasesKeys = Object.keys(aliases);
|
|
12
|
+
const aliasesRegexes = Object.keys(aliases).map((alias) => {
|
|
13
|
+
return new RegExp(`^${alias.replace('*', '(.)+')}$`);
|
|
14
|
+
});
|
|
15
|
+
const cache = new Map();
|
|
16
|
+
const getFile = (original, paths) => {
|
|
17
|
+
if (paths.length === 0) {
|
|
18
|
+
throw new Error('Cannot resolve import ' + original);
|
|
19
|
+
}
|
|
20
|
+
const path = paths[0];
|
|
21
|
+
try {
|
|
22
|
+
return [path, fs.readFileSync(path).toString()];
|
|
23
|
+
}
|
|
24
|
+
catch (e) {
|
|
25
|
+
return getFile(original, paths.slice(1));
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
const isPathRelativeOrAliased = (path) => {
|
|
29
|
+
const aliasRegexIdx = aliasesRegexes.findIndex((aliasRegex) => aliasRegex.test(path));
|
|
30
|
+
const isRelative = path.startsWith('./') || path.startsWith('../');
|
|
31
|
+
return aliasRegexIdx > -1 || isRelative;
|
|
32
|
+
};
|
|
33
|
+
const cacheKey = (identifier, filePath) => `${identifier}-${filePath}`;
|
|
34
|
+
const lookup = (identifier, filePath, cwd) => {
|
|
35
|
+
const cached = cache.get(cacheKey(identifier, filePath));
|
|
36
|
+
if (cached) {
|
|
37
|
+
return cached;
|
|
38
|
+
}
|
|
39
|
+
const withExtension = /(\.ts|\.tsx)$/.test(filePath)
|
|
40
|
+
? [filePath]
|
|
41
|
+
: [
|
|
42
|
+
`${filePath}.ts`,
|
|
43
|
+
`${filePath}.tsx`,
|
|
44
|
+
`${filePath}/index.ts`,
|
|
45
|
+
`${filePath}/index.tsx`,
|
|
46
|
+
`${filePath}.js`,
|
|
47
|
+
`${filePath}.jsx`,
|
|
48
|
+
`${filePath}/index.js`,
|
|
49
|
+
`${filePath}/index.jsx`
|
|
50
|
+
];
|
|
51
|
+
const [resolvedFilePath, file] = getFile(filePath, withExtension);
|
|
52
|
+
const ast = parser.parse(file, {
|
|
53
|
+
sourceType: 'module',
|
|
54
|
+
plugins: [
|
|
55
|
+
'jsx',
|
|
56
|
+
'typescript',
|
|
57
|
+
'objectRestSpread',
|
|
58
|
+
'classProperties',
|
|
59
|
+
'asyncGenerators',
|
|
60
|
+
'decorators-legacy'
|
|
61
|
+
]
|
|
62
|
+
});
|
|
63
|
+
/**
|
|
64
|
+
* {
|
|
65
|
+
* identifier?: string,
|
|
66
|
+
* source: string
|
|
67
|
+
* }
|
|
68
|
+
*/
|
|
69
|
+
const toLookup = [];
|
|
70
|
+
let resolvedAs = null;
|
|
71
|
+
ast.program.body.forEach((declaration) => {
|
|
72
|
+
var _a;
|
|
73
|
+
if (resolvedAs === null) {
|
|
74
|
+
if (types.isExportNamedDeclaration(declaration)) {
|
|
75
|
+
if (types.isVariableDeclaration(declaration.declaration)) {
|
|
76
|
+
const hasIdentifier = declaration.declaration.declarations.find((declarator) => {
|
|
77
|
+
return declarator.id.name === identifier;
|
|
78
|
+
});
|
|
79
|
+
if (hasIdentifier) {
|
|
80
|
+
resolvedAs = {
|
|
81
|
+
type: 'named',
|
|
82
|
+
identifier,
|
|
83
|
+
source: filePath
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
else if (types.isFunctionDeclaration(declaration.declaration) ||
|
|
88
|
+
types.isClassDeclaration(declaration.declaration)) {
|
|
89
|
+
if (declaration.declaration.id.name === identifier) {
|
|
90
|
+
resolvedAs = {
|
|
91
|
+
type: 'named',
|
|
92
|
+
identifier,
|
|
93
|
+
source: filePath
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
const source = (_a = declaration.source) === null || _a === void 0 ? void 0 : _a.value;
|
|
99
|
+
declaration.specifiers.forEach((specifier) => {
|
|
100
|
+
if (types.isExportSpecifier(specifier)) {
|
|
101
|
+
if (specifier.exported.name === identifier) {
|
|
102
|
+
if (specifier.local.name === 'default' && source) {
|
|
103
|
+
resolvedAs = {
|
|
104
|
+
type: 'default',
|
|
105
|
+
identifier,
|
|
106
|
+
source: getModulePath(source, resolvedFilePath, cwd)
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
else if (source === undefined) {
|
|
110
|
+
resolvedAs = {
|
|
111
|
+
type: 'named',
|
|
112
|
+
identifier,
|
|
113
|
+
source: filePath
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
else if (isPathRelativeOrAliased(source)) {
|
|
117
|
+
toLookup.push({
|
|
118
|
+
identifier: specifier.exported.local,
|
|
119
|
+
source: getModulePath(source, resolvedFilePath, cwd)
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
else if (types.isExportAllDeclaration(declaration) &&
|
|
128
|
+
isPathRelativeOrAliased(declaration.source.value)) {
|
|
129
|
+
toLookup.push({
|
|
130
|
+
identifier,
|
|
131
|
+
source: getModulePath(declaration.source.value, resolvedFilePath, cwd)
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
if (resolvedAs) {
|
|
137
|
+
return resolvedAs;
|
|
138
|
+
}
|
|
139
|
+
const nestedResult = toLookup
|
|
140
|
+
.map(({ identifier, source }) => lookup(identifier, source, cwd))
|
|
141
|
+
.filter(Boolean);
|
|
142
|
+
return nestedResult[0];
|
|
143
|
+
};
|
|
144
|
+
const getModulePath = (sourcePath, fileName, cwd) => {
|
|
145
|
+
var _a;
|
|
146
|
+
const aliasRegexIdx = aliasesRegexes.findIndex((aliasRegex) => aliasRegex.test(sourcePath));
|
|
147
|
+
const relativeFileName = node_path.relative(cwd, fileName);
|
|
148
|
+
const aliasKey = aliasesKeys[aliasRegexIdx];
|
|
149
|
+
const alias = (_a = aliases[aliasKey]) === null || _a === void 0 ? void 0 : _a[0];
|
|
150
|
+
let modulePath = '';
|
|
151
|
+
if (alias) {
|
|
152
|
+
let relative = alias;
|
|
153
|
+
if (aliasKey.endsWith('*')) {
|
|
154
|
+
const aliasKeyPrefix = aliasKey.replace('*', '');
|
|
155
|
+
relative = alias.replace('*', sourcePath.replace(aliasKeyPrefix, ''));
|
|
156
|
+
}
|
|
157
|
+
modulePath = node_path.resolve(cwd, relative);
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
// we need ../ to skip current file name
|
|
161
|
+
modulePath = node_path.resolve(relativeFileName, '../' + sourcePath);
|
|
162
|
+
}
|
|
163
|
+
return modulePath;
|
|
164
|
+
};
|
|
165
|
+
return {
|
|
166
|
+
visitor: {
|
|
167
|
+
Program: {
|
|
168
|
+
enter(_, { filename }) {
|
|
169
|
+
//console.log(filename);
|
|
170
|
+
}
|
|
171
|
+
},
|
|
172
|
+
ImportDeclaration(path, { filename, cwd }) {
|
|
173
|
+
const sourceRelative = (source) => {
|
|
174
|
+
const rel = node_path.relative(node_path.dirname(filename), source);
|
|
175
|
+
return rel.startsWith('.') ? rel : './' + rel;
|
|
176
|
+
};
|
|
177
|
+
const node = path.node;
|
|
178
|
+
const source = node.source;
|
|
179
|
+
if (source.type !== 'StringLiteral') {
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
const shouldSkip = node[SKIP] || !isPathRelativeOrAliased(source.value);
|
|
183
|
+
if (shouldSkip) {
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
const modulePath = getModulePath(source.value, filename, cwd);
|
|
187
|
+
const defaultSpecifier = node.specifiers.find((specifier) => specifier.type === 'ImportDefaultSpecifier');
|
|
188
|
+
const namespaceSpecifier = node.specifiers.find((specifier) => specifier.type === 'ImportNamespaceSpecifier');
|
|
189
|
+
const specifiers = node.specifiers.filter((specifier) => specifier.type === 'ImportSpecifier');
|
|
190
|
+
const results = specifiers.map((specifier) => {
|
|
191
|
+
const importedName = specifier.imported.name;
|
|
192
|
+
const result = lookup(importedName, modulePath, cwd);
|
|
193
|
+
if (!result) {
|
|
194
|
+
return {
|
|
195
|
+
identifier: importedName,
|
|
196
|
+
local: specifier.local.name,
|
|
197
|
+
source: source.value
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
cache.set(cacheKey(importedName, modulePath), result);
|
|
201
|
+
return {
|
|
202
|
+
...result,
|
|
203
|
+
source: sourceRelative(result.source),
|
|
204
|
+
local: specifier.local.name
|
|
205
|
+
};
|
|
206
|
+
});
|
|
207
|
+
const defaultResult = defaultSpecifier
|
|
208
|
+
? lookup('default', modulePath, cwd)
|
|
209
|
+
: null;
|
|
210
|
+
if (defaultResult) {
|
|
211
|
+
cache.set(cacheKey('default', modulePath), defaultResult);
|
|
212
|
+
}
|
|
213
|
+
const buildNamed = template(`
|
|
214
|
+
import { %%IMPORT_NAME%% } from %%SOURCE%%;
|
|
215
|
+
`);
|
|
216
|
+
const buildNamedWithAlias = template(`
|
|
217
|
+
import { %%IMPORTED_NAME%% as %%LOCAL_NAME%% } from %%SOURCE%%;
|
|
218
|
+
`);
|
|
219
|
+
const buildDefault = template(`
|
|
220
|
+
import %%IMPORT_NAME%% from %%SOURCE%%;
|
|
221
|
+
`);
|
|
222
|
+
const buildNamespace = template(`
|
|
223
|
+
import * as %%IMPORT_NAME%% from %%SOURCE%%;
|
|
224
|
+
`);
|
|
225
|
+
const defaultImport = defaultResult
|
|
226
|
+
? [
|
|
227
|
+
buildDefault({
|
|
228
|
+
IMPORT_NAME: types.identifier(defaultSpecifier.local.name),
|
|
229
|
+
SOURCE: types.stringLiteral(sourceRelative(defaultResult.source))
|
|
230
|
+
})
|
|
231
|
+
]
|
|
232
|
+
: defaultSpecifier
|
|
233
|
+
? [
|
|
234
|
+
buildDefault({
|
|
235
|
+
IMPORT_NAME: types.identifier(defaultSpecifier.local.name),
|
|
236
|
+
SOURCE: types.stringLiteral(source.value)
|
|
237
|
+
})
|
|
238
|
+
]
|
|
239
|
+
: [];
|
|
240
|
+
const namespaceImport = namespaceSpecifier
|
|
241
|
+
? [
|
|
242
|
+
buildNamespace({
|
|
243
|
+
IMPORT_NAME: types.identifier(namespaceSpecifier.local.name),
|
|
244
|
+
SOURCE: types.stringLiteral(source.value)
|
|
245
|
+
})
|
|
246
|
+
]
|
|
247
|
+
: [];
|
|
248
|
+
const named = results.map(({ type, identifier, local, source }) => {
|
|
249
|
+
if (type === 'default') {
|
|
250
|
+
return buildDefault({
|
|
251
|
+
IMPORT_NAME: types.identifier(identifier),
|
|
252
|
+
SOURCE: types.stringLiteral(source)
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
else if (identifier !== local) {
|
|
256
|
+
return buildNamedWithAlias({
|
|
257
|
+
IMPORTED_NAME: types.identifier(identifier),
|
|
258
|
+
LOCAL_NAME: types.identifier(local),
|
|
259
|
+
SOURCE: types.stringLiteral(source)
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
else {
|
|
263
|
+
return buildNamed({
|
|
264
|
+
IMPORT_NAME: types.identifier(identifier),
|
|
265
|
+
SOURCE: types.stringLiteral(source)
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
});
|
|
269
|
+
const newImports = [...namespaceImport, ...defaultImport, ...named].map((node) => {
|
|
270
|
+
node[SKIP] = true;
|
|
271
|
+
return node;
|
|
272
|
+
});
|
|
273
|
+
path.replaceWithMultiple(newImports);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
};
|
|
277
|
+
};
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*eslint-disable @typescript-eslint/no-var-requires */
|
|
3
|
+
const node_path = require('path');
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const parser = require('@babel/parser');
|
|
6
|
+
const template = require('@babel/template').default;
|
|
7
|
+
const SKIP = Symbol('SKIP');
|
|
8
|
+
module.exports = function plugin({ types }, { tsConfigPath }) {
|
|
9
|
+
const tsConfig = require(tsConfigPath);
|
|
10
|
+
const aliases = tsConfig.compilerOptions.paths;
|
|
11
|
+
const aliasesKeys = Object.keys(aliases);
|
|
12
|
+
const aliasesRegexes = Object.keys(aliases).map((alias) => {
|
|
13
|
+
return new RegExp(`^${alias.replace('*', '(.)+')}$`);
|
|
14
|
+
});
|
|
15
|
+
const cache = new Map();
|
|
16
|
+
const getFile = (original, paths) => {
|
|
17
|
+
if (paths.length === 0) {
|
|
18
|
+
throw new Error('Cannot resolve import ' + original);
|
|
19
|
+
}
|
|
20
|
+
const path = paths[0];
|
|
21
|
+
try {
|
|
22
|
+
return [path, fs.readFileSync(path).toString()];
|
|
23
|
+
}
|
|
24
|
+
catch (e) {
|
|
25
|
+
return getFile(original, paths.slice(1));
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
const isPathRelativeOrAliased = (path) => {
|
|
29
|
+
const aliasRegexIdx = aliasesRegexes.findIndex((aliasRegex) => aliasRegex.test(path));
|
|
30
|
+
const isRelative = path.startsWith('./') || path.startsWith('../');
|
|
31
|
+
return aliasRegexIdx > -1 || isRelative;
|
|
32
|
+
};
|
|
33
|
+
const cacheKey = (identifier, filePath) => `${identifier}-${filePath}`;
|
|
34
|
+
const lookup = (identifier, filePath, cwd) => {
|
|
35
|
+
const cached = cache.get(cacheKey(identifier, filePath));
|
|
36
|
+
if (cached) {
|
|
37
|
+
return cached;
|
|
38
|
+
}
|
|
39
|
+
const withExtension = /(\.ts|\.tsx)$/.test(filePath)
|
|
40
|
+
? [filePath]
|
|
41
|
+
: [
|
|
42
|
+
`${filePath}.ts`,
|
|
43
|
+
`${filePath}.tsx`,
|
|
44
|
+
`${filePath}/index.ts`,
|
|
45
|
+
`${filePath}/index.tsx`,
|
|
46
|
+
`${filePath}.js`,
|
|
47
|
+
`${filePath}.jsx`,
|
|
48
|
+
`${filePath}/index.js`,
|
|
49
|
+
`${filePath}/index.jsx`
|
|
50
|
+
];
|
|
51
|
+
const [resolvedFilePath, file] = getFile(filePath, withExtension);
|
|
52
|
+
const ast = parser.parse(file, {
|
|
53
|
+
sourceType: 'module',
|
|
54
|
+
plugins: [
|
|
55
|
+
'jsx',
|
|
56
|
+
'typescript',
|
|
57
|
+
'objectRestSpread',
|
|
58
|
+
'classProperties',
|
|
59
|
+
'asyncGenerators',
|
|
60
|
+
'decorators-legacy'
|
|
61
|
+
]
|
|
62
|
+
});
|
|
63
|
+
/**
|
|
64
|
+
* {
|
|
65
|
+
* identifier?: string,
|
|
66
|
+
* source: string
|
|
67
|
+
* }
|
|
68
|
+
*/
|
|
69
|
+
const toLookup = [];
|
|
70
|
+
let resolvedAs = null;
|
|
71
|
+
ast.program.body.forEach((declaration) => {
|
|
72
|
+
var _a;
|
|
73
|
+
if (resolvedAs === null) {
|
|
74
|
+
if (types.isExportNamedDeclaration(declaration)) {
|
|
75
|
+
if (types.isVariableDeclaration(declaration.declaration)) {
|
|
76
|
+
const hasIdentifier = declaration.declaration.declarations.find((declarator) => {
|
|
77
|
+
return declarator.id.name === identifier;
|
|
78
|
+
});
|
|
79
|
+
if (hasIdentifier) {
|
|
80
|
+
resolvedAs = {
|
|
81
|
+
type: 'named',
|
|
82
|
+
identifier,
|
|
83
|
+
source: filePath
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
else if (types.isFunctionDeclaration(declaration.declaration) ||
|
|
88
|
+
types.isClassDeclaration(declaration.declaration)) {
|
|
89
|
+
if (declaration.declaration.id.name === identifier) {
|
|
90
|
+
resolvedAs = {
|
|
91
|
+
type: 'named',
|
|
92
|
+
identifier,
|
|
93
|
+
source: filePath
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
const source = (_a = declaration.source) === null || _a === void 0 ? void 0 : _a.value;
|
|
99
|
+
declaration.specifiers.forEach((specifier) => {
|
|
100
|
+
if (types.isExportSpecifier(specifier)) {
|
|
101
|
+
if (specifier.exported.name === identifier) {
|
|
102
|
+
if (specifier.local.name === 'default' && source) {
|
|
103
|
+
resolvedAs = {
|
|
104
|
+
type: 'default',
|
|
105
|
+
identifier,
|
|
106
|
+
source: getModulePath(source, resolvedFilePath, cwd)
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
else if (source === undefined) {
|
|
110
|
+
resolvedAs = {
|
|
111
|
+
type: 'named',
|
|
112
|
+
identifier,
|
|
113
|
+
source: filePath
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
else if (isPathRelativeOrAliased(source)) {
|
|
117
|
+
toLookup.push({
|
|
118
|
+
identifier: specifier.exported.local,
|
|
119
|
+
source: getModulePath(source, resolvedFilePath, cwd)
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
else if (types.isExportAllDeclaration(declaration) &&
|
|
128
|
+
isPathRelativeOrAliased(declaration.source.value)) {
|
|
129
|
+
toLookup.push({
|
|
130
|
+
identifier,
|
|
131
|
+
source: getModulePath(declaration.source.value, resolvedFilePath, cwd)
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
if (resolvedAs) {
|
|
137
|
+
return resolvedAs;
|
|
138
|
+
}
|
|
139
|
+
const nestedResult = toLookup
|
|
140
|
+
.map(({ identifier, source }) => lookup(identifier, source, cwd))
|
|
141
|
+
.filter(Boolean);
|
|
142
|
+
return nestedResult[0];
|
|
143
|
+
};
|
|
144
|
+
const getModulePath = (sourcePath, fileName, cwd) => {
|
|
145
|
+
var _a;
|
|
146
|
+
const aliasRegexIdx = aliasesRegexes.findIndex((aliasRegex) => aliasRegex.test(sourcePath));
|
|
147
|
+
const relativeFileName = node_path.relative(cwd, fileName);
|
|
148
|
+
const aliasKey = aliasesKeys[aliasRegexIdx];
|
|
149
|
+
const alias = (_a = aliases[aliasKey]) === null || _a === void 0 ? void 0 : _a[0];
|
|
150
|
+
let modulePath = '';
|
|
151
|
+
if (alias) {
|
|
152
|
+
let relative = alias;
|
|
153
|
+
if (aliasKey.endsWith('*')) {
|
|
154
|
+
const aliasKeyPrefix = aliasKey.replace('*', '');
|
|
155
|
+
relative = alias.replace('*', sourcePath.replace(aliasKeyPrefix, ''));
|
|
156
|
+
}
|
|
157
|
+
modulePath = node_path.resolve(cwd, relative);
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
// we need ../ to skip current file name
|
|
161
|
+
modulePath = node_path.resolve(relativeFileName, '../' + sourcePath);
|
|
162
|
+
}
|
|
163
|
+
return modulePath;
|
|
164
|
+
};
|
|
165
|
+
return {
|
|
166
|
+
visitor: {
|
|
167
|
+
Program: {
|
|
168
|
+
enter(_, { filename }) {
|
|
169
|
+
//console.log(filename);
|
|
170
|
+
}
|
|
171
|
+
},
|
|
172
|
+
ImportDeclaration(path, { filename, cwd }) {
|
|
173
|
+
const sourceRelative = (source) => {
|
|
174
|
+
const rel = node_path.relative(node_path.dirname(filename), source);
|
|
175
|
+
return rel.startsWith('.') ? rel : './' + rel;
|
|
176
|
+
};
|
|
177
|
+
const node = path.node;
|
|
178
|
+
const source = node.source;
|
|
179
|
+
if (source.type !== 'StringLiteral') {
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
const shouldSkip = node[SKIP] || !isPathRelativeOrAliased(source.value);
|
|
183
|
+
if (shouldSkip) {
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
const modulePath = getModulePath(source.value, filename, cwd);
|
|
187
|
+
const defaultSpecifier = node.specifiers.find((specifier) => specifier.type === 'ImportDefaultSpecifier');
|
|
188
|
+
const namespaceSpecifier = node.specifiers.find((specifier) => specifier.type === 'ImportNamespaceSpecifier');
|
|
189
|
+
const specifiers = node.specifiers.filter((specifier) => specifier.type === 'ImportSpecifier');
|
|
190
|
+
const results = specifiers.map((specifier) => {
|
|
191
|
+
const importedName = specifier.imported.name;
|
|
192
|
+
const result = lookup(importedName, modulePath, cwd);
|
|
193
|
+
if (!result) {
|
|
194
|
+
return {
|
|
195
|
+
identifier: importedName,
|
|
196
|
+
local: specifier.local.name,
|
|
197
|
+
source: source.value
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
cache.set(cacheKey(importedName, modulePath), result);
|
|
201
|
+
return {
|
|
202
|
+
...result,
|
|
203
|
+
source: sourceRelative(result.source),
|
|
204
|
+
local: specifier.local.name
|
|
205
|
+
};
|
|
206
|
+
});
|
|
207
|
+
const defaultResult = defaultSpecifier
|
|
208
|
+
? lookup('default', modulePath, cwd)
|
|
209
|
+
: null;
|
|
210
|
+
if (defaultResult) {
|
|
211
|
+
cache.set(cacheKey('default', modulePath), defaultResult);
|
|
212
|
+
}
|
|
213
|
+
const buildNamed = template(`
|
|
214
|
+
import { %%IMPORT_NAME%% } from %%SOURCE%%;
|
|
215
|
+
`);
|
|
216
|
+
const buildNamedWithAlias = template(`
|
|
217
|
+
import { %%IMPORTED_NAME%% as %%LOCAL_NAME%% } from %%SOURCE%%;
|
|
218
|
+
`);
|
|
219
|
+
const buildDefault = template(`
|
|
220
|
+
import %%IMPORT_NAME%% from %%SOURCE%%;
|
|
221
|
+
`);
|
|
222
|
+
const buildNamespace = template(`
|
|
223
|
+
import * as %%IMPORT_NAME%% from %%SOURCE%%;
|
|
224
|
+
`);
|
|
225
|
+
const defaultImport = defaultResult
|
|
226
|
+
? [
|
|
227
|
+
buildDefault({
|
|
228
|
+
IMPORT_NAME: types.identifier(defaultSpecifier.local.name),
|
|
229
|
+
SOURCE: types.stringLiteral(sourceRelative(defaultResult.source))
|
|
230
|
+
})
|
|
231
|
+
]
|
|
232
|
+
: defaultSpecifier
|
|
233
|
+
? [
|
|
234
|
+
buildDefault({
|
|
235
|
+
IMPORT_NAME: types.identifier(defaultSpecifier.local.name),
|
|
236
|
+
SOURCE: types.stringLiteral(source.value)
|
|
237
|
+
})
|
|
238
|
+
]
|
|
239
|
+
: [];
|
|
240
|
+
const namespaceImport = namespaceSpecifier
|
|
241
|
+
? [
|
|
242
|
+
buildNamespace({
|
|
243
|
+
IMPORT_NAME: types.identifier(namespaceSpecifier.local.name),
|
|
244
|
+
SOURCE: types.stringLiteral(source.value)
|
|
245
|
+
})
|
|
246
|
+
]
|
|
247
|
+
: [];
|
|
248
|
+
const named = results.map(({ type, identifier, local, source }) => {
|
|
249
|
+
if (type === 'default') {
|
|
250
|
+
return buildDefault({
|
|
251
|
+
IMPORT_NAME: types.identifier(identifier),
|
|
252
|
+
SOURCE: types.stringLiteral(source)
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
else if (identifier !== local) {
|
|
256
|
+
return buildNamedWithAlias({
|
|
257
|
+
IMPORTED_NAME: types.identifier(identifier),
|
|
258
|
+
LOCAL_NAME: types.identifier(local),
|
|
259
|
+
SOURCE: types.stringLiteral(source)
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
else {
|
|
263
|
+
return buildNamed({
|
|
264
|
+
IMPORT_NAME: types.identifier(identifier),
|
|
265
|
+
SOURCE: types.stringLiteral(source)
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
});
|
|
269
|
+
const newImports = [...namespaceImport, ...defaultImport, ...named].map((node) => {
|
|
270
|
+
node[SKIP] = true;
|
|
271
|
+
return node;
|
|
272
|
+
});
|
|
273
|
+
path.replaceWithMultiple(newImports);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
};
|
|
277
|
+
};
|