@endo/compartment-mapper 1.5.0 → 1.6.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.
Files changed (143) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +278 -111
  3. package/SECURITY.md +2 -2
  4. package/bundle.d.ts +1 -1
  5. package/bundle.js +4 -1
  6. package/functor-lite.d.ts +3 -0
  7. package/functor-lite.d.ts.map +1 -0
  8. package/functor-lite.js +4 -0
  9. package/functor.d.ts +3 -0
  10. package/functor.d.ts.map +1 -0
  11. package/functor.js +4 -0
  12. package/index.d.ts +1 -1
  13. package/index.js +4 -1
  14. package/package.json +15 -11
  15. package/script-lite.d.ts +3 -0
  16. package/script-lite.d.ts.map +1 -0
  17. package/script-lite.js +4 -0
  18. package/script.d.ts +3 -0
  19. package/script.d.ts.map +1 -0
  20. package/script.js +4 -0
  21. package/src/archive-lite.d.ts +5 -5
  22. package/src/archive-lite.d.ts.map +1 -1
  23. package/src/archive-lite.js +1 -1
  24. package/src/archive.d.ts +5 -5
  25. package/src/archive.d.ts.map +1 -1
  26. package/src/archive.js +3 -1
  27. package/src/bundle-cjs.d.ts +12 -2
  28. package/src/bundle-cjs.d.ts.map +1 -1
  29. package/src/bundle-cjs.js +57 -28
  30. package/src/bundle-json.d.ts.map +1 -1
  31. package/src/bundle-json.js +2 -3
  32. package/src/bundle-lite.d.ts +91 -0
  33. package/src/bundle-lite.d.ts.map +1 -0
  34. package/src/bundle-lite.js +667 -0
  35. package/src/bundle-mjs.d.ts +13 -3
  36. package/src/bundle-mjs.d.ts.map +1 -1
  37. package/src/bundle-mjs.js +36 -19
  38. package/src/bundle.d.ts +48 -10
  39. package/src/bundle.d.ts.map +1 -1
  40. package/src/bundle.js +392 -126
  41. package/src/capture-lite.d.ts +1 -1
  42. package/src/capture-lite.d.ts.map +1 -1
  43. package/src/capture-lite.js +4 -2
  44. package/src/compartment-map.d.ts +1 -1
  45. package/src/compartment-map.d.ts.map +1 -1
  46. package/src/import-archive-lite.d.ts +2 -2
  47. package/src/import-archive-lite.d.ts.map +1 -1
  48. package/src/import-archive-lite.js +3 -1
  49. package/src/import-archive.d.ts +3 -3
  50. package/src/import-archive.d.ts.map +1 -1
  51. package/src/import-archive.js +3 -1
  52. package/src/import-hook.d.ts +3 -16
  53. package/src/import-hook.d.ts.map +1 -1
  54. package/src/import-hook.js +214 -116
  55. package/src/import-lite.d.ts +1 -1
  56. package/src/import-lite.d.ts.map +1 -1
  57. package/src/import-lite.js +7 -3
  58. package/src/import.d.ts.map +1 -1
  59. package/src/import.js +3 -1
  60. package/src/infer-exports.d.ts +5 -7
  61. package/src/infer-exports.d.ts.map +1 -1
  62. package/src/infer-exports.js +23 -8
  63. package/src/link.d.ts.map +1 -1
  64. package/src/link.js +4 -1
  65. package/src/map-parser.d.ts.map +1 -1
  66. package/src/map-parser.js +51 -5
  67. package/src/node-module-specifier.d.ts.map +1 -1
  68. package/src/node-module-specifier.js +3 -1
  69. package/src/node-modules.d.ts +4 -47
  70. package/src/node-modules.d.ts.map +1 -1
  71. package/src/node-modules.js +267 -148
  72. package/src/node-powers.d.ts +1 -1
  73. package/src/node-powers.d.ts.map +1 -1
  74. package/src/node-powers.js +3 -1
  75. package/src/parse-archive-cjs.d.ts +5 -1
  76. package/src/parse-archive-cjs.d.ts.map +1 -1
  77. package/src/parse-archive-cjs.js +11 -4
  78. package/src/parse-archive-mjs.d.ts +5 -1
  79. package/src/parse-archive-mjs.d.ts.map +1 -1
  80. package/src/parse-archive-mjs.js +3 -1
  81. package/src/parse-bytes.d.ts +5 -1
  82. package/src/parse-bytes.d.ts.map +1 -1
  83. package/src/parse-bytes.js +3 -1
  84. package/src/parse-cjs-shared-export-wrapper.d.ts.map +1 -1
  85. package/src/parse-cjs-shared-export-wrapper.js +5 -11
  86. package/src/parse-cjs.d.ts +5 -1
  87. package/src/parse-cjs.d.ts.map +1 -1
  88. package/src/parse-cjs.js +4 -2
  89. package/src/parse-json.d.ts +5 -2
  90. package/src/parse-json.d.ts.map +1 -1
  91. package/src/parse-mjs.d.ts +5 -1
  92. package/src/parse-mjs.d.ts.map +1 -1
  93. package/src/parse-mjs.js +2 -2
  94. package/src/parse-pre-cjs.d.ts +5 -1
  95. package/src/parse-pre-cjs.d.ts.map +1 -1
  96. package/src/parse-pre-cjs.js +3 -1
  97. package/src/parse-pre-mjs.d.ts +5 -1
  98. package/src/parse-pre-mjs.d.ts.map +1 -1
  99. package/src/parse-pre-mjs.js +3 -1
  100. package/src/parse-text.d.ts +5 -1
  101. package/src/parse-text.d.ts.map +1 -1
  102. package/src/parse-text.js +3 -1
  103. package/src/policy-format.d.ts +2 -1
  104. package/src/policy-format.d.ts.map +1 -1
  105. package/src/policy-format.js +5 -2
  106. package/src/policy.d.ts +2 -2
  107. package/src/policy.d.ts.map +1 -1
  108. package/src/policy.js +10 -11
  109. package/src/powers.d.ts +1 -1
  110. package/src/powers.d.ts.map +1 -1
  111. package/src/powers.js +3 -1
  112. package/src/search.d.ts +7 -12
  113. package/src/search.d.ts.map +1 -1
  114. package/src/search.js +32 -13
  115. package/src/types/compartment-map-schema.d.ts +8 -1
  116. package/src/types/compartment-map-schema.d.ts.map +1 -1
  117. package/src/types/compartment-map-schema.ts +8 -1
  118. package/src/types/external.d.ts +127 -17
  119. package/src/types/external.d.ts.map +1 -1
  120. package/src/types/external.ts +142 -17
  121. package/src/types/internal.d.ts +116 -29
  122. package/src/types/internal.d.ts.map +1 -1
  123. package/src/types/internal.ts +144 -31
  124. package/src/types/node-modules.d.ts +79 -0
  125. package/src/types/node-modules.d.ts.map +1 -0
  126. package/src/types/node-modules.ts +89 -0
  127. package/src/types/node-powers.d.ts +7 -5
  128. package/src/types/node-powers.d.ts.map +1 -1
  129. package/src/types/node-powers.ts +7 -5
  130. package/src/types/policy-schema.d.ts +3 -1
  131. package/src/types/policy-schema.d.ts.map +1 -1
  132. package/src/types/policy-schema.ts +3 -1
  133. package/src/types/policy.d.ts +3 -1
  134. package/src/types/policy.d.ts.map +1 -1
  135. package/src/types/policy.ts +3 -1
  136. package/src/types/powers.d.ts +5 -3
  137. package/src/types/powers.d.ts.map +1 -1
  138. package/src/types/powers.ts +5 -3
  139. package/src/types/typescript.d.ts +3 -1
  140. package/src/types/typescript.d.ts.map +1 -1
  141. package/src/types/typescript.ts +3 -1
  142. package/src/url.d.ts.map +1 -1
  143. package/src/url.js +3 -1
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @module Provides functions for constructing a compartment map that has a
2
+ * Provides functions for constructing a compartment map that has a
3
3
  * compartment descriptor corresponding to every reachable package from an
