astro 4.13.4 → 4.14.1

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.
Files changed (98) hide show
  1. package/components/Code.astro +9 -0
  2. package/dist/@types/astro.d.ts +249 -1
  3. package/dist/actions/consts.d.ts +1 -1
  4. package/dist/actions/consts.js +1 -1
  5. package/dist/actions/index.js +12 -21
  6. package/dist/assets/endpoint/node.js +1 -1
  7. package/dist/assets/utils/resolveImports.d.ts +9 -0
  8. package/dist/assets/utils/resolveImports.js +22 -0
  9. package/dist/cli/add/index.d.ts +2 -2
  10. package/dist/cli/add/index.js +2 -2
  11. package/dist/cli/build/index.d.ts +2 -2
  12. package/dist/cli/build/index.js +5 -1
  13. package/dist/cli/check/index.d.ts +2 -2
  14. package/dist/cli/check/index.js +5 -2
  15. package/dist/cli/db/index.d.ts +4 -3
  16. package/dist/cli/db/index.js +10 -3
  17. package/dist/cli/dev/index.d.ts +2 -2
  18. package/dist/cli/dev/index.js +1 -0
  19. package/dist/cli/docs/index.d.ts +2 -2
  20. package/dist/cli/flags.d.ts +3 -1
  21. package/dist/cli/flags.js +2 -1
  22. package/dist/cli/index.d.ts +1 -1
  23. package/dist/cli/index.js +26 -13
  24. package/dist/cli/info/index.d.ts +2 -2
  25. package/dist/cli/preferences/index.d.ts +2 -2
  26. package/dist/cli/preferences/index.js +1 -1
  27. package/dist/cli/preview/index.d.ts +2 -2
  28. package/dist/cli/sync/index.d.ts +2 -2
  29. package/dist/cli/sync/index.js +5 -2
  30. package/dist/cli/telemetry/index.d.ts +2 -2
  31. package/dist/content/consts.d.ts +16 -2
  32. package/dist/content/consts.js +32 -2
  33. package/dist/content/content-layer.d.ts +40 -0
  34. package/dist/content/content-layer.js +253 -0
  35. package/dist/content/data-store.d.ts +54 -0
  36. package/dist/content/data-store.js +72 -0
  37. package/dist/content/loaders/file.d.ts +7 -0
  38. package/dist/content/loaders/file.js +72 -0
  39. package/dist/content/loaders/glob.d.ts +25 -0
  40. package/dist/content/loaders/glob.js +218 -0
  41. package/dist/content/loaders/index.d.ts +3 -0
  42. package/dist/content/loaders/index.js +7 -0
  43. package/dist/content/loaders/types.d.ts +36 -0
  44. package/dist/content/loaders/types.js +0 -0
  45. package/dist/content/mutable-data-store.d.ts +77 -0
  46. package/dist/content/mutable-data-store.js +269 -0
  47. package/dist/content/runtime.d.ts +46 -8
  48. package/dist/content/runtime.js +225 -31
  49. package/dist/content/types-generator.js +123 -35
  50. package/dist/content/utils.d.ts +307 -2
  51. package/dist/content/utils.js +101 -7
  52. package/dist/content/vite-plugin-content-assets.js +9 -1
  53. package/dist/content/vite-plugin-content-virtual-mod.js +94 -2
  54. package/dist/core/build/index.js +14 -7
  55. package/dist/core/build/plugins/plugin-ssr.js +32 -4
  56. package/dist/core/config/config.d.ts +2 -5
  57. package/dist/core/config/config.js +0 -12
  58. package/dist/core/config/index.d.ts +1 -1
  59. package/dist/core/config/index.js +0 -2
  60. package/dist/core/config/schema.d.ts +34 -0
  61. package/dist/core/config/schema.js +6 -2
  62. package/dist/core/config/settings.js +5 -3
  63. package/dist/core/constants.js +1 -1
  64. package/dist/core/create-vite.js +1 -1
  65. package/dist/core/dev/container.js +2 -1
  66. package/dist/core/dev/dev.js +33 -3
  67. package/dist/core/dev/restart.js +25 -10
  68. package/dist/core/errors/errors-data.d.ts +21 -0
  69. package/dist/core/errors/errors-data.js +13 -0
  70. package/dist/core/index.js +1 -1
  71. package/dist/core/logger/vite.js +1 -1
  72. package/dist/core/messages.js +2 -2
  73. package/dist/core/preview/static-preview-server.js +1 -1
  74. package/dist/core/routing/manifest/create.js +1 -1
  75. package/dist/core/sync/constants.d.ts +1 -0
  76. package/dist/core/sync/constants.js +4 -0
  77. package/dist/core/sync/index.d.ts +12 -4
  78. package/dist/core/sync/index.js +54 -24
  79. package/dist/core/sync/write-files.d.ts +4 -0
  80. package/dist/core/sync/write-files.js +69 -0
  81. package/dist/core/util.js +1 -1
  82. package/dist/env/sync.js +6 -4
  83. package/dist/integrations/hooks.d.ts +7 -1
  84. package/dist/integrations/hooks.js +54 -0
  85. package/dist/preferences/index.d.ts +1 -1
  86. package/dist/preferences/index.js +2 -2
  87. package/dist/runtime/server/render/server-islands.js +6 -4
  88. package/dist/vite-plugin-astro-server/response.js +1 -1
  89. package/dist/vite-plugin-env/index.d.ts +3 -1
  90. package/dist/vite-plugin-env/index.js +11 -1
  91. package/dist/vite-plugin-markdown/content-entry-type.js +25 -2
  92. package/dist/vite-plugin-scanner/index.js +15 -5
  93. package/package.json +10 -5
  94. package/templates/content/module.mjs +6 -1
  95. package/templates/content/types.d.ts +18 -5
  96. package/types/content.d.ts +34 -1
  97. package/dist/core/sync/setup-env-ts.d.ts +0 -8
  98. package/dist/core/sync/setup-env-ts.js +0 -79
