astro 7.2.1 → 7.2.2
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/fonts/constants.d.ts +8 -0
- package/dist/assets/fonts/constants.js +2 -0
- package/dist/assets/fonts/vite-plugin-fonts.js +3 -1
- package/dist/assets/utils/node.js +35 -3
- package/dist/cli/dev/index.js +2 -2
- package/dist/cli/infra/build-time-astro-version-provider.js +1 -1
- package/dist/cli/preview/index.js +1 -1
- package/dist/cli/server.js +4 -4
- package/dist/content/content-layer.js +3 -3
- package/dist/content/loaders/file.js +19 -4
- package/dist/content/loaders/glob.js +15 -2
- package/dist/core/build/generate.js +10 -1
- package/dist/core/build/plugins/plugin-incremental.js +145 -37
- package/dist/core/constants.js +1 -1
- package/dist/core/dev/dev.js +1 -1
- package/dist/core/dev/lockfile.d.ts +19 -2
- package/dist/core/dev/lockfile.js +25 -2
- package/dist/core/messages/runtime.js +1 -1
- package/dist/core/util.js +2 -2
- package/dist/vite-plugin-css/index.js +2 -2
- package/package.json +5 -4
|
@@ -15,3 +15,11 @@ export declare const FONT_FORMATS: Array<{
|
|
|
15
15
|
}>;
|
|
16
16
|
export declare const GENERIC_FALLBACK_NAMES: readonly ["serif", "sans-serif", "monospace", "cursive", "fantasy", "system-ui", "ui-serif", "ui-sans-serif", "ui-monospace", "ui-rounded", "emoji", "math", "fangsong"];
|
|
17
17
|
export declare const FONTS_TYPES_FILE = "fonts.d.ts";
|
|
18
|
+
/**
|
|
19
|
+
* Variable name used in the font-file-url-resolver virtual module to hold
|
|
20
|
+
* the ephemeral font HTTP server address. The incremental build plugin
|
|
21
|
+
* strips the variable declaration (which contains an OS-assigned port that
|
|
22
|
+
* changes every build) from the module source before hashing so that the
|
|
23
|
+
* dependency hash is deterministic across builds.
|
|
24
|
+
*/
|
|
25
|
+
export declare const FONTS_SERVER_ADDRESS_PLACEHOLDER = "__ASTRO_FONTS_SERVER_ADDRESS__";
|
|
@@ -39,10 +39,12 @@ const GENERIC_FALLBACK_NAMES = [
|
|
|
39
39
|
"fangsong"
|
|
40
40
|
];
|
|
41
41
|
const FONTS_TYPES_FILE = "fonts.d.ts";
|
|
42
|
+
const FONTS_SERVER_ADDRESS_PLACEHOLDER = "__ASTRO_FONTS_SERVER_ADDRESS__";
|
|
42
43
|
export {
|
|
43
44
|
ASSETS_DIR,
|
|
44
45
|
CACHE_DIR,
|
|
45
46
|
DEFAULTS,
|
|
47
|
+
FONTS_SERVER_ADDRESS_PLACEHOLDER,
|
|
46
48
|
FONTS_TYPES_FILE,
|
|
47
49
|
FONT_FORMATS,
|
|
48
50
|
FONT_TYPES,
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
ASSETS_DIR,
|
|
14
14
|
CACHE_DIR,
|
|
15
15
|
DEFAULTS,
|
|
16
|
+
FONTS_SERVER_ADDRESS_PLACEHOLDER,
|
|
16
17
|
RESOLVED_RUNTIME_FONT_FILE_URL_RESOLVER_VIRTUAL_MODULE_ID,
|
|
17
18
|
RESOLVED_RUNTIME_VIRTUAL_MODULE_ID,
|
|
18
19
|
RESOLVED_VIRTUAL_MODULE_ID,
|
|
@@ -285,9 +286,10 @@ function fontsPlugin({ settings, sync, logger }) {
|
|
|
285
286
|
return {
|
|
286
287
|
code: `
|
|
287
288
|
import { RemoteRuntimeFontFileUrlResolver } from ${JSON.stringify(new URL("./infra/remote-runtime-font-file-url-resolver.js", import.meta.url))};
|
|
289
|
+
const ${FONTS_SERVER_ADDRESS_PLACEHOLDER} = ${JSON.stringify(serverAddress)};
|
|
288
290
|
export const runtimeFontFileUrlResolver = new RemoteRuntimeFontFileUrlResolver({
|
|
289
291
|
urls: new Set(${JSON.stringify(urls)}),
|
|
290
|
-
address: ${
|
|
292
|
+
address: ${FONTS_SERVER_ADDRESS_PLACEHOLDER},
|
|
291
293
|
});
|
|
292
294
|
`
|
|
293
295
|
};
|
|
@@ -36,6 +36,35 @@ async function handleSvgDeduplication(fileData, filename, fileEmitter) {
|
|
|
36
36
|
return handle;
|
|
37
37
|
}
|
|
38
38
|
}
|
|
39
|
+
const TRANSIENT_ERROR_CODES = /* @__PURE__ */ new Set(["EMFILE", "ENFILE", "EAGAIN", "EBUSY"]);
|
|
40
|
+
const MAX_CONCURRENT_READS = 200;
|
|
41
|
+
let activeReads = 0;
|
|
42
|
+
const readQueue = [];
|
|
43
|
+
async function readFileWithRetry(url, maxRetries = 5) {
|
|
44
|
+
if (activeReads >= MAX_CONCURRENT_READS) {
|
|
45
|
+
await new Promise((resolve) => readQueue.push(resolve));
|
|
46
|
+
}
|
|
47
|
+
activeReads++;
|
|
48
|
+
try {
|
|
49
|
+
for (let attempt = 0; ; attempt++) {
|
|
50
|
+
try {
|
|
51
|
+
return await fs.readFile(url);
|
|
52
|
+
} catch (err) {
|
|
53
|
+
const code = err instanceof Error && "code" in err ? err.code : void 0;
|
|
54
|
+
if (code && TRANSIENT_ERROR_CODES.has(code) && attempt < maxRetries) {
|
|
55
|
+
await new Promise((resolve) => setTimeout(resolve, 50 * 2 ** attempt));
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
throw err;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
} finally {
|
|
62
|
+
activeReads--;
|
|
63
|
+
if (readQueue.length > 0) {
|
|
64
|
+
readQueue.shift()();
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
39
68
|
async function emitImageMetadata(id, fileEmitter) {
|
|
40
69
|
if (!id) {
|
|
41
70
|
return void 0;
|
|
@@ -43,9 +72,12 @@ async function emitImageMetadata(id, fileEmitter) {
|
|
|
43
72
|
const url = pathToFileURL(id);
|
|
44
73
|
let fileData;
|
|
45
74
|
try {
|
|
46
|
-
fileData = await
|
|
47
|
-
} catch {
|
|
48
|
-
|
|
75
|
+
fileData = await readFileWithRetry(url);
|
|
76
|
+
} catch (err) {
|
|
77
|
+
if (err instanceof Error && "code" in err && err.code === "ENOENT") {
|
|
78
|
+
return void 0;
|
|
79
|
+
}
|
|
80
|
+
throw err;
|
|
49
81
|
}
|
|
50
82
|
const fileMetadata = await imageMetadata(fileData, id);
|
|
51
83
|
const emittedImage = {
|
package/dist/cli/dev/index.js
CHANGED
|
@@ -128,7 +128,7 @@ Run \`astro dev --help\` to see available commands.`
|
|
|
128
128
|
}
|
|
129
129
|
const root = pathToFileURL(resolveRoot(flags.root) + "/");
|
|
130
130
|
if (ignoreLock) {
|
|
131
|
-
const existingServer2 = checkExistingServer(root);
|
|
131
|
+
const existingServer2 = await checkExistingServer(root);
|
|
132
132
|
if (existingServer2) {
|
|
133
133
|
logger.info(
|
|
134
134
|
"SKIP_FORMAT",
|
|
@@ -141,7 +141,7 @@ Run \`astro dev --help\` to see available commands.`
|
|
|
141
141
|
const inlineConfig2 = flagsToAstroInlineConfig(flags);
|
|
142
142
|
return await devServer(inlineConfig2);
|
|
143
143
|
}
|
|
144
|
-
const existingServer = checkExistingServer(root);
|
|
144
|
+
const existingServer = await checkExistingServer(root);
|
|
145
145
|
if (existingServer) {
|
|
146
146
|
if (flags.force) {
|
|
147
147
|
await killDevServer(root, existingServer);
|
|
@@ -69,7 +69,7 @@ Run \`astro preview --help\` to see available commands.`
|
|
|
69
69
|
process.exit(1);
|
|
70
70
|
}
|
|
71
71
|
const root = pathToFileURL(resolveRoot(flags.root) + "/");
|
|
72
|
-
const existingServer = checkExistingServer(root, "preview");
|
|
72
|
+
const existingServer = await checkExistingServer(root, "preview");
|
|
73
73
|
if (existingServer) {
|
|
74
74
|
const message = [
|
|
75
75
|
"Another astro preview server is already running.",
|
package/dist/cli/server.js
CHANGED
|
@@ -101,7 +101,7 @@ async function background({
|
|
|
101
101
|
config
|
|
102
102
|
}) {
|
|
103
103
|
const root = getRootURL(flags);
|
|
104
|
-
const existing = checkExistingServer(root, config.command);
|
|
104
|
+
const existing = await checkExistingServer(root, config.command);
|
|
105
105
|
if (existing && !flags.force) {
|
|
106
106
|
logger.info("SKIP_FORMAT", formatServerRunningMessage(existing, config, { existing: true }));
|
|
107
107
|
return;
|
|
@@ -160,7 +160,7 @@ async function stop({
|
|
|
160
160
|
config
|
|
161
161
|
}) {
|
|
162
162
|
const root = getRootURL(flags);
|
|
163
|
-
const existing = checkExistingServer(root, config.command);
|
|
163
|
+
const existing = await checkExistingServer(root, config.command);
|
|
164
164
|
if (!existing) {
|
|
165
165
|
logger.info("SKIP_FORMAT", `No ${config.command} server is running.`);
|
|
166
166
|
return;
|
|
@@ -174,7 +174,7 @@ async function status({
|
|
|
174
174
|
config
|
|
175
175
|
}) {
|
|
176
176
|
const root = getRootURL(flags);
|
|
177
|
-
const existing = checkExistingServer(root, config.command);
|
|
177
|
+
const existing = await checkExistingServer(root, config.command);
|
|
178
178
|
if (!existing) {
|
|
179
179
|
logger.info("SKIP_FORMAT", `No ${config.command} server is running.`);
|
|
180
180
|
return;
|
|
@@ -198,7 +198,7 @@ async function logs({
|
|
|
198
198
|
config
|
|
199
199
|
}) {
|
|
200
200
|
const root = getRootURL(flags);
|
|
201
|
-
const existing = checkExistingServer(root, config.command);
|
|
201
|
+
const existing = await checkExistingServer(root, config.command);
|
|
202
202
|
if (!existing) {
|
|
203
203
|
logger.error("SKIP_FORMAT", `No ${config.command} server is running.`);
|
|
204
204
|
process.exit(1);
|
|
@@ -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.2.
|
|
199
|
+
if (previousAstroVersion && previousAstroVersion !== "7.2.2") {
|
|
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.2.
|
|
208
|
-
this.#store.metaStore().set("astro-version", "7.2.
|
|
207
|
+
if ("7.2.2") {
|
|
208
|
+
this.#store.metaStore().set("astro-version", "7.2.2");
|
|
209
209
|
}
|
|
210
210
|
if (currentConfigDigest) {
|
|
211
211
|
this.#store.metaStore().set("content-config-digest", currentConfigDigest);
|
|
@@ -2,7 +2,11 @@ import { existsSync, promises as fs } from "node:fs";
|
|
|
2
2
|
import { fileURLToPath } from "node:url";
|
|
3
3
|
import * as yaml from "js-yaml";
|
|
4
4
|
import * as toml from "smol-toml";
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
DuplicateContentEntrySlugError,
|
|
7
|
+
FileGlobNotSupported,
|
|
8
|
+
FileParserNotFound
|
|
9
|
+
} from "../../core/errors/errors-data.js";
|
|
6
10
|
import { AstroError } from "../../core/errors/index.js";
|
|
7
11
|
import { posixRelative } from "../utils.js";
|
|
8
12
|
function file(fileName, options) {
|
|
@@ -27,7 +31,7 @@ function file(fileName, options) {
|
|
|
27
31
|
message: FileParserNotFound.message(fileName)
|
|
28
32
|
});
|
|
29
33
|
}
|
|
30
|
-
async function syncData(filePath, { logger, parseData, store, config }) {
|
|
34
|
+
async function syncData(filePath, { logger, parseData, store, config, collection }) {
|
|
31
35
|
let data;
|
|
32
36
|
try {
|
|
33
37
|
const contents = await fs.readFile(filePath, "utf-8");
|
|
@@ -52,9 +56,20 @@ function file(fileName, options) {
|
|
|
52
56
|
continue;
|
|
53
57
|
}
|
|
54
58
|
if (idList.has(id)) {
|
|
55
|
-
|
|
56
|
-
|
|
59
|
+
const message = DuplicateContentEntrySlugError.message(
|
|
60
|
+
collection,
|
|
61
|
+
id,
|
|
62
|
+
fileName,
|
|
63
|
+
fileName
|
|
57
64
|
);
|
|
65
|
+
if (config.prerenderConflictBehavior === "error") {
|
|
66
|
+
throw new AstroError({
|
|
67
|
+
...DuplicateContentEntrySlugError,
|
|
68
|
+
message
|
|
69
|
+
});
|
|
70
|
+
} else if (config.prerenderConflictBehavior !== "ignore") {
|
|
71
|
+
logger.warn(message);
|
|
72
|
+
}
|
|
58
73
|
}
|
|
59
74
|
idList.add(id);
|
|
60
75
|
const parsedData = await parseData({ id, data: rawItem, filePath });
|
|
@@ -5,6 +5,8 @@ import pLimit from "p-limit";
|
|
|
5
5
|
import colors from "piccolore";
|
|
6
6
|
import picomatch from "picomatch";
|
|
7
7
|
import { glob as tinyglobby } from "tinyglobby";
|
|
8
|
+
import * as AstroErrorData from "../../core/errors/errors-data.js";
|
|
9
|
+
import { AstroError } from "../../core/errors/index.js";
|
|
8
10
|
import { getContentEntryIdAndSlug, posixRelative } from "../utils.js";
|
|
9
11
|
function generateIdDefault({ entry, base, data }, isLegacy) {
|
|
10
12
|
if (data.slug) {
|
|
@@ -107,9 +109,20 @@ function glob(globOptions) {
|
|
|
107
109
|
if (existingEntry && existingEntry.filePath && existingEntry.filePath !== relativePath2) {
|
|
108
110
|
const oldFilePath = new URL(existingEntry.filePath, config.root);
|
|
109
111
|
if (existsSync(oldFilePath)) {
|
|
110
|
-
|
|
111
|
-
|
|
112
|
+
const message = AstroErrorData.DuplicateContentEntrySlugError.message(
|
|
113
|
+
collection,
|
|
114
|
+
id,
|
|
115
|
+
existingEntry.filePath,
|
|
116
|
+
relativePath2
|
|
112
117
|
);
|
|
118
|
+
if (config.prerenderConflictBehavior === "error") {
|
|
119
|
+
throw new AstroError({
|
|
120
|
+
...AstroErrorData.DuplicateContentEntrySlugError,
|
|
121
|
+
message
|
|
122
|
+
});
|
|
123
|
+
} else if (config.prerenderConflictBehavior !== "ignore") {
|
|
124
|
+
logger.warn(message);
|
|
125
|
+
}
|
|
113
126
|
}
|
|
114
127
|
}
|
|
115
128
|
if (entryType.getRenderFunction && !globOptions.deferRender) {
|
|
@@ -222,7 +222,16 @@ ${colors.bgGreen(colors.black(` ${verb} static routes `))}`);
|
|
|
222
222
|
if (prerenderer.collectStaticImages) {
|
|
223
223
|
const adapterImages = await prerenderer.collectStaticImages();
|
|
224
224
|
for (const [path, entry] of adapterImages) {
|
|
225
|
-
staticImageList.
|
|
225
|
+
const existing = staticImageList.get(path);
|
|
226
|
+
if (existing) {
|
|
227
|
+
for (const [hash, transform] of entry.transforms) {
|
|
228
|
+
if (!existing.transforms.has(hash)) {
|
|
229
|
+
existing.transforms.set(hash, transform);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
} else {
|
|
233
|
+
staticImageList.set(path, entry);
|
|
234
|
+
}
|
|
226
235
|
}
|
|
227
236
|
}
|
|
228
237
|
} finally {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import crypto from "node:crypto";
|
|
2
|
+
import { FONTS_SERVER_ADDRESS_PLACEHOLDER } from "../../../assets/fonts/constants.js";
|
|
2
3
|
import { PROPAGATED_ASSET_FLAG } from "../../../content/consts.js";
|
|
3
4
|
import { hasContentFlag } from "../../../content/utils.js";
|
|
4
5
|
import { ASTRO_VITE_ENVIRONMENT_NAMES } from "../../constants.js";
|
|
@@ -7,29 +8,13 @@ import { rootRelativePath } from "../../viteUtils.js";
|
|
|
7
8
|
import { moduleIsTopLevelPage } from "../graph.js";
|
|
8
9
|
import { isContentDataIncrementalModule } from "../incremental-metadata.js";
|
|
9
10
|
import { getPageDataByViteID } from "../internal.js";
|
|
10
|
-
function collectTransitiveDeps(graph, rootId) {
|
|
11
|
-
const deps = /* @__PURE__ */ new Set();
|
|
12
|
-
const queue = [rootId];
|
|
13
|
-
while (queue.length > 0) {
|
|
14
|
-
const current = queue.pop();
|
|
15
|
-
if (deps.has(current)) continue;
|
|
16
|
-
const modInfo = graph.getModuleInfo(current);
|
|
17
|
-
if (isContentDataIncrementalModule(modInfo)) continue;
|
|
18
|
-
deps.add(current);
|
|
19
|
-
if (!modInfo) continue;
|
|
20
|
-
for (const dep of modInfo.importedIds) {
|
|
21
|
-
if (!deps.has(dep)) queue.push(dep);
|
|
22
|
-
}
|
|
23
|
-
for (const dep of modInfo.dynamicallyImportedIds) {
|
|
24
|
-
if (!deps.has(dep)) queue.push(dep);
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
return [...deps].sort();
|
|
28
|
-
}
|
|
29
11
|
const ASSET_PLACEHOLDERS = [
|
|
30
12
|
{ token: "__ASTRO_ASSET_IMAGE__", pattern: /__ASTRO_ASSET_IMAGE__([\w$]+)__(?:_(.*?)__)?/g },
|
|
31
13
|
{ token: "__VITE_ASSET__", pattern: /__VITE_ASSET__([\w$]+)__(?:\$_(.*?)__)?/g }
|
|
32
14
|
];
|
|
15
|
+
const FONTS_ADDRESS_DECLARATION = new RegExp(
|
|
16
|
+
`(?:const|let|var)\\s+${FONTS_SERVER_ADDRESS_PLACEHOLDER}\\s*=[^;]+;`
|
|
17
|
+
);
|
|
33
18
|
function resolveAssetPlaceholders(graph, code) {
|
|
34
19
|
let resolved = code;
|
|
35
20
|
for (const { token, pattern } of ASSET_PLACEHOLDERS) {
|
|
@@ -42,6 +27,9 @@ function resolveAssetPlaceholders(graph, code) {
|
|
|
42
27
|
}
|
|
43
28
|
});
|
|
44
29
|
}
|
|
30
|
+
if (resolved.includes(FONTS_SERVER_ADDRESS_PLACEHOLDER)) {
|
|
31
|
+
resolved = resolved.replace(FONTS_ADDRESS_DECLARATION, "");
|
|
32
|
+
}
|
|
45
33
|
return resolved;
|
|
46
34
|
}
|
|
47
35
|
function hashModules(graph, sortedIds) {
|
|
@@ -57,11 +45,131 @@ function hashModules(graph, sortedIds) {
|
|
|
57
45
|
}
|
|
58
46
|
return hasher.digest("hex");
|
|
59
47
|
}
|
|
60
|
-
function
|
|
48
|
+
function createTransitiveGraphCache(graph) {
|
|
49
|
+
const modules = /* @__PURE__ */ new Map();
|
|
50
|
+
const dependencies = /* @__PURE__ */ new Map();
|
|
51
|
+
const excludedModules = /* @__PURE__ */ new Set();
|
|
52
|
+
const pending = [...graph.getModuleIds()];
|
|
53
|
+
for (const id of pending) {
|
|
54
|
+
if (modules.has(id)) continue;
|
|
55
|
+
const info = graph.getModuleInfo(id);
|
|
56
|
+
modules.set(id, info);
|
|
57
|
+
if (isContentDataIncrementalModule(info)) {
|
|
58
|
+
excludedModules.add(id);
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
const importedIds = [...info?.importedIds ?? [], ...info?.dynamicallyImportedIds ?? []];
|
|
62
|
+
dependencies.set(id, importedIds);
|
|
63
|
+
pending.push(...importedIds);
|
|
64
|
+
}
|
|
65
|
+
for (const id of excludedModules) modules.delete(id);
|
|
66
|
+
for (const [id, importedIds] of dependencies) {
|
|
67
|
+
dependencies.set(
|
|
68
|
+
id,
|
|
69
|
+
importedIds.filter((importedId) => !excludedModules.has(importedId))
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
const reverseDependencies = /* @__PURE__ */ new Map();
|
|
73
|
+
for (const id of modules.keys()) reverseDependencies.set(id, []);
|
|
74
|
+
for (const [id, importedIds] of dependencies) {
|
|
75
|
+
for (const importedId of importedIds) reverseDependencies.get(importedId)?.push(id);
|
|
76
|
+
}
|
|
77
|
+
const visited = /* @__PURE__ */ new Set();
|
|
78
|
+
const finishOrder = [];
|
|
79
|
+
for (const rootId of modules.keys()) {
|
|
80
|
+
if (visited.has(rootId)) continue;
|
|
81
|
+
visited.add(rootId);
|
|
82
|
+
const stack = [[rootId, 0]];
|
|
83
|
+
while (stack.length > 0) {
|
|
84
|
+
const frame = stack[stack.length - 1];
|
|
85
|
+
const importedIds = dependencies.get(frame[0]) ?? [];
|
|
86
|
+
if (frame[1] < importedIds.length) {
|
|
87
|
+
const importedId = importedIds[frame[1]++];
|
|
88
|
+
if (!visited.has(importedId)) {
|
|
89
|
+
visited.add(importedId);
|
|
90
|
+
stack.push([importedId, 0]);
|
|
91
|
+
}
|
|
92
|
+
} else {
|
|
93
|
+
finishOrder.push(frame[0]);
|
|
94
|
+
stack.pop();
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
const componentByModule = /* @__PURE__ */ new Map();
|
|
99
|
+
const components = [];
|
|
100
|
+
for (const rootId of finishOrder.toReversed()) {
|
|
101
|
+
if (componentByModule.has(rootId)) continue;
|
|
102
|
+
const componentIndex = components.length;
|
|
103
|
+
const component = [];
|
|
104
|
+
const stack = [rootId];
|
|
105
|
+
componentByModule.set(rootId, componentIndex);
|
|
106
|
+
while (stack.length > 0) {
|
|
107
|
+
const id = stack.pop();
|
|
108
|
+
component.push(id);
|
|
109
|
+
for (const importerId of reverseDependencies.get(id) ?? []) {
|
|
110
|
+
if (!componentByModule.has(importerId)) {
|
|
111
|
+
componentByModule.set(importerId, componentIndex);
|
|
112
|
+
stack.push(importerId);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
components.push(component.sort());
|
|
117
|
+
}
|
|
118
|
+
const componentDependencies = components.map(() => /* @__PURE__ */ new Set());
|
|
119
|
+
const componentImporters = components.map(() => /* @__PURE__ */ new Set());
|
|
120
|
+
for (const [id, importedIds] of dependencies) {
|
|
121
|
+
const componentIndex = componentByModule.get(id);
|
|
122
|
+
for (const importedId of importedIds) {
|
|
123
|
+
const dependencyIndex = componentByModule.get(importedId);
|
|
124
|
+
if (dependencyIndex === componentIndex) continue;
|
|
125
|
+
componentDependencies[componentIndex].add(dependencyIndex);
|
|
126
|
+
componentImporters[dependencyIndex].add(componentIndex);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
const componentHashes = /* @__PURE__ */ new Map();
|
|
130
|
+
const componentHasServerIsland = /* @__PURE__ */ new Map();
|
|
131
|
+
const unresolvedDependencies = componentDependencies.map((items) => items.size);
|
|
132
|
+
const ready = unresolvedDependencies.flatMap((count, index) => count === 0 ? [index] : []);
|
|
133
|
+
for (const componentIndex of ready) {
|
|
134
|
+
const hasher = crypto.createHash("sha256");
|
|
135
|
+
hasher.update(hashModules(graph, components[componentIndex]));
|
|
136
|
+
const dependencyHashes = [...componentDependencies[componentIndex]].map((dependencyIndex) => componentHashes.get(dependencyIndex)).sort();
|
|
137
|
+
for (const dependencyHash of dependencyHashes) {
|
|
138
|
+
hasher.update("\n");
|
|
139
|
+
hasher.update(dependencyHash);
|
|
140
|
+
}
|
|
141
|
+
componentHashes.set(componentIndex, hasher.digest("hex"));
|
|
142
|
+
componentHasServerIsland.set(
|
|
143
|
+
componentIndex,
|
|
144
|
+
components[componentIndex].some(
|
|
145
|
+
(id) => (modules.get(id)?.meta?.astro?.serverComponents?.length ?? 0) > 0
|
|
146
|
+
) || [...componentDependencies[componentIndex]].some(
|
|
147
|
+
(dependencyIndex) => componentHasServerIsland.get(dependencyIndex)
|
|
148
|
+
)
|
|
149
|
+
);
|
|
150
|
+
for (const importerIndex of componentImporters[componentIndex]) {
|
|
151
|
+
unresolvedDependencies[importerIndex]--;
|
|
152
|
+
if (unresolvedDependencies[importerIndex] === 0) ready.push(importerIndex);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return {
|
|
156
|
+
hashes: new Map(
|
|
157
|
+
[...componentByModule].map(([id, componentIndex]) => [
|
|
158
|
+
id,
|
|
159
|
+
componentHashes.get(componentIndex)
|
|
160
|
+
])
|
|
161
|
+
),
|
|
162
|
+
serverIslandModules: new Set(
|
|
163
|
+
[...componentByModule].filter(([, componentIndex]) => componentHasServerIsland.get(componentIndex)).map(([id]) => id)
|
|
164
|
+
)
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
function collectClientEntrypointHashes(transitiveHashes, entrypointIds, pagesByEntrypoint, hashesByComponent) {
|
|
61
168
|
for (const entrypointId of entrypointIds) {
|
|
62
169
|
const pages = pagesByEntrypoint.get(entrypointId);
|
|
63
170
|
if (!pages?.size) continue;
|
|
64
|
-
const hash =
|
|
171
|
+
const hash = transitiveHashes.get(entrypointId);
|
|
172
|
+
if (!hash) continue;
|
|
65
173
|
for (const pageData of pages) {
|
|
66
174
|
let list = hashesByComponent.get(pageData.component);
|
|
67
175
|
if (!list) {
|
|
@@ -75,15 +183,16 @@ function collectClientEntrypointHashes(graph, entrypointIds, pagesByEntrypoint,
|
|
|
75
183
|
function foldClientDependencies(graph, internals) {
|
|
76
184
|
const baseHashes = internals.pageDependencyHashes;
|
|
77
185
|
if (!baseHashes) return;
|
|
186
|
+
const { hashes: transitiveHashes } = createTransitiveGraphCache(graph);
|
|
78
187
|
const hashesByComponent = /* @__PURE__ */ new Map();
|
|
79
188
|
collectClientEntrypointHashes(
|
|
80
|
-
|
|
189
|
+
transitiveHashes,
|
|
81
190
|
internals.discoveredClientOnlyComponents.keys(),
|
|
82
191
|
internals.pagesByClientOnly,
|
|
83
192
|
hashesByComponent
|
|
84
193
|
);
|
|
85
194
|
collectClientEntrypointHashes(
|
|
86
|
-
|
|
195
|
+
transitiveHashes,
|
|
87
196
|
internals.discoveredScripts,
|
|
88
197
|
internals.pagesByScriptId,
|
|
89
198
|
hashesByComponent
|
|
@@ -98,24 +207,17 @@ function foldClientDependencies(graph, internals) {
|
|
|
98
207
|
baseHashes.set(component, hasher.digest("hex"));
|
|
99
208
|
}
|
|
100
209
|
}
|
|
101
|
-
function collectContentEntryHashes(graph, root) {
|
|
210
|
+
function collectContentEntryHashes(graph, root, transitiveHashes) {
|
|
102
211
|
const entryHashes = /* @__PURE__ */ new Map();
|
|
103
212
|
for (const id of graph.getModuleIds()) {
|
|
104
213
|
if (!hasContentFlag(id, PROPAGATED_ASSET_FLAG)) continue;
|
|
105
214
|
const renderModuleId = removeQueryString(id);
|
|
106
215
|
const key = rootRelativePath(root, renderModuleId, false);
|
|
107
|
-
const
|
|
108
|
-
entryHashes.set(key,
|
|
216
|
+
const hash = transitiveHashes.get(renderModuleId);
|
|
217
|
+
if (hash) entryHashes.set(key, hash);
|
|
109
218
|
}
|
|
110
219
|
return entryHashes;
|
|
111
220
|
}
|
|
112
|
-
function pageContainsServerIsland(graph, ids) {
|
|
113
|
-
for (const id of ids) {
|
|
114
|
-
const serverComponents = graph.getModuleInfo(id)?.meta?.astro?.serverComponents;
|
|
115
|
-
if (serverComponents?.length) return true;
|
|
116
|
-
}
|
|
117
|
-
return false;
|
|
118
|
-
}
|
|
119
221
|
function pluginIncremental(internals, root) {
|
|
120
222
|
return {
|
|
121
223
|
name: "@astro/plugin-incremental",
|
|
@@ -127,6 +229,7 @@ function pluginIncremental(internals, root) {
|
|
|
127
229
|
foldClientDependencies(this, internals);
|
|
128
230
|
return;
|
|
129
231
|
}
|
|
232
|
+
const transitiveGraph = createTransitiveGraphCache(this);
|
|
130
233
|
const hashes = /* @__PURE__ */ new Map();
|
|
131
234
|
const serverIslandComponents = /* @__PURE__ */ new Set();
|
|
132
235
|
for (const id of this.getModuleIds()) {
|
|
@@ -135,14 +238,19 @@ function pluginIncremental(internals, root) {
|
|
|
135
238
|
if (!moduleIsTopLevelPage(info)) continue;
|
|
136
239
|
const pageData = getPageDataByViteID(internals, info.id);
|
|
137
240
|
if (!pageData) continue;
|
|
138
|
-
const
|
|
139
|
-
|
|
140
|
-
|
|
241
|
+
const hash = transitiveGraph.hashes.get(info.id);
|
|
242
|
+
if (!hash) continue;
|
|
243
|
+
hashes.set(pageData.component, hash);
|
|
244
|
+
if (transitiveGraph.serverIslandModules.has(info.id)) {
|
|
141
245
|
serverIslandComponents.add(pageData.component);
|
|
142
246
|
}
|
|
143
247
|
}
|
|
144
248
|
internals.pageDependencyHashes = hashes;
|
|
145
|
-
internals.contentEntryRenderHashes = collectContentEntryHashes(
|
|
249
|
+
internals.contentEntryRenderHashes = collectContentEntryHashes(
|
|
250
|
+
this,
|
|
251
|
+
root,
|
|
252
|
+
transitiveGraph.hashes
|
|
253
|
+
);
|
|
146
254
|
internals.serverIslandPageComponents = serverIslandComponents;
|
|
147
255
|
}
|
|
148
256
|
};
|
package/dist/core/constants.js
CHANGED
package/dist/core/dev/dev.js
CHANGED
|
@@ -26,7 +26,7 @@ async function dev(inlineConfig) {
|
|
|
26
26
|
await telemetry.record([]);
|
|
27
27
|
const restart = await createContainerWithAutomaticRestart({ inlineConfig, fs });
|
|
28
28
|
const logger = restart.container.logger;
|
|
29
|
-
const currentVersion = "7.2.
|
|
29
|
+
const currentVersion = "7.2.2";
|
|
30
30
|
const isPrerelease = currentVersion.includes("-");
|
|
31
31
|
if (!isPrerelease) {
|
|
32
32
|
try {
|
|
@@ -32,6 +32,22 @@ export declare function serializeLockFile(data: LockFileData): string;
|
|
|
32
32
|
* Signal 0 does not kill the process — it only checks whether the process exists.
|
|
33
33
|
*/
|
|
34
34
|
export declare function isProcessAlive(pid: number): boolean;
|
|
35
|
+
/**
|
|
36
|
+
* Check whether a process command points to the Astro CLI.
|
|
37
|
+
*/
|
|
38
|
+
export declare function isAstroCommand(command: string): boolean;
|
|
39
|
+
interface ProcessInfo {
|
|
40
|
+
pid: number;
|
|
41
|
+
cmd?: string;
|
|
42
|
+
}
|
|
43
|
+
type ProcessLookup = (by: 'pid', value: number, options: {
|
|
44
|
+
logLevel: 'error';
|
|
45
|
+
}) => Promise<ProcessInfo[]>;
|
|
46
|
+
/**
|
|
47
|
+
* Check whether the live process recorded in a lock file is still Astro.
|
|
48
|
+
* If the command cannot be inspected, keep the existing PID-only behavior.
|
|
49
|
+
*/
|
|
50
|
+
export declare function isLockFileProcessAlive(data: LockFileData, find?: ProcessLookup): Promise<boolean>;
|
|
35
51
|
/**
|
|
36
52
|
* Read the lock file from disk. Returns null if it doesn't exist or is invalid.
|
|
37
53
|
*/
|
|
@@ -58,8 +74,9 @@ export declare function evaluateExistingServer(data: LockFileData | null, alive:
|
|
|
58
74
|
*/
|
|
59
75
|
export declare function killDevServer(root: URL, data: LockFileData): Promise<void>;
|
|
60
76
|
/**
|
|
61
|
-
* Check for an existing server by reading the lock file and checking process
|
|
77
|
+
* Check for an existing server by reading the lock file and checking process identity.
|
|
62
78
|
* Automatically cleans up stale lock files.
|
|
63
79
|
* Returns the server info if a live server is found, null otherwise.
|
|
64
80
|
*/
|
|
65
|
-
export declare function checkExistingServer(root: URL, command?: ServerCommand): LockFileData | null
|
|
81
|
+
export declare function checkExistingServer(root: URL, command?: ServerCommand): Promise<LockFileData | null>;
|
|
82
|
+
export {};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { existsSync, readFileSync, unlinkSync, writeFileSync, mkdirSync } from "node:fs";
|
|
2
2
|
import { fileURLToPath } from "node:url";
|
|
3
|
+
import findProcess from "find-process";
|
|
3
4
|
const GRACEFUL_SHUTDOWN_TIMEOUT = 5e3;
|
|
4
5
|
function getLockFileURL(root, command = "dev") {
|
|
5
6
|
return new URL(`.astro/${command}.json`, root);
|
|
@@ -42,6 +43,23 @@ function isProcessAlive(pid) {
|
|
|
42
43
|
return false;
|
|
43
44
|
}
|
|
44
45
|
}
|
|
46
|
+
const ASTRO_COMMAND_PATTERN = /(?:^|[\\/\s"'])(?:astro[\\/]bin[\\/]astro\.mjs|\.bin[\\/]astro(?:\.cmd)?)(?=$|[\s"'])/i;
|
|
47
|
+
function isAstroCommand(command) {
|
|
48
|
+
return ASTRO_COMMAND_PATTERN.test(command);
|
|
49
|
+
}
|
|
50
|
+
async function isLockFileProcessAlive(data, find = findProcess) {
|
|
51
|
+
if (!isProcessAlive(data.pid)) {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
try {
|
|
55
|
+
const processInfo = (await find("pid", data.pid, { logLevel: "error" })).find(
|
|
56
|
+
({ pid }) => pid === data.pid
|
|
57
|
+
);
|
|
58
|
+
return processInfo?.cmd === void 0 || isAstroCommand(processInfo.cmd);
|
|
59
|
+
} catch {
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
45
63
|
function readLockFile(root, command = "dev") {
|
|
46
64
|
const lockFileURL = getLockFileURL(root, command);
|
|
47
65
|
try {
|
|
@@ -98,9 +116,12 @@ async function killDevServer(root, data) {
|
|
|
98
116
|
}
|
|
99
117
|
removeLockFile(root);
|
|
100
118
|
}
|
|
101
|
-
function checkExistingServer(root, command = "dev") {
|
|
119
|
+
async function checkExistingServer(root, command = "dev") {
|
|
102
120
|
const data = readLockFile(root, command);
|
|
103
|
-
const result = evaluateExistingServer(
|
|
121
|
+
const result = evaluateExistingServer(
|
|
122
|
+
data,
|
|
123
|
+
data !== null && await isLockFileProcessAlive(data)
|
|
124
|
+
);
|
|
104
125
|
if (result === null) {
|
|
105
126
|
return null;
|
|
106
127
|
}
|
|
@@ -115,6 +136,8 @@ export {
|
|
|
115
136
|
checkExistingServer,
|
|
116
137
|
evaluateExistingServer,
|
|
117
138
|
getLogFileURL,
|
|
139
|
+
isAstroCommand,
|
|
140
|
+
isLockFileProcessAlive,
|
|
118
141
|
isProcessAlive,
|
|
119
142
|
killDevServer,
|
|
120
143
|
parseLockFile,
|
package/dist/core/util.js
CHANGED
|
@@ -47,8 +47,8 @@ function resolvePages(config) {
|
|
|
47
47
|
return new URL("./pages", config.srcDir);
|
|
48
48
|
}
|
|
49
49
|
function isInPagesDir(file, config) {
|
|
50
|
-
const pagesDir = resolvePages(config)
|
|
51
|
-
return file.toString().startsWith(pagesDir
|
|
50
|
+
const pagesDir = `${resolvePages(config).toString()}/`;
|
|
51
|
+
return file.toString().startsWith(pagesDir);
|
|
52
52
|
}
|
|
53
53
|
function isInjectedRoute(file, settings) {
|
|
54
54
|
let fileURL = file.toString();
|
|
@@ -76,7 +76,7 @@ function astroDevCssPlugin({
|
|
|
76
76
|
server = viteServer;
|
|
77
77
|
},
|
|
78
78
|
applyToEnvironment(env) {
|
|
79
|
-
return env.name === ASTRO_VITE_ENVIRONMENT_NAMES.ssr || env.name === ASTRO_VITE_ENVIRONMENT_NAMES.client || env.name === ASTRO_VITE_ENVIRONMENT_NAMES.prerender;
|
|
79
|
+
return command === "dev" && env.name === ASTRO_VITE_ENVIRONMENT_NAMES.astro || env.name === ASTRO_VITE_ENVIRONMENT_NAMES.ssr || env.name === ASTRO_VITE_ENVIRONMENT_NAMES.client || env.name === ASTRO_VITE_ENVIRONMENT_NAMES.prerender;
|
|
80
80
|
},
|
|
81
81
|
resolveId: {
|
|
82
82
|
filter: {
|
|
@@ -163,7 +163,7 @@ function astroDevCssPlugin({
|
|
|
163
163
|
{
|
|
164
164
|
name: MODULE_DEV_CSS_ALL,
|
|
165
165
|
applyToEnvironment(env) {
|
|
166
|
-
return env.name === ASTRO_VITE_ENVIRONMENT_NAMES.ssr || env.name === ASTRO_VITE_ENVIRONMENT_NAMES.client || env.name === ASTRO_VITE_ENVIRONMENT_NAMES.prerender;
|
|
166
|
+
return command === "dev" && env.name === ASTRO_VITE_ENVIRONMENT_NAMES.astro || env.name === ASTRO_VITE_ENVIRONMENT_NAMES.ssr || env.name === ASTRO_VITE_ENVIRONMENT_NAMES.client || env.name === ASTRO_VITE_ENVIRONMENT_NAMES.prerender;
|
|
167
167
|
},
|
|
168
168
|
resolveId: {
|
|
169
169
|
filter: {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "astro",
|
|
3
|
-
"version": "7.2.
|
|
3
|
+
"version": "7.2.2",
|
|
4
4
|
"description": "Astro is a modern site builder with web best practices, performance, and DX front-of-mind.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"author": "withastro",
|
|
@@ -112,6 +112,7 @@
|
|
|
112
112
|
"dset": "^3.1.4",
|
|
113
113
|
"es-module-lexer": "^2.0.0",
|
|
114
114
|
"esbuild": "^0.28.0",
|
|
115
|
+
"find-process": "^2.1.1",
|
|
115
116
|
"flattie": "^1.1.1",
|
|
116
117
|
"fontace": "~0.4.1",
|
|
117
118
|
"get-tsconfig": "5.0.0-beta.4",
|
|
@@ -145,9 +146,9 @@
|
|
|
145
146
|
"xxhash-wasm": "^1.1.0",
|
|
146
147
|
"yargs-parser": "^22.0.0",
|
|
147
148
|
"zod": "^4.3.6",
|
|
148
|
-
"@astrojs/
|
|
149
|
+
"@astrojs/telemetry": "3.3.3",
|
|
149
150
|
"@astrojs/markdown-satteri": "0.3.5",
|
|
150
|
-
"@astrojs/
|
|
151
|
+
"@astrojs/internal-helpers": "0.10.2"
|
|
151
152
|
},
|
|
152
153
|
"optionalDependencies": {
|
|
153
154
|
"sharp": "^0.34.0 || ^0.35.0"
|
|
@@ -185,8 +186,8 @@
|
|
|
185
186
|
"typescript": "^6.0.3",
|
|
186
187
|
"undici": "^7.22.0",
|
|
187
188
|
"vitest": "^4.1.0",
|
|
188
|
-
"@astrojs/check": "0.9.10",
|
|
189
189
|
"@astrojs/markdown-remark": "7.2.2",
|
|
190
|
+
"@astrojs/check": "0.9.10",
|
|
190
191
|
"astro-scripts": "0.0.14"
|
|
191
192
|
},
|
|
192
193
|
"engines": {
|