diff --git a/eslint-local-rules/frontmatter-title.mjs b/eslint-local-rules/frontmatter-title.mjs new file mode 100644 index 00000000..f599ad3c --- /dev/null +++ b/eslint-local-rules/frontmatter-title.mjs @@ -0,0 +1,78 @@ +import matter from 'gray-matter'; + +export default { + meta: { + type: 'problem', + fixable: 'code', + messages: { + mismatch: 'Frontmatter title "{{fm}}" does not match H1 "{{h1}}".', + missingFrontmatterTitle: 'Missing frontmatter title.', + missingH1Title: 'Missing H1 title.', + }, + }, + + create(context) { + const { sourceCode } = context; + let h1Node = null; + + return { + heading(node) { + if (node.depth === 1 && !h1Node) h1Node = node; + }, + + 'root:exit'() { + const fmTitle = matter(sourceCode.text).data.title; + const h1Title = h1Node + ? sourceCode.getText(h1Node).replace(/^#\s+/, '').trim() + : null; + + if (!fmTitle && !h1Title) return; + + if (fmTitle && h1Title && fmTitle !== h1Title) { + context.report({ + node: h1Node, + messageId: 'mismatch', + data: { fm: fmTitle, h1: h1Title }, + fix(fixer) { + const match = /^title:.*$/m.exec(sourceCode.text); + if (match) { + return fixer.replaceTextRange( + [match.index, match.index + match[0].length], + `title: ${h1Title}` + ); + } + }, + }); + } else if (!fmTitle && h1Title) { + context.report({ + node: h1Node, + messageId: 'missingFrontmatterTitle', + fix(fixer) { + const hasFrontmatter = sourceCode.text.startsWith('---'); + if (hasFrontmatter) { + const match = /^---\r?\n/.exec(sourceCode.text); + if (match) { + return fixer.insertTextAfterRange( + [0, match[0].length], + `title: ${h1Title}\n` + ); + } + } else { + return fixer.insertTextBeforeRange( + [0, 0], + `---\ntitle: ${h1Title}\n---\n\n` + ); + } + }, + }); + } else if (!h1Title) { + // Missing H1, should put it manually. + context.report({ + loc: { line: 1, column: 0 }, + messageId: 'missingH1Title', + }); + } + }, + }; + }, +}; diff --git a/eslint.config.mjs b/eslint.config.mjs index 7aaf7509..1a5e1054 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -1,9 +1,15 @@ import js from '@eslint/js'; import globals from 'globals'; +import markdown from '@eslint/markdown'; +import frontmatterTitle from './eslint-local-rules/frontmatter-title.mjs'; export default [ - js.configs.recommended, { + ...js.configs.recommended, + files: ['**/*.js', '**/*.jsx', '**/*.mjs', '**/*.cjs'], + }, + { + files: ['**/*.js', '**/*.jsx', '**/*.mjs', '**/*.cjs'], languageOptions: { ecmaVersion: 'latest', sourceType: 'module', @@ -28,6 +34,26 @@ export default [ 'pages/docs/api', 'pages/docs/loaders', 'pages/docs/plugins', + 'pages/about/governance', + 'pages/404.md', + 'pages/index.md', + 'pages/blog/index.md', + 'pages/about/sponsors.md', ], }, + { + files: ['pages/**/*.md'], + plugins: { + markdown, + local: { + rules: { + 'frontmatter-title': frontmatterTitle, + }, + }, + }, + language: 'markdown/commonmark', + rules: { + 'local/frontmatter-title': 'error', + }, + }, ]; diff --git a/package-lock.json b/package-lock.json index ceac1875..7859bfb4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,6 +17,7 @@ }, "devDependencies": { "@eslint/js": "^10.0.1", + "@eslint/markdown": "^8.0.3", "eslint": "^10.4.0", "feed": "^6.0.0", "globals": "^17.6.0", @@ -447,6 +448,32 @@ } } }, + "node_modules/@eslint/markdown": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/@eslint/markdown/-/markdown-8.0.3.tgz", + "integrity": "sha512-rBTSSShrq7e4O+PWfeE4azH4/CWPNrC+VGxBXiW00o3vYVJnznsZiDayj3KC9JztIMVRZxZHn2nrDIUau/4j7A==", + "dev": true, + "license": "MIT", + "workspaces": [ + "examples/*" + ], + "dependencies": { + "@eslint/core": "^1.2.1", + "@eslint/plugin-kit": "^0.7.2", + "github-slugger": "^2.0.0", + "mdast-util-from-markdown": "^2.0.2", + "mdast-util-frontmatter": "^2.0.1", + "mdast-util-gfm": "^3.1.0", + "mdast-util-math": "^3.0.0", + "micromark-extension-frontmatter": "^2.0.0", + "micromark-extension-gfm": "^3.0.0", + "micromark-extension-math": "^3.1.0", + "micromark-util-normalize-identifier": "^2.0.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, "node_modules/@eslint/object-schema": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-3.0.5.tgz", @@ -3001,6 +3028,13 @@ "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", "license": "MIT" }, + "node_modules/@types/katex": { + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/@types/katex/-/katex-0.16.8.tgz", + "integrity": "sha512-trgaNyfU+Xh2Tc+ABIb44a5AYUpicB3uwirOioeOkNPPbmgRNtcWyDeeFRzjPZENO9Vq8gvVqfhaaXWLlevVwg==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/mdast": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", @@ -4833,6 +4867,20 @@ "reusify": "^1.0.4" } }, + "node_modules/fault": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fault/-/fault-2.0.1.tgz", + "integrity": "sha512-WtySTkS4OKev5JtpHXnib4Gxiurzh5NCGvWrFaZ34m6JehfTUhKZvn9njTfw48t6JumVQOmrKqpmGcdwxnhqBQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "format": "^0.2.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/fdir": { "version": "6.5.0", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", @@ -4943,6 +4991,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/format": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/format/-/format-0.2.2.tgz", + "integrity": "sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==", + "dev": true, + "engines": { + "node": ">=0.4.x" + } + }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", @@ -6259,6 +6316,33 @@ "dev": true, "license": "MIT" }, + "node_modules/katex": { + "version": "0.16.47", + "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.47.tgz", + "integrity": "sha512-Eeo8Ys1doU1z+x8AZsPpQu+p/QcZBI5PeOo7QGQdy2x2m0MU/hYagBbGOmXwr5KVbEfVuWv9LpnQWeehogurjg==", + "dev": true, + "funding": [ + "https://opencollective.com/katex", + "https://github.com/sponsors/katex" + ], + "license": "MIT", + "dependencies": { + "commander": "^8.3.0" + }, + "bin": { + "katex": "cli.js" + } + }, + "node_modules/katex/node_modules/commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -6935,6 +7019,38 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/mdast-util-frontmatter": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-frontmatter/-/mdast-util-frontmatter-2.0.1.tgz", + "integrity": "sha512-LRqI9+wdgC25P0URIJY9vwocIzCcksduHQ9OF2joxQoyTNVduwLAFUzjoopuRJbJAReaKrNQKAZKL3uCMugWJA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "escape-string-regexp": "^5.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "micromark-extension-frontmatter": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-frontmatter/node_modules/escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/mdast-util-gfm": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-3.1.0.tgz", @@ -7036,6 +7152,26 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/mdast-util-math": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-math/-/mdast-util-math-3.0.0.tgz", + "integrity": "sha512-Tl9GBNeG/AhJnQM221bJR2HPvLOSnLE/T9cJI9tlc6zwQk2nPk/4f0cHkOdEixQPC/j8UtKDdITswvLAy1OZ1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "longest-streak": "^3.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.1.0", + "unist-util-remove-position": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/mdast-util-mdx": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/mdast-util-mdx/-/mdast-util-mdx-3.0.0.tgz", @@ -7335,6 +7471,23 @@ "micromark-util-types": "^2.0.0" } }, + "node_modules/micromark-extension-frontmatter": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-frontmatter/-/micromark-extension-frontmatter-2.0.0.tgz", + "integrity": "sha512-C4AkuM3dA58cgZha7zVnuVxBhDsbttIMiytjgsM2XbHAB2faRVaHRle40558FBN+DJcrLNCoqG5mlrpdU4cRtg==", + "dev": true, + "license": "MIT", + "dependencies": { + "fault": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/micromark-extension-gfm": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-3.0.0.tgz", @@ -7456,6 +7609,26 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/micromark-extension-math": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-math/-/micromark-extension-math-3.1.0.tgz", + "integrity": "sha512-lvEqd+fHjATVs+2v/8kg9i5Q0AP2k85H0WUOwpIVvUML8BapsMvh1XAogmQjOCsLpoKRCVQqEkQBB3NhVBcsOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/katex": "^0.16.0", + "devlop": "^1.0.0", + "katex": "^0.16.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/micromark-extension-mdx-expression": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/micromark-extension-mdx-expression/-/micromark-extension-mdx-expression-3.0.1.tgz", @@ -8772,7 +8945,6 @@ "resolved": "https://registry.npmjs.org/preact-render-to-string/-/preact-render-to-string-6.7.0.tgz", "integrity": "sha512-Z4WR8fmLMRpdYqJ9i7vrlXSsSrxVJydwrkEXHapexfARbWfGb7vGcnvNQnIzN0cXciMVOlz/XLoiMCi9gUsy9Q==", "license": "MIT", - "peer": true, "peerDependencies": { "preact": ">=10 || >= 11.0.0-0" } @@ -10872,6 +11044,21 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/unist-util-remove-position": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-5.0.0.tgz", + "integrity": "sha512-Hp5Kh3wLxv0PHj9m2yZhhLt58KzPtEYKQQ4yxfYFEO7EvHwzyDYnduhHnY1mDxoqr7VUwVuHXk9RXKIiYS1N8Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-visit": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/unist-util-select": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/unist-util-select/-/unist-util-select-5.1.0.tgz", diff --git a/package.json b/package.json index fbaed220..8d7fa6e9 100644 --- a/package.json +++ b/package.json @@ -34,6 +34,7 @@ }, "devDependencies": { "@eslint/js": "^10.0.1", + "@eslint/markdown": "^8.0.3", "eslint": "^10.4.0", "feed": "^6.0.0", "globals": "^17.6.0", diff --git a/pages/guides/tooling-and-integration/typescript.md b/pages/guides/tooling-and-integration/typescript.md index 4659df3a..4e6553f9 100644 --- a/pages/guides/tooling-and-integration/typescript.md +++ b/pages/guides/tooling-and-integration/typescript.md @@ -1,6 +1,6 @@ --- -title: Typescript -authors: morsdyce,kkamali,mtrivera,byzyk,EugeneHlushko,chenxsan,snitin315,tusharthakur04,ThierryRakotomanana,valentina-buoro,avivkeller +title: TypeScript +authors: 'morsdyce,kkamali,mtrivera,byzyk,EugeneHlushko,chenxsan,snitin315,tusharthakur04,ThierryRakotomanana,valentina-buoro,avivkeller' --- # TypeScript