Skip to content

Commit afafd3f

Browse files
committed
simple-indent
1 parent d1ae830 commit afafd3f

File tree

9 files changed

+462
-16
lines changed

9 files changed

+462
-16
lines changed
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import { TSESTree } from "@typescript-eslint/typescript-estree";
2+
import { createRule } from "./utils";
3+
4+
type MessageId = "simpleIndentError";
5+
type Options = any;
6+
7+
export = createRule<Options, MessageId>({
8+
name: "simple-indent",
9+
meta: {
10+
docs: {
11+
description: "Enforce consistent indentation",
12+
category: "Stylistic Issues",
13+
recommended: "error",
14+
},
15+
messages: {
16+
simpleIndentError: "4 space indentation expected",
17+
},
18+
fixable: 'whitespace',
19+
schema: [],
20+
type: "layout",
21+
},
22+
defaultOptions: [],
23+
create(context) {
24+
const TAB_SIZE = 4;
25+
const TAB_REGEX = /\t/g;
26+
const sourceCode = context.getSourceCode();
27+
const linebreaks = sourceCode.getText().match(/\r\n|[\r\n\u2028\u2029]/gu);
28+
29+
const checkIndent = (node: TSESTree.Program) => {
30+
const lines = sourceCode.getLines();
31+
const linesLen = lines.length;
32+
let totalLen = 0;
33+
34+
for (let i = 0; i < linesLen; i++) {
35+
const lineNumber = i + 1;
36+
const line = lines[i];
37+
const linebreaksLen = linebreaks && linebreaks[i] ? linebreaks[i].length : 1;
38+
const lineLen = line.length + linebreaksLen;
39+
const matches = /\S/.exec(line);
40+
41+
if (matches && matches.index) {
42+
const indentEnd = matches.index;
43+
const whitespace = line.slice(0, indentEnd);
44+
45+
if (!TAB_REGEX.test(whitespace)) {
46+
totalLen += lineLen;
47+
continue;
48+
}
49+
50+
context.report({
51+
messageId: 'simpleIndentError',
52+
node,
53+
loc: { column: indentEnd, line: lineNumber },
54+
fix(fixer) {
55+
const rangeStart = totalLen;
56+
const rangeEnd = rangeStart + indentEnd;
57+
58+
return fixer
59+
.replaceTextRange([rangeStart, rangeEnd], whitespace.replace(TAB_REGEX, " ".repeat(TAB_SIZE)));
60+
}
61+
});
62+
}
63+
64+
totalLen += lineLen;
65+
}
66+
}
67+
68+
return {
69+
Program: checkIndent,
70+
}
71+
},
72+
});

scripts/eslint/tests/boolean-trivia.test.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
1-
import { RuleTester, ROOT_DIR } from "./support/RuleTester";
1+
import { RuleTester, ROOT_DIR, FILENAME } from "./support/RuleTester";
22
import rule = require("../rules/boolean-trivia");
33

