@useavalon/avalon 0.1.5 → 0.1.7

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 (39) hide show
  1. package/package.json +31 -58
  2. package/src/build/prop-extractors/index.ts +11 -11
  3. package/src/build/prop-extractors/lit.ts +1 -1
  4. package/src/build/prop-extractors/qwik.ts +2 -2
  5. package/src/build/prop-extractors/solid.ts +1 -1
  6. package/src/build/prop-extractors/svelte.ts +1 -1
  7. package/src/schemas/routing.ts +2 -2
  8. package/src/vite-plugin/nitro-integration.ts +1 -1
  9. package/src/vite-plugin/plugin.ts +20 -16
  10. package/src/build/README.md +0 -310
  11. package/src/client/tests/css-hmr-handler.test.ts +0 -360
  12. package/src/client/tests/framework-adapter.test.ts +0 -519
  13. package/src/client/tests/hmr-coordinator.test.ts +0 -176
  14. package/src/client/tests/hydration-option-parsing.test.ts +0 -107
  15. package/src/client/tests/lit-adapter.test.ts +0 -427
  16. package/src/client/tests/preact-adapter.test.ts +0 -353
  17. package/src/client/tests/qwik-adapter.test.ts +0 -343
  18. package/src/client/tests/react-adapter.test.ts +0 -317
  19. package/src/client/tests/solid-adapter.test.ts +0 -396
  20. package/src/client/tests/svelte-adapter.test.ts +0 -387
  21. package/src/client/tests/vue-adapter.test.ts +0 -407
  22. package/src/components/tests/component-analyzer.test.ts +0 -96
  23. package/src/components/tests/component-detection.test.ts +0 -347
  24. package/src/components/tests/persistent-islands.test.ts +0 -398
  25. package/src/core/components/tests/enhanced-framework-detector.test.ts +0 -577
  26. package/src/core/components/tests/framework-registry.test.ts +0 -465
  27. package/src/core/integrations/README.md +0 -282
  28. package/src/core/layout/tests/enhanced-layout-resolver.test.ts +0 -477
  29. package/src/core/layout/tests/layout-cache-optimization.test.ts +0 -149
  30. package/src/core/layout/tests/layout-composer.test.ts +0 -486
  31. package/src/core/layout/tests/layout-data-loader.test.ts +0 -443
  32. package/src/core/layout/tests/layout-discovery.test.ts +0 -253
  33. package/src/core/layout/tests/layout-matcher.test.ts +0 -480
  34. package/src/core/modules/tests/framework-module-resolver.test.ts +0 -263
  35. package/src/core/modules/tests/module-resolution-integration.test.ts +0 -117
  36. package/src/islands/discovery/tests/island-discovery.test.ts +0 -881
  37. package/src/middleware/__tests__/discovery.test.ts +0 -107
  38. package/src/types/tests/layout-types.test.ts +0 -197
  39. package/src/vite-plugin/tests/image-optimization.test.ts +0 -54
