refactor: move more logic to content-internals

This commit is contained in:
bholmesdev 2022-10-24 11:13:30 -04:00
parent a2218fa905
commit 47645d8dfd
3 changed files with 42 additions and 41 deletions

View file

@ -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]][]>;

View file

@ -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
}; };

View file

@ -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.`),
};