astro 7.2.9 → 7.3.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 +1 -1
- package/dist/assets/build/generate.js +3 -1
- package/dist/assets/endpoint/dev.js +6 -5
- package/dist/assets/endpoint/generic.js +5 -4
- package/dist/assets/endpoint/node.js +3 -3
- package/dist/assets/endpoint/shared.d.ts +3 -1
- package/dist/assets/endpoint/shared.js +9 -3
- package/dist/assets/fonts/config.d.ts +2 -2
- package/dist/assets/internal.d.ts +2 -1
- package/dist/assets/internal.js +11 -10
- package/dist/assets/services/service.d.ts +8 -7
- package/dist/assets/services/sharp.js +5 -5
- package/dist/assets/vite-plugin-assets.js +22 -5
- package/dist/cli/add/index.js +3 -1
- package/dist/cli/index.js +1 -1
- package/dist/cli/infra/build-time-astro-version-provider.js +1 -1
- package/dist/cli/preferences/index.js +32 -30
- package/dist/cli/preview/index.js +44 -1
- package/dist/cli/telemetry/index.d.ts +2 -1
- package/dist/cli/telemetry/index.js +2 -2
- package/dist/content/content-layer.js +3 -3
- package/dist/content/data-store-writer.d.ts +18 -5
- package/dist/content/data-store-writer.js +11 -3
- package/dist/content/index.d.ts +1 -1
- package/dist/content/index.js +5 -1
- package/dist/content/mutable-data-store.d.ts +13 -1
- package/dist/content/mutable-data-store.js +35 -5
- package/dist/content/runtime.d.ts +8 -3
- package/dist/content/runtime.js +43 -32
- package/dist/content/types-generator.js +2 -1
- package/dist/content/utils.d.ts +5 -35
- package/dist/content/utils.js +71 -65
- package/dist/content/vite-plugin-content-imports.js +2 -1
- package/dist/content/vite-plugin-content-virtual-mod.d.ts +11 -1
- package/dist/content/vite-plugin-content-virtual-mod.js +36 -3
- package/dist/core/app/entrypoints/index.d.ts +4 -2
- package/dist/core/app/entrypoints/index.js +13 -11
- package/dist/core/app/entrypoints/virtual/dev.js +2 -2
- package/dist/core/app/prerender.d.ts +32 -0
- package/dist/core/app/prerender.js +22 -0
- package/dist/core/build/default-prerenderer.js +5 -2
- package/dist/core/build/generate.d.ts +3 -1
- package/dist/core/build/generate.js +40 -62
- package/dist/core/build/plugins/plugin-manifest.js +1 -1
- package/dist/core/cache/handler.js +12 -1
- package/dist/core/cache/memory-provider.js +35 -8
- package/dist/core/cache/runtime/cache.js +3 -0
- package/dist/core/cache/types.d.ts +2 -0
- package/dist/core/config/schemas/base.d.ts +5 -5
- package/dist/core/config/schemas/relative.d.ts +7 -7
- package/dist/core/constants.js +1 -1
- package/dist/core/cookies/cookies.d.ts +2 -1
- package/dist/core/cookies/cookies.js +5 -4
- package/dist/core/dev/dev.js +5 -3
- package/dist/core/fetch/fetch-state.d.ts +1 -1
- package/dist/core/fetch/fetch-state.js +4 -23
- package/dist/core/logger/core.d.ts +2 -0
- package/dist/core/logger/core.js +14 -0
- package/dist/core/logger/vite-plugin.d.ts +13 -2
- package/dist/core/logger/vite-plugin.js +8 -7
- package/dist/core/messages/runtime.js +1 -1
- package/dist/core/middleware/index.js +14 -1
- package/dist/core/render-scope/collect.d.ts +24 -0
- package/dist/core/render-scope/collect.js +27 -0
- package/dist/core/render-scope/node-scope.d.ts +10 -0
- package/dist/core/render-scope/node-scope.js +8 -0
- package/dist/core/render-scope/record.d.ts +14 -0
- package/dist/core/render-scope/record.js +12 -0
- package/dist/core/render-scope/scope.d.ts +38 -0
- package/dist/core/render-scope/scope.js +28 -0
- package/dist/core/rewrites/handler.js +1 -1
- package/dist/core/routing/create-manifest.js +5 -1
- package/dist/core/server-islands/endpoint.js +4 -1
- package/dist/core/server-islands/vite-plugin-server-islands.js +2 -2
- package/dist/core/sync/index.js +1 -1
- package/dist/core/util.d.ts +0 -9
- package/dist/core/util.js +0 -34
- package/dist/core/viteUtils.d.ts +1 -13
- package/dist/core/viteUtils.js +3 -33
- package/dist/types/public/integrations.d.ts +20 -13
- package/dist/vite-plugin-app/createAstroServerApp.js +1 -1
- package/dist/vite-plugin-routes/index.js +22 -8
- package/package.json +6 -6
- package/templates/content/module.mjs +13 -5
- package/dist/core/build/incremental-content-collector.d.ts +0 -10
- package/dist/core/build/incremental-content-collector.js +0 -33
- package/dist/core/build/incremental-image-collector.d.ts +0 -11
- package/dist/core/build/incremental-image-collector.js +0 -32
|
@@ -5,6 +5,7 @@ import { resolveRoot } from "../../core/config/config.js";
|
|
|
5
5
|
import { printHelp } from "../../core/messages/runtime.js";
|
|
6
6
|
import previewServer from "../../core/preview/index.js";
|
|
7
7
|
import { isRunByAgent } from "../agent.js";
|
|
8
|
+
import { isIgnoreLock } from "../dev/index.js";
|
|
8
9
|
import { createLoggerFromFlags, flagsToAstroInlineConfig } from "../flags.js";
|
|
9
10
|
import { background, logs, previewServerCommand, status, stop } from "../server.js";
|
|
10
11
|
async function preview({ flags }) {
|
|
@@ -24,6 +25,10 @@ async function preview({ flags }) {
|
|
|
24
25
|
["--host", `Listen on all addresses, including LAN and public addresses.`],
|
|
25
26
|
["--host <custom-address>", `Expose on a network IP address at <custom-address>`],
|
|
26
27
|
["--open", "Automatically open the app in the browser on server start"],
|
|
28
|
+
[
|
|
29
|
+
"--ignore-lock",
|
|
30
|
+
"Start the preview server even if another one is already running, without checking or writing the lock file."
|
|
31
|
+
],
|
|
27
32
|
[
|
|
28
33
|
"--allowed-hosts",
|
|
29
34
|
"Specify a comma-separated list of allowed hosts or allow any hostname."
|
|
@@ -41,6 +46,8 @@ async function preview({ flags }) {
|
|
|
41
46
|
if (agentDetected) {
|
|
42
47
|
flags.json = true;
|
|
43
48
|
}
|
|
49
|
+
const ignoreLock = isIgnoreLock(flags);
|
|
50
|
+
const wantsBackground = !!flags.background || agentDetected;
|
|
44
51
|
const logger = createLoggerFromFlags(flags);
|
|
45
52
|
const subcommand = flags._[3]?.toString();
|
|
46
53
|
if (subcommand === "stop") {
|
|
@@ -55,7 +62,29 @@ async function preview({ flags }) {
|
|
|
55
62
|
await logs({ flags, logger, config: previewServerCommand });
|
|
56
63
|
return;
|
|
57
64
|
}
|
|
58
|
-
if (
|
|
65
|
+
if (ignoreLock) {
|
|
66
|
+
if (flags.force) {
|
|
67
|
+
throw new Error(
|
|
68
|
+
[
|
|
69
|
+
"`--force` and `--ignore-lock` cannot be used together.",
|
|
70
|
+
"",
|
|
71
|
+
"`--force` replaces the existing preview server; `--ignore-lock` starts a new one alongside it without touching the lock file. Choose one."
|
|
72
|
+
].join("\n")
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
if (ignoreLock && wantsBackground) {
|
|
77
|
+
const reason = flags.background ? "`--background`" : "an auto-detected AI agent environment, which runs the preview server in the background automatically";
|
|
78
|
+
throw new Error(
|
|
79
|
+
[
|
|
80
|
+
`\`--ignore-lock\` cannot be used together with ${reason}.`,
|
|
81
|
+
"",
|
|
82
|
+
"Background preview servers rely on the lock file so `astro preview stop`, `astro preview status`, and `astro preview logs` can find them.",
|
|
83
|
+
"Run the preview server in the foreground to use --ignore-lock."
|
|
84
|
+
].join("\n")
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
if (wantsBackground) {
|
|
59
88
|
await background({ flags, logger, config: previewServerCommand });
|
|
60
89
|
return;
|
|
61
90
|
}
|
|
@@ -69,6 +98,20 @@ Run \`astro preview --help\` to see available commands.`
|
|
|
69
98
|
process.exit(1);
|
|
70
99
|
}
|
|
71
100
|
const root = pathToFileURL(resolveRoot(flags.root) + "/");
|
|
101
|
+
if (ignoreLock) {
|
|
102
|
+
const existingServer2 = await checkExistingServer(root, "preview");
|
|
103
|
+
if (existingServer2) {
|
|
104
|
+
logger.info(
|
|
105
|
+
"SKIP_FORMAT",
|
|
106
|
+
[
|
|
107
|
+
`Starting a new preview server alongside the one already running at ${existingServer2.url} (pid ${existingServer2.pid}).`,
|
|
108
|
+
"This instance is not tracked by `astro preview stop`, `astro preview status`, or `astro preview logs`."
|
|
109
|
+
].join("\n")
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
const inlineConfig2 = flagsToAstroInlineConfig(flags);
|
|
113
|
+
return await previewServer(inlineConfig2);
|
|
114
|
+
}
|
|
72
115
|
const existingServer = await checkExistingServer(root, "preview");
|
|
73
116
|
if (existingServer) {
|
|
74
117
|
const message = [
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import type { AstroLogger } from '../../core/logger/core.js';
|
|
1
2
|
import { type Flags } from '../flags.js';
|
|
2
3
|
interface TelemetryOptions {
|
|
3
4
|
flags: Flags;
|
|
4
5
|
}
|
|
5
|
-
export declare function notify(): Promise<void>;
|
|
6
|
+
export declare function notify(logger: AstroLogger): Promise<void>;
|
|
6
7
|
export declare function update(subcommand: string, { flags }: TelemetryOptions): Promise<void>;
|
|
7
8
|
export {};
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import * as msg from "../../core/messages/runtime.js";
|
|
2
2
|
import { telemetry } from "../../events/index.js";
|
|
3
3
|
import { createLoggerFromFlags } from "../flags.js";
|
|
4
|
-
async function notify() {
|
|
4
|
+
async function notify(logger) {
|
|
5
5
|
await telemetry.notify(() => {
|
|
6
|
-
|
|
6
|
+
logger.info("SKIP_FORMAT", msg.telemetryNotice() + "\n");
|
|
7
7
|
return true;
|
|
8
8
|
});
|
|
9
9
|
}
|
|
@@ -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.3.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.3.0") {
|
|
208
|
+
this.#store.metaStore().set("astro-version", "7.3.0");
|
|
209
209
|
}
|
|
210
210
|
if (currentConfigDigest) {
|
|
211
211
|
this.#store.metaStore().set("content-config-digest", currentConfigDigest);
|
|
@@ -8,8 +8,17 @@ export type DataStoreManifest = Record<string, string[]>;
|
|
|
8
8
|
* (build/dev) and are never imported at runtime.
|
|
9
9
|
*/
|
|
10
10
|
export interface DataStoreWriter {
|
|
11
|
-
/**
|
|
12
|
-
|
|
11
|
+
/**
|
|
12
|
+
* Serialize and persist the given collections.
|
|
13
|
+
* Resolves to `true` if the data on disk changed, or `false` if the write
|
|
14
|
+
* was skipped because the persisted data was already identical.
|
|
15
|
+
*/
|
|
16
|
+
write(collections: Map<string, Map<string, any>>): Promise<boolean>;
|
|
17
|
+
/**
|
|
18
|
+
* The file whose write commits a store update: the store file itself, or
|
|
19
|
+
* the manifest for chunked stores.
|
|
20
|
+
*/
|
|
21
|
+
readonly target: PathLike;
|
|
13
22
|
}
|
|
14
23
|
/**
|
|
15
24
|
* Serialize collections to a deterministic devalue string.
|
|
@@ -35,15 +44,18 @@ export declare function chunkString(str: string, maxBytes: number): string[];
|
|
|
35
44
|
* partial reads. If the file already contains identical data, the write is
|
|
36
45
|
* skipped. Callers are responsible for serializing concurrent writes to the
|
|
37
46
|
* same file.
|
|
47
|
+
*
|
|
48
|
+
* Returns `true` if the file was written, or `false` if the write was skipped.
|
|
38
49
|
*/
|
|
39
|
-
export declare function writeFileAtomic(file: PathLike, data: string): Promise<
|
|
50
|
+
export declare function writeFileAtomic(file: PathLike, data: string): Promise<boolean>;
|
|
40
51
|
/**
|
|
41
52
|
* A {@link DataStoreWriter} that serializes the whole store to a single file.
|
|
42
53
|
*/
|
|
43
54
|
export declare class FileWriter implements DataStoreWriter {
|
|
44
55
|
#private;
|
|
45
56
|
constructor(file: PathLike);
|
|
46
|
-
|
|
57
|
+
get target(): PathLike;
|
|
58
|
+
write(collections: Map<string, Map<string, any>>): Promise<boolean>;
|
|
47
59
|
}
|
|
48
60
|
/**
|
|
49
61
|
* A {@link DataStoreWriter} that splits the store across many content-addressed
|
|
@@ -60,5 +72,6 @@ export declare class FileWriter implements DataStoreWriter {
|
|
|
60
72
|
export declare class ChunkedWriter implements DataStoreWriter {
|
|
61
73
|
#private;
|
|
62
74
|
constructor(dir: URL, chunkSize: number);
|
|
63
|
-
|
|
75
|
+
get target(): PathLike;
|
|
76
|
+
write(collections: Map<string, Map<string, any>>): Promise<boolean>;
|
|
64
77
|
}
|
|
@@ -47,18 +47,22 @@ async function writeFileAtomic(file, data) {
|
|
|
47
47
|
const tempFile = file instanceof URL ? new URL(`${file.href}.tmp`) : `${file}.tmp`;
|
|
48
48
|
const oldData = await fs.readFile(file, "utf-8").catch(() => "");
|
|
49
49
|
if (oldData === data) {
|
|
50
|
-
return;
|
|
50
|
+
return false;
|
|
51
51
|
}
|
|
52
52
|
await fs.writeFile(tempFile, data);
|
|
53
53
|
await fs.rename(tempFile, file);
|
|
54
|
+
return true;
|
|
54
55
|
}
|
|
55
56
|
class FileWriter {
|
|
56
57
|
#file;
|
|
57
58
|
constructor(file) {
|
|
58
59
|
this.#file = file;
|
|
59
60
|
}
|
|
61
|
+
get target() {
|
|
62
|
+
return this.#file;
|
|
63
|
+
}
|
|
60
64
|
async write(collections) {
|
|
61
|
-
await writeFileAtomic(this.#file, serializeDataStore(collections));
|
|
65
|
+
return await writeFileAtomic(this.#file, serializeDataStore(collections));
|
|
62
66
|
}
|
|
63
67
|
}
|
|
64
68
|
class ChunkedWriter {
|
|
@@ -72,6 +76,9 @@ class ChunkedWriter {
|
|
|
72
76
|
this.#manifestFile = new URL(`./${DATA_STORE_MANIFEST_FILE}`, dir);
|
|
73
77
|
this.#chunkSize = chunkSize;
|
|
74
78
|
}
|
|
79
|
+
get target() {
|
|
80
|
+
return this.#manifestFile;
|
|
81
|
+
}
|
|
75
82
|
async write(collections) {
|
|
76
83
|
if (!this.#hasher) {
|
|
77
84
|
this.#hasher = await xxhash();
|
|
@@ -81,9 +88,10 @@ class ChunkedWriter {
|
|
|
81
88
|
for (const [collectionName, entries] of sortCollections(collections)) {
|
|
82
89
|
manifest[collectionName] = await this.#writeCollection(entries);
|
|
83
90
|
}
|
|
84
|
-
await writeFileAtomic(this.#manifestFile, JSON.stringify(manifest));
|
|
91
|
+
const didWrite = await writeFileAtomic(this.#manifestFile, JSON.stringify(manifest));
|
|
85
92
|
this.#writtenFiles.add(DATA_STORE_MANIFEST_FILE);
|
|
86
93
|
emptyDir(this.#dir, this.#writtenFiles);
|
|
94
|
+
return didWrite;
|
|
87
95
|
}
|
|
88
96
|
async #writeCollection(entries) {
|
|
89
97
|
const parts = [];
|
package/dist/content/index.d.ts
CHANGED
|
@@ -3,4 +3,4 @@ export { createContentTypesGenerator } from './types-generator.js';
|
|
|
3
3
|
export { getContentPaths } from './utils.js';
|
|
4
4
|
export { astroContentAssetPropagationPlugin } from './vite-plugin-content-assets.js';
|
|
5
5
|
export { astroContentImportPlugin } from './vite-plugin-content-imports.js';
|
|
6
|
-
export { astroContentVirtualModPlugin } from './vite-plugin-content-virtual-mod.js';
|
|
6
|
+
export { astroContentVirtualModPlugin, attachDataStoreInvalidation, } from './vite-plugin-content-virtual-mod.js';
|
package/dist/content/index.js
CHANGED
|
@@ -3,12 +3,16 @@ import { createContentTypesGenerator } from "./types-generator.js";
|
|
|
3
3
|
import { getContentPaths } from "./utils.js";
|
|
4
4
|
import { astroContentAssetPropagationPlugin } from "./vite-plugin-content-assets.js";
|
|
5
5
|
import { astroContentImportPlugin } from "./vite-plugin-content-imports.js";
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
astroContentVirtualModPlugin,
|
|
8
|
+
attachDataStoreInvalidation
|
|
9
|
+
} from "./vite-plugin-content-virtual-mod.js";
|
|
7
10
|
export {
|
|
8
11
|
astroContentAssetPropagationPlugin,
|
|
9
12
|
astroContentImportPlugin,
|
|
10
13
|
astroContentVirtualModPlugin,
|
|
11
14
|
attachContentServerListeners,
|
|
15
|
+
attachDataStoreInvalidation,
|
|
12
16
|
createContentTypesGenerator,
|
|
13
17
|
getContentPaths
|
|
14
18
|
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type PathLike } from 'node:fs';
|
|
2
|
+
import type { AstroLogger } from '../core/logger/core.js';
|
|
2
3
|
import { type DataEntry, ImmutableDataStore } from './data-store.js';
|
|
3
4
|
/**
|
|
4
5
|
* Extends the DataStore with the ability to change entries and write them to disk.
|
|
@@ -6,6 +7,17 @@ import { type DataEntry, ImmutableDataStore } from './data-store.js';
|
|
|
6
7
|
*/
|
|
7
8
|
export declare class MutableDataStore extends ImmutableDataStore {
|
|
8
9
|
#private;
|
|
10
|
+
/**
|
|
11
|
+
* Registers a listener called with the file path whenever this store writes a
|
|
12
|
+
* file to disk (the data store itself, or the asset/module import files).
|
|
13
|
+
* Writes that are skipped because the data on disk is already identical do
|
|
14
|
+
* not notify. The dev server uses this to invalidate the content virtual
|
|
15
|
+
* modules deterministically, instead of relying on the file watcher to
|
|
16
|
+
* observe the write — on some platforms (notably Windows) the watcher can
|
|
17
|
+
* miss the atomic rename that commits it.
|
|
18
|
+
* Returns a function that removes the listener.
|
|
19
|
+
*/
|
|
20
|
+
onFileWritten(listener: (path: string) => void): () => void;
|
|
9
21
|
set(collectionName: string, key: string, value: unknown): void;
|
|
10
22
|
delete(collectionName: string, key: string): void;
|
|
11
23
|
clear(collectionName: string): void;
|
|
@@ -42,7 +54,7 @@ export declare class MutableDataStore extends ImmutableDataStore {
|
|
|
42
54
|
* manifest exists but can't be read (corrupt cache), it warns and starts
|
|
43
55
|
* empty so loaders rebuild it, rather than failing the sync.
|
|
44
56
|
*/
|
|
45
|
-
static fromDir(dirPath: URL, chunkSize: number): Promise<MutableDataStore>;
|
|
57
|
+
static fromDir(dirPath: URL, chunkSize: number, logger: Pick<AstroLogger, 'warn'>): Promise<MutableDataStore>;
|
|
46
58
|
}
|
|
47
59
|
export interface DataStore {
|
|
48
60
|
get: <TData extends Record<string, unknown> = Record<string, unknown>>(key: string) => DataEntry<TData> | undefined;
|
|
@@ -30,6 +30,32 @@ class MutableDataStore extends ImmutableDataStore {
|
|
|
30
30
|
#moduleImports = /* @__PURE__ */ new Map();
|
|
31
31
|
#writeInProgress = false;
|
|
32
32
|
#writeQueued = false;
|
|
33
|
+
#fileWrittenListeners = /* @__PURE__ */ new Set();
|
|
34
|
+
/**
|
|
35
|
+
* Registers a listener called with the file path whenever this store writes a
|
|
36
|
+
* file to disk (the data store itself, or the asset/module import files).
|
|
37
|
+
* Writes that are skipped because the data on disk is already identical do
|
|
38
|
+
* not notify. The dev server uses this to invalidate the content virtual
|
|
39
|
+
* modules deterministically, instead of relying on the file watcher to
|
|
40
|
+
* observe the write — on some platforms (notably Windows) the watcher can
|
|
41
|
+
* miss the atomic rename that commits it.
|
|
42
|
+
* Returns a function that removes the listener.
|
|
43
|
+
*/
|
|
44
|
+
onFileWritten(listener) {
|
|
45
|
+
this.#fileWrittenListeners.add(listener);
|
|
46
|
+
return () => {
|
|
47
|
+
this.#fileWrittenListeners.delete(listener);
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
#notifyFileWritten(path) {
|
|
51
|
+
if (this.#fileWrittenListeners.size === 0) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
const normalized = path instanceof URL ? fileURLToPath(path) : path.toString();
|
|
55
|
+
for (const listener of this.#fileWrittenListeners) {
|
|
56
|
+
listener(normalized);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
33
59
|
set(collectionName, key, value) {
|
|
34
60
|
const collection = this._collections.get(collectionName) ?? /* @__PURE__ */ new Map();
|
|
35
61
|
collection.set(String(key), value);
|
|
@@ -275,6 +301,7 @@ ${lines.join(",\n")}]);
|
|
|
275
301
|
}
|
|
276
302
|
await fs.writeFile(tempFile, data);
|
|
277
303
|
await fs.rename(tempFile, filePath);
|
|
304
|
+
this.#notifyFileWritten(filePath);
|
|
278
305
|
} finally {
|
|
279
306
|
this.#writing.delete(fileKey);
|
|
280
307
|
if (this.#pending.has(fileKey)) {
|
|
@@ -415,7 +442,10 @@ ${lines.join(",\n")}]);
|
|
|
415
442
|
try {
|
|
416
443
|
this.#dirty = false;
|
|
417
444
|
this.#writeInProgress = true;
|
|
418
|
-
await this.#writer.write(this._collections);
|
|
445
|
+
const didWrite = await this.#writer.write(this._collections);
|
|
446
|
+
if (didWrite) {
|
|
447
|
+
this.#notifyFileWritten(this.#writer.target);
|
|
448
|
+
}
|
|
419
449
|
} catch (err) {
|
|
420
450
|
throw new AstroError(AstroErrorData.UnknownFilesystemError, { cause: err });
|
|
421
451
|
} finally {
|
|
@@ -471,7 +501,7 @@ ${lines.join(",\n")}]);
|
|
|
471
501
|
* manifest exists but can't be read (corrupt cache), it warns and starts
|
|
472
502
|
* empty so loaders rebuild it, rather than failing the sync.
|
|
473
503
|
*/
|
|
474
|
-
static async fromDir(dirPath, chunkSize) {
|
|
504
|
+
static async fromDir(dirPath, chunkSize, logger) {
|
|
475
505
|
const manifestFile = new URL(`./${DATA_STORE_MANIFEST_FILE}`, dirPath);
|
|
476
506
|
if (existsSync(manifestFile)) {
|
|
477
507
|
try {
|
|
@@ -489,9 +519,9 @@ ${lines.join(",\n")}]);
|
|
|
489
519
|
store2.#writer = new ChunkedWriter(dirPath, chunkSize);
|
|
490
520
|
return store2;
|
|
491
521
|
} catch (err) {
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
err
|
|
522
|
+
logger.warn(
|
|
523
|
+
"content",
|
|
524
|
+
`Could not read the chunked data store at ${fileURLToPath(dirPath)}, rebuilding from scratch. ${err instanceof Error ? err.message : err}`
|
|
495
525
|
);
|
|
496
526
|
}
|
|
497
527
|
}
|
|
@@ -8,14 +8,16 @@ import type { LIVE_CONTENT_TYPE } from './consts.js';
|
|
|
8
8
|
import { type DataEntry } from './data-store.js';
|
|
9
9
|
import { LiveCollectionCacheHintError, LiveCollectionError, LiveCollectionValidationError, LiveEntryNotFoundError } from './loaders/errors.js';
|
|
10
10
|
import type { LiveLoader } from './loaders/types.js';
|
|
11
|
+
import type { AstroLogger } from '../core/logger/core.js';
|
|
11
12
|
export { LiveCollectionError, LiveCollectionCacheHintError, LiveEntryNotFoundError, LiveCollectionValidationError, };
|
|
12
13
|
type LiveCollectionConfigMap = Record<string, {
|
|
13
14
|
loader: LiveLoader;
|
|
14
15
|
type: typeof LIVE_CONTENT_TYPE;
|
|
15
16
|
schema?: zCore.$ZodType;
|
|
16
17
|
}>;
|
|
17
|
-
export declare function createGetCollection({ liveCollections, }: {
|
|
18
|
+
export declare function createGetCollection({ liveCollections, logger, }: {
|
|
18
19
|
liveCollections: LiveCollectionConfigMap;
|
|
20
|
+
logger: AstroLogger;
|
|
19
21
|
}): (collection: string, filter?: ((entry: any) => unknown) | Record<string, unknown>) => Promise<{
|
|
20
22
|
data: Record<string, unknown>;
|
|
21
23
|
collection: string;
|
|
@@ -50,8 +52,9 @@ type EntryLookupObject = {
|
|
|
50
52
|
collection: string;
|
|
51
53
|
slug: string;
|
|
52
54
|
};
|
|
53
|
-
export declare function createGetEntry({ liveCollections }: {
|
|
55
|
+
export declare function createGetEntry({ liveCollections, logger, }: {
|
|
54
56
|
liveCollections: LiveCollectionConfigMap;
|
|
57
|
+
logger: AstroLogger;
|
|
55
58
|
}): (collectionOrLookupObject: string | EntryLookupObject, lookup?: string | Record<string, unknown>) => Promise<ContentEntryResult | DataEntryResult | undefined>;
|
|
56
59
|
export declare function createGetEntries(getEntry: ReturnType<typeof createGetEntry>): (entries: {
|
|
57
60
|
collection: string;
|
|
@@ -73,7 +76,9 @@ type RenderResult = {
|
|
|
73
76
|
};
|
|
74
77
|
export declare function updateImageReferencesInData<T extends Record<string, unknown>>(data: T, fileName?: string, imageAssetMap?: Map<string, ImageMetadata>, imageImports?: (string | number)[][]): T;
|
|
75
78
|
export declare function resolveEntryData<T extends Record<string, unknown>>(entry: DataEntry<T>, imageAssetMap?: Map<string, ImageMetadata>): T;
|
|
76
|
-
export declare function
|
|
79
|
+
export declare function createRenderEntry({ logger }: {
|
|
80
|
+
logger: AstroLogger;
|
|
81
|
+
}): (entry: DataEntry) => Promise<RenderResult>;
|
|
77
82
|
export declare function createReference(): (collection: string) => z.ZodPipe<z.ZodUnion<readonly [z.ZodPipe<z.ZodNumber, z.ZodTransform<string, number>>, z.ZodString, z.ZodObject<{
|
|
78
83
|
id: z.ZodString;
|
|
79
84
|
collection: z.ZodString;
|
package/dist/content/runtime.js
CHANGED
|
@@ -2,7 +2,7 @@ import { escape } from "html-escaper";
|
|
|
2
2
|
import * as z from "zod/v4";
|
|
3
3
|
import { createSvgComponent } from "../assets/runtime.js";
|
|
4
4
|
import { imageSrcToImportId } from "../assets/utils/resolveImports.js";
|
|
5
|
-
import { recordContentEntryRender } from "../core/
|
|
5
|
+
import { recordContentEntryRender } from "../core/render-scope/record.js";
|
|
6
6
|
import { AstroError, AstroErrorData } from "../core/errors/index.js";
|
|
7
7
|
import { isRemotePath, prependForwardSlash } from "../core/path.js";
|
|
8
8
|
import {
|
|
@@ -61,7 +61,8 @@ async function parseLiveEntry(entry, schema, collection) {
|
|
|
61
61
|
}
|
|
62
62
|
}
|
|
63
63
|
function createGetCollection({
|
|
64
|
-
liveCollections
|
|
64
|
+
liveCollections,
|
|
65
|
+
logger
|
|
65
66
|
}) {
|
|
66
67
|
return async function getCollection(collection, filter) {
|
|
67
68
|
if (collection in liveCollections) {
|
|
@@ -89,7 +90,8 @@ function createGetCollection({
|
|
|
89
90
|
}
|
|
90
91
|
return result;
|
|
91
92
|
} else {
|
|
92
|
-
|
|
93
|
+
logger.warn(
|
|
94
|
+
"content",
|
|
93
95
|
`The collection ${JSON.stringify(
|
|
94
96
|
collection
|
|
95
97
|
)} does not exist or is empty. Please check your content config file for errors.`
|
|
@@ -98,7 +100,10 @@ function createGetCollection({
|
|
|
98
100
|
}
|
|
99
101
|
};
|
|
100
102
|
}
|
|
101
|
-
function createGetEntry({
|
|
103
|
+
function createGetEntry({
|
|
104
|
+
liveCollections,
|
|
105
|
+
logger
|
|
106
|
+
}) {
|
|
102
107
|
return async function getEntry(collectionOrLookupObject, lookup) {
|
|
103
108
|
let collection, lookupId;
|
|
104
109
|
if (typeof collectionOrLookupObject === "string") {
|
|
@@ -129,7 +134,7 @@ function createGetEntry({ liveCollections }) {
|
|
|
129
134
|
if (await store.hasCollection(collection)) {
|
|
130
135
|
const entry = await store.get(collection, lookupId);
|
|
131
136
|
if (!entry) {
|
|
132
|
-
|
|
137
|
+
logger.warn("content", `Entry ${collection} \u2192 ${lookupId} was not found.`);
|
|
133
138
|
return;
|
|
134
139
|
}
|
|
135
140
|
const { default: imageAssetMap } = await import("astro:asset-imports");
|
|
@@ -140,12 +145,14 @@ function createGetEntry({ liveCollections }) {
|
|
|
140
145
|
collection
|
|
141
146
|
};
|
|
142
147
|
warnForPropertyAccess(
|
|
148
|
+
logger,
|
|
143
149
|
result.data,
|
|
144
150
|
"slug",
|
|
145
151
|
`[content] Attempted to access deprecated property on "${collection}" entry.
|
|
146
152
|
The "slug" property is no longer automatically added to entries. Please use the "id" property instead.`
|
|
147
153
|
);
|
|
148
154
|
warnForPropertyAccess(
|
|
155
|
+
logger,
|
|
149
156
|
result,
|
|
150
157
|
"render",
|
|
151
158
|
`[content] Invalid attempt to access "render()" method on "${collection}" entry.
|
|
@@ -156,13 +163,13 @@ To render an entry, use "render(entry)" from "astro:content".`
|
|
|
156
163
|
return void 0;
|
|
157
164
|
};
|
|
158
165
|
}
|
|
159
|
-
function warnForPropertyAccess(entry, prop, message) {
|
|
166
|
+
function warnForPropertyAccess(logger, entry, prop, message) {
|
|
160
167
|
if (!(prop in entry)) {
|
|
161
168
|
let _value = void 0;
|
|
162
169
|
Object.defineProperty(entry, prop, {
|
|
163
170
|
get() {
|
|
164
171
|
if (_value === void 0) {
|
|
165
|
-
|
|
172
|
+
logger.error("content", message);
|
|
166
173
|
}
|
|
167
174
|
return _value;
|
|
168
175
|
},
|
|
@@ -349,7 +356,9 @@ async function updateImageReferencesInBody(html, fileName) {
|
|
|
349
356
|
return Object.entries({
|
|
350
357
|
...attributes,
|
|
351
358
|
src: image.src,
|
|
352
|
-
srcset
|
|
359
|
+
// An empty `srcset` is invalid HTML, so only emit it when there are
|
|
360
|
+
// actual candidates. This matches `vite-plugin-markdown/images.ts`.
|
|
361
|
+
...image.srcSet.values.length > 0 ? { srcset: image.srcSet.attribute } : {},
|
|
353
362
|
// This attribute is used by the toolbar audit
|
|
354
363
|
...import.meta.env.DEV ? { "data-image-component": "true" } : {}
|
|
355
364
|
}).filter(([, value]) => value != null).map(([key, value]) => value === "" ? `${key}=""` : `${key}="${escape(String(value))}"`).join(" ");
|
|
@@ -402,30 +411,32 @@ function updateImageReferencesInData(data, fileName, imageAssetMap, imageImports
|
|
|
402
411
|
function resolveEntryData(entry, imageAssetMap) {
|
|
403
412
|
return updateImageReferencesInData(entry.data, entry.filePath, imageAssetMap, entry.imageImports);
|
|
404
413
|
}
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
recordContentEntryRender(entry.filePath);
|
|
410
|
-
if (entry.deferredRender) {
|
|
411
|
-
try {
|
|
412
|
-
const { default: contentModules } = await import("astro:content-module-imports");
|
|
413
|
-
const renderEntryImport = contentModules.get(entry.filePath);
|
|
414
|
-
return render({
|
|
415
|
-
collection: "",
|
|
416
|
-
id: entry.id,
|
|
417
|
-
renderEntryImport
|
|
418
|
-
});
|
|
419
|
-
} catch (e) {
|
|
420
|
-
console.error(e);
|
|
414
|
+
function createRenderEntry({ logger }) {
|
|
415
|
+
return async function renderEntry(entry) {
|
|
416
|
+
if (!entry) {
|
|
417
|
+
throw new AstroError(AstroErrorData.RenderUndefinedEntryError);
|
|
421
418
|
}
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
419
|
+
recordContentEntryRender(entry.filePath);
|
|
420
|
+
if (entry.deferredRender) {
|
|
421
|
+
try {
|
|
422
|
+
const { default: contentModules } = await import("astro:content-module-imports");
|
|
423
|
+
const renderEntryImport = contentModules.get(entry.filePath);
|
|
424
|
+
return render({
|
|
425
|
+
collection: "",
|
|
426
|
+
id: entry.id,
|
|
427
|
+
renderEntryImport
|
|
428
|
+
});
|
|
429
|
+
} catch (e) {
|
|
430
|
+
logger.error("content", `${e}`);
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
const html = entry?.rendered?.metadata?.imagePaths?.length && entry.filePath ? await updateImageReferencesInBody(entry.rendered.html, entry.filePath) : entry?.rendered?.html;
|
|
434
|
+
const Content = createComponent(() => serverRender`${unescapeHTML(html)}`);
|
|
435
|
+
return {
|
|
436
|
+
Content,
|
|
437
|
+
headings: entry?.rendered?.metadata?.headings ?? [],
|
|
438
|
+
remarkPluginFrontmatter: entry?.rendered?.metadata?.frontmatter ?? {}
|
|
439
|
+
};
|
|
429
440
|
};
|
|
430
441
|
}
|
|
431
442
|
async function render({
|
|
@@ -580,9 +591,9 @@ export {
|
|
|
580
591
|
createGetLiveCollection,
|
|
581
592
|
createGetLiveEntry,
|
|
582
593
|
createReference,
|
|
594
|
+
createRenderEntry,
|
|
583
595
|
defineCollection,
|
|
584
596
|
defineLiveCollection,
|
|
585
|
-
renderEntry,
|
|
586
597
|
resolveEntryData,
|
|
587
598
|
updateImageReferencesInData
|
|
588
599
|
};
|
|
@@ -85,7 +85,8 @@ async function createContentTypesGenerator({
|
|
|
85
85
|
await reloadContentConfigObserver({
|
|
86
86
|
fs,
|
|
87
87
|
settings,
|
|
88
|
-
environment: viteServer.environments[ASTRO_VITE_ENVIRONMENT_NAMES.astro]
|
|
88
|
+
environment: viteServer.environments[ASTRO_VITE_ENVIRONMENT_NAMES.astro],
|
|
89
|
+
logger
|
|
89
90
|
});
|
|
90
91
|
return { shouldGenerateTypes: true };
|
|
91
92
|
}
|
package/dist/content/utils.d.ts
CHANGED
|
@@ -12,38 +12,7 @@ export declare const loaderReturnSchema: z.ZodUnion<readonly [z.ZodArray<z.ZodOb
|
|
|
12
12
|
}, z.core.$loose>>, z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
13
13
|
id: z.ZodOptional<z.ZodString>;
|
|
14
14
|
}, z.core.$loose>>]>;
|
|
15
|
-
declare
|
|
16
|
-
type: z.ZodOptional<z.ZodLiteral<"content">>;
|
|
17
|
-
schema: z.ZodOptional<z.ZodAny>;
|
|
18
|
-
loader: z.ZodOptional<z.ZodNever>;
|
|
19
|
-
}, z.core.$strip>, z.ZodObject<{
|
|
20
|
-
type: z.ZodOptional<z.ZodLiteral<"data">>;
|
|
21
|
-
schema: z.ZodOptional<z.ZodAny>;
|
|
22
|
-
loader: z.ZodOptional<z.ZodNever>;
|
|
23
|
-
}, z.core.$strip>, z.ZodObject<{
|
|
24
|
-
type: z.ZodLiteral<"content_layer">;
|
|
25
|
-
schema: z.ZodOptional<z.ZodAny>;
|
|
26
|
-
loader: z.ZodUnion<readonly [z.ZodFunction<z.core.$ZodFunctionArgs, z.core.$ZodFunctionOut>, z.ZodObject<{
|
|
27
|
-
name: z.ZodString;
|
|
28
|
-
load: z.ZodFunction<z.ZodTuple<readonly [z.ZodCustom<LoaderContext, LoaderContext>], null>, z.ZodCustom<void | {
|
|
29
|
-
schema?: any;
|
|
30
|
-
types?: string;
|
|
31
|
-
}, void | {
|
|
32
|
-
schema?: any;
|
|
33
|
-
types?: string;
|
|
34
|
-
}>>;
|
|
35
|
-
schema: z.ZodOptional<z.ZodPipe<z.ZodAny, z.ZodTransform<any, any>>>;
|
|
36
|
-
createSchema: z.ZodOptional<z.ZodFunction<z.ZodTuple<readonly [], null>, z.ZodPromise<z.ZodObject<{
|
|
37
|
-
schema: z.ZodCustom<z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>, z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>>;
|
|
38
|
-
types: z.ZodString;
|
|
39
|
-
}, z.core.$strip>>>>;
|
|
40
|
-
}, z.core.$strip>]>;
|
|
41
|
-
}, z.core.$strip>, z.ZodObject<{
|
|
42
|
-
type: z.ZodDefault<z.ZodOptional<z.ZodLiteral<"live">>>;
|
|
43
|
-
schema: z.ZodOptional<z.ZodAny>;
|
|
44
|
-
loader: z.ZodFunction<z.core.$ZodFunctionArgs, z.core.$ZodFunctionOut>;
|
|
45
|
-
}, z.core.$strip>]>;
|
|
46
|
-
declare const contentConfigParser: z.ZodObject<{
|
|
15
|
+
declare function contentConfigParser(logger: AstroLogger): z.ZodObject<{
|
|
47
16
|
collections: z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodObject<{
|
|
48
17
|
type: z.ZodOptional<z.ZodLiteral<"content">>;
|
|
49
18
|
schema: z.ZodOptional<z.ZodAny>;
|
|
@@ -66,7 +35,7 @@ declare const contentConfigParser: z.ZodObject<{
|
|
|
66
35
|
}>>;
|
|
67
36
|
schema: z.ZodOptional<z.ZodPipe<z.ZodAny, z.ZodTransform<any, any>>>;
|
|
68
37
|
createSchema: z.ZodOptional<z.ZodFunction<z.ZodTuple<readonly [], null>, z.ZodPromise<z.ZodObject<{
|
|
69
|
-
schema: z.ZodCustom<z.
|
|
38
|
+
schema: z.ZodCustom<z.ZodSchema<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>, z.ZodSchema<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>>;
|
|
70
39
|
types: z.ZodString;
|
|
71
40
|
}, z.core.$strip>>>>;
|
|
72
41
|
}, z.core.$strip>]>;
|
|
@@ -76,10 +45,10 @@ declare const contentConfigParser: z.ZodObject<{
|
|
|
76
45
|
loader: z.ZodFunction<z.core.$ZodFunctionArgs, z.core.$ZodFunctionOut>;
|
|
77
46
|
}, z.core.$strip>]>>;
|
|
78
47
|
}, z.core.$strip>;
|
|
79
|
-
export type
|
|
80
|
-
export type ContentConfig = z.infer<typeof contentConfigParser> & {
|
|
48
|
+
export type ContentConfig = z.infer<ReturnType<typeof contentConfigParser>> & {
|
|
81
49
|
digest?: string;
|
|
82
50
|
};
|
|
51
|
+
export type CollectionConfig = ContentConfig['collections'][string];
|
|
83
52
|
type EntryInternal = {
|
|
84
53
|
rawData: string | undefined;
|
|
85
54
|
filePath: string;
|
|
@@ -138,6 +107,7 @@ export declare function reloadContentConfigObserver({ observer, ...loadContentCo
|
|
|
138
107
|
settings: AstroSettings;
|
|
139
108
|
environment: RunnableDevEnvironment;
|
|
140
109
|
observer?: ContentObservable;
|
|
110
|
+
logger: AstroLogger;
|
|
141
111
|
}): Promise<void>;
|
|
142
112
|
type ContentCtx = {
|
|
143
113
|
status: 'init';
|