refactor: replace duplicate parseSlug

This commit is contained in:
bholmesdev 2023-04-27 12:22:36 -04:00
parent 45404cfd8b
commit 7f77913b9f
3 changed files with 57 additions and 32 deletions

View file

@ -12,16 +12,15 @@ import { CONTENT_TYPES_FILE, VIRTUAL_MODULE_ID } from './consts.js';
import { import {
getContentPaths, getContentPaths,
getEntryInfo, getEntryInfo,
getEntrySlug,
getEntryType, getEntryType,
loadContentConfig, loadContentConfig,
NoCollectionError, NoCollectionError,
parseFrontmatter,
type ContentConfig, type ContentConfig,
type ContentObservable, type ContentObservable,
type ContentPaths, type ContentPaths,
type EntryInfo, type EntryInfo,
getContentEntryConfigByExtMap, getContentEntryConfigByExtMap,
getEntrySlug,
} from './utils.js'; } from './utils.js';
type ChokidarEvent = 'add' | 'addDir' | 'change' | 'unlink' | 'unlinkDir'; type ChokidarEvent = 'add' | 'addDir' | 'change' | 'unlink' | 'unlinkDir';
@ -187,14 +186,23 @@ export async function createContentTypesGenerator({
return { shouldGenerateTypes: false }; 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 collectionKey = JSON.stringify(collection);
const entryKey = JSON.stringify(id); const entryKey = JSON.stringify(id);
switch (event.name) { switch (event.name) {
case 'add': 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)) { if (!(collectionKey in contentTypes)) {
addCollection(contentTypes, collectionKey); addCollection(contentTypes, collectionKey);
} }
@ -210,7 +218,14 @@ export async function createContentTypesGenerator({
case 'change': case 'change':
// User may modify `slug` in their frontmatter. // User may modify `slug` in their frontmatter.
// Only regen types if this change is detected. // 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) { if (contentTypes[collectionKey]?.[entryKey]?.slug !== changedSlug) {
setEntry(contentTypes, collectionKey, entryKey, changedSlug); setEntry(contentTypes, collectionKey, entryKey, changedSlug);
return { shouldGenerateTypes: true }; return { shouldGenerateTypes: true };
@ -309,25 +324,6 @@ function removeCollection(contentMap: ContentTypes, collectionKey: string) {
delete contentMap[collectionKey]; 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( function setEntry(
contentTypes: ContentTypes, contentTypes: ContentTypes,
collectionKey: string, collectionKey: string,

View file

@ -52,7 +52,7 @@ export const msg = {
`${collection} does not have a config. We suggest adding one for type safety!`, `${collection} does not have a config. We suggest adding one for type safety!`,
}; };
export function getEntrySlug({ export function parseEntrySlug({
id, id,
collection, collection,
generatedSlug, generatedSlug,
@ -422,16 +422,19 @@ export async function getStringifiedLookupMap({
contentGlob.map(async (filePath) => { contentGlob.map(async (filePath) => {
const info = getEntryInfo({ contentDir, entry: filePath }); const info = getEntryInfo({ contentDir, entry: filePath });
if (info instanceof NoCollectionError) return; if (info instanceof NoCollectionError) return;
const contentEntryConfig = contentEntryConfigByExt.get(extname(filePath)); const contentEntryType = contentEntryConfigByExt.get(extname(filePath));
if (!contentEntryConfig) return; if (!contentEntryType) return;
const { id, collection, slug: generatedSlug } = info; const { id, collection, slug: generatedSlug } = info;
filePathByLookupId[collection] ??= {}; filePathByLookupId[collection] ??= {};
const { slug: frontmatterSlug } = await contentEntryConfig.getEntryInfo({ const slug = await getEntrySlug({
id,
collection,
generatedSlug,
fs,
fileUrl: pathToFileURL(filePath), 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); filePathByLookupId[collection][slug] = rootRelativePath(root, filePath);
}) })
); );
@ -439,6 +442,32 @@ export async function getStringifiedLookupMap({
return JSON.stringify(filePathByLookupId); 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<ContentEntryType, 'getEntryInfo'>;
}) {
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[]) { export function getExtGlob(exts: string[]) {
return exts.length === 1 return exts.length === 1
? // Wrapping {...} breaks when there is only one extension ? // Wrapping {...} breaks when there is only one extension

View file

@ -14,7 +14,7 @@ import {
getContentPaths, getContentPaths,
getEntryData, getEntryData,
getEntryInfo, getEntryInfo,
getEntrySlug, parseEntrySlug,
getEntryType, getEntryType,
globalContentConfigObserver, globalContentConfigObserver,
NoCollectionError, NoCollectionError,
@ -221,7 +221,7 @@ export function astroContentImportPlugin({
const _internal = { filePath: fileId, rawData: rawData }; const _internal = { filePath: fileId, rawData: rawData };
// TODO: move slug calculation to the start of the build // TODO: move slug calculation to the start of the build
// to generate a performant lookup map for `getEntryBySlug` // to generate a performant lookup map for `getEntryBySlug`
const slug = getEntrySlug({ const slug = parseEntrySlug({
id, id,
collection, collection,
generatedSlug, generatedSlug,