astro 7.1.5 → 7.2.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.
Files changed (113) hide show
  1. package/dist/assets/build/generate.d.ts +8 -1
  2. package/dist/assets/build/generate.js +21 -1
  3. package/dist/assets/fonts/providers/index.js +12 -2
  4. package/dist/assets/types.d.ts +13 -0
  5. package/dist/assets/vite-plugin-assets.js +8 -0
  6. package/dist/cli/add/index.js +1 -1
  7. package/dist/cli/agent.d.ts +1 -0
  8. package/dist/cli/agent.js +11 -0
  9. package/dist/cli/dev/background.d.ts +3 -16
  10. package/dist/cli/dev/background.js +7 -93
  11. package/dist/cli/dev/index.d.ts +12 -0
  12. package/dist/cli/dev/index.js +30 -23
  13. package/dist/cli/dev/logs.js +2 -64
  14. package/dist/cli/dev/status.d.ts +2 -9
  15. package/dist/cli/dev/status.js +6 -24
  16. package/dist/cli/dev/stop.d.ts +2 -6
  17. package/dist/cli/dev/stop.js +6 -14
  18. package/dist/cli/infra/build-time-astro-version-provider.js +1 -1
  19. package/dist/cli/install-package.js +1 -1
  20. package/dist/cli/preview/index.js +77 -3
  21. package/dist/cli/server.d.ts +60 -0
  22. package/dist/cli/server.js +274 -0
  23. package/dist/content/consts.d.ts +4 -0
  24. package/dist/content/consts.js +8 -0
  25. package/dist/content/content-layer.js +3 -3
  26. package/dist/content/data-store-writer.d.ts +1 -1
  27. package/dist/content/data-store-writer.js +4 -3
  28. package/dist/content/data-store.d.ts +3 -3
  29. package/dist/content/data-store.js +3 -3
  30. package/dist/content/mutable-data-store.d.ts +3 -3
  31. package/dist/content/mutable-data-store.js +5 -5
  32. package/dist/content/paths.d.ts +2 -1
  33. package/dist/content/paths.js +11 -0
  34. package/dist/content/runtime.d.ts +2 -0
  35. package/dist/content/runtime.js +2 -0
  36. package/dist/content/types-generator.js +1 -0
  37. package/dist/content/vite-plugin-content-assets.js +6 -1
  38. package/dist/content/vite-plugin-content-imports.js +12 -3
  39. package/dist/content/vite-plugin-content-virtual-mod.js +69 -31
  40. package/dist/core/app/entrypoints/index.d.ts +2 -0
  41. package/dist/core/app/entrypoints/index.js +14 -0
  42. package/dist/core/build/config-hash/index.d.ts +8 -0
  43. package/dist/core/build/config-hash/index.js +23 -0
  44. package/dist/core/build/config-hash/input.d.ts +199 -0
  45. package/dist/core/build/config-hash/input.js +43 -0
  46. package/dist/core/build/generate.d.ts +3 -1
  47. package/dist/core/build/generate.js +204 -62
  48. package/dist/core/build/incremental-content-collector.d.ts +10 -0
  49. package/dist/core/build/incremental-content-collector.js +33 -0
  50. package/dist/core/build/incremental-image-collector.d.ts +11 -0
  51. package/dist/core/build/incremental-image-collector.js +32 -0
  52. package/dist/core/build/incremental-metadata.d.ts +9 -0
  53. package/dist/core/build/incremental-metadata.js +17 -0
  54. package/dist/core/build/incremental.d.ts +123 -0
  55. package/dist/core/build/incremental.js +178 -0
  56. package/dist/core/build/index.d.ts +3 -0
  57. package/dist/core/build/index.js +6 -2
  58. package/dist/core/build/internal.d.ts +19 -0
  59. package/dist/core/build/lockfile/finder.d.ts +22 -0
  60. package/dist/core/build/lockfile/finder.js +45 -0
  61. package/dist/core/build/lockfile/hasher.d.ts +16 -0
  62. package/dist/core/build/lockfile/hasher.js +46 -0
  63. package/dist/core/build/lockfile/index.d.ts +9 -0
  64. package/dist/core/build/lockfile/index.js +16 -0
  65. package/dist/core/build/plugins/index.js +3 -1
  66. package/dist/core/build/plugins/plugin-incremental.d.ts +14 -0
  67. package/dist/core/build/plugins/plugin-incremental.js +134 -0
  68. package/dist/core/build/types.d.ts +2 -0
  69. package/dist/core/config/schemas/base.d.ts +7 -6
  70. package/dist/core/config/schemas/base.js +9 -1
  71. package/dist/core/config/schemas/defaults.d.ts +1 -0
  72. package/dist/core/config/schemas/defaults.js +1 -0
  73. package/dist/core/config/schemas/relative.d.ts +19 -10
  74. package/dist/core/constants.js +1 -1
  75. package/dist/core/create-vite.js +2 -1
  76. package/dist/core/dev/dev.js +5 -4
  77. package/dist/core/dev/lockfile.d.ts +8 -7
  78. package/dist/core/dev/lockfile.js +13 -13
  79. package/dist/core/encryption.d.ts +6 -0
  80. package/dist/core/encryption.js +14 -2
  81. package/dist/core/errors/default-handler.js +4 -5
  82. package/dist/core/fetch/index.js +1 -1
  83. package/dist/core/logger/config.d.ts +1 -1
  84. package/dist/core/logger/load.d.ts +3 -1
  85. package/dist/core/logger/load.js +3 -3
  86. package/dist/core/logger/utils.d.ts +6 -4
  87. package/dist/core/logger/utils.js +13 -5
  88. package/dist/core/logger/vite-plugin.js +1 -1
  89. package/dist/core/messages/runtime.js +1 -1
  90. package/dist/core/preview/static-preview-server.d.ts +2 -0
  91. package/dist/core/preview/static-preview-server.js +1 -0
  92. package/dist/core/routing/handler.js +1 -1
  93. package/dist/core/session/config.d.ts +2 -2
  94. package/dist/core/session/config.js +2 -1
  95. package/dist/core/session/provider-disabled.d.ts +2 -0
  96. package/dist/core/session/provider-disabled.js +7 -0
  97. package/dist/core/session/provider.d.ts +1 -0
  98. package/dist/core/session/provider.js +4 -0
  99. package/dist/core/session/utils.js +3 -0
  100. package/dist/core/session/vite-plugin.d.ts +3 -0
  101. package/dist/core/session/vite-plugin.js +41 -6
  102. package/dist/core/sync/index.js +5 -4
  103. package/dist/integrations/hooks.js +3 -1
  104. package/dist/runtime/prerender/static-paths.js +5 -1
  105. package/dist/runtime/server/render/util.js +3 -0
  106. package/dist/types/public/common.d.ts +1 -0
  107. package/dist/types/public/config.d.ts +73 -7
  108. package/dist/types/public/integrations.d.ts +29 -2
  109. package/dist/types/public/preview.d.ts +2 -0
  110. package/dist/vite-plugin-config-alias/index.js +1 -0
  111. package/dist/vite-plugin-environment/index.js +4 -1
  112. package/dist/vite-plugin-hmr-reload/index.js +60 -18
  113. package/package.json +2 -3
