evolit 0.1.0-alpha.2 → 0.1.0-alpha.21
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 +73 -3
- package/package.json +13 -6
- package/src/build.js +37 -9
- package/src/cli.js +16 -9
- package/src/client-assets.js +326 -70
- package/src/compiler.js +176 -9
- package/src/deployment-runtime.js +246 -43
- package/src/development-events.js +96 -0
- package/src/navigation-client.js +519 -0
- package/src/navigation-server.js +3 -0
- package/src/navigation-url.js +28 -0
- package/src/render.js +328 -19
- package/src/route-config.js +3 -1
- package/src/route-segments.js +272 -0
- package/src/scaffold.js +4 -0
- package/src/server.js +156 -13
- package/src/ssr-adapter.js +127 -12
- package/src/terminal.js +32 -0
- package/templates/default/yarnrc.yml.template +1 -0
package/src/render.js
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
2
|
import { randomUUID } from "node:crypto";
|
|
3
|
+
import { html } from "lit";
|
|
4
|
+
import { unsafeHTML } from "lit/directives/unsafe-html.js";
|
|
5
|
+
import { renderToString } from "@litsx/ssr";
|
|
3
6
|
import { discoverAppRouteHandlers, discoverAppRoutes, matchRoute } from "./app-discovery.js";
|
|
4
7
|
import { importCompiledModule } from "./compiler.js";
|
|
5
8
|
import { APP_DIRECTORY, MODULE_EXTENSIONS } from "./constants.js";
|
|
6
9
|
import { pathExists } from "./fs-utils.js";
|
|
7
10
|
import { mergeRouteConfig, normalizeRouteCachePolicy } from "./route-config.js";
|
|
11
|
+
import { createRouteSegmentPlan, wrapRouteSegment } from "./route-segments.js";
|
|
8
12
|
import {
|
|
9
13
|
createRequestContext,
|
|
10
14
|
applyRequestContextToResponse,
|
|
@@ -12,6 +16,7 @@ import {
|
|
|
12
16
|
isEvolitHttpSignal,
|
|
13
17
|
runWithRequestContext,
|
|
14
18
|
} from "./request-context.js";
|
|
19
|
+
import { createDevelopmentEventReporter } from "./development-events.js";
|
|
15
20
|
import {
|
|
16
21
|
LITSX_COMPONENT,
|
|
17
22
|
LITSX_SERVER_COMPONENT,
|
|
@@ -89,7 +94,12 @@ function createBoundaryError(error, { mode, route, reportRouteError }) {
|
|
|
89
94
|
if (typeof reportRouteError === "function") {
|
|
90
95
|
reportRouteError(report);
|
|
91
96
|
} else {
|
|
92
|
-
|
|
97
|
+
createDevelopmentEventReporter()({
|
|
98
|
+
type: "route-error",
|
|
99
|
+
digest,
|
|
100
|
+
pathname: route.pathname,
|
|
101
|
+
error: originalError,
|
|
102
|
+
});
|
|
93
103
|
}
|
|
94
104
|
|
|
95
105
|
const boundaryError = new Error("An unexpected server error occurred.");
|
|
@@ -133,6 +143,7 @@ function createRouteModuleOptions(projectRoot, mode, options = {}) {
|
|
|
133
143
|
mode,
|
|
134
144
|
ssr: true,
|
|
135
145
|
staticAssetPublicUrls: resolvedStaticAssetPublicUrls,
|
|
146
|
+
onDevelopmentEvent: options.onDevelopmentEvent,
|
|
136
147
|
};
|
|
137
148
|
}
|
|
138
149
|
|
|
@@ -153,26 +164,277 @@ function getNearestBoundary(route, boundaryName) {
|
|
|
153
164
|
return (route?.[boundaryName] ?? []).at(-1)?.module ?? null;
|
|
154
165
|
}
|
|
155
166
|
|
|
156
|
-
async function renderComponentTree(
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
167
|
+
async function renderComponentTree(
|
|
168
|
+
component,
|
|
169
|
+
layoutComponents,
|
|
170
|
+
requestContext,
|
|
171
|
+
request,
|
|
172
|
+
extraProps = {},
|
|
173
|
+
segments = [],
|
|
174
|
+
) {
|
|
175
|
+
let renderedTree = wrapRouteSegment(
|
|
176
|
+
segments.at(-1),
|
|
177
|
+
await component({
|
|
178
|
+
params: requestContext.params,
|
|
179
|
+
searchParams: requestContext.searchParams,
|
|
180
|
+
request,
|
|
181
|
+
...extraProps,
|
|
182
|
+
}),
|
|
183
|
+
);
|
|
163
184
|
|
|
164
185
|
for (let index = layoutComponents.length - 1; index >= 0; index -= 1) {
|
|
165
|
-
renderedTree = await layoutComponents[index]({
|
|
186
|
+
renderedTree = wrapRouteSegment(segments[index], await layoutComponents[index]({
|
|
166
187
|
params: requestContext.params,
|
|
167
188
|
searchParams: requestContext.searchParams,
|
|
168
189
|
request,
|
|
169
190
|
children: renderedTree,
|
|
170
|
-
});
|
|
191
|
+
}));
|
|
171
192
|
}
|
|
172
193
|
|
|
173
194
|
return renderedTree;
|
|
174
195
|
}
|
|
175
196
|
|
|
197
|
+
function createSegmentChildrenMarker(segment) {
|
|
198
|
+
return `<!--evolit:children:${segment.id}-->`;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function combineSegmentSsrArtifacts(results) {
|
|
202
|
+
const roots = [];
|
|
203
|
+
const payload = { roots: {}, instances: {} };
|
|
204
|
+
const clientImports = new Set();
|
|
205
|
+
const headTags = new Set();
|
|
206
|
+
|
|
207
|
+
for (const result of results) {
|
|
208
|
+
if (!result) continue;
|
|
209
|
+
for (const clientImport of result.clientImports ?? []) clientImports.add(clientImport);
|
|
210
|
+
for (const headTag of result.headTags ?? []) headTags.add(headTag);
|
|
211
|
+
if (!result.hydrationData) continue;
|
|
212
|
+
roots.push(...(result.hydrationData.roots ?? []));
|
|
213
|
+
Object.assign(payload.roots, result.hydrationData.payload?.roots ?? {});
|
|
214
|
+
Object.assign(payload.instances, result.hydrationData.payload?.instances ?? {});
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
return {
|
|
218
|
+
clientImports: [...clientImports],
|
|
219
|
+
headTags: [...headTags],
|
|
220
|
+
hydrationData: roots.length > 0
|
|
221
|
+
? { version: 1, roots, payload, clientImports: [...clientImports] }
|
|
222
|
+
: null,
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
function createTrackedRouteValues(values) {
|
|
227
|
+
const accessedKeys = new Set();
|
|
228
|
+
let accessedAll = false;
|
|
229
|
+
const value = new Proxy(values, {
|
|
230
|
+
get(target, property, receiver) {
|
|
231
|
+
if (typeof property === "string") accessedKeys.add(property);
|
|
232
|
+
return Reflect.get(target, property, receiver);
|
|
233
|
+
},
|
|
234
|
+
has(target, property) {
|
|
235
|
+
if (typeof property === "string") accessedKeys.add(property);
|
|
236
|
+
return Reflect.has(target, property);
|
|
237
|
+
},
|
|
238
|
+
ownKeys(target) {
|
|
239
|
+
accessedAll = true;
|
|
240
|
+
return Reflect.ownKeys(target);
|
|
241
|
+
},
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
return {
|
|
245
|
+
value,
|
|
246
|
+
profile() {
|
|
247
|
+
return accessedAll ? { all: true, keys: [] } : { all: false, keys: [...accessedKeys].sort() };
|
|
248
|
+
},
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
function createSegmentCacheKey(segment, idPrefix, profile, params, searchParams) {
|
|
253
|
+
const select = (values) => Object.fromEntries(
|
|
254
|
+
(profile.all ? Object.keys(values) : profile.keys)
|
|
255
|
+
.sort()
|
|
256
|
+
.map((key) => [key, values[key]]),
|
|
257
|
+
);
|
|
258
|
+
return JSON.stringify({
|
|
259
|
+
modulePath: segment.modulePath,
|
|
260
|
+
idPrefix,
|
|
261
|
+
params: select(params),
|
|
262
|
+
searchParams: select(searchParams),
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
function getSegmentCacheExpiry(cachePolicy) {
|
|
267
|
+
if (cachePolicy?.mode === "static") return Number.POSITIVE_INFINITY;
|
|
268
|
+
if (cachePolicy?.mode === "revalidate" && Number.isFinite(cachePolicy.ttlSeconds)) {
|
|
269
|
+
return Date.now() + (cachePolicy.ttlSeconds * 1_000);
|
|
270
|
+
}
|
|
271
|
+
return null;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
function createSegmentRenderCache(projectRoot, onDevelopmentEvent) {
|
|
275
|
+
const entries = new Map();
|
|
276
|
+
const profiles = new Map();
|
|
277
|
+
|
|
278
|
+
return {
|
|
279
|
+
get(segment, idPrefix, params, searchParams) {
|
|
280
|
+
const profile = profiles.get(segment.modulePath);
|
|
281
|
+
if (!profile) {
|
|
282
|
+
onDevelopmentEvent?.({ type: "segment-cache-miss", modulePath: segment.modulePath, reason: "cold" });
|
|
283
|
+
return null;
|
|
284
|
+
}
|
|
285
|
+
const key = createSegmentCacheKey(segment, idPrefix, profile, params, searchParams);
|
|
286
|
+
const entry = entries.get(key);
|
|
287
|
+
if (!entry || entry.expiresAt <= Date.now()) {
|
|
288
|
+
if (entry) entries.delete(key);
|
|
289
|
+
onDevelopmentEvent?.({
|
|
290
|
+
type: "segment-cache-miss",
|
|
291
|
+
modulePath: segment.modulePath,
|
|
292
|
+
reason: entry ? "expired" : "key-change",
|
|
293
|
+
});
|
|
294
|
+
return null;
|
|
295
|
+
}
|
|
296
|
+
onDevelopmentEvent?.({ type: "segment-cache-hit", modulePath: segment.modulePath });
|
|
297
|
+
return entry.result;
|
|
298
|
+
},
|
|
299
|
+
set(segment, idPrefix, params, searchParams, profile, result, cachePolicy) {
|
|
300
|
+
const expiresAt = getSegmentCacheExpiry(cachePolicy);
|
|
301
|
+
if (expiresAt == null) return;
|
|
302
|
+
profiles.set(segment.modulePath, profile);
|
|
303
|
+
const key = createSegmentCacheKey(segment, idPrefix, profile, params, searchParams);
|
|
304
|
+
entries.set(key, { result, expiresAt, modulePath: segment.modulePath });
|
|
305
|
+
if (entries.size > 256) entries.delete(entries.keys().next().value);
|
|
306
|
+
},
|
|
307
|
+
invalidate(changedPaths = null) {
|
|
308
|
+
if (!Array.isArray(changedPaths) || changedPaths.length === 0) {
|
|
309
|
+
const entryCount = entries.size;
|
|
310
|
+
entries.clear();
|
|
311
|
+
profiles.clear();
|
|
312
|
+
return entryCount;
|
|
313
|
+
}
|
|
314
|
+
const normalizedPaths = new Set(changedPaths.map((changedPath) => path.resolve(changedPath)));
|
|
315
|
+
const hasSharedChange = [...normalizedPaths].some((changedPath) =>
|
|
316
|
+
!path.relative(projectRoot, changedPath).startsWith(`app${path.sep}`),
|
|
317
|
+
);
|
|
318
|
+
if (hasSharedChange) {
|
|
319
|
+
const entryCount = entries.size;
|
|
320
|
+
entries.clear();
|
|
321
|
+
profiles.clear();
|
|
322
|
+
return entryCount;
|
|
323
|
+
}
|
|
324
|
+
let entryCount = 0;
|
|
325
|
+
for (const [key, entry] of entries) {
|
|
326
|
+
if (normalizedPaths.has(path.resolve(projectRoot, entry.modulePath))) {
|
|
327
|
+
entries.delete(key);
|
|
328
|
+
entryCount += 1;
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
for (const modulePath of profiles.keys()) {
|
|
332
|
+
if (normalizedPaths.has(path.resolve(projectRoot, modulePath))) profiles.delete(modulePath);
|
|
333
|
+
}
|
|
334
|
+
return entryCount;
|
|
335
|
+
},
|
|
336
|
+
clear() {
|
|
337
|
+
const entryCount = entries.size;
|
|
338
|
+
entries.clear();
|
|
339
|
+
profiles.clear();
|
|
340
|
+
return entryCount;
|
|
341
|
+
},
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* Renders route segments separately, then composes layout shells through an
|
|
347
|
+
* internal children marker. Authored layouts still receive a normal Lit
|
|
348
|
+
* renderable and can project children any number of times.
|
|
349
|
+
*/
|
|
350
|
+
async function renderSegmentedComponentTree(
|
|
351
|
+
component,
|
|
352
|
+
layoutComponents,
|
|
353
|
+
requestContext,
|
|
354
|
+
request,
|
|
355
|
+
extraProps = {},
|
|
356
|
+
segments = [],
|
|
357
|
+
options = {},
|
|
358
|
+
) {
|
|
359
|
+
if (segments.length !== layoutComponents.length + 1) return null;
|
|
360
|
+
const results = [];
|
|
361
|
+
|
|
362
|
+
async function renderSegmentAt(index, projectionPath) {
|
|
363
|
+
const segment = segments[index];
|
|
364
|
+
const idPrefix = `${segment.id}-p${projectionPath.join("-") || "0"}`;
|
|
365
|
+
|
|
366
|
+
if (index === layoutComponents.length) {
|
|
367
|
+
const pageValue = await component({
|
|
368
|
+
params: requestContext.params,
|
|
369
|
+
searchParams: requestContext.searchParams,
|
|
370
|
+
request,
|
|
371
|
+
...extraProps,
|
|
372
|
+
});
|
|
373
|
+
const pageResult = await renderToString(wrapRouteSegment(segment, pageValue), {
|
|
374
|
+
assetResolver: options.assetResolver,
|
|
375
|
+
context: { idPrefix },
|
|
376
|
+
});
|
|
377
|
+
results.push(pageResult);
|
|
378
|
+
return pageResult.html;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
const childrenMarker = createSegmentChildrenMarker(segment);
|
|
382
|
+
let layoutResult = options.segmentCache?.get(
|
|
383
|
+
segment,
|
|
384
|
+
idPrefix,
|
|
385
|
+
requestContext.params,
|
|
386
|
+
requestContext.searchParams,
|
|
387
|
+
) ?? null;
|
|
388
|
+
if (!layoutResult) {
|
|
389
|
+
const trackedParams = createTrackedRouteValues(requestContext.params);
|
|
390
|
+
const trackedSearchParams = createTrackedRouteValues(requestContext.searchParams);
|
|
391
|
+
const didUseDynamicRequestData = requestContext.didUseDynamicRequestData;
|
|
392
|
+
const layoutValue = await layoutComponents[index]({
|
|
393
|
+
params: trackedParams.value,
|
|
394
|
+
searchParams: trackedSearchParams.value,
|
|
395
|
+
request,
|
|
396
|
+
children: html`${unsafeHTML(childrenMarker)}`,
|
|
397
|
+
});
|
|
398
|
+
layoutResult = await renderToString(wrapRouteSegment(segment, layoutValue), {
|
|
399
|
+
assetResolver: options.assetResolver,
|
|
400
|
+
context: { idPrefix },
|
|
401
|
+
});
|
|
402
|
+
if (!didUseDynamicRequestData && !requestContext.didUseDynamicRequestData) {
|
|
403
|
+
options.segmentCache?.set(
|
|
404
|
+
segment,
|
|
405
|
+
idPrefix,
|
|
406
|
+
requestContext.params,
|
|
407
|
+
requestContext.searchParams,
|
|
408
|
+
{
|
|
409
|
+
all: trackedParams.profile().all || trackedSearchParams.profile().all,
|
|
410
|
+
keys: [...new Set([
|
|
411
|
+
...trackedParams.profile().keys,
|
|
412
|
+
...trackedSearchParams.profile().keys,
|
|
413
|
+
])].sort(),
|
|
414
|
+
},
|
|
415
|
+
layoutResult,
|
|
416
|
+
options.cachePolicy,
|
|
417
|
+
);
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
results.push(layoutResult);
|
|
421
|
+
const projectionCount = layoutResult.html.split(childrenMarker).length - 1;
|
|
422
|
+
if (projectionCount === 0) return null;
|
|
423
|
+
|
|
424
|
+
const childHtml = [];
|
|
425
|
+
for (let projection = 0; projection < projectionCount; projection += 1) {
|
|
426
|
+
const renderedChild = await renderSegmentAt(index + 1, [...projectionPath, projection]);
|
|
427
|
+
if (renderedChild == null) return null;
|
|
428
|
+
childHtml.push(renderedChild);
|
|
429
|
+
}
|
|
430
|
+
let projection = 0;
|
|
431
|
+
return layoutResult.html.replaceAll(childrenMarker, () => childHtml[projection++]);
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
const tree = await renderSegmentAt(0, []);
|
|
435
|
+
return tree == null ? null : { tree, ssrArtifacts: combineSegmentSsrArtifacts(results) };
|
|
436
|
+
}
|
|
437
|
+
|
|
176
438
|
export async function resolveStaticParamsForRoute(route, projectRoot, mode = "production", options = {}) {
|
|
177
439
|
const { pageModule, layoutModules } = await loadRouteModules(route, projectRoot, mode, options);
|
|
178
440
|
const generators = [...layoutModules, pageModule]
|
|
@@ -217,6 +479,8 @@ export async function resolveStaticParamsForRoute(route, projectRoot, mode = "pr
|
|
|
217
479
|
export async function createRouteResolver(projectRoot, mode = "development", resolverOptions = {}) {
|
|
218
480
|
const routes = await discoverAppRoutes(projectRoot);
|
|
219
481
|
const routeHandlers = await discoverAppRouteHandlers(projectRoot);
|
|
482
|
+
const segmentRenderCache = resolverOptions.segmentRenderCache
|
|
483
|
+
?? createSegmentRenderCache(projectRoot, resolverOptions.onDevelopmentEvent);
|
|
220
484
|
const rootLayoutPath = await findAppModule(projectRoot, "layout");
|
|
221
485
|
const rootNotFoundPath = await findAppModule(projectRoot, "not-found");
|
|
222
486
|
|
|
@@ -384,7 +648,10 @@ export async function createRouteResolver(projectRoot, mode = "development", res
|
|
|
384
648
|
return renderNotFound(request);
|
|
385
649
|
}
|
|
386
650
|
|
|
387
|
-
const
|
|
651
|
+
const preloadedModules = options.routePolicyResult?.route === match.route
|
|
652
|
+
? options.routePolicyResult.__evolitLoadedModules
|
|
653
|
+
: null;
|
|
654
|
+
const { pageModule, layoutModules } = preloadedModules ?? await loadRouteModules(
|
|
388
655
|
match.route,
|
|
389
656
|
projectRoot,
|
|
390
657
|
mode,
|
|
@@ -394,7 +661,7 @@ export async function createRouteResolver(projectRoot, mode = "development", res
|
|
|
394
661
|
const cachePolicy = normalizeRouteCachePolicy(routeConfig);
|
|
395
662
|
|
|
396
663
|
if (options.renderTree === false) {
|
|
397
|
-
|
|
664
|
+
const routePolicyResult = {
|
|
398
665
|
type: "route",
|
|
399
666
|
status: 200,
|
|
400
667
|
route: match.route,
|
|
@@ -404,6 +671,10 @@ export async function createRouteResolver(projectRoot, mode = "development", res
|
|
|
404
671
|
cachePolicy,
|
|
405
672
|
cacheKey: createRequestCacheKey(url),
|
|
406
673
|
};
|
|
674
|
+
Object.defineProperty(routePolicyResult, "__evolitLoadedModules", {
|
|
675
|
+
value: { pageModule, layoutModules },
|
|
676
|
+
});
|
|
677
|
+
return routePolicyResult;
|
|
407
678
|
}
|
|
408
679
|
|
|
409
680
|
const pageComponent = assertServerComponent(
|
|
@@ -425,8 +696,31 @@ export async function createRouteResolver(projectRoot, mode = "development", res
|
|
|
425
696
|
});
|
|
426
697
|
|
|
427
698
|
try {
|
|
428
|
-
const
|
|
429
|
-
|
|
699
|
+
const segments = createRouteSegmentPlan(match.route, projectRoot);
|
|
700
|
+
const segmentedRender = await runWithRequestContext(requestContext, () =>
|
|
701
|
+
renderSegmentedComponentTree(
|
|
702
|
+
pageComponent,
|
|
703
|
+
layoutComponents,
|
|
704
|
+
requestContext,
|
|
705
|
+
requestContext.routeRequest,
|
|
706
|
+
{},
|
|
707
|
+
segments,
|
|
708
|
+
{
|
|
709
|
+
assetResolver: resolverOptions.assetResolver,
|
|
710
|
+
segmentCache: segmentRenderCache,
|
|
711
|
+
cachePolicy,
|
|
712
|
+
},
|
|
713
|
+
),
|
|
714
|
+
);
|
|
715
|
+
const tree = segmentedRender?.tree ?? await runWithRequestContext(requestContext, () =>
|
|
716
|
+
renderComponentTree(
|
|
717
|
+
pageComponent,
|
|
718
|
+
layoutComponents,
|
|
719
|
+
requestContext,
|
|
720
|
+
requestContext.routeRequest,
|
|
721
|
+
{},
|
|
722
|
+
segments,
|
|
723
|
+
),
|
|
430
724
|
);
|
|
431
725
|
const contextResponse = getRequestContextResponse(requestContext);
|
|
432
726
|
|
|
@@ -439,6 +733,8 @@ export async function createRouteResolver(projectRoot, mode = "development", res
|
|
|
439
733
|
params: match.params,
|
|
440
734
|
searchParams: requestContext.searchParams,
|
|
441
735
|
routeConfig,
|
|
736
|
+
segments,
|
|
737
|
+
ssrArtifacts: segmentedRender?.ssrArtifacts ?? null,
|
|
442
738
|
cachePolicy: contextResponse.didUseDynamicRequestData ? { mode: "dynamic" } : cachePolicy,
|
|
443
739
|
cacheKey: createRequestCacheKey(url),
|
|
444
740
|
responseHeaders: contextResponse.headers,
|
|
@@ -480,10 +776,18 @@ export async function createRouteResolver(projectRoot, mode = "development", res
|
|
|
480
776
|
route: match.route,
|
|
481
777
|
reportRouteError: resolverOptions.reportRouteError,
|
|
482
778
|
});
|
|
779
|
+
const segments = createRouteSegmentPlan(match.route, projectRoot, {
|
|
780
|
+
boundaryModule: errorBoundaryPath,
|
|
781
|
+
});
|
|
483
782
|
const tree = await runWithRequestContext(requestContext, () =>
|
|
484
|
-
renderComponentTree(
|
|
485
|
-
|
|
486
|
-
|
|
783
|
+
renderComponentTree(
|
|
784
|
+
boundary.component,
|
|
785
|
+
layoutComponents,
|
|
786
|
+
requestContext,
|
|
787
|
+
requestContext.routeRequest,
|
|
788
|
+
{ error: boundaryError },
|
|
789
|
+
segments,
|
|
790
|
+
),
|
|
487
791
|
);
|
|
488
792
|
const contextResponse = getRequestContextResponse(requestContext);
|
|
489
793
|
return {
|
|
@@ -491,6 +795,7 @@ export async function createRouteResolver(projectRoot, mode = "development", res
|
|
|
491
795
|
status: 500,
|
|
492
796
|
tree,
|
|
493
797
|
boundaryModule: errorBoundaryPath,
|
|
798
|
+
segments,
|
|
494
799
|
metadata: mergeMetadata([...layoutModules, boundary.moduleRecord]),
|
|
495
800
|
route: match.route,
|
|
496
801
|
params: match.params,
|
|
@@ -509,8 +814,12 @@ export async function createRouteResolver(projectRoot, mode = "development", res
|
|
|
509
814
|
async resolveRoutePolicy(request) {
|
|
510
815
|
return resolveMatchedRequest(request, { renderTree: false });
|
|
511
816
|
},
|
|
512
|
-
async resolveRequest(request) {
|
|
513
|
-
return resolveMatchedRequest(request, { renderTree: true });
|
|
817
|
+
async resolveRequest(request, routePolicyResult = null) {
|
|
818
|
+
return resolveMatchedRequest(request, { renderTree: true, routePolicyResult });
|
|
819
|
+
},
|
|
820
|
+
invalidateSegmentCache(changedPaths = null) {
|
|
821
|
+
return segmentRenderCache.invalidate(changedPaths);
|
|
514
822
|
},
|
|
823
|
+
segmentRenderCache,
|
|
515
824
|
};
|
|
516
825
|
}
|
package/src/route-config.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
export const DEFAULT_REVALIDATE_SECONDS = 60;
|
|
2
|
+
|
|
1
3
|
function normalizeRevalidatePolicy(cacheConfig) {
|
|
2
4
|
if (!cacheConfig || typeof cacheConfig !== "object" || Array.isArray(cacheConfig)) {
|
|
3
5
|
throw new Error("Expected routeConfig.cache to be an object when using revalidate mode.");
|
|
@@ -15,7 +17,7 @@ function normalizeRevalidatePolicy(cacheConfig) {
|
|
|
15
17
|
}
|
|
16
18
|
|
|
17
19
|
export function normalizeRouteCachePolicy(routeConfig = {}) {
|
|
18
|
-
const cacheConfig = routeConfig?.cache ??
|
|
20
|
+
const cacheConfig = routeConfig?.cache ?? { revalidate: DEFAULT_REVALIDATE_SECONDS };
|
|
19
21
|
|
|
20
22
|
if (cacheConfig === "dynamic") {
|
|
21
23
|
return { mode: "dynamic" };
|