full site update

This commit is contained in:
2025-07-24 18:46:24 +02:00
parent bfe2b90d8d
commit 37a6e0ab31
6912 changed files with 540482 additions and 361712 deletions

View File

@@ -1,19 +1,22 @@
import { promises as fs, existsSync } from "node:fs";
import { existsSync, promises as fs } from "node:fs";
import * as devalue from "devalue";
import { Traverse } from "neotraverse/modern";
import { imageSrcToImportId, importIdToSymbolName } from "../assets/utils/resolveImports.js";
import { AstroError, AstroErrorData } from "../core/errors/index.js";
import { IMAGE_IMPORT_PREFIX } from "./consts.js";
import { DataStore } from "./data-store.js";
import { ImmutableDataStore } from "./data-store.js";
import { contentModuleToId } from "./utils.js";
const SAVE_DEBOUNCE_MS = 500;
class MutableDataStore extends DataStore {
const MAX_DEPTH = 10;
class MutableDataStore extends ImmutableDataStore {
#file;
#assetsFile;
#modulesFile;
#saveTimeout;
#assetsSaveTimeout;
#modulesSaveTimeout;
#savePromise;
#savePromiseResolve;
#dirty = false;
#assetsDirty = false;
#modulesDirty = false;
@@ -61,7 +64,7 @@ class MutableDataStore extends DataStore {
this.#assetsFile = filePath;
if (this.#assetImports.size === 0) {
try {
await fs.writeFile(filePath, "export default new Map();");
await this.#writeFileAtomic(filePath, "export default new Map();");
} catch (err) {
throw new AstroError(AstroErrorData.UnknownFilesystemError, { cause: err });
}
@@ -73,7 +76,7 @@ class MutableDataStore extends DataStore {
const exports = [];
this.#assetImports.forEach((id) => {
const symbol = importIdToSymbolName(id);
imports.push(`import ${symbol} from '${id}';`);
imports.push(`import ${symbol} from ${JSON.stringify(id)};`);
exports.push(`[${JSON.stringify(id)}, ${symbol}]`);
});
const code = (
@@ -84,7 +87,7 @@ export default new Map([${exports.join(", ")}]);
`
);
try {
await fs.writeFile(filePath, code);
await this.#writeFileAtomic(filePath, code);
} catch (err) {
throw new AstroError(AstroErrorData.UnknownFilesystemError, { cause: err });
}
@@ -94,7 +97,7 @@ export default new Map([${exports.join(", ")}]);
this.#modulesFile = filePath;
if (this.#moduleImports.size === 0) {
try {
await fs.writeFile(filePath, "export default new Map();");
await this.#writeFileAtomic(filePath, "export default new Map();");
} catch (err) {
throw new AstroError(AstroErrorData.UnknownFilesystemError, { cause: err });
}
@@ -104,28 +107,41 @@ export default new Map([${exports.join(", ")}]);
}
const lines = [];
for (const [fileName, specifier] of this.#moduleImports) {
lines.push(`['${fileName}', () => import('${specifier}')]`);
lines.push(`[${JSON.stringify(fileName)}, () => import(${JSON.stringify(specifier)})]`);
}
const code = `
export default new Map([
${lines.join(",\n")}]);
`;
try {
await fs.writeFile(filePath, code);
await this.#writeFileAtomic(filePath, code);
} catch (err) {
throw new AstroError(AstroErrorData.UnknownFilesystemError, { cause: err });
}
this.#modulesDirty = false;
}
#maybeResolveSavePromise() {
if (!this.#saveTimeout && !this.#assetsSaveTimeout && !this.#modulesSaveTimeout && this.#savePromiseResolve) {
this.#savePromiseResolve();
this.#savePromiseResolve = void 0;
this.#savePromise = void 0;
}
}
#writeAssetsImportsDebounced() {
this.#assetsDirty = true;
if (this.#assetsFile) {
if (this.#assetsSaveTimeout) {
clearTimeout(this.#assetsSaveTimeout);
}
this.#assetsSaveTimeout = setTimeout(() => {
if (!this.#savePromise) {
this.#savePromise = new Promise((resolve) => {
this.#savePromiseResolve = resolve;
});
}
this.#assetsSaveTimeout = setTimeout(async () => {
this.#assetsSaveTimeout = void 0;
this.writeAssetImports(this.#assetsFile);
await this.writeAssetImports(this.#assetsFile);
this.#maybeResolveSavePromise();
}, SAVE_DEBOUNCE_MS);
}
}
@@ -135,22 +151,73 @@ ${lines.join(",\n")}]);
if (this.#modulesSaveTimeout) {
clearTimeout(this.#modulesSaveTimeout);
}
this.#modulesSaveTimeout = setTimeout(() => {
if (!this.#savePromise) {
this.#savePromise = new Promise((resolve) => {
this.#savePromiseResolve = resolve;
});
}
this.#modulesSaveTimeout = setTimeout(async () => {
this.#modulesSaveTimeout = void 0;
this.writeModuleImports(this.#modulesFile);
await this.writeModuleImports(this.#modulesFile);
this.#maybeResolveSavePromise();
}, SAVE_DEBOUNCE_MS);
}
}
// Skips the debounce and writes to disk immediately
async #saveToDiskNow() {
if (this.#saveTimeout) {
clearTimeout(this.#saveTimeout);
}
this.#saveTimeout = void 0;
if (this.#file) {
await this.writeToDisk();
}
this.#maybeResolveSavePromise();
}
#saveToDiskDebounced() {
this.#dirty = true;
if (this.#file) {
if (this.#saveTimeout) {
clearTimeout(this.#saveTimeout);
if (this.#saveTimeout) {
clearTimeout(this.#saveTimeout);
}
if (!this.#savePromise) {
this.#savePromise = new Promise((resolve) => {
this.#savePromiseResolve = resolve;
});
}
this.#saveTimeout = setTimeout(async () => {
this.#saveTimeout = void 0;
if (this.#file) {
await this.writeToDisk();
}
this.#maybeResolveSavePromise();
}, SAVE_DEBOUNCE_MS);
}
#writing = /* @__PURE__ */ new Set();
#pending = /* @__PURE__ */ new Set();
async #writeFileAtomic(filePath, data, depth = 0) {
if (depth > MAX_DEPTH) {
return;
}
const fileKey = filePath.toString();
if (this.#writing.has(fileKey)) {
this.#pending.add(fileKey);
return;
}
this.#writing.add(fileKey);
const tempFile = filePath instanceof URL ? new URL(`${filePath.href}.tmp`) : `${filePath}.tmp`;
try {
const oldData = await fs.readFile(filePath, "utf-8").catch(() => "");
if (oldData === data) {
return;
}
await fs.writeFile(tempFile, data);
await fs.rename(tempFile, filePath);
} finally {
this.#writing.delete(fileKey);
if (this.#pending.has(fileKey)) {
this.#pending.delete(fileKey);
await this.#writeFileAtomic(filePath, data, depth + 1);
}
this.#saveTimeout = setTimeout(() => {
this.#saveTimeout = void 0;
this.writeToDisk(this.#file);
}, SAVE_DEBOUNCE_MS);
}
}
scopedStore(collectionName) {
@@ -159,7 +226,17 @@ ${lines.join(",\n")}]);
entries: () => this.entries(collectionName),
values: () => this.values(collectionName),
keys: () => this.keys(collectionName),
set: ({ id: key, data, body, filePath, deferredRender, digest, rendered, assetImports }) => {
set: ({
id: key,
data,
body,
filePath,
deferredRender,
digest,
rendered,
assetImports,
legacyId
}) => {
if (!key) {
throw new Error(`ID must be a non-empty string`);
}
@@ -200,6 +277,9 @@ ${lines.join(",\n")}]);
if (rendered) {
entry.rendered = rendered;
}
if (legacyId) {
entry.legacyId = legacyId;
}
if (deferredRender) {
entry.deferredRender = deferredRender;
if (filePath) {
@@ -229,17 +309,30 @@ ${lines.join(",\n")}]);
has: (key) => this.has(collectionKey, key)
};
}
/**
* Returns a promise that resolves when all pending saves are complete.
* This includes any in-progress debounced saves for the data store, asset imports, and module imports.
*/
async waitUntilSaveComplete() {
if (!this.#savePromise) {
return Promise.resolve();
}
await this.#saveToDiskNow();
return this.#savePromise;
}
toString() {
return devalue.stringify(this._collections);
}
async writeToDisk(filePath) {
async writeToDisk() {
if (!this.#dirty) {
return;
}
if (!this.#file) {
throw new AstroError(AstroErrorData.UnknownFilesystemError);
}
try {
await fs.writeFile(filePath, this.toString());
this.#file = filePath;
this.#dirty = false;
await this.#writeFileAtomic(this.#file, this.toString());
} catch (err) {
throw new AstroError(AstroErrorData.UnknownFilesystemError, { cause: err });
}
@@ -270,11 +363,17 @@ ${lines.join(",\n")}]);
try {
if (existsSync(filePath)) {
const data = await fs.readFile(filePath, "utf-8");
return MutableDataStore.fromString(data);
const store2 = await MutableDataStore.fromString(data);
store2.#file = filePath;
return store2;
} else {
await fs.mkdir(new URL("./", filePath), { recursive: true });
}
} catch {
}
return new MutableDataStore();
const store = new MutableDataStore();
store.#file = filePath;
return store;
}
}
export {