Refactor more code, improve small things

This commit is contained in:
Titus Wormer 2023-12-15 12:57:49 +01:00
parent 547188ec6f
commit 627363b2a2
No known key found for this signature in database
GPG Key ID: E6E581152ED04E2E
35 changed files with 344 additions and 143 deletions

View File

@ -136,6 +136,7 @@
"remark-comment-config": "^8.0.0", "remark-comment-config": "^8.0.0",
"remark-gfm": "^4.0.0", "remark-gfm": "^4.0.0",
"remark-github": "^12.0.0", "remark-github": "^12.0.0",
"remark-mdx": "^3.0.0",
"remark-toc": "^9.0.0", "remark-toc": "^9.0.0",
"remark-validate-links": "^13.0.0", "remark-validate-links": "^13.0.0",
"strip-indent": "^4.0.0", "strip-indent": "^4.0.0",

View File

@ -206,8 +206,8 @@ const remarkLintCheckboxCharacterStyle = lintRule(
value.slice(point.offset - 2, point.offset + 1) value.slice(point.offset - 2, point.offset + 1)
) )
// Failsafe to make sure we dont crash if there actually isnt a checkbox. /* c8 ignore next 2 -- failsafe so we dont crash if there actually isnt
/* c8 ignore next */ * a checkbox. */
if (!match) return if (!match) return
const style = node.checked ? checked : unchecked const style = node.checked ? checked : unchecked

View File

@ -182,7 +182,8 @@ When configured with `{ checked: 'x' }`.
###### In ###### In
> 👉 **Note**: this example uses GFM ([`remark-gfm`][github-remark-gfm]). > 👉 **Note**: this example uses
> GFM ([`remark-gfm`][github-remark-gfm]).
```markdown ```markdown
- [x] List item - [x] List item
@ -199,7 +200,8 @@ When configured with `{ checked: 'X' }`.
###### In ###### In
> 👉 **Note**: this example uses GFM ([`remark-gfm`][github-remark-gfm]). > 👉 **Note**: this example uses
> GFM ([`remark-gfm`][github-remark-gfm]).
```markdown ```markdown
- [X] List item - [X] List item
@ -216,7 +218,8 @@ When configured with `{ unchecked: ' ' }`.
###### In ###### In
> 👉 **Note**: this example uses GFM ([`remark-gfm`][github-remark-gfm]). > 👉 **Note**: this example uses
> GFM ([`remark-gfm`][github-remark-gfm]).
```markdown ```markdown
- [ ] List item - [ ] List item
@ -235,7 +238,8 @@ When configured with `{ unchecked: '\t' }`.
###### In ###### In
> 👉 **Note**: this example uses GFM ([`remark-gfm`][github-remark-gfm]). > 👉 **Note**: this example uses
> GFM ([`remark-gfm`][github-remark-gfm]).
```markdown ```markdown
- [␉] List item - [␉] List item
@ -250,7 +254,8 @@ No messages.
###### In ###### In
> 👉 **Note**: this example uses GFM ([`remark-gfm`][github-remark-gfm]). > 👉 **Note**: this example uses
> GFM ([`remark-gfm`][github-remark-gfm]).
```markdown ```markdown
- [x] List item - [x] List item

View File

@ -159,7 +159,8 @@ content after them with a single space between.
###### In ###### In
> 👉 **Note**: this example uses GFM ([`remark-gfm`][github-remark-gfm]). > 👉 **Note**: this example uses
> GFM ([`remark-gfm`][github-remark-gfm]).
```markdown ```markdown
- [ ] List item - [ ] List item
@ -176,7 +177,8 @@ No messages.
###### In ###### In
> 👉 **Note**: this example uses GFM ([`remark-gfm`][github-remark-gfm]). > 👉 **Note**: this example uses
> GFM ([`remark-gfm`][github-remark-gfm]).
```markdown ```markdown
- [ ] List item - [ ] List item

View File

