mirror of
https://github.com/remarkjs/remark-lint.git
synced 2024-08-17 15:50:25 +03:00
Refactor more code, improve small things
This commit is contained in:
parent
547188ec6f
commit
627363b2a2
@ -136,6 +136,7 @@
|
||||
"remark-comment-config": "^8.0.0",
|
||||
"remark-gfm": "^4.0.0",
|
||||
"remark-github": "^12.0.0",
|
||||
"remark-mdx": "^3.0.0",
|
||||
"remark-toc": "^9.0.0",
|
||||
"remark-validate-links": "^13.0.0",
|
||||
"strip-indent": "^4.0.0",
|
||||
|
@ -206,8 +206,8 @@ const remarkLintCheckboxCharacterStyle = lintRule(
|
||||
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 */
|
||||
/* c8 ignore next 2 -- failsafe so we don’t crash if there actually isn’t
|
||||
* a checkbox. */
|
||||
if (!match) return
|
||||
|
||||
const style = node.checked ? checked : unchecked
|
||||
|
@ -182,7 +182,8 @@ When configured with `{ checked: 'x' }`.
|
||||
|
||||
###### In
|
||||
|
||||
> 👉 **Note**: this example uses GFM ([`remark-gfm`][github-remark-gfm]).
|
||||
> 👉 **Note**: this example uses
|
||||
> GFM ([`remark-gfm`][github-remark-gfm]).
|
||||
|
||||
```markdown
|
||||
- [x] List item
|
||||
@ -199,7 +200,8 @@ When configured with `{ checked: 'X' }`.
|
||||
|
||||
###### In
|
||||
|
||||
> 👉 **Note**: this example uses GFM ([`remark-gfm`][github-remark-gfm]).
|
||||
> 👉 **Note**: this example uses
|
||||
> GFM ([`remark-gfm`][github-remark-gfm]).
|
||||
|
||||
```markdown
|
||||
- [X] List item
|
||||
@ -216,7 +218,8 @@ When configured with `{ unchecked: ' ' }`.
|
||||
|
||||
###### In
|
||||
|
||||
> 👉 **Note**: this example uses GFM ([`remark-gfm`][github-remark-gfm]).
|
||||
> 👉 **Note**: this example uses
|
||||
> GFM ([`remark-gfm`][github-remark-gfm]).
|
||||
|
||||
```markdown
|
||||
- [ ] List item
|
||||
@ -235,7 +238,8 @@ When configured with `{ unchecked: '\t' }`.
|
||||
|
||||
###### In
|
||||
|
||||
> 👉 **Note**: this example uses GFM ([`remark-gfm`][github-remark-gfm]).
|
||||
> 👉 **Note**: this example uses
|
||||
> GFM ([`remark-gfm`][github-remark-gfm]).
|
||||
|
||||
```markdown
|
||||
- [␉] List item
|
||||
@ -250,7 +254,8 @@ No messages.
|
||||
|
||||
###### In
|
||||
|
||||
> 👉 **Note**: this example uses GFM ([`remark-gfm`][github-remark-gfm]).
|
||||
> 👉 **Note**: this example uses
|
||||
> GFM ([`remark-gfm`][github-remark-gfm]).
|
||||
|
||||
```markdown
|
||||
- [x] List item
|
||||
|
@ -159,7 +159,8 @@ content after them with a single space between.
|
||||
|
||||
###### In
|
||||
|
||||
> 👉 **Note**: this example uses GFM ([`remark-gfm`][github-remark-gfm]).
|
||||
> 👉 **Note**: this example uses
|
||||
> GFM ([`remark-gfm`][github-remark-gfm]).
|
||||
|
||||
```markdown
|
||||
- [ ] List item
|
||||
@ -176,7 +177,8 @@ No messages.
|
||||
|
||||
###### In
|
||||
|
||||
> 👉 **Note**: this example uses GFM ([`remark-gfm`][github-remark-gfm]).
|
||||
> 👉 **Note**: this example uses
|
||||
> GFM ([`remark-gfm`][github-remark-gfm]).
|
||||
|
||||
```markdown
|
||||
- [ ] List item
|
||||
|
@ -57,26 +57,44 @@
|
||||
* @example
|
||||
* {"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
|
||||
* {"name": "ok-comments.md"}
|
||||
* {"name": "ok-html-comments.md"}
|
||||
*
|
||||
* Paragraph.
|
||||
*
|
||||
* [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/
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {import('mdast').Definition} Definition
|
||||
* @typedef {import('mdast').FootnoteDefinition} FootnoteDefinition
|
||||
* @typedef {import('mdast').Root} Root
|
||||
*
|
||||
* @typedef {import('unist').Point} Point
|
||||
*/
|
||||
|
||||
/// <reference types="mdast-util-mdx" />
|
||||
|
||||
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'
|
||||
|
||||
const remarkLintFinalDefinition = lintRule(
|
||||
@ -91,40 +109,47 @@ const remarkLintFinalDefinition = lintRule(
|
||||
* Nothing.
|
||||
*/
|
||||
function (tree, file) {
|
||||
let last = 0
|
||||
/** @type {Array<Definition | FootnoteDefinition>} */
|
||||
const definitions = []
|
||||
/** @type {Point | undefined} */
|
||||
let last
|
||||
|
||||
visit(
|
||||
tree,
|
||||
function (node) {
|
||||
const start = pointStart(node)
|
||||
visit(tree, function (node) {
|
||||
if (node.type === 'definition' || node.type === 'footnoteDefinition') {
|
||||
definitions.push(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?
|
||||
// Ignore generated and HTML comment nodes.
|
||||
if (
|
||||
!start ||
|
||||
node.type === 'root' ||
|
||||
(node.type === 'html' && /^\s*<!--/.test(node.value))
|
||||
) {
|
||||
return
|
||||
if (place) {
|
||||
last = place
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const line = start.line
|
||||
for (const node of definitions) {
|
||||
const point = pointStart(node)
|
||||
|
||||
if (node.type === 'definition') {
|
||||
if (last && last > line) {
|
||||
file.message(
|
||||
'Move definitions to the end of the file (after the node at line `' +
|
||||
last +
|
||||
'`)',
|
||||
node
|
||||
)
|
||||
}
|
||||
} else if (last === 0) {
|
||||
last = line
|
||||
}
|
||||
},
|
||||
true
|
||||
)
|
||||
if (point && last && point.line < last.line) {
|
||||
file.message(
|
||||
'Move definitions to the end of the file (after `' +
|
||||
stringifyPosition(last) +
|
||||
'`)',
|
||||
node
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -33,8 +33,11 @@
|
||||
],
|
||||
"dependencies": {
|
||||
"@types/mdast": "^4.0.0",
|
||||
"@types/unist": "^3.0.0",
|
||||
"mdast-util-mdx": "^3.0.0",
|
||||
"unified-lint-rule": "^2.0.0",
|
||||
"unist-util-position": "^5.0.0",
|
||||
"unist-util-stringify-position": "^4.0.0",
|
||||
"unist-util-visit": "^5.0.0"
|
||||
},
|
||||
"scripts": {},
|
||||
|
@ -171,10 +171,10 @@ Another paragraph.
|
||||
###### Out
|
||||
|
||||
```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
|
||||
|
||||
@ -183,7 +183,28 @@ Paragraph.
|
||||
|
||||
[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/
|
||||
```
|
||||
@ -263,6 +284,8 @@ abide by its terms.
|
||||
|
||||
[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
|
||||
|
||||
[npm-install]: https://docs.npmjs.com/cli/install
|
||||
|
@ -81,6 +81,7 @@
|
||||
*/
|
||||
|
||||
import {lintRule} from 'unified-lint-rule'
|
||||
import {location} from 'vfile-location'
|
||||
|
||||
const remarkLintFinalNewline = lintRule(
|
||||
{
|
||||
@ -95,11 +96,11 @@ const remarkLintFinalNewline = lintRule(
|
||||
*/
|
||||
function (_, file) {
|
||||
const value = String(file)
|
||||
const end = location(file).toPoint(value.length)
|
||||
const last = value.length - 1
|
||||
|
||||
if (last > -1 && value.charAt(last) !== '\n') {
|
||||
// To do: warn at last character.
|
||||
file.message('Missing newline character at end of file')
|
||||
if (end && last > -1 && value.charAt(last) !== '\n') {
|
||||
file.message('Missing newline character at end of file', end)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
@ -33,7 +33,8 @@
|
||||
],
|
||||
"dependencies": {
|
||||
"@types/mdast": "^4.0.0",
|
||||
"unified-lint-rule": "^2.0.0"
|
||||
"unified-lint-rule": "^2.0.0",
|
||||
"vfile-location": "^5.0.0"
|
||||
},
|
||||
"scripts": {},
|
||||
"typeCoverage": {
|
||||
|
@ -143,6 +143,13 @@
|
||||
* {"name": "not-ok-html.md", "config": 2, "label": "output"}
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/// <reference types="mdast-util-mdx" />
|
||||
|
||||
import {lintRule} from 'unified-lint-rule'
|
||||
import {position} from 'unist-util-position'
|
||||
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(
|
||||
{
|
||||
@ -187,11 +197,17 @@ const remarkLintFirstHeadingLevel = lintRule(
|
||||
/** @type {Depth | undefined} */
|
||||
let rank
|
||||
|
||||
// To do: MDX?
|
||||
if (node.type === 'heading') {
|
||||
rank = node.depth
|
||||
} 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
|
||||
}
|
||||
|
||||
|
@ -34,6 +34,7 @@
|
||||
],
|
||||
"dependencies": {
|
||||
"@types/mdast": "^4.0.0",
|
||||
"mdast-util-mdx": "^3.0.0",
|
||||
"unified-lint-rule": "^2.0.0",
|
||||
"unist-util-position": "^5.0.0",
|
||||
"unist-util-visit": "^5.0.0"
|
||||
|
@ -305,6 +305,23 @@ Paragraph.
|
||||
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
|
||||
|
||||
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-mdx]: https://mdxjs.com/packages/remark-mdx/
|
||||
|
||||
[github-unified-transformer]: https://github.com/unifiedjs/unified#transformer
|
||||
|
||||
[npm-install]: https://docs.npmjs.com/cli/install
|
||||
|
@ -126,8 +126,7 @@ const remarkLintListItemContentIndent = lintRule(
|
||||
|
||||
file.message(
|
||||
'Don’t use mixed indentation for children, ' +
|
||||
// Hard to test, I couldn’t find it at least.
|
||||
/* c8 ignore next */
|
||||
/* c8 ignore next -- hard to test, I couldn’t find it at least. */
|
||||
(diff > 0 ? 'add' : 'remove') +
|
||||
' ' +
|
||||
abs +
|
||||
|
@ -151,7 +151,8 @@ Further children should align with it.
|
||||
|
||||
###### In
|
||||
|
||||
> 👉 **Note**: this example uses GFM ([`remark-gfm`][github-remark-gfm]).
|
||||
> 👉 **Note**: this example uses
|
||||
> GFM ([`remark-gfm`][github-remark-gfm]).
|
||||
|
||||
```markdown
|
||||
1.␠[x] Alpha
|
||||
@ -166,7 +167,8 @@ No messages.
|
||||
|
||||
###### In
|
||||
|
||||
> 👉 **Note**: this example uses GFM ([`remark-gfm`][github-remark-gfm]).
|
||||
> 👉 **Note**: this example uses
|
||||
> GFM ([`remark-gfm`][github-remark-gfm]).
|
||||
|
||||
```markdown
|
||||
1.␠[x] Charlie
|
||||
|
@ -119,6 +119,8 @@
|
||||
* 4:12: Line must be at most 10 characters
|
||||
*/
|
||||
|
||||
/// <reference types="mdast-util-mdx" />
|
||||
|
||||
/**
|
||||
* @typedef {import('mdast').Root} Root
|
||||
*/
|
||||
@ -146,26 +148,14 @@ const remarkLintMaximumLineLength = lintRule(
|
||||
const option = options || 80
|
||||
|
||||
visit(tree, function (node) {
|
||||
// To do: add MDX types, etc.
|
||||
// To do: remove MDX 1?
|
||||
if (
|
||||
node.type === 'code' ||
|
||||
node.type === 'definition' ||
|
||||
node.type === 'heading' ||
|
||||
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' ||
|
||||
// @ts-expect-error: MDX
|
||||
node.type === 'mdxTextExpression' ||
|
||||
// @ts-expect-error: MDX
|
||||
node.type === 'mdxjsEsm' ||
|
||||
node.type === 'table' ||
|
||||
// @ts-expect-error: TOML from frontmatter.
|
||||
node.type === 'toml' ||
|
||||
node.type === 'yaml'
|
||||
|
@ -32,6 +32,7 @@
|
||||
],
|
||||
"dependencies": {
|
||||
"@types/mdast": "^4.0.0",
|
||||
"mdast-util-mdx": "^3.0.0",
|
||||
"unified-lint-rule": "^2.0.0",
|
||||
"unist-util-position": "^5.0.0",
|
||||
"unist-util-visit": "^5.0.0"
|
||||
|
@ -151,7 +151,8 @@ Whether to wrap prose or not is a stylistic choice.
|
||||
|
||||
###### In
|
||||
|
||||
> 👉 **Note**: this example uses GFM ([`remark-gfm`][github-remark-gfm]).
|
||||
> 👉 **Note**: this example uses
|
||||
> GFM ([`remark-gfm`][github-remark-gfm]).
|
||||
|
||||
```markdown
|
||||
This line is simply not toooooooooooooooooooooooooooooooooooooooooooo
|
||||
|
@ -166,7 +166,8 @@ No messages.
|
||||
|
||||
###### In
|
||||
|
||||
> 👉 **Note**: this example uses GFM ([`remark-gfm`][github-remark-gfm]).
|
||||
> 👉 **Note**: this example uses
|
||||
> GFM ([`remark-gfm`][github-remark-gfm]).
|
||||
|
||||
```markdown
|
||||
http://foo.bar/baz
|
||||
|
@ -90,6 +90,7 @@
|
||||
* @typedef {import('mdast').Root} Root
|
||||
*/
|
||||
|
||||
import {collapseWhiteSpace} from 'collapse-white-space'
|
||||
import {lintRule} from 'unified-lint-rule'
|
||||
import {position} from 'unist-util-position'
|
||||
import {visit} from 'unist-util-visit'
|
||||
@ -140,23 +141,27 @@ const remarkLintNoShellDollars = lintRule(
|
||||
|
||||
// Check known shell code.
|
||||
if (place && node.lang && flags.has(node.lang)) {
|
||||
const lines = node.value.split('\n').filter(function (line) {
|
||||
return line.trim().length > 0
|
||||
})
|
||||
const lines = node.value.split('\n')
|
||||
let index = -1
|
||||
|
||||
if (lines.length === 0) {
|
||||
return
|
||||
}
|
||||
let hasLines = false
|
||||
|
||||
while (++index < lines.length) {
|
||||
const line = lines[index].trim()
|
||||
const line = collapseWhiteSpace(lines[index], {style: 'html'})
|
||||
|
||||
if (!line) continue
|
||||
|
||||
hasLines = true
|
||||
|
||||
if (!/^\$/.test(line)) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasLines) {
|
||||
return
|
||||
}
|
||||
|
||||
file.message('Do not use dollar signs before shell commands', place)
|
||||
}
|
||||
})
|
||||
|
@ -32,6 +32,7 @@
|
||||
],
|
||||
"dependencies": {
|
||||
"@types/mdast": "^4.0.0",
|
||||
"collapse-white-space": "^2.0.0",
|
||||
"unified-lint-rule": "^2.0.0",
|
||||
"unist-util-position": "^5.0.0",
|
||||
"unist-util-visit": "^5.0.0"
|
||||
|
@ -138,8 +138,8 @@ const remarkLintNoTableIndentation = lintRule(
|
||||
const head = parent.children[0]
|
||||
column = pointStart(head)?.column
|
||||
|
||||
// Skip past the first line if we’re the first child of a list item.
|
||||
/* c8 ignore next 3 */
|
||||
/* c8 ignore next 4 -- skip past the first line if we’re the first
|
||||
* child of a list item. */
|
||||
if (typeof line === 'number' && head === node) {
|
||||
line++
|
||||
}
|
||||
|
@ -152,7 +152,8 @@ So it’s recommended to not indent tables and to turn this rule on.
|
||||
|
||||
###### In
|
||||
|
||||
> 👉 **Note**: this example uses GFM ([`remark-gfm`][github-remark-gfm]).
|
||||
> 👉 **Note**: this example uses
|
||||
> GFM ([`remark-gfm`][github-remark-gfm]).
|
||||
|
||||
```markdown
|
||||
Paragraph.
|
||||
@ -170,7 +171,8 @@ No messages.
|
||||
|
||||
###### In
|
||||
|
||||
> 👉 **Note**: this example uses GFM ([`remark-gfm`][github-remark-gfm]).
|
||||
> 👉 **Note**: this example uses
|
||||
> GFM ([`remark-gfm`][github-remark-gfm]).
|
||||
|
||||
```markdown
|
||||
Paragraph.
|
||||
@ -192,7 +194,8 @@ Paragraph.
|
||||
|
||||
###### In
|
||||
|
||||
> 👉 **Note**: this example uses GFM ([`remark-gfm`][github-remark-gfm]).
|
||||
> 👉 **Note**: this example uses
|
||||
> GFM ([`remark-gfm`][github-remark-gfm]).
|
||||
|
||||
```markdown
|
||||
>␠␠| A |
|
||||
@ -209,7 +212,8 @@ Paragraph.
|
||||
|
||||
###### In
|
||||
|
||||
> 👉 **Note**: this example uses GFM ([`remark-gfm`][github-remark-gfm]).
|
||||
> 👉 **Note**: this example uses
|
||||
> GFM ([`remark-gfm`][github-remark-gfm]).
|
||||
|
||||
```markdown
|
||||
-␠␠␠paragraph
|
||||
|
@ -213,9 +213,9 @@ const remarkLintNoUndefinedReferences = lintRule(
|
||||
|
||||
visit(tree, function (node) {
|
||||
const place = position(node)
|
||||
// CM specifiers that references only form when defined.
|
||||
// Still, they could be added by plugins, so let’s keep it.
|
||||
/* c8 ignore next 10 */
|
||||
/* c8 ignore next 12 -- CM specifies that references only form when
|
||||
* defined.
|
||||
* Still, they could be added by plugins, so let’s keep it. */
|
||||
if (
|
||||
(node.type === 'imageReference' ||
|
||||
node.type === 'linkReference' ||
|
||||
|
@ -92,7 +92,7 @@ const remarkLintNoUnneededFullReferenceImage = lintRule(
|
||||
if (
|
||||
!place ||
|
||||
node.referenceType !== 'full' ||
|
||||
/* c8 ignore next */
|
||||
/* c8 ignore next -- generated AST can omit `alt`. */
|
||||
normalizeIdentifier(node.alt || '') !== node.identifier.toUpperCase()
|
||||
) {
|
||||
return
|
||||
|
@ -62,11 +62,12 @@
|
||||
* @example
|
||||
* {"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
|
||||
*/
|
||||
|
||||
@ -86,13 +87,19 @@ const remarkLintNoUnusedDefinitions = lintRule(
|
||||
* Nothing.
|
||||
*/
|
||||
function (tree, file) {
|
||||
/** @type {Map<string, {node: DefinitionContent | undefined, used: boolean}>} */
|
||||
const map = new Map()
|
||||
/** @type {Map<string, {node: Definition | FootnoteDefinition | undefined, used: boolean}>} */
|
||||
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) {
|
||||
if ('identifier' in node) {
|
||||
const id = node.identifier.toLowerCase()
|
||||
const map =
|
||||
node.type === 'footnoteDefinition' ||
|
||||
node.type === 'footnoteReference'
|
||||
? footnoteDefinitions
|
||||
: definitions
|
||||
let entry = map.get(id)
|
||||
|
||||
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)
|
||||
|
||||
if (place && !entry.used) {
|
||||
|
@ -170,7 +170,8 @@ No messages.
|
||||
|
||||
###### In
|
||||
|
||||
> 👉 **Note**: this example uses GFM ([`remark-gfm`][github-remark-gfm]).
|
||||
> 👉 **Note**: this example uses
|
||||
> GFM ([`remark-gfm`][github-remark-gfm]).
|
||||
|
||||
```markdown
|
||||
a[^x].
|
||||
@ -182,7 +183,7 @@ a[^x].
|
||||
###### Out
|
||||
|
||||
```text
|
||||
4:1-4:13: Found unused definition
|
||||
4:1-4:13: Found unused footnote definition
|
||||
```
|
||||
|
||||
## Compatibility
|
||||
|
@ -177,7 +177,8 @@ When configured with `'~'`.
|
||||
|
||||
###### In
|
||||
|
||||
> 👉 **Note**: this example uses GFM ([`remark-gfm`][github-remark-gfm]).
|
||||
> 👉 **Note**: this example uses
|
||||
> GFM ([`remark-gfm`][github-remark-gfm]).
|
||||
|
||||
```markdown
|
||||
~foo~
|
||||
@ -193,7 +194,8 @@ When configured with `'~'`.
|
||||
|
||||
###### In
|
||||
|
||||
> 👉 **Note**: this example uses GFM ([`remark-gfm`][github-remark-gfm]).
|
||||
> 👉 **Note**: this example uses
|
||||
> GFM ([`remark-gfm`][github-remark-gfm]).
|
||||
|
||||
```markdown
|
||||
~~foo~~
|
||||
@ -211,7 +213,8 @@ When configured with `'~~'`.
|
||||
|
||||
###### In
|
||||
|
||||
> 👉 **Note**: this example uses GFM ([`remark-gfm`][github-remark-gfm]).
|
||||
> 👉 **Note**: this example uses
|
||||
> GFM ([`remark-gfm`][github-remark-gfm]).
|
||||
|
||||
```markdown
|
||||
~~foo~~
|
||||
@ -227,7 +230,8 @@ When configured with `'~~'`.
|
||||
|
||||
###### In
|
||||
|
||||
> 👉 **Note**: this example uses GFM ([`remark-gfm`][github-remark-gfm]).
|
||||
> 👉 **Note**: this example uses
|
||||
> GFM ([`remark-gfm`][github-remark-gfm]).
|
||||
|
||||
```markdown
|
||||
~foo~
|
||||
@ -243,7 +247,8 @@ When configured with `'~~'`.
|
||||
|
||||
###### In
|
||||
|
||||
> 👉 **Note**: this example uses GFM ([`remark-gfm`][github-remark-gfm]).
|
||||
> 👉 **Note**: this example uses
|
||||
> GFM ([`remark-gfm`][github-remark-gfm]).
|
||||
|
||||
```markdown
|
||||
~~foo~~
|
||||
|
@ -271,7 +271,7 @@ const remarkLintTableCellPadding = lintRule(
|
||||
|
||||
visit(tree, 'table', function (node) {
|
||||
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 || []
|
||||
/** @type {Array<number>} */
|
||||
const sizes = []
|
||||
@ -414,7 +414,6 @@ export default remarkLintTableCellPadding
|
||||
function size(node) {
|
||||
const head = pointStart(node.children[0])?.offset
|
||||
const tail = pointEnd(node.children[node.children.length - 1])?.offset
|
||||
// Only called when we’re sure offsets exist.
|
||||
/* c8 ignore next */
|
||||
/* c8 ignore next -- Only called when we’re sure offsets exist. */
|
||||
return typeof head === 'number' && typeof tail === 'number' ? tail - head : 0
|
||||
}
|
||||
|
@ -186,7 +186,8 @@ When configured with `'padded'`.
|
||||
|
||||
###### In
|
||||
|
||||
> 👉 **Note**: this example uses GFM ([`remark-gfm`][github-remark-gfm]).
|
||||
> 👉 **Note**: this example uses
|
||||
> GFM ([`remark-gfm`][github-remark-gfm]).
|
||||
|
||||
```markdown
|
||||
| A | B |
|
||||
@ -204,7 +205,8 @@ When configured with `'padded'`.
|
||||
|
||||
###### In
|
||||
|
||||
> 👉 **Note**: this example uses GFM ([`remark-gfm`][github-remark-gfm]).
|
||||
> 👉 **Note**: this example uses
|
||||
> GFM ([`remark-gfm`][github-remark-gfm]).
|
||||
|
||||
```markdown
|
||||
| A | B |
|
||||
@ -242,7 +244,8 @@ When configured with `'compact'`.
|
||||
|
||||
###### In
|
||||
|
||||
> 👉 **Note**: this example uses GFM ([`remark-gfm`][github-remark-gfm]).
|
||||
> 👉 **Note**: this example uses
|
||||
> GFM ([`remark-gfm`][github-remark-gfm]).
|
||||
|
||||
```markdown
|
||||
|A |B |
|
||||
@ -260,7 +263,8 @@ When configured with `'compact'`.
|
||||
|
||||
###### In
|
||||
|
||||
> 👉 **Note**: this example uses GFM ([`remark-gfm`][github-remark-gfm]).
|
||||
> 👉 **Note**: this example uses
|
||||
> GFM ([`remark-gfm`][github-remark-gfm]).
|
||||
|
||||
```markdown
|
||||
| A | B |
|
||||
@ -284,7 +288,8 @@ When configured with `'compact'`.
|
||||
|
||||
###### In
|
||||
|
||||
> 👉 **Note**: this example uses GFM ([`remark-gfm`][github-remark-gfm]).
|
||||
> 👉 **Note**: this example uses
|
||||
> GFM ([`remark-gfm`][github-remark-gfm]).
|
||||
|
||||
```markdown
|
||||
The default is `'consistent'`.
|
||||
@ -308,7 +313,8 @@ When configured with `'consistent'`.
|
||||
|
||||
###### In
|
||||
|
||||
> 👉 **Note**: this example uses GFM ([`remark-gfm`][github-remark-gfm]).
|
||||
> 👉 **Note**: this example uses
|
||||
> GFM ([`remark-gfm`][github-remark-gfm]).
|
||||
|
||||
```markdown
|
||||
| A | B |
|
||||
@ -332,7 +338,8 @@ When configured with `'consistent'`.
|
||||
|
||||
###### In
|
||||
|
||||
> 👉 **Note**: this example uses GFM ([`remark-gfm`][github-remark-gfm]).
|
||||
> 👉 **Note**: this example uses
|
||||
> GFM ([`remark-gfm`][github-remark-gfm]).
|
||||
|
||||
```markdown
|
||||
|A |B |
|
||||
@ -354,7 +361,8 @@ When configured with `'consistent'`.
|
||||
|
||||
###### In
|
||||
|
||||
> 👉 **Note**: this example uses GFM ([`remark-gfm`][github-remark-gfm]).
|
||||
> 👉 **Note**: this example uses
|
||||
> GFM ([`remark-gfm`][github-remark-gfm]).
|
||||
|
||||
```markdown
|
||||
|A |B |
|
||||
@ -388,7 +396,8 @@ When configured with `'padded'`.
|
||||
|
||||
###### In
|
||||
|
||||
> 👉 **Note**: this example uses GFM ([`remark-gfm`][github-remark-gfm]).
|
||||
> 👉 **Note**: this example uses
|
||||
> GFM ([`remark-gfm`][github-remark-gfm]).
|
||||
|
||||
```markdown
|
||||
<!-- Empty cells are OK, but those surrounding them may not be. -->
|
||||
@ -412,7 +421,8 @@ When configured with `'padded'`.
|
||||
|
||||
###### In
|
||||
|
||||
> 👉 **Note**: this example uses GFM ([`remark-gfm`][github-remark-gfm]).
|
||||
> 👉 **Note**: this example uses
|
||||
> GFM ([`remark-gfm`][github-remark-gfm]).
|
||||
|
||||
```markdown
|
||||
<!-- Missing cells are fine as well. -->
|
||||
|
@ -160,7 +160,8 @@ in which case this rule must be turned off.
|
||||
|
||||
###### In
|
||||
|
||||
> 👉 **Note**: this example uses GFM ([`remark-gfm`][github-remark-gfm]).
|
||||
> 👉 **Note**: this example uses
|
||||
> GFM ([`remark-gfm`][github-remark-gfm]).
|
||||
|
||||
```markdown
|
||||
| A | B |
|
||||
@ -176,7 +177,8 @@ No messages.
|
||||
|
||||
###### In
|
||||
|
||||
> 👉 **Note**: this example uses GFM ([`remark-gfm`][github-remark-gfm]).
|
||||
> 👉 **Note**: this example uses
|
||||
> GFM ([`remark-gfm`][github-remark-gfm]).
|
||||
|
||||
```markdown
|
||||
| A | B |
|
||||
@ -195,7 +197,8 @@ No messages.
|
||||
|
||||
###### In
|
||||
|
||||
> 👉 **Note**: this example uses GFM ([`remark-gfm`][github-remark-gfm]).
|
||||
> 👉 **Note**: this example uses
|
||||
> GFM ([`remark-gfm`][github-remark-gfm]).
|
||||
|
||||
```markdown
|
||||
| | B | |
|
||||
@ -211,7 +214,8 @@ No messages.
|
||||
|
||||
###### In
|
||||
|
||||
> 👉 **Note**: this example uses GFM ([`remark-gfm`][github-remark-gfm]).
|
||||
> 👉 **Note**: this example uses
|
||||
> GFM ([`remark-gfm`][github-remark-gfm]).
|
||||
|
||||
```markdown
|
||||
| | | |
|
||||
|
@ -153,7 +153,8 @@ delimiters.
|
||||
|
||||
###### In
|
||||
|
||||
> 👉 **Note**: this example uses GFM ([`remark-gfm`][github-remark-gfm]).
|
||||
> 👉 **Note**: this example uses
|
||||
> GFM ([`remark-gfm`][github-remark-gfm]).
|
||||
|
||||
```markdown
|
||||
| A | B |
|
||||
@ -169,7 +170,8 @@ No messages.
|
||||
|
||||
###### In
|
||||
|
||||
> 👉 **Note**: this example uses GFM ([`remark-gfm`][github-remark-gfm]).
|
||||
> 👉 **Note**: this example uses
|
||||
> GFM ([`remark-gfm`][github-remark-gfm]).
|
||||
|
||||
```markdown
|
||||
A | B
|
||||
|
@ -56,8 +56,7 @@ export function lintRule(meta, rule) {
|
||||
const id = typeof meta === 'string' ? meta : meta.origin
|
||||
const url = typeof meta === 'string' ? undefined : meta.url
|
||||
const parts = id.split(':')
|
||||
// Possibly useful if externalised later.
|
||||
/* c8 ignore next */
|
||||
/* c8 ignore next -- Possibly useful if externalised later. */
|
||||
const source = parts[1] ? parts[0] : undefined
|
||||
const ruleId = parts[1]
|
||||
|
||||
@ -94,9 +93,9 @@ export function lintRule(meta, rule) {
|
||||
wrap(rule, function (error) {
|
||||
const messages = file.messages
|
||||
|
||||
// Add the error, if not already properly added.
|
||||
// Only happens for incorrect plugins.
|
||||
/* c8 ignore next 6 */
|
||||
/* c8 ignore next 8 -- add the error,
|
||||
* if not already properly added.
|
||||
* Only happens for incorrect plugins. */
|
||||
// @ts-expect-error: errors could be `messages`.
|
||||
if (error && !messages.includes(error)) {
|
||||
try {
|
||||
|
@ -12,6 +12,8 @@
|
||||
* Whether to use GFM.
|
||||
* @property {string} input
|
||||
* Input.
|
||||
* @property {boolean} mdx
|
||||
* Whether to use MDX.
|
||||
* @property {string} name
|
||||
* Name.
|
||||
* @property {Array<string>} output
|
||||
@ -27,6 +29,8 @@
|
||||
* Whether to use GFM (optional).
|
||||
* @property {'input' | 'output'} [label]
|
||||
* Label (optional).
|
||||
* @property {boolean} [mdx]
|
||||
* Whether to use MDX (optional).
|
||||
* @property {boolean} [positionless]
|
||||
* Whether this check also applies without positions (optional).
|
||||
* @property {string} name
|
||||
@ -168,7 +172,14 @@ async function addPlugin(name) {
|
||||
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 name = info.name
|
||||
|
||||
@ -179,6 +190,7 @@ async function addPlugin(name) {
|
||||
positionless: info.positionless || false,
|
||||
gfm: info.gfm || false,
|
||||
input: exampleValue,
|
||||
mdx: info.mdx || false,
|
||||
output: []
|
||||
})
|
||||
|
||||
@ -198,6 +210,7 @@ async function addPlugin(name) {
|
||||
positionless: info.positionless || false,
|
||||
gfm: info.gfm || false,
|
||||
input: '',
|
||||
mdx: info.mdx || false,
|
||||
output: []
|
||||
}
|
||||
result.checks.push(found)
|
||||
|
@ -1255,6 +1255,10 @@ function generateReadmeExample(state) {
|
||||
'github-remark-gfm',
|
||||
'https://github.com/remarkjs/remark-gfm'
|
||||
)
|
||||
state.urls.set(
|
||||
'github-remark-mdx',
|
||||
'https://mdxjs.com/packages/remark-mdx/'
|
||||
)
|
||||
|
||||
if (!empty) {
|
||||
children.push({
|
||||
@ -1263,8 +1267,40 @@ function generateReadmeExample(state) {
|
||||
children: [{type: 'text', value: 'In'}]
|
||||
})
|
||||
|
||||
// To do: other plugins.
|
||||
/** @type {Array<PhrasingContent>} */
|
||||
const phrasing = []
|
||||
|
||||
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({
|
||||
type: 'blockquote',
|
||||
children: [
|
||||
@ -1276,14 +1312,9 @@ function generateReadmeExample(state) {
|
||||
type: 'strong',
|
||||
children: [{type: 'text', value: 'Note'}]
|
||||
},
|
||||
{type: 'text', value: ': this example uses GFM ('},
|
||||
{
|
||||
type: 'linkReference',
|
||||
identifier: 'github-remark-gfm',
|
||||
referenceType: 'full',
|
||||
children: [{type: 'inlineCode', value: 'remark-gfm'}]
|
||||
},
|
||||
{type: 'text', value: ').'}
|
||||
{type: 'text', value: ': this example uses\n'},
|
||||
...phrasing,
|
||||
{type: 'text', value: '.'}
|
||||
]
|
||||
}
|
||||
]
|
||||
@ -1292,7 +1323,7 @@ function generateReadmeExample(state) {
|
||||
|
||||
children.push({
|
||||
type: 'code',
|
||||
lang: 'markdown',
|
||||
lang: check.mdx ? 'mdx' : 'markdown',
|
||||
value: check.input
|
||||
})
|
||||
}
|
||||
|
35
test.js
35
test.js
@ -16,6 +16,7 @@ import remarkLintFinalNewline from 'remark-lint-final-newline'
|
||||
import remarkLintNoHeadingPunctuation from 'remark-lint-no-heading-punctuation'
|
||||
import remarkLintNoMultipleToplevelHeadings from 'remark-lint-no-multiple-toplevel-headings'
|
||||
import remarkLintNoUndefinedReferences from 'remark-lint-no-undefined-references'
|
||||
import remarkMdx from 'remark-mdx'
|
||||
import {lintRule} from 'unified-lint-rule'
|
||||
import {removePosition} from 'unist-util-remove-position'
|
||||
import {VFile} from 'vfile'
|
||||
@ -81,9 +82,12 @@ test('remark-lint', async function (t) {
|
||||
|
||||
assert.deepEqual(file.messages.map(jsonClone), [
|
||||
{
|
||||
column: 2,
|
||||
fatal: true,
|
||||
line: 1,
|
||||
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',
|
||||
ruleId: 'final-newline',
|
||||
source: 'remark-lint',
|
||||
@ -96,7 +100,7 @@ test('remark-lint', async function (t) {
|
||||
const file = await remark().use(remarkLintFinalNewline, true).process('.')
|
||||
|
||||
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('.')
|
||||
|
||||
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), [
|
||||
{
|
||||
column: 2,
|
||||
fatal: true,
|
||||
line: 1,
|
||||
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',
|
||||
ruleId: 'final-newline',
|
||||
source: 'remark-lint',
|
||||
@ -160,9 +167,12 @@ test('remark-lint', async function (t) {
|
||||
|
||||
assert.deepEqual(file.messages.map(jsonClone), [
|
||||
{
|
||||
column: 2,
|
||||
fatal: false,
|
||||
line: 1,
|
||||
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',
|
||||
ruleId: 'final-newline',
|
||||
source: 'remark-lint',
|
||||
@ -181,9 +191,12 @@ test('remark-lint', async function (t) {
|
||||
|
||||
assert.deepEqual(file.messages.map(jsonClone), [
|
||||
{
|
||||
column: 2,
|
||||
fatal: false,
|
||||
line: 1,
|
||||
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',
|
||||
ruleId: 'final-newline',
|
||||
source: 'remark-lint',
|
||||
@ -302,9 +315,17 @@ async function assertCheck(plugin, info, check) {
|
||||
/** @type {{config: unknown}} */
|
||||
const {config} = JSON.parse(check.configuration)
|
||||
/** @type {PluggableList} */
|
||||
const extras = check.gfm ? [remarkGfm] : []
|
||||
const extras = []
|
||||
const value = controlPictures(check.input)
|
||||
|
||||
if (check.gfm) {
|
||||
extras.push(remarkGfm)
|
||||
}
|
||||
|
||||
if (check.mdx) {
|
||||
extras.push(remarkMdx)
|
||||
}
|
||||
|
||||
const file = await remark()
|
||||
.use(plugin, config)
|
||||
.use(extras)
|
||||
|
Loading…
Reference in New Issue
Block a user