simple-indent

This commit is contained in:
Alexander T 2019-08-02 18:27:06 +03:00 committed by Alexander
parent d1ae830def
commit afafd3fcb3
9 changed files with 462 additions and 16 deletions

View File

@ -0,0 +1,72 @@
import { TSESTree } from "@typescript-eslint/typescript-estree";
import { createRule } from "./utils";
type MessageId = "simpleIndentError";
type Options = any;
export = createRule<Options, MessageId>({
name: "simple-indent",
meta: {
docs: {
description: "Enforce consistent indentation",
category: "Stylistic Issues",
recommended: "error",
},
messages: {
simpleIndentError: "4 space indentation expected",
},
fixable: 'whitespace',
schema: [],
type: "layout",
},
defaultOptions: [],
create(context) {
const TAB_SIZE = 4;
const TAB_REGEX = /\t/g;
const sourceCode = context.getSourceCode();
const linebreaks = sourceCode.getText().match(/\r\n|[\r\n\u2028\u2029]/gu);
const checkIndent = (node: TSESTree.Program) => {
const lines = sourceCode.getLines();
const linesLen = lines.length;
let totalLen = 0;
for (let i = 0; i < linesLen; i++) {
const lineNumber = i + 1;
const line = lines[i];
const linebreaksLen = linebreaks && linebreaks[i] ? linebreaks[i].length : 1;
const lineLen = line.length + linebreaksLen;
const matches = /\S/.exec(line);
if (matches && matches.index) {
const indentEnd = matches.index;
const whitespace = line.slice(0, indentEnd);
if (!TAB_REGEX.test(whitespace)) {
totalLen += lineLen;
continue;
}
context.report({
messageId: 'simpleIndentError',
node,
loc: { column: indentEnd, line: lineNumber },
fix(fixer) {
const rangeStart = totalLen;
const rangeEnd = rangeStart + indentEnd;
return fixer
.replaceTextRange([rangeStart, rangeEnd], whitespace.replace(TAB_REGEX, " ".repeat(TAB_SIZE)));
}
});
}
totalLen += lineLen;
}
}
return {
Program: checkIndent,
}
},
});

View File

