From b1fd89affb4f4e317a2170e7fde59c1f60c6c278 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Mon, 2 Sep 2024 21:51:51 +0200 Subject: [PATCH 1/2] feat(core): #268 add a function to access sibling documents Extend the context collection with a documents function, which returns all documents of the current collection. Closes: #268 --- .changeset/good-pandas-deny.md | 5 ++++ docs/transform.mdx | 25 ++++++++++++++++++ packages/core/src/__tests__/config.005.ts | 23 ++++++++++++++++ packages/core/src/config.ts | 5 ++-- packages/core/src/transformer.test.ts | 32 +++++++++++++++++++++++ packages/core/src/transformer.ts | 9 ++++--- packages/core/src/types.ts | 5 ---- 7 files changed, 94 insertions(+), 10 deletions(-) create mode 100644 .changeset/good-pandas-deny.md create mode 100644 packages/core/src/__tests__/config.005.ts 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..d6eca75d 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; From 639c2eaceee26a7c37ec1920972a60d6128bce6e Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Mon, 2 Sep 2024 21:55:57 +0200 Subject: [PATCH 2/2] fix(core): type check of markdown and mdx package --- packages/core/src/config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/src/config.ts b/packages/core/src/config.ts index d6eca75d..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>;