@@ -1,486 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import { join, resolve } from 'node:path';
3
- import { existsSync } from 'node:fs';
4
- import { mkdir, writeFile, rm } from 'node:fs/promises';
5
- import { LayoutComposer } from '../layout-composer.ts';
6
- import type { LayoutConfig, LayoutDiscoveryOptions } from '../layout-types.ts';
7
-
8
- // Test fixtures directory
9
- const TEST_FIXTURES_DIR = resolve('tests/fixtures/layout-composer');
10
- const TEST_PAGES_DIR = join(TEST_FIXTURES_DIR, 'src/pages');
11
- const TEST_LAYOUTS_DIR = join(TEST_FIXTURES_DIR, 'src/layouts');
12
-
13
- // Mock page module interface
14
- interface MockPageModule {
15
- default: any;
16
- layoutConfig?: LayoutConfig;
17
- }
18
-
19
- // Helper function to create test layout files
20
- async function createTestLayouts() {
21
- // Plain JS (no JSX) to avoid needing preact/jsx-dev-runtime at test time
22
- const rootLayoutContent = `
23
- export default function RootLayout(props) {
24
- return props.children;
25
- }
26
-
27
- export async function layoutLoader(ctx) {
28
- return { title: 'Root Layout' };
29
- }
30
- `;
31
-
32
- const blogLayoutContent = `
33
- export default function BlogLayout(props) {
34
- return props.children;
35
- }
36
-
37
- export async function layoutLoader(ctx) {
38
- return { blogTitle: 'My Blog' };
39
- }
40
- `;
41
-
42
- const customLayoutContent = `
43
- export default function CustomLayout(props) {
44
- return props.children;
45
- }
46
-
47
- export async function layoutLoader(ctx) {
48
- return { customData: 'Custom Layout Data' };
49
- }
50
- `;
51
-
52
- await writeFile(join(TEST_PAGES_DIR, '_layout.js'), rootLayoutContent);
53
-
54
- await mkdir(join(TEST_PAGES_DIR, 'blog'), { recursive: true });
55
- await writeFile(join(TEST_PAGES_DIR, 'blog', '_layout.js'), blogLayoutContent);
56
-
57
- await writeFile(join(TEST_LAYOUTS_DIR, 'custom.js'), customLayoutContent);
58
- }
59
-
60
- async function setupTestFixtures(): Promise<LayoutComposer> {
61
- if (existsSync(TEST_FIXTURES_DIR)) {
62
- await rm(TEST_FIXTURES_DIR, { recursive: true });
63
- }
64
- await mkdir(TEST_FIXTURES_DIR, { recursive: true });
65
- await mkdir(TEST_PAGES_DIR, { recursive: true });
66
- await mkdir(TEST_LAYOUTS_DIR, { recursive: true });
67
-
68
- await createTestLayouts();
69
-
70
- const options: LayoutDiscoveryOptions = {
71
- baseDirectory: join(TEST_FIXTURES_DIR, 'src'),
72
- filePattern: '_layout.js',
73
- excludeDirectories: ['node_modules', '.git'],
74
- enableWatching: false,
75
- developmentMode: true,
76
- };
77
-
78
- return new LayoutComposer(options);
79
- }
80
-
81
- async function cleanupTestFixtures() {
82
- if (existsSync(TEST_FIXTURES_DIR)) {
83
- await rm(TEST_FIXTURES_DIR, { recursive: true });
84
- }
85
- }
86
-
87
- describe('LayoutComposer - Basic Layout Resolution', () => {
88
- it('should resolve layouts', async () => {
89
- const composer = await setupTestFixtures();
90
-
91
- try {
92
- const pageModule: MockPageModule = {
93
- default: () => 'Page Component',
94
- };
95
-
96
- const layouts = await composer.resolveLayouts('/blog/post', pageModule);
97
-
98
- expect(layouts).toBeDefined();
99
- expect(Array.isArray(layouts)).toEqual(true);
100
- } finally {
101
- await cleanupTestFixtures();
102
- }
103
- });
104
- });
105
-
106
- describe('LayoutComposer - replaceLayout Configuration', () => {
107
- it('should skip all parent layouts', async () => {
108
- const composer = await setupTestFixtures();
109
-
110
- try {
111
- const pageModule: MockPageModule = {
112
- default: () => 'Page Component',
113
- layoutConfig: {
114
- replaceLayout: true,
115
- },
116
- };
117
-
118
- const layouts = await composer.resolveLayouts('/blog/post', pageModule);
119
-
120
- expect(layouts.length).toEqual(0);
121
- } finally {
122
- await cleanupTestFixtures();
123
- }
124
- });
125
-
126
- it('should use only the custom layout with replaceLayout', async () => {
127
- const composer = await setupTestFixtures();
128
-
129
- try {
130
- const customLayoutPath = join(TEST_LAYOUTS_DIR, 'custom.js');
131
-
132
- const pageModule: MockPageModule = {
133
- default: () => 'Page Component',
134
- layoutConfig: {
135
- replaceLayout: true,
136
- customLayout: customLayoutPath,
137
- },
138
- };
139
-
140
- const layouts = await composer.resolveLayouts('/blog/post', pageModule);
141
-
142
- expect(layouts.length).toEqual(1);
143
- expect(layouts[0].path).toEqual(customLayoutPath);
144
- } finally {
145
- await cleanupTestFixtures();
146
- }
147
- });
148
- });
149
-
150
- describe('LayoutComposer - skipLayouts Configuration', () => {
151
- it('should exclude specified layouts', async () => {
152
- const composer = await setupTestFixtures();
153
-
154
- try {
155
- const pageModule: MockPageModule = {
156
- default: () => 'Page Component',
157
- layoutConfig: {
158
- skipLayouts: ['_layout.js'],
159
- },
160
- };
161
-
162
- const allLayouts = await composer.resolveLayouts('/blog/post', { default: () => 'Page' });
163
- const filteredLayouts = await composer.resolveLayouts('/blog/post', pageModule);
164
-
165
- expect(filteredLayouts.length <= allLayouts.length).toEqual(true);
166
- } finally {
167
- await cleanupTestFixtures();
168
- }
169
- });
170
-
171
- it('should support pattern matching', async () => {
172
- const composer = await setupTestFixtures();
173
-
174
- try {
175
- const pageModule: MockPageModule = {
176
- default: () => 'Page Component',
177
- layoutConfig: {
178
- skipLayouts: ['*blog*'],
179
- },
180
- };
181
-
182
- const layouts = await composer.resolveLayouts('/blog/post', pageModule);
183
-
184
- const blogLayouts = layouts.filter(layout => layout.path.includes('blog'));
185
- expect(blogLayouts.length).toEqual(0);
186
- } finally {
187
- await cleanupTestFixtures();
188
- }
189
- });
190
- });
191
-
192
- describe('LayoutComposer - onlyLayouts Configuration', () => {
193
- it('should only apply specified layouts', async () => {
194
- const composer = await setupTestFixtures();
195
-
196
- try {
197
- const pageModule: MockPageModule = {
198
- default: () => 'Page Component',
199
- layoutConfig: {
200
- onlyLayouts: ['_layout.js'],
201
- },
202
- };
203
-
204
- const layouts = await composer.resolveLayouts('/blog/post', pageModule);
205
-
206
- expect(layouts.every(layout => layout.path.includes('_layout.js'))).toEqual(true);
207
- } finally {
208
- await cleanupTestFixtures();
209
- }
210
- });
211
-
212
- it('should support custom layout paths', async () => {
213
- const composer = await setupTestFixtures();
214
-
215
- try {
216
- const customLayoutPath = join(TEST_LAYOUTS_DIR, 'custom.js');
217
-
218
- const pageModule: MockPageModule = {
219
- default: () => 'Page Component',
220
- layoutConfig: {
221
- onlyLayouts: [customLayoutPath],
222
- },
223
- };
224
-
225
- const layouts = await composer.resolveLayouts('/blog/post', pageModule);
226
-
227
- expect(layouts.some(layout => layout.path === customLayoutPath)).toEqual(true);
228
- } finally {
229
- await cleanupTestFixtures();
230
- }
231
- });
232
- });
233
-
234
- describe('LayoutComposer - customLayout Configuration', () => {
235
- it('should add specified layout file', async () => {
236
- const composer = await setupTestFixtures();
237
-
238
- try {
239
- const customLayoutPath = join(TEST_LAYOUTS_DIR, 'custom.js');
240
-
241
- const pageModule: MockPageModule = {
242
- default: () => 'Page Component',
243
- layoutConfig: {
244
- customLayout: customLayoutPath,
245
- },
246
- };
247
-
248
- const layouts = await composer.resolveLayouts('/blog/post', pageModule);
249
-
250
- expect(layouts.some(layout => layout.path === customLayoutPath)).toEqual(true);
251
- } finally {
252
- await cleanupTestFixtures();
253
- }
254
- });
255
-
256
- it('should support relative paths', async () => {
257
- const composer = await setupTestFixtures();
258
-
259
- try {
260
- const pageModule: MockPageModule = {
261
- default: () => 'Page Component',
262
- layoutConfig: {
263
- customLayout: 'src/layouts/custom.js',
264
- },
265
- };
266
-
267
- const layouts = await composer.resolveLayouts('/blog/post', pageModule);
268
-
269
- expect(layouts.some(layout => layout.path.includes('custom.js'))).toEqual(true);
270
- } finally {
271
- await cleanupTestFixtures();
272
- }
273
- });
274
- });
275
-
276
- describe('LayoutComposer - Configuration Validation', () => {
277
- it('should validate valid config', async () => {
278
- const composer = await setupTestFixtures();
279
-
280
- try {
281
- const validConfig: LayoutConfig = {
282
- skipLayouts: ['layout1.tsx'],
283
- customLayout: 'custom.tsx',
284
- };
285
-
286
- const result = composer.validateLayoutConfig(validConfig);
287
-
288
- expect(result.valid).toEqual(true);
289
- expect(result.errors.length).toEqual(0);
290
- } finally {
291
- await cleanupTestFixtures();
292
- }
293
- });
294
-
295
- it('should detect conflicting replaceLayout and onlyLayouts', async () => {
296
- const composer = await setupTestFixtures();
297
-
298
- try {
299
- const invalidConfig: LayoutConfig = {
300
- replaceLayout: true,
301
- onlyLayouts: ['layout1.tsx'],
302
- };
303
-
304
- const result = composer.validateLayoutConfig(invalidConfig);
305
-
306
- expect(result.valid).toEqual(false);
307
- expect(result.errors.includes('replaceLayout and onlyLayouts cannot be used together')).toEqual(true);
308
- } finally {
309
- await cleanupTestFixtures();
310
- }
311
- });
312
-
313
- it('should detect conflicting replaceLayout and skipLayouts', async () => {
314
- const composer = await setupTestFixtures();
315
-
316
- try {
317
- const invalidConfig: LayoutConfig = {
318
- replaceLayout: true,
319
- skipLayouts: ['layout1.tsx'],
320
- };
321
-
322
- const result = composer.validateLayoutConfig(invalidConfig);
323
-
324
- expect(result.valid).toEqual(false);
325
- expect(result.errors.includes('replaceLayout and skipLayouts cannot be used together')).toEqual(true);
326
- } finally {
327
- await cleanupTestFixtures();
328
- }
329
- });
330
-
331
- it('should validate array types', async () => {
332
- const composer = await setupTestFixtures();
333
-
334
- try {
335
- const invalidConfig: any = {
336
- skipLayouts: 'not-an-array',
337
- onlyLayouts: 123,
338
- };
339
-
340
- const result = composer.validateLayoutConfig(invalidConfig);
341
-
342
- expect(result.valid).toEqual(false);
343
- expect(result.errors.includes('skipLayouts must be an array of strings')).toEqual(true);
344
- expect(result.errors.includes('onlyLayouts must be an array of strings')).toEqual(true);
345
- } finally {
346
- await cleanupTestFixtures();
347
- }
348
- });
349
-
350
- it('should validate customLayout type', async () => {
351
- const composer = await setupTestFixtures();
352
-
353
- try {
354
- const invalidConfig: any = {
355
- customLayout: 123,
356
- };
357
-
358
- const result = composer.validateLayoutConfig(invalidConfig);
359
-
360
- expect(result.valid).toEqual(false);
361
- expect(result.errors.includes('customLayout must be a string path')).toEqual(true);
362
- } finally {
363
- await cleanupTestFixtures();
364
- }
365
- });
366
- });
367
-
368
- describe('LayoutComposer - Complex Configuration', () => {
369
- it('skipLayouts with customLayout should work together', async () => {
370
- const composer = await setupTestFixtures();
371
-
372
- try {
373
- const customLayoutPath = join(TEST_LAYOUTS_DIR, 'custom.js');
374
-
375
- const pageModule: MockPageModule = {
376
- default: () => 'Page Component',
377
- layoutConfig: {
378
- skipLayouts: ['*blog*'],
379
- customLayout: customLayoutPath,
380
- },
381
- };
382
-
383
- const layouts = await composer.resolveLayouts('/blog/post', pageModule);
384
-
385
- expect(layouts.some(layout => layout.path === customLayoutPath)).toEqual(true);
386
- expect(layouts.filter(layout => layout.path.includes('blog')).length).toEqual(0);
387
- } finally {
388
- await cleanupTestFixtures();
389
- }
390
- });
391
- });
392
-
393
- describe('LayoutComposer - Cache Management', () => {
394
- it('should cache custom layouts', async () => {
395
- const composer = await setupTestFixtures();
396
-
397
- try {
398
- const customLayoutPath = join(TEST_LAYOUTS_DIR, 'custom.js');
399
-
400
- const pageModule: MockPageModule = {
401
- default: () => 'Page Component',
402
- layoutConfig: {
403
- customLayout: customLayoutPath,
404
- },
405
- };
406
-
407
- await composer.resolveLayouts('/blog/post', pageModule);
408
- const stats1 = composer.getCompositionStats();
409
-
410
- await composer.resolveLayouts('/blog/post', pageModule);
411
- const stats2 = composer.getCompositionStats();
412
-
413
- expect(stats1.customLayoutCacheSize > 0).toEqual(true);
414
- expect(stats2.customLayoutCacheSize).toEqual(stats1.customLayoutCacheSize);
415
- } finally {
416
- await cleanupTestFixtures();
417
- }
418
- });
419
-
420
- it('should clear cache', async () => {
421
- const composer = await setupTestFixtures();
422
-
423
- try {
424
- const customLayoutPath = join(TEST_LAYOUTS_DIR, 'custom.js');
425
-
426
- const pageModule: MockPageModule = {
427
- default: () => 'Page Component',
428
- layoutConfig: {
429
- customLayout: customLayoutPath,
430
- },
431
- };
432
-
433
- await composer.resolveLayouts('/blog/post', pageModule);
434
-
435
- composer.clearCache();
436
- const stats = composer.getCompositionStats();
437
-
438
- expect(stats.customLayoutCacheSize).toEqual(0);
439
- } finally {
440
- await cleanupTestFixtures();
441
- }
442
- });
443
- });
444
-
445
- describe('LayoutComposer - Development Mode', () => {
446
- it('should not throw in development mode', async () => {
447
- const composer = await setupTestFixtures();
448
-
449
- try {
450
- expect(composer.isDevelopmentMode()).toEqual(true);
451
-
452
- const pageModule: MockPageModule = {
453
- default: () => 'Page Component',
454
- layoutConfig: {
455
- replaceLayout: true,
456
- },
457
- };
458
-
459
- const layouts = await composer.resolveLayouts('/blog/post', pageModule);
460
- expect(layouts).toBeDefined();
461
- } finally {
462
- await cleanupTestFixtures();
463
- }
464
- });
465
- });
466
-
467
- describe('LayoutComposer - Error Handling', () => {
468
- it('should handle invalid custom layout path', async () => {
469
- const composer = await setupTestFixtures();
470
-
471
- try {
472
- const pageModule: MockPageModule = {
473
- default: () => 'Page Component',
474
- layoutConfig: {
475
- customLayout: '/invalid/path/that/does/not/exist.tsx',
476
- },
477
- };
478
-
479
- const layouts = await composer.resolveLayouts('/blog/post', pageModule);
480
- expect(layouts).toBeDefined();
481
- expect(Array.isArray(layouts)).toEqual(true);
482
- } finally {
483
- await cleanupTestFixtures();
484
- }
485
- });
486
- });