move eslint rules from eslint-plugin-microsoft-typescript to scripts/eslint

This commit is contained in:
Alexander
2019-07-24 07:17:16 +03:00
committed by Alexander T
parent a79f598269
commit 0059763d8f
41 changed files with 1254 additions and 38 deletions

View File

@@ -0,0 +1,105 @@
import { SyntaxKind } from "typescript";
import { AST_NODE_TYPES, TSESTree } from "@typescript-eslint/experimental-utils";
import { getEsTreeNodeToTSNodeMap, createRule } from "./utils";
export = createRule({
name: "boolean-trivia",
meta: {
docs: {
description: ``,
category: "Best Practices",
recommended: "error",
},
messages: {
booleanTriviaArgumentError: `Tag argument with parameter name`,
booleanTriviaArgumentSpaceError: `There should be 1 space between an argument and its comment`,
},
schema: [],
type: "problem",
},
defaultOptions: [],
create(context) {
const esTreeNodeToTSNodeMap = getEsTreeNodeToTSNodeMap(context.parserServices);
const sourceCode = context.getSourceCode();
const sourceCodeText = sourceCode.getText();
const isSetOrAssert = (name: string): boolean => name.startsWith("set") || name.startsWith("assert");
const isTrivia = (node: TSESTree.Expression): boolean => {
const tsNode = esTreeNodeToTSNodeMap.get(node);
if (tsNode.kind === SyntaxKind.Identifier) {
return tsNode.originalKeywordKind === SyntaxKind.UndefinedKeyword;
}
return [SyntaxKind.TrueKeyword, SyntaxKind.FalseKeyword, SyntaxKind.NullKeyword].indexOf(tsNode.kind) >= 0;
};
const shouldIgnoreCalledExpression = (node: TSESTree.CallExpression): boolean => {
if (node.callee && node.callee.type === AST_NODE_TYPES.MemberExpression) {
const methodName = node.callee.property.type === AST_NODE_TYPES.Identifier
? node.callee.property.name
: "";
if (isSetOrAssert(methodName)) {
return true;
}
return ["apply", "call", "equal", "fail", "isTrue", "output", "stringify", "push"].indexOf(methodName) >= 0;
}
if (node.callee && node.callee.type === AST_NODE_TYPES.Identifier) {
const functionName = node.callee.name;
if (isSetOrAssert(functionName)) {
return true;
}
return [
"createImportSpecifier",
"createAnonymousType",
"createSignature",
"createProperty",
"resolveName",
"contains",
].indexOf(functionName) >= 0;
}
return false;
};
const checkArg = (node: TSESTree.Expression): void => {
if (!isTrivia(node)) {
return;
}
const comments = sourceCode.getCommentsBefore(node);
if (!comments || comments.length !== 1 || comments[0].type !== "Block") {
context.report({ messageId: "booleanTriviaArgumentError", node });
return;
}
const argRangeStart = node.range[0];
const commentRangeEnd = comments[0].range[1];
const hasNewLine = sourceCodeText.slice(commentRangeEnd, argRangeStart).indexOf("\n") >= 0;
if (argRangeStart !== commentRangeEnd + 1 && !hasNewLine) {
context.report({ messageId: "booleanTriviaArgumentSpaceError", node });
}
};
const checkBooleanTrivia = (node: TSESTree.CallExpression) => {
if (shouldIgnoreCalledExpression(node)) {
return;
}
for (const arg of node.arguments) {
checkArg(arg);
}
};
return {
CallExpression: checkBooleanTrivia,
};
},
});

View File

