@esmx/import 3.0.0-rc.116 → 3.0.0-rc.118

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.
@@ -0,0 +1,653 @@
1
+ import fs from "node:fs";
2
+ import os from "node:os";
3
+ import path from "node:path";
4
+ import { afterEach, beforeEach, describe, expect, it } from "vitest";
5
+ import { createVmImport } from "./import-vm.mjs";
6
+ describe("createVmImport", () => {
7
+ let tempDir;
8
+ let vmImport;
9
+ beforeEach(() => {
10
+ tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "import-vm-test-"));
11
+ vmImport = createVmImport(new URL(`file://${tempDir}/`));
12
+ });
13
+ afterEach(() => {
14
+ fs.rmSync(tempDir, { recursive: true, force: true });
15
+ });
16
+ describe("basic module loading", () => {
17
+ it("should load a simple ES module", async () => {
18
+ const modulePath = path.join(tempDir, "simple.mjs");
19
+ fs.writeFileSync(
20
+ modulePath,
21
+ `export const value = 42;
22
+ export default 'hello';`
23
+ );
24
+ const namespace = await vmImport(
25
+ "./simple.mjs",
26
+ `file://${tempDir}/entry.mjs`
27
+ );
28
+ expect(namespace.value).toBe(42);
29
+ expect(namespace.default).toBe("hello");
30
+ });
31
+ it("should load a module with multiple exports", async () => {
32
+ const modulePath = path.join(tempDir, "math.mjs");
33
+ fs.writeFileSync(
34
+ modulePath,
35
+ `export const add = (a, b) => a + b;
36
+ export const mul = (a, b) => a * b;`
37
+ );
38
+ const namespace = await vmImport(
39
+ "./math.mjs",
40
+ `file://${tempDir}/entry.mjs`
41
+ );
42
+ expect(namespace.add(2, 3)).toBe(5);
43
+ expect(namespace.mul(4, 5)).toBe(20);
44
+ });
45
+ it("should provide correct import.meta", async () => {
46
+ const modulePath = path.join(tempDir, "meta.mjs");
47
+ fs.writeFileSync(
48
+ modulePath,
49
+ `export const url = import.meta.url;
50
+ export const filename = import.meta.filename;`
51
+ );
52
+ const namespace = await vmImport(
53
+ "./meta.mjs",
54
+ `file://${tempDir}/entry.mjs`
55
+ );
56
+ expect(namespace.url).toMatch(/^file:\/\/\//);
57
+ expect(namespace.url).toContain("meta.mjs");
58
+ expect(namespace.filename).toBe(modulePath);
59
+ });
60
+ });
61
+ describe("module dependencies", () => {
62
+ it("should load a module that imports another module", async () => {
63
+ fs.writeFileSync(
64
+ path.join(tempDir, "helper.mjs"),
65
+ `export const helper = () => 'helper-value';`
66
+ );
67
+ fs.writeFileSync(
68
+ path.join(tempDir, "main.mjs"),
69
+ `import { helper } from './helper.mjs';
70
+ export const result = helper();`
71
+ );
72
+ const namespace = await vmImport(
73
+ "./main.mjs",
74
+ `file://${tempDir}/entry.mjs`
75
+ );
76
+ expect(namespace.result).toBe("helper-value");
77
+ });
78
+ it("should load deeply nested module chains", async () => {
79
+ fs.writeFileSync(
80
+ path.join(tempDir, "a.mjs"),
81
+ `export const a = 'A';`
82
+ );
83
+ fs.writeFileSync(
84
+ path.join(tempDir, "b.mjs"),
85
+ `import { a } from './a.mjs';
86
+ export const b = a + 'B';`
87
+ );
88
+ fs.writeFileSync(
89
+ path.join(tempDir, "c.mjs"),
90
+ `import { b } from './b.mjs';
91
+ export const c = b + 'C';`
92
+ );
93
+ const namespace = await vmImport(
94
+ "./c.mjs",
95
+ `file://${tempDir}/entry.mjs`
96
+ );
97
+ expect(namespace.c).toBe("ABC");
98
+ });
99
+ });
100
+ describe("circular dependencies", () => {
101
+ it("should handle A -> B -> A circular reference", async () => {
102
+ fs.writeFileSync(
103
+ path.join(tempDir, "a.mjs"),
104
+ `import * as b from './b.mjs';
105
+ export const a = 'A';
106
+ export const getB = () => b.b;`
107
+ );
108
+ fs.writeFileSync(
109
+ path.join(tempDir, "b.mjs"),
110
+ `import * as a from './a.mjs';
111
+ export const b = 'B';
112
+ export const getA = () => a.a;`
113
+ );
114
+ const namespace = await vmImport(
115
+ "./a.mjs",
116
+ `file://${tempDir}/entry.mjs`
117
+ );
118
+ expect(namespace.a).toBe("A");
119
+ expect(namespace.getB()).toBe("B");
120
+ });
121
+ it("should handle self-reference (A -> A)", async () => {
122
+ fs.writeFileSync(
123
+ path.join(tempDir, "self.mjs"),
124
+ `import * as self from './self.mjs';
125
+ export const foo = 'bar';
126
+ export const hasSelf = () => typeof self === 'object';`
127
+ );
128
+ const namespace = await vmImport(
129
+ "./self.mjs",
130
+ `file://${tempDir}/entry.mjs`
131
+ );
132
+ expect(namespace.foo).toBe("bar");
133
+ expect(namespace.hasSelf()).toBe(true);
134
+ });
135
+ it("should handle three-way circular reference", async () => {
136
+ fs.writeFileSync(
137
+ path.join(tempDir, "x.mjs"),
138
+ `import * as y from './y.mjs';
139
+ export const x = 'X';
140
+ export const getY = () => y.y;`
141
+ );
142
+ fs.writeFileSync(
143
+ path.join(tempDir, "y.mjs"),
144
+ `import * as z from './z.mjs';
145
+ export const y = 'Y';
146
+ export const getZ = () => z.z;`
147
+ );
148
+ fs.writeFileSync(
149
+ path.join(tempDir, "z.mjs"),
150
+ `import * as x from './x.mjs';
151
+ export const z = 'Z';
152
+ export const getX = () => x.x;`
153
+ );
154
+ const namespace = await vmImport(
155
+ "./x.mjs",
156
+ `file://${tempDir}/entry.mjs`
157
+ );
158
+ expect(namespace.x).toBe("X");
159
+ expect(namespace.getY()).toBe("Y");
160
+ });
161
+ });
162
+ describe("builtin modules", () => {
163
+ it("should load node:path module", async () => {
164
+ const namespace = await vmImport(
165
+ "node:path",
166
+ `file://${tempDir}/entry.mjs`
167
+ );
168
+ expect(typeof namespace.join).toBe("function");
169
+ expect(namespace.join("/a", "b")).toBe(path.join("/a", "b"));
170
+ });
171
+ it("should load node:fs module", async () => {
172
+ const namespace = await vmImport(
173
+ "node:fs",
174
+ `file://${tempDir}/entry.mjs`
175
+ );
176
+ expect(typeof namespace.readFileSync).toBe("function");
177
+ });
178
+ it("should load node:os module", async () => {
179
+ const namespace = await vmImport(
180
+ "node:os",
181
+ `file://${tempDir}/entry.mjs`
182
+ );
183
+ expect(typeof namespace.platform).toBe("function");
184
+ });
185
+ it("should load builtin module as dependency of user module", async () => {
186
+ fs.writeFileSync(
187
+ path.join(tempDir, "uses-path.mjs"),
188
+ `import { join } from 'node:path';
189
+ export const result = join('/test', 'file.txt');`
190
+ );
191
+ const namespace = await vmImport(
192
+ "./uses-path.mjs",
193
+ `file://${tempDir}/entry.mjs`
194
+ );
195
+ expect(namespace.result).toBe(path.join("/test", "file.txt"));
196
+ });
197
+ });
198
+ describe("caching", () => {
199
+ it("should cache loaded modules within a single vmImport call", async () => {
200
+ fs.writeFileSync(
201
+ path.join(tempDir, "counter.mjs"),
202
+ `export let count = 0;
203
+ export const increment = () => ++count;`
204
+ );
205
+ fs.writeFileSync(
206
+ path.join(tempDir, "consumer.mjs"),
207
+ `import { count, increment } from './counter.mjs';
208
+ export const getCount = () => count;
209
+ export const inc = increment;`
210
+ );
211
+ const ns = await vmImport(
212
+ "./consumer.mjs",
213
+ `file://${tempDir}/entry.mjs`
214
+ );
215
+ expect(ns.getCount()).toBe(0);
216
+ ns.inc();
217
+ expect(ns.getCount()).toBe(1);
218
+ });
219
+ it("should not share cache across separate vmImport calls", async () => {
220
+ fs.writeFileSync(
221
+ path.join(tempDir, "state.mjs"),
222
+ `export let value = Math.random();`
223
+ );
224
+ const ns1 = await vmImport(
225
+ "./state.mjs",
226
+ `file://${tempDir}/a.mjs`
227
+ );
228
+ const ns2 = await vmImport(
229
+ "./state.mjs",
230
+ `file://${tempDir}/b.mjs`
231
+ );
232
+ expect(ns1.value).not.toBe(ns2.value);
233
+ });
234
+ it("should cache shared dependencies within single vmImport call", async () => {
235
+ fs.writeFileSync(
236
+ path.join(tempDir, "shared-dep.mjs"),
237
+ `export let counter = 0;
238
+ export const inc = () => ++counter;`
239
+ );
240
+ fs.writeFileSync(
241
+ path.join(tempDir, "user-a.mjs"),
242
+ `import { counter, inc } from './shared-dep.mjs';
243
+ export const getCount = () => counter;
244
+ export const increment = inc;`
245
+ );
246
+ fs.writeFileSync(
247
+ path.join(tempDir, "user-b.mjs"),
248
+ `import { counter, inc } from './shared-dep.mjs';
249
+ export const getCount = () => counter;
250
+ export const increment = inc;`
251
+ );
252
+ fs.writeFileSync(
253
+ path.join(tempDir, "main-cache.mjs"),
254
+ `import * as a from './user-a.mjs';
255
+ import * as b from './user-b.mjs';
256
+ export const aCount = a.getCount;
257
+ export const bCount = b.getCount;
258
+ export const aInc = a.increment;
259
+ export const bInc = b.increment;`
260
+ );
261
+ const ns = await vmImport(
262
+ "./main-cache.mjs",
263
+ `file://${tempDir}/entry.mjs`
264
+ );
265
+ expect(ns.aCount()).toBe(0);
266
+ expect(ns.bCount()).toBe(0);
267
+ ns.aInc();
268
+ expect(ns.aCount()).toBe(1);
269
+ expect(ns.bCount()).toBe(1);
270
+ ns.bInc();
271
+ expect(ns.aCount()).toBe(2);
272
+ expect(ns.bCount()).toBe(2);
273
+ });
274
+ });
275
+ describe("dynamic import", () => {
276
+ it("should handle dynamic import() inside VM module", async () => {
277
+ fs.writeFileSync(
278
+ path.join(tempDir, "lazy.mjs"),
279
+ `export const lazy = 'lazy-value';`
280
+ );
281
+ fs.writeFileSync(
282
+ path.join(tempDir, "dynamic.mjs"),
283
+ `export const result = await import('./lazy.mjs').then(m => m.lazy);`
284
+ );
285
+ const namespace = await vmImport(
286
+ "./dynamic.mjs",
287
+ `file://${tempDir}/entry.mjs`
288
+ );
289
+ expect(namespace.result).toBe("lazy-value");
290
+ });
291
+ it("should handle dynamic import of already cached module", async () => {
292
+ fs.writeFileSync(
293
+ path.join(tempDir, "dep.mjs"),
294
+ `export const value = 'dep-value';`
295
+ );
296
+ fs.writeFileSync(
297
+ path.join(tempDir, "dynamic-cache.mjs"),
298
+ `import { value } from './dep.mjs';
299
+ export const staticValue = value;
300
+ export const dynamicValue = await import('./dep.mjs').then(m => m.value);`
301
+ );
302
+ const namespace = await vmImport(
303
+ "./dynamic-cache.mjs",
304
+ `file://${tempDir}/entry.mjs`
305
+ );
306
+ expect(namespace.staticValue).toBe("dep-value");
307
+ expect(namespace.dynamicValue).toBe("dep-value");
308
+ });
309
+ });
310
+ describe("error handling", () => {
311
+ it("should throw FileReadError for non-existent module", async () => {
312
+ await expect(
313
+ vmImport("./non-existent.mjs", `file://${tempDir}/entry.mjs`)
314
+ ).rejects.toThrow("Failed to read module");
315
+ });
316
+ it("should throw for syntax errors in module", async () => {
317
+ fs.writeFileSync(
318
+ path.join(tempDir, "bad-syntax.mjs"),
319
+ `export const x = {`
320
+ // 语法错误
321
+ );
322
+ await expect(
323
+ vmImport("./bad-syntax.mjs", `file://${tempDir}/entry.mjs`)
324
+ ).rejects.toThrow();
325
+ });
326
+ it("should throw for runtime errors during module evaluation", async () => {
327
+ fs.writeFileSync(
328
+ path.join(tempDir, "runtime-error.mjs"),
329
+ `throw new Error('Runtime error');`
330
+ );
331
+ await expect(
332
+ vmImport("./runtime-error.mjs", `file://${tempDir}/entry.mjs`)
333
+ ).rejects.toThrow("Runtime error");
334
+ });
335
+ });
336
+ describe("sandbox", () => {
337
+ it("should create isolated context", async () => {
338
+ fs.writeFileSync(
339
+ path.join(tempDir, "global.mjs"),
340
+ `if (typeof globalThis.customVar === 'undefined') {
341
+ globalThis.customVar = 'set';
342
+ }
343
+ export const hasVar = globalThis.customVar;`
344
+ );
345
+ const ns1 = await vmImport(
346
+ "./global.mjs",
347
+ `file://${tempDir}/entry.mjs`,
348
+ {}
349
+ );
350
+ expect(ns1.hasVar).toBe("set");
351
+ const ns2 = await vmImport(
352
+ "./global.mjs",
353
+ `file://${tempDir}/entry.mjs`,
354
+ {}
355
+ );
356
+ expect(ns2.hasVar).toBe("set");
357
+ });
358
+ });
359
+ describe("import map", () => {
360
+ it("should resolve specifiers using import map", async () => {
361
+ fs.writeFileSync(
362
+ path.join(tempDir, "lib.mjs"),
363
+ `export const lib = 'library';`
364
+ );
365
+ const importMap = {
366
+ imports: {
367
+ "#lib": `file://${tempDir}/lib.mjs`
368
+ }
369
+ };
370
+ const vmImportWithMap = createVmImport(
371
+ new URL(`file://${tempDir}/`),
372
+ importMap
373
+ );
374
+ fs.writeFileSync(
375
+ path.join(tempDir, "app.mjs"),
376
+ `import { lib } from '#lib';
377
+ export const result = lib;`
378
+ );
379
+ const namespace = await vmImportWithMap(
380
+ "./app.mjs",
381
+ `file://${tempDir}/entry.mjs`
382
+ );
383
+ expect(namespace.result).toBe("library");
384
+ });
385
+ });
386
+ describe("in-memory filesystem", () => {
387
+ let memfs;
388
+ let memImport;
389
+ const memBase = process.platform === "win32" ? "C:\\project" : "/project";
390
+ const memBaseURL = new URL(`file://${memBase}/`);
391
+ const memEntry = `file://${memBase}/entry.mjs`;
392
+ const memPath = (filename) => path.join(memBase, filename);
393
+ const createMemRead = () => (filepath) => {
394
+ const content = memfs.get(filepath);
395
+ if (content === void 0) {
396
+ const err = new Error(
397
+ `ENOENT: no such file or directory, open '${filepath}'`
398
+ );
399
+ err.code = "ENOENT";
400
+ throw err;
401
+ }
402
+ return content;
403
+ };
404
+ beforeEach(() => {
405
+ memfs = /* @__PURE__ */ new Map();
406
+ memImport = createVmImport(
407
+ memBaseURL,
408
+ {},
409
+ {
410
+ readFileSync: createMemRead()
411
+ }
412
+ );
413
+ });
414
+ it("should load a simple ES module from memory", async () => {
415
+ memfs.set(memPath("simple.mjs"), `export const value = 42;`);
416
+ const namespace = await memImport("./simple.mjs", memEntry);
417
+ expect(namespace.value).toBe(42);
418
+ });
419
+ it("should load a module with multiple exports from memory", async () => {
420
+ memfs.set(
421
+ memPath("math.mjs"),
422
+ `export const add = (a, b) => a + b;
423
+ export const mul = (a, b) => a * b;`
424
+ );
425
+ const namespace = await memImport("./math.mjs", memEntry);
426
+ expect(namespace.add(2, 3)).toBe(5);
427
+ expect(namespace.mul(4, 5)).toBe(20);
428
+ });
429
+ it("should load a module that imports another module from memory", async () => {
430
+ memfs.set(
431
+ memPath("helper.mjs"),
432
+ `export const helper = () => 'helper-value';`
433
+ );
434
+ memfs.set(
435
+ memPath("main.mjs"),
436
+ `import { helper } from './helper.mjs';
437
+ export const result = helper();`
438
+ );
439
+ const namespace = await memImport("./main.mjs", memEntry);
440
+ expect(namespace.result).toBe("helper-value");
441
+ });
442
+ it("should handle deeply nested module chains from memory", async () => {
443
+ memfs.set(memPath("a.mjs"), `export const a = 'A';`);
444
+ memfs.set(
445
+ memPath("b.mjs"),
446
+ `import { a } from './a.mjs';
447
+ export const b = a + 'B';`
448
+ );
449
+ memfs.set(
450
+ memPath("c.mjs"),
451
+ `import { b } from './b.mjs';
452
+ export const c = b + 'C';`
453
+ );
454
+ const namespace = await memImport("./c.mjs", memEntry);
455
+ expect(namespace.c).toBe("ABC");
456
+ });
457
+ it("should handle A -> B -> A circular reference from memory", async () => {
458
+ memfs.set(
459
+ memPath("a.mjs"),
460
+ `import * as b from './b.mjs';
461
+ export const a = 'A';
462
+ export const getB = () => b.b;`
463
+ );
464
+ memfs.set(
465
+ memPath("b.mjs"),
466
+ `import * as a from './a.mjs';
467
+ export const b = 'B';
468
+ export const getA = () => a.a;`
469
+ );
470
+ const namespace = await memImport("./a.mjs", memEntry);
471
+ expect(namespace.a).toBe("A");
472
+ expect(namespace.getB()).toBe("B");
473
+ });
474
+ it("should cache loaded modules within a single memImport call", async () => {
475
+ memfs.set(
476
+ memPath("counter.mjs"),
477
+ `export let count = 0;
478
+ export const increment = () => ++count;`
479
+ );
480
+ memfs.set(
481
+ memPath("consumer.mjs"),
482
+ `import { count, increment } from './counter.mjs';
483
+ export const getCount = () => count;
484
+ export const inc = increment;`
485
+ );
486
+ const ns = await memImport("./consumer.mjs", memEntry);
487
+ expect(ns.getCount()).toBe(0);
488
+ ns.inc();
489
+ expect(ns.getCount()).toBe(1);
490
+ });
491
+ it("should handle dynamic import() inside VM module from memory", async () => {
492
+ memfs.set(memPath("lazy.mjs"), `export const lazy = 'lazy-value';`);
493
+ memfs.set(
494
+ memPath("dynamic.mjs"),
495
+ `export const result = await import('./lazy.mjs').then(m => m.lazy);`
496
+ );
497
+ const namespace = await memImport("./dynamic.mjs", memEntry);
498
+ expect(namespace.result).toBe("lazy-value");
499
+ });
500
+ it("should throw FileReadError for non-existent module in memory", async () => {
501
+ await expect(
502
+ memImport("./non-existent.mjs", memEntry)
503
+ ).rejects.toThrow("Failed to read module");
504
+ });
505
+ it("should resolve specifiers using import map with memory fs", async () => {
506
+ memfs.set(memPath("lib.mjs"), `export const lib = 'library';`);
507
+ const importMap = {
508
+ imports: {
509
+ "#lib": `file://${memBase}/lib.mjs`
510
+ }
511
+ };
512
+ const vmImportWithMap = createVmImport(memBaseURL, importMap, {
513
+ readFileSync: createMemRead()
514
+ });
515
+ memfs.set(
516
+ memPath("app.mjs"),
517
+ `import { lib } from '#lib';
518
+ export const result = lib;`
519
+ );
520
+ const namespace = await vmImportWithMap("./app.mjs", memEntry);
521
+ expect(namespace.result).toBe("library");
522
+ });
523
+ it("should provide correct import.meta with memory fs", async () => {
524
+ memfs.set(
525
+ memPath("meta.mjs"),
526
+ `export const url = import.meta.url;
527
+ export const filename = import.meta.filename;`
528
+ );
529
+ const namespace = await memImport("./meta.mjs", memEntry);
530
+ expect(namespace.url).toBe(new URL("meta.mjs", memBaseURL).href);
531
+ expect(namespace.filename).toBe(memPath("meta.mjs"));
532
+ });
533
+ });
534
+ describe("style asset imports", () => {
535
+ it("does not parse .css as JS \u2014 side-effect import resolves cleanly", async () => {
536
+ const cssPath = path.join(tempDir, "styles.css");
537
+ fs.writeFileSync(
538
+ cssPath,
539
+ "/* contains a dot selector \u2014 would crash the JS parser */\n.btn { color: red; }"
540
+ );
541
+ const consumerPath = path.join(tempDir, "consumer.mjs");
542
+ fs.writeFileSync(
543
+ consumerPath,
544
+ `import './styles.css';
545
+ export const ok = true;`
546
+ );
547
+ const namespace = await vmImport(
548
+ "./consumer.mjs",
549
+ `file://${tempDir}/entry.mjs`
550
+ );
551
+ expect(namespace.ok).toBe(true);
552
+ });
553
+ it("exposes the asset URL as default + href named export", async () => {
554
+ const cssPath = path.join(tempDir, "theme.css");
555
+ fs.writeFileSync(cssPath, ":root { --x: 1; }");
556
+ const consumerPath = path.join(tempDir, "consumer.mjs");
557
+ fs.writeFileSync(
558
+ consumerPath,
559
+ `import url, { href } from './theme.css';
560
+ export { url, href };`
561
+ );
562
+ const namespace = await vmImport(
563
+ "./consumer.mjs",
564
+ `file://${tempDir}/entry.mjs`
565
+ );
566
+ const expected = new URL("theme.css", `file://${tempDir}/`).href;
567
+ expect(namespace.url).toBe(expected);
568
+ expect(namespace.href).toBe(expected);
569
+ });
570
+ it.each([
571
+ "scss",
572
+ "sass",
573
+ "less",
574
+ "stylus",
575
+ "styl",
576
+ "pcss",
577
+ "postcss"
578
+ ])("treats .%s as a style asset", async (ext) => {
579
+ const assetPath = path.join(tempDir, `styles.${ext}`);
580
+ fs.writeFileSync(
581
+ assetPath,
582
+ ".x { color: red; } // would crash JS parser"
583
+ );
584
+ const consumerPath = path.join(tempDir, `c-${ext}.mjs`);
585
+ fs.writeFileSync(
586
+ consumerPath,
587
+ `import './styles.${ext}';
588
+ export const ok = true;`
589
+ );
590
+ const namespace = await vmImport(
591
+ `./c-${ext}.mjs`,
592
+ `file://${tempDir}/entry.mjs`
593
+ );
594
+ expect(namespace.ok).toBe(true);
595
+ });
596
+ it("tolerates query-string suffixes (?inline, ?url)", async () => {
597
+ const cssPath = path.join(tempDir, "q.css");
598
+ fs.writeFileSync(cssPath, ".q {}");
599
+ const consumerPath = path.join(tempDir, "qc.mjs");
600
+ fs.writeFileSync(
601
+ consumerPath,
602
+ `import './q.css?inline';
603
+ export const ok = true;`
604
+ );
605
+ const namespace = await vmImport(
606
+ "./qc.mjs",
607
+ `file://${tempDir}/entry.mjs`
608
+ );
609
+ expect(namespace.ok).toBe(true);
610
+ });
611
+ it("caches style modules \u2014 same URL imported twice yields one module", async () => {
612
+ const cssPath = path.join(tempDir, "shared.css");
613
+ fs.writeFileSync(cssPath, ".s {}");
614
+ const aPath = path.join(tempDir, "a.mjs");
615
+ const bPath = path.join(tempDir, "b.mjs");
616
+ const rootPath = path.join(tempDir, "root.mjs");
617
+ fs.writeFileSync(
618
+ aPath,
619
+ `import url from './shared.css';
620
+ export { url as a };`
621
+ );
622
+ fs.writeFileSync(
623
+ bPath,
624
+ `import url from './shared.css';
625
+ export { url as b };`
626
+ );
627
+ fs.writeFileSync(
628
+ rootPath,
629
+ `export { a } from './a.mjs';
630
+ export { b } from './b.mjs';`
631
+ );
632
+ const namespace = await vmImport(
633
+ "./root.mjs",
634
+ `file://${tempDir}/entry.mjs`
635
+ );
636
+ expect(namespace.a).toBeDefined();
637
+ expect(namespace.a).toBe(namespace.b);
638
+ });
639
+ it("does not affect non-style imports", async () => {
640
+ const jsPath = path.join(tempDir, "normal.mjs");
641
+ fs.writeFileSync(
642
+ jsPath,
643
+ 'export const value = 42;\nexport default "x";'
644
+ );
645
+ const namespace = await vmImport(
646
+ "./normal.mjs",
647
+ `file://${tempDir}/entry.mjs`
648
+ );
649
+ expect(namespace.value).toBe(42);
650
+ expect(namespace.default).toBe("x");
651
+ });
652
+ });
653
+ });
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
1
  export { CircularDependencyError, FileReadError, formatCircularDependency, formatModuleChain, ModuleLoadingError } from './error';
2
2
  export { createLoaderImport } from './import-loader';
3
- export { createVmImport } from './import-vm';
4
- export type { ImportMap, ScopesMap, SpecifierMap } from './types';
3
+ export { createVmImport, type VmImportOptions } from './import-vm';
4
+ export type { ImportMap, IntegrityMap, ScopesMap, SpecifierMap } from './types';
package/dist/types.d.ts CHANGED
@@ -1,7 +1,14 @@
1
1
  export type SpecifierMap = Record<string, string>;
2
2
  export type ScopesMap = Record<string, SpecifierMap>;
3
+ export type IntegrityMap = Record<string, string>;
3
4
  export interface ImportMap {
4
5
  imports?: SpecifierMap;
5
6
  scopes?: ScopesMap;
7
+ /**
8
+ * Subresource Integrity metadata, mapping module URLs to integrity hashes.
9
+ * Part of the Import Maps specification; ignored by browsers that do not
10
+ * support it.
11
+ */
12
+ integrity?: IntegrityMap;
6
13
  }
7
14
  export type ImportMapResolver = (specifier: string, parent: string) => string | null;