Skip to content

Commit 8ab980b

Browse files
authored
Update indent rule to support Class static block and typescript-eslint v5(rc) (#51)
* Update indent rule to support typescript-eslint/parser v5. * fix * Add testcase
1 parent 7b87848 commit 8ab980b

File tree

7 files changed

+231
-53
lines changed

7 files changed

+231
-53
lines changed

package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,9 @@
6767
"@types/eslint-visitor-keys": "^1.0.0",
6868
"@types/mocha": "^9.0.0",
6969
"@types/node": "^15.0.1",
70-
"@typescript-eslint/eslint-plugin": "^4.22.0",
71-
"@typescript-eslint/parser": "^4.22.0",
70+
"@typescript-eslint/eslint-plugin": "^5.0.0-0",
71+
"@typescript-eslint/parser": "^5.0.0-0",
72+
"@typescript-eslint/parser-v4": "npm:@typescript-eslint/parser@4",
7273
"env-cmd": "^10.1.0",
7374
"eslint": "^7.25.0",
7475
"eslint-config-prettier": "^8.3.0",

src/rules/indent-helpers/ts.ts

Lines changed: 109 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
isClosingBracketToken,
55
isClosingParenToken,
66
isNotClosingParenToken,
7+
isNotOpeningBraceToken,
78
isOpeningBraceToken,
89
isOpeningBracketToken,
910
isOpeningParenToken,
@@ -297,15 +298,6 @@ export function defineVisitor(context: IndentContext): NodeListener {
297298
// A & B
298299
visitor.TSUnionType(node)
299300
},
300-
TSParenthesizedType(node: TSESTree.TSParenthesizedType) {
301-
// (T)
302-
offsets.setOffsetElementList(
303-
[node.typeAnnotation],
304-
sourceCode.getFirstToken(node),
305-
sourceCode.getLastToken(node),
306-
1,
307-
)
308-
},
309301
TSMappedType(node: TSESTree.TSMappedType) {
310302
// {[key in foo]: bar}
311303
const leftBraceToken = sourceCode.getFirstToken(node)
@@ -728,8 +720,7 @@ export function defineVisitor(context: IndentContext): NodeListener {
728720
TSAbstractMethodDefinition(
729721
node:
730722
| TSESTree.TSAbstractMethodDefinition
731-
| TSESTree.TSAbstractClassProperty
732-
| TSESTree.ClassProperty
723+
| TSESTree.TSAbstractPropertyDefinition
733724
| TSESTree.TSEnumMember,
734725
) {
735726
const { keyNode, valueNode } =
@@ -777,7 +768,7 @@ export function defineVisitor(context: IndentContext): NodeListener {
777768
)
778769
}
779770
},
780-
TSAbstractClassProperty(node: TSESTree.TSAbstractClassProperty) {
771+
TSAbstractPropertyDefinition(node: TSESTree.TSAbstractPropertyDefinition) {
781772
visitor.TSAbstractMethodDefinition(node)
782773
},
783774
TSEnumMember(node: TSESTree.TSEnumMember) {
@@ -951,9 +942,6 @@ export function defineVisitor(context: IndentContext): NodeListener {
951942
// ----------------------------------------------------------------------
952943
// NON-STANDARD NODES
953944
// ----------------------------------------------------------------------
954-
ClassProperty(node: TSESTree.ClassProperty) {
955-
visitor.TSAbstractMethodDefinition(node)
956-
},
957945
Decorator(node: TSESTree.Decorator) {
958946
// @Decorator
959947
const [atToken, secondToken] = sourceCode.getFirstTokens(node, {
@@ -992,6 +980,21 @@ export function defineVisitor(context: IndentContext): NodeListener {
992980
)
993981
}
994982
},
983+
StaticBlock(node: TSESTree.StaticBlock) {
984+
const firstToken = sourceCode.getFirstToken(node)
985+
let next = sourceCode.getTokenAfter(firstToken)
986+
while (next && isNotOpeningBraceToken(next)) {
987+
offsets.setOffsetToken(next, 0, firstToken)
988+
next = sourceCode.getTokenAfter(next)
989+
}
990+
offsets.setOffsetToken(next, 0, firstToken)
991+
offsets.setOffsetElementList(
992+
node.body,
993+
next!,
994+
sourceCode.getLastToken(node),
995+
1,
996+
)
997+
},
995998
// ----------------------------------------------------------------------
996999
// SINGLE TOKEN NODES
9971000
// ----------------------------------------------------------------------
@@ -1080,10 +1083,9 @@ export function defineVisitor(context: IndentContext): NodeListener {
10801083
const commonsVisitor: any = {
10811084
// Process semicolons.
10821085
["TSTypeAliasDeclaration, TSCallSignatureDeclaration, TSConstructSignatureDeclaration, TSImportEqualsDeclaration," +
1083-
"TSAbstractMethodDefinition, TSAbstractClassProperty, TSEnumMember, ClassProperty," +
1084-
"TSPropertySignature, TSIndexSignature, TSMethodSignature"](
1085-
node: ESTree.Node,
1086-
) {
1086+
"TSAbstractMethodDefinition, TSAbstractPropertyDefinition, TSEnumMember," +
1087+
"TSPropertySignature, TSIndexSignature, TSMethodSignature," +
1088+
"TSAbstractClassProperty, ClassProperty"](node: ESTree.Node) {
10871089
const firstToken = sourceCode.getFirstToken(node)
10881090
const lastToken = sourceCode.getLastToken(node)
10891091
if (isSemicolonToken(lastToken) && firstToken !== lastToken) {
@@ -1094,6 +1096,71 @@ export function defineVisitor(context: IndentContext): NodeListener {
10941096
}
10951097
}
10961098
},
1099+
// eslint-disable-next-line complexity -- ignore
1100+
"*[type=/^TS/]"(node: TSESTree.Node) {
1101+
if (
1102+
node.type !== "TSAnyKeyword" &&
1103+
node.type !== "TSArrayType" &&
1104+
node.type !== "TSBigIntKeyword" &&
1105+
node.type !== "TSBooleanKeyword" &&
1106+
node.type !== "TSConditionalType" &&
1107+
node.type !== "TSConstructorType" &&
1108+
node.type !== "TSFunctionType" &&
1109+
node.type !== "TSImportType" &&
1110+
node.type !== "TSIndexedAccessType" &&
1111+
node.type !== "TSInferType" &&
1112+
node.type !== "TSIntersectionType" &&
1113+
node.type !== "TSIntrinsicKeyword" &&
1114+
node.type !== "TSLiteralType" &&
1115+
node.type !== "TSMappedType" &&
1116+
node.type !== "TSNamedTupleMember" &&
1117+
node.type !== "TSNeverKeyword" &&
1118+
node.type !== "TSNullKeyword" &&
1119+
node.type !== "TSNumberKeyword" &&
1120+
node.type !== "TSObjectKeyword" &&
1121+
node.type !== "TSOptionalType" &&
1122+
node.type !== "TSRestType" &&
1123+
node.type !== "TSStringKeyword" &&
1124+
node.type !== "TSSymbolKeyword" &&
1125+
node.type !== "TSTemplateLiteralType" &&
1126+
node.type !== "TSThisType" &&
1127+
node.type !== "TSTupleType" &&
1128+
node.type !== "TSTypeLiteral" &&
1129+
node.type !== "TSTypeOperator" &&
1130+
node.type !== "TSTypePredicate" &&
1131+
node.type !== "TSTypeQuery" &&
1132+
node.type !== "TSTypeReference" &&
1133+
node.type !== "TSUndefinedKeyword" &&
1134+
node.type !== "TSUnionType" &&
1135+
node.type !== "TSUnknownKeyword" &&
1136+
node.type !== "TSVoidKeyword"
1137+
) {
1138+
return
1139+
}
1140+
const typeNode: TSESTree.TypeNode = node
1141+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- ignore
1142+
if ((typeNode.parent as any).type === "TSParenthesizedType") {
1143+
return
1144+
}
1145+
// Process parentheses.
1146+
let leftToken = sourceCode.getTokenBefore(typeNode)
1147+
let rightToken = sourceCode.getTokenAfter(typeNode)
1148+
let firstToken = sourceCode.getFirstToken(typeNode)
1149+
1150+
while (
1151+
leftToken &&
1152+
isOpeningParenToken(leftToken) &&
1153+
rightToken &&
1154+
isClosingParenToken(rightToken)
1155+
) {
1156+
offsets.setOffsetToken(firstToken, 1, leftToken)
1157+
offsets.setOffsetToken(rightToken, 0, leftToken)
1158+
1159+
firstToken = leftToken
1160+
leftToken = sourceCode.getTokenBefore(leftToken)
1161+
rightToken = sourceCode.getTokenAfter(rightToken)
1162+
}
1163+
},
10971164
}
10981165

10991166
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- ignore
@@ -1125,11 +1192,34 @@ export function defineVisitor(context: IndentContext): NodeListener {
11251192
}
11261193
},
11271194
}
1195+
1196+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- ignore
1197+
const deprecatedVisitor: any = {
1198+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- ignore
1199+
TSParenthesizedType(node: any) {
1200+
// (T)
1201+
offsets.setOffsetElementList(
1202+
[node.typeAnnotation],
1203+
sourceCode.getFirstToken(node),
1204+
sourceCode.getLastToken(node),
1205+
1,
1206+
)
1207+
},
1208+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- ignore
1209+
ClassProperty(node: any) {
1210+
visitor.TSAbstractMethodDefinition(node)
1211+
},
1212+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- ignore
1213+
TSAbstractClassProperty(node: any) {
1214+
visitor.TSAbstractMethodDefinition(node)
1215+
},
1216+
}
11281217
const v: NodeListener = visitor
11291218

11301219
return {
11311220
...v,
11321221
...commonsVisitor,
11331222
...extendsESVisitor,
1223+
...deprecatedVisitor,
11341224
}
11351225
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
[
2+
{
3+
"message": "Expected indentation of 2 spaces but found 0 spaces.",
4+
"line": 3,
5+
"column": 1
6+
},
7+
{
8+
"message": "Expected indentation of 4 spaces but found 0 spaces.",
9+
"line": 4,
10+
"column": 1
11+
},
12+
{
13+
"message": "Expected indentation of 6 spaces but found 0 spaces.",
14+
"line": 5,
15+
"column": 1
16+
},
17+
{
18+
"message": "Expected indentation of 8 spaces but found 0 spaces.",
19+
"line": 6,
20+
"column": 1
21+
},
22+
{
23+
"message": "Expected indentation of 6 spaces but found 0 spaces.",
24+
"line": 7,
25+
"column": 1
26+
},
27+
{
28+
"message": "Expected indentation of 4 spaces but found 0 spaces.",
29+
"line": 8,
30+
"column": 1
31+
},
32+
{
33+
"message": "Expected indentation of 2 spaces but found 0 spaces.",
34+
"line": 9,
35+
"column": 1
36+
}
37+
]
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<!-- prettier-ignore -->
2+
<script lang="ts">
3+
class Foo {
4+
static {
5+
foo(
6+
arg
7+
)
8+
}
9+
}
10+
</script>
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<!-- prettier-ignore -->
2+
<script lang="ts">
3+
class Foo {
4+
static {
5+
foo(
6+
arg
7+
)
8+
}
9+
}
10+
</script>

tests/src/rules/indent.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { RuleTester } from "eslint"
2+
import path from "path"
23
import rule from "../../../src/rules/indent"
34
import { loadTestCases } from "../../utils/utils"
45

@@ -14,3 +15,26 @@ const tester = new RuleTester({
1415
})
1516

1617
tester.run("indent", rule as any, loadTestCases("indent"))
18+
19+
const testerForTsParserV4 = new RuleTester({
20+
parserOptions: {
21+
ecmaVersion: 2020,
22+
sourceType: "module",
23+
parser: {
24+
ts: "@typescript-eslint/parser-v4",
25+
js: "espree",
26+
},
27+
},
28+
})
29+
30+
describe("use @typescript-eslint/parser@4", () => {
31+
testerForTsParserV4.run(
32+
"indent",
33+
rule as any,
34+
loadTestCases("indent", {
35+
filter(filename) {
36+
return path.basename(path.dirname(filename)) === "ts"
37+
},
38+
}),
39+
)
40+
})

tests/utils/utils.ts

Lines changed: 38 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -56,10 +56,12 @@ function getMinIndent(lines: string[]) {
5656
*/
5757
export function loadTestCases(
5858
ruleName: string,
59-
_options?: any,
60-
additionals?: {
61-
valid?: (RuleTester.ValidTestCase | string)[]
62-
invalid?: RuleTester.InvalidTestCase[]
59+
options?: {
60+
additionals?: {
61+
valid?: (RuleTester.ValidTestCase | string)[]
62+
invalid?: RuleTester.InvalidTestCase[]
63+
}
64+
filter: (file: string) => boolean
6365
},
6466
): {
6567
valid: RuleTester.ValidTestCase[]
@@ -74,44 +76,48 @@ export function loadTestCases(
7476
`../fixtures/rules/${ruleName}/invalid/`,
7577
)
7678

77-
const valid = listupInput(validFixtureRoot).map((inputFile) =>
78-
getConfig(ruleName, inputFile),
79-
)
79+
const filter = options?.filter ?? (() => true)
80+
81+
const valid = listupInput(validFixtureRoot)
82+
.filter(filter)
83+
.map((inputFile) => getConfig(ruleName, inputFile))
8084

8185
const fixable = plugin.rules[ruleName].meta.fixable != null
8286

83-
const invalid = listupInput(invalidFixtureRoot).map((inputFile) => {
84-
const config = getConfig(ruleName, inputFile)
85-
const errorFile = inputFile.replace(/input\.svelte$/u, "errors.json")
86-
const outputFile = inputFile.replace(/input\.svelte$/u, "output.svelte")
87-
let errors
88-
try {
89-
errors = fs.readFileSync(errorFile, "utf8")
90-
} catch (e) {
91-
writeFixtures(ruleName, inputFile)
92-
errors = fs.readFileSync(errorFile, "utf8")
93-
}
94-
config.errors = JSON.parse(errors)
95-
if (fixable) {
96-
let output
87+
const invalid = listupInput(invalidFixtureRoot)
88+
.filter(filter)
89+
.map((inputFile) => {
90+
const config = getConfig(ruleName, inputFile)
91+
const errorFile = inputFile.replace(/input\.svelte$/u, "errors.json")
92+
const outputFile = inputFile.replace(/input\.svelte$/u, "output.svelte")
93+
let errors
9794
try {
98-
output = fs.readFileSync(outputFile, "utf8")
95+
errors = fs.readFileSync(errorFile, "utf8")
9996
} catch (e) {
10097
writeFixtures(ruleName, inputFile)
101-
output = fs.readFileSync(outputFile, "utf8")
98+
errors = fs.readFileSync(errorFile, "utf8")
99+
}
100+
config.errors = JSON.parse(errors)
101+
if (fixable) {
102+
let output
103+
try {
104+
output = fs.readFileSync(outputFile, "utf8")
105+
} catch (e) {
106+
writeFixtures(ruleName, inputFile)
107+
output = fs.readFileSync(outputFile, "utf8")
108+
}
109+
config.output = output
102110
}
103-
config.output = output
104-
}
105111

106-
return config
107-
})
112+
return config
113+
})
108114

109-
if (additionals) {
110-
if (additionals.valid) {
111-
valid.push(...additionals.valid)
115+
if (options?.additionals) {
116+
if (options.additionals.valid) {
117+
valid.push(...options.additionals.valid)
112118
}
113-
if (additionals.invalid) {
114-
invalid.push(...additionals.invalid)
119+
if (options.additionals.invalid) {
120+
invalid.push(...options.additionals.invalid)
115121
}
116122
}
117123
for (const test of valid) {

0 commit comments

Comments
 (0)