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
|
@@ -1,22 +1,29 @@
|
|
|
1
1
|
import fs from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
compileModuleGraph,
|
|
5
|
+
invalidateDevelopmentCompilationCache,
|
|
6
|
+
} from "./compiler.js";
|
|
4
7
|
import {
|
|
5
8
|
collectTransitiveAssetPreloads,
|
|
6
9
|
collectTransitiveStyleUrls,
|
|
7
10
|
createAssetResolver,
|
|
8
11
|
createHydrationBootstrap,
|
|
9
12
|
createStaticAssetPublicUrlMap,
|
|
13
|
+
buildSharedVendorRuntime,
|
|
10
14
|
emitBundledClientAssetsWithState,
|
|
11
15
|
getAssetByPublicUrl,
|
|
12
16
|
getSharedOutputRoot,
|
|
13
17
|
normalizeHydrationDataForClient,
|
|
14
18
|
normalizeClientAssetManifest,
|
|
19
|
+
resetDevelopmentAssetCaches,
|
|
20
|
+
resolveRouteClientImports,
|
|
15
21
|
resolveSharedVendorModuleUrl,
|
|
16
22
|
resolveBrowserPackageAssetFilePath,
|
|
17
23
|
rewriteHydrationDataScript,
|
|
18
24
|
} from "./client-assets.js";
|
|
19
25
|
import { loadEvolitConfig } from "./config.js";
|
|
26
|
+
import { DEV_DIRECTORY, INTERNAL_DIRECTORY } from "./constants.js";
|
|
20
27
|
import { createRouteResolver } from "./render.js";
|
|
21
28
|
import {
|
|
22
29
|
createCachedRouteResponse,
|
|
@@ -25,6 +32,7 @@ import {
|
|
|
25
32
|
resolveResponseCacheRuntime,
|
|
26
33
|
} from "./response-cache.js";
|
|
27
34
|
import { createSsrAdapter, renderRouteTreeWithAdapter } from "./ssr-adapter.js";
|
|
35
|
+
import { createNavigationResponseFromDocument } from "./route-segments.js";
|
|
28
36
|
const CONTENT_TYPE_BY_EXTENSION = new Map([
|
|
29
37
|
[".css", "text/css; charset=utf-8"],
|
|
30
38
|
[".svg", "image/svg+xml"],
|
|
@@ -208,7 +216,14 @@ export function createResponseCacheController({ responseCacheRuntime }) {
|
|
|
208
216
|
};
|
|
209
217
|
}
|
|
210
218
|
|
|
211
|
-
export async function createRequestRenderer({
|
|
219
|
+
export async function createRequestRenderer({
|
|
220
|
+
projectRoot,
|
|
221
|
+
mode,
|
|
222
|
+
assetManifest,
|
|
223
|
+
routeResolver,
|
|
224
|
+
responseCacheRuntime,
|
|
225
|
+
onDevelopmentEvent,
|
|
226
|
+
}) {
|
|
212
227
|
let currentAssetManifest = normalizeClientAssetManifest(assetManifest);
|
|
213
228
|
let currentAssetResolver = createAssetResolver(projectRoot, {
|
|
214
229
|
assetManifest: currentAssetManifest,
|
|
@@ -218,25 +233,70 @@ export async function createRequestRenderer({ projectRoot, mode, assetManifest,
|
|
|
218
233
|
mode,
|
|
219
234
|
"@litsx/ssr/hydration",
|
|
220
235
|
);
|
|
236
|
+
let currentNavigationModuleUrl = await resolveSharedVendorModuleUrl(
|
|
237
|
+
projectRoot,
|
|
238
|
+
mode,
|
|
239
|
+
"evolit/navigation",
|
|
240
|
+
);
|
|
221
241
|
const devBundledEntries = new Set();
|
|
242
|
+
const devPreparedClientModules = new Set();
|
|
243
|
+
const devClientModuleDependencies = new Map();
|
|
222
244
|
let devRollupCache = null;
|
|
245
|
+
let devClientAssetWork = Promise.resolve();
|
|
246
|
+
const developmentMetrics = {
|
|
247
|
+
clientArtifactBuilds: 0,
|
|
248
|
+
clientArtifactCacheHits: 0,
|
|
249
|
+
invalidations: 0,
|
|
250
|
+
selectivelyInvalidatedClientEntries: 0,
|
|
251
|
+
};
|
|
223
252
|
const usesFrameworkRouteResolver = !routeResolver;
|
|
253
|
+
let sharedSegmentRenderCache = null;
|
|
254
|
+
|
|
255
|
+
function enqueueDevClientAssetWork(work) {
|
|
256
|
+
const nextWork = devClientAssetWork.then(work, work);
|
|
257
|
+
devClientAssetWork = nextWork.catch(() => {});
|
|
258
|
+
return nextWork;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
function reportDevelopmentEvent(event) {
|
|
262
|
+
if (mode === "development" && typeof onDevelopmentEvent === "function") {
|
|
263
|
+
onDevelopmentEvent(event);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
function getDevClientModuleKey(clientModule) {
|
|
268
|
+
return path.resolve(projectRoot, clientModule);
|
|
269
|
+
}
|
|
224
270
|
async function createFrameworkRouteResolver() {
|
|
225
|
-
|
|
271
|
+
const resolver = await createRouteResolver(projectRoot, mode, {
|
|
272
|
+
onDevelopmentEvent,
|
|
273
|
+
segmentRenderCache: sharedSegmentRenderCache,
|
|
226
274
|
getStaticAssetPublicUrls() {
|
|
227
275
|
return createStaticAssetPublicUrlMap(currentAssetManifest);
|
|
228
276
|
},
|
|
277
|
+
assetResolver(moduleId) {
|
|
278
|
+
return currentAssetResolver(moduleId);
|
|
279
|
+
},
|
|
229
280
|
});
|
|
281
|
+
sharedSegmentRenderCache ??= resolver.segmentRenderCache;
|
|
282
|
+
return resolver;
|
|
230
283
|
}
|
|
231
284
|
let effectiveRouteResolver = routeResolver ?? await createFrameworkRouteResolver();
|
|
232
285
|
const ssrAdapter = createSsrAdapter({
|
|
233
286
|
assetResolver(moduleId) {
|
|
234
287
|
return currentAssetResolver(moduleId);
|
|
235
288
|
},
|
|
236
|
-
async resolveAdditionalHead({ result }) {
|
|
237
|
-
const clientImports =
|
|
238
|
-
|
|
239
|
-
|
|
289
|
+
async resolveAdditionalHead({ routeResult, result }) {
|
|
290
|
+
const clientImports = [
|
|
291
|
+
...(Array.isArray(result.clientImports) ? result.clientImports : []),
|
|
292
|
+
...resolveRouteClientImports(routeResult, projectRoot, currentAssetManifest),
|
|
293
|
+
];
|
|
294
|
+
const hydratedClientImports = Array.isArray(result.clientImports) ? result.clientImports : [];
|
|
295
|
+
const urls = currentAssetManifest && hydratedClientImports.length > 0
|
|
296
|
+
? [...new Set([
|
|
297
|
+
...hydratedClientImports,
|
|
298
|
+
...collectTransitiveAssetPreloads(hydratedClientImports, currentAssetManifest),
|
|
299
|
+
])]
|
|
240
300
|
: [];
|
|
241
301
|
const styleUrls = currentAssetManifest
|
|
242
302
|
? collectTransitiveStyleUrls(clientImports, currentAssetManifest)
|
|
@@ -246,23 +306,42 @@ export async function createRequestRenderer({ projectRoot, mode, assetManifest,
|
|
|
246
306
|
}
|
|
247
307
|
|
|
248
308
|
return [
|
|
249
|
-
...urls.map((href) => `<link rel="modulepreload" href="${href}">`),
|
|
250
|
-
...styleUrls.map((href) => `<link rel="stylesheet" href="${href}">`),
|
|
309
|
+
...urls.map((href) => `<link rel="modulepreload" href="${href}" data-evolit-route-asset="preload">`),
|
|
310
|
+
...styleUrls.map((href) => `<link rel="stylesheet" href="${href}" data-evolit-route-asset="style">`),
|
|
251
311
|
].join("\n");
|
|
252
312
|
},
|
|
253
|
-
resolveBootstrap({ result }) {
|
|
313
|
+
resolveBootstrap({ routeResult, result }) {
|
|
314
|
+
const routeClientImports = resolveRouteClientImports(
|
|
315
|
+
routeResult,
|
|
316
|
+
projectRoot,
|
|
317
|
+
currentAssetManifest,
|
|
318
|
+
);
|
|
254
319
|
return createHydrationBootstrap({
|
|
255
|
-
hydrationData: normalizeHydrationDataForClient(
|
|
320
|
+
hydrationData: normalizeHydrationDataForClient(
|
|
321
|
+
result.hydrationData,
|
|
322
|
+
projectRoot,
|
|
323
|
+
routeClientImports,
|
|
324
|
+
),
|
|
256
325
|
assetResolver(moduleId) {
|
|
257
326
|
return currentAssetResolver(moduleId);
|
|
258
327
|
},
|
|
259
328
|
hydrationModuleUrl: currentHydrationModuleUrl,
|
|
329
|
+
navigationModuleUrl: currentNavigationModuleUrl,
|
|
260
330
|
});
|
|
261
331
|
},
|
|
262
|
-
transformDocument({ result, document }) {
|
|
332
|
+
transformDocument({ routeResult, result, document }) {
|
|
333
|
+
const routeClientImports = resolveRouteClientImports(
|
|
334
|
+
routeResult,
|
|
335
|
+
projectRoot,
|
|
336
|
+
currentAssetManifest,
|
|
337
|
+
);
|
|
263
338
|
return rewriteHydrationDataScript(
|
|
264
339
|
document,
|
|
265
|
-
normalizeHydrationDataForClient(
|
|
340
|
+
normalizeHydrationDataForClient(
|
|
341
|
+
result.hydrationData,
|
|
342
|
+
projectRoot,
|
|
343
|
+
routeClientImports,
|
|
344
|
+
),
|
|
266
345
|
);
|
|
267
346
|
},
|
|
268
347
|
});
|
|
@@ -278,21 +357,52 @@ export async function createRequestRenderer({ projectRoot, mode, assetManifest,
|
|
|
278
357
|
return effectiveRouteResolver;
|
|
279
358
|
},
|
|
280
359
|
responseCacheController,
|
|
281
|
-
async invalidateDevelopmentState() {
|
|
360
|
+
async invalidateDevelopmentState(changedPaths = null) {
|
|
282
361
|
if (mode !== "development") {
|
|
283
362
|
return;
|
|
284
363
|
}
|
|
285
364
|
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
365
|
+
return enqueueDevClientAssetWork(async () => {
|
|
366
|
+
developmentMetrics.invalidations += 1;
|
|
367
|
+
const normalizedChangedPaths = Array.isArray(changedPaths) && changedPaths.length > 0
|
|
368
|
+
? new Set(changedPaths.map((changedPath) => path.resolve(changedPath)))
|
|
369
|
+
: null;
|
|
370
|
+
invalidateDevelopmentCompilationCache(projectRoot, changedPaths);
|
|
371
|
+
const affectedClientModules = normalizedChangedPaths == null
|
|
372
|
+
? new Set(devPreparedClientModules)
|
|
373
|
+
: new Set(
|
|
374
|
+
[...devClientModuleDependencies]
|
|
375
|
+
.filter(([, dependencies]) =>
|
|
376
|
+
[...dependencies].some((dependency) => normalizedChangedPaths.has(dependency))
|
|
377
|
+
)
|
|
378
|
+
.map(([clientModule]) => clientModule),
|
|
379
|
+
);
|
|
380
|
+
|
|
381
|
+
if (affectedClientModules.size > 0 || normalizedChangedPaths == null) {
|
|
382
|
+
developmentMetrics.selectivelyInvalidatedClientEntries += affectedClientModules.size;
|
|
383
|
+
currentAssetManifest = null;
|
|
384
|
+
currentAssetResolver = createAssetResolver(projectRoot, {
|
|
385
|
+
assetManifest: currentAssetManifest,
|
|
386
|
+
});
|
|
387
|
+
devRollupCache = null;
|
|
388
|
+
for (const clientModule of affectedClientModules) {
|
|
389
|
+
devPreparedClientModules.delete(clientModule);
|
|
390
|
+
devClientModuleDependencies.delete(clientModule);
|
|
391
|
+
}
|
|
392
|
+
}
|
|
292
393
|
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
394
|
+
reportDevelopmentEvent({
|
|
395
|
+
type: "invalidated",
|
|
396
|
+
changedPathCount: normalizedChangedPaths?.size ?? null,
|
|
397
|
+
affectedClientEntryCount: affectedClientModules.size,
|
|
398
|
+
});
|
|
399
|
+
|
|
400
|
+
if (usesFrameworkRouteResolver) {
|
|
401
|
+
const segmentEntryCount = effectiveRouteResolver.invalidateSegmentCache?.(changedPaths) ?? 0;
|
|
402
|
+
reportDevelopmentEvent({ type: "segment-cache-invalidated", entryCount: segmentEntryCount });
|
|
403
|
+
effectiveRouteResolver = await createFrameworkRouteResolver();
|
|
404
|
+
}
|
|
405
|
+
});
|
|
296
406
|
},
|
|
297
407
|
async prepareRouteClientArtifacts(routeResult) {
|
|
298
408
|
if (routeResult.type !== "route" && !routeResult.boundaryModule) {
|
|
@@ -304,19 +414,57 @@ export async function createRequestRenderer({ projectRoot, mode, assetManifest,
|
|
|
304
414
|
...(routeResult.boundaryModule ? [routeResult.boundaryModule] : []),
|
|
305
415
|
];
|
|
306
416
|
|
|
307
|
-
|
|
308
|
-
const
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
417
|
+
if (mode !== "development") {
|
|
418
|
+
for (const clientModule of new Set(clientModules)) {
|
|
419
|
+
await compileModuleGraph(clientModule, {
|
|
420
|
+
projectRoot,
|
|
421
|
+
mode,
|
|
422
|
+
sourceMaps: false,
|
|
423
|
+
target: "client",
|
|
424
|
+
onDevelopmentEvent,
|
|
425
|
+
});
|
|
426
|
+
}
|
|
427
|
+
return;
|
|
317
428
|
}
|
|
318
429
|
|
|
319
|
-
|
|
430
|
+
return enqueueDevClientAssetWork(async () => {
|
|
431
|
+
let discoveredClientEntry = false;
|
|
432
|
+
for (const clientModule of new Set(clientModules)) {
|
|
433
|
+
const clientModuleKey = getDevClientModuleKey(clientModule);
|
|
434
|
+
if (devPreparedClientModules.has(clientModuleKey)) {
|
|
435
|
+
continue;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
const clientBuild = await compileModuleGraph(clientModule, {
|
|
439
|
+
projectRoot,
|
|
440
|
+
mode,
|
|
441
|
+
sourceMaps: true,
|
|
442
|
+
target: "client",
|
|
443
|
+
onDevelopmentEvent,
|
|
444
|
+
});
|
|
445
|
+
devPreparedClientModules.add(clientModuleKey);
|
|
446
|
+
devClientModuleDependencies.set(
|
|
447
|
+
clientModuleKey,
|
|
448
|
+
new Set(clientBuild.sourceFiles ?? [clientModuleKey]),
|
|
449
|
+
);
|
|
450
|
+
devBundledEntries.add(
|
|
451
|
+
path.relative(clientBuild.outputRoot, clientBuild.entrypoint).split(path.sep).join("/"),
|
|
452
|
+
);
|
|
453
|
+
discoveredClientEntry = true;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
if (!discoveredClientEntry && currentAssetManifest) {
|
|
457
|
+
developmentMetrics.clientArtifactCacheHits += 1;
|
|
458
|
+
reportDevelopmentEvent({ type: "client-assets-cache-hit" });
|
|
459
|
+
return;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
developmentMetrics.clientArtifactBuilds += 1;
|
|
463
|
+
const buildStartedAt = performance.now();
|
|
464
|
+
reportDevelopmentEvent({
|
|
465
|
+
type: "client-assets-building",
|
|
466
|
+
entryCount: devBundledEntries.size,
|
|
467
|
+
});
|
|
320
468
|
const bundledClientAssets = await emitBundledClientAssetsWithState(projectRoot, {
|
|
321
469
|
mode: "development",
|
|
322
470
|
entryClientModules: devBundledEntries,
|
|
@@ -336,19 +484,25 @@ export async function createRequestRenderer({ projectRoot, mode, assetManifest,
|
|
|
336
484
|
},
|
|
337
485
|
) ?? currentHydrationModuleUrl;
|
|
338
486
|
devRollupCache = bundledClientAssets.rollupCache;
|
|
339
|
-
|
|
487
|
+
reportDevelopmentEvent({
|
|
488
|
+
type: "client-assets-ready",
|
|
489
|
+
durationMs: Math.round(performance.now() - buildStartedAt),
|
|
490
|
+
entryCount: devBundledEntries.size,
|
|
491
|
+
});
|
|
492
|
+
});
|
|
340
493
|
},
|
|
341
494
|
async resolveRoutePolicy(request) {
|
|
342
495
|
return effectiveRouteResolver.resolveRoutePolicy(request);
|
|
343
496
|
},
|
|
344
497
|
async renderRoute(request, routePolicyResult = null) {
|
|
345
498
|
const shouldPrepareBeforeResolve = mode === "development" && !currentAssetManifest;
|
|
499
|
+
let resolvedRoutePolicyResult = routePolicyResult;
|
|
346
500
|
if (shouldPrepareBeforeResolve) {
|
|
347
|
-
|
|
501
|
+
resolvedRoutePolicyResult ??= await effectiveRouteResolver.resolveRoutePolicy(request);
|
|
348
502
|
await this.prepareRouteClientArtifacts(resolvedRoutePolicyResult);
|
|
349
503
|
}
|
|
350
504
|
|
|
351
|
-
const routeResult = await effectiveRouteResolver.resolveRequest(request);
|
|
505
|
+
const routeResult = await effectiveRouteResolver.resolveRequest(request, resolvedRoutePolicyResult);
|
|
352
506
|
if (!shouldPrepareBeforeResolve || routeResult.boundaryModule) {
|
|
353
507
|
await this.prepareRouteClientArtifacts(routeResult);
|
|
354
508
|
}
|
|
@@ -361,6 +515,9 @@ export async function createRequestRenderer({ projectRoot, mode, assetManifest,
|
|
|
361
515
|
get assetManifest() {
|
|
362
516
|
return currentAssetManifest;
|
|
363
517
|
},
|
|
518
|
+
get developmentMetrics() {
|
|
519
|
+
return mode === "development" ? { ...developmentMetrics } : null;
|
|
520
|
+
},
|
|
364
521
|
};
|
|
365
522
|
}
|
|
366
523
|
|
|
@@ -370,7 +527,26 @@ export async function createDeploymentRuntime({
|
|
|
370
527
|
assetManifest,
|
|
371
528
|
responseCacheRuntime,
|
|
372
529
|
routeResolver,
|
|
530
|
+
onDevelopmentEvent,
|
|
373
531
|
} = {}) {
|
|
532
|
+
function reportDevelopmentEvent(event) {
|
|
533
|
+
if (mode === "development" && typeof onDevelopmentEvent === "function") {
|
|
534
|
+
onDevelopmentEvent(event);
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
if (mode === "development") {
|
|
539
|
+
reportDevelopmentEvent({ type: "initializing" });
|
|
540
|
+
const developmentRoot = path.join(projectRoot, INTERNAL_DIRECTORY, DEV_DIRECTORY);
|
|
541
|
+
await fs.rm(developmentRoot, { recursive: true, force: true });
|
|
542
|
+
invalidateDevelopmentCompilationCache(projectRoot);
|
|
543
|
+
resetDevelopmentAssetCaches(projectRoot);
|
|
544
|
+
await buildSharedVendorRuntime(projectRoot, "development", {
|
|
545
|
+
additionalEntrySpecifiers: [],
|
|
546
|
+
});
|
|
547
|
+
reportDevelopmentEvent({ type: "vendor-runtime-ready" });
|
|
548
|
+
}
|
|
549
|
+
|
|
374
550
|
const effectiveResponseCacheRuntime = responseCacheRuntime
|
|
375
551
|
?? await resolveResponseCacheRuntime(
|
|
376
552
|
projectRoot,
|
|
@@ -378,8 +554,18 @@ export async function createDeploymentRuntime({
|
|
|
378
554
|
await loadEvolitConfig(projectRoot),
|
|
379
555
|
);
|
|
380
556
|
const runtimeState = {
|
|
381
|
-
assetManifest: normalizeClientAssetManifest(assetManifest),
|
|
557
|
+
assetManifest: mode === "development" ? null : normalizeClientAssetManifest(assetManifest),
|
|
382
558
|
};
|
|
559
|
+
|
|
560
|
+
function formatResponseForRequest(request, response) {
|
|
561
|
+
const representation = request.headers.get("accept")?.includes("application/vnd.evolit.navigation+json")
|
|
562
|
+
? createNavigationResponseFromDocument(response)
|
|
563
|
+
: response;
|
|
564
|
+
// The same URL has an HTML document representation and a navigation
|
|
565
|
+
// delta representation. Tell intermediary HTTP caches that Accept selects
|
|
566
|
+
// between them.
|
|
567
|
+
return addNavigationVaryHeader(representation);
|
|
568
|
+
}
|
|
383
569
|
const assets = createPublicAssetOrigin({
|
|
384
570
|
projectRoot,
|
|
385
571
|
mode,
|
|
@@ -393,18 +579,19 @@ export async function createDeploymentRuntime({
|
|
|
393
579
|
assetManifest: runtimeState.assetManifest,
|
|
394
580
|
responseCacheRuntime: effectiveResponseCacheRuntime,
|
|
395
581
|
routeResolver,
|
|
582
|
+
onDevelopmentEvent,
|
|
396
583
|
});
|
|
397
584
|
|
|
398
585
|
return {
|
|
399
586
|
assets,
|
|
400
587
|
cache: renderer.responseCacheController,
|
|
401
588
|
renderer,
|
|
402
|
-
async invalidateDevelopmentState() {
|
|
589
|
+
async invalidateDevelopmentState(changedPaths = null) {
|
|
403
590
|
if (mode !== "development") {
|
|
404
591
|
return;
|
|
405
592
|
}
|
|
406
593
|
|
|
407
|
-
await renderer.invalidateDevelopmentState();
|
|
594
|
+
await renderer.invalidateDevelopmentState(changedPaths);
|
|
408
595
|
if (typeof effectiveResponseCacheRuntime.store.clear === "function") {
|
|
409
596
|
await effectiveResponseCacheRuntime.store.clear();
|
|
410
597
|
}
|
|
@@ -422,7 +609,7 @@ export async function createDeploymentRuntime({
|
|
|
422
609
|
const cachedResponse = await renderer.responseCacheController.read(request, routePolicyResult);
|
|
423
610
|
if (cachedResponse) {
|
|
424
611
|
if (cachedResponse.fresh) {
|
|
425
|
-
return cachedResponse.response;
|
|
612
|
+
return formatResponseForRequest(request, cachedResponse.response);
|
|
426
613
|
}
|
|
427
614
|
|
|
428
615
|
if (
|
|
@@ -446,12 +633,15 @@ export async function createDeploymentRuntime({
|
|
|
446
633
|
revalidationTasks.set(cachedResponse.cacheKey, revalidationTask);
|
|
447
634
|
}
|
|
448
635
|
|
|
449
|
-
return cachedResponse.response;
|
|
636
|
+
return formatResponseForRequest(request, cachedResponse.response);
|
|
450
637
|
}
|
|
451
638
|
|
|
452
639
|
const { routeResult, response } = await renderer.renderRoute(request, routePolicyResult);
|
|
453
640
|
runtimeState.assetManifest = normalizeClientAssetManifest(renderer.assetManifest);
|
|
454
|
-
return
|
|
641
|
+
return formatResponseForRequest(
|
|
642
|
+
request,
|
|
643
|
+
await renderer.responseCacheController.write(request, routeResult, response),
|
|
644
|
+
);
|
|
455
645
|
},
|
|
456
646
|
async close() {
|
|
457
647
|
const revalidationTasks = this.revalidationTasks;
|
|
@@ -461,3 +651,16 @@ export async function createDeploymentRuntime({
|
|
|
461
651
|
},
|
|
462
652
|
};
|
|
463
653
|
}
|
|
654
|
+
|
|
655
|
+
function addNavigationVaryHeader(response) {
|
|
656
|
+
if (!response?.headers) return response;
|
|
657
|
+
const fields = String(response.headers.vary ?? "")
|
|
658
|
+
.split(",")
|
|
659
|
+
.map((field) => field.trim())
|
|
660
|
+
.filter(Boolean);
|
|
661
|
+
if (!fields.some((field) => field.toLowerCase() === "accept")) fields.push("accept");
|
|
662
|
+
return {
|
|
663
|
+
...response,
|
|
664
|
+
headers: { ...response.headers, vary: fields.join(", ") },
|
|
665
|
+
};
|
|
666
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { terminal } from "./terminal.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Creates the default development-event sink used by the CLI and dev server.
|
|
5
|
+
* Keeping presentation here lets integrations replace it with onDevelopmentEvent
|
|
6
|
+
* without losing any lifecycle, build, or request diagnostics.
|
|
7
|
+
*/
|
|
8
|
+
export function createDevelopmentEventReporter() {
|
|
9
|
+
return (event) => {
|
|
10
|
+
switch (event.type) {
|
|
11
|
+
case "server-starting":
|
|
12
|
+
console.log(`${terminal.cyan("▲")} ${terminal.bold("Evolit")} ${terminal.cyan(event.mode ?? "dev")}`);
|
|
13
|
+
break;
|
|
14
|
+
case "usage":
|
|
15
|
+
console.log(`${terminal.bold("Usage:")} evolit <directory>`);
|
|
16
|
+
console.log(` evolit ${terminal.cyan("init")} <directory>`);
|
|
17
|
+
console.log(` evolit ${terminal.cyan("<dev|build|start>")} [--port 3000]`);
|
|
18
|
+
break;
|
|
19
|
+
case "site-created":
|
|
20
|
+
console.log(`${terminal.green("✓")} Created evolit site: ${terminal.cyan(event.directory)}`);
|
|
21
|
+
break;
|
|
22
|
+
case "build-ready":
|
|
23
|
+
console.log(`${terminal.green("✓")} Built Evolit app: ${terminal.cyan(event.manifestPath)}`);
|
|
24
|
+
break;
|
|
25
|
+
case "initializing":
|
|
26
|
+
console.log(`${terminal.cyan("[evolit]")} ${terminal.yellow("○")} Preparing development runtime…`);
|
|
27
|
+
break;
|
|
28
|
+
case "vendor-runtime-ready":
|
|
29
|
+
console.log(`${terminal.cyan("[evolit]")} ${terminal.green("✓")} Vendor runtime ready.`);
|
|
30
|
+
break;
|
|
31
|
+
case "client-assets-building":
|
|
32
|
+
console.log(`${terminal.cyan("[evolit]")} ${terminal.magenta("○")} Compiling client assets (${event.entryCount} entries)…`);
|
|
33
|
+
break;
|
|
34
|
+
case "client-assets-ready":
|
|
35
|
+
console.log(`${terminal.cyan("[evolit]")} ${terminal.green("✓")} Client assets compiled in ${terminal.dim(`${event.durationMs}ms`)} (${event.entryCount} entries).`);
|
|
36
|
+
break;
|
|
37
|
+
case "client-assets-cache-hit":
|
|
38
|
+
console.log(`${terminal.cyan("[evolit]")} ${terminal.blue("●")} Client assets served from cache.`);
|
|
39
|
+
break;
|
|
40
|
+
case "segment-cache-hit":
|
|
41
|
+
console.log(`${terminal.cyan("[evolit]")} ${terminal.blue("●")} Reused route segment ${terminal.dim(event.modulePath)}.`);
|
|
42
|
+
break;
|
|
43
|
+
case "segment-cache-miss":
|
|
44
|
+
console.log(`${terminal.cyan("[evolit]")} ${terminal.magenta("○")} Rendering route segment ${terminal.dim(event.modulePath)} ${terminal.dim(`(${event.reason})`)}.`);
|
|
45
|
+
break;
|
|
46
|
+
case "segment-cache-invalidated":
|
|
47
|
+
console.log(`${terminal.cyan("[evolit]")} ${terminal.yellow("△")} Invalidated ${event.entryCount} cached route segments.`);
|
|
48
|
+
break;
|
|
49
|
+
case "invalidated":
|
|
50
|
+
console.log(
|
|
51
|
+
`${terminal.cyan("[evolit]")} ${terminal.yellow("△")} Source change detected${event.changedPathCount == null ? "" : ` (${event.changedPathCount} files)`}; invalidated ${event.affectedClientEntryCount} client entries.`,
|
|
52
|
+
);
|
|
53
|
+
break;
|
|
54
|
+
case "unmanaged-import":
|
|
55
|
+
console.warn(
|
|
56
|
+
`${terminal.yellow("[evolit]")} ${terminal.yellow("⚠")} Unmanaged import ${terminal.cyan(JSON.stringify(event.resolvedImportPath))} from ${terminal.cyan(JSON.stringify(event.sourcePath))} is outside the project root. Evolit does not watch it; changes may not trigger a rebuild, and it may be unavailable in another checkout or deployment. It will be emitted under ${terminal.cyan("/_evolit/static/__unmanaged__/")}.`,
|
|
57
|
+
);
|
|
58
|
+
break;
|
|
59
|
+
case "server-ready":
|
|
60
|
+
console.log(`${terminal.green("✓")} Ready on ${terminal.cyan(`http://localhost:${event.port}`)}`);
|
|
61
|
+
break;
|
|
62
|
+
case "request": {
|
|
63
|
+
const statusColor = event.status >= 500
|
|
64
|
+
? terminal.red
|
|
65
|
+
: event.status >= 400
|
|
66
|
+
? terminal.yellow
|
|
67
|
+
: event.status >= 300
|
|
68
|
+
? terminal.blue
|
|
69
|
+
: terminal.green;
|
|
70
|
+
console.log(
|
|
71
|
+
`${terminal.cyan("[evolit]")} ${terminal.dim(event.method)} ${event.pathname} ${statusColor(event.status)} in ${terminal.dim(`${event.durationMs}ms`)}${event.cacheState ? ` ${terminal.blue(`(${event.cacheState})`)}` : ""}`,
|
|
72
|
+
);
|
|
73
|
+
break;
|
|
74
|
+
}
|
|
75
|
+
case "request-error":
|
|
76
|
+
console.error(
|
|
77
|
+
`${terminal.red("[evolit]")} ${terminal.red("✖")} ${event.method} ${event.pathname} failed`,
|
|
78
|
+
event.error,
|
|
79
|
+
);
|
|
80
|
+
break;
|
|
81
|
+
case "fatal-error":
|
|
82
|
+
console.error(
|
|
83
|
+
`${terminal.red("[evolit]")} ${terminal.red("✖")} ${event.error instanceof Error ? event.error.stack ?? event.error.message : String(event.error)}`,
|
|
84
|
+
);
|
|
85
|
+
break;
|
|
86
|
+
case "route-error":
|
|
87
|
+
console.error(
|
|
88
|
+
`${terminal.red("[evolit]")} ${terminal.red("✖")} Route rendering failed ${terminal.dim(`(${event.digest})`)} at ${terminal.cyan(event.pathname)}`,
|
|
89
|
+
event.error,
|
|
90
|
+
);
|
|
91
|
+
break;
|
|
92
|
+
default:
|
|
93
|
+
break;
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
}
|