eslint-plugin-jsdoc 48.5.2 → 48.7.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.
@@ -0,0 +1,124 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = void 0;
7
+ var _jsdoccomment = require("@es-joy/jsdoccomment");
8
+ var _iterateJsdoc = _interopRequireDefault(require("../iterateJsdoc.cjs"));
9
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
10
+ var _default = exports.default = (0, _iterateJsdoc.default)(({
11
+ context,
12
+ utils,
13
+ node,
14
+ settings,
15
+ report
16
+ }) => {
17
+ const {
18
+ requireSeparateTemplates = false
19
+ } = context.options[0] || {};
20
+ const {
21
+ mode
22
+ } = settings;
23
+ const usedNames = new Set();
24
+ const templateTags = utils.getTags('template');
25
+ const templateNames = templateTags.flatMap(({
26
+ name
27
+ }) => {
28
+ return name.split(/,\s*/);
29
+ });
30
+ for (const tag of templateTags) {
31
+ const {
32
+ name
33
+ } = tag;
34
+ const names = name.split(/,\s*/);
35
+ if (requireSeparateTemplates && names.length > 1) {
36
+ report(`Missing separate @template for ${names[1]}`, null, tag);
37
+ }
38
+ }
39
+
40
+ /**
41
+ * @param {import('@typescript-eslint/types').TSESTree.TSTypeAliasDeclaration} aliasDeclaration
42
+ */
43
+ const checkParameters = aliasDeclaration => {
44
+ /* c8 ignore next -- Guard */
45
+ const {
46
+ params
47
+ } = aliasDeclaration.typeParameters ?? {
48
+ params: []
49
+ };
50
+ for (const {
51
+ name: {
52
+ name
53
+ }
54
+ } of params) {
55
+ usedNames.add(name);
56
+ }
57
+ for (const usedName of usedNames) {
58
+ if (!templateNames.includes(usedName)) {
59
+ report(`Missing @template ${usedName}`);
60
+ }
61
+ }
62
+ };
63
+ const handleTypeAliases = () => {
64
+ var _nde$declaration;
65
+ const nde = /** @type {import('@typescript-eslint/types').TSESTree.Node} */
66
+ node;
67
+ if (!nde) {
68
+ return;
69
+ }
70
+ switch (nde.type) {
71
+ case 'ExportNamedDeclaration':
72
+ if (((_nde$declaration = nde.declaration) === null || _nde$declaration === void 0 ? void 0 : _nde$declaration.type) === 'TSTypeAliasDeclaration') {
73
+ checkParameters(nde.declaration);
74
+ }
75
+ break;
76
+ case 'TSTypeAliasDeclaration':
77
+ checkParameters(nde);
78
+ break;
79
+ }
80
+ };
81
+ const typedefTags = utils.getTags('typedef');
82
+ if (!typedefTags.length || typedefTags.length >= 2) {
83
+ handleTypeAliases();
84
+ return;
85
+ }
86
+ const potentialType = typedefTags[0].type;
87
+ const parsedType = mode === 'permissive' ? (0, _jsdoccomment.tryParse)( /** @type {string} */potentialType) : (0, _jsdoccomment.parse)( /** @type {string} */potentialType, mode);
88
+ (0, _jsdoccomment.traverse)(parsedType, nde => {
89
+ const {
90
+ type,
91
+ value
92
+ } = /** @type {import('jsdoc-type-pratt-parser').NameResult} */nde;
93
+ if (type === 'JsdocTypeName' && /^[A-Z]$/.test(value)) {
94
+ usedNames.add(value);
95
+ }
96
+ });
97
+
98
+ // Could check against whitelist/blacklist
99
+ for (const usedName of usedNames) {
100
+ if (!templateNames.includes(usedName)) {
101
+ report(`Missing @template ${usedName}`, null, typedefTags[0]);
102
+ }
103
+ }
104
+ }, {
105
+ iterateAllJsdocs: true,
106
+ meta: {
107
+ docs: {
108
+ description: 'Requires template tags for each generic type parameter',
109
+ url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/require-template.md#repos-sticky-header'
110
+ },
111
+ schema: [{
112
+ additionalProperties: false,
113
+ properties: {
114
+ requireSeparateTemplates: {
115
+ type: 'boolean'
116
+ }
117
+ },
118
+ type: 'object'
119
+ }],
120
+ type: 'suggestion'
121
+ }
122
+ });
123
+ module.exports = exports.default;
124
+ //# sourceMappingURL=requireTemplate.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"requireTemplate.cjs","names":["_jsdoccomment","require","_iterateJsdoc","_interopRequireDefault","e","__esModule","default","_default","exports","iterateJsdoc","context","utils","node","settings","report","requireSeparateTemplates","options","mode","usedNames","Set","templateTags","getTags","templateNames","flatMap","name","split","tag","names","length","checkParameters","aliasDeclaration","params","typeParameters","add","usedName","includes","handleTypeAliases","_nde$declaration","nde","type","declaration","typedefTags","potentialType","parsedType","tryParseType","parseType","traverse","value","test","iterateAllJsdocs","meta","docs","description","url","schema","additionalProperties","properties","module"],"sources":["../../src/rules/requireTemplate.js"],"sourcesContent":["import {\n parse as parseType,\n traverse,\n tryParse as tryParseType,\n} from '@es-joy/jsdoccomment';\nimport iterateJsdoc from '../iterateJsdoc.js';\n\nexport default iterateJsdoc(({\n context,\n utils,\n node,\n settings,\n report,\n}) => {\n const {\n requireSeparateTemplates = false,\n } = context.options[0] || {};\n\n const {\n mode\n } = settings;\n\n const usedNames = new Set();\n const templateTags = utils.getTags('template');\n const templateNames = templateTags.flatMap(({name}) => {\n return name.split(/,\\s*/);\n });\n\n for (const tag of templateTags) {\n const {name} = tag;\n const names = name.split(/,\\s*/);\n if (requireSeparateTemplates && names.length > 1) {\n report(`Missing separate @template for ${names[1]}`, null, tag);\n }\n }\n\n /**\n * @param {import('@typescript-eslint/types').TSESTree.TSTypeAliasDeclaration} aliasDeclaration\n */\n const checkParameters = (aliasDeclaration) => {\n /* c8 ignore next -- Guard */\n const {params} = aliasDeclaration.typeParameters ?? {params: []};\n for (const {name: {name}} of params) {\n usedNames.add(name);\n }\n for (const usedName of usedNames) {\n if (!templateNames.includes(usedName)) {\n report(`Missing @template ${usedName}`);\n }\n }\n };\n\n const handleTypeAliases = () => {\n const nde = /** @type {import('@typescript-eslint/types').TSESTree.Node} */ (\n node\n );\n if (!nde) {\n return;\n }\n switch (nde.type) {\n case 'ExportNamedDeclaration':\n if (nde.declaration?.type === 'TSTypeAliasDeclaration') {\n checkParameters(nde.declaration);\n }\n break;\n case 'TSTypeAliasDeclaration':\n checkParameters(nde);\n break;\n }\n };\n\n const typedefTags = utils.getTags('typedef');\n if (!typedefTags.length || typedefTags.length >= 2) {\n handleTypeAliases();\n return;\n }\n\n const potentialType = typedefTags[0].type;\n const parsedType = mode === 'permissive' ?\n tryParseType(/** @type {string} */ (potentialType)) :\n parseType(/** @type {string} */ (potentialType), mode)\n\n traverse(parsedType, (nde) => {\n const {\n type,\n value,\n } = /** @type {import('jsdoc-type-pratt-parser').NameResult} */ (nde);\n if (type === 'JsdocTypeName' && (/^[A-Z]$/).test(value)) {\n usedNames.add(value);\n }\n });\n\n // Could check against whitelist/blacklist\n for (const usedName of usedNames) {\n if (!templateNames.includes(usedName)) {\n report(`Missing @template ${usedName}`, null, typedefTags[0]);\n }\n }\n}, {\n iterateAllJsdocs: true,\n meta: {\n docs: {\n description: 'Requires template tags for each generic type parameter',\n url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/require-template.md#repos-sticky-header',\n },\n schema: [\n {\n additionalProperties: false,\n properties: {\n requireSeparateTemplates: {\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,13 +5,13 @@
5
5
  "url": "http://gajus.com"
