Skip to content

Commit

Permalink
no-undefined-references: add support for regex as option
Browse files Browse the repository at this point in the history
Closes GH-296.
Closes GH-297.

Reviewed-by: JounQin <admin@1stg.me>
Reviewed-by: Titus Wormer <tituswormer@gmail.com>
  • Loading branch information
sisp authored Sep 9, 2022
1 parent 3704121 commit 1ab5509
Show file tree
Hide file tree
Showing 3 changed files with 121 additions and 12 deletions.
71 changes: 62 additions & 9 deletions packages/remark-lint-no-undefined-references/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
*
Expand Down Expand Up @@ -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]
Expand Down Expand Up @@ -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
*/

/**
Expand All @@ -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
*/
Expand All @@ -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') &&
Expand All @@ -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)
}
Expand Down Expand Up @@ -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))
)
}
}
)

Expand Down
46 changes: 43 additions & 3 deletions packages/remark-lint-no-undefined-references/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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
Expand Down
16 changes: 16 additions & 0 deletions test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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) => {
Expand Down

0 comments on commit 1ab5509

Please sign in to comment.