4
4
  * entry module and how to create links between them.
5
5
  * The resulting compartment map does not describe individual modules but does
@@ -7,6 +7,8 @@
7
7
  * wildcard expansion.
8
8
  * See {@link link} to expand a compartment map to capture module descriptors
9
9
  * for transitive dependencies.
10
+ *
11
+ * @module
10
12
  */
11
13
 
12
14
  /* eslint no-shadow: 0 */
@@ -17,57 +19,27 @@
17
19
  * CompartmentDescriptor,
18
20
  * CompartmentMapDescriptor,
19
21
  * CompartmentMapForNodeModulesOptions,
20
- * Language,
21
22
  * LanguageForExtension,
22
23
  * MapNodeModulesOptions,
23
24
  * MaybeReadFn,
24
25
  * MaybeReadPowers,
25
- * ModuleDescriptor,
26
+ * PackageDescriptor,
27
+ * ReadDescriptorFn,
26
28
  * ReadFn,
27
29
  * ReadPowers,
28
- * ScopeDescriptor,
29
30
  * SomePackagePolicy,
30
31
  * SomePolicy,
31
32
  * } from './types.js'
32
- */
33
-
34
- /**
35
- * The graph is an intermediate object model that the functions of this module
36
- * build by exploring the `node_modules` tree dropped by tools like npm and
37
- * consumed by tools like Node.js.
38
- * This gets translated finally into a compartment map.
39
- *
40
- * @typedef {Record<string, Node>} Graph
41
- */
42
-
43
- /**
44
- * @typedef {object} Node
45
- * @property {string} label
46
- * @property {string} name
47
- * @property {Array<string>} path
48
- * @property {Array<string>} logicalPath
49
- * @property {boolean} explicitExports
50
- * @property {Record<string, string>} internalAliases
51
- * @property {Record<string, string>} externalAliases
52
- * @property {Record<string, string>} dependencyLocations - from module name to
53
- * location in storage.
54
- * @property {LanguageForExtension} parsers - the parser for
55
- * modules based on their extension.
56
- * @property {Record<string, Language>} types - the parser for specific
57
- * modules.
58
- */
59
-
60
- /**
61
- * @typedef {object} LanguageOptions
62
- * @property {LanguageForExtension} commonjsLanguageForExtension
63
- * @property {LanguageForExtension} moduleLanguageForExtension
64
- * @property {LanguageForExtension} workspaceCommonjsLanguageForExtension
65
- * @property {LanguageForExtension} workspaceModuleLanguageForExtension
66
- * @property {Set<string>} languages
67
- */
68
-
69
- /**
70
- * @typedef {Record<string, {spec: string, alias: string}>} CommonDependencyDescriptors
33
+ * @import {
34
+ * Graph,
35
+ * Node,
36
+ * LanguageOptions,
37
+ * CommonDependencyDescriptors,
38
+ * GatherDependencyOptions,
39
+ * GraphPackageOptions,
40
+ * GraphPackagesOptions,
41
+ * PackageDetails,
42
+ * } from './types/node-modules.js'
71
43
  */
