astro 5.9.3 → 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 +44 -44
- package/dist/core/config/schemas/base.js +9 -9
- package/dist/core/config/schemas/refined.js +0 -7
- package/dist/core/config/schemas/relative.d.ts +58 -58
- 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/core/session.d.ts +1 -1
- package/dist/core/session.js +10 -9
- package/dist/integrations/hooks.js +5 -2
- package/dist/runtime/client/dev-toolbar/apps/astro.js +4 -6
- package/dist/runtime/server/render/csp.js +1 -1
- package/dist/types/public/config.d.ts +40 -131
- package/dist/types/public/content.d.ts +30 -0
- package/package.json +4 -3
- package/templates/content/module.mjs +14 -0
- package/templates/content/types.d.ts +43 -0
- package/types/content.d.ts +23 -80
package/client.d.ts
CHANGED
|
@@ -48,9 +48,7 @@ declare module 'astro:assets' {
|
|
|
48
48
|
getImage: (
|
|
49
49
|
options: import('./dist/assets/types.js').UnresolvedImageTransform,
|
|
50
50
|
) => Promise<import('./dist/assets/types.js').GetImageResult>;
|
|
51
|
-
imageConfig: import('./dist/types/public/config.js').AstroConfig['image']
|
|
52
|
-
experimentalResponsiveImages: boolean;
|
|
53
|
-
};
|
|
51
|
+
imageConfig: import('./dist/types/public/config.js').AstroConfig['image'];
|
|
54
52
|
getConfiguredImageService: typeof import('./dist/assets/index.js').getConfiguredImageService;
|
|
55
53
|
inferRemoteSize: typeof import('./dist/assets/utils/index.js').inferRemoteSize;
|
|
56
54
|
Image: typeof import('./components/Image.astro').default;
|
package/components/Image.astro
CHANGED
|
@@ -24,14 +24,13 @@ if (typeof props.height === 'string') {
|
|
|
24
24
|
props.height = parseInt(props.height);
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
const layout = props.layout ?? imageConfig.
|
|
28
|
-
const useResponsive = imageConfig.experimentalResponsiveImages && layout !== 'none';
|
|
27
|
+
const layout = props.layout ?? imageConfig.layout ?? 'none';
|
|
29
28
|
|
|
30
|
-
if (
|
|
29
|
+
if (layout !== 'none') {
|
|
31
30
|
// Apply defaults from imageConfig if not provided
|
|
32
|
-
props.layout ??= imageConfig.
|
|
33
|
-
props.fit ??= imageConfig.
|
|
34
|
-
props.position ??= imageConfig.
|
|
31
|
+
props.layout ??= imageConfig.layout;
|
|
32
|
+
props.fit ??= imageConfig.objectFit ?? 'cover';
|
|
33
|
+
props.position ??= imageConfig.objectPosition ?? 'center';
|
|
35
34
|
}
|
|
36
35
|
|
|
37
36
|
const image = await getImage(props as UnresolvedImageTransform);
|
package/components/Picture.astro
CHANGED
|
@@ -42,14 +42,14 @@ if (scopedStyleClass) {
|
|
|
42
42
|
}
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
const layout = props.layout ?? imageConfig.
|
|
46
|
-
const useResponsive =
|
|
45
|
+
const layout = props.layout ?? imageConfig.layout ?? 'none';
|
|
46
|
+
const useResponsive = layout !== 'none';
|
|
47
47
|
|
|
48
48
|
if (useResponsive) {
|
|
49
49
|
// Apply defaults from imageConfig if not provided
|
|
50
|
-
props.layout ??= imageConfig.
|
|
51
|
-
props.fit ??= imageConfig.
|
|
52
|
-
props.position ??= imageConfig.
|
|
50
|
+
props.layout ??= imageConfig.layout;
|
|
51
|
+
props.fit ??= imageConfig.objectFit ?? 'cover';
|
|
52
|
+
props.position ??= imageConfig.objectPosition ?? 'center';
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
for (const key in props) {
|
|
@@ -4,6 +4,7 @@ import { default as Picture, type Props as PictureProps } from './Picture.astro'
|
|
|
4
4
|
type Props = PictureProps;
|
|
5
5
|
|
|
6
6
|
const { class: className, ...props } = Astro.props;
|
|
7
|
+
import './image.css';
|
|
7
8
|
---
|
|
8
9
|
|
|
9
10
|
{/* Applying class outside of the spread prevents it from applying unnecessary astro-* classes */}
|
|
@@ -4,6 +4,7 @@ import type { AstroIntegration } from '../types/public/integrations.js';
|
|
|
4
4
|
* This integration is applied when the user is using Actions in their project.
|
|
5
5
|
* It will inject the necessary routes and middlewares to handle actions.
|
|
6
6
|
*/
|
|
7
|
-
export default function astroIntegrationActionsRouteHandler({ settings, }: {
|
|
7
|
+
export default function astroIntegrationActionsRouteHandler({ settings, filename, }: {
|
|
8
8
|
settings: AstroSettings;
|
|
9
|
+
filename: string;
|
|
9
10
|
}): AstroIntegration;
|
|
@@ -3,7 +3,8 @@ import { AstroError } from "../core/errors/errors.js";
|
|
|
3
3
|
import { viteID } from "../core/util.js";
|
|
4
4
|
import { ACTIONS_TYPES_FILE, ACTION_RPC_ROUTE_PATTERN, VIRTUAL_MODULE_ID } from "./consts.js";
|
|
5
5
|
function astroIntegrationActionsRouteHandler({
|
|
6
|
-
settings
|
|
6
|
+
settings,
|
|
7
|
+
filename
|
|
7
8
|
}) {
|
|
8
9
|
return {
|
|
9
10
|
name: VIRTUAL_MODULE_ID,
|
|
@@ -23,7 +24,7 @@ function astroIntegrationActionsRouteHandler({
|
|
|
23
24
|
throw error;
|
|
24
25
|
}
|
|
25
26
|
const stringifiedActionsImport = JSON.stringify(
|
|
26
|
-
viteID(new URL(
|
|
27
|
+
viteID(new URL(`./${filename}`, params.config.srcDir))
|
|
27
28
|
);
|
|
28
29
|
settings.injectedTypes.push({
|
|
29
30
|
filename: ACTIONS_TYPES_FILE,
|
package/dist/actions/utils.d.ts
CHANGED
|
@@ -7,4 +7,4 @@ export declare function createCallAction(context: ActionAPIContext): APIContext[
|
|
|
7
7
|
/**
|
|
8
8
|
* Check whether the Actions config file is present.
|
|
9
9
|
*/
|
|
10
|
-
export declare function isActionsFilePresent(fs: typeof fsMod, srcDir: URL): Promise<
|
|
10
|
+
export declare function isActionsFilePresent(fs: typeof fsMod, srcDir: URL): Promise<string | false>;
|
package/dist/actions/utils.js
CHANGED
|
@@ -26,20 +26,20 @@ async function isActionsFilePresent(fs, srcDir) {
|
|
|
26
26
|
if (!actionsFile) return false;
|
|
27
27
|
let contents;
|
|
28
28
|
try {
|
|
29
|
-
contents = fs.readFileSync(actionsFile, "utf-8");
|
|
29
|
+
contents = fs.readFileSync(actionsFile.url, "utf-8");
|
|
30
30
|
} catch {
|
|
31
31
|
return false;
|
|
32
32
|
}
|
|
33
|
-
const [, exports] = eslexer.parse(contents, actionsFile.pathname);
|
|
33
|
+
const [, exports] = eslexer.parse(contents, actionsFile.url.pathname);
|
|
34
34
|
for (const exp of exports) {
|
|
35
35
|
if (exp.n === "server") {
|
|
36
|
-
return
|
|
36
|
+
return actionsFile.filename;
|
|
37
37
|
}
|
|
38
38
|
}
|
|
39
39
|
return false;
|
|
40
40
|
}
|
|
41
41
|
function search(fs, srcDir) {
|
|
42
|
-
const
|
|
42
|
+
const filenames = [
|
|
43
43
|
"actions.mjs",
|
|
44
44
|
"actions.js",
|
|
45
45
|
"actions.mts",
|
|
@@ -48,10 +48,11 @@ function search(fs, srcDir) {
|
|
|
48
48
|
"actions/index.js",
|
|
49
49
|
"actions/index.mts",
|
|
50
50
|
"actions/index.ts"
|
|
51
|
-
]
|
|
52
|
-
for (const
|
|
53
|
-
|
|
54
|
-
|
|
51
|
+
];
|
|
52
|
+
for (const filename of filenames) {
|
|
53
|
+
const url = new URL(filename, srcDir);
|
|
54
|
+
if (fs.existsSync(url)) {
|
|
55
|
+
return { filename, url };
|
|
55
56
|
}
|
|
56
57
|
}
|
|
57
58
|
return void 0;
|
|
@@ -2,8 +2,4 @@ import type { AstroConfig } from '../types/public/config.js';
|
|
|
2
2
|
import { type ImageService } from './services/service.js';
|
|
3
3
|
import { type GetImageResult, type UnresolvedImageTransform } from './types.js';
|
|
4
4
|
export declare function getConfiguredImageService(): Promise<ImageService>;
|
|
5
|
-
|
|
6
|
-
experimentalResponsiveImages: boolean;
|
|
7
|
-
};
|
|
8
|
-
export declare function getImage(options: UnresolvedImageTransform, imageConfig: ImageConfig): Promise<GetImageResult>;
|
|
9
|
-
export {};
|
|
5
|
+
export declare function getImage(options: UnresolvedImageTransform, imageConfig: AstroConfig['image']): Promise<GetImageResult>;
|
package/dist/assets/internal.js
CHANGED
|
@@ -89,36 +89,34 @@ async function getImage(options, imageConfig) {
|
|
|
89
89
|
}
|
|
90
90
|
}
|
|
91
91
|
resolvedOptions.src = clonedSrc;
|
|
92
|
-
const layout = options.layout ?? imageConfig.
|
|
93
|
-
if (
|
|
92
|
+
const layout = options.layout ?? imageConfig.layout ?? "none";
|
|
93
|
+
if (resolvedOptions.priority) {
|
|
94
|
+
resolvedOptions.loading ??= "eager";
|
|
95
|
+
resolvedOptions.decoding ??= "sync";
|
|
96
|
+
resolvedOptions.fetchpriority ??= "high";
|
|
97
|
+
delete resolvedOptions.priority;
|
|
98
|
+
} else {
|
|
99
|
+
resolvedOptions.loading ??= "lazy";
|
|
100
|
+
resolvedOptions.decoding ??= "async";
|
|
101
|
+
resolvedOptions.fetchpriority ??= "auto";
|
|
102
|
+
}
|
|
103
|
+
if (layout !== "none") {
|
|
94
104
|
resolvedOptions.widths ||= getWidths({
|
|
95
105
|
width: resolvedOptions.width,
|
|
96
106
|
layout,
|
|
97
107
|
originalWidth,
|
|
98
|
-
breakpoints: imageConfig.
|
|
108
|
+
breakpoints: imageConfig.breakpoints?.length ? imageConfig.breakpoints : isLocalService(service) ? LIMITED_RESOLUTIONS : DEFAULT_RESOLUTIONS
|
|
99
109
|
});
|
|
100
110
|
resolvedOptions.sizes ||= getSizesAttribute({ width: resolvedOptions.width, layout });
|
|
101
|
-
if (resolvedOptions.priority) {
|
|
102
|
-
resolvedOptions.loading ??= "eager";
|
|
103
|
-
resolvedOptions.decoding ??= "sync";
|
|
104
|
-
resolvedOptions.fetchpriority ??= "high";
|
|
105
|
-
} else {
|
|
106
|
-
resolvedOptions.loading ??= "lazy";
|
|
107
|
-
resolvedOptions.decoding ??= "async";
|
|
108
|
-
resolvedOptions.fetchpriority ??= "auto";
|
|
109
|
-
}
|
|
110
|
-
delete resolvedOptions.priority;
|
|
111
111
|
delete resolvedOptions.densities;
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
resolvedOptions["data-astro-image"] = layout;
|
|
121
|
-
}
|
|
112
|
+
resolvedOptions.style = addCSSVarsToStyle(
|
|
113
|
+
{
|
|
114
|
+
fit: cssFitValues.includes(resolvedOptions.fit ?? "") && resolvedOptions.fit,
|
|
115
|
+
pos: resolvedOptions.position
|
|
116
|
+
},
|
|
117
|
+
resolvedOptions.style
|
|
118
|
+
);
|
|
119
|
+
resolvedOptions["data-astro-image"] = layout;
|
|
122
120
|
}
|
|
123
121
|
const validatedOptions = service.validateOptions ? await service.validateOptions(resolvedOptions, imageConfig) : resolvedOptions;
|
|
124
122
|
const srcSetTransforms = service.getSrcSet ? await service.getSrcSet(validatedOptions, imageConfig) : [];
|
package/dist/assets/types.d.ts
CHANGED
|
@@ -138,9 +138,9 @@ type ImageSharedProps<T> = T & {
|
|
|
138
138
|
quality?: ImageQuality;
|
|
139
139
|
} & ({
|
|
140
140
|
/**
|
|
141
|
-
* The layout type for responsive images.
|
|
141
|
+
* The layout type for responsive images.
|
|
142
142
|
*
|
|
143
|
-
* Allowed values are `constrained`, `fixed`, `full-width` or `none`. Defaults to value of `image.
|
|
143
|
+
* Allowed values are `constrained`, `fixed`, `full-width` or `none`. Defaults to value of `image.layout`.
|
|
144
144
|
*
|
|
145
145
|
* - `constrained` - The image will scale to fit the container, maintaining its aspect ratio, but will not exceed the specified dimensions.
|
|
146
146
|
* - `fixed` - The image will maintain its original dimensions.
|
|
@@ -153,7 +153,7 @@ type ImageSharedProps<T> = T & {
|
|
|
153
153
|
*/
|
|
154
154
|
layout?: ImageLayout;
|
|
155
155
|
/**
|
|
156
|
-
* Defines how the image should be cropped if the aspect ratio is changed. Requires
|
|
156
|
+
* Defines how the image should be cropped if the aspect ratio is changed. Requires `layout` to be set.
|
|
157
157
|
*
|
|
158
158
|
* Default is `cover`. Allowed values are `fill`, `contain`, `cover`, `none` or `scale-down`. These behave like the equivalent CSS `object-fit` values. Other values may be passed if supported by the image service.
|
|
159
159
|
*
|
|
@@ -164,7 +164,7 @@ type ImageSharedProps<T> = T & {
|
|
|
164
164
|
*/
|
|
165
165
|
fit?: ImageFit;
|
|
166
166
|
/**
|
|
167
|
-
* Defines the position of the image when cropping. Requires
|
|
167
|
+
* Defines the position of the image when cropping. Requires `layout` to be set.
|
|
168
168
|
*
|
|
169
169
|
* The value is a string that specifies the position of the image, which matches the CSS `object-position` property. Other values may be passed if supported by the image service.
|
|
170
170
|
*
|
|
@@ -71,7 +71,7 @@ function assets({ fs, settings, sync, logger }) {
|
|
|
71
71
|
globalThis.astroAsset = {
|
|
72
72
|
referencedImages: /* @__PURE__ */ new Set()
|
|
73
73
|
};
|
|
74
|
-
const imageComponentPrefix = settings.config.
|
|
74
|
+
const imageComponentPrefix = settings.config.image.responsiveStyles ? "Responsive" : "";
|
|
75
75
|
return [
|
|
76
76
|
// Expose the components and different utilities from `astro:assets`
|
|
77
77
|
{
|
|
@@ -98,7 +98,7 @@ function assets({ fs, settings, sync, logger }) {
|
|
|
98
98
|
export { default as Font } from "astro/components/Font.astro";
|
|
99
99
|
export { inferRemoteSize } from "astro/assets/utils/inferRemoteSize.js";
|
|
100
100
|
|
|
101
|
-
export const imageConfig = ${JSON.stringify(
|
|
101
|
+
export const imageConfig = ${JSON.stringify(settings.config.image)};
|
|
102
102
|
// This is used by the @astrojs/node integration to locate images.
|
|
103
103
|
// It's unused on other platforms, but on some platforms like Netlify (and presumably also Vercel)
|
|
104
104
|
// new URL("dist/...") is interpreted by the bundler as a signal to include that directory
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import type { ZodLiteral, ZodNumber, ZodObject, ZodString, ZodType, ZodUnion } from 'zod';
|
|
2
|
+
import type { LiveLoader, Loader } from './loaders/types.js';
|
|
3
|
+
export type ImageFunction = () => ZodObject<{
|
|
4
|
+
src: ZodString;
|
|
5
|
+
width: ZodNumber;
|
|
6
|
+
height: ZodNumber;
|
|
7
|
+
format: ZodUnion<[
|
|
8
|
+
ZodLiteral<'png'>,
|
|
9
|
+
ZodLiteral<'jpg'>,
|
|
10
|
+
ZodLiteral<'jpeg'>,
|
|
11
|
+
ZodLiteral<'tiff'>,
|
|
12
|
+
ZodLiteral<'webp'>,
|
|
13
|
+
ZodLiteral<'gif'>,
|
|
14
|
+
ZodLiteral<'svg'>,
|
|
15
|
+
ZodLiteral<'avif'>
|
|
16
|
+
]>;
|
|
17
|
+
}>;
|
|
18
|
+
export interface DataEntry {
|
|
19
|
+
id: string;
|
|
20
|
+
data: Record<string, unknown>;
|
|
21
|
+
filePath?: string;
|
|
22
|
+
body?: string;
|
|
23
|
+
}
|
|
24
|
+
export interface DataStore {
|
|
25
|
+
get: (key: string) => DataEntry;
|
|
26
|
+
entries: () => Array<[id: string, DataEntry]>;
|
|
27
|
+
set: (key: string, data: Record<string, unknown>, body?: string, filePath?: string) => void;
|
|
28
|
+
values: () => Array<DataEntry>;
|
|
29
|
+
keys: () => Array<string>;
|
|
30
|
+
delete: (key: string) => void;
|
|
31
|
+
clear: () => void;
|
|
32
|
+
has: (key: string) => boolean;
|
|
33
|
+
}
|
|
34
|
+
export interface MetaStore {
|
|
35
|
+
get: (key: string) => string | undefined;
|
|
36
|
+
set: (key: string, value: string) => void;
|
|
37
|
+
delete: (key: string) => void;
|
|
38
|
+
has: (key: string) => boolean;
|
|
39
|
+
}
|
|
40
|
+
export type BaseSchema = ZodType;
|
|
41
|
+
export type SchemaContext = {
|
|
42
|
+
image: ImageFunction;
|
|
43
|
+
};
|
|
44
|
+
type ContentLayerConfig<S extends BaseSchema, TData extends {
|
|
45
|
+
id: string;
|
|
46
|
+
} = {
|
|
47
|
+
id: string;
|
|
48
|
+
}> = {
|
|
49
|
+
type?: 'content_layer';
|
|
50
|
+
schema?: S | ((context: SchemaContext) => S);
|
|
51
|
+
loader: Loader | (() => Array<TData> | Promise<Array<TData>> | Record<string, Omit<TData, 'id'> & {
|
|
52
|
+
id?: string;
|
|
53
|
+
}> | Promise<Record<string, Omit<TData, 'id'> & {
|
|
54
|
+
id?: string;
|
|
55
|
+
}>>);
|
|
56
|
+
};
|
|
57
|
+
type DataCollectionConfig<S extends BaseSchema> = {
|
|
58
|
+
type: 'data';
|
|
59
|
+
schema?: S | ((context: SchemaContext) => S);
|
|
60
|
+
};
|
|
61
|
+
type ContentCollectionConfig<S extends BaseSchema> = {
|
|
62
|
+
type?: 'content';
|
|
63
|
+
schema?: S | ((context: SchemaContext) => S);
|
|
64
|
+
loader?: never;
|
|
65
|
+
};
|
|
66
|
+
export type LiveCollectionConfig<L extends LiveLoader, S extends BaseSchema | undefined = undefined> = {
|
|
67
|
+
type: 'live';
|
|
68
|
+
schema?: S;
|
|
69
|
+
loader: L;
|
|
70
|
+
};
|
|
71
|
+
export type CollectionConfig<S extends BaseSchema> = ContentCollectionConfig<S> | DataCollectionConfig<S> | ContentLayerConfig<S>;
|
|
72
|
+
export declare function defineLiveCollection<L extends LiveLoader, S extends BaseSchema | undefined = undefined>(config: LiveCollectionConfig<L, S>): LiveCollectionConfig<L, S>;
|
|
73
|
+
export declare function defineCollection<S extends BaseSchema>(config: CollectionConfig<S>): CollectionConfig<S>;
|
|
74
|
+
export {};
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { AstroError, AstroErrorData, AstroUserError } from "../core/errors/index.js";
|
|
2
|
+
import { CONTENT_LAYER_TYPE, LIVE_CONTENT_TYPE } from "./consts.js";
|
|
3
|
+
function getImporterFilename() {
|
|
4
|
+
const stackLine = new Error().stack?.split("\n").find(
|
|
5
|
+
(line) => !line.includes("defineCollection") && !line.includes("defineLiveCollection") && !line.includes("getImporterFilename") && line !== "Error"
|
|
6
|
+
);
|
|
7
|
+
if (!stackLine) {
|
|
8
|
+
return void 0;
|
|
9
|
+
}
|
|
10
|
+
const match = /\/((?:src|chunks)\/.*?):\d+:\d+/.exec(stackLine);
|
|
11
|
+
return match?.[1] ?? void 0;
|
|
12
|
+
}
|
|
13
|
+
function defineLiveCollection(config) {
|
|
14
|
+
const importerFilename = getImporterFilename();
|
|
15
|
+
if (!importerFilename?.includes("live.config")) {
|
|
16
|
+
throw new AstroError({
|
|
17
|
+
...AstroErrorData.LiveContentConfigError,
|
|
18
|
+
message: AstroErrorData.LiveContentConfigError.message(
|
|
19
|
+
"Live collections must be defined in a `src/live.config.ts` file.",
|
|
20
|
+
importerFilename ?? "your content config file"
|
|
21
|
+
)
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
if (config.type !== LIVE_CONTENT_TYPE) {
|
|
25
|
+
throw new AstroError({
|
|
26
|
+
...AstroErrorData.LiveContentConfigError,
|
|
27
|
+
message: AstroErrorData.LiveContentConfigError.message(
|
|
28
|
+
"Collections in a live config file must have a type of `live`.",
|
|
29
|
+
importerFilename
|
|
30
|
+
)
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
if (!config.loader) {
|
|
34
|
+
throw new AstroError({
|
|
35
|
+
...AstroErrorData.LiveContentConfigError,
|
|
36
|
+
message: AstroErrorData.LiveContentConfigError.message(
|
|
37
|
+
"Live collections must have a `loader` defined.",
|
|
38
|
+
importerFilename
|
|
39
|
+
)
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
if (typeof config.schema === "function") {
|
|
43
|
+
throw new AstroError({
|
|
44
|
+
...AstroErrorData.LiveContentConfigError,
|
|
45
|
+
message: AstroErrorData.LiveContentConfigError.message(
|
|
46
|
+
"The schema cannot be a function for live collections. Please use a schema object instead.",
|
|
47
|
+
importerFilename
|
|
48
|
+
)
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
return config;
|
|
52
|
+
}
|
|
53
|
+
function defineCollection(config) {
|
|
54
|
+
const importerFilename = getImporterFilename();
|
|
55
|
+
if (importerFilename?.includes("live.config")) {
|
|
56
|
+
throw new AstroError({
|
|
57
|
+
...AstroErrorData.LiveContentConfigError,
|
|
58
|
+
message: AstroErrorData.LiveContentConfigError.message(
|
|
59
|
+
"Collections in a live config file must use `defineLiveCollection`.",
|
|
60
|
+
importerFilename
|
|
61
|
+
)
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
if ("loader" in config) {
|
|
65
|
+
if (config.type && config.type !== CONTENT_LAYER_TYPE) {
|
|
66
|
+
throw new AstroUserError(
|
|
67
|
+
`Collections that use the Content Layer API must have a \`loader\` defined and no \`type\` set. Check your collection definitions in ${importerFilename ?? "your content config file"}.`
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
config.type = CONTENT_LAYER_TYPE;
|
|
71
|
+
}
|
|
72
|
+
if (!config.type) config.type = "content";
|
|
73
|
+
return config;
|
|
74
|
+
}
|
|
75
|
+
export {
|
|
76
|
+
defineCollection,
|
|
77
|
+
defineLiveCollection
|
|
78
|
+
};
|
package/dist/content/consts.d.ts
CHANGED
|
@@ -24,3 +24,4 @@ export declare const MODULES_IMPORTS_FILE = "content-modules.mjs";
|
|
|
24
24
|
export declare const COLLECTIONS_MANIFEST_FILE = "collections/collections.json";
|
|
25
25
|
export declare const COLLECTIONS_DIR = "collections/";
|
|
26
26
|
export declare const CONTENT_LAYER_TYPE = "content_layer";
|
|
27
|
+
export declare const LIVE_CONTENT_TYPE = "live";
|
package/dist/content/consts.js
CHANGED
|
@@ -31,6 +31,7 @@ const MODULES_IMPORTS_FILE = "content-modules.mjs";
|
|
|
31
31
|
const COLLECTIONS_MANIFEST_FILE = "collections/collections.json";
|
|
32
32
|
const COLLECTIONS_DIR = "collections/";
|
|
33
33
|
const CONTENT_LAYER_TYPE = "content_layer";
|
|
34
|
+
const LIVE_CONTENT_TYPE = "live";
|
|
34
35
|
export {
|
|
35
36
|
ASSET_IMPORTS_FILE,
|
|
36
37
|
ASSET_IMPORTS_RESOLVED_STUB_ID,
|
|
@@ -50,6 +51,7 @@ export {
|
|
|
50
51
|
DEFERRED_MODULE,
|
|
51
52
|
IMAGE_IMPORT_PREFIX,
|
|
52
53
|
LINKS_PLACEHOLDER,
|
|
54
|
+
LIVE_CONTENT_TYPE,
|
|
53
55
|
MODULES_IMPORTS_FILE,
|
|
54
56
|
MODULES_MJS_ID,
|
|
55
57
|
MODULES_MJS_VIRTUAL_ID,
|
|
@@ -164,7 +164,7 @@ ${contentConfig.error.message}`);
|
|
|
164
164
|
logger.info("Content config changed");
|
|
165
165
|
shouldClear = true;
|
|
166
166
|
}
|
|
167
|
-
if (previousAstroVersion && previousAstroVersion !== "5.
|
|
167
|
+
if (previousAstroVersion && previousAstroVersion !== "5.10.0") {
|
|
168
168
|
logger.info("Astro version changed");
|
|
169
169
|
shouldClear = true;
|
|
170
170
|
}
|
|
@@ -172,8 +172,8 @@ ${contentConfig.error.message}`);
|
|
|
172
172
|
logger.info("Clearing content store");
|
|
173
173
|
this.#store.clearAll();
|
|
174
174
|
}
|
|
175
|
-
if ("5.
|
|
176
|
-
await this.#store.metaStore().set("astro-version", "5.
|
|
175
|
+
if ("5.10.0") {
|
|
176
|
+
await this.#store.metaStore().set("astro-version", "5.10.0");
|
|
177
177
|
}
|
|
178
178
|
if (currentConfigDigest) {
|
|
179
179
|
await this.#store.metaStore().set("content-config-digest", currentConfigDigest);
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { ZodError } from 'zod';
|
|
2
|
+
export declare class LiveCollectionError extends Error {
|
|
3
|
+
readonly collection: string;
|
|
4
|
+
readonly message: string;
|
|
5
|
+
readonly cause?: Error | undefined;
|
|
6
|
+
constructor(collection: string, message: string, cause?: Error | undefined);
|
|
7
|
+
static is(error: unknown): error is LiveCollectionError;
|
|
8
|
+
}
|
|
9
|
+
export declare class LiveEntryNotFoundError extends LiveCollectionError {
|
|
10
|
+
constructor(collection: string, entryFilter: string | Record<string, unknown>);
|
|
11
|
+
static is(error: unknown): error is LiveEntryNotFoundError;
|
|
12
|
+
}
|
|
13
|
+
export declare class LiveCollectionValidationError extends LiveCollectionError {
|
|
14
|
+
constructor(collection: string, entryId: string, error: ZodError);
|
|
15
|
+
static is(error: unknown): error is LiveCollectionValidationError;
|
|
16
|
+
}
|
|
17
|
+
export declare class LiveCollectionCacheHintError extends LiveCollectionError {
|
|
18
|
+
constructor(collection: string, entryId: string | undefined, error: ZodError);
|
|
19
|
+
static is(error: unknown): error is LiveCollectionCacheHintError;
|
|
20
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
class LiveCollectionError extends Error {
|
|
2
|
+
constructor(collection, message, cause) {
|
|
3
|
+
super(message);
|
|
4
|
+
this.collection = collection;
|
|
5
|
+
this.message = message;
|
|
6
|
+
this.cause = cause;
|
|
7
|
+
this.name = "LiveCollectionError";
|
|
8
|
+
}
|
|
9
|
+
static is(error) {
|
|
10
|
+
return error instanceof LiveCollectionError;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
class LiveEntryNotFoundError extends LiveCollectionError {
|
|
14
|
+
constructor(collection, entryFilter) {
|
|
15
|
+
super(
|
|
16
|
+
collection,
|
|
17
|
+
`Entry ${collection} \u2192 ${typeof entryFilter === "string" ? entryFilter : JSON.stringify(entryFilter)} was not found.`
|
|
18
|
+
);
|
|
19
|
+
this.name = "LiveEntryNotFoundError";
|
|
20
|
+
}
|
|
21
|
+
static is(error) {
|
|
22
|
+
return error?.name === "LiveEntryNotFoundError";
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
class LiveCollectionValidationError extends LiveCollectionError {
|
|
26
|
+
constructor(collection, entryId, error) {
|
|
27
|
+
super(
|
|
28
|
+
collection,
|
|
29
|
+
[
|
|
30
|
+
`**${collection} \u2192 ${entryId}** data does not match the collection schema.
|
|
31
|
+
`,
|
|
32
|
+
...error.errors.map((zodError) => ` **${zodError.path.join(".")}**: ${zodError.message}`),
|
|
33
|
+
""
|
|
34
|
+
].join("\n")
|
|
35
|
+
);
|
|
36
|
+
this.name = "LiveCollectionValidationError";
|
|
37
|
+
}
|
|
38
|
+
static is(error) {
|
|
39
|
+
return error?.name === "LiveCollectionValidationError";
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
class LiveCollectionCacheHintError extends LiveCollectionError {
|
|
43
|
+
constructor(collection, entryId, error) {
|
|
44
|
+
super(
|
|
45
|
+
collection,
|
|
46
|
+
[
|
|
47
|
+
`**${String(collection)}${entryId ? ` \u2192 ${String(entryId)}` : ""}** returned an invalid cache hint.
|
|
48
|
+
`,
|
|
49
|
+
...error.errors.map((zodError) => ` **${zodError.path.join(".")}**: ${zodError.message}`),
|
|
50
|
+
""
|
|
51
|
+
].join("\n")
|
|
52
|
+
);
|
|
53
|
+
this.name = "LiveCollectionCacheHintError";
|
|
54
|
+
}
|
|
55
|
+
static is(error) {
|
|
56
|
+
return error?.name === "LiveCollectionCacheHintError";
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
export {
|
|
60
|
+
LiveCollectionCacheHintError,
|
|
61
|
+
LiveCollectionError,
|
|
62
|
+
LiveCollectionValidationError,
|
|
63
|
+
LiveEntryNotFoundError
|
|
64
|
+
};
|
|
@@ -2,6 +2,7 @@ import type { FSWatcher } from 'vite';
|
|
|
2
2
|
import type { ZodSchema } from 'zod';
|
|
3
3
|
import type { AstroIntegrationLogger } from '../../core/logger/core.js';
|
|
4
4
|
import type { AstroConfig } from '../../types/public/config.js';
|
|
5
|
+
import type { LiveDataCollection, LiveDataEntry } from '../../types/public/content.js';
|
|
5
6
|
import type { RenderedContent } from '../data-store.js';
|
|
6
7
|
import type { DataStore, MetaStore } from '../mutable-data-store.js';
|
|
7
8
|
export type { DataStore, MetaStore };
|
|
@@ -42,3 +43,23 @@ export interface Loader {
|
|
|
42
43
|
/** Optionally, define the schema of the data. Will be overridden by user-defined schema */
|
|
43
44
|
schema?: ZodSchema | Promise<ZodSchema> | (() => ZodSchema | Promise<ZodSchema>);
|
|
44
45
|
}
|
|
46
|
+
export interface LoadEntryContext<TEntryFilter = never> {
|
|
47
|
+
filter: TEntryFilter extends never ? {
|
|
48
|
+
id: string;
|
|
49
|
+
} : TEntryFilter;
|
|
50
|
+
}
|
|
51
|
+
export interface LoadCollectionContext<TCollectionFilter = unknown> {
|
|
52
|
+
filter?: TCollectionFilter;
|
|
53
|
+
}
|
|
54
|
+
export interface LiveLoader<TData extends Record<string, any> = Record<string, unknown>, TEntryFilter extends Record<string, any> | never = never, TCollectionFilter extends Record<string, any> | never = never, TError extends Error = Error> {
|
|
55
|
+
/** Unique name of the loader, e.g. the npm package name */
|
|
56
|
+
name: string;
|
|
57
|
+
/** Load a single entry */
|
|
58
|
+
loadEntry: (context: LoadEntryContext<TEntryFilter>) => Promise<LiveDataEntry<TData> | undefined | {
|
|
59
|
+
error: TError;
|
|
60
|
+
}>;
|
|
61
|
+
/** Load a collection of entries */
|
|
62
|
+
loadCollection: (context: LoadCollectionContext<TCollectionFilter>) => Promise<LiveDataCollection<TData> | {
|
|
63
|
+
error: TError;
|
|
64
|
+
}>;
|
|
65
|
+
}
|
|
@@ -1,24 +1,33 @@
|
|
|
1
1
|
import type { MarkdownHeading } from '@astrojs/markdown-remark';
|
|
2
2
|
import { z } from 'zod';
|
|
3
3
|
import { type AstroComponentFactory } from '../runtime/server/index.js';
|
|
4
|
+
import type { LiveDataCollectionResult, LiveDataEntryResult } from '../types/public/content.js';
|
|
5
|
+
import { type LIVE_CONTENT_TYPE } from './consts.js';
|
|
4
6
|
import { type DataEntry } from './data-store.js';
|
|
7
|
+
import { LiveCollectionCacheHintError, LiveCollectionError, LiveCollectionValidationError, LiveEntryNotFoundError } from './loaders/errors.js';
|
|
8
|
+
import type { LiveLoader } from './loaders/types.js';
|
|
5
9
|
import type { ContentLookupMap } from './utils.js';
|
|
10
|
+
export { LiveCollectionError, LiveCollectionCacheHintError, LiveEntryNotFoundError, LiveCollectionValidationError, };
|
|
6
11
|
type LazyImport = () => Promise<any>;
|
|
7
12
|
type GlobResult = Record<string, LazyImport>;
|
|
8
13
|
type CollectionToEntryMap = Record<string, GlobResult>;
|
|
9
14
|
type GetEntryImport = (collection: string, lookupId: string) => Promise<LazyImport>;
|
|
10
|
-
|
|
11
|
-
|
|
15
|
+
type LiveCollectionConfigMap = Record<string, {
|
|
16
|
+
loader: LiveLoader;
|
|
17
|
+
type: typeof LIVE_CONTENT_TYPE;
|
|
18
|
+
schema?: z.ZodType;
|
|
19
|
+
}>;
|
|
12
20
|
export declare function createCollectionToGlobResultMap({ globResult, contentDir, }: {
|
|
13
21
|
globResult: GlobResult;
|
|
14
22
|
contentDir: string;
|
|
15
23
|
}): CollectionToEntryMap;
|
|
16
|
-
export declare function createGetCollection({ contentCollectionToEntryMap, dataCollectionToEntryMap, getRenderEntryImport, cacheEntriesByCollection, }: {
|
|
24
|
+
export declare function createGetCollection({ contentCollectionToEntryMap, dataCollectionToEntryMap, getRenderEntryImport, cacheEntriesByCollection, liveCollections, }: {
|
|
17
25
|
contentCollectionToEntryMap: CollectionToEntryMap;
|
|
18
26
|
dataCollectionToEntryMap: CollectionToEntryMap;
|
|
19
27
|
getRenderEntryImport: GetEntryImport;
|
|
20
28
|
cacheEntriesByCollection: Map<string, any[]>;
|
|
21
|
-
|
|
29
|
+
liveCollections: LiveCollectionConfigMap;
|
|
30
|
+
}): (collection: string, filter?: ((entry: any) => unknown) | Record<string, unknown>) => Promise<any[]>;
|
|
22
31
|
export declare function createGetEntryBySlug({ getEntryImport, getRenderEntryImport, collectionNames, getEntry, }: {
|
|
23
32
|
getEntryImport: GetEntryImport;
|
|
24
33
|
getRenderEntryImport: GetEntryImport;
|
|
@@ -61,11 +70,12 @@ type EntryLookupObject = {
|
|
|
61
70
|
collection: string;
|
|
62
71
|
slug: string;
|
|
63
72
|
};
|
|
64
|
-
export declare function createGetEntry({ getEntryImport, getRenderEntryImport, collectionNames, }: {
|
|
73
|
+
export declare function createGetEntry({ getEntryImport, getRenderEntryImport, collectionNames, liveCollections, }: {
|
|
65
74
|
getEntryImport: GetEntryImport;
|
|
66
75
|
getRenderEntryImport: GetEntryImport;
|
|
67
76
|
collectionNames: Set<string>;
|
|
68
|
-
|
|
77
|
+
liveCollections: LiveCollectionConfigMap;
|
|
78
|
+
}): (collectionOrLookupObject: string | EntryLookupObject, lookup?: string | Record<string, unknown>) => Promise<ContentEntryResult | DataEntryResult | undefined>;
|
|
69
79
|
export declare function createGetEntries(getEntry: ReturnType<typeof createGetEntry>): (entries: {
|
|
70
80
|
collection: string;
|
|
71
81
|
id: string;
|
|
@@ -73,6 +83,12 @@ export declare function createGetEntries(getEntry: ReturnType<typeof createGetEn
|
|
|
73
83
|
collection: string;
|
|
74
84
|
slug: string;
|
|
75
85
|
}[]) => Promise<(ContentEntryResult | DataEntryResult | undefined)[]>;
|
|
86
|
+
export declare function createGetLiveCollection({ liveCollections, }: {
|
|
87
|
+
liveCollections: LiveCollectionConfigMap;
|
|
88
|
+
}): (collection: string, filter?: Record<string, unknown>) => Promise<LiveDataCollectionResult>;
|
|
89
|
+
export declare function createGetLiveEntry({ liveCollections, }: {
|
|
90
|
+
liveCollections: LiveCollectionConfigMap;
|
|
91
|
+
}): (collection: string, lookup: string | Record<string, unknown>) => Promise<LiveDataEntryResult>;
|
|
76
92
|
type RenderResult = {
|
|
77
93
|
Content: AstroComponentFactory;
|
|
78
94
|
headings: MarkdownHeading[];
|
|
@@ -122,4 +138,4 @@ export declare function createReference({ lookupMap }: {
|
|
|
122
138
|
slug: string;
|
|
123
139
|
collection: string;
|
|
124
140
|
}>;
|
|
125
|
-
export
|
|
141
|
+
export declare function defineCollection(config: any): import("./config.js").CollectionConfig<import("./config.js").BaseSchema>;
|