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.
- package/dist/@types/astro.d.ts +59 -0
- package/dist/content/consts.d.ts +9 -0
- package/dist/content/consts.js +20 -0
- package/dist/content/index.d.ts +3 -0
- package/dist/content/index.js +12 -0
- package/dist/content/internal.d.ts +37 -0
- package/dist/content/internal.js +131 -0
- package/dist/content/template/types.generated.d.js +0 -0
- package/dist/content/template/types.generated.d.mts +3 -0
- package/dist/content/types-generator.d.ts +32 -0
- package/dist/content/types-generator.js +290 -0
- package/dist/content/utils.d.ts +166 -0
- package/dist/content/utils.js +175 -0
- package/dist/content/vite-plugin-content-assets.d.ts +8 -0
- package/dist/content/vite-plugin-content-assets.js +92 -0
- package/dist/content/vite-plugin-content-server.d.ts +13 -0
- package/dist/content/vite-plugin-content-server.js +174 -0
- package/dist/content/vite-plugin-content-virtual-mod.d.ts +7 -0
- package/dist/content/vite-plugin-content-virtual-mod.js +38 -0
- package/dist/core/app/index.js +4 -1
- package/dist/core/build/common.js +5 -1
- package/dist/core/build/generate.d.ts +1 -1
- package/dist/core/build/generate.js +23 -7
- package/dist/core/build/graph.d.ts +1 -1
- package/dist/core/build/graph.js +4 -2
- package/dist/core/build/internal.d.ts +12 -0
- package/dist/core/build/internal.js +30 -0
- package/dist/core/build/page-data.js +2 -0
- package/dist/core/build/static-build.js +110 -22
- package/dist/core/build/types.d.ts +3 -1
- package/dist/core/build/vite-plugin-analyzer.js +7 -3
- package/dist/core/build/vite-plugin-css.d.ts +1 -1
- package/dist/core/build/vite-plugin-css.js +50 -2
- package/dist/core/build/vite-plugin-internals.js +7 -0
- package/dist/core/build/vite-plugin-pages.d.ts +1 -1
- package/dist/core/build/vite-plugin-pages.js +2 -2
- package/dist/core/build/vite-plugin-prerender.d.ts +4 -0
- package/dist/core/build/vite-plugin-prerender.js +36 -0
- package/dist/core/build/vite-plugin-ssr.js +31 -3
- package/dist/core/compile/style.js +6 -3
- package/dist/core/config/config.js +11 -1
- package/dist/core/config/schema.d.ts +142 -69
- package/dist/core/config/schema.js +13 -1
- package/dist/core/constants.js +1 -1
- package/dist/core/cookies/cookies.d.ts +1 -3
- package/dist/core/cookies/cookies.js +3 -0
- package/dist/core/create-vite.js +13 -1
- package/dist/core/dev/container.js +2 -2
- package/dist/core/dev/dev.js +1 -1
- package/dist/core/errors/dev/utils.d.ts +4 -0
- package/dist/core/errors/dev/utils.js +38 -12
- package/dist/core/errors/dev/vite.d.ts +16 -1
- package/dist/core/errors/dev/vite.js +35 -12
- package/dist/core/errors/errors-data.d.ts +31 -0
- package/dist/core/errors/errors-data.js +27 -1
- package/dist/core/errors/errors.d.ts +3 -1
- package/dist/core/errors/errors.js +1 -3
- package/dist/core/errors/overlay.d.ts +2 -0
- package/dist/core/errors/overlay.js +540 -0
- package/dist/core/errors/utils.d.ts +10 -0
- package/dist/core/messages.js +14 -5
- package/dist/core/preview/index.js +2 -2
- package/dist/core/preview/static-preview-server.d.ts +3 -2
- package/dist/core/preview/static-preview-server.js +13 -2
- package/dist/core/render/dev/vite.js +3 -0
- package/dist/core/render/result.js +2 -1
- package/dist/core/routing/match.d.ts +2 -0
- package/dist/core/routing/match.js +13 -0
- package/dist/core/util.d.ts +1 -0
- package/dist/core/util.js +8 -0
- package/dist/integrations/index.js +2 -0
- package/dist/jsx/babel.js +2 -1
- package/dist/runtime/server/astro-component.d.ts +8 -1
- package/dist/runtime/server/index.d.ts +2 -2
- package/dist/runtime/server/index.js +2 -0
- package/dist/runtime/server/render/astro/factory.d.ts +1 -1
- package/dist/runtime/server/render/astro/index.d.ts +1 -1
- package/dist/runtime/server/render/astro/instance.d.ts +6 -2
- package/dist/runtime/server/render/astro/instance.js +5 -2
- package/dist/runtime/server/render/component.js +9 -2
- package/dist/runtime/server/render/index.d.ts +2 -2
- package/dist/runtime/server/render/index.js +2 -1
- package/dist/runtime/server/render/{stylesheet.d.ts → tags.d.ts} +3 -2
- package/dist/runtime/server/render/{stylesheet.js → tags.js} +16 -12
- package/dist/vite-plugin-astro/index.js +2 -1
- package/dist/vite-plugin-astro/types.d.ts +4 -0
- package/dist/vite-plugin-astro-server/plugin.js +2 -7
- package/dist/vite-plugin-astro-server/response.js +4 -1
- package/dist/vite-plugin-astro-server/route.js +4 -0
- package/dist/vite-plugin-markdown/index.js +10 -5
- package/dist/vite-plugin-markdown-legacy/index.js +2 -1
- package/dist/vite-plugin-scanner/index.d.ts +7 -0
- package/dist/vite-plugin-scanner/index.js +42 -0
- package/dist/vite-plugin-scanner/scan.d.ts +2 -0
- package/dist/vite-plugin-scanner/scan.js +41 -0
- package/dist/vite-plugin-scripts/page-ssr.js +1 -1
- package/dist/vite-plugin-utils/index.d.ts +7 -1
- package/dist/vite-plugin-utils/index.js +4 -0
- package/package.json +15 -6
- package/src/content/template/types.generated.d.ts +49 -0
- package/src/content/template/types.generated.mjs +40 -0
- package/zod.d.ts +5 -0
- package/zod.mjs +3 -0
package/dist/@types/astro.d.ts
CHANGED
|
@@ -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,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,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
|
+
};
|