@@ -1,14 +1,25 @@
1
1
  import colors from "piccolore";
2
+ import { pathToFileURL } from "node:url";
3
+ import { checkExistingServer, removeLockFile, writeLockFile } from "../../core/dev/lockfile.js";
4
+ import { resolveRoot } from "../../core/config/config.js";
2
5
  import { printHelp } from "../../core/messages/runtime.js";
3
6
  import previewServer from "../../core/preview/index.js";
4
- import { flagsToAstroInlineConfig } from "../flags.js";
7
+ import { isRunByAgent } from "../agent.js";
8
+ import { createLoggerFromFlags, flagsToAstroInlineConfig } from "../flags.js";
9
+ import { background, logs, previewServerCommand, status, stop } from "../server.js";
5
10
  async function preview({ flags }) {
6
11
  if (flags?.help || flags?.h) {
7
12
  printHelp({
8
13
  commandName: "astro preview",
9
- usage: "[...flags]",
14
+ usage: "[command] [...flags]",
10
15
  tables: {
16
+ Commands: [
17
+ ["stop", "Stop a running background preview server."],
18
+ ["status", "Check if a preview server is running."],
19
+ ["logs [--follow]", "View logs from a background preview server."]
20
+ ],
11
21
  Flags: [
22
+ ["--background", "Start the preview server as a background process."],
12
23
  ["--port", `Specify which port to run on. Defaults to 4321.`],
13
24
  ["--host", `Listen on all addresses, including LAN and public addresses.`],
14
25
  ["--host <custom-address>", `Expose on a network IP address at <custom-address>`],
@@ -26,8 +37,71 @@ async function preview({ flags }) {
26
37
  });
27
38
  return;
28
39
  }
40
+ const agentDetected = !process.env.ASTRO_PREVIEW_BACKGROUND && isRunByAgent();
41
+ if (agentDetected) {
42
+ flags.json = true;
43
+ }
44
+ const logger = createLoggerFromFlags(flags);
45
+ const subcommand = flags._[3]?.toString();
46
+ if (subcommand === "stop") {
47
+ await stop({ flags, logger, config: previewServerCommand });
48
+ return;
49
+ }
50
+ if (subcommand === "status") {
51
+ await status({ flags, logger, config: previewServerCommand });
52
+ return;
53
+ }
54
+ if (subcommand === "logs") {
55
+ await logs({ flags, logger, config: previewServerCommand });
56
+ return;
57
+ }
58
+ if (flags.background || agentDetected) {
59
+ await background({ flags, logger, config: previewServerCommand });
60
+ return;
61
+ }
62
+ if (subcommand) {
63
+ logger.error(
64
+ "SKIP_FORMAT",
65
+ `Unknown command: astro preview ${subcommand}
66
+
67
+ Run \`astro preview --help\` to see available commands.`
68
+ );
69
+ process.exit(1);
70
+ }
71
+ const root = pathToFileURL(resolveRoot(flags.root) + "/");
72
+ const existingServer = checkExistingServer(root, "preview");
73
+ if (existingServer) {
74
+ const message = [
75
+ "Another astro preview server is already running.",
76
+ "",
77
+ ` URL: ${existingServer.url}`,
78
+ ` PID: ${existingServer.pid}`,
79
+ "",
80
+ `Run \`astro preview stop\` to stop it, or use \`astro preview --force\` to replace it.`
81
+ ].join("\n");
82
+ throw new Error(message);
83
+ }
29
84
  const inlineConfig = flagsToAstroInlineConfig(flags);
30
- return await previewServer(inlineConfig);
85
+ const server = await previewServer(inlineConfig);
86
+ const serverUrl = server.urls?.local[0] ? new URL(server.urls.local[0]).origin : `http://${server.host ?? "localhost"}:${server.port}`;
87
+ writeLockFile(
88
+ root,
89
+ {
90
+ pid: process.pid,
91
+ port: server.port,
92
+ url: serverUrl,
93
+ urls: server.urls,
94
+ background: !!process.env.ASTRO_PREVIEW_BACKGROUND,
95
+ startedAt: (/* @__PURE__ */ new Date()).toISOString()
96
+ },
97
+ "preview"
98
+ );
99
+ const originalStop = server.stop.bind(server);
100
+ server.stop = async () => {
101
+ removeLockFile(root, "preview");
102
+ await originalStop();
103
+ };
104
+ return server;
31
105
  }
32
106
  export {
33
107
  preview
@@ -0,0 +1,60 @@
1
+ import { type LockFileData, type ServerCommand } from '../core/dev/lockfile.js';
2
+ import type { AstroLogger } from '../core/logger/core.js';
3
+ import type { Flags } from './flags.js';
4
+ export interface BackgroundCommandConfig {
5
+ command: ServerCommand;
6
+ displayName: string;
7
+ envVar: 'ASTRO_DEV_BACKGROUND' | 'ASTRO_PREVIEW_BACKGROUND';
8
+ }
9
+ export declare const devServerCommand: BackgroundCommandConfig;
10
+ export declare const previewServerCommand: BackgroundCommandConfig;
11
+ export interface BackgroundResult {
12
+ pid: number;
13
+ url: string;
14
+ existing?: boolean;
15
+ }
16
+ export interface BackgroundErrorResult {
17
+ error: string;
18
+ message: string;
19
+ }
20
+ export interface StopResult {
21
+ stopped: boolean;
22
+ pid?: number;
23
+ reason?: string;
24
+ }
25
+ export interface StatusResult {
26
+ running: boolean;
27
+ pid?: number;
28
+ url?: string;
29
+ port?: number;
30
+ background?: boolean;
31
+ uptime?: number;
32
+ }
33
+ export declare function formatBackgroundOutput(result: BackgroundResult | BackgroundErrorResult): string;
34
+ export declare function formatStopOutput(result: StopResult): string;
35
+ export declare function formatStatusOutput(result: StatusResult): string;
36
+ export declare function formatServerRunningMessage(data: LockFileData, config: BackgroundCommandConfig, { existing }?: {
37
+ existing?: boolean;
38
+ }): string;
39
+ export declare function buildBackgroundArgs(command: ServerCommand, flags: Flags): string[];
40
+ export declare function stopExistingServer(root: URL, command: ServerCommand, pid: number): Promise<void>;
41
+ export declare function background({ flags, logger, config, }: {
42
+ flags: Flags;
43
+ logger: AstroLogger;
44
+ config: BackgroundCommandConfig;
45
+ }): Promise<void>;
46
+ export declare function stop({ flags, logger, config, }: {
47
+ flags: Flags;
48
+ logger: AstroLogger;
49
+ config: BackgroundCommandConfig;
50
+ }): Promise<void>;
51
+ export declare function status({ flags, logger, config, }: {
52
+ flags: Flags;
53
+ logger: AstroLogger;
54
+ config: BackgroundCommandConfig;
55
+ }): Promise<void>;
56
+ export declare function logs({ flags, logger, config, }: {
57
+ flags: Flags;
58
+ logger: AstroLogger;
59
+ config: BackgroundCommandConfig;
60
+ }): Promise<void>;
@@ -0,0 +1,274 @@
1
+ import { spawn } from "node:child_process";
2
+ import {
3
+ createReadStream,
4
+ existsSync,
5
+ mkdirSync,
6
+ openSync,
7
+ readFileSync,
8
+ statSync,
9
+ watch
10
+ } from "node:fs";
11
+ import { createRequire } from "node:module";
12
+ import { dirname, resolve } from "node:path";
13
+ import { fileURLToPath, pathToFileURL } from "node:url";
14
+ import { resolveRoot } from "../core/config/config.js";
15
+ import {
16
+ GRACEFUL_SHUTDOWN_TIMEOUT,
17
+ checkExistingServer,
18
+ getLogFileURL,
19
+ isProcessAlive,
20
+ readLockFile,
21
+ removeLockFile
22
+ } from "../core/dev/lockfile.js";
23
+ const require2 = createRequire(import.meta.url);
24
+ const devServerCommand = {
25
+ command: "dev",
26
+ displayName: "Dev server",
27
+ envVar: "ASTRO_DEV_BACKGROUND"
28
+ };
29
+ const previewServerCommand = {
30
+ command: "preview",
31
+ displayName: "Preview server",
32
+ envVar: "ASTRO_PREVIEW_BACKGROUND"
33
+ };
34
+ function formatBackgroundOutput(result) {
35
+ return JSON.stringify(result);
36
+ }
37
+ function formatStopOutput(result) {
38
+ return JSON.stringify(result);
39
+ }
40
+ function formatStatusOutput(result) {
41
+ return JSON.stringify(result);
42
+ }
43
+ function formatServerRunningMessage(data, config, { existing = false } = {}) {
44
+ const command = `astro ${config.command}`;
45
+ const lines = [
46
+ `${config.displayName} ${existing ? "already running" : "running"} at ${data.url} (pid ${data.pid})`
47
+ ];
48
+ if (data.urls && data.urls.network.length > 0) {
49
+ lines.push(" Network:");
50
+ for (const url of data.urls.network) {
51
+ lines.push(` ${url}`);
52
+ }
53
+ }
54
+ lines.push(
55
+ ` Stop: ${command} stop`,
56
+ ` Status: ${command} status`,
57
+ ` Logs: ${command} logs`
58
+ );
59
+ return lines.join("\n");
60
+ }
61
+ function getRootURL(flags) {
62
+ return pathToFileURL(resolveRoot(flags.root) + "/");
63
+ }
64
+ function buildBackgroundArgs(command, flags) {
65
+ const args = [command];
66
+ if (flags.port) args.push("--port", String(flags.port));
67
+ if (flags.host != null) {
68
+ if (typeof flags.host === "string") {
69
+ args.push("--host", flags.host);
70
+ } else {
71
+ args.push("--host");
72
+ }
73
+ }
74
+ if (flags.config) args.push("--config", String(flags.config));
75
+ if (flags.root) args.push("--root", String(flags.root));
76
+ if (flags.allowedHosts) args.push("--allowed-hosts", String(flags.allowedHosts));
77
+ if (flags.json) args.push("--json");
78
+ return args;
79
+ }
80
+ async function stopExistingServer(root, command, pid) {
81
+ try {
82
+ process.kill(pid, "SIGTERM");
83
+ } catch {
84
+ }
85
+ const deadline = Date.now() + GRACEFUL_SHUTDOWN_TIMEOUT;
86
+ while (Date.now() < deadline) {
87
+ if (!isProcessAlive(pid)) break;
88
+ await new Promise((r) => setTimeout(r, 100));
89
+ }
90
+ if (isProcessAlive(pid)) {
91
+ try {
92
+ process.kill(pid, "SIGKILL");
93
+ } catch {
94
+ }
95
+ }
96
+ removeLockFile(root, command);
97
+ }
98
+ async function background({
99
+ flags,
100
+ logger,
101
+ config
102
+ }) {
103
+ const root = getRootURL(flags);
104
+ const existing = checkExistingServer(root, config.command);
105
+ if (existing && !flags.force) {
106
+ logger.info("SKIP_FORMAT", formatServerRunningMessage(existing, config, { existing: true }));
107
+ return;
108
+ }
109
+ if (existing && flags.force) {
110
+ await stopExistingServer(root, config.command, existing.pid);
111
+ }
112
+ const args = buildBackgroundArgs(config.command, flags);
113
+ const logFileURL = getLogFileURL(root, config.command);
114
+ const logFilePath = fileURLToPath(logFileURL);
115
+ const dotAstroDir = fileURLToPath(new URL(".astro/", root));
116
+ if (!existsSync(dotAstroDir)) {
117
+ mkdirSync(dotAstroDir, { recursive: true });
118
+ }
119
+ const logFd = openSync(logFilePath, "w");
120
+ const rootPath = fileURLToPath(root);
121
+ const astroBin = resolve(dirname(require2.resolve("astro/package.json")), "bin", "astro.mjs");
122
+ const child = spawn(process.execPath, [astroBin, ...args], {
123
+ detached: true,
124
+ windowsHide: true,
125
+ stdio: ["ignore", logFd, logFd],
126
+ cwd: rootPath,
127
+ env: { ...process.env, [config.envVar]: "1" }
128
+ });
129
+ child.unref();
130
+ const childPid = child.pid;
131
+ if (!childPid) {
132
+ logger.error("SKIP_FORMAT", `Failed to spawn background ${config.command} server process.`);
133
+ process.exit(1);
134
+ }
135
+ const timeout = 3e4;
136
+ const deadline = Date.now() + timeout;
137
+ while (Date.now() < deadline) {
138
+ if (!isProcessAlive(childPid)) {
139
+ logger.error("SKIP_FORMAT", `${config.displayName} process exited before becoming ready.`);
140
+ process.exit(1);
141
+ }
142
+ const lockData = readLockFile(root, config.command);
143
+ if (lockData && lockData.pid === childPid) {
144
+ logger.info("SKIP_FORMAT", formatServerRunningMessage(lockData, config));
145
+ return;
146
+ }
147
+ await new Promise((r) => setTimeout(r, 200));
148
+ }
149
+ try {
150
+ process.kill(childPid, "SIGTERM");
151
+ } catch {
152
+ }
153
+ removeLockFile(root, config.command);
154
+ logger.error("SKIP_FORMAT", `${config.displayName} failed to start within ${timeout / 1e3}s.`);
155
+ process.exit(1);
156
+ }
157
+ async function stop({
158
+ flags,
159
+ logger,
160
+ config
161
+ }) {
162
+ const root = getRootURL(flags);
163
+ const existing = checkExistingServer(root, config.command);
164
+ if (!existing) {
165
+ logger.info("SKIP_FORMAT", `No ${config.command} server is running.`);
166
+ return;
167
+ }
168
+ await stopExistingServer(root, config.command, existing.pid);
169
+ logger.info("SKIP_FORMAT", `Stopped ${config.command} server (pid ${existing.pid}).`);
170
+ }
171
+ async function status({
172
+ flags,
173
+ logger,
174
+ config
175
+ }) {
176
+ const root = getRootURL(flags);
177
+ const existing = checkExistingServer(root, config.command);
178
+ if (!existing) {
179
+ logger.info("SKIP_FORMAT", `No ${config.command} server is running.`);
180
+ return;
181
+ }
182
+ const startedAt = new Date(existing.startedAt).getTime();
183
+ const uptime = Math.floor((Date.now() - startedAt) / 1e3);
184
+ const lines = [
185
+ `${config.displayName} running at ${existing.url} (pid ${existing.pid}, uptime ${uptime}s${existing.background ? ", background" : ""})`
186
+ ];
187
+ if (existing.urls && existing.urls.network.length > 0) {
188
+ lines.push(" Network:");
189
+ for (const url of existing.urls.network) {
190
+ lines.push(` ${url}`);
191
+ }
192
+ }
193
+ logger.info("SKIP_FORMAT", lines.join("\n"));
194
+ }
195
+ async function logs({
196
+ flags,
197
+ logger,
198
+ config
199
+ }) {
200
+ const root = getRootURL(flags);
201
+ const existing = checkExistingServer(root, config.command);
202
+ if (!existing) {
203
+ logger.error("SKIP_FORMAT", `No ${config.command} server is running.`);
204
+ process.exit(1);
205
+ }
206
+ if (!existing.background) {
207
+ logger.error(
208
+ "SKIP_FORMAT",
209
+ `The running ${config.command} server was not started with \`astro ${config.command} --background\`. View logs in the terminal where it was started.`
210
+ );
211
+ process.exit(1);
212
+ }
213
+ const logFileURL = getLogFileURL(root, config.command);
214
+ const logFilePath = fileURLToPath(logFileURL);
215
+ if (!existsSync(logFilePath)) {
216
+ logger.error("SKIP_FORMAT", "No log file found.");
217
+ process.exit(1);
218
+ }
219
+ const follow = flags.follow || flags.f;
220
+ if (!follow) {
221
+ const content = readFileSync(logFilePath, "utf-8");
222
+ process.stdout.write(content);
223
+ return;
224
+ }
225
+ let offset = statSync(logFilePath).size;
226
+ if (offset > 0) {
227
+ const content = readFileSync(logFilePath, "utf-8");
228
+ process.stdout.write(content);
229
+ }
230
+ const watcher = watch(logFilePath, () => {
231
+ const currentSize = statSync(logFilePath).size;
232
+ if (currentSize > offset) {
233
+ const stream = createReadStream(logFilePath, { start: offset, encoding: "utf-8" });
234
+ stream.on("data", (chunk) => process.stdout.write(chunk));
235
+ stream.on("end", () => {
236
+ offset = currentSize;
237
+ });
238
+ }
239
+ });
240
+ const aliveCheck = setInterval(() => {
241
+ if (!isProcessAlive(existing.pid)) {
242
+ const currentSize = statSync(logFilePath).size;
243
+ if (currentSize > offset) {
244
+ const remaining = readFileSync(logFilePath, { encoding: "utf-8" }).slice(offset);
245
+ process.stdout.write(remaining);
246
+ }
247
+ watcher.close();
248
+ clearInterval(aliveCheck);
249
+ }
250
+ }, 1e3);
251
+ const cleanup = () => {
252
+ watcher.close();
253
+ clearInterval(aliveCheck);
254
+ process.exit(0);
255
+ };
256
+ process.on("SIGINT", cleanup);
257
+ process.on("SIGTERM", cleanup);
258
+ await new Promise(() => {
259
+ });
260
+ }
261
+ export {
262
+ background,
263
+ buildBackgroundArgs,
264
+ devServerCommand,
265
+ formatBackgroundOutput,
266
+ formatServerRunningMessage,
267
+ formatStatusOutput,
268
+ formatStopOutput,
269
+ logs,
270
+ previewServerCommand,
271
+ status,
272
+ stop,
273
+ stopExistingServer
274
+ };
@@ -28,3 +28,7 @@ export declare const COLLECTIONS_MANIFEST_FILE = "collections/collections.json";
28
28
  export declare const COLLECTIONS_DIR = "collections/";
29
29
  export declare const CONTENT_LAYER_TYPE = "content_layer";
30
30
  export declare const LIVE_CONTENT_TYPE = "live";
31
+ export declare const DATA_STORE_CHUNK_VIRTUAL_ID_PREFIX = "astro:data-layer-content-chunk:";
32
+ export declare const RESOLVED_DATA_STORE_CHUNK_VIRTUAL_ID_PREFIX = "\0astro:data-layer-content-chunk:";
33
+ export declare const RESOLVED_DATA_STORE_CHUNK_VIRTUAL_ID_SUFFIX = ".mjs";
34
+ export declare const DATA_STORE_CHUNK_FILE_NAME_PATTERN: RegExp;
@@ -35,6 +35,10 @@ const COLLECTIONS_MANIFEST_FILE = "collections/collections.json";
35
35
  const COLLECTIONS_DIR = "collections/";
36
36
  const CONTENT_LAYER_TYPE = "content_layer";
37
37
  const LIVE_CONTENT_TYPE = "live";
38
+ const DATA_STORE_CHUNK_VIRTUAL_ID_PREFIX = `${DATA_STORE_VIRTUAL_ID}-chunk:`;
39
+ const RESOLVED_DATA_STORE_CHUNK_VIRTUAL_ID_PREFIX = `\0${DATA_STORE_CHUNK_VIRTUAL_ID_PREFIX}`;
40
+ const RESOLVED_DATA_STORE_CHUNK_VIRTUAL_ID_SUFFIX = ".mjs";
41
+ const DATA_STORE_CHUNK_FILE_NAME_PATTERN = /^[a-f0-9]{16}\.txt$/;
38
42
  export {
39
43
  ASSET_IMPORTS_FILE,
40
44
  ASSET_IMPORTS_RESOLVED_STUB_ID,
@@ -49,6 +53,8 @@ export {
49
53
  CONTENT_RENDER_FLAG,
50
54
  CONTENT_TYPES_FILE,
51
55
  DATA_FLAG,
56
+ DATA_STORE_CHUNK_FILE_NAME_PATTERN,
57
+ DATA_STORE_CHUNK_VIRTUAL_ID_PREFIX,
52
58
  DATA_STORE_DIR,
53
59
  DATA_STORE_FILE,
54
60
  DATA_STORE_MANIFEST_FILE,
@@ -62,6 +68,8 @@ export {
62
68
  MODULES_MJS_VIRTUAL_ID,
63
69
  PROPAGATED_ASSET_FLAG,
64
70
  PROPAGATED_ASSET_QUERY_PARAM,
71
+ RESOLVED_DATA_STORE_CHUNK_VIRTUAL_ID_PREFIX,
72
+ RESOLVED_DATA_STORE_CHUNK_VIRTUAL_ID_SUFFIX,
65
73
  RESOLVED_DATA_STORE_VIRTUAL_ID,
66
74
  RESOLVED_VIRTUAL_MODULE_ID,
67
75
  STYLES_PLACEHOLDER,
@@ -196,7 +196,7 @@ ${contentConfig.error.message}`
196
196
  logger.info("Content config changed");
197
197
  shouldClear = true;
198
198
  }
199
- if (previousAstroVersion && previousAstroVersion !== "7.1.5") {
199
+ if (previousAstroVersion && previousAstroVersion !== "7.2.0") {
200
200
  logger.info("Astro version changed");
201
201
  shouldClear = true;
202
202
  }
@@ -204,8 +204,8 @@ ${contentConfig.error.message}`
204
204
  logger.info("Clearing content store");
205
205
  this.#store.clearAll();
206
206
  }
207
- if ("7.1.5") {
208
- this.#store.metaStore().set("astro-version", "7.1.5");
207
+ if ("7.2.0") {
208
+ this.#store.metaStore().set("astro-version", "7.2.0");
209
209
  }
210
210
  if (currentConfigDigest) {
211
211
  this.#store.metaStore().set("content-config-digest", currentConfigDigest);
@@ -62,6 +62,6 @@ export declare class FileWriter implements DataStoreWriter {
62
62
  */
63
63
  export declare class ChunkedWriter implements DataStoreWriter {
64
64
  #private;
65
- constructor(dir: URL);
65
+ constructor(dir: URL, chunkSize: number);
66
66
  write(collections: Map<string, Map<string, any>>): Promise<void>;
67
67
  }
@@ -3,7 +3,6 @@ import * as devalue from "devalue";
3
3
  import xxhash, {} from "xxhash-wasm";
4
4
  import { emptyDir } from "../core/fs/index.js";
5
5
  import { DATA_STORE_MANIFEST_FILE } from "./consts.js";
6
- const CHUNK_SIZE_LIMIT = 20 * 1024 * 1024;
7
6
  function sortCollections(collections) {
8
7
  return new Map(
9
8
  [...collections.entries()].sort(([a], [b]) => a.localeCompare(b)).map(([key, collection]) => [
@@ -57,10 +56,12 @@ class FileWriter {
57
56
  class ChunkedWriter {
58
57
  #dir;
59
58
  #manifestFile;
59
+ #chunkSize;
60
60
  #hasher;
61
- constructor(dir) {
61
+ constructor(dir, chunkSize) {
62
62
  this.#dir = dir;
63
63
  this.#manifestFile = new URL(`./${DATA_STORE_MANIFEST_FILE}`, dir);
64
+ this.#chunkSize = chunkSize;
64
65
  }
65
66
  async write(collections) {
66
67
  if (!this.#hasher) {
@@ -72,7 +73,7 @@ class ChunkedWriter {
72
73
  for (const [collectionName, entries] of sortCollections(collections)) {
73
74
  const stringified = devalue.stringify(entries);
74
75
  const parts = [];
75
- for (const part of chunkString(stringified, CHUNK_SIZE_LIMIT)) {
76
+ for (const part of chunkString(stringified, this.#chunkSize)) {
76
77
  const fileName = `${h64ToString(part)}.txt`;
77
78
  await writeFileAtomic(new URL(`./${fileName}`, this.#dir), part);
78
79
  parts.push(fileName);
@@ -51,9 +51,9 @@ export declare class ImmutableDataStore {
51
51
  * names have already been swapped for their contents.
52
52
  *
53
53
  * Each collection maps to a list of parts. A part is either a raw string
54
- * (when the store is loaded from disk) or an ESM namespace from a `?raw`
55
- * import (`{ default: string }`, when emitted into the virtual module at
56
- * runtime). A collection's parts are concatenated back into the exact
54
+ * (when the store is loaded from disk) or an ESM namespace from a virtual
55
+ * chunk import (`{ default: string }`, when emitted at runtime). A collection's
56
+ * parts are concatenated back into the exact
57
57
  * serialized string, then parsed with devalue. This is the inverse of
58
58
  * {@link import('./data-store-writer.js').ChunkedWriter} and stays free of
59
59
  * Node built-ins so it can run at runtime.
@@ -38,9 +38,9 @@ class ImmutableDataStore {
38
38
  * names have already been swapped for their contents.
39
39
  *
40
40
  * Each collection maps to a list of parts. A part is either a raw string
41
- * (when the store is loaded from disk) or an ESM namespace from a `?raw`
42
- * import (`{ default: string }`, when emitted into the virtual module at
43
- * runtime). A collection's parts are concatenated back into the exact
41
+ * (when the store is loaded from disk) or an ESM namespace from a virtual
42
+ * chunk import (`{ default: string }`, when emitted at runtime). A collection's
43
+ * parts are concatenated back into the exact
44
44
  * serialized string, then parsed with devalue. This is the inverse of
45
45
  * {@link import('./data-store-writer.js').ChunkedWriter} and stays free of
46
46
  * Node built-ins so it can run at runtime.
@@ -36,13 +36,13 @@ export declare class MutableDataStore extends ImmutableDataStore {
36
36
  static fromString(data: string): Promise<MutableDataStore>;
37
37
  static fromFile(filePath: string | URL): Promise<MutableDataStore>;
38
38
  /**
39
- * Loads a MutableDataStore from a chunked store directory (experimental
40
- * `collectionStorage: 'chunked'`), reading the manifest and its referenced parts.
39
+ * Loads a MutableDataStore from a chunked store directory, reading the manifest
40
+ * and its referenced parts.
41
41
  * If the directory has no manifest yet (fresh build) it starts empty. If the
42
42
  * manifest exists but can't be read (corrupt cache), it warns and starts
43
43
  * empty so loaders rebuild it, rather than failing the sync.
44
44
  */
45
- static fromDir(dirPath: URL): Promise<MutableDataStore>;
45
+ static fromDir(dirPath: URL, chunkSize: number): Promise<MutableDataStore>;
46
46
  }
47
47
  export interface DataStore {
48
48
  get: <TData extends Record<string, unknown> = Record<string, unknown>>(key: string) => DataEntry<TData> | undefined;
@@ -412,13 +412,13 @@ ${lines.join(",\n")}]);
412
412
  return store;
413
413
  }
414
414
  /**
415
- * Loads a MutableDataStore from a chunked store directory (experimental
416
- * `collectionStorage: 'chunked'`), reading the manifest and its referenced parts.
415
+ * Loads a MutableDataStore from a chunked store directory, reading the manifest
416
+ * and its referenced parts.
417
417
  * If the directory has no manifest yet (fresh build) it starts empty. If the
418
418
  * manifest exists but can't be read (corrupt cache), it warns and starts
419
419
  * empty so loaders rebuild it, rather than failing the sync.
420
420
  */
421
- static async fromDir(dirPath) {
421
+ static async fromDir(dirPath, chunkSize) {
422
422
  const manifestFile = new URL(`./${DATA_STORE_MANIFEST_FILE}`, dirPath);
423
423
  if (existsSync(manifestFile)) {
424
424
  try {
@@ -434,7 +434,7 @@ ${lines.join(",\n")}]);
434
434
  }
435
435
  const map = ImmutableDataStore.manifestToMap(expanded);
436
436
  const store2 = await MutableDataStore.fromMap(map);
437
- store2.#writer = new ChunkedWriter(dirPath);
437
+ store2.#writer = new ChunkedWriter(dirPath, chunkSize);
438
438
  return store2;
439
439
  } catch (err) {
440
440
  console.warn(
@@ -445,7 +445,7 @@ ${lines.join(",\n")}]);
445
445
  }
446
446
  await fs.mkdir(dirPath, { recursive: true });
447
447
  const store = new MutableDataStore();
448
- store.#writer = new ChunkedWriter(dirPath);
448
+ store.#writer = new ChunkedWriter(dirPath, chunkSize);
449
449
  return store;
450
450
  }
451
451
  }
@@ -5,9 +5,10 @@ import type { AstroSettings } from '../types/astro.js';
5
5
  * In production, it's in the cache directory so that it's preserved between builds.
6
6
  */
7
7
  export declare function getDataStoreFile(settings: AstroSettings, isDev: boolean): URL;
8
+ export declare function getDataStoreChunkSize(settings: AstroSettings): number | undefined;
8
9
  /**
9
10
  * Get the path to the data store directory, used when the store is split across
10
- * multiple files (experimental `collectionStorage: 'chunked'`).
11
+ * multiple files.
11
12
  * During development, this is in the `.astro` directory so that the Vite watcher can see it.
12
13
  * In production, it's in the cache directory so that it's preserved between builds.
13
14
  */
@@ -2,10 +2,21 @@ import { DATA_STORE_DIR, DATA_STORE_FILE } from "./consts.js";
2
2
  function getDataStoreFile(settings, isDev) {
3
3
  return new URL(DATA_STORE_FILE, isDev ? settings.dotAstroDir : settings.config.cacheDir);
4
4
  }
5
+ function getDataStoreChunkSize(settings) {
6
+ const storage = settings.config.experimental.collectionStorage;
7
+ if (storage === void 0 || storage === "single-file") {
8
+ return void 0;
9
+ }
10
+ if (storage === "chunked") {
11
+ return 20 * 1024 * 1024;
12
+ }
13
+ return storage.chunkSize;
14
+ }
5
15
  function getDataStoreDir(settings, isDev) {
6
16
  return new URL(DATA_STORE_DIR, isDev ? settings.dotAstroDir : settings.config.cacheDir);
7
17
  }
8
18
  export {
19
+ getDataStoreChunkSize,
9
20
  getDataStoreDir,
10
21
  getDataStoreFile
11
22
  };
@@ -33,12 +33,14 @@ type ContentEntryResult = {
33
33
  body: string;
34
34
  collection: string;
35
35
  data: Record<string, any>;
36
+ digest?: string | number;
36
37
  render(): Promise<RenderResult>;
37
38
  };
38
39
  type DataEntryResult = {
39
40
  id: string;
40
41
  collection: string;
41
42
  data: Record<string, any>;
43
+ digest?: string | number;
42
44
  };
43
45
  type EntryLookupObject = {
44
46
  collection: string;
@@ -3,6 +3,7 @@ import { forEach } from "neotraverse";
3
3
  import * as z from "zod/v4";
4
4
  import { createSvgComponent } from "../assets/runtime.js";
5
5
  import { imageSrcToImportId } from "../assets/utils/resolveImports.js";
6
+ import { recordContentEntryRender } from "../core/build/incremental-content-collector.js";
6
7
  import { AstroError, AstroErrorData } from "../core/errors/index.js";
7
8
  import { isRemotePath, prependForwardSlash } from "../core/path.js";
8
9
  import {
@@ -385,6 +386,7 @@ async function renderEntry(entry) {
385
386
  if (!entry) {
386
387
  throw new AstroError(AstroErrorData.RenderUndefinedEntryError);
387
388
  }
389
+ recordContentEntryRender(entry.filePath);
388
390
  if (entry.deferredRender) {
389
391
  try {
390
392
  const { default: contentModules } = await import("astro:content-module-imports");