@squiz/render-runtime-lib 1.2.1-alpha.60

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/README.md +11 -0
  2. package/lib/component-runner/component-runner.spec.d.ts +1 -0
  3. package/lib/component-runner/index.d.ts +19 -0
  4. package/lib/component-runner/worker/WorkerPool.d.ts +50 -0
  5. package/lib/component-runner/worker/getRuntimeModules.d.ts +1 -0
  6. package/lib/component-runner/worker/worker-root.d.ts +1 -0
  7. package/lib/index.d.ts +9 -0
  8. package/lib/index.js +63783 -0
  9. package/lib/index.js.map +7 -0
  10. package/lib/render-runtime-lib.spec.d.ts +1 -0
  11. package/lib/test/helpers/fixtures.d.ts +6 -0
  12. package/lib/test/helpers/stack.d.ts +2 -0
  13. package/lib/utils/convertFunctionStaticFilesToFqdn.d.ts +1 -0
  14. package/lib/utils/log.d.ts +3 -0
  15. package/lib/webserver/app.d.ts +2 -0
  16. package/lib/webserver/controllers/core.d.ts +3 -0
  17. package/lib/webserver/controllers/definition.d.ts +3 -0
  18. package/lib/webserver/controllers/definition.spec.d.ts +1 -0
  19. package/lib/webserver/controllers/index.d.ts +4 -0
  20. package/lib/webserver/controllers/render.d.ts +3 -0
  21. package/lib/webserver/controllers/render.spec.d.ts +1 -0
  22. package/lib/webserver/controllers/static.d.ts +3 -0
  23. package/lib/webserver/controllers/static.spec.d.ts +1 -0
  24. package/lib/webserver/index.d.ts +15 -0
  25. package/lib/worker/bridge.js +1000 -0
  26. package/lib/worker/compiler.js +87 -0
  27. package/lib/worker/events.js +977 -0
  28. package/lib/worker/nodevm.js +503 -0
  29. package/lib/worker/resolver-compat.js +342 -0
  30. package/lib/worker/resolver.js +882 -0
  31. package/lib/worker/script.js +388 -0
  32. package/lib/worker/setup-node-sandbox.js +464 -0
  33. package/lib/worker/setup-sandbox.js +453 -0
  34. package/lib/worker/transformer.js +176 -0
  35. package/lib/worker/vm.js +539 -0
  36. package/lib/worker/worker-root.js +40146 -0
  37. package/lib/worker/worker-root.js.map +7 -0
  38. package/package.json +55 -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;