0
Fork 0
mirror of https://github.com/withastro/astro.git synced 2024-12-23 21:53:55 -05:00

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>
This commit is contained in:
Matthew Phillips 2023-01-19 08:34:27 -05:00 committed by GitHub
parent 4f649014e9
commit be901dc98c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 61 additions and 22 deletions

View file

@ -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! -->
```

View file

@ -69,18 +69,30 @@ export function createGetCollection({
}; };
} }
export function createGetEntry({ export function createGetEntryBySlug({
collectionToEntryMap, getCollection,
collectionToRenderEntryMap, collectionToRenderEntryMap,
}: { }: {
collectionToEntryMap: CollectionToEntryMap; getCollection: ReturnType<typeof createGetCollection>;
collectionToRenderEntryMap: CollectionToEntryMap; collectionToRenderEntryMap: CollectionToEntryMap;
}) { }) {
return async function getEntry(collection: string, entryId: string) { return async function getEntryBySlug(collection: string, slug: string) {
const lazyImport = collectionToEntryMap[collection]?.[entryId]; // This is not an optimized lookup. Should look into an O(1) implementation
if (!lazyImport) throw new Error(`Failed to import ${JSON.stringify(entryId)}.`); // 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;
}
}
const entry = await lazyImport(); if(typeof candidate === 'undefined') {
return undefined;
}
const entry = candidate;
return { return {
id: entry.id, id: entry.id,
slug: entry.slug, slug: entry.slug,

View file

@ -30,17 +30,21 @@ declare module 'astro:content' {
input: BaseCollectionConfig<S> input: BaseCollectionConfig<S>
): 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, collection: C,
entryKey: E // Note that this has to accept a regular string too, for SSR
): Promise<(typeof entryMap)[C][E] & Render>; entrySlug: E
): E extends ValidEntrySlug<C> ? Promise<CollectionEntry<C>> : Promise<CollectionEntry<C> | undefined>;
export function getCollection< export function getCollection<
C extends keyof typeof entryMap, C extends keyof typeof entryMap,
E extends keyof (typeof entryMap)[C]
>( >(
collection: C, collection: C,
filter?: (data: (typeof entryMap)[C][E]) => boolean filter?: (data: CollectionEntry<C>) => boolean
): Promise<((typeof entryMap)[C][E] & Render)[]>; ): Promise<CollectionEntry<C>[]>;
type InferEntrySchema<C extends keyof typeof entryMap> = import('astro/zod').infer< type InferEntrySchema<C extends keyof typeof entryMap> = import('astro/zod').infer<
Required<ContentConfig['collections'][C]>['schema'] Required<ContentConfig['collections'][C]>['schema']

View file

@ -2,7 +2,7 @@
import { import {
createCollectionToGlobResultMap, createCollectionToGlobResultMap,
createGetCollection, createGetCollection,
createGetEntry, createGetEntryBySlug,
} from 'astro/content/internal'; } from 'astro/content/internal';
export { z } from 'astro/zod'; export { z } from 'astro/zod';
@ -34,7 +34,7 @@ export const getCollection = createGetCollection({
collectionToRenderEntryMap, collectionToRenderEntryMap,
}); });
export const getEntry = createGetEntry({ export const getEntryBySlug = createGetEntryBySlug({
collectionToEntryMap, getCollection,
collectionToRenderEntryMap, collectionToRenderEntryMap,
}); });

View file

@ -2,7 +2,6 @@ import { z, defineCollection } from 'astro:content';
const withSlugConfig = defineCollection({ const withSlugConfig = defineCollection({
slug({ id, data }) { slug({ id, data }) {
console.log({id, data})
return `${data.prefix}-${id}`; return `${data.prefix}-${id}`;
}, },
schema: z.object({ schema: z.object({

View file

@ -1,12 +1,12 @@
import { getEntry } from 'astro:content'; import { getEntryBySlug } from 'astro:content';
import * as devalue from 'devalue'; import * as devalue from 'devalue';
import { stripRenderFn } from '../utils.js'; import { stripRenderFn } from '../utils.js';
export async function get() { export async function get() {
const columbiaWithoutConfig = stripRenderFn(await getEntry('without-config', 'columbia.md')); const columbiaWithoutConfig = stripRenderFn(await getEntryBySlug('without-config', 'columbia'));
const oneWithSchemaConfig = stripRenderFn(await getEntry('with-schema-config', 'one.md')); const oneWithSchemaConfig = stripRenderFn(await getEntryBySlug('with-schema-config', 'one'));
const twoWithSlugConfig = stripRenderFn(await getEntry('with-slug-config', 'two.md')); const twoWithSlugConfig = stripRenderFn(await getEntryBySlug('with-slug-config', 'interesting-two.md'));
const postWithUnionSchema = stripRenderFn(await getEntry('with-union-schema', 'post.md')); const postWithUnionSchema = stripRenderFn(await getEntryBySlug('with-union-schema', 'post'));
return { return {
body: devalue.stringify({columbiaWithoutConfig, oneWithSchemaConfig, twoWithSlugConfig, postWithUnionSchema}), body: devalue.stringify({columbiaWithoutConfig, oneWithSchemaConfig, twoWithSlugConfig, postWithUnionSchema}),