diff --git a/.gitignore b/.gitignore index 71fd2246..f42d6fe9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,80 @@ coverage/ node_modules/ .DS_Store +/packages/remark-lint/*.d.ts +/packages/remark-lint-blockquote-indentation/*.d.ts +/packages/remark-lint-checkbox-character-style/*.d.ts +/packages/remark-lint-checkbox-content-indent/*.d.ts +/packages/remark-lint-code-block-style/*.d.ts +/packages/remark-lint-definition-case/*.d.ts +/packages/remark-lint-definition-spacing/*.d.ts +/packages/remark-lint-emphasis-marker/*.d.ts +/packages/remark-lint-fenced-code-flag/*.d.ts +/packages/remark-lint-fenced-code-marker/*.d.ts +/packages/remark-lint-file-extension/*.d.ts +/packages/remark-lint-final-definition/*.d.ts +/packages/remark-lint-final-newline/*.d.ts +/packages/remark-lint-first-heading-level/*.d.ts +/packages/remark-lint-hard-break-spaces/*.d.ts +/packages/remark-lint-heading-increment/*.d.ts +/packages/remark-lint-heading-style/*.d.ts +/packages/remark-lint-linebreak-style/*.d.ts +/packages/remark-lint-link-title-style/*.d.ts +/packages/remark-lint-list-item-bullet-indent/*.d.ts +/packages/remark-lint-list-item-content-indent/*.d.ts +/packages/remark-lint-list-item-indent/*.d.ts +/packages/remark-lint-list-item-spacing/*.d.ts +/packages/remark-lint-maximum-heading-length/*.d.ts +/packages/remark-lint-maximum-line-length/*.d.ts +/packages/remark-lint-no-auto-link-without-protocol/*.d.ts +/packages/remark-lint-no-blockquote-without-marker/*.d.ts +/packages/remark-lint-no-consecutive-blank-lines/*.d.ts +/packages/remark-lint-no-duplicate-defined-urls/*.d.ts +/packages/remark-lint-no-duplicate-definitions/*.d.ts +/packages/remark-lint-no-duplicate-headings/*.d.ts +/packages/remark-lint-no-duplicate-headings-in-section/*.d.ts +/packages/remark-lint-no-emphasis-as-heading/*.d.ts +/packages/remark-lint-no-empty-url/*.d.ts +/packages/remark-lint-no-file-name-articles/*.d.ts +/packages/remark-lint-no-file-name-consecutive-dashes/*.d.ts +/packages/remark-lint-no-file-name-irregular-characters/*.d.ts +/packages/remark-lint-no-file-name-mixed-case/*.d.ts +/packages/remark-lint-no-file-name-outer-dashes/*.d.ts +/packages/remark-lint-no-heading-content-indent/*.d.ts +/packages/remark-lint-no-heading-indent/*.d.ts +/packages/remark-lint-no-heading-like-paragraph/*.d.ts +/packages/remark-lint-no-heading-punctuation/*.d.ts +/packages/remark-lint-no-html/*.d.ts +/packages/remark-lint-no-inline-padding/*.d.ts +/packages/remark-lint-no-literal-urls/*.d.ts +/packages/remark-lint-no-missing-blank-lines/*.d.ts +/packages/remark-lint-no-multiple-toplevel-headings/*.d.ts +/packages/remark-lint-no-paragraph-content-indent/*.d.ts +/packages/remark-lint-no-reference-like-url/*.d.ts +/packages/remark-lint-no-shell-dollars/*.d.ts +/packages/remark-lint-no-shortcut-reference-image/*.d.ts +/packages/remark-lint-no-shortcut-reference-link/*.d.ts +/packages/remark-lint-no-table-indentation/*.d.ts +/packages/remark-lint-no-tabs/*.d.ts +/packages/remark-lint-no-undefined-references/*.d.ts +/packages/remark-lint-no-unneeded-full-reference-image/*.d.ts +/packages/remark-lint-no-unneeded-full-reference-link/*.d.ts +/packages/remark-lint-no-unused-definitions/*.d.ts +/packages/remark-lint-ordered-list-marker-style/*.d.ts +/packages/remark-lint-ordered-list-marker-value/*.d.ts +/packages/remark-lint-rule-style/*.d.ts +/packages/remark-lint-strikethrough-marker/*.d.ts +/packages/remark-lint-strong-marker/*.d.ts +/packages/remark-lint-table-cell-padding/*.d.ts +/packages/remark-lint-table-pipe-alignment/*.d.ts +/packages/remark-lint-table-pipes/*.d.ts +/packages/remark-lint-unordered-list-marker-style/*.d.ts +/packages/remark-preset-lint-consistent/*.d.ts +/packages/remark-preset-lint-markdown-style-guide/*.d.ts +/packages/remark-preset-lint-recommended/*.d.ts +/packages/unified-lint-rule/lib/*.d.ts +/script/**/*.d.ts +/*.d.ts *.log package-lock.json yarn.lock diff --git a/package.json b/package.json index b401e40b..01ca3d16 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,10 @@ ], "type": "module", "devDependencies": { + "@types/mdast": "^3.0.0", + "@types/parse-author": "^2.0.0", + "@types/pluralize": "^0.0.29", + "@types/tape": "^4.0.0", "c8": "^7.0.0", "comment-parser": "^1.0.0", "lerna": "^4.0.0", @@ -46,22 +50,27 @@ "remark-github": "^11.0.0", "remark-toc": "^8.0.0", "remark-validate-links": "^11.0.0", + "rimraf": "^3.0.0", "strip-indent": "^4.0.0", "tape": "^5.0.0", "to-vfile": "^7.0.0", + "type-coverage": "^2.0.0", + "type-fest": "^2.0.0", + "typescript": "^4.0.0", "unist-builder": "^3.0.0", "unist-util-remove-position": "^4.0.0", "xo": "^0.39.0" }, "scripts": { "postinstall": "lerna bootstrap --no-ci", + "build-packages": "node script/build-presets && node script/build-rules", + "build-workspace": "lerna run build", + "build-monorepo": "rimraf \"test.d.ts\" \"script/**/*.d.ts\" && tsc && type-coverage", + "build": "npm run build-packages && npm run build-workspace && npm run build-monorepo", "format": "remark . -qfo && prettier . -w --loglevel warn && xo --fix", - "generate:presets": "node script/build-presets", - "generate:rules": "node script/build-rules", - "generate": "npm run generate:presets && npm run generate:rules", "test-api": "node --conditions development test.js", "test-coverage": "c8 --check-coverage --branches 100 --functions 100 --lines 100 --statements 100 --reporter lcov npm run test-api", - "test": "npm run generate && npm run format && npm run test-coverage" + "test": "npm run build && npm run format && npm run test-coverage" }, "prettier": { "tabWidth": 2, @@ -88,5 +97,11 @@ } } ] + }, + "typeCoverage": { + "atLeast": 100, + "detail": true, + "strict": true, + "ignoreCatch": true } } diff --git a/packages/remark-lint-blockquote-indentation/index.js b/packages/remark-lint-blockquote-indentation/index.js index 0a6656ae..e78955d4 100644 --- a/packages/remark-lint-blockquote-indentation/index.js +++ b/packages/remark-lint-blockquote-indentation/index.js @@ -48,6 +48,12 @@ * 9:3: Add 1 space between block quote and content */ +/** + * @typedef {import('mdast').Root} Root + * @typedef {import('mdast').Blockquote} Blockquote + * @typedef {'consistent'|number} Options + */ + import {lintRule} from 'unified-lint-rule' import plural from 'pluralize' import {visit} from 'unist-util-visit' @@ -56,41 +62,42 @@ import {generated} from 'unist-util-generated' const remarkLintBlockquoteIndentation = lintRule( 'remark-lint:blockquote-indentation', - blockquoteIndentation -) - -export default remarkLintBlockquoteIndentation - -function blockquoteIndentation(tree, file, option) { - let preferred = typeof option === 'number' ? option : null - - visit(tree, 'blockquote', (node) => { - if (generated(node) || node.children.length === 0) { - return - } + /** @type {import('unified-lint-rule').Rule} */ + (tree, file, option = 'consistent') => { + visit(tree, 'blockquote', (node) => { + if (generated(node) || node.children.length === 0) { + return + } - if (preferred) { - const diff = preferred - check(node) + if (option === 'consistent') { + option = check(node) + } else { + const diff = option - check(node) - if (diff !== 0) { - const abs = Math.abs(diff) + if (diff !== 0) { + const abs = Math.abs(diff) - file.message( - (diff > 0 ? 'Add' : 'Remove') + - ' ' + - abs + - ' ' + - plural('space', abs) + - ' between block quote and content', - pointStart(node.children[0]) - ) + file.message( + (diff > 0 ? 'Add' : 'Remove') + + ' ' + + abs + + ' ' + + plural('space', abs) + + ' between block quote and content', + pointStart(node.children[0]) + ) + } } - } else { - preferred = check(node) - } - }) -} + }) + } +) +export default remarkLintBlockquoteIndentation + +/** + * @param {Blockquote} node + * @returns {number} + */ function check(node) { return pointStart(node.children[0]).column - pointStart(node).column } diff --git a/packages/remark-lint-blockquote-indentation/package.json b/packages/remark-lint-blockquote-indentation/package.json index 7ed8308d..0355bf3d 100644 --- a/packages/remark-lint-blockquote-indentation/package.json +++ b/packages/remark-lint-blockquote-indentation/package.json @@ -25,15 +25,28 @@ "sideEffects": false, "type": "module", "main": "index.js", + "types": "index.d.ts", "files": [ + "index.d.ts", "index.js" ], "dependencies": { + "@types/mdast": "^3.0.0", "pluralize": "^8.0.0", + "unified": "^10.0.0", "unified-lint-rule": "^1.0.0", "unist-util-generated": "^2.0.0", "unist-util-position": "^4.0.0", "unist-util-visit": "^4.0.0" }, - "xo": false + "scripts": { + "build": "rimraf \"*.d.ts\" && tsc && type-coverage" + }, + "xo": false, + "typeCoverage": { + "atLeast": 100, + "detail": true, + "strict": true, + "ignoreCatch": true + } } diff --git a/packages/remark-lint-blockquote-indentation/readme.md b/packages/remark-lint-blockquote-indentation/readme.md index d6d80d29..673c68a1 100644 --- a/packages/remark-lint-blockquote-indentation/readme.md +++ b/packages/remark-lint-blockquote-indentation/readme.md @@ -89,12 +89,18 @@ Paragraph. ## Install +This package is [ESM only][esm]: +Node 12+ is needed to use it and it must be `imported`ed instead of `required`d. + [npm][]: ```sh npm install remark-lint-blockquote-indentation ``` +This package exports no identifiers. +The default export is `remarkLintBlockquoteIndentation`. + ## Use You probably want to use it on the CLI through a config file: @@ -175,6 +181,8 @@ abide by its terms. [chat]: https://github.com/remarkjs/remark/discussions +[esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c + [npm]: https://docs.npmjs.com/cli/install [health]: https://github.com/remarkjs/.github diff --git a/packages/remark-lint-blockquote-indentation/tsconfig.json b/packages/remark-lint-blockquote-indentation/tsconfig.json new file mode 100644 index 00000000..7e61871a --- /dev/null +++ b/packages/remark-lint-blockquote-indentation/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.json", + "include": ["*.js"] +} diff --git a/packages/remark-lint-checkbox-character-style/index.js b/packages/remark-lint-checkbox-character-style/index.js index 2a548f14..b134dcdd 100644 --- a/packages/remark-lint-checkbox-character-style/index.js +++ b/packages/remark-lint-checkbox-character-style/index.js @@ -77,49 +77,65 @@ * 1:1: Incorrect checked checkbox marker `💩`: use either `'x'`, or `'X'` */ +/** + * @typedef {import('mdast').Root} Root + * + * @typedef Styles + * @property {'x'|'X'|'consistent'} [checked='consistent'] + * @property {' '|'\x09'|'consistent'} [unchecked='consistent'] + * + * @typedef {'consistent'|Styles} Options + */ + import {lintRule} from 'unified-lint-rule' import {visit} from 'unist-util-visit' import {pointStart} from 'unist-util-position' -import {generated} from 'unist-util-generated' - -const checked = {x: true, X: true} -const unchecked = {' ': true, '\t': true} -const types = {true: 'checked', false: 'unchecked'} const remarkLintCheckboxCharacterStyle = lintRule( 'remark-lint:checkbox-character-style', - (tree, file, option) => { + /** @type {import('unified-lint-rule').Rule} */ + (tree, file, option = 'consistent') => { const value = String(file) - const preferred = typeof option === 'object' ? option : {} + /** @type {'x'|'X'|'consistent'} */ + let checked = 'consistent' + /** @type {' '|'\x09'|'consistent'} */ + let unchecked = 'consistent' - if (preferred.unchecked && unchecked[preferred.unchecked] !== true) { + if (typeof option === 'object') { + checked = option.checked || 'consistent' + unchecked = option.unchecked || 'consistent' + } + + if (unchecked !== 'consistent' && unchecked !== ' ' && unchecked !== '\t') { file.fail( 'Incorrect unchecked checkbox marker `' + - preferred.unchecked + + unchecked + "`: use either `'\\t'`, or `' '`" ) } - if (preferred.checked && checked[preferred.checked] !== true) { + if (checked !== 'consistent' && checked !== 'x' && checked !== 'X') { file.fail( 'Incorrect checked checkbox marker `' + - preferred.checked + + checked + "`: use either `'x'`, or `'X'`" ) } visit(tree, 'listItem', (node) => { const head = node.children[0] + const point = pointStart(head) + // Exit early for items without checkbox. // A list item cannot be checked and empty, according to GFM. - if (typeof node.checked !== 'boolean' || generated(node) || !head) { + if ( + typeof node.checked !== 'boolean' || + !head || + typeof point.offset !== 'number' + ) { return } - const type = types[node.checked] - - const point = pointStart(head) - // Move back to before `] `. point.offset -= 2 point.column -= 2 @@ -133,21 +149,24 @@ const remarkLintCheckboxCharacterStyle = lintRule( /* c8 ignore next */ if (!match) return - const style = preferred[type] + const style = node.checked ? checked : unchecked - if (style) { - if (match[1] !== style) { - file.message( - type.charAt(0).toUpperCase() + - type.slice(1) + - ' checkboxes should use `' + - style + - '` as a marker', - point - ) + if (style === 'consistent') { + if (node.checked) { + // @ts-expect-error: valid marker. + checked = match[1] + } else { + // @ts-expect-error: valid marker. + unchecked = match[1] } - } else { - preferred[type] = match[1] + } else if (match[1] !== style) { + file.message( + (node.checked ? 'Checked' : 'Unchecked') + + ' checkboxes should use `' + + style + + '` as a marker', + point + ) } }) } diff --git a/packages/remark-lint-checkbox-character-style/package.json b/packages/remark-lint-checkbox-character-style/package.json index 920d29cd..7eb532b9 100644 --- a/packages/remark-lint-checkbox-character-style/package.json +++ b/packages/remark-lint-checkbox-character-style/package.json @@ -26,14 +26,26 @@ "sideEffects": false, "type": "module", "main": "index.js", + "types": "index.d.ts", "files": [ + "index.d.ts", "index.js" ], "dependencies": { + "@types/mdast": "^3.0.0", + "unified": "^10.0.0", "unified-lint-rule": "^1.0.0", - "unist-util-generated": "^2.0.0", "unist-util-position": "^4.0.0", "unist-util-visit": "^4.0.0" }, - "xo": false + "scripts": { + "build": "rimraf \"*.d.ts\" && tsc && type-coverage" + }, + "xo": false, + "typeCoverage": { + "atLeast": 100, + "detail": true, + "strict": true, + "ignoreCatch": true + } } diff --git a/packages/remark-lint-checkbox-character-style/readme.md b/packages/remark-lint-checkbox-character-style/readme.md index 6e807ef1..eb472e75 100644 --- a/packages/remark-lint-checkbox-character-style/readme.md +++ b/packages/remark-lint-checkbox-character-style/readme.md @@ -160,12 +160,18 @@ When configured with `{ checked: '💩' }`. ## Install +This package is [ESM only][esm]: +Node 12+ is needed to use it and it must be `imported`ed instead of `required`d. + [npm][]: ```sh npm install remark-lint-checkbox-character-style ``` +This package exports no identifiers. +The default export is `remarkLintCheckboxCharacterStyle`. + ## Use You probably want to use it on the CLI through a config file: @@ -246,6 +252,8 @@ abide by its terms. [chat]: https://github.com/remarkjs/remark/discussions +[esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c + [npm]: https://docs.npmjs.com/cli/install [health]: https://github.com/remarkjs/.github diff --git a/packages/remark-lint-checkbox-character-style/tsconfig.json b/packages/remark-lint-checkbox-character-style/tsconfig.json new file mode 100644 index 00000000..7e61871a --- /dev/null +++ b/packages/remark-lint-checkbox-character-style/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.json", + "include": ["*.js"] +} diff --git a/packages/remark-lint-checkbox-content-indent/index.js b/packages/remark-lint-checkbox-content-indent/index.js index 69795a79..996d0bf5 100644 --- a/packages/remark-lint-checkbox-content-indent/index.js +++ b/packages/remark-lint-checkbox-content-indent/index.js @@ -30,29 +30,36 @@ * 4:7-4:10: Checkboxes should be followed by a single character */ +/** + * @typedef {import('mdast').Root} Root + */ + import {lintRule} from 'unified-lint-rule' import {location} from 'vfile-location' import {visit} from 'unist-util-visit' import {pointStart} from 'unist-util-position' -import {generated} from 'unist-util-generated' const remarkLintCheckboxContentIndent = lintRule( 'remark-lint:checkbox-content-indent', + /** @type {import('unified-lint-rule').Rule} */ (tree, file) => { const value = String(file) const loc = location(file) visit(tree, 'listItem', (node) => { const head = node.children[0] + const point = pointStart(head) // Exit early for items without checkbox. // A list item cannot be checked and empty, according to GFM. - if (typeof node.checked !== 'boolean' || generated(node) || !head) { + if ( + typeof node.checked !== 'boolean' || + !head || + typeof point.offset !== 'number' + ) { return } - const point = pointStart(head) - // Assume we start with a checkbox, because well, `checked` is set. const match = /\[([\t xX])]/.exec( value.slice(point.offset - 4, point.offset + 1) diff --git a/packages/remark-lint-checkbox-content-indent/package.json b/packages/remark-lint-checkbox-content-indent/package.json index 5eb12bb8..8fe5522c 100644 --- a/packages/remark-lint-checkbox-content-indent/package.json +++ b/packages/remark-lint-checkbox-content-indent/package.json @@ -26,15 +26,27 @@ "sideEffects": false, "type": "module", "main": "index.js", + "types": "index.d.ts", "files": [ + "index.d.ts", "index.js" ], "dependencies": { + "@types/mdast": "^3.0.0", + "unified": "^10.0.0", "unified-lint-rule": "^1.0.0", - "unist-util-generated": "^2.0.0", "unist-util-position": "^4.0.0", "unist-util-visit": "^4.0.0", "vfile-location": "^4.0.0" }, - "xo": false + "scripts": { + "build": "rimraf \"*.d.ts\" && tsc && type-coverage" + }, + "xo": false, + "typeCoverage": { + "atLeast": 100, + "detail": true, + "strict": true, + "ignoreCatch": true + } } diff --git a/packages/remark-lint-checkbox-content-indent/readme.md b/packages/remark-lint-checkbox-content-indent/readme.md index 108bd1fa..34945006 100644 --- a/packages/remark-lint-checkbox-content-indent/readme.md +++ b/packages/remark-lint-checkbox-content-indent/readme.md @@ -58,12 +58,18 @@ Note: this example uses [GFM][]. ## Install +This package is [ESM only][esm]: +Node 12+ is needed to use it and it must be `imported`ed instead of `required`d. + [npm][]: ```sh npm install remark-lint-checkbox-content-indent ``` +This package exports no identifiers. +The default export is `remarkLintCheckboxContentIndent`. + ## Use You probably want to use it on the CLI through a config file: @@ -144,6 +150,8 @@ abide by its terms. [chat]: https://github.com/remarkjs/remark/discussions +[esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c + [npm]: https://docs.npmjs.com/cli/install [health]: https://github.com/remarkjs/.github diff --git a/packages/remark-lint-checkbox-content-indent/tsconfig.json b/packages/remark-lint-checkbox-content-indent/tsconfig.json new file mode 100644 index 00000000..7e61871a --- /dev/null +++ b/packages/remark-lint-checkbox-content-indent/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.json", + "include": ["*.js"] +} diff --git a/packages/remark-lint-code-block-style/index.js b/packages/remark-lint-code-block-style/index.js index 74c13e04..31b6ff7f 100644 --- a/packages/remark-lint-code-block-style/index.js +++ b/packages/remark-lint-code-block-style/index.js @@ -101,52 +101,55 @@ * 1:1: Incorrect code block style `💩`: use either `'consistent'`, `'fenced'`, or `'indented'` */ +/** + * @typedef {import('mdast').Root} Root + * @typedef {'fenced'|'indented'} Style + * @typedef {'consistent'|Style} Options + */ + import {lintRule} from 'unified-lint-rule' import {visit} from 'unist-util-visit' import {pointStart, pointEnd} from 'unist-util-position' import {generated} from 'unist-util-generated' -const styles = {null: true, fenced: true, indented: true} - const remarkLintCodeBlockStyle = lintRule( 'remark-lint:code-block-style', - codeBlockStyle -) + /** @type {import('unified-lint-rule').Rule} */ + (tree, file, option = 'consistent') => { + const value = String(file) -export default remarkLintCodeBlockStyle - -function codeBlockStyle(tree, file, option) { - const value = String(file) - let preferred = - typeof option === 'string' && option !== 'consistent' ? option : null - - if (styles[preferred] !== true) { - file.fail( - 'Incorrect code block style `' + - preferred + - "`: use either `'consistent'`, `'fenced'`, or `'indented'`" - ) - } - - visit(tree, 'code', (node) => { - if (generated(node)) { - return + if ( + option !== 'consistent' && + option !== 'fenced' && + option !== 'indented' + ) { + file.fail( + 'Incorrect code block style `' + + option + + "`: use either `'consistent'`, `'fenced'`, or `'indented'`" + ) } - const initial = pointStart(node).offset - const final = pointEnd(node).offset + visit(tree, 'code', (node) => { + if (generated(node)) { + return + } + + const initial = pointStart(node).offset + const final = pointEnd(node).offset - const current = - node.lang || /^\s*([~`])\1{2,}/.test(value.slice(initial, final)) - ? 'fenced' - : 'indented' + const current = + node.lang || /^\s*([~`])\1{2,}/.test(value.slice(initial, final)) + ? 'fenced' + : 'indented' - if (preferred) { - if (preferred !== current) { - file.message('Code blocks should be ' + preferred, node) + if (option === 'consistent') { + option = current + } else if (option !== current) { + file.message('Code blocks should be ' + option, node) } - } else { - preferred = current - } - }) -} + }) + } +) + +export default remarkLintCodeBlockStyle diff --git a/packages/remark-lint-code-block-style/package.json b/packages/remark-lint-code-block-style/package.json index c0245c91..08b8bf24 100644 --- a/packages/remark-lint-code-block-style/package.json +++ b/packages/remark-lint-code-block-style/package.json @@ -24,14 +24,27 @@ "sideEffects": false, "type": "module", "main": "index.js", + "types": "index.d.ts", "files": [ + "index.d.ts", "index.js" ], "dependencies": { + "@types/mdast": "^3.0.0", + "unified": "^10.0.0", "unified-lint-rule": "^1.0.0", "unist-util-generated": "^2.0.0", "unist-util-position": "^4.0.0", "unist-util-visit": "^4.0.0" }, - "xo": false + "scripts": { + "build": "rimraf \"*.d.ts\" && tsc && type-coverage" + }, + "xo": false, + "typeCoverage": { + "atLeast": 100, + "detail": true, + "strict": true, + "ignoreCatch": true + } } diff --git a/packages/remark-lint-code-block-style/readme.md b/packages/remark-lint-code-block-style/readme.md index a1c96ba5..2aa06fdc 100644 --- a/packages/remark-lint-code-block-style/readme.md +++ b/packages/remark-lint-code-block-style/readme.md @@ -158,12 +158,18 @@ When configured with `'💩'`. ## Install +This package is [ESM only][esm]: +Node 12+ is needed to use it and it must be `imported`ed instead of `required`d. + [npm][]: ```sh npm install remark-lint-code-block-style ``` +This package exports no identifiers. +The default export is `remarkLintCodeBlockStyle`. + ## Use You probably want to use it on the CLI through a config file: @@ -244,6 +250,8 @@ abide by its terms. [chat]: https://github.com/remarkjs/remark/discussions +[esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c + [npm]: https://docs.npmjs.com/cli/install [health]: https://github.com/remarkjs/.github diff --git a/packages/remark-lint-code-block-style/tsconfig.json b/packages/remark-lint-code-block-style/tsconfig.json new file mode 100644 index 00000000..7e61871a --- /dev/null +++ b/packages/remark-lint-code-block-style/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.json", + "include": ["*.js"] +} diff --git a/packages/remark-lint-definition-case/index.js b/packages/remark-lint-definition-case/index.js index ea9820ac..9b41aa54 100644 --- a/packages/remark-lint-definition-case/index.js +++ b/packages/remark-lint-definition-case/index.js @@ -22,32 +22,36 @@ * 1:1-1:47: Do not use uppercase characters in definition labels */ +/** + * @typedef {import('mdast').Root} Root + */ + import {lintRule} from 'unified-lint-rule' import {visit} from 'unist-util-visit' import {pointStart, pointEnd} from 'unist-util-position' -import {generated} from 'unist-util-generated' const label = /^\s*\[((?:\\[\s\S]|[^[\]])+)]/ const remarkLintDefinitionCase = lintRule( 'remark-lint:definition-case', + /** @type {import('unified-lint-rule').Rule} */ (tree, file) => { const value = String(file) visit(tree, (node) => { - if ( - (node.type === 'definition' || node.type === 'footnoteDefinition') && - !generated(node) - ) { + if (node.type === 'definition' || node.type === 'footnoteDefinition') { const start = pointStart(node).offset const end = pointEnd(node).offset - const slice = value.slice(start, end).match(label)[1] - if (slice !== slice.toLowerCase()) { - file.message( - 'Do not use uppercase characters in definition labels', - node - ) + if (typeof start === 'number' && typeof end === 'number') { + const match = value.slice(start, end).match(label) + + if (match && match[1] !== match[1].toLowerCase()) { + file.message( + 'Do not use uppercase characters in definition labels', + node + ) + } } } }) diff --git a/packages/remark-lint-definition-case/package.json b/packages/remark-lint-definition-case/package.json index 1e5a594a..d32cf901 100644 --- a/packages/remark-lint-definition-case/package.json +++ b/packages/remark-lint-definition-case/package.json @@ -24,14 +24,26 @@ "sideEffects": false, "type": "module", "main": "index.js", + "types": "index.d.ts", "files": [ + "index.d.ts", "index.js" ], "dependencies": { + "@types/mdast": "^3.0.0", + "unified": "^10.0.0", "unified-lint-rule": "^1.0.0", - "unist-util-generated": "^2.0.0", "unist-util-position": "^4.0.0", "unist-util-visit": "^4.0.0" }, - "xo": false + "scripts": { + "build": "rimraf \"*.d.ts\" && tsc && type-coverage" + }, + "xo": false, + "typeCoverage": { + "atLeast": 100, + "detail": true, + "strict": true, + "ignoreCatch": true + } } diff --git a/packages/remark-lint-definition-case/readme.md b/packages/remark-lint-definition-case/readme.md index 27528f19..5dbc9e8b 100644 --- a/packages/remark-lint-definition-case/readme.md +++ b/packages/remark-lint-definition-case/readme.md @@ -50,12 +50,18 @@ No messages. ## Install +This package is [ESM only][esm]: +Node 12+ is needed to use it and it must be `imported`ed instead of `required`d. + [npm][]: ```sh npm install remark-lint-definition-case ``` +This package exports no identifiers. +The default export is `remarkLintDefinitionCase`. + ## Use You probably want to use it on the CLI through a config file: @@ -136,6 +142,8 @@ abide by its terms. [chat]: https://github.com/remarkjs/remark/discussions +[esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c + [npm]: https://docs.npmjs.com/cli/install [health]: https://github.com/remarkjs/.github diff --git a/packages/remark-lint-definition-case/tsconfig.json b/packages/remark-lint-definition-case/tsconfig.json new file mode 100644 index 00000000..7e61871a --- /dev/null +++ b/packages/remark-lint-definition-case/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.json", + "include": ["*.js"] +} diff --git a/packages/remark-lint-definition-spacing/index.js b/packages/remark-lint-definition-spacing/index.js index 6af1c79b..252f8f9f 100644 --- a/packages/remark-lint-definition-spacing/index.js +++ b/packages/remark-lint-definition-spacing/index.js @@ -22,31 +22,36 @@ * 1:1-1:57: Do not use consecutive whitespace in definition labels */ +/** + * @typedef {import('mdast').Root} Root + */ + import {lintRule} from 'unified-lint-rule' import {visit} from 'unist-util-visit' import {pointStart, pointEnd} from 'unist-util-position' -import {generated} from 'unist-util-generated' const label = /^\s*\[((?:\\[\s\S]|[^[\]])+)]/ const remarkLintDefinitionSpacing = lintRule( 'remark-lint:definition-spacing', + /** @type {import('unified-lint-rule').Rule} */ (tree, file) => { const value = String(file) visit(tree, (node) => { - if ( - (node.type === 'definition' || node.type === 'footnoteDefinition') && - !generated(node) - ) { + if (node.type === 'definition' || node.type === 'footnoteDefinition') { const start = pointStart(node).offset const end = pointEnd(node).offset - if (/[ \t\n]{2,}/.test(value.slice(start, end).match(label)[1])) { - file.message( - 'Do not use consecutive whitespace in definition labels', - node - ) + if (typeof start === 'number' && typeof end === 'number') { + const match = value.slice(start, end).match(label) + + if (match && /[ \t\n]{2,}/.test(match[1])) { + file.message( + 'Do not use consecutive whitespace in definition labels', + node + ) + } } } }) diff --git a/packages/remark-lint-definition-spacing/package.json b/packages/remark-lint-definition-spacing/package.json index 5b5a9dca..e6158f39 100644 --- a/packages/remark-lint-definition-spacing/package.json +++ b/packages/remark-lint-definition-spacing/package.json @@ -24,14 +24,26 @@ "sideEffects": false, "type": "module", "main": "index.js", + "types": "index.d.ts", "files": [ + "index.d.ts", "index.js" ], "dependencies": { + "@types/mdast": "^3.0.0", + "unified": "^10.0.0", "unified-lint-rule": "^1.0.0", - "unist-util-generated": "^2.0.0", "unist-util-position": "^4.0.0", "unist-util-visit": "^4.0.0" }, - "xo": false + "scripts": { + "build": "rimraf \"*.d.ts\" && tsc && type-coverage" + }, + "xo": false, + "typeCoverage": { + "atLeast": 100, + "detail": true, + "strict": true, + "ignoreCatch": true + } } diff --git a/packages/remark-lint-definition-spacing/readme.md b/packages/remark-lint-definition-spacing/readme.md index 81bad1b0..91e26f51 100644 --- a/packages/remark-lint-definition-spacing/readme.md +++ b/packages/remark-lint-definition-spacing/readme.md @@ -52,12 +52,18 @@ Note: `·` represents a space. ## Install +This package is [ESM only][esm]: +Node 12+ is needed to use it and it must be `imported`ed instead of `required`d. + [npm][]: ```sh npm install remark-lint-definition-spacing ``` +This package exports no identifiers. +The default export is `remarkLintDefinitionSpacing`. + ## Use You probably want to use it on the CLI through a config file: @@ -138,6 +144,8 @@ abide by its terms. [chat]: https://github.com/remarkjs/remark/discussions +[esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c + [npm]: https://docs.npmjs.com/cli/install [health]: https://github.com/remarkjs/.github diff --git a/packages/remark-lint-definition-spacing/tsconfig.json b/packages/remark-lint-definition-spacing/tsconfig.json new file mode 100644 index 00000000..7e61871a --- /dev/null +++ b/packages/remark-lint-definition-spacing/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.json", + "include": ["*.js"] +} diff --git a/packages/remark-lint-emphasis-marker/index.js b/packages/remark-lint-emphasis-marker/index.js index b774d4ff..13eef19a 100644 --- a/packages/remark-lint-emphasis-marker/index.js +++ b/packages/remark-lint-emphasis-marker/index.js @@ -69,41 +69,40 @@ * 1:1: Incorrect emphasis marker `💩`: use either `'consistent'`, `'*'`, or `'_'` */ +/** + * @typedef {import('mdast').Root} Root + * @typedef {'*'|'_'} Marker + * @typedef {'consistent'|Marker} Options + */ + import {lintRule} from 'unified-lint-rule' import {visit} from 'unist-util-visit' import {pointStart} from 'unist-util-position' -import {generated} from 'unist-util-generated' - -const markers = {null: true, '*': true, _: true} const remarkLintEmphasisMarker = lintRule( 'remark-lint:emphasis-marker', - (tree, file, option) => { + /** @type {import('unified-lint-rule').Rule} */ + (tree, file, option = 'consistent') => { const value = String(file) - let preferred = - typeof option === 'string' && option !== 'consistent' ? option : null - if (markers[preferred] !== true) { + if (option !== '*' && option !== '_' && option !== 'consistent') { file.fail( 'Incorrect emphasis marker `' + - preferred + + option + "`: use either `'consistent'`, `'*'`, or `'_'`" ) } visit(tree, 'emphasis', (node) => { - if (!generated(node)) { - const marker = value.charAt(pointStart(node).offset) + const start = pointStart(node).offset + + if (typeof start === 'number') { + const marker = /** @type {Marker} */ (value.charAt(start)) - if (preferred) { - if (marker !== preferred) { - file.message( - 'Emphasis should use `' + preferred + '` as a marker', - node - ) - } - } else { - preferred = marker + if (option === 'consistent') { + option = marker + } else if (marker !== option) { + file.message('Emphasis should use `' + option + '` as a marker', node) } } }) diff --git a/packages/remark-lint-emphasis-marker/package.json b/packages/remark-lint-emphasis-marker/package.json index 98d3da1a..45f57acd 100644 --- a/packages/remark-lint-emphasis-marker/package.json +++ b/packages/remark-lint-emphasis-marker/package.json @@ -24,14 +24,26 @@ "sideEffects": false, "type": "module", "main": "index.js", + "types": "index.d.ts", "files": [ + "index.d.ts", "index.js" ], "dependencies": { + "@types/mdast": "^3.0.0", + "unified": "^10.0.0", "unified-lint-rule": "^1.0.0", - "unist-util-generated": "^2.0.0", "unist-util-position": "^4.0.0", "unist-util-visit": "^4.0.0" }, - "xo": false + "scripts": { + "build": "rimraf \"*.d.ts\" && tsc && type-coverage" + }, + "xo": false, + "typeCoverage": { + "atLeast": 100, + "detail": true, + "strict": true, + "ignoreCatch": true + } } diff --git a/packages/remark-lint-emphasis-marker/readme.md b/packages/remark-lint-emphasis-marker/readme.md index 61200639..f3892b10 100644 --- a/packages/remark-lint-emphasis-marker/readme.md +++ b/packages/remark-lint-emphasis-marker/readme.md @@ -126,12 +126,18 @@ When configured with `'💩'`. ## Install +This package is [ESM only][esm]: +Node 12+ is needed to use it and it must be `imported`ed instead of `required`d. + [npm][]: ```sh npm install remark-lint-emphasis-marker ``` +This package exports no identifiers. +The default export is `remarkLintEmphasisMarker`. + ## Use You probably want to use it on the CLI through a config file: @@ -212,6 +218,8 @@ abide by its terms. [chat]: https://github.com/remarkjs/remark/discussions +[esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c + [npm]: https://docs.npmjs.com/cli/install [health]: https://github.com/remarkjs/.github diff --git a/packages/remark-lint-emphasis-marker/tsconfig.json b/packages/remark-lint-emphasis-marker/tsconfig.json new file mode 100644 index 00000000..7e61871a --- /dev/null +++ b/packages/remark-lint-emphasis-marker/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.json", + "include": ["*.js"] +} diff --git a/packages/remark-lint-fenced-code-flag/index.js b/packages/remark-lint-fenced-code-flag/index.js index a6953bf6..204be7c2 100644 --- a/packages/remark-lint-fenced-code-flag/index.js +++ b/packages/remark-lint-fenced-code-flag/index.js @@ -61,6 +61,13 @@ * ``` * * @example + * {"name": "ok.md", "setting": {"flags":["alpha"]}} + * + * ```alpha + * bravo() + * ``` + * + * @example * {"name": "not-ok.md", "setting": ["charlie"], "label": "input"} * * ```alpha @@ -73,6 +80,18 @@ * 1:1-3:4: Incorrect code language flag */ +/** + * @typedef {import('mdast').Root} Root + * + * @typedef {string[]} Flags + * + * @typedef FlagMap + * @property {Flags} [flags] + * @property {boolean} [allowEmpty=false] + * + * @typedef {Flags|FlagMap} Options + */ + import {lintRule} from 'unified-lint-rule' import {visit} from 'unist-util-visit' import {pointStart, pointEnd} from 'unist-util-position' @@ -82,19 +101,23 @@ const fence = /^ {0,3}([~`])\1{2,}/ const remarkLintFencedCodeFlag = lintRule( 'remark-lint:fenced-code-flag', + /** @type {import('unified-lint-rule').Rule} */ (tree, file, option) => { const value = String(file) let allowEmpty = false + /** @type {string[]} */ let allowed = [] - let flags = option - if (typeof flags === 'object' && !Array.isArray(flags)) { - allowEmpty = Boolean(flags.allowEmpty) - flags = flags.flags - } + if (typeof option === 'object') { + if (Array.isArray(option)) { + allowed = option + } else { + allowEmpty = Boolean(option.allowEmpty) - if (Array.isArray(flags)) { - allowed = String(flags).split(',') + if (option.flags) { + allowed = option.flags + } + } } visit(tree, 'code', (node) => { diff --git a/packages/remark-lint-fenced-code-flag/package.json b/packages/remark-lint-fenced-code-flag/package.json index 19daa884..d079e02a 100644 --- a/packages/remark-lint-fenced-code-flag/package.json +++ b/packages/remark-lint-fenced-code-flag/package.json @@ -26,14 +26,27 @@ "sideEffects": false, "type": "module", "main": "index.js", + "types": "index.d.ts", "files": [ + "index.d.ts", "index.js" ], "dependencies": { + "@types/mdast": "^3.0.0", + "unified": "^10.0.0", "unified-lint-rule": "^1.0.0", "unist-util-generated": "^2.0.0", "unist-util-position": "^4.0.0", "unist-util-visit": "^4.0.0" }, - "xo": false + "scripts": { + "build": "rimraf \"*.d.ts\" && tsc && type-coverage" + }, + "xo": false, + "typeCoverage": { + "atLeast": 100, + "detail": true, + "strict": true, + "ignoreCatch": true + } } diff --git a/packages/remark-lint-fenced-code-flag/readme.md b/packages/remark-lint-fenced-code-flag/readme.md index 7a1b8469..5fb5d3c1 100644 --- a/packages/remark-lint-fenced-code-flag/readme.md +++ b/packages/remark-lint-fenced-code-flag/readme.md @@ -111,6 +111,22 @@ bravo() No messages. +##### `ok.md` + +When configured with `{ flags: [ 'alpha' ] }`. + +###### In + +````markdown +```alpha +bravo() +``` +```` + +###### Out + +No messages. + ##### `not-ok.md` When configured with `[ 'charlie' ]`. @@ -131,12 +147,18 @@ bravo() ## Install +This package is [ESM only][esm]: +Node 12+ is needed to use it and it must be `imported`ed instead of `required`d. + [npm][]: ```sh npm install remark-lint-fenced-code-flag ``` +This package exports no identifiers. +The default export is `remarkLintFencedCodeFlag`. + ## Use You probably want to use it on the CLI through a config file: @@ -217,6 +239,8 @@ abide by its terms. [chat]: https://github.com/remarkjs/remark/discussions +[esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c + [npm]: https://docs.npmjs.com/cli/install [health]: https://github.com/remarkjs/.github diff --git a/packages/remark-lint-fenced-code-flag/tsconfig.json b/packages/remark-lint-fenced-code-flag/tsconfig.json new file mode 100644 index 00000000..7e61871a --- /dev/null +++ b/packages/remark-lint-fenced-code-flag/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.json", + "include": ["*.js"] +} diff --git a/packages/remark-lint-fenced-code-marker/index.js b/packages/remark-lint-fenced-code-marker/index.js index 8c43c8e3..0b6ad6ea 100644 --- a/packages/remark-lint-fenced-code-marker/index.js +++ b/packages/remark-lint-fenced-code-marker/index.js @@ -89,53 +89,50 @@ * 1:1: Incorrect fenced code marker `💩`: use either `'consistent'`, `` '`' ``, or `'~'` */ +/** + * @typedef {import('mdast').Root} Root + * @typedef {'~'|'`'} Marker + * @typedef {'consistent'|Marker} Options + */ + import {lintRule} from 'unified-lint-rule' import {visit} from 'unist-util-visit' import {pointStart} from 'unist-util-position' -import {generated} from 'unist-util-generated' - -const markers = { - '`': true, - '~': true, - null: true -} const remarkLintFencedCodeMarker = lintRule( 'remark-lint:fenced-code-marker', - (tree, file, option) => { + /** @type {import('unified-lint-rule').Rule} */ + (tree, file, option = 'consistent') => { const contents = String(file) - let preferred = - typeof option === 'string' && option !== 'consistent' ? option : null - if (markers[preferred] !== true) { + if (option !== 'consistent' && option !== '~' && option !== '`') { file.fail( 'Incorrect fenced code marker `' + - preferred + + option + "`: use either `'consistent'`, `` '`' ``, or `'~'`" ) } visit(tree, 'code', (node) => { - if (!generated(node)) { - const start = pointStart(node).offset + const start = pointStart(node).offset + + if (typeof start === 'number') { const marker = contents .slice(start, start + 4) .replace(/^\s+/, '') .charAt(0) // Ignore unfenced code blocks. - if (markers[marker] === true) { - if (preferred) { - if (marker !== preferred) { - file.message( - 'Fenced code should use `' + - (preferred === '~' ? preferred : '` ` `') + - '` as a marker', - node - ) - } - } else { - preferred = marker + if (marker === '~' || marker === '`') { + if (option === 'consistent') { + option = marker + } else if (marker !== option) { + file.message( + 'Fenced code should use `' + + (option === '~' ? option : '` ` `') + + '` as a marker', + node + ) } } } diff --git a/packages/remark-lint-fenced-code-marker/package.json b/packages/remark-lint-fenced-code-marker/package.json index c938c385..8a9f82f4 100644 --- a/packages/remark-lint-fenced-code-marker/package.json +++ b/packages/remark-lint-fenced-code-marker/package.json @@ -25,14 +25,26 @@ "sideEffects": false, "type": "module", "main": "index.js", + "types": "index.d.ts", "files": [ + "index.d.ts", "index.js" ], "dependencies": { + "@types/mdast": "^3.0.0", + "unified": "^10.0.0", "unified-lint-rule": "^1.0.0", - "unist-util-generated": "^2.0.0", "unist-util-position": "^4.0.0", "unist-util-visit": "^4.0.0" }, - "xo": false + "scripts": { + "build": "rimraf \"*.d.ts\" && tsc && type-coverage" + }, + "xo": false, + "typeCoverage": { + "atLeast": 100, + "detail": true, + "strict": true, + "ignoreCatch": true + } } diff --git a/packages/remark-lint-fenced-code-marker/readme.md b/packages/remark-lint-fenced-code-marker/readme.md index 2f70a3e3..85556fa8 100644 --- a/packages/remark-lint-fenced-code-marker/readme.md +++ b/packages/remark-lint-fenced-code-marker/readme.md @@ -145,12 +145,18 @@ When configured with `'💩'`. ## Install +This package is [ESM only][esm]: +Node 12+ is needed to use it and it must be `imported`ed instead of `required`d. + [npm][]: ```sh npm install remark-lint-fenced-code-marker ``` +This package exports no identifiers. +The default export is `remarkLintFencedCodeMarker`. + ## Use You probably want to use it on the CLI through a config file: @@ -231,6 +237,8 @@ abide by its terms. [chat]: https://github.com/remarkjs/remark/discussions +[esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c + [npm]: https://docs.npmjs.com/cli/install [health]: https://github.com/remarkjs/.github diff --git a/packages/remark-lint-fenced-code-marker/tsconfig.json b/packages/remark-lint-fenced-code-marker/tsconfig.json new file mode 100644 index 00000000..7e61871a --- /dev/null +++ b/packages/remark-lint-fenced-code-marker/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.json", + "include": ["*.js"] +} diff --git a/packages/remark-lint-file-extension/index.js b/packages/remark-lint-file-extension/index.js index e5ed5de9..fdeec71c 100644 --- a/packages/remark-lint-file-extension/index.js +++ b/packages/remark-lint-file-extension/index.js @@ -26,11 +26,17 @@ * {"name": "readme.mkd", "setting": "mkd"} */ +/** + * @typedef {import('mdast').Root} Root + * @typedef {string} Options + */ + import {lintRule} from 'unified-lint-rule' const remarkLintFileExtension = lintRule( 'remark-lint:file-extension', - (tree, file, option) => { + /** @type {import('unified-lint-rule').Rule} */ + (_, file, option) => { const ext = file.extname const preferred = typeof option === 'string' ? option : 'md' diff --git a/packages/remark-lint-file-extension/package.json b/packages/remark-lint-file-extension/package.json index 12886e94..0d3286d4 100644 --- a/packages/remark-lint-file-extension/package.json +++ b/packages/remark-lint-file-extension/package.json @@ -25,11 +25,24 @@ "sideEffects": false, "type": "module", "main": "index.js", + "types": "index.d.ts", "files": [ + "index.d.ts", "index.js" ], "dependencies": { + "@types/mdast": "^3.0.0", + "unified": "^10.0.0", "unified-lint-rule": "^1.0.0" }, - "xo": false + "scripts": { + "build": "rimraf \"*.d.ts\" && tsc && type-coverage" + }, + "xo": false, + "typeCoverage": { + "atLeast": 100, + "detail": true, + "strict": true, + "ignoreCatch": true + } } diff --git a/packages/remark-lint-file-extension/readme.md b/packages/remark-lint-file-extension/readme.md index 8ea1979d..9aca8f0f 100644 --- a/packages/remark-lint-file-extension/readme.md +++ b/packages/remark-lint-file-extension/readme.md @@ -57,12 +57,18 @@ No messages. ## Install +This package is [ESM only][esm]: +Node 12+ is needed to use it and it must be `imported`ed instead of `required`d. + [npm][]: ```sh npm install remark-lint-file-extension ``` +This package exports no identifiers. +The default export is `remarkLintFileExtension`. + ## Use You probably want to use it on the CLI through a config file: @@ -143,6 +149,8 @@ abide by its terms. [chat]: https://github.com/remarkjs/remark/discussions +[esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c + [npm]: https://docs.npmjs.com/cli/install [health]: https://github.com/remarkjs/.github diff --git a/packages/remark-lint-file-extension/tsconfig.json b/packages/remark-lint-file-extension/tsconfig.json new file mode 100644 index 00000000..7e61871a --- /dev/null +++ b/packages/remark-lint-file-extension/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.json", + "include": ["*.js"] +} diff --git a/packages/remark-lint-final-definition/index.js b/packages/remark-lint-final-definition/index.js index 86417648..833292c3 100644 --- a/packages/remark-lint-final-definition/index.js +++ b/packages/remark-lint-final-definition/index.js @@ -40,6 +40,10 @@ * [example-2]: http://example.com/two/ */ +/** + * @typedef {import('mdast').Root} Root + */ + import {lintRule} from 'unified-lint-rule' import {visit} from 'unist-util-visit' import {pointStart} from 'unist-util-position' @@ -47,8 +51,9 @@ import {generated} from 'unist-util-generated' const remarkLintFinalDefinition = lintRule( 'remark-lint:final-definition', + /** @type {import('unified-lint-rule').Rule} */ (tree, file) => { - let last = null + let last = 0 visit( tree, @@ -65,7 +70,7 @@ const remarkLintFinalDefinition = lintRule( const line = pointStart(node).line if (node.type === 'definition') { - if (last !== null && last > line) { + if (last && last > line) { file.message( 'Move definitions to the end of the file (after the node at line `' + last + @@ -73,7 +78,7 @@ const remarkLintFinalDefinition = lintRule( node ) } - } else if (last === null) { + } else if (last === 0) { last = line } }, diff --git a/packages/remark-lint-final-definition/package.json b/packages/remark-lint-final-definition/package.json index 665bf74b..17c23efa 100644 --- a/packages/remark-lint-final-definition/package.json +++ b/packages/remark-lint-final-definition/package.json @@ -25,14 +25,27 @@ "sideEffects": false, "type": "module", "main": "index.js", + "types": "index.d.ts", "files": [ + "index.d.ts", "index.js" ], "dependencies": { + "@types/mdast": "^3.0.0", + "unified": "^10.0.0", "unified-lint-rule": "^1.0.0", "unist-util-generated": "^2.0.0", "unist-util-position": "^4.0.0", "unist-util-visit": "^4.0.0" }, - "xo": false + "scripts": { + "build": "rimraf \"*.d.ts\" && tsc && type-coverage" + }, + "xo": false, + "typeCoverage": { + "atLeast": 100, + "detail": true, + "strict": true, + "ignoreCatch": true + } } diff --git a/packages/remark-lint-final-definition/readme.md b/packages/remark-lint-final-definition/readme.md index ac637efa..dde8a829 100644 --- a/packages/remark-lint-final-definition/readme.md +++ b/packages/remark-lint-final-definition/readme.md @@ -75,12 +75,18 @@ No messages. ## Install +This package is [ESM only][esm]: +Node 12+ is needed to use it and it must be `imported`ed instead of `required`d. + [npm][]: ```sh npm install remark-lint-final-definition ``` +This package exports no identifiers. +The default export is `remarkLintFinalDefinition`. + ## Use You probably want to use it on the CLI through a config file: @@ -161,6 +167,8 @@ abide by its terms. [chat]: https://github.com/remarkjs/remark/discussions +[esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c + [npm]: https://docs.npmjs.com/cli/install [health]: https://github.com/remarkjs/.github diff --git a/packages/remark-lint-final-definition/tsconfig.json b/packages/remark-lint-final-definition/tsconfig.json new file mode 100644 index 00000000..7e61871a --- /dev/null +++ b/packages/remark-lint-final-definition/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.json", + "include": ["*.js"] +} diff --git a/packages/remark-lint-final-newline/index.js b/packages/remark-lint-final-newline/index.js index 3bd6d857..b6039903 100644 --- a/packages/remark-lint-final-newline/index.js +++ b/packages/remark-lint-final-newline/index.js @@ -50,11 +50,16 @@ * ``` */ +/** + * @typedef {import('mdast').Root} Root + */ + import {lintRule} from 'unified-lint-rule' const remarkLintFinalNewline = lintRule( 'remark-lint:final-newline', - (tree, file) => { + /** @type {import('unified-lint-rule').Rule} */ + (_, file) => { const value = String(file) const last = value.length - 1 diff --git a/packages/remark-lint-final-newline/package.json b/packages/remark-lint-final-newline/package.json index cbcc6fb1..5d09bb87 100644 --- a/packages/remark-lint-final-newline/package.json +++ b/packages/remark-lint-final-newline/package.json @@ -25,11 +25,24 @@ "sideEffects": false, "type": "module", "main": "index.js", + "types": "index.d.ts", "files": [ + "index.d.ts", "index.js" ], "dependencies": { + "@types/mdast": "^3.0.0", + "unified": "^10.0.0", "unified-lint-rule": "^1.0.0" }, - "xo": false + "scripts": { + "build": "rimraf \"*.d.ts\" && tsc && type-coverage" + }, + "xo": false, + "typeCoverage": { + "atLeast": 100, + "detail": true, + "strict": true, + "ignoreCatch": true + } } diff --git a/packages/remark-lint-final-newline/readme.md b/packages/remark-lint-final-newline/readme.md index 198cf452..5c89acf0 100644 --- a/packages/remark-lint-final-newline/readme.md +++ b/packages/remark-lint-final-newline/readme.md @@ -65,12 +65,18 @@ This rule is included in the following presets: ## Install +This package is [ESM only][esm]: +Node 12+ is needed to use it and it must be `imported`ed instead of `required`d. + [npm][]: ```sh npm install remark-lint-final-newline ``` +This package exports no identifiers. +The default export is `remarkLintFinalNewline`. + ## Use You probably want to use it on the CLI through a config file: @@ -151,6 +157,8 @@ abide by its terms. [chat]: https://github.com/remarkjs/remark/discussions +[esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c + [npm]: https://docs.npmjs.com/cli/install [health]: https://github.com/remarkjs/.github diff --git a/packages/remark-lint-final-newline/tsconfig.json b/packages/remark-lint-final-newline/tsconfig.json new file mode 100644 index 00000000..7e61871a --- /dev/null +++ b/packages/remark-lint-final-newline/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.json", + "include": ["*.js"] +} diff --git a/packages/remark-lint-first-heading-level/index.js b/packages/remark-lint-first-heading-level/index.js index 9c0d4848..0d93a1bd 100644 --- a/packages/remark-lint-first-heading-level/index.js +++ b/packages/remark-lint-first-heading-level/index.js @@ -90,6 +90,13 @@ * 1:1-1:14: First heading level should be `2` */ +/** + * @typedef {import('mdast').Root} Root + * @typedef {import('mdast').HTML} HTML + * @typedef {import('mdast').Heading['depth']} Depth + * @typedef {Depth} Options + */ + import {lintRule} from 'unified-lint-rule' import {visit, EXIT} from 'unist-util-visit' import {generated} from 'unist-util-generated' @@ -98,11 +105,11 @@ const re = / { - const preferred = option && option !== true ? option : 1 - + /** @type {import('unified-lint-rule').Rule} */ + (tree, file, option = 1) => { visit(tree, (node) => { if (!generated(node)) { + /** @type {Depth|undefined} */ let rank if (node.type === 'heading') { @@ -112,11 +119,8 @@ const remarkLintFirstHeadingLevel = lintRule( } if (rank !== undefined) { - if (rank !== preferred) { - file.message( - 'First heading level should be `' + preferred + '`', - node - ) + if (rank !== option) { + file.message('First heading level should be `' + option + '`', node) } return EXIT @@ -128,7 +132,12 @@ const remarkLintFirstHeadingLevel = lintRule( export default remarkLintFirstHeadingLevel +/** + * @param {HTML} node + * @returns {Depth|undefined} + */ function infer(node) { const results = node.value.match(re) + // @ts-expect-error: can be castes fine. return results ? Number(results[1]) : undefined } diff --git a/packages/remark-lint-first-heading-level/package.json b/packages/remark-lint-first-heading-level/package.json index a8777a6d..c7578e30 100644 --- a/packages/remark-lint-first-heading-level/package.json +++ b/packages/remark-lint-first-heading-level/package.json @@ -26,13 +26,26 @@ "sideEffects": false, "type": "module", "main": "index.js", + "types": "index.d.ts", "files": [ + "index.d.ts", "index.js" ], "dependencies": { + "@types/mdast": "^3.0.0", + "unified": "^10.0.0", "unified-lint-rule": "^1.0.0", "unist-util-generated": "^2.0.0", "unist-util-visit": "^4.0.0" }, - "xo": false + "scripts": { + "build": "rimraf \"*.d.ts\" && tsc && type-coverage" + }, + "xo": false, + "typeCoverage": { + "atLeast": 100, + "detail": true, + "strict": true, + "ignoreCatch": true + } } diff --git a/packages/remark-lint-first-heading-level/readme.md b/packages/remark-lint-first-heading-level/readme.md index 95d0653a..f7a237c3 100644 --- a/packages/remark-lint-first-heading-level/readme.md +++ b/packages/remark-lint-first-heading-level/readme.md @@ -162,12 +162,18 @@ Paragraph. ## Install +This package is [ESM only][esm]: +Node 12+ is needed to use it and it must be `imported`ed instead of `required`d. + [npm][]: ```sh npm install remark-lint-first-heading-level ``` +This package exports no identifiers. +The default export is `remarkLintFirstHeadingLevel`. + ## Use You probably want to use it on the CLI through a config file: @@ -248,6 +254,8 @@ abide by its terms. [chat]: https://github.com/remarkjs/remark/discussions +[esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c + [npm]: https://docs.npmjs.com/cli/install [health]: https://github.com/remarkjs/.github diff --git a/packages/remark-lint-first-heading-level/tsconfig.json b/packages/remark-lint-first-heading-level/tsconfig.json new file mode 100644 index 00000000..7e61871a --- /dev/null +++ b/packages/remark-lint-first-heading-level/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.json", + "include": ["*.js"] +} diff --git a/packages/remark-lint-hard-break-spaces/index.js b/packages/remark-lint-hard-break-spaces/index.js index ffb68b54..3b8cf438 100644 --- a/packages/remark-lint-hard-break-spaces/index.js +++ b/packages/remark-lint-hard-break-spaces/index.js @@ -24,6 +24,10 @@ * 1:12-2:1: Use two spaces for hard line breaks */ +/** + * @typedef {import('mdast').Root} Root + */ + import {lintRule} from 'unified-lint-rule' import {visit} from 'unist-util-visit' import {pointStart, pointEnd} from 'unist-util-position' @@ -31,6 +35,7 @@ import {generated} from 'unist-util-generated' const remarkLintHardBreakSpaces = lintRule( 'remark-lint:hard-break-spaces', + /** @type {import('unified-lint-rule').Rule} */ (tree, file) => { const value = String(file) diff --git a/packages/remark-lint-hard-break-spaces/package.json b/packages/remark-lint-hard-break-spaces/package.json index d2d224d7..81f33a56 100644 --- a/packages/remark-lint-hard-break-spaces/package.json +++ b/packages/remark-lint-hard-break-spaces/package.json @@ -26,14 +26,27 @@ "sideEffects": false, "type": "module", "main": "index.js", + "types": "index.d.ts", "files": [ + "index.d.ts", "index.js" ], "dependencies": { + "@types/mdast": "^3.0.0", + "unified": "^10.0.0", "unified-lint-rule": "^1.0.0", "unist-util-generated": "^2.0.0", "unist-util-position": "^4.0.0", "unist-util-visit": "^4.0.0" }, - "xo": false + "scripts": { + "build": "rimraf \"*.d.ts\" && tsc && type-coverage" + }, + "xo": false, + "typeCoverage": { + "atLeast": 100, + "detail": true, + "strict": true, + "ignoreCatch": true + } } diff --git a/packages/remark-lint-hard-break-spaces/readme.md b/packages/remark-lint-hard-break-spaces/readme.md index 49de9e79..c207b259 100644 --- a/packages/remark-lint-hard-break-spaces/readme.md +++ b/packages/remark-lint-hard-break-spaces/readme.md @@ -57,12 +57,18 @@ dolor sit amet. ## Install +This package is [ESM only][esm]: +Node 12+ is needed to use it and it must be `imported`ed instead of `required`d. + [npm][]: ```sh npm install remark-lint-hard-break-spaces ``` +This package exports no identifiers. +The default export is `remarkLintHardBreakSpaces`. + ## Use You probably want to use it on the CLI through a config file: @@ -143,6 +149,8 @@ abide by its terms. [chat]: https://github.com/remarkjs/remark/discussions +[esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c + [npm]: https://docs.npmjs.com/cli/install [health]: https://github.com/remarkjs/.github diff --git a/packages/remark-lint-hard-break-spaces/tsconfig.json b/packages/remark-lint-hard-break-spaces/tsconfig.json new file mode 100644 index 00000000..7e61871a --- /dev/null +++ b/packages/remark-lint-hard-break-spaces/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.json", + "include": ["*.js"] +} diff --git a/packages/remark-lint-heading-increment/index.js b/packages/remark-lint-heading-increment/index.js index 5c89b366..963a36df 100644 --- a/packages/remark-lint-heading-increment/index.js +++ b/packages/remark-lint-heading-increment/index.js @@ -26,13 +26,20 @@ * 3:1-3:10: Heading levels should increment by one level at a time */ +/** + * @typedef {import('mdast').Root} Root + * @typedef {import('mdast').Heading['depth']} Depth + */ + import {lintRule} from 'unified-lint-rule' import {visit} from 'unist-util-visit' import {generated} from 'unist-util-generated' const remarkLintHeadingIncrement = lintRule( 'remark-lint:heading-increment', + /** @type {import('unified-lint-rule').Rule} */ (tree, file) => { + /** @type {Depth} */ let previous visit(tree, 'heading', (node) => { diff --git a/packages/remark-lint-heading-increment/package.json b/packages/remark-lint-heading-increment/package.json index e42c774f..22ae6041 100644 --- a/packages/remark-lint-heading-increment/package.json +++ b/packages/remark-lint-heading-increment/package.json @@ -25,13 +25,26 @@ "sideEffects": false, "type": "module", "main": "index.js", + "types": "index.d.ts", "files": [ + "index.d.ts", "index.js" ], "dependencies": { + "@types/mdast": "^3.0.0", + "unified": "^10.0.0", "unified-lint-rule": "^1.0.0", "unist-util-generated": "^2.0.0", "unist-util-visit": "^4.0.0" }, - "xo": false + "scripts": { + "build": "rimraf \"*.d.ts\" && tsc && type-coverage" + }, + "xo": false, + "typeCoverage": { + "atLeast": 100, + "detail": true, + "strict": true, + "ignoreCatch": true + } } diff --git a/packages/remark-lint-heading-increment/readme.md b/packages/remark-lint-heading-increment/readme.md index 99352ce1..9a3593a7 100644 --- a/packages/remark-lint-heading-increment/readme.md +++ b/packages/remark-lint-heading-increment/readme.md @@ -54,12 +54,18 @@ No messages. ## Install +This package is [ESM only][esm]: +Node 12+ is needed to use it and it must be `imported`ed instead of `required`d. + [npm][]: ```sh npm install remark-lint-heading-increment ``` +This package exports no identifiers. +The default export is `remarkLintHeadingIncrement`. + ## Use You probably want to use it on the CLI through a config file: @@ -140,6 +146,8 @@ abide by its terms. [chat]: https://github.com/remarkjs/remark/discussions +[esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c + [npm]: https://docs.npmjs.com/cli/install [health]: https://github.com/remarkjs/.github diff --git a/packages/remark-lint-heading-increment/tsconfig.json b/packages/remark-lint-heading-increment/tsconfig.json new file mode 100644 index 00000000..7e61871a --- /dev/null +++ b/packages/remark-lint-heading-increment/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.json", + "include": ["*.js"] +} diff --git a/packages/remark-lint-heading-style/index.js b/packages/remark-lint-heading-style/index.js index 9496b662..246c9a16 100644 --- a/packages/remark-lint-heading-style/index.js +++ b/packages/remark-lint-heading-style/index.js @@ -69,6 +69,17 @@ * * 4:1-4:8: Headings should use setext * 6:1-6:13: Headings should use setext + * + * @example + * {"name": "not-ok.md", "setting": "💩", "label": "output", "positionless": true} + * + * 1:1: Incorrect heading style type `💩`: use either `'consistent'`, `'atx'`, `'atx-closed'`, or `'setext'` + */ + +/** + * @typedef {import('mdast').Root} Root + * @typedef {'atx'|'atx-closed'|'setext'} Type + * @typedef {'consistent'|Type} Options */ import {lintRule} from 'unified-lint-rule' @@ -76,21 +87,31 @@ import {visit} from 'unist-util-visit' import {headingStyle} from 'mdast-util-heading-style' import {generated} from 'unist-util-generated' -const types = new Set(['atx', 'atx-closed', 'setext']) - const remarkLintHeadingStyle = lintRule( 'remark-lint:heading-style', - (tree, file, option) => { - let preferred = types.has(option) ? option : null + /** @type {import('unified-lint-rule').Rule} */ + (tree, file, option = 'consistent') => { + if ( + option !== 'consistent' && + option !== 'atx' && + option !== 'atx-closed' && + option !== 'setext' + ) { + file.fail( + 'Incorrect heading style type `' + + option + + "`: use either `'consistent'`, `'atx'`, `'atx-closed'`, or `'setext'`" + ) + } visit(tree, 'heading', (node) => { if (!generated(node)) { - if (preferred) { - if (headingStyle(node, preferred) !== preferred) { - file.message('Headings should use ' + preferred, node) - } - } else { - preferred = headingStyle(node, preferred) + if (option === 'consistent') { + // Funky nodes perhaps cannot be detected. + /* c8 ignore next */ + option = headingStyle(node) || 'consistent' + } else if (headingStyle(node, option) !== option) { + file.message('Headings should use ' + option, node) } } }) diff --git a/packages/remark-lint-heading-style/package.json b/packages/remark-lint-heading-style/package.json index ffc09200..5d222510 100644 --- a/packages/remark-lint-heading-style/package.json +++ b/packages/remark-lint-heading-style/package.json @@ -26,14 +26,27 @@ "sideEffects": false, "type": "module", "main": "index.js", + "types": "index.d.ts", "files": [ + "index.d.ts", "index.js" ], "dependencies": { + "@types/mdast": "^3.0.0", "mdast-util-heading-style": "^2.0.0", + "unified": "^10.0.0", "unified-lint-rule": "^1.0.0", "unist-util-generated": "^2.0.0", "unist-util-visit": "^4.0.0" }, - "xo": false + "scripts": { + "build": "rimraf \"*.d.ts\" && tsc && type-coverage" + }, + "xo": false, + "typeCoverage": { + "atLeast": 100, + "detail": true, + "strict": true, + "ignoreCatch": true + } } diff --git a/packages/remark-lint-heading-style/readme.md b/packages/remark-lint-heading-style/readme.md index 538a8f56..680df304 100644 --- a/packages/remark-lint-heading-style/readme.md +++ b/packages/remark-lint-heading-style/readme.md @@ -118,14 +118,30 @@ Juliett 6:1-6:13: Headings should use setext ``` +##### `not-ok.md` + +When configured with `'💩'`. + +###### Out + +```text +1:1: Incorrect heading style type `💩`: use either `'consistent'`, `'atx'`, `'atx-closed'`, or `'setext'` +``` + ## Install +This package is [ESM only][esm]: +Node 12+ is needed to use it and it must be `imported`ed instead of `required`d. + [npm][]: ```sh npm install remark-lint-heading-style ``` +This package exports no identifiers. +The default export is `remarkLintHeadingStyle`. + ## Use You probably want to use it on the CLI through a config file: @@ -206,6 +222,8 @@ abide by its terms. [chat]: https://github.com/remarkjs/remark/discussions +[esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c + [npm]: https://docs.npmjs.com/cli/install [health]: https://github.com/remarkjs/.github diff --git a/packages/remark-lint-heading-style/tsconfig.json b/packages/remark-lint-heading-style/tsconfig.json new file mode 100644 index 00000000..7e61871a --- /dev/null +++ b/packages/remark-lint-heading-style/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.json", + "include": ["*.js"] +} diff --git a/packages/remark-lint-linebreak-style/index.js b/packages/remark-lint-linebreak-style/index.js index 8a5e4ad7..1c678a27 100644 --- a/packages/remark-lint-linebreak-style/index.js +++ b/packages/remark-lint-linebreak-style/index.js @@ -51,41 +51,43 @@ * 1:6: Expected linebreaks to be windows (`\r\n`), not unix (`\n`) */ +/** + * @typedef {import('mdast').Root} Root + * @typedef {'unix'|'windows'} Type + * @typedef {'consistent'|Type} Options + */ + import {lintRule} from 'unified-lint-rule' import {location} from 'vfile-location' const escaped = {unix: '\\n', windows: '\\r\\n'} -const types = {true: 'windows', false: 'unix'} const remarkLintLinebreakStyle = lintRule( 'remark-lint:linebreak-style', - (tree, file, option) => { + /** @type {import('unified-lint-rule').Rule} */ + (_, file, option = 'consistent') => { const value = String(file) const toPoint = location(value).toPoint - let preferred = - typeof option === 'string' && option !== 'consistent' ? option : null let index = value.indexOf('\n') while (index !== -1) { - const type = types[value.charAt(index - 1) === '\r'] + const type = value.charAt(index - 1) === '\r' ? 'windows' : 'unix' - if (preferred) { - if (preferred !== type) { - file.message( - 'Expected linebreaks to be ' + - preferred + - ' (`' + - escaped[preferred] + - '`), not ' + - type + - ' (`' + - escaped[type] + - '`)', - toPoint(index) - ) - } - } else { - preferred = type + if (option === 'consistent') { + option = type + } else if (option !== type) { + file.message( + 'Expected linebreaks to be ' + + option + + ' (`' + + escaped[option] + + '`), not ' + + type + + ' (`' + + escaped[type] + + '`)', + toPoint(index) + ) } index = value.indexOf('\n', index + 1) diff --git a/packages/remark-lint-linebreak-style/package.json b/packages/remark-lint-linebreak-style/package.json index f776b353..6b5c7d9f 100644 --- a/packages/remark-lint-linebreak-style/package.json +++ b/packages/remark-lint-linebreak-style/package.json @@ -29,12 +29,25 @@ "sideEffects": false, "type": "module", "main": "index.js", + "types": "index.d.ts", "files": [ + "index.d.ts", "index.js" ], "dependencies": { + "@types/mdast": "^3.0.0", + "unified": "^10.0.0", "unified-lint-rule": "^1.0.0", "vfile-location": "^4.0.0" }, - "xo": false + "scripts": { + "build": "rimraf \"*.d.ts\" && tsc && type-coverage" + }, + "xo": false, + "typeCoverage": { + "atLeast": 100, + "detail": true, + "strict": true, + "ignoreCatch": true + } } diff --git a/packages/remark-lint-linebreak-style/readme.md b/packages/remark-lint-linebreak-style/readme.md index 7d88ce80..17a2351a 100644 --- a/packages/remark-lint-linebreak-style/readme.md +++ b/packages/remark-lint-linebreak-style/readme.md @@ -98,12 +98,18 @@ Alpha␊ ## Install +This package is [ESM only][esm]: +Node 12+ is needed to use it and it must be `imported`ed instead of `required`d. + [npm][]: ```sh npm install remark-lint-linebreak-style ``` +This package exports no identifiers. +The default export is `remarkLintLinebreakStyle`. + ## Use You probably want to use it on the CLI through a config file: @@ -184,6 +190,8 @@ abide by its terms. [chat]: https://github.com/remarkjs/remark/discussions +[esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c + [npm]: https://docs.npmjs.com/cli/install [health]: https://github.com/remarkjs/.github diff --git a/packages/remark-lint-linebreak-style/tsconfig.json b/packages/remark-lint-linebreak-style/tsconfig.json new file mode 100644 index 00000000..7e61871a --- /dev/null +++ b/packages/remark-lint-linebreak-style/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.json", + "include": ["*.js"] +} diff --git a/packages/remark-lint-link-title-style/index.js b/packages/remark-lint-link-title-style/index.js index 78168277..d421de8c 100644 --- a/packages/remark-lint-link-title-style/index.js +++ b/packages/remark-lint-link-title-style/index.js @@ -98,11 +98,16 @@ * 1:1: Incorrect link title style marker `💩`: use either `'consistent'`, `'"'`, `'\''`, or `'()'` */ +/** + * @typedef {import('mdast').Root} Root + * @typedef {'"'|"'"|'()'} Marker + * @typedef {'consistent'|Marker} Options + */ + import {lintRule} from 'unified-lint-rule' import {location} from 'vfile-location' import {visit} from 'unist-util-visit' import {pointStart, pointEnd} from 'unist-util-position' -import {generated} from 'unist-util-generated' const own = {}.hasOwnProperty @@ -114,42 +119,48 @@ const markers = { const remarkLintLinkTitleStyle = lintRule( 'remark-lint:link-title-style', - (tree, file, option) => { + /** @type {import('unified-lint-rule').Rule} */ + (tree, file, option = 'consistent') => { const value = String(file) const loc = location(file) - let preferred = - typeof option === 'string' && option !== 'consistent' ? option : null - - if (preferred === '()' || preferred === '(') { - preferred = ')' - } + // @ts-expect-error: allow other paren combos. + let look = option === '()' || option === '(' ? ')' : option - if (preferred && !own.call(markers, preferred)) { + if (look !== 'consistent' && !own.call(markers, look)) { file.fail( 'Incorrect link title style marker `' + - preferred + + look + "`: use either `'consistent'`, `'\"'`, `'\\''`, or `'()'`" ) } visit(tree, (node) => { if ( - (node.type === 'link' || - node.type === 'image' || - node.type === 'definition') && - !generated(node) + node.type === 'link' || + node.type === 'image' || + node.type === 'definition' ) { - const tail = node.children - ? node.children[node.children.length - 1] - : null + const tail = + 'children' in node + ? node.children[node.children.length - 1] + : undefined const begin = tail ? pointEnd(tail) : pointStart(node) - let last = pointEnd(node).offset - 1 + const end = pointEnd(node) + + if ( + typeof begin.offset !== 'number' || + typeof end.offset !== 'number' + ) { + return + } + + let last = end.offset - 1 if (node.type !== 'definition') { last-- } - const final = value.charAt(last) + const final = /** @type {keyof markers} */ (value.charAt(last)) // Exit if the final marker is not a known marker. if (!(final in markers)) { @@ -163,24 +174,22 @@ const remarkLintLinkTitleStyle = lintRule( // Exit if there’s no starting delimiter, the starting delimiter is before // the start of the node, or if it’s not preceded by whitespace. - if (first <= begin || !/\s/.test(value.charAt(first - 1))) { + if (first <= begin.offset || !/\s/.test(value.charAt(first - 1))) { return } - if (preferred) { - if (preferred !== final) { - file.message( - 'Titles should use `' + - (preferred === ')' ? '()' : preferred) + - '` as a quote', - { - start: loc.toPoint(first), - end: loc.toPoint(last + 1) - } - ) - } - } else { - preferred = final + if (look === 'consistent') { + look = final + } else if (look !== final) { + file.message( + 'Titles should use `' + + (look === ')' ? '()' : look) + + '` as a quote', + { + start: loc.toPoint(first), + end: loc.toPoint(last + 1) + } + ) } } }) diff --git a/packages/remark-lint-link-title-style/package.json b/packages/remark-lint-link-title-style/package.json index 1f2f76d8..38e8e5f3 100644 --- a/packages/remark-lint-link-title-style/package.json +++ b/packages/remark-lint-link-title-style/package.json @@ -26,15 +26,27 @@ "sideEffects": false, "type": "module", "main": "index.js", + "types": "index.d.ts", "files": [ + "index.d.ts", "index.js" ], "dependencies": { + "@types/mdast": "^3.0.0", + "unified": "^10.0.0", "unified-lint-rule": "^1.0.0", - "unist-util-generated": "^2.0.0", "unist-util-position": "^4.0.0", "unist-util-visit": "^4.0.0", "vfile-location": "^4.0.0" }, - "xo": false + "scripts": { + "build": "rimraf \"*.d.ts\" && tsc && type-coverage" + }, + "xo": false, + "typeCoverage": { + "atLeast": 100, + "detail": true, + "strict": true, + "ignoreCatch": true + } } diff --git a/packages/remark-lint-link-title-style/readme.md b/packages/remark-lint-link-title-style/readme.md index a0652f68..c6d11e0d 100644 --- a/packages/remark-lint-link-title-style/readme.md +++ b/packages/remark-lint-link-title-style/readme.md @@ -170,12 +170,18 @@ When configured with `'💩'`. ## Install +This package is [ESM only][esm]: +Node 12+ is needed to use it and it must be `imported`ed instead of `required`d. + [npm][]: ```sh npm install remark-lint-link-title-style ``` +This package exports no identifiers. +The default export is `remarkLintLinkTitleStyle`. + ## Use You probably want to use it on the CLI through a config file: @@ -256,6 +262,8 @@ abide by its terms. [chat]: https://github.com/remarkjs/remark/discussions +[esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c + [npm]: https://docs.npmjs.com/cli/install [health]: https://github.com/remarkjs/.github diff --git a/packages/remark-lint-link-title-style/tsconfig.json b/packages/remark-lint-link-title-style/tsconfig.json new file mode 100644 index 00000000..7e61871a --- /dev/null +++ b/packages/remark-lint-link-title-style/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.json", + "include": ["*.js"] +} diff --git a/packages/remark-lint-list-item-bullet-indent/index.js b/packages/remark-lint-list-item-bullet-indent/index.js index e4d5e199..7f4188db 100644 --- a/packages/remark-lint-list-item-bullet-indent/index.js +++ b/packages/remark-lint-list-item-bullet-indent/index.js @@ -37,13 +37,17 @@ * 4:2: Incorrect indentation before bullet: remove 1 space */ +/** + * @typedef {import('mdast').Root} Root + */ + import {lintRule} from 'unified-lint-rule' import plural from 'pluralize' import {visit} from 'unist-util-visit' -import {generated} from 'unist-util-generated' const remarkLintListItemBulletIndent = lintRule( 'remark-lint:list-item-bullet-indent', + /** @type {import('unified-lint-rule').Rule} */ (tree, file) => { visit(tree, 'list', (list, _, grandparent) => { let index = -1 @@ -54,8 +58,10 @@ const remarkLintListItemBulletIndent = lintRule( if ( grandparent && grandparent.type === 'root' && - !generated(item) && - !generated(grandparent) + grandparent.position && + typeof grandparent.position.start.column === 'number' && + item.position && + typeof item.position.start.column === 'number' ) { const indent = item.position.start.column - grandparent.position.start.column diff --git a/packages/remark-lint-list-item-bullet-indent/package.json b/packages/remark-lint-list-item-bullet-indent/package.json index 36566714..86ed6e65 100644 --- a/packages/remark-lint-list-item-bullet-indent/package.json +++ b/packages/remark-lint-list-item-bullet-indent/package.json @@ -25,14 +25,26 @@ "sideEffects": false, "type": "module", "main": "index.js", + "types": "index.d.ts", "files": [ + "index.d.ts", "index.js" ], "dependencies": { + "@types/mdast": "^3.0.0", "pluralize": "^8.0.0", + "unified": "^10.0.0", "unified-lint-rule": "^1.0.0", - "unist-util-generated": "^2.0.0", "unist-util-visit": "^4.0.0" }, - "xo": false + "scripts": { + "build": "rimraf \"*.d.ts\" && tsc && type-coverage" + }, + "xo": false, + "typeCoverage": { + "atLeast": 100, + "detail": true, + "strict": true, + "ignoreCatch": true + } } diff --git a/packages/remark-lint-list-item-bullet-indent/readme.md b/packages/remark-lint-list-item-bullet-indent/readme.md index cb1d893f..615757a9 100644 --- a/packages/remark-lint-list-item-bullet-indent/readme.md +++ b/packages/remark-lint-list-item-bullet-indent/readme.md @@ -67,12 +67,18 @@ Paragraph. ## Install +This package is [ESM only][esm]: +Node 12+ is needed to use it and it must be `imported`ed instead of `required`d. + [npm][]: ```sh npm install remark-lint-list-item-bullet-indent ``` +This package exports no identifiers. +The default export is `remarkLintListItemBulletIndent`. + ## Use You probably want to use it on the CLI through a config file: @@ -153,6 +159,8 @@ abide by its terms. [chat]: https://github.com/remarkjs/remark/discussions +[esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c + [npm]: https://docs.npmjs.com/cli/install [health]: https://github.com/remarkjs/.github diff --git a/packages/remark-lint-list-item-bullet-indent/tsconfig.json b/packages/remark-lint-list-item-bullet-indent/tsconfig.json new file mode 100644 index 00000000..7e61871a --- /dev/null +++ b/packages/remark-lint-list-item-bullet-indent/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.json", + "include": ["*.js"] +} diff --git a/packages/remark-lint-list-item-content-indent/index.js b/packages/remark-lint-list-item-content-indent/index.js index 850cdc0e..d35ffc71 100644 --- a/packages/remark-lint-list-item-content-indent/index.js +++ b/packages/remark-lint-list-item-content-indent/index.js @@ -24,31 +24,38 @@ * 2:5: Don’t use mixed indentation for children, remove 1 space */ +/** + * @typedef {import('mdast').Root} Root + */ + import {lintRule} from 'unified-lint-rule' import plural from 'pluralize' import {visit} from 'unist-util-visit' import {pointStart} from 'unist-util-position' -import {generated} from 'unist-util-generated' const remarkLintListItemContentIndent = lintRule( 'remark-lint:list-item-content-indent', + /** @type {import('unified-lint-rule').Rule} */ (tree, file) => { const value = String(file) visit(tree, 'listItem', (node) => { let index = -1 + /** @type {number|undefined} */ let style while (++index < node.children.length) { const item = node.children[index] + const begin = pointStart(item) + let column = begin.column - if (generated(item)) { + if ( + typeof begin.column !== 'number' || + typeof begin.offset !== 'number' + ) { continue } - const begin = pointStart(item) - let column = begin.column - // Get indentation for the first child. Only the first item can have a // checkbox, so here we remove that from the column. if (index === 0) { @@ -70,7 +77,7 @@ const remarkLintListItemContentIndent = lintRule( } // Warn for violating children. - if (column !== style) { + if (style && column !== style) { const diff = style - column const abs = Math.abs(diff) diff --git a/packages/remark-lint-list-item-content-indent/package.json b/packages/remark-lint-list-item-content-indent/package.json index e1d24b7f..a4219d1f 100644 --- a/packages/remark-lint-list-item-content-indent/package.json +++ b/packages/remark-lint-list-item-content-indent/package.json @@ -26,15 +26,27 @@ "sideEffects": false, "type": "module", "main": "index.js", + "types": "index.d.ts", "files": [ + "index.d.ts", "index.js" ], "dependencies": { + "@types/mdast": "^3.0.0", "pluralize": "^8.0.0", + "unified": "^10.0.0", "unified-lint-rule": "^1.0.0", - "unist-util-generated": "^2.0.0", "unist-util-position": "^4.0.0", "unist-util-visit": "^4.0.0" }, - "xo": false + "scripts": { + "build": "rimraf \"*.d.ts\" && tsc && type-coverage" + }, + "xo": false, + "typeCoverage": { + "atLeast": 100, + "detail": true, + "strict": true, + "ignoreCatch": true + } } diff --git a/packages/remark-lint-list-item-content-indent/readme.md b/packages/remark-lint-list-item-content-indent/readme.md index 7c8899bc..27a75520 100644 --- a/packages/remark-lint-list-item-content-indent/readme.md +++ b/packages/remark-lint-list-item-content-indent/readme.md @@ -61,12 +61,18 @@ Note: `·` represents a space. ## Install +This package is [ESM only][esm]: +Node 12+ is needed to use it and it must be `imported`ed instead of `required`d. + [npm][]: ```sh npm install remark-lint-list-item-content-indent ``` +This package exports no identifiers. +The default export is `remarkLintListItemContentIndent`. + ## Use You probably want to use it on the CLI through a config file: @@ -147,6 +153,8 @@ abide by its terms. [chat]: https://github.com/remarkjs/remark/discussions +[esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c + [npm]: https://docs.npmjs.com/cli/install [health]: https://github.com/remarkjs/.github diff --git a/packages/remark-lint-list-item-content-indent/tsconfig.json b/packages/remark-lint-list-item-content-indent/tsconfig.json new file mode 100644 index 00000000..7e61871a --- /dev/null +++ b/packages/remark-lint-list-item-content-indent/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.json", + "include": ["*.js"] +} diff --git a/packages/remark-lint-list-item-indent/index.js b/packages/remark-lint-list-item-indent/index.js index a17ac4d5..1e1a7ce8 100644 --- a/packages/remark-lint-list-item-indent/index.js +++ b/packages/remark-lint-list-item-indent/index.js @@ -113,24 +113,27 @@ * 1:1: Incorrect list-item indent style `💩`: use either `'tab-size'`, `'space'`, or `'mixed'` */ +/** + * @typedef {import('mdast').Root} Root + * @typedef {'tab-size'|'space'|'mixed'} Options + */ + import {lintRule} from 'unified-lint-rule' import plural from 'pluralize' import {visit} from 'unist-util-visit' import {pointStart} from 'unist-util-position' import {generated} from 'unist-util-generated' -const styles = {'tab-size': true, mixed: true, space: true} - const remarkLintListItemIndent = lintRule( 'remark-lint:list-item-indent', - (tree, file, option) => { - const preferred = typeof option === 'string' ? option : 'tab-size' + /** @type {import('unified-lint-rule').Rule} */ + (tree, file, option = 'tab-size') => { const value = String(file) - if (styles[preferred] !== true) { + if (option !== 'tab-size' && option !== 'space' && option !== 'mixed') { file.fail( 'Incorrect list-item indent style `' + - preferred + + option + "`: use either `'tab-size'`, `'space'`, or `'mixed'`" ) } @@ -138,7 +141,8 @@ const remarkLintListItemIndent = lintRule( visit(tree, 'list', (node) => { if (generated(node)) return - const spread = node.spread || node.loose + // @ts-expect-error: legacy. + const spread = Boolean(node.spread || node.loose) let index = -1 while (++index < node.children.length) { @@ -153,7 +157,7 @@ const remarkLintListItemIndent = lintRule( const bulletSize = marker.replace(/\s+$/, '').length const style = - preferred === 'tab-size' || (preferred === 'mixed' && spread) + option === 'tab-size' || (option === 'mixed' && spread) ? Math.ceil(bulletSize / 4) * 4 : bulletSize + 1 diff --git a/packages/remark-lint-list-item-indent/package.json b/packages/remark-lint-list-item-indent/package.json index a79826df..2805e55e 100644 --- a/packages/remark-lint-list-item-indent/package.json +++ b/packages/remark-lint-list-item-indent/package.json @@ -25,15 +25,28 @@ "sideEffects": false, "type": "module", "main": "index.js", + "types": "index.d.ts", "files": [ + "index.d.ts", "index.js" ], "dependencies": { + "@types/mdast": "^3.0.0", "pluralize": "^8.0.0", + "unified": "^10.0.0", "unified-lint-rule": "^1.0.0", "unist-util-generated": "^2.0.0", "unist-util-position": "^4.0.0", "unist-util-visit": "^4.0.0" }, - "xo": false + "scripts": { + "build": "rimraf \"*.d.ts\" && tsc && type-coverage" + }, + "xo": false, + "typeCoverage": { + "atLeast": 100, + "detail": true, + "strict": true, + "ignoreCatch": true + } } diff --git a/packages/remark-lint-list-item-indent/readme.md b/packages/remark-lint-list-item-indent/readme.md index 5a5b059c..e3e8971c 100644 --- a/packages/remark-lint-list-item-indent/readme.md +++ b/packages/remark-lint-list-item-indent/readme.md @@ -191,12 +191,18 @@ When configured with `'💩'`. ## Install +This package is [ESM only][esm]: +Node 12+ is needed to use it and it must be `imported`ed instead of `required`d. + [npm][]: ```sh npm install remark-lint-list-item-indent ``` +This package exports no identifiers. +The default export is `remarkLintListItemIndent`. + ## Use You probably want to use it on the CLI through a config file: @@ -277,6 +283,8 @@ abide by its terms. [chat]: https://github.com/remarkjs/remark/discussions +[esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c + [npm]: https://docs.npmjs.com/cli/install [health]: https://github.com/remarkjs/.github diff --git a/packages/remark-lint-list-item-indent/tsconfig.json b/packages/remark-lint-list-item-indent/tsconfig.json new file mode 100644 index 00000000..7e61871a --- /dev/null +++ b/packages/remark-lint-list-item-indent/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.json", + "include": ["*.js"] +} diff --git a/packages/remark-lint-list-item-spacing/index.js b/packages/remark-lint-list-item-spacing/index.js index 9b3fcd20..3bac1c3a 100644 --- a/packages/remark-lint-list-item-spacing/index.js +++ b/packages/remark-lint-list-item-spacing/index.js @@ -113,6 +113,14 @@ * 14:15-16:1: Extraneous new line after list item */ +/** + * @typedef {import('mdast').Root} Root + * @typedef {import('mdast').ListItem} ListItem + * + * @typedef Options + * @property {boolean} [checkBlanks=false] + */ + import {lintRule} from 'unified-lint-rule' import {visit} from 'unist-util-visit' import {pointStart, pointEnd} from 'unist-util-position' @@ -120,6 +128,7 @@ import {generated} from 'unist-util-generated' const remarkLintListItemSpacing = lintRule( 'remark-lint:list-item-spacing', + /** @type {import('unified-lint-rule').Rule} */ (tree, file, option = {}) => { const {checkBlanks} = option const infer = checkBlanks ? inferBlankLine : inferMultiline @@ -158,6 +167,10 @@ const remarkLintListItemSpacing = lintRule( export default remarkLintListItemSpacing +/** + * @param {ListItem} node + * @returns {boolean} + */ function inferBlankLine(node) { let index = 0 @@ -174,6 +187,10 @@ function inferBlankLine(node) { return false } +/** + * @param {ListItem} node + * @returns {boolean} + */ function inferMultiline(node) { return ( pointEnd(node.children[node.children.length - 1]).line - diff --git a/packages/remark-lint-list-item-spacing/package.json b/packages/remark-lint-list-item-spacing/package.json index 71cfbd7b..b850c1d8 100644 --- a/packages/remark-lint-list-item-spacing/package.json +++ b/packages/remark-lint-list-item-spacing/package.json @@ -26,14 +26,27 @@ "sideEffects": false, "type": "module", "main": "index.js", + "types": "index.d.ts", "files": [ + "index.d.ts", "index.js" ], "dependencies": { + "@types/mdast": "^3.0.0", + "unified": "^10.0.0", "unified-lint-rule": "^1.0.0", "unist-util-generated": "^2.0.0", "unist-util-position": "^4.0.0", "unist-util-visit": "^4.0.0" }, - "xo": false + "scripts": { + "build": "rimraf \"*.d.ts\" && tsc && type-coverage" + }, + "xo": false, + "typeCoverage": { + "atLeast": 100, + "detail": true, + "strict": true, + "ignoreCatch": true + } } diff --git a/packages/remark-lint-list-item-spacing/readme.md b/packages/remark-lint-list-item-spacing/readme.md index 09066421..262f0b7a 100644 --- a/packages/remark-lint-list-item-spacing/readme.md +++ b/packages/remark-lint-list-item-spacing/readme.md @@ -156,12 +156,18 @@ A loose list: ## Install +This package is [ESM only][esm]: +Node 12+ is needed to use it and it must be `imported`ed instead of `required`d. + [npm][]: ```sh npm install remark-lint-list-item-spacing ``` +This package exports no identifiers. +The default export is `remarkLintListItemSpacing`. + ## Use You probably want to use it on the CLI through a config file: @@ -242,6 +248,8 @@ abide by its terms. [chat]: https://github.com/remarkjs/remark/discussions +[esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c + [npm]: https://docs.npmjs.com/cli/install [health]: https://github.com/remarkjs/.github diff --git a/packages/remark-lint-list-item-spacing/tsconfig.json b/packages/remark-lint-list-item-spacing/tsconfig.json new file mode 100644 index 00000000..7e61871a --- /dev/null +++ b/packages/remark-lint-list-item-spacing/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.json", + "include": ["*.js"] +} diff --git a/packages/remark-lint-maximum-heading-length/index.js b/packages/remark-lint-maximum-heading-length/index.js index e2fc3856..87018273 100644 --- a/packages/remark-lint-maximum-heading-length/index.js +++ b/packages/remark-lint-maximum-heading-length/index.js @@ -28,6 +28,11 @@ * 1:1-1:52: Use headings shorter than `40` */ +/** + * @typedef {import('mdast').Root} Root + * @typedef {number} Options + */ + import {lintRule} from 'unified-lint-rule' import {visit} from 'unist-util-visit' import {generated} from 'unist-util-generated' @@ -35,12 +40,11 @@ import {toString} from 'mdast-util-to-string' const remarkLintMaximumHeadingLength = lintRule( 'remark-lint:maximum-heading-length', - (tree, file, option) => { - const preferred = typeof option === 'number' ? option : 60 - + /** @type {import('unified-lint-rule').Rule} */ + (tree, file, option = 60) => { visit(tree, 'heading', (node) => { - if (!generated(node) && toString(node).length > preferred) { - file.message('Use headings shorter than `' + preferred + '`', node) + if (!generated(node) && toString(node).length > option) { + file.message('Use headings shorter than `' + option + '`', node) } }) } diff --git a/packages/remark-lint-maximum-heading-length/package.json b/packages/remark-lint-maximum-heading-length/package.json index 4bd151e5..c66d4756 100644 --- a/packages/remark-lint-maximum-heading-length/package.json +++ b/packages/remark-lint-maximum-heading-length/package.json @@ -24,14 +24,27 @@ "sideEffects": false, "type": "module", "main": "index.js", + "types": "index.d.ts", "files": [ + "index.d.ts", "index.js" ], "dependencies": { + "@types/mdast": "^3.0.0", "mdast-util-to-string": "^3.0.0", + "unified": "^10.0.0", "unified-lint-rule": "^1.0.0", "unist-util-generated": "^2.0.0", "unist-util-visit": "^4.0.0" }, - "xo": false + "scripts": { + "build": "rimraf \"*.d.ts\" && tsc && type-coverage" + }, + "xo": false, + "typeCoverage": { + "atLeast": 100, + "detail": true, + "strict": true, + "ignoreCatch": true + } } diff --git a/packages/remark-lint-maximum-heading-length/readme.md b/packages/remark-lint-maximum-heading-length/readme.md index d13f01ae..109f9b8a 100644 --- a/packages/remark-lint-maximum-heading-length/readme.md +++ b/packages/remark-lint-maximum-heading-length/readme.md @@ -58,12 +58,18 @@ No messages. ## Install +This package is [ESM only][esm]: +Node 12+ is needed to use it and it must be `imported`ed instead of `required`d. + [npm][]: ```sh npm install remark-lint-maximum-heading-length ``` +This package exports no identifiers. +The default export is `remarkLintMaximumHeadingLength`. + ## Use You probably want to use it on the CLI through a config file: @@ -144,6 +150,8 @@ abide by its terms. [chat]: https://github.com/remarkjs/remark/discussions +[esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c + [npm]: https://docs.npmjs.com/cli/install [health]: https://github.com/remarkjs/.github diff --git a/packages/remark-lint-maximum-heading-length/tsconfig.json b/packages/remark-lint-maximum-heading-length/tsconfig.json new file mode 100644 index 00000000..7e61871a --- /dev/null +++ b/packages/remark-lint-maximum-heading-length/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.json", + "include": ["*.js"] +} diff --git a/packages/remark-lint-maximum-line-length/index.js b/packages/remark-lint-maximum-line-length/index.js index 7c3cb233..57621b43 100644 --- a/packages/remark-lint-maximum-line-length/index.js +++ b/packages/remark-lint-maximum-line-length/index.js @@ -96,6 +96,12 @@ * 4:12: Line must be at most 10 characters */ +/** + * @typedef {import('mdast').Root} Root + * @typedef {import('mdast').Parent} Parent + * @typedef {number} Options + */ + import {lintRule} from 'unified-lint-rule' import {visit} from 'unist-util-visit' import {pointStart, pointEnd} from 'unist-util-position' @@ -103,85 +109,93 @@ import {generated} from 'unist-util-generated' const remarkLintMaximumLineLength = lintRule( 'remark-lint:maximum-line-length', - maximumLineLength -) + /** @type {import('unified-lint-rule').Rule} */ + (tree, file, option = 80) => { + const value = String(file) + const lines = value.split(/\r?\n/) -export default remarkLintMaximumLineLength + visit(tree, (node) => { + if ( + (node.type === 'heading' || + node.type === 'table' || + node.type === 'code' || + node.type === 'definition' || + node.type === 'html' || + // @ts-expect-error: JSX is from MDX: . + node.type === 'jsx' || + node.type === 'yaml' || + // @ts-expect-error: TOML is from frontmatter. + node.type === 'toml') && + !generated(node) + ) { + allowList(pointStart(node).line - 1, pointEnd(node).line) + } + }) -function maximumLineLength(tree, file, option) { - const preferred = typeof option === 'number' ? option : 80 - const value = String(file) - const lines = value.split(/\r?\n/) + // Finally, allow some inline spans, but only if they occur at or after + // the wrap. + // However, when they do, and there’s whitespace after it, they are not + // allowed. + visit(tree, (node, pos, parent_) => { + const parent = /** @type {Parent} */ (parent_) - visit(tree, (node) => { - // Note: JSX is from MDX: . - if ( - (node.type === 'heading' || - node.type === 'table' || - node.type === 'code' || - node.type === 'definition' || - node.type === 'html' || - node.type === 'jsx' || - node.type === 'yaml' || - node.type === 'toml') && - !generated(node) - ) { - allowList(pointStart(node).line - 1, pointEnd(node).line) - } - }) + if ( + (node.type === 'link' || + node.type === 'image' || + node.type === 'inlineCode') && + !generated(node) && + parent && + typeof pos === 'number' + ) { + const initial = pointStart(node) + const final = pointEnd(node) - // Finally, allow some inline spans, but only if they occur at or after - // the wrap. - // However, when they do, and there’s whitespace after it, they are not - // allowed. - visit(tree, (node, pos, parent) => { - if ( - (node.type === 'link' || - node.type === 'image' || - node.type === 'inlineCode') && - !generated(node) - ) { - const initial = pointStart(node) - const final = pointEnd(node) + // Not allowing when starting after the border, or ending before it. + if (initial.column > option || final.column < option) { + return + } - // Not allowing when starting after the border, or ending before it. - if (initial.column > preferred || final.column < preferred) { - return - } + const next = parent.children[pos + 1] - const next = parent.children[pos + 1] + // Not allowing when there’s whitespace after the link. + if ( + next && + pointStart(next).line === initial.line && + (!('value' in next) || /^(.+?[ \t].+?)/.test(next.value)) + ) { + return + } - // Not allowing when there’s whitespace after the link. - if ( - next && - pointStart(next).line === initial.line && - (!next.value || /^(.+?[ \t].+?)/.test(next.value)) - ) { - return + allowList(initial.line - 1, final.line) } + }) - allowList(initial.line - 1, final.line) - } - }) - - // Iterate over every line, and warn for violating lines. - let index = -1 + // Iterate over every line, and warn for violating lines. + let index = -1 - while (++index < lines.length) { - const lineLength = lines[index].length + while (++index < lines.length) { + const lineLength = lines[index].length - if (lineLength > preferred) { - file.message('Line must be at most ' + preferred + ' characters', { - line: index + 1, - column: lineLength + 1 - }) + if (lineLength > option) { + file.message('Line must be at most ' + option + ' characters', { + line: index + 1, + column: lineLength + 1 + }) + } } - } - // Allowlist from `initial` to `final`, zero-based. - function allowList(initial, final) { - while (initial < final) { - lines[initial++] = '' + /** + * Allowlist from `initial` to `final`, zero-based. + * + * @param {number} initial + * @param {number} final + */ + function allowList(initial, final) { + while (initial < final) { + lines[initial++] = '' + } } } -} +) + +export default remarkLintMaximumLineLength diff --git a/packages/remark-lint-maximum-line-length/package.json b/packages/remark-lint-maximum-line-length/package.json index f369dbfa..b8811f68 100644 --- a/packages/remark-lint-maximum-line-length/package.json +++ b/packages/remark-lint-maximum-line-length/package.json @@ -24,14 +24,27 @@ "sideEffects": false, "type": "module", "main": "index.js", + "types": "index.d.ts", "files": [ + "index.d.ts", "index.js" ], "dependencies": { + "@types/mdast": "^3.0.0", + "unified": "^10.0.0", "unified-lint-rule": "^1.0.0", "unist-util-generated": "^2.0.0", "unist-util-position": "^4.0.0", "unist-util-visit": "^4.0.0" }, - "xo": false + "scripts": { + "build": "rimraf \"*.d.ts\" && tsc && type-coverage" + }, + "xo": false, + "typeCoverage": { + "atLeast": 100, + "detail": true, + "strict": true, + "ignoreCatch": true + } } diff --git a/packages/remark-lint-maximum-line-length/readme.md b/packages/remark-lint-maximum-line-length/readme.md index 3aa61882..4c1ec60c 100644 --- a/packages/remark-lint-maximum-line-length/readme.md +++ b/packages/remark-lint-maximum-line-length/readme.md @@ -151,12 +151,18 @@ No messages. ## Install +This package is [ESM only][esm]: +Node 12+ is needed to use it and it must be `imported`ed instead of `required`d. + [npm][]: ```sh npm install remark-lint-maximum-line-length ``` +This package exports no identifiers. +The default export is `remarkLintMaximumLineLength`. + ## Use You probably want to use it on the CLI through a config file: @@ -237,6 +243,8 @@ abide by its terms. [chat]: https://github.com/remarkjs/remark/discussions +[esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c + [npm]: https://docs.npmjs.com/cli/install [health]: https://github.com/remarkjs/.github diff --git a/packages/remark-lint-maximum-line-length/tsconfig.json b/packages/remark-lint-maximum-line-length/tsconfig.json new file mode 100644 index 00000000..7e61871a --- /dev/null +++ b/packages/remark-lint-maximum-line-length/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.json", + "include": ["*.js"] +} diff --git a/packages/remark-lint-no-auto-link-without-protocol/index.js b/packages/remark-lint-no-auto-link-without-protocol/index.js index 1e2f1425..a3150cef 100644 --- a/packages/remark-lint-no-auto-link-without-protocol/index.js +++ b/packages/remark-lint-no-auto-link-without-protocol/index.js @@ -36,6 +36,10 @@ * 1:1-1:14: All automatic links must start with a protocol */ +/** + * @typedef {import('mdast').Root} Root + */ + import {lintRule} from 'unified-lint-rule' import {visit} from 'unist-util-visit' import {pointStart, pointEnd} from 'unist-util-position' @@ -48,6 +52,7 @@ const protocol = /^[a-z][a-z+.-]+:\/?/i const remarkLintNoAutoLinkWithoutProtocol = lintRule( 'remark-lint:no-auto-link-without-protocol', + /** @type {import('unified-lint-rule').Rule} */ (tree, file) => { visit(tree, 'link', (node) => { if ( diff --git a/packages/remark-lint-no-auto-link-without-protocol/package.json b/packages/remark-lint-no-auto-link-without-protocol/package.json index 9be8aafe..e6d1d72a 100644 --- a/packages/remark-lint-no-auto-link-without-protocol/package.json +++ b/packages/remark-lint-no-auto-link-without-protocol/package.json @@ -25,15 +25,28 @@ "sideEffects": false, "type": "module", "main": "index.js", + "types": "index.d.ts", "files": [ + "index.d.ts", "index.js" ], "dependencies": { + "@types/mdast": "^3.0.0", "mdast-util-to-string": "^3.0.0", + "unified": "^10.0.0", "unified-lint-rule": "^1.0.0", "unist-util-generated": "^2.0.0", "unist-util-position": "^4.0.0", "unist-util-visit": "^4.0.0" }, - "xo": false + "scripts": { + "build": "rimraf \"*.d.ts\" && tsc && type-coverage" + }, + "xo": false, + "typeCoverage": { + "atLeast": 100, + "detail": true, + "strict": true, + "ignoreCatch": true + } } diff --git a/packages/remark-lint-no-auto-link-without-protocol/readme.md b/packages/remark-lint-no-auto-link-without-protocol/readme.md index 55d9ed4d..3bde196b 100644 --- a/packages/remark-lint-no-auto-link-without-protocol/readme.md +++ b/packages/remark-lint-no-auto-link-without-protocol/readme.md @@ -65,12 +65,18 @@ No messages. ## Install +This package is [ESM only][esm]: +Node 12+ is needed to use it and it must be `imported`ed instead of `required`d. + [npm][]: ```sh npm install remark-lint-no-auto-link-without-protocol ``` +This package exports no identifiers. +The default export is `remarkLintNoAutoLinkWithoutProtocol`. + ## Use You probably want to use it on the CLI through a config file: @@ -151,6 +157,8 @@ abide by its terms. [chat]: https://github.com/remarkjs/remark/discussions +[esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c + [npm]: https://docs.npmjs.com/cli/install [health]: https://github.com/remarkjs/.github diff --git a/packages/remark-lint-no-auto-link-without-protocol/tsconfig.json b/packages/remark-lint-no-auto-link-without-protocol/tsconfig.json new file mode 100644 index 00000000..7e61871a --- /dev/null +++ b/packages/remark-lint-no-auto-link-without-protocol/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.json", + "include": ["*.js"] +} diff --git a/packages/remark-lint-no-blockquote-without-marker/index.js b/packages/remark-lint-no-blockquote-without-marker/index.js index e22e168b..05600b5e 100644 --- a/packages/remark-lint-no-blockquote-without-marker/index.js +++ b/packages/remark-lint-no-blockquote-without-marker/index.js @@ -55,6 +55,10 @@ * 3:1: Missing marker in block quote */ +/** + * @typedef {import('mdast').Root} Root + */ + import {lintRule} from 'unified-lint-rule' import {location} from 'vfile-location' import {visit} from 'unist-util-visit' @@ -63,6 +67,7 @@ import {generated} from 'unist-util-generated' const remarkLintNoBlockquoteWithoutMarker = lintRule( 'remark-lint:no-blockquote-without-marker', + /** @type {import('unified-lint-rule').Rule} */ (tree, file) => { const value = String(file) const loc = location(file) diff --git a/packages/remark-lint-no-blockquote-without-marker/package.json b/packages/remark-lint-no-blockquote-without-marker/package.json index c9a71fae..974bd7e8 100644 --- a/packages/remark-lint-no-blockquote-without-marker/package.json +++ b/packages/remark-lint-no-blockquote-without-marker/package.json @@ -25,15 +25,28 @@ "sideEffects": false, "type": "module", "main": "index.js", + "types": "index.d.ts", "files": [ + "index.d.ts", "index.js" ], "dependencies": { + "@types/mdast": "^3.0.0", + "unified": "^10.0.0", "unified-lint-rule": "^1.0.0", "unist-util-generated": "^2.0.0", "unist-util-position": "^4.0.0", "unist-util-visit": "^4.0.0", "vfile-location": "^4.0.0" }, - "xo": false + "scripts": { + "build": "rimraf \"*.d.ts\" && tsc && type-coverage" + }, + "xo": false, + "typeCoverage": { + "atLeast": 100, + "detail": true, + "strict": true, + "ignoreCatch": true + } } diff --git a/packages/remark-lint-no-blockquote-without-marker/readme.md b/packages/remark-lint-no-blockquote-without-marker/readme.md index 08fe4350..e92b484d 100644 --- a/packages/remark-lint-no-blockquote-without-marker/readme.md +++ b/packages/remark-lint-no-blockquote-without-marker/readme.md @@ -99,12 +99,18 @@ Note: `»` represents a tab. ## Install +This package is [ESM only][esm]: +Node 12+ is needed to use it and it must be `imported`ed instead of `required`d. + [npm][]: ```sh npm install remark-lint-no-blockquote-without-marker ``` +This package exports no identifiers. +The default export is `remarkLintNoBlockquoteWithoutMarker`. + ## Use You probably want to use it on the CLI through a config file: @@ -185,6 +191,8 @@ abide by its terms. [chat]: https://github.com/remarkjs/remark/discussions +[esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c + [npm]: https://docs.npmjs.com/cli/install [health]: https://github.com/remarkjs/.github diff --git a/packages/remark-lint-no-blockquote-without-marker/tsconfig.json b/packages/remark-lint-no-blockquote-without-marker/tsconfig.json new file mode 100644 index 00000000..7e61871a --- /dev/null +++ b/packages/remark-lint-no-blockquote-without-marker/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.json", + "include": ["*.js"] +} diff --git a/packages/remark-lint-no-consecutive-blank-lines/index.js b/packages/remark-lint-no-consecutive-blank-lines/index.js index 2e252c23..6cd97186 100644 --- a/packages/remark-lint-no-consecutive-blank-lines/index.js +++ b/packages/remark-lint-no-consecutive-blank-lines/index.js @@ -44,6 +44,11 @@ * 4:5: Remove 2 lines after node */ +/** + * @typedef {import('mdast').Root} Root + * @typedef {import('unist').Point} Point + */ + import {lintRule} from 'unified-lint-rule' import plural from 'pluralize' import {visit} from 'unist-util-visit' @@ -52,6 +57,7 @@ import {generated} from 'unist-util-generated' const remarkLintNoConsecutiveBlankLines = lintRule( 'remark-lint:no-consecutive-blank-lines', + /** @type {import('unified-lint-rule').Rule} */ (tree, file) => { visit(tree, (node) => { if (!generated(node) && 'children' in node) { @@ -83,8 +89,14 @@ const remarkLintNoConsecutiveBlankLines = lintRule( } }) - // Compare the difference between `start` and `end`, and warn when that - // difference exceeds `max`. + /** + * Compare the difference between `start` and `end`, and warn when that + * difference exceeds `max`. + * + * @param {Point} start + * @param {Point} end + * @param {0|1|2} max + */ function compare(start, end, max) { const diff = end.line - start.line const lines = Math.abs(diff) - max diff --git a/packages/remark-lint-no-consecutive-blank-lines/package.json b/packages/remark-lint-no-consecutive-blank-lines/package.json index 36d79ad8..9fe32389 100644 --- a/packages/remark-lint-no-consecutive-blank-lines/package.json +++ b/packages/remark-lint-no-consecutive-blank-lines/package.json @@ -24,15 +24,29 @@ "sideEffects": false, "type": "module", "main": "index.js", + "types": "index.d.ts", "files": [ + "index.d.ts", "index.js" ], "dependencies": { + "@types/mdast": "^3.0.0", + "@types/unist": "^2.0.0", "pluralize": "^8.0.0", + "unified": "^10.0.0", "unified-lint-rule": "^1.0.0", "unist-util-generated": "^2.0.0", "unist-util-position": "^4.0.0", "unist-util-visit": "^4.0.0" }, - "xo": false + "scripts": { + "build": "rimraf \"*.d.ts\" && tsc && type-coverage" + }, + "xo": false, + "typeCoverage": { + "atLeast": 100, + "detail": true, + "strict": true, + "ignoreCatch": true + } } diff --git a/packages/remark-lint-no-consecutive-blank-lines/readme.md b/packages/remark-lint-no-consecutive-blank-lines/readme.md index fc84c359..0e111af2 100644 --- a/packages/remark-lint-no-consecutive-blank-lines/readme.md +++ b/packages/remark-lint-no-consecutive-blank-lines/readme.md @@ -79,12 +79,18 @@ Foo… ## Install +This package is [ESM only][esm]: +Node 12+ is needed to use it and it must be `imported`ed instead of `required`d. + [npm][]: ```sh npm install remark-lint-no-consecutive-blank-lines ``` +This package exports no identifiers. +The default export is `remarkLintNoConsecutiveBlankLines`. + ## Use You probably want to use it on the CLI through a config file: @@ -165,6 +171,8 @@ abide by its terms. [chat]: https://github.com/remarkjs/remark/discussions +[esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c + [npm]: https://docs.npmjs.com/cli/install [health]: https://github.com/remarkjs/.github diff --git a/packages/remark-lint-no-consecutive-blank-lines/tsconfig.json b/packages/remark-lint-no-consecutive-blank-lines/tsconfig.json new file mode 100644 index 00000000..7e61871a --- /dev/null +++ b/packages/remark-lint-no-consecutive-blank-lines/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.json", + "include": ["*.js"] +} diff --git a/packages/remark-lint-no-duplicate-defined-urls/index.js b/packages/remark-lint-no-duplicate-defined-urls/index.js index bbcd568c..ca9fcde0 100644 --- a/packages/remark-lint-no-duplicate-defined-urls/index.js +++ b/packages/remark-lint-no-duplicate-defined-urls/index.js @@ -24,6 +24,10 @@ * 2:1-2:19: Do not use different definitions with the same URL (1:1) */ +/** + * @typedef {import('mdast').Root} Root + */ + import {lintRule} from 'unified-lint-rule' import {pointStart} from 'unist-util-position' import {generated} from 'unist-util-generated' @@ -32,7 +36,9 @@ import {visit} from 'unist-util-visit' const remarkLintNoDuplicateDefinedUrls = lintRule( 'remark-lint:no-duplicate-defined-urls', + /** @type {import('unified-lint-rule').Rule} */ (tree, file) => { + /** @type {Record} */ const map = Object.create(null) visit(tree, 'definition', (node) => { @@ -43,13 +49,13 @@ const remarkLintNoDuplicateDefinedUrls = lintRule( if (duplicate) { file.message( 'Do not use different definitions with the same URL (' + - stringifyPosition(pointStart(duplicate)) + + duplicate + ')', node ) } - map[url] = node + map[url] = stringifyPosition(pointStart(node)) } }) } diff --git a/packages/remark-lint-no-duplicate-defined-urls/package.json b/packages/remark-lint-no-duplicate-defined-urls/package.json index f27c93db..4218d614 100644 --- a/packages/remark-lint-no-duplicate-defined-urls/package.json +++ b/packages/remark-lint-no-duplicate-defined-urls/package.json @@ -25,15 +25,28 @@ "sideEffects": false, "type": "module", "main": "index.js", + "types": "index.d.ts", "files": [ + "index.d.ts", "index.js" ], "dependencies": { + "@types/mdast": "^3.0.0", + "unified": "^10.0.0", "unified-lint-rule": "^1.0.0", "unist-util-generated": "^2.0.0", "unist-util-position": "^4.0.0", "unist-util-stringify-position": "^3.0.0", "unist-util-visit": "^4.0.0" }, - "xo": false + "scripts": { + "build": "rimraf \"*.d.ts\" && tsc && type-coverage" + }, + "xo": false, + "typeCoverage": { + "atLeast": 100, + "detail": true, + "strict": true, + "ignoreCatch": true + } } diff --git a/packages/remark-lint-no-duplicate-defined-urls/readme.md b/packages/remark-lint-no-duplicate-defined-urls/readme.md index 36dc1d1d..12c10009 100644 --- a/packages/remark-lint-no-duplicate-defined-urls/readme.md +++ b/packages/remark-lint-no-duplicate-defined-urls/readme.md @@ -48,12 +48,18 @@ No messages. ## Install +This package is [ESM only][esm]: +Node 12+ is needed to use it and it must be `imported`ed instead of `required`d. + [npm][]: ```sh npm install remark-lint-no-duplicate-defined-urls ``` +This package exports no identifiers. +The default export is `remarkLintNoDuplicateDefinedUrls`. + ## Use You probably want to use it on the CLI through a config file: @@ -134,6 +140,8 @@ abide by its terms. [chat]: https://github.com/remarkjs/remark/discussions +[esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c + [npm]: https://docs.npmjs.com/cli/install [health]: https://github.com/remarkjs/.github diff --git a/packages/remark-lint-no-duplicate-defined-urls/tsconfig.json b/packages/remark-lint-no-duplicate-defined-urls/tsconfig.json new file mode 100644 index 00000000..7e61871a --- /dev/null +++ b/packages/remark-lint-no-duplicate-defined-urls/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.json", + "include": ["*.js"] +} diff --git a/packages/remark-lint-no-duplicate-definitions/index.js b/packages/remark-lint-no-duplicate-definitions/index.js index 43c2a7d1..68ff27a7 100644 --- a/packages/remark-lint-no-duplicate-definitions/index.js +++ b/packages/remark-lint-no-duplicate-definitions/index.js @@ -24,6 +24,10 @@ * 2:1-2:11: Do not use definitions with the same identifier (1:1) */ +/** + * @typedef {import('mdast').Root} Root + */ + import {lintRule} from 'unified-lint-rule' import {pointStart} from 'unist-util-position' import {generated} from 'unist-util-generated' @@ -32,7 +36,9 @@ import {visit} from 'unist-util-visit' const remarkLintNoDuplicateDefinitions = lintRule( 'remark-lint:no-duplicate-definitions', + /** @type {import('unified-lint-rule').Rule} */ (tree, file) => { + /** @type {Record} */ const map = Object.create(null) visit(tree, (node) => { @@ -46,13 +52,13 @@ const remarkLintNoDuplicateDefinitions = lintRule( if (duplicate) { file.message( 'Do not use definitions with the same identifier (' + - stringifyPosition(pointStart(duplicate)) + + duplicate + ')', node ) } - map[identifier] = node + map[identifier] = stringifyPosition(pointStart(node)) } }) } diff --git a/packages/remark-lint-no-duplicate-definitions/package.json b/packages/remark-lint-no-duplicate-definitions/package.json index 8fbdde23..75621e34 100644 --- a/packages/remark-lint-no-duplicate-definitions/package.json +++ b/packages/remark-lint-no-duplicate-definitions/package.json @@ -24,15 +24,28 @@ "sideEffects": false, "type": "module", "main": "index.js", + "types": "index.d.ts", "files": [ + "index.d.ts", "index.js" ], "dependencies": { + "@types/mdast": "^3.0.0", + "unified": "^10.0.0", "unified-lint-rule": "^1.0.0", "unist-util-generated": "^2.0.0", "unist-util-position": "^4.0.0", "unist-util-stringify-position": "^3.0.0", "unist-util-visit": "^4.0.0" }, - "xo": false + "scripts": { + "build": "rimraf \"*.d.ts\" && tsc && type-coverage" + }, + "xo": false, + "typeCoverage": { + "atLeast": 100, + "detail": true, + "strict": true, + "ignoreCatch": true + } } diff --git a/packages/remark-lint-no-duplicate-definitions/readme.md b/packages/remark-lint-no-duplicate-definitions/readme.md index f33cc67b..52997bf9 100644 --- a/packages/remark-lint-no-duplicate-definitions/readme.md +++ b/packages/remark-lint-no-duplicate-definitions/readme.md @@ -52,12 +52,18 @@ No messages. ## Install +This package is [ESM only][esm]: +Node 12+ is needed to use it and it must be `imported`ed instead of `required`d. + [npm][]: ```sh npm install remark-lint-no-duplicate-definitions ``` +This package exports no identifiers. +The default export is `remarkLintNoDuplicateDefinitions`. + ## Use You probably want to use it on the CLI through a config file: @@ -138,6 +144,8 @@ abide by its terms. [chat]: https://github.com/remarkjs/remark/discussions +[esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c + [npm]: https://docs.npmjs.com/cli/install [health]: https://github.com/remarkjs/.github diff --git a/packages/remark-lint-no-duplicate-definitions/tsconfig.json b/packages/remark-lint-no-duplicate-definitions/tsconfig.json new file mode 100644 index 00000000..7e61871a --- /dev/null +++ b/packages/remark-lint-no-duplicate-definitions/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.json", + "include": ["*.js"] +} diff --git a/packages/remark-lint-no-duplicate-headings-in-section/index.js b/packages/remark-lint-no-duplicate-headings-in-section/index.js index 3b327784..46b0da3d 100644 --- a/packages/remark-lint-no-duplicate-headings-in-section/index.js +++ b/packages/remark-lint-no-duplicate-headings-in-section/index.js @@ -59,6 +59,11 @@ * 7:1-7:11: Do not use headings with similar content per section (3:1) */ +/** + * @typedef {import('mdast').Root} Root + * @typedef {import('mdast').Heading} Heading + */ + import {lintRule} from 'unified-lint-rule' import {pointStart} from 'unist-util-position' import {generated} from 'unist-util-generated' @@ -68,7 +73,9 @@ import {toString} from 'mdast-util-to-string' const remarkLintNoDuplicateHeadingsInSection = lintRule( 'remark-lint:no-duplicate-headings-in-section', + /** @type {import('unified-lint-rule').Rule} */ (tree, file) => { + /** @type {Array.>} */ let stack = [] visit(tree, 'heading', (node) => { diff --git a/packages/remark-lint-no-duplicate-headings-in-section/package.json b/packages/remark-lint-no-duplicate-headings-in-section/package.json index d622e93a..ee964de1 100644 --- a/packages/remark-lint-no-duplicate-headings-in-section/package.json +++ b/packages/remark-lint-no-duplicate-headings-in-section/package.json @@ -25,16 +25,29 @@ "sideEffects": false, "type": "module", "main": "index.js", + "types": "index.d.ts", "files": [ + "index.d.ts", "index.js" ], "dependencies": { + "@types/mdast": "^3.0.0", "mdast-util-to-string": "^3.0.0", + "unified": "^10.0.0", "unified-lint-rule": "^1.0.0", "unist-util-generated": "^2.0.0", "unist-util-position": "^4.0.0", "unist-util-stringify-position": "^3.0.0", "unist-util-visit": "^4.0.0" }, - "xo": false + "scripts": { + "build": "rimraf \"*.d.ts\" && tsc && type-coverage" + }, + "xo": false, + "typeCoverage": { + "atLeast": 100, + "detail": true, + "strict": true, + "ignoreCatch": true + } } diff --git a/packages/remark-lint-no-duplicate-headings-in-section/readme.md b/packages/remark-lint-no-duplicate-headings-in-section/readme.md index a8af562c..d23536ef 100644 --- a/packages/remark-lint-no-duplicate-headings-in-section/readme.md +++ b/packages/remark-lint-no-duplicate-headings-in-section/readme.md @@ -87,12 +87,18 @@ No messages. ## Install +This package is [ESM only][esm]: +Node 12+ is needed to use it and it must be `imported`ed instead of `required`d. + [npm][]: ```sh npm install remark-lint-no-duplicate-headings-in-section ``` +This package exports no identifiers. +The default export is `remarkLintNoDuplicateHeadingsInSection`. + ## Use You probably want to use it on the CLI through a config file: @@ -173,6 +179,8 @@ abide by its terms. [chat]: https://github.com/remarkjs/remark/discussions +[esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c + [npm]: https://docs.npmjs.com/cli/install [health]: https://github.com/remarkjs/.github diff --git a/packages/remark-lint-no-duplicate-headings-in-section/tsconfig.json b/packages/remark-lint-no-duplicate-headings-in-section/tsconfig.json new file mode 100644 index 00000000..7e61871a --- /dev/null +++ b/packages/remark-lint-no-duplicate-headings-in-section/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.json", + "include": ["*.js"] +} diff --git a/packages/remark-lint-no-duplicate-headings/index.js b/packages/remark-lint-no-duplicate-headings/index.js index 3e1d6e05..c02fbd25 100644 --- a/packages/remark-lint-no-duplicate-headings/index.js +++ b/packages/remark-lint-no-duplicate-headings/index.js @@ -29,6 +29,10 @@ * 5:1-5:29: Do not use headings with similar content (3:1) */ +/** + * @typedef {import('mdast').Root} Root + */ + import {lintRule} from 'unified-lint-rule' import {pointStart} from 'unist-util-position' import {generated} from 'unist-util-generated' @@ -38,7 +42,9 @@ import {toString} from 'mdast-util-to-string' const remarkLintNoDuplicateHeadings = lintRule( 'remark-lint:no-duplicate-headings', + /** @type {import('unified-lint-rule').Rule} */ (tree, file) => { + /** @type {Record} */ const map = Object.create(null) visit(tree, 'heading', (node) => { @@ -48,14 +54,12 @@ const remarkLintNoDuplicateHeadings = lintRule( if (duplicate) { file.message( - 'Do not use headings with similar content (' + - stringifyPosition(pointStart(duplicate)) + - ')', + 'Do not use headings with similar content (' + duplicate + ')', node ) } - map[value] = node + map[value] = stringifyPosition(pointStart(node)) } }) } diff --git a/packages/remark-lint-no-duplicate-headings/package.json b/packages/remark-lint-no-duplicate-headings/package.json index 34e889b7..20d254a7 100644 --- a/packages/remark-lint-no-duplicate-headings/package.json +++ b/packages/remark-lint-no-duplicate-headings/package.json @@ -24,16 +24,29 @@ "sideEffects": false, "type": "module", "main": "index.js", + "types": "index.d.ts", "files": [ + "index.d.ts", "index.js" ], "dependencies": { + "@types/mdast": "^3.0.0", "mdast-util-to-string": "^3.0.0", + "unified": "^10.0.0", "unified-lint-rule": "^1.0.0", "unist-util-generated": "^2.0.0", "unist-util-position": "^4.0.0", "unist-util-stringify-position": "^3.0.0", "unist-util-visit": "^4.0.0" }, - "xo": false + "scripts": { + "build": "rimraf \"*.d.ts\" && tsc && type-coverage" + }, + "xo": false, + "typeCoverage": { + "atLeast": 100, + "detail": true, + "strict": true, + "ignoreCatch": true + } } diff --git a/packages/remark-lint-no-duplicate-headings/readme.md b/packages/remark-lint-no-duplicate-headings/readme.md index 27587619..52407cfd 100644 --- a/packages/remark-lint-no-duplicate-headings/readme.md +++ b/packages/remark-lint-no-duplicate-headings/readme.md @@ -57,12 +57,18 @@ No messages. ## Install +This package is [ESM only][esm]: +Node 12+ is needed to use it and it must be `imported`ed instead of `required`d. + [npm][]: ```sh npm install remark-lint-no-duplicate-headings ``` +This package exports no identifiers. +The default export is `remarkLintNoDuplicateHeadings`. + ## Use You probably want to use it on the CLI through a config file: @@ -143,6 +149,8 @@ abide by its terms. [chat]: https://github.com/remarkjs/remark/discussions +[esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c + [npm]: https://docs.npmjs.com/cli/install [health]: https://github.com/remarkjs/.github diff --git a/packages/remark-lint-no-duplicate-headings/tsconfig.json b/packages/remark-lint-no-duplicate-headings/tsconfig.json new file mode 100644 index 00000000..7e61871a --- /dev/null +++ b/packages/remark-lint-no-duplicate-headings/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.json", + "include": ["*.js"] +} diff --git a/packages/remark-lint-no-emphasis-as-heading/index.js b/packages/remark-lint-no-emphasis-as-heading/index.js index 8f8632a1..92756a23 100644 --- a/packages/remark-lint-no-emphasis-as-heading/index.js +++ b/packages/remark-lint-no-emphasis-as-heading/index.js @@ -32,30 +32,41 @@ * 5:1-5:8: Don’t use emphasis to introduce a section, use a heading */ +/** + * @typedef {import('mdast').Root} Root + */ + import {lintRule} from 'unified-lint-rule' import {visit} from 'unist-util-visit' import {generated} from 'unist-util-generated' const remarkLintNoEmphasisAsHeading = lintRule( 'remark-lint:no-emphasis-as-heading', + /** @type {import('unified-lint-rule').Rule} */ (tree, file) => { visit(tree, 'paragraph', (node, index, parent) => { const head = node.children[0] - const previous = parent.children[index - 1] - const next = parent.children[index + 1] if ( + parent && + typeof index === 'number' && !generated(node) && - (!previous || previous.type !== 'heading') && - next && - next.type === 'paragraph' && node.children.length === 1 && (head.type === 'emphasis' || head.type === 'strong') ) { - file.message( - 'Don’t use emphasis to introduce a section, use a heading', - node - ) + const previous = parent.children[index - 1] + const next = parent.children[index + 1] + + if ( + (!previous || previous.type !== 'heading') && + next && + next.type === 'paragraph' + ) { + file.message( + 'Don’t use emphasis to introduce a section, use a heading', + node + ) + } } }) } diff --git a/packages/remark-lint-no-emphasis-as-heading/package.json b/packages/remark-lint-no-emphasis-as-heading/package.json index 957d1010..f0967990 100644 --- a/packages/remark-lint-no-emphasis-as-heading/package.json +++ b/packages/remark-lint-no-emphasis-as-heading/package.json @@ -24,13 +24,26 @@ "sideEffects": false, "type": "module", "main": "index.js", + "types": "index.d.ts", "files": [ + "index.d.ts", "index.js" ], "dependencies": { + "@types/mdast": "^3.0.0", + "unified": "^10.0.0", "unified-lint-rule": "^1.0.0", "unist-util-generated": "^2.0.0", "unist-util-visit": "^4.0.0" }, - "xo": false + "scripts": { + "build": "rimraf \"*.d.ts\" && tsc && type-coverage" + }, + "xo": false, + "typeCoverage": { + "atLeast": 100, + "detail": true, + "strict": true, + "ignoreCatch": true + } } diff --git a/packages/remark-lint-no-emphasis-as-heading/readme.md b/packages/remark-lint-no-emphasis-as-heading/readme.md index 46f4c52f..9f196084 100644 --- a/packages/remark-lint-no-emphasis-as-heading/readme.md +++ b/packages/remark-lint-no-emphasis-as-heading/readme.md @@ -60,12 +60,18 @@ Quux. ## Install +This package is [ESM only][esm]: +Node 12+ is needed to use it and it must be `imported`ed instead of `required`d. + [npm][]: ```sh npm install remark-lint-no-emphasis-as-heading ``` +This package exports no identifiers. +The default export is `remarkLintNoEmphasisAsHeading`. + ## Use You probably want to use it on the CLI through a config file: @@ -146,6 +152,8 @@ abide by its terms. [chat]: https://github.com/remarkjs/remark/discussions +[esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c + [npm]: https://docs.npmjs.com/cli/install [health]: https://github.com/remarkjs/.github diff --git a/packages/remark-lint-no-emphasis-as-heading/tsconfig.json b/packages/remark-lint-no-emphasis-as-heading/tsconfig.json new file mode 100644 index 00000000..7e61871a --- /dev/null +++ b/packages/remark-lint-no-emphasis-as-heading/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.json", + "include": ["*.js"] +} diff --git a/packages/remark-lint-no-empty-url/index.js b/packages/remark-lint-no-empty-url/index.js index 83704535..59d1ffce 100644 --- a/packages/remark-lint-no-empty-url/index.js +++ b/packages/remark-lint-no-empty-url/index.js @@ -27,12 +27,17 @@ * 3:1-3:11: Don’t use images without URL */ +/** + * @typedef {import('mdast').Root} Root + */ + import {lintRule} from 'unified-lint-rule' import {visit} from 'unist-util-visit' import {generated} from 'unist-util-generated' const remarkLintNoEmptyUrl = lintRule( 'remark-lint:no-empty-url', + /** @type {import('unified-lint-rule').Rule} */ (tree, file) => { visit(tree, (node) => { if ( diff --git a/packages/remark-lint-no-empty-url/package.json b/packages/remark-lint-no-empty-url/package.json index 40c967cd..9cb04591 100644 --- a/packages/remark-lint-no-empty-url/package.json +++ b/packages/remark-lint-no-empty-url/package.json @@ -26,13 +26,26 @@ "sideEffects": false, "type": "module", "main": "index.js", + "types": "index.d.ts", "files": [ + "index.d.ts", "index.js" ], "dependencies": { + "@types/mdast": "^3.0.0", + "unified": "^10.0.0", "unified-lint-rule": "^1.0.0", "unist-util-generated": "^2.0.0", "unist-util-visit": "^4.0.0" }, - "xo": false + "scripts": { + "build": "rimraf \"*.d.ts\" && tsc && type-coverage" + }, + "xo": false, + "typeCoverage": { + "atLeast": 100, + "detail": true, + "strict": true, + "ignoreCatch": true + } } diff --git a/packages/remark-lint-no-empty-url/readme.md b/packages/remark-lint-no-empty-url/readme.md index 6eee767a..486c5456 100644 --- a/packages/remark-lint-no-empty-url/readme.md +++ b/packages/remark-lint-no-empty-url/readme.md @@ -51,12 +51,18 @@ No messages. ## Install +This package is [ESM only][esm]: +Node 12+ is needed to use it and it must be `imported`ed instead of `required`d. + [npm][]: ```sh npm install remark-lint-no-empty-url ``` +This package exports no identifiers. +The default export is `remarkLintNoEmptyUrl`. + ## Use You probably want to use it on the CLI through a config file: @@ -137,6 +143,8 @@ abide by its terms. [chat]: https://github.com/remarkjs/remark/discussions +[esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c + [npm]: https://docs.npmjs.com/cli/install [health]: https://github.com/remarkjs/.github diff --git a/packages/remark-lint-no-empty-url/tsconfig.json b/packages/remark-lint-no-empty-url/tsconfig.json new file mode 100644 index 00000000..7e61871a --- /dev/null +++ b/packages/remark-lint-no-empty-url/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.json", + "include": ["*.js"] +} diff --git a/packages/remark-lint-no-file-name-articles/index.js b/packages/remark-lint-no-file-name-articles/index.js index 23160349..c53b692f 100644 --- a/packages/remark-lint-no-file-name-articles/index.js +++ b/packages/remark-lint-no-file-name-articles/index.js @@ -30,11 +30,16 @@ * 1:1: Do not start file names with `an` */ +/** + * @typedef {import('mdast').Root} Root + */ + import {lintRule} from 'unified-lint-rule' const remarkLintNoFileNameArticles = lintRule( 'remark-lint:no-file-name-articles', - (tree, file) => { + /** @type {import('unified-lint-rule').Rule} */ + (_, file) => { const match = file.stem && file.stem.match(/^(the|teh|an?)\b/i) if (match) { diff --git a/packages/remark-lint-no-file-name-articles/package.json b/packages/remark-lint-no-file-name-articles/package.json index 5a0e0663..6c400464 100644 --- a/packages/remark-lint-no-file-name-articles/package.json +++ b/packages/remark-lint-no-file-name-articles/package.json @@ -26,11 +26,24 @@ "sideEffects": false, "type": "module", "main": "index.js", + "types": "index.d.ts", "files": [ + "index.d.ts", "index.js" ], "dependencies": { + "@types/mdast": "^3.0.0", + "unified": "^10.0.0", "unified-lint-rule": "^1.0.0" }, - "xo": false + "scripts": { + "build": "rimraf \"*.d.ts\" && tsc && type-coverage" + }, + "xo": false, + "typeCoverage": { + "atLeast": 100, + "detail": true, + "strict": true, + "ignoreCatch": true + } } diff --git a/packages/remark-lint-no-file-name-articles/readme.md b/packages/remark-lint-no-file-name-articles/readme.md index f0945e19..41913a37 100644 --- a/packages/remark-lint-no-file-name-articles/readme.md +++ b/packages/remark-lint-no-file-name-articles/readme.md @@ -62,12 +62,18 @@ No messages. ## Install +This package is [ESM only][esm]: +Node 12+ is needed to use it and it must be `imported`ed instead of `required`d. + [npm][]: ```sh npm install remark-lint-no-file-name-articles ``` +This package exports no identifiers. +The default export is `remarkLintNoFileNameArticles`. + ## Use You probably want to use it on the CLI through a config file: @@ -148,6 +154,8 @@ abide by its terms. [chat]: https://github.com/remarkjs/remark/discussions +[esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c + [npm]: https://docs.npmjs.com/cli/install [health]: https://github.com/remarkjs/.github diff --git a/packages/remark-lint-no-file-name-articles/tsconfig.json b/packages/remark-lint-no-file-name-articles/tsconfig.json new file mode 100644 index 00000000..7e61871a --- /dev/null +++ b/packages/remark-lint-no-file-name-articles/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.json", + "include": ["*.js"] +} diff --git a/packages/remark-lint-no-file-name-consecutive-dashes/index.js b/packages/remark-lint-no-file-name-consecutive-dashes/index.js index 4c30ad44..8bf292fb 100644 --- a/packages/remark-lint-no-file-name-consecutive-dashes/index.js +++ b/packages/remark-lint-no-file-name-consecutive-dashes/index.js @@ -15,11 +15,16 @@ * 1:1: Do not use consecutive dashes in a file name */ +/** + * @typedef {import('mdast').Root} Root + */ + import {lintRule} from 'unified-lint-rule' const remarkLintNoFileNameConsecutiveDashes = lintRule( 'remark-lint:no-file-name-consecutive-dashes', - (tree, file) => { + /** @type {import('unified-lint-rule').Rule} */ + (_, file) => { if (file.stem && /-{2,}/.test(file.stem)) { file.message('Do not use consecutive dashes in a file name') } diff --git a/packages/remark-lint-no-file-name-consecutive-dashes/package.json b/packages/remark-lint-no-file-name-consecutive-dashes/package.json index 96454314..defafb9b 100644 --- a/packages/remark-lint-no-file-name-consecutive-dashes/package.json +++ b/packages/remark-lint-no-file-name-consecutive-dashes/package.json @@ -27,11 +27,24 @@ "sideEffects": false, "type": "module", "main": "index.js", + "types": "index.d.ts", "files": [ + "index.d.ts", "index.js" ], "dependencies": { + "@types/mdast": "^3.0.0", + "unified": "^10.0.0", "unified-lint-rule": "^1.0.0" }, - "xo": false + "scripts": { + "build": "rimraf \"*.d.ts\" && tsc && type-coverage" + }, + "xo": false, + "typeCoverage": { + "atLeast": 100, + "detail": true, + "strict": true, + "ignoreCatch": true + } } diff --git a/packages/remark-lint-no-file-name-consecutive-dashes/readme.md b/packages/remark-lint-no-file-name-consecutive-dashes/readme.md index fa2277dc..a5589e7e 100644 --- a/packages/remark-lint-no-file-name-consecutive-dashes/readme.md +++ b/packages/remark-lint-no-file-name-consecutive-dashes/readme.md @@ -38,12 +38,18 @@ No messages. ## Install +This package is [ESM only][esm]: +Node 12+ is needed to use it and it must be `imported`ed instead of `required`d. + [npm][]: ```sh npm install remark-lint-no-file-name-consecutive-dashes ``` +This package exports no identifiers. +The default export is `remarkLintNoFileNameConsecutiveDashes`. + ## Use You probably want to use it on the CLI through a config file: @@ -124,6 +130,8 @@ abide by its terms. [chat]: https://github.com/remarkjs/remark/discussions +[esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c + [npm]: https://docs.npmjs.com/cli/install [health]: https://github.com/remarkjs/.github diff --git a/packages/remark-lint-no-file-name-consecutive-dashes/tsconfig.json b/packages/remark-lint-no-file-name-consecutive-dashes/tsconfig.json new file mode 100644 index 00000000..7e61871a --- /dev/null +++ b/packages/remark-lint-no-file-name-consecutive-dashes/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.json", + "include": ["*.js"] +} diff --git a/packages/remark-lint-no-file-name-irregular-characters/index.js b/packages/remark-lint-no-file-name-irregular-characters/index.js index c7f27b32..8e697a78 100644 --- a/packages/remark-lint-no-file-name-irregular-characters/index.js +++ b/packages/remark-lint-no-file-name-irregular-characters/index.js @@ -37,13 +37,20 @@ * 1:1: Do not use ` ` in a file name */ +/** + * @typedef {import('mdast').Root} Root + * + * @typedef {RegExp|string} Options + */ + import {lintRule} from 'unified-lint-rule' const expression = /[^\\.a-zA-Z\d-]/ const remarkLintNoFileNameIrregularCharacters = lintRule( 'remark-lint:no-file-name-irregular-characters', - (tree, file, option) => { + /** @type {import('unified-lint-rule').Rule} */ + (_, file, option) => { let preferred = option || expression if (typeof preferred === 'string') { diff --git a/packages/remark-lint-no-file-name-irregular-characters/package.json b/packages/remark-lint-no-file-name-irregular-characters/package.json index 193b800a..bf5cad7e 100644 --- a/packages/remark-lint-no-file-name-irregular-characters/package.json +++ b/packages/remark-lint-no-file-name-irregular-characters/package.json @@ -26,11 +26,24 @@ "sideEffects": false, "type": "module", "main": "index.js", + "types": "index.d.ts", "files": [ + "index.d.ts", "index.js" ], "dependencies": { + "@types/mdast": "^3.0.0", + "unified": "^10.0.0", "unified-lint-rule": "^1.0.0" }, - "xo": false + "scripts": { + "build": "rimraf \"*.d.ts\" && tsc && type-coverage" + }, + "xo": false, + "typeCoverage": { + "atLeast": 100, + "detail": true, + "strict": true, + "ignoreCatch": true + } } diff --git a/packages/remark-lint-no-file-name-irregular-characters/readme.md b/packages/remark-lint-no-file-name-irregular-characters/readme.md index 704505c8..5901269e 100644 --- a/packages/remark-lint-no-file-name-irregular-characters/readme.md +++ b/packages/remark-lint-no-file-name-irregular-characters/readme.md @@ -71,12 +71,18 @@ When configured with `'\\.a-z0-9'`. ## Install +This package is [ESM only][esm]: +Node 12+ is needed to use it and it must be `imported`ed instead of `required`d. + [npm][]: ```sh npm install remark-lint-no-file-name-irregular-characters ``` +This package exports no identifiers. +The default export is `remarkLintNoFileNameIrregularCharacters`. + ## Use You probably want to use it on the CLI through a config file: @@ -157,6 +163,8 @@ abide by its terms. [chat]: https://github.com/remarkjs/remark/discussions +[esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c + [npm]: https://docs.npmjs.com/cli/install [health]: https://github.com/remarkjs/.github diff --git a/packages/remark-lint-no-file-name-irregular-characters/tsconfig.json b/packages/remark-lint-no-file-name-irregular-characters/tsconfig.json new file mode 100644 index 00000000..7e61871a --- /dev/null +++ b/packages/remark-lint-no-file-name-irregular-characters/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.json", + "include": ["*.js"] +} diff --git a/packages/remark-lint-no-file-name-mixed-case/index.js b/packages/remark-lint-no-file-name-mixed-case/index.js index 04823d78..ab687561 100644 --- a/packages/remark-lint-no-file-name-mixed-case/index.js +++ b/packages/remark-lint-no-file-name-mixed-case/index.js @@ -18,11 +18,16 @@ * 1:1: Do not mix casing in file names */ +/** + * @typedef {import('mdast').Root} Root + */ + import {lintRule} from 'unified-lint-rule' const remarkLintNofileNameMixedCase = lintRule( 'remark-lint:no-file-name-mixed-case', - (tree, file) => { + /** @type {import('unified-lint-rule').Rule} */ + (_, file) => { const name = file.stem if (name && !(name === name.toLowerCase() || name === name.toUpperCase())) { diff --git a/packages/remark-lint-no-file-name-mixed-case/package.json b/packages/remark-lint-no-file-name-mixed-case/package.json index da96a49e..aab64648 100644 --- a/packages/remark-lint-no-file-name-mixed-case/package.json +++ b/packages/remark-lint-no-file-name-mixed-case/package.json @@ -25,11 +25,24 @@ "sideEffects": false, "type": "module", "main": "index.js", + "types": "index.d.ts", "files": [ + "index.d.ts", "index.js" ], "dependencies": { + "@types/mdast": "^3.0.0", + "unified": "^10.0.0", "unified-lint-rule": "^1.0.0" }, - "xo": false + "scripts": { + "build": "rimraf \"*.d.ts\" && tsc && type-coverage" + }, + "xo": false, + "typeCoverage": { + "atLeast": 100, + "detail": true, + "strict": true, + "ignoreCatch": true + } } diff --git a/packages/remark-lint-no-file-name-mixed-case/readme.md b/packages/remark-lint-no-file-name-mixed-case/readme.md index 66114921..9af8acbd 100644 --- a/packages/remark-lint-no-file-name-mixed-case/readme.md +++ b/packages/remark-lint-no-file-name-mixed-case/readme.md @@ -44,12 +44,18 @@ No messages. ## Install +This package is [ESM only][esm]: +Node 12+ is needed to use it and it must be `imported`ed instead of `required`d. + [npm][]: ```sh npm install remark-lint-no-file-name-mixed-case ``` +This package exports no identifiers. +The default export is `remarkLintNoFileNameMixedCase`. + ## Use You probably want to use it on the CLI through a config file: @@ -130,6 +136,8 @@ abide by its terms. [chat]: https://github.com/remarkjs/remark/discussions +[esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c + [npm]: https://docs.npmjs.com/cli/install [health]: https://github.com/remarkjs/.github diff --git a/packages/remark-lint-no-file-name-mixed-case/tsconfig.json b/packages/remark-lint-no-file-name-mixed-case/tsconfig.json new file mode 100644 index 00000000..7e61871a --- /dev/null +++ b/packages/remark-lint-no-file-name-mixed-case/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.json", + "include": ["*.js"] +} diff --git a/packages/remark-lint-no-file-name-outer-dashes/index.js b/packages/remark-lint-no-file-name-outer-dashes/index.js index 22220d61..9558b1b5 100644 --- a/packages/remark-lint-no-file-name-outer-dashes/index.js +++ b/packages/remark-lint-no-file-name-outer-dashes/index.js @@ -20,11 +20,16 @@ * 1:1: Do not use initial or final dashes in a file name */ +/** + * @typedef {import('mdast').Root} Root + */ + import {lintRule} from 'unified-lint-rule' const remarkLintNofileNameOuterDashes = lintRule( 'remark-lint:no-file-name-outer-dashes', - (tree, file) => { + /** @type {import('unified-lint-rule').Rule} */ + (_, file) => { if (file.stem && /^-|-$/.test(file.stem)) { file.message('Do not use initial or final dashes in a file name') } diff --git a/packages/remark-lint-no-file-name-outer-dashes/package.json b/packages/remark-lint-no-file-name-outer-dashes/package.json index 19f4282c..738f0645 100644 --- a/packages/remark-lint-no-file-name-outer-dashes/package.json +++ b/packages/remark-lint-no-file-name-outer-dashes/package.json @@ -26,11 +26,24 @@ "sideEffects": false, "type": "module", "main": "index.js", + "types": "index.d.ts", "files": [ + "index.d.ts", "index.js" ], "dependencies": { + "@types/mdast": "^3.0.0", + "unified": "^10.0.0", "unified-lint-rule": "^1.0.0" }, - "xo": false + "scripts": { + "build": "rimraf \"*.d.ts\" && tsc && type-coverage" + }, + "xo": false, + "typeCoverage": { + "atLeast": 100, + "detail": true, + "strict": true, + "ignoreCatch": true + } } diff --git a/packages/remark-lint-no-file-name-outer-dashes/readme.md b/packages/remark-lint-no-file-name-outer-dashes/readme.md index 532b68cc..270d1891 100644 --- a/packages/remark-lint-no-file-name-outer-dashes/readme.md +++ b/packages/remark-lint-no-file-name-outer-dashes/readme.md @@ -46,12 +46,18 @@ No messages. ## Install +This package is [ESM only][esm]: +Node 12+ is needed to use it and it must be `imported`ed instead of `required`d. + [npm][]: ```sh npm install remark-lint-no-file-name-outer-dashes ``` +This package exports no identifiers. +The default export is `remarkLintNoFileNameOuterDashes`. + ## Use You probably want to use it on the CLI through a config file: @@ -132,6 +138,8 @@ abide by its terms. [chat]: https://github.com/remarkjs/remark/discussions +[esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c + [npm]: https://docs.npmjs.com/cli/install [health]: https://github.com/remarkjs/.github diff --git a/packages/remark-lint-no-file-name-outer-dashes/tsconfig.json b/packages/remark-lint-no-file-name-outer-dashes/tsconfig.json new file mode 100644 index 00000000..7e61871a --- /dev/null +++ b/packages/remark-lint-no-file-name-outer-dashes/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.json", + "include": ["*.js"] +} diff --git a/packages/remark-lint-no-heading-content-indent/index.js b/packages/remark-lint-no-heading-content-indent/index.js index d54062cf..362f79c4 100644 --- a/packages/remark-lint-no-heading-content-indent/index.js +++ b/packages/remark-lint-no-heading-content-indent/index.js @@ -50,6 +50,10 @@ * #·· */ +/** + * @typedef {import('mdast').Root} Root + */ + import {lintRule} from 'unified-lint-rule' import {visit} from 'unist-util-visit' import {headingStyle} from 'mdast-util-heading-style' @@ -59,6 +63,7 @@ import {generated} from 'unist-util-generated' const remarkLintNoHeadingContentIndent = lintRule( 'remark-lint:no-heading-content-indent', + /** @type {import('unified-lint-rule').Rule} */ (tree, file) => { visit(tree, 'heading', (node) => { if (generated(node)) { diff --git a/packages/remark-lint-no-heading-content-indent/package.json b/packages/remark-lint-no-heading-content-indent/package.json index 08e63128..5665b595 100644 --- a/packages/remark-lint-no-heading-content-indent/package.json +++ b/packages/remark-lint-no-heading-content-indent/package.json @@ -25,16 +25,29 @@ "sideEffects": false, "type": "module", "main": "index.js", + "types": "index.d.ts", "files": [ + "index.d.ts", "index.js" ], "dependencies": { + "@types/mdast": "^3.0.0", "mdast-util-heading-style": "^2.0.0", "pluralize": "^8.0.0", + "unified": "^10.0.0", "unified-lint-rule": "^1.0.0", "unist-util-generated": "^2.0.0", "unist-util-position": "^4.0.0", "unist-util-visit": "^4.0.0" }, - "xo": false + "scripts": { + "build": "rimraf \"*.d.ts\" && tsc && type-coverage" + }, + "xo": false, + "typeCoverage": { + "atLeast": 100, + "detail": true, + "strict": true, + "ignoreCatch": true + } } diff --git a/packages/remark-lint-no-heading-content-indent/readme.md b/packages/remark-lint-no-heading-content-indent/readme.md index c4803c4b..a93eeba4 100644 --- a/packages/remark-lint-no-heading-content-indent/readme.md +++ b/packages/remark-lint-no-heading-content-indent/readme.md @@ -91,12 +91,18 @@ No messages. ## Install +This package is [ESM only][esm]: +Node 12+ is needed to use it and it must be `imported`ed instead of `required`d. + [npm][]: ```sh npm install remark-lint-no-heading-content-indent ``` +This package exports no identifiers. +The default export is `remarkLintNoHeadingContentIndent`. + ## Use You probably want to use it on the CLI through a config file: @@ -177,6 +183,8 @@ abide by its terms. [chat]: https://github.com/remarkjs/remark/discussions +[esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c + [npm]: https://docs.npmjs.com/cli/install [health]: https://github.com/remarkjs/.github diff --git a/packages/remark-lint-no-heading-content-indent/tsconfig.json b/packages/remark-lint-no-heading-content-indent/tsconfig.json new file mode 100644 index 00000000..7e61871a --- /dev/null +++ b/packages/remark-lint-no-heading-content-indent/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.json", + "include": ["*.js"] +} diff --git a/packages/remark-lint-no-heading-indent/index.js b/packages/remark-lint-no-heading-indent/index.js index 0e58afbd..0a106b94 100644 --- a/packages/remark-lint-no-heading-indent/index.js +++ b/packages/remark-lint-no-heading-indent/index.js @@ -49,6 +49,10 @@ * 8:4: Remove 3 spaces before this heading */ +/** + * @typedef {import('mdast').Root} Root + */ + import {lintRule} from 'unified-lint-rule' import plural from 'pluralize' import {visit} from 'unist-util-visit' @@ -57,6 +61,7 @@ import {generated} from 'unist-util-generated' const remarkLintNoHeadingIndent = lintRule( 'remark-lint:no-heading-indent', + /** @type {import('unified-lint-rule').Rule} */ (tree, file) => { visit(tree, 'heading', (node, _, parent) => { // Note: it’s rather complex to detect what the expected indent is in block diff --git a/packages/remark-lint-no-heading-indent/package.json b/packages/remark-lint-no-heading-indent/package.json index 37258a16..7581d401 100644 --- a/packages/remark-lint-no-heading-indent/package.json +++ b/packages/remark-lint-no-heading-indent/package.json @@ -24,15 +24,28 @@ "sideEffects": false, "type": "module", "main": "index.js", + "types": "index.d.ts", "files": [ + "index.d.ts", "index.js" ], "dependencies": { + "@types/mdast": "^3.0.0", "pluralize": "^8.0.0", + "unified": "^10.0.0", "unified-lint-rule": "^1.0.0", "unist-util-generated": "^2.0.0", "unist-util-position": "^4.0.0", "unist-util-visit": "^4.0.0" }, - "xo": false + "scripts": { + "build": "rimraf \"*.d.ts\" && tsc && type-coverage" + }, + "xo": false, + "typeCoverage": { + "atLeast": 100, + "detail": true, + "strict": true, + "ignoreCatch": true + } } diff --git a/packages/remark-lint-no-heading-indent/readme.md b/packages/remark-lint-no-heading-indent/readme.md index b345f173..e57fc6c8 100644 --- a/packages/remark-lint-no-heading-indent/readme.md +++ b/packages/remark-lint-no-heading-indent/readme.md @@ -77,12 +77,18 @@ Note: `·` represents a space. ## Install +This package is [ESM only][esm]: +Node 12+ is needed to use it and it must be `imported`ed instead of `required`d. + [npm][]: ```sh npm install remark-lint-no-heading-indent ``` +This package exports no identifiers. +The default export is `remarkLintNoHeadingIndent`. + ## Use You probably want to use it on the CLI through a config file: @@ -163,6 +169,8 @@ abide by its terms. [chat]: https://github.com/remarkjs/remark/discussions +[esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c + [npm]: https://docs.npmjs.com/cli/install [health]: https://github.com/remarkjs/.github diff --git a/packages/remark-lint-no-heading-indent/tsconfig.json b/packages/remark-lint-no-heading-indent/tsconfig.json new file mode 100644 index 00000000..7e61871a --- /dev/null +++ b/packages/remark-lint-no-heading-indent/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.json", + "include": ["*.js"] +} diff --git a/packages/remark-lint-no-heading-like-paragraph/index.js b/packages/remark-lint-no-heading-like-paragraph/index.js index 48d8a66b..8f4b917e 100644 --- a/packages/remark-lint-no-heading-like-paragraph/index.js +++ b/packages/remark-lint-no-heading-like-paragraph/index.js @@ -26,6 +26,10 @@ * 1:1-1:16: This looks like a heading but has too many hashes */ +/** + * @typedef {import('mdast').Root} Root + */ + import {lintRule} from 'unified-lint-rule' import {visit} from 'unist-util-visit' import {generated} from 'unist-util-generated' @@ -34,6 +38,7 @@ const fence = '#######' const remarkLintNoHeadingLikeParagraph = lintRule( 'remark-lint:no-heading-like-paragraph', + /** @type {import('unified-lint-rule').Rule} */ (tree, file) => { visit(tree, 'paragraph', (node) => { if (!generated(node)) { diff --git a/packages/remark-lint-no-heading-like-paragraph/package.json b/packages/remark-lint-no-heading-like-paragraph/package.json index ac4512e3..c698dc1e 100644 --- a/packages/remark-lint-no-heading-like-paragraph/package.json +++ b/packages/remark-lint-no-heading-like-paragraph/package.json @@ -24,13 +24,26 @@ "sideEffects": false, "type": "module", "main": "index.js", + "types": "index.d.ts", "files": [ + "index.d.ts", "index.js" ], "dependencies": { + "@types/mdast": "^3.0.0", + "unified": "^10.0.0", "unified-lint-rule": "^1.0.0", "unist-util-generated": "^2.0.0", "unist-util-visit": "^4.0.0" }, - "xo": false + "scripts": { + "build": "rimraf \"*.d.ts\" && tsc && type-coverage" + }, + "xo": false, + "typeCoverage": { + "atLeast": 100, + "detail": true, + "strict": true, + "ignoreCatch": true + } } diff --git a/packages/remark-lint-no-heading-like-paragraph/readme.md b/packages/remark-lint-no-heading-like-paragraph/readme.md index ad66e130..8503ba80 100644 --- a/packages/remark-lint-no-heading-like-paragraph/readme.md +++ b/packages/remark-lint-no-heading-like-paragraph/readme.md @@ -50,12 +50,18 @@ Delta. ## Install +This package is [ESM only][esm]: +Node 12+ is needed to use it and it must be `imported`ed instead of `required`d. + [npm][]: ```sh npm install remark-lint-no-heading-like-paragraph ``` +This package exports no identifiers. +The default export is `remarkLintNoHeadingLikeParagraph`. + ## Use You probably want to use it on the CLI through a config file: @@ -136,6 +142,8 @@ abide by its terms. [chat]: https://github.com/remarkjs/remark/discussions +[esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c + [npm]: https://docs.npmjs.com/cli/install [health]: https://github.com/remarkjs/.github diff --git a/packages/remark-lint-no-heading-like-paragraph/tsconfig.json b/packages/remark-lint-no-heading-like-paragraph/tsconfig.json new file mode 100644 index 00000000..7e61871a --- /dev/null +++ b/packages/remark-lint-no-heading-like-paragraph/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.json", + "include": ["*.js"] +} diff --git a/packages/remark-lint-no-heading-punctuation/index.js b/packages/remark-lint-no-heading-punctuation/index.js index 56ee9a7f..aa012f30 100644 --- a/packages/remark-lint-no-heading-punctuation/index.js +++ b/packages/remark-lint-no-heading-punctuation/index.js @@ -44,19 +44,21 @@ * 9:1-9:9: Don’t add a trailing `;` to headings */ +/** + * @typedef {import('mdast').Root} Root + * @typedef {string} Options + */ + import {lintRule} from 'unified-lint-rule' import {visit} from 'unist-util-visit' import {generated} from 'unist-util-generated' import {toString} from 'mdast-util-to-string' -const defaults = '\\.,;:!?' - const remarkLintNoHeadingPunctuation = lintRule( 'remark-lint:no-heading-punctuation', - (tree, file, option) => { - const expression = new RegExp( - '[' + (typeof option === 'string' ? option : defaults) + ']' - ) + /** @type {import('unified-lint-rule').Rule} */ + (tree, file, option = '\\.,;:!?') => { + const expression = new RegExp('[' + option + ']') visit(tree, 'heading', (node) => { if (!generated(node)) { diff --git a/packages/remark-lint-no-heading-punctuation/package.json b/packages/remark-lint-no-heading-punctuation/package.json index d817d7b4..d7a01283 100644 --- a/packages/remark-lint-no-heading-punctuation/package.json +++ b/packages/remark-lint-no-heading-punctuation/package.json @@ -24,14 +24,27 @@ "sideEffects": false, "type": "module", "main": "index.js", + "types": "index.d.ts", "files": [ + "index.d.ts", "index.js" ], "dependencies": { - "unified-lint-rule": "^1.0.0", + "@types/mdast": "^3.0.0", "mdast-util-to-string": "^3.0.0", + "unified": "^10.0.0", + "unified-lint-rule": "^1.0.0", "unist-util-generated": "^2.0.0", "unist-util-visit": "^4.0.0" }, - "xo": false + "scripts": { + "build": "rimraf \"*.d.ts\" && tsc && type-coverage" + }, + "xo": false, + "typeCoverage": { + "atLeast": 100, + "detail": true, + "strict": true, + "ignoreCatch": true + } } diff --git a/packages/remark-lint-no-heading-punctuation/readme.md b/packages/remark-lint-no-heading-punctuation/readme.md index b7bbe47d..b5719dcb 100644 --- a/packages/remark-lint-no-heading-punctuation/readme.md +++ b/packages/remark-lint-no-heading-punctuation/readme.md @@ -81,12 +81,18 @@ No messages. ## Install +This package is [ESM only][esm]: +Node 12+ is needed to use it and it must be `imported`ed instead of `required`d. + [npm][]: ```sh npm install remark-lint-no-heading-punctuation ``` +This package exports no identifiers. +The default export is `remarkLintNoHeadingPunctuation`. + ## Use You probably want to use it on the CLI through a config file: @@ -167,6 +173,8 @@ abide by its terms. [chat]: https://github.com/remarkjs/remark/discussions +[esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c + [npm]: https://docs.npmjs.com/cli/install [health]: https://github.com/remarkjs/.github diff --git a/packages/remark-lint-no-heading-punctuation/tsconfig.json b/packages/remark-lint-no-heading-punctuation/tsconfig.json new file mode 100644 index 00000000..7e61871a --- /dev/null +++ b/packages/remark-lint-no-heading-punctuation/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.json", + "include": ["*.js"] +} diff --git a/packages/remark-lint-no-html/index.js b/packages/remark-lint-no-html/index.js index a2716c64..6af0dec8 100644 --- a/packages/remark-lint-no-html/index.js +++ b/packages/remark-lint-no-html/index.js @@ -27,16 +27,24 @@ * 1:1-1:15: Do not use HTML in markdown */ +/** + * @typedef {import('mdast').Root} Root + */ + import {lintRule} from 'unified-lint-rule' import {visit} from 'unist-util-visit' import {generated} from 'unist-util-generated' -const remarkLintNoHtml = lintRule('remark-lint:no-html', (tree, file) => { - visit(tree, 'html', (node) => { - if (!generated(node) && !/^\s*`). -// All rules are in their own packages and presets. +/** + * The core plugin for `remark-lint`. + * This adds support for ignoring stuff from messages (``). + * All rules are in their own packages and presets. + * + * @type {import('unified').Plugin} + */ export default function remarkLint() { this.use(lintMessageControl) } +/** @type {import('unified').Plugin} */ function lintMessageControl() { return remarkMessageControl({name: 'lint', source: 'remark-lint'}) } diff --git a/packages/remark-lint/package.json b/packages/remark-lint/package.json index 8137c567..5dcdb6e9 100644 --- a/packages/remark-lint/package.json +++ b/packages/remark-lint/package.json @@ -26,11 +26,24 @@ "sideEffects": false, "type": "module", "main": "index.js", + "types": "index.d.ts", "files": [ + "index.d.ts", "index.js" ], "dependencies": { - "remark-message-control": "^7.0.0" + "@types/mdast": "^3.0.0", + "remark-message-control": "^7.0.0", + "unified": "^10.1.0" }, - "xo": false + "scripts": { + "build": "rimraf \"*.d.ts\" && tsc && type-coverage" + }, + "xo": false, + "typeCoverage": { + "atLeast": 100, + "detail": true, + "strict": true, + "ignoreCatch": true + } } diff --git a/packages/remark-lint/tsconfig.json b/packages/remark-lint/tsconfig.json new file mode 100644 index 00000000..7e61871a --- /dev/null +++ b/packages/remark-lint/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.json", + "include": ["*.js"] +} diff --git a/packages/remark-preset-lint-consistent/package.json b/packages/remark-preset-lint-consistent/package.json index 648bdbe2..579d9faa 100644 --- a/packages/remark-preset-lint-consistent/package.json +++ b/packages/remark-preset-lint-consistent/package.json @@ -23,10 +23,13 @@ "sideEffects": false, "type": "module", "main": "index.js", + "types": "index.d.ts", "files": [ + "index.d.ts", "index.js" ], "dependencies": { + "@types/mdast": "^3.0.0", "remark-lint": "^8.0.0", "remark-lint-blockquote-indentation": "^2.0.0", "remark-lint-checkbox-character-style": "^3.0.0", @@ -39,7 +42,17 @@ "remark-lint-ordered-list-marker-style": "^2.0.0", "remark-lint-rule-style": "^2.0.0", "remark-lint-strong-marker": "^2.0.0", - "remark-lint-table-cell-padding": "^3.0.0" + "remark-lint-table-cell-padding": "^3.0.0", + "unified": "^10.0.0" }, - "xo": false + "scripts": { + "build": "rimraf \"*.d.ts\" && tsc && type-coverage" + }, + "xo": false, + "typeCoverage": { + "atLeast": 100, + "detail": true, + "strict": true, + "ignoreCatch": true + } } diff --git a/packages/remark-preset-lint-consistent/readme.md b/packages/remark-preset-lint-consistent/readme.md index ca844d90..5870e331 100644 --- a/packages/remark-preset-lint-consistent/readme.md +++ b/packages/remark-preset-lint-consistent/readme.md @@ -34,12 +34,18 @@ This preset configures [`remark-lint`](https://github.com/remarkjs/remark-lint) ## Install +This package is [ESM only][esm]: +Node 12+ is needed to use it and it must be `imported`ed instead of `required`d. + [npm][]: ```sh npm install remark-preset-lint-consistent ``` +This package exports no identifiers. +The default export is `remarkPresetLintConsistent`. + ## Use You probably want to use it on the CLI through a config file: @@ -113,6 +119,8 @@ abide by its terms. [chat]: https://github.com/remarkjs/remark/discussions +[esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c + [npm]: https://docs.npmjs.com/cli/install [health]: https://github.com/remarkjs/.github diff --git a/packages/remark-preset-lint-consistent/tsconfig.json b/packages/remark-preset-lint-consistent/tsconfig.json new file mode 100644 index 00000000..7e61871a --- /dev/null +++ b/packages/remark-preset-lint-consistent/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.json", + "include": ["*.js"] +} diff --git a/packages/remark-preset-lint-markdown-style-guide/package.json b/packages/remark-preset-lint-markdown-style-guide/package.json index 2086d7cb..166367dd 100644 --- a/packages/remark-preset-lint-markdown-style-guide/package.json +++ b/packages/remark-preset-lint-markdown-style-guide/package.json @@ -24,55 +24,68 @@ "sideEffects": false, "type": "module", "main": "index.js", + "types": "index.d.ts", "files": [ + "index.d.ts", "index.js" ], "dependencies": { + "@types/mdast": "^3.0.0", "remark-lint": "^8.0.0", + "remark-lint-blockquote-indentation": "^2.0.0", + "remark-lint-code-block-style": "^2.0.0", + "remark-lint-definition-case": "^2.0.0", + "remark-lint-definition-spacing": "^2.0.0", + "remark-lint-emphasis-marker": "^2.0.0", + "remark-lint-fenced-code-flag": "^2.0.0", + "remark-lint-fenced-code-marker": "^2.0.0", "remark-lint-file-extension": "^1.0.0", - "remark-lint-no-file-name-mixed-case": "^1.0.0", - "remark-lint-no-file-name-articles": "^1.0.0", - "remark-lint-no-file-name-irregular-characters": "^1.0.0", - "remark-lint-no-file-name-consecutive-dashes": "^1.0.0", - "remark-lint-no-file-name-outer-dashes": "^1.0.0", - "remark-lint-no-consecutive-blank-lines": "^3.0.0", - "remark-lint-maximum-line-length": "^2.0.0", - "remark-lint-no-shell-dollars": "^2.0.0", + "remark-lint-final-definition": "^2.0.0", "remark-lint-hard-break-spaces": "^2.0.0", - "remark-lint-heading-style": "^2.0.0", "remark-lint-heading-increment": "^2.0.0", - "remark-lint-no-duplicate-headings": "^2.0.0", - "remark-lint-no-multiple-toplevel-headings": "^2.0.0", - "remark-lint-maximum-heading-length": "^2.0.0", - "remark-lint-no-heading-punctuation": "^2.0.0", - "remark-lint-blockquote-indentation": "^2.0.0", - "remark-lint-no-blockquote-without-marker": "^4.0.0", - "remark-lint-unordered-list-marker-style": "^2.0.0", - "remark-lint-ordered-list-marker-style": "^2.0.0", - "remark-lint-ordered-list-marker-value": "^2.0.0", - "remark-lint-list-item-indent": "^2.0.0", + "remark-lint-heading-style": "^2.0.0", + "remark-lint-link-title-style": "^2.0.0", "remark-lint-list-item-content-indent": "^2.0.0", + "remark-lint-list-item-indent": "^2.0.0", "remark-lint-list-item-spacing": "^3.0.0", - "remark-lint-code-block-style": "^2.0.0", - "remark-lint-fenced-code-flag": "^2.0.0", - "remark-lint-fenced-code-marker": "^2.0.0", - "remark-lint-rule-style": "^2.0.0", - "remark-lint-no-table-indentation": "^3.0.0", - "remark-lint-table-pipes": "^3.0.0", - "remark-lint-table-pipe-alignment": "^2.0.0", - "remark-lint-table-cell-padding": "^3.0.0", + "remark-lint-maximum-heading-length": "^2.0.0", + "remark-lint-maximum-line-length": "^2.0.0", + "remark-lint-no-auto-link-without-protocol": "^2.0.0", + "remark-lint-no-blockquote-without-marker": "^4.0.0", + "remark-lint-no-consecutive-blank-lines": "^3.0.0", + "remark-lint-no-duplicate-headings": "^2.0.0", + "remark-lint-no-emphasis-as-heading": "^2.0.0", + "remark-lint-no-file-name-articles": "^1.0.0", + "remark-lint-no-file-name-consecutive-dashes": "^1.0.0", + "remark-lint-no-file-name-irregular-characters": "^1.0.0", + "remark-lint-no-file-name-mixed-case": "^1.0.0", + "remark-lint-no-file-name-outer-dashes": "^1.0.0", + "remark-lint-no-heading-punctuation": "^2.0.0", "remark-lint-no-inline-padding": "^3.0.0", + "remark-lint-no-literal-urls": "^2.0.0", + "remark-lint-no-multiple-toplevel-headings": "^2.0.0", + "remark-lint-no-shell-dollars": "^2.0.0", "remark-lint-no-shortcut-reference-image": "^2.0.0", "remark-lint-no-shortcut-reference-link": "^2.0.0", - "remark-lint-final-definition": "^2.0.0", - "remark-lint-definition-case": "^2.0.0", - "remark-lint-definition-spacing": "^2.0.0", - "remark-lint-link-title-style": "^2.0.0", + "remark-lint-no-table-indentation": "^3.0.0", + "remark-lint-ordered-list-marker-style": "^2.0.0", + "remark-lint-ordered-list-marker-value": "^2.0.0", + "remark-lint-rule-style": "^2.0.0", "remark-lint-strong-marker": "^2.0.0", - "remark-lint-emphasis-marker": "^2.0.0", - "remark-lint-no-emphasis-as-heading": "^2.0.0", - "remark-lint-no-literal-urls": "^2.0.0", - "remark-lint-no-auto-link-without-protocol": "^2.0.0" + "remark-lint-table-cell-padding": "^3.0.0", + "remark-lint-table-pipe-alignment": "^2.0.0", + "remark-lint-table-pipes": "^3.0.0", + "remark-lint-unordered-list-marker-style": "^2.0.0", + "unified": "^10.0.0" + }, + "scripts": { + "build": "rimraf \"*.d.ts\" && tsc && type-coverage" }, - "xo": false + "xo": false, + "typeCoverage": { + "atLeast": 100, + "detail": true, + "strict": true, + "ignoreCatch": true + } } diff --git a/packages/remark-preset-lint-markdown-style-guide/readme.md b/packages/remark-preset-lint-markdown-style-guide/readme.md index d9ebe6da..04ffc439 100644 --- a/packages/remark-preset-lint-markdown-style-guide/readme.md +++ b/packages/remark-preset-lint-markdown-style-guide/readme.md @@ -158,12 +158,18 @@ This preset configures [`remark-lint`](https://github.com/remarkjs/remark-lint) ## Install +This package is [ESM only][esm]: +Node 12+ is needed to use it and it must be `imported`ed instead of `required`d. + [npm][]: ```sh npm install remark-preset-lint-markdown-style-guide ``` +This package exports no identifiers. +The default export is `remarkPresetLintMarkdownStyleGuide`. + ## Use You probably want to use it on the CLI through a config file: @@ -237,6 +243,8 @@ abide by its terms. [chat]: https://github.com/remarkjs/remark/discussions +[esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c + [npm]: https://docs.npmjs.com/cli/install [health]: https://github.com/remarkjs/.github diff --git a/packages/remark-preset-lint-markdown-style-guide/tsconfig.json b/packages/remark-preset-lint-markdown-style-guide/tsconfig.json new file mode 100644 index 00000000..7e61871a --- /dev/null +++ b/packages/remark-preset-lint-markdown-style-guide/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.json", + "include": ["*.js"] +} diff --git a/packages/remark-preset-lint-recommended/package.json b/packages/remark-preset-lint-recommended/package.json index f49c8bd4..6809c942 100644 --- a/packages/remark-preset-lint-recommended/package.json +++ b/packages/remark-preset-lint-recommended/package.json @@ -22,26 +22,39 @@ "sideEffects": false, "type": "module", "main": "index.js", + "types": "index.d.ts", "files": [ + "index.d.ts", "index.js" ], "dependencies": { + "@types/mdast": "^3.0.0", "remark-lint": "^8.0.0", "remark-lint-final-newline": "^1.0.0", + "remark-lint-hard-break-spaces": "^2.0.0", "remark-lint-list-item-bullet-indent": "^3.0.0", "remark-lint-list-item-indent": "^2.0.0", "remark-lint-no-auto-link-without-protocol": "^2.0.0", "remark-lint-no-blockquote-without-marker": "^4.0.0", - "remark-lint-no-literal-urls": "^2.0.0", - "remark-lint-ordered-list-marker-style": "^2.0.0", - "remark-lint-hard-break-spaces": "^2.0.0", "remark-lint-no-duplicate-definitions": "^2.0.0", "remark-lint-no-heading-content-indent": "^3.0.0", "remark-lint-no-inline-padding": "^3.0.0", + "remark-lint-no-literal-urls": "^2.0.0", "remark-lint-no-shortcut-reference-image": "^2.0.0", "remark-lint-no-shortcut-reference-link": "^2.0.0", "remark-lint-no-undefined-references": "^3.0.0", - "remark-lint-no-unused-definitions": "^2.0.0" + "remark-lint-no-unused-definitions": "^2.0.0", + "remark-lint-ordered-list-marker-style": "^2.0.0", + "unified": "^10.0.0" + }, + "scripts": { + "build": "rimraf \"*.d.ts\" && tsc && type-coverage" }, - "xo": false + "xo": false, + "typeCoverage": { + "atLeast": 100, + "detail": true, + "strict": true, + "ignoreCatch": true + } } diff --git a/packages/remark-preset-lint-recommended/readme.md b/packages/remark-preset-lint-recommended/readme.md index 2e76c0ff..09c4b30f 100644 --- a/packages/remark-preset-lint-recommended/readme.md +++ b/packages/remark-preset-lint-recommended/readme.md @@ -37,12 +37,18 @@ This preset configures [`remark-lint`](https://github.com/remarkjs/remark-lint) ## Install +This package is [ESM only][esm]: +Node 12+ is needed to use it and it must be `imported`ed instead of `required`d. + [npm][]: ```sh npm install remark-preset-lint-recommended ``` +This package exports no identifiers. +The default export is `remarkPresetLintRecommended`. + ## Use You probably want to use it on the CLI through a config file: @@ -116,6 +122,8 @@ abide by its terms. [chat]: https://github.com/remarkjs/remark/discussions +[esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c + [npm]: https://docs.npmjs.com/cli/install [health]: https://github.com/remarkjs/.github diff --git a/packages/remark-preset-lint-recommended/tsconfig.json b/packages/remark-preset-lint-recommended/tsconfig.json new file mode 100644 index 00000000..7e61871a --- /dev/null +++ b/packages/remark-preset-lint-recommended/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.json", + "include": ["*.js"] +} diff --git a/packages/unified-lint-rule/index.d.ts b/packages/unified-lint-rule/index.d.ts new file mode 100644 index 00000000..450d01cf --- /dev/null +++ b/packages/unified-lint-rule/index.d.ts @@ -0,0 +1,22 @@ +import type {Node} from 'unist' +import type {VFile} from 'vfile' +import type {Plugin} from 'unified' +import type {Label, Severity} from './lib/index.js' + +export function lintRule( + name: string, + rule: Rule +): Plugin< + | void[] + | [Settings | Label | Severity] + | [boolean | Label | Severity, Settings], + Tree +> + +export type Rule = ( + node: Tree, + file: VFile, + settings: Settings +) => Promise | Tree | undefined | void + +export type {Severity, Label} diff --git a/packages/unified-lint-rule/index.js b/packages/unified-lint-rule/index.js index a7bcc156..dc0efbd8 100644 --- a/packages/unified-lint-rule/index.js +++ b/packages/unified-lint-rule/index.js @@ -1,97 +1 @@ -import {wrap} from 'trough' - -export function lintRule(id, rule) { - const parts = id.split(':') - // Possibly useful if externalised later. - /* c8 ignore next */ - const source = parts[1] ? parts[0] : undefined - const ruleId = parts[1] - - Object.defineProperty(plugin, 'name', {value: id}) - - return plugin - - function plugin(raw) { - const [severity, options] = coerce(ruleId, raw) - - if (!severity) return - - const fatal = severity === 2 - - return (tree, file, next) => { - let index = file.messages.length - 1 - - wrap(rule, (error) => { - const messages = file.messages - - // Add the error, if not already properly added. - // Only happens for incorrect plugins. - /* c8 ignore next 5 */ - if (error && !messages.includes(error)) { - try { - file.fail(error) - } catch {} - } - - while (++index < messages.length) { - Object.assign(messages[index], {ruleId, source, fatal}) - } - - next() - })(tree, file, options) - } - } -} - -// Coerce a value to a severity--options tuple. -function coerce(name, value) { - const def = 1 - let result - - if (typeof value === 'boolean') { - result = [value] - } else if (value === null || value === undefined) { - result = [def] - } else if ( - Array.isArray(value) && - (typeof value[0] === 'number' || - typeof value[0] === 'boolean' || - typeof value[0] === 'string') - ) { - result = [...value] - } else { - result = [1, value] - } - - let level = result[0] - - if (typeof level === 'boolean') { - level = level ? 1 : 0 - } else if (typeof level === 'string') { - if (level === 'off') { - level = 0 - } else if (level === 'on' || level === 'warn') { - level = 1 - } else if (level === 'error') { - level = 2 - } else { - level = 1 - result = [level, result] - } - } - - if (level < 0 || level > 2) { - throw new Error( - 'Incorrect severity `' + - level + - '` for `' + - name + - '`, ' + - 'expected 0, 1, or 2' - ) - } - - result[0] = level - - return result -} +export {lintRule} from './lib/index.js' diff --git a/packages/unified-lint-rule/lib/index.js b/packages/unified-lint-rule/lib/index.js new file mode 100644 index 00000000..e06c26fb --- /dev/null +++ b/packages/unified-lint-rule/lib/index.js @@ -0,0 +1,129 @@ +/** + * @typedef {import('unist').Node} Node + * @typedef {import('vfile').VFile} VFile + * + * @typedef {0|1|2} Severity + * @typedef {'warn'|'on'|'off'|'error'} Label + * @typedef {[Severity, ...unknown[]]} SeverityTuple + * + * @callback Rule + * @param {Node} tree + * @param {VFile} file + * @param {unknown} options + * @returns {void} + */ + +import {wrap} from 'trough' + +const primitives = new Set(['string', 'number', 'boolean']) + +/** + * @param {string} id + * @param {Rule} rule + */ +export function lintRule(id, rule) { + const parts = id.split(':') + // Possibly useful if externalised later. + /* c8 ignore next */ + const source = parts[1] ? parts[0] : undefined + const ruleId = parts[1] + + Object.defineProperty(plugin, 'name', {value: id}) + + return plugin + + /** @type {import('unified').Plugin<[unknown]|void[]>} */ + function plugin(raw) { + const [severity, options] = coerce(ruleId, raw) + + if (!severity) return + + const fatal = severity === 2 + + return (tree, file, next) => { + let index = file.messages.length - 1 + + wrap(rule, (error) => { + const messages = file.messages + + // Add the error, if not already properly added. + // Only happens for incorrect plugins. + /* c8 ignore next 6 */ + // @ts-expect-error: errors could be `messages`. + if (error && !messages.includes(error)) { + try { + file.fail(error) + } catch {} + } + + while (++index < messages.length) { + Object.assign(messages[index], {ruleId, source, fatal}) + } + + next() + })(tree, file, options) + } + } +} + +/** + * Coerce a value to a severity--options tuple. + * + * @param {string} name + * @param {unknown} value + * @returns {SeverityTuple} + */ +function coerce(name, value) { + /** @type {unknown[]} */ + let result + + if (typeof value === 'boolean') { + result = [value] + } else if (value === null || value === undefined) { + result = [1] + } else if ( + Array.isArray(value) && + // `isArray(unknown)` is turned into `any[]`: + // type-coverage:ignore-next-line + primitives.has(typeof value[0]) + ) { + // `isArray(unknown)` is turned into `any[]`: + // type-coverage:ignore-next-line + result = [...value] + } else { + result = [1, value] + } + + let level = result[0] + + if (typeof level === 'boolean') { + level = level ? 1 : 0 + } else if (typeof level === 'string') { + if (level === 'off') { + level = 0 + } else if (level === 'on' || level === 'warn') { + level = 1 + } else if (level === 'error') { + level = 2 + } else { + level = 1 + result = [level, result] + } + } + + if (typeof level !== 'number' || level < 0 || level > 2) { + throw new Error( + 'Incorrect severity `' + + level + + '` for `' + + name + + '`, ' + + 'expected 0, 1, or 2' + ) + } + + result[0] = level + + // @ts-expect-error: it’s now a valid tuple. + return result +} diff --git a/packages/unified-lint-rule/package.json b/packages/unified-lint-rule/package.json index 63794e27..83097fef 100644 --- a/packages/unified-lint-rule/package.json +++ b/packages/unified-lint-rule/package.json @@ -23,11 +23,26 @@ "sideEffects": false, "type": "module", "main": "index.js", + "types": "index.d.ts", "files": [ + "lib/", + "index.d.ts", "index.js" ], "dependencies": { - "trough": "^2.0.0" + "@types/unist": "^2.0.0", + "trough": "^2.0.0", + "unified": "^10.0.0", + "vfile": "^5.0.0" }, - "xo": false + "scripts": { + "build": "rimraf \"lib/**/*.d.ts\" && tsc && type-coverage" + }, + "xo": false, + "typeCoverage": { + "atLeast": 100, + "detail": true, + "strict": true, + "ignoreCatch": true + } } diff --git a/packages/unified-lint-rule/tsconfig.json b/packages/unified-lint-rule/tsconfig.json new file mode 100644 index 00000000..4f4b4b06 --- /dev/null +++ b/packages/unified-lint-rule/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.json", + "include": ["lib/**/*.js"] +} diff --git a/script/build-presets.js b/script/build-presets.js index 316b0073..15a9fa06 100644 --- a/script/build-presets.js +++ b/script/build-presets.js @@ -1,7 +1,12 @@ +/** + * @typedef {import('type-fest').PackageJson} PackageJson + * @typedef {import('mdast').TableContent} TableContent + * @typedef {import('mdast').BlockContent|import('mdast').DefinitionContent} BlockContent + */ + import fs from 'fs' import path from 'path' import {inspect} from 'util' -import {u} from 'unist-builder' import {parse} from 'comment-parser' import {remark} from 'remark' import remarkGfm from 'remark-gfm' @@ -9,29 +14,45 @@ import strip from 'strip-indent' import parseAuthor from 'parse-author' import {presets} from './util/presets.js' -const pkg = JSON.parse(fs.readFileSync('package.json')) +/** @type {PackageJson} */ +const pkg = JSON.parse(String(fs.readFileSync('package.json'))) const remote = pkg.repository +if (typeof remote !== 'string') { + throw new TypeError( + 'Expected `string` for `repository` in root `package.json`' + ) +} + const own = {}.hasOwnProperty const root = path.join(process.cwd(), 'packages') presets(root).then((presetObjects) => { let index = -1 - while (++index < presetObjects) { + + while (++index < presetObjects.length) { const {name, packages} = presetObjects[index] const base = path.resolve(root, name) - const pack = JSON.parse(fs.readFileSync(path.join(base, 'package.json'))) + /** @type {PackageJson} */ + const pack = JSON.parse( + String(fs.readFileSync(path.join(base, 'package.json'))) + ) const doc = fs.readFileSync(path.join(base, 'index.js'), 'utf8') const tags = parse(doc, {spacing: 'preserve'})[0].tags - const author = parseAuthor(pack.author) - const description = strip( - tags.find((d) => d.tag === 'fileoverview').description - ).trim() - const rows = [] - let children + const author = + typeof pack.author === 'string' ? parseAuthor(pack.author) : pack.author + const fileoverviewTag = tags.find((d) => d.tag === 'fileoverview') + + if (!fileoverviewTag) { + throw new Error('Expected `@fileoverview` in jsdoc') + } + + const description = strip(fileoverviewTag.description).trim() const short = name.replace(/^remark-/, '') - const camelcased = name.replace(/-(\w)/g, (_, $1) => $1.toUpperCase()) + const camelcased = name.replace(/-(\w)/g, (_, /** @type {string} */ $1) => + $1.toUpperCase() + ) const org = remote.split('/').slice(0, -1).join('/') const main = remote + '/blob/main' const health = org + '/.github' @@ -49,13 +70,18 @@ presets(root).then((presetObjects) => { ) } - rows.push( - u('tableRow', [ - u('tableCell', [u('text', 'Rule')]), - u('tableCell', [u('text', 'Setting')]) - ]) - ) + /** @type {TableContent[]} */ + const rows = [] + rows.push({ + type: 'tableRow', + children: [ + {type: 'tableCell', children: [{type: 'text', value: 'Rule'}]}, + {type: 'tableCell', children: [{type: 'text', value: 'Setting'}]} + ] + }) + + /** @type {string} */ let rule for (rule in packages) { @@ -65,94 +91,254 @@ presets(root).then((presetObjects) => { if (rule === 'remark-lint') continue - rows.push( - u('tableRow', [ - u('tableCell', [ - u('link', {url, title: null}, [u('inlineCode', rule)]) - ]), - u('tableCell', option ? [u('inlineCode', inspect(option))] : []) - ]) - ) + rows.push({ + type: 'tableRow', + children: [ + { + type: 'tableCell', + children: [ + { + type: 'link', + url, + title: null, + children: [{type: 'inlineCode', value: rule}] + } + ] + }, + { + type: 'tableCell', + children: option + ? [{type: 'inlineCode', value: inspect(option)}] + : [] + } + ] + }) } } - children = [ - u('html', ''), - u('heading', {depth: 1}, [u('text', name)]), - u('paragraph', [ - u('linkReference', {identifier: 'build'}, [ - u('imageReference', {identifier: 'build-badge', alt: 'Build'}) - ]), - u('text', '\n'), - u('linkReference', {identifier: 'coverage'}, [ - u('imageReference', {identifier: 'coverage-badge', alt: 'Coverage'}) - ]), - u('text', '\n'), - u('linkReference', {identifier: 'downloads'}, [ - u('imageReference', {identifier: 'downloads-badge', alt: 'Downloads'}) - ]), - u('text', '\n'), - u('linkReference', {identifier: 'size'}, [ - u('imageReference', {identifier: 'size-badge', alt: 'Size'}) - ]), - u('text', '\n'), - u('linkReference', {identifier: 'collective'}, [ - u('imageReference', {identifier: 'sponsors-badge', alt: 'Sponsors'}) - ]), - u('text', '\n'), - u('linkReference', {identifier: 'collective'}, [ - u('imageReference', {identifier: 'backers-badge', alt: 'Backers'}) - ]), - u('text', '\n'), - u('linkReference', {identifier: 'chat'}, [ - u('imageReference', {identifier: 'chat-badge', alt: 'Chat'}) - ]) - ]) - ] - - children = children.concat(remark().parse(description).children) + /** @type {BlockContent[]} */ + // @ts-expect-error: fine. + const descriptionContent = remark().parse(description).children - children.push( - u('heading', {depth: 2}, [u('text', 'Rules')]), - u('paragraph', [ - u('text', 'This preset configures '), - u('link', {url: remote}, [u('inlineCode', 'remark-lint')]), - u('text', ' with the following rules:') - ]), - u('table', {align: []}, rows), - u('heading', {depth: 2}, [u('text', 'Install')]), - u('paragraph', [ - u('linkReference', {identifier: 'npm', referenceType: 'collapsed'}, [ - u('text', 'npm') - ]), - u('text', ':') - ]), - u('code', {lang: 'sh'}, 'npm install ' + name), - u('heading', {depth: 2}, [u('text', 'Use')]), - u('paragraph', [ - u( - 'text', - 'You probably want to use it on the CLI through a config file:' - ) - ]), - u( - 'code', - {lang: 'diff'}, - [ + /** @type {BlockContent[]} */ + 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' + } + ] + } + ] + }, + ...descriptionContent, + { + type: 'heading', + depth: 2, + children: [{type: 'text', value: 'Rules'}] + }, + { + type: 'paragraph', + children: [ + {type: 'text', value: 'This preset configures '}, + { + type: 'link', + url: remote, + 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: ':\nNode 12+ is needed to use it and it must be ' + }, + {type: 'inlineCode', value: 'imported'}, + {type: 'text', value: 'ed instead of '}, + {type: 'inlineCode', value: 'required'}, + {type: 'text', value: 'd.'} + ] + }, + { + type: 'paragraph', + children: [ + { + 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: + 'This package exports no identifiers.\nThe default export is ' + }, + { + type: 'inlineCode', + value: name.replace(/-(\w)/g, (_, /** @type {string} */ $1) => + $1.toUpperCase() + ) + }, + {type: 'text', value: '.'} + ] + }, + {type: 'heading', depth: 2, children: [{type: 'text', value: 'Use'}]}, + { + type: 'paragraph', + children: [ + { + type: 'text', + value: + 'You probably want to use it on the CLI through a config file:' + } + ] + }, + { + type: 'code', + lang: 'diff', + value: [ ' …', ' "remarkConfig": {', '+ "plugins": ["' + short + '"]', ' }', ' …' ].join('\n') - ), - u('paragraph', [u('text', 'Or use it on the CLI directly')]), - u('code', {lang: 'sh'}, 'remark -u ' + short + ' readme.md'), - u('paragraph', [u('text', 'Or use this on the API:')]), - u( - 'code', - {lang: 'diff'}, - [ + }, + { + type: 'paragraph', + children: [{type: 'text', value: 'Or use it on the CLI directly'}] + }, + {type: 'code', lang: 'sh', value: 'remark -u ' + short + ' readme.md'}, + { + type: 'paragraph', + children: [{type: 'text', value: 'Or use this on the API:'}] + }, + { + type: 'code', + lang: 'diff', + value: [ " import {remark} from 'remark'", " import {reporter} from 'vfile-reporter'", ' import ' + camelcased + " from '" + name + "'", @@ -164,110 +350,189 @@ presets(root).then((presetObjects) => { ' console.error(reporter(file))', ' })' ].join('\n') - ), - u('heading', {depth: 2}, [u('text', 'Contribute')]), - u('paragraph', [ - u('text', 'See '), - u('linkReference', {identifier: 'contributing'}, [ - u('inlineCode', 'contributing.md') - ]), - u('text', ' in '), - u('linkReference', {identifier: 'health'}, [ - u('inlineCode', health.split('/').slice(-2).join('/')) - ]), - u('text', ' for ways\nto get started.\nSee '), - u('linkReference', {identifier: 'support'}, [ - u('inlineCode', 'support.md') - ]), - u('text', ' for ways to get help.') - ]), - u('paragraph', [ - u('text', 'This project has a '), - u('linkReference', {identifier: 'coc'}, [u('text', 'code of conduct')]), - u( - 'text', - '.\nBy interacting with this repository, organization, or community you agree to\nabide by its terms.' - ) - ]), - u('heading', {depth: 2}, [u('text', 'License')]), - u('paragraph', [ - u('linkReference', {identifier: 'license'}, [u('text', pack.license)]), - u('text', ' © '), - u('linkReference', {identifier: 'author'}, [u('text', author.name)]) - ]), - u('definition', { + }, + { + 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' - }), - u('definition', { + }, + { + type: 'definition', identifier: 'build', url: 'https://github.com/' + slug + '/actions' - }), - u('definition', { + }, + { + type: 'definition', identifier: 'coverage-badge', url: 'https://img.shields.io/codecov/c/github/' + slug + '.svg' - }), - u('definition', { + }, + { + type: 'definition', identifier: 'coverage', url: 'https://codecov.io/github/' + slug - }), - u('definition', { + }, + { + type: 'definition', identifier: 'downloads-badge', url: 'https://img.shields.io/npm/dm/' + name + '.svg' - }), - u('definition', { + }, + { + type: 'definition', identifier: 'downloads', url: 'https://www.npmjs.com/package/' + name - }), - u('definition', { + }, + { + type: 'definition', identifier: 'size-badge', url: 'https://img.shields.io/bundlephobia/minzip/' + name + '.svg' - }), - u('definition', { + }, + { + type: 'definition', identifier: 'size', url: 'https://bundlephobia.com/result?p=' + name - }), - u('definition', { + }, + { + type: 'definition', identifier: 'sponsors-badge', url: 'https://opencollective.com/unified/sponsors/badge.svg' - }), - u('definition', { + }, + { + type: 'definition', identifier: 'backers-badge', url: 'https://opencollective.com/unified/backers/badge.svg' - }), - u('definition', { + }, + { + type: 'definition', identifier: 'collective', url: 'https://opencollective.com/unified' - }), - u('definition', { + }, + { + type: 'definition', identifier: 'chat-badge', url: 'https://img.shields.io/badge/chat-discussions-success.svg' - }), - u('definition', { + }, + { + type: 'definition', identifier: 'chat', url: 'https://github.com/remarkjs/remark/discussions' - }), - u('definition', { + }, + { + type: 'definition', + identifier: 'esm', + url: 'https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c' + }, + { + type: 'definition', identifier: 'npm', url: 'https://docs.npmjs.com/cli/install' - }), - u('definition', {identifier: 'health', url: health}), - u('definition', { + }, + { + type: 'definition', + identifier: 'health', + url: health + }, + { + type: 'definition', identifier: 'contributing', url: hMain + '/contributing.md' - }), - u('definition', {identifier: 'support', url: hMain + '/support.md'}), - u('definition', { + }, + { + type: 'definition', + identifier: 'support', + url: hMain + '/support.md' + }, + { + type: 'definition', identifier: 'coc', url: hMain + '/code-of-conduct.md' - }), - u('definition', {identifier: 'license', url: main + '/license'}), - u('definition', {identifier: 'author', url: author.url}) - ) + }, + { + type: 'definition', + identifier: 'license', + url: main + '/license' + }, + { + type: 'definition', + identifier: 'author', + url: String((author && author.url) || '') + } + ] fs.writeFileSync( path.join(base, 'readme.md'), - remark().use(remarkGfm).stringify(u('root', children)) + remark().use(remarkGfm).stringify({type: 'root', children}) ) console.log('✓ wrote `readme.md` in `' + name + '`') diff --git a/script/build-rules.js b/script/build-rules.js index ad019464..e43e7bef 100644 --- a/script/build-rules.js +++ b/script/build-rules.js @@ -1,7 +1,12 @@ +/** + * @typedef {import('type-fest').PackageJson} PackageJson + * @typedef {import('mdast').BlockContent|import('mdast').DefinitionContent} BlockContent + * @typedef {import('mdast').TableContent} TableContent + */ + import fs from 'fs' import path from 'path' import {inspect} from 'util' -import {u} from 'unist-builder' import {remark} from 'remark' import remarkGfm from 'remark-gfm' import parseAuthor from 'parse-author' @@ -12,10 +17,19 @@ import {characters} from './characters.js' const own = {}.hasOwnProperty -const pkg = JSON.parse(fs.readFileSync('package.json')) +/** @type {PackageJson} */ +const pkg = JSON.parse(String(fs.readFileSync('package.json'))) const remote = pkg.repository + +if (typeof remote !== 'string') { + throw new TypeError( + 'Expected `string` for `repository` in root `package.json`' + ) +} + const root = path.join(process.cwd(), 'packages') +// eslint-disable-next-line complexity presets(root).then((presetObjects) => { const allRules = rules(root) let index = -1 @@ -23,51 +37,29 @@ presets(root).then((presetObjects) => { while (++index < allRules.length) { const basename = allRules[index] const base = path.resolve(root, basename) - const pack = JSON.parse(fs.readFileSync(path.join(base, 'package.json'))) + /** @type {PackageJson} */ + const pack = JSON.parse( + String(fs.readFileSync(path.join(base, 'package.json'))) + ) const info = rule(base) const tests = info.tests - const author = parseAuthor(pack.author) + const author = + typeof pack.author === 'string' ? parseAuthor(pack.author) : pack.author const short = basename.replace(/^remark-/, '') - const camelcased = basename.replace(/-(\w)/g, (_, $1) => $1.toUpperCase()) + const camelcased = basename.replace( + /-(\w)/g, + (_, /** @type {string} */ $1) => $1.toUpperCase() + ) const org = remote.split('/').slice(0, -1).join('/') const main = remote + '/blob/main' const health = org + '/.github' const hMain = health + '/blob/HEAD' const slug = remote.split('/').slice(-2).join('/') let hasGfm = false - let children = [ - u('html', ''), - u('heading', {depth: 1}, [u('text', basename)]), - u('paragraph', [ - u('linkReference', {identifier: 'build'}, [ - u('imageReference', {identifier: 'build-badge', alt: 'Build'}) - ]), - u('text', '\n'), - u('linkReference', {identifier: 'coverage'}, [ - u('imageReference', {identifier: 'coverage-badge', alt: 'Coverage'}) - ]), - u('text', '\n'), - u('linkReference', {identifier: 'downloads'}, [ - u('imageReference', {identifier: 'downloads-badge', alt: 'Downloads'}) - ]), - u('text', '\n'), - u('linkReference', {identifier: 'size'}, [ - u('imageReference', {identifier: 'size-badge', alt: 'Size'}) - ]), - u('text', '\n'), - u('linkReference', {identifier: 'collective'}, [ - u('imageReference', {identifier: 'sponsors-badge', alt: 'Sponsors'}) - ]), - u('text', '\n'), - u('linkReference', {identifier: 'collective'}, [ - u('imageReference', {identifier: 'backers-badge', alt: 'Backers'}) - ]), - u('text', '\n'), - u('linkReference', {identifier: 'chat'}, [ - u('imageReference', {identifier: 'chat-badge', alt: 'Chat'}) - ]) - ]) - ].concat(remark().parse(info.description).children) + + /** @type {BlockContent[]} */ + // @ts-expect-error: fine. + const descriptionContent = remark().parse(info.description).children if (basename !== pack.name) { throw new Error( @@ -83,56 +75,214 @@ presets(root).then((presetObjects) => { return basename in preset.packages }) - children.push(u('heading', {depth: 2}, [u('text', 'Presets')])) + /** @type {BlockContent[]} */ + const children = [ + {type: 'html', value: ''}, + { + type: 'heading', + depth: 1, + children: [{type: 'text', value: basename}] + }, + { + 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' + } + ] + } + ] + }, + ...descriptionContent, + { + type: 'heading', + depth: 2, + children: [{type: 'text', value: 'Presets'}] + } + ] if (includes.length === 0) { - children.push( - u('paragraph', [ - u('text', 'This rule is not included in any default preset') - ]) - ) + children.push({ + type: 'paragraph', + children: [ + { + type: 'text', + value: 'This rule is not included in any default preset' + } + ] + }) } else { children.push( - u('paragraph', [ - u('text', 'This rule is included in the following presets:') - ]), - u( - 'table', - {align: []}, - [ - u('tableRow', [ - u('tableCell', [u('text', 'Preset')]), - u('tableCell', [u('text', 'Setting')]) - ]) - ].concat( - includes.map((preset) => { - const url = remote + '/tree/main/packages/' + preset.name - const option = preset.packages[pack.name] - - return u('tableRow', [ - u('tableCell', [ - u('link', {url, title: null}, [u('inlineCode', preset.name)]) - ]), - u('tableCell', option ? [u('inlineCode', inspect(option))] : []) - ]) + { + type: 'paragraph', + children: [ + { + type: 'text', + value: 'This rule is included in the following presets:' + } + ] + }, + { + type: 'table', + align: [], + children: [ + { + type: 'tableRow', + children: [ + { + type: 'tableCell', + children: [{type: 'text', value: 'Preset'}] + }, + { + type: 'tableCell', + children: [{type: 'text', value: 'Setting'}] + } + ] + }, + ...includes.map((preset) => { + const option = preset.packages[basename] + + /** @type {TableContent} */ + const row = { + type: 'tableRow', + children: [ + { + type: 'tableCell', + children: [ + { + type: 'link', + url: remote + '/tree/main/packages/' + preset.name, + title: null, + children: [{type: 'inlineCode', value: preset.name}] + } + ] + }, + { + type: 'tableCell', + children: option + ? [{type: 'inlineCode', value: inspect(option)}] + : [] + } + ] + } + + return row }) - ) - ) + ] + } ) } - let setting let first = true + /** @type {string} */ + let setting for (setting in tests) { if (own.call(tests, setting)) { const fixtures = tests[setting] if (first) { - children.push(u('heading', {depth: 2}, [u('text', 'Example')])) + children.push({ + type: 'heading', + depth: 2, + children: [{type: 'text', value: 'Example'}] + }) first = false } + /** @type {string} */ let fileName for (fileName in fixtures) { @@ -141,16 +291,21 @@ presets(root).then((presetObjects) => { const label = inspect(JSON.parse(setting)) let clean = fixture.input - children.push(u('heading', {depth: 5}, [u('inlineCode', fileName)])) + children.push({ + type: 'heading', + depth: 5, + children: [{type: 'inlineCode', value: fileName}] + }) if (label !== 'true') { - children.push( - u('paragraph', [ - u('text', 'When configured with '), - u('inlineCode', label), - u('text', '.') - ]) - ) + children.push({ + type: 'paragraph', + children: [ + {type: 'text', value: 'When configured with '}, + {type: 'inlineCode', value: label}, + {type: 'text', value: '.'} + ] + }) } if ( @@ -158,21 +313,28 @@ presets(root).then((presetObjects) => { fixture.input !== undefined && fixture.input.trim() !== '' ) { - children.push(u('heading', {depth: 6}, [u('text', 'In')])) + children.push({ + type: 'heading', + depth: 6, + children: [{type: 'text', value: 'In'}] + }) if (fixture.gfm) { hasGfm = true - children.push( - u('paragraph', [ - u('text', 'Note: this example uses '), - u( - 'linkReference', - {label: 'GFM', referenceType: 'collapsed'}, - [u('text', 'GFM')] - ), - u('text', '.') - ]) - ) + children.push({ + type: 'paragraph', + children: [ + {type: 'text', value: 'Note: this example uses '}, + { + type: 'linkReference', + label: 'GFM', + identifier: 'gfm', + referenceType: 'collapsed', + children: [{type: 'text', value: 'GFM'}] + }, + {type: 'text', value: '.'} + ] + }) } let index = -1 @@ -181,55 +343,120 @@ presets(root).then((presetObjects) => { const next = clean.replace(char.in, char.out) if (clean !== next) { - children.push( - u('paragraph', [ - u('text', 'Note: '), - u('inlineCode', char.char), - u('text', ' represents ' + char.name + '.') - ]) - ) + children.push({ + type: 'paragraph', + children: [ + {type: 'text', value: 'Note: '}, + {type: 'inlineCode', value: char.char}, + {type: 'text', value: ' represents ' + char.name + '.'} + ] + }) clean = next } } - children.push(u('code', {lang: 'markdown'}, fixture.input)) + children.push({ + type: 'code', + lang: 'markdown', + value: fixture.input + }) } - children.push(u('heading', {depth: 6}, [u('text', 'Out')])) + children.push({ + type: 'heading', + depth: 6, + children: [{type: 'text', value: 'Out'}] + }) if (fixture.output.length === 0) { - children.push(u('paragraph', [u('text', 'No messages.')])) + children.push({ + type: 'paragraph', + children: [{type: 'text', value: 'No messages.'}] + }) } else { - children.push( - u('code', {lang: 'text'}, fixture.output.join('\n')) - ) + children.push({ + type: 'code', + lang: 'text', + value: fixture.output.join('\n') + }) } } } } } - children = children.concat([ - u('heading', {depth: 2}, [u('text', 'Install')]), - u('paragraph', [ - u('linkReference', {identifier: 'npm', referenceType: 'collapsed'}, [ - u('text', 'npm') - ]), - u('text', ':') - ]), - u('code', {lang: 'sh'}, 'npm install ' + basename), - u('heading', {depth: 2}, [u('text', 'Use')]), - u('paragraph', [ - u( - 'text', - 'You probably want to use it on the CLI through a config file:' - ) - ]), - u( - 'code', - {lang: 'diff'}, - [ + children.push( + { + type: 'heading', + depth: 2, + children: [{type: 'text', value: 'Install'}] + }, + { + type: 'paragraph', + children: [ + {type: 'text', value: 'This package is '}, + { + type: 'linkReference', + identifier: 'esm', + referenceType: 'full', + children: [{type: 'text', value: 'ESM only'}] + }, + { + type: 'text', + value: ':\nNode 12+ is needed to use it and it must be ' + }, + {type: 'inlineCode', value: 'imported'}, + {type: 'text', value: 'ed instead of '}, + {type: 'inlineCode', value: 'required'}, + {type: 'text', value: 'd.'} + ] + }, + { + type: 'paragraph', + children: [ + { + type: 'linkReference', + identifier: 'npm', + referenceType: 'collapsed', + children: [{type: 'text', value: 'npm'}] + }, + {type: 'text', value: ':'} + ] + }, + {type: 'code', lang: 'sh', value: 'npm install ' + basename}, + { + type: 'paragraph', + children: [ + { + type: 'text', + value: + 'This package exports no identifiers.\nThe default export is ' + }, + { + type: 'inlineCode', + value: basename.replace(/-(\w)/g, (_, /** @type {string} */ $1) => + $1.toUpperCase() + ) + }, + {type: 'text', value: '.'} + ] + }, + {type: 'heading', depth: 2, children: [{type: 'text', value: 'Use'}]}, + { + type: 'paragraph', + children: [ + { + type: 'text', + value: + 'You probably want to use it on the CLI through a config file:' + } + ] + }, + { + type: 'code', + lang: 'diff', + value: [ ' …', ' "remarkConfig": {', ' "plugins": [', @@ -241,14 +468,24 @@ presets(root).then((presetObjects) => { ' }', ' …' ].join('\n') - ), - u('paragraph', [u('text', 'Or use it on the CLI directly')]), - u('code', {lang: 'sh'}, 'remark -u lint -u ' + short + ' readme.md'), - u('paragraph', [u('text', 'Or use this on the API:')]), - u( - 'code', - {lang: 'diff'}, - [ + }, + { + type: 'paragraph', + children: [{type: 'text', value: 'Or use it on the CLI directly'}] + }, + { + type: 'code', + lang: 'sh', + value: 'remark -u lint -u ' + short + ' readme.md' + }, + { + type: 'paragraph', + children: [{type: 'text', value: 'Or use this on the API:'}] + }, + { + type: 'code', + lang: 'diff', + value: [ " import {remark} from 'remark'", " import {reporter} from 'vfile-reporter'", " import remarkLint from 'remark-lint'", @@ -262,119 +499,197 @@ presets(root).then((presetObjects) => { ' console.error(reporter(file))', ' })' ].join('\n') - ), - u('heading', {depth: 2}, [u('text', 'Contribute')]), - u('paragraph', [ - u('text', 'See '), - u('linkReference', {identifier: 'contributing'}, [ - u('inlineCode', 'contributing.md') - ]), - u('text', ' in '), - u('linkReference', {identifier: 'health'}, [ - u('inlineCode', health.split('/').slice(-2).join('/')) - ]), - u('text', ' for ways\nto get started.\nSee '), - u('linkReference', {identifier: 'support'}, [ - u('inlineCode', 'support.md') - ]), - u('text', ' for ways to get help.') - ]), - u('paragraph', [ - u('text', 'This project has a '), - u('linkReference', {identifier: 'coc'}, [u('text', 'code of conduct')]), - u( - 'text', - '.\nBy interacting with this repository, organization, or community you agree to\nabide by its terms.' - ) - ]), - u('heading', {depth: 2}, [u('text', 'License')]), - u('paragraph', [ - u('linkReference', {identifier: 'license'}, [u('text', pack.license)]), - u('text', ' © '), - u('linkReference', {identifier: 'author'}, [u('text', author.name)]) - ]), - u('definition', { + }, + { + 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' - }), - u('definition', { + }, + { + type: 'definition', identifier: 'build', url: 'https://github.com/' + slug + '/actions' - }), - u('definition', { + }, + { + type: 'definition', identifier: 'coverage-badge', url: 'https://img.shields.io/codecov/c/github/' + slug + '.svg' - }), - u('definition', { + }, + { + type: 'definition', identifier: 'coverage', url: 'https://codecov.io/github/' + slug - }), - u('definition', { + }, + { + type: 'definition', identifier: 'downloads-badge', url: 'https://img.shields.io/npm/dm/' + basename + '.svg' - }), - u('definition', { + }, + { + type: 'definition', identifier: 'downloads', url: 'https://www.npmjs.com/package/' + basename - }), - u('definition', { + }, + { + type: 'definition', identifier: 'size-badge', url: 'https://img.shields.io/bundlephobia/minzip/' + basename + '.svg' - }), - u('definition', { + }, + { + type: 'definition', identifier: 'size', url: 'https://bundlephobia.com/result?p=' + basename - }), - u('definition', { + }, + { + type: 'definition', identifier: 'sponsors-badge', url: 'https://opencollective.com/unified/sponsors/badge.svg' - }), - u('definition', { + }, + { + type: 'definition', identifier: 'backers-badge', url: 'https://opencollective.com/unified/backers/badge.svg' - }), - u('definition', { + }, + { + type: 'definition', identifier: 'collective', url: 'https://opencollective.com/unified' - }), - u('definition', { + }, + { + type: 'definition', identifier: 'chat-badge', url: 'https://img.shields.io/badge/chat-discussions-success.svg' - }), - u('definition', { + }, + { + type: 'definition', identifier: 'chat', url: 'https://github.com/remarkjs/remark/discussions' - }), - u('definition', { + }, + { + type: 'definition', + identifier: 'esm', + url: 'https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c' + }, + { + type: 'definition', identifier: 'npm', url: 'https://docs.npmjs.com/cli/install' - }), - u('definition', {identifier: 'health', url: health}), - u('definition', { + }, + { + type: 'definition', + identifier: 'health', + url: health + }, + { + type: 'definition', identifier: 'contributing', url: hMain + '/contributing.md' - }), - u('definition', {identifier: 'support', url: hMain + '/support.md'}), - u('definition', { + }, + { + type: 'definition', + identifier: 'support', + url: hMain + '/support.md' + }, + { + type: 'definition', identifier: 'coc', url: hMain + '/code-of-conduct.md' - }), - u('definition', {identifier: 'license', url: main + '/license'}), - u('definition', {identifier: 'author', url: author.url}) - ]) + }, + { + type: 'definition', + identifier: 'license', + url: main + '/license' + }, + { + type: 'definition', + identifier: 'author', + url: String((author && author.url) || '') + } + ) if (hasGfm) { - children.push( - u('definition', { - identifier: 'gfm', - url: 'https://github.com/remarkjs/remark-gfm' - }) - ) + children.push({ + type: 'definition', + identifier: 'gfm', + url: 'https://github.com/remarkjs/remark-gfm' + }) } fs.writeFileSync( path.join(base, 'readme.md'), - remark().use(remarkGfm).stringify(u('root', children)) + remark().use(remarkGfm).stringify({type: 'root', children}) ) console.log('✓ wrote `readme.md` in `' + basename + '`') diff --git a/script/plugin/list-of-presets.js b/script/plugin/list-of-presets.js index b90f1831..9de6e559 100644 --- a/script/plugin/list-of-presets.js +++ b/script/plugin/list-of-presets.js @@ -1,42 +1,64 @@ +/** + * @typedef {import('type-fest').PackageJson} PackageJson + * @typedef {import('mdast').Root} Root + * @typedef {import('mdast').List} List + * @typedef {import('mdast').ListItem} ListItem + */ + import fs from 'fs' import path from 'path' import {zone} from 'mdast-zone' -import {u} from 'unist-builder' import {presets} from '../util/presets.js' const root = path.join(process.cwd(), 'packages') +/** @type {import('unified').Plugin} */ export default function listOfPresets() { const presetPromise = presets(root) return async (tree) => { const presetObjects = await presetPromise - zone(tree, 'presets', (start, nodes, end) => { - return [ - start, - u( - 'list', - {ordered: false, spread: false}, - presetObjects.map(({name}) => { - const pack = JSON.parse( - fs.readFileSync(path.join(root, name, 'package.json')) - ) - const description = pack.description.replace( - /^remark preset to configure remark-lint with ?/i, - '' - ) - - return u('listItem', {spread: false}, [ - u('paragraph', [ - u('link', {url: pack.repository}, [u('inlineCode', name)]), - u('text', ' — ' + description) - ]) - ]) - }) - ), - end - ] + zone(tree, 'presets', (start, _, end) => { + /** @type {List} */ + const list = { + type: 'list', + ordered: false, + spread: false, + children: presetObjects.map(({name}) => { + /** @type {PackageJson} */ + const pack = JSON.parse( + String(fs.readFileSync(path.join(root, name, 'package.json'))) + ) + const description = String(pack.description || '').replace( + /^remark preset to configure remark-lint with ?/i, + '' + ) + + /** @type {ListItem} */ + const item = { + type: 'listItem', + spread: false, + children: [ + { + type: 'paragraph', + children: [ + { + type: 'link', + url: String(pack.repository || ''), + children: [{type: 'inlineCode', value: name}] + }, + {type: 'text', value: ' — ' + description} + ] + } + ] + } + + return item + }) + } + + return [start, list, end] }) } } diff --git a/script/plugin/list-of-rules.js b/script/plugin/list-of-rules.js index 7f6ee992..f6cdc1a0 100644 --- a/script/plugin/list-of-rules.js +++ b/script/plugin/list-of-rules.js @@ -1,43 +1,61 @@ +/** + * @typedef {import('type-fest').PackageJson} PackageJson + * @typedef {import('mdast').ListItem} ListItem + * @typedef {import('mdast').List} List + * @typedef {import('mdast').Root} Root + */ + import fs from 'fs' import path from 'path' import {zone} from 'mdast-zone' -import {u} from 'unist-builder' import {rules} from '../util/rules.js' const root = path.join(process.cwd(), 'packages') +/** @type {import('unified').Plugin} */ export default function listOfRules() { - return transformer -} + return (tree) => { + zone(tree, 'rules', (start, _, end) => { + /** @type {List} */ + const list = { + type: 'list', + ordered: false, + spread: false, + children: rules(root).map((basename) => { + const name = basename.slice('remark-lint-'.length) + /** @type {PackageJson} */ + const pack = JSON.parse( + String(fs.readFileSync(path.join(root, basename, 'package.json'))) + ) + const description = String(pack.description || '').replace( + /^remark-lint rule to ?/i, + '' + ) -function transformer(tree) { - zone(tree, 'rules', replace) -} + /** @type {ListItem} */ + const item = { + type: 'listItem', + spread: false, + children: [ + { + type: 'paragraph', + children: [ + { + type: 'link', + url: String(pack.repository || ''), + children: [{type: 'inlineCode', value: name}] + }, + {type: 'text', value: ' — ' + description} + ] + } + ] + } -function replace(start, nodes, end) { - return [ - start, - u( - 'list', - {ordered: false, spread: false}, - rules(root).map((basename) => { - const name = basename.slice('remark-lint-'.length) - const pack = JSON.parse( - fs.readFileSync(path.join(root, basename, 'package.json')) - ) - const description = pack.description.replace( - /^remark-lint rule to ?/i, - '' - ) + return item + }) + } - return u('listItem', {spread: false}, [ - u('paragraph', [ - u('link', {url: pack.repository}, [u('inlineCode', name)]), - u('text', ' — ' + description) - ]) - ]) - }) - ), - end - ] + return [start, list, end] + }) + } } diff --git a/script/util/presets.js b/script/util/presets.js index 7e1c3e3b..061c9c8c 100644 --- a/script/util/presets.js +++ b/script/util/presets.js @@ -1,7 +1,16 @@ +/** + * @typedef {import('unified').Preset} Preset + * @typedef {import('unified').Plugin} Plugin + */ + import {promises as fs} from 'fs' import path from 'path' import url from 'url' +/** + * @param {string} base + * @returns {Promise}>>} + */ export async function presets(base) { const files = (await fs.readdir(base)).filter((basename) => /remark-preset-lint/.test(basename) @@ -9,30 +18,52 @@ export async function presets(base) { return Promise.all( files.map(async (name) => { - const plugins = ( - await import(url.pathToFileURL(path.join(base, name, 'index.js'))) - ).default.plugins + const href = url.pathToFileURL(path.join(base, name, 'index.js')).href + /** @type {Preset} */ + // type-coverage:ignore-next-line + const preset = (await import(href)).default + const plugins = preset.plugins || [] + /** @type {Record} */ const packages = {} let index = -1 while (++index < plugins.length) { const plugin = plugins[index] + /** @type {Plugin} */ let fn + /** @type {unknown} */ let option if (typeof plugin === 'function') { fn = plugin - } else { + } else if (Array.isArray(plugin)) { + // Fine: + // type-coverage:ignore-next-line fn = plugin[0] + // Fine: + // type-coverage:ignore-next-line option = plugin[1] + } else { + throw new TypeError( + 'Expected plugin, plugin tuple, not `' + plugin + '`' + ) } + /** @type {string} */ + // @ts-expect-error: `displayName`s are fine. const name = fn.displayName || fn.name packages[ name - .replace(/[:-](\w)/g, (_, $1) => $1.toUpperCase()) - .replace(/[A-Z]/g, ($0) => '-' + $0.toLowerCase()) + .replace( + /[:-](\w)/g, + (/** @type {string} */ _, /** @type {string} */ $1) => + $1.toUpperCase() + ) + .replace( + /[A-Z]/g, + (/** @type {string} */ $0) => '-' + $0.toLowerCase() + ) ] = option } diff --git a/script/util/rule.js b/script/util/rule.js index a54a6247..4e75fee7 100755 --- a/script/util/rule.js +++ b/script/util/rule.js @@ -1,17 +1,52 @@ +/** + * @typedef Rule + * @property {string} ruleId + * @property {string} description + * @property {Record} tests + * @property {string} filePath + * + * @typedef {Record} Checks + * + * @typedef Check + * @property {string} input + * @property {string[]} output + * @property {string} setting + * @property {boolean} gfm + * @property {boolean} positionless + */ + import fs from 'fs' import path from 'path' import {parse} from 'comment-parser' import strip from 'strip-indent' -// Get information for a rule at `filePath`. +/** + * Get information for a rule at `filePath`. + * + * @param {string} filePath + * @returns {Rule} + */ export function rule(filePath) { const ruleId = path.basename(filePath).slice('remark-lint-'.length) - const result = {} + /** @type {Record} */ const tests = {} const code = fs.readFileSync(path.join(filePath, 'index.js'), 'utf-8') const tags = parse(code, {spacing: 'preserve'})[0].tags - const name = tags.find((d) => d.tag === 'module').name - let description = tags.find((d) => d.tag === 'fileoverview').description + const moduleTag = tags.find((d) => d.tag === 'module') + const fileoverviewTag = tags.find((d) => d.tag === 'fileoverview') + + /* c8 ignore next 3 */ + if (!moduleTag) { + throw new Error('Expected `@module` in JSDoc') + } + + /* c8 ignore next 3 */ + if (!fileoverviewTag) { + throw new Error('Expected `@fileoverview` in JSDoc') + } + + const name = moduleTag.name + let description = fileoverviewTag.description /* c8 ignore next 3 */ if (name !== ruleId) { @@ -25,10 +60,13 @@ export function rule(filePath) { description = strip(description) - result.ruleId = ruleId - result.description = description.trim() - result.tests = tests - result.filePath = filePath + /** @type {Rule} */ + const result = { + ruleId, + description: description.trim(), + tests, + filePath + } const examples = tags .filter((d) => d.tag === 'example') @@ -37,6 +75,7 @@ export function rule(filePath) { while (++index < examples.length) { const lines = examples[index].split('\n') + /** @type {{name: string, label?: 'input'|'output', setting?: unknown, positionless?: boolean, gfm?: boolean}} */ let info try { @@ -52,17 +91,12 @@ export function rule(filePath) { const exampleValue = strip(lines.join('\n').replace(/^\r?\n/g, '')) const setting = JSON.stringify(info.setting || true) const name = info.name - let context = tests[setting] - - if (!context) { - context = [] - tests[setting] = context - } + const context = setting in tests ? tests[setting] : (tests[setting] = {}) if (!info.label) { context[name] = { - positionless: info.positionless, - gfm: info.gfm, + positionless: info.positionless || false, + gfm: info.gfm || false, setting, input: exampleValue, output: [] @@ -83,7 +117,13 @@ export function rule(filePath) { } if (!context[name]) { - context[name] = {positionless: info.positionless, gfm: info.gfm} + context[name] = { + positionless: info.positionless || false, + gfm: info.gfm || false, + setting: '', + input: '', + output: [] + } } context[name].setting = setting diff --git a/script/util/rules.js b/script/util/rules.js index 6de22224..28f78b5f 100644 --- a/script/util/rules.js +++ b/script/util/rules.js @@ -1,5 +1,9 @@ import fs from 'fs' +/** + * @param {string} filePath + * @returns {string[]} + */ export function rules(filePath) { return fs .readdirSync(filePath) diff --git a/test.js b/test.js index 5984a702..0a6aa0a1 100644 --- a/test.js +++ b/test.js @@ -1,3 +1,11 @@ +/** + * @typedef {import('unified').Plugin} Plugin + * @typedef {import('vfile-message').VFileMessage} VFileMessage + * @typedef {import('tape').Test} Test + * @typedef {import('./script/util/rule').Rule} Rule + * @typedef {import('./script/util/rule').Check} Check + */ + import url from 'url' import path from 'path' import test from 'tape' @@ -15,240 +23,204 @@ import finalNewline from './packages/remark-lint-final-newline/index.js' const own = {}.hasOwnProperty -test('core', (t) => { - t.test('should work', (t) => { - const doc = [ - '# A heading', - '', - '# Another main heading.', - '', - '', - '', - '# Another main heading.' - ].join('\n') - - t.plan(2) - - remark() - .use(noHeadingPunctuation) - .use(noMultipleToplevelHeadings) - .use(lint) - .process(toVFile({path: 'virtual.md', value: doc}), (error, file) => { - t.deepEqual( - [error].concat(file.messages.map((d) => String(d))), - [ - null, - 'virtual.md:3:1-3:24: Don’t add a trailing `.` to headings', - 'virtual.md:3:1-3:24: Don’t use multiple top level headings (1:1)' - ], - 'should support `remark-lint` last' - ) - }) +test('core', async (t) => { + const doc = [ + '# A heading', + '', + '# Another main heading.', + '', + '', + '', + '# Another main heading.' + ].join('\n') + + t.deepEqual( + asStrings( + ( + await remark() + .use(noHeadingPunctuation) + .use(noMultipleToplevelHeadings) + .use(lint) + .process(toVFile({path: 'virtual.md', value: doc})) + ).messages + ), + [ + 'virtual.md:3:1-3:24: Don’t add a trailing `.` to headings', + 'virtual.md:3:1-3:24: Don’t use multiple top level headings (1:1)' + ], + 'should support `remark-lint` last' + ) - remark() - .use(lint) - .use(noHeadingPunctuation) - .use(noMultipleToplevelHeadings) - .process(toVFile({path: 'virtual.md', value: doc}), (error, file) => { - t.deepEqual( - [error].concat(file.messages.map((d) => String(d))), - [ - null, - 'virtual.md:3:1-3:24: Don’t add a trailing `.` to headings', - 'virtual.md:3:1-3:24: Don’t use multiple top level headings (1:1)' - ], - 'should support `remark-lint` first' - ) - }) - }) - - t.test('should support no rules', (t) => { - t.plan(1) - - remark() - .use(lint) - .process('.', (error, file) => { - t.deepEqual( - [error].concat(file.messages.map((d) => String(d))), - [null], - 'should warn for missing new lines' - ) - }) - }) - - t.test('should support successful rules', (t) => { - t.plan(1) - - remark() - .use(finalNewline) - .process('', (error, file) => { - t.deepEqual( - [error].concat(file.messages.map((d) => String(d))), - [null], - 'should support successful rules' - ) - }) - }) - - t.test('should support a list with a severity', (t) => { - t.plan(2) - - remark() - .use(finalNewline, [2]) - .process('.', (error, file) => { - t.deepEqual( - [error].concat(file.messages.map((d) => String(d))), - [null, '1:1: Missing newline character at end of file'], - 'should emit fatally (1)' - ) - t.equal(file.messages[0].fatal, true, 'should emit fatally (2)') - }) - }) - - t.test('should support a boolean (`true`)', (t) => { - // Note! This is handled by unified. - t.plan(1) - - remark() - .use(finalNewline, true) - .process('.', (error, file) => { - t.deepEqual( - [error].concat(file.messages.map((d) => String(d))), - [null, '1:1: Missing newline character at end of file'], - 'should emit' - ) - }) - }) - - t.test('should support a boolean (`false`)', (t) => { - // Note! This is handled by unified. - t.plan(1) - - remark() - .use(finalNewline, false) - .process('.', (error, file) => { - t.deepEqual( - [error].concat(file.messages.map((d) => String(d))), - [null], - 'should not emit' - ) - }) - }) - - t.test( - 'should support a list with a boolean severity (true, for on)', - (t) => { - t.plan(1) - - remark() - .use(finalNewline, [true]) - .process('.', (error, file) => { - t.deepEqual( - [error].concat(file.messages.map((d) => String(d))), - [null, '1:1: Missing newline character at end of file'], - 'should emit' - ) - }) - } + t.deepEqual( + asStrings( + ( + await remark() + .use(lint) + .use(noHeadingPunctuation) + .use(noMultipleToplevelHeadings) + .process(toVFile({path: 'virtual.md', value: doc})) + ).messages + ), + [ + 'virtual.md:3:1-3:24: Don’t add a trailing `.` to headings', + 'virtual.md:3:1-3:24: Don’t use multiple top level headings (1:1)' + ], + 'should support `remark-lint` first' ) - t.test( - 'should support a list with boolean severity (false, for off)', - (t) => { - t.plan(1) - - remark() - .use(finalNewline, [false]) - .process('.', (error, file) => { - t.deepEqual( - [error].concat(file.messages.map((d) => String(d))), - [null], - 'should not emit' - ) - }) - } + t.deepEqual( + asStrings((await remark().use(lint).process('.')).messages), + [], + 'should support no rules' ) - t.test('should support a list with string severity (`error`)', (t) => { - t.plan(2) - - remark() - .use(finalNewline, ['error']) - .process('.', (error, file) => { - t.deepEqual( - [error].concat(file.messages.map((d) => String(d))), - [null, '1:1: Missing newline character at end of file'], - 'should emit fatally (1)' - ) - t.equal(file.messages[0].fatal, true, 'should emit fatally (2)') - }) - }) - - t.test('should support a list with a string severity (`on`)', (t) => { - t.plan(2) - - remark() - .use(finalNewline, ['on']) - .process('.', (error, file) => { - t.deepEqual( - [error].concat(file.messages.map((d) => String(d))), - [null, '1:1: Missing newline character at end of file'], - 'should message' - ) - t.equal(file.messages[0].fatal, false, 'should not emit fatally') - }) - }) - - t.test('should support a list with a string severity (`warn`)', (t) => { - t.plan(2) - - remark() - .use(finalNewline, ['warn']) - .process('.', (error, file) => { - t.deepEqual( - [error].concat(file.messages.map((d) => String(d))), - [null, '1:1: Missing newline character at end of file'], - 'should message' - ) - t.equal(file.messages[0].fatal, false, 'should not emit fatally') - }) - }) - - t.test('should support a list with a string severity (`off`)', (t) => { - t.plan(1) - - remark() - .use(finalNewline, ['off']) - .process('.', (error, file) => { - t.deepEqual( - [error].concat(file.messages.map((d) => String(d))), - [null], - 'should disable `final-newline`' - ) - }) - }) - - t.test('should fail on incorrect severities', (t) => { - t.throws( - () => { - remark().use(finalNewline, [3]).freeze() - }, - /^Error: Incorrect severity `3` for `final-newline`, expected 0, 1, or 2$/, - 'should throw when too high' - ) - - t.throws( - () => { - remark().use(finalNewline, [-1]).freeze() - }, - /^Error: Incorrect severity `-1` for `final-newline`, expected 0, 1, or 2$/, - 'should throw too low' - ) - - t.end() - }) + t.deepEqual( + asStrings((await remark().use(finalNewline).process('')).messages), + [], + 'should support successful rules' + ) - t.end() + t.deepEqual( + (await remark().use(finalNewline, [2]).process('.')).messages.map((d) => + JSON.parse(JSON.stringify(d)) + ), + [ + { + name: '1:1', + message: 'Missing newline character at end of file', + reason: 'Missing newline character at end of file', + line: null, + column: null, + source: 'remark-lint', + ruleId: 'final-newline', + position: { + start: {line: null, column: null}, + end: {line: null, column: null} + }, + fatal: true + } + ], + 'should support a list with a severity' + ) + + t.deepEqual( + asStrings((await remark().use(finalNewline, true).process('.')).messages), + ['1:1: Missing newline character at end of file'], + 'should support a boolean (`true`)' + ) + + t.deepEqual( + asStrings((await remark().use(finalNewline, false).process('.')).messages), + [], + 'should support a boolean (`false`)' + ) + + t.deepEqual( + asStrings((await remark().use(finalNewline, [true]).process('.')).messages), + ['1:1: Missing newline character at end of file'], + 'should support a list with a boolean severity (true, for on)' + ) + + t.deepEqual( + asStrings( + (await remark().use(finalNewline, [false]).process('.')).messages + ), + [], + 'should support a list with boolean severity (false, for off)' + ) + + t.deepEqual( + (await remark().use(finalNewline, ['error']).process('.')).messages.map( + (d) => JSON.parse(JSON.stringify(d)) + ), + [ + { + name: '1:1', + message: 'Missing newline character at end of file', + reason: 'Missing newline character at end of file', + line: null, + column: null, + source: 'remark-lint', + ruleId: 'final-newline', + position: { + start: {line: null, column: null}, + end: {line: null, column: null} + }, + fatal: true + } + ], + 'should support a list with string severity (`error`)' + ) + + t.deepEqual( + (await remark().use(finalNewline, ['on']).process('.')).messages.map((d) => + JSON.parse(JSON.stringify(d)) + ), + [ + { + name: '1:1', + message: 'Missing newline character at end of file', + reason: 'Missing newline character at end of file', + line: null, + column: null, + source: 'remark-lint', + ruleId: 'final-newline', + position: { + start: {line: null, column: null}, + end: {line: null, column: null} + }, + fatal: false + } + ], + 'should support a list with string severity (`on`)' + ) + + t.deepEqual( + (await remark().use(finalNewline, ['warn']).process('.')).messages.map( + (d) => JSON.parse(JSON.stringify(d)) + ), + [ + { + name: '1:1', + message: 'Missing newline character at end of file', + reason: 'Missing newline character at end of file', + line: null, + column: null, + source: 'remark-lint', + ruleId: 'final-newline', + position: { + start: {line: null, column: null}, + end: {line: null, column: null} + }, + fatal: false + } + ], + 'should support a list with string severity (`warn`)' + ) + + t.deepEqual( + asStrings( + (await remark().use(finalNewline, ['off']).process('.')).messages + ), + [], + 'should support a list with string severity (`off`)' + ) + + t.throws( + () => { + remark().use(finalNewline, [3]).freeze() + }, + /^Error: Incorrect severity `3` for `final-newline`, expected 0, 1, or 2$/, + 'should fail on incorrect severities (too high)' + ) + + t.throws( + () => { + remark().use(finalNewline, [-1]).freeze() + }, + /^Error: Incorrect severity `-1` for `final-newline`, expected 0, 1, or 2$/, + 'should fail on incorrect severities (too low)' + ) }) test('rules', async (t) => { @@ -260,8 +232,11 @@ test('rules', async (t) => { const basename = all[index] const base = path.resolve(root, basename) const info = rule(base) - const fn = (await import(url.pathToFileURL(base).href + '/index.js')) - .default + const href = url.pathToFileURL(base).href + '/index.js' + + /** @type {Plugin} */ + // type-coverage:ignore-next-line + const fn = (await import(href)).default if (Object.keys(info.tests).length === 0) { t.pass(info.ruleId + ': no tests') @@ -275,17 +250,26 @@ test('rules', async (t) => { t.end() }) -// Assert a rule. +/** + * Assert a rule. + * + * @param {Test} t + * @param {Plugin} rule + * @param {Rule} info + */ function assertRule(t, rule, info) { const tests = info.tests + /** @type {string} */ let setting for (setting in tests) { if (own.call(tests, setting)) { const checks = tests[setting] + /** @type {unknown} */ const options = JSON.parse(setting) t.test(setting, (t) => { + /** @type {string} */ let name for (name in checks) { @@ -305,6 +289,14 @@ function assertRule(t, rule, info) { t.end() } +/** + * @param {Test} t + * @param {Plugin} rule + * @param {Rule} info + * @param {Check} fixture + * @param {string} basename + * @param {unknown} settings + */ /* eslint-disable-next-line max-params */ function assertFixture(t, rule, info, fixture, basename, settings) { const ruleId = info.ruleId @@ -346,25 +338,36 @@ function assertFixture(t, rule, info, fixture, basename, settings) { if (!positionless) { file.messages = [] - proc = remark().use(clear).use(rule, settings) + proc = remark() + .use(() => (tree) => removePosition(tree)) + .use(rule, settings) if (fixture.gfm) proc.use(remarkGfm) proc.processSync(file) t.deepEqual(normalize(file.messages), [], 'should equal without position') } - - function clear() { - return removePosition - } } +/** + * @param {VFileMessage[]} messages + * @returns {string[]} + */ function normalize(messages) { - return messages.map((message) => { - const value = String(message) - return value.slice(value.indexOf(':') + 1) - }) + return asStrings(messages).map((value) => value.slice(value.indexOf(':') + 1)) +} + +/** + * @param {VFileMessage[]} messages + * @returns {string[]} + */ +function asStrings(messages) { + return messages.map((message) => String(message)) } +/** + * @param {string} value + * @returns {string} + */ function preprocess(value) { let index = -1 diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 00000000..3c7623ba --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,17 @@ +{ + "include": ["script/**/*.js", "test.js"], + "compilerOptions": { + "target": "ES2020", + "lib": ["ES2020"], + "module": "ES2020", + "moduleResolution": "node", + "resolveJsonModule": true, + "allowJs": true, + "checkJs": true, + "declaration": true, + "emitDeclarationOnly": true, + "allowSyntheticDefaultImports": true, + "skipLibCheck": true, + "strict": true + } +}