Add JSDoc based types

This commit is contained in:
Titus Wormer 2021-08-12 14:01:59 +02:00
parent 5c215ec00b
commit 1a1e7cc586
No known key found for this signature in database
GPG Key ID: E6E581152ED04E2E
296 changed files with 4656 additions and 1506 deletions

74
.gitignore vendored
View File

@ -1,6 +1,80 @@
coverage/
node_modules/
.DS_Store
/packages/remark-lint/*.d.ts
/packages/remark-lint-blockquote-indentation/*.d.ts
/packages/remark-lint-checkbox-character-style/*.d.ts
/packages/remark-lint-checkbox-content-indent/*.d.ts
/packages/remark-lint-code-block-style/*.d.ts
/packages/remark-lint-definition-case/*.d.ts
/packages/remark-lint-definition-spacing/*.d.ts
/packages/remark-lint-emphasis-marker/*.d.ts
/packages/remark-lint-fenced-code-flag/*.d.ts
/packages/remark-lint-fenced-code-marker/*.d.ts
/packages/remark-lint-file-extension/*.d.ts
/packages/remark-lint-final-definition/*.d.ts
/packages/remark-lint-final-newline/*.d.ts
/packages/remark-lint-first-heading-level/*.d.ts
/packages/remark-lint-hard-break-spaces/*.d.ts
/packages/remark-lint-heading-increment/*.d.ts
/packages/remark-lint-heading-style/*.d.ts
/packages/remark-lint-linebreak-style/*.d.ts
/packages/remark-lint-link-title-style/*.d.ts
/packages/remark-lint-list-item-bullet-indent/*.d.ts
/packages/remark-lint-list-item-content-indent/*.d.ts
/packages/remark-lint-list-item-indent/*.d.ts
/packages/remark-lint-list-item-spacing/*.d.ts
/packages/remark-lint-maximum-heading-length/*.d.ts
/packages/remark-lint-maximum-line-length/*.d.ts
/packages/remark-lint-no-auto-link-without-protocol/*.d.ts
/packages/remark-lint-no-blockquote-without-marker/*.d.ts
/packages/remark-lint-no-consecutive-blank-lines/*.d.ts
/packages/remark-lint-no-duplicate-defined-urls/*.d.ts
/packages/remark-lint-no-duplicate-definitions/*.d.ts
/packages/remark-lint-no-duplicate-headings/*.d.ts
/packages/remark-lint-no-duplicate-headings-in-section/*.d.ts
/packages/remark-lint-no-emphasis-as-heading/*.d.ts
/packages/remark-lint-no-empty-url/*.d.ts
/packages/remark-lint-no-file-name-articles/*.d.ts
/packages/remark-lint-no-file-name-consecutive-dashes/*.d.ts
/packages/remark-lint-no-file-name-irregular-characters/*.d.ts
/packages/remark-lint-no-file-name-mixed-case/*.d.ts
/packages/remark-lint-no-file-name-outer-dashes/*.d.ts
/packages/remark-lint-no-heading-content-indent/*.d.ts
/packages/remark-lint-no-heading-indent/*.d.ts
/packages/remark-lint-no-heading-like-paragraph/*.d.ts
/packages/remark-lint-no-heading-punctuation/*.d.ts
/packages/remark-lint-no-html/*.d.ts
/packages/remark-lint-no-inline-padding/*.d.ts
/packages/remark-lint-no-literal-urls/*.d.ts
/packages/remark-lint-no-missing-blank-lines/*.d.ts
/packages/remark-lint-no-multiple-toplevel-headings/*.d.ts
/packages/remark-lint-no-paragraph-content-indent/*.d.ts
/packages/remark-lint-no-reference-like-url/*.d.ts
/packages/remark-lint-no-shell-dollars/*.d.ts
/packages/remark-lint-no-shortcut-reference-image/*.d.ts
/packages/remark-lint-no-shortcut-reference-link/*.d.ts
/packages/remark-lint-no-table-indentation/*.d.ts
/packages/remark-lint-no-tabs/*.d.ts
/packages/remark-lint-no-undefined-references/*.d.ts
/packages/remark-lint-no-unneeded-full-reference-image/*.d.ts
/packages/remark-lint-no-unneeded-full-reference-link/*.d.ts
/packages/remark-lint-no-unused-definitions/*.d.ts
/packages/remark-lint-ordered-list-marker-style/*.d.ts
/packages/remark-lint-ordered-list-marker-value/*.d.ts
/packages/remark-lint-rule-style/*.d.ts
/packages/remark-lint-strikethrough-marker/*.d.ts
/packages/remark-lint-strong-marker/*.d.ts
/packages/remark-lint-table-cell-padding/*.d.ts
/packages/remark-lint-table-pipe-alignment/*.d.ts
/packages/remark-lint-table-pipes/*.d.ts
/packages/remark-lint-unordered-list-marker-style/*.d.ts
/packages/remark-preset-lint-consistent/*.d.ts
/packages/remark-preset-lint-markdown-style-guide/*.d.ts
/packages/remark-preset-lint-recommended/*.d.ts
/packages/unified-lint-rule/lib/*.d.ts
/script/**/*.d.ts
/*.d.ts
*.log
package-lock.json
yarn.lock

View File

@ -33,6 +33,10 @@
],
"type": "module",
"devDependencies": {
"@types/mdast": "^3.0.0",
"@types/parse-author": "^2.0.0",
"@types/pluralize": "^0.0.29",
"@types/tape": "^4.0.0",
"c8": "^7.0.0",
"comment-parser": "^1.0.0",
"lerna": "^4.0.0",
@ -46,22 +50,27 @@
"remark-github": "^11.0.0",
"remark-toc": "^8.0.0",
"remark-validate-links": "^11.0.0",
"rimraf": "^3.0.0",
"strip-indent": "^4.0.0",
"tape": "^5.0.0",
"to-vfile": "^7.0.0",
"type-coverage": "^2.0.0",
"type-fest": "^2.0.0",
"typescript": "^4.0.0",
"unist-builder": "^3.0.0",
"unist-util-remove-position": "^4.0.0",
"xo": "^0.39.0"
},
"scripts": {
"postinstall": "lerna bootstrap --no-ci",
"build-packages": "node script/build-presets && node script/build-rules",
"build-workspace": "lerna run build",
"build-monorepo": "rimraf \"test.d.ts\" \"script/**/*.d.ts\" && tsc && type-coverage",
"build": "npm run build-packages && npm run build-workspace && npm run build-monorepo",
"format": "remark . -qfo && prettier . -w --loglevel warn && xo --fix",
"generate:presets": "node script/build-presets",
"generate:rules": "node script/build-rules",
"generate": "npm run generate:presets && npm run generate:rules",
"test-api": "node --conditions development test.js",
"test-coverage": "c8 --check-coverage --branches 100 --functions 100 --lines 100 --statements 100 --reporter lcov npm run test-api",
"test": "npm run generate && npm run format && npm run test-coverage"
"test": "npm run build && npm run format && npm run test-coverage"
},
"prettier": {
"tabWidth": 2,
@ -88,5 +97,11 @@
}
}
]
},
"typeCoverage": {
"atLeast": 100,
"detail": true,
"strict": true,
"ignoreCatch": true
}
}

View File

@ -48,6 +48,12 @@
* 9:3: Add 1 space between block quote and content
*/
/**
* @typedef {import('mdast').Root} Root
* @typedef {import('mdast').Blockquote} Blockquote
* @typedef {'consistent'|number} Options
*/
import {lintRule} from 'unified-lint-rule'
import plural from 'pluralize'
import {visit} from 'unist-util-visit'
@ -56,41 +62,42 @@ import {generated} from 'unist-util-generated'
const remarkLintBlockquoteIndentation = lintRule(
'remark-lint:blockquote-indentation',
blockquoteIndentation
/** @type {import('unified-lint-rule').Rule<Root, Options>} */
(tree, file, option = 'consistent') => {
visit(tree, 'blockquote', (node) => {
if (generated(node) || node.children.length === 0) {
return
}
if (option === 'consistent') {
option = check(node)
} else {
const diff = option - check(node)
if (diff !== 0) {
const abs = Math.abs(diff)
file.message(
(diff > 0 ? 'Add' : 'Remove') +
' ' +
abs +
' ' +
plural('space', abs) +
' between block quote and content',
pointStart(node.children[0])
)
}
}
})
}
)
export default remarkLintBlockquoteIndentation
function blockquoteIndentation(tree, file, option) {
let preferred = typeof option === 'number' ? option : null
visit(tree, 'blockquote', (node) => {
if (generated(node) || node.children.length === 0) {
return
}
if (preferred) {
const diff = preferred - check(node)
if (diff !== 0) {
const abs = Math.abs(diff)
file.message(
(diff > 0 ? 'Add' : 'Remove') +
' ' +
abs +
' ' +
plural('space', abs) +
' between block quote and content',
pointStart(node.children[0])
)
}
} else {
preferred = check(node)
}
})
}
/**
* @param {Blockquote} node
* @returns {number}
*/
function check(node) {
return pointStart(node.children[0]).column - pointStart(node).column
}

View File

@ -25,15 +25,28 @@
"sideEffects": false,
"type": "module",
"main": "index.js",
"types": "index.d.ts",
"files": [
"index.d.ts",
"index.js"
],
"dependencies": {
"@types/mdast": "^3.0.0",
"pluralize": "^8.0.0",
"unified": "^10.0.0",
"unified-lint-rule": "^1.0.0",
"unist-util-generated": "^2.0.0",
"unist-util-position": "^4.0.0",
"unist-util-visit": "^4.0.0"
},
"xo": false
"scripts": {
"build": "rimraf \"*.d.ts\" && tsc && type-coverage"
},
"xo": false,
"typeCoverage": {
"atLeast": 100,
"detail": true,
"strict": true,
"ignoreCatch": true
}
}

View File

