Refactor code style in scripts

This commit is contained in:
Titus Wormer 2023-11-11 17:47:26 +01:00
parent db4810d891
commit 0e79b65cc7
No known key found for this signature in database
GPG Key ID: E6E581152ED04E2E
89 changed files with 3128 additions and 3384 deletions

View File

@ -58,9 +58,14 @@ touch .remarkrc.js
```js ```js
// .remarkrc.js // .remarkrc.js
module.exports = { /**
plugins: [] * @typedef {import('unified').Preset} Preset
} */
/** @type {Preset} */
const preset = {plugins: []}
export default preset
``` ```
Then, in our `package.json`, add the following script to process all the Then, in our `package.json`, add the following script to process all the
@ -142,7 +147,7 @@ import {lintRule} from 'unified-lint-rule'
const remarkLintNoGifAllowed = lintRule( const remarkLintNoGifAllowed = lintRule(
'remark-lint:no-gif-allowed', 'remark-lint:no-gif-allowed',
(tree, file, options) => { function (tree, file, options) {
// Rule implementation // Rule implementation
} }
) )
@ -155,9 +160,16 @@ namespace.
If your project was named `my-project`, then you can export your rule as: If your project was named `my-project`, then you can export your rule as:
```js ```js
const remarkLintNoGifAllowed = lintRule('my-project-name:no-gif-allowed', () => {}) const remarkLintNoGifAllowed = lintRule(
'my-project-name:no-gif-allowed',
function () {}
)
// Or: // Or:
const remarkLintNoGifAllowed = lintRule('my-npm-published-package:no-gif-allowed', () => {}) const remarkLintNoGifAllowed = lintRule(
'my-npm-published-package:no-gif-allowed',
function () {}
)
``` ```
This can help you when wanting to create a group of rules under the same This can help you when wanting to create a group of rules under the same
@ -168,7 +180,7 @@ This can help you when wanting to create a group of rules under the same
Your rule function will receive three arguments: Your rule function will receive three arguments:
```js ```js
(tree, file, options) => {} function rule(tree, file, options) {}
``` ```
* `tree` (*required*): [mdast][] * `tree` (*required*): [mdast][]
@ -187,38 +199,36 @@ recursively inspect all the image nodes, and
nodes that we have generated ourselves and do not belong to the `doc.md`. nodes that we have generated ourselves and do not belong to the `doc.md`.
```js ```js
import {lintRule} from 'unified-lint-rule' /**
import {visit} from 'unist-visit-util' * @typedef {import('mdast').Root} Root
import {generated} from 'unist-util-generated' */
function isValidNode(node) { import {lintRule} from 'unified-lint-rule'
// Here we check whether the given node violates our rule. import {visit} from 'unist-util-visit'
// Implementation details are not relevant to the scope of this example.
// This is an overly simplified solution for demonstration purposes
if (node.url && typeof node.url === 'string') {
return !node.url.endsWith('.gif')
}
}
const remarkLintNoGifAllowed = lintRule( const remarkLintNoGifAllowed = lintRule(
'remark-lint:no-gif-allowed', 'remark-lint:no-gif-allowed',
(tree, file, options) => { /**
visit(tree, 'image', (node) => { * @param {Root} tree
if (!generated(node)) { * Tree.
// This is an extremely simplified example of how to structure * @returns {undefined}
// the logic to check whether a node violates your rule. * Nothing.
// You have complete freedom over how to visit/inspect the tree, */
// and on how to implement the validation logic for your node. function (tree, file, options) {
const isValid = isValidNode(node) visit(tree, 'image', function (node) {
// This is an extremely simplified example of how to structure
// the logic to check whether a node violates your rule.
// You have complete freedom over how to visit/inspect the tree,
// and on how to implement the validation logic for your node.
const isValid = !node.url.endsWith('.gif')
if (!isValid) { if (!isValid) {
// Remember to provide the node as second argument to the message, // Remember to provide the node as second argument to the message,
// in order to obtain the position and column where the violation occurred. // in order to obtain the position and column where the violation occurred.
file.message( file.message(
'Invalid image file extensions. Please do not use gifs', 'Invalid image file extensions. Please do not use gifs',
node node
) )
}
} }
}) })
} }
@ -236,13 +246,14 @@ You can do that by importing your rule and adding it in `plugins` array:
```js ```js
// .remarkrc.js // .remarkrc.js
/**
* @typedef {import('unified').Preset} Preset
*/
import remarkLintNoGifAllowed from './rules/no-gif-allowed.js' import remarkLintNoGifAllowed from './rules/no-gif-allowed.js'
const plugins = { /** @type {Preset} */
plugins: [remarkLintNoGifAllowed] const preset = {plugins: [remarkLintNoGifAllowed]}
}
const preset = {plugins}
export default preset export default preset
``` ```

View File

@ -116,7 +116,11 @@
"c8": "^8.0.0", "c8": "^8.0.0",
"comment-parser": "^1.0.0", "comment-parser": "^1.0.0",
"github-slugger": "^2.0.0", "github-slugger": "^2.0.0",
"mdast-util-from-markdown": "^2.0.0",
"mdast-util-gfm": "^3.0.0",
"mdast-util-to-markdown": "^2.0.0",
"mdast-zone": "^6.0.0", "mdast-zone": "^6.0.0",
"micromark-extension-gfm": "^3.0.0",
"parse-author": "^2.0.0", "parse-author": "^2.0.0",
"prettier": "^3.0.0", "prettier": "^3.0.0",
"remark": "^15.0.0", "remark": "^15.0.0",
@ -127,16 +131,16 @@
"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",
"to-vfile": "^8.0.0",
"type-coverage": "^2.0.0", "type-coverage": "^2.0.0",
"type-fest": "^4.0.0", "type-fest": "^4.0.0",
"typescript": "^5.0.0", "typescript": "^5.0.0",
"unist-builder": "^4.0.0", "unist-builder": "^4.0.0",
"unist-util-remove-position": "^5.0.0", "unist-util-remove-position": "^5.0.0",
"vfile": "^6.0.0",
"xo": "^0.56.0" "xo": "^0.56.0"
}, },
"scripts": { "scripts": {
"generate": "node --conditions development script/build-presets.js && node --conditions development script/build-rules.js", "generate": "node --conditions development script/build-plugins.js && node --conditions development script/build-presets.js",
"build": "tsc --build --clean && tsc --build && type-coverage", "build": "tsc --build --clean && tsc --build && type-coverage",
"format": "remark . --frail --output --quiet && prettier . --log-level warn --write && xo --fix", "format": "remark . --frail --output --quiet && prettier . --log-level warn --write && xo --fix",
"test": "npm run build && npm run generate && npm run format && npm run test-coverage", "test": "npm run build && npm run generate && npm run format && npm run test-coverage",
@ -174,8 +178,8 @@
"remark-lint-list-item-indent", "remark-lint-list-item-indent",
"space" "space"
], ],
"./script/plugin/list-of-presets.js", "./script/plugin/list-of-plugins.js",
"./script/plugin/list-of-rules.js" "./script/plugin/list-of-presets.js"
] ]
}, },
"typeCoverage": { "typeCoverage": {
@ -193,7 +197,8 @@
], ],
"rules": { "rules": {
"max-depth": "off", "max-depth": "off",
"no-await-in-loop": "off" "no-await-in-loop": "off",
"unicorn/no-array-callback-reference": "off"
} }
} }
], ],

View File

@ -77,22 +77,20 @@ In browsers with [`esm.sh`][esmsh]:
On the API: On the API:
```js ```js
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
import {remark} from 'remark' import {remark} from 'remark'
import remarkLint from 'remark-lint' import remarkLint from 'remark-lint'
import remarkLintBlockquoteIndentation from 'remark-lint-blockquote-indentation' import remarkLintBlockquoteIndentation from 'remark-lint-blockquote-indentation'
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
main() const file = await read('example.md')
async function main() { await remark()
const file = await remark() .use(remarkLint)
.use(remarkLint) .use(remarkLintBlockquoteIndentation)
.use(remarkLintBlockquoteIndentation) .process(file)
.process(await read('example.md'))
console.error(reporter(file)) console.error(reporter(file))
}
``` ```
On the CLI: On the CLI:

View File

@ -76,22 +76,20 @@ In browsers with [`esm.sh`][esmsh]:
On the API: On the API:
```js ```js
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
import {remark} from 'remark' import {remark} from 'remark'
import remarkLint from 'remark-lint' import remarkLint from 'remark-lint'
import remarkLintCheckboxCharacterStyle from 'remark-lint-checkbox-character-style' import remarkLintCheckboxCharacterStyle from 'remark-lint-checkbox-character-style'
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
main() const file = await read('example.md')
async function main() { await remark()
const file = await remark() .use(remarkLint)
.use(remarkLint) .use(remarkLintCheckboxCharacterStyle)
.use(remarkLintCheckboxCharacterStyle) .process(file)
.process(await read('example.md'))
console.error(reporter(file)) console.error(reporter(file))
}
``` ```
On the CLI: On the CLI:

View File

@ -72,22 +72,20 @@ In browsers with [`esm.sh`][esmsh]:
On the API: On the API:
```js ```js
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
import {remark} from 'remark' import {remark} from 'remark'
import remarkLint from 'remark-lint' import remarkLint from 'remark-lint'
import remarkLintCheckboxContentIndent from 'remark-lint-checkbox-content-indent' import remarkLintCheckboxContentIndent from 'remark-lint-checkbox-content-indent'
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
main() const file = await read('example.md')
async function main() { await remark()
const file = await remark() .use(remarkLint)
.use(remarkLint) .use(remarkLintCheckboxContentIndent)
.use(remarkLintCheckboxContentIndent) .process(file)
.process(await read('example.md'))
console.error(reporter(file)) console.error(reporter(file))
}
``` ```
On the CLI: On the CLI:

View File

@ -75,22 +75,20 @@ In browsers with [`esm.sh`][esmsh]:
On the API: On the API:
```js ```js
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
import {remark} from 'remark' import {remark} from 'remark'
import remarkLint from 'remark-lint' import remarkLint from 'remark-lint'
import remarkLintCodeBlockStyle from 'remark-lint-code-block-style' import remarkLintCodeBlockStyle from 'remark-lint-code-block-style'
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
main() const file = await read('example.md')
async function main() { await remark()
const file = await remark() .use(remarkLint)
.use(remarkLint) .use(remarkLintCodeBlockStyle)
.use(remarkLintCodeBlockStyle) .process(file)
.process(await read('example.md'))
console.error(reporter(file)) console.error(reporter(file))
}
``` ```
On the CLI: On the CLI:

View File

@ -74,22 +74,20 @@ In browsers with [`esm.sh`][esmsh]:
On the API: On the API:
```js ```js
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
import {remark} from 'remark' import {remark} from 'remark'
import remarkLint from 'remark-lint' import remarkLint from 'remark-lint'
import remarkLintDefinitionCase from 'remark-lint-definition-case' import remarkLintDefinitionCase from 'remark-lint-definition-case'
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
main() const file = await read('example.md')
async function main() { await remark()
const file = await remark() .use(remarkLint)
.use(remarkLint) .use(remarkLintDefinitionCase)
.use(remarkLintDefinitionCase) .process(file)
.process(await read('example.md'))
console.error(reporter(file)) console.error(reporter(file))
}
``` ```
On the CLI: On the CLI:

View File

@ -75,22 +75,20 @@ In browsers with [`esm.sh`][esmsh]:
On the API: On the API:
```js ```js
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
import {remark} from 'remark' import {remark} from 'remark'
import remarkLint from 'remark-lint' import remarkLint from 'remark-lint'
import remarkLintDefinitionSpacing from 'remark-lint-definition-spacing' import remarkLintDefinitionSpacing from 'remark-lint-definition-spacing'
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
main() const file = await read('example.md')
async function main() { await remark()
const file = await remark() .use(remarkLint)
.use(remarkLint) .use(remarkLintDefinitionSpacing)
.use(remarkLintDefinitionSpacing) .process(file)
.process(await read('example.md'))
console.error(reporter(file)) console.error(reporter(file))
}
``` ```
On the CLI: On the CLI:

View File

@ -75,22 +75,20 @@ In browsers with [`esm.sh`][esmsh]:
On the API: On the API:
```js ```js
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
import {remark} from 'remark' import {remark} from 'remark'
import remarkLint from 'remark-lint' import remarkLint from 'remark-lint'
import remarkLintEmphasisMarker from 'remark-lint-emphasis-marker' import remarkLintEmphasisMarker from 'remark-lint-emphasis-marker'
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
main() const file = await read('example.md')
async function main() { await remark()
const file = await remark() .use(remarkLint)
.use(remarkLint) .use(remarkLintEmphasisMarker)
.use(remarkLintEmphasisMarker) .process(file)
.process(await read('example.md'))
console.error(reporter(file)) console.error(reporter(file))
}
``` ```
On the CLI: On the CLI:

View File

@ -74,22 +74,20 @@ In browsers with [`esm.sh`][esmsh]:
On the API: On the API:
```js ```js
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
import {remark} from 'remark' import {remark} from 'remark'
import remarkLint from 'remark-lint' import remarkLint from 'remark-lint'
import remarkLintFencedCodeFlag from 'remark-lint-fenced-code-flag' import remarkLintFencedCodeFlag from 'remark-lint-fenced-code-flag'
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
main() const file = await read('example.md')
async function main() { await remark()
const file = await remark() .use(remarkLint)
.use(remarkLint) .use(remarkLintFencedCodeFlag)
.use(remarkLintFencedCodeFlag) .process(file)
.process(await read('example.md'))
console.error(reporter(file)) console.error(reporter(file))
}
``` ```
On the CLI: On the CLI:

View File

@ -75,22 +75,20 @@ In browsers with [`esm.sh`][esmsh]:
On the API: On the API:
```js ```js
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
import {remark} from 'remark' import {remark} from 'remark'
import remarkLint from 'remark-lint' import remarkLint from 'remark-lint'
import remarkLintFencedCodeMarker from 'remark-lint-fenced-code-marker' import remarkLintFencedCodeMarker from 'remark-lint-fenced-code-marker'
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
main() const file = await read('example.md')
async function main() { await remark()
const file = await remark() .use(remarkLint)
.use(remarkLint) .use(remarkLintFencedCodeMarker)
.use(remarkLintFencedCodeMarker) .process(file)
.process(await read('example.md'))
console.error(reporter(file)) console.error(reporter(file))
}
``` ```
On the CLI: On the CLI:
@ -162,46 +160,6 @@ Indented code blocks are not affected by this rule:
No messages. No messages.
##### `not-ok-consistent-tick.md`
###### In
````markdown
```alpha
bravo()
```
~~~
charlie()
~~~
````
###### Out
```text
5:1-7:4: Fenced code should use `` ` `` as a marker
```
##### `not-ok-consistent-tilde.md`
###### In
````markdown
~~~alpha
bravo()
~~~
```
charlie()
```
````
###### Out
```text
5:1-7:4: Fenced code should use `~` as a marker
```
##### `ok.md` ##### `ok.md`
When configured with ``'`'``. When configured with ``'`'``.
@ -242,6 +200,46 @@ charlie()
No messages. No messages.
##### `not-ok-consistent-tick.md`
###### In
````markdown
```alpha
bravo()
```
~~~
charlie()
~~~
````
###### Out
```text
5:1-7:4: Fenced code should use `` ` `` as a marker
```
##### `not-ok-consistent-tilde.md`
###### In
````markdown
~~~alpha
bravo()
~~~
```
charlie()
```
````
###### Out
```text
5:1-7:4: Fenced code should use `~` as a marker
```
##### `not-ok-incorrect.md` ##### `not-ok-incorrect.md`
When configured with `'💩'`. When configured with `'💩'`.

View File

@ -73,22 +73,20 @@ In browsers with [`esm.sh`][esmsh]:
On the API: On the API:
```js ```js
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
import {remark} from 'remark' import {remark} from 'remark'
import remarkLint from 'remark-lint' import remarkLint from 'remark-lint'
import remarkLintFileExtension from 'remark-lint-file-extension' import remarkLintFileExtension from 'remark-lint-file-extension'
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
main() const file = await read('example.md')
async function main() { await remark()
const file = await remark() .use(remarkLint)
.use(remarkLint) .use(remarkLintFileExtension)
.use(remarkLintFileExtension) .process(file)
.process(await read('example.md'))
console.error(reporter(file)) console.error(reporter(file))
}
``` ```
On the CLI: On the CLI:

View File

@ -75,22 +75,20 @@ In browsers with [`esm.sh`][esmsh]:
On the API: On the API:
```js ```js
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
import {remark} from 'remark' import {remark} from 'remark'
import remarkLint from 'remark-lint' import remarkLint from 'remark-lint'
import remarkLintFinalDefinition from 'remark-lint-final-definition' import remarkLintFinalDefinition from 'remark-lint-final-definition'
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
main() const file = await read('example.md')
async function main() { await remark()
const file = await remark() .use(remarkLint)
.use(remarkLint) .use(remarkLintFinalDefinition)
.use(remarkLintFinalDefinition) .process(file)
.process(await read('example.md'))
console.error(reporter(file)) console.error(reporter(file))
}
``` ```
On the CLI: On the CLI:

View File

@ -74,22 +74,20 @@ In browsers with [`esm.sh`][esmsh]:
On the API: On the API:
```js ```js
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
import {remark} from 'remark' import {remark} from 'remark'
import remarkLint from 'remark-lint' import remarkLint from 'remark-lint'
import remarkLintFinalNewline from 'remark-lint-final-newline' import remarkLintFinalNewline from 'remark-lint-final-newline'
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
main() const file = await read('example.md')
async function main() { await remark()
const file = await remark() .use(remarkLint)
.use(remarkLint) .use(remarkLintFinalNewline)
.use(remarkLintFinalNewline) .process(file)
.process(await read('example.md'))
console.error(reporter(file)) console.error(reporter(file))
}
``` ```
On the CLI: On the CLI:

View File

@ -69,22 +69,20 @@ In browsers with [`esm.sh`][esmsh]:
On the API: On the API:
```js ```js
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
import {remark} from 'remark' import {remark} from 'remark'
import remarkLint from 'remark-lint' import remarkLint from 'remark-lint'
import remarkLintFirstHeadingLevel from 'remark-lint-first-heading-level' import remarkLintFirstHeadingLevel from 'remark-lint-first-heading-level'
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
main() const file = await read('example.md')
async function main() { await remark()
const file = await remark() .use(remarkLint)
.use(remarkLint) .use(remarkLintFirstHeadingLevel)
.use(remarkLintFirstHeadingLevel) .process(file)
.process(await read('example.md'))
console.error(reporter(file)) console.error(reporter(file))
}
``` ```
On the CLI: On the CLI:

View File

@ -76,22 +76,20 @@ In browsers with [`esm.sh`][esmsh]:
On the API: On the API:
```js ```js
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
import {remark} from 'remark' import {remark} from 'remark'
import remarkLint from 'remark-lint' import remarkLint from 'remark-lint'
import remarkLintHardBreakSpaces from 'remark-lint-hard-break-spaces' import remarkLintHardBreakSpaces from 'remark-lint-hard-break-spaces'
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
main() const file = await read('example.md')
async function main() { await remark()
const file = await remark() .use(remarkLint)
.use(remarkLint) .use(remarkLintHardBreakSpaces)
.use(remarkLintHardBreakSpaces) .process(file)
.process(await read('example.md'))
console.error(reporter(file)) console.error(reporter(file))
}
``` ```
On the CLI: On the CLI:

