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 +16 -0
- package/README.md +2 -0
- package/docs/prefer-param-tag.md +25 -0
- package/package.json +3 -3
- package/src/index.js +3 -0
- package/src/rules/ascii-order.js +2 -7
- package/src/rules/prefer-param-tag.js +252 -0
- package/src/rules/pretty-imports.js +0 -3
- package/src/rules/pretty-jsdoc-casting.js +6 -14
- package/src/rules/pretty-sequence.js +3 -8
- package/src/rules/pretty-ternary.js +4 -12
- package/src/rules/svelte-naming-convention.js +12 -18
- package/src/utils/skip_hole_commas.js +4 -6
- package/types/index.d.ts.map +1 -1
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.
|
|
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.
|
|
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,
|
package/src/rules/ascii-order.js
CHANGED
|
@@ -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").
|
|
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
|
|
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
|
+
}
|
|
@@ -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
|
|
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
|
|
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
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
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 =
|
|
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
|
-
|
|
196
|
+
default:
|
|
201
197
|
return {
|
|
202
198
|
end: node.range[1],
|
|
203
|
-
items: node
|
|
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 =
|
|
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
|
|
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
|
-
|
|
169
|
-
|
|
170
|
-
|
|
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;
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
812
|
-
|
|
813
|
-
|
|
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)
|
|
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
|
|
16
|
+
/** @type {import("eslint").Rule.ReportDescriptor & { node: import("eslint").AST.Token }} */ descriptor
|
|
17
17
|
) => {
|
|
18
18
|
const token = descriptor.node
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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
|
}
|
package/types/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.js"],"names":[],"mappings":"
|
|
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"}
|