slicejs-web-framework 3.4.4 → 3.5.1

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.
Files changed (110) hide show
  1. package/Slice/Components/Structural/Controller/Controller.js +135 -0
  2. package/Slice/Components/Structural/EventManager/EventManager.js +289 -4
  3. package/Slice/Components/Structural/EventManager/EventManagerDebugger.js +368 -30
  4. package/Slice/Components/Structural/LeakInspector/LeakInspector.js +296 -0
  5. package/Slice/Components/Structural/StylesManager/ThemeManager/ThemeManager.js +56 -0
  6. package/Slice/Slice.js +14 -1
  7. package/Slice/tests/event-emitters.test.js +79 -0
  8. package/Slice/tests/event-registry.test.js +141 -0
  9. package/Slice/tests/leak-orphans.test.js +131 -0
  10. package/api/{middleware → framework}/securityMiddleware.js +18 -74
  11. package/api/framework/server.js +333 -0
  12. package/api/index.js +5 -279
  13. package/api/tests/public-env-resolver.test.js +1 -1
  14. package/package.json +4 -2
  15. package/sliceConfig.schema.json +10 -1
  16. package/src/App/index.html +31 -1
  17. package/src/manifest.json +15 -0
  18. package/src/robots.txt +4 -0
  19. package/src/sitemap.xml +8 -0
  20. package/src/sliceConfig.json +5 -1
  21. package/types/index.d.ts +12 -0
  22. package/slicejs-cli/.github/workflows/ci.yml +0 -43
  23. package/slicejs-cli/LICENSE +0 -21
  24. package/slicejs-cli/assets/Slice.js-logo.png +0 -0
  25. package/slicejs-cli/client.js +0 -740
  26. package/slicejs-cli/commands/Print.js +0 -164
  27. package/slicejs-cli/commands/Validations.js +0 -92
  28. package/slicejs-cli/commands/build/build.js +0 -40
  29. package/slicejs-cli/commands/buildProduction/buildProduction.js +0 -577
  30. package/slicejs-cli/commands/bundle/bundle.js +0 -236
  31. package/slicejs-cli/commands/createComponent/VisualComponentTemplate.js +0 -55
  32. package/slicejs-cli/commands/createComponent/createComponent.js +0 -128
  33. package/slicejs-cli/commands/deleteComponent/deleteComponent.js +0 -81
  34. package/slicejs-cli/commands/doctor/doctor.js +0 -516
  35. package/slicejs-cli/commands/getComponent/getComponent.js +0 -695
  36. package/slicejs-cli/commands/init/init.js +0 -473
  37. package/slicejs-cli/commands/listComponents/listComponents.js +0 -172
  38. package/slicejs-cli/commands/startServer/startServer.js +0 -240
  39. package/slicejs-cli/commands/startServer/watchServer.js +0 -66
  40. package/slicejs-cli/commands/types/types.js +0 -583
  41. package/slicejs-cli/commands/utils/LocalCliDelegation.js +0 -53
  42. package/slicejs-cli/commands/utils/PackageManager.js +0 -148
  43. package/slicejs-cli/commands/utils/PathHelper.js +0 -75
  44. package/slicejs-cli/commands/utils/VersionChecker.js +0 -169
  45. package/slicejs-cli/commands/utils/bundling/BundleGenerator.js +0 -2525
  46. package/slicejs-cli/commands/utils/bundling/DependencyAnalyzer.js +0 -933
  47. package/slicejs-cli/commands/utils/loadConfig.js +0 -31
  48. package/slicejs-cli/commands/utils/sliceScripts.js +0 -48
  49. package/slicejs-cli/commands/utils/updateManager.js +0 -471
  50. package/slicejs-cli/package.json +0 -71
  51. package/slicejs-cli/playwright.config.js +0 -51
  52. package/slicejs-cli/pnpm-lock.yaml +0 -1629
  53. package/slicejs-cli/tests/build-command-integration.test.js +0 -87
  54. package/slicejs-cli/tests/build-mangling-e2e.test.js +0 -290
  55. package/slicejs-cli/tests/build-production-e2e.test.js +0 -140
  56. package/slicejs-cli/tests/builder-edge-cases.test.js +0 -322
  57. package/slicejs-cli/tests/bundle-generate-e2e.test.js +0 -115
  58. package/slicejs-cli/tests/bundle-generator.test.js +0 -691
  59. package/slicejs-cli/tests/bundle-v2-register-output.test.js +0 -470
  60. package/slicejs-cli/tests/bundling-dependency-edges.test.js +0 -127
  61. package/slicejs-cli/tests/bundling-imports-unit.test.js +0 -267
  62. package/slicejs-cli/tests/client-launcher-contract.test.js +0 -211
  63. package/slicejs-cli/tests/commands-component-crud.test.js +0 -128
  64. package/slicejs-cli/tests/commands-doctor.test.js +0 -152
  65. package/slicejs-cli/tests/commands-version-checker.test.js +0 -37
  66. package/slicejs-cli/tests/component-registry-parse.test.js +0 -34
  67. package/slicejs-cli/tests/dependency-analyzer.test.js +0 -24
  68. package/slicejs-cli/tests/e2e/bundles.spec.js +0 -91
  69. package/slicejs-cli/tests/e2e/dependency-scenarios.spec.js +0 -56
  70. package/slicejs-cli/tests/e2e/fixtures/components/Service/FetchManager/FetchManager.js +0 -136
  71. package/slicejs-cli/tests/e2e/fixtures/components/Service/IndexedDbManager/IndexedDbManager.js +0 -149
  72. package/slicejs-cli/tests/e2e/fixtures/components/Service/LocalStorageManager/LocalStorageManager.js +0 -45
  73. package/slicejs-cli/tests/e2e/fixtures/components/Visual/Button/Button.css +0 -106
  74. package/slicejs-cli/tests/e2e/fixtures/components/Visual/Button/Button.html +0 -5
  75. package/slicejs-cli/tests/e2e/fixtures/components/Visual/Button/Button.js +0 -158
  76. package/slicejs-cli/tests/e2e/fixtures/components/Visual/Link/Link.js +0 -33
  77. package/slicejs-cli/tests/e2e/fixtures/components/Visual/Loading/Loading.css +0 -56
  78. package/slicejs-cli/tests/e2e/fixtures/components/Visual/Loading/Loading.html +0 -83
  79. package/slicejs-cli/tests/e2e/fixtures/components/Visual/Loading/Loading.js +0 -164
  80. package/slicejs-cli/tests/e2e/fixtures/components/Visual/MultiRoute/MultiRoute.js +0 -167
  81. package/slicejs-cli/tests/e2e/fixtures/components/Visual/Navbar/Navbar.css +0 -116
  82. package/slicejs-cli/tests/e2e/fixtures/components/Visual/Navbar/Navbar.html +0 -44
  83. package/slicejs-cli/tests/e2e/fixtures/components/Visual/Navbar/Navbar.js +0 -180
  84. package/slicejs-cli/tests/e2e/fixtures/components/Visual/NotFound/NotFound.js +0 -20
  85. package/slicejs-cli/tests/e2e/fixtures/components/Visual/Route/Route.js +0 -181
  86. package/slicejs-cli/tests/e2e/fixtures/components/registry.json +0 -12
  87. package/slicejs-cli/tests/e2e/fixtures/vendor-components.mjs +0 -65
  88. package/slicejs-cli/tests/e2e/navigation.spec.js +0 -44
  89. package/slicejs-cli/tests/e2e/render.spec.js +0 -34
  90. package/slicejs-cli/tests/e2e/serve.mjs +0 -264
  91. package/slicejs-cli/tests/e2e/shared-deps.spec.js +0 -61
  92. package/slicejs-cli/tests/e2e/unminified.spec.js +0 -33
  93. package/slicejs-cli/tests/e2e-serve.test.js +0 -148
  94. package/slicejs-cli/tests/fixtures/components.js +0 -8
  95. package/slicejs-cli/tests/fixtures/sliceConfig.json +0 -74
  96. package/slicejs-cli/tests/getcomponent.test.js +0 -435
  97. package/slicejs-cli/tests/helpers/setup.js +0 -102
  98. package/slicejs-cli/tests/init-command-contract.test.js +0 -101
  99. package/slicejs-cli/tests/init-e2e.test.js +0 -82
  100. package/slicejs-cli/tests/init-project-isolation.test.js +0 -98
  101. package/slicejs-cli/tests/local-cli-delegation.test.js +0 -81
  102. package/slicejs-cli/tests/package-manager.test.js +0 -127
  103. package/slicejs-cli/tests/path-helper.test.js +0 -206
  104. package/slicejs-cli/tests/perf-budget.test.js +0 -84
  105. package/slicejs-cli/tests/postinstall-command.test.js +0 -103
  106. package/slicejs-cli/tests/property-mangling-regression.test.js +0 -182
  107. package/slicejs-cli/tests/types-breakage.test.js +0 -491
  108. package/slicejs-cli/tests/types-generator-errors.test.js +0 -361
  109. package/slicejs-cli/tests/types-generator.test.js +0 -348
  110. /package/api/{utils → framework}/publicEnvResolver.js +0 -0
