eslint-plugin-lube 0.8.0 → 0.9.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.
Files changed (39) hide show
  1. package/CHANGELOG.md +95 -0
  2. package/README.md +17 -4
  3. package/docs/closures-last.md +34 -0
  4. package/docs/indent.md +102 -0
  5. package/docs/no-ambiguous-start.md +23 -0
  6. package/docs/no-loose-types.md +25 -0
  7. package/docs/no-typedef.md +24 -0
  8. package/docs/pretty-jsdoc-lines.md +30 -0
  9. package/docs/pretty-parens.md +59 -0
  10. package/docs/require-returns.md +21 -0
  11. package/docs/svelte-naming-convention.md +5 -6
  12. package/package.json +2 -7
  13. package/src/index.js +78 -19
  14. package/src/rules/ascii-order.js +113 -127
  15. package/src/rules/closures-last.js +323 -0
  16. package/src/rules/indent.js +84 -0
  17. package/src/rules/no-ambiguous-start.js +42 -0
  18. package/src/rules/no-loose-types.js +127 -0
  19. package/src/rules/no-typedef.js +44 -0
  20. package/src/rules/prefer-param-tag.js +35 -35
  21. package/src/rules/prefer-type-tag.js +29 -29
  22. package/src/rules/pretty-imports.js +23 -23
  23. package/src/rules/pretty-jsdoc-casting.js +39 -39
  24. package/src/rules/pretty-jsdoc-lines.js +104 -0
  25. package/src/rules/pretty-parens.js +280 -0
  26. package/src/rules/pretty-sequence.js +79 -95
  27. package/src/rules/pretty-ternary.js +27 -31
  28. package/src/rules/require-returns.js +190 -0
  29. package/src/rules/svelte-naming-convention.js +457 -546
  30. package/src/utils/accept_cr_eol.js +36 -0
  31. package/src/utils/indent_model.js +1596 -0
  32. package/src/utils/jsdoc_tags.js +30 -0
  33. package/src/utils/order_compare.js +17 -0
  34. package/src/utils/rule_options.js +30 -0
  35. package/src/utils/skip_unchanged_fixes.js +3 -3
  36. package/src/utils/source_text.js +80 -255
  37. package/src/utils/split_declarations.js +35 -14
  38. package/src/utils/trim_fixes.js +108 -0
  39. package/types/index.d.ts.map +1 -1
package/src/index.js CHANGED
@@ -1,21 +1,32 @@
1
1
  import ascii_order from "./rules/ascii-order.js"
2
+ import closures_last from "./rules/closures-last.js"
3
+ import indent from "./rules/indent.js"
4
+ import no_ambiguous_start from "./rules/no-ambiguous-start.js"
5
+ import no_loose_types from "./rules/no-loose-types.js"
6
+ import no_typedef from "./rules/no-typedef.js"
2
7
  import prefer_param_tag from "./rules/prefer-param-tag.js"
3
8
  import prefer_type_tag from "./rules/prefer-type-tag.js"
4
9
  import pretty_imports from "./rules/pretty-imports.js"
5
10
  import pretty_jsdoc_casting from "./rules/pretty-jsdoc-casting.js"
11
+ import pretty_jsdoc_lines from "./rules/pretty-jsdoc-lines.js"
12
+ import pretty_parens from "./rules/pretty-parens.js"
6
13
  import pretty_sequence from "./rules/pretty-sequence.js"
7
14
  import pretty_ternary from "./rules/pretty-ternary.js"
15
+ import require_returns from "./rules/require-returns.js"
8
16
  import svelte_naming_convention from "./rules/svelte-naming-convention.js"
17
+ import { accept_cr_eol } from "./utils/accept_cr_eol.js"
9
18
  import { keep_body_comments } from "./utils/keep_body_comments.js"
10
19
  import {
11
20
  keep_comment_order,
12
21
  keep_comments
13
22
  } from "./utils/keep_comments.js"
14
23
  import { keep_line_breaks } from "./utils/keep_line_breaks.js"
24
+ import { record_options } from "./utils/rule_options.js"
15
25
  import { skip_hole_commas } from "./utils/skip_hole_commas.js"
16
26
  import { skip_optional_patterns } from "./utils/skip_optional_patterns.js"
17
27
  import { skip_unchanged_fixes } from "./utils/skip_unchanged_fixes.js"
18
28
  import { split_declarations } from "./utils/split_declarations.js"
29
+ import { trim_fixes } from "./utils/trim_fixes.js"
19
30
  import stylistic_plugin from "@stylistic/eslint-plugin"
20
31
  import { builtinRules } from "eslint/use-at-your-own-risk"
21
32
  import { readFileSync } from "node:fs"
@@ -31,6 +42,12 @@ const manifest = JSON.parse(
31
42
  "utf8"
32
43
  )
33
44
  )
45
+ const whole_comment_rules = new Set(
46
+ [
47
+ "pretty-jsdoc-lines",
48
+ "require-returns"
49
+ ]
50
+ )
34
51
  const brace_rules = /** @type {const} */([
35
52
  "object-curly-newline",
36
53
  "object-curly-spacing"
@@ -66,11 +83,6 @@ const strict_rules = {
66
83
  "@stylistic/function-call-argument-newline": [ "error", "consistent" ],
67
84
  "@stylistic/function-call-spacing": [ "error", "never" ],
68
85
  "@stylistic/generator-star-spacing": [ "error", "after" ],
69
- "@stylistic/indent": [
70
- "error",
71
- "tab",
72
- { "SwitchCase": 0 }
73
- ],
74
86
  "@stylistic/jsx-quotes": [ "error", "prefer-double" ],
75
87
  "@stylistic/key-spacing": [
76
88
  "error",
@@ -152,13 +164,28 @@ const strict_rules = {
152
164
  checkTests: true
153
165
  }
154
166
  ],
167
+ "lube/closures-last": "error",
155
168
  "lube/curly": [ "error", "all" ],
169
+ "lube/indent": [
170
+ "error",
171
+ "tab",
172
+ {
173
+ "SwitchCase": 0,
174
+ "binaryOperator": 1
175
+ }
176
+ ],
177
+ "lube/no-ambiguous-start": "error",
178
+ "lube/no-loose-types": "error",
179
+ "lube/no-typedef": "error",
156
180
  "lube/prefer-param-tag": "error",
157
181
  "lube/prefer-type-tag": "error",
158
182
  "lube/pretty-imports": "error",
159
183
  "lube/pretty-jsdoc-casting": "error",
184
+ "lube/pretty-jsdoc-lines": "error",
185
+ "lube/pretty-parens": "error",
160
186
  "lube/pretty-sequence": "error",
161
187
  "lube/pretty-ternary": "error",
188
+ "lube/require-returns": "error",
162
189
  "lube/svelte-naming-convention": "error",
163
190
  "no-case-declarations": "off",
164
191
  "no-console": "warn",
@@ -212,8 +239,15 @@ const recommended_rules = {
212
239
  checkTests: false
213
240
  }
214
241
  ],
242
+ "lube/indent": [
243
+ "error",
244
+ "tab",
245
+ { "SwitchCase": 0 }
246
+ ],
215
247
  "lube/pretty-imports": [ "error", { maxLength: 80 } ],
216
248
  "lube/pretty-jsdoc-casting": "error",
249
+ "lube/pretty-jsdoc-lines": "error",
250
+ "lube/pretty-parens": "error",
217
251
  "lube/pretty-sequence": [ "error", { maxLength: 80 } ],
218
252
  "lube/pretty-ternary": "error"
219
253
  }
@@ -243,19 +277,39 @@ const plugin = {
243
277
  name: "eslint-plugin-lube",
244
278
  version: manifest.version
245
279
  },
246
- rules: {
247
- "ascii-order": ascii_order,
248
- "curly": keep_body_comments(
249
- /** @type {import("eslint").Rule.RuleModule} */(builtinRules.get("curly"))/**/
250
- ),
251
- "prefer-param-tag": prefer_param_tag,
252
- "prefer-type-tag": prefer_type_tag,
253
- "pretty-imports": pretty_imports,
254
- "pretty-jsdoc-casting": pretty_jsdoc_casting,
255
- "pretty-sequence": pretty_sequence,
256
- "pretty-ternary": pretty_ternary,
257
- "svelte-naming-convention": svelte_naming_convention
258
- }
280
+ rules: Object.fromEntries(
281
+ Object.entries(
282
+ /** @type {Record<string, import("eslint").Rule.RuleModule>} */({
283
+ "ascii-order": ascii_order,
284
+ "closures-last": closures_last,
285
+ "curly": keep_body_comments(
286
+ /** @type {import("eslint").Rule.RuleModule} */(builtinRules.get("curly"))/**/
287
+ ),
288
+ "indent": indent,
289
+ "no-ambiguous-start": no_ambiguous_start,
290
+ "no-loose-types": no_loose_types,
291
+ "no-typedef": no_typedef,
292
+ "prefer-param-tag": prefer_param_tag,
293
+ "prefer-type-tag": prefer_type_tag,
294
+ "pretty-imports": pretty_imports,
295
+ "pretty-jsdoc-casting": pretty_jsdoc_casting,
296
+ "pretty-jsdoc-lines": pretty_jsdoc_lines,
297
+ "pretty-parens": pretty_parens,
298
+ "pretty-sequence": pretty_sequence,
299
+ "pretty-ternary": pretty_ternary,
300
+ "require-returns": require_returns,
301
+ "svelte-naming-convention": svelte_naming_convention
302
+ })/**/
303
+ ).map(
304
+ ([ name, rule ]) => [
305
+ name,
306
+ record_options(
307
+ name,
308
+ rule.meta?.fixable && !whole_comment_rules.has(name) ? trim_fixes(rule) : rule
309
+ )
310
+ ]
311
+ )
312
+ )
259
313
  }
260
314
  /** @type {Record<string, import("eslint").Rule.RuleModule>} */
261
315
  const stylistic_rules = {
@@ -263,6 +317,9 @@ const stylistic_rules = {
263
317
  "comma-style": skip_hole_commas(
264
318
  /** @type {import("eslint").Rule.RuleModule} */(stylistic.rules?.["comma-style"])/**/
265
319
  ),
320
+ "eol-last": accept_cr_eol(
321
+ /** @type {import("eslint").Rule.RuleModule} */(stylistic.rules?.["eol-last"])/**/
322
+ ),
266
323
  "max-statements-per-line": split_declarations(
267
324
  /** @type {import("eslint").Rule.RuleModule} */(stylistic.rules?.["max-statements-per-line"])/**/
268
325
  ),
@@ -298,7 +355,9 @@ const plugins = {
298
355
  Object.entries(stylistic_rules).map(
299
356
  ([ name, rule ]) => [
300
357
  name,
301
- skip_unchanged_fixes(keep_line_breaks(rule))
358
+ trim_fixes(
359
+ skip_unchanged_fixes(keep_line_breaks(rule))
360
+ )
302
361
  ]
303
362
  )
304
363
  )
@@ -1,3 +1,4 @@
1
+ import { compare } from "../utils/order_compare.js"
1
2
  import { create_source_text } from "../utils/source_text.js"
2
3
  const not_static = Symbol("not static")
3
4
  const portable_flags_regex = /^[gimsuy]*$/
@@ -59,22 +60,6 @@ const safe_constructors = new Set(
59
60
  const closers = new Set([ ")", "]", "}" ])
60
61
  const member_separators = new Set([ ",", ";" ])
61
62
  const shebang_types = new Set([ "Hashbang", "Shebang" ])
62
- /**
63
- * @param {import("../../private.js").OrderEntry} a
64
- * @param {import("../../private.js").OrderEntry} b
65
- * @returns {number}
66
- */
67
- function compare(a, b) {
68
- const rank = (a.rank ?? 1) - (b.rank ?? 1)
69
- if (rank) {
70
- return rank
71
- }
72
- return a.name < b.name
73
- ? -1
74
- : a.name > b.name
75
- ? 1
76
- : 0
77
- }
78
63
  /**
79
64
  * @param {import("../../private.js").Comment[]} comments
80
65
  * @param {number} line
@@ -93,11 +78,13 @@ function count_same_line(comments, line) {
93
78
  }
94
79
  /**
95
80
  * @param {import("eslint").Rule.RuleContext} context
81
+ * @returns {{ verify: (entries: import("../../private.js").OrderEntry[], message_id: string, layout: import("../../private.js").OrderLayout, get_level?: ((node: import("../../private.js").OrderNode) => number) | undefined) => void; }}
96
82
  */
97
83
  function create_reorder(context) {
98
84
  const source_code = context.sourceCode
99
85
  const source = create_source_text(source_code)
100
86
  const text = source.text
87
+ return { verify }
101
88
  /**
102
89
  * @param {import("../../private.js").OrderEntry} entry
103
90
  * @param {import("../../private.js").OrderLayout} layout
@@ -216,11 +203,11 @@ function create_reorder(context) {
216
203
  const has_tail = item.tail_start < item.after
217
204
  if (
218
205
  item.leading && !slot.break_before
219
- || i == 0 && slot.head && !slot.break_before
220
- || slot.header && item != slot
221
- || slot.tail_start < slot.after && !slot.break_after && (i < last_index || !slot.closes)
222
- || has_tail && !slot.break_after && (i < last_index || item.tail_line)
223
- || layout == "statement" && i < last_index && !item.terminated && !slot.break_after
206
+ || i == 0 && slot.head && !slot.break_before
207
+ || slot.header && item != slot
208
+ || slot.tail_start < slot.after && !slot.break_after && (i < last_index || !slot.closes)
209
+ || has_tail && !slot.break_after && (i < last_index || item.tail_line)
210
+ || layout == "statement" && i < last_index && !item.terminated && !slot.break_after
224
211
  ) {
225
212
  return null
226
213
  }
@@ -302,7 +289,6 @@ function create_reorder(context) {
302
289
  }
303
290
  )
304
291
  }
305
- return { verify }
306
292
  }
307
293
  /**
308
294
  * @param {import("../../private.js").OrderItem[]} items
@@ -409,6 +395,107 @@ export default {
409
395
  const default_exports = new Set()
410
396
  /** @type {Parameters<typeof verify>[]} */
411
397
  const pending = []
398
+ /** @type {import("../../private.js").OrderRuleListener} */
399
+ const listener = {
400
+ BlockStatement: verify_statements,
401
+ ExportNamedDeclaration: node => {
402
+ if (check_names) {
403
+ verify_nodes(
404
+ node.specifiers,
405
+ get_specifier_name,
406
+ "names",
407
+ "sequence"
408
+ )
409
+ }
410
+ },
411
+ ImportDeclaration: node => {
412
+ if (check_names) {
413
+ verify_nodes(
414
+ node.specifiers,
415
+ get_specifier_name,
416
+ "names",
417
+ "sequence"
418
+ )
419
+ }
420
+ },
421
+ ObjectExpression: node => {
422
+ if (!check_keys) {
423
+ return
424
+ }
425
+ const condition = is_package_condition(node)
426
+ verify_nodes(
427
+ node.properties,
428
+ get_key_name,
429
+ condition ? "conditions" : "keys",
430
+ "sequence",
431
+ classify,
432
+ condition ? get_condition_rank : undefined
433
+ )
434
+ },
435
+ ObjectPattern: node => {
436
+ if (check_keys) {
437
+ /** @type {Map<import("../../private.js").OrderNode, string[]>} */
438
+ const targets = new Map()
439
+ /** @type {Set<string>} */
440
+ const seen = new Set()
441
+ /** @type {Set<string>} */
442
+ const repeated = new Set()
443
+ for (const property of node.properties) {
444
+ const names = collect_targets(property, [])
445
+ for (const name of new Set(names)) {
446
+ if (seen.has(name)) {
447
+ repeated.add(name)
448
+ }
449
+ seen.add(name)
450
+ }
451
+ targets.set(property, names)
452
+ }
453
+ verify_nodes(
454
+ node.properties,
455
+ get_key_name,
456
+ "keys",
457
+ "sequence",
458
+ property => targets.get(property)?.some(name => repeated.has(name))
459
+ ? 2
460
+ : classify_pattern(property)
461
+ )
462
+ }
463
+ },
464
+ Program: node => {
465
+ collect_default_exports(node)
466
+ verify_statements(node)
467
+ },
468
+ "Program:exit": () => {
469
+ for (let i = pending.length - 1; i >= 0; i--) {
470
+ verify(
471
+ .../** @type {Parameters<typeof verify>} */(pending[i])/**/
472
+ )
473
+ }
474
+ },
475
+ StaticBlock: verify_statements,
476
+ TSInterfaceBody: node => {
477
+ if (check_keys) {
478
+ verify_nodes(
479
+ node.body,
480
+ get_key_name,
481
+ "keys",
482
+ "member"
483
+ )
484
+ }
485
+ },
486
+ TSModuleBlock: verify_statements,
487
+ TSTypeLiteral: node => {
488
+ if (check_keys) {
489
+ verify_nodes(
490
+ node.members,
491
+ get_key_name,
492
+ "keys",
493
+ "member"
494
+ )
495
+ }
496
+ }
497
+ }
498
+ return /** @type {import("eslint").Rule.RuleListener} */(/** @type {unknown} */(listener))/**/
412
499
  /**
413
500
  * @param {import("../../private.js").OrderNode} node
414
501
  * @returns {number}
@@ -534,8 +621,8 @@ export default {
534
621
  const args = node.arguments.map(static_value)
535
622
  if (
536
623
  args.includes(not_static)
537
- || (callee.name.includes("Array") || callee.name == "DataView") && args.some(is_large)
538
- || callee.name == "RegExp" && !is_portable_regexp(args)
624
+ || (callee.name.includes("Array") || callee.name == "DataView") && args.some(is_large)
625
+ || callee.name == "RegExp" && !is_portable_regexp(args)
539
626
  ) {
540
627
  return not_static
541
628
  }
@@ -694,8 +781,8 @@ export default {
694
781
  const parent = /** @type {import("eslint").Rule.Node} */(declaration)/**/.parent
695
782
  if (
696
783
  parent?.type == "SwitchCase"
697
- || parent?.parent?.type == "SwitchCase"
698
- || /** @type {[number, number]} */(node.range)/**/[0] < /** @type {[number, number]} */(declaration.range)/**/[1]
784
+ || parent?.parent?.type == "SwitchCase"
785
+ || /** @type {[number, number]} */(node.range)/**/[0] < /** @type {[number, number]} */(declaration.range)/**/[1]
699
786
  ) {
700
787
  return false
701
788
  }
@@ -892,107 +979,6 @@ export default {
892
979
  }
893
980
  }
894
981
  }
895
- /** @type {import("../../private.js").OrderRuleListener} */
896
- const listener = {
897
- BlockStatement: verify_statements,
898
- ExportNamedDeclaration: node => {
899
- if (check_names) {
900
- verify_nodes(
901
- node.specifiers,
902
- get_specifier_name,
903
- "names",
904
- "sequence"
905
- )
906
- }
907
- },
908
- ImportDeclaration: node => {
909
- if (check_names) {
910
- verify_nodes(
911
- node.specifiers,
912
- get_specifier_name,
913
- "names",
914
- "sequence"
915
- )
916
- }
917
- },
918
- ObjectExpression: node => {
919
- if (!check_keys) {
920
- return
921
- }
922
- const condition = is_package_condition(node)
923
- verify_nodes(
924
- node.properties,
925
- get_key_name,
926
- condition ? "conditions" : "keys",
927
- "sequence",
928
- classify,
929
- condition ? get_condition_rank : undefined
930
- )
931
- },
932
- ObjectPattern: node => {
933
- if (check_keys) {
934
- /** @type {Map<import("../../private.js").OrderNode, string[]>} */
935
- const targets = new Map()
936
- /** @type {Set<string>} */
937
- const seen = new Set()
938
- /** @type {Set<string>} */
939
- const repeated = new Set()
940
- for (const property of node.properties) {
941
- const names = collect_targets(property, [])
942
- for (const name of new Set(names)) {
943
- if (seen.has(name)) {
944
- repeated.add(name)
945
- }
946
- seen.add(name)
947
- }
948
- targets.set(property, names)
949
- }
950
- verify_nodes(
951
- node.properties,
952
- get_key_name,
953
- "keys",
954
- "sequence",
955
- property => targets.get(property)?.some(name => repeated.has(name))
956
- ? 2
957
- : classify_pattern(property)
958
- )
959
- }
960
- },
961
- Program: node => {
962
- collect_default_exports(node)
963
- verify_statements(node)
964
- },
965
- "Program:exit": () => {
966
- for (let i = pending.length - 1; i >= 0; i--) {
967
- verify(
968
- .../** @type {Parameters<typeof verify>} */(pending[i])/**/
969
- )
970
- }
971
- },
972
- StaticBlock: verify_statements,
973
- TSInterfaceBody: node => {
974
- if (check_keys) {
975
- verify_nodes(
976
- node.body,
977
- get_key_name,
978
- "keys",
979
- "member"
980
- )
981
- }
982
- },
983
- TSModuleBlock: verify_statements,
984
- TSTypeLiteral: node => {
985
- if (check_keys) {
986
- verify_nodes(
987
- node.members,
988
- get_key_name,
989
- "keys",
990
- "member"
991
- )
992
- }
993
- }
994
- }
995
- return /** @type {import("eslint").Rule.RuleListener} */(/** @type {unknown} */(listener))/**/
996
982
  },
997
983
  meta: {
998
984
  docs: {