bare-module-traverse 2.5.9 → 2.6.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/README.md +5 -1
- package/index.d.ts +13 -0
- package/index.js +218 -40
- package/lib/errors.js +12 -0
- package/lib/resolve/bare.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -219,12 +219,16 @@ Options are the same as `traverse()` for all functions.
|
|
|
219
219
|
|
|
220
220
|
### `const generator = traverse.package(url, source, artifacts, visited[, options])`
|
|
221
221
|
|
|
222
|
-
### `const generator = traverse.preresolved(url, source, resolutions, artifacts, visited[, options])`
|
|
222
|
+
### `const generator = traverse.preresolved(url, source, attributes, resolutions, artifacts, visited[, options])`
|
|
223
223
|
|
|
224
224
|
### `const generator = traverse.imports(parentURL, source, imports, artifacts, lexer, visited[, options])`
|
|
225
225
|
|
|
226
226
|
### `const generator = traverse.link(entry, specifier, condition, parentURL, imports, artifacts, visited[, options])`
|
|
227
227
|
|
|
228
|
+
### `traverse.assertAddonContext(url, scope, context, addonContexts)`
|
|
229
|
+
|
|
230
|
+
Record in `addonContexts` that the package at `scope` loads its addons in `context`, an object of the `host` and the URL of the `wasi` provider, either `null` for the defaults. Throws if it was recorded for another context, or if `url` doesn't load on `context.host`.
|
|
231
|
+
|
|
228
232
|
### `const generator = traverse.addons(parentURL, artifacts, visited[, options])`
|
|
229
233
|
|
|
230
234
|
### `const generator = traverse.assets(patterns, parentURL, artifacts, visited[, options])`
|
package/index.d.ts
CHANGED
|
@@ -174,9 +174,22 @@ declare namespace traverse {
|
|
|
174
174
|
opts?: TraverseOptions
|
|
175
175
|
): Traversal
|
|
176
176
|
|
|
177
|
+
interface AddonContext {
|
|
178
|
+
host: string | null
|
|
179
|
+
wasi: string | null
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
export function assertAddonContext(
|
|
183
|
+
url: URL,
|
|
184
|
+
scope: string,
|
|
185
|
+
context: Partial<AddonContext>,
|
|
186
|
+
addonContexts: Map<string, AddonContext>
|
|
187
|
+
): void
|
|
188
|
+
|
|
177
189
|
export function preresolved(
|
|
178
190
|
url: URL,
|
|
179
191
|
source: string | Buffer,
|
|
192
|
+
attributes: Record<string, string> | null,
|
|
180
193
|
resolution: ResolutionsMap,
|
|
181
194
|
artifacts: Artifacts,
|
|
182
195
|
visited: Set<string>,
|
package/index.js
CHANGED
|
@@ -192,10 +192,29 @@ 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, types } = opts
|
|
196
|
-
|
|
197
195
|
attributes = attributes || {}
|
|
198
196
|
|
|
197
|
+
if (opts.contexts === undefined) opts = { ...opts, contexts: new Map() }
|
|
198
|
+
if (opts.addonContexts === undefined) opts = { ...opts, addonContexts: new Map() }
|
|
199
|
+
|
|
200
|
+
// The host and WASI attributes set the context that the addons of the module
|
|
201
|
+
// and everything it imports load in. A graph is walked again for each context
|
|
202
|
+
// it is imported under, even where it was visited before.
|
|
203
|
+
if (attributes.host !== undefined || attributes.wasi !== undefined) {
|
|
204
|
+
const { addonHost = null, addonWASI = null } = opts
|
|
205
|
+
|
|
206
|
+
const host = attributes.host === undefined ? addonHost : addonHostForAttribute(attributes.host)
|
|
207
|
+
const wasi = attributes.wasi === undefined ? addonWASI : attributes.wasi
|
|
208
|
+
|
|
209
|
+
if (host !== addonHost || wasi !== addonWASI) {
|
|
210
|
+
opts = { ...opts, addonHost: host, addonWASI: wasi }
|
|
211
|
+
|
|
212
|
+
visited = visitedFor(opts.contexts, contextKey(host, wasi))
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
const { resolutions = null, asset = false, probed, types } = opts
|
|
217
|
+
|
|
199
218
|
if (visited.has(url.href)) {
|
|
200
219
|
// An asset is only read for its bytes, so it doesn't care what type the
|
|
201
220
|
// module is imported as elsewhere.
|
|
@@ -214,7 +233,9 @@ exports.module = function* (url, source, attributes, artifacts, visited, opts =
|
|
|
214
233
|
if (opts.prefixes === undefined) opts = { ...opts, prefixes: new Map() }
|
|
215
234
|
if (opts.types === undefined) opts = { ...opts, types: new Map() }
|
|
216
235
|
|
|
217
|
-
const artifact =
|
|
236
|
+
const artifact =
|
|
237
|
+
asset === true ||
|
|
238
|
+
(moduleType(url, attributes, null, opts) === constants.ADDON && !isWebAssembly(url))
|
|
218
239
|
|
|
219
240
|
if (source === null) {
|
|
220
241
|
if (url.protocol === 'data:') {
|
|
@@ -235,7 +256,9 @@ exports.module = function* (url, source, attributes, artifacts, visited, opts =
|
|
|
235
256
|
}
|
|
236
257
|
|
|
237
258
|
if (resolutions) {
|
|
238
|
-
if (
|
|
259
|
+
if (
|
|
260
|
+
yield* exports.preresolved(url, source, attributes, resolutions, artifacts, visited, opts)
|
|
261
|
+
) {
|
|
239
262
|
return true
|
|
240
263
|
}
|
|
241
264
|
}
|
|
@@ -291,14 +314,17 @@ exports.module = function* (url, source, attributes, artifacts, visited, opts =
|
|
|
291
314
|
}
|
|
292
315
|
|
|
293
316
|
yield {
|
|
294
|
-
dependency:
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
317
|
+
dependency: withWASI(
|
|
318
|
+
{
|
|
319
|
+
url: exports.alias(url, opts),
|
|
320
|
+
source,
|
|
321
|
+
type,
|
|
322
|
+
naturalType,
|
|
323
|
+
imports: compressImportsMap(imports),
|
|
324
|
+
lexer
|
|
325
|
+
},
|
|
326
|
+
opts
|
|
327
|
+
)
|
|
302
328
|
}
|
|
303
329
|
|
|
304
330
|
return true
|
|
@@ -342,7 +368,15 @@ exports.package = function* (url, source, artifacts, visited, opts = {}) {
|
|
|
342
368
|
return false
|
|
343
369
|
}
|
|
344
370
|
|
|
345
|
-
exports.preresolved = function* (
|
|
371
|
+
exports.preresolved = function* (
|
|
372
|
+
url,
|
|
373
|
+
source,
|
|
374
|
+
attributes,
|
|
375
|
+
resolutions,
|
|
376
|
+
artifacts,
|
|
377
|
+
visited,
|
|
378
|
+
opts = {}
|
|
379
|
+
) {
|
|
346
380
|
const {
|
|
347
381
|
builtinProtocol = 'builtin:',
|
|
348
382
|
linkedProtocol = 'linked:',
|
|
@@ -353,6 +387,17 @@ exports.preresolved = function* (url, source, resolutions, artifacts, visited, o
|
|
|
353
387
|
|
|
354
388
|
if (typeof imports !== 'object' || imports === null) return false
|
|
355
389
|
|
|
390
|
+
attributes = attributes || {}
|
|
391
|
+
|
|
392
|
+
const parentURL = url
|
|
393
|
+
|
|
394
|
+
// The resolutions map leaves out the import attributes, so they are read
|
|
395
|
+
// from the source instead.
|
|
396
|
+
const lexed =
|
|
397
|
+
opts.asset !== true && isJavaScript(moduleType(url, attributes, null, opts))
|
|
398
|
+
? lex(source)
|
|
399
|
+
: null
|
|
400
|
+
|
|
356
401
|
let info = null
|
|
357
402
|
|
|
358
403
|
for (const [specifier, entry] of Object.entries(imports)) {
|
|
@@ -382,7 +427,11 @@ exports.preresolved = function* (url, source, resolutions, artifacts, visited, o
|
|
|
382
427
|
} else if (url.protocol === deferredProtocol) {
|
|
383
428
|
continue
|
|
384
429
|
} else if (url.protocol === builtinProtocol || url.protocol === linkedProtocol) {
|
|
385
|
-
if (addon
|
|
430
|
+
if (addon) {
|
|
431
|
+
assertImportedAddonContext(url, imports, parentURL, opts)
|
|
432
|
+
|
|
433
|
+
if (artifacts !== null) addURL(artifacts.addons, url)
|
|
434
|
+
}
|
|
386
435
|
} else if (asset) {
|
|
387
436
|
if (artifacts !== null) {
|
|
388
437
|
const expanded = yield* expandAsset(url, artifacts, visited, { ...opts, importType: 0 })
|
|
@@ -402,13 +451,24 @@ exports.preresolved = function* (url, source, resolutions, artifacts, visited, o
|
|
|
402
451
|
}
|
|
403
452
|
}
|
|
404
453
|
} else {
|
|
405
|
-
if (addon
|
|
454
|
+
if (addon) {
|
|
455
|
+
assertImportedAddonContext(url, imports, parentURL, opts)
|
|
456
|
+
|
|
457
|
+
if (artifacts !== null) addURL(artifacts.addons, url)
|
|
458
|
+
}
|
|
406
459
|
|
|
407
460
|
yield {
|
|
408
|
-
children: exports.module(
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
461
|
+
children: exports.module(
|
|
462
|
+
url,
|
|
463
|
+
null,
|
|
464
|
+
addon ? { type: 'addon' } : importAttributes(lexed, specifier, imports),
|
|
465
|
+
artifacts,
|
|
466
|
+
visited,
|
|
467
|
+
{
|
|
468
|
+
...opts,
|
|
469
|
+
importType: 0
|
|
470
|
+
}
|
|
471
|
+
),
|
|
412
472
|
deferred: false
|
|
413
473
|
}
|
|
414
474
|
}
|
|
@@ -424,26 +484,27 @@ exports.preresolved = function* (url, source, resolutions, artifacts, visited, o
|
|
|
424
484
|
}
|
|
425
485
|
}
|
|
426
486
|
|
|
427
|
-
const type = moduleType(url,
|
|
487
|
+
const type = moduleType(url, attributes, info, opts)
|
|
428
488
|
|
|
429
489
|
const lexer = { imports: [], exports: [] }
|
|
430
490
|
|
|
431
|
-
if (
|
|
432
|
-
const lexed = lex(source)
|
|
433
|
-
|
|
491
|
+
if (lexed !== null && isJavaScript(type)) {
|
|
434
492
|
lexer.imports = lexed.imports
|
|
435
493
|
lexer.exports = lexed.exports
|
|
436
494
|
}
|
|
437
495
|
|
|
438
496
|
yield {
|
|
439
|
-
dependency:
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
497
|
+
dependency: withWASI(
|
|
498
|
+
{
|
|
499
|
+
url: exports.alias(url, opts),
|
|
500
|
+
source,
|
|
501
|
+
type,
|
|
502
|
+
naturalType: naturalModuleType(url, attributes, info, type, opts),
|
|
503
|
+
imports: compressImportsMap(imports),
|
|
504
|
+
lexer
|
|
505
|
+
},
|
|
506
|
+
opts
|
|
507
|
+
)
|
|
447
508
|
}
|
|
448
509
|
|
|
449
510
|
return true
|
|
@@ -491,8 +552,10 @@ exports.link = function* (
|
|
|
491
552
|
visited,
|
|
492
553
|
opts = {}
|
|
493
554
|
) {
|
|
494
|
-
|
|
495
|
-
const specifier = entry.attributes
|
|
555
|
+
for (const name of ['imports', 'wasi']) {
|
|
556
|
+
const specifier = entry.attributes[name]
|
|
557
|
+
|
|
558
|
+
if (!specifier) continue
|
|
496
559
|
|
|
497
560
|
yield* resolveImport(
|
|
498
561
|
{ type: 0, specifier, names: [], attributes: {}, position: [0, 0, 0] },
|
|
@@ -515,7 +578,8 @@ function* resolveImport(entry, specifier, condition, parentURL, imports, artifac
|
|
|
515
578
|
builtinProtocol = 'builtin:',
|
|
516
579
|
linkedProtocol = 'linked:',
|
|
517
580
|
deferredProtocol = 'deferred:',
|
|
518
|
-
deferUnresolved = false
|
|
581
|
+
deferUnresolved = false,
|
|
582
|
+
loadable = null
|
|
519
583
|
} = opts
|
|
520
584
|
|
|
521
585
|
const matchedConditions = []
|
|
@@ -524,7 +588,11 @@ function* resolveImport(entry, specifier, condition, parentURL, imports, artifac
|
|
|
524
588
|
|
|
525
589
|
matchedConditions.push(condition)
|
|
526
590
|
|
|
527
|
-
const resolver = resolve(
|
|
591
|
+
const resolver = resolve(
|
|
592
|
+
entry,
|
|
593
|
+
parentURL,
|
|
594
|
+
condition === 'addon' && opts.addonHost ? { ...opts, hosts: [opts.addonHost] } : opts
|
|
595
|
+
)
|
|
528
596
|
const candidates = []
|
|
529
597
|
|
|
530
598
|
let next = resolver.next()
|
|
@@ -548,6 +616,8 @@ function* resolveImport(entry, specifier, condition, parentURL, imports, artifac
|
|
|
548
616
|
url.protocol === linkedProtocol ||
|
|
549
617
|
url.protocol === deferredProtocol
|
|
550
618
|
) {
|
|
619
|
+
if (condition === 'addon') assertImportedAddonContext(url, imports, parentURL, opts)
|
|
620
|
+
|
|
551
621
|
addResolution(imports, specifier, matchedConditions, url)
|
|
552
622
|
|
|
553
623
|
resolved = true
|
|
@@ -565,7 +635,7 @@ function* resolveImport(entry, specifier, condition, parentURL, imports, artifac
|
|
|
565
635
|
condition === 'addon' ||
|
|
566
636
|
moduleType(url, entry.attributes, null, opts) === constants.ADDON
|
|
567
637
|
) {
|
|
568
|
-
let exists = yield { probe: url }
|
|
638
|
+
let exists = isWebAssembly(url) ? undefined : yield { probe: url }
|
|
569
639
|
let source = null
|
|
570
640
|
|
|
571
641
|
if (exists === undefined) {
|
|
@@ -573,13 +643,15 @@ function* resolveImport(entry, specifier, condition, parentURL, imports, artifac
|
|
|
573
643
|
exists = source !== null
|
|
574
644
|
}
|
|
575
645
|
|
|
576
|
-
if (exists)
|
|
577
|
-
|
|
646
|
+
if (exists) resolution = yield* postresolve(url)
|
|
647
|
+
|
|
648
|
+
if (exists && (loadable === null || loadable(resolution))) {
|
|
649
|
+
assertImportedAddonContext(resolution, imports, parentURL, opts)
|
|
578
650
|
|
|
579
651
|
addResolution(imports, specifier, matchedConditions, exports.alias(resolution, opts))
|
|
580
652
|
|
|
581
653
|
yield {
|
|
582
|
-
children: exports.module(resolution, source, {}, artifacts, visited, {
|
|
654
|
+
children: exports.module(resolution, source, { type: 'addon' }, artifacts, visited, {
|
|
583
655
|
...opts,
|
|
584
656
|
probed: true
|
|
585
657
|
}),
|
|
@@ -608,6 +680,10 @@ function* resolveImport(entry, specifier, condition, parentURL, imports, artifac
|
|
|
608
680
|
attributes = { ...attributes, imports: imports[attributes.imports].default }
|
|
609
681
|
}
|
|
610
682
|
|
|
683
|
+
if (attributes.wasi) {
|
|
684
|
+
attributes = { ...attributes, wasi: imports[attributes.wasi].default }
|
|
685
|
+
}
|
|
686
|
+
|
|
611
687
|
yield {
|
|
612
688
|
children: exports.module(resolution, source, attributes, artifacts, visited, {
|
|
613
689
|
...opts,
|
|
@@ -657,7 +733,109 @@ function* resolveImport(entry, specifier, condition, parentURL, imports, artifac
|
|
|
657
733
|
}
|
|
658
734
|
}
|
|
659
735
|
|
|
660
|
-
const ADDON_EXTENSION = /\.(bare|node)$/
|
|
736
|
+
const ADDON_EXTENSION = /\.(bare|node|wasm)$/
|
|
737
|
+
|
|
738
|
+
function importAttributes(lexed, specifier, imports) {
|
|
739
|
+
const attributes = {}
|
|
740
|
+
|
|
741
|
+
if (lexed === null) return attributes
|
|
742
|
+
|
|
743
|
+
for (const entry of lexed.imports) {
|
|
744
|
+
if (entry.specifier !== specifier) continue
|
|
745
|
+
|
|
746
|
+
for (const name of ['type', 'host', 'wasi']) {
|
|
747
|
+
if (typeof entry.attributes[name] === 'string') attributes[name] = entry.attributes[name]
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
if (attributes.wasi !== undefined) attributes.wasi = defaultResolution(imports[attributes.wasi])
|
|
752
|
+
|
|
753
|
+
return attributes
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
function defaultResolution(entry) {
|
|
757
|
+
return typeof entry === 'string' ? entry : entry.default
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
// Only hosts that load on any platform may be asked for, so that an import
|
|
761
|
+
// can't pick the native addons of another platform.
|
|
762
|
+
const portableHosts = ['wasi-wasm32']
|
|
763
|
+
|
|
764
|
+
function addonHostForAttribute(host) {
|
|
765
|
+
if (portableHosts.includes(host)) return host
|
|
766
|
+
|
|
767
|
+
throw errors.UNKNOWN_ADDON_HOST(`Addon host '${host}' is not supported`)
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
function contextKey(host, wasi) {
|
|
771
|
+
return JSON.stringify([host, wasi])
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
function visitedFor(contexts, key) {
|
|
775
|
+
let visited = contexts.get(key)
|
|
776
|
+
|
|
777
|
+
if (visited === undefined) contexts.set(key, (visited = new Set()))
|
|
778
|
+
|
|
779
|
+
return visited
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
function withWASI(dependency, opts) {
|
|
783
|
+
const { addonWASI = null } = opts
|
|
784
|
+
|
|
785
|
+
if (addonWASI !== null && dependency.type === constants.ADDON && isWebAssembly(dependency.url)) {
|
|
786
|
+
dependency.wasi = addonWASI
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
return dependency
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
function assertImportedAddonContext(url, imports, parentURL, opts) {
|
|
793
|
+
const { addonHost = null, addonWASI = null, addonContexts } = opts
|
|
794
|
+
|
|
795
|
+
exports.assertAddonContext(
|
|
796
|
+
url,
|
|
797
|
+
imports['#package'] || parentURL.href,
|
|
798
|
+
{ host: addonHost, wasi: addonWASI },
|
|
799
|
+
addonContexts
|
|
800
|
+
)
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
exports.assertAddonContext = function assertAddonContext(url, scope, context, addonContexts) {
|
|
804
|
+
if (url.protocol === 'builtin:') return
|
|
805
|
+
|
|
806
|
+
const { host = null } = context
|
|
807
|
+
|
|
808
|
+
if (host !== null && !isWebAssembly(url)) {
|
|
809
|
+
throw errors.ADDON_HOST_INCOMPATIBLE(
|
|
810
|
+
`Addon '${url.href}' is not a WebAssembly addon, but '${host}' was asked for`
|
|
811
|
+
)
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
// Only WebAssembly addons are given WASI capabilities.
|
|
815
|
+
const wasi = isWebAssembly(url) ? context.wasi || null : null
|
|
816
|
+
|
|
817
|
+
const recorded = addonContexts.get(scope)
|
|
818
|
+
|
|
819
|
+
if (recorded === undefined) {
|
|
820
|
+
addonContexts.set(scope, { host, wasi })
|
|
821
|
+
} else if (recorded.host !== host) {
|
|
822
|
+
throw errors.ADDON_HOST_INCOMPATIBLE(
|
|
823
|
+
`Addons of '${scope}' are already resolved for ${recorded.host === null ? 'the default hosts' : `'${recorded.host}'`}`
|
|
824
|
+
)
|
|
825
|
+
} else if (recorded.wasi !== wasi) {
|
|
826
|
+
throw errors.ADDON_WASI_INCOMPATIBLE(
|
|
827
|
+
`Addons of '${scope}' are already given ${recorded.wasi === null ? 'the default WASI capabilities' : `the WASI capabilities of '${recorded.wasi}'`}`
|
|
828
|
+
)
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
function isJavaScript(type) {
|
|
833
|
+
return type === constants.SCRIPT || type === constants.MODULE
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
function isWebAssembly(url) {
|
|
837
|
+
return url.pathname.endsWith('.wasm')
|
|
838
|
+
}
|
|
661
839
|
|
|
662
840
|
exports.addons = function* (parentURL, artifacts, visited, opts = {}) {
|
|
663
841
|
let yielded = false
|
package/lib/errors.js
CHANGED
|
@@ -49,6 +49,18 @@ module.exports = class ModuleTraverseError extends Error {
|
|
|
49
49
|
return new ModuleTraverseError(msg, ModuleTraverseError.TYPE_INCOMPATIBLE)
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
+
static UNKNOWN_ADDON_HOST(msg) {
|
|
53
|
+
return new ModuleTraverseError(msg, ModuleTraverseError.UNKNOWN_ADDON_HOST)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
static ADDON_HOST_INCOMPATIBLE(msg) {
|
|
57
|
+
return new ModuleTraverseError(msg, ModuleTraverseError.ADDON_HOST_INCOMPATIBLE)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
static ADDON_WASI_INCOMPATIBLE(msg) {
|
|
61
|
+
return new ModuleTraverseError(msg, ModuleTraverseError.ADDON_WASI_INCOMPATIBLE)
|
|
62
|
+
}
|
|
63
|
+
|
|
52
64
|
static AMBIGUOUS_MODULE_TYPE(msg) {
|
|
53
65
|
return new ModuleTraverseError(msg, ModuleTraverseError.AMBIGUOUS_MODULE_TYPE)
|
|
54
66
|
}
|
package/lib/resolve/bare.js
CHANGED
|
@@ -3,7 +3,7 @@ const resolve = require('../resolve')
|
|
|
3
3
|
const runtime = require('../runtime')
|
|
4
4
|
|
|
5
5
|
module.exports = function (entry, parentURL, opts = {}) {
|
|
6
|
-
const { linked = false, host = runtime.host, hosts = [host] } = opts
|
|
6
|
+
const { linked = false, host = runtime.host, hosts = [host, 'wasi-wasm32'] } = opts
|
|
7
7
|
|
|
8
8
|
let directories = false
|
|
9
9
|
let extensions
|