Skip to content

Commit

Permalink
fix: use slug for content references
Browse files Browse the repository at this point in the history
  • Loading branch information
bholmesdev committed Apr 28, 2023
1 parent 6e0ee39 commit 9cc4539
Show file tree
Hide file tree
Showing 4 changed files with 57 additions and 28 deletions.
23 changes: 13 additions & 10 deletions packages/astro/src/content/runtime.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { AstroError, AstroErrorData } from '../core/errors/index.js';
import { prependForwardSlash } from '../core/path.js';
import { ZodIssueCode, string as zodString, type z } from 'zod';

import {
createComponent,
createHeadAndContent,
Expand All @@ -12,12 +11,12 @@ import {
renderUniqueStylesheet,
unescapeHTML,
} from '../runtime/server/index.js';
import type { ContentLookupMap } from './utils.js';

type LazyImport = () => Promise<any>;
type GlobResult = Record<string, LazyImport>;
type CollectionToEntryMap = Record<string, GlobResult>;
type GetEntryImport = (collection: string, lookupId: string) => Promise<LazyImport>;
type LookupMap = { [collectionName: string]: { [lookupId: string]: string } };

export function createCollectionToGlobResultMap({
globResult,
Expand Down Expand Up @@ -285,33 +284,37 @@ async function render({
};
}

export function createReference({ lookupMap }: { lookupMap: LookupMap }) {
export function createReference({ lookupMap }: { lookupMap: ContentLookupMap }) {
return function reference(collection: string) {
return zodString().transform((id: string, ctx) => {
return zodString().transform((lookupId: string, ctx) => {
const flattenedErrorPath = ctx.path.join('.');
const lookupIds = lookupMap[collection];
if (!lookupIds) {
if (!lookupMap[collection]) {
ctx.addIssue({
code: ZodIssueCode.custom,
message: `**${flattenedErrorPath}:** Reference to ${collection} invalid. Collection does not exist or is empty.`,
});
return;
}

const entry = lookupIds[id];
const { type, entries } = lookupMap[collection];
const entry = entries[lookupId];

if (!entry) {
ctx.addIssue({
code: ZodIssueCode.custom,
message: `**${flattenedErrorPath}**: Reference to ${collection} invalid. Expected ${Object.keys(
lookupIds
entries
)
.map((c) => JSON.stringify(c))
.join(' | ')}. Received ${JSON.stringify(id)}.`,
.join(' | ')}. Received ${JSON.stringify(lookupId)}.`,
});
return;
}
return { id, collection };
// Content is still indentified by slugs, so map to a `slug` key for consistency.
if (type === 'content') {
return { slug: lookupId, collection };
}
return { id: lookupId, collection };
});
};
}
42 changes: 33 additions & 9 deletions packages/astro/src/content/template/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,6 @@ declare module 'astro:content' {
type ValidContentEntrySlug<C extends keyof ContentEntryMap> = AllValuesOf<
ContentEntryMap[C]
>['slug'];
type ValidDataEntryId<C extends keyof DataEntryMap> = keyof DataEntryMap[C];

export function getEntryBySlug<
C extends keyof ContentEntryMap,
Expand Down Expand Up @@ -116,7 +115,7 @@ declare module 'astro:content' {
E extends keyof ValidContentEntrySlug<C> | (string & {})
>(params: {
collection: C;
id: E;
slug: E;
}): E extends keyof ValidContentEntrySlug<C>
? Promise<CollectionEntry<C>>
: Promise<CollectionEntry<C> | undefined>;
Expand All @@ -129,18 +128,43 @@ declare module 'astro:content' {
}): E extends keyof DataEntryMap[C]
? Promise<DataEntryMap[C][E]>
: Promise<CollectionEntry<C> | undefined>;
export function getEntry<
C extends keyof ContentEntryMap,
E extends keyof ValidContentEntrySlug<C> | (string & {})
>(
collection: C,
slug: E
): E extends keyof ValidContentEntrySlug<C>
? Promise<CollectionEntry<C>>
: Promise<CollectionEntry<C> | undefined>;
export function getEntry<
C extends keyof DataEntryMap,
E extends keyof DataEntryMap[C] | (string & {})
>(
collection: C,
id: E
): E extends keyof DataEntryMap[C]
? Promise<DataEntryMap[C][E]>
: Promise<CollectionEntry<C> | undefined>;

export function reference<C extends keyof AnyEntryMap | (string & {})>(
collection: C
): import('astro/zod').ZodEffects<
import('astro/zod').ZodString,
{
collection: C;
// Allow generic `string` type to avoid type errors
// in the config before `astro sync` is run.
// Invalid collection names will be caught at build time.
id: C extends keyof AnyEntryMap ? keyof AnyEntryMap[C] : string;
}
C extends keyof ContentEntryMap
? {
collection: C;
slug: ValidContentEntrySlug<C>;
}
: C extends keyof DataEntryMap
? {
collection: C;
id: keyof DataEntryMap[C];
}
: // Allow generic `string` type to avoid type errors
// in the config before `astro sync` is run.
// Invalid collection names will be caught at build time.
{ collection: string; id: string }
>;

type ReturnTypeOrOriginal<T> = T extends (...args: any[]) => infer R ? R : T;
Expand Down
2 changes: 1 addition & 1 deletion packages/astro/src/content/template/virtual-mod.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ let lookupMap = {};

function createGlobLookup(glob) {
return async (collection, lookupId) => {
const filePath = lookupMap[collection]?.[lookupId];
const filePath = lookupMap[collection]?.entries[lookupId];

if (!filePath) return undefined;
return glob[collection][filePath];
Expand Down
18 changes: 10 additions & 8 deletions packages/astro/src/content/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -491,6 +491,10 @@ function search(fs: typeof fsMod, srcDir: URL) {
return { exists: false, url: paths[0] };
}

export type ContentLookupMap = {
[collectionName: string]: { type: 'content' | 'data'; entries: { [lookupId: string]: string } };
};

export async function getStringifiedLookupMap({
contentPaths,
contentEntryConfigByExt,
Expand All @@ -514,9 +518,7 @@ export async function getStringifiedLookupMap({
},
};

let filePathByLookupId: {
[collection: string]: Record<string, string>;
} = {};
let lookupMap: ContentLookupMap = {};
const relContentDir = rootRelativePath(root, contentDir, false);
const contentGlob = await glob(
`${relContentDir}**/*${getExtGlob([...contentEntryConfigByExt.keys()])}`,
Expand All @@ -538,7 +540,7 @@ export async function getStringifiedLookupMap({
contentDir,
collection,
});
filePathByLookupId[collection] ??= {};
lookupMap[collection] ??= { type: 'content', entries: {} };
const slug = await getEntrySlug({
id,
collection,
Expand All @@ -547,7 +549,7 @@ export async function getStringifiedLookupMap({
fileUrl: pathToFileURL(filePath),
contentEntryType,
});
filePathByLookupId[collection][slug] = rootRelativePath(root, filePath);
lookupMap[collection].entries[slug] = rootRelativePath(root, filePath);
})
);

Expand All @@ -566,12 +568,12 @@ export async function getStringifiedLookupMap({
dataDir,
collection,
});
filePathByLookupId[collection] ??= {};
filePathByLookupId[collection][id] = rootRelativePath(root, filePath);
lookupMap[collection] ??= { type: 'data', entries: {} };
lookupMap[collection].entries[id] = rootRelativePath(root, filePath);
})
);

return JSON.stringify(filePathByLookupId);
return JSON.stringify(lookupMap);
}

/**
Expand Down

0 comments on commit 9cc4539

Please sign in to comment.