@depup/eslint-plugin-jsdoc 63.2.2-depup.0 → 63.3.0-depup.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -13,15 +13,16 @@ npm install @depup/eslint-plugin-jsdoc
13
13
 
14
14
  | Field | Value |
15
15
  |-------|-------|
16
- | Original | [eslint-plugin-jsdoc](https://www.npmjs.com/package/eslint-plugin-jsdoc) @ 63.2.2 |
17
- | Processed | 2026-07-24 |
16
+ | Original | [eslint-plugin-jsdoc](https://www.npmjs.com/package/eslint-plugin-jsdoc) @ 63.3.0 |
17
+ | Processed | 2026-07-26 |
18
18
  | Smoke test | passed |
19
- | Deps updated | 2 |
19
+ | Deps updated | 3 |
20
20
 
21
21
  ## Dependency Changes
22
22
 
23
23
  | Dependency | From | To |
24
24
  |------------|------|-----|
25
+ | @es-joy/jsdoccomment | ~0.90.0 | ^0.91.0 |
25
26
  | are-docs-informative | ^0.0.2 | ^0.1.1 |
26
27
  | escape-string-regexp | ^4.0.0 | ^5.0.0 |
27
28
 
package/changes.json CHANGED
@@ -1,5 +1,9 @@
1
1
  {
2
2
  "bumped": {
3
+ "@es-joy/jsdoccomment": {
4
+ "from": "~0.90.0",
5
+ "to": "^0.91.0"
6
+ },
3
7
  "are-docs-informative": {
4
8
  "from": "^0.0.2",
5
9
  "to": "^0.1.1"
@@ -9,6 +13,6 @@
9
13
  "to": "^5.0.0"
10
14
  }
11
15
  },
12
- "timestamp": "2026-07-24T08:54:29.430Z",
13
- "totalUpdated": 2
16
+ "timestamp": "2026-07-26T08:49:28.747Z",
17
+ "totalUpdated": 3
14
18
  }
@@ -6,9 +6,9 @@ Object.defineProperty(exports, "__esModule", {
6
6
  exports.default = void 0;
7
7
  var _iterateJsdoc = _interopRequireDefault(require("../iterateJsdoc.cjs"));
8
8
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
9
- const markdownLinkRegex = /(?<!\\)\[([^`\]\r\n]+)\]\(([^`\(\)\s]+)\)/gv;
10
- const prefixLinkRegex = /(?<!\\)\[([^\]\r\n]+)\]\{@link\s+([^\}\s\|]+)\}/gv;
11
- const pipeLinkRegex = /\{@link\s+([^\}\s\|]+)\s*\|\s*([^\}\r\n]+)\}/gv;
9
+ const markdownLinkRegex = /(?<!\\)\[((?:[^\\`\]\r\n]|\\[^\r\n])+)\]\(([^`\(\)\s]+)\)/gv;
10
+ const prefixLinkRegex = /(?<!\\)\[((?:[^\\\]\r\n]|\\[^\r\n])+)\]\{@link\s+([^\}\s\|]+)\}/gv;
11
+ const pipeLinkRegex = /\{@link\s+([^\}\s\|]+)\s*\|\s*((?:[^\\\}\r\n]|\\[^\r\n])+)\}/gv;
12
12
  const markdownLinkAttemptRegex = /(?<!\\)\[[^\]\r\n]*\]\([^\r\n]*(?:\)|$)/v;
13
13
  const prefixLinkAttemptRegex = /(?<!\\)\[[^\]\r\n]*\]\{@link[^\}\r\n]*(?:\}|$)/v;
14
14
  const pipeLinkAttemptRegex = /\{@link[^\}\r\n]*\|[^\}\r\n]*(?:\}|$)/v;
@@ -17,18 +17,26 @@ const markdownCodeSpanRegex = /(?<!\\)(`+)(?!`)[\s\S]*?(?<!`)\1(?!`)/gv;
17
17
  const bareHttpUrlRegex = /^https?:\S+$/v;
18
18
 
19
19
  /**
20
- * @param {'pipe'|'prefix'} canonicalForm
21
20
  * @param {string} label
22
21
  * @returns {boolean}
23
22
  */
24
- const cannotSafelyFormatLink = (canonicalForm, label) => {
25
- if (!label.trim()) {
26
- return true;
27
- }
28
- if (canonicalForm === 'pipe') {
29
- return label.includes('}');
30
- }
31
- return label.includes(']');
23
+ const cannotSafelyFormatLink = label => {
24
+ return !label.trim();
25
+ };
26
+
27
+ /**
28
+ * @param {'pipe'|'prefix'} canonicalForm
29
+ * @param {string} label
30
+ * @param {'markdown'|'pipe'|'prefix'} sourceForm
31
+ * @returns {string}
32
+ */
33
+ const escapeLinkLabel = (canonicalForm, label, sourceForm) => {
34
+ const sourceEscapePattern = sourceForm === 'pipe' ? /\\([\\\}])/gv : sourceForm === 'prefix' ? /\\([\\\]])/gv : /\\([\\\}\]])/gv;
35
+ const targetEscapePattern = canonicalForm === 'pipe' ? /\\(?=$|[\\\}])|\}/gv : /\\(?=$|[\\\]])|\]/gv;
36
+ const decodedLabel = label.trim().replaceAll(sourceEscapePattern, '$1');
37
+ return decodedLabel.replaceAll(targetEscapePattern, character => {
38
+ return `\\${character}`;
39
+ });
32
40
  };
