bare-module-traverse 2.5.2 → 2.5.4

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/README.md CHANGED
@@ -128,6 +128,9 @@ next.value = {
128
128
  url: URL,
129
129
  source: 'string' | Buffer,
130
130
  type: constants.SCRIPT,
131
+ // The type the module has on its own, ignoring import attributes, or 0 if
132
+ // it has no type of its own.
133
+ naturalType: constants.SCRIPT,
131
134
  imports: {
132
135
  // See https://github.com/holepunchto/bare-module#imports
133
136
  },
package/index.d.ts CHANGED
@@ -11,7 +11,15 @@ import { type Import, type Export } from 'bare-module-lexer'
11
11
  interface Dependency {
12
12
  url: URL
13
13
  source: string | Buffer
14
+ /** The type the module is loaded as. See `constants` for possible values. */
14
15
  type: number
16
+ /**
17
+ * The type the module has on its own, ignoring import attributes, or `0` if
18
+ * it has no type of its own. This differs from `type` only when an import
19
+ * attribute asked for a different type, so a loader can use it to tell
20
+ * whether an attribute changed the type.
21
+ */
22
+ naturalType: number
15
23
  imports: ImportsMap
16
24
  lexer: {
17
25
  imports: Import[]
@@ -34,7 +42,19 @@ type AliasableExtension =
34
42
  | '.txt'
35
43
 
36
44
  interface TraverseOptions extends ResolveOptions {
45
+ /**
46
+ * The type to assume for a module that has no type of its own, such as one
47
+ * with an ambiguous extension or a `data:` URL that no import reaches. See
48
+ * `constants` for possible values.
49
+ */
37
50
  defaultType?: number
51
+ /**
52
+ * The type of the import that led to the entry. A JavaScript `data:` URL
53
+ * takes its type from this: `REQUIRE` means a script and `IMPORT` a module,
54
+ * while `IMPORT | DYNAMIC` could mean either and so needs a `type`
55
+ * attribute.
56
+ */
57
+ importType?: number
38
58
  artifacts?: boolean
39
59
  aliases?: Record<string, AliasableExtension>
40
60
  /**
package/index.js CHANGED
@@ -192,9 +192,19 @@ exports.alias = function alias(url, opts = {}) {
192
192
  }
193
193
 
194
194
  exports.module = function* (url, source, attributes, artifacts, visited, opts = {}) {
195
- const { resolutions = null, asset = false, probed } = opts
195
+ const { resolutions = null, asset = false, probed, types } = opts
196
196
 
197
- if (visited.has(url.href)) return false
197
+ attributes = attributes || {}
198
+
199
+ if (visited.has(url.href)) {
200
+ // An asset is only read for its bytes, so it doesn't care what type the
201
+ // module is imported as elsewhere.
202
+ if (asset === false && types !== undefined) {
203
+ assertTypeAgrees(url, attributes, types.get(url.href), opts)
204
+ }
205
+
206
+ return false
207
+ }
198
208
 
199
209
  visited.add(url.href)
200
210
 
@@ -202,8 +212,7 @@ exports.module = function* (url, source, attributes, artifacts, visited, opts =
202
212
 
203
213
  if (opts.packages === undefined) opts = { ...opts, packages: new Map() }
204
214
  if (opts.prefixes === undefined) opts = { ...opts, prefixes: new Map() }
205
-
206
- attributes = attributes || {}
215
+ if (opts.types === undefined) opts = { ...opts, types: new Map() }
207
216
 
208
217
  const artifact = asset === true || moduleType(url, attributes, null, opts) === constants.ADDON
209
218
 
@@ -264,6 +273,9 @@ exports.module = function* (url, source, attributes, artifacts, visited, opts =
264
273
  }
265
274
 
266
275
  const type = moduleType(url, attributes, info, opts)
276
+ const naturalType = naturalModuleType(url, attributes, info, type, opts)
277
+
278
+ if (asset === false) opts.types.set(url.href, { type, asserted: attributes.type, info })
267
279
 
268
280
  const lexer = { imports: [], exports: [] }
269
281
 
@@ -271,7 +283,7 @@ exports.module = function* (url, source, attributes, artifacts, visited, opts =
271
283
  if (type === constants.SCRIPT || type === constants.MODULE) {
272
284
  yield* exports.imports(url, source, imports, artifacts, lexer, visited, {
273
285
  ...opts,
274
- referrerType: type
286
+ importType: 0
275
287
  })
276
288
  } else if (type === constants.ADDON && artifacts !== null) {
277
289
  yield* exports.addons(url, artifacts, visited, opts)
@@ -283,6 +295,7 @@ exports.module = function* (url, source, attributes, artifacts, visited, opts =
283
295
  url: exports.alias(url, opts),
284
296
  source,
285
297
  type,
298
+ naturalType,
286
299
  imports: compressImportsMap(imports),
287
300
  lexer
288
301
  }
@@ -310,6 +323,7 @@ exports.package = function* (url, source, artifacts, visited, opts = {}) {
310
323
  url,
311
324
  source,
312
325
  type: constants.JSON,
326
+ naturalType: constants.JSON,
313
327
  imports: {},
314
328
  lexer: { imports: [], exports: [] }
315
329
  }
@@ -363,7 +377,7 @@ exports.preresolved = function* (url, source, resolutions, artifacts, visited, o
363
377
  children: exports.module(url, null, {}, artifacts, visited, {
364
378
  ...opts,
365
379
  asset: true,
366
- referrerType: type
380
+ importType: 0
367
381
  }),
368
382
  deferred: true
369
383
  }
@@ -376,7 +390,7 @@ exports.preresolved = function* (url, source, resolutions, artifacts, visited, o
376
390
  yield {
377
391
  children: exports.module(url, null, {}, artifacts, visited, {
378
392
  ...opts,
379
- referrerType: type
393
+ importType: 0
380
394
  }),
381
395
  deferred: false
382
396
  }
@@ -400,6 +414,7 @@ exports.preresolved = function* (url, source, resolutions, artifacts, visited, o
400
414
  url: exports.alias(url, opts),
401
415
  source,
402
416
  type,
417
+ naturalType: type,
403
418
  imports: compressImportsMap(imports),
404
419
  lexer
405
420
  }
@@ -611,7 +626,10 @@ function* resolveImport(entry, specifier, condition, parentURL, imports, artifac
611
626
  }
612
627
 
613
628
  yield {
614
- children: exports.module(resolution, source, attributes, artifacts, visited, opts),
629
+ children: exports.module(resolution, source, attributes, artifacts, visited, {
630
+ ...opts,
631
+ importType: entry.type
632
+ }),
615
633
  deferred: false
616
634
  }
617
635
 
@@ -807,6 +825,17 @@ function* postresolve(url) {
807
825
  return (yield { resolution: url }) || url
808
826
  }
809
827
 
828
+ // The type a module has on its own, ignoring import attributes. A JavaScript
829
+ // data: URL has no type of its own, so this returns 0; its type depends on how
830
+ // it is imported.
831
+ function naturalModuleType(url, attributes, info, type, opts) {
832
+ if (attributes.type === undefined) return type
833
+
834
+ if (url.protocol === 'data:') return 0
835
+
836
+ return moduleType(url, {}, info, opts)
837
+ }
838
+
810
839
  function moduleType(url, attributes, info, opts = {}) {
811
840
  const { defaultType = constants.SCRIPT, aliases = null } = opts
812
841
 
@@ -854,6 +883,46 @@ function moduleType(url, attributes, info, opts = {}) {
854
883
  return defaultType
855
884
  }
856
885
 
886
+ // A URL has a single type within a graph, and the first import to reach it
887
+ // decides what that is. Later imports have to agree, so an attribute on one
888
+ // import cannot quietly change the type another import sees.
889
+ function assertTypeAgrees(url, attributes, recorded, opts) {
890
+ if (recorded === undefined) return
891
+ if (attributes.type === recorded.asserted) return
892
+
893
+ const expected =
894
+ typeof attributes.type === 'string'
895
+ ? typeForAttribute(attributes.type)
896
+ : moduleType(url, {}, recorded.info, opts)
897
+
898
+ if (expected === recorded.type) return
899
+
900
+ throw errors.TYPE_INCOMPATIBLE(
901
+ `Module '${url.href}' is already of type '${attributeForType(recorded.type)}' in the graph`
902
+ )
903
+ }
904
+
905
+ function attributeForType(type) {
906
+ switch (type) {
907
+ case constants.SCRIPT:
908
+ return 'script'
909
+ case constants.MODULE:
910
+ return 'module'
911
+ case constants.JSON:
912
+ return 'json'
913
+ case constants.BUNDLE:
914
+ return 'bundle'
915
+ case constants.ADDON:
916
+ return 'addon'
917
+ case constants.BINARY:
918
+ return 'binary'
919
+ case constants.TEXT:
920
+ return 'text'
921
+ }
922
+
923
+ return 'unknown'
924
+ }
925
+
857
926
  function typeForAttribute(type) {
858
927
  switch (type) {
859
928
  case 'script':
@@ -876,22 +945,22 @@ function typeForAttribute(type) {
876
945
  }
877
946
 
878
947
  function dataURLModuleType(url, attributes, opts = {}) {
879
- const { defaultType = constants.SCRIPT, referrerType } = opts
880
-
881
948
  const { mime } = parseDataURL(url)
882
949
 
950
+ if (mime === null) {
951
+ throw errors.UNKNOWN_DATA_URL_MEDIA_TYPE(`Data URL '${url.href}' does not declare a media type`)
952
+ }
953
+
883
954
  const asserted = typeof attributes.type === 'string' ? typeForAttribute(attributes.type) : null
884
955
 
885
- if (mime === null || mime.subtype === 'javascript') {
956
+ if (mime.subtype === 'javascript') {
886
957
  if (asserted === constants.SCRIPT || asserted === constants.MODULE) return asserted
887
958
 
888
959
  if (asserted !== null) {
889
960
  throw errors.TYPE_INCOMPATIBLE(`Module '${url.href}' is not of type '${attributes.type}'`)
890
961
  }
891
962
 
892
- if (referrerType) return referrerType
893
-
894
- return defaultType === constants.MODULE ? constants.MODULE : constants.SCRIPT
963
+ return javaScriptDataURLModuleType(url, opts)
895
964
  }
896
965
 
897
966
  let type
@@ -915,6 +984,27 @@ function dataURLModuleType(url, attributes, opts = {}) {
915
984
  return type
916
985
  }
917
986
 
987
+ // A data URL has no extension, and 'text/javascript' covers both module
988
+ // systems, so the type follows how the module was imported: `require()` means a
989
+ // script and `import` a module. A dynamic `import()` could mean either, so it
990
+ // has to say which. When there is no import to read at all, `defaultType`
991
+ // decides.
992
+ function javaScriptDataURLModuleType(url, opts) {
993
+ const { defaultType = constants.SCRIPT, importType = 0 } = opts
994
+
995
+ if (importType & lex.constants.REQUIRE) return constants.SCRIPT
996
+
997
+ if (importType & lex.constants.IMPORT) {
998
+ if ((importType & lex.constants.DYNAMIC) === 0) return constants.MODULE
999
+
1000
+ throw errors.AMBIGUOUS_MODULE_TYPE(
1001
+ `Module '${url.href}' must declare whether it is of type 'script' or 'module'`
1002
+ )
1003
+ }
1004
+
1005
+ return defaultType === constants.MODULE ? constants.MODULE : constants.SCRIPT
1006
+ }
1007
+
918
1008
  function parseDataURL(url) {
919
1009
  const { pathname } = url
920
1010
 
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
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bare-module-traverse",
3
- "version": "2.5.2",
3
+ "version": "2.5.4",
4
4
  "description": "Low-level module graph traversal for Bare",
5
5
  "exports": {
6
6
  ".": {