@@ -0,0 +1,60 @@
import { AST_NODE_TYPES, TSESTree } from "@typescript-eslint/experimental-utils";
import { createRule } from "./utils";
export = createRule({
name: "debug-assert",
meta: {
docs: {
description: ``,
category: "Possible Errors",
recommended: "error",
},
messages: {
secondArgumentDebugAssertError: `Second argument to 'Debug.assert' should be a string literal`,
thirdArgumentDebugAssertError: `Third argument to 'Debug.assert' should be a string literal or arrow function`,
},
schema: [],
type: "problem",
},
defaultOptions: [],
create(context) {
const isArrowFunction = (node: TSESTree.Node) => node.type === AST_NODE_TYPES.ArrowFunctionExpression;
const isStringLiteral = (node: TSESTree.Node): boolean => (
(node.type === AST_NODE_TYPES.Literal && typeof node.value === "string") || node.type === AST_NODE_TYPES.TemplateLiteral
);
const isDebugAssert = (node: TSESTree.MemberExpression): boolean => (
node.object.type === AST_NODE_TYPES.Identifier
&& node.object.name === "Debug"
&& node.property.type === AST_NODE_TYPES.Identifier
&& node.property.name === "assert"
);
const checkDebugAssert = (node: TSESTree.CallExpression) => {
const args = node.arguments;
const argsLen = args.length;
if (!(node.callee.type === AST_NODE_TYPES.MemberExpression && isDebugAssert(node.callee)) || argsLen < 2) {
return;
}
const message1Node = args[1];
if (message1Node && !isStringLiteral(message1Node)) {
context.report({ messageId: "secondArgumentDebugAssertError", node: message1Node });
}
if (argsLen < 3) {
return;
}
const message2Node = args[2];
if (message2Node && (!isStringLiteral(message2Node) && !isArrowFunction(message2Node))) {
context.report({ messageId: "thirdArgumentDebugAssertError", node: message2Node });
}
};
return {
CallExpression: checkDebugAssert,
};
},
});

View File

