remark-lint rule to warn when identifiers are defined multiple times in Markdown documents
npx @tessl/cli install tessl/npm-remark-lint-no-duplicate-definitions@3.1.00
# remark-lint-no-duplicate-definitions
1
2
remark-lint-no-duplicate-definitions is a remark-lint rule plugin that warns when identifiers are defined multiple times in Markdown documents. It integrates with the unified ecosystem to detect duplicate link reference definitions and footnote definitions, helping maintain clean and error-free Markdown by catching potential conflicts that could lead to ambiguous references.
3
4
This package is built using the `unified-lint-rule` wrapper and follows the standard remark-lint plugin patterns for consistent error reporting and integration with the remark processing pipeline.
5
6
## Package Information
7
8
- **Package Name**: remark-lint-no-duplicate-definitions
9
- **Package Type**: npm
10
- **Language**: JavaScript (ES modules)
11
- **Installation**: `npm install remark-lint-no-duplicate-definitions`
12
13
## Core Imports
14
15
```javascript
16
import remarkLintNoDuplicateDefinitions from 'remark-lint-no-duplicate-definitions';
17
```
18
19
For CommonJS (not supported - ESM only):
20
```javascript
21
// Not supported - package is ESM only
22
```
23
24
Complete imports for typical usage:
25
```javascript
26
import {unified} from 'unified';
27
import remarkParse from 'remark-parse';
28
import remarkLint from 'remark-lint';
29
import remarkLintNoDuplicateDefinitions from 'remark-lint-no-duplicate-definitions';
30
import remarkStringify from 'remark-stringify';
31
import {read} from 'to-vfile';
32
import {reporter} from 'vfile-reporter';
33
```
34
35
## Basic Usage
36
37
```javascript
38
import {unified} from 'unified';
39
import remarkParse from 'remark-parse';
40
import remarkLint from 'remark-lint';
41
import remarkLintNoDuplicateDefinitions from 'remark-lint-no-duplicate-definitions';
42
import remarkStringify from 'remark-stringify';
43
import {read} from 'to-vfile';
44
import {reporter} from 'vfile-reporter';
45
46
const file = await read('example.md');
47
48
await unified()
49
.use(remarkParse)
50
.use(remarkLint)
51
.use(remarkLintNoDuplicateDefinitions)
52
.use(remarkStringify)
53
.process(file);
54
55
console.error(reporter(file));
56
```
57
58
## Capabilities
59
60
### Lint Rule Plugin
61
62
The main and only export of this package is a unified plugin function created using `unified-lint-rule` that provides a remark-lint rule for detecting duplicate definitions.
63
64
```javascript { .api }
65
/**
66
* remark-lint rule to warn when identifiers are defined multiple times
67
* Created using unified-lint-rule wrapper with origin 'remark-lint:no-duplicate-definitions'
68
* @returns Transform function (Transformer from unified)
69
*/
70
function remarkLintNoDuplicateDefinitions(): Transformer;
71
```
72
73
**Parameters**: None (the rule takes no configuration options)
74
75
**Returns**: Transform function that can be used with unified processors
76
77
**Implementation**: This plugin is created using the `lintRule` function from `unified-lint-rule` with the configuration:
78
- **Origin**: `'remark-lint:no-duplicate-definitions'`
79
- **URL**: Documentation URL for the rule
80
- **Rule function**: Processes the markdown AST to detect duplicate definitions
81
82
**Detection Capabilities**:
83
- **Link reference definitions**: Detects when `[identifier]: url` is defined multiple times
84
- **Footnote definitions**: Detects when `[^identifier]: content` is defined multiple times (GFM/GitHub Flavored Markdown)
85
86
**Error Messages**:
87
- For duplicate link definitions: "Unexpected definition with an already defined identifier (`identifier`), expected unique identifiers"
88
- For duplicate footnote definitions: "Unexpected footnote definition with an already defined identifier (`identifier`), expected unique identifiers"
89
90
## Usage Examples
91
92
### Valid Markdown (No Warnings)
93
94
```markdown
95
[mercury]: https://example.com/mercury/
96
[venus]: https://example.com/venus/
97
```
98
99
### Invalid Markdown (Produces Warning)
100
101
```markdown
102
[mercury]: https://example.com/mercury/
103
[mercury]: https://example.com/venus/
104
```
105
106
Warning: `2:1-2:38: Unexpected definition with an already defined identifier (mercury), expected unique identifiers`
107
108
### Footnote Definitions (GFM)
109
110
```markdown
111
Mercury[^mercury].
112
113
[^mercury]:
114
Mercury is the first planet from the Sun and the smallest in the Solar System.
115
116
[^mercury]:
117
Venus is the second planet from the Sun.
118
```
119
120
Warning: `7:1-7:12: Unexpected footnote definition with an already defined identifier (mercury), expected unique identifiers`
121
122
## Integration
123
124
### CLI Usage
125
126
```bash
127
remark --frail --use remark-lint --use remark-lint-no-duplicate-definitions .
128
```
129
130
### Configuration File
131
132
```json
133
{
134
"remarkConfig": {
135
"plugins": [
136
"remark-lint",
137
"remark-lint-no-duplicate-definitions"
138
]
139
}
140
}
141
```
142
143
## Types
144
145
This package is written in JavaScript but includes TypeScript type definitions (published as `index.d.ts`). It follows the unified ecosystem patterns:
146
147
```typescript { .api }
148
// TypeScript definitions are available
149
import type {Transformer} from 'unified';
150
import type {Root} from 'mdast';
151
import type {Nodes} from 'mdast';
152
153
// The plugin function signature
154
function remarkLintNoDuplicateDefinitions(): Transformer<Root>;
155
156
// Internal types used by the implementation
157
type DefinitionNode = Extract<Nodes, {type: 'definition'}>;
158
type FootnoteDefinitionNode = Extract<Nodes, {type: 'footnoteDefinition'}>;
159
```
160
161
### Type Dependencies
162
163
The implementation uses these key type imports:
164
```typescript { .api }
165
import type {Nodes, Root} from 'mdast';
166
// From the source code internal types
167
```
168
169
## Error Handling
170
171
The plugin creates VFileMessage objects with detailed information:
172
- **Location**: Precise position information for both original and duplicate definitions
173
- **Context**: Full ancestor tree information for better error reporting
174
- **Cause**: References to the original definition location using nested VFileMessage
175
- **Source**: Always marked as 'remark-lint' with rule ID 'no-duplicate-definitions'
176
177
### VFileMessage Structure
178
179
When duplicate definitions are found, the plugin creates messages with this structure:
180
```javascript { .api }
181
// Primary error message
182
file.message(
183
'Unexpected [footnote ]definition with an already defined identifier (`identifier`), expected unique identifiers',
184
{
185
ancestors: [...parents, node], // Full ancestry chain
186
cause: new VFileMessage('Identifier already defined here', {
187
ancestors: duplicateAncestors, // Original definition ancestry
188
place: duplicate.position, // Original definition position
189
source: 'remark-lint',
190
ruleId: 'no-duplicate-definitions'
191
}),
192
place: node.position // Current duplicate position
193
}
194
);
195
```
196
197
## Implementation Notes
198
199
- **Performance**: Skips phrasing content during AST traversal using `mdast-util-phrasing` for optimal performance
200
- **Scope**: Only processes `definition` and `footnoteDefinition` nodes
201
- **Tracking**: Maintains separate `Map<string, Array<Nodes>>` objects for regular definitions and footnote definitions
202
- **Ancestry**: Preserves full parent chain information for detailed error reporting
203
- **Traversal**: Uses `unist-util-visit-parents` for efficient AST walking with ancestor tracking
204
- **Assertion**: Uses `devlop` package for development-time assertions
205
206
### Dependencies Used
207
208
The implementation relies on these core dependencies:
209
```javascript { .api }
210
import {ok as assert} from 'devlop'; // Development assertions
211
import {phrasing} from 'mdast-util-phrasing'; // Skip phrasing content
212
import {lintRule} from 'unified-lint-rule'; // Lint rule wrapper
213
import {SKIP, visitParents} from 'unist-util-visit-parents'; // AST traversal
214
import {VFileMessage} from 'vfile-message'; // Error message creation
215
```
216
217
This rule is part of the `remark-preset-lint-recommended` preset and integrates seamlessly with other remark-lint rules and the broader unified ecosystem.