astro 1.6.14 → 1.7.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.
Files changed (103) hide show
  1. package/dist/@types/astro.d.ts +59 -0
  2. package/dist/content/consts.d.ts +9 -0
  3. package/dist/content/consts.js +20 -0
  4. package/dist/content/index.d.ts +3 -0
  5. package/dist/content/index.js +12 -0
  6. package/dist/content/internal.d.ts +37 -0
  7. package/dist/content/internal.js +131 -0
  8. package/dist/content/template/types.generated.d.js +0 -0
  9. package/dist/content/template/types.generated.d.mts +3 -0
  10. package/dist/content/types-generator.d.ts +32 -0
  11. package/dist/content/types-generator.js +290 -0
  12. package/dist/content/utils.d.ts +166 -0
  13. package/dist/content/utils.js +175 -0
  14. package/dist/content/vite-plugin-content-assets.d.ts +8 -0
  15. package/dist/content/vite-plugin-content-assets.js +92 -0
  16. package/dist/content/vite-plugin-content-server.d.ts +13 -0
  17. package/dist/content/vite-plugin-content-server.js +174 -0
  18. package/dist/content/vite-plugin-content-virtual-mod.d.ts +7 -0
  19. package/dist/content/vite-plugin-content-virtual-mod.js +38 -0
  20. package/dist/core/app/index.js +4 -1
  21. package/dist/core/build/common.js +5 -1
  22. package/dist/core/build/generate.d.ts +1 -1
  23. package/dist/core/build/generate.js +23 -7
  24. package/dist/core/build/graph.d.ts +1 -1
  25. package/dist/core/build/graph.js +4 -2
  26. package/dist/core/build/internal.d.ts +12 -0
  27. package/dist/core/build/internal.js +30 -0
  28. package/dist/core/build/page-data.js +2 -0
  29. package/dist/core/build/static-build.js +110 -22
  30. package/dist/core/build/types.d.ts +3 -1
  31. package/dist/core/build/vite-plugin-analyzer.js +7 -3
  32. package/dist/core/build/vite-plugin-css.d.ts +1 -1
  33. package/dist/core/build/vite-plugin-css.js +50 -2
  34. package/dist/core/build/vite-plugin-internals.js +7 -0
  35. package/dist/core/build/vite-plugin-pages.d.ts +1 -1
  36. package/dist/core/build/vite-plugin-pages.js +2 -2
  37. package/dist/core/build/vite-plugin-prerender.d.ts +4 -0
  38. package/dist/core/build/vite-plugin-prerender.js +36 -0
  39. package/dist/core/build/vite-plugin-ssr.js +31 -3
  40. package/dist/core/compile/style.js +6 -3
  41. package/dist/core/config/config.js +11 -1
  42. package/dist/core/config/schema.d.ts +142 -69
  43. package/dist/core/config/schema.js +13 -1
  44. package/dist/core/constants.js +1 -1
  45. package/dist/core/cookies/cookies.d.ts +1 -3
  46. package/dist/core/cookies/cookies.js +3 -0
  47. package/dist/core/create-vite.js +13 -1
  48. package/dist/core/dev/container.js +2 -2
  49. package/dist/core/dev/dev.js +1 -1
  50. package/dist/core/errors/dev/utils.d.ts +4 -0
  51. package/dist/core/errors/dev/utils.js +38 -12
  52. package/dist/core/errors/dev/vite.d.ts +16 -1
  53. package/dist/core/errors/dev/vite.js +35 -12
  54. package/dist/core/errors/errors-data.d.ts +31 -0
  55. package/dist/core/errors/errors-data.js +27 -1
  56. package/dist/core/errors/errors.d.ts +3 -1
  57. package/dist/core/errors/errors.js +1 -3
  58. package/dist/core/errors/overlay.d.ts +2 -0
  59. package/dist/core/errors/overlay.js +540 -0
  60. package/dist/core/errors/utils.d.ts +10 -0
  61. package/dist/core/messages.js +14 -5
  62. package/dist/core/preview/index.js +2 -2
  63. package/dist/core/preview/static-preview-server.d.ts +3 -2
  64. package/dist/core/preview/static-preview-server.js +13 -2
  65. package/dist/core/render/dev/vite.js +3 -0
  66. package/dist/core/render/result.js +2 -1
  67. package/dist/core/routing/match.d.ts +2 -0
  68. package/dist/core/routing/match.js +13 -0
  69. package/dist/core/util.d.ts +1 -0
  70. package/dist/core/util.js +8 -0
  71. package/dist/integrations/index.js +2 -0
  72. package/dist/jsx/babel.js +2 -1
  73. package/dist/runtime/server/astro-component.d.ts +8 -1
  74. package/dist/runtime/server/index.d.ts +2 -2
  75. package/dist/runtime/server/index.js +2 -0
  76. package/dist/runtime/server/render/astro/factory.d.ts +1 -1
  77. package/dist/runtime/server/render/astro/index.d.ts +1 -1
  78. package/dist/runtime/server/render/astro/instance.d.ts +6 -2
  79. package/dist/runtime/server/render/astro/instance.js +5 -2
  80. package/dist/runtime/server/render/component.js +9 -2
  81. package/dist/runtime/server/render/index.d.ts +2 -2
  82. package/dist/runtime/server/render/index.js +2 -1
  83. package/dist/runtime/server/render/{stylesheet.d.ts → tags.d.ts} +3 -2
  84. package/dist/runtime/server/render/{stylesheet.js → tags.js} +16 -12
  85. package/dist/vite-plugin-astro/index.js +2 -1
  86. package/dist/vite-plugin-astro/types.d.ts +4 -0
  87. package/dist/vite-plugin-astro-server/plugin.js +2 -7
  88. package/dist/vite-plugin-astro-server/response.js +4 -1
  89. package/dist/vite-plugin-astro-server/route.js +4 -0
  90. package/dist/vite-plugin-markdown/index.js +10 -5
  91. package/dist/vite-plugin-markdown-legacy/index.js +2 -1
  92. package/dist/vite-plugin-scanner/index.d.ts +7 -0
  93. package/dist/vite-plugin-scanner/index.js +42 -0
  94. package/dist/vite-plugin-scanner/scan.d.ts +2 -0
  95. package/dist/vite-plugin-scanner/scan.js +41 -0
  96. package/dist/vite-plugin-scripts/page-ssr.js +1 -1
  97. package/dist/vite-plugin-utils/index.d.ts +7 -1
  98. package/dist/vite-plugin-utils/index.js +4 -0
  99. package/package.json +15 -6
  100. package/src/content/template/types.generated.d.ts +49 -0
  101. package/src/content/template/types.generated.mjs +40 -0
  102. package/zod.d.ts +5 -0
  103. package/zod.mjs +3 -0
