@endo/compartment-mapper 1.6.2 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +12 -16
- package/src/archive-lite.d.ts +7 -7
- package/src/archive-lite.d.ts.map +1 -1
- package/src/archive-lite.js +78 -27
- package/src/archive.d.ts.map +1 -1
- package/src/archive.js +7 -0
- package/src/bundle-lite.d.ts +3 -3
- package/src/bundle-lite.d.ts.map +1 -1
- package/src/bundle-lite.js +19 -24
- package/src/bundle.d.ts +3 -3
- package/src/bundle.d.ts.map +1 -1
- package/src/bundle.js +19 -24
- package/src/capture-lite.d.ts +2 -2
- package/src/capture-lite.d.ts.map +1 -1
- package/src/capture-lite.js +217 -25
- package/src/compartment-map.d.ts +9 -2
- package/src/compartment-map.d.ts.map +1 -1
- package/src/compartment-map.js +737 -254
- package/src/digest.d.ts +22 -2
- package/src/digest.d.ts.map +1 -1
- package/src/digest.js +179 -56
- package/src/generic-graph.d.ts +84 -0
- package/src/generic-graph.d.ts.map +1 -0
- package/src/generic-graph.js +356 -0
- package/src/guards.d.ts +18 -0
- package/src/guards.d.ts.map +1 -0
- package/src/guards.js +109 -0
- package/src/hooks.md +124 -0
- package/src/import-archive-lite.d.ts.map +1 -1
- package/src/import-archive-lite.js +15 -11
- package/src/import-archive.d.ts +5 -19
- package/src/import-archive.d.ts.map +1 -1
- package/src/import-archive.js +7 -27
- package/src/import-hook.d.ts +4 -3
- package/src/import-hook.d.ts.map +1 -1
- package/src/import-hook.js +156 -71
- package/src/import-lite.d.ts +6 -6
- package/src/import-lite.d.ts.map +1 -1
- package/src/import-lite.js +8 -5
- package/src/import.d.ts +3 -3
- package/src/import.d.ts.map +1 -1
- package/src/import.js +16 -6
- package/src/infer-exports.d.ts.map +1 -1
- package/src/infer-exports.js +16 -6
- package/src/json.d.ts +1 -1
- package/src/json.d.ts.map +1 -1
- package/src/json.js +10 -3
- package/src/link.d.ts +4 -3
- package/src/link.d.ts.map +1 -1
- package/src/link.js +70 -58
- package/src/node-modules.d.ts +5 -3
- package/src/node-modules.d.ts.map +1 -1
- package/src/node-modules.js +648 -245
- 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/parse-cjs-shared-export-wrapper.d.ts.map +1 -1
- package/src/parse-cjs-shared-export-wrapper.js +3 -1
- package/src/policy-format.d.ts +22 -5
- package/src/policy-format.d.ts.map +1 -1
- package/src/policy-format.js +342 -108
- package/src/policy.d.ts +13 -28
- package/src/policy.d.ts.map +1 -1
- package/src/policy.js +161 -106
- package/src/types/canonical-name.d.ts +97 -0
- package/src/types/canonical-name.d.ts.map +1 -0
- package/src/types/canonical-name.ts +151 -0
- package/src/types/compartment-map-schema.d.ts +114 -35
- package/src/types/compartment-map-schema.d.ts.map +1 -1
- package/src/types/compartment-map-schema.ts +202 -37
- package/src/types/external.d.ts +173 -29
- package/src/types/external.d.ts.map +1 -1
- package/src/types/external.ts +221 -27
- 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 +24 -42
- package/src/types/internal.d.ts.map +1 -1
- package/src/types/internal.ts +52 -50
- package/src/types/node-modules.d.ts +101 -17
- package/src/types/node-modules.d.ts.map +1 -1
- package/src/types/node-modules.ts +142 -17
- package/src/types/policy-schema.d.ts +26 -11
- package/src/types/policy-schema.d.ts.map +1 -1
- package/src/types/policy-schema.ts +29 -16
- package/src/types/policy.d.ts +6 -2
- package/src/types/policy.d.ts.map +1 -1
- package/src/types/policy.ts +7 -2
- 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/types/typescript.d.ts +28 -0
- package/src/types/typescript.d.ts.map +1 -1
- package/src/types/typescript.ts +37 -1
package/src/node-modules.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/* eslint-disable no-underscore-dangle */
|
|
1
2
|
/**
|
|
2
3
|
* Provides functions for constructing a compartment map that has a
|
|
3
4
|
* compartment descriptor corresponding to every reachable package from an
|
|
@@ -13,22 +14,45 @@
|
|
|
13
14
|
|
|
14
15
|
/* eslint no-shadow: 0 */
|
|
15
16
|
|
|
17
|
+
import { inferExportsAndAliases } from './infer-exports.js';
|
|
18
|
+
import { parseLocatedJson } from './json.js';
|
|
19
|
+
import { join } from './node-module-specifier.js';
|
|
20
|
+
import {
|
|
21
|
+
assertPolicy,
|
|
22
|
+
ATTENUATORS_COMPARTMENT,
|
|
23
|
+
ENTRY_COMPARTMENT,
|
|
24
|
+
generateCanonicalName,
|
|
25
|
+
} from './policy-format.js';
|
|
26
|
+
import { dependencyAllowedByPolicy, makePackagePolicy } from './policy.js';
|
|
27
|
+
import { unpackReadPowers } from './powers.js';
|
|
28
|
+
import { search, searchDescriptor } from './search.js';
|
|
29
|
+
import { GenericGraph, makeShortestPath } from './generic-graph.js';
|
|
30
|
+
|
|
16
31
|
/**
|
|
17
32
|
* @import {
|
|
18
33
|
* CanonicalFn,
|
|
19
34
|
* CompartmentDescriptor,
|
|
20
35
|
* CompartmentMapDescriptor,
|
|
21
36
|
* CompartmentMapForNodeModulesOptions,
|
|
37
|
+
* FileUrlString,
|
|
22
38
|
* LanguageForExtension,
|
|
23
39
|
* MapNodeModulesOptions,
|
|
40
|
+
* MaybeReadDescriptorFn,
|
|
24
41
|
* MaybeReadFn,
|
|
25
42
|
* MaybeReadPowers,
|
|
26
43
|
* PackageDescriptor,
|
|
27
|
-
* ReadDescriptorFn,
|
|
28
44
|
* ReadFn,
|
|
29
45
|
* ReadPowers,
|
|
30
|
-
* SomePackagePolicy,
|
|
31
46
|
* SomePolicy,
|
|
47
|
+
* LogFn,
|
|
48
|
+
* CompartmentModuleConfiguration,
|
|
49
|
+
* PackageCompartmentDescriptor,
|
|
50
|
+
* PackageCompartmentMapDescriptor,
|
|
51
|
+
* ScopeDescriptor,
|
|
52
|
+
* CanonicalName,
|
|
53
|
+
* SomePackagePolicy,
|
|
54
|
+
* PackageCompartmentDescriptorName,
|
|
55
|
+
* PackageData,
|
|
32
56
|
* } from './types.js'
|
|
33
57
|
* @import {
|
|
34
58
|
* Graph,
|
|
@@ -38,29 +62,21 @@
|
|
|
38
62
|
* GatherDependencyOptions,
|
|
39
63
|
* GraphPackageOptions,
|
|
40
64
|
* GraphPackagesOptions,
|
|
65
|
+
* LogicalPathGraph,
|
|
41
66
|
* PackageDetails,
|
|
67
|
+
* FinalGraph,
|
|
68
|
+
* CanonicalNameMap,
|
|
69
|
+
* FinalNode,
|
|
70
|
+
TranslateGraphOptions,
|
|
42
71
|
* } from './types/node-modules.js'
|
|
43
72
|
*/
|
|
44
73
|
|
|
45
|
-
|
|
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
|
-
const { assign, create, keys, values, entries } = Object;
|
|
74
|
+
const { assign, create, keys, values, entries, freeze } = Object;
|
|
59
75
|
|
|
60
76
|
const decoder = new TextDecoder();
|
|
61
77
|
|
|
62
78
|
// q, as in quote, for enquoting strings in error messages.
|
|
63
|
-
const q =
|
|
79
|
+
const { quote: q } = assert;
|
|
64
80
|
|
|
65
81
|
/**
|
|
66
82
|
* Default logger that does nothing.
|
|
@@ -68,14 +84,103 @@ const q = JSON.stringify;
|
|
|
68
84
|
const noop = () => {};
|
|
69
85
|
|
|
70
86
|
/**
|
|
87
|
+
* Default handler for unknown canonical names found in policy.
|
|
88
|
+
* Logs a warning when a canonical name from policy is not found in the compartment map.
|
|
89
|
+
*
|
|
90
|
+
* @param {object} params
|
|
91
|
+
* @param {CanonicalName} params.canonicalName
|
|
92
|
+
* @param {string} params.message
|
|
93
|
+
* @param {LogFn} params.log
|
|
94
|
+
*/
|
|
95
|
+
const defaultUnknownCanonicalNameHandler = ({
|
|
96
|
+
canonicalName,
|
|
97
|
+
message,
|
|
98
|
+
log,
|
|
99
|
+
}) => {
|
|
100
|
+
log(`WARN: Invalid resource ${q(canonicalName)} in policy: ${message}`);
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Default filter for package dependencies based on policy.
|
|
105
|
+
* Filters out dependencies not allowed by the package policy.
|
|
106
|
+
*
|
|
107
|
+
* **Note:** This filter is _only_ applied if a policy is provided.
|
|
108
|
+
*
|
|
109
|
+
* @param {object} params - The parameters object
|
|
110
|
+
* @param {CanonicalName} params.canonicalName - The canonical name of the package
|
|
111
|
+
* @param {Readonly<Set<CanonicalName>>} params.dependencies - The set of dependencies
|
|
112
|
+
* @param {LogFn} params.log - The logging function
|
|
113
|
+
* @param {SomePolicy} policy - The policy to check against
|
|
114
|
+
* @returns {Partial<{ dependencies: Set<CanonicalName> }> | void}
|
|
115
|
+
*/
|
|
116
|
+
const prePackageDependenciesFilter = (
|
|
117
|
+
{ canonicalName, dependencies, log },
|
|
118
|
+
policy,
|
|
119
|
+
) => {
|
|
120
|
+
const packagePolicy = makePackagePolicy(canonicalName, { policy });
|
|
121
|
+
if (!packagePolicy) {
|
|
122
|
+
return { dependencies };
|
|
123
|
+
}
|
|
124
|
+
const filteredDependencies = new Set(
|
|
125
|
+
[...dependencies].filter(dependency => {
|
|
126
|
+
const allowed = dependencyAllowedByPolicy(dependency, packagePolicy);
|
|
127
|
+
if (!allowed) {
|
|
128
|
+
log(
|
|
129
|
+
`Excluding dependency ${q(dependency)} of package ${q(canonicalName)} per policy`,
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
return allowed;
|
|
133
|
+
}),
|
|
134
|
+
);
|
|
135
|
+
|
|
136
|
+
return { dependencies: filteredDependencies };
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Given a relative path andd URL, return a fully qualified URL string.
|
|
141
|
+
*
|
|
142
|
+
* @overload
|
|
71
143
|
* @param {string} rel - a relative URL
|
|
72
|
-
* @param {
|
|
73
|
-
* @returns {string}
|
|
144
|
+
* @param {URL} abs - a fully qualified URL
|
|
145
|
+
* @returns {string} Fully qualified URL string
|
|
146
|
+
*/
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Given a relative path and fully qualified stringlike URL, return a fully
|
|
150
|
+
* qualified stringlike URL.
|
|
151
|
+
*
|
|
152
|
+
* @template {string} [T=string] Type of fully qualified URL string
|
|
153
|
+
* @overload
|
|
154
|
+
* @param {string} rel - a relative URL
|
|
155
|
+
* @param {T} abs - a fully qualified URL
|
|
156
|
+
* @returns {T} Fully qualified stringlike URL
|
|
157
|
+
*/
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* @param {string} rel - a relative URL
|
|
161
|
+
* @param {string|URL} abs - a fully qualified URL
|
|
74
162
|
*/
|
|
75
163
|
const resolveLocation = (rel, abs) => {
|
|
76
164
|
return new URL(rel, abs).toString();
|
|
77
165
|
};
|
|
78
166
|
|
|
167
|
+
/**
|
|
168
|
+
* Ensures a string is a file URL (a {@link FileUrlString})
|
|
169
|
+
*
|
|
170
|
+
* @param {unknown} allegedPackageLocation - a package location to assert
|
|
171
|
+
* @returns {asserts allegedPackageLocation is FileUrlString}
|
|
172
|
+
*/
|
|
173
|
+
const assertFileUrlString = allegedPackageLocation => {
|
|
174
|
+
assert(
|
|
175
|
+
typeof allegedPackageLocation === 'string',
|
|
176
|
+
`Package location must be a string, got ${q(allegedPackageLocation)}`,
|
|
177
|
+
);
|
|
178
|
+
assert(
|
|
179
|
+
allegedPackageLocation.startsWith('file://'),
|
|
180
|
+
`Package location must be a file URL, got ${q(allegedPackageLocation)}`,
|
|
181
|
+
);
|
|
182
|
+
};
|
|
183
|
+
|
|
79
184
|
// Exported for testing:
|
|
80
185
|
/**
|
|
81
186
|
* @param {string} location
|
|
@@ -93,10 +198,28 @@ export const basename = location => {
|
|
|
93
198
|
return pathname.slice(index + 1);
|
|
94
199
|
};
|
|
95
200
|
|
|
201
|
+
/**
|
|
202
|
+
* Asserts that the given value is a `PackageDescriptor`.
|
|
203
|
+
*
|
|
204
|
+
* TODO: This only validates that the value is a plain object. As mentioned in
|
|
205
|
+
* {@link PackageDescriptor}, `name` is currently a required field, but in the
|
|
206
|
+
* real world this is not so. We _do_ make assumptions about the shape of a
|
|
207
|
+
* `PackageDescriptor`, but it may not be worth eagerly validating further.
|
|
208
|
+
* @param {unknown} allegedPackageDescriptor
|
|
209
|
+
* @returns {asserts allegedPackageDescriptor is PackageDescriptor}
|
|
210
|
+
*/
|
|
211
|
+
const assertPackageDescriptor = allegedPackageDescriptor => {
|
|
212
|
+
assert(
|
|
213
|
+
typeof allegedPackageDescriptor !== 'function' &&
|
|
214
|
+
Object(allegedPackageDescriptor) === allegedPackageDescriptor,
|
|
215
|
+
`Package descriptor must be a plain object, got ${q(allegedPackageDescriptor)}`,
|
|
216
|
+
);
|
|
217
|
+
};
|
|
218
|
+
|
|
96
219
|
/**
|
|
97
220
|
* @param {MaybeReadFn} maybeRead
|
|
98
221
|
* @param {string} packageLocation
|
|
99
|
-
* @returns {Promise<
|
|
222
|
+
* @returns {Promise<PackageDescriptor|undefined>}
|
|
100
223
|
*/
|
|
101
224
|
const readDescriptor = async (maybeRead, packageLocation) => {
|
|
102
225
|
const descriptorLocation = resolveLocation('package.json', packageLocation);
|
|
@@ -106,16 +229,20 @@ const readDescriptor = async (maybeRead, packageLocation) => {
|
|
|
106
229
|
}
|
|
107
230
|
const descriptorText = decoder.decode(descriptorBytes);
|
|
108
231
|
const descriptor = parseLocatedJson(descriptorText, descriptorLocation);
|
|
232
|
+
assertPackageDescriptor(descriptor);
|
|
109
233
|
return descriptor;
|
|
110
234
|
};
|
|
111
235
|
|
|
112
236
|
/**
|
|
113
|
-
*
|
|
237
|
+
* Memoized {@link readDescriptor}
|
|
238
|
+
*
|
|
239
|
+
* @param {Record<string, Promise<PackageDescriptor|undefined>>} memo
|
|
114
240
|
* @param {MaybeReadFn} maybeRead
|
|
115
241
|
* @param {string} packageLocation
|
|
116
|
-
* @returns {Promise<
|
|
242
|
+
* @returns {Promise<PackageDescriptor|undefined>}
|
|
117
243
|
*/
|
|
118
244
|
const readDescriptorWithMemo = async (memo, maybeRead, packageLocation) => {
|
|
245
|
+
/** @type {Promise<PackageDescriptor|undefined>} */
|
|
119
246
|
let promise = memo[packageLocation];
|
|
120
247
|
if (promise !== undefined) {
|
|
121
248
|
return promise;
|
|
@@ -125,90 +252,6 @@ const readDescriptorWithMemo = async (memo, maybeRead, packageLocation) => {
|
|
|
125
252
|
return promise;
|
|
126
253
|
};
|
|
127
254
|
|
|
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
255
|
/**
|
|
213
256
|
* `findPackage` behaves as Node.js to find third-party modules by searching
|
|
214
257
|
* parent to ancestor directories for a `node_modules` directory that contains
|
|
@@ -218,9 +261,9 @@ const updateShortestPaths = (
|
|
|
218
261
|
* these are the locations that package managers drop a package so Node.js can
|
|
219
262
|
* find it efficiently.
|
|
220
263
|
*
|
|
221
|
-
* @param {
|
|
264
|
+
* @param {MaybeReadDescriptorFn} readDescriptor
|
|
222
265
|
* @param {CanonicalFn} canonical
|
|
223
|
-
* @param {
|
|
266
|
+
* @param {FileUrlString} directory
|
|
224
267
|
* @param {string} name
|
|
225
268
|
* @returns {Promise<PackageDetails|undefined>}
|
|
226
269
|
*/
|
|
@@ -232,6 +275,10 @@ const findPackage = async (readDescriptor, canonical, directory, name) => {
|
|
|
232
275
|
resolveLocation(`node_modules/${name}/`, directory),
|
|
233
276
|
);
|
|
234
277
|
|
|
278
|
+
// We have no guarantee that `canonical` will return a file URL; it spits
|
|
279
|
+
// back whatever we give it if `fs.promises.realpath()` rejects.
|
|
280
|
+
assertFileUrlString(packageLocation);
|
|
281
|
+
|
|
235
282
|
// eslint-disable-next-line no-await-in-loop
|
|
236
283
|
const packageDescriptor = await readDescriptor(packageLocation);
|
|
237
284
|
if (packageDescriptor !== undefined) {
|
|
@@ -339,6 +386,37 @@ const inferParsers = (descriptor, location, languageOptions) => {
|
|
|
339
386
|
return { ...commonjsLanguageForExtension, ...packageLanguageForExtension };
|
|
340
387
|
};
|
|
341
388
|
|
|
389
|
+
/**
|
|
390
|
+
* This returns the "weight" of a package name, which is used when determining
|
|
391
|
+
* the shortest path.
|
|
392
|
+
*
|
|
393
|
+
* It is an analogue of the `pathCompare` function.
|
|
394
|
+
*
|
|
395
|
+
* The weight is calculated as follows:
|
|
396
|
+
*
|
|
397
|
+
* 1. The {@link String.length length} of the package name contributes a fixed
|
|
398
|
+
* value of `0x10000` per character. This is because the `pathCompare`
|
|
399
|
+
* algorithm first compares strings by length and only evaluates code unit
|
|
400
|
+
* values if the lengths of two strings are equal. `0x10000` is one (1)
|
|
401
|
+
* greater than the maximum value that {@link String.charCodeAt charCodeAt}
|
|
402
|
+
* can return (`0xFFFF`), which guarantees longer strings will have higher
|
|
403
|
+
* weights.
|
|
404
|
+
* 2. Each character in the package name contributes its UTF-16 code unit value
|
|
405
|
+
* (`0x0` thru `0xFFFF`) to the total. This is the same operation used when
|
|
406
|
+
* comparing two strings using comparison operators.
|
|
407
|
+
* 3. The total weight is the sum of 1. and 2.
|
|
408
|
+
*
|
|
409
|
+
* @param {string} packageName - Name of package to calculate weight for.
|
|
410
|
+
* @returns {number} Numeric weight
|
|
411
|
+
*/
|
|
412
|
+
const calculatePackageWeight = packageName => {
|
|
413
|
+
let totalCodeValue = packageName.length * 65536; // each character contributes 65536
|
|
414
|
+
for (let i = 0; i < packageName.length; i += 1) {
|
|
415
|
+
totalCodeValue += packageName.charCodeAt(i);
|
|
416
|
+
}
|
|
417
|
+
return totalCodeValue;
|
|
418
|
+
};
|
|
419
|
+
|
|
342
420
|
/**
|
|
343
421
|
* `graphPackage` and {@link gatherDependency} are mutually recursive functions that
|
|
344
422
|
* gather the metadata for a package and its transitive dependencies.
|
|
@@ -348,7 +426,7 @@ const inferParsers = (descriptor, location, languageOptions) => {
|
|
|
348
426
|
* that the package exports.
|
|
349
427
|
*
|
|
350
428
|
* @param {string} name
|
|
351
|
-
* @param {
|
|
429
|
+
* @param {MaybeReadDescriptorFn} readDescriptor
|
|
352
430
|
* @param {CanonicalFn} canonical
|
|
353
431
|
* @param {Graph} graph
|
|
354
432
|
* @param {PackageDetails} packageDetails
|
|
@@ -356,6 +434,7 @@ const inferParsers = (descriptor, location, languageOptions) => {
|
|
|
356
434
|
* @param {boolean | undefined} dev
|
|
357
435
|
* @param {LanguageOptions} languageOptions
|
|
358
436
|
* @param {boolean} strict
|
|
437
|
+
* @param {LogicalPathGraph} logicalPathGraph
|
|
359
438
|
* @param {GraphPackageOptions} options
|
|
360
439
|
* @returns {Promise<undefined>}
|
|
361
440
|
*/
|
|
@@ -369,21 +448,15 @@ const graphPackage = async (
|
|
|
369
448
|
dev,
|
|
370
449
|
languageOptions,
|
|
371
450
|
strict,
|
|
451
|
+
logicalPathGraph,
|
|
372
452
|
{
|
|
373
453
|
commonDependencyDescriptors = {},
|
|
374
|
-
preferredPackageLogicalPathMap = new Map(),
|
|
375
|
-
logicalPath = [],
|
|
376
454
|
log = noop,
|
|
455
|
+
packageDependenciesHook,
|
|
456
|
+
policy,
|
|
377
457
|
} = {},
|
|
378
458
|
) => {
|
|
379
459
|
if (graph[packageLocation] !== undefined) {
|
|
380
|
-
updateShortestPaths(
|
|
381
|
-
graph,
|
|
382
|
-
packageLocation,
|
|
383
|
-
logicalPath,
|
|
384
|
-
preferredPackageLogicalPathMap,
|
|
385
|
-
);
|
|
386
|
-
|
|
387
460
|
// Returning the promise here would create a causal cycle and stall recursion.
|
|
388
461
|
return undefined;
|
|
389
462
|
}
|
|
@@ -396,7 +469,7 @@ const graphPackage = async (
|
|
|
396
469
|
});
|
|
397
470
|
}
|
|
398
471
|
|
|
399
|
-
const result = /** @type {Node} */ ({});
|
|
472
|
+
const result = /** @type {Node} */ ({ location: packageLocation });
|
|
400
473
|
graph[packageLocation] = result;
|
|
401
474
|
|
|
402
475
|
/** @type {Node['dependencyLocations']} */
|
|
@@ -447,20 +520,19 @@ const graphPackage = async (
|
|
|
447
520
|
// use the peerDependenciesMeta field (because there was no way to define
|
|
448
521
|
// an "optional" peerDependency prior to npm v7). this is plainly wrong,
|
|
449
522
|
// but not exactly rare, either
|
|
450
|
-
for (const [
|
|
523
|
+
for (const [dependencyName, meta] of entries(peerDependenciesMeta)) {
|
|
451
524
|
if (Object(meta) === meta && meta.optional) {
|
|
452
|
-
optionals.add(
|
|
453
|
-
allDependencies.add(
|
|
525
|
+
optionals.add(dependencyName);
|
|
526
|
+
allDependencies.add(dependencyName);
|
|
454
527
|
}
|
|
455
528
|
}
|
|
456
529
|
|
|
457
|
-
for (const
|
|
458
|
-
optionals.add(
|
|
530
|
+
for (const dependencyName of keys(optionalDependencies)) {
|
|
531
|
+
optionals.add(dependencyName);
|
|
459
532
|
}
|
|
460
533
|
|
|
461
|
-
for (const
|
|
462
|
-
const optional = optionals.has(
|
|
463
|
-
const childLogicalPath = [...logicalPath, name];
|
|
534
|
+
for (const dependencyName of [...allDependencies].sort()) {
|
|
535
|
+
const optional = optionals.has(dependencyName);
|
|
464
536
|
children.push(
|
|
465
537
|
// Mutual recursion ahead:
|
|
466
538
|
// eslint-disable-next-line no-use-before-define
|
|
@@ -470,16 +542,17 @@ const graphPackage = async (
|
|
|
470
542
|
graph,
|
|
471
543
|
dependencyLocations,
|
|
472
544
|
packageLocation,
|
|
473
|
-
|
|
545
|
+
dependencyName,
|
|
474
546
|
conditions,
|
|
475
|
-
preferredPackageLogicalPathMap,
|
|
476
547
|
languageOptions,
|
|
477
548
|
strict,
|
|
549
|
+
logicalPathGraph,
|
|
478
550
|
{
|
|
479
|
-
childLogicalPath,
|
|
480
551
|
optional,
|
|
481
552
|
commonDependencyDescriptors,
|
|
482
553
|
log,
|
|
554
|
+
packageDependenciesHook,
|
|
555
|
+
policy,
|
|
483
556
|
},
|
|
484
557
|
),
|
|
485
558
|
);
|
|
@@ -521,9 +594,9 @@ const graphPackage = async (
|
|
|
521
594
|
|
|
522
595
|
const sourceDirname = basename(packageLocation);
|
|
523
596
|
|
|
524
|
-
|
|
597
|
+
/** @type {Partial<Node>} */
|
|
598
|
+
const partialNode = {
|
|
525
599
|
name,
|
|
526
|
-
path: logicalPath,
|
|
527
600
|
label: `${name}${version ? `-v${version}` : ''}`,
|
|
528
601
|
sourceDirname,
|
|
529
602
|
explicitExports: exportsDescriptor !== undefined,
|
|
@@ -532,7 +605,9 @@ const graphPackage = async (
|
|
|
532
605
|
dependencyLocations,
|
|
533
606
|
types,
|
|
534
607
|
parsers,
|
|
535
|
-
|
|
608
|
+
packageDescriptor,
|
|
609
|
+
};
|
|
610
|
+
assign(result, partialNode);
|
|
536
611
|
|
|
537
612
|
await Promise.all(
|
|
538
613
|
values(result.externalAliases).map(async item => {
|
|
@@ -579,17 +654,17 @@ const graphPackage = async (
|
|
|
579
654
|
/**
|
|
580
655
|
* Adds information for the dependency of the package at `packageLocation` to the `graph` object.
|
|
581
656
|
*
|
|
582
|
-
* @param {
|
|
657
|
+
* @param {MaybeReadDescriptorFn} readDescriptor
|
|
583
658
|
* @param {CanonicalFn} canonical
|
|
584
659
|
* @param {Graph} graph - the partially build graph.
|
|
585
660
|
* @param {Record<string, string>} dependencyLocations
|
|
586
|
-
* @param {
|
|
661
|
+
* @param {FileUrlString} packageLocation - location of the package of interest.
|
|
587
662
|
* @param {string} name - name of the package of interest.
|
|
588
663
|
* @param {Set<string>} conditions
|
|
589
|
-
* @param {Map<string, Array<string>>} preferredPackageLogicalPathMap
|
|
590
664
|
* @param {LanguageOptions} languageOptions
|
|
591
665
|
* @param {boolean} strict - If `true`, a missing dependency will throw an exception
|
|
592
|
-
* @param {
|
|
666
|
+
* @param {LogicalPathGraph} logicalPathGraph
|
|
667
|
+
* @param {GatherDependencyOptions} [options]
|
|
593
668
|
* @returns {Promise<void>}
|
|
594
669
|
*/
|
|
595
670
|
const gatherDependency = async (
|
|
@@ -600,14 +675,15 @@ const gatherDependency = async (
|
|
|
600
675
|
packageLocation,
|
|
601
676
|
name,
|
|
602
677
|
conditions,
|
|
603
|
-
preferredPackageLogicalPathMap,
|
|
604
678
|
languageOptions,
|
|
605
679
|
strict,
|
|
680
|
+
logicalPathGraph,
|
|
606
681
|
{
|
|
607
|
-
childLogicalPath = [],
|
|
608
682
|
optional = false,
|
|
609
683
|
commonDependencyDescriptors = {},
|
|
610
684
|
log = noop,
|
|
685
|
+
packageDependenciesHook,
|
|
686
|
+
policy,
|
|
611
687
|
} = {},
|
|
612
688
|
) => {
|
|
613
689
|
const dependency = await findPackage(
|
|
@@ -616,6 +692,7 @@ const gatherDependency = async (
|
|
|
616
692
|
packageLocation,
|
|
617
693
|
name,
|
|
618
694
|
);
|
|
695
|
+
|
|
619
696
|
if (dependency === undefined) {
|
|
620
697
|
// allow the dependency to be missing if optional
|
|
621
698
|
if (optional || !strict) {
|
|
@@ -623,21 +700,15 @@ const gatherDependency = async (
|
|
|
623
700
|
}
|
|
624
701
|
throw Error(`Cannot find dependency ${name} for ${packageLocation}`);
|
|
625
702
|
}
|
|
703
|
+
|
|
626
704
|
dependencyLocations[name] = dependency.packageLocation;
|
|
627
705
|
|
|
628
|
-
|
|
629
|
-
|
|
706
|
+
logicalPathGraph.addEdge(
|
|
707
|
+
packageLocation,
|
|
630
708
|
dependency.packageLocation,
|
|
631
|
-
|
|
709
|
+
calculatePackageWeight(name),
|
|
632
710
|
);
|
|
633
711
|
|
|
634
|
-
if (bestLogicalPath === childLogicalPath) {
|
|
635
|
-
preferredPackageLogicalPathMap.set(
|
|
636
|
-
dependency.packageLocation,
|
|
637
|
-
bestLogicalPath,
|
|
638
|
-
);
|
|
639
|
-
}
|
|
640
|
-
|
|
641
712
|
await graphPackage(
|
|
642
713
|
name,
|
|
643
714
|
readDescriptor,
|
|
@@ -648,11 +719,12 @@ const gatherDependency = async (
|
|
|
648
719
|
false,
|
|
649
720
|
languageOptions,
|
|
650
721
|
strict,
|
|
722
|
+
logicalPathGraph,
|
|
651
723
|
{
|
|
652
724
|
commonDependencyDescriptors,
|
|
653
|
-
preferredPackageLogicalPathMap,
|
|
654
|
-
logicalPath: childLogicalPath,
|
|
655
725
|
log,
|
|
726
|
+
packageDependenciesHook,
|
|
727
|
+
policy,
|
|
656
728
|
},
|
|
657
729
|
);
|
|
658
730
|
};
|
|
@@ -663,7 +735,7 @@ const gatherDependency = async (
|
|
|
663
735
|
*
|
|
664
736
|
* @param {MaybeReadFn} maybeRead
|
|
665
737
|
* @param {CanonicalFn} canonical
|
|
666
|
-
* @param {
|
|
738
|
+
* @param {FileUrlString} packageLocation - location of the main package.
|
|
667
739
|
* @param {Set<string>} conditions
|
|
668
740
|
* @param {PackageDescriptor} mainPackageDescriptor - the parsed contents of the
|
|
669
741
|
* main `package.json`, which was already read when searching for the
|
|
@@ -674,7 +746,9 @@ const gatherDependency = async (
|
|
|
674
746
|
* to all packages
|
|
675
747
|
* @param {LanguageOptions} languageOptions
|
|
676
748
|
* @param {boolean} strict
|
|
749
|
+
* @param {LogicalPathGraph} logicalPathGraph
|
|
677
750
|
* @param {GraphPackagesOptions} options
|
|
751
|
+
* @returns {Promise<Graph>}
|
|
678
752
|
*/
|
|
679
753
|
const graphPackages = async (
|
|
680
754
|
maybeRead,
|
|
@@ -686,12 +760,12 @@ const graphPackages = async (
|
|
|
686
760
|
commonDependencies,
|
|
687
761
|
languageOptions,
|
|
688
762
|
strict,
|
|
689
|
-
|
|
763
|
+
logicalPathGraph,
|
|
764
|
+
{ log = noop, packageDependenciesHook, policy } = {},
|
|
690
765
|
) => {
|
|
691
766
|
const memo = create(null);
|
|
692
767
|
/**
|
|
693
|
-
* @
|
|
694
|
-
* @returns {Promise<PackageDescriptor>}
|
|
768
|
+
* @type {MaybeReadDescriptorFn}
|
|
695
769
|
*/
|
|
696
770
|
const readDescriptor = packageLocation =>
|
|
697
771
|
readDescriptorWithMemo(memo, maybeRead, packageLocation);
|
|
@@ -700,19 +774,22 @@ const graphPackages = async (
|
|
|
700
774
|
memo[packageLocation] = Promise.resolve(mainPackageDescriptor);
|
|
701
775
|
}
|
|
702
776
|
|
|
703
|
-
const
|
|
777
|
+
const allegedPackageDescriptor = await readDescriptor(packageLocation);
|
|
778
|
+
|
|
779
|
+
if (allegedPackageDescriptor === undefined) {
|
|
780
|
+
throw TypeError(
|
|
781
|
+
`Cannot find package.json for application at ${packageLocation}`,
|
|
782
|
+
);
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
assertPackageDescriptor(allegedPackageDescriptor);
|
|
786
|
+
const packageDescriptor = allegedPackageDescriptor;
|
|
704
787
|
|
|
705
788
|
conditions = new Set(conditions || []);
|
|
706
789
|
conditions.add('import');
|
|
707
790
|
conditions.add('default');
|
|
708
791
|
conditions.add('endo');
|
|
709
792
|
|
|
710
|
-
if (packageDescriptor === undefined) {
|
|
711
|
-
throw Error(
|
|
712
|
-
`Cannot find package.json for application at ${packageLocation}`,
|
|
713
|
-
);
|
|
714
|
-
}
|
|
715
|
-
|
|
716
793
|
// Resolve common dependencies.
|
|
717
794
|
/** @type {CommonDependencyDescriptors} */
|
|
718
795
|
const commonDependencyDescriptors = {};
|
|
@@ -730,6 +807,8 @@ const graphPackages = async (
|
|
|
730
807
|
};
|
|
731
808
|
}
|
|
732
809
|
|
|
810
|
+
logicalPathGraph.addNode(packageLocation);
|
|
811
|
+
|
|
733
812
|
const graph = create(null);
|
|
734
813
|
await graphPackage(
|
|
735
814
|
packageDescriptor.name,
|
|
@@ -744,9 +823,12 @@ const graphPackages = async (
|
|
|
744
823
|
dev,
|
|
745
824
|
languageOptions,
|
|
746
825
|
strict,
|
|
826
|
+
logicalPathGraph,
|
|
747
827
|
{
|
|
748
828
|
commonDependencyDescriptors,
|
|
749
829
|
log,
|
|
830
|
+
packageDependenciesHook,
|
|
831
|
+
policy,
|
|
750
832
|
},
|
|
751
833
|
);
|
|
752
834
|
return graph;
|
|
@@ -761,19 +843,101 @@ const graphPackages = async (
|
|
|
761
843
|
* @param {Graph} graph
|
|
762
844
|
* @param {Set<string>} conditions - build conditions about the target environment
|
|
763
845
|
* for selecting relevant exports, e.g., "browser" or "node".
|
|
764
|
-
* @param {
|
|
765
|
-
* @returns {
|
|
846
|
+
* @param {TranslateGraphOptions} [options]
|
|
847
|
+
* @returns {PackageCompartmentMapDescriptor}
|
|
766
848
|
*/
|
|
767
849
|
const translateGraph = (
|
|
768
850
|
entryPackageLocation,
|
|
769
851
|
entryModuleSpecifier,
|
|
770
852
|
graph,
|
|
771
853
|
conditions,
|
|
772
|
-
policy,
|
|
854
|
+
{ policy, log = noop, packageDependenciesHook } = {},
|
|
773
855
|
) => {
|
|
774
|
-
/** @type {
|
|
856
|
+
/** @type {Record<PackageCompartmentDescriptorName, PackageCompartmentDescriptor>} */
|
|
775
857
|
const compartments = create(null);
|
|
776
858
|
|
|
859
|
+
/**
|
|
860
|
+
* Execute package dependencies hooks: default first (if policy exists), then user-provided.
|
|
861
|
+
*
|
|
862
|
+
* @param {CanonicalName} label
|
|
863
|
+
* @param {Record<string, FileUrlString>} dependencyLocations
|
|
864
|
+
* @returns {Record<string, FileUrlString>}
|
|
865
|
+
*/
|
|
866
|
+
const executePackageDependenciesHook = (label, dependencyLocations) => {
|
|
867
|
+
const dependencies = new Set(
|
|
868
|
+
values(dependencyLocations).map(
|
|
869
|
+
dependencyLocation => graph[dependencyLocation].label,
|
|
870
|
+
),
|
|
871
|
+
);
|
|
872
|
+
|
|
873
|
+
const packageDependenciesHookInput = {
|
|
874
|
+
canonicalName: label,
|
|
875
|
+
dependencies: new Set(dependencies),
|
|
876
|
+
log,
|
|
877
|
+
};
|
|
878
|
+
|
|
879
|
+
// Call default filter first if policy exists
|
|
880
|
+
let packageDependenciesHookResult;
|
|
881
|
+
if (policy) {
|
|
882
|
+
packageDependenciesHookResult = prePackageDependenciesFilter(
|
|
883
|
+
packageDependenciesHookInput,
|
|
884
|
+
policy,
|
|
885
|
+
);
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
// Then call user-provided hook if it exists
|
|
889
|
+
if (packageDependenciesHook) {
|
|
890
|
+
const userResult = packageDependenciesHook(packageDependenciesHookInput);
|
|
891
|
+
// If user hook also returned a result, use it (overrides default)
|
|
892
|
+
if (userResult?.dependencies) {
|
|
893
|
+
packageDependenciesHookResult = userResult;
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
// if "dependencies" are in here, then something changed the list.
|
|
898
|
+
if (packageDependenciesHookResult?.dependencies) {
|
|
899
|
+
const size = packageDependenciesHookResult.dependencies.size;
|
|
900
|
+
if (typeof size === 'number' && size > 0) {
|
|
901
|
+
// because the list of dependencies contains canonical names, we need to lookup any new ones.
|
|
902
|
+
const nodesByCanonicalName = new Map(
|
|
903
|
+
entries(graph).map(([location, node]) => [
|
|
904
|
+
node.label,
|
|
905
|
+
{
|
|
906
|
+
...node,
|
|
907
|
+
packageLocation: /** @type {FileUrlString} */ (location),
|
|
908
|
+
},
|
|
909
|
+
]),
|
|
910
|
+
);
|
|
911
|
+
|
|
912
|
+
/** @type {typeof dependencyLocations} */
|
|
913
|
+
const newDependencyLocations = {};
|
|
914
|
+
try {
|
|
915
|
+
for (const label of packageDependenciesHookResult.dependencies) {
|
|
916
|
+
const { name, packageLocation } =
|
|
917
|
+
nodesByCanonicalName.get(label) ?? create(null);
|
|
918
|
+
if (name && packageLocation) {
|
|
919
|
+
newDependencyLocations[name] = packageLocation;
|
|
920
|
+
} else {
|
|
921
|
+
log(
|
|
922
|
+
`WARNING: packageDependencies hook returned unknown package with label ${q(label)}`,
|
|
923
|
+
);
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
return newDependencyLocations;
|
|
927
|
+
} catch {
|
|
928
|
+
log(
|
|
929
|
+
`WARNING: packageDependencies hook returned invalid value ${q(
|
|
930
|
+
packageDependenciesHookResult,
|
|
931
|
+
)}; using original dependencies`,
|
|
932
|
+
);
|
|
933
|
+
}
|
|
934
|
+
} else {
|
|
935
|
+
dependencyLocations = create(null);
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
return dependencyLocations;
|
|
939
|
+
};
|
|
940
|
+
|
|
777
941
|
// For each package, build a map of all the external modules the package can
|
|
778
942
|
// import from other packages.
|
|
779
943
|
// The keys of this map are the full specifiers of those modules from the
|
|
@@ -783,36 +947,24 @@ const translateGraph = (
|
|
|
783
947
|
// The full map includes every exported module from every dependencey
|
|
784
948
|
// package and is a complete list of every external module that the
|
|
785
949
|
// corresponding compartment can import.
|
|
786
|
-
for (const dependeeLocation of
|
|
950
|
+
for (const dependeeLocation of /** @type {PackageCompartmentDescriptorName[]} */ (
|
|
951
|
+
keys(graph).sort()
|
|
952
|
+
)) {
|
|
787
953
|
const {
|
|
788
954
|
name,
|
|
789
|
-
path,
|
|
790
955
|
label,
|
|
791
956
|
sourceDirname,
|
|
792
|
-
dependencyLocations,
|
|
793
957
|
internalAliases,
|
|
794
958
|
parsers,
|
|
795
959
|
types,
|
|
960
|
+
packageDescriptor,
|
|
796
961
|
} = graph[dependeeLocation];
|
|
797
|
-
/** @type {
|
|
962
|
+
/** @type {Record<string, CompartmentModuleConfiguration>} */
|
|
798
963
|
const moduleDescriptors = create(null);
|
|
799
|
-
/** @type {
|
|
964
|
+
/** @type {Record<string, ScopeDescriptor<PackageCompartmentDescriptorName>>} */
|
|
800
965
|
const scopes = create(null);
|
|
801
966
|
|
|
802
|
-
|
|
803
|
-
* List of all the compartments (by name) that this compartment can import from.
|
|
804
|
-
*
|
|
805
|
-
* @type {Set<string>}
|
|
806
|
-
*/
|
|
807
|
-
const compartmentNames = new Set();
|
|
808
|
-
const packagePolicy = getPolicyForPackage(
|
|
809
|
-
{
|
|
810
|
-
isEntry: dependeeLocation === entryPackageLocation,
|
|
811
|
-
name,
|
|
812
|
-
path,
|
|
813
|
-
},
|
|
814
|
-
policy,
|
|
815
|
-
);
|
|
967
|
+
const packagePolicy = makePackagePolicy(label, { policy });
|
|
816
968
|
|
|
817
969
|
/* c8 ignore next */
|
|
818
970
|
if (policy && !packagePolicy) {
|
|
@@ -820,34 +972,29 @@ const translateGraph = (
|
|
|
820
972
|
throw new TypeError('Unexpectedly falsy package policy');
|
|
821
973
|
}
|
|
822
974
|
|
|
975
|
+
let dependencyLocations = graph[dependeeLocation].dependencyLocations;
|
|
976
|
+
dependencyLocations = executePackageDependenciesHook(
|
|
977
|
+
label,
|
|
978
|
+
dependencyLocations,
|
|
979
|
+
);
|
|
980
|
+
|
|
823
981
|
/**
|
|
824
982
|
* @param {string} dependencyName
|
|
825
|
-
* @param {
|
|
983
|
+
* @param {PackageCompartmentDescriptorName} packageLocation
|
|
826
984
|
*/
|
|
827
985
|
const digestExternalAliases = (dependencyName, packageLocation) => {
|
|
828
|
-
const { externalAliases, explicitExports
|
|
829
|
-
graph[packageLocation];
|
|
986
|
+
const { externalAliases, explicitExports } = graph[packageLocation];
|
|
830
987
|
for (const exportPath of keys(externalAliases).sort()) {
|
|
831
988
|
const targetPath = externalAliases[exportPath];
|
|
832
989
|
// dependency name may be different from package's name,
|
|
833
|
-
// as in the case of browser field dependency replacements
|
|
990
|
+
// as in the case of browser field dependency replacements.
|
|
991
|
+
// note that policy still applies
|
|
834
992
|
const localPath = join(dependencyName, exportPath);
|
|
835
|
-
if
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
name,
|
|
841
|
-
path,
|
|
842
|
-
},
|
|
843
|
-
packagePolicy,
|
|
844
|
-
))
|
|
845
|
-
) {
|
|
846
|
-
moduleDescriptors[localPath] = {
|
|
847
|
-
compartment: packageLocation,
|
|
848
|
-
module: targetPath,
|
|
849
|
-
};
|
|
850
|
-
}
|
|
993
|
+
// if we have policy, this has already been vetted
|
|
994
|
+
moduleDescriptors[localPath] = {
|
|
995
|
+
compartment: packageLocation,
|
|
996
|
+
module: targetPath,
|
|
997
|
+
};
|
|
851
998
|
}
|
|
852
999
|
// if the exports field is not present, then all modules must be accessible
|
|
853
1000
|
if (!explicitExports) {
|
|
@@ -862,7 +1009,6 @@ const translateGraph = (
|
|
|
862
1009
|
for (const dependencyName of keys(dependencyLocations).sort()) {
|
|
863
1010
|
const dependencyLocation = dependencyLocations[dependencyName];
|
|
864
1011
|
digestExternalAliases(dependencyName, dependencyLocation);
|
|
865
|
-
compartmentNames.add(dependencyLocation);
|
|
866
1012
|
}
|
|
867
1013
|
// digest own internal aliases
|
|
868
1014
|
for (const modulePath of keys(internalAliases).sort()) {
|
|
@@ -879,9 +1025,9 @@ const translateGraph = (
|
|
|
879
1025
|
}
|
|
880
1026
|
|
|
881
1027
|
compartments[dependeeLocation] = {
|
|
1028
|
+
version: packageDescriptor.version ? packageDescriptor.version : '',
|
|
882
1029
|
label,
|
|
883
1030
|
name,
|
|
884
|
-
path,
|
|
885
1031
|
location: dependeeLocation,
|
|
886
1032
|
sourceDirname,
|
|
887
1033
|
modules: moduleDescriptors,
|
|
@@ -889,7 +1035,6 @@ const translateGraph = (
|
|
|
889
1035
|
parsers,
|
|
890
1036
|
types,
|
|
891
1037
|
policy: /** @type {SomePackagePolicy} */ (packagePolicy),
|
|
892
|
-
compartments: compartmentNames,
|
|
893
1038
|
};
|
|
894
1039
|
}
|
|
895
1040
|
|
|
@@ -898,7 +1043,7 @@ const translateGraph = (
|
|
|
898
1043
|
// https://github.com/endojs/endo/issues/2388
|
|
899
1044
|
tags: [...conditions],
|
|
900
1045
|
entry: {
|
|
901
|
-
compartment: entryPackageLocation,
|
|
1046
|
+
compartment: /** @type {FileUrlString} */ (entryPackageLocation),
|
|
902
1047
|
module: entryModuleSpecifier,
|
|
903
1048
|
},
|
|
904
1049
|
compartments,
|
|
@@ -972,23 +1117,200 @@ const makeLanguageOptions = ({
|
|
|
972
1117
|
workspaceModuleLanguageForExtension,
|
|
973
1118
|
};
|
|
974
1119
|
};
|
|
1120
|
+
/**
|
|
1121
|
+
* Creates a `Node` in `graph` corresponding to the "attenuators" Compartment.
|
|
1122
|
+
*
|
|
1123
|
+
* Only does so if `policy` is provided.
|
|
1124
|
+
*
|
|
1125
|
+
* @param {Graph} graph Graph
|
|
1126
|
+
* @param {Node} entryNode Entry node of the grpah
|
|
1127
|
+
* @param {SomePolicy} [policy]
|
|
1128
|
+
* @throws If there's already a `Node` in `graph` for the "attenuators"
|
|
1129
|
+
* Compartment
|
|
1130
|
+
* @returns {void}
|
|
1131
|
+
*/
|
|
1132
|
+
const makeAttenuatorsNode = (graph, entryNode, policy) => {
|
|
1133
|
+
if (policy) {
|
|
1134
|
+
assertPolicy(policy);
|
|
1135
|
+
|
|
1136
|
+
assert(
|
|
1137
|
+
graph[ATTENUATORS_COMPARTMENT] === undefined,
|
|
1138
|
+
`${q(ATTENUATORS_COMPARTMENT)} is a reserved compartment name`,
|
|
1139
|
+
);
|
|
1140
|
+
|
|
1141
|
+
graph[ATTENUATORS_COMPARTMENT] = {
|
|
1142
|
+
...entryNode,
|
|
1143
|
+
internalAliases: {},
|
|
1144
|
+
externalAliases: {},
|
|
1145
|
+
packageDescriptor: { name: ATTENUATORS_COMPARTMENT },
|
|
1146
|
+
name: ATTENUATORS_COMPARTMENT,
|
|
1147
|
+
};
|
|
1148
|
+
}
|
|
1149
|
+
};
|
|
975
1150
|
|
|
976
1151
|
/**
|
|
977
|
-
*
|
|
978
|
-
*
|
|
1152
|
+
* Transforms a `Graph` into a readonly `FinalGraph`, in preparation for
|
|
1153
|
+
* conversion to a `CompartmentDescriptor`.
|
|
1154
|
+
*
|
|
1155
|
+
* @param {Graph} graph Graph
|
|
1156
|
+
* @param {LogicalPathGraph} logicalPathGraph Logical path graph
|
|
1157
|
+
* @param {FileUrlString} entryPackageLocation Entry package location
|
|
1158
|
+
* @param {CanonicalNameMap} canonicalNameMap Mapping of canonical names to `Node` names (keys in `graph`)
|
|
1159
|
+
* @returns {Readonly<FinalGraph>}
|
|
1160
|
+
*/
|
|
1161
|
+
const finalizeGraph = (
|
|
1162
|
+
graph,
|
|
1163
|
+
logicalPathGraph,
|
|
1164
|
+
entryPackageLocation,
|
|
1165
|
+
canonicalNameMap,
|
|
1166
|
+
) => {
|
|
1167
|
+
const shortestPath = makeShortestPath(logicalPathGraph);
|
|
1168
|
+
|
|
1169
|
+
// neither the entry package nor the attenuators compartment have a path; omit
|
|
1170
|
+
const {
|
|
1171
|
+
[ATTENUATORS_COMPARTMENT]: attenuatorsNode,
|
|
1172
|
+
[entryPackageLocation]: entryNode,
|
|
1173
|
+
...subgraph
|
|
1174
|
+
} = graph;
|
|
1175
|
+
|
|
1176
|
+
/** @type {FinalGraph} */
|
|
1177
|
+
const finalGraph = create(null);
|
|
1178
|
+
|
|
1179
|
+
/** @type {Readonly<FinalNode>} */
|
|
1180
|
+
finalGraph[entryPackageLocation] = freeze({
|
|
1181
|
+
...entryNode,
|
|
1182
|
+
label: generateCanonicalName({
|
|
1183
|
+
isEntry: true,
|
|
1184
|
+
path: [],
|
|
1185
|
+
}),
|
|
1186
|
+
});
|
|
1187
|
+
|
|
1188
|
+
canonicalNameMap.set(ENTRY_COMPARTMENT, entryPackageLocation);
|
|
1189
|
+
|
|
1190
|
+
if (attenuatorsNode) {
|
|
1191
|
+
/** @type {Readonly<FinalNode>} */
|
|
1192
|
+
finalGraph[ATTENUATORS_COMPARTMENT] = freeze({
|
|
1193
|
+
...attenuatorsNode,
|
|
1194
|
+
label: generateCanonicalName({
|
|
1195
|
+
name: ATTENUATORS_COMPARTMENT,
|
|
1196
|
+
path: [],
|
|
1197
|
+
}),
|
|
1198
|
+
});
|
|
1199
|
+
}
|
|
1200
|
+
|
|
1201
|
+
const subgraphEntries = /** @type {[FileUrlString, Node][]} */ (
|
|
1202
|
+
entries(subgraph)
|
|
1203
|
+
);
|
|
1204
|
+
|
|
1205
|
+
for (const [location, node] of subgraphEntries) {
|
|
1206
|
+
const shortestLogicalPath = shortestPath(entryPackageLocation, location);
|
|
1207
|
+
|
|
1208
|
+
// the first element will always be the root package location; this is omitted from the path.
|
|
1209
|
+
shortestLogicalPath.shift();
|
|
1210
|
+
|
|
1211
|
+
const path = shortestLogicalPath.map(location => graph[location].name);
|
|
1212
|
+
const canonicalName = generateCanonicalName({ path });
|
|
1213
|
+
|
|
1214
|
+
/** @type {Readonly<FinalNode>} */
|
|
1215
|
+
const finalNode = freeze({
|
|
1216
|
+
...node,
|
|
1217
|
+
label: canonicalName,
|
|
1218
|
+
});
|
|
1219
|
+
|
|
1220
|
+
canonicalNameMap.set(canonicalName, location);
|
|
1221
|
+
|
|
1222
|
+
finalGraph[location] = finalNode;
|
|
1223
|
+
}
|
|
1224
|
+
|
|
1225
|
+
for (const node of values(finalGraph)) {
|
|
1226
|
+
Object.freeze(node);
|
|
1227
|
+
}
|
|
1228
|
+
|
|
1229
|
+
return freeze(finalGraph);
|
|
1230
|
+
};
|
|
1231
|
+
|
|
1232
|
+
/**
|
|
1233
|
+
* Returns an array of "issue" objects if any resources referenced in `policy`
|
|
1234
|
+
* are unknown.
|
|
1235
|
+
*
|
|
1236
|
+
* @param {Set<CanonicalName>} canonicalNames Set of all known canonical names
|
|
1237
|
+
* @param {SomePolicy} policy Policy to validate
|
|
1238
|
+
* @returns {Array<{canonicalName: CanonicalName, message: string, path:
|
|
1239
|
+
* string[], suggestion?: CanonicalName}>} Array of issue objects, or `undefined` if no issues were
|
|
1240
|
+
* found
|
|
1241
|
+
*/
|
|
1242
|
+
const validatePolicyResources = (canonicalNames, policy) => {
|
|
1243
|
+
/**
|
|
1244
|
+
* Finds a suggestion for `badName` if it is a suffix of any
|
|
1245
|
+
* canonical name in `canonicalNames`.
|
|
1246
|
+
*
|
|
1247
|
+
* @param {string} badName Unknown canonical name
|
|
1248
|
+
* @returns {CanonicalName | undefined}
|
|
1249
|
+
*/
|
|
1250
|
+
const findSuggestion = badName => {
|
|
1251
|
+
for (const canonicalName of canonicalNames) {
|
|
1252
|
+
if (canonicalName.endsWith(`>${badName}`)) {
|
|
1253
|
+
return canonicalName;
|
|
1254
|
+
}
|
|
1255
|
+
}
|
|
1256
|
+
return undefined;
|
|
1257
|
+
};
|
|
1258
|
+
|
|
1259
|
+
/** @type {Array<{canonicalName: CanonicalName, message: string, path: string[], suggestion?: CanonicalName}>} */
|
|
1260
|
+
const issues = [];
|
|
1261
|
+
for (const [resourceName, resourcePolicy] of entries(
|
|
1262
|
+
policy.resources ?? {},
|
|
1263
|
+
)) {
|
|
1264
|
+
if (!canonicalNames.has(resourceName)) {
|
|
1265
|
+
const issueMessage = `Resource ${q(resourceName)} was not found`;
|
|
1266
|
+
const suggestion = findSuggestion(resourceName);
|
|
1267
|
+
const issue = {
|
|
1268
|
+
canonicalName: resourceName,
|
|
1269
|
+
message: issueMessage,
|
|
1270
|
+
path: ['resources', resourceName],
|
|
1271
|
+
};
|
|
1272
|
+
if (suggestion) {
|
|
1273
|
+
issue.suggestion = suggestion;
|
|
1274
|
+
}
|
|
1275
|
+
issues.push(issue);
|
|
1276
|
+
}
|
|
1277
|
+
if (typeof resourcePolicy?.packages === 'object') {
|
|
1278
|
+
for (const packageName of keys(resourcePolicy.packages)) {
|
|
1279
|
+
if (!canonicalNames.has(packageName)) {
|
|
1280
|
+
const issueMessage = `Resource ${q(packageName)} from resource ${q(resourceName)} was not found`;
|
|
1281
|
+
const suggestion = findSuggestion(packageName);
|
|
1282
|
+
const issue = {
|
|
1283
|
+
canonicalName: packageName,
|
|
1284
|
+
message: issueMessage,
|
|
1285
|
+
path: ['resources', resourceName, 'packages', packageName],
|
|
1286
|
+
};
|
|
1287
|
+
if (suggestion) {
|
|
1288
|
+
issue.suggestion = suggestion;
|
|
1289
|
+
}
|
|
1290
|
+
issues.push(issue);
|
|
1291
|
+
}
|
|
1292
|
+
}
|
|
1293
|
+
}
|
|
1294
|
+
}
|
|
1295
|
+
|
|
1296
|
+
return issues;
|
|
1297
|
+
};
|
|
1298
|
+
|
|
1299
|
+
/**
|
|
1300
|
+
* @param {ReadFn | ReadPowers<FileUrlString> | MaybeReadPowers<FileUrlString>} readPowers
|
|
1301
|
+
* @param {FileUrlString} entryPackageLocation
|
|
979
1302
|
* @param {Set<string>} conditionsOption
|
|
980
1303
|
* @param {PackageDescriptor} packageDescriptor
|
|
981
|
-
* @param {string}
|
|
1304
|
+
* @param {string} entryModuleSpecifier
|
|
982
1305
|
* @param {CompartmentMapForNodeModulesOptions} [options]
|
|
983
|
-
* @returns {Promise<
|
|
984
|
-
* @deprecated Use {@link mapNodeModules} instead.
|
|
1306
|
+
* @returns {Promise<PackageCompartmentMapDescriptor>}
|
|
985
1307
|
*/
|
|
986
|
-
export const
|
|
1308
|
+
export const compartmentMapForNodeModules_ = async (
|
|
987
1309
|
readPowers,
|
|
988
|
-
|
|
1310
|
+
entryPackageLocation,
|
|
989
1311
|
conditionsOption,
|
|
990
1312
|
packageDescriptor,
|
|
991
|
-
|
|
1313
|
+
entryModuleSpecifier,
|
|
992
1314
|
options = {},
|
|
993
1315
|
) => {
|
|
994
1316
|
const {
|
|
@@ -997,52 +1319,109 @@ export const compartmentMapForNodeModules = async (
|
|
|
997
1319
|
policy,
|
|
998
1320
|
strict = false,
|
|
999
1321
|
log = noop,
|
|
1322
|
+
unknownCanonicalNameHook,
|
|
1323
|
+
packageDataHook,
|
|
1324
|
+
packageDependenciesHook,
|
|
1000
1325
|
} = options;
|
|
1001
1326
|
const { maybeRead, canonical } = unpackReadPowers(readPowers);
|
|
1002
1327
|
const languageOptions = makeLanguageOptions(options);
|
|
1003
1328
|
|
|
1004
1329
|
const conditions = new Set(conditionsOption || []);
|
|
1005
1330
|
|
|
1331
|
+
/**
|
|
1332
|
+
* This graph will contain nodes for each package location (a
|
|
1333
|
+
* {@link FileUrlString}) and edges representing dependencies between packages.
|
|
1334
|
+
*
|
|
1335
|
+
* The edges are weighted by {@link calculatePackageWeight}.
|
|
1336
|
+
*
|
|
1337
|
+
* @type {LogicalPathGraph}
|
|
1338
|
+
*/
|
|
1339
|
+
const logicalPathGraph = new GenericGraph();
|
|
1340
|
+
|
|
1006
1341
|
// dev is only set for the entry package, and implied by the development
|
|
1007
1342
|
// condition.
|
|
1008
|
-
// The dev option is deprecated in favor of using conditions, since that
|
|
1009
|
-
// covers more intentional behaviors of the development mode.
|
|
1010
1343
|
|
|
1011
1344
|
const graph = await graphPackages(
|
|
1012
1345
|
maybeRead,
|
|
1013
1346
|
canonical,
|
|
1014
|
-
|
|
1347
|
+
entryPackageLocation,
|
|
1015
1348
|
conditions,
|
|
1016
1349
|
packageDescriptor,
|
|
1017
1350
|
dev || (conditions && conditions.has('development')),
|
|
1018
1351
|
commonDependencies,
|
|
1019
1352
|
languageOptions,
|
|
1020
1353
|
strict,
|
|
1021
|
-
|
|
1354
|
+
logicalPathGraph,
|
|
1355
|
+
{ log, policy, packageDependenciesHook },
|
|
1022
1356
|
);
|
|
1023
1357
|
|
|
1024
|
-
|
|
1025
|
-
assertPolicy(policy);
|
|
1358
|
+
makeAttenuatorsNode(graph, graph[entryPackageLocation], policy);
|
|
1026
1359
|
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1360
|
+
/**
|
|
1361
|
+
* @type {CanonicalNameMap}
|
|
1362
|
+
*/
|
|
1363
|
+
const canonicalNameMap = new Map();
|
|
1031
1364
|
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1365
|
+
const finalGraph = finalizeGraph(
|
|
1366
|
+
graph,
|
|
1367
|
+
logicalPathGraph,
|
|
1368
|
+
entryPackageLocation,
|
|
1369
|
+
canonicalNameMap,
|
|
1370
|
+
);
|
|
1371
|
+
|
|
1372
|
+
// if policy exists, cross-reference the policy "resources" against the list
|
|
1373
|
+
// of known canonical names and fire the `unknownCanonicalName` hook for each
|
|
1374
|
+
// unknown resource, if found
|
|
1375
|
+
if (policy) {
|
|
1376
|
+
const canonicalNames = new Set(canonicalNameMap.keys());
|
|
1377
|
+
const issues = validatePolicyResources(canonicalNames, policy) ?? [];
|
|
1378
|
+
// Call default handler first if policy exists
|
|
1379
|
+
for (const { message, canonicalName, path, suggestion } of issues) {
|
|
1380
|
+
const hookInput = {
|
|
1381
|
+
canonicalName,
|
|
1382
|
+
message,
|
|
1383
|
+
path,
|
|
1384
|
+
log,
|
|
1385
|
+
};
|
|
1386
|
+
if (suggestion) {
|
|
1387
|
+
hookInput.suggestion = suggestion;
|
|
1388
|
+
}
|
|
1389
|
+
defaultUnknownCanonicalNameHandler(hookInput);
|
|
1390
|
+
// Then call user-provided hook if it exists
|
|
1391
|
+
if (unknownCanonicalNameHook) {
|
|
1392
|
+
unknownCanonicalNameHook(hookInput);
|
|
1393
|
+
}
|
|
1394
|
+
}
|
|
1395
|
+
}
|
|
1396
|
+
|
|
1397
|
+
// Fire packageData hook with all package data before translateGraph
|
|
1398
|
+
if (packageDataHook) {
|
|
1399
|
+
const packageData =
|
|
1400
|
+
/** @type {Map<PackageCompartmentDescriptorName, PackageData>} */ (
|
|
1401
|
+
new Map(
|
|
1402
|
+
values(finalGraph).map(node => [
|
|
1403
|
+
node.label,
|
|
1404
|
+
{
|
|
1405
|
+
name: node.name,
|
|
1406
|
+
packageDescriptor: node.packageDescriptor,
|
|
1407
|
+
location: node.location,
|
|
1408
|
+
canonicalName: node.label,
|
|
1409
|
+
},
|
|
1410
|
+
]),
|
|
1411
|
+
)
|
|
1412
|
+
);
|
|
1413
|
+
packageDataHook({
|
|
1414
|
+
packageData,
|
|
1415
|
+
log,
|
|
1416
|
+
});
|
|
1038
1417
|
}
|
|
1039
1418
|
|
|
1040
1419
|
const compartmentMap = translateGraph(
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1420
|
+
entryPackageLocation,
|
|
1421
|
+
entryModuleSpecifier,
|
|
1422
|
+
finalGraph,
|
|
1044
1423
|
conditions,
|
|
1045
|
-
policy,
|
|
1424
|
+
{ policy, log, packageDependenciesHook },
|
|
1046
1425
|
);
|
|
1047
1426
|
|
|
1048
1427
|
return compartmentMap;
|
|
@@ -1054,15 +1433,24 @@ export const compartmentMapForNodeModules = async (
|
|
|
1054
1433
|
*
|
|
1055
1434
|
* Locates the {@link PackageDescriptor} for the module at `moduleLocation`
|
|
1056
1435
|
*
|
|
1057
|
-
* @param {ReadFn | ReadPowers | MaybeReadPowers} readPowers
|
|
1436
|
+
* @param {ReadFn | ReadPowers<FileUrlString> | MaybeReadPowers<FileUrlString>} readPowers
|
|
1058
1437
|
* @param {string} moduleLocation
|
|
1059
1438
|
* @param {MapNodeModulesOptions} [options]
|
|
1060
|
-
* @returns {Promise<
|
|
1439
|
+
* @returns {Promise<PackageCompartmentMapDescriptor>}
|
|
1061
1440
|
*/
|
|
1062
1441
|
export const mapNodeModules = async (
|
|
1063
1442
|
readPowers,
|
|
1064
1443
|
moduleLocation,
|
|
1065
|
-
{
|
|
1444
|
+
{
|
|
1445
|
+
tags = new Set(),
|
|
1446
|
+
conditions = tags,
|
|
1447
|
+
log = noop,
|
|
1448
|
+
unknownCanonicalNameHook,
|
|
1449
|
+
packageDataHook,
|
|
1450
|
+
packageDependenciesHook,
|
|
1451
|
+
policy,
|
|
1452
|
+
...otherOptions
|
|
1453
|
+
} = {},
|
|
1066
1454
|
) => {
|
|
1067
1455
|
const {
|
|
1068
1456
|
packageLocation,
|
|
@@ -1071,16 +1459,31 @@ export const mapNodeModules = async (
|
|
|
1071
1459
|
moduleSpecifier,
|
|
1072
1460
|
} = await search(readPowers, moduleLocation, { log });
|
|
1073
1461
|
|
|
1074
|
-
const packageDescriptor = /** @type {
|
|
1075
|
-
parseLocatedJson
|
|
1076
|
-
);
|
|
1462
|
+
const packageDescriptor = /** @type {typeof parseLocatedJson<unknown>} */ (
|
|
1463
|
+
parseLocatedJson
|
|
1464
|
+
)(packageDescriptorText, packageDescriptorLocation);
|
|
1077
1465
|
|
|
1078
|
-
|
|
1466
|
+
assertPackageDescriptor(packageDescriptor);
|
|
1467
|
+
assertFileUrlString(packageLocation);
|
|
1468
|
+
|
|
1469
|
+
return compartmentMapForNodeModules_(
|
|
1079
1470
|
readPowers,
|
|
1080
1471
|
packageLocation,
|
|
1081
1472
|
conditions,
|
|
1082
1473
|
packageDescriptor,
|
|
1083
1474
|
moduleSpecifier,
|
|
1084
|
-
{
|
|
1475
|
+
{
|
|
1476
|
+
log,
|
|
1477
|
+
policy,
|
|
1478
|
+
unknownCanonicalNameHook,
|
|
1479
|
+
packageDependenciesHook,
|
|
1480
|
+
packageDataHook,
|
|
1481
|
+
...otherOptions,
|
|
1482
|
+
},
|
|
1085
1483
|
);
|
|
1086
1484
|
};
|
|
1485
|
+
|
|
1486
|
+
/**
|
|
1487
|
+
* @deprecated Use {@link mapNodeModules} instead.
|
|
1488
|
+
*/
|
|
1489
|
+
export const compartmentMapForNodeModules = compartmentMapForNodeModules_;
|