From 97a8448c8cf124398b05e0aa58403d26dab451d8 Mon Sep 17 00:00:00 2001 From: Stinobe Date: Thu, 14 May 2026 23:11:07 +0200 Subject: [PATCH 1/5] Update readme Add a note that `zod` is not required to use the library --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index b5b78b4..ea5f4c4 100644 --- a/README.md +++ b/README.md @@ -56,6 +56,9 @@ try { ``` Visit the website for more information about [Zod](https://zod.dev/) +> [!NOTE] +> Zod is not required to use this library + ## Options ### `excerpt` _(optional)_ From 007838781ee430f3500646a9e7dd7b669ed92a81 Mon Sep 17 00:00:00 2001 From: Stinobe Date: Thu, 14 May 2026 23:11:16 +0200 Subject: [PATCH 2/5] Add tags for NPM --- package.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/package.json b/package.json index e8edf0d..0b1cf81 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,9 @@ "markdown", "mdx", "md", + "parser", + "typescript", + "zod", "parser" ], "publishConfig": { From 9ffa418fec4fc138e3b457fb3b49aeacd3c3478b Mon Sep 17 00:00:00 2001 From: Stinobe Date: Fri, 15 May 2026 16:19:00 +0200 Subject: [PATCH 3/5] Improve documentation --- README.md | 136 ++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 90 insertions(+), 46 deletions(-) diff --git a/README.md b/README.md index ea5f4c4..ddc3e82 100644 --- a/README.md +++ b/README.md @@ -1,39 +1,48 @@ # Mattr -A tiny, type-safe Markdown frontmatter parser for modern TypeScript projects. Built for workflows where good typing, predictable APIs, and small utilities go a long way. This package __focuses on the essentials:__ clean ergonomics, minimal overhead, and a developer experience that integrates naturally into content-focused applications and tooling. +A tiny, type-safe Markdown frontmatter parser for modern TypeScript projects. Built for workflows where good typing, predictable APIs, and small utilities go a long way. This package **focuses on the essentials:** clean ergonomics, minimal overhead, and a developer experience that integrates naturally into content-focused applications and tooling. + +![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/stinobe/mattr/validation.yml?label=tests) +![NPM Version](https://img.shields.io/npm/v/%40stinobe%2Fmattr) ## Usage + ### Without type safety ```typescript import { mattr } from "@stinobe/mattr"; try { - const parsed = mattr(fileContents); -} catch(error) { - // ... + const parsed = mattr(fileContents); +} catch (error) { + // ... } ``` -___ + +--- + ### With TypeScript ```typescript import { mattr } from "@stinobe/mattr"; type Schema = { - title: string; - description?: string; -} + title: string; + description?: string; +}; try { - const parsed = mattr(fileContents); -} catch(error) { - // ... + const parsed = mattr(fileContents); +} catch (error) { + // ... } ``` + > [!IMPORTANT] -> This only provides type safety during compiling, __not__ during runtime -___ +> This only provides type safety during compiling, **not** during runtime + +--- + ### With Zod ```typescript @@ -41,27 +50,39 @@ import { mattr } from "@stinobe/mattr"; import { z } from "zod"; const Schema = z.object({ - title: z.string(), - description: z.optional(z.string()) -}) + title: z.string(), + description: z.optional(z.string()), +}); try { - // You can specify the type as well - // but that's not really necessary - // since it will be inferred from the schema option - const parsed = mattr(fileContents, { schema: Schema }); -} catch(error) { - // ... + // You can specify the type as well + // but that's not really necessary + // since it will be inferred from the schema option + const parsed = mattr(fileContents, { schema: Schema }); +} catch (error) { + // ... } -``` +``` + Visit the website for more information about [Zod](https://zod.dev/) > [!NOTE] > Zod is not required to use this library +### What is returned + +No _mattr_ which option you're choosing, the output of the `mattr` function is always the same. +| Property | Description | +|---|---| +| `data` | The frontmatter as JSON ouput with given type _(default: `Record`)_ | +| `content` | The markdown content itself without the frontmatter | +| `excerpt` | The excerpt according to the settings as `string`, or `null` if no excerpt was found | +| `raw` | The file contents as they were passed to the mattr function | + ## Options ### `excerpt` _(optional)_ + Type: `boolean | ExcerptFunction` Default: `true` @@ -72,6 +93,7 @@ Enables and configures excerpt generation. - `ExcerptFunction` - custom extraction logic [More info](#create-a-custom-excerpt-function) #### Default strategy + When enabled (`true`), the excerpt is resovled using following rules 1. If `excerptSeparator` is defined, takes content until seperator, otherwise passes entire content to next step @@ -79,74 +101,96 @@ When enabled (`true`), the excerpt is resovled using following rules 3. If none of the above is defined, take first paragraph ### `excerptSeparator` _(optional)_ + Type: `string` Default: `undefined` When defined with a non-empty string, takes content from input file after frontmatter untill separator ### `excerptLength` _(optional)_ + Type: `number` Default: `undefined` Maximum character length of the excerpt. When less than or equal to `0` this will throw an `InvalidExcerptError`. ### `schema` _(optional)_ + Type: `ZodType` Default: `undefined` Takes a Zod-schema to validate frontmatter output against ## Customization + ### Create a custom excerpt function -I've added and exported a type in case you want to create a custom excerpt function. This contains some data passed on from options by the `mattr` function. -| `ExcerptFn` param | Description | -|---|---| -|`ctx.raw` | file contents | -|`ctx.content` | file content without frontmatter | -|`options.length` | `excerptLength` passed to `mattr` | -|`options.separator` | `excerptSeparator` passed to `mattr` | +I've added and exported a type in case you want to create a custom excerpt function. This contains some data passed on from options by the `mattr` function. + +| `ExcerptFn` param | Description | +| ------------------- | ------------------------------------ | +| `ctx.raw` | file contents | +| `ctx.content` | file content without frontmatter | +| `options.length` | `excerptLength` passed to `mattr` | +| `options.separator` | `excerptSeparator` passed to `mattr` | ```typescript import type { MattrExcerptFn } from "@stinobe/mattr"; const myCustomExcerptFunction: MattrExcerptFn = (ctx, excerptOptions) => { - // Do some magic - return "a string"; -} + // Do some magic + return "a string"; +}; ``` + ### Use in a wrapper function + You can also create a wrapper function where one of your parameters is an options object. For example if you would have a function traversing over files ```typescript -import type { MattrOptions, MattrFile, MattrAllowedTypes } from "@stinobe/mattr"; - -const myGlobFunction = async (globPath: string, options: MattrOptions): MattrFile[] => { - const posts: MattrFile[] = []; - for await (const post of glob(globPath)) { - try { - const parsed = mattr(post, options); - posts.push(parsed); - } catch { - // Handle errors thrown - } +import type { + MattrOptions, + MattrFile, + MattrAllowedTypes, +} from "@stinobe/mattr"; + +const myGlobFunction = async ( + globPath: string, + options: MattrOptions, +): MattrFile[] => { + const posts: MattrFile[] = []; + for await (const post of glob(globPath)) { + try { + const parsed = mattr(post, options); + posts.push(parsed); + } catch { + // Handle errors thrown } - return posts; -} + } + return posts; +}; ``` + ## Erorr handling + In some situations an erorr will be thrown so don't forget to do the handling correct ### MatterParseError + Thrown when: + - A Yaml block in a markdownfile is not closed - The `parse` function from the `yaml` package has thrown an error. _The error thrown by the `parse` function is passed as cause._ ### MattrSchemaError + Thrown when: + - unsuccesful parse of the Zod schema, issues form `safeParse`are passed to the error. ### MattrExcerptError + Thrown when: + - `excerptLength` is smaller than or equal to `0` From a496698318e29e9df6c3cf12d611c3cc573a791f Mon Sep 17 00:00:00 2001 From: Stinobe Date: Fri, 15 May 2026 16:26:20 +0200 Subject: [PATCH 4/5] Add issue templates --- .github/ISSUE_TEMPLATE/bug_report.yml | 45 ++++++++++++++++++++++ .github/ISSUE_TEMPLATE/documentation.yml | 18 +++++++++ .github/ISSUE_TEMPLATE/feature_request.yml | 26 +++++++++++++ 3 files changed, 89 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.yml create mode 100644 .github/ISSUE_TEMPLATE/documentation.yml create mode 100644 .github/ISSUE_TEMPLATE/feature_request.yml diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 0000000..77d0ebb --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,45 @@ +name: Bug report +description: Report a reproducible issue or unexpected behavior +title: "[Bug]: " +labels: + - bug + +body: + - type: textarea + id: description + attributes: + label: Description + description: What happened? + validations: + required: true + + - type: textarea + id: reproduction + attributes: + label: Reproduction + description: Provide a minimal reproduction if possible + placeholder: | + ```typescript + import { mattr } from "@stinobe/mattr" + ``` + validations: + required: true + + - type: textarea + id: expected + attributes: + label: Expected behavior + validations: + required: true + + - type: input + id: version + attributes: + label: Package version + placeholder: 0.1.0 + + - type: input + id: runtime + attributes: + label: Runtime + placeholder: Node.js 22 / Bun 1.x diff --git a/.github/ISSUE_TEMPLATE/documentation.yml b/.github/ISSUE_TEMPLATE/documentation.yml new file mode 100644 index 0000000..3617a35 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/documentation.yml @@ -0,0 +1,18 @@ +name: Documentation improvement +description: Suggest improvements or additions to the documentation +title: "[Docs]: " +labels: + - documentation + +body: + - type: textarea + id: issue + attributes: + label: What is unclear or missing? + validations: + required: true + + - type: textarea + id: suggestion + attributes: + label: Suggested improvement diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 0000000..3228948 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,26 @@ +name: Feature request +description: Suggest an idea or improvement +title: "[Feature]: " +labels: + - enhancement + +body: + - type: textarea + id: problem + attributes: + label: Problem + description: What problem does this solve? + validations: + required: true + + - type: textarea + id: proposal + attributes: + label: Proposed solution + validations: + required: true + + - type: textarea + id: alternatives + attributes: + label: Alternatives considered From e54592fd760887641651c90c37838e69c803e750 Mon Sep 17 00:00:00 2001 From: Stinobe Date: Fri, 15 May 2026 16:36:21 +0200 Subject: [PATCH 5/5] Update configuration for package size improvement --- package.json | 3 +++ tsup.config.ts | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 0b1cf81..7e4ad67 100644 --- a/package.json +++ b/package.json @@ -52,6 +52,9 @@ "license": "MIT", "packageManager": "pnpm", "type": "module", + "engines": { + "node": ">=18" + }, "dependencies": { "yaml": "^2.8.4" }, diff --git a/tsup.config.ts b/tsup.config.ts index 25f4642..0c98487 100644 --- a/tsup.config.ts +++ b/tsup.config.ts @@ -14,5 +14,5 @@ export default defineConfig({ treeshake: true, minify: true, target: "es2022", - external: ["zod"], + external: ["yaml", "zod"], });