From 28d4e501b7d31643bb6006bb7826591941f07b85 Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Wed, 11 Aug 2021 13:05:30 +0200 Subject: [PATCH] Refactor code-style --- package.json | 35 +- .../index.js | 33 +- .../index.js | 140 ++-- .../index.js | 79 +-- .../remark-lint-code-block-style/index.js | 28 +- packages/remark-lint-definition-case/index.js | 47 +- .../remark-lint-definition-spacing/index.js | 44 +- packages/remark-lint-emphasis-marker/index.js | 64 +- .../remark-lint-fenced-code-flag/index.js | 69 +- .../remark-lint-fenced-code-marker/index.js | 83 ++- packages/remark-lint-file-extension/index.js | 18 +- .../remark-lint-final-definition/index.js | 64 +- packages/remark-lint-final-newline/index.js | 18 +- .../remark-lint-first-heading-level/index.js | 54 +- .../remark-lint-hard-break-spaces/index.js | 40 +- .../remark-lint-heading-increment/index.js | 39 +- packages/remark-lint-heading-style/index.js | 12 +- packages/remark-lint-linebreak-style/index.js | 70 +- .../remark-lint-link-title-style/index.js | 175 +++-- .../index.js | 56 +- .../index.js | 107 ++- .../remark-lint-list-item-indent/index.js | 114 ++-- .../remark-lint-list-item-spacing/index.js | 95 +-- .../index.js | 22 +- .../remark-lint-maximum-line-length/index.js | 105 ++- .../index.js | 37 +- .../index.js | 58 +- .../index.js | 90 ++- .../index.js | 49 +- .../index.js | 52 +- .../index.js | 48 +- .../index.js | 49 +- .../index.js | 45 +- packages/remark-lint-no-empty-url/index.js | 25 +- .../index.js | 16 +- .../index.js | 14 +- .../index.js | 29 +- .../index.js | 18 +- .../index.js | 14 +- .../index.js | 101 ++- .../remark-lint-no-heading-indent/index.js | 46 +- .../index.js | 44 +- .../index.js | 41 +- packages/remark-lint-no-html/index.js | 20 +- .../remark-lint-no-inline-padding/index.js | 36 +- packages/remark-lint-no-literal-urls/index.js | 38 +- .../index.js | 54 +- .../index.js | 37 +- .../index.js | 107 ++- .../index.js | 59 +- .../remark-lint-no-shell-dollars/index.js | 68 +- .../index.js | 20 +- .../index.js | 20 +- .../remark-lint-no-table-indentation/index.js | 98 ++- packages/remark-lint-no-tabs/index.js | 22 +- .../index.js | 294 ++++---- .../index.js | 35 +- .../index.js | 39 +- .../index.js | 62 +- .../index.js | 77 +-- .../index.js | 96 +-- packages/remark-lint-rule-style/index.js | 54 +- .../remark-lint-strikethrough-marker/index.js | 70 +- packages/remark-lint-strong-marker/index.js | 62 +- .../remark-lint-table-cell-padding/index.js | 198 +++--- .../remark-lint-table-pipe-alignment/index.js | 89 +-- packages/remark-lint-table-pipes/index.js | 46 +- .../index.js | 80 ++- packages/unified-lint-rule/index.js | 67 +- script.js | 45 -- script/build-presets.js | 467 ++++++------- script/build-rules.js | 642 +++++++++--------- script/plugin/list-of-presets.js | 57 +- script/plugin/list-of-rules.js | 39 +- script/util/find.js | 8 +- script/util/presets.js | 16 +- script/util/rule.js | 55 +- script/util/rules.js | 10 +- test.js | 322 ++++----- 79 files changed, 2748 insertions(+), 3148 deletions(-) delete mode 100644 script.js diff --git a/package.json b/package.json index e2e2c177..bef668f4 100644 --- a/package.json +++ b/package.json @@ -51,7 +51,7 @@ "to-vfile": "^7.0.0", "unist-builder": "^3.0.0", "unist-util-remove-position": "^4.0.0", - "xo": "^0.38.0" + "xo": "^0.39.0" }, "scripts": { "postinstall": "lerna bootstrap --no-ci", @@ -73,27 +73,20 @@ }, "xo": { "prettier": true, - "esnext": false, "rules": { - "unicorn/explicit-length-check": "off", - "unicorn/no-array-callback-reference": "off", - "unicorn/no-array-for-each": "off", - "unicorn/no-new-array": "off", - "unicorn/prefer-default-parameters": "off", - "unicorn/prefer-includes": "off", - "unicorn/prefer-number-properties": "off", - "unicorn/prefer-optional-catch-binding": "off", - "unicorn/string-content": "off", - "guard-for-in": "off", - "max-depth": "off", - "no-eq-null": "off", - "eqeqeq": [ - "error", - "always", - { - "null": "ignore" + "unicorn/prefer-switch": "off" + }, + "overrides": [ + { + "files": [ + "test.js", + "script/**/*.js" + ], + "rules": { + "max-depth": "off", + "no-await-in-loop": "off" } - ] - } + } + ] } } diff --git a/packages/remark-lint-blockquote-indentation/index.js b/packages/remark-lint-blockquote-indentation/index.js index 666319cb..d2dcf196 100644 --- a/packages/remark-lint-blockquote-indentation/index.js +++ b/packages/remark-lint-blockquote-indentation/index.js @@ -59,38 +59,33 @@ const remarkLintBlockquoteIndentation = lintRule( export default remarkLintBlockquoteIndentation function blockquoteIndentation(tree, file, option) { - var preferred = typeof option === 'number' && !isNaN(option) ? option : null - - visit(tree, 'blockquote', visitor) - - function visitor(node) { - var abs - var diff - var reason + let preferred = typeof option === 'number' ? option : null + visit(tree, 'blockquote', (node) => { if (generated(node) || node.children.length === 0) { return } if (preferred) { - diff = preferred - check(node) + const diff = preferred - check(node) if (diff !== 0) { - abs = Math.abs(diff) - reason = - (diff > 0 ? 'Add' : 'Remove') + - ' ' + - abs + - ' ' + - plural('space', abs) + - ' between block quote and content' + const abs = Math.abs(diff) - file.message(reason, 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) } - } + }) } function check(node) { diff --git a/packages/remark-lint-checkbox-character-style/index.js b/packages/remark-lint-checkbox-character-style/index.js index f8e647ef..4ce091a7 100644 --- a/packages/remark-lint-checkbox-character-style/index.js +++ b/packages/remark-lint-checkbox-character-style/index.js @@ -74,86 +74,78 @@ import {visit} from 'unist-util-visit' import {pointStart, pointEnd} 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', - checkboxCharacterStyle -) - -export default remarkLintCheckboxCharacterStyle - -var checked = {x: true, X: true} -var unchecked = {' ': true, '\t': true} -var types = {true: 'checked', false: 'unchecked'} - -function checkboxCharacterStyle(tree, file, option) { - var contents = String(file) - var preferred = typeof option === 'object' ? option : {} - - if (preferred.unchecked && unchecked[preferred.unchecked] !== true) { - file.fail( - 'Incorrect unchecked checkbox marker `' + - preferred.unchecked + - "`: use either `'\\t'`, or `' '`" - ) - } - - if (preferred.checked && checked[preferred.checked] !== true) { - file.fail( - 'Incorrect checked checkbox marker `' + - preferred.checked + - "`: use either `'x'`, or `'X'`" - ) - } - - visit(tree, 'listItem', visitor) - - function visitor(node) { - var type - var point - var value - var style - var reason + (tree, file, option) => { + const value = String(file) + const preferred = typeof option === 'object' ? option : {} + + if (preferred.unchecked && unchecked[preferred.unchecked] !== true) { + file.fail( + 'Incorrect unchecked checkbox marker `' + + preferred.unchecked + + "`: use either `'\\t'`, or `' '`" + ) + } - // Exit early for items without checkbox. - if (typeof node.checked !== 'boolean' || generated(node)) { - return + if (preferred.checked && checked[preferred.checked] !== true) { + file.fail( + 'Incorrect checked checkbox marker `' + + preferred.checked + + "`: use either `'x'`, or `'X'`" + ) } - type = types[node.checked] + visit(tree, 'listItem', (node) => { + // Exit early for items without checkbox. + if (typeof node.checked !== 'boolean' || generated(node)) { + return + } - // A list item cannot be checked and empty, according to GFM, but - // theoretically it makes sense to get the end if that were possible. - point = + const type = types[node.checked] + + // A list item cannot be checked and empty, according to GFM, but + // theoretically it makes sense to get the end if that were possible. + const point = + /* c8 ignore next 2 */ + node.children.length === 0 + ? pointEnd(node) + : pointStart(node.children[0]) + // Move back to before `] `. + point.offset -= 2 + point.column -= 2 + + // Assume we start with a checkbox, because well, `checked` is set. + const match = /\[([\t Xx])]/.exec( + value.slice(point.offset - 2, point.offset + 1) + ) + + // Failsafe to make sure we don‘t crash if there actually isn’t a checkbox. /* c8 ignore next */ - node.children.length === 0 ? pointEnd(node) : pointStart(node.children[0]) - // Move back to before `] `. - point.offset -= 2 - point.column -= 2 - - // Assume we start with a checkbox, because well, `checked` is set. - value = /\[([\t Xx])]/.exec( - contents.slice(point.offset - 2, point.offset + 1) - ) - - // Failsafe to make sure we don‘t crash if there actually isn’t a checkbox. - /* c8 ignore next */ - if (!value) return - - style = preferred[type] - - if (style) { - if (value[1] !== style) { - reason = - type.charAt(0).toUpperCase() + - type.slice(1) + - ' checkboxes should use `' + - style + - '` as a marker' - - file.message(reason, point) + if (!match) return + + const style = preferred[type] + + if (style) { + if (match[1] !== style) { + file.message( + type.charAt(0).toUpperCase() + + type.slice(1) + + ' checkboxes should use `' + + style + + '` as a marker', + point + ) + } + } else { + preferred[type] = match[1] } - } else { - preferred[type] = value[1] - } + }) } -} +) + +export default remarkLintCheckboxCharacterStyle diff --git a/packages/remark-lint-checkbox-content-indent/index.js b/packages/remark-lint-checkbox-content-indent/index.js index 96232a38..d6233e4a 100644 --- a/packages/remark-lint-checkbox-content-indent/index.js +++ b/packages/remark-lint-checkbox-content-indent/index.js @@ -35,56 +35,47 @@ import {generated} from 'unist-util-generated' const remarkLintCheckboxContentIndent = lintRule( 'remark-lint:checkbox-content-indent', - checkboxContentIndent -) - -export default remarkLintCheckboxContentIndent - -var reason = 'Checkboxes should be followed by a single character' - -function checkboxContentIndent(tree, file) { - var contents = String(file) - var loc = location(file) + (tree, file) => { + const value = String(file) + const loc = location(file) - visit(tree, 'listItem', visitor) + visit(tree, 'listItem', (node) => { + // Exit early for items without checkbox. + if (typeof node.checked !== 'boolean' || generated(node)) { + return + } - function visitor(node) { - var initial - var final - var value - var point + // A list item cannot be checked and empty, according to GFM, but + // theoretically it makes sense to get the end if that were possible. + const point = + /* c8 ignore next 2 */ + node.children.length === 0 + ? pointEnd(node) + : pointStart(node.children[0]) - // Exit early for items without checkbox. - if (typeof node.checked !== 'boolean' || generated(node)) { - return - } + // Assume we start with a checkbox, because well, `checked` is set. + const match = /\[([\t xX])]/.exec( + value.slice(point.offset - 4, point.offset + 1) + ) - // A list item cannot be checked and empty, according to GFM, but - // theoretically it makes sense to get the end if that were possible. - point = + // Failsafe to make sure we don‘t crash if there actually isn’t a checkbox. /* c8 ignore next */ - node.children.length === 0 ? pointEnd(node) : pointStart(node.children[0]) + if (!match) return - // Assume we start with a checkbox, because well, `checked` is set. - value = /\[([\t xX])]/.exec( - contents.slice(point.offset - 4, point.offset + 1) - ) + // Move past checkbox. + const initial = point.offset + let final = initial - // Failsafe to make sure we don‘t crash if there actually isn’t a checkbox. - /* c8 ignore next */ - if (!value) return + while (/[\t ]/.test(value.charAt(final))) final++ - // Move past checkbox. - initial = point.offset - final = initial - - while (/[\t ]/.test(contents.charAt(final))) final++ - - if (final - initial > 0) { - file.message(reason, { - start: loc.toPoint(initial), - end: loc.toPoint(final) - }) - } + if (final - initial > 0) { + file.message('Checkboxes should be followed by a single character', { + start: loc.toPoint(initial), + end: loc.toPoint(final) + }) + } + }) } -} +) + +export default remarkLintCheckboxContentIndent diff --git a/packages/remark-lint-code-block-style/index.js b/packages/remark-lint-code-block-style/index.js index 99982bec..ed67346c 100644 --- a/packages/remark-lint-code-block-style/index.js +++ b/packages/remark-lint-code-block-style/index.js @@ -97,6 +97,8 @@ 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 @@ -104,11 +106,9 @@ const remarkLintCodeBlockStyle = lintRule( export default remarkLintCodeBlockStyle -var styles = {null: true, fenced: true, indented: true} - function codeBlockStyle(tree, file, option) { - var contents = String(file) - var preferred = + const value = String(file) + let preferred = typeof option === 'string' && option !== 'consistent' ? option : null if (styles[preferred] !== true) { @@ -119,22 +119,16 @@ function codeBlockStyle(tree, file, option) { ) } - visit(tree, 'code', visitor) - - function visitor(node) { - var initial - var final - var current - + visit(tree, 'code', (node) => { if (generated(node)) { - return null + return } - initial = pointStart(node).offset - final = pointEnd(node).offset + const initial = pointStart(node).offset + const final = pointEnd(node).offset - current = - node.lang || /^\s*([~`])\1{2,}/.test(contents.slice(initial, final)) + const current = + node.lang || /^\s*([~`])\1{2,}/.test(value.slice(initial, final)) ? 'fenced' : 'indented' @@ -145,5 +139,5 @@ function codeBlockStyle(tree, file, option) { } else { preferred = current } - } + }) } diff --git a/packages/remark-lint-definition-case/index.js b/packages/remark-lint-definition-case/index.js index 39fb5c1c..f9c3a51d 100644 --- a/packages/remark-lint-definition-case/index.js +++ b/packages/remark-lint-definition-case/index.js @@ -24,32 +24,31 @@ 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', - definitionCase + (tree, file) => { + const value = String(file) + + visit(tree, (node) => { + if ( + (node.type === 'definition' || node.type === 'footnoteDefinition') && + !generated(node) + ) { + 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 + ) + } + } + }) + } ) export default remarkLintDefinitionCase - -var label = /^\s*\[((?:\\[\s\S]|[^[\]])+)]/ -var reason = 'Do not use uppercase characters in definition labels' - -function definitionCase(tree, file) { - var contents = String(file) - - visit(tree, ['definition', 'footnoteDefinition'], check) - - function check(node) { - var start = pointStart(node).offset - var end = pointEnd(node).offset - var value - - if (!generated(node)) { - value = contents.slice(start, end).match(label)[1] - - if (value !== value.toLowerCase()) { - file.message(reason, node) - } - } - } -} diff --git a/packages/remark-lint-definition-spacing/index.js b/packages/remark-lint-definition-spacing/index.js index 20d5aed5..f28a168b 100644 --- a/packages/remark-lint-definition-spacing/index.js +++ b/packages/remark-lint-definition-spacing/index.js @@ -24,30 +24,30 @@ 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', - definitionSpacing + (tree, file) => { + const value = String(file) + + visit(tree, (node) => { + if ( + (node.type === 'definition' || node.type === 'footnoteDefinition') && + !generated(node) + ) { + 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 + ) + } + } + }) + } ) export default remarkLintDefinitionSpacing - -var label = /^\s*\[((?:\\[\s\S]|[^[\]])+)]/ -var reason = 'Do not use consecutive whitespace in definition labels' - -function definitionSpacing(tree, file) { - var contents = String(file) - - visit(tree, ['definition', 'footnoteDefinition'], check) - - function check(node) { - var start = pointStart(node).offset - var end = pointEnd(node).offset - - if ( - !generated(node) && - /[ \t\n]{2,}/.test(contents.slice(start, end).match(label)[1]) - ) { - file.message(reason, node) - } - } -} diff --git a/packages/remark-lint-emphasis-marker/index.js b/packages/remark-lint-emphasis-marker/index.js index f6d480b1..c6eaa526 100644 --- a/packages/remark-lint-emphasis-marker/index.js +++ b/packages/remark-lint-emphasis-marker/index.js @@ -65,46 +65,40 @@ 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', - emphasisMarker -) - -export default remarkLintEmphasisMarker - -var markers = {null: true, '*': true, _: true} - -function emphasisMarker(tree, file, option) { - var contents = String(file) - var preferred = - typeof option === 'string' && option !== 'consistent' ? option : null - - if (markers[preferred] !== true) { - file.fail( - 'Incorrect emphasis marker `' + - preferred + - "`: use either `'consistent'`, `'*'`, or `'_'`" - ) - } - - visit(tree, 'emphasis', visitor) + (tree, file, option) => { + const value = String(file) + let preferred = + typeof option === 'string' && option !== 'consistent' ? option : null - function visitor(node) { - var marker + if (markers[preferred] !== true) { + file.fail( + 'Incorrect emphasis marker `' + + preferred + + "`: use either `'consistent'`, `'*'`, or `'_'`" + ) + } - if (!generated(node)) { - marker = contents.charAt(pointStart(node).offset) + visit(tree, 'emphasis', (node) => { + if (!generated(node)) { + const marker = value.charAt(pointStart(node).offset) - if (preferred) { - if (marker !== preferred) { - file.message( - 'Emphasis should use `' + preferred + '` as a marker', - node - ) + if (preferred) { + if (marker !== preferred) { + file.message( + 'Emphasis should use `' + preferred + '` as a marker', + node + ) + } + } else { + preferred = marker } - } else { - preferred = marker } - } + }) } -} +) + +export default remarkLintEmphasisMarker diff --git a/packages/remark-lint-fenced-code-flag/index.js b/packages/remark-lint-fenced-code-flag/index.js index 6da302da..b72050de 100644 --- a/packages/remark-lint-fenced-code-flag/index.js +++ b/packages/remark-lint-fenced-code-flag/index.js @@ -69,49 +69,44 @@ import {visit} from 'unist-util-visit' import {pointStart, pointEnd} from 'unist-util-position' import {generated} from 'unist-util-generated' +const fence = /^ {0,3}([~`])\1{2,}/ + const remarkLintFencedCodeFlag = lintRule( 'remark-lint:fenced-code-flag', - fencedCodeFlag -) - -export default remarkLintFencedCodeFlag - -var fence = /^ {0,3}([~`])\1{2,}/ -var reasonIncorrect = 'Incorrect code language flag' -var reasonMissing = 'Missing code language flag' + (tree, file, option) => { + const value = String(file) + let allowEmpty = false + let allowed = [] + let flags = option -function fencedCodeFlag(tree, file, option) { - var contents = String(file) - var allowEmpty = false - var allowed = [] - var flags = option - - if (typeof flags === 'object' && !('length' in flags)) { - allowEmpty = Boolean(flags.allowEmpty) - flags = flags.flags - } - - if (typeof flags === 'object' && 'length' in flags) { - allowed = String(flags).split(',') - } - - visit(tree, 'code', visitor) + if (typeof flags === 'object' && !Array.isArray(flags)) { + allowEmpty = Boolean(flags.allowEmpty) + flags = flags.flags + } - function visitor(node) { - var value + if (Array.isArray(flags)) { + allowed = String(flags).split(',') + } - if (!generated(node)) { - if (node.lang) { - if (allowed.length !== 0 && allowed.indexOf(node.lang) === -1) { - file.message(reasonIncorrect, node) - } - } else { - value = contents.slice(pointStart(node).offset, pointEnd(node).offset) + visit(tree, 'code', (node) => { + if (!generated(node)) { + if (node.lang) { + if (allowed.length > 0 && !allowed.includes(node.lang)) { + file.message('Incorrect code language flag', node) + } + } else { + const slice = value.slice( + pointStart(node).offset, + pointEnd(node).offset + ) - if (!allowEmpty && fence.test(value)) { - file.message(reasonMissing, node) + if (!allowEmpty && fence.test(slice)) { + file.message('Missing code language flag', node) + } } } - } + }) } -} +) + +export default remarkLintFencedCodeFlag diff --git a/packages/remark-lint-fenced-code-marker/index.js b/packages/remark-lint-fenced-code-marker/index.js index d407b3ee..0e99dfdf 100644 --- a/packages/remark-lint-fenced-code-marker/index.js +++ b/packages/remark-lint-fenced-code-marker/index.js @@ -86,60 +86,53 @@ import {visit} from 'unist-util-visit' import {pointStart} from 'unist-util-position' import {generated} from 'unist-util-generated' -const remarkLintFencedCodeMarker = lintRule( - 'remark-lint:fenced-code-marker', - fencedCodeMarker -) - -export default remarkLintFencedCodeMarker - -var markers = { +const markers = { '`': true, '~': true, null: true } -function fencedCodeMarker(tree, file, option) { - var contents = String(file) - var preferred = - typeof option === 'string' && option !== 'consistent' ? option : null - - if (markers[preferred] !== true) { - file.fail( - 'Incorrect fenced code marker `' + - preferred + - "`: use either `'consistent'`, `` '`' ``, or `'~'`" - ) - } - - visit(tree, 'code', visitor) +const remarkLintFencedCodeMarker = lintRule( + 'remark-lint:fenced-code-marker', + (tree, file, option) => { + const contents = String(file) + let preferred = + typeof option === 'string' && option !== 'consistent' ? option : null - function visitor(node) { - var start - var marker - var label + if (markers[preferred] !== true) { + file.fail( + 'Incorrect fenced code marker `' + + preferred + + "`: use either `'consistent'`, `` '`' ``, or `'~'`" + ) + } - if (!generated(node)) { - start = pointStart(node).offset - marker = contents - .slice(start, start + 4) - .replace(/^\s+/, '') - .charAt(0) + visit(tree, 'code', (node) => { + if (!generated(node)) { + const start = pointStart(node).offset + const marker = contents + .slice(start, start + 4) + .replace(/^\s+/, '') + .charAt(0) - // Ignore unfenced code blocks. - if (markers[marker] === true) { - if (preferred) { - if (marker !== preferred) { - label = preferred === '~' ? preferred : '` ` `' - file.message( - 'Fenced code should use `' + label + '` as a marker', - node - ) + // 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 } - } else { - preferred = marker } } - } + }) } -} +) + +export default remarkLintFencedCodeMarker diff --git a/packages/remark-lint-file-extension/index.js b/packages/remark-lint-file-extension/index.js index e10341e2..802c8371 100644 --- a/packages/remark-lint-file-extension/index.js +++ b/packages/remark-lint-file-extension/index.js @@ -26,16 +26,14 @@ import {lintRule} from 'unified-lint-rule' const remarkLintFileExtension = lintRule( 'remark-lint:file-extension', - fileExtension + (tree, file, option) => { + const ext = file.extname + const preferred = typeof option === 'string' ? option : 'md' + + if (ext && ext.slice(1) !== preferred) { + file.message('Incorrect extension: use `' + preferred + '`') + } + } ) export default remarkLintFileExtension - -function fileExtension(tree, file, option) { - var ext = file.extname - var preferred = typeof option === 'string' ? option : 'md' - - if (ext && ext.slice(1) !== preferred) { - file.message('Incorrect extension: use `' + preferred + '`') - } -} diff --git a/packages/remark-lint-final-definition/index.js b/packages/remark-lint-final-definition/index.js index 5ea1fabb..47251d42 100644 --- a/packages/remark-lint-final-definition/index.js +++ b/packages/remark-lint-final-definition/index.js @@ -43,39 +43,39 @@ import {generated} from 'unist-util-generated' const remarkLintFinalDefinition = lintRule( 'remark-lint:final-definition', - finalDefinition -) - -export default remarkLintFinalDefinition - -function finalDefinition(tree, file) { - var last = null + (tree, file) => { + let last = null - visit(tree, visitor, true) + visit( + tree, + (node) => { + // Ignore generated and HTML comment nodes. + if ( + node.type === 'root' || + generated(node) || + (node.type === 'html' && /^\s*'), + 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('tableCell', option ? [u('inlineCode', inspect(option))] : []) + 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 = [ - 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'}) + children = children.concat(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('text', '\n'), - u('linkReference', {identifier: 'collective'}, [ - u('imageReference', {identifier: 'sponsors-badge', alt: 'Sponsors'}) + 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('text', '\n'), - u('linkReference', {identifier: 'collective'}, [ - u('imageReference', {identifier: 'backers-badge', alt: 'Backers'}) + 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('text', '\n'), - u('linkReference', {identifier: 'chat'}, [ - u('imageReference', {identifier: 'chat-badge', alt: 'Chat'}) - ]) - ]) - ] - - children = children.concat(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'}, - [ - ' …', - ' "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'}, - [ - " import {remark} from 'remark'", - " import {reporter} from 'vfile-reporter'", - ' import ' + camelcased + " from '" + name + "'", - '', - ' remark()', - '+ .use(' + camelcased + ')', - " .process('_Emphasis_ and **importance**')", - ' .then((file) => {', - ' 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( + 'code', + {lang: 'diff'}, + [ + ' …', + ' "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'}, + [ + " import {remark} from 'remark'", + " import {reporter} from 'vfile-reporter'", + ' import ' + camelcased + " from '" + name + "'", + '', + ' remark()', + '+ .use(' + camelcased + ')', + " .process('_Emphasis_ and **importance**')", + ' .then((file) => {', + ' 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('text', ' in '), - u('linkReference', {identifier: 'health'}, [ - u('inlineCode', health.split('/').slice(-2).join('/')) + 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('text', ' for ways\nto get started.\nSee '), - u('linkReference', {identifier: 'support'}, [ - u('inlineCode', 'support.md') + 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('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', { - identifier: 'build-badge', - url: 'https://github.com/' + slug + '/workflows/main/badge.svg' - }), - u('definition', { - identifier: 'build', - url: 'https://github.com/' + slug + '/actions' - }), - u('definition', { - identifier: 'coverage-badge', - url: 'https://img.shields.io/codecov/c/github/' + slug + '.svg' - }), - u('definition', { - identifier: 'coverage', - url: 'https://codecov.io/github/' + slug - }), - u('definition', { - identifier: 'downloads-badge', - url: 'https://img.shields.io/npm/dm/' + name + '.svg' - }), - u('definition', { - identifier: 'downloads', - url: 'https://www.npmjs.com/package/' + name - }), - u('definition', { - identifier: 'size-badge', - url: 'https://img.shields.io/bundlephobia/minzip/' + name + '.svg' - }), - u('definition', { - identifier: 'size', - url: 'https://bundlephobia.com/result?p=' + name - }), - u('definition', { - identifier: 'sponsors-badge', - url: 'https://opencollective.com/unified/sponsors/badge.svg' - }), - u('definition', { - identifier: 'backers-badge', - url: 'https://opencollective.com/unified/backers/badge.svg' - }), - u('definition', { - identifier: 'collective', - url: 'https://opencollective.com/unified' - }), - u('definition', { - identifier: 'chat-badge', - url: 'https://img.shields.io/badge/chat-discussions-success.svg' - }), - u('definition', { - identifier: 'chat', - url: 'https://github.com/remarkjs/remark/discussions' - }), - u('definition', { - identifier: 'npm', - url: 'https://docs.npmjs.com/cli/install' - }), - u('definition', {identifier: 'health', url: health}), - u('definition', { - identifier: 'contributing', - url: hMain + '/contributing.md' - }), - u('definition', {identifier: 'support', url: hMain + '/support.md'}), - u('definition', { - identifier: 'coc', - url: hMain + '/code-of-conduct.md' - }), - u('definition', {identifier: 'license', url: main + '/license'}), - u('definition', {identifier: 'author', url: author.url}) - ) + u('definition', { + identifier: 'build-badge', + url: 'https://github.com/' + slug + '/workflows/main/badge.svg' + }), + u('definition', { + identifier: 'build', + url: 'https://github.com/' + slug + '/actions' + }), + u('definition', { + identifier: 'coverage-badge', + url: 'https://img.shields.io/codecov/c/github/' + slug + '.svg' + }), + u('definition', { + identifier: 'coverage', + url: 'https://codecov.io/github/' + slug + }), + u('definition', { + identifier: 'downloads-badge', + url: 'https://img.shields.io/npm/dm/' + name + '.svg' + }), + u('definition', { + identifier: 'downloads', + url: 'https://www.npmjs.com/package/' + name + }), + u('definition', { + identifier: 'size-badge', + url: 'https://img.shields.io/bundlephobia/minzip/' + name + '.svg' + }), + u('definition', { + identifier: 'size', + url: 'https://bundlephobia.com/result?p=' + name + }), + u('definition', { + identifier: 'sponsors-badge', + url: 'https://opencollective.com/unified/sponsors/badge.svg' + }), + u('definition', { + identifier: 'backers-badge', + url: 'https://opencollective.com/unified/backers/badge.svg' + }), + u('definition', { + identifier: 'collective', + url: 'https://opencollective.com/unified' + }), + u('definition', { + identifier: 'chat-badge', + url: 'https://img.shields.io/badge/chat-discussions-success.svg' + }), + u('definition', { + identifier: 'chat', + url: 'https://github.com/remarkjs/remark/discussions' + }), + u('definition', { + identifier: 'npm', + url: 'https://docs.npmjs.com/cli/install' + }), + u('definition', {identifier: 'health', url: health}), + u('definition', { + identifier: 'contributing', + url: hMain + '/contributing.md' + }), + u('definition', {identifier: 'support', url: hMain + '/support.md'}), + u('definition', { + identifier: 'coc', + url: hMain + '/code-of-conduct.md' + }), + u('definition', {identifier: 'license', url: main + '/license'}), + u('definition', {identifier: 'author', url: author.url}) + ) - fs.writeFileSync( - path.join(base, 'readme.md'), - remark().use(remarkGfm).stringify(u('root', children)) - ) + fs.writeFileSync( + path.join(base, 'readme.md'), + remark().use(remarkGfm).stringify(u('root', children)) + ) - console.log('✓ wrote `readme.md` in `' + name + '`') + console.log('✓ wrote `readme.md` in `' + name + '`') + } }) diff --git a/script/build-rules.js b/script/build-rules.js index 6acef2d9..ad019464 100644 --- a/script/build-rules.js +++ b/script/build-rules.js @@ -10,347 +10,373 @@ import {rule} from './util/rule.js' import {presets} from './util/presets.js' import {characters} from './characters.js' +const own = {}.hasOwnProperty + const pkg = JSON.parse(fs.readFileSync('package.json')) const remote = pkg.repository -var root = path.join(process.cwd(), 'packages') +const root = path.join(process.cwd(), 'packages') -const presetObjects = await presets(root) +presets(root).then((presetObjects) => { + const allRules = rules(root) + let index = -1 -rules(root).forEach(function (basename) { - var base = path.resolve(root, basename) - var pack = JSON.parse(fs.readFileSync(path.join(base, 'package.json'))) - var info = rule(base) - var tests = info.tests - var author = parseAuthor(pack.author) - var short = basename.replace(/^remark-/, '') - var camelcased = basename.replace(/-(\w)/g, (_, $1) => $1.toUpperCase()) - var org = remote.split('/').slice(0, -1).join('/') - var main = remote + '/blob/main' - var health = org + '/.github' - var hMain = health + '/blob/HEAD' - var slug = remote.split('/').slice(-2).join('/') - var includes - var hasGfm - var 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'}) + 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'))) + const info = rule(base) + const tests = info.tests + const author = parseAuthor(pack.author) + const short = basename.replace(/^remark-/, '') + const camelcased = basename.replace(/-(\w)/g, (_, $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) - - if (basename !== pack.name) { - throw new Error( - 'Expected package name (`' + - pack.name + - '`) to be the same as directory name (`' + - basename + - '`)' - ) - } + ].concat(remark().parse(info.description).children) - includes = presetObjects.filter(function (preset) { - return basename in preset.packages - }) + if (basename !== pack.name) { + throw new Error( + 'Expected package name (`' + + pack.name + + '`) to be the same as directory name (`' + + basename + + '`)' + ) + } - children.push(u('heading', {depth: 2}, [u('text', 'Presets')])) + const includes = presetObjects.filter((preset) => { + return basename in preset.packages + }) - if (includes.length === 0) { - children.push( - u('paragraph', [ - u('text', '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(function (preset) { - var url = remote + '/tree/main/packages/' + preset.name - var option = preset.packages[pack.name] + children.push(u('heading', {depth: 2}, [u('text', 'Presets')])) - return u('tableRow', [ - u('tableCell', [ - u('link', {url: url, title: null}, [ - u('inlineCode', preset.name) - ]) - ]), - u('tableCell', option ? [u('inlineCode', inspect(option))] : []) + if (includes.length === 0) { + children.push( + u('paragraph', [ + u('text', '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))] : []) + ]) + }) + ) ) ) - ) - } + } - Object.keys(tests).forEach(function (setting, index) { - var fixtures = tests[setting] + let setting + let first = true - if (index === 0) { - children.push(u('heading', {depth: 2}, [u('text', 'Example')])) - } + for (setting in tests) { + if (own.call(tests, setting)) { + const fixtures = tests[setting] - Object.keys(fixtures).forEach(function (fileName) { - var fixture = fixtures[fileName] - var label = inspect(JSON.parse(setting)) - var clean = fixture.input + if (first) { + children.push(u('heading', {depth: 2}, [u('text', 'Example')])) + first = false + } - children.push(u('heading', {depth: 5}, [u('inlineCode', fileName)])) + let fileName - if (label !== 'true') { - children.push( - u('paragraph', [ - u('text', 'When configured with '), - u('inlineCode', label), - u('text', '.') - ]) - ) - } + for (fileName in fixtures) { + if (own.call(fixtures, fileName)) { + const fixture = fixtures[fileName] + const label = inspect(JSON.parse(setting)) + let clean = fixture.input - if (fixture.input != null && fixture.input.trim() !== '') { - children.push(u('heading', {depth: 6}, [u('text', 'In')])) + children.push(u('heading', {depth: 5}, [u('inlineCode', fileName)])) - 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', '.') - ]) - ) - } + if (label !== 'true') { + children.push( + u('paragraph', [ + u('text', 'When configured with '), + u('inlineCode', label), + u('text', '.') + ]) + ) + } - characters.forEach(function (char) { - var next = clean.replace(char.in, char.out) + if ( + fixture.input !== null && + fixture.input !== undefined && + fixture.input.trim() !== '' + ) { + children.push(u('heading', {depth: 6}, [u('text', 'In')])) - if (clean !== next) { - children.push( - u('paragraph', [ - u('text', 'Note: '), - u('inlineCode', char.char), - u('text', ' represents ' + char.name + '.') - ]) - ) + 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', '.') + ]) + ) + } - clean = next - } - }) + let index = -1 + while (++index < characters.length) { + const char = characters[index] + const next = clean.replace(char.in, char.out) - children.push(u('code', {lang: 'markdown'}, fixture.input)) - } + if (clean !== next) { + children.push( + u('paragraph', [ + u('text', 'Note: '), + u('inlineCode', char.char), + u('text', ' represents ' + char.name + '.') + ]) + ) + + clean = next + } + } - children.push(u('heading', {depth: 6}, [u('text', 'Out')])) + children.push(u('code', {lang: 'markdown'}, fixture.input)) + } - if (fixture.output.length === 0) { - children.push(u('paragraph', [u('text', 'No messages.')])) - } else { - children.push(u('code', {lang: 'text'}, fixture.output.join('\n'))) + children.push(u('heading', {depth: 6}, [u('text', 'Out')])) + + if (fixture.output.length === 0) { + children.push(u('paragraph', [u('text', 'No messages.')])) + } else { + children.push( + u('code', {lang: 'text'}, 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') + children = children.concat([ + u('heading', {depth: 2}, [u('text', 'Install')]), + u('paragraph', [ + u('linkReference', {identifier: 'npm', referenceType: 'collapsed'}, [ + u('text', 'npm') + ]), + u('text', ':') ]), - 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'}, - [ - ' …', - ' "remarkConfig": {', - ' "plugins": [', - ' …', - ' "lint",', - '+ "' + short + '",', - ' …', - ' ]', - ' }', - ' …' - ].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'}, - [ - " import {remark} from 'remark'", - " import {reporter} from 'vfile-reporter'", - " import remarkLint from 'remark-lint'", - ' import ' + camelcased + " from '" + basename + "'", - '', - ' remark()', - ' .use(remarkLint)', - '+ .use(' + camelcased + ')', - " .process('_Emphasis_ and **importance**')", - ' .then((file) => {', - ' 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('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('text', ' in '), - u('linkReference', {identifier: 'health'}, [ - u('inlineCode', health.split('/').slice(-2).join('/')) + u( + 'code', + {lang: 'diff'}, + [ + ' …', + ' "remarkConfig": {', + ' "plugins": [', + ' …', + ' "lint",', + '+ "' + short + '",', + ' …', + ' ]', + ' }', + ' …' + ].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'}, + [ + " import {remark} from 'remark'", + " import {reporter} from 'vfile-reporter'", + " import remarkLint from 'remark-lint'", + ' import ' + camelcased + " from '" + basename + "'", + '', + ' remark()', + ' .use(remarkLint)', + '+ .use(' + camelcased + ')', + " .process('_Emphasis_ and **importance**')", + ' .then((file) => {', + ' 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('text', ' for ways\nto get started.\nSee '), - u('linkReference', {identifier: 'support'}, [ - u('inlineCode', 'support.md') + 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('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', { + identifier: 'build-badge', + url: 'https://github.com/' + slug + '/workflows/main/badge.svg' + }), + u('definition', { + identifier: 'build', + url: 'https://github.com/' + slug + '/actions' + }), + u('definition', { + identifier: 'coverage-badge', + url: 'https://img.shields.io/codecov/c/github/' + slug + '.svg' + }), + u('definition', { + identifier: 'coverage', + url: 'https://codecov.io/github/' + slug + }), + u('definition', { + identifier: 'downloads-badge', + url: 'https://img.shields.io/npm/dm/' + basename + '.svg' + }), + u('definition', { + identifier: 'downloads', + url: 'https://www.npmjs.com/package/' + basename + }), + u('definition', { + identifier: 'size-badge', + url: 'https://img.shields.io/bundlephobia/minzip/' + basename + '.svg' + }), + u('definition', { + identifier: 'size', + url: 'https://bundlephobia.com/result?p=' + basename + }), + u('definition', { + identifier: 'sponsors-badge', + url: 'https://opencollective.com/unified/sponsors/badge.svg' + }), + u('definition', { + identifier: 'backers-badge', + url: 'https://opencollective.com/unified/backers/badge.svg' + }), + u('definition', { + identifier: 'collective', + url: 'https://opencollective.com/unified' + }), + u('definition', { + identifier: 'chat-badge', + url: 'https://img.shields.io/badge/chat-discussions-success.svg' + }), + u('definition', { + identifier: 'chat', + url: 'https://github.com/remarkjs/remark/discussions' + }), + u('definition', { + identifier: 'npm', + url: 'https://docs.npmjs.com/cli/install' + }), + u('definition', {identifier: 'health', url: health}), + u('definition', { + identifier: 'contributing', + url: hMain + '/contributing.md' + }), + u('definition', {identifier: 'support', url: hMain + '/support.md'}), + u('definition', { + identifier: 'coc', + url: hMain + '/code-of-conduct.md' + }), + u('definition', {identifier: 'license', url: main + '/license'}), + u('definition', {identifier: 'author', url: author.url}) + ]) + + if (hasGfm) { + children.push( + u('definition', { + identifier: 'gfm', + url: 'https://github.com/remarkjs/remark-gfm' + }) ) - ]), - 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', { - identifier: 'build-badge', - url: 'https://github.com/' + slug + '/workflows/main/badge.svg' - }), - u('definition', { - identifier: 'build', - url: 'https://github.com/' + slug + '/actions' - }), - u('definition', { - identifier: 'coverage-badge', - url: 'https://img.shields.io/codecov/c/github/' + slug + '.svg' - }), - u('definition', { - identifier: 'coverage', - url: 'https://codecov.io/github/' + slug - }), - u('definition', { - identifier: 'downloads-badge', - url: 'https://img.shields.io/npm/dm/' + basename + '.svg' - }), - u('definition', { - identifier: 'downloads', - url: 'https://www.npmjs.com/package/' + basename - }), - u('definition', { - identifier: 'size-badge', - url: 'https://img.shields.io/bundlephobia/minzip/' + basename + '.svg' - }), - u('definition', { - identifier: 'size', - url: 'https://bundlephobia.com/result?p=' + basename - }), - u('definition', { - identifier: 'sponsors-badge', - url: 'https://opencollective.com/unified/sponsors/badge.svg' - }), - u('definition', { - identifier: 'backers-badge', - url: 'https://opencollective.com/unified/backers/badge.svg' - }), - u('definition', { - identifier: 'collective', - url: 'https://opencollective.com/unified' - }), - u('definition', { - identifier: 'chat-badge', - url: 'https://img.shields.io/badge/chat-discussions-success.svg' - }), - u('definition', { - identifier: 'chat', - url: 'https://github.com/remarkjs/remark/discussions' - }), - u('definition', { - identifier: 'npm', - url: 'https://docs.npmjs.com/cli/install' - }), - u('definition', {identifier: 'health', url: health}), - u('definition', { - identifier: 'contributing', - url: hMain + '/contributing.md' - }), - u('definition', {identifier: 'support', url: hMain + '/support.md'}), - u('definition', { - identifier: 'coc', - url: hMain + '/code-of-conduct.md' - }), - u('definition', {identifier: 'license', url: main + '/license'}), - u('definition', {identifier: 'author', url: author.url}) - ]) + } - if (hasGfm) { - children.push( - u('definition', { - identifier: 'gfm', - url: 'https://github.com/remarkjs/remark-gfm' - }) + fs.writeFileSync( + path.join(base, 'readme.md'), + remark().use(remarkGfm).stringify(u('root', children)) ) - } - fs.writeFileSync( - path.join(base, 'readme.md'), - remark().use(remarkGfm).stringify(u('root', children)) - ) - - console.log('✓ wrote `readme.md` in `' + basename + '`') + console.log('✓ wrote `readme.md` in `' + basename + '`') + } }) diff --git a/script/plugin/list-of-presets.js b/script/plugin/list-of-presets.js index b4e2b54d..b90f1831 100644 --- a/script/plugin/list-of-presets.js +++ b/script/plugin/list-of-presets.js @@ -4,38 +4,39 @@ import {zone} from 'mdast-zone' import {u} from 'unist-builder' import {presets} from '../util/presets.js' -var root = path.join(process.cwd(), 'packages') -const presetObjects = await presets(root) +const root = path.join(process.cwd(), 'packages') export default function listOfPresets() { - return transformer -} - -function transformer(tree) { - zone(tree, 'presets', replace) -} + const presetPromise = presets(root) -function replace(start, nodes, end) { - return [ - start, - u('list', {ordered: false, spread: false}, presetObjects.map(item)), - end - ] + return async (tree) => { + const presetObjects = await presetPromise - function item({name}) { - var pack = JSON.parse( - fs.readFileSync(path.join(root, name, 'package.json')) - ) - var description = pack.description.replace( - /^remark preset to configure remark-lint with ?/i, - '' - ) + 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) - ]) - ]) + return u('listItem', {spread: false}, [ + u('paragraph', [ + u('link', {url: pack.repository}, [u('inlineCode', name)]), + u('text', ' — ' + description) + ]) + ]) + }) + ), + end + ] + }) } } diff --git a/script/plugin/list-of-rules.js b/script/plugin/list-of-rules.js index 1a8c5024..7f6ee992 100644 --- a/script/plugin/list-of-rules.js +++ b/script/plugin/list-of-rules.js @@ -4,7 +4,7 @@ import {zone} from 'mdast-zone' import {u} from 'unist-builder' import {rules} from '../util/rules.js' -var root = path.join(process.cwd(), 'packages') +const root = path.join(process.cwd(), 'packages') export default function listOfRules() { return transformer @@ -17,22 +17,27 @@ function transformer(tree) { function replace(start, nodes, end) { return [ start, - u('list', {ordered: false, spread: false}, rules(root).map(item)), + 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 u('listItem', {spread: false}, [ + u('paragraph', [ + u('link', {url: pack.repository}, [u('inlineCode', name)]), + u('text', ' — ' + description) + ]) + ]) + }) + ), end ] - - function item(basename) { - var name = basename.slice('remark-lint-'.length) - var pack = JSON.parse( - fs.readFileSync(path.join(root, basename, 'package.json')) - ) - var description = pack.description.replace(/^remark-lint rule to ?/i, '') - - return u('listItem', {spread: false}, [ - u('paragraph', [ - u('link', {url: pack.repository}, [u('inlineCode', name)]), - u('text', ' — ' + description) - ]) - ]) - } } diff --git a/script/util/find.js b/script/util/find.js index 2259e51b..7d89145d 100755 --- a/script/util/find.js +++ b/script/util/find.js @@ -1,8 +1,8 @@ // Find the first tag in `tags` with a type set to `key`. export function find(tags, key) { - var value = null + let value = null - tags.some(function (tag) { + tags.some((tag) => { if (tag && tag.type === key) { value = tag @@ -18,10 +18,10 @@ export function find(tags, key) { // Find the first tag in `tags` with a type set to `key`. export function findAll(tags, key) { return tags - .filter(function (tag) { + .filter((tag) => { return tag && tag.type === key }) - .map(function (tag) { + .map((tag) => { return tag.string }) } diff --git a/script/util/presets.js b/script/util/presets.js index 3d2096e4..5e6fa915 100644 --- a/script/util/presets.js +++ b/script/util/presets.js @@ -2,13 +2,15 @@ import {promises as fs} from 'fs' import path from 'path' export async function presets(base) { - const files = (await fs.readdir(base)).filter(filter) + const files = (await fs.readdir(base)).filter((basename) => + /remark-preset-lint/.test(basename) + ) return Promise.all( - files.map(async function (name) { + files.map(async (name) => { const plugins = (await import(path.join(base, name, 'index.js'))).default .plugins - var packages = {} + const packages = {} let index = -1 while (++index < plugins.length) { @@ -23,7 +25,7 @@ export async function presets(base) { option = plugin[1] } - let name = fn.displayName || fn.name + const name = fn.displayName || fn.name packages[ name @@ -32,11 +34,7 @@ export async function presets(base) { ] = option } - return {name: name, packages: packages} + return {name, packages} }) ) } - -function filter(basename) { - return /remark-preset-lint/.test(basename) -} diff --git a/script/util/rule.js b/script/util/rule.js index 678af521..86945edb 100755 --- a/script/util/rule.js +++ b/script/util/rule.js @@ -6,19 +6,13 @@ import {find, findAll} from './find.js' // Get information for a rule at `filePath`. export function rule(filePath) { - var ruleId = path.basename(filePath) - var result = {} - var tests = {} - var description - var code - var tags - var name - - ruleId = ruleId.slice('remark-lint-'.length) - code = fs.readFileSync(path.join(filePath, 'index.js'), 'utf-8') - tags = dox.parseComments(code)[0].tags - description = find(tags, 'fileoverview') - name = find(tags, 'module') + const ruleId = path.basename(filePath).slice('remark-lint-'.length) + const result = {} + const tests = {} + const code = fs.readFileSync(path.join(filePath, 'index.js'), 'utf-8') + const tags = dox.parseComments(code)[0].tags + const name = find(tags, 'module') + let description = find(tags, 'fileoverview') /* c8 ignore next 3 */ if (name !== ruleId) { @@ -37,17 +31,14 @@ export function rule(filePath) { result.tests = tests result.filePath = filePath - findAll(tags, 'example').map(strip).forEach(check) + const examples = findAll(tags, 'example') + let index = -1 - return result - - function check(example) { - var lines = example.split('\n') - var value = strip(lines.slice(1).join('\n')) - var info - var setting - var context - var name + while (++index < examples.length) { + const example = strip(examples[index]) + const lines = example.split('\n') + const value = strip(lines.slice(1).join('\n')) + let info try { info = JSON.parse(lines[0]) @@ -58,9 +49,9 @@ export function rule(filePath) { ) } - setting = JSON.stringify(info.setting || true) - context = tests[setting] - name = info.name + const setting = JSON.stringify(info.setting || true) + const name = info.name + let context = tests[setting] if (!context) { context = [] @@ -71,12 +62,12 @@ export function rule(filePath) { context[name] = { positionless: info.positionless, gfm: info.gfm, - setting: setting, + setting, input: value, output: [] } - return + continue } /* c8 ignore next 9 */ @@ -97,9 +88,11 @@ export function rule(filePath) { context[name].setting = setting if (info.label === 'output') { - value = value.split('\n') + context[name][info.label] = value.split('\n') + } else { + context[name][info.label] = value } - - context[name][info.label] = value } + + return result } diff --git a/script/util/rules.js b/script/util/rules.js index fca1dde9..6de22224 100644 --- a/script/util/rules.js +++ b/script/util/rules.js @@ -1,9 +1,9 @@ import fs from 'fs' export function rules(filePath) { - return fs.readdirSync(filePath).filter(filter) -} - -function filter(basename) { - return /remark-lint/.test(basename) && basename !== 'remark-lint' + return fs + .readdirSync(filePath) + .filter( + (basename) => /remark-lint/.test(basename) && basename !== 'remark-lint' + ) } diff --git a/test.js b/test.js index fb9a58b5..5984a702 100644 --- a/test.js +++ b/test.js @@ -13,9 +13,11 @@ import noHeadingPunctuation from './packages/remark-lint-no-heading-punctuation/ import noMultipleToplevelHeadings from './packages/remark-lint-no-multiple-toplevel-headings/index.js' import finalNewline from './packages/remark-lint-final-newline/index.js' -test('core', function (t) { - t.test('should work', function (st) { - var doc = [ +const own = {}.hasOwnProperty + +test('core', (t) => { + t.test('should work', (t) => { + const doc = [ '# A heading', '', '# Another main heading.', @@ -25,114 +27,108 @@ test('core', function (t) { '# Another main heading.' ].join('\n') - st.plan(2) + t.plan(2) remark() .use(noHeadingPunctuation) .use(noMultipleToplevelHeadings) .use(lint) - .process( - toVFile({path: 'virtual.md', value: doc}), - function (error, file) { - st.deepEqual( - [error].concat(file.messages.map(String)), - [ - 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' - ) - } - ) + .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' + ) + }) remark() .use(lint) .use(noHeadingPunctuation) .use(noMultipleToplevelHeadings) - .process( - toVFile({path: 'virtual.md', value: doc}), - function (error, file) { - st.deepEqual( - [error].concat(file.messages.map(String)), - [ - 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' - ) - } - ) + .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', function (st) { - st.plan(1) + t.test('should support no rules', (t) => { + t.plan(1) remark() .use(lint) - .process('.', function (error, file) { - st.deepEqual( - [error].concat(file.messages.map(String)), + .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', function (st) { - st.plan(1) + t.test('should support successful rules', (t) => { + t.plan(1) remark() .use(finalNewline) - .process('', function (error, file) { - st.deepEqual( - [error].concat(file.messages.map(String)), + .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', function (st) { - st.plan(2) + t.test('should support a list with a severity', (t) => { + t.plan(2) remark() .use(finalNewline, [2]) - .process('.', function (error, file) { - st.deepEqual( - [error].concat(file.messages.map(String)), + .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)' ) - st.equal(file.messages[0].fatal, true, 'should emit fatally (2)') + t.equal(file.messages[0].fatal, true, 'should emit fatally (2)') }) }) - t.test('should support a boolean (`true`)', function (st) { + t.test('should support a boolean (`true`)', (t) => { // Note! This is handled by unified. - st.plan(1) + t.plan(1) remark() .use(finalNewline, true) - .process('.', function (error, file) { - st.deepEqual( - [error].concat(file.messages.map(String)), + .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`)', function (st) { + t.test('should support a boolean (`false`)', (t) => { // Note! This is handled by unified. - st.plan(1) + t.plan(1) remark() .use(finalNewline, false) - .process('.', function (error, file) { - st.deepEqual( - [error].concat(file.messages.map(String)), + .process('.', (error, file) => { + t.deepEqual( + [error].concat(file.messages.map((d) => String(d))), [null], 'should not emit' ) @@ -141,14 +137,14 @@ test('core', function (t) { t.test( 'should support a list with a boolean severity (true, for on)', - function (st) { - st.plan(1) + (t) => { + t.plan(1) remark() .use(finalNewline, [true]) - .process('.', function (error, file) { - st.deepEqual( - [error].concat(file.messages.map(String)), + .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' ) @@ -158,14 +154,14 @@ test('core', function (t) { t.test( 'should support a list with boolean severity (false, for off)', - function (st) { - st.plan(1) + (t) => { + t.plan(1) remark() .use(finalNewline, [false]) - .process('.', function (error, file) { - st.deepEqual( - [error].concat(file.messages.map(String)), + .process('.', (error, file) => { + t.deepEqual( + [error].concat(file.messages.map((d) => String(d))), [null], 'should not emit' ) @@ -173,149 +169,149 @@ test('core', function (t) { } ) - t.test('should support a list with string severity (`error`)', function (st) { - st.plan(2) + t.test('should support a list with string severity (`error`)', (t) => { + t.plan(2) remark() .use(finalNewline, ['error']) - .process('.', function (error, file) { - st.deepEqual( - [error].concat(file.messages.map(String)), + .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)' ) - st.equal(file.messages[0].fatal, true, 'should emit fatally (2)') + t.equal(file.messages[0].fatal, true, 'should emit fatally (2)') }) }) - t.test('should support a list with a string severity (`on`)', function (st) { - st.plan(2) + t.test('should support a list with a string severity (`on`)', (t) => { + t.plan(2) remark() .use(finalNewline, ['on']) - .process('.', function (error, file) { - st.deepEqual( - [error].concat(file.messages.map(String)), + .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' ) - st.equal(file.messages[0].fatal, false, 'should not emit fatally') + t.equal(file.messages[0].fatal, false, 'should not emit fatally') }) }) - t.test( - 'should support a list with a string severity (`warn`)', - function (st) { - st.plan(2) + t.test('should support a list with a string severity (`warn`)', (t) => { + t.plan(2) - remark() - .use(finalNewline, ['warn']) - .process('.', function (error, file) { - st.deepEqual( - [error].concat(file.messages.map(String)), - [null, '1:1: Missing newline character at end of file'], - 'should message' - ) - st.equal(file.messages[0].fatal, false, 'should not emit fatally') - }) - } - ) + 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`)', function (st) { - st.plan(1) + t.test('should support a list with a string severity (`off`)', (t) => { + t.plan(1) remark() .use(finalNewline, ['off']) - .process('.', function (error, file) { - st.deepEqual( - [error].concat(file.messages.map(String)), + .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', function (st) { - st.throws( - function () { + 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' ) - st.throws( - function () { + t.throws( + () => { remark().use(finalNewline, [-1]).freeze() }, /^Error: Incorrect severity `-1` for `final-newline`, expected 0, 1, or 2$/, 'should throw too low' ) - st.end() + t.end() }) t.end() }) -test('rules', function (t) { - var root = path.join(process.cwd(), 'packages') - var all = rules(root) - - t.plan(all.length) - - all.forEach(each) - - async function each(basename) { - var base = path.resolve(root, basename) - var info = rule(base) - var fn = (await import(url.pathToFileURL(base).href + '/index.js')).default - var handle = Object.keys(info.tests).length === 0 ? ignore : one - - t.test(info.ruleId, handle) - - function one(st) { - assertRule(st, fn, info) - } - - function ignore(st) { - st.pass('no tests') - st.end() +test('rules', async (t) => { + const root = path.join(process.cwd(), 'packages') + const all = rules(root) + let index = -1 + + while (++index < all.length) { + 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 + + if (Object.keys(info.tests).length === 0) { + t.pass(info.ruleId + ': no tests') + } else { + t.test(info.ruleId, (t) => { + assertRule(t, fn, info) + }) } } + + t.end() }) // Assert a rule. function assertRule(t, rule, info) { - var tests = info.tests - var settings = Object.keys(tests) - - t.plan(settings.length) - - settings.forEach(function (setting) { - var fixture = tests[setting] - var names = Object.keys(fixture) - var settings = JSON.parse(setting) - - t.test(setting, function (st) { - st.plan(names.length) - - names.forEach(function (name) { - st.test(name, function (sst) { - assertFixture(sst, rule, info, fixture[name], name, settings) - }) + const tests = info.tests + let setting + + for (setting in tests) { + if (own.call(tests, setting)) { + const checks = tests[setting] + const options = JSON.parse(setting) + + t.test(setting, (t) => { + let name + + for (name in checks) { + if (own.call(checks, name)) { + const basename = name + const check = checks[name] + + t.test(name, (t) => { + assertFixture(t, rule, info, check, basename, options) + }) + } + } }) - }) - }) + } + } + + t.end() } /* eslint-disable-next-line max-params */ function assertFixture(t, rule, info, fixture, basename, settings) { - var ruleId = info.ruleId - var file = toVFile(basename) - var expected = fixture.output - var positionless = fixture.positionless - var proc = remark().use(rule, settings) + const ruleId = info.ruleId + const file = toVFile(basename) + const expected = fixture.output + const positionless = fixture.positionless + let proc = remark().use(rule, settings) if (fixture.gfm) proc.use(remarkGfm) @@ -331,7 +327,9 @@ function assertFixture(t, rule, info, fixture, basename, settings) { } } - file.messages.forEach(function (message) { + let index = -1 + while (++index < file.messages.length) { + const message = file.messages[index] if (message.ruleId !== ruleId) { throw new Error( 'Expected `' + @@ -342,7 +340,7 @@ function assertFixture(t, rule, info, fixture, basename, settings) { message ) } - }) + } t.deepEqual(normalize(file.messages), expected, 'should equal with position') @@ -361,16 +359,18 @@ function assertFixture(t, rule, info, fixture, basename, settings) { } function normalize(messages) { - return messages.map(function (message) { - var value = String(message) + return messages.map((message) => { + const value = String(message) return value.slice(value.indexOf(':') + 1) }) } function preprocess(value) { - characters.forEach(function (char) { - value = value.replace(char.in, char.out) - }) + let index = -1 + + while (++index < characters.length) { + value = value.replace(characters[index].in, characters[index].out) + } return value }