@tai-kun/vp-lint-plugins 0.0.1 → 0.0.2

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.
@@ -1,6 +1,7 @@
1
1
  /**
2
2
  * 連続する変数宣言の後に別の種類の文が続く場合、空行を 1 行以上確保します。
3
3
  *
4
+ * 宣言した変数を使う式文は同じ宣言グループとして扱い、グループ内の空行は削除します。
4
5
  * return の直前は blank-line-before-return が担当するため対象外にします。
5
6
  */
6
7
  declare const blankLineAfterVariableDeclaration: import("vite-plus/lint/plugins").Rule;
@@ -1 +1 @@
1
- {"version":3,"file":"blank-line-after-variable-declaration.d.ts","sourceRoot":"","sources":["../../../src/rules/blank-line-after-variable-declaration.ts"],"names":[],"mappings":"AAkBA;;;;GAIG;AACH,QAAA,MAAM,iCAAiC,uCA0DrC,CAAC;eAEY,iCAAiC"}
1
+ {"version":3,"file":"blank-line-after-variable-declaration.d.ts","sourceRoot":"","sources":["../../../src/rules/blank-line-after-variable-declaration.ts"],"names":[],"mappings":"AA8CA;;;;;GAKG;AACH,QAAA,MAAM,iCAAiC,uCA8GrC,CAAC;eAEY,iCAAiC"}
@@ -1,49 +1,105 @@
1
1
  import { defineRule } from "vite-plus/lint/plugins";