6
6
  },
7
7
  "dependencies": {
8
- "@es-joy/jsdoccomment": "~0.43.1",
8
+ "@es-joy/jsdoccomment": "~0.46.0",
9
9
  "are-docs-informative": "^0.0.2",
10
10
  "comment-parser": "1.4.1",
11
11
  "debug": "^4.3.5",
12
12
  "escape-string-regexp": "^4.0.0",
13
- "esquery": "^1.5.0",
14
- "parse-imports": "^2.1.0",
13
+ "esquery": "^1.6.0",
14
+ "parse-imports": "^2.1.1",
15
15
  "semver": "^7.6.2",
16
16
  "spdx-expression-parse": "^4.0.0",
17
17
  "synckit": "^0.9.0"
@@ -29,7 +29,7 @@
29
29
  "@es-joy/jsdoc-eslint-parser": "^0.21.1",
30
30
  "@hkdobrev/run-if-changed": "^0.3.1",
31
31
  "@semantic-release/commit-analyzer": "^13.0.0",
32
- "@semantic-release/github": "^10.0.6",
32
+ "@semantic-release/github": "^10.1.0",
33
33
  "@semantic-release/npm": "^12.0.1",
34
34
  "@types/chai": "^4.3.16",
35
35
  "@types/debug": "^4.1.12",
@@ -39,35 +39,35 @@
39
39
  "@types/json-schema": "^7.0.15",
40
40
  "@types/lodash.defaultsdeep": "^4.6.9",
41
41
  "@types/mocha": "^10.0.7",
42
- "@types/node": "^20.14.9",
42
+ "@types/node": "^20.14.10",
43
43
  "@types/semver": "^7.5.8",
44
44
  "@types/spdx-expression-parse": "^3.0.5",
45
- "@typescript-eslint/types": "^7.14.1",
45
+ "@typescript-eslint/types": "^7.16.0",
46
46
  "babel-plugin-add-module-exports": "^1.0.4",
47
- "babel-plugin-istanbul": "^6.1.1",
47
+ "babel-plugin-istanbul": "^7.0.0",
48
48
  "babel-plugin-transform-import-meta": "^2.2.1",
49
49
  "c8": "^10.1.2",
50
- "camelcase": "^6.3.0",
51
- "chai": "^4.3.10",
50
+ "camelcase": "^8.0.0",
51
+ "chai": "^5.1.1",
52
52
  "cross-env": "^7.0.3",
53
- "decamelize": "^5.0.1",
54
- "eslint": "9.5.0",
53
+ "decamelize": "^6.0.0",
54
+ "eslint": "9.6.0",
55
55
  "eslint-config-canonical": "~43.0.13",
56
56
  "espree": "^10.1.0",
57
- "gitdown": "^3.1.5",
57
+ "gitdown": "^4.0.0",
58
58
  "glob": "^10.4.2",
59
- "globals": "^15.6.0",
59
+ "globals": "^15.8.0",
60
60
  "husky": "^9.0.11",
61
61
  "jsdoc-type-pratt-parser": "^4.0.0",
62
62
  "json-schema": "^0.4.0",
63
63
  "lint-staged": "^15.2.7",
64
64
  "lodash.defaultsdeep": "^4.6.1",
65
- "mocha": "^10.5.2",
66
- "open-editor": "^3.0.0",
65
+ "mocha": "^10.6.0",
66
+ "open-editor": "^4.1.1",
67
67
  "replace": "^1.2.2",
68
68
  "rimraf": "^5.0.7",
69
69
  "semantic-release": "^24.0.0",
70
- "typescript": "5.3.x",
70
+ "typescript": "5.5.x",
71
71
  "typescript-eslint": "^8.0.0-alpha.34"
72
72
  },
