@nodesecure/js-x-ray 7.1.1 → 7.3.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 +6 -1
- package/index.js +16 -0
- package/package.json +3 -3
- package/src/AstAnalyser.js +207 -137
- package/src/EntryFilesAnalyser.js +112 -99
- package/src/utils/isOneLineExpressionExport.js +12 -6
- package/types/api.d.ts +2 -0
package/README.md
CHANGED
|
@@ -223,6 +223,8 @@ interface RuntimeOptions {
|
|
|
223
223
|
module?: boolean;
|
|
224
224
|
removeHTMLComments?: boolean;
|
|
225
225
|
isMinified?: boolean;
|
|
226
|
+
initialize?: (sourceFile: SourceFile) => void;
|
|
227
|
+
finalize?: (sourceFile: SourceFile) => void;
|
|
226
228
|
}
|
|
227
229
|
```
|
|
228
230
|
|
|
@@ -256,6 +258,8 @@ interface RuntimeFileOptions {
|
|
|
256
258
|
module?: boolean;
|
|
257
259
|
removeHTMLComments?: boolean;
|
|
258
260
|
packageName?: string;
|
|
261
|
+
initialize?: (sourceFile: SourceFile) => void;
|
|
262
|
+
finalize?: (sourceFile: SourceFile) => void;
|
|
259
263
|
}
|
|
260
264
|
```
|
|
261
265
|
|
|
@@ -303,7 +307,7 @@ $ yarn add @nodesecure/estree-ast-util
|
|
|
303
307
|
## Contributors ✨
|
|
304
308
|
|
|
305
309
|
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
|
|
306
|
-
[](#contributors-)
|
|
307
311
|
<!-- ALL-CONTRIBUTORS-BADGE:END -->
|
|
308
312
|
|
|
309
313
|
Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
|
|
@@ -335,6 +339,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
|
|
|
335
339
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/FredGuiou"><img src="https://avatars.githubusercontent.com/u/99122562?v=4?s=100" width="100px;" alt="FredGuiou"/><br /><sub><b>FredGuiou</b></sub></a><br /><a href="https://github.com/NodeSecure/js-x-ray/commits?author=FredGuiou" title="Documentation">📖</a> <a href="https://github.com/NodeSecure/js-x-ray/commits?author=FredGuiou" title="Code">💻</a></td>
|
|
336
340
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/madina0801"><img src="https://avatars.githubusercontent.com/u/101329759?v=4?s=100" width="100px;" alt="Madina"/><br /><sub><b>Madina</b></sub></a><br /><a href="https://github.com/NodeSecure/js-x-ray/commits?author=madina0801" title="Code">💻</a></td>
|
|
337
341
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/sairuss7"><img src="https://avatars.githubusercontent.com/u/87803528?v=4?s=100" width="100px;" alt="SairussDev"/><br /><sub><b>SairussDev</b></sub></a><br /><a href="https://github.com/NodeSecure/js-x-ray/commits?author=sairuss7" title="Code">💻</a></td>
|
|
342
|
+
<td align="center" valign="top" width="14.28%"><a href="https://github.com/fless-lab"><img src="https://avatars.githubusercontent.com/u/71844440?v=4?s=100" width="100px;" alt="Abdou-Raouf ATARMLA"/><br /><sub><b>Abdou-Raouf ATARMLA</b></sub></a><br /><a href="https://github.com/NodeSecure/js-x-ray/commits?author=fless-lab" title="Code">💻</a></td>
|
|
338
343
|
</tr>
|
|
339
344
|
</tbody>
|
|
340
345
|
</table>
|
package/index.js
CHANGED
|
@@ -8,6 +8,14 @@ function runASTAnalysis(
|
|
|
8
8
|
str,
|
|
9
9
|
options = Object.create(null)
|
|
10
10
|
) {
|
|
11
|
+
process.emitWarning(
|
|
12
|
+
'The runASTAnalysis API is deprecated and will be removed in v8. Please use the AstAnalyser class instead.',
|
|
13
|
+
{
|
|
14
|
+
code: 'DeprecationWarning',
|
|
15
|
+
detail: 'The runASTAnalysis API is deprecated and will be removed in v8. Please use the AstAnalyser class instead.'
|
|
16
|
+
}
|
|
17
|
+
);
|
|
18
|
+
|
|
11
19
|
const {
|
|
12
20
|
customParser = new JsSourceParser(),
|
|
13
21
|
customProbes = [],
|
|
@@ -28,6 +36,14 @@ async function runASTAnalysisOnFile(
|
|
|
28
36
|
pathToFile,
|
|
29
37
|
options = {}
|
|
30
38
|
) {
|
|
39
|
+
process.emitWarning(
|
|
40
|
+
'The runASTAnalysisOnFile API is deprecated and will be removed in v8. Please use the AstAnalyser class instead.',
|
|
41
|
+
{
|
|
42
|
+
code: 'DeprecationWarning',
|
|
43
|
+
detail: 'The runASTAnalysisOnFile API is deprecated and will be removed in v8. Please use the AstAnalyser class instead.'
|
|
44
|
+
}
|
|
45
|
+
);
|
|
46
|
+
|
|
31
47
|
const {
|
|
32
48
|
customProbes = [],
|
|
33
49
|
customParser = new JsSourceParser(),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nodesecure/js-x-ray",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.3.0",
|
|
4
4
|
"description": "JavaScript AST XRay analysis",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": "./index.js",
|
|
@@ -56,10 +56,10 @@
|
|
|
56
56
|
"devDependencies": {
|
|
57
57
|
"@nodesecure/eslint-config": "^1.6.0",
|
|
58
58
|
"@types/node": "^20.6.2",
|
|
59
|
-
"c8": "^
|
|
59
|
+
"c8": "^10.1.2",
|
|
60
60
|
"cross-env": "^7.0.3",
|
|
61
61
|
"eslint": "^9.0.0",
|
|
62
|
-
"glob": "^
|
|
62
|
+
"glob": "^11.0.0",
|
|
63
63
|
"iterator-matcher": "^2.1.0",
|
|
64
64
|
"pkg-ok": "^3.0.0"
|
|
65
65
|
}
|
package/src/AstAnalyser.js
CHANGED
|
@@ -1,137 +1,207 @@
|
|
|
1
|
-
// Import Node.js Dependencies
|
|
2
|
-
import fs from "node:fs/promises";
|
|
3
|
-
import
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
import
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
* @
|
|
18
|
-
* @param {
|
|
19
|
-
* @param {
|
|
20
|
-
* @param {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
this.
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
1
|
+
// Import Node.js Dependencies
|
|
2
|
+
import fs from "node:fs/promises";
|
|
3
|
+
import fsSync from "node:fs";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
|
|
6
|
+
// Import Third-party Dependencies
|
|
7
|
+
import { walk } from "estree-walker";
|
|
8
|
+
import isMinified from "is-minified-code";
|
|
9
|
+
|
|
10
|
+
// Import Internal Dependencies
|
|
11
|
+
import { SourceFile } from "./SourceFile.js";
|
|
12
|
+
import { isOneLineExpressionExport } from "./utils/index.js";
|
|
13
|
+
import { JsSourceParser } from "./JsSourceParser.js";
|
|
14
|
+
|
|
15
|
+
export class AstAnalyser {
|
|
16
|
+
/**
|
|
17
|
+
* @constructor
|
|
18
|
+
* @param {object} [options={}]
|
|
19
|
+
* @param {SourceParser} [options.customParser]
|
|
20
|
+
* @param {Array<object>} [options.customProbes]
|
|
21
|
+
* @param {boolean} [options.skipDefaultProbes=false]
|
|
22
|
+
*/
|
|
23
|
+
constructor(options = {}) {
|
|
24
|
+
this.parser = options.customParser ?? new JsSourceParser();
|
|
25
|
+
this.probesOptions = {
|
|
26
|
+
customProbes: options.customProbes ?? [],
|
|
27
|
+
skipDefaultProbes: options.skipDefaultProbes ?? false
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
analyse(str, options = Object.create(null)) {
|
|
32
|
+
const {
|
|
33
|
+
isMinified = false,
|
|
34
|
+
module = true,
|
|
35
|
+
removeHTMLComments = false,
|
|
36
|
+
initialize,
|
|
37
|
+
finalize
|
|
38
|
+
} = options;
|
|
39
|
+
|
|
40
|
+
const body = this.parser.parse(this.prepareSource(str, { removeHTMLComments }), {
|
|
41
|
+
isEcmaScriptModule: Boolean(module)
|
|
42
|
+
});
|
|
43
|
+
const source = new SourceFile(str, this.probesOptions);
|
|
44
|
+
|
|
45
|
+
// TODO: this check should be factorized in a way that we reuse it
|
|
46
|
+
// on analyze and anlyseFile
|
|
47
|
+
if (initialize) {
|
|
48
|
+
if (typeof initialize !== "function") {
|
|
49
|
+
throw new TypeError("options.initialize must be a function");
|
|
50
|
+
}
|
|
51
|
+
initialize(source);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// we walk each AST Nodes, this is a purely synchronous I/O
|
|
55
|
+
walk(body, {
|
|
56
|
+
enter(node) {
|
|
57
|
+
// Skip the root of the AST.
|
|
58
|
+
if (Array.isArray(node)) {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const action = source.walk(node);
|
|
63
|
+
if (action === "skip") {
|
|
64
|
+
this.skip();
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
// TODO: this check should be factorized in a way that we reuse it
|
|
70
|
+
// on analyze and anlyseFile
|
|
71
|
+
if (finalize) {
|
|
72
|
+
if (typeof finalize !== "function") {
|
|
73
|
+
throw new TypeError("options.finalize must be a function");
|
|
74
|
+
}
|
|
75
|
+
finalize(source);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return {
|
|
79
|
+
...source.getResult(isMinified),
|
|
80
|
+
dependencies: source.dependencies,
|
|
81
|
+
isOneLineRequire: isOneLineExpressionExport(body)
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
async analyseFile(
|
|
86
|
+
pathToFile,
|
|
87
|
+
options = {}
|
|
88
|
+
) {
|
|
89
|
+
try {
|
|
90
|
+
const {
|
|
91
|
+
packageName = null,
|
|
92
|
+
module = true,
|
|
93
|
+
removeHTMLComments = false,
|
|
94
|
+
initialize,
|
|
95
|
+
finalize
|
|
96
|
+
} = options;
|
|
97
|
+
|
|
98
|
+
const str = await fs.readFile(pathToFile, "utf-8");
|
|
99
|
+
const filePathString = pathToFile instanceof URL ? pathToFile.href : pathToFile;
|
|
100
|
+
|
|
101
|
+
const isMin = filePathString.includes(".min") || isMinified(str);
|
|
102
|
+
const data = this.analyse(str, {
|
|
103
|
+
isMinified: isMin,
|
|
104
|
+
module: path.extname(filePathString) === ".mjs" ? true : module,
|
|
105
|
+
removeHTMLComments,
|
|
106
|
+
initialize,
|
|
107
|
+
finalize
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
if (packageName !== null) {
|
|
111
|
+
data.dependencies.delete(packageName);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return {
|
|
115
|
+
ok: true,
|
|
116
|
+
dependencies: data.dependencies,
|
|
117
|
+
warnings: data.warnings,
|
|
118
|
+
isMinified: !data.isOneLineRequire && isMin
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
catch (error) {
|
|
122
|
+
return {
|
|
123
|
+
ok: false,
|
|
124
|
+
warnings: [
|
|
125
|
+
{ kind: "parsing-error", value: error.message, location: [[0, 0], [0, 0]] }
|
|
126
|
+
]
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
analyseFileSync(
|
|
132
|
+
pathToFile,
|
|
133
|
+
options = {}
|
|
134
|
+
) {
|
|
135
|
+
try {
|
|
136
|
+
const {
|
|
137
|
+
packageName = null,
|
|
138
|
+
module = true,
|
|
139
|
+
removeHTMLComments = false,
|
|
140
|
+
initialize,
|
|
141
|
+
finalize
|
|
142
|
+
} = options;
|
|
143
|
+
|
|
144
|
+
const str = fsSync.readFileSync(pathToFile, "utf-8");
|
|
145
|
+
const filePathString = pathToFile instanceof URL ? pathToFile.href : pathToFile;
|
|
146
|
+
|
|
147
|
+
const isMin = filePathString.includes(".min") || isMinified(str);
|
|
148
|
+
const data = this.analyse(str, {
|
|
149
|
+
isMinified: isMin,
|
|
150
|
+
module: path.extname(filePathString) === ".mjs" ? true : module,
|
|
151
|
+
removeHTMLComments,
|
|
152
|
+
initialize,
|
|
153
|
+
finalize
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
if (packageName !== null) {
|
|
157
|
+
data.dependencies.delete(packageName);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return {
|
|
161
|
+
ok: true,
|
|
162
|
+
dependencies: data.dependencies,
|
|
163
|
+
warnings: data.warnings,
|
|
164
|
+
isMinified: !data.isOneLineRequire && isMin
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
catch (error) {
|
|
168
|
+
return {
|
|
169
|
+
ok: false,
|
|
170
|
+
warnings: [
|
|
171
|
+
{ kind: "parsing-error", value: error.message, location: [[0, 0], [0, 0]] }
|
|
172
|
+
]
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* @param {!string} source
|
|
179
|
+
* @param {object} options
|
|
180
|
+
* @param {boolean} [options.removeHTMLComments=false]
|
|
181
|
+
*/
|
|
182
|
+
prepareSource(source, options = {}) {
|
|
183
|
+
if (typeof source !== "string") {
|
|
184
|
+
throw new TypeError("source must be a string");
|
|
185
|
+
}
|
|
186
|
+
const { removeHTMLComments = false } = options;
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* if the file start with a shebang then we remove it because meriyah.parseScript fail to parse it.
|
|
190
|
+
* @example
|
|
191
|
+
* #!/usr/bin/env node
|
|
192
|
+
*/
|
|
193
|
+
const rawNoShebang = source.startsWith("#") ?
|
|
194
|
+
source.slice(source.indexOf("\n") + 1) : source;
|
|
195
|
+
|
|
196
|
+
return removeHTMLComments ?
|
|
197
|
+
this.#removeHTMLComment(rawNoShebang) : rawNoShebang;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* @param {!string} str
|
|
202
|
+
* @returns {string}
|
|
203
|
+
*/
|
|
204
|
+
#removeHTMLComment(str) {
|
|
205
|
+
return str.replaceAll(/<!--[\s\S]*?(?:-->)/g, "");
|
|
206
|
+
}
|
|
207
|
+
}
|
|
@@ -1,99 +1,112 @@
|
|
|
1
|
-
// Import Node.js Dependencies
|
|
2
|
-
import fs from "node:fs/promises";
|
|
3
|
-
import path from "node:path";
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
* @
|
|
15
|
-
* @param {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
1
|
+
// Import Node.js Dependencies
|
|
2
|
+
import fs from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
|
|
6
|
+
// Import Internal Dependencies
|
|
7
|
+
import { AstAnalyser } from "./AstAnalyser.js";
|
|
8
|
+
|
|
9
|
+
// CONSTANTS
|
|
10
|
+
const kDefaultExtensions = ["js", "cjs", "mjs", "node"];
|
|
11
|
+
|
|
12
|
+
export class EntryFilesAnalyser {
|
|
13
|
+
/**
|
|
14
|
+
* @constructor
|
|
15
|
+
* @param {object} [options={}]
|
|
16
|
+
* @param {AstAnalyser} [options.astAnalyzer=new AstAnalyser()]
|
|
17
|
+
* @param {function} [options.loadExtensions]
|
|
18
|
+
*/
|
|
19
|
+
constructor(options = {}) {
|
|
20
|
+
this.astAnalyzer = options.astAnalyzer ?? new AstAnalyser();
|
|
21
|
+
const rawAllowedExtensions = options.loadExtensions
|
|
22
|
+
? options.loadExtensions(kDefaultExtensions)
|
|
23
|
+
: kDefaultExtensions;
|
|
24
|
+
|
|
25
|
+
this.allowedExtensions = new Set(rawAllowedExtensions);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Asynchronously analyze a set of entry files yielding analysis reports.
|
|
30
|
+
*
|
|
31
|
+
* @param {(string | URL)[]} entryFiles
|
|
32
|
+
* @yields {Object} - Yields an object containing the analysis report for each file.
|
|
33
|
+
*/
|
|
34
|
+
async* analyse(entryFiles) {
|
|
35
|
+
this.analyzedDeps = new Set();
|
|
36
|
+
|
|
37
|
+
for (const file of entryFiles) {
|
|
38
|
+
yield* this.#analyzeFile(file);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async* #analyzeFile(file) {
|
|
43
|
+
const filePath = file instanceof URL ? fileURLToPath(file) : file;
|
|
44
|
+
const report = await this.astAnalyzer.analyseFile(file);
|
|
45
|
+
|
|
46
|
+
yield { url: filePath, ...report };
|
|
47
|
+
|
|
48
|
+
if (!report.ok) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
yield* this.#analyzeDeps(
|
|
53
|
+
report.dependencies,
|
|
54
|
+
path.dirname(filePath)
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
async* #analyzeDeps(deps, basePath) {
|
|
59
|
+
for (const [name] of deps) {
|
|
60
|
+
const depPath = await this.#getInternalDepPath(name, basePath);
|
|
61
|
+
|
|
62
|
+
if (depPath && !this.analyzedDeps.has(depPath)) {
|
|
63
|
+
this.analyzedDeps.add(depPath);
|
|
64
|
+
|
|
65
|
+
yield* this.#analyzeFile(depPath);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
async #getInternalDepPath(name, basePath) {
|
|
71
|
+
const depPath = path.join(basePath, name);
|
|
72
|
+
const existingExt = path.extname(name);
|
|
73
|
+
|
|
74
|
+
if (existingExt === "") {
|
|
75
|
+
for (const ext of this.allowedExtensions) {
|
|
76
|
+
const depPathWithExt = `${depPath}.${ext}`;
|
|
77
|
+
|
|
78
|
+
const fileExist = await this.#fileExists(depPathWithExt);
|
|
79
|
+
if (fileExist) {
|
|
80
|
+
return depPathWithExt;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
if (!this.allowedExtensions.has(existingExt.slice(1))) {
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const fileExist = await this.#fileExists(depPath);
|
|
90
|
+
if (fileExist) {
|
|
91
|
+
return depPath;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
async #fileExists(path) {
|
|
99
|
+
try {
|
|
100
|
+
await fs.access(path, fs.constants.R_OK);
|
|
101
|
+
|
|
102
|
+
return true;
|
|
103
|
+
}
|
|
104
|
+
catch (error) {
|
|
105
|
+
if (error.code !== "ENOENT") {
|
|
106
|
+
throw error;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return false;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
@@ -2,17 +2,23 @@
|
|
|
2
2
|
import { exportAssignmentHasRequireLeave } from "./exportAssignmentHasRequireLeave.js";
|
|
3
3
|
|
|
4
4
|
export function isOneLineExpressionExport(body) {
|
|
5
|
-
if (body.length > 1) {
|
|
5
|
+
if (body.length === 0 || body.length > 1) {
|
|
6
6
|
return false;
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
const [firstNode] = body;
|
|
10
|
+
if (firstNode.type !== "ExpressionStatement") {
|
|
10
11
|
return false;
|
|
11
12
|
}
|
|
12
13
|
|
|
13
|
-
|
|
14
|
-
|
|
14
|
+
switch (firstNode.expression.type) {
|
|
15
|
+
// module.exports = require('...');
|
|
16
|
+
case "AssignmentExpression":
|
|
17
|
+
return exportAssignmentHasRequireLeave(firstNode.expression.right);
|
|
18
|
+
// require('...');
|
|
19
|
+
case "CallExpression":
|
|
20
|
+
return exportAssignmentHasRequireLeave(firstNode.expression);
|
|
21
|
+
default:
|
|
22
|
+
return false;
|
|
15
23
|
}
|
|
16
|
-
|
|
17
|
-
return exportAssignmentHasRequireLeave(body[0].expression.right);
|
|
18
24
|
}
|
package/types/api.d.ts
CHANGED
|
@@ -53,6 +53,8 @@ interface RuntimeOptions {
|
|
|
53
53
|
* @default false
|
|
54
54
|
*/
|
|
55
55
|
isMinified?: boolean;
|
|
56
|
+
initialize?: (sourceFile: SourceFile) => void;
|
|
57
|
+
finalize?: (sourceFile: SourceFile) => void;
|
|
56
58
|
}
|
|
57
59
|
|
|
58
60
|
interface RuntimeFileOptions extends Omit<RuntimeOptions, "isMinified"> {
|