@ -1,8 +1,6 @@
import { RuleTester, ROOT_DIR } from "./support/RuleTester";
import { RuleTester, ROOT_DIR, FILENAME } from "./support/RuleTester";
import rule = require("../rules/boolean-trivia");
const ruleTester = new RuleTester({
parserOptions: {
warnOnUnsupportedTypeScriptVersion: false,
@ -17,21 +15,25 @@ const ruleTester = new RuleTester({
ruleTester.run("boolean-trivia", rule, {
valid: [{
filename: FILENAME,
code: `
const fn = (prop: boolean) => {};
fn(/* boolean prop */ true);
`,
}, {
filename: FILENAME,
code: `
const fn = (prop: null) => {};
fn(/* null prop */ null);
`,
}, {
filename: FILENAME,
code: `
const fn = (prop: null) => {};
fn(/*null prop*/ null);
`,
}, {
filename: FILENAME,
code: `
const fn = (prop: boolean) => {};
fn(/* comment */
@ -39,6 +41,7 @@ fn(/* comment */
);
`,
}, {
filename: FILENAME,
code: `
const fn = (prop: boolean) => {};
fn.apply(null, true);
@ -46,24 +49,28 @@ fn.apply(null, true);
}],
invalid: [{
filename: FILENAME,
code: `
const fn = (prop: null) => {};
fn(null);
`,
errors: [{ messageId: "booleanTriviaArgumentError" }],
}, {
filename: FILENAME,
code: `
const fn = (prop: boolean) => {};
fn(false);
`,
errors: [{ messageId: "booleanTriviaArgumentError" }],
}, {
filename: FILENAME,
code: `
const fn = (prop: boolean) => {};
fn(/* boolean arg */false);
`,
errors: [{ messageId: "booleanTriviaArgumentSpaceError" }],
}, {
filename: FILENAME,
code: `
const fn = (prop: boolean) => {};
fn(/* first comment */ /* second comment */ false);

0
scripts/eslint/tests/fixtures/file.ts vendored Normal file
View File

View File

@ -1,9 +1,9 @@
{
"compilerOptions": {
"esModuleInterop": true,
"target": "es5",
"module": "commonjs",
"strict": true,
"lib": ["es2015", "es2017", "esnext"]
}
}
{
"compilerOptions": {
"esModuleInterop": true,
"target": "es5",
"module": "commonjs",
"strict": true,
"lib": ["es2015", "es2017", "esnext"],
}
}

View File

@ -1,4 +1,4 @@
import { RuleTester, ROOT_DIR } from "./support/RuleTester";
import { RuleTester, ROOT_DIR, FILENAME } from "./support/RuleTester";
import rule = require("../rules/no-double-space");
const ruleTester = new RuleTester({
@ -15,51 +15,69 @@ const ruleTester = new RuleTester({
ruleTester.run("no-double-space", rule, {
valid: [{
filename: FILENAME,
code: `const a = {};`,
}, {
filename: FILENAME,
code: `function fn() {}`,
}, {
filename: FILENAME,
code: `const a = " ";`,
}, {
filename: FILENAME,
code: `// ^ ^`,
}, {
filename: FILENAME,
code: `class Cl {}`,
}, {
filename: FILENAME,
code: `// comment `,
}, {
filename: FILENAME,
code: `/* comment */`,
}, {
filename: FILENAME,
code: `" string ";`,
}, {
filename: FILENAME,
code: `/ regexp /g;`,
}, {
filename: FILENAME,
code: `const rgx = / regexp /g;`,
}, {
filename: FILENAME,
code: "const str = ` string template`;",
}, {
filename: FILENAME,
code: ` // comment`,
}, {
filename: FILENAME,
code: ` /* comment */`,
}, {
filename: FILENAME,
code: `// `,
}, {
filename: FILENAME,
code: `
const a =
1;
`,
}, {
filename: FILENAME,
code: `
/**
* comment
*/
`,
}, {
filename: FILENAME,
code: `
// comment
// - comment
// - comment
`,
}, {
filename: FILENAME,
code: `
interface Props {
prop: string[]; // comment prop
@ -67,6 +85,7 @@ interface Props {
}
`,
}, {
filename: FILENAME,
code: `
/**
* Returns a JSON-encoded value of the type: string[]
@ -76,6 +95,7 @@ interface Props {
*/
`,
}, {
filename: FILENAME,
code: `
const obj = {
content: "function f() { 1; }",
@ -84,27 +104,35 @@ const obj = {
}],
invalid: [{
filename: FILENAME,
code: `const a = {};`,
errors: [{ messageId: "noDoubleSpaceError", line: 1, column: 6 }],
}, {
filename: FILENAME,
code: `function fn() {}`,
errors: [{ messageId: "noDoubleSpaceError", line: 1, column: 9 }],
}, {
filename: FILENAME,
code: `class Cl {}`,
errors: [{ messageId: "noDoubleSpaceError", line: 1, column: 6 }],
}, {
filename: FILENAME,
code: "const str = ` string template`;",
errors: [{ messageId: "noDoubleSpaceError", line: 1, column: 12 }],
}, {
filename: FILENAME,
code: `/** comment */`,
errors: [{ messageId: "noDoubleSpaceError", line: 1, column: 12 }],
}, {
filename: FILENAME,
code: `/** comment with many spaces */`,
errors: [{ messageId: "noDoubleSpaceError", line: 1, column: 12 }],
}, {
filename: FILENAME,
code: `// comment with many spaces`,
errors: [{ messageId: "noDoubleSpaceError", line: 1, column: 11 }],
}, {
filename: FILENAME,
code: `
const a = 1;
const b = 2;

View File

@ -0,0 +1,333 @@
import { RuleTester } from "./support/RuleTester";
import rule = require("../rules/simple-indent");
const ruleTester = new RuleTester({
parserOptions: {
warnOnUnsupportedTypeScriptVersion: false,
},
parser: require.resolve("@typescript-eslint/parser"),
});
ruleTester.run("simple-indent", rule, {
valid: [
{
code: `
/**
* Comment
*/
`
},
{
code: `
module TestModule {
var func = () => {
console.warn("hi");
};
}
`,
},
{
code: `
class TestClass {
private variable;
testFunction() {
this.variable = 3;
}
}
`,
},
{
code: `
var obj = {
a: 1,
b: 2,
c: 3
};
`,
},
{
code: `
export enum TestEnum {
VALUE1,
VALUE2
}
`,
},
{
code: `
switch (integerValue) {
case 1:
console.warn("1");
break;
default:
console.warn("default");
break;
}
`,
},
{
code: `
function loops() {
for (var i = 0; i < 1; ++i) {
console.warn(i);
}
while (i < 1) {
console.warn(i);
}
do {
console.warn(i);
} while (i < 1);
if (i < 1) {
console.warn(i);
} else {
console.warn(i + 1);
}
}
`,
},
],
invalid: [
{
code: `
module TestModule {
\tvar testVariable = 123;
}
`,
output: `
module TestModule {
var testVariable = 123;
}
`,
errors: [
{ messageId: 'simpleIndentError', line: 3, column: 2 },
],
},
{
code: `
function a() {
\t\tvar test = 123;
}
`,
output: `
function a() {
var test = 123;
}
`,
errors: [
{ messageId: 'simpleIndentError', line: 3, column: 3 },
],
},
{
code: `
class TestClass {
\tprivate variable;
\ttestFunction() {
\t\tthis.variable = 3;
\t}
}
`,
output: `
class TestClass {
private variable;
testFunction() {
this.variable = 3;
}
}
`,
errors: [
{ messageId: 'simpleIndentError', line: 3, column: 2 },
{ messageId: 'simpleIndentError', line: 5, column: 2 },
{ messageId: 'simpleIndentError', line: 6, column: 3 },
{ messageId: 'simpleIndentError', line: 7, column: 2 },
],
},
{
code: `
var obj = {
\ta: 1,
\tb: 2,
\tc: 3
};
`,
output: `
var obj = {
a: 1,
b: 2,
c: 3
};
`,
errors: [
{ messageId: 'simpleIndentError', line: 3, column: 2 },
{ messageId: 'simpleIndentError', line: 4, column: 2 },
{ messageId: 'simpleIndentError', line: 5, column: 2 },
]
},
{
code: `
enum TestEnum {
\tVALUE1,
VALUE2
}
`,
output: `
enum TestEnum {
VALUE1,
VALUE2
}
`,
errors: [
{ messageId: 'simpleIndentError', line: 3, column: 2 },
],
},
{
code: `
switch (integerValue) {
\tcase 0:
\t\tconsole.warn("1");
\t\tbreak;
case 1:
console.warn("1");
break;
\tdefault:
\t\tconsole.log("2");
\t\tbreak;
}
`,
output: `
switch (integerValue) {
case 0:
console.warn("1");
break;
case 1:
console.warn("1");
break;
default:
console.log("2");
break;
}
`,
errors: [
{ messageId: 'simpleIndentError', line: 3, column: 2 },
{ messageId: 'simpleIndentError', line: 4, column: 3 },
{ messageId: 'simpleIndentError', line: 5, column: 3 },
{ messageId: 'simpleIndentError', line: 9, column: 2 },
{ messageId: 'simpleIndentError', line: 10, column: 3 },
{ messageId: 'simpleIndentError', line: 11, column: 3 },
]
},
{
code: `
for (var i = 0; i < 1; ++i) {
\tconsole.warn("123");
}
`,
output: `
for (var i = 0; i < 1; ++i) {
console.warn("123");
}
`,
errors: [
{ messageId: 'simpleIndentError', line: 3, column: 2 },
]
},
{
code: `
while (i < 1) {
\tconsole.warn("123");
}
`,
output: `
while (i < 1) {
console.warn("123");
}
`,
errors: [
{ messageId: 'simpleIndentError', line: 3, column: 2 },
]
},
{
code: `
do {
\tconsole.warn("123");
} while (i < 1);
`,
output: `
do {
console.warn("123");
} while (i < 1);
`,
errors: [
{ messageId: 'simpleIndentError', line: 3, column: 2 },
]
},
{
code: `
if (i < 1) {
\tconsole.warn("123");
}
`,
output: `
if (i < 1) {
console.warn("123");
}
`,
errors: [
{ messageId: 'simpleIndentError', line: 3, column: 2 },
]
},
{
code: `
var arr = [
\t1,
2
];
`,
output: `
var arr = [
1,
2
];
`,
errors: [
{ messageId: 'simpleIndentError', line: 3, column: 2 },
]
},
{
code: `
var arr2 = [
{
\t\ta: 1,
b: 2
},
{
a: 3,
\t\tb: 4
}
];
`,
output: `
var arr2 = [
{
a: 1,
b: 2
},
{
a: 3,
b: 4
}
];
`,
errors: [
{ messageId: 'simpleIndentError', line: 4, column: 3 },
{ messageId: 'simpleIndentError', line: 9, column: 3 },
]
}
],
});

View File

@ -1,5 +1,6 @@
import * as path from "path";
import { TSESLint } from "@typescript-eslint/experimental-utils";
export const ROOT_DIR = path.resolve(__dirname);
export const ROOT_DIR = path.join(process.cwd(), 'scripts', 'eslint', 'tests', 'fixtures');
export const FILENAME = path.join(ROOT_DIR, 'file.ts');
export const RuleTester = TSESLint.RuleTester;

View File

@ -15,7 +15,7 @@
"noResolve": false,
"strict": true,
"module": "commonjs",
"target": "es5",
"target": "es6",
"outDir": "../../built/eslint",
"lib": ["es2015", "es2016"]
},
@ -24,5 +24,9 @@
"rules",
"tests",
"tests/support/*.json"
],
"exclude": [
"tests/fixtures"
]
}

View File

@ -5916,7 +5916,8 @@ namespace ts {
// If fileName is provided, gets all the diagnostics associated with that file name.
// Otherwise, returns all the diagnostics (global and file associated) in this collection.
getDiagnostics(fileName?: string): DiagnosticWithLocation[];
getDiagnostics(): Diagnostic[];
getDiagnostics(fileName: string): DiagnosticWithLocation[];
reattachFileDiagnostics(newFile: SourceFile): void;
}