33
41
 
34
42
  /**
@@ -89,12 +97,14 @@ const isInsideMarkdownCodeSpan = (description, index) => {
89
97
  /**
90
98
  * @param {'pipe'|'prefix'} canonicalForm
91
99
  * @param {string} label
100
+ * @param {'markdown'|'pipe'|'prefix'} sourceForm
92
101
  * @param {string} target
93
102
  * @returns {string}
94
103
  */
95
- const formatLink = (canonicalForm, label, target) => {
104
+ const formatLink = (canonicalForm, label, sourceForm, target) => {
96
105
  const encodedTarget = encodeLinkTarget(target);
97
- return canonicalForm === 'pipe' ? `{@link ${encodedTarget}|${label.trim()}}` : `[${label.trim()}]{@link ${encodedTarget}}`;
106
+ const escapedLabel = escapeLinkLabel(canonicalForm, label, sourceForm);
107
+ return canonicalForm === 'pipe' ? `{@link ${encodedTarget}|${escapedLabel}}` : `[${escapedLabel}]{@link ${encodedTarget}}`;
98
108
  };
99
109
 
100
110
  /**
@@ -104,15 +114,15 @@ const formatLink = (canonicalForm, label, target) => {
104
114
  */
105
115
  const normalizeDescription = (description, canonicalForm) => {
106
116
  const markdownNormalized = description.replaceAll(new RegExp(markdownLinkRegex, 'gv'), (_match, label, target) => {
107
- return formatLink(canonicalForm, label, target);
117
+ return formatLink(canonicalForm, label, 'markdown', target);
108
118
  });
109
119
  if (canonicalForm === 'pipe') {
110
120
  return markdownNormalized.replaceAll(new RegExp(prefixLinkRegex, 'gv'), (_match, label, target) => {
111
- return formatLink(canonicalForm, label, target);
121
+ return formatLink(canonicalForm, label, 'prefix', target);
112
122
  });
113
123
  }
114
124
  return markdownNormalized.replaceAll(new RegExp(pipeLinkRegex, 'gv'), (_match, target, label) => {
115
- return formatLink(canonicalForm, label, target);
125
+ return formatLink(canonicalForm, label, 'pipe', target);
116
126
  });
117
127
  };
118
128
  var _default = exports.default = (0, _iterateJsdoc.default)(({
@@ -148,7 +158,7 @@ var _default = exports.default = (0, _iterateJsdoc.default)(({
148
158
  for (const match of rawDescription.matchAll(markdownLinkRegex)) {
149
159
  const label = match[1];
150
160
  const target = match[2];
151
- if (isInsideMarkdownCodeSpan(rawDescription, match.index) || scopedPackageNameRegex.test(target) || cannotSafelyFormatLink(canonicalForm, label)) {
161
+ if (isInsideMarkdownCodeSpan(rawDescription, match.index) || scopedPackageNameRegex.test(target) || cannotSafelyFormatLink(label)) {
152
162
  hasAmbiguousLink = true;
153
163
  } else {
154
164
  hasNoncanonicalLink = true;
@@ -162,7 +172,7 @@ var _default = exports.default = (0, _iterateJsdoc.default)(({
162
172
  hasAmbiguousLink = true;
163
173
  continue;
164
174
  }
165
- if (cannotSafelyFormatLink(canonicalForm, inlineTag.text)) {
175
+ if (cannotSafelyFormatLink(inlineTag.text)) {
166
176
  hasAmbiguousLink = true;
167
177
  continue;
168
178
  }
@@ -1 +1 @@
1
- {"version":3,"file":"normalizeSeeLinks.cjs","names":["iterateJsdoc"],"sources":["../../src/rules/normalizeSeeLinks.js"],"sourcesContent":["import iterateJsdoc from '../iterateJsdoc.js';\n\nconst markdownLinkRegex = /(?<!\\\\)\\[([^`\\]\\r\\n]+)\\]\\(([^`\\(\\)\\s]+)\\)/gv;\nconst prefixLinkRegex = /(?<!\\\\)\\[([^\\]\\r\\n]+)\\]\\{@link\\s+([^\\}\\s\\|]+)\\}/gv;\nconst pipeLinkRegex = /\\{@link\\s+([^\\}\\s\\|]+)\\s*\\|\\s*([^\\}\\r\\n]+)\\}/gv;\n\nconst markdownLinkAttemptRegex = /(?<!\\\\)\\[[^\\]\\r\\n]*\\]\\([^\\r\\n]*(?:\\)|$)/v;\nconst prefixLinkAttemptRegex = /(?<!\\\\)\\[[^\\]\\r\\n]*\\]\\{@link[^\\}\\r\\n]*(?:\\}|$)/v;\nconst pipeLinkAttemptRegex = /\\{@link[^\\}\\r\\n]*\\|[^\\}\\r\\n]*(?:\\}|$)/v;\n\nconst scopedPackageNameRegex = /^@[\\w.\\-]+\\/[\\w.\\-]+/v;\nconst markdownCodeSpanRegex = /(?<!\\\\)(`+)(?!`)[\\s\\S]*?(?<!`)\\1(?!`)/gv;\nconst bareHttpUrlRegex = /^https?:\\S+$/v;\n\n/**\n * @param {'pipe'|'prefix'} canonicalForm\n * @param {string} label\n * @returns {boolean}\n */\nconst cannotSafelyFormatLink = (canonicalForm, label) => {\n if (!label.trim()) {\n return true;\n }\n\n if (canonicalForm === 'pipe') {\n return label.includes('}');\n }\n\n return label.includes(']');\n};\n\n/**\n * @param {string} target\n * @returns {string}\n */\nconst encodeLinkTarget = (target) => {\n return encodeURI(target).replaceAll(/%25(?=[\\dA-F]{2})/giv, '%');\n};\n\n/**\n * @param {string} description\n * @param {boolean} enabled\n * @returns {string|null}\n */\nconst getBareHttpUrl = (description, enabled) => {\n if (!enabled) {\n return null;\n }\n\n const trimmedDescription = description.trim();\n\n return bareHttpUrlRegex.test(trimmedDescription) &&\n URL.canParse(trimmedDescription) ?\n trimmedDescription : null;\n};\n\n/**\n * @param {string} target\n * @returns {string}\n */\nconst formatBareUrl = (target) => {\n return `{@link ${encodeLinkTarget(target)}}`;\n};\n\n/**\n * @param {string|undefined} bareUrl\n * @param {'pipe'|'prefix'} canonicalForm\n * @returns {string}\n */\nconst getExpectedMessage = (bareUrl, canonicalForm) => {\n return bareUrl ?\n 'Expected bare @see URL to use a {@link} tag.' :\n `Expected @see link to use the ${canonicalForm} form.`;\n};\n\n/**\n * @param {string} description\n * @param {number} index\n * @returns {boolean}\n */\nconst isInsideMarkdownCodeSpan = (description, index) => {\n for (const match of description.matchAll(markdownCodeSpanRegex)) {\n const delimiterLength = match[1].length;\n const contentStart = match.index + delimiterLength;\n const contentEnd = match.index + match[0].length - delimiterLength;\n\n if (index >= contentStart && index < contentEnd) {\n return true;\n }\n }\n\n return false;\n};\n\n/**\n * @param {'pipe'|'prefix'} canonicalForm\n * @param {string} label\n * @param {string} target\n * @returns {string}\n */\nconst formatLink = (canonicalForm, label, target) => {\n const encodedTarget = encodeLinkTarget(target);\n\n return canonicalForm === 'pipe' ?\n `{@link ${encodedTarget}|${label.trim()}}` :\n `[${label.trim()}]{@link ${encodedTarget}}`;\n};\n\n/**\n * @param {string} description\n * @param {'pipe'|'prefix'} canonicalForm\n * @returns {string}\n */\nconst normalizeDescription = (description, canonicalForm) => {\n const markdownNormalized = description.replaceAll(\n new RegExp(markdownLinkRegex, 'gv'),\n (_match, label, target) => {\n return formatLink(canonicalForm, label, target);\n },\n );\n\n if (canonicalForm === 'pipe') {\n return markdownNormalized.replaceAll(\n new RegExp(prefixLinkRegex, 'gv'),\n (_match, label, target) => {\n return formatLink(canonicalForm, label, target);\n },\n );\n }\n\n return markdownNormalized.replaceAll(\n new RegExp(pipeLinkRegex, 'gv'),\n (_match, target, label) => {\n return formatLink(canonicalForm, label, target);\n },\n );\n};\n\nexport default iterateJsdoc(({\n context,\n jsdoc,\n utils,\n}) => {\n const {\n canonicalForm = 'pipe',\n enableFixer = true,\n wrapBareUrls = false,\n } = context.options[0] || {};\n\n const descriptionMatcher = canonicalForm === 'pipe' ?\n new RegExp(`${markdownLinkRegex.source}|${prefixLinkRegex.source}`, 'v') :\n new RegExp(`${markdownLinkRegex.source}|${pipeLinkRegex.source}`, 'v');\n\n /** @type {import('comment-parser').Spec[]} */\n const fixableTags = [];\n /** @type {Map<import('comment-parser').Spec, string>} */\n const bareUrlTags = new Map();\n\n for (const tag of jsdoc.tags) {\n if (tag.tag !== 'see') {\n continue;\n }\n\n const firstTokens = tag.source[0].tokens;\n const tagDescription = String(utils.getTagDescription(tag));\n const rawDescription = firstTokens.name ?\n firstTokens.name + firstTokens.postName + tagDescription :\n tagDescription;\n\n let hasAmbiguousLink = false;\n let hasNoncanonicalLink = false;\n const bareUrl = getBareHttpUrl(rawDescription, wrapBareUrls);\n\n if (bareUrl) {\n bareUrlTags.set(tag, bareUrl);\n hasNoncanonicalLink = true;\n }\n\n for (const match of rawDescription.matchAll(markdownLinkRegex)) {\n const label = match[1];\n const target = match[2];\n\n if (\n isInsideMarkdownCodeSpan(rawDescription, match.index) ||\n scopedPackageNameRegex.test(target) ||\n cannotSafelyFormatLink(canonicalForm, label)\n ) {\n hasAmbiguousLink = true;\n } else {\n hasNoncanonicalLink = true;\n }\n }\n\n for (const inlineTag of tag.inlineTags) {\n if (inlineTag.tag !== 'link' || !inlineTag.text) {\n continue;\n }\n\n if (inlineTag.format === 'space') {\n hasAmbiguousLink = true;\n continue;\n }\n\n if (cannotSafelyFormatLink(\n canonicalForm,\n inlineTag.text,\n )) {\n hasAmbiguousLink = true;\n continue;\n }\n\n if (inlineTag.format === canonicalForm) {\n continue;\n }\n\n const {\n start,\n } = /** @type {typeof inlineTag & {start: number}} */ (inlineTag);\n\n if (\n isInsideMarkdownCodeSpan(rawDescription, start) ||\n scopedPackageNameRegex.test(inlineTag.namepathOrURL)\n ) {\n hasAmbiguousLink = true;\n } else {\n hasNoncanonicalLink = true;\n }\n }\n\n const unrecognizedLinkText = rawDescription\n .replaceAll(new RegExp(markdownLinkRegex, 'gv'), '')\n .replaceAll(new RegExp(prefixLinkRegex, 'gv'), '')\n .replaceAll(new RegExp(pipeLinkRegex, 'gv'), '');\n\n hasAmbiguousLink ||= markdownLinkAttemptRegex.test(unrecognizedLinkText) ||\n prefixLinkAttemptRegex.test(unrecognizedLinkText) ||\n pipeLinkAttemptRegex.test(unrecognizedLinkText);\n\n if (hasAmbiguousLink) {\n utils.reportJSDoc('@see link cannot be safely normalized.', {\n line: tag.source[0].number,\n }, null);\n continue;\n }\n\n if (!hasNoncanonicalLink) {\n continue;\n }\n\n fixableTags.push(tag);\n }\n\n for (const [\n reportIdx,\n tag,\n ] of fixableTags.entries()) {\n const bareUrl = bareUrlTags.get(tag);\n\n utils.reportJSDoc(\n getExpectedMessage(bareUrl, canonicalForm),\n {\n line: tag.source[0].number,\n },\n enableFixer && reportIdx === 0 ?\n () => {\n for (const fixableTag of fixableTags) {\n const firstTokens = fixableTag.source[0].tokens;\n\n if (firstTokens.name) {\n firstTokens.description = firstTokens.name +\n firstTokens.postName + firstTokens.description;\n firstTokens.name = '';\n firstTokens.postName = '';\n }\n\n const fixableBareUrl = bareUrlTags.get(fixableTag);\n\n if (fixableBareUrl) {\n for (const source of fixableTag.source) {\n if (!source.tokens.description.includes(fixableBareUrl)) {\n continue;\n }\n\n source.tokens.description = source.tokens.description.replace(\n fixableBareUrl,\n () => {\n return formatBareUrl(fixableBareUrl);\n },\n );\n break;\n }\n\n continue;\n }\n\n for (\n let sourceIdx = 0;\n sourceIdx < fixableTag.source.length;\n sourceIdx++\n ) {\n if (!descriptionMatcher.test(\n fixableTag.source[sourceIdx].tokens.description,\n )) {\n continue;\n }\n\n utils.setTagDescription(\n fixableTag,\n descriptionMatcher,\n (description) => {\n return normalizeDescription(description, canonicalForm);\n },\n );\n }\n }\n } :\n null,\n true,\n );\n }\n}, {\n iterateAllJsdocs: true,\n meta: {\n docs: {\n description: 'Normalizes labeled links in `@see` tags to a canonical `{@link}` form.',\n url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/normalize-see-links.md#repos-sticky-header',\n },\n fixable: 'code',\n schema: [\n {\n additionalProperties: false,\n properties: {\n canonicalForm: {\n description: 'The canonical `{@link}` form: `\"pipe\"` produces `{@link url|label}`, while `\"prefix\"` produces `[label]{@link url}`. Defaults to `\"pipe\"`.',\n enum: [\n 'pipe',\n 'prefix',\n ],\n type: 'string',\n },\n enableFixer: {\n description: 'Whether to enable the fixer. Defaults to `true`.',\n type: 'boolean',\n },\n wrapBareUrls: {\n description: 'Whether to wrap an `@see` description containing only a lowercase `http:` or `https:` URL (for example, `@see https://example.com`) in a plain `{@link https://example.com}`. Defaults to `false`.',\n type: 'boolean',\n },\n },\n type: 'object',\n },\n ],\n type: 'suggestion',\n },\n});\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"file":"normalizeSeeLinks.cjs","names":["iterateJsdoc"],"sources":["../../src/rules/normalizeSeeLinks.js"],"sourcesContent":["import iterateJsdoc from '../iterateJsdoc.js';\n\nconst markdownLinkRegex = /(?<!\\\\)\\[((?:[^\\\\`\\]\\r\\n]|\\\\[^\\r\\n])+)\\]\\(([^`\\(\\)\\s]+)\\)/gv;\nconst prefixLinkRegex = /(?<!\\\\)\\[((?:[^\\\\\\]\\r\\n]|\\\\[^\\r\\n])+)\\]\\{@link\\s+([^\\}\\s\\|]+)\\}/gv;\nconst pipeLinkRegex = /\\{@link\\s+([^\\}\\s\\|]+)\\s*\\|\\s*((?:[^\\\\\\}\\r\\n]|\\\\[^\\r\\n])+)\\}/gv;\n\nconst markdownLinkAttemptRegex = /(?<!\\\\)\\[[^\\]\\r\\n]*\\]\\([^\\r\\n]*(?:\\)|$)/v;\nconst prefixLinkAttemptRegex = /(?<!\\\\)\\[[^\\]\\r\\n]*\\]\\{@link[^\\}\\r\\n]*(?:\\}|$)/v;\nconst pipeLinkAttemptRegex = /\\{@link[^\\}\\r\\n]*\\|[^\\}\\r\\n]*(?:\\}|$)/v;\n\nconst scopedPackageNameRegex = /^@[\\w.\\-]+\\/[\\w.\\-]+/v;\nconst markdownCodeSpanRegex = /(?<!\\\\)(`+)(?!`)[\\s\\S]*?(?<!`)\\1(?!`)/gv;\nconst bareHttpUrlRegex = /^https?:\\S+$/v;\n\n/**\n * @param {string} label\n * @returns {boolean}\n */\nconst cannotSafelyFormatLink = (label) => {\n return !label.trim();\n};\n\n/**\n * @param {'pipe'|'prefix'} canonicalForm\n * @param {string} label\n * @param {'markdown'|'pipe'|'prefix'} sourceForm\n * @returns {string}\n */\nconst escapeLinkLabel = (canonicalForm, label, sourceForm) => {\n const sourceEscapePattern = sourceForm === 'pipe' ?\n /\\\\([\\\\\\}])/gv :\n (sourceForm === 'prefix' ?\n /\\\\([\\\\\\]])/gv :\n /\\\\([\\\\\\}\\]])/gv);\n const targetEscapePattern = canonicalForm === 'pipe' ?\n /\\\\(?=$|[\\\\\\}])|\\}/gv :\n /\\\\(?=$|[\\\\\\]])|\\]/gv;\n const decodedLabel = label.trim().replaceAll(sourceEscapePattern, '$1');\n\n return decodedLabel.replaceAll(targetEscapePattern, (character) => {\n return `\\\\${character}`;\n });\n};\n\n/**\n * @param {string} target\n * @returns {string}\n */\nconst encodeLinkTarget = (target) => {\n return encodeURI(target).replaceAll(/%25(?=[\\dA-F]{2})/giv, '%');\n};\n\n/**\n * @param {string} description\n * @param {boolean} enabled\n * @returns {string|null}\n */\nconst getBareHttpUrl = (description, enabled) => {\n if (!enabled) {\n return null;\n }\n\n const trimmedDescription = description.trim();\n\n return bareHttpUrlRegex.test(trimmedDescription) &&\n URL.canParse(trimmedDescription) ?\n trimmedDescription : null;\n};\n\n/**\n * @param {string} target\n * @returns {string}\n */\nconst formatBareUrl = (target) => {\n return `{@link ${encodeLinkTarget(target)}}`;\n};\n\n/**\n * @param {string|undefined} bareUrl\n * @param {'pipe'|'prefix'} canonicalForm\n * @returns {string}\n */\nconst getExpectedMessage = (bareUrl, canonicalForm) => {\n return bareUrl ?\n 'Expected bare @see URL to use a {@link} tag.' :\n `Expected @see link to use the ${canonicalForm} form.`;\n};\n\n/**\n * @param {string} description\n * @param {number} index\n * @returns {boolean}\n */\nconst isInsideMarkdownCodeSpan = (description, index) => {\n for (const match of description.matchAll(markdownCodeSpanRegex)) {\n const delimiterLength = match[1].length;\n const contentStart = match.index + delimiterLength;\n const contentEnd = match.index + match[0].length - delimiterLength;\n\n if (index >= contentStart && index < contentEnd) {\n return true;\n }\n }\n\n return false;\n};\n\n/**\n * @param {'pipe'|'prefix'} canonicalForm\n * @param {string} label\n * @param {'markdown'|'pipe'|'prefix'} sourceForm\n * @param {string} target\n * @returns {string}\n */\nconst formatLink = (canonicalForm, label, sourceForm, target) => {\n const encodedTarget = encodeLinkTarget(target);\n const escapedLabel = escapeLinkLabel(canonicalForm, label, sourceForm);\n\n return canonicalForm === 'pipe' ?\n `{@link ${encodedTarget}|${escapedLabel}}` :\n `[${escapedLabel}]{@link ${encodedTarget}}`;\n};\n\n/**\n * @param {string} description\n * @param {'pipe'|'prefix'} canonicalForm\n * @returns {string}\n */\nconst normalizeDescription = (description, canonicalForm) => {\n const markdownNormalized = description.replaceAll(\n new RegExp(markdownLinkRegex, 'gv'),\n (_match, label, target) => {\n return formatLink(canonicalForm, label, 'markdown', target);\n },\n );\n\n if (canonicalForm === 'pipe') {\n return markdownNormalized.replaceAll(\n new RegExp(prefixLinkRegex, 'gv'),\n (_match, label, target) => {\n return formatLink(canonicalForm, label, 'prefix', target);\n },\n );\n }\n\n return markdownNormalized.replaceAll(\n new RegExp(pipeLinkRegex, 'gv'),\n (_match, target, label) => {\n return formatLink(canonicalForm, label, 'pipe', target);\n },\n );\n};\n\nexport default iterateJsdoc(({\n context,\n jsdoc,\n utils,\n}) => {\n const {\n canonicalForm = 'pipe',\n enableFixer = true,\n wrapBareUrls = false,\n } = context.options[0] || {};\n\n const descriptionMatcher = canonicalForm === 'pipe' ?\n new RegExp(`${markdownLinkRegex.source}|${prefixLinkRegex.source}`, 'v') :\n new RegExp(`${markdownLinkRegex.source}|${pipeLinkRegex.source}`, 'v');\n\n /** @type {import('comment-parser').Spec[]} */\n const fixableTags = [];\n /** @type {Map<import('comment-parser').Spec, string>} */\n const bareUrlTags = new Map();\n\n for (const tag of jsdoc.tags) {\n if (tag.tag !== 'see') {\n continue;\n }\n\n const firstTokens = tag.source[0].tokens;\n const tagDescription = String(utils.getTagDescription(tag));\n const rawDescription = firstTokens.name ?\n firstTokens.name + firstTokens.postName + tagDescription :\n tagDescription;\n\n let hasAmbiguousLink = false;\n let hasNoncanonicalLink = false;\n const bareUrl = getBareHttpUrl(rawDescription, wrapBareUrls);\n\n if (bareUrl) {\n bareUrlTags.set(tag, bareUrl);\n hasNoncanonicalLink = true;\n }\n\n for (const match of rawDescription.matchAll(markdownLinkRegex)) {\n const label = match[1];\n const target = match[2];\n\n if (\n isInsideMarkdownCodeSpan(rawDescription, match.index) ||\n scopedPackageNameRegex.test(target) ||\n cannotSafelyFormatLink(label)\n ) {\n hasAmbiguousLink = true;\n } else {\n hasNoncanonicalLink = true;\n }\n }\n\n for (const inlineTag of tag.inlineTags) {\n if (inlineTag.tag !== 'link' || !inlineTag.text) {\n continue;\n }\n\n if (inlineTag.format === 'space') {\n hasAmbiguousLink = true;\n continue;\n }\n\n if (cannotSafelyFormatLink(inlineTag.text)) {\n hasAmbiguousLink = true;\n continue;\n }\n\n if (inlineTag.format === canonicalForm) {\n continue;\n }\n\n const {\n start,\n } = /** @type {typeof inlineTag & {start: number}} */ (inlineTag);\n\n if (\n isInsideMarkdownCodeSpan(rawDescription, start) ||\n scopedPackageNameRegex.test(inlineTag.namepathOrURL)\n ) {\n hasAmbiguousLink = true;\n } else {\n hasNoncanonicalLink = true;\n }\n }\n\n const unrecognizedLinkText = rawDescription\n .replaceAll(new RegExp(markdownLinkRegex, 'gv'), '')\n .replaceAll(new RegExp(prefixLinkRegex, 'gv'), '')\n .replaceAll(new RegExp(pipeLinkRegex, 'gv'), '');\n\n hasAmbiguousLink ||= markdownLinkAttemptRegex.test(unrecognizedLinkText) ||\n prefixLinkAttemptRegex.test(unrecognizedLinkText) ||\n pipeLinkAttemptRegex.test(unrecognizedLinkText);\n\n if (hasAmbiguousLink) {\n utils.reportJSDoc('@see link cannot be safely normalized.', {\n line: tag.source[0].number,\n }, null);\n continue;\n }\n\n if (!hasNoncanonicalLink) {\n continue;\n }\n\n fixableTags.push(tag);\n }\n\n for (const [\n reportIdx,\n tag,\n ] of fixableTags.entries()) {\n const bareUrl = bareUrlTags.get(tag);\n\n utils.reportJSDoc(\n getExpectedMessage(bareUrl, canonicalForm),\n {\n line: tag.source[0].number,\n },\n enableFixer && reportIdx === 0 ?\n () => {\n for (const fixableTag of fixableTags) {\n const firstTokens = fixableTag.source[0].tokens;\n\n if (firstTokens.name) {\n firstTokens.description = firstTokens.name +\n firstTokens.postName + firstTokens.description;\n firstTokens.name = '';\n firstTokens.postName = '';\n }\n\n const fixableBareUrl = bareUrlTags.get(fixableTag);\n\n if (fixableBareUrl) {\n for (const source of fixableTag.source) {\n if (!source.tokens.description.includes(fixableBareUrl)) {\n continue;\n }\n\n source.tokens.description = source.tokens.description.replace(\n fixableBareUrl,\n () => {\n return formatBareUrl(fixableBareUrl);\n },\n );\n break;\n }\n\n continue;\n }\n\n for (\n let sourceIdx = 0;\n sourceIdx < fixableTag.source.length;\n sourceIdx++\n ) {\n if (!descriptionMatcher.test(\n fixableTag.source[sourceIdx].tokens.description,\n )) {\n continue;\n }\n\n utils.setTagDescription(\n fixableTag,\n descriptionMatcher,\n (description) => {\n return normalizeDescription(description, canonicalForm);\n },\n );\n }\n }\n } :\n null,\n true,\n );\n }\n}, {\n iterateAllJsdocs: true,\n meta: {\n docs: {\n description: 'Normalizes labeled links in `@see` tags to a canonical `{@link}` form.',\n url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/normalize-see-links.md#repos-sticky-header',\n },\n fixable: 'code',\n schema: [\n {\n additionalProperties: false,\n properties: {\n canonicalForm: {\n description: 'The canonical `{@link}` form: `\"pipe\"` produces `{@link url|label}`, while `\"prefix\"` produces `[label]{@link url}`. Defaults to `\"pipe\"`.',\n enum: [\n 'pipe',\n 'prefix',\n ],\n type: 'string',\n },\n enableFixer: {\n description: 'Whether to enable the fixer. Defaults to `true`.',\n type: 'boolean',\n },\n wrapBareUrls: {\n description: 'Whether to wrap an `@see` description containing only a lowercase `http:` or `https:` URL (for example, `@see https://example.com`) in a plain `{@link https://example.com}`. Defaults to `false`.',\n type: 'boolean',\n },\n },\n type: 'object',\n },\n ],\n type: 'suggestion',\n },\n});\n"],"mappings":"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","ignoreList":[]}
package/package.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "url": "http://gajus.com"
6
6
  },
7
7
  "dependencies": {
8
- "@es-joy/jsdoccomment": "~0.90.0",
8
+ "@es-joy/jsdoccomment": "^0.91.0",
9
9
  "@es-joy/resolve.exports": "1.2.0",
10
10
  "are-docs-informative": "^0.1.1",
11
11
  "comment-parser": "1.4.7",
@@ -177,6 +177,7 @@
177
177
  "create-rule": "node ./src/bin/generateRule.js",
178
178
  "create-options": "node ./src/bin/generateOptions.js",
179
179
  "install-offline": "CI=1 pnpm install --prefer-offline --no-frozen-lockfile",
180
+ "inspect-config": "eslint --inspect-config",
180
181
  "lint": "eslint",
181
182
  "lint-fix": "eslint --fix",
182
183
  "test-no-cov": "BABEL_ENV=test mocha",
@@ -184,9 +185,13 @@
184
185
  "test-cov": "TIMING=1 c8 --reporter text pnpm run test-no-cov",
185
186
  "test-index": "pnpm run test-no-cov test/rules/index.js"
186
187
  },
187
- "version": "63.2.2-depup.0",
188
+ "version": "63.3.0-depup.0",
188
189
  "depup": {
189
190
  "changes": {
191
+ "@es-joy/jsdoccomment": {
192
+ "from": "~0.90.0",
193
+ "to": "^0.91.0"
194
+ },
190
195
  "are-docs-informative": {
191
196
  "from": "^0.0.2",
192
197
  "to": "^0.1.1"
@@ -196,10 +201,10 @@
196
201
  "to": "^5.0.0"
197
202
  }
198
203
  },
199
- "depsUpdated": 2,
204
+ "depsUpdated": 3,
200
205
  "originalPackage": "eslint-plugin-jsdoc",
201
- "originalVersion": "63.2.2",
202
- "processedAt": "2026-07-24T08:54:54.216Z",
206
+ "originalVersion": "63.3.0",
207
+ "processedAt": "2026-07-26T08:49:54.272Z",
203
208
  "smokeTest": "passed"
204
209
  }
205
210
  }
@@ -1,8 +1,8 @@
1
1
  import iterateJsdoc from '../iterateJsdoc.js';
2
2
 
3
- const markdownLinkRegex = /(?<!\\)\[([^`\]\r\n]+)\]\(([^`\(\)\s]+)\)/gv;
4
- const prefixLinkRegex = /(?<!\\)\[([^\]\r\n]+)\]\{@link\s+([^\}\s\|]+)\}/gv;
5
- const pipeLinkRegex = /\{@link\s+([^\}\s\|]+)\s*\|\s*([^\}\r\n]+)\}/gv;
3
+ const markdownLinkRegex = /(?<!\\)\[((?:[^\\`\]\r\n]|\\[^\r\n])+)\]\(([^`\(\)\s]+)\)/gv;
4
+ const prefixLinkRegex = /(?<!\\)\[((?:[^\\\]\r\n]|\\[^\r\n])+)\]\{@link\s+([^\}\s\|]+)\}/gv;
5
+ const pipeLinkRegex = /\{@link\s+([^\}\s\|]+)\s*\|\s*((?:[^\\\}\r\n]|\\[^\r\n])+)\}/gv;
6
6
 