@@ -0,0 +1,81 @@
import { TSESTree } from "@typescript-eslint/experimental-utils";
import { SyntaxKind } from "typescript";
import { getEsTreeNodeToTSNodeMap, createRule } from "./utils";
export = createRule({
name: "no-double-space",
meta: {
docs: {
description: ``,
category: "Stylistic Issues",
recommended: "error",
},
messages: {
noDoubleSpaceError: `Use only one space`,
},
schema: [],
type: "problem",
},
defaultOptions: [],
create(context) {
const esTreeNodeToTSNodeMap = getEsTreeNodeToTSNodeMap(context.parserServices);
const sourceCode = context.getSourceCode();
const lines = sourceCode.getLines();
const isLiteral = (node: TSESTree.Node | null) => {
if (!node) {
return false;
}
const tsNode = esTreeNodeToTSNodeMap.get(node);
if (!tsNode) {
return false;
}
return [
SyntaxKind.NoSubstitutionTemplateLiteral,
SyntaxKind.RegularExpressionLiteral,
SyntaxKind.TemplateMiddle,
SyntaxKind.StringLiteral,
SyntaxKind.TemplateHead,
SyntaxKind.TemplateTail,
].indexOf(tsNode.kind) >= 0;
};
const checkDoubleSpace = (node: TSESTree.Node) => {
lines.forEach((line, index) => {
const firstNonSpace = /\S/.exec(line);
if (!firstNonSpace || line.includes("@param")) {
return;
}
// Allow common uses of double spaces
// * To align `=` or `!=` signs
// * To align comments at the end of lines
// * To indent inside a comment
// * To use two spaces after a period
// * To include aligned `->` in a comment
const rgx = /[^/*. ][ ]{2}[^-!/= ]/g;
rgx.lastIndex = firstNonSpace.index;
const doubleSpace = rgx.exec(line);
if (!doubleSpace) {
return;
}
const locIndex = sourceCode.getIndexFromLoc({ column: doubleSpace.index, line: index + 1 });
const sourceNode = sourceCode.getNodeByRangeIndex(locIndex);
if (isLiteral(sourceNode)) {
return;
}
context.report({ messageId: "noDoubleSpaceError", node, loc: { line: index + 1, column: doubleSpace.index + 1 } });
});
};
return {
Program: checkDoubleSpace,
};
},
});

View File

@@ -0,0 +1,32 @@
import { TSESTree } from "@typescript-eslint/experimental-utils";
import { createRule } from "./utils";
export = createRule({
name: "no-in-operator",
meta: {
docs: {
description: ``,
category: "Best Practices",
recommended: "error",
},
messages: {
noInOperatorError: `Don't use the 'in' keyword - use 'hasProperty' to check for key presence instead`,
},
schema: [],
type: "suggestion",
},
defaultOptions: [],
create(context) {
const IN_OPERATOR = "in";
const checkInOperator = (node: TSESTree.BinaryExpression) => {
if (node.operator === IN_OPERATOR) {
context.report({ messageId: "noInOperatorError", node });
}
};
return {
BinaryExpression: checkInOperator,
};
},
});

View File

@@ -0,0 +1,70 @@
import { TSESTree, AST_NODE_TYPES } from "@typescript-eslint/experimental-utils";
import { createRule } from "./utils";
export = createRule({
name: "no-keywords",
meta: {
docs: {
description: `disallows the use of certain TypeScript keywords as variable or parameter names`,
category: "Stylistic Issues",
recommended: "error",
},
messages: {
noKeywordsError: `{{ name }} clashes with keyword/type`,
},
schema: [{
properties: {
properties: { type: "boolean" },
keywords: { type: "boolean" },
},
type: "object",
}],
type: "suggestion",
},
defaultOptions: [],
create(context) {
const keywords = [
"Undefined",
"undefined",
"Boolean",
"boolean",
"String",
"string",
"Number",
"number",
"any",
];
const hasKeyword = (name: string) => keywords.includes(name);
const shouldReport = (node: TSESTree.Identifier) => {
if (!node || !node.parent || !hasKeyword(node.name)) {
return false;
}
const parent = node.parent;
if (parent.type === AST_NODE_TYPES.FunctionDeclaration || parent.type === AST_NODE_TYPES.FunctionExpression) {
return !(parent.id && hasKeyword(parent.id.name));
}
if (parent.type === AST_NODE_TYPES.TSMethodSignature && parent.key.type === AST_NODE_TYPES.Identifier) {
return !(parent.key && hasKeyword(parent.key.name));
}
return [
AST_NODE_TYPES.ArrowFunctionExpression,
AST_NODE_TYPES.VariableDeclarator,
AST_NODE_TYPES.TSFunctionType,
].includes(node.parent.type);
};
const check = (node: TSESTree.Identifier) => {
if (shouldReport(node)) {
context.report({ messageId: "noKeywordsError", data: { name: node.name }, node });
}
};
return { Identifier: check };
},
});

View File

@@ -0,0 +1,43 @@
import { TSESTree } from "@typescript-eslint/experimental-utils";
import { createRule } from "./utils";
export = createRule({
name: "no-type-assertion-whitespace",
meta: {
docs: {
description: ``,
category: "Stylistic Issues",
recommended: "error",
},
messages: {
noTypeAssertionWhitespace: `Excess trailing whitespace found around type assertion`,
},
schema: [],
type: "problem",
},
defaultOptions: [],
create(context) {
const sourceCode = context.getSourceCode();
const checkTypeAssertionWhitespace = (node: TSESTree.TSTypeAssertion) => {
const leftToken = sourceCode.getLastToken(node.typeAnnotation);
const rightToken = sourceCode.getFirstToken(node.expression);
if (!leftToken || !rightToken) {
return;
}
if (sourceCode.isSpaceBetweenTokens(leftToken, rightToken)) {
context.report({
messageId: "noTypeAssertionWhitespace",
node,
loc: { column: leftToken.loc.end.column + 1, line: leftToken.loc.end.line },
});
}
};
return {
TSTypeAssertion: checkTypeAssertionWhitespace,
};
},
});

View File

@@ -0,0 +1,71 @@
import { TSESTree } from "@typescript-eslint/experimental-utils";
import { createRule } from "./utils";
export = createRule({
name: "object-literal-surrounding-space",
meta: {
docs: {
description: ``,
category: "Stylistic Issues",
recommended: "error",
},
messages: {
leadingExcessStringError: `No trailing whitespace found on single-line object literal`,
leadingStringError: `No leading whitespace found on single-line object literal`,
trailingExcessStringError: `Excess trailing whitespace found on single-line object literal.`,
trailingStringError: `No trailing whitespace found on single-line object literal`,
},
schema: [],
type: "suggestion",
},
defaultOptions: [],
create(context) {
const sourceCode = context.getSourceCode();
const SPACE_SYMBOL = " ";
const CLOSE_SYMBOL = "}";
const OPEN_SYMBOL = "{";
const manySpaces = (text: string, startIndex: number): boolean => (
[startIndex, startIndex + 1].every(i => text.charAt(i) === SPACE_SYMBOL)
);
const checkObjectLiteralSurroundingSpace = (node: TSESTree.ObjectExpression) => {
const text = sourceCode.getText(node);
const startLine = node.loc.start.line;
const endLine = node.loc.end.line;
if (!node.properties.length || !text.match(/^{[^\n]+}$/g)) {
return;
}
if (text.charAt(0) === OPEN_SYMBOL) {
if (text.charAt(1) !== SPACE_SYMBOL) {
context.report({ messageId: "leadingStringError", node });
}
if (manySpaces(text, 1)) {
context.report({ messageId: "leadingExcessStringError", node, loc: { column: node.loc.start.column + 1, line: startLine } });
}
}
if (text.charAt(text.length - 1) === CLOSE_SYMBOL) {
const index = text.length - 2;
const endColumn = node.loc.end.column;
if (text.charAt(index) !== SPACE_SYMBOL) {
context.report({ messageId: "trailingStringError", node, loc: { column: endColumn - 1, line: endLine } });
}
if (manySpaces(text, index - 1)) {
context.report({ messageId: "trailingExcessStringError", node, loc: { column: endColumn - 2, line: endLine } });
}
}
};
return {
ObjectExpression: checkObjectLiteralSurroundingSpace,
};
},
});

View File

@@ -0,0 +1,91 @@
import { AST_NODE_TYPES, TSESTree } from "@typescript-eslint/experimental-utils";
import { createRule } from "./utils";
type MessageId = "onlyArrowFunctionsError";
type Options = [{
allowNamedFunctions?: boolean;
allowDeclarations?: boolean;
}];
export = createRule<Options, MessageId>({
name: "only-arrow-functions",
meta: {
docs: {
description: `Disallows traditional (non-arrow) function expressions.`,
category: "Best Practices",
recommended: "error",
},
messages: {
onlyArrowFunctionsError: "non-arrow functions are forbidden",
},
schema: [{
additionalProperties: false,
properties: {
allowNamedFunctions: { type: "boolean" },
allowDeclarations: { type: "boolean" },
},
type: "object",
}],
type: "suggestion",
},
defaultOptions: [{
allowNamedFunctions: false,
allowDeclarations: false,
}],
create(context, [{ allowNamedFunctions, allowDeclarations }]) {
const isThisParameter = (node: TSESTree.FunctionDeclaration | TSESTree.FunctionExpression) => (
node.params.length && !!node.params.find(param => param.type === AST_NODE_TYPES.Identifier && param.name === "this")
);
const isMethodType = (node: TSESTree.Node) => {
const types = [
AST_NODE_TYPES.MethodDefinition,
AST_NODE_TYPES.Property,
];
const parent = node.parent;
if (!parent) {
return false;
}
return node.type === AST_NODE_TYPES.FunctionExpression && types.includes(parent.type);
};
const stack: boolean[] = [];
const enterFunction = () => {
stack.push(false);
};
const markThisUsed = () => {
if (stack.length) {
stack[stack.length - 1] = true;
}
};
const exitFunction = (node: TSESTree.FunctionDeclaration | TSESTree.FunctionExpression) => {
const methodUsesThis = stack.pop();
if (node.type === AST_NODE_TYPES.FunctionDeclaration && allowDeclarations) {
return;
}
if ((allowNamedFunctions && node.id !== null) || isMethodType(node)) { // eslint-disable-line no-null/no-null
return;
}
if (!(node.generator || methodUsesThis || isThisParameter(node))) {
context.report({ messageId: "onlyArrowFunctionsError", node });
}
};
return {
"FunctionDeclaration": enterFunction,
"FunctionDeclaration:exit": exitFunction,
"FunctionExpression": enterFunction,
"FunctionExpression:exit": exitFunction,
"ThisExpression": markThisUsed,
};
},
});

View File

@@ -0,0 +1,44 @@
import { TSESTree, AST_TOKEN_TYPES } from "@typescript-eslint/experimental-utils";
import { createRule } from "./utils";
export = createRule({
name: "type-operator-spacing",
meta: {
docs: {
description: ``,
category: "Stylistic Issues",
recommended: "error",
},
messages: {
typeOperatorSpacingError: `The '|' and '&' operators must be surrounded by spaces`,
},
schema: [],
type: "suggestion",
},
defaultOptions: [],
create(context) {
const sourceCode = context.getSourceCode();
const tokens = ["|", "&"];
const text = sourceCode.getText();
const checkTypeOperatorSpacing = (node: TSESTree.TSIntersectionType | TSESTree.TSUnionType) => {
node.types.forEach(node => {
const token = sourceCode.getTokenBefore(node);
if (!!token && token.type === AST_TOKEN_TYPES.Punctuator && tokens.indexOf(token.value) >= 0) {
const [start, end] = token.range;
if (/\S/.test(text[start - 1]) || /\S/.test(text[end])) {
context.report({ messageId: "typeOperatorSpacingError", node: token });
}
}
});
};
return {
TSIntersectionType: checkTypeOperatorSpacing,
TSUnionType: checkTypeOperatorSpacing,
};
},
});

View File

@@ -0,0 +1,18 @@
import { ParserServices, ESLintUtils } from "@typescript-eslint/experimental-utils";
export const createRule = ESLintUtils.RuleCreator(() => "");
export const getTypeChecker = (parserServices: ParserServices | undefined) => {
if (!parserServices || !parserServices.program || !parserServices.program.getTypeChecker) {
throw new Error("'typeChecker' was not found");
}
return parserServices.program.getTypeChecker();
};
export const getEsTreeNodeToTSNodeMap = (parserServices: ParserServices | undefined) => {
if (!parserServices || !parserServices.esTreeNodeToTSNodeMap) {
throw new Error("'esTreeNodeToTSNodeMap' was not found");
}
return parserServices.esTreeNodeToTSNodeMap;
};

View File

@@ -0,0 +1,73 @@
import { RuleTester, ROOT_DIR } from "./support/RuleTester";
import rule = require("../rules/boolean-trivia");
const ruleTester = new RuleTester({
parserOptions: {
warnOnUnsupportedTypeScriptVersion: false,
tsconfigRootDir: ROOT_DIR,
ecmaFeatures: {},
ecmaVersion: 6,
sourceType: "module",
project: "./tsconfig.json",
},
parser: require.resolve("@typescript-eslint/parser"),
});
ruleTester.run("boolean-trivia", rule, {
valid: [{
code: `
const fn = (prop: boolean) => {};
fn(/* boolean prop */ true);
`,
}, {
code: `
const fn = (prop: null) => {};
fn(/* null prop */ null);
`,
}, {
code: `
const fn = (prop: null) => {};
fn(/*null prop*/ null);
`,
}, {
code: `
const fn = (prop: boolean) => {};
fn(/* comment */
false
);
`,
}, {
code: `
const fn = (prop: boolean) => {};
fn.apply(null, true);
`,
}],
invalid: [{
code: `
const fn = (prop: null) => {};
fn(null);
`,
errors: [{ messageId: "booleanTriviaArgumentError" }],
}, {
code: `
const fn = (prop: boolean) => {};
fn(false);
`,
errors: [{ messageId: "booleanTriviaArgumentError" }],
}, {
code: `
const fn = (prop: boolean) => {};
fn(/* boolean arg */false);
`,
errors: [{ messageId: "booleanTriviaArgumentSpaceError" }],
}, {
code: `
const fn = (prop: boolean) => {};
fn(/* first comment */ /* second comment */ false);
`,
errors: [{ messageId: "booleanTriviaArgumentError" }],
}],
});

View File

@@ -0,0 +1,40 @@
import { RuleTester } from "./support/RuleTester";
import rule = require("../rules/debug-assert");
const ruleTester = new RuleTester({
parserOptions: {
warnOnUnsupportedTypeScriptVersion: false,
},
parser: require.resolve("@typescript-eslint/parser"),
});
ruleTester.run("debug-assert", rule, {
valid: [{
code: `Debug.assert(true)`,
}, {
code: `Debug.assert(true, 'error message')`,
}, {
code: `Debug.assert(true, 'error message 1', 'error message 2')`,
}, {
code: `Debug.assert(true, 'error message 1', () => {})`,
}, {
code: "Debug.assert(true, `error message 1`, () => {})",
}, {
code: `Debug.assert(true, "error message 1", () => {})`,
}],
invalid: [{
code: `Debug.assert(true, 1)`,
errors: [{ messageId: "secondArgumentDebugAssertError" }],
}, {
code: `Debug.assert(true, 'error message', 1)`,
errors: [{ messageId: "thirdArgumentDebugAssertError" }],
}, {
code: `Debug.assert(true, null, 1)`,
errors: [{
messageId: "secondArgumentDebugAssertError",
}, {
messageId: "thirdArgumentDebugAssertError",
}],
}],
});

View File

@@ -0,0 +1,115 @@
import { RuleTester, ROOT_DIR } from "./support/RuleTester";
import rule = require("../rules/no-double-space");
const ruleTester = new RuleTester({
parser: require.resolve("@typescript-eslint/parser"),
parserOptions: {
warnOnUnsupportedTypeScriptVersion: false,
tsconfigRootDir: ROOT_DIR,
ecmaFeatures: {},
ecmaVersion: 6,
sourceType: "module",
project: "./tsconfig.json",
},
});
ruleTester.run("no-double-space", rule, {
valid: [{
code: `const a = {};`,
}, {
code: `function fn() {}`,
}, {
code: `const a = " ";`,
}, {
code: `// ^ ^`,
}, {
code: `class Cl {}`,
}, {
code: `// comment `,
}, {
code: `/* comment */`,
}, {
code: `" string ";`,
}, {
code: `/ regexp /g;`,
}, {
code: `const rgx = / regexp /g;`,
}, {
code: "const str = ` string template`;",
}, {
code: ` // comment`,
}, {
code: ` /* comment */`,
}, {
code: `// `,
}, {
code: `
const a =
1;
`,
}, {
code: `
/**
* comment
*/
`,
}, {
code: `
// comment
// - comment
// - comment
`,
}, {
code: `
interface Props {
prop: string[]; // comment prop
propB: string[]; // comment propB
}
`,
}, {
code: `
/**
* Returns a JSON-encoded value of the type: string[]
*
* @param exclude A JSON encoded string[] containing the paths to exclude
* when enumerating the directory.
*/
`,
}, {
code: `
const obj = {
content: "function f() { 1; }",
};
`,
}],
invalid: [{
code: `const a = {};`,
errors: [{ messageId: "noDoubleSpaceError", line: 1, column: 6 }],
}, {
code: `function fn() {}`,
errors: [{ messageId: "noDoubleSpaceError", line: 1, column: 9 }],
}, {
code: `class Cl {}`,
errors: [{ messageId: "noDoubleSpaceError", line: 1, column: 6 }],
}, {
code: "const str = ` string template`;",
errors: [{ messageId: "noDoubleSpaceError", line: 1, column: 12 }],
}, {
code: `/** comment */`,
errors: [{ messageId: "noDoubleSpaceError", line: 1, column: 12 }],
}, {
code: `/** comment with many spaces */`,
errors: [{ messageId: "noDoubleSpaceError", line: 1, column: 12 }],
}, {
code: `// comment with many spaces`,
errors: [{ messageId: "noDoubleSpaceError", line: 1, column: 11 }],
}, {
code: `
const a = 1;
const b = 2;
const c = 3;
`.trim(),
errors: [{ messageId: "noDoubleSpaceError", line: 3, column: 10 }],
}],
});

View File

@@ -0,0 +1,26 @@
import { RuleTester } from "./support/RuleTester";
import rule = require("../rules/no-in-operator");
const ruleTester = new RuleTester({
parserOptions: {
warnOnUnsupportedTypeScriptVersion: false,
},
parser: require.resolve("@typescript-eslint/parser"),
});
ruleTester.run("no-in-operator", rule, {
valid: [{
code: `
const prop = {};
prop.hasProperty('a');
`,
}],
invalid: [{
code: `
const prop = {};
prop in 'a';
`,
errors: [{ messageId: "noInOperatorError" }],
}],
});

View File

@@ -0,0 +1,77 @@
import { RuleTester } from "./support/RuleTester";
import rule = require("../rules/no-keywords");
const ruleTester = new RuleTester({
parserOptions: {
warnOnUnsupportedTypeScriptVersion: false,
},
parser: require.resolve("@typescript-eslint/parser"),
});
ruleTester.run("no-keywords", rule, {
valid: [{
code: `const a = {};`,
}, {
code: `function a() {};`,
}, {
code: `const x = function string() {};`,
}, {
code: `const y = function () {};`,
}, {
code: `const y = () => {};`,
}, {
code: `
class A {
b = () => {}
a() {}
number() {}
}
`,
}, {
code: `
interface A {
b(): void;
}
`,
}, {
code: `
const obj = {
a: null,
b() {},
c: function (c: number) {},
string: function d (d: string) {},
e: () => {},
};
`,
}],
invalid: [{
code: `const number = 1;`,
errors: [{ messageId: "noKeywordsError" }],
}, {
code: `function x(number: number) {};`,
errors: [{ messageId: "noKeywordsError" }],
}, {
code: `const y = function (number: number) {};`,
errors: [{ messageId: "noKeywordsError" }],
}, {
code: `const y = (number: number) => {};`,
errors: [{ messageId: "noKeywordsError" }],
}, {
code: `
class A {
b = function (any: any) {};
a(number: number) {}
}
`,
errors: [{ messageId: "noKeywordsError" }, { messageId: "noKeywordsError" }],
}, {
code: `
interface A {
a(number: number): void;
b: (any: any) => void;
}
`,
errors: [{ messageId: "noKeywordsError" }, { messageId: "noKeywordsError" }],
}],
});

View File

@@ -0,0 +1,31 @@
import { RuleTester } from "./support/RuleTester";
import rule = require("../rules/no-type-assertion-whitespace");
const ruleTester = new RuleTester({
parserOptions: {
warnOnUnsupportedTypeScriptVersion: false,
},
parser: require.resolve("@typescript-eslint/parser"),
});
ruleTester.run("no-type-assertion-whitespace", rule, {
valid: [{
code: `const a = <number>1`,
}, {
code: `const s = <Type>(new Symbol(1, 'name'));`,
}],
invalid: [{
code: `const a = <number> 1`,
errors: [{ messageId: "noTypeAssertionWhitespace", column: 19, line: 1 }],
}, {
code: `const a = <number> 1`,
errors: [{ messageId: "noTypeAssertionWhitespace", column: 19, line: 1 }],
}, {
code: `const a = <number> 1`,
errors: [{ messageId: "noTypeAssertionWhitespace", column: 19, line: 1 }],
}, {
code: `const s = <Type> (new Symbol(1, 'name'));`,
errors: [{ messageId: "noTypeAssertionWhitespace", column: 17, line: 1 }],
}],
});

View File

@@ -0,0 +1,40 @@
import { RuleTester } from "./support/RuleTester";
import rule = require("../rules/object-literal-surrounding-space");
const ruleTester = new RuleTester({
parserOptions: {
warnOnUnsupportedTypeScriptVersion: false,
},
parser: require.resolve("@typescript-eslint/parser"),
});
ruleTester.run("object-literal-surrounding-space", rule, {
valid: [{
code: `const prop = {}`,
}, {
code: `const prop = { }`,
}, {
code: `const prop = { x: 1 }`,
}],
invalid: [{
code: `const prop = {x: 1}`,
errors: [{
messageId: "leadingStringError",
}, {
messageId: "trailingStringError",
}],
}, {
code: `const prop = { x: 1 }`,
errors: [{ messageId: "leadingExcessStringError" }],
}, {
code: `const prop = { x: 1 }`,
errors: [{ messageId: "trailingExcessStringError" }],
}, {
code: `const prop = { x: 1}`,
errors: [{ messageId: "trailingStringError" }],
}, {
code: `const prop = {x: 1 }`,
errors: [{ messageId: "leadingStringError" }],
}],
});

View File

@@ -0,0 +1,101 @@
import { RuleTester } from "./support/RuleTester";
import rule = require("../rules/only-arrow-functions");
const ruleTester = new RuleTester({
parserOptions: {
warnOnUnsupportedTypeScriptVersion: false,
},
parser: require.resolve("@typescript-eslint/parser"),
});
ruleTester.run("only-arrow-functions", rule, {
valid: [{
code: `const a = () => {};`,
}, {
code: `((func) => func())(() => {});`,
}, {
code: `function* generator() {}`,
}, {
code: `let generator = function*() {}`,
}, {
code: `function hasThisParameter(this) {}`,
}, {
code: `let hasThisParameter = function(this) {}`,
}, {
code: `let usesThis = function() { this; }`,
}, {
code: `let usesThis2 = function(foo = this) {}`,
}, {
code: `
let fn = function fn() {};
function z() {};
`,
options: [{ allowNamedFunctions: true }],
}, {
code: `
function fn() {};
let generator = function*() {}
function hasThisParameter(this) {}
let hasThisParameter = function(this) {}
`,
options: [{ allowDeclarations: true }],
}, {
code: `
class A {
test() {}
}
`,
}, {
code: `
const obj = {
test() {}
}
`,
}],
invalid: [{
code: `function foo(a: any): any {}`,
errors: [{ messageId: "onlyArrowFunctionsError" }],
}, {
code: `let b = function () {};`,
errors: [{ messageId: "onlyArrowFunctionsError" }],
}, {
code: `function c() {}`,
errors: [{ messageId: "onlyArrowFunctionsError" }],
}, {
code: `((func) => func())(function e(): void {});`,
errors: [{ messageId: "onlyArrowFunctionsError" }],
}, {
code: `
let notUsesThis = function() {
function f() { this; }
}
`,
errors: [{ messageId: "onlyArrowFunctionsError" }],
}, {
code: `
let notUsesThis2 = function() {
return class { method() { this; } }
}
`,
errors: [{ messageId: "onlyArrowFunctionsError" }],
}, {
code: `export function exported() {}`,
errors: [{ messageId: "onlyArrowFunctionsError" }],
}, {
code: `async function asyncFunction() {}`,
errors: [{ messageId: "onlyArrowFunctionsError" }],
}, {
code: `
let b = function () {};
((func) => func())(function e(): void {});
`,
options: [{ allowDeclarations: true }],
errors: [{ messageId: "onlyArrowFunctionsError" }, { messageId: "onlyArrowFunctionsError" }],
}, {
code: `const x = function() {}`,
options: [{ allowNamedFunctions: true }],
errors: [{ messageId: "onlyArrowFunctionsError" }],
}],
});

View File

@@ -0,0 +1,5 @@
import * as path from "path";
import { TSESLint } from "@typescript-eslint/experimental-utils";
export const ROOT_DIR = path.resolve(__dirname);
export const RuleTester = TSESLint.RuleTester;

View File

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

View File

@@ -0,0 +1,35 @@
import { RuleTester } from "./support/RuleTester";
import rule = require("../rules/type-operator-spacing");
const ruleTester = new RuleTester({
parserOptions: {
warnOnUnsupportedTypeScriptVersion: false,
},
parser: require.resolve("@typescript-eslint/parser"),
});
ruleTester.run("type-operator-spacing", rule, {
valid: [{
code: `type T = string | number`,
}, {
code: `type T = string & number`,
}, {
code: `function fn(): string | number {}`,
}, {
code: `function fn(): string & number {}`,
}],
invalid: [{
code: `type T = string|number`,
errors: [{ messageId: "typeOperatorSpacingError" }],
}, {
code: `type T = string&number`,
errors: [{ messageId: "typeOperatorSpacingError" }],
}, {
code: `function fn(): string|number {}`,
errors: [{ messageId: "typeOperatorSpacingError" }],
}, {
code: `function fn(): string&number {}`,
errors: [{ messageId: "typeOperatorSpacingError" }],
}],
});

View File

@@ -0,0 +1,28 @@
{
"compilerOptions": {
"forceConsistentCasingInFileNames": true,
"suppressImplicitAnyIndexErrors": true,
"experimentalDecorators": true,
"noImplicitReturns": true,
"resolveJsonModule": true,
"moduleResolution": "node",
"esModuleInterop": true,
"noImplicitThis": true,
"noUnusedLocals": true,
"noImplicitAny": true,
"skipLibCheck": true,
"declaration": false,
"noResolve": false,
"strict": true,
"module": "commonjs",
"target": "es5",
"outDir": "../../built/eslint",
"lib": ["es2015", "es2016"]
},
"include": [
"rules",
"tests",
"tests/support/*.json"
]
}