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:
parent
4f649014e9
commit
be901dc98c
6 changed files with 61 additions and 22 deletions
24
.changeset/neat-eagles-trade.md
Normal file
24
.changeset/neat-eagles-trade.md
Normal 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! -->
|
||||||
|
```
|
|
@ -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,
|
||||||
|
|
16
packages/astro/src/content/template/types.d.ts
vendored
16
packages/astro/src/content/template/types.d.ts
vendored
|
@ -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']
|
||||||
|
|
|
@ -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,
|
||||||
});
|
});
|
||||||
|
|
|
@ -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({
|
||||||
|
|
|
@ -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}),
|
||||||
|
|
Loading…
Reference in a new issue