diff options
author | 2023-01-19 08:34:27 -0500 | |
---|---|---|
committer | 2023-01-19 08:34:27 -0500 | |
commit | be901dc98c4a7f6b5536540aa8f7ba5108e939a0 (patch) | |
tree | 70a472215ee49ef4ac63cef2b776818d007df10d | |
parent | 4f649014e9323db30d82e36e73f8e9ac1cf13cfa (diff) | |
download | astro-be901dc98c4a7f6b5536540aa8f7ba5108e939a0.tar.gz astro-be901dc98c4a7f6b5536540aa8f7ba5108e939a0.tar.zst astro-be901dc98c4a7f6b5536540aa8f7ba5108e939a0.zip |
Rename getEntry to getEntryBySlug (#5893)
* Rename getEntry to getEntryBySchema
* Improve entrySlug types and return undefined
* Add changeset
* Update packages/astro/src/content/template/types.d.ts
Co-authored-by: Ben Holmes <hey@bholmes.dev>
* Update the types to accept both raw string and known value
* Add comment on the implementation not currently being O(1)
Co-authored-by: Ben Holmes <hey@bholmes.dev>
6 files changed, 61 insertions, 22 deletions
diff --git a/.changeset/neat-eagles-trade.md b/.changeset/neat-eagles-trade.md new file mode 100644 index 000000000..b92353e8e --- /dev/null +++ b/.changeset/neat-eagles-trade.md @@ -0,0 +1,24 @@ +--- +'astro': major +--- + +Move getEntry to getEntryBySlug + +This change moves `getEntry` to `getEntryBySlug` and accepts a slug rather than an id. + +In order to improve support in `[id].astro` routes, particularly in SSR where you do not know what the id of a collection is. Using `getEntryBySlug` instead allows you to map the `[id]` param in your route to the entry. You can use it like this: + +```astro +--- +import { getEntryBySlug } from 'astro:content'; + +const entry = await getEntryBySlug('docs', Astro.params.id); + +if(!entry) { + return new Response(null, { + status: 404 + }); +} +--- +<!-- You have an entry! Use it! --> +``` diff --git a/packages/astro/src/content/internal.ts b/packages/astro/src/content/internal.ts index d3af7f8f9..550f6665a 100644 --- a/packages/astro/src/content/internal.ts +++ b/packages/astro/src/content/internal.ts @@ -69,18 +69,30 @@ export function createGetCollection({ }; } -export function createGetEntry({ - collectionToEntryMap, +export function createGetEntryBySlug({ + getCollection, collectionToRenderEntryMap, }: { - collectionToEntryMap: CollectionToEntryMap; + getCollection: ReturnType<typeof createGetCollection>; collectionToRenderEntryMap: CollectionToEntryMap; }) { - return async function getEntry(collection: string, entryId: string) { - const lazyImport = collectionToEntryMap[collection]?.[entryId]; - if (!lazyImport) throw new Error(`Failed to import ${JSON.stringify(entryId)}.`); + return async function getEntryBySlug(collection: string, slug: string) { + // This is not an optimized lookup. Should look into an O(1) implementation + // as it's probably that people will have very large collections. + const entries = await getCollection(collection); + let candidate: typeof entries[number] | undefined = undefined; + for(let entry of entries) { + if(entry.slug === slug) { + candidate = entry; + break; + } + } + + if(typeof candidate === 'undefined') { + return undefined; + } - const entry = await lazyImport(); + const entry = candidate; return { id: entry.id, slug: entry.slug, diff --git a/packages/astro/src/content/template/types.d.ts b/packages/astro/src/content/template/types.d.ts index 73d6c21ab..5b4dc874b 100644 --- a/packages/astro/src/content/template/types.d.ts +++ b/packages/astro/src/content/template/types.d.ts @@ -30,17 +30,21 @@ declare module 'astro:content' { input: BaseCollectionConfig<S> ): BaseCollectionConfig<S>; - export function getEntry<C extends keyof typeof entryMap, E extends keyof (typeof entryMap)[C]>( + type EntryMapKeys = keyof typeof entryMap; + type AllValuesOf<T> = T extends any ? T[keyof T] : never; + type ValidEntrySlug<C extends EntryMapKeys> = AllValuesOf<typeof entryMap[C]>['slug']; + + export function getEntryBySlug<C extends keyof typeof entryMap, E extends ValidEntrySlug<C> | (string & {})>( collection: C, - entryKey: E - ): Promise<(typeof entryMap)[C][E] & Render>; + // Note that this has to accept a regular string too, for SSR + entrySlug: E + ): E extends ValidEntrySlug<C> ? Promise<CollectionEntry<C>> : Promise<CollectionEntry<C> | undefined>; export function getCollection< C extends keyof typeof entryMap, - E extends keyof (typeof entryMap)[C] >( collection: C, - filter?: (data: (typeof entryMap)[C][E]) => boolean - ): Promise<((typeof entryMap)[C][E] & Render)[]>; + filter?: (data: CollectionEntry<C>) => boolean + ): Promise<CollectionEntry<C>[]>; type InferEntrySchema<C extends keyof typeof entryMap> = import('astro/zod').infer< Required<ContentConfig['collections'][C]>['schema'] diff --git a/packages/astro/src/content/template/virtual-mod.mjs b/packages/astro/src/content/template/virtual-mod.mjs index 799e780b4..49e01ea02 100644 --- a/packages/astro/src/content/template/virtual-mod.mjs +++ b/packages/astro/src/content/template/virtual-mod.mjs @@ -2,7 +2,7 @@ import { createCollectionToGlobResultMap, createGetCollection, - createGetEntry, + createGetEntryBySlug, } from 'astro/content/internal'; export { z } from 'astro/zod'; @@ -34,7 +34,7 @@ export const getCollection = createGetCollection({ collectionToRenderEntryMap, }); -export const getEntry = createGetEntry({ - collectionToEntryMap, +export const getEntryBySlug = createGetEntryBySlug({ + getCollection, collectionToRenderEntryMap, }); diff --git a/packages/astro/test/fixtures/content-collections/src/content/config.ts b/packages/astro/test/fixtures/content-collections/src/content/config.ts index f3b4e921a..dee35967c 100644 --- a/packages/astro/test/fixtures/content-collections/src/content/config.ts +++ b/packages/astro/test/fixtures/content-collections/src/content/config.ts @@ -2,7 +2,6 @@ import { z, defineCollection } from 'astro:content'; const withSlugConfig = defineCollection({ slug({ id, data }) { - console.log({id, data}) return `${data.prefix}-${id}`; }, schema: z.object({ diff --git a/packages/astro/test/fixtures/content-collections/src/pages/entries.json.js b/packages/astro/test/fixtures/content-collections/src/pages/entries.json.js index 7c9d8f1d4..05fb1187b 100644 --- a/packages/astro/test/fixtures/content-collections/src/pages/entries.json.js +++ b/packages/astro/test/fixtures/content-collections/src/pages/entries.json.js @@ -1,12 +1,12 @@ -import { getEntry } from 'astro:content'; +import { getEntryBySlug } from 'astro:content'; import * as devalue from 'devalue'; import { stripRenderFn } from '../utils.js'; export async function get() { - const columbiaWithoutConfig = stripRenderFn(await getEntry('without-config', 'columbia.md')); - const oneWithSchemaConfig = stripRenderFn(await getEntry('with-schema-config', 'one.md')); - const twoWithSlugConfig = stripRenderFn(await getEntry('with-slug-config', 'two.md')); - const postWithUnionSchema = stripRenderFn(await getEntry('with-union-schema', 'post.md')); + const columbiaWithoutConfig = stripRenderFn(await getEntryBySlug('without-config', 'columbia')); + const oneWithSchemaConfig = stripRenderFn(await getEntryBySlug('with-schema-config', 'one')); + const twoWithSlugConfig = stripRenderFn(await getEntryBySlug('with-slug-config', 'interesting-two.md')); + const postWithUnionSchema = stripRenderFn(await getEntryBySlug('with-union-schema', 'post')); return { body: devalue.stringify({columbiaWithoutConfig, oneWithSchemaConfig, twoWithSlugConfig, postWithUnionSchema}), |