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.0remark-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.
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.
npm install remark-lint-no-duplicate-definitionsimport remarkLintNoDuplicateDefinitions from 'remark-lint-no-duplicate-definitions';For CommonJS (not supported - ESM only):
// Not supported - package is ESM onlyComplete imports for typical usage:
import {unified} from 'unified';
import remarkParse from 'remark-parse';
import remarkLint from 'remark-lint';
import remarkLintNoDuplicateDefinitions from 'remark-lint-no-duplicate-definitions';
import remarkStringify from 'remark-stringify';
import {read} from 'to-vfile';
import {reporter} from 'vfile-reporter';import {unified} from 'unified';
import remarkParse from 'remark-parse';
import remarkLint from 'remark-lint';
import remarkLintNoDuplicateDefinitions from 'remark-lint-no-duplicate-definitions';
import remarkStringify from 'remark-stringify';
import {read} from 'to-vfile';
import {reporter} from 'vfile-reporter';
const file = await read('example.md');
await unified()
.use(remarkParse)
.use(remarkLint)
.use(remarkLintNoDuplicateDefinitions)
.use(remarkStringify)
.process(file);
console.error(reporter(file));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.
/**
* remark-lint rule to warn when identifiers are defined multiple times
* Created using unified-lint-rule wrapper with origin 'remark-lint:no-duplicate-definitions'
* @returns Transform function (Transformer from unified)
*/
function remarkLintNoDuplicateDefinitions(): Transformer;Parameters: None (the rule takes no configuration options)
Returns: Transform function that can be used with unified processors
Implementation: This plugin is created using the lintRule function from unified-lint-rule with the configuration:
'remark-lint:no-duplicate-definitions'Detection Capabilities:
[identifier]: url is defined multiple times[^identifier]: content is defined multiple times (GFM/GitHub Flavored Markdown)Error Messages:
identifier), expected unique identifiers"identifier), expected unique identifiers"[mercury]: https://example.com/mercury/
[venus]: https://example.com/venus/[mercury]: https://example.com/mercury/
[mercury]: https://example.com/venus/Warning: 2:1-2:38: Unexpected definition with an already defined identifier (mercury), expected unique identifiers
Mercury[^mercury].
[^mercury]:
Mercury is the first planet from the Sun and the smallest in the Solar System.
[^mercury]:
Venus is the second planet from the Sun.Warning: 7:1-7:12: Unexpected footnote definition with an already defined identifier (mercury), expected unique identifiers
remark --frail --use remark-lint --use remark-lint-no-duplicate-definitions .{
"remarkConfig": {
"plugins": [
"remark-lint",
"remark-lint-no-duplicate-definitions"
]
}
}This package is written in JavaScript but includes TypeScript type definitions (published as index.d.ts). It follows the unified ecosystem patterns:
// TypeScript definitions are available
import type {Transformer} from 'unified';
import type {Root} from 'mdast';
import type {Nodes} from 'mdast';
// The plugin function signature
function remarkLintNoDuplicateDefinitions(): Transformer<Root>;
// Internal types used by the implementation
type DefinitionNode = Extract<Nodes, {type: 'definition'}>;
type FootnoteDefinitionNode = Extract<Nodes, {type: 'footnoteDefinition'}>;The implementation uses these key type imports:
import type {Nodes, Root} from 'mdast';
// From the source code internal typesThe plugin creates VFileMessage objects with detailed information:
When duplicate definitions are found, the plugin creates messages with this structure:
// Primary error message
file.message(
'Unexpected [footnote ]definition with an already defined identifier (`identifier`), expected unique identifiers',
{
ancestors: [...parents, node], // Full ancestry chain
cause: new VFileMessage('Identifier already defined here', {
ancestors: duplicateAncestors, // Original definition ancestry
place: duplicate.position, // Original definition position
source: 'remark-lint',
ruleId: 'no-duplicate-definitions'
}),
place: node.position // Current duplicate position
}
);mdast-util-phrasing for optimal performancedefinition and footnoteDefinition nodesMap<string, Array<Nodes>> objects for regular definitions and footnote definitionsunist-util-visit-parents for efficient AST walking with ancestor trackingdevlop package for development-time assertionsThe implementation relies on these core dependencies:
import {ok as assert} from 'devlop'; // Development assertions
import {phrasing} from 'mdast-util-phrasing'; // Skip phrasing content
import {lintRule} from 'unified-lint-rule'; // Lint rule wrapper
import {SKIP, visitParents} from 'unist-util-visit-parents'; // AST traversal
import {VFileMessage} from 'vfile-message'; // Error message creationThis rule is part of the remark-preset-lint-recommended preset and integrates seamlessly with other remark-lint rules and the broader unified ecosystem.