@ -57,26 +57,44 @@
* @example * @example
* {"name": "not-ok.md", "label": "output"} * {"name": "not-ok.md", "label": "output"}
* *
* 3:1-3:47: Move definitions to the end of the file (after the node at line `5`) * 3:1-3:47: Move definitions to the end of the file (after `5:19`)
* *
* @example * @example
* {"name": "ok-comments.md"} * {"name": "ok-html-comments.md"}
* *
* Paragraph. * Paragraph.
* *
* [example-1]: http://example.com/one/ * [example-1]: http://example.com/one/
* *
* <!-- Comments are fine between and after definitions --> * <!-- Comments are fine between and after definitions. -->
*
* [example-2]: http://example.com/two/
*
* @example
* {"name": "ok-mdx-comments.mdx", "mdx": true}
*
* Paragraph.
*
* [example-1]: http://example.com/one/
*
* {/* Comments are fine in MDX. *␀/}
* *
* [example-2]: http://example.com/two/ * [example-2]: http://example.com/two/
*/ */
/** /**
* @typedef {import('mdast').Definition} Definition
* @typedef {import('mdast').FootnoteDefinition} FootnoteDefinition
* @typedef {import('mdast').Root} Root * @typedef {import('mdast').Root} Root
*
* @typedef {import('unist').Point} Point
*/ */
/// <reference types="mdast-util-mdx" />
import {lintRule} from 'unified-lint-rule' import {lintRule} from 'unified-lint-rule'
import {pointStart} from 'unist-util-position' import {pointEnd, pointStart} from 'unist-util-position'
import {stringifyPosition} from 'unist-util-stringify-position'
import {visit} from 'unist-util-visit' import {visit} from 'unist-util-visit'
const remarkLintFinalDefinition = lintRule( const remarkLintFinalDefinition = lintRule(
@ -91,40 +109,47 @@ const remarkLintFinalDefinition = lintRule(
* Nothing. * Nothing.
*/ */
function (tree, file) { function (tree, file) {
let last = 0 /** @type {Array<Definition | FootnoteDefinition>} */
const definitions = []
/** @type {Point | undefined} */
let last
visit( visit(tree, function (node) {
tree, if (node.type === 'definition' || node.type === 'footnoteDefinition') {
function (node) { definitions.push(node)
const start = pointStart(node) } else if (
node.type === 'root' ||
node.type === 'blockquote' ||
node.type === 'listItem' ||
// Ignore HTML comments.
(node.type === 'html' && /^[\t ]*<!--/.test(node.value)) ||
// Ignore MDX comments.
((node.type === 'mdxFlowExpression' ||
node.type === 'mdxTextExpression') &&
/^\s*\/\*/.test(node.value))
) {
// Empty.
} else {
const place = pointEnd(node)
// To do: ignore MDX comments? if (place) {
// Ignore generated and HTML comment nodes. last = place
if (
!start ||
node.type === 'root' ||
(node.type === 'html' && /^\s*<!--/.test(node.value))
) {
return
} }
}
})
const line = start.line for (const node of definitions) {
const point = pointStart(node)
if (node.type === 'definition') { if (point && last && point.line < last.line) {
if (last && last > line) { file.message(
file.message( 'Move definitions to the end of the file (after `' +
'Move definitions to the end of the file (after the node at line `' + stringifyPosition(last) +
last + '`)',
'`)', node
node )
) }
} }
} else if (last === 0) {
last = line
}
},
true
)
} }
) )

View File

@ -33,8 +33,11 @@
], ],
"dependencies": { "dependencies": {
"@types/mdast": "^4.0.0", "@types/mdast": "^4.0.0",
"@types/unist": "^3.0.0",
"mdast-util-mdx": "^3.0.0",
"unified-lint-rule": "^2.0.0", "unified-lint-rule": "^2.0.0",
"unist-util-position": "^5.0.0", "unist-util-position": "^5.0.0",
"unist-util-stringify-position": "^4.0.0",
"unist-util-visit": "^5.0.0" "unist-util-visit": "^5.0.0"
}, },
"scripts": {}, "scripts": {},

View File

@ -171,10 +171,10 @@ Another paragraph.
###### Out ###### Out
```text ```text
3:1-3:47: Move definitions to the end of the file (after the node at line `5`) 3:1-3:47: Move definitions to the end of the file (after `5:19`)
``` ```
##### `ok-comments.md` ##### `ok-html-comments.md`
###### In ###### In
@ -183,7 +183,28 @@ Paragraph.
[example-1]: http://example.com/one/ [example-1]: http://example.com/one/
<!-- Comments are fine between and after definitions --> <!-- Comments are fine between and after definitions. -->
[example-2]: http://example.com/two/
```
###### Out
No messages.
##### `ok-mdx-comments.mdx`
###### In
> 👉 **Note**: this example uses
> MDX ([`remark-mdx`][github-remark-mdx]).
```mdx
Paragraph.
[example-1]: http://example.com/one/
{/* Comments are fine in MDX. */}
[example-2]: http://example.com/two/ [example-2]: http://example.com/two/
``` ```
@ -263,6 +284,8 @@ abide by its terms.
[github-remark-lint]: https://github.com/remarkjs/remark-lint [github-remark-lint]: https://github.com/remarkjs/remark-lint
[github-remark-mdx]: https://mdxjs.com/packages/remark-mdx/
[github-unified-transformer]: https://github.com/unifiedjs/unified#transformer [github-unified-transformer]: https://github.com/unifiedjs/unified#transformer
[npm-install]: https://docs.npmjs.com/cli/install [npm-install]: https://docs.npmjs.com/cli/install

View File

