├── .editorconfig
├── .eslintrc.cjs
├── .github
├── FUNDING.yaml
└── workflows
│ └── release.yml
├── .gitignore
├── .prettierrc.cjs
├── .releaserc.yaml
├── CHANGELOG.md
├── LICENSE
├── README.md
├── demo
├── .remarkrc.mjs
├── .vscode
│ ├── extensions.json
│ └── settings.json
├── README.md
├── content
│ ├── creative-work.schema.yaml
│ ├── creative-work
│ │ ├── behind-the-gare-st-lazare__local-broken.md
│ │ ├── guten-nachte__local-correct.md
│ │ └── the-shipwreck__global-broken.md
│ └── page.schema.yaml
├── package.json
└── pipeline.ts
├── docs
├── screenshot-2.png
├── screenshot-3.png
└── screenshot.png
├── index.ts
├── package.json
├── pnpm-lock.yaml
├── renovate.json
└── tsconfig.json
/.editorconfig:
--------------------------------------------------------------------------------
1 | # https://EditorConfig.org
2 |
3 | root = true
4 |
5 | [*]
6 | indent_style = tab
7 | indent_size = 2
8 | end_of_line = lf
9 | charset = utf-8
10 | trim_trailing_whitespace = false
11 | insert_final_newline = true
12 |
13 | [*.md]
14 | trim_trailing_whitespace = false
15 |
16 |
17 | [*.json,*.yaml]
18 | indent_style = space
19 |
--------------------------------------------------------------------------------
/.eslintrc.cjs:
--------------------------------------------------------------------------------
1 | /** @type {import("@types/eslint").Linter.Config} */
2 |
3 | module.exports = {
4 | /**
5 | * References:
6 | *
7 | * https://github.com/JulianCataldo/web-garden/blob/develop/configs/eslint-js.cjs
8 | * https://github.com/JulianCataldo/web-garden/blob/develop/configs/eslint-ts.cjs
9 | *
10 | * */
11 | extends: [
12 | './node_modules/webdev-configs/eslint-js.cjs',
13 | './node_modules/webdev-configs/eslint-ts.cjs',
14 | ],
15 | };
16 |
--------------------------------------------------------------------------------
/.github/FUNDING.yaml:
--------------------------------------------------------------------------------
1 | github: [JulianCataldo]
2 |
3 | custom: ['https://www.buymeacoffee.com/JulianCataldo']
4 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: Release
2 | on:
3 | push:
4 | branches:
5 | - master
6 | jobs:
7 | release:
8 | name: Release
9 | runs-on: ubuntu-latest
10 | steps:
11 | - name: Checkout
12 | uses: actions/checkout@v3
13 | with:
14 | fetch-depth: 0
15 |
16 | - name: Setup Node.js
17 | uses: actions/setup-node@v3
18 | with:
19 | node-version: 'lts/*'
20 |
21 | - name: Setup PNPM 8.5.1
22 | uses: pnpm/action-setup@v2.4.0
23 | with:
24 | version: 8.5.1
25 |
26 | - name: Install dependencies
27 | run: pnpm install --frozen-lockfile
28 |
29 | - name: Build distributable files
30 | run: pnpm run build
31 |
32 | - name: Release
33 | env:
34 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
35 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
36 | run: ls && pnpm run release
37 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .parcel-cache
3 | .DS_Store
4 | .dev
5 | dist
6 | demo/pnpm-lock.yaml
7 |
8 | **/.vscode/settings.json
9 |
10 | #
11 |
--------------------------------------------------------------------------------
/.prettierrc.cjs:
--------------------------------------------------------------------------------
1 | /** @type {import("prettier").Options} */
2 |
3 | module.exports = {
4 | /**
5 | * Reference:
6 | *
7 | * https://github.com/JulianCataldo/web-garden/blob/develop/configs/prettier-base.cjs
8 | *
9 | * */
10 | ...require('webdev-configs/prettier-base.cjs'),
11 |
12 | overrides: [
13 | {
14 | files: ['*.json', '*.yaml'],
15 | options: {
16 | useTabs: false,
17 | },
18 | },
19 | ],
20 | };
21 |
--------------------------------------------------------------------------------
/.releaserc.yaml:
--------------------------------------------------------------------------------
1 | plugins:
2 | - '@semantic-release/commit-analyzer'
3 | - '@semantic-release/release-notes-generator'
4 | - '@semantic-release/changelog'
5 | - '@semantic-release/npm'
6 | - '@semantic-release/git'
7 | - '@semantic-release/github'
8 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## [3.15.4](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/compare/v3.15.3...v3.15.4) (2023-10-15)
2 |
3 |
4 | ### Bug Fixes
5 |
6 | * upgrade deps, fix demo ([9dd9dbb](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/commit/9dd9dbb896fbe7a2c63c7651a8cc3899e0b3ec65))
7 |
8 | ## [3.15.3](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/compare/v3.15.2...v3.15.3) (2023-09-15)
9 |
10 |
11 | ### Bug Fixes
12 |
13 | * treat first yaml section as frontmatter, even if it is not the first child ([c09c902](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/commit/c09c9021fc274033824be9728ffdcd3023dd3619))
14 |
15 | ## [3.15.2](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/compare/v3.15.1...v3.15.2) (2023-04-12)
16 |
17 |
18 | ### Bug Fixes
19 |
20 | * trigger release ([11db24b](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/commit/11db24b9fb8851e28417e42e912a43617279cddb))
21 |
22 | ## [3.15.1](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/compare/v3.15.0...v3.15.1) (2022-11-19)
23 |
24 |
25 | ### Bug Fixes
26 |
27 | * better configuration example (yaml) ([c4629bb](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/commit/c4629bb926d6f8be92bf7a580188874440dd783f))
28 |
29 | # [3.15.0](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/compare/v3.14.0...v3.15.0) (2022-11-03)
30 |
31 |
32 | ### Features
33 |
34 | * bump major version ([336f10e](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/commit/336f10e4c37819e6cd1a6ff9e490d72e26e578c1))
35 |
36 | # [3.14.0](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/compare/v3.13.0...v3.14.0) (2022-11-03)
37 |
38 |
39 | ### Bug Fixes
40 |
41 | * align demo with new feats ([3cbc16a](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/commit/3cbc16a20c3ceae772548f322d949ce978550b8e))
42 | * use bundle instead of dereference ([6a8824a](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/commit/6a8824a521015ec083408694e2101b914140efb8))
43 |
44 |
45 | ### Features
46 |
47 | * resolve current remark working directory ([1887ef1](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/commit/1887ef1e857b2deaf64fd542ffca1f328d298628))
48 | * use only json-schema-ref-parser, drop ajv's ([99b66bd](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/commit/99b66bd7fb36d8e17daf001976ab41eb6b133ecd))
49 |
50 | # [3.13.0](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/compare/v3.12.0...v3.13.0) (2022-10-30)
51 |
52 |
53 | ### Features
54 |
55 | * support relative path for local `$schema` ([20f53bf](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/commit/20f53bff49512e27a0b04a304cead2cecd0859b8))
56 |
57 | # [3.12.0](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/compare/v3.11.0...v3.12.0) (2022-10-30)
58 |
59 |
60 | ### Features
61 |
62 | * support for `const` auto-fix / suggestion ([a133c54](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/commit/a133c54eeb322c03e68582d80036fa39194d161e))
63 |
64 | # [3.11.0](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/compare/v3.10.2...v3.11.0) (2022-10-30)
65 |
66 |
67 | ### Features
68 |
69 | * better error output for eslint / remark ([aff95db](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/commit/aff95db261f45eb4dd8f1a947640ec0a312c8772))
70 |
71 | ## [3.10.2](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/compare/v3.10.1...v3.10.2) (2022-10-29)
72 |
73 |
74 | ### Bug Fixes
75 |
76 | * global schema assoc. w. `eslint-plugin-mdx` ([6849182](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/commit/6849182449db52e7cd1ea76b671466e3c690cd9c))
77 |
78 | ## [3.10.1](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/compare/v3.10.0...v3.10.1) (2022-10-29)
79 |
80 |
81 | ### Bug Fixes
82 |
83 | * allow empty settings for runtime plugin ([7534ea4](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/commit/7534ea4287abfa772779944839311acc7dcdd354))
84 |
85 | # [3.10.0](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/compare/v3.9.0...v3.10.0) (2022-10-29)
86 |
87 |
88 | ### Features
89 |
90 | * embedded `$ref` for demo pipeline ([59434d7](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/commit/59434d78470e4e0d5fc0756ddf988f40acd862e5))
91 |
92 | # [3.9.0](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/compare/v3.8.0...v3.9.0) (2022-10-29)
93 |
94 |
95 | ### Features
96 |
97 | * convert lint rule to async, for file loading ([4675b25](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/commit/4675b256fcd84a703deeebf3b412d2023c0cac6e))
98 | * load external schema definition references ([b1b9805](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/commit/b1b98051d03c17b6cc2bc3a29ebb8206172a52c0))
99 |
100 | # [3.8.0](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/compare/v3.7.9...v3.8.0) (2022-10-28)
101 |
102 |
103 | ### Features
104 |
105 | * core meta schema validation docs + example ([1976cc2](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/commit/1976cc29f486448fbd72db45f0b9156214e0d430))
106 | * embed native errors reports in vfile msg ([5a39364](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/commit/5a393647d61f7e2730f07649891c2b0347729ced))
107 |
108 | ## [3.7.9](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/compare/v3.7.8...v3.7.9) (2022-10-27)
109 |
110 |
111 | ### Bug Fixes
112 |
113 | * new cli harvest image + remove pointing hand ([42c5658](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/commit/42c56581598f919b0060f315196c639b8d18b8b9))
114 |
115 | ## [3.7.8](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/compare/v3.7.7...v3.7.8) (2022-10-27)
116 |
117 |
118 | ### Bug Fixes
119 |
120 | * update badge, links,… + pjson ordering ([8ebaca9](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/commit/8ebaca9b11cb3ec97e13e2ee84adaff5cb43df7e))
121 |
122 | ## [3.7.7](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/compare/v3.7.6...v3.7.7) (2022-10-20)
123 |
124 |
125 | ### Bug Fixes
126 |
127 | * properties spacing + bold ([e168cab](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/commit/e168cabd2d2cbf7619f07da43efd506d54386dae))
128 |
129 | ## [3.7.6](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/compare/v3.7.5...v3.7.6) (2022-10-20)
130 |
131 |
132 | ### Bug Fixes
133 |
134 | * remove todo, rename package ([ae1c0b5](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/commit/ae1c0b5d7d6247900540a4e66d4378c6b066dd4c))
135 |
136 | ## [3.7.5](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/compare/v3.7.4...v3.7.5) (2022-10-15)
137 |
138 |
139 | ### Bug Fixes
140 |
141 | * update package name ([c14c50a](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/commit/c14c50a47ee2a4c4765a3b41f361f8afcf1a307b))
142 |
143 | ## [3.7.4](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/compare/v3.7.3...v3.7.4) (2022-09-23)
144 |
145 |
146 | ### Bug Fixes
147 |
148 | * add more helpful dummy content ([b330095](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/commit/b330095ce6ad20b8cf56f459363e6226ef79a62c)), closes [#15](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/issues/15)
149 | * separate schema path and its hash ([f9063f3](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/commit/f9063f327b3a935ffd7e6ff0b24e2ee6d6f609d3)), closes [#14](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/issues/14)
150 |
151 | ## [3.7.3](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/compare/v3.7.2...v3.7.3) (2022-09-22)
152 |
153 |
154 | ### Bug Fixes
155 |
156 | * swap custom `glob-to-regexp` w. `minimatch` ([e7022d6](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/commit/e7022d61922f41d9a0d177e528ba1d078e0f2bb6)), closes [#12](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/issues/12) [#13](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/issues/13)
157 |
158 | ## [3.7.2](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/compare/v3.7.1...v3.7.2) (2022-09-09)
159 |
160 |
161 | ### Bug Fixes
162 |
163 | * add js native error message from pretty note ([e29ea28](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/commit/e29ea280b8c2b158b3b72a429cdeb0ee0e65d0fd))
164 |
165 | ## [3.7.1](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/compare/v3.7.0...v3.7.1) (2022-09-09)
166 |
167 |
168 | ### Bug Fixes
169 |
170 | * simplify doc headings hierarchy ([25d23c1](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/commit/25d23c14ac1667ca8f8147e5c041d4e3f30e773b))
171 |
172 | # [3.7.0](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/compare/v3.6.2...v3.7.0) (2022-09-09)
173 |
174 |
175 | ### Bug Fixes
176 |
177 | * docs living example updates ([881d7d3](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/commit/881d7d35ed3388c0aa17c4a5172089e7286a8160))
178 |
179 |
180 | ### Features
181 |
182 | * add user defined ajv settings ([a01f825](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/commit/a01f8256fa35463e8a87abb50082f81438612415))
183 | * export message typings + more message infos ([e132158](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/commit/e1321584fe9dbd1efac73ac470b01cd21de3a79c))
184 | * js native error message name ([8baa395](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/commit/8baa395161b08a8cb31827daa726b9b9a950628c))
185 |
186 | ## [3.6.2](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/compare/v3.6.1...v3.6.2) (2022-08-26)
187 |
188 |
189 | ### Bug Fixes
190 |
191 | * better type guards and typings ([cddf31c](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/commit/cddf31c97c0e41f70b2f22eee25f8a3d2e84e90a))
192 |
193 | ## [3.6.1](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/compare/v3.6.0...v3.6.1) (2022-08-25)
194 |
195 |
196 | ### Bug Fixes
197 |
198 | * reduce package size drastically ([d126030](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/commit/d12603011c388bbe01803ba776528178339de26d))
199 |
200 | # [3.6.0](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/compare/v3.5.1...v3.6.0) (2022-08-24)
201 |
202 |
203 | ### Features
204 |
205 | * vfile `location` > yaml `lineCounter` [#8](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/issues/8) ([e5ad3ea](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/commit/e5ad3eaa1cf8442a76e827b6d8f7509203a90356))
206 |
207 | ## [3.5.1](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/compare/v3.5.0...v3.5.1) (2022-08-19)
208 |
209 |
210 | ### Bug Fixes
211 |
212 | * more readable message note construction ([a9328fe](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/commit/a9328fe7c8e320bee0b4b47d7435108d51a10045))
213 |
214 | # [3.5.0](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/compare/v3.4.1...v3.5.0) (2022-08-19)
215 |
216 |
217 | ### Bug Fixes
218 |
219 | * rehaul docs titles hierarchy ([03ba93c](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/commit/03ba93c5e245444acfe199ebd5221a736e355141))
220 |
221 |
222 | ### Features
223 |
224 | * live implementation + frameworks in docs ([8cdb18e](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/commit/8cdb18efb7c65650a67efd7e0cd2be5de4e030f3))
225 |
226 | ## [3.4.1](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/compare/v3.4.0...v3.4.1) (2022-08-18)
227 |
228 |
229 | ### Bug Fixes
230 |
231 | * screenshots links in docs ([8544088](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/commit/8544088ebe23ab48bd71b2a5ab655d6a81089df5))
232 |
233 | # [3.4.0](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/compare/v3.3.1...v3.4.0) (2022-08-11)
234 |
235 |
236 | ### Bug Fixes
237 |
238 | * crash when no global schemas ([7107487](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/commit/71074876a31d224184a11a234f07c610c3c14380))
239 |
240 |
241 | ### Features
242 |
243 | * direct schema embedding through settings ([5430797](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/commit/543079764969e78fbe270aa5b5d8d844c7efff16))
244 |
245 | ## [3.3.1](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/compare/v3.3.0...v3.3.1) (2022-08-10)
246 |
247 |
248 | ### Bug Fixes
249 |
250 | * 'JSON Schema malformed' when required missing ([0160337](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/commit/01603375de98813351e24ca377b8b71de556837c))
251 |
252 | # [3.3.0](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/compare/v3.2.2...v3.3.0) (2022-08-09)
253 |
254 |
255 | ### Features
256 |
257 | * better warning message display ([77542a9](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/commit/77542a9d6101aa39668d915367628e42ff047c89))
258 |
259 | ## [3.2.2](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/compare/v3.2.1...v3.2.2) (2022-08-09)
260 |
261 |
262 | ### Bug Fixes
263 |
264 | * add details to features + re-orders [misfire] ([788bf70](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/commit/788bf70ecf6cad3e84523a0e7f6b374ef5ad7559))
265 |
266 | ## [3.2.1](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/compare/v3.2.0...v3.2.1) (2022-08-09)
267 |
268 |
269 | ### Bug Fixes
270 |
271 | * add details to features + re-orders ([dbe44e2](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/commit/dbe44e219d051c00796101db57d0253ed78cc1b8))
272 |
273 | # [3.2.0](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/compare/v3.1.0...v3.2.0) (2022-08-09)
274 |
275 |
276 | ### Bug Fixes
277 |
278 | * add example for global associations + details ([9f7d471](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/commit/9f7d471f40d62d4deafe8b9176b4f8cefe2fecbb))
279 |
280 |
281 | ### Features
282 |
283 | * update demo to reflect global settings ([0916295](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/commit/091629511ce8d2b1354dc0352de1df529eb2c868))
284 |
285 | # [3.1.0](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/compare/v3.0.0...v3.1.0) (2022-08-09)
286 |
287 |
288 | ### Features
289 |
290 | * init plugin settings, with glob' schemas 🤯 ([23c06f7](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/commit/23c06f76fbc578474c832957b184ef3d22439e02))
291 |
292 | # [3.0.0](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/compare/v2.5.6...v3.0.0) (2022-08-08)
293 |
294 |
295 | ### Bug Fixes
296 |
297 | * `unified` and `unist` missing, breaking ci ([6b4dbab](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/commit/6b4dbabaa104023b42e2f7dd1be9e23db58db6b2))
298 | * change screenshot link to raw github ([9d65364](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/commit/9d65364acec56c5f787fd870e2178537abf5c0ff))
299 | * correct typings + coercition for ast prop ([69766fd](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/commit/69766fd7849e7f3ff6ab53fac17ad1cb382e68ef))
300 |
301 |
302 | ### Features
303 |
304 | * major rehaul w. type safety, guards + errors ([4dd0dad](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/commit/4dd0dadca808a1affac2605d061447feb8c6ee86))
305 |
306 |
307 | ### BREAKING CHANGES
308 |
309 | * - `Root` and `YAML from `types/mdast`
310 | - `isNode` from `yaml`
311 | - `vfile.message` instead of pushing to `vfile.messages`
312 | - parse YAML once and use `toJS()`
313 | - `vFile` `cwd` instead of `process.cwd()`
314 |
315 | Guards / Errors for:
316 |
317 | - JSON Schema not found (wrong path)
318 | - JSON Schema malformed
319 | - YAML Schema parsing error
320 | - YAML Frontmatter parsing error (doesn't seems to occur anyway)
321 |
322 | Also, splitting functions a bit, adding some explanations and ideas.
323 |
324 | Thanks a lot @remcohaszing 👏
325 |
326 | ## [2.5.6](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/compare/v2.5.5...v2.5.6) (2022-08-08)
327 |
328 |
329 | ### Bug Fixes
330 |
331 | * add command details ([b4a648a](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/commit/b4a648aaa252efc101871a777145f606b6c0b480))
332 |
333 | ## [2.5.5](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/compare/v2.5.4...v2.5.5) (2022-08-08)
334 |
335 |
336 | ### Bug Fixes
337 |
338 | * clean screenshot unneeded files ([c7026e3](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/commit/c7026e3ef29ffefca56661d544945910fe7aa728))
339 |
340 | ## [2.5.4](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/compare/v2.5.3...v2.5.4) (2022-08-08)
341 |
342 |
343 | ### Bug Fixes
344 |
345 | * update demo content ([5a4c94c](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/commit/5a4c94c55bca3aa7c4c471ff6c6f85b7e89b588b))
346 | * update docs badge and description ([3a55113](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/commit/3a55113969e12d78f8620c994011e69cb72cf1c8))
347 |
348 | ## [2.5.3](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/compare/v2.5.2...v2.5.3) (2022-08-07)
349 |
350 |
351 | ### Bug Fixes
352 |
353 | * doc, add remark config example ([e64163e](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/commit/e64163e3d38aacb352aa9fa0c98e5d0dce296d78))
354 |
355 | ## [2.5.2](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/compare/v2.5.1...v2.5.2) (2022-08-07)
356 |
357 |
358 | ### Bug Fixes
359 |
360 | * docs, correct full install + more details ([0eb1c9c](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/commit/0eb1c9c692e6a672767a46e05a1ca3e90faa05cd))
361 |
362 | ## [2.5.1](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/compare/v2.5.0...v2.5.1) (2022-08-07)
363 |
364 |
365 | ### Bug Fixes
366 |
367 | * check yaml presence + extract function ([d08f5ae](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/commit/d08f5aea005245579321d99af77fa324e9ad3d86))
368 |
369 | # [2.5.0](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/compare/v2.4.7...v2.5.0) (2022-08-07)
370 |
371 |
372 | ### Features
373 |
374 | * use `yaml` instead of `js-yaml` ([95444d3](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/commit/95444d3fe1848cd00c646e76ae444d01454cbdd9))
375 |
376 | ## [2.4.7](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/compare/v2.4.6...v2.4.7) (2022-08-06)
377 |
378 |
379 | ### Bug Fixes
380 |
381 | * re-order package.json fields + add details ([3e4298e](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/commit/3e4298ef9b15e2ce0413146f1192dd3214a1b635))
382 |
383 | ## [2.4.6](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/compare/v2.4.5...v2.4.6) (2022-08-06)
384 |
385 |
386 | ### Bug Fixes
387 |
388 | * add npm package badge link ([14a2ef4](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/commit/14a2ef439ce560bfadb5484ef41704a3f67d2dc5))
389 | * docs, add npm badge + better description ([7e7d614](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/commit/7e7d614560f18b973bbd48c9d8f6aa5f186199ef))
390 |
391 | ## [2.4.5](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/compare/v2.4.4...v2.4.5) (2022-08-06)
392 |
393 |
394 | ### Bug Fixes
395 |
396 | * add npm package badge link ([923ad35](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/commit/923ad35e4e3cb91a06fd3af19fc6dec0fc752fec))
397 |
398 | ## [2.4.4](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/compare/v2.4.3...v2.4.4) (2022-08-06)
399 |
400 |
401 | ### Bug Fixes
402 |
403 | * docs, add npm badge + better description ([e96a79e](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/commit/e96a79ecc17fdfb58ef16b7660887917d06b837f))
404 |
405 | ## [2.4.3](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/compare/v2.4.2...v2.4.3) (2022-08-06)
406 |
407 |
408 | ### Bug Fixes
409 |
410 | * add details to docs + new screenshot ([e6b94b9](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/commit/e6b94b9916908a79bc486874632ebda0cd41650b))
411 |
412 | ## [2.4.2](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/compare/v2.4.1...v2.4.2) (2022-08-06)
413 |
414 |
415 | ### Bug Fixes
416 |
417 | * typo for url in docs ([8d70a9b](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/commit/8d70a9b074f63ca89927d3fabef111133fdc3d96))
418 |
419 | ## [2.4.1](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/compare/v2.4.0...v2.4.1) (2022-08-05)
420 |
421 |
422 | ### Bug Fixes
423 |
424 | * remove dead code + refactor ([e167e11](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/commit/e167e11945480219318375c8b541c2e749c71bad))
425 |
426 | # [2.4.0](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/compare/v2.3.0...v2.4.0) (2022-08-05)
427 |
428 |
429 | ### Features
430 |
431 | * add code examples in docs ([c449814](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/commit/c4498145efb6e5bfc93be49238c04aeb4e5de418))
432 |
433 | # [2.3.0](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/compare/v2.2.0...v2.3.0) (2022-08-05)
434 |
435 |
436 | ### Features
437 |
438 | * docs for known limitations + keywords ([a12fdb4](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/commit/a12fdb4da2eaacfc73df4b3a41d2f48951510436))
439 |
440 | # [2.2.0](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/compare/v2.1.0...v2.2.0) (2022-08-05)
441 |
442 |
443 | ### Features
444 |
445 | * init root doc + demo screenshot ([3fffa85](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/commit/3fffa858268bd662d613428537fe756feab2d995))
446 |
447 | # [2.1.0](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/compare/v2.0.0...v2.1.0) (2022-08-05)
448 |
449 |
450 | ### Features
451 |
452 | * demo file package ([216acfd](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/commit/216acfdd6a401b2d7ec7b3f4fdfd94ff44649319))
453 |
454 | # [2.0.0](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/compare/v1.3.0...v2.0.0) (2022-08-05)
455 |
456 |
457 | ### Features
458 |
459 | * force bump major version (deleted npm) ([7d439b2](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/commit/7d439b28046d6a1624b0baa0ffa11d4f2b48f853))
460 |
461 |
462 | ### BREAKING CHANGES
463 |
464 | *
465 |
466 | # [1.3.0](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/compare/v1.2.0...v1.3.0) (2022-08-05)
467 |
468 |
469 | ### Features
470 |
471 | * vscode extensions recommendation for demo ([8d4cb7b](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/commit/8d4cb7b9e9b31648b702b109d62d39e4d5c3f50a))
472 |
473 | # [1.2.0](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/compare/v1.1.0...v1.2.0) (2022-08-05)
474 |
475 |
476 | ### Features
477 |
478 | * remark lint base demo config ([ae67bc3](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/commit/ae67bc3b8190949928cb6bb838db97d73f1b1eb1))
479 |
480 | # [1.1.0](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/compare/v1.0.0...v1.1.0) (2022-08-05)
481 |
482 |
483 | ### Features
484 |
485 | * add demo content ([f0c1e2e](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/commit/f0c1e2ec5a13ea1edd6134a5628262a035349b6a))
486 |
487 | # 1.0.0 (2022-08-05)
488 |
489 |
490 | ### Features
491 |
492 | * init plugin script index file ([fd0d451](https://github.com/JulianCataldo/remark-lint-frontmatter-schema/commit/fd0d4511a3e1cb37df4cbd5fe680f248ce71e72d))
493 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | ISC License
2 |
3 | Copyright (c) 2022 Julian Cataldo — https://www.juliancataldo.com
4 |
5 | Permission to use, copy, modify, and/or distribute this software for any
6 | purpose with or without fee is hereby granted, provided that the above
7 | copyright notice and this permission notice appear in all copies.
8 |
9 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
10 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11 | AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
12 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13 | LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
14 | OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15 | PERFORMANCE OF THIS SOFTWARE.
16 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # `remark-lint-frontmatter-schema` 📑
2 |
3 |
4 |
5 | [](https://code.visualstudio.com)
6 | [](https://unifiedjs.com)
7 | [](https://www.npmjs.com/package/remark-lint-frontmatter-schema)
8 | 
9 | [](./LICENSE)
10 | [](https://github.com/JulianCataldo/remark-lint-frontmatter-schema)
11 | [](https://makeapullrequest.com)
12 | [](http://www.typescriptlang.org/)
13 | [](https://prettier.io)
14 | [](https://editorconfig.org)
15 | [](https://eslint.org)
16 |
17 |
18 |
19 | Validate **Markdown** frontmatter **YAML** against an associated **JSON schema** with this **remark-lint** rule plugin.
20 |
21 | Supports:
22 |
23 | - **Types validation**, pattern, enumerations,… and all you can get with JSON Schema
24 | - **Code location** problems indicator (for IDE to underline)
25 | - **Auto-fixes** with suggestions
26 | - **C**ommand **L**ine **I**nterface reports
27 | - **VS Code** integration (see below)
28 | - **Global patterns** or **in-file** schemas associations
29 | - In JS framework **MD / MDX pipelines**
30 |
31 | # Demo
32 |
33 |
34 |
35 | [
36 | **🕹 Preview it online!**](https://astro-content.dev/__content)
37 |
38 | (w. Astro Content — Editor)
39 |
40 |
41 |
42 | ---
43 |
44 | **Jump to**:
45 |
46 | - [👉 **Play with pre-configured ./demo**](#play-with-pre-configured-demo)
47 | - [Base](#base)
48 | - [VS Code (optional)](#vs-code-optional)
49 | - [CLI / IDE (VS Code) — **Static** linting](#cli--ide-vs-code--static-linting)
50 | - [Workspace](#workspace)
51 | - [Schema example](#schema-example)
52 | - [🆕 Add references to external definitions (advanced)](#add-references-to-external-definitions-advanced)
53 | - [Schemas associations](#schemas-associations)
54 | - [Inside frontmatter](#inside-frontmatter)
55 | - [Globally, with patterns](#globally-with-patterns)
56 | - [CLI usage](#cli-usage)
57 | - [Bonus](#bonus)
58 | - [Validate your schema with _JSON meta schema_](#validate-your-schema-with-json-meta-schema)
59 | - [ESLint MDX plugin setup](#eslint-mdx-plugin-setup)
60 | - [Known issues](#known-issues)
61 | - [MD / MDX pipeline — **Runtime** validation](#md--mdx-pipeline--runtime-validation)
62 | - [Custom pipeline](#custom-pipeline)
63 | - [Implementation living example](#implementation-living-example)
64 | - [Important foot-notes for custom pipeline](#important-foot-notes-for-custom-pipeline)
65 | - [Framework](#framework)
66 | - [Astro](#astro)
67 | - [Gatsby](#gatsby)
68 |
69 | ---
70 |
71 | [](https://raw.githubusercontent.com/JulianCataldo/remark-lint-frontmatter-schema/master/docs/screenshot.png)
72 |
73 | ---
74 |
75 | [](https://raw.githubusercontent.com/JulianCataldo/remark-lint-frontmatter-schema/master/docs/screenshot-2.png)
76 |
77 | ---
78 |
79 | [](https://raw.githubusercontent.com/JulianCataldo/remark-lint-frontmatter-schema/master/docs/screenshot-3.png)
80 |
81 | ---
82 |
83 | ## 👉 **Play with pre-configured [./demo](./demo/)**
84 |
85 | Quick shallow **clone** with:
86 |
87 | ```sh
88 | pnpx degit JulianCataldo/remark-lint-frontmatter-schema/demo ./demo
89 | ```
90 |
91 | ---
92 |
93 | # Installation
94 |
95 | ### Base
96 |
97 | ```sh
98 | pnpm install -D \
99 | remark remark-cli \
100 | remark-frontmatter \
101 | remark-lint-frontmatter-schema
102 | ```
103 |
104 | > **Remove** `-D` flag for runtime **`unified`** MD / MDX **pipeline** (custom, Astro, Gatsby, etc.), for production.
105 | > **Keep it** if you just want to lint with **CLI** or your **IDE** locally, without any production / CI needs.
106 |
107 | ### VS Code (optional)
108 |
109 | ```sh
110 | code --install-extension unifiedjs.vscode-remark
111 | ```
112 |
113 | # Configuration
114 |
115 | ### CLI / IDE (VS Code) — **Static** linting
116 |
117 | 👉 **See [./demo](./demo/)** folder to get a working, pre-configured, bare project workspace.
118 | You also get example Markdown files and associated schema to play with.
119 | Supports `remark-cli` and/or `unifiedjs.vscode-remark` extension.
120 |
121 | 📌 Check out the **[demo/README.md](./demo) for bootstrapping** it.
122 |
123 | #### Workspace
124 |
125 | Create the root config. file for `remark` to source from:
126 | `touch ./.remarkrc.mjs`
127 |
128 | Paste this base configuration:
129 |
130 | ```mjs
131 | import remarkFrontmatter from 'remark-frontmatter';
132 | import remarkLintFrontmatterSchema from 'remark-lint-frontmatter-schema';
133 |
134 | const remarkConfig = {
135 | plugins: [remarkFrontmatter, remarkLintFrontmatterSchema],
136 | };
137 | export default remarkConfig;
138 | ```
139 |
140 | You can use YAML / JSON / …, too (uses [cosmiconfig](https://github.com/davidtheclark/cosmiconfig)).
141 |
142 | #### Schema example
143 |
144 | `./content/creative-work.schema.yaml`
145 |
146 | ```yaml
147 | type: object
148 | properties:
149 | title:
150 | type: string
151 | # …
152 | ```
153 |
154 | ##### 🆕 Add references to external definitions (advanced)
155 |
156 | Referencing schema definitions
157 | allows re-using bit and piece instead of duplicate them,
158 | accross your content schemas.
159 |
160 | You can reference an external schema relatively, using `$ref`.
161 | For example we can -_kind of_- merge an host object with a reference properties:
162 |
163 | The host schema, `content/articles/index.schema.yaml`
164 |
165 | ```yaml
166 | allOf:
167 | - $ref: ../page.schema.yaml
168 |
169 | - properties:
170 | layout:
171 | const: src/layouts/Article.astro
172 | category:
173 | type: string
174 | enum:
175 | - Book
176 | - Movie
177 | foo:
178 | type: string
179 |
180 | required:
181 | - layout
182 | - category
183 | ```
184 |
185 | A referenced schema, `content/page.schema.yaml`
186 |
187 | ```yaml
188 | properties:
189 | title:
190 | type: string
191 | maxLength: 80
192 | # ...
193 | # ...
194 |
195 | required:
196 | - title
197 | ```
198 |
199 | The result will be _(virtually)_ the same as this:
200 |
201 | ```yaml
202 | properties:
203 | title:
204 | type: string
205 | maxLength: 80
206 | # ...
207 | # ...
208 | layout:
209 | const: src/layouts/Article.astro
210 | category:
211 | type: string
212 | enum:
213 | - Book
214 | - Movie
215 | foo:
216 | type: string
217 | # ...
218 |
219 | required:
220 | - title
221 | - layout
222 | - category
223 | ```
224 |
225 | #### Schemas associations
226 |
227 | Inspired by [VS Code JSON Schema](https://code.visualstudio.com/docs/languages/json#_json-schemas-and-settings)
228 | and [`redhat.vscode-yaml`](https://marketplace.visualstudio.com/items?itemName=redhat.vscode-yaml) conventions.
229 |
230 | ##### Inside frontmatter
231 |
232 | See **[./demo/content](./demo/content)** files for examples.
233 |
234 | Schema association can be done directly **inside** the **frontmatter** of the **Markdown** file,
235 | relative to project root, thanks to the `'$schema'` key:
236 |
237 | ```markdown
238 | ---
239 | # From workspace root (`foo/…`, `/foo/…` or `./foo/…` is the same)
240 | '$schema': content/creative-work.schema.yaml
241 |
242 | # —Or— relatively, from this current file directory (`./foo/…` or `../foo/…`)
243 | # '$schema': ../creative-work.schema.yaml
244 |
245 | layout: src/layouts/Article.astro
246 |
247 | title: Hello there
248 | category: Book
249 | # …
250 | ---
251 |
252 | # You're welcome!
253 |
254 | 🌝 My **Markdown** content… 🌚
255 | …
256 | ```
257 |
258 | ##### Globally, with patterns
259 |
260 | > **Note**:
261 | > Locally defined **`'$schema'` takes precedence** over global settings below.
262 |
263 | ```js
264 | const remarkConfig = {
265 | plugins: [
266 | remarkFrontmatter,
267 | [
268 | remarkLintFrontmatterSchema,
269 | {
270 | schemas: {
271 | /* One schema for many files */
272 | './content/creative-work.schema.yaml': [
273 | /* Per-file association */
274 | './content/creative-work/the-shipwreck__global-broken.md',
275 |
276 | /* Support glob patterns ———v */
277 | // './content/creative-work/*.md',
278 | // …
279 | // `./` prefix is optional
280 | // 'content/creative-work/foobiz.md',
281 | ],
282 |
283 | // './content/ghost.schema.yaml': [
284 | // './content/casper.md',
285 | // './content/ether.md',
286 | // ],
287 | },
288 | },
289 | ],
290 | ],
291 | };
292 | ```
293 |
294 | `'./foo'`, `'/foo'`, `'foo'`, all will work.
295 | It's always relative to your `./.remarkrc.mjs` file, in your workspace root.
296 |
297 | #### CLI usage
298 |
299 | Linting whole workspace files (as `./**/*.md`) with `remark-cli`:
300 |
301 | ```sh
302 | pnpm remark .
303 | ```
304 |
305 | Yields:
306 |
307 | 
308 |
309 | #### Bonus
310 |
311 | ##### Validate your schema with _JSON meta schema_
312 |
313 | First, install the [YAML for Visual Studio Code](https://marketplace.visualstudio.com/items?itemName=redhat.vscode-yaml) extension:
314 |
315 | ```sh
316 | code --install-extension redhat.vscode-yaml
317 | ```
318 |
319 | Then, add this to your `.vscode/settings.json`:
320 |
321 | ```jsonc
322 | {
323 | "yaml.schemas": {
324 | "http://json-schema.org/draft-07/schema#": ["content/**/*.schema.yaml"]
325 | }
326 | /* ... */
327 | }
328 | ```
329 |
330 | ##### ESLint MDX plugin setup
331 |
332 | Will work with the ESLint VS Code extension and the CLI command.
333 |
334 | Install the [ESLint MDX plugin](https://github.com/mdx-js/eslint-mdx),
335 | the [MDX VS Code extension](https://marketplace.visualstudio.com/items?itemName=unifiedjs.vscode-mdx) and the [ESLint VS Code extension](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint).
336 |
337 | Add this dependencies to your project:
338 |
339 | ```sh
340 | pnpm i -D eslint eslint-plugin-mdx \
341 | eslint-plugin-prettier eslint-config-prettier
342 | ```
343 |
344 | Add a `.eslintrc.cjs`:
345 |
346 | ```js
347 | /** @type {import("@types/eslint").Linter.Config} */
348 |
349 | module.exports = {
350 | overrides: [
351 | {
352 | files: ['*.md', '*.mdx'],
353 | extends: ['plugin:mdx/recommended'],
354 | },
355 | ],
356 | };
357 | ```
358 |
359 | Add a `.remarkrc.yaml` (or JSON, etc.), e.g.:
360 |
361 | ```yaml
362 | plugins:
363 | - remark-frontmatter
364 |
365 | - - remark-lint-frontmatter-schema
366 | - schemas:
367 | src/schemas/blog-post.schema.yaml:
368 | - content/blog-posts/*.{md,mdx}
369 |
370 | # - remark-preset-lint-consistent
371 | # - remark-preset-lint-markdown-style-guide
372 | # - remark-preset-lint-recommended
373 | ```
374 |
375 | ---
376 |
377 | Result:
378 |
379 | [
380 | ](https://res.cloudinary.com/dzfylx93l/image/upload/eslint-plugin-mdx-1.png)
381 |
382 | ---
383 |
384 | Lint with CLI:
385 |
386 | ```sh
387 | pnpm eslint --ext .mdx .
388 | ```
389 |
390 | > Efforts has been made to have the best output for both remark and ESLint,
391 | > for IDE extensions and CLIs.
392 |
393 | ###### Known issues
394 |
395 | - Expected `enum` values suggestions are working with the remark extension, not with the ESLint one.
396 | - Similarly, ESLint output will give less details (see screenshot above), and a bit different layout for CLI output, too.
397 | - remark extension seems to load faster, and is more reactive to schema changes.
398 | - As of `eslint-plugin-mdx@2`, `.remarkrc.mjs` (ES Module) is not loaded, JSON and YAML configs are fine.
399 |
400 | ### MD / MDX pipeline — **Runtime** validation
401 |
402 | Use it as usual like any remark plugin inside your framework or your custom `unified` pipeline.
403 |
404 | #### Custom pipeline
405 |
406 | When processing Markdown as single files inside your JS/TS app.
407 | An minimal example is provided in [`./demo/pipeline.ts`](./demo/pipeline.ts), you can launch it with `pnpm pipeline` from `./demo`.
408 |
409 | ---
410 |
411 | Schema should be provided programmatically like this:
412 |
413 | ```ts
414 | // …
415 | import remarkFrontmatter from 'remark-frontmatter';
416 | import remarkLintFrontmatterSchema from 'remark-lint-frontmatter-schema';
417 | import type { JSONSchema7 } from 'json-schema';
418 | import { reporter } from 'vfile-reporter';
419 |
420 | const mySchema: JSONSchema7 = {
421 | /* … */
422 | };
423 |
424 | const output = await unified()
425 | // Your pipeline (basic example)
426 | .use(remarkParse)
427 | // …
428 | .use(remarkFrontmatter)
429 |
430 | .use(remarkLintFrontmatterSchema, {
431 | /* Bring your own schema */
432 | embed: mySchema,
433 | })
434 |
435 | // …
436 | .use(remarkRehype)
437 | .use(rehypeStringify)
438 | .use(rehypeFormat)
439 | .process(theRawMarkdownLiteral);
440 |
441 | /* `path` is for debugging purpose here, as MD literal comes from your app. */
442 | output.path = './the-current-processed-md-file.md';
443 |
444 | console.error(reporter([output]));
445 | ```
446 |
447 | Yields:
448 |
449 | ```
450 | ./the-current-processed-md-file.md
451 | 1:1 warning Must have required property 'tag' frontmatter-schema remark-lint
452 |
453 | ⚠ 1 warning
454 | ```
455 |
456 | ##### Implementation living example
457 |
458 | Checkout [**Astro Content**](https://github.com/JulianCataldo/astro-content) repository.
459 |
460 |
461 |
462 |
463 | Astro Content relies on this library, among others, for providing linting reports.
464 |
465 |
466 |
467 | ##### Important foot-notes for custom pipeline
468 |
469 | This is **different from static linting**, with VS Code extension or CLI.
470 | It **will not source `.remarkrc`** (but you can source it by your own means, if you want).
471 | In fact, it's not aware of your file structure,
472 | nor it will associate or import any schema / Markdown files.
473 | That way, it will integrate easier with your own business logic and existing pipelines.
474 | I found that **static linting** (during editing) / and **runtime validation** are two different
475 | uses cases enough to separate them in their setups, but I might converge them partially.
476 |
477 | #### Framework
478 |
479 | > **Warning**
480 | > WIP. **NOT tested yet**! It is not a common use case for `remark-lint`.
481 | > Linting data inside frameworks are generally ignored.
482 | > AFAIK, `messages` data isn't forwarded to CLI output.
483 | > Feel free to open a PR if you have some uses cases in this area that need special care.
484 | > Maybe Astro or Astro Content could leverage these linter warnings in the future.
485 |
486 | See [global patterns `schemas` associations](#globally-with-patterns) for settings reference.
487 |
488 | ##### Astro
489 |
490 | In `astro.config.mjs`
491 |
492 | ```ts
493 | // …
494 | export default defineConfig({
495 | // …
496 | remarkPlugins: [
497 | // …
498 | 'remark-frontmatter',
499 | ['remark-lint-frontmatter-schema', { schemas }],
500 | // …
501 | ];
502 | // …
503 | });
504 | ```
505 |
506 | ##### Gatsby
507 |
508 | In `gatsby-config.js`
509 |
510 | ```ts
511 | {
512 | // …
513 | plugins: [
514 | // …
515 | {
516 | resolve: 'gatsby-transformer-remark',
517 | options: {
518 | plugins: [
519 | // …
520 | 'remark-frontmatter',
521 | ['remark-lint-frontmatter-schema', { schemas }],
522 | // …
523 | ],
524 | },
525 | },
526 | // …
527 | ];
528 | }
529 | ```
530 |
531 | # Interfaces
532 |
533 | ```ts
534 | export interface Settings {
535 | /**
536 | * Global workspace file associations mapping (for linter extension).
537 | *
538 | * **Example**: `'schemas/thing.schema.yaml': ['content/things/*.md']`
539 | */
540 | schemas?: Record;
541 |
542 | /**
543 | * Direct schema embedding (for using inside an `unified` transform pipeline).
544 | *
545 | * Format: JSON Schema - draft-2019-09
546 | *
547 | * **Documentation**: https://ajv.js.org/json-schema.html#draft-07
548 | */
549 | embed?: JSONSchema7;
550 |
551 | /**
552 | * **Documentation**: https://ajv.js.org/options.html
553 | */
554 | ajvOptions?: AjvOptions;
555 | }
556 |
557 | export interface FrontmatterSchemaMessage extends VFileMessage {
558 | schema: AjvErrorObject & { url: JSONSchemaReference };
559 | }
560 | ```
561 |
562 | Example of a `VFileMessage` content you could collect from this lint rule:
563 |
564 | ```jsonc
565 | [
566 | // …
567 | {
568 | // JS native `Error`
569 | "name": "Markdown YAML frontmatter error (JSON Schema)",
570 | "message": "Keyword: type\nType: string\nSchema path: #/properties/title/type",
571 |
572 | // `VFileMessage` (Linter / VS Code…)
573 | "reason": "/clientType: Must be equal to one of the allowed values",
574 | "line": 16,
575 | "column": 13,
576 | "url": "https://github.com/JulianCataldo/remark-lint-frontmatter-schema",
577 | "source": "remark-lint",
578 | "ruleId": "frontmatter-schema",
579 | "position": {
580 | "start": {
581 | "line": 16,
582 | "column": 13
583 | },
584 | "end": {
585 | "line": 16,
586 | "column": 24
587 | }
588 | },
589 | "fatal": false,
590 | "actual": "Individuaaaaaaaal",
591 | "expected": ["Corporate", "Non-profit", "Individual"],
592 | // Condensed string, human readable version of AJV error object
593 | "note": "Keyword: enum\nAllowed values: Corporate, Non-profit, Individual\nSchema path: #/properties/clientType/enum",
594 |
595 | // AJV's `ErrorObject`
596 | "schema": {
597 | "url": "https://ajv.js.org/json-schema.html",
598 | "instancePath": "/clientType",
599 | "schemaPath": "#/properties/clientType/enum",
600 | "keyword": "enum",
601 | "params": {
602 | "allowedValues": ["Corporate", "Non-profit", "Individual"]
603 | },
604 | "message": "must be equal to one of the allowed values"
605 | }
606 | }
607 | ]
608 | ```
609 |
610 | ---
611 |
612 |
613 |
614 | # Footnotes
615 |
616 | **100% ESM**, including dependencies.
617 |
618 | Environments:
619 |
620 | - **CLI Tool**
621 | > Remark lint | https://github.com/remarkjs/remark-lint
622 | - **IDE Extension** (optional)
623 | > VS Code `unifiedjs.vscode-remark`
624 | > https://github.com/remarkjs/vscode-remark
625 |
626 | Major dependencies:
627 |
628 | `ajv`, `yaml`, `remark`, `remark-frontmatter`, `unified`, `remark-cli`
629 |
630 | ---
631 |
632 | See [CHANGELOG.md](./CHANGELOG.md) for release history.
633 |
634 | ---
635 |
636 | **Other projects 👀**…
637 |
638 | - [retext-case-police](https://github.com/JulianCataldo/retext-case-police): Check popular names casing. Example: ⚠️ `github` → ✅ `GitHub`.
639 | - [remark-embed](https://github.com/JulianCataldo/remark-embed): A `remark` plugin for embedding remote / local Markdown or code snippets.
640 | - [astro-content](https://github.com/JulianCataldo/astro-content): A text based, structured content manager, for edition and consumption.
641 | - [Web garden](https://github.com/JulianCataldo/web-garden): Building blocks for making progressive and future-proof websites.
642 |
643 | ---
644 |
645 |
646 |
647 | **Find this project useful?**
648 |
649 | [](https://github.com/JulianCataldo/remark-lint-frontmatter-schema)
650 |
651 |
652 |
653 | ---
654 |
655 | 🔗 [JulianCataldo.com](https://www.juliancataldo.com)
656 |
--------------------------------------------------------------------------------
/demo/.remarkrc.mjs:
--------------------------------------------------------------------------------
1 | import remarkFrontmatter from 'remark-frontmatter';
2 | import remarkLintFrontmatterSchema from 'remark-lint-frontmatter-schema';
3 | /* —————————————————————————————————————————————————————————————————————————— */
4 |
5 | const remarkConfig = {
6 | plugins: [
7 | remarkFrontmatter,
8 | /* v————— Use it without settings, with local '$schema' associations only */
9 | // rlFmSchema
10 |
11 | /* v————— Or with global schemas associations */
12 | [
13 | remarkLintFrontmatterSchema,
14 | {
15 | schemas: {
16 | /* One schema for many files */
17 | './content/creative-work.schema.yaml': [
18 | './content/creative-work/the-shipwreck__global-broken.md',
19 |
20 | /* Support glob patterns ———v */
21 | // './content/creative-work/*.md',
22 | // …
23 | // `./` prefix is optional
24 | // 'content/creative-work/foobiz.md',
25 | // './content/elsewhere/does-not-exist-anymore.md',
26 | ],
27 |
28 | // './content/ghost.schema.yaml': [
29 | // './content/casper.md',
30 | // './content/ether.md',
31 | // ],
32 | },
33 | },
34 | ],
35 | ],
36 | };
37 |
38 | export default remarkConfig;
39 |
--------------------------------------------------------------------------------
/demo/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": ["unifiedjs.vscode-remark"]
3 | }
4 |
--------------------------------------------------------------------------------
/demo/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "yaml.schemas": {
3 | "http://json-schema.org/draft-07/schema#": ["content/**/*.schema.yaml"]
4 | },
5 | "workbench.colorCustomizations": {
6 | "editorRuler.foreground": "#98c2bc1c",
7 | "editorInlayHint.foreground": "#B99",
8 | "editorInlayHint.background": "#222",
9 | "[GitHub Dark]": {
10 | "sideBar.background": "#1b1b1b",
11 | "editor.background": "#1b1b1b",
12 | "panel.background": "#181818"
13 | }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/demo/README.md:
--------------------------------------------------------------------------------
1 | # Demo
2 |
3 | ## Install VS Code Remark extension (optional)
4 |
5 | ```sh
6 | code --install-extension unifiedjs.vscode-remark
7 | ```
8 |
9 | ## Bootstrap current demo project
10 |
11 | ```sh
12 | pnpm i
13 | ```
14 |
15 | ## Open content
16 |
17 | Let's open [./content](./content/) **markdowns** and **schemas** with VS Code:
18 |
19 | ```sh
20 | (cd ./content && code -r \
21 | creative-work.schema.yaml \
22 | correct-creative-work.md \
23 | broken-creative-work.md)
24 | ```
25 |
26 | ## Review problems
27 |
28 | ### Continuous linting with VS Code
29 |
30 | Toggle 'Problems' pane view (`⌘ + ⇧ + M` on mac)
31 |
32 | ### One-shot, full project linting with Command Line Interface
33 |
34 | ```
35 | pnpm remark .
36 | ```
37 |
38 | ## Edit content
39 |
40 | Play with schema and markdown frontmatter and see what happens in VS Code problem list!
41 |
42 | ## (Alt.) Custom pipeline
43 |
44 | Run `./pipeline.ts`:
45 |
46 | ```sh
47 | pnpm pipeline
48 | ```
49 |
50 | ---
51 |
52 | 🔗 [JulianCataldo.com](https://www.juliancataldo.com)
53 |
--------------------------------------------------------------------------------
/demo/content/creative-work.schema.yaml:
--------------------------------------------------------------------------------
1 | allOf:
2 | # (Optional) You can load an external definition file from relative path
3 | # Docs: https://github.com/JulianCataldo/remark-lint-frontmatter-schema#add-references-to-external-definitions-advanced
4 | - $ref: ./page.schema.yaml
5 |
6 | # "Creative work" props. will be merged with page schema props.
7 | - properties:
8 | category:
9 | type: string
10 | enum:
11 | - Book
12 | - Movie
13 | - Painting
14 | - Photo
15 | - Musical piece
16 | complex:
17 | type: object
18 | required:
19 | - time
20 | properties:
21 | time:
22 | type: number
23 | some:
24 | type: string
25 |
26 | required:
27 | - category
28 | - complex
29 | # #
30 | # # Or simply put your local properties directly:
31 | # properties:
32 | # category:
33 | # type: string
34 | # # ...
35 |
--------------------------------------------------------------------------------
/demo/content/creative-work/behind-the-gare-st-lazare__local-broken.md:
--------------------------------------------------------------------------------
1 | ---
2 | # E.g, with a full relative path
3 | '$schema': ../creative-work.schema.yaml
4 |
5 | # title: Behind the Gare St. Lazare, Paris, 1932
6 | category: Video game
7 | ---
8 |
9 | # Markdownish title-ish
10 |
11 | > …Frontmatter **Schema** _validation_…
12 |
13 | 1. ~~Head~~
14 | 2. ~~Shoulders~~
15 | 3. ~~Knees~~
16 |
17 | ---
18 |
19 | [me cleeck](http://perdu.com)
20 |
--------------------------------------------------------------------------------
/demo/content/creative-work/guten-nachte__local-correct.md:
--------------------------------------------------------------------------------
1 | ---
2 | # E.g, with a path relative to remark config root folder
3 | '$schema': ./content/creative-work.schema.yaml
4 |
5 | title: Guten Nachte
6 | category: Musical piece
7 |
8 | complex:
9 | time: 8
10 | ---
11 |
12 | # Markdownish title-ish
13 |
14 | > …Frontmatter **Schema** _validation_…
15 |
16 | 1. Head
17 | 2. Shoulders
18 | 3. Knees
19 |
20 | ---
21 |
22 | [WideWebWorld](./link.foo)
23 |
--------------------------------------------------------------------------------
/demo/content/creative-work/the-shipwreck__global-broken.md:
--------------------------------------------------------------------------------
1 | ---
2 | # Schema association for this file is defined in:
3 | # 🔗 `file:///./../../.remarkrc.mjs`
4 |
5 | complex:
6 | time: '1859'
7 |
8 | ## v—————— Must have a string title
9 | # title: My Great Title 🤩
10 |
11 | ## v—————— Must be `Book` | `Movie` | …
12 | category: Car
13 |
14 | description: ['not', 'an', 'array']
15 | ---
16 |
17 | # You're welcome!
18 |
19 | **Associated globally in [.remarkrc.mjs](.remarkrc.mjs)**
20 |
21 | …Frontmatter Schema Validation…
22 |
23 | ---
24 |
25 | © 2222 — ACME incorporated corporation reserved.
26 |
--------------------------------------------------------------------------------
/demo/content/page.schema.yaml:
--------------------------------------------------------------------------------
1 | title: Page
2 |
3 | properties:
4 | title:
5 | title: Title
6 | description: Used for SEO and tab title
7 | type: string
8 | maxLength: 80
9 |
10 | description:
11 | title: Description
12 | description: Used for SEO
13 | type: string
14 | maxLength: 300
15 |
16 | image:
17 | description: |
18 | Absolute or relative URL to image file.
19 | Used for hero header and page thumbnail.
20 | type: string
21 |
22 | required:
23 | - title
24 |
--------------------------------------------------------------------------------
/demo/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "demo",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "type": "module",
7 | "scripts": {
8 | "pipeline": "tsx pipeline.ts",
9 | "test": "echo \"Error: no test specified\" && exit 1"
10 | },
11 | "keywords": [],
12 | "author": "",
13 | "license": "ISC",
14 | "devDependencies": {
15 | "remark-cli": "12.0.0",
16 | "tsx": "^3.13.0"
17 | },
18 | "dependencies": {
19 | "remark": "15.0.1",
20 | "remark-frontmatter": "5.0.0",
21 | "remark-lint-frontmatter-schema": "latest",
22 | "vfile-reporter": "^8.1.0"
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/demo/pipeline.ts:
--------------------------------------------------------------------------------
1 | /* Launch with `pnpm pipeline` */
2 |
3 | import { remark } from 'remark';
4 | import remarkFrontmatter from 'remark-frontmatter';
5 | import remarkLintFrontmatterSchema from 'remark-lint-frontmatter-schema';
6 | import type { JSONSchema7 } from 'json-schema';
7 | import { reporter } from 'vfile-reporter';
8 | /* —————————————————————————————————————————————————————————————————————————— */
9 |
10 | const mySchema: JSONSchema7 = {
11 | allOf: [
12 | {
13 | /* Works with local / remote, YAML / JSON */
14 | $ref: './content/page.schema.yaml',
15 | // $ref: 'https://raw.githubusercontent.com/JulianCataldo/remark-lint-frontmatter-schema/master/demo/content/page.schema.yaml',
16 | },
17 | {
18 | properties: {
19 | baz: {
20 | type: 'string',
21 | },
22 | },
23 | },
24 | ],
25 | };
26 |
27 | /* ·········································································· */
28 |
29 | const mdContent = `---
30 | $schema: './content/creative-work.schema.yaml'
31 | title: 1234
32 | baz: ['wrong']
33 | ---
34 |
35 | # Hey !`;
36 |
37 | /* ·········································································· */
38 |
39 | console.log('Demo pipeline starting!…\n');
40 |
41 | const output = await remark()
42 | // Your pipeline (basic example)
43 | // …
44 | .use(remarkFrontmatter)
45 |
46 | .use(remarkLintFrontmatterSchema, {
47 | /* Bring your own schema */
48 | // embed: mySchema,
49 | //
50 | /* Override default AJV options */
51 | // ajvOptions: {
52 | // },
53 | /* —Or— just (local only) */
54 | // embed: {
55 | // ...mySchema,
56 | // },
57 | })
58 | .process(mdContent);
59 |
60 | /* ·········································································· */
61 |
62 | output.path = import.meta.url;
63 | // Or if you like, for easier referencing:
64 | // output.path = mySchema.$id ?? '';
65 |
66 | console.error(reporter([output]));
67 |
68 | /**
69 | * Yields:
70 | *
71 | * ```sh
72 | * file:///<...>/content/
73 | * 2:8-2:12 warning Keyword: type frontmatter-schema remark-lint
74 | * Type: string
75 | * Schema path: · ./content/page.schema.yaml/properties/title/type
76 | * 3:6-3:15 warning Keyword: type frontmatter-schema remark-lint
77 | * Type: string
78 | * Schema path: · #/allOf/1/properties/baz/type
79 | *
80 | * ⚠ 2 warnings
81 | * ```
82 | *
83 | */
84 |
--------------------------------------------------------------------------------
/docs/screenshot-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JulianCataldo/remark-lint-frontmatter-schema/5e2e246ae4c08d7939b829233f1713970d66e55c/docs/screenshot-2.png
--------------------------------------------------------------------------------
/docs/screenshot-3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JulianCataldo/remark-lint-frontmatter-schema/5e2e246ae4c08d7939b829233f1713970d66e55c/docs/screenshot-3.png
--------------------------------------------------------------------------------
/docs/screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JulianCataldo/remark-lint-frontmatter-schema/5e2e246ae4c08d7939b829233f1713970d66e55c/docs/screenshot.png
--------------------------------------------------------------------------------
/index.ts:
--------------------------------------------------------------------------------
1 | /* ——————————————————————————————————————————————————————————————————————————— *
2 | * © Julian Cataldo — https://www.juliancataldo.com. *
3 | * See LICENSE in the project root. *
4 | /* —————————————————————————————————————————————————————————————————————————— */
5 |
6 | /* eslint-disable max-lines */
7 | import path from 'node:path';
8 | import { existsSync } from 'node:fs';
9 | import { findUp } from 'find-up';
10 | // NOTE: minimatch@9 is breaking import.
11 | import { minimatch } from 'minimatch';
12 | /* ·········································································· */
13 | import yaml, { type Document, isNode, LineCounter } from 'yaml';
14 | import Ajv from 'ajv';
15 | import type { Options as AjvOptions, ErrorObject as AjvErrorObject } from 'ajv';
16 | import addFormats from 'ajv-formats';
17 | import $RefParser from '@apidevtools/json-schema-ref-parser';
18 | import type { JSONSchema7 } from 'json-schema';
19 | /* ·········································································· */
20 | import { lintRule } from 'unified-lint-rule';
21 | import type { VFile } from 'unified-lint-rule/lib';
22 | import type { Root, YAML } from 'mdast';
23 | import type { VFileMessage } from 'vfile-message';
24 | /* —————————————————————————————————————————————————————————————————————————— */
25 |
26 | const url = 'https://github.com/JulianCataldo/remark-lint-frontmatter-schema';
27 | const nativeJsErrorName = 'Markdown YAML frontmatter error (JSON Schema)';
28 |
29 | export interface Settings {
30 | /**
31 | * Global workspace file associations mapping (for linter extension).
32 | *
33 | * **Example**: `'schemas/thing.schema.yaml': ['content/things/*.md']`
34 | */
35 | schemas?: Record;
36 |
37 | /**
38 | * Direct schema embedding (for using inside an `unified` transform pipeline).
39 | *
40 | * Format: JSON Schema - draft-2019-09
41 | *
42 | * **Documentation**: https://ajv.js.org/json-schema.html#draft-07
43 | */
44 | embed?: JSONSchema7;
45 |
46 | /**
47 | * **Documentation**: https://ajv.js.org/options.html
48 | */
49 | ajvOptions?: AjvOptions;
50 | }
51 |
52 | // IDEA: Might be interesting to populate with corresponding error reference
53 | type JSONSchemaReference = 'https://ajv.js.org/json-schema.html';
54 |
55 | export interface FrontmatterSchemaMessage extends VFileMessage {
56 | schema: AjvErrorObject & { url: JSONSchemaReference };
57 | }
58 |
59 | interface FrontmatterObject {
60 | $schema: string | undefined;
61 | /* This is the typical Frontmatter object, as treated by common consumers */
62 | [key: string]: unknown;
63 | }
64 |
65 | /* ·········································································· */
66 |
67 | /* The vFile cwd isn't the same as the one from IDE extension.
68 | Extension will cascade upward from the current processed file and
69 | take the remarkrc file as its cwd. It's multi-level workspace
70 | friendly. We have to mimick this behavior here, as remark lint rules doesn't
71 | seems to offer an API to hook up on this? */
72 | async function getRemarkCwd(startDir: string) {
73 | const remarkConfigNames = [
74 | '.remarkrc',
75 | '.remarkrc.json',
76 | '.remarkrc.yaml',
77 | '.remarkrc.yml',
78 | '.remarkrc.mjs',
79 | '.remarkrc.js',
80 | '.remarkrc.cjs',
81 | ];
82 | const remarkConfPath = await findUp(remarkConfigNames, {
83 | cwd: path.dirname(startDir),
84 | });
85 | if (remarkConfPath) {
86 | return path.dirname(remarkConfPath);
87 | }
88 | return process.cwd();
89 | }
90 |
91 | /* ·········································································· */
92 |
93 | function pushErrors(
94 | errors: AjvErrorObject[],
95 | yamlDoc: Document,
96 | vFile: VFile,
97 | /** File path from local `$schema` key or from global settings */
98 | schemaRelPath: string,
99 | /** Used to map character range to line / column tuple */
100 | lineCounter: LineCounter,
101 | ) {
102 | errors.forEach((error) => {
103 | let reason = '';
104 |
105 | if (error.message) {
106 | /* Capitalize error message */
107 | const errMessage =
108 | `${error.message.charAt(0).toUpperCase()}` +
109 | `${error.message.substring(1)}`;
110 |
111 | let expected = '';
112 | if (Array.isArray(error.params.allowedValues)) {
113 | expected = `: \`${error.params.allowedValues.join('`, `')}\``;
114 | } else if (typeof error.params.allowedValue === 'string') {
115 | expected = `: \`${error.params.allowedValue}\``;
116 | }
117 | const sPath = schemaRelPath ? ` • ${schemaRelPath}` : '';
118 |
119 | reason = `${errMessage}${expected}${sPath} • ${error.schemaPath}`;
120 | }
121 |
122 | /* Explode AJV error instance path and get corresponding YAML AST node */
123 | const ajvPath = error.instancePath.substring(1).split('/');
124 | const node = yamlDoc.getIn(ajvPath, true);
125 |
126 | const message = vFile.message(reason);
127 |
128 | // FIXME: Doesn't seems to be used in custom pipeline?
129 | // Always returning `false`
130 | message.fatal = true;
131 |
132 | /* `name` comes from native JS `Error` object */
133 | message.name = nativeJsErrorName;
134 |
135 | /* Map YAML characters range to column / line positions,
136 | -OR- squiggling the opening frontmatter fence for **root** path errors */
137 | if (isNode(node)) {
138 | /* Incriminated token */
139 | message.actual = node.toString();
140 |
141 | /* Map AJV Range to VFile Position, via YAML lib. parser */
142 | if (node.range) {
143 | const OPENING_FENCE_LINE_COUNT = 1; /* Takes the `---` into account */
144 | const start = lineCounter.linePos(node.range[0]);
145 | const end = lineCounter.linePos(node.range[1]);
146 | message.position = {
147 | start: {
148 | line: start.line + OPENING_FENCE_LINE_COUNT,
149 | column: start.col,
150 | },
151 | end: {
152 | line: end.line + OPENING_FENCE_LINE_COUNT,
153 | column: end.col,
154 | },
155 | };
156 | // NOTE: Seems redundant, but otherwise, it is always set to 1:1 */
157 | message.line = message.position.start.line;
158 | message.column = message.position.start.column;
159 | }
160 | }
161 |
162 | /* Assemble pretty per-error insights for end-user */
163 | let note = `Keyword: ${error.keyword}`;
164 | if (Array.isArray(error.params.allowedValues)) {
165 | note += `\nAllowed values: ${error.params.allowedValues.join(', ')}`;
166 |
167 | /* Auto-fix replacement suggestions for `enum` */
168 | message.expected = error.params.allowedValues;
169 | } else if (typeof error.params.allowedValue === 'string') {
170 | note += `\nAllowed value: ${error.params.allowedValue}`;
171 |
172 | /* Auto-fix replacement suggestion for `const` */
173 | message.expected = [error.params.allowedValue];
174 | }
175 | if (typeof error.params.missingProperty === 'string') {
176 | note += `\nMissing property: ${error.params.missingProperty}`;
177 | }
178 | if (typeof error.params.type === 'string') {
179 | note += `\nType: ${error.params.type}`;
180 | }
181 | /* `schemaRelPath` path prefix will show up only when using
182 | file association, not when using pipeline embedded schema */
183 | note += `\nSchema path: ${schemaRelPath} · ${error.schemaPath}`;
184 | message.note = note;
185 | /* `message` comes from native JS `Error` object */
186 | message.message = reason;
187 |
188 | /* Adding custom data from AJV */
189 | /* It’s OK to store custom data directly on the VFileMessage:
190 | https://github.com/vfile/vfile-message#well-known-fields */
191 | // NOTE: Might be better to type `message` before, instead of asserting here
192 | (message as FrontmatterSchemaMessage).schema = {
193 | url: 'https://ajv.js.org/json-schema.html',
194 | ...error,
195 | };
196 | });
197 | }
198 |
199 | /* ·········································································· */
200 |
201 | async function validateFrontmatter(
202 | sourceYaml: YAML,
203 | vFile: VFile,
204 | settings: Settings,
205 | ) {
206 | const hasPropSchema = typeof settings.embed === 'object';
207 | const lineCounter = new LineCounter();
208 | let yamlDoc;
209 | let yamlJS;
210 | let hasLocalAssoc = false;
211 | let schemaPathFromCwd: string | undefined;
212 | const remarkCwd = await getRemarkCwd(vFile.path);
213 |
214 | /* Parse the YAML literal and get the YAML Abstract Syntax Tree,
215 | previously extracted by `remark-frontmatter` */
216 | try {
217 | yamlDoc = yaml.parseDocument(sourceYaml.value, { lineCounter });
218 | yamlJS = yamlDoc.toJS() as FrontmatterObject | null;
219 |
220 | /* Local `$schema` association takes precedence over global / prop. */
221 | if (yamlJS?.$schema && typeof yamlJS.$schema === 'string') {
222 | hasLocalAssoc = true;
223 | /* Fallback if it's an embedded schema (no `path`) */
224 | const vFilePath = vFile.path || '';
225 |
226 | /* From current processed file directory (e.g. `./foo…` or `../foo…`) */
227 | const dirFromCwd = path.isAbsolute(vFilePath)
228 | ? path.relative(process.cwd(), path.dirname(vFilePath))
229 | : path.dirname(vFilePath);
230 |
231 | const standardPath = path.join(dirFromCwd, yamlJS.$schema);
232 | if (existsSync(standardPath)) {
233 | schemaPathFromCwd = standardPath;
234 | } else {
235 | /* Non standard behavior, like TS / Vite, not JSON Schema resolution.
236 | Resolving `/my/path` or `my/path` from current remark project root */
237 | schemaPathFromCwd = path.join(remarkCwd, yamlJS.$schema);
238 | }
239 | }
240 | } catch (error) {
241 | if (error instanceof Error) {
242 | const banner = `YAML frontmatter parsing: ${schemaPathFromCwd ?? ''}`;
243 | vFile.message(`${banner} — ${error.name}: ${error.message}`);
244 | }
245 | }
246 |
247 | /* ········································································ */
248 |
249 | /* Global schemas associations, only if no local schema is set */
250 | if (yamlDoc && yamlJS && !hasLocalAssoc) {
251 | Object.entries(settings.schemas ?? {}).forEach(
252 | ([globSchemaPath, globSchemaAssocs]) => {
253 | /* Check if current markdown file is associated with this schema */
254 | globSchemaAssocs.forEach((mdFilePath) => {
255 | if (typeof mdFilePath === 'string') {
256 | const mdPathCleaned = path.normalize(mdFilePath);
257 |
258 | /* With `remark`, `vFile.path` is already relative to project root,
259 | while `eslint-plugin-mdx` gives an absolute path */
260 | const vFilePathRel = path.relative(remarkCwd, vFile.path);
261 |
262 | if (minimatch(vFilePathRel, mdPathCleaned)) {
263 | schemaPathFromCwd = path.join(remarkCwd, globSchemaPath);
264 | }
265 | }
266 | });
267 | },
268 | );
269 | }
270 |
271 | /* ········································································ */
272 |
273 | let schema: JSONSchema7 | undefined;
274 | if (hasPropSchema) {
275 | schema = settings.embed;
276 | } else if (schemaPathFromCwd) {
277 | /* Load schema + references */
278 | schema = await $RefParser
279 | // NOTE: Ext. `$refs` are embedded, not local defs.
280 | // Could be useful to embed ext. refs. in definitions,
281 | // so we could keep the ref. name for debugging?
282 | .bundle(schemaPathFromCwd)
283 | .catch((error) => {
284 | if (error instanceof Error) {
285 | const banner = `YAML schema file load/parse: ${
286 | schemaPathFromCwd ?? ''
287 | }`;
288 | vFile.message(`${banner} — ${error.name}: ${error.message}`);
289 | }
290 | return undefined;
291 | })
292 | /* Asserting then using a JSONSchema4 for AJV (JSONSchema7) is OK */
293 | .then((refSchema) =>
294 | refSchema ? (refSchema as JSONSchema7) : undefined,
295 | );
296 |
297 | /* Schema is now extracted,
298 | remove in-file `$schema` key, so it will not interfere later */
299 |
300 | if (hasLocalAssoc && yamlJS && typeof yamlJS.$schema === 'string') {
301 | delete yamlJS.$schema;
302 | }
303 | }
304 |
305 | /* ········································································ */
306 |
307 | /* We got an extracted schema to work with */
308 | if (schema && yamlDoc) {
309 | /* Setup AJV (Another JSON-Schema Validator) */
310 | const ajv = new Ajv({
311 | /* Defaults */
312 | allErrors: true /* So it doesn't stop at the first found error */,
313 | strict: false /* Prevents warnings for valid, but relaxed schemas */,
314 |
315 | /* User settings / overrides */
316 | ...settings.ajvOptions,
317 | });
318 | addFormats(ajv);
319 |
320 | /* JSON Schema compilation + validation with AJV */
321 | try {
322 | const validate = ajv.compile(schema);
323 | validate(yamlJS);
324 |
325 | /* Push JSON Schema validation failures messages */
326 | if (validate.errors?.length) {
327 | pushErrors(
328 | validate.errors,
329 | yamlDoc,
330 | vFile,
331 | schemaPathFromCwd ?? '',
332 | lineCounter,
333 | );
334 | }
335 | } catch (error) {
336 | if (error instanceof Error) {
337 | const banner = `JSON schema malformed: ${schemaPathFromCwd ?? ''}`;
338 | vFile.message(`${banner} — ${error.name}: ${error.message}`);
339 | }
340 | }
341 | }
342 | }
343 |
344 | /* ·········································································· */
345 |
346 | const remarkFrontmatterSchema = lintRule(
347 | {
348 | url,
349 | origin: 'remark-lint:frontmatter-schema',
350 | },
351 | async (ast: Root, vFile: VFile, settings: Settings = {}) => {
352 | if (ast.children.length) {
353 | /* Handle only if the processed Markdown file has a frontmatter section */
354 | const frontmatter = ast.children.find((child) => child.type === 'yaml');
355 | if (frontmatter?.type === 'yaml') {
356 | await validateFrontmatter(frontmatter, vFile, settings);
357 | }
358 | }
359 | },
360 | );
361 |
362 | export default remarkFrontmatterSchema;
363 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "remark-lint-frontmatter-schema",
3 | "version": "3.15.4",
4 | "description": "Validate Markdown frontmatter YAML against an associated JSON schema — remark-lint rule plugin",
5 | "keywords": [
6 | "lint",
7 | "yaml",
8 | "remark",
9 | "validation",
10 | "linting",
11 | "json-schema",
12 | "linter",
13 | "unified",
14 | "frontmatter",
15 | "remarkjs",
16 | "rule",
17 | "ajv",
18 | "markdown",
19 | "vscode",
20 | "tooling"
21 | ],
22 | "homepage": "https://github.com/JulianCataldo/remark-lint-frontmatter-schema",
23 | "bugs": {
24 | "url": "https://github.com/JulianCataldo/remark-lint-frontmatter-schema/issues",
25 | "email": "contact@juliancataldo.com"
26 | },
27 | "repository": {
28 | "type": "git",
29 | "url": "https://github.com/JulianCataldo/remark-lint-frontmatter-schema"
30 | },
31 | "license": "ISC",
32 | "author": "Julian Cataldo",
33 | "type": "module",
34 | "main": "./dist/index.js",
35 | "source": "./index.ts",
36 | "types": "./dist/index.d.ts",
37 | "exports": {
38 | ".": "./dist/index.js"
39 | },
40 | "files": [
41 | "dist/*",
42 | "index.ts"
43 | ],
44 | "scripts": {
45 | "build": "pnpm tsc",
46 | "dev": "pnpm tsc -w",
47 | "release": "semantic-release"
48 | },
49 | "dependencies": {
50 | "@apidevtools/json-schema-ref-parser": "^11.2.0",
51 | "ajv": "^8.12.0",
52 | "ajv-formats": "^2.1.1",
53 | "find-up": "^6.3.0",
54 | "minimatch": "^9.0.3",
55 | "unified-lint-rule": "^2.1.2",
56 | "yaml": "^2.3.3"
57 | },
58 | "devDependencies": {
59 | "@semantic-release/changelog": "6.0.3",
60 | "@semantic-release/git": "10.0.1",
61 | "@semantic-release/github": "9.2.1",
62 | "@semantic-release/npm": "11.0.0",
63 | "@types/eslint": "8.44.4",
64 | "@types/json-schema": "7.0.13",
65 | "@types/mdast": "3.0.13",
66 | "@types/minimatch": "5.1.2",
67 | "@types/node": "20.8.6",
68 | "@types/unist": "2.0.8",
69 | "@typescript-eslint/eslint-plugin": "6.7.5",
70 | "@typescript-eslint/parser": "6.7.5",
71 | "eslint": "8.51.0",
72 | "eslint-config-airbnb-base": "15.0.0",
73 | "eslint-config-airbnb-typescript": "17.1.0",
74 | "eslint-config-prettier": "9.0.0",
75 | "eslint-import-resolver-typescript": "3.6.1",
76 | "eslint-plugin-import": "2.28.1",
77 | "eslint-plugin-prettier": "5.0.1",
78 | "eslint-plugin-tsdoc": "0.2.17",
79 | "prettier": "3.0.3",
80 | "remark": "15.0.1",
81 | "remark-cli": "12.0.0",
82 | "semantic-release": "22.0.5",
83 | "typescript": "5.2.2",
84 | "unified": "10.1.2",
85 | "vfile-message": "3.1.4",
86 | "webdev-configs": "1.5.0"
87 | },
88 | "packageManager": "pnpm@8.5.1"
89 | }
90 |
--------------------------------------------------------------------------------
/renovate.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json",
3 | "extends": ["config:base"],
4 | "schedule": ["before 3am on the first day of the month"]
5 | }
6 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | // Enable top-level await, and other modern ESM features.
4 | "target": "ESNext",
5 | "module": "ESNext",
6 | // Enable node-style module resolution, for things like npm package imports.
7 | "moduleResolution": "node",
8 | // Enable JSON imports.
9 | "resolveJsonModule": true,
10 | // Enable stricter transpilation for better output.
11 | "isolatedModules": true,
12 | "types": ["node", "unist", "unified", "minimatch"],
13 |
14 | "allowJs": true,
15 |
16 | "noImplicitAny": true,
17 |
18 | "baseUrl": ".",
19 | "paths": {},
20 |
21 | "allowSyntheticDefaultImports": true,
22 |
23 | "strictNullChecks": true,
24 |
25 | "declaration": true,
26 | "declarationMap": true,
27 | "outDir": "./dist"
28 | },
29 |
30 | "include": ["./index.ts"]
31 | }
32 |
--------------------------------------------------------------------------------