@@ -1,6 +1,8 @@
1
+ import { Traverse } from "neotraverse/modern";
1
2
  import pLimit from "p-limit";
2
- import { ZodIssueCode, string as zodString } from "zod";
3
- import { AstroError, AstroErrorData } from "../core/errors/index.js";
3
+ import { ZodIssueCode, z } from "zod";
4
+ import { imageSrcToImportId } from "../assets/utils/resolveImports.js";
5
+ import { AstroError, AstroErrorData, AstroUserError } from "../core/errors/index.js";
4
6
  import { prependForwardSlash } from "../core/path.js";
5
7
  import {
6
8
  createComponent,
@@ -9,9 +11,21 @@ import {
9
11
  renderScriptElement,
10
12
  renderTemplate,
11
13
  renderUniqueStylesheet,
14
+ render as serverRender,
12
15
  unescapeHTML
13
16
  } from "../runtime/server/index.js";
17
+ import { CONTENT_LAYER_TYPE, IMAGE_IMPORT_PREFIX } from "./consts.js";
18
+ import { globalDataStore } from "./data-store.js";
14
19
  function defineCollection(config) {
20
+ if ("loader" in config) {
21
+ if (config.type && config.type !== CONTENT_LAYER_TYPE) {
22
+ throw new AstroUserError(
23
+ "Collections that use the Content Layer API must have a `loader` defined and no `type` set.",
24
+ "Check your collection definitions in `src/content/config.*`.'"
25
+ );
26
+ }
27
+ config.type = CONTENT_LAYER_TYPE;
28
+ }
15
29
  if (!config.type) config.type = "content";
16
30
  return config;
17
31
  }
@@ -37,11 +51,29 @@ function createGetCollection({
37
51
  cacheEntriesByCollection
38
52
  }) {
39
53
  return async function getCollection(collection, filter) {
54
+ const hasFilter = typeof filter === "function";
55
+ const store = await globalDataStore.get();
40
56
  let type;
41
57
  if (collection in contentCollectionToEntryMap) {
42
58
  type = "content";
43
59
  } else if (collection in dataCollectionToEntryMap) {
44
60
  type = "data";
61
+ } else if (store.hasCollection(collection)) {
62
+ const { default: imageAssetMap } = await import("astro:asset-imports");
63
+ const result = [];
64
+ for (const rawEntry of store.values(collection)) {
65
+ const data = rawEntry.filePath ? updateImageReferencesInData(rawEntry.data, rawEntry.filePath, imageAssetMap) : rawEntry.data;
66
+ const entry = {
67
+ ...rawEntry,
68
+ data,
69
+ collection
70
+ };
71
+ if (hasFilter && !filter(entry)) {
72
+ continue;
73
+ }
74
+ result.push(entry);
75
+ }
76
+ return result;
45
77
  } else {
46
78
  console.warn(
47
79
  `The collection ${JSON.stringify(
@@ -85,7 +117,7 @@ function createGetCollection({
85
117
  );
86
118
  cacheEntriesByCollection.set(collection, entries);
87
119
  }
88
- if (typeof filter === "function") {
120
+ if (hasFilter) {
89
121
  return entries.filter(filter);
90
122
  } else {
91
123
  return entries.slice();
@@ -94,9 +126,21 @@ function createGetCollection({
94
126
  }
95
127
  function createGetEntryBySlug({
96
128
  getEntryImport,
97
- getRenderEntryImport
129
+ getRenderEntryImport,
130
+ collectionNames
98
131
  }) {
99
132
  return async function getEntryBySlug(collection, slug) {
133
+ const store = await globalDataStore.get();
134
+ if (!collectionNames.has(collection)) {
135
+ if (store.hasCollection(collection)) {
136
+ throw new AstroError({
137
+ ...AstroErrorData.GetEntryDeprecationError,
138
+ message: AstroErrorData.GetEntryDeprecationError.message(collection, "getEntryBySlug")
139
+ });
140
+ }
141
+ console.warn(`The collection ${JSON.stringify(collection)} does not exist.`);
142
+ return void 0;
143
+ }
100
144
  const entryImport = await getEntryImport(collection, slug);
101
145
  if (typeof entryImport !== "function") return void 0;
102
146
  const entry = await entryImport();
@@ -116,8 +160,22 @@ function createGetEntryBySlug({
116
160
  };
117
161
  };
118
162
  }
119
- function createGetDataEntryById({ getEntryImport }) {
163
+ function createGetDataEntryById({
164
+ getEntryImport,
165
+ collectionNames
166
+ }) {
120
167
  return async function getDataEntryById(collection, id) {
168
+ const store = await globalDataStore.get();
169
+ if (!collectionNames.has(collection)) {
170
+ if (store.hasCollection(collection)) {
171
+ throw new AstroError({
172
+ ...AstroErrorData.GetEntryDeprecationError,
173
+ message: AstroErrorData.GetEntryDeprecationError.message(collection, "getDataEntryById")
174
+ });
175
+ }
176
+ console.warn(`The collection ${JSON.stringify(collection)} does not exist.`);
177
+ return void 0;
178
+ }
121
179
  const lazyImport = await getEntryImport(collection, id);
122
180
  if (!lazyImport) throw new Error(`Entry ${collection} \u2192 ${id} was not found.`);
123
181
  const entry = await lazyImport();
@@ -130,7 +188,8 @@ function createGetDataEntryById({ getEntryImport }) {
130
188
  }
131
189
  function createGetEntry({
132
190
  getEntryImport,
133
- getRenderEntryImport
191
+ getRenderEntryImport,
192
+ collectionNames
134
193
  }) {
135
194
  return async function getEntry(collectionOrLookupObject, _lookupId) {
136
195
  let collection, lookupId;
@@ -146,6 +205,26 @@ function createGetEntry({
146
205
  collection = collectionOrLookupObject.collection;
147
206
  lookupId = "id" in collectionOrLookupObject ? collectionOrLookupObject.id : collectionOrLookupObject.slug;
148
207
  }
208
+ const store = await globalDataStore.get();
209
+ if (store.hasCollection(collection)) {
210
+ const entry2 = store.get(collection, lookupId);
211
+ if (!entry2) {
212
+ console.warn(`Entry ${collection} \u2192 ${lookupId} was not found.`);
213
+ return;
214
+ }
215
+ if (entry2.filePath) {
216
+ const { default: imageAssetMap } = await import("astro:asset-imports");
217
+ entry2.data = updateImageReferencesInData(entry2.data, entry2.filePath, imageAssetMap);
218
+ }
219
+ return {
220
+ ...entry2,
221
+ collection
222
+ };
223
+ }
224
+ if (!collectionNames.has(collection)) {
225
+ console.warn(`The collection ${JSON.stringify(collection)} does not exist.`);
226
+ return void 0;
227
+ }
149
228
  const entryImport = await getEntryImport(collection, lookupId);
150
229
  if (typeof entryImport !== "function") return void 0;
151
230
  const entry = await entryImport();
@@ -179,6 +258,82 @@ function createGetEntries(getEntry) {
179
258
  return Promise.all(entries.map((e) => getEntry(e)));
180
259
  };
181
260
  }
261
+ const CONTENT_LAYER_IMAGE_REGEX = /__ASTRO_IMAGE_="([^"]+)"/g;
262
+ async function updateImageReferencesInBody(html, fileName) {
263
+ const { default: imageAssetMap } = await import("astro:asset-imports");
264
+ const imageObjects = /* @__PURE__ */ new Map();
265
+ const { getImage } = await import("astro:assets");
266
+ for (const [_full, imagePath] of html.matchAll(CONTENT_LAYER_IMAGE_REGEX)) {
267
+ try {
268
+ const decodedImagePath = JSON.parse(imagePath.replaceAll(""", '"'));
269
+ const id = imageSrcToImportId(decodedImagePath.src, fileName);
270
+ const imported = imageAssetMap.get(id);
271
+ if (!id || imageObjects.has(id) || !imported) {
272
+ continue;
273
+ }
274
+ const image = await getImage({ ...decodedImagePath, src: imported });
275
+ imageObjects.set(imagePath, image);
276
+ } catch {
277
+ throw new Error(`Failed to parse image reference: ${imagePath}`);
278
+ }
279
+ }
280
+ return html.replaceAll(CONTENT_LAYER_IMAGE_REGEX, (full, imagePath) => {
281
+ const image = imageObjects.get(imagePath);
282
+ if (!image) {
283
+ return full;
284
+ }
285
+ const { index, ...attributes } = image.attributes;
286
+ return Object.entries({
287
+ ...attributes,
288
+ src: image.src,
289
+ srcset: image.srcSet.attribute
290
+ }).map(([key, value]) => value ? `${key}=${JSON.stringify(String(value))}` : "").join(" ");
291
+ });
292
+ }
293
+ function updateImageReferencesInData(data, fileName, imageAssetMap) {
294
+ return new Traverse(data).map(function(ctx, val) {
295
+ if (typeof val === "string" && val.startsWith(IMAGE_IMPORT_PREFIX)) {
296
+ const src = val.replace(IMAGE_IMPORT_PREFIX, "");
297
+ const id = imageSrcToImportId(src, fileName);
298
+ if (!id) {
299
+ ctx.update(src);
300
+ return;
301
+ }
302
+ const imported = imageAssetMap.get(id);
303
+ if (imported) {
304
+ ctx.update(imported);
305
+ } else {
306
+ ctx.update(src);
307
+ }
308
+ }
309
+ });
310
+ }
311
+ async function renderEntry(entry) {
312
+ if (entry && "render" in entry) {
313
+ return entry.render();
314
+ }
315
+ if (entry.deferredRender) {
316
+ try {
317
+ const { default: contentModules } = await import("astro:content-module-imports");
318
+ const module = contentModules.get(entry.filePath);
319
+ const deferredMod = await module();
320
+ return {
321
+ Content: deferredMod.Content,
322
+ headings: deferredMod.getHeadings?.() ?? [],
323
+ remarkPluginFrontmatter: deferredMod.frontmatter ?? {}
324
+ };
325
+ } catch (e) {
326
+ console.error(e);
327
+ }
328
+ }
329
+ const html = entry?.rendered?.metadata?.imagePaths?.length && entry.filePath ? await updateImageReferencesInBody(entry.rendered.html, entry.filePath) : entry?.rendered?.html;
330
+ const Content = createComponent(() => serverRender`${unescapeHTML(html)}`);
331
+ return {
332
+ Content,
333
+ headings: entry?.rendered?.metadata?.headings ?? [],
334
+ remarkPluginFrontmatter: entry?.rendered?.metadata?.frontmatter ?? {}
335
+ };
336
+ }
182
337
  async function render({
183
338
  collection,
184
339
  id,
@@ -256,31 +411,69 @@ async function render({
256
411
  }
257
412
  function createReference({ lookupMap }) {
258
413
  return function reference(collection) {
259
- return zodString().transform((lookupId, ctx) => {
260
- const flattenedErrorPath = ctx.path.join(".");
261
- if (!lookupMap[collection]) {
262
- ctx.addIssue({
263
- code: ZodIssueCode.custom,
264
- message: `**${flattenedErrorPath}:** Reference to ${collection} invalid. Collection does not exist or is empty.`
265
- });
266
- return;
267
- }
268
- const { type, entries } = lookupMap[collection];
269
- const entry = entries[lookupId];
270
- if (!entry) {
271
- ctx.addIssue({
272
- code: ZodIssueCode.custom,
273
- message: `**${flattenedErrorPath}**: Reference to ${collection} invalid. Expected ${Object.keys(
274
- entries
275
- ).map((c) => JSON.stringify(c)).join(" | ")}. Received ${JSON.stringify(lookupId)}.`
276
- });
277
- return;
278
- }
279
- if (type === "content") {
280
- return { slug: lookupId, collection };
414
+ return z.union([
415
+ z.string(),
416
+ z.object({
417
+ id: z.string(),
418
+ collection: z.string()
419
+ }),
420
+ z.object({
421
+ slug: z.string(),
422
+ collection: z.string()
423
+ })
424
+ ]).transform(
425
+ async (lookup, ctx) => {
426
+ const flattenedErrorPath = ctx.path.join(".");
427
+ const store = await globalDataStore.get();
428
+ const collectionIsInStore = store.hasCollection(collection);
429
+ if (typeof lookup === "object") {
430
+ if (lookup.collection !== collection) {
431
+ ctx.addIssue({
432
+ code: ZodIssueCode.custom,
433
+ message: `**${flattenedErrorPath}**: Reference to ${collection} invalid. Expected ${collection}. Received ${lookup.collection}.`
434
+ });
435
+ return;
436
+ }
437
+ if (!lookupMap[collection] && !collectionIsInStore) {
438
+ ctx.addIssue({
439
+ code: ZodIssueCode.custom,
440
+ message: `**${flattenedErrorPath}:** Reference to ${collection} invalid. Collection does not exist or is empty.`
441
+ });
442
+ return;
443
+ }
444
+ return lookup;
445
+ }
446
+ if (collectionIsInStore) {
447
+ const entry2 = store.get(collection, lookup);
448
+ if (!entry2) {
449
+ ctx.addIssue({
450
+ code: ZodIssueCode.custom,
451
+ message: `**${flattenedErrorPath}**: Reference to ${collection} invalid. Entry ${lookup} does not exist.`
452
+ });
453
+ return;
454
+ }
455
+ return { id: lookup, collection };
456
+ }
457
+ if (!lookupMap[collection] && store.collections().size === 0) {
458
+ return { id: lookup, collection };
459
+ }
460
+ const { type, entries } = lookupMap[collection];
461
+ const entry = entries[lookup];
462
+ if (!entry) {
463
+ ctx.addIssue({
464
+ code: ZodIssueCode.custom,
465
+ message: `**${flattenedErrorPath}**: Reference to ${collection} invalid. Expected ${Object.keys(
466
+ entries
467
+ ).map((c) => JSON.stringify(c)).join(" | ")}. Received ${JSON.stringify(lookup)}.`
468
+ });
469
+ return;
470
+ }
471
+ if (type === "content") {
472
+ return { slug: lookup, collection };
473
+ }
474
+ return { id: lookup, collection };
281
475
  }
282
- return { id: lookupId, collection };
283
- });
476
+ );
284
477
  };
285
478
  }
286
479
  function isPropagatedAssetsModule(module) {
@@ -294,5 +487,6 @@ export {
294
487
  createGetEntry,
295
488
  createGetEntryBySlug,
296
489
  createReference,
297
- defineCollection
490
+ defineCollection,
491
+ renderEntry
298
492
  };
@@ -5,10 +5,11 @@ import { bold, cyan } from "kleur/colors";
5
5
  import { normalizePath } from "vite";
6
6
  import { z } from "zod";
7
7
  import { zodToJsonSchema } from "zod-to-json-schema";
8
+ import { printNode, zodToTs } from "zod-to-ts";
8
9
  import { AstroError } from "../core/errors/errors.js";
9
10
  import { AstroErrorData } from "../core/errors/index.js";
10
11
  import { isRelativePath } from "../core/path.js";
11
- import { CONTENT_TYPES_FILE, VIRTUAL_MODULE_ID } from "./consts.js";
12
+ import { CONTENT_LAYER_TYPE, CONTENT_TYPES_FILE, VIRTUAL_MODULE_ID } from "./consts.js";
12
13
  import {
13
14
  getContentEntryIdAndSlug,
14
15
  getContentPaths,
@@ -269,6 +270,36 @@ function normalizeConfigPath(from, to) {
269
270
  const normalizedPath = configPath.replaceAll("\\", "/");
270
271
  return `"${isRelativePath(configPath) ? "" : "./"}${normalizedPath}"`;
271
272
  }
273
+ const schemaCache = /* @__PURE__ */ new Map();
274
+ async function getContentLayerSchema(collection, collectionKey) {
275
+ const cached = schemaCache.get(collectionKey);
276
+ if (cached) {
277
+ return cached;
278
+ }
279
+ if (collection?.type === CONTENT_LAYER_TYPE && typeof collection.loader === "object" && collection.loader.schema) {
280
+ let schema = collection.loader.schema;
281
+ if (typeof schema === "function") {
282
+ schema = await schema();
283
+ }
284
+ if (schema) {
285
+ schemaCache.set(collectionKey, await schema);
286
+ return schema;
287
+ }
288
+ }
289
+ }
290
+ async function typeForCollection(collection, collectionKey) {
291
+ if (collection?.schema) {
292
+ return `InferEntrySchema<${collectionKey}>`;
293
+ }
294
+ if (collection?.type === CONTENT_LAYER_TYPE) {
295
+ const schema = await getContentLayerSchema(collection, collectionKey);
296
+ if (schema) {
297
+ const ast = zodToTs(schema);
298
+ return printNode(ast.node);
299
+ }
300
+ }
301
+ return "any";
302
+ }
272
303
  async function writeContentFiles({
273
304
  fs,
274
305
  contentPaths,
@@ -292,10 +323,11 @@ async function writeContentFiles({
292
323
  entries: {}
293
324
  };
294
325
  }
326
+ let contentCollectionsMap = {};
295
327
  for (const collectionKey of Object.keys(collectionEntryMap).sort()) {
296
328
  const collectionConfig = contentConfig?.collections[JSON.parse(collectionKey)];
297
329
  const collection = collectionEntryMap[collectionKey];
298
- if (collectionConfig?.type && collection.type !== "unknown" && collection.type !== collectionConfig.type) {
330
+ if (collectionConfig?.type && collection.type !== "unknown" && collectionConfig.type !== CONTENT_LAYER_TYPE && collection.type !== collectionConfig.type) {
299
331
  viteServer.hot.send({
300
332
  type: "error",
301
333
  err: new AstroError({
@@ -319,7 +351,7 @@ async function writeContentFiles({
319
351
  collectionConfig?.type ?? "data"
320
352
  ) : collection.type;
321
353
  const collectionEntryKeys = Object.keys(collection.entries).sort();
322
- const dataType = collectionConfig?.schema ? `InferEntrySchema<${collectionKey}>` : "any";
354
+ const dataType = await typeForCollection(collectionConfig, collectionKey);
323
355
  switch (resolvedType) {
324
356
  case "content":
325
357
  if (collectionEntryKeys.length === 0) {
@@ -352,6 +384,15 @@ async function writeContentFiles({
352
384
  `;
353
385
  }
354
386
  contentTypesStr += `};
387
+ `;
388
+ break;
389
+ case CONTENT_LAYER_TYPE:
390
+ dataTypesStr += `${collectionKey}: Record<string, {
391
+ id: string;
392
+ collection: ${collectionKey};
393
+ data: ${dataType};
394
+ rendered?: RenderedContent
395
+ }>;
355
396
  `;
356
397
  break;
357
398
  case "data":
@@ -377,42 +418,51 @@ async function writeContentFiles({
377
418
  `;
378
419
  }
379
420
  if (collectionConfig?.schema) {
380
- let zodSchemaForJson = typeof collectionConfig.schema === "function" ? collectionConfig.schema({ image: () => z.string() }) : collectionConfig.schema;
381
- if (zodSchemaForJson instanceof z.ZodObject) {
382
- zodSchemaForJson = zodSchemaForJson.extend({
383
- $schema: z.string().optional()
384
- });
385
- }
386
- try {
387
- await fs.promises.writeFile(
388
- new URL(`./${collectionKey.replace(/"/g, "")}.schema.json`, collectionSchemasDir),
389
- JSON.stringify(
390
- zodToJsonSchema(zodSchemaForJson, {
391
- name: collectionKey.replace(/"/g, ""),
392
- markdownDescription: true,
393
- errorMessages: true,
394
- // Fix for https://github.com/StefanTerdell/zod-to-json-schema/issues/110
395
- dateStrategy: ["format:date-time", "format:date", "integer"]
396
- }),
397
- null,
398
- 2
399
- )
400
- );
401
- } catch (err) {
402
- logger.warn(
403
- "content",
404
- `An error was encountered while creating the JSON schema for the ${collectionKey} collection. Proceeding without it. Error: ${err}`
405
- );
406
- }
421
+ await generateJSONSchema(
422
+ fs,
423
+ collectionConfig,
424
+ collectionKey,
425
+ collectionSchemasDir,
426
+ logger
427
+ );
407
428
  }
408
429
  break;
409
430
  }
431
+ if (settings.config.experimental.contentIntellisense && collectionConfig && (collectionConfig.schema || await getContentLayerSchema(collectionConfig, collectionKey))) {
432
+ await generateJSONSchema(fs, collectionConfig, collectionKey, collectionSchemasDir, logger);
433
+ contentCollectionsMap[collectionKey] = collection;
434
+ }
435
+ }
436
+ if (settings.config.experimental.contentIntellisense) {
437
+ let contentCollectionManifest = {
438
+ collections: [],
439
+ entries: {}
440
+ };
441
+ Object.entries(contentCollectionsMap).forEach(([collectionKey, collection]) => {
442
+ const collectionConfig = contentConfig?.collections[JSON.parse(collectionKey)];
443
+ const key = JSON.parse(collectionKey);
444
+ contentCollectionManifest.collections.push({
445
+ hasSchema: Boolean(collectionConfig?.schema || schemaCache.has(collectionKey)),
446
+ name: key
447
+ });
448
+ Object.keys(collection.entries).forEach((entryKey) => {
449
+ const entryPath = new URL(
450
+ JSON.parse(entryKey),
451
+ contentPaths.contentDir + `${key}/`
452
+ ).toString();
453
+ contentCollectionManifest.entries[entryPath.toLowerCase()] = key;
454
+ });
455
+ });
456
+ await fs.promises.writeFile(
457
+ new URL("./collections.json", collectionSchemasDir),
458
+ JSON.stringify(contentCollectionManifest, null, 2)
459
+ );
410
460
  }
411
461
  if (!fs.existsSync(settings.dotAstroDir)) {
412
462
  fs.mkdirSync(settings.dotAstroDir, { recursive: true });
413
463
  }
414
464
  const configPathRelativeToCacheDir = normalizeConfigPath(
415
- settings.dotAstroDir.pathname,
465
+ new URL("astro", settings.dotAstroDir).pathname,
416
466
  contentPaths.config.url.pathname
417
467
  );
418
468
  for (const contentEntryType of contentEntryTypes) {
@@ -426,10 +476,48 @@ async function writeContentFiles({
426
476
  "'@@CONTENT_CONFIG_TYPE@@'",
427
477
  contentConfig ? `typeof import(${configPathRelativeToCacheDir})` : "never"
428
478
  );
429
- await fs.promises.writeFile(
430
- new URL(CONTENT_TYPES_FILE, settings.dotAstroDir),
431
- typeTemplateContent
432
- );
479
+ if (settings.injectedTypes.some((t) => t.filename === CONTENT_TYPES_FILE)) {
480
+ const filePath = fileURLToPath(new URL(CONTENT_TYPES_FILE, settings.dotAstroDir));
481
+ await fs.promises.mkdir(path.dirname(filePath), { recursive: true });
482
+ await fs.promises.writeFile(filePath, typeTemplateContent, "utf-8");
483
+ } else {
484
+ settings.injectedTypes.push({
485
+ filename: CONTENT_TYPES_FILE,
486
+ content: typeTemplateContent
487
+ });
488
+ }
489
+ }
490
+ async function generateJSONSchema(fsMod, collectionConfig, collectionKey, collectionSchemasDir, logger) {
491
+ let zodSchemaForJson = typeof collectionConfig.schema === "function" ? collectionConfig.schema({ image: () => z.string() }) : collectionConfig.schema;
492
+ if (!zodSchemaForJson && collectionConfig.type === CONTENT_LAYER_TYPE) {
493
+ zodSchemaForJson = await getContentLayerSchema(collectionConfig, collectionKey);
494
+ }
495
+ if (zodSchemaForJson instanceof z.ZodObject) {
496
+ zodSchemaForJson = zodSchemaForJson.extend({
497
+ $schema: z.string().optional()
498
+ });
499
+ }
500
+ try {
501
+ await fsMod.promises.writeFile(
502
+ new URL(`./${collectionKey.replace(/"/g, "")}.schema.json`, collectionSchemasDir),
503
+ JSON.stringify(
504
+ zodToJsonSchema(zodSchemaForJson, {
505
+ name: collectionKey.replace(/"/g, ""),
506
+ markdownDescription: true,
507
+ errorMessages: true,
508
+ // Fix for https://github.com/StefanTerdell/zod-to-json-schema/issues/110
509
+ dateStrategy: ["format:date-time", "format:date", "integer"]
510
+ }),
511
+ null,
512
+ 2
513
+ )
514
+ );
515
+ } catch (err) {
516
+ logger.warn(
517
+ "content",
518
+ `An error was encountered while creating the JSON schema for the ${collectionKey} collection. Proceeding without it. Error: ${err}`
519
+ );
520
+ }
433
521
  }
434
522
  export {
435
523
  createContentTypesGenerator