@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.
- package/README.md +63 -7
- package/package.json +4 -47
- package/src/adapters/bun/create-app.ts +54 -2
- package/src/adapters/bun/hmr-manager.test.ts +0 -2
- package/src/adapters/bun/hmr-manager.ts +1 -24
- package/src/adapters/bun/server-adapter.ts +30 -4
- package/src/adapters/node/node-hmr-manager.test.ts +0 -2
- package/src/adapters/node/node-hmr-manager.ts +2 -25
- package/src/adapters/shared/explicit-static-render-preparation.ts +58 -0
- package/src/adapters/shared/explicit-static-route-matcher.test.ts +6 -6
- package/src/adapters/shared/explicit-static-route-matcher.ts +22 -31
- package/src/adapters/shared/file-route-middleware-pipeline.test.ts +5 -10
- package/src/adapters/shared/file-route-middleware-pipeline.ts +8 -17
- package/src/adapters/shared/fs-server-response-factory.test.ts +32 -43
- package/src/adapters/shared/fs-server-response-factory.ts +15 -37
- package/src/adapters/shared/fs-server-response-matcher.test.ts +65 -39
- package/src/adapters/shared/fs-server-response-matcher.ts +94 -43
- package/src/adapters/shared/hmr-manager.contract.test.ts +0 -4
- package/src/adapters/shared/render-context.ts +3 -3
- package/src/adapters/shared/server-adapter.test.ts +53 -0
- package/src/adapters/shared/server-adapter.ts +228 -159
- package/src/adapters/shared/server-route-handler.test.ts +6 -5
- package/src/adapters/shared/server-route-handler.ts +4 -4
- package/src/adapters/shared/server-static-builder.test.ts +4 -4
- package/src/adapters/shared/server-static-builder.ts +4 -4
- package/src/config/README.md +1 -1
- package/src/config/config-builder.test.ts +0 -1
- package/src/config/config-builder.ts +2 -7
- package/src/dev/host-runtime.ts +34 -0
- package/src/eco/eco.browser.test.ts +2 -2
- package/src/eco/eco.browser.ts +2 -2
- package/src/eco/eco.test.ts +6 -6
- package/src/eco/eco.ts +12 -12
- package/src/eco/eco.types.ts +3 -3
- package/src/errors/index.ts +1 -0
- package/src/hmr/client/hmr-runtime.ts +4 -2
- package/src/hmr/strategies/js-hmr-strategy.test.ts +0 -1
- package/src/hmr/strategies/js-hmr-strategy.ts +0 -6
- package/src/integrations/ghtml/ghtml-renderer.test.ts +7 -7
- package/src/integrations/ghtml/ghtml-renderer.ts +1 -11
- package/src/plugins/eco-component-meta-plugin.ts +0 -1
- package/src/plugins/integration-plugin.test.ts +9 -14
- package/src/plugins/integration-plugin.ts +34 -22
- package/src/plugins/processor.ts +17 -0
- package/src/route-renderer/GRAPH.md +81 -289
- package/src/route-renderer/README.md +67 -105
- package/src/route-renderer/orchestration/component-render-context.ts +45 -38
- package/src/route-renderer/orchestration/declared-ownership-graph.ts +62 -0
- package/src/route-renderer/orchestration/foreign-subtree-execution.service.ts +383 -0
- package/src/route-renderer/orchestration/integration-renderer.test.ts +118 -121
- package/src/route-renderer/orchestration/integration-renderer.ts +362 -403
- package/src/route-renderer/orchestration/ownership-planning.service.ts +97 -0
- package/src/route-renderer/orchestration/ownership-validation.service.ts +76 -0
- package/src/route-renderer/orchestration/processed-asset-dedupe.ts +1 -1
- package/src/route-renderer/orchestration/{queued-boundary-runtime.service.test.ts → queued-foreign-subtree-resolution.service.test.ts} +76 -71
- package/src/route-renderer/orchestration/{queued-boundary-runtime.service.ts → queued-foreign-subtree-resolution.service.ts} +68 -63
- package/src/route-renderer/orchestration/render-output.utils.ts +21 -13
- package/src/route-renderer/orchestration/{render-preparation.service.test.ts → route-render-orchestrator.prepare-render-options.test.ts} +160 -85
- package/src/route-renderer/orchestration/route-render-orchestrator.test.ts +265 -0
- package/src/route-renderer/orchestration/{render-preparation.service.ts → route-render-orchestrator.ts} +244 -160
- package/src/route-renderer/page-loading/component-dependency-collection.ts +9 -3
- package/src/route-renderer/page-loading/declared-asset-collection.ts +2 -5
- package/src/route-renderer/page-loading/dependency-resolver.test.ts +107 -11
- package/src/route-renderer/page-loading/dependency-resolver.ts +6 -12
- package/src/route-renderer/page-loading/ecopages-virtual-imports.ts +1 -1
- package/src/route-renderer/page-loading/lazy-entry-collection.ts +1 -1
- package/src/route-renderer/page-loading/lazy-trigger-planning.ts +1 -1
- package/src/route-renderer/page-loading/module-declaration-aggregation.ts +1 -1
- package/src/route-renderer/page-loading/module-declaration-scripts.ts +1 -1
- package/src/route-renderer/page-loading/page-dependency-bundling.ts +105 -66
- package/src/route-renderer/route-renderer.ts +28 -31
- package/src/router/README.md +16 -19
- package/src/router/server/route-registry.test.ts +176 -0
- package/src/router/server/route-registry.ts +382 -0
- package/src/services/README.md +1 -2
- package/src/services/assets/asset-processing-service/asset-dependency-keys.ts +1 -1
- package/src/services/assets/asset-processing-service/asset-processing.service.test.ts +1 -4
- package/src/services/assets/asset-processing-service/asset-processing.service.ts +1 -2
- package/src/services/assets/asset-processing-service/assets.types.ts +3 -0
- package/src/services/assets/asset-processing-service/grouped-content-bundles.ts +1 -1
- package/src/services/assets/asset-processing-service/index.ts +1 -0
- package/src/{route-renderer/orchestration/page-packaging.service.test.ts → services/assets/asset-processing-service/page-package.test.ts} +38 -14
- package/src/services/assets/asset-processing-service/page-package.ts +93 -0
- package/src/services/assets/asset-processing-service/processors/base/base-script-processor.ts +4 -5
- package/src/services/assets/asset-processing-service/processors/script/content-script.processor.test.ts +13 -10
- package/src/services/assets/asset-processing-service/processors/script/content-script.processor.ts +3 -0
- package/src/services/assets/asset-processing-service/processors/script/file-script.processor.ts +6 -0
- package/src/services/assets/asset-processing-service/processors/script/node-module-script.processor.ts +2 -0
- package/src/services/assets/asset-processing-service/processors/stylesheet/content-stylesheet.processor.ts +1 -0
- package/src/services/assets/asset-processing-service/processors/stylesheet/file-stylesheet.processor.ts +2 -0
- package/src/services/assets/asset-processing-service/ungrouped-dependency-processing.ts +1 -1
- package/src/services/html/html-transformer.service.test.ts +1 -4
- package/src/services/module-loading/app-server-module-transpiler.service.ts +1 -3
- package/src/services/module-loading/node-bootstrap-plugin.ts +17 -3
- package/src/services/module-loading/page-module-import.service.ts +0 -1
- package/src/services/module-loading/source-module-support.ts +1 -1
- package/src/static-site-generator/static-site-generator.test.ts +124 -32
- package/src/static-site-generator/static-site-generator.ts +168 -185
- package/src/types/internal-types.ts +13 -12
- package/src/types/public-types.ts +55 -39
- package/src/watchers/project-watcher.test-helpers.ts +4 -3
- package/src/route-renderer/orchestration/boundary-planning.service.ts +0 -146
- package/src/route-renderer/orchestration/page-packaging.service.ts +0 -85
- package/src/route-renderer/orchestration/render-execution.service.test.ts +0 -196
- package/src/route-renderer/orchestration/render-execution.service.ts +0 -182
- package/src/route-renderer/orchestration/route-shell-composer.service.ts +0 -162
- package/src/router/server/fs-router-scanner.test.ts +0 -83
- package/src/router/server/fs-router-scanner.ts +0 -224
- package/src/router/server/fs-router.test.ts +0 -214
- package/src/router/server/fs-router.ts +0 -122
- package/src/services/runtime-state/runtime-specifier-registry.service.ts +0 -96
|
@@ -2,10 +2,31 @@ import path from 'node:path';
|
|
|
2
2
|
import { appLogger } from '../global/app-logger.ts';
|
|
3
3
|
import type { EcoPagesAppConfig } from '../types/internal-types.ts';
|
|
4
4
|
import type { EcoPageComponent, StaticRoute } from '../types/public-types.ts';
|
|
5
|
-
import type {
|
|
6
|
-
|
|
5
|
+
import type {
|
|
6
|
+
ExplicitViewRenderer,
|
|
7
|
+
ExplicitViewRendererResolver,
|
|
8
|
+
PageRendererResolver,
|
|
9
|
+
StaticGenerationRendererResolver,
|
|
10
|
+
} from '../route-renderer/route-renderer.ts';
|
|
11
|
+
import type { StaticGenerationRoute } from '../router/server/route-registry.ts';
|
|
7
12
|
import { fileSystem } from '@ecopages/file-system';
|
|
8
13
|
import { PathUtils } from '../utils/path-utils.module.ts';
|
|
14
|
+
import { prepareExplicitStaticRender } from '../adapters/shared/explicit-static-render-preparation.ts';
|
|
15
|
+
|
|
16
|
+
type StaticGenerationRouteSource = {
|
|
17
|
+
listStaticGenerationRoutes(input: { runtimeOrigin: string }): Promise<readonly StaticGenerationRoute[]>;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
type StaticPageRouteRendererFactory = PageRendererResolver;
|
|
21
|
+
|
|
22
|
+
type ExplicitStaticRouteRendererFactory = ExplicitViewRendererResolver;
|
|
23
|
+
|
|
24
|
+
type StaticGenerationRendererFactory = StaticGenerationRendererResolver;
|
|
25
|
+
|
|
26
|
+
type ExplicitStaticRouteEntry = {
|
|
27
|
+
pathname: string;
|
|
28
|
+
params: Record<string, string | string[]>;
|
|
29
|
+
};
|
|
9
30
|
|
|
10
31
|
export const STATIC_SITE_GENERATOR_ERRORS = {
|
|
11
32
|
ROUTE_RENDERER_FACTORY_REQUIRED: 'RouteRendererFactory is required for render strategy',
|
|
@@ -56,9 +77,9 @@ export class StaticSiteGenerator {
|
|
|
56
77
|
*/
|
|
57
78
|
private async shouldSkipStaticPageFile(
|
|
58
79
|
filePath: string,
|
|
59
|
-
routeRendererFactory:
|
|
80
|
+
routeRendererFactory: StaticPageRouteRendererFactory,
|
|
60
81
|
): Promise<boolean> {
|
|
61
|
-
const module = (await routeRendererFactory.
|
|
82
|
+
const module = (await routeRendererFactory.getPageRenderer(filePath).loadPageModule(filePath, {
|
|
62
83
|
cacheScope: 'static-page-probe',
|
|
63
84
|
})) as {
|
|
64
85
|
default?: EcoPageComponent<any>;
|
|
@@ -131,27 +152,65 @@ export class StaticSiteGenerator {
|
|
|
131
152
|
return Array.from(directories);
|
|
132
153
|
}
|
|
133
154
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
private
|
|
142
|
-
const
|
|
143
|
-
const
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
155
|
+
private writeStaticOutput(routePath: string, contents: string | Buffer, directories: string[] = []): string {
|
|
156
|
+
const outputPath = this.getOutputPath(routePath, directories);
|
|
157
|
+
fileSystem.ensureDir(path.dirname(outputPath));
|
|
158
|
+
fileSystem.write(outputPath, contents);
|
|
159
|
+
return outputPath;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
private getStaticBuildStrategy(filePath: string): 'fetch' | 'render' {
|
|
163
|
+
const ext = PathUtils.getEcoTemplateExtension(filePath);
|
|
164
|
+
const integration = this.appConfig.integrations.find((plugin) => plugin.extensions.includes(ext));
|
|
165
|
+
return integration?.staticBuildStep || 'render';
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
private async createFilesystemStaticContents(
|
|
169
|
+
route: StaticGenerationRoute,
|
|
170
|
+
baseUrl: string,
|
|
171
|
+
routeRendererFactory?: StaticPageRouteRendererFactory,
|
|
172
|
+
): Promise<string | Buffer | null> {
|
|
173
|
+
const {
|
|
174
|
+
templateRoute: { filePath },
|
|
175
|
+
params,
|
|
176
|
+
} = route;
|
|
177
|
+
|
|
178
|
+
if (this.getStaticBuildStrategy(filePath) === 'fetch') {
|
|
179
|
+
const fetchUrl = this.resolveStaticFetchUrl(route.requestUrl, baseUrl);
|
|
180
|
+
const response = await fetch(fetchUrl);
|
|
181
|
+
|
|
182
|
+
if (!response.ok) {
|
|
183
|
+
appLogger.error(`Failed to fetch ${fetchUrl}. Status: ${response.status}`);
|
|
184
|
+
return null;
|
|
151
185
|
}
|
|
186
|
+
|
|
187
|
+
return response.text();
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
if (!routeRendererFactory) {
|
|
191
|
+
throw new Error(STATIC_SITE_GENERATOR_ERRORS.ROUTE_RENDERER_FACTORY_REQUIRED);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (await this.shouldSkipStaticPageFile(filePath, routeRendererFactory)) {
|
|
195
|
+
return null;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const renderer = routeRendererFactory.getPageRenderer(filePath);
|
|
199
|
+
const result = await renderer.execute({
|
|
200
|
+
file: filePath,
|
|
201
|
+
params: params as Record<string, string>,
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
const body = result.body;
|
|
205
|
+
if (typeof body === 'string' || Buffer.isBuffer(body)) {
|
|
206
|
+
return body;
|
|
152
207
|
}
|
|
153
208
|
|
|
154
|
-
|
|
209
|
+
if (body instanceof ReadableStream) {
|
|
210
|
+
return new Response(body).text();
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
throw new Error(STATIC_SITE_GENERATOR_ERRORS.unsupportedBodyType(typeof body));
|
|
155
214
|
}
|
|
156
215
|
|
|
157
216
|
/**
|
|
@@ -162,95 +221,31 @@ export class StaticSiteGenerator {
|
|
|
162
221
|
* issuing a request against the running server origin. Render-strategy routes
|
|
163
222
|
* go through the normal route renderer directly.
|
|
164
223
|
*/
|
|
165
|
-
async generateStaticPages(
|
|
166
|
-
|
|
224
|
+
async generateStaticPages(
|
|
225
|
+
router: StaticGenerationRouteSource,
|
|
226
|
+
baseUrl: string,
|
|
227
|
+
routeRendererFactory?: StaticPageRouteRendererFactory,
|
|
228
|
+
) {
|
|
229
|
+
const routes = await router.listStaticGenerationRoutes({ runtimeOrigin: baseUrl });
|
|
167
230
|
|
|
168
|
-
appLogger.debug(
|
|
169
|
-
|
|
170
|
-
|
|
231
|
+
appLogger.debug(
|
|
232
|
+
'Static Pages',
|
|
233
|
+
routes.map((route) => route.requestUrl),
|
|
234
|
+
);
|
|
171
235
|
|
|
172
|
-
|
|
173
|
-
fileSystem.ensureDir(path.join(this.getExportDir(), directory));
|
|
174
|
-
}
|
|
236
|
+
const directories = this.getDirectories(routes.map((route) => route.requestUrl));
|
|
175
237
|
|
|
176
238
|
for (const route of routes) {
|
|
177
239
|
try {
|
|
178
|
-
const
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
const integration = this.appConfig.integrations.find((plugin) => plugin.extensions.includes(ext));
|
|
182
|
-
const strategy = integration?.staticBuildStep || 'render';
|
|
183
|
-
|
|
184
|
-
let contents: string | Buffer;
|
|
185
|
-
|
|
186
|
-
if (strategy === 'fetch') {
|
|
187
|
-
const fetchUrl = this.resolveStaticFetchUrl(route, baseUrl);
|
|
188
|
-
const response = await fetch(fetchUrl);
|
|
189
|
-
|
|
190
|
-
if (!response.ok) {
|
|
191
|
-
appLogger.error(`Failed to fetch ${fetchUrl}. Status: ${response.status}`);
|
|
192
|
-
continue;
|
|
193
|
-
}
|
|
194
|
-
contents = await response.text();
|
|
195
|
-
} else {
|
|
196
|
-
if (!routeRendererFactory) {
|
|
197
|
-
throw new Error(STATIC_SITE_GENERATOR_ERRORS.ROUTE_RENDERER_FACTORY_REQUIRED);
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
if (await this.shouldSkipStaticPageFile(filePath, routeRendererFactory)) {
|
|
201
|
-
continue;
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
let pathname = routePathname;
|
|
205
|
-
const pathnameSegments = pathname.split('/').filter(Boolean);
|
|
206
|
-
|
|
207
|
-
if (pathname === '/') {
|
|
208
|
-
pathname = '/index.html';
|
|
209
|
-
} else if (pathnameSegments.join('/').includes('[')) {
|
|
210
|
-
pathname = `${route.replace(router.origin, '')}.html`;
|
|
211
|
-
} else if (pathnameSegments.length >= 1 && directories.includes(`/${pathnameSegments.join('/')}`)) {
|
|
212
|
-
pathname = `${pathname.endsWith('/') ? pathname : `${pathname}/`}index.html`;
|
|
213
|
-
} else {
|
|
214
|
-
pathname += '.html';
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
const renderer = routeRendererFactory.createRenderer(filePath);
|
|
218
|
-
const params = this.extractParams(routePathname, pathname.replace('.html', ''));
|
|
219
|
-
|
|
220
|
-
const result = await renderer.createRoute({
|
|
221
|
-
file: filePath,
|
|
222
|
-
params,
|
|
223
|
-
});
|
|
224
|
-
|
|
225
|
-
const body = result.body;
|
|
226
|
-
|
|
227
|
-
if (typeof body === 'string' || Buffer.isBuffer(body)) {
|
|
228
|
-
contents = body;
|
|
229
|
-
} else if (body instanceof ReadableStream) {
|
|
230
|
-
contents = await new Response(body).text();
|
|
231
|
-
} else {
|
|
232
|
-
throw new Error(STATIC_SITE_GENERATOR_ERRORS.unsupportedBodyType(typeof body));
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
let pathname = routePathname;
|
|
237
|
-
const pathnameSegments = pathname.split('/').filter(Boolean);
|
|
238
|
-
|
|
239
|
-
if (pathname === '/') {
|
|
240
|
-
pathname = '/index.html';
|
|
241
|
-
} else if (pathnameSegments.join('/').includes('[')) {
|
|
242
|
-
pathname = `${route.replace(router.origin, '')}.html`;
|
|
243
|
-
} else if (pathnameSegments.length >= 1 && directories.includes(`/${pathnameSegments.join('/')}`)) {
|
|
244
|
-
pathname = `${pathname.endsWith('/') ? pathname : `${pathname}/`}index.html`;
|
|
245
|
-
} else {
|
|
246
|
-
pathname += '.html';
|
|
240
|
+
const contents = await this.createFilesystemStaticContents(route, baseUrl, routeRendererFactory);
|
|
241
|
+
if (contents === null) {
|
|
242
|
+
continue;
|
|
247
243
|
}
|
|
248
244
|
|
|
249
|
-
|
|
250
|
-
fileSystem.write(outputPath, contents);
|
|
245
|
+
this.writeStaticOutput(route.pathname, contents, directories);
|
|
251
246
|
} catch (error) {
|
|
252
247
|
appLogger.error(
|
|
253
|
-
`Error generating static page for ${route}:`,
|
|
248
|
+
`Error generating static page for ${route.requestUrl}:`,
|
|
254
249
|
error instanceof Error ? error : String(error),
|
|
255
250
|
);
|
|
256
251
|
}
|
|
@@ -280,9 +275,9 @@ export class StaticSiteGenerator {
|
|
|
280
275
|
routeRendererFactory,
|
|
281
276
|
staticRoutes,
|
|
282
277
|
}: {
|
|
283
|
-
router:
|
|
278
|
+
router: StaticGenerationRouteSource;
|
|
284
279
|
baseUrl: string;
|
|
285
|
-
routeRendererFactory?:
|
|
280
|
+
routeRendererFactory?: StaticGenerationRendererFactory;
|
|
286
281
|
staticRoutes?: StaticRoute[];
|
|
287
282
|
}) {
|
|
288
283
|
this.generateRobotsTxt();
|
|
@@ -299,7 +294,7 @@ export class StaticSiteGenerator {
|
|
|
299
294
|
*/
|
|
300
295
|
private async generateExplicitStaticPages(
|
|
301
296
|
staticRoutes: StaticRoute[],
|
|
302
|
-
routeRendererFactory:
|
|
297
|
+
routeRendererFactory: ExplicitStaticRouteRendererFactory,
|
|
303
298
|
): Promise<void> {
|
|
304
299
|
appLogger.debug(
|
|
305
300
|
'Generating explicit static routes',
|
|
@@ -314,13 +309,7 @@ export class StaticSiteGenerator {
|
|
|
314
309
|
continue;
|
|
315
310
|
}
|
|
316
311
|
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
if (isDynamic) {
|
|
320
|
-
await this.generateDynamicStaticRoute(route.path, view, routeRendererFactory);
|
|
321
|
-
} else {
|
|
322
|
-
await this.generateSingleStaticRoute(route.path, view, routeRendererFactory);
|
|
323
|
-
}
|
|
312
|
+
await this.generateExplicitStaticRoute(route.path, view, routeRendererFactory);
|
|
324
313
|
} catch (error) {
|
|
325
314
|
appLogger.error(
|
|
326
315
|
`Error generating explicit static page for ${route.path}:`,
|
|
@@ -330,64 +319,81 @@ export class StaticSiteGenerator {
|
|
|
330
319
|
}
|
|
331
320
|
}
|
|
332
321
|
|
|
333
|
-
|
|
334
|
-
* Generate a single static page for a non-dynamic route.
|
|
335
|
-
*/
|
|
336
|
-
private async generateSingleStaticRoute(
|
|
322
|
+
private async generateExplicitStaticRoute(
|
|
337
323
|
routePath: string,
|
|
338
324
|
view: EcoPageComponent<any>,
|
|
339
|
-
routeRendererFactory:
|
|
325
|
+
routeRendererFactory: ExplicitStaticRouteRendererFactory,
|
|
340
326
|
): Promise<void> {
|
|
341
|
-
const
|
|
342
|
-
if (!integrationName) {
|
|
343
|
-
throw new Error(STATIC_SITE_GENERATOR_ERRORS.missingIntegration(routePath));
|
|
344
|
-
}
|
|
327
|
+
const { renderer, routeEntries } = await this.planExplicitStaticRoute(routePath, view, routeRendererFactory);
|
|
345
328
|
|
|
346
|
-
const
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
329
|
+
for (const { pathname, params } of routeEntries) {
|
|
330
|
+
const contents = await this.createExplicitStaticContents(
|
|
331
|
+
routePath,
|
|
332
|
+
view,
|
|
333
|
+
params,
|
|
334
|
+
routeRendererFactory,
|
|
335
|
+
renderer,
|
|
336
|
+
);
|
|
350
337
|
|
|
351
|
-
|
|
352
|
-
? (
|
|
353
|
-
await view.staticProps({
|
|
354
|
-
pathname: { params: {} },
|
|
355
|
-
appConfig: this.appConfig,
|
|
356
|
-
runtimeOrigin: this.appConfig.baseUrl,
|
|
357
|
-
})
|
|
358
|
-
).props
|
|
359
|
-
: {};
|
|
338
|
+
const outputPath = this.writeStaticOutput(pathname, contents);
|
|
360
339
|
|
|
361
|
-
|
|
362
|
-
|
|
340
|
+
appLogger.debug(`Generated static page: ${pathname} -> ${outputPath}`);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
363
343
|
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
344
|
+
private async planExplicitStaticRoute(
|
|
345
|
+
routePath: string,
|
|
346
|
+
view: EcoPageComponent<any>,
|
|
347
|
+
routeRendererFactory: ExplicitStaticRouteRendererFactory,
|
|
348
|
+
): Promise<{ renderer: ExplicitViewRenderer; routeEntries: ExplicitStaticRouteEntry[] }> {
|
|
349
|
+
const { renderer } = await prepareExplicitStaticRender({
|
|
350
|
+
routePath,
|
|
351
|
+
view,
|
|
352
|
+
params: {},
|
|
353
|
+
appConfig: this.appConfig,
|
|
354
|
+
runtimeOrigin: this.appConfig.baseUrl,
|
|
355
|
+
routeRendererFactory,
|
|
356
|
+
errors: STATIC_SITE_GENERATOR_ERRORS,
|
|
357
|
+
});
|
|
367
358
|
|
|
368
|
-
|
|
359
|
+
return {
|
|
360
|
+
renderer,
|
|
361
|
+
routeEntries: await this.listExplicitStaticRouteEntries(routePath, view),
|
|
362
|
+
};
|
|
369
363
|
}
|
|
370
364
|
|
|
371
|
-
|
|
372
|
-
* Generate static pages for a dynamic route using staticPaths.
|
|
373
|
-
*/
|
|
374
|
-
private async generateDynamicStaticRoute(
|
|
365
|
+
private async createExplicitStaticContents(
|
|
375
366
|
routePath: string,
|
|
376
367
|
view: EcoPageComponent<any>,
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
}
|
|
368
|
+
params: Record<string, string | string[]>,
|
|
369
|
+
routeRendererFactory: ExplicitStaticRouteRendererFactory,
|
|
370
|
+
renderer: ExplicitViewRenderer,
|
|
371
|
+
): Promise<string> {
|
|
372
|
+
const { props, view: renderableView } = await prepareExplicitStaticRender({
|
|
373
|
+
routePath,
|
|
374
|
+
view,
|
|
375
|
+
params,
|
|
376
|
+
appConfig: this.appConfig,
|
|
377
|
+
runtimeOrigin: this.appConfig.baseUrl,
|
|
378
|
+
routeRendererFactory,
|
|
379
|
+
errors: STATIC_SITE_GENERATOR_ERRORS,
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
const response = await renderer.renderToResponse(renderableView, props, {});
|
|
383
|
+
return response.text();
|
|
384
|
+
}
|
|
382
385
|
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
+
private async listExplicitStaticRouteEntries(
|
|
387
|
+
routePath: string,
|
|
388
|
+
view: EcoPageComponent<any>,
|
|
389
|
+
): Promise<ExplicitStaticRouteEntry[]> {
|
|
390
|
+
const isDynamic = routePath.includes(':') || routePath.includes('[');
|
|
391
|
+
if (!isDynamic) {
|
|
392
|
+
return [{ pathname: routePath, params: {} }];
|
|
386
393
|
}
|
|
387
394
|
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
throw new Error(STATIC_SITE_GENERATOR_ERRORS.noRendererForIntegration(integrationName));
|
|
395
|
+
if (!view.staticPaths) {
|
|
396
|
+
throw new Error(STATIC_SITE_GENERATOR_ERRORS.dynamicRouteRequiresStaticPaths(routePath));
|
|
391
397
|
}
|
|
392
398
|
|
|
393
399
|
const { paths } = await view.staticPaths({
|
|
@@ -395,28 +401,10 @@ export class StaticSiteGenerator {
|
|
|
395
401
|
runtimeOrigin: this.appConfig.baseUrl,
|
|
396
402
|
});
|
|
397
403
|
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
? (
|
|
403
|
-
await view.staticProps({
|
|
404
|
-
pathname: { params },
|
|
405
|
-
appConfig: this.appConfig,
|
|
406
|
-
runtimeOrigin: this.appConfig.baseUrl,
|
|
407
|
-
})
|
|
408
|
-
).props
|
|
409
|
-
: {};
|
|
410
|
-
|
|
411
|
-
const response = await renderer.renderToResponse(view, props, {});
|
|
412
|
-
const contents = await response.text();
|
|
413
|
-
|
|
414
|
-
const outputPath = this.getOutputPath(resolvedPath);
|
|
415
|
-
fileSystem.ensureDir(path.dirname(outputPath));
|
|
416
|
-
fileSystem.write(outputPath, contents);
|
|
417
|
-
|
|
418
|
-
appLogger.debug(`Generated static page: ${resolvedPath} -> ${outputPath}`);
|
|
419
|
-
}
|
|
404
|
+
return paths.map(({ params }) => ({
|
|
405
|
+
pathname: this.resolveRoutePath(routePath, params),
|
|
406
|
+
params,
|
|
407
|
+
}));
|
|
420
408
|
}
|
|
421
409
|
|
|
422
410
|
/**
|
|
@@ -439,11 +427,13 @@ export class StaticSiteGenerator {
|
|
|
439
427
|
/**
|
|
440
428
|
* Get the output file path for a given route.
|
|
441
429
|
*/
|
|
442
|
-
private getOutputPath(routePath: string): string {
|
|
430
|
+
private getOutputPath(routePath: string, directories: string[] = []): string {
|
|
443
431
|
let outputName: string;
|
|
444
432
|
|
|
445
433
|
if (routePath === '/') {
|
|
446
434
|
outputName = 'index.html';
|
|
435
|
+
} else if (directories.includes(routePath)) {
|
|
436
|
+
outputName = `${routePath}/index.html`;
|
|
447
437
|
} else if (routePath.endsWith('/')) {
|
|
448
438
|
outputName = `${routePath}index.html`;
|
|
449
439
|
} else {
|
|
@@ -453,10 +443,3 @@ export class StaticSiteGenerator {
|
|
|
453
443
|
return path.join(this.getExportDir(), outputName);
|
|
454
444
|
}
|
|
455
445
|
}
|
|
456
|
-
|
|
457
|
-
/**
|
|
458
|
-
* Splits a path into segments, filtering out empty strings.
|
|
459
|
-
*/
|
|
460
|
-
function templateSegmentsFromPath(path: string) {
|
|
461
|
-
return path.split('/').filter(Boolean);
|
|
462
|
-
}
|
|
@@ -1,17 +1,16 @@
|
|
|
1
1
|
import type { EcoBuildPlugin } from '../build/build-types.ts';
|
|
2
2
|
import type { AppBuildManifest } from '../build/build-manifest.ts';
|
|
3
3
|
import type { BuildAdapter, BuildExecutor, BuildOwnership } from '../build/build-adapter.ts';
|
|
4
|
-
import type {
|
|
4
|
+
import type { AnyIntegrationPlugin } from '../plugins/integration-plugin.ts';
|
|
5
5
|
import type { Processor } from '../plugins/processor.ts';
|
|
6
6
|
import type { EcoSourceTransform } from '../plugins/source-transform.ts';
|
|
7
7
|
import type { PageMetadataProps } from './public-types.ts';
|
|
8
|
-
import type {
|
|
8
|
+
import type { RouteRegistry } from '../router/server/route-registry.ts';
|
|
9
9
|
import type { CacheConfig } from '../services/cache/cache.types.ts';
|
|
10
10
|
import type { DevGraphService } from '../services/runtime-state/dev-graph.service.ts';
|
|
11
11
|
import type { AppModuleLoader } from '../services/module-loading/app-module-loader.service.ts';
|
|
12
12
|
import type { SourceModuleLoader } from '../services/module-loading/module-loading-types.ts';
|
|
13
13
|
import type { EntrypointDependencyGraph } from '../services/runtime-state/entrypoint-dependency-graph.service.ts';
|
|
14
|
-
import type { RuntimeSpecifierRegistry } from '../services/runtime-state/runtime-specifier-registry.service.ts';
|
|
15
14
|
import type { ServerInvalidationState } from '../services/runtime-state/server-invalidation-state.service.ts';
|
|
16
15
|
import type { ServerModuleTranspiler } from '../services/module-loading/server-module-transpiler.service.ts';
|
|
17
16
|
|
|
@@ -104,7 +103,7 @@ export type EcoPagesAppConfig = {
|
|
|
104
103
|
*/
|
|
105
104
|
defaultMetadata: PageMetadataProps;
|
|
106
105
|
/** Integrations plugins */
|
|
107
|
-
integrations:
|
|
106
|
+
integrations: AnyIntegrationPlugin[];
|
|
108
107
|
/** Integrations dependencies */
|
|
109
108
|
integrationsDependencies: IntegrationDependencyConfig[];
|
|
110
109
|
/** Derived Paths */
|
|
@@ -157,7 +156,6 @@ export type EcoPagesAppConfig = {
|
|
|
157
156
|
entrypointDependencyGraph?: EntrypointDependencyGraph;
|
|
158
157
|
hostModuleLoader?: SourceModuleLoader;
|
|
159
158
|
rendererModuleContext?: unknown;
|
|
160
|
-
runtimeSpecifierRegistry?: RuntimeSpecifierRegistry;
|
|
161
159
|
serverInvalidationState?: ServerInvalidationState;
|
|
162
160
|
serverModuleTranspiler?: ServerModuleTranspiler;
|
|
163
161
|
};
|
|
@@ -189,11 +187,14 @@ export type RouteKind = 'exact' | 'catch-all' | 'dynamic';
|
|
|
189
187
|
* Represents the result of a route match.
|
|
190
188
|
*/
|
|
191
189
|
export type MatchResult = {
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
190
|
+
requestedPathname: string;
|
|
191
|
+
templateRoute: {
|
|
192
|
+
filePath: string;
|
|
193
|
+
kind: RouteKind;
|
|
194
|
+
pathname: string;
|
|
195
|
+
};
|
|
196
|
+
query: Record<string, string>;
|
|
197
|
+
params: Record<string, string | string[]>;
|
|
197
198
|
};
|
|
198
199
|
|
|
199
200
|
/**
|
|
@@ -224,10 +225,10 @@ export type FileSystemServerOptions = {
|
|
|
224
225
|
export interface EcoPagesFileSystemServerAdapter<ServerInstanceOptions = unknown> {
|
|
225
226
|
startServer(serverOptions: ServerInstanceOptions):
|
|
226
227
|
| {
|
|
227
|
-
router:
|
|
228
|
+
router: RouteRegistry;
|
|
228
229
|
server: unknown;
|
|
229
230
|
}
|
|
230
|
-
| Promise<{ router:
|
|
231
|
+
| Promise<{ router: RouteRegistry; server: unknown }>;
|
|
231
232
|
}
|
|
232
233
|
|
|
233
234
|
export interface ProcessorPlugin {
|