bare-module-traverse 2.5.1 → 2.5.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/index.d.ts CHANGED
@@ -34,7 +34,19 @@ type AliasableExtension =
34
34
  | '.txt'
35
35
 
36
36
  interface TraverseOptions extends ResolveOptions {
37
+ /**
38
+ * The type assumed for a module that names none of its own, such as an
39
+ * ambiguous extension or a `data:` URL that no import reaches. See
40
+ * `constants` for possible values.
41
+ */
37
42
  defaultType?: number
43
+ /**
44
+ * The type of the import that names the entry. A `data:` URL of JavaScript
45
+ * takes its type from this: `REQUIRE` names a script and `IMPORT` a module,
46
+ * while `IMPORT | DYNAMIC` names either and so must carry a `type` attribute.
47
+ */
48
+ importType?: number
49
+ artifacts?: boolean
38
50
  aliases?: Record<string, AliasableExtension>
39
51
  /**
40
52
  * @param entry - The import to resolve, as produced by `bare-module-lexer`.
@@ -83,7 +95,7 @@ declare function traverse(
83
95
  declare function traverse(
84
96
  entry: URL,
85
97
  readModule: (url: URL) => Promise<Buffer | string | null>,
86
- listPrefix?: (url: URL) => AsyncIterable<URL>,
98
+ listPrefix?: (url: URL, expand: boolean) => AsyncIterable<URL>,
87
99
  probeModule?: (url: URL) => Promise<boolean | undefined>,
88
100
  resolveModule?: (url: URL) => Promise<URL>
89
101
  ): AsyncIterable<Dependency>
@@ -92,7 +104,7 @@ declare function traverse(
92
104
  entry: URL,
93
105
  opts: TraverseOptions,
94
106
  readModule: (url: URL) => Buffer | string | null,
95
- listPrefix?: (url: URL) => Iterable<URL>,
107
+ listPrefix?: (url: URL, expand: true) => Iterable<URL>,
96
108
  probeModule?: (url: URL) => boolean | undefined,
97
109
  resolveModule?: (url: URL) => URL
98
110
  ): Iterable<Dependency>
@@ -101,7 +113,7 @@ declare function traverse(
101
113
  entry: URL,
102
114
  opts: TraverseOptions,
103
115
  readModule: (url: URL) => Promise<Buffer | string | null>,
104
- listPrefix?: (url: URL) => AsyncIterable<URL>,
116
+ listPrefix?: (url: URL, expand: true) => AsyncIterable<URL>,
105
117
  probeModule?: (url: URL) => Promise<boolean | undefined>,
106
118
  resolveModule?: (url: URL) => Promise<URL>
107
119
  ): AsyncIterable<Dependency>
package/index.js CHANGED
@@ -43,7 +43,7 @@ module.exports = exports = function traverse(
43
43
 
44
44
  return {
45
45
  *[Symbol.iterator]() {
46
- const artifacts = { addons: [], assets: [] }
46
+ const artifacts = opts.artifacts === false ? null : { addons: [], assets: [] }
47
47
 
48
48
  const visited = opts.visited || new Set()
49
49
 
@@ -66,9 +66,13 @@ module.exports = exports = function traverse(
66
66
  const result = []
67
67
 
68
68
  if (typeof listPrefix === 'function') {
69
- for (const url of listPrefix(value.prefix)) {
69
+ const listed = listPrefix(value.prefix, value.expand)
70
+
71
+ for (const url of listed) {
70
72
  result.push(url)
71
73
  }
74
+
75
+ result.resolved = listed.resolved === true
72
76
  } else {
73
77
  if (readModule(value.prefix) !== null) {
74
78
  result.push(value.prefix)
@@ -101,7 +105,7 @@ module.exports = exports = function traverse(
101
105
  },
102
106
 
103
107
  async *[Symbol.asyncIterator]() {
104
- const artifacts = { addons: [], assets: [] }
108
+ const artifacts = opts.artifacts === false ? null : { addons: [], assets: [] }
105
109
 
106
110
  const visited = opts.visited || new Set()
107
111
 
@@ -124,9 +128,13 @@ module.exports = exports = function traverse(
124
128
  const result = []
125
129
 
126
130
  if (typeof listPrefix === 'function') {
127
- for await (const url of listPrefix(value.prefix)) {
131
+ const listed = listPrefix(value.prefix, value.expand)
132
+
133
+ for await (const url of listed) {
128
134
  result.push(url)
129
135
  }
136
+
137
+ result.resolved = listed.resolved === true
130
138
  } else {
131
139
  if ((await readModule(value.prefix)) !== null) {
132
140
  result.push(value.prefix)
@@ -192,6 +200,9 @@ exports.module = function* (url, source, attributes, artifacts, visited, opts =
192
200
 
193
201
  if (probed !== undefined) opts = { ...opts, probed: undefined }
194
202
 
203
+ if (opts.packages === undefined) opts = { ...opts, packages: new Map() }
204
+ if (opts.prefixes === undefined) opts = { ...opts, prefixes: new Map() }
205
+
195
206
  attributes = attributes || {}
196
207
 
197
208
  const artifact = asset === true || moduleType(url, attributes, null, opts) === constants.ADDON
@@ -225,20 +236,16 @@ exports.module = function* (url, source, attributes, artifacts, visited, opts =
225
236
  let info = null
226
237
 
227
238
  if (url.protocol !== 'data:') {
228
- for (const packageURL of lookupPackageScope(url, opts)) {
229
- const source = yield { module: packageURL, artifact: false }
230
-
231
- if (source !== null) {
232
- info = JSON.parse(source)
239
+ const scope = yield* lookupPackage(url, opts)
233
240
 
234
- imports['#package'] = packageURL.href
241
+ if (scope !== null) {
242
+ info = scope.info
235
243
 
236
- yield {
237
- children: exports.package(packageURL, source, artifacts, visited, opts),
238
- deferred: false
239
- }
244
+ imports['#package'] = scope.url.href
240
245
 
241
- break
246
+ yield {
247
+ children: exports.package(scope.url, scope.source, artifacts, visited, opts),
248
+ deferred: false
242
249
  }
243
250
  }
244
251
  }
@@ -264,9 +271,9 @@ exports.module = function* (url, source, attributes, artifacts, visited, opts =
264
271
  if (type === constants.SCRIPT || type === constants.MODULE) {
265
272
  yield* exports.imports(url, source, imports, artifacts, lexer, visited, {
266
273
  ...opts,
267
- referrerType: type
274
+ importType: 0
268
275
  })
269
- } else if (type === constants.ADDON) {
276
+ } else if (type === constants.ADDON && artifacts !== null) {
270
277
  yield* exports.addons(url, artifacts, visited, opts)
271
278
  }
272
279
  }
@@ -308,7 +315,7 @@ exports.package = function* (url, source, artifacts, visited, opts = {}) {
308
315
  }
309
316
  }
310
317
 
311
- if (info.assets) {
318
+ if (info.assets && artifacts !== null) {
312
319
  yield {
313
320
  children: exports.assets(info.assets, url, artifacts, visited, opts),
314
321
  deferred: false
@@ -349,15 +356,17 @@ exports.preresolved = function* (url, source, resolutions, artifacts, visited, o
349
356
  deferred: false
350
357
  }
351
358
  } else if (asset) {
352
- addURL(artifacts.assets, url)
353
-
354
- yield {
355
- children: exports.module(url, null, {}, artifacts, visited, {
356
- ...opts,
357
- asset: true,
358
- referrerType: type
359
- }),
360
- deferred: true
359
+ if (artifacts !== null) {
360
+ addURL(artifacts.assets, url)
361
+
362
+ yield {
363
+ children: exports.module(url, null, {}, artifacts, visited, {
364
+ ...opts,
365
+ asset: true,
366
+ importType: 0
367
+ }),
368
+ deferred: true
369
+ }
361
370
  }
362
371
  } else if (
363
372
  url.protocol !== builtinProtocol &&
@@ -367,7 +376,7 @@ exports.preresolved = function* (url, source, resolutions, artifacts, visited, o
367
376
  yield {
368
377
  children: exports.module(url, null, {}, artifacts, visited, {
369
378
  ...opts,
370
- referrerType: type
379
+ importType: 0
371
380
  }),
372
381
  deferred: false
373
382
  }
@@ -465,7 +474,8 @@ function* resolveImport(entry, specifier, condition, parentURL, imports, artifac
465
474
  builtinProtocol = 'builtin:',
466
475
  linkedProtocol = 'linked:',
467
476
  deferredProtocol = 'deferred:',
468
- deferUnresolved = false
477
+ deferUnresolved = false,
478
+ prefixes = new Map()
469
479
  } = opts
470
480
 
471
481
  const matchedConditions = []
@@ -503,31 +513,55 @@ function* resolveImport(entry, specifier, condition, parentURL, imports, artifac
503
513
  resolved = true
504
514
  } else if (condition === 'asset') {
505
515
  const prefix = url
516
+ const expand = artifacts !== null
506
517
 
507
- let prefixResolution = null
518
+ let expanded = prefixes.get(prefix.href)
508
519
 
509
- for (const url of yield { prefix }) {
510
- const resolution = yield* postresolve(url)
520
+ if (expanded === undefined) {
521
+ const listed = yield { prefix, expand }
511
522
 
512
- if (url.href === prefix.href) prefixResolution = resolution
523
+ const urls = []
513
524
 
514
- yield {
515
- children: exports.module(resolution, null, {}, artifacts, visited, {
516
- ...opts,
517
- asset: true
518
- }),
519
- deferred: true
525
+ let prefixResolution = null
526
+ let found = false
527
+
528
+ for (const url of listed) {
529
+ found = true
530
+
531
+ if (expand === false) break
532
+
533
+ const resolution = listed.resolved === true ? url : yield* postresolve(url)
534
+
535
+ if (url.href === prefix.href) prefixResolution = resolution
536
+
537
+ yield {
538
+ children: exports.module(resolution, null, {}, artifacts, visited, {
539
+ ...opts,
540
+ asset: true
541
+ }),
542
+ deferred: true
543
+ }
544
+
545
+ urls.push(resolution)
520
546
  }
521
547
 
522
- addURL(artifacts.assets, resolution)
548
+ if (found) {
549
+ expanded = { resolution: prefixResolution || (yield* postresolve(prefix)), urls }
523
550
 
524
- resolved = true
551
+ prefixes.set(prefix.href, expanded)
552
+ }
525
553
  }
526
554
 
527
- if (resolved) {
528
- resolution = prefixResolution || (yield* postresolve(prefix))
555
+ if (expanded !== undefined) {
556
+ if (expand) {
557
+ for (const url of expanded.urls) addURL(artifacts.assets, url)
558
+ }
559
+
560
+ resolution = expanded.resolution
529
561
 
530
562
  addResolution(imports, specifier, matchedConditions, resolution)
563
+
564
+ resolved = true
531
565
  }
532
566
  } else if (
533
567
  condition === 'addon' ||
@@ -577,7 +611,10 @@ function* resolveImport(entry, specifier, condition, parentURL, imports, artifac
577
611
  }
578
612
 
579
613
  yield {
580
- children: exports.module(resolution, source, attributes, artifacts, visited, opts),
614
+ children: exports.module(resolution, source, attributes, artifacts, visited, {
615
+ ...opts,
616
+ importType: entry.type
617
+ }),
581
618
  deferred: false
582
619
  }
583
620
 
@@ -586,7 +623,7 @@ function* resolveImport(entry, specifier, condition, parentURL, imports, artifac
586
623
  }
587
624
 
588
625
  if (resolved) {
589
- if (condition === 'addon') addURL(artifacts.addons, resolution)
626
+ if (condition === 'addon' && artifacts !== null) addURL(artifacts.addons, resolution)
590
627
 
591
628
  resolutions++
592
629
  }
@@ -632,8 +669,10 @@ exports.addons = function* (parentURL, artifacts, visited, opts = {}) {
632
669
 
633
670
  prefix.pathname = prefix.pathname.replace(ADDON_EXTENSION, '') + '/'
634
671
 
635
- for (const url of yield { prefix }) {
636
- const resolution = yield* postresolve(url)
672
+ const listed = yield { prefix, expand: true }
673
+
674
+ for (const url of listed) {
675
+ const resolution = listed.resolved === true ? url : yield* postresolve(url)
637
676
 
638
677
  yield {
639
678
  children: exports.module(resolution, null, {}, artifacts, visited, opts),
@@ -698,7 +737,7 @@ exports.patternMatches = function* patternMatches(pattern, parentURL, matches, o
698
737
 
699
738
  const prefix = new URL(patternBase, parentURL)
700
739
 
701
- for (const url of yield { prefix }) {
740
+ for (const url of yield { prefix, expand: true }) {
702
741
  if (patternIndex === -1) {
703
742
  if (patternNegate) removeURL(matches, url)
704
743
  else addURL(matches, url)
@@ -735,6 +774,38 @@ exports.patternMatches = function* patternMatches(pattern, parentURL, matches, o
735
774
  return matches
736
775
  }
737
776
 
777
+ function* lookupPackage(url, opts) {
778
+ const { packages = new Map() } = opts
779
+
780
+ const pending = []
781
+
782
+ for (const packageURL of lookupPackageScope(url, opts)) {
783
+ const scope = packages.get(packageURL.href)
784
+
785
+ if (scope !== undefined) {
786
+ for (const href of pending) packages.set(href, scope)
787
+
788
+ return scope
789
+ }
790
+
791
+ pending.push(packageURL.href)
792
+
793
+ const source = yield { module: packageURL, artifact: false }
794
+
795
+ if (source !== null) {
796
+ const scope = { url: packageURL, source, info: JSON.parse(source) }
797
+
798
+ for (const href of pending) packages.set(href, scope)
799
+
800
+ return scope
801
+ }
802
+ }
803
+
804
+ for (const href of pending) packages.set(href, null)
805
+
806
+ return null
807
+ }
808
+
738
809
  function* postresolve(url) {
739
810
  return (yield { resolution: url }) || url
740
811
  }
@@ -808,22 +879,22 @@ function typeForAttribute(type) {
808
879
  }
809
880
 
810
881
  function dataURLModuleType(url, attributes, opts = {}) {
811
- const { defaultType = constants.SCRIPT, referrerType } = opts
812
-
813
882
  const { mime } = parseDataURL(url)
814
883
 
884
+ if (mime === null) {
885
+ throw errors.UNKNOWN_DATA_URL_MEDIA_TYPE(`Data URL '${url.href}' does not declare a media type`)
886
+ }
887
+
815
888
  const asserted = typeof attributes.type === 'string' ? typeForAttribute(attributes.type) : null
816
889
 
817
- if (mime === null || mime.subtype === 'javascript') {
890
+ if (mime.subtype === 'javascript') {
818
891
  if (asserted === constants.SCRIPT || asserted === constants.MODULE) return asserted
819
892
 
820
893
  if (asserted !== null) {
821
894
  throw errors.TYPE_INCOMPATIBLE(`Module '${url.href}' is not of type '${attributes.type}'`)
822
895
  }
823
896
 
824
- if (referrerType) return referrerType
825
-
826
- return defaultType === constants.MODULE ? constants.MODULE : constants.SCRIPT
897
+ return javaScriptDataURLModuleType(url, opts)
827
898
  }
828
899
 
829
900
  let type
@@ -847,6 +918,27 @@ function dataURLModuleType(url, attributes, opts = {}) {
847
918
  return type
848
919
  }
849
920
 
921
+ // A data URL carries no extension and 'text/javascript' spans both module
922
+ // systems, so the type follows how the module was named: `require()` names a
923
+ // script and `import` a module. A dynamic `import()` names either and so must
924
+ // say which, as must a caller naming a data URL with no import to read, who
925
+ // answers with `defaultType`.
926
+ function javaScriptDataURLModuleType(url, opts) {
927
+ const { defaultType = constants.SCRIPT, importType = 0 } = opts
928
+
929
+ if (importType & lex.constants.REQUIRE) return constants.SCRIPT
930
+
931
+ if (importType & lex.constants.IMPORT) {
932
+ if ((importType & lex.constants.DYNAMIC) === 0) return constants.MODULE
933
+
934
+ throw errors.AMBIGUOUS_MODULE_TYPE(
935
+ `Module '${url.href}' must declare whether it is of type 'script' or 'module'`
936
+ )
937
+ }
938
+
939
+ return defaultType === constants.MODULE ? constants.MODULE : constants.SCRIPT
940
+ }
941
+
850
942
  function parseDataURL(url) {
851
943
  const { pathname } = url
852
944
 
package/lib/errors.js CHANGED
@@ -49,6 +49,14 @@ module.exports = class ModuleTraverseError extends Error {
49
49
  return new ModuleTraverseError(msg, ModuleTraverseError.TYPE_INCOMPATIBLE)
50
50
  }
51
51
 
52
+ static AMBIGUOUS_MODULE_TYPE(msg) {
53
+ return new ModuleTraverseError(msg, ModuleTraverseError.AMBIGUOUS_MODULE_TYPE)
54
+ }
55
+
56
+ static UNKNOWN_DATA_URL_MEDIA_TYPE(msg) {
57
+ return new ModuleTraverseError(msg, ModuleTraverseError.UNKNOWN_DATA_URL_MEDIA_TYPE)
58
+ }
59
+
52
60
  static UNKNOWN_DATA_URL_CHARSET(msg, charset) {
53
61
  const err = new ModuleTraverseError(msg, ModuleTraverseError.UNKNOWN_DATA_URL_CHARSET)
54
62
 
@@ -5,6 +5,7 @@ const runtime = require('../runtime')
5
5
  module.exports = function (entry, parentURL, opts = {}) {
6
6
  const { linked = false, host = runtime.host, hosts = [host] } = opts
7
7
 
8
+ let directories = false
8
9
  let extensions
9
10
  let conditions = hosts.map((host) => ['bare', 'node', ...host.split('-')])
10
11
 
@@ -23,6 +24,7 @@ module.exports = function (entry, parentURL, opts = {}) {
23
24
 
24
25
  if (entry.type & lex.constants.ASSET) {
25
26
  conditions = conditions.map((conditions) => ['asset', ...conditions])
27
+ directories = true
26
28
  } else {
27
29
  extensions = ['.js', '.cjs', '.mjs', '.ts', '.cts', '.mts', '.json', '.bare', '.node']
28
30
 
@@ -36,6 +38,7 @@ module.exports = function (entry, parentURL, opts = {}) {
36
38
  return resolve.module(entry.specifier, parentURL, {
37
39
  ...opts,
38
40
  extensions,
39
- conditions
41
+ conditions,
42
+ directories
40
43
  })
41
44
  }
@@ -5,6 +5,7 @@ const runtime = require('../runtime')
5
5
  module.exports = function (entry, parentURL, opts = {}) {
6
6
  const { host = runtime.host, hosts = [host] } = opts
7
7
 
8
+ let directories = false
8
9
  let extensions
9
10
  let conditions = hosts.map((host) => ['node', ...host.split('-')])
10
11
 
@@ -22,6 +23,7 @@ module.exports = function (entry, parentURL, opts = {}) {
22
23
 
23
24
  if (entry.type & lex.constants.ASSET) {
24
25
  conditions = conditions.map((conditions) => ['asset', ...conditions])
26
+ directories = true
25
27
  } else if (entry.type & lex.constants.REQUIRE) {
26
28
  extensions = ['.js', '.json', '.node']
27
29
  conditions = conditions.map((conditions) => ['require', ...conditions])
@@ -32,6 +34,7 @@ module.exports = function (entry, parentURL, opts = {}) {
32
34
  return resolve.module(entry.specifier, parentURL, {
33
35
  ...opts,
34
36
  extensions,
35
- conditions
37
+ conditions,
38
+ directories
36
39
  })
37
40
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bare-module-traverse",
3
- "version": "2.5.1",
3
+ "version": "2.5.3",
4
4
  "description": "Low-level module graph traversal for Bare",
5
5
  "exports": {
6
6
  ".": {