Schema Validation Plugin
When a source declares a schema, the schema-validation plugin checks every Markdown, JSON, and YAML document on the file:parsed hook — including content.update(). How failures are handled depends on mode.
Register the plugin explicitly. Skip validation for whole sources or individual paths with the ignore option.
Usage
import { comarkContent, type JsonSchema } from 'comark-content'
import fs from 'comark-content/sources/fs'
import json from 'comark-content/plugins/json'
import yaml from 'comark-content/plugins/yaml'
import schemaValidation from 'comark-content/plugins/schema-validation'
const schema: JsonSchema = {
type: 'object',
properties: {
title: { type: 'string' },
author: { type: 'string' },
tags: { type: 'array', items: { type: 'string' } },
seo: {
type: 'object',
properties: { title: { type: 'string' } },
required: ['title'],
},
},
required: ['title', 'author'],
}
const content = comarkContent({
source: fs('./content', { schema }),
plugins: [json(), yaml(), schemaValidation()],
})Given a content folder with valid and invalid files:
---
title: Valid post
author: Ada Lovelace
---
# Valid post{
"title": "Site settings",
"author": "Ada Lovelace"
}---
title: Missing author
---
# This file is excluded — `author` is required{
"title": 42,
"author": "Ada Lovelace"
}title: Incomplete SEO
author: Ada Lovelace
seo: {}await content.init()
content.stat('/valid-post') // loaded
content.stat('/valid-settings') // loaded
content.stat('/missing-author') // undefined — rejected
content.stat('/bad-types') // undefined — rejected
content.stat('/incomplete-seo') // undefined — rejectedInvalid documents never enter the manifest when mode is ignore. With mode: 'error', load stops on the first invalid file.
See the Vite + Vue schema validation example for a runnable demo.
API
schemaValidation(options?)
Returns a ContentPlugin that hooks file:parsed.
Parameters:
| Name | Type | Default | Description |
|---|---|---|---|
mode | 'warn' | 'error' | 'ignore' | 'ignore' | How validation failures are handled on load, watch, and update(). |
ignore | Iterable<string> | undefined | Source names or document paths to skip validation for. Accepts an array or Set. |
Returns: ContentPlugin
import schemaValidation from 'comark-content/plugins/schema-validation'
const content = comarkContent({
plugins: [
schemaValidation({
mode: 'error',
ignore: new Set(['drafts', 'default/bad-types.json']),
}),
],
})mode
| Value | Behavior |
|---|---|
error | Throw — stops load, watch handling, or update() on the first invalid file. |
warn | Log a warning and keep the file (including on update()). |
ignore | Log a warning and exclude the file (ctx.file = null). On update(), the write is rejected. |
ignore
Each entry is one of:
| Form | Example | Skips |
|---|---|---|
| Source name | 'drafts' | Every document in the drafts source |
| Warning id | 'default/bad-types.json' | One file — same source/path as validation logs |
| Pasted log line | '├ default/bad-types.json: Expected …' | Normalized to the warning id above |
| Public path | '/about' | The document at that path |
| Glob | 'default/legacy/*.json', '**/drafts/**' | Picomatch against warning ids and keys |
Copy straight from a validation warning — the source/file.ext before the colon:
├ default/bad-types.json: Expected string, received integer for property "title".
^^^^^^^^^^^^^^^^^^^^^^ paste thisconst content = comarkContent({
source: fs('./content', { schema }),
plugins: [
schemaValidation({
ignore: [
'default/bad-types.json',
'drafts',
'default/legacy/*.json',
],
}),
],
})The source schema is still used for types and query columns on ignored entries.
const content = comarkContent({
sources: {
default: fs('./content', { schema }),
drafts: fs('./drafts', { schema }),
},
plugins: [schemaValidation({ ignore: ['drafts'] })],
})How it works
On file:parsed:
- Collect validation issues against
source.schema(skipped when the file matchesignore). - Apply
mode— warn and keep, warn and exclude, or throw.
Invalid files log one line per issue:
├ default/bad-types.json: Expected string, received integer for property "title".Snapshot hydration and manifest hydration skip file:parsed entirely — artifacts are checksum-verified exports loaded as-is.
Register markdown-fields before this plugin so markdown field transforms run before validation.
Validated extensions
.md, .markdown, .json, .yaml, .yml
Media and other binary entries are skipped. Parsed MarkdownDocument values are accepted for x-content: { type: 'markdown' } fields.