@endo/compartment-mapper 1.6.2 → 1.6.3
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/package.json +4 -4
- package/src/generic-graph.d.ts +84 -0
- package/src/generic-graph.d.ts.map +1 -0
- package/src/generic-graph.js +351 -0
- package/src/import-hook.d.ts.map +1 -1
- package/src/import-hook.js +18 -2
- package/src/json.d.ts +1 -1
- package/src/json.d.ts.map +1 -1
- package/src/json.js +10 -3
- package/src/node-modules.d.ts +3 -2
- package/src/node-modules.d.ts.map +1 -1
- package/src/node-modules.js +200 -165
- package/src/node-powers.d.ts +6 -5
- package/src/node-powers.d.ts.map +1 -1
- package/src/node-powers.js +11 -8
- package/src/types/external.d.ts +5 -1
- package/src/types/external.d.ts.map +1 -1
- package/src/types/external.ts +6 -1
- package/src/types/generic-graph.d.ts +17 -0
- package/src/types/generic-graph.d.ts.map +1 -0
- package/src/types/generic-graph.ts +17 -0
- package/src/types/internal.d.ts +1 -0
- package/src/types/internal.d.ts.map +1 -1
- package/src/types/internal.ts +1 -0
- package/src/types/node-modules.d.ts +36 -13
- package/src/types/node-modules.d.ts.map +1 -1
- package/src/types/node-modules.ts +40 -13
- package/src/types/powers.d.ts +38 -11
- package/src/types/powers.d.ts.map +1 -1
- package/src/types/powers.ts +50 -17
package/src/node-modules.js
CHANGED
|
@@ -13,18 +13,32 @@
|
|
|
13
13
|
|
|
14
14
|
/* eslint no-shadow: 0 */
|
|
15
15
|
|
|
16
|
+
import { inferExportsAndAliases } from './infer-exports.js';
|
|
17
|
+
import { parseLocatedJson } from './json.js';
|
|
18
|
+
import { join } from './node-module-specifier.js';
|
|
19
|
+
import { assertPolicy } from './policy-format.js';
|
|
20
|
+
import {
|
|
21
|
+
ATTENUATORS_COMPARTMENT,
|
|
22
|
+
dependencyAllowedByPolicy,
|
|
23
|
+
getPolicyForPackage,
|
|
24
|
+
} from './policy.js';
|
|
25
|
+
import { unpackReadPowers } from './powers.js';
|
|
26
|
+
import { search, searchDescriptor } from './search.js';
|
|
27
|
+
import { GenericGraph, makeShortestPath } from './generic-graph.js';
|
|
28
|
+
|
|
16
29
|
/**
|
|
17
30
|
* @import {
|
|
18
31
|
* CanonicalFn,
|
|
19
32
|
* CompartmentDescriptor,
|
|
20
33
|
* CompartmentMapDescriptor,
|
|
21
34
|
* CompartmentMapForNodeModulesOptions,
|
|
35
|
+
* FileUrlString,
|
|
22
36
|
* LanguageForExtension,
|
|
23
37
|
* MapNodeModulesOptions,
|
|
38
|
+
* MaybeReadDescriptorFn,
|
|
24
39
|
* MaybeReadFn,
|
|
25
40
|
* MaybeReadPowers,
|
|
26
41
|
* PackageDescriptor,
|
|
27
|
-
* ReadDescriptorFn,
|
|
28
42
|
* ReadFn,
|
|
29
43
|
* ReadPowers,
|
|
30
44
|
* SomePackagePolicy,
|
|
@@ -38,23 +52,11 @@
|
|
|
38
52
|
* GatherDependencyOptions,
|
|
39
53
|
* GraphPackageOptions,
|
|
40
54
|
* GraphPackagesOptions,
|
|
55
|
+
* LogicalPathGraph,
|
|
41
56
|
* PackageDetails,
|
|
42
57
|
* } from './types/node-modules.js'
|
|
43
58
|
*/
|
|
44
59
|
|
|
45
|
-
import { pathCompare } from '@endo/path-compare';
|
|
46
|
-
import { inferExportsAndAliases } from './infer-exports.js';
|
|
47
|
-
import { parseLocatedJson } from './json.js';
|
|
48
|
-
import { join } from './node-module-specifier.js';
|
|
49
|
-
import { assertPolicy } from './policy-format.js';
|
|
50
|
-
import {
|
|
51
|
-
ATTENUATORS_COMPARTMENT,
|
|
52
|
-
dependencyAllowedByPolicy,
|
|
53
|
-
getPolicyForPackage,
|
|
54
|
-
} from './policy.js';
|
|
55
|
-
import { unpackReadPowers } from './powers.js';
|
|
56
|
-
import { search, searchDescriptor } from './search.js';
|
|
57
|
-
|
|
58
60
|
const { assign, create, keys, values, entries } = Object;
|
|
59
61
|
|
|
60
62
|
const decoder = new TextDecoder();
|
|
@@ -68,14 +70,50 @@ const q = JSON.stringify;
|
|
|
68
70
|
const noop = () => {};
|
|
69
71
|
|
|
70
72
|
/**
|
|
73
|
+
* Given a relative path andd URL, return a fully qualified URL string.
|
|
74
|
+
*
|
|
75
|
+
* @overload
|
|
71
76
|
* @param {string} rel - a relative URL
|
|
72
|
-
* @param {
|
|
73
|
-
* @returns {string}
|
|
77
|
+
* @param {URL} abs - a fully qualified URL
|
|
78
|
+
* @returns {string} Fully qualified URL string
|
|
79
|
+
*/
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Given a relative path and fully qualified stringlike URL, return a fully
|
|
83
|
+
* qualified stringlike URL.
|
|
84
|
+
*
|
|
85
|
+
* @template {string} [T=string] Type of fully qualified URL string
|
|
86
|
+
* @overload
|
|
87
|
+
* @param {string} rel - a relative URL
|
|
88
|
+
* @param {T} abs - a fully qualified URL
|
|
89
|
+
* @returns {T} Fully qualified stringlike URL
|
|
90
|
+
*/
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* @param {string} rel - a relative URL
|
|
94
|
+
* @param {string|URL} abs - a fully qualified URL
|
|
74
95
|
*/
|
|
75
96
|
const resolveLocation = (rel, abs) => {
|
|
76
97
|
return new URL(rel, abs).toString();
|
|
77
98
|
};
|
|
78
99
|
|
|
100
|
+
/**
|
|
101
|
+
* Ensures a string is a file URL (a {@link FileUrlString})
|
|
102
|
+
*
|
|
103
|
+
* @param {unknown} allegedPackageLocation - a package location to assert
|
|
104
|
+
* @returns {asserts allegedPackageLocation is FileUrlString}
|
|
105
|
+
*/
|
|
106
|
+
const assertFileUrlString = allegedPackageLocation => {
|
|
107
|
+
assert(
|
|
108
|
+
typeof allegedPackageLocation === 'string',
|
|
109
|
+
`Package location must be a string, got ${q(allegedPackageLocation)}`,
|
|
110
|
+
);
|
|
111
|
+
assert(
|
|
112
|
+
allegedPackageLocation.startsWith('file://'),
|
|
113
|
+
`Package location must be a file URL, got ${q(allegedPackageLocation)}`,
|
|
114
|
+
);
|
|
115
|
+
};
|
|
116
|
+
|
|
79
117
|
// Exported for testing:
|
|
80
118
|
/**
|
|
81
119
|
* @param {string} location
|
|
@@ -93,10 +131,28 @@ export const basename = location => {
|
|
|
93
131
|
return pathname.slice(index + 1);
|
|
94
132
|
};
|
|
95
133
|
|
|
134
|
+
/**
|
|
135
|
+
* Asserts that the given value is a `PackageDescriptor`.
|
|
136
|
+
*
|
|
137
|
+
* TODO: This only validates that the value is a plain object. As mentioned in
|
|
138
|
+
* {@link PackageDescriptor}, `name` is currently a required field, but in the
|
|
139
|
+
* real world this is not so. We _do_ make assumptions about the shape of a
|
|
140
|
+
* `PackageDescriptor`, but it may not be worth eagerly validating further.
|
|
141
|
+
* @param {unknown} allegedPackageDescriptor
|
|
142
|
+
* @returns {asserts allegedPackageDescriptor is PackageDescriptor}
|
|
143
|
+
*/
|
|
144
|
+
const assertPackageDescriptor = allegedPackageDescriptor => {
|
|
145
|
+
assert(
|
|
146
|
+
typeof allegedPackageDescriptor !== 'function' &&
|
|
147
|
+
Object(allegedPackageDescriptor) === allegedPackageDescriptor,
|
|
148
|
+
`Package descriptor must be a plain object, got ${q(allegedPackageDescriptor)}`,
|
|
149
|
+
);
|
|
150
|
+
};
|
|
151
|
+
|
|
96
152
|
/**
|
|
97
153
|
* @param {MaybeReadFn} maybeRead
|
|
98
154
|
* @param {string} packageLocation
|
|
99
|
-
* @returns {Promise<
|
|
155
|
+
* @returns {Promise<PackageDescriptor|undefined>}
|
|
100
156
|
*/
|
|
101
157
|
const readDescriptor = async (maybeRead, packageLocation) => {
|
|
102
158
|
const descriptorLocation = resolveLocation('package.json', packageLocation);
|
|
@@ -106,16 +162,20 @@ const readDescriptor = async (maybeRead, packageLocation) => {
|
|
|
106
162
|
}
|
|
107
163
|
const descriptorText = decoder.decode(descriptorBytes);
|
|
108
164
|
const descriptor = parseLocatedJson(descriptorText, descriptorLocation);
|
|
165
|
+
assertPackageDescriptor(descriptor);
|
|
109
166
|
return descriptor;
|
|
110
167
|
};
|
|
111
168
|
|
|
112
169
|
/**
|
|
113
|
-
*
|
|
170
|
+
* Memoized {@link readDescriptor}
|
|
171
|
+
*
|
|
172
|
+
* @param {Record<string, Promise<PackageDescriptor|undefined>>} memo
|
|
114
173
|
* @param {MaybeReadFn} maybeRead
|
|
115
174
|
* @param {string} packageLocation
|
|
116
|
-
* @returns {Promise<
|
|
175
|
+
* @returns {Promise<PackageDescriptor|undefined>}
|
|
117
176
|
*/
|
|
118
177
|
const readDescriptorWithMemo = async (memo, maybeRead, packageLocation) => {
|
|
178
|
+
/** @type {Promise<PackageDescriptor|undefined>} */
|
|
119
179
|
let promise = memo[packageLocation];
|
|
120
180
|
if (promise !== undefined) {
|
|
121
181
|
return promise;
|
|
@@ -125,90 +185,6 @@ const readDescriptorWithMemo = async (memo, maybeRead, packageLocation) => {
|
|
|
125
185
|
return promise;
|
|
126
186
|
};
|
|
127
187
|
|
|
128
|
-
/**
|
|
129
|
-
* Compares `logicalPath` to the current best logical path in `preferredPackageLogicalPathMap` for `packageLocation`.
|
|
130
|
-
*
|
|
131
|
-
* If no current best path exists, it returns `logicalPath`.
|
|
132
|
-
*
|
|
133
|
-
* @template {string[]} T
|
|
134
|
-
* @template {string[]} U
|
|
135
|
-
* @param {T} logicalPath
|
|
136
|
-
* @param {string} packageLocation
|
|
137
|
-
* @param {Map<string, U>} preferredPackageLogicalPathMap
|
|
138
|
-
* @returns {T|U}
|
|
139
|
-
*/
|
|
140
|
-
const currentBestLogicalPath = (
|
|
141
|
-
logicalPath,
|
|
142
|
-
packageLocation,
|
|
143
|
-
preferredPackageLogicalPathMap,
|
|
144
|
-
) => {
|
|
145
|
-
const theCurrentBest = preferredPackageLogicalPathMap.get(packageLocation);
|
|
146
|
-
if (theCurrentBest === undefined) {
|
|
147
|
-
return logicalPath;
|
|
148
|
-
}
|
|
149
|
-
return pathCompare(logicalPath, theCurrentBest) < 0
|
|
150
|
-
? logicalPath
|
|
151
|
-
: theCurrentBest;
|
|
152
|
-
};
|
|
153
|
-
|
|
154
|
-
/**
|
|
155
|
-
* Updates the shortest paths in a subgraph of `graph` starting with `packageLocation`.
|
|
156
|
-
*
|
|
157
|
-
* This should be called upon the second (and each subsequent) visit to a graph node.
|
|
158
|
-
*
|
|
159
|
-
* @param {Graph} graph Graph
|
|
160
|
-
* @param {string} packageLocation Location of the package to start with
|
|
161
|
-
* @param {string[]} logicalPath Current path parts of the same package
|
|
162
|
-
* @param {Map<string, string[]>} [preferredPackageLogicalPathMap] Mapping of shortest known paths for each package location
|
|
163
|
-
* @returns {void}
|
|
164
|
-
*/
|
|
165
|
-
const updateShortestPaths = (
|
|
166
|
-
graph,
|
|
167
|
-
packageLocation,
|
|
168
|
-
logicalPath,
|
|
169
|
-
preferredPackageLogicalPathMap = new Map(),
|
|
170
|
-
) => {
|
|
171
|
-
const node = graph[packageLocation];
|
|
172
|
-
if (!node) {
|
|
173
|
-
throw new ReferenceError(
|
|
174
|
-
`Cannot find package at ${packageLocation} in graph`,
|
|
175
|
-
);
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
const bestLogicalPath = currentBestLogicalPath(
|
|
179
|
-
logicalPath,
|
|
180
|
-
packageLocation,
|
|
181
|
-
preferredPackageLogicalPathMap,
|
|
182
|
-
);
|
|
183
|
-
|
|
184
|
-
if (bestLogicalPath === logicalPath) {
|
|
185
|
-
preferredPackageLogicalPathMap.set(packageLocation, bestLogicalPath);
|
|
186
|
-
|
|
187
|
-
for (const name of keys(node.dependencyLocations).sort()) {
|
|
188
|
-
const packageLocation = node.dependencyLocations[name];
|
|
189
|
-
if (!packageLocation) {
|
|
190
|
-
// "should never happen"
|
|
191
|
-
throw new ReferenceError(
|
|
192
|
-
`Expected graph node ${q(node.name)} to contain a dependency location for ${q(name)}`,
|
|
193
|
-
);
|
|
194
|
-
}
|
|
195
|
-
updateShortestPaths(
|
|
196
|
-
graph,
|
|
197
|
-
packageLocation,
|
|
198
|
-
[...logicalPath, name],
|
|
199
|
-
preferredPackageLogicalPathMap,
|
|
200
|
-
);
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
// a path length of 0 means the node represents the eventual entry compartment.
|
|
205
|
-
// we do not want to mess with that path.
|
|
206
|
-
if (node.path.length && node.path !== bestLogicalPath) {
|
|
207
|
-
node.path = bestLogicalPath;
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
return undefined;
|
|
211
|
-
};
|
|
212
188
|
/**
|
|
213
189
|
* `findPackage` behaves as Node.js to find third-party modules by searching
|
|
214
190
|
* parent to ancestor directories for a `node_modules` directory that contains
|
|
@@ -218,9 +194,9 @@ const updateShortestPaths = (
|
|
|
218
194
|
* these are the locations that package managers drop a package so Node.js can
|
|
219
195
|
* find it efficiently.
|
|
220
196
|
*
|
|
221
|
-
* @param {
|
|
197
|
+
* @param {MaybeReadDescriptorFn} readDescriptor
|
|
222
198
|
* @param {CanonicalFn} canonical
|
|
223
|
-
* @param {
|
|
199
|
+
* @param {FileUrlString} directory
|
|
224
200
|
* @param {string} name
|
|
225
201
|
* @returns {Promise<PackageDetails|undefined>}
|
|
226
202
|
*/
|
|
@@ -232,6 +208,10 @@ const findPackage = async (readDescriptor, canonical, directory, name) => {
|
|
|
232
208
|
resolveLocation(`node_modules/${name}/`, directory),
|
|
233
209
|
);
|
|
234
210
|
|
|
211
|
+
// We have no guarantee that `canonical` will return a file URL; it spits
|
|
212
|
+
// back whatever we give it if `fs.promises.realpath()` rejects.
|
|
213
|
+
assertFileUrlString(packageLocation);
|
|
214
|
+
|
|
235
215
|
// eslint-disable-next-line no-await-in-loop
|
|
236
216
|
const packageDescriptor = await readDescriptor(packageLocation);
|
|
237
217
|
if (packageDescriptor !== undefined) {
|
|
@@ -339,6 +319,37 @@ const inferParsers = (descriptor, location, languageOptions) => {
|
|
|
339
319
|
return { ...commonjsLanguageForExtension, ...packageLanguageForExtension };
|
|
340
320
|
};
|
|
341
321
|
|
|
322
|
+
/**
|
|
323
|
+
* This returns the "weight" of a package name, which is used when determining
|
|
324
|
+
* the shortest path.
|
|
325
|
+
*
|
|
326
|
+
* It is an analogue of the `pathCompare` function.
|
|
327
|
+
*
|
|
328
|
+
* The weight is calculated as follows:
|
|
329
|
+
*
|
|
330
|
+
* 1. The {@link String.length length} of the package name contributes a fixed
|
|
331
|
+
* value of `0x10000` per character. This is because the `pathCompare`
|
|
332
|
+
* algorithm first compares strings by length and only evaluates code unit
|
|
333
|
+
* values if the lengths of two strings are equal. `0x10000` is one (1)
|
|
334
|
+
* greater than the maximum value that {@link String.charCodeAt charCodeAt}
|
|
335
|
+
* can return (`0xFFFF`), which guarantees longer strings will have higher
|
|
336
|
+
* weights.
|
|
337
|
+
* 2. Each character in the package name contributes its UTF-16 code unit value
|
|
338
|
+
* (`0x0` thru `0xFFFF`) to the total. This is the same operation used when
|
|
339
|
+
* comparing two strings using comparison operators.
|
|
340
|
+
* 3. The total weight is the sum of 1. and 2.
|
|
341
|
+
*
|
|
342
|
+
* @param {string} packageName - Name of package to calculate weight for.
|
|
343
|
+
* @returns {number} Numeric weight
|
|
344
|
+
*/
|
|
345
|
+
const calculatePackageWeight = packageName => {
|
|
346
|
+
let totalCodeValue = packageName.length * 65536; // each character contributes 65536
|
|
347
|
+
for (let i = 0; i < packageName.length; i += 1) {
|
|
348
|
+
totalCodeValue += packageName.charCodeAt(i);
|
|
349
|
+
}
|
|
350
|
+
return totalCodeValue;
|
|
351
|
+
};
|
|
352
|
+
|
|
342
353
|
/**
|
|
343
354
|
* `graphPackage` and {@link gatherDependency} are mutually recursive functions that
|
|
344
355
|
* gather the metadata for a package and its transitive dependencies.
|
|
@@ -348,7 +359,7 @@ const inferParsers = (descriptor, location, languageOptions) => {
|
|
|
348
359
|
* that the package exports.
|
|
349
360
|
*
|
|
350
361
|
* @param {string} name
|
|
351
|
-
* @param {
|
|
362
|
+
* @param {MaybeReadDescriptorFn} readDescriptor
|
|
352
363
|
* @param {CanonicalFn} canonical
|
|
353
364
|
* @param {Graph} graph
|
|
354
365
|
* @param {PackageDetails} packageDetails
|
|
@@ -356,7 +367,8 @@ const inferParsers = (descriptor, location, languageOptions) => {
|
|
|
356
367
|
* @param {boolean | undefined} dev
|
|
357
368
|
* @param {LanguageOptions} languageOptions
|
|
358
369
|
* @param {boolean} strict
|
|
359
|
-
* @param {
|
|
370
|
+
* @param {LogicalPathGraph} logicalPathGraph
|
|
371
|
+
* @param {GraphPackageOptions} [options]
|
|
360
372
|
* @returns {Promise<undefined>}
|
|
361
373
|
*/
|
|
362
374
|
const graphPackage = async (
|
|
@@ -369,21 +381,10 @@ const graphPackage = async (
|
|
|
369
381
|
dev,
|
|
370
382
|
languageOptions,
|
|
371
383
|
strict,
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
preferredPackageLogicalPathMap = new Map(),
|
|
375
|
-
logicalPath = [],
|
|
376
|
-
log = noop,
|
|
377
|
-
} = {},
|
|
384
|
+
logicalPathGraph,
|
|
385
|
+
{ commonDependencyDescriptors = {}, logicalPath = [], log = noop } = {},
|
|
378
386
|
) => {
|
|
379
387
|
if (graph[packageLocation] !== undefined) {
|
|
380
|
-
updateShortestPaths(
|
|
381
|
-
graph,
|
|
382
|
-
packageLocation,
|
|
383
|
-
logicalPath,
|
|
384
|
-
preferredPackageLogicalPathMap,
|
|
385
|
-
);
|
|
386
|
-
|
|
387
388
|
// Returning the promise here would create a causal cycle and stall recursion.
|
|
388
389
|
return undefined;
|
|
389
390
|
}
|
|
@@ -447,20 +448,20 @@ const graphPackage = async (
|
|
|
447
448
|
// use the peerDependenciesMeta field (because there was no way to define
|
|
448
449
|
// an "optional" peerDependency prior to npm v7). this is plainly wrong,
|
|
449
450
|
// but not exactly rare, either
|
|
450
|
-
for (const [
|
|
451
|
+
for (const [dependencyName, meta] of entries(peerDependenciesMeta)) {
|
|
451
452
|
if (Object(meta) === meta && meta.optional) {
|
|
452
|
-
optionals.add(
|
|
453
|
-
allDependencies.add(
|
|
453
|
+
optionals.add(dependencyName);
|
|
454
|
+
allDependencies.add(dependencyName);
|
|
454
455
|
}
|
|
455
456
|
}
|
|
456
457
|
|
|
457
|
-
for (const
|
|
458
|
-
optionals.add(
|
|
458
|
+
for (const dependencyName of keys(optionalDependencies)) {
|
|
459
|
+
optionals.add(dependencyName);
|
|
459
460
|
}
|
|
460
461
|
|
|
461
|
-
for (const
|
|
462
|
-
const optional = optionals.has(
|
|
463
|
-
const childLogicalPath = [...logicalPath,
|
|
462
|
+
for (const dependencyName of [...allDependencies].sort()) {
|
|
463
|
+
const optional = optionals.has(dependencyName);
|
|
464
|
+
const childLogicalPath = [...logicalPath, dependencyName];
|
|
464
465
|
children.push(
|
|
465
466
|
// Mutual recursion ahead:
|
|
466
467
|
// eslint-disable-next-line no-use-before-define
|
|
@@ -470,11 +471,11 @@ const graphPackage = async (
|
|
|
470
471
|
graph,
|
|
471
472
|
dependencyLocations,
|
|
472
473
|
packageLocation,
|
|
473
|
-
|
|
474
|
+
dependencyName,
|
|
474
475
|
conditions,
|
|
475
|
-
preferredPackageLogicalPathMap,
|
|
476
476
|
languageOptions,
|
|
477
477
|
strict,
|
|
478
|
+
logicalPathGraph,
|
|
478
479
|
{
|
|
479
480
|
childLogicalPath,
|
|
480
481
|
optional,
|
|
@@ -579,17 +580,17 @@ const graphPackage = async (
|
|
|
579
580
|
/**
|
|
580
581
|
* Adds information for the dependency of the package at `packageLocation` to the `graph` object.
|
|
581
582
|
*
|
|
582
|
-
* @param {
|
|
583
|
+
* @param {MaybeReadDescriptorFn} readDescriptor
|
|
583
584
|
* @param {CanonicalFn} canonical
|
|
584
585
|
* @param {Graph} graph - the partially build graph.
|
|
585
586
|
* @param {Record<string, string>} dependencyLocations
|
|
586
|
-
* @param {
|
|
587
|
+
* @param {FileUrlString} packageLocation - location of the package of interest.
|
|
587
588
|
* @param {string} name - name of the package of interest.
|
|
588
589
|
* @param {Set<string>} conditions
|
|
589
|
-
* @param {Map<string, Array<string>>} preferredPackageLogicalPathMap
|
|
590
590
|
* @param {LanguageOptions} languageOptions
|
|
591
591
|
* @param {boolean} strict - If `true`, a missing dependency will throw an exception
|
|
592
|
-
* @param {
|
|
592
|
+
* @param {LogicalPathGraph} logicalPathGraph
|
|
593
|
+
* @param {GatherDependencyOptions} [options]
|
|
593
594
|
* @returns {Promise<void>}
|
|
594
595
|
*/
|
|
595
596
|
const gatherDependency = async (
|
|
@@ -600,9 +601,9 @@ const gatherDependency = async (
|
|
|
600
601
|
packageLocation,
|
|
601
602
|
name,
|
|
602
603
|
conditions,
|
|
603
|
-
preferredPackageLogicalPathMap,
|
|
604
604
|
languageOptions,
|
|
605
605
|
strict,
|
|
606
|
+
logicalPathGraph,
|
|
606
607
|
{
|
|
607
608
|
childLogicalPath = [],
|
|
608
609
|
optional = false,
|
|
@@ -625,19 +626,12 @@ const gatherDependency = async (
|
|
|
625
626
|
}
|
|
626
627
|
dependencyLocations[name] = dependency.packageLocation;
|
|
627
628
|
|
|
628
|
-
|
|
629
|
-
|
|
629
|
+
logicalPathGraph.addEdge(
|
|
630
|
+
packageLocation,
|
|
630
631
|
dependency.packageLocation,
|
|
631
|
-
|
|
632
|
+
calculatePackageWeight(name),
|
|
632
633
|
);
|
|
633
634
|
|
|
634
|
-
if (bestLogicalPath === childLogicalPath) {
|
|
635
|
-
preferredPackageLogicalPathMap.set(
|
|
636
|
-
dependency.packageLocation,
|
|
637
|
-
bestLogicalPath,
|
|
638
|
-
);
|
|
639
|
-
}
|
|
640
|
-
|
|
641
635
|
await graphPackage(
|
|
642
636
|
name,
|
|
643
637
|
readDescriptor,
|
|
@@ -648,9 +642,9 @@ const gatherDependency = async (
|
|
|
648
642
|
false,
|
|
649
643
|
languageOptions,
|
|
650
644
|
strict,
|
|
645
|
+
logicalPathGraph,
|
|
651
646
|
{
|
|
652
647
|
commonDependencyDescriptors,
|
|
653
|
-
preferredPackageLogicalPathMap,
|
|
654
648
|
logicalPath: childLogicalPath,
|
|
655
649
|
log,
|
|
656
650
|
},
|
|
@@ -663,7 +657,7 @@ const gatherDependency = async (
|
|
|
663
657
|
*
|
|
664
658
|
* @param {MaybeReadFn} maybeRead
|
|
665
659
|
* @param {CanonicalFn} canonical
|
|
666
|
-
* @param {
|
|
660
|
+
* @param {FileUrlString} packageLocation - location of the main package.
|
|
667
661
|
* @param {Set<string>} conditions
|
|
668
662
|
* @param {PackageDescriptor} mainPackageDescriptor - the parsed contents of the
|
|
669
663
|
* main `package.json`, which was already read when searching for the
|
|
@@ -674,7 +668,9 @@ const gatherDependency = async (
|
|
|
674
668
|
* to all packages
|
|
675
669
|
* @param {LanguageOptions} languageOptions
|
|
676
670
|
* @param {boolean} strict
|
|
677
|
-
* @param {
|
|
671
|
+
* @param {LogicalPathGraph} logicalPathGraph
|
|
672
|
+
* @param {GraphPackagesOptions} [options]
|
|
673
|
+
* @returns {Promise<Graph>}
|
|
678
674
|
*/
|
|
679
675
|
const graphPackages = async (
|
|
680
676
|
maybeRead,
|
|
@@ -686,12 +682,12 @@ const graphPackages = async (
|
|
|
686
682
|
commonDependencies,
|
|
687
683
|
languageOptions,
|
|
688
684
|
strict,
|
|
685
|
+
logicalPathGraph,
|
|
689
686
|
{ log = noop } = {},
|
|
690
687
|
) => {
|
|
691
688
|
const memo = create(null);
|
|
692
689
|
/**
|
|
693
|
-
* @
|
|
694
|
-
* @returns {Promise<PackageDescriptor>}
|
|
690
|
+
* @type {MaybeReadDescriptorFn}
|
|
695
691
|
*/
|
|
696
692
|
const readDescriptor = packageLocation =>
|
|
697
693
|
readDescriptorWithMemo(memo, maybeRead, packageLocation);
|
|
@@ -700,19 +696,22 @@ const graphPackages = async (
|
|
|
700
696
|
memo[packageLocation] = Promise.resolve(mainPackageDescriptor);
|
|
701
697
|
}
|
|
702
698
|
|
|
703
|
-
const
|
|
699
|
+
const allegedPackageDescriptor = await readDescriptor(packageLocation);
|
|
700
|
+
|
|
701
|
+
if (allegedPackageDescriptor === undefined) {
|
|
702
|
+
throw TypeError(
|
|
703
|
+
`Cannot find package.json for application at ${packageLocation}`,
|
|
704
|
+
);
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
assertPackageDescriptor(allegedPackageDescriptor);
|
|
708
|
+
const packageDescriptor = allegedPackageDescriptor;
|
|
704
709
|
|
|
705
710
|
conditions = new Set(conditions || []);
|
|
706
711
|
conditions.add('import');
|
|
707
712
|
conditions.add('default');
|
|
708
713
|
conditions.add('endo');
|
|
709
714
|
|
|
710
|
-
if (packageDescriptor === undefined) {
|
|
711
|
-
throw Error(
|
|
712
|
-
`Cannot find package.json for application at ${packageLocation}`,
|
|
713
|
-
);
|
|
714
|
-
}
|
|
715
|
-
|
|
716
715
|
// Resolve common dependencies.
|
|
717
716
|
/** @type {CommonDependencyDescriptors} */
|
|
718
717
|
const commonDependencyDescriptors = {};
|
|
@@ -730,6 +729,8 @@ const graphPackages = async (
|
|
|
730
729
|
};
|
|
731
730
|
}
|
|
732
731
|
|
|
732
|
+
logicalPathGraph.addNode(packageLocation);
|
|
733
|
+
|
|
733
734
|
const graph = create(null);
|
|
734
735
|
await graphPackage(
|
|
735
736
|
packageDescriptor.name,
|
|
@@ -744,6 +745,7 @@ const graphPackages = async (
|
|
|
744
745
|
dev,
|
|
745
746
|
languageOptions,
|
|
746
747
|
strict,
|
|
748
|
+
logicalPathGraph,
|
|
747
749
|
{
|
|
748
750
|
commonDependencyDescriptors,
|
|
749
751
|
log,
|
|
@@ -974,8 +976,8 @@ const makeLanguageOptions = ({
|
|
|
974
976
|
};
|
|
975
977
|
|
|
976
978
|
/**
|
|
977
|
-
* @param {ReadFn | ReadPowers | MaybeReadPowers} readPowers
|
|
978
|
-
* @param {
|
|
979
|
+
* @param {ReadFn | ReadPowers<FileUrlString> | MaybeReadPowers<FileUrlString>} readPowers
|
|
980
|
+
* @param {FileUrlString} packageLocation
|
|
979
981
|
* @param {Set<string>} conditionsOption
|
|
980
982
|
* @param {PackageDescriptor} packageDescriptor
|
|
981
983
|
* @param {string} moduleSpecifier
|
|
@@ -1003,10 +1005,18 @@ export const compartmentMapForNodeModules = async (
|
|
|
1003
1005
|
|
|
1004
1006
|
const conditions = new Set(conditionsOption || []);
|
|
1005
1007
|
|
|
1008
|
+
/**
|
|
1009
|
+
* This graph will contain nodes for each package location (a
|
|
1010
|
+
* {@link FileUrlString}) and edges representing dependencies between packages.
|
|
1011
|
+
*
|
|
1012
|
+
* The edges are weighted by {@link calculatePackageWeight}.
|
|
1013
|
+
*
|
|
1014
|
+
* @type {LogicalPathGraph}
|
|
1015
|
+
*/
|
|
1016
|
+
const logicalPathGraph = new GenericGraph();
|
|
1017
|
+
|
|
1006
1018
|
// dev is only set for the entry package, and implied by the development
|
|
1007
1019
|
// condition.
|
|
1008
|
-
// The dev option is deprecated in favor of using conditions, since that
|
|
1009
|
-
// covers more intentional behaviors of the development mode.
|
|
1010
1020
|
|
|
1011
1021
|
const graph = await graphPackages(
|
|
1012
1022
|
maybeRead,
|
|
@@ -1018,6 +1028,7 @@ export const compartmentMapForNodeModules = async (
|
|
|
1018
1028
|
commonDependencies,
|
|
1019
1029
|
languageOptions,
|
|
1020
1030
|
strict,
|
|
1031
|
+
logicalPathGraph,
|
|
1021
1032
|
{ log },
|
|
1022
1033
|
);
|
|
1023
1034
|
|
|
@@ -1037,6 +1048,27 @@ export const compartmentMapForNodeModules = async (
|
|
|
1037
1048
|
};
|
|
1038
1049
|
}
|
|
1039
1050
|
|
|
1051
|
+
const shortestPath = makeShortestPath(logicalPathGraph);
|
|
1052
|
+
// neither the entry package nor the attenuators compartment have a path; omit
|
|
1053
|
+
const {
|
|
1054
|
+
[ATTENUATORS_COMPARTMENT]: _,
|
|
1055
|
+
[packageLocation]: __,
|
|
1056
|
+
...subgraph
|
|
1057
|
+
} = graph;
|
|
1058
|
+
|
|
1059
|
+
for (const [location, node] of entries(subgraph)) {
|
|
1060
|
+
const shortestLogicalPath = shortestPath(
|
|
1061
|
+
packageLocation,
|
|
1062
|
+
// entries() loses some type information
|
|
1063
|
+
/** @type {FileUrlString} */ (location),
|
|
1064
|
+
);
|
|
1065
|
+
|
|
1066
|
+
// the first element will always be the root package location; this is omitted from the path.
|
|
1067
|
+
shortestLogicalPath.shift();
|
|
1068
|
+
node.path = shortestLogicalPath.map(location => graph[location].name);
|
|
1069
|
+
log(`Canonical name for package at ${location}: ${node.path.join('>')}`);
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1040
1072
|
const compartmentMap = translateGraph(
|
|
1041
1073
|
packageLocation,
|
|
1042
1074
|
moduleSpecifier,
|
|
@@ -1054,7 +1086,7 @@ export const compartmentMapForNodeModules = async (
|
|
|
1054
1086
|
*
|
|
1055
1087
|
* Locates the {@link PackageDescriptor} for the module at `moduleLocation`
|
|
1056
1088
|
*
|
|
1057
|
-
* @param {ReadFn | ReadPowers | MaybeReadPowers} readPowers
|
|
1089
|
+
* @param {ReadFn | ReadPowers<FileUrlString> | MaybeReadPowers<FileUrlString>} readPowers
|
|
1058
1090
|
* @param {string} moduleLocation
|
|
1059
1091
|
* @param {MapNodeModulesOptions} [options]
|
|
1060
1092
|
* @returns {Promise<CompartmentMapDescriptor>}
|
|
@@ -1071,9 +1103,12 @@ export const mapNodeModules = async (
|
|
|
1071
1103
|
moduleSpecifier,
|
|
1072
1104
|
} = await search(readPowers, moduleLocation, { log });
|
|
1073
1105
|
|
|
1074
|
-
const packageDescriptor = /** @type {
|
|
1075
|
-
parseLocatedJson
|
|
1076
|
-
);
|
|
1106
|
+
const packageDescriptor = /** @type {typeof parseLocatedJson<unknown>} */ (
|
|
1107
|
+
parseLocatedJson
|
|
1108
|
+
)(packageDescriptorText, packageDescriptorLocation);
|
|
1109
|
+
|
|
1110
|
+
assertPackageDescriptor(packageDescriptor);
|
|
1111
|
+
assertFileUrlString(packageLocation);
|
|
1077
1112
|
|
|
1078
1113
|
return compartmentMapForNodeModules(
|
|
1079
1114
|
readPowers,
|
package/src/node-powers.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ export function makeReadNowPowers({ fs, url, crypto, path, }: {
|
|
|
3
3
|
url?: UrlInterface | undefined;
|
|
4
4
|
crypto?: CryptoInterface | undefined;
|
|
5
5
|
path?: PathInterface | undefined;
|
|
6
|
-
}):
|
|
6
|
+
}): ReadNowPowers<FileUrlString>;
|
|
7
7
|
/**
|
|
8
8
|
* The implementation of `makeReadPowers` and the deprecated
|
|
9
9
|
* `makeNodeReadPowers` handles the case when the `url` power is not provided,
|
|
@@ -14,14 +14,14 @@ export function makeReadNowPowers({ fs, url, crypto, path, }: {
|
|
|
14
14
|
* @param {UrlInterface} [args.url]
|
|
15
15
|
* @param {CryptoInterface} [args.crypto]
|
|
16
16
|
* @param {PathInterface} [args.path]
|
|
17
|
-
* @returns {MaybeReadPowers}
|
|
17
|
+
* @returns {MaybeReadPowers<FileUrlString>}
|
|
18
18
|
*/
|
|
19
19
|
export function makeReadPowers({ fs, url, crypto, path, }: {
|
|
20
20
|
fs: FsInterface;
|
|
21
21
|
url?: UrlInterface | undefined;
|
|
22
22
|
crypto?: CryptoInterface | undefined;
|
|
23
23
|
path?: PathInterface | undefined;
|
|
24
|
-
}): MaybeReadPowers
|
|
24
|
+
}): MaybeReadPowers<FileUrlString>;
|
|
25
25
|
/**
|
|
26
26
|
* The implementation of `makeWritePowers` and the deprecated
|
|
27
27
|
* `makeNodeWritePowers` handles the case when the `url` power is not provided,
|
|
@@ -37,14 +37,15 @@ export function makeWritePowers({ fs, url }: {
|
|
|
37
37
|
}): {
|
|
38
38
|
write: (location: string, data: Uint8Array) => Promise<void>;
|
|
39
39
|
};
|
|
40
|
-
export function makeNodeReadPowers(fs: FsInterface, crypto?: CryptoInterface): ReadPowers
|
|
40
|
+
export function makeNodeReadPowers(fs: FsInterface, crypto?: CryptoInterface): ReadPowers<FileUrlString>;
|
|
41
41
|
export function makeNodeWritePowers(fs: FsInterface): WritePowers;
|
|
42
42
|
import type { FsInterface } from './types/node-powers.js';
|
|
43
43
|
import type { UrlInterface } from './types/node-powers.js';
|
|
44
44
|
import type { CryptoInterface } from './types/node-powers.js';
|
|
45
45
|
import type { PathInterface } from './types/node-powers.js';
|
|
46
|
-
import type {
|
|
46
|
+
import type { FileUrlString } from './types/external.js';
|
|
47
47
|
import type { ReadNowPowers } from './types/powers.js';
|
|
48
|
+
import type { MaybeReadPowers } from './types/powers.js';
|
|
48
49
|
import type { ReadPowers } from './types/powers.js';
|
|
49
50
|
import type { WritePowers } from './types/powers.js';
|
|
50
51
|
//# sourceMappingURL=node-powers.d.ts.map
|
package/src/node-powers.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"node-powers.d.ts","sourceRoot":"","sources":["node-powers.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"node-powers.d.ts","sourceRoot":"","sources":["node-powers.js"],"names":[],"mappings":"AA+LO,8DANJ;IAA0B,EAAE,EAApB,WAAW;IACS,GAAG;IACA,MAAM;IACR,IAAI;CACjC,GAAU,cAAc,aAAa,CAAC,CAoCxC;AApKD;;;;;;;;;;;GAWG;AACH,2DANG;IAA0B,EAAE,EAApB,WAAW;IACS,GAAG;IACA,MAAM;IACR,IAAI;CACjC,GAAU,gBAAgB,aAAa,CAAC,CA4G1C;AAgDD;;;;;;;;GAQG;AACH,6CAHG;IAA0B,EAAE,EAApB,WAAW;IACS,GAAG;CAAC;sBAOtB,MAAM,QACN,UAAU;EAYtB;AA2BM,uCALI,WAAW,WACX,eAAe,GACb,WAAW,aAAa,CAAC,CAKrC;AAWM,wCAJI,WAAW,GACT,WAAW,CAKvB;iCAvRS,wBAAwB;kCAAxB,wBAAwB;qCAAxB,wBAAwB;mCAAxB,wBAAwB;mCACF,qBAAqB;mCAe3C,mBAAmB;qCAAnB,mBAAmB;gCAAnB,mBAAmB;iCAAnB,mBAAmB"}
|