73
73
  "engines": {
@@ -144,5 +144,5 @@
144
144
  "test-cov": "cross-env TIMING=1 c8 --reporter text npm run test-no-cov",
145
145
  "test-index": "npm run test-no-cov -- test/rules/index.js"
146
146
  },
147
- "version": "48.5.2"
147
+ "version": "48.7.0"
148
148
  }
package/src/index.js CHANGED
@@ -9,6 +9,7 @@ import checkSyntax from './rules/checkSyntax.js';
9
9
  import checkTagNames from './rules/checkTagNames.js';
10
10
  import checkTypes from './rules/checkTypes.js';
11
11
  import checkValues from './rules/checkValues.js';
12
+ import convertToJsdocComments from './rules/convertToJsdocComments.js';
12
13
  import emptyTags from './rules/emptyTags.js';
13
14
  import implementsOnClasses from './rules/implementsOnClasses.js';
14
15
  import importsAsDependencies from './rules/importsAsDependencies.js';
@@ -44,6 +45,7 @@ import requireReturns from './rules/requireReturns.js';
44
45
  import requireReturnsCheck from './rules/requireReturnsCheck.js';
45
46
  import requireReturnsDescription from './rules/requireReturnsDescription.js';
46
47
  import requireReturnsType from './rules/requireReturnsType.js';