@@ -1,470 +0,0 @@
1
- import { test } from 'node:test';
2
- import assert from 'node:assert/strict';
3
- import BundleGenerator from '../commands/utils/bundling/BundleGenerator.js';
4
-
5
- test('generated V2 bundle includes meta and registerAll exports', () => {
6
- const generator = new BundleGenerator(import.meta.url, {
7
- components: [],
8
- routes: [],
9
- metrics: {
10
- totalComponents: 0,
11
- totalRoutes: 0,
12
- sharedPercentage: 0,
13
- totalSize: 0
14
- }
15
- }, { output: 'src' });
16
-
17
- const source = generator.generateBundleFileContent(
18
- 'slice-bundle.test.js',
19
- 'route',
20
- [{ name: 'Button', category: 'Visual', categoryType: 'Visual', dependencies: new Set(), size: 100, js: '', html: '', css: '' }],
21
- '/test'
22
- );
23
-
24
- assert.match(source, /export const SLICE_BUNDLE_META/);
25
- assert.match(source, /export async function registerAll\(/);
26
- });
27
-
28
- test('generateBundleFileContent deduplicates components by name', () => {
29
- const generator = new BundleGenerator(import.meta.url, {
30
- components: [],
31
- routes: [],
32
- metrics: {
33
- totalComponents: 0,
34
- totalRoutes: 0,
35
- sharedPercentage: 0,
36
- totalSize: 0
37
- }
38
- }, { output: 'src' });
39
-
40
- const source = generator.generateBundleFileContent(
41
- 'slice-bundle.test.js',
42
- 'route',
43
- [
44
- { name: 'Button', category: 'Visual', categoryType: 'Visual', dependencies: new Set(), size: 100, js: '', html: '', css: '' },
45
- { name: 'Button', category: 'Visual', categoryType: 'Visual', dependencies: new Set(), size: 100, js: '', html: '', css: '' }
46
- ],
47
- '/test'
48
- );
49
-
50
- assert.equal((source.match(/const SLICE_CLASS_FACTORY_SliceComponent_Button/g) || []).length, 1);
51
- assert.equal((source.match(/if \(!controller\.classes\.has\("Button"\)\) \{\s*controller\.classes\.set\("Button", SLICE_CLASS_FACTORY_SliceComponent_Button\(\)\);\s*\}/g) || []).length, 1);
52
- });
53
-
54
- test('registerAll guards class, template, style, and category writes', () => {
55
- const generator = new BundleGenerator(import.meta.url, {
56
- components: [],
57
- routes: [],
58
- metrics: {
59
- totalComponents: 0,
60
- totalRoutes: 0,
61
- sharedPercentage: 0,
62
- totalSize: 0
63
- }
64
- }, { output: 'src' });
65
-
66
- const source = generator.generateBundleFileContent(
67
- 'slice-bundle.test.js',
68
- 'route',
69
- [{ name: 'Button', category: 'Visual', categoryType: 'Visual', dependencies: new Set(), size: 100, js: '', html: '<button>ok</button>', css: '.btn{}' }],
70
- '/test'
71
- );
72
-
73
- assert.match(source, /if \(!controller\.classes\.has\("Button"\)\) \{\s*controller\.classes\.set\("Button", SLICE_CLASS_FACTORY_SliceComponent_Button\(\)\);\s*\}/);
74
- assert.match(source, /if \(!controller\.templates\.has\("Button"\)\) \{\s*controller\.templates\.set\("Button", __templateElement_SliceComponent_Button\);\s*\}/);
75
- assert.match(source, /if \(!stylesManager\.__sliceRegisteredComponentStyles\) \{\s*stylesManager\.__sliceRegisteredComponentStyles = new Set\(\);\s*\}/);
76
- assert.match(source, /if \(!stylesManager\.__sliceRegisteredComponentStyles\.has\("Button"\)\) \{\s*stylesManager\.registerComponentStyles\("Button", ".*"\);\s*stylesManager\.__sliceRegisteredComponentStyles\.add\("Button"\);\s*\}/);
77
- assert.match(source, /if \(!controller\.componentCategories\.has\("Button"\)\) \{\s*controller\.componentCategories\.set\("Button", "Visual"\);\s*\}/);
78
- });
79
-
80
- test('registerAll stores templates as template elements', () => {
81
- const generator = new BundleGenerator(import.meta.url, {
82
- components: [],
83
- routes: [],
84
- metrics: {
85
- totalComponents: 0,
86
- totalRoutes: 0,
87
- sharedPercentage: 0,
88
- totalSize: 0
89
- }
90
- }, { output: 'src' });
91
-
92
- const source = generator.generateBundleFileContent(
93
- 'slice-bundle.test.js',
94
- 'route',
95
- [{ name: 'DocumentationPage', category: 'AppComponents', categoryType: 'Visual', dependencies: new Set(), size: 100, js: '', html: '<div>docs</div>', css: '' }],
96
- '/docs'
97
- );
98
-
99
- assert.match(source, /const __templateElement_SliceComponent_DocumentationPage = document\.createElement\('template'\);/);
100
- assert.match(source, /__templateElement_SliceComponent_DocumentationPage\.innerHTML = "<div>docs<\/div>";/);
101
- assert.match(source, /controller\.templates\.set\("DocumentationPage", __templateElement_SliceComponent_DocumentationPage\);/);
102
- });
103
-
104
- test('bundle output inlines dependency modules and binds imported symbols in class factories', () => {
105
- const generator = new BundleGenerator(import.meta.url, {
106
- components: [],
107
- routes: [],
108
- metrics: {
109
- totalComponents: 0,
110
- totalRoutes: 0,
111
- sharedPercentage: 0,
112
- totalSize: 0
113
- }
114
- }, { output: 'src' });
115
-
116
- const source = generator.generateBundleFileContent(
117
- 'slice-bundle.test.js',
118
- 'route',
119
- [{
120
- name: 'DocumentationPage',
121
- category: 'AppComponents',
122
- categoryType: 'Visual',
123
- dependencies: new Set(),
124
- size: 100,
125
- js: 'class DocumentationPage extends HTMLElement { connectedCallback(){ return documentationRoutes.length; } }\nwindow.DocumentationPage = DocumentationPage;\nreturn DocumentationPage;',
126
- html: '',
127
- css: '',
128
- externalDependencies: {
129
- 'App/documentationRoutes.js': {
130
- content: 'export const documentationRoutes = ["/docs"];',
131
- bindings: [{ type: 'named', importedName: 'documentationRoutes', localName: 'documentationRoutes' }]
132
- },
133
- 'App/purify.js': {
134
- content: 'export const purify = (value) => value;',
135
- bindings: [{ type: 'default', importedName: 'default', localName: 'purify' }]
136
- }
137
- }
138
- }],
139
- '/docs'
140
- );
141
-
142
- assert.match(source, /const SLICE_BUNDLE_DEPENDENCIES = \{\};/);
143
- assert.match(source, /const __sliceSharedDeps = typeof window !== 'undefined' \? \(window\.__SLICE_SHARED_DEPS__ \|\| \{\}\) : \{\};/);
144
- assert.match(source, /function __sliceResolveDefaultExport\(dep, depName, preferredKey\) \{/);
145
- assert.match(source, /SLICE_BUNDLE_DEPENDENCIES\["App\/documentationRoutes\.js"\] = __sliceDepExports0;/);
146
- assert.match(source, /const documentationRoutes = __sliceResolveBundleDependency\("App\/documentationRoutes\.js"\)\.documentationRoutes;/);
147
- assert.match(source, /const purify = __sliceResolveDefaultExport\(__sliceResolveBundleDependency\("App\/purify\.js"\), "App\/purify\.js", "purifyData"\);/);
148
- assert.doesNotMatch(source, /\.default !== undefined \?/);
149
- assert.doesNotMatch(source, /SLICE_BUNDLE_DEPENDENCIES\["App\/purify\.js"\]\.purifyData/);
150
- });
151
-
152
- test('route bundle omits extracted vendor-shared modules from inline dependency block and keeps bindings', () => {
153
- const generator = new BundleGenerator(import.meta.url, {
154
- components: [],
155
- routes: [],
156
- metrics: {
157
- totalComponents: 0,
158
- totalRoutes: 0,
159
- sharedPercentage: 0,
160
- totalSize: 0
161
- }
162
- }, { output: 'src' });
163
-
164
- generator.vendorShared.sharedDependencySet = new Set(['App/documentationRoutes.js']);
165
-
166
- const source = generator.generateBundleFileContent(
167
- 'slice-bundle.test.js',
168
- 'route',
169
- [{
170
- name: 'DocumentationPage',
171
- category: 'AppComponents',
172
- categoryType: 'Visual',
173
- dependencies: new Set(),
174
- size: 100,
175
- js: 'class DocumentationPage extends HTMLElement { connectedCallback(){ return [documentationRoutes.length, docsNs.documentationRoutes.length, purify(1)].length; } }\nwindow.DocumentationPage = DocumentationPage;\nreturn DocumentationPage;',
176
- html: '',
177
- css: '',
178
- externalDependencies: {
179
- 'App/documentationRoutes.js': {
180
- content: 'export const documentationRoutes = ["/docs"];',
181
- bindings: [
182
- { type: 'named', importedName: 'documentationRoutes', localName: 'documentationRoutes' },
183
- { type: 'namespace', localName: 'docsNs' }
184
- ]
185
- },
186
- 'App/purify.js': {
187
- content: 'export default (value) => value;',
188
- bindings: [{ type: 'default', importedName: 'default', localName: 'purify' }]
189
- }
190
- }
191
- }],
192
- '/docs'
193
- );
194
-
195
- assert.match(source, /const __sliceSharedDeps = typeof window !== 'undefined' \? \(window\.__SLICE_SHARED_DEPS__ \|\| \{\}\) : \{\};/);
196
- assert.match(source, /const __sliceResolveBundleDependency = \(depName\) => Object\.prototype\.hasOwnProperty\.call\(__sliceSharedDeps, depName\) \? __sliceSharedDeps\[depName\] : SLICE_BUNDLE_DEPENDENCIES\[depName\];/);
197
- assert.doesNotMatch(source, /SLICE_BUNDLE_DEPENDENCIES\["App\/documentationRoutes\.js"\] = __sliceDepExports\d+;/);
198
- assert.match(source, /SLICE_BUNDLE_DEPENDENCIES\["App\/purify\.js"\] = __sliceDepExports\d+;/);
199
- assert.match(source, /const documentationRoutes = __sliceResolveBundleDependency\("App\/documentationRoutes\.js"\)\.documentationRoutes;/);
200
- assert.match(source, /const docsNs = __sliceResolveBundleDependency\("App\/documentationRoutes\.js"\);/);
201
- assert.match(source, /const purify = __sliceResolveDefaultExport\(__sliceResolveBundleDependency\("App\/purify\.js"\), "App\/purify\.js", "purifyData"\);/);
202
- });
203
-
204
- test('route bundle emits guarded shared deps resolver for non-browser contexts', () => {
205
- const generator = new BundleGenerator(import.meta.url, {
206
- components: [],
207
- routes: [],
208
- metrics: {
209
- totalComponents: 0,
210
- totalRoutes: 0,
211
- sharedPercentage: 0,
212
- totalSize: 0
213
- }
214
- }, { output: 'src' });
215
-
216
- const source = generator.generateBundleFileContent(
217
- 'slice-bundle.test.js',
218
- 'route',
219
- [{
220
- name: 'DocsPage',
221
- category: 'AppComponents',
222
- categoryType: 'Visual',
223
- dependencies: new Set(),
224
- size: 100,
225
- js: 'class DocsPage extends HTMLElement {}\nwindow.DocsPage = DocsPage;\nreturn DocsPage;',
226
- html: '',
227
- css: '',
228
- externalDependencies: {
229
- 'App/deps.js': {
230
- content: 'export const value = 1;',
231
- bindings: [{ type: 'named', importedName: 'value', localName: 'value' }]
232
- }
233
- }
234
- }],
235
- '/docs'
236
- );
237
-
238
- assert.match(source, /const __sliceSharedDeps = typeof window !== 'undefined' \? \(window\.__SLICE_SHARED_DEPS__ \|\| \{\}\) : \{\};/);
239
- assert.match(source, /const __sliceResolveBundleDependency = \(depName\) => Object\.prototype\.hasOwnProperty\.call\(__sliceSharedDeps, depName\) \? __sliceSharedDeps\[depName\] : SLICE_BUNDLE_DEPENDENCIES\[depName\];/);
240
- });
241
-
242
- test('non-route bundle does not emit shared resolver block', () => {
243
- const generator = new BundleGenerator(import.meta.url, {
244
- components: [],
245
- routes: [],
246
- metrics: {
247
- totalComponents: 0,
248
- totalRoutes: 0,
249
- sharedPercentage: 0,
250
- totalSize: 0
251
- }
252
- }, { output: 'src' });
253
-
254
- const source = generator.generateBundleFileContent(
255
- 'slice-bundle.framework.js',
256
- 'framework',
257
- [{
258
- name: 'FrameworkComp',
259
- category: 'Framework',
260
- categoryType: 'Visual',
261
- dependencies: new Set(),
262
- size: 100,
263
- js: 'class FrameworkComp extends HTMLElement {}\nwindow.FrameworkComp = FrameworkComp;\nreturn FrameworkComp;',
264
- html: '',
265
- css: '',
266
- externalDependencies: {
267
- 'App/frameworkDep.js': {
268
- content: 'export const dep = 1;',
269
- bindings: [{ type: 'named', importedName: 'dep', localName: 'dep' }]
270
- }
271
- }
272
- }],
273
- null
274
- );
275
-
276
- assert.doesNotMatch(source, /const __sliceSharedDeps = /);
277
- assert.doesNotMatch(source, /const __sliceResolveBundleDependency = /);
278
- assert.match(source, /const dep = SLICE_BUNDLE_DEPENDENCIES\["App\/frameworkDep\.js"\]\.dep;/);
279
- });
280
-
281
- test('route bundle keeps local fallback binding when shared map is unavailable', () => {
282
- const generator = new BundleGenerator(import.meta.url, {
283
- components: [],
284
- routes: [],
285
- metrics: {
286
- totalComponents: 0,
287
- totalRoutes: 0,
288
- sharedPercentage: 0,
289
- totalSize: 0
290
- }
291
- }, { output: 'src' });
292
-
293
- const source = generator.generateBundleFileContent(
294
- 'slice-bundle.test.js',
295
- 'route',
296
- [{
297
- name: 'FallbackComp',
298
- category: 'Visual',
299
- categoryType: 'Visual',
300
- dependencies: new Set(),
301
- size: 100,
302
- js: 'class FallbackComp extends HTMLElement {}\nwindow.FallbackComp = FallbackComp;\nreturn FallbackComp;',
303
- html: '',
304
- css: '',
305
- externalDependencies: {
306
- 'App/local.js': {
307
- content: 'export const local = 1;',
308
- bindings: [{ type: 'named', importedName: 'local', localName: 'local' }]
309
- }
310
- }
311
- }],
312
- '/fallback'
313
- );
314
-
315
- assert.match(source, /const __sliceResolveBundleDependency = \(depName\) => Object\.prototype\.hasOwnProperty\.call\(__sliceSharedDeps, depName\) \? __sliceSharedDeps\[depName\] : SLICE_BUNDLE_DEPENDENCIES\[depName\];/);
316
- assert.match(source, /const local = __sliceResolveBundleDependency\("App\/local\.js"\)\.local;/);
317
- });
318
-
319
- test('bundle output hoists allowed absolute imports to module top-level', () => {
320
- const generator = new BundleGenerator(import.meta.url, {
321
- components: [],
322
- routes: [],
323
- metrics: {
324
- totalComponents: 0,
325
- totalRoutes: 0,
326
- sharedPercentage: 0,
327
- totalSize: 0
328
- }
329
- }, { output: 'src' });
330
-
331
- const source = generator.generateBundleFileContent(
332
- 'slice-bundle.test.js',
333
- 'route',
334
- [{
335
- name: 'HeroCard',
336
- category: 'Visual',
337
- categoryType: 'Visual',
338
- dependencies: new Set(),
339
- size: 100,
340
- js: 'class HeroCard extends HTMLElement {}\nwindow.HeroCard = HeroCard;\nreturn HeroCard;',
341
- html: '',
342
- css: '',
343
- hoistedImports: ["import hero from '/public/hero.js';"]
344
- }],
345
- '/test'
346
- );
347
-
348
- assert.match(source, /import hero from '\/public\/hero\.js';/);
349
- assert.doesNotMatch(source, /SLICE_CLASS_FACTORY_SliceComponent_HeroCard = \(\) => \{[\s\S]*import hero from '\/public\/hero\.js';/);
350
- });
351
-
352
- test('generateBundleFileContent throws on hoisted import local binding collisions', () => {
353
- const generator = new BundleGenerator(import.meta.url, {
354
- components: [],
355
- routes: [],
356
- metrics: {
357
- totalComponents: 0,
358
- totalRoutes: 0,
359
- sharedPercentage: 0,
360
- totalSize: 0
361
- }
362
- }, { output: 'src' });
363
-
364
- assert.throws(() => {
365
- generator.generateBundleFileContent(
366
- 'slice-bundle.test.js',
367
- 'route',
368
- [
369
- {
370
- name: 'CompA',
371
- category: 'Visual',
372
- categoryType: 'Visual',
373
- dependencies: new Set(),
374
- size: 100,
375
- js: 'class CompA extends HTMLElement {}\nwindow.CompA = CompA;\nreturn CompA;',
376
- html: '',
377
- css: '',
378
- hoistedImports: ["import foo from '/public/a.js';"]
379
- },
380
- {
381
- name: 'CompB',
382
- category: 'Visual',
383
- categoryType: 'Visual',
384
- dependencies: new Set(),
385
- size: 100,
386
- js: 'class CompB extends HTMLElement {}\nwindow.CompB = CompB;\nreturn CompB;',
387
- html: '',
388
- css: '',
389
- hoistedImports: ["import foo from '/public/b.js';"]
390
- }
391
- ],
392
- '/test'
393
- );
394
- }, /Hoisted import binding collision: foo/);
395
- });
396
-
397
- test('generateBundleFileContent throws on reserved identifier collision', () => {
398
- const generator = new BundleGenerator(import.meta.url, {
399
- components: [],
400
- routes: [],
401
- metrics: {
402
- totalComponents: 0,
403
- totalRoutes: 0,
404
- sharedPercentage: 0,
405
- totalSize: 0
406
- }
407
- }, { output: 'src' });
408
-
409
- assert.throws(() => {
410
- generator.generateBundleFileContent(
411
- 'slice-bundle.test.js',
412
- 'route',
413
- [{
414
- name: 'CompMeta',
415
- category: 'Visual',
416
- categoryType: 'Visual',
417
- dependencies: new Set(),
418
- size: 100,
419
- js: 'class CompMeta extends HTMLElement {}\nwindow.CompMeta = CompMeta;\nreturn CompMeta;',
420
- html: '',
421
- css: '',
422
- hoistedImports: ["import SLICE_BUNDLE_META from '/public/meta.js';"]
423
- }],
424
- '/test'
425
- );
426
- }, /reserved identifier collision: SLICE_BUNDLE_META/);
427
- });
428
-
429
- test('generateBundleConfig emits vendor-shared metadata and route dependency graph edges', () => {
430
- const generator = new BundleGenerator(import.meta.url, {
431
- components: [],
432
- routes: [],
433
- metrics: {
434
- totalComponents: 0,
435
- totalRoutes: 1,
436
- sharedPercentage: 0,
437
- totalSize: 0
438
- }
439
- }, { output: 'src' });
440
-
441
- generator.bundles.routes = {
442
- docs: {
443
- path: '/docs',
444
- components: [{ name: 'DocumentationPage' }],
445
- size: 100,
446
- file: 'slice-bundle.docs.js'
447
- }
448
- };
449
-
450
- generator.vendorShared.sharedDependencySet = new Set(['App/documentationRoutes.js']);
451
- generator.vendorShared.bundleKeysUsingSharedDependencies = new Set(['docs']);
452
- generator.vendorShared.bundle = {
453
- file: 'slice-bundle.vendor-shared.js',
454
- size: 2048,
455
- hash: 'deadbeef',
456
- integrity: 'sha256:deadbeef'
457
- };
458
-
459
- const config = generator.generateBundleConfig(null);
460
-
461
- assert.equal(config.bundles.vendorShared.bundleKey, 'vendor-shared');
462
- assert.equal(config.bundles.vendorShared.type, 'vendor-shared');
463
- assert.equal(config.bundles.vendorShared.dependencyCount, 1);
464
- assert.deepEqual(config.bundles.routes.docs.dependencies, ['critical', 'vendor-shared']);
465
- assert.deepEqual(config.routeBundles['/docs'], ['critical', 'vendor-shared', 'docs']);
466
- assert.deepEqual(config.routeDependencyGraph['/docs'].edges, [
467
- { from: 'critical', to: 'docs' },
468
- { from: 'vendor-shared', to: 'docs' }
469
- ]);
470
- });
@@ -1,127 +0,0 @@
1
- import { test, describe } from 'node:test';
2
- import assert from 'node:assert/strict';
3
- import { parse } from '@babel/parser';
4
- import BundleGenerator from '../commands/utils/bundling/BundleGenerator.js';
5
-
6
- const MODULE_URL = import.meta.url;
7
-
8
- function makeGenerator() {
9
- const gen = new BundleGenerator(MODULE_URL, null, {});
10
- gen.sliceConfig = {};
11
- return gen;
12
- }
13
-
14
- function evalSnippet(code) {
15
- // eslint-disable-next-line no-new-func
16
- return new Function(code)();
17
- }
18
-
19
- // Build the dependency-exports object the bundler would emit for a single
20
- // shared module, then read back its keys at runtime.
21
- function evalDepExports(content, moduleName = 'shared/util.js') {
22
- const gen = makeGenerator();
23
- const block = gen.buildV2DependencyModuleBlockFromModules([{ name: moduleName, content }]);
24
- // The block declares `const __sliceDepExports0 = {}` and fills it; expose it.
25
- return evalSnippet(`${block}\nreturn typeof __sliceDepExports0 !== 'undefined' ? __sliceDepExports0 : {};`);
26
- }
27
-
28
- describe('shared dependency module transforms', () => {
29
- test('plain named exports resolve', () => {
30
- const d = evalDepExports('export const a = 1;\nexport const b = 2;');
31
- assert.equal(d.a, 1);
32
- assert.equal(d.b, 2);
33
- });
34
-
35
- test('multiline export list resolves every name', () => {
36
- const d = evalDepExports('const a = 1;\nconst b = 2;\nconst c = 3;\nexport {\n a,\n b,\n c\n};');
37
- assert.deepEqual([d.a, d.b, d.c], [1, 2, 3]);
38
- });
39
-
40
- test('export { x as default } exposes a usable default', () => {
41
- // A consumer doing `import def from '...'` resolves default via the runtime
42
- // helper, which first checks `.default`. So the exports object should carry
43
- // a `default` (or the basename-Data fallback) for the aliased-default form.
44
- const d = evalDepExports('const x = 7;\nexport { x as default };', 'shared/util.js');
45
- const resolvable = d.default !== undefined || d.utilData !== undefined;
46
- assert.ok(resolvable, 'aliased default export is not resolvable by the bundle');
47
- });
48
-
49
- test('destructuring export does not silently drop the binding', () => {
50
- // `export const { a } = obj` is a valid ESM export; the consumer expects `a`.
51
- const d = evalDepExports('const obj = { a: 5 };\nexport const { a } = obj;');
52
- assert.equal(d.a, 5, 'destructured export binding was dropped');
53
- });
54
-
55
- test('exports that reference one another resolve at runtime', () => {
56
- // A helper export referencing a constant export must still find it — the
57
- // transform has to keep a usable local binding, not only the exports field.
58
- const d = evalDepExports(
59
- "export const TAG = 'x';\nexport function badge(label) { return '[' + TAG + '] ' + label; }"
60
- );
61
- assert.equal(d.TAG, 'x');
62
- assert.equal(typeof d.badge, 'function');
63
- assert.equal(d.badge('hi'), '[x] hi');
64
- });
65
-
66
- test('a transitive dependency is inlined and bound (topological order)', () => {
67
- const gen = makeGenerator();
68
- // mid imports leaf; pass mid FIRST so the topological sort must reorder.
69
- const block = gen.buildV2DependencyModuleBlockFromModules([
70
- {
71
- name: 'shared/mid.js',
72
- content: "import { LEAF } from './leaf.js';\nexport function midValue() { return 'mid:' + LEAF; }",
73
- moduleImports: [
74
- { depName: 'shared/leaf.js', bindings: [{ type: 'named', importedName: 'LEAF', localName: 'LEAF' }] },
75
- ],
76
- },
77
- { name: 'shared/leaf.js', content: "export const LEAF = 'leaf-value';", moduleImports: [] },
78
- ]);
79
- const deps = evalSnippet(`${block}\nreturn SLICE_BUNDLE_DEPENDENCIES;`);
80
- assert.equal(deps['shared/leaf.js'].LEAF, 'leaf-value');
81
- assert.equal(deps['shared/mid.js'].midValue(), 'mid:leaf-value');
82
- });
83
- });
84
-
85
- describe('transitive dependencies of a shared module', () => {
86
- function bundleWithDependency(depName, depContent, bindings) {
87
- const gen = makeGenerator();
88
- const comp = {
89
- name: 'Widget',
90
- category: 'Visual',
91
- categoryType: 'Visual',
92
- js: 'const C = class {};\nreturn C;',
93
- hoistedImports: [],
94
- html: '',
95
- css: '',
96
- externalDependencies: { [depName]: { content: depContent, bindings } },
97
- size: 100,
98
- };
99
- return gen.generateBundleFileContent('slice-bundle.critical.js', 'critical', [comp]);
100
- }
101
-
102
- test('a relative import inside a shared module must not leak into the bundle', () => {
103
- const code = bundleWithDependency(
104
- 'shared/helper.js',
105
- "import deep from './deep.js';\nexport const helper = () => deep;",
106
- [{ type: 'named', importedName: 'helper', localName: 'helper' }]
107
- );
108
- // Whatever the strategy, the emitted bundle must not contain an unresolved
109
- // relative import — it would 404 / fail to resolve in production.
110
- assert.ok(
111
- !/import\s+[^;]*from\s+['"]\.\.?\//.test(code),
112
- 'unresolved relative import leaked from a transitive dependency'
113
- );
114
- });
115
-
116
- test('a bare import inside a shared module must not leak into the bundle', () => {
117
- const code = bundleWithDependency(
118
- 'shared/helper.js',
119
- "import 'side-effect-polyfill';\nexport const helper = () => 1;",
120
- [{ type: 'named', importedName: 'helper', localName: 'helper' }]
121
- );
122
- assert.ok(
123
- !/import\s+['"][^'"]+['"]/.test(code),
124
- 'unresolved bare import leaked from a transitive dependency'
125
- );
126
- });
127
- });