7
7
  const markdownLinkAttemptRegex = /(?<!\\)\[[^\]\r\n]*\]\([^\r\n]*(?:\)|$)/v;
8
8
  const prefixLinkAttemptRegex = /(?<!\\)\[[^\]\r\n]*\]\{@link[^\}\r\n]*(?:\}|$)/v;
@@ -13,20 +13,33 @@ const markdownCodeSpanRegex = /(?<!\\)(`+)(?!`)[\s\S]*?(?<!`)\1(?!`)/gv;
13
13
  const bareHttpUrlRegex = /^https?:\S+$/v;
14
14
 
15
15
  /**
16
- * @param {'pipe'|'prefix'} canonicalForm
17
16
  * @param {string} label
18
17
  * @returns {boolean}
19
18
  */
20
- const cannotSafelyFormatLink = (canonicalForm, label) => {
21
- if (!label.trim()) {
22
- return true;
23
- }
24
-
25
- if (canonicalForm === 'pipe') {
26
- return label.includes('}');
27
- }
19
+ const cannotSafelyFormatLink = (label) => {
20
+ return !label.trim();
21
+ };
28
22
 
29
- return label.includes(']');
23
+ /**
24
+ * @param {'pipe'|'prefix'} canonicalForm
25
+ * @param {string} label
26
+ * @param {'markdown'|'pipe'|'prefix'} sourceForm
27
+ * @returns {string}
28
+ */
29
+ const escapeLinkLabel = (canonicalForm, label, sourceForm) => {
30
+ const sourceEscapePattern = sourceForm === 'pipe' ?
31
+ /\\([\\\}])/gv :
32
+ (sourceForm === 'prefix' ?
33
+ /\\([\\\]])/gv :
34
+ /\\([\\\}\]])/gv);
35
+ const targetEscapePattern = canonicalForm === 'pipe' ?
36
+ /\\(?=$|[\\\}])|\}/gv :
37
+ /\\(?=$|[\\\]])|\]/gv;
38
+ const decodedLabel = label.trim().replaceAll(sourceEscapePattern, '$1');
39
+
40
+ return decodedLabel.replaceAll(targetEscapePattern, (character) => {
41
+ return `\\${character}`;
42
+ });
30
43
  };
