Create a Comark Content Parser

Turn new file types into documents with content.addParser().

By default the Content instance only parses Markdown. But content.addParser() has been designed to handle any extension.

The built-in json and yaml plugins are thin wrappers over this API.

content.addParser(extensions, parse)

Registers a Parser that runs every time the Content instance sees a file with one of the given extensions.

Parameters:

  • extensions: an array of dotted file extensions, e.g. ['.csv'].
  • parse: a Parser callback that turns a raw file into a ParserResult.

Returns: void


Parser

The callback content.addParser runs: (ctx: ParserContext) => ParserResult | null. It receives a ParserContext describing the file and returns a ParserResult, or null to skip the file.

Parameters:

FieldTypeDescription
read() => string | Promise<string>Fetch the raw file contents.
extensionstringThe file's extension, e.g. .csv.
filepathstringThe path of the file being parsed.
partialbooleantrue on lightweight scans (manifest build): parse only metadata and skip the body.

Returns: a ParserResult (or null to skip). Every field is optional.

FieldTypeDescription
kind'document' | 'media'How to track the file. Defaults to 'document'.
typestringThe file's MIME type.
dataContentFile['data']The parsed data (frontmatter for documents).
metaRecord<string, any>Extra metadata merged onto the entry.
nodesContentFile['nodes']The parsed AST, empty for data-only files.
partialbooleanWhether only metadata was parsed. Defaults to the context's partial.
To trim which data fields listings keep, pair the parser with content.addListingFields().

Usage

Write a parser plugin, register it on comarkContent(), then read the new files like any other document.

Define the parser

Call content.addParser from the plugin's setup. This CSV parser reads the raw file and returns each row as data, with empty nodes:

csv-plugin.ts
import { defineContentPlugin } from 'comark-content'

export default defineContentPlugin(() => ({
  setup(content) {
    content.addParser(['.csv'], async ({ read }) => {
      const body = await read()
      const [header, ...rows] = body.trim().split('\n').map(line => line.split(','))
      const data = rows.map(row => Object.fromEntries(header!.map((h, i) => [h, row[i]])))
      return { kind: 'document', data: { rows: data }, meta: {}, nodes: [] }
    })
  },
}))

Register the parser

content.ts
import { comarkContent } from 'comark-content'
import fs from 'comark-content/sources/fs'
import csv from './csv-plugin'

const content = comarkContent({ source: fs('./content'), plugins: [csv()] })

Read the result

read.ts
const team = await content.get('/teams/roster')
team?.data.rows   // parsed CSV rows, queryable and listable
Return data with empty nodes for any data-only format (TOML, CSV, INI): entries stay queryable and listable but carry no rendered body