@endo/compartment-mapper 1.2.2 → 1.3.1
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/archive-lite.d.ts.map +1 -0
- package/archive-parsers.d.ts.map +1 -0
- package/archive.d.ts.map +1 -0
- package/bundle.d.ts.map +1 -0
- package/capture-lite.d.ts.map +1 -0
- package/import-archive-lite.d.ts.map +1 -0
- package/import-archive-parsers.d.ts.map +1 -0
- package/import-archive.d.ts.map +1 -0
- package/import-lite.d.ts.map +1 -0
- package/import-parsers.d.ts.map +1 -0
- package/import.d.ts.map +1 -0
- package/index.d.ts.map +1 -0
- package/node-modules.d.ts.map +1 -0
- package/node-powers.d.ts +1 -1
- package/node-powers.d.ts.map +1 -0
- package/node-powers.js +5 -1
- package/package.json +15 -11
- package/src/compartment-map.d.ts +1 -1
- package/src/compartment-map.d.ts.map +1 -1
- package/src/compartment-map.js +1 -3
- package/src/import-hook.d.ts +14 -1
- package/src/import-hook.d.ts.map +1 -1
- package/src/import-hook.js +493 -144
- package/src/import-lite.d.ts +20 -3
- package/src/import-lite.d.ts.map +1 -1
- package/src/import-lite.js +137 -15
- package/src/import.d.ts +45 -5
- package/src/import.d.ts.map +1 -1
- package/src/import.js +52 -6
- package/src/link.d.ts +2 -11
- package/src/link.d.ts.map +1 -1
- package/src/link.js +76 -154
- package/src/map-parser.d.ts +4 -0
- package/src/map-parser.d.ts.map +1 -0
- package/src/map-parser.js +339 -0
- package/src/node-modules.d.ts +2 -5
- package/src/node-modules.d.ts.map +1 -1
- package/src/node-modules.js +12 -5
- package/src/node-powers.d.ts +29 -23
- package/src/node-powers.d.ts.map +1 -1
- package/src/node-powers.js +102 -25
- package/src/parse-archive-cjs.js +2 -1
- package/src/parse-archive-mjs.js +2 -1
- package/src/parse-bytes.d.ts.map +1 -1
- package/src/parse-bytes.js +2 -6
- package/src/parse-cjs-shared-export-wrapper.d.ts.map +1 -1
- package/src/parse-cjs-shared-export-wrapper.js +23 -6
- package/src/parse-cjs.js +3 -2
- package/src/parse-json.d.ts +5 -3
- package/src/parse-json.d.ts.map +1 -1
- package/src/parse-json.js +9 -9
- package/src/parse-mjs.js +2 -1
- package/src/parse-pre-cjs.js +3 -2
- package/src/parse-pre-mjs.js +2 -1
- package/src/parse-text.d.ts.map +1 -1
- package/src/parse-text.js +2 -6
- package/src/policy.d.ts +21 -14
- package/src/policy.d.ts.map +1 -1
- package/src/policy.js +53 -43
- package/src/powers.d.ts +8 -1
- package/src/powers.d.ts.map +1 -1
- package/src/powers.js +60 -10
- package/src/search.d.ts.map +1 -1
- package/src/search.js +1 -2
- package/src/types.d.ts +343 -21
- package/src/types.d.ts.map +1 -1
- package/src/types.js +369 -22
package/src/import-hook.js
CHANGED
|
@@ -7,20 +7,41 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
// @ts-check
|
|
10
|
+
/**
|
|
11
|
+
* @import {
|
|
12
|
+
* ImportHook,
|
|
13
|
+
* ImportNowHook,
|
|
14
|
+
* RedirectStaticModuleInterface,
|
|
15
|
+
* StaticModuleType
|
|
16
|
+
* } from 'ses'
|
|
17
|
+
* @import {
|
|
18
|
+
* CompartmentDescriptor,
|
|
19
|
+
* ChooseModuleDescriptorOperators,
|
|
20
|
+
* ChooseModuleDescriptorOptions,
|
|
21
|
+
* ChooseModuleDescriptorYieldables,
|
|
22
|
+
* ExitModuleImportHook,
|
|
23
|
+
* FindRedirectParams,
|
|
24
|
+
* HashFn,
|
|
25
|
+
* ImportHookMaker,
|
|
26
|
+
* ImportNowHookMaker,
|
|
27
|
+
* MakeImportNowHookMakerOptions,
|
|
28
|
+
* ModuleDescriptor,
|
|
29
|
+
* ParseResult,
|
|
30
|
+
* ReadFn,
|
|
31
|
+
* ReadPowers,
|
|
32
|
+
* SourceMapHook,
|
|
33
|
+
* Sources,
|
|
34
|
+
* ReadNowPowers
|
|
35
|
+
* } from './types.js'
|
|
36
|
+
*/
|
|
10
37
|
|
|
11
|
-
|
|
12
|
-
/** @import {StaticModuleType} from 'ses' */
|
|
13
|
-
/** @import {RedirectStaticModuleInterface} from 'ses' */
|
|
14
|
-
/** @import {ReadFn} from './types.js' */
|
|
15
|
-
/** @import {ReadPowers} from './types.js' */
|
|
16
|
-
/** @import {HashFn} from './types.js' */
|
|
17
|
-
/** @import {Sources} from './types.js' */
|
|
18
|
-
/** @import {CompartmentDescriptor} from './types.js' */
|
|
19
|
-
/** @import {ImportHookMaker} from './types.js' */
|
|
20
|
-
/** @import {ExitModuleImportHook} from './types.js' */
|
|
21
|
-
|
|
22
|
-
import { attenuateModuleHook, enforceModulePolicy } from './policy.js';
|
|
38
|
+
import { asyncTrampoline, syncTrampoline } from '@endo/trampoline';
|
|
23
39
|
import { resolve } from './node-module-specifier.js';
|
|
40
|
+
import {
|
|
41
|
+
attenuateModuleHook,
|
|
42
|
+
ATTENUATORS_COMPARTMENT,
|
|
43
|
+
enforceModulePolicy,
|
|
44
|
+
} from './policy.js';
|
|
24
45
|
import { unpackReadPowers } from './powers.js';
|
|
25
46
|
|
|
26
47
|
// q, as in quote, for quoting strings in error messages.
|
|
@@ -36,6 +57,8 @@ const { apply } = Reflect;
|
|
|
36
57
|
*/
|
|
37
58
|
const freeze = Object.freeze;
|
|
38
59
|
|
|
60
|
+
const { entries, keys, assign, create } = Object;
|
|
61
|
+
|
|
39
62
|
const { hasOwnProperty } = Object.prototype;
|
|
40
63
|
/**
|
|
41
64
|
* @param {Record<string, any>} haystack
|
|
@@ -68,10 +91,97 @@ const nodejsConventionSearchSuffixes = [
|
|
|
68
91
|
'/index.node',
|
|
69
92
|
];
|
|
70
93
|
|
|
94
|
+
/**
|
|
95
|
+
* Given a module specifier which is an absolute path, attempt to match it with
|
|
96
|
+
* an existing compartment; return a {@link RedirectStaticModuleInterface} if found.
|
|
97
|
+
*
|
|
98
|
+
* @throws If we determine `absoluteModuleSpecifier` is unknown
|
|
99
|
+
* @param {FindRedirectParams} params Parameters
|
|
100
|
+
* @returns {RedirectStaticModuleInterface|undefined} A redirect or nothing
|
|
101
|
+
*/
|
|
102
|
+
const findRedirect = ({
|
|
103
|
+
compartmentDescriptor,
|
|
104
|
+
compartmentDescriptors,
|
|
105
|
+
compartments,
|
|
106
|
+
absoluteModuleSpecifier,
|
|
107
|
+
packageLocation,
|
|
108
|
+
}) => {
|
|
109
|
+
const moduleSpecifierLocation = new URL(
|
|
110
|
+
absoluteModuleSpecifier,
|
|
111
|
+
packageLocation,
|
|
112
|
+
).href;
|
|
113
|
+
|
|
114
|
+
// a file:// URL string
|
|
115
|
+
let someLocation = new URL('./', moduleSpecifierLocation).href;
|
|
116
|
+
|
|
117
|
+
// we are guaranteed an absolute path, so we can search "up" for the compartment
|
|
118
|
+
// due to the structure of `node_modules`
|
|
119
|
+
|
|
120
|
+
// n === count of path components to the fs root
|
|
121
|
+
for (;;) {
|
|
122
|
+
if (
|
|
123
|
+
someLocation !== ATTENUATORS_COMPARTMENT &&
|
|
124
|
+
someLocation in compartments
|
|
125
|
+
) {
|
|
126
|
+
const location = someLocation;
|
|
127
|
+
const someCompartmentDescriptor = compartmentDescriptors[location];
|
|
128
|
+
if (compartmentDescriptor === someCompartmentDescriptor) {
|
|
129
|
+
// this compartmentDescriptor wants to dynamically load its own module
|
|
130
|
+
// using an absolute path
|
|
131
|
+
return undefined;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// this tests the compartment referred to by the absolute path
|
|
135
|
+
// is a dependency of the compartment descriptor
|
|
136
|
+
if (compartmentDescriptor.compartments.has(location)) {
|
|
137
|
+
return {
|
|
138
|
+
specifier: absoluteModuleSpecifier,
|
|
139
|
+
compartment: compartments[location],
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// this tests if the compartment descriptor is a dependency of the
|
|
144
|
+
// compartment referred to by the absolute path.
|
|
145
|
+
// it may be in scope, but disallowed by policy.
|
|
146
|
+
if (
|
|
147
|
+
someCompartmentDescriptor.compartments.has(
|
|
148
|
+
compartmentDescriptor.location,
|
|
149
|
+
)
|
|
150
|
+
) {
|
|
151
|
+
enforceModulePolicy(
|
|
152
|
+
compartmentDescriptor.name,
|
|
153
|
+
someCompartmentDescriptor,
|
|
154
|
+
{
|
|
155
|
+
errorHint: `Blocked in import hook. ${q(absoluteModuleSpecifier)} is part of the compartment map and resolves to ${location}`,
|
|
156
|
+
},
|
|
157
|
+
);
|
|
158
|
+
return {
|
|
159
|
+
specifier: absoluteModuleSpecifier,
|
|
160
|
+
compartment: compartments[location],
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
throw new Error(`Could not import module: ${q(absoluteModuleSpecifier)}`);
|
|
165
|
+
} else {
|
|
166
|
+
// go up a directory
|
|
167
|
+
const parentLocation = new URL('../', someLocation).href;
|
|
168
|
+
|
|
169
|
+
// afaict this behavior is consistent across both windows and posix
|
|
170
|
+
if (parentLocation === someLocation) {
|
|
171
|
+
throw new Error(
|
|
172
|
+
`Could not import unknown module: ${q(absoluteModuleSpecifier)}`,
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
someLocation = parentLocation;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
};
|
|
180
|
+
|
|
71
181
|
/**
|
|
72
182
|
* @param {object} params
|
|
73
183
|
* @param {Record<string, any>=} params.modules
|
|
74
|
-
* @param {ExitModuleImportHook
|
|
184
|
+
* @param {ExitModuleImportHook} [params.exitModuleImportHook]
|
|
75
185
|
* @returns {ExitModuleImportHook|undefined}
|
|
76
186
|
*/
|
|
77
187
|
export const exitModuleImportHookMaker = ({
|
|
@@ -84,12 +194,12 @@ export const exitModuleImportHookMaker = ({
|
|
|
84
194
|
return async specifier => {
|
|
85
195
|
if (modules && has(modules, specifier)) {
|
|
86
196
|
const ns = modules[specifier];
|
|
87
|
-
return
|
|
197
|
+
return freeze({
|
|
88
198
|
imports: [],
|
|
89
|
-
exports: ns ?
|
|
199
|
+
exports: ns ? keys(ns) : [],
|
|
90
200
|
execute: moduleExports => {
|
|
91
201
|
moduleExports.default = ns;
|
|
92
|
-
|
|
202
|
+
assign(moduleExports, ns);
|
|
93
203
|
},
|
|
94
204
|
});
|
|
95
205
|
}
|
|
@@ -100,6 +210,185 @@ export const exitModuleImportHookMaker = ({
|
|
|
100
210
|
};
|
|
101
211
|
};
|
|
102
212
|
|
|
213
|
+
/**
|
|
214
|
+
* Expands a module specifier into a list of potential candidates based on
|
|
215
|
+
* `searchSuffixes`.
|
|
216
|
+
*
|
|
217
|
+
* @param {string} moduleSpecifier Module specifier
|
|
218
|
+
* @param {string[]} searchSuffixes Suffixes to search if the unmodified
|
|
219
|
+
* specifier is not found
|
|
220
|
+
* @returns {string[]} A list of potential candidates (including
|
|
221
|
+
* `moduleSpecifier` itself)
|
|
222
|
+
*/
|
|
223
|
+
const nominateCandidates = (moduleSpecifier, searchSuffixes) => {
|
|
224
|
+
// Collate candidate locations for the moduleSpecifier,
|
|
225
|
+
// to support Node.js conventions and similar.
|
|
226
|
+
const candidates = [moduleSpecifier];
|
|
227
|
+
for (const candidateSuffix of searchSuffixes) {
|
|
228
|
+
candidates.push(`${moduleSpecifier}${candidateSuffix}`);
|
|
229
|
+
}
|
|
230
|
+
return candidates;
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Returns a generator which applies {@link ChooseModuleDescriptorOperators} in
|
|
235
|
+
* `operators` using the options in options to ultimately result in a
|
|
236
|
+
* {@link StaticModuleType} for a particular {@link CompartmentDescriptor} (or
|
|
237
|
+
* `undefined`).
|
|
238
|
+
*
|
|
239
|
+
* Supports both {@link SyncChooseModuleDescriptorOperators sync} and
|
|
240
|
+
* {@link AsyncChooseModuleDescriptorOperators async} operators.
|
|
241
|
+
*
|
|
242
|
+
* Used by both {@link makeImportNowHookMaker} and {@link makeImportHookMaker}.
|
|
243
|
+
*
|
|
244
|
+
* @template {ChooseModuleDescriptorOperators} Operators Type of operators (sync
|
|
245
|
+
* or async)
|
|
246
|
+
* @param {ChooseModuleDescriptorOptions} options Options/context
|
|
247
|
+
* @param {Operators} operators Operators
|
|
248
|
+
* @returns {Generator<ChooseModuleDescriptorYieldables,
|
|
249
|
+
* StaticModuleType|undefined, Awaited<ChooseModuleDescriptorYieldables>>}
|
|
250
|
+
* Generator
|
|
251
|
+
*/
|
|
252
|
+
function* chooseModuleDescriptor(
|
|
253
|
+
{
|
|
254
|
+
candidates,
|
|
255
|
+
compartmentDescriptor,
|
|
256
|
+
compartmentDescriptors,
|
|
257
|
+
compartments,
|
|
258
|
+
computeSha512,
|
|
259
|
+
moduleDescriptors,
|
|
260
|
+
moduleSpecifier,
|
|
261
|
+
packageLocation,
|
|
262
|
+
packageSources,
|
|
263
|
+
readPowers,
|
|
264
|
+
sourceMapHook,
|
|
265
|
+
strictlyRequiredForCompartment,
|
|
266
|
+
},
|
|
267
|
+
{ maybeRead, parse, shouldDeferError = () => false },
|
|
268
|
+
) {
|
|
269
|
+
for (const candidateSpecifier of candidates) {
|
|
270
|
+
const candidateModuleDescriptor = moduleDescriptors[candidateSpecifier];
|
|
271
|
+
if (candidateModuleDescriptor !== undefined) {
|
|
272
|
+
const { compartment: candidateCompartmentName = packageLocation } =
|
|
273
|
+
candidateModuleDescriptor;
|
|
274
|
+
const candidateCompartment = compartments[candidateCompartmentName];
|
|
275
|
+
if (candidateCompartment === undefined) {
|
|
276
|
+
throw Error(
|
|
277
|
+
`compartment missing for candidate ${candidateSpecifier} in ${candidateCompartmentName}`,
|
|
278
|
+
);
|
|
279
|
+
}
|
|
280
|
+
// modify compartmentMap to include this redirect
|
|
281
|
+
const candidateCompartmentDescriptor =
|
|
282
|
+
compartmentDescriptors[candidateCompartmentName];
|
|
283
|
+
if (candidateCompartmentDescriptor === undefined) {
|
|
284
|
+
throw Error(
|
|
285
|
+
`compartmentDescriptor missing for candidate ${candidateSpecifier} in ${candidateCompartmentName}`,
|
|
286
|
+
);
|
|
287
|
+
}
|
|
288
|
+
candidateCompartmentDescriptor.modules[moduleSpecifier] =
|
|
289
|
+
candidateModuleDescriptor;
|
|
290
|
+
// return a redirect
|
|
291
|
+
/** @type {RedirectStaticModuleInterface} */
|
|
292
|
+
const record = {
|
|
293
|
+
specifier: candidateSpecifier,
|
|
294
|
+
compartment: candidateCompartment,
|
|
295
|
+
};
|
|
296
|
+
return record;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// Using a specifier as a location.
|
|
300
|
+
// This is not always valid.
|
|
301
|
+
// But, for Node.js, when the specifier is relative and not a directory
|
|
302
|
+
// name, they are usable as URL's.
|
|
303
|
+
const moduleLocation = resolveLocation(candidateSpecifier, packageLocation);
|
|
304
|
+
|
|
305
|
+
// "next" values must have type assertions for narrowing because we have
|
|
306
|
+
// multiple yielded types
|
|
307
|
+
const moduleBytes = /** @type {Uint8Array|undefined} */ (
|
|
308
|
+
yield maybeRead(moduleLocation)
|
|
309
|
+
);
|
|
310
|
+
|
|
311
|
+
if (moduleBytes !== undefined) {
|
|
312
|
+
/** @type {string | undefined} */
|
|
313
|
+
let sourceMap;
|
|
314
|
+
// must be narrowed
|
|
315
|
+
const envelope = /** @type {ParseResult} */ (
|
|
316
|
+
yield parse(
|
|
317
|
+
moduleBytes,
|
|
318
|
+
candidateSpecifier,
|
|
319
|
+
moduleLocation,
|
|
320
|
+
packageLocation,
|
|
321
|
+
{
|
|
322
|
+
readPowers,
|
|
323
|
+
sourceMapHook:
|
|
324
|
+
sourceMapHook &&
|
|
325
|
+
(nextSourceMapObject => {
|
|
326
|
+
sourceMap = JSON.stringify(nextSourceMapObject);
|
|
327
|
+
}),
|
|
328
|
+
compartmentDescriptor,
|
|
329
|
+
},
|
|
330
|
+
)
|
|
331
|
+
);
|
|
332
|
+
const {
|
|
333
|
+
parser,
|
|
334
|
+
bytes: transformedBytes,
|
|
335
|
+
record: concreteRecord,
|
|
336
|
+
} = envelope;
|
|
337
|
+
|
|
338
|
+
// Facilitate a redirect if the returned record has a different
|
|
339
|
+
// module specifier than the requested one.
|
|
340
|
+
if (candidateSpecifier !== moduleSpecifier) {
|
|
341
|
+
moduleDescriptors[moduleSpecifier] = {
|
|
342
|
+
module: candidateSpecifier,
|
|
343
|
+
compartment: packageLocation,
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
/** @type {StaticModuleType} */
|
|
347
|
+
const record = {
|
|
348
|
+
record: concreteRecord,
|
|
349
|
+
specifier: candidateSpecifier,
|
|
350
|
+
importMeta: { url: moduleLocation },
|
|
351
|
+
};
|
|
352
|
+
|
|
353
|
+
let sha512;
|
|
354
|
+
if (computeSha512 !== undefined) {
|
|
355
|
+
sha512 = computeSha512(transformedBytes);
|
|
356
|
+
|
|
357
|
+
if (sourceMapHook !== undefined && sourceMap !== undefined) {
|
|
358
|
+
sourceMapHook(sourceMap, {
|
|
359
|
+
compartment: packageLocation,
|
|
360
|
+
module: candidateSpecifier,
|
|
361
|
+
location: moduleLocation,
|
|
362
|
+
sha512,
|
|
363
|
+
});
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
const packageRelativeLocation = moduleLocation.slice(
|
|
368
|
+
packageLocation.length,
|
|
369
|
+
);
|
|
370
|
+
packageSources[candidateSpecifier] = {
|
|
371
|
+
location: packageRelativeLocation,
|
|
372
|
+
sourceLocation: moduleLocation,
|
|
373
|
+
parser,
|
|
374
|
+
bytes: transformedBytes,
|
|
375
|
+
record: concreteRecord,
|
|
376
|
+
sha512,
|
|
377
|
+
};
|
|
378
|
+
if (!shouldDeferError(parser)) {
|
|
379
|
+
for (const importSpecifier of getImportsFromRecord(record)) {
|
|
380
|
+
strictlyRequiredForCompartment(packageLocation).add(
|
|
381
|
+
resolve(importSpecifier, moduleSpecifier),
|
|
382
|
+
);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
return record;
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
return undefined;
|
|
390
|
+
}
|
|
391
|
+
|
|
103
392
|
/**
|
|
104
393
|
* @param {ReadFn|ReadPowers} readPowers
|
|
105
394
|
* @param {string} baseLocation
|
|
@@ -110,23 +399,23 @@ export const exitModuleImportHookMaker = ({
|
|
|
110
399
|
* @param {HashFn} [options.computeSha512]
|
|
111
400
|
* @param {Array<string>} [options.searchSuffixes] - Suffixes to search if the
|
|
112
401
|
* unmodified specifier is not found.
|
|
113
|
-
* Pass [] to emulate Node.js
|
|
114
|
-
* The default handles Node.js
|
|
402
|
+
* Pass [] to emulate Node.js' strict behavior.
|
|
403
|
+
* The default handles Node.js' CommonJS behavior.
|
|
115
404
|
* Unlike Node.js, the Compartment Mapper lifts CommonJS up, more like a
|
|
116
405
|
* bundler, and does not attempt to vary the behavior of resolution depending
|
|
117
406
|
* on the language of the importing module.
|
|
118
407
|
* @param {string} options.entryCompartmentName
|
|
119
408
|
* @param {string} options.entryModuleSpecifier
|
|
120
409
|
* @param {ExitModuleImportHook} [options.exitModuleImportHook]
|
|
121
|
-
* @param {
|
|
410
|
+
* @param {SourceMapHook} [options.sourceMapHook]
|
|
122
411
|
* @returns {ImportHookMaker}
|
|
123
412
|
*/
|
|
124
413
|
export const makeImportHookMaker = (
|
|
125
414
|
readPowers,
|
|
126
415
|
baseLocation,
|
|
127
416
|
{
|
|
128
|
-
sources =
|
|
129
|
-
compartmentDescriptors =
|
|
417
|
+
sources = create(null),
|
|
418
|
+
compartmentDescriptors = create(null),
|
|
130
419
|
archiveOnly = false,
|
|
131
420
|
computeSha512 = undefined,
|
|
132
421
|
searchSuffixes = nodejsConventionSearchSuffixes,
|
|
@@ -168,11 +457,10 @@ export const makeImportHookMaker = (
|
|
|
168
457
|
}) => {
|
|
169
458
|
// per-compartment:
|
|
170
459
|
packageLocation = resolveLocation(packageLocation, baseLocation);
|
|
171
|
-
const packageSources = sources[packageLocation] ||
|
|
460
|
+
const packageSources = sources[packageLocation] || create(null);
|
|
172
461
|
sources[packageLocation] = packageSources;
|
|
173
462
|
const compartmentDescriptor = compartmentDescriptors[packageLocation] || {};
|
|
174
|
-
const { modules: moduleDescriptors =
|
|
175
|
-
compartmentDescriptor;
|
|
463
|
+
const { modules: moduleDescriptors = create(null) } = compartmentDescriptor;
|
|
176
464
|
compartmentDescriptor.modules = moduleDescriptors;
|
|
177
465
|
|
|
178
466
|
/**
|
|
@@ -209,9 +497,11 @@ export const makeImportHookMaker = (
|
|
|
209
497
|
|
|
210
498
|
/** @type {ImportHook} */
|
|
211
499
|
const importHook = async moduleSpecifier => {
|
|
212
|
-
await null;
|
|
213
500
|
compartmentDescriptor.retained = true;
|
|
214
501
|
|
|
502
|
+
// for lint rule
|
|
503
|
+
await null;
|
|
504
|
+
|
|
215
505
|
// per-module:
|
|
216
506
|
|
|
217
507
|
// In Node.js, an absolute specifier always indicates a built-in or
|
|
@@ -257,130 +547,31 @@ export const makeImportHookMaker = (
|
|
|
257
547
|
);
|
|
258
548
|
}
|
|
259
549
|
|
|
260
|
-
// Collate candidate locations for the moduleSpecifier,
|
|
261
|
-
// to support Node.js conventions and similar.
|
|
262
|
-
const candidates = [moduleSpecifier];
|
|
263
|
-
for (const candidateSuffix of searchSuffixes) {
|
|
264
|
-
candidates.push(`${moduleSpecifier}${candidateSuffix}`);
|
|
265
|
-
}
|
|
266
|
-
|
|
267
550
|
const { maybeRead } = unpackReadPowers(readPowers);
|
|
268
551
|
|
|
269
|
-
|
|
270
|
-
const candidateModuleDescriptor = moduleDescriptors[candidateSpecifier];
|
|
271
|
-
if (candidateModuleDescriptor !== undefined) {
|
|
272
|
-
const { compartment: candidateCompartmentName = packageLocation } =
|
|
273
|
-
candidateModuleDescriptor;
|
|
274
|
-
const candidateCompartment = compartments[candidateCompartmentName];
|
|
275
|
-
if (candidateCompartment === undefined) {
|
|
276
|
-
throw Error(
|
|
277
|
-
`compartment missing for candidate ${candidateSpecifier} in ${candidateCompartmentName}`,
|
|
278
|
-
);
|
|
279
|
-
}
|
|
280
|
-
// modify compartmentMap to include this redirect
|
|
281
|
-
const candidateCompartmentDescriptor =
|
|
282
|
-
compartmentDescriptors[candidateCompartmentName];
|
|
283
|
-
if (candidateCompartmentDescriptor === undefined) {
|
|
284
|
-
throw Error(
|
|
285
|
-
`compartmentDescriptor missing for candidate ${candidateSpecifier} in ${candidateCompartmentName}`,
|
|
286
|
-
);
|
|
287
|
-
}
|
|
288
|
-
candidateCompartmentDescriptor.modules[moduleSpecifier] =
|
|
289
|
-
candidateModuleDescriptor;
|
|
290
|
-
// return a redirect
|
|
291
|
-
/** @type {RedirectStaticModuleInterface} */
|
|
292
|
-
const record = {
|
|
293
|
-
specifier: candidateSpecifier,
|
|
294
|
-
compartment: candidateCompartment,
|
|
295
|
-
};
|
|
296
|
-
return record;
|
|
297
|
-
}
|
|
552
|
+
const candidates = nominateCandidates(moduleSpecifier, searchSuffixes);
|
|
298
553
|
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
554
|
+
const record = await asyncTrampoline(
|
|
555
|
+
chooseModuleDescriptor,
|
|
556
|
+
{
|
|
557
|
+
candidates,
|
|
558
|
+
compartmentDescriptor,
|
|
559
|
+
compartmentDescriptors,
|
|
560
|
+
compartments,
|
|
561
|
+
computeSha512,
|
|
562
|
+
moduleDescriptors,
|
|
563
|
+
moduleSpecifier,
|
|
305
564
|
packageLocation,
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
const envelope = await parse(
|
|
314
|
-
moduleBytes,
|
|
315
|
-
candidateSpecifier,
|
|
316
|
-
moduleLocation,
|
|
317
|
-
packageLocation,
|
|
318
|
-
{
|
|
319
|
-
readPowers,
|
|
320
|
-
sourceMapHook:
|
|
321
|
-
sourceMapHook &&
|
|
322
|
-
(nextSourceMapObject => {
|
|
323
|
-
sourceMap = JSON.stringify(nextSourceMapObject);
|
|
324
|
-
}),
|
|
325
|
-
compartmentDescriptor,
|
|
326
|
-
},
|
|
327
|
-
);
|
|
328
|
-
const {
|
|
329
|
-
parser,
|
|
330
|
-
bytes: transformedBytes,
|
|
331
|
-
record: concreteRecord,
|
|
332
|
-
} = envelope;
|
|
333
|
-
|
|
334
|
-
// Facilitate a redirect if the returned record has a different
|
|
335
|
-
// module specifier than the requested one.
|
|
336
|
-
if (candidateSpecifier !== moduleSpecifier) {
|
|
337
|
-
moduleDescriptors[moduleSpecifier] = {
|
|
338
|
-
module: candidateSpecifier,
|
|
339
|
-
compartment: packageLocation,
|
|
340
|
-
};
|
|
341
|
-
}
|
|
342
|
-
/** @type {StaticModuleType} */
|
|
343
|
-
const record = {
|
|
344
|
-
record: concreteRecord,
|
|
345
|
-
specifier: candidateSpecifier,
|
|
346
|
-
importMeta: { url: moduleLocation },
|
|
347
|
-
};
|
|
348
|
-
|
|
349
|
-
let sha512;
|
|
350
|
-
if (computeSha512 !== undefined) {
|
|
351
|
-
sha512 = computeSha512(transformedBytes);
|
|
352
|
-
|
|
353
|
-
if (sourceMapHook !== undefined && sourceMap !== undefined) {
|
|
354
|
-
sourceMapHook(sourceMap, {
|
|
355
|
-
compartment: packageLocation,
|
|
356
|
-
module: candidateSpecifier,
|
|
357
|
-
location: moduleLocation,
|
|
358
|
-
sha512,
|
|
359
|
-
});
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
const packageRelativeLocation = moduleLocation.slice(
|
|
364
|
-
packageLocation.length,
|
|
365
|
-
);
|
|
366
|
-
packageSources[candidateSpecifier] = {
|
|
367
|
-
location: packageRelativeLocation,
|
|
368
|
-
sourceLocation: moduleLocation,
|
|
369
|
-
parser,
|
|
370
|
-
bytes: transformedBytes,
|
|
371
|
-
record: concreteRecord,
|
|
372
|
-
sha512,
|
|
373
|
-
};
|
|
374
|
-
if (!shouldDeferError(parser)) {
|
|
375
|
-
for (const importSpecifier of getImportsFromRecord(record)) {
|
|
376
|
-
strictlyRequiredForCompartment(packageLocation).add(
|
|
377
|
-
resolve(importSpecifier, moduleSpecifier),
|
|
378
|
-
);
|
|
379
|
-
}
|
|
380
|
-
}
|
|
565
|
+
packageSources,
|
|
566
|
+
readPowers,
|
|
567
|
+
sourceMapHook,
|
|
568
|
+
strictlyRequiredForCompartment,
|
|
569
|
+
},
|
|
570
|
+
{ maybeRead, parse, shouldDeferError },
|
|
571
|
+
);
|
|
381
572
|
|
|
382
|
-
|
|
383
|
-
|
|
573
|
+
if (record) {
|
|
574
|
+
return record;
|
|
384
575
|
}
|
|
385
576
|
|
|
386
577
|
return deferError(
|
|
@@ -399,3 +590,161 @@ export const makeImportHookMaker = (
|
|
|
399
590
|
};
|
|
400
591
|
return makeImportHook;
|
|
401
592
|
};
|
|
593
|
+
|
|
594
|
+
/**
|
|
595
|
+
* Synchronous import for dynamic requires.
|
|
596
|
+
*
|
|
597
|
+
* @param {ReadNowPowers} readPowers
|
|
598
|
+
* @param {string} baseLocation
|
|
599
|
+
* @param {MakeImportNowHookMakerOptions} options
|
|
600
|
+
* @returns {ImportNowHookMaker}
|
|
601
|
+
*/
|
|
602
|
+
export function makeImportNowHookMaker(
|
|
603
|
+
readPowers,
|
|
604
|
+
baseLocation,
|
|
605
|
+
{
|
|
606
|
+
sources = create(null),
|
|
607
|
+
compartmentDescriptors = create(null),
|
|
608
|
+
computeSha512 = undefined,
|
|
609
|
+
searchSuffixes = nodejsConventionSearchSuffixes,
|
|
610
|
+
sourceMapHook = undefined,
|
|
611
|
+
exitModuleImportNowHook,
|
|
612
|
+
},
|
|
613
|
+
) {
|
|
614
|
+
// Set of specifiers for modules (scoped to compartment) whose parser is not
|
|
615
|
+
// using heuristics to determine imports.
|
|
616
|
+
/** @type {Map<string, Set<string>>} compartment name ->* module specifier */
|
|
617
|
+
const strictlyRequired = new Map();
|
|
618
|
+
|
|
619
|
+
/**
|
|
620
|
+
* @param {string} compartmentName
|
|
621
|
+
*/
|
|
622
|
+
const strictlyRequiredForCompartment = compartmentName => {
|
|
623
|
+
let compartmentStrictlyRequired = strictlyRequired.get(compartmentName);
|
|
624
|
+
if (compartmentStrictlyRequired !== undefined) {
|
|
625
|
+
return compartmentStrictlyRequired;
|
|
626
|
+
}
|
|
627
|
+
compartmentStrictlyRequired = new Set();
|
|
628
|
+
strictlyRequired.set(compartmentName, compartmentStrictlyRequired);
|
|
629
|
+
return compartmentStrictlyRequired;
|
|
630
|
+
};
|
|
631
|
+
|
|
632
|
+
/**
|
|
633
|
+
* @type {ImportNowHookMaker}
|
|
634
|
+
*/
|
|
635
|
+
const makeImportNowHook = ({
|
|
636
|
+
packageLocation,
|
|
637
|
+
packageName: _packageName,
|
|
638
|
+
parse,
|
|
639
|
+
compartments,
|
|
640
|
+
}) => {
|
|
641
|
+
if (!('isSyncParser' in parse)) {
|
|
642
|
+
return function impossibleTransformImportNowHook() {
|
|
643
|
+
throw new Error(
|
|
644
|
+
'Dynamic requires are only possible with synchronous parsers and no asynchronous module transforms in options',
|
|
645
|
+
);
|
|
646
|
+
};
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
const compartmentDescriptor = compartmentDescriptors[packageLocation] || {};
|
|
650
|
+
|
|
651
|
+
packageLocation = resolveLocation(packageLocation, baseLocation);
|
|
652
|
+
const packageSources = sources[packageLocation] || create(null);
|
|
653
|
+
sources[packageLocation] = packageSources;
|
|
654
|
+
const {
|
|
655
|
+
modules:
|
|
656
|
+
moduleDescriptors = /** @type {Record<string, ModuleDescriptor>} */ (
|
|
657
|
+
create(null)
|
|
658
|
+
),
|
|
659
|
+
} = compartmentDescriptor;
|
|
660
|
+
compartmentDescriptor.modules = moduleDescriptors;
|
|
661
|
+
|
|
662
|
+
let { policy } = compartmentDescriptor;
|
|
663
|
+
policy = policy || create(null);
|
|
664
|
+
|
|
665
|
+
// Associates modules with compartment descriptors based on policy
|
|
666
|
+
// in cases where the association was not made when building the
|
|
667
|
+
// compartment map but is indicated by the policy.
|
|
668
|
+
if ('packages' in policy && typeof policy.packages === 'object') {
|
|
669
|
+
for (const [packageName, packagePolicyItem] of entries(policy.packages)) {
|
|
670
|
+
if (
|
|
671
|
+
!(packageName in compartmentDescriptor.modules) &&
|
|
672
|
+
packageName in compartmentDescriptor.scopes &&
|
|
673
|
+
packagePolicyItem
|
|
674
|
+
) {
|
|
675
|
+
compartmentDescriptor.modules[packageName] =
|
|
676
|
+
compartmentDescriptor.scopes[packageName];
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
const { maybeReadNow, isAbsolute } = readPowers;
|
|
682
|
+
|
|
683
|
+
/** @type {ImportNowHook} */
|
|
684
|
+
const importNowHook = moduleSpecifier => {
|
|
685
|
+
if (isAbsolute(moduleSpecifier)) {
|
|
686
|
+
const record = findRedirect({
|
|
687
|
+
compartmentDescriptor,
|
|
688
|
+
compartmentDescriptors,
|
|
689
|
+
compartments,
|
|
690
|
+
absoluteModuleSpecifier: moduleSpecifier,
|
|
691
|
+
packageLocation,
|
|
692
|
+
});
|
|
693
|
+
if (record) {
|
|
694
|
+
return record;
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
const candidates = nominateCandidates(moduleSpecifier, searchSuffixes);
|
|
699
|
+
|
|
700
|
+
const record = syncTrampoline(
|
|
701
|
+
chooseModuleDescriptor,
|
|
702
|
+
{
|
|
703
|
+
candidates,
|
|
704
|
+
compartmentDescriptor,
|
|
705
|
+
compartmentDescriptors,
|
|
706
|
+
compartments,
|
|
707
|
+
computeSha512,
|
|
708
|
+
moduleDescriptors,
|
|
709
|
+
moduleSpecifier,
|
|
710
|
+
packageLocation,
|
|
711
|
+
packageSources,
|
|
712
|
+
readPowers,
|
|
713
|
+
sourceMapHook,
|
|
714
|
+
strictlyRequiredForCompartment,
|
|
715
|
+
},
|
|
716
|
+
{
|
|
717
|
+
maybeRead: maybeReadNow,
|
|
718
|
+
parse,
|
|
719
|
+
},
|
|
720
|
+
);
|
|
721
|
+
|
|
722
|
+
if (record) {
|
|
723
|
+
return record;
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
if (exitModuleImportNowHook) {
|
|
727
|
+
// this hook is responsible for ensuring that the moduleSpecifier actually refers to an exit module
|
|
728
|
+
const exitRecord = exitModuleImportNowHook(
|
|
729
|
+
moduleSpecifier,
|
|
730
|
+
packageLocation,
|
|
731
|
+
);
|
|
732
|
+
|
|
733
|
+
if (!exitRecord) {
|
|
734
|
+
throw new Error(`Could not import module: ${q(moduleSpecifier)}`);
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
return exitRecord;
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
throw new Error(
|
|
741
|
+
`Could not import module: ${q(
|
|
742
|
+
moduleSpecifier,
|
|
743
|
+
)}; try providing an importNowHook`,
|
|
744
|
+
);
|
|
745
|
+
};
|
|
746
|
+
|
|
747
|
+
return importNowHook;
|
|
748
|
+
};
|
|
749
|
+
return makeImportNowHook;
|
|
750
|
+
}
|