astro 7.2.9 → 7.3.0
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/dist/assets/build/generate.d.ts +1 -1
- package/dist/assets/build/generate.js +3 -1
- package/dist/assets/endpoint/dev.js +6 -5
- package/dist/assets/endpoint/generic.js +5 -4
- package/dist/assets/endpoint/node.js +3 -3
- package/dist/assets/endpoint/shared.d.ts +3 -1
- package/dist/assets/endpoint/shared.js +9 -3
- package/dist/assets/fonts/config.d.ts +2 -2
- package/dist/assets/internal.d.ts +2 -1
- package/dist/assets/internal.js +11 -10
- package/dist/assets/services/service.d.ts +8 -7
- package/dist/assets/services/sharp.js +5 -5
- package/dist/assets/vite-plugin-assets.js +22 -5
- package/dist/cli/add/index.js +3 -1
- package/dist/cli/index.js +1 -1
- package/dist/cli/infra/build-time-astro-version-provider.js +1 -1
- package/dist/cli/preferences/index.js +32 -30
- package/dist/cli/preview/index.js +44 -1
- package/dist/cli/telemetry/index.d.ts +2 -1
- package/dist/cli/telemetry/index.js +2 -2
- package/dist/content/content-layer.js +3 -3
- package/dist/content/data-store-writer.d.ts +18 -5
- package/dist/content/data-store-writer.js +11 -3
- package/dist/content/index.d.ts +1 -1
- package/dist/content/index.js +5 -1
- package/dist/content/mutable-data-store.d.ts +13 -1
- package/dist/content/mutable-data-store.js +35 -5
- package/dist/content/runtime.d.ts +8 -3
- package/dist/content/runtime.js +43 -32
- package/dist/content/types-generator.js +2 -1
- package/dist/content/utils.d.ts +5 -35
- package/dist/content/utils.js +71 -65
- package/dist/content/vite-plugin-content-imports.js +2 -1
- package/dist/content/vite-plugin-content-virtual-mod.d.ts +11 -1
- package/dist/content/vite-plugin-content-virtual-mod.js +36 -3
- package/dist/core/app/entrypoints/index.d.ts +4 -2
- package/dist/core/app/entrypoints/index.js +13 -11
- package/dist/core/app/entrypoints/virtual/dev.js +2 -2
- package/dist/core/app/prerender.d.ts +32 -0
- package/dist/core/app/prerender.js +22 -0
- package/dist/core/build/default-prerenderer.js +5 -2
- package/dist/core/build/generate.d.ts +3 -1
- package/dist/core/build/generate.js +40 -62
- package/dist/core/build/plugins/plugin-manifest.js +1 -1
- package/dist/core/cache/handler.js +12 -1
- package/dist/core/cache/memory-provider.js +35 -8
- package/dist/core/cache/runtime/cache.js +3 -0
- package/dist/core/cache/types.d.ts +2 -0
- package/dist/core/config/schemas/base.d.ts +5 -5
- package/dist/core/config/schemas/relative.d.ts +7 -7
- package/dist/core/constants.js +1 -1
- package/dist/core/cookies/cookies.d.ts +2 -1
- package/dist/core/cookies/cookies.js +5 -4
- package/dist/core/dev/dev.js +5 -3
- package/dist/core/fetch/fetch-state.d.ts +1 -1
- package/dist/core/fetch/fetch-state.js +4 -23
- package/dist/core/logger/core.d.ts +2 -0
- package/dist/core/logger/core.js +14 -0
- package/dist/core/logger/vite-plugin.d.ts +13 -2
- package/dist/core/logger/vite-plugin.js +8 -7
- package/dist/core/messages/runtime.js +1 -1
- package/dist/core/middleware/index.js +14 -1
- package/dist/core/render-scope/collect.d.ts +24 -0
- package/dist/core/render-scope/collect.js +27 -0
- package/dist/core/render-scope/node-scope.d.ts +10 -0
- package/dist/core/render-scope/node-scope.js +8 -0
- package/dist/core/render-scope/record.d.ts +14 -0
- package/dist/core/render-scope/record.js +12 -0
- package/dist/core/render-scope/scope.d.ts +38 -0
- package/dist/core/render-scope/scope.js +28 -0
- package/dist/core/rewrites/handler.js +1 -1
- package/dist/core/routing/create-manifest.js +5 -1
- package/dist/core/server-islands/endpoint.js +4 -1
- package/dist/core/server-islands/vite-plugin-server-islands.js +2 -2
- package/dist/core/sync/index.js +1 -1
- package/dist/core/util.d.ts +0 -9
- package/dist/core/util.js +0 -34
- package/dist/core/viteUtils.d.ts +1 -13
- package/dist/core/viteUtils.js +3 -33
- package/dist/types/public/integrations.d.ts +20 -13
- package/dist/vite-plugin-app/createAstroServerApp.js +1 -1
- package/dist/vite-plugin-routes/index.js +22 -8
- package/package.json +6 -6
- package/templates/content/module.mjs +13 -5
- package/dist/core/build/incremental-content-collector.d.ts +0 -10
- package/dist/core/build/incremental-content-collector.js +0 -33
- package/dist/core/build/incremental-image-collector.d.ts +0 -11
- package/dist/core/build/incremental-image-collector.js +0 -32
package/dist/content/utils.js
CHANGED
|
@@ -36,66 +36,71 @@ const loaderReturnSchema = z.union([
|
|
|
36
36
|
}).passthrough()
|
|
37
37
|
)
|
|
38
38
|
]);
|
|
39
|
-
|
|
40
|
-
z.object({
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
loader: z.never().optional()
|
|
49
|
-
}),
|
|
50
|
-
z.object({
|
|
51
|
-
type: z.literal(CONTENT_LAYER_TYPE),
|
|
52
|
-
schema: z.any().optional(),
|
|
53
|
-
loader: z.union([
|
|
54
|
-
z.function(),
|
|
55
|
-
z.object({
|
|
56
|
-
name: z.string(),
|
|
57
|
-
load: z.function({
|
|
58
|
-
input: [z.custom()],
|
|
59
|
-
output: z.custom()
|
|
39
|
+
function contentConfigParser(logger) {
|
|
40
|
+
return z.object({
|
|
41
|
+
collections: z.record(
|
|
42
|
+
z.string(),
|
|
43
|
+
z.union([
|
|
44
|
+
z.object({
|
|
45
|
+
type: z.literal("content").optional(),
|
|
46
|
+
schema: z.any().optional(),
|
|
47
|
+
loader: z.never().optional()
|
|
60
48
|
}),
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
ctx.addIssue({
|
|
72
|
-
code: z.ZodIssueCode.custom,
|
|
73
|
-
message: "Invalid Zod schema"
|
|
74
|
-
});
|
|
75
|
-
return z.NEVER;
|
|
76
|
-
}
|
|
77
|
-
}).optional(),
|
|
78
|
-
createSchema: z.function({
|
|
79
|
-
input: [],
|
|
80
|
-
output: z.promise(
|
|
49
|
+
z.object({
|
|
50
|
+
type: z.literal("data").optional(),
|
|
51
|
+
schema: z.any().optional(),
|
|
52
|
+
loader: z.never().optional()
|
|
53
|
+
}),
|
|
54
|
+
z.object({
|
|
55
|
+
type: z.literal(CONTENT_LAYER_TYPE),
|
|
56
|
+
schema: z.any().optional(),
|
|
57
|
+
loader: z.union([
|
|
58
|
+
z.function(),
|
|
81
59
|
z.object({
|
|
82
|
-
|
|
83
|
-
|
|
60
|
+
name: z.string(),
|
|
61
|
+
load: z.function({
|
|
62
|
+
input: [z.custom()],
|
|
63
|
+
output: z.custom()
|
|
64
|
+
}),
|
|
65
|
+
schema: z.any().transform((v) => {
|
|
66
|
+
if (typeof v === "function") {
|
|
67
|
+
logger.warn(
|
|
68
|
+
"content",
|
|
69
|
+
`Your loader's schema is defined using a function. This is no longer supported and the schema will be ignored. Please update your loader to use the \`createSchema()\` utility instead, or report this to the loader author. In a future major version, this will cause the loader to break entirely.`
|
|
70
|
+
);
|
|
71
|
+
return void 0;
|
|
72
|
+
}
|
|
73
|
+
return v;
|
|
74
|
+
}).superRefine((v, ctx) => {
|
|
75
|
+
if (v !== void 0 && !("_zod" in v)) {
|
|
76
|
+
ctx.addIssue({
|
|
77
|
+
code: z.ZodIssueCode.custom,
|
|
78
|
+
message: "Invalid Zod schema"
|
|
79
|
+
});
|
|
80
|
+
return z.NEVER;
|
|
81
|
+
}
|
|
82
|
+
}).optional(),
|
|
83
|
+
createSchema: z.function({
|
|
84
|
+
input: [],
|
|
85
|
+
output: z.promise(
|
|
86
|
+
z.object({
|
|
87
|
+
schema: z.custom((v) => "_zod" in v),
|
|
88
|
+
types: z.string()
|
|
89
|
+
})
|
|
90
|
+
)
|
|
91
|
+
}).optional()
|
|
84
92
|
})
|
|
85
|
-
)
|
|
86
|
-
})
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
})
|
|
95
|
-
|
|
96
|
-
const contentConfigParser = z.object({
|
|
97
|
-
collections: z.record(z.string(), collectionConfigParser)
|
|
98
|
-
});
|
|
93
|
+
])
|
|
94
|
+
}),
|
|
95
|
+
z.object({
|
|
96
|
+
type: z.literal(LIVE_CONTENT_TYPE).optional().default(LIVE_CONTENT_TYPE),
|
|
97
|
+
schema: z.any().optional(),
|
|
98
|
+
loader: z.function()
|
|
99
|
+
})
|
|
100
|
+
])
|
|
101
|
+
)
|
|
102
|
+
});
|
|
103
|
+
}
|
|
99
104
|
function parseEntrySlug({
|
|
100
105
|
id,
|
|
101
106
|
collection,
|
|
@@ -358,7 +363,8 @@ function isDeferredModule(viteId) {
|
|
|
358
363
|
async function loadContentConfig({
|
|
359
364
|
fs,
|
|
360
365
|
settings,
|
|
361
|
-
environment
|
|
366
|
+
environment,
|
|
367
|
+
logger
|
|
362
368
|
}) {
|
|
363
369
|
const contentPaths = getContentPaths(
|
|
364
370
|
settings.config,
|
|
@@ -370,19 +376,17 @@ async function loadContentConfig({
|
|
|
370
376
|
}
|
|
371
377
|
const configPathname = fileURLToPath(contentPaths.config.url);
|
|
372
378
|
const unparsedConfig = await environment.runner.import(configPathname);
|
|
373
|
-
const config = contentConfigParser.safeParse(unparsedConfig);
|
|
379
|
+
const config = contentConfigParser(logger).safeParse(unparsedConfig);
|
|
374
380
|
if (config.success) {
|
|
375
381
|
const hasher = await xxhash();
|
|
376
382
|
const digest = hasher.h64ToString(await fs.promises.readFile(configPathname, "utf-8"));
|
|
377
383
|
return { ...config.data, digest };
|
|
378
384
|
} else {
|
|
379
385
|
const message = config.error.issues.map((issue) => ` \u2192 ${colors.green(issue.path.join("."))}: ${colors.red(issue.message)}`).join("\n");
|
|
380
|
-
|
|
381
|
-
`${colors.green("[content]")} There was a problem with your content config:
|
|
386
|
+
logger.error("content", `There was a problem with your content config:
|
|
382
387
|
|
|
383
388
|
${message}
|
|
384
|
-
`
|
|
385
|
-
);
|
|
389
|
+
`);
|
|
386
390
|
const liveCollections = Object.entries(unparsedConfig.collections ?? {}).filter(
|
|
387
391
|
([, collection]) => collection?.type === LIVE_CONTENT_TYPE
|
|
388
392
|
);
|
|
@@ -401,7 +405,8 @@ ${message}
|
|
|
401
405
|
async function autogenerateCollections({
|
|
402
406
|
config,
|
|
403
407
|
settings,
|
|
404
|
-
fs
|
|
408
|
+
fs,
|
|
409
|
+
logger
|
|
405
410
|
}) {
|
|
406
411
|
if (!config) {
|
|
407
412
|
return config;
|
|
@@ -455,7 +460,8 @@ async function autogenerateCollections({
|
|
|
455
460
|
}
|
|
456
461
|
}
|
|
457
462
|
if (orphanedCollections.length > 0) {
|
|
458
|
-
|
|
463
|
+
logger.warn(
|
|
464
|
+
"SKIP_FORMAT",
|
|
459
465
|
`
|
|
460
466
|
Auto-generating collections for folders in "src/content/" that are not defined as collections.
|
|
461
467
|
This is deprecated, so you should define these collections yourself in "src/content.config.ts".
|
|
@@ -138,7 +138,8 @@ export const _internal = {
|
|
|
138
138
|
await reloadContentConfigObserver({
|
|
139
139
|
fs,
|
|
140
140
|
settings,
|
|
141
|
-
environment: viteServer.environments[ASTRO_VITE_ENVIRONMENT_NAMES.astro]
|
|
141
|
+
environment: viteServer.environments[ASTRO_VITE_ENVIRONMENT_NAMES.astro],
|
|
142
|
+
logger
|
|
142
143
|
});
|
|
143
144
|
}
|
|
144
145
|
for (const modUrl of environment.moduleGraph.urlToModuleMap.keys()) {
|
|
@@ -1,9 +1,19 @@
|
|
|
1
1
|
import nodeFs from 'node:fs';
|
|
2
|
-
import { type Plugin } from 'vite';
|
|
2
|
+
import { type Plugin, type ViteDevServer } from 'vite';
|
|
3
3
|
import type { AstroSettings } from '../types/astro.js';
|
|
4
|
+
import type { MutableDataStore } from './mutable-data-store.js';
|
|
4
5
|
interface AstroContentVirtualModPluginParams {
|
|
5
6
|
settings: AstroSettings;
|
|
6
7
|
fs: typeof nodeFs;
|
|
7
8
|
}
|
|
9
|
+
/**
|
|
10
|
+
* Invalidates the content virtual modules directly whenever the given store
|
|
11
|
+
* writes to disk. The watcher listeners in `configureServer` cover writes from
|
|
12
|
+
* other processes, but the watcher can miss the atomic rename that commits a
|
|
13
|
+
* write on some platforms (notably Windows, see #17335), leaving dev serving
|
|
14
|
+
* stale content until a restart. Subscribing to the store's own write
|
|
15
|
+
* notifications makes invalidation of this process's writes deterministic.
|
|
16
|
+
*/
|
|
17
|
+
export declare function attachDataStoreInvalidation(store: MutableDataStore, server: ViteDevServer, settings: AstroSettings): void;
|
|
8
18
|
export declare function astroContentVirtualModPlugin({ settings, fs, }: AstroContentVirtualModPluginParams): Plugin;
|
|
9
19
|
export {};
|
|
@@ -70,6 +70,35 @@ function invalidateDataStore(viteServer, { notifyClient = true } = {}) {
|
|
|
70
70
|
});
|
|
71
71
|
}
|
|
72
72
|
}
|
|
73
|
+
const directInvalidations = /* @__PURE__ */ new Map();
|
|
74
|
+
const DIRECT_INVALIDATION_ECHO_MS = 1e3;
|
|
75
|
+
function markDirectInvalidation(path) {
|
|
76
|
+
directInvalidations.set(path, Date.now());
|
|
77
|
+
}
|
|
78
|
+
function isDirectInvalidationEcho(path) {
|
|
79
|
+
const time = directInvalidations.get(path);
|
|
80
|
+
return time !== void 0 && Date.now() - time < DIRECT_INVALIDATION_ECHO_MS;
|
|
81
|
+
}
|
|
82
|
+
function getDevDataStoreFile(settings) {
|
|
83
|
+
if (getDataStoreChunkSize(settings) !== void 0) {
|
|
84
|
+
return new URL(DATA_STORE_MANIFEST_FILE, getDataStoreDir(settings, true));
|
|
85
|
+
}
|
|
86
|
+
return getDataStoreFile(settings, true);
|
|
87
|
+
}
|
|
88
|
+
function attachDataStoreInvalidation(store, server, settings) {
|
|
89
|
+
const dataStorePath = fileURLToPath(getDevDataStoreFile(settings));
|
|
90
|
+
const assetImportsPath = fileURLToPath(new URL(ASSET_IMPORTS_FILE, settings.dotAstroDir));
|
|
91
|
+
store.onFileWritten((path) => {
|
|
92
|
+
if (path === dataStorePath) {
|
|
93
|
+
markDirectInvalidation(dataStorePath);
|
|
94
|
+
invalidateDataStore(server);
|
|
95
|
+
invalidateAssetImports(server, assetImportsPath);
|
|
96
|
+
} else if (path === assetImportsPath) {
|
|
97
|
+
markDirectInvalidation(assetImportsPath);
|
|
98
|
+
invalidateAssetImports(server, assetImportsPath);
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
}
|
|
73
102
|
function astroContentVirtualModPlugin({
|
|
74
103
|
settings,
|
|
75
104
|
fs
|
|
@@ -270,16 +299,19 @@ function astroContentVirtualModPlugin({
|
|
|
270
299
|
const dataStorePath = fileURLToPath(dataStoreFile);
|
|
271
300
|
const assetImportsPath = fileURLToPath(new URL(ASSET_IMPORTS_FILE, settings.dotAstroDir));
|
|
272
301
|
server.watcher.on("add", (addedPath) => {
|
|
273
|
-
if (addedPath === dataStorePath) {
|
|
302
|
+
if (addedPath === dataStorePath && !isDirectInvalidationEcho(dataStorePath)) {
|
|
274
303
|
invalidateDataStore(server);
|
|
275
304
|
invalidateAssetImports(server, assetImportsPath);
|
|
276
305
|
}
|
|
277
306
|
});
|
|
278
307
|
server.watcher.on("change", (changedPath) => {
|
|
279
308
|
if (changedPath === dataStorePath) {
|
|
309
|
+
if (isDirectInvalidationEcho(dataStorePath)) {
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
280
312
|
invalidateDataStore(server);
|
|
281
313
|
invalidateAssetImports(server, assetImportsPath);
|
|
282
|
-
} else if (changedPath === assetImportsPath) {
|
|
314
|
+
} else if (changedPath === assetImportsPath && !isDirectInvalidationEcho(assetImportsPath)) {
|
|
283
315
|
invalidateAssetImports(server, assetImportsPath);
|
|
284
316
|
}
|
|
285
317
|
});
|
|
@@ -314,5 +346,6 @@ async function generateContentEntryFile({
|
|
|
314
346
|
return virtualModContents;
|
|
315
347
|
}
|
|
316
348
|
export {
|
|
317
|
-
astroContentVirtualModPlugin
|
|
349
|
+
astroContentVirtualModPlugin,
|
|
350
|
+
attachDataStoreInvalidation
|
|
318
351
|
};
|
|
@@ -4,5 +4,7 @@ export { BaseApp, type RenderErrorOptions, type RenderOptions, type LogRequestPa
|
|
|
4
4
|
export { fromRoutingStrategy, toRoutingStrategy } from '../common.js';
|
|
5
5
|
export { createConsoleLogger } from '../../logger/impls/console.js';
|
|
6
6
|
export { deserializeManifest, deserializeRouteData, deserializeRouteInfo, serializeRouteData, serializeRouteInfo, } from '../manifest.js';
|
|
7
|
-
export {
|
|
8
|
-
export {
|
|
7
|
+
export { getInstalledRenderScope, installRenderScope, type RenderCollectors, type RenderCollectorScope, } from '../../render-scope/scope.js';
|
|
8
|
+
export { recordStaticImage } from '../../render-scope/record.js';
|
|
9
|
+
export { collectPrerenderMetadata, type CollectedPrerenderMetadata, } from '../../render-scope/collect.js';
|
|
10
|
+
export { renderForPrerender, type PrerenderableApp, type PrerenderRenderOptions, } from '../prerender.js';
|
|
@@ -12,27 +12,29 @@ import {
|
|
|
12
12
|
serializeRouteInfo
|
|
13
13
|
} from "../manifest.js";
|
|
14
14
|
import {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
} from "../../
|
|
15
|
+
getInstalledRenderScope,
|
|
16
|
+
installRenderScope
|
|
17
|
+
} from "../../render-scope/scope.js";
|
|
18
|
+
import { recordStaticImage } from "../../render-scope/record.js";
|
|
18
19
|
import {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
collectPrerenderMetadata
|
|
21
|
+
} from "../../render-scope/collect.js";
|
|
22
|
+
import {
|
|
23
|
+
renderForPrerender
|
|
24
|
+
} from "../prerender.js";
|
|
23
25
|
export {
|
|
24
26
|
App,
|
|
25
27
|
BaseApp,
|
|
26
|
-
|
|
27
|
-
beginImageCollection,
|
|
28
|
+
collectPrerenderMetadata,
|
|
28
29
|
createConsoleLogger,
|
|
29
30
|
deserializeManifest,
|
|
30
31
|
deserializeRouteData,
|
|
31
32
|
deserializeRouteInfo,
|
|
32
|
-
endContentEntryCollection,
|
|
33
|
-
endImageCollection,
|
|
34
33
|
fromRoutingStrategy,
|
|
34
|
+
getInstalledRenderScope,
|
|
35
|
+
installRenderScope,
|
|
35
36
|
recordStaticImage,
|
|
37
|
+
renderForPrerender,
|
|
36
38
|
serializeRouteData,
|
|
37
39
|
serializeRouteInfo,
|
|
38
40
|
toRoutingStrategy
|
|
@@ -11,7 +11,7 @@ import { updateRouteTable } from "../../../routing/route-table.js";
|
|
|
11
11
|
import { DevFacadeApp } from "../../dev-facade.js";
|
|
12
12
|
let hmrWired = false;
|
|
13
13
|
const createApp = ({ streaming } = {}) => {
|
|
14
|
-
setLogger(manifest, createConsoleLogger(manifest.logLevel));
|
|
14
|
+
setLogger(manifest, createConsoleLogger({ level: manifest.logLevel }));
|
|
15
15
|
setEnvironment(manifest, createNonRunnableEnvironment());
|
|
16
16
|
const app = new DevFacadeApp(manifest, streaming);
|
|
17
17
|
app.setFetchHandler(fetchable);
|
|
@@ -22,7 +22,7 @@ const createApp = ({ streaming } = {}) => {
|
|
|
22
22
|
const { routes: newRoutes } = await import("virtual:astro:routes");
|
|
23
23
|
updateRouteTable(
|
|
24
24
|
manifest,
|
|
25
|
-
newRoutes.map((
|
|
25
|
+
newRoutes.map((route) => route.routeData)
|
|
26
26
|
);
|
|
27
27
|
} catch (e) {
|
|
28
28
|
getLogger(manifest).error("router", `Failed to update routes via HMR:
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { PrerenderResult } from '../../types/public/integrations.js';
|
|
2
|
+
import type { RouteData } from '../../types/public/internal.js';
|
|
3
|
+
import type { AstroLogger } from '../logger/core.js';
|
|
4
|
+
export interface PrerenderableApp {
|
|
5
|
+
logger: AstroLogger;
|
|
6
|
+
render(request: Request, opts: {
|
|
7
|
+
routeData?: RouteData;
|
|
8
|
+
}): Promise<Response>;
|
|
9
|
+
}
|
|
10
|
+
export interface PrerenderRenderOptions {
|
|
11
|
+
routeData?: RouteData;
|
|
12
|
+
/** Collect per-render incremental metadata and return it on the result. Default false. */
|
|
13
|
+
collectMetadata?: boolean;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Renders a single prerendered path through `app.render`, optionally collecting
|
|
17
|
+
* the per-render incremental metadata (content entries rendered, image
|
|
18
|
+
* transforms resolved) and returning it by value on the result.
|
|
19
|
+
*
|
|
20
|
+
* When not collecting, this short-circuits to a bare `app.render` — the
|
|
21
|
+
* response is the app's own, unbuffered. When collecting, the response body is
|
|
22
|
+
* fully buffered *inside* the render scope so lazily-driven rendering work
|
|
23
|
+
* records before the metadata snapshot; the returned response is a
|
|
24
|
+
* reconstruction carrying the buffered body with status/statusText/headers and
|
|
25
|
+
* body-nullness preserved.
|
|
26
|
+
*
|
|
27
|
+
* This function performs no scope installation; installing a render scope for
|
|
28
|
+
* the rendering runtime is the caller's responsibility. When collection is
|
|
29
|
+
* requested but no scope is installed, `metadata` is `undefined`
|
|
30
|
+
* ("not tracked").
|
|
31
|
+
*/
|
|
32
|
+
export declare function renderForPrerender(app: PrerenderableApp, request: Request, options?: PrerenderRenderOptions): Promise<PrerenderResult>;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { collectPrerenderMetadata } from "../render-scope/collect.js";
|
|
2
|
+
const NULL_BODY_STATUSES = [101, 204, 205, 304];
|
|
3
|
+
async function renderForPrerender(app, request, options) {
|
|
4
|
+
const routeData = options?.routeData;
|
|
5
|
+
if (!options?.collectMetadata) {
|
|
6
|
+
return { response: await app.render(request, { routeData }) };
|
|
7
|
+
}
|
|
8
|
+
const { value: response, metadata } = await collectPrerenderMetadata(async () => {
|
|
9
|
+
const rendered = await app.render(request, { routeData });
|
|
10
|
+
const bytes = rendered.body === null ? null : await rendered.arrayBuffer();
|
|
11
|
+
const nullBody = bytes === null || NULL_BODY_STATUSES.includes(rendered.status);
|
|
12
|
+
return new Response(nullBody ? null : bytes, {
|
|
13
|
+
status: rendered.status,
|
|
14
|
+
statusText: rendered.statusText,
|
|
15
|
+
headers: rendered.headers
|
|
16
|
+
});
|
|
17
|
+
}, app.logger);
|
|
18
|
+
return { response, metadata };
|
|
19
|
+
}
|
|
20
|
+
export {
|
|
21
|
+
renderForPrerender
|
|
22
|
+
};
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { renderForPrerender } from "../app/prerender.js";
|
|
2
|
+
import { ensureAsyncRenderScope } from "../render-scope/node-scope.js";
|
|
1
3
|
import { StaticPaths } from "../../runtime/prerender/static-paths.js";
|
|
2
4
|
function createDefaultPrerenderer({
|
|
3
5
|
internals,
|
|
@@ -24,8 +26,9 @@ function createDefaultPrerenderer({
|
|
|
24
26
|
const staticPaths = new StaticPaths(prerenderer.app);
|
|
25
27
|
return staticPaths.getAll();
|
|
26
28
|
},
|
|
27
|
-
async render(request, { routeData }) {
|
|
28
|
-
|
|
29
|
+
async render(request, { routeData, collectMetadata }) {
|
|
30
|
+
if (collectMetadata) ensureAsyncRenderScope();
|
|
31
|
+
return renderForPrerender(prerenderer.app, request, { routeData, collectMetadata });
|
|
29
32
|
},
|
|
30
33
|
async teardown() {
|
|
31
34
|
}
|
|
@@ -23,6 +23,8 @@ interface RenderToPathPayload {
|
|
|
23
23
|
options: StaticBuildOptions;
|
|
24
24
|
routeToHeaders?: RouteToHeaders;
|
|
25
25
|
logger: AstroLogger;
|
|
26
|
+
/** Ask the prerenderer to collect and report per-render incremental metadata. */
|
|
27
|
+
collectMetadata?: boolean;
|
|
26
28
|
}
|
|
27
29
|
/**
|
|
28
30
|
* Renders a single prerendered path to an in-memory result.
|
|
@@ -48,5 +50,5 @@ interface RenderToPathPayload {
|
|
|
48
50
|
* not need to inspect the headers after the call can omit this.
|
|
49
51
|
* @param params.logger - Logger instance.
|
|
50
52
|
*/
|
|
51
|
-
export declare function renderPath({ prerenderer, pathname, route, options, routeToHeaders, logger, }: RenderToPathPayload): Promise<RenderPathResult | null>;
|
|
53
|
+
export declare function renderPath({ prerenderer, pathname, route, options, routeToHeaders, logger, collectMetadata, }: RenderToPathPayload): Promise<RenderPathResult | null>;
|
|
52
54
|
export {};
|
|
@@ -26,15 +26,9 @@ import { getRedirectLocationOrThrow } from "../redirects/index.js";
|
|
|
26
26
|
import { createRequest } from "../request.js";
|
|
27
27
|
import { redirectTemplate } from "../routing/3xx.js";
|
|
28
28
|
import { routeIsRedirect } from "../routing/helpers.js";
|
|
29
|
-
import { matchRoute } from "../routing/match.js";
|
|
30
29
|
import { getOutputFilename } from "../output-filename.js";
|
|
31
30
|
import { getOutFile, getOutFolder } from "./common.js";
|
|
32
31
|
import { createDefaultPrerenderer } from "./default-prerenderer.js";
|
|
33
|
-
import {
|
|
34
|
-
beginContentEntryCollection,
|
|
35
|
-
endContentEntryCollection
|
|
36
|
-
} from "./incremental-content-collector.js";
|
|
37
|
-
import { beginImageCollection, endImageCollection } from "./incremental-image-collector.js";
|
|
38
32
|
import { IncrementalBuildCache } from "./incremental.js";
|
|
39
33
|
import { computeConfigHash } from "./config-hash/index.js";
|
|
40
34
|
import { computeLockfileHash } from "./lockfile/index.js";
|
|
@@ -77,32 +71,25 @@ ${colors.bgGreen(colors.black(` ${verb} static routes `))}`);
|
|
|
77
71
|
let staticImageList = getStaticImageList();
|
|
78
72
|
let cache = null;
|
|
79
73
|
if (options.settings.config.experimental.incrementalBuild) {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
configHash,
|
|
94
|
-
lockfileHash,
|
|
95
|
-
keyDigest,
|
|
96
|
-
internals.contentEntryRenderHashes ?? /* @__PURE__ */ new Map(),
|
|
97
|
-
options.force
|
|
98
|
-
);
|
|
99
|
-
}
|
|
74
|
+
const [configHash, lockfileHash, keyDigest] = await Promise.all([
|
|
75
|
+
computeConfigHash(options.settings.config),
|
|
76
|
+
computeLockfileHash(fileURLToPath(options.settings.config.root)),
|
|
77
|
+
options.key.then(hashCryptoKey)
|
|
78
|
+
]);
|
|
79
|
+
cache = IncrementalBuildCache.load(
|
|
80
|
+
options.settings,
|
|
81
|
+
configHash,
|
|
82
|
+
lockfileHash,
|
|
83
|
+
keyDigest,
|
|
84
|
+
internals.contentEntryRenderHashes ?? /* @__PURE__ */ new Map(),
|
|
85
|
+
options.force
|
|
86
|
+
);
|
|
100
87
|
}
|
|
101
88
|
try {
|
|
102
89
|
const pathsWithRoutes = await prerenderer.getStaticPaths();
|
|
103
90
|
const hasI18nDomains = ssr && options.settings.config.i18n?.domains && Object.keys(options.settings.config.i18n.domains).length > 0;
|
|
104
91
|
const { config } = options.settings;
|
|
105
|
-
const builtPaths = /* @__PURE__ */ new
|
|
92
|
+
const builtPaths = /* @__PURE__ */ new Map();
|
|
106
93
|
const filteredPaths = [];
|
|
107
94
|
const fallbackPaths = [];
|
|
108
95
|
for (const pathWithRoute of pathsWithRoutes) {
|
|
@@ -115,33 +102,28 @@ ${colors.bgGreen(colors.black(` ${verb} static routes `))}`);
|
|
|
115
102
|
}
|
|
116
103
|
const normalized = removeTrailingForwardSlash(pathname);
|
|
117
104
|
if (!builtPaths.has(normalized)) {
|
|
118
|
-
builtPaths.
|
|
105
|
+
builtPaths.set(normalized, route);
|
|
119
106
|
} else {
|
|
120
|
-
const
|
|
121
|
-
if (
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
throw new AstroError({
|
|
127
|
-
...AstroErrorData.PrerenderRouteConflict,
|
|
128
|
-
message: AstroErrorData.PrerenderRouteConflict.message(
|
|
129
|
-
matchedRoute.route,
|
|
130
|
-
route.route,
|
|
131
|
-
normalized
|
|
132
|
-
),
|
|
133
|
-
hint: AstroErrorData.PrerenderRouteConflict.hint(matchedRoute.route, route.route)
|
|
134
|
-
});
|
|
135
|
-
} else if (config.prerenderConflictBehavior === "warn") {
|
|
136
|
-
const msg = AstroErrorData.PrerenderRouteConflict.message(
|
|
137
|
-
matchedRoute.route,
|
|
107
|
+
const winningRoute = builtPaths.get(normalized);
|
|
108
|
+
if (config.prerenderConflictBehavior === "error") {
|
|
109
|
+
throw new AstroError({
|
|
110
|
+
...AstroErrorData.PrerenderRouteConflict,
|
|
111
|
+
message: AstroErrorData.PrerenderRouteConflict.message(
|
|
112
|
+
winningRoute.route,
|
|
138
113
|
route.route,
|
|
139
114
|
normalized
|
|
140
|
-
)
|
|
141
|
-
|
|
142
|
-
}
|
|
143
|
-
|
|
115
|
+
),
|
|
116
|
+
hint: AstroErrorData.PrerenderRouteConflict.hint(winningRoute.route, route.route)
|
|
117
|
+
});
|
|
118
|
+
} else if (config.prerenderConflictBehavior === "warn") {
|
|
119
|
+
const msg = AstroErrorData.PrerenderRouteConflict.message(
|
|
120
|
+
winningRoute.route,
|
|
121
|
+
route.route,
|
|
122
|
+
normalized
|
|
123
|
+
);
|
|
124
|
+
logger.warn("build", msg);
|
|
144
125
|
}
|
|
126
|
+
continue;
|
|
145
127
|
}
|
|
146
128
|
const paths = route.type === "fallback" ? fallbackPaths : filteredPaths;
|
|
147
129
|
paths.push(pathWithRoute);
|
|
@@ -283,7 +265,8 @@ async function renderPath({
|
|
|
283
265
|
route,
|
|
284
266
|
options,
|
|
285
267
|
routeToHeaders = /* @__PURE__ */ new Map(),
|
|
286
|
-
logger
|
|
268
|
+
logger,
|
|
269
|
+
collectMetadata
|
|
287
270
|
}) {
|
|
288
271
|
const { config } = options.settings;
|
|
289
272
|
if (route.type === "fallback" && route.pathname !== "/") {
|
|
@@ -324,7 +307,7 @@ async function renderPath({
|
|
|
324
307
|
let metadata;
|
|
325
308
|
try {
|
|
326
309
|
const rendered = normalizePrerenderResult(
|
|
327
|
-
await prerenderer.render(request, { routeData: route })
|
|
310
|
+
await prerenderer.render(request, { routeData: route, collectMetadata })
|
|
328
311
|
);
|
|
329
312
|
response = rendered.response;
|
|
330
313
|
metadata = rendered.metadata;
|
|
@@ -431,22 +414,17 @@ async function generatePathWithPrerenderer(prerenderer, pathname, route, options
|
|
|
431
414
|
if (route.type === "page") {
|
|
432
415
|
addPageName(pathname, options);
|
|
433
416
|
}
|
|
434
|
-
if (cache) {
|
|
435
|
-
beginContentEntryCollection();
|
|
436
|
-
beginImageCollection();
|
|
437
|
-
}
|
|
438
417
|
const result = await renderPath({
|
|
439
418
|
prerenderer,
|
|
440
419
|
pathname,
|
|
441
420
|
route,
|
|
442
421
|
options,
|
|
443
422
|
routeToHeaders,
|
|
444
|
-
logger
|
|
423
|
+
logger,
|
|
424
|
+
collectMetadata: cache !== null
|
|
445
425
|
});
|
|
446
|
-
const
|
|
447
|
-
const
|
|
448
|
-
const contentEntryKeys = result?.metadata?.contentEntryKeys ?? collectedContentEntryKeys;
|
|
449
|
-
const staticImages = result?.metadata?.staticImages ?? collectedStaticImages;
|
|
426
|
+
const contentEntryKeys = result?.metadata?.contentEntryKeys;
|
|
427
|
+
const staticImages = result?.metadata?.staticImages;
|
|
450
428
|
const headers = cache ? [...routeToHeaders.get(pathname)?.headers ?? []] : void 0;
|
|
451
429
|
if (!result) {
|
|
452
430
|
logRenderTime(logger, timeStart, true);
|
|
@@ -454,7 +432,7 @@ async function generatePathWithPrerenderer(prerenderer, pathname, route, options
|
|
|
454
432
|
}
|
|
455
433
|
await nodeFs.promises.mkdir(result.outFolder, { recursive: true });
|
|
456
434
|
await nodeFs.promises.writeFile(result.outFile, result.body);
|
|
457
|
-
if (cache && cacheKey !== void 0) {
|
|
435
|
+
if (cache && cacheKey !== void 0 && result.metadata !== void 0) {
|
|
458
436
|
await cache.writeOutputFile(options.settings, relativeOutFile, result.body);
|
|
459
437
|
cache.record(
|
|
460
438
|
route.component,
|
|
@@ -31,7 +31,7 @@ import { makePageDataKey } from "./util.js";
|
|
|
31
31
|
import { cacheConfigToManifest } from "../../cache/utils.js";
|
|
32
32
|
import { sessionConfigToManifest } from "../../session/utils.js";
|
|
33
33
|
const MANIFEST_REPLACE = "@@ASTRO_MANIFEST_REPLACE@@";
|
|
34
|
-
const replaceExp = new RegExp(`['"]${MANIFEST_REPLACE}['"]`, "g");
|
|
34
|
+
const replaceExp = new RegExp(`['"\`]${MANIFEST_REPLACE}['"\`]`, "g");
|
|
35
35
|
async function manifestBuildPostHook(options, internals, {
|
|
36
36
|
chunks,
|
|
37
37
|
mutate
|
|
@@ -52,7 +52,18 @@ async function handleCache(state, next) {
|
|
|
52
52
|
{
|
|
53
53
|
request: state.request,
|
|
54
54
|
url: new URL(state.request.url),
|
|
55
|
-
waitUntil: state.renderOptions.waitUntil
|
|
55
|
+
waitUntil: state.renderOptions.waitUntil,
|
|
56
|
+
logger: {
|
|
57
|
+
info(msg) {
|
|
58
|
+
state.logger.info("cache", msg);
|
|
59
|
+
},
|
|
60
|
+
warn(msg) {
|
|
61
|
+
state.logger.warn("cache", msg);
|
|
62
|
+
},
|
|
63
|
+
error(msg) {
|
|
64
|
+
state.logger.error("cache", msg);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
56
67
|
},
|
|
57
68
|
async () => {
|
|
58
69
|
const res = await next();
|