diff --git a/.changeset/good-pandas-deny.md b/.changeset/good-pandas-deny.md new file mode 100644 index 00000000..3becbad5 --- /dev/null +++ b/.changeset/good-pandas-deny.md @@ -0,0 +1,5 @@ +--- +"@content-collections/core": minor +--- + +Add a function to access sibling documents during transformation diff --git a/docs/transform.mdx b/docs/transform.mdx index 1761567a..aa86a432 100644 --- a/docs/transform.mdx +++ b/docs/transform.mdx @@ -36,6 +36,27 @@ In this example, the `markdownToHtml` function is only called if the content has **Note**: Caching the compilation steps of `@content-collections/markdown` or `@content-collections/mdx` is unnecessary as they already utilize the same caching mechanism. +## Access sibling documents + +Since version 0.7.0, it is possible to access other documents of the same collection by using the `documents` function of the `collection` object, which is part of the `context` object. The function is asynchronous, requires no parameters, and returns an array of all documents of the collection. The documents are not transformed; they have the shape as defined in the schema of the collection. + +Example: + +```ts +const posts = defineCollection({ + // ... + transform: async (doc, {collection}) => { + const docs = await collection.documents(); + const idx = docs.findIndex(d => doc._meta.filePath === d._meta.filePath); + return { + ...doc, + prev: idx > 0 ? docs[idx - 1] : null, + next: idx < docs.length - 1 ? docs[idx + 1] : null, + }; + }, +}); +``` + ## Access other collections The `transform` function can access other collections using the `documents` function of the `context` object. The function requires a collection reference as parameter and returns an array of documents for that collection. But keep in mind the returned document are not transformed, they have the shape as defined in the schema of the referenced collection. @@ -66,6 +87,10 @@ const posts = defineCollection({ For a complete example have a look at the [Join collections](#join-collections) example. + + It is not possible to access documents of the same collection with the `documents` function. Use the `collection.documents` function instead. Please refer to [Access sibling documents](#access-sibling-documents) for more information. + + ## Examples Here are some common use cases of the `transform` function: diff --git a/packages/core/src/__tests__/config.005.ts b/packages/core/src/__tests__/config.005.ts new file mode 100644 index 00000000..cb236900 --- /dev/null +++ b/packages/core/src/__tests__/config.005.ts @@ -0,0 +1,23 @@ +import { defineCollection, defineConfig } from "@content-collections/core"; + +const posts = defineCollection({ + name: "posts", + directory: "sources/posts", + include: "**/*.md(x)?", + schema: (z) => ({ + title: z.string(), + }), + transform: async (doc, {collection}) => { + const docs = await collection.documents(); + const idx = docs.findIndex(d => doc._meta.filePath === d._meta.filePath); + return { + ...doc, + prev: idx > 0 ? docs[idx - 1] : null, + next: idx < docs.length - 1 ? docs[idx + 1] : null, + }; + }, +}); + +export default defineConfig({ + collections: [posts], +}); diff --git a/packages/core/src/config.ts b/packages/core/src/config.ts index 367205d5..a893988d 100644 --- a/packages/core/src/config.ts +++ b/packages/core/src/config.ts @@ -46,7 +46,7 @@ export type Schema< _meta: Meta; }; -export type Context = { +export type Context = { documents( collection: TCollection ): Array>; @@ -54,6 +54,7 @@ export type Context = { collection: { name: string; directory: string; + documents: () => Promise>; }; }; @@ -71,7 +72,7 @@ export type CollectionRequest< parser?: TParser; typeName?: string; schema: (z: Z) => TShape; - transform?: (data: TSchema, context: Context) => TTransformResult; + transform?: (data: TSchema, context: Context) => TTransformResult; directory: string; include: string | string[]; exclude?: string | string[]; diff --git a/packages/core/src/transformer.test.ts b/packages/core/src/transformer.test.ts index a690fdaf..93f09cd2 100644 --- a/packages/core/src/transformer.test.ts +++ b/packages/core/src/transformer.test.ts @@ -659,4 +659,36 @@ describe("transform", () => { expect(collection?.documents[0].document.collectionDirectory).toBe("tests"); }); + + it("should access documents of the same collection", async () => { + const posts = defineCollection({ + name: "posts", + schema: (z) => ({ + name: z.string(), + }), + directory: "tests", + include: "*.md", + transform: async (doc, context) => { + const docs = await context.collection.documents(); + return { + ...doc, + docs + }; + }, + }); + + const [collection] = await createTransformer( + emitter, + noopCacheManager + )([ + { + ...posts, + files: [sampleOne, sampleTwo], + }, + ]); + + expect(collection?.documents[0].document.docs).toHaveLength(2); + expect(collection?.documents[0].document.docs[0].name).toBe("One"); + expect(collection?.documents[0].document.docs[1].name).toBe("Two"); + }); }); diff --git a/packages/core/src/transformer.ts b/packages/core/src/transformer.ts index 02c8dc1c..fd778ffa 100644 --- a/packages/core/src/transformer.ts +++ b/packages/core/src/transformer.ts @@ -1,4 +1,4 @@ -import { CollectionFile, MakeRequired } from "./types"; +import { CollectionFile } from "./types"; import { AnyCollection, Context } from "./config"; import { isDefined } from "./utils"; import { Emitter } from "./events"; @@ -129,7 +129,7 @@ export function createTransformer( collections: Array, collection: TransformedCollection, cache: Cache - ): Context { + ): Context { return { documents: (collection) => { const resolved = collections.find((c) => c.name === collection.name); @@ -144,6 +144,9 @@ export function createTransformer( collection: { name: collection.name, directory: collection.directory, + documents: async () => { + return collection.documents.map((doc) => doc.document); + }, }, cache: cache.cacheFn, }; @@ -152,7 +155,7 @@ export function createTransformer( async function transformDocument( collections: Array, collection: TransformedCollection, - transform: (data: any, context: Context) => any, + transform: (data: any, context: Context) => any, doc: any ) { const cache = cacheManager.cache(collection.name, doc.document._meta.path); diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index fac88386..3021dbb7 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -45,8 +45,3 @@ export type GetTypeByName< TName extends keyof CollectionByName, TCollection = CollectionByName[TName], > = TCollection extends AnyCollection ? GetDocument : never; - - -export type MakeRequired = { - [P in K]-?: T[P]; -} & Omit;