@esmx/core 3.0.0-rc.59 → 3.0.0-rc.60

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.
@@ -1,145 +1,1242 @@
1
- import { assert, expect, test } from 'vitest';
2
- import { getImportMap } from './import-map';
1
+ import { assert, describe, test } from 'vitest';
2
+ import { buildImportsMap, buildScopesMap, getImportMap } from './import-map';
3
+ import type { GetImportMapOptions, ImportMapManifest } from './import-map';
3
4
 
4
- test('should return empty import map when no manifests provided', async () => {
5
- assert.deepEqual(
6
- getImportMap({
7
- manifests: [],
8
- getScope(name) {
9
- return `/${name}/`;
10
- },
11
- getFile(name, file) {
12
- return `${name}/${file}`;
13
- }
14
- }),
15
- {
16
- imports: {},
17
- scopes: {}
18
- }
19
- );
20
- });
5
+ describe('buildImportsMap', () => {
6
+ test('should return empty object for empty manifests', () => {
7
+ const result = buildImportsMap([], (name, file) => `${name}/${file}`);
8
+ assert.deepEqual(result, {});
9
+ });
21
10
 
22
- test('should generate import map with remote exports and module scopes', async () => {
23
- const result = getImportMap({
24
- manifests: [
11
+ test('should process all exports including package exports', () => {
12
+ const manifests: ImportMapManifest[] = [
25
13
  {
26
- name: 'ssr-vue2-remote',
14
+ name: 'test-module',
27
15
  imports: {},
28
16
  exports: {
29
- 'src/entry.server': {
30
- name: 'src/entry.server',
31
- rewrite: true,
32
- file: 'src/entry.server.mjs',
33
- identifier: 'ssr-vue2-remote/src/entry.server'
17
+ component: {
18
+ name: 'component',
19
+ pkg: false,
20
+ file: 'component.js',
21
+ identifier: 'test-module/component'
34
22
  },
35
23
  vue: {
36
24
  name: 'vue',
37
- rewrite: false,
38
- file: 'vue.mjs',
39
- identifier: 'ssr-vue2-remote/vue'
25
+ pkg: true,
26
+ file: 'vue.js',
27
+ identifier: 'test-module/vue'
28
+ }
29
+ },
30
+ scopes: {}
31
+ }
32
+ ];
33
+
34
+ const result = buildImportsMap(
35
+ manifests,
36
+ (name, file) => `${name}/${file}`
37
+ );
38
+
39
+ assert.deepEqual(result, {
40
+ 'test-module/component': 'test-module/component.js',
41
+ 'test-module/vue': 'test-module/vue.js'
42
+ });
43
+ });
44
+
45
+ test('should handle user imports with existing identifiers', () => {
46
+ const manifests: ImportMapManifest[] = [
47
+ {
48
+ name: 'test-module',
49
+ imports: {
50
+ 'custom-name': 'test-module/component'
51
+ },
52
+ exports: {
53
+ component: {
54
+ name: 'component',
55
+ pkg: false,
56
+ file: 'component.js',
57
+ identifier: 'test-module/component'
58
+ }
59
+ },
60
+ scopes: {}
61
+ }
62
+ ];
63
+
64
+ const result = buildImportsMap(
65
+ manifests,
66
+ (name, file) => `${name}/${file}`
67
+ );
68
+
69
+ assert.deepEqual(result, {
70
+ 'test-module/component': 'test-module/component.js',
71
+ 'test-module/custom-name': 'test-module/component.js'
72
+ });
73
+ });
74
+
75
+ test('should handle user imports with non existing identifiers', () => {
76
+ const manifests: ImportMapManifest[] = [
77
+ {
78
+ name: 'test-module',
79
+ imports: {
80
+ external: 'https://cdn.com/lib.js'
81
+ },
82
+ exports: {
83
+ component: {
84
+ name: 'component',
85
+ pkg: false,
86
+ file: 'component.js',
87
+ identifier: 'test-module/component'
88
+ }
89
+ },
90
+ scopes: {}
91
+ }
92
+ ];
93
+
94
+ const result = buildImportsMap(
95
+ manifests,
96
+ (name, file) => `${name}/${file}`
97
+ );
98
+
99
+ assert.deepEqual(result, {
100
+ 'test-module/component': 'test-module/component.js',
101
+ 'test-module/external': 'https://cdn.com/lib.js'
102
+ });
103
+ });
104
+
105
+ test('should create aliases for index suffixes', () => {
106
+ const manifests: ImportMapManifest[] = [
107
+ {
108
+ name: 'test-module',
109
+ imports: {},
110
+ exports: {
111
+ 'src/index': {
112
+ name: 'src/index',
113
+ pkg: false,
114
+ file: 'src/index.js',
115
+ identifier: 'test-module/src/index'
116
+ }
117
+ },
118
+ scopes: {}
119
+ }
120
+ ];
121
+
122
+ const result = buildImportsMap(
123
+ manifests,
124
+ (name, file) => `${name}/${file}`
125
+ );
126
+
127
+ assert.deepEqual(result, {
128
+ 'test-module/src/index': 'test-module/src/index.js',
129
+ 'test-module/src': 'test-module/src/index.js'
130
+ });
131
+ });
132
+
133
+ test('should handle multiple manifests', () => {
134
+ const manifests: ImportMapManifest[] = [
135
+ {
136
+ name: 'module-a',
137
+ imports: {},
138
+ exports: {
139
+ utils: {
140
+ name: 'utils',
141
+ pkg: false,
142
+ file: 'utils.js',
143
+ identifier: 'module-a/utils'
144
+ }
145
+ },
146
+ scopes: {}
147
+ },
148
+ {
149
+ name: 'module-b',
150
+ imports: {
151
+ shared: 'module-a/utils'
152
+ },
153
+ exports: {},
154
+ scopes: {}
155
+ }
156
+ ];
157
+
158
+ const result = buildImportsMap(
159
+ manifests,
160
+ (name, file) => `${name}/${file}`
161
+ );
162
+
163
+ assert.deepEqual(result, {
164
+ 'module-a/utils': 'module-a/utils.js',
165
+ 'module-b/shared': 'module-a/utils.js'
166
+ });
167
+ });
168
+
169
+ test('should prioritize user imports', () => {
170
+ const manifests: ImportMapManifest[] = [
171
+ {
172
+ name: 'test-module',
173
+ imports: {
174
+ react: 'preact',
175
+ vue: './custom/vue.js'
176
+ },
177
+ exports: {
178
+ react: {
179
+ name: 'react',
180
+ pkg: false,
181
+ file: 'react.js',
182
+ identifier: 'test-module/react'
40
183
  },
184
+ vue: {
185
+ name: 'vue',
186
+ pkg: false,
187
+ file: 'vue.js',
188
+ identifier: 'test-module/vue'
189
+ }
190
+ },
191
+ scopes: {}
192
+ }
193
+ ];
194
+
195
+ const result = buildImportsMap(
196
+ manifests,
197
+ (name, file) => `${name}/${file}`
198
+ );
199
+
200
+ assert.deepEqual(result, {
201
+ 'test-module/react': 'preact',
202
+ 'test-module/vue': './custom/vue.js'
203
+ });
204
+ });
205
+
206
+ test('should handle mixed user import types', () => {
207
+ const manifests: ImportMapManifest[] = [
208
+ {
209
+ name: 'test-module',
210
+ imports: {
211
+ 'url-import': 'https://example.com/lib.js',
212
+ 'relative-import': './local/file.js',
213
+ 'identifier-import': 'test-module/component'
214
+ },
215
+ exports: {
216
+ component: {
217
+ name: 'component',
218
+ pkg: false,
219
+ file: 'component.js',
220
+ identifier: 'test-module/component'
221
+ }
222
+ },
223
+ scopes: {}
224
+ }
225
+ ];
226
+
227
+ const result = buildImportsMap(
228
+ manifests,
229
+ (name, file) => `${name}/${file}`
230
+ );
231
+
232
+ assert.deepEqual(result, {
233
+ 'test-module/component': 'test-module/component.js',
234
+ 'test-module/url-import': 'https://example.com/lib.js',
235
+ 'test-module/relative-import': './local/file.js',
236
+ 'test-module/identifier-import': 'test-module/component.js'
237
+ });
238
+ });
239
+
240
+ test('should handle empty exports', () => {
241
+ const manifests: ImportMapManifest[] = [
242
+ {
243
+ name: 'test-module',
244
+ imports: {
245
+ external: 'https://cdn.com/lib.js'
246
+ },
247
+ exports: {},
248
+ scopes: {}
249
+ }
250
+ ];
251
+
252
+ const result = buildImportsMap(
253
+ manifests,
254
+ (name, file) => `${name}/${file}`
255
+ );
256
+
257
+ assert.deepEqual(result, {
258
+ 'test-module/external': 'https://cdn.com/lib.js'
259
+ });
260
+ });
261
+
262
+ test('should handle empty imports', () => {
263
+ const manifests: ImportMapManifest[] = [
264
+ {
265
+ name: 'test-module',
266
+ imports: {},
267
+ exports: {
268
+ component: {
269
+ name: 'component',
270
+ pkg: false,
271
+ file: 'component.js',
272
+ identifier: 'test-module/component'
273
+ }
274
+ },
275
+ scopes: {}
276
+ }
277
+ ];
278
+
279
+ const result = buildImportsMap(
280
+ manifests,
281
+ (name, file) => `${name}/${file}`
282
+ );
283
+
284
+ assert.deepEqual(result, {
285
+ 'test-module/component': 'test-module/component.js'
286
+ });
287
+ });
288
+
289
+ test('should handle duplicate alias creation', () => {
290
+ const manifests: ImportMapManifest[] = [
291
+ {
292
+ name: 'test-module',
293
+ imports: {},
294
+ exports: {
41
295
  'src/components/index': {
42
296
  name: 'src/components/index',
43
- rewrite: true,
44
- file: 'src/components/index.mjs',
45
- identifier: 'ssr-vue2-remote/src/components/index'
297
+ pkg: false,
298
+ file: 'src/components/index.js',
299
+ identifier: 'test-module/src/components/index'
46
300
  }
47
- }
301
+ },
302
+ scopes: {}
303
+ }
304
+ ];
305
+
306
+ const result = buildImportsMap(
307
+ manifests,
308
+ (name, file) => `${name}/${file}`
309
+ );
310
+
311
+ assert.deepEqual(result, {
312
+ 'test-module/src/components/index':
313
+ 'test-module/src/components/index.js',
314
+ 'test-module/src/components': 'test-module/src/components/index.js'
315
+ });
316
+ });
317
+
318
+ test('should handle cross-module references', () => {
319
+ const manifests: ImportMapManifest[] = [
320
+ {
321
+ name: 'module-a',
322
+ imports: {},
323
+ exports: {
324
+ utils: {
325
+ name: 'utils',
326
+ pkg: false,
327
+ file: 'utils.js',
328
+ identifier: 'module-a/utils'
329
+ }
330
+ },
331
+ scopes: {}
48
332
  },
49
333
  {
50
- name: 'ssr-vue2-host',
334
+ name: 'module-b',
51
335
  imports: {
52
- vue: 'ssr-vue2-remote/vue'
53
- },
54
- exports: {}
55
- }
56
- ],
57
- getScope(name) {
58
- return `/${name}/`;
59
- },
60
- getFile(name, file) {
61
- return `${name}/${file}`;
62
- }
63
- });
64
- assert.deepEqual(result, {
65
- imports: {
66
- 'ssr-vue2-remote/src/entry.server':
67
- 'ssr-vue2-remote/src/entry.server.mjs',
68
- 'ssr-vue2-remote/vue': 'ssr-vue2-remote/vue.mjs',
69
- 'ssr-vue2-remote/src/components/index':
70
- 'ssr-vue2-remote/src/components/index.mjs',
71
- 'ssr-vue2-remote/src/components':
72
- 'ssr-vue2-remote/src/components/index.mjs'
73
- },
74
- scopes: {
75
- '/ssr-vue2-host/': {
76
- vue: 'ssr-vue2-remote/vue.mjs'
336
+ shared: 'module-a/utils'
337
+ },
338
+ exports: {},
339
+ scopes: {}
340
+ }
341
+ ];
342
+
343
+ const result = buildImportsMap(
344
+ manifests,
345
+ (name, file) => `${name}/${file}`
346
+ );
347
+
348
+ assert.deepEqual(result, {
349
+ 'module-a/utils': 'module-a/utils.js',
350
+ 'module-b/shared': 'module-a/utils.js'
351
+ });
352
+ });
353
+
354
+ test('should handle cross-module non-existent references', () => {
355
+ const manifests: ImportMapManifest[] = [
356
+ {
357
+ name: 'module-a',
358
+ imports: {},
359
+ exports: {
360
+ utils: {
361
+ name: 'utils',
362
+ pkg: false,
363
+ file: 'utils.js',
364
+ identifier: 'module-a/utils'
365
+ }
366
+ },
367
+ scopes: {}
77
368
  },
78
- '/ssr-vue2-remote/': { vue: 'ssr-vue2-remote/vue.mjs' }
79
- }
369
+ {
370
+ name: 'module-b',
371
+ imports: {
372
+ external: 'module-a/non-existent'
373
+ },
374
+ exports: {},
375
+ scopes: {}
376
+ }
377
+ ];
378
+
379
+ const result = buildImportsMap(
380
+ manifests,
381
+ (name, file) => `${name}/${file}`
382
+ );
383
+
384
+ assert.deepEqual(result, {
385
+ 'module-a/utils': 'module-a/utils.js',
386
+ 'module-b/external': 'module-a/non-existent'
387
+ });
388
+ });
389
+
390
+ test('should handle identifier conflicts across modules', () => {
391
+ const manifests: ImportMapManifest[] = [
392
+ {
393
+ name: 'module-a',
394
+ imports: {},
395
+ exports: {
396
+ utils: {
397
+ name: 'utils',
398
+ pkg: false,
399
+ file: 'utils-a.js',
400
+ identifier: 'module-a/utils'
401
+ }
402
+ },
403
+ scopes: {}
404
+ },
405
+ {
406
+ name: 'module-b',
407
+ imports: {},
408
+ exports: {
409
+ utils: {
410
+ name: 'utils',
411
+ pkg: false,
412
+ file: 'utils-b.js',
413
+ identifier: 'module-b/utils'
414
+ }
415
+ },
416
+ scopes: {}
417
+ }
418
+ ];
419
+
420
+ const result = buildImportsMap(
421
+ manifests,
422
+ (name, file) => `${name}/${file}`
423
+ );
424
+
425
+ assert.deepEqual(result, {
426
+ 'module-a/utils': 'module-a/utils-a.js',
427
+ 'module-b/utils': 'module-b/utils-b.js'
428
+ });
429
+ });
430
+
431
+ test('should handle user imports referencing aliased identifiers', () => {
432
+ const manifests: ImportMapManifest[] = [
433
+ {
434
+ name: 'test-module',
435
+ imports: {
436
+ 'alias-test': 'test-module/src/index'
437
+ },
438
+ exports: {
439
+ 'src/index': {
440
+ name: 'src/index',
441
+ pkg: false,
442
+ file: 'src/index.js',
443
+ identifier: 'test-module/src/index'
444
+ }
445
+ },
446
+ scopes: {}
447
+ }
448
+ ];
449
+
450
+ const result = buildImportsMap(
451
+ manifests,
452
+ (name, file) => `${name}/${file}`
453
+ );
454
+
455
+ assert.deepEqual(result, {
456
+ 'test-module/src/index': 'test-module/src/index.js',
457
+ 'test-module/src': 'test-module/src/index.js',
458
+ 'test-module/alias-test': 'test-module/src/index.js'
459
+ });
460
+ });
461
+
462
+ test('should handle nested index aliases', () => {
463
+ const manifests: ImportMapManifest[] = [
464
+ {
465
+ name: 'test-module',
466
+ imports: {},
467
+ exports: {
468
+ 'src/components/utils/index': {
469
+ name: 'src/components/utils/index',
470
+ pkg: false,
471
+ file: 'src/components/utils/index.js',
472
+ identifier: 'test-module/src/components/utils/index'
473
+ }
474
+ },
475
+ scopes: {}
476
+ }
477
+ ];
478
+
479
+ const result = buildImportsMap(
480
+ manifests,
481
+ (name, file) => `${name}/${file}`
482
+ );
483
+
484
+ assert.deepEqual(result, {
485
+ 'test-module/src/components/utils/index':
486
+ 'test-module/src/components/utils/index.js',
487
+ 'test-module/src/components/utils':
488
+ 'test-module/src/components/utils/index.js'
489
+ });
80
490
  });
81
491
  });
82
- test('should generate import map with remote exports and module scopes', async () => {
83
- const result = getImportMap({
84
- manifests: [
492
+
493
+ describe('buildScopesMap', () => {
494
+ test('should return empty object for empty manifests', () => {
495
+ const imports = {};
496
+ const manifests: ImportMapManifest[] = [];
497
+ const result = buildScopesMap(
498
+ imports,
499
+ manifests,
500
+ (name, scope) => `${name}/${scope}`
501
+ );
502
+ assert.deepEqual(result, {});
503
+ });
504
+
505
+ test('should return empty object when manifests have no scopes', () => {
506
+ const imports = {
507
+ 'test-module/component': 'test-module/component.js'
508
+ };
509
+ const manifests: ImportMapManifest[] = [
85
510
  {
86
- name: 'ssr-vue2-remote',
511
+ name: 'test-module',
87
512
  imports: {},
88
513
  exports: {
89
- vue: {
90
- name: 'vue',
91
- rewrite: false,
92
- file: 'vue.mjs',
93
- identifier: 'ssr-vue2-remote/vue'
514
+ component: {
515
+ name: 'component',
516
+ pkg: false,
517
+ file: 'component.js',
518
+ identifier: 'test-module/component'
519
+ }
520
+ },
521
+ scopes: {}
522
+ }
523
+ ];
524
+ const result = buildScopesMap(
525
+ imports,
526
+ manifests,
527
+ (name, scope) => `${name}/${scope}`
528
+ );
529
+ assert.deepEqual(result, {});
530
+ });
531
+
532
+ test('should build scopes map with basic scope configuration', () => {
533
+ const imports = {
534
+ 'test-module/component': 'test-module/component.js',
535
+ 'test-module/utils': 'test-module/utils.js'
536
+ };
537
+ const manifests: ImportMapManifest[] = [
538
+ {
539
+ name: 'test-module',
540
+ imports: {},
541
+ exports: {
542
+ component: {
543
+ name: 'component',
544
+ pkg: false,
545
+ file: 'component.js',
546
+ identifier: 'test-module/component'
547
+ },
548
+ utils: {
549
+ name: 'utils',
550
+ pkg: false,
551
+ file: 'utils.js',
552
+ identifier: 'test-module/utils'
553
+ }
554
+ },
555
+ scopes: {
556
+ node_modules: {
557
+ react: 'test-module/component',
558
+ lodash: 'test-module/utils'
559
+ }
560
+ }
561
+ }
562
+ ];
563
+ const result = buildScopesMap(
564
+ imports,
565
+ manifests,
566
+ (name, scope) => `${name}/${scope}`
567
+ );
568
+ assert.deepEqual(result, {
569
+ 'test-module//node_modules': {
570
+ react: 'test-module/component.js',
571
+ lodash: 'test-module/utils.js'
572
+ }
573
+ });
574
+ });
575
+
576
+ test('should handle scope with non-existent identifiers', () => {
577
+ const imports = {
578
+ 'test-module/component': 'test-module/component.js'
579
+ };
580
+ const manifests: ImportMapManifest[] = [
581
+ {
582
+ name: 'test-module',
583
+ imports: {},
584
+ exports: {
585
+ component: {
586
+ name: 'component',
587
+ pkg: false,
588
+ file: 'component.js',
589
+ identifier: 'test-module/component'
590
+ }
591
+ },
592
+ scopes: {
593
+ node_modules: {
594
+ react: 'test-module/component',
595
+ 'non-existent': 'test-module/non-existent'
596
+ }
597
+ }
598
+ }
599
+ ];
600
+ const result = buildScopesMap(
601
+ imports,
602
+ manifests,
603
+ (name, scope) => `${name}/${scope}`
604
+ );
605
+ assert.deepEqual(result, {
606
+ 'test-module//node_modules': {
607
+ react: 'test-module/component.js',
608
+ 'non-existent': 'test-module/non-existent'
609
+ }
610
+ });
611
+ });
612
+
613
+ test('should handle scope with external URLs', () => {
614
+ const imports = {
615
+ 'test-module/component': 'test-module/component.js'
616
+ };
617
+ const manifests: ImportMapManifest[] = [
618
+ {
619
+ name: 'test-module',
620
+ imports: {},
621
+ exports: {
622
+ component: {
623
+ name: 'component',
624
+ pkg: false,
625
+ file: 'component.js',
626
+ identifier: 'test-module/component'
627
+ }
628
+ },
629
+ scopes: {
630
+ node_modules: {
631
+ react: 'https://cdn.com/react.js',
632
+ 'local-component': 'test-module/component'
633
+ }
634
+ }
635
+ }
636
+ ];
637
+ const result = buildScopesMap(
638
+ imports,
639
+ manifests,
640
+ (name, scope) => `${name}/${scope}`
641
+ );
642
+ assert.deepEqual(result, {
643
+ 'test-module//node_modules': {
644
+ react: 'https://cdn.com/react.js',
645
+ 'local-component': 'test-module/component.js'
646
+ }
647
+ });
648
+ });
649
+
650
+ test('should use scope path from imports when available', () => {
651
+ const imports = {
652
+ 'test-module/node_modules': 'test-module/node_modules/index.js',
653
+ 'test-module/component': 'test-module/component.js'
654
+ };
655
+ const manifests: ImportMapManifest[] = [
656
+ {
657
+ name: 'test-module',
658
+ imports: {},
659
+ exports: {
660
+ node_modules: {
661
+ name: 'node_modules',
662
+ pkg: false,
663
+ file: 'node_modules/index.js',
664
+ identifier: 'test-module/node_modules'
665
+ },
666
+ component: {
667
+ name: 'component',
668
+ pkg: false,
669
+ file: 'component.js',
670
+ identifier: 'test-module/component'
671
+ }
672
+ },
673
+ scopes: {
674
+ node_modules: {
675
+ react: 'test-module/component'
676
+ }
677
+ }
678
+ }
679
+ ];
680
+ const result = buildScopesMap(
681
+ imports,
682
+ manifests,
683
+ (name, scope) => `${name}/${scope}`
684
+ );
685
+ assert.deepEqual(result, {
686
+ 'test-module/test-module/node_modules/index.js': {
687
+ react: 'test-module/component.js'
688
+ }
689
+ });
690
+ });
691
+
692
+ test('should fall back to scope path when not found in imports', () => {
693
+ const imports = {
694
+ 'test-module/component': 'test-module/component.js'
695
+ };
696
+ const manifests: ImportMapManifest[] = [
697
+ {
698
+ name: 'test-module',
699
+ imports: {},
700
+ exports: {
701
+ component: {
702
+ name: 'component',
703
+ pkg: false,
704
+ file: 'component.js',
705
+ identifier: 'test-module/component'
706
+ }
707
+ },
708
+ scopes: {
709
+ node_modules: {
710
+ react: 'test-module/component'
711
+ }
712
+ }
713
+ }
714
+ ];
715
+ const result = buildScopesMap(
716
+ imports,
717
+ manifests,
718
+ (name, scope) => `${name}/${scope}`
719
+ );
720
+ assert.deepEqual(result, {
721
+ 'test-module//node_modules': {
722
+ react: 'test-module/component.js'
723
+ }
724
+ });
725
+ });
726
+
727
+ test('should handle multiple scopes in single manifest', () => {
728
+ const imports = {
729
+ 'test-module/component': 'test-module/component.js',
730
+ 'test-module/utils': 'test-module/utils.js'
731
+ };
732
+ const manifests: ImportMapManifest[] = [
733
+ {
734
+ name: 'test-module',
735
+ imports: {},
736
+ exports: {
737
+ component: {
738
+ name: 'component',
739
+ pkg: false,
740
+ file: 'component.js',
741
+ identifier: 'test-module/component'
742
+ },
743
+ utils: {
744
+ name: 'utils',
745
+ pkg: false,
746
+ file: 'utils.js',
747
+ identifier: 'test-module/utils'
748
+ }
749
+ },
750
+ scopes: {
751
+ node_modules: {
752
+ react: 'test-module/component'
753
+ },
754
+ vendor: {
755
+ lodash: 'test-module/utils'
756
+ }
757
+ }
758
+ }
759
+ ];
760
+ const result = buildScopesMap(
761
+ imports,
762
+ manifests,
763
+ (name, scope) => `${name}/${scope}`
764
+ );
765
+ assert.deepEqual(result, {
766
+ 'test-module//node_modules': {
767
+ react: 'test-module/component.js'
768
+ },
769
+ 'test-module//vendor': {
770
+ lodash: 'test-module/utils.js'
771
+ }
772
+ });
773
+ });
774
+
775
+ test('should handle multiple manifests with scopes', () => {
776
+ const imports = {
777
+ 'module-a/component': 'module-a/component.js',
778
+ 'module-b/utils': 'module-b/utils.js'
779
+ };
780
+ const manifests: ImportMapManifest[] = [
781
+ {
782
+ name: 'module-a',
783
+ imports: {},
784
+ exports: {
785
+ component: {
786
+ name: 'component',
787
+ pkg: false,
788
+ file: 'component.js',
789
+ identifier: 'module-a/component'
790
+ }
791
+ },
792
+ scopes: {
793
+ node_modules: {
794
+ react: 'module-a/component'
94
795
  }
95
796
  }
96
797
  },
97
798
  {
98
- name: 'ssr-vue2-host',
99
- imports: {
100
- vue: 'ssr-vue2-remote/vue3'
799
+ name: 'module-b',
800
+ imports: {},
801
+ exports: {
802
+ utils: {
803
+ name: 'utils',
804
+ pkg: false,
805
+ file: 'utils.js',
806
+ identifier: 'module-b/utils'
807
+ }
101
808
  },
102
- exports: {}
809
+ scopes: {
810
+ vendor: {
811
+ lodash: 'module-b/utils'
812
+ }
813
+ }
814
+ }
815
+ ];
816
+ const result = buildScopesMap(
817
+ imports,
818
+ manifests,
819
+ (name, scope) => `${name}/${scope}`
820
+ );
821
+ assert.deepEqual(result, {
822
+ 'module-a//node_modules': {
823
+ react: 'module-a/component.js'
824
+ },
825
+ 'module-b//vendor': {
826
+ lodash: 'module-b/utils.js'
103
827
  }
104
- ],
105
- getScope(name) {
106
- return `/${name}/`;
107
- },
108
- getFile(name, file) {
109
- return `${name}/${file}`;
110
- }
828
+ });
111
829
  });
112
- assert.deepEqual(result, {
113
- imports: { 'ssr-vue2-remote/vue': 'ssr-vue2-remote/vue.mjs' },
114
- scopes: {
115
- '/ssr-vue2-remote/': { vue: 'ssr-vue2-remote/vue.mjs' },
116
- '/ssr-vue2-host/': { vue: 'ssr-vue2-remote/vue3' }
117
- }
830
+
831
+ test('should handle empty scope specifier map', () => {
832
+ const imports = {
833
+ 'test-module/component': 'test-module/component.js'
834
+ };
835
+ const manifests: ImportMapManifest[] = [
836
+ {
837
+ name: 'test-module',
838
+ imports: {},
839
+ exports: {
840
+ component: {
841
+ name: 'component',
842
+ pkg: false,
843
+ file: 'component.js',
844
+ identifier: 'test-module/component'
845
+ }
846
+ },
847
+ scopes: {
848
+ './node_modules': {}
849
+ }
850
+ }
851
+ ];
852
+ const result = buildScopesMap(
853
+ imports,
854
+ manifests,
855
+ (name, scope) => `${name}/${scope}`
856
+ );
857
+ assert.deepEqual(result, {
858
+ 'test-module//./node_modules': {}
859
+ });
860
+ });
861
+
862
+ test('should handle undefined scopes property', () => {
863
+ const imports = {
864
+ 'test-module/component': 'test-module/component.js'
865
+ };
866
+ const manifests: ImportMapManifest[] = [
867
+ {
868
+ name: 'test-module',
869
+ imports: {},
870
+ exports: {
871
+ component: {
872
+ name: 'component',
873
+ pkg: false,
874
+ file: 'component.js',
875
+ identifier: 'test-module/component'
876
+ }
877
+ },
878
+ scopes: undefined as any
879
+ }
880
+ ];
881
+ const result = buildScopesMap(
882
+ imports,
883
+ manifests,
884
+ (name, scope) => `${name}/${scope}`
885
+ );
886
+ assert.deepEqual(result, {});
118
887
  });
119
888
  });
120
889
 
121
- test('should throw error when encountering legacy format manifests', async () => {
122
- // 模拟旧版本的清单格式,其中 exports 的值是字符串而不是对象
123
- const legacyManifest = {
124
- name: 'legacy-module',
125
- imports: {},
126
- exports: {
127
- 'legacy-export': 'legacy-module/legacy-file.js'
128
- } as any
129
- };
130
-
131
- const expectedErrorMessage =
132
- 'Detected incompatible legacy manifest format in legacy-module. Please upgrade your ESMX dependencies first, then rebuild and redeploy your service.';
133
-
134
- expect(() => {
135
- getImportMap({
136
- manifests: [legacyManifest],
137
- getScope(name) {
138
- return `/${name}/`;
890
+ describe('getImportMap', () => {
891
+ test('should return empty import map for empty manifests', () => {
892
+ const options: GetImportMapOptions = {
893
+ manifests: [],
894
+ getFile: (name, file) => `${name}/${file}`,
895
+ getScope: (name, scope) => `${name}/${scope}`
896
+ };
897
+ const result = getImportMap(options);
898
+ assert.deepEqual(result, {
899
+ imports: {},
900
+ scopes: {}
901
+ });
902
+ });
903
+
904
+ test('should build complete import map with imports and scopes', () => {
905
+ const options: GetImportMapOptions = {
906
+ manifests: [
907
+ {
908
+ name: 'test-module',
909
+ imports: {
910
+ 'custom-react': 'test-module/component'
911
+ },
912
+ exports: {
913
+ component: {
914
+ name: 'component',
915
+ pkg: false,
916
+ file: 'component.js',
917
+ identifier: 'test-module/component'
918
+ },
919
+ utils: {
920
+ name: 'utils',
921
+ pkg: false,
922
+ file: 'utils.js',
923
+ identifier: 'test-module/utils'
924
+ }
925
+ },
926
+ scopes: {
927
+ node_modules: {
928
+ react: 'test-module/component',
929
+ lodash: 'test-module/utils'
930
+ }
931
+ }
932
+ }
933
+ ],
934
+ getFile: (name, file) => `${name}/${file}`,
935
+ getScope: (name, scope) => `${name}/${scope}`
936
+ };
937
+ const result = getImportMap(options);
938
+ assert.deepEqual(result, {
939
+ imports: {
940
+ 'test-module/component': 'test-module/component.js',
941
+ 'test-module/utils': 'test-module/utils.js',
942
+ 'test-module/custom-react': 'test-module/component.js'
943
+ },
944
+ scopes: {
945
+ 'test-module//node_modules': {
946
+ react: 'test-module/component.js',
947
+ lodash: 'test-module/utils.js'
948
+ }
949
+ }
950
+ });
951
+ });
952
+
953
+ test('should handle complex multi-module scenario', () => {
954
+ const options: GetImportMapOptions = {
955
+ manifests: [
956
+ {
957
+ name: 'module-a',
958
+ imports: {},
959
+ exports: {
960
+ utils: {
961
+ name: 'utils',
962
+ pkg: false,
963
+ file: 'utils.js',
964
+ identifier: 'module-a/utils'
965
+ }
966
+ },
967
+ scopes: {
968
+ node_modules: {
969
+ react: 'module-a/utils'
970
+ }
971
+ }
972
+ },
973
+ {
974
+ name: 'module-b',
975
+ imports: {
976
+ shared: 'module-a/utils'
977
+ },
978
+ exports: {
979
+ component: {
980
+ name: 'component',
981
+ pkg: false,
982
+ file: 'component.js',
983
+ identifier: 'module-b/component'
984
+ }
985
+ },
986
+ scopes: {
987
+ vendor: {
988
+ lodash: 'module-a/utils'
989
+ }
990
+ }
991
+ }
992
+ ],
993
+ getFile: (name, file) => `${name}/${file}`,
994
+ getScope: (name, scope) => `${name}/${scope}`
995
+ };
996
+ const result = getImportMap(options);
997
+ assert.deepEqual(result, {
998
+ imports: {
999
+ 'module-a/utils': 'module-a/utils.js',
1000
+ 'module-b/component': 'module-b/component.js',
1001
+ 'module-b/shared': 'module-a/utils.js'
1002
+ },
1003
+ scopes: {
1004
+ 'module-a//node_modules': {
1005
+ react: 'module-a/utils.js'
1006
+ },
1007
+ 'module-b//vendor': {
1008
+ lodash: 'module-a/utils.js'
1009
+ }
1010
+ }
1011
+ });
1012
+ });
1013
+
1014
+ test('should handle manifests with only exports', () => {
1015
+ const options: GetImportMapOptions = {
1016
+ manifests: [
1017
+ {
1018
+ name: 'test-module',
1019
+ imports: {},
1020
+ exports: {
1021
+ component: {
1022
+ name: 'component',
1023
+ pkg: false,
1024
+ file: 'component.js',
1025
+ identifier: 'test-module/component'
1026
+ }
1027
+ },
1028
+ scopes: {}
1029
+ }
1030
+ ],
1031
+ getFile: (name, file) => `${name}/${file}`,
1032
+ getScope: (name, scope) => `${name}/${scope}`
1033
+ };
1034
+ const result = getImportMap(options);
1035
+ assert.deepEqual(result, {
1036
+ imports: {
1037
+ 'test-module/component': 'test-module/component.js'
1038
+ },
1039
+ scopes: {}
1040
+ });
1041
+ });
1042
+
1043
+ test('should handle manifests with only imports', () => {
1044
+ const options: GetImportMapOptions = {
1045
+ manifests: [
1046
+ {
1047
+ name: 'test-module',
1048
+ imports: {
1049
+ external: 'https://cdn.com/lib.js'
1050
+ },
1051
+ exports: {},
1052
+ scopes: {}
1053
+ }
1054
+ ],
1055
+ getFile: (name, file) => `${name}/${file}`,
1056
+ getScope: (name, scope) => `${name}/${scope}`
1057
+ };
1058
+ const result = getImportMap(options);
1059
+ assert.deepEqual(result, {
1060
+ imports: {
1061
+ 'test-module/external': 'https://cdn.com/lib.js'
1062
+ },
1063
+ scopes: {}
1064
+ });
1065
+ });
1066
+
1067
+ test('should handle manifests with only scopes', () => {
1068
+ const options: GetImportMapOptions = {
1069
+ manifests: [
1070
+ {
1071
+ name: 'test-module',
1072
+ imports: {},
1073
+ exports: {},
1074
+ scopes: {
1075
+ node_modules: {
1076
+ react: 'https://cdn.com/react.js'
1077
+ }
1078
+ }
1079
+ }
1080
+ ],
1081
+ getFile: (name, file) => `${name}/${file}`,
1082
+ getScope: (name, scope) => `${name}/${scope}`
1083
+ };
1084
+ const result = getImportMap(options);
1085
+ assert.deepEqual(result, {
1086
+ imports: {},
1087
+ scopes: {
1088
+ 'test-module//node_modules': {
1089
+ react: 'https://cdn.com/react.js'
1090
+ }
1091
+ }
1092
+ });
1093
+ });
1094
+
1095
+ test('should handle custom getFile and getScope functions', () => {
1096
+ const options: GetImportMapOptions = {
1097
+ manifests: [
1098
+ {
1099
+ name: 'test-module',
1100
+ imports: {},
1101
+ exports: {
1102
+ component: {
1103
+ name: 'component',
1104
+ pkg: false,
1105
+ file: 'component.js',
1106
+ identifier: 'test-module/component'
1107
+ }
1108
+ },
1109
+ scopes: {
1110
+ node_modules: {
1111
+ react: 'test-module/component'
1112
+ }
1113
+ }
1114
+ }
1115
+ ],
1116
+ getFile: (name, file) => `/custom/path/${name}/${file}`,
1117
+ getScope: (name, scope) => `custom-scope-${name}-${scope}`
1118
+ };
1119
+ const result = getImportMap(options);
1120
+ assert.deepEqual(result, {
1121
+ imports: {
1122
+ 'test-module/component': '/custom/path/test-module/component.js'
1123
+ },
1124
+ scopes: {
1125
+ 'custom-scope-test-module-/node_modules': {
1126
+ react: '/custom/path/test-module/component.js'
1127
+ }
1128
+ }
1129
+ });
1130
+ });
1131
+
1132
+ test('should handle edge case with undefined scopes in manifests', () => {
1133
+ const options: GetImportMapOptions = {
1134
+ manifests: [
1135
+ {
1136
+ name: 'test-module',
1137
+ imports: {},
1138
+ exports: {
1139
+ component: {
1140
+ name: 'component',
1141
+ pkg: false,
1142
+ file: 'component.js',
1143
+ identifier: 'test-module/component'
1144
+ }
1145
+ },
1146
+ scopes: undefined as any
1147
+ }
1148
+ ],
1149
+ getFile: (name, file) => `${name}/${file}`,
1150
+ getScope: (name, scope) => `${name}/${scope}`
1151
+ };
1152
+ const result = getImportMap(options);
1153
+ assert.deepEqual(result, {
1154
+ imports: {
1155
+ 'test-module/component': 'test-module/component.js'
1156
+ },
1157
+ scopes: {}
1158
+ });
1159
+ });
1160
+
1161
+ test('should handle mixed scenarios with external URLs and local modules', () => {
1162
+ const options: GetImportMapOptions = {
1163
+ manifests: [
1164
+ {
1165
+ name: 'test-module',
1166
+ imports: {
1167
+ 'external-react': 'https://cdn.com/react.js',
1168
+ 'local-alias': 'test-module/component'
1169
+ },
1170
+ exports: {
1171
+ component: {
1172
+ name: 'component',
1173
+ pkg: false,
1174
+ file: 'component.js',
1175
+ identifier: 'test-module/component'
1176
+ }
1177
+ },
1178
+ scopes: {
1179
+ node_modules: {
1180
+ 'external-lib': 'https://cdn.com/lodash.js',
1181
+ 'local-lib': 'test-module/component'
1182
+ }
1183
+ }
1184
+ }
1185
+ ],
1186
+ getFile: (name, file) => `${name}/${file}`,
1187
+ getScope: (name, scope) => `${name}/${scope}`
1188
+ };
1189
+ const result = getImportMap(options);
1190
+ assert.deepEqual(result, {
1191
+ imports: {
1192
+ 'test-module/component': 'test-module/component.js',
1193
+ 'test-module/external-react': 'https://cdn.com/react.js',
1194
+ 'test-module/local-alias': 'test-module/component.js'
1195
+ },
1196
+ scopes: {
1197
+ 'test-module//node_modules': {
1198
+ 'external-lib': 'https://cdn.com/lodash.js',
1199
+ 'local-lib': 'test-module/component.js'
1200
+ }
1201
+ }
1202
+ });
1203
+ });
1204
+
1205
+ test('should handle index path aliases in complete import map', () => {
1206
+ const options: GetImportMapOptions = {
1207
+ manifests: [
1208
+ {
1209
+ name: 'test-module',
1210
+ imports: {},
1211
+ exports: {
1212
+ 'src/index': {
1213
+ name: 'src/index',
1214
+ pkg: false,
1215
+ file: 'src/index.js',
1216
+ identifier: 'test-module/src/index'
1217
+ }
1218
+ },
1219
+ scopes: {
1220
+ src: {
1221
+ main: 'test-module/src/index'
1222
+ }
1223
+ }
1224
+ }
1225
+ ],
1226
+ getFile: (name, file) => `${name}/${file}`,
1227
+ getScope: (name, scope) => `${name}/${scope}`
1228
+ };
1229
+ const result = getImportMap(options);
1230
+ assert.deepEqual(result, {
1231
+ imports: {
1232
+ 'test-module/src/index': 'test-module/src/index.js',
1233
+ 'test-module/src': 'test-module/src/index.js'
139
1234
  },
140
- getFile(name, file) {
141
- return `${name}/${file}`;
1235
+ scopes: {
1236
+ 'test-module/test-module/src/index.js': {
1237
+ main: 'test-module/src/index.js'
1238
+ }
142
1239
  }
143
1240
  });
144
- }).toThrowError(expectedErrorMessage);
1241
+ });
145
1242
  });