31
44
 
32
45
  /**
@@ -95,15 +108,17 @@ const isInsideMarkdownCodeSpan = (description, index) => {
95
108
  /**
96
109
  * @param {'pipe'|'prefix'} canonicalForm
97
110
  * @param {string} label
111
+ * @param {'markdown'|'pipe'|'prefix'} sourceForm
98
112
  * @param {string} target
99
113
  * @returns {string}
100
114
  */
101
- const formatLink = (canonicalForm, label, target) => {
115
+ const formatLink = (canonicalForm, label, sourceForm, target) => {
102
116
  const encodedTarget = encodeLinkTarget(target);
117
+ const escapedLabel = escapeLinkLabel(canonicalForm, label, sourceForm);
103
118
 
104
119
  return canonicalForm === 'pipe' ?
105
- `{@link ${encodedTarget}|${label.trim()}}` :
106
- `[${label.trim()}]{@link ${encodedTarget}}`;
120
+ `{@link ${encodedTarget}|${escapedLabel}}` :
121
+ `[${escapedLabel}]{@link ${encodedTarget}}`;
107
122
  };
108
123
 
109
124
  /**
@@ -115,7 +130,7 @@ const normalizeDescription = (description, canonicalForm) => {
115
130
  const markdownNormalized = description.replaceAll(
116
131
  new RegExp(markdownLinkRegex, 'gv'),
117
132
  (_match, label, target) => {
118
- return formatLink(canonicalForm, label, target);
133
+ return formatLink(canonicalForm, label, 'markdown', target);
119
134
  },
120
135
  );
121
136
 
@@ -123,7 +138,7 @@ const normalizeDescription = (description, canonicalForm) => {
123
138
  return markdownNormalized.replaceAll(
124
139
  new RegExp(prefixLinkRegex, 'gv'),
125
140
  (_match, label, target) => {
126
- return formatLink(canonicalForm, label, target);
141
+ return formatLink(canonicalForm, label, 'prefix', target);
127
142
  },
128
143
  );
129
144
  }
@@ -131,7 +146,7 @@ const normalizeDescription = (description, canonicalForm) => {
131
146
  return markdownNormalized.replaceAll(
132
147
  new RegExp(pipeLinkRegex, 'gv'),
133
148
  (_match, target, label) => {
134
- return formatLink(canonicalForm, label, target);
149
+ return formatLink(canonicalForm, label, 'pipe', target);
135
150
  },
136
151
  );
137
152
  };
@@ -183,7 +198,7 @@ export default iterateJsdoc(({
183
198
  if (
184
199
  isInsideMarkdownCodeSpan(rawDescription, match.index) ||
185
200
  scopedPackageNameRegex.test(target) ||
186
- cannotSafelyFormatLink(canonicalForm, label)
201
+ cannotSafelyFormatLink(label)
187
202
  ) {
188
203
  hasAmbiguousLink = true;
189
204
  } else {
@@ -201,10 +216,7 @@ export default iterateJsdoc(({
201
216
  continue;
202
217
  }
203
218
 
204
- if (cannotSafelyFormatLink(
205
- canonicalForm,
206
- inlineTag.text,
207
- )) {
219
+ if (cannotSafelyFormatLink(inlineTag.text)) {
208
220
  hasAmbiguousLink = true;
209
221
  continue;
210
222
  }