eslint-plugin-lube 0.2.4 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -4
- package/docs/pretty-imports.md +73 -0
- package/docs/pretty-jsdoc-casting.md +24 -0
- package/docs/svelte-naming-convention.md +48 -0
- package/index.js +5 -3
- package/package.json +26 -26
- package/private.d.ts +43 -22
- package/rules/pretty-imports.js +41 -29
- package/rules/pretty-jsdoc-casting.js +215 -0
- package/rules/svelte-naming-convention.js +169 -205
- package/test/pretty-imports/{check_exports.js → ExportNamedDeclaration.js} +4 -4
- package/test/pretty-imports/{check_imports.js → ImportDeclaration.js} +4 -4
- package/test/pretty-imports.js +2 -2
- package/test/pretty-jsdoc-casting/case_by_expression.js +240 -0
- package/test/pretty-jsdoc-casting/nested_casting.js +96 -0
- package/test/pretty-jsdoc-casting.js +19 -0
- package/test/svelte-naming-convention/basic_snake_case.js +0 -7
- package/test/svelte-naming-convention/case_by_identifier.js +0 -137
- package/test/svelte-naming-convention/snake_case_with_symbols.js +0 -8
- package/node-type.text +0 -71
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
"use strict"
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @param {number} length
|
|
5
|
+
* @returns {number[]}
|
|
6
|
+
*/
|
|
7
|
+
function get_indexed_array(length) {
|
|
8
|
+
const array = []
|
|
9
|
+
while (length) array[--length] = length
|
|
10
|
+
return array
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/** @type {import("eslint").Rule.RuleModule} */
|
|
14
|
+
module.exports = {
|
|
15
|
+
meta: {
|
|
16
|
+
docs: {
|
|
17
|
+
description: "Enforces additional empty comment when type casting in JSDoc.",
|
|
18
|
+
recommended: true,
|
|
19
|
+
url: "https://github.com/Artxe2/lube-series/blob/master/packages/eslint-plugin-lube/docs/pretty-jsdoc-casting.md"
|
|
20
|
+
},
|
|
21
|
+
fixable: "code",
|
|
22
|
+
messages: {
|
|
23
|
+
not_match: "This casting is not pretty"
|
|
24
|
+
},
|
|
25
|
+
type: "layout"
|
|
26
|
+
},
|
|
27
|
+
create(context) {
|
|
28
|
+
const source_code = context.sourceCode
|
|
29
|
+
const origin_text = source_code.text
|
|
30
|
+
let fixed_text = origin_text
|
|
31
|
+
const text_length = fixed_text.length + 1
|
|
32
|
+
const text_indexes = get_indexed_array(text_length)
|
|
33
|
+
const left_space_regex = /\s+$/
|
|
34
|
+
const right_space_regex = /^\s+/
|
|
35
|
+
const type_regex = /(?<=(?:^|.*\s*)@type\s*\{).+?(?=\}.*?$)/
|
|
36
|
+
/** @type {import("../private").Comment[]} */
|
|
37
|
+
const comments = []
|
|
38
|
+
for (const comment of /** @type {import("../private").Comment[]} */(source_code.getAllComments().reverse())/**/) {
|
|
39
|
+
const [start, end] = comment.range
|
|
40
|
+
comments[start] = comment
|
|
41
|
+
comments[end - 1] = comment
|
|
42
|
+
}
|
|
43
|
+
/** @type {import("../private").AstNode[] & import("estree").Expression[]} */
|
|
44
|
+
const expressions = []
|
|
45
|
+
/** @type {Set<string>} */
|
|
46
|
+
const node_ranges = new Set
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* @param {import("../private").AstNode} node
|
|
50
|
+
* @returns {void}
|
|
51
|
+
*/
|
|
52
|
+
function verify_correct(node) {
|
|
53
|
+
let temp_text = fixed_text
|
|
54
|
+
const temp_indexes = [...text_indexes]
|
|
55
|
+
/** @type {(string | undefined)[]} */
|
|
56
|
+
const types = []
|
|
57
|
+
/**
|
|
58
|
+
* @param {number} start
|
|
59
|
+
* @param {number} end
|
|
60
|
+
* @param {string=} insert
|
|
61
|
+
* @returns {void}
|
|
62
|
+
*/
|
|
63
|
+
function temp_text_changes(start, end, insert = "") {
|
|
64
|
+
const gap = insert.length + temp_indexes[start] - temp_indexes[end]
|
|
65
|
+
temp_text = temp_text.slice(0, temp_indexes[start]) + insert + temp_text.slice(temp_indexes[end])
|
|
66
|
+
if (gap) {
|
|
67
|
+
while (end < text_length) {
|
|
68
|
+
temp_indexes[end++] += gap
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
let open = 0
|
|
73
|
+
let close = 0
|
|
74
|
+
let prev_open = 0
|
|
75
|
+
let [left, right] = node.range
|
|
76
|
+
W: while (left > 0) {
|
|
77
|
+
if (comments[left - 1]) {
|
|
78
|
+
const comment = comments[left - 1]
|
|
79
|
+
left = comment.range[0]
|
|
80
|
+
if (open && !types[open - 1]) {
|
|
81
|
+
const type = comment.type == "Block" && type_regex.exec(comment.value)?.[0]
|
|
82
|
+
if (type) {
|
|
83
|
+
types[open - 1] = type
|
|
84
|
+
temp_text_changes(...comment.range)
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
} else {
|
|
88
|
+
const space = left_space_regex.exec(origin_text.slice(0, left))?.[0].length
|
|
89
|
+
if (space) {
|
|
90
|
+
left -= space
|
|
91
|
+
} else if (origin_text[left - 1] == "(") {
|
|
92
|
+
while (right < text_length) {
|
|
93
|
+
if (comments[right]) {
|
|
94
|
+
const comment = comments[right]
|
|
95
|
+
right = comment.range[1]
|
|
96
|
+
} else {
|
|
97
|
+
const space = right_space_regex.exec(origin_text.slice(right))?.[0].length
|
|
98
|
+
if (space) {
|
|
99
|
+
right += space
|
|
100
|
+
} else {
|
|
101
|
+
if (origin_text[right] == ")") {
|
|
102
|
+
prev_open = left--
|
|
103
|
+
temp_text_changes(left, left + 1)
|
|
104
|
+
close++
|
|
105
|
+
if (temp_text.slice(temp_indexes[right + 1], temp_indexes[right + 5]) == "/**/") {
|
|
106
|
+
temp_text_changes(right, right += 5)
|
|
107
|
+
} else {
|
|
108
|
+
temp_text_changes(right, ++right)
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
break
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
if (open == close) break W
|
|
116
|
+
open++
|
|
117
|
+
} else {
|
|
118
|
+
break
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
if (close && !types[close - 1]) {
|
|
123
|
+
close--
|
|
124
|
+
temp_text_changes(prev_open, prev_open, "(")
|
|
125
|
+
temp_text_changes(right, right, ")")
|
|
126
|
+
}
|
|
127
|
+
const start = temp_indexes[node.range[0]]
|
|
128
|
+
let end = temp_indexes[node.range[1]]
|
|
129
|
+
if (temp_text.slice(end, end + 4) == "/**/") {
|
|
130
|
+
end += 4
|
|
131
|
+
}
|
|
132
|
+
let corrected_text = temp_text.slice(start, end)
|
|
133
|
+
for (let i = 0; i < close; i++) {
|
|
134
|
+
corrected_text = types[i]
|
|
135
|
+
? `/** @type {${types[i]}} */(${corrected_text})/**/`
|
|
136
|
+
: `(${corrected_text})`
|
|
137
|
+
}
|
|
138
|
+
corrected_text = temp_text.slice(temp_indexes[left], start)
|
|
139
|
+
+ corrected_text
|
|
140
|
+
+ temp_text.slice(end, temp_indexes[right])
|
|
141
|
+
if (fixed_text.slice(text_indexes[left], text_indexes[right]) != corrected_text) {
|
|
142
|
+
report(node, left, right, corrected_text)
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* @param {import("../private").AstNode} node
|
|
147
|
+
* @param {number} start
|
|
148
|
+
* @param {number} end
|
|
149
|
+
* @param {string} corrected_text
|
|
150
|
+
* @returns {void}
|
|
151
|
+
*/
|
|
152
|
+
function report(node, start, end, corrected_text) {
|
|
153
|
+
fixed_text = fixed_text.slice(0, text_indexes[start]) + corrected_text + fixed_text.slice(text_indexes[end])
|
|
154
|
+
const gap = corrected_text.length + text_indexes[start] - text_indexes[end]
|
|
155
|
+
if (gap) {
|
|
156
|
+
for (let i = end; i < text_length; i++) {
|
|
157
|
+
text_indexes[i] += gap
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
context.report({
|
|
161
|
+
fix(fixer) {
|
|
162
|
+
return fixer.replaceTextRange([start, end], corrected_text)
|
|
163
|
+
},
|
|
164
|
+
loc: {
|
|
165
|
+
start: source_code.getLocFromIndex(start),
|
|
166
|
+
end: source_code.getLocFromIndex(end)
|
|
167
|
+
},
|
|
168
|
+
messageId: "not_match",
|
|
169
|
+
node
|
|
170
|
+
})
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* @param {import("../private").AstNode} node
|
|
174
|
+
* @returns {void}
|
|
175
|
+
*/
|
|
176
|
+
function push_to_reverse(node) {
|
|
177
|
+
const range = `${node.range[0]} ${node.range[1]}`
|
|
178
|
+
if (node_ranges.has(range)) return
|
|
179
|
+
node_ranges.add(range)
|
|
180
|
+
expressions.push(node)
|
|
181
|
+
}
|
|
182
|
+
return {
|
|
183
|
+
"Program:exit": () => {
|
|
184
|
+
for (const node of expressions.reverse()) {
|
|
185
|
+
verify_correct(node)
|
|
186
|
+
}
|
|
187
|
+
},
|
|
188
|
+
ArrayExpression: push_to_reverse,
|
|
189
|
+
ArrowFunctionExpression: push_to_reverse,
|
|
190
|
+
AssignmentExpression: push_to_reverse,
|
|
191
|
+
AwaitExpression: push_to_reverse,
|
|
192
|
+
BinaryExpression: push_to_reverse,
|
|
193
|
+
CallExpression: push_to_reverse,
|
|
194
|
+
ChainExpression: push_to_reverse,
|
|
195
|
+
ClassExpression: push_to_reverse,
|
|
196
|
+
ConditionalExpression: push_to_reverse,
|
|
197
|
+
FunctionExpression: push_to_reverse,
|
|
198
|
+
Identifier: push_to_reverse,
|
|
199
|
+
ImportExpression: push_to_reverse,
|
|
200
|
+
Literal: push_to_reverse,
|
|
201
|
+
LogicalExpression: push_to_reverse,
|
|
202
|
+
MemberExpression: push_to_reverse,
|
|
203
|
+
MetaProperty: push_to_reverse,
|
|
204
|
+
NewExpression: push_to_reverse,
|
|
205
|
+
ObjectExpression: push_to_reverse,
|
|
206
|
+
SequenceExpression: push_to_reverse,
|
|
207
|
+
TaggedTemplateExpression: push_to_reverse,
|
|
208
|
+
TemplateLiteral: push_to_reverse,
|
|
209
|
+
ThisExpression: push_to_reverse,
|
|
210
|
+
UnaryExpression: push_to_reverse,
|
|
211
|
+
UpdateExpression: push_to_reverse,
|
|
212
|
+
YieldExpression: push_to_reverse
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|