From 7f77913b9fdf8c5a3b0a8cd4e0c81afb54430b67 Mon Sep 17 00:00:00 2001 From: bholmesdev Date: Thu, 27 Apr 2023 12:22:36 -0400 Subject: [PATCH] refactor: replace duplicate parseSlug --- packages/astro/src/content/types-generator.ts | 44 +++++++++---------- packages/astro/src/content/utils.ts | 41 ++++++++++++++--- .../content/vite-plugin-content-imports.ts | 4 +- 3 files changed, 57 insertions(+), 32 deletions(-) diff --git a/packages/astro/src/content/types-generator.ts b/packages/astro/src/content/types-generator.ts index da0505ca9..d63afd812 100644 --- a/packages/astro/src/content/types-generator.ts +++ b/packages/astro/src/content/types-generator.ts @@ -12,16 +12,15 @@ import { CONTENT_TYPES_FILE, VIRTUAL_MODULE_ID } from './consts.js'; import { getContentPaths, getEntryInfo, - getEntrySlug, getEntryType, loadContentConfig, NoCollectionError, - parseFrontmatter, type ContentConfig, type ContentObservable, type ContentPaths, type EntryInfo, getContentEntryConfigByExtMap, + getEntrySlug, } from './utils.js'; type ChokidarEvent = 'add' | 'addDir' | 'change' | 'unlink' | 'unlinkDir'; @@ -187,14 +186,23 @@ export async function createContentTypesGenerator({ return { shouldGenerateTypes: false }; } - const { id, collection } = entryInfo; + const { id, collection, slug: generatedSlug } = entryInfo; + const contentEntryType = contentEntryConfigByExt.get(path.extname(event.entry.pathname)); + if (!contentEntryType) return { shouldGenerateTypes: false }; const collectionKey = JSON.stringify(collection); const entryKey = JSON.stringify(id); switch (event.name) { case 'add': - const addedSlug = await parseSlug({ fs, event, entryInfo }); + const addedSlug = await getEntrySlug({ + generatedSlug, + id, + collection, + fileUrl: event.entry, + contentEntryType, + fs, + }); if (!(collectionKey in contentTypes)) { addCollection(contentTypes, collectionKey); } @@ -210,7 +218,14 @@ export async function createContentTypesGenerator({ case 'change': // User may modify `slug` in their frontmatter. // Only regen types if this change is detected. - const changedSlug = await parseSlug({ fs, event, entryInfo }); + const changedSlug = await getEntrySlug({ + generatedSlug, + id, + collection, + fileUrl: event.entry, + contentEntryType, + fs, + }); if (contentTypes[collectionKey]?.[entryKey]?.slug !== changedSlug) { setEntry(contentTypes, collectionKey, entryKey, changedSlug); return { shouldGenerateTypes: true }; @@ -309,25 +324,6 @@ function removeCollection(contentMap: ContentTypes, collectionKey: string) { delete contentMap[collectionKey]; } -async function parseSlug({ - fs, - event, - entryInfo: { id, collection, slug: generatedSlug }, -}: { - fs: typeof fsMod; - event: ContentEvent; - entryInfo: EntryInfo; -}) { - // `slug` may be present in entry frontmatter. - // This should be respected by the generated `slug` type! - // Parse frontmatter and retrieve `slug` value for this. - // Note: will raise any YAML exceptions and `slug` parse errors (i.e. `slug` is a boolean) - // on dev server startup or production build init. - const rawContents = await fs.promises.readFile(event.entry, 'utf-8'); - const { data: frontmatter } = parseFrontmatter(rawContents, fileURLToPath(event.entry)); - return getEntrySlug({ id, collection, generatedSlug, frontmatterSlug: frontmatter.slug }); -} - function setEntry( contentTypes: ContentTypes, collectionKey: string, diff --git a/packages/astro/src/content/utils.ts b/packages/astro/src/content/utils.ts index 2320084f7..2a9e41689 100644 --- a/packages/astro/src/content/utils.ts +++ b/packages/astro/src/content/utils.ts @@ -52,7 +52,7 @@ export const msg = { `${collection} does not have a config. We suggest adding one for type safety!`, }; -export function getEntrySlug({ +export function parseEntrySlug({ id, collection, generatedSlug, @@ -422,16 +422,19 @@ export async function getStringifiedLookupMap({ contentGlob.map(async (filePath) => { const info = getEntryInfo({ contentDir, entry: filePath }); if (info instanceof NoCollectionError) return; - const contentEntryConfig = contentEntryConfigByExt.get(extname(filePath)); - if (!contentEntryConfig) return; + const contentEntryType = contentEntryConfigByExt.get(extname(filePath)); + if (!contentEntryType) return; const { id, collection, slug: generatedSlug } = info; filePathByLookupId[collection] ??= {}; - const { slug: frontmatterSlug } = await contentEntryConfig.getEntryInfo({ + const slug = await getEntrySlug({ + id, + collection, + generatedSlug, + fs, fileUrl: pathToFileURL(filePath), - contents: await fs.promises.readFile(filePath, 'utf-8'), + contentEntryType, }); - const slug = getEntrySlug({ id, collection, generatedSlug, frontmatterSlug }); filePathByLookupId[collection][slug] = rootRelativePath(root, filePath); }) ); @@ -439,6 +442,32 @@ export async function getStringifiedLookupMap({ return JSON.stringify(filePathByLookupId); } +/** + * Check for slug in content entry frontmatter and validate the type, + * falling back to the `generatedSlug` if none is found. + */ +export async function getEntrySlug({ + id, + collection, + generatedSlug, + contentEntryType, + fileUrl, + fs, +}: { + fs: typeof fsMod; + id: string; + collection: string; + generatedSlug: string; + fileUrl: URL; + contentEntryType: Pick; +}) { + const { slug: frontmatterSlug } = await contentEntryType.getEntryInfo({ + fileUrl, + contents: await fs.promises.readFile(fileUrl, 'utf-8'), + }); + return parseEntrySlug({ generatedSlug, frontmatterSlug, id, collection }); +} + export function getExtGlob(exts: string[]) { return exts.length === 1 ? // Wrapping {...} breaks when there is only one extension diff --git a/packages/astro/src/content/vite-plugin-content-imports.ts b/packages/astro/src/content/vite-plugin-content-imports.ts index 75e63550e..18ba56b07 100644 --- a/packages/astro/src/content/vite-plugin-content-imports.ts +++ b/packages/astro/src/content/vite-plugin-content-imports.ts @@ -14,7 +14,7 @@ import { getContentPaths, getEntryData, getEntryInfo, - getEntrySlug, + parseEntrySlug, getEntryType, globalContentConfigObserver, NoCollectionError, @@ -221,7 +221,7 @@ export function astroContentImportPlugin({ const _internal = { filePath: fileId, rawData: rawData }; // TODO: move slug calculation to the start of the build // to generate a performant lookup map for `getEntryBySlug` - const slug = getEntrySlug({ + const slug = parseEntrySlug({ id, collection, generatedSlug,