@ -81,6 +81,7 @@
*/ */
import {lintRule} from 'unified-lint-rule' import {lintRule} from 'unified-lint-rule'
import {location} from 'vfile-location'
const remarkLintFinalNewline = lintRule( const remarkLintFinalNewline = lintRule(
{ {
@ -95,11 +96,11 @@ const remarkLintFinalNewline = lintRule(
*/ */
function (_, file) { function (_, file) {
const value = String(file) const value = String(file)
const end = location(file).toPoint(value.length)
const last = value.length - 1 const last = value.length - 1
if (last > -1 && value.charAt(last) !== '\n') { if (end && last > -1 && value.charAt(last) !== '\n') {
// To do: warn at last character. file.message('Missing newline character at end of file', end)
file.message('Missing newline character at end of file')
} }
} }
) )

View File

@ -33,7 +33,8 @@
], ],
"dependencies": { "dependencies": {
"@types/mdast": "^4.0.0", "@types/mdast": "^4.0.0",
"unified-lint-rule": "^2.0.0" "unified-lint-rule": "^2.0.0",
"vfile-location": "^5.0.0"
}, },
"scripts": {}, "scripts": {},
"typeCoverage": { "typeCoverage": {

View File

@ -143,6 +143,13 @@
* {"name": "not-ok-html.md", "config": 2, "label": "output"} * {"name": "not-ok-html.md", "config": 2, "label": "output"}
* *
* 1:1-1:14: First heading level should be `2` * 1:1-1:14: First heading level should be `2`
*
* @example
* {"name": "ok.mdx", "mdx": true}
*
* In <b>MDX</b>, JSX is supported.
*
* <h1>First heading</h1>
*/ */
/** /**
@ -158,11 +165,14 @@
* Configuration. * Configuration.
*/ */
/// <reference types="mdast-util-mdx" />
import {lintRule} from 'unified-lint-rule' import {lintRule} from 'unified-lint-rule'
import {position} from 'unist-util-position' import {position} from 'unist-util-position'
import {EXIT, visit} from 'unist-util-visit' import {EXIT, visit} from 'unist-util-visit'
const re = /<h([1-6])/ const htmlRe = /<h([1-6])/
const jsxNameRe = /h([1-6])/
const remarkLintFirstHeadingLevel = lintRule( const remarkLintFirstHeadingLevel = lintRule(
{ {
@ -187,11 +197,17 @@ const remarkLintFirstHeadingLevel = lintRule(
/** @type {Depth | undefined} */ /** @type {Depth | undefined} */
let rank let rank
// To do: MDX?
if (node.type === 'heading') { if (node.type === 'heading') {
rank = node.depth rank = node.depth
} else if (node.type === 'html') { } else if (node.type === 'html') {
const results = node.value.match(re) const results = node.value.match(htmlRe)
rank = results ? /** @type {Depth} */ (Number(results[1])) : undefined
} else if (
(node.type === 'mdxJsxFlowElement' ||
node.type === 'mdxJsxTextElement') &&
node.name
) {
const results = node.name.match(jsxNameRe)
rank = results ? /** @type {Depth} */ (Number(results[1])) : undefined rank = results ? /** @type {Depth} */ (Number(results[1])) : undefined
} }

View File

@ -34,6 +34,7 @@
], ],
"dependencies": { "dependencies": {
"@types/mdast": "^4.0.0", "@types/mdast": "^4.0.0",
"mdast-util-mdx": "^3.0.0",
"unified-lint-rule": "^2.0.0", "unified-lint-rule": "^2.0.0",
"unist-util-position": "^5.0.0", "unist-util-position": "^5.0.0",
"unist-util-visit": "^5.0.0" "unist-util-visit": "^5.0.0"

View File

@ -305,6 +305,23 @@ Paragraph.
1:1-1:14: First heading level should be `2` 1:1-1:14: First heading level should be `2`
``` ```
##### `ok.mdx`
###### In
> 👉 **Note**: this example uses
> MDX ([`remark-mdx`][github-remark-mdx]).
```mdx
In <b>MDX</b>, JSX is supported.
<h1>First heading</h1>
```
###### Out
No messages.
## Compatibility ## Compatibility
Projects maintained by the unified collective are compatible with maintained Projects maintained by the unified collective are compatible with maintained
@ -380,6 +397,8 @@ abide by its terms.
[github-remark-lint]: https://github.com/remarkjs/remark-lint [github-remark-lint]: https://github.com/remarkjs/remark-lint
[github-remark-mdx]: https://mdxjs.com/packages/remark-mdx/
[github-unified-transformer]: https://github.com/unifiedjs/unified#transformer [github-unified-transformer]: https://github.com/unifiedjs/unified#transformer
[npm-install]: https://docs.npmjs.com/cli/install [npm-install]: https://docs.npmjs.com/cli/install

View File

@ -126,8 +126,7 @@ const remarkLintListItemContentIndent = lintRule(
file.message( file.message(
'Dont use mixed indentation for children, ' + 'Dont use mixed indentation for children, ' +
// Hard to test, I couldnt find it at least. /* c8 ignore next -- hard to test, I couldnt find it at least. */
/* c8 ignore next */
(diff > 0 ? 'add' : 'remove') + (diff > 0 ? 'add' : 'remove') +
' ' + ' ' +
abs + abs +

View File

@ -151,7 +151,8 @@ Further children should align with it.
###### In ###### In
> 👉 **Note**: this example uses GFM ([`remark-gfm`][github-remark-gfm]). > 👉 **Note**: this example uses
> GFM ([`remark-gfm`][github-remark-gfm]).
```markdown ```markdown
1.␠[x] Alpha 1.␠[x] Alpha
@ -166,7 +167,8 @@ No messages.
###### In ###### In
> 👉 **Note**: this example uses GFM ([`remark-gfm`][github-remark-gfm]). > 👉 **Note**: this example uses
> GFM ([`remark-gfm`][github-remark-gfm]).
```markdown ```markdown
1.␠[x] Charlie 1.␠[x] Charlie

View File

@ -119,6 +119,8 @@
* 4:12: Line must be at most 10 characters * 4:12: Line must be at most 10 characters
*/ */
/// <reference types="mdast-util-mdx" />
/** /**
* @typedef {import('mdast').Root} Root * @typedef {import('mdast').Root} Root
*/ */
@ -146,26 +148,14 @@ const remarkLintMaximumLineLength = lintRule(
const option = options || 80 const option = options || 80
visit(tree, function (node) { visit(tree, function (node) {
// To do: add MDX types, etc.
// To do: remove MDX 1?
if ( if (
node.type === 'code' || node.type === 'code' ||
node.type === 'definition' || node.type === 'definition' ||
node.type === 'heading' || node.type === 'heading' ||
node.type === 'html' || node.type === 'html' ||
node.type === 'table' ||
// @ts-expect-error: These are from MDX@1 and MDX@2: <https://github.com/mdx-js/specification>.
node.type === 'jsx' ||
// @ts-expect-error: MDX
node.type === 'mdxFlowExpression' ||
// @ts-expect-error: MDX
node.type === 'mdxJsxFlowElement' ||
// @ts-expect-error: MDX
node.type === 'mdxJsxTextElement' || node.type === 'mdxJsxTextElement' ||
// @ts-expect-error: MDX
node.type === 'mdxTextExpression' || node.type === 'mdxTextExpression' ||
// @ts-expect-error: MDX node.type === 'table' ||
node.type === 'mdxjsEsm' ||
// @ts-expect-error: TOML from frontmatter. // @ts-expect-error: TOML from frontmatter.
node.type === 'toml' || node.type === 'toml' ||
node.type === 'yaml' node.type === 'yaml'

View File

@ -32,6 +32,7 @@
], ],
"dependencies": { "dependencies": {
"@types/mdast": "^4.0.0", "@types/mdast": "^4.0.0",
"mdast-util-mdx": "^3.0.0",
"unified-lint-rule": "^2.0.0", "unified-lint-rule": "^2.0.0",
"unist-util-position": "^5.0.0", "unist-util-position": "^5.0.0",
"unist-util-visit": "^5.0.0" "unist-util-visit": "^5.0.0"

View File

@ -151,7 +151,8 @@ Whether to wrap prose or not is a stylistic choice.
###### In ###### In
> 👉 **Note**: this example uses GFM ([`remark-gfm`][github-remark-gfm]). > 👉 **Note**: this example uses
> GFM ([`remark-gfm`][github-remark-gfm]).
```markdown ```markdown
This line is simply not toooooooooooooooooooooooooooooooooooooooooooo This line is simply not toooooooooooooooooooooooooooooooooooooooooooo

View File

@ -166,7 +166,8 @@ No messages.
###### In ###### In
> 👉 **Note**: this example uses GFM ([`remark-gfm`][github-remark-gfm]). > 👉 **Note**: this example uses
> GFM ([`remark-gfm`][github-remark-gfm]).
```markdown ```markdown
http://foo.bar/baz http://foo.bar/baz

View File

@ -90,6 +90,7 @@
* @typedef {import('mdast').Root} Root * @typedef {import('mdast').Root} Root
*/ */
import {collapseWhiteSpace} from 'collapse-white-space'
import {lintRule} from 'unified-lint-rule' import {lintRule} from 'unified-lint-rule'
import {position} from 'unist-util-position' import {position} from 'unist-util-position'
import {visit} from 'unist-util-visit' import {visit} from 'unist-util-visit'
@ -140,23 +141,27 @@ const remarkLintNoShellDollars = lintRule(
// Check known shell code. // Check known shell code.
if (place && node.lang && flags.has(node.lang)) { if (place && node.lang && flags.has(node.lang)) {
const lines = node.value.split('\n').filter(function (line) { const lines = node.value.split('\n')
return line.trim().length > 0
})
let index = -1 let index = -1
if (lines.length === 0) { let hasLines = false
return
}
while (++index < lines.length) { while (++index < lines.length) {
const line = lines[index].trim() const line = collapseWhiteSpace(lines[index], {style: 'html'})
if (!line) continue
hasLines = true
if (!/^\$/.test(line)) { if (!/^\$/.test(line)) {
return return
} }
} }
if (!hasLines) {
return
}
file.message('Do not use dollar signs before shell commands', place) file.message('Do not use dollar signs before shell commands', place)
} }
}) })

View File

@ -32,6 +32,7 @@
], ],
"dependencies": { "dependencies": {
"@types/mdast": "^4.0.0", "@types/mdast": "^4.0.0",
"collapse-white-space": "^2.0.0",
"unified-lint-rule": "^2.0.0", "unified-lint-rule": "^2.0.0",
"unist-util-position": "^5.0.0", "unist-util-position": "^5.0.0",
"unist-util-visit": "^5.0.0" "unist-util-visit": "^5.0.0"

View File

@ -138,8 +138,8 @@ const remarkLintNoTableIndentation = lintRule(
const head = parent.children[0] const head = parent.children[0]
column = pointStart(head)?.column column = pointStart(head)?.column
// Skip past the first line if were the first child of a list item. /* c8 ignore next 4 -- skip past the first line if were the first
/* c8 ignore next 3 */ * child of a list item. */
if (typeof line === 'number' && head === node) { if (typeof line === 'number' && head === node) {
line++ line++
} }

View File

@ -152,7 +152,8 @@ So its recommended to not indent tables and to turn this rule on.
###### In ###### In
> 👉 **Note**: this example uses GFM ([`remark-gfm`][github-remark-gfm]). > 👉 **Note**: this example uses
> GFM ([`remark-gfm`][github-remark-gfm]).
```markdown ```markdown
Paragraph. Paragraph.
@ -170,7 +171,8 @@ No messages.
###### In ###### In
> 👉 **Note**: this example uses GFM ([`remark-gfm`][github-remark-gfm]). > 👉 **Note**: this example uses
> GFM ([`remark-gfm`][github-remark-gfm]).
```markdown ```markdown
Paragraph. Paragraph.
@ -192,7 +194,8 @@ Paragraph.
###### In ###### In
> 👉 **Note**: this example uses GFM ([`remark-gfm`][github-remark-gfm]). > 👉 **Note**: this example uses
> GFM ([`remark-gfm`][github-remark-gfm]).
```markdown ```markdown
>␠␠| A | >␠␠| A |
@ -209,7 +212,8 @@ Paragraph.
###### In ###### In
> 👉 **Note**: this example uses GFM ([`remark-gfm`][github-remark-gfm]). > 👉 **Note**: this example uses
> GFM ([`remark-gfm`][github-remark-gfm]).
```markdown ```markdown
-␠␠␠paragraph -␠␠␠paragraph

View File

@ -213,9 +213,9 @@ const remarkLintNoUndefinedReferences = lintRule(
visit(tree, function (node) { visit(tree, function (node) {
const place = position(node) const place = position(node)
// CM specifiers that references only form when defined. /* c8 ignore next 12 -- CM specifies that references only form when
// Still, they could be added by plugins, so lets keep it. * defined.
/* c8 ignore next 10 */ * Still, they could be added by plugins, so lets keep it. */
if ( if (
(node.type === 'imageReference' || (node.type === 'imageReference' ||
node.type === 'linkReference' || node.type === 'linkReference' ||

View File

@ -92,7 +92,7 @@ const remarkLintNoUnneededFullReferenceImage = lintRule(
if ( if (
!place || !place ||
node.referenceType !== 'full' || node.referenceType !== 'full' ||
/* c8 ignore next */ /* c8 ignore next -- generated AST can omit `alt`. */
normalizeIdentifier(node.alt || '') !== node.identifier.toUpperCase() normalizeIdentifier(node.alt || '') !== node.identifier.toUpperCase()
) { ) {
return return

View File

@ -62,11 +62,12 @@
* @example * @example
* {"name": "footnote.md", "gfm": true, "label": "output"} * {"name": "footnote.md", "gfm": true, "label": "output"}
* *
* 4:1-4:13: Found unused definition * 4:1-4:13: Found unused footnote definition
*/ */
/** /**
* @typedef {import('mdast').DefinitionContent} DefinitionContent * @typedef {import('mdast').Definition} Definition
* @typedef {import('mdast').FootnoteDefinition} FootnoteDefinition
* @typedef {import('mdast').Root} Root * @typedef {import('mdast').Root} Root
*/ */
@ -86,13 +87,19 @@ const remarkLintNoUnusedDefinitions = lintRule(
* Nothing. * Nothing.
*/ */
function (tree, file) { function (tree, file) {
/** @type {Map<string, {node: DefinitionContent | undefined, used: boolean}>} */ /** @type {Map<string, {node: Definition | FootnoteDefinition | undefined, used: boolean}>} */
const map = new Map() const footnoteDefinitions = new Map()
/** @type {Map<string, {node: Definition | FootnoteDefinition | undefined, used: boolean}>} */
const definitions = new Map()
// To do: separate maps for footnotes/definitions.
visit(tree, function (node) { visit(tree, function (node) {
if ('identifier' in node) { if ('identifier' in node) {
const id = node.identifier.toLowerCase() const id = node.identifier.toLowerCase()
const map =
node.type === 'footnoteDefinition' ||
node.type === 'footnoteReference'
? footnoteDefinitions
: definitions
let entry = map.get(id) let entry = map.get(id)
if (!entry) { if (!entry) {
@ -112,7 +119,15 @@ const remarkLintNoUnusedDefinitions = lintRule(
} }
}) })
for (const entry of map.values()) { for (const entry of footnoteDefinitions.values()) {
const place = position(entry.node)
if (place && !entry.used) {
file.message('Found unused footnote definition', place)
}
}
for (const entry of definitions.values()) {
const place = position(entry.node) const place = position(entry.node)
if (place && !entry.used) { if (place && !entry.used) {

View File

@ -170,7 +170,8 @@ No messages.
###### In ###### In
> 👉 **Note**: this example uses GFM ([`remark-gfm`][github-remark-gfm]). > 👉 **Note**: this example uses
> GFM ([`remark-gfm`][github-remark-gfm]).
```markdown ```markdown
a[^x]. a[^x].
@ -182,7 +183,7 @@ a[^x].
###### Out ###### Out
```text ```text
4:1-4:13: Found unused definition 4:1-4:13: Found unused footnote definition
``` ```
## Compatibility ## Compatibility

View File

@ -177,7 +177,8 @@ When configured with `'~'`.
###### In ###### In
> 👉 **Note**: this example uses GFM ([`remark-gfm`][github-remark-gfm]). > 👉 **Note**: this example uses
> GFM ([`remark-gfm`][github-remark-gfm]).
```markdown ```markdown
~foo~ ~foo~
@ -193,7 +194,8 @@ When configured with `'~'`.
###### In ###### In
> 👉 **Note**: this example uses GFM ([`remark-gfm`][github-remark-gfm]). > 👉 **Note**: this example uses
> GFM ([`remark-gfm`][github-remark-gfm]).
```markdown ```markdown
~~foo~~ ~~foo~~
@ -211,7 +213,8 @@ When configured with `'~~'`.
###### In ###### In
> 👉 **Note**: this example uses GFM ([`remark-gfm`][github-remark-gfm]). > 👉 **Note**: this example uses
> GFM ([`remark-gfm`][github-remark-gfm]).
```markdown ```markdown
~~foo~~ ~~foo~~
@ -227,7 +230,8 @@ When configured with `'~~'`.
###### In ###### In
> 👉 **Note**: this example uses GFM ([`remark-gfm`][github-remark-gfm]). > 👉 **Note**: this example uses
> GFM ([`remark-gfm`][github-remark-gfm]).
```markdown ```markdown
~foo~ ~foo~
@ -243,7 +247,8 @@ When configured with `'~~'`.
###### In ###### In
> 👉 **Note**: this example uses GFM ([`remark-gfm`][github-remark-gfm]). > 👉 **Note**: this example uses
> GFM ([`remark-gfm`][github-remark-gfm]).
```markdown ```markdown
~~foo~~ ~~foo~~

View File

@ -271,7 +271,7 @@ const remarkLintTableCellPadding = lintRule(
visit(tree, 'table', function (node) { visit(tree, 'table', function (node) {
const rows = node.children const rows = node.children
/* c8 ignore next -- to do: fix types to always have `align` defined. */ /* c8 ignore next -- generated AST can omit `align`. */
const align = node.align || [] const align = node.align || []
/** @type {Array<number>} */ /** @type {Array<number>} */
const sizes = [] const sizes = []
@ -414,7 +414,6 @@ export default remarkLintTableCellPadding
function size(node) { function size(node) {
const head = pointStart(node.children[0])?.offset const head = pointStart(node.children[0])?.offset
const tail = pointEnd(node.children[node.children.length - 1])?.offset const tail = pointEnd(node.children[node.children.length - 1])?.offset
// Only called when were sure offsets exist. /* c8 ignore next -- Only called when were sure offsets exist. */
/* c8 ignore next */
return typeof head === 'number' && typeof tail === 'number' ? tail - head : 0 return typeof head === 'number' && typeof tail === 'number' ? tail - head : 0
} }

View File

@ -186,7 +186,8 @@ When configured with `'padded'`.
###### In ###### In
> 👉 **Note**: this example uses GFM ([`remark-gfm`][github-remark-gfm]). > 👉 **Note**: this example uses
> GFM ([`remark-gfm`][github-remark-gfm]).
```markdown ```markdown
| A | B | | A | B |
@ -204,7 +205,8 @@ When configured with `'padded'`.
###### In ###### In
> 👉 **Note**: this example uses GFM ([`remark-gfm`][github-remark-gfm]). > 👉 **Note**: this example uses
> GFM ([`remark-gfm`][github-remark-gfm]).
```markdown ```markdown
| A | B | | A | B |
@ -242,7 +244,8 @@ When configured with `'compact'`.
###### In ###### In
> 👉 **Note**: this example uses GFM ([`remark-gfm`][github-remark-gfm]). > 👉 **Note**: this example uses
> GFM ([`remark-gfm`][github-remark-gfm]).
```markdown ```markdown
|A |B | |A |B |
@ -260,7 +263,8 @@ When configured with `'compact'`.
###### In ###### In
> 👉 **Note**: this example uses GFM ([`remark-gfm`][github-remark-gfm]). > 👉 **Note**: this example uses
> GFM ([`remark-gfm`][github-remark-gfm]).
```markdown ```markdown
| A | B | | A | B |
@ -284,7 +288,8 @@ When configured with `'compact'`.
###### In ###### In
> 👉 **Note**: this example uses GFM ([`remark-gfm`][github-remark-gfm]). > 👉 **Note**: this example uses
> GFM ([`remark-gfm`][github-remark-gfm]).
```markdown ```markdown
The default is `'consistent'`. The default is `'consistent'`.
@ -308,7 +313,8 @@ When configured with `'consistent'`.
###### In ###### In
> 👉 **Note**: this example uses GFM ([`remark-gfm`][github-remark-gfm]). > 👉 **Note**: this example uses
> GFM ([`remark-gfm`][github-remark-gfm]).
```markdown ```markdown
| A | B | | A | B |
@ -332,7 +338,8 @@ When configured with `'consistent'`.
###### In ###### In
> 👉 **Note**: this example uses GFM ([`remark-gfm`][github-remark-gfm]). > 👉 **Note**: this example uses
> GFM ([`remark-gfm`][github-remark-gfm]).
```markdown ```markdown
|A |B | |A |B |
@ -354,7 +361,8 @@ When configured with `'consistent'`.
###### In ###### In
> 👉 **Note**: this example uses GFM ([`remark-gfm`][github-remark-gfm]). > 👉 **Note**: this example uses
> GFM ([`remark-gfm`][github-remark-gfm]).
```markdown ```markdown
|A |B | |A |B |
@ -388,7 +396,8 @@ When configured with `'padded'`.
###### In ###### In
> 👉 **Note**: this example uses GFM ([`remark-gfm`][github-remark-gfm]). > 👉 **Note**: this example uses
> GFM ([`remark-gfm`][github-remark-gfm]).
```markdown ```markdown
<!-- Empty cells are OK, but those surrounding them may not be. --> <!-- Empty cells are OK, but those surrounding them may not be. -->
@ -412,7 +421,8 @@ When configured with `'padded'`.
###### In ###### In
> 👉 **Note**: this example uses GFM ([`remark-gfm`][github-remark-gfm]). > 👉 **Note**: this example uses
> GFM ([`remark-gfm`][github-remark-gfm]).
```markdown ```markdown
<!-- Missing cells are fine as well. --> <!-- Missing cells are fine as well. -->

View File

@ -160,7 +160,8 @@ in which case this rule must be turned off.
###### In ###### In
> 👉 **Note**: this example uses GFM ([`remark-gfm`][github-remark-gfm]). > 👉 **Note**: this example uses
> GFM ([`remark-gfm`][github-remark-gfm]).
```markdown ```markdown
| A | B | | A | B |
@ -176,7 +177,8 @@ No messages.
###### In ###### In
> 👉 **Note**: this example uses GFM ([`remark-gfm`][github-remark-gfm]). > 👉 **Note**: this example uses
> GFM ([`remark-gfm`][github-remark-gfm]).
```markdown ```markdown
| A | B | | A | B |
@ -195,7 +197,8 @@ No messages.
###### In ###### In
> 👉 **Note**: this example uses GFM ([`remark-gfm`][github-remark-gfm]). > 👉 **Note**: this example uses
> GFM ([`remark-gfm`][github-remark-gfm]).
```markdown ```markdown
| | B | | | | B | |
@ -211,7 +214,8 @@ No messages.
###### In ###### In
> 👉 **Note**: this example uses GFM ([`remark-gfm`][github-remark-gfm]). > 👉 **Note**: this example uses
> GFM ([`remark-gfm`][github-remark-gfm]).
```markdown ```markdown
| | | | | | | |

View File

@ -153,7 +153,8 @@ delimiters.
###### In ###### In
> 👉 **Note**: this example uses GFM ([`remark-gfm`][github-remark-gfm]). > 👉 **Note**: this example uses
> GFM ([`remark-gfm`][github-remark-gfm]).
```markdown ```markdown
| A | B | | A | B |
@ -169,7 +170,8 @@ No messages.
###### In ###### In
> 👉 **Note**: this example uses GFM ([`remark-gfm`][github-remark-gfm]). > 👉 **Note**: this example uses
> GFM ([`remark-gfm`][github-remark-gfm]).
```markdown ```markdown
A | B A | B

View File

@ -56,8 +56,7 @@ export function lintRule(meta, rule) {
const id = typeof meta === 'string' ? meta : meta.origin const id = typeof meta === 'string' ? meta : meta.origin
const url = typeof meta === 'string' ? undefined : meta.url const url = typeof meta === 'string' ? undefined : meta.url
const parts = id.split(':') const parts = id.split(':')
// Possibly useful if externalised later. /* c8 ignore next -- Possibly useful if externalised later. */
/* c8 ignore next */
const source = parts[1] ? parts[0] : undefined const source = parts[1] ? parts[0] : undefined
const ruleId = parts[1] const ruleId = parts[1]
@ -94,9 +93,9 @@ export function lintRule(meta, rule) {
wrap(rule, function (error) { wrap(rule, function (error) {
const messages = file.messages const messages = file.messages
// Add the error, if not already properly added. /* c8 ignore next 8 -- add the error,
// Only happens for incorrect plugins. * if not already properly added.
/* c8 ignore next 6 */ * Only happens for incorrect plugins. */
// @ts-expect-error: errors could be `messages`. // @ts-expect-error: errors could be `messages`.
if (error && !messages.includes(error)) { if (error && !messages.includes(error)) {
try { try {

View File

@ -12,6 +12,8 @@
* Whether to use GFM. * Whether to use GFM.
* @property {string} input * @property {string} input
* Input. * Input.
* @property {boolean} mdx
* Whether to use MDX.
* @property {string} name * @property {string} name
* Name. * Name.
* @property {Array<string>} output * @property {Array<string>} output
@ -27,6 +29,8 @@
* Whether to use GFM (optional). * Whether to use GFM (optional).
* @property {'input' | 'output'} [label] * @property {'input' | 'output'} [label]
* Label (optional). * Label (optional).
* @property {boolean} [mdx]
* Whether to use MDX (optional).
* @property {boolean} [positionless] * @property {boolean} [positionless]
* Whether this check also applies without positions (optional). * Whether this check also applies without positions (optional).
* @property {string} name * @property {string} name
@ -168,7 +172,14 @@ async function addPlugin(name) {
throw new Error('Could not parse example in `' + ruleId + '`', {cause}) throw new Error('Could not parse example in `' + ruleId + '`', {cause})
} }
const exampleValue = strip(lines.join('\n').replace(/^\r?\n/g, '')) const exampleValue = strip(
lines
.join('\n')
// Remove head.
.replace(/^\r?\n/g, '')
// Remove magic handling of `nul` control picture.
.replace(/␀/g, '')
)
const configuration = JSON.stringify({config: info.config || true}) const configuration = JSON.stringify({config: info.config || true})
const name = info.name const name = info.name
@ -179,6 +190,7 @@ async function addPlugin(name) {
positionless: info.positionless || false, positionless: info.positionless || false,
gfm: info.gfm || false, gfm: info.gfm || false,
input: exampleValue, input: exampleValue,
mdx: info.mdx || false,
output: [] output: []
}) })
@ -198,6 +210,7 @@ async function addPlugin(name) {
positionless: info.positionless || false, positionless: info.positionless || false,
gfm: info.gfm || false, gfm: info.gfm || false,
input: '', input: '',
mdx: info.mdx || false,
output: [] output: []
} }
result.checks.push(found) result.checks.push(found)

View File

@ -1255,6 +1255,10 @@ function generateReadmeExample(state) {
'github-remark-gfm', 'github-remark-gfm',
'https://github.com/remarkjs/remark-gfm' 'https://github.com/remarkjs/remark-gfm'
) )
state.urls.set(
'github-remark-mdx',
'https://mdxjs.com/packages/remark-mdx/'
)
if (!empty) { if (!empty) {
children.push({ children.push({
@ -1263,8 +1267,40 @@ function generateReadmeExample(state) {
children: [{type: 'text', value: 'In'}] children: [{type: 'text', value: 'In'}]
}) })
// To do: other plugins. /** @type {Array<PhrasingContent>} */
const phrasing = []
if (check.gfm) { if (check.gfm) {
phrasing.push(
{type: 'text', value: 'GFM ('},
{
type: 'linkReference',
identifier: 'github-remark-gfm',
referenceType: 'full',
children: [{type: 'inlineCode', value: 'remark-gfm'}]
},
{type: 'text', value: ')'}
)
}
if (check.mdx) {
if (phrasing.length > 0) {
phrasing.push({type: 'text', value: ',\n'})
}
phrasing.push(
{type: 'text', value: 'MDX ('},
{
type: 'linkReference',
identifier: 'github-remark-mdx',
referenceType: 'full',
children: [{type: 'inlineCode', value: 'remark-mdx'}]
},
{type: 'text', value: ')'}
)
}
if (phrasing.length > 0) {
children.push({ children.push({
type: 'blockquote', type: 'blockquote',
children: [ children: [
@ -1276,14 +1312,9 @@ function generateReadmeExample(state) {
type: 'strong', type: 'strong',
children: [{type: 'text', value: 'Note'}] children: [{type: 'text', value: 'Note'}]
}, },
{type: 'text', value: ': this example uses GFM ('}, {type: 'text', value: ': this example uses\n'},
{ ...phrasing,
type: 'linkReference', {type: 'text', value: '.'}
identifier: 'github-remark-gfm',
referenceType: 'full',
children: [{type: 'inlineCode', value: 'remark-gfm'}]
},
{type: 'text', value: ').'}
] ]
} }
] ]
@ -1292,7 +1323,7 @@ function generateReadmeExample(state) {
children.push({ children.push({
type: 'code', type: 'code',
lang: 'markdown', lang: check.mdx ? 'mdx' : 'markdown',
value: check.input value: check.input
}) })
} }

35
test.js
View File

@ -16,6 +16,7 @@ import remarkLintFinalNewline from 'remark-lint-final-newline'
import remarkLintNoHeadingPunctuation from 'remark-lint-no-heading-punctuation' import remarkLintNoHeadingPunctuation from 'remark-lint-no-heading-punctuation'
import remarkLintNoMultipleToplevelHeadings from 'remark-lint-no-multiple-toplevel-headings' import remarkLintNoMultipleToplevelHeadings from 'remark-lint-no-multiple-toplevel-headings'
import remarkLintNoUndefinedReferences from 'remark-lint-no-undefined-references' import remarkLintNoUndefinedReferences from 'remark-lint-no-undefined-references'
import remarkMdx from 'remark-mdx'
import {lintRule} from 'unified-lint-rule' import {lintRule} from 'unified-lint-rule'
import {removePosition} from 'unist-util-remove-position' import {removePosition} from 'unist-util-remove-position'
import {VFile} from 'vfile' import {VFile} from 'vfile'
@ -81,9 +82,12 @@ test('remark-lint', async function (t) {
assert.deepEqual(file.messages.map(jsonClone), [ assert.deepEqual(file.messages.map(jsonClone), [
{ {
column: 2,
fatal: true, fatal: true,
line: 1,
message: 'Missing newline character at end of file', message: 'Missing newline character at end of file',
name: '1:1', name: '1:2',
place: {column: 2, line: 1, offset: 1},
reason: 'Missing newline character at end of file', reason: 'Missing newline character at end of file',
ruleId: 'final-newline', ruleId: 'final-newline',
source: 'remark-lint', source: 'remark-lint',
@ -96,7 +100,7 @@ test('remark-lint', async function (t) {
const file = await remark().use(remarkLintFinalNewline, true).process('.') const file = await remark().use(remarkLintFinalNewline, true).process('.')
assert.deepEqual(file.messages.map(String), [ assert.deepEqual(file.messages.map(String), [
'1:1: Missing newline character at end of file' '1:2: Missing newline character at end of file'
]) ])
}) })
@ -114,7 +118,7 @@ test('remark-lint', async function (t) {
.process('.') .process('.')
assert.deepEqual(file.messages.map(String), [ assert.deepEqual(file.messages.map(String), [
'1:1: Missing newline character at end of file' '1:2: Missing newline character at end of file'
]) ])
} }
) )
@ -139,9 +143,12 @@ test('remark-lint', async function (t) {
assert.deepEqual(file.messages.map(jsonClone), [ assert.deepEqual(file.messages.map(jsonClone), [
{ {
column: 2,
fatal: true, fatal: true,
line: 1,
message: 'Missing newline character at end of file', message: 'Missing newline character at end of file',
name: '1:1', name: '1:2',
place: {column: 2, line: 1, offset: 1},
reason: 'Missing newline character at end of file', reason: 'Missing newline character at end of file',
ruleId: 'final-newline', ruleId: 'final-newline',
source: 'remark-lint', source: 'remark-lint',
@ -160,9 +167,12 @@ test('remark-lint', async function (t) {
assert.deepEqual(file.messages.map(jsonClone), [ assert.deepEqual(file.messages.map(jsonClone), [
{ {
column: 2,
fatal: false, fatal: false,
line: 1,
message: 'Missing newline character at end of file', message: 'Missing newline character at end of file',
name: '1:1', name: '1:2',
place: {column: 2, line: 1, offset: 1},
reason: 'Missing newline character at end of file', reason: 'Missing newline character at end of file',
ruleId: 'final-newline', ruleId: 'final-newline',
source: 'remark-lint', source: 'remark-lint',
@ -181,9 +191,12 @@ test('remark-lint', async function (t) {
assert.deepEqual(file.messages.map(jsonClone), [ assert.deepEqual(file.messages.map(jsonClone), [
{ {
column: 2,
fatal: false, fatal: false,
line: 1,
message: 'Missing newline character at end of file', message: 'Missing newline character at end of file',
name: '1:1', name: '1:2',
place: {column: 2, line: 1, offset: 1},
reason: 'Missing newline character at end of file', reason: 'Missing newline character at end of file',
ruleId: 'final-newline', ruleId: 'final-newline',
source: 'remark-lint', source: 'remark-lint',
@ -302,9 +315,17 @@ async function assertCheck(plugin, info, check) {
/** @type {{config: unknown}} */ /** @type {{config: unknown}} */
const {config} = JSON.parse(check.configuration) const {config} = JSON.parse(check.configuration)
/** @type {PluggableList} */ /** @type {PluggableList} */
const extras = check.gfm ? [remarkGfm] : [] const extras = []
const value = controlPictures(check.input) const value = controlPictures(check.input)
if (check.gfm) {
extras.push(remarkGfm)
}
if (check.mdx) {
extras.push(remarkMdx)
}
const file = await remark() const file = await remark()
.use(plugin, config) .use(plugin, config)
.use(extras) .use(extras)