72
44
 
73
45
  import { pathCompare } from './compartment-map.js';
@@ -83,13 +55,18 @@ import {
83
55
  import { unpackReadPowers } from './powers.js';
84
56
  import { search, searchDescriptor } from './search.js';
85
57
 
86
- const { assign, create, keys, values } = Object;
58
+ const { assign, create, keys, values, entries } = Object;
87
59
 
88
60
  const decoder = new TextDecoder();
89
61
 
90
62
  // q, as in quote, for enquoting strings in error messages.
91
63
  const q = JSON.stringify;
92
64
 
65
+ /**
66
+ * Default logger that does nothing.
67
+ */
68
+ const noop = () => {};
69
+
93
70
  /**
94
71
  * @param {string} rel - a relative URL
95
72
  * @param {string} abs - a fully qualified URL
@@ -99,12 +76,16 @@ const resolveLocation = (rel, abs) => {
99
76
  return new URL(rel, abs).toString();
100
77
  };
101
78
 
79
+ // Exported for testing:
102
80
  /**
103
81
  * @param {string} location
104
82
  * @returns {string}
105
83
  */
106
- const basename = location => {
107
- const { pathname } = new URL(location);
84
+ export const basename = location => {
85
+ let { pathname } = new URL(location);
86
+ if (pathname.endsWith('/')) {
87
+ pathname = pathname.slice(0, -1);
88
+ }
108
89
  const index = pathname.lastIndexOf('/');
109
90
  if (index < 0) {
110
91
  return pathname;
@@ -145,27 +126,103 @@ const readDescriptorWithMemo = async (memo, maybeRead, packageLocation) => {
145
126
  };
146
127
 
147
128
  /**
148
- * @callback ReadDescriptorFn
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
149
136
  * @param {string} packageLocation
150
- * @returns {Promise<object>}
137
+ * @param {Map<string, U>} preferredPackageLogicalPathMap
138
+ * @returns {T|U}
151
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
+ }
152
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
+ };
153
212
  /**
154
- * findPackage behaves as Node.js to find third-party modules by searching
213
+ * `findPackage` behaves as Node.js to find third-party modules by searching
155
214
  * parent to ancestor directories for a `node_modules` directory that contains
156
215
  * the name.
216
+ *
157
217
  * Node.js does not actually require these to be packages, but in practice,
158
- * these are the locations that pakcage managers drop a package so Node.js can
218
+ * these are the locations that package managers drop a package so Node.js can
159
219
  * find it efficiently.
160
220
  *
161
221
  * @param {ReadDescriptorFn} readDescriptor
162
222
  * @param {CanonicalFn} canonical
163
223
  * @param {string} directory
164
224
  * @param {string} name
165
- * @returns {Promise<{
166
- * packageLocation: string,
167
- * packageDescriptor: object,
168
- * } | undefined>}
225
+ * @returns {Promise<PackageDetails|undefined>}
169
226
  */
170
227
  const findPackage = async (readDescriptor, canonical, directory, name) => {
171
228
  await null;
@@ -198,6 +255,7 @@ const findPackage = async (readDescriptor, canonical, directory, name) => {
198
255
  }
199
256
  };
200
257
 
258
+ /** @satisfies {LanguageForExtension} */
201
259
  const defaultLanguageForExtension = /** @type {const} */ ({
202
260
  mjs: 'mjs',
203
261
  cjs: 'cjs',
@@ -205,15 +263,19 @@ const defaultLanguageForExtension = /** @type {const} */ ({
205
263
  text: 'text',
206
264
  bytes: 'bytes',
207
265
  });
266
+
267
+ /** @satisfies {LanguageForExtension} */
208
268
  const defaultCommonjsLanguageForExtension = /** @type {const} */ ({
209
269
  js: 'cjs',
210
270
  });
271
+
272
+ /** @satisfies {LanguageForExtension} */
211
273
  const defaultModuleLanguageForExtension = /** @type {const} */ ({
212
274
  js: 'mjs',
213
275
  });
214
276
 
215
277
  /**
216
- * @param {object} descriptor
278
+ * @param {PackageDescriptor} descriptor
217
279
  * @param {string} location
218
280
  * @param {LanguageOptions} languageOptions
219
281
  * @returns {Record<string, string>}
@@ -278,7 +340,7 @@ const inferParsers = (descriptor, location, languageOptions) => {
278
340
  };
279
341
 
280
342
  /**
281
- * graphPackage and gatherDependency are mutually recursive functions that
343
+ * `graphPackage` and {@link gatherDependency} are mutually recursive functions that
282
344
  * gather the metadata for a package and its transitive dependencies.
283
345
  * The keys of the graph are the locations of the package descriptors.
284
346
  * The metadata include a label (which is informative and not necessarily
@@ -289,16 +351,12 @@ const inferParsers = (descriptor, location, languageOptions) => {
289
351
  * @param {ReadDescriptorFn} readDescriptor
290
352
  * @param {CanonicalFn} canonical
291
353
  * @param {Graph} graph
292
- * @param {object} packageDetails
293
- * @param {string} packageDetails.packageLocation
294
- * @param {object} packageDetails.packageDescriptor
354
+ * @param {PackageDetails} packageDetails
295
355
  * @param {Set<string>} conditions
296
356
  * @param {boolean | undefined} dev
297
- * @param {CommonDependencyDescriptors} commonDependencyDescriptors
298
357
  * @param {LanguageOptions} languageOptions
299
358
  * @param {boolean} strict
300
- * @param {Map<string, Array<string>>} preferredPackageLogicalPathMap
301
- * @param {Array<string>} logicalPath
359
+ * @param {GraphPackageOptions} options
302
360
  * @returns {Promise<undefined>}
303
361
  */
304
362
  const graphPackage = async (
@@ -309,34 +367,56 @@ const graphPackage = async (
309
367
  { packageLocation, packageDescriptor },
310
368
  conditions,
311
369
  dev,
312
- commonDependencyDescriptors,
313
370
  languageOptions,
314
371
  strict,
315
- preferredPackageLogicalPathMap = new Map(),
316
- logicalPath = [],
372
+ {
373
+ commonDependencyDescriptors = {},
374
+ preferredPackageLogicalPathMap = new Map(),
375
+ logicalPath = [],
376
+ log = noop,
377
+ } = {},
317
378
  ) => {
318
379
  if (graph[packageLocation] !== undefined) {
380
+ updateShortestPaths(
381
+ graph,
382
+ packageLocation,
383
+ logicalPath,
384
+ preferredPackageLogicalPathMap,
385
+ );
386
+
319
387
  // Returning the promise here would create a causal cycle and stall recursion.
320
388
  return undefined;
321
389
  }
322
390
 
323
391
  if (packageDescriptor.name !== name) {
324
- console.warn(
325
- `Package named ${q(
326
- name,
327
- )} does not match location ${packageLocation} got (${q(
328
- packageDescriptor.name,
329
- )})`,
330
- );
392
+ log('Package name does not match location', {
393
+ name,
394
+ packageDescriptorName: packageDescriptor.name,
395
+ packageLocation,
396
+ });
331
397
  }
332
398
 
333
- const result = {};
334
- graph[packageLocation] = /** @type {Node} */ (result);
399
+ const result = /** @type {Node} */ ({});
400
+ graph[packageLocation] = result;
335
401
 
336
- /** @type {Record<string, string>} */
402
+ /** @type {Node['dependencyLocations']} */
337
403
  const dependencyLocations = {};
404
+ /** @type {ReturnType<typeof gatherDependency>[]} */
338
405
  const children = [];
406
+
407
+ /**
408
+ * A set containing dependency names which are considered "optional"
409
+ */
339
410
  const optionals = new Set();
411
+
412
+ /**
413
+ * Contains the names of _all_ dependencies
414
+ *
415
+ * @type {Set<string>}
416
+ */
417
+ const allDependencies = new Set();
418
+
419
+ // these are fields from package.json containing dependencies
340
420
  const {
341
421
  dependencies = {},
342
422
  peerDependencies = {},
@@ -345,32 +425,40 @@ const graphPackage = async (
345
425
  optionalDependencies = {},
346
426
  devDependencies = {},
347
427
  } = packageDescriptor;
348
- const allDependencies = {};
349
- for (const [name, descriptor] of Object.entries(
350
- commonDependencyDescriptors,
351
- )) {
428
+
429
+ for (const [name, descriptor] of entries(commonDependencyDescriptors)) {
352
430
  if (Object(descriptor) === descriptor) {
353
- const { spec } = descriptor;
354
- allDependencies[name] = spec;
431
+ allDependencies.add(name);
355
432
  }
356
433
  }
357
- assign(allDependencies, dependencies);
358
- assign(allDependencies, peerDependencies);
359
- for (const [name, meta] of Object.entries(peerDependenciesMeta)) {
434
+
435
+ // only consider devDependencies if dev flag is true
436
+ for (const name of keys({
437
+ ...dependencies,
438
+ ...peerDependencies,
439
+ ...bundleDependencies,
440
+ ...optionalDependencies,
441
+ ...(dev ? devDependencies : {}),
442
+ })) {
443
+ allDependencies.add(name);
444
+ }
445
+
446
+ // for historical reasons, some packages omit peerDependencies and only
447
+ // use the peerDependenciesMeta field (because there was no way to define
448
+ // an "optional" peerDependency prior to npm v7). this is plainly wrong,
449
+ // but not exactly rare, either
450
+ for (const [name, meta] of entries(peerDependenciesMeta)) {
360
451
  if (Object(meta) === meta && meta.optional) {
361
452
  optionals.add(name);
453
+ allDependencies.add(name);
362
454
  }
363
455
  }
364
- assign(allDependencies, bundleDependencies);
365
- assign(allDependencies, optionalDependencies);
366
- for (const name of Object.keys(optionalDependencies)) {
456
+
457
+ for (const name of keys(optionalDependencies)) {
367
458
  optionals.add(name);
368
459
  }
369
- if (dev) {
370
- assign(allDependencies, devDependencies);
371
- }
372
460
 
373
- for (const name of keys(allDependencies).sort()) {
461
+ for (const name of [...allDependencies].sort()) {
374
462
  const optional = optionals.has(name);
375
463
  const childLogicalPath = [...logicalPath, name];
376
464
  children.push(
@@ -387,17 +475,24 @@ const graphPackage = async (
387
475
  preferredPackageLogicalPathMap,
388
476
  languageOptions,
389
477
  strict,
390
- childLogicalPath,
391
- optional,
392
- commonDependencyDescriptors,
478
+ {
479
+ childLogicalPath,
480
+ optional,
481
+ commonDependencyDescriptors,
482
+ log,
483
+ },
393
484
  ),
394
485
  );
395
486
  }
396
487
 
397
488
  const { version = '', exports: exportsDescriptor } = packageDescriptor;
398
- /** @type {Record<string, Language>} */
489
+ /** @type {Node['types']} */
399
490
  const types = {};
400
491
 
492
+ /**
493
+ * @param {string} path
494
+ * @returns {Promise<PackageDescriptor>}
495
+ */
401
496
  const readDescriptorUpwards = async path => {
402
497
  const location = resolveLocation(path, packageLocation);
403
498
  // readDescriptor coming from above is memoized, so this is not awfully slow
@@ -405,9 +500,9 @@ const graphPackage = async (
405
500
  return data;
406
501
  };
407
502
 
408
- /** @type {Record<string, string>} */
503
+ /** @type {Node['externalAliases']} */
409
504
  const externalAliases = {};
410
- /** @type {Record<string, string>} */
505
+ /** @type {Node['internalAliases']} */
411
506
  const internalAliases = {};
412
507
 
413
508
  inferExportsAndAliases(
@@ -424,10 +519,13 @@ const graphPackage = async (
424
519
  languageOptions,
425
520
  );
426
521
 
427
- Object.assign(result, {
522
+ const sourceDirname = basename(packageLocation);
523
+
524
+ assign(result, {
428
525
  name,
429
526
  path: logicalPath,
430
527
  label: `${name}${version ? `-v${version}` : ''}`,
528
+ sourceDirname,
431
529
  explicitExports: exportsDescriptor !== undefined,
432
530
  externalAliases,
433
531
  internalAliases,
@@ -448,7 +546,7 @@ const graphPackage = async (
448
546
  await Promise.all(children);
449
547
 
450
548
  // handle commonDependencyDescriptors package aliases
451
- for (const [name, { alias }] of Object.entries(commonDependencyDescriptors)) {
549
+ for (const [name, { alias }] of entries(commonDependencyDescriptors)) {
452
550
  // update the dependencyLocations to point to the common dependency
453
551
  const targetLocation = dependencyLocations[name];
454
552
  if (targetLocation === undefined) {
@@ -479,6 +577,8 @@ const graphPackage = async (
479
577
  };
480
578
 
481
579
  /**
580
+ * Adds information for the dependency of the package at `packageLocation` to the `graph` object.
581
+ *
482
582
  * @param {ReadDescriptorFn} readDescriptor
483
583
  * @param {CanonicalFn} canonical
484
584
  * @param {Graph} graph - the partially build graph.
@@ -488,10 +588,9 @@ const graphPackage = async (
488
588
  * @param {Set<string>} conditions
489
589
  * @param {Map<string, Array<string>>} preferredPackageLogicalPathMap
490
590
  * @param {LanguageOptions} languageOptions
491
- * @param {boolean} strict
492
- * @param {Array<string>} [childLogicalPath]
493
- * @param {boolean} [optional] - whether the dependency is optional
494
- * @param {object} [commonDependencyDescriptors] - dependencies to be added to all packages
591
+ * @param {boolean} strict - If `true`, a missing dependency will throw an exception
592
+ * @param {GatherDependencyOptions} options
593
+ * @returns {Promise<void>}
495
594
  */
496
595
  const gatherDependency = async (
497
596
  readDescriptor,
@@ -504,9 +603,12 @@ const gatherDependency = async (
504
603
  preferredPackageLogicalPathMap,
505
604
  languageOptions,
506
605
  strict,
507
- childLogicalPath = [],
508
- optional = false,
509
- commonDependencyDescriptors = undefined,
606
+ {
607
+ childLogicalPath = [],
608
+ optional = false,
609
+ commonDependencyDescriptors = {},
610
+ log = noop,
611
+ } = {},
510
612
  ) => {
511
613
  const dependency = await findPackage(
512
614
  readDescriptor,
@@ -522,15 +624,20 @@ const gatherDependency = async (
522
624
  throw Error(`Cannot find dependency ${name} for ${packageLocation}`);
523
625
  }
524
626
  dependencyLocations[name] = dependency.packageLocation;
525
- const theCurrentBest = preferredPackageLogicalPathMap.get(
627
+
628
+ const bestLogicalPath = currentBestLogicalPath(
629
+ childLogicalPath,
526
630
  dependency.packageLocation,
631
+ preferredPackageLogicalPathMap,
527
632
  );
528
- if (pathCompare(childLogicalPath, theCurrentBest) < 0) {
633
+
634
+ if (bestLogicalPath === childLogicalPath) {
529
635
  preferredPackageLogicalPathMap.set(
530
636
  dependency.packageLocation,
531
- childLogicalPath,
637
+ bestLogicalPath,
532
638
  );
533
639
  }
640
+
534
641
  await graphPackage(
535
642
  name,
536
643
  readDescriptor,
@@ -539,34 +646,35 @@ const gatherDependency = async (
539
646
  dependency,
540
647
  conditions,
541
648
  false,
542
- commonDependencyDescriptors,
543
649
  languageOptions,
544
650
  strict,
545
- preferredPackageLogicalPathMap,
546
- childLogicalPath,
651
+ {
652
+ commonDependencyDescriptors,
653
+ preferredPackageLogicalPathMap,
654
+ logicalPath: childLogicalPath,
655
+ log,
656
+ },
547
657
  );
548
658
  };
549
659
 
550
660
  /**
551
- * graphPackages returns a graph whose keys are nominally URLs, one per
552
- * package, with values that are label: (an informative Compartment name, built
553
- * as ${name}@${version}), dependencies: (a list of URLs), and exports: (an
554
- * object whose keys are the thing being imported, and the values are the names
555
- * of the matching module, relative to the containing package's root, that is,
556
- * the URL that was used as the key of graph).
557
- * The URLs in dependencies will all exist as other keys of graph.
661
+ * Resolves with a {@link Graph} representing the packages for which
662
+ * {@link CompartmentDescriptor CompartmentDescriptors} will be created.
558
663
  *
559
664
  * @param {MaybeReadFn} maybeRead
560
665
  * @param {CanonicalFn} canonical
561
666
  * @param {string} packageLocation - location of the main package.
562
667
  * @param {Set<string>} conditions
563
- * @param {object} mainPackageDescriptor - the parsed contents of the main
564
- * package.json, which was already read when searching for the package.json.
565
- * @param {boolean|undefined} dev - whether to use devDependencies from this package (and
566
- * only this package).
567
- * @param {Record<string,string>} commonDependencies - dependencies to be added to all packages
668
+ * @param {PackageDescriptor} mainPackageDescriptor - the parsed contents of the
669
+ * main `package.json`, which was already read when searching for the
670
+ * `package.json`.
671
+ * @param {boolean|undefined} dev - whether to use devDependencies from this
672
+ * package (and only this package).
673
+ * @param {Record<string,string>} commonDependencies - dependencies to be added
674
+ * to all packages
568
675
  * @param {LanguageOptions} languageOptions
569
676
  * @param {boolean} strict
677
+ * @param {GraphPackagesOptions} options
570
678
  */
571
679
  const graphPackages = async (
572
680
  maybeRead,
@@ -578,11 +686,12 @@ const graphPackages = async (
578
686
  commonDependencies,
579
687
  languageOptions,
580
688
  strict,
689
+ { log = noop } = {},
581
690
  ) => {
582
691
  const memo = create(null);
583
692
  /**
584
693
  * @param {string} packageLocation
585
- * @returns {Promise<object>}
694
+ * @returns {Promise<PackageDescriptor>}
586
695
  */
587
696
  const readDescriptor = packageLocation =>
588
697
  readDescriptorWithMemo(memo, maybeRead, packageLocation);
@@ -608,7 +717,7 @@ const graphPackages = async (
608
717
  /** @type {CommonDependencyDescriptors} */
609
718
  const commonDependencyDescriptors = {};
610
719
  const packageDescriptorDependencies = packageDescriptor.dependencies || {};
611
- for (const [alias, dependencyName] of Object.entries(commonDependencies)) {
720
+ for (const [alias, dependencyName] of entries(commonDependencies)) {
612
721
  const spec = packageDescriptorDependencies[dependencyName];
613
722
  if (spec === undefined) {
614
723
  throw Error(
@@ -633,16 +742,19 @@ const graphPackages = async (
633
742
  },
634
743
  conditions,
635
744
  dev,
636
- commonDependencyDescriptors,
637
745
  languageOptions,
638
746
  strict,
747
+ {
748
+ commonDependencyDescriptors,
749
+ log,
750
+ },
639
751
  );
640
752
  return graph;
641
753
  };
642
754
 
643
755
  /**
644
- * translateGraph converts the graph returned by graph packages (above) into a
645
- * compartment map.
756
+ * `translateGraph` converts the graph returned by graph packages (above) into a
757
+ * {@link CompartmentMapDescriptor compartment map}.
646
758
  *
647
759
  * @param {string} entryPackageLocation
648
760
  * @param {string} entryModuleSpecifier
@@ -659,8 +771,8 @@ const translateGraph = (
659
771
  conditions,
660
772
  policy,
661
773
  ) => {
662
- /** @type {Record<string, CompartmentDescriptor>} */
663
- const compartments = Object.create(null);
774
+ /** @type {CompartmentMapDescriptor['compartments']} */
775
+ const compartments = create(null);
664
776
 
665
777
  // For each package, build a map of all the external modules the package can
666
778
  // import from other packages.
@@ -676,15 +788,16 @@ const translateGraph = (
676
788
  name,
677
789
  path,
678
790
  label,
791
+ sourceDirname,
679
792
  dependencyLocations,
680
793
  internalAliases,
681
794
  parsers,
682
795
  types,
683
796
  } = graph[dependeeLocation];
684
- /** @type {Record<string, ModuleDescriptor>} */
685
- const moduleDescriptors = Object.create(null);
686
- /** @type {Record<string, ScopeDescriptor>} */
687
- const scopes = Object.create(null);
797
+ /** @type {CompartmentDescriptor['modules']} */
798
+ const moduleDescriptors = create(null);
799
+ /** @type {CompartmentDescriptor['scopes']} */
800
+ const scopes = create(null);
688
801
 
689
802
  /**
690
803
  * List of all the compartments (by name) that this compartment can import from.
@@ -770,6 +883,7 @@ const translateGraph = (
770
883
  name,
771
884
  path,
772
885
  location: dependeeLocation,
886
+ sourceDirname,
773
887
  modules: moduleDescriptors,
774
888
  scopes,
775
889
  parsers,
@@ -843,10 +957,10 @@ const makeLanguageOptions = ({
843
957
  };
844
958
 
845
959
  const languages = new Set([
846
- ...Object.values(moduleLanguageForExtension),
847
- ...Object.values(commonjsLanguageForExtension),
848
- ...Object.values(workspaceModuleLanguageForExtension),
849
- ...Object.values(workspaceCommonjsLanguageForExtension),
960
+ ...values(moduleLanguageForExtension),
961
+ ...values(commonjsLanguageForExtension),
962
+ ...values(workspaceModuleLanguageForExtension),
963
+ ...values(workspaceCommonjsLanguageForExtension),
850
964
  ...additionalLanguages,
851
965
  ]);
852
966
 
@@ -863,10 +977,11 @@ const makeLanguageOptions = ({
863
977
  * @param {ReadFn | ReadPowers | MaybeReadPowers} readPowers
864
978
  * @param {string} packageLocation
865
979
  * @param {Set<string>} conditionsOption
866
- * @param {object} packageDescriptor
980
+ * @param {PackageDescriptor} packageDescriptor
867
981
  * @param {string} moduleSpecifier
868
982
  * @param {CompartmentMapForNodeModulesOptions} [options]
869
983
  * @returns {Promise<CompartmentMapDescriptor>}
984
+ * @deprecated Use {@link mapNodeModules} instead.
870
985
  */
871
986
  export const compartmentMapForNodeModules = async (
872
987
  readPowers,
@@ -881,6 +996,7 @@ export const compartmentMapForNodeModules = async (
881
996
  commonDependencies = {},
882
997
  policy,
883
998
  strict = false,
999
+ log = noop,
884
1000
  } = options;
885
1001
  const { maybeRead, canonical } = unpackReadPowers(readPowers);
886
1002
  const languageOptions = makeLanguageOptions(options);
@@ -902,6 +1018,7 @@ export const compartmentMapForNodeModules = async (
902
1018
  commonDependencies,
903
1019
  languageOptions,
904
1020
  strict,
1021
+ { log },
905
1022
  );
906
1023
 
907
1024
  if (policy) {
@@ -932,6 +1049,11 @@ export const compartmentMapForNodeModules = async (
932
1049
  };
933
1050
 
934
1051
  /**
1052
+ * Creates a {@link CompartmentMapDescriptor} from the module at
1053
+ * `moduleLocation`, considering dependencies found in `node_modules`.
1054
+ *
1055
+ * Locates the {@link PackageDescriptor} for the module at `moduleLocation`
1056
+ *
935
1057
  * @param {ReadFn | ReadPowers | MaybeReadPowers} readPowers
936
1058
  * @param {string} moduleLocation
937
1059
  * @param {MapNodeModulesOptions} [options]
@@ -940,20 +1062,17 @@ export const compartmentMapForNodeModules = async (
940
1062
  export const mapNodeModules = async (
941
1063
  readPowers,
942
1064
  moduleLocation,
943
- options = {},
1065
+ { tags = new Set(), conditions = tags, log = noop, ...otherOptions } = {},
944
1066
  ) => {
945
- const { tags = new Set(), conditions = tags, ...otherOptions } = options;
946
-
947
1067
  const {
948
1068
  packageLocation,
949
1069
  packageDescriptorText,
950
1070
  packageDescriptorLocation,
951
1071
  moduleSpecifier,
952
- } = await search(readPowers, moduleLocation);
1072
+ } = await search(readPowers, moduleLocation, { log });
953
1073
 
954
- const packageDescriptor = parseLocatedJson(
955
- packageDescriptorText,
956
- packageDescriptorLocation,
1074
+ const packageDescriptor = /** @type {PackageDescriptor} */ (
1075
+ parseLocatedJson(packageDescriptorText, packageDescriptorLocation)
957
1076
  );
958
1077
 
959
1078
  return compartmentMapForNodeModules(
@@ -962,6 +1081,6 @@ export const mapNodeModules = async (
962
1081
  conditions,
963
1082
  packageDescriptor,
964
1083
  moduleSpecifier,
965
- otherOptions,
1084
+ { log, ...otherOptions },
966
1085
  );
967
1086
  };