eslint-plugin-lube 0.3.5 → 0.4.1

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