48
+ import requireTemplate from './rules/requireTemplate.js';
47
49
  import requireThrows from './rules/requireThrows.js';
48
50
  import requireYields from './rules/requireYields.js';
49
51
  import requireYieldsCheck from './rules/requireYieldsCheck.js';
@@ -81,6 +83,7 @@ const index = {
81
83
  'check-tag-names': checkTagNames,
82
84
  'check-types': checkTypes,
83
85
  'check-values': checkValues,
86
+ 'convert-to-jsdoc-comments': convertToJsdocComments,
84
87
  'empty-tags': emptyTags,
85
88
  'implements-on-classes': implementsOnClasses,
86
89
  'imports-as-dependencies': importsAsDependencies,
@@ -116,6 +119,7 @@ const index = {
116
119
  'require-returns-check': requireReturnsCheck,
117
120
  'require-returns-description': requireReturnsDescription,
118
121
  'require-returns-type': requireReturnsType,
122
+ 'require-template': requireTemplate,
119
123
  'require-throws': requireThrows,
120
124
  'require-yields': requireYields,
121
125
  'require-yields-check': requireYieldsCheck,
@@ -153,6 +157,7 @@ const createRecommendedRuleset = (warnOrError, flatName) => {
153
157
  'jsdoc/check-tag-names': warnOrError,
154
158
  'jsdoc/check-types': warnOrError,
155
159
  'jsdoc/check-values': warnOrError,
160
+ 'jsdoc/convert-to-jsdoc-comments': 'off',
156
161
  'jsdoc/empty-tags': warnOrError,
157
162
  'jsdoc/implements-on-classes': warnOrError,
158
163
  'jsdoc/imports-as-dependencies': 'off',
@@ -188,6 +193,7 @@ const createRecommendedRuleset = (warnOrError, flatName) => {
188
193
  'jsdoc/require-returns-check': warnOrError,
189
194
  'jsdoc/require-returns-description': warnOrError,
190
195
  'jsdoc/require-returns-type': warnOrError,
196
+ 'jsdoc/require-template': 'off',
191
197
  'jsdoc/require-throws': 'off',
192
198
  'jsdoc/require-yields': warnOrError,
193
199
  'jsdoc/require-yields-check': warnOrError,
package/src/jsdocUtils.js CHANGED
@@ -1321,7 +1321,7 @@ const parseClosureTemplateTag = (tag) => {
1321
1321
  * @param {{
1322
1322
  * contexts?: import('./iterateJsdoc.js').Context[]
1323
1323
  * }} settings
1324
- * @returns {string[]}
1324
+ * @returns {(string|import('./iterateJsdoc.js').ContextObject)[]}
1325
1325
  */
1326
1326
  const enforcedContexts = (context, defaultContexts, settings) => {
1327
1327
  const contexts = context.options[0]?.contexts || settings.contexts || (defaultContexts === true ? [
@@ -0,0 +1,384 @@
1
+ import iterateJsdoc from '../iterateJsdoc.js';
2
+ import {
3
+ getSettings,
4
+ } from '../iterateJsdoc.js';
5
+ import jsdocUtils from '../jsdocUtils.js';
6
+ import {
7
+ getNonJsdocComment,
8
+ getDecorator,
9
+ getReducedASTNode,
10
+ getFollowingComment,
11
+ } from '@es-joy/jsdoccomment';
12
+
13
+ /** @type {import('eslint').Rule.RuleModule} */
14
+ export default {
15
+ create (context) {
16
+ /**
17
+ * @typedef {import('eslint').AST.Token | import('estree').Comment | {
18
+ * type: import('eslint').AST.TokenType|"Line"|"Block"|"Shebang",
19
+ * range: [number, number],
20
+ * value: string
21
+ * }} Token
22
+ */
23
+
24
+ /**
25
+ * @callback AddComment
26
+ * @param {boolean|undefined} inlineCommentBlock
27
+ * @param {Token} comment
28
+ * @param {string} indent
29
+ * @param {number} lines
30
+ * @param {import('eslint').Rule.RuleFixer} fixer
31
+ */
32
+
33
+ /* c8 ignore next -- Fallback to deprecated method */
34
+ const {
35
+ sourceCode = context.getSourceCode(),
36
+ } = context;
37
+ const settings = getSettings(context);
38
+ if (!settings) {
39
+ return {};
40
+ }
41
+
42
+ const {
43
+ contexts = settings.contexts || [],
44
+ contextsAfter = /** @type {string[]} */ ([]),
45
+ contextsBeforeAndAfter = [
46
+ 'VariableDeclarator', 'TSPropertySignature', 'PropertyDefinition'
47
+ ],
48
+ enableFixer = true,
49
+ enforceJsdocLineStyle = 'multi',
50
+ lineOrBlockStyle = 'both',
51
+ allowedPrefixes = ['@ts-', 'istanbul ', 'c8 ', 'v8 ', 'eslint', 'prettier-']
52
+ } = context.options[0] ?? {};
53
+
54
+ let reportingNonJsdoc = false;
55
+
56
+ /**
57
+ * @param {string} messageId
58
+ * @param {import('estree').Comment|Token} comment
59
+ * @param {import('eslint').Rule.Node} node
60
+ * @param {import('eslint').Rule.ReportFixer} fixer
61
+ */
62
+ const report = (messageId, comment, node, fixer) => {
63
+ const loc = {
64
+ end: {
65
+ column: 0,
66
+ /* c8 ignore next 2 -- Guard */
67
+ // @ts-expect-error Ok
68
+ line: (comment.loc?.start?.line ?? 1),
69
+ },
70
+ start: {
71
+ column: 0,
72
+ /* c8 ignore next 2 -- Guard */
73
+ // @ts-expect-error Ok
74
+ line: (comment.loc?.start?.line ?? 1)
75
+ },
76
+ };
77
+
78
+ context.report({
79
+ fix: enableFixer ? fixer : null,
80
+ loc,
81
+ messageId,
82
+ node,
83
+ });
84
+ };
85
+
86
+ /**
87
+ * @param {import('eslint').Rule.Node} node
88
+ * @param {import('eslint').AST.Token | import('estree').Comment | {
89
+ * type: import('eslint').AST.TokenType|"Line"|"Block"|"Shebang",
90
+ * range: [number, number],
91
+ * value: string
92
+ * }} comment
93
+ * @param {AddComment} addComment
94
+ * @param {import('../iterateJsdoc.js').Context[]} ctxts
95
+ */
96
+ const getFixer = (node, comment, addComment, ctxts) => {
97
+ return /** @type {import('eslint').Rule.ReportFixer} */ (fixer) => {
98
+ // Default to one line break if the `minLines`/`maxLines` settings allow
99
+ const lines = settings.minLines === 0 && settings.maxLines >= 1 ? 1 : settings.minLines;
100
+ let baseNode =
101
+ /**
102
+ * @type {import('@typescript-eslint/types').TSESTree.Node|import('eslint').Rule.Node}
103
+ */ (
104
+ getReducedASTNode(node, sourceCode)
105
+ );
106
+
107
+ const decorator = getDecorator(
108
+ /** @type {import('eslint').Rule.Node} */
109
+ (baseNode)
110
+ );
111
+ if (decorator) {
112
+ baseNode = /** @type {import('@typescript-eslint/types').TSESTree.Decorator} */ (
113
+ decorator
114
+ );
115
+ }
116
+
117
+ const indent = jsdocUtils.getIndent({
118
+ text: sourceCode.getText(
119
+ /** @type {import('eslint').Rule.Node} */ (baseNode),
120
+ /** @type {import('eslint').AST.SourceLocation} */
121
+ (
122
+ /** @type {import('eslint').Rule.Node} */ (baseNode).loc
123
+ ).start.column,
124
+ ),
125
+ });
126
+
127
+ const {
128
+ inlineCommentBlock,
129
+ } =
130
+ /**
131
+ * @type {{
132
+ * context: string,
133
+ * inlineCommentBlock: boolean,
134
+ * minLineCount: import('../iterateJsdoc.js').Integer
135
+ * }[]}
136
+ */ (ctxts).find((contxt) => {
137
+ if (typeof contxt === 'string') {
138
+ return false;
139
+ }
140
+
141
+ const {
142
+ context: ctxt,
143
+ } = contxt;
144
+ return ctxt === node.type;
145
+ }) || {};
146
+
147
+ return addComment(inlineCommentBlock, comment, indent, lines, fixer);
148
+ };
149
+ };
150
+
151
+ /**
152
+ * @param {import('eslint').AST.Token | import('estree').Comment | {
153
+ * type: import('eslint').AST.TokenType|"Line"|"Block"|"Shebang",
154
+ * range: [number, number],
155
+ * value: string
156
+ * }} comment
157
+ * @param {import('eslint').Rule.Node} node
158
+ * @param {AddComment} addComment
159
+ * @param {import('../iterateJsdoc.js').Context[]} ctxts
160
+ */
161
+ const reportings = (comment, node, addComment, ctxts) => {
162
+ const fixer = getFixer(node, comment, addComment, ctxts);
163
+
164
+ if (comment.type === 'Block') {
165
+ if (lineOrBlockStyle === 'line') {
166
+ return;
167
+ }
168
+ report('blockCommentsJsdocStyle', comment, node, fixer);
169
+ return;
170
+ }
171
+
172
+ if (comment.type === 'Line') {
173
+ if (lineOrBlockStyle === 'block') {
174
+ return;
175
+ }
176
+ report('lineCommentsJsdocStyle', comment, node, fixer);
177
+ }
178
+ };
179
+
180
+ /**
181
+ * @type {import('../iterateJsdoc.js').CheckJsdoc}
182
+ */
183
+ const checkNonJsdoc = (_info, _handler, node) => {
184
+ const comment = getNonJsdocComment(sourceCode, node, settings);
185
+
186
+ if (
187
+ !comment ||
188
+ /** @type {string[]} */
189
+ (allowedPrefixes).some((prefix) => {
190
+ return comment.value.trimStart().startsWith(prefix);
191
+ })
192
+ ) {
193
+ return;
194
+ }
195
+
196
+ reportingNonJsdoc = true;
197
+
198
+ /** @type {AddComment} */
199
+ const addComment = (inlineCommentBlock, comment, indent, lines, fixer) => {
200
+ const insertion = (
201
+ inlineCommentBlock || enforceJsdocLineStyle === 'single'
202
+ ? `/** ${comment.value.trim()} `
203
+ : `/**\n${indent}*${comment.value.trimEnd()}\n${indent}`
204
+ ) +
205
+ `*/${'\n'.repeat((lines || 1) - 1)}`;
206
+
207
+ return fixer.replaceText(
208
+ /** @type {import('eslint').AST.Token} */
209
+ (comment),
210
+ insertion,
211
+ );
212
+ };
213
+
214
+ reportings(comment, node, addComment, contexts);
215
+ };
216
+
217
+ /**
218
+ * @param {import('eslint').Rule.Node} node
219
+ * @param {import('../iterateJsdoc.js').Context[]} ctxts
220
+ */
221
+ const checkNonJsdocAfter = (node, ctxts) => {
222
+ const comment = getFollowingComment(sourceCode, node);
223
+
224
+ if (
225
+ !comment ||
226
+ comment.value.startsWith('*') ||
227
+ /** @type {string[]} */
228
+ (allowedPrefixes).some((prefix) => {
229
+ return comment.value.trimStart().startsWith(prefix);
230
+ })
231
+ ) {
232
+ return;
233
+ }
234
+
235
+ /** @type {AddComment} */
236
+ const addComment = (inlineCommentBlock, comment, indent, lines, fixer) => {
237
+ const insertion = (
238
+ inlineCommentBlock || enforceJsdocLineStyle === 'single'
239
+ ? `/** ${comment.value.trim()} `
240
+ : `/**\n${indent}*${comment.value.trimEnd()}\n${indent}`
241
+ ) +
242
+ `*/${'\n'.repeat((lines || 1) - 1)}${lines ? `\n${indent.slice(1)}` : ' '}`;
243
+
244
+ return [fixer.remove(
245
+ /** @type {import('eslint').AST.Token} */
246
+ (comment)
247
+ ), fixer.insertTextBefore(
248
+ node.type === 'VariableDeclarator' ? node.parent : node,
249
+ insertion,
250
+ )];
251
+ };
252
+
253
+ reportings(comment, node, addComment, ctxts);
254
+ };
255
+
256
+ // Todo: add contexts to check after (and handle if want both before and after)
257
+ return {
258
+ ...jsdocUtils.getContextObject(
259
+ jsdocUtils.enforcedContexts(context, true, settings),
260
+ checkNonJsdoc,
261
+ ),
262
+ ...jsdocUtils.getContextObject(
263
+ contextsAfter,
264
+ (_info, _handler, node) => {
265
+ checkNonJsdocAfter(node, contextsAfter);
266
+ },
267
+ ),
268
+ ...jsdocUtils.getContextObject(
269
+ contextsBeforeAndAfter,
270
+ (_info, _handler, node) => {
271
+ checkNonJsdoc({}, null, node);
272
+ if (!reportingNonJsdoc) {
273
+ checkNonJsdocAfter(node, contextsBeforeAndAfter);
274
+ }
275
+ }
276
+ )
277
+ };
278
+ },
279
+ meta: {
280
+ fixable: 'code',
281
+
282
+ messages: {
283
+ blockCommentsJsdocStyle: 'Block comments should be JSDoc-style.',
284
+ lineCommentsJsdocStyle: 'Line comments should be JSDoc-style.',
285
+ },
286
+
287
+ docs: {
288
+ description: 'Converts non-JSDoc comments preceding or following nodes into JSDoc ones',
289
+ url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/convert-to-jsdoc-comments.md#repos-sticky-header',
290
+ },
291
+ schema: [
292
+ {
293
+ additionalProperties: false,
294
+ properties: {
295
+ allowedPrefixes: {
296
+ type: 'array',
297
+ items: {
298
+ type: 'string'
299
+ }
300
+ },
301
+ contexts: {
302
+ items: {
303
+ anyOf: [
304
+ {
305
+ type: 'string',
306
+ },
307
+ {
308
+ additionalProperties: false,
309
+ properties: {
310
+ context: {
311
+ type: 'string',
312
+ },
313
+ inlineCommentBlock: {
314
+ type: 'boolean',
315
+ },
316
+ },
317
+ type: 'object',
318
+ },
319
+ ],
320
+ },
321
+ type: 'array',
322
+ },
323
+ contextsAfter: {
324
+ items: {
325
+ anyOf: [
326
+ {
327
+ type: 'string',
328
+ },
329
+ {
330
+ additionalProperties: false,
331
+ properties: {
332
+ context: {
333
+ type: 'string',
334
+ },
335
+ inlineCommentBlock: {
336
+ type: 'boolean',
337
+ },
338
+ },
339
+ type: 'object',
340
+ },
341
+ ],
342
+ },
343
+ type: 'array',
344
+ },
345
+ contextsBeforeAndAfter: {
346
+ items: {
347
+ anyOf: [
348
+ {
349
+ type: 'string',
350
+ },
351
+ {
352
+ additionalProperties: false,
353
+ properties: {
354
+ context: {
355
+ type: 'string',
356
+ },
357
+ inlineCommentBlock: {
358
+ type: 'boolean',
359
+ },
360
+ },
361
+ type: 'object',
362
+ },
363
+ ],
364
+ },
365
+ type: 'array',
366
+ },
367
+ enableFixer: {
368
+ type: 'boolean'
369
+ },
370
+ enforceJsdocLineStyle: {
371
+ type: 'string',
372
+ enum: ['multi', 'single']
373
+ },
374
+ lineOrBlockStyle: {
375
+ type: 'string',
376
+ enum: ['block', 'line', 'both']
377
+ },
378
+ },
379
+ type: 'object',
380
+ },
381
+ ],
382
+ type: 'suggestion',
383
+ },
384
+ };
@@ -18,6 +18,11 @@ import {
18
18
  * }} RequireJsdocOpts
19
19
  */
20
20
 
21
+ /**
22
+ * @typedef {import('eslint').Rule.Node|
23
+ * import('@typescript-eslint/types').TSESTree.Node} ESLintOrTSNode
24
+ */
25
+
21
26
  /** @type {import('json-schema').JSONSchema4} */
22
27
  const OPTIONS_SCHEMA = {
23
28
  additionalProperties: false,
@@ -411,10 +416,13 @@ export default {
411
416
  const fix = /** @type {import('eslint').Rule.ReportFixer} */ (fixer) => {
412
417
  // Default to one line break if the `minLines`/`maxLines` settings allow
413
418
  const lines = settings.minLines === 0 && settings.maxLines >= 1 ? 1 : settings.minLines;
414
- /** @type {import('eslint').Rule.Node|import('@typescript-eslint/types').TSESTree.Decorator} */
419
+ /** @type {ESLintOrTSNode|import('@typescript-eslint/types').TSESTree.Decorator} */
415
420
  let baseNode = getReducedASTNode(node, sourceCode);
416
421
 
417
- const decorator = getDecorator(baseNode);
422
+ const decorator = getDecorator(
423
+ /** @type {import('eslint').Rule.Node} */
424
+ (baseNode)
425
+ );
418
426
  if (decorator) {
419
427
  baseNode = decorator;
420
428
  }