astro 7.1.6 → 7.2.1
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/utils/resolveImports.d.ts +0 -1
- package/dist/assets/utils/resolveImports.js +1 -4
- 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/content-layer.js +63 -3
- package/dist/content/loaders/file.js +1 -1
- package/dist/content/loaders/glob.js +3 -2
- package/dist/content/mutable-data-store.js +3 -3
- package/dist/content/runtime.d.ts +3 -0
- package/dist/content/runtime.js +8 -2
- 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 +28 -16
- package/dist/core/app/entrypoints/index.d.ts +2 -0
- package/dist/core/app/entrypoints/index.js +14 -0
- package/dist/core/app/types.d.ts +2 -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 +149 -14
- 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 +152 -0
- package/dist/core/build/plugins/plugin-manifest.js +11 -2
- package/dist/core/build/types.d.ts +2 -0
- package/dist/core/config/schemas/base.d.ts +3 -2
- package/dist/core/config/schemas/base.js +1 -0
- 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 +7 -4
- package/dist/core/config/settings.js +1 -1
- package/dist/core/constants.js +1 -1
- package/dist/core/create-vite.js +2 -1
- package/dist/core/dev/dev.js +1 -1
- 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/fetch-state.js +1 -0
- 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/middleware/vite-plugin.d.ts +1 -1
- package/dist/core/middleware/vite-plugin.js +11 -24
- 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/dev.d.ts +3 -1
- package/dist/core/routing/dev.js +10 -2
- 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/integrations/hooks.js +3 -1
- package/dist/prefetch/index.js +18 -1
- package/dist/prefetch/speculation-rules.d.ts +6 -0
- package/dist/prefetch/speculation-rules.js +22 -0
- package/dist/runtime/prerender/static-paths.js +5 -1
- package/dist/runtime/server/render/head.js +10 -0
- package/dist/runtime/server/render/util.js +3 -0
- package/dist/transitions/swap-functions.js +49 -4
- package/dist/types/public/common.d.ts +1 -0
- package/dist/types/public/config.d.ts +81 -9
- package/dist/types/public/integrations.d.ts +29 -2
- package/dist/types/public/internal.d.ts +2 -0
- package/dist/types/public/preview.d.ts +2 -0
- package/dist/vite-plugin-app/app.d.ts +3 -1
- package/dist/vite-plugin-app/app.js +4 -3
- package/dist/vite-plugin-config-alias/index.js +1 -0
- package/dist/vite-plugin-environment/index.js +4 -1
- package/package.json +1 -2
|
@@ -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
|
+
};
|
|
@@ -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
|
|
199
|
+
if (previousAstroVersion && previousAstroVersion !== "7.2.1") {
|
|
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
|
|
208
|
-
this.#store.metaStore().set("astro-version", "7.1
|
|
207
|
+
if ("7.2.1") {
|
|
208
|
+
this.#store.metaStore().set("astro-version", "7.2.1");
|
|
209
209
|
}
|
|
210
210
|
if (currentConfigDigest) {
|
|
211
211
|
this.#store.metaStore().set("content-config-digest", currentConfigDigest);
|
|
@@ -265,6 +265,7 @@ ${contentConfig.error.message}`
|
|
|
265
265
|
}
|
|
266
266
|
})
|
|
267
267
|
);
|
|
268
|
+
this.#validateReferences(contentConfig.config.collections, logger);
|
|
268
269
|
await fs.mkdir(this.#settings.config.cacheDir, { recursive: true });
|
|
269
270
|
await fs.mkdir(this.#settings.dotAstroDir, { recursive: true });
|
|
270
271
|
const assetImportsFile = new URL(ASSET_IMPORTS_FILE, this.#settings.dotAstroDir);
|
|
@@ -277,6 +278,65 @@ ${contentConfig.error.message}`
|
|
|
277
278
|
await this.regenerateCollectionFileManifest();
|
|
278
279
|
}
|
|
279
280
|
}
|
|
281
|
+
/**
|
|
282
|
+
* After all loaders complete, walks every entry's data to find reference objects
|
|
283
|
+
* (`{ id, collection }`) and checks that the referenced entry exists in the store.
|
|
284
|
+
* This replaces the inline Zod validation that was removed in the Zod 4 upgrade.
|
|
285
|
+
*/
|
|
286
|
+
#validateReferences(collections, logger) {
|
|
287
|
+
const collectionNames = new Set(Object.keys(collections));
|
|
288
|
+
for (const collectionName of collectionNames) {
|
|
289
|
+
for (const entry of this.#store.values(collectionName)) {
|
|
290
|
+
if (entry?.data) {
|
|
291
|
+
this.#findInvalidReferences(
|
|
292
|
+
entry.data,
|
|
293
|
+
collectionNames,
|
|
294
|
+
collectionName,
|
|
295
|
+
entry.id,
|
|
296
|
+
logger,
|
|
297
|
+
""
|
|
298
|
+
);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
#findInvalidReferences(value, collectionNames, ownerCollection, ownerId, logger, path) {
|
|
304
|
+
if (value == null || typeof value !== "object") return;
|
|
305
|
+
if (Array.isArray(value)) {
|
|
306
|
+
for (let i = 0; i < value.length; i++) {
|
|
307
|
+
this.#findInvalidReferences(
|
|
308
|
+
value[i],
|
|
309
|
+
collectionNames,
|
|
310
|
+
ownerCollection,
|
|
311
|
+
ownerId,
|
|
312
|
+
logger,
|
|
313
|
+
`${path}[${i}]`
|
|
314
|
+
);
|
|
315
|
+
}
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
const obj = value;
|
|
319
|
+
if (typeof obj.collection === "string" && collectionNames.has(obj.collection)) {
|
|
320
|
+
const refId = typeof obj.id === "string" ? obj.id : typeof obj.slug === "string" ? obj.slug : void 0;
|
|
321
|
+
if (refId !== void 0 && !this.#store.has(obj.collection, refId)) {
|
|
322
|
+
const fieldPath = path ? ` (field: ${path})` : "";
|
|
323
|
+
logger.error(
|
|
324
|
+
`Invalid content reference: entry "${ownerId}" in collection "${ownerCollection}"${fieldPath} references "${refId}" in collection "${obj.collection}", but that entry does not exist.`
|
|
325
|
+
);
|
|
326
|
+
}
|
|
327
|
+
return;
|
|
328
|
+
}
|
|
329
|
+
for (const [key, val] of Object.entries(obj)) {
|
|
330
|
+
this.#findInvalidReferences(
|
|
331
|
+
val,
|
|
332
|
+
collectionNames,
|
|
333
|
+
ownerCollection,
|
|
334
|
+
ownerId,
|
|
335
|
+
logger,
|
|
336
|
+
path ? `${path}.${key}` : key
|
|
337
|
+
);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
280
340
|
async regenerateCollectionFileManifest() {
|
|
281
341
|
const collectionsManifest = new URL(COLLECTIONS_MANIFEST_FILE, this.#settings.dotAstroDir);
|
|
282
342
|
this.#logger.debug("content", "Regenerating collection file manifest");
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { existsSync, promises as fs } from "node:fs";
|
|
2
2
|
import { fileURLToPath } from "node:url";
|
|
3
|
-
import yaml from "js-yaml";
|
|
3
|
+
import * as yaml from "js-yaml";
|
|
4
4
|
import * as toml from "smol-toml";
|
|
5
5
|
import { FileGlobNotSupported, FileParserNotFound } from "../../core/errors/errors-data.js";
|
|
6
6
|
import { AstroError } from "../../core/errors/index.js";
|
|
@@ -8,7 +8,7 @@ import { glob as tinyglobby } from "tinyglobby";
|
|
|
8
8
|
import { getContentEntryIdAndSlug, posixRelative } from "../utils.js";
|
|
9
9
|
function generateIdDefault({ entry, base, data }, isLegacy) {
|
|
10
10
|
if (data.slug) {
|
|
11
|
-
return data.slug;
|
|
11
|
+
return String(data.slug);
|
|
12
12
|
}
|
|
13
13
|
const entryURL = new URL(encodeURI(entry), base);
|
|
14
14
|
if (isLegacy) {
|
|
@@ -45,7 +45,8 @@ function glob(globOptions) {
|
|
|
45
45
|
);
|
|
46
46
|
}
|
|
47
47
|
const isLegacy = !!globOptions[secretLegacyFlag];
|
|
48
|
-
const
|
|
48
|
+
const userGenerateId = globOptions?.generateId ?? ((opts) => generateIdDefault(opts, isLegacy));
|
|
49
|
+
const generateId = (opts) => String(userGenerateId(opts));
|
|
49
50
|
const fileToIdMap = /* @__PURE__ */ new Map();
|
|
50
51
|
return {
|
|
51
52
|
name: "glob-loader",
|
|
@@ -2,7 +2,7 @@ import { existsSync, promises as fs } from "node:fs";
|
|
|
2
2
|
import { fileURLToPath } from "node:url";
|
|
3
3
|
import * as devalue from "devalue";
|
|
4
4
|
import { forEach } from "neotraverse";
|
|
5
|
-
import { imageSrcToImportId
|
|
5
|
+
import { imageSrcToImportId } from "../assets/utils/resolveImports.js";
|
|
6
6
|
import { AstroError, AstroErrorData } from "../core/errors/index.js";
|
|
7
7
|
import { DATA_STORE_MANIFEST_FILE, IMAGE_IMPORT_PREFIX } from "./consts.js";
|
|
8
8
|
import {
|
|
@@ -109,8 +109,8 @@ class MutableDataStore extends ImmutableDataStore {
|
|
|
109
109
|
const imports = [];
|
|
110
110
|
const exports = [];
|
|
111
111
|
const sortedAssetImports = [...this.#assetImports].sort();
|
|
112
|
-
sortedAssetImports.forEach((id) => {
|
|
113
|
-
const symbol =
|
|
112
|
+
sortedAssetImports.forEach((id, index) => {
|
|
113
|
+
const symbol = `__ASTRO_IMAGE_IMPORT_${index}`;
|
|
114
114
|
imports.push(`import ${symbol} from ${JSON.stringify(id)};`);
|
|
115
115
|
exports.push(`[${JSON.stringify(id)}, ${symbol}]`);
|
|
116
116
|
});
|
|
@@ -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;
|
|
@@ -69,6 +71,7 @@ type RenderResult = {
|
|
|
69
71
|
remarkPluginFrontmatter: Record<string, any>;
|
|
70
72
|
};
|
|
71
73
|
export declare function updateImageReferencesInData<T extends Record<string, unknown>>(data: T, fileName?: string, imageAssetMap?: Map<string, ImageMetadata>): T;
|
|
74
|
+
export declare function resolveEntryData<T extends Record<string, unknown>>(entry: DataEntry<T>, imageAssetMap?: Map<string, ImageMetadata>): T;
|
|
72
75
|
export declare function renderEntry(entry: DataEntry): Promise<RenderResult>;
|
|
73
76
|
export declare function createReference(): (collection: string) => z.ZodPipe<z.ZodUnion<readonly [z.ZodPipe<z.ZodNumber, z.ZodTransform<string, number>>, z.ZodString, z.ZodObject<{
|
|
74
77
|
id: z.ZodString;
|
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 {
|
|
@@ -77,7 +78,7 @@ function createGetCollection({
|
|
|
77
78
|
const { default: imageAssetMap } = await import("astro:asset-imports");
|
|
78
79
|
const result = [];
|
|
79
80
|
for (const rawEntry of await store.values(collection)) {
|
|
80
|
-
const data =
|
|
81
|
+
const data = resolveEntryData(rawEntry, imageAssetMap);
|
|
81
82
|
let entry = {
|
|
82
83
|
...rawEntry,
|
|
83
84
|
data,
|
|
@@ -134,7 +135,7 @@ function createGetEntry({ liveCollections }) {
|
|
|
134
135
|
return;
|
|
135
136
|
}
|
|
136
137
|
const { default: imageAssetMap } = await import("astro:asset-imports");
|
|
137
|
-
const data =
|
|
138
|
+
const data = resolveEntryData(entry, imageAssetMap);
|
|
138
139
|
const result = {
|
|
139
140
|
...entry,
|
|
140
141
|
data,
|
|
@@ -381,10 +382,14 @@ function updateImageReferencesInData(data, fileName, imageAssetMap) {
|
|
|
381
382
|
});
|
|
382
383
|
return copy;
|
|
383
384
|
}
|
|
385
|
+
function resolveEntryData(entry, imageAssetMap) {
|
|
386
|
+
return entry.assetImports?.length ? updateImageReferencesInData(entry.data, entry.filePath, imageAssetMap) : structuredClone(entry.data);
|
|
387
|
+
}
|
|
384
388
|
async function renderEntry(entry) {
|
|
385
389
|
if (!entry) {
|
|
386
390
|
throw new AstroError(AstroErrorData.RenderUndefinedEntryError);
|
|
387
391
|
}
|
|
392
|
+
recordContentEntryRender(entry.filePath);
|
|
388
393
|
if (entry.deferredRender) {
|
|
389
394
|
try {
|
|
390
395
|
const { default: contentModules } = await import("astro:content-module-imports");
|
|
@@ -561,5 +566,6 @@ export {
|
|
|
561
566
|
defineCollection,
|
|
562
567
|
defineLiveCollection,
|
|
563
568
|
renderEntry,
|
|
569
|
+
resolveEntryData,
|
|
564
570
|
updateImageReferencesInData
|
|
565
571
|
};
|
|
@@ -373,6 +373,7 @@ async function writeContentFiles({
|
|
|
373
373
|
data: ${dataType};
|
|
374
374
|
rendered?: RenderedContent;
|
|
375
375
|
filePath?: string;
|
|
376
|
+
digest?: string | number;
|
|
376
377
|
}>;
|
|
377
378
|
`;
|
|
378
379
|
if (collectionConfig && (collectionConfig.schema || await getContentLayerSchema(collectionConfig, collectionKey))) {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { extname } from "node:path";
|
|
2
2
|
import { getAssetsPrefix } from "../assets/utils/getAssetsPrefix.js";
|
|
3
|
+
import { createContentDataIncrementalMetadata } from "../core/build/incremental-metadata.js";
|
|
3
4
|
import { fileExtension } from "../core/path.js";
|
|
4
5
|
import { fileURLToPath } from "node:url";
|
|
5
6
|
import { isRunnableDevEnvironment } from "vite";
|
|
@@ -107,7 +108,11 @@ function astroContentAssetPropagationPlugin({
|
|
|
107
108
|
const defaultMod = { __astroPropagation: true, getMod, collectedLinks, collectedStyles, collectedScripts: [] };
|
|
108
109
|
export default defaultMod;
|
|
109
110
|
`;
|
|
110
|
-
return {
|
|
111
|
+
return {
|
|
112
|
+
code,
|
|
113
|
+
map: { mappings: "" },
|
|
114
|
+
meta: createContentDataIncrementalMetadata()
|
|
115
|
+
};
|
|
111
116
|
}
|
|
112
117
|
}
|
|
113
118
|
}
|