From 547188ec6f862a252fff878d1e27badfa0e22519 Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Wed, 13 Dec 2023 16:54:37 +0100 Subject: [PATCH] Refactor docs --- package.json | 22 +- .../remark-lint-blockquote-indentation/.npmrc | 2 + .../index.js | 46 +- .../package.json | 8 +- .../readme.md | 198 ++- .../.npmrc | 2 + .../index.js | 74 +- .../package.json | 8 +- .../readme.md | 246 +-- .../.npmrc | 2 + .../index.js | 34 +- .../package.json | 8 +- .../readme.md | 190 ++- packages/remark-lint-code-block-style/.npmrc | 2 + .../remark-lint-code-block-style/index.js | 84 +- .../remark-lint-code-block-style/package.json | 8 +- .../remark-lint-code-block-style/readme.md | 240 +-- packages/remark-lint-definition-case/.npmrc | 2 + packages/remark-lint-definition-case/index.js | 28 +- .../remark-lint-definition-case/package.json | 8 +- .../remark-lint-definition-case/readme.md | 181 +- .../remark-lint-definition-spacing/.npmrc | 2 + .../remark-lint-definition-spacing/index.js | 35 +- .../package.json | 8 +- .../remark-lint-definition-spacing/readme.md | 188 ++- packages/remark-lint-emphasis-marker/.npmrc | 2 + packages/remark-lint-emphasis-marker/index.js | 80 +- .../remark-lint-emphasis-marker/package.json | 8 +- .../remark-lint-emphasis-marker/readme.md | 236 +-- packages/remark-lint-fenced-code-flag/.npmrc | 2 + .../remark-lint-fenced-code-flag/index.js | 91 +- .../remark-lint-fenced-code-flag/package.json | 8 +- .../remark-lint-fenced-code-flag/readme.md | 212 +-- .../remark-lint-fenced-code-marker/.npmrc | 2 + .../remark-lint-fenced-code-marker/index.js | 69 +- .../package.json | 8 +- .../remark-lint-fenced-code-marker/readme.md | 225 +-- packages/remark-lint-file-extension/.npmrc | 2 + packages/remark-lint-file-extension/index.js | 34 +- .../remark-lint-file-extension/package.json | 8 +- packages/remark-lint-file-extension/readme.md | 184 ++- packages/remark-lint-final-definition/.npmrc | 2 + .../remark-lint-final-definition/index.js | 27 +- .../remark-lint-final-definition/package.json | 8 +- .../remark-lint-final-definition/readme.md | 180 +- packages/remark-lint-final-newline/.npmrc | 2 + packages/remark-lint-final-newline/index.js | 28 +- .../remark-lint-final-newline/package.json | 8 +- packages/remark-lint-final-newline/readme.md | 181 +- .../remark-lint-first-heading-level/.npmrc | 2 + .../remark-lint-first-heading-level/index.js | 59 +- .../package.json | 8 +- .../remark-lint-first-heading-level/readme.md | 212 ++- packages/remark-lint-hard-break-spaces/.npmrc | 2 + .../remark-lint-hard-break-spaces/index.js | 31 +- .../package.json | 8 +- .../remark-lint-hard-break-spaces/readme.md | 186 ++- packages/remark-lint-heading-increment/.npmrc | 2 + .../remark-lint-heading-increment/index.js | 49 +- .../package.json | 8 +- .../remark-lint-heading-increment/readme.md | 194 ++- packages/remark-lint-heading-style/.npmrc | 2 + packages/remark-lint-heading-style/index.js | 104 +- .../remark-lint-heading-style/package.json | 8 +- packages/remark-lint-heading-style/readme.md | 252 +-- packages/remark-lint-linebreak-style/.npmrc | 2 + packages/remark-lint-linebreak-style/index.js | 78 +- .../remark-lint-linebreak-style/package.json | 9 +- .../remark-lint-linebreak-style/readme.md | 237 +-- packages/remark-lint-link-title-style/.npmrc | 2 + .../remark-lint-link-title-style/index.js | 83 +- .../remark-lint-link-title-style/package.json | 8 +- .../remark-lint-link-title-style/readme.md | 231 +-- .../.npmrc | 2 + .../index.js | 42 +- .../package.json | 8 +- .../readme.md | 198 +-- .../.npmrc | 2 + .../index.js | 37 +- .../package.json | 8 +- .../readme.md | 201 +-- packages/remark-lint-list-item-indent/.npmrc | 2 + .../remark-lint-list-item-indent/index.js | 159 +- .../remark-lint-list-item-indent/package.json | 8 +- .../remark-lint-list-item-indent/readme.md | 314 ++-- packages/remark-lint-list-item-spacing/.npmrc | 2 + .../remark-lint-list-item-spacing/index.js | 74 +- .../package.json | 8 +- .../remark-lint-list-item-spacing/readme.md | 225 +-- .../remark-lint-maximum-heading-length/.npmrc | 2 + .../index.js | 36 +- .../package.json | 8 +- .../readme.md | 189 ++- .../remark-lint-maximum-line-length/.npmrc | 2 + .../remark-lint-maximum-line-length/index.js | 48 +- .../package.json | 8 +- .../remark-lint-maximum-line-length/readme.md | 213 +-- .../.npmrc | 2 + .../index.js | 10 +- .../package.json | 8 +- .../readme.md | 210 ++- .../.npmrc | 2 + .../index.js | 41 +- .../package.json | 8 +- .../readme.md | 199 +-- .../.npmrc | 2 + .../index.js | 42 +- .../package.json | 8 +- .../readme.md | 200 +-- .../.npmrc | 2 + .../index.js | 23 +- .../package.json | 8 +- .../readme.md | 174 +- .../.npmrc | 2 + .../index.js | 23 +- .../package.json | 8 +- .../readme.md | 176 +- .../.npmrc | 2 + .../index.js | 26 +- .../package.json | 8 +- .../readme.md | 177 +- .../remark-lint-no-duplicate-headings/.npmrc | 2 + .../index.js | 40 +- .../package.json | 8 +- .../readme.md | 193 ++- .../remark-lint-no-emphasis-as-heading/.npmrc | 2 + .../index.js | 31 +- .../package.json | 8 +- .../readme.md | 184 ++- packages/remark-lint-no-empty-url/.npmrc | 2 + packages/remark-lint-no-empty-url/index.js | 29 +- .../remark-lint-no-empty-url/package.json | 8 +- packages/remark-lint-no-empty-url/readme.md | 180 +- .../remark-lint-no-file-name-articles/.npmrc | 2 + .../index.js | 24 +- .../package.json | 8 +- .../readme.md | 177 +- .../.npmrc | 2 + .../index.js | 24 +- .../package.json | 8 +- .../readme.md | 177 +- .../.npmrc | 2 + .../index.js | 45 +- .../package.json | 8 +- .../readme.md | 185 ++- .../.npmrc | 2 + .../index.js | 25 +- .../package.json | 8 +- .../readme.md | 178 +- .../.npmrc | 2 + .../index.js | 24 +- .../package.json | 8 +- .../readme.md | 177 +- .../.npmrc | 2 + .../index.js | 44 +- .../package.json | 8 +- .../readme.md | 204 +-- packages/remark-lint-no-heading-indent/.npmrc | 2 + .../remark-lint-no-heading-indent/index.js | 39 +- .../package.json | 8 +- .../remark-lint-no-heading-indent/readme.md | 195 +-- .../.npmrc | 2 + .../index.js | 26 +- .../package.json | 8 +- .../readme.md | 177 +- .../remark-lint-no-heading-punctuation/.npmrc | 2 + .../index.js | 45 +- .../package.json | 8 +- .../readme.md | 185 ++- packages/remark-lint-no-html/.npmrc | 2 + packages/remark-lint-no-html/index.js | 23 +- packages/remark-lint-no-html/package.json | 8 +- packages/remark-lint-no-html/readme.md | 174 +- packages/remark-lint-no-inline-padding/.npmrc | 2 + .../remark-lint-no-inline-padding/index.js | 19 +- .../package.json | 8 +- .../remark-lint-no-inline-padding/readme.md | 176 +- packages/remark-lint-no-literal-urls/.npmrc | 2 + packages/remark-lint-no-literal-urls/index.js | 36 +- .../remark-lint-no-literal-urls/package.json | 8 +- .../remark-lint-no-literal-urls/readme.md | 194 ++- .../remark-lint-no-missing-blank-lines/.npmrc | 2 + .../index.js | 48 +- .../package.json | 8 +- .../readme.md | 201 ++- .../.npmrc | 2 + .../index.js | 44 +- .../package.json | 8 +- .../readme.md | 197 ++- .../.npmrc | 2 + .../index.js | 42 +- .../package.json | 8 +- .../readme.md | 195 +-- .../remark-lint-no-reference-like-url/.npmrc | 2 + .../index.js | 34 +- .../package.json | 8 +- .../readme.md | 185 ++- packages/remark-lint-no-shell-dollars/.npmrc | 2 + .../remark-lint-no-shell-dollars/index.js | 65 +- .../remark-lint-no-shell-dollars/package.json | 8 +- .../remark-lint-no-shell-dollars/readme.md | 185 ++- .../.npmrc | 2 + .../index.js | 30 +- .../package.json | 8 +- .../readme.md | 183 ++- .../.npmrc | 2 + .../index.js | 30 +- .../package.json | 8 +- .../readme.md | 183 ++- .../remark-lint-no-table-indentation/.npmrc | 2 + .../remark-lint-no-table-indentation/index.js | 51 +- .../package.json | 8 +- .../readme.md | 220 +-- packages/remark-lint-no-tabs/.npmrc | 2 + packages/remark-lint-no-tabs/index.js | 64 +- packages/remark-lint-no-tabs/package.json | 8 +- packages/remark-lint-no-tabs/readme.md | 219 +-- .../.npmrc | 2 + .../index.js | 73 +- .../package.json | 10 +- .../readme.md | 211 +-- .../.npmrc | 2 + .../index.js | 29 +- .../package.json | 8 +- .../readme.md | 180 +- .../.npmrc | 2 + .../index.js | 29 +- .../package.json | 8 +- .../readme.md | 180 +- .../remark-lint-no-unused-definitions/.npmrc | 2 + .../index.js | 34 +- .../package.json | 8 +- .../readme.md | 180 +- .../.npmrc | 2 + .../index.js | 69 +- .../package.json | 8 +- .../readme.md | 225 +-- .../.npmrc | 2 + .../index.js | 74 +- .../package.json | 9 +- .../readme.md | 217 +-- packages/remark-lint-rule-style/.npmrc | 2 + packages/remark-lint-rule-style/index.js | 71 +- packages/remark-lint-rule-style/package.json | 8 +- packages/remark-lint-rule-style/readme.md | 220 +-- .../remark-lint-strikethrough-marker/.npmrc | 2 + .../remark-lint-strikethrough-marker/index.js | 72 +- .../package.json | 9 +- .../readme.md | 232 +-- packages/remark-lint-strong-marker/.npmrc | 2 + packages/remark-lint-strong-marker/index.js | 80 +- .../remark-lint-strong-marker/package.json | 8 +- packages/remark-lint-strong-marker/readme.md | 236 +-- .../remark-lint-table-cell-padding/.npmrc | 2 + .../remark-lint-table-cell-padding/index.js | 88 +- .../package.json | 8 +- .../remark-lint-table-cell-padding/readme.md | 246 +-- .../remark-lint-table-pipe-alignment/.npmrc | 2 + .../remark-lint-table-pipe-alignment/index.js | 54 +- .../package.json | 8 +- .../readme.md | 217 +-- packages/remark-lint-table-pipes/.npmrc | 2 + packages/remark-lint-table-pipes/index.js | 41 +- packages/remark-lint-table-pipes/package.json | 8 +- packages/remark-lint-table-pipes/readme.md | 200 +-- .../.npmrc | 2 + .../index.js | 66 +- .../package.json | 8 +- .../readme.md | 222 +-- packages/remark-lint/.npmrc | 2 + packages/remark-lint/index.js | 33 + packages/remark-lint/package.json | 8 +- packages/remark-lint/readme.md | 182 ++- packages/remark-preset-lint-consistent/.npmrc | 2 + .../remark-preset-lint-consistent/index.js | 19 +- .../package.json | 8 +- .../remark-preset-lint-consistent/readme.md | 171 +- .../.npmrc | 2 + .../index.js | 91 +- .../package.json | 8 +- .../readme.md | 243 +-- .../remark-preset-lint-recommended/.npmrc | 2 + .../remark-preset-lint-recommended/index.js | 25 +- .../package.json | 8 +- .../remark-preset-lint-recommended/readme.md | 177 +- packages/unified-lint-rule/.npmrc | 2 + packages/unified-lint-rule/index.js | 96 +- packages/unified-lint-rule/lib/index.js | 8 +- packages/unified-lint-rule/package.json | 12 +- packages/unified-lint-rule/readme.md | 229 +-- script/build-packages.js | 36 + script/build-plugins.js | 924 ----------- script/build-presets.js | 690 -------- script/characters.js | 26 - script/info.js | 65 +- script/pipeline-package.js | 1454 +++++++++++++++++ script/plugin/list-of-plugins.js | 104 +- script/plugin/list-of-presets.js | 6 +- test.js | 8 +- 299 files changed, 12519 insertions(+), 9615 deletions(-) create mode 100644 packages/remark-lint-blockquote-indentation/.npmrc create mode 100644 packages/remark-lint-checkbox-character-style/.npmrc create mode 100644 packages/remark-lint-checkbox-content-indent/.npmrc create mode 100644 packages/remark-lint-code-block-style/.npmrc create mode 100644 packages/remark-lint-definition-case/.npmrc create mode 100644 packages/remark-lint-definition-spacing/.npmrc create mode 100644 packages/remark-lint-emphasis-marker/.npmrc create mode 100644 packages/remark-lint-fenced-code-flag/.npmrc create mode 100644 packages/remark-lint-fenced-code-marker/.npmrc create mode 100644 packages/remark-lint-file-extension/.npmrc create mode 100644 packages/remark-lint-final-definition/.npmrc create mode 100644 packages/remark-lint-final-newline/.npmrc create mode 100644 packages/remark-lint-first-heading-level/.npmrc create mode 100644 packages/remark-lint-hard-break-spaces/.npmrc create mode 100644 packages/remark-lint-heading-increment/.npmrc create mode 100644 packages/remark-lint-heading-style/.npmrc create mode 100644 packages/remark-lint-linebreak-style/.npmrc create mode 100644 packages/remark-lint-link-title-style/.npmrc create mode 100644 packages/remark-lint-list-item-bullet-indent/.npmrc create mode 100644 packages/remark-lint-list-item-content-indent/.npmrc create mode 100644 packages/remark-lint-list-item-indent/.npmrc create mode 100644 packages/remark-lint-list-item-spacing/.npmrc create mode 100644 packages/remark-lint-maximum-heading-length/.npmrc create mode 100644 packages/remark-lint-maximum-line-length/.npmrc create mode 100644 packages/remark-lint-no-auto-link-without-protocol/.npmrc create mode 100644 packages/remark-lint-no-blockquote-without-marker/.npmrc create mode 100644 packages/remark-lint-no-consecutive-blank-lines/.npmrc create mode 100644 packages/remark-lint-no-duplicate-defined-urls/.npmrc create mode 100644 packages/remark-lint-no-duplicate-definitions/.npmrc create mode 100644 packages/remark-lint-no-duplicate-headings-in-section/.npmrc create mode 100644 packages/remark-lint-no-duplicate-headings/.npmrc create mode 100644 packages/remark-lint-no-emphasis-as-heading/.npmrc create mode 100644 packages/remark-lint-no-empty-url/.npmrc create mode 100644 packages/remark-lint-no-file-name-articles/.npmrc create mode 100644 packages/remark-lint-no-file-name-consecutive-dashes/.npmrc create mode 100644 packages/remark-lint-no-file-name-irregular-characters/.npmrc create mode 100644 packages/remark-lint-no-file-name-mixed-case/.npmrc create mode 100644 packages/remark-lint-no-file-name-outer-dashes/.npmrc create mode 100644 packages/remark-lint-no-heading-content-indent/.npmrc create mode 100644 packages/remark-lint-no-heading-indent/.npmrc create mode 100644 packages/remark-lint-no-heading-like-paragraph/.npmrc create mode 100644 packages/remark-lint-no-heading-punctuation/.npmrc create mode 100644 packages/remark-lint-no-html/.npmrc create mode 100644 packages/remark-lint-no-inline-padding/.npmrc create mode 100644 packages/remark-lint-no-literal-urls/.npmrc create mode 100644 packages/remark-lint-no-missing-blank-lines/.npmrc create mode 100644 packages/remark-lint-no-multiple-toplevel-headings/.npmrc create mode 100644 packages/remark-lint-no-paragraph-content-indent/.npmrc create mode 100644 packages/remark-lint-no-reference-like-url/.npmrc create mode 100644 packages/remark-lint-no-shell-dollars/.npmrc create mode 100644 packages/remark-lint-no-shortcut-reference-image/.npmrc create mode 100644 packages/remark-lint-no-shortcut-reference-link/.npmrc create mode 100644 packages/remark-lint-no-table-indentation/.npmrc create mode 100644 packages/remark-lint-no-tabs/.npmrc create mode 100644 packages/remark-lint-no-undefined-references/.npmrc create mode 100644 packages/remark-lint-no-unneeded-full-reference-image/.npmrc create mode 100644 packages/remark-lint-no-unneeded-full-reference-link/.npmrc create mode 100644 packages/remark-lint-no-unused-definitions/.npmrc create mode 100644 packages/remark-lint-ordered-list-marker-style/.npmrc create mode 100644 packages/remark-lint-ordered-list-marker-value/.npmrc create mode 100644 packages/remark-lint-rule-style/.npmrc create mode 100644 packages/remark-lint-strikethrough-marker/.npmrc create mode 100644 packages/remark-lint-strong-marker/.npmrc create mode 100644 packages/remark-lint-table-cell-padding/.npmrc create mode 100644 packages/remark-lint-table-pipe-alignment/.npmrc create mode 100644 packages/remark-lint-table-pipes/.npmrc create mode 100644 packages/remark-lint-unordered-list-marker-style/.npmrc create mode 100644 packages/remark-lint/.npmrc create mode 100644 packages/remark-preset-lint-consistent/.npmrc create mode 100644 packages/remark-preset-lint-markdown-style-guide/.npmrc create mode 100644 packages/remark-preset-lint-recommended/.npmrc create mode 100644 packages/unified-lint-rule/.npmrc create mode 100644 script/build-packages.js delete mode 100644 script/build-plugins.js delete mode 100644 script/build-presets.js delete mode 100644 script/characters.js create mode 100644 script/pipeline-package.js diff --git a/package.json b/package.json index 4fd3aa5..c6adf6b 100644 --- a/package.json +++ b/package.json @@ -1,11 +1,12 @@ { "private": true, "license": "MIT", - "repository": { - "type": "git", - "url": "https://github.com/remarkjs/remark-lint" - }, + "repository": "https://github.com/remarkjs/remark-lint", "bugs": "https://github.com/remarkjs/remark-lint/issues", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + }, "author": "Titus Wormer (https://wooorm.com)", "contributors": [ "Titus Wormer (https://wooorm.com)", @@ -117,12 +118,17 @@ "@ungap/structured-clone": "^1.0.0", "c8": "^8.0.0", "comment-parser": "^1.0.0", + "control-pictures": "^2.0.0", + "estree-util-is-identifier-name": "^3.0.0", "github-slugger": "^2.0.0", + "mdast-util-find-and-replace": "^3.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-gfm": "^3.0.0", "mdast-util-to-markdown": "^2.0.0", + "mdast-util-to-string": "^4.0.0", "mdast-zone": "^6.0.0", "micromark-extension-gfm": "^3.0.0", + "micromark-util-normalize-identifier": "^2.0.0", "parse-author": "^2.0.0", "prettier": "^3.0.0", "remark": "^15.0.0", @@ -133,6 +139,7 @@ "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", @@ -140,10 +147,13 @@ "unist-builder": "^4.0.0", "unist-util-remove-position": "^5.0.0", "vfile": "^6.0.0", + "vfile-find-down": "^7.0.0", + "vfile-reporter": "^8.0.0", + "vfile-sort": "^4.0.0", "xo": "^0.56.0" }, "scripts": { - "generate": "node --conditions development script/build-plugins.js && node --conditions development script/build-presets.js", + "generate": "node --conditions development script/build-packages.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", @@ -152,8 +162,8 @@ }, "prettier": { "bracketSpacing": false, - "singleQuote": true, "semi": false, + "singleQuote": true, "tabWidth": 2, "trailingComma": "none", "useTabs": false diff --git a/packages/remark-lint-blockquote-indentation/.npmrc b/packages/remark-lint-blockquote-indentation/.npmrc new file mode 100644 index 0000000..3757b30 --- /dev/null +++ b/packages/remark-lint-blockquote-indentation/.npmrc @@ -0,0 +1,2 @@ +ignore-scripts=true +package-lock=false diff --git a/packages/remark-lint-blockquote-indentation/index.js b/packages/remark-lint-blockquote-indentation/index.js index 50d42dd..a6221df 100644 --- a/packages/remark-lint-blockquote-indentation/index.js +++ b/packages/remark-lint-blockquote-indentation/index.js @@ -1,18 +1,41 @@ /** + * remark-lint rule to warn when block quotes are indented too much or + * too little. + * + * ## What is this? + * + * This package checks the “indent” of block quotes: the `>` (greater than) + * marker *and* the spaces before content. + * * ## When should I use this? * - * You can use this package to check that the “indent” of block quotes is - * consistent. - * Indent here is the `>` (greater than) marker and the spaces before content. + * You can use this rule to check markdown code style. * * ## API * - * The following options (default: `'consistent'`) are accepted: + * ### `unified().use(remarkLintBlockquoteIndentation[, options])` * - * * `number` (example: `2`) - * — preferred indent of `>` and spaces before content - * * `'consistent'` - * — detect the first used style and warn when further block quotes differ + * Warn when block quotes are indented too much or too little. + * + * ###### Parameters + * + * * `options` ([`Options`][api-options], default: `'consistent'`) + * — either a preferred indent or whether to detect the first style + * and warn for further differences + * + * ###### Returns + * + * Transform ([`Transformer` from `unified`][github-unified-transformer]). + * + * ### `Options` + * + * Configuration (TypeScript type). + * + * ###### Type + * + * ```ts + * type Options = number | 'consistent' + * ``` * * ## Recommendation * @@ -34,10 +57,11 @@ * * Due to this, it’s recommended to configure this rule with `2`. * + * [api-options]: #options + * [api-remark-lint-blockquote-indentation]: #unifieduseremarklintblockquoteindentation-options + * [github-unified-transformer]: https://github.com/unifiedjs/unified#transformer + * * @module blockquote-indentation - * @summary - * remark-lint rule to warn when block quotes are indented too much or - * too little. * @author Titus Wormer * @copyright 2015 Titus Wormer * @license MIT diff --git a/packages/remark-lint-blockquote-indentation/package.json b/packages/remark-lint-blockquote-indentation/package.json index 3760f89..8921573 100644 --- a/packages/remark-lint-blockquote-indentation/package.json +++ b/packages/remark-lint-blockquote-indentation/package.json @@ -13,11 +13,7 @@ "remark-lint-rule", "rule" ], - "repository": { - "type": "git", - "url": "https://github.com/remarkjs/remark-lint", - "directory": "packages/remark-lint-blockquote-indentation" - }, + "repository": "https://github.com/remarkjs/remark-lint/tree/main/packages/remark-lint-blockquote-indentation", "bugs": "https://github.com/remarkjs/remark-lint/issues", "funding": { "type": "opencollective", @@ -25,7 +21,7 @@ }, "author": "Titus Wormer (https://wooorm.com)", "contributors": [ - "Titus Wormer (https://wooorm.com)" + "Titus Wormer " ], "sideEffects": false, "type": "module", diff --git a/packages/remark-lint-blockquote-indentation/readme.md b/packages/remark-lint-blockquote-indentation/readme.md index d4ce897..9f80fcb 100644 --- a/packages/remark-lint-blockquote-indentation/readme.md +++ b/packages/remark-lint-blockquote-indentation/readme.md @@ -2,15 +2,15 @@ # remark-lint-blockquote-indentation -[![Build][build-badge]][build] -[![Coverage][coverage-badge]][coverage] -[![Downloads][downloads-badge]][downloads] -[![Size][size-badge]][size] -[![Sponsors][sponsors-badge]][collective] -[![Backers][backers-badge]][collective] -[![Chat][chat-badge]][chat] +[![Build][badge-build-image]][badge-build-url] +[![Coverage][badge-coverage-image]][badge-coverage-url] +[![Downloads][badge-downloads-image]][badge-downloads-url] +[![Size][badge-size-image]][badge-size-url] +[![Sponsors][badge-funding-sponsors-image]][badge-funding-url] +[![Backers][badge-funding-backers-image]][badge-funding-url] +[![Chat][badge-chat-image]][badge-chat-url] -[`remark-lint`][mono] rule to warn when block quotes are indented too much or +[`remark-lint`][github-remark-lint] rule to warn when block quotes are indented too much or too little. ## Contents @@ -21,7 +21,8 @@ too little. * [Install](#install) * [Use](#use) * [API](#api) - * [`unified().use(remarkLintBlockquoteIndentation[, config])`](#unifieduseremarklintblockquoteindentation-config) + * [`unified().use(remarkLintBlockquoteIndentation[, options])`](#unifieduseremarklintblockquoteindentation-options) + * [`Options`](#options) * [Recommendation](#recommendation) * [Examples](#examples) * [Compatibility](#compatibility) @@ -30,41 +31,39 @@ too little. ## What is this? -This package is a [unified][] ([remark][]) plugin, specifically a `remark-lint` -rule. -Lint rules check markdown code style. +This package checks the “indent” of block quotes: the `>` (greater than) +marker *and* the spaces before content. ## When should I use this? -You can use this package to check that the “indent” of block quotes is -consistent. -Indent here is the `>` (greater than) marker and the spaces before content. +You can use this rule to check markdown code style. ## Presets -This rule is included in the following presets: +This plugin is included in the following presets: -| Preset | Setting | +| Preset | Options | | - | - | | [`remark-preset-lint-consistent`](https://github.com/remarkjs/remark-lint/tree/main/packages/remark-preset-lint-consistent) | `'consistent'` | | [`remark-preset-lint-markdown-style-guide`](https://github.com/remarkjs/remark-lint/tree/main/packages/remark-preset-lint-markdown-style-guide) | `2` | ## Install -This package is [ESM only][esm]. -In Node.js (version 12.20+, 14.14+, or 16.0+), install with [npm][]: +This package is [ESM only][github-gist-esm]. +In Node.js (version 16+), +install with [npm][npm-install]: ```sh npm install remark-lint-blockquote-indentation ``` -In Deno with [`esm.sh`][esmsh]: +In Deno with [`esm.sh`][esm-sh]: ```js import remarkLintBlockquoteIndentation from 'https://esm.sh/remark-lint-blockquote-indentation@3' ``` -In browsers with [`esm.sh`][esmsh]: +In browsers with [`esm.sh`][esm-sh]: ```html +``` + +## Use + +On the API: + +```js +import remarkLint from 'remark-lint' +import remarkLintNoAutoLinkWithoutProtocol from 'remark-lint-no-auto-link-without-protocol' +import remarkParse from 'remark-parse' +import remarkStringify from 'remark-stringify' +import {read} from 'to-vfile' +import {unified} from 'unified' +import {reporter} from 'vfile-reporter' + +const file = await read('example.md') + +await unified() + .use(remarkParse) + .use(remarkLint) + .use(remarkLintNoAutoLinkWithoutProtocol) + .use(remarkStringify) + .process(file) + +console.error(reporter(file)) +``` + +On the CLI: + +```sh +remark --frail --use remark-lint --use remark-lint-no-auto-link-without-protocol . +``` + +On the CLI in a config file (here a `package.json`): + +```diff + … + "remarkConfig": { + "plugins": [ + … + "remark-lint", ++ "remark-lint-no-auto-link-without-protocol", + … + ] + } + … +``` + +## API + +This package exports no identifiers. +It exports no additional [TypeScript][typescript] types. +The default export is +[`remarkLintNoAutoLinkWithoutProtocol`][api-remark-lint-no-auto-link-without-protocol]. + +to do: remove. + +## Examples + +##### `ok.md` + +###### In + +```markdown + + + +Most Markdown vendors don’t recognize the following as a link: + +``` + +###### Out + +No messages. + +##### `not-ok.md` + +###### In + +```markdown + +``` + +###### Out + +```text +1:1-1:14: All automatic links must start with a protocol +``` + +## Compatibility + +Projects maintained by the unified collective are compatible with maintained +versions of Node.js. + +When we cut a new major release, we drop support for unmaintained versions of +Node. +This means we try to keep the current release line, +`remark-lint-no-auto-link-without-protocol@3`, +compatible with Node.js 12. + +## Contribute + +See [`contributing.md`][github-dotfiles-contributing] in [`remarkjs/.github`][github-dotfiles-health] for ways +to get started. +See [`support.md`][github-dotfiles-support] for ways to get help. + +This project has a [code of conduct][github-dotfiles-coc]. +By interacting with this repository, organization, or community you agree to +abide by its terms. ## License -[MIT][license] © [Titus Wormer][author] +[MIT][file-license] © [Titus Wormer][author] -[license]: https://github.com/remarkjs/remark-lint/blob/main/license +[api-remark-lint-no-auto-link-without-protocol]: #api [author]: https://wooorm.com + +[badge-build-image]: https://github.com/remarkjs/remark-lint/workflows/main/badge.svg + +[badge-build-url]: https://github.com/remarkjs/remark-lint/actions + +[badge-chat-image]: https://img.shields.io/badge/chat-discussions-success.svg + +[badge-chat-url]: https://github.com/remarkjs/remark/discussions + +[badge-coverage-image]: https://img.shields.io/codecov/c/github/remarkjs/remark-lint.svg + +[badge-coverage-url]: https://codecov.io/github/remarkjs/remark-lint + +[badge-downloads-image]: https://img.shields.io/npm/dm/remark-lint-no-auto-link-without-protocol.svg + +[badge-downloads-url]: https://www.npmjs.com/package/remark-lint-no-auto-link-without-protocol + +[badge-funding-backers-image]: https://opencollective.com/unified/backers/badge.svg + +[badge-funding-sponsors-image]: https://opencollective.com/unified/sponsors/badge.svg + +[badge-funding-url]: https://opencollective.com/unified + +[badge-size-image]: https://img.shields.io/bundlejs/size/remark-lint-no-auto-link-without-protocol + +[badge-size-url]: https://bundlejs.com/?q=remark-lint-no-auto-link-without-protocol + +[esm-sh]: https://esm.sh + +[file-license]: https://github.com/remarkjs/remark-lint/blob/main/license + +[github-dotfiles-coc]: https://github.com/remarkjs/.github/blob/main/code-of-conduct.md + +[github-dotfiles-contributing]: https://github.com/remarkjs/.github/blob/main/contributing.md + +[github-dotfiles-health]: https://github.com/remarkjs/.github + +[github-dotfiles-support]: https://github.com/remarkjs/.github/blob/main/support.md + +[github-gist-esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c + +[npm-install]: https://docs.npmjs.com/cli/install + +[typescript]: https://www.typescriptlang.org diff --git a/packages/remark-lint-no-blockquote-without-marker/.npmrc b/packages/remark-lint-no-blockquote-without-marker/.npmrc new file mode 100644 index 0000000..3757b30 --- /dev/null +++ b/packages/remark-lint-no-blockquote-without-marker/.npmrc @@ -0,0 +1,2 @@ +ignore-scripts=true +package-lock=false diff --git a/packages/remark-lint-no-blockquote-without-marker/index.js b/packages/remark-lint-no-blockquote-without-marker/index.js index c7fd213..09c4904 100644 --- a/packages/remark-lint-no-blockquote-without-marker/index.js +++ b/packages/remark-lint-no-blockquote-without-marker/index.js @@ -1,25 +1,44 @@ /** + * remark-lint rule to warn for lazy lines in block quotes. + * + * ## What is this? + * + * This package checks the style of block quotes. + * * ## When should I use this? * - * You can use this package to check that lines in block quotes start with `>`. + * You can use this package to check that the style of block quotes is + * consistent. * * ## API * + * ### `unified().use(remarkLintNoBlockquoteWithoutMarker)` + * + * Warn for lazy lines in block quotes. + * + * ###### Parameters + * * There are no options. * + * ###### Returns + * + * Transform ([`Transformer` from `unified`][github-unified-transformer]). + * * ## Recommendation * - * Rules around “lazy” lines are not straightforward and visually confusing, + * Rules around lazy lines are not straightforward and visually confusing, * so it’s recommended to start each line with a `>`. * * ## Fix * - * [`remark-stringify`](https://github.com/remarkjs/remark/tree/main/packages/remark-stringify) - * adds `>` markers to every line in a block quote. + * [`remark-stringify`][github-remark-stringify] adds `>` markers to every line + * in a block quote. + * + * [api-remark-lint-no-blockquote-without-marker]: #unifieduseremarklintnoblockquotewithoutmarker + * [github-remark-stringify]: https://github.com/remarkjs/remark/tree/main/packages/remark-stringify + * [github-unified-transformer]: https://github.com/unifiedjs/unified#transformer * * @module no-blockquote-without-marker - * @summary - * remark-lint rule to warn when lines in block quotes start without `>`. * @author Titus Wormer * @copyright 2015 Titus Wormer * @license MIT @@ -33,9 +52,9 @@ * @example * {"name": "ok-tabs.md"} * - * >»Foo… - * >»…bar… - * >»…baz. + * >␉Foo… + * >␉…bar… + * >␉…baz. * * @example * {"name": "not-ok.md", "label": "input"} @@ -52,8 +71,8 @@ * @example * {"name": "not-ok-tabs.md", "label": "input"} * - * >»Foo… - * »…bar… + * >␉Foo… + * ␉…bar… * …baz. * * @example diff --git a/packages/remark-lint-no-blockquote-without-marker/package.json b/packages/remark-lint-no-blockquote-without-marker/package.json index 4958e2b..4ebe09c 100644 --- a/packages/remark-lint-no-blockquote-without-marker/package.json +++ b/packages/remark-lint-no-blockquote-without-marker/package.json @@ -13,11 +13,7 @@ "remark-lint-rule", "rule" ], - "repository": { - "type": "git", - "url": "https://github.com/remarkjs/remark-lint", - "directory": "packages/remark-lint-no-blockquote-without-marker" - }, + "repository": "https://github.com/remarkjs/remark-lint/tree/main/packages/remark-lint-no-blockquote-without-marker", "bugs": "https://github.com/remarkjs/remark-lint/issues", "funding": { "type": "opencollective", @@ -25,7 +21,7 @@ }, "author": "Titus Wormer (https://wooorm.com)", "contributors": [ - "Titus Wormer (https://wooorm.com)" + "Titus Wormer " ], "sideEffects": false, "type": "module", diff --git a/packages/remark-lint-no-blockquote-without-marker/readme.md b/packages/remark-lint-no-blockquote-without-marker/readme.md index de28815..ec9d1c0 100644 --- a/packages/remark-lint-no-blockquote-without-marker/readme.md +++ b/packages/remark-lint-no-blockquote-without-marker/readme.md @@ -2,15 +2,15 @@ # remark-lint-no-blockquote-without-marker -[![Build][build-badge]][build] -[![Coverage][coverage-badge]][coverage] -[![Downloads][downloads-badge]][downloads] -[![Size][size-badge]][size] -[![Sponsors][sponsors-badge]][collective] -[![Backers][backers-badge]][collective] -[![Chat][chat-badge]][chat] +[![Build][badge-build-image]][badge-build-url] +[![Coverage][badge-coverage-image]][badge-coverage-url] +[![Downloads][badge-downloads-image]][badge-downloads-url] +[![Size][badge-size-image]][badge-size-url] +[![Sponsors][badge-funding-sponsors-image]][badge-funding-url] +[![Backers][badge-funding-backers-image]][badge-funding-url] +[![Chat][badge-chat-image]][badge-chat-url] -[`remark-lint`][mono] rule to warn when lines in block quotes start without `>`. +[`remark-lint`][github-remark-lint] rule to warn for lazy lines in block quotes. ## Contents @@ -20,7 +20,7 @@ * [Install](#install) * [Use](#use) * [API](#api) - * [`unified().use(remarkLintNoBlockquoteWithoutMarker[, config])`](#unifieduseremarklintnoblockquotewithoutmarker-config) + * [`unified().use(remarkLintNoBlockquoteWithoutMarker)`](#unifieduseremarklintnoblockquotewithoutmarker) * [Recommendation](#recommendation) * [Fix](#fix) * [Examples](#examples) @@ -30,39 +30,39 @@ ## What is this? -This package is a [unified][] ([remark][]) plugin, specifically a `remark-lint` -rule. -Lint rules check markdown code style. +This package checks the style of block quotes. ## When should I use this? -You can use this package to check that lines in block quotes start with `>`. +You can use this package to check that the style of block quotes is +consistent. ## Presets -This rule is included in the following presets: +This plugin is included in the following presets: -| Preset | Setting | +| Preset | Options | | - | - | | [`remark-preset-lint-markdown-style-guide`](https://github.com/remarkjs/remark-lint/tree/main/packages/remark-preset-lint-markdown-style-guide) | | | [`remark-preset-lint-recommended`](https://github.com/remarkjs/remark-lint/tree/main/packages/remark-preset-lint-recommended) | | ## Install -This package is [ESM only][esm]. -In Node.js (version 12.20+, 14.14+, or 16.0+), install with [npm][]: +This package is [ESM only][github-gist-esm]. +In Node.js (version 16+), +install with [npm][npm-install]: ```sh npm install remark-lint-no-blockquote-without-marker ``` -In Deno with [`esm.sh`][esmsh]: +In Deno with [`esm.sh`][esm-sh]: ```js import remarkLintNoBlockquoteWithoutMarker from 'https://esm.sh/remark-lint-no-blockquote-without-marker@5' ``` -In browsers with [`esm.sh`][esmsh]: +In browsers with [`esm.sh`][esm-sh]: ```html ``` @@ -64,15 +77,19 @@ In browsers with [`esm.sh`][esmsh]: On the API: ```js -import {remark} from 'remark' import remarkLint from 'remark-lint' +import remarkParse from 'remark-parse' +import remarkStringify from 'remark-stringify' import {read} from 'to-vfile' +import {unified} from 'unified' import {reporter} from 'vfile-reporter' const file = await read('example.md') -await remark() +await unified() + .use(remarkParse) .use(remarkLint) + .use(remarkStringify) .process(file) console.error(reporter(file)) @@ -81,7 +98,7 @@ console.error(reporter(file)) On the CLI: ```sh -remark --use remark-lint example.md +remark --frail --use remark-lint . ``` On the CLI in a config file (here a `package.json`): @@ -101,86 +118,97 @@ On the CLI in a config file (here a `package.json`): ## API This package exports no identifiers. -The default export is `remarkLint`. +It exports no additional [TypeScript][typescript] types. +The default export is +[`remarkLint`][api-remark-lint]. ### `unified().use(remarkLint)` Add support for configuration comments. -There are no options. -See [Ignore warnings][ignore] in the monorepo readme for how to use it. +See [Ignore warnings][mono-ignore] in the monorepo readme for how to use it. + +###### Returns + +Transform ([`Transformer` from `unified`][github-unified-transformer]). ## Compatibility -Projects maintained by the unified collective are compatible with all maintained +Projects maintained by the unified collective are compatible with maintained versions of Node.js. -As of now, that is Node.js 12.20+, 14.14+, and 16.0+. -Our projects sometimes work with older versions, but this is not guaranteed. + +When we cut a new major release, we drop support for unmaintained versions of +Node. +This means we try to keep the current release line, +`remark-lint@9`, +compatible with Node.js 12. ## Contribute -See [`contributing.md`][contributing] in [`remarkjs/.github`][health] for ways +See [`contributing.md`][github-dotfiles-contributing] in [`remarkjs/.github`][github-dotfiles-health] for ways to get started. -See [`support.md`][support] for ways to get help. +See [`support.md`][github-dotfiles-support] for ways to get help. -This project has a [code of conduct][coc]. +This project has a [code of conduct][github-dotfiles-coc]. By interacting with this repository, organization, or community you agree to abide by its terms. ## License -[MIT][license] © [Titus Wormer][author] +[MIT][file-license] © [Titus Wormer][author] -[logo]: https://raw.githubusercontent.com/remarkjs/remark-lint/014fca7/logo.svg?sanitize=true - -[build-badge]: https://github.com/remarkjs/remark-lint/workflows/main/badge.svg - -[build]: https://github.com/remarkjs/remark-lint/actions - -[coverage-badge]: https://img.shields.io/codecov/c/github/remarkjs/remark-lint.svg - -[coverage]: https://codecov.io/github/remarkjs/remark-lint - -[downloads-badge]: https://img.shields.io/npm/dm/remark-lint.svg - -[downloads]: https://www.npmjs.com/package/remark-lint - -[size-badge]: https://img.shields.io/bundlephobia/minzip/remark-lint.svg - -[size]: https://bundlephobia.com/result?p=remark-lint - -[sponsors-badge]: https://opencollective.com/unified/sponsors/badge.svg - -[backers-badge]: https://opencollective.com/unified/backers/badge.svg - -[collective]: https://opencollective.com/unified - -[chat-badge]: https://img.shields.io/badge/chat-discussions-success.svg - -[chat]: https://github.com/remarkjs/remark/discussions - -[esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c - -[esmsh]: https://esm.sh - -[npm]: https://docs.npmjs.com/cli/install - -[health]: https://github.com/remarkjs/.github - -[contributing]: https://github.com/remarkjs/.github/blob/main/contributing.md - -[support]: https://github.com/remarkjs/.github/blob/main/support.md - -[coc]: https://github.com/remarkjs/.github/blob/main/code-of-conduct.md - -[license]: https://github.com/remarkjs/remark-lint/blob/main/license +[api-remark-lint]: #unifieduseremarklint [author]: https://wooorm.com -[unified]: https://github.com/unifiedjs/unified +[badge-build-image]: https://github.com/remarkjs/remark-lint/workflows/main/badge.svg -[remark]: https://github.com/remarkjs/remark +[badge-build-url]: https://github.com/remarkjs/remark-lint/actions -[mono]: https://github.com/remarkjs/remark-lint +[badge-chat-image]: https://img.shields.io/badge/chat-discussions-success.svg -[ignore]: https://github.com/remarkjs/remark-lint#ignore-warnings +[badge-chat-url]: https://github.com/remarkjs/remark/discussions + +[badge-coverage-image]: https://img.shields.io/codecov/c/github/remarkjs/remark-lint.svg + +[badge-coverage-url]: https://codecov.io/github/remarkjs/remark-lint + +[badge-downloads-image]: https://img.shields.io/npm/dm/remark-lint.svg + +[badge-downloads-url]: https://www.npmjs.com/package/remark-lint + +[badge-funding-backers-image]: https://opencollective.com/unified/backers/badge.svg + +[badge-funding-sponsors-image]: https://opencollective.com/unified/sponsors/badge.svg + +[badge-funding-url]: https://opencollective.com/unified + +[badge-size-image]: https://img.shields.io/bundlejs/size/remark-lint + +[badge-size-url]: https://bundlejs.com/?q=remark-lint + +[esm-sh]: https://esm.sh + +[file-license]: https://github.com/remarkjs/remark-lint/blob/main/license + +[github-dotfiles-coc]: https://github.com/remarkjs/.github/blob/main/code-of-conduct.md + +[github-dotfiles-contributing]: https://github.com/remarkjs/.github/blob/main/contributing.md + +[github-dotfiles-health]: https://github.com/remarkjs/.github + +[github-dotfiles-support]: https://github.com/remarkjs/.github/blob/main/support.md + +[github-gist-esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c + +[github-remark]: https://github.com/remarkjs/remark + +[github-remark-lint]: https://github.com/remarkjs/remark-lint + +[github-unified-transformer]: https://github.com/unifiedjs/unified#transformer + +[mono-ignore]: https://github.com/remarkjs/remark-lint#ignore-warnings + +[npm-install]: https://docs.npmjs.com/cli/install + +[typescript]: https://www.typescriptlang.org diff --git a/packages/remark-preset-lint-consistent/.npmrc b/packages/remark-preset-lint-consistent/.npmrc new file mode 100644 index 0000000..3757b30 --- /dev/null +++ b/packages/remark-preset-lint-consistent/.npmrc @@ -0,0 +1,2 @@ +ignore-scripts=true +package-lock=false diff --git a/packages/remark-preset-lint-consistent/index.js b/packages/remark-preset-lint-consistent/index.js index 5e93577..3bf0bdb 100644 --- a/packages/remark-preset-lint-consistent/index.js +++ b/packages/remark-preset-lint-consistent/index.js @@ -1,10 +1,25 @@ /** + * Preset of remark-lint rules to warn for inconsistencies. + * + * ## What is this? + * + * This package is a preset containing `remark-lint` rules. + * Lint rules check markdown code style. + * * ## When should I use this? * * You can use this package to check that markdown is consistent. * - * @summary - * Preset of remark-lint rules to warn for inconsistencies. + * ## API + * + * ### `unified().use(remarkPresetLintConsistent)` + * + * Check that markdown is consistent. + * + * You can reconfigure rules in the preset by using them afterwards with different + * options. + * + * [api-remark-preset-lint-consistent]: #unifieduseremarkpresetlintconsistent */ /** diff --git a/packages/remark-preset-lint-consistent/package.json b/packages/remark-preset-lint-consistent/package.json index 83388cd..4a9d855 100644 --- a/packages/remark-preset-lint-consistent/package.json +++ b/packages/remark-preset-lint-consistent/package.json @@ -11,11 +11,7 @@ "remark-lint", "remark-preset" ], - "repository": { - "type": "git", - "url": "https://github.com/remarkjs/remark-lint", - "directory": "packages/remark-preset-lint-consistent" - }, + "repository": "https://github.com/remarkjs/remark-lint/tree/main/packages/remark-preset-lint-consistent", "bugs": "https://github.com/remarkjs/remark-lint/issues", "funding": { "type": "opencollective", @@ -23,7 +19,7 @@ }, "author": "Titus Wormer (https://wooorm.com)", "contributors": [ - "Titus Wormer (https://wooorm.com)" + "Titus Wormer " ], "sideEffects": false, "type": "module", diff --git a/packages/remark-preset-lint-consistent/readme.md b/packages/remark-preset-lint-consistent/readme.md index 3112bb7..bb4deee 100644 --- a/packages/remark-preset-lint-consistent/readme.md +++ b/packages/remark-preset-lint-consistent/readme.md @@ -2,21 +2,21 @@ # remark-preset-lint-consistent -[![Build][build-badge]][build] -[![Coverage][coverage-badge]][coverage] -[![Downloads][downloads-badge]][downloads] -[![Size][size-badge]][size] -[![Sponsors][sponsors-badge]][collective] -[![Backers][backers-badge]][collective] -[![Chat][chat-badge]][chat] +[![Build][badge-build-image]][badge-build-url] +[![Coverage][badge-coverage-image]][badge-coverage-url] +[![Downloads][badge-downloads-image]][badge-downloads-url] +[![Size][badge-size-image]][badge-size-url] +[![Sponsors][badge-funding-sponsors-image]][badge-funding-url] +[![Backers][badge-funding-backers-image]][badge-funding-url] +[![Chat][badge-chat-image]][badge-chat-url] -Preset of [`remark-lint`][mono] rules to warn for inconsistencies. +Preset of [`remark-lint`][github-remark-lint] rules to warn for inconsistencies. ## Contents * [What is this?](#what-is-this) * [When should I use this?](#when-should-i-use-this) -* [Rules](#rules) +* [Plugins](#plugins) * [Install](#install) * [Use](#use) * [API](#api) @@ -27,20 +27,20 @@ Preset of [`remark-lint`][mono] rules to warn for inconsistencies. ## What is this? -This package is a [unified][] ([remark][]) preset, specifically consisting of -`remark-lint` rules. +This package is a preset containing `remark-lint` rules. Lint rules check markdown code style. ## When should I use this? You can use this package to check that markdown is consistent. -## Rules +## Plugins -This preset configures [`remark-lint`][mono] with the following rules: +This preset includes the following plugins: -| Rule | Setting | +| Plugin | Options | | - | - | +| [`remark-lint`](https://github.com/remarkjs/remark-lint/tree/main/packages/remark-lint) | | | [`remark-lint-blockquote-indentation`](https://github.com/remarkjs/remark-lint/tree/main/packages/remark-lint-blockquote-indentation) | `'consistent'` | | [`remark-lint-checkbox-character-style`](https://github.com/remarkjs/remark-lint/tree/main/packages/remark-lint-checkbox-character-style) | `'consistent'` | | [`remark-lint-code-block-style`](https://github.com/remarkjs/remark-lint/tree/main/packages/remark-lint-code-block-style) | `'consistent'` | @@ -56,20 +56,21 @@ This preset configures [`remark-lint`][mono] with the following rules: ## Install -This package is [ESM only][esm]. -In Node.js (version 12.20+, 14.14+, or 16.0+), install with [npm][]: +This package is [ESM only][github-gist-esm]. +In Node.js (version 16+), +install with [npm][npm-install]: ```sh npm install remark-preset-lint-consistent ``` -In Deno with [`esm.sh`][esmsh]: +In Deno with [`esm.sh`][esm-sh]: ```js import remarkPresetLintConsistent from 'https://esm.sh/remark-preset-lint-consistent@5' ``` -In browsers with [`esm.sh`][esmsh]: +In browsers with [`esm.sh`][esm-sh]: ```html " - }, - { - 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') - } - ) - - const apiCategory = categories.get('api') - - if (apiCategory) { - const [apiHeading, ...apiBody] = apiCategory - - 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.get('recommendation') || []), - ...(categories.get('fix') || []), - ...(categories.get('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 deleted file mode 100644 index 3e92e4f..0000000 --- a/script/build-presets.js +++ /dev/null @@ -1,690 +0,0 @@ -/** - * @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 {parse} from 'comment-parser' -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 parseAuthor from 'parse-author' -import stripIndent from 'strip-indent' -import {packagesUrl, presets} from './info.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 - -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 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() - ) - - // Autolink `remark-lint` - findAndReplace(summaryTree, [ - /remark-lint/g, - function () { - return { - type: 'linkReference', - identifier: 'mono', - referenceType: 'full', - children: [{type: 'inlineCode', value: 'remark-lint'}] - } - } - ]) - - 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('/') - - 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: 'link', - url: remote + '/tree/main/packages/' + rule, - children: [{type: 'inlineCode', value: rule}] - } - ] - }, - { - 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/characters.js b/script/characters.js deleted file mode 100644 index 7ea8367..0000000 --- a/script/characters.js +++ /dev/null @@ -1,26 +0,0 @@ -export const characters = [ - { - name: 'a space', - in: /·/g, - out: ' ', - char: '·' - }, - { - name: 'a tab', - in: /»/g, - out: '\t', - char: '»' - }, - { - name: 'a carriage return and a line feed', - in: /␍␊\n?/g, - out: '\r\n', - char: '␍␊' - }, - { - name: 'a line feed', - in: /␊\n?/g, - out: '\n', - char: '␊' - } -] diff --git a/script/info.js b/script/info.js index 2a897d1..6167eb4 100644 --- a/script/info.js +++ b/script/info.js @@ -1,5 +1,6 @@ /** * @typedef {import('unified').Preset} Preset + * @typedef {import('type-fest').PackageJson} PackageJson */ /** @@ -39,10 +40,8 @@ * Description. * @property {string} name * Name. - * @property {string} ruleId + * @property {string | undefined} ruleId * Rule ID. - * @property {string | undefined} summary - * Summary. * @property {Array} checks * Checks. * @@ -50,7 +49,7 @@ * Preset. * @property {string} name * Name. - * @property {Array<[string, unknown]>} plugins + * @property {Array<[name: string, options: unknown]>} plugins * Plugins and their configuration. */ @@ -58,8 +57,16 @@ import assert from 'node:assert/strict' import fs from 'node:fs/promises' import {parse} from 'comment-parser' import strip from 'strip-indent' +import {read} from 'to-vfile' -export const packagesUrl = new URL('../packages/', import.meta.url) +const packagesUrl = new URL('../packages/', import.meta.url) + +/** @type {PackageJson} */ +export const ancestorPackage = JSON.parse( + String(await read(new URL('../package.json', packagesUrl))) +) + +export {packagesUrl} /** * @type {Array} @@ -76,7 +83,7 @@ export const presets = [] const names = await fs.readdir(packagesUrl) for (const name of names) { - if (name.startsWith('remark-lint-')) { + if (name.startsWith('remark-lint')) { await addPlugin(name) } @@ -92,42 +99,48 @@ for (const name of names) { * 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' - }) + let description = '' + /** @type {string | undefined} */ + let ruleId + let deprecated = false - assert(moduleTag, 'expected `@module` in JSDoc') + if (name === 'remark-lint') { + // Empty. + } else { + ruleId = name.slice('remark-lint-'.length) - assert.equal(moduleTag.name, ruleId, 'expected correct `@module`') + const deprecatedTag = tags.find(function (d) { + return d.tag === 'deprecated' + }) + const moduleTag = tags.find(function (d) { + return d.tag === 'module' + }) - let description = deprecatedTag - ? deprecatedTag.description - : fileInfo.description + assert(moduleTag, 'expected `@module` in JSDoc') + assert.equal(moduleTag.name, ruleId, 'expected correct `@module`') - assert(description, 'expected description (or `@deprecated`)') + description = deprecatedTag + ? deprecatedTag.description + : fileInfo.description - description = strip(description) + assert(description, 'expected description (or `@deprecated`)') + + description = strip(description).trim() + deprecated = Boolean(deprecatedTag) + } /** @type {PluginInfo} */ const result = { - deprecated: Boolean(deprecatedTag), - description: description.trim(), + deprecated, + description, name, ruleId, - summary: summaryTag ? strip(summaryTag.description).trim() : undefined, checks: [] } diff --git a/script/pipeline-package.js b/script/pipeline-package.js new file mode 100644 index 0000000..27f32f2 --- /dev/null +++ b/script/pipeline-package.js @@ -0,0 +1,1454 @@ +/** + * @typedef {import('mdast').Paragraph} Paragraph + * @typedef {import('mdast').PhrasingContent} PhrasingContent + * @typedef {import('mdast').Root} Root + * @typedef {import('mdast').TableContent} TableContent + * @typedef {import('mdast').TopLevelContent} TopLevelContent + * @typedef {import('parse-author').Author} Author + * @typedef {import('type-fest').PackageJson} PackageJson + */ + +/** + * @typedef Specifiers + * Parsed specifiers of a module. + * @property {string | undefined} default + * Name of default export. + * @property {Array} named + * Names of named exports. + * + * @typedef State + * Info passed around. + * @property {Author} author + * Author. + * @property {string} id + * Name of package as a JS identifier. + * @property {string} license + * SPDX. + * @property {string} name + * Name. + * @property {string | undefined} origin + * Name of package as a source + rule id. + * @property {string} remote + * URL to monorepo on GH. + * @property {Array} types + * Names of types. + * @property {Map} urls + * Possible definitions for link references. + * @property {string} versionMajor + * Major version of package. + */ + +import assert from 'node:assert/strict' +import {exec as execCallback} from 'node:child_process' +import {relative, sep} from 'node:path' +import {fileURLToPath} from 'node:url' +import {inspect, promisify} from 'node:util' +import {parse} from 'comment-parser' +import {name as isIdentifierName} from 'estree-util-is-identifier-name' +import {slug} from 'github-slugger' +import {findAndReplace} from 'mdast-util-find-and-replace' +import {fromMarkdown} from 'mdast-util-from-markdown' +import {gfmFromMarkdown, gfmToMarkdown} from 'mdast-util-gfm' +import {toMarkdown} from 'mdast-util-to-markdown' +import {toString} from 'mdast-util-to-string' +import {gfm} from 'micromark-extension-gfm' +import {normalizeIdentifier} from 'micromark-util-normalize-identifier' +import parseAuthor from 'parse-author' +import stripIndent from 'strip-indent' +import {read} from 'to-vfile' +import {visit} from 'unist-util-visit' +import {VFile} from 'vfile' +import {findDownAll} from 'vfile-find-down' +import {ancestorPackage, packagesUrl, plugins, presets} from './info.js' + +const exec = promisify(execCallback) + +const collator = new Intl.Collator('en') + +/** + * @param {string} name + * Name. + * @returns {Promise>} + * Promise to `package.json`s and other metadata files. + */ +export async function pipelinePackage(name) { + const [[types, dtsFile], packageFile] = await Promise.all([ + readIndexDts(name), + generatePackageJson(name) + ]) + + /** @type {PackageJson} */ + const packageJson = JSON.parse(String(packageFile)) + + /** @type {Author | undefined} */ + let author + + if (typeof packageJson.author === 'string') { + author = parseAuthor(packageJson.author) + } else { + packageFile.message('Expected `author` in local `package.json` as a string') + } + + assert(author) + + const remote = ancestorPackage.repository + assert(remote, 'expected `remote` set in monorepo `package.json`') + assert( + typeof remote === 'string', + 'expected `remote` as string in monorepo `package.json`' + ) + + const pluginInfo = plugins.find((d) => d.name === name) + + /** @type {State} */ + const state = { + author, + id: name.replace(/-([a-z])/g, function (_, /** @type {string} */ $1) { + return $1.toUpperCase() + }), + license: packageJson.license || 'MIT', + name, + origin: + pluginInfo && pluginInfo.ruleId + ? 'remark-lint:' + pluginInfo.ruleId + : undefined, + remote, + types, + urls: new Map(), + versionMajor: (packageJson.version || '0').split('.')[0] + } + + return [ + dtsFile, + packageFile, + generateNmrc(state), + ...(await generateReadme(state)) + ] +} + +/** + * @param {string} name + * Name. + * @returns {Promise} + * Files. + */ +async function generatePackageJson(name) { + const packageUrl = new URL(name + '/', packagesUrl) + const folderPath = relative( + fileURLToPath(new URL('../', packagesUrl)), + fileURLToPath(packageUrl) + ) + + const [files, file, commitResult] = await Promise.all([ + findDownAll(['.js', '.map', '.ts'], fileURLToPath(packageUrl)), + read(new URL('package.json', packageUrl)), + exec('git log --all --format="%cN <%cE>" "' + folderPath + '"') + ]) + + const codePaths = files.map(function (file) { + return relative(fileURLToPath(packageUrl), file.path) + }) + + /** @type {PackageJson} */ + const previousPackage = JSON.parse(String(file)) + assert(ancestorPackage.author) + + const gitContributors = [...new Set(commitResult.stdout.split('\n'))] + .sort() + .filter(Boolean) + .filter(function (d) { + return !d.includes('} */ + const packageJson = { + name, + version: previousPackage.version, + description: previousPackage.description, + license: ancestorPackage.license, + keywords: (previousPackage.keywords || []).sort(), + repository: ancestorPackage.repository + '/tree/main/' + folderPath, + bugs: ancestorPackage.bugs, + funding: ancestorPackage.funding, + author: ancestorPackage.author, + contributors: + gitContributors.length > 0 ? gitContributors : [ancestorPackage.author], + sideEffects: false, + type: 'module', + exports: './index.js', + files: codePaths + .map((d) => { + const index = d.indexOf(sep) + return index === -1 ? d : d.slice(0, index + 1) + }) + .filter(function (d, index, all) { + return all.indexOf(d) === index + }) + .sort(), + dependencies: previousPackage.dependencies, + scripts: {}, + typeCoverage: { + atLeast: 100, + detail: true, + ignoreCatch: true, + strict: true + }, + xo: previousPackage.xo || { + prettier: true, + rules: {'capitalized-comments': 'off'} + } + } + + file.value = JSON.stringify(packageJson, undefined, 2) + '\n' + file.data.changed = true + + return file +} + +/** + * @param {string} name + * Name. + * @returns {Promise<[Array, VFile]>} + * Files. + */ +async function readIndexDts(name) { + const file = await read(new URL(name + '/index.d.ts', packagesUrl), 'utf8') + const value = String(file.value) + /** @type {Array} */ + const symbols = [] + // Note: this is super naïve of course. + const matches = value.matchAll(/export type (\w+) = (?!import\('[@a-z])/g) + + for (const match of matches) { + const symbol = match[1] + + if (isIdentifierName(symbol)) { + const head = symbol.charAt(0) + assert(head === head.toUpperCase()) + symbols.push(symbol) + } else { + const message = file.message('Expected a valid type `' + symbol + '`') + message.fatal = true + } + } + + return [symbols, file] +} + +/** + * @param {State} state + * Info passed around. + * @returns {VFile} + * File. + */ +function generateNmrc(state) { + const file = new VFile({ + path: new URL(state.name + '/.npmrc', packagesUrl), + value: ['ignore-scripts=true', 'package-lock=false', ''].join('\n') + }) + + file.data.changed = true + + return file +} + +/** + * @param {State} state + * Info passed around. + * @returns {Promise>} + * Files. + */ +// eslint-disable-next-line complexity +async function generateReadme(state) { + const packageUrl = new URL(state.name + '/', packagesUrl) + const moduleUrl = new URL('index.js', packageUrl) + /** @type {[VFile, Record]} */ + const [indexFile, indexModule] = await Promise.all([ + read(moduleUrl), + import(moduleUrl.href) + ]) + + const file = new VFile(new URL('readme.md', packageUrl)) + + // Check identifier name. + if (!isIdentifierName(state.id)) { + const message = indexFile.message('Expected a valid identifier as name') + message.fatal = true + } + + if ( + presets.some(function (d) { + return d.name === state.name + }) + ) { + if ( + !('default' in indexModule) || + !indexModule.default || + typeof indexModule.default !== 'object' + ) { + const message = indexFile.message('Expected `export default` in preset') + message.fatal = true + } + } else if ( + plugins.some(function (d) { + return d.name === state.name + }) + ) { + const name = state.name === 'remark-lint' ? state.id : state.origin + assert(name) + if ( + !('default' in indexModule) || + !indexModule.default || + typeof indexModule.default !== 'function' + ) { + const message = indexFile.message('Expected `export default` in plugin') + message.fatal = true + } else if (indexModule.default.name !== name) { + const message = indexFile.message( + 'Unexpected export default `' + + indexModule.default.name + + '`, expected `' + + name + + '`' + ) + message.fatal = true + } + } + + /** @type {Specifiers} */ + const specifiers = { + default: + 'default' in indexModule && indexModule.default ? state.id : undefined, + named: Object.keys(indexModule).filter(function (d) { + return d !== 'default' + }) + } + + const block = parse(String(indexFile), {spacing: 'preserve'}) + const fileInfo = block[0] || {} + + const description = stripIndent(fileInfo.description || '').trim() + + const explicitDocs = fromMarkdown(description, { + extensions: [gfm()], + mdastExtensions: [gfmFromMarkdown()] + }) + + /** @type {Record>} */ + const categories = {} + let category = 'intro' + let contentIndex = -1 + + while (++contentIndex < explicitDocs.children.length) { + const node = /** @type {TopLevelContent} */ ( + explicitDocs.children[contentIndex] + ) + + if (node.type === 'heading' && node.depth === 2) { + category = slug(toString(node)) + } + + if (!(category in categories)) { + categories[category] = [] + } + + categories[category].push(node) + } + + if (!categories.intro || categories.intro.length === 0) { + indexFile.message('Missing `intro` section in description') + } + + if (!categories.use) { + if ( + plugins.some(function (d) { + return d.name === state.name + }) || + presets.some(function (d) { + return d.name === state.name + }) + ) { + categories.use = generateReadmePluggableUseSection(state) + } else { + indexFile.message('Missing `use` section in description') + } + } + + if (!categories.api) { + indexFile.message('Missing `api` section in description') + categories.api = [] + } + + const [apiHeading, ...apiRest] = categories.api || [] + + if (categories.intro) { + state.urls.set('github-remark', 'https://github.com/remarkjs/remark') + state.urls.set( + 'github-remark-lint', + 'https://github.com/remarkjs/remark-lint' + ) + state.urls.set('github-unified', 'https://github.com/unifiedjs/unified') + /** @type {Root} */ + const fragment = {type: 'root', children: categories.intro} + findAndReplace( + fragment, + ['remark-lint', 'remark', 'unified'].map(function (d) { + return [ + new RegExp('\\b' + d + '\\b(?!-)', 'g'), + function () { + const code = d === 'remark-lint' + /** @type {PhrasingContent} */ + let result = code + ? {type: 'inlineCode', value: d} + : {type: 'text', value: d} + + result = { + type: 'linkReference', + identifier: 'github-' + d, + referenceType: code ? 'full' : 'collapsed', + children: [result] + } + + if (d === 'remark' || d === 'unified') { + result = {type: 'strong', children: [result]} + } + + return result + } + ] + }) + ) + } + + /** @type {Array} */ + const topLevelContent = [ + ...generateReadmeHead(state), + ...generateReadmeMeta(state), + ...(categories.intro || []), + {type: 'heading', depth: 2, children: [{type: 'text', value: 'Contents'}]}, + ...(categories['what-is-this'] || []), + ...(categories['when-should-i-use-this'] || []), + ...generateReadmeIncludes(state), + ...generateReadmeInstall(state, specifiers), + ...(categories.use || []), + ...(apiHeading + ? [apiHeading, ...generateReadmeApiByline(state, specifiers), ...apiRest] + : []), + ...(categories.recommendation || []), + ...(categories.fix || []), + ...(categories.example || generateReadmeExample(state)), + ...generateReadmeTail(state) + ] + + /** @type {Root} */ + const tree = {type: 'root', children: topLevelContent} + /** @type {Set} */ + const used = new Set() + + visit(tree, function (node, index, parent) { + if (node.type === 'definition' && parent && typeof index === 'number') { + const id = normalizeIdentifier(node.identifier).toLowerCase() + state.urls.set(id, node.url) + parent.children.splice(index, 1) + return index + } + }) + + visit(tree, function (node) { + if ('identifier' in node && 'referenceType' in node) { + const id = normalizeIdentifier(node.identifier).toLowerCase() + + if (state.urls.has(id)) { + used.add(id) + } else { + file.message( + 'Missing link reference in `state.urls` for `' + id + '`', + node + ) + } + } + }) + + for (const identifier of [...used].sort()) { + const url = state.urls.get(identifier) + assert(url) + tree.children.push({type: 'definition', identifier, url}) + } + + file.value = toMarkdown(tree, {extensions: [gfmToMarkdown()]}) + file.data.changed = true + + return [file, indexFile] +} + +/** + * @param {State} state + * Info passed around. + * @returns {Array} + * Content. + */ +function generateReadmeHead(state) { + return [ + {type: 'html', value: ''}, + { + type: 'heading', + depth: 1, + children: [{type: 'text', value: state.name}] + } + ] +} + +/** + * @param {State} state + * Info passed around. + * @returns {Array} + * Content. + */ +function generateReadmeMeta(state) { + const ghSlug = state.remote.split('/').slice(-2).join('/') + const ghUrl = 'https://github.com/' + ghSlug + + state.urls.set('badge-build-url', ghUrl + '/actions') + state.urls.set('badge-build-image', ghUrl + '/workflows/main/badge.svg') + state.urls.set('badge-coverage-url', 'https://codecov.io/github/' + ghSlug) + state.urls.set( + 'badge-coverage-image', + 'https://img.shields.io/codecov/c/github/' + ghSlug + '.svg' + ) + state.urls.set( + 'badge-downloads-url', + 'https://www.npmjs.com/package/' + state.name + ) + state.urls.set( + 'badge-downloads-image', + 'https://img.shields.io/npm/dm/' + state.name + '.svg' + ) + state.urls.set('badge-size-url', 'https://bundlejs.com/?q=' + state.name) + state.urls.set( + 'badge-size-image', + 'https://img.shields.io/bundlejs/size/' + state.name + ) + state.urls.set('badge-funding-url', 'https://opencollective.com/unified') + state.urls.set( + 'badge-funding-sponsors-image', + 'https://opencollective.com/unified/sponsors/badge.svg' + ) + state.urls.set( + 'badge-funding-backers-image', + 'https://opencollective.com/unified/backers/badge.svg' + ) + state.urls.set( + 'badge-chat-url', + 'https://github.com/remarkjs/remark/discussions' + ) + state.urls.set( + 'badge-chat-image', + 'https://img.shields.io/badge/chat-discussions-success.svg' + ) + + return [ + { + type: 'paragraph', + children: [ + { + type: 'linkReference', + identifier: 'badge-build-url', + referenceType: 'full', + children: [ + { + type: 'imageReference', + identifier: 'badge-build-image', + referenceType: 'full', + alt: 'Build' + } + ] + }, + {type: 'text', value: '\n'}, + { + type: 'linkReference', + identifier: 'badge-coverage-url', + referenceType: 'full', + children: [ + { + type: 'imageReference', + identifier: 'badge-coverage-image', + referenceType: 'full', + alt: 'Coverage' + } + ] + }, + {type: 'text', value: '\n'}, + { + type: 'linkReference', + identifier: 'badge-downloads-url', + referenceType: 'full', + children: [ + { + type: 'imageReference', + identifier: 'badge-downloads-image', + referenceType: 'full', + alt: 'Downloads' + } + ] + }, + {type: 'text', value: '\n'}, + { + type: 'linkReference', + identifier: 'badge-size-url', + referenceType: 'full', + children: [ + { + type: 'imageReference', + identifier: 'badge-size-image', + referenceType: 'full', + alt: 'Size' + } + ] + }, + {type: 'text', value: '\n'}, + { + type: 'linkReference', + identifier: 'badge-funding-url', + referenceType: 'full', + children: [ + { + type: 'imageReference', + identifier: 'badge-funding-sponsors-image', + referenceType: 'full', + alt: 'Sponsors' + } + ] + }, + {type: 'text', value: '\n'}, + { + type: 'linkReference', + identifier: 'badge-funding-url', + referenceType: 'full', + children: [ + { + type: 'imageReference', + identifier: 'badge-funding-backers-image', + referenceType: 'full', + alt: 'Backers' + } + ] + }, + {type: 'text', value: '\n'}, + { + type: 'linkReference', + identifier: 'badge-chat-url', + referenceType: 'full', + children: [ + { + type: 'imageReference', + identifier: 'badge-chat-image', + referenceType: 'full', + alt: 'Chat' + } + ] + } + ] + } + ] +} + +/** + * @param {State} state + * Info passed around. + * @returns {Array} + * Content. + */ +function generateReadmeIncludes(state) { + /** @type {Array<[presetName: string, options: unknown]>} */ + const inPresets = [] + /** @type {Array<[name: string, options: unknown]>} */ + let pluginsInPreset = [] + + for (const preset of presets) { + if (preset.name === state.name) { + pluginsInPreset = preset.plugins + } + + for (const [name, options] of preset.plugins) { + if (name === state.name) { + inPresets.push([preset.name, options]) + } + } + } + + pluginsInPreset.sort(function (a, b) { + return collator.compare(a[0], b[0]) + }) + + inPresets.sort(function (a, b) { + return collator.compare(a[0], b[0]) + }) + + /** @type {Array} */ + const children = [] + + if ( + presets.some(function (d) { + return d.name === state.name + }) + ) { + assert(pluginsInPreset.length > 0, 'expected plugins in preset') + /** @type {Array} */ + children.push( + { + type: 'heading', + depth: 2, + children: [{type: 'text', value: 'Plugins'}] + }, + { + type: 'paragraph', + children: [ + { + type: 'text', + value: 'This preset includes the following plugins:' + } + ] + }, + { + type: 'table', + align: [], + children: [ + { + type: 'tableRow', + children: [ + { + type: 'tableCell', + children: [{type: 'text', value: 'Plugin'}] + }, + { + type: 'tableCell', + children: [{type: 'text', value: 'Options'}] + } + ] + }, + ...pluginsInPreset.map(function ([name, options]) { + /** @type {TableContent} */ + const row = { + type: 'tableRow', + children: [ + { + type: 'tableCell', + children: [ + { + type: 'link', + url: state.remote + '/tree/main/packages/' + name, + children: [{type: 'inlineCode', value: name}] + } + ] + }, + { + type: 'tableCell', + children: options + ? [{type: 'inlineCode', value: inspect(options)}] + : [] + } + ] + } + + return row + }) + ] + } + ) + } + + if ( + plugins.some(function (d) { + return d.name === state.name + }) + ) { + children.push({ + type: 'heading', + depth: 2, + children: [{type: 'text', value: 'Presets'}] + }) + + if (inPresets.length === 0) { + children.push({ + type: 'paragraph', + children: [ + { + type: 'text', + value: 'This plugin is not included in presets maintained here.' + } + ] + }) + } else { + children.push( + { + type: 'paragraph', + children: [ + { + type: 'text', + value: 'This plugin 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: 'Options'}] + } + ] + }, + ...inPresets.map(function ([name, options]) { + /** @type {TableContent} */ + const row = { + type: 'tableRow', + children: [ + { + type: 'tableCell', + children: [ + { + type: 'link', + url: state.remote + '/tree/main/packages/' + name, + children: [{type: 'inlineCode', value: name}] + } + ] + }, + { + type: 'tableCell', + children: options + ? [{type: 'inlineCode', value: inspect(options)}] + : [] + } + ] + } + + return row + }) + ] + } + ) + } + } + + return children +} + +/** + * @param {State} state + * Info passed around. + * @param {Specifiers} specifiers + * Specifiers. + * @returns {Array} + * Content. + */ +function generateReadmeInstall(state, specifiers) { + let defaultImportSummary = specifiers.default + /** @type {string | undefined} */ + let specifiersImportSummary + + const named = [...specifiers.named].sort() + + if (named.length > 3) { + // Star import will include the default. + defaultImportSummary = undefined + specifiersImportSummary = '* as ' + state.id + } else if (named.length > 0) { + specifiersImportSummary = '{' + named.join(', ') + '}' + } + + const importCodeSummary = + defaultImportSummary && specifiersImportSummary + ? defaultImportSummary + ', ' + specifiersImportSummary + : defaultImportSummary || specifiersImportSummary + assert(importCodeSummary) + + state.urls.set( + 'github-gist-esm', + 'https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c' + ) + state.urls.set('npm-install', 'https://docs.npmjs.com/cli/install') + state.urls.set('esm-sh', 'https://esm.sh') + + return [ + { + type: 'heading', + depth: 2, + children: [{type: 'text', value: 'Install'}] + }, + { + type: 'paragraph', + children: [ + {type: 'text', value: 'This package is '}, + { + type: 'linkReference', + identifier: 'github-gist-esm', + referenceType: 'full', + children: [{type: 'text', value: 'ESM only'}] + }, + { + type: 'text', + value: '.\nIn Node.js (version 16+),\ninstall with ' + }, + { + type: 'linkReference', + identifier: 'npm-install', + referenceType: 'collapsed', + children: [{type: 'text', value: 'npm'}] + }, + {type: 'text', value: ':'} + ] + }, + {type: 'code', lang: 'sh', value: 'npm install ' + state.name}, + { + type: 'paragraph', + children: [ + {type: 'text', value: 'In Deno with '}, + { + type: 'linkReference', + identifier: 'esm-sh', + label: 'esm-sh', + referenceType: 'full', + children: [{type: 'inlineCode', value: 'esm.sh'}] + }, + {type: 'text', value: ':'} + ] + }, + { + type: 'code', + // Idea: use estree, prettier? + lang: 'js', + value: + 'import ' + + importCodeSummary + + " from 'https://esm.sh/" + + state.name + + '@' + + state.versionMajor + + "'" + }, + { + type: 'paragraph', + children: [ + {type: 'text', value: 'In browsers with '}, + { + type: 'linkReference', + identifier: 'esm-sh', + label: 'esm-sh', + referenceType: 'full', + children: [{type: 'inlineCode', value: 'esm.sh'}] + }, + {type: 'text', value: ':'} + ] + }, + { + type: 'code', + lang: 'html', + value: + '" + } + ] +} + +/** + * @param {State} state + * Info passed around. + * @returns {Array} + * Content. + */ +function generateReadmePluggableUseSection(state) { + const addRemark = + state.name !== 'remark-lint' && + plugins.some(function (d) { + return d.name === state.name + }) + /** @type {Array<[string, string]>} */ + const imports = [ + [state.id, state.name], + ['remarkParse', 'remark-parse'], + ['remarkStringify', 'remark-stringify'], + ['{read}', 'to-vfile'], + ['{unified}', 'unified'], + ['{reporter}', 'vfile-reporter'] + ] + + if (addRemark) { + imports.push(['remarkLint', 'remark-lint']) + } + + imports.sort(function (a, b) { + return a[1].localeCompare(b[1]) + }) + + const apiLines = [ + ...imports.map(function (d) { + return 'import ' + d[0] + " from '" + d[1] + "'" + }), + '', + "const file = await read('example.md')", + '', + 'await unified()', + ' .use(remarkParse)' + ] + + if (addRemark) { + apiLines.push(' .use(remarkLint)') + } + + apiLines.push( + ' .use(' + state.id + ')', + ' .use(remarkStringify)', + ' .process(file)', + '', + 'console.error(reporter(file))' + ) + + const cliLines = [' …', ' "remarkConfig": {', ' "plugins": [', ' …'] + + if (addRemark) { + cliLines.push(' "remark-lint",') + } + + cliLines.push('+ "' + state.name + '",', ' …', ' ]', ' }', ' …') + + // Idea: use estree, prettier? + const api = apiLines.join('\n') + const cli = cliLines.join('\n') + + return [ + {type: 'heading', depth: 2, children: [{type: 'text', value: 'Use'}]}, + { + type: 'paragraph', + children: [{type: 'text', value: 'On the API:'}] + }, + { + type: 'code', + lang: 'js', + value: api + }, + {type: 'paragraph', children: [{type: 'text', value: 'On the CLI:'}]}, + { + type: 'code', + lang: 'sh', + value: + 'remark --frail' + + (addRemark ? ' --use remark-lint' : '') + + ' --use ' + + state.name + + ' .' + }, + { + 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: cli + } + ] +} + +/** + * @param {State} state + * Info passed around. + * @param {Specifiers} specifiers + * Specifiers. + * @returns {Array} + * Content. + */ +function generateReadmeApiByline(state, specifiers) { + /** @type {Paragraph} */ + const byline = {type: 'paragraph', children: []} + const named = [...specifiers.named].sort() + + if (named.length > 0) { + byline.children.push({ + type: 'text', + value: + 'This package exports the identifier' + + (named.length === 1 ? '' : 's') + + '\n' + }) + + let index = -1 + + while (++index < named.length) { + if (index !== 0) { + byline.children.push( + named.length === 2 + ? {type: 'text', value: ' and\n'} + : named.length - 1 === index + ? {type: 'text', value: ', and\n'} + : {type: 'text', value: ',\n'} + ) + } + + const specifier = named[index] + + byline.children.push({ + type: 'linkReference', + identifier: 'api-' + dashCase(specifier), + referenceType: 'full', + children: [{type: 'inlineCode', value: specifier}] + }) + } + + byline.children.push({type: 'text', value: '.'}) + } else { + byline.children.push({ + type: 'text', + value: 'This package exports no identifiers.' + }) + } + + state.urls.set('typescript', 'https://www.typescriptlang.org') + + const types = [...state.types].sort() + + if (types.length === 0) { + byline.children.push( + {type: 'text', value: '\nIt exports no additional '}, + { + type: 'linkReference', + identifier: 'typescript', + referenceType: 'collapsed', + children: [{type: 'text', value: 'TypeScript'}] + }, + {type: 'text', value: ' types.'} + ) + } else { + byline.children.push( + {type: 'text', value: '\nIt exports the '}, + { + type: 'linkReference', + identifier: 'typescript', + referenceType: 'collapsed', + children: [{type: 'text', value: 'TypeScript'}] + }, + { + type: 'text', + value: ' type' + (types.length === 1 ? '' : 's') + '\n' + } + ) + + let index = -1 + + while (++index < types.length) { + if (index !== 0) { + byline.children.push( + types.length === 2 + ? {type: 'text', value: ' and\n'} + : types.length - 1 === index + ? {type: 'text', value: ', and\n'} + : {type: 'text', value: ',\n'} + ) + } + + const type = types[index] + + byline.children.push({ + type: 'linkReference', + identifier: 'api' + dashCase(type), + referenceType: 'full', + children: [{type: 'inlineCode', value: type}] + }) + } + + byline.children.push({type: 'text', value: '.'}) + } + + if (specifiers.default) { + byline.children.push( + {type: 'text', value: '\nThe default export is\n'}, + { + type: 'linkReference', + identifier: 'api-' + dashCase(specifiers.default), + referenceType: 'full', + children: [{type: 'inlineCode', value: specifiers.default}] + }, + {type: 'text', value: '.'} + ) + } else { + byline.children.push({type: 'text', value: '\nThere is no default export.'}) + } + + return [byline] +} + +/** + * @param {State} state + * Info passed around. + * @returns {Array} + * Content. + */ +function generateReadmeExample(state) { + const info = plugins.find(function (d) { + return d.name === state.name + }) + /** @type {Array} */ + const children = [] + const checks = info?.checks || [] + let first = true + + for (const check of checks) { + if (first) { + children.push({ + type: 'heading', + depth: 2, + children: [{type: 'text', value: 'Examples'}] + }) + + first = false + } + + children.push({ + type: 'heading', + depth: 5, + children: [{type: 'inlineCode', value: check.name}] + }) + + /** @type {{config: unknown}} */ + const {config} = JSON.parse(check.configuration) + + if (config !== true) { + children.push({ + type: 'paragraph', + children: [ + {type: 'text', value: 'When configured with '}, + {type: 'inlineCode', value: inspect(config)}, + {type: 'text', value: '.'} + ] + }) + } + + const empty = check.input.trim() === '' + + state.urls.set( + 'github-remark-gfm', + 'https://github.com/remarkjs/remark-gfm' + ) + + if (!empty) { + children.push({ + type: 'heading', + depth: 6, + children: [{type: 'text', value: 'In'}] + }) + + // To do: other plugins. + if (check.gfm) { + 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: 'github-remark-gfm', + referenceType: 'full', + children: [{type: 'inlineCode', value: 'remark-gfm'}] + }, + {type: 'text', value: ').'} + ] + } + ] + }) + } + + 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') + }) + } + } + + return children +} + +/** + * @param {State} state + * Info passed around. + * @returns {Array} + * Content. + */ +function generateReadmeTail(state) { + const org = state.remote.split('/').slice(0, -1).join('/') + const health = org + '/.github' + const hMain = health + '/blob/main' + + state.urls.set('github-dotfiles-coc', hMain + '/code-of-conduct.md') + state.urls.set('github-dotfiles-contributing', hMain + '/contributing.md') + state.urls.set('github-dotfiles-health', health) + state.urls.set('github-dotfiles-support', hMain + '/support.md') + state.urls.set('file-license', state.remote + '/blob/main/license') + state.urls.set('author', String(state.author.url || '')) + + return [ + { + type: 'heading', + depth: 2, + children: [{type: 'text', value: 'Compatibility'}] + }, + { + type: 'paragraph', + children: [ + { + type: 'text', + value: + 'Projects maintained by the unified collective are compatible with maintained\nversions of Node.js.\n\nWhen we cut a new major release, we drop support for unmaintained versions of\nNode.\nThis means we try to keep the current release line,\n' + }, + { + type: 'inlineCode', + value: state.name + '@' + state.versionMajor + }, + { + type: 'text', + value: ',\ncompatible with Node.js 12.' + } + ] + }, + { + type: 'heading', + depth: 2, + children: [{type: 'text', value: 'Contribute'}] + }, + { + type: 'paragraph', + children: [ + {type: 'text', value: 'See '}, + { + type: 'linkReference', + referenceType: 'full', + identifier: 'github-dotfiles-contributing', + children: [{type: 'inlineCode', value: 'contributing.md'}] + }, + {type: 'text', value: ' in '}, + { + type: 'linkReference', + referenceType: 'full', + identifier: 'github-dotfiles-health', + children: [{type: 'inlineCode', value: 'remarkjs/.github'}] + }, + {type: 'text', value: ' for ways\nto get started.\nSee '}, + { + type: 'linkReference', + referenceType: 'full', + identifier: 'github-dotfiles-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: 'full', + identifier: 'github-dotfiles-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: 'full', + identifier: 'file-license', + children: [{type: 'text', value: String(state.license || '')}] + }, + {type: 'text', value: ' © '}, + { + type: 'linkReference', + referenceType: 'full', + identifier: 'author', + children: [{type: 'text', value: String(state.author.name || '')}] + } + ] + } + ] +} + +/** + * @param {string} value + * @returns {string} + */ +function dashCase(value) { + return value.replace(/[A-Z]/g, replacer) + + /** + * @param {string} $0 + * @returns {string} + */ + function replacer($0) { + return '-' + $0.toLowerCase() + } +} diff --git a/script/plugin/list-of-plugins.js b/script/plugin/list-of-plugins.js index 8aee7b7..a675bc0 100644 --- a/script/plugin/list-of-plugins.js +++ b/script/plugin/list-of-plugins.js @@ -12,57 +12,6 @@ import structuredClone from '@ungap/structured-clone' 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. * @@ -75,10 +24,59 @@ export default function remarkListOfRules() { * * @param {Root} tree * Tree. - * @returns {undefined} + * @returns {Promise} * Nothing. */ - return function (tree) { + return async function (tree) { + /** + * @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 (!info.ruleId || /^deprecated/i.test(description)) return + + assert(typeof pack.repository === 'string') + + return { + type: 'listItem', + spread: false, + children: [ + { + type: 'paragraph', + children: [ + { + type: 'link', + url: pack.repository, + 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) + } + 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 960450d..1075615 100644 --- a/script/plugin/list-of-presets.js +++ b/script/plugin/list-of-presets.js @@ -29,8 +29,7 @@ const items = await Promise.all( '' ) - assert(pack.repository && typeof pack.repository === 'object') - assert(pack.repository.directory) + assert(typeof pack.repository === 'string') return { type: 'listItem', @@ -41,8 +40,7 @@ const items = await Promise.all( children: [ { type: 'link', - url: - pack.repository.url + '/tree/main/' + pack.repository.directory, + url: pack.repository, children: [{type: 'inlineCode', value: info.name}] }, {type: 'text', value: ' — ' + description} diff --git a/test.js b/test.js index 743b7e9..1ac29a7 100644 --- a/test.js +++ b/test.js @@ -8,6 +8,7 @@ import assert from 'node:assert/strict' import test from 'node:test' +import {controlPictures} from 'control-pictures' import {remark} from 'remark' import remarkGfm from 'remark-gfm' import remarkLint from 'remark-lint' @@ -18,7 +19,6 @@ import remarkLintNoUndefinedReferences from 'remark-lint-no-undefined-references import {lintRule} from 'unified-lint-rule' import {removePosition} from 'unist-util-remove-position' import {VFile} from 'vfile' -import {characters} from './script/characters.js' import {plugins} from './script/info.js' test('remark-lint', async function (t) { @@ -303,11 +303,7 @@ async function assertCheck(plugin, info, check) { const {config} = JSON.parse(check.configuration) /** @type {PluggableList} */ const extras = check.gfm ? [remarkGfm] : [] - let value = check.input - - for (const character of characters) { - value = value.replace(character.in, character.out) - } + const value = controlPictures(check.input) const file = await remark() .use(plugin, config)