4-
5-
64
const ruleTester = new RuleTester({
75
parserOptions: {
86
warnOnUnsupportedTypeScriptVersion: false,
@@ -17,53 +15,62 @@ const ruleTester = new RuleTester({
1715

1816
ruleTester.run("boolean-trivia", rule, {
1917
valid: [{
18+
filename: FILENAME,
2019
code: `
2120
const fn = (prop: boolean) => {};
2221
fn(/* boolean prop */ true);
2322
`,
2423
}, {
24+
filename: FILENAME,
2525
code: `
2626
const fn = (prop: null) => {};
2727
fn(/* null prop */ null);
2828
`,
2929
}, {
30+
filename: FILENAME,
3031
code: `
3132
const fn = (prop: null) => {};
3233
fn(/*null prop*/ null);
3334
`,
3435
}, {
36+
filename: FILENAME,
3537
code: `
3638
const fn = (prop: boolean) => {};
3739
fn(/* comment */
3840
false
3941
);
4042
`,
4143
}, {
44+
filename: FILENAME,
4245
code: `
4346
const fn = (prop: boolean) => {};
4447
fn.apply(null, true);
4548
`,
4649
}],
4750

4851
invalid: [{
52+
filename: FILENAME,
4953
code: `
5054
const fn = (prop: null) => {};
5155
fn(null);
5256
`,
5357
errors: [{ messageId: "booleanTriviaArgumentError" }],
5458
}, {
59+
filename: FILENAME,
5560
code: `
5661
const fn = (prop: boolean) => {};
5762
fn(false);
5863
`,
5964
errors: [{ messageId: "booleanTriviaArgumentError" }],
6065
}, {
66+
filename: FILENAME,
6167
code: `
6268
const fn = (prop: boolean) => {};
6369
fn(/* boolean arg */false);
6470
`,
6571
errors: [{ messageId: "booleanTriviaArgumentSpaceError" }],
6672
}, {
73+
filename: FILENAME,
6774
code: `
6875
const fn = (prop: boolean) => {};
6976
fn(/* first comment */ /* second comment */ false);

scripts/eslint/tests/fixtures/file.ts

Whitespace-only changes.
Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
{
2-
"compilerOptions": {
3-
"esModuleInterop": true,
4-
"target": "es5",
5-
"module": "commonjs",
6-
"strict": true,
7-
"lib": ["es2015", "es2017", "esnext"]
8-
}
9-
}
1+
{
2+
"compilerOptions": {
3+
"esModuleInterop": true,
4+
"target": "es5",
5+
"module": "commonjs",
6+
"strict": true,
7+
"lib": ["es2015", "es2017", "esnext"],
8+
}
9+
}

scripts/eslint/tests/no-double-space.test.ts

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { RuleTester, ROOT_DIR } from "./support/RuleTester";
1+
import { RuleTester, ROOT_DIR, FILENAME } from "./support/RuleTester";
22
import rule = require("../rules/no-double-space");
33

44
const ruleTester = new RuleTester({
@@ -15,58 +15,77 @@ const ruleTester = new RuleTester({
1515

1616
ruleTester.run("no-double-space", rule, {
1717
valid: [{
18+
filename: FILENAME,
1819
code: `const a = {};`,
1920
}, {
21+
filename: FILENAME,
2022
code: `function fn() {}`,
2123
}, {
24+
filename: FILENAME,
2225
code: `const a = " ";`,
2326
}, {
27+
filename: FILENAME,
2428
code: `// ^ ^`,
2529
}, {
30+
filename: FILENAME,
2631
code: `class Cl {}`,
2732
}, {
33+
filename: FILENAME,
2834
code: `// comment `,
2935
}, {
36+
filename: FILENAME,
3037
code: `/* comment */`,
3138
}, {
39+
filename: FILENAME,
3240
code: `" string ";`,
3341
}, {
42+
filename: FILENAME,
3443
code: `/ regexp /g;`,
3544
}, {
45+
filename: FILENAME,
3646
code: `const rgx = / regexp /g;`,
3747
}, {
48+
filename: FILENAME,
3849
code: "const str = ` string template`;",
3950
}, {
51+
filename: FILENAME,
4052
code: ` // comment`,
4153
}, {
54+
filename: FILENAME,
4255
code: ` /* comment */`,
4356
}, {
57+
filename: FILENAME,
4458
code: `// `,
4559
}, {
60+
filename: FILENAME,
4661
code: `
4762
const a =
4863
1;
4964
`,
5065
}, {
66+
filename: FILENAME,
5167
code: `
5268
/**
5369
* comment
5470
*/
5571
`,
5672
}, {
73+
filename: FILENAME,
5774
code: `
5875
// comment
5976
// - comment
6077
// - comment
6178
`,
6279
}, {
80+
filename: FILENAME,
6381
code: `
6482
interface Props {
6583
prop: string[]; // comment prop
6684
propB: string[]; // comment propB
6785
}
6886
`,
6987
}, {
88+
filename: FILENAME,
7089
code: `
7190
/**
7291
* Returns a JSON-encoded value of the type: string[]
@@ -76,6 +95,7 @@ interface Props {
7695
*/
7796
`,
7897
}, {
98+
filename: FILENAME,
7999
code: `
80100
const obj = {
81101
content: "function f() { 1; }",
@@ -84,27 +104,35 @@ const obj = {
84104
}],
85105

86106
invalid: [{
107+
filename: FILENAME,
87108
code: `const a = {};`,
88109
errors: [{ messageId: "noDoubleSpaceError", line: 1, column: 6 }],
89110
}, {
111+
filename: FILENAME,
90112
code: `function fn() {}`,
91113
errors: [{ messageId: "noDoubleSpaceError", line: 1, column: 9 }],
92114
}, {
115+
filename: FILENAME,
93116
code: `class Cl {}`,
94117
errors: [{ messageId: "noDoubleSpaceError", line: 1, column: 6 }],
95118
}, {
119+
filename: FILENAME,
96120
code: "const str = ` string template`;",
97121
errors: [{ messageId: "noDoubleSpaceError", line: 1, column: 12 }],
98122
}, {
123+
filename: FILENAME,
99124
code: `/** comment */`,
100125
errors: [{ messageId: "noDoubleSpaceError", line: 1, column: 12 }],
101126
}, {
127+
filename: FILENAME,
102128
code: `/** comment with many spaces */`,
103129
errors: [{ messageId: "noDoubleSpaceError", line: 1, column: 12 }],
104130
}, {
131+
filename: FILENAME,
105132
code: `// comment with many spaces`,
106133
errors: [{ messageId: "noDoubleSpaceError", line: 1, column: 11 }],
107134
}, {
135+
filename: FILENAME,
108136
code: `
109137
const a = 1;
110138
const b = 2;

0 commit comments

Comments
 (0)