@so1ve/eslint-plugin 0.47.3 → 0.50.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 (3) hide show
  1. package/dist/index.cjs +231 -216
  2. package/dist/index.mjs +231 -216
  3. package/package.json +1 -1
package/dist/index.cjs CHANGED
@@ -1,8 +1,8 @@
1
1
  'use strict';
2
2
 
3
3
  const utils = require('@typescript-eslint/utils');
4
- const util = require('@typescript-eslint/utils/dist/ast-utils');
5
4
  const reactivity = require('@vue/reactivity');
5
+ const util = require('@typescript-eslint/utils/dist/ast-utils');
6
6
 
7
7
  function _interopNamespaceDefault(e) {
8
8
  const n = Object.create(null);
@@ -19,9 +19,81 @@ const util__namespace = /*#__PURE__*/_interopNamespaceDefault(util);
19
19
 
20
20
  const createEslintRule = utils.ESLintUtils.RuleCreator((ruleName) => ruleName);
21
21
 
22
- const RULE_NAME$8 = "generic-spacing";
23
- const genericSpacing = createEslintRule({
22
+ const RULE_NAME$8 = "array-bracket-spacing";
23
+ const arrayBracketSpacing = createEslintRule({
24
24
  name: RULE_NAME$8,
25
+ meta: {
26
+ type: "layout",
27
+ docs: {
28
+ description: "Array bracket spacing",
29
+ recommended: "error"
30
+ },
31
+ fixable: "whitespace",
32
+ schema: [],
33
+ messages: {
34
+ arrayBracketSpacing: "Array bracket spacing mismatch"
35
+ }
36
+ },
37
+ defaultOptions: [],
38
+ create: (context) => {
39
+ const sourceCode = context.getSourceCode();
40
+ const text = sourceCode.getText();
41
+ const checkNode = (node) => {
42
+ const elements = node.type === utils.AST_NODE_TYPES.TSTupleType ? node.elementTypes : node.elements;
43
+ const firstElement = elements[0];
44
+ const lastElement = elements[elements.length - 1];
45
+ if (!firstElement) {
46
+ return;
47
+ }
48
+ const leftToken = sourceCode.getTokenBefore(firstElement);
49
+ const rightToken = reactivity.ref(sourceCode.getTokenAfter(lastElement));
50
+ if (rightToken.value.value === ",") {
51
+ rightToken.value = sourceCode.getTokenAfter(rightToken.value);
52
+ }
53
+ const textBetweenFirstAndToken = reactivity.computed(() => text.slice(leftToken.range[1], firstElement.range[0]));
54
+ const isNewline = reactivity.computed(() => textBetweenFirstAndToken.value.includes("\n"));
55
+ const textBetweenLastAndToken = reactivity.computed(() => text.slice(lastElement.range[1], rightToken.value.range[0]));
56
+ const hasNewlineAfter = reactivity.computed(() => textBetweenLastAndToken.value.includes("\n"));
57
+ if (sourceCode.isSpaceBetween(leftToken, firstElement) && !isNewline.value) {
58
+ context.report({
59
+ node,
60
+ messageId: "arrayBracketSpacing",
61
+ *fix(fixer) {
62
+ yield fixer.removeRange([leftToken.range[1], firstElement.range[0]]);
63
+ }
64
+ });
65
+ }
66
+ if (sourceCode.isSpaceBetween(lastElement, rightToken.value)) {
67
+ if (!isNewline.value) {
68
+ context.report({
69
+ node,
70
+ messageId: "arrayBracketSpacing",
71
+ *fix(fixer) {
72
+ yield fixer.removeRange([lastElement.range[1], rightToken.value.range[0]]);
73
+ }
74
+ });
75
+ }
76
+ }
77
+ if (isNewline.value && !hasNewlineAfter.value) {
78
+ context.report({
79
+ node,
80
+ messageId: "arrayBracketSpacing",
81
+ *fix(fixer) {
82
+ yield fixer.replaceTextRange([lastElement.range[1], rightToken.value.range[0]], "\n");
83
+ }
84
+ });
85
+ }
86
+ };
87
+ return {
88
+ TSTupleType: checkNode,
89
+ ArrayExpression: checkNode
90
+ };
91
+ }
92
+ });
93
+
94
+ const RULE_NAME$7 = "generic-spacing";
95
+ const genericSpacing = createEslintRule({
96
+ name: RULE_NAME$7,
25
97
  meta: {
26
98
  type: "layout",
27
99
  docs: {
@@ -159,7 +231,7 @@ const genericSpacing = createEslintRule({
159
231
  if (!node.default) {
160
232
  return;
161
233
  }
162
- const endNode = node.constraint || node.name;
234
+ const endNode = node.constraint ?? node.name;
163
235
  const from = endNode.range[1];
164
236
  const to = node.default.range[0];
165
237
  const spaceAndEqual = sourceCode.text.slice(from, to);
@@ -289,9 +361,9 @@ const genericSpacing = createEslintRule({
289
361
  }
290
362
  });
291
363
 
292
- const RULE_NAME$7 = "import-dedupe";
364
+ const RULE_NAME$6 = "import-dedupe";
293
365
  const importDedupe = createEslintRule({
294
- name: RULE_NAME$7,
366
+ name: RULE_NAME$6,
295
367
  meta: {
296
368
  type: "problem",
297
369
  docs: {
@@ -339,86 +411,61 @@ const importDedupe = createEslintRule({
339
411
  }
340
412
  });
341
413
 
342
- const operatorOrAnyBracketOrKeywordRE = /^(\||&|\*|\+|\-|\/|%|<|>|<=|>=|==|!=|===|!==|\[|\(|\{|as|extends|implements|keyof|new|readonly|typeof|unique|unknown)/;
343
- const RULE_NAME$6 = "space-between-generic-and-paren";
344
- const spaceBetweenGenericAndParen = createEslintRule({
345
- name: RULE_NAME$6,
414
+ const RULE_NAME$5 = "no-inline-type-import";
415
+ const noInlineTypeImport = createEslintRule({
416
+ name: RULE_NAME$5,
346
417
  meta: {
347
418
  type: "layout",
348
419
  docs: {
349
- description: "Spaces between generic type parameters and paren",
420
+ description: "Disallow inline type import",
350
421
  recommended: "error"
351
422
  },
352
- fixable: "whitespace",
423
+ fixable: "code",
353
424
  schema: [],
354
425
  messages: {
355
- noSpaceBetweenGenericAndParen: "Expected no space between generic type parameters and paren"
426
+ noInlineTypeImport: "Expected no inline type import"
356
427
  }
357
428
  },
358
429
  defaultOptions: [],
359
430
  create: (context) => {
360
431
  const sourceCode = context.getSourceCode();
361
- const text = sourceCode.text;
362
432
  return {
363
- TSTypeParameter: (node) => {
364
- const spaceStartRange = node.range[1] + 1;
365
- const post = text.slice(spaceStartRange);
366
- const postSpace = post.match(/^(\s*)/)?.[0];
367
- const postEqual = post.slice(postSpace.length).match(/^(=)/)?.[0];
368
- const postComma = text.slice(node.range[1]).match(/^(,)/)?.[0];
369
- const postQuestionMark = text.slice(spaceStartRange + postSpace.length).match(/^(\?)/)?.[0];
370
- const postOperatorOrAnyBracketOrKeyword = text.slice(spaceStartRange + postSpace.length).match(operatorOrAnyBracketOrKeywordRE)?.[0];
371
- if (postSpace && postSpace.length && !postEqual && !postComma && !postQuestionMark && !postOperatorOrAnyBracketOrKeyword && node.parent.type !== utils.AST_NODE_TYPES.TSInferType) {
433
+ ImportDeclaration: (node) => {
434
+ const specifiers = node.specifiers;
435
+ const typeSpecifiers = specifiers.filter((s) => s.type === "ImportSpecifier" && s.importKind === "type");
436
+ const valueSpecifiers = specifiers.filter((s) => s.type === "ImportSpecifier" && s.importKind === "value");
437
+ if (typeSpecifiers.length && valueSpecifiers.length) {
372
438
  context.report({
373
- loc: {
374
- start: {
375
- line: node.loc.end.line,
376
- column: node.loc.end.column + 1
377
- },
378
- end: {
379
- line: node.loc.end.line,
380
- column: node.loc.end.column + 1 + postSpace.length
381
- }
382
- },
383
- node,
384
- messageId: "noSpaceBetweenGenericAndParen",
439
+ loc: node.loc,
440
+ messageId: "noInlineTypeImport",
385
441
  *fix(fixer) {
386
- yield fixer.replaceTextRange([spaceStartRange, spaceStartRange + postSpace.length], "");
442
+ const typeSpecifiersText = typeSpecifiers.map((s) => sourceCode.getText(s).replace("type ", "")).join(", ");
443
+ const valueSpecifiersText = valueSpecifiers.map((s) => sourceCode.getText(s)).join(", ");
444
+ yield fixer.replaceText(
445
+ node,
446
+ `import type { ${typeSpecifiersText} } from "${node.source.value}";
447
+ import { ${valueSpecifiersText} } from "${node.source.value}";`
448
+ );
449
+ }
450
+ });
451
+ } else if (typeSpecifiers.length) {
452
+ context.report({
453
+ loc: node.loc,
454
+ messageId: "noInlineTypeImport",
455
+ *fix(fixer) {
456
+ const typeSpecifiersText = typeSpecifiers.map((s) => sourceCode.getText(s).replace("type ", "")).join(", ");
457
+ yield fixer.replaceText(node, `import type { ${typeSpecifiersText} } from "${node.source.value}";`);
387
458
  }
388
459
  });
389
- }
390
- if (node.parent?.parent.type === utils.AST_NODE_TYPES.FunctionDeclaration) {
391
- const spaceEndRange = node.range[0] - 1;
392
- const pre = sourceCode.text.slice(0, spaceEndRange);
393
- const preSpace = pre.match(/(\s+)$/)?.[0];
394
- if (preSpace && preSpace.length) {
395
- context.report({
396
- loc: {
397
- start: {
398
- line: node.loc.start.line,
399
- column: node.loc.start.column - preSpace.length
400
- },
401
- end: {
402
- line: node.loc.start.line,
403
- column: node.loc.start.column - 1
404
- }
405
- },
406
- node,
407
- messageId: "noSpaceBetweenGenericAndParen",
408
- *fix(fixer) {
409
- yield fixer.replaceTextRange([spaceEndRange - preSpace.length, spaceEndRange], "");
410
- }
411
- });
412
- }
413
460
  }
414
461
  }
415
462
  };
416
463
  }
417
464
  });
418
465
 
419
- const RULE_NAME$5 = "no-space-before-paren";
466
+ const RULE_NAME$4 = "no-space-before-paren";
420
467
  const noSpaceBeforeParen = createEslintRule({
421
- name: RULE_NAME$5,
468
+ name: RULE_NAME$4,
422
469
  meta: {
423
470
  type: "layout",
424
471
  docs: {
@@ -460,8 +507,12 @@ const noSpaceBeforeParen = createEslintRule({
460
507
  const callerEnd = reactivity.ref(caller.range[1]);
461
508
  const textAfterCaller = reactivity.computed(() => text.slice(callerEnd.value));
462
509
  const parenStart = reactivity.ref(callerEnd.value + textAfterCaller.value.indexOf("("));
463
- const textBetweenFunctionNameAndParenRange = reactivity.computed(() => [callerEnd.value, parenStart.value]);
464
- const textBetweenFunctionNameAndParen = reactivity.computed(() => text.slice(...textBetweenFunctionNameAndParenRange.value));
510
+ const textBetweenFunctionNameAndParenRange = reactivity.computed(
511
+ () => [callerEnd.value, parenStart.value]
512
+ );
513
+ const textBetweenFunctionNameAndParen = reactivity.computed(
514
+ () => text.slice(...textBetweenFunctionNameAndParenRange.value)
515
+ );
465
516
  const hasGenerics = reactivity.computed(() => /^\s*</.test(textBetweenFunctionNameAndParen.value));
466
517
  const hasIndex = reactivity.computed(() => textBetweenFunctionNameAndParen.value.startsWith("]"));
467
518
  if (hasIndex.value) {
@@ -470,20 +521,28 @@ const noSpaceBeforeParen = createEslintRule({
470
521
  if (node.optional) {
471
522
  parenStart.value = callerEnd.value + textAfterCaller.value.indexOf("(");
472
523
  }
524
+ const spaces = /(\s*)$/.exec(textBetweenFunctionNameAndParen.value)[1];
473
525
  if (!hasGenerics.value) {
474
- if (textBetweenFunctionNameAndParen.value.length > 0 && textBetweenFunctionNameAndParen.value !== "?.") {
526
+ if (spaces.length > 0 && textBetweenFunctionNameAndParen.value !== "?.") {
527
+ const textBeforeSpaces = textBetweenFunctionNameAndParen.value.slice(
528
+ 0,
529
+ textBetweenFunctionNameAndParen.value.length - spaces.length
530
+ );
475
531
  context.report({
476
532
  node,
477
533
  messageId: "noSpaceBeforeParen",
478
534
  *fix(fixer) {
479
- yield fixer.replaceTextRange(textBetweenFunctionNameAndParenRange.value, node.optional ? "?." : "");
535
+ yield fixer.replaceTextRange(
536
+ textBetweenFunctionNameAndParenRange.value,
537
+ textBeforeSpaces + (node.optional ? "?." : "")
538
+ );
480
539
  }
481
540
  });
482
541
  }
483
542
  } else {
484
543
  const preSpaces = /^(\s*)/.exec(textBetweenFunctionNameAndParen.value)[1];
485
544
  const postSpaces = /(\s*)$/.exec(textBetweenFunctionNameAndParen.value)[1];
486
- const spacesBeforeOptionalMark = /(\s*)\?\./.exec(textBetweenFunctionNameAndParen.value)?.[1] || "";
545
+ const spacesBeforeOptionalMark = /(\s*)\?\./.exec(textBetweenFunctionNameAndParen.value)?.[1] ?? "";
487
546
  if (preSpaces.length > 0) {
488
547
  context.report({
489
548
  node,
@@ -555,65 +614,48 @@ const noSpaceBeforeParen = createEslintRule({
555
614
  }
556
615
  });
557
616
 
558
- const RULE_NAME$4 = "space-in-empty-block";
559
- const spaceInEmptyBlock = createEslintRule({
560
- name: RULE_NAME$4,
617
+ const RULE_NAME$3 = "semi-spacing";
618
+ const semiSpacing = createEslintRule({
619
+ name: RULE_NAME$3,
561
620
  meta: {
562
621
  type: "layout",
563
622
  docs: {
564
- description: "Disallow spaces in empty block",
623
+ description: "Semicolon spacing in types",
565
624
  recommended: "error"
566
625
  },
567
626
  fixable: "whitespace",
568
627
  schema: [],
569
628
  messages: {
570
- noSpaceInEmptyBlock: "Expected no space in empty block"
629
+ noSpaceBeforeSemi: "Expected no space before semicolon"
571
630
  }
572
631
  },
573
632
  defaultOptions: [],
574
633
  create: (context) => {
575
634
  const sourceCode = context.getSourceCode();
576
635
  return {
577
- BlockStatement: (node) => {
578
- if (node.body.length || sourceCode.getCommentsInside(node).length) {
636
+ TSTypeAliasDeclaration(node) {
637
+ const leftToken = node.typeAnnotation;
638
+ const rightToken = sourceCode.getTokenAfter(node.typeAnnotation);
639
+ if (!rightToken || rightToken.type !== utils.AST_TOKEN_TYPES.Punctuator || rightToken.value !== ";") {
579
640
  return;
580
641
  }
581
- const spaceStartRange = node.range[1] - 2;
582
- const post = sourceCode.text.slice(spaceStartRange);
583
- const postSpace = post.match(/^(\s*)/)?.[0];
584
- if (postSpace && postSpace.length) {
585
- context.report({
586
- loc: {
587
- start: node.loc.start,
588
- end: {
589
- line: node.loc.end.line,
590
- column: node.loc.end.column - 1 + postSpace.length
591
- }
592
- },
593
- messageId: "noSpaceInEmptyBlock",
594
- *fix(fixer) {
595
- yield fixer.replaceTextRange([node.range[0] + 1, spaceStartRange + postSpace.length], "");
596
- }
597
- });
598
- }
599
- const spaceEndRange = node.range[0] + 1;
600
- const pre = sourceCode.text.slice(0, spaceEndRange);
601
- const preSpace = pre.match(/(\s+)$/)?.[0];
602
- if (preSpace && preSpace.length) {
642
+ const hasSpacing = sourceCode.isSpaceBetween(leftToken, rightToken);
643
+ if (hasSpacing) {
603
644
  context.report({
604
645
  loc: {
605
646
  start: {
606
- line: node.loc.start.line,
607
- column: node.loc.start.column - preSpace.length
647
+ line: leftToken.loc.end.line,
648
+ column: leftToken.loc.end.column
608
649
  },
609
650
  end: {
610
- line: node.loc.start.line,
611
- column: node.loc.start.column
651
+ line: rightToken.loc.start.line,
652
+ column: rightToken.loc.start.column
612
653
  }
613
654
  },
614
- messageId: "noSpaceInEmptyBlock",
655
+ node,
656
+ messageId: "noSpaceBeforeSemi",
615
657
  *fix(fixer) {
616
- yield fixer.replaceTextRange([spaceEndRange - preSpace.length, spaceEndRange], "");
658
+ yield fixer.removeRange([leftToken.range[1], rightToken.range[0]]);
617
659
  }
618
660
  });
619
661
  }
@@ -622,9 +664,9 @@ const spaceInEmptyBlock = createEslintRule({
622
664
  }
623
665
  });
624
666
 
625
- const RULE_NAME$3 = "space-before-function-paren";
667
+ const RULE_NAME$2 = "space-before-function-paren";
626
668
  const spaceBeforeFunctionParen = createEslintRule({
627
- name: RULE_NAME$3,
669
+ name: RULE_NAME$2,
628
670
  meta: {
629
671
  type: "layout",
630
672
  docs: {
@@ -729,173 +771,146 @@ const spaceBeforeFunctionParen = createEslintRule({
729
771
  }
730
772
  });
731
773
 
732
- const RULE_NAME$2 = "semi-spacing";
733
- const semiSpacing = createEslintRule({
734
- name: RULE_NAME$2,
774
+ const operatorOrAnyBracketOrKeywordRE = /^(\||&|\*|\+|\-|\/|%|<|>|<=|>=|==|!=|===|!==|\[|\(|\{|as|extends|implements|keyof|new|readonly|typeof|unique|unknown)/;
775
+ const RULE_NAME$1 = "space-between-generic-and-paren";
776
+ const spaceBetweenGenericAndParen = createEslintRule({
777
+ name: RULE_NAME$1,
735
778
  meta: {
736
779
  type: "layout",
737
780
  docs: {
738
- description: "Semicolon spacing in types",
781
+ description: "Spaces between generic type parameters and paren",
739
782
  recommended: "error"
740
783
  },
741
784
  fixable: "whitespace",
742
785
  schema: [],
743
786
  messages: {
744
- noSpaceBeforeSemi: "Expected no space before semicolon"
787
+ noSpaceBetweenGenericAndParen: "Expected no space between generic type parameters and paren"
745
788
  }
746
789
  },
747
790
  defaultOptions: [],
748
791
  create: (context) => {
749
792
  const sourceCode = context.getSourceCode();
793
+ const text = sourceCode.text;
750
794
  return {
751
- TSTypeAliasDeclaration(node) {
752
- const leftToken = node.typeAnnotation;
753
- const rightToken = sourceCode.getTokenAfter(node.typeAnnotation);
754
- if (!rightToken || rightToken.type !== utils.AST_TOKEN_TYPES.Punctuator || rightToken.value !== ";") {
755
- return;
756
- }
757
- const hasSpacing = sourceCode.isSpaceBetween(leftToken, rightToken);
758
- if (hasSpacing) {
795
+ TSTypeParameter: (node) => {
796
+ const spaceStartRange = node.range[1] + 1;
797
+ const post = text.slice(spaceStartRange);
798
+ const postSpace = post.match(/^(\s*)/)?.[0];
799
+ const postEqual = post.slice(postSpace.length).match(/^(=)/)?.[0];
800
+ const postComma = text.slice(node.range[1]).match(/^(,)/)?.[0];
801
+ const postQuestionMark = text.slice(spaceStartRange + postSpace.length).match(/^(\?)/)?.[0];
802
+ const postOperatorOrAnyBracketOrKeyword = text.slice(spaceStartRange + postSpace.length).match(operatorOrAnyBracketOrKeywordRE)?.[0];
803
+ if (postSpace?.length && !postEqual && !postComma && !postQuestionMark && !postOperatorOrAnyBracketOrKeyword && node.parent.type !== utils.AST_NODE_TYPES.TSInferType) {
759
804
  context.report({
760
805
  loc: {
761
806
  start: {
762
- line: leftToken.loc.end.line,
763
- column: leftToken.loc.end.column
807
+ line: node.loc.end.line,
808
+ column: node.loc.end.column + 1
764
809
  },
765
810
  end: {
766
- line: rightToken.loc.start.line,
767
- column: rightToken.loc.start.column
811
+ line: node.loc.end.line,
812
+ column: node.loc.end.column + 1 + postSpace.length
768
813
  }
769
814
  },
770
815
  node,
771
- messageId: "noSpaceBeforeSemi",
816
+ messageId: "noSpaceBetweenGenericAndParen",
772
817
  *fix(fixer) {
773
- yield fixer.removeRange([leftToken.range[1], rightToken.range[0]]);
818
+ yield fixer.replaceTextRange([spaceStartRange, spaceStartRange + postSpace.length], "");
774
819
  }
775
820
  });
776
821
  }
822
+ if (node.parent?.parent.type === utils.AST_NODE_TYPES.FunctionDeclaration) {
823
+ const spaceEndRange = node.range[0] - 1;
824
+ const pre = sourceCode.text.slice(0, spaceEndRange);
825
+ const preSpace = pre.match(/(\s+)$/)?.[0];
826
+ if (preSpace?.length) {
827
+ context.report({
828
+ loc: {
829
+ start: {
830
+ line: node.loc.start.line,
831
+ column: node.loc.start.column - preSpace.length
832
+ },
833
+ end: {
834
+ line: node.loc.start.line,
835
+ column: node.loc.start.column - 1
836
+ }
837
+ },
838
+ node,
839
+ messageId: "noSpaceBetweenGenericAndParen",
840
+ *fix(fixer) {
841
+ yield fixer.replaceTextRange([spaceEndRange - preSpace.length, spaceEndRange], "");
842
+ }
843
+ });
844
+ }
845
+ }
777
846
  }
778
847
  };
779
848
  }
780
849
  });
781
850
 
782
- const RULE_NAME$1 = "no-inline-type-import";
783
- const noInlineTypeImport = createEslintRule({
784
- name: RULE_NAME$1,
851
+ const RULE_NAME = "space-in-empty-block";
852
+ const spaceInEmptyBlock = createEslintRule({
853
+ name: RULE_NAME,
785
854
  meta: {
786
855
  type: "layout",
787
856
  docs: {
788
- description: "Disallow inline type import",
857
+ description: "Disallow spaces in empty block",
789
858
  recommended: "error"
790
859
  },
791
- fixable: "code",
860
+ fixable: "whitespace",
792
861
  schema: [],
793
862
  messages: {
794
- noInlineTypeImport: "Expected no inline type import"
863
+ noSpaceInEmptyBlock: "Expected no space in empty block"
795
864
  }
796
865
  },
797
866
  defaultOptions: [],
798
867
  create: (context) => {
799
868
  const sourceCode = context.getSourceCode();
800
869
  return {
801
- ImportDeclaration: (node) => {
802
- const specifiers = node.specifiers;
803
- const typeSpecifiers = specifiers.filter((s) => s.type === "ImportSpecifier" && s.importKind === "type");
804
- const valueSpecifiers = specifiers.filter((s) => s.type === "ImportSpecifier" && s.importKind === "value");
805
- if (typeSpecifiers.length && valueSpecifiers.length) {
806
- context.report({
807
- loc: node.loc,
808
- messageId: "noInlineTypeImport",
809
- *fix(fixer) {
810
- const typeSpecifiersText = typeSpecifiers.map((s) => sourceCode.getText(s).replace("type ", "")).join(", ");
811
- const valueSpecifiersText = valueSpecifiers.map((s) => sourceCode.getText(s)).join(", ");
812
- yield fixer.replaceText(node, `import type { ${typeSpecifiersText} } from "${node.source.value}";
813
- import { ${valueSpecifiersText} } from "${node.source.value}";`);
814
- }
815
- });
816
- } else if (typeSpecifiers.length) {
870
+ BlockStatement: (node) => {
871
+ if (node.body.length || sourceCode.getCommentsInside(node).length) {
872
+ return;
873
+ }
874
+ const spaceStartRange = node.range[1] - 2;
875
+ const post = sourceCode.text.slice(spaceStartRange);
876
+ const postSpace = post.match(/^(\s*)/)?.[0];
877
+ if (postSpace?.length) {
817
878
  context.report({
818
- loc: node.loc,
819
- messageId: "noInlineTypeImport",
879
+ loc: {
880
+ start: node.loc.start,
881
+ end: {
882
+ line: node.loc.end.line,
883
+ column: node.loc.end.column - 1 + postSpace.length
884
+ }
885
+ },
886
+ messageId: "noSpaceInEmptyBlock",
820
887
  *fix(fixer) {
821
- const typeSpecifiersText = typeSpecifiers.map((s) => sourceCode.getText(s).replace("type ", "")).join(", ");
822
- yield fixer.replaceText(node, `import type { ${typeSpecifiersText} } from "${node.source.value}";`);
888
+ yield fixer.replaceTextRange([node.range[0] + 1, spaceStartRange + postSpace.length], "");
823
889
  }
824
890
  });
825
891
  }
826
- }
827
- };
828
- }
829
- });
830
-
831
- const RULE_NAME = "array-bracket-spacing";
832
- const arrayBracketSpacing = createEslintRule({
833
- name: RULE_NAME,
834
- meta: {
835
- type: "layout",
836
- docs: {
837
- description: "Array bracket spacing",
838
- recommended: "error"
839
- },
840
- fixable: "whitespace",
841
- schema: [],
842
- messages: {
843
- arrayBracketSpacing: "Array bracket spacing mismatch"
844
- }
845
- },
846
- defaultOptions: [],
847
- create: (context) => {
848
- const sourceCode = context.getSourceCode();
849
- const text = sourceCode.getText();
850
- const checkNode = (node) => {
851
- const elements = node.type === utils.AST_NODE_TYPES.TSTupleType ? node.elementTypes : node.elements;
852
- const firstElement = elements[0];
853
- const lastElement = elements[elements.length - 1];
854
- if (!firstElement) {
855
- return;
856
- }
857
- const leftToken = sourceCode.getTokenBefore(firstElement);
858
- const rightToken = reactivity.ref(sourceCode.getTokenAfter(lastElement));
859
- if (rightToken.value.value === ",") {
860
- rightToken.value = sourceCode.getTokenAfter(rightToken.value);
861
- }
862
- const textBetweenFirstAndToken = reactivity.computed(() => text.slice(leftToken.range[1], firstElement.range[0]));
863
- const isNewline = reactivity.computed(() => textBetweenFirstAndToken.value.includes("\n"));
864
- const textBetweenLastAndToken = reactivity.computed(() => text.slice(lastElement.range[1], rightToken.value.range[0]));
865
- const hasNewlineAfter = reactivity.computed(() => textBetweenLastAndToken.value.includes("\n"));
866
- if (sourceCode.isSpaceBetween(leftToken, firstElement) && !isNewline.value) {
867
- context.report({
868
- node,
869
- messageId: "arrayBracketSpacing",
870
- *fix(fixer) {
871
- yield fixer.removeRange([leftToken.range[1], firstElement.range[0]]);
872
- }
873
- });
874
- }
875
- if (sourceCode.isSpaceBetween(lastElement, rightToken.value)) {
876
- if (!isNewline.value) {
892
+ const spaceEndRange = node.range[0] + 1;
893
+ const pre = sourceCode.text.slice(0, spaceEndRange);
894
+ const preSpace = pre.match(/(\s+)$/)?.[0];
895
+ if (preSpace?.length) {
877
896
  context.report({
878
- node,
879
- messageId: "arrayBracketSpacing",
897
+ loc: {
898
+ start: {
899
+ line: node.loc.start.line,
900
+ column: node.loc.start.column - preSpace.length
901
+ },
902
+ end: {
903
+ line: node.loc.start.line,
904
+ column: node.loc.start.column
905
+ }
906
+ },
907
+ messageId: "noSpaceInEmptyBlock",
880
908
  *fix(fixer) {
881
- yield fixer.removeRange([lastElement.range[1], rightToken.value.range[0]]);
909
+ yield fixer.replaceTextRange([spaceEndRange - preSpace.length, spaceEndRange], "");
882
910
  }
883
911
  });
884
912
  }
885
913
  }
886
- if (isNewline.value && !hasNewlineAfter.value) {
887
- context.report({
888
- node,
889
- messageId: "arrayBracketSpacing",
890
- *fix(fixer) {
891
- yield fixer.replaceTextRange([lastElement.range[1], rightToken.value.range[0]], "\n");
892
- }
893
- });
894
- }
895
- };
896
- return {
897
- TSTupleType: checkNode,
898
- ArrayExpression: checkNode
899
914
  };
900
915
  }
901
916
  });
package/dist/index.mjs CHANGED
@@ -1,12 +1,84 @@
1
1
  import { ESLintUtils, AST_NODE_TYPES, AST_TOKEN_TYPES } from '@typescript-eslint/utils';
2
- import * as util from '@typescript-eslint/utils/dist/ast-utils';
3
2
  import { ref, computed } from '@vue/reactivity';
3
+ import * as util from '@typescript-eslint/utils/dist/ast-utils';
4
4
 
5
5
  const createEslintRule = ESLintUtils.RuleCreator((ruleName) => ruleName);
6
6
 
7
- const RULE_NAME$8 = "generic-spacing";
8
- const genericSpacing = createEslintRule({
7
+ const RULE_NAME$8 = "array-bracket-spacing";
8
+ const arrayBracketSpacing = createEslintRule({
9
9
  name: RULE_NAME$8,
10
+ meta: {
11
+ type: "layout",
12
+ docs: {
13
+ description: "Array bracket spacing",
14
+ recommended: "error"
15
+ },
16
+ fixable: "whitespace",
17
+ schema: [],
18
+ messages: {
19
+ arrayBracketSpacing: "Array bracket spacing mismatch"
20
+ }
21
+ },
22
+ defaultOptions: [],
23
+ create: (context) => {
24
+ const sourceCode = context.getSourceCode();
25
+ const text = sourceCode.getText();
26
+ const checkNode = (node) => {
27
+ const elements = node.type === AST_NODE_TYPES.TSTupleType ? node.elementTypes : node.elements;
28
+ const firstElement = elements[0];
29
+ const lastElement = elements[elements.length - 1];
30
+ if (!firstElement) {
31
+ return;
32
+ }
33
+ const leftToken = sourceCode.getTokenBefore(firstElement);
34
+ const rightToken = ref(sourceCode.getTokenAfter(lastElement));
35
+ if (rightToken.value.value === ",") {
36
+ rightToken.value = sourceCode.getTokenAfter(rightToken.value);
37
+ }
38
+ const textBetweenFirstAndToken = computed(() => text.slice(leftToken.range[1], firstElement.range[0]));
39
+ const isNewline = computed(() => textBetweenFirstAndToken.value.includes("\n"));
40
+ const textBetweenLastAndToken = computed(() => text.slice(lastElement.range[1], rightToken.value.range[0]));
41
+ const hasNewlineAfter = computed(() => textBetweenLastAndToken.value.includes("\n"));
42
+ if (sourceCode.isSpaceBetween(leftToken, firstElement) && !isNewline.value) {
43
+ context.report({
44
+ node,
45
+ messageId: "arrayBracketSpacing",
46
+ *fix(fixer) {
47
+ yield fixer.removeRange([leftToken.range[1], firstElement.range[0]]);
48
+ }
49
+ });
50
+ }
51
+ if (sourceCode.isSpaceBetween(lastElement, rightToken.value)) {
52
+ if (!isNewline.value) {
53
+ context.report({
54
+ node,
55
+ messageId: "arrayBracketSpacing",
56
+ *fix(fixer) {
57
+ yield fixer.removeRange([lastElement.range[1], rightToken.value.range[0]]);
58
+ }
59
+ });
60
+ }
61
+ }
62
+ if (isNewline.value && !hasNewlineAfter.value) {
63
+ context.report({
64
+ node,
65
+ messageId: "arrayBracketSpacing",
66
+ *fix(fixer) {
67
+ yield fixer.replaceTextRange([lastElement.range[1], rightToken.value.range[0]], "\n");
68
+ }
69
+ });
70
+ }
71
+ };
72
+ return {
73
+ TSTupleType: checkNode,
74
+ ArrayExpression: checkNode
75
+ };
76
+ }
77
+ });
78
+
79
+ const RULE_NAME$7 = "generic-spacing";
80
+ const genericSpacing = createEslintRule({
81
+ name: RULE_NAME$7,
10
82
  meta: {
11
83
  type: "layout",
12
84
  docs: {
@@ -144,7 +216,7 @@ const genericSpacing = createEslintRule({
144
216
  if (!node.default) {
145
217
  return;
146
218
  }
147
- const endNode = node.constraint || node.name;
219
+ const endNode = node.constraint ?? node.name;
148
220
  const from = endNode.range[1];
149
221
  const to = node.default.range[0];
150
222
  const spaceAndEqual = sourceCode.text.slice(from, to);
@@ -274,9 +346,9 @@ const genericSpacing = createEslintRule({
274
346
  }
275
347
  });
276
348
 
277
- const RULE_NAME$7 = "import-dedupe";
349
+ const RULE_NAME$6 = "import-dedupe";
278
350
  const importDedupe = createEslintRule({
279
- name: RULE_NAME$7,
351
+ name: RULE_NAME$6,
280
352
  meta: {
281
353
  type: "problem",
282
354
  docs: {
@@ -324,86 +396,61 @@ const importDedupe = createEslintRule({
324
396
  }
325
397
  });
326
398
 
327
- const operatorOrAnyBracketOrKeywordRE = /^(\||&|\*|\+|\-|\/|%|<|>|<=|>=|==|!=|===|!==|\[|\(|\{|as|extends|implements|keyof|new|readonly|typeof|unique|unknown)/;
328
- const RULE_NAME$6 = "space-between-generic-and-paren";
329
- const spaceBetweenGenericAndParen = createEslintRule({
330
- name: RULE_NAME$6,
399
+ const RULE_NAME$5 = "no-inline-type-import";
400
+ const noInlineTypeImport = createEslintRule({
401
+ name: RULE_NAME$5,
331
402
  meta: {
332
403
  type: "layout",
333
404
  docs: {
334
- description: "Spaces between generic type parameters and paren",
405
+ description: "Disallow inline type import",
335
406
  recommended: "error"
336
407
  },
337
- fixable: "whitespace",
408
+ fixable: "code",
338
409
  schema: [],
339
410
  messages: {
340
- noSpaceBetweenGenericAndParen: "Expected no space between generic type parameters and paren"
411
+ noInlineTypeImport: "Expected no inline type import"
341
412
  }
342
413
  },
343
414
  defaultOptions: [],
344
415
  create: (context) => {
345
416
  const sourceCode = context.getSourceCode();
346
- const text = sourceCode.text;
347
417
  return {
348
- TSTypeParameter: (node) => {
349
- const spaceStartRange = node.range[1] + 1;
350
- const post = text.slice(spaceStartRange);
351
- const postSpace = post.match(/^(\s*)/)?.[0];
352
- const postEqual = post.slice(postSpace.length).match(/^(=)/)?.[0];
353
- const postComma = text.slice(node.range[1]).match(/^(,)/)?.[0];
354
- const postQuestionMark = text.slice(spaceStartRange + postSpace.length).match(/^(\?)/)?.[0];
355
- const postOperatorOrAnyBracketOrKeyword = text.slice(spaceStartRange + postSpace.length).match(operatorOrAnyBracketOrKeywordRE)?.[0];
356
- if (postSpace && postSpace.length && !postEqual && !postComma && !postQuestionMark && !postOperatorOrAnyBracketOrKeyword && node.parent.type !== AST_NODE_TYPES.TSInferType) {
418
+ ImportDeclaration: (node) => {
419
+ const specifiers = node.specifiers;
420
+ const typeSpecifiers = specifiers.filter((s) => s.type === "ImportSpecifier" && s.importKind === "type");
421
+ const valueSpecifiers = specifiers.filter((s) => s.type === "ImportSpecifier" && s.importKind === "value");
422
+ if (typeSpecifiers.length && valueSpecifiers.length) {
357
423
  context.report({
358
- loc: {
359
- start: {
360
- line: node.loc.end.line,
361
- column: node.loc.end.column + 1
362
- },
363
- end: {
364
- line: node.loc.end.line,
365
- column: node.loc.end.column + 1 + postSpace.length
366
- }
367
- },
368
- node,
369
- messageId: "noSpaceBetweenGenericAndParen",
424
+ loc: node.loc,
425
+ messageId: "noInlineTypeImport",
370
426
  *fix(fixer) {
371
- yield fixer.replaceTextRange([spaceStartRange, spaceStartRange + postSpace.length], "");
427
+ const typeSpecifiersText = typeSpecifiers.map((s) => sourceCode.getText(s).replace("type ", "")).join(", ");
428
+ const valueSpecifiersText = valueSpecifiers.map((s) => sourceCode.getText(s)).join(", ");
429
+ yield fixer.replaceText(
430
+ node,
431
+ `import type { ${typeSpecifiersText} } from "${node.source.value}";
432
+ import { ${valueSpecifiersText} } from "${node.source.value}";`
433
+ );
434
+ }
435
+ });
436
+ } else if (typeSpecifiers.length) {
437
+ context.report({
438
+ loc: node.loc,
439
+ messageId: "noInlineTypeImport",
440
+ *fix(fixer) {
441
+ const typeSpecifiersText = typeSpecifiers.map((s) => sourceCode.getText(s).replace("type ", "")).join(", ");
442
+ yield fixer.replaceText(node, `import type { ${typeSpecifiersText} } from "${node.source.value}";`);
372
443
  }
373
444
  });
374
- }
375
- if (node.parent?.parent.type === AST_NODE_TYPES.FunctionDeclaration) {
376
- const spaceEndRange = node.range[0] - 1;
377
- const pre = sourceCode.text.slice(0, spaceEndRange);
378
- const preSpace = pre.match(/(\s+)$/)?.[0];
379
- if (preSpace && preSpace.length) {
380
- context.report({
381
- loc: {
382
- start: {
383
- line: node.loc.start.line,
384
- column: node.loc.start.column - preSpace.length
385
- },
386
- end: {
387
- line: node.loc.start.line,
388
- column: node.loc.start.column - 1
389
- }
390
- },
391
- node,
392
- messageId: "noSpaceBetweenGenericAndParen",
393
- *fix(fixer) {
394
- yield fixer.replaceTextRange([spaceEndRange - preSpace.length, spaceEndRange], "");
395
- }
396
- });
397
- }
398
445
  }
399
446
  }
400
447
  };
401
448
  }
402
449
  });
403
450
 
404
- const RULE_NAME$5 = "no-space-before-paren";
451
+ const RULE_NAME$4 = "no-space-before-paren";
405
452
  const noSpaceBeforeParen = createEslintRule({
406
- name: RULE_NAME$5,
453
+ name: RULE_NAME$4,
407
454
  meta: {
408
455
  type: "layout",
409
456
  docs: {
@@ -445,8 +492,12 @@ const noSpaceBeforeParen = createEslintRule({
445
492
  const callerEnd = ref(caller.range[1]);
446
493
  const textAfterCaller = computed(() => text.slice(callerEnd.value));
447
494
  const parenStart = ref(callerEnd.value + textAfterCaller.value.indexOf("("));
448
- const textBetweenFunctionNameAndParenRange = computed(() => [callerEnd.value, parenStart.value]);
449
- const textBetweenFunctionNameAndParen = computed(() => text.slice(...textBetweenFunctionNameAndParenRange.value));
495
+ const textBetweenFunctionNameAndParenRange = computed(
496
+ () => [callerEnd.value, parenStart.value]
497
+ );
498
+ const textBetweenFunctionNameAndParen = computed(
499
+ () => text.slice(...textBetweenFunctionNameAndParenRange.value)
500
+ );
450
501
  const hasGenerics = computed(() => /^\s*</.test(textBetweenFunctionNameAndParen.value));
451
502
  const hasIndex = computed(() => textBetweenFunctionNameAndParen.value.startsWith("]"));
452
503
  if (hasIndex.value) {
@@ -455,20 +506,28 @@ const noSpaceBeforeParen = createEslintRule({
455
506
  if (node.optional) {
456
507
  parenStart.value = callerEnd.value + textAfterCaller.value.indexOf("(");
457
508
  }
509
+ const spaces = /(\s*)$/.exec(textBetweenFunctionNameAndParen.value)[1];
458
510
  if (!hasGenerics.value) {
459
- if (textBetweenFunctionNameAndParen.value.length > 0 && textBetweenFunctionNameAndParen.value !== "?.") {
511
+ if (spaces.length > 0 && textBetweenFunctionNameAndParen.value !== "?.") {
512
+ const textBeforeSpaces = textBetweenFunctionNameAndParen.value.slice(
513
+ 0,
514
+ textBetweenFunctionNameAndParen.value.length - spaces.length
515
+ );
460
516
  context.report({
461
517
  node,
462
518
  messageId: "noSpaceBeforeParen",
463
519
  *fix(fixer) {
464
- yield fixer.replaceTextRange(textBetweenFunctionNameAndParenRange.value, node.optional ? "?." : "");
520
+ yield fixer.replaceTextRange(
521
+ textBetweenFunctionNameAndParenRange.value,
522
+ textBeforeSpaces + (node.optional ? "?." : "")
523
+ );
465
524
  }
466
525
  });
467
526
  }
468
527
  } else {
469
528
  const preSpaces = /^(\s*)/.exec(textBetweenFunctionNameAndParen.value)[1];
470
529
  const postSpaces = /(\s*)$/.exec(textBetweenFunctionNameAndParen.value)[1];
471
- const spacesBeforeOptionalMark = /(\s*)\?\./.exec(textBetweenFunctionNameAndParen.value)?.[1] || "";
530
+ const spacesBeforeOptionalMark = /(\s*)\?\./.exec(textBetweenFunctionNameAndParen.value)?.[1] ?? "";
472
531
  if (preSpaces.length > 0) {
473
532
  context.report({
474
533
  node,
@@ -540,65 +599,48 @@ const noSpaceBeforeParen = createEslintRule({
540
599
  }
541
600
  });
542
601
 
543
- const RULE_NAME$4 = "space-in-empty-block";
544
- const spaceInEmptyBlock = createEslintRule({
545
- name: RULE_NAME$4,
602
+ const RULE_NAME$3 = "semi-spacing";
603
+ const semiSpacing = createEslintRule({
604
+ name: RULE_NAME$3,
546
605
  meta: {
547
606
  type: "layout",
548
607
  docs: {
549
- description: "Disallow spaces in empty block",
608
+ description: "Semicolon spacing in types",
550
609
  recommended: "error"
551
610
  },
552
611
  fixable: "whitespace",
553
612
  schema: [],
554
613
  messages: {
555
- noSpaceInEmptyBlock: "Expected no space in empty block"
614
+ noSpaceBeforeSemi: "Expected no space before semicolon"
556
615
  }
557
616
  },
558
617
  defaultOptions: [],
559
618
  create: (context) => {
560
619
  const sourceCode = context.getSourceCode();
561
620
  return {
562
- BlockStatement: (node) => {
563
- if (node.body.length || sourceCode.getCommentsInside(node).length) {
621
+ TSTypeAliasDeclaration(node) {
622
+ const leftToken = node.typeAnnotation;
623
+ const rightToken = sourceCode.getTokenAfter(node.typeAnnotation);
624
+ if (!rightToken || rightToken.type !== AST_TOKEN_TYPES.Punctuator || rightToken.value !== ";") {
564
625
  return;
565
626
  }
566
- const spaceStartRange = node.range[1] - 2;
567
- const post = sourceCode.text.slice(spaceStartRange);
568
- const postSpace = post.match(/^(\s*)/)?.[0];
569
- if (postSpace && postSpace.length) {
570
- context.report({
571
- loc: {
572
- start: node.loc.start,
573
- end: {
574
- line: node.loc.end.line,
575
- column: node.loc.end.column - 1 + postSpace.length
576
- }
577
- },
578
- messageId: "noSpaceInEmptyBlock",
579
- *fix(fixer) {
580
- yield fixer.replaceTextRange([node.range[0] + 1, spaceStartRange + postSpace.length], "");
581
- }
582
- });
583
- }
584
- const spaceEndRange = node.range[0] + 1;
585
- const pre = sourceCode.text.slice(0, spaceEndRange);
586
- const preSpace = pre.match(/(\s+)$/)?.[0];
587
- if (preSpace && preSpace.length) {
627
+ const hasSpacing = sourceCode.isSpaceBetween(leftToken, rightToken);
628
+ if (hasSpacing) {
588
629
  context.report({
589
630
  loc: {
590
631
  start: {
591
- line: node.loc.start.line,
592
- column: node.loc.start.column - preSpace.length
632
+ line: leftToken.loc.end.line,
633
+ column: leftToken.loc.end.column
593
634
  },
594
635
  end: {
595
- line: node.loc.start.line,
596
- column: node.loc.start.column
636
+ line: rightToken.loc.start.line,
637
+ column: rightToken.loc.start.column
597
638
  }
598
639
  },
599
- messageId: "noSpaceInEmptyBlock",
640
+ node,
641
+ messageId: "noSpaceBeforeSemi",
600
642
  *fix(fixer) {
601
- yield fixer.replaceTextRange([spaceEndRange - preSpace.length, spaceEndRange], "");
643
+ yield fixer.removeRange([leftToken.range[1], rightToken.range[0]]);
602
644
  }
603
645
  });
604
646
  }
@@ -607,9 +649,9 @@ const spaceInEmptyBlock = createEslintRule({
607
649
  }
608
650
  });
609
651
 
610
- const RULE_NAME$3 = "space-before-function-paren";
652
+ const RULE_NAME$2 = "space-before-function-paren";
611
653
  const spaceBeforeFunctionParen = createEslintRule({
612
- name: RULE_NAME$3,
654
+ name: RULE_NAME$2,
613
655
  meta: {
614
656
  type: "layout",
615
657
  docs: {
@@ -714,173 +756,146 @@ const spaceBeforeFunctionParen = createEslintRule({
714
756
  }
715
757
  });
716
758
 
717
- const RULE_NAME$2 = "semi-spacing";
718
- const semiSpacing = createEslintRule({
719
- name: RULE_NAME$2,
759
+ const operatorOrAnyBracketOrKeywordRE = /^(\||&|\*|\+|\-|\/|%|<|>|<=|>=|==|!=|===|!==|\[|\(|\{|as|extends|implements|keyof|new|readonly|typeof|unique|unknown)/;
760
+ const RULE_NAME$1 = "space-between-generic-and-paren";
761
+ const spaceBetweenGenericAndParen = createEslintRule({
762
+ name: RULE_NAME$1,
720
763
  meta: {
721
764
  type: "layout",
722
765
  docs: {
723
- description: "Semicolon spacing in types",
766
+ description: "Spaces between generic type parameters and paren",
724
767
  recommended: "error"
725
768
  },
726
769
  fixable: "whitespace",
727
770
  schema: [],
728
771
  messages: {
729
- noSpaceBeforeSemi: "Expected no space before semicolon"
772
+ noSpaceBetweenGenericAndParen: "Expected no space between generic type parameters and paren"
730
773
  }
731
774
  },
732
775
  defaultOptions: [],
733
776
  create: (context) => {
734
777
  const sourceCode = context.getSourceCode();
778
+ const text = sourceCode.text;
735
779
  return {
736
- TSTypeAliasDeclaration(node) {
737
- const leftToken = node.typeAnnotation;
738
- const rightToken = sourceCode.getTokenAfter(node.typeAnnotation);
739
- if (!rightToken || rightToken.type !== AST_TOKEN_TYPES.Punctuator || rightToken.value !== ";") {
740
- return;
741
- }
742
- const hasSpacing = sourceCode.isSpaceBetween(leftToken, rightToken);
743
- if (hasSpacing) {
780
+ TSTypeParameter: (node) => {
781
+ const spaceStartRange = node.range[1] + 1;
782
+ const post = text.slice(spaceStartRange);
783
+ const postSpace = post.match(/^(\s*)/)?.[0];
784
+ const postEqual = post.slice(postSpace.length).match(/^(=)/)?.[0];
785
+ const postComma = text.slice(node.range[1]).match(/^(,)/)?.[0];
786
+ const postQuestionMark = text.slice(spaceStartRange + postSpace.length).match(/^(\?)/)?.[0];
787
+ const postOperatorOrAnyBracketOrKeyword = text.slice(spaceStartRange + postSpace.length).match(operatorOrAnyBracketOrKeywordRE)?.[0];
788
+ if (postSpace?.length && !postEqual && !postComma && !postQuestionMark && !postOperatorOrAnyBracketOrKeyword && node.parent.type !== AST_NODE_TYPES.TSInferType) {
744
789
  context.report({
745
790
  loc: {
746
791
  start: {
747
- line: leftToken.loc.end.line,
748
- column: leftToken.loc.end.column
792
+ line: node.loc.end.line,
793
+ column: node.loc.end.column + 1
749
794
  },
750
795
  end: {
751
- line: rightToken.loc.start.line,
752
- column: rightToken.loc.start.column
796
+ line: node.loc.end.line,
797
+ column: node.loc.end.column + 1 + postSpace.length
753
798
  }
754
799
  },
755
800
  node,
756
- messageId: "noSpaceBeforeSemi",
801
+ messageId: "noSpaceBetweenGenericAndParen",
757
802
  *fix(fixer) {
758
- yield fixer.removeRange([leftToken.range[1], rightToken.range[0]]);
803
+ yield fixer.replaceTextRange([spaceStartRange, spaceStartRange + postSpace.length], "");
759
804
  }
760
805
  });
761
806
  }
807
+ if (node.parent?.parent.type === AST_NODE_TYPES.FunctionDeclaration) {
808
+ const spaceEndRange = node.range[0] - 1;
809
+ const pre = sourceCode.text.slice(0, spaceEndRange);
810
+ const preSpace = pre.match(/(\s+)$/)?.[0];
811
+ if (preSpace?.length) {
812
+ context.report({
813
+ loc: {
814
+ start: {
815
+ line: node.loc.start.line,
816
+ column: node.loc.start.column - preSpace.length
817
+ },
818
+ end: {
819
+ line: node.loc.start.line,
820
+ column: node.loc.start.column - 1
821
+ }
822
+ },
823
+ node,
824
+ messageId: "noSpaceBetweenGenericAndParen",
825
+ *fix(fixer) {
826
+ yield fixer.replaceTextRange([spaceEndRange - preSpace.length, spaceEndRange], "");
827
+ }
828
+ });
829
+ }
830
+ }
762
831
  }
763
832
  };
764
833
  }
765
834
  });
766
835
 
767
- const RULE_NAME$1 = "no-inline-type-import";
768
- const noInlineTypeImport = createEslintRule({
769
- name: RULE_NAME$1,
836
+ const RULE_NAME = "space-in-empty-block";
837
+ const spaceInEmptyBlock = createEslintRule({
838
+ name: RULE_NAME,
770
839
  meta: {
771
840
  type: "layout",
772
841
  docs: {
773
- description: "Disallow inline type import",
842
+ description: "Disallow spaces in empty block",
774
843
  recommended: "error"
775
844
  },
776
- fixable: "code",
845
+ fixable: "whitespace",
777
846
  schema: [],
778
847
  messages: {
779
- noInlineTypeImport: "Expected no inline type import"
848
+ noSpaceInEmptyBlock: "Expected no space in empty block"
780
849
  }
781
850
  },
782
851
  defaultOptions: [],
783
852
  create: (context) => {
784
853
  const sourceCode = context.getSourceCode();
785
854
  return {
786
- ImportDeclaration: (node) => {
787
- const specifiers = node.specifiers;
788
- const typeSpecifiers = specifiers.filter((s) => s.type === "ImportSpecifier" && s.importKind === "type");
789
- const valueSpecifiers = specifiers.filter((s) => s.type === "ImportSpecifier" && s.importKind === "value");
790
- if (typeSpecifiers.length && valueSpecifiers.length) {
791
- context.report({
792
- loc: node.loc,
793
- messageId: "noInlineTypeImport",
794
- *fix(fixer) {
795
- const typeSpecifiersText = typeSpecifiers.map((s) => sourceCode.getText(s).replace("type ", "")).join(", ");
796
- const valueSpecifiersText = valueSpecifiers.map((s) => sourceCode.getText(s)).join(", ");
797
- yield fixer.replaceText(node, `import type { ${typeSpecifiersText} } from "${node.source.value}";
798
- import { ${valueSpecifiersText} } from "${node.source.value}";`);
799
- }
800
- });
801
- } else if (typeSpecifiers.length) {
855
+ BlockStatement: (node) => {
856
+ if (node.body.length || sourceCode.getCommentsInside(node).length) {
857
+ return;
858
+ }
859
+ const spaceStartRange = node.range[1] - 2;
860
+ const post = sourceCode.text.slice(spaceStartRange);
861
+ const postSpace = post.match(/^(\s*)/)?.[0];
862
+ if (postSpace?.length) {
802
863
  context.report({
803
- loc: node.loc,
804
- messageId: "noInlineTypeImport",
864
+ loc: {
865
+ start: node.loc.start,
866
+ end: {
867
+ line: node.loc.end.line,
868
+ column: node.loc.end.column - 1 + postSpace.length
869
+ }
870
+ },
871
+ messageId: "noSpaceInEmptyBlock",
805
872
  *fix(fixer) {
806
- const typeSpecifiersText = typeSpecifiers.map((s) => sourceCode.getText(s).replace("type ", "")).join(", ");
807
- yield fixer.replaceText(node, `import type { ${typeSpecifiersText} } from "${node.source.value}";`);
873
+ yield fixer.replaceTextRange([node.range[0] + 1, spaceStartRange + postSpace.length], "");
808
874
  }
809
875
  });
810
876
  }
811
- }
812
- };
813
- }
814
- });
815
-
816
- const RULE_NAME = "array-bracket-spacing";
817
- const arrayBracketSpacing = createEslintRule({
818
- name: RULE_NAME,
819
- meta: {
820
- type: "layout",
821
- docs: {
822
- description: "Array bracket spacing",
823
- recommended: "error"
824
- },
825
- fixable: "whitespace",
826
- schema: [],
827
- messages: {
828
- arrayBracketSpacing: "Array bracket spacing mismatch"
829
- }
830
- },
831
- defaultOptions: [],
832
- create: (context) => {
833
- const sourceCode = context.getSourceCode();
834
- const text = sourceCode.getText();
835
- const checkNode = (node) => {
836
- const elements = node.type === AST_NODE_TYPES.TSTupleType ? node.elementTypes : node.elements;
837
- const firstElement = elements[0];
838
- const lastElement = elements[elements.length - 1];
839
- if (!firstElement) {
840
- return;
841
- }
842
- const leftToken = sourceCode.getTokenBefore(firstElement);
843
- const rightToken = ref(sourceCode.getTokenAfter(lastElement));
844
- if (rightToken.value.value === ",") {
845
- rightToken.value = sourceCode.getTokenAfter(rightToken.value);
846
- }
847
- const textBetweenFirstAndToken = computed(() => text.slice(leftToken.range[1], firstElement.range[0]));
848
- const isNewline = computed(() => textBetweenFirstAndToken.value.includes("\n"));
849
- const textBetweenLastAndToken = computed(() => text.slice(lastElement.range[1], rightToken.value.range[0]));
850
- const hasNewlineAfter = computed(() => textBetweenLastAndToken.value.includes("\n"));
851
- if (sourceCode.isSpaceBetween(leftToken, firstElement) && !isNewline.value) {
852
- context.report({
853
- node,
854
- messageId: "arrayBracketSpacing",
855
- *fix(fixer) {
856
- yield fixer.removeRange([leftToken.range[1], firstElement.range[0]]);
857
- }
858
- });
859
- }
860
- if (sourceCode.isSpaceBetween(lastElement, rightToken.value)) {
861
- if (!isNewline.value) {
877
+ const spaceEndRange = node.range[0] + 1;
878
+ const pre = sourceCode.text.slice(0, spaceEndRange);
879
+ const preSpace = pre.match(/(\s+)$/)?.[0];
880
+ if (preSpace?.length) {
862
881
  context.report({
863
- node,
864
- messageId: "arrayBracketSpacing",
882
+ loc: {
883
+ start: {
884
+ line: node.loc.start.line,
885
+ column: node.loc.start.column - preSpace.length
886
+ },
887
+ end: {
888
+ line: node.loc.start.line,
889
+ column: node.loc.start.column
890
+ }
891
+ },
892
+ messageId: "noSpaceInEmptyBlock",
865
893
  *fix(fixer) {
866
- yield fixer.removeRange([lastElement.range[1], rightToken.value.range[0]]);
894
+ yield fixer.replaceTextRange([spaceEndRange - preSpace.length, spaceEndRange], "");
867
895
  }
868
896
  });
869
897
  }
870
898
  }
871
- if (isNewline.value && !hasNewlineAfter.value) {
872
- context.report({
873
- node,
874
- messageId: "arrayBracketSpacing",
875
- *fix(fixer) {
876
- yield fixer.replaceTextRange([lastElement.range[1], rightToken.value.range[0]], "\n");
877
- }
878
- });
879
- }
880
- };
881
- return {
882
- TSTupleType: checkNode,
883
- ArrayExpression: checkNode
884
899
  };
885
900
  }
886
901
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@so1ve/eslint-plugin",
3
- "version": "0.47.3",
3
+ "version": "0.50.0",
4
4
  "author": "Anthony Fu <anthonyfu117@hotmail.com> (https://github.com/antfu/)",
5
5
  "contributors": [
6
6
  {