remark-lint-frontmatter-schema
Validate Markdown frontmatter YAML against an associated JSON schema with this remark-lint rule plugin.
Supports:
- Types validation, pattern, enumerations,… and all you can get with JSON Schema
- Code location problems indicator (for IDE to underline)
- Auto-fixes with suggestions
- Command Line Interface reporting
- VS Code integration (see below)
- Global patterns or in-file schemas associations
- In JS framework MD / MDX pipelines
Demo
👉
Play with pre-configured ./demo
Quick shallow clone with:
pnpx degit JulianCataldo/remark-lint-frontmatter-schema/demo ./demo
Jump to:
Quick start
Installation
pnpm install -D \
remark remark-cli \
remark-frontmatter \
@julian_cataldo/remark-lint-frontmatter-schema
Remove
-D
flag for runtimeunified
MD / MDX pipeline (custom, Astro, Gatsby, etc.), for production.
Keep it if you just want to lint with CLI or your IDE locally, without any production / CI needs.
VS Code (optional)
code --install-extension unifiedjs.vscode-remark
Configuration
CLI / IDE (VS Code) — Static linting
You also get example markdown files and associated schema to play with.
Supports remark-cli
and/or unifiedjs.vscode-remark
extension.
Workspace
Create root config file for remark
to source from:
touch ./.remarkrc.mjs
Paste this base config:
import remarkFrontmatter from 'remark-frontmatter';
import rlFmSchema from '@julian_cataldo/remark-lint-frontmatter-schema';
const remarkConfig = {
plugins: [remarkFrontmatter, rlFmSchema],
};
export default remarkConfig;
Schema example
./content/creative-work.schema.yaml
type: object
properties:
title:
type: string
# …
Schemas associations
Inspired by VS Code JSON Schema and redhat.vscode-yaml
conventions.
Inside frontmatter
See ./demo/content files for examples.
Schema association can be done directly inside the frontmatter of the markdown file, relative to project root, thanks to the '$schema'
key:
---
'$schema': /content/creative-work.schema.yaml
title: Hello there
category: Book
# …
---
# You're welcome!
🌝 My **markdown** content… 🌚
…
🆕
Globally, with patterns
Note:
Locally defined'$schema'
takes precedence over global settings below.
const remarkConfig = {
plugins: [
remarkFrontmatter,
[
rlFmSchema,
{
schemas: {
/* One schema for many files */
'./content/creative-work.schema.yaml': [
/* Support glob patterns */
'./content/*-creative-work.md',
/* Or direct file association */
'./content/the-one.md',
],
'./content/ghost.schema.yaml': [
'./content/casper.md',
'./content/ether.md',
],
},
},
],
],
};
'./foo'
, '/foo'
, 'foo'
, all will work.
It's always relative to your ./.remarkrc.mjs
file, in your workspace root.
CLI usage
Linting whole workspace files (as ./**/*.md
) with remark-cli
:
pnpm remark .
Yields:
# …
content/correct-creative-work.md
1:1 warning /category: Must be equal to one of the allowed values frontmatter-schema remark-lint
1:1 warning /complex/some: Must be string frontmatter-schema remark-lint
⚠ 6 warnings
MD / MDX pipeline — Runtime validation
Use it as usual like any remark plugin inside your framework or your custom unified
pipeline.
🆕
Custom pipeline
When processing markdown as single files inside your JS/TS app.
Schema should be provided programmatically like this:
// …
import remarkFrontmatter from 'remark-frontmatter';
import rlFmSchema from '@julian_cataldo/remark-lint-frontmatter-schema';
import type { JSONSchema7 } from 'json-schema';
import { reporter } from 'vfile-reporter';
const mySchema: JSONSchema7 = {
/* … */
};
const output = await unified()
// Your pipeline (basic example)
.use(remarkParse)
// …
.use(remarkFrontmatter)
.use(rlFmSchema, {
/* Bring your own schema */
embed: mySchema,
})
// …
.use(remarkRehype)
.use(rehypeStringify)
.use(rehypeFormat)
.process(theRawMarkdownLiteral);
/* `path` is for debugging purpose here, as MD literal comes from your app. */
output.path = './the-current-processed-md-file.md';
console.error(reporter([output]));
Yields:
./the-current-processed-md-file.md
1:1 warning Must have required property 'tag' frontmatter-schema remark-lint
⚠ 1 warning
Implementation living example
Checkout content-maestro repository.
It's a text based, structured content framework, for edition and consumption.
Content-Maestro relies on this library for providing file or API based linting errors logs.
You can see remark-lint-frontmatter-schema in action, on this line, in content-maestro source.
Important foot-notes for custom pipeline
This is different from static linting, with VS Code extension or CLI.
It will not source .remarkrc
(but you can source it by your own means, if you want).
In fact, it's not aware of your file structure, nor it will associate or import any schema / markdown files.
That way, it will integrate easier with your own business logic.
I found that static linting (during editing) / and runtime validation are two different uses cases enough to separate them in their setups, but I might converge them partially.
Framework
Warning
WIP. NOT tested yet!
See global patterns schemas
associations for settings reference.
Astro
In astro.config.mjs
// …
export default defineConfig({
// …
remarkPlugins: [
// …
'remark-frontmatter',
['@julian_cataldo/remark-lint-frontmatter-schema', { schemas }],
// …
];
// …
});
Gatsby
In gatsby-config.js
{
// …
plugins: [
// …
{
resolve: 'gatsby-transformer-remark',
options: {
plugins: [
// …
'remark-frontmatter',
['@julian_cataldo/remark-lint-frontmatter-schema', { schemas }],
// …
],
},
},
// …
];
}
Using:
- CLI Tool
Remark lint | https://github.com/remarkjs/remark-lint
- IDE Extension (optional)
VS Code
unifiedjs.vscode-remark
https://github.com/remarkjs/vscode-remark
Major dependencies:
ajv
, yaml
, remark
, remark-frontmatter
, unified
, remark-cli
See CHANGELOG.md for release history.