eslint-plugin-lube 0.7.0 → 0.8.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/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.8.0
4
+
5
+ ### Breaking
6
+
7
+ - **The strict config moves inline parameter types into `@param` tags** with the new
8
+ `prefer-param-tag`: `function f(/** @type {T} */ a) {}` becomes `@param {T} a` in the JSDoc block of
9
+ the declaration. It has a fix, so run `eslint --fix` once; a destructured parameter is left to you.
10
+
11
+ ### Added
12
+
13
+ - `prefer-param-tag`, in the strict config: reports a function declaration whose parameters are typed
14
+ with inline comments, `function f(/** @type {T} */ a) {}`, and fixes it into `@param {T} a` tags in the
15
+ JSDoc block of the declaration, creating the block if there is none, each tag at the position of its
16
+ parameter among the existing `@param` tags. Arrow functions and function expressions stay, and a
17
+ destructured parameter is reported without a fix.
18
+
3
19
  ## 0.7.0
4
20
 
5
21
  ### Breaking
package/README.md CHANGED
@@ -89,6 +89,7 @@ Both configs are flat config objects with `plugins` and `rules`, and differ in h
89
89
 
90
90
  **`configs.strict`** is the house style of this repository, and several of its rules cannot be fixed. It is everything in `recommended` plus:
91
91
  - `svelte-naming-convention`, which renames declarations to snake_case.
92
+ - `prefer-param-tag`, which moves the inline `/** @type {T} */` comments of a function declaration's parameters into `@param` tags on the declaration ([prefer-param-tag](https://github.com/artxe/lube-series/blob/master/packages/eslint-plugin-lube/docs/prefer-param-tag.md)).
92
93
  - `prefer-type-tag`, which moves the cast of a declaration's value into a `@type` tag on the declaration, in a project linted with type information ([prefer-type-tag](https://github.com/artxe/lube-series/blob/master/packages/eslint-plugin-lube/docs/prefer-type-tag.md) shows the parser options).
93
94
  - `ascii-order` for import statements, object keys, function declarations and test cases too (see below).
94
95
  - `pretty-sequence` and `pretty-imports` at 30 characters.
