eslint-plugin-jsdoc 63.0.9 → 63.0.10
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.
|
@@ -33,25 +33,110 @@ var _default = exports.default = (0, _iterateJsdoc.default)(({
|
|
|
33
33
|
/** @type {number[]} */
|
|
34
34
|
const indexes = [];
|
|
35
35
|
const unescapedInlineTagRegex = /(?:^|\s)@(\w+)/gv;
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
36
|
+
const scopedPackageNameRegex = /^@[\w.\-]+\/[\w.\-]+/v;
|
|
37
|
+
const declarationReferenceInlineTags = new Set(['inheritDoc', 'link', 'linkcode', 'linkplain']);
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* @param {string} desc
|
|
41
|
+
* @param {number} atSignIndex
|
|
42
|
+
* @returns {string}
|
|
43
|
+
*/
|
|
44
|
+
const getInlineTagName = (desc, atSignIndex) => {
|
|
45
|
+
const inlineTagStart = desc.lastIndexOf('{@', atSignIndex);
|
|
46
|
+
if (inlineTagStart === -1) {
|
|
47
|
+
return '';
|
|
48
|
+
}
|
|
49
|
+
const inlineTagEnd = desc.indexOf('}', inlineTagStart);
|
|
50
|
+
if (inlineTagEnd === -1 || inlineTagEnd <= atSignIndex) {
|
|
51
|
+
return '';
|
|
52
|
+
}
|
|
53
|
+
return desc.slice(inlineTagStart + 2).match(/^\w+/v)?.[0] || '';
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* @param {string} desc
|
|
58
|
+
* @param {string} match
|
|
59
|
+
* @param {number} offset
|
|
60
|
+
* @returns {boolean}
|
|
61
|
+
*/
|
|
62
|
+
const shouldIgnoreMatch = (desc, match, offset) => {
|
|
63
|
+
const atSignIndex = offset + match.lastIndexOf('@');
|
|
64
|
+
const inlineTagName = getInlineTagName(desc, atSignIndex);
|
|
65
|
+
return declarationReferenceInlineTags.has(inlineTagName) && scopedPackageNameRegex.test(desc.slice(atSignIndex));
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* @param {string} tagName
|
|
70
|
+
* @returns {[
|
|
71
|
+
* RegExp,
|
|
72
|
+
* (description: string) => string
|
|
73
|
+
* ]}
|
|
74
|
+
*/
|
|
75
|
+
const escapeInlineTags = tagName => {
|
|
76
|
+
const regex = new RegExp(`(^|\\s)@${
|
|
77
|
+
// No need to escape, as contains only safe characters
|
|
78
|
+
tagName}`, 'gv');
|
|
79
|
+
return [regex,
|
|
80
|
+
/**
|
|
81
|
+
* @param {string} desc
|
|
82
|
+
*/
|
|
83
|
+
desc => {
|
|
84
|
+
return desc.replaceAll(regex, (match, prefix, offset) => {
|
|
85
|
+
if (shouldIgnoreMatch(desc, match, offset)) {
|
|
86
|
+
return match;
|
|
87
|
+
}
|
|
88
|
+
return fixType === 'backticks' ? prefix + '`@' + tagName + '`' : prefix + '\\@' + tagName;
|
|
89
|
+
});
|
|
90
|
+
}];
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* @param {string} desc
|
|
95
|
+
* @returns {string}
|
|
96
|
+
*/
|
|
97
|
+
const getUnescapedInlineTagName = desc => {
|
|
98
|
+
unescapedInlineTagRegex.lastIndex = 0;
|
|
99
|
+
let match;
|
|
100
|
+
while ((match = unescapedInlineTagRegex.exec(desc)) !== null) {
|
|
101
|
+
const [fullMatch, tagName] = match;
|
|
102
|
+
if (allowedInlineTags.includes(tagName) || shouldIgnoreMatch(desc, fullMatch, match.index)) {
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
return tagName;
|
|
106
|
+
}
|
|
107
|
+
return '';
|
|
108
|
+
};
|
|
109
|
+
const normalizedDescription = description.startsWith('\n') ? description.slice(1) : description;
|
|
110
|
+
let nextLineStartOffset = 0;
|
|
111
|
+
for (const [idx, descLine] of normalizedDescription.split('\n').entries()) {
|
|
112
|
+
const lineStartOffset = nextLineStartOffset;
|
|
113
|
+
|
|
114
|
+
// +1 for the `\n` removed by split.
|
|
115
|
+
nextLineStartOffset += descLine.length + 1;
|
|
116
|
+
descLine.replaceAll(unescapedInlineTagRegex, (match, tagName, offset) => {
|
|
117
|
+
if (allowedInlineTags.includes(tagName) ||
|
|
118
|
+
// Run ignore-detection against the full description text so that
|
|
119
|
+
// multi-line inline tags (e.g. `{@link` on one line and
|
|
120
|
+
// `@scope/pkg#Member}` on the next) are recognized as being inside an
|
|
121
|
+
// inline tag rather than scanned per-line.
|
|
122
|
+
shouldIgnoreMatch(normalizedDescription, match, lineStartOffset + offset)) {
|
|
123
|
+
return match;
|
|
40
124
|
}
|
|
41
125
|
tagNames.push(tagName);
|
|
42
126
|
indexes.push(idx);
|
|
43
|
-
return
|
|
127
|
+
return match;
|
|
44
128
|
});
|
|
45
129
|
}
|
|
46
130
|
for (const [idx, tagName] of tagNames.entries()) {
|
|
47
131
|
utils.reportJSDoc(`Unexpected inline JSDoc tag. Did you mean to use {@${tagName}}, \\@${tagName}, or \`@${tagName}\`?`, {
|
|
48
132
|
line: indexes[idx] + 1
|
|
49
133
|
}, enableFixer ? () => {
|
|
134
|
+
// `tagName` and `fixType` are constant here, so compute the escaper
|
|
135
|
+
// once rather than rebuilding the RegExp + closure for every line.
|
|
136
|
+
const [, escapeInlineTag] = escapeInlineTags(tagName);
|
|
50
137
|
utils.setBlockDescription((info, seedTokens, descLines) => {
|
|
51
138
|
return descLines.map(desc => {
|
|
52
|
-
const newDesc = desc
|
|
53
|
-
// No need to escape, as contains only safe characters
|
|
54
|
-
tagName}`, 'gv'), fixType === 'backticks' ? '$1`@' + tagName + '`' : '$1\\@' + tagName);
|
|
139
|
+
const newDesc = escapeInlineTag(desc);
|
|
55
140
|
return {
|
|
56
141
|
number: 0,
|
|
57
142
|
source: '',
|
|
@@ -65,26 +150,6 @@ var _default = exports.default = (0, _iterateJsdoc.default)(({
|
|
|
65
150
|
});
|
|
66
151
|
} : null);
|
|
67
152
|
}
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* @param {string} tagName
|
|
71
|
-
* @returns {[
|
|
72
|
-
* RegExp,
|
|
73
|
-
* (description: string) => string
|
|
74
|
-
* ]}
|
|
75
|
-
*/
|
|
76
|
-
const escapeInlineTags = tagName => {
|
|
77
|
-
const regex = new RegExp(`(^|\\s)@${
|
|
78
|
-
// No need to escape, as contains only safe characters
|
|
79
|
-
tagName}`, 'gv');
|
|
80
|
-
return [regex,
|
|
81
|
-
/**
|
|
82
|
-
* @param {string} desc
|
|
83
|
-
*/
|
|
84
|
-
desc => {
|
|
85
|
-
return desc.replaceAll(regex, fixType === 'backticks' ? '$1`@' + tagName + '`' : '$1\\@' + tagName);
|
|
86
|
-
}];
|
|
87
|
-
};
|
|
88
153
|
for (const tag of jsdoc.tags) {
|
|
89
154
|
if (tag.tag === 'example') {
|
|
90
155
|
continue;
|
|
@@ -95,10 +160,7 @@ var _default = exports.default = (0, _iterateJsdoc.default)(({
|
|
|
95
160
|
while (/** @type {string[]} */utils.getTagDescription(tag, true)
|
|
96
161
|
// eslint-disable-next-line no-loop-func -- Safe
|
|
97
162
|
.some(desc => {
|
|
98
|
-
tagName =
|
|
99
|
-
if (allowedInlineTags.includes(tagName)) {
|
|
100
|
-
return false;
|
|
101
|
-
}
|
|
163
|
+
tagName = getUnescapedInlineTagName(desc);
|
|
102
164
|
return tagName;
|
|
103
165
|
})) {
|
|
104
166
|
const line = utils.setTagDescription(tag, ...escapeInlineTags(tagName)) + tag.source[0].number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"escapeInlineTags.cjs","names":["_iterateJsdoc","_interopRequireDefault","require","e","__esModule","default","_default","exports","iterateJsdoc","context","jsdoc","settings","utils","mode","allowedInlineTags","enableFixer","fixType","options","description","getDescription","tagNames","indexes","unescapedInlineTagRegex","idx","descLine","startsWith","slice","split","entries","replaceAll","_","tagName","includes","push","reportJSDoc","line","setBlockDescription","info","seedTokens","descLines","map","desc","newDesc","RegExp","number","source","tokens","postDelimiter","trim","escapeInlineTags","regex","tag","tags","getTagDescription","some","exec","setTagDescription","iterateAllJsdocs","meta","docs","url","fixable","schema","additionalProperties","properties","items","type","enum","module"],"sources":["../../src/rules/escapeInlineTags.js"],"sourcesContent":["import iterateJsdoc from '../iterateJsdoc.js';\n\nexport default iterateJsdoc(({\n context,\n jsdoc,\n settings,\n utils,\n}) => {\n const {\n mode,\n } = settings;\n\n if (mode !== 'typescript') {\n return;\n }\n\n const {\n allowedInlineTags = [],\n enableFixer = false,\n fixType = 'backslash',\n } = context.options[0] || {};\n\n const {\n description,\n } = utils.getDescription();\n\n /** @type {string[]} */\n const tagNames = [];\n\n /** @type {number[]} */\n const indexes = [];\n\n const unescapedInlineTagRegex = /(?:^|\\s)@(\\w+)/gv;\n for (const [\n idx,\n descLine,\n ] of (\n description.startsWith('\\n') ? description.slice(1) : description\n ).split('\\n').entries()\n ) {\n descLine.replaceAll(unescapedInlineTagRegex, (_, tagName) => {\n if (allowedInlineTags.includes(tagName)) {\n return _;\n }\n\n tagNames.push(tagName);\n indexes.push(idx);\n\n return _;\n });\n }\n\n for (const [\n idx,\n tagName,\n ] of tagNames.entries()) {\n utils.reportJSDoc(\n `Unexpected inline JSDoc tag. Did you mean to use {@${tagName}}, \\\\@${tagName}, or \\`@${tagName}\\`?`,\n {\n line: indexes[idx] + 1,\n },\n enableFixer ?\n () => {\n utils.setBlockDescription((info, seedTokens, descLines) => {\n return descLines.map((desc) => {\n const newDesc = desc.replaceAll(\n new RegExp(`(^|\\\\s)@${\n // No need to escape, as contains only safe characters\n tagName\n }`, 'gv'),\n fixType === 'backticks' ? '$1`@' + tagName + '`' : '$1\\\\@' + tagName,\n );\n\n return {\n number: 0,\n source: '',\n tokens: seedTokens({\n ...info,\n description: newDesc,\n postDelimiter: newDesc.trim() ? ' ' : '',\n }),\n };\n });\n });\n } :\n null,\n );\n }\n\n /**\n * @param {string} tagName\n * @returns {[\n * RegExp,\n * (description: string) => string\n * ]}\n */\n const escapeInlineTags = (tagName) => {\n const regex = new RegExp(`(^|\\\\s)@${\n // No need to escape, as contains only safe characters\n tagName\n }`, 'gv');\n\n return [\n regex,\n /**\n * @param {string} desc\n */\n (desc) => {\n return desc.replaceAll(\n regex,\n fixType === 'backticks' ? '$1`@' + tagName + '`' : '$1\\\\@' + tagName,\n );\n },\n ];\n };\n\n for (const tag of jsdoc.tags) {\n if (tag.tag === 'example') {\n continue;\n }\n\n /** @type {string} */\n let tagName = '';\n while (/** @type {string[]} */ (\n utils.getTagDescription(tag, true)\n // eslint-disable-next-line no-loop-func -- Safe\n ).some((desc) => {\n tagName = unescapedInlineTagRegex.exec(desc)?.[1] ?? '';\n if (allowedInlineTags.includes(tagName)) {\n return false;\n }\n\n return tagName;\n })) {\n const line = utils.setTagDescription(tag, ...escapeInlineTags(tagName)) +\n tag.source[0].number;\n utils.reportJSDoc(\n `Unexpected inline JSDoc tag. Did you mean to use {@${tagName}}, \\\\@${tagName}, or \\`@${tagName}\\`?`,\n {\n line,\n },\n enableFixer ? () => {} : null,\n true,\n );\n }\n }\n}, {\n iterateAllJsdocs: true,\n meta: {\n docs: {\n description: 'Reports use of JSDoc tags in non-tag positions (in the default \"typescript\" mode).',\n url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/escape-inline-tags.md#repos-sticky-header',\n },\n fixable: 'code',\n schema: [\n {\n additionalProperties: false,\n properties: {\n allowedInlineTags: {\n description: 'A listing of tags you wish to allow unescaped. Defaults to an empty array.',\n items: {\n type: 'string',\n },\n type: 'array',\n },\n enableFixer: {\n description: 'Whether to enable the fixer. Defaults to `false`.',\n type: 'boolean',\n },\n fixType: {\n description: `How to escape the inline tag.\n\nMay be \"backticks\" to enclose tags in backticks (treating as code segments), or\n\"backslash\" to escape tags with a backslash, i.e., \\`\\\\@\\`\n\nDefaults to \"backslash\".`,\n enum: [\n 'backticks',\n 'backslash',\n ],\n type: 'string',\n },\n },\n type: 'object',\n },\n ],\n type: 'suggestion',\n },\n});\n"],"mappings":"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","ignoreList":[]}
|
|
1
|
+
{"version":3,"file":"escapeInlineTags.cjs","names":["_iterateJsdoc","_interopRequireDefault","require","e","__esModule","default","_default","exports","iterateJsdoc","context","jsdoc","settings","utils","mode","allowedInlineTags","enableFixer","fixType","options","description","getDescription","tagNames","indexes","unescapedInlineTagRegex","scopedPackageNameRegex","declarationReferenceInlineTags","Set","getInlineTagName","desc","atSignIndex","inlineTagStart","lastIndexOf","inlineTagEnd","indexOf","slice","match","shouldIgnoreMatch","offset","inlineTagName","has","test","escapeInlineTags","tagName","regex","RegExp","replaceAll","prefix","getUnescapedInlineTagName","lastIndex","exec","fullMatch","includes","index","normalizedDescription","startsWith","nextLineStartOffset","idx","descLine","split","entries","lineStartOffset","length","push","reportJSDoc","line","escapeInlineTag","setBlockDescription","info","seedTokens","descLines","map","newDesc","number","source","tokens","postDelimiter","trim","tag","tags","getTagDescription","some","setTagDescription","iterateAllJsdocs","meta","docs","url","fixable","schema","additionalProperties","properties","items","type","enum","module"],"sources":["../../src/rules/escapeInlineTags.js"],"sourcesContent":["import iterateJsdoc from '../iterateJsdoc.js';\n\nexport default iterateJsdoc(({\n context,\n jsdoc,\n settings,\n utils,\n}) => {\n const {\n mode,\n } = settings;\n\n if (mode !== 'typescript') {\n return;\n }\n\n const {\n allowedInlineTags = [],\n enableFixer = false,\n fixType = 'backslash',\n } = context.options[0] || {};\n\n const {\n description,\n } = utils.getDescription();\n\n /** @type {string[]} */\n const tagNames = [];\n\n /** @type {number[]} */\n const indexes = [];\n\n const unescapedInlineTagRegex = /(?:^|\\s)@(\\w+)/gv;\n\n const scopedPackageNameRegex = /^@[\\w.\\-]+\\/[\\w.\\-]+/v;\n\n const declarationReferenceInlineTags = new Set([\n 'inheritDoc',\n 'link',\n 'linkcode',\n 'linkplain',\n ]);\n\n /**\n * @param {string} desc\n * @param {number} atSignIndex\n * @returns {string}\n */\n const getInlineTagName = (desc, atSignIndex) => {\n const inlineTagStart = desc.lastIndexOf('{@', atSignIndex);\n\n if (inlineTagStart === -1) {\n return '';\n }\n\n const inlineTagEnd = desc.indexOf('}', inlineTagStart);\n\n if (inlineTagEnd === -1 || inlineTagEnd <= atSignIndex) {\n return '';\n }\n\n return desc.slice(inlineTagStart + 2).match(/^\\w+/v)?.[0] || '';\n };\n\n /**\n * @param {string} desc\n * @param {string} match\n * @param {number} offset\n * @returns {boolean}\n */\n const shouldIgnoreMatch = (desc, match, offset) => {\n const atSignIndex = offset + match.lastIndexOf('@');\n const inlineTagName = getInlineTagName(desc, atSignIndex);\n\n return declarationReferenceInlineTags.has(inlineTagName) &&\n scopedPackageNameRegex.test(desc.slice(atSignIndex));\n };\n\n /**\n * @param {string} tagName\n * @returns {[\n * RegExp,\n * (description: string) => string\n * ]}\n */\n const escapeInlineTags = (tagName) => {\n const regex = new RegExp(`(^|\\\\s)@${\n // No need to escape, as contains only safe characters\n tagName\n }`, 'gv');\n\n return [\n regex,\n /**\n * @param {string} desc\n */\n (desc) => {\n return desc.replaceAll(\n regex,\n (match, prefix, offset) => {\n if (shouldIgnoreMatch(desc, match, offset)) {\n return match;\n }\n\n return fixType === 'backticks' ?\n prefix + '`@' + tagName + '`' :\n prefix + '\\\\@' + tagName;\n },\n );\n },\n ];\n };\n\n /**\n * @param {string} desc\n * @returns {string}\n */\n const getUnescapedInlineTagName = (desc) => {\n unescapedInlineTagRegex.lastIndex = 0;\n\n let match;\n while ((match = unescapedInlineTagRegex.exec(desc)) !== null) {\n const [\n fullMatch,\n tagName,\n ] = match;\n\n if (\n allowedInlineTags.includes(tagName) ||\n shouldIgnoreMatch(desc, fullMatch, match.index)\n ) {\n continue;\n }\n\n return tagName;\n }\n\n return '';\n };\n\n const normalizedDescription = description.startsWith('\\n') ?\n description.slice(1) :\n description;\n\n let nextLineStartOffset = 0;\n for (const [\n idx,\n descLine,\n ] of normalizedDescription.split('\\n').entries()\n ) {\n const lineStartOffset = nextLineStartOffset;\n\n // +1 for the `\\n` removed by split.\n nextLineStartOffset += descLine.length + 1;\n\n descLine.replaceAll(unescapedInlineTagRegex, (match, tagName, offset) => {\n if (\n allowedInlineTags.includes(tagName) ||\n // Run ignore-detection against the full description text so that\n // multi-line inline tags (e.g. `{@link` on one line and\n // `@scope/pkg#Member}` on the next) are recognized as being inside an\n // inline tag rather than scanned per-line.\n shouldIgnoreMatch(\n normalizedDescription,\n match,\n lineStartOffset + offset,\n )\n ) {\n return match;\n }\n\n tagNames.push(tagName);\n indexes.push(idx);\n\n return match;\n });\n }\n\n for (const [\n idx,\n tagName,\n ] of tagNames.entries()) {\n utils.reportJSDoc(\n `Unexpected inline JSDoc tag. Did you mean to use {@${tagName}}, \\\\@${tagName}, or \\`@${tagName}\\`?`,\n {\n line: indexes[idx] + 1,\n },\n enableFixer ?\n () => {\n // `tagName` and `fixType` are constant here, so compute the escaper\n // once rather than rebuilding the RegExp + closure for every line.\n const [\n ,\n escapeInlineTag,\n ] = escapeInlineTags(tagName);\n\n utils.setBlockDescription((info, seedTokens, descLines) => {\n return descLines.map((desc) => {\n const newDesc = escapeInlineTag(desc);\n\n return {\n number: 0,\n source: '',\n tokens: seedTokens({\n ...info,\n description: newDesc,\n postDelimiter: newDesc.trim() ? ' ' : '',\n }),\n };\n });\n });\n } :\n null,\n );\n }\n\n for (const tag of jsdoc.tags) {\n if (tag.tag === 'example') {\n continue;\n }\n\n /** @type {string} */\n let tagName = '';\n while (/** @type {string[]} */ (\n utils.getTagDescription(tag, true)\n // eslint-disable-next-line no-loop-func -- Safe\n ).some((desc) => {\n tagName = getUnescapedInlineTagName(desc);\n\n return tagName;\n })) {\n const line = utils.setTagDescription(tag, ...escapeInlineTags(tagName)) +\n tag.source[0].number;\n utils.reportJSDoc(\n `Unexpected inline JSDoc tag. Did you mean to use {@${tagName}}, \\\\@${tagName}, or \\`@${tagName}\\`?`,\n {\n line,\n },\n enableFixer ? () => {} : null,\n true,\n );\n }\n }\n}, {\n iterateAllJsdocs: true,\n meta: {\n docs: {\n description: 'Reports use of JSDoc tags in non-tag positions (in the default \"typescript\" mode).',\n url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/escape-inline-tags.md#repos-sticky-header',\n },\n fixable: 'code',\n schema: [\n {\n additionalProperties: false,\n properties: {\n allowedInlineTags: {\n description: 'A listing of tags you wish to allow unescaped. Defaults to an empty array.',\n items: {\n type: 'string',\n },\n type: 'array',\n },\n enableFixer: {\n description: 'Whether to enable the fixer. Defaults to `false`.',\n type: 'boolean',\n },\n fixType: {\n description: `How to escape the inline tag.\n\nMay be \"backticks\" to enclose tags in backticks (treating as code segments), or\n\"backslash\" to escape tags with a backslash, i.e., \\`\\\\@\\`\n\nDefaults to \"backslash\".`,\n enum: [\n 'backticks',\n 'backslash',\n ],\n type: 'string',\n },\n },\n type: 'object',\n },\n ],\n type: 'suggestion',\n },\n});\n"],"mappings":";;;;;;AAAA,IAAAA,aAAA,GAAAC,sBAAA,CAAAC,OAAA;AAA8C,SAAAD,uBAAAE,CAAA,WAAAA,CAAA,IAAAA,CAAA,CAAAC,UAAA,GAAAD,CAAA,KAAAE,OAAA,EAAAF,CAAA;AAAA,IAAAG,QAAA,GAAAC,OAAA,CAAAF,OAAA,GAE/B,IAAAG,qBAAY,EAAC,CAAC;EAC3BC,OAAO;EACPC,KAAK;EACLC,QAAQ;EACRC;AACF,CAAC,KAAK;EACJ,MAAM;IACJC;EACF,CAAC,GAAGF,QAAQ;EAEZ,IAAIE,IAAI,KAAK,YAAY,EAAE;IACzB;EACF;EAEA,MAAM;IACJC,iBAAiB,GAAG,EAAE;IACtBC,WAAW,GAAG,KAAK;IACnBC,OAAO,GAAG;EACZ,CAAC,GAAGP,OAAO,CAACQ,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;EAE5B,MAAM;IACJC;EACF,CAAC,GAAGN,KAAK,CAACO,cAAc,CAAC,CAAC;;EAE1B;EACA,MAAMC,QAAQ,GAAG,EAAE;;EAEnB;EACA,MAAMC,OAAO,GAAG,EAAE;EAElB,MAAMC,uBAAuB,GAAG,kBAAkB;EAElD,MAAMC,sBAAsB,GAAG,uBAAuB;EAEtD,MAAMC,8BAA8B,GAAG,IAAIC,GAAG,CAAC,CAC7C,YAAY,EACZ,MAAM,EACN,UAAU,EACV,WAAW,CACZ,CAAC;;EAEF;AACF;AACA;AACA;AACA;EACE,MAAMC,gBAAgB,GAAGA,CAACC,IAAI,EAAEC,WAAW,KAAK;IAC9C,MAAMC,cAAc,GAAGF,IAAI,CAACG,WAAW,CAAC,IAAI,EAAEF,WAAW,CAAC;IAE1D,IAAIC,cAAc,KAAK,CAAC,CAAC,EAAE;MACzB,OAAO,EAAE;IACX;IAEA,MAAME,YAAY,GAAGJ,IAAI,CAACK,OAAO,CAAC,GAAG,EAAEH,cAAc,CAAC;IAEtD,IAAIE,YAAY,KAAK,CAAC,CAAC,IAAIA,YAAY,IAAIH,WAAW,EAAE;MACtD,OAAO,EAAE;IACX;IAEA,OAAOD,IAAI,CAACM,KAAK,CAACJ,cAAc,GAAG,CAAC,CAAC,CAACK,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE;EACjE,CAAC;;EAED;AACF;AACA;AACA;AACA;AACA;EACE,MAAMC,iBAAiB,GAAGA,CAACR,IAAI,EAAEO,KAAK,EAAEE,MAAM,KAAK;IACjD,MAAMR,WAAW,GAAGQ,MAAM,GAAGF,KAAK,CAACJ,WAAW,CAAC,GAAG,CAAC;IACnD,MAAMO,aAAa,GAAGX,gBAAgB,CAACC,IAAI,EAAEC,WAAW,CAAC;IAEzD,OAAOJ,8BAA8B,CAACc,GAAG,CAACD,aAAa,CAAC,IACtDd,sBAAsB,CAACgB,IAAI,CAACZ,IAAI,CAACM,KAAK,CAACL,WAAW,CAAC,CAAC;EACxD,CAAC;;EAED;AACF;AACA;AACA;AACA;AACA;AACA;EACE,MAAMY,gBAAgB,GAAIC,OAAO,IAAK;IACpC,MAAMC,KAAK,GAAG,IAAIC,MAAM,CAAC;IACvB;IACAF,OAAO,EACP,EAAE,IAAI,CAAC;IAET,OAAO,CACLC,KAAK;IACL;AACN;AACA;IACOf,IAAI,IAAK;MACR,OAAOA,IAAI,CAACiB,UAAU,CACpBF,KAAK,EACL,CAACR,KAAK,EAAEW,MAAM,EAAET,MAAM,KAAK;QACzB,IAAID,iBAAiB,CAACR,IAAI,EAAEO,KAAK,EAAEE,MAAM,CAAC,EAAE;UAC1C,OAAOF,KAAK;QACd;QAEA,OAAOlB,OAAO,KAAK,WAAW,GAC5B6B,MAAM,GAAG,IAAI,GAAGJ,OAAO,GAAG,GAAG,GAC7BI,MAAM,GAAG,KAAK,GAAGJ,OAAO;MAC5B,CACF,CAAC;IACH,CAAC,CACF;EACH,CAAC;;EAED;AACF;AACA;AACA;EACE,MAAMK,yBAAyB,GAAInB,IAAI,IAAK;IAC1CL,uBAAuB,CAACyB,SAAS,GAAG,CAAC;IAErC,IAAIb,KAAK;IACT,OAAO,CAACA,KAAK,GAAGZ,uBAAuB,CAAC0B,IAAI,CAACrB,IAAI,CAAC,MAAM,IAAI,EAAE;MAC5D,MAAM,CACJsB,SAAS,EACTR,OAAO,CACR,GAAGP,KAAK;MAET,IACEpB,iBAAiB,CAACoC,QAAQ,CAACT,OAAO,CAAC,IACnCN,iBAAiB,CAACR,IAAI,EAAEsB,SAAS,EAAEf,KAAK,CAACiB,KAAK,CAAC,EAC/C;QACA;MACF;MAEA,OAAOV,OAAO;IAChB;IAEA,OAAO,EAAE;EACX,CAAC;EAED,MAAMW,qBAAqB,GAAGlC,WAAW,CAACmC,UAAU,CAAC,IAAI,CAAC,GACxDnC,WAAW,CAACe,KAAK,CAAC,CAAC,CAAC,GACpBf,WAAW;EAEb,IAAIoC,mBAAmB,GAAG,CAAC;EAC3B,KAAK,MAAM,CACTC,GAAG,EACHC,QAAQ,CACT,IAAIJ,qBAAqB,CAACK,KAAK,CAAC,IAAI,CAAC,CAACC,OAAO,CAAC,CAAC,EAC9C;IACA,MAAMC,eAAe,GAAGL,mBAAmB;;IAE3C;IACAA,mBAAmB,IAAIE,QAAQ,CAACI,MAAM,GAAG,CAAC;IAE1CJ,QAAQ,CAACZ,UAAU,CAACtB,uBAAuB,EAAE,CAACY,KAAK,EAAEO,OAAO,EAAEL,MAAM,KAAK;MACvE,IACEtB,iBAAiB,CAACoC,QAAQ,CAACT,OAAO,CAAC;MACnC;MACA;MACA;MACA;MACAN,iBAAiB,CACfiB,qBAAqB,EACrBlB,KAAK,EACLyB,eAAe,GAAGvB,MACpB,CAAC,EACD;QACA,OAAOF,KAAK;MACd;MAEAd,QAAQ,CAACyC,IAAI,CAACpB,OAAO,CAAC;MACtBpB,OAAO,CAACwC,IAAI,CAACN,GAAG,CAAC;MAEjB,OAAOrB,KAAK;IACd,CAAC,CAAC;EACJ;EAEA,KAAK,MAAM,CACTqB,GAAG,EACHd,OAAO,CACR,IAAIrB,QAAQ,CAACsC,OAAO,CAAC,CAAC,EAAE;IACvB9C,KAAK,CAACkD,WAAW,CACf,sDAAsDrB,OAAO,SAASA,OAAO,WAAWA,OAAO,KAAK,EACpG;MACEsB,IAAI,EAAE1C,OAAO,CAACkC,GAAG,CAAC,GAAG;IACvB,CAAC,EACDxC,WAAW,GACT,MAAM;MACJ;MACA;MACA,MAAM,GAEJiD,eAAe,CAChB,GAAGxB,gBAAgB,CAACC,OAAO,CAAC;MAE7B7B,KAAK,CAACqD,mBAAmB,CAAC,CAACC,IAAI,EAAEC,UAAU,EAAEC,SAAS,KAAK;QACzD,OAAOA,SAAS,CAACC,GAAG,CAAE1C,IAAI,IAAK;UAC7B,MAAM2C,OAAO,GAAGN,eAAe,CAACrC,IAAI,CAAC;UAErC,OAAO;YACL4C,MAAM,EAAE,CAAC;YACTC,MAAM,EAAE,EAAE;YACVC,MAAM,EAAEN,UAAU,CAAC;cACjB,GAAGD,IAAI;cACPhD,WAAW,EAAEoD,OAAO;cACpBI,aAAa,EAAEJ,OAAO,CAACK,IAAI,CAAC,CAAC,GAAG,GAAG,GAAG;YACxC,CAAC;UACH,CAAC;QACH,CAAC,CAAC;MACJ,CAAC,CAAC;IACJ,CAAC,GACD,IACJ,CAAC;EACH;EAEA,KAAK,MAAMC,GAAG,IAAIlE,KAAK,CAACmE,IAAI,EAAE;IAC5B,IAAID,GAAG,CAACA,GAAG,KAAK,SAAS,EAAE;MACzB;IACF;;IAEA;IACA,IAAInC,OAAO,GAAG,EAAE;IAChB,OAAO,uBACL7B,KAAK,CAACkE,iBAAiB,CAACF,GAAG,EAAE,IAAI;IACnC;IAAA,CACEG,IAAI,CAAEpD,IAAI,IAAK;MACfc,OAAO,GAAGK,yBAAyB,CAACnB,IAAI,CAAC;MAEzC,OAAOc,OAAO;IAChB,CAAC,CAAC,EAAE;MACF,MAAMsB,IAAI,GAAGnD,KAAK,CAACoE,iBAAiB,CAACJ,GAAG,EAAE,GAAGpC,gBAAgB,CAACC,OAAO,CAAC,CAAC,GACnEmC,GAAG,CAACJ,MAAM,CAAC,CAAC,CAAC,CAACD,MAAM;MACxB3D,KAAK,CAACkD,WAAW,CACf,sDAAsDrB,OAAO,SAASA,OAAO,WAAWA,OAAO,KAAK,EACpG;QACEsB;MACF,CAAC,EACDhD,WAAW,GAAG,MAAM,CAAC,CAAC,GAAG,IAAI,EAC7B,IACF,CAAC;IACH;EACF;AACF,CAAC,EAAE;EACDkE,gBAAgB,EAAE,IAAI;EACtBC,IAAI,EAAE;IACJC,IAAI,EAAE;MACJjE,WAAW,EAAE,oFAAoF;MACjGkE,GAAG,EAAE;IACP,CAAC;IACDC,OAAO,EAAE,MAAM;IACfC,MAAM,EAAE,CACN;MACEC,oBAAoB,EAAE,KAAK;MAC3BC,UAAU,EAAE;QACV1E,iBAAiB,EAAE;UACjBI,WAAW,EAAE,4EAA4E;UACzFuE,KAAK,EAAE;YACLC,IAAI,EAAE;UACR,CAAC;UACDA,IAAI,EAAE;QACR,CAAC;QACD3E,WAAW,EAAE;UACXG,WAAW,EAAE,mDAAmD;UAChEwE,IAAI,EAAE;QACR,CAAC;QACD1E,OAAO,EAAE;UACPE,WAAW,EAAE;AACzB;AACA;AACA;AACA;AACA,yBAAyB;UACbyE,IAAI,EAAE,CACJ,WAAW,EACX,WAAW,CACZ;UACDD,IAAI,EAAE;QACR;MACF,CAAC;MACDA,IAAI,EAAE;IACR,CAAC,CACF;IACDA,IAAI,EAAE;EACR;AACF,CAAC,CAAC;AAAAE,MAAA,CAAArF,OAAA,GAAAA,OAAA,CAAAF,OAAA","ignoreList":[]}
|
package/package.json
CHANGED
|
@@ -31,22 +31,148 @@ export default iterateJsdoc(({
|
|
|
31
31
|
const indexes = [];
|
|
32
32
|
|
|
33
33
|
const unescapedInlineTagRegex = /(?:^|\s)@(\w+)/gv;
|
|
34
|
+
|
|
35
|
+
const scopedPackageNameRegex = /^@[\w.\-]+\/[\w.\-]+/v;
|
|
36
|
+
|
|
37
|
+
const declarationReferenceInlineTags = new Set([
|
|
38
|
+
'inheritDoc',
|
|
39
|
+
'link',
|
|
40
|
+
'linkcode',
|
|
41
|
+
'linkplain',
|
|
42
|
+
]);
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* @param {string} desc
|
|
46
|
+
* @param {number} atSignIndex
|
|
47
|
+
* @returns {string}
|
|
48
|
+
*/
|
|
49
|
+
const getInlineTagName = (desc, atSignIndex) => {
|
|
50
|
+
const inlineTagStart = desc.lastIndexOf('{@', atSignIndex);
|
|
51
|
+
|
|
52
|
+
if (inlineTagStart === -1) {
|
|
53
|
+
return '';
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const inlineTagEnd = desc.indexOf('}', inlineTagStart);
|
|
57
|
+
|
|
58
|
+
if (inlineTagEnd === -1 || inlineTagEnd <= atSignIndex) {
|
|
59
|
+
return '';
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return desc.slice(inlineTagStart + 2).match(/^\w+/v)?.[0] || '';
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* @param {string} desc
|
|
67
|
+
* @param {string} match
|
|
68
|
+
* @param {number} offset
|
|
69
|
+
* @returns {boolean}
|
|
70
|
+
*/
|
|
71
|
+
const shouldIgnoreMatch = (desc, match, offset) => {
|
|
72
|
+
const atSignIndex = offset + match.lastIndexOf('@');
|
|
73
|
+
const inlineTagName = getInlineTagName(desc, atSignIndex);
|
|
74
|
+
|
|
75
|
+
return declarationReferenceInlineTags.has(inlineTagName) &&
|
|
76
|
+
scopedPackageNameRegex.test(desc.slice(atSignIndex));
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* @param {string} tagName
|
|
81
|
+
* @returns {[
|
|
82
|
+
* RegExp,
|
|
83
|
+
* (description: string) => string
|
|
84
|
+
* ]}
|
|
85
|
+
*/
|
|
86
|
+
const escapeInlineTags = (tagName) => {
|
|
87
|
+
const regex = new RegExp(`(^|\\s)@${
|
|
88
|
+
// No need to escape, as contains only safe characters
|
|
89
|
+
tagName
|
|
90
|
+
}`, 'gv');
|
|
91
|
+
|
|
92
|
+
return [
|
|
93
|
+
regex,
|
|
94
|
+
/**
|
|
95
|
+
* @param {string} desc
|
|
96
|
+
*/
|
|
97
|
+
(desc) => {
|
|
98
|
+
return desc.replaceAll(
|
|
99
|
+
regex,
|
|
100
|
+
(match, prefix, offset) => {
|
|
101
|
+
if (shouldIgnoreMatch(desc, match, offset)) {
|
|
102
|
+
return match;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return fixType === 'backticks' ?
|
|
106
|
+
prefix + '`@' + tagName + '`' :
|
|
107
|
+
prefix + '\\@' + tagName;
|
|
108
|
+
},
|
|
109
|
+
);
|
|
110
|
+
},
|
|
111
|
+
];
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* @param {string} desc
|
|
116
|
+
* @returns {string}
|
|
117
|
+
*/
|
|
118
|
+
const getUnescapedInlineTagName = (desc) => {
|
|
119
|
+
unescapedInlineTagRegex.lastIndex = 0;
|
|
120
|
+
|
|
121
|
+
let match;
|
|
122
|
+
while ((match = unescapedInlineTagRegex.exec(desc)) !== null) {
|
|
123
|
+
const [
|
|
124
|
+
fullMatch,
|
|
125
|
+
tagName,
|
|
126
|
+
] = match;
|
|
127
|
+
|
|
128
|
+
if (
|
|
129
|
+
allowedInlineTags.includes(tagName) ||
|
|
130
|
+
shouldIgnoreMatch(desc, fullMatch, match.index)
|
|
131
|
+
) {
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return tagName;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return '';
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
const normalizedDescription = description.startsWith('\n') ?
|
|
142
|
+
description.slice(1) :
|
|
143
|
+
description;
|
|
144
|
+
|
|
145
|
+
let nextLineStartOffset = 0;
|
|
34
146
|
for (const [
|
|
35
147
|
idx,
|
|
36
148
|
descLine,
|
|
37
|
-
] of (
|
|
38
|
-
description.startsWith('\n') ? description.slice(1) : description
|
|
39
|
-
).split('\n').entries()
|
|
149
|
+
] of normalizedDescription.split('\n').entries()
|
|
40
150
|
) {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
151
|
+
const lineStartOffset = nextLineStartOffset;
|
|
152
|
+
|
|
153
|
+
// +1 for the `\n` removed by split.
|
|
154
|
+
nextLineStartOffset += descLine.length + 1;
|
|
155
|
+
|
|
156
|
+
descLine.replaceAll(unescapedInlineTagRegex, (match, tagName, offset) => {
|
|
157
|
+
if (
|
|
158
|
+
allowedInlineTags.includes(tagName) ||
|
|
159
|
+
// Run ignore-detection against the full description text so that
|
|
160
|
+
// multi-line inline tags (e.g. `{@link` on one line and
|
|
161
|
+
// `@scope/pkg#Member}` on the next) are recognized as being inside an
|
|
162
|
+
// inline tag rather than scanned per-line.
|
|
163
|
+
shouldIgnoreMatch(
|
|
164
|
+
normalizedDescription,
|
|
165
|
+
match,
|
|
166
|
+
lineStartOffset + offset,
|
|
167
|
+
)
|
|
168
|
+
) {
|
|
169
|
+
return match;
|
|
44
170
|
}
|
|
45
171
|
|
|
46
172
|
tagNames.push(tagName);
|
|
47
173
|
indexes.push(idx);
|
|
48
174
|
|
|
49
|
-
return
|
|
175
|
+
return match;
|
|
50
176
|
});
|
|
51
177
|
}
|
|
52
178
|
|
|
@@ -61,15 +187,16 @@ export default iterateJsdoc(({
|
|
|
61
187
|
},
|
|
62
188
|
enableFixer ?
|
|
63
189
|
() => {
|
|
190
|
+
// `tagName` and `fixType` are constant here, so compute the escaper
|
|
191
|
+
// once rather than rebuilding the RegExp + closure for every line.
|
|
192
|
+
const [
|
|
193
|
+
,
|
|
194
|
+
escapeInlineTag,
|
|
195
|
+
] = escapeInlineTags(tagName);
|
|
196
|
+
|
|
64
197
|
utils.setBlockDescription((info, seedTokens, descLines) => {
|
|
65
198
|
return descLines.map((desc) => {
|
|
66
|
-
const newDesc = desc
|
|
67
|
-
new RegExp(`(^|\\s)@${
|
|
68
|
-
// No need to escape, as contains only safe characters
|
|
69
|
-
tagName
|
|
70
|
-
}`, 'gv'),
|
|
71
|
-
fixType === 'backticks' ? '$1`@' + tagName + '`' : '$1\\@' + tagName,
|
|
72
|
-
);
|
|
199
|
+
const newDesc = escapeInlineTag(desc);
|
|
73
200
|
|
|
74
201
|
return {
|
|
75
202
|
number: 0,
|
|
@@ -87,33 +214,6 @@ export default iterateJsdoc(({
|
|
|
87
214
|
);
|
|
88
215
|
}
|
|
89
216
|
|
|
90
|
-
/**
|
|
91
|
-
* @param {string} tagName
|
|
92
|
-
* @returns {[
|
|
93
|
-
* RegExp,
|
|
94
|
-
* (description: string) => string
|
|
95
|
-
* ]}
|
|
96
|
-
*/
|
|
97
|
-
const escapeInlineTags = (tagName) => {
|
|
98
|
-
const regex = new RegExp(`(^|\\s)@${
|
|
99
|
-
// No need to escape, as contains only safe characters
|
|
100
|
-
tagName
|
|
101
|
-
}`, 'gv');
|
|
102
|
-
|
|
103
|
-
return [
|
|
104
|
-
regex,
|
|
105
|
-
/**
|
|
106
|
-
* @param {string} desc
|
|
107
|
-
*/
|
|
108
|
-
(desc) => {
|
|
109
|
-
return desc.replaceAll(
|
|
110
|
-
regex,
|
|
111
|
-
fixType === 'backticks' ? '$1`@' + tagName + '`' : '$1\\@' + tagName,
|
|
112
|
-
);
|
|
113
|
-
},
|
|
114
|
-
];
|
|
115
|
-
};
|
|
116
|
-
|
|
117
217
|
for (const tag of jsdoc.tags) {
|
|
118
218
|
if (tag.tag === 'example') {
|
|
119
219
|
continue;
|
|
@@ -125,10 +225,7 @@ export default iterateJsdoc(({
|
|
|
125
225
|
utils.getTagDescription(tag, true)
|
|
126
226
|
// eslint-disable-next-line no-loop-func -- Safe
|
|
127
227
|
).some((desc) => {
|
|
128
|
-
tagName =
|
|
129
|
-
if (allowedInlineTags.includes(tagName)) {
|
|
130
|
-
return false;
|
|
131
|
-
}
|
|
228
|
+
tagName = getUnescapedInlineTagName(desc);
|
|
132
229
|
|
|
133
230
|
return tagName;
|
|
134
231
|
})) {
|