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.
@@ -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
+ }