@squiz/render-runtime-lib 1.2.1-alpha.100
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 +11 -0
- package/lib/component-runner/component-runner.spec.d.ts +1 -0
- package/lib/component-runner/index.d.ts +22 -0
- package/lib/component-runner/worker/WorkerPool.d.ts +50 -0
- package/lib/component-runner/worker/getRuntimeModules.d.ts +1 -0
- package/lib/component-runner/worker/worker-root.d.ts +1 -0
- package/lib/index.d.ts +25 -0
- package/lib/index.js +112791 -0
- package/lib/index.js.map +7 -0
- package/lib/migrations/20220704054051_initial.sql +19 -0
- package/lib/migrations/20220718172237_adding_component_sets.sql +23 -0
- package/lib/migrations/20220728113941_add_env_vars_field.sql +1 -0
- package/lib/migrations/20220817113300_removing_null_props_from_jsonb.sql +41 -0
- package/lib/render-runtime-lib.spec.d.ts +1 -0
- package/lib/test/helpers/fixtures.d.ts +20 -0
- package/lib/test/helpers/stack.d.ts +6 -0
- package/lib/test/index.d.ts +2 -0
- package/lib/utils/convertFunctionStaticFilesToFqdn.d.ts +1 -0
- package/lib/utils/convertFunctionStaticFilesToFqdn.spec.d.ts +1 -0
- package/lib/utils/getFunctionDefinitionFromManifest.d.ts +2 -0
- package/lib/utils/getManifestPath.d.ts +1 -0
- package/lib/utils/getManifestPath.spec.d.ts +1 -0
- package/lib/utils/getPreviewFilePath.d.ts +1 -0
- package/lib/utils/getPreviewFilePath.spec.d.ts +1 -0
- package/lib/utils/isInProductionMode.d.ts +1 -0
- package/lib/utils/isInProductionMode.spec.d.ts +1 -0
- package/lib/utils/resolvePreviewOutput.d.ts +2 -0
- package/lib/utils/resolvePreviewOutput.spec.d.ts +1 -0
- package/lib/webserver/app.d.ts +4 -0
- package/lib/webserver/controllers/core.d.ts +3 -0
- package/lib/webserver/controllers/core.spec.d.ts +1 -0
- package/lib/webserver/controllers/definition.d.ts +3 -0
- package/lib/webserver/controllers/definition.spec.d.ts +1 -0
- package/lib/webserver/controllers/index.d.ts +4 -0
- package/lib/webserver/controllers/render.d.ts +3 -0
- package/lib/webserver/controllers/render.spec.d.ts +1 -0
- package/lib/webserver/controllers/static.d.ts +3 -0
- package/lib/webserver/controllers/static.spec.d.ts +1 -0
- package/lib/webserver/controllers/test/definition-route-tests.d.ts +1 -0
- package/lib/webserver/controllers/test/render-route-tests.d.ts +1 -0
- package/lib/webserver/controllers/test/static-route-tests.d.ts +1 -0
- package/lib/webserver/index.d.ts +22 -0
- package/lib/worker/bridge.js +1010 -0
- package/lib/worker/compiler.js +87 -0
- package/lib/worker/events.js +977 -0
- package/lib/worker/nodevm.js +503 -0
- package/lib/worker/resolver-compat.js +342 -0
- package/lib/worker/resolver.js +882 -0
- package/lib/worker/script.js +388 -0
- package/lib/worker/setup-node-sandbox.js +469 -0
- package/lib/worker/setup-sandbox.js +456 -0
- package/lib/worker/transformer.js +180 -0
- package/lib/worker/vm.js +539 -0
- package/lib/worker/worker-root.js +41743 -0
- package/lib/worker/worker-root.js.map +7 -0
- package/package.json +60 -0
|
@@ -0,0 +1,882 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// The Resolver is currently experimental and might be exposed to users in the future.
|
|
4
|
+
|
|
5
|
+
const pa = require('path');
|
|
6
|
+
const fs = require('fs');
|
|
7
|
+
|
|
8
|
+
const {
|
|
9
|
+
VMError
|
|
10
|
+
} = require('./bridge');
|
|
11
|
+
const { VMScript } = require('./script');
|
|
12
|
+
|
|
13
|
+
// This should match. Note that '\', '%' are invalid characters
|
|
14
|
+
// 1. name/.*
|
|
15
|
+
// 2. @scope/name/.*
|
|
16
|
+
const EXPORTS_PATTERN = /^((?:@[^/\\%]+\/)?[^/\\%]+)(\/.*)?$/;
|
|
17
|
+
|
|
18
|
+
// See https://tc39.es/ecma262/#integer-index
|
|
19
|
+
function isArrayIndex(key) {
|
|
20
|
+
const keyNum = +key;
|
|
21
|
+
if (`${keyNum}` !== key) return false;
|
|
22
|
+
return keyNum >= 0 && keyNum < 0xFFFFFFFF;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
class Resolver {
|
|
26
|
+
|
|
27
|
+
constructor(builtinModules, globalPaths, hostRequire) {
|
|
28
|
+
this.builtinModules = builtinModules;
|
|
29
|
+
this.globalPaths = globalPaths;
|
|
30
|
+
this.hostRequire = hostRequire;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
init(vm) {
|
|
34
|
+
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
pathResolve(path) {
|
|
38
|
+
return pa.resolve(path);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
pathIsRelative(path) {
|
|
42
|
+
if (path === '' || path[0] !== '.') return false;
|
|
43
|
+
if (path.length === 1) return true;
|
|
44
|
+
const idx = path[1] === '.' ? 2 : 1;
|
|
45
|
+
if (path.length <= idx) return false;
|
|
46
|
+
return path[idx] === '/' || path[idx] === pa.sep;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
pathIsAbsolute(path) {
|
|
50
|
+
return pa.isAbsolute(path);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
pathConcat(...paths) {
|
|
54
|
+
return pa.join(...paths);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
pathBasename(path) {
|
|
58
|
+
return pa.basename(path);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
pathDirname(path) {
|
|
62
|
+
return pa.dirname(path);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
lookupPaths(mod, id) {
|
|
66
|
+
if (typeof id === 'string') throw new Error('Id is not a string');
|
|
67
|
+
if (this.pathIsRelative(id)) return [mod.path || '.'];
|
|
68
|
+
return [...mod.paths, ...this.globalPaths];
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
getBuiltinModulesList() {
|
|
72
|
+
return Object.getOwnPropertyNames(this.builtinModules);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
loadBuiltinModule(vm, id) {
|
|
76
|
+
const handler = this.builtinModules[id];
|
|
77
|
+
return handler && handler(this, vm, id);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
loadJS(vm, mod, filename) {
|
|
81
|
+
throw new VMError(`Access denied to require '${filename}'`, 'EDENIED');
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
loadJSON(vm, mod, filename) {
|
|
85
|
+
throw new VMError(`Access denied to require '${filename}'`, 'EDENIED');
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
loadNode(vm, mod, filename) {
|
|
89
|
+
throw new VMError(`Access denied to require '${filename}'`, 'EDENIED');
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
registerModule(mod, filename, path, parent, direct) {
|
|
93
|
+
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
resolve(mod, x, options, ext, direct) {
|
|
97
|
+
if (typeof x !== 'string') throw new Error('Id is not a string');
|
|
98
|
+
|
|
99
|
+
if (x.startsWith('node:') || this.builtinModules[x]) {
|
|
100
|
+
// a. return the core module
|
|
101
|
+
// b. STOP
|
|
102
|
+
return x;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return this.resolveFull(mod, x, options, ext, direct);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
resolveFull(mod, x, options, ext, direct) {
|
|
109
|
+
// 7. THROW "not found"
|
|
110
|
+
throw new VMError(`Cannot find module '${x}'`, 'ENOTFOUND');
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// NODE_MODULES_PATHS(START)
|
|
114
|
+
genLookupPaths(path) {
|
|
115
|
+
// 1. let PARTS = path split(START)
|
|
116
|
+
// 2. let I = count of PARTS - 1
|
|
117
|
+
// 3. let DIRS = []
|
|
118
|
+
const dirs = [];
|
|
119
|
+
// 4. while I >= 0,
|
|
120
|
+
while (true) {
|
|
121
|
+
const name = this.pathBasename(path);
|
|
122
|
+
// a. if PARTS[I] = "node_modules" CONTINUE
|
|
123
|
+
if (name !== 'node_modules') {
|
|
124
|
+
// b. DIR = path join(PARTS[0 .. I] + "node_modules")
|
|
125
|
+
// c. DIRS = DIR + DIRS // Note: this seems wrong. Should be DIRS + DIR
|
|
126
|
+
dirs.push(this.pathConcat(path, 'node_modules'));
|
|
127
|
+
}
|
|
128
|
+
const dir = this.pathDirname(path);
|
|
129
|
+
if (dir == path) break;
|
|
130
|
+
// d. let I = I - 1
|
|
131
|
+
path = dir;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return dirs;
|
|
135
|
+
// This is done later on
|
|
136
|
+
// 5. return DIRS + GLOBAL_FOLDERS
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
class DefaultResolver extends Resolver {
|
|
142
|
+
|
|
143
|
+
constructor(builtinModules, checkPath, globalPaths, pathContext, customResolver, hostRequire, compiler) {
|
|
144
|
+
super(builtinModules, globalPaths, hostRequire);
|
|
145
|
+
this.checkPath = checkPath;
|
|
146
|
+
this.pathContext = pathContext;
|
|
147
|
+
this.customResolver = customResolver;
|
|
148
|
+
this.compiler = compiler;
|
|
149
|
+
this.packageCache = {__proto__: null};
|
|
150
|
+
this.scriptCache = {__proto__: null};
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
isPathAllowed(path) {
|
|
154
|
+
return this.checkPath(path);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
pathTestIsDirectory(path) {
|
|
158
|
+
try {
|
|
159
|
+
const stat = fs.statSync(path, {__proto__: null, throwIfNoEntry: false});
|
|
160
|
+
return stat && stat.isDirectory();
|
|
161
|
+
} catch (e) {
|
|
162
|
+
return false;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
pathTestIsFile(path) {
|
|
167
|
+
try {
|
|
168
|
+
const stat = fs.statSync(path, {__proto__: null, throwIfNoEntry: false});
|
|
169
|
+
return stat && stat.isFile();
|
|
170
|
+
} catch (e) {
|
|
171
|
+
return false;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
readFile(path) {
|
|
176
|
+
return fs.readFileSync(path, {encoding: 'utf8'});
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
readFileWhenExists(path) {
|
|
180
|
+
return this.pathTestIsFile(path) ? this.readFile(path) : undefined;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
readScript(filename) {
|
|
184
|
+
let script = this.scriptCache[filename];
|
|
185
|
+
if (!script) {
|
|
186
|
+
script = new VMScript(this.readFile(filename), {filename, compiler: this.compiler});
|
|
187
|
+
this.scriptCache[filename] = script;
|
|
188
|
+
}
|
|
189
|
+
return script;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
checkAccess(mod, filename) {
|
|
193
|
+
if (!this.isPathAllowed(filename)) {
|
|
194
|
+
throw new VMError(`Module '${filename}' is not allowed to be required. The path is outside the border!`, 'EDENIED');
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
loadJS(vm, mod, filename) {
|
|
199
|
+
filename = this.pathResolve(filename);
|
|
200
|
+
this.checkAccess(mod, filename);
|
|
201
|
+
if (this.pathContext(filename, 'js') === 'sandbox') {
|
|
202
|
+
const script = this.readScript(filename);
|
|
203
|
+
vm.run(script, {filename, strict: true, module: mod, wrapper: 'none', dirname: mod.path});
|
|
204
|
+
} else {
|
|
205
|
+
const m = this.hostRequire(filename);
|
|
206
|
+
mod.exports = vm.readonly(m);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
loadJSON(vm, mod, filename) {
|
|
211
|
+
filename = this.pathResolve(filename);
|
|
212
|
+
this.checkAccess(mod, filename);
|
|
213
|
+
const json = this.readFile(filename);
|
|
214
|
+
mod.exports = vm._jsonParse(json);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
loadNode(vm, mod, filename) {
|
|
218
|
+
filename = this.pathResolve(filename);
|
|
219
|
+
this.checkAccess(mod, filename);
|
|
220
|
+
if (this.pathContext(filename, 'node') === 'sandbox') throw new VMError('Native modules can be required only with context set to \'host\'.');
|
|
221
|
+
const m = this.hostRequire(filename);
|
|
222
|
+
mod.exports = vm.readonly(m);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// require(X) from module at path Y
|
|
226
|
+
resolveFull(mod, x, options, ext, direct) {
|
|
227
|
+
// Note: core module handled by caller
|
|
228
|
+
|
|
229
|
+
const extList = Object.getOwnPropertyNames(ext);
|
|
230
|
+
const path = mod.path || '.';
|
|
231
|
+
|
|
232
|
+
// 5. LOAD_PACKAGE_SELF(X, dirname(Y))
|
|
233
|
+
let f = this.loadPackageSelf(x, path, extList);
|
|
234
|
+
if (f) return f;
|
|
235
|
+
|
|
236
|
+
// 4. If X begins with '#'
|
|
237
|
+
if (x[0] === '#') {
|
|
238
|
+
// a. LOAD_PACKAGE_IMPORTS(X, dirname(Y))
|
|
239
|
+
f = this.loadPackageImports(x, path, extList);
|
|
240
|
+
if (f) return f;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// 2. If X begins with '/'
|
|
244
|
+
if (this.pathIsAbsolute(x)) {
|
|
245
|
+
// a. set Y to be the filesystem root
|
|
246
|
+
f = this.loadAsFileOrDirecotry(x, extList);
|
|
247
|
+
if (f) return f;
|
|
248
|
+
|
|
249
|
+
// c. THROW "not found"
|
|
250
|
+
throw new VMError(`Cannot find module '${x}'`, 'ENOTFOUND');
|
|
251
|
+
|
|
252
|
+
// 3. If X begins with './' or '/' or '../'
|
|
253
|
+
} else if (this.pathIsRelative(x)) {
|
|
254
|
+
if (typeof options === 'object' && options !== null) {
|
|
255
|
+
const paths = options.paths;
|
|
256
|
+
if (Array.isArray(paths)) {
|
|
257
|
+
for (let i = 0; i < paths.length; i++) {
|
|
258
|
+
// a. LOAD_AS_FILE(Y + X)
|
|
259
|
+
// b. LOAD_AS_DIRECTORY(Y + X)
|
|
260
|
+
f = this.loadAsFileOrDirecotry(this.pathConcat(paths[i], x), extList);
|
|
261
|
+
if (f) return f;
|
|
262
|
+
}
|
|
263
|
+
} else if (paths === undefined) {
|
|
264
|
+
// a. LOAD_AS_FILE(Y + X)
|
|
265
|
+
// b. LOAD_AS_DIRECTORY(Y + X)
|
|
266
|
+
f = this.loadAsFileOrDirecotry(this.pathConcat(path, x), extList);
|
|
267
|
+
if (f) return f;
|
|
268
|
+
} else {
|
|
269
|
+
throw new VMError('Invalid options.paths option.');
|
|
270
|
+
}
|
|
271
|
+
} else {
|
|
272
|
+
// a. LOAD_AS_FILE(Y + X)
|
|
273
|
+
// b. LOAD_AS_DIRECTORY(Y + X)
|
|
274
|
+
f = this.loadAsFileOrDirecotry(this.pathConcat(path, x), extList);
|
|
275
|
+
if (f) return f;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// c. THROW "not found"
|
|
279
|
+
throw new VMError(`Cannot find module '${x}'`, 'ENOTFOUND');
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
let dirs;
|
|
283
|
+
if (typeof options === 'object' && options !== null) {
|
|
284
|
+
const paths = options.paths;
|
|
285
|
+
if (Array.isArray(paths)) {
|
|
286
|
+
dirs = [];
|
|
287
|
+
|
|
288
|
+
for (let i = 0; i < paths.length; i++) {
|
|
289
|
+
const lookups = this.genLookupPaths(paths[i]);
|
|
290
|
+
for (let j = 0; j < lookups.length; j++) {
|
|
291
|
+
if (!dirs.includes(lookups[j])) dirs.push(lookups[j]);
|
|
292
|
+
}
|
|
293
|
+
if (i === 0) {
|
|
294
|
+
const globalPaths = this.globalPaths;
|
|
295
|
+
for (let j = 0; j < globalPaths.length; j++) {
|
|
296
|
+
if (!dirs.includes(globalPaths[j])) dirs.push(globalPaths[j]);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
} else if (paths === undefined) {
|
|
301
|
+
dirs = [...mod.paths, ...this.globalPaths];
|
|
302
|
+
} else {
|
|
303
|
+
throw new VMError('Invalid options.paths option.');
|
|
304
|
+
}
|
|
305
|
+
} else {
|
|
306
|
+
dirs = [...mod.paths, ...this.globalPaths];
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// 6. LOAD_NODE_MODULES(X, dirname(Y))
|
|
310
|
+
f = this.loadNodeModules(x, dirs, extList);
|
|
311
|
+
if (f) return f;
|
|
312
|
+
|
|
313
|
+
f = this.customResolver(this, x, path, extList);
|
|
314
|
+
if (f) return f;
|
|
315
|
+
|
|
316
|
+
return super.resolveFull(mod, x, options, ext, direct);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
loadAsFileOrDirecotry(x, extList) {
|
|
320
|
+
// a. LOAD_AS_FILE(X)
|
|
321
|
+
const f = this.loadAsFile(x, extList);
|
|
322
|
+
if (f) return f;
|
|
323
|
+
// b. LOAD_AS_DIRECTORY(X)
|
|
324
|
+
return this.loadAsDirectory(x, extList);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
tryFile(x) {
|
|
328
|
+
x = this.pathResolve(x);
|
|
329
|
+
return this.isPathAllowed(x) && this.pathTestIsFile(x) ? x : undefined;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
tryWithExtension(x, extList) {
|
|
333
|
+
for (let i = 0; i < extList.length; i++) {
|
|
334
|
+
const ext = extList[i];
|
|
335
|
+
if (ext !== this.pathBasename(ext)) continue;
|
|
336
|
+
const f = this.tryFile(x + ext);
|
|
337
|
+
if (f) return f;
|
|
338
|
+
}
|
|
339
|
+
return undefined;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
readPackage(path) {
|
|
343
|
+
const packagePath = this.pathResolve(this.pathConcat(path, 'package.json'));
|
|
344
|
+
|
|
345
|
+
const cache = this.packageCache[packagePath];
|
|
346
|
+
if (cache !== undefined) return cache;
|
|
347
|
+
|
|
348
|
+
if (!this.isPathAllowed(packagePath)) return undefined;
|
|
349
|
+
const content = this.readFileWhenExists(packagePath);
|
|
350
|
+
if (!content) {
|
|
351
|
+
this.packageCache[packagePath] = false;
|
|
352
|
+
return false;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
let parsed;
|
|
356
|
+
try {
|
|
357
|
+
parsed = JSON.parse(content);
|
|
358
|
+
} catch (e) {
|
|
359
|
+
e.path = packagePath;
|
|
360
|
+
e.message = 'Error parsing ' + packagePath + ': ' + e.message;
|
|
361
|
+
throw e;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
const filtered = {
|
|
365
|
+
name: parsed.name,
|
|
366
|
+
main: parsed.main,
|
|
367
|
+
exports: parsed.exports,
|
|
368
|
+
imports: parsed.imports,
|
|
369
|
+
type: parsed.type
|
|
370
|
+
};
|
|
371
|
+
this.packageCache[packagePath] = filtered;
|
|
372
|
+
return filtered;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
readPackageScope(path) {
|
|
376
|
+
while (true) {
|
|
377
|
+
const dir = this.pathDirname(path);
|
|
378
|
+
if (dir === path) break;
|
|
379
|
+
const basename = this.pathBasename(dir);
|
|
380
|
+
if (basename === 'node_modules') break;
|
|
381
|
+
const pack = this.readPackage(dir);
|
|
382
|
+
if (pack) return {data: pack, scope: dir};
|
|
383
|
+
path = dir;
|
|
384
|
+
}
|
|
385
|
+
return {data: undefined, scope: undefined};
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
// LOAD_AS_FILE(X)
|
|
389
|
+
loadAsFile(x, extList) {
|
|
390
|
+
// 1. If X is a file, load X as its file extension format. STOP
|
|
391
|
+
const f = this.tryFile(x);
|
|
392
|
+
if (f) return f;
|
|
393
|
+
// 2. If X.js is a file, load X.js as JavaScript text. STOP
|
|
394
|
+
// 3. If X.json is a file, parse X.json to a JavaScript Object. STOP
|
|
395
|
+
// 4. If X.node is a file, load X.node as binary addon. STOP
|
|
396
|
+
return this.tryWithExtension(x, extList);
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
// LOAD_INDEX(X)
|
|
400
|
+
loadIndex(x, extList) {
|
|
401
|
+
// 1. If X/index.js is a file, load X/index.js as JavaScript text. STOP
|
|
402
|
+
// 2. If X/index.json is a file, parse X/index.json to a JavaScript object. STOP
|
|
403
|
+
// 3. If X/index.node is a file, load X/index.node as binary addon. STOP
|
|
404
|
+
return this.tryWithExtension(this.pathConcat(x, 'index'), extList);
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
// LOAD_AS_DIRECTORY(X)
|
|
408
|
+
loadAsPackage(x, pack, extList) {
|
|
409
|
+
// 1. If X/package.json is a file,
|
|
410
|
+
// already done.
|
|
411
|
+
if (pack) {
|
|
412
|
+
// a. Parse X/package.json, and look for "main" field.
|
|
413
|
+
// b. If "main" is a falsy value, GOTO 2.
|
|
414
|
+
if (typeof pack.main === 'string') {
|
|
415
|
+
// c. let M = X + (json main field)
|
|
416
|
+
const m = this.pathConcat(x, pack.main);
|
|
417
|
+
// d. LOAD_AS_FILE(M)
|
|
418
|
+
let f = this.loadAsFile(m, extList);
|
|
419
|
+
if (f) return f;
|
|
420
|
+
// e. LOAD_INDEX(M)
|
|
421
|
+
f = this.loadIndex(m, extList);
|
|
422
|
+
if (f) return f;
|
|
423
|
+
// f. LOAD_INDEX(X) DEPRECATED
|
|
424
|
+
f = this.loadIndex(x, extList);
|
|
425
|
+
if (f) return f;
|
|
426
|
+
// g. THROW "not found"
|
|
427
|
+
throw new VMError(`Cannot find module '${x}'`, 'ENOTFOUND');
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
// 2. LOAD_INDEX(X)
|
|
432
|
+
return this.loadIndex(x, extList);
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
// LOAD_AS_DIRECTORY(X)
|
|
436
|
+
loadAsDirectory(x, extList) {
|
|
437
|
+
// 1. If X/package.json is a file,
|
|
438
|
+
const pack = this.readPackage(x);
|
|
439
|
+
return this.loadAsPackage(x, pack, extList);
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
// LOAD_NODE_MODULES(X, START)
|
|
443
|
+
loadNodeModules(x, dirs, extList) {
|
|
444
|
+
// 1. let DIRS = NODE_MODULES_PATHS(START)
|
|
445
|
+
// This step is already done.
|
|
446
|
+
|
|
447
|
+
// 2. for each DIR in DIRS:
|
|
448
|
+
for (let i = 0; i < dirs.length; i++) {
|
|
449
|
+
const dir = dirs[i];
|
|
450
|
+
// a. LOAD_PACKAGE_EXPORTS(X, DIR)
|
|
451
|
+
let f = this.loadPackageExports(x, dir, extList);
|
|
452
|
+
if (f) return f;
|
|
453
|
+
// b. LOAD_AS_FILE(DIR/X)
|
|
454
|
+
f = this.loadAsFile(dir + '/' + x, extList);
|
|
455
|
+
if (f) return f;
|
|
456
|
+
// c. LOAD_AS_DIRECTORY(DIR/X)
|
|
457
|
+
f = this.loadAsDirectory(dir + '/' + x, extList);
|
|
458
|
+
if (f) return f;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
return undefined;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
// LOAD_PACKAGE_IMPORTS(X, DIR)
|
|
465
|
+
loadPackageImports(x, dir, extList) {
|
|
466
|
+
// 1. Find the closest package scope SCOPE to DIR.
|
|
467
|
+
const {data, scope} = this.readPackageScope(dir);
|
|
468
|
+
// 2. If no scope was found, return.
|
|
469
|
+
if (!data) return undefined;
|
|
470
|
+
// 3. If the SCOPE/package.json "imports" is null or undefined, return.
|
|
471
|
+
if (typeof data.imports !== 'object' || data.imports === null || Array.isArray(data.imports)) return undefined;
|
|
472
|
+
// 4. let MATCH = PACKAGE_IMPORTS_RESOLVE(X, pathToFileURL(SCOPE),
|
|
473
|
+
// ["node", "require"]) defined in the ESM resolver.
|
|
474
|
+
|
|
475
|
+
// PACKAGE_IMPORTS_RESOLVE(specifier, parentURL, conditions)
|
|
476
|
+
// 1. Assert: specifier begins with "#".
|
|
477
|
+
// 2. If specifier is exactly equal to "#" or starts with "#/", then
|
|
478
|
+
if (x === '#' || x.startsWith('#/')) {
|
|
479
|
+
// a. Throw an Invalid Module Specifier error.
|
|
480
|
+
throw new VMError(`Invalid module specifier '${x}'`, 'ERR_INVALID_MODULE_SPECIFIER');
|
|
481
|
+
}
|
|
482
|
+
// 3. Let packageURL be the result of LOOKUP_PACKAGE_SCOPE(parentURL).
|
|
483
|
+
// Note: packageURL === parentURL === scope
|
|
484
|
+
// 4. If packageURL is not null, then
|
|
485
|
+
// Always true
|
|
486
|
+
// a. Let pjson be the result of READ_PACKAGE_JSON(packageURL).
|
|
487
|
+
// pjson === data
|
|
488
|
+
// b. If pjson.imports is a non-null Object, then
|
|
489
|
+
// Already tested
|
|
490
|
+
// x. Let resolved be the result of PACKAGE_IMPORTS_EXPORTS_RESOLVE( specifier, pjson.imports, packageURL, true, conditions).
|
|
491
|
+
const match = this.packageImportsExportsResolve(x, data.imports, scope, true, ['node', 'require'], extList);
|
|
492
|
+
// y. If resolved is not null or undefined, return resolved.
|
|
493
|
+
if (!match) {
|
|
494
|
+
// 5. Throw a Package Import Not Defined error.
|
|
495
|
+
throw new VMError(`Package import not defined for '${x}'`, 'ERR_PACKAGE_IMPORT_NOT_DEFINED');
|
|
496
|
+
}
|
|
497
|
+
// END PACKAGE_IMPORTS_RESOLVE
|
|
498
|
+
|
|
499
|
+
// 5. RESOLVE_ESM_MATCH(MATCH).
|
|
500
|
+
return this.resolveEsmMatch(match, x, extList);
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
// LOAD_PACKAGE_EXPORTS(X, DIR)
|
|
504
|
+
loadPackageExports(x, dir, extList) {
|
|
505
|
+
// 1. Try to interpret X as a combination of NAME and SUBPATH where the name
|
|
506
|
+
// may have a @scope/ prefix and the subpath begins with a slash (`/`).
|
|
507
|
+
const res = x.match(EXPORTS_PATTERN);
|
|
508
|
+
// 2. If X does not match this pattern or DIR/NAME/package.json is not a file,
|
|
509
|
+
// return.
|
|
510
|
+
if (!res) return undefined;
|
|
511
|
+
const scope = this.pathConcat(dir, res[1]);
|
|
512
|
+
const pack = this.readPackage(scope);
|
|
513
|
+
if (!pack) return undefined;
|
|
514
|
+
// 3. Parse DIR/NAME/package.json, and look for "exports" field.
|
|
515
|
+
// 4. If "exports" is null or undefined, return.
|
|
516
|
+
if (!pack.exports) return undefined;
|
|
517
|
+
// 5. let MATCH = PACKAGE_EXPORTS_RESOLVE(pathToFileURL(DIR/NAME), "." + SUBPATH,
|
|
518
|
+
// `package.json` "exports", ["node", "require"]) defined in the ESM resolver.
|
|
519
|
+
const match = this.packageExportsResolve(scope, '.' + (res[2] || ''), pack.exports, ['node', 'require'], extList);
|
|
520
|
+
// 6. RESOLVE_ESM_MATCH(MATCH)
|
|
521
|
+
return this.resolveEsmMatch(match, x, extList);
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
// LOAD_PACKAGE_SELF(X, DIR)
|
|
525
|
+
loadPackageSelf(x, dir, extList) {
|
|
526
|
+
// 1. Find the closest package scope SCOPE to DIR.
|
|
527
|
+
const {data, scope} = this.readPackageScope(dir);
|
|
528
|
+
// 2. If no scope was found, return.
|
|
529
|
+
if (!data) return undefined;
|
|
530
|
+
// 3. If the SCOPE/package.json "exports" is null or undefined, return.
|
|
531
|
+
if (!data.exports) return undefined;
|
|
532
|
+
// 4. If the SCOPE/package.json "name" is not the first segment of X, return.
|
|
533
|
+
if (x !== data.name && !x.startsWith(data.name + '/')) return undefined;
|
|
534
|
+
// 5. let MATCH = PACKAGE_EXPORTS_RESOLVE(pathToFileURL(SCOPE),
|
|
535
|
+
// "." + X.slice("name".length), `package.json` "exports", ["node", "require"])
|
|
536
|
+
// defined in the ESM resolver.
|
|
537
|
+
const match = this.packageExportsResolve(scope, '.' + x.slice(data.name.length), data.exports, ['node', 'require'], extList);
|
|
538
|
+
// 6. RESOLVE_ESM_MATCH(MATCH)
|
|
539
|
+
return this.resolveEsmMatch(match, x, extList);
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
// RESOLVE_ESM_MATCH(MATCH)
|
|
543
|
+
resolveEsmMatch(match, x, extList) {
|
|
544
|
+
// 1. let { RESOLVED, EXACT } = MATCH
|
|
545
|
+
const resolved = match;
|
|
546
|
+
const exact = true;
|
|
547
|
+
// 2. let RESOLVED_PATH = fileURLToPath(RESOLVED)
|
|
548
|
+
const resolvedPath = resolved;
|
|
549
|
+
let f;
|
|
550
|
+
// 3. If EXACT is true,
|
|
551
|
+
if (exact) {
|
|
552
|
+
// a. If the file at RESOLVED_PATH exists, load RESOLVED_PATH as its extension
|
|
553
|
+
// format. STOP
|
|
554
|
+
f = this.tryFile(resolvedPath);
|
|
555
|
+
// 4. Otherwise, if EXACT is false,
|
|
556
|
+
} else {
|
|
557
|
+
// a. LOAD_AS_FILE(RESOLVED_PATH)
|
|
558
|
+
// b. LOAD_AS_DIRECTORY(RESOLVED_PATH)
|
|
559
|
+
f = this.loadAsFileOrDirecotry(resolvedPath, extList);
|
|
560
|
+
}
|
|
561
|
+
if (f) return f;
|
|
562
|
+
// 5. THROW "not found"
|
|
563
|
+
throw new VMError(`Cannot find module '${x}'`, 'ENOTFOUND');
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
// PACKAGE_EXPORTS_RESOLVE(packageURL, subpath, exports, conditions)
|
|
567
|
+
packageExportsResolve(packageURL, subpath, rexports, conditions, extList) {
|
|
568
|
+
// 1. If exports is an Object with both a key starting with "." and a key not starting with ".", throw an Invalid Package Configuration error.
|
|
569
|
+
let hasDots = false;
|
|
570
|
+
if (typeof rexports === 'object' && !Array.isArray(rexports)) {
|
|
571
|
+
const keys = Object.getOwnPropertyNames(rexports);
|
|
572
|
+
if (keys.length > 0) {
|
|
573
|
+
hasDots = keys[0][0] === '.';
|
|
574
|
+
for (let i = 0; i < keys.length; i++) {
|
|
575
|
+
if (hasDots !== (keys[i][0] === '.')) {
|
|
576
|
+
throw new VMError('Invalid package configuration', 'ERR_INVALID_PACKAGE_CONFIGURATION');
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
// 2. If subpath is equal to ".", then
|
|
582
|
+
if (subpath === '.') {
|
|
583
|
+
// a. Let mainExport be undefined.
|
|
584
|
+
let mainExport = undefined;
|
|
585
|
+
// b. If exports is a String or Array, or an Object containing no keys starting with ".", then
|
|
586
|
+
if (typeof rexports === 'string' || Array.isArray(rexports) || !hasDots) {
|
|
587
|
+
// x. Set mainExport to exports.
|
|
588
|
+
mainExport = rexports;
|
|
589
|
+
// c. Otherwise if exports is an Object containing a "." property, then
|
|
590
|
+
} else if (hasDots) {
|
|
591
|
+
// x. Set mainExport to exports["."].
|
|
592
|
+
mainExport = rexports['.'];
|
|
593
|
+
}
|
|
594
|
+
// d. If mainExport is not undefined, then
|
|
595
|
+
if (mainExport) {
|
|
596
|
+
// x. Let resolved be the result of PACKAGE_TARGET_RESOLVE( packageURL, mainExport, "", false, false, conditions).
|
|
597
|
+
const resolved = this.packageTargetResolve(packageURL, mainExport, '', false, false, conditions, extList);
|
|
598
|
+
// y. If resolved is not null or undefined, return resolved.
|
|
599
|
+
if (resolved) return resolved;
|
|
600
|
+
}
|
|
601
|
+
// 3. Otherwise, if exports is an Object and all keys of exports start with ".", then
|
|
602
|
+
} else if (hasDots) {
|
|
603
|
+
// a. Let matchKey be the string "./" concatenated with subpath.
|
|
604
|
+
// Note: Here subpath starts already with './'
|
|
605
|
+
// b. Let resolved be the result of PACKAGE_IMPORTS_EXPORTS_RESOLVE( matchKey, exports, packageURL, false, conditions).
|
|
606
|
+
const resolved = this.packageImportsExportsResolve(subpath, rexports, packageURL, false, conditions, extList);
|
|
607
|
+
// c. If resolved is not null or undefined, return resolved.
|
|
608
|
+
if (resolved) return resolved;
|
|
609
|
+
}
|
|
610
|
+
// 4. Throw a Package Path Not Exported error.
|
|
611
|
+
throw new VMError(`Package path '${subpath}' is not exported`, 'ERR_PACKAGE_PATH_NOT_EXPORTED');
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
// PACKAGE_IMPORTS_EXPORTS_RESOLVE(matchKey, matchObj, packageURL, isImports, conditions)
|
|
615
|
+
packageImportsExportsResolve(matchKey, matchObj, packageURL, isImports, conditions, extList) {
|
|
616
|
+
// 1. If matchKey is a key of matchObj and does not contain "*", then
|
|
617
|
+
let target = matchObj[matchKey];
|
|
618
|
+
if (target && matchKey.indexOf('*') === -1) {
|
|
619
|
+
// a. Let target be the value of matchObj[matchKey].
|
|
620
|
+
// b. Return the result of PACKAGE_TARGET_RESOLVE(packageURL, target, "", false, isImports, conditions).
|
|
621
|
+
return this.packageTargetResolve(packageURL, target, '', false, isImports, conditions, extList);
|
|
622
|
+
}
|
|
623
|
+
// 2. Let expansionKeys be the list of keys of matchObj containing only a single "*",
|
|
624
|
+
// sorted by the sorting function PATTERN_KEY_COMPARE which orders in descending order of specificity.
|
|
625
|
+
const expansionKeys = Object.getOwnPropertyNames(matchObj);
|
|
626
|
+
let bestKey = '';
|
|
627
|
+
let bestSubpath;
|
|
628
|
+
// 3. For each key expansionKey in expansionKeys, do
|
|
629
|
+
for (let i = 0; i < expansionKeys.length; i++) {
|
|
630
|
+
const expansionKey = expansionKeys[i];
|
|
631
|
+
if (matchKey.length < expansionKey.length) continue;
|
|
632
|
+
// a. Let patternBase be the substring of expansionKey up to but excluding the first "*" character.
|
|
633
|
+
const star = expansionKey.indexOf('*');
|
|
634
|
+
if (star === -1) continue; // Note: expansionKeys was not filtered
|
|
635
|
+
const patternBase = expansionKey.slice(0, star);
|
|
636
|
+
// b. If matchKey starts with but is not equal to patternBase, then
|
|
637
|
+
if (matchKey.startsWith(patternBase) && expansionKey.indexOf('*', star + 1) === -1) { // Note: expansionKeys was not filtered
|
|
638
|
+
// 1. Let patternTrailer be the substring of expansionKey from the index after the first "*" character.
|
|
639
|
+
const patternTrailer = expansionKey.slice(star + 1);
|
|
640
|
+
// 2. If patternTrailer has zero length, or if matchKey ends with patternTrailer and the length of matchKey is greater than or
|
|
641
|
+
// equal to the length of expansionKey, then
|
|
642
|
+
if (matchKey.endsWith(patternTrailer) && this.patternKeyCompare(bestKey, expansionKey) === 1) { // Note: expansionKeys was not sorted
|
|
643
|
+
// a. Let target be the value of matchObj[expansionKey].
|
|
644
|
+
target = matchObj[expansionKey];
|
|
645
|
+
// b. Let subpath be the substring of matchKey starting at the index of the length of patternBase up to the length of
|
|
646
|
+
// matchKey minus the length of patternTrailer.
|
|
647
|
+
bestKey = expansionKey;
|
|
648
|
+
bestSubpath = matchKey.slice(patternBase.length, matchKey.length - patternTrailer.length);
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
if (bestSubpath) { // Note: expansionKeys was not sorted
|
|
653
|
+
// c. Return the result of PACKAGE_TARGET_RESOLVE(packageURL, target, subpath, true, isImports, conditions).
|
|
654
|
+
return this.packageTargetResolve(packageURL, target, bestSubpath, true, isImports, conditions, extList);
|
|
655
|
+
}
|
|
656
|
+
// 4. Return null.
|
|
657
|
+
return null;
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
// PATTERN_KEY_COMPARE(keyA, keyB)
|
|
661
|
+
patternKeyCompare(keyA, keyB) {
|
|
662
|
+
// 1. Assert: keyA ends with "/" or contains only a single "*".
|
|
663
|
+
// 2. Assert: keyB ends with "/" or contains only a single "*".
|
|
664
|
+
// 3. Let baseLengthA be the index of "*" in keyA plus one, if keyA contains "*", or the length of keyA otherwise.
|
|
665
|
+
const baseAStar = keyA.indexOf('*');
|
|
666
|
+
const baseLengthA = baseAStar === -1 ? keyA.length : baseAStar + 1;
|
|
667
|
+
// 4. Let baseLengthB be the index of "*" in keyB plus one, if keyB contains "*", or the length of keyB otherwise.
|
|
668
|
+
const baseBStar = keyB.indexOf('*');
|
|
669
|
+
const baseLengthB = baseBStar === -1 ? keyB.length : baseBStar + 1;
|
|
670
|
+
// 5. If baseLengthA is greater than baseLengthB, return -1.
|
|
671
|
+
if (baseLengthA > baseLengthB) return -1;
|
|
672
|
+
// 6. If baseLengthB is greater than baseLengthA, return 1.
|
|
673
|
+
if (baseLengthB > baseLengthA) return 1;
|
|
674
|
+
// 7. If keyA does not contain "*", return 1.
|
|
675
|
+
if (baseAStar === -1) return 1;
|
|
676
|
+
// 8. If keyB does not contain "*", return -1.
|
|
677
|
+
if (baseBStar === -1) return -1;
|
|
678
|
+
// 9. If the length of keyA is greater than the length of keyB, return -1.
|
|
679
|
+
if (keyA.length > keyB.length) return -1;
|
|
680
|
+
// 10. If the length of keyB is greater than the length of keyA, return 1.
|
|
681
|
+
if (keyB.length > keyA.length) return 1;
|
|
682
|
+
// 11. Return 0.
|
|
683
|
+
return 0;
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
// PACKAGE_TARGET_RESOLVE(packageURL, target, subpath, pattern, internal, conditions)
|
|
687
|
+
packageTargetResolve(packageURL, target, subpath, pattern, internal, conditions, extList) {
|
|
688
|
+
// 1. If target is a String, then
|
|
689
|
+
if (typeof target === 'string') {
|
|
690
|
+
// a. If pattern is false, subpath has non-zero length and target does not end with "/", throw an Invalid Module Specifier error.
|
|
691
|
+
if (!pattern && subpath.length > 0 && !target.endsWith('/')) {
|
|
692
|
+
throw new VMError(`Invalid package specifier '${subpath}'`, 'ERR_INVALID_MODULE_SPECIFIER');
|
|
693
|
+
}
|
|
694
|
+
// b. If target does not start with "./", then
|
|
695
|
+
if (!target.startsWith('./')) {
|
|
696
|
+
// 1. If internal is true and target does not start with "../" or "/" and is not a valid URL, then
|
|
697
|
+
if (internal && !target.startsWith('../') && !target.startsWith('/')) {
|
|
698
|
+
let isURL = false;
|
|
699
|
+
try {
|
|
700
|
+
// eslint-disable-next-line no-new
|
|
701
|
+
new URL(target);
|
|
702
|
+
isURL = true;
|
|
703
|
+
} catch (e) {}
|
|
704
|
+
if (!isURL) {
|
|
705
|
+
// a. If pattern is true, then
|
|
706
|
+
if (pattern) {
|
|
707
|
+
// 1. Return PACKAGE_RESOLVE(target with every instance of "*" replaced by subpath, packageURL + "/").
|
|
708
|
+
return this.packageResolve(target.replace(/\*/g, subpath), packageURL, conditions, extList);
|
|
709
|
+
}
|
|
710
|
+
// b. Return PACKAGE_RESOLVE(target + subpath, packageURL + "/").
|
|
711
|
+
return this.packageResolve(this.pathConcat(target, subpath), packageURL, conditions, extList);
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
// Otherwise, throw an Invalid Package Target error.
|
|
715
|
+
throw new VMError(`Invalid package target for '${subpath}'`, 'ERR_INVALID_PACKAGE_TARGET');
|
|
716
|
+
}
|
|
717
|
+
target = decodeURI(target);
|
|
718
|
+
// c. If target split on "/" or "\" contains any ".", ".." or "node_modules" segments after the first segment, case insensitive
|
|
719
|
+
// and including percent encoded variants, throw an Invalid Package Target error.
|
|
720
|
+
if (target.split(/[/\\]/).slice(1).findIndex(x => x === '.' || x === '..' || x.toLowerCase() === 'node_modules') !== -1) {
|
|
721
|
+
throw new VMError(`Invalid package target for '${subpath}'`, 'ERR_INVALID_PACKAGE_TARGET');
|
|
722
|
+
}
|
|
723
|
+
// d. Let resolvedTarget be the URL resolution of the concatenation of packageURL and target.
|
|
724
|
+
const resolvedTarget = this.pathConcat(packageURL, target);
|
|
725
|
+
// e. Assert: resolvedTarget is contained in packageURL.
|
|
726
|
+
subpath = decodeURI(subpath);
|
|
727
|
+
// f. If subpath split on "/" or "\" contains any ".", ".." or "node_modules" segments, case insensitive and including percent
|
|
728
|
+
// encoded variants, throw an Invalid Module Specifier error.
|
|
729
|
+
if (subpath.split(/[/\\]/).findIndex(x => x === '.' || x === '..' || x.toLowerCase() === 'node_modules') !== -1) {
|
|
730
|
+
throw new VMError(`Invalid package specifier '${subpath}'`, 'ERR_INVALID_MODULE_SPECIFIER');
|
|
731
|
+
}
|
|
732
|
+
// g. If pattern is true, then
|
|
733
|
+
if (pattern) {
|
|
734
|
+
// 1. Return the URL resolution of resolvedTarget with every instance of "*" replaced with subpath.
|
|
735
|
+
return resolvedTarget.replace(/\*/g, subpath);
|
|
736
|
+
}
|
|
737
|
+
// h. Otherwise,
|
|
738
|
+
// 1. Return the URL resolution of the concatenation of subpath and resolvedTarget.
|
|
739
|
+
return this.pathConcat(resolvedTarget, subpath);
|
|
740
|
+
// 3. Otherwise, if target is an Array, then
|
|
741
|
+
} else if (Array.isArray(target)) {
|
|
742
|
+
// a. If target.length is zero, return null.
|
|
743
|
+
if (target.length === 0) return null;
|
|
744
|
+
let lastException = undefined;
|
|
745
|
+
// b. For each item targetValue in target, do
|
|
746
|
+
for (let i = 0; i < target.length; i++) {
|
|
747
|
+
const targetValue = target[i];
|
|
748
|
+
// 1. Let resolved be the result of PACKAGE_TARGET_RESOLVE( packageURL, targetValue, subpath, pattern, internal, conditions),
|
|
749
|
+
// continuing the loop on any Invalid Package Target error.
|
|
750
|
+
let resolved;
|
|
751
|
+
try {
|
|
752
|
+
resolved = this.packageTargetResolve(packageURL, targetValue, subpath, pattern, internal, conditions, extList);
|
|
753
|
+
} catch (e) {
|
|
754
|
+
if (e.code !== 'ERR_INVALID_PACKAGE_TARGET') throw e;
|
|
755
|
+
lastException = e;
|
|
756
|
+
continue;
|
|
757
|
+
}
|
|
758
|
+
// 2. If resolved is undefined, continue the loop.
|
|
759
|
+
// 3. Return resolved.
|
|
760
|
+
if (resolved !== undefined) return resolved;
|
|
761
|
+
if (resolved === null) {
|
|
762
|
+
lastException = null;
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
// c. Return or throw the last fallback resolution null return or error.
|
|
766
|
+
if (lastException === undefined || lastException === null) return lastException;
|
|
767
|
+
throw lastException;
|
|
768
|
+
// 2. Otherwise, if target is a non-null Object, then
|
|
769
|
+
} else if (typeof target === 'object' && target !== null) {
|
|
770
|
+
const keys = Object.getOwnPropertyNames(target);
|
|
771
|
+
// a. If exports contains any index property keys, as defined in ECMA-262 6.1.7 Array Index, throw an Invalid Package Configuration error.
|
|
772
|
+
for (let i = 0; i < keys.length; i++) {
|
|
773
|
+
const p = keys[i];
|
|
774
|
+
if (isArrayIndex(p)) throw new VMError(`Invalid package configuration for '${subpath}'`, 'ERR_INVALID_PACKAGE_CONFIGURATION');
|
|
775
|
+
}
|
|
776
|
+
// b. For each property p of target, in object insertion order as,
|
|
777
|
+
for (let i = 0; i < keys.length; i++) {
|
|
778
|
+
const p = keys[i];
|
|
779
|
+
// 1. If p equals "default" or conditions contains an entry for p, then
|
|
780
|
+
if (p === 'default' || conditions.includes(p)) {
|
|
781
|
+
// a. Let targetValue be the value of the p property in target.
|
|
782
|
+
const targetValue = target[p];
|
|
783
|
+
// b. Let resolved be the result of PACKAGE_TARGET_RESOLVE( packageURL, targetValue, subpath, pattern, internal, conditions).
|
|
784
|
+
const resolved = this.packageTargetResolve(packageURL, targetValue, subpath, pattern, internal, conditions, extList);
|
|
785
|
+
// c. If resolved is equal to undefined, continue the loop.
|
|
786
|
+
// d. Return resolved.
|
|
787
|
+
if (resolved !== undefined) return resolved;
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
// c. Return undefined.
|
|
791
|
+
return undefined;
|
|
792
|
+
// 4. Otherwise, if target is null, return null.
|
|
793
|
+
} else if (target == null) {
|
|
794
|
+
return null;
|
|
795
|
+
}
|
|
796
|
+
// Otherwise throw an Invalid Package Target error.
|
|
797
|
+
throw new VMError(`Invalid package target for '${subpath}'`, 'ERR_INVALID_PACKAGE_TARGET');
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
// PACKAGE_RESOLVE(packageSpecifier, parentURL)
|
|
801
|
+
packageResolve(packageSpecifier, parentURL, conditions, extList) {
|
|
802
|
+
// 1. Let packageName be undefined.
|
|
803
|
+
let packageName = undefined;
|
|
804
|
+
// 2. If packageSpecifier is an empty string, then
|
|
805
|
+
if (packageSpecifier === '') {
|
|
806
|
+
// a. Throw an Invalid Module Specifier error.
|
|
807
|
+
throw new VMError(`Invalid package specifier '${packageSpecifier}'`, 'ERR_INVALID_MODULE_SPECIFIER');
|
|
808
|
+
}
|
|
809
|
+
// 3. If packageSpecifier is a Node.js builtin module name, then
|
|
810
|
+
if (this.builtinModules[packageSpecifier]) {
|
|
811
|
+
// a. Return the string "node:" concatenated with packageSpecifier.
|
|
812
|
+
return 'node:' + packageSpecifier;
|
|
813
|
+
}
|
|
814
|
+
let idx = packageSpecifier.indexOf('/');
|
|
815
|
+
// 5. Otherwise,
|
|
816
|
+
if (packageSpecifier[0] === '@') {
|
|
817
|
+
// a. If packageSpecifier does not contain a "/" separator, then
|
|
818
|
+
if (idx === -1) {
|
|
819
|
+
// x. Throw an Invalid Module Specifier error.
|
|
820
|
+
throw new VMError(`Invalid package specifier '${packageSpecifier}'`, 'ERR_INVALID_MODULE_SPECIFIER');
|
|
821
|
+
}
|
|
822
|
+
// b. Set packageName to the substring of packageSpecifier until the second "/" separator or the end of the string.
|
|
823
|
+
idx = packageSpecifier.indexOf('/', idx + 1);
|
|
824
|
+
}
|
|
825
|
+
// else
|
|
826
|
+
// 4. If packageSpecifier does not start with "@", then
|
|
827
|
+
// a. Set packageName to the substring of packageSpecifier until the first "/" separator or the end of the string.
|
|
828
|
+
packageName = idx === -1 ? packageSpecifier : packageSpecifier.slice(0, idx);
|
|
829
|
+
// 6. If packageName starts with "." or contains "\" or "%", then
|
|
830
|
+
if (idx !== 0 && (packageName[0] === '.' || packageName.indexOf('\\') >= 0 || packageName.indexOf('%') >= 0)) {
|
|
831
|
+
// a. Throw an Invalid Module Specifier error.
|
|
832
|
+
throw new VMError(`Invalid package specifier '${packageSpecifier}'`, 'ERR_INVALID_MODULE_SPECIFIER');
|
|
833
|
+
}
|
|
834
|
+
// 7. Let packageSubpath be "." concatenated with the substring of packageSpecifier from the position at the length of packageName.
|
|
835
|
+
const packageSubpath = '.' + packageSpecifier.slice(packageName.length);
|
|
836
|
+
// 8. If packageSubpath ends in "/", then
|
|
837
|
+
if (packageSubpath[packageSubpath.length - 1] === '/') {
|
|
838
|
+
// a. Throw an Invalid Module Specifier error.
|
|
839
|
+
throw new VMError(`Invalid package specifier '${packageSpecifier}'`, 'ERR_INVALID_MODULE_SPECIFIER');
|
|
840
|
+
}
|
|
841
|
+
// 9. Let selfUrl be the result of PACKAGE_SELF_RESOLVE(packageName, packageSubpath, parentURL).
|
|
842
|
+
const selfUrl = this.packageSelfResolve(packageName, packageSubpath, parentURL);
|
|
843
|
+
// 10. If selfUrl is not undefined, return selfUrl.
|
|
844
|
+
if (selfUrl) return selfUrl;
|
|
845
|
+
// 11. While parentURL is not the file system root,
|
|
846
|
+
let packageURL;
|
|
847
|
+
while (true) {
|
|
848
|
+
// a. Let packageURL be the URL resolution of "node_modules/" concatenated with packageSpecifier, relative to parentURL.
|
|
849
|
+
packageURL = this.pathResolve(this.pathConcat(parentURL, 'node_modules', packageSpecifier));
|
|
850
|
+
// b. Set parentURL to the parent folder URL of parentURL.
|
|
851
|
+
const parentParentURL = this.pathDirname(parentURL);
|
|
852
|
+
// c. If the folder at packageURL does not exist, then
|
|
853
|
+
if (this.isPathAllowed(packageURL) && this.pathTestIsDirectory(packageURL)) break;
|
|
854
|
+
// 1. Continue the next loop iteration.
|
|
855
|
+
if (parentParentURL === parentURL) {
|
|
856
|
+
// 12. Throw a Module Not Found error.
|
|
857
|
+
throw new VMError(`Cannot find module '${packageSpecifier}'`, 'ENOTFOUND');
|
|
858
|
+
}
|
|
859
|
+
parentURL = parentParentURL;
|
|
860
|
+
}
|
|
861
|
+
// d. Let pjson be the result of READ_PACKAGE_JSON(packageURL).
|
|
862
|
+
const pack = this.readPackage(packageURL);
|
|
863
|
+
// e. If pjson is not null and pjson.exports is not null or undefined, then
|
|
864
|
+
if (pack && pack.exports) {
|
|
865
|
+
// 1. Return the result of PACKAGE_EXPORTS_RESOLVE(packageURL, packageSubpath, pjson.exports, defaultConditions).
|
|
866
|
+
return this.packageExportsResolve(packageURL, packageSubpath, pack.exports, conditions, extList);
|
|
867
|
+
}
|
|
868
|
+
// f. Otherwise, if packageSubpath is equal to ".", then
|
|
869
|
+
if (packageSubpath === '.') {
|
|
870
|
+
// 1. If pjson.main is a string, then
|
|
871
|
+
// a. Return the URL resolution of main in packageURL.
|
|
872
|
+
return this.loadAsPackage(packageSubpath, pack, extList);
|
|
873
|
+
}
|
|
874
|
+
// g. Otherwise,
|
|
875
|
+
// 1. Return the URL resolution of packageSubpath in packageURL.
|
|
876
|
+
return this.pathConcat(packageURL, packageSubpath);
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
exports.Resolver = Resolver;
|
|
882
|
+
exports.DefaultResolver = DefaultResolver;
|