astro 5.9.4 → 5.10.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/client.d.ts +1 -3
- package/components/Image.astro +5 -6
- package/components/Picture.astro +5 -5
- package/components/ResponsivePicture.astro +1 -0
- package/dist/actions/integration.d.ts +2 -1
- package/dist/actions/integration.js +3 -2
- package/dist/actions/utils.d.ts +1 -1
- package/dist/actions/utils.js +9 -8
- package/dist/assets/internal.d.ts +1 -5
- package/dist/assets/internal.js +21 -23
- package/dist/assets/types.d.ts +4 -4
- package/dist/assets/vite-plugin-assets.js +2 -2
- package/dist/content/config.d.ts +74 -0
- package/dist/content/config.js +78 -0
- package/dist/content/consts.d.ts +1 -0
- package/dist/content/consts.js +2 -0
- package/dist/content/content-layer.js +3 -3
- package/dist/content/loaders/errors.d.ts +20 -0
- package/dist/content/loaders/errors.js +64 -0
- package/dist/content/loaders/types.d.ts +21 -0
- package/dist/content/runtime.d.ts +23 -7
- package/dist/content/runtime.js +218 -28
- package/dist/content/types-generator.js +11 -4
- package/dist/content/utils.d.ts +37 -1
- package/dist/content/utils.js +29 -8
- package/dist/content/vite-plugin-content-virtual-mod.d.ts +1 -1
- package/dist/content/vite-plugin-content-virtual-mod.js +20 -6
- package/dist/core/config/schemas/base.d.ts +39 -39
- package/dist/core/config/schemas/base.js +8 -8
- package/dist/core/config/schemas/refined.js +0 -7
- package/dist/core/config/schemas/relative.d.ts +51 -51
- package/dist/core/constants.js +1 -1
- package/dist/core/csp/config.d.ts +3 -3
- package/dist/core/csp/config.js +1 -0
- package/dist/core/dev/dev.js +1 -1
- package/dist/core/errors/errors-data.d.ts +16 -0
- package/dist/core/errors/errors-data.js +15 -4
- package/dist/core/errors/errors.js +1 -1
- package/dist/core/messages.js +2 -2
- package/dist/integrations/hooks.js +5 -2
- package/dist/runtime/client/dev-toolbar/apps/astro.js +4 -6
- package/dist/types/public/config.d.ts +39 -130
- package/dist/types/public/content.d.ts +30 -0
- package/package.json +2 -1
- package/templates/content/module.mjs +14 -0
- package/templates/content/types.d.ts +43 -0
- package/types/content.d.ts +23 -80
package/dist/content/runtime.js
CHANGED
|
@@ -3,8 +3,9 @@ import { Traverse } from "neotraverse/modern";
|
|
|
3
3
|
import pLimit from "p-limit";
|
|
4
4
|
import { ZodIssueCode, z } from "zod";
|
|
5
5
|
import { imageSrcToImportId } from "../assets/utils/resolveImports.js";
|
|
6
|
-
import { AstroError, AstroErrorData
|
|
6
|
+
import { AstroError, AstroErrorData } from "../core/errors/index.js";
|
|
7
7
|
import { prependForwardSlash } from "../core/path.js";
|
|
8
|
+
import { defineCollection as defineCollectionOrig } from "./config.js";
|
|
8
9
|
import {
|
|
9
10
|
createComponent,
|
|
10
11
|
createHeadAndContent,
|
|
@@ -15,28 +16,14 @@ import {
|
|
|
15
16
|
render as serverRender,
|
|
16
17
|
unescapeHTML
|
|
17
18
|
} from "../runtime/server/index.js";
|
|
18
|
-
import {
|
|
19
|
+
import { IMAGE_IMPORT_PREFIX } from "./consts.js";
|
|
19
20
|
import { globalDataStore } from "./data-store.js";
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
return match?.[1] ?? null;
|
|
27
|
-
}
|
|
28
|
-
function defineCollection(config) {
|
|
29
|
-
if ("loader" in config) {
|
|
30
|
-
if (config.type && config.type !== CONTENT_LAYER_TYPE) {
|
|
31
|
-
throw new AstroUserError(
|
|
32
|
-
`Collections that use the Content Layer API must have a \`loader\` defined and no \`type\` set. Check your collection definitions in ${getImporterFilename() ?? "your content config file"}.`
|
|
33
|
-
);
|
|
34
|
-
}
|
|
35
|
-
config.type = CONTENT_LAYER_TYPE;
|
|
36
|
-
}
|
|
37
|
-
if (!config.type) config.type = "content";
|
|
38
|
-
return config;
|
|
39
|
-
}
|
|
21
|
+
import {
|
|
22
|
+
LiveCollectionCacheHintError,
|
|
23
|
+
LiveCollectionError,
|
|
24
|
+
LiveCollectionValidationError,
|
|
25
|
+
LiveEntryNotFoundError
|
|
26
|
+
} from "./loaders/errors.js";
|
|
40
27
|
function createCollectionToGlobResultMap({
|
|
41
28
|
globResult,
|
|
42
29
|
contentDir
|
|
@@ -52,13 +39,57 @@ function createCollectionToGlobResultMap({
|
|
|
52
39
|
}
|
|
53
40
|
return collectionToGlobResultMap;
|
|
54
41
|
}
|
|
42
|
+
const cacheHintSchema = z.object({
|
|
43
|
+
tags: z.array(z.string()).optional(),
|
|
44
|
+
maxAge: z.number().optional()
|
|
45
|
+
});
|
|
46
|
+
async function parseLiveEntry(entry, schema, collection) {
|
|
47
|
+
try {
|
|
48
|
+
const parsed = await schema.safeParseAsync(entry.data);
|
|
49
|
+
if (!parsed.success) {
|
|
50
|
+
return {
|
|
51
|
+
error: new LiveCollectionValidationError(collection, entry.id, parsed.error)
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
if (entry.cacheHint) {
|
|
55
|
+
const cacheHint = cacheHintSchema.safeParse(entry.cacheHint);
|
|
56
|
+
if (!cacheHint.success) {
|
|
57
|
+
return {
|
|
58
|
+
error: new LiveCollectionCacheHintError(collection, entry.id, cacheHint.error)
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
entry.cacheHint = cacheHint.data;
|
|
62
|
+
}
|
|
63
|
+
return {
|
|
64
|
+
entry: {
|
|
65
|
+
...entry,
|
|
66
|
+
data: parsed.data
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
} catch (error) {
|
|
70
|
+
return {
|
|
71
|
+
error: new LiveCollectionError(
|
|
72
|
+
collection,
|
|
73
|
+
`Unexpected error parsing entry ${entry.id} in collection ${collection}`,
|
|
74
|
+
error
|
|
75
|
+
)
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
}
|
|
55
79
|
function createGetCollection({
|
|
56
80
|
contentCollectionToEntryMap,
|
|
57
81
|
dataCollectionToEntryMap,
|
|
58
82
|
getRenderEntryImport,
|
|
59
|
-
cacheEntriesByCollection
|
|
83
|
+
cacheEntriesByCollection,
|
|
84
|
+
liveCollections
|
|
60
85
|
}) {
|
|
61
86
|
return async function getCollection(collection, filter) {
|
|
87
|
+
if (collection in liveCollections) {
|
|
88
|
+
throw new AstroError({
|
|
89
|
+
...AstroErrorData.UnknownContentCollectionError,
|
|
90
|
+
message: `Collection "${collection}" is a live collection. Use getLiveCollection() instead of getCollection().`
|
|
91
|
+
});
|
|
92
|
+
}
|
|
62
93
|
const hasFilter = typeof filter === "function";
|
|
63
94
|
const store = await globalDataStore.get();
|
|
64
95
|
let type;
|
|
@@ -219,22 +250,35 @@ function emulateLegacyEntry({ legacyId, ...entry }) {
|
|
|
219
250
|
function createGetEntry({
|
|
220
251
|
getEntryImport,
|
|
221
252
|
getRenderEntryImport,
|
|
222
|
-
collectionNames
|
|
253
|
+
collectionNames,
|
|
254
|
+
liveCollections
|
|
223
255
|
}) {
|
|
224
|
-
return async function getEntry(collectionOrLookupObject,
|
|
256
|
+
return async function getEntry(collectionOrLookupObject, lookup) {
|
|
225
257
|
let collection, lookupId;
|
|
226
258
|
if (typeof collectionOrLookupObject === "string") {
|
|
227
259
|
collection = collectionOrLookupObject;
|
|
228
|
-
if (!
|
|
260
|
+
if (!lookup)
|
|
229
261
|
throw new AstroError({
|
|
230
262
|
...AstroErrorData.UnknownContentCollectionError,
|
|
231
263
|
message: "`getEntry()` requires an entry identifier as the second argument."
|
|
232
264
|
});
|
|
233
|
-
lookupId =
|
|
265
|
+
lookupId = lookup;
|
|
234
266
|
} else {
|
|
235
267
|
collection = collectionOrLookupObject.collection;
|
|
236
268
|
lookupId = "id" in collectionOrLookupObject ? collectionOrLookupObject.id : collectionOrLookupObject.slug;
|
|
237
269
|
}
|
|
270
|
+
if (collection in liveCollections) {
|
|
271
|
+
throw new AstroError({
|
|
272
|
+
...AstroErrorData.UnknownContentCollectionError,
|
|
273
|
+
message: `Collection "${collection}" is a live collection. Use getLiveEntry() instead of getEntry().`
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
if (typeof lookupId === "object") {
|
|
277
|
+
throw new AstroError({
|
|
278
|
+
...AstroErrorData.UnknownContentCollectionError,
|
|
279
|
+
message: `The entry identifier must be a string. Received object.`
|
|
280
|
+
});
|
|
281
|
+
}
|
|
238
282
|
const store = await globalDataStore.get();
|
|
239
283
|
if (store.hasCollection(collection)) {
|
|
240
284
|
const entry2 = store.get(collection, lookupId);
|
|
@@ -291,6 +335,136 @@ function createGetEntries(getEntry) {
|
|
|
291
335
|
return Promise.all(entries.map((e) => getEntry(e)));
|
|
292
336
|
};
|
|
293
337
|
}
|
|
338
|
+
function createGetLiveCollection({
|
|
339
|
+
liveCollections
|
|
340
|
+
}) {
|
|
341
|
+
return async function getLiveCollection(collection, filter) {
|
|
342
|
+
if (!(collection in liveCollections)) {
|
|
343
|
+
return {
|
|
344
|
+
error: new LiveCollectionError(
|
|
345
|
+
collection,
|
|
346
|
+
`Collection "${collection}" is not a live collection. Use getCollection() instead of getLiveCollection() to load regular content collections.`
|
|
347
|
+
)
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
try {
|
|
351
|
+
const context = {
|
|
352
|
+
filter
|
|
353
|
+
};
|
|
354
|
+
const response = await liveCollections[collection].loader?.loadCollection?.(context);
|
|
355
|
+
if (response && "error" in response) {
|
|
356
|
+
return { error: response.error };
|
|
357
|
+
}
|
|
358
|
+
const { schema } = liveCollections[collection];
|
|
359
|
+
let processedEntries = response.entries;
|
|
360
|
+
if (schema) {
|
|
361
|
+
const entryResults = await Promise.all(
|
|
362
|
+
response.entries.map((entry) => parseLiveEntry(entry, schema, collection))
|
|
363
|
+
);
|
|
364
|
+
for (const result of entryResults) {
|
|
365
|
+
if (result.error) {
|
|
366
|
+
return { error: result.error };
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
processedEntries = entryResults.map((result) => result.entry);
|
|
370
|
+
}
|
|
371
|
+
let cacheHint = response.cacheHint;
|
|
372
|
+
if (cacheHint) {
|
|
373
|
+
const cacheHintResult = cacheHintSchema.safeParse(cacheHint);
|
|
374
|
+
if (!cacheHintResult.success) {
|
|
375
|
+
return {
|
|
376
|
+
error: new LiveCollectionCacheHintError(collection, void 0, cacheHintResult.error)
|
|
377
|
+
};
|
|
378
|
+
}
|
|
379
|
+
cacheHint = cacheHintResult.data;
|
|
380
|
+
}
|
|
381
|
+
if (processedEntries.length > 0) {
|
|
382
|
+
const entryTags = /* @__PURE__ */ new Set();
|
|
383
|
+
let minMaxAge;
|
|
384
|
+
for (const entry of processedEntries) {
|
|
385
|
+
if (entry.cacheHint) {
|
|
386
|
+
if (entry.cacheHint.tags) {
|
|
387
|
+
entry.cacheHint.tags.forEach((tag) => entryTags.add(tag));
|
|
388
|
+
}
|
|
389
|
+
if (typeof entry.cacheHint.maxAge === "number") {
|
|
390
|
+
minMaxAge = minMaxAge === void 0 ? entry.cacheHint.maxAge : Math.min(minMaxAge, entry.cacheHint.maxAge);
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
if (entryTags.size > 0 || minMaxAge !== void 0 || cacheHint) {
|
|
395
|
+
const mergedCacheHint = {};
|
|
396
|
+
if (cacheHint?.tags || entryTags.size > 0) {
|
|
397
|
+
mergedCacheHint.tags = [...cacheHint?.tags || [], ...entryTags];
|
|
398
|
+
}
|
|
399
|
+
if (cacheHint?.maxAge !== void 0 || minMaxAge !== void 0) {
|
|
400
|
+
mergedCacheHint.maxAge = cacheHint?.maxAge !== void 0 && minMaxAge !== void 0 ? Math.min(cacheHint.maxAge, minMaxAge) : cacheHint?.maxAge ?? minMaxAge;
|
|
401
|
+
}
|
|
402
|
+
cacheHint = mergedCacheHint;
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
return {
|
|
406
|
+
entries: processedEntries,
|
|
407
|
+
cacheHint
|
|
408
|
+
};
|
|
409
|
+
} catch (error) {
|
|
410
|
+
return {
|
|
411
|
+
error: new LiveCollectionError(
|
|
412
|
+
collection,
|
|
413
|
+
`Unexpected error loading collection ${collection}`,
|
|
414
|
+
error
|
|
415
|
+
)
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
function createGetLiveEntry({
|
|
421
|
+
liveCollections
|
|
422
|
+
}) {
|
|
423
|
+
return async function getLiveEntry(collection, lookup) {
|
|
424
|
+
if (!(collection in liveCollections)) {
|
|
425
|
+
return {
|
|
426
|
+
error: new LiveCollectionError(
|
|
427
|
+
collection,
|
|
428
|
+
`Collection "${collection}" is not a live collection. Use getCollection() instead of getLiveEntry() to load regular content collections.`
|
|
429
|
+
)
|
|
430
|
+
};
|
|
431
|
+
}
|
|
432
|
+
try {
|
|
433
|
+
const lookupObject = {
|
|
434
|
+
filter: typeof lookup === "string" ? { id: lookup } : lookup
|
|
435
|
+
};
|
|
436
|
+
let entry = await liveCollections[collection].loader?.loadEntry?.(lookupObject);
|
|
437
|
+
if (entry && "error" in entry) {
|
|
438
|
+
return { error: entry.error };
|
|
439
|
+
}
|
|
440
|
+
if (!entry) {
|
|
441
|
+
return {
|
|
442
|
+
error: new LiveEntryNotFoundError(collection, lookup)
|
|
443
|
+
};
|
|
444
|
+
}
|
|
445
|
+
const { schema } = liveCollections[collection];
|
|
446
|
+
if (schema) {
|
|
447
|
+
const result = await parseLiveEntry(entry, schema, collection);
|
|
448
|
+
if (result.error) {
|
|
449
|
+
return { error: result.error };
|
|
450
|
+
}
|
|
451
|
+
entry = result.entry;
|
|
452
|
+
}
|
|
453
|
+
return {
|
|
454
|
+
entry,
|
|
455
|
+
cacheHint: entry.cacheHint
|
|
456
|
+
};
|
|
457
|
+
} catch (error) {
|
|
458
|
+
return {
|
|
459
|
+
error: new LiveCollectionError(
|
|
460
|
+
collection,
|
|
461
|
+
`Unexpected error loading entry ${collection} \u2192 ${typeof lookup === "string" ? lookup : JSON.stringify(lookup)}`,
|
|
462
|
+
error
|
|
463
|
+
)
|
|
464
|
+
};
|
|
465
|
+
}
|
|
466
|
+
};
|
|
467
|
+
}
|
|
294
468
|
const CONTENT_LAYER_IMAGE_REGEX = /__ASTRO_IMAGE_="([^"]+)"/g;
|
|
295
469
|
async function updateImageReferencesInBody(html, fileName) {
|
|
296
470
|
const { default: imageAssetMap } = await import("astro:asset-imports");
|
|
@@ -499,15 +673,31 @@ function createReference({ lookupMap }) {
|
|
|
499
673
|
function isPropagatedAssetsModule(module) {
|
|
500
674
|
return typeof module === "object" && module != null && "__astroPropagation" in module;
|
|
501
675
|
}
|
|
676
|
+
function defineCollection(config) {
|
|
677
|
+
if (config.type === "live") {
|
|
678
|
+
throw new AstroError({
|
|
679
|
+
...AstroErrorData.LiveContentConfigError,
|
|
680
|
+
message: AstroErrorData.LiveContentConfigError.message(
|
|
681
|
+
"Collections with type `live` must be defined in a `src/live.config.ts` file."
|
|
682
|
+
)
|
|
683
|
+
});
|
|
684
|
+
}
|
|
685
|
+
return defineCollectionOrig(config);
|
|
686
|
+
}
|
|
502
687
|
export {
|
|
688
|
+
LiveCollectionCacheHintError,
|
|
689
|
+
LiveCollectionError,
|
|
690
|
+
LiveCollectionValidationError,
|
|
691
|
+
LiveEntryNotFoundError,
|
|
503
692
|
createCollectionToGlobResultMap,
|
|
504
693
|
createGetCollection,
|
|
505
694
|
createGetDataEntryById,
|
|
506
695
|
createGetEntries,
|
|
507
696
|
createGetEntry,
|
|
508
697
|
createGetEntryBySlug,
|
|
698
|
+
createGetLiveCollection,
|
|
699
|
+
createGetLiveEntry,
|
|
509
700
|
createReference,
|
|
510
701
|
defineCollection,
|
|
511
|
-
getImporterFilename,
|
|
512
702
|
renderEntry
|
|
513
703
|
};
|
|
@@ -6,12 +6,13 @@ import { normalizePath } from "vite";
|
|
|
6
6
|
import { z } from "zod";
|
|
7
7
|
import { zodToJsonSchema } from "zod-to-json-schema";
|
|
8
8
|
import { AstroError } from "../core/errors/errors.js";
|
|
9
|
-
import { AstroErrorData } from "../core/errors/index.js";
|
|
9
|
+
import { AstroErrorData, AstroUserError } from "../core/errors/index.js";
|
|
10
10
|
import { isRelativePath } from "../core/path.js";
|
|
11
11
|
import {
|
|
12
12
|
COLLECTIONS_DIR,
|
|
13
13
|
CONTENT_LAYER_TYPE,
|
|
14
14
|
CONTENT_TYPES_FILE,
|
|
15
|
+
LIVE_CONTENT_TYPE,
|
|
15
16
|
VIRTUAL_MODULE_ID
|
|
16
17
|
} from "./consts.js";
|
|
17
18
|
import {
|
|
@@ -366,6 +367,10 @@ async function writeContentFiles({
|
|
|
366
367
|
const collectionEntryKeys = Object.keys(collection.entries).sort();
|
|
367
368
|
const dataType = await typeForCollection(collectionConfig, collectionKey);
|
|
368
369
|
switch (resolvedType) {
|
|
370
|
+
case LIVE_CONTENT_TYPE:
|
|
371
|
+
throw new AstroUserError(
|
|
372
|
+
`Invalid definition for collection ${collectionKey}: Live content collections must be defined in "src/live.config.ts"`
|
|
373
|
+
);
|
|
369
374
|
case "content":
|
|
370
375
|
if (collectionEntryKeys.length === 0) {
|
|
371
376
|
contentTypesStr += `${collectionKey}: Record<string, {
|
|
@@ -468,16 +473,18 @@ async function writeContentFiles({
|
|
|
468
473
|
settings.dotAstroDir.pathname,
|
|
469
474
|
contentPaths.config.url.pathname
|
|
470
475
|
);
|
|
476
|
+
const liveConfigPathRelativeToCacheDir = contentPaths.liveConfig?.exists ? normalizeConfigPath(settings.dotAstroDir.pathname, contentPaths.liveConfig.url.pathname) : void 0;
|
|
471
477
|
for (const contentEntryType of contentEntryTypes) {
|
|
472
478
|
if (contentEntryType.contentModuleTypes) {
|
|
473
479
|
typeTemplateContent = contentEntryType.contentModuleTypes + "\n" + typeTemplateContent;
|
|
474
480
|
}
|
|
475
481
|
}
|
|
476
|
-
typeTemplateContent = typeTemplateContent.replace("// @@CONTENT_ENTRY_MAP@@", contentTypesStr)
|
|
477
|
-
typeTemplateContent = typeTemplateContent.replace("// @@DATA_ENTRY_MAP@@", dataTypesStr);
|
|
478
|
-
typeTemplateContent = typeTemplateContent.replace(
|
|
482
|
+
typeTemplateContent = typeTemplateContent.replace("// @@CONTENT_ENTRY_MAP@@", contentTypesStr).replace("// @@DATA_ENTRY_MAP@@", dataTypesStr).replace(
|
|
479
483
|
"'@@CONTENT_CONFIG_TYPE@@'",
|
|
480
484
|
contentConfig ? `typeof import(${configPathRelativeToCacheDir})` : "never"
|
|
485
|
+
).replace(
|
|
486
|
+
"'@@LIVE_CONTENT_CONFIG_TYPE@@'",
|
|
487
|
+
liveConfigPathRelativeToCacheDir ? `typeof import(${liveConfigPathRelativeToCacheDir})` : "never"
|
|
481
488
|
);
|
|
482
489
|
if (settings.injectedTypes.some((t) => t.filename === CONTENT_TYPES_FILE)) {
|
|
483
490
|
await fs.promises.writeFile(
|
package/dist/content/utils.d.ts
CHANGED
|
@@ -176,6 +176,18 @@ declare const collectionConfigParser: z.ZodUnion<[z.ZodObject<{
|
|
|
176
176
|
};
|
|
177
177
|
schema?: any;
|
|
178
178
|
_legacy?: boolean | undefined;
|
|
179
|
+
}>, z.ZodObject<{
|
|
180
|
+
type: z.ZodLiteral<"live">;
|
|
181
|
+
schema: z.ZodOptional<z.ZodAny>;
|
|
182
|
+
loader: z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodUnknown>;
|
|
183
|
+
}, "strip", z.ZodTypeAny, {
|
|
184
|
+
type: "live";
|
|
185
|
+
loader: (...args: unknown[]) => unknown;
|
|
186
|
+
schema?: any;
|
|
187
|
+
}, {
|
|
188
|
+
type: "live";
|
|
189
|
+
loader: (...args: unknown[]) => unknown;
|
|
190
|
+
schema?: any;
|
|
179
191
|
}>]>;
|
|
180
192
|
declare const contentConfigParser: z.ZodObject<{
|
|
181
193
|
collections: z.ZodRecord<z.ZodString, z.ZodUnion<[z.ZodObject<{
|
|
@@ -321,6 +333,18 @@ declare const contentConfigParser: z.ZodObject<{
|
|
|
321
333
|
};
|
|
322
334
|
schema?: any;
|
|
323
335
|
_legacy?: boolean | undefined;
|
|
336
|
+
}>, z.ZodObject<{
|
|
337
|
+
type: z.ZodLiteral<"live">;
|
|
338
|
+
schema: z.ZodOptional<z.ZodAny>;
|
|
339
|
+
loader: z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodUnknown>;
|
|
340
|
+
}, "strip", z.ZodTypeAny, {
|
|
341
|
+
type: "live";
|
|
342
|
+
loader: (...args: unknown[]) => unknown;
|
|
343
|
+
schema?: any;
|
|
344
|
+
}, {
|
|
345
|
+
type: "live";
|
|
346
|
+
loader: (...args: unknown[]) => unknown;
|
|
347
|
+
schema?: any;
|
|
324
348
|
}>]>>;
|
|
325
349
|
}, "strip", z.ZodTypeAny, {
|
|
326
350
|
collections: Record<string, {
|
|
@@ -351,6 +375,10 @@ declare const contentConfigParser: z.ZodObject<{
|
|
|
351
375
|
};
|
|
352
376
|
schema?: any;
|
|
353
377
|
_legacy?: boolean | undefined;
|
|
378
|
+
} | {
|
|
379
|
+
type: "live";
|
|
380
|
+
loader: (...args: unknown[]) => unknown;
|
|
381
|
+
schema?: any;
|
|
354
382
|
}>;
|
|
355
383
|
}, {
|
|
356
384
|
collections: Record<string, {
|
|
@@ -381,6 +409,10 @@ declare const contentConfigParser: z.ZodObject<{
|
|
|
381
409
|
};
|
|
382
410
|
schema?: any;
|
|
383
411
|
_legacy?: boolean | undefined;
|
|
412
|
+
} | {
|
|
413
|
+
type: "live";
|
|
414
|
+
loader: (...args: unknown[]) => unknown;
|
|
415
|
+
schema?: any;
|
|
384
416
|
}>;
|
|
385
417
|
}>;
|
|
386
418
|
export type CollectionConfig = z.infer<typeof collectionConfigParser>;
|
|
@@ -484,8 +516,12 @@ export type ContentPaths = {
|
|
|
484
516
|
exists: boolean;
|
|
485
517
|
url: URL;
|
|
486
518
|
};
|
|
519
|
+
liveConfig: {
|
|
520
|
+
exists: boolean;
|
|
521
|
+
url: URL;
|
|
522
|
+
};
|
|
487
523
|
};
|
|
488
|
-
export declare function getContentPaths({ srcDir, legacy, root }: Pick<AstroConfig, 'root' | 'srcDir' | 'legacy'>, fs?: typeof fsMod): ContentPaths;
|
|
524
|
+
export declare function getContentPaths({ srcDir, legacy, root, experimental, }: Pick<AstroConfig, 'root' | 'srcDir' | 'legacy' | 'experimental'>, fs?: typeof fsMod): ContentPaths;
|
|
489
525
|
/**
|
|
490
526
|
* Check for slug in content entry frontmatter and validate the type,
|
|
491
527
|
* falling back to the `generatedSlug` if none is found.
|
package/dist/content/utils.js
CHANGED
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
CONTENT_MODULE_FLAG,
|
|
16
16
|
DEFERRED_MODULE,
|
|
17
17
|
IMAGE_IMPORT_PREFIX,
|
|
18
|
+
LIVE_CONTENT_TYPE,
|
|
18
19
|
PROPAGATED_ASSET_FLAG
|
|
19
20
|
} from "./consts.js";
|
|
20
21
|
import { glob } from "./loaders/glob.js";
|
|
@@ -78,6 +79,11 @@ const collectionConfigParser = z.union([
|
|
|
78
79
|
]),
|
|
79
80
|
/** deprecated */
|
|
80
81
|
_legacy: z.boolean().optional()
|
|
82
|
+
}),
|
|
83
|
+
z.object({
|
|
84
|
+
type: z.literal(LIVE_CONTENT_TYPE),
|
|
85
|
+
schema: z.any().optional(),
|
|
86
|
+
loader: z.function()
|
|
81
87
|
})
|
|
82
88
|
]);
|
|
83
89
|
const contentConfigParser = z.object({
|
|
@@ -394,7 +400,7 @@ async function autogenerateCollections({
|
|
|
394
400
|
const dataPattern = globWithUnderscoresIgnored("", dataExts);
|
|
395
401
|
let usesContentLayer = false;
|
|
396
402
|
for (const collectionName of Object.keys(collections)) {
|
|
397
|
-
if (collections[collectionName]?.type === "content_layer") {
|
|
403
|
+
if (collections[collectionName]?.type === "content_layer" || collections[collectionName]?.type === "live") {
|
|
398
404
|
usesContentLayer = true;
|
|
399
405
|
continue;
|
|
400
406
|
}
|
|
@@ -496,8 +502,14 @@ function contentObservable(initialCtx) {
|
|
|
496
502
|
subscribe
|
|
497
503
|
};
|
|
498
504
|
}
|
|
499
|
-
function getContentPaths({
|
|
500
|
-
|
|
505
|
+
function getContentPaths({
|
|
506
|
+
srcDir,
|
|
507
|
+
legacy,
|
|
508
|
+
root,
|
|
509
|
+
experimental
|
|
510
|
+
}, fs = fsMod) {
|
|
511
|
+
const configStats = searchConfig(fs, srcDir, legacy?.collections);
|
|
512
|
+
const liveConfigStats = experimental?.liveContentCollections ? searchLiveConfig(fs, srcDir) : { exists: false, url: new URL("./", srcDir) };
|
|
501
513
|
const pkgBase = new URL("../../", import.meta.url);
|
|
502
514
|
return {
|
|
503
515
|
root: new URL("./", root),
|
|
@@ -505,23 +517,32 @@ function getContentPaths({ srcDir, legacy, root }, fs = fsMod) {
|
|
|
505
517
|
assetsDir: new URL("./assets/", srcDir),
|
|
506
518
|
typesTemplate: new URL("templates/content/types.d.ts", pkgBase),
|
|
507
519
|
virtualModTemplate: new URL("templates/content/module.mjs", pkgBase),
|
|
508
|
-
config: configStats
|
|
520
|
+
config: configStats,
|
|
521
|
+
liveConfig: liveConfigStats
|
|
509
522
|
};
|
|
510
523
|
}
|
|
511
|
-
function
|
|
524
|
+
function searchConfig(fs, srcDir, legacy) {
|
|
512
525
|
const paths = [
|
|
513
526
|
...legacy ? [] : ["content.config.mjs", "content.config.js", "content.config.mts", "content.config.ts"],
|
|
514
527
|
"content/config.mjs",
|
|
515
528
|
"content/config.js",
|
|
516
529
|
"content/config.mts",
|
|
517
530
|
"content/config.ts"
|
|
518
|
-
]
|
|
519
|
-
|
|
531
|
+
];
|
|
532
|
+
return search(fs, srcDir, paths);
|
|
533
|
+
}
|
|
534
|
+
function searchLiveConfig(fs, srcDir) {
|
|
535
|
+
const paths = ["live.config.mjs", "live.config.js", "live.config.mts", "live.config.ts"];
|
|
536
|
+
return search(fs, srcDir, paths);
|
|
537
|
+
}
|
|
538
|
+
function search(fs, srcDir, paths) {
|
|
539
|
+
const urls = paths.map((p) => new URL(`./${p}`, srcDir));
|
|
540
|
+
for (const file of urls) {
|
|
520
541
|
if (fs.existsSync(file)) {
|
|
521
542
|
return { exists: true, url: file };
|
|
522
543
|
}
|
|
523
544
|
}
|
|
524
|
-
return { exists: false, url:
|
|
545
|
+
return { exists: false, url: urls[0] };
|
|
525
546
|
}
|
|
526
547
|
async function getEntrySlug({
|
|
527
548
|
id,
|
|
@@ -4,6 +4,7 @@ import { fileURLToPath, pathToFileURL } from "node:url";
|
|
|
4
4
|
import { dataToEsm } from "@rollup/pluginutils";
|
|
5
5
|
import pLimit from "p-limit";
|
|
6
6
|
import { glob } from "tinyglobby";
|
|
7
|
+
import { normalizePath } from "vite";
|
|
7
8
|
import { AstroError, AstroErrorData } from "../core/errors/index.js";
|
|
8
9
|
import { rootRelativePath } from "../core/viteUtils.js";
|
|
9
10
|
import { createDefaultAstroMetadata } from "../vite-plugin-astro/metadata.js";
|
|
@@ -52,11 +53,16 @@ function astroContentVirtualModPlugin({
|
|
|
52
53
|
}) {
|
|
53
54
|
let dataStoreFile;
|
|
54
55
|
let devServer;
|
|
56
|
+
let liveConfig;
|
|
55
57
|
return {
|
|
56
58
|
name: "astro-content-virtual-mod-plugin",
|
|
57
59
|
enforce: "pre",
|
|
58
60
|
config(_, env) {
|
|
59
61
|
dataStoreFile = getDataStoreFile(settings, env.command === "serve");
|
|
62
|
+
const contentPaths = getContentPaths(settings.config);
|
|
63
|
+
if (contentPaths.liveConfig.exists) {
|
|
64
|
+
liveConfig = normalizePath(fileURLToPath(contentPaths.liveConfig.url));
|
|
65
|
+
}
|
|
60
66
|
},
|
|
61
67
|
buildStart() {
|
|
62
68
|
if (devServer) {
|
|
@@ -64,8 +70,13 @@ function astroContentVirtualModPlugin({
|
|
|
64
70
|
invalidateDataStore(devServer);
|
|
65
71
|
}
|
|
66
72
|
},
|
|
67
|
-
async resolveId(id) {
|
|
73
|
+
async resolveId(id, importer) {
|
|
68
74
|
if (id === VIRTUAL_MODULE_ID) {
|
|
75
|
+
if (liveConfig && importer && liveConfig === normalizePath(importer)) {
|
|
76
|
+
return this.resolve("astro/content/config", importer, {
|
|
77
|
+
skipSelf: true
|
|
78
|
+
});
|
|
79
|
+
}
|
|
69
80
|
return RESOLVED_VIRTUAL_MODULE_ID;
|
|
70
81
|
}
|
|
71
82
|
if (id === DATA_STORE_VIRTUAL_ID) {
|
|
@@ -202,14 +213,17 @@ async function generateContentEntryFile({
|
|
|
202
213
|
message: AstroErrorData.ServerOnlyModule.message("astro:content")
|
|
203
214
|
});
|
|
204
215
|
} else {
|
|
205
|
-
virtualModContents = nodeFs.readFileSync(contentPaths.virtualModTemplate, "utf-8").replace("@@CONTENT_DIR@@", relContentDir).replace("'@@CONTENT_ENTRY_GLOB_PATH@@'", contentEntryGlobResult).replace("'@@DATA_ENTRY_GLOB_PATH@@'", dataEntryGlobResult).replace("'@@RENDER_ENTRY_GLOB_PATH@@'", renderEntryGlobResult).replace("/* @@LOOKUP_MAP_ASSIGNMENT@@ */", `lookupMap = ${JSON.stringify(lookupMap)};`)
|
|
216
|
+
virtualModContents = nodeFs.readFileSync(contentPaths.virtualModTemplate, "utf-8").replace("@@CONTENT_DIR@@", relContentDir).replace("'@@CONTENT_ENTRY_GLOB_PATH@@'", contentEntryGlobResult).replace("'@@DATA_ENTRY_GLOB_PATH@@'", dataEntryGlobResult).replace("'@@RENDER_ENTRY_GLOB_PATH@@'", renderEntryGlobResult).replace("/* @@LOOKUP_MAP_ASSIGNMENT@@ */", `lookupMap = ${JSON.stringify(lookupMap)};`).replace(
|
|
217
|
+
"/* @@LIVE_CONTENT_CONFIG@@ */",
|
|
218
|
+
contentPaths.liveConfig.exists ? (
|
|
219
|
+
// Dynamic import so it extracts the chunk and avoids a circular import
|
|
220
|
+
`const liveCollections = (await import(${JSON.stringify(fileURLToPath(contentPaths.liveConfig.url))})).collections;`
|
|
221
|
+
) : "const liveCollections = {};"
|
|
222
|
+
);
|
|
206
223
|
}
|
|
207
224
|
return virtualModContents;
|
|
208
225
|
}
|
|
209
|
-
async function generateLookupMap({
|
|
210
|
-
settings,
|
|
211
|
-
fs
|
|
212
|
-
}) {
|
|
226
|
+
async function generateLookupMap({ settings, fs }) {
|
|
213
227
|
const { root } = settings.config;
|
|
214
228
|
const contentPaths = getContentPaths(settings.config);
|
|
215
229
|
const relContentDir = rootRelativePath(root, contentPaths.contentDir, false);
|