eslint-plugin-lube 0.3.5 → 0.4.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.
@@ -12,47 +12,34 @@ module.exports = {
12
12
  exports: "This exports is not pretty",
13
13
  imports: "This imports is not pretty"
14
14
  },
15
- schema: [
15
+ schema: [
16
16
  {
17
17
  properties: {
18
- checkExports: {
19
- type: "boolean",
20
- default: true
21
- },
22
- checkImports: {
23
- type: "boolean",
24
- default: true
25
- },
26
- indent: {
27
- type: "string",
28
- default: "\t"
29
- },
30
- maxLength: {
31
- type: "number",
32
- default: 30
33
- },
34
- semicolon: {
35
- type: "boolean",
36
- default: false
37
- }
18
+ checkExports: { type: "boolean", default: true },
19
+ checkImports: { type: "boolean", default: true },
20
+ indent: { type: "string", default: "\t" },
21
+ maxLength: { type: "number", default: 30 },
22
+ semicolon: { type: "boolean", default: false }
38
23
  },
39
24
  type: "object"
40
- }
25
+ }
41
26
  ],
42
27
  type: "layout"
43
28
  },
44
29
  create(context) {
45
- const check_exports = /** @type {boolean} */(context.options[0]?.checkExports)/**/ ?? true
46
- const check_imports = /** @type {boolean} */(context.options[0]?.checkImports)/**/ ?? true
47
- const indent = /** @type {string} */(context.options[0]?.indent)/**/ ?? "\t"
48
- const max_length = /** @type {number} */(context.options[0]?.maxLength)/**/ ?? 30
49
- const semicolon = /** @type {number} */(context.options[0]?.semicolon)/**/ ?? false
30
+ /** @type {import("../private").RuleOptions["pretty-imports"]} */
31
+ const option = context.options[0]
32
+ const check_exports = option?.checkExports ?? true
33
+ const check_imports = option?.checkImports ?? true
34
+ const indent = option?.indent ?? "\t"
35
+ const max_length = option?.maxLength ?? 30
36
+ const semicolon = option?.semicolon ?? false
50
37
 
51
38
  const source_code = context.sourceCode
52
39
  const text = source_code.text
53
-
54
40
  const indent_regex = /[ \t]*(?=[^\n]*$)/
55
41
  const _r_regex = /\r/g
42
+
56
43
  /**
57
44
  * @param {number} index
58
45
  * @returns {string}
@@ -60,7 +47,6 @@ module.exports = {
60
47
  function get_indent(index) {
61
48
  return text.slice(0, index).match(indent_regex)?.[0] || ""
62
49
  }
63
-
64
50
  /**
65
51
  * @overload
66
52
  * @param {number} start
@@ -78,10 +64,12 @@ module.exports = {
78
64
  function get_text(node_or_start, end) {
79
65
  const substr = typeof node_or_start == "number"
80
66
  ? text.slice(node_or_start, end)
81
- : text.slice(node_or_start.range[0], node_or_start.range[1])
67
+ : text.slice(
68
+ node_or_start.range[0],
69
+ node_or_start.range[1]
70
+ )
82
71
  return substr.replace(_r_regex, "")
83
72
  }
84
-
85
73
  /**
86
74
  * @param {import("../private").AstNode} node
87
75
  * @param {string} corrected_text
@@ -89,13 +77,15 @@ module.exports = {
89
77
  * @returns {void}
90
78
  */
91
79
  function report(node, corrected_text, message_id) {
92
- context.report({
93
- fix(fixer) {
94
- return fixer.replaceText(node, corrected_text)
95
- },
96
- messageId: message_id,
97
- node: /** @type {import("estree").Node} */(node)/**/
98
- })
80
+ context.report(
81
+ {
82
+ fix(fixer) {
83
+ return fixer.replaceText(node, corrected_text)
84
+ },
85
+ messageId: message_id,
86
+ node: /** @type {import("estree").Node} */(node)/**/
87
+ }
88
+ )
99
89
  }
100
90
 
101
91
  return {
@@ -112,12 +102,10 @@ module.exports = {
112
102
  }
113
103
  let corrected_text = length > max_length
114
104
  ? `export {\n${line_indent + indent}${
115
-
116
105
  /** @type {import("../private").AstNode[]} */(node.specifiers)/**/.map(get_text)
117
106
  .join(",\n" + line_indent + indent)
118
107
  }\n${line_indent}}`
119
108
  : `export { ${
120
-
121
109
  /** @type {import("../private").AstNode[]} */(node.specifiers)/**/.map(get_text).join(", ")
122
110
  } }`
123
111
  if (node.source) {
@@ -142,12 +130,10 @@ module.exports = {
142
130
  }
143
131
  let corrected_text = length > max_length
144
132
  ? `import {\n${line_indent + indent}${
145
-
146
133
  /** @type {import("../private").AstNode[]} */(node.specifiers)/**/.map(get_text)
147
134
  .join(",\n" + line_indent + indent)
148
135
  }\n${line_indent}} from ${node.source.raw}`
149
136
  : `import { ${
150
-
151
137
  /** @type {import("../private").AstNode[]} */(node.specifiers)/**/.map(get_text).join(", ")
152
138
  } } from ${node.source.raw}`
153
139
  if (semicolon) {
@@ -1,5 +1,9 @@
1
1
  "use strict"
2
2
 
3
+ const left_space_regex = /\s+$/
4
+ const right_space_regex = /^\s+/
5
+ const type_regex = /(?<=(?:^|.*\s*)@type\s*\{).+(?=\}.*?$)/
6
+
3
7
  /**
4
8
  * @param {number} length
5
9
  * @returns {number[]}
@@ -30,15 +34,11 @@ module.exports = {
30
34
  let fixed_text = origin_text
31
35
  const text_length = fixed_text.length + 1
32
36
  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
37
  /** @type {import("../private").Comment[]} */
37
38
  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
39
+ for (const comment of /** @type {import("../private").Comment[]} */(source_code.getAllComments())/**/) {
40
+ let [ start, end ] = comment.range
41
+ while (start < end) comments[start++] = comment
42
42
  }
43
43
  /** @type {import("../private").AstNode[] & import("estree").Expression[]} */
44
44
  const expressions = []
@@ -51,7 +51,7 @@ module.exports = {
51
51
  */
52
52
  function verify_correct(node) {
53
53
  let temp_text = fixed_text
54
- const temp_indexes = [...text_indexes]
54
+ const temp_indexes = [ ...text_indexes ]
55
55
  /** @type {(string | undefined)[]} */
56
56
  const types = []
57
57
  /** @type {true[]} */
@@ -75,7 +75,7 @@ module.exports = {
75
75
  let close = 0
76
76
  let prev_open = 0
77
77
  let prev_close = 0
78
- let [left, right] = node.range
78
+ let [ left, right ] = node.range
79
79
  W: while (left > 0) {
80
80
  if (comments[left - 1]) {
81
81
  const comment = comments[left - 1]
@@ -106,7 +106,10 @@ module.exports = {
106
106
  prev_close = right
107
107
  temp_text_changes(left, left + 1)
108
108
  close++
109
- if (temp_text.slice(temp_indexes[right + 1], temp_indexes[right + 5]) == "/**/") {
109
+ if (temp_text.slice(
110
+ temp_indexes[right + 1],
111
+ temp_indexes[right + 5]
112
+ ) == "/**/") {
110
113
  extras[right] = true
111
114
  temp_text_changes(right, right += 5)
112
115
  } else {
@@ -143,7 +146,10 @@ module.exports = {
143
146
  corrected_text = temp_text.slice(temp_indexes[left], start)
144
147
  + corrected_text
145
148
  + temp_text.slice(end, temp_indexes[right])
146
- if (fixed_text.slice(text_indexes[left], text_indexes[right]) != corrected_text) {
149
+ if (fixed_text.slice(
150
+ text_indexes[left],
151
+ text_indexes[right]
152
+ ) != corrected_text) {
147
153
  report(node, left, right, corrected_text)
148
154
  }
149
155
  }
@@ -162,17 +168,19 @@ module.exports = {
162
168
  text_indexes[i] += gap
163
169
  }
164
170
  }
165
- context.report({
166
- fix(fixer) {
167
- return fixer.replaceTextRange([start, end], corrected_text)
168
- },
169
- loc: {
170
- start: source_code.getLocFromIndex(start),
171
- end: source_code.getLocFromIndex(end)
172
- },
173
- messageId: "not_match",
174
- node
175
- })
171
+ context.report(
172
+ {
173
+ fix(fixer) {
174
+ return fixer.replaceTextRange([ start, end ], corrected_text)
175
+ },
176
+ loc: {
177
+ start: source_code.getLocFromIndex(start),
178
+ end: source_code.getLocFromIndex(end)
179
+ },
180
+ messageId: "not_match",
181
+ node
182
+ }
183
+ )
176
184
  }
177
185
  /**
178
186
  * @param {import("../private").AstNode} node
@@ -0,0 +1,417 @@
1
+ "use strict"
2
+
3
+ const indent_regex = /[ \t]*(?=[^\n]*$)/
4
+ const left_space_regex = /\s+$/
5
+ const right_space_regex = /^\s+/
6
+ const __n_regex = /(?<=\n)/g
7
+ const _r_regex = /\r/g
8
+
9
+ /**
10
+ * @param {number} length
11
+ * @returns {number[]}
12
+ */
13
+ function get_indexed_array(length) {
14
+ const array = []
15
+ while (length) array[--length] = length
16
+ return array
17
+ }
18
+
19
+ /** @type {import("eslint").Rule.RuleModule} */
20
+ module.exports = {
21
+ meta: {
22
+ docs: {
23
+ description: "Enforces additional empty comment when type casting in JSDoc.",
24
+ recommended: true,
25
+ url: "https://github.com/Artxe2/lube-series/blob/master/packages/eslint-plugin-lube/docs/pretty-sequence.md"
26
+ },
27
+ fixable: "code",
28
+ messages: {
29
+ not_match: "This sequence is not pretty"
30
+ },
31
+ schema: [
32
+ {
33
+ properties: {
34
+ arrayBracketSpacing: { type: "boolean", default: true },
35
+ checkArray: { type: "boolean", default: true },
36
+ checkCall: { type: "boolean", default: true },
37
+ checkObject: { type: "boolean", default: true },
38
+ checkSequence: { type: "boolean", default: true },
39
+ funcCallSpacing: { type: "boolean", default: false },
40
+ indent: { type: "string", default: "\t" },
41
+ maxLength: { type: "number", default: 30 },
42
+ objectCurlySpacing: { type: "boolean", default: true }
43
+ },
44
+ type: "object"
45
+ }
46
+ ],
47
+ type: "layout"
48
+ },
49
+ create(context) {
50
+ /** @type {import("../private").RuleOptions["pretty-sequence"]} */
51
+ const option = context.options[0]
52
+ const array_bracket_spacing = option?.arrayBracketSpacing ?? true
53
+ const check_array = option?.checkArray ?? true
54
+ const check_call = option?.checkCall ?? true
55
+ const check_object = option?.checkObject ?? true
56
+ const check_sequence = option?.checkSequence ?? true
57
+ const func_call_spacing = option?.funcCallSpacing ?? false
58
+ const indent = option?.indent ?? "\t"
59
+ const max_length = option?.maxLength ?? 30
60
+ const object_curly_spacing = option?.objectCurlySpacing ?? true
61
+
62
+ const source_code = context.sourceCode
63
+ const origin_text = source_code.text
64
+ let fixed_text = origin_text
65
+ const text_length = fixed_text.length + 1
66
+ const text_indexes = get_indexed_array(text_length)
67
+ /** @type {import("../private").Comment[]} */
68
+ const comments = []
69
+ for (const comment of /** @type {import("../private").Comment[]} */(source_code.getAllComments())/**/) {
70
+ let [ start, end ] = comment.range
71
+ while (start < end) comments[start++] = comment
72
+ }
73
+ /** @type {import("../private").AstNode[] & import("estree").Expression[]} */
74
+ const nodes = []
75
+
76
+ /**
77
+ * @param {import("../private").AstNode} node
78
+ * @param {number} start
79
+ * @param {number} end
80
+ * @param {boolean=} add_indentation
81
+ * @returns {string}
82
+ */
83
+ function get_text(node, start, end, add_indentation) {
84
+ let open_paren = false
85
+ let [ left, right ] = node.range
86
+ W: while (left > start) {
87
+ if (comments[left - 1]) {
88
+ left = comments[left - 1].range[0]
89
+ } else {
90
+ const left_space = left_space_regex.exec(origin_text.slice(0, left))?.[0].length
91
+ if (left_space) {
92
+ left -= left_space
93
+ } else if (origin_text[left - 1] == "(") {
94
+ open_paren = true
95
+ while (right < end) {
96
+ if (comments[right]) {
97
+ const comment = comments[right]
98
+ right = comment.range[1]
99
+ } else {
100
+ const right_space = right_space_regex.exec(origin_text.slice(right))?.[0].length
101
+ if (right_space) {
102
+ right += right_space
103
+ } else {
104
+ if (origin_text[right] == ")") {
105
+ left--
106
+ right++
107
+ open_paren = false
108
+ }
109
+ break
110
+ }
111
+ }
112
+ }
113
+ if (open_paren) break W
114
+ } else {
115
+ break
116
+ }
117
+ }
118
+ }
119
+ while (right < end) {
120
+ if (comments[right]) {
121
+ const comment = comments[right]
122
+ right = comment.range[1]
123
+ } else {
124
+ const right_space = right_space_regex.exec(origin_text.slice(right))?.[0].length
125
+ if (right_space) {
126
+ right += right_space
127
+ } else {
128
+ break
129
+ }
130
+ }
131
+ }
132
+ const substr = fixed_text.slice(
133
+ text_indexes[left],
134
+ text_indexes[right]
135
+ )
136
+ .replace(_r_regex, "")
137
+ .trim()
138
+ return add_indentation
139
+ ? substr.replace(__n_regex, indent)
140
+ : substr
141
+ }
142
+ /**
143
+ * @param {import("../private").AstNode} node
144
+ * @returns {void}
145
+ */
146
+ function push_to_reverse(node) {
147
+ nodes.push(node)
148
+ }
149
+ /**
150
+ * @param {import("../private").AstNode} node
151
+ * @param {number} start
152
+ * @param {number} end
153
+ * @param {string} corrected_text
154
+ * @returns {void}
155
+ */
156
+ function report(node, start, end, corrected_text) {
157
+ fixed_text = fixed_text.slice(0, text_indexes[start]) + corrected_text + fixed_text.slice(text_indexes[end])
158
+ const gap = corrected_text.length + text_indexes[start] - text_indexes[end]
159
+ if (gap) {
160
+ for (let i = end; i < text_length; i++) {
161
+ text_indexes[i] += gap
162
+ }
163
+ }
164
+ context.report(
165
+ {
166
+ fix(fixer) {
167
+ return fixer.replaceTextRange([ start, end ], corrected_text)
168
+ },
169
+ loc: {
170
+ start: source_code.getLocFromIndex(start),
171
+ end: source_code.getLocFromIndex(end)
172
+ },
173
+ messageId: "not_match",
174
+ node
175
+ }
176
+ )
177
+ }
178
+ /**
179
+ * @param {import("../private").AstNode} node
180
+ * @returns {void}
181
+ */
182
+ function verify_correct(node) {
183
+ /**
184
+ * @param {number} index
185
+ * @returns {string}
186
+ */
187
+ function get_indent(index) {
188
+ return fixed_text.slice(0, text_indexes[index]).match(indent_regex)?.[0] || ""
189
+ }
190
+ let length = 0
191
+ let start = 0
192
+ let end = 0
193
+ let corrected_text = ""
194
+ let line_indent = get_indent(node.range[0])
195
+ switch (node.type) {
196
+ case "ArrayExpression":
197
+ case "ArrayPattern":
198
+ start = node.range[0] + 1
199
+ end = node.range[1] - 1
200
+ let null_anchor = start
201
+ const elements = node.elements.map(
202
+ v => {
203
+ const n = v ?? {
204
+ range: [ null_anchor, null_anchor ]
205
+ }
206
+ const text = get_text(n, start, end)
207
+ null_anchor += text.length + 1
208
+ return n
209
+ }
210
+ )
211
+ for (const text of elements.map(v => get_text(v, start, end))) {
212
+ if (text.includes("\n")) {
213
+ length = max_length + 1
214
+ break
215
+ }
216
+ length += text.length
217
+ }
218
+ corrected_text = length > max_length
219
+ ? `\n${line_indent + indent}${
220
+ elements.map(
221
+ v => get_text(
222
+ v,
223
+ start,
224
+ end,
225
+ line_indent == get_indent(v.range[0])
226
+ )
227
+ )
228
+ .join(",\n" + line_indent + indent)
229
+ }\n${line_indent}`
230
+ : `${length && array_bracket_spacing ? " " : ""}${
231
+ elements.map(v => get_text(v, start, end)).join(", ")
232
+ }${length && array_bracket_spacing ? " " : ""}`
233
+ break
234
+ case "ArrowFunctionExpression":
235
+ case "FunctionDeclaration":
236
+ case "FunctionExpression":
237
+ if (node.type == "ArrowFunctionExpression") {
238
+ if (
239
+ node.params.length != 1
240
+ || origin_text.slice(
241
+ node.range[0],
242
+ node.params[0].range[0]
243
+ ).includes("(")
244
+ ) {
245
+ start = origin_text.indexOf("(", node.range[0]) + 1
246
+ end = origin_text.indexOf(
247
+ ")",
248
+ node.params.length
249
+ ? node.params[node.params.length - 1].range[1]
250
+ : start
251
+ )
252
+ } else {
253
+ [ start, end ] = node.params[0].range
254
+ }
255
+ } else {
256
+ start = node.type == "FunctionDeclaration"
257
+ ? origin_text.indexOf("(", node.id.range[1]) + 1
258
+ : node.id
259
+ ? origin_text.indexOf("(", node.id.range[1]) + 1
260
+ : origin_text.indexOf("(", node.range[0]) + 1
261
+ end = origin_text.indexOf(
262
+ ")",
263
+ node.params.length
264
+ ? node.params[node.params.length - 1].range[1]
265
+ : start
266
+ )
267
+ }
268
+ for (const text of node.params.map(v => get_text(v, start, end))) {
269
+ if (text.includes("\n")) {
270
+ length = max_length + 1
271
+ break
272
+ }
273
+ length += text.length
274
+ }
275
+ corrected_text = length > max_length
276
+ ? `\n${line_indent + indent}${
277
+ node.params.map(
278
+ v => get_text(
279
+ v,
280
+ start,
281
+ end,
282
+ line_indent == get_indent(v.range[0])
283
+ )
284
+ )
285
+ .join(",\n" + line_indent + indent)
286
+ }\n${line_indent}`
287
+ : `${length && func_call_spacing ? " " : ""}${
288
+ node.params.map(v => get_text(v, start, end)).join(", ")
289
+ }${length && func_call_spacing ? " " : ""}`
290
+ break
291
+ case "CallExpression":
292
+ case "NewExpression":
293
+ if (node.range[1] == node.callee.range[1]) return
294
+ line_indent = get_indent(node.callee.range[1])
295
+ start = origin_text.indexOf("(", node.callee.range[1]) + 1
296
+ end = node.range[1] - 1
297
+ for (const text of node.arguments.map(v => get_text(v, start, end))) {
298
+ if (text.includes("\n")) {
299
+ length = max_length + 1
300
+ break
301
+ }
302
+ length += text.length
303
+ }
304
+ corrected_text = length > max_length
305
+ ? `\n${line_indent + indent}${
306
+ node.arguments.map(
307
+ v => get_text(
308
+ v,
309
+ start,
310
+ end,
311
+ line_indent == get_indent(v.range[0])
312
+ )
313
+ )
314
+ .join(",\n" + line_indent + indent)
315
+ }\n${line_indent}`
316
+ : `${length && func_call_spacing ? " " : ""}${
317
+ node.arguments.map(v => get_text(v, start, end)).join(", ")
318
+ }${length && func_call_spacing ? " " : ""}`
319
+ break
320
+ case "ObjectExpression":
321
+ case "ObjectPattern":
322
+ start = node.range[0] + 1
323
+ end = node.range[1] - 1
324
+ for (const text of node.properties.map(v => get_text(v, start, end))) {
325
+ if (text.includes("\n")) {
326
+ length = max_length + 1
327
+ break
328
+ }
329
+ length += text.length
330
+ }
331
+ corrected_text = length > max_length
332
+ ? `\n${line_indent + indent}${
333
+ node.properties.map(
334
+ v => get_text(
335
+ v,
336
+ start,
337
+ end,
338
+ line_indent == get_indent(v.range[0])
339
+ )
340
+ )
341
+ .join(",\n" + line_indent + indent)
342
+ }\n${line_indent}`
343
+ : `${length && object_curly_spacing ? " " : ""}${
344
+ node.properties.map(v => get_text(v, start, end))
345
+ .join(", ")
346
+ }${length && object_curly_spacing ? " " : ""}`
347
+ break
348
+ case "SequenceExpression":
349
+ start = node.range[0]
350
+ end = node.range[1]
351
+ for (const text of node.expressions.map(v => get_text(v, start, end))) {
352
+ if (text.includes("\n")) {
353
+ length = max_length + 1
354
+ break
355
+ }
356
+ length += text.length
357
+ }
358
+ corrected_text = length > max_length
359
+ ? `${node.expressions
360
+ .map(v => get_text(v, start, end))
361
+ .join(",\n" + line_indent)}`
362
+ : `${node.expressions
363
+ .map(v => get_text(v, start, end))
364
+ .join(", ")}`
365
+ break
366
+ }
367
+
368
+ if (
369
+ fixed_text.slice(
370
+ text_indexes[start],
371
+ text_indexes[end]
372
+ )
373
+ .replace(_r_regex, "")
374
+ != corrected_text
375
+ ) {
376
+ report(node, start, end, corrected_text)
377
+ }
378
+ }
379
+ return /** @type {import("../private").RuleListener} */({
380
+ "Program:exit": () => {
381
+ for (const node of nodes.reverse()) {
382
+ verify_correct(node)
383
+ }
384
+ },
385
+ ArrayExpression: node => {
386
+ if (check_array) push_to_reverse(node)
387
+ },
388
+ ArrayPattern: node => {
389
+ if (check_array) push_to_reverse(node)
390
+ },
391
+ ArrowFunctionExpression: node => {
392
+ if (check_call) push_to_reverse(node)
393
+ },
394
+ CallExpression: node => {
395
+ if (check_call) push_to_reverse(node)
396
+ },
397
+ FunctionExpression: node => {
398
+ if (check_call) push_to_reverse(node)
399
+ },
400
+ FunctionDeclaration: node => {
401
+ if (check_call) push_to_reverse(node)
402
+ },
403
+ NewExpression: node => {
404
+ if (check_call) push_to_reverse(node)
405
+ },
406
+ ObjectExpression: node => {
407
+ if (check_object) push_to_reverse(node)
408
+ },
409
+ ObjectPattern: node => {
410
+ if (check_object) push_to_reverse(node)
411
+ },
412
+ SequenceExpression: node => {
413
+ if (check_sequence) push_to_reverse(node)
414
+ }
415
+ })/**/
416
+ }
417
+ }