remark-lint-blockquote-indentation
Version:
remark-lint rule to check whitespace after block quote markers
206 lines (193 loc) • 5.08 kB
JavaScript
/**
* remark-lint rule to warn when block quotes are indented too much or
* too little.
*
* ## What is this?
*
* This package checks the “indent” of block quotes: the `>` (greater than)
* marker *and* the spaces before content.
*
* ## When should I use this?
*
* You can use this rule to check markdown code style.
*
* ## API
*
* ### `unified().use(remarkLintBlockquoteIndentation[, options])`
*
* Warn when block quotes are indented too much or too little.
*
* ###### Parameters
*
* * `options` ([`Options`][api-options], default: `'consistent'`)
* — either a preferred indent or whether to detect the first style
* and warn for further differences
*
* ###### Returns
*
* Transform ([`Transformer` from `unified`][github-unified-transformer]).
*
* ### `Options`
*
* Configuration (TypeScript type).
*
* ###### Type
*
* ```ts
* type Options = number | 'consistent'
* ```
*
* ## Recommendation
*
* CommonMark specifies that when block quotes are used the `>` markers can be
* followed by an optional space.
* No space at all arguably looks rather ugly:
*
* ```markdown
* >Mars and
* >Venus.
* ```
*
* There is no specific handling of more that one space, so if 5 spaces were
* used after `>`, then indented code kicks in:
*
* ```markdown
* > neptune()
* ```
*
* Due to this, it’s recommended to configure this rule with `2`.
*
* [api-options]: #options
* [api-remark-lint-blockquote-indentation]: #unifieduseremarklintblockquoteindentation-options
* [github-unified-transformer]: https://github.com/unifiedjs/unified#transformer
*
* @module blockquote-indentation
* @author Titus Wormer
* @copyright Titus Wormer
* @license MIT
*
* @example
* {"config": 2, "name": "ok-2.md"}
*
* > Mercury.
*
* Venus.
*
* > Earth.
*
* @example
* {"config": 4, "name": "ok-4.md"}
*
* > Mercury.
*
* Venus.
*
* > Earth.
*
* @example
* { "name": "ok-tab.md"}
*
* >␉Mercury.
*
* @example
* {"label": "input", "name": "not-ok.md"}
*
* > Mercury.
*
* Venus.
*
* > Earth.
*
* Mars.
*
* > Jupiter
* @example
* {"label": "output", "name": "not-ok.md"}
*
* 5:5: Unexpected `4` spaces between block quote marker and content, expected `3` spaces, remove `1` space
* 9:3: Unexpected `2` spaces between block quote marker and content, expected `3` spaces, add `1` space
*
* @example
* {"config": "🌍", "label": "output", "name": "not-ok-options.md", "positionless": true}
*
* 1:1: Unexpected value `🌍` for `options`, expected `number` or `'consistent'`
*/
/**
* @import {Root} from 'mdast'
*/
/**
* @typedef {number | 'consistent'} Options
* Configuration.
*/
import {phrasing} from 'mdast-util-phrasing'
import pluralize from 'pluralize'
import {lintRule} from 'unified-lint-rule'
import {pointStart} from 'unist-util-position'
import {SKIP, visitParents} from 'unist-util-visit-parents'
const remarkLintBlockquoteIndentation = lintRule(
{
origin: 'remark-lint:blockquote-indentation',
url: 'https://github.com/remarkjs/remark-lint/tree/main/packages/remark-lint-blockquote-indentation#readme'
},
/**
* @param {Root} tree
* Tree.
* @param {Options | null | undefined} [options='consistent']
* Configuration (default: `'consistent'`).
* @returns {undefined}
* Nothing.
*/
function (tree, file, options) {
/** @type {number | undefined} */
let expected
if (options === null || options === undefined || options === 'consistent') {
// Empty.
} else if (typeof options === 'number') {
expected = options
} else {
file.fail(
'Unexpected value `' +
options +
"` for `options`, expected `number` or `'consistent'`"
)
}
visitParents(tree, function (node, parents) {
// Do not walk into phrasing.
if (phrasing(node)) {
return SKIP
}
if (node.type !== 'blockquote') return
const start = pointStart(node)
const headStart = pointStart(node.children[0])
if (headStart && start) {
const actual = headStart.column - start.column
if (expected) {
const difference = expected - actual
const differenceAbsolute = Math.abs(difference)
if (difference !== 0) {
file.message(
'Unexpected `' +
actual +
'` ' +
pluralize('space', actual) +
' between block quote marker and content, expected `' +
expected +
'` ' +
pluralize('space', expected) +
', ' +
(difference > 0 ? 'add' : 'remove') +
' `' +
differenceAbsolute +
'` ' +
pluralize('space', differenceAbsolute),
{ancestors: [...parents, node], place: headStart}
)
}
} else {
expected = actual
}
}
})
}
)
export default remarkLintBlockquoteIndentation