@@ -60,6 +60,9 @@ export interface CLIFlags {
60
60
  port?: number;
61
61
  config?: string;
62
62
  drafts?: boolean;
63
+ experimentalErrorOverlay?: boolean;
64
+ experimentalPrerender?: boolean;
65
+ experimentalContentCollections?: boolean;
63
66
  }
64
67
  export interface BuildConfig {
65
68
  /**
@@ -392,6 +395,8 @@ export interface AstroUserConfig {
392
395
  * @description
393
396
  * The base path to deploy to. Astro will build your pages and assets using this path as the root. Currently, this has no effect during development.
394
397
  *
398
+ * You can access this value in your app via `import.meta.env.BASE_URL`.
399
+ *
395
400
  * ```js
396
401
  * {
397
402
  * base: '/docs'
@@ -836,6 +841,60 @@ export interface AstroUserConfig {
836
841
  */
837
842
  astroFlavoredMarkdown?: boolean;
838
843
  };
844
+ /**
845
+ * @docs
846
+ * @kind heading
847
+ * @name Experimental Flags
848
+ * @description
849
+ * Astro offers experimental flags to give users early access to new features.
850
+ * These flags are not guaranteed to be stable.
851
+ */
852
+ experimental?: {
853
+ /**
854
+ * @hidden
855
+ * Turn on experimental support for the new error overlay component.
856
+ */
857
+ errorOverlay?: boolean;
858
+ /**
859
+ * @docs
860
+ * @name experimental.prerender
861
+ * @type {boolean}
862
+ * @default `false`
863
+ * @version 1.7.0
864
+ * @description
865
+ * Enable experimental support for prerendered pages when generating a server.
866
+ *
867
+ * To enable this feature, set `experimental.prerender` to `true` in your Astro config:
868
+ *
869
+ * ```js
870
+ * {
871
+ * experimental: {
872
+ * prerender: true,
873
+ * },
874
+ * }
875
+ * ```
876
+ */
877
+ prerender?: boolean;
878
+ /**
879
+ * @docs
880
+ * @name experimental.contentCollections
881
+ * @type {boolean}
882
+ * @default `false`
883
+ * @version 1.7.0
884
+ * @description
885
+ * Enable experimental support for [Content Collections](/en/guides/content-collections). This makes the `src/content/` directory a reserved directory for Astro to manage, and introduces the `astro:content` module for querying this content.
886
+ *
887
+ * To enable this feature, set `experimental.contentCollections` to `true` in your Astro config:
888
+ *
889
+ * ```js
890
+ * {
891
+ * experimental: {
892
+ * contentCollections: true,
893
+ * },
894
+ * }
895
+ */
896
+ contentCollections?: boolean;
897
+ };
839
898
  /** @deprecated - Use "integrations" instead. Run Astro to learn more about migrating. */
