From 0e79b65cc7ec6cba67291737f12b68f6f739b551 Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Sat, 11 Nov 2023 17:47:26 +0100 Subject: [PATCH] Refactor code style in scripts --- doc/create-a-custom-rule.md | 89 +- package.json | 15 +- .../readme.md | 18 +- .../readme.md | 18 +- .../readme.md | 18 +- .../remark-lint-code-block-style/readme.md | 18 +- .../remark-lint-definition-case/readme.md | 18 +- .../remark-lint-definition-spacing/readme.md | 18 +- .../remark-lint-emphasis-marker/readme.md | 18 +- .../remark-lint-fenced-code-flag/readme.md | 18 +- .../remark-lint-fenced-code-marker/readme.md | 98 +- packages/remark-lint-file-extension/readme.md | 18 +- .../remark-lint-final-definition/readme.md | 18 +- packages/remark-lint-final-newline/readme.md | 18 +- .../remark-lint-first-heading-level/readme.md | 18 +- .../remark-lint-hard-break-spaces/readme.md | 18 +- .../remark-lint-heading-increment/readme.md | 18 +- packages/remark-lint-heading-style/readme.md | 18 +- .../remark-lint-linebreak-style/readme.md | 18 +- .../remark-lint-link-title-style/readme.md | 18 +- .../readme.md | 18 +- .../readme.md | 18 +- .../remark-lint-list-item-indent/readme.md | 54 +- .../remark-lint-list-item-spacing/readme.md | 18 +- .../readme.md | 18 +- .../remark-lint-maximum-line-length/readme.md | 18 +- .../readme.md | 18 +- .../readme.md | 18 +- .../readme.md | 18 +- .../readme.md | 18 +- .../readme.md | 18 +- .../readme.md | 18 +- .../readme.md | 18 +- packages/remark-lint-no-empty-url/readme.md | 18 +- .../readme.md | 18 +- .../readme.md | 18 +- .../readme.md | 34 +- .../readme.md | 18 +- .../readme.md | 18 +- .../readme.md | 18 +- .../remark-lint-no-heading-indent/readme.md | 18 +- .../readme.md | 18 +- .../readme.md | 46 +- packages/remark-lint-no-html/readme.md | 18 +- .../remark-lint-no-inline-padding/readme.md | 18 +- .../remark-lint-no-literal-urls/readme.md | 18 +- .../readme.md | 18 +- .../readme.md | 18 +- .../index.js | 1 - .../package.json | 3 +- .../readme.md | 18 +- .../readme.md | 18 +- .../remark-lint-no-shell-dollars/readme.md | 18 +- .../readme.md | 18 +- .../readme.md | 18 +- .../readme.md | 18 +- packages/remark-lint-no-tabs/readme.md | 18 +- .../index.js | 4 - .../package.json | 2 + .../readme.md | 82 +- .../readme.md | 18 +- .../readme.md | 18 +- .../readme.md | 18 +- .../readme.md | 50 +- .../readme.md | 86 +- packages/remark-lint-rule-style/readme.md | 18 +- .../readme.md | 18 +- packages/remark-lint-strong-marker/readme.md | 46 +- .../remark-lint-table-cell-padding/readme.md | 108 +- .../readme.md | 18 +- packages/remark-lint-table-pipes/readme.md | 18 +- .../readme.md | 52 +- .../remark-preset-lint-consistent/readme.md | 16 +- .../readme.md | 16 +- .../remark-preset-lint-recommended/readme.md | 16 +- readme.md | 54 +- script/build-plugins.js | 919 +++++++++++ script/build-presets.js | 1372 ++++++++--------- script/build-rules.js | 967 ------------ script/info.js | 253 +++ script/plugin/list-of-plugins.js | 85 + script/plugin/list-of-presets.js | 110 +- script/plugin/list-of-rules.js | 67 - script/util/presets.js | 76 - script/util/repo-url.js | 38 - script/util/rule.js | 140 -- script/util/rules.js | 13 - test.js | 590 ++++--- tsconfig.json | 2 +- 89 files changed, 3128 insertions(+), 3384 deletions(-) create mode 100644 script/build-plugins.js delete mode 100644 script/build-rules.js create mode 100644 script/info.js create mode 100644 script/plugin/list-of-plugins.js delete mode 100644 script/plugin/list-of-rules.js delete mode 100644 script/util/presets.js delete mode 100644 script/util/repo-url.js delete mode 100755 script/util/rule.js delete mode 100644 script/util/rules.js diff --git a/doc/create-a-custom-rule.md b/doc/create-a-custom-rule.md index 9029002..9d6ea1b 100644 --- a/doc/create-a-custom-rule.md +++ b/doc/create-a-custom-rule.md @@ -58,9 +58,14 @@ touch .remarkrc.js ```js // .remarkrc.js -module.exports = { - plugins: [] -} +/** + * @typedef {import('unified').Preset} Preset + */ + +/** @type {Preset} */ +const preset = {plugins: []} + +export default preset ``` Then, in our `package.json`, add the following script to process all the @@ -142,7 +147,7 @@ import {lintRule} from 'unified-lint-rule' const remarkLintNoGifAllowed = lintRule( 'remark-lint:no-gif-allowed', - (tree, file, options) => { + function (tree, file, options) { // Rule implementation } ) @@ -155,9 +160,16 @@ namespace. If your project was named `my-project`, then you can export your rule as: ```js -const remarkLintNoGifAllowed = lintRule('my-project-name:no-gif-allowed', () => {}) +const remarkLintNoGifAllowed = lintRule( + 'my-project-name:no-gif-allowed', + function () {} +) + // Or: -const remarkLintNoGifAllowed = lintRule('my-npm-published-package:no-gif-allowed', () => {}) +const remarkLintNoGifAllowed = lintRule( + 'my-npm-published-package:no-gif-allowed', + function () {} +) ``` This can help you when wanting to create a group of rules under the same @@ -168,7 +180,7 @@ This can help you when wanting to create a group of rules under the same Your rule function will receive three arguments: ```js -(tree, file, options) => {} +function rule(tree, file, options) {} ``` * `tree` (*required*): [mdast][] @@ -187,38 +199,36 @@ recursively inspect all the image nodes, and nodes that we have generated ourselves and do not belong to the `doc.md`. ```js -import {lintRule} from 'unified-lint-rule' -import {visit} from 'unist-visit-util' -import {generated} from 'unist-util-generated' +/** + * @typedef {import('mdast').Root} Root + */ -function isValidNode(node) { - // Here we check whether the given node violates our rule. - // Implementation details are not relevant to the scope of this example. - // This is an overly simplified solution for demonstration purposes - if (node.url && typeof node.url === 'string') { - return !node.url.endsWith('.gif') - } -} +import {lintRule} from 'unified-lint-rule' +import {visit} from 'unist-util-visit' const remarkLintNoGifAllowed = lintRule( 'remark-lint:no-gif-allowed', - (tree, file, options) => { - visit(tree, 'image', (node) => { - if (!generated(node)) { - // This is an extremely simplified example of how to structure - // the logic to check whether a node violates your rule. - // You have complete freedom over how to visit/inspect the tree, - // and on how to implement the validation logic for your node. - const isValid = isValidNode(node) + /** + * @param {Root} tree + * Tree. + * @returns {undefined} + * Nothing. + */ + function (tree, file, options) { + visit(tree, 'image', function (node) { + // This is an extremely simplified example of how to structure + // the logic to check whether a node violates your rule. + // You have complete freedom over how to visit/inspect the tree, + // and on how to implement the validation logic for your node. + const isValid = !node.url.endsWith('.gif') - if (!isValid) { - // Remember to provide the node as second argument to the message, - // in order to obtain the position and column where the violation occurred. - file.message( - 'Invalid image file extensions. Please do not use gifs', - node - ) - } + if (!isValid) { + // Remember to provide the node as second argument to the message, + // in order to obtain the position and column where the violation occurred. + file.message( + 'Invalid image file extensions. Please do not use gifs', + node + ) } }) } @@ -236,13 +246,14 @@ You can do that by importing your rule and adding it in `plugins` array: ```js // .remarkrc.js +/** + * @typedef {import('unified').Preset} Preset + */ + import remarkLintNoGifAllowed from './rules/no-gif-allowed.js' -const plugins = { - plugins: [remarkLintNoGifAllowed] -} - -const preset = {plugins} +/** @type {Preset} */ +const preset = {plugins: [remarkLintNoGifAllowed]} export default preset ``` diff --git a/package.json b/package.json index dc39bd7..5d44ce9 100644 --- a/package.json +++ b/package.json @@ -116,7 +116,11 @@ "c8": "^8.0.0", "comment-parser": "^1.0.0", "github-slugger": "^2.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-gfm": "^3.0.0", + "mdast-util-to-markdown": "^2.0.0", "mdast-zone": "^6.0.0", + "micromark-extension-gfm": "^3.0.0", "parse-author": "^2.0.0", "prettier": "^3.0.0", "remark": "^15.0.0", @@ -127,16 +131,16 @@ "remark-toc": "^9.0.0", "remark-validate-links": "^13.0.0", "strip-indent": "^4.0.0", - "to-vfile": "^8.0.0", "type-coverage": "^2.0.0", "type-fest": "^4.0.0", "typescript": "^5.0.0", "unist-builder": "^4.0.0", "unist-util-remove-position": "^5.0.0", + "vfile": "^6.0.0", "xo": "^0.56.0" }, "scripts": { - "generate": "node --conditions development script/build-presets.js && node --conditions development script/build-rules.js", + "generate": "node --conditions development script/build-plugins.js && node --conditions development script/build-presets.js", "build": "tsc --build --clean && tsc --build && type-coverage", "format": "remark . --frail --output --quiet && prettier . --log-level warn --write && xo --fix", "test": "npm run build && npm run generate && npm run format && npm run test-coverage", @@ -174,8 +178,8 @@ "remark-lint-list-item-indent", "space" ], - "./script/plugin/list-of-presets.js", - "./script/plugin/list-of-rules.js" + "./script/plugin/list-of-plugins.js", + "./script/plugin/list-of-presets.js" ] }, "typeCoverage": { @@ -193,7 +197,8 @@ ], "rules": { "max-depth": "off", - "no-await-in-loop": "off" + "no-await-in-loop": "off", + "unicorn/no-array-callback-reference": "off" } } ], diff --git a/packages/remark-lint-blockquote-indentation/readme.md b/packages/remark-lint-blockquote-indentation/readme.md index ad960ce..d4ce897 100644 --- a/packages/remark-lint-blockquote-indentation/readme.md +++ b/packages/remark-lint-blockquote-indentation/readme.md @@ -77,22 +77,20 @@ In browsers with [`esm.sh`][esmsh]: On the API: ```js -import {read} from 'to-vfile' -import {reporter} from 'vfile-reporter' import {remark} from 'remark' import remarkLint from 'remark-lint' import remarkLintBlockquoteIndentation from 'remark-lint-blockquote-indentation' +import {read} from 'to-vfile' +import {reporter} from 'vfile-reporter' -main() +const file = await read('example.md') -async function main() { - const file = await remark() - .use(remarkLint) - .use(remarkLintBlockquoteIndentation) - .process(await read('example.md')) +await remark() + .use(remarkLint) + .use(remarkLintBlockquoteIndentation) + .process(file) - console.error(reporter(file)) -} +console.error(reporter(file)) ``` On the CLI: diff --git a/packages/remark-lint-checkbox-character-style/readme.md b/packages/remark-lint-checkbox-character-style/readme.md index 6b431a6..1fd022d 100644 --- a/packages/remark-lint-checkbox-character-style/readme.md +++ b/packages/remark-lint-checkbox-character-style/readme.md @@ -76,22 +76,20 @@ In browsers with [`esm.sh`][esmsh]: On the API: ```js -import {read} from 'to-vfile' -import {reporter} from 'vfile-reporter' import {remark} from 'remark' import remarkLint from 'remark-lint' import remarkLintCheckboxCharacterStyle from 'remark-lint-checkbox-character-style' +import {read} from 'to-vfile' +import {reporter} from 'vfile-reporter' -main() +const file = await read('example.md') -async function main() { - const file = await remark() - .use(remarkLint) - .use(remarkLintCheckboxCharacterStyle) - .process(await read('example.md')) +await remark() + .use(remarkLint) + .use(remarkLintCheckboxCharacterStyle) + .process(file) - console.error(reporter(file)) -} +console.error(reporter(file)) ``` On the CLI: diff --git a/packages/remark-lint-checkbox-content-indent/readme.md b/packages/remark-lint-checkbox-content-indent/readme.md index 8de9386..fa23edd 100644 --- a/packages/remark-lint-checkbox-content-indent/readme.md +++ b/packages/remark-lint-checkbox-content-indent/readme.md @@ -72,22 +72,20 @@ In browsers with [`esm.sh`][esmsh]: On the API: ```js -import {read} from 'to-vfile' -import {reporter} from 'vfile-reporter' import {remark} from 'remark' import remarkLint from 'remark-lint' import remarkLintCheckboxContentIndent from 'remark-lint-checkbox-content-indent' +import {read} from 'to-vfile' +import {reporter} from 'vfile-reporter' -main() +const file = await read('example.md') -async function main() { - const file = await remark() - .use(remarkLint) - .use(remarkLintCheckboxContentIndent) - .process(await read('example.md')) +await remark() + .use(remarkLint) + .use(remarkLintCheckboxContentIndent) + .process(file) - console.error(reporter(file)) -} +console.error(reporter(file)) ``` On the CLI: diff --git a/packages/remark-lint-code-block-style/readme.md b/packages/remark-lint-code-block-style/readme.md index 720bf60..11a3253 100644 --- a/packages/remark-lint-code-block-style/readme.md +++ b/packages/remark-lint-code-block-style/readme.md @@ -75,22 +75,20 @@ In browsers with [`esm.sh`][esmsh]: On the API: ```js -import {read} from 'to-vfile' -import {reporter} from 'vfile-reporter' import {remark} from 'remark' import remarkLint from 'remark-lint' import remarkLintCodeBlockStyle from 'remark-lint-code-block-style' +import {read} from 'to-vfile' +import {reporter} from 'vfile-reporter' -main() +const file = await read('example.md') -async function main() { - const file = await remark() - .use(remarkLint) - .use(remarkLintCodeBlockStyle) - .process(await read('example.md')) +await remark() + .use(remarkLint) + .use(remarkLintCodeBlockStyle) + .process(file) - console.error(reporter(file)) -} +console.error(reporter(file)) ``` On the CLI: diff --git a/packages/remark-lint-definition-case/readme.md b/packages/remark-lint-definition-case/readme.md index fd349d2..21f38dd 100644 --- a/packages/remark-lint-definition-case/readme.md +++ b/packages/remark-lint-definition-case/readme.md @@ -74,22 +74,20 @@ In browsers with [`esm.sh`][esmsh]: On the API: ```js -import {read} from 'to-vfile' -import {reporter} from 'vfile-reporter' import {remark} from 'remark' import remarkLint from 'remark-lint' import remarkLintDefinitionCase from 'remark-lint-definition-case' +import {read} from 'to-vfile' +import {reporter} from 'vfile-reporter' -main() +const file = await read('example.md') -async function main() { - const file = await remark() - .use(remarkLint) - .use(remarkLintDefinitionCase) - .process(await read('example.md')) +await remark() + .use(remarkLint) + .use(remarkLintDefinitionCase) + .process(file) - console.error(reporter(file)) -} +console.error(reporter(file)) ``` On the CLI: diff --git a/packages/remark-lint-definition-spacing/readme.md b/packages/remark-lint-definition-spacing/readme.md index c4707cc..a0e614e 100644 --- a/packages/remark-lint-definition-spacing/readme.md +++ b/packages/remark-lint-definition-spacing/readme.md @@ -75,22 +75,20 @@ In browsers with [`esm.sh`][esmsh]: On the API: ```js -import {read} from 'to-vfile' -import {reporter} from 'vfile-reporter' import {remark} from 'remark' import remarkLint from 'remark-lint' import remarkLintDefinitionSpacing from 'remark-lint-definition-spacing' +import {read} from 'to-vfile' +import {reporter} from 'vfile-reporter' -main() +const file = await read('example.md') -async function main() { - const file = await remark() - .use(remarkLint) - .use(remarkLintDefinitionSpacing) - .process(await read('example.md')) +await remark() + .use(remarkLint) + .use(remarkLintDefinitionSpacing) + .process(file) - console.error(reporter(file)) -} +console.error(reporter(file)) ``` On the CLI: diff --git a/packages/remark-lint-emphasis-marker/readme.md b/packages/remark-lint-emphasis-marker/readme.md index 3fd963f..6317e74 100644 --- a/packages/remark-lint-emphasis-marker/readme.md +++ b/packages/remark-lint-emphasis-marker/readme.md @@ -75,22 +75,20 @@ In browsers with [`esm.sh`][esmsh]: On the API: ```js -import {read} from 'to-vfile' -import {reporter} from 'vfile-reporter' import {remark} from 'remark' import remarkLint from 'remark-lint' import remarkLintEmphasisMarker from 'remark-lint-emphasis-marker' +import {read} from 'to-vfile' +import {reporter} from 'vfile-reporter' -main() +const file = await read('example.md') -async function main() { - const file = await remark() - .use(remarkLint) - .use(remarkLintEmphasisMarker) - .process(await read('example.md')) +await remark() + .use(remarkLint) + .use(remarkLintEmphasisMarker) + .process(file) - console.error(reporter(file)) -} +console.error(reporter(file)) ``` On the CLI: diff --git a/packages/remark-lint-fenced-code-flag/readme.md b/packages/remark-lint-fenced-code-flag/readme.md index 539d922..ff3e4bf 100644 --- a/packages/remark-lint-fenced-code-flag/readme.md +++ b/packages/remark-lint-fenced-code-flag/readme.md @@ -74,22 +74,20 @@ In browsers with [`esm.sh`][esmsh]: On the API: ```js -import {read} from 'to-vfile' -import {reporter} from 'vfile-reporter' import {remark} from 'remark' import remarkLint from 'remark-lint' import remarkLintFencedCodeFlag from 'remark-lint-fenced-code-flag' +import {read} from 'to-vfile' +import {reporter} from 'vfile-reporter' -main() +const file = await read('example.md') -async function main() { - const file = await remark() - .use(remarkLint) - .use(remarkLintFencedCodeFlag) - .process(await read('example.md')) +await remark() + .use(remarkLint) + .use(remarkLintFencedCodeFlag) + .process(file) - console.error(reporter(file)) -} +console.error(reporter(file)) ``` On the CLI: diff --git a/packages/remark-lint-fenced-code-marker/readme.md b/packages/remark-lint-fenced-code-marker/readme.md index 42370b1..571f3a9 100644 --- a/packages/remark-lint-fenced-code-marker/readme.md +++ b/packages/remark-lint-fenced-code-marker/readme.md @@ -75,22 +75,20 @@ In browsers with [`esm.sh`][esmsh]: On the API: ```js -import {read} from 'to-vfile' -import {reporter} from 'vfile-reporter' import {remark} from 'remark' import remarkLint from 'remark-lint' import remarkLintFencedCodeMarker from 'remark-lint-fenced-code-marker' +import {read} from 'to-vfile' +import {reporter} from 'vfile-reporter' -main() +const file = await read('example.md') -async function main() { - const file = await remark() - .use(remarkLint) - .use(remarkLintFencedCodeMarker) - .process(await read('example.md')) +await remark() + .use(remarkLint) + .use(remarkLintFencedCodeMarker) + .process(file) - console.error(reporter(file)) -} +console.error(reporter(file)) ``` On the CLI: @@ -162,46 +160,6 @@ Indented code blocks are not affected by this rule: No messages. -##### `not-ok-consistent-tick.md` - -###### In - -````markdown -```alpha -bravo() -``` - -~~~ -charlie() -~~~ -```` - -###### Out - -```text -5:1-7:4: Fenced code should use `` ` `` as a marker -``` - -##### `not-ok-consistent-tilde.md` - -###### In - -````markdown -~~~alpha -bravo() -~~~ - -``` -charlie() -``` -```` - -###### Out - -```text -5:1-7:4: Fenced code should use `~` as a marker -``` - ##### `ok.md` When configured with ``'`'``. @@ -242,6 +200,46 @@ charlie() No messages. +##### `not-ok-consistent-tick.md` + +###### In + +````markdown +```alpha +bravo() +``` + +~~~ +charlie() +~~~ +```` + +###### Out + +```text +5:1-7:4: Fenced code should use `` ` `` as a marker +``` + +##### `not-ok-consistent-tilde.md` + +###### In + +````markdown +~~~alpha +bravo() +~~~ + +``` +charlie() +``` +```` + +###### Out + +```text +5:1-7:4: Fenced code should use `~` as a marker +``` + ##### `not-ok-incorrect.md` When configured with `'💩'`. diff --git a/packages/remark-lint-file-extension/readme.md b/packages/remark-lint-file-extension/readme.md index 8472ac0..47e79fe 100644 --- a/packages/remark-lint-file-extension/readme.md +++ b/packages/remark-lint-file-extension/readme.md @@ -73,22 +73,20 @@ In browsers with [`esm.sh`][esmsh]: On the API: ```js -import {read} from 'to-vfile' -import {reporter} from 'vfile-reporter' import {remark} from 'remark' import remarkLint from 'remark-lint' import remarkLintFileExtension from 'remark-lint-file-extension' +import {read} from 'to-vfile' +import {reporter} from 'vfile-reporter' -main() +const file = await read('example.md') -async function main() { - const file = await remark() - .use(remarkLint) - .use(remarkLintFileExtension) - .process(await read('example.md')) +await remark() + .use(remarkLint) + .use(remarkLintFileExtension) + .process(file) - console.error(reporter(file)) -} +console.error(reporter(file)) ``` On the CLI: diff --git a/packages/remark-lint-final-definition/readme.md b/packages/remark-lint-final-definition/readme.md index 0bbb23d..bd9ce84 100644 --- a/packages/remark-lint-final-definition/readme.md +++ b/packages/remark-lint-final-definition/readme.md @@ -75,22 +75,20 @@ In browsers with [`esm.sh`][esmsh]: On the API: ```js -import {read} from 'to-vfile' -import {reporter} from 'vfile-reporter' import {remark} from 'remark' import remarkLint from 'remark-lint' import remarkLintFinalDefinition from 'remark-lint-final-definition' +import {read} from 'to-vfile' +import {reporter} from 'vfile-reporter' -main() +const file = await read('example.md') -async function main() { - const file = await remark() - .use(remarkLint) - .use(remarkLintFinalDefinition) - .process(await read('example.md')) +await remark() + .use(remarkLint) + .use(remarkLintFinalDefinition) + .process(file) - console.error(reporter(file)) -} +console.error(reporter(file)) ``` On the CLI: diff --git a/packages/remark-lint-final-newline/readme.md b/packages/remark-lint-final-newline/readme.md index 5a98782..174745e 100644 --- a/packages/remark-lint-final-newline/readme.md +++ b/packages/remark-lint-final-newline/readme.md @@ -74,22 +74,20 @@ In browsers with [`esm.sh`][esmsh]: On the API: ```js -import {read} from 'to-vfile' -import {reporter} from 'vfile-reporter' import {remark} from 'remark' import remarkLint from 'remark-lint' import remarkLintFinalNewline from 'remark-lint-final-newline' +import {read} from 'to-vfile' +import {reporter} from 'vfile-reporter' -main() +const file = await read('example.md') -async function main() { - const file = await remark() - .use(remarkLint) - .use(remarkLintFinalNewline) - .process(await read('example.md')) +await remark() + .use(remarkLint) + .use(remarkLintFinalNewline) + .process(file) - console.error(reporter(file)) -} +console.error(reporter(file)) ``` On the CLI: diff --git a/packages/remark-lint-first-heading-level/readme.md b/packages/remark-lint-first-heading-level/readme.md index b61557e..f966917 100644 --- a/packages/remark-lint-first-heading-level/readme.md +++ b/packages/remark-lint-first-heading-level/readme.md @@ -69,22 +69,20 @@ In browsers with [`esm.sh`][esmsh]: On the API: ```js -import {read} from 'to-vfile' -import {reporter} from 'vfile-reporter' import {remark} from 'remark' import remarkLint from 'remark-lint' import remarkLintFirstHeadingLevel from 'remark-lint-first-heading-level' +import {read} from 'to-vfile' +import {reporter} from 'vfile-reporter' -main() +const file = await read('example.md') -async function main() { - const file = await remark() - .use(remarkLint) - .use(remarkLintFirstHeadingLevel) - .process(await read('example.md')) +await remark() + .use(remarkLint) + .use(remarkLintFirstHeadingLevel) + .process(file) - console.error(reporter(file)) -} +console.error(reporter(file)) ``` On the CLI: diff --git a/packages/remark-lint-hard-break-spaces/readme.md b/packages/remark-lint-hard-break-spaces/readme.md index 0cad3ab..385a0df 100644 --- a/packages/remark-lint-hard-break-spaces/readme.md +++ b/packages/remark-lint-hard-break-spaces/readme.md @@ -76,22 +76,20 @@ In browsers with [`esm.sh`][esmsh]: On the API: ```js -import {read} from 'to-vfile' -import {reporter} from 'vfile-reporter' import {remark} from 'remark' import remarkLint from 'remark-lint' import remarkLintHardBreakSpaces from 'remark-lint-hard-break-spaces' +import {read} from 'to-vfile' +import {reporter} from 'vfile-reporter' -main() +const file = await read('example.md') -async function main() { - const file = await remark() - .use(remarkLint) - .use(remarkLintHardBreakSpaces) - .process(await read('example.md')) +await remark() + .use(remarkLint) + .use(remarkLintHardBreakSpaces) + .process(file) - console.error(reporter(file)) -} +console.error(reporter(file)) ``` On the CLI: diff --git a/packages/remark-lint-heading-increment/readme.md b/packages/remark-lint-heading-increment/readme.md index b7a6816..9e070b9 100644 --- a/packages/remark-lint-heading-increment/readme.md +++ b/packages/remark-lint-heading-increment/readme.md @@ -75,22 +75,20 @@ In browsers with [`esm.sh`][esmsh]: On the API: ```js -import {read} from 'to-vfile' -import {reporter} from 'vfile-reporter' import {remark} from 'remark' import remarkLint from 'remark-lint' import remarkLintHeadingIncrement from 'remark-lint-heading-increment' +import {read} from 'to-vfile' +import {reporter} from 'vfile-reporter' -main() +const file = await read('example.md') -async function main() { - const file = await remark() - .use(remarkLint) - .use(remarkLintHeadingIncrement) - .process(await read('example.md')) +await remark() + .use(remarkLint) + .use(remarkLintHeadingIncrement) + .process(file) - console.error(reporter(file)) -} +console.error(reporter(file)) ``` On the CLI: diff --git a/packages/remark-lint-heading-style/readme.md b/packages/remark-lint-heading-style/readme.md index bb0455c..dd896f2 100644 --- a/packages/remark-lint-heading-style/readme.md +++ b/packages/remark-lint-heading-style/readme.md @@ -75,22 +75,20 @@ In browsers with [`esm.sh`][esmsh]: On the API: ```js -import {read} from 'to-vfile' -import {reporter} from 'vfile-reporter' import {remark} from 'remark' import remarkLint from 'remark-lint' import remarkLintHeadingStyle from 'remark-lint-heading-style' +import {read} from 'to-vfile' +import {reporter} from 'vfile-reporter' -main() +const file = await read('example.md') -async function main() { - const file = await remark() - .use(remarkLint) - .use(remarkLintHeadingStyle) - .process(await read('example.md')) +await remark() + .use(remarkLint) + .use(remarkLintHeadingStyle) + .process(file) - console.error(reporter(file)) -} +console.error(reporter(file)) ``` On the CLI: diff --git a/packages/remark-lint-linebreak-style/readme.md b/packages/remark-lint-linebreak-style/readme.md index 121fefd..432444c 100644 --- a/packages/remark-lint-linebreak-style/readme.md +++ b/packages/remark-lint-linebreak-style/readme.md @@ -70,22 +70,20 @@ In browsers with [`esm.sh`][esmsh]: On the API: ```js -import {read} from 'to-vfile' -import {reporter} from 'vfile-reporter' import {remark} from 'remark' import remarkLint from 'remark-lint' import remarkLintLinebreakStyle from 'remark-lint-linebreak-style' +import {read} from 'to-vfile' +import {reporter} from 'vfile-reporter' -main() +const file = await read('example.md') -async function main() { - const file = await remark() - .use(remarkLint) - .use(remarkLintLinebreakStyle) - .process(await read('example.md')) +await remark() + .use(remarkLint) + .use(remarkLintLinebreakStyle) + .process(file) - console.error(reporter(file)) -} +console.error(reporter(file)) ``` On the CLI: diff --git a/packages/remark-lint-link-title-style/readme.md b/packages/remark-lint-link-title-style/readme.md index 06e350d..a464b0d 100644 --- a/packages/remark-lint-link-title-style/readme.md +++ b/packages/remark-lint-link-title-style/readme.md @@ -75,22 +75,20 @@ In browsers with [`esm.sh`][esmsh]: On the API: ```js -import {read} from 'to-vfile' -import {reporter} from 'vfile-reporter' import {remark} from 'remark' import remarkLint from 'remark-lint' import remarkLintLinkTitleStyle from 'remark-lint-link-title-style' +import {read} from 'to-vfile' +import {reporter} from 'vfile-reporter' -main() +const file = await read('example.md') -async function main() { - const file = await remark() - .use(remarkLint) - .use(remarkLintLinkTitleStyle) - .process(await read('example.md')) +await remark() + .use(remarkLint) + .use(remarkLintLinkTitleStyle) + .process(file) - console.error(reporter(file)) -} +console.error(reporter(file)) ``` On the CLI: diff --git a/packages/remark-lint-list-item-bullet-indent/readme.md b/packages/remark-lint-list-item-bullet-indent/readme.md index 2416232..8483748 100644 --- a/packages/remark-lint-list-item-bullet-indent/readme.md +++ b/packages/remark-lint-list-item-bullet-indent/readme.md @@ -74,22 +74,20 @@ In browsers with [`esm.sh`][esmsh]: On the API: ```js -import {read} from 'to-vfile' -import {reporter} from 'vfile-reporter' import {remark} from 'remark' import remarkLint from 'remark-lint' import remarkLintListItemBulletIndent from 'remark-lint-list-item-bullet-indent' +import {read} from 'to-vfile' +import {reporter} from 'vfile-reporter' -main() +const file = await read('example.md') -async function main() { - const file = await remark() - .use(remarkLint) - .use(remarkLintListItemBulletIndent) - .process(await read('example.md')) +await remark() + .use(remarkLint) + .use(remarkLintListItemBulletIndent) + .process(file) - console.error(reporter(file)) -} +console.error(reporter(file)) ``` On the CLI: diff --git a/packages/remark-lint-list-item-content-indent/readme.md b/packages/remark-lint-list-item-content-indent/readme.md index bd3261d..cc96c33 100644 --- a/packages/remark-lint-list-item-content-indent/readme.md +++ b/packages/remark-lint-list-item-content-indent/readme.md @@ -75,22 +75,20 @@ In browsers with [`esm.sh`][esmsh]: On the API: ```js -import {read} from 'to-vfile' -import {reporter} from 'vfile-reporter' import {remark} from 'remark' import remarkLint from 'remark-lint' import remarkLintListItemContentIndent from 'remark-lint-list-item-content-indent' +import {read} from 'to-vfile' +import {reporter} from 'vfile-reporter' -main() +const file = await read('example.md') -async function main() { - const file = await remark() - .use(remarkLint) - .use(remarkLintListItemContentIndent) - .process(await read('example.md')) +await remark() + .use(remarkLint) + .use(remarkLintListItemContentIndent) + .process(file) - console.error(reporter(file)) -} +console.error(reporter(file)) ``` On the CLI: diff --git a/packages/remark-lint-list-item-indent/readme.md b/packages/remark-lint-list-item-indent/readme.md index c998c48..1753e00 100644 --- a/packages/remark-lint-list-item-indent/readme.md +++ b/packages/remark-lint-list-item-indent/readme.md @@ -77,22 +77,20 @@ In browsers with [`esm.sh`][esmsh]: On the API: ```js -import {read} from 'to-vfile' -import {reporter} from 'vfile-reporter' import {remark} from 'remark' import remarkLint from 'remark-lint' import remarkLintListItemIndent from 'remark-lint-list-item-indent' +import {read} from 'to-vfile' +import {reporter} from 'vfile-reporter' -main() +const file = await read('example.md') -async function main() { - const file = await remark() - .use(remarkLint) - .use(remarkLintListItemIndent) - .process(await read('example.md')) +await remark() + .use(remarkLint) + .use(remarkLintListItemIndent) + .process(file) - console.error(reporter(file)) -} +console.error(reporter(file)) ``` On the CLI: @@ -230,24 +228,6 @@ Paragraph. No messages. -##### `not-ok.md` - -When configured with `'mixed'`. - -###### In - -> 👉 **Note**: `·` represents a space. - -```markdown -*···List item. -``` - -###### Out - -```text -1:5: Incorrect list-item indent: remove 2 spaces -``` - ##### `ok.md` When configured with `'space'`. @@ -316,6 +296,24 @@ When configured with `'tab-size'`. ##### `not-ok.md` +When configured with `'mixed'`. + +###### In + +> 👉 **Note**: `·` represents a space. + +```markdown +*···List item. +``` + +###### Out + +```text +1:5: Incorrect list-item indent: remove 2 spaces +``` + +##### `not-ok.md` + When configured with `'💩'`. ###### Out diff --git a/packages/remark-lint-list-item-spacing/readme.md b/packages/remark-lint-list-item-spacing/readme.md index 596b2d2..88ade3a 100644 --- a/packages/remark-lint-list-item-spacing/readme.md +++ b/packages/remark-lint-list-item-spacing/readme.md @@ -75,22 +75,20 @@ In browsers with [`esm.sh`][esmsh]: On the API: ```js -import {read} from 'to-vfile' -import {reporter} from 'vfile-reporter' import {remark} from 'remark' import remarkLint from 'remark-lint' import remarkLintListItemSpacing from 'remark-lint-list-item-spacing' +import {read} from 'to-vfile' +import {reporter} from 'vfile-reporter' -main() +const file = await read('example.md') -async function main() { - const file = await remark() - .use(remarkLint) - .use(remarkLintListItemSpacing) - .process(await read('example.md')) +await remark() + .use(remarkLint) + .use(remarkLintListItemSpacing) + .process(file) - console.error(reporter(file)) -} +console.error(reporter(file)) ``` On the CLI: diff --git a/packages/remark-lint-maximum-heading-length/readme.md b/packages/remark-lint-maximum-heading-length/readme.md index 0d6190b..3f63d0f 100644 --- a/packages/remark-lint-maximum-heading-length/readme.md +++ b/packages/remark-lint-maximum-heading-length/readme.md @@ -73,22 +73,20 @@ In browsers with [`esm.sh`][esmsh]: On the API: ```js -import {read} from 'to-vfile' -import {reporter} from 'vfile-reporter' import {remark} from 'remark' import remarkLint from 'remark-lint' import remarkLintMaximumHeadingLength from 'remark-lint-maximum-heading-length' +import {read} from 'to-vfile' +import {reporter} from 'vfile-reporter' -main() +const file = await read('example.md') -async function main() { - const file = await remark() - .use(remarkLint) - .use(remarkLintMaximumHeadingLength) - .process(await read('example.md')) +await remark() + .use(remarkLint) + .use(remarkLintMaximumHeadingLength) + .process(file) - console.error(reporter(file)) -} +console.error(reporter(file)) ``` On the CLI: diff --git a/packages/remark-lint-maximum-line-length/readme.md b/packages/remark-lint-maximum-line-length/readme.md index 4c2cd57..0690de1 100644 --- a/packages/remark-lint-maximum-line-length/readme.md +++ b/packages/remark-lint-maximum-line-length/readme.md @@ -73,22 +73,20 @@ In browsers with [`esm.sh`][esmsh]: On the API: ```js -import {read} from 'to-vfile' -import {reporter} from 'vfile-reporter' import {remark} from 'remark' import remarkLint from 'remark-lint' import remarkLintMaximumLineLength from 'remark-lint-maximum-line-length' +import {read} from 'to-vfile' +import {reporter} from 'vfile-reporter' -main() +const file = await read('example.md') -async function main() { - const file = await remark() - .use(remarkLint) - .use(remarkLintMaximumLineLength) - .process(await read('example.md')) +await remark() + .use(remarkLint) + .use(remarkLintMaximumLineLength) + .process(file) - console.error(reporter(file)) -} +console.error(reporter(file)) ``` On the CLI: diff --git a/packages/remark-lint-no-blockquote-without-marker/readme.md b/packages/remark-lint-no-blockquote-without-marker/readme.md index 81f4d90..de28815 100644 --- a/packages/remark-lint-no-blockquote-without-marker/readme.md +++ b/packages/remark-lint-no-blockquote-without-marker/readme.md @@ -75,22 +75,20 @@ In browsers with [`esm.sh`][esmsh]: On the API: ```js -import {read} from 'to-vfile' -import {reporter} from 'vfile-reporter' import {remark} from 'remark' import remarkLint from 'remark-lint' import remarkLintNoBlockquoteWithoutMarker from 'remark-lint-no-blockquote-without-marker' +import {read} from 'to-vfile' +import {reporter} from 'vfile-reporter' -main() +const file = await read('example.md') -async function main() { - const file = await remark() - .use(remarkLint) - .use(remarkLintNoBlockquoteWithoutMarker) - .process(await read('example.md')) +await remark() + .use(remarkLint) + .use(remarkLintNoBlockquoteWithoutMarker) + .process(file) - console.error(reporter(file)) -} +console.error(reporter(file)) ``` On the CLI: diff --git a/packages/remark-lint-no-consecutive-blank-lines/readme.md b/packages/remark-lint-no-consecutive-blank-lines/readme.md index 659779d..6087956 100644 --- a/packages/remark-lint-no-consecutive-blank-lines/readme.md +++ b/packages/remark-lint-no-consecutive-blank-lines/readme.md @@ -76,22 +76,20 @@ In browsers with [`esm.sh`][esmsh]: On the API: ```js -import {read} from 'to-vfile' -import {reporter} from 'vfile-reporter' import {remark} from 'remark' import remarkLint from 'remark-lint' import remarkLintNoConsecutiveBlankLines from 'remark-lint-no-consecutive-blank-lines' +import {read} from 'to-vfile' +import {reporter} from 'vfile-reporter' -main() +const file = await read('example.md') -async function main() { - const file = await remark() - .use(remarkLint) - .use(remarkLintNoConsecutiveBlankLines) - .process(await read('example.md')) +await remark() + .use(remarkLint) + .use(remarkLintNoConsecutiveBlankLines) + .process(file) - console.error(reporter(file)) -} +console.error(reporter(file)) ``` On the CLI: diff --git a/packages/remark-lint-no-duplicate-defined-urls/readme.md b/packages/remark-lint-no-duplicate-defined-urls/readme.md index 60b573c..88a60f7 100644 --- a/packages/remark-lint-no-duplicate-defined-urls/readme.md +++ b/packages/remark-lint-no-duplicate-defined-urls/readme.md @@ -69,22 +69,20 @@ In browsers with [`esm.sh`][esmsh]: On the API: ```js -import {read} from 'to-vfile' -import {reporter} from 'vfile-reporter' import {remark} from 'remark' import remarkLint from 'remark-lint' import remarkLintNoDuplicateDefinedUrls from 'remark-lint-no-duplicate-defined-urls' +import {read} from 'to-vfile' +import {reporter} from 'vfile-reporter' -main() +const file = await read('example.md') -async function main() { - const file = await remark() - .use(remarkLint) - .use(remarkLintNoDuplicateDefinedUrls) - .process(await read('example.md')) +await remark() + .use(remarkLint) + .use(remarkLintNoDuplicateDefinedUrls) + .process(file) - console.error(reporter(file)) -} +console.error(reporter(file)) ``` On the CLI: diff --git a/packages/remark-lint-no-duplicate-definitions/readme.md b/packages/remark-lint-no-duplicate-definitions/readme.md index 48a8103..05ad24c 100644 --- a/packages/remark-lint-no-duplicate-definitions/readme.md +++ b/packages/remark-lint-no-duplicate-definitions/readme.md @@ -73,22 +73,20 @@ In browsers with [`esm.sh`][esmsh]: On the API: ```js -import {read} from 'to-vfile' -import {reporter} from 'vfile-reporter' import {remark} from 'remark' import remarkLint from 'remark-lint' import remarkLintNoDuplicateDefinitions from 'remark-lint-no-duplicate-definitions' +import {read} from 'to-vfile' +import {reporter} from 'vfile-reporter' -main() +const file = await read('example.md') -async function main() { - const file = await remark() - .use(remarkLint) - .use(remarkLintNoDuplicateDefinitions) - .process(await read('example.md')) +await remark() + .use(remarkLint) + .use(remarkLintNoDuplicateDefinitions) + .process(file) - console.error(reporter(file)) -} +console.error(reporter(file)) ``` On the CLI: diff --git a/packages/remark-lint-no-duplicate-headings-in-section/readme.md b/packages/remark-lint-no-duplicate-headings-in-section/readme.md index 82e8c78..9e22edd 100644 --- a/packages/remark-lint-no-duplicate-headings-in-section/readme.md +++ b/packages/remark-lint-no-duplicate-headings-in-section/readme.md @@ -71,22 +71,20 @@ In browsers with [`esm.sh`][esmsh]: On the API: ```js -import {read} from 'to-vfile' -import {reporter} from 'vfile-reporter' import {remark} from 'remark' import remarkLint from 'remark-lint' import remarkLintNoDuplicateHeadingsInSection from 'remark-lint-no-duplicate-headings-in-section' +import {read} from 'to-vfile' +import {reporter} from 'vfile-reporter' -main() +const file = await read('example.md') -async function main() { - const file = await remark() - .use(remarkLint) - .use(remarkLintNoDuplicateHeadingsInSection) - .process(await read('example.md')) +await remark() + .use(remarkLint) + .use(remarkLintNoDuplicateHeadingsInSection) + .process(file) - console.error(reporter(file)) -} +console.error(reporter(file)) ``` On the CLI: diff --git a/packages/remark-lint-no-duplicate-headings/readme.md b/packages/remark-lint-no-duplicate-headings/readme.md index 367479c..603227c 100644 --- a/packages/remark-lint-no-duplicate-headings/readme.md +++ b/packages/remark-lint-no-duplicate-headings/readme.md @@ -74,22 +74,20 @@ In browsers with [`esm.sh`][esmsh]: On the API: ```js -import {read} from 'to-vfile' -import {reporter} from 'vfile-reporter' import {remark} from 'remark' import remarkLint from 'remark-lint' import remarkLintNoDuplicateHeadings from 'remark-lint-no-duplicate-headings' +import {read} from 'to-vfile' +import {reporter} from 'vfile-reporter' -main() +const file = await read('example.md') -async function main() { - const file = await remark() - .use(remarkLint) - .use(remarkLintNoDuplicateHeadings) - .process(await read('example.md')) +await remark() + .use(remarkLint) + .use(remarkLintNoDuplicateHeadings) + .process(file) - console.error(reporter(file)) -} +console.error(reporter(file)) ``` On the CLI: diff --git a/packages/remark-lint-no-emphasis-as-heading/readme.md b/packages/remark-lint-no-emphasis-as-heading/readme.md index 2dc7c4e..e228c45 100644 --- a/packages/remark-lint-no-emphasis-as-heading/readme.md +++ b/packages/remark-lint-no-emphasis-as-heading/readme.md @@ -75,22 +75,20 @@ In browsers with [`esm.sh`][esmsh]: On the API: ```js -import {read} from 'to-vfile' -import {reporter} from 'vfile-reporter' import {remark} from 'remark' import remarkLint from 'remark-lint' import remarkLintNoEmphasisAsHeading from 'remark-lint-no-emphasis-as-heading' +import {read} from 'to-vfile' +import {reporter} from 'vfile-reporter' -main() +const file = await read('example.md') -async function main() { - const file = await remark() - .use(remarkLint) - .use(remarkLintNoEmphasisAsHeading) - .process(await read('example.md')) +await remark() + .use(remarkLint) + .use(remarkLintNoEmphasisAsHeading) + .process(file) - console.error(reporter(file)) -} +console.error(reporter(file)) ``` On the CLI: diff --git a/packages/remark-lint-no-empty-url/readme.md b/packages/remark-lint-no-empty-url/readme.md index 88e37e0..e96aff0 100644 --- a/packages/remark-lint-no-empty-url/readme.md +++ b/packages/remark-lint-no-empty-url/readme.md @@ -69,22 +69,20 @@ In browsers with [`esm.sh`][esmsh]: On the API: ```js -import {read} from 'to-vfile' -import {reporter} from 'vfile-reporter' import {remark} from 'remark' import remarkLint from 'remark-lint' import remarkLintNoEmptyUrl from 'remark-lint-no-empty-url' +import {read} from 'to-vfile' +import {reporter} from 'vfile-reporter' -main() +const file = await read('example.md') -async function main() { - const file = await remark() - .use(remarkLint) - .use(remarkLintNoEmptyUrl) - .process(await read('example.md')) +await remark() + .use(remarkLint) + .use(remarkLintNoEmptyUrl) + .process(file) - console.error(reporter(file)) -} +console.error(reporter(file)) ``` On the CLI: diff --git a/packages/remark-lint-no-file-name-articles/readme.md b/packages/remark-lint-no-file-name-articles/readme.md index b3eee0e..53737f8 100644 --- a/packages/remark-lint-no-file-name-articles/readme.md +++ b/packages/remark-lint-no-file-name-articles/readme.md @@ -73,22 +73,20 @@ In browsers with [`esm.sh`][esmsh]: On the API: ```js -import {read} from 'to-vfile' -import {reporter} from 'vfile-reporter' import {remark} from 'remark' import remarkLint from 'remark-lint' import remarkLintNoFileNameArticles from 'remark-lint-no-file-name-articles' +import {read} from 'to-vfile' +import {reporter} from 'vfile-reporter' -main() +const file = await read('example.md') -async function main() { - const file = await remark() - .use(remarkLint) - .use(remarkLintNoFileNameArticles) - .process(await read('example.md')) +await remark() + .use(remarkLint) + .use(remarkLintNoFileNameArticles) + .process(file) - console.error(reporter(file)) -} +console.error(reporter(file)) ``` On the CLI: diff --git a/packages/remark-lint-no-file-name-consecutive-dashes/readme.md b/packages/remark-lint-no-file-name-consecutive-dashes/readme.md index 16509fa..a23b6ab 100644 --- a/packages/remark-lint-no-file-name-consecutive-dashes/readme.md +++ b/packages/remark-lint-no-file-name-consecutive-dashes/readme.md @@ -73,22 +73,20 @@ In browsers with [`esm.sh`][esmsh]: On the API: ```js -import {read} from 'to-vfile' -import {reporter} from 'vfile-reporter' import {remark} from 'remark' import remarkLint from 'remark-lint' import remarkLintNoFileNameConsecutiveDashes from 'remark-lint-no-file-name-consecutive-dashes' +import {read} from 'to-vfile' +import {reporter} from 'vfile-reporter' -main() +const file = await read('example.md') -async function main() { - const file = await remark() - .use(remarkLint) - .use(remarkLintNoFileNameConsecutiveDashes) - .process(await read('example.md')) +await remark() + .use(remarkLint) + .use(remarkLintNoFileNameConsecutiveDashes) + .process(file) - console.error(reporter(file)) -} +console.error(reporter(file)) ``` On the CLI: diff --git a/packages/remark-lint-no-file-name-irregular-characters/readme.md b/packages/remark-lint-no-file-name-irregular-characters/readme.md index 27f8b78..e52f9f4 100644 --- a/packages/remark-lint-no-file-name-irregular-characters/readme.md +++ b/packages/remark-lint-no-file-name-irregular-characters/readme.md @@ -72,22 +72,20 @@ In browsers with [`esm.sh`][esmsh]: On the API: ```js -import {read} from 'to-vfile' -import {reporter} from 'vfile-reporter' import {remark} from 'remark' import remarkLint from 'remark-lint' import remarkLintNoFileNameIrregularCharacters from 'remark-lint-no-file-name-irregular-characters' +import {read} from 'to-vfile' +import {reporter} from 'vfile-reporter' -main() +const file = await read('example.md') -async function main() { - const file = await remark() - .use(remarkLint) - .use(remarkLintNoFileNameIrregularCharacters) - .process(await read('example.md')) +await remark() + .use(remarkLint) + .use(remarkLintNoFileNameIrregularCharacters) + .process(file) - console.error(reporter(file)) -} +console.error(reporter(file)) ``` On the CLI: @@ -151,14 +149,6 @@ No messages. 1:1: Do not use `_` in a file name ``` -##### `plug ins.md` - -###### Out - -```text -1:1: Do not use ` ` in a file name -``` - ##### `README.md` When configured with `'\\.a-z0-9'`. @@ -169,6 +159,14 @@ When configured with `'\\.a-z0-9'`. 1:1: Do not use `R` in a file name ``` +##### `plug ins.md` + +###### Out + +```text +1:1: Do not use ` ` in a file name +``` + ## Compatibility Projects maintained by the unified collective are compatible with all maintained diff --git a/packages/remark-lint-no-file-name-mixed-case/readme.md b/packages/remark-lint-no-file-name-mixed-case/readme.md index 70c3fb3..a9c717a 100644 --- a/packages/remark-lint-no-file-name-mixed-case/readme.md +++ b/packages/remark-lint-no-file-name-mixed-case/readme.md @@ -73,22 +73,20 @@ In browsers with [`esm.sh`][esmsh]: On the API: ```js -import {read} from 'to-vfile' -import {reporter} from 'vfile-reporter' import {remark} from 'remark' import remarkLint from 'remark-lint' import remarkLintNoFileNameMixedCase from 'remark-lint-no-file-name-mixed-case' +import {read} from 'to-vfile' +import {reporter} from 'vfile-reporter' -main() +const file = await read('example.md') -async function main() { - const file = await remark() - .use(remarkLint) - .use(remarkLintNoFileNameMixedCase) - .process(await read('example.md')) +await remark() + .use(remarkLint) + .use(remarkLintNoFileNameMixedCase) + .process(file) - console.error(reporter(file)) -} +console.error(reporter(file)) ``` On the CLI: diff --git a/packages/remark-lint-no-file-name-outer-dashes/readme.md b/packages/remark-lint-no-file-name-outer-dashes/readme.md index 813e0a1..0c79468 100644 --- a/packages/remark-lint-no-file-name-outer-dashes/readme.md +++ b/packages/remark-lint-no-file-name-outer-dashes/readme.md @@ -73,22 +73,20 @@ In browsers with [`esm.sh`][esmsh]: On the API: ```js -import {read} from 'to-vfile' -import {reporter} from 'vfile-reporter' import {remark} from 'remark' import remarkLint from 'remark-lint' import remarkLintNoFileNameOuterDashes from 'remark-lint-no-file-name-outer-dashes' +import {read} from 'to-vfile' +import {reporter} from 'vfile-reporter' -main() +const file = await read('example.md') -async function main() { - const file = await remark() - .use(remarkLint) - .use(remarkLintNoFileNameOuterDashes) - .process(await read('example.md')) +await remark() + .use(remarkLint) + .use(remarkLintNoFileNameOuterDashes) + .process(file) - console.error(reporter(file)) -} +console.error(reporter(file)) ``` On the CLI: diff --git a/packages/remark-lint-no-heading-content-indent/readme.md b/packages/remark-lint-no-heading-content-indent/readme.md index 228c2d2..1224b1c 100644 --- a/packages/remark-lint-no-heading-content-indent/readme.md +++ b/packages/remark-lint-no-heading-content-indent/readme.md @@ -76,22 +76,20 @@ In browsers with [`esm.sh`][esmsh]: On the API: ```js -import {read} from 'to-vfile' -import {reporter} from 'vfile-reporter' import {remark} from 'remark' import remarkLint from 'remark-lint' import remarkLintNoHeadingContentIndent from 'remark-lint-no-heading-content-indent' +import {read} from 'to-vfile' +import {reporter} from 'vfile-reporter' -main() +const file = await read('example.md') -async function main() { - const file = await remark() - .use(remarkLint) - .use(remarkLintNoHeadingContentIndent) - .process(await read('example.md')) +await remark() + .use(remarkLint) + .use(remarkLintNoHeadingContentIndent) + .process(file) - console.error(reporter(file)) -} +console.error(reporter(file)) ``` On the CLI: diff --git a/packages/remark-lint-no-heading-indent/readme.md b/packages/remark-lint-no-heading-indent/readme.md index 5e28dd8..c34cfa3 100644 --- a/packages/remark-lint-no-heading-indent/readme.md +++ b/packages/remark-lint-no-heading-indent/readme.md @@ -70,22 +70,20 @@ In browsers with [`esm.sh`][esmsh]: On the API: ```js -import {read} from 'to-vfile' -import {reporter} from 'vfile-reporter' import {remark} from 'remark' import remarkLint from 'remark-lint' import remarkLintNoHeadingIndent from 'remark-lint-no-heading-indent' +import {read} from 'to-vfile' +import {reporter} from 'vfile-reporter' -main() +const file = await read('example.md') -async function main() { - const file = await remark() - .use(remarkLint) - .use(remarkLintNoHeadingIndent) - .process(await read('example.md')) +await remark() + .use(remarkLint) + .use(remarkLintNoHeadingIndent) + .process(file) - console.error(reporter(file)) -} +console.error(reporter(file)) ``` On the CLI: diff --git a/packages/remark-lint-no-heading-like-paragraph/readme.md b/packages/remark-lint-no-heading-like-paragraph/readme.md index 8553b7a..ad8e81b 100644 --- a/packages/remark-lint-no-heading-like-paragraph/readme.md +++ b/packages/remark-lint-no-heading-like-paragraph/readme.md @@ -70,22 +70,20 @@ In browsers with [`esm.sh`][esmsh]: On the API: ```js -import {read} from 'to-vfile' -import {reporter} from 'vfile-reporter' import {remark} from 'remark' import remarkLint from 'remark-lint' import remarkLintNoHeadingLikeParagraph from 'remark-lint-no-heading-like-paragraph' +import {read} from 'to-vfile' +import {reporter} from 'vfile-reporter' -main() +const file = await read('example.md') -async function main() { - const file = await remark() - .use(remarkLint) - .use(remarkLintNoHeadingLikeParagraph) - .process(await read('example.md')) +await remark() + .use(remarkLint) + .use(remarkLintNoHeadingLikeParagraph) + .process(file) - console.error(reporter(file)) -} +console.error(reporter(file)) ``` On the CLI: diff --git a/packages/remark-lint-no-heading-punctuation/readme.md b/packages/remark-lint-no-heading-punctuation/readme.md index a06b3c0..51397d8 100644 --- a/packages/remark-lint-no-heading-punctuation/readme.md +++ b/packages/remark-lint-no-heading-punctuation/readme.md @@ -72,22 +72,20 @@ In browsers with [`esm.sh`][esmsh]: On the API: ```js -import {read} from 'to-vfile' -import {reporter} from 'vfile-reporter' import {remark} from 'remark' import remarkLint from 'remark-lint' import remarkLintNoHeadingPunctuation from 'remark-lint-no-heading-punctuation' +import {read} from 'to-vfile' +import {reporter} from 'vfile-reporter' -main() +const file = await read('example.md') -async function main() { - const file = await remark() - .use(remarkLint) - .use(remarkLintNoHeadingPunctuation) - .process(await read('example.md')) +await remark() + .use(remarkLint) + .use(remarkLintNoHeadingPunctuation) + .process(file) - console.error(reporter(file)) -} +console.error(reporter(file)) ``` On the CLI: @@ -143,6 +141,20 @@ The following options (default: `'\\.,;:!?'`) are accepted: No messages. +##### `ok.md` + +When configured with `',;:!?'`. + +###### In + +```markdown +# Hello… +``` + +###### Out + +No messages. + ##### `not-ok.md` ###### In @@ -169,20 +181,6 @@ No messages. 9:1-9:9: Don’t add a trailing `;` to headings ``` -##### `ok.md` - -When configured with `',;:!?'`. - -###### In - -```markdown -# Hello… -``` - -###### Out - -No messages. - ## Compatibility Projects maintained by the unified collective are compatible with all maintained diff --git a/packages/remark-lint-no-html/readme.md b/packages/remark-lint-no-html/readme.md index a4b96f4..8de7a43 100644 --- a/packages/remark-lint-no-html/readme.md +++ b/packages/remark-lint-no-html/readme.md @@ -68,22 +68,20 @@ In browsers with [`esm.sh`][esmsh]: On the API: ```js -import {read} from 'to-vfile' -import {reporter} from 'vfile-reporter' import {remark} from 'remark' import remarkLint from 'remark-lint' import remarkLintNoHtml from 'remark-lint-no-html' +import {read} from 'to-vfile' +import {reporter} from 'vfile-reporter' -main() +const file = await read('example.md') -async function main() { - const file = await remark() - .use(remarkLint) - .use(remarkLintNoHtml) - .process(await read('example.md')) +await remark() + .use(remarkLint) + .use(remarkLintNoHtml) + .process(file) - console.error(reporter(file)) -} +console.error(reporter(file)) ``` On the CLI: diff --git a/packages/remark-lint-no-inline-padding/readme.md b/packages/remark-lint-no-inline-padding/readme.md index 820c51d..4902963 100644 --- a/packages/remark-lint-no-inline-padding/readme.md +++ b/packages/remark-lint-no-inline-padding/readme.md @@ -76,22 +76,20 @@ In browsers with [`esm.sh`][esmsh]: On the API: ```js -import {read} from 'to-vfile' -import {reporter} from 'vfile-reporter' import {remark} from 'remark' import remarkLint from 'remark-lint' import remarkLintNoInlinePadding from 'remark-lint-no-inline-padding' +import {read} from 'to-vfile' +import {reporter} from 'vfile-reporter' -main() +const file = await read('example.md') -async function main() { - const file = await remark() - .use(remarkLint) - .use(remarkLintNoInlinePadding) - .process(await read('example.md')) +await remark() + .use(remarkLint) + .use(remarkLintNoInlinePadding) + .process(file) - console.error(reporter(file)) -} +console.error(reporter(file)) ``` On the CLI: diff --git a/packages/remark-lint-no-literal-urls/readme.md b/packages/remark-lint-no-literal-urls/readme.md index ddb65cb..69393e5 100644 --- a/packages/remark-lint-no-literal-urls/readme.md +++ b/packages/remark-lint-no-literal-urls/readme.md @@ -75,22 +75,20 @@ In browsers with [`esm.sh`][esmsh]: On the API: ```js -import {read} from 'to-vfile' -import {reporter} from 'vfile-reporter' import {remark} from 'remark' import remarkLint from 'remark-lint' import remarkLintNoLiteralUrls from 'remark-lint-no-literal-urls' +import {read} from 'to-vfile' +import {reporter} from 'vfile-reporter' -main() +const file = await read('example.md') -async function main() { - const file = await remark() - .use(remarkLint) - .use(remarkLintNoLiteralUrls) - .process(await read('example.md')) +await remark() + .use(remarkLint) + .use(remarkLintNoLiteralUrls) + .process(file) - console.error(reporter(file)) -} +console.error(reporter(file)) ``` On the CLI: diff --git a/packages/remark-lint-no-missing-blank-lines/readme.md b/packages/remark-lint-no-missing-blank-lines/readme.md index 3ee3f24..6c91945 100644 --- a/packages/remark-lint-no-missing-blank-lines/readme.md +++ b/packages/remark-lint-no-missing-blank-lines/readme.md @@ -70,22 +70,20 @@ In browsers with [`esm.sh`][esmsh]: On the API: ```js -import {read} from 'to-vfile' -import {reporter} from 'vfile-reporter' import {remark} from 'remark' import remarkLint from 'remark-lint' import remarkLintNoMissingBlankLines from 'remark-lint-no-missing-blank-lines' +import {read} from 'to-vfile' +import {reporter} from 'vfile-reporter' -main() +const file = await read('example.md') -async function main() { - const file = await remark() - .use(remarkLint) - .use(remarkLintNoMissingBlankLines) - .process(await read('example.md')) +await remark() + .use(remarkLint) + .use(remarkLintNoMissingBlankLines) + .process(file) - console.error(reporter(file)) -} +console.error(reporter(file)) ``` On the CLI: diff --git a/packages/remark-lint-no-multiple-toplevel-headings/readme.md b/packages/remark-lint-no-multiple-toplevel-headings/readme.md index 588a4d2..11113b3 100644 --- a/packages/remark-lint-no-multiple-toplevel-headings/readme.md +++ b/packages/remark-lint-no-multiple-toplevel-headings/readme.md @@ -74,22 +74,20 @@ In browsers with [`esm.sh`][esmsh]: On the API: ```js -import {read} from 'to-vfile' -import {reporter} from 'vfile-reporter' import {remark} from 'remark' import remarkLint from 'remark-lint' import remarkLintNoMultipleToplevelHeadings from 'remark-lint-no-multiple-toplevel-headings' +import {read} from 'to-vfile' +import {reporter} from 'vfile-reporter' -main() +const file = await read('example.md') -async function main() { - const file = await remark() - .use(remarkLint) - .use(remarkLintNoMultipleToplevelHeadings) - .process(await read('example.md')) +await remark() + .use(remarkLint) + .use(remarkLintNoMultipleToplevelHeadings) + .process(file) - console.error(reporter(file)) -} +console.error(reporter(file)) ``` On the CLI: diff --git a/packages/remark-lint-no-paragraph-content-indent/index.js b/packages/remark-lint-no-paragraph-content-indent/index.js index 1b67319..6bd117d 100644 --- a/packages/remark-lint-no-paragraph-content-indent/index.js +++ b/packages/remark-lint-no-paragraph-content-indent/index.js @@ -106,7 +106,6 @@ const remarkLintNoParagraphContentIndent = lintRule( const value = String(file) const loc = location(value) - // eslint-disable-next-line complexity visit(tree, 'paragraph', (node, _, parent) => { const end = pointEnd(node)?.line let line = pointStart(node)?.line diff --git a/packages/remark-lint-no-paragraph-content-indent/package.json b/packages/remark-lint-no-paragraph-content-indent/package.json index 4eea544..25b012d 100644 --- a/packages/remark-lint-no-paragraph-content-indent/package.json +++ b/packages/remark-lint-no-paragraph-content-indent/package.json @@ -53,7 +53,8 @@ "xo": { "prettier": true, "rules": { - "capitalized-comments": "off" + "capitalized-comments": "off", + "complexity": "off" } } } diff --git a/packages/remark-lint-no-paragraph-content-indent/readme.md b/packages/remark-lint-no-paragraph-content-indent/readme.md index eacb1da..ca95cf7 100644 --- a/packages/remark-lint-no-paragraph-content-indent/readme.md +++ b/packages/remark-lint-no-paragraph-content-indent/readme.md @@ -70,22 +70,20 @@ In browsers with [`esm.sh`][esmsh]: On the API: ```js -import {read} from 'to-vfile' -import {reporter} from 'vfile-reporter' import {remark} from 'remark' import remarkLint from 'remark-lint' import remarkLintNoParagraphContentIndent from 'remark-lint-no-paragraph-content-indent' +import {read} from 'to-vfile' +import {reporter} from 'vfile-reporter' -main() +const file = await read('example.md') -async function main() { - const file = await remark() - .use(remarkLint) - .use(remarkLintNoParagraphContentIndent) - .process(await read('example.md')) +await remark() + .use(remarkLint) + .use(remarkLintNoParagraphContentIndent) + .process(file) - console.error(reporter(file)) -} +console.error(reporter(file)) ``` On the CLI: diff --git a/packages/remark-lint-no-reference-like-url/readme.md b/packages/remark-lint-no-reference-like-url/readme.md index 76f33e9..aa80775 100644 --- a/packages/remark-lint-no-reference-like-url/readme.md +++ b/packages/remark-lint-no-reference-like-url/readme.md @@ -70,22 +70,20 @@ In browsers with [`esm.sh`][esmsh]: On the API: ```js -import {read} from 'to-vfile' -import {reporter} from 'vfile-reporter' import {remark} from 'remark' import remarkLint from 'remark-lint' import remarkLintNoReferenceLikeUrl from 'remark-lint-no-reference-like-url' +import {read} from 'to-vfile' +import {reporter} from 'vfile-reporter' -main() +const file = await read('example.md') -async function main() { - const file = await remark() - .use(remarkLint) - .use(remarkLintNoReferenceLikeUrl) - .process(await read('example.md')) +await remark() + .use(remarkLint) + .use(remarkLintNoReferenceLikeUrl) + .process(file) - console.error(reporter(file)) -} +console.error(reporter(file)) ``` On the CLI: diff --git a/packages/remark-lint-no-shell-dollars/readme.md b/packages/remark-lint-no-shell-dollars/readme.md index debe3cc..4acff5f 100644 --- a/packages/remark-lint-no-shell-dollars/readme.md +++ b/packages/remark-lint-no-shell-dollars/readme.md @@ -74,22 +74,20 @@ In browsers with [`esm.sh`][esmsh]: On the API: ```js -import {read} from 'to-vfile' -import {reporter} from 'vfile-reporter' import {remark} from 'remark' import remarkLint from 'remark-lint' import remarkLintNoShellDollars from 'remark-lint-no-shell-dollars' +import {read} from 'to-vfile' +import {reporter} from 'vfile-reporter' -main() +const file = await read('example.md') -async function main() { - const file = await remark() - .use(remarkLint) - .use(remarkLintNoShellDollars) - .process(await read('example.md')) +await remark() + .use(remarkLint) + .use(remarkLintNoShellDollars) + .process(file) - console.error(reporter(file)) -} +console.error(reporter(file)) ``` On the CLI: diff --git a/packages/remark-lint-no-shortcut-reference-image/readme.md b/packages/remark-lint-no-shortcut-reference-image/readme.md index ba71808..8cc3d8b 100644 --- a/packages/remark-lint-no-shortcut-reference-image/readme.md +++ b/packages/remark-lint-no-shortcut-reference-image/readme.md @@ -75,22 +75,20 @@ In browsers with [`esm.sh`][esmsh]: On the API: ```js -import {read} from 'to-vfile' -import {reporter} from 'vfile-reporter' import {remark} from 'remark' import remarkLint from 'remark-lint' import remarkLintNoShortcutReferenceImage from 'remark-lint-no-shortcut-reference-image' +import {read} from 'to-vfile' +import {reporter} from 'vfile-reporter' -main() +const file = await read('example.md') -async function main() { - const file = await remark() - .use(remarkLint) - .use(remarkLintNoShortcutReferenceImage) - .process(await read('example.md')) +await remark() + .use(remarkLint) + .use(remarkLintNoShortcutReferenceImage) + .process(file) - console.error(reporter(file)) -} +console.error(reporter(file)) ``` On the CLI: diff --git a/packages/remark-lint-no-shortcut-reference-link/readme.md b/packages/remark-lint-no-shortcut-reference-link/readme.md index 12b7528..c74e89c 100644 --- a/packages/remark-lint-no-shortcut-reference-link/readme.md +++ b/packages/remark-lint-no-shortcut-reference-link/readme.md @@ -75,22 +75,20 @@ In browsers with [`esm.sh`][esmsh]: On the API: ```js -import {read} from 'to-vfile' -import {reporter} from 'vfile-reporter' import {remark} from 'remark' import remarkLint from 'remark-lint' import remarkLintNoShortcutReferenceLink from 'remark-lint-no-shortcut-reference-link' +import {read} from 'to-vfile' +import {reporter} from 'vfile-reporter' -main() +const file = await read('example.md') -async function main() { - const file = await remark() - .use(remarkLint) - .use(remarkLintNoShortcutReferenceLink) - .process(await read('example.md')) +await remark() + .use(remarkLint) + .use(remarkLintNoShortcutReferenceLink) + .process(file) - console.error(reporter(file)) -} +console.error(reporter(file)) ``` On the CLI: diff --git a/packages/remark-lint-no-table-indentation/readme.md b/packages/remark-lint-no-table-indentation/readme.md index 640ed2e..be2d42f 100644 --- a/packages/remark-lint-no-table-indentation/readme.md +++ b/packages/remark-lint-no-table-indentation/readme.md @@ -76,22 +76,20 @@ In browsers with [`esm.sh`][esmsh]: On the API: ```js -import {read} from 'to-vfile' -import {reporter} from 'vfile-reporter' import {remark} from 'remark' import remarkLint from 'remark-lint' import remarkLintNoTableIndentation from 'remark-lint-no-table-indentation' +import {read} from 'to-vfile' +import {reporter} from 'vfile-reporter' -main() +const file = await read('example.md') -async function main() { - const file = await remark() - .use(remarkLint) - .use(remarkLintNoTableIndentation) - .process(await read('example.md')) +await remark() + .use(remarkLint) + .use(remarkLintNoTableIndentation) + .process(file) - console.error(reporter(file)) -} +console.error(reporter(file)) ``` On the CLI: diff --git a/packages/remark-lint-no-tabs/readme.md b/packages/remark-lint-no-tabs/readme.md index 124ab27..394a877 100644 --- a/packages/remark-lint-no-tabs/readme.md +++ b/packages/remark-lint-no-tabs/readme.md @@ -70,22 +70,20 @@ In browsers with [`esm.sh`][esmsh]: On the API: ```js -import {read} from 'to-vfile' -import {reporter} from 'vfile-reporter' import {remark} from 'remark' import remarkLint from 'remark-lint' import remarkLintNoTabs from 'remark-lint-no-tabs' +import {read} from 'to-vfile' +import {reporter} from 'vfile-reporter' -main() +const file = await read('example.md') -async function main() { - const file = await remark() - .use(remarkLint) - .use(remarkLintNoTabs) - .process(await read('example.md')) +await remark() + .use(remarkLint) + .use(remarkLintNoTabs) + .process(file) - console.error(reporter(file)) -} +console.error(reporter(file)) ``` On the CLI: diff --git a/packages/remark-lint-no-undefined-references/index.js b/packages/remark-lint-no-undefined-references/index.js index 0404ab1..74c6499 100644 --- a/packages/remark-lint-no-undefined-references/index.js +++ b/packages/remark-lint-no-undefined-references/index.js @@ -213,7 +213,6 @@ const remarkLintNoUndefinedReferences = lintRule( /** @type {Array} */ let ranges = [] - // eslint-disable-next-line complexity visit(node, (child) => { // Ignore the node itself. if (child === node) return @@ -292,12 +291,10 @@ const remarkLintNoUndefinedReferences = lintRule( let range = ranges.pop() // Range should always exist. - // eslint-disable-next-line max-depth if (range) { range.push(lines[lineIndex][0] + index) // This is the end of a reference already. - // eslint-disable-next-line max-depth if (range.length === 4) { handleRange(range) range = [] @@ -314,7 +311,6 @@ const remarkLintNoUndefinedReferences = lintRule( const range = ranges.pop() // Range should always exist. - // eslint-disable-next-line max-depth if (range) { range.push(lines[lineIndex][0] + index) handleRange(range) diff --git a/packages/remark-lint-no-undefined-references/package.json b/packages/remark-lint-no-undefined-references/package.json index 82e4638..78f5eb2 100644 --- a/packages/remark-lint-no-undefined-references/package.json +++ b/packages/remark-lint-no-undefined-references/package.json @@ -55,6 +55,8 @@ "prettier": true, "rules": { "capitalized-comments": "off", + "complexity": "off", + "max-depth": "off", "unicorn/prefer-at": "off", "unicorn/prefer-code-point": "off", "unicorn/prefer-string-replace-all": "off", diff --git a/packages/remark-lint-no-undefined-references/readme.md b/packages/remark-lint-no-undefined-references/readme.md index f717ed9..229567f 100644 --- a/packages/remark-lint-no-undefined-references/readme.md +++ b/packages/remark-lint-no-undefined-references/readme.md @@ -73,22 +73,20 @@ In browsers with [`esm.sh`][esmsh]: On the API: ```js -import {read} from 'to-vfile' -import {reporter} from 'vfile-reporter' import {remark} from 'remark' import remarkLint from 'remark-lint' import remarkLintNoUndefinedReferences from 'remark-lint-no-undefined-references' +import {read} from 'to-vfile' +import {reporter} from 'vfile-reporter' -main() +const file = await read('example.md') -async function main() { - const file = await remark() - .use(remarkLint) - .use(remarkLintNoUndefinedReferences) - .process(await read('example.md')) +await remark() + .use(remarkLint) + .use(remarkLintNoUndefinedReferences) + .process(file) - console.error(reporter(file)) -} +console.error(reporter(file)) ``` On the CLI: @@ -178,6 +176,38 @@ Just two braces can’t link: []. No messages. +##### `ok-allow.md` + +When configured with `{ allow: [ '...', '…' ] }`. + +###### In + +```markdown +> Eliding a portion of a quoted passage […] is acceptable. +``` + +###### Out + +No messages. + +##### `ok-allow.md` + +When configured with `{ allow: [ 'a', { source: '^b\\.' } ] }`. + +###### In + +```markdown +[foo][b.c] + +[bar][a] + +Matching is case-insensitive: [bar][B.C] +``` + +###### Out + +No messages. + ##### `not-ok.md` ###### In @@ -216,38 +246,6 @@ Multiple pairs: [a][b][c]. 17:23-17:26: Found reference to undefined definition ``` -##### `ok-allow.md` - -When configured with `{ allow: [ '...', '…' ] }`. - -###### In - -```markdown -> Eliding a portion of a quoted passage […] is acceptable. -``` - -###### Out - -No messages. - -##### `ok-allow.md` - -When configured with `{ allow: [ 'a', { source: '^b\\.' } ] }`. - -###### In - -```markdown -[foo][b.c] - -[bar][a] - -Matching is case-insensitive: [bar][B.C] -``` - -###### Out - -No messages. - ##### `not-ok.md` When configured with `{ allow: [ 'a', { source: '^b\\.' } ] }`. diff --git a/packages/remark-lint-no-unneeded-full-reference-image/readme.md b/packages/remark-lint-no-unneeded-full-reference-image/readme.md index ab563a1..050e0c2 100644 --- a/packages/remark-lint-no-unneeded-full-reference-image/readme.md +++ b/packages/remark-lint-no-unneeded-full-reference-image/readme.md @@ -71,22 +71,20 @@ In browsers with [`esm.sh`][esmsh]: On the API: ```js -import {read} from 'to-vfile' -import {reporter} from 'vfile-reporter' import {remark} from 'remark' import remarkLint from 'remark-lint' import remarkLintNoUnneededFullReferenceImage from 'remark-lint-no-unneeded-full-reference-image' +import {read} from 'to-vfile' +import {reporter} from 'vfile-reporter' -main() +const file = await read('example.md') -async function main() { - const file = await remark() - .use(remarkLint) - .use(remarkLintNoUnneededFullReferenceImage) - .process(await read('example.md')) +await remark() + .use(remarkLint) + .use(remarkLintNoUnneededFullReferenceImage) + .process(file) - console.error(reporter(file)) -} +console.error(reporter(file)) ``` On the CLI: diff --git a/packages/remark-lint-no-unneeded-full-reference-link/readme.md b/packages/remark-lint-no-unneeded-full-reference-link/readme.md index 99d52dd..6ac492e 100644 --- a/packages/remark-lint-no-unneeded-full-reference-link/readme.md +++ b/packages/remark-lint-no-unneeded-full-reference-link/readme.md @@ -71,22 +71,20 @@ In browsers with [`esm.sh`][esmsh]: On the API: ```js -import {read} from 'to-vfile' -import {reporter} from 'vfile-reporter' import {remark} from 'remark' import remarkLint from 'remark-lint' import remarkLintNoUnneededFullReferenceLink from 'remark-lint-no-unneeded-full-reference-link' +import {read} from 'to-vfile' +import {reporter} from 'vfile-reporter' -main() +const file = await read('example.md') -async function main() { - const file = await remark() - .use(remarkLint) - .use(remarkLintNoUnneededFullReferenceLink) - .process(await read('example.md')) +await remark() + .use(remarkLint) + .use(remarkLintNoUnneededFullReferenceLink) + .process(file) - console.error(reporter(file)) -} +console.error(reporter(file)) ``` On the CLI: diff --git a/packages/remark-lint-no-unused-definitions/readme.md b/packages/remark-lint-no-unused-definitions/readme.md index e6fea48..6f7eb1e 100644 --- a/packages/remark-lint-no-unused-definitions/readme.md +++ b/packages/remark-lint-no-unused-definitions/readme.md @@ -73,22 +73,20 @@ In browsers with [`esm.sh`][esmsh]: On the API: ```js -import {read} from 'to-vfile' -import {reporter} from 'vfile-reporter' import {remark} from 'remark' import remarkLint from 'remark-lint' import remarkLintNoUnusedDefinitions from 'remark-lint-no-unused-definitions' +import {read} from 'to-vfile' +import {reporter} from 'vfile-reporter' -main() +const file = await read('example.md') -async function main() { - const file = await remark() - .use(remarkLint) - .use(remarkLintNoUnusedDefinitions) - .process(await read('example.md')) +await remark() + .use(remarkLint) + .use(remarkLintNoUnusedDefinitions) + .process(file) - console.error(reporter(file)) -} +console.error(reporter(file)) ``` On the CLI: diff --git a/packages/remark-lint-ordered-list-marker-style/readme.md b/packages/remark-lint-ordered-list-marker-style/readme.md index d04facb..19724ea 100644 --- a/packages/remark-lint-ordered-list-marker-style/readme.md +++ b/packages/remark-lint-ordered-list-marker-style/readme.md @@ -76,22 +76,20 @@ In browsers with [`esm.sh`][esmsh]: On the API: ```js -import {read} from 'to-vfile' -import {reporter} from 'vfile-reporter' import {remark} from 'remark' import remarkLint from 'remark-lint' import remarkLintOrderedListMarkerStyle from 'remark-lint-ordered-list-marker-style' +import {read} from 'to-vfile' +import {reporter} from 'vfile-reporter' -main() +const file = await read('example.md') -async function main() { - const file = await remark() - .use(remarkLint) - .use(remarkLintOrderedListMarkerStyle) - .process(await read('example.md')) +await remark() + .use(remarkLint) + .use(remarkLintOrderedListMarkerStyle) + .process(file) - console.error(reporter(file)) -} +console.error(reporter(file)) ``` On the CLI: @@ -170,22 +168,6 @@ Unordered lists are not affected by this rule. No messages. -##### `not-ok.md` - -###### In - -```markdown -1. Foo - -2) Bar -``` - -###### Out - -```text -3:1-3:8: Marker style should be `.` -``` - ##### `ok.md` When configured with `'.'`. @@ -220,6 +202,22 @@ No messages. ##### `not-ok.md` +###### In + +```markdown +1. Foo + +2) Bar +``` + +###### Out + +```text +3:1-3:8: Marker style should be `.` +``` + +##### `not-ok.md` + When configured with `'💩'`. ###### Out diff --git a/packages/remark-lint-ordered-list-marker-value/readme.md b/packages/remark-lint-ordered-list-marker-value/readme.md index 60e44fa..ebec1d1 100644 --- a/packages/remark-lint-ordered-list-marker-value/readme.md +++ b/packages/remark-lint-ordered-list-marker-value/readme.md @@ -74,22 +74,20 @@ In browsers with [`esm.sh`][esmsh]: On the API: ```js -import {read} from 'to-vfile' -import {reporter} from 'vfile-reporter' import {remark} from 'remark' import remarkLint from 'remark-lint' import remarkLintOrderedListMarkerValue from 'remark-lint-ordered-list-marker-value' +import {read} from 'to-vfile' +import {reporter} from 'vfile-reporter' -main() +const file = await read('example.md') -async function main() { - const file = await remark() - .use(remarkLint) - .use(remarkLintOrderedListMarkerValue) - .process(await read('example.md')) +await remark() + .use(remarkLint) + .use(remarkLintOrderedListMarkerValue) + .process(file) - console.error(reporter(file)) -} +console.error(reporter(file)) ``` On the CLI: @@ -202,40 +200,6 @@ Paragraph. No messages. -##### `not-ok.md` - -When configured with `'one'`. - -###### In - -```markdown -1. Foo -2. Bar -``` - -###### Out - -```text -2:1-2:8: Marker should be `1`, was `2` -``` - -##### `also-not-ok.md` - -When configured with `'one'`. - -###### In - -```markdown -2. Foo -1. Bar -``` - -###### Out - -```text -1:1-1:8: Marker should be `1`, was `2` -``` - ##### `ok.md` When configured with `'single'`. @@ -294,6 +258,40 @@ No messages. ##### `not-ok.md` +When configured with `'one'`. + +###### In + +```markdown +1. Foo +2. Bar +``` + +###### Out + +```text +2:1-2:8: Marker should be `1`, was `2` +``` + +##### `also-not-ok.md` + +When configured with `'one'`. + +###### In + +```markdown +2. Foo +1. Bar +``` + +###### Out + +```text +1:1-1:8: Marker should be `1`, was `2` +``` + +##### `not-ok.md` + When configured with `'ordered'`. ###### In diff --git a/packages/remark-lint-rule-style/readme.md b/packages/remark-lint-rule-style/readme.md index ba96e96..38e000d 100644 --- a/packages/remark-lint-rule-style/readme.md +++ b/packages/remark-lint-rule-style/readme.md @@ -76,22 +76,20 @@ In browsers with [`esm.sh`][esmsh]: On the API: ```js -import {read} from 'to-vfile' -import {reporter} from 'vfile-reporter' import {remark} from 'remark' import remarkLint from 'remark-lint' import remarkLintRuleStyle from 'remark-lint-rule-style' +import {read} from 'to-vfile' +import {reporter} from 'vfile-reporter' -main() +const file = await read('example.md') -async function main() { - const file = await remark() - .use(remarkLint) - .use(remarkLintRuleStyle) - .process(await read('example.md')) +await remark() + .use(remarkLint) + .use(remarkLintRuleStyle) + .process(file) - console.error(reporter(file)) -} +console.error(reporter(file)) ``` On the CLI: diff --git a/packages/remark-lint-strikethrough-marker/readme.md b/packages/remark-lint-strikethrough-marker/readme.md index 3e1af36..13093ad 100644 --- a/packages/remark-lint-strikethrough-marker/readme.md +++ b/packages/remark-lint-strikethrough-marker/readme.md @@ -74,22 +74,20 @@ In browsers with [`esm.sh`][esmsh]: On the API: ```js -import {read} from 'to-vfile' -import {reporter} from 'vfile-reporter' import {remark} from 'remark' import remarkLint from 'remark-lint' import remarkLintStrikethroughMarker from 'remark-lint-strikethrough-marker' +import {read} from 'to-vfile' +import {reporter} from 'vfile-reporter' -main() +const file = await read('example.md') -async function main() { - const file = await remark() - .use(remarkLint) - .use(remarkLintStrikethroughMarker) - .process(await read('example.md')) +await remark() + .use(remarkLint) + .use(remarkLintStrikethroughMarker) + .process(file) - console.error(reporter(file)) -} +console.error(reporter(file)) ``` On the CLI: diff --git a/packages/remark-lint-strong-marker/readme.md b/packages/remark-lint-strong-marker/readme.md index 113696d..110eefc 100644 --- a/packages/remark-lint-strong-marker/readme.md +++ b/packages/remark-lint-strong-marker/readme.md @@ -75,22 +75,20 @@ In browsers with [`esm.sh`][esmsh]: On the API: ```js -import {read} from 'to-vfile' -import {reporter} from 'vfile-reporter' import {remark} from 'remark' import remarkLint from 'remark-lint' import remarkLintStrongMarker from 'remark-lint-strong-marker' +import {read} from 'to-vfile' +import {reporter} from 'vfile-reporter' -main() +const file = await read('example.md') -async function main() { - const file = await remark() - .use(remarkLint) - .use(remarkLintStrongMarker) - .process(await read('example.md')) +await remark() + .use(remarkLint) + .use(remarkLintStrongMarker) + .process(file) - console.error(reporter(file)) -} +console.error(reporter(file)) ``` On the CLI: @@ -178,20 +176,6 @@ __foo__ and __bar__. No messages. -##### `not-ok.md` - -###### In - -```markdown -**foo** and __bar__. -``` - -###### Out - -```text -1:13-1:20: Strong should use `*` as a marker -``` - ##### `ok.md` When configured with `'*'`. @@ -222,6 +206,20 @@ No messages. ##### `not-ok.md` +###### In + +```markdown +**foo** and __bar__. +``` + +###### Out + +```text +1:13-1:20: Strong should use `*` as a marker +``` + +##### `not-ok.md` + When configured with `'💩'`. ###### Out diff --git a/packages/remark-lint-table-cell-padding/readme.md b/packages/remark-lint-table-cell-padding/readme.md index 854f4f0..e08354d 100644 --- a/packages/remark-lint-table-cell-padding/readme.md +++ b/packages/remark-lint-table-cell-padding/readme.md @@ -77,22 +77,20 @@ In browsers with [`esm.sh`][esmsh]: On the API: ```js -import {read} from 'to-vfile' -import {reporter} from 'vfile-reporter' import {remark} from 'remark' import remarkLint from 'remark-lint' import remarkLintTableCellPadding from 'remark-lint-table-cell-padding' +import {read} from 'to-vfile' +import {reporter} from 'vfile-reporter' -main() +const file = await read('example.md') -async function main() { - const file = await remark() - .use(remarkLint) - .use(remarkLintTableCellPadding) - .process(await read('example.md')) +await remark() + .use(remarkLint) + .use(remarkLintTableCellPadding) + .process(file) - console.error(reporter(file)) -} +console.error(reporter(file)) ``` On the CLI: @@ -206,51 +204,6 @@ Too much padding isn’t good either: 13:32: Cell should be padded with 1 space, not 2 ``` -##### `empty.md` - -When configured with `'padded'`. - -###### In - -> 👉 **Note**: this example uses GFM ([`remark-gfm`][gfm]). - -```markdown - - -| | Alpha | Bravo| -| ------ | ----- | ---: | -| Charlie| | Echo| -``` - -###### Out - -```text -3:25: Cell should be padded -5:10: Cell should be padded -5:25: Cell should be padded -``` - -##### `missing-body.md` - -When configured with `'padded'`. - -###### In - -> 👉 **Note**: this example uses GFM ([`remark-gfm`][gfm]). - -```markdown - - -| Alpha | Bravo | Charlie | -| ----- | ------- | ------- | -| Delta | -| Echo | Foxtrot | -``` - -###### Out - -No messages. - ##### `ok.md` When configured with `'compact'`. @@ -397,6 +350,51 @@ When configured with `'💩'`. 1:1: Incorrect table cell padding style `💩`, expected `'padded'`, `'compact'`, or `'consistent'` ``` +##### `empty.md` + +When configured with `'padded'`. + +###### In + +> 👉 **Note**: this example uses GFM ([`remark-gfm`][gfm]). + +```markdown + + +| | Alpha | Bravo| +| ------ | ----- | ---: | +| Charlie| | Echo| +``` + +###### Out + +```text +3:25: Cell should be padded +5:10: Cell should be padded +5:25: Cell should be padded +``` + +##### `missing-body.md` + +When configured with `'padded'`. + +###### In + +> 👉 **Note**: this example uses GFM ([`remark-gfm`][gfm]). + +```markdown + + +| Alpha | Bravo | Charlie | +| ----- | ------- | ------- | +| Delta | +| Echo | Foxtrot | +``` + +###### Out + +No messages. + ## Compatibility Projects maintained by the unified collective are compatible with all maintained diff --git a/packages/remark-lint-table-pipe-alignment/readme.md b/packages/remark-lint-table-pipe-alignment/readme.md index af52310..317d3a6 100644 --- a/packages/remark-lint-table-pipe-alignment/readme.md +++ b/packages/remark-lint-table-pipe-alignment/readme.md @@ -76,22 +76,20 @@ In browsers with [`esm.sh`][esmsh]: On the API: ```js -import {read} from 'to-vfile' -import {reporter} from 'vfile-reporter' import {remark} from 'remark' import remarkLint from 'remark-lint' import remarkLintTablePipeAlignment from 'remark-lint-table-pipe-alignment' +import {read} from 'to-vfile' +import {reporter} from 'vfile-reporter' -main() +const file = await read('example.md') -async function main() { - const file = await remark() - .use(remarkLint) - .use(remarkLintTablePipeAlignment) - .process(await read('example.md')) +await remark() + .use(remarkLint) + .use(remarkLintTablePipeAlignment) + .process(file) - console.error(reporter(file)) -} +console.error(reporter(file)) ``` On the CLI: diff --git a/packages/remark-lint-table-pipes/readme.md b/packages/remark-lint-table-pipes/readme.md index b6a2712..f12c854 100644 --- a/packages/remark-lint-table-pipes/readme.md +++ b/packages/remark-lint-table-pipes/readme.md @@ -78,22 +78,20 @@ In browsers with [`esm.sh`][esmsh]: On the API: ```js -import {read} from 'to-vfile' -import {reporter} from 'vfile-reporter' import {remark} from 'remark' import remarkLint from 'remark-lint' import remarkLintTablePipes from 'remark-lint-table-pipes' +import {read} from 'to-vfile' +import {reporter} from 'vfile-reporter' -main() +const file = await read('example.md') -async function main() { - const file = await remark() - .use(remarkLint) - .use(remarkLintTablePipes) - .process(await read('example.md')) +await remark() + .use(remarkLint) + .use(remarkLintTablePipes) + .process(file) - console.error(reporter(file)) -} +console.error(reporter(file)) ``` On the CLI: diff --git a/packages/remark-lint-unordered-list-marker-style/readme.md b/packages/remark-lint-unordered-list-marker-style/readme.md index 2ecd43a..937b4bb 100644 --- a/packages/remark-lint-unordered-list-marker-style/readme.md +++ b/packages/remark-lint-unordered-list-marker-style/readme.md @@ -75,22 +75,20 @@ In browsers with [`esm.sh`][esmsh]: On the API: ```js -import {read} from 'to-vfile' -import {reporter} from 'vfile-reporter' import {remark} from 'remark' import remarkLint from 'remark-lint' import remarkLintUnorderedListMarkerStyle from 'remark-lint-unordered-list-marker-style' +import {read} from 'to-vfile' +import {reporter} from 'vfile-reporter' -main() +const file = await read('example.md') -async function main() { - const file = await remark() - .use(remarkLint) - .use(remarkLintUnorderedListMarkerStyle) - .process(await read('example.md')) +await remark() + .use(remarkLint) + .use(remarkLintUnorderedListMarkerStyle) + .process(file) - console.error(reporter(file)) -} +console.error(reporter(file)) ``` On the CLI: @@ -174,23 +172,6 @@ Ordered lists are not affected. No messages. -##### `not-ok.md` - -###### In - -```markdown -* Foo -- Bar -+ Baz -``` - -###### Out - -```text -2:1-2:6: Marker style should be `*` -3:1-3:6: Marker style should be `*` -``` - ##### `ok.md` When configured with `'*'`. @@ -235,6 +216,23 @@ No messages. ##### `not-ok.md` +###### In + +```markdown +* Foo +- Bar ++ Baz +``` + +###### Out + +```text +2:1-2:6: Marker style should be `*` +3:1-3:6: Marker style should be `*` +``` + +##### `not-ok.md` + When configured with `'💩'`. ###### Out diff --git a/packages/remark-preset-lint-consistent/readme.md b/packages/remark-preset-lint-consistent/readme.md index fb6e88c..3112bb7 100644 --- a/packages/remark-preset-lint-consistent/readme.md +++ b/packages/remark-preset-lint-consistent/readme.md @@ -82,20 +82,18 @@ In browsers with [`esm.sh`][esmsh]: On the API: ```js -import {read} from 'to-vfile' -import {reporter} from 'vfile-reporter' import {remark} from 'remark' import remarkPresetLintConsistent from 'remark-preset-lint-consistent' +import {read} from 'to-vfile' +import {reporter} from 'vfile-reporter' -main() +const file = await read('example.md') -async function main() { - const file = await remark() - .use(remarkPresetLintConsistent) - .process(await read('example.md')) +await remark() + .use(remarkPresetLintConsistent) + .process(await read('example.md')) - console.error(reporter(file)) -} +console.error(reporter(file)) ``` On the CLI: diff --git a/packages/remark-preset-lint-markdown-style-guide/readme.md b/packages/remark-preset-lint-markdown-style-guide/readme.md index abb687b..77a2fd3 100644 --- a/packages/remark-preset-lint-markdown-style-guide/readme.md +++ b/packages/remark-preset-lint-markdown-style-guide/readme.md @@ -203,20 +203,18 @@ In browsers with [`esm.sh`][esmsh]: On the API: ```js -import {read} from 'to-vfile' -import {reporter} from 'vfile-reporter' import {remark} from 'remark' import remarkPresetLintMarkdownStyleGuide from 'remark-preset-lint-markdown-style-guide' +import {read} from 'to-vfile' +import {reporter} from 'vfile-reporter' -main() +const file = await read('example.md') -async function main() { - const file = await remark() - .use(remarkPresetLintMarkdownStyleGuide) - .process(await read('example.md')) +await remark() + .use(remarkPresetLintMarkdownStyleGuide) + .process(await read('example.md')) - console.error(reporter(file)) -} +console.error(reporter(file)) ``` On the CLI: diff --git a/packages/remark-preset-lint-recommended/readme.md b/packages/remark-preset-lint-recommended/readme.md index 0bcc62c..0ea285c 100644 --- a/packages/remark-preset-lint-recommended/readme.md +++ b/packages/remark-preset-lint-recommended/readme.md @@ -84,20 +84,18 @@ In browsers with [`esm.sh`][esmsh]: On the API: ```js -import {read} from 'to-vfile' -import {reporter} from 'vfile-reporter' import {remark} from 'remark' import remarkPresetLintRecommended from 'remark-preset-lint-recommended' +import {read} from 'to-vfile' +import {reporter} from 'vfile-reporter' -main() +const file = await read('example.md') -async function main() { - const file = await remark() - .use(remarkPresetLintRecommended) - .process(await read('example.md')) +await remark() + .use(remarkPresetLintRecommended) + .process(await read('example.md')) - console.error(reporter(file)) -} +console.error(reporter(file)) ``` On the CLI: diff --git a/readme.md b/readme.md index ff61339..3fa7e78 100644 --- a/readme.md +++ b/readme.md @@ -369,28 +369,24 @@ npm install vfile-reporter remark remark-preset-lint-consistent remark-preset-li Then create a module `example.js` that contains: ```js -import {reporter} from 'vfile-reporter' import {remark} from 'remark' +import remarkLintListItemIndent from 'remark-lint-list-item-indent' import remarkPresetLintConsistent from 'remark-preset-lint-consistent' import remarkPresetLintRecommended from 'remark-preset-lint-recommended' -import remarkLintListItemIndent from 'remark-lint-list-item-indent' +import {reporter} from 'vfile-reporter' -main() +const file = await remark() + // Check that markdown is consistent. + .use(remarkPresetLintConsistent) + // Few recommended rules. + .use(remarkPresetLintRecommended) + // `remark-lint-list-item-indent` is configured with `tab-size` in the + // recommended preset, but if we’d prefer something else, it can be + // reconfigured: + .use(remarkLintListItemIndent, 'space') + .process('1) Hello, _Jupiter_ and *Neptune*!') -async function main() { - const file = await remark() - // Check that markdown is consistent. - .use(remarkPresetLintConsistent) - // Few recommended rules. - .use(remarkPresetLintRecommended) - // `remark-lint-list-item-indent` is configured with `tab-size` in the - // recommended preset, but if we’d prefer something else, it can be - // reconfigured: - .use(remarkLintListItemIndent, 'space') - .process('1) Hello, _Jupiter_ and *Neptune*!') - - console.error(reporter(file)) -} +console.error(reporter(file)) ``` Running that with `node example.js` yields: @@ -411,25 +407,21 @@ When you configure lint rules and use remark to format markdown, you must manually synchronize their configuration: ```js -import {reporter} from 'vfile-reporter' import {remark} from 'remark' import remarkLintEmphasisMarker from 'remark-lint-emphasis-marker' import remarkLintStrongMarker from 'remark-lint-strong-marker' +import {reporter} from 'vfile-reporter' -main() +const file = await remark() + .use(remarkLintEmphasisMarker, '*') + .use(remarkLintStrongMarker, '*') + .use({ + settings: {emphasis: '*', strong: '*'} // `remark-stringify` settings. + }) + .process('_Hello_, __world__!') -async function main() { - const file = await remark() - .use(remarkLintEmphasisMarker, '*') - .use(remarkLintStrongMarker, '*') - .use({ - settings: {emphasis: '*', strong: '*'} // `remark-stringify` settings. - }) - .process('_Hello_, __world__!') - - console.error(reporter(file)) - console.log(String(file)) -} +console.error(reporter(file)) +console.log(String(file)) ``` Yields: diff --git a/script/build-plugins.js b/script/build-plugins.js new file mode 100644 index 0000000..9fedc6f --- /dev/null +++ b/script/build-plugins.js @@ -0,0 +1,919 @@ +/** + * @typedef {import('mdast').TableContent} TableContent + * @typedef {import('mdast').TopLevelContent} TopLevelContent + * + * @typedef {import('type-fest').PackageJson} PackageJson + */ + +import assert from 'node:assert/strict' +import fs from 'node:fs/promises' +import {inspect} from 'node:util' +import {slug as githubSlug} from 'github-slugger' +import {findAndReplace} from 'mdast-util-find-and-replace' +import {fromMarkdown} from 'mdast-util-from-markdown' +import {gfmToMarkdown} from 'mdast-util-gfm' +import {toMarkdown} from 'mdast-util-to-markdown' +import {toString} from 'mdast-util-to-string' +import parseAuthor from 'parse-author' +import {packagesUrl, plugins, presets} from './info.js' +import {characters} from './characters.js' + +/** @type {PackageJson} */ +const pack = JSON.parse(await fs.readFile('package.json', 'utf8')) +assert(pack.repository && typeof pack.repository === 'object') +const remote = pack.repository.url + +let index = -1 + +while (++index < plugins.length) { + const info = plugins[index] + const packageUrl = new URL(info.name + '/', packagesUrl) + /** @type {PackageJson} */ + const pack = JSON.parse( + await fs.readFile(new URL('package.json', packageUrl), 'utf8') + ) + const version = (pack.version || '0').split('.')[0] + const author = + typeof pack.author === 'string' ? parseAuthor(pack.author) : pack.author + const camelcased = info.name.replace( + /-(\w)/g, + function (_, /** @type {string} */ $1) { + return $1.toUpperCase() + } + ) + const org = remote.split('/').slice(0, -1).join('/') + const main = remote + '/blob/main' + const health = org + '/.github' + const hMain = health + '/blob/main' + const slug = remote.split('/').slice(-2).join('/') + let hasGfm = false + const descriptionTree = fromMarkdown(info.description) + const summaryTree = fromMarkdown(info.summary || '') + + // Autolink `remark-lint` + findAndReplace(summaryTree, [ + /remark-lint/g, + function () { + return { + type: 'linkReference', + identifier: 'mono', + referenceType: 'full', + children: [{type: 'inlineCode', value: 'remark-lint'}] + } + } + ]) + + const descriptionContent = /** @type {Array} */ ( + descriptionTree.children + ) + const summaryContent = /** @type {Array} */ ( + summaryTree.children + ) + + assert.equal(info.name, pack.name, 'expected correct package name') + + /** @type {Record>} */ + const categories = {} + let category = 'Intro' + let contentIndex = -1 + + while (++contentIndex < descriptionContent.length) { + const node = descriptionContent[contentIndex] + + if (node.type === 'heading' && node.depth === 2) { + category = githubSlug(toString(node)) + } + + if (!(category in categories)) { + categories[category] = [] + } + + categories[category].push(node) + } + + const includes = presets.filter(function (preset) { + return preset.plugins.find(function (d) { + return d[0] === info.name + }) + }) + + /** @type {Array} */ + const children = [ + {type: 'html', value: ''}, + { + type: 'heading', + depth: 1, + children: [{type: 'text', value: info.name}] + } + ] + + if (info.deprecated) { + children.push(...descriptionContent) + } else { + children.push( + { + type: 'paragraph', + children: [ + { + type: 'linkReference', + identifier: 'build', + referenceType: 'full', + children: [ + { + type: 'imageReference', + identifier: 'build-badge', + referenceType: 'full', + alt: 'Build' + } + ] + }, + {type: 'text', value: '\n'}, + { + type: 'linkReference', + identifier: 'coverage', + referenceType: 'full', + children: [ + { + type: 'imageReference', + identifier: 'coverage-badge', + referenceType: 'full', + alt: 'Coverage' + } + ] + }, + {type: 'text', value: '\n'}, + { + type: 'linkReference', + identifier: 'downloads', + referenceType: 'full', + children: [ + { + type: 'imageReference', + identifier: 'downloads-badge', + referenceType: 'full', + alt: 'Downloads' + } + ] + }, + {type: 'text', value: '\n'}, + { + type: 'linkReference', + identifier: 'size', + referenceType: 'full', + children: [ + { + type: 'imageReference', + identifier: 'size-badge', + referenceType: 'full', + alt: 'Size' + } + ] + }, + {type: 'text', value: '\n'}, + { + type: 'linkReference', + identifier: 'collective', + referenceType: 'full', + children: [ + { + type: 'imageReference', + identifier: 'sponsors-badge', + referenceType: 'full', + alt: 'Sponsors' + } + ] + }, + {type: 'text', value: '\n'}, + { + type: 'linkReference', + identifier: 'collective', + referenceType: 'full', + children: [ + { + type: 'imageReference', + identifier: 'backers-badge', + referenceType: 'full', + alt: 'Backers' + } + ] + }, + {type: 'text', value: '\n'}, + { + type: 'linkReference', + identifier: 'chat', + referenceType: 'full', + children: [ + { + type: 'imageReference', + identifier: 'chat-badge', + referenceType: 'full', + alt: 'Chat' + } + ] + } + ] + }, + ...summaryContent, + { + type: 'heading', + depth: 2, + children: [{type: 'text', value: 'Contents'}] + }, + { + type: 'heading', + depth: 2, + children: [{type: 'text', value: 'What is this?'}] + }, + { + type: 'paragraph', + children: [ + {type: 'text', value: 'This package is a '}, + { + type: 'linkReference', + identifier: 'unified', + referenceType: 'collapsed', + children: [{type: 'text', value: 'unified'}] + }, + {type: 'text', value: ' ('}, + { + type: 'linkReference', + identifier: 'remark', + referenceType: 'collapsed', + children: [{type: 'text', value: 'remark'}] + }, + { + type: 'text', + value: ') plugin, specifically a ' + }, + { + type: 'inlineCode', + value: 'remark-lint' + }, + { + type: 'text', + value: '\nrule.\nLint rules check markdown code style.' + } + ] + }, + ...(categories['when-should-i-use-this'] || []), + { + type: 'heading', + depth: 2, + children: [{type: 'text', value: 'Presets'}] + } + ) + + if (includes.length === 0) { + children.push({ + type: 'paragraph', + children: [ + { + type: 'text', + value: 'This rule is not included in a preset maintained here.' + } + ] + }) + } else { + children.push( + { + type: 'paragraph', + children: [ + { + type: 'text', + value: 'This rule is included in the following presets:' + } + ] + }, + { + type: 'table', + align: [], + children: [ + { + type: 'tableRow', + children: [ + { + type: 'tableCell', + children: [{type: 'text', value: 'Preset'}] + }, + { + type: 'tableCell', + children: [{type: 'text', value: 'Setting'}] + } + ] + }, + ...includes.map(function (preset) { + const tuple = preset.plugins.find(function (d) { + return d[0] === info.name + }) + assert(tuple) + const option = tuple[1] + + /** @type {TableContent} */ + const row = { + type: 'tableRow', + children: [ + { + type: 'tableCell', + children: [ + { + type: 'link', + url: remote + '/tree/main/packages/' + preset.name, + children: [{type: 'inlineCode', value: preset.name}] + } + ] + }, + { + type: 'tableCell', + children: option + ? [{type: 'inlineCode', value: inspect(option)}] + : [] + } + ] + } + + return row + }) + ] + } + ) + } + + children.push( + { + type: 'heading', + depth: 2, + children: [{type: 'text', value: 'Install'}] + }, + { + type: 'paragraph', + children: [ + {type: 'text', value: 'This package is '}, + { + type: 'linkReference', + identifier: 'esm', + referenceType: 'full', + children: [{type: 'text', value: 'ESM only'}] + }, + { + type: 'text', + value: + '.\nIn Node.js (version 12.20+, 14.14+, or 16.0+), ' + + 'install with ' + }, + { + type: 'linkReference', + identifier: 'npm', + referenceType: 'collapsed', + children: [{type: 'text', value: 'npm'}] + }, + {type: 'text', value: ':'} + ] + }, + {type: 'code', lang: 'sh', value: 'npm install ' + info.name}, + { + type: 'paragraph', + children: [ + {type: 'text', value: 'In Deno with '}, + { + type: 'linkReference', + identifier: 'esmsh', + label: 'esmsh', + referenceType: 'full', + children: [{type: 'inlineCode', value: 'esm.sh'}] + }, + {type: 'text', value: ':'} + ] + }, + { + type: 'code', + lang: 'js', + value: + 'import ' + + camelcased + + " from 'https://esm.sh/" + + info.name + + '@' + + version + + "'" + }, + { + type: 'paragraph', + children: [ + {type: 'text', value: 'In browsers with '}, + { + type: 'linkReference', + identifier: 'esmsh', + label: 'esmsh', + referenceType: 'full', + children: [{type: 'inlineCode', value: 'esm.sh'}] + }, + {type: 'text', value: ':'} + ] + }, + { + type: 'code', + lang: 'html', + value: + '" + }, + { + type: 'heading', + depth: 2, + children: [{type: 'text', value: 'Use'}] + }, + { + type: 'paragraph', + children: [{type: 'text', value: 'On the API:'}] + }, + { + type: 'code', + lang: 'js', + value: [ + "import {remark} from 'remark'", + "import remarkLint from 'remark-lint'", + 'import ' + camelcased + " from '" + info.name + "'", + "import {read} from 'to-vfile'", + "import {reporter} from 'vfile-reporter'", + '', + "const file = await read('example.md')", + '', + 'await remark()', + ' .use(remarkLint)', + ' .use(' + camelcased + ')', + ' .process(file)', + '', + 'console.error(reporter(file))' + ].join('\n') + }, + { + type: 'paragraph', + children: [{type: 'text', value: 'On the CLI:'}] + }, + { + type: 'code', + lang: 'sh', + value: 'remark --use remark-lint --use ' + info.name + ' example.md' + }, + { + type: 'paragraph', + children: [ + { + type: 'text', + value: 'On the CLI in a config file (here a ' + }, + { + type: 'inlineCode', + value: 'package.json' + }, + { + type: 'text', + value: '):' + } + ] + }, + { + type: 'code', + lang: 'diff', + value: [ + ' …', + ' "remarkConfig": {', + ' "plugins": [', + ' …', + ' "remark-lint",', + '+ "' + info.name + '",', + ' …', + ' ]', + ' }', + ' …' + ].join('\n') + } + ) + + if ('api' in categories) { + const [apiHeading, ...apiBody] = categories.api + + children.push( + apiHeading, + { + type: 'paragraph', + children: [ + { + type: 'text', + value: + 'This package exports no identifiers.\nThe default export is ' + }, + {type: 'inlineCode', value: camelcased}, + {type: 'text', value: '.'} + ] + }, + { + type: 'heading', + depth: 3, + children: [ + { + type: 'inlineCode', + value: 'unified().use(' + camelcased + '[, config])' + } + ] + }, + { + type: 'paragraph', + children: [ + { + type: 'text', + value: + 'This rule supports standard configuration that all remark lint rules accept\n(such as ' + }, + {type: 'inlineCode', value: 'false'}, + {type: 'text', value: ' to turn it off or '}, + {type: 'inlineCode', value: '[1, options]'}, + {type: 'text', value: ' to configure it).'} + ] + }, + ...apiBody + ) + } + + children.push( + ...(categories.recommendation || []), + ...(categories.fix || []), + ...(categories.example || []) + ) + + let first = true + + for (const check of info.checks) { + if (first) { + children.push({ + type: 'heading', + depth: 2, + children: [{type: 'text', value: 'Examples'}] + }) + first = false + } + + /** @type {{config: unknown}} */ + const {config} = JSON.parse(check.configuration) + let clean = check.input + + children.push({ + type: 'heading', + depth: 5, + children: [{type: 'inlineCode', value: check.name}] + }) + + if (config !== true) { + children.push({ + type: 'paragraph', + children: [ + {type: 'text', value: 'When configured with '}, + {type: 'inlineCode', value: inspect(config)}, + {type: 'text', value: '.'} + ] + }) + } + + if (check.input.trim() !== '') { + children.push({ + type: 'heading', + depth: 6, + children: [{type: 'text', value: 'In'}] + }) + + if (check.gfm) { + hasGfm = true + children.push({ + type: 'blockquote', + children: [ + { + type: 'paragraph', + children: [ + {type: 'text', value: '👉 '}, + { + type: 'strong', + children: [{type: 'text', value: 'Note'}] + }, + {type: 'text', value: ': this example uses GFM ('}, + { + type: 'linkReference', + identifier: 'gfm', + referenceType: 'full', + children: [{type: 'inlineCode', value: 'remark-gfm'}] + }, + {type: 'text', value: ').'} + ] + } + ] + }) + } + + let index = -1 + while (++index < characters.length) { + const char = characters[index] + const next = clean.replace(char.in, char.out) + + if (clean !== next) { + children.push({ + type: 'blockquote', + children: [ + { + type: 'paragraph', + children: [ + {type: 'text', value: '👉 '}, + { + type: 'strong', + children: [{type: 'text', value: 'Note'}] + }, + {type: 'text', value: ': '}, + {type: 'inlineCode', value: char.char}, + { + type: 'text', + value: ' represents ' + char.name + '.' + } + ] + } + ] + }) + + clean = next + } + } + + children.push({ + type: 'code', + lang: 'markdown', + value: check.input + }) + } + + children.push({ + type: 'heading', + depth: 6, + children: [{type: 'text', value: 'Out'}] + }) + + if (check.output.length === 0) { + children.push({ + type: 'paragraph', + children: [{type: 'text', value: 'No messages.'}] + }) + } else { + children.push({ + type: 'code', + lang: 'text', + value: check.output.join('\n') + }) + } + } + + children.push( + { + type: 'heading', + depth: 2, + children: [{type: 'text', value: 'Compatibility'}] + }, + { + type: 'paragraph', + children: [ + { + type: 'text', + value: + 'Projects maintained by the unified collective are compatible with all maintained\nversions of Node.js.\nAs of now, that is Node.js 12.20+, 14.14+, and 16.0+.\nOur projects sometimes work with older versions, but this is not guaranteed.' + } + ] + }, + { + type: 'heading', + depth: 2, + children: [{type: 'text', value: 'Contribute'}] + }, + { + type: 'paragraph', + children: [ + {type: 'text', value: 'See '}, + { + type: 'linkReference', + referenceType: 'collapsed', + identifier: 'contributing', + children: [{type: 'inlineCode', value: 'contributing.md'}] + }, + {type: 'text', value: ' in '}, + { + type: 'linkReference', + referenceType: 'collapsed', + identifier: 'health', + children: [ + { + type: 'inlineCode', + value: health.split('/').slice(-2).join('/') + } + ] + }, + {type: 'text', value: ' for ways\nto get started.\nSee '}, + { + type: 'linkReference', + referenceType: 'collapsed', + identifier: 'support', + children: [{type: 'inlineCode', value: 'support.md'}] + }, + {type: 'text', value: ' for ways to get help.'} + ] + }, + { + type: 'paragraph', + children: [ + {type: 'text', value: 'This project has a '}, + { + type: 'linkReference', + referenceType: 'collapsed', + identifier: 'coc', + children: [{type: 'text', value: 'code of conduct'}] + }, + { + type: 'text', + value: + '.\nBy interacting with this repository, organization, or community you agree to\nabide by its terms.' + } + ] + } + ) + } + + children.push( + {type: 'heading', depth: 2, children: [{type: 'text', value: 'License'}]}, + { + type: 'paragraph', + children: [ + { + type: 'linkReference', + referenceType: 'collapsed', + identifier: 'license', + children: [{type: 'text', value: String(pack.license || '')}] + }, + {type: 'text', value: ' © '}, + { + type: 'linkReference', + referenceType: 'collapsed', + identifier: 'author', + children: [ + {type: 'text', value: String((author && author.name) || '')} + ] + } + ] + } + ) + + if (!info.deprecated) { + children.push( + { + type: 'definition', + identifier: 'build-badge', + url: 'https://github.com/' + slug + '/workflows/main/badge.svg' + }, + { + type: 'definition', + identifier: 'build', + url: 'https://github.com/' + slug + '/actions' + }, + { + type: 'definition', + identifier: 'coverage-badge', + url: 'https://img.shields.io/codecov/c/github/' + slug + '.svg' + }, + { + type: 'definition', + identifier: 'coverage', + url: 'https://codecov.io/github/' + slug + }, + { + type: 'definition', + identifier: 'downloads-badge', + url: 'https://img.shields.io/npm/dm/' + info.name + '.svg' + }, + { + type: 'definition', + identifier: 'downloads', + url: 'https://www.npmjs.com/package/' + info.name + }, + { + type: 'definition', + identifier: 'size-badge', + url: 'https://img.shields.io/bundlephobia/minzip/' + info.name + '.svg' + }, + { + type: 'definition', + identifier: 'size', + url: 'https://bundlephobia.com/result?p=' + info.name + }, + { + type: 'definition', + identifier: 'sponsors-badge', + url: 'https://opencollective.com/unified/sponsors/badge.svg' + }, + { + type: 'definition', + identifier: 'backers-badge', + url: 'https://opencollective.com/unified/backers/badge.svg' + }, + { + type: 'definition', + identifier: 'collective', + url: 'https://opencollective.com/unified' + }, + { + type: 'definition', + identifier: 'chat-badge', + url: 'https://img.shields.io/badge/chat-discussions-success.svg' + }, + { + type: 'definition', + identifier: 'chat', + url: 'https://github.com/remarkjs/remark/discussions' + }, + { + type: 'definition', + identifier: 'unified', + url: 'https://github.com/unifiedjs/unified' + }, + { + type: 'definition', + identifier: 'remark', + url: 'https://github.com/remarkjs/remark' + }, + { + type: 'definition', + identifier: 'mono', + url: 'https://github.com/' + slug + }, + { + type: 'definition', + identifier: 'esm', + url: 'https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c' + }, + { + type: 'definition', + identifier: 'esmsh', + url: 'https://esm.sh' + }, + { + type: 'definition', + identifier: 'npm', + url: 'https://docs.npmjs.com/cli/install' + }, + { + type: 'definition', + identifier: 'health', + url: health + }, + { + type: 'definition', + identifier: 'contributing', + url: hMain + '/contributing.md' + }, + { + type: 'definition', + identifier: 'support', + url: hMain + '/support.md' + }, + { + type: 'definition', + identifier: 'coc', + url: hMain + '/code-of-conduct.md' + } + ) + } + + children.push( + { + type: 'definition', + identifier: 'license', + url: main + '/license' + }, + { + type: 'definition', + identifier: 'author', + url: String((author && author.url) || '') + } + ) + + if (hasGfm) { + children.push({ + type: 'definition', + identifier: 'gfm', + url: 'https://github.com/remarkjs/remark-gfm' + }) + } + + await fs.writeFile( + new URL('readme.md', packageUrl), + toMarkdown({type: 'root', children}, {extensions: [gfmToMarkdown()]}) + ) + + console.log('✓ wrote `readme.md` in `' + info.name + '`') +} diff --git a/script/build-presets.js b/script/build-presets.js index 6872e58..3e92e4f 100644 --- a/script/build-presets.js +++ b/script/build-presets.js @@ -1,732 +1,690 @@ /** - * @typedef {import('mdast').BlockContent} BlockContent - * @typedef {import('mdast').DefinitionContent} DefinitionContent * @typedef {import('mdast').TableContent} TableContent + * @typedef {import('mdast').TopLevelContent} TopLevelContent + * * @typedef {import('type-fest').PackageJson} PackageJson */ -/** - * @typedef {BlockContent | DefinitionContent} BlockAndDefinitionContent - */ - -import fs from 'node:fs' -import path from 'node:path' -import process from 'node:process' +import assert from 'node:assert/strict' +import fs from 'node:fs/promises' import {inspect} from 'node:util' import {parse} from 'comment-parser' -import {unified} from 'unified' -import {remark} from 'remark' -import remarkParse from 'remark-parse' -import remarkGfm from 'remark-gfm' import {findAndReplace} from 'mdast-util-find-and-replace' -import strip from 'strip-indent' +import {fromMarkdown} from 'mdast-util-from-markdown' +import {gfmToMarkdown} from 'mdast-util-gfm' +import {toMarkdown} from 'mdast-util-to-markdown' import parseAuthor from 'parse-author' -import {presets} from './util/presets.js' -import {repoUrl} from './util/repo-url.js' +import stripIndent from 'strip-indent' +import {packagesUrl, presets} from './info.js' -const remote = repoUrl('package.json') +/** @type {PackageJson} */ +const pack = JSON.parse(await fs.readFile('package.json', 'utf8')) +assert(pack.repository && typeof pack.repository === 'object') +const remote = pack.repository.url -const own = {}.hasOwnProperty +for (const {name, plugins} of presets) { + const packageUrl = new URL(name + '/', packagesUrl) + /** @type {PackageJson} */ + const pack = JSON.parse( + await fs.readFile(new URL('package.json', packageUrl), 'utf8') + ) + const version = (pack.version || '0').split('.')[0] -const root = path.join(process.cwd(), 'packages') + const doc = await fs.readFile(new URL('index.js', packageUrl), 'utf8') + const fileInfo = parse(doc, {spacing: 'preserve'})[0] + const tags = fileInfo.tags + const summaryTag = tags.find(function (d) { + return d.tag === 'summary' + }) + const author = + typeof pack.author === 'string' ? parseAuthor(pack.author) : pack.author + const descriptionTree = fromMarkdown(stripIndent(fileInfo.description).trim()) + const summaryTree = fromMarkdown( + stripIndent(summaryTag ? summaryTag.description : '').trim() + ) -// eslint-disable-next-line unicorn/prefer-top-level-await -presets(root).then((presetObjects) => { - let index = -1 - - while (++index < presetObjects.length) { - const {name, packages} = presetObjects[index] - const base = path.resolve(root, name) - /** @type {PackageJson} */ - const pack = JSON.parse( - String(fs.readFileSync(path.join(base, 'package.json'))) - ) - const version = (pack.version || '0').split('.')[0] - const doc = fs.readFileSync(path.join(base, 'index.js'), 'utf8') - // Note: To do: `comment-parser` types are wrong. - /** @type {import('comment-parser/primitives').Block} */ - const fileInfo = parse(doc, {spacing: 'preserve'})[0] - const tags = fileInfo.tags - const summaryTag = tags.find((d) => d.tag === 'summary') - const author = - typeof pack.author === 'string' ? parseAuthor(pack.author) : pack.author - const descriptionTree = unified() - .use(remarkParse) - .parse(strip(fileInfo.description).trim()) - const summaryTree = unified() - .use(remarkParse) - .parse(strip(summaryTag ? summaryTag.description : '').trim()) - - // Autolink `remark-lint` - unified() - .use( - /** @type {import('unified').Plugin, import('mdast').Root>} */ - () => (tree) => { - findAndReplace(tree, [ - /remark-lint/g, - () => { - return { - type: 'linkReference', - identifier: 'mono', - referenceType: 'full', - children: [{type: 'inlineCode', value: 'remark-lint'}] - } - } - ]) - } - ) - .runSync(summaryTree) - - const camelcased = name.replace(/-(\w)/g, (_, /** @type {string} */ $1) => - $1.toUpperCase() - ) - const org = remote.split('/').slice(0, -1).join('/') - const main = remote + '/blob/main' - const health = org + '/.github' - const hMain = health + '/blob/main' - const slug = remote.split('/').slice(-2).join('/') - - if (name !== pack.name) { - throw new Error( - 'Expected package name (`' + - pack.name + - '`) to be the same as ' + - 'directory name (`' + - name + - '`)' - ) + // Autolink `remark-lint` + findAndReplace(summaryTree, [ + /remark-lint/g, + function () { + return { + type: 'linkReference', + identifier: 'mono', + referenceType: 'full', + children: [{type: 'inlineCode', value: 'remark-lint'}] + } } + ]) - const descriptionContent = /** @type {Array} */ ( - descriptionTree.children - ) - const summaryContent = /** @type {Array} */ ( - summaryTree.children - ) + const camelcased = name.replace( + /-(\w)/g, + function (_, /** @type {string} */ $1) { + return $1.toUpperCase() + } + ) + const org = remote.split('/').slice(0, -1).join('/') + const main = remote + '/blob/main' + const health = org + '/.github' + const hMain = health + '/blob/main' + const slug = remote.split('/').slice(-2).join('/') - /** @type {Array} */ - const rows = [] + assert.equal(name, pack.name, 'expected correct package name') + + const descriptionContent = /** @type {Array} */ ( + descriptionTree.children + ) + const summaryContent = /** @type {Array} */ ( + summaryTree.children + ) + + /** @type {Array} */ + const rows = [] + + rows.push({ + type: 'tableRow', + children: [ + {type: 'tableCell', children: [{type: 'text', value: 'Rule'}]}, + {type: 'tableCell', children: [{type: 'text', value: 'Setting'}]} + ] + }) + + for (const [rule, option] of plugins) { + if (rule === 'remark-lint') continue rows.push({ type: 'tableRow', children: [ - {type: 'tableCell', children: [{type: 'text', value: 'Rule'}]}, - {type: 'tableCell', children: [{type: 'text', value: 'Setting'}]} - ] - }) - - /** @type {string} */ - let rule - - for (rule in packages) { - if (own.call(packages, rule)) { - const url = remote + '/tree/main/packages/' + rule - const option = packages[rule] - - if (rule === 'remark-lint') continue - - rows.push({ - type: 'tableRow', + { + type: 'tableCell', children: [ { - type: 'tableCell', - children: [ - { - type: 'link', - url, - title: null, - children: [{type: 'inlineCode', value: rule}] - } - ] - }, - { - type: 'tableCell', - children: option - ? [{type: 'inlineCode', value: inspect(option)}] - : [] + type: 'link', + url: remote + '/tree/main/packages/' + rule, + children: [{type: 'inlineCode', value: rule}] } ] - }) - } - } - - /** @type {Array} */ - const children = [ - {type: 'html', value: ''}, - { - type: 'heading', - depth: 1, - children: [{type: 'text', value: name}] - }, - { - type: 'paragraph', - children: [ - { - type: 'linkReference', - identifier: 'build', - referenceType: 'full', - children: [ - { - type: 'imageReference', - identifier: 'build-badge', - referenceType: 'full', - alt: 'Build' - } - ] - }, - {type: 'text', value: '\n'}, - { - type: 'linkReference', - identifier: 'coverage', - referenceType: 'full', - children: [ - { - type: 'imageReference', - identifier: 'coverage-badge', - referenceType: 'full', - alt: 'Coverage' - } - ] - }, - {type: 'text', value: '\n'}, - { - type: 'linkReference', - identifier: 'downloads', - referenceType: 'full', - children: [ - { - type: 'imageReference', - identifier: 'downloads-badge', - referenceType: 'full', - alt: 'Downloads' - } - ] - }, - {type: 'text', value: '\n'}, - { - type: 'linkReference', - identifier: 'size', - referenceType: 'full', - children: [ - { - type: 'imageReference', - identifier: 'size-badge', - referenceType: 'full', - alt: 'Size' - } - ] - }, - {type: 'text', value: '\n'}, - { - type: 'linkReference', - identifier: 'collective', - referenceType: 'full', - children: [ - { - type: 'imageReference', - identifier: 'sponsors-badge', - referenceType: 'full', - alt: 'Sponsors' - } - ] - }, - {type: 'text', value: '\n'}, - { - type: 'linkReference', - identifier: 'collective', - referenceType: 'full', - children: [ - { - type: 'imageReference', - identifier: 'backers-badge', - referenceType: 'full', - alt: 'Backers' - } - ] - }, - {type: 'text', value: '\n'}, - { - type: 'linkReference', - identifier: 'chat', - referenceType: 'full', - children: [ - { - type: 'imageReference', - identifier: 'chat-badge', - referenceType: 'full', - alt: 'Chat' - } - ] - } - ] - }, - ...summaryContent, - { - type: 'heading', - depth: 2, - children: [{type: 'text', value: 'Contents'}] - }, - { - type: 'heading', - depth: 2, - children: [{type: 'text', value: 'What is this?'}] - }, - { - type: 'paragraph', - children: [ - {type: 'text', value: 'This package is a '}, - { - type: 'linkReference', - identifier: 'unified', - referenceType: 'collapsed', - children: [{type: 'text', value: 'unified'}] - }, - {type: 'text', value: ' ('}, - { - type: 'linkReference', - identifier: 'remark', - referenceType: 'collapsed', - children: [{type: 'text', value: 'remark'}] - }, - { - type: 'text', - value: ') preset, specifically consisting of\n' - }, - { - type: 'inlineCode', - value: 'remark-lint' - }, - { - type: 'text', - value: ' rules.\nLint rules check markdown code style.' - } - ] - }, - ...descriptionContent, - { - type: 'heading', - depth: 2, - children: [{type: 'text', value: 'Rules'}] - }, - { - type: 'paragraph', - children: [ - {type: 'text', value: 'This preset configures '}, - { - type: 'linkReference', - identifier: 'mono', - referenceType: 'full', - children: [{type: 'inlineCode', value: 'remark-lint'}] - }, - {type: 'text', value: ' with the following rules:'} - ] - }, - {type: 'table', align: [], children: rows}, - { - type: 'heading', - depth: 2, - children: [{type: 'text', value: 'Install'}] - }, - { - type: 'paragraph', - children: [ - {type: 'text', value: 'This package is '}, - { - type: 'linkReference', - identifier: 'esm', - referenceType: 'full', - children: [{type: 'text', value: 'ESM only'}] - }, - { - type: 'text', - value: - '.\nIn Node.js (version 12.20+, 14.14+, or 16.0+), ' + - 'install with ' - }, - { - type: 'linkReference', - identifier: 'npm', - referenceType: 'collapsed', - children: [{type: 'text', value: 'npm'}] - }, - {type: 'text', value: ':'} - ] - }, - {type: 'code', lang: 'sh', value: 'npm install ' + name}, - { - type: 'paragraph', - children: [ - {type: 'text', value: 'In Deno with '}, - { - type: 'linkReference', - identifier: 'esmsh', - label: 'esmsh', - referenceType: 'full', - children: [{type: 'inlineCode', value: 'esm.sh'}] - }, - {type: 'text', value: ':'} - ] - }, - { - type: 'code', - lang: 'js', - value: - 'import ' + - camelcased + - " from 'https://esm.sh/" + - name + - '@' + - version + - "'" - }, - { - type: 'paragraph', - children: [ - {type: 'text', value: 'In browsers with '}, - { - type: 'linkReference', - identifier: 'esmsh', - label: 'esmsh', - referenceType: 'full', - children: [{type: 'inlineCode', value: 'esm.sh'}] - }, - {type: 'text', value: ':'} - ] - }, - { - type: 'code', - lang: 'html', - value: - '" - }, - { - type: 'heading', - depth: 2, - children: [{type: 'text', value: 'Use'}] - }, - { - type: 'paragraph', - children: [{type: 'text', value: 'On the API:'}] - }, - { - type: 'code', - lang: 'js', - value: [ - "import {read} from 'to-vfile'", - "import {reporter} from 'vfile-reporter'", - "import {remark} from 'remark'", - 'import ' + camelcased + " from '" + name + "'", - '', - 'main()', - '', - 'async function main() {', - ' const file = await remark()', - ' .use(' + camelcased + ')', - " .process(await read('example.md'))", - '', - ' console.error(reporter(file))', - '}' - ].join('\n') - }, - { - type: 'paragraph', - children: [{type: 'text', value: 'On the CLI:'}] - }, - { - type: 'code', - lang: 'sh', - value: 'remark --use ' + name + ' example.md' - }, - { - type: 'paragraph', - children: [ - { - type: 'text', - value: 'On the CLI in a config file (here a ' - }, - { - type: 'inlineCode', - value: 'package.json' - }, - { - type: 'text', - value: '):' - } - ] - }, - { - type: 'code', - lang: 'diff', - value: [ - ' …', - ' "remarkConfig": {', - ' "plugins": [', - ' …', - '+ "' + name + '",', - ' …', - ' ]', - ' }', - ' …' - ].join('\n') - }, - {type: 'heading', depth: 2, children: [{type: 'text', value: 'API'}]}, - { - type: 'paragraph', - children: [ - { - type: 'text', - value: - 'This package exports no identifiers.\nThe default export is ' - }, - {type: 'inlineCode', value: camelcased}, - {type: 'text', value: '.'} - ] - }, - { - type: 'heading', - depth: 3, - children: [ - {type: 'inlineCode', value: 'unified().use(' + camelcased + ')'} - ] - }, - { - type: 'paragraph', - children: [ - { - type: 'text', - value: - 'Use the preset.\nPresets don’t have options.\nYou can reconfigure rules in them by using the afterwards with different\noptions.' - } - ] - }, - { - type: 'heading', - depth: 2, - children: [{type: 'text', value: 'Compatibility'}] - }, - { - type: 'paragraph', - children: [ - { - type: 'text', - value: - 'Projects maintained by the unified collective are compatible with all maintained\nversions of Node.js.\nAs of now, that is Node.js 12.20+, 14.14+, and 16.0+.\nOur projects sometimes work with older versions, but this is not guaranteed.' - } - ] - }, - { - type: 'heading', - depth: 2, - children: [{type: 'text', value: 'Contribute'}] - }, - { - type: 'paragraph', - children: [ - {type: 'text', value: 'See '}, - { - type: 'linkReference', - referenceType: 'collapsed', - identifier: 'contributing', - children: [{type: 'inlineCode', value: 'contributing.md'}] - }, - {type: 'text', value: ' in '}, - { - type: 'linkReference', - referenceType: 'collapsed', - identifier: 'health', - children: [ - { - type: 'inlineCode', - value: health.split('/').slice(-2).join('/') - } - ] - }, - {type: 'text', value: ' for ways\nto get started.\nSee '}, - { - type: 'linkReference', - referenceType: 'collapsed', - identifier: 'support', - children: [{type: 'inlineCode', value: 'support.md'}] - }, - {type: 'text', value: ' for ways to get help.'} - ] - }, - { - type: 'paragraph', - children: [ - {type: 'text', value: 'This project has a '}, - { - type: 'linkReference', - referenceType: 'collapsed', - identifier: 'coc', - children: [{type: 'text', value: 'code of conduct'}] - }, - { - type: 'text', - value: - '.\nBy interacting with this repository, organization, or community you agree to\nabide by its terms.' - } - ] - }, - {type: 'heading', depth: 2, children: [{type: 'text', value: 'License'}]}, - { - type: 'paragraph', - children: [ - { - type: 'linkReference', - referenceType: 'collapsed', - identifier: 'license', - children: [{type: 'text', value: String(pack.license || '')}] - }, - {type: 'text', value: ' © '}, - { - type: 'linkReference', - referenceType: 'collapsed', - identifier: 'author', - children: [ - {type: 'text', value: String((author && author.name) || '')} - ] - } - ] - }, - { - type: 'definition', - identifier: 'build-badge', - url: 'https://github.com/' + slug + '/workflows/main/badge.svg' - }, - { - type: 'definition', - identifier: 'build', - url: 'https://github.com/' + slug + '/actions' - }, - { - type: 'definition', - identifier: 'coverage-badge', - url: 'https://img.shields.io/codecov/c/github/' + slug + '.svg' - }, - { - type: 'definition', - identifier: 'coverage', - url: 'https://codecov.io/github/' + slug - }, - { - type: 'definition', - identifier: 'downloads-badge', - url: 'https://img.shields.io/npm/dm/' + name + '.svg' - }, - { - type: 'definition', - identifier: 'downloads', - url: 'https://www.npmjs.com/package/' + name - }, - { - type: 'definition', - identifier: 'size-badge', - url: 'https://img.shields.io/bundlephobia/minzip/' + name + '.svg' - }, - { - type: 'definition', - identifier: 'size', - url: 'https://bundlephobia.com/result?p=' + name - }, - { - type: 'definition', - identifier: 'sponsors-badge', - url: 'https://opencollective.com/unified/sponsors/badge.svg' - }, - { - type: 'definition', - identifier: 'backers-badge', - url: 'https://opencollective.com/unified/backers/badge.svg' - }, - { - type: 'definition', - identifier: 'collective', - url: 'https://opencollective.com/unified' - }, - { - type: 'definition', - identifier: 'chat-badge', - url: 'https://img.shields.io/badge/chat-discussions-success.svg' - }, - { - type: 'definition', - identifier: 'chat', - url: 'https://github.com/remarkjs/remark/discussions' - }, - { - type: 'definition', - identifier: 'unified', - url: 'https://github.com/unifiedjs/unified' - }, - { - type: 'definition', - identifier: 'remark', - url: 'https://github.com/remarkjs/remark' - }, - { - type: 'definition', - identifier: 'mono', - url: 'https://github.com/' + slug - }, - { - type: 'definition', - identifier: 'esm', - url: 'https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c' - }, - { - type: 'definition', - identifier: 'esmsh', - url: 'https://esm.sh' - }, - { - type: 'definition', - identifier: 'npm', - url: 'https://docs.npmjs.com/cli/install' - }, - { - type: 'definition', - identifier: 'health', - url: health - }, - { - type: 'definition', - identifier: 'contributing', - url: hMain + '/contributing.md' - }, - { - type: 'definition', - identifier: 'support', - url: hMain + '/support.md' - }, - { - type: 'definition', - identifier: 'coc', - url: hMain + '/code-of-conduct.md' - }, - { - type: 'definition', - identifier: 'license', - url: main + '/license' - }, - { - type: 'definition', - identifier: 'author', - url: String((author && author.url) || '') - } - ] - - fs.writeFileSync( - path.join(base, 'readme.md'), - remark().use(remarkGfm).stringify({type: 'root', children}) - ) - - console.log('✓ wrote `readme.md` in `' + name + '`') + }, + { + type: 'tableCell', + children: option ? [{type: 'inlineCode', value: inspect(option)}] : [] + } + ] + }) } -}) + + /** @type {Array} */ + const children = [ + {type: 'html', value: ''}, + { + type: 'heading', + depth: 1, + children: [{type: 'text', value: name}] + }, + { + type: 'paragraph', + children: [ + { + type: 'linkReference', + identifier: 'build', + referenceType: 'full', + children: [ + { + type: 'imageReference', + identifier: 'build-badge', + referenceType: 'full', + alt: 'Build' + } + ] + }, + {type: 'text', value: '\n'}, + { + type: 'linkReference', + identifier: 'coverage', + referenceType: 'full', + children: [ + { + type: 'imageReference', + identifier: 'coverage-badge', + referenceType: 'full', + alt: 'Coverage' + } + ] + }, + {type: 'text', value: '\n'}, + { + type: 'linkReference', + identifier: 'downloads', + referenceType: 'full', + children: [ + { + type: 'imageReference', + identifier: 'downloads-badge', + referenceType: 'full', + alt: 'Downloads' + } + ] + }, + {type: 'text', value: '\n'}, + { + type: 'linkReference', + identifier: 'size', + referenceType: 'full', + children: [ + { + type: 'imageReference', + identifier: 'size-badge', + referenceType: 'full', + alt: 'Size' + } + ] + }, + {type: 'text', value: '\n'}, + { + type: 'linkReference', + identifier: 'collective', + referenceType: 'full', + children: [ + { + type: 'imageReference', + identifier: 'sponsors-badge', + referenceType: 'full', + alt: 'Sponsors' + } + ] + }, + {type: 'text', value: '\n'}, + { + type: 'linkReference', + identifier: 'collective', + referenceType: 'full', + children: [ + { + type: 'imageReference', + identifier: 'backers-badge', + referenceType: 'full', + alt: 'Backers' + } + ] + }, + {type: 'text', value: '\n'}, + { + type: 'linkReference', + identifier: 'chat', + referenceType: 'full', + children: [ + { + type: 'imageReference', + identifier: 'chat-badge', + referenceType: 'full', + alt: 'Chat' + } + ] + } + ] + }, + ...summaryContent, + { + type: 'heading', + depth: 2, + children: [{type: 'text', value: 'Contents'}] + }, + { + type: 'heading', + depth: 2, + children: [{type: 'text', value: 'What is this?'}] + }, + { + type: 'paragraph', + children: [ + {type: 'text', value: 'This package is a '}, + { + type: 'linkReference', + identifier: 'unified', + referenceType: 'collapsed', + children: [{type: 'text', value: 'unified'}] + }, + {type: 'text', value: ' ('}, + { + type: 'linkReference', + identifier: 'remark', + referenceType: 'collapsed', + children: [{type: 'text', value: 'remark'}] + }, + { + type: 'text', + value: ') preset, specifically consisting of\n' + }, + { + type: 'inlineCode', + value: 'remark-lint' + }, + { + type: 'text', + value: ' rules.\nLint rules check markdown code style.' + } + ] + }, + ...descriptionContent, + { + type: 'heading', + depth: 2, + children: [{type: 'text', value: 'Rules'}] + }, + { + type: 'paragraph', + children: [ + {type: 'text', value: 'This preset configures '}, + { + type: 'linkReference', + identifier: 'mono', + referenceType: 'full', + children: [{type: 'inlineCode', value: 'remark-lint'}] + }, + {type: 'text', value: ' with the following rules:'} + ] + }, + {type: 'table', align: [], children: rows}, + { + type: 'heading', + depth: 2, + children: [{type: 'text', value: 'Install'}] + }, + { + type: 'paragraph', + children: [ + {type: 'text', value: 'This package is '}, + { + type: 'linkReference', + identifier: 'esm', + referenceType: 'full', + children: [{type: 'text', value: 'ESM only'}] + }, + { + type: 'text', + value: + '.\nIn Node.js (version 12.20+, 14.14+, or 16.0+), ' + + 'install with ' + }, + { + type: 'linkReference', + identifier: 'npm', + referenceType: 'collapsed', + children: [{type: 'text', value: 'npm'}] + }, + {type: 'text', value: ':'} + ] + }, + {type: 'code', lang: 'sh', value: 'npm install ' + name}, + { + type: 'paragraph', + children: [ + {type: 'text', value: 'In Deno with '}, + { + type: 'linkReference', + identifier: 'esmsh', + label: 'esmsh', + referenceType: 'full', + children: [{type: 'inlineCode', value: 'esm.sh'}] + }, + {type: 'text', value: ':'} + ] + }, + { + type: 'code', + lang: 'js', + value: + 'import ' + + camelcased + + " from 'https://esm.sh/" + + name + + '@' + + version + + "'" + }, + { + type: 'paragraph', + children: [ + {type: 'text', value: 'In browsers with '}, + { + type: 'linkReference', + identifier: 'esmsh', + label: 'esmsh', + referenceType: 'full', + children: [{type: 'inlineCode', value: 'esm.sh'}] + }, + {type: 'text', value: ':'} + ] + }, + { + type: 'code', + lang: 'html', + value: + '" + }, + { + type: 'heading', + depth: 2, + children: [{type: 'text', value: 'Use'}] + }, + { + type: 'paragraph', + children: [{type: 'text', value: 'On the API:'}] + }, + { + type: 'code', + lang: 'js', + value: [ + "import {remark} from 'remark'", + 'import ' + camelcased + " from '" + name + "'", + "import {read} from 'to-vfile'", + "import {reporter} from 'vfile-reporter'", + '', + "const file = await read('example.md')", + '', + 'await remark()', + ' .use(' + camelcased + ')', + " .process(await read('example.md'))", + '', + 'console.error(reporter(file))' + ].join('\n') + }, + { + type: 'paragraph', + children: [{type: 'text', value: 'On the CLI:'}] + }, + { + type: 'code', + lang: 'sh', + value: 'remark --use ' + name + ' example.md' + }, + { + type: 'paragraph', + children: [ + { + type: 'text', + value: 'On the CLI in a config file (here a ' + }, + { + type: 'inlineCode', + value: 'package.json' + }, + { + type: 'text', + value: '):' + } + ] + }, + { + type: 'code', + lang: 'diff', + value: [ + ' …', + ' "remarkConfig": {', + ' "plugins": [', + ' …', + '+ "' + name + '",', + ' …', + ' ]', + ' }', + ' …' + ].join('\n') + }, + {type: 'heading', depth: 2, children: [{type: 'text', value: 'API'}]}, + { + type: 'paragraph', + children: [ + { + type: 'text', + value: 'This package exports no identifiers.\nThe default export is ' + }, + {type: 'inlineCode', value: camelcased}, + {type: 'text', value: '.'} + ] + }, + { + type: 'heading', + depth: 3, + children: [ + {type: 'inlineCode', value: 'unified().use(' + camelcased + ')'} + ] + }, + { + type: 'paragraph', + children: [ + { + type: 'text', + value: + 'Use the preset.\nPresets don’t have options.\nYou can reconfigure rules in them by using the afterwards with different\noptions.' + } + ] + }, + { + type: 'heading', + depth: 2, + children: [{type: 'text', value: 'Compatibility'}] + }, + { + type: 'paragraph', + children: [ + { + type: 'text', + value: + 'Projects maintained by the unified collective are compatible with all maintained\nversions of Node.js.\nAs of now, that is Node.js 12.20+, 14.14+, and 16.0+.\nOur projects sometimes work with older versions, but this is not guaranteed.' + } + ] + }, + { + type: 'heading', + depth: 2, + children: [{type: 'text', value: 'Contribute'}] + }, + { + type: 'paragraph', + children: [ + {type: 'text', value: 'See '}, + { + type: 'linkReference', + referenceType: 'collapsed', + identifier: 'contributing', + children: [{type: 'inlineCode', value: 'contributing.md'}] + }, + {type: 'text', value: ' in '}, + { + type: 'linkReference', + referenceType: 'collapsed', + identifier: 'health', + children: [ + { + type: 'inlineCode', + value: health.split('/').slice(-2).join('/') + } + ] + }, + {type: 'text', value: ' for ways\nto get started.\nSee '}, + { + type: 'linkReference', + referenceType: 'collapsed', + identifier: 'support', + children: [{type: 'inlineCode', value: 'support.md'}] + }, + {type: 'text', value: ' for ways to get help.'} + ] + }, + { + type: 'paragraph', + children: [ + {type: 'text', value: 'This project has a '}, + { + type: 'linkReference', + referenceType: 'collapsed', + identifier: 'coc', + children: [{type: 'text', value: 'code of conduct'}] + }, + { + type: 'text', + value: + '.\nBy interacting with this repository, organization, or community you agree to\nabide by its terms.' + } + ] + }, + {type: 'heading', depth: 2, children: [{type: 'text', value: 'License'}]}, + { + type: 'paragraph', + children: [ + { + type: 'linkReference', + referenceType: 'collapsed', + identifier: 'license', + children: [{type: 'text', value: String(pack.license || '')}] + }, + {type: 'text', value: ' © '}, + { + type: 'linkReference', + referenceType: 'collapsed', + identifier: 'author', + children: [ + {type: 'text', value: String((author && author.name) || '')} + ] + } + ] + }, + { + type: 'definition', + identifier: 'build-badge', + url: 'https://github.com/' + slug + '/workflows/main/badge.svg' + }, + { + type: 'definition', + identifier: 'build', + url: 'https://github.com/' + slug + '/actions' + }, + { + type: 'definition', + identifier: 'coverage-badge', + url: 'https://img.shields.io/codecov/c/github/' + slug + '.svg' + }, + { + type: 'definition', + identifier: 'coverage', + url: 'https://codecov.io/github/' + slug + }, + { + type: 'definition', + identifier: 'downloads-badge', + url: 'https://img.shields.io/npm/dm/' + name + '.svg' + }, + { + type: 'definition', + identifier: 'downloads', + url: 'https://www.npmjs.com/package/' + name + }, + { + type: 'definition', + identifier: 'size-badge', + url: 'https://img.shields.io/bundlephobia/minzip/' + name + '.svg' + }, + { + type: 'definition', + identifier: 'size', + url: 'https://bundlephobia.com/result?p=' + name + }, + { + type: 'definition', + identifier: 'sponsors-badge', + url: 'https://opencollective.com/unified/sponsors/badge.svg' + }, + { + type: 'definition', + identifier: 'backers-badge', + url: 'https://opencollective.com/unified/backers/badge.svg' + }, + { + type: 'definition', + identifier: 'collective', + url: 'https://opencollective.com/unified' + }, + { + type: 'definition', + identifier: 'chat-badge', + url: 'https://img.shields.io/badge/chat-discussions-success.svg' + }, + { + type: 'definition', + identifier: 'chat', + url: 'https://github.com/remarkjs/remark/discussions' + }, + { + type: 'definition', + identifier: 'unified', + url: 'https://github.com/unifiedjs/unified' + }, + { + type: 'definition', + identifier: 'remark', + url: 'https://github.com/remarkjs/remark' + }, + { + type: 'definition', + identifier: 'mono', + url: 'https://github.com/' + slug + }, + { + type: 'definition', + identifier: 'esm', + url: 'https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c' + }, + { + type: 'definition', + identifier: 'esmsh', + url: 'https://esm.sh' + }, + { + type: 'definition', + identifier: 'npm', + url: 'https://docs.npmjs.com/cli/install' + }, + { + type: 'definition', + identifier: 'health', + url: health + }, + { + type: 'definition', + identifier: 'contributing', + url: hMain + '/contributing.md' + }, + { + type: 'definition', + identifier: 'support', + url: hMain + '/support.md' + }, + { + type: 'definition', + identifier: 'coc', + url: hMain + '/code-of-conduct.md' + }, + { + type: 'definition', + identifier: 'license', + url: main + '/license' + }, + { + type: 'definition', + identifier: 'author', + url: String((author && author.url) || '') + } + ] + + await fs.writeFile( + new URL('readme.md', packageUrl), + toMarkdown({type: 'root', children}, {extensions: [gfmToMarkdown()]}) + ) + + console.log('✓ wrote `readme.md` in `' + name + '`') +} diff --git a/script/build-rules.js b/script/build-rules.js deleted file mode 100644 index a18a0c0..0000000 --- a/script/build-rules.js +++ /dev/null @@ -1,967 +0,0 @@ -/** - * @typedef {import('mdast').BlockContent} BlockContent - * @typedef {import('mdast').DefinitionContent} DefinitionContent - * @typedef {import('mdast').TableContent} TableContent - * @typedef {import('type-fest').PackageJson} PackageJson - */ - -/** - * @typedef {BlockContent | DefinitionContent} BlockAndDefinitionContent - */ - -import fs from 'node:fs' -import path from 'node:path' -import process from 'node:process' -import {inspect} from 'node:util' -import {unified} from 'unified' -import {remark} from 'remark' -import remarkParse from 'remark-parse' -import remarkGfm from 'remark-gfm' -import {findAndReplace} from 'mdast-util-find-and-replace' -import {toString} from 'mdast-util-to-string' -import {slug as githubSlug} from 'github-slugger' -import parseAuthor from 'parse-author' -import {rules} from './util/rules.js' -import {rule} from './util/rule.js' -import {presets} from './util/presets.js' -import {repoUrl} from './util/repo-url.js' -import {characters} from './characters.js' - -const own = {}.hasOwnProperty - -const remote = repoUrl('package.json') - -const root = path.join(process.cwd(), 'packages') - -// eslint-disable-next-line complexity, unicorn/prefer-top-level-await -presets(root).then((presetObjects) => { - const allRules = rules(root) - let index = -1 - - while (++index < allRules.length) { - const basename = allRules[index] - const base = path.resolve(root, basename) - /** @type {PackageJson} */ - const pack = JSON.parse( - String(fs.readFileSync(path.join(base, 'package.json'))) - ) - const version = (pack.version || '0').split('.')[0] - const info = rule(base) - const tests = info.tests - const author = - typeof pack.author === 'string' ? parseAuthor(pack.author) : pack.author - const camelcased = basename.replace( - /-(\w)/g, - (_, /** @type {string} */ $1) => $1.toUpperCase() - ) - const org = remote.split('/').slice(0, -1).join('/') - const main = remote + '/blob/main' - const health = org + '/.github' - const hMain = health + '/blob/main' - const slug = remote.split('/').slice(-2).join('/') - let hasGfm = false - - const descriptionTree = unified().use(remarkParse).parse(info.description) - const summaryTree = unified() - .use(remarkParse) - .parse(info.summary || '') - - // Autolink `remark-lint` - unified() - .use( - /** @type {import('unified').Plugin, import('mdast').Root>} */ - () => (tree) => { - findAndReplace(tree, [ - /remark-lint/g, - () => { - return { - type: 'linkReference', - identifier: 'mono', - referenceType: 'full', - children: [{type: 'inlineCode', value: 'remark-lint'}] - } - } - ]) - } - ) - .runSync(summaryTree) - - const descriptionContent = /** @type {Array} */ ( - descriptionTree.children - ) - const summaryContent = /** @type {Array} */ ( - summaryTree.children - ) - - if (basename !== pack.name) { - throw new Error( - 'Expected package name (`' + - pack.name + - '`) to be the same as directory name (`' + - basename + - '`)' - ) - } - - /** @type {Record>} */ - const categories = {} - let category = 'Intro' - let contentIndex = -1 - - while (++contentIndex < descriptionContent.length) { - const node = descriptionContent[contentIndex] - if (node.type === 'heading' && node.depth === 2) { - category = githubSlug(toString(node)) - } - - if (!(category in categories)) { - categories[category] = [] - } - - categories[category].push(node) - } - - const includes = presetObjects.filter( - (preset) => basename in preset.packages - ) - - /** @type {Array} */ - const children = [ - {type: 'html', value: ''}, - { - type: 'heading', - depth: 1, - children: [{type: 'text', value: basename}] - } - ] - - if (info.deprecated) { - children.push(...descriptionContent) - } else { - children.push( - { - type: 'paragraph', - children: [ - { - type: 'linkReference', - identifier: 'build', - referenceType: 'full', - children: [ - { - type: 'imageReference', - identifier: 'build-badge', - referenceType: 'full', - alt: 'Build' - } - ] - }, - {type: 'text', value: '\n'}, - { - type: 'linkReference', - identifier: 'coverage', - referenceType: 'full', - children: [ - { - type: 'imageReference', - identifier: 'coverage-badge', - referenceType: 'full', - alt: 'Coverage' - } - ] - }, - {type: 'text', value: '\n'}, - { - type: 'linkReference', - identifier: 'downloads', - referenceType: 'full', - children: [ - { - type: 'imageReference', - identifier: 'downloads-badge', - referenceType: 'full', - alt: 'Downloads' - } - ] - }, - {type: 'text', value: '\n'}, - { - type: 'linkReference', - identifier: 'size', - referenceType: 'full', - children: [ - { - type: 'imageReference', - identifier: 'size-badge', - referenceType: 'full', - alt: 'Size' - } - ] - }, - {type: 'text', value: '\n'}, - { - type: 'linkReference', - identifier: 'collective', - referenceType: 'full', - children: [ - { - type: 'imageReference', - identifier: 'sponsors-badge', - referenceType: 'full', - alt: 'Sponsors' - } - ] - }, - {type: 'text', value: '\n'}, - { - type: 'linkReference', - identifier: 'collective', - referenceType: 'full', - children: [ - { - type: 'imageReference', - identifier: 'backers-badge', - referenceType: 'full', - alt: 'Backers' - } - ] - }, - {type: 'text', value: '\n'}, - { - type: 'linkReference', - identifier: 'chat', - referenceType: 'full', - children: [ - { - type: 'imageReference', - identifier: 'chat-badge', - referenceType: 'full', - alt: 'Chat' - } - ] - } - ] - }, - ...summaryContent, - { - type: 'heading', - depth: 2, - children: [{type: 'text', value: 'Contents'}] - }, - { - type: 'heading', - depth: 2, - children: [{type: 'text', value: 'What is this?'}] - }, - { - type: 'paragraph', - children: [ - {type: 'text', value: 'This package is a '}, - { - type: 'linkReference', - identifier: 'unified', - referenceType: 'collapsed', - children: [{type: 'text', value: 'unified'}] - }, - {type: 'text', value: ' ('}, - { - type: 'linkReference', - identifier: 'remark', - referenceType: 'collapsed', - children: [{type: 'text', value: 'remark'}] - }, - { - type: 'text', - value: ') plugin, specifically a ' - }, - { - type: 'inlineCode', - value: 'remark-lint' - }, - { - type: 'text', - value: '\nrule.\nLint rules check markdown code style.' - } - ] - }, - ...(categories['when-should-i-use-this'] || []), - { - type: 'heading', - depth: 2, - children: [{type: 'text', value: 'Presets'}] - } - ) - - if (includes.length === 0) { - children.push({ - type: 'paragraph', - children: [ - { - type: 'text', - value: 'This rule is not included in a preset maintained here.' - } - ] - }) - } else { - children.push( - { - type: 'paragraph', - children: [ - { - type: 'text', - value: 'This rule is included in the following presets:' - } - ] - }, - { - type: 'table', - align: [], - children: [ - { - type: 'tableRow', - children: [ - { - type: 'tableCell', - children: [{type: 'text', value: 'Preset'}] - }, - { - type: 'tableCell', - children: [{type: 'text', value: 'Setting'}] - } - ] - }, - ...includes.map((preset) => { - const option = preset.packages[basename] - - /** @type {TableContent} */ - const row = { - type: 'tableRow', - children: [ - { - type: 'tableCell', - children: [ - { - type: 'link', - url: remote + '/tree/main/packages/' + preset.name, - title: null, - children: [{type: 'inlineCode', value: preset.name}] - } - ] - }, - { - type: 'tableCell', - children: option - ? [{type: 'inlineCode', value: inspect(option)}] - : [] - } - ] - } - - return row - }) - ] - } - ) - } - - children.push( - { - type: 'heading', - depth: 2, - children: [{type: 'text', value: 'Install'}] - }, - { - type: 'paragraph', - children: [ - {type: 'text', value: 'This package is '}, - { - type: 'linkReference', - identifier: 'esm', - referenceType: 'full', - children: [{type: 'text', value: 'ESM only'}] - }, - { - type: 'text', - value: - '.\nIn Node.js (version 12.20+, 14.14+, or 16.0+), ' + - 'install with ' - }, - { - type: 'linkReference', - identifier: 'npm', - referenceType: 'collapsed', - children: [{type: 'text', value: 'npm'}] - }, - {type: 'text', value: ':'} - ] - }, - {type: 'code', lang: 'sh', value: 'npm install ' + basename}, - { - type: 'paragraph', - children: [ - {type: 'text', value: 'In Deno with '}, - { - type: 'linkReference', - identifier: 'esmsh', - label: 'esmsh', - referenceType: 'full', - children: [{type: 'inlineCode', value: 'esm.sh'}] - }, - {type: 'text', value: ':'} - ] - }, - { - type: 'code', - lang: 'js', - value: - 'import ' + - camelcased + - " from 'https://esm.sh/" + - basename + - '@' + - version + - "'" - }, - { - type: 'paragraph', - children: [ - {type: 'text', value: 'In browsers with '}, - { - type: 'linkReference', - identifier: 'esmsh', - label: 'esmsh', - referenceType: 'full', - children: [{type: 'inlineCode', value: 'esm.sh'}] - }, - {type: 'text', value: ':'} - ] - }, - { - type: 'code', - lang: 'html', - value: - '" - }, - { - type: 'heading', - depth: 2, - children: [{type: 'text', value: 'Use'}] - }, - { - type: 'paragraph', - children: [{type: 'text', value: 'On the API:'}] - }, - { - type: 'code', - lang: 'js', - value: [ - "import {read} from 'to-vfile'", - "import {reporter} from 'vfile-reporter'", - "import {remark} from 'remark'", - "import remarkLint from 'remark-lint'", - 'import ' + camelcased + " from '" + basename + "'", - '', - 'main()', - '', - 'async function main() {', - ' const file = await remark()', - ' .use(remarkLint)', - ' .use(' + camelcased + ')', - " .process(await read('example.md'))", - '', - ' console.error(reporter(file))', - '}' - ].join('\n') - }, - { - type: 'paragraph', - children: [{type: 'text', value: 'On the CLI:'}] - }, - { - type: 'code', - lang: 'sh', - value: 'remark --use remark-lint --use ' + basename + ' example.md' - }, - { - type: 'paragraph', - children: [ - { - type: 'text', - value: 'On the CLI in a config file (here a ' - }, - { - type: 'inlineCode', - value: 'package.json' - }, - { - type: 'text', - value: '):' - } - ] - }, - { - type: 'code', - lang: 'diff', - value: [ - ' …', - ' "remarkConfig": {', - ' "plugins": [', - ' …', - ' "remark-lint",', - '+ "' + basename + '",', - ' …', - ' ]', - ' }', - ' …' - ].join('\n') - } - ) - - if ('api' in categories) { - const [apiHeading, ...apiBody] = categories.api - - children.push( - apiHeading, - { - type: 'paragraph', - children: [ - { - type: 'text', - value: - 'This package exports no identifiers.\nThe default export is ' - }, - {type: 'inlineCode', value: camelcased}, - {type: 'text', value: '.'} - ] - }, - { - type: 'heading', - depth: 3, - children: [ - { - type: 'inlineCode', - value: 'unified().use(' + camelcased + '[, config])' - } - ] - }, - { - type: 'paragraph', - children: [ - { - type: 'text', - value: - 'This rule supports standard configuration that all remark lint rules accept\n(such as ' - }, - {type: 'inlineCode', value: 'false'}, - {type: 'text', value: ' to turn it off or '}, - {type: 'inlineCode', value: '[1, options]'}, - {type: 'text', value: ' to configure it).'} - ] - }, - ...apiBody - ) - } - - children.push( - ...(categories.recommendation || []), - ...(categories.fix || []), - ...(categories.example || []) - ) - - let first = true - /** @type {string} */ - let configuration - - for (configuration in tests) { - if (own.call(tests, configuration)) { - const fixtures = tests[configuration] - - if (first) { - children.push({ - type: 'heading', - depth: 2, - children: [{type: 'text', value: 'Examples'}] - }) - first = false - } - - /** @type {string} */ - let fileName - - for (fileName in fixtures) { - if (own.call(fixtures, fileName)) { - const fixture = fixtures[fileName] - /** @type {{config: unknown}} */ - const {config} = JSON.parse(configuration) - let clean = fixture.input - - children.push({ - type: 'heading', - depth: 5, - children: [{type: 'inlineCode', value: fileName}] - }) - - if (config !== true) { - children.push({ - type: 'paragraph', - children: [ - {type: 'text', value: 'When configured with '}, - {type: 'inlineCode', value: inspect(config)}, - {type: 'text', value: '.'} - ] - }) - } - - if ( - fixture.input !== null && - fixture.input !== undefined && - fixture.input.trim() !== '' - ) { - children.push({ - type: 'heading', - depth: 6, - children: [{type: 'text', value: 'In'}] - }) - - if (fixture.gfm) { - hasGfm = true - children.push({ - type: 'blockquote', - children: [ - { - type: 'paragraph', - children: [ - {type: 'text', value: '👉 '}, - { - type: 'strong', - children: [{type: 'text', value: 'Note'}] - }, - {type: 'text', value: ': this example uses GFM ('}, - { - type: 'linkReference', - identifier: 'gfm', - referenceType: 'full', - children: [ - {type: 'inlineCode', value: 'remark-gfm'} - ] - }, - {type: 'text', value: ').'} - ] - } - ] - }) - } - - let index = -1 - while (++index < characters.length) { - const char = characters[index] - const next = clean.replace(char.in, char.out) - - if (clean !== next) { - children.push({ - type: 'blockquote', - children: [ - { - type: 'paragraph', - children: [ - {type: 'text', value: '👉 '}, - { - type: 'strong', - children: [{type: 'text', value: 'Note'}] - }, - {type: 'text', value: ': '}, - {type: 'inlineCode', value: char.char}, - { - type: 'text', - value: ' represents ' + char.name + '.' - } - ] - } - ] - }) - - clean = next - } - } - - children.push({ - type: 'code', - lang: 'markdown', - value: fixture.input - }) - } - - children.push({ - type: 'heading', - depth: 6, - children: [{type: 'text', value: 'Out'}] - }) - - if (fixture.output.length === 0) { - children.push({ - type: 'paragraph', - children: [{type: 'text', value: 'No messages.'}] - }) - } else { - children.push({ - type: 'code', - lang: 'text', - value: fixture.output.join('\n') - }) - } - } - } - } - } - - children.push( - { - type: 'heading', - depth: 2, - children: [{type: 'text', value: 'Compatibility'}] - }, - { - type: 'paragraph', - children: [ - { - type: 'text', - value: - 'Projects maintained by the unified collective are compatible with all maintained\nversions of Node.js.\nAs of now, that is Node.js 12.20+, 14.14+, and 16.0+.\nOur projects sometimes work with older versions, but this is not guaranteed.' - } - ] - }, - { - type: 'heading', - depth: 2, - children: [{type: 'text', value: 'Contribute'}] - }, - { - type: 'paragraph', - children: [ - {type: 'text', value: 'See '}, - { - type: 'linkReference', - referenceType: 'collapsed', - identifier: 'contributing', - children: [{type: 'inlineCode', value: 'contributing.md'}] - }, - {type: 'text', value: ' in '}, - { - type: 'linkReference', - referenceType: 'collapsed', - identifier: 'health', - children: [ - { - type: 'inlineCode', - value: health.split('/').slice(-2).join('/') - } - ] - }, - {type: 'text', value: ' for ways\nto get started.\nSee '}, - { - type: 'linkReference', - referenceType: 'collapsed', - identifier: 'support', - children: [{type: 'inlineCode', value: 'support.md'}] - }, - {type: 'text', value: ' for ways to get help.'} - ] - }, - { - type: 'paragraph', - children: [ - {type: 'text', value: 'This project has a '}, - { - type: 'linkReference', - referenceType: 'collapsed', - identifier: 'coc', - children: [{type: 'text', value: 'code of conduct'}] - }, - { - type: 'text', - value: - '.\nBy interacting with this repository, organization, or community you agree to\nabide by its terms.' - } - ] - } - ) - } - - children.push( - {type: 'heading', depth: 2, children: [{type: 'text', value: 'License'}]}, - { - type: 'paragraph', - children: [ - { - type: 'linkReference', - referenceType: 'collapsed', - identifier: 'license', - children: [{type: 'text', value: String(pack.license || '')}] - }, - {type: 'text', value: ' © '}, - { - type: 'linkReference', - referenceType: 'collapsed', - identifier: 'author', - children: [ - {type: 'text', value: String((author && author.name) || '')} - ] - } - ] - } - ) - - if (!info.deprecated) { - children.push( - { - type: 'definition', - identifier: 'build-badge', - url: 'https://github.com/' + slug + '/workflows/main/badge.svg' - }, - { - type: 'definition', - identifier: 'build', - url: 'https://github.com/' + slug + '/actions' - }, - { - type: 'definition', - identifier: 'coverage-badge', - url: 'https://img.shields.io/codecov/c/github/' + slug + '.svg' - }, - { - type: 'definition', - identifier: 'coverage', - url: 'https://codecov.io/github/' + slug - }, - { - type: 'definition', - identifier: 'downloads-badge', - url: 'https://img.shields.io/npm/dm/' + basename + '.svg' - }, - { - type: 'definition', - identifier: 'downloads', - url: 'https://www.npmjs.com/package/' + basename - }, - { - type: 'definition', - identifier: 'size-badge', - url: 'https://img.shields.io/bundlephobia/minzip/' + basename + '.svg' - }, - { - type: 'definition', - identifier: 'size', - url: 'https://bundlephobia.com/result?p=' + basename - }, - { - type: 'definition', - identifier: 'sponsors-badge', - url: 'https://opencollective.com/unified/sponsors/badge.svg' - }, - { - type: 'definition', - identifier: 'backers-badge', - url: 'https://opencollective.com/unified/backers/badge.svg' - }, - { - type: 'definition', - identifier: 'collective', - url: 'https://opencollective.com/unified' - }, - { - type: 'definition', - identifier: 'chat-badge', - url: 'https://img.shields.io/badge/chat-discussions-success.svg' - }, - { - type: 'definition', - identifier: 'chat', - url: 'https://github.com/remarkjs/remark/discussions' - }, - { - type: 'definition', - identifier: 'unified', - url: 'https://github.com/unifiedjs/unified' - }, - { - type: 'definition', - identifier: 'remark', - url: 'https://github.com/remarkjs/remark' - }, - { - type: 'definition', - identifier: 'mono', - url: 'https://github.com/' + slug - }, - { - type: 'definition', - identifier: 'esm', - url: 'https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c' - }, - { - type: 'definition', - identifier: 'esmsh', - url: 'https://esm.sh' - }, - { - type: 'definition', - identifier: 'npm', - url: 'https://docs.npmjs.com/cli/install' - }, - { - type: 'definition', - identifier: 'health', - url: health - }, - { - type: 'definition', - identifier: 'contributing', - url: hMain + '/contributing.md' - }, - { - type: 'definition', - identifier: 'support', - url: hMain + '/support.md' - }, - { - type: 'definition', - identifier: 'coc', - url: hMain + '/code-of-conduct.md' - } - ) - } - - children.push( - { - type: 'definition', - identifier: 'license', - url: main + '/license' - }, - { - type: 'definition', - identifier: 'author', - url: String((author && author.url) || '') - } - ) - - if (hasGfm) { - children.push({ - type: 'definition', - identifier: 'gfm', - url: 'https://github.com/remarkjs/remark-gfm' - }) - } - - fs.writeFileSync( - path.join(base, 'readme.md'), - remark().use(remarkGfm).stringify({type: 'root', children}) - ) - - console.log('✓ wrote `readme.md` in `' + basename + '`') - } -}) diff --git a/script/info.js b/script/info.js new file mode 100644 index 0000000..ce459fb --- /dev/null +++ b/script/info.js @@ -0,0 +1,253 @@ +/** + * @typedef {import('unified').Preset} Preset + */ + +/** + * @typedef Check + * Check. + * @property {string} configuration + * Configuration. + * @property {boolean} gfm + * Whether to use GFM. + * @property {string} input + * Input. + * @property {string} name + * Name. + * @property {Array} output + * Output. + * @property {boolean} positionless + * Whether this check also applies without positions. + * + * @typedef ExampleInfo + * Example. + * @property {unknown} [config] + * Configuration. + * @property {boolean} [gfm] + * Whether to use GFM. + * @property {'input' | 'output'} [label] + * Label. + * @property {boolean} [positionless] + * Whether this check also applies without positions. + * @property {string} name + * Name. + * + * @typedef PluginInfo + * Plugin. + * @property {boolean} deprecated + * Whether the plugin is deprecated. + * @property {string} description + * Description. + * @property {string} name + * Name. + * @property {string} ruleId + * Rule ID. + * @property {string | undefined} summary + * Summary. + * @property {Array} checks + * Checks. + * + * @typedef PresetInfo + * Preset. + * @property {string} name + * Name. + * @property {Array<[string, unknown]>} plugins + * Plugins and their configuration. + */ + +import assert from 'node:assert/strict' +import fs from 'node:fs/promises' +import {parse} from 'comment-parser' +import strip from 'strip-indent' + +export const packagesUrl = new URL('../packages/', import.meta.url) + +/** + * @type {Array} + * Plugins. + */ +export const plugins = [] + +/** + * @type {Array} + * Presets. + */ +export const presets = [] + +const names = await fs.readdir(packagesUrl) + +for (const name of names) { + if (name.startsWith('remark-lint-')) { + await addPlugin(name) + } + + if (name.startsWith('remark-preset-lint-')) { + await addPreset(name) + } +} + +/** + * @param {string} name + * Plugin name. + * @returns {Promise} + * Nothing. + */ +async function addPlugin(name) { + const ruleId = name.slice('remark-lint-'.length) + const code = await fs.readFile( + new URL(name + '/index.js', packagesUrl), + 'utf8' + ) + const fileInfo = parse(code, {spacing: 'preserve'})[0] + const tags = fileInfo.tags + const deprecatedTag = tags.find(function (d) { + return d.tag === 'deprecated' + }) + const moduleTag = tags.find(function (d) { + return d.tag === 'module' + }) + const summaryTag = tags.find(function (d) { + return d.tag === 'summary' + }) + + assert(moduleTag, 'expected `@module` in JSDoc') + + assert.equal(moduleTag.name, ruleId, 'expected correct `@module`') + + let description = deprecatedTag + ? deprecatedTag.description + : fileInfo.description + + assert(description, 'expected description (or `@deprecated`)') + + description = strip(description) + + /** @type {PluginInfo} */ + const result = { + deprecated: Boolean(deprecatedTag), + description: description.trim(), + name, + ruleId, + summary: summaryTag ? strip(summaryTag.description).trim() : undefined, + checks: [] + } + + const examples = tags + .filter(function (d) { + return d.tag === 'example' + }) + .map(function (d) { + return d.description.replace(/^\r?\n|\r?\n$/g, '') + }) + + let index = -1 + + while (++index < examples.length) { + const lines = examples[index].split('\n') + /** @type {ExampleInfo} */ + let info + + try { + info = JSON.parse(lines[0]) + lines.splice(0, 1) + /* c8 ignore next 4 */ + } catch (error) { + const cause = /** @type {Error} */ (error) + throw new Error('Could not parse example in `' + ruleId + '`', {cause}) + } + + const exampleValue = strip(lines.join('\n').replace(/^\r?\n/g, '')) + const configuration = JSON.stringify({config: info.config || true}) + const name = info.name + + if (!info.label) { + result.checks.push({ + configuration, + name, + positionless: info.positionless || false, + gfm: info.gfm || false, + input: exampleValue, + output: [] + }) + + continue + } + + assert(info.label === 'input' || info.label === 'output') + + let found = result.checks.find(function (d) { + return d.configuration === configuration && d.name === name + }) + + if (!found) { + found = { + configuration, + name, + positionless: info.positionless || false, + gfm: info.gfm || false, + input: '', + output: [] + } + result.checks.push(found) + } + + if (info.label === 'input') { + found.input = exampleValue + } else { + found.output = exampleValue.split('\n') + } + } + + plugins.push(result) +} + +/** + * @param {string} name + * Preset name. + * @returns {Promise} + * Nothing. + */ +async function addPreset(name) { + /** @type {{default: Preset}} */ + const mod = await import(new URL(name + '/index.js', packagesUrl).href) + const plugins = mod.default.plugins + assert(plugins, 'expected plugins in preset') + /** @type {PresetInfo} */ + const presetInfo = {name, plugins: []} + + let index = -1 + + while (++index < plugins.length) { + const plugin = plugins[index] + /** @type {import('unified').Plugin<[unknown]>} */ + let fn + /** @type {unknown} */ + let option + + if (Array.isArray(plugin)) { + ;[fn, option] = /** @type {import('unified').PluginTuple<[unknown]>} */ ( + plugin + ) + } else { + assert(typeof plugin === 'function') + fn = plugin + } + + // @ts-expect-error: `displayName`s are fine. + const name = /** @type {string} */ (fn.displayName || fn.name) + + const pluginName = name + .replace( + /[:-](\w)/g, + function (/** @type {string} */ _, /** @type {string} */ $1) { + return $1.toUpperCase() + } + ) + .replace(/[A-Z]/g, function (/** @type {string} */ $0) { + return '-' + $0.toLowerCase() + }) + + presetInfo.plugins.push([pluginName, option]) + } + + presets.push(presetInfo) +} diff --git a/script/plugin/list-of-plugins.js b/script/plugin/list-of-plugins.js new file mode 100644 index 0000000..ac12a49 --- /dev/null +++ b/script/plugin/list-of-plugins.js @@ -0,0 +1,85 @@ +/** + * @typedef {import('mdast').List} List + * @typedef {import('mdast').ListItem} ListItem + * @typedef {import('mdast').Root} Root + * + * @typedef {import('type-fest').PackageJson} PackageJson + */ + +import assert from 'node:assert/strict' +import fs from 'node:fs/promises' +import {zone} from 'mdast-zone' +import {packagesUrl, plugins} from '../info.js' + +/** + * @type {Array} + * List items. + */ +const items = await Promise.all( + plugins.map(async function (info) { + const packageUrl = new URL(info.name + '/', packagesUrl) + /** @type {PackageJson} */ + const pack = JSON.parse( + String(await fs.readFile(new URL('package.json', packageUrl))) + ) + const description = String(pack.description || '').replace( + /^remark-lint rule to ?/i, + '' + ) + + if (/^deprecated/i.test(description)) return + + assert(pack.repository && typeof pack.repository === 'object') + assert(pack.repository.directory) + + return { + type: 'listItem', + spread: false, + children: [ + { + type: 'paragraph', + children: [ + { + type: 'link', + url: + pack.repository.url + '/tree/main/' + pack.repository.directory, + children: [{type: 'inlineCode', value: info.name}] + }, + {type: 'text', value: ' — ' + description} + ] + } + ] + } + }) +) + +/** @type {List} */ +const list = { + type: 'list', + ordered: false, + spread: false, + // @ts-expect-error: filter is correct. + children: items.filter(Boolean) +} + +/** + * List rules. + * + * @returns + * Transform. + */ +export default function remarkListOfRules() { + /** + * Transform. + * + * @param {Root} tree + * Tree. + * @returns {undefined} + * Nothing. + */ + return function (tree) { + zone(tree, 'rules', function (start, _, end) { + return [start, structuredClone(list), end] + }) + } +} diff --git a/script/plugin/list-of-presets.js b/script/plugin/list-of-presets.js index 4bdb94b..e552f25 100644 --- a/script/plugin/list-of-presets.js +++ b/script/plugin/list-of-presets.js @@ -2,65 +2,77 @@ * @typedef {import('mdast').List} List * @typedef {import('mdast').ListItem} ListItem * @typedef {import('mdast').Root} Root + * * @typedef {import('type-fest').PackageJson} PackageJson */ -import fs from 'node:fs' -import path from 'node:path' -import process from 'node:process' +import assert from 'node:assert/strict' +import fs from 'node:fs/promises' import {zone} from 'mdast-zone' -import {presets} from '../util/presets.js' -import {repoUrl} from '../util/repo-url.js' +import {packagesUrl, presets} from '../info.js' -const root = path.join(process.cwd(), 'packages') +/** + * @type {Array} + * List items. + */ +const items = await Promise.all( + presets.map(async function (info) { + const packageUrl = new URL(info.name + '/', packagesUrl) + /** @type {PackageJson} */ + const pack = JSON.parse( + await fs.readFile(new URL('package.json', packageUrl), 'utf8') + ) -/** @type {import('unified').Plugin, Root>} */ -export default function listOfPresets() { - const presetPromise = presets(root) + const description = String(pack.description || '').replace( + /^remark preset to configure remark-lint with ?/i, + '' + ) - return async (tree) => { - const presetObjects = await presetPromise + assert(pack.repository && typeof pack.repository === 'object') + assert(pack.repository.directory) - zone(tree, 'presets', (start, _, end) => { - /** @type {List} */ - const list = { - type: 'list', - ordered: false, - spread: false, - children: presetObjects.map(({name}) => { - /** @type {PackageJson} */ - const pack = JSON.parse( - String(fs.readFileSync(path.join(root, name, 'package.json'))) - ) - const description = String(pack.description || '').replace( - /^remark preset to configure remark-lint with ?/i, - '' - ) + return { + type: 'listItem', + spread: false, + children: [ + { + type: 'paragraph', + children: [ + { + type: 'link', + url: + pack.repository.url + '/tree/main/' + pack.repository.directory, + children: [{type: 'inlineCode', value: info.name}] + }, + {type: 'text', value: ' — ' + description} + ] + } + ] + } + }) +) - /** @type {ListItem} */ - const item = { - type: 'listItem', - spread: false, - children: [ - { - type: 'paragraph', - children: [ - { - type: 'link', - url: repoUrl(pack), - children: [{type: 'inlineCode', value: name}] - }, - {type: 'text', value: ' — ' + description} - ] - } - ] - } +/** @type {List} */ +const list = {type: 'list', ordered: false, spread: false, children: items} - return item - }) - } - - return [start, list, end] +/** + * List presets. + * + * @returns + * Transform. + */ +export default function remarkListOfPresets() { + /** + * Transform. + * + * @param {Root} tree + * Tree. + * @returns {undefined} + * Nothing. + */ + return function (tree) { + zone(tree, 'presets', function (start, _, end) { + return [start, structuredClone(list), end] }) } } diff --git a/script/plugin/list-of-rules.js b/script/plugin/list-of-rules.js deleted file mode 100644 index 9ccc8b4..0000000 --- a/script/plugin/list-of-rules.js +++ /dev/null @@ -1,67 +0,0 @@ -/** - * @typedef {import('type-fest').PackageJson} PackageJson - * @typedef {import('mdast').ListItem} ListItem - * @typedef {import('mdast').List} List - * @typedef {import('mdast').Root} Root - */ - -import fs from 'node:fs' -import path from 'node:path' -import process from 'node:process' -import {zone} from 'mdast-zone' -import {rules} from '../util/rules.js' -import {repoUrl} from '../util/repo-url.js' - -const root = path.join(process.cwd(), 'packages') - -/** @type {import('unified').Plugin, Root>} */ -export default function listOfRules() { - return (tree) => { - zone(tree, 'rules', (start, _, end) => { - /** @type {List} */ - const list = { - type: 'list', - ordered: false, - spread: false, - children: rules(root) - .map((basename) => { - /** @type {PackageJson} */ - const pack = JSON.parse( - String(fs.readFileSync(path.join(root, basename, 'package.json'))) - ) - const description = String(pack.description || '').replace( - /^remark-lint rule to ?/i, - '' - ) - const deprecated = /^deprecated/i.test(description) - - /** @type {ListItem} */ - const item = { - type: 'listItem', - spread: false, - children: deprecated - ? [] - : [ - { - type: 'paragraph', - children: [ - { - type: 'link', - url: repoUrl(pack), - children: [{type: 'inlineCode', value: basename}] - }, - {type: 'text', value: ' — ' + description} - ] - } - ] - } - - return item - }) - .filter((d) => d.children.length > 0) - } - - return [start, list, end] - }) - } -} diff --git a/script/util/presets.js b/script/util/presets.js deleted file mode 100644 index de999af..0000000 --- a/script/util/presets.js +++ /dev/null @@ -1,76 +0,0 @@ -/** - * @typedef {import('unified').Plugin} Plugin - * @typedef {import('unified').Preset} Preset - */ - -import {promises as fs} from 'node:fs' -import path from 'node:path' -import url from 'node:url' - -/** - * @param {string} base - * @returns {Promise}>>} - */ -export async function presets(base) { - const allFiles = await fs.readdir(base) - const files = allFiles.filter((basename) => - /remark-preset-lint/.test(basename) - ) - - return Promise.all( - files.map(async (name) => { - const href = url.pathToFileURL(path.join(base, name, 'index.js')).href - // type-coverage:ignore-next-line - const presetMod = await import(href) - /** @type {Preset} */ - // type-coverage:ignore-next-line - const preset = presetMod.default - const plugins = preset.plugins || [] - /** @type {Record} */ - const packages = {} - - let index = -1 - while (++index < plugins.length) { - const plugin = plugins[index] - /** @type {Plugin} */ - let fn - /** @type {unknown} */ - let option - - if (typeof plugin === 'function') { - fn = plugin - } else if (Array.isArray(plugin)) { - // Fine: - // type-coverage:ignore-next-line - fn = plugin[0] - // Fine: - // type-coverage:ignore-next-line - option = plugin[1] - } else { - throw new TypeError( - 'Expected plugin, plugin tuple, not `' + plugin + '`' - ) - } - - /** @type {string} */ - // @ts-expect-error: `displayName`s are fine. - const name = fn.displayName || fn.name - - packages[ - name - .replace( - /[:-](\w)/g, - (/** @type {string} */ _, /** @type {string} */ $1) => - $1.toUpperCase() - ) - .replace( - /[A-Z]/g, - (/** @type {string} */ $0) => '-' + $0.toLowerCase() - ) - ] = option - } - - return {name, packages} - }) - ) -} diff --git a/script/util/repo-url.js b/script/util/repo-url.js deleted file mode 100644 index d01385f..0000000 --- a/script/util/repo-url.js +++ /dev/null @@ -1,38 +0,0 @@ -import fs from 'node:fs' - -/** - * @typedef {import('type-fest').PackageJson} PackageJson - */ - -/** - * @param {string | PackageJson} pathOrJson - * @returns {string} - */ -export function repoUrl(pathOrJson) { - const pkg = - typeof pathOrJson === 'string' ? readPackageJson(pathOrJson) : pathOrJson - - if ( - pkg.repository === undefined || - typeof pkg.repository !== 'object' || - typeof pkg.repository.url !== 'string' - ) { - throw new TypeError( - `Expected \`string\` for \`repository.url\` in \`${pathOrJson}\`` - ) - } - - if (pkg.repository.directory) { - return pkg.repository.url + `/tree/main/${pkg.repository.directory}` - } - - return pkg.repository.url -} - -/** - * @param {string} filePath - * @returns {PackageJson} - */ -function readPackageJson(filePath) { - return JSON.parse(String(fs.readFileSync(filePath))) -} diff --git a/script/util/rule.js b/script/util/rule.js deleted file mode 100755 index 340340d..0000000 --- a/script/util/rule.js +++ /dev/null @@ -1,140 +0,0 @@ -/** - * @typedef Rule - * @property {string} ruleId - * @property {string} description - * @property {string | undefined} summary - * @property {boolean} deprecated - * @property {Record} tests - * @property {string} filePath - * - * @typedef {Record} Checks - * - * @typedef Check - * @property {string} input - * @property {Array} output - * @property {boolean} gfm - * @property {boolean} positionless - */ - -import fs from 'node:fs' -import path from 'node:path' -import {parse} from 'comment-parser' -import strip from 'strip-indent' - -/** - * Get information for a rule at `filePath`. - * - * @param {string} filePath - * @returns {Rule} - */ -export function rule(filePath) { - const ruleId = path.basename(filePath).slice('remark-lint-'.length) - /** @type {Record} */ - const tests = {} - const code = fs.readFileSync(path.join(filePath, 'index.js'), 'utf8') - // Note: To do: `comment-parser` types are wrong. - /** @type {import('comment-parser/primitives').Block} */ - const fileInfo = parse(code, {spacing: 'preserve'})[0] - const tags = fileInfo.tags - const moduleTag = tags.find((d) => d.tag === 'module') - const summaryTag = tags.find((d) => d.tag === 'summary') - const deprecatedTag = tags.find((d) => d.tag === 'deprecated') - - /* c8 ignore next 3 */ - if (!moduleTag) { - throw new Error('Expected `@module` in JSDoc') - } - - const name = moduleTag.name - let description = - (deprecatedTag && deprecatedTag.description) || fileInfo.description - - /* c8 ignore next 3 */ - if (name !== ruleId) { - throw new Error(ruleId + ' has an incorrect `@module`: ' + name) - } - - /* c8 ignore next 3 */ - if (!description) { - throw new Error(ruleId + ' is missing a description or `@deprecated`') - } - - description = strip(description) - - /** @type {Rule} */ - const result = { - ruleId, - description: description.trim(), - summary: summaryTag ? strip(summaryTag.description).trim() : undefined, - deprecated: Boolean(deprecatedTag), - tests, - filePath - } - - const examples = tags - .filter((d) => d.tag === 'example') - .map((d) => d.description.replace(/^\r?\n|\r?\n$/g, '')) - let index = -1 - - while (++index < examples.length) { - const lines = examples[index].split('\n') - /** @type {{name: string, label?: 'input' | 'output', config?: unknown, positionless?: boolean, gfm?: boolean}} */ - let info - - try { - info = JSON.parse(lines[0]) - lines.splice(0, 1) - /* c8 ignore next 6 */ - } catch (error) { - const exception = /** @type Error */ (error) - throw new Error( - 'Could not parse example in ' + ruleId + ':\n' + exception.stack - ) - } - - const exampleValue = strip(lines.join('\n').replace(/^\r?\n/g, '')) - const configuration = JSON.stringify({config: info.config || true}) - const name = info.name - const context = - configuration in tests - ? tests[configuration] - : (tests[configuration] = {}) - - if (!info.label) { - context[name] = { - positionless: info.positionless || false, - gfm: info.gfm || false, - input: exampleValue, - output: [] - } - - continue - } - - /* c8 ignore next 9 */ - if (info.label !== 'input' && info.label !== 'output') { - throw new Error( - 'Expected `input` or `ouput` for `label` in ' + - ruleId + - ', not `' + - info.label + - '`' - ) - } - - if (!context[name]) { - context[name] = { - positionless: info.positionless || false, - gfm: info.gfm || false, - input: '', - output: [] - } - } - - // @ts-expect-error: fine: array for output, string for rest. - context[name][info.label] = - info.label === 'output' ? exampleValue.split('\n') : exampleValue - } - - return result -} diff --git a/script/util/rules.js b/script/util/rules.js deleted file mode 100644 index ca7f5c8..0000000 --- a/script/util/rules.js +++ /dev/null @@ -1,13 +0,0 @@ -import fs from 'node:fs' - -/** - * @param {string} filePath - * @returns {Array} - */ -export function rules(filePath) { - return fs - .readdirSync(filePath) - .filter( - (basename) => /remark-lint/.test(basename) && basename !== 'remark-lint' - ) -} diff --git a/test.js b/test.js index e859488..743b7e9 100644 --- a/test.js +++ b/test.js @@ -1,32 +1,33 @@ /** - * @typedef {import('unified').Plugin} Plugin - * @typedef {import('vfile-message').VFileMessage} VFileMessage - * @typedef {import('./script/util/rule.js').Check} Check - * @typedef {import('./script/util/rule.js').Rule} Rule + * @typedef {import('unified').PluggableList} PluggableList + * @typedef {import('unified').Plugin<[unknown]>} Plugin + * + * @typedef {import('./script/info.js').Check} Check + * @typedef {import('./script/info.js').PluginInfo} PluginInfo */ import assert from 'node:assert/strict' -import path from 'node:path' -import process from 'node:process' import test from 'node:test' -import url from 'node:url' -import {toVFile} from 'to-vfile' -import {removePosition} from 'unist-util-remove-position' import {remark} from 'remark' import remarkGfm from 'remark-gfm' +import remarkLint from 'remark-lint' +import remarkLintFinalNewline from 'remark-lint-final-newline' +import remarkLintNoHeadingPunctuation from 'remark-lint-no-heading-punctuation' +import remarkLintNoMultipleToplevelHeadings from 'remark-lint-no-multiple-toplevel-headings' +import remarkLintNoUndefinedReferences from 'remark-lint-no-undefined-references' import {lintRule} from 'unified-lint-rule' -import {rules} from './script/util/rules.js' -import {rule} from './script/util/rule.js' +import {removePosition} from 'unist-util-remove-position' +import {VFile} from 'vfile' import {characters} from './script/characters.js' -import lint from './packages/remark-lint/index.js' -import noHeadingPunctuation from './packages/remark-lint-no-heading-punctuation/index.js' -import noMultipleToplevelHeadings from './packages/remark-lint-no-multiple-toplevel-headings/index.js' -import noUndefinedReferences from './packages/remark-lint-no-undefined-references/index.js' -import finalNewline from './packages/remark-lint-final-newline/index.js' +import {plugins} from './script/info.js' -const own = {}.hasOwnProperty +test('remark-lint', async function (t) { + await t.test('should expose the public api', async function () { + assert.deepEqual(Object.keys(await import('remark-lint')).sort(), [ + 'default' + ]) + }) -test('core', async () => { const doc = [ '# A heading', '', @@ -37,53 +38,48 @@ test('core', async () => { '# Another main heading.' ].join('\n') - let file = await remark() - .use(noHeadingPunctuation) - .use(noMultipleToplevelHeadings) - .use(lint) - .process(toVFile({path: 'virtual.md', value: doc})) + await t.test('should support `remark-lint` last', async function () { + const file = await remark() + .use(remarkLintNoHeadingPunctuation) + .use(remarkLintNoMultipleToplevelHeadings) + .use(remarkLint) + .process({path: 'virtual.md', value: doc}) - assert.deepEqual( - asStrings(file.messages), - [ + assert.deepEqual(file.messages.map(String), [ 'virtual.md:3:1-3:24: Don’t add a trailing `.` to headings', 'virtual.md:3:1-3:24: Don’t use multiple top level headings (1:1)' - ], - 'should support `remark-lint` last' - ) + ]) + }) - file = await remark() - .use(lint) - .use(noHeadingPunctuation) - .use(noMultipleToplevelHeadings) - .process(toVFile({path: 'virtual.md', value: doc})) + await t.test('should support `remark-lint` first', async function () { + const file = await remark() + .use(remarkLint) + .use(remarkLintNoHeadingPunctuation) + .use(remarkLintNoMultipleToplevelHeadings) + .process({path: 'virtual.md', value: doc}) - assert.deepEqual( - asStrings(file.messages), - [ + assert.deepEqual(file.messages.map(String), [ 'virtual.md:3:1-3:24: Don’t add a trailing `.` to headings', 'virtual.md:3:1-3:24: Don’t use multiple top level headings (1:1)' - ], - 'should support `remark-lint` first' - ) + ]) + }) - file = await remark().use(lint).process('.') + await t.test('should support no rules', async function () { + const file = await remark().use(remarkLint).process('.') - assert.deepEqual(asStrings(file.messages), [], 'should support no rules') + assert.deepEqual(file.messages, []) + }) - file = await remark().use(finalNewline).process('') + await t.test('should support successful rules', async function () { + const file = await remark().use(remarkLintFinalNewline).process('') - assert.deepEqual( - asStrings(file.messages), - [], - 'should support successful rules' - ) + assert.deepEqual(file.messages, []) + }) - file = await remark().use(finalNewline, [2]).process('.') + await t.test('should support a list with a severity', async function () { + const file = await remark().use(remarkLintFinalNewline, [2]).process('.') - assert.deepEqual( - file.messages.map((d) => JSON.parse(JSON.stringify(d))), - [ + assert.deepEqual(file.messages.map(jsonClone), [ { fatal: true, message: 'Missing newline character at end of file', @@ -93,315 +89,269 @@ test('core', async () => { source: 'remark-lint', url: 'https://github.com/remarkjs/remark-lint/tree/main/packages/remark-lint-final-newline#readme' } - ], - 'should support a list with a severity' + ]) + }) + + await t.test('should support a boolean (`true`)', async function () { + const file = await remark().use(remarkLintFinalNewline, true).process('.') + + assert.deepEqual(file.messages.map(String), [ + '1:1: Missing newline character at end of file' + ]) + }) + + await t.test('should support a boolean (`false`)', async function () { + const file = await remark().use(remarkLintFinalNewline, false).process('.') + + assert.deepEqual(file.messages, []) + }) + + await t.test( + 'should support a list with a boolean severity (true, for on)', + async function () { + const file = await remark() + .use(remarkLintFinalNewline, [true]) + .process('.') + + assert.deepEqual(file.messages.map(String), [ + '1:1: Missing newline character at end of file' + ]) + } ) - file = await remark().use(finalNewline, true).process('.') + await t.test( + 'should support a list with boolean severity (false, for off)', + async function () { + const file = await remark() + .use(remarkLintFinalNewline, [false]) + .process('.') - assert.deepEqual( - asStrings(file.messages), - ['1:1: Missing newline character at end of file'], - 'should support a boolean (`true`)' + assert.deepEqual(file.messages, []) + } ) - file = await remark().use(finalNewline, false).process('.') + await t.test( + 'should support a list with string severity (`error`)', + async function () { + const file = await remark() + .use(remarkLintFinalNewline, ['error']) + .process('.') - assert.deepEqual( - asStrings(file.messages), - [], - 'should support a boolean (`false`)' + assert.deepEqual(file.messages.map(jsonClone), [ + { + fatal: true, + message: 'Missing newline character at end of file', + name: '1:1', + reason: 'Missing newline character at end of file', + ruleId: 'final-newline', + source: 'remark-lint', + url: 'https://github.com/remarkjs/remark-lint/tree/main/packages/remark-lint-final-newline#readme' + } + ]) + } ) - file = await remark().use(finalNewline, [true]).process('.') + await t.test( + 'should support a list with string severity (`on`)', + async function () { + const file = await remark() + .use(remarkLintFinalNewline, ['on']) + .process('.') - assert.deepEqual( - asStrings(file.messages), - ['1:1: Missing newline character at end of file'], - 'should support a list with a boolean severity (true, for on)' + assert.deepEqual(file.messages.map(jsonClone), [ + { + fatal: false, + message: 'Missing newline character at end of file', + name: '1:1', + reason: 'Missing newline character at end of file', + ruleId: 'final-newline', + source: 'remark-lint', + url: 'https://github.com/remarkjs/remark-lint/tree/main/packages/remark-lint-final-newline#readme' + } + ]) + } ) - file = await remark().use(finalNewline, [false]).process('.') + await t.test( + 'should support a list with string severity (`warn`)', + async function () { + const file = await remark() + .use(remarkLintFinalNewline, ['warn']) + .process('.') - assert.deepEqual( - asStrings(file.messages), - [], - 'should support a list with boolean severity (false, for off)' + assert.deepEqual(file.messages.map(jsonClone), [ + { + fatal: false, + message: 'Missing newline character at end of file', + name: '1:1', + reason: 'Missing newline character at end of file', + ruleId: 'final-newline', + source: 'remark-lint', + url: 'https://github.com/remarkjs/remark-lint/tree/main/packages/remark-lint-final-newline#readme' + } + ]) + } ) - file = await remark().use(finalNewline, ['error']).process('.') + await t.test( + 'should support a list with string severity (`off`)', + async function () { + const file = await remark() + .use(remarkLintFinalNewline, ['off']) + .process('.') - assert.deepEqual( - file.messages.map((d) => JSON.parse(JSON.stringify(d))), - [ - { - fatal: true, - message: 'Missing newline character at end of file', - name: '1:1', - reason: 'Missing newline character at end of file', - ruleId: 'final-newline', - source: 'remark-lint', - url: 'https://github.com/remarkjs/remark-lint/tree/main/packages/remark-lint-final-newline#readme' - } - ], - 'should support a list with string severity (`error`)' + assert.deepEqual(file.messages, []) + } ) - file = await remark().use(finalNewline, ['on']).process('.') - - assert.deepEqual( - file.messages.map((d) => JSON.parse(JSON.stringify(d))), - [ - { - fatal: false, - message: 'Missing newline character at end of file', - name: '1:1', - reason: 'Missing newline character at end of file', - ruleId: 'final-newline', - source: 'remark-lint', - url: 'https://github.com/remarkjs/remark-lint/tree/main/packages/remark-lint-final-newline#readme' - } - ], - 'should support a list with string severity (`on`)' + await t.test( + 'should fail on incorrect severities (too high)', + async function () { + assert.throws(function () { + remark().use(remarkLintFinalNewline, [3]).freeze() + }, /^Error: Incorrect severity `3` for `final-newline`, expected 0, 1, or 2$/) + } ) - file = await remark().use(finalNewline, ['warn']).process('.') - - assert.deepEqual( - file.messages.map((d) => JSON.parse(JSON.stringify(d))), - [ - { - fatal: false, - message: 'Missing newline character at end of file', - name: '1:1', - reason: 'Missing newline character at end of file', - ruleId: 'final-newline', - source: 'remark-lint', - url: 'https://github.com/remarkjs/remark-lint/tree/main/packages/remark-lint-final-newline#readme' - } - ], - 'should support a list with string severity (`warn`)' + await t.test( + 'should fail on incorrect severities (too low)', + async function () { + assert.throws(function () { + remark().use(remarkLintFinalNewline, [-1]).freeze() + }, /^Error: Incorrect severity `-1` for `final-newline`, expected 0, 1, or 2$/) + } ) - file = await remark().use(finalNewline, ['off']).process('.') + await t.test( + 'should support regex as options (remark-lint-no-undefined-references)', + async function () { + const file = await remark() + .use(remarkLintNoUndefinedReferences, {allow: [/^b\./i]}) + .process({ + path: 'virtual.md', + value: ['[foo][b.c]', '', '[bar][b]'].join('\n') + }) - assert.deepEqual( - asStrings(file.messages), - [], - 'should support a list with string severity (`off`)' + assert.deepEqual(file.messages.map(String), [ + 'virtual.md:3:1-3:9: Found reference to undefined definition' + ]) + } ) - assert.throws( - () => { - remark().use(finalNewline, [3]).freeze() - }, - /^Error: Incorrect severity `3` for `final-newline`, expected 0, 1, or 2$/, - 'should fail on incorrect severities (too high)' - ) + await t.test( + 'should support meta as a string (unified-lint-rule)', + async function () { + const file = await remark() + .use( + lintRule('test:rule', function (_, file) { + file.message('Test message') + }), + ['warn'] + ) + .process('.') - assert.throws( - () => { - remark().use(finalNewline, [-1]).freeze() - }, - /^Error: Incorrect severity `-1` for `final-newline`, expected 0, 1, or 2$/, - 'should fail on incorrect severities (too low)' - ) - - file = await remark() - .use(noUndefinedReferences, {allow: [/^b\./i]}) - .process( - toVFile({ - path: 'virtual.md', - value: ['[foo][b.c]', '', '[bar][b]'].join('\n') - }) - ) - - assert.deepEqual( - asStrings(file.messages), - ['virtual.md:3:1-3:9: Found reference to undefined definition'], - 'no-undefined-references allow option should work with native regex' - ) - - file = await remark() - .use( - lintRule('test:rule', (tree, file) => { - file.message('Test message') - }), - ['warn'] - ) - .process('.') - - assert.deepEqual( - file.messages.map((d) => JSON.parse(JSON.stringify(d))), - [ - { - fatal: false, - message: 'Test message', - name: '1:1', - reason: 'Test message', - ruleId: 'rule', - source: 'test' - } - ], - 'should support string meta' + assert.deepEqual(file.messages.map(jsonClone), [ + { + fatal: false, + message: 'Test message', + name: '1:1', + reason: 'Test message', + ruleId: 'rule', + source: 'test' + } + ]) + } ) }) -test('rules', async (t) => { - const root = path.join(process.cwd(), 'packages') - const all = rules(root) - let index = -1 +test('plugins', async function (t) { + for (const plugin of plugins) { + await t.test(plugin.name, async function () { + await assertPlugin(plugin) + }) + } +}) - while (++index < all.length) { - const basename = all[index] - const base = path.resolve(root, basename) - const info = rule(base) - const href = url.pathToFileURL(base).href + '/index.js' +/** + * @param {PluginInfo} info + * Info. + * @returns {Promise} + * Nothing. + */ +async function assertPlugin(info) { + /** @type {{default: Plugin}} */ + const pluginMod = await import(info.name) + const plugin = pluginMod.default - /** @type {{default: Plugin}} */ - const pluginMod = await import(href) - const fn = pluginMod.default + for (const check of info.checks) { + await assertCheck(plugin, info, check) + } +} - if (Object.keys(info.tests).length === 0) { - assert.ok(true, info.ruleId + ': no tests') - } else { - await t.test(info.ruleId, async () => { - const tests = info.tests - /** @type {string} */ - let configuration +/** + * @param {Plugin} plugin + * Plugin. + * @param {PluginInfo} info + * info. + * @param {Check} check + * Check. + * @returns {Promise} + * Nothing. + */ +async function assertCheck(plugin, info, check) { + /** @type {{config: unknown}} */ + const {config} = JSON.parse(check.configuration) + /** @type {PluggableList} */ + const extras = check.gfm ? [remarkGfm] : [] + let value = check.input - for (configuration in tests) { - if (own.call(tests, configuration)) { - const checks = tests[configuration] - /** @type {{config: unknown}} */ - const {config} = JSON.parse(configuration) + for (const character of characters) { + value = value.replace(character.in, character.out) + } - /** @type {string} */ - let name + const file = await remark() + .use(plugin, config) + .use(extras) + .process(new VFile({path: check.name, value})) - for (name in checks) { - if (own.call(checks, name)) { - const basename = name - const check = checks[name] + for (const message of file.messages) { + assert.equal(message.ruleId, info.ruleId) + assert.equal( + message.url, + 'https://github.com/remarkjs/remark-lint/tree/main/packages/remark-lint-' + + info.ruleId + + '#readme' + ) + } - await assertFixture(fn, info, check, basename, config) - } - } - } + assert.deepEqual( + file.messages.map(String).map(function (value) { + return value.slice(value.indexOf(':') + 1) + }), + check.output + ) + + if (!check.positionless) { + const file = await remark() + .use(function () { + return function (tree) { + removePosition(tree) } }) - } - } -}) + .use(plugin, config) + .use(extras) + .process(new VFile({path: check.name, value})) -/** - * @param {Plugin} rule - * @param {Rule} info - * @param {Check} fixture - * @param {string} basename - * @param {unknown} config - */ -/* eslint-disable-next-line max-params */ -function assertFixture(rule, info, fixture, basename, config) { - const ruleId = info.ruleId - const file = toVFile(basename) - const expected = fixture.output - const positionless = fixture.positionless - // @ts-expect-error: to do: fix types. - let proc = remark().use(rule, config) - - if (fixture.gfm) proc.use(remarkGfm) - - file.value = preprocess(fixture.input || '') - - try { - proc.runSync(proc.parse(file), file) - } catch (error) { - const exception = /** @type VFileMessage */ (error) - if (exception && exception.source !== 'remark-lint') { - throw exception - } - } - - let index = -1 - while (++index < file.messages.length) { - const message = file.messages[index] - if (message.ruleId !== ruleId) { - throw new Error( - 'Expected `' + - ruleId + - '`, not `' + - message.ruleId + - '` as `ruleId` for ' + - message - ) - } - - const expectedUrl = - 'https://github.com/remarkjs/remark-lint/tree/main/packages/remark-lint-' + - ruleId + - '#readme' - if (message.url !== expectedUrl) { - throw new Error( - 'Expected `' + - expectedUrl + - '`, not `' + - message.url + - '` as `ruleId` for ' + - message - ) - } - } - - assert.deepEqual( - normalize(file.messages), - expected, - 'should equal with position' - ) - - if (!positionless) { - file.messages = [] - proc = remark() - .use(() => (tree) => removePosition(tree)) - // @ts-expect-error: to do: fix types. - .use(rule, config) - if (fixture.gfm) proc.use(remarkGfm) - proc.processSync(file) - - assert.deepEqual( - normalize(file.messages), - [], - 'should equal without position' - ) + assert.deepEqual(file.messages, []) } } /** - * @param {Array} messages - * @returns {Array} + * @param {unknown} d + * Value. + * @returns {unknown} + * Cloned value. */ -function normalize(messages) { - return asStrings(messages).map((value) => value.slice(value.indexOf(':') + 1)) -} - -/** - * @param {Array} messages - * @returns {Array} - */ -function asStrings(messages) { - return messages.map(String) -} - -/** - * @param {string} value - * @returns {string} - */ -function preprocess(value) { - let index = -1 - - while (++index < characters.length) { - value = value.replace(characters[index].in, characters[index].out) - } - - return value +function jsonClone(d) { + return JSON.parse(JSON.stringify(d)) } diff --git a/tsconfig.json b/tsconfig.json index 14c5ac8..9212b25 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -6,7 +6,7 @@ "declarationMap": true, "emitDeclarationOnly": true, "exactOptionalPropertyTypes": true, - "lib": ["es2020"], + "lib": ["es2022"], "module": "node16", "strict": true, "target": "es2020"