@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
|
@@ -1,22 +1,33 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
2
|
import { appLogger } from '../../global/app-logger.ts';
|
|
3
3
|
import type { EcoPagesAppConfig, MatchResult } from '../../types/internal-types.ts';
|
|
4
|
-
import type {
|
|
5
|
-
import type {
|
|
4
|
+
import type { PageRendererResolver } from '../../route-renderer/route-renderer.ts';
|
|
5
|
+
import type { RouteRegistry } from '../../router/server/route-registry.ts';
|
|
6
6
|
import type { PageCacheService } from '../../services/cache/page-cache-service.ts';
|
|
7
7
|
import type { CacheStrategy, RenderResult } from '../../services/cache/cache.types.ts';
|
|
8
8
|
import { PageRequestCacheCoordinator } from '../../services/cache/page-request-cache-coordinator.service.ts';
|
|
9
9
|
import { ServerUtils } from '../../utils/server-utils.module.ts';
|
|
10
|
-
import type {
|
|
10
|
+
import type { FileRouteMiddleware, RequestLocals } from '../../types/public-types.ts';
|
|
11
11
|
import { FileRouteMiddlewarePipeline } from './file-route-middleware-pipeline.ts';
|
|
12
12
|
import { LocalsAccessError } from '../../errors/locals-access-error.ts';
|
|
13
13
|
import { isDevelopmentRuntime } from '../../utils/runtime.ts';
|
|
14
14
|
import type { FileSystemServerResponseFactory } from './fs-server-response-factory.ts';
|
|
15
15
|
|
|
16
|
+
type FileRouteExecutionPlan = {
|
|
17
|
+
cacheKey: string;
|
|
18
|
+
request: Request;
|
|
19
|
+
pageFilePath: string;
|
|
20
|
+
pageMiddleware: FileRouteMiddleware[];
|
|
21
|
+
pageCacheStrategy: CacheStrategy;
|
|
22
|
+
localsStore: RequestLocals;
|
|
23
|
+
localsForRender: RequestLocals | undefined;
|
|
24
|
+
};
|
|
25
|
+
|
|
16
26
|
export interface FileSystemResponseMatcherOptions {
|
|
17
27
|
appConfig: EcoPagesAppConfig;
|
|
18
|
-
|
|
19
|
-
|
|
28
|
+
assetPrefix: string;
|
|
29
|
+
router: RouteRegistry;
|
|
30
|
+
routeRendererFactory: PageRendererResolver;
|
|
20
31
|
fileSystemResponseFactory: FileSystemServerResponseFactory;
|
|
21
32
|
/** Optional cache service. When null, caching is disabled. */
|
|
22
33
|
cacheService?: PageCacheService | null;
|
|
@@ -27,20 +38,22 @@ export interface FileSystemResponseMatcherOptions {
|
|
|
27
38
|
/**
|
|
28
39
|
* Matches file-system routes to rendered HTML responses.
|
|
29
40
|
*
|
|
30
|
-
* render pipeline
|
|
41
|
+
* This render pipeline coordinates page module inspection, request-local policy,
|
|
31
42
|
* renderer invocation, middleware execution, cache integration, and fallback
|
|
32
43
|
* error translation.
|
|
33
44
|
*/
|
|
34
45
|
export class FileSystemResponseMatcher {
|
|
35
46
|
private appConfig: EcoPagesAppConfig;
|
|
36
|
-
private
|
|
37
|
-
private
|
|
47
|
+
private assetPrefix: string;
|
|
48
|
+
private router: RouteRegistry;
|
|
49
|
+
private routeRendererFactory: PageRendererResolver;
|
|
38
50
|
private fileSystemResponseFactory: FileSystemServerResponseFactory;
|
|
39
51
|
private pageRequestCacheCoordinator: PageRequestCacheCoordinator;
|
|
40
52
|
private fileRouteMiddlewarePipeline: FileRouteMiddlewarePipeline;
|
|
41
53
|
|
|
42
54
|
constructor({
|
|
43
55
|
appConfig,
|
|
56
|
+
assetPrefix,
|
|
44
57
|
router,
|
|
45
58
|
routeRendererFactory,
|
|
46
59
|
fileSystemResponseFactory,
|
|
@@ -48,6 +61,7 @@ export class FileSystemResponseMatcher {
|
|
|
48
61
|
defaultCacheStrategy = 'static',
|
|
49
62
|
}: FileSystemResponseMatcherOptions) {
|
|
50
63
|
this.appConfig = appConfig;
|
|
64
|
+
this.assetPrefix = assetPrefix;
|
|
51
65
|
this.router = router;
|
|
52
66
|
this.routeRendererFactory = routeRendererFactory;
|
|
53
67
|
this.fileSystemResponseFactory = fileSystemResponseFactory;
|
|
@@ -65,14 +79,15 @@ export class FileSystemResponseMatcher {
|
|
|
65
79
|
const isStaticFileRequest = ServerUtils.hasKnownExtension(requestUrl);
|
|
66
80
|
|
|
67
81
|
if (!isStaticFileRequest) {
|
|
68
|
-
return this.
|
|
82
|
+
return this.renderCustomNotFoundResponse();
|
|
69
83
|
}
|
|
70
84
|
|
|
71
85
|
const relativeUrl = requestUrl.startsWith('/') ? requestUrl.slice(1) : requestUrl;
|
|
72
|
-
const filePath = path.join(this.
|
|
86
|
+
const filePath = path.join(this.assetPrefix, relativeUrl);
|
|
73
87
|
const contentType = ServerUtils.getContentType(filePath);
|
|
74
88
|
|
|
75
|
-
|
|
89
|
+
const response = await this.fileSystemResponseFactory.createFileResponse(filePath, contentType);
|
|
90
|
+
return response ?? this.renderCustomNotFoundResponse();
|
|
76
91
|
}
|
|
77
92
|
|
|
78
93
|
/**
|
|
@@ -87,44 +102,28 @@ export class FileSystemResponseMatcher {
|
|
|
87
102
|
* @returns Final response for the matched route.
|
|
88
103
|
*/
|
|
89
104
|
async handleMatch(match: MatchResult, request?: Request): Promise<Response> {
|
|
90
|
-
const cacheKey = this.pageRequestCacheCoordinator.buildCacheKey(match);
|
|
91
|
-
|
|
92
105
|
try {
|
|
93
|
-
const
|
|
94
|
-
request ??
|
|
95
|
-
new Request(new URL(cacheKey, this.router.origin).toString(), {
|
|
96
|
-
method: 'GET',
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
const localsStore: RequestLocals = {};
|
|
100
|
-
const pageModule = await this.importPageModule(match.filePath);
|
|
101
|
-
const Page = (pageModule as any)?.default;
|
|
102
|
-
const pageMiddleware = (Page?.middleware ?? []) as Middleware[];
|
|
103
|
-
const pageCacheStrategy =
|
|
104
|
-
(Page?.cache as CacheStrategy | undefined) ??
|
|
105
|
-
this.pageRequestCacheCoordinator.getDefaultCacheStrategy();
|
|
106
|
-
const localsForRender: RequestLocals | undefined =
|
|
107
|
-
pageCacheStrategy === 'dynamic' ? localsStore : undefined;
|
|
106
|
+
const executionPlan = await this.createExecutionPlan(match, request);
|
|
108
107
|
|
|
109
108
|
this.fileRouteMiddlewarePipeline.assertValidConfiguration({
|
|
110
|
-
middleware: pageMiddleware,
|
|
111
|
-
pageCacheStrategy,
|
|
112
|
-
filePath:
|
|
109
|
+
middleware: executionPlan.pageMiddleware,
|
|
110
|
+
pageCacheStrategy: executionPlan.pageCacheStrategy,
|
|
111
|
+
filePath: executionPlan.pageFilePath,
|
|
113
112
|
});
|
|
114
113
|
|
|
115
|
-
const routeRenderer = this.routeRendererFactory.
|
|
114
|
+
const routeRenderer = this.routeRendererFactory.getPageRenderer(executionPlan.pageFilePath);
|
|
116
115
|
const middlewareContext = this.fileRouteMiddlewarePipeline.createContext({
|
|
117
|
-
request:
|
|
116
|
+
request: executionPlan.request,
|
|
118
117
|
params: match.params as Record<string, string>,
|
|
119
|
-
locals: localsStore,
|
|
118
|
+
locals: executionPlan.localsStore,
|
|
120
119
|
});
|
|
121
120
|
|
|
122
121
|
const renderFn = async (): Promise<RenderResult> => {
|
|
123
|
-
const result = await routeRenderer.
|
|
124
|
-
file:
|
|
122
|
+
const result = await routeRenderer.execute({
|
|
123
|
+
file: executionPlan.pageFilePath,
|
|
125
124
|
params: match.params,
|
|
126
125
|
query: match.query,
|
|
127
|
-
locals: localsForRender,
|
|
126
|
+
locals: executionPlan.localsForRender,
|
|
128
127
|
});
|
|
129
128
|
const html = await this.pageRequestCacheCoordinator.bodyToString(result.body);
|
|
130
129
|
const strategy = result.cacheStrategy ?? this.pageRequestCacheCoordinator.getDefaultCacheStrategy();
|
|
@@ -132,14 +131,14 @@ export class FileSystemResponseMatcher {
|
|
|
132
131
|
};
|
|
133
132
|
const renderResponse = async (): Promise<Response> => {
|
|
134
133
|
return this.pageRequestCacheCoordinator.render({
|
|
135
|
-
cacheKey,
|
|
136
|
-
pageCacheStrategy,
|
|
134
|
+
cacheKey: executionPlan.cacheKey,
|
|
135
|
+
pageCacheStrategy: executionPlan.pageCacheStrategy,
|
|
137
136
|
renderFn,
|
|
138
137
|
});
|
|
139
138
|
};
|
|
140
139
|
|
|
141
140
|
return await this.fileRouteMiddlewarePipeline.run({
|
|
142
|
-
middleware: pageMiddleware,
|
|
141
|
+
middleware: executionPlan.pageMiddleware,
|
|
143
142
|
context: middlewareContext,
|
|
144
143
|
renderResponse,
|
|
145
144
|
});
|
|
@@ -155,13 +154,65 @@ export class FileSystemResponseMatcher {
|
|
|
155
154
|
}
|
|
156
155
|
if (error instanceof Error) {
|
|
157
156
|
if (isDevelopmentRuntime() || appLogger.isDebugEnabled()) {
|
|
158
|
-
appLogger.error(`[FileSystemResponseMatcher] ${error.message} at ${match.
|
|
157
|
+
appLogger.error(`[FileSystemResponseMatcher] ${error.message} at ${match.requestedPathname}`);
|
|
159
158
|
}
|
|
160
159
|
}
|
|
161
|
-
return this.
|
|
160
|
+
return this.renderCustomNotFoundResponse();
|
|
162
161
|
}
|
|
163
162
|
}
|
|
164
163
|
|
|
164
|
+
/**
|
|
165
|
+
* Renders the app-owned custom 404 page, falling back to the default text 404
|
|
166
|
+
* when the page template cannot be resolved.
|
|
167
|
+
*/
|
|
168
|
+
private async renderCustomNotFoundResponse(): Promise<Response> {
|
|
169
|
+
const error404TemplatePath = this.appConfig.absolutePaths.error404TemplatePath;
|
|
170
|
+
|
|
171
|
+
try {
|
|
172
|
+
const routeRenderer = this.routeRendererFactory.getPageRenderer(error404TemplatePath);
|
|
173
|
+
const result = await routeRenderer.execute({
|
|
174
|
+
file: error404TemplatePath,
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
return this.fileSystemResponseFactory.createHtmlNotFoundResponse(result.body);
|
|
178
|
+
} catch {
|
|
179
|
+
appLogger.debug(
|
|
180
|
+
'Custom 404 template not found, falling back to default 404 response',
|
|
181
|
+
error404TemplatePath,
|
|
182
|
+
);
|
|
183
|
+
return this.fileSystemResponseFactory.createDefaultNotFoundResponse();
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
private async createExecutionPlan(match: MatchResult, request?: Request): Promise<FileRouteExecutionPlan> {
|
|
188
|
+
const cacheKey = this.pageRequestCacheCoordinator.buildCacheKey({
|
|
189
|
+
pathname: match.requestedPathname,
|
|
190
|
+
query: match.query,
|
|
191
|
+
});
|
|
192
|
+
const resolvedRequest =
|
|
193
|
+
request ??
|
|
194
|
+
new Request(new URL(cacheKey, this.router.origin).toString(), {
|
|
195
|
+
method: 'GET',
|
|
196
|
+
});
|
|
197
|
+
const localsStore: RequestLocals = {};
|
|
198
|
+
const pageFilePath = match.templateRoute.filePath;
|
|
199
|
+
const pageModule = await this.importPageModule(pageFilePath);
|
|
200
|
+
const Page = (pageModule as any)?.default;
|
|
201
|
+
const pageMiddleware = (Page?.middleware ?? []) as FileRouteMiddleware[];
|
|
202
|
+
const pageCacheStrategy =
|
|
203
|
+
(Page?.cache as CacheStrategy | undefined) ?? this.pageRequestCacheCoordinator.getDefaultCacheStrategy();
|
|
204
|
+
|
|
205
|
+
return {
|
|
206
|
+
cacheKey,
|
|
207
|
+
request: resolvedRequest,
|
|
208
|
+
pageFilePath,
|
|
209
|
+
pageMiddleware,
|
|
210
|
+
pageCacheStrategy,
|
|
211
|
+
localsStore,
|
|
212
|
+
localsForRender: pageCacheStrategy === 'dynamic' ? localsStore : undefined,
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
|
|
165
216
|
/**
|
|
166
217
|
* Loads the matched page module for request-time inspection.
|
|
167
218
|
*
|
|
@@ -174,7 +225,7 @@ export class FileSystemResponseMatcher {
|
|
|
174
225
|
* @returns Imported page module.
|
|
175
226
|
*/
|
|
176
227
|
private async importPageModule(filePath: string): Promise<unknown> {
|
|
177
|
-
const routeRenderer = this.routeRendererFactory.
|
|
228
|
+
const routeRenderer = this.routeRendererFactory.getPageRenderer(filePath);
|
|
178
229
|
return routeRenderer.loadPageModule(filePath, {
|
|
179
230
|
cacheScope: 'request-metadata',
|
|
180
231
|
});
|
|
@@ -12,9 +12,7 @@ type SharedHmrManager = {
|
|
|
12
12
|
handleFileChange(filePath: string, options?: { broadcast?: boolean }): Promise<void>;
|
|
13
13
|
registerEntrypoint(entrypointPath: string): Promise<string>;
|
|
14
14
|
registerScriptEntrypoint(entrypointPath: string): Promise<string>;
|
|
15
|
-
registerSpecifierMap(map: Record<string, string>): void;
|
|
16
15
|
getWatchedFiles(): Map<string, string>;
|
|
17
|
-
getSpecifierMap(): Map<string, string>;
|
|
18
16
|
stop(): void;
|
|
19
17
|
appConfig: Awaited<ReturnType<ConfigBuilder['build']>>;
|
|
20
18
|
};
|
|
@@ -221,12 +219,10 @@ describe.each(runtimes)('shared HMR manager contract: $name', ({ create }) => {
|
|
|
221
219
|
fs.writeFileSync(outputPath, 'export default 1;', 'utf8');
|
|
222
220
|
});
|
|
223
221
|
|
|
224
|
-
manager.registerSpecifierMap({ react: '/assets/vendors/react.js' });
|
|
225
222
|
await manager.registerEntrypoint(entrypointPath);
|
|
226
223
|
|
|
227
224
|
manager.stop();
|
|
228
225
|
|
|
229
226
|
assert.equal(manager.getWatchedFiles().size, 0);
|
|
230
|
-
assert.equal(manager.getSpecifierMap().size, 0);
|
|
231
227
|
});
|
|
232
228
|
});
|
|
@@ -3,11 +3,11 @@ import type {
|
|
|
3
3
|
IntegrationRenderer,
|
|
4
4
|
RenderToResponseContext,
|
|
5
5
|
} from '../../route-renderer/orchestration/integration-renderer.ts';
|
|
6
|
-
import type {
|
|
6
|
+
import type { AnyIntegrationPlugin } from '../../plugins/integration-plugin.ts';
|
|
7
7
|
import { invariant } from '../../utils/invariant.ts';
|
|
8
8
|
|
|
9
9
|
export interface CreateRenderContextOptions {
|
|
10
|
-
integrations:
|
|
10
|
+
integrations: AnyIntegrationPlugin[];
|
|
11
11
|
rendererModules?: unknown;
|
|
12
12
|
}
|
|
13
13
|
|
|
@@ -54,7 +54,7 @@ export function createRenderContext(options: CreateRenderContextOptions): Render
|
|
|
54
54
|
|
|
55
55
|
return integration.initializeRenderer({
|
|
56
56
|
rendererModules: options.rendererModules,
|
|
57
|
-
});
|
|
57
|
+
}) as IntegrationRenderer;
|
|
58
58
|
};
|
|
59
59
|
|
|
60
60
|
const renderContext: RenderContext = {
|
|
@@ -5,6 +5,7 @@ import path from 'node:path';
|
|
|
5
5
|
import { afterEach, test } from 'vitest';
|
|
6
6
|
import { ConfigBuilder } from '../../config/config-builder.ts';
|
|
7
7
|
import type { ServerAdapterResult } from '../abstract/server-adapter.ts';
|
|
8
|
+
import type { ApiHandler } from '../../types/public-types.ts';
|
|
8
9
|
import { SharedServerAdapter } from './server-adapter.ts';
|
|
9
10
|
|
|
10
11
|
const tempRoots: string[] = [];
|
|
@@ -43,6 +44,18 @@ class TestSharedServerAdapter extends SharedServerAdapter<any, ServerAdapterResu
|
|
|
43
44
|
});
|
|
44
45
|
}
|
|
45
46
|
|
|
47
|
+
public async handleSharedRequestForTest(request: Request, apiHandlers: ApiHandler[]): Promise<Response> {
|
|
48
|
+
return await this.handleSharedRequest(request, {
|
|
49
|
+
apiHandlers,
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
public setRouteHandlerForTest(handleResponse: (request: Request) => Promise<Response>): void {
|
|
54
|
+
this.routeHandler = {
|
|
55
|
+
handleResponse,
|
|
56
|
+
} as any;
|
|
57
|
+
}
|
|
58
|
+
|
|
46
59
|
constructor(
|
|
47
60
|
private readonly hmrDir: string,
|
|
48
61
|
rootDir: string,
|
|
@@ -75,3 +88,43 @@ test('SharedServerAdapter serves /assets/_hmr files from the HMR manager directo
|
|
|
75
88
|
false,
|
|
76
89
|
);
|
|
77
90
|
});
|
|
91
|
+
|
|
92
|
+
test('SharedServerAdapter dispatches matching API handlers before filesystem routes', async () => {
|
|
93
|
+
const rootDir = createTempRoot('ecopages-shared-server-api-dispatch');
|
|
94
|
+
const adapter = new TestSharedServerAdapter('', rootDir);
|
|
95
|
+
adapter.setRouteHandlerForTest(async () => new Response('filesystem'));
|
|
96
|
+
|
|
97
|
+
const response = await adapter.handleSharedRequestForTest(
|
|
98
|
+
new Request('http://localhost/api/posts/123', { method: 'GET' }),
|
|
99
|
+
[
|
|
100
|
+
{
|
|
101
|
+
path: '/api/posts/[id]',
|
|
102
|
+
method: 'GET',
|
|
103
|
+
handler: ({ params }) => new Response(`api:${params.id}`),
|
|
104
|
+
},
|
|
105
|
+
],
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
assert.equal(response.status, 200);
|
|
109
|
+
assert.equal(await response.text(), 'api:123');
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
test('SharedServerAdapter falls back to the route handler when no API handler matches', async () => {
|
|
113
|
+
const rootDir = createTempRoot('ecopages-shared-server-route-fallback');
|
|
114
|
+
const adapter = new TestSharedServerAdapter('', rootDir);
|
|
115
|
+
adapter.setRouteHandlerForTest(async () => new Response('filesystem'));
|
|
116
|
+
|
|
117
|
+
const response = await adapter.handleSharedRequestForTest(
|
|
118
|
+
new Request('http://localhost/blog/post-1', { method: 'GET' }),
|
|
119
|
+
[
|
|
120
|
+
{
|
|
121
|
+
path: '/api/posts/[id]',
|
|
122
|
+
method: 'GET',
|
|
123
|
+
handler: () => new Response('api'),
|
|
124
|
+
},
|
|
125
|
+
],
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
assert.equal(response.status, 200);
|
|
129
|
+
assert.equal(await response.text(), 'filesystem');
|
|
130
|
+
});
|