Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions packages/astro/src/content/runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,11 @@ export function createCollectionToGlobResultMap({
return collectionToGlobResultMap;
}

const cacheHintSchema = z.object({
tags: z.array(z.string()).optional(),
maxAge: z.number().optional(),
});

async function parseLiveEntry(
entry: LiveDataEntry,
schema: z.ZodType,
Expand All @@ -137,6 +142,21 @@ async function parseLiveEntry(
),
});
}
if (entry.cacheHint) {
const cacheHint = cacheHintSchema.safeParse(entry.cacheHint);

if (!cacheHint.success) {
throw new AstroError({
...AstroErrorData.InvalidCacheHintError,
message: AstroErrorData.InvalidCacheHintError.message(
collection,
entry.id,
cacheHint.error,
),
});
}
entry.cacheHint = cacheHint.data;
}
return {
...entry,
data: parsed.data,
Expand Down Expand Up @@ -184,6 +204,22 @@ export function createGetCollection({
);
}

if (response.cacheHint) {
const cacheHint = cacheHintSchema.safeParse(response.cacheHint);

if (!cacheHint.success) {
throw new AstroError({
...AstroErrorData.InvalidCacheHintError,
message: AstroErrorData.InvalidCacheHintError.message(
collection,
undefined,
cacheHint.error,
),
});
}
response.cacheHint = cacheHint.data;
}

return {
...response,
collection,
Expand Down
35 changes: 31 additions & 4 deletions packages/astro/src/core/errors/errors-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1614,8 +1614,9 @@ export const InvalidContentEntryDataError = {
title: 'Content entry data does not match schema.',
message(collection: string, entryId: string, error: ZodError) {
return [
`**${String(collection)}${String(entryId)}** data does not match collection schema.`,
...error.errors.map((zodError) => zodError.message),
`**${String(collection)}${String(entryId)}** data does not match collection schema.\n`,
...error.errors.map((zodError) => ` **${zodError.path.join('.')}**: ${zodError.message}`),
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When adding the new error I noticed that these were missing the path, which could cause unhelpful errors

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 things:

  1. Why do you need String(...) when the type is already string?
  2. I think the formatting of the error should happen where the throw happens rather than inside the error data, see https://github.com/withastro/astro/blob/main/packages/astro/src/core/errors/README.md#shape

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you provide two screenshots: one before and after the change?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a very old error - I just added the zodError.path.join('.') bit. I followed the same pattern for the one I added. You're right the string cast isn't needed, but I think that standardising the formatting of the zod error does make sense

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Before:
image

After:
image

See the field name title has been added

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah sorry I didn't notice it was an old one

'',
].join('\n');
},
hint: 'See https://docs.astro.build/en/guides/content-collections/ for more information on content schemas.',
Expand Down Expand Up @@ -1665,13 +1666,39 @@ export const ContentEntryDataError = {
title: 'Content entry data does not match schema.',
message(collection: string, entryId: string, error: ZodError) {
return [
`**${String(collection)}${String(entryId)}** data does not match collection schema.`,
...error.errors.map((zodError) => zodError.message),
`**${String(collection)}${String(entryId)}** data does not match collection schema.\n`,
...error.errors.map((zodError) => ` **${zodError.path.join('.')}**: ${zodError.message}`),
'',
].join('\n');
},
hint: 'See https://docs.astro.build/en/guides/content-collections/ for more information on content schemas.',
} satisfies ErrorData;

/**
* @docs
* @message
* **Example error message:**<br/>
* **blog** → **post** returned an invalid cache hint.<br/>
* **maxAge**: Expected number, received string
* @description
* The loader for a live content collection returned an invalid cache hint.
* Make sure that `cacheHint` is an object with the correct properties, or is undefined.
* @see
* - [Experimental live content](https://astro.build/en/reference/experimental-flags/live-content/)
*/
export const InvalidCacheHintError = {
name: 'InvalidCacheHintError',
title: 'Invalid cache hint.',
message(collection: string, entryId: string | undefined, error: ZodError) {
return [
`**${String(collection)}${entryId ? ` → ${String(entryId)}` : ''}** returned an invalid cache hint.\n`,
...error.errors.map((zodError) => ` **${zodError.path.join('.')}**: ${zodError.message}`),
'',
].join('\n');
},
hint: 'See https://docs.astro.build/en/reference/experimental-flags/live-content/ for more information.',
} satisfies ErrorData;

/**
* @docs
* @message
Expand Down
12 changes: 11 additions & 1 deletion packages/astro/src/types/public/content.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,16 +125,26 @@ export interface DataEntryType {

export type GetDataEntryInfoReturnType = { data: Record<string, unknown>; rawData?: string };

export interface CacheHint {
/** Cache tags */
tags?: Array<string>;
/** Maximum age of the response in seconds */
maxAge?: number;
}

export interface LiveDataEntry<TData extends Record<string, unknown> = Record<string, unknown>> {
/** The ID of the entry. Unique per collection. */
id: string;
/** The parsed entry data */
data: TData;
/** A hint for how to cache this entry */
cacheHint?: CacheHint;
}

export interface LiveDataCollection<
TData extends Record<string, unknown> = Record<string, unknown>,
> {
entries: Array<LiveDataEntry<TData>>;
// TODO: pagination etc.
/** A hint for how to cache this collection. Individual entries can also have cache hints */
cacheHint?: CacheHint;
}
15 changes: 14 additions & 1 deletion packages/astro/test/fixtures/live-loaders/src/live.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,24 @@ const entries = {
const loader: LiveLoader<Entry, { id: keyof typeof entries }> = {
name: 'test-loader',
loadEntry: async (context) => {
return entries[context.filter.id] || null;
if(!entries[context.filter.id]) {
return;
}
return {
...entries[context.filter.id],
cacheHint: {
tags: [`page:${context.filter.id}`],
maxAge: 60,
},
};
},
loadCollection: async (context) => {
return {
entries: Object.values(entries),
cacheHint: {
tags: ['page'],
maxAge: 60,
},
};
},
};
Expand Down
Loading