2
- import { hasBlankLine, getBlankLineFix, getNextStatement, getInsertionTarget, } from "./blank-line.js";
3
- function isVariableDeclarationStatement(node) {
2
+ import { hasBlankLine, getBlankLineFix, getNextStatement, getInsertionTarget, getPreviousStatement, getRemoveBlankLineFix, } from "./blank-line.js";
3
+ function getVariableDeclaration(node) {
4
4
  if (node.type === "VariableDeclaration") {
5
- return true;
5
+ return node;
6
6
  }
7
- return node.type === "ExportNamedDeclaration" && node.declaration?.type === "VariableDeclaration";
7
+ if (node.type === "ExportNamedDeclaration" && node.declaration?.type === "VariableDeclaration") {
8
+ return node.declaration;
9
+ }
10
+ return null;
11
+ }
12
+ function isRelatedExpression(statement, variables) {
13
+ if (statement.type !== "ExpressionStatement") {
14
+ return false;
15
+ }
16
+ for (const variable of variables) {
17
+ for (const reference of variable.references) {
18
+ if (reference.identifier.range[0] >= statement.range[0] &&
19
+ reference.identifier.range[1] <= statement.range[1]) {
20
+ return true;
21
+ }
22
+ }
23
+ }
24
+ return false;
8
25
  }
9
26
  /**
10
27
  * 連続する変数宣言の後に別の種類の文が続く場合、空行を 1 行以上確保します。
11
28
  *
29
+ * 宣言した変数を使う式文は同じ宣言グループとして扱い、グループ内の空行は削除します。
12
30
  * return の直前は blank-line-before-return が担当するため対象外にします。
13
31
  */
14
32
  const blankLineAfterVariableDeclaration = defineRule({
15
33
  meta: {
16
34
  docs: {
17
- description: "Require a blank line after a variable declaration group when another statement follows.",
35
+ description: "Require a blank line after a variable declaration group and remove blank lines before related statements.",
18
36
  },
19
37
  type: "layout",
20
38
  fixable: "whitespace",
21
39
  messages: {
22
40
  blankLine: "Expected a blank line after a variable declaration group when another statement follows.",
41
+ unexpectedBlankLine: "Expected no blank line between a variable declaration and its related statement.",
23
42
  },
24
43
  },
25
44
  create(context) {
26
45
  const sourceCode = context.sourceCode;
27
46
  function checkStatement(node) {
47
+ const variableDeclaration = getVariableDeclaration(node);
48
+ if (variableDeclaration === null) {
49
+ return;
50
+ }
28
51
  if (node.type === "VariableDeclaration" && node.parent.type === "ExportNamedDeclaration") {
29
52
  return;
30
53
  }
31
- const nextStatement = getNextStatement(node);
54
+ const previousStatement = getPreviousStatement(node);
55
+ if (previousStatement !== null && getVariableDeclaration(previousStatement) !== null) {
56
+ return;
57
+ }
58
+ const variables = new Set(sourceCode.getDeclaredVariables(variableDeclaration));
59
+ let last = node;
60
+ let nextStatement = getNextStatement(last);
61
+ while (nextStatement !== null) {
62
+ const nextVariableDeclaration = getVariableDeclaration(nextStatement);
63
+ if (nextVariableDeclaration !== null && getVariableDeclaration(last) !== null) {
64
+ for (const variable of sourceCode.getDeclaredVariables(nextVariableDeclaration)) {
65
+ variables.add(variable);
66
+ }
67
+ last = nextStatement;
68
+ nextStatement = getNextStatement(last);
69
+ continue;
70
+ }
71
+ if (nextStatement.type === "ExpressionStatement" &&
72
+ isRelatedExpression(nextStatement, variables)) {
73
+ const target = getInsertionTarget(last, nextStatement, sourceCode);
74
+ if (hasBlankLine(last, target)) {
75
+ context.report({
76
+ fix(fixer) {
77
+ return getRemoveBlankLineFix(fixer, sourceCode, last, target);
78
+ },
79
+ node: nextStatement,
80
+ messageId: "unexpectedBlankLine",
81
+ });
82
+ }
83
+ last = nextStatement;
84
+ nextStatement = getNextStatement(last);
85
+ continue;
86
+ }
87
+ break;
88
+ }
32
89
  if (nextStatement === null ||
33
90
  nextStatement.type === "EmptyStatement" ||
34
- nextStatement.type === "ReturnStatement" ||
35
- isVariableDeclarationStatement(nextStatement)) {
91
+ nextStatement.type === "ReturnStatement") {
36
92
  return;
37
93
  }
38
- const target = getInsertionTarget(node, nextStatement, sourceCode);
39
- if (hasBlankLine(node, target)) {
94
+ const target = getInsertionTarget(last, nextStatement, sourceCode);
95
+ if (hasBlankLine(last, target)) {
40
96
  return;
41
97
  }
42
98
  context.report({
43
99
  fix(fixer) {
44
- return getBlankLineFix(fixer, sourceCode, node, target);
100
+ return getBlankLineFix(fixer, sourceCode, last, target);
45
101
  },
46
- node,
102
+ node: nextStatement,
47
103
  messageId: "blankLine",
48
104
  });
49
105
  }
@@ -10,5 +10,9 @@ declare function getPreviousStatement(node: ESTree.Node): ESTree.Node | null;
10
10
  declare function getInsertionTarget(node: ESTree.Node, nextStatement: ESTree.Node, sourceCode: SourceCode): ESTree.Node | Comment;
11
11
  declare function hasBlankLine(node: ESTree.Node, target: ESTree.Node | Comment): boolean;
12
12
  declare function getBlankLineFix(fixer: Fixer, sourceCode: SourceCode, node: ESTree.Node, target: ESTree.Node | Comment): Fix | null;
13
- export { hasBlankLine, getBlankLineFix, getNextStatement, getInsertionTarget, getPreviousStatement, };
13
+ /**
14
+ * 指定したノードと対象の間の空行を削除します。
15
+ */
16
+ declare function getRemoveBlankLineFix(fixer: Fixer, sourceCode: SourceCode, node: ESTree.Node, target: ESTree.Node | Comment): Fix | null;
17
+ export { hasBlankLine, getBlankLineFix, getNextStatement, getInsertionTarget, getPreviousStatement, getRemoveBlankLineFix, };
14
18
  //# sourceMappingURL=blank-line.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"blank-line.d.ts","sourceRoot":"","sources":["../../../src/rules/blank-line.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AA4BtF,iBAAS,gBAAgB,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,GAAG,MAAM,CAAC,IAAI,GAAG,IAAI,CAc/D;AAED,iBAAS,oBAAoB,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,GAAG,MAAM,CAAC,IAAI,GAAG,IAAI,CAcnE;AAED;;;;;GAKG;AACH,iBAAS,kBAAkB,CACzB,IAAI,EAAE,MAAM,CAAC,IAAI,EACjB,aAAa,EAAE,MAAM,CAAC,IAAI,EAC1B,UAAU,EAAE,UAAU,GACrB,MAAM,CAAC,IAAI,GAAG,OAAO,CAWvB;AAED,iBAAS,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,IAAI,GAAG,OAAO,GAAG,OAAO,CAE/E;AAED,iBAAS,eAAe,CACtB,KAAK,EAAE,KAAK,EACZ,UAAU,EAAE,UAAU,EACtB,IAAI,EAAE,MAAM,CAAC,IAAI,EACjB,MAAM,EAAE,MAAM,CAAC,IAAI,GAAG,OAAO,GAC5B,GAAG,GAAG,IAAI,CAYZ;AAED,OAAO,EACL,YAAY,EACZ,eAAe,EACf,gBAAgB,EAChB,kBAAkB,EAClB,oBAAoB,GACrB,CAAC"}
1
+ {"version":3,"file":"blank-line.d.ts","sourceRoot":"","sources":["../../../src/rules/blank-line.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AA4BtF,iBAAS,gBAAgB,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,GAAG,MAAM,CAAC,IAAI,GAAG,IAAI,CAc/D;AAED,iBAAS,oBAAoB,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,GAAG,MAAM,CAAC,IAAI,GAAG,IAAI,CAcnE;AAED;;;;;GAKG;AACH,iBAAS,kBAAkB,CACzB,IAAI,EAAE,MAAM,CAAC,IAAI,EACjB,aAAa,EAAE,MAAM,CAAC,IAAI,EAC1B,UAAU,EAAE,UAAU,GACrB,MAAM,CAAC,IAAI,GAAG,OAAO,CAWvB;AAED,iBAAS,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,IAAI,GAAG,OAAO,GAAG,OAAO,CAE/E;AAED,iBAAS,eAAe,CACtB,KAAK,EAAE,KAAK,EACZ,UAAU,EAAE,UAAU,EACtB,IAAI,EAAE,MAAM,CAAC,IAAI,EACjB,MAAM,EAAE,MAAM,CAAC,IAAI,GAAG,OAAO,GAC5B,GAAG,GAAG,IAAI,CAYZ;AAED;;GAEG;AACH,iBAAS,qBAAqB,CAC5B,KAAK,EAAE,KAAK,EACZ,UAAU,EAAE,UAAU,EACtB,IAAI,EAAE,MAAM,CAAC,IAAI,EACjB,MAAM,EAAE,MAAM,CAAC,IAAI,GAAG,OAAO,GAC5B,GAAG,GAAG,IAAI,CAmBZ;AAED,OAAO,EACL,YAAY,EACZ,eAAe,EACf,gBAAgB,EAChB,kBAAkB,EAClB,oBAAoB,EACpB,qBAAqB,GACtB,CAAC"}
@@ -65,4 +65,22 @@ function getBlankLineFix(fixer, sourceCode, node, target) {
65
65
  }
66
66
  return fixer.insertTextBeforeRange([lineEnd, lineEnd + 1], "\n");
67
67
  }
68
- export { hasBlankLine, getBlankLineFix, getNextStatement, getInsertionTarget, getPreviousStatement, };
68
+ /**
69
+ * 指定したノードと対象の間の空行を削除します。
70
+ */
71
+ function getRemoveBlankLineFix(fixer, sourceCode, node, target) {
72
+ if (target.loc.start.line <= node.loc.end.line + 1) {
73
+ return null;
74
+ }
75
+ const fromLineEnd = sourceCode.text.indexOf("\n", node.range[1]);
76
+ if (fromLineEnd === -1) {
77
+ return null;
78
+ }
79
+ const targetLineStart = sourceCode.text.lastIndexOf("\n", target.range[0]) + 1;
80
+ const indent = sourceCode.text.slice(targetLineStart, target.range[0]);
81
+ if (indent.trim() !== "") {
82
+ return null;
83
+ }
84
+ return fixer.replaceTextRange([fromLineEnd, target.range[0]], `\n${indent}`);
85
+ }
86
+ export { hasBlankLine, getBlankLineFix, getNextStatement, getInsertionTarget, getPreviousStatement, getRemoveBlankLineFix, };
@@ -1 +1 @@
1
- {"version":3,"file":"comment-one-sentence-per-line.d.ts","sourceRoot":"","sources":["../../../src/rules/comment-one-sentence-per-line.ts"],"names":[],"mappings":"AAiKA;;;;;GAKG;AACH,QAAA,MAAM,yBAAyB,uCA8F7B,CAAC;eAEY,yBAAyB"}
1
+ {"version":3,"file":"comment-one-sentence-per-line.d.ts","sourceRoot":"","sources":["../../../src/rules/comment-one-sentence-per-line.ts"],"names":[],"mappings":"AAiKA;;;;;GAKG;AACH,QAAA,MAAM,yBAAyB,uCA6F7B,CAAC;eAEY,yBAAyB"}
@@ -1 +1 @@
1
- {"version":3,"file":"jsdoc-utils.d.ts","sourceRoot":"","sources":["../../../src/rules/jsdoc-utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAI1E,UAAU,SAAS;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB;AAED,iBAAS,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAI/C;AAyBD;;GAEG;AACH,iBAAS,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAqBpD;AAED;;;;GAIG;AACH,iBAAS,aAAa,CAAC,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,UAAU,GAAG,SAAS,EAAE,GAAG,IAAI,CA+DnF;AAED,iBAAS,iBAAiB,CAAC,KAAK,EAAE,SAAS,SAAS,EAAE,GAAG,OAAO,CAE/D;AAcD;;GAEG;AACH,iBAAS,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,UAAU,EAAE,UAAU,GAAG,OAAO,GAAG,IAAI,CAmB/E;AAED,YAAY,EAAE,SAAS,EAAE,CAAC;AAE1B,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,YAAY,EAAE,aAAa,EAAE,iBAAiB,EAAE,CAAC"}
1
+ {"version":3,"file":"jsdoc-utils.d.ts","sourceRoot":"","sources":["../../../src/rules/jsdoc-utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAI1E,UAAU,SAAS;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB;AAED,iBAAS,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAI/C;AAyBD;;GAEG;AACH,iBAAS,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAqBpD;AAED;;;;GAIG;AACH,iBAAS,aAAa,CAAC,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,UAAU,GAAG,SAAS,EAAE,GAAG,IAAI,CA8DnF;AAED,iBAAS,iBAAiB,CAAC,KAAK,EAAE,SAAS,SAAS,EAAE,GAAG,OAAO,CAE/D;AAcD;;GAEG;AACH,iBAAS,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,UAAU,EAAE,UAAU,GAAG,OAAO,GAAG,IAAI,CAmB/E;AAED,YAAY,EAAE,SAAS,EAAE,CAAC;AAE1B,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,YAAY,EAAE,aAAa,EAAE,iBAAiB,EAAE,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"object-sort-utils.d.ts","sourceRoot":"","sources":["../../../src/rules/object-sort-utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAW,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAE1E,KAAK,cAAc,GAAG,MAAM,CAAC,gBAAgB,GAAG,MAAM,CAAC,aAAa,CAAC;AAIrE,UAAU,eAAe;IACvB;;OAEG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IAEpB;;OAEG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAYD,KAAK,QAAQ,GACT;IACE,QAAQ,EAAE,KAAK,CAAC;IAChB,WAAW,EAAE,IAAI,CAAC;CACnB,GACD;IACE,QAAQ,EAAE,IAAI,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC;AAuEN,iBAAS,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAiBzD;AAeD;;;;;;;;GAQG;AACH,iBAAS,WAAW,CAClB,IAAI,EAAE,cAAc,EACpB,UAAU,EAAE,UAAU,EACtB,OAAO,GAAE,eAAoB,GAC5B,QAAQ,GAAG,IAAI,CAwLjB;AAED,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,CAAC"}
1
+ {"version":3,"file":"object-sort-utils.d.ts","sourceRoot":"","sources":["../../../src/rules/object-sort-utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAW,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAE1E,KAAK,cAAc,GAAG,MAAM,CAAC,gBAAgB,GAAG,MAAM,CAAC,aAAa,CAAC;AAIrE,UAAU,eAAe;IACvB;;OAEG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IAEpB;;OAEG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAYD,KAAK,QAAQ,GACT;IACE,QAAQ,EAAE,KAAK,CAAC;IAChB,WAAW,EAAE,IAAI,CAAC;CACnB,GACD;IACE,QAAQ,EAAE,IAAI,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC;AAuEN,iBAAS,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAiBzD;AAeD;;;;;;;;GAQG;AACH,iBAAS,WAAW,CAClB,IAAI,EAAE,cAAc,EACpB,UAAU,EAAE,UAAU,EACtB,OAAO,GAAE,eAAoB,GAC5B,QAAQ,GAAG,IAAI,CAuLjB;AAED,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"overload-signatures-adjacent.d.ts","sourceRoot":"","sources":["../../../src/rules/overload-signatures-adjacent.ts"],"names":[],"mappings":"AAiGA;;GAEG;AACH,QAAA,MAAM,0BAA0B,uCAqG9B,CAAC;eAEY,0BAA0B"}
1
+ {"version":3,"file":"overload-signatures-adjacent.d.ts","sourceRoot":"","sources":["../../../src/rules/overload-signatures-adjacent.ts"],"names":[],"mappings":"AAuEA;;GAEG;AACH,QAAA,MAAM,0BAA0B,uCAqG9B,CAAC;eAEY,0BAA0B"}
@@ -1,5 +1,5 @@
1
1
  import { defineRule } from "vite-plus/lint/plugins";
2
- import { getNextStatement } from "./blank-line.js";
2
+ import { getNextStatement, getRemoveBlankLineFix } from "./blank-line.js";
3
3
  function getFunction(node) {
4
4
  if (node.type === "FunctionDeclaration" || node.type === "TSDeclareFunction") {
5
5
  return node;
@@ -25,21 +25,6 @@ function getKeyName(node) {
25
25
  }
26
26
  return null;
27
27
  }
28
- function getRemoveBlankLinesFix(fixer, sourceCode, previous, next) {
29
- if (next.loc.start.line <= previous.loc.end.line + 1) {
30
- return null;
31
- }
32
- const fromLineEnd = sourceCode.text.indexOf("\n", previous.range[1]);
33
- if (fromLineEnd === -1) {
34
- return null;
35
- }
36
- const nextLineStart = sourceCode.text.lastIndexOf("\n", next.range[0]) + 1;
37
- const indent = sourceCode.text.slice(nextLineStart, next.range[0]);
38
- if (indent.trim() !== "") {
39
- return null;
40
- }
41
- return fixer.replaceTextRange([fromLineEnd, next.range[0]], `\n${indent}`);
42
- }
43
28
  function hasCommentBetween(sourceCode, previous, next) {
44
29
  return sourceCode
45
30
  .getAllComments()
@@ -52,7 +37,7 @@ function reportIfSeparated(context, sourceCode, previous, next) {
52
37
  }
53
38
  context.report({
54
39
  fix(fixer) {
55
- return getRemoveBlankLinesFix(fixer, sourceCode, previous, next);
40
+ return getRemoveBlankLineFix(fixer, sourceCode, previous, next);
56
41
  },
57
42
  node: next,
58
43
  messageId: "adjacent",
@@ -1 +1 @@
1
- {"version":3,"file":"sort-jsx-props.d.ts","sourceRoot":"","sources":["../../../src/rules/sort-jsx-props.ts"],"names":[],"mappings":"AAsEA;;;GAGG;AACH,QAAA,MAAM,YAAY,uCAwEhB,CAAC;eAEY,YAAY"}
1
+ {"version":3,"file":"sort-jsx-props.d.ts","sourceRoot":"","sources":["../../../src/rules/sort-jsx-props.ts"],"names":[],"mappings":"AAsEA;;;GAGG;AACH,QAAA,MAAM,YAAY,uCAuEhB,CAAC;eAEY,YAAY"}
@@ -1 +1 @@
1
- {"version":3,"file":"spaced-comment.d.ts","sourceRoot":"","sources":["../../../src/rules/spaced-comment.ts"],"names":[],"mappings":"AAyBA;;GAEG;AACH,QAAA,MAAM,aAAa,uCAkGjB,CAAC;eAEY,aAAa"}
1
+ {"version":3,"file":"spaced-comment.d.ts","sourceRoot":"","sources":["../../../src/rules/spaced-comment.ts"],"names":[],"mappings":"AAyBA;;GAEG;AACH,QAAA,MAAM,aAAa,uCAgGjB,CAAC;eAEY,aAAa"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tai-kun/vp-lint-plugins",
3
- "version": "0.0.1",
3
+ "version": "0.0.2",
4
4
  "description": "",
5
5
  "homepage": "https://tai-kun.github.io/vp-lint-plugins/",
6
6
  "license": "MIT",
@@ -1,69 +1,150 @@
1
1
  import { defineRule } from "vite-plus/lint/plugins";
2
- import type { ESTree } from "vite-plus/lint/plugins";
2
+ import type { ESTree, Variable } from "vite-plus/lint/plugins";
3
3
 
4
4
  import {
5
5
  hasBlankLine,
6
6
  getBlankLineFix,
7
7
  getNextStatement,
8
8
  getInsertionTarget,
9
+ getPreviousStatement,
10
+ getRemoveBlankLineFix,
9
11
  } from "./blank-line.js";
10
12
 
11
- function isVariableDeclarationStatement(node: ESTree.Node): boolean {
13
+ function getVariableDeclaration(node: ESTree.Node): ESTree.VariableDeclaration | null {
12
14
  if (node.type === "VariableDeclaration") {
13
- return true;
15
+ return node;
14
16
  }
15
17
 
16
- return node.type === "ExportNamedDeclaration" && node.declaration?.type === "VariableDeclaration";
18
+ if (node.type === "ExportNamedDeclaration" && node.declaration?.type === "VariableDeclaration") {
19
+ return node.declaration;
20
+ }
21
+
22
+ return null;
23
+ }
24
+
25
+ function isRelatedExpression(
26
+ statement: ESTree.Statement,
27
+ variables: ReadonlySet<Variable>,
28
+ ): boolean {
29
+ if (statement.type !== "ExpressionStatement") {
30
+ return false;
31
+ }
32
+
33
+ for (const variable of variables) {
34
+ for (const reference of variable.references) {
35
+ if (
36
+ reference.identifier.range[0] >= statement.range[0] &&
37
+ reference.identifier.range[1] <= statement.range[1]
38
+ ) {
39
+ return true;
40
+ }
41
+ }
42
+ }
43
+
44
+ return false;
17
45
  }
18
46
 
19
47
  /**
20
48
  * 連続する変数宣言の後に別の種類の文が続く場合、空行を 1 行以上確保します。
21
49
  *
50
+ * 宣言した変数を使う式文は同じ宣言グループとして扱い、グループ内の空行は削除します。
22
51
  * return の直前は blank-line-before-return が担当するため対象外にします。
23
52
  */
24
53
  const blankLineAfterVariableDeclaration = defineRule({
25
54
  meta: {
26
55
  docs: {
27
56
  description:
28
- "Require a blank line after a variable declaration group when another statement follows.",
57
+ "Require a blank line after a variable declaration group and remove blank lines before related statements.",
29
58
  },
30
59
  type: "layout",
31
60
  fixable: "whitespace",
32
61
  messages: {
33
62
  blankLine:
34
63
  "Expected a blank line after a variable declaration group when another statement follows.",
64
+ unexpectedBlankLine:
65
+ "Expected no blank line between a variable declaration and its related statement.",
35
66
  },
36
67
  },
37
68
  create(context) {
38
69
  const sourceCode = context.sourceCode;
39
70
 
40
71
  function checkStatement(node: ESTree.Node): void {
72
+ const variableDeclaration = getVariableDeclaration(node);
73
+
74
+ if (variableDeclaration === null) {
75
+ return;
76
+ }
77
+
41
78
  if (node.type === "VariableDeclaration" && node.parent.type === "ExportNamedDeclaration") {
42
79
  return;
43
80
  }
44
81
 
45
- const nextStatement = getNextStatement(node);
82
+ const previousStatement = getPreviousStatement(node);
83
+
84
+ if (previousStatement !== null && getVariableDeclaration(previousStatement) !== null) {
85
+ return;
86
+ }
87
+
88
+ const variables = new Set<Variable>(sourceCode.getDeclaredVariables(variableDeclaration));
89
+ let last = node;
90
+ let nextStatement = getNextStatement(last);
91
+
92
+ while (nextStatement !== null) {
93
+ const nextVariableDeclaration = getVariableDeclaration(nextStatement);
94
+
95
+ if (nextVariableDeclaration !== null && getVariableDeclaration(last) !== null) {
96
+ for (const variable of sourceCode.getDeclaredVariables(nextVariableDeclaration)) {
97
+ variables.add(variable);
98
+ }
99
+
100
+ last = nextStatement;
101
+ nextStatement = getNextStatement(last);
102
+ continue;
103
+ }
104
+
105
+ if (
106
+ nextStatement.type === "ExpressionStatement" &&
107
+ isRelatedExpression(nextStatement, variables)
108
+ ) {
109
+ const target = getInsertionTarget(last, nextStatement, sourceCode);
110
+
111
+ if (hasBlankLine(last, target)) {
112
+ context.report({
113
+ fix(fixer) {
114
+ return getRemoveBlankLineFix(fixer, sourceCode, last, target);
115
+ },
116
+ node: nextStatement,
117
+ messageId: "unexpectedBlankLine",
118
+ });
119
+ }
120
+
121
+ last = nextStatement;
122
+ nextStatement = getNextStatement(last);
123
+ continue;
124
+ }
125
+
126
+ break;
127
+ }
46
128
 
47
129
  if (
48
130
  nextStatement === null ||
49
131
  nextStatement.type === "EmptyStatement" ||
50
- nextStatement.type === "ReturnStatement" ||
51
- isVariableDeclarationStatement(nextStatement)
132
+ nextStatement.type === "ReturnStatement"
52
133
  ) {
53
134
  return;
54
135
  }
55
136
 
56
- const target = getInsertionTarget(node, nextStatement, sourceCode);
137
+ const target = getInsertionTarget(last, nextStatement, sourceCode);
57
138
 
58
- if (hasBlankLine(node, target)) {
139
+ if (hasBlankLine(last, target)) {
59
140
  return;
60
141
  }
61
142
 
62
143
  context.report({
63
144
  fix(fixer) {
64
- return getBlankLineFix(fixer, sourceCode, node, target);
145
+ return getBlankLineFix(fixer, sourceCode, last, target);
65
146
  },
66
- node,
147
+ node: nextStatement,
67
148
  messageId: "blankLine",
68
149
  });
69
150
  }
@@ -104,10 +104,40 @@ function getBlankLineFix(
104
104
  return fixer.insertTextBeforeRange([lineEnd, lineEnd + 1], "\n");
105
105
  }
106
106
 
107
+ /**
108
+ * 指定したノードと対象の間の空行を削除します。
109
+ */
110
+ function getRemoveBlankLineFix(
111
+ fixer: Fixer,
112
+ sourceCode: SourceCode,
113
+ node: ESTree.Node,
114
+ target: ESTree.Node | Comment,
115
+ ): Fix | null {
116
+ if (target.loc.start.line <= node.loc.end.line + 1) {
117
+ return null;
118
+ }
119
+
120
+ const fromLineEnd = sourceCode.text.indexOf("\n", node.range[1]);
121
+
122
+ if (fromLineEnd === -1) {
123
+ return null;
124
+ }
125
+
126
+ const targetLineStart = sourceCode.text.lastIndexOf("\n", target.range[0]) + 1;
127
+ const indent = sourceCode.text.slice(targetLineStart, target.range[0]);
128
+
129
+ if (indent.trim() !== "") {
130
+ return null;
131
+ }
132
+
133
+ return fixer.replaceTextRange([fromLineEnd, target.range[0]], `\n${indent}`);
134
+ }
135
+
107
136
  export {
108
137
  hasBlankLine,
109
138
  getBlankLineFix,
110
139
  getNextStatement,
111
140
  getInsertionTarget,
112
141
  getPreviousStatement,
142
+ getRemoveBlankLineFix,
113
143
  };
@@ -129,7 +129,6 @@ function collectDiagnostics(
129
129
  if (next !== undefined && next !== null && canContinue(content, next)) {
130
130
  const nextLeadingSpace = /^\s*/u.exec(next.content)?.[0] ?? "";
131
131
  const separator = nextLeadingSpace === "" && needsSpace(content, next.content) ? " " : "";
132
-
133
132
  fixes.push({
134
133
  end: next.start,
135
134
  loc: {
@@ -145,6 +144,7 @@ function collectDiagnostics(
145
144
  text: separator,
146
145
  start: info.end,
147
146
  });
147
+
148
148
  continue;
149
149
  }
150
150
 
@@ -205,7 +205,6 @@ const commentOneSentencePerLine = defineRule({
205
205
 
206
206
  for (const info of diagnostics.missingPunctuation) {
207
207
  const column = info.column + info.content.trimEnd().length;
208
-
209
208
  context.report({
210
209
  loc: {
211
210
  end: {
@@ -101,13 +101,13 @@ function getJSDocLines(comment: Comment, sourceCode: SourceCode): JSDocLine[] |
101
101
  const closeIndex = line.lastIndexOf("*/");
102
102
  const contentEnd = closeIndex === -1 ? line.length : Math.max(contentStart, closeIndex);
103
103
  const content = line.slice(contentStart, contentEnd);
104
-
105
104
  lines.push({
106
105
  raw: line,
107
106
  prefix: line.slice(0, contentStart),
108
107
  content,
109
108
  tagName: getTagName(content),
110
109
  });
110
+
111
111
  continue;
112
112
  }
113
113
 
@@ -122,7 +122,6 @@ function getJSDocLines(comment: Comment, sourceCode: SourceCode): JSDocLine[] |
122
122
  const closeIndex = line.lastIndexOf("*/");
123
123
  const contentEnd = closeIndex === -1 ? line.length : Math.max(contentStart, closeIndex);
124
124
  const content = line.slice(contentStart, contentEnd);
125
-
126
125
  lines.push({
127
126
  raw: line,
128
127
  prefix,
@@ -130,8 +130,8 @@ function compareUnits(left: NamedSortUnit, right: NamedSortUnit): number {
130
130
 
131
131
  function sortGroup(group: readonly SortUnit[]): SortUnit[] {
132
132
  const sortable = group.filter((unit): unit is NamedSortUnit => unit.name !== null);
133
-
134
133
  sortable.sort(compareUnits);
134
+
135
135
  const rest = group.filter((unit) => unit.name === null);
136
136
 
137
137
  return [...sortable, ...rest];
@@ -181,7 +181,6 @@ function getSortPlan(
181
181
 
182
182
  for (const property of node.properties) {
183
183
  const isRest = property.type === "RestElement";
184
-
185
184
  units.push({
186
185
  name: isRest ? null : getPropertyName(property),
187
186
  text: source.slice(property.range[0], property.range[1]),
@@ -246,7 +245,6 @@ function getSortPlan(
246
245
  const startLine = first.loc.start.line;
247
246
  const unitStart = lineStarts[startLine - 1] ?? 0;
248
247
  const isRest = property.type === "RestElement";
249
-
250
248
  units.push({
251
249
  name: isRest ? null : getPropertyName(property),
252
250
  text: source.slice(unitStart, property.range[1]),
@@ -254,6 +252,7 @@ function getSortPlan(
254
252
  property,
255
253
  startLine,
256
254
  });
255
+
257
256
  previousProperty = property;
258
257
  }
259
258
 
@@ -1,7 +1,7 @@
1
1
  import { defineRule } from "vite-plus/lint/plugins";
2
- import type { Fix, Fixer, ESTree, Context, SourceCode } from "vite-plus/lint/plugins";
2
+ import type { ESTree, Context, SourceCode } from "vite-plus/lint/plugins";
3
3
 
4
- import { getNextStatement } from "./blank-line.js";
4
+ import { getNextStatement, getRemoveBlankLineFix } from "./blank-line.js";
5
5
 
6
6
  function getFunction(node: ESTree.Node): ESTree.Function | null {
7
7
  if (node.type === "FunctionDeclaration" || node.type === "TSDeclareFunction") {
@@ -37,32 +37,6 @@ function getKeyName(node: ESTree.MethodDefinition): string | null {
37
37
  return null;
38
38
  }
39
39
 
40
- function getRemoveBlankLinesFix(
41
- fixer: Fixer,
42
- sourceCode: SourceCode,
43
- previous: ESTree.Node,
44
- next: ESTree.Node,
45
- ): Fix | null {
46
- if (next.loc.start.line <= previous.loc.end.line + 1) {
47
- return null;
48
- }
49
-
50
- const fromLineEnd = sourceCode.text.indexOf("\n", previous.range[1]);
51
-
52
- if (fromLineEnd === -1) {
53
- return null;
54
- }
55
-
56
- const nextLineStart = sourceCode.text.lastIndexOf("\n", next.range[0]) + 1;
57
- const indent = sourceCode.text.slice(nextLineStart, next.range[0]);
58
-
59
- if (indent.trim() !== "") {
60
- return null;
61
- }
62
-
63
- return fixer.replaceTextRange([fromLineEnd, next.range[0]], `\n${indent}`);
64
- }
65
-
66
40
  function hasCommentBetween(
67
41
  sourceCode: SourceCode,
68
42
  previous: ESTree.Node,
@@ -88,7 +62,7 @@ function reportIfSeparated(
88
62
 
89
63
  context.report({
90
64
  fix(fixer) {
91
- return getRemoveBlankLinesFix(fixer, sourceCode, previous, next);
65
+ return getRemoveBlankLineFix(fixer, sourceCode, previous, next);
92
66
  },
93
67
  node: next,
94
68
  messageId: "adjacent",
@@ -130,7 +130,6 @@ const sortJsxProps = defineRule({
130
130
  const separator = multiline
131
131
  ? `\n${getLineIndent(sourceCode.text, firstAttribute.range[0])}`
132
132
  : " ";
133
-
134
133
  context.report({
135
134
  fix(fixer) {
136
135
  return fixer.replaceTextRange(
@@ -176,8 +176,8 @@ function splitGroups(units: readonly MemberUnit[]): MemberUnit[][] {
176
176
 
177
177
  function sortGroup(group: readonly MemberUnit[]): MemberUnit[] {
178
178
  const sortable = group.filter((unit): unit is NamedUnit => unit.name !== null);
179
-
180
179
  sortable.sort((left, right) => compareNames(left.name, right.name));
180
+
181
181
  const rest = group.filter((unit) => unit.name === null);
182
182
 
183
183
  return [...sortable, ...rest];
@@ -70,7 +70,6 @@ const spacedComment = defineRule({
70
70
  const contentStart = prefixLength;
71
71
  const contentEnd = raw.lastIndexOf("*/");
72
72
  const content = raw.slice(contentStart, contentEnd);
73
-
74
73
  needsLeading = content !== "" && !isWhitespace(content[0]);
75
74
  needsTrailing = content !== "" && !isWhitespace(content.at(-1));
76
75
  } else {
@@ -79,7 +78,6 @@ const spacedComment = defineRule({
79
78
  const leadingContent = firstLine.slice(prefixLength);
80
79
  const closeIndex = lastLine.lastIndexOf("*/");
81
80
  const trailingContent = lastLine.slice(0, closeIndex);
82
-
83
81
  needsLeading = leadingContent !== "" && !isWhitespace(leadingContent[0]);
84
82
  needsTrailing = trailingContent !== "" && !isWhitespace(trailingContent.at(-1));
85
83
  }