840
899
  renderers?: never;
841
900
  /** @deprecated `projectRoot` has been renamed to `root` */
@@ -0,0 +1,9 @@
1
+ export declare const contentFileExts: string[];
2
+ export declare const DELAYED_ASSET_FLAG = "astroAssetSsr";
3
+ export declare const CONTENT_FLAG = "astroContent";
4
+ export declare const VIRTUAL_MODULE_ID = "astro:content";
5
+ export declare const LINKS_PLACEHOLDER = "@@ASTRO-LINKS@@";
6
+ export declare const STYLES_PLACEHOLDER = "@@ASTRO-STYLES@@";
7
+ export declare const CONTENT_BASE = "types.generated";
8
+ export declare const CONTENT_FILE: string;
9
+ export declare const CONTENT_TYPES_FILE: string;
@@ -0,0 +1,20 @@
1
+ const contentFileExts = [".md", ".mdx"];
2
+ const DELAYED_ASSET_FLAG = "astroAssetSsr";
3
+ const CONTENT_FLAG = "astroContent";
4
+ const VIRTUAL_MODULE_ID = "astro:content";
5
+ const LINKS_PLACEHOLDER = "@@ASTRO-LINKS@@";
6
+ const STYLES_PLACEHOLDER = "@@ASTRO-STYLES@@";
7
+ const CONTENT_BASE = "types.generated";
8
+ const CONTENT_FILE = CONTENT_BASE + ".mjs";
9
+ const CONTENT_TYPES_FILE = CONTENT_BASE + ".d.ts";
10
+ export {
11
+ CONTENT_BASE,
12
+ CONTENT_FILE,
13
+ CONTENT_FLAG,
14
+ CONTENT_TYPES_FILE,
15
+ DELAYED_ASSET_FLAG,
16
+ LINKS_PLACEHOLDER,
17
+ STYLES_PLACEHOLDER,
18
+ VIRTUAL_MODULE_ID,
19
+ contentFileExts
20
+ };
@@ -0,0 +1,3 @@
1
+ export { astroBundleDelayedAssetPlugin, astroDelayedAssetPlugin, } from './vite-plugin-content-assets.js';
2
+ export { astroContentServerPlugin } from './vite-plugin-content-server.js';
3
+ export { astroContentVirtualModPlugin } from './vite-plugin-content-virtual-mod.js';
@@ -0,0 +1,12 @@
1
+ import {
2
+ astroBundleDelayedAssetPlugin,
3
+ astroDelayedAssetPlugin
4
+ } from "./vite-plugin-content-assets.js";
5
+ import { astroContentServerPlugin } from "./vite-plugin-content-server.js";
6
+ import { astroContentVirtualModPlugin } from "./vite-plugin-content-virtual-mod.js";
7
+ export {
8
+ astroBundleDelayedAssetPlugin,
9
+ astroContentServerPlugin,
10
+ astroContentVirtualModPlugin,
11
+ astroDelayedAssetPlugin
12
+ };
@@ -0,0 +1,37 @@
1
+ declare type GlobResult = Record<string, () => Promise<any>>;
2
+ declare type CollectionToEntryMap = Record<string, GlobResult>;
3
+ export declare function createCollectionToGlobResultMap({ globResult, contentDir, }: {
4
+ globResult: GlobResult;
5
+ contentDir: string;
6
+ }): CollectionToEntryMap;
7
+ export declare function createGetCollection({ collectionToEntryMap, collectionToRenderEntryMap, }: {
8
+ collectionToEntryMap: CollectionToEntryMap;
9
+ collectionToRenderEntryMap: CollectionToEntryMap;
10
+ }): (collection: string, filter?: () => boolean) => Promise<{
11
+ id: any;
12
+ slug: any;
13
+ body: any;
14
+ collection: any;
15
+ data: any;
16
+ render(): Promise<{
17
+ Content: import("../runtime/server/index.js").AstroComponentFactory;
18
+ headings: any;
19
+ injectedFrontmatter: any;
20
+ }>;
21
+ }[]>;
22
+ export declare function createGetEntry({ collectionToEntryMap, collectionToRenderEntryMap, }: {
23
+ collectionToEntryMap: CollectionToEntryMap;
24
+ collectionToRenderEntryMap: CollectionToEntryMap;
25
+ }): (collection: string, entryId: string) => Promise<{
26
+ id: any;
27
+ slug: any;
28
+ body: any;
29
+ collection: any;
30
+ data: any;
31
+ render(): Promise<{
32
+ Content: import("../runtime/server/index.js").AstroComponentFactory;
33
+ headings: any;
34
+ injectedFrontmatter: any;
35
+ }>;
36
+ }>;
37
+ export {};
@@ -0,0 +1,131 @@
1
+ import { prependForwardSlash } from "../core/path.js";
2
+ import {
3
+ createComponent,
4
+ createHeadAndContent,
5
+ renderComponent,
6
+ renderStyleElement,
7
+ renderTemplate,
8
+ renderUniqueStylesheet,
9
+ unescapeHTML
10
+ } from "../runtime/server/index.js";
11
+ function createCollectionToGlobResultMap({
12
+ globResult,
13
+ contentDir
14
+ }) {
15
+ const collectionToGlobResultMap = {};
16
+ for (const key in globResult) {
17
+ const keyRelativeToContentDir = key.replace(new RegExp(`^${contentDir}`), "");
18
+ const segments = keyRelativeToContentDir.split("/");
19
+ if (segments.length <= 1)
20
+ continue;
21
+ const collection = segments[0];
22
+ const entryId = segments.slice(1).join("/");
23
+ collectionToGlobResultMap[collection] ?? (collectionToGlobResultMap[collection] = {});
24
+ collectionToGlobResultMap[collection][entryId] = globResult[key];
25
+ }
26
+ return collectionToGlobResultMap;
27
+ }
28
+ function createGetCollection({
29
+ collectionToEntryMap,
30
+ collectionToRenderEntryMap
31
+ }) {
32
+ return async function getCollection(collection, filter) {
33
+ const lazyImports = Object.values(collectionToEntryMap[collection] ?? {});
34
+ const entries = Promise.all(
35
+ lazyImports.map(async (lazyImport) => {
36
+ const entry = await lazyImport();
37
+ return {
38
+ id: entry.id,
39
+ slug: entry.slug,
40
+ body: entry.body,
41
+ collection: entry.collection,
42
+ data: entry.data,
43
+ async render() {
44
+ return render({
45
+ collection: entry.collection,
46
+ id: entry.id,
47
+ collectionToRenderEntryMap
48
+ });
49
+ }
50
+ };
51
+ })
52
+ );
53
+ if (typeof filter === "function") {
54
+ return (await entries).filter(filter);
55
+ } else {
56
+ return entries;
57
+ }
58
+ };
59
+ }
60
+ function createGetEntry({
61
+ collectionToEntryMap,
62
+ collectionToRenderEntryMap
63
+ }) {
64
+ return async function getEntry(collection, entryId) {
65
+ var _a;
66
+ const lazyImport = (_a = collectionToEntryMap[collection]) == null ? void 0 : _a[entryId];
67
+ if (!lazyImport)
68
+ throw new Error(`Failed to import ${JSON.stringify(entryId)}.`);
69
+ const entry = await lazyImport();
70
+ return {
71
+ id: entry.id,
72
+ slug: entry.slug,
73
+ body: entry.body,
74
+ collection: entry.collection,
75
+ data: entry.data,
76
+ async render() {
77
+ return render({
78
+ collection: entry.collection,
79
+ id: entry.id,
80
+ collectionToRenderEntryMap
81
+ });
82
+ }
83
+ };
84
+ };
85
+ }
86
+ async function render({
87
+ collection,
88
+ id,
89
+ collectionToRenderEntryMap
90
+ }) {
91
+ var _a;
92
+ const lazyImport = (_a = collectionToRenderEntryMap[collection]) == null ? void 0 : _a[id];
93
+ if (!lazyImport)
94
+ throw new Error(`${String(collection)} \u2192 ${String(id)} does not exist.`);
95
+ const mod = await lazyImport();
96
+ const Content = createComponent({
97
+ factory(result, props, slots) {
98
+ let styles = "", links = "";
99
+ if (Array.isArray(mod == null ? void 0 : mod.collectedStyles)) {
100
+ styles = mod.collectedStyles.map((style) => renderStyleElement(style)).join("");
101
+ }
102
+ if (Array.isArray(mod == null ? void 0 : mod.collectedLinks)) {
103
+ links = mod.collectedLinks.map((link) => {
104
+ return renderUniqueStylesheet(result, {
105
+ href: prependForwardSlash(link)
106
+ });
107
+ }).join("");
108
+ }
109
+ return createHeadAndContent(
110
+ unescapeHTML(styles + links),
111
+ renderTemplate`${renderComponent(result, "Content", mod.Content, props, slots)}`
112
+ );
113
+ },
114
+ propagation: "self"
115
+ });
116
+ if (!mod._internal && id.endsWith(".mdx")) {
117
+ throw new Error(
118
+ `[Content] Failed to render MDX entry. Try installing @astrojs/mdx@next--content-schemas`
119
+ );
120
+ }
121
+ return {
122
+ Content,
123
+ headings: mod.getHeadings(),
124
+ injectedFrontmatter: mod._internal.injectedFrontmatter
125
+ };
126
+ }
127
+ export {
128
+ createCollectionToGlobResultMap,
129
+ createGetCollection,
130
+ createGetEntry
131
+ };
File without changes
@@ -0,0 +1,3 @@
1
+ export function defineCollection(config: any): any;
2
+ export const getCollection: any;
3
+ export const getEntry: any;
@@ -0,0 +1,32 @@
1
+ /// <reference types="node" />
2
+ import fsMod from 'node:fs';
3
+ import type { AstroSettings } from '../@types/astro.js';
4
+ import { LogOptions } from '../core/logger/core.js';
5
+ import { ContentObservable, ContentPaths } from './utils.js';
6
+ declare type ChokidarEvent = 'add' | 'addDir' | 'change' | 'unlink' | 'unlinkDir';
7
+ declare type RawContentEvent = {
8
+ name: ChokidarEvent;
9
+ entry: string;
10
+ };
11
+ declare type EntryInfo = {
12
+ id: string;
13
+ slug: string;
14
+ collection: string;
15
+ };
16
+ export declare type GenerateContentTypes = {
17
+ init(): Promise<void>;
18
+ queueEvent(event: RawContentEvent): void;
19
+ };
20
+ declare type CreateContentGeneratorParams = {
21
+ contentPaths: ContentPaths;
22
+ contentConfigObserver: ContentObservable;
23
+ logging: LogOptions;
24
+ settings: AstroSettings;
25
+ fs: typeof fsMod;
26
+ };
27
+ export declare function createContentTypesGenerator({ contentPaths, contentConfigObserver, fs, logging, settings, }: CreateContentGeneratorParams): Promise<GenerateContentTypes>;
28
+ export declare function getEntryInfo({ entry, contentDir, }: Pick<ContentPaths, 'contentDir'> & {
29
+ entry: URL;
30
+ }): EntryInfo | Error;
31
+ export declare function getEntryType(entryPath: string, paths: ContentPaths): 'content' | 'config' | 'unknown' | 'generated-types';
32
+ export {};
@@ -0,0 +1,290 @@
1
+ import glob from "fast-glob";
2
+ import { cyan } from "kleur/colors";
3
+ import fsMod from "node:fs";
4
+ import * as path from "node:path";
5
+ import { fileURLToPath, pathToFileURL } from "node:url";
6
+ import { normalizePath } from "vite";
7
+ import { info, warn } from "../core/logger/core.js";
8
+ import { appendForwardSlash, isRelativePath } from "../core/path.js";
9
+ import { contentFileExts, CONTENT_TYPES_FILE } from "./consts.js";
10
+ import { loadContentConfig } from "./utils.js";
11
+ class UnsupportedFileTypeError extends Error {
12
+ }
13
+ async function createContentTypesGenerator({
14
+ contentPaths,
15
+ contentConfigObserver,
16
+ fs,
17
+ logging,
18
+ settings
19
+ }) {
20
+ const contentTypes = {};
21
+ let events = [];
22
+ let debounceTimeout;
23
+ const contentTypesBase = await fsMod.promises.readFile(
24
+ new URL(CONTENT_TYPES_FILE, contentPaths.generatedInputDir),
25
+ "utf-8"
26
+ );
27
+ async function init() {
28
+ await handleEvent({ name: "add", entry: contentPaths.config }, { logLevel: "warn" });
29
+ const globResult = await glob("./**/*.*", {
30
+ cwd: fileURLToPath(contentPaths.contentDir),
31
+ fs: {
32
+ readdir: fs.readdir.bind(fs),
33
+ readdirSync: fs.readdirSync.bind(fs)
34
+ }
35
+ });
36
+ const entries = globResult.map((e) => new URL(e, contentPaths.contentDir)).filter(
37
+ (e) => !e.href.startsWith(contentPaths.config.href)
38
+ );
39
+ for (const entry of entries) {
40
+ events.push(handleEvent({ name: "add", entry }, { logLevel: "warn" }));
41
+ }
42
+ await runEvents();
43
+ }
44
+ async function handleEvent(event, opts) {
45
+ const logLevel = (opts == null ? void 0 : opts.logLevel) ?? "info";
46
+ if (event.name === "addDir" || event.name === "unlinkDir") {
47
+ const collection2 = normalizePath(
48
+ path.relative(fileURLToPath(contentPaths.contentDir), fileURLToPath(event.entry))
49
+ );
50
+ const isCollectionEvent = collection2.split("/").length === 1;
51
+ if (!isCollectionEvent)
52
+ return { shouldGenerateTypes: false };
53
+ switch (event.name) {
54
+ case "addDir":
55
+ addCollection(contentTypes, JSON.stringify(collection2));
56
+ if (logLevel === "info") {
57
+ info(logging, "content", `${cyan(collection2)} collection added`);
58
+ }
59
+ break;
60
+ case "unlinkDir":
61
+ removeCollection(contentTypes, JSON.stringify(collection2));
62
+ break;
63
+ }
64
+ return { shouldGenerateTypes: true };
65
+ }
66
+ const fileType = getEntryType(fileURLToPath(event.entry), contentPaths);
67
+ if (fileType === "generated-types") {
68
+ return { shouldGenerateTypes: false };
69
+ }
70
+ if (fileType === "config") {
71
+ contentConfigObserver.set({ status: "loading" });
72
+ const config = await loadContentConfig({ fs, settings });
73
+ if (config instanceof Error) {
74
+ contentConfigObserver.set({ status: "error", error: config });
75
+ } else {
76
+ contentConfigObserver.set({ status: "loaded", config });
77
+ }
78
+ return { shouldGenerateTypes: true };
79
+ }
80
+ const entryInfo = getEntryInfo({
81
+ entry: event.entry,
82
+ contentDir: contentPaths.contentDir
83
+ });
84
+ if (entryInfo instanceof Error)
85
+ return { shouldGenerateTypes: false };
86
+ if (fileType === "unknown") {
87
+ if (entryInfo.id.startsWith("_") && (event.name === "add" || event.name === "change")) {
88
+ return { shouldGenerateTypes: false };
89
+ } else {
90
+ return {
91
+ shouldGenerateTypes: false,
92
+ error: new UnsupportedFileTypeError(entryInfo.id)
93
+ };
94
+ }
95
+ }
96
+ if (entryInfo.collection === ".") {
97
+ if (["info", "warn"].includes(logLevel)) {
98
+ warn(
99
+ logging,
100
+ "content",
101
+ `${cyan(
102
+ normalizePath(
103
+ path.relative(fileURLToPath(contentPaths.contentDir), fileURLToPath(event.entry))
104
+ )
105
+ )} must be nested in a collection directory. Skipping.`
106
+ );
107
+ }
108
+ return { shouldGenerateTypes: false };
109
+ }
110
+ const { id, slug, collection } = entryInfo;
111
+ const collectionKey = JSON.stringify(collection);
112
+ const entryKey = JSON.stringify(id);
113
+ switch (event.name) {
114
+ case "add":
115
+ if (!(collectionKey in contentTypes)) {
116
+ addCollection(contentTypes, collectionKey);
117
+ }
118
+ if (!(entryKey in contentTypes[collectionKey])) {
119
+ addEntry(contentTypes, collectionKey, entryKey, slug);
120
+ }
121
+ return { shouldGenerateTypes: true };
122
+ case "unlink":
123
+ if (collectionKey in contentTypes && entryKey in contentTypes[collectionKey]) {
124
+ removeEntry(contentTypes, collectionKey, entryKey);
125
+ }
126
+ return { shouldGenerateTypes: true };
127
+ case "change":
128
+ return { shouldGenerateTypes: false };
129
+ }
130
+ }
131
+ function queueEvent(rawEvent, opts) {
132
+ const event = {
133
+ entry: pathToFileURL(rawEvent.entry),
134
+ name: rawEvent.name
135
+ };
136
+ if (!event.entry.pathname.startsWith(contentPaths.contentDir.pathname))
137
+ return;
138
+ events.push(handleEvent(event, opts));
139
+ debounceTimeout && clearTimeout(debounceTimeout);
140
+ debounceTimeout = setTimeout(
141
+ async () => runEvents(opts),
142
+ 50
143
+ );
144
+ }
145
+ async function runEvents(opts) {
146
+ const logLevel = (opts == null ? void 0 : opts.logLevel) ?? "info";
147
+ const eventResponses = await Promise.all(events);
148
+ events = [];
149
+ let unsupportedFiles = [];
150
+ for (const response of eventResponses) {
151
+ if (response.error instanceof UnsupportedFileTypeError) {
152
+ unsupportedFiles.push(response.error.message);
153
+ }
154
+ }
155
+ if (unsupportedFiles.length > 0 && ["info", "warn"].includes(logLevel)) {
156
+ warn(
157
+ logging,
158
+ "content",
159
+ `Unsupported file types found. Prefix with an underscore (\`_\`) to ignore:
160
+ - ${unsupportedFiles.join(
161
+ "\n"
162
+ )}`
163
+ );
164
+ }
165
+ const observable = contentConfigObserver.get();
166
+ if (eventResponses.some((r) => r.shouldGenerateTypes)) {
167
+ await writeContentFiles({
168
+ fs,
169
+ contentTypes,
170
+ contentPaths,
171
+ contentTypesBase,
172
+ contentConfig: observable.status === "loaded" ? observable.config : void 0
173
+ });
174
+ if (observable.status === "loaded" && ["info", "warn"].includes(logLevel)) {
175
+ warnNonexistentCollections({
176
+ logging,
177
+ contentConfig: observable.config,
178
+ contentTypes
179
+ });
180
+ }
181
+ }
182
+ }
183
+ return { init, queueEvent };
184
+ }
185
+ function addCollection(contentMap, collectionKey) {
186
+ contentMap[collectionKey] = {};
187
+ }
188
+ function removeCollection(contentMap, collectionKey) {
189
+ delete contentMap[collectionKey];
190
+ }
191
+ function addEntry(contentTypes, collectionKey, entryKey, slug) {
192
+ contentTypes[collectionKey][entryKey] = { slug };
193
+ }
194
+ function removeEntry(contentTypes, collectionKey, entryKey) {
195
+ delete contentTypes[collectionKey][entryKey];
196
+ }
197
+ function getEntryInfo({
198
+ entry,
199
+ contentDir
200
+ }) {
201
+ const rawRelativePath = path.relative(fileURLToPath(contentDir), fileURLToPath(entry));
202
+ const rawCollection = path.dirname(rawRelativePath).split(path.sep).shift();
203
+ if (!rawCollection)
204
+ return new Error();
205
+ const rawId = path.relative(rawCollection, rawRelativePath);
206
+ const rawSlug = rawId.replace(path.extname(rawId), "");
207
+ const res = {
208
+ id: normalizePath(rawId),
209
+ slug: normalizePath(rawSlug),
210
+ collection: normalizePath(rawCollection)
211
+ };
212
+ return res;
213
+ }
214
+ function getEntryType(entryPath, paths) {
215
+ const { dir: rawDir, ext, name, base } = path.parse(entryPath);
216
+ const dir = appendForwardSlash(pathToFileURL(rawDir).href);
217
+ if (contentFileExts.includes(ext)) {
218
+ return "content";
219
+ } else if (new URL(name, dir).pathname === paths.config.pathname) {
220
+ return "config";
221
+ } else if (new URL(base, dir).pathname === new URL(CONTENT_TYPES_FILE, paths.cacheDir).pathname) {
222
+ return "generated-types";
223
+ } else {
224
+ return "unknown";
225
+ }
226
+ }
227
+ async function writeContentFiles({
228
+ fs,
229
+ contentPaths,
230
+ contentTypes,
231
+ contentTypesBase,
232
+ contentConfig
233
+ }) {
234
+ let contentTypesStr = "";
235
+ const collectionKeys = Object.keys(contentTypes).sort();
236
+ for (const collectionKey of collectionKeys) {
237
+ const collectionConfig = contentConfig == null ? void 0 : contentConfig.collections[JSON.parse(collectionKey)];
238
+ contentTypesStr += `${collectionKey}: {
239
+ `;
240
+ const entryKeys = Object.keys(contentTypes[collectionKey]).sort();
241
+ for (const entryKey of entryKeys) {
242
+ const entryMetadata = contentTypes[collectionKey][entryKey];
243
+ const dataType = (collectionConfig == null ? void 0 : collectionConfig.schema) ? `InferEntrySchema<${collectionKey}>` : "any";
244
+ const slugType = (collectionConfig == null ? void 0 : collectionConfig.slug) ? "string" : JSON.stringify(entryMetadata.slug);
245
+ contentTypesStr += `${entryKey}: {
246
+ id: ${entryKey},
247
+ slug: ${slugType},
248
+ body: string,
249
+ collection: ${collectionKey},
250
+ data: ${dataType}
251
+ },
252
+ `;
253
+ }
254
+ contentTypesStr += `},
255
+ `;
256
+ }
257
+ let configPathRelativeToCacheDir = normalizePath(
258
+ path.relative(contentPaths.cacheDir.pathname, contentPaths.config.pathname)
259
+ );
260
+ if (!isRelativePath(configPathRelativeToCacheDir))
261
+ configPathRelativeToCacheDir = "./" + configPathRelativeToCacheDir;
262
+ contentTypesBase = contentTypesBase.replace("// @@ENTRY_MAP@@", contentTypesStr);
263
+ contentTypesBase = contentTypesBase.replace(
264
+ "'@@CONTENT_CONFIG_TYPE@@'",
265
+ contentConfig ? `typeof import(${JSON.stringify(configPathRelativeToCacheDir)})` : "never"
266
+ );
267
+ await fs.promises.writeFile(new URL(CONTENT_TYPES_FILE, contentPaths.cacheDir), contentTypesBase);
268
+ }
269
+ function warnNonexistentCollections({
270
+ contentConfig,
271
+ contentTypes,
272
+ logging
273
+ }) {
274
+ for (const configuredCollection in contentConfig.collections) {
275
+ if (!contentTypes[JSON.stringify(configuredCollection)]) {
276
+ warn(
277
+ logging,
278
+ "content",
279
+ `${JSON.stringify(
280
+ configuredCollection
281
+ )} is not a collection. Check your content config for typos.`
282
+ );
283
+ }
284
+ }
285
+ }
286
+ export {
287
+ createContentTypesGenerator,
288
+ getEntryInfo,
289
+ getEntryType
290
+ };