View File

@ -75,22 +75,20 @@ In browsers with [`esm.sh`][esmsh]:
On the API: On the API:
```js ```js
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
import {remark} from 'remark' import {remark} from 'remark'
import remarkLint from 'remark-lint' import remarkLint from 'remark-lint'
import remarkLintHeadingIncrement from 'remark-lint-heading-increment' import remarkLintHeadingIncrement from 'remark-lint-heading-increment'
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
main() const file = await read('example.md')
async function main() { await remark()
const file = await remark() .use(remarkLint)
.use(remarkLint) .use(remarkLintHeadingIncrement)
.use(remarkLintHeadingIncrement) .process(file)
.process(await read('example.md'))
console.error(reporter(file)) console.error(reporter(file))
}
``` ```
On the CLI: On the CLI:

View File

@ -75,22 +75,20 @@ In browsers with [`esm.sh`][esmsh]:
On the API: On the API:
```js ```js
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
import {remark} from 'remark' import {remark} from 'remark'
import remarkLint from 'remark-lint' import remarkLint from 'remark-lint'
import remarkLintHeadingStyle from 'remark-lint-heading-style' import remarkLintHeadingStyle from 'remark-lint-heading-style'
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
main() const file = await read('example.md')
async function main() { await remark()
const file = await remark() .use(remarkLint)
.use(remarkLint) .use(remarkLintHeadingStyle)
.use(remarkLintHeadingStyle) .process(file)
.process(await read('example.md'))
console.error(reporter(file)) console.error(reporter(file))
}
``` ```
On the CLI: On the CLI:

View File

@ -70,22 +70,20 @@ In browsers with [`esm.sh`][esmsh]:
On the API: On the API:
```js ```js
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
import {remark} from 'remark' import {remark} from 'remark'
import remarkLint from 'remark-lint' import remarkLint from 'remark-lint'
import remarkLintLinebreakStyle from 'remark-lint-linebreak-style' import remarkLintLinebreakStyle from 'remark-lint-linebreak-style'
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
main() const file = await read('example.md')
async function main() { await remark()
const file = await remark() .use(remarkLint)
.use(remarkLint) .use(remarkLintLinebreakStyle)
.use(remarkLintLinebreakStyle) .process(file)
.process(await read('example.md'))
console.error(reporter(file)) console.error(reporter(file))
}
``` ```
On the CLI: On the CLI:

View File

@ -75,22 +75,20 @@ In browsers with [`esm.sh`][esmsh]:
On the API: On the API:
```js ```js
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
import {remark} from 'remark' import {remark} from 'remark'
import remarkLint from 'remark-lint' import remarkLint from 'remark-lint'
import remarkLintLinkTitleStyle from 'remark-lint-link-title-style' import remarkLintLinkTitleStyle from 'remark-lint-link-title-style'
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
main() const file = await read('example.md')
async function main() { await remark()
const file = await remark() .use(remarkLint)
.use(remarkLint) .use(remarkLintLinkTitleStyle)
.use(remarkLintLinkTitleStyle) .process(file)
.process(await read('example.md'))
console.error(reporter(file)) console.error(reporter(file))
}
``` ```
On the CLI: On the CLI:

View File

@ -74,22 +74,20 @@ In browsers with [`esm.sh`][esmsh]:
On the API: On the API:
```js ```js
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
import {remark} from 'remark' import {remark} from 'remark'
import remarkLint from 'remark-lint' import remarkLint from 'remark-lint'
import remarkLintListItemBulletIndent from 'remark-lint-list-item-bullet-indent' import remarkLintListItemBulletIndent from 'remark-lint-list-item-bullet-indent'
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
main() const file = await read('example.md')
async function main() { await remark()
const file = await remark() .use(remarkLint)
.use(remarkLint) .use(remarkLintListItemBulletIndent)
.use(remarkLintListItemBulletIndent) .process(file)
.process(await read('example.md'))
console.error(reporter(file)) console.error(reporter(file))
}
``` ```
On the CLI: On the CLI:

View File

@ -75,22 +75,20 @@ In browsers with [`esm.sh`][esmsh]:
On the API: On the API:
```js ```js
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
import {remark} from 'remark' import {remark} from 'remark'
import remarkLint from 'remark-lint' import remarkLint from 'remark-lint'
import remarkLintListItemContentIndent from 'remark-lint-list-item-content-indent' import remarkLintListItemContentIndent from 'remark-lint-list-item-content-indent'
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
main() const file = await read('example.md')
async function main() { await remark()
const file = await remark() .use(remarkLint)
.use(remarkLint) .use(remarkLintListItemContentIndent)
.use(remarkLintListItemContentIndent) .process(file)
.process(await read('example.md'))
console.error(reporter(file)) console.error(reporter(file))
}
``` ```
On the CLI: On the CLI:

View File

@ -77,22 +77,20 @@ In browsers with [`esm.sh`][esmsh]:
On the API: On the API:
```js ```js
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
import {remark} from 'remark' import {remark} from 'remark'
import remarkLint from 'remark-lint' import remarkLint from 'remark-lint'
import remarkLintListItemIndent from 'remark-lint-list-item-indent' import remarkLintListItemIndent from 'remark-lint-list-item-indent'
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
main() const file = await read('example.md')
async function main() { await remark()
const file = await remark() .use(remarkLint)
.use(remarkLint) .use(remarkLintListItemIndent)
.use(remarkLintListItemIndent) .process(file)
.process(await read('example.md'))
console.error(reporter(file)) console.error(reporter(file))
}
``` ```
On the CLI: On the CLI:
@ -230,24 +228,6 @@ Paragraph.
No messages. No messages.
##### `not-ok.md`
When configured with `'mixed'`.
###### In
> 👉 **Note**: `·` represents a space.
```markdown
*···List item.
```
###### Out
```text
1:5: Incorrect list-item indent: remove 2 spaces
```
##### `ok.md` ##### `ok.md`
When configured with `'space'`. When configured with `'space'`.
@ -316,6 +296,24 @@ When configured with `'tab-size'`.
##### `not-ok.md` ##### `not-ok.md`
When configured with `'mixed'`.
###### In
> 👉 **Note**: `·` represents a space.
```markdown
*···List item.
```
###### Out
```text
1:5: Incorrect list-item indent: remove 2 spaces
```
##### `not-ok.md`
When configured with `'💩'`. When configured with `'💩'`.
###### Out ###### Out

View File

@ -75,22 +75,20 @@ In browsers with [`esm.sh`][esmsh]:
On the API: On the API:
```js ```js
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
import {remark} from 'remark' import {remark} from 'remark'
import remarkLint from 'remark-lint' import remarkLint from 'remark-lint'
import remarkLintListItemSpacing from 'remark-lint-list-item-spacing' import remarkLintListItemSpacing from 'remark-lint-list-item-spacing'
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
main() const file = await read('example.md')
async function main() { await remark()
const file = await remark() .use(remarkLint)
.use(remarkLint) .use(remarkLintListItemSpacing)
.use(remarkLintListItemSpacing) .process(file)
.process(await read('example.md'))
console.error(reporter(file)) console.error(reporter(file))
}
``` ```
On the CLI: On the CLI:

View File

@ -73,22 +73,20 @@ In browsers with [`esm.sh`][esmsh]:
On the API: On the API:
```js ```js
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
import {remark} from 'remark' import {remark} from 'remark'
import remarkLint from 'remark-lint' import remarkLint from 'remark-lint'
import remarkLintMaximumHeadingLength from 'remark-lint-maximum-heading-length' import remarkLintMaximumHeadingLength from 'remark-lint-maximum-heading-length'
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
main() const file = await read('example.md')
async function main() { await remark()
const file = await remark() .use(remarkLint)
.use(remarkLint) .use(remarkLintMaximumHeadingLength)
.use(remarkLintMaximumHeadingLength) .process(file)
.process(await read('example.md'))
console.error(reporter(file)) console.error(reporter(file))
}
``` ```
On the CLI: On the CLI:

View File

@ -73,22 +73,20 @@ In browsers with [`esm.sh`][esmsh]:
On the API: On the API:
```js ```js
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
import {remark} from 'remark' import {remark} from 'remark'
import remarkLint from 'remark-lint' import remarkLint from 'remark-lint'
import remarkLintMaximumLineLength from 'remark-lint-maximum-line-length' import remarkLintMaximumLineLength from 'remark-lint-maximum-line-length'
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
main() const file = await read('example.md')
async function main() { await remark()
const file = await remark() .use(remarkLint)
.use(remarkLint) .use(remarkLintMaximumLineLength)
.use(remarkLintMaximumLineLength) .process(file)
.process(await read('example.md'))
console.error(reporter(file)) console.error(reporter(file))
}
``` ```
On the CLI: On the CLI:

View File

@ -75,22 +75,20 @@ In browsers with [`esm.sh`][esmsh]:
On the API: On the API:
```js ```js
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
import {remark} from 'remark' import {remark} from 'remark'
import remarkLint from 'remark-lint' import remarkLint from 'remark-lint'
import remarkLintNoBlockquoteWithoutMarker from 'remark-lint-no-blockquote-without-marker' import remarkLintNoBlockquoteWithoutMarker from 'remark-lint-no-blockquote-without-marker'
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
main() const file = await read('example.md')
async function main() { await remark()
const file = await remark() .use(remarkLint)
.use(remarkLint) .use(remarkLintNoBlockquoteWithoutMarker)
.use(remarkLintNoBlockquoteWithoutMarker) .process(file)
.process(await read('example.md'))
console.error(reporter(file)) console.error(reporter(file))
}
``` ```
On the CLI: On the CLI:

View File

@ -76,22 +76,20 @@ In browsers with [`esm.sh`][esmsh]:
On the API: On the API:
```js ```js
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
import {remark} from 'remark' import {remark} from 'remark'
import remarkLint from 'remark-lint' import remarkLint from 'remark-lint'
import remarkLintNoConsecutiveBlankLines from 'remark-lint-no-consecutive-blank-lines' import remarkLintNoConsecutiveBlankLines from 'remark-lint-no-consecutive-blank-lines'
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
main() const file = await read('example.md')
async function main() { await remark()
const file = await remark() .use(remarkLint)
.use(remarkLint) .use(remarkLintNoConsecutiveBlankLines)
.use(remarkLintNoConsecutiveBlankLines) .process(file)
.process(await read('example.md'))
console.error(reporter(file)) console.error(reporter(file))
}
``` ```
On the CLI: On the CLI:

View File

@ -69,22 +69,20 @@ In browsers with [`esm.sh`][esmsh]:
On the API: On the API:
```js ```js
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
import {remark} from 'remark' import {remark} from 'remark'
import remarkLint from 'remark-lint' import remarkLint from 'remark-lint'
import remarkLintNoDuplicateDefinedUrls from 'remark-lint-no-duplicate-defined-urls' import remarkLintNoDuplicateDefinedUrls from 'remark-lint-no-duplicate-defined-urls'
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
main() const file = await read('example.md')
async function main() { await remark()
const file = await remark() .use(remarkLint)
.use(remarkLint) .use(remarkLintNoDuplicateDefinedUrls)
.use(remarkLintNoDuplicateDefinedUrls) .process(file)
.process(await read('example.md'))
console.error(reporter(file)) console.error(reporter(file))
}
``` ```
On the CLI: On the CLI:

View File

@ -73,22 +73,20 @@ In browsers with [`esm.sh`][esmsh]:
On the API: On the API:
```js ```js
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
import {remark} from 'remark' import {remark} from 'remark'
import remarkLint from 'remark-lint' import remarkLint from 'remark-lint'
import remarkLintNoDuplicateDefinitions from 'remark-lint-no-duplicate-definitions' import remarkLintNoDuplicateDefinitions from 'remark-lint-no-duplicate-definitions'
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
main() const file = await read('example.md')
async function main() { await remark()
const file = await remark() .use(remarkLint)
.use(remarkLint) .use(remarkLintNoDuplicateDefinitions)
.use(remarkLintNoDuplicateDefinitions) .process(file)
.process(await read('example.md'))
console.error(reporter(file)) console.error(reporter(file))
}
``` ```
On the CLI: On the CLI:

View File

@ -71,22 +71,20 @@ In browsers with [`esm.sh`][esmsh]:
On the API: On the API:
```js ```js
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
import {remark} from 'remark' import {remark} from 'remark'
import remarkLint from 'remark-lint' import remarkLint from 'remark-lint'
import remarkLintNoDuplicateHeadingsInSection from 'remark-lint-no-duplicate-headings-in-section' import remarkLintNoDuplicateHeadingsInSection from 'remark-lint-no-duplicate-headings-in-section'
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
main() const file = await read('example.md')
async function main() { await remark()
const file = await remark() .use(remarkLint)
.use(remarkLint) .use(remarkLintNoDuplicateHeadingsInSection)
.use(remarkLintNoDuplicateHeadingsInSection) .process(file)
.process(await read('example.md'))
console.error(reporter(file)) console.error(reporter(file))
}
``` ```
On the CLI: On the CLI:

View File

@ -74,22 +74,20 @@ In browsers with [`esm.sh`][esmsh]:
On the API: On the API:
```js ```js
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
import {remark} from 'remark' import {remark} from 'remark'
import remarkLint from 'remark-lint' import remarkLint from 'remark-lint'
import remarkLintNoDuplicateHeadings from 'remark-lint-no-duplicate-headings' import remarkLintNoDuplicateHeadings from 'remark-lint-no-duplicate-headings'
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
main() const file = await read('example.md')
async function main() { await remark()
const file = await remark() .use(remarkLint)
.use(remarkLint) .use(remarkLintNoDuplicateHeadings)
.use(remarkLintNoDuplicateHeadings) .process(file)
.process(await read('example.md'))
console.error(reporter(file)) console.error(reporter(file))
}
``` ```
On the CLI: On the CLI:

View File

@ -75,22 +75,20 @@ In browsers with [`esm.sh`][esmsh]:
On the API: On the API:
```js ```js
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
import {remark} from 'remark' import {remark} from 'remark'
import remarkLint from 'remark-lint' import remarkLint from 'remark-lint'
import remarkLintNoEmphasisAsHeading from 'remark-lint-no-emphasis-as-heading' import remarkLintNoEmphasisAsHeading from 'remark-lint-no-emphasis-as-heading'
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
main() const file = await read('example.md')
async function main() { await remark()
const file = await remark() .use(remarkLint)
.use(remarkLint) .use(remarkLintNoEmphasisAsHeading)
.use(remarkLintNoEmphasisAsHeading) .process(file)
.process(await read('example.md'))
console.error(reporter(file)) console.error(reporter(file))
}
``` ```
On the CLI: On the CLI:

View File

@ -69,22 +69,20 @@ In browsers with [`esm.sh`][esmsh]:
On the API: On the API:
```js ```js
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
import {remark} from 'remark' import {remark} from 'remark'
import remarkLint from 'remark-lint' import remarkLint from 'remark-lint'
import remarkLintNoEmptyUrl from 'remark-lint-no-empty-url' import remarkLintNoEmptyUrl from 'remark-lint-no-empty-url'
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
main() const file = await read('example.md')
async function main() { await remark()
const file = await remark() .use(remarkLint)
.use(remarkLint) .use(remarkLintNoEmptyUrl)
.use(remarkLintNoEmptyUrl) .process(file)
.process(await read('example.md'))
console.error(reporter(file)) console.error(reporter(file))
}
``` ```
On the CLI: On the CLI:

View File

@ -73,22 +73,20 @@ In browsers with [`esm.sh`][esmsh]:
On the API: On the API:
```js ```js
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
import {remark} from 'remark' import {remark} from 'remark'
import remarkLint from 'remark-lint' import remarkLint from 'remark-lint'
import remarkLintNoFileNameArticles from 'remark-lint-no-file-name-articles' import remarkLintNoFileNameArticles from 'remark-lint-no-file-name-articles'
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
main() const file = await read('example.md')
async function main() { await remark()
const file = await remark() .use(remarkLint)
.use(remarkLint) .use(remarkLintNoFileNameArticles)
.use(remarkLintNoFileNameArticles) .process(file)
.process(await read('example.md'))
console.error(reporter(file)) console.error(reporter(file))
}
``` ```
On the CLI: On the CLI:

View File

@ -73,22 +73,20 @@ In browsers with [`esm.sh`][esmsh]:
On the API: On the API:
```js ```js
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
import {remark} from 'remark' import {remark} from 'remark'
import remarkLint from 'remark-lint' import remarkLint from 'remark-lint'
import remarkLintNoFileNameConsecutiveDashes from 'remark-lint-no-file-name-consecutive-dashes' import remarkLintNoFileNameConsecutiveDashes from 'remark-lint-no-file-name-consecutive-dashes'
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
main() const file = await read('example.md')
async function main() { await remark()
const file = await remark() .use(remarkLint)
.use(remarkLint) .use(remarkLintNoFileNameConsecutiveDashes)
.use(remarkLintNoFileNameConsecutiveDashes) .process(file)
.process(await read('example.md'))
console.error(reporter(file)) console.error(reporter(file))
}
``` ```
On the CLI: On the CLI:

View File

@ -72,22 +72,20 @@ In browsers with [`esm.sh`][esmsh]:
On the API: On the API:
```js ```js
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
import {remark} from 'remark' import {remark} from 'remark'
import remarkLint from 'remark-lint' import remarkLint from 'remark-lint'
import remarkLintNoFileNameIrregularCharacters from 'remark-lint-no-file-name-irregular-characters' import remarkLintNoFileNameIrregularCharacters from 'remark-lint-no-file-name-irregular-characters'
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
main() const file = await read('example.md')
async function main() { await remark()
const file = await remark() .use(remarkLint)
.use(remarkLint) .use(remarkLintNoFileNameIrregularCharacters)
.use(remarkLintNoFileNameIrregularCharacters) .process(file)
.process(await read('example.md'))
console.error(reporter(file)) console.error(reporter(file))
}
``` ```
On the CLI: On the CLI:
@ -151,14 +149,6 @@ No messages.
1:1: Do not use `_` in a file name 1:1: Do not use `_` in a file name
``` ```
##### `plug ins.md`
###### Out
```text
1:1: Do not use ` ` in a file name
```
##### `README.md` ##### `README.md`
When configured with `'\\.a-z0-9'`. When configured with `'\\.a-z0-9'`.
@ -169,6 +159,14 @@ When configured with `'\\.a-z0-9'`.
1:1: Do not use `R` in a file name 1:1: Do not use `R` in a file name
``` ```
##### `plug ins.md`
###### Out
```text
1:1: Do not use ` ` in a file name
```
## Compatibility ## Compatibility
Projects maintained by the unified collective are compatible with all maintained Projects maintained by the unified collective are compatible with all maintained

View File

