import { assert, describe, test } from 'vitest'; import type { GetImportMapOptions, ImportMapManifest } from './import-map'; import { compressImportMap, createClientImportMap, createImportMap, createImportsMap, createScopesMap, fixImportMapNestedScopes } from './import-map'; describe('createImportsMap', () => { test('should return empty object for empty manifests', () => { const result = createImportsMap([], (name, file) => `${name}/${file}`); assert.deepEqual(result, {}); }); test('should process all exports including package exports', () => { const manifests: ImportMapManifest[] = [ { name: 'test-module', exports: { component: { name: 'component', pkg: false, file: 'component.js', identifier: 'test-module/component' }, vue: { name: 'vue', pkg: true, file: 'vue.js', identifier: 'test-module/vue' } }, scopes: {} } ]; const result = createImportsMap( manifests, (name, file) => `${name}/${file}` ); assert.deepEqual(result, { 'test-module/component': 'test-module/component.js', 'test-module/vue': 'test-module/vue.js' }); }); test('should create aliases for index suffixes', () => { const manifests: ImportMapManifest[] = [ { name: 'test-module', exports: { 'src/index': { name: 'src/index', pkg: false, file: 'src/index.js', identifier: 'test-module/src/index' } }, scopes: {} } ]; const result = createImportsMap( manifests, (name, file) => `${name}/${file}` ); assert.deepEqual(result, { 'test-module/src/index': 'test-module/src/index.js', 'test-module/src': 'test-module/src/index.js' }); }); test('should handle multiple manifests', () => { const manifests: ImportMapManifest[] = [ { name: 'module-a', exports: { utils: { name: 'utils', pkg: false, file: 'utils.js', identifier: 'module-a/utils' } }, scopes: {} }, { name: 'module-b', exports: {}, scopes: {} } ]; const result = createImportsMap( manifests, (name, file) => `${name}/${file}` ); assert.deepEqual(result, { 'module-a/utils': 'module-a/utils.js' }); }); test('should handle empty exports', () => { const manifests: ImportMapManifest[] = [ { name: 'test-module', exports: {}, scopes: {} } ]; const result = createImportsMap( manifests, (name, file) => `${name}/${file}` ); assert.deepEqual(result, {}); }); test('should handle duplicate alias creation', () => { const manifests: ImportMapManifest[] = [ { name: 'test-module', exports: { 'src/components/index': { name: 'src/components/index', pkg: false, file: 'src/components/index.js', identifier: 'test-module/src/components/index' } }, scopes: {} } ]; const result = createImportsMap( manifests, (name, file) => `${name}/${file}` ); assert.deepEqual(result, { 'test-module/src/components/index': 'test-module/src/components/index.js', 'test-module/src/components': 'test-module/src/components/index.js' }); }); test('should handle identifier conflicts across modules', () => { const manifests: ImportMapManifest[] = [ { name: 'module-a', exports: { utils: { name: 'utils', pkg: false, file: 'utils-a.js', identifier: 'module-a/utils' } }, scopes: {} }, { name: 'module-b', exports: { utils: { name: 'utils', pkg: false, file: 'utils-b.js', identifier: 'module-b/utils' } }, scopes: {} } ]; const result = createImportsMap( manifests, (name, file) => `${name}/${file}` ); assert.deepEqual(result, { 'module-a/utils': 'module-a/utils-a.js', 'module-b/utils': 'module-b/utils-b.js' }); }); test('should handle nested index aliases', () => { const manifests: ImportMapManifest[] = [ { name: 'test-module', exports: { 'src/components/utils/index': { name: 'src/components/utils/index', pkg: false, file: 'src/components/utils/index.js', identifier: 'test-module/src/components/utils/index' } }, scopes: {} } ]; const result = createImportsMap( manifests, (name, file) => `${name}/${file}` ); assert.deepEqual(result, { 'test-module/src/components/utils/index': 'test-module/src/components/utils/index.js', 'test-module/src/components/utils': 'test-module/src/components/utils/index.js' }); }); }); describe('fixImportMapNestedScopes', () => { test('should return unchanged import map for empty scopes', () => { const importMap = { imports: { 'shared-modules/vue': '/shared-modules/vue.620a1e89.final.mjs' }, scopes: {} }; const result = fixImportMapNestedScopes(importMap); assert.deepEqual(result, importMap); }); test('should return unchanged import map for shallow scopes only', () => { const importMap = { imports: { 'shared-modules/vue': '/shared-modules/vue.620a1e89.final.mjs' }, scopes: { '/shared-modules/': { vue: '/shared-modules/vue.620a1e89.final.mjs' } } }; const result = fixImportMapNestedScopes(importMap); assert.deepEqual(result, importMap); }); test('should create file-level scopes for nested scopes', () => { const importMap = { imports: { 'shared-modules/vue2': '/shared-modules/vue2.a1b2c3d4.final.mjs', 'shared-modules/vue2/@esmx/router-vue': '/shared-modules/vue2/@esmx/router-vue.e5f6g7h8.final.mjs', 'shared-modules/vue2/index': '/shared-modules/vue2/index.i9j0k1l2.final.mjs', 'shared-modules/vue': '/shared-modules/vue.m3n4o5p6.final.mjs' }, scopes: { '/shared-modules/': { vue: '/shared-modules/vue.m3n4o5p6.final.mjs' }, '/shared-modules/vue2/': { vue: '/shared-modules/vue2.a1b2c3d4.final.mjs' } } }; const expected = { imports: { 'shared-modules/vue2': '/shared-modules/vue2.a1b2c3d4.final.mjs', 'shared-modules/vue2/@esmx/router-vue': '/shared-modules/vue2/@esmx/router-vue.e5f6g7h8.final.mjs', 'shared-modules/vue2/index': '/shared-modules/vue2/index.i9j0k1l2.final.mjs', 'shared-modules/vue': '/shared-modules/vue.m3n4o5p6.final.mjs' }, scopes: { '/shared-modules/vue.m3n4o5p6.final.mjs': { vue: '/shared-modules/vue.m3n4o5p6.final.mjs' }, '/shared-modules/vue2.a1b2c3d4.final.mjs': { vue: '/shared-modules/vue.m3n4o5p6.final.mjs' }, '/shared-modules/vue2/@esmx/router-vue.e5f6g7h8.final.mjs': { vue: '/shared-modules/vue2.a1b2c3d4.final.mjs' }, '/shared-modules/vue2/index.i9j0k1l2.final.mjs': { vue: '/shared-modules/vue2.a1b2c3d4.final.mjs' } } }; const result = fixImportMapNestedScopes(importMap); assert.deepEqual(result, expected); }); test('should handle multiple nested scopes correctly', () => { const importMap = { imports: { 'shared-modules/vue2': '/shared-modules/vue2.q7r8s9t0.final.mjs', 'shared-modules/vue2/component': '/shared-modules/vue2/component.u1v2w3x4.final.mjs', 'shared-modules/vue3': '/shared-modules/vue3.y5z6a7b8.final.mjs', 'shared-modules/vue3/component': '/shared-modules/vue3/component.c9d0e1f2.final.mjs', 'shared-modules/vue': '/shared-modules/vue.g3h4i5j6.final.mjs' }, scopes: { '/shared-modules/': { vue: '/shared-modules/vue.g3h4i5j6.final.mjs' }, '/shared-modules/vue2/': { vue: '/shared-modules/vue2.q7r8s9t0.final.mjs' }, '/shared-modules/vue3/': { vue: '/shared-modules/vue3.y5z6a7b8.final.mjs' } } }; const expected = { imports: { 'shared-modules/vue2': '/shared-modules/vue2.q7r8s9t0.final.mjs', 'shared-modules/vue2/component': '/shared-modules/vue2/component.u1v2w3x4.final.mjs', 'shared-modules/vue3': '/shared-modules/vue3.y5z6a7b8.final.mjs', 'shared-modules/vue3/component': '/shared-modules/vue3/component.c9d0e1f2.final.mjs', 'shared-modules/vue': '/shared-modules/vue.g3h4i5j6.final.mjs' }, scopes: { '/shared-modules/vue.g3h4i5j6.final.mjs': { vue: '/shared-modules/vue.g3h4i5j6.final.mjs' }, '/shared-modules/vue2.q7r8s9t0.final.mjs': { vue: '/shared-modules/vue.g3h4i5j6.final.mjs' }, '/shared-modules/vue2/component.u1v2w3x4.final.mjs': { vue: '/shared-modules/vue2.q7r8s9t0.final.mjs' }, '/shared-modules/vue3.y5z6a7b8.final.mjs': { vue: '/shared-modules/vue.g3h4i5j6.final.mjs' }, '/shared-modules/vue3/component.c9d0e1f2.final.mjs': { vue: '/shared-modules/vue3.y5z6a7b8.final.mjs' } } }; const result = fixImportMapNestedScopes(importMap); assert.deepEqual(result, expected); }); test('should handle deeply nested scopes', () => { const importMap = { imports: { 'shared-modules/vue2/components/button': '/shared-modules/vue2/components/button.k7l8m9n0.final.mjs', 'shared-modules/vue2/components/input': '/shared-modules/vue2/components/input.o1p2q3r4.final.mjs' }, scopes: { '/shared-modules/vue2/components/': { vue: '/shared-modules/vue2.s5t6u7v8.final.mjs' } } }; const expected = { imports: { 'shared-modules/vue2/components/button': '/shared-modules/vue2/components/button.k7l8m9n0.final.mjs', 'shared-modules/vue2/components/input': '/shared-modules/vue2/components/input.o1p2q3r4.final.mjs' }, scopes: { '/shared-modules/vue2/components/button.k7l8m9n0.final.mjs': { vue: '/shared-modules/vue2.s5t6u7v8.final.mjs' }, '/shared-modules/vue2/components/input.o1p2q3r4.final.mjs': { vue: '/shared-modules/vue2.s5t6u7v8.final.mjs' } } }; const result = fixImportMapNestedScopes(importMap); assert.deepEqual(result, expected); }); test('should not create file-level scopes for imports not matching nested scope', () => { const importMap = { imports: { 'shared-modules/vue': '/shared-modules/vue.w9x0y1z2.final.mjs', 'shared-modules/vue2/component': '/shared-modules/vue2/component.a3b4c5d6.final.mjs', 'other-modules/component': '/other-modules/component.e7f8g9h0.final.mjs' }, scopes: { '/shared-modules/vue2/': { vue: '/shared-modules/vue2.i1j2k3l4.final.mjs' } } }; const expected = { imports: importMap.imports, scopes: { '/shared-modules/vue2/component.a3b4c5d6.final.mjs': { vue: '/shared-modules/vue2.i1j2k3l4.final.mjs' } } }; const result = fixImportMapNestedScopes(importMap); assert.deepEqual(result, expected); }); test('should handle empty imports', () => { const importMap = { imports: {}, scopes: { '/shared-modules/vue2/': { vue: '/shared-modules/vue2.m5n6o7p8.final.mjs' } } }; const expected = { imports: {}, scopes: {} }; const result = fixImportMapNestedScopes(importMap); assert.deepEqual(result, expected); }); test('should preserve original import map structure', () => { const importMap = { imports: { 'shared-modules/vue2': '/shared-modules/vue2.q9r8s7t6.final.mjs', 'shared-modules/vue2/component': '/shared-modules/vue2/component.u5v4w3x2.final.mjs' }, scopes: { '/shared-modules/vue2/': { vue: '/shared-modules/vue2.q9r8s7t6.final.mjs', 'vue-router': '/shared-modules/vue2/router.y1z0a9b8.final.mjs' } } }; const result = fixImportMapNestedScopes(importMap); const expected = { imports: importMap.imports, scopes: { '/shared-modules/vue2/component.u5v4w3x2.final.mjs': { vue: '/shared-modules/vue2.q9r8s7t6.final.mjs', 'vue-router': '/shared-modules/vue2/router.y1z0a9b8.final.mjs' } } }; assert.deepEqual(result, expected); }); test('should handle complex priority scenarios with multiple nested levels', () => { const importMap = { imports: { 'shared-modules/vue2': '/shared-modules/vue2.c7d8e9f0.final.mjs', 'shared-modules/vue2/test': '/shared-modules/vue2/test.a1b2c3d4.final.mjs', 'shared-modules/vue2/test/component': '/shared-modules/vue2/test/component.e5f6g7h8.final.mjs', 'shared-modules/vue': '/shared-modules/vue.i9j0k1l2.final.mjs' }, scopes: { '/shared-modules/': { vue: '/shared-modules/vue.i9j0k1l2.final.mjs', 'vue-router': '/shared-modules/@esmx/router.m3n4o5p6.final.mjs' }, '/shared-modules/vue2/': { vue: '/shared-modules/vue2.c7d8e9f0.final.mjs', 'vue-router': '/shared-modules/vue2/@esmx/router-vue.q7r8s9t0.final.mjs' }, '/shared-modules/vue2/test/': { vue: '/shared-modules/vue2.c7d8e9f0.final.mjs', 'test-utils': '/shared-modules/vue2/test/utils.u1v2w3x4.final.mjs' } } }; const expected = { imports: { 'shared-modules/vue2': '/shared-modules/vue2.c7d8e9f0.final.mjs', 'shared-modules/vue2/test': '/shared-modules/vue2/test.a1b2c3d4.final.mjs', 'shared-modules/vue2/test/component': '/shared-modules/vue2/test/component.e5f6g7h8.final.mjs', 'shared-modules/vue': '/shared-modules/vue.i9j0k1l2.final.mjs' }, scopes: { '/shared-modules/vue.i9j0k1l2.final.mjs': { vue: '/shared-modules/vue.i9j0k1l2.final.mjs', 'vue-router': '/shared-modules/@esmx/router.m3n4o5p6.final.mjs' }, '/shared-modules/vue2.c7d8e9f0.final.mjs': { vue: '/shared-modules/vue.i9j0k1l2.final.mjs', 'vue-router': '/shared-modules/@esmx/router.m3n4o5p6.final.mjs' }, '/shared-modules/vue2/test.a1b2c3d4.final.mjs': { vue: '/shared-modules/vue2.c7d8e9f0.final.mjs', 'vue-router': '/shared-modules/vue2/@esmx/router-vue.q7r8s9t0.final.mjs' }, '/shared-modules/vue2/test/component.e5f6g7h8.final.mjs': { vue: '/shared-modules/vue2.c7d8e9f0.final.mjs', 'test-utils': '/shared-modules/vue2/test/utils.u1v2w3x4.final.mjs', 'vue-router': '/shared-modules/vue2/@esmx/router-vue.q7r8s9t0.final.mjs' } } }; const result = fixImportMapNestedScopes(importMap); assert.deepEqual(result, expected); }); test('should handle priority with overlapping nested scopes', () => { const importMap = { imports: { 'shared-modules/vue2': '/shared-modules/vue2.n3o4p5q6.final.mjs', 'shared-modules/vue2/components': '/shared-modules/vue2/components.r7s8t9u0.final.mjs', 'shared-modules/vue2/components/button': '/shared-modules/vue2/components/button.v1w2x3y4.final.mjs', 'shared-modules/vue2/components/input': '/shared-modules/vue2/components/input.z5a6b7c8.final.mjs' }, scopes: { '/shared-modules/vue2/': { vue: '/shared-modules/vue2.n3o4p5q6.final.mjs', 'vue-router': '/shared-modules/vue2/@esmx/router-vue.d9e0f1g2.final.mjs' }, '/shared-modules/vue2/components/': { vue: '/shared-modules/vue2.n3o4p5q6.final.mjs', 'component-utils': '/shared-modules/vue2/components/utils.h3i4j5k6.final.mjs' } } }; const expected = { imports: { 'shared-modules/vue2': '/shared-modules/vue2.n3o4p5q6.final.mjs', 'shared-modules/vue2/components': '/shared-modules/vue2/components.r7s8t9u0.final.mjs', 'shared-modules/vue2/components/button': '/shared-modules/vue2/components/button.v1w2x3y4.final.mjs', 'shared-modules/vue2/components/input': '/shared-modules/vue2/components/input.z5a6b7c8.final.mjs' }, scopes: { '/shared-modules/vue2/components.r7s8t9u0.final.mjs': { vue: '/shared-modules/vue2.n3o4p5q6.final.mjs', 'vue-router': '/shared-modules/vue2/@esmx/router-vue.d9e0f1g2.final.mjs' }, '/shared-modules/vue2/components/button.v1w2x3y4.final.mjs': { 'component-utils': '/shared-modules/vue2/components/utils.h3i4j5k6.final.mjs', vue: '/shared-modules/vue2.n3o4p5q6.final.mjs', 'vue-router': '/shared-modules/vue2/@esmx/router-vue.d9e0f1g2.final.mjs' }, '/shared-modules/vue2/components/input.z5a6b7c8.final.mjs': { 'component-utils': '/shared-modules/vue2/components/utils.h3i4j5k6.final.mjs', vue: '/shared-modules/vue2.n3o4p5q6.final.mjs', 'vue-router': '/shared-modules/vue2/@esmx/router-vue.d9e0f1g2.final.mjs' } } }; const result = fixImportMapNestedScopes(importMap); assert.deepEqual(result, expected); }); test('should handle very deeply nested scope priority scenarios', () => { const importMap = { imports: { 'shared-modules/vue2': '/shared-modules/vue2.l7m8n9o0.final.mjs', 'shared-modules/vue2/test': '/shared-modules/vue2/test.p1q2r3s4.final.mjs', 'shared-modules/vue2/test/unit': '/shared-modules/vue2/test/unit.t5u6v7w8.final.mjs', 'shared-modules/vue2/test/unit/component': '/shared-modules/vue2/test/unit/component.x9y0z1a2.final.mjs' }, scopes: { '/shared-modules/': { vue: '/shared-modules/vue.b3c4d5e6.final.mjs' }, '/shared-modules/vue2/': { vue: '/shared-modules/vue2.l7m8n9o0.final.mjs' }, '/shared-modules/vue2/test/': { vue: '/shared-modules/vue2.l7m8n9o0.final.mjs', 'test-utils': '/shared-modules/vue2/test/utils.f7g8h9i0.final.mjs' }, '/shared-modules/vue2/test/unit/': { vue: '/shared-modules/vue2.l7m8n9o0.final.mjs', 'test-utils': '/shared-modules/vue2/test/utils.f7g8h9i0.final.mjs' } } }; const expected = { imports: { 'shared-modules/vue2': '/shared-modules/vue2.l7m8n9o0.final.mjs', 'shared-modules/vue2/test': '/shared-modules/vue2/test.p1q2r3s4.final.mjs', 'shared-modules/vue2/test/unit': '/shared-modules/vue2/test/unit.t5u6v7w8.final.mjs', 'shared-modules/vue2/test/unit/component': '/shared-modules/vue2/test/unit/component.x9y0z1a2.final.mjs' }, scopes: { '/shared-modules/vue2.l7m8n9o0.final.mjs': { vue: '/shared-modules/vue.b3c4d5e6.final.mjs' }, '/shared-modules/vue2/test.p1q2r3s4.final.mjs': { vue: '/shared-modules/vue2.l7m8n9o0.final.mjs' }, '/shared-modules/vue2/test/unit.t5u6v7w8.final.mjs': { vue: '/shared-modules/vue2.l7m8n9o0.final.mjs', 'test-utils': '/shared-modules/vue2/test/utils.f7g8h9i0.final.mjs' }, '/shared-modules/vue2/test/unit/component.x9y0z1a2.final.mjs': { vue: '/shared-modules/vue2.l7m8n9o0.final.mjs', 'test-utils': '/shared-modules/vue2/test/utils.f7g8h9i0.final.mjs' } } }; const result = fixImportMapNestedScopes(importMap); assert.deepEqual(result, expected); }); test('should ensure different directory levels have distinct values for proper testing', () => { const importMap = { imports: { 'shared-modules/level1': '/shared-modules/level1.a1b2c3d4.final.mjs', 'shared-modules/level1/level2': '/shared-modules/level1/level2.e5f6g7h8.final.mjs', 'shared-modules/level1/level2/level3': '/shared-modules/level1/level2/level3.i9j0k1l2.final.mjs' }, scopes: { '/shared-modules/level1/': { vue: '/shared-modules/level1.a1b2c3d4.final.mjs' }, '/shared-modules/level1/level2/': { vue: '/shared-modules/level1/level2.e5f6g7h8.final.mjs' } } }; const expected = { imports: { 'shared-modules/level1': '/shared-modules/level1.a1b2c3d4.final.mjs', 'shared-modules/level1/level2': '/shared-modules/level1/level2.e5f6g7h8.final.mjs', 'shared-modules/level1/level2/level3': '/shared-modules/level1/level2/level3.i9j0k1l2.final.mjs' }, scopes: { '/shared-modules/level1/level2.e5f6g7h8.final.mjs': { vue: '/shared-modules/level1.a1b2c3d4.final.mjs' }, '/shared-modules/level1/level2/level3.i9j0k1l2.final.mjs': { vue: '/shared-modules/level1/level2.e5f6g7h8.final.mjs' } } }; const result = fixImportMapNestedScopes(importMap); assert.deepEqual(result, expected); assert.equal( result.scopes['/shared-modules/level1/level2.e5f6g7h8.final.mjs'] .vue, '/shared-modules/level1.a1b2c3d4.final.mjs' ); assert.equal( result.scopes[ '/shared-modules/level1/level2/level3.i9j0k1l2.final.mjs' ].vue, '/shared-modules/level1/level2.e5f6g7h8.final.mjs' ); }); test('should successfully delete properties using Reflect.deleteProperty', () => { const importMap = { imports: { 'shared/modules/vue2': '/shared/modules/vue2.a1b2c3d4.final.mjs' }, scopes: { '/shared/modules/vue2/': { vue: '/shared/modules/vue2.a1b2c3d4.final.mjs' } } }; const result = fixImportMapNestedScopes(importMap); const expected = { imports: importMap.imports, scopes: {} }; assert.deepEqual(result, expected); assert.doesNotThrow(() => { fixImportMapNestedScopes(importMap); }); }); describe('scope path processing logic', () => { test('should process all scope paths regardless of format', () => { const importMap = { imports: { 'shared/modules/vue2': '/shared/modules/vue2.a1b2c3d4.final.mjs', 'shared/vue': '/shared/vue.e5f6g7h8.final.mjs' }, scopes: { 'shared/modules/vue2/': { vue: '/shared/modules/vue2.a1b2c3d4.final.mjs' }, '/shared/modules/vue2': { vue: '/shared/modules/vue2.a1b2c3d4.final.mjs' }, '/shared/': { vue: '/shared/vue.e5f6g7h8.final.mjs' }, '/shared/modules/vue2/': { vue: '/shared/modules/vue2.a1b2c3d4.final.mjs' } } }; const expected = { imports: { 'shared/modules/vue2': '/shared/modules/vue2.a1b2c3d4.final.mjs', 'shared/vue': '/shared/vue.e5f6g7h8.final.mjs' }, scopes: { '/shared/modules/vue2.a1b2c3d4.final.mjs': { vue: '/shared/modules/vue2.a1b2c3d4.final.mjs' }, '/shared/vue.e5f6g7h8.final.mjs': { vue: '/shared/vue.e5f6g7h8.final.mjs' } } }; const result = fixImportMapNestedScopes(importMap); assert.deepEqual(result, expected); }); test('should handle scope paths with any depth', () => { const importMap = { imports: { 'a/b/c': '/a/b/c.a1b2c3d4.final.mjs' }, scopes: { '/a/b/c/': { vue: '/a/b/c.a1b2c3d4.final.mjs' } } }; const expected = { imports: { 'a/b/c': '/a/b/c.a1b2c3d4.final.mjs' }, scopes: {} }; const result = fixImportMapNestedScopes(importMap); assert.deepEqual(result, expected); }); test('should handle very deep nested scope paths', () => { const importMap = { imports: { 'a/b/c/d/e/f': '/a/b/c/d/e/f.a1b2c3d4.final.mjs' }, scopes: { '/a/b/c/d/e/f/': { vue: '/a/b/c/d/e/f.a1b2c3d4.final.mjs' } } }; const expected = { imports: { 'a/b/c/d/e/f': '/a/b/c/d/e/f.a1b2c3d4.final.mjs' }, scopes: {} }; const result = fixImportMapNestedScopes(importMap); assert.deepEqual(result, expected); }); test('should create file-level scopes for all scope paths', () => { const importMap = { imports: { 'modules/vue': '/modules/vue.a1b2c3d4.final.mjs', 'shared/vue': '/shared/vue.e5f6g7h8.final.mjs' }, scopes: { '/modules/': { vue: '/modules/vue.a1b2c3d4.final.mjs' }, '/shared/': { vue: '/shared/vue.e5f6g7h8.final.mjs' } } }; const expected = { imports: { 'modules/vue': '/modules/vue.a1b2c3d4.final.mjs', 'shared/vue': '/shared/vue.e5f6g7h8.final.mjs' }, scopes: { '/modules/vue.a1b2c3d4.final.mjs': { vue: '/modules/vue.a1b2c3d4.final.mjs' }, '/shared/vue.e5f6g7h8.final.mjs': { vue: '/shared/vue.e5f6g7h8.final.mjs' } } }; const result = fixImportMapNestedScopes(importMap); assert.deepEqual(result, expected); }); test('should process multiple scope paths in correct order by depth', () => { const importMap = { imports: { 'shared/modules/vue2': '/shared/modules/vue2.a1b2c3d4.final.mjs', 'shared/modules/vue2/component': '/shared/modules/vue2/component.e5f6g7h8.final.mjs', 'shared/modules/vue2/utils': '/shared/modules/vue2/utils.i9j0k1l2.final.mjs' }, scopes: { '/shared/modules/vue2/': { vue: '/shared/modules/vue2.a1b2c3d4.final.mjs' }, '/shared/modules/vue2/utils/': { 'test-utils': '/shared/modules/vue2/utils.i9j0k1l2.final.mjs' } } }; const expected = { imports: { 'shared/modules/vue2': '/shared/modules/vue2.a1b2c3d4.final.mjs', 'shared/modules/vue2/component': '/shared/modules/vue2/component.e5f6g7h8.final.mjs', 'shared/modules/vue2/utils': '/shared/modules/vue2/utils.i9j0k1l2.final.mjs' }, scopes: { '/shared/modules/vue2/component.e5f6g7h8.final.mjs': { vue: '/shared/modules/vue2.a1b2c3d4.final.mjs' }, '/shared/modules/vue2/utils.i9j0k1l2.final.mjs': { vue: '/shared/modules/vue2.a1b2c3d4.final.mjs' } } }; const result = fixImportMapNestedScopes(importMap); assert.deepEqual(result, expected); }); }); }); describe('createScopesMap', () => { test('should return empty object for empty manifests', () => { const imports = {}; const manifests: ImportMapManifest[] = []; const result = createScopesMap( imports, manifests, (name, scope) => `${name}/${scope}` ); assert.deepEqual(result, {}); }); test('should return empty object when manifests have no scopes', () => { const imports = { 'test-module/component': 'test-module/component.js' }; const manifests: ImportMapManifest[] = [ { name: 'test-module', exports: { component: { name: 'component', pkg: false, file: 'component.js', identifier: 'test-module/component' } }, scopes: {} } ]; const result = createScopesMap( imports, manifests, (name, scope) => `${name}/${scope}` ); assert.deepEqual(result, {}); }); test('should build scopes map with basic scope configuration', () => { const imports = { 'test-module/component': 'test-module/component.js', 'test-module/utils': 'test-module/utils.js' }; const manifests: ImportMapManifest[] = [ { name: 'test-module', exports: { component: { name: 'component', pkg: false, file: 'component.js', identifier: 'test-module/component' }, utils: { name: 'utils', pkg: false, file: 'utils.js', identifier: 'test-module/utils' } }, scopes: { node_modules: { react: 'test-module/component', lodash: 'test-module/utils' } } } ]; const result = createScopesMap( imports, manifests, (name, scope) => `${name}/${scope}` ); assert.deepEqual(result, { 'test-module//node_modules': { react: 'test-module/component.js', lodash: 'test-module/utils.js' } }); }); test('should handle scope with non-existent identifiers', () => { const imports = { 'test-module/component': 'test-module/component.js' }; const manifests: ImportMapManifest[] = [ { name: 'test-module', exports: { component: { name: 'component', pkg: false, file: 'component.js', identifier: 'test-module/component' } }, scopes: { node_modules: { react: 'test-module/component', 'non-existent': 'test-module/non-existent' } } } ]; const result = createScopesMap( imports, manifests, (name, scope) => `${name}/${scope}` ); assert.deepEqual(result, { 'test-module//node_modules': { react: 'test-module/component.js', 'non-existent': 'test-module/non-existent' } }); }); test('should handle scope with external URLs', () => { const imports = { 'test-module/component': 'test-module/component.js' }; const manifests: ImportMapManifest[] = [ { name: 'test-module', exports: { component: { name: 'component', pkg: false, file: 'component.js', identifier: 'test-module/component' } }, scopes: { node_modules: { react: 'https://cdn.com/react.js', 'local-component': 'test-module/component' } } } ]; const result = createScopesMap( imports, manifests, (name, scope) => `${name}/${scope}` ); assert.deepEqual(result, { 'test-module//node_modules': { react: 'https://cdn.com/react.js', 'local-component': 'test-module/component.js' } }); }); test('should use scope path from imports when available', () => { const imports = { 'test-module/node_modules': 'test-module/node_modules/index.js', 'test-module/component': 'test-module/component.js' }; const manifests: ImportMapManifest[] = [ { name: 'test-module', exports: { node_modules: { name: 'node_modules', pkg: false, file: 'node_modules/index.js', identifier: 'test-module/node_modules' }, component: { name: 'component', pkg: false, file: 'component.js', identifier: 'test-module/component' } }, scopes: { node_modules: { react: 'test-module/component' } } } ]; const result = createScopesMap( imports, manifests, (name, scope) => `${name}/${scope}` ); assert.deepEqual(result, { 'test-module/test-module/node_modules/index.js': { react: 'test-module/component.js' } }); }); test('should fall back to scope path when not found in imports', () => { const imports = { 'test-module/component': 'test-module/component.js' }; const manifests: ImportMapManifest[] = [ { name: 'test-module', exports: { component: { name: 'component', pkg: false, file: 'component.js', identifier: 'test-module/component' } }, scopes: { node_modules: { react: 'test-module/component' } } } ]; const result = createScopesMap( imports, manifests, (name, scope) => `${name}/${scope}` ); assert.deepEqual(result, { 'test-module//node_modules': { react: 'test-module/component.js' } }); }); test('should handle multiple scopes in single manifest', () => { const imports = { 'test-module/component': 'test-module/component.js', 'test-module/utils': 'test-module/utils.js' }; const manifests: ImportMapManifest[] = [ { name: 'test-module', exports: { component: { name: 'component', pkg: false, file: 'component.js', identifier: 'test-module/component' }, utils: { name: 'utils', pkg: false, file: 'utils.js', identifier: 'test-module/utils' } }, scopes: { node_modules: { react: 'test-module/component' }, vendor: { lodash: 'test-module/utils' } } } ]; const result = createScopesMap( imports, manifests, (name, scope) => `${name}/${scope}` ); assert.deepEqual(result, { 'test-module//node_modules': { react: 'test-module/component.js' }, 'test-module//vendor': { lodash: 'test-module/utils.js' } }); }); test('should handle multiple manifests with scopes', () => { const imports = { 'module-a/component': 'module-a/component.js', 'module-b/utils': 'module-b/utils.js' }; const manifests: ImportMapManifest[] = [ { name: 'module-a', exports: { component: { name: 'component', pkg: false, file: 'component.js', identifier: 'module-a/component' } }, scopes: { node_modules: { react: 'module-a/component' } } }, { name: 'module-b', exports: { utils: { name: 'utils', pkg: false, file: 'utils.js', identifier: 'module-b/utils' } }, scopes: { vendor: { lodash: 'module-b/utils' } } } ]; const result = createScopesMap( imports, manifests, (name, scope) => `${name}/${scope}` ); assert.deepEqual(result, { 'module-a//node_modules': { react: 'module-a/component.js' }, 'module-b//vendor': { lodash: 'module-b/utils.js' } }); }); test('should handle empty scope specifier map', () => { const imports = { 'test-module/component': 'test-module/component.js' }; const manifests: ImportMapManifest[] = [ { name: 'test-module', exports: { component: { name: 'component', pkg: false, file: 'component.js', identifier: 'test-module/component' } }, scopes: { './node_modules': {} } } ]; const result = createScopesMap( imports, manifests, (name, scope) => `${name}/${scope}` ); assert.deepEqual(result, { 'test-module//./node_modules': {} }); }); test('should handle undefined scopes property', () => { const imports = { 'test-module/component': 'test-module/component.js' }; const manifests: ImportMapManifest[] = [ { name: 'test-module', exports: { component: { name: 'component', pkg: false, file: 'component.js', identifier: 'test-module/component' } }, scopes: undefined as any } ]; const result = createScopesMap( imports, manifests, (name, scope) => `${name}/${scope}` ); assert.deepEqual(result, {}); }); }); describe('createImportMap', () => { test('should return empty import map for empty manifests', () => { const options: GetImportMapOptions = { manifests: [], getFile: (name, file) => `${name}/${file}`, getScope: (name, scope) => `${name}/${scope}` }; const result = createImportMap(options); assert.deepEqual(result, { imports: {}, scopes: {} }); }); test('should build complete import map with exports and scopes', () => { const options: GetImportMapOptions = { manifests: [ { name: 'test-module', exports: { component: { name: 'component', pkg: false, file: 'component.js', identifier: 'test-module/component' }, utils: { name: 'utils', pkg: false, file: 'utils.js', identifier: 'test-module/utils' } }, scopes: { node_modules: { react: 'test-module/component', lodash: 'test-module/utils' } } } ], getFile: (name, file) => `${name}/${file}`, getScope: (name, scope) => `${name}/${scope}` }; const result = createImportMap(options); assert.deepEqual(result, { imports: { 'test-module/component': 'test-module/component.js', 'test-module/utils': 'test-module/utils.js' }, scopes: { 'test-module//node_modules': { react: 'test-module/component.js', lodash: 'test-module/utils.js' } } }); }); test('should handle complex multi-module scenario', () => { const options: GetImportMapOptions = { manifests: [ { name: 'module-a', exports: { utils: { name: 'utils', pkg: false, file: 'utils.js', identifier: 'module-a/utils' } }, scopes: { node_modules: { react: 'module-a/utils' } } }, { name: 'module-b', exports: { component: { name: 'component', pkg: false, file: 'component.js', identifier: 'module-b/component' } }, scopes: { vendor: { lodash: 'module-a/utils' } } } ], getFile: (name, file) => `${name}/${file}`, getScope: (name, scope) => `${name}/${scope}` }; const result = createImportMap(options); assert.deepEqual(result, { imports: { 'module-a/utils': 'module-a/utils.js', 'module-b/component': 'module-b/component.js' }, scopes: { 'module-a//node_modules': { react: 'module-a/utils.js' }, 'module-b//vendor': { lodash: 'module-a/utils.js' } } }); }); test('should handle manifests with only exports', () => { const options: GetImportMapOptions = { manifests: [ { name: 'test-module', exports: { component: { name: 'component', pkg: false, file: 'component.js', identifier: 'test-module/component' } }, scopes: {} } ], getFile: (name, file) => `${name}/${file}`, getScope: (name, scope) => `${name}/${scope}` }; const result = createImportMap(options); assert.deepEqual(result, { imports: { 'test-module/component': 'test-module/component.js' }, scopes: {} }); }); test('should handle manifests with only scopes', () => { const options: GetImportMapOptions = { manifests: [ { name: 'test-module', exports: {}, scopes: { node_modules: { react: 'https://cdn.com/react.js' } } } ], getFile: (name, file) => `${name}/${file}`, getScope: (name, scope) => `${name}/${scope}` }; const result = createImportMap(options); assert.deepEqual(result, { imports: {}, scopes: { 'test-module//node_modules': { react: 'https://cdn.com/react.js' } } }); }); test('should handle custom getFile and getScope functions', () => { const options: GetImportMapOptions = { manifests: [ { name: 'test-module', exports: { component: { name: 'component', pkg: false, file: 'component.js', identifier: 'test-module/component' } }, scopes: { node_modules: { react: 'test-module/component' } } } ], getFile: (name, file) => `/custom/path/${name}/${file}`, getScope: (name, scope) => `custom-scope-${name}-${scope}` }; const result = createImportMap(options); assert.deepEqual(result, { imports: { 'test-module/component': '/custom/path/test-module/component.js' }, scopes: { 'custom-scope-test-module-/node_modules': { react: '/custom/path/test-module/component.js' } } }); }); test('should handle edge case with undefined scopes in manifests', () => { const options: GetImportMapOptions = { manifests: [ { name: 'test-module', exports: { component: { name: 'component', pkg: false, file: 'component.js', identifier: 'test-module/component' } }, scopes: undefined as any } ], getFile: (name, file) => `${name}/${file}`, getScope: (name, scope) => `${name}/${scope}` }; const result = createImportMap(options); assert.deepEqual(result, { imports: { 'test-module/component': 'test-module/component.js' }, scopes: {} }); }); test('should handle mixed scenarios with external URLs and local modules', () => { const options: GetImportMapOptions = { manifests: [ { name: 'test-module', exports: { component: { name: 'component', pkg: false, file: 'component.js', identifier: 'test-module/component' } }, scopes: { node_modules: { 'external-lib': 'https://cdn.com/lodash.js', 'local-lib': 'test-module/component' } } } ], getFile: (name, file) => `${name}/${file}`, getScope: (name, scope) => `${name}/${scope}` }; const result = createImportMap(options); assert.deepEqual(result, { imports: { 'test-module/component': 'test-module/component.js' }, scopes: { 'test-module//node_modules': { 'external-lib': 'https://cdn.com/lodash.js', 'local-lib': 'test-module/component.js' } } }); }); test('should handle index path aliases in complete import map', () => { const options: GetImportMapOptions = { manifests: [ { name: 'test-module', exports: { 'src/index': { name: 'src/index', pkg: false, file: 'src/index.js', identifier: 'test-module/src/index' } }, scopes: { src: { main: 'test-module/src/index' } } } ], getFile: (name, file) => `${name}/${file}`, getScope: (name, scope) => `${name}/${scope}` }; const result = createImportMap(options); assert.deepEqual(result, { imports: { 'test-module/src/index': 'test-module/src/index.js', 'test-module/src': 'test-module/src/index.js' }, scopes: { 'test-module/test-module/src/index.js': { main: 'test-module/src/index.js' } } }); }); }); describe('compressImportMap', () => { test('does not promote when no global exists; keeps scopes intact', () => { const importMap = { imports: {}, scopes: { '/a/': { vue: '/a/vue.final.mjs' }, '/b/': { vue: '/a/vue.final.mjs' } } }; const result = compressImportMap(importMap); assert.deepEqual(result, { imports: { vue: '/a/vue.final.mjs' } }); }); test('does not promote when scoped mappings conflict across scopes', () => { const importMap = { imports: {}, scopes: { '/a/': { vue: '/a/vue.final.mjs' }, '/b/': { vue: '/b/vue.final.mjs' } } }; const result = compressImportMap(importMap); assert.deepEqual(result, importMap); }); test('removes scoped entries that equal global mapping', () => { const importMap = { imports: { vue: '/shared/vue.final.mjs' }, scopes: { '/a/': { vue: '/shared/vue.final.mjs', lodash: '/a/lodash.final.mjs' } } }; const result = compressImportMap(importMap); const expected = { imports: { vue: '/shared/vue.final.mjs', lodash: '/a/lodash.final.mjs' } }; assert.deepEqual(result, expected); }); test('promotes to global when global matches single unique target across scopes', () => { const importMap = { imports: { vue: '/shared/vue.final.mjs' }, scopes: { '/a/': { vue: '/shared/vue.final.mjs' }, '/b/': { vue: '/shared/vue.final.mjs' } } }; const result = compressImportMap(importMap); assert.deepEqual(result, { imports: { vue: '/shared/vue.final.mjs' } }); }); test('promotes to global when no global exists and targets are consistent across scopes (promote mode)', () => { const importMap = { imports: {}, scopes: { '/a/': { vue: '/x/vue.final.mjs' }, '/b/': { vue: '/x/vue.final.mjs' } } }; const result = compressImportMap(importMap); assert.deepEqual(result, { imports: { vue: '/x/vue.final.mjs' } }); }); test('promotes dominant target and keeps exceptions in scopes (promote mode)', () => { const importMap = { imports: {}, scopes: { '/a/': { vue: '/x/vue.final.mjs' }, '/b/': { vue: '/x/vue.final.mjs' }, '/c/': { vue: '/y/vue2.final.mjs' } } }; const result = compressImportMap(importMap); assert.deepEqual(result, { imports: { vue: '/x/vue.final.mjs' }, scopes: { '/c/': { vue: '/y/vue2.final.mjs' } } }); }); test('does not promote when no global exists; keeps scopes intact', () => { const importMap = { imports: {}, scopes: { '/a/': { vue: '/shared/vue.final.mjs' }, '/b/': { vue: '/shared/vue.final.mjs' }, '/c/': { vue: '/c/vue2.final.mjs' } } }; const result = compressImportMap(importMap); assert.deepEqual(result, { imports: { vue: '/shared/vue.final.mjs' }, scopes: { '/c/': { vue: '/c/vue2.final.mjs' } } }); }); test('aggressive default does not override different existing global', () => { const importMap = { imports: { vue: '/global/vue.final.mjs' }, scopes: { '/a/': { vue: '/shared/vue.final.mjs' }, '/b/': { vue: '/shared/vue.final.mjs' } } }; const result = compressImportMap(importMap); assert.deepEqual(result, { imports: { vue: '/global/vue.final.mjs' }, scopes: { '/a/': { vue: '/shared/vue.final.mjs' }, '/b/': { vue: '/shared/vue.final.mjs' } } }); }); test('returns new object and keeps input unchanged', () => { const original = { imports: {}, scopes: { '/a/': { vue: '/x/vue.final.mjs' }, '/b/': { vue: '/x/vue.final.mjs' }, '/c/': { vue: '/y/vue2.final.mjs' } } }; const importMap = JSON.parse(JSON.stringify(original)); const result = compressImportMap(importMap); assert.notStrictEqual(result, importMap); assert.notStrictEqual(result.imports, importMap.imports); assert.notStrictEqual(result.scopes, importMap.scopes); assert.deepEqual(importMap, original); assert.deepEqual(result, { imports: { vue: '/x/vue.final.mjs' }, scopes: { '/c/': { vue: '/y/vue2.final.mjs' } } }); }); }); describe('createClientImportMap integrity', () => { const getFile = (name: string, file: string) => `/${name}/${file}`; const getScope = (name: string, scope: string) => scope; test('omits integrity when no manifest provides it', () => { const manifests: ImportMapManifest[] = [ { name: 'module-a', exports: { index: { name: 'index', pkg: false, file: 'src/index.mjs', identifier: 'module-a' } }, scopes: {} } ]; const result = createClientImportMap({ manifests, getFile, getScope }); assert.isUndefined(result.integrity); }); test('converts relative integrity paths to absolute URL paths', () => { const manifests: ImportMapManifest[] = [ { name: 'ssr-micro-vue2', exports: { routes: { name: 'routes', pkg: false, file: 'src/routes.xxx.mjs', identifier: 'ssr-micro-vue2/routes' } }, scopes: {}, integrity: { 'src/routes.xxx.mjs': 'sha384-abc' } } ]; const result = createClientImportMap({ manifests, getFile, getScope }); assert.deepEqual(result.integrity, { '/ssr-micro-vue2/src/routes.xxx.mjs': 'sha384-abc' }); }); test('merges integrity across multiple manifests', () => { const manifests: ImportMapManifest[] = [ { name: 'module-a', exports: {}, scopes: {}, integrity: { 'src/a.mjs': 'sha384-a' } }, { name: 'module-b', exports: {}, scopes: {}, integrity: { 'src/b.mjs': 'sha384-b' } } ]; const result = createClientImportMap({ manifests, getFile, getScope }); assert.deepEqual(result.integrity, { '/module-a/src/a.mjs': 'sha384-a', '/module-b/src/b.mjs': 'sha384-b' }); }); }); describe('createClientImportMap code-split chunk scopes', () => { const getFile = (name: string, file: string) => `/${name}/${file}`; const getScope = (name: string, scope: string) => `/${name}${scope}`; const makeVueModule = (name: string, hash: string): ImportMapManifest => ({ name, exports: { entry: { name: 'entry', pkg: false, file: `src/entry.client.${hash}.mjs`, identifier: `${name}/src/entry.client` }, vue: { name: 'vue', pkg: true, file: `vue.${hash}.mjs`, identifier: `${name}/vue` } }, scopes: { '': { vue: `${name}/vue` } }, // A Vite-style code-split chunk that is NOT an export. chunks: { routesChunk: { js: `chunks/routes.${hash}.mjs` } } }); test('scopes a module code-split chunk to its own externals', () => { // Two modules each bundling a different "vue" file → vue must stay // scoped (never promoted to a single global import). const manifests = [ makeVueModule('app-a', 'aaa'), makeVueModule('app-b', 'bbb') ]; const result = createClientImportMap({ manifests, getFile, getScope }); assert.isUndefined(result.imports?.vue); assert.equal( result.scopes?.['/app-a/chunks/routes.aaa.mjs']?.vue, '/app-a/vue.aaa.mjs' ); assert.equal( result.scopes?.['/app-b/chunks/routes.bbb.mjs']?.vue, '/app-b/vue.bbb.mjs' ); }); test('skips a chunk scope when a global import already resolves it', () => { // A single module → its sole "vue" target gets promoted to a global // import, so the chunk needs no redundant scope of its own. const result = createClientImportMap({ manifests: [makeVueModule('solo', 'aaa')], getFile, getScope }); assert.equal(result.imports?.vue, '/solo/vue.aaa.mjs'); assert.isUndefined(result.scopes?.['/solo/chunks/routes.aaa.mjs']); }); test('is a no-op for all-in-one manifests without code-split chunks', () => { const allInOne: ImportMapManifest = { name: 'rspack-app', exports: { entry: { name: 'entry', pkg: false, file: 'src/entry.client.zzz.mjs', identifier: 'rspack-app/src/entry.client' }, vue: { name: 'vue', pkg: true, file: 'vue.zzz.mjs', identifier: 'rspack-app/vue' } }, scopes: { '': { vue: 'rspack-app/vue' } } // no `chunks` — rspack all-in-one output }; // Pair with a different-vue module so vue stays scoped, not promoted. const result = createClientImportMap({ manifests: [allInOne, makeVueModule('app-b', 'bbb')], getFile, getScope }); const invented = Object.keys(result.scopes ?? {}).filter((k) => k.startsWith('/rspack-app/chunks/') ); assert.deepEqual(invented, []); }); test('applies all of a module externals to its chunk', () => { const make = (name: string, hash: string): ImportMapManifest => ({ name, exports: { vue: { name: 'vue', pkg: true, file: `vue.${hash}.mjs`, identifier: `${name}/vue` }, pinia: { name: 'pinia', pkg: true, file: `pinia.${hash}.mjs`, identifier: `${name}/pinia` } }, scopes: { '': { vue: `${name}/vue`, pinia: `${name}/pinia` } }, chunks: { storeChunk: { js: `chunks/store.${hash}.mjs` } } }); // Two modules with different targets → both externals stay scoped. const result = createClientImportMap({ manifests: [make('app-a', 'aaa'), make('app-b', 'bbb')], getFile, getScope }); const scope = result.scopes?.['/app-a/chunks/store.aaa.mjs']; assert.equal(scope?.vue, '/app-a/vue.aaa.mjs'); assert.equal(scope?.pinia, '/app-a/pinia.aaa.mjs'); }); }); describe('multi-version coexistence (scope isolation)', () => { // Two providers each declare their own copy of `vue` (single-owner per // (package,major) still allows legitimate cross-major coexistence). Each // copy must stay isolated in its own module scope, never collapsed. const layeredManifests: ImportMapManifest[] = [ { name: 'base', exports: { vue: { name: 'vue', pkg: true, file: 'vue.base.mjs', identifier: 'base/vue' } }, scopes: { '': { vue: 'base/vue' } }, chunks: { 'base@src/app.ts': { js: 'app.base.mjs' } }, integrity: { 'vue.base.mjs': 'sha384-base', 'app.base.mjs': 'sha384-base-app' } }, { name: 'vue-base', exports: { vue: { name: 'vue', pkg: true, file: 'vue.winner.mjs', identifier: 'vue-base/vue' } }, scopes: { '': { vue: 'vue-base/vue' } }, integrity: { 'vue.winner.mjs': 'sha384-winner' } } ]; const getFile = (name: string, file: string) => `/${name}/${file}`; const getScope = (name: string, scope: string) => `/${name}${scope}`; test('each copy stays isolated in its own scope', () => { const result = createImportMap({ manifests: layeredManifests, getFile, getScope }); assert.equal(result.imports['base/vue'], '/base/vue.base.mjs'); assert.equal( result.imports['vue-base/vue'], '/vue-base/vue.winner.mjs' ); assert.equal(result.scopes['/base/']?.vue, '/base/vue.base.mjs'); assert.equal( result.scopes['/vue-base/']?.vue, '/vue-base/vue.winner.mjs' ); }); });