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.
- package/dist/assets/build/generate.d.ts +8 -1
- package/dist/assets/build/generate.js +21 -1
- package/dist/assets/fonts/providers/index.js +12 -2
- package/dist/assets/types.d.ts +13 -0
- package/dist/assets/vite-plugin-assets.js +8 -0
- package/dist/cli/add/index.js +1 -1
- package/dist/cli/agent.d.ts +1 -0
- package/dist/cli/agent.js +11 -0
- package/dist/cli/dev/background.d.ts +3 -16
- package/dist/cli/dev/background.js +7 -93
- package/dist/cli/dev/index.d.ts +12 -0
- package/dist/cli/dev/index.js +30 -23
- package/dist/cli/dev/logs.js +2 -64
- package/dist/cli/dev/status.d.ts +2 -9
- package/dist/cli/dev/status.js +6 -24
- package/dist/cli/dev/stop.d.ts +2 -6
- package/dist/cli/dev/stop.js +6 -14
- package/dist/cli/infra/build-time-astro-version-provider.js +1 -1
- package/dist/cli/install-package.js +1 -1
- package/dist/cli/preview/index.js +77 -3
- package/dist/cli/server.d.ts +60 -0
- package/dist/cli/server.js +274 -0
- package/dist/content/consts.d.ts +4 -0
- package/dist/content/consts.js +8 -0
- package/dist/content/content-layer.js +3 -3
- package/dist/content/data-store-writer.d.ts +1 -1
- package/dist/content/data-store-writer.js +4 -3
- package/dist/content/data-store.d.ts +3 -3
- package/dist/content/data-store.js +3 -3
- package/dist/content/mutable-data-store.d.ts +3 -3
- package/dist/content/mutable-data-store.js +5 -5
- package/dist/content/paths.d.ts +2 -1
- package/dist/content/paths.js +11 -0
- package/dist/content/runtime.d.ts +2 -0
- package/dist/content/runtime.js +2 -0
- package/dist/content/types-generator.js +1 -0
- package/dist/content/vite-plugin-content-assets.js +6 -1
- package/dist/content/vite-plugin-content-imports.js +12 -3
- package/dist/content/vite-plugin-content-virtual-mod.js +69 -31
- package/dist/core/app/entrypoints/index.d.ts +2 -0
- package/dist/core/app/entrypoints/index.js +14 -0
- package/dist/core/build/config-hash/index.d.ts +8 -0
- package/dist/core/build/config-hash/index.js +23 -0
- package/dist/core/build/config-hash/input.d.ts +199 -0
- package/dist/core/build/config-hash/input.js +43 -0
- package/dist/core/build/generate.d.ts +3 -1
- package/dist/core/build/generate.js +204 -62
- package/dist/core/build/incremental-content-collector.d.ts +10 -0
- package/dist/core/build/incremental-content-collector.js +33 -0
- package/dist/core/build/incremental-image-collector.d.ts +11 -0
- package/dist/core/build/incremental-image-collector.js +32 -0
- package/dist/core/build/incremental-metadata.d.ts +9 -0
- package/dist/core/build/incremental-metadata.js +17 -0
- package/dist/core/build/incremental.d.ts +123 -0
- package/dist/core/build/incremental.js +178 -0
- package/dist/core/build/index.d.ts +3 -0
- package/dist/core/build/index.js +6 -2
- package/dist/core/build/internal.d.ts +19 -0
- package/dist/core/build/lockfile/finder.d.ts +22 -0
- package/dist/core/build/lockfile/finder.js +45 -0
- package/dist/core/build/lockfile/hasher.d.ts +16 -0
- package/dist/core/build/lockfile/hasher.js +46 -0
- package/dist/core/build/lockfile/index.d.ts +9 -0
- package/dist/core/build/lockfile/index.js +16 -0
- package/dist/core/build/plugins/index.js +3 -1
- package/dist/core/build/plugins/plugin-incremental.d.ts +14 -0
- package/dist/core/build/plugins/plugin-incremental.js +134 -0
- package/dist/core/build/types.d.ts +2 -0
- package/dist/core/config/schemas/base.d.ts +7 -6
- package/dist/core/config/schemas/base.js +9 -1
- package/dist/core/config/schemas/defaults.d.ts +1 -0
- package/dist/core/config/schemas/defaults.js +1 -0
- package/dist/core/config/schemas/relative.d.ts +19 -10
- package/dist/core/constants.js +1 -1
- package/dist/core/create-vite.js +2 -1
- package/dist/core/dev/dev.js +5 -4
- package/dist/core/dev/lockfile.d.ts +8 -7
- package/dist/core/dev/lockfile.js +13 -13
- package/dist/core/encryption.d.ts +6 -0
- package/dist/core/encryption.js +14 -2
- package/dist/core/errors/default-handler.js +4 -5
- package/dist/core/fetch/index.js +1 -1
- package/dist/core/logger/config.d.ts +1 -1
- package/dist/core/logger/load.d.ts +3 -1
- package/dist/core/logger/load.js +3 -3
- package/dist/core/logger/utils.d.ts +6 -4
- package/dist/core/logger/utils.js +13 -5
- package/dist/core/logger/vite-plugin.js +1 -1
- package/dist/core/messages/runtime.js +1 -1
- package/dist/core/preview/static-preview-server.d.ts +2 -0
- package/dist/core/preview/static-preview-server.js +1 -0
- package/dist/core/routing/handler.js +1 -1
- package/dist/core/session/config.d.ts +2 -2
- package/dist/core/session/config.js +2 -1
- package/dist/core/session/provider-disabled.d.ts +2 -0
- package/dist/core/session/provider-disabled.js +7 -0
- package/dist/core/session/provider.d.ts +1 -0
- package/dist/core/session/provider.js +4 -0
- package/dist/core/session/utils.js +3 -0
- package/dist/core/session/vite-plugin.d.ts +3 -0
- package/dist/core/session/vite-plugin.js +41 -6
- package/dist/core/sync/index.js +5 -4
- package/dist/integrations/hooks.js +3 -1
- package/dist/runtime/prerender/static-paths.js +5 -1
- package/dist/runtime/server/render/util.js +3 -0
- package/dist/types/public/common.d.ts +1 -0
- package/dist/types/public/config.d.ts +73 -7
- package/dist/types/public/integrations.d.ts +29 -2
- package/dist/types/public/preview.d.ts +2 -0
- package/dist/vite-plugin-config-alias/index.js +1 -0
- package/dist/vite-plugin-environment/index.js +4 -1
- package/dist/vite-plugin-hmr-reload/index.js +60 -18
- 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 {
|
|
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
|
-
|
|
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
|
+
};
|
package/dist/content/consts.d.ts
CHANGED
|
@@ -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;
|
package/dist/content/consts.js
CHANGED
|
@@ -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.
|
|
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.
|
|
208
|
-
this.#store.metaStore().set("astro-version", "7.
|
|
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,
|
|
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
|
|
55
|
-
* import (`{ default: string }`, when emitted
|
|
56
|
-
*
|
|
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
|
|
42
|
-
* import (`{ default: string }`, when emitted
|
|
43
|
-
*
|
|
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
|
|
40
|
-
*
|
|
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
|
|
416
|
-
*
|
|
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
|
}
|
package/dist/content/paths.d.ts
CHANGED
|
@@ -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
|
|
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
|
*/
|
package/dist/content/paths.js
CHANGED
|
@@ -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;
|
package/dist/content/runtime.js
CHANGED
|
@@ -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");
|