@ -73,22 +73,20 @@ In browsers with [`esm.sh`][esmsh]:
On the API: On the API:
```js ```js
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
import {remark} from 'remark' import {remark} from 'remark'
import remarkLint from 'remark-lint' import remarkLint from 'remark-lint'
import remarkLintNoFileNameMixedCase from 'remark-lint-no-file-name-mixed-case' import remarkLintNoFileNameMixedCase from 'remark-lint-no-file-name-mixed-case'
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
main() const file = await read('example.md')
async function main() { await remark()
const file = await remark() .use(remarkLint)
.use(remarkLint) .use(remarkLintNoFileNameMixedCase)
.use(remarkLintNoFileNameMixedCase) .process(file)
.process(await read('example.md'))
console.error(reporter(file)) console.error(reporter(file))
}
``` ```
On the CLI: On the CLI:

View File

@ -73,22 +73,20 @@ In browsers with [`esm.sh`][esmsh]:
On the API: On the API:
```js ```js
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
import {remark} from 'remark' import {remark} from 'remark'
import remarkLint from 'remark-lint' import remarkLint from 'remark-lint'
import remarkLintNoFileNameOuterDashes from 'remark-lint-no-file-name-outer-dashes' import remarkLintNoFileNameOuterDashes from 'remark-lint-no-file-name-outer-dashes'
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
main() const file = await read('example.md')
async function main() { await remark()
const file = await remark() .use(remarkLint)
.use(remarkLint) .use(remarkLintNoFileNameOuterDashes)
.use(remarkLintNoFileNameOuterDashes) .process(file)
.process(await read('example.md'))
console.error(reporter(file)) console.error(reporter(file))
}
``` ```
On the CLI: On the CLI:

View File

@ -76,22 +76,20 @@ In browsers with [`esm.sh`][esmsh]:
On the API: On the API:
```js ```js
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
import {remark} from 'remark' import {remark} from 'remark'
import remarkLint from 'remark-lint' import remarkLint from 'remark-lint'
import remarkLintNoHeadingContentIndent from 'remark-lint-no-heading-content-indent' import remarkLintNoHeadingContentIndent from 'remark-lint-no-heading-content-indent'
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
main() const file = await read('example.md')
async function main() { await remark()
const file = await remark() .use(remarkLint)
.use(remarkLint) .use(remarkLintNoHeadingContentIndent)
.use(remarkLintNoHeadingContentIndent) .process(file)
.process(await read('example.md'))
console.error(reporter(file)) console.error(reporter(file))
}
``` ```
On the CLI: On the CLI:

View File

@ -70,22 +70,20 @@ In browsers with [`esm.sh`][esmsh]:
On the API: On the API:
```js ```js
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
import {remark} from 'remark' import {remark} from 'remark'
import remarkLint from 'remark-lint' import remarkLint from 'remark-lint'
import remarkLintNoHeadingIndent from 'remark-lint-no-heading-indent' import remarkLintNoHeadingIndent from 'remark-lint-no-heading-indent'
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
main() const file = await read('example.md')
async function main() { await remark()
const file = await remark() .use(remarkLint)
.use(remarkLint) .use(remarkLintNoHeadingIndent)
.use(remarkLintNoHeadingIndent) .process(file)
.process(await read('example.md'))
console.error(reporter(file)) console.error(reporter(file))
}
``` ```
On the CLI: On the CLI:

View File

@ -70,22 +70,20 @@ In browsers with [`esm.sh`][esmsh]:
On the API: On the API:
```js ```js
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
import {remark} from 'remark' import {remark} from 'remark'
import remarkLint from 'remark-lint' import remarkLint from 'remark-lint'
import remarkLintNoHeadingLikeParagraph from 'remark-lint-no-heading-like-paragraph' import remarkLintNoHeadingLikeParagraph from 'remark-lint-no-heading-like-paragraph'
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
main() const file = await read('example.md')
async function main() { await remark()
const file = await remark() .use(remarkLint)
.use(remarkLint) .use(remarkLintNoHeadingLikeParagraph)
.use(remarkLintNoHeadingLikeParagraph) .process(file)
.process(await read('example.md'))
console.error(reporter(file)) console.error(reporter(file))
}
``` ```
On the CLI: On the CLI:

View File

@ -72,22 +72,20 @@ In browsers with [`esm.sh`][esmsh]:
On the API: On the API:
```js ```js
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
import {remark} from 'remark' import {remark} from 'remark'
import remarkLint from 'remark-lint' import remarkLint from 'remark-lint'
import remarkLintNoHeadingPunctuation from 'remark-lint-no-heading-punctuation' import remarkLintNoHeadingPunctuation from 'remark-lint-no-heading-punctuation'
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
main() const file = await read('example.md')
async function main() { await remark()
const file = await remark() .use(remarkLint)
.use(remarkLint) .use(remarkLintNoHeadingPunctuation)
.use(remarkLintNoHeadingPunctuation) .process(file)
.process(await read('example.md'))
console.error(reporter(file)) console.error(reporter(file))
}
``` ```
On the CLI: On the CLI:
@ -143,6 +141,20 @@ The following options (default: `'\\.,;:!?'`) are accepted:
No messages. No messages.
##### `ok.md`
When configured with `',;:!?'`.
###### In
```markdown
# Hello…
```
###### Out
No messages.
##### `not-ok.md` ##### `not-ok.md`
###### In ###### In
@ -169,20 +181,6 @@ No messages.
9:1-9:9: Dont add a trailing `;` to headings 9:1-9:9: Dont add a trailing `;` to headings
``` ```
##### `ok.md`
When configured with `',;:!?'`.
###### In
```markdown
# Hello…
```
###### Out
No messages.
## Compatibility ## Compatibility
Projects maintained by the unified collective are compatible with all maintained Projects maintained by the unified collective are compatible with all maintained

View File

@ -68,22 +68,20 @@ In browsers with [`esm.sh`][esmsh]:
On the API: On the API:
```js ```js
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
import {remark} from 'remark' import {remark} from 'remark'
import remarkLint from 'remark-lint' import remarkLint from 'remark-lint'
import remarkLintNoHtml from 'remark-lint-no-html' import remarkLintNoHtml from 'remark-lint-no-html'
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
main() const file = await read('example.md')
async function main() { await remark()
const file = await remark() .use(remarkLint)
.use(remarkLint) .use(remarkLintNoHtml)
.use(remarkLintNoHtml) .process(file)
.process(await read('example.md'))
console.error(reporter(file)) console.error(reporter(file))
}
``` ```
On the CLI: On the CLI:

View File

@ -76,22 +76,20 @@ In browsers with [`esm.sh`][esmsh]:
On the API: On the API:
```js ```js
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
import {remark} from 'remark' import {remark} from 'remark'
import remarkLint from 'remark-lint' import remarkLint from 'remark-lint'
import remarkLintNoInlinePadding from 'remark-lint-no-inline-padding' import remarkLintNoInlinePadding from 'remark-lint-no-inline-padding'
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
main() const file = await read('example.md')
async function main() { await remark()
const file = await remark() .use(remarkLint)
.use(remarkLint) .use(remarkLintNoInlinePadding)
.use(remarkLintNoInlinePadding) .process(file)
.process(await read('example.md'))
console.error(reporter(file)) console.error(reporter(file))
}
``` ```
On the CLI: On the CLI:

View File

@ -75,22 +75,20 @@ In browsers with [`esm.sh`][esmsh]:
On the API: On the API:
```js ```js
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
import {remark} from 'remark' import {remark} from 'remark'
import remarkLint from 'remark-lint' import remarkLint from 'remark-lint'
import remarkLintNoLiteralUrls from 'remark-lint-no-literal-urls' import remarkLintNoLiteralUrls from 'remark-lint-no-literal-urls'
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
main() const file = await read('example.md')
async function main() { await remark()
const file = await remark() .use(remarkLint)
.use(remarkLint) .use(remarkLintNoLiteralUrls)
.use(remarkLintNoLiteralUrls) .process(file)
.process(await read('example.md'))
console.error(reporter(file)) console.error(reporter(file))
}
``` ```
On the CLI: On the CLI:

View File

@ -70,22 +70,20 @@ In browsers with [`esm.sh`][esmsh]:
On the API: On the API:
```js ```js
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
import {remark} from 'remark' import {remark} from 'remark'
import remarkLint from 'remark-lint' import remarkLint from 'remark-lint'
import remarkLintNoMissingBlankLines from 'remark-lint-no-missing-blank-lines' import remarkLintNoMissingBlankLines from 'remark-lint-no-missing-blank-lines'
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
main() const file = await read('example.md')
async function main() { await remark()
const file = await remark() .use(remarkLint)
.use(remarkLint) .use(remarkLintNoMissingBlankLines)
.use(remarkLintNoMissingBlankLines) .process(file)
.process(await read('example.md'))
console.error(reporter(file)) console.error(reporter(file))
}
``` ```
On the CLI: On the CLI:

View File

@ -74,22 +74,20 @@ In browsers with [`esm.sh`][esmsh]:
On the API: On the API:
```js ```js
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
import {remark} from 'remark' import {remark} from 'remark'
import remarkLint from 'remark-lint' import remarkLint from 'remark-lint'
import remarkLintNoMultipleToplevelHeadings from 'remark-lint-no-multiple-toplevel-headings' import remarkLintNoMultipleToplevelHeadings from 'remark-lint-no-multiple-toplevel-headings'
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
main() const file = await read('example.md')
async function main() { await remark()
const file = await remark() .use(remarkLint)
.use(remarkLint) .use(remarkLintNoMultipleToplevelHeadings)
.use(remarkLintNoMultipleToplevelHeadings) .process(file)
.process(await read('example.md'))
console.error(reporter(file)) console.error(reporter(file))
}
``` ```
On the CLI: On the CLI:

View File

