refactor: move more logic to content-internals
This commit is contained in:
parent
a2218fa905
commit
47645d8dfd
3 changed files with 42 additions and 41 deletions
|
@ -20,5 +20,5 @@ export declare function fetchContent<
|
||||||
E extends keyof typeof contentMap[C]
|
E extends keyof typeof contentMap[C]
|
||||||
>(
|
>(
|
||||||
collection: C,
|
collection: C,
|
||||||
filter?: (data: typeof contentMap[C][E]['data']) => boolean
|
filter?: (data: typeof contentMap[C][E]) => boolean
|
||||||
): Promise<typeof contentMap[C][keyof typeof contentMap[C]][]>;
|
): Promise<typeof contentMap[C][keyof typeof contentMap[C]][]>;
|
||||||
|
|
|
@ -1,50 +1,14 @@
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
import { getFrontmatterErrorLine, errorMap } from 'astro/content-internals';
|
import { getErrorMsg, parseEntryData } from 'astro/content-internals';
|
||||||
|
|
||||||
const NO_SCHEMA_MSG = (/** @type {string} */ collection) =>
|
|
||||||
`${collection} does not have a ~schema file. We suggest adding one for type safety!`;
|
|
||||||
|
|
||||||
const defaultSchemaFileResolved = { schema: { parse: (mod) => mod } };
|
const defaultSchemaFileResolved = { schema: { parse: (mod) => mod } };
|
||||||
/** Used to stub out `schemaMap` entries that don't have a `~schema.ts` file */
|
/** Used to stub out `schemaMap` entries that don't have a `~schema.ts` file */
|
||||||
const defaultSchemaFile = (/** @type {string} */ collection) =>
|
const defaultSchemaFile = (/** @type {string} */ collection) =>
|
||||||
new Promise((/** @type {(value: typeof defaultSchemaFileResolved) => void} */ resolve) => {
|
new Promise((/** @type {(value: typeof defaultSchemaFileResolved) => void} */ resolve) => {
|
||||||
console.warn(NO_SCHEMA_MSG(collection));
|
console.warn(getErrorMsg.schemaMissing(collection));
|
||||||
resolve(defaultSchemaFileResolved);
|
resolve(defaultSchemaFileResolved);
|
||||||
});
|
});
|
||||||
|
|
||||||
const getSchemaError = (collection) =>
|
|
||||||
new Error(`${collection}/~schema needs a named \`schema\` export.`);
|
|
||||||
|
|
||||||
async function parseEntryData(
|
|
||||||
/** @type {string} */ collection,
|
|
||||||
/** @type {string} */ entryKey,
|
|
||||||
/** @type {{ data: any; rawData: string; }} */ unparsedEntry,
|
|
||||||
/** @type {{ schemaMap: any }} */ { schemaMap }
|
|
||||||
) {
|
|
||||||
const defineSchemaResult = await schemaMap[collection];
|
|
||||||
if (!defineSchemaResult) throw getSchemaError(collection);
|
|
||||||
const { schema } = defineSchemaResult;
|
|
||||||
|
|
||||||
try {
|
|
||||||
return schema.parse(unparsedEntry.data, { errorMap });
|
|
||||||
} catch (e) {
|
|
||||||
if (e instanceof z.ZodError) {
|
|
||||||
const formattedError = new Error(
|
|
||||||
[
|
|
||||||
`Could not parse frontmatter in ${String(collection)} → ${String(entryKey)}`,
|
|
||||||
...e.errors.map((e) => e.message),
|
|
||||||
].join('\n')
|
|
||||||
);
|
|
||||||
formattedError.loc = {
|
|
||||||
file: 'TODO',
|
|
||||||
line: getFrontmatterErrorLine(unparsedEntry.rawData, String(e.errors[0].path[0])),
|
|
||||||
column: 1,
|
|
||||||
};
|
|
||||||
throw formattedError;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const contentMap = {
|
export const contentMap = {
|
||||||
// GENERATED_CONTENT_MAP_ENTRIES
|
// GENERATED_CONTENT_MAP_ENTRIES
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,8 +1,38 @@
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
export async function parseEntryData(
|
||||||
|
collection: string,
|
||||||
|
entryKey: string,
|
||||||
|
unparsedEntry: { data: any; rawData: string },
|
||||||
|
{ schemaMap }: { schemaMap: Record<string, any> }
|
||||||
|
) {
|
||||||
|
const schemaImport = (await schemaMap[collection]) ?? {};
|
||||||
|
if (!('schema' in schemaImport)) throw getErrorMsg.schemaNamedExp(collection);
|
||||||
|
const { schema } = schemaImport;
|
||||||
|
|
||||||
|
try {
|
||||||
|
return schema.parse(unparsedEntry.data, { errorMap });
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof z.ZodError) {
|
||||||
|
const formattedError = new Error(
|
||||||
|
[
|
||||||
|
`Could not parse frontmatter in ${String(collection)} → ${String(entryKey)}`,
|
||||||
|
...e.errors.map((e) => e.message),
|
||||||
|
].join('\n')
|
||||||
|
);
|
||||||
|
(formattedError as any).loc = {
|
||||||
|
file: 'TODO',
|
||||||
|
line: getFrontmatterErrorLine(unparsedEntry.rawData, String(e.errors[0].path[0])),
|
||||||
|
column: 1,
|
||||||
|
};
|
||||||
|
throw formattedError;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const flattenPath = (path: (string | number)[]) => path.join('.');
|
const flattenPath = (path: (string | number)[]) => path.join('.');
|
||||||
|
|
||||||
export const errorMap: z.ZodErrorMap = (error, ctx) => {
|
const errorMap: z.ZodErrorMap = (error, ctx) => {
|
||||||
if (error.code === 'invalid_type') {
|
if (error.code === 'invalid_type') {
|
||||||
const badKeyPath = JSON.stringify(flattenPath(error.path));
|
const badKeyPath = JSON.stringify(flattenPath(error.path));
|
||||||
if (error.received === 'undefined') {
|
if (error.received === 'undefined') {
|
||||||
|
@ -15,7 +45,7 @@ export const errorMap: z.ZodErrorMap = (error, ctx) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
// WARNING: MAXIMUM JANK AHEAD
|
// WARNING: MAXIMUM JANK AHEAD
|
||||||
export function getFrontmatterErrorLine(rawFrontmatter: string, frontmatterKey: string) {
|
function getFrontmatterErrorLine(rawFrontmatter: string, frontmatterKey: string) {
|
||||||
console.log({ rawFrontmatter, frontmatterKey });
|
console.log({ rawFrontmatter, frontmatterKey });
|
||||||
const indexOfFrontmatterKey = rawFrontmatter.indexOf(`\n${frontmatterKey}`);
|
const indexOfFrontmatterKey = rawFrontmatter.indexOf(`\n${frontmatterKey}`);
|
||||||
if (indexOfFrontmatterKey === -1) return 0;
|
if (indexOfFrontmatterKey === -1) return 0;
|
||||||
|
@ -24,3 +54,10 @@ export function getFrontmatterErrorLine(rawFrontmatter: string, frontmatterKey:
|
||||||
const numNewlinesBeforeKey = frontmatterBeforeKey.split('\n').length;
|
const numNewlinesBeforeKey = frontmatterBeforeKey.split('\n').length;
|
||||||
return numNewlinesBeforeKey;
|
return numNewlinesBeforeKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const getErrorMsg = {
|
||||||
|
schemaMissing: (collection: string) =>
|
||||||
|
`${collection} does not have a ~schema file. We suggest adding one for type safety!`,
|
||||||
|
schemaNamedExp: (collection: string) =>
|
||||||
|
new Error(`${collection}/~schema needs a named \`schema\` export.`),
|
||||||
|
};
|
||||||
|
|
Loading…
Reference in a new issue