mirror of
https://github.com/remarkjs/remark-lint.git
synced 2024-09-11 19:57:15 +03:00
no-undefined-references: add support for regex as option
Closes GH-296. Closes GH-297. Reviewed-by: JounQin <admin@1stg.me> Reviewed-by: Titus Wormer <tituswormer@gmail.com>
This commit is contained in:
parent
37041219cb
commit
1ab55098cc
@ -8,9 +8,12 @@
|
||||
* The following options (default: `undefined`) are accepted:
|
||||
*
|
||||
* * `Object` with the following fields:
|
||||
* * `allow` (`Array<string>`, default: `[]`)
|
||||
* — text that you want to allowed between `[` and `]` even though it’s
|
||||
* undefined
|
||||
* * `allow` (`Array<string | RegExp | { source: string }>`,
|
||||
* default: `[]`)
|
||||
* — text or regex that you want to be allowed between `[` and `]`
|
||||
* even though it’s undefined; regex is provided via a `RegExp` object
|
||||
* or via a `{ source: string }` object where `source` is the source
|
||||
* text of a case-insensitive regex
|
||||
*
|
||||
* ## Recommendation
|
||||
*
|
||||
@ -61,6 +64,15 @@
|
||||
* > Eliding a portion of a quoted passage […] is acceptable.
|
||||
*
|
||||
* @example
|
||||
* {"name": "ok-allow.md", "config": {"allow": ["a", {"source": "^b\\."}]}}
|
||||
*
|
||||
* [foo][b.c]
|
||||
*
|
||||
* [bar][a]
|
||||
*
|
||||
* Matching is case-insensitive: [bar][B.C]
|
||||
*
|
||||
* @example
|
||||
* {"name": "not-ok.md", "label": "input"}
|
||||
*
|
||||
* [bar]
|
||||
@ -93,6 +105,19 @@
|
||||
* 15:13-15:25: Found reference to undefined definition
|
||||
* 17:17-17:23: Found reference to undefined definition
|
||||
* 17:23-17:26: Found reference to undefined definition
|
||||
*
|
||||
* @example
|
||||
* {"name": "not-ok.md", "label": "input", "config": {"allow": ["a", {"source": "^b\\."}]}}
|
||||
*
|
||||
* [foo][a.c]
|
||||
*
|
||||
* [bar][b]
|
||||
*
|
||||
* @example
|
||||
* {"name": "not-ok.md", "label": "output", "config": {"allow": ["a", {"source": "^b\\."}]}}
|
||||
*
|
||||
* 1:1-1:11: Found reference to undefined definition
|
||||
* 3:1-3:9: Found reference to undefined definition
|
||||
*/
|
||||
|
||||
/**
|
||||
@ -101,7 +126,7 @@
|
||||
* @typedef {import('mdast').Paragraph} Paragraph
|
||||
*
|
||||
* @typedef Options
|
||||
* @property {Array<string>} [allow]
|
||||
* @property {Array<string | RegExp | { source: string }>} [allow]
|
||||
*
|
||||
* @typedef {Array<number>} Range
|
||||
*/
|
||||
@ -123,12 +148,28 @@ const remarkLintNoUndefinedReferences = lintRule(
|
||||
const contents = String(file)
|
||||
const loc = location(file)
|
||||
const lineEnding = /(\r?\n|\r)[\t ]*(>[\t ]*)*/g
|
||||
const allow = new Set(
|
||||
(option.allow || []).map((d) => normalizeIdentifier(d))
|
||||
)
|
||||
/** @type {Record<string, boolean>} */
|
||||
const map = Object.create(null)
|
||||
|
||||
const allow = option.allow || []
|
||||
/** @type {Array<RegExp>} */
|
||||
const regexes = []
|
||||
/** @type {Set<string>} */
|
||||
const strings = new Set()
|
||||
|
||||
let index = -1
|
||||
|
||||
while (++index < allow.length) {
|
||||
const value = allow[index]
|
||||
if (typeof value === 'string') {
|
||||
strings.add(normalizeIdentifier(value))
|
||||
} else if (value instanceof RegExp) {
|
||||
regexes.push(value)
|
||||
} else {
|
||||
regexes.push(new RegExp(value.source, 'i'))
|
||||
}
|
||||
}
|
||||
|
||||
visit(tree, (node) => {
|
||||
if (
|
||||
(node.type === 'definition' || node.type === 'footnoteDefinition') &&
|
||||
@ -148,7 +189,7 @@ const remarkLintNoUndefinedReferences = lintRule(
|
||||
node.type === 'footnoteReference') &&
|
||||
!generated(node) &&
|
||||
!(normalizeIdentifier(node.identifier) in map) &&
|
||||
!allow.has(normalizeIdentifier(node.identifier))
|
||||
!isAllowed(node.identifier)
|
||||
) {
|
||||
file.message('Found reference to undefined definition', node)
|
||||
}
|
||||
@ -305,12 +346,24 @@ const remarkLintNoUndefinedReferences = lintRule(
|
||||
if (
|
||||
!generated({position: pos}) &&
|
||||
!(normalizeIdentifier(id) in map) &&
|
||||
!allow.has(normalizeIdentifier(id))
|
||||
!isAllowed(id)
|
||||
) {
|
||||
file.message('Found reference to undefined definition', pos)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} id
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function isAllowed(id) {
|
||||
const normalized = normalizeIdentifier(id)
|
||||
return (
|
||||
strings.has(normalized) ||
|
||||
regexes.some((regex) => regex.test(normalized))
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -125,9 +125,12 @@ This rule supports standard configuration that all remark lint rules accept
|
||||
The following options (default: `undefined`) are accepted:
|
||||
|
||||
* `Object` with the following fields:
|
||||
* `allow` (`Array<string>`, default: `[]`)
|
||||
— text that you want to allowed between `[` and `]` even though it’s
|
||||
undefined
|
||||
* `allow` (`Array<string | RegExp | { source: string }>`,
|
||||
default: `[]`)
|
||||
— text or regex that you want to be allowed between `[` and `]`
|
||||
even though it’s undefined; regex is provided via a `RegExp` object
|
||||
or via a `{ source: string }` object where `source` is the source
|
||||
text of a case-insensitive regex
|
||||
|
||||
## Recommendation
|
||||
|
||||
@ -227,6 +230,43 @@ When configured with `{ allow: [ '...', '…' ] }`.
|
||||
|
||||
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`
|
||||
|
||||
When configured with `{ allow: [ 'a', { source: '^b\\.' } ] }`.
|
||||
|
||||
###### In
|
||||
|
||||
```markdown
|
||||
[foo][a.c]
|
||||
|
||||
[bar][b]
|
||||
```
|
||||
|
||||
###### Out
|
||||
|
||||
```text
|
||||
1:1-1:11: Found reference to undefined definition
|
||||
3:1-3:9: Found reference to undefined definition
|
||||
```
|
||||
|
||||
## Compatibility
|
||||
|
||||
Projects maintained by the unified collective are compatible with all maintained
|
||||
|
16
test.js
16
test.js
@ -21,6 +21,7 @@ import {characters} from './script/characters.js'
|
||||
import lint from './packages/remark-lint/index.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
|
||||
@ -226,6 +227,21 @@ test('core', async (t) => {
|
||||
'should fail on incorrect severities (too low)'
|
||||
)
|
||||
|
||||
file = await remark()
|
||||
.use(noUndefinedReferences, {allow: [/^b\./i]})
|
||||
.process(
|
||||
toVFile({
|
||||
path: 'virtual.md',
|
||||
value: ['[foo][b.c]', '', '[bar][b]'].join('\n')
|
||||
})
|
||||
)
|
||||
|
||||
t.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) => {
|
||||
|
Loading…
Reference in New Issue
Block a user