@ -106,7 +106,6 @@ const remarkLintNoParagraphContentIndent = lintRule(
const value = String(file) const value = String(file)
const loc = location(value) const loc = location(value)
// eslint-disable-next-line complexity
visit(tree, 'paragraph', (node, _, parent) => { visit(tree, 'paragraph', (node, _, parent) => {
const end = pointEnd(node)?.line const end = pointEnd(node)?.line
let line = pointStart(node)?.line let line = pointStart(node)?.line

View File

@ -53,7 +53,8 @@
"xo": { "xo": {
"prettier": true, "prettier": true,
"rules": { "rules": {
"capitalized-comments": "off" "capitalized-comments": "off",
"complexity": "off"
} }
} }
} }

View File

@ -70,22 +70,20 @@ In browsers with [`esm.sh`][esmsh]:
On the API: On the API:
```js ```js
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
import {remark} from 'remark' import {remark} from 'remark'
import remarkLint from 'remark-lint' import remarkLint from 'remark-lint'
import remarkLintNoParagraphContentIndent from 'remark-lint-no-paragraph-content-indent' import remarkLintNoParagraphContentIndent from 'remark-lint-no-paragraph-content-indent'
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
main() const file = await read('example.md')
async function main() { await remark()
const file = await remark() .use(remarkLint)
.use(remarkLint) .use(remarkLintNoParagraphContentIndent)
.use(remarkLintNoParagraphContentIndent) .process(file)
.process(await read('example.md'))
console.error(reporter(file)) console.error(reporter(file))
}
``` ```
On the CLI: On the CLI:

View File

@ -70,22 +70,20 @@ In browsers with [`esm.sh`][esmsh]:
On the API: On the API:
```js ```js
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
import {remark} from 'remark' import {remark} from 'remark'
import remarkLint from 'remark-lint' import remarkLint from 'remark-lint'
import remarkLintNoReferenceLikeUrl from 'remark-lint-no-reference-like-url' import remarkLintNoReferenceLikeUrl from 'remark-lint-no-reference-like-url'
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
main() const file = await read('example.md')
async function main() { await remark()
const file = await remark() .use(remarkLint)
.use(remarkLint) .use(remarkLintNoReferenceLikeUrl)
.use(remarkLintNoReferenceLikeUrl) .process(file)
.process(await read('example.md'))
console.error(reporter(file)) console.error(reporter(file))
}
``` ```
On the CLI: On the CLI:

View File

@ -74,22 +74,20 @@ In browsers with [`esm.sh`][esmsh]:
On the API: On the API:
```js ```js
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
import {remark} from 'remark' import {remark} from 'remark'
import remarkLint from 'remark-lint' import remarkLint from 'remark-lint'
import remarkLintNoShellDollars from 'remark-lint-no-shell-dollars' import remarkLintNoShellDollars from 'remark-lint-no-shell-dollars'
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
main() const file = await read('example.md')
async function main() { await remark()
const file = await remark() .use(remarkLint)
.use(remarkLint) .use(remarkLintNoShellDollars)
.use(remarkLintNoShellDollars) .process(file)
.process(await read('example.md'))
console.error(reporter(file)) console.error(reporter(file))
}
``` ```
On the CLI: On the CLI:

View File

@ -75,22 +75,20 @@ In browsers with [`esm.sh`][esmsh]:
On the API: On the API:
```js ```js
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
import {remark} from 'remark' import {remark} from 'remark'
import remarkLint from 'remark-lint' import remarkLint from 'remark-lint'
import remarkLintNoShortcutReferenceImage from 'remark-lint-no-shortcut-reference-image' import remarkLintNoShortcutReferenceImage from 'remark-lint-no-shortcut-reference-image'
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
main() const file = await read('example.md')
async function main() { await remark()
const file = await remark() .use(remarkLint)
.use(remarkLint) .use(remarkLintNoShortcutReferenceImage)
.use(remarkLintNoShortcutReferenceImage) .process(file)
.process(await read('example.md'))
console.error(reporter(file)) console.error(reporter(file))
}
``` ```
On the CLI: On the CLI:

View File

@ -75,22 +75,20 @@ In browsers with [`esm.sh`][esmsh]:
On the API: On the API:
```js ```js
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
import {remark} from 'remark' import {remark} from 'remark'
import remarkLint from 'remark-lint' import remarkLint from 'remark-lint'
import remarkLintNoShortcutReferenceLink from 'remark-lint-no-shortcut-reference-link' import remarkLintNoShortcutReferenceLink from 'remark-lint-no-shortcut-reference-link'
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
main() const file = await read('example.md')
async function main() { await remark()
const file = await remark() .use(remarkLint)
.use(remarkLint) .use(remarkLintNoShortcutReferenceLink)
.use(remarkLintNoShortcutReferenceLink) .process(file)
.process(await read('example.md'))
console.error(reporter(file)) console.error(reporter(file))
}
``` ```
On the CLI: On the CLI:

View File

@ -76,22 +76,20 @@ In browsers with [`esm.sh`][esmsh]:
On the API: On the API:
```js ```js
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
import {remark} from 'remark' import {remark} from 'remark'
import remarkLint from 'remark-lint' import remarkLint from 'remark-lint'
import remarkLintNoTableIndentation from 'remark-lint-no-table-indentation' import remarkLintNoTableIndentation from 'remark-lint-no-table-indentation'
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
main() const file = await read('example.md')
async function main() { await remark()
const file = await remark() .use(remarkLint)
.use(remarkLint) .use(remarkLintNoTableIndentation)
.use(remarkLintNoTableIndentation) .process(file)
.process(await read('example.md'))
console.error(reporter(file)) console.error(reporter(file))
}
``` ```
On the CLI: On the CLI:

View File

@ -70,22 +70,20 @@ In browsers with [`esm.sh`][esmsh]:
On the API: On the API:
```js ```js
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
import {remark} from 'remark' import {remark} from 'remark'
import remarkLint from 'remark-lint' import remarkLint from 'remark-lint'
import remarkLintNoTabs from 'remark-lint-no-tabs' import remarkLintNoTabs from 'remark-lint-no-tabs'
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
main() const file = await read('example.md')
async function main() { await remark()
const file = await remark() .use(remarkLint)
.use(remarkLint) .use(remarkLintNoTabs)
.use(remarkLintNoTabs) .process(file)
.process(await read('example.md'))
console.error(reporter(file)) console.error(reporter(file))
}
``` ```
On the CLI: On the CLI:

View File

@ -213,7 +213,6 @@ const remarkLintNoUndefinedReferences = lintRule(
/** @type {Array<Range>} */ /** @type {Array<Range>} */
let ranges = [] let ranges = []
// eslint-disable-next-line complexity
visit(node, (child) => { visit(node, (child) => {
// Ignore the node itself. // Ignore the node itself.
if (child === node) return if (child === node) return
@ -292,12 +291,10 @@ const remarkLintNoUndefinedReferences = lintRule(
let range = ranges.pop() let range = ranges.pop()
// Range should always exist. // Range should always exist.
// eslint-disable-next-line max-depth
if (range) { if (range) {
range.push(lines[lineIndex][0] + index) range.push(lines[lineIndex][0] + index)
// This is the end of a reference already. // This is the end of a reference already.
// eslint-disable-next-line max-depth
if (range.length === 4) { if (range.length === 4) {
handleRange(range) handleRange(range)
range = [] range = []
@ -314,7 +311,6 @@ const remarkLintNoUndefinedReferences = lintRule(
const range = ranges.pop() const range = ranges.pop()
// Range should always exist. // Range should always exist.
// eslint-disable-next-line max-depth
if (range) { if (range) {
range.push(lines[lineIndex][0] + index) range.push(lines[lineIndex][0] + index)
handleRange(range) handleRange(range)

View File

@ -55,6 +55,8 @@
"prettier": true, "prettier": true,
"rules": { "rules": {
"capitalized-comments": "off", "capitalized-comments": "off",
"complexity": "off",
"max-depth": "off",
"unicorn/prefer-at": "off", "unicorn/prefer-at": "off",
"unicorn/prefer-code-point": "off", "unicorn/prefer-code-point": "off",
"unicorn/prefer-string-replace-all": "off", "unicorn/prefer-string-replace-all": "off",

View File

@ -73,22 +73,20 @@ In browsers with [`esm.sh`][esmsh]:
On the API: On the API:
```js ```js
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
import {remark} from 'remark' import {remark} from 'remark'
import remarkLint from 'remark-lint' import remarkLint from 'remark-lint'
import remarkLintNoUndefinedReferences from 'remark-lint-no-undefined-references' import remarkLintNoUndefinedReferences from 'remark-lint-no-undefined-references'
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
main() const file = await read('example.md')
async function main() { await remark()
const file = await remark() .use(remarkLint)
.use(remarkLint) .use(remarkLintNoUndefinedReferences)
.use(remarkLintNoUndefinedReferences) .process(file)
.process(await read('example.md'))
console.error(reporter(file)) console.error(reporter(file))
}
``` ```
On the CLI: On the CLI:
@ -178,6 +176,38 @@ Just two braces cant link: [].
No messages. No messages.
##### `ok-allow.md`
When configured with `{ allow: [ '...', '…' ] }`.
###### In
```markdown
> Eliding a portion of a quoted passage […] is acceptable.
```
###### Out
No messages.
##### `ok-allow.md`
When configured with `{ allow: [ 'a', { source: '^b\\.' } ] }`.
###### In
```markdown
[foo][b.c]
[bar][a]
Matching is case-insensitive: [bar][B.C]
```
###### Out
No messages.
##### `not-ok.md` ##### `not-ok.md`
###### In ###### In
@ -216,38 +246,6 @@ Multiple pairs: [a][b][c].
17:23-17:26: Found reference to undefined definition 17:23-17:26: Found reference to undefined definition
``` ```
##### `ok-allow.md`
When configured with `{ allow: [ '...', '…' ] }`.
###### In
```markdown
> Eliding a portion of a quoted passage […] is acceptable.
```
###### Out
No messages.
##### `ok-allow.md`
When configured with `{ allow: [ 'a', { source: '^b\\.' } ] }`.
###### In
```markdown
[foo][b.c]
[bar][a]
Matching is case-insensitive: [bar][B.C]
```
###### Out
No messages.
##### `not-ok.md` ##### `not-ok.md`
When configured with `{ allow: [ 'a', { source: '^b\\.' } ] }`. When configured with `{ allow: [ 'a', { source: '^b\\.' } ] }`.

View File

@ -71,22 +71,20 @@ In browsers with [`esm.sh`][esmsh]:
On the API: On the API:
```js ```js
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
import {remark} from 'remark' import {remark} from 'remark'
import remarkLint from 'remark-lint' import remarkLint from 'remark-lint'
import remarkLintNoUnneededFullReferenceImage from 'remark-lint-no-unneeded-full-reference-image' import remarkLintNoUnneededFullReferenceImage from 'remark-lint-no-unneeded-full-reference-image'
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
main() const file = await read('example.md')
async function main() { await remark()
const file = await remark() .use(remarkLint)
.use(remarkLint) .use(remarkLintNoUnneededFullReferenceImage)
.use(remarkLintNoUnneededFullReferenceImage) .process(file)
.process(await read('example.md'))
console.error(reporter(file)) console.error(reporter(file))
}
``` ```
On the CLI: On the CLI:

View File

@ -71,22 +71,20 @@ In browsers with [`esm.sh`][esmsh]:
On the API: On the API:
```js ```js
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
import {remark} from 'remark' import {remark} from 'remark'
import remarkLint from 'remark-lint' import remarkLint from 'remark-lint'
import remarkLintNoUnneededFullReferenceLink from 'remark-lint-no-unneeded-full-reference-link' import remarkLintNoUnneededFullReferenceLink from 'remark-lint-no-unneeded-full-reference-link'
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
main() const file = await read('example.md')
async function main() { await remark()
const file = await remark() .use(remarkLint)
.use(remarkLint) .use(remarkLintNoUnneededFullReferenceLink)
.use(remarkLintNoUnneededFullReferenceLink) .process(file)
.process(await read('example.md'))
console.error(reporter(file)) console.error(reporter(file))
}
``` ```
On the CLI: On the CLI:

View File

@ -73,22 +73,20 @@ In browsers with [`esm.sh`][esmsh]:
On the API: On the API:
```js ```js
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
import {remark} from 'remark' import {remark} from 'remark'
import remarkLint from 'remark-lint' import remarkLint from 'remark-lint'
import remarkLintNoUnusedDefinitions from 'remark-lint-no-unused-definitions' import remarkLintNoUnusedDefinitions from 'remark-lint-no-unused-definitions'
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
main() const file = await read('example.md')
async function main() { await remark()
const file = await remark() .use(remarkLint)
.use(remarkLint) .use(remarkLintNoUnusedDefinitions)
.use(remarkLintNoUnusedDefinitions) .process(file)
.process(await read('example.md'))
console.error(reporter(file)) console.error(reporter(file))
}
``` ```
On the CLI: On the CLI:

View File

@ -76,22 +76,20 @@ In browsers with [`esm.sh`][esmsh]:
On the API: On the API:
```js ```js
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
import {remark} from 'remark' import {remark} from 'remark'
import remarkLint from 'remark-lint' import remarkLint from 'remark-lint'
import remarkLintOrderedListMarkerStyle from 'remark-lint-ordered-list-marker-style' import remarkLintOrderedListMarkerStyle from 'remark-lint-ordered-list-marker-style'
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
main() const file = await read('example.md')
async function main() { await remark()
const file = await remark() .use(remarkLint)
.use(remarkLint) .use(remarkLintOrderedListMarkerStyle)
.use(remarkLintOrderedListMarkerStyle) .process(file)
.process(await read('example.md'))
console.error(reporter(file)) console.error(reporter(file))
}
``` ```
On the CLI: On the CLI:
@ -170,22 +168,6 @@ Unordered lists are not affected by this rule.
No messages. No messages.
##### `not-ok.md`
###### In
```markdown
1. Foo
2) Bar
```
###### Out
```text
3:1-3:8: Marker style should be `.`
```
##### `ok.md` ##### `ok.md`
When configured with `'.'`. When configured with `'.'`.
@ -220,6 +202,22 @@ No messages.
##### `not-ok.md` ##### `not-ok.md`
###### In
```markdown
1. Foo
2) Bar
```
###### Out
```text
3:1-3:8: Marker style should be `.`
```
##### `not-ok.md`
When configured with `'💩'`. When configured with `'💩'`.
###### Out ###### Out

View File

@ -74,22 +74,20 @@ In browsers with [`esm.sh`][esmsh]:
On the API: On the API:
```js ```js
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
import {remark} from 'remark' import {remark} from 'remark'
import remarkLint from 'remark-lint' import remarkLint from 'remark-lint'
import remarkLintOrderedListMarkerValue from 'remark-lint-ordered-list-marker-value' import remarkLintOrderedListMarkerValue from 'remark-lint-ordered-list-marker-value'
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
main() const file = await read('example.md')
async function main() { await remark()
const file = await remark() .use(remarkLint)
.use(remarkLint) .use(remarkLintOrderedListMarkerValue)
.use(remarkLintOrderedListMarkerValue) .process(file)
.process(await read('example.md'))
console.error(reporter(file)) console.error(reporter(file))
}
``` ```
On the CLI: On the CLI:
@ -202,40 +200,6 @@ Paragraph.
No messages. No messages.
##### `not-ok.md`
When configured with `'one'`.
###### In
```markdown
1. Foo
2. Bar
```
###### Out
```text
2:1-2:8: Marker should be `1`, was `2`
```
##### `also-not-ok.md`
When configured with `'one'`.
###### In
```markdown
2. Foo
1. Bar
```
###### Out
```text
1:1-1:8: Marker should be `1`, was `2`
```
##### `ok.md` ##### `ok.md`
When configured with `'single'`. When configured with `'single'`.
@ -294,6 +258,40 @@ No messages.
##### `not-ok.md` ##### `not-ok.md`
When configured with `'one'`.
###### In
```markdown
1. Foo
2. Bar
```
###### Out
```text
2:1-2:8: Marker should be `1`, was `2`
```
##### `also-not-ok.md`
When configured with `'one'`.
###### In
```markdown
2. Foo
1. Bar
```
###### Out
```text
1:1-1:8: Marker should be `1`, was `2`
```
##### `not-ok.md`
When configured with `'ordered'`. When configured with `'ordered'`.
###### In ###### In

View File

@ -76,22 +76,20 @@ In browsers with [`esm.sh`][esmsh]:
On the API: On the API:
```js ```js
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
import {remark} from 'remark' import {remark} from 'remark'
import remarkLint from 'remark-lint' import remarkLint from 'remark-lint'
import remarkLintRuleStyle from 'remark-lint-rule-style' import remarkLintRuleStyle from 'remark-lint-rule-style'
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
main() const file = await read('example.md')
async function main() { await remark()
const file = await remark() .use(remarkLint)
.use(remarkLint) .use(remarkLintRuleStyle)
.use(remarkLintRuleStyle) .process(file)
.process(await read('example.md'))
console.error(reporter(file)) console.error(reporter(file))
}
``` ```
On the CLI: On the CLI:

View File

@ -74,22 +74,20 @@ In browsers with [`esm.sh`][esmsh]:
On the API: On the API:
```js ```js
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
import {remark} from 'remark' import {remark} from 'remark'
import remarkLint from 'remark-lint' import remarkLint from 'remark-lint'
import remarkLintStrikethroughMarker from 'remark-lint-strikethrough-marker' import remarkLintStrikethroughMarker from 'remark-lint-strikethrough-marker'
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
main() const file = await read('example.md')
async function main() { await remark()
const file = await remark() .use(remarkLint)
.use(remarkLint) .use(remarkLintStrikethroughMarker)
.use(remarkLintStrikethroughMarker) .process(file)
.process(await read('example.md'))
console.error(reporter(file)) console.error(reporter(file))
}
``` ```
On the CLI: On the CLI:

View File

@ -75,22 +75,20 @@ In browsers with [`esm.sh`][esmsh]:
On the API: On the API:
```js ```js
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
import {remark} from 'remark' import {remark} from 'remark'
import remarkLint from 'remark-lint' import remarkLint from 'remark-lint'
import remarkLintStrongMarker from 'remark-lint-strong-marker' import remarkLintStrongMarker from 'remark-lint-strong-marker'
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
main() const file = await read('example.md')
async function main() { await remark()
const file = await remark() .use(remarkLint)
.use(remarkLint) .use(remarkLintStrongMarker)
.use(remarkLintStrongMarker) .process(file)
.process(await read('example.md'))
console.error(reporter(file)) console.error(reporter(file))
}
``` ```
On the CLI: On the CLI:
@ -178,20 +176,6 @@ __foo__ and __bar__.
No messages. No messages.
##### `not-ok.md`
###### In
```markdown
**foo** and __bar__.
```
###### Out
```text
1:13-1:20: Strong should use `*` as a marker
```
##### `ok.md` ##### `ok.md`
When configured with `'*'`. When configured with `'*'`.
@ -222,6 +206,20 @@ No messages.
##### `not-ok.md` ##### `not-ok.md`
###### In
```markdown
**foo** and __bar__.
```
###### Out
```text
1:13-1:20: Strong should use `*` as a marker
```
##### `not-ok.md`
When configured with `'💩'`. When configured with `'💩'`.
###### Out ###### Out

View File

@ -77,22 +77,20 @@ In browsers with [`esm.sh`][esmsh]:
On the API: On the API:
```js ```js
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
import {remark} from 'remark' import {remark} from 'remark'
import remarkLint from 'remark-lint' import remarkLint from 'remark-lint'
import remarkLintTableCellPadding from 'remark-lint-table-cell-padding' import remarkLintTableCellPadding from 'remark-lint-table-cell-padding'
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
main() const file = await read('example.md')
async function main() { await remark()
const file = await remark() .use(remarkLint)
.use(remarkLint) .use(remarkLintTableCellPadding)
.use(remarkLintTableCellPadding) .process(file)
.process(await read('example.md'))
console.error(reporter(file)) console.error(reporter(file))
}
``` ```
On the CLI: On the CLI:
@ -206,51 +204,6 @@ Too much padding isnt good either:
13:32: Cell should be padded with 1 space, not 2 13:32: Cell should be padded with 1 space, not 2
``` ```
##### `empty.md`
When configured with `'padded'`.
###### In
> 👉 **Note**: this example uses GFM ([`remark-gfm`][gfm]).
```markdown
<!-- Empty cells are OK, but those surrounding them may not be. -->
| | Alpha | Bravo|
| ------ | ----- | ---: |
| Charlie| | Echo|
```
###### Out
```text
3:25: Cell should be padded
5:10: Cell should be padded
5:25: Cell should be padded
```
##### `missing-body.md`
When configured with `'padded'`.
###### In
> 👉 **Note**: this example uses GFM ([`remark-gfm`][gfm]).
```markdown
<!-- Missing cells are fine as well. -->
| Alpha | Bravo | Charlie |
| ----- | ------- | ------- |
| Delta |
| Echo | Foxtrot |
```
###### Out
No messages.
##### `ok.md` ##### `ok.md`
When configured with `'compact'`. When configured with `'compact'`.
@ -397,6 +350,51 @@ When configured with `'💩'`.
1:1: Incorrect table cell padding style `💩`, expected `'padded'`, `'compact'`, or `'consistent'` 1:1: Incorrect table cell padding style `💩`, expected `'padded'`, `'compact'`, or `'consistent'`
``` ```
##### `empty.md`
When configured with `'padded'`.
###### In
> 👉 **Note**: this example uses GFM ([`remark-gfm`][gfm]).
```markdown
<!-- Empty cells are OK, but those surrounding them may not be. -->
| | Alpha | Bravo|
| ------ | ----- | ---: |
| Charlie| | Echo|
```
###### Out
```text
3:25: Cell should be padded
5:10: Cell should be padded
5:25: Cell should be padded
```
##### `missing-body.md`
When configured with `'padded'`.
###### In
> 👉 **Note**: this example uses GFM ([`remark-gfm`][gfm]).
```markdown
<!-- Missing cells are fine as well. -->
| Alpha | Bravo | Charlie |
| ----- | ------- | ------- |
| Delta |
| Echo | Foxtrot |
```
###### Out
No messages.
## Compatibility ## Compatibility
Projects maintained by the unified collective are compatible with all maintained Projects maintained by the unified collective are compatible with all maintained

View File

@ -76,22 +76,20 @@ In browsers with [`esm.sh`][esmsh]:
On the API: On the API:
```js ```js
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
import {remark} from 'remark' import {remark} from 'remark'
import remarkLint from 'remark-lint' import remarkLint from 'remark-lint'
import remarkLintTablePipeAlignment from 'remark-lint-table-pipe-alignment' import remarkLintTablePipeAlignment from 'remark-lint-table-pipe-alignment'
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
main() const file = await read('example.md')
async function main() { await remark()
const file = await remark() .use(remarkLint)
.use(remarkLint) .use(remarkLintTablePipeAlignment)
.use(remarkLintTablePipeAlignment) .process(file)
.process(await read('example.md'))
console.error(reporter(file)) console.error(reporter(file))
}
``` ```
On the CLI: On the CLI:

View File

@ -78,22 +78,20 @@ In browsers with [`esm.sh`][esmsh]:
On the API: On the API:
```js ```js
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
import {remark} from 'remark' import {remark} from 'remark'
import remarkLint from 'remark-lint' import remarkLint from 'remark-lint'
import remarkLintTablePipes from 'remark-lint-table-pipes' import remarkLintTablePipes from 'remark-lint-table-pipes'
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
main() const file = await read('example.md')
async function main() { await remark()
const file = await remark() .use(remarkLint)
.use(remarkLint) .use(remarkLintTablePipes)
.use(remarkLintTablePipes) .process(file)
.process(await read('example.md'))
console.error(reporter(file)) console.error(reporter(file))
}
``` ```
On the CLI: On the CLI:

View File

@ -75,22 +75,20 @@ In browsers with [`esm.sh`][esmsh]:
On the API: On the API:
```js ```js
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
import {remark} from 'remark' import {remark} from 'remark'
import remarkLint from 'remark-lint' import remarkLint from 'remark-lint'
import remarkLintUnorderedListMarkerStyle from 'remark-lint-unordered-list-marker-style' import remarkLintUnorderedListMarkerStyle from 'remark-lint-unordered-list-marker-style'
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
main() const file = await read('example.md')
async function main() { await remark()
const file = await remark() .use(remarkLint)
.use(remarkLint) .use(remarkLintUnorderedListMarkerStyle)
.use(remarkLintUnorderedListMarkerStyle) .process(file)
.process(await read('example.md'))
console.error(reporter(file)) console.error(reporter(file))
}
``` ```
On the CLI: On the CLI:
@ -174,23 +172,6 @@ Ordered lists are not affected.
No messages. No messages.
##### `not-ok.md`
###### In
```markdown
* Foo
- Bar
+ Baz
```
###### Out
```text
2:1-2:6: Marker style should be `*`
3:1-3:6: Marker style should be `*`
```
##### `ok.md` ##### `ok.md`
When configured with `'*'`. When configured with `'*'`.
@ -235,6 +216,23 @@ No messages.
##### `not-ok.md` ##### `not-ok.md`
###### In
```markdown
* Foo
- Bar
+ Baz
```
###### Out
```text
2:1-2:6: Marker style should be `*`
3:1-3:6: Marker style should be `*`
```
##### `not-ok.md`
When configured with `'💩'`. When configured with `'💩'`.
###### Out ###### Out

View File

@ -82,20 +82,18 @@ In browsers with [`esm.sh`][esmsh]:
On the API: On the API:
```js ```js
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
import {remark} from 'remark' import {remark} from 'remark'
import remarkPresetLintConsistent from 'remark-preset-lint-consistent' import remarkPresetLintConsistent from 'remark-preset-lint-consistent'
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
main() const file = await read('example.md')
async function main() { await remark()
const file = await remark() .use(remarkPresetLintConsistent)
.use(remarkPresetLintConsistent) .process(await read('example.md'))
.process(await read('example.md'))
console.error(reporter(file)) console.error(reporter(file))
}
``` ```
On the CLI: On the CLI:

View File

@ -203,20 +203,18 @@ In browsers with [`esm.sh`][esmsh]:
On the API: On the API:
```js ```js
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
import {remark} from 'remark' import {remark} from 'remark'
import remarkPresetLintMarkdownStyleGuide from 'remark-preset-lint-markdown-style-guide' import remarkPresetLintMarkdownStyleGuide from 'remark-preset-lint-markdown-style-guide'
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
main() const file = await read('example.md')
async function main() { await remark()
const file = await remark() .use(remarkPresetLintMarkdownStyleGuide)
.use(remarkPresetLintMarkdownStyleGuide) .process(await read('example.md'))
.process(await read('example.md'))
console.error(reporter(file)) console.error(reporter(file))
}
``` ```
On the CLI: On the CLI:

View File

@ -84,20 +84,18 @@ In browsers with [`esm.sh`][esmsh]:
On the API: On the API:
```js ```js
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
import {remark} from 'remark' import {remark} from 'remark'
import remarkPresetLintRecommended from 'remark-preset-lint-recommended' import remarkPresetLintRecommended from 'remark-preset-lint-recommended'
import {read} from 'to-vfile'
import {reporter} from 'vfile-reporter'
main() const file = await read('example.md')
async function main() { await remark()
const file = await remark() .use(remarkPresetLintRecommended)
.use(remarkPresetLintRecommended) .process(await read('example.md'))
.process(await read('example.md'))
console.error(reporter(file)) console.error(reporter(file))
}
``` ```
On the CLI: On the CLI:

View File

@ -369,28 +369,24 @@ npm install vfile-reporter remark remark-preset-lint-consistent remark-preset-li
Then create a module `example.js` that contains: Then create a module `example.js` that contains:
```js ```js
import {reporter} from 'vfile-reporter'
import {remark} from 'remark' import {remark} from 'remark'
import remarkLintListItemIndent from 'remark-lint-list-item-indent'
import remarkPresetLintConsistent from 'remark-preset-lint-consistent' import remarkPresetLintConsistent from 'remark-preset-lint-consistent'
import remarkPresetLintRecommended from 'remark-preset-lint-recommended' import remarkPresetLintRecommended from 'remark-preset-lint-recommended'
import remarkLintListItemIndent from 'remark-lint-list-item-indent' import {reporter} from 'vfile-reporter'
main() const file = await remark()
// Check that markdown is consistent.
.use(remarkPresetLintConsistent)
// Few recommended rules.
.use(remarkPresetLintRecommended)
// `remark-lint-list-item-indent` is configured with `tab-size` in the
// recommended preset, but if wed prefer something else, it can be
// reconfigured:
.use(remarkLintListItemIndent, 'space')
.process('1) Hello, _Jupiter_ and *Neptune*!')
async function main() { console.error(reporter(file))
const file = await remark()
// Check that markdown is consistent.
.use(remarkPresetLintConsistent)
// Few recommended rules.
.use(remarkPresetLintRecommended)
// `remark-lint-list-item-indent` is configured with `tab-size` in the
// recommended preset, but if wed prefer something else, it can be
// reconfigured:
.use(remarkLintListItemIndent, 'space')
.process('1) Hello, _Jupiter_ and *Neptune*!')
console.error(reporter(file))
}
``` ```
Running that with `node example.js` yields: Running that with `node example.js` yields:
@ -411,25 +407,21 @@ When you configure lint rules and use remark to format markdown, you must
manually synchronize their configuration: manually synchronize their configuration:
```js ```js
import {reporter} from 'vfile-reporter'
import {remark} from 'remark' import {remark} from 'remark'
import remarkLintEmphasisMarker from 'remark-lint-emphasis-marker' import remarkLintEmphasisMarker from 'remark-lint-emphasis-marker'
import remarkLintStrongMarker from 'remark-lint-strong-marker' import remarkLintStrongMarker from 'remark-lint-strong-marker'
import {reporter} from 'vfile-reporter'
main() const file = await remark()
.use(remarkLintEmphasisMarker, '*')
.use(remarkLintStrongMarker, '*')
.use({
settings: {emphasis: '*', strong: '*'} // `remark-stringify` settings.
})
.process('_Hello_, __world__!')
async function main() { console.error(reporter(file))
const file = await remark() console.log(String(file))
.use(remarkLintEmphasisMarker, '*')
.use(remarkLintStrongMarker, '*')
.use({
settings: {emphasis: '*', strong: '*'} // `remark-stringify` settings.
})
.process('_Hello_, __world__!')
console.error(reporter(file))
console.log(String(file))
}
``` ```
Yields: Yields:

919
script/build-plugins.js Normal file
View File

@ -0,0 +1,919 @@
/**
* @typedef {import('mdast').TableContent} TableContent
* @typedef {import('mdast').TopLevelContent} TopLevelContent
*
* @typedef {import('type-fest').PackageJson} PackageJson
*/
import assert from 'node:assert/strict'
import fs from 'node:fs/promises'
import {inspect} from 'node:util'
import {slug as githubSlug} from 'github-slugger'
import {findAndReplace} from 'mdast-util-find-and-replace'
import {fromMarkdown} from 'mdast-util-from-markdown'
import {gfmToMarkdown} from 'mdast-util-gfm'
import {toMarkdown} from 'mdast-util-to-markdown'
import {toString} from 'mdast-util-to-string'
import parseAuthor from 'parse-author'
import {packagesUrl, plugins, presets} from './info.js'
import {characters} from './characters.js'
/** @type {PackageJson} */
const pack = JSON.parse(await fs.readFile('package.json', 'utf8'))
assert(pack.repository && typeof pack.repository === 'object')
const remote = pack.repository.url
let index = -1
while (++index < plugins.length) {
const info = plugins[index]
const packageUrl = new URL(info.name + '/', packagesUrl)
/** @type {PackageJson} */
const pack = JSON.parse(
await fs.readFile(new URL('package.json', packageUrl), 'utf8')
)
const version = (pack.version || '0').split('.')[0]
const author =
typeof pack.author === 'string' ? parseAuthor(pack.author) : pack.author
const camelcased = info.name.replace(
/-(\w)/g,
function (_, /** @type {string} */ $1) {
return $1.toUpperCase()
}
)
const org = remote.split('/').slice(0, -1).join('/')
const main = remote + '/blob/main'
const health = org + '/.github'
const hMain = health + '/blob/main'
const slug = remote.split('/').slice(-2).join('/')
let hasGfm = false
const descriptionTree = fromMarkdown(info.description)
const summaryTree = fromMarkdown(info.summary || '')
// Autolink `remark-lint`
findAndReplace(summaryTree, [
/remark-lint/g,
function () {
return {
type: 'linkReference',
identifier: 'mono',
referenceType: 'full',
children: [{type: 'inlineCode', value: 'remark-lint'}]
}
}
])
const descriptionContent = /** @type {Array<TopLevelContent>} */ (
descriptionTree.children
)
const summaryContent = /** @type {Array<TopLevelContent>} */ (
summaryTree.children
)
assert.equal(info.name, pack.name, 'expected correct package name')
/** @type {Record<string, Array<TopLevelContent>>} */
const categories = {}
let category = 'Intro'
let contentIndex = -1
while (++contentIndex < descriptionContent.length) {
const node = descriptionContent[contentIndex]
if (node.type === 'heading' && node.depth === 2) {
category = githubSlug(toString(node))
}
if (!(category in categories)) {
categories[category] = []
}
categories[category].push(node)
}
const includes = presets.filter(function (preset) {
return preset.plugins.find(function (d) {
return d[0] === info.name
})
})
/** @type {Array<TopLevelContent>} */
const children = [
{type: 'html', value: '<!--This file is generated-->'},
{
type: 'heading',
depth: 1,
children: [{type: 'text', value: info.name}]
}
]
if (info.deprecated) {
children.push(...descriptionContent)
} else {
children.push(
{
type: 'paragraph',
children: [
{
type: 'linkReference',
identifier: 'build',
referenceType: 'full',
children: [
{
type: 'imageReference',
identifier: 'build-badge',
referenceType: 'full',
alt: 'Build'
}
]
},
{type: 'text', value: '\n'},
{
type: 'linkReference',
identifier: 'coverage',
referenceType: 'full',
children: [
{
type: 'imageReference',
identifier: 'coverage-badge',
referenceType: 'full',
alt: 'Coverage'
}
]
},
{type: 'text', value: '\n'},
{
type: 'linkReference',
identifier: 'downloads',
referenceType: 'full',
children: [
{
type: 'imageReference',
identifier: 'downloads-badge',
referenceType: 'full',
alt: 'Downloads'
}
]
},
{type: 'text', value: '\n'},
{
type: 'linkReference',
identifier: 'size',
referenceType: 'full',
children: [
{
type: 'imageReference',
identifier: 'size-badge',
referenceType: 'full',
alt: 'Size'
}
]
},
{type: 'text', value: '\n'},
{
type: 'linkReference',
identifier: 'collective',
referenceType: 'full',
children: [
{
type: 'imageReference',
identifier: 'sponsors-badge',
referenceType: 'full',
alt: 'Sponsors'
}
]
},
{type: 'text', value: '\n'},
{
type: 'linkReference',
identifier: 'collective',
referenceType: 'full',
children: [
{
type: 'imageReference',
identifier: 'backers-badge',
referenceType: 'full',
alt: 'Backers'
}
]
},
{type: 'text', value: '\n'},
{
type: 'linkReference',
identifier: 'chat',
referenceType: 'full',
children: [
{
type: 'imageReference',
identifier: 'chat-badge',
referenceType: 'full',
alt: 'Chat'
}
]
}
]
},
...summaryContent,
{
type: 'heading',
depth: 2,
children: [{type: 'text', value: 'Contents'}]
},
{
type: 'heading',
depth: 2,
children: [{type: 'text', value: 'What is this?'}]
},
{
type: 'paragraph',
children: [
{type: 'text', value: 'This package is a '},
{
type: 'linkReference',
identifier: 'unified',
referenceType: 'collapsed',
children: [{type: 'text', value: 'unified'}]
},
{type: 'text', value: ' ('},
{
type: 'linkReference',
identifier: 'remark',
referenceType: 'collapsed',
children: [{type: 'text', value: 'remark'}]
},
{
type: 'text',
value: ') plugin, specifically a '
},
{
type: 'inlineCode',
value: 'remark-lint'
},
{
type: 'text',
value: '\nrule.\nLint rules check markdown code style.'
}
]
},
...(categories['when-should-i-use-this'] || []),
{
type: 'heading',
depth: 2,
children: [{type: 'text', value: 'Presets'}]
}
)
if (includes.length === 0) {
children.push({
type: 'paragraph',
children: [
{
type: 'text',
value: 'This rule is not included in a preset maintained here.'
}
]
})
} else {
children.push(
{
type: 'paragraph',
children: [
{
type: 'text',
value: 'This rule is included in the following presets:'
}
]
},
{
type: 'table',
align: [],
children: [
{
type: 'tableRow',
children: [
{
type: 'tableCell',
children: [{type: 'text', value: 'Preset'}]
},
{
type: 'tableCell',
children: [{type: 'text', value: 'Setting'}]
}
]
},
...includes.map(function (preset) {
const tuple = preset.plugins.find(function (d) {
return d[0] === info.name
})
assert(tuple)
const option = tuple[1]
/** @type {TableContent} */
const row = {
type: 'tableRow',
children: [
{
type: 'tableCell',
children: [
{
type: 'link',
url: remote + '/tree/main/packages/' + preset.name,
children: [{type: 'inlineCode', value: preset.name}]
}
]
},
{
type: 'tableCell',
children: option
? [{type: 'inlineCode', value: inspect(option)}]
: []
}
]
}
return row
})
]
}
)
}
children.push(
{
type: 'heading',
depth: 2,
children: [{type: 'text', value: 'Install'}]
},
{
type: 'paragraph',
children: [
{type: 'text', value: 'This package is '},
{
type: 'linkReference',
identifier: 'esm',
referenceType: 'full',
children: [{type: 'text', value: 'ESM only'}]
},
{
type: 'text',
value:
'.\nIn Node.js (version 12.20+, 14.14+, or 16.0+), ' +
'install with '
},
{
type: 'linkReference',
identifier: 'npm',
referenceType: 'collapsed',
children: [{type: 'text', value: 'npm'}]
},
{type: 'text', value: ':'}
]
},
{type: 'code', lang: 'sh', value: 'npm install ' + info.name},
{
type: 'paragraph',
children: [
{type: 'text', value: 'In Deno with '},
{
type: 'linkReference',
identifier: 'esmsh',
label: 'esmsh',
referenceType: 'full',
children: [{type: 'inlineCode', value: 'esm.sh'}]
},
{type: 'text', value: ':'}
]
},
{
type: 'code',
lang: 'js',
value:
'import ' +
camelcased +
" from 'https://esm.sh/" +
info.name +
'@' +
version +
"'"
},
{
type: 'paragraph',
children: [
{type: 'text', value: 'In browsers with '},
{
type: 'linkReference',
identifier: 'esmsh',
label: 'esmsh',
referenceType: 'full',
children: [{type: 'inlineCode', value: 'esm.sh'}]
},
{type: 'text', value: ':'}
]
},
{
type: 'code',
lang: 'html',
value:
'<script type="module">\n import ' +
camelcased +
" from 'https://esm.sh/" +
info.name +
'@' +
version +
"?bundle'\n</script>"
},
{
type: 'heading',
depth: 2,
children: [{type: 'text', value: 'Use'}]
},
{
type: 'paragraph',
children: [{type: 'text', value: 'On the API:'}]
},
{
type: 'code',
lang: 'js',
value: [
"import {remark} from 'remark'",
"import remarkLint from 'remark-lint'",
'import ' + camelcased + " from '" + info.name + "'",
"import {read} from 'to-vfile'",
"import {reporter} from 'vfile-reporter'",
'',
"const file = await read('example.md')",
'',
'await remark()',
' .use(remarkLint)',
' .use(' + camelcased + ')',
' .process(file)',
'',
'console.error(reporter(file))'
].join('\n')
},
{
type: 'paragraph',
children: [{type: 'text', value: 'On the CLI:'}]
},
{
type: 'code',
lang: 'sh',
value: 'remark --use remark-lint --use ' + info.name + ' example.md'
},
{
type: 'paragraph',
children: [
{
type: 'text',
value: 'On the CLI in a config file (here a '
},
{
type: 'inlineCode',
value: 'package.json'
},
{
type: 'text',
value: '):'
}
]
},
{
type: 'code',
lang: 'diff',
value: [
' …',
' "remarkConfig": {',
' "plugins": [',
' …',
' "remark-lint",',
'+ "' + info.name + '",',
' …',
' ]',
' }',
' …'
].join('\n')
}
)
if ('api' in categories) {
const [apiHeading, ...apiBody] = categories.api
children.push(
apiHeading,
{
type: 'paragraph',
children: [
{
type: 'text',
value:
'This package exports no identifiers.\nThe default export is '
},
{type: 'inlineCode', value: camelcased},
{type: 'text', value: '.'}
]
},
{
type: 'heading',
depth: 3,
children: [
{
type: 'inlineCode',
value: 'unified().use(' + camelcased + '[, config])'
}
]
},
{
type: 'paragraph',
children: [
{
type: 'text',
value:
'This rule supports standard configuration that all remark lint rules accept\n(such as '
},
{type: 'inlineCode', value: 'false'},
{type: 'text', value: ' to turn it off or '},
{type: 'inlineCode', value: '[1, options]'},
{type: 'text', value: ' to configure it).'}
]
},
...apiBody
)
}
children.push(
...(categories.recommendation || []),
...(categories.fix || []),
...(categories.example || [])
)
let first = true
for (const check of info.checks) {
if (first) {
children.push({
type: 'heading',
depth: 2,
children: [{type: 'text', value: 'Examples'}]
})
first = false
}
/** @type {{config: unknown}} */
const {config} = JSON.parse(check.configuration)
let clean = check.input
children.push({
type: 'heading',
depth: 5,
children: [{type: 'inlineCode', value: check.name}]
})
if (config !== true) {
children.push({
type: 'paragraph',
children: [
{type: 'text', value: 'When configured with '},
{type: 'inlineCode', value: inspect(config)},
{type: 'text', value: '.'}
]
})
}
if (check.input.trim() !== '') {
children.push({
type: 'heading',
depth: 6,
children: [{type: 'text', value: 'In'}]
})
if (check.gfm) {
hasGfm = true
children.push({
type: 'blockquote',
children: [
{
type: 'paragraph',
children: [
{type: 'text', value: '👉 '},
{
type: 'strong',
children: [{type: 'text', value: 'Note'}]
},
{type: 'text', value: ': this example uses GFM ('},
{
type: 'linkReference',
identifier: 'gfm',
referenceType: 'full',
children: [{type: 'inlineCode', value: 'remark-gfm'}]
},
{type: 'text', value: ').'}
]
}
]
})
}
let index = -1
while (++index < characters.length) {
const char = characters[index]
const next = clean.replace(char.in, char.out)
if (clean !== next) {
children.push({
type: 'blockquote',
children: [
{
type: 'paragraph',
children: [
{type: 'text', value: '👉 '},
{
type: 'strong',
children: [{type: 'text', value: 'Note'}]
},
{type: 'text', value: ': '},
{type: 'inlineCode', value: char.char},
{
type: 'text',
value: ' represents ' + char.name + '.'
}
]
}
]
})
clean = next
}
}
children.push({
type: 'code',
lang: 'markdown',
value: check.input
})
}
children.push({
type: 'heading',
depth: 6,
children: [{type: 'text', value: 'Out'}]
})
if (check.output.length === 0) {
children.push({
type: 'paragraph',
children: [{type: 'text', value: 'No messages.'}]
})
} else {
children.push({
type: 'code',
lang: 'text',
value: check.output.join('\n')
})
}
}
children.push(
{
type: 'heading',
depth: 2,
children: [{type: 'text', value: 'Compatibility'}]
},
{
type: 'paragraph',
children: [
{
type: 'text',
value:
'Projects maintained by the unified collective are compatible with all maintained\nversions of Node.js.\nAs of now, that is Node.js 12.20+, 14.14+, and 16.0+.\nOur projects sometimes work with older versions, but this is not guaranteed.'
}
]
},
{
type: 'heading',
depth: 2,
children: [{type: 'text', value: 'Contribute'}]
},
{
type: 'paragraph',
children: [
{type: 'text', value: 'See '},
{
type: 'linkReference',
referenceType: 'collapsed',
identifier: 'contributing',
children: [{type: 'inlineCode', value: 'contributing.md'}]
},
{type: 'text', value: ' in '},
{
type: 'linkReference',
referenceType: 'collapsed',
identifier: 'health',
children: [
{
type: 'inlineCode',
value: health.split('/').slice(-2).join('/')
}
]
},
{type: 'text', value: ' for ways\nto get started.\nSee '},
{
type: 'linkReference',
referenceType: 'collapsed',
identifier: 'support',
children: [{type: 'inlineCode', value: 'support.md'}]
},
{type: 'text', value: ' for ways to get help.'}
]
},
{
type: 'paragraph',
children: [
{type: 'text', value: 'This project has a '},
{
type: 'linkReference',
referenceType: 'collapsed',
identifier: 'coc',
children: [{type: 'text', value: 'code of conduct'}]
},
{
type: 'text',
value:
'.\nBy interacting with this repository, organization, or community you agree to\nabide by its terms.'
}
]
}
)
}
children.push(
{type: 'heading', depth: 2, children: [{type: 'text', value: 'License'}]},
{
type: 'paragraph',
children: [
{
type: 'linkReference',
referenceType: 'collapsed',
identifier: 'license',
children: [{type: 'text', value: String(pack.license || '')}]
},
{type: 'text', value: ' © '},
{
type: 'linkReference',
referenceType: 'collapsed',
identifier: 'author',
children: [
{type: 'text', value: String((author && author.name) || '')}
]
}
]
}
)
if (!info.deprecated) {
children.push(
{
type: 'definition',
identifier: 'build-badge',
url: 'https://github.com/' + slug + '/workflows/main/badge.svg'
},
{
type: 'definition',
identifier: 'build',
url: 'https://github.com/' + slug + '/actions'
},
{
type: 'definition',
identifier: 'coverage-badge',
url: 'https://img.shields.io/codecov/c/github/' + slug + '.svg'
},
{
type: 'definition',
identifier: 'coverage',
url: 'https://codecov.io/github/' + slug
},
{
type: 'definition',
identifier: 'downloads-badge',
url: 'https://img.shields.io/npm/dm/' + info.name + '.svg'
},
{
type: 'definition',
identifier: 'downloads',
url: 'https://www.npmjs.com/package/' + info.name
},
{
type: 'definition',
identifier: 'size-badge',
url: 'https://img.shields.io/bundlephobia/minzip/' + info.name + '.svg'
},
{
type: 'definition',
identifier: 'size',
url: 'https://bundlephobia.com/result?p=' + info.name
},
{
type: 'definition',
identifier: 'sponsors-badge',
url: 'https://opencollective.com/unified/sponsors/badge.svg'
},
{
type: 'definition',
identifier: 'backers-badge',
url: 'https://opencollective.com/unified/backers/badge.svg'
},
{
type: 'definition',
identifier: 'collective',
url: 'https://opencollective.com/unified'
},
{
type: 'definition',
identifier: 'chat-badge',
url: 'https://img.shields.io/badge/chat-discussions-success.svg'
},
{
type: 'definition',
identifier: 'chat',
url: 'https://github.com/remarkjs/remark/discussions'
},
{
type: 'definition',
identifier: 'unified',
url: 'https://github.com/unifiedjs/unified'
},
{
type: 'definition',
identifier: 'remark',
url: 'https://github.com/remarkjs/remark'
},
{
type: 'definition',
identifier: 'mono',
url: 'https://github.com/' + slug
},
{
type: 'definition',
identifier: 'esm',
url: 'https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c'
},
{
type: 'definition',
identifier: 'esmsh',
url: 'https://esm.sh'
},
{
type: 'definition',
identifier: 'npm',
url: 'https://docs.npmjs.com/cli/install'
},
{
type: 'definition',
identifier: 'health',
url: health
},
{
type: 'definition',
identifier: 'contributing',
url: hMain + '/contributing.md'
},
{
type: 'definition',
identifier: 'support',
url: hMain + '/support.md'
},
{
type: 'definition',
identifier: 'coc',
url: hMain + '/code-of-conduct.md'
}
)
}
children.push(
{
type: 'definition',
identifier: 'license',
url: main + '/license'
},
{
type: 'definition',
identifier: 'author',
url: String((author && author.url) || '')
}
)
if (hasGfm) {
children.push({
type: 'definition',
identifier: 'gfm',
url: 'https://github.com/remarkjs/remark-gfm'
})
}
await fs.writeFile(
new URL('readme.md', packageUrl),
toMarkdown({type: 'root', children}, {extensions: [gfmToMarkdown()]})
)
console.log('✓ wrote `readme.md` in `' + info.name + '`')
}

File diff suppressed because it is too large Load Diff

View File

@ -1,967 +0,0 @@
/**
* @typedef {import('mdast').BlockContent} BlockContent
* @typedef {import('mdast').DefinitionContent} DefinitionContent
* @typedef {import('mdast').TableContent} TableContent
* @typedef {import('type-fest').PackageJson} PackageJson
*/
/**
* @typedef {BlockContent | DefinitionContent} BlockAndDefinitionContent
*/
import fs from 'node:fs'
import path from 'node:path'
import process from 'node:process'
import {inspect} from 'node:util'
import {unified} from 'unified'
import {remark} from 'remark'
import remarkParse from 'remark-parse'
import remarkGfm from 'remark-gfm'
import {findAndReplace} from 'mdast-util-find-and-replace'
import {toString} from 'mdast-util-to-string'
import {slug as githubSlug} from 'github-slugger'
import parseAuthor from 'parse-author'
import {rules} from './util/rules.js'
import {rule} from './util/rule.js'
import {presets} from './util/presets.js'
import {repoUrl} from './util/repo-url.js'
import {characters} from './characters.js'
const own = {}.hasOwnProperty
const remote = repoUrl('package.json')
const root = path.join(process.cwd(), 'packages')
// eslint-disable-next-line complexity, unicorn/prefer-top-level-await
presets(root).then((presetObjects) => {
const allRules = rules(root)
let index = -1
while (++index < allRules.length) {
const basename = allRules[index]
const base = path.resolve(root, basename)
/** @type {PackageJson} */
const pack = JSON.parse(
String(fs.readFileSync(path.join(base, 'package.json')))
)
const version = (pack.version || '0').split('.')[0]
const info = rule(base)
const tests = info.tests
const author =
typeof pack.author === 'string' ? parseAuthor(pack.author) : pack.author
const camelcased = basename.replace(
/-(\w)/g,
(_, /** @type {string} */ $1) => $1.toUpperCase()
)
const org = remote.split('/').slice(0, -1).join('/')
const main = remote + '/blob/main'
const health = org + '/.github'
const hMain = health + '/blob/main'
const slug = remote.split('/').slice(-2).join('/')
let hasGfm = false
const descriptionTree = unified().use(remarkParse).parse(info.description)
const summaryTree = unified()
.use(remarkParse)
.parse(info.summary || '')
// Autolink `remark-lint`
unified()
.use(
/** @type {import('unified').Plugin<Array<void>, import('mdast').Root>} */
() => (tree) => {
findAndReplace(tree, [
/remark-lint/g,
() => {
return {
type: 'linkReference',
identifier: 'mono',
referenceType: 'full',
children: [{type: 'inlineCode', value: 'remark-lint'}]
}
}
])
}
)
.runSync(summaryTree)
const descriptionContent = /** @type {Array<BlockAndDefinitionContent>} */ (
descriptionTree.children
)
const summaryContent = /** @type {Array<BlockAndDefinitionContent>} */ (
summaryTree.children
)
if (basename !== pack.name) {
throw new Error(
'Expected package name (`' +
pack.name +
'`) to be the same as directory name (`' +
basename +
'`)'
)
}
/** @type {Record<string, Array<BlockAndDefinitionContent>>} */
const categories = {}
let category = 'Intro'
let contentIndex = -1
while (++contentIndex < descriptionContent.length) {
const node = descriptionContent[contentIndex]
if (node.type === 'heading' && node.depth === 2) {
category = githubSlug(toString(node))
}
if (!(category in categories)) {
categories[category] = []
}
categories[category].push(node)
}
const includes = presetObjects.filter(
(preset) => basename in preset.packages
)
/** @type {Array<BlockAndDefinitionContent>} */
const children = [
{type: 'html', value: '<!--This file is generated-->'},
{
type: 'heading',
depth: 1,
children: [{type: 'text', value: basename}]
}
]
if (info.deprecated) {
children.push(...descriptionContent)
} else {
children.push(
{
type: 'paragraph',
children: [
{
type: 'linkReference',
identifier: 'build',
referenceType: 'full',
children: [
{
type: 'imageReference',
identifier: 'build-badge',
referenceType: 'full',
alt: 'Build'
}
]
},
{type: 'text', value: '\n'},
{
type: 'linkReference',
identifier: 'coverage',
referenceType: 'full',
children: [
{
type: 'imageReference',
identifier: 'coverage-badge',
referenceType: 'full',
alt: 'Coverage'
}
]
},
{type: 'text', value: '\n'},
{
type: 'linkReference',
identifier: 'downloads',
referenceType: 'full',
children: [
{
type: 'imageReference',
identifier: 'downloads-badge',
referenceType: 'full',
alt: 'Downloads'
}
]
},
{type: 'text', value: '\n'},
{
type: 'linkReference',
identifier: 'size',
referenceType: 'full',
children: [
{
type: 'imageReference',
identifier: 'size-badge',
referenceType: 'full',
alt: 'Size'
}
]
},
{type: 'text', value: '\n'},
{
type: 'linkReference',
identifier: 'collective',
referenceType: 'full',
children: [
{
type: 'imageReference',
identifier: 'sponsors-badge',
referenceType: 'full',
alt: 'Sponsors'
}
]
},
{type: 'text', value: '\n'},
{
type: 'linkReference',
identifier: 'collective',
referenceType: 'full',
children: [
{
type: 'imageReference',
identifier: 'backers-badge',
referenceType: 'full',
alt: 'Backers'
}
]
},
{type: 'text', value: '\n'},
{
type: 'linkReference',
identifier: 'chat',
referenceType: 'full',
children: [
{
type: 'imageReference',
identifier: 'chat-badge',
referenceType: 'full',
alt: 'Chat'
}
]
}
]
},
...summaryContent,
{
type: 'heading',
depth: 2,
children: [{type: 'text', value: 'Contents'}]
},
{
type: 'heading',
depth: 2,
children: [{type: 'text', value: 'What is this?'}]
},
{
type: 'paragraph',
children: [
{type: 'text', value: 'This package is a '},
{
type: 'linkReference',
identifier: 'unified',
referenceType: 'collapsed',
children: [{type: 'text', value: 'unified'}]
},
{type: 'text', value: ' ('},
{
type: 'linkReference',
identifier: 'remark',
referenceType: 'collapsed',
children: [{type: 'text', value: 'remark'}]
},
{
type: 'text',
value: ') plugin, specifically a '
},
{
type: 'inlineCode',
value: 'remark-lint'
},
{
type: 'text',
value: '\nrule.\nLint rules check markdown code style.'
}
]
},
...(categories['when-should-i-use-this'] || []),
{
type: 'heading',
depth: 2,
children: [{type: 'text', value: 'Presets'}]
}
)
if (includes.length === 0) {
children.push({
type: 'paragraph',
children: [
{
type: 'text',
value: 'This rule is not included in a preset maintained here.'
}
]
})
} else {
children.push(
{
type: 'paragraph',
children: [
{
type: 'text',
value: 'This rule is included in the following presets:'
}
]
},
{
type: 'table',
align: [],
children: [
{
type: 'tableRow',
children: [
{
type: 'tableCell',
children: [{type: 'text', value: 'Preset'}]
},
{
type: 'tableCell',
children: [{type: 'text', value: 'Setting'}]
}
]
},
...includes.map((preset) => {
const option = preset.packages[basename]
/** @type {TableContent} */
const row = {
type: 'tableRow',
children: [
{
type: 'tableCell',
children: [
{
type: 'link',
url: remote + '/tree/main/packages/' + preset.name,
title: null,
children: [{type: 'inlineCode', value: preset.name}]
}
]
},
{
type: 'tableCell',
children: option
? [{type: 'inlineCode', value: inspect(option)}]
: []
}
]
}
return row
})
]
}
)
}
children.push(
{
type: 'heading',
depth: 2,
children: [{type: 'text', value: 'Install'}]
},
{
type: 'paragraph',
children: [
{type: 'text', value: 'This package is '},
{
type: 'linkReference',
identifier: 'esm',
referenceType: 'full',
children: [{type: 'text', value: 'ESM only'}]
},
{
type: 'text',
value:
'.\nIn Node.js (version 12.20+, 14.14+, or 16.0+), ' +
'install with '
},
{
type: 'linkReference',
identifier: 'npm',
referenceType: 'collapsed',
children: [{type: 'text', value: 'npm'}]
},
{type: 'text', value: ':'}
]
},
{type: 'code', lang: 'sh', value: 'npm install ' + basename},
{
type: 'paragraph',
children: [
{type: 'text', value: 'In Deno with '},
{
type: 'linkReference',
identifier: 'esmsh',
label: 'esmsh',
referenceType: 'full',
children: [{type: 'inlineCode', value: 'esm.sh'}]
},
{type: 'text', value: ':'}
]
},
{
type: 'code',
lang: 'js',
value:
'import ' +
camelcased +
" from 'https://esm.sh/" +
basename +
'@' +
version +
"'"
},
{
type: 'paragraph',
children: [
{type: 'text', value: 'In browsers with '},
{
type: 'linkReference',
identifier: 'esmsh',
label: 'esmsh',
referenceType: 'full',
children: [{type: 'inlineCode', value: 'esm.sh'}]
},
{type: 'text', value: ':'}
]
},
{
type: 'code',
lang: 'html',
value:
'<script type="module">\n import ' +
camelcased +
" from 'https://esm.sh/" +
basename +
'@' +
version +
"?bundle'\n</script>"
},
{
type: 'heading',
depth: 2,
children: [{type: 'text', value: 'Use'}]
},
{
type: 'paragraph',
children: [{type: 'text', value: 'On the API:'}]
},
{
type: 'code',
lang: 'js',
value: [
"import {read} from 'to-vfile'",
"import {reporter} from 'vfile-reporter'",
"import {remark} from 'remark'",
"import remarkLint from 'remark-lint'",
'import ' + camelcased + " from '" + basename + "'",
'',
'main()',
'',
'async function main() {',
' const file = await remark()',
' .use(remarkLint)',
' .use(' + camelcased + ')',
" .process(await read('example.md'))",
'',
' console.error(reporter(file))',
'}'
].join('\n')
},
{
type: 'paragraph',
children: [{type: 'text', value: 'On the CLI:'}]
},
{
type: 'code',
lang: 'sh',
value: 'remark --use remark-lint --use ' + basename + ' example.md'
},
{
type: 'paragraph',
children: [
{
type: 'text',
value: 'On the CLI in a config file (here a '
},
{
type: 'inlineCode',
value: 'package.json'
},
{
type: 'text',
value: '):'
}
]
},
{
type: 'code',
lang: 'diff',
value: [
' …',
' "remarkConfig": {',
' "plugins": [',
' …',
' "remark-lint",',
'+ "' + basename + '",',
' …',
' ]',
' }',
' …'
].join('\n')
}
)
if ('api' in categories) {
const [apiHeading, ...apiBody] = categories.api
children.push(
apiHeading,
{
type: 'paragraph',
children: [
{
type: 'text',
value:
'This package exports no identifiers.\nThe default export is '
},
{type: 'inlineCode', value: camelcased},
{type: 'text', value: '.'}
]
},
{
type: 'heading',
depth: 3,
children: [
{
type: 'inlineCode',
value: 'unified().use(' + camelcased + '[, config])'
}
]
},
{
type: 'paragraph',
children: [
{
type: 'text',
value:
'This rule supports standard configuration that all remark lint rules accept\n(such as '
},
{type: 'inlineCode', value: 'false'},
{type: 'text', value: ' to turn it off or '},
{type: 'inlineCode', value: '[1, options]'},
{type: 'text', value: ' to configure it).'}
]
},
...apiBody
)
}
children.push(
...(categories.recommendation || []),
...(categories.fix || []),
...(categories.example || [])
)
let first = true
/** @type {string} */
let configuration
for (configuration in tests) {
if (own.call(tests, configuration)) {
const fixtures = tests[configuration]
if (first) {
children.push({
type: 'heading',
depth: 2,
children: [{type: 'text', value: 'Examples'}]
})
first = false
}
/** @type {string} */
let fileName
for (fileName in fixtures) {
if (own.call(fixtures, fileName)) {
const fixture = fixtures[fileName]
/** @type {{config: unknown}} */
const {config} = JSON.parse(configuration)
let clean = fixture.input
children.push({
type: 'heading',
depth: 5,
children: [{type: 'inlineCode', value: fileName}]
})
if (config !== true) {
children.push({
type: 'paragraph',
children: [
{type: 'text', value: 'When configured with '},
{type: 'inlineCode', value: inspect(config)},
{type: 'text', value: '.'}
]
})
}
if (
fixture.input !== null &&
fixture.input !== undefined &&
fixture.input.trim() !== ''
) {
children.push({
type: 'heading',
depth: 6,
children: [{type: 'text', value: 'In'}]
})
if (fixture.gfm) {
hasGfm = true
children.push({
type: 'blockquote',
children: [
{
type: 'paragraph',
children: [
{type: 'text', value: '👉 '},
{
type: 'strong',
children: [{type: 'text', value: 'Note'}]
},
{type: 'text', value: ': this example uses GFM ('},
{
type: 'linkReference',
identifier: 'gfm',
referenceType: 'full',
children: [
{type: 'inlineCode', value: 'remark-gfm'}
]
},
{type: 'text', value: ').'}
]
}
]
})
}
let index = -1
while (++index < characters.length) {
const char = characters[index]
const next = clean.replace(char.in, char.out)
if (clean !== next) {
children.push({
type: 'blockquote',
children: [
{
type: 'paragraph',
children: [
{type: 'text', value: '👉 '},
{
type: 'strong',
children: [{type: 'text', value: 'Note'}]
},
{type: 'text', value: ': '},
{type: 'inlineCode', value: char.char},
{
type: 'text',
value: ' represents ' + char.name + '.'
}
]
}
]
})
clean = next
}
}
children.push({
type: 'code',
lang: 'markdown',
value: fixture.input
})
}
children.push({
type: 'heading',
depth: 6,
children: [{type: 'text', value: 'Out'}]
})
if (fixture.output.length === 0) {
children.push({
type: 'paragraph',
children: [{type: 'text', value: 'No messages.'}]
})
} else {
children.push({
type: 'code',
lang: 'text',
value: fixture.output.join('\n')
})
}
}
}
}
}
children.push(
{
type: 'heading',
depth: 2,
children: [{type: 'text', value: 'Compatibility'}]
},
{
type: 'paragraph',
children: [
{
type: 'text',
value:
'Projects maintained by the unified collective are compatible with all maintained\nversions of Node.js.\nAs of now, that is Node.js 12.20+, 14.14+, and 16.0+.\nOur projects sometimes work with older versions, but this is not guaranteed.'
}
]
},
{
type: 'heading',
depth: 2,
children: [{type: 'text', value: 'Contribute'}]
},
{
type: 'paragraph',
children: [
{type: 'text', value: 'See '},
{
type: 'linkReference',
referenceType: 'collapsed',
identifier: 'contributing',
children: [{type: 'inlineCode', value: 'contributing.md'}]
},
{type: 'text', value: ' in '},
{
type: 'linkReference',
referenceType: 'collapsed',
identifier: 'health',
children: [
{
type: 'inlineCode',
value: health.split('/').slice(-2).join('/')
}
]
},
{type: 'text', value: ' for ways\nto get started.\nSee '},
{
type: 'linkReference',
referenceType: 'collapsed',
identifier: 'support',
children: [{type: 'inlineCode', value: 'support.md'}]
},
{type: 'text', value: ' for ways to get help.'}
]
},
{
type: 'paragraph',
children: [
{type: 'text', value: 'This project has a '},
{
type: 'linkReference',
referenceType: 'collapsed',
identifier: 'coc',
children: [{type: 'text', value: 'code of conduct'}]
},
{
type: 'text',
value:
'.\nBy interacting with this repository, organization, or community you agree to\nabide by its terms.'
}
]
}
)
}
children.push(
{type: 'heading', depth: 2, children: [{type: 'text', value: 'License'}]},
{
type: 'paragraph',
children: [
{
type: 'linkReference',
referenceType: 'collapsed',
identifier: 'license',
children: [{type: 'text', value: String(pack.license || '')}]
},
{type: 'text', value: ' © '},
{
type: 'linkReference',
referenceType: 'collapsed',
identifier: 'author',
children: [
{type: 'text', value: String((author && author.name) || '')}
]
}
]
}
)
if (!info.deprecated) {
children.push(
{
type: 'definition',
identifier: 'build-badge',
url: 'https://github.com/' + slug + '/workflows/main/badge.svg'
},
{
type: 'definition',
identifier: 'build',
url: 'https://github.com/' + slug + '/actions'
},
{
type: 'definition',
identifier: 'coverage-badge',
url: 'https://img.shields.io/codecov/c/github/' + slug + '.svg'
},
{
type: 'definition',
identifier: 'coverage',
url: 'https://codecov.io/github/' + slug
},
{
type: 'definition',
identifier: 'downloads-badge',
url: 'https://img.shields.io/npm/dm/' + basename + '.svg'
},
{
type: 'definition',
identifier: 'downloads',
url: 'https://www.npmjs.com/package/' + basename
},
{
type: 'definition',
identifier: 'size-badge',
url: 'https://img.shields.io/bundlephobia/minzip/' + basename + '.svg'
},
{
type: 'definition',
identifier: 'size',
url: 'https://bundlephobia.com/result?p=' + basename
},
{
type: 'definition',
identifier: 'sponsors-badge',
url: 'https://opencollective.com/unified/sponsors/badge.svg'
},
{
type: 'definition',
identifier: 'backers-badge',
url: 'https://opencollective.com/unified/backers/badge.svg'
},
{
type: 'definition',
identifier: 'collective',
url: 'https://opencollective.com/unified'
},
{
type: 'definition',
identifier: 'chat-badge',
url: 'https://img.shields.io/badge/chat-discussions-success.svg'
},
{
type: 'definition',
identifier: 'chat',
url: 'https://github.com/remarkjs/remark/discussions'
},
{
type: 'definition',
identifier: 'unified',
url: 'https://github.com/unifiedjs/unified'
},
{
type: 'definition',
identifier: 'remark',
url: 'https://github.com/remarkjs/remark'
},
{
type: 'definition',
identifier: 'mono',
url: 'https://github.com/' + slug
},
{
type: 'definition',
identifier: 'esm',
url: 'https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c'
},
{
type: 'definition',
identifier: 'esmsh',
url: 'https://esm.sh'
},
{
type: 'definition',
identifier: 'npm',
url: 'https://docs.npmjs.com/cli/install'
},
{
type: 'definition',
identifier: 'health',
url: health
},
{
type: 'definition',
identifier: 'contributing',
url: hMain + '/contributing.md'
},
{
type: 'definition',
identifier: 'support',
url: hMain + '/support.md'
},
{
type: 'definition',
identifier: 'coc',
url: hMain + '/code-of-conduct.md'
}
)
}
children.push(
{
type: 'definition',
identifier: 'license',
url: main + '/license'
},
{
type: 'definition',
identifier: 'author',
url: String((author && author.url) || '')
}
)
if (hasGfm) {
children.push({
type: 'definition',
identifier: 'gfm',
url: 'https://github.com/remarkjs/remark-gfm'
})
}
fs.writeFileSync(
path.join(base, 'readme.md'),
remark().use(remarkGfm).stringify({type: 'root', children})
)
console.log('✓ wrote `readme.md` in `' + basename + '`')
}
})

253
script/info.js Normal file
View File

@ -0,0 +1,253 @@
/**
* @typedef {import('unified').Preset} Preset
*/
/**
* @typedef Check
* Check.
* @property {string} configuration
* Configuration.
* @property {boolean} gfm
* Whether to use GFM.
* @property {string} input
* Input.
* @property {string} name
* Name.
* @property {Array<string>} output
* Output.
* @property {boolean} positionless
* Whether this check also applies without positions.
*
* @typedef ExampleInfo
* Example.
* @property {unknown} [config]
* Configuration.
* @property {boolean} [gfm]
* Whether to use GFM.
* @property {'input' | 'output'} [label]
* Label.
* @property {boolean} [positionless]
* Whether this check also applies without positions.
* @property {string} name
* Name.
*
* @typedef PluginInfo
* Plugin.
* @property {boolean} deprecated
* Whether the plugin is deprecated.
* @property {string} description
* Description.
* @property {string} name
* Name.
* @property {string} ruleId
* Rule ID.
* @property {string | undefined} summary
* Summary.
* @property {Array<Check>} checks
* Checks.
*
* @typedef PresetInfo
* Preset.
* @property {string} name
* Name.
* @property {Array<[string, unknown]>} plugins
* Plugins and their configuration.
*/
import assert from 'node:assert/strict'
import fs from 'node:fs/promises'
import {parse} from 'comment-parser'
import strip from 'strip-indent'
export const packagesUrl = new URL('../packages/', import.meta.url)
/**
* @type {Array<PluginInfo>}
* Plugins.
*/
export const plugins = []
/**
* @type {Array<PresetInfo>}
* Presets.
*/
export const presets = []
const names = await fs.readdir(packagesUrl)
for (const name of names) {
if (name.startsWith('remark-lint-')) {
await addPlugin(name)
}
if (name.startsWith('remark-preset-lint-')) {
await addPreset(name)
}
}
/**
* @param {string} name
* Plugin name.
* @returns {Promise<undefined>}
* Nothing.
*/
async function addPlugin(name) {
const ruleId = name.slice('remark-lint-'.length)
const code = await fs.readFile(
new URL(name + '/index.js', packagesUrl),
'utf8'
)
const fileInfo = parse(code, {spacing: 'preserve'})[0]
const tags = fileInfo.tags
const deprecatedTag = tags.find(function (d) {
return d.tag === 'deprecated'
})
const moduleTag = tags.find(function (d) {
return d.tag === 'module'
})
const summaryTag = tags.find(function (d) {
return d.tag === 'summary'
})
assert(moduleTag, 'expected `@module` in JSDoc')
assert.equal(moduleTag.name, ruleId, 'expected correct `@module`')
let description = deprecatedTag
? deprecatedTag.description
: fileInfo.description
assert(description, 'expected description (or `@deprecated`)')
description = strip(description)
/** @type {PluginInfo} */
const result = {
deprecated: Boolean(deprecatedTag),
description: description.trim(),
name,
ruleId,
summary: summaryTag ? strip(summaryTag.description).trim() : undefined,
checks: []
}
const examples = tags
.filter(function (d) {
return d.tag === 'example'
})
.map(function (d) {
return d.description.replace(/^\r?\n|\r?\n$/g, '')
})
let index = -1
while (++index < examples.length) {
const lines = examples[index].split('\n')
/** @type {ExampleInfo} */
let info
try {
info = JSON.parse(lines[0])
lines.splice(0, 1)
/* c8 ignore next 4 */
} catch (error) {
const cause = /** @type {Error} */ (error)
throw new Error('Could not parse example in `' + ruleId + '`', {cause})
}
const exampleValue = strip(lines.join('\n').replace(/^\r?\n/g, ''))
const configuration = JSON.stringify({config: info.config || true})
const name = info.name
if (!info.label) {
result.checks.push({
configuration,
name,
positionless: info.positionless || false,
gfm: info.gfm || false,
input: exampleValue,
output: []
})
continue
}
assert(info.label === 'input' || info.label === 'output')
let found = result.checks.find(function (d) {
return d.configuration === configuration && d.name === name
})
if (!found) {
found = {
configuration,
name,
positionless: info.positionless || false,
gfm: info.gfm || false,
input: '',
output: []
}
result.checks.push(found)
}
if (info.label === 'input') {
found.input = exampleValue
} else {
found.output = exampleValue.split('\n')
}
}
plugins.push(result)
}
/**
* @param {string} name
* Preset name.
* @returns {Promise<undefined>}
* Nothing.
*/
async function addPreset(name) {
/** @type {{default: Preset}} */
const mod = await import(new URL(name + '/index.js', packagesUrl).href)
const plugins = mod.default.plugins
assert(plugins, 'expected plugins in preset')
/** @type {PresetInfo} */
const presetInfo = {name, plugins: []}
let index = -1
while (++index < plugins.length) {
const plugin = plugins[index]
/** @type {import('unified').Plugin<[unknown]>} */
let fn
/** @type {unknown} */
let option
if (Array.isArray(plugin)) {
;[fn, option] = /** @type {import('unified').PluginTuple<[unknown]>} */ (
plugin
)
} else {
assert(typeof plugin === 'function')
fn = plugin
}
// @ts-expect-error: `displayName`s are fine.
const name = /** @type {string} */ (fn.displayName || fn.name)
const pluginName = name
.replace(
/[:-](\w)/g,
function (/** @type {string} */ _, /** @type {string} */ $1) {
return $1.toUpperCase()
}
)
.replace(/[A-Z]/g, function (/** @type {string} */ $0) {
return '-' + $0.toLowerCase()
})
presetInfo.plugins.push([pluginName, option])
}
presets.push(presetInfo)
}

View File

@ -0,0 +1,85 @@
/**
* @typedef {import('mdast').List} List
* @typedef {import('mdast').ListItem} ListItem
* @typedef {import('mdast').Root} Root
*
* @typedef {import('type-fest').PackageJson} PackageJson
*/
import assert from 'node:assert/strict'
import fs from 'node:fs/promises'
import {zone} from 'mdast-zone'
import {packagesUrl, plugins} from '../info.js'
/**
* @type {Array<ListItem | undefined>}
* List items.
*/
const items = await Promise.all(
plugins.map(async function (info) {
const packageUrl = new URL(info.name + '/', packagesUrl)
/** @type {PackageJson} */
const pack = JSON.parse(
String(await fs.readFile(new URL('package.json', packageUrl)))
)
const description = String(pack.description || '').replace(
/^remark-lint rule to ?/i,
''
)
if (/^deprecated/i.test(description)) return
assert(pack.repository && typeof pack.repository === 'object')
assert(pack.repository.directory)
return {
type: 'listItem',
spread: false,
children: [
{
type: 'paragraph',
children: [
{
type: 'link',
url:
pack.repository.url + '/tree/main/' + pack.repository.directory,
children: [{type: 'inlineCode', value: info.name}]
},
{type: 'text', value: ' — ' + description}
]
}
]
}
})
)
/** @type {List} */
const list = {
type: 'list',
ordered: false,
spread: false,
// @ts-expect-error: filter is correct.
children: items.filter(Boolean)
}
/**
* List rules.
*
* @returns
* Transform.
*/
export default function remarkListOfRules() {
/**
* Transform.
*
* @param {Root} tree
* Tree.
* @returns {undefined}
* Nothing.
*/
return function (tree) {
zone(tree, 'rules', function (start, _, end) {
return [start, structuredClone(list), end]
})
}
}

View File

@ -2,65 +2,77 @@
* @typedef {import('mdast').List} List * @typedef {import('mdast').List} List
* @typedef {import('mdast').ListItem} ListItem * @typedef {import('mdast').ListItem} ListItem
* @typedef {import('mdast').Root} Root * @typedef {import('mdast').Root} Root
*
* @typedef {import('type-fest').PackageJson} PackageJson * @typedef {import('type-fest').PackageJson} PackageJson
*/ */
import fs from 'node:fs' import assert from 'node:assert/strict'
import path from 'node:path' import fs from 'node:fs/promises'
import process from 'node:process'
import {zone} from 'mdast-zone' import {zone} from 'mdast-zone'
import {presets} from '../util/presets.js' import {packagesUrl, presets} from '../info.js'
import {repoUrl} from '../util/repo-url.js'
const root = path.join(process.cwd(), 'packages') /**
* @type {Array<ListItem>}
* List items.
*/
const items = await Promise.all(
presets.map(async function (info) {
const packageUrl = new URL(info.name + '/', packagesUrl)
/** @type {PackageJson} */
const pack = JSON.parse(
await fs.readFile(new URL('package.json', packageUrl), 'utf8')
)
/** @type {import('unified').Plugin<Array<void>, Root>} */ const description = String(pack.description || '').replace(
export default function listOfPresets() { /^remark preset to configure remark-lint with ?/i,
const presetPromise = presets(root) ''
)
return async (tree) => { assert(pack.repository && typeof pack.repository === 'object')
const presetObjects = await presetPromise assert(pack.repository.directory)
zone(tree, 'presets', (start, _, end) => { return {
/** @type {List} */ type: 'listItem',
const list = { spread: false,
type: 'list', children: [
ordered: false, {
spread: false, type: 'paragraph',
children: presetObjects.map(({name}) => { children: [
/** @type {PackageJson} */ {
const pack = JSON.parse( type: 'link',
String(fs.readFileSync(path.join(root, name, 'package.json'))) url:
) pack.repository.url + '/tree/main/' + pack.repository.directory,
const description = String(pack.description || '').replace( children: [{type: 'inlineCode', value: info.name}]
/^remark preset to configure remark-lint with ?/i, },
'' {type: 'text', value: ' — ' + description}
) ]
}
]
}
})
)
/** @type {ListItem} */ /** @type {List} */
const item = { const list = {type: 'list', ordered: false, spread: false, children: items}
type: 'listItem',
spread: false,
children: [
{
type: 'paragraph',
children: [
{
type: 'link',
url: repoUrl(pack),
children: [{type: 'inlineCode', value: name}]
},
{type: 'text', value: ' — ' + description}
]
}
]
}
return item /**
}) * List presets.
} *
* @returns
return [start, list, end] * Transform.
*/
export default function remarkListOfPresets() {
/**
* Transform.
*
* @param {Root} tree
* Tree.
* @returns {undefined}
* Nothing.
*/
return function (tree) {
zone(tree, 'presets', function (start, _, end) {
return [start, structuredClone(list), end]
}) })
} }
} }

View File

@ -1,67 +0,0 @@
/**
* @typedef {import('type-fest').PackageJson} PackageJson
* @typedef {import('mdast').ListItem} ListItem
* @typedef {import('mdast').List} List
* @typedef {import('mdast').Root} Root
*/
import fs from 'node:fs'
import path from 'node:path'
import process from 'node:process'
import {zone} from 'mdast-zone'
import {rules} from '../util/rules.js'
import {repoUrl} from '../util/repo-url.js'
const root = path.join(process.cwd(), 'packages')
/** @type {import('unified').Plugin<Array<void>, Root>} */
export default function listOfRules() {
return (tree) => {
zone(tree, 'rules', (start, _, end) => {
/** @type {List} */
const list = {
type: 'list',
ordered: false,
spread: false,
children: rules(root)
.map((basename) => {
/** @type {PackageJson} */
const pack = JSON.parse(
String(fs.readFileSync(path.join(root, basename, 'package.json')))
)
const description = String(pack.description || '').replace(
/^remark-lint rule to ?/i,
''
)
const deprecated = /^deprecated/i.test(description)
/** @type {ListItem} */
const item = {
type: 'listItem',
spread: false,
children: deprecated
? []
: [
{
type: 'paragraph',
children: [
{
type: 'link',
url: repoUrl(pack),
children: [{type: 'inlineCode', value: basename}]
},
{type: 'text', value: ' — ' + description}
]
}
]
}
return item
})
.filter((d) => d.children.length > 0)
}
return [start, list, end]
})
}
}

View File

@ -1,76 +0,0 @@
/**
* @typedef {import('unified').Plugin} Plugin
* @typedef {import('unified').Preset} Preset
*/
import {promises as fs} from 'node:fs'
import path from 'node:path'
import url from 'node:url'
/**
* @param {string} base
* @returns {Promise<Array<{name: string, packages: Record<string, unknown>}>>}
*/
export async function presets(base) {
const allFiles = await fs.readdir(base)
const files = allFiles.filter((basename) =>
/remark-preset-lint/.test(basename)
)
return Promise.all(
files.map(async (name) => {
const href = url.pathToFileURL(path.join(base, name, 'index.js')).href
// type-coverage:ignore-next-line
const presetMod = await import(href)
/** @type {Preset} */
// type-coverage:ignore-next-line
const preset = presetMod.default
const plugins = preset.plugins || []
/** @type {Record<string, unknown>} */
const packages = {}
let index = -1
while (++index < plugins.length) {
const plugin = plugins[index]
/** @type {Plugin} */
let fn
/** @type {unknown} */
let option
if (typeof plugin === 'function') {
fn = plugin
} else if (Array.isArray(plugin)) {
// Fine:
// type-coverage:ignore-next-line
fn = plugin[0]
// Fine:
// type-coverage:ignore-next-line
option = plugin[1]
} else {
throw new TypeError(
'Expected plugin, plugin tuple, not `' + plugin + '`'
)
}
/** @type {string} */
// @ts-expect-error: `displayName`s are fine.
const name = fn.displayName || fn.name
packages[
name
.replace(
/[:-](\w)/g,
(/** @type {string} */ _, /** @type {string} */ $1) =>
$1.toUpperCase()
)
.replace(
/[A-Z]/g,
(/** @type {string} */ $0) => '-' + $0.toLowerCase()
)
] = option
}
return {name, packages}
})
)
}

View File

@ -1,38 +0,0 @@
import fs from 'node:fs'
/**
* @typedef {import('type-fest').PackageJson} PackageJson
*/
/**
* @param {string | PackageJson} pathOrJson
* @returns {string}
*/
export function repoUrl(pathOrJson) {
const pkg =
typeof pathOrJson === 'string' ? readPackageJson(pathOrJson) : pathOrJson
if (
pkg.repository === undefined ||
typeof pkg.repository !== 'object' ||
typeof pkg.repository.url !== 'string'
) {
throw new TypeError(
`Expected \`string\` for \`repository.url\` in \`${pathOrJson}\``
)
}
if (pkg.repository.directory) {
return pkg.repository.url + `/tree/main/${pkg.repository.directory}`
}
return pkg.repository.url
}
/**
* @param {string} filePath
* @returns {PackageJson}
*/
function readPackageJson(filePath) {
return JSON.parse(String(fs.readFileSync(filePath)))
}

View File

@ -1,140 +0,0 @@
/**
* @typedef Rule
* @property {string} ruleId
* @property {string} description
* @property {string | undefined} summary
* @property {boolean} deprecated
* @property {Record<string, Checks>} tests
* @property {string} filePath
*
* @typedef {Record<string, Check>} Checks
*
* @typedef Check
* @property {string} input
* @property {Array<string>} output
* @property {boolean} gfm
* @property {boolean} positionless
*/
import fs from 'node:fs'
import path from 'node:path'
import {parse} from 'comment-parser'
import strip from 'strip-indent'
/**
* Get information for a rule at `filePath`.
*
* @param {string} filePath
* @returns {Rule}
*/
export function rule(filePath) {
const ruleId = path.basename(filePath).slice('remark-lint-'.length)
/** @type {Record<string, Checks>} */
const tests = {}
const code = fs.readFileSync(path.join(filePath, 'index.js'), 'utf8')
// Note: To do: `comment-parser` types are wrong.
/** @type {import('comment-parser/primitives').Block} */
const fileInfo = parse(code, {spacing: 'preserve'})[0]
const tags = fileInfo.tags
const moduleTag = tags.find((d) => d.tag === 'module')
const summaryTag = tags.find((d) => d.tag === 'summary')
const deprecatedTag = tags.find((d) => d.tag === 'deprecated')
/* c8 ignore next 3 */
if (!moduleTag) {
throw new Error('Expected `@module` in JSDoc')
}
const name = moduleTag.name
let description =
(deprecatedTag && deprecatedTag.description) || fileInfo.description
/* c8 ignore next 3 */
if (name !== ruleId) {
throw new Error(ruleId + ' has an incorrect `@module`: ' + name)
}
/* c8 ignore next 3 */
if (!description) {
throw new Error(ruleId + ' is missing a description or `@deprecated`')
}
description = strip(description)
/** @type {Rule} */
const result = {
ruleId,
description: description.trim(),
summary: summaryTag ? strip(summaryTag.description).trim() : undefined,
deprecated: Boolean(deprecatedTag),
tests,
filePath
}
const examples = tags
.filter((d) => d.tag === 'example')
.map((d) => d.description.replace(/^\r?\n|\r?\n$/g, ''))
let index = -1
while (++index < examples.length) {
const lines = examples[index].split('\n')
/** @type {{name: string, label?: 'input' | 'output', config?: unknown, positionless?: boolean, gfm?: boolean}} */
let info
try {
info = JSON.parse(lines[0])
lines.splice(0, 1)
/* c8 ignore next 6 */
} catch (error) {
const exception = /** @type Error */ (error)
throw new Error(
'Could not parse example in ' + ruleId + ':\n' + exception.stack
)
}
const exampleValue = strip(lines.join('\n').replace(/^\r?\n/g, ''))
const configuration = JSON.stringify({config: info.config || true})
const name = info.name
const context =
configuration in tests
? tests[configuration]
: (tests[configuration] = {})
if (!info.label) {
context[name] = {
positionless: info.positionless || false,
gfm: info.gfm || false,
input: exampleValue,
output: []
}
continue
}
/* c8 ignore next 9 */
if (info.label !== 'input' && info.label !== 'output') {
throw new Error(
'Expected `input` or `ouput` for `label` in ' +
ruleId +
', not `' +
info.label +
'`'
)
}
if (!context[name]) {
context[name] = {
positionless: info.positionless || false,
gfm: info.gfm || false,
input: '',
output: []
}
}
// @ts-expect-error: fine: array for output, string for rest.
context[name][info.label] =
info.label === 'output' ? exampleValue.split('\n') : exampleValue
}
return result
}

View File

@ -1,13 +0,0 @@
import fs from 'node:fs'
/**
* @param {string} filePath
* @returns {Array<string>}
*/
export function rules(filePath) {
return fs
.readdirSync(filePath)
.filter(
(basename) => /remark-lint/.test(basename) && basename !== 'remark-lint'
)
}

590
test.js
View File

@ -1,32 +1,33 @@
/** /**
* @typedef {import('unified').Plugin} Plugin * @typedef {import('unified').PluggableList} PluggableList
* @typedef {import('vfile-message').VFileMessage} VFileMessage * @typedef {import('unified').Plugin<[unknown]>} Plugin
* @typedef {import('./script/util/rule.js').Check} Check *
* @typedef {import('./script/util/rule.js').Rule} Rule * @typedef {import('./script/info.js').Check} Check
* @typedef {import('./script/info.js').PluginInfo} PluginInfo
*/ */
import assert from 'node:assert/strict' import assert from 'node:assert/strict'
import path from 'node:path'
import process from 'node:process'
import test from 'node:test' import test from 'node:test'
import url from 'node:url'
import {toVFile} from 'to-vfile'
import {removePosition} from 'unist-util-remove-position'
import {remark} from 'remark' import {remark} from 'remark'
import remarkGfm from 'remark-gfm' import remarkGfm from 'remark-gfm'
import remarkLint from 'remark-lint'
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 {lintRule} from 'unified-lint-rule' import {lintRule} from 'unified-lint-rule'
import {rules} from './script/util/rules.js' import {removePosition} from 'unist-util-remove-position'
import {rule} from './script/util/rule.js' import {VFile} from 'vfile'
import {characters} from './script/characters.js' import {characters} from './script/characters.js'
import lint from './packages/remark-lint/index.js' import {plugins} from './script/info.js'
import noHeadingPunctuation from './packages/remark-lint-no-heading-punctuation/index.js'
import noMultipleToplevelHeadings from './packages/remark-lint-no-multiple-toplevel-headings/index.js'
import noUndefinedReferences from './packages/remark-lint-no-undefined-references/index.js'
import finalNewline from './packages/remark-lint-final-newline/index.js'
const own = {}.hasOwnProperty test('remark-lint', async function (t) {
await t.test('should expose the public api', async function () {
assert.deepEqual(Object.keys(await import('remark-lint')).sort(), [
'default'
])
})
test('core', async () => {
const doc = [ const doc = [
'# A heading', '# A heading',
'', '',
@ -37,53 +38,48 @@ test('core', async () => {
'# Another main heading.' '# Another main heading.'
].join('\n') ].join('\n')
let file = await remark() await t.test('should support `remark-lint` last', async function () {
.use(noHeadingPunctuation) const file = await remark()
.use(noMultipleToplevelHeadings) .use(remarkLintNoHeadingPunctuation)
.use(lint) .use(remarkLintNoMultipleToplevelHeadings)
.process(toVFile({path: 'virtual.md', value: doc})) .use(remarkLint)
.process({path: 'virtual.md', value: doc})
assert.deepEqual( assert.deepEqual(file.messages.map(String), [
asStrings(file.messages),
[
'virtual.md:3:1-3:24: Dont add a trailing `.` to headings', 'virtual.md:3:1-3:24: Dont add a trailing `.` to headings',
'virtual.md:3:1-3:24: Dont use multiple top level headings (1:1)' 'virtual.md:3:1-3:24: Dont use multiple top level headings (1:1)'
], ])
'should support `remark-lint` last' })
)
file = await remark() await t.test('should support `remark-lint` first', async function () {
.use(lint) const file = await remark()
.use(noHeadingPunctuation) .use(remarkLint)
.use(noMultipleToplevelHeadings) .use(remarkLintNoHeadingPunctuation)
.process(toVFile({path: 'virtual.md', value: doc})) .use(remarkLintNoMultipleToplevelHeadings)
.process({path: 'virtual.md', value: doc})
assert.deepEqual( assert.deepEqual(file.messages.map(String), [
asStrings(file.messages),
[
'virtual.md:3:1-3:24: Dont add a trailing `.` to headings', 'virtual.md:3:1-3:24: Dont add a trailing `.` to headings',
'virtual.md:3:1-3:24: Dont use multiple top level headings (1:1)' 'virtual.md:3:1-3:24: Dont use multiple top level headings (1:1)'
], ])
'should support `remark-lint` first' })
)
file = await remark().use(lint).process('.') await t.test('should support no rules', async function () {
const file = await remark().use(remarkLint).process('.')
assert.deepEqual(asStrings(file.messages), [], 'should support no rules') assert.deepEqual(file.messages, [])
})
file = await remark().use(finalNewline).process('') await t.test('should support successful rules', async function () {
const file = await remark().use(remarkLintFinalNewline).process('')
assert.deepEqual( assert.deepEqual(file.messages, [])
asStrings(file.messages), })
[],
'should support successful rules'
)
file = await remark().use(finalNewline, [2]).process('.') await t.test('should support a list with a severity', async function () {
const file = await remark().use(remarkLintFinalNewline, [2]).process('.')
assert.deepEqual( assert.deepEqual(file.messages.map(jsonClone), [
file.messages.map((d) => JSON.parse(JSON.stringify(d))),
[
{ {
fatal: true, fatal: true,
message: 'Missing newline character at end of file', message: 'Missing newline character at end of file',
@ -93,315 +89,269 @@ test('core', async () => {
source: 'remark-lint', source: 'remark-lint',
url: 'https://github.com/remarkjs/remark-lint/tree/main/packages/remark-lint-final-newline#readme' url: 'https://github.com/remarkjs/remark-lint/tree/main/packages/remark-lint-final-newline#readme'
} }
], ])
'should support a list with a severity' })
await t.test('should support a boolean (`true`)', async function () {
const file = await remark().use(remarkLintFinalNewline, true).process('.')
assert.deepEqual(file.messages.map(String), [
'1:1: Missing newline character at end of file'
])
})
await t.test('should support a boolean (`false`)', async function () {
const file = await remark().use(remarkLintFinalNewline, false).process('.')
assert.deepEqual(file.messages, [])
})
await t.test(
'should support a list with a boolean severity (true, for on)',
async function () {
const file = await remark()
.use(remarkLintFinalNewline, [true])
.process('.')
assert.deepEqual(file.messages.map(String), [
'1:1: Missing newline character at end of file'
])
}
) )
file = await remark().use(finalNewline, true).process('.') await t.test(
'should support a list with boolean severity (false, for off)',
async function () {
const file = await remark()
.use(remarkLintFinalNewline, [false])
.process('.')
assert.deepEqual( assert.deepEqual(file.messages, [])
asStrings(file.messages), }
['1:1: Missing newline character at end of file'],
'should support a boolean (`true`)'
) )
file = await remark().use(finalNewline, false).process('.') await t.test(
'should support a list with string severity (`error`)',
async function () {
const file = await remark()
.use(remarkLintFinalNewline, ['error'])
.process('.')
assert.deepEqual( assert.deepEqual(file.messages.map(jsonClone), [
asStrings(file.messages), {
[], fatal: true,
'should support a boolean (`false`)' message: 'Missing newline character at end of file',
name: '1:1',
reason: 'Missing newline character at end of file',
ruleId: 'final-newline',
source: 'remark-lint',
url: 'https://github.com/remarkjs/remark-lint/tree/main/packages/remark-lint-final-newline#readme'
}
])
}
) )
file = await remark().use(finalNewline, [true]).process('.') await t.test(
'should support a list with string severity (`on`)',
async function () {
const file = await remark()
.use(remarkLintFinalNewline, ['on'])
.process('.')
assert.deepEqual( assert.deepEqual(file.messages.map(jsonClone), [
asStrings(file.messages), {
['1:1: Missing newline character at end of file'], fatal: false,
'should support a list with a boolean severity (true, for on)' message: 'Missing newline character at end of file',
name: '1:1',
reason: 'Missing newline character at end of file',
ruleId: 'final-newline',
source: 'remark-lint',
url: 'https://github.com/remarkjs/remark-lint/tree/main/packages/remark-lint-final-newline#readme'
}
])
}
) )
file = await remark().use(finalNewline, [false]).process('.') await t.test(
'should support a list with string severity (`warn`)',
async function () {
const file = await remark()
.use(remarkLintFinalNewline, ['warn'])
.process('.')
assert.deepEqual( assert.deepEqual(file.messages.map(jsonClone), [
asStrings(file.messages), {
[], fatal: false,
'should support a list with boolean severity (false, for off)' message: 'Missing newline character at end of file',
name: '1:1',
reason: 'Missing newline character at end of file',
ruleId: 'final-newline',
source: 'remark-lint',
url: 'https://github.com/remarkjs/remark-lint/tree/main/packages/remark-lint-final-newline#readme'
}
])
}
) )
file = await remark().use(finalNewline, ['error']).process('.') await t.test(
'should support a list with string severity (`off`)',
async function () {
const file = await remark()
.use(remarkLintFinalNewline, ['off'])
.process('.')
assert.deepEqual( assert.deepEqual(file.messages, [])
file.messages.map((d) => JSON.parse(JSON.stringify(d))), }
[
{
fatal: true,
message: 'Missing newline character at end of file',
name: '1:1',
reason: 'Missing newline character at end of file',
ruleId: 'final-newline',
source: 'remark-lint',
url: 'https://github.com/remarkjs/remark-lint/tree/main/packages/remark-lint-final-newline#readme'
}
],
'should support a list with string severity (`error`)'
) )
file = await remark().use(finalNewline, ['on']).process('.') await t.test(
'should fail on incorrect severities (too high)',
assert.deepEqual( async function () {
file.messages.map((d) => JSON.parse(JSON.stringify(d))), assert.throws(function () {
[ remark().use(remarkLintFinalNewline, [3]).freeze()
{ }, /^Error: Incorrect severity `3` for `final-newline`, expected 0, 1, or 2$/)
fatal: false, }
message: 'Missing newline character at end of file',
name: '1:1',
reason: 'Missing newline character at end of file',
ruleId: 'final-newline',
source: 'remark-lint',
url: 'https://github.com/remarkjs/remark-lint/tree/main/packages/remark-lint-final-newline#readme'
}
],
'should support a list with string severity (`on`)'
) )
file = await remark().use(finalNewline, ['warn']).process('.') await t.test(
'should fail on incorrect severities (too low)',
assert.deepEqual( async function () {
file.messages.map((d) => JSON.parse(JSON.stringify(d))), assert.throws(function () {
[ remark().use(remarkLintFinalNewline, [-1]).freeze()
{ }, /^Error: Incorrect severity `-1` for `final-newline`, expected 0, 1, or 2$/)
fatal: false, }
message: 'Missing newline character at end of file',
name: '1:1',
reason: 'Missing newline character at end of file',
ruleId: 'final-newline',
source: 'remark-lint',
url: 'https://github.com/remarkjs/remark-lint/tree/main/packages/remark-lint-final-newline#readme'
}
],
'should support a list with string severity (`warn`)'
) )
file = await remark().use(finalNewline, ['off']).process('.') await t.test(
'should support regex as options (remark-lint-no-undefined-references)',
async function () {
const file = await remark()
.use(remarkLintNoUndefinedReferences, {allow: [/^b\./i]})
.process({
path: 'virtual.md',
value: ['[foo][b.c]', '', '[bar][b]'].join('\n')
})
assert.deepEqual( assert.deepEqual(file.messages.map(String), [
asStrings(file.messages), 'virtual.md:3:1-3:9: Found reference to undefined definition'
[], ])
'should support a list with string severity (`off`)' }
) )
assert.throws( await t.test(
() => { 'should support meta as a string (unified-lint-rule)',
remark().use(finalNewline, [3]).freeze() async function () {
}, const file = await remark()
/^Error: Incorrect severity `3` for `final-newline`, expected 0, 1, or 2$/, .use(
'should fail on incorrect severities (too high)' lintRule('test:rule', function (_, file) {
) file.message('Test message')
}),
['warn']
)
.process('.')
assert.throws( assert.deepEqual(file.messages.map(jsonClone), [
() => { {
remark().use(finalNewline, [-1]).freeze() fatal: false,
}, message: 'Test message',
/^Error: Incorrect severity `-1` for `final-newline`, expected 0, 1, or 2$/, name: '1:1',
'should fail on incorrect severities (too low)' reason: 'Test message',
) ruleId: 'rule',
source: 'test'
file = await remark() }
.use(noUndefinedReferences, {allow: [/^b\./i]}) ])
.process( }
toVFile({
path: 'virtual.md',
value: ['[foo][b.c]', '', '[bar][b]'].join('\n')
})
)
assert.deepEqual(
asStrings(file.messages),
['virtual.md:3:1-3:9: Found reference to undefined definition'],
'no-undefined-references allow option should work with native regex'
)
file = await remark()
.use(
lintRule('test:rule', (tree, file) => {
file.message('Test message')
}),
['warn']
)
.process('.')
assert.deepEqual(
file.messages.map((d) => JSON.parse(JSON.stringify(d))),
[
{
fatal: false,
message: 'Test message',
name: '1:1',
reason: 'Test message',
ruleId: 'rule',
source: 'test'
}
],
'should support string meta'
) )
}) })
test('rules', async (t) => { test('plugins', async function (t) {
const root = path.join(process.cwd(), 'packages') for (const plugin of plugins) {
const all = rules(root) await t.test(plugin.name, async function () {
let index = -1 await assertPlugin(plugin)
})
}
})
while (++index < all.length) { /**
const basename = all[index] * @param {PluginInfo} info
const base = path.resolve(root, basename) * Info.
const info = rule(base) * @returns {Promise<undefined>}
const href = url.pathToFileURL(base).href + '/index.js' * Nothing.
*/
async function assertPlugin(info) {
/** @type {{default: Plugin}} */
const pluginMod = await import(info.name)
const plugin = pluginMod.default
/** @type {{default: Plugin}} */ for (const check of info.checks) {
const pluginMod = await import(href) await assertCheck(plugin, info, check)
const fn = pluginMod.default }
}
if (Object.keys(info.tests).length === 0) { /**
assert.ok(true, info.ruleId + ': no tests') * @param {Plugin} plugin
} else { * Plugin.
await t.test(info.ruleId, async () => { * @param {PluginInfo} info
const tests = info.tests * info.
/** @type {string} */ * @param {Check} check
let configuration * Check.
* @returns {Promise<undefined>}
* Nothing.
*/
async function assertCheck(plugin, info, check) {
/** @type {{config: unknown}} */
const {config} = JSON.parse(check.configuration)
/** @type {PluggableList} */
const extras = check.gfm ? [remarkGfm] : []
let value = check.input
for (configuration in tests) { for (const character of characters) {
if (own.call(tests, configuration)) { value = value.replace(character.in, character.out)
const checks = tests[configuration] }
/** @type {{config: unknown}} */
const {config} = JSON.parse(configuration)
/** @type {string} */ const file = await remark()
let name .use(plugin, config)
.use(extras)
.process(new VFile({path: check.name, value}))
for (name in checks) { for (const message of file.messages) {
if (own.call(checks, name)) { assert.equal(message.ruleId, info.ruleId)
const basename = name assert.equal(
const check = checks[name] message.url,
'https://github.com/remarkjs/remark-lint/tree/main/packages/remark-lint-' +
info.ruleId +
'#readme'
)
}
await assertFixture(fn, info, check, basename, config) assert.deepEqual(
} file.messages.map(String).map(function (value) {
} return value.slice(value.indexOf(':') + 1)
} }),
check.output
)
if (!check.positionless) {
const file = await remark()
.use(function () {
return function (tree) {
removePosition(tree)
} }
}) })
} .use(plugin, config)
} .use(extras)
}) .process(new VFile({path: check.name, value}))
/** assert.deepEqual(file.messages, [])
* @param {Plugin} rule
* @param {Rule} info
* @param {Check} fixture
* @param {string} basename
* @param {unknown} config
*/
/* eslint-disable-next-line max-params */
function assertFixture(rule, info, fixture, basename, config) {
const ruleId = info.ruleId
const file = toVFile(basename)
const expected = fixture.output
const positionless = fixture.positionless
// @ts-expect-error: to do: fix types.
let proc = remark().use(rule, config)
if (fixture.gfm) proc.use(remarkGfm)
file.value = preprocess(fixture.input || '')
try {
proc.runSync(proc.parse(file), file)
} catch (error) {
const exception = /** @type VFileMessage */ (error)
if (exception && exception.source !== 'remark-lint') {
throw exception
}
}
let index = -1
while (++index < file.messages.length) {
const message = file.messages[index]
if (message.ruleId !== ruleId) {
throw new Error(
'Expected `' +
ruleId +
'`, not `' +
message.ruleId +
'` as `ruleId` for ' +
message
)
}
const expectedUrl =
'https://github.com/remarkjs/remark-lint/tree/main/packages/remark-lint-' +
ruleId +
'#readme'
if (message.url !== expectedUrl) {
throw new Error(
'Expected `' +
expectedUrl +
'`, not `' +
message.url +
'` as `ruleId` for ' +
message
)
}
}
assert.deepEqual(
normalize(file.messages),
expected,
'should equal with position'
)
if (!positionless) {
file.messages = []
proc = remark()
.use(() => (tree) => removePosition(tree))
// @ts-expect-error: to do: fix types.
.use(rule, config)
if (fixture.gfm) proc.use(remarkGfm)
proc.processSync(file)
assert.deepEqual(
normalize(file.messages),
[],
'should equal without position'
)
} }
} }
/** /**
* @param {Array<VFileMessage>} messages * @param {unknown} d
* @returns {Array<string>} * Value.
* @returns {unknown}
* Cloned value.
*/ */
function normalize(messages) { function jsonClone(d) {
return asStrings(messages).map((value) => value.slice(value.indexOf(':') + 1)) return JSON.parse(JSON.stringify(d))
}
/**
* @param {Array<VFileMessage>} messages
* @returns {Array<string>}
*/
function asStrings(messages) {
return messages.map(String)
}
/**
* @param {string} value
* @returns {string}
*/
function preprocess(value) {
let index = -1
while (++index < characters.length) {
value = value.replace(characters[index].in, characters[index].out)
}
return value
} }

View File

@ -6,7 +6,7 @@
"declarationMap": true, "declarationMap": true,
"emitDeclarationOnly": true, "emitDeclarationOnly": true,
"exactOptionalPropertyTypes": true, "exactOptionalPropertyTypes": true,
"lib": ["es2020"], "lib": ["es2022"],
"module": "node16", "module": "node16",
"strict": true, "strict": true,
"target": "es2020" "target": "es2020"