@ -89,12 +89,18 @@ Paragraph.
## Install
This package is [ESM only][esm]:
Node 12+ is needed to use it and it must be `imported`ed instead of `required`d.
[npm][]:
```sh
npm install remark-lint-blockquote-indentation
```
This package exports no identifiers.
The default export is `remarkLintBlockquoteIndentation`.
## Use
You probably want to use it on the CLI through a config file:
@ -175,6 +181,8 @@ abide by its terms.
[chat]: https://github.com/remarkjs/remark/discussions
[esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c
[npm]: https://docs.npmjs.com/cli/install
[health]: https://github.com/remarkjs/.github

View File

@ -0,0 +1,4 @@
{
"extends": "../../tsconfig.json",
"include": ["*.js"]
}

View File

@ -77,49 +77,65 @@
* 1:1: Incorrect checked checkbox marker `💩`: use either `'x'`, or `'X'`
*/
/**
* @typedef {import('mdast').Root} Root
*
* @typedef Styles
* @property {'x'|'X'|'consistent'} [checked='consistent']
* @property {' '|'\x09'|'consistent'} [unchecked='consistent']
*
* @typedef {'consistent'|Styles} Options
*/
import {lintRule} from 'unified-lint-rule'
import {visit} from 'unist-util-visit'
import {pointStart} from 'unist-util-position'
import {generated} from 'unist-util-generated'
const checked = {x: true, X: true}
const unchecked = {' ': true, '\t': true}
const types = {true: 'checked', false: 'unchecked'}
const remarkLintCheckboxCharacterStyle = lintRule(
'remark-lint:checkbox-character-style',
(tree, file, option) => {
/** @type {import('unified-lint-rule').Rule<Root, Options>} */
(tree, file, option = 'consistent') => {
const value = String(file)
const preferred = typeof option === 'object' ? option : {}
/** @type {'x'|'X'|'consistent'} */
let checked = 'consistent'
/** @type {' '|'\x09'|'consistent'} */
let unchecked = 'consistent'
if (preferred.unchecked && unchecked[preferred.unchecked] !== true) {
if (typeof option === 'object') {
checked = option.checked || 'consistent'
unchecked = option.unchecked || 'consistent'
}
if (unchecked !== 'consistent' && unchecked !== ' ' && unchecked !== '\t') {
file.fail(
'Incorrect unchecked checkbox marker `' +
preferred.unchecked +
unchecked +
"`: use either `'\\t'`, or `' '`"
)
}
if (preferred.checked && checked[preferred.checked] !== true) {
if (checked !== 'consistent' && checked !== 'x' && checked !== 'X') {
file.fail(
'Incorrect checked checkbox marker `' +
preferred.checked +
checked +
"`: use either `'x'`, or `'X'`"
)
}
visit(tree, 'listItem', (node) => {
const head = node.children[0]
const point = pointStart(head)
// Exit early for items without checkbox.
// A list item cannot be checked and empty, according to GFM.
if (typeof node.checked !== 'boolean' || generated(node) || !head) {
if (
typeof node.checked !== 'boolean' ||
!head ||
typeof point.offset !== 'number'
) {
return
}
const type = types[node.checked]
const point = pointStart(head)
// Move back to before `] `.
point.offset -= 2
point.column -= 2
@ -133,21 +149,24 @@ const remarkLintCheckboxCharacterStyle = lintRule(
/* c8 ignore next */
if (!match) return
const style = preferred[type]
const style = node.checked ? checked : unchecked
if (style) {
if (match[1] !== style) {
file.message(
type.charAt(0).toUpperCase() +
type.slice(1) +
' checkboxes should use `' +
style +
'` as a marker',
point
)
if (style === 'consistent') {
if (node.checked) {
// @ts-expect-error: valid marker.
checked = match[1]
} else {
// @ts-expect-error: valid marker.
unchecked = match[1]
}
} else {
preferred[type] = match[1]
} else if (match[1] !== style) {
file.message(
(node.checked ? 'Checked' : 'Unchecked') +
' checkboxes should use `' +
style +
'` as a marker',
point
)
}
})
}

View File

@ -26,14 +26,26 @@
"sideEffects": false,
"type": "module",
"main": "index.js",
"types": "index.d.ts",
"files": [
"index.d.ts",
"index.js"
],
"dependencies": {
"@types/mdast": "^3.0.0",
"unified": "^10.0.0",
"unified-lint-rule": "^1.0.0",
"unist-util-generated": "^2.0.0",
"unist-util-position": "^4.0.0",
"unist-util-visit": "^4.0.0"
},
"xo": false
"scripts": {
"build": "rimraf \"*.d.ts\" && tsc && type-coverage"
},
"xo": false,
"typeCoverage": {
"atLeast": 100,
"detail": true,
"strict": true,
"ignoreCatch": true
}
}

View File

@ -160,12 +160,18 @@ When configured with `{ checked: '💩' }`.
## Install
This package is [ESM only][esm]:
Node 12+ is needed to use it and it must be `imported`ed instead of `required`d.
[npm][]:
```sh
npm install remark-lint-checkbox-character-style
```
This package exports no identifiers.
The default export is `remarkLintCheckboxCharacterStyle`.
## Use
You probably want to use it on the CLI through a config file:
@ -246,6 +252,8 @@ abide by its terms.
[chat]: https://github.com/remarkjs/remark/discussions
[esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c
[npm]: https://docs.npmjs.com/cli/install
[health]: https://github.com/remarkjs/.github

View File

@ -0,0 +1,4 @@
{
"extends": "../../tsconfig.json",
"include": ["*.js"]
}

View File

@ -30,29 +30,36 @@
* 4:7-4:10: Checkboxes should be followed by a single character
*/
/**
* @typedef {import('mdast').Root} Root
*/
import {lintRule} from 'unified-lint-rule'
import {location} from 'vfile-location'
import {visit} from 'unist-util-visit'
import {pointStart} from 'unist-util-position'
import {generated} from 'unist-util-generated'
const remarkLintCheckboxContentIndent = lintRule(
'remark-lint:checkbox-content-indent',
/** @type {import('unified-lint-rule').Rule<Root, void>} */
(tree, file) => {
const value = String(file)
const loc = location(file)
visit(tree, 'listItem', (node) => {
const head = node.children[0]
const point = pointStart(head)
// Exit early for items without checkbox.
// A list item cannot be checked and empty, according to GFM.
if (typeof node.checked !== 'boolean' || generated(node) || !head) {
if (
typeof node.checked !== 'boolean' ||
!head ||
typeof point.offset !== 'number'
) {
return
}
const point = pointStart(head)
// Assume we start with a checkbox, because well, `checked` is set.
const match = /\[([\t xX])]/.exec(
value.slice(point.offset - 4, point.offset + 1)

View File

@ -26,15 +26,27 @@
"sideEffects": false,
"type": "module",
"main": "index.js",
"types": "index.d.ts",
"files": [
"index.d.ts",
"index.js"
],
"dependencies": {
"@types/mdast": "^3.0.0",
"unified": "^10.0.0",
"unified-lint-rule": "^1.0.0",
"unist-util-generated": "^2.0.0",
"unist-util-position": "^4.0.0",
"unist-util-visit": "^4.0.0",
"vfile-location": "^4.0.0"
},
"xo": false
"scripts": {
"build": "rimraf \"*.d.ts\" && tsc && type-coverage"
},
"xo": false,
"typeCoverage": {
"atLeast": 100,
"detail": true,
"strict": true,
"ignoreCatch": true
}
}

View File

@ -58,12 +58,18 @@ Note: this example uses [GFM][].
## Install
This package is [ESM only][esm]:
Node 12+ is needed to use it and it must be `imported`ed instead of `required`d.
[npm][]:
```sh
npm install remark-lint-checkbox-content-indent
```
This package exports no identifiers.
The default export is `remarkLintCheckboxContentIndent`.
## Use
You probably want to use it on the CLI through a config file:
@ -144,6 +150,8 @@ abide by its terms.
[chat]: https://github.com/remarkjs/remark/discussions
[esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c
[npm]: https://docs.npmjs.com/cli/install
[health]: https://github.com/remarkjs/.github

View File

@ -0,0 +1,4 @@
{
"extends": "../../tsconfig.json",
"include": ["*.js"]
}

View File

@ -101,52 +101,55 @@
* 1:1: Incorrect code block style `💩`: use either `'consistent'`, `'fenced'`, or `'indented'`
*/
/**
* @typedef {import('mdast').Root} Root
* @typedef {'fenced'|'indented'} Style
* @typedef {'consistent'|Style} Options
*/
import {lintRule} from 'unified-lint-rule'
import {visit} from 'unist-util-visit'
import {pointStart, pointEnd} from 'unist-util-position'
import {generated} from 'unist-util-generated'
const styles = {null: true, fenced: true, indented: true}
const remarkLintCodeBlockStyle = lintRule(
'remark-lint:code-block-style',
codeBlockStyle
/** @type {import('unified-lint-rule').Rule<Root, Options>} */
(tree, file, option = 'consistent') => {
const value = String(file)
if (
option !== 'consistent' &&
option !== 'fenced' &&
option !== 'indented'
) {
file.fail(
'Incorrect code block style `' +
option +
"`: use either `'consistent'`, `'fenced'`, or `'indented'`"
)
}
visit(tree, 'code', (node) => {
if (generated(node)) {
return
}
const initial = pointStart(node).offset
const final = pointEnd(node).offset
const current =
node.lang || /^\s*([~`])\1{2,}/.test(value.slice(initial, final))
? 'fenced'
: 'indented'
if (option === 'consistent') {
option = current
} else if (option !== current) {
file.message('Code blocks should be ' + option, node)
}
})
}
)
export default remarkLintCodeBlockStyle
function codeBlockStyle(tree, file, option) {
const value = String(file)
let preferred =
typeof option === 'string' && option !== 'consistent' ? option : null
if (styles[preferred] !== true) {
file.fail(
'Incorrect code block style `' +
preferred +
"`: use either `'consistent'`, `'fenced'`, or `'indented'`"
)
}
visit(tree, 'code', (node) => {
if (generated(node)) {
return
}
const initial = pointStart(node).offset
const final = pointEnd(node).offset
const current =
node.lang || /^\s*([~`])\1{2,}/.test(value.slice(initial, final))
? 'fenced'
: 'indented'
if (preferred) {
if (preferred !== current) {
file.message('Code blocks should be ' + preferred, node)
}
} else {
preferred = current
}
})
}

View File

@ -24,14 +24,27 @@
"sideEffects": false,
"type": "module",
"main": "index.js",
"types": "index.d.ts",
"files": [
"index.d.ts",
"index.js"
],
"dependencies": {
"@types/mdast": "^3.0.0",
"unified": "^10.0.0",
"unified-lint-rule": "^1.0.0",
"unist-util-generated": "^2.0.0",
"unist-util-position": "^4.0.0",
"unist-util-visit": "^4.0.0"
},
"xo": false
"scripts": {
"build": "rimraf \"*.d.ts\" && tsc && type-coverage"
},
"xo": false,
"typeCoverage": {
"atLeast": 100,
"detail": true,
"strict": true,
"ignoreCatch": true
}
}

View File

@ -158,12 +158,18 @@ When configured with `'💩'`.
## Install
This package is [ESM only][esm]:
Node 12+ is needed to use it and it must be `imported`ed instead of `required`d.
[npm][]:
```sh
npm install remark-lint-code-block-style
```
This package exports no identifiers.
The default export is `remarkLintCodeBlockStyle`.
## Use
You probably want to use it on the CLI through a config file:
@ -244,6 +250,8 @@ abide by its terms.
[chat]: https://github.com/remarkjs/remark/discussions
[esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c
[npm]: https://docs.npmjs.com/cli/install
[health]: https://github.com/remarkjs/.github

View File

@ -0,0 +1,4 @@
{
"extends": "../../tsconfig.json",
"include": ["*.js"]
}

View File

@ -22,32 +22,36 @@
* 1:1-1:47: Do not use uppercase characters in definition labels
*/
/**
* @typedef {import('mdast').Root} Root
*/
import {lintRule} from 'unified-lint-rule'
import {visit} from 'unist-util-visit'
import {pointStart, pointEnd} from 'unist-util-position'
import {generated} from 'unist-util-generated'
const label = /^\s*\[((?:\\[\s\S]|[^[\]])+)]/
const remarkLintDefinitionCase = lintRule(
'remark-lint:definition-case',
/** @type {import('unified-lint-rule').Rule<Root, void>} */
(tree, file) => {
const value = String(file)
visit(tree, (node) => {
if (
(node.type === 'definition' || node.type === 'footnoteDefinition') &&
!generated(node)
) {
if (node.type === 'definition' || node.type === 'footnoteDefinition') {
const start = pointStart(node).offset
const end = pointEnd(node).offset
const slice = value.slice(start, end).match(label)[1]
if (slice !== slice.toLowerCase()) {
file.message(
'Do not use uppercase characters in definition labels',
node
)
if (typeof start === 'number' && typeof end === 'number') {
const match = value.slice(start, end).match(label)
if (match && match[1] !== match[1].toLowerCase()) {
file.message(
'Do not use uppercase characters in definition labels',
node
)
}
}
}
})

View File

@ -24,14 +24,26 @@
"sideEffects": false,
"type": "module",
"main": "index.js",
"types": "index.d.ts",
"files": [
"index.d.ts",
"index.js"
],
"dependencies": {
"@types/mdast": "^3.0.0",
"unified": "^10.0.0",
"unified-lint-rule": "^1.0.0",
"unist-util-generated": "^2.0.0",
"unist-util-position": "^4.0.0",
"unist-util-visit": "^4.0.0"
},
"xo": false
"scripts": {
"build": "rimraf \"*.d.ts\" && tsc && type-coverage"
},
"xo": false,
"typeCoverage": {
"atLeast": 100,
"detail": true,
"strict": true,
"ignoreCatch": true
}
}

View File

@ -50,12 +50,18 @@ No messages.
## Install
This package is [ESM only][esm]:
Node 12+ is needed to use it and it must be `imported`ed instead of `required`d.
[npm][]:
```sh
npm install remark-lint-definition-case
```
This package exports no identifiers.
The default export is `remarkLintDefinitionCase`.
## Use
You probably want to use it on the CLI through a config file:
@ -136,6 +142,8 @@ abide by its terms.
[chat]: https://github.com/remarkjs/remark/discussions
[esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c
[npm]: https://docs.npmjs.com/cli/install
[health]: https://github.com/remarkjs/.github

View File

@ -0,0 +1,4 @@
{
"extends": "../../tsconfig.json",
"include": ["*.js"]
}

View File

@ -22,31 +22,36 @@
* 1:1-1:57: Do not use consecutive whitespace in definition labels
*/
/**
* @typedef {import('mdast').Root} Root
*/
import {lintRule} from 'unified-lint-rule'
import {visit} from 'unist-util-visit'
import {pointStart, pointEnd} from 'unist-util-position'
import {generated} from 'unist-util-generated'
const label = /^\s*\[((?:\\[\s\S]|[^[\]])+)]/
const remarkLintDefinitionSpacing = lintRule(
'remark-lint:definition-spacing',
/** @type {import('unified-lint-rule').Rule<Root, void>} */
(tree, file) => {
const value = String(file)
visit(tree, (node) => {
if (
(node.type === 'definition' || node.type === 'footnoteDefinition') &&
!generated(node)
) {
if (node.type === 'definition' || node.type === 'footnoteDefinition') {
const start = pointStart(node).offset
const end = pointEnd(node).offset
if (/[ \t\n]{2,}/.test(value.slice(start, end).match(label)[1])) {
file.message(
'Do not use consecutive whitespace in definition labels',
node
)
if (typeof start === 'number' && typeof end === 'number') {
const match = value.slice(start, end).match(label)
if (match && /[ \t\n]{2,}/.test(match[1])) {
file.message(
'Do not use consecutive whitespace in definition labels',
node
)
}
}
}
})

View File

@ -24,14 +24,26 @@
"sideEffects": false,
"type": "module",
"main": "index.js",
"types": "index.d.ts",
"files": [
"index.d.ts",
"index.js"
],
"dependencies": {
"@types/mdast": "^3.0.0",
"unified": "^10.0.0",
"unified-lint-rule": "^1.0.0",
"unist-util-generated": "^2.0.0",
"unist-util-position": "^4.0.0",
"unist-util-visit": "^4.0.0"
},
"xo": false
"scripts": {
"build": "rimraf \"*.d.ts\" && tsc && type-coverage"
},
"xo": false,
"typeCoverage": {
"atLeast": 100,
"detail": true,
"strict": true,
"ignoreCatch": true
}
}

View File

@ -52,12 +52,18 @@ Note: `·` represents a space.
## Install
This package is [ESM only][esm]:
Node 12+ is needed to use it and it must be `imported`ed instead of `required`d.
[npm][]:
```sh
npm install remark-lint-definition-spacing
```
This package exports no identifiers.
The default export is `remarkLintDefinitionSpacing`.
## Use
You probably want to use it on the CLI through a config file:
@ -138,6 +144,8 @@ abide by its terms.
[chat]: https://github.com/remarkjs/remark/discussions
[esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c
[npm]: https://docs.npmjs.com/cli/install
[health]: https://github.com/remarkjs/.github

View File

@ -0,0 +1,4 @@
{
"extends": "../../tsconfig.json",
"include": ["*.js"]
}

View File

@ -69,41 +69,40 @@
* 1:1: Incorrect emphasis marker `💩`: use either `'consistent'`, `'*'`, or `'_'`
*/
/**
* @typedef {import('mdast').Root} Root
* @typedef {'*'|'_'} Marker
* @typedef {'consistent'|Marker} Options
*/
import {lintRule} from 'unified-lint-rule'
import {visit} from 'unist-util-visit'
import {pointStart} from 'unist-util-position'
import {generated} from 'unist-util-generated'
const markers = {null: true, '*': true, _: true}
const remarkLintEmphasisMarker = lintRule(
'remark-lint:emphasis-marker',
(tree, file, option) => {
/** @type {import('unified-lint-rule').Rule<Root, Options>} */
(tree, file, option = 'consistent') => {
const value = String(file)
let preferred =
typeof option === 'string' && option !== 'consistent' ? option : null
if (markers[preferred] !== true) {
if (option !== '*' && option !== '_' && option !== 'consistent') {
file.fail(
'Incorrect emphasis marker `' +
preferred +
option +
"`: use either `'consistent'`, `'*'`, or `'_'`"
)
}
visit(tree, 'emphasis', (node) => {
if (!generated(node)) {
const marker = value.charAt(pointStart(node).offset)
const start = pointStart(node).offset
if (preferred) {
if (marker !== preferred) {
file.message(
'Emphasis should use `' + preferred + '` as a marker',
node
)
}
} else {
preferred = marker
if (typeof start === 'number') {
const marker = /** @type {Marker} */ (value.charAt(start))
if (option === 'consistent') {
option = marker
} else if (marker !== option) {
file.message('Emphasis should use `' + option + '` as a marker', node)
}
}
})

View File

@ -24,14 +24,26 @@
"sideEffects": false,
"type": "module",
"main": "index.js",
"types": "index.d.ts",
"files": [
"index.d.ts",
"index.js"
],
"dependencies": {
"@types/mdast": "^3.0.0",
"unified": "^10.0.0",
"unified-lint-rule": "^1.0.0",
"unist-util-generated": "^2.0.0",
"unist-util-position": "^4.0.0",
"unist-util-visit": "^4.0.0"
},
"xo": false
"scripts": {
"build": "rimraf \"*.d.ts\" && tsc && type-coverage"
},
"xo": false,
"typeCoverage": {
"atLeast": 100,
"detail": true,
"strict": true,
"ignoreCatch": true
}
}

View File

@ -126,12 +126,18 @@ When configured with `'💩'`.
## Install
This package is [ESM only][esm]:
Node 12+ is needed to use it and it must be `imported`ed instead of `required`d.
[npm][]:
```sh
npm install remark-lint-emphasis-marker
```
This package exports no identifiers.
The default export is `remarkLintEmphasisMarker`.
## Use
You probably want to use it on the CLI through a config file:
@ -212,6 +218,8 @@ abide by its terms.
[chat]: https://github.com/remarkjs/remark/discussions
[esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c
[npm]: https://docs.npmjs.com/cli/install
[health]: https://github.com/remarkjs/.github

View File

@ -0,0 +1,4 @@
{
"extends": "../../tsconfig.json",
"include": ["*.js"]
}

View File

@ -61,6 +61,13 @@
* ```
*
* @example
* {"name": "ok.md", "setting": {"flags":["alpha"]}}
*
* ```alpha
* bravo()
* ```
*
* @example
* {"name": "not-ok.md", "setting": ["charlie"], "label": "input"}
*
* ```alpha
@ -73,6 +80,18 @@
* 1:1-3:4: Incorrect code language flag
*/
/**
* @typedef {import('mdast').Root} Root
*
* @typedef {string[]} Flags
*
* @typedef FlagMap
* @property {Flags} [flags]
* @property {boolean} [allowEmpty=false]
*
* @typedef {Flags|FlagMap} Options
*/
import {lintRule} from 'unified-lint-rule'
import {visit} from 'unist-util-visit'
import {pointStart, pointEnd} from 'unist-util-position'
@ -82,19 +101,23 @@ const fence = /^ {0,3}([~`])\1{2,}/
const remarkLintFencedCodeFlag = lintRule(
'remark-lint:fenced-code-flag',
/** @type {import('unified-lint-rule').Rule<Root, Options>} */
(tree, file, option) => {
const value = String(file)
let allowEmpty = false
/** @type {string[]} */
let allowed = []
let flags = option
if (typeof flags === 'object' && !Array.isArray(flags)) {
allowEmpty = Boolean(flags.allowEmpty)
flags = flags.flags
}
if (typeof option === 'object') {
if (Array.isArray(option)) {
allowed = option
} else {
allowEmpty = Boolean(option.allowEmpty)
if (Array.isArray(flags)) {
allowed = String(flags).split(',')
if (option.flags) {
allowed = option.flags
}
}
}
visit(tree, 'code', (node) => {

View File

@ -26,14 +26,27 @@
"sideEffects": false,
"type": "module",
"main": "index.js",
"types": "index.d.ts",
"files": [
"index.d.ts",
"index.js"
],
"dependencies": {
"@types/mdast": "^3.0.0",
"unified": "^10.0.0",
"unified-lint-rule": "^1.0.0",
"unist-util-generated": "^2.0.0",
"unist-util-position": "^4.0.0",
"unist-util-visit": "^4.0.0"
},
"xo": false
"scripts": {
"build": "rimraf \"*.d.ts\" && tsc && type-coverage"
},
"xo": false,
"typeCoverage": {
"atLeast": 100,
"detail": true,
"strict": true,
"ignoreCatch": true
}
}

View File

@ -111,6 +111,22 @@ bravo()
No messages.
##### `ok.md`
When configured with `{ flags: [ 'alpha' ] }`.
###### In
````markdown
```alpha
bravo()
```
````
###### Out
No messages.
##### `not-ok.md`
When configured with `[ 'charlie' ]`.
@ -131,12 +147,18 @@ bravo()
## Install
This package is [ESM only][esm]:
Node 12+ is needed to use it and it must be `imported`ed instead of `required`d.
[npm][]:
```sh
npm install remark-lint-fenced-code-flag
```
This package exports no identifiers.
The default export is `remarkLintFencedCodeFlag`.
## Use
You probably want to use it on the CLI through a config file:
@ -217,6 +239,8 @@ abide by its terms.
[chat]: https://github.com/remarkjs/remark/discussions
[esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c
[npm]: https://docs.npmjs.com/cli/install
[health]: https://github.com/remarkjs/.github

View File

@ -0,0 +1,4 @@
{
"extends": "../../tsconfig.json",
"include": ["*.js"]
}

View File

@ -89,53 +89,50 @@
* 1:1: Incorrect fenced code marker `💩`: use either `'consistent'`, `` '`' ``, or `'~'`
*/
/**
* @typedef {import('mdast').Root} Root
* @typedef {'~'|'`'} Marker
* @typedef {'consistent'|Marker} Options
*/
import {lintRule} from 'unified-lint-rule'
import {visit} from 'unist-util-visit'
import {pointStart} from 'unist-util-position'
import {generated} from 'unist-util-generated'
const markers = {
'`': true,
'~': true,
null: true
}
const remarkLintFencedCodeMarker = lintRule(
'remark-lint:fenced-code-marker',
(tree, file, option) => {
/** @type {import('unified-lint-rule').Rule<Root, Options>} */
(tree, file, option = 'consistent') => {
const contents = String(file)
let preferred =
typeof option === 'string' && option !== 'consistent' ? option : null
if (markers[preferred] !== true) {
if (option !== 'consistent' && option !== '~' && option !== '`') {
file.fail(
'Incorrect fenced code marker `' +
preferred +
option +
"`: use either `'consistent'`, `` '`' ``, or `'~'`"
)
}
visit(tree, 'code', (node) => {
if (!generated(node)) {
const start = pointStart(node).offset
const start = pointStart(node).offset
if (typeof start === 'number') {
const marker = contents
.slice(start, start + 4)
.replace(/^\s+/, '')
.charAt(0)
// Ignore unfenced code blocks.
if (markers[marker] === true) {
if (preferred) {
if (marker !== preferred) {
file.message(
'Fenced code should use `' +
(preferred === '~' ? preferred : '` ` `') +
'` as a marker',
node
)
}
} else {
preferred = marker
if (marker === '~' || marker === '`') {
if (option === 'consistent') {
option = marker
} else if (marker !== option) {
file.message(
'Fenced code should use `' +
(option === '~' ? option : '` ` `') +
'` as a marker',
node
)
}
}
}

View File

@ -25,14 +25,26 @@
"sideEffects": false,
"type": "module",
"main": "index.js",
"types": "index.d.ts",
"files": [
"index.d.ts",
"index.js"
],
"dependencies": {
"@types/mdast": "^3.0.0",
"unified": "^10.0.0",
"unified-lint-rule": "^1.0.0",
"unist-util-generated": "^2.0.0",
"unist-util-position": "^4.0.0",
"unist-util-visit": "^4.0.0"
},
"xo": false
"scripts": {
"build": "rimraf \"*.d.ts\" && tsc && type-coverage"
},
"xo": false,
"typeCoverage": {
"atLeast": 100,
"detail": true,
"strict": true,
"ignoreCatch": true
}
}

View File

@ -145,12 +145,18 @@ When configured with `'💩'`.
## Install
This package is [ESM only][esm]:
Node 12+ is needed to use it and it must be `imported`ed instead of `required`d.
[npm][]:
```sh
npm install remark-lint-fenced-code-marker
```
This package exports no identifiers.
The default export is `remarkLintFencedCodeMarker`.
## Use
You probably want to use it on the CLI through a config file:
@ -231,6 +237,8 @@ abide by its terms.
[chat]: https://github.com/remarkjs/remark/discussions
[esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c
[npm]: https://docs.npmjs.com/cli/install
[health]: https://github.com/remarkjs/.github

View File

@ -0,0 +1,4 @@
{
"extends": "../../tsconfig.json",
"include": ["*.js"]
}

View File

@ -26,11 +26,17 @@
* {"name": "readme.mkd", "setting": "mkd"}
*/
/**
* @typedef {import('mdast').Root} Root
* @typedef {string} Options
*/
import {lintRule} from 'unified-lint-rule'
const remarkLintFileExtension = lintRule(
'remark-lint:file-extension',
(tree, file, option) => {
/** @type {import('unified-lint-rule').Rule<Root, Options>} */
(_, file, option) => {
const ext = file.extname
const preferred = typeof option === 'string' ? option : 'md'

View File

@ -25,11 +25,24 @@
"sideEffects": false,
"type": "module",
"main": "index.js",
"types": "index.d.ts",
"files": [
"index.d.ts",
"index.js"
],
"dependencies": {
"@types/mdast": "^3.0.0",
"unified": "^10.0.0",
"unified-lint-rule": "^1.0.0"
},
"xo": false
"scripts": {
"build": "rimraf \"*.d.ts\" && tsc && type-coverage"
},
"xo": false,
"typeCoverage": {
"atLeast": 100,
"detail": true,
"strict": true,
"ignoreCatch": true
}
}

View File

@ -57,12 +57,18 @@ No messages.
## Install
This package is [ESM only][esm]:
Node 12+ is needed to use it and it must be `imported`ed instead of `required`d.
[npm][]:
```sh
npm install remark-lint-file-extension
```
This package exports no identifiers.
The default export is `remarkLintFileExtension`.
## Use
You probably want to use it on the CLI through a config file:
@ -143,6 +149,8 @@ abide by its terms.
[chat]: https://github.com/remarkjs/remark/discussions
[esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c
[npm]: https://docs.npmjs.com/cli/install
[health]: https://github.com/remarkjs/.github

View File

@ -0,0 +1,4 @@
{
"extends": "../../tsconfig.json",
"include": ["*.js"]
}

View File

@ -40,6 +40,10 @@
* [example-2]: http://example.com/two/
*/
/**
* @typedef {import('mdast').Root} Root
*/
import {lintRule} from 'unified-lint-rule'
import {visit} from 'unist-util-visit'
import {pointStart} from 'unist-util-position'
@ -47,8 +51,9 @@ import {generated} from 'unist-util-generated'
const remarkLintFinalDefinition = lintRule(
'remark-lint:final-definition',
/** @type {import('unified-lint-rule').Rule<Root, void>} */
(tree, file) => {
let last = null
let last = 0
visit(
tree,
@ -65,7 +70,7 @@ const remarkLintFinalDefinition = lintRule(
const line = pointStart(node).line
if (node.type === 'definition') {
if (last !== null && last > line) {
if (last && last > line) {
file.message(
'Move definitions to the end of the file (after the node at line `' +
last +
@ -73,7 +78,7 @@ const remarkLintFinalDefinition = lintRule(
node
)
}
} else if (last === null) {
} else if (last === 0) {
last = line
}
},

View File

@ -25,14 +25,27 @@
"sideEffects": false,
"type": "module",
"main": "index.js",
"types": "index.d.ts",
"files": [
"index.d.ts",
"index.js"
],
"dependencies": {
"@types/mdast": "^3.0.0",
"unified": "^10.0.0",
"unified-lint-rule": "^1.0.0",
"unist-util-generated": "^2.0.0",
"unist-util-position": "^4.0.0",
"unist-util-visit": "^4.0.0"
},
"xo": false
"scripts": {
"build": "rimraf \"*.d.ts\" && tsc && type-coverage"
},
"xo": false,
"typeCoverage": {
"atLeast": 100,
"detail": true,
"strict": true,
"ignoreCatch": true
}
}

View File

@ -75,12 +75,18 @@ No messages.
## Install
This package is [ESM only][esm]:
Node 12+ is needed to use it and it must be `imported`ed instead of `required`d.
[npm][]:
```sh
npm install remark-lint-final-definition
```
This package exports no identifiers.
The default export is `remarkLintFinalDefinition`.
## Use
You probably want to use it on the CLI through a config file:
@ -161,6 +167,8 @@ abide by its terms.
[chat]: https://github.com/remarkjs/remark/discussions
[esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c
[npm]: https://docs.npmjs.com/cli/install
[health]: https://github.com/remarkjs/.github

View File

@ -0,0 +1,4 @@
{
"extends": "../../tsconfig.json",
"include": ["*.js"]
}

View File

@ -50,11 +50,16 @@
* ```
*/
/**
* @typedef {import('mdast').Root} Root
*/
import {lintRule} from 'unified-lint-rule'
const remarkLintFinalNewline = lintRule(
'remark-lint:final-newline',
(tree, file) => {
/** @type {import('unified-lint-rule').Rule<Root, void>} */
(_, file) => {
const value = String(file)
const last = value.length - 1

View File

@ -25,11 +25,24 @@
"sideEffects": false,
"type": "module",
"main": "index.js",
"types": "index.d.ts",
"files": [
"index.d.ts",
"index.js"
],
"dependencies": {
"@types/mdast": "^3.0.0",
"unified": "^10.0.0",
"unified-lint-rule": "^1.0.0"
},
"xo": false
"scripts": {
"build": "rimraf \"*.d.ts\" && tsc && type-coverage"
},
"xo": false,
"typeCoverage": {
"atLeast": 100,
"detail": true,
"strict": true,
"ignoreCatch": true
}
}

View File

@ -65,12 +65,18 @@ This rule is included in the following presets:
## Install
This package is [ESM only][esm]:
Node 12+ is needed to use it and it must be `imported`ed instead of `required`d.
[npm][]:
```sh
npm install remark-lint-final-newline
```
This package exports no identifiers.
The default export is `remarkLintFinalNewline`.
## Use
You probably want to use it on the CLI through a config file:
@ -151,6 +157,8 @@ abide by its terms.
[chat]: https://github.com/remarkjs/remark/discussions
[esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c
[npm]: https://docs.npmjs.com/cli/install
[health]: https://github.com/remarkjs/.github

View File

@ -0,0 +1,4 @@
{
"extends": "../../tsconfig.json",
"include": ["*.js"]
}

View File

@ -90,6 +90,13 @@
* 1:1-1:14: First heading level should be `2`
*/
/**
* @typedef {import('mdast').Root} Root
* @typedef {import('mdast').HTML} HTML
* @typedef {import('mdast').Heading['depth']} Depth
* @typedef {Depth} Options
*/
import {lintRule} from 'unified-lint-rule'
import {visit, EXIT} from 'unist-util-visit'
import {generated} from 'unist-util-generated'
@ -98,11 +105,11 @@ const re = /<h([1-6])/
const remarkLintFirstHeadingLevel = lintRule(
'remark-lint:first-heading-level',
(tree, file, option) => {
const preferred = option && option !== true ? option : 1
/** @type {import('unified-lint-rule').Rule<Root, Options>} */
(tree, file, option = 1) => {
visit(tree, (node) => {
if (!generated(node)) {
/** @type {Depth|undefined} */
let rank
if (node.type === 'heading') {
@ -112,11 +119,8 @@ const remarkLintFirstHeadingLevel = lintRule(
}
if (rank !== undefined) {
if (rank !== preferred) {
file.message(
'First heading level should be `' + preferred + '`',
node
)
if (rank !== option) {
file.message('First heading level should be `' + option + '`', node)
}
return EXIT
@ -128,7 +132,12 @@ const remarkLintFirstHeadingLevel = lintRule(
export default remarkLintFirstHeadingLevel
/**
* @param {HTML} node
* @returns {Depth|undefined}
*/
function infer(node) {
const results = node.value.match(re)
// @ts-expect-error: can be castes fine.
return results ? Number(results[1]) : undefined
}

View File

@ -26,13 +26,26 @@
"sideEffects": false,
"type": "module",
"main": "index.js",
"types": "index.d.ts",
"files": [
"index.d.ts",
"index.js"
],
"dependencies": {
"@types/mdast": "^3.0.0",
"unified": "^10.0.0",
"unified-lint-rule": "^1.0.0",
"unist-util-generated": "^2.0.0",
"unist-util-visit": "^4.0.0"
},
"xo": false
"scripts": {
"build": "rimraf \"*.d.ts\" && tsc && type-coverage"
},
"xo": false,
"typeCoverage": {
"atLeast": 100,
"detail": true,
"strict": true,
"ignoreCatch": true
}
}

View File

@ -162,12 +162,18 @@ Paragraph.
## Install
This package is [ESM only][esm]:
Node 12+ is needed to use it and it must be `imported`ed instead of `required`d.
[npm][]:
```sh
npm install remark-lint-first-heading-level
```
This package exports no identifiers.
The default export is `remarkLintFirstHeadingLevel`.
## Use
You probably want to use it on the CLI through a config file:
@ -248,6 +254,8 @@ abide by its terms.
[chat]: https://github.com/remarkjs/remark/discussions
[esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c
[npm]: https://docs.npmjs.com/cli/install
[health]: https://github.com/remarkjs/.github

View File

@ -0,0 +1,4 @@
{
"extends": "../../tsconfig.json",
"include": ["*.js"]
}

View File

@ -24,6 +24,10 @@
* 1:12-2:1: Use two spaces for hard line breaks
*/
/**
* @typedef {import('mdast').Root} Root
*/
import {lintRule} from 'unified-lint-rule'
import {visit} from 'unist-util-visit'
import {pointStart, pointEnd} from 'unist-util-position'
@ -31,6 +35,7 @@ import {generated} from 'unist-util-generated'
const remarkLintHardBreakSpaces = lintRule(
'remark-lint:hard-break-spaces',
/** @type {import('unified-lint-rule').Rule<Root, void>} */
(tree, file) => {
const value = String(file)

View File

@ -26,14 +26,27 @@
"sideEffects": false,
"type": "module",
"main": "index.js",
"types": "index.d.ts",
"files": [
"index.d.ts",
"index.js"
],
"dependencies": {
"@types/mdast": "^3.0.0",
"unified": "^10.0.0",
"unified-lint-rule": "^1.0.0",
"unist-util-generated": "^2.0.0",
"unist-util-position": "^4.0.0",
"unist-util-visit": "^4.0.0"
},
"xo": false
"scripts": {
"build": "rimraf \"*.d.ts\" && tsc && type-coverage"
},
"xo": false,
"typeCoverage": {
"atLeast": 100,
"detail": true,
"strict": true,
"ignoreCatch": true
}
}

View File

@ -57,12 +57,18 @@ dolor sit amet.
## Install
This package is [ESM only][esm]:
Node 12+ is needed to use it and it must be `imported`ed instead of `required`d.
[npm][]:
```sh
npm install remark-lint-hard-break-spaces
```
This package exports no identifiers.
The default export is `remarkLintHardBreakSpaces`.
## Use
You probably want to use it on the CLI through a config file:
@ -143,6 +149,8 @@ abide by its terms.
[chat]: https://github.com/remarkjs/remark/discussions
[esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c
[npm]: https://docs.npmjs.com/cli/install
[health]: https://github.com/remarkjs/.github

View File

@ -0,0 +1,4 @@
{
"extends": "../../tsconfig.json",
"include": ["*.js"]
}

View File

@ -26,13 +26,20 @@
* 3:1-3:10: Heading levels should increment by one level at a time
*/
/**
* @typedef {import('mdast').Root} Root
* @typedef {import('mdast').Heading['depth']} Depth
*/
import {lintRule} from 'unified-lint-rule'
import {visit} from 'unist-util-visit'
import {generated} from 'unist-util-generated'
const remarkLintHeadingIncrement = lintRule(
'remark-lint:heading-increment',
/** @type {import('unified-lint-rule').Rule<Root, void>} */
(tree, file) => {
/** @type {Depth} */
let previous
visit(tree, 'heading', (node) => {

View File

@ -25,13 +25,26 @@
"sideEffects": false,
"type": "module",
"main": "index.js",
"types": "index.d.ts",
"files": [
"index.d.ts",
"index.js"
],
"dependencies": {
"@types/mdast": "^3.0.0",
"unified": "^10.0.0",
"unified-lint-rule": "^1.0.0",
"unist-util-generated": "^2.0.0",
"unist-util-visit": "^4.0.0"
},
"xo": false
"scripts": {
"build": "rimraf \"*.d.ts\" && tsc && type-coverage"
},
"xo": false,
"typeCoverage": {
"atLeast": 100,
"detail": true,
"strict": true,
"ignoreCatch": true
}
}

View File

@ -54,12 +54,18 @@ No messages.
## Install
This package is [ESM only][esm]:
Node 12+ is needed to use it and it must be `imported`ed instead of `required`d.
[npm][]:
```sh
npm install remark-lint-heading-increment
```
This package exports no identifiers.
The default export is `remarkLintHeadingIncrement`.
## Use
You probably want to use it on the CLI through a config file:
@ -140,6 +146,8 @@ abide by its terms.
[chat]: https://github.com/remarkjs/remark/discussions
[esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c
[npm]: https://docs.npmjs.com/cli/install
[health]: https://github.com/remarkjs/.github

View File

@ -0,0 +1,4 @@
{
"extends": "../../tsconfig.json",
"include": ["*.js"]
}

View File

@ -69,6 +69,17 @@
*
* 4:1-4:8: Headings should use setext
* 6:1-6:13: Headings should use setext
*
* @example
* {"name": "not-ok.md", "setting": "💩", "label": "output", "positionless": true}
*
* 1:1: Incorrect heading style type `💩`: use either `'consistent'`, `'atx'`, `'atx-closed'`, or `'setext'`
*/
/**
* @typedef {import('mdast').Root} Root
* @typedef {'atx'|'atx-closed'|'setext'} Type
* @typedef {'consistent'|Type} Options
*/
import {lintRule} from 'unified-lint-rule'
@ -76,21 +87,31 @@ import {visit} from 'unist-util-visit'
import {headingStyle} from 'mdast-util-heading-style'
import {generated} from 'unist-util-generated'
const types = new Set(['atx', 'atx-closed', 'setext'])
const remarkLintHeadingStyle = lintRule(
'remark-lint:heading-style',
(tree, file, option) => {
let preferred = types.has(option) ? option : null
/** @type {import('unified-lint-rule').Rule<Root, Options>} */
(tree, file, option = 'consistent') => {
if (
option !== 'consistent' &&
option !== 'atx' &&
option !== 'atx-closed' &&
option !== 'setext'
) {
file.fail(
'Incorrect heading style type `' +
option +
"`: use either `'consistent'`, `'atx'`, `'atx-closed'`, or `'setext'`"
)
}
visit(tree, 'heading', (node) => {
if (!generated(node)) {
if (preferred) {
if (headingStyle(node, preferred) !== preferred) {
file.message('Headings should use ' + preferred, node)
}
} else {
preferred = headingStyle(node, preferred)
if (option === 'consistent') {
// Funky nodes perhaps cannot be detected.
/* c8 ignore next */
option = headingStyle(node) || 'consistent'
} else if (headingStyle(node, option) !== option) {
file.message('Headings should use ' + option, node)
}
}
})

View File

@ -26,14 +26,27 @@
"sideEffects": false,
"type": "module",
"main": "index.js",
"types": "index.d.ts",
"files": [
"index.d.ts",
"index.js"
],
"dependencies": {
"@types/mdast": "^3.0.0",
"mdast-util-heading-style": "^2.0.0",
"unified": "^10.0.0",
"unified-lint-rule": "^1.0.0",
"unist-util-generated": "^2.0.0",
"unist-util-visit": "^4.0.0"
},
"xo": false
"scripts": {
"build": "rimraf \"*.d.ts\" && tsc && type-coverage"
},
"xo": false,
"typeCoverage": {
"atLeast": 100,
"detail": true,
"strict": true,
"ignoreCatch": true
}
}

View File

@ -118,14 +118,30 @@ Juliett
6:1-6:13: Headings should use setext
```
##### `not-ok.md`
When configured with `'💩'`.
###### Out
```text
1:1: Incorrect heading style type `💩`: use either `'consistent'`, `'atx'`, `'atx-closed'`, or `'setext'`
```
## Install
This package is [ESM only][esm]:
Node 12+ is needed to use it and it must be `imported`ed instead of `required`d.
[npm][]:
```sh
npm install remark-lint-heading-style
```
This package exports no identifiers.
The default export is `remarkLintHeadingStyle`.
## Use
You probably want to use it on the CLI through a config file:
@ -206,6 +222,8 @@ abide by its terms.
[chat]: https://github.com/remarkjs/remark/discussions
[esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c
[npm]: https://docs.npmjs.com/cli/install
[health]: https://github.com/remarkjs/.github

View File

@ -0,0 +1,4 @@
{
"extends": "../../tsconfig.json",
"include": ["*.js"]
}

View File

@ -51,41 +51,43 @@
* 1:6: Expected linebreaks to be windows (`\r\n`), not unix (`\n`)
*/
/**
* @typedef {import('mdast').Root} Root
* @typedef {'unix'|'windows'} Type
* @typedef {'consistent'|Type} Options
*/
import {lintRule} from 'unified-lint-rule'
import {location} from 'vfile-location'
const escaped = {unix: '\\n', windows: '\\r\\n'}
const types = {true: 'windows', false: 'unix'}
const remarkLintLinebreakStyle = lintRule(
'remark-lint:linebreak-style',
(tree, file, option) => {
/** @type {import('unified-lint-rule').Rule<Root, Options>} */
(_, file, option = 'consistent') => {
const value = String(file)
const toPoint = location(value).toPoint
let preferred =
typeof option === 'string' && option !== 'consistent' ? option : null
let index = value.indexOf('\n')
while (index !== -1) {
const type = types[value.charAt(index - 1) === '\r']
const type = value.charAt(index - 1) === '\r' ? 'windows' : 'unix'
if (preferred) {
if (preferred !== type) {
file.message(
'Expected linebreaks to be ' +
preferred +
' (`' +
escaped[preferred] +
'`), not ' +
type +
' (`' +
escaped[type] +
'`)',
toPoint(index)
)
}
} else {
preferred = type
if (option === 'consistent') {
option = type
} else if (option !== type) {
file.message(
'Expected linebreaks to be ' +
option +
' (`' +
escaped[option] +
'`), not ' +
type +
' (`' +
escaped[type] +
'`)',
toPoint(index)
)
}
index = value.indexOf('\n', index + 1)

View File

@ -29,12 +29,25 @@
"sideEffects": false,
"type": "module",
"main": "index.js",
"types": "index.d.ts",
"files": [
"index.d.ts",
"index.js"
],
"dependencies": {
"@types/mdast": "^3.0.0",
"unified": "^10.0.0",
"unified-lint-rule": "^1.0.0",
"vfile-location": "^4.0.0"
},
"xo": false
"scripts": {
"build": "rimraf \"*.d.ts\" && tsc && type-coverage"
},
"xo": false,
"typeCoverage": {
"atLeast": 100,
"detail": true,
"strict": true,
"ignoreCatch": true
}
}

View File

@ -98,12 +98,18 @@ Alpha␊
## Install
This package is [ESM only][esm]:
Node 12+ is needed to use it and it must be `imported`ed instead of `required`d.
[npm][]:
```sh
npm install remark-lint-linebreak-style
```
This package exports no identifiers.
The default export is `remarkLintLinebreakStyle`.
## Use
You probably want to use it on the CLI through a config file:
@ -184,6 +190,8 @@ abide by its terms.
[chat]: https://github.com/remarkjs/remark/discussions
[esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c
[npm]: https://docs.npmjs.com/cli/install
[health]: https://github.com/remarkjs/.github

View File

@ -0,0 +1,4 @@
{
"extends": "../../tsconfig.json",
"include": ["*.js"]
}

View File

@ -98,11 +98,16 @@
* 1:1: Incorrect link title style marker `💩`: use either `'consistent'`, `'"'`, `'\''`, or `'()'`
*/
/**
* @typedef {import('mdast').Root} Root
* @typedef {'"'|"'"|'()'} Marker
* @typedef {'consistent'|Marker} Options
*/
import {lintRule} from 'unified-lint-rule'
import {location} from 'vfile-location'
import {visit} from 'unist-util-visit'
import {pointStart, pointEnd} from 'unist-util-position'
import {generated} from 'unist-util-generated'
const own = {}.hasOwnProperty
@ -114,42 +119,48 @@ const markers = {
const remarkLintLinkTitleStyle = lintRule(
'remark-lint:link-title-style',
(tree, file, option) => {
/** @type {import('unified-lint-rule').Rule<Root, Options>} */
(tree, file, option = 'consistent') => {
const value = String(file)
const loc = location(file)
let preferred =
typeof option === 'string' && option !== 'consistent' ? option : null
// @ts-expect-error: allow other paren combos.
let look = option === '()' || option === '(' ? ')' : option
if (preferred === '()' || preferred === '(') {
preferred = ')'
}
if (preferred && !own.call(markers, preferred)) {
if (look !== 'consistent' && !own.call(markers, look)) {
file.fail(
'Incorrect link title style marker `' +
preferred +
look +
"`: use either `'consistent'`, `'\"'`, `'\\''`, or `'()'`"
)
}
visit(tree, (node) => {
if (
(node.type === 'link' ||
node.type === 'image' ||
node.type === 'definition') &&
!generated(node)
node.type === 'link' ||
node.type === 'image' ||
node.type === 'definition'
) {
const tail = node.children
? node.children[node.children.length - 1]
: null
const tail =
'children' in node
? node.children[node.children.length - 1]
: undefined
const begin = tail ? pointEnd(tail) : pointStart(node)
let last = pointEnd(node).offset - 1
const end = pointEnd(node)
if (
typeof begin.offset !== 'number' ||
typeof end.offset !== 'number'
) {
return
}
let last = end.offset - 1
if (node.type !== 'definition') {
last--
}
const final = value.charAt(last)
const final = /** @type {keyof markers} */ (value.charAt(last))
// Exit if the final marker is not a known marker.
if (!(final in markers)) {
@ -163,24 +174,22 @@ const remarkLintLinkTitleStyle = lintRule(
// Exit if theres no starting delimiter, the starting delimiter is before
// the start of the node, or if its not preceded by whitespace.
if (first <= begin || !/\s/.test(value.charAt(first - 1))) {
if (first <= begin.offset || !/\s/.test(value.charAt(first - 1))) {
return
}
if (preferred) {
if (preferred !== final) {
file.message(
'Titles should use `' +
(preferred === ')' ? '()' : preferred) +
'` as a quote',
{
start: loc.toPoint(first),
end: loc.toPoint(last + 1)
}
)
}
} else {
preferred = final
if (look === 'consistent') {
look = final
} else if (look !== final) {
file.message(
'Titles should use `' +
(look === ')' ? '()' : look) +
'` as a quote',
{
start: loc.toPoint(first),
end: loc.toPoint(last + 1)
}
)
}
}
})

View File

@ -26,15 +26,27 @@
"sideEffects": false,
"type": "module",
"main": "index.js",
"types": "index.d.ts",
"files": [
"index.d.ts",
"index.js"
],
"dependencies": {
"@types/mdast": "^3.0.0",
"unified": "^10.0.0",
"unified-lint-rule": "^1.0.0",
"unist-util-generated": "^2.0.0",
"unist-util-position": "^4.0.0",
"unist-util-visit": "^4.0.0",
"vfile-location": "^4.0.0"
},
"xo": false
"scripts": {
"build": "rimraf \"*.d.ts\" && tsc && type-coverage"
},
"xo": false,
"typeCoverage": {
"atLeast": 100,
"detail": true,
"strict": true,
"ignoreCatch": true
}
}

View File

@ -170,12 +170,18 @@ When configured with `'💩'`.
## Install
This package is [ESM only][esm]:
Node 12+ is needed to use it and it must be `imported`ed instead of `required`d.
[npm][]:
```sh
npm install remark-lint-link-title-style
```
This package exports no identifiers.
The default export is `remarkLintLinkTitleStyle`.
## Use
You probably want to use it on the CLI through a config file:
@ -256,6 +262,8 @@ abide by its terms.
[chat]: https://github.com/remarkjs/remark/discussions
[esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c
[npm]: https://docs.npmjs.com/cli/install
[health]: https://github.com/remarkjs/.github

View File

@ -0,0 +1,4 @@
{
"extends": "../../tsconfig.json",
"include": ["*.js"]
}

View File

@ -37,13 +37,17 @@
* 4:2: Incorrect indentation before bullet: remove 1 space
*/
/**
* @typedef {import('mdast').Root} Root
*/
import {lintRule} from 'unified-lint-rule'
import plural from 'pluralize'
import {visit} from 'unist-util-visit'
import {generated} from 'unist-util-generated'
const remarkLintListItemBulletIndent = lintRule(
'remark-lint:list-item-bullet-indent',
/** @type {import('unified-lint-rule').Rule<Root, void>} */
(tree, file) => {
visit(tree, 'list', (list, _, grandparent) => {
let index = -1
@ -54,8 +58,10 @@ const remarkLintListItemBulletIndent = lintRule(
if (
grandparent &&
grandparent.type === 'root' &&
!generated(item) &&
!generated(grandparent)
grandparent.position &&
typeof grandparent.position.start.column === 'number' &&
item.position &&
typeof item.position.start.column === 'number'
) {
const indent =
item.position.start.column - grandparent.position.start.column

View File

@ -25,14 +25,26 @@
"sideEffects": false,
"type": "module",
"main": "index.js",
"types": "index.d.ts",
"files": [
"index.d.ts",
"index.js"
],
"dependencies": {
"@types/mdast": "^3.0.0",
"pluralize": "^8.0.0",
"unified": "^10.0.0",
"unified-lint-rule": "^1.0.0",
"unist-util-generated": "^2.0.0",
"unist-util-visit": "^4.0.0"
},
"xo": false
"scripts": {
"build": "rimraf \"*.d.ts\" && tsc && type-coverage"
},
"xo": false,
"typeCoverage": {
"atLeast": 100,
"detail": true,
"strict": true,
"ignoreCatch": true
}
}

View File

@ -67,12 +67,18 @@ Paragraph.
## Install
This package is [ESM only][esm]:
Node 12+ is needed to use it and it must be `imported`ed instead of `required`d.
[npm][]:
```sh
npm install remark-lint-list-item-bullet-indent
```
This package exports no identifiers.
The default export is `remarkLintListItemBulletIndent`.
## Use
You probably want to use it on the CLI through a config file:
@ -153,6 +159,8 @@ abide by its terms.
[chat]: https://github.com/remarkjs/remark/discussions
[esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c
[npm]: https://docs.npmjs.com/cli/install
[health]: https://github.com/remarkjs/.github

View File

@ -0,0 +1,4 @@
{
"extends": "../../tsconfig.json",
"include": ["*.js"]
}

View File

@ -24,31 +24,38 @@
* 2:5: Dont use mixed indentation for children, remove 1 space
*/
/**
* @typedef {import('mdast').Root} Root
*/
import {lintRule} from 'unified-lint-rule'
import plural from 'pluralize'
import {visit} from 'unist-util-visit'
import {pointStart} from 'unist-util-position'
import {generated} from 'unist-util-generated'
const remarkLintListItemContentIndent = lintRule(
'remark-lint:list-item-content-indent',
/** @type {import('unified-lint-rule').Rule<Root, void>} */
(tree, file) => {
const value = String(file)
visit(tree, 'listItem', (node) => {
let index = -1
/** @type {number|undefined} */
let style
while (++index < node.children.length) {
const item = node.children[index]
if (generated(item)) {
continue
}
const begin = pointStart(item)
let column = begin.column
if (
typeof begin.column !== 'number' ||
typeof begin.offset !== 'number'
) {
continue
}
// Get indentation for the first child. Only the first item can have a
// checkbox, so here we remove that from the column.
if (index === 0) {
@ -70,7 +77,7 @@ const remarkLintListItemContentIndent = lintRule(
}
// Warn for violating children.
if (column !== style) {
if (style && column !== style) {
const diff = style - column
const abs = Math.abs(diff)

View File

@ -26,15 +26,27 @@
"sideEffects": false,
"type": "module",
"main": "index.js",
"types": "index.d.ts",
"files": [
"index.d.ts",
"index.js"
],
"dependencies": {
"@types/mdast": "^3.0.0",
"pluralize": "^8.0.0",
"unified": "^10.0.0",
"unified-lint-rule": "^1.0.0",
"unist-util-generated": "^2.0.0",
"unist-util-position": "^4.0.0",
"unist-util-visit": "^4.0.0"
},
"xo": false
"scripts": {
"build": "rimraf \"*.d.ts\" && tsc && type-coverage"
},
"xo": false,
"typeCoverage": {
"atLeast": 100,
"detail": true,
"strict": true,
"ignoreCatch": true
}
}

View File

@ -61,12 +61,18 @@ Note: `·` represents a space.
## Install
This package is [ESM only][esm]:
Node 12+ is needed to use it and it must be `imported`ed instead of `required`d.
[npm][]:
```sh
npm install remark-lint-list-item-content-indent
```
This package exports no identifiers.
The default export is `remarkLintListItemContentIndent`.
## Use
You probably want to use it on the CLI through a config file:
@ -147,6 +153,8 @@ abide by its terms.
[chat]: https://github.com/remarkjs/remark/discussions
[esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c
[npm]: https://docs.npmjs.com/cli/install
[health]: https://github.com/remarkjs/.github

View File

@ -0,0 +1,4 @@
{
"extends": "../../tsconfig.json",
"include": ["*.js"]
}

View File

@ -113,24 +113,27 @@
* 1:1: Incorrect list-item indent style `💩`: use either `'tab-size'`, `'space'`, or `'mixed'`
*/
/**
* @typedef {import('mdast').Root} Root
* @typedef {'tab-size'|'space'|'mixed'} Options
*/
import {lintRule} from 'unified-lint-rule'
import plural from 'pluralize'
import {visit} from 'unist-util-visit'
import {pointStart} from 'unist-util-position'
import {generated} from 'unist-util-generated'
const styles = {'tab-size': true, mixed: true, space: true}
const remarkLintListItemIndent = lintRule(
'remark-lint:list-item-indent',
(tree, file, option) => {
const preferred = typeof option === 'string' ? option : 'tab-size'
/** @type {import('unified-lint-rule').Rule<Root, Options>} */
(tree, file, option = 'tab-size') => {
const value = String(file)
if (styles[preferred] !== true) {
if (option !== 'tab-size' && option !== 'space' && option !== 'mixed') {
file.fail(
'Incorrect list-item indent style `' +
preferred +
option +
"`: use either `'tab-size'`, `'space'`, or `'mixed'`"
)
}
@ -138,7 +141,8 @@ const remarkLintListItemIndent = lintRule(
visit(tree, 'list', (node) => {
if (generated(node)) return
const spread = node.spread || node.loose
// @ts-expect-error: legacy.
const spread = Boolean(node.spread || node.loose)
let index = -1
while (++index < node.children.length) {
@ -153,7 +157,7 @@ const remarkLintListItemIndent = lintRule(
const bulletSize = marker.replace(/\s+$/, '').length
const style =
preferred === 'tab-size' || (preferred === 'mixed' && spread)
option === 'tab-size' || (option === 'mixed' && spread)
? Math.ceil(bulletSize / 4) * 4
: bulletSize + 1

View File

@ -25,15 +25,28 @@
"sideEffects": false,
"type": "module",
"main": "index.js",
"types": "index.d.ts",
"files": [
"index.d.ts",
"index.js"
],
"dependencies": {
"@types/mdast": "^3.0.0",
"pluralize": "^8.0.0",
"unified": "^10.0.0",
"unified-lint-rule": "^1.0.0",
"unist-util-generated": "^2.0.0",
"unist-util-position": "^4.0.0",
"unist-util-visit": "^4.0.0"
},
"xo": false
"scripts": {
"build": "rimraf \"*.d.ts\" && tsc && type-coverage"
},
"xo": false,
"typeCoverage": {
"atLeast": 100,
"detail": true,
"strict": true,
"ignoreCatch": true
}
}

View File

@ -191,12 +191,18 @@ When configured with `'💩'`.
## Install
This package is [ESM only][esm]:
Node 12+ is needed to use it and it must be `imported`ed instead of `required`d.
[npm][]:
```sh
npm install remark-lint-list-item-indent
```
This package exports no identifiers.
The default export is `remarkLintListItemIndent`.
## Use
You probably want to use it on the CLI through a config file:
@ -277,6 +283,8 @@ abide by its terms.
[chat]: https://github.com/remarkjs/remark/discussions
[esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c
[npm]: https://docs.npmjs.com/cli/install
[health]: https://github.com/remarkjs/.github

View File

@ -0,0 +1,4 @@
{
"extends": "../../tsconfig.json",
"include": ["*.js"]
}

View File

@ -113,6 +113,14 @@
* 14:15-16:1: Extraneous new line after list item
*/
/**
* @typedef {import('mdast').Root} Root
* @typedef {import('mdast').ListItem} ListItem
*
* @typedef Options
* @property {boolean} [checkBlanks=false]
*/
import {lintRule} from 'unified-lint-rule'
import {visit} from 'unist-util-visit'
import {pointStart, pointEnd} from 'unist-util-position'
@ -120,6 +128,7 @@ import {generated} from 'unist-util-generated'
const remarkLintListItemSpacing = lintRule(
'remark-lint:list-item-spacing',
/** @type {import('unified-lint-rule').Rule<Root, Options>} */
(tree, file, option = {}) => {
const {checkBlanks} = option
const infer = checkBlanks ? inferBlankLine : inferMultiline
@ -158,6 +167,10 @@ const remarkLintListItemSpacing = lintRule(
export default remarkLintListItemSpacing
/**
* @param {ListItem} node
* @returns {boolean}
*/
function inferBlankLine(node) {
let index = 0
@ -174,6 +187,10 @@ function inferBlankLine(node) {
return false
}
/**
* @param {ListItem} node
* @returns {boolean}
*/
function inferMultiline(node) {
return (
pointEnd(node.children[node.children.length - 1]).line -

View File

@ -26,14 +26,27 @@
"sideEffects": false,
"type": "module",
"main": "index.js",
"types": "index.d.ts",
"files": [
"index.d.ts",
"index.js"
],
"dependencies": {
"@types/mdast": "^3.0.0",
"unified": "^10.0.0",
"unified-lint-rule": "^1.0.0",
"unist-util-generated": "^2.0.0",
"unist-util-position": "^4.0.0",
"unist-util-visit": "^4.0.0"
},
"xo": false
"scripts": {
"build": "rimraf \"*.d.ts\" && tsc && type-coverage"
},
"xo": false,
"typeCoverage": {
"atLeast": 100,
"detail": true,
"strict": true,
"ignoreCatch": true
}
}

View File

@ -156,12 +156,18 @@ A loose list:
## Install
This package is [ESM only][esm]:
Node 12+ is needed to use it and it must be `imported`ed instead of `required`d.
[npm][]:
```sh
npm install remark-lint-list-item-spacing
```
This package exports no identifiers.
The default export is `remarkLintListItemSpacing`.
## Use
You probably want to use it on the CLI through a config file:
@ -242,6 +248,8 @@ abide by its terms.
[chat]: https://github.com/remarkjs/remark/discussions
[esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c
[npm]: https://docs.npmjs.com/cli/install
[health]: https://github.com/remarkjs/.github

View File

@ -0,0 +1,4 @@
{
"extends": "../../tsconfig.json",
"include": ["*.js"]
}

View File

@ -28,6 +28,11 @@
* 1:1-1:52: Use headings shorter than `40`
*/
/**
* @typedef {import('mdast').Root} Root
* @typedef {number} Options
*/
import {lintRule} from 'unified-lint-rule'
import {visit} from 'unist-util-visit'
import {generated} from 'unist-util-generated'
@ -35,12 +40,11 @@ import {toString} from 'mdast-util-to-string'
const remarkLintMaximumHeadingLength = lintRule(
'remark-lint:maximum-heading-length',
(tree, file, option) => {
const preferred = typeof option === 'number' ? option : 60
/** @type {import('unified-lint-rule').Rule<Root, Options>} */
(tree, file, option = 60) => {
visit(tree, 'heading', (node) => {
if (!generated(node) && toString(node).length > preferred) {
file.message('Use headings shorter than `' + preferred + '`', node)
if (!generated(node) && toString(node).length > option) {
file.message('Use headings shorter than `' + option + '`', node)
}
})
}

View File

@ -24,14 +24,27 @@
"sideEffects": false,
"type": "module",
"main": "index.js",
"types": "index.d.ts",
"files": [
"index.d.ts",
"index.js"
],
"dependencies": {
"@types/mdast": "^3.0.0",
"mdast-util-to-string": "^3.0.0",
"unified": "^10.0.0",
"unified-lint-rule": "^1.0.0",
"unist-util-generated": "^2.0.0",
"unist-util-visit": "^4.0.0"
},
"xo": false
"scripts": {
"build": "rimraf \"*.d.ts\" && tsc && type-coverage"
},
"xo": false,
"typeCoverage": {
"atLeast": 100,
"detail": true,
"strict": true,
"ignoreCatch": true
}
}

View File

@ -58,12 +58,18 @@ No messages.
## Install
This package is [ESM only][esm]:
Node 12+ is needed to use it and it must be `imported`ed instead of `required`d.
[npm][]:
```sh
npm install remark-lint-maximum-heading-length
```
This package exports no identifiers.
The default export is `remarkLintMaximumHeadingLength`.
## Use
You probably want to use it on the CLI through a config file:
@ -144,6 +150,8 @@ abide by its terms.
[chat]: https://github.com/remarkjs/remark/discussions
[esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c
[npm]: https://docs.npmjs.com/cli/install
[health]: https://github.com/remarkjs/.github

View File

@ -0,0 +1,4 @@
{
"extends": "../../tsconfig.json",
"include": ["*.js"]
}

View File

@ -96,6 +96,12 @@
* 4:12: Line must be at most 10 characters
*/
/**
* @typedef {import('mdast').Root} Root
* @typedef {import('mdast').Parent} Parent
* @typedef {number} Options
*/
import {lintRule} from 'unified-lint-rule'
import {visit} from 'unist-util-visit'
import {pointStart, pointEnd} from 'unist-util-position'
@ -103,85 +109,93 @@ import {generated} from 'unist-util-generated'
const remarkLintMaximumLineLength = lintRule(
'remark-lint:maximum-line-length',
maximumLineLength
/** @type {import('unified-lint-rule').Rule<Root, Options>} */
(tree, file, option = 80) => {
const value = String(file)
const lines = value.split(/\r?\n/)
visit(tree, (node) => {
if (
(node.type === 'heading' ||
node.type === 'table' ||
node.type === 'code' ||
node.type === 'definition' ||
node.type === 'html' ||
// @ts-expect-error: JSX is from MDX: <https://github.com/mdx-js/specification>.
node.type === 'jsx' ||
node.type === 'yaml' ||
// @ts-expect-error: TOML is from frontmatter.
node.type === 'toml') &&
!generated(node)
) {
allowList(pointStart(node).line - 1, pointEnd(node).line)
}
})
// Finally, allow some inline spans, but only if they occur at or after
// the wrap.
// However, when they do, and theres whitespace after it, they are not
// allowed.
visit(tree, (node, pos, parent_) => {
const parent = /** @type {Parent} */ (parent_)
if (
(node.type === 'link' ||
node.type === 'image' ||
node.type === 'inlineCode') &&
!generated(node) &&
parent &&
typeof pos === 'number'
) {
const initial = pointStart(node)
const final = pointEnd(node)
// Not allowing when starting after the border, or ending before it.
if (initial.column > option || final.column < option) {
return
}
const next = parent.children[pos + 1]
// Not allowing when theres whitespace after the link.
if (
next &&
pointStart(next).line === initial.line &&
(!('value' in next) || /^(.+?[ \t].+?)/.test(next.value))
) {
return
}
allowList(initial.line - 1, final.line)
}
})
// Iterate over every line, and warn for violating lines.
let index = -1
while (++index < lines.length) {
const lineLength = lines[index].length
if (lineLength > option) {
file.message('Line must be at most ' + option + ' characters', {
line: index + 1,
column: lineLength + 1
})
}
}
/**
* Allowlist from `initial` to `final`, zero-based.
*
* @param {number} initial
* @param {number} final
*/
function allowList(initial, final) {
while (initial < final) {
lines[initial++] = ''
}
}
}
)
export default remarkLintMaximumLineLength
function maximumLineLength(tree, file, option) {
const preferred = typeof option === 'number' ? option : 80
const value = String(file)
const lines = value.split(/\r?\n/)
visit(tree, (node) => {
// Note: JSX is from MDX: <https://github.com/mdx-js/specification>.
if (
(node.type === 'heading' ||
node.type === 'table' ||
node.type === 'code' ||
node.type === 'definition' ||
node.type === 'html' ||
node.type === 'jsx' ||
node.type === 'yaml' ||
node.type === 'toml') &&
!generated(node)
) {
allowList(pointStart(node).line - 1, pointEnd(node).line)
}
})
// Finally, allow some inline spans, but only if they occur at or after
// the wrap.
// However, when they do, and theres whitespace after it, they are not
// allowed.
visit(tree, (node, pos, parent) => {
if (
(node.type === 'link' ||
node.type === 'image' ||
node.type === 'inlineCode') &&
!generated(node)
) {
const initial = pointStart(node)
const final = pointEnd(node)
// Not allowing when starting after the border, or ending before it.
if (initial.column > preferred || final.column < preferred) {
return
}
const next = parent.children[pos + 1]
// Not allowing when theres whitespace after the link.
if (
next &&
pointStart(next).line === initial.line &&
(!next.value || /^(.+?[ \t].+?)/.test(next.value))
) {
return
}
allowList(initial.line - 1, final.line)
}
})
// Iterate over every line, and warn for violating lines.
let index = -1
while (++index < lines.length) {
const lineLength = lines[index].length
if (lineLength > preferred) {
file.message('Line must be at most ' + preferred + ' characters', {
line: index + 1,
column: lineLength + 1
})
}
}
// Allowlist from `initial` to `final`, zero-based.
function allowList(initial, final) {
while (initial < final) {
lines[initial++] = ''
}
}
}

View File

@ -24,14 +24,27 @@
"sideEffects": false,
"type": "module",
"main": "index.js",
"types": "index.d.ts",
"files": [
"index.d.ts",
"index.js"
],
"dependencies": {
"@types/mdast": "^3.0.0",
"unified": "^10.0.0",
"unified-lint-rule": "^1.0.0",
"unist-util-generated": "^2.0.0",
"unist-util-position": "^4.0.0",
"unist-util-visit": "^4.0.0"
},
"xo": false
"scripts": {
"build": "rimraf \"*.d.ts\" && tsc && type-coverage"
},
"xo": false,
"typeCoverage": {
"atLeast": 100,
"detail": true,
"strict": true,
"ignoreCatch": true
}
}

View File

@ -151,12 +151,18 @@ No messages.
## Install
This package is [ESM only][esm]:
Node 12+ is needed to use it and it must be `imported`ed instead of `required`d.
[npm][]:
```sh
npm install remark-lint-maximum-line-length
```
This package exports no identifiers.
The default export is `remarkLintMaximumLineLength`.
## Use
You probably want to use it on the CLI through a config file:
@ -237,6 +243,8 @@ abide by its terms.
[chat]: https://github.com/remarkjs/remark/discussions
[esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c
[npm]: https://docs.npmjs.com/cli/install
[health]: https://github.com/remarkjs/.github

View File

@ -0,0 +1,4 @@
{
"extends": "../../tsconfig.json",
"include": ["*.js"]
}

View File

@ -36,6 +36,10 @@
* 1:1-1:14: All automatic links must start with a protocol
*/
/**
* @typedef {import('mdast').Root} Root
*/
import {lintRule} from 'unified-lint-rule'
import {visit} from 'unist-util-visit'
import {pointStart, pointEnd} from 'unist-util-position'
@ -48,6 +52,7 @@ const protocol = /^[a-z][a-z+.-]+:\/?/i
const remarkLintNoAutoLinkWithoutProtocol = lintRule(
'remark-lint:no-auto-link-without-protocol',
/** @type {import('unified-lint-rule').Rule<Root, void>} */
(tree, file) => {
visit(tree, 'link', (node) => {
if (

View File

@ -25,15 +25,28 @@
"sideEffects": false,
"type": "module",
"main": "index.js",
"types": "index.d.ts",
"files": [
"index.d.ts",
"index.js"
],
"dependencies": {
"@types/mdast": "^3.0.0",
"mdast-util-to-string": "^3.0.0",
"unified": "^10.0.0",
"unified-lint-rule": "^1.0.0",
"unist-util-generated": "^2.0.0",
"unist-util-position": "^4.0.0",
"unist-util-visit": "^4.0.0"
},
"xo": false
"scripts": {
"build": "rimraf \"*.d.ts\" && tsc && type-coverage"
},
"xo": false,
"typeCoverage": {
"atLeast": 100,
"detail": true,
"strict": true,
"ignoreCatch": true
}
}

Some files were not shown because too many files have changed in this diff Show More