@@ -111,6 +112,7 @@ Override a rule after the spread: `rules: { ...lube.configs.recommended.rules, "
111
112
  | --- | --- | --- |
112
113
  | [ascii-order](https://github.com/artxe/lube-series/blob/master/packages/eslint-plugin-lube/docs/ascii-order.md) | ASCII order of import statements, import and export names, object keys, function declarations and test cases | names only |
113
114
  | [curly](https://eslint.org/docs/latest/rules/curly) | Braces around the bodies of `if`, `else`, `for`, `while` and `do`, as ESLint's own rule does, with a fix that keeps a comment in front of the body, such as a JSDoc cast, inside the braces | no |
115
+ | [prefer-param-tag](https://github.com/artxe/lube-series/blob/master/packages/eslint-plugin-lube/docs/prefer-param-tag.md) | JSDoc `@param` tags on a function declaration instead of inline `/** @type {T} */` comments in its parameter list | no |
114
116
  | [prefer-type-tag](https://github.com/artxe/lube-series/blob/master/packages/eslint-plugin-lube/docs/prefer-type-tag.md) | A JSDoc `@type` tag on a declaration instead of a cast of its value, wherever the tag would type check (needs type information) | no |
115
117
  | [pretty-imports](https://github.com/artxe/lube-series/blob/master/packages/eslint-plugin-lube/docs/pretty-imports.md) | Import and export names on one line when they fit, or one per line | yes |
116
118
  | [pretty-jsdoc-casting](https://github.com/artxe/lube-series/blob/master/packages/eslint-plugin-lube/docs/pretty-jsdoc-casting.md) | JSDoc type casts written as `/** @type {T} */(value)/**/` | yes |
@@ -0,0 +1,25 @@
1
+ # prefer-param-tag
2
+ Enforces JSDoc `@param` tags on a function declaration, instead of inline `/** @type {T} */` comments inside its parameter list.
3
+
4
+ - Only function declarations are checked, `export` and `export default` ones too. Arrow functions and function expressions, such as `items.map((/** @type {string} */ x) => x)`, have no declaration to carry a tag and are left alone, and so are casts in a body.
5
+ - The fix removes the inline comments and adds one `@param {T} name` line per parameter to the JSDoc block above the declaration, in parameter order, after the existing `@param` tags and before `@returns` and the like. It creates the block when there is none, and keeps the indentation of the declaration. A `@param` tag that already names the parameter is not repeated.
6
+ - A default value, `/** @type {number} */ n = 1`, is typed by `@param {number} n` too.
7
+ - A destructured or rest parameter has no simple name to write in a tag and is reported without a fix. So is a declaration that does not start its line, where a block cannot be added.
8
+
9
+ ## options
10
+ None.
11
+
12
+ ## Valid
13
+ ```js
14
+ /**
15
+ * @param {string} tx
16
+ */
17
+ function use(tx) {}
18
+ items.map((/** @type {string} */ x) => x)
19
+ ```
20
+
21
+ ## Invalid
22
+ ```js
23
+ function use(/** @type {string} */ tx) {}
24
+ async function source(/** @type {unknown} */ n, /** @type {number} */ m = 1) {}
25
+ ```
package/package.json CHANGED
@@ -10,7 +10,7 @@
10
10
  "devDependencies": {
11
11
  "@types/estree": "^1.0.9",
12
12
  "@typescript/native": "npm:typescript@^7.0.2",
13
- "eslint": "^10.10.0"
13
+ "eslint": "^10.12.0"
14
14
  },
15
15
  "engines": {
16
16
  "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -53,10 +53,10 @@
53
53
  },
54
54
  "type": "module",
55
55
  "types": "types/index.d.ts",
56
- "version": "0.7.0",
56
+ "version": "0.8.0",
57
57
  "scripts": {
58
58
  "dts": "node ../../scripts/tsc_with_clean.js eslint-plugin-lube",
59
59
  "lint": "pnpm -w lint",
60
- "test": "pnpm -w exec vitest run --coverage test/eslint-plugin-lube"
60
+ "test": "pnpm -w exec vitest run --coverage --coverage.include=packages/eslint-plugin-lube/src/** test/eslint-plugin-lube"
61
61
  }
62
62
  }
package/src/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import ascii_order from "./rules/ascii-order.js"
2
+ import prefer_param_tag from "./rules/prefer-param-tag.js"
2
3
  import prefer_type_tag from "./rules/prefer-type-tag.js"
3
4
  import pretty_imports from "./rules/pretty-imports.js"
4
5
  import pretty_jsdoc_casting from "./rules/pretty-jsdoc-casting.js"
@@ -152,6 +153,7 @@ const strict_rules = {
152
153
  }
153
154
  ],
154
155
  "lube/curly": [ "error", "all" ],
156
+ "lube/prefer-param-tag": "error",
155
157
  "lube/prefer-type-tag": "error",
156
158
  "lube/pretty-imports": "error",
157
159
  "lube/pretty-jsdoc-casting": "error",
@@ -246,6 +248,7 @@ const plugin = {
246
248
  "curly": keep_body_comments(
247
249
  /** @type {import("eslint").Rule.RuleModule} */(builtinRules.get("curly"))/**/
248
250
  ),
251
+ "prefer-param-tag": prefer_param_tag,
249
252
  "prefer-type-tag": prefer_type_tag,
250
253
  "pretty-imports": pretty_imports,
251
254
  "pretty-jsdoc-casting": pretty_jsdoc_casting,
@@ -251,13 +251,8 @@ function create_reorder(context) {
251
251
  * @returns {string}
252
252
  */
253
253
  function get_snippet(node) {
254
- const value = node.type == "Property"
255
- ? node.computed && node.key.type != "Literal"
256
- ? node.key
257
- : node.value
258
- : node
259
254
  const snippet = source_code.getText(
260
- /** @type {import("estree").Node} */(value)/**/
255
+ /** @type {import("estree").Property} */(node)/**/.value
261
256
  ).replace(/\s+/g, " ")
262
257
  return snippet.length > 30 ? snippet.slice(0, 29) + "…" : snippet
263
258
  }
@@ -341,7 +336,7 @@ function get_name(node) {
341
336
  case "TemplateLiteral":
342
337
  return node.expressions.length
343
338
  ? undefined
344
- : /** @type {import("estree").TemplateElement} */(node.quasis[0])/**/.value.cooked ?? undefined
339
+ : /** @type {string} */(/** @type {import("estree").TemplateElement} */(node.quasis[0])/**/.value.cooked)/**/
345
340
  }
346
341
  return undefined
347
342
  }
@@ -0,0 +1,252 @@
1
+ const blank_regex = /^\s*\*?\s*$/
2
+ const directive_regex = /^\s*(?:eslint-disable-next-line\b|@ts-(?:expect-error|ignore)\b)/
3
+ const line_break_regex = /\s*(?:\r\n|[\n\r\p{Zl}\p{Zp}])\s*/gu
4
+ const line_terminator_regex = /\r\n|[\n\r\p{Zl}\p{Zp}]/u
5
+ const param_tag_regex = /^\s*\*\s*@param\b/
6
+ const space_regex = /^\s*$/
7
+ const tag_after_param_regex = /^\s*\*\s*@(?:deprecated|example|returns?|see|throws|yields?)\b/
8
+ const tag_regex = /^\s*\*\s*@/
9
+ const type_regex = /^\*\s*@type\s*(\{[^]*\})\s*$/
10
+ const white_space_regex = /\s/
11
+ /**
12
+ * @param {string} text
13
+ * @returns {string}
14
+ */
15
+ function escape_regex(text) {
16
+ return text.replace(/[$()*+.?[\\\]^{|}]/g, "\\$&")
17
+ }
18
+ /**
19
+ * @param {import("estree").Comment} comment
20
+ * @param {string} indent
21
+ * @param {string} eol
22
+ * @param {{ index: number, tag: string }[]} tags
23
+ * @returns {string}
24
+ */
25
+ function jsdoc_with(comment, indent, eol, tags) {
26
+ let rows = `/*${comment.value}*/`.split(line_terminator_regex)
27
+ /**
28
+ * @param {number} index
29
+ * @returns {string}
30
+ */
31
+ function row_at(index) {
32
+ return /** @type {string} */(rows[index])/**/
33
+ }
34
+ if (rows.length < 2 || !/^\/\*\*\s*$/.test(row_at(0)) || !/^\s*\*\/$/.test(row_at(rows.length - 1))) {
35
+ const body = comment.value.slice(1).split(line_terminator_regex)
36
+ .map(
37
+ row => row.replace(/^\s*\*?\s?/, "").trimEnd()
38
+ )
39
+ while (body[0] === "") {
40
+ body.shift()
41
+ }
42
+ while (body.at(-1) === "") {
43
+ body.pop()
44
+ }
45
+ rows = [
46
+ "/**",
47
+ ...body.map(
48
+ row => `${indent} * ${row}`.trimEnd()
49
+ ),
50
+ `${indent} */`
51
+ ]
52
+ }
53
+ for (const { index, tag } of tags) {
54
+ const rows_end = rows.length - 1
55
+ /** @type {{ end: number, start: number }[]} */
56
+ const blocks = []
57
+ for (let i = 1; i < rows_end; i++) {
58
+ if (param_tag_regex.test(row_at(i))) {
59
+ let end = i + 1
60
+ while (end < rows_end && !tag_regex.test(row_at(end)) && !blank_regex.test(row_at(end))) {
61
+ end++
62
+ }
63
+ blocks.push({ end, start: i })
64
+ }
65
+ }
66
+ let at = rows_end
67
+ const first = blocks[0]
68
+ if (first) {
69
+ at = index == 0
70
+ ? first.start
71
+ : /** @type {{ end: number, start: number }} */(blocks[Math.min(index, blocks.length) - 1])/**/.end
72
+ } else {
73
+ const tag_at = rows.findIndex(
74
+ row => tag_after_param_regex.test(row)
75
+ )
76
+ if (tag_at >= 0) {
77
+ at = tag_at
78
+ }
79
+ }
80
+ const added = [ `${indent} * ${tag}` ]
81
+ if (at == rows_end && at > 1 && !tag_regex.test(row_at(at - 1)) && !blank_regex.test(row_at(at - 1))) {
82
+ added.unshift(`${indent} *`)
83
+ }
84
+ rows.splice(at, 0, ...added)
85
+ }
86
+ return rows.join(eol)
87
+ }
88
+ /** @type {import("eslint").Rule.RuleModule} */
89
+ export default {
90
+ create(context) {
91
+ const source_code = context.sourceCode
92
+ const text = source_code.text
93
+ /**
94
+ * @param {import("estree").Comment} comment
95
+ * @returns {import("eslint").AST.Range}
96
+ */
97
+ function comment_range(comment) {
98
+ return /** @type {import("eslint").AST.Range} */(comment.range)/**/
99
+ }
100
+ /**
101
+ * @param {number} position
102
+ * @returns {number}
103
+ */
104
+ function line_start_of(position) {
105
+ let start = position
106
+ while (start > 0 && !line_terminator_regex.test(text.charAt(start - 1))) {
107
+ start--
108
+ }
109
+ return start
110
+ }
111
+ /**
112
+ * @param {import("estree").Comment} comment
113
+ * @returns {boolean}
114
+ */
115
+ function starts_line(comment) {
116
+ const start = comment_range(comment)[0]
117
+ return space_regex.test(
118
+ text.slice(line_start_of(start), start)
119
+ )
120
+ }
121
+ return {
122
+ FunctionDeclaration(node) {
123
+ /** @type {{ comment: import("estree").Comment, index: number, name: string, type: string }[]} */
124
+ const named = []
125
+ const data = {
126
+ name: node.id?.name ?? "default"
127
+ }
128
+ for (const [ index, param ] of node.params.entries()) {
129
+ const comment = source_code.getCommentsBefore(param).findLast(
130
+ item => item.type == "Block" && type_regex.test(item.value)
131
+ )
132
+ if (!comment) {
133
+ continue
134
+ }
135
+ const target = param.type == "AssignmentPattern" ? param.left : param
136
+ if (target.type == "Identifier") {
137
+ named.push(
138
+ {
139
+ comment,
140
+ index,
141
+ name: target.name,
142
+ type: /** @type {string} */(/** @type {RegExpExecArray} */(type_regex.exec(comment.value))/**/[1])/**/.replace(line_break_regex, " ")
143
+ }
144
+ )
145
+ } else {
146
+ context.report(
147
+ {
148
+ data,
149
+ loc: /** @type {import("estree").SourceLocation} */(comment.loc)/**/,
150
+ messageId: "name"
151
+ }
152
+ )
153
+ }
154
+ }
155
+ const first = named[0]
156
+ if (!first) {
157
+ return
158
+ }
159
+ const loc = /** @type {import("estree").SourceLocation} */(first.comment.loc)/**/
160
+ const statement = node.parent.type == "ExportNamedDeclaration" || node.parent.type == "ExportDefaultDeclaration"
161
+ ? node.parent
162
+ : node
163
+ const leading = source_code.getCommentsBefore(statement)
164
+ let start = /** @type {import("eslint").AST.Range} */(statement.range)/**/[0]
165
+ let last = leading.length - 1
166
+ while (last >= 0) {
167
+ const item = /** @type {import("estree").Comment} */(leading[last])/**/
168
+ if (!starts_line(item) || !directive_regex.test(item.value)) {
169
+ break
170
+ }
171
+ start = comment_range(item)[0]
172
+ last--
173
+ }
174
+ const candidate = leading[last]
175
+ const jsdoc = candidate?.type == "Block" && candidate.value.startsWith("*")
176
+ ? candidate
177
+ : undefined
178
+ const anchor = jsdoc ? comment_range(jsdoc)[0] : start
179
+ const indent = text.slice(line_start_of(anchor), anchor)
180
+ if (!space_regex.test(indent)) {
181
+ context.report(
182
+ { data, loc, messageId: "hand" }
183
+ )
184
+ return
185
+ }
186
+ const eol = text.match(line_terminator_regex)?.[0] ?? "\n"
187
+ const tags = named.filter(
188
+ item => !jsdoc || !new RegExp(
189
+ `@param\\s+(?:\\{[^]*?\\}\\s+)?\\[?${escape_regex(item.name)}(?![\\w$])`
190
+ ).test(jsdoc.value)
191
+ ).map(
192
+ item => (
193
+ {
194
+ index: item.index,
195
+ tag: `@param ${item.type} ${item.name}`
196
+ }
197
+ )
198
+ )
199
+ context.report(
200
+ {
201
+ data,
202
+ fix(fixer) {
203
+ const fixes = named.map(
204
+ ({ comment }) => {
205
+ const [ begin, close ] = comment_range(comment)
206
+ let end = close
207
+ while (white_space_regex.test(text.charAt(end))) {
208
+ end++
209
+ }
210
+ return fixer.removeRange([ begin, end ])
211
+ }
212
+ )
213
+ if (jsdoc) {
214
+ fixes.push(
215
+ fixer.replaceTextRange(
216
+ comment_range(jsdoc),
217
+ jsdoc_with(jsdoc, indent, eol, tags)
218
+ )
219
+ )
220
+ } else {
221
+ fixes.push(
222
+ fixer.insertTextBeforeRange(
223
+ [ start, start ],
224
+ `/**${eol}${tags.map(({ tag }) => `${indent} * ${tag}${eol}`).join("")}${indent} */${eol}${indent}`
225
+ )
226
+ )
227
+ }
228
+ return fixes
229
+ },
230
+ loc,
231
+ messageId: "tag"
232
+ }
233
+ )
234
+ }
235
+ }
236
+ },
237
+ meta: {
238
+ docs: {
239
+ description: "Enforces JSDoc `@param` tags on a function declaration instead of inline `/** @type {T} */` comments inside its parameter list.",
240
+ recommended: false,
241
+ url: "https://github.com/artxe/lube-series/blob/master/packages/eslint-plugin-lube/docs/prefer-param-tag.md"
242
+ },
243
+ fixable: "code",
244
+ messages: {
245
+ hand: "Type the parameters of `{{name}}` with `@param` tags on the declaration instead of inline comments; it is not fixed automatically because the declaration or its JSDoc comment does not start its line, so write the tags by hand",
246
+ name: "Type this parameter of `{{name}}` with a `@param` tag on the declaration instead of an inline comment; it is not fixed automatically because the parameter has no simple name to write in the tag, so document it by hand",
247
+ tag: "Type the parameters of `{{name}}` with `@param` tags on the declaration instead of inline `@type` comments"
248
+ },
249
+ schema: [],
250
+ type: "suggestion"
251
+ }
252
+ }
@@ -52,9 +52,6 @@ export default {
52
52
  const open_index = tokens.findIndex(
53
53
  token => token.range[0] == first.range[0]
54
54
  ) - 1
55
- if (tokens[open_index]?.value != "{") {
56
- return
57
- }
58
55
  const head = tokens.slice(0, open_index)
59
56
  .map(token => token.value)
60
57
  .join(" ")
@@ -36,10 +36,7 @@ export default {
36
36
  * @returns {boolean}
37
37
  */
38
38
  function is_arguments(position) {
39
- const node = /** @type {import("../../private.js").AstNode | null} */(/** @type {unknown} */(source_code.getNodeByRangeIndex(position)))/**/
40
- if (!node) {
41
- return false
42
- }
39
+ const node = /** @type {import("../../private.js").AstNode} */(/** @type {unknown} */(source_code.getNodeByRangeIndex(position)))/**/
43
40
  const type = node.type
44
41
  if (type != "CallExpression" && type != "ImportExpression" && type != "NewExpression") {
45
42
  return false
@@ -73,7 +70,7 @@ export default {
73
70
  * @param {number} start
74
71
  * @param {number} end
75
72
  * @param {[number, number][]} removed
76
- * @returns {string | null}
73
+ * @returns {string}
77
74
  */
78
75
  function render_without(start, end, removed) {
79
76
  let position = start
@@ -82,9 +79,6 @@ export default {
82
79
  if (removed_end <= start || removed_start >= end) {
83
80
  continue
84
81
  }
85
- if (source.crosses_edit(removed_start) || source.crosses_edit(removed_end)) {
86
- return null
87
- }
88
82
  let chunk_end = removed_start
89
83
  if (text[removed_start] == ")") {
90
84
  while (chunk_end > position && is_space(text.charCodeAt(chunk_end - 1))) {
@@ -239,12 +233,10 @@ export default {
239
233
  const prefix = render_without(left, content_start, removed)
240
234
  const suffix = render_without(content_end, right, removed)
241
235
  if (
242
- prefix == null
243
- || suffix == null
244
- || line_break_regex.test(prefix)
245
- && restricted_regex.test(
246
- text.slice(Math.max(0, left - 7), left)
247
- )
236
+ line_break_regex.test(prefix)
237
+ && restricted_regex.test(
238
+ text.slice(Math.max(0, left - 7), left)
239
+ )
248
240
  ) {
249
241
  return
250
242
  }
@@ -73,12 +73,8 @@ export default {
73
73
  if (left <= start || text[left - 1] != "(") {
74
74
  break
75
75
  }
76
- const right = source.skip_right(code_end, end)
77
- if (right >= end || text[right] != ")") {
78
- break
79
- }
80
76
  code_start = left - 1
81
- code_end = right + 1
77
+ code_end = source.skip_right(code_end, end) + 1
82
78
  }
83
79
  const right = source.skip_right(code_end, end)
84
80
  let last = right
@@ -197,10 +193,10 @@ export default {
197
193
  node.properties,
198
194
  object_curly_spacing
199
195
  )
200
- case "SequenceExpression":
196
+ default:
201
197
  return {
202
198
  end: node.range[1],
203
- items: node.expressions,
199
+ items: /** @type {import("../../private.js").AstNode & import("estree").SequenceExpression} */(node)/**/.expressions,
204
200
  line_indent: source.get_line_indent(
205
201
  node,
206
202
  source.get_anchor(node.range[0]),
@@ -210,7 +206,6 @@ export default {
210
206
  start: node.range[0]
211
207
  }
212
208
  }
213
- return undefined
214
209
  }
215
210
  /**
216
211
  * @param {import("../../private.js").AstNode} node
@@ -23,26 +23,18 @@ export default {
23
23
  */
24
24
  function collect(node, depth, gaps) {
25
25
  const parts = get_parts(node)
26
- const question = source_code.getTokenAfter(
26
+ const question = /** @type {import("eslint").AST.Token} */(source_code.getTokenAfter(
27
27
  /** @type {import("estree").Node} */(/** @type {unknown} */(parts.test))/**/,
28
28
  {
29
29
  filter: token => token.value == "?"
30
30
  }
31
- )
32
- const colon = question && source_code.getTokenAfter(
31
+ ))/**/
32
+ const colon = /** @type {import("eslint").AST.Token} */(source_code.getTokenAfter(
33
33
  /** @type {import("estree").Node} */(/** @type {unknown} */(parts.consequent))/**/,
34
34
  {
35
35
  filter: token => token.value == ":"
36
36
  }
37
- )
38
- if (
39
- !question
40
- || !colon
41
- || question.range[1] > parts.consequent.range[0]
42
- || colon.range[1] > parts.alternate.range[0]
43
- ) {
44
- return false
45
- }
37
+ ))/**/
46
38
  for (const operator of [ question, colon ]) {
47
39
  const before = /** @type {import("eslint").AST.Token | import("estree").Comment} */(source_code.getTokenBefore(
48
40
  operator,
@@ -160,16 +160,14 @@ export default {
160
160
  function declaration_edits(node, resolution) {
161
161
  /** @type {import("../../private.js").RenameEdit[]} */
162
162
  const edits = [ rename_edit(node) ]
163
- const block = /** @type {import("../../private.js").AstNode | undefined} */(resolution.get(node)?.scope.block)/**/
163
+ const block = /** @type {import("../../private.js").AstNode} */(/** @type {import("eslint").Scope.Variable} */(resolution.get(node))/**/.scope.block)/**/
164
164
  const func = function_of_param(node)
165
165
  const leading = func && leading_comment(func)
166
166
  /** @type {Set<import("../../private.js").Comment>} */
167
167
  const comments = new Set(
168
- block
169
- ? /** @type {import("../../private.js").Comment[]} */(source.getAllComments())/**/.filter(
170
- comment => block.type == "Program" || comment.range[0] >= block.range[0] && comment.range[1] <= block.range[1]
171
- )
172
- : []
168
+ /** @type {import("../../private.js").Comment[]} */(source.getAllComments())/**/.filter(
169
+ comment => block.type == "Program" || comment.range[0] >= block.range[0] && comment.range[1] <= block.range[1]
170
+ )
173
171
  )
174
172
  if (leading) {
175
173
  comments.add(leading)
@@ -241,7 +239,7 @@ export default {
241
239
  * @returns {boolean}
242
240
  */
243
241
  function is_declaration(node) {
244
- for (let parent = node.parent; parent; parent = parent.parent) {
242
+ for (let parent = node.parent; ; parent = parent.parent) {
245
243
  switch (parent.type) {
246
244
  case "ArrayPattern":
247
245
  case "AssignmentPattern":
@@ -257,7 +255,6 @@ export default {
257
255
  return true
258
256
  }
259
257
  }
260
- return true
261
258
  }
262
259
  /**
263
260
  * @param {import("../../private.js").AstNode} node
@@ -308,9 +305,7 @@ export default {
308
305
  anchor = parent
309
306
  break
310
307
  case "VariableDeclarator":
311
- if (parent.init == func) {
312
- anchor = parent.parent
313
- }
308
+ anchor = parent.parent
314
309
  }
315
310
  if (anchor.parent.type == "ExportDefaultDeclaration" || anchor.parent.type == "ExportNamedDeclaration") {
316
311
  anchor = anchor.parent
@@ -771,7 +766,7 @@ export default {
771
766
  const resolution = new Map()
772
767
  /** @type {Map<string, import("eslint").Scope.Variable[]>} */
773
768
  const variables_by_name = new Map()
774
- const scopes = source.scopeManager?.scopes ?? []
769
+ const scopes = /** @type {import("eslint").Scope.ScopeManager} */(source.scopeManager)/**/.scopes
775
770
  for (const scope of scopes) {
776
771
  for (const variable of scope.variables) {
777
772
  const same_names = variables_by_name.get(variable.name)
@@ -808,10 +803,9 @@ export default {
808
803
  for (const nodes of declarations.values()) {
809
804
  for (const node of nodes) {
810
805
  declared.add(node)
811
- const variable = resolution.get(node)
812
- if (variable) {
813
- renamable.add(variable)
814
- }
806
+ renamable.add(
807
+ /** @type {import("eslint").Scope.Variable} */(resolution.get(node))/**/
808
+ )
815
809
  }
816
810
  }
817
811
  /** @type {Map<string, Set<import("eslint").Scope.Scope>>} */
@@ -859,7 +853,7 @@ export default {
859
853
  if (!others.length) {
860
854
  return false
861
855
  }
862
- for (const variable of variables_by_name.get(name) ?? []) {
856
+ for (const variable of /** @type {import("eslint").Scope.Variable[]} */(variables_by_name.get(name))/**/) {
863
857
  if (!renamable.has(variable)) {
864
858
  continue
865
859
  }
@@ -895,7 +889,7 @@ export default {
895
889
  * @returns {boolean}
896
890
  */
897
891
  function has_foreign_declaration(name) {
898
- return (variables_by_name.get(name) ?? []).some(
892
+ return /** @type {import("eslint").Scope.Variable[]} */(variables_by_name.get(name))/**/.some(
899
893
  variable => renamable.has(variable) && variable.identifiers.some(
900
894
  identifier => !declared.has(
901
895
  /** @type {import("../../private.js").AstNode} */(/** @type {unknown} */(identifier))/**/
@@ -13,14 +13,12 @@ function skip_hole_commas(rule) {
13
13
  {
14
14
  report: {
15
15
  value: (
16
- /** @type {import("eslint").Rule.ReportDescriptor & { node?: import("eslint").AST.Token }} */ descriptor
16
+ /** @type {import("eslint").Rule.ReportDescriptor & { node: import("eslint").AST.Token }} */ descriptor
17
17
  ) => {
18
18
  const token = descriptor.node
19
- if (token?.type == "Punctuator" && token.value == ",") {
20
- const before = source_code.getTokenBefore(token)
21
- if (before?.value == "," && before.loc.end.line < token.loc.start.line) {
22
- return
23
- }
19
+ const before = source_code.getTokenBefore(token)
20
+ if (before?.value == "," && before.loc.end.line < token.loc.start.line) {
21
+ return
24
22
  }
25
23
  context.report(descriptor)
26
24
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.js"],"names":[],"mappings":"AAyNA;;;;;;;;;;;;;GAaG;AACH,QAAA,MAAM,MAAM,EAbF,OAAO,QAAQ,EAAE,MAAM,CAAC,MAAM,GAAG;IACtC,OAAO,EAAE;QACP,WAAW,EAAE,OAAO,QAAQ,EAAE,MAAM,CAAC,MAAM,GAAG;YAC5C,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,QAAQ,EAAE,MAAM,CAAC,MAAM,CAAC,CAAA;YACvD,KAAK,EAAE,OAAO,QAAQ,EAAE,MAAM,CAAC,WAAW,CAAA;SAC3C,CAAA;QACD,MAAM,EAAE,OAAO,QAAQ,EAAE,MAAM,CAAC,MAAM,GAAG;YACvC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,QAAQ,EAAE,MAAM,CAAC,MAAM,CAAC,CAAA;YACvD,KAAK,EAAE,OAAO,QAAQ,EAAE,MAAM,CAAC,WAAW,CAAA;SAC3C,CAAA;KACF,CAAA;CA2BL,CAAA;eAmDc,MAAM;AACrB,OAAO,EAAE,MAAM,IAAI,gBAAgB,EAAE,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.js"],"names":[],"mappings":"AA2NA;;;;;;;;;;;;;GAaG;AACH,QAAA,MAAM,MAAM,EAbF,OAAO,QAAQ,EAAE,MAAM,CAAC,MAAM,GAAG;IACtC,OAAO,EAAE;QACP,WAAW,EAAE,OAAO,QAAQ,EAAE,MAAM,CAAC,MAAM,GAAG;YAC5C,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,QAAQ,EAAE,MAAM,CAAC,MAAM,CAAC,CAAA;YACvD,KAAK,EAAE,OAAO,QAAQ,EAAE,MAAM,CAAC,WAAW,CAAA;SAC3C,CAAA;QACD,MAAM,EAAE,OAAO,QAAQ,EAAE,MAAM,CAAC,MAAM,GAAG;YACvC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,QAAQ,EAAE,MAAM,CAAC,MAAM,CAAC,CAAA;YACvD,KAAK,EAAE,OAAO,QAAQ,EAAE,MAAM,CAAC,WAAW,CAAA;SAC3C,CAAA;KACF,CAAA;CA4BL,CAAA;eAmDc,MAAM;AACrB,OAAO,EAAE,MAAM,IAAI,gBAAgB,EAAE,CAAA"}