astro 6.0.0-alpha.4 → 6.0.0-alpha.5
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.js +1 -1
- package/dist/cli/infra/build-time-astro-version-provider.js +1 -1
- package/dist/content/config.d.ts +25 -9
- package/dist/content/config.js +13 -17
- package/dist/content/content-layer.js +38 -21
- package/dist/content/loaders/glob.d.ts +4 -1
- package/dist/content/loaders/glob.js +36 -8
- package/dist/content/types-generator.js +10 -6
- package/dist/content/utils.d.ts +17 -1
- package/dist/content/utils.js +129 -8
- package/dist/content/vite-plugin-content-assets.d.ts +4 -5
- package/dist/content/vite-plugin-content-assets.js +3 -10
- package/dist/content/vite-plugin-content-imports.js +5 -1
- package/dist/content/vite-plugin-content-virtual-mod.js +10 -2
- package/dist/core/build/plugins/plugin-manifest.d.ts +4 -6
- package/dist/core/build/plugins/plugin-manifest.js +14 -45
- package/dist/core/build/static-build.d.ts +10 -0
- package/dist/core/build/static-build.js +42 -34
- package/dist/core/config/schemas/base.d.ts +6 -2
- package/dist/core/config/schemas/base.js +6 -2
- package/dist/core/config/schemas/relative.d.ts +9 -3
- package/dist/core/config/settings.js +5 -1
- package/dist/core/constants.js +1 -1
- package/dist/core/dev/dev.js +1 -1
- package/dist/core/messages.js +2 -2
- package/dist/core/sync/index.js +10 -2
- package/dist/types/public/config.d.ts +19 -1
- package/dist/vite-plugin-config-alias/index.js +1 -1
- package/dist/vite-plugin-pages/pages.js +17 -1
- package/package.json +3 -3
|
@@ -28,7 +28,7 @@ async function prepareAssetsGenerationEnv(app, totalCount) {
|
|
|
28
28
|
const isServerOutput = settings.buildOutput === "server";
|
|
29
29
|
let serverRoot, clientRoot;
|
|
30
30
|
if (isServerOutput) {
|
|
31
|
-
serverRoot = manifest.buildServerDir;
|
|
31
|
+
serverRoot = new URL(".prerender/", manifest.buildServerDir);
|
|
32
32
|
clientRoot = manifest.buildClientDir;
|
|
33
33
|
} else {
|
|
34
34
|
serverRoot = getOutDirWithinCwd(manifest.outDir);
|
package/dist/content/config.d.ts
CHANGED
|
@@ -43,11 +43,6 @@ export type { ImageFunction };
|
|
|
43
43
|
export type SchemaContext = {
|
|
44
44
|
image: ImageFunction;
|
|
45
45
|
};
|
|
46
|
-
export type LiveCollectionConfig<L extends LiveLoader, S extends BaseSchema | undefined = undefined> = {
|
|
47
|
-
type?: 'live';
|
|
48
|
-
schema?: S;
|
|
49
|
-
loader: L;
|
|
50
|
-
};
|
|
51
46
|
type LoaderConstraint<TData extends {
|
|
52
47
|
id: string;
|
|
53
48
|
}> = Loader | (() => Array<TData> | Promise<Array<TData>> | Record<string, Omit<TData, 'id'> & {
|
|
@@ -55,14 +50,35 @@ type LoaderConstraint<TData extends {
|
|
|
55
50
|
}> | Promise<Record<string, Omit<TData, 'id'> & {
|
|
56
51
|
id?: string;
|
|
57
52
|
}>>);
|
|
58
|
-
|
|
53
|
+
type ContentLayerConfig<S extends BaseSchema, TLoader extends LoaderConstraint<{
|
|
59
54
|
id: string;
|
|
60
55
|
}>> = {
|
|
61
56
|
type?: 'content_layer';
|
|
62
|
-
schema?:
|
|
57
|
+
schema?: S | ((context: SchemaContext) => S);
|
|
63
58
|
loader: TLoader;
|
|
64
59
|
};
|
|
60
|
+
type DataCollectionConfig<S extends BaseSchema> = {
|
|
61
|
+
type: 'data';
|
|
62
|
+
schema?: S | ((context: SchemaContext) => S);
|
|
63
|
+
};
|
|
64
|
+
type ContentCollectionConfig<S extends BaseSchema> = {
|
|
65
|
+
type?: 'content';
|
|
66
|
+
schema?: S | ((context: SchemaContext) => S);
|
|
67
|
+
loader?: never;
|
|
68
|
+
};
|
|
69
|
+
export type LiveCollectionConfig<L extends LiveLoader, S extends BaseSchema | undefined = undefined> = {
|
|
70
|
+
type?: 'live';
|
|
71
|
+
schema?: S;
|
|
72
|
+
loader: L;
|
|
73
|
+
};
|
|
74
|
+
export type CollectionConfig<S extends BaseSchema, TLoader extends LoaderConstraint<{
|
|
75
|
+
id: string;
|
|
76
|
+
}> = LoaderConstraint<{
|
|
77
|
+
id: string;
|
|
78
|
+
}>> = ContentCollectionConfig<S> | DataCollectionConfig<S> | ContentLayerConfig<S, TLoader>;
|
|
65
79
|
export declare function defineLiveCollection<L extends LiveLoader, S extends BaseSchema | undefined = undefined>(config: LiveCollectionConfig<L, S>): LiveCollectionConfig<L, S>;
|
|
66
|
-
export declare function defineCollection<
|
|
80
|
+
export declare function defineCollection<S extends BaseSchema, TLoader extends LoaderConstraint<{
|
|
81
|
+
id: string;
|
|
82
|
+
}> = LoaderConstraint<{
|
|
67
83
|
id: string;
|
|
68
|
-
}>>(config: CollectionConfig<
|
|
84
|
+
}>>(config: CollectionConfig<S, TLoader>): CollectionConfig<S, TLoader>;
|
package/dist/content/config.js
CHANGED
|
@@ -71,24 +71,20 @@ function defineCollection(config) {
|
|
|
71
71
|
)
|
|
72
72
|
});
|
|
73
73
|
}
|
|
74
|
-
if (
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
if (typeof config.loader === "object" && typeof config.loader.load !== "function" && ("loadEntry" in config.loader || "loadCollection" in config.loader)) {
|
|
87
|
-
throw new AstroUserError(
|
|
88
|
-
`Live content collections must be defined in "src/live.config.ts" file. Check your collection definitions in "${importerFilename ?? "your content config file"}" to ensure you are not using a live loader.`
|
|
89
|
-
);
|
|
74
|
+
if ("loader" in config) {
|
|
75
|
+
if (config.type && config.type !== CONTENT_LAYER_TYPE) {
|
|
76
|
+
throw new AstroUserError(
|
|
77
|
+
`A content collection is defined with legacy features (e.g. missing a \`loader\` or has a \`type\`). Check your collection definitions in ${importerFilename ?? "your content config file"} to ensure that all collections are defined using the current properties.`
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
if (typeof config.loader === "object" && typeof config.loader.load !== "function" && ("loadEntry" in config.loader || "loadCollection" in config.loader)) {
|
|
81
|
+
throw new AstroUserError(
|
|
82
|
+
`Live content collections must be defined in "src/live.config.ts" file. Check the loaders used in "${importerFilename ?? "your content config file"}" to ensure you are not using a live loader to define a build-time content collection.`
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
config.type = CONTENT_LAYER_TYPE;
|
|
90
86
|
}
|
|
91
|
-
config.type =
|
|
87
|
+
if (!config.type) config.type = "content";
|
|
92
88
|
return config;
|
|
93
89
|
}
|
|
94
90
|
export {
|
|
@@ -133,14 +133,24 @@ class ContentLayer {
|
|
|
133
133
|
)
|
|
134
134
|
]);
|
|
135
135
|
}
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
136
|
+
switch (contentConfig?.status) {
|
|
137
|
+
case "loaded":
|
|
138
|
+
break;
|
|
139
|
+
case "error":
|
|
140
|
+
logger.error(
|
|
141
|
+
`Error loading content config. Skipping sync.
|
|
142
|
+
${contentConfig.error.message}`
|
|
143
|
+
);
|
|
144
|
+
return;
|
|
145
|
+
case "does-not-exist":
|
|
146
|
+
return;
|
|
147
|
+
case "init":
|
|
148
|
+
case "loading":
|
|
149
|
+
case void 0:
|
|
150
|
+
logger.error(
|
|
151
|
+
`Content config not loaded, skipping sync. Status was ${contentConfig?.status}`
|
|
152
|
+
);
|
|
153
|
+
return;
|
|
144
154
|
}
|
|
145
155
|
logger.info("Syncing content");
|
|
146
156
|
const {
|
|
@@ -164,7 +174,7 @@ ${contentConfig.error.message}`);
|
|
|
164
174
|
logger.info("Content config changed");
|
|
165
175
|
shouldClear = true;
|
|
166
176
|
}
|
|
167
|
-
if (previousAstroVersion && previousAstroVersion !== "6.0.0-alpha.
|
|
177
|
+
if (previousAstroVersion && previousAstroVersion !== "6.0.0-alpha.5") {
|
|
168
178
|
logger.info("Astro version changed");
|
|
169
179
|
shouldClear = true;
|
|
170
180
|
}
|
|
@@ -172,8 +182,8 @@ ${contentConfig.error.message}`);
|
|
|
172
182
|
logger.info("Clearing content store");
|
|
173
183
|
this.#store.clearAll();
|
|
174
184
|
}
|
|
175
|
-
if ("6.0.0-alpha.
|
|
176
|
-
await this.#store.metaStore().set("astro-version", "6.0.0-alpha.
|
|
185
|
+
if ("6.0.0-alpha.5") {
|
|
186
|
+
await this.#store.metaStore().set("astro-version", "6.0.0-alpha.5");
|
|
177
187
|
}
|
|
178
188
|
if (currentConfigDigest) {
|
|
179
189
|
await this.#store.metaStore().set("content-config-digest", currentConfigDigest);
|
|
@@ -184,19 +194,24 @@ ${contentConfig.error.message}`);
|
|
|
184
194
|
if (!options?.loaders?.length) {
|
|
185
195
|
this.#watcher?.removeAllTrackedListeners();
|
|
186
196
|
}
|
|
197
|
+
const backwardsCompatEnabled = this.#settings.config.legacy?.collectionsBackwardsCompat ?? false;
|
|
187
198
|
await Promise.all(
|
|
188
199
|
Object.entries(contentConfig.config.collections).map(async ([name, collection]) => {
|
|
189
|
-
if (collection.type !== CONTENT_LAYER_TYPE) {
|
|
200
|
+
if (collection.type !== CONTENT_LAYER_TYPE && !backwardsCompatEnabled) {
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
if (collection.type !== CONTENT_LAYER_TYPE && !("loader" in collection)) {
|
|
190
204
|
return;
|
|
191
205
|
}
|
|
192
206
|
let { schema } = collection;
|
|
193
|
-
|
|
207
|
+
const loaderName = "loader" in collection ? collection.loader.name : "content";
|
|
208
|
+
if (!schema && "loader" in collection && typeof collection.loader === "object") {
|
|
194
209
|
schema = collection.loader.schema;
|
|
195
210
|
if (!schema && collection.loader.createSchema) {
|
|
196
211
|
({ schema } = await collection.loader.createSchema());
|
|
197
212
|
}
|
|
198
213
|
}
|
|
199
|
-
if (options?.loaders && (typeof collection.loader !== "object" || !options.loaders.includes(collection.loader.name))) {
|
|
214
|
+
if (options?.loaders && "loader" in collection && (typeof collection.loader !== "object" || !options.loaders.includes(collection.loader.name))) {
|
|
200
215
|
return;
|
|
201
216
|
}
|
|
202
217
|
const context = await this.#getLoaderContext({
|
|
@@ -214,16 +229,18 @@ ${contentConfig.error.message}`);
|
|
|
214
229
|
{ ...collection, schema },
|
|
215
230
|
false
|
|
216
231
|
),
|
|
217
|
-
loaderName
|
|
232
|
+
loaderName,
|
|
218
233
|
refreshContextData: options?.context
|
|
219
234
|
});
|
|
220
|
-
if (
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
235
|
+
if ("loader" in collection) {
|
|
236
|
+
if (typeof collection.loader === "function") {
|
|
237
|
+
return simpleLoader(collection.loader, context);
|
|
238
|
+
}
|
|
239
|
+
if (!collection.loader?.load) {
|
|
240
|
+
throw new Error(`Collection loader for ${name} does not have a load method`);
|
|
241
|
+
}
|
|
242
|
+
return collection.loader.load(context);
|
|
225
243
|
}
|
|
226
|
-
return collection.loader.load(context);
|
|
227
244
|
})
|
|
228
245
|
);
|
|
229
246
|
await fs.mkdir(this.#settings.config.cacheDir, { recursive: true });
|
|
@@ -18,9 +18,12 @@ interface GlobOptions {
|
|
|
18
18
|
**/
|
|
19
19
|
generateId?: (options: GenerateIdOptions) => string;
|
|
20
20
|
}
|
|
21
|
+
export declare const secretLegacyFlag: unique symbol;
|
|
21
22
|
/**
|
|
22
23
|
* Loads multiple entries, using a glob pattern to match files.
|
|
23
24
|
* @param pattern A glob pattern to match files, relative to the content directory.
|
|
24
25
|
*/
|
|
25
|
-
export declare function glob(globOptions: GlobOptions
|
|
26
|
+
export declare function glob(globOptions: GlobOptions & {
|
|
27
|
+
[secretLegacyFlag]?: boolean;
|
|
28
|
+
}): Loader;
|
|
26
29
|
export {};
|
|
@@ -6,11 +6,19 @@ import colors from "piccolore";
|
|
|
6
6
|
import picomatch from "picomatch";
|
|
7
7
|
import { glob as tinyglobby } from "tinyglobby";
|
|
8
8
|
import { getContentEntryIdAndSlug, posixRelative } from "../utils.js";
|
|
9
|
-
function generateIdDefault({ entry, base, data }) {
|
|
9
|
+
function generateIdDefault({ entry, base, data }, isLegacy) {
|
|
10
10
|
if (data.slug) {
|
|
11
11
|
return data.slug;
|
|
12
12
|
}
|
|
13
13
|
const entryURL = new URL(encodeURI(entry), base);
|
|
14
|
+
if (isLegacy) {
|
|
15
|
+
const { id } = getContentEntryIdAndSlug({
|
|
16
|
+
entry: entryURL,
|
|
17
|
+
contentDir: base,
|
|
18
|
+
collection: ""
|
|
19
|
+
});
|
|
20
|
+
return id;
|
|
21
|
+
}
|
|
14
22
|
const { slug } = getContentEntryIdAndSlug({
|
|
15
23
|
entry: entryURL,
|
|
16
24
|
contentDir: base,
|
|
@@ -24,6 +32,7 @@ function checkPrefix(pattern, prefix) {
|
|
|
24
32
|
}
|
|
25
33
|
return pattern.startsWith(prefix);
|
|
26
34
|
}
|
|
35
|
+
const secretLegacyFlag = Symbol("astro.legacy-glob");
|
|
27
36
|
function glob(globOptions) {
|
|
28
37
|
if (checkPrefix(globOptions.pattern, "../")) {
|
|
29
38
|
throw new Error(
|
|
@@ -35,11 +44,21 @@ function glob(globOptions) {
|
|
|
35
44
|
"Glob patterns cannot start with `/`. Set the `base` option to a parent directory or use a relative path instead."
|
|
36
45
|
);
|
|
37
46
|
}
|
|
38
|
-
const
|
|
47
|
+
const isLegacy = !!globOptions[secretLegacyFlag];
|
|
48
|
+
const generateId = globOptions?.generateId ?? ((opts) => generateIdDefault(opts, isLegacy));
|
|
39
49
|
const fileToIdMap = /* @__PURE__ */ new Map();
|
|
40
50
|
return {
|
|
41
51
|
name: "glob-loader",
|
|
42
|
-
load: async ({
|
|
52
|
+
load: async ({
|
|
53
|
+
config,
|
|
54
|
+
collection,
|
|
55
|
+
logger,
|
|
56
|
+
watcher,
|
|
57
|
+
parseData,
|
|
58
|
+
store,
|
|
59
|
+
generateDigest,
|
|
60
|
+
entryTypes
|
|
61
|
+
}) => {
|
|
43
62
|
const renderFunctionByContentType = /* @__PURE__ */ new WeakMap();
|
|
44
63
|
const untouchedEntries = new Set(store.keys());
|
|
45
64
|
async function syncData(entry, base, entryType, oldId) {
|
|
@@ -84,13 +103,16 @@ function glob(globOptions) {
|
|
|
84
103
|
data,
|
|
85
104
|
filePath: filePath2
|
|
86
105
|
});
|
|
87
|
-
if (
|
|
88
|
-
|
|
89
|
-
if (
|
|
106
|
+
if (existingEntry && existingEntry.filePath && existingEntry.filePath !== relativePath2) {
|
|
107
|
+
const oldFilePath = new URL(existingEntry.filePath, config.root);
|
|
108
|
+
if (existsSync(oldFilePath)) {
|
|
90
109
|
logger.warn(
|
|
91
110
|
`Duplicate id "${id}" found in ${filePath2}. Later items with the same id will overwrite earlier ones.`
|
|
92
111
|
);
|
|
93
112
|
}
|
|
113
|
+
}
|
|
114
|
+
if (entryType.getRenderFunction) {
|
|
115
|
+
let render = renderFunctionByContentType.get(entryType);
|
|
94
116
|
if (!render) {
|
|
95
117
|
render = await entryType.getRenderFunction(config);
|
|
96
118
|
renderFunctionByContentType.set(entryType, render);
|
|
@@ -130,7 +152,12 @@ function glob(globOptions) {
|
|
|
130
152
|
}
|
|
131
153
|
fileToIdMap.set(filePath2, id);
|
|
132
154
|
}
|
|
133
|
-
|
|
155
|
+
let baseDir;
|
|
156
|
+
if (isLegacy && !globOptions.base) {
|
|
157
|
+
baseDir = new URL(`./src/content/${collection}`, config.root);
|
|
158
|
+
} else {
|
|
159
|
+
baseDir = globOptions.base ? new URL(globOptions.base, config.root) : config.root;
|
|
160
|
+
}
|
|
134
161
|
if (!baseDir.pathname.endsWith("/")) {
|
|
135
162
|
baseDir.pathname = `${baseDir.pathname}/`;
|
|
136
163
|
}
|
|
@@ -229,5 +256,6 @@ function glob(globOptions) {
|
|
|
229
256
|
};
|
|
230
257
|
}
|
|
231
258
|
export {
|
|
232
|
-
glob
|
|
259
|
+
glob,
|
|
260
|
+
secretLegacyFlag
|
|
233
261
|
};
|
|
@@ -35,7 +35,11 @@ async function createContentTypesGenerator({
|
|
|
35
35
|
viteServer
|
|
36
36
|
}) {
|
|
37
37
|
const collectionEntryMap = {};
|
|
38
|
-
const contentPaths = getContentPaths(
|
|
38
|
+
const contentPaths = getContentPaths(
|
|
39
|
+
settings.config,
|
|
40
|
+
fs,
|
|
41
|
+
settings.config.legacy?.collectionsBackwardsCompat
|
|
42
|
+
);
|
|
39
43
|
const contentEntryConfigByExt = getEntryConfigByExtMap(settings.contentEntryTypes);
|
|
40
44
|
const contentEntryExts = [...contentEntryConfigByExt.keys()];
|
|
41
45
|
const dataEntryExts = getDataEntryExts(settings);
|
|
@@ -288,10 +292,10 @@ async function typeForCollection(collection, collectionKey) {
|
|
|
288
292
|
if (collection?.schema) {
|
|
289
293
|
return { type: `InferEntrySchema<${collectionKey}>` };
|
|
290
294
|
}
|
|
291
|
-
if (!collection?.type || typeof collection.loader === "function") {
|
|
295
|
+
if (!collection?.type || typeof collection.loader === "function" || !collection.loader) {
|
|
292
296
|
return { type: "any" };
|
|
293
297
|
}
|
|
294
|
-
if (collection.loader.schema) {
|
|
298
|
+
if (typeof collection.loader === "object" && collection.loader.schema) {
|
|
295
299
|
return { type: `InferLoaderSchema<${collectionKey}>` };
|
|
296
300
|
}
|
|
297
301
|
const result = await getCreateSchemaResult(collection, collectionKey);
|
|
@@ -323,7 +327,7 @@ async function writeContentFiles({
|
|
|
323
327
|
fs.mkdirSync(collectionSchemasDir, { recursive: true });
|
|
324
328
|
for (const [collection, config] of Object.entries(contentConfig?.collections ?? {})) {
|
|
325
329
|
collectionEntryMap[JSON.stringify(collection)] ??= {
|
|
326
|
-
type: config.type,
|
|
330
|
+
type: config.type ?? "unknown",
|
|
327
331
|
entries: {}
|
|
328
332
|
};
|
|
329
333
|
}
|
|
@@ -388,8 +392,8 @@ async function writeContentFiles({
|
|
|
388
392
|
hasSchema: Boolean(
|
|
389
393
|
// Is there a user provided schema or
|
|
390
394
|
collectionConfig?.schema || // Is it a loader object and
|
|
391
|
-
typeof collectionConfig?.loader
|
|
392
|
-
(collectionConfig
|
|
395
|
+
typeof collectionConfig?.loader === "object" && // Is it a loader static schema or
|
|
396
|
+
(collectionConfig.loader.schema || // is it a loader dynamic schema
|
|
393
397
|
createSchemaResultCache.has(collectionKey))
|
|
394
398
|
),
|
|
395
399
|
name: key
|
package/dist/content/utils.d.ts
CHANGED
|
@@ -14,6 +14,14 @@ export declare const loaderReturnSchema: z.ZodUnion<readonly [z.ZodArray<z.ZodOb
|
|
|
14
14
|
id: z.ZodOptional<z.ZodString>;
|
|
15
15
|
}, z.core.$loose>>]>;
|
|
16
16
|
declare const collectionConfigParser: z.ZodUnion<readonly [z.ZodObject<{
|
|
17
|
+
type: z.ZodOptional<z.ZodLiteral<"content">>;
|
|
18
|
+
schema: z.ZodOptional<z.ZodAny>;
|
|
19
|
+
loader: z.ZodOptional<z.ZodNever>;
|
|
20
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
21
|
+
type: z.ZodOptional<z.ZodLiteral<"data">>;
|
|
22
|
+
schema: z.ZodOptional<z.ZodAny>;
|
|
23
|
+
loader: z.ZodOptional<z.ZodNever>;
|
|
24
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
17
25
|
type: z.ZodLiteral<"content_layer">;
|
|
18
26
|
schema: z.ZodOptional<z.ZodAny>;
|
|
19
27
|
loader: z.ZodUnion<readonly [z.ZodFunction<z.core.$ZodFunctionArgs, z.core.$ZodFunctionOut>, z.ZodObject<{
|
|
@@ -38,6 +46,14 @@ declare const collectionConfigParser: z.ZodUnion<readonly [z.ZodObject<{
|
|
|
38
46
|
}, z.core.$strip>]>;
|
|
39
47
|
declare const contentConfigParser: z.ZodObject<{
|
|
40
48
|
collections: z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodObject<{
|
|
49
|
+
type: z.ZodOptional<z.ZodLiteral<"content">>;
|
|
50
|
+
schema: z.ZodOptional<z.ZodAny>;
|
|
51
|
+
loader: z.ZodOptional<z.ZodNever>;
|
|
52
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
53
|
+
type: z.ZodOptional<z.ZodLiteral<"data">>;
|
|
54
|
+
schema: z.ZodOptional<z.ZodAny>;
|
|
55
|
+
loader: z.ZodOptional<z.ZodNever>;
|
|
56
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
41
57
|
type: z.ZodLiteral<"content_layer">;
|
|
42
58
|
schema: z.ZodOptional<z.ZodAny>;
|
|
43
59
|
loader: z.ZodUnion<readonly [z.ZodFunction<z.core.$ZodFunctionArgs, z.core.$ZodFunctionOut>, z.ZodObject<{
|
|
@@ -158,7 +174,7 @@ export type ContentPaths = {
|
|
|
158
174
|
url: URL;
|
|
159
175
|
};
|
|
160
176
|
};
|
|
161
|
-
export declare function getContentPaths({ srcDir, root }: Pick<AstroConfig, 'root' | 'srcDir'>, fs?: typeof fsMod): ContentPaths;
|
|
177
|
+
export declare function getContentPaths({ srcDir, root }: Pick<AstroConfig, 'root' | 'srcDir'>, fs?: typeof fsMod, legacyCollectionsBackwardsCompat?: boolean): ContentPaths;
|
|
162
178
|
/**
|
|
163
179
|
* Check for slug in content entry frontmatter and validate the type,
|
|
164
180
|
* falling back to the `generatedSlug` if none is found.
|
package/dist/content/utils.js
CHANGED
|
@@ -8,6 +8,7 @@ import xxhash from "xxhash-wasm";
|
|
|
8
8
|
import * as z from "zod/v4";
|
|
9
9
|
import { AstroError, AstroErrorData, errorMap, MarkdownError } from "../core/errors/index.js";
|
|
10
10
|
import { isYAMLException } from "../core/errors/utils.js";
|
|
11
|
+
import { appendForwardSlash } from "../core/path.js";
|
|
11
12
|
import { normalizePath } from "../core/viteUtils.js";
|
|
12
13
|
import {
|
|
13
14
|
CONTENT_LAYER_TYPE,
|
|
@@ -17,6 +18,7 @@ import {
|
|
|
17
18
|
LIVE_CONTENT_TYPE,
|
|
18
19
|
PROPAGATED_ASSET_FLAG
|
|
19
20
|
} from "./consts.js";
|
|
21
|
+
import { glob, secretLegacyFlag } from "./loaders/glob.js";
|
|
20
22
|
import { createImage } from "./runtime-assets.js";
|
|
21
23
|
const entryTypeSchema = z.object({
|
|
22
24
|
id: z.string({
|
|
@@ -36,6 +38,16 @@ const loaderReturnSchema = z.union([
|
|
|
36
38
|
)
|
|
37
39
|
]);
|
|
38
40
|
const collectionConfigParser = z.union([
|
|
41
|
+
z.object({
|
|
42
|
+
type: z.literal("content").optional(),
|
|
43
|
+
schema: z.any().optional(),
|
|
44
|
+
loader: z.never().optional()
|
|
45
|
+
}),
|
|
46
|
+
z.object({
|
|
47
|
+
type: z.literal("data").optional(),
|
|
48
|
+
schema: z.any().optional(),
|
|
49
|
+
loader: z.never().optional()
|
|
50
|
+
}),
|
|
39
51
|
z.object({
|
|
40
52
|
type: z.literal(CONTENT_LAYER_TYPE),
|
|
41
53
|
schema: z.any().optional(),
|
|
@@ -349,7 +361,11 @@ async function loadContentConfig({
|
|
|
349
361
|
settings,
|
|
350
362
|
environment
|
|
351
363
|
}) {
|
|
352
|
-
const contentPaths = getContentPaths(
|
|
364
|
+
const contentPaths = getContentPaths(
|
|
365
|
+
settings.config,
|
|
366
|
+
fs,
|
|
367
|
+
settings.config.legacy?.collectionsBackwardsCompat
|
|
368
|
+
);
|
|
353
369
|
if (!contentPaths.config.exists) {
|
|
354
370
|
return void 0;
|
|
355
371
|
}
|
|
@@ -383,6 +399,74 @@ ${message}
|
|
|
383
399
|
return void 0;
|
|
384
400
|
}
|
|
385
401
|
}
|
|
402
|
+
async function autogenerateCollections({
|
|
403
|
+
config,
|
|
404
|
+
settings,
|
|
405
|
+
fs
|
|
406
|
+
}) {
|
|
407
|
+
if (!config) {
|
|
408
|
+
return config;
|
|
409
|
+
}
|
|
410
|
+
if (!settings.config.legacy?.collectionsBackwardsCompat) {
|
|
411
|
+
return config;
|
|
412
|
+
}
|
|
413
|
+
const contentDir = new URL("./content/", settings.config.srcDir);
|
|
414
|
+
const collections = config.collections ?? {};
|
|
415
|
+
const contentExts = getContentEntryExts(settings);
|
|
416
|
+
const dataExts = getDataEntryExts(settings);
|
|
417
|
+
const contentPattern = globWithUnderscoresIgnored("", contentExts);
|
|
418
|
+
const dataPattern = globWithUnderscoresIgnored("", dataExts);
|
|
419
|
+
let usesContentLayer = false;
|
|
420
|
+
for (const collectionName of Object.keys(collections)) {
|
|
421
|
+
const collection = collections[collectionName];
|
|
422
|
+
if (collection?.type === CONTENT_LAYER_TYPE || collection?.type === LIVE_CONTENT_TYPE) {
|
|
423
|
+
usesContentLayer = true;
|
|
424
|
+
continue;
|
|
425
|
+
}
|
|
426
|
+
const isDataCollection = collection?.type === "data";
|
|
427
|
+
const base = new URL(`${collectionName}/`, contentDir);
|
|
428
|
+
collections[collectionName] = {
|
|
429
|
+
...collection,
|
|
430
|
+
type: CONTENT_LAYER_TYPE,
|
|
431
|
+
loader: glob({
|
|
432
|
+
base,
|
|
433
|
+
pattern: isDataCollection ? dataPattern : contentPattern,
|
|
434
|
+
[secretLegacyFlag]: true
|
|
435
|
+
})
|
|
436
|
+
};
|
|
437
|
+
}
|
|
438
|
+
if (!usesContentLayer && fs.existsSync(contentDir)) {
|
|
439
|
+
const orphanedCollections = [];
|
|
440
|
+
for (const entry of await fs.promises.readdir(contentDir, { withFileTypes: true })) {
|
|
441
|
+
const collectionName = entry.name;
|
|
442
|
+
if (["_", "."].includes(collectionName.at(0) ?? "")) {
|
|
443
|
+
continue;
|
|
444
|
+
}
|
|
445
|
+
if (entry.isDirectory() && !(collectionName in collections)) {
|
|
446
|
+
orphanedCollections.push(collectionName);
|
|
447
|
+
const base = new URL(`${collectionName}/`, contentDir);
|
|
448
|
+
collections[collectionName] = {
|
|
449
|
+
type: CONTENT_LAYER_TYPE,
|
|
450
|
+
loader: glob({
|
|
451
|
+
base,
|
|
452
|
+
pattern: contentPattern,
|
|
453
|
+
[secretLegacyFlag]: true
|
|
454
|
+
})
|
|
455
|
+
};
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
if (orphanedCollections.length > 0) {
|
|
459
|
+
console.warn(
|
|
460
|
+
`
|
|
461
|
+
Auto-generating collections for folders in "src/content/" that are not defined as collections.
|
|
462
|
+
This is deprecated, so you should define these collections yourself in "src/content.config.ts".
|
|
463
|
+
The following collections have been auto-generated: ${orphanedCollections.map((name) => colors.green(name)).join(", ")}
|
|
464
|
+
`
|
|
465
|
+
);
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
return { ...config, collections };
|
|
469
|
+
}
|
|
386
470
|
async function reloadContentConfigObserver({
|
|
387
471
|
observer = globalContentConfigObserver,
|
|
388
472
|
...loadContentConfigOpts
|
|
@@ -390,6 +474,10 @@ async function reloadContentConfigObserver({
|
|
|
390
474
|
observer.set({ status: "loading" });
|
|
391
475
|
try {
|
|
392
476
|
let config = await loadContentConfig(loadContentConfigOpts);
|
|
477
|
+
config = await autogenerateCollections({
|
|
478
|
+
config,
|
|
479
|
+
...loadContentConfigOpts
|
|
480
|
+
});
|
|
393
481
|
if (config) {
|
|
394
482
|
observer.set({ status: "loaded", config });
|
|
395
483
|
} else {
|
|
@@ -424,20 +512,38 @@ function contentObservable(initialCtx) {
|
|
|
424
512
|
subscribe
|
|
425
513
|
};
|
|
426
514
|
}
|
|
427
|
-
function getContentPaths({ srcDir, root }, fs = fsMod) {
|
|
515
|
+
function getContentPaths({ srcDir, root }, fs = fsMod, legacyCollectionsBackwardsCompat = false) {
|
|
516
|
+
const pkgBase = new URL("../../", import.meta.url);
|
|
428
517
|
const configStats = searchConfig(fs, srcDir);
|
|
429
518
|
if (!configStats.exists) {
|
|
430
519
|
const legacyConfigStats = searchLegacyConfig(fs, srcDir);
|
|
431
520
|
if (legacyConfigStats.exists) {
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
521
|
+
if (!legacyCollectionsBackwardsCompat) {
|
|
522
|
+
const relativePath = path.relative(
|
|
523
|
+
fileURLToPath(root),
|
|
524
|
+
fileURLToPath(legacyConfigStats.url)
|
|
525
|
+
);
|
|
526
|
+
throw new AstroError({
|
|
527
|
+
...AstroErrorData.LegacyContentConfigError,
|
|
528
|
+
message: AstroErrorData.LegacyContentConfigError.message(relativePath)
|
|
529
|
+
});
|
|
530
|
+
}
|
|
531
|
+
return getContentPathsWithConfig(root, srcDir, pkgBase, legacyConfigStats, fs);
|
|
437
532
|
}
|
|
438
533
|
}
|
|
439
534
|
const liveConfigStats = searchLiveConfig(fs, srcDir);
|
|
440
|
-
|
|
535
|
+
return {
|
|
536
|
+
root: new URL("./", root),
|
|
537
|
+
contentDir: new URL("./content/", srcDir),
|
|
538
|
+
assetsDir: new URL("./assets/", srcDir),
|
|
539
|
+
typesTemplate: new URL("templates/content/types.d.ts", pkgBase),
|
|
540
|
+
virtualModTemplate: new URL("templates/content/module.mjs", pkgBase),
|
|
541
|
+
config: configStats,
|
|
542
|
+
liveConfig: liveConfigStats
|
|
543
|
+
};
|
|
544
|
+
}
|
|
545
|
+
function getContentPathsWithConfig(root, srcDir, pkgBase, configStats, fs) {
|
|
546
|
+
const liveConfigStats = searchLiveConfig(fs, srcDir);
|
|
441
547
|
return {
|
|
442
548
|
root: new URL("./", root),
|
|
443
549
|
contentDir: new URL("./content/", srcDir),
|
|
@@ -499,6 +605,21 @@ async function getEntrySlug({
|
|
|
499
605
|
});
|
|
500
606
|
return parseEntrySlug({ generatedSlug, frontmatterSlug, id, collection });
|
|
501
607
|
}
|
|
608
|
+
function getExtGlob(exts) {
|
|
609
|
+
return exts.length === 1 ? (
|
|
610
|
+
// Wrapping {...} breaks when there is only one extension
|
|
611
|
+
exts[0]
|
|
612
|
+
) : `{${exts.join(",")}}`;
|
|
613
|
+
}
|
|
614
|
+
function globWithUnderscoresIgnored(relContentDir, exts) {
|
|
615
|
+
const extGlob = getExtGlob(exts);
|
|
616
|
+
const contentDir = relContentDir.length > 0 ? appendForwardSlash(relContentDir) : relContentDir;
|
|
617
|
+
return [
|
|
618
|
+
`${contentDir}**/*${extGlob}`,
|
|
619
|
+
`!${contentDir}**/_*/**/*${extGlob}`,
|
|
620
|
+
`!${contentDir}**/_*${extGlob}`
|
|
621
|
+
];
|
|
622
|
+
}
|
|
502
623
|
function hasAssetPropagationFlag(id) {
|
|
503
624
|
try {
|
|
504
625
|
return new URL(id, "file://").searchParams.has(PROPAGATED_ASSET_FLAG);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type * as vite from 'vite';
|
|
2
1
|
import { type Plugin } from 'vite';
|
|
3
2
|
import type { BuildInternals } from '../core/build/internal.js';
|
|
3
|
+
import type { ExtractedChunk } from '../core/build/static-build.js';
|
|
4
4
|
import type { AstroSettings } from '../types/astro.js';
|
|
5
5
|
export declare function astroContentAssetPropagationPlugin({ settings, }: {
|
|
6
6
|
settings: AstroSettings;
|
|
@@ -10,8 +10,7 @@ export declare function astroContentAssetPropagationPlugin({ settings, }: {
|
|
|
10
10
|
* Finds chunks with LINKS_PLACEHOLDER and STYLES_PLACEHOLDER, and replaces them
|
|
11
11
|
* with actual styles from propagatedStylesMap.
|
|
12
12
|
*/
|
|
13
|
-
export declare function contentAssetsBuildPostHook(base: string, internals: BuildInternals, {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
mutate: (chunk: vite.Rollup.OutputChunk, envs: ['server'], code: string) => void;
|
|
13
|
+
export declare function contentAssetsBuildPostHook(base: string, internals: BuildInternals, { chunks, mutate, }: {
|
|
14
|
+
chunks: ExtractedChunk[];
|
|
15
|
+
mutate: (fileName: string, code: string, prerender: boolean) => void;
|
|
17
16
|
}): Promise<void>;
|
|
@@ -148,17 +148,10 @@ async function getStylesForURL(filePath, environment) {
|
|
|
148
148
|
};
|
|
149
149
|
}
|
|
150
150
|
async function contentAssetsBuildPostHook(base, internals, {
|
|
151
|
-
|
|
152
|
-
prerenderOutputs,
|
|
151
|
+
chunks,
|
|
153
152
|
mutate
|
|
154
153
|
}) {
|
|
155
|
-
const
|
|
156
|
-
...(Array.isArray(prerenderOutputs) ? prerenderOutputs : [prerenderOutputs]).flatMap(
|
|
157
|
-
(o) => o.output
|
|
158
|
-
)
|
|
159
|
-
);
|
|
160
|
-
for (const chunk of outputs) {
|
|
161
|
-
if (chunk.type !== "chunk") continue;
|
|
154
|
+
for (const chunk of chunks) {
|
|
162
155
|
if (!chunk.code.includes(LINKS_PLACEHOLDER)) continue;
|
|
163
156
|
const entryStyles = /* @__PURE__ */ new Set();
|
|
164
157
|
const entryLinks = /* @__PURE__ */ new Set();
|
|
@@ -189,7 +182,7 @@ async function contentAssetsBuildPostHook(base, internals, {
|
|
|
189
182
|
} else {
|
|
190
183
|
newCode = newCode.replace(JSON.stringify(LINKS_PLACEHOLDER), "[]");
|
|
191
184
|
}
|
|
192
|
-
mutate(chunk,
|
|
185
|
+
mutate(chunk.fileName, newCode, chunk.prerender);
|
|
193
186
|
}
|
|
194
187
|
}
|
|
195
188
|
export {
|
|
@@ -40,7 +40,11 @@ function astroContentImportPlugin({
|
|
|
40
40
|
settings,
|
|
41
41
|
logger
|
|
42
42
|
}) {
|
|
43
|
-
const contentPaths = getContentPaths(
|
|
43
|
+
const contentPaths = getContentPaths(
|
|
44
|
+
settings.config,
|
|
45
|
+
fs,
|
|
46
|
+
settings.config.legacy?.collectionsBackwardsCompat
|
|
47
|
+
);
|
|
44
48
|
const contentEntryExts = getContentEntryExts(settings);
|
|
45
49
|
const dataEntryExts = getDataEntryExts(settings);
|
|
46
50
|
const contentEntryConfigByExt = getEntryConfigByExtMap(settings.contentEntryTypes);
|
|
@@ -46,7 +46,11 @@ function astroContentVirtualModPlugin({
|
|
|
46
46
|
enforce: "pre",
|
|
47
47
|
config(_, env) {
|
|
48
48
|
dataStoreFile = getDataStoreFile(settings, env.command === "serve");
|
|
49
|
-
const contentPaths = getContentPaths(
|
|
49
|
+
const contentPaths = getContentPaths(
|
|
50
|
+
settings.config,
|
|
51
|
+
void 0,
|
|
52
|
+
settings.config.legacy?.collectionsBackwardsCompat
|
|
53
|
+
);
|
|
50
54
|
if (contentPaths.liveConfig.exists) {
|
|
51
55
|
liveConfig = normalizePath(fileURLToPath(contentPaths.liveConfig.url));
|
|
52
56
|
}
|
|
@@ -178,7 +182,11 @@ async function generateContentEntryFile({
|
|
|
178
182
|
settings,
|
|
179
183
|
isClient
|
|
180
184
|
}) {
|
|
181
|
-
const contentPaths = getContentPaths(
|
|
185
|
+
const contentPaths = getContentPaths(
|
|
186
|
+
settings.config,
|
|
187
|
+
void 0,
|
|
188
|
+
settings.config.legacy?.collectionsBackwardsCompat
|
|
189
|
+
);
|
|
182
190
|
const relContentDir = rootRelativePath(settings.config.root, contentPaths.contentDir);
|
|
183
191
|
let virtualModContents;
|
|
184
192
|
if (isClient) {
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type * as vite from 'vite';
|
|
1
|
+
import type { ExtractedChunk } from '../static-build.js';
|
|
3
2
|
import type { BuildInternals } from '../internal.js';
|
|
4
3
|
import type { StaticBuildOptions } from '../types.js';
|
|
5
4
|
/**
|
|
@@ -27,8 +26,7 @@ export declare const MANIFEST_REPLACE = "@@ASTRO_MANIFEST_REPLACE@@";
|
|
|
27
26
|
* Post-build hook that injects the computed manifest into bundled chunks.
|
|
28
27
|
* Finds the serialized manifest chunk and replaces the placeholder token with real data.
|
|
29
28
|
*/
|
|
30
|
-
export declare function manifestBuildPostHook(options: StaticBuildOptions, internals: BuildInternals, {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
mutate: (chunk: OutputChunk, envs: ['server'], code: string) => void;
|
|
29
|
+
export declare function manifestBuildPostHook(options: StaticBuildOptions, internals: BuildInternals, { chunks, mutate, }: {
|
|
30
|
+
chunks: ExtractedChunk[];
|
|
31
|
+
mutate: (fileName: string, code: string, prerender: boolean) => void;
|
|
34
32
|
}): Promise<void>;
|
|
@@ -29,30 +29,14 @@ import { sessionConfigToManifest } from "../../session/utils.js";
|
|
|
29
29
|
const MANIFEST_REPLACE = "@@ASTRO_MANIFEST_REPLACE@@";
|
|
30
30
|
const replaceExp = new RegExp(`['"]${MANIFEST_REPLACE}['"]`, "g");
|
|
31
31
|
async function manifestBuildPostHook(options, internals, {
|
|
32
|
-
|
|
33
|
-
prerenderOutputs,
|
|
32
|
+
chunks,
|
|
34
33
|
mutate
|
|
35
34
|
}) {
|
|
36
35
|
const manifest = await createManifest(options, internals);
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
if (chunk.type === "asset") {
|
|
42
|
-
continue;
|
|
43
|
-
}
|
|
44
|
-
if (chunk.code && chunk.moduleIds.includes(SERIALIZED_MANIFEST_RESOLVED_ID)) {
|
|
45
|
-
manifestEntryChunk = chunk;
|
|
46
|
-
break;
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
if (manifestEntryChunk) {
|
|
50
|
-
break;
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
if (!manifestEntryChunk) {
|
|
54
|
-
throw new Error(`Did not find serialized manifest chunk for SSR`);
|
|
55
|
-
}
|
|
36
|
+
const ssrManifestChunk = chunks.find(
|
|
37
|
+
(c) => !c.prerender && c.moduleIds.includes(SERIALIZED_MANIFEST_RESOLVED_ID)
|
|
38
|
+
);
|
|
39
|
+
if (ssrManifestChunk) {
|
|
56
40
|
const shouldPassMiddlewareEntryPoint = options.settings.adapter?.adapterFeatures?.edgeMiddleware;
|
|
57
41
|
await runHookBuildSsr({
|
|
58
42
|
config: options.settings.config,
|
|
@@ -60,29 +44,15 @@ async function manifestBuildPostHook(options, internals, {
|
|
|
60
44
|
logger: options.logger,
|
|
61
45
|
middlewareEntryPoint: shouldPassMiddlewareEntryPoint ? internals.middlewareEntryPoint : void 0
|
|
62
46
|
});
|
|
63
|
-
const code = injectManifest(manifest,
|
|
64
|
-
mutate(
|
|
47
|
+
const code = injectManifest(manifest, ssrManifestChunk.code);
|
|
48
|
+
mutate(ssrManifestChunk.fileName, code, false);
|
|
65
49
|
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
}
|
|
73
|
-
if (chunk.code && chunk.moduleIds.includes(SERIALIZED_MANIFEST_RESOLVED_ID)) {
|
|
74
|
-
prerenderManifestChunk = chunk;
|
|
75
|
-
break;
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
if (prerenderManifestChunk) {
|
|
79
|
-
break;
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
if (prerenderManifestChunk) {
|
|
83
|
-
const prerenderCode = injectManifest(manifest, prerenderManifestChunk);
|
|
84
|
-
mutate(prerenderManifestChunk, ["server"], prerenderCode);
|
|
85
|
-
}
|
|
50
|
+
const prerenderManifestChunk = chunks.find(
|
|
51
|
+
(c) => c.prerender && c.moduleIds.includes(SERIALIZED_MANIFEST_RESOLVED_ID)
|
|
52
|
+
);
|
|
53
|
+
if (prerenderManifestChunk) {
|
|
54
|
+
const code = injectManifest(manifest, prerenderManifestChunk.code);
|
|
55
|
+
mutate(prerenderManifestChunk.fileName, code, true);
|
|
86
56
|
}
|
|
87
57
|
}
|
|
88
58
|
async function createManifest(buildOpts, internals) {
|
|
@@ -99,8 +69,7 @@ async function createManifest(buildOpts, internals) {
|
|
|
99
69
|
const manifest = await buildManifest(buildOpts, internals, Array.from(staticFiles), encodedKey);
|
|
100
70
|
return manifest;
|
|
101
71
|
}
|
|
102
|
-
function injectManifest(manifest,
|
|
103
|
-
const code = chunk.code;
|
|
72
|
+
function injectManifest(manifest, code) {
|
|
104
73
|
return code.replace(replaceExp, () => {
|
|
105
74
|
return JSON.stringify(manifest);
|
|
106
75
|
});
|
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
import { type BuildInternals } from '../../core/build/internal.js';
|
|
2
2
|
import type { RouteData } from '../../types/public/internal.js';
|
|
3
3
|
import type { StaticBuildOptions } from './types.js';
|
|
4
|
+
/**
|
|
5
|
+
* Minimal chunk data extracted from RollupOutput for deferred manifest/content injection.
|
|
6
|
+
* Allows releasing full RollupOutput objects early to reduce memory usage.
|
|
7
|
+
*/
|
|
8
|
+
export interface ExtractedChunk {
|
|
9
|
+
fileName: string;
|
|
10
|
+
code: string;
|
|
11
|
+
moduleIds: string[];
|
|
12
|
+
prerender: boolean;
|
|
13
|
+
}
|
|
4
14
|
export declare function viteBuild(opts: StaticBuildOptions): Promise<{
|
|
5
15
|
internals: BuildInternals;
|
|
6
16
|
prerenderOutputDir: URL;
|
|
@@ -4,6 +4,7 @@ import { fileURLToPath, pathToFileURL } from "node:url";
|
|
|
4
4
|
import colors from "piccolore";
|
|
5
5
|
import { glob } from "tinyglobby";
|
|
6
6
|
import * as vite from "vite";
|
|
7
|
+
import { LINKS_PLACEHOLDER } from "../../content/consts.js";
|
|
7
8
|
import { contentAssetsBuildPostHook } from "../../content/vite-plugin-content-assets.js";
|
|
8
9
|
import { createBuildInternals } from "../../core/build/internal.js";
|
|
9
10
|
import { emptyDir, removeEmptyDirs } from "../../core/fs/index.js";
|
|
@@ -27,6 +28,25 @@ import { encodeName, getTimeStat, viteBuildReturnToRollupOutputs } from "./util.
|
|
|
27
28
|
import { NOOP_MODULE_ID } from "./plugins/plugin-noop.js";
|
|
28
29
|
import { ASTRO_VITE_ENVIRONMENT_NAMES } from "../constants.js";
|
|
29
30
|
const PRERENDER_ENTRY_FILENAME_PREFIX = "prerender-entry";
|
|
31
|
+
function extractRelevantChunks(outputs, prerender) {
|
|
32
|
+
const extracted = [];
|
|
33
|
+
for (const output of outputs) {
|
|
34
|
+
for (const chunk of output.output) {
|
|
35
|
+
if (chunk.type === "asset") continue;
|
|
36
|
+
const needsContentInjection = chunk.code.includes(LINKS_PLACEHOLDER);
|
|
37
|
+
const needsManifestInjection = chunk.moduleIds.includes(SERIALIZED_MANIFEST_RESOLVED_ID);
|
|
38
|
+
if (needsContentInjection || needsManifestInjection) {
|
|
39
|
+
extracted.push({
|
|
40
|
+
fileName: chunk.fileName,
|
|
41
|
+
code: chunk.code,
|
|
42
|
+
moduleIds: [...chunk.moduleIds],
|
|
43
|
+
prerender
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return extracted;
|
|
49
|
+
}
|
|
30
50
|
async function viteBuild(opts) {
|
|
31
51
|
const { allPages, settings } = opts;
|
|
32
52
|
settings.timer.start("SSR build");
|
|
@@ -45,16 +65,13 @@ async function viteBuild(opts) {
|
|
|
45
65
|
}
|
|
46
66
|
const ssrTime = performance.now();
|
|
47
67
|
opts.logger.info("build", `Building ${settings.buildOutput} entrypoints...`);
|
|
48
|
-
const {
|
|
68
|
+
const { extractedChunks } = await buildEnvironments(opts, internals);
|
|
49
69
|
opts.logger.info(
|
|
50
70
|
"build",
|
|
51
71
|
colors.green(`\u2713 Completed in ${getTimeStat(ssrTime, performance.now())}.`)
|
|
52
72
|
);
|
|
53
73
|
settings.timer.end("SSR build");
|
|
54
|
-
|
|
55
|
-
const clientOutputs = viteBuildReturnToRollupOutputs(clientOutput ?? []);
|
|
56
|
-
const prerenderOutputs = viteBuildReturnToRollupOutputs(prerenderOutput);
|
|
57
|
-
await runManifestInjection(opts, internals, ssrOutputs, clientOutputs, prerenderOutputs);
|
|
74
|
+
await runManifestInjection(opts, internals, extractedChunks);
|
|
58
75
|
const prerenderOutputDir = new URL("./.prerender/", getServerOutputDirectory(settings));
|
|
59
76
|
return { internals, prerenderOutputDir };
|
|
60
77
|
}
|
|
@@ -208,16 +225,23 @@ async function buildEnvironments(opts, internals) {
|
|
|
208
225
|
logger: opts.logger
|
|
209
226
|
});
|
|
210
227
|
const builder = await vite.createBuilder(updatedViteBuildConfig);
|
|
211
|
-
|
|
212
|
-
const
|
|
228
|
+
let ssrOutput = settings.buildOutput === "static" ? [] : await builder.build(builder.environments[ASTRO_VITE_ENVIRONMENT_NAMES.ssr]);
|
|
229
|
+
const ssrChunks = extractRelevantChunks(viteBuildReturnToRollupOutputs(ssrOutput), false);
|
|
230
|
+
ssrOutput = void 0;
|
|
231
|
+
let prerenderOutput = await builder.build(builder.environments.prerender);
|
|
213
232
|
extractPrerenderEntryFileName(internals, prerenderOutput);
|
|
233
|
+
const prerenderChunks = extractRelevantChunks(
|
|
234
|
+
viteBuildReturnToRollupOutputs(prerenderOutput),
|
|
235
|
+
true
|
|
236
|
+
);
|
|
237
|
+
prerenderOutput = void 0;
|
|
214
238
|
internals.clientInput = getClientInput(internals, settings);
|
|
215
239
|
if (!internals.clientInput.size) {
|
|
216
240
|
internals.clientInput.add(NOOP_MODULE_ID);
|
|
217
241
|
}
|
|
218
242
|
builder.environments.client.config.build.rollupOptions.input = Array.from(internals.clientInput);
|
|
219
|
-
|
|
220
|
-
return {
|
|
243
|
+
await builder.build(builder.environments.client);
|
|
244
|
+
return { extractedChunks: [...ssrChunks, ...prerenderChunks] };
|
|
221
245
|
}
|
|
222
246
|
function getPrerenderEntryFileName(prerenderOutput) {
|
|
223
247
|
const outputs = viteBuildReturnToRollupOutputs(prerenderOutput);
|
|
@@ -238,41 +262,25 @@ function getPrerenderEntryFileName(prerenderOutput) {
|
|
|
238
262
|
function extractPrerenderEntryFileName(internals, prerenderOutput) {
|
|
239
263
|
internals.prerenderEntryFileName = getPrerenderEntryFileName(prerenderOutput);
|
|
240
264
|
}
|
|
241
|
-
async function runManifestInjection(opts, internals,
|
|
265
|
+
async function runManifestInjection(opts, internals, chunks) {
|
|
242
266
|
const mutations = /* @__PURE__ */ new Map();
|
|
243
|
-
const mutate = (
|
|
244
|
-
|
|
245
|
-
mutations.set(chunk.fileName, {
|
|
246
|
-
targets,
|
|
247
|
-
code: newCode
|
|
248
|
-
});
|
|
267
|
+
const mutate = (fileName, newCode, prerender) => {
|
|
268
|
+
mutations.set(fileName, { code: newCode, prerender });
|
|
249
269
|
};
|
|
250
|
-
await manifestBuildPostHook(opts, internals, {
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
mutate
|
|
254
|
-
});
|
|
255
|
-
await contentAssetsBuildPostHook(opts.settings.config.base, internals, {
|
|
256
|
-
ssrOutputs,
|
|
257
|
-
prerenderOutputs,
|
|
258
|
-
mutate
|
|
259
|
-
});
|
|
260
|
-
await writeMutatedChunks(opts, mutations, prerenderOutputs);
|
|
270
|
+
await manifestBuildPostHook(opts, internals, { chunks, mutate });
|
|
271
|
+
await contentAssetsBuildPostHook(opts.settings.config.base, internals, { chunks, mutate });
|
|
272
|
+
await writeMutatedChunks(opts, mutations);
|
|
261
273
|
}
|
|
262
|
-
async function writeMutatedChunks(opts, mutations
|
|
274
|
+
async function writeMutatedChunks(opts, mutations) {
|
|
263
275
|
const { settings } = opts;
|
|
264
276
|
const config = settings.config;
|
|
265
|
-
const build = settings.config.build;
|
|
266
277
|
const serverOutputDir = getServerOutputDirectory(settings);
|
|
267
278
|
for (const [fileName, mutation] of mutations) {
|
|
268
279
|
let root;
|
|
269
|
-
|
|
270
|
-
(output) => output.output.some((chunk) => chunk.type !== "asset" && chunk.fileName === fileName)
|
|
271
|
-
);
|
|
272
|
-
if (isPrerender) {
|
|
280
|
+
if (mutation.prerender) {
|
|
273
281
|
root = new URL("./.prerender/", serverOutputDir);
|
|
274
282
|
} else if (settings.buildOutput === "server") {
|
|
275
|
-
root =
|
|
283
|
+
root = config.build.server;
|
|
276
284
|
} else {
|
|
277
285
|
root = getOutDirWithinCwd(config.outDir);
|
|
278
286
|
}
|
|
@@ -57,7 +57,9 @@ export declare const ASTRO_CONFIG_DEFAULTS: {
|
|
|
57
57
|
integrations: never[];
|
|
58
58
|
markdown: Required<import("@astrojs/markdown-remark").AstroMarkdownOptions>;
|
|
59
59
|
vite: {};
|
|
60
|
-
legacy: {
|
|
60
|
+
legacy: {
|
|
61
|
+
collectionsBackwardsCompat: false;
|
|
62
|
+
};
|
|
61
63
|
redirects: {};
|
|
62
64
|
security: {
|
|
63
65
|
checkOrigin: true;
|
|
@@ -521,7 +523,9 @@ export declare const AstroConfigSchema: z.ZodObject<{
|
|
|
521
523
|
chromeDevtoolsWorkspace: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
522
524
|
svgo: z.ZodDefault<z.ZodOptional<z.ZodUnion<readonly [z.ZodBoolean, z.ZodCustom<SvgoConfig, SvgoConfig>]>>>;
|
|
523
525
|
}, z.core.$strict>>;
|
|
524
|
-
legacy: z.
|
|
526
|
+
legacy: z.ZodPrefault<z.ZodObject<{
|
|
527
|
+
collectionsBackwardsCompat: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
528
|
+
}, z.core.$strip>>;
|
|
525
529
|
}, z.core.$strip>;
|
|
526
530
|
export type AstroConfigType = z.infer<typeof AstroConfigSchema>;
|
|
527
531
|
export {};
|
|
@@ -41,7 +41,9 @@ const ASTRO_CONFIG_DEFAULTS = {
|
|
|
41
41
|
integrations: [],
|
|
42
42
|
markdown: markdownConfigDefaults,
|
|
43
43
|
vite: {},
|
|
44
|
-
legacy: {
|
|
44
|
+
legacy: {
|
|
45
|
+
collectionsBackwardsCompat: false
|
|
46
|
+
},
|
|
45
47
|
redirects: {},
|
|
46
48
|
security: {
|
|
47
49
|
checkOrigin: true,
|
|
@@ -276,7 +278,9 @@ const AstroConfigSchema = z.object({
|
|
|
276
278
|
chromeDevtoolsWorkspace: z.boolean().optional().default(ASTRO_CONFIG_DEFAULTS.experimental.chromeDevtoolsWorkspace),
|
|
277
279
|
svgo: z.union([z.boolean(), z.custom((value) => value && typeof value === "object")]).optional().default(ASTRO_CONFIG_DEFAULTS.experimental.svgo)
|
|
278
280
|
}).prefault({}),
|
|
279
|
-
legacy: z.object({
|
|
281
|
+
legacy: z.object({
|
|
282
|
+
collectionsBackwardsCompat: z.boolean().optional().default(false)
|
|
283
|
+
}).prefault({})
|
|
280
284
|
});
|
|
281
285
|
export {
|
|
282
286
|
ASTRO_CONFIG_DEFAULTS,
|
|
@@ -415,7 +415,9 @@ export declare function createRelativeSchema(cmd: string, fileProtocolRoot: stri
|
|
|
415
415
|
chromeDevtoolsWorkspace: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
416
416
|
svgo: z.ZodDefault<z.ZodOptional<z.ZodUnion<readonly [z.ZodBoolean, z.ZodCustom<import("svgo").Config, import("svgo").Config>]>>>;
|
|
417
417
|
}, z.core.$strict>>;
|
|
418
|
-
legacy: z.
|
|
418
|
+
legacy: z.ZodPrefault<z.ZodObject<{
|
|
419
|
+
collectionsBackwardsCompat: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
420
|
+
}, z.core.$strip>>;
|
|
419
421
|
root: z.ZodPipe<z.ZodDefault<z.ZodString>, z.ZodTransform<import("url").URL, string>>;
|
|
420
422
|
srcDir: z.ZodPipe<z.ZodDefault<z.ZodString>, z.ZodTransform<import("url").URL, string>>;
|
|
421
423
|
compressHTML: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
@@ -633,7 +635,9 @@ export declare function createRelativeSchema(cmd: string, fileProtocolRoot: stri
|
|
|
633
635
|
optimizedFallbacks?: boolean | undefined;
|
|
634
636
|
})[] | undefined;
|
|
635
637
|
};
|
|
636
|
-
legacy:
|
|
638
|
+
legacy: {
|
|
639
|
+
collectionsBackwardsCompat: boolean;
|
|
640
|
+
};
|
|
637
641
|
root: import("url").URL;
|
|
638
642
|
srcDir: import("url").URL;
|
|
639
643
|
compressHTML: boolean;
|
|
@@ -890,7 +894,9 @@ export declare function createRelativeSchema(cmd: string, fileProtocolRoot: stri
|
|
|
890
894
|
optimizedFallbacks?: boolean | undefined;
|
|
891
895
|
})[] | undefined;
|
|
892
896
|
};
|
|
893
|
-
legacy:
|
|
897
|
+
legacy: {
|
|
898
|
+
collectionsBackwardsCompat: boolean;
|
|
899
|
+
};
|
|
894
900
|
root: import("url").URL;
|
|
895
901
|
srcDir: import("url").URL;
|
|
896
902
|
compressHTML: boolean;
|
|
@@ -17,7 +17,11 @@ import {
|
|
|
17
17
|
import { AstroTimer } from "./timer.js";
|
|
18
18
|
import { loadTSConfig } from "./tsconfig.js";
|
|
19
19
|
function createBaseSettings(config, logLevel) {
|
|
20
|
-
const { contentDir } = getContentPaths(
|
|
20
|
+
const { contentDir } = getContentPaths(
|
|
21
|
+
config,
|
|
22
|
+
void 0,
|
|
23
|
+
config.legacy?.collectionsBackwardsCompat
|
|
24
|
+
);
|
|
21
25
|
const dotAstroDir = new URL(".astro/", config.root);
|
|
22
26
|
const preferences = createPreferences(config, dotAstroDir);
|
|
23
27
|
return {
|
package/dist/core/constants.js
CHANGED
package/dist/core/dev/dev.js
CHANGED
|
@@ -22,7 +22,7 @@ async function dev(inlineConfig) {
|
|
|
22
22
|
await telemetry.record([]);
|
|
23
23
|
const restart = await createContainerWithAutomaticRestart({ inlineConfig, fs });
|
|
24
24
|
const logger = restart.container.logger;
|
|
25
|
-
const currentVersion = "6.0.0-alpha.
|
|
25
|
+
const currentVersion = "6.0.0-alpha.5";
|
|
26
26
|
const isPrerelease = currentVersion.includes("-");
|
|
27
27
|
if (!isPrerelease) {
|
|
28
28
|
try {
|
package/dist/core/messages.js
CHANGED
|
@@ -28,7 +28,7 @@ function serverStart({
|
|
|
28
28
|
host,
|
|
29
29
|
base
|
|
30
30
|
}) {
|
|
31
|
-
const version = "6.0.0-alpha.
|
|
31
|
+
const version = "6.0.0-alpha.5";
|
|
32
32
|
const localPrefix = `${dim("\u2503")} Local `;
|
|
33
33
|
const networkPrefix = `${dim("\u2503")} Network `;
|
|
34
34
|
const emptyPrefix = " ".repeat(11);
|
|
@@ -265,7 +265,7 @@ function printHelp({
|
|
|
265
265
|
message.push(
|
|
266
266
|
linebreak(),
|
|
267
267
|
` ${bgGreen(black(` ${commandName} `))} ${green(
|
|
268
|
-
`v${"6.0.0-alpha.
|
|
268
|
+
`v${"6.0.0-alpha.5"}`
|
|
269
269
|
)} ${headline}`
|
|
270
270
|
);
|
|
271
271
|
}
|
package/dist/core/sync/index.js
CHANGED
|
@@ -109,7 +109,11 @@ async function syncInternal({
|
|
|
109
109
|
}
|
|
110
110
|
settings.timer.end("Sync content layer");
|
|
111
111
|
} else {
|
|
112
|
-
const paths = getContentPaths(
|
|
112
|
+
const paths = getContentPaths(
|
|
113
|
+
settings.config,
|
|
114
|
+
fs,
|
|
115
|
+
settings.config.legacy?.collectionsBackwardsCompat
|
|
116
|
+
);
|
|
113
117
|
if (paths.config.exists || paths.liveConfig.exists) {
|
|
114
118
|
settings.injectedTypes.push({
|
|
115
119
|
filename: CONTENT_TYPES_FILE
|
|
@@ -210,7 +214,11 @@ async function syncContentCollections(settings, { mode, logger, fs }) {
|
|
|
210
214
|
}
|
|
211
215
|
let configFile;
|
|
212
216
|
try {
|
|
213
|
-
const contentPaths = getContentPaths(
|
|
217
|
+
const contentPaths = getContentPaths(
|
|
218
|
+
settings.config,
|
|
219
|
+
fs,
|
|
220
|
+
settings.config.legacy?.collectionsBackwardsCompat
|
|
221
|
+
);
|
|
214
222
|
if (contentPaths.config.exists) {
|
|
215
223
|
const matches = /\/(src\/.+)/.exec(contentPaths.config.url.href);
|
|
216
224
|
if (matches) {
|
|
@@ -2249,7 +2249,25 @@ export interface AstroUserConfig<TLocales extends Locales = never, TDriver exten
|
|
|
2249
2249
|
* These flags allow you to opt in to some deprecated or otherwise outdated behavior of Astro
|
|
2250
2250
|
* in the latest version, so that you can continue to upgrade and take advantage of new Astro releases.
|
|
2251
2251
|
*/
|
|
2252
|
-
legacy?:
|
|
2252
|
+
legacy?: {
|
|
2253
|
+
/**
|
|
2254
|
+
* Enable backwards compatibility for v4 content collections.
|
|
2255
|
+
*
|
|
2256
|
+
* When enabled, restores the following v4 behaviors:
|
|
2257
|
+
* - Allows legacy config file location: `src/content/config.ts`
|
|
2258
|
+
* - Allows collections without explicit loaders (automatically wraps with glob loader)
|
|
2259
|
+
* - Supports `type: 'content'` and `type: 'data'` without loaders
|
|
2260
|
+
* - Preserves legacy entry API: `entry.slug` and `entry.render()`
|
|
2261
|
+
* - Uses path-based entry IDs instead of slug-based IDs
|
|
2262
|
+
*
|
|
2263
|
+
* This is a temporary migration helper for projects upgrading to v6.
|
|
2264
|
+
* Migrate collections to the Content Layer API, then disable this flag.
|
|
2265
|
+
*
|
|
2266
|
+
* @type {boolean}
|
|
2267
|
+
* @default false
|
|
2268
|
+
*/
|
|
2269
|
+
collectionsBackwardsCompat?: boolean;
|
|
2270
|
+
};
|
|
2253
2271
|
/**
|
|
2254
2272
|
*
|
|
2255
2273
|
* @kind heading
|
|
@@ -5,6 +5,18 @@ import { getVirtualModulePageName } from "./util.js";
|
|
|
5
5
|
import { ASTRO_VITE_ENVIRONMENT_NAMES } from "../core/constants.js";
|
|
6
6
|
const VIRTUAL_PAGES_MODULE_ID = "virtual:astro:pages";
|
|
7
7
|
const VIRTUAL_PAGES_RESOLVED_MODULE_ID = "\0" + VIRTUAL_PAGES_MODULE_ID;
|
|
8
|
+
function getRoutesForEnvironment(routes, isPrerender) {
|
|
9
|
+
const result = /* @__PURE__ */ new Set();
|
|
10
|
+
for (const route of routes) {
|
|
11
|
+
if (route.prerender === isPrerender) {
|
|
12
|
+
result.add(route);
|
|
13
|
+
}
|
|
14
|
+
if (route.redirectRoute) {
|
|
15
|
+
result.add(route.redirectRoute);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
return result;
|
|
19
|
+
}
|
|
8
20
|
function pluginPages({ routesList }) {
|
|
9
21
|
return {
|
|
10
22
|
name: "@astro/plugin-pages",
|
|
@@ -28,7 +40,11 @@ function pluginPages({ routesList }) {
|
|
|
28
40
|
const imports = [];
|
|
29
41
|
const pageMap = [];
|
|
30
42
|
let i = 0;
|
|
31
|
-
|
|
43
|
+
const envName = this.environment.name;
|
|
44
|
+
const isSSR = envName === ASTRO_VITE_ENVIRONMENT_NAMES.ssr;
|
|
45
|
+
const isPrerender = envName === ASTRO_VITE_ENVIRONMENT_NAMES.prerender;
|
|
46
|
+
const routes = isSSR || isPrerender ? getRoutesForEnvironment(routesList.routes, isPrerender) : new Set(routesList.routes);
|
|
47
|
+
for (const route of routes) {
|
|
32
48
|
if (routeIsRedirect(route)) {
|
|
33
49
|
continue;
|
|
34
50
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "astro",
|
|
3
|
-
"version": "6.0.0-alpha.
|
|
3
|
+
"version": "6.0.0-alpha.5",
|
|
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",
|
|
@@ -194,8 +194,8 @@
|
|
|
194
194
|
"undici": "^6.22.0",
|
|
195
195
|
"unified": "^11.0.5",
|
|
196
196
|
"vitest": "^3.2.4",
|
|
197
|
-
"
|
|
198
|
-
"
|
|
197
|
+
"astro-scripts": "0.0.14",
|
|
198
|
+
"@astrojs/check": "0.9.6-alpha.0"
|
|
199
199
|
},
|
|
200
200
|
"engines": {
|
|
201
201
|
"node": "^20.19.5 || >=22.12.0",
|