@ecopages/core 0.2.0-alpha.25 → 0.2.0-alpha.27

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 (111) hide show
  1. package/README.md +63 -7
  2. package/package.json +4 -47
  3. package/src/adapters/bun/create-app.ts +54 -2
  4. package/src/adapters/bun/hmr-manager.test.ts +0 -2
  5. package/src/adapters/bun/hmr-manager.ts +1 -24
  6. package/src/adapters/bun/server-adapter.ts +30 -4
  7. package/src/adapters/node/node-hmr-manager.test.ts +0 -2
  8. package/src/adapters/node/node-hmr-manager.ts +2 -25
  9. package/src/adapters/shared/explicit-static-render-preparation.ts +58 -0
  10. package/src/adapters/shared/explicit-static-route-matcher.test.ts +6 -6
  11. package/src/adapters/shared/explicit-static-route-matcher.ts +22 -31
  12. package/src/adapters/shared/file-route-middleware-pipeline.test.ts +5 -10
  13. package/src/adapters/shared/file-route-middleware-pipeline.ts +8 -17
  14. package/src/adapters/shared/fs-server-response-factory.test.ts +32 -43
  15. package/src/adapters/shared/fs-server-response-factory.ts +15 -37
  16. package/src/adapters/shared/fs-server-response-matcher.test.ts +65 -39
  17. package/src/adapters/shared/fs-server-response-matcher.ts +94 -43
  18. package/src/adapters/shared/hmr-manager.contract.test.ts +0 -4
  19. package/src/adapters/shared/render-context.ts +3 -3
  20. package/src/adapters/shared/server-adapter.test.ts +53 -0
  21. package/src/adapters/shared/server-adapter.ts +228 -159
  22. package/src/adapters/shared/server-route-handler.test.ts +6 -5
  23. package/src/adapters/shared/server-route-handler.ts +4 -4
  24. package/src/adapters/shared/server-static-builder.test.ts +4 -4
  25. package/src/adapters/shared/server-static-builder.ts +4 -4
  26. package/src/config/README.md +1 -1
  27. package/src/config/config-builder.test.ts +0 -1
  28. package/src/config/config-builder.ts +2 -7
  29. package/src/dev/host-runtime.ts +34 -0
  30. package/src/eco/eco.browser.test.ts +2 -2
  31. package/src/eco/eco.browser.ts +2 -2
  32. package/src/eco/eco.test.ts +6 -6
  33. package/src/eco/eco.ts +12 -12
  34. package/src/eco/eco.types.ts +3 -3
  35. package/src/errors/index.ts +1 -0
  36. package/src/hmr/client/hmr-runtime.ts +4 -2
  37. package/src/hmr/strategies/js-hmr-strategy.test.ts +0 -1
  38. package/src/hmr/strategies/js-hmr-strategy.ts +0 -6
  39. package/src/integrations/ghtml/ghtml-renderer.test.ts +7 -7
  40. package/src/integrations/ghtml/ghtml-renderer.ts +1 -11
  41. package/src/plugins/eco-component-meta-plugin.ts +0 -1
  42. package/src/plugins/integration-plugin.test.ts +9 -14
  43. package/src/plugins/integration-plugin.ts +34 -22
  44. package/src/plugins/processor.ts +17 -0
  45. package/src/route-renderer/GRAPH.md +81 -289
  46. package/src/route-renderer/README.md +67 -105
  47. package/src/route-renderer/orchestration/component-render-context.ts +45 -38
  48. package/src/route-renderer/orchestration/declared-ownership-graph.ts +62 -0
  49. package/src/route-renderer/orchestration/foreign-subtree-execution.service.ts +383 -0
  50. package/src/route-renderer/orchestration/integration-renderer.test.ts +118 -121
  51. package/src/route-renderer/orchestration/integration-renderer.ts +362 -403
  52. package/src/route-renderer/orchestration/ownership-planning.service.ts +97 -0
  53. package/src/route-renderer/orchestration/ownership-validation.service.ts +76 -0
  54. package/src/route-renderer/orchestration/processed-asset-dedupe.ts +1 -1
  55. package/src/route-renderer/orchestration/{queued-boundary-runtime.service.test.ts → queued-foreign-subtree-resolution.service.test.ts} +76 -71
  56. package/src/route-renderer/orchestration/{queued-boundary-runtime.service.ts → queued-foreign-subtree-resolution.service.ts} +68 -63
  57. package/src/route-renderer/orchestration/render-output.utils.ts +21 -13
  58. package/src/route-renderer/orchestration/{render-preparation.service.test.ts → route-render-orchestrator.prepare-render-options.test.ts} +160 -85
  59. package/src/route-renderer/orchestration/route-render-orchestrator.test.ts +265 -0
  60. package/src/route-renderer/orchestration/{render-preparation.service.ts → route-render-orchestrator.ts} +244 -160
  61. package/src/route-renderer/page-loading/component-dependency-collection.ts +9 -3
  62. package/src/route-renderer/page-loading/declared-asset-collection.ts +2 -5
  63. package/src/route-renderer/page-loading/dependency-resolver.test.ts +107 -11
  64. package/src/route-renderer/page-loading/dependency-resolver.ts +6 -12
  65. package/src/route-renderer/page-loading/ecopages-virtual-imports.ts +1 -1
  66. package/src/route-renderer/page-loading/lazy-entry-collection.ts +1 -1
  67. package/src/route-renderer/page-loading/lazy-trigger-planning.ts +1 -1
  68. package/src/route-renderer/page-loading/module-declaration-aggregation.ts +1 -1
  69. package/src/route-renderer/page-loading/module-declaration-scripts.ts +1 -1
  70. package/src/route-renderer/page-loading/page-dependency-bundling.ts +105 -66
  71. package/src/route-renderer/route-renderer.ts +28 -31
  72. package/src/router/README.md +16 -19
  73. package/src/router/server/route-registry.test.ts +176 -0
  74. package/src/router/server/route-registry.ts +382 -0
  75. package/src/services/README.md +1 -2
  76. package/src/services/assets/asset-processing-service/asset-dependency-keys.ts +1 -1
  77. package/src/services/assets/asset-processing-service/asset-processing.service.test.ts +1 -4
  78. package/src/services/assets/asset-processing-service/asset-processing.service.ts +1 -2
  79. package/src/services/assets/asset-processing-service/assets.types.ts +3 -0
  80. package/src/services/assets/asset-processing-service/grouped-content-bundles.ts +1 -1
  81. package/src/services/assets/asset-processing-service/index.ts +1 -0
  82. package/src/{route-renderer/orchestration/page-packaging.service.test.ts → services/assets/asset-processing-service/page-package.test.ts} +38 -14
  83. package/src/services/assets/asset-processing-service/page-package.ts +93 -0
  84. package/src/services/assets/asset-processing-service/processors/base/base-script-processor.ts +4 -5
  85. package/src/services/assets/asset-processing-service/processors/script/content-script.processor.test.ts +13 -10
  86. package/src/services/assets/asset-processing-service/processors/script/content-script.processor.ts +3 -0
  87. package/src/services/assets/asset-processing-service/processors/script/file-script.processor.ts +6 -0
  88. package/src/services/assets/asset-processing-service/processors/script/node-module-script.processor.ts +2 -0
  89. package/src/services/assets/asset-processing-service/processors/stylesheet/content-stylesheet.processor.ts +1 -0
  90. package/src/services/assets/asset-processing-service/processors/stylesheet/file-stylesheet.processor.ts +2 -0
  91. package/src/services/assets/asset-processing-service/ungrouped-dependency-processing.ts +1 -1
  92. package/src/services/html/html-transformer.service.test.ts +1 -4
  93. package/src/services/module-loading/app-server-module-transpiler.service.ts +1 -3
  94. package/src/services/module-loading/node-bootstrap-plugin.ts +17 -3
  95. package/src/services/module-loading/page-module-import.service.ts +0 -1
  96. package/src/services/module-loading/source-module-support.ts +1 -1
  97. package/src/static-site-generator/static-site-generator.test.ts +124 -32
  98. package/src/static-site-generator/static-site-generator.ts +168 -185
  99. package/src/types/internal-types.ts +13 -12
  100. package/src/types/public-types.ts +55 -39
  101. package/src/watchers/project-watcher.test-helpers.ts +4 -3
  102. package/src/route-renderer/orchestration/boundary-planning.service.ts +0 -146
  103. package/src/route-renderer/orchestration/page-packaging.service.ts +0 -85
  104. package/src/route-renderer/orchestration/render-execution.service.test.ts +0 -196
  105. package/src/route-renderer/orchestration/render-execution.service.ts +0 -182
  106. package/src/route-renderer/orchestration/route-shell-composer.service.ts +0 -162
  107. package/src/router/server/fs-router-scanner.test.ts +0 -83
  108. package/src/router/server/fs-router-scanner.ts +0 -224
  109. package/src/router/server/fs-router.test.ts +0 -214
  110. package/src/router/server/fs-router.ts +0 -122
  111. package/src/services/runtime-state/runtime-specifier-registry.service.ts +0 -96
@@ -2,8 +2,6 @@ import { describe, expect, test, beforeEach, afterEach, vi } from 'vitest';
2
2
  import { fileSystem } from '@ecopages/file-system';
3
3
  import { StaticSiteGenerator } from './static-site-generator';
4
4
  import type { EcoPagesAppConfig } from '../types/internal-types';
5
- import type { FSRouter } from '../router/server/fs-router';
6
- import type { RouteRendererFactory } from '../route-renderer/route-renderer';
7
5
  import { appLogger } from '../global/app-logger.ts';
8
6
  import { DEFAULT_ECOPAGES_WORK_DIR } from '../config/constants.ts';
9
7
 
@@ -30,6 +28,17 @@ const createMockConfig = (overrides: Partial<EcoPagesAppConfig> = {}): EcoPagesA
30
28
  ...overrides,
31
29
  }) as EcoPagesAppConfig;
32
30
 
31
+ const testInjectedMeta = {
32
+ id: 'test-page',
33
+ file: '/src/pages/test.tsx',
34
+ integration: 'test',
35
+ };
36
+
37
+ type StaticGenerationRouteSource = Parameters<StaticSiteGenerator['generateStaticPages']>[0];
38
+ type StaticPageRouteRendererFactory = NonNullable<Parameters<StaticSiteGenerator['generateStaticPages']>[2]>;
39
+ type StaticGenerationRendererFactory = NonNullable<Parameters<StaticSiteGenerator['run']>[0]['routeRendererFactory']>;
40
+ type StaticGenerationRunnerInput = Parameters<StaticSiteGenerator['run']>[0];
41
+
33
42
  describe('StaticSiteGenerator', () => {
34
43
  let ensureDirMock: any;
35
44
  let writeMock: any;
@@ -144,11 +153,21 @@ describe('StaticSiteGenerator', () => {
144
153
  describe('generateStaticPages', () => {
145
154
  const createMockRouter = (routes: Record<string, { filePath: string; pathname: string }>) =>
146
155
  ({
147
- routes: Object.fromEntries(
148
- Object.entries(routes).map(([key, value]) => [key, { ...value, kind: 'exact' }]),
156
+ listStaticGenerationRoutes: vi.fn(async () =>
157
+ Object.values(routes)
158
+ .filter((value) => !value.pathname.includes('['))
159
+ .map((value) => ({
160
+ requestUrl: `http://localhost:3000${value.pathname}`,
161
+ pathname: value.pathname,
162
+ templateRoute: {
163
+ ...value,
164
+ kind: 'exact' as const,
165
+ paramNames: [],
166
+ },
167
+ params: {},
168
+ })),
149
169
  ),
150
- origin: 'http://localhost:3000',
151
- }) as unknown as FSRouter;
170
+ }) satisfies StaticGenerationRouteSource;
152
171
 
153
172
  const createStaticPageModule = () => ({
154
173
  default: Object.assign(() => null, { cache: 'static' }),
@@ -162,11 +181,11 @@ describe('StaticSiteGenerator', () => {
162
181
  });
163
182
 
164
183
  const RendererFactory = {
165
- createRenderer: vi.fn(() => ({
184
+ getPageRenderer: vi.fn(() => ({
166
185
  loadPageModule: vi.fn(async () => createStaticPageModule()),
167
- createRoute: vi.fn(async () => ({ body: '<html>Static</html>' })),
186
+ execute: vi.fn(async () => ({ body: '<html>Static</html>' })),
168
187
  })),
169
- } as unknown as RouteRendererFactory;
188
+ } satisfies StaticPageRouteRendererFactory;
170
189
 
171
190
  await ssg.generateStaticPages(Router, 'http://localhost:3000', RendererFactory);
172
191
 
@@ -180,11 +199,11 @@ describe('StaticSiteGenerator', () => {
180
199
  });
181
200
 
182
201
  const RendererFactory = {
183
- createRenderer: vi.fn(() => ({
202
+ getPageRenderer: vi.fn(() => ({
184
203
  loadPageModule: vi.fn(async () => createStaticPageModule()),
185
- createRoute: vi.fn(async () => ({ body: '<html>Blog Post</html>' })),
204
+ execute: vi.fn(async () => ({ body: '<html>Blog Post</html>' })),
186
205
  })),
187
- } as unknown as RouteRendererFactory;
206
+ } satisfies StaticPageRouteRendererFactory;
188
207
 
189
208
  await ssg.generateStaticPages(Router, 'http://localhost:3000', RendererFactory);
190
209
 
@@ -207,11 +226,11 @@ describe('StaticSiteGenerator', () => {
207
226
  });
208
227
 
209
228
  const RendererFactory = {
210
- createRenderer: vi.fn(() => ({
229
+ getPageRenderer: vi.fn(() => ({
211
230
  loadPageModule: vi.fn(async () => createStaticPageModule()),
212
- createRoute: vi.fn(async () => ({ body: '<html>Home</html>' })),
231
+ execute: vi.fn(async () => ({ body: '<html>Home</html>' })),
213
232
  })),
214
- } as unknown as RouteRendererFactory;
233
+ } satisfies StaticPageRouteRendererFactory;
215
234
 
216
235
  await ssg.generateStaticPages(Router, 'http://localhost:3000', RendererFactory);
217
236
 
@@ -226,11 +245,11 @@ describe('StaticSiteGenerator', () => {
226
245
 
227
246
  const bufferContent = Buffer.from('<html>Buffer Content</html>');
228
247
  const RendererFactory = {
229
- createRenderer: vi.fn(() => ({
248
+ getPageRenderer: vi.fn(() => ({
230
249
  loadPageModule: vi.fn(async () => createStaticPageModule()),
231
- createRoute: vi.fn(async () => ({ body: bufferContent })),
250
+ execute: vi.fn(async () => ({ body: bufferContent })),
232
251
  })),
233
- } as unknown as RouteRendererFactory;
252
+ } satisfies StaticPageRouteRendererFactory;
234
253
 
235
254
  await ssg.generateStaticPages(Router, 'http://localhost:3000', RendererFactory);
236
255
 
@@ -243,24 +262,24 @@ describe('StaticSiteGenerator', () => {
243
262
  '/dashboard': { filePath: '/src/pages/dashboard.tsx', pathname: '/dashboard' },
244
263
  });
245
264
 
246
- const createRoute = vi.fn(async () => ({ body: '<html>Dashboard</html>' }));
265
+ const execute = vi.fn(async () => ({ body: '<html>Dashboard</html>' }));
247
266
  const loadPageModule = vi.fn(async () => ({
248
267
  default: Object.assign(() => null, { cache: 'dynamic' }),
249
268
  }));
250
269
  const RendererFactory = {
251
- createRenderer: vi.fn(() => ({
252
- createRoute,
270
+ getPageRenderer: vi.fn(() => ({
271
+ execute,
253
272
  loadPageModule,
254
273
  })),
255
- } as unknown as RouteRendererFactory;
274
+ } satisfies StaticPageRouteRendererFactory;
256
275
 
257
276
  await ssg.generateStaticPages(Router, 'http://localhost:3000', RendererFactory);
258
277
 
259
- expect(RendererFactory.createRenderer).toHaveBeenCalledWith('/src/pages/dashboard.tsx');
278
+ expect(RendererFactory.getPageRenderer).toHaveBeenCalledWith('/src/pages/dashboard.tsx');
260
279
  expect(loadPageModule).toHaveBeenCalledWith('/src/pages/dashboard.tsx', {
261
280
  cacheScope: 'static-page-probe',
262
281
  });
263
- expect(createRoute).not.toHaveBeenCalled();
282
+ expect(execute).not.toHaveBeenCalled();
264
283
  expect(writeMock).not.toHaveBeenCalled();
265
284
  expect(appLogger.warn).toHaveBeenCalledWith(
266
285
  "Pages with cache: 'dynamic' are not supported in static generation or preview, so they will be skipped\n",
@@ -273,9 +292,8 @@ describe('StaticSiteGenerator', () => {
273
292
  test('should call generateRobotsTxt and generateStaticPages', async () => {
274
293
  const ssg = new StaticSiteGenerator({ appConfig: createMockConfig() });
275
294
  const Router = {
276
- routes: {},
277
- origin: 'http://localhost:3000',
278
- } as unknown as FSRouter;
295
+ listStaticGenerationRoutes: vi.fn(async () => []),
296
+ } satisfies StaticGenerationRouteSource;
279
297
 
280
298
  await ssg.run({
281
299
  router: Router,
@@ -292,13 +310,13 @@ describe('StaticSiteGenerator', () => {
292
310
 
293
311
  await ssg.run({
294
312
  router: {
295
- routes: {},
296
- origin: 'http://localhost:3000',
297
- } as unknown as FSRouter,
313
+ listStaticGenerationRoutes: vi.fn(async () => []),
314
+ } satisfies StaticGenerationRunnerInput['router'],
298
315
  baseUrl: 'http://localhost:3000',
299
316
  routeRendererFactory: {
300
- getRendererByIntegration: vi.fn(),
301
- } as unknown as RouteRendererFactory,
317
+ getPageRenderer: vi.fn(),
318
+ getExplicitViewRenderer: vi.fn(),
319
+ } satisfies StaticGenerationRendererFactory,
302
320
  staticRoutes: [
303
321
  {
304
322
  path: '/dashboard',
@@ -312,5 +330,79 @@ describe('StaticSiteGenerator', () => {
312
330
  '➤ /dashboard',
313
331
  );
314
332
  });
333
+
334
+ test('should render dynamic explicit static routes from staticPaths', async () => {
335
+ const ssg = new StaticSiteGenerator({
336
+ appConfig: createMockConfig({ baseUrl: 'http://localhost:3000' } as any),
337
+ });
338
+ const renderToResponse = vi.fn(async () => new Response('<html>Post</html>'));
339
+ const staticProps = vi.fn(async ({ pathname }) => ({
340
+ props: pathname.params,
341
+ }));
342
+
343
+ await ssg.run({
344
+ router: {
345
+ listStaticGenerationRoutes: vi.fn(async () => []),
346
+ } satisfies StaticGenerationRunnerInput['router'],
347
+ baseUrl: 'http://localhost:3000',
348
+ routeRendererFactory: {
349
+ getPageRenderer: vi.fn(),
350
+ getExplicitViewRenderer: vi.fn(() => ({
351
+ renderToResponse,
352
+ })),
353
+ } satisfies StaticGenerationRendererFactory,
354
+ staticRoutes: [
355
+ {
356
+ path: '/blog/[slug]',
357
+ loader: async () => ({
358
+ default: Object.assign(() => null, {
359
+ config: { __eco: testInjectedMeta },
360
+ staticPaths: async () => ({ paths: [{ params: { slug: 'hello-world' } }] }),
361
+ staticProps,
362
+ }),
363
+ }),
364
+ },
365
+ ],
366
+ });
367
+
368
+ expect(staticProps).toHaveBeenCalledWith({
369
+ pathname: { params: { slug: 'hello-world' } },
370
+ appConfig: expect.any(Object),
371
+ runtimeOrigin: 'http://localhost:3000',
372
+ });
373
+ expect(renderToResponse).toHaveBeenCalledWith(expect.any(Function), { slug: 'hello-world' }, {});
374
+ expect(writeMock).toHaveBeenCalledWith('/test/project/dist/blog/hello-world.html', '<html>Post</html>');
375
+ });
376
+
377
+ test('should log an error for dynamic explicit routes without staticPaths', async () => {
378
+ const ssg = new StaticSiteGenerator({
379
+ appConfig: createMockConfig({ baseUrl: 'http://localhost:3000' } as any),
380
+ });
381
+
382
+ await ssg.run({
383
+ router: {
384
+ listStaticGenerationRoutes: vi.fn(async () => []),
385
+ } satisfies StaticGenerationRunnerInput['router'],
386
+ baseUrl: 'http://localhost:3000',
387
+ routeRendererFactory: {
388
+ getPageRenderer: vi.fn(),
389
+ getExplicitViewRenderer: vi.fn(() => ({
390
+ renderToResponse: vi.fn(),
391
+ })),
392
+ } satisfies StaticGenerationRendererFactory,
393
+ staticRoutes: [
394
+ {
395
+ path: '/blog/[slug]',
396
+ loader: async () => ({
397
+ default: Object.assign(() => null, {
398
+ config: { __eco: testInjectedMeta },
399
+ }),
400
+ }),
401
+ },
402
+ ],
403
+ });
404
+
405
+ expect(writeMock).not.toHaveBeenCalledWith('/test/project/dist/blog/hello-world.html', expect.anything());
406
+ });
315
407
  });
316
408
  });