@so1ve/eslint-plugin 0.48.0 → 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 +229 -216
  2. package/dist/index.mjs +229 -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) {
@@ -473,19 +524,25 @@ const noSpaceBeforeParen = createEslintRule({
473
524
  const spaces = /(\s*)$/.exec(textBetweenFunctionNameAndParen.value)[1];
474
525
  if (!hasGenerics.value) {
475
526
  if (spaces.length > 0 && textBetweenFunctionNameAndParen.value !== "?.") {
476
- const textBeforeSpaces = textBetweenFunctionNameAndParen.value.slice(0, textBetweenFunctionNameAndParen.value.length - spaces.length);
527
+ const textBeforeSpaces = textBetweenFunctionNameAndParen.value.slice(
528
+ 0,
529
+ textBetweenFunctionNameAndParen.value.length - spaces.length
530
+ );
477
531
  context.report({
478
532
  node,
479
533
  messageId: "noSpaceBeforeParen",
480
534
  *fix(fixer) {
481
- yield fixer.replaceTextRange(textBetweenFunctionNameAndParenRange.value, textBeforeSpaces + (node.optional ? "?." : ""));
535
+ yield fixer.replaceTextRange(
536
+ textBetweenFunctionNameAndParenRange.value,
537
+ textBeforeSpaces + (node.optional ? "?." : "")
538
+ );
482
539
  }
483
540
  });
484
541
  }
485
542
  } else {
486
543
  const preSpaces = /^(\s*)/.exec(textBetweenFunctionNameAndParen.value)[1];
487
544
  const postSpaces = /(\s*)$/.exec(textBetweenFunctionNameAndParen.value)[1];
488
- const spacesBeforeOptionalMark = /(\s*)\?\./.exec(textBetweenFunctionNameAndParen.value)?.[1] || "";
545
+ const spacesBeforeOptionalMark = /(\s*)\?\./.exec(textBetweenFunctionNameAndParen.value)?.[1] ?? "";
489
546
  if (preSpaces.length > 0) {
490
547
  context.report({
491
548
  node,
@@ -557,65 +614,48 @@ const noSpaceBeforeParen = createEslintRule({
557
614
  }
558
615
  });
559
616
 
560
- const RULE_NAME$4 = "space-in-empty-block";
561
- const spaceInEmptyBlock = createEslintRule({
562
- name: RULE_NAME$4,
617
+ const RULE_NAME$3 = "semi-spacing";
618
+ const semiSpacing = createEslintRule({
619
+ name: RULE_NAME$3,
563
620
  meta: {
564
621
  type: "layout",
565
622
  docs: {
566
- description: "Disallow spaces in empty block",
623
+ description: "Semicolon spacing in types",
567
624
  recommended: "error"
568
625
  },
569
626
  fixable: "whitespace",
570
627
  schema: [],
571
628
  messages: {
572
- noSpaceInEmptyBlock: "Expected no space in empty block"
629
+ noSpaceBeforeSemi: "Expected no space before semicolon"
573
630
  }
574
631
  },
575
632
  defaultOptions: [],
576
633
  create: (context) => {
577
634
  const sourceCode = context.getSourceCode();
578
635
  return {
579
- BlockStatement: (node) => {
580
- 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 !== ";") {
581
640
  return;
582
641
  }
583
- const spaceStartRange = node.range[1] - 2;
584
- const post = sourceCode.text.slice(spaceStartRange);
585
- const postSpace = post.match(/^(\s*)/)?.[0];
586
- if (postSpace && postSpace.length) {
587
- context.report({
588
- loc: {
589
- start: node.loc.start,
590
- end: {
591
- line: node.loc.end.line,
592
- column: node.loc.end.column - 1 + postSpace.length
593
- }
594
- },
595
- messageId: "noSpaceInEmptyBlock",
596
- *fix(fixer) {
597
- yield fixer.replaceTextRange([node.range[0] + 1, spaceStartRange + postSpace.length], "");
598
- }
599
- });
600
- }
601
- const spaceEndRange = node.range[0] + 1;
602
- const pre = sourceCode.text.slice(0, spaceEndRange);
603
- const preSpace = pre.match(/(\s+)$/)?.[0];
604
- if (preSpace && preSpace.length) {
642
+ const hasSpacing = sourceCode.isSpaceBetween(leftToken, rightToken);
643
+ if (hasSpacing) {
605
644
  context.report({
606
645
  loc: {
607
646
  start: {
608
- line: node.loc.start.line,
609
- column: node.loc.start.column - preSpace.length
647
+ line: leftToken.loc.end.line,
648
+ column: leftToken.loc.end.column
610
649
  },
611
650
  end: {
612
- line: node.loc.start.line,
613
- column: node.loc.start.column
651
+ line: rightToken.loc.start.line,
652
+ column: rightToken.loc.start.column
614
653
  }
615
654
  },
616
- messageId: "noSpaceInEmptyBlock",
655
+ node,
656
+ messageId: "noSpaceBeforeSemi",
617
657
  *fix(fixer) {
618
- yield fixer.replaceTextRange([spaceEndRange - preSpace.length, spaceEndRange], "");
658
+ yield fixer.removeRange([leftToken.range[1], rightToken.range[0]]);
619
659
  }
620
660
  });
621
661
  }
@@ -624,9 +664,9 @@ const spaceInEmptyBlock = createEslintRule({
624
664
  }
625
665
  });
626
666
 
627
- const RULE_NAME$3 = "space-before-function-paren";
667
+ const RULE_NAME$2 = "space-before-function-paren";
628
668
  const spaceBeforeFunctionParen = createEslintRule({
629
- name: RULE_NAME$3,
669
+ name: RULE_NAME$2,
630
670
  meta: {
631
671
  type: "layout",
632
672
  docs: {
@@ -731,173 +771,146 @@ const spaceBeforeFunctionParen = createEslintRule({
731
771
  }
732
772
  });
733
773
 
734
- const RULE_NAME$2 = "semi-spacing";
735
- const semiSpacing = createEslintRule({
736
- 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,
737
778
  meta: {
738
779
  type: "layout",
739
780
  docs: {
740
- description: "Semicolon spacing in types",
781
+ description: "Spaces between generic type parameters and paren",
741
782
  recommended: "error"
742
783
  },
743
784
  fixable: "whitespace",
744
785
  schema: [],
745
786
  messages: {
746
- noSpaceBeforeSemi: "Expected no space before semicolon"
787
+ noSpaceBetweenGenericAndParen: "Expected no space between generic type parameters and paren"
747
788
  }
748
789
  },
749
790
  defaultOptions: [],
750
791
  create: (context) => {
751
792
  const sourceCode = context.getSourceCode();
793
+ const text = sourceCode.text;
752
794
  return {
753
- TSTypeAliasDeclaration(node) {
754
- const leftToken = node.typeAnnotation;
755
- const rightToken = sourceCode.getTokenAfter(node.typeAnnotation);
756
- if (!rightToken || rightToken.type !== utils.AST_TOKEN_TYPES.Punctuator || rightToken.value !== ";") {
757
- return;
758
- }
759
- const hasSpacing = sourceCode.isSpaceBetween(leftToken, rightToken);
760
- 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) {
761
804
  context.report({
762
805
  loc: {
763
806
  start: {
764
- line: leftToken.loc.end.line,
765
- column: leftToken.loc.end.column
807
+ line: node.loc.end.line,
808
+ column: node.loc.end.column + 1
766
809
  },
767
810
  end: {
768
- line: rightToken.loc.start.line,
769
- column: rightToken.loc.start.column
811
+ line: node.loc.end.line,
812
+ column: node.loc.end.column + 1 + postSpace.length
770
813
  }
771
814
  },
772
815
  node,
773
- messageId: "noSpaceBeforeSemi",
816
+ messageId: "noSpaceBetweenGenericAndParen",
774
817
  *fix(fixer) {
775
- yield fixer.removeRange([leftToken.range[1], rightToken.range[0]]);
818
+ yield fixer.replaceTextRange([spaceStartRange, spaceStartRange + postSpace.length], "");
776
819
  }
777
820
  });
778
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
+ }
779
846
  }
780
847
  };
781
848
  }
782
849
  });
783
850
 
784
- const RULE_NAME$1 = "no-inline-type-import";
785
- const noInlineTypeImport = createEslintRule({
786
- name: RULE_NAME$1,
851
+ const RULE_NAME = "space-in-empty-block";
852
+ const spaceInEmptyBlock = createEslintRule({
853
+ name: RULE_NAME,
787
854
  meta: {
788
855
  type: "layout",
789
856
  docs: {
790
- description: "Disallow inline type import",
857
+ description: "Disallow spaces in empty block",
791
858
  recommended: "error"
792
859
  },
793
- fixable: "code",
860
+ fixable: "whitespace",
794
861
  schema: [],
795
862
  messages: {
796
- noInlineTypeImport: "Expected no inline type import"
863
+ noSpaceInEmptyBlock: "Expected no space in empty block"
797
864
  }
798
865
  },
799
866
  defaultOptions: [],
800
867
  create: (context) => {
801
868
  const sourceCode = context.getSourceCode();
802
869
  return {
803
- ImportDeclaration: (node) => {
804
- const specifiers = node.specifiers;
805
- const typeSpecifiers = specifiers.filter((s) => s.type === "ImportSpecifier" && s.importKind === "type");
806
- const valueSpecifiers = specifiers.filter((s) => s.type === "ImportSpecifier" && s.importKind === "value");
807
- if (typeSpecifiers.length && valueSpecifiers.length) {
808
- context.report({
809
- loc: node.loc,
810
- messageId: "noInlineTypeImport",
811
- *fix(fixer) {
812
- const typeSpecifiersText = typeSpecifiers.map((s) => sourceCode.getText(s).replace("type ", "")).join(", ");
813
- const valueSpecifiersText = valueSpecifiers.map((s) => sourceCode.getText(s)).join(", ");
814
- yield fixer.replaceText(node, `import type { ${typeSpecifiersText} } from "${node.source.value}";
815
- import { ${valueSpecifiersText} } from "${node.source.value}";`);
816
- }
817
- });
818
- } 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) {
819
878
  context.report({
820
- loc: node.loc,
821
- 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",
822
887
  *fix(fixer) {
823
- const typeSpecifiersText = typeSpecifiers.map((s) => sourceCode.getText(s).replace("type ", "")).join(", ");
824
- yield fixer.replaceText(node, `import type { ${typeSpecifiersText} } from "${node.source.value}";`);
888
+ yield fixer.replaceTextRange([node.range[0] + 1, spaceStartRange + postSpace.length], "");
825
889
  }
826
890
  });
827
891
  }
828
- }
829
- };
830
- }
831
- });
832
-
833
- const RULE_NAME = "array-bracket-spacing";
834
- const arrayBracketSpacing = createEslintRule({
835
- name: RULE_NAME,
836
- meta: {
837
- type: "layout",
838
- docs: {
839
- description: "Array bracket spacing",
840
- recommended: "error"
841
- },
842
- fixable: "whitespace",
843
- schema: [],
844
- messages: {
845
- arrayBracketSpacing: "Array bracket spacing mismatch"
846
- }
847
- },
848
- defaultOptions: [],
849
- create: (context) => {
850
- const sourceCode = context.getSourceCode();
851
- const text = sourceCode.getText();
852
- const checkNode = (node) => {
853
- const elements = node.type === utils.AST_NODE_TYPES.TSTupleType ? node.elementTypes : node.elements;
854
- const firstElement = elements[0];
855
- const lastElement = elements[elements.length - 1];
856
- if (!firstElement) {
857
- return;
858
- }
859
- const leftToken = sourceCode.getTokenBefore(firstElement);
860
- const rightToken = reactivity.ref(sourceCode.getTokenAfter(lastElement));
861
- if (rightToken.value.value === ",") {
862
- rightToken.value = sourceCode.getTokenAfter(rightToken.value);
863
- }
864
- const textBetweenFirstAndToken = reactivity.computed(() => text.slice(leftToken.range[1], firstElement.range[0]));
865
- const isNewline = reactivity.computed(() => textBetweenFirstAndToken.value.includes("\n"));
866
- const textBetweenLastAndToken = reactivity.computed(() => text.slice(lastElement.range[1], rightToken.value.range[0]));
867
- const hasNewlineAfter = reactivity.computed(() => textBetweenLastAndToken.value.includes("\n"));
868
- if (sourceCode.isSpaceBetween(leftToken, firstElement) && !isNewline.value) {
869
- context.report({
870
- node,
871
- messageId: "arrayBracketSpacing",
872
- *fix(fixer) {
873
- yield fixer.removeRange([leftToken.range[1], firstElement.range[0]]);
874
- }
875
- });
876
- }
877
- if (sourceCode.isSpaceBetween(lastElement, rightToken.value)) {
878
- 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) {
879
896
  context.report({
880
- node,
881
- 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",
882
908
  *fix(fixer) {
883
- yield fixer.removeRange([lastElement.range[1], rightToken.value.range[0]]);
909
+ yield fixer.replaceTextRange([spaceEndRange - preSpace.length, spaceEndRange], "");
884
910
  }
885
911
  });
886
912
  }
887
913
  }
888
- if (isNewline.value && !hasNewlineAfter.value) {
889
- context.report({
890
- node,
891
- messageId: "arrayBracketSpacing",
892
- *fix(fixer) {
893
- yield fixer.replaceTextRange([lastElement.range[1], rightToken.value.range[0]], "\n");
894
- }
895
- });
896
- }
897
- };
898
- return {
899
- TSTupleType: checkNode,
900
- ArrayExpression: checkNode
901
914
  };
902
915
  }
903
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) {
@@ -458,19 +509,25 @@ const noSpaceBeforeParen = createEslintRule({
458
509
  const spaces = /(\s*)$/.exec(textBetweenFunctionNameAndParen.value)[1];
459
510
  if (!hasGenerics.value) {
460
511
  if (spaces.length > 0 && textBetweenFunctionNameAndParen.value !== "?.") {
461
- const textBeforeSpaces = textBetweenFunctionNameAndParen.value.slice(0, textBetweenFunctionNameAndParen.value.length - spaces.length);
512
+ const textBeforeSpaces = textBetweenFunctionNameAndParen.value.slice(
513
+ 0,
514
+ textBetweenFunctionNameAndParen.value.length - spaces.length
515
+ );
462
516
  context.report({
463
517
  node,
464
518
  messageId: "noSpaceBeforeParen",
465
519
  *fix(fixer) {
466
- yield fixer.replaceTextRange(textBetweenFunctionNameAndParenRange.value, textBeforeSpaces + (node.optional ? "?." : ""));
520
+ yield fixer.replaceTextRange(
521
+ textBetweenFunctionNameAndParenRange.value,
522
+ textBeforeSpaces + (node.optional ? "?." : "")
523
+ );
467
524
  }
468
525
  });
469
526
  }
470
527
  } else {
471
528
  const preSpaces = /^(\s*)/.exec(textBetweenFunctionNameAndParen.value)[1];
472
529
  const postSpaces = /(\s*)$/.exec(textBetweenFunctionNameAndParen.value)[1];
473
- const spacesBeforeOptionalMark = /(\s*)\?\./.exec(textBetweenFunctionNameAndParen.value)?.[1] || "";
530
+ const spacesBeforeOptionalMark = /(\s*)\?\./.exec(textBetweenFunctionNameAndParen.value)?.[1] ?? "";
474
531
  if (preSpaces.length > 0) {
475
532
  context.report({
476
533
  node,
@@ -542,65 +599,48 @@ const noSpaceBeforeParen = createEslintRule({
542
599
  }
543
600
  });
544
601
 
545
- const RULE_NAME$4 = "space-in-empty-block";
546
- const spaceInEmptyBlock = createEslintRule({
547
- name: RULE_NAME$4,
602
+ const RULE_NAME$3 = "semi-spacing";
603
+ const semiSpacing = createEslintRule({
604
+ name: RULE_NAME$3,
548
605
  meta: {
549
606
  type: "layout",
550
607
  docs: {
551
- description: "Disallow spaces in empty block",
608
+ description: "Semicolon spacing in types",
552
609
  recommended: "error"
553
610
  },
554
611
  fixable: "whitespace",
555
612
  schema: [],
556
613
  messages: {
557
- noSpaceInEmptyBlock: "Expected no space in empty block"
614
+ noSpaceBeforeSemi: "Expected no space before semicolon"
558
615
  }
559
616
  },
560
617
  defaultOptions: [],
561
618
  create: (context) => {
562
619
  const sourceCode = context.getSourceCode();
563
620
  return {
564
- BlockStatement: (node) => {
565
- 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 !== ";") {
566
625
  return;
567
626
  }
568
- const spaceStartRange = node.range[1] - 2;
569
- const post = sourceCode.text.slice(spaceStartRange);
570
- const postSpace = post.match(/^(\s*)/)?.[0];
571
- if (postSpace && postSpace.length) {
572
- context.report({
573
- loc: {
574
- start: node.loc.start,
575
- end: {
576
- line: node.loc.end.line,
577
- column: node.loc.end.column - 1 + postSpace.length
578
- }
579
- },
580
- messageId: "noSpaceInEmptyBlock",
581
- *fix(fixer) {
582
- yield fixer.replaceTextRange([node.range[0] + 1, spaceStartRange + postSpace.length], "");
583
- }
584
- });
585
- }
586
- const spaceEndRange = node.range[0] + 1;
587
- const pre = sourceCode.text.slice(0, spaceEndRange);
588
- const preSpace = pre.match(/(\s+)$/)?.[0];
589
- if (preSpace && preSpace.length) {
627
+ const hasSpacing = sourceCode.isSpaceBetween(leftToken, rightToken);
628
+ if (hasSpacing) {
590
629
  context.report({
591
630
  loc: {
592
631
  start: {
593
- line: node.loc.start.line,
594
- column: node.loc.start.column - preSpace.length
632
+ line: leftToken.loc.end.line,
633
+ column: leftToken.loc.end.column
595
634
  },
596
635
  end: {
597
- line: node.loc.start.line,
598
- column: node.loc.start.column
636
+ line: rightToken.loc.start.line,
637
+ column: rightToken.loc.start.column
599
638
  }
600
639
  },
601
- messageId: "noSpaceInEmptyBlock",
640
+ node,
641
+ messageId: "noSpaceBeforeSemi",
602
642
  *fix(fixer) {
603
- yield fixer.replaceTextRange([spaceEndRange - preSpace.length, spaceEndRange], "");
643
+ yield fixer.removeRange([leftToken.range[1], rightToken.range[0]]);
604
644
  }
605
645
  });
606
646
  }
@@ -609,9 +649,9 @@ const spaceInEmptyBlock = createEslintRule({
609
649
  }
610
650
  });
611
651
 
612
- const RULE_NAME$3 = "space-before-function-paren";
652
+ const RULE_NAME$2 = "space-before-function-paren";
613
653
  const spaceBeforeFunctionParen = createEslintRule({
614
- name: RULE_NAME$3,
654
+ name: RULE_NAME$2,
615
655
  meta: {
616
656
  type: "layout",
617
657
  docs: {
@@ -716,173 +756,146 @@ const spaceBeforeFunctionParen = createEslintRule({
716
756
  }
717
757
  });
718
758
 
719
- const RULE_NAME$2 = "semi-spacing";
720
- const semiSpacing = createEslintRule({
721
- 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,
722
763
  meta: {
723
764
  type: "layout",
724
765
  docs: {
725
- description: "Semicolon spacing in types",
766
+ description: "Spaces between generic type parameters and paren",
726
767
  recommended: "error"
727
768
  },
728
769
  fixable: "whitespace",
729
770
  schema: [],
730
771
  messages: {
731
- noSpaceBeforeSemi: "Expected no space before semicolon"
772
+ noSpaceBetweenGenericAndParen: "Expected no space between generic type parameters and paren"
732
773
  }
733
774
  },
734
775
  defaultOptions: [],
735
776
  create: (context) => {
736
777
  const sourceCode = context.getSourceCode();
778
+ const text = sourceCode.text;
737
779
  return {
738
- TSTypeAliasDeclaration(node) {
739
- const leftToken = node.typeAnnotation;
740
- const rightToken = sourceCode.getTokenAfter(node.typeAnnotation);
741
- if (!rightToken || rightToken.type !== AST_TOKEN_TYPES.Punctuator || rightToken.value !== ";") {
742
- return;
743
- }
744
- const hasSpacing = sourceCode.isSpaceBetween(leftToken, rightToken);
745
- 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) {
746
789
  context.report({
747
790
  loc: {
748
791
  start: {
749
- line: leftToken.loc.end.line,
750
- column: leftToken.loc.end.column
792
+ line: node.loc.end.line,
793
+ column: node.loc.end.column + 1
751
794
  },
752
795
  end: {
753
- line: rightToken.loc.start.line,
754
- column: rightToken.loc.start.column
796
+ line: node.loc.end.line,
797
+ column: node.loc.end.column + 1 + postSpace.length
755
798
  }
756
799
  },
757
800
  node,
758
- messageId: "noSpaceBeforeSemi",
801
+ messageId: "noSpaceBetweenGenericAndParen",
759
802
  *fix(fixer) {
760
- yield fixer.removeRange([leftToken.range[1], rightToken.range[0]]);
803
+ yield fixer.replaceTextRange([spaceStartRange, spaceStartRange + postSpace.length], "");
761
804
  }
762
805
  });
763
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
+ }
764
831
  }
765
832
  };
766
833
  }
767
834
  });
768
835
 
769
- const RULE_NAME$1 = "no-inline-type-import";
770
- const noInlineTypeImport = createEslintRule({
771
- name: RULE_NAME$1,
836
+ const RULE_NAME = "space-in-empty-block";
837
+ const spaceInEmptyBlock = createEslintRule({
838
+ name: RULE_NAME,
772
839
  meta: {
773
840
  type: "layout",
774
841
  docs: {
775
- description: "Disallow inline type import",
842
+ description: "Disallow spaces in empty block",
776
843
  recommended: "error"
777
844
  },
778
- fixable: "code",
845
+ fixable: "whitespace",
779
846
  schema: [],
780
847
  messages: {
781
- noInlineTypeImport: "Expected no inline type import"
848
+ noSpaceInEmptyBlock: "Expected no space in empty block"
782
849
  }
783
850
  },
784
851
  defaultOptions: [],
785
852
  create: (context) => {
786
853
  const sourceCode = context.getSourceCode();
787
854
  return {
788
- ImportDeclaration: (node) => {
789
- const specifiers = node.specifiers;
790
- const typeSpecifiers = specifiers.filter((s) => s.type === "ImportSpecifier" && s.importKind === "type");
791
- const valueSpecifiers = specifiers.filter((s) => s.type === "ImportSpecifier" && s.importKind === "value");
792
- if (typeSpecifiers.length && valueSpecifiers.length) {
793
- context.report({
794
- loc: node.loc,
795
- messageId: "noInlineTypeImport",
796
- *fix(fixer) {
797
- const typeSpecifiersText = typeSpecifiers.map((s) => sourceCode.getText(s).replace("type ", "")).join(", ");
798
- const valueSpecifiersText = valueSpecifiers.map((s) => sourceCode.getText(s)).join(", ");
799
- yield fixer.replaceText(node, `import type { ${typeSpecifiersText} } from "${node.source.value}";
800
- import { ${valueSpecifiersText} } from "${node.source.value}";`);
801
- }
802
- });
803
- } 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) {
804
863
  context.report({
805
- loc: node.loc,
806
- 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",
807
872
  *fix(fixer) {
808
- const typeSpecifiersText = typeSpecifiers.map((s) => sourceCode.getText(s).replace("type ", "")).join(", ");
809
- yield fixer.replaceText(node, `import type { ${typeSpecifiersText} } from "${node.source.value}";`);
873
+ yield fixer.replaceTextRange([node.range[0] + 1, spaceStartRange + postSpace.length], "");
810
874
  }
811
875
  });
812
876
  }
813
- }
814
- };
815
- }
816
- });
817
-
818
- const RULE_NAME = "array-bracket-spacing";
819
- const arrayBracketSpacing = createEslintRule({
820
- name: RULE_NAME,
821
- meta: {
822
- type: "layout",
823
- docs: {
824
- description: "Array bracket spacing",
825
- recommended: "error"
826
- },
827
- fixable: "whitespace",
828
- schema: [],
829
- messages: {
830
- arrayBracketSpacing: "Array bracket spacing mismatch"
831
- }
832
- },
833
- defaultOptions: [],
834
- create: (context) => {
835
- const sourceCode = context.getSourceCode();
836
- const text = sourceCode.getText();
837
- const checkNode = (node) => {
838
- const elements = node.type === AST_NODE_TYPES.TSTupleType ? node.elementTypes : node.elements;
839
- const firstElement = elements[0];
840
- const lastElement = elements[elements.length - 1];
841
- if (!firstElement) {
842
- return;
843
- }
844
- const leftToken = sourceCode.getTokenBefore(firstElement);
845
- const rightToken = ref(sourceCode.getTokenAfter(lastElement));
846
- if (rightToken.value.value === ",") {
847
- rightToken.value = sourceCode.getTokenAfter(rightToken.value);
848
- }
849
- const textBetweenFirstAndToken = computed(() => text.slice(leftToken.range[1], firstElement.range[0]));
850
- const isNewline = computed(() => textBetweenFirstAndToken.value.includes("\n"));
851
- const textBetweenLastAndToken = computed(() => text.slice(lastElement.range[1], rightToken.value.range[0]));
852
- const hasNewlineAfter = computed(() => textBetweenLastAndToken.value.includes("\n"));
853
- if (sourceCode.isSpaceBetween(leftToken, firstElement) && !isNewline.value) {
854
- context.report({
855
- node,
856
- messageId: "arrayBracketSpacing",
857
- *fix(fixer) {
858
- yield fixer.removeRange([leftToken.range[1], firstElement.range[0]]);
859
- }
860
- });
861
- }
862
- if (sourceCode.isSpaceBetween(lastElement, rightToken.value)) {
863
- 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) {
864
881
  context.report({
865
- node,
866
- 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",
867
893
  *fix(fixer) {
868
- yield fixer.removeRange([lastElement.range[1], rightToken.value.range[0]]);
894
+ yield fixer.replaceTextRange([spaceEndRange - preSpace.length, spaceEndRange], "");
869
895
  }
870
896
  });
871
897
  }
872
898
  }
873
- if (isNewline.value && !hasNewlineAfter.value) {
874
- context.report({
875
- node,
876
- messageId: "arrayBracketSpacing",
877
- *fix(fixer) {
878
- yield fixer.replaceTextRange([lastElement.range[1], rightToken.value.range[0]], "\n");
879
- }
880
- });
881
- }
882
- };
883
- return {
884
- TSTupleType: checkNode,
885
- ArrayExpression: checkNode
886
899
  };
887
900
  }
888
901
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@so1ve/eslint-plugin",
3
- "version": "0.48.0",
3
+ "version": "0.50.0",
4
4
  "author": "Anthony Fu <anthonyfu117@hotmail.com> (https://github.com/antfu/)",
5
5
  "contributors": [
6
6
  {