mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-04 03:09:39 -06:00
move eslint rules from eslint-plugin-microsoft-typescript to scripts/eslint
This commit is contained in:
parent
a79f598269
commit
0059763d8f
26
.eslintrc
26
.eslintrc
@ -12,7 +12,7 @@
|
||||
"es6": true
|
||||
},
|
||||
"plugins": [
|
||||
"@typescript-eslint", "microsoft-typescript", "jsdoc", "no-null", "import"
|
||||
"@typescript-eslint", "jsdoc", "no-null", "import"
|
||||
],
|
||||
"rules": {
|
||||
"@typescript-eslint/adjacent-overload-signatures": "error",
|
||||
@ -56,19 +56,19 @@
|
||||
"@typescript-eslint/type-annotation-spacing": "error",
|
||||
"@typescript-eslint/unified-signatures": "error",
|
||||
|
||||
/** eslint-plugin-microsoft-typescript */
|
||||
"microsoft-typescript/object-literal-surrounding-space": "error",
|
||||
"microsoft-typescript/no-type-assertion-whitespace": "error",
|
||||
"microsoft-typescript/type-operator-spacing": "error",
|
||||
"microsoft-typescript/only-arrow-functions": ["error", {
|
||||
/** scripts/eslint/rules */
|
||||
"object-literal-surrounding-space": "error",
|
||||
"no-type-assertion-whitespace": "error",
|
||||
"type-operator-spacing": "error",
|
||||
"only-arrow-functions": ["error", {
|
||||
"allowNamedFunctions": true ,
|
||||
"allowDeclarations": true
|
||||
}],
|
||||
"microsoft-typescript/no-double-space": "error",
|
||||
"microsoft-typescript/boolean-trivia": "error",
|
||||
"microsoft-typescript/no-in-operator": "error",
|
||||
"microsoft-typescript/debug-assert": "error",
|
||||
"microsoft-typescript/no-keywords": "error",
|
||||
"no-double-space": "error",
|
||||
"boolean-trivia": "error",
|
||||
"no-in-operator": "error",
|
||||
"debug-assert": "error",
|
||||
"no-keywords": "error",
|
||||
|
||||
/** eslint-plugin-import */
|
||||
"import/no-extraneous-dependencies": ["error", { "optionalDependencies": false }],
|
||||
@ -151,8 +151,8 @@
|
||||
"@typescript-eslint/prefer-function-type": "off",
|
||||
"@typescript-eslint/unified-signatures": "off",
|
||||
|
||||
/** eslint-plugin-microsoft-typescript */
|
||||
"microsoft-typescript/no-keywords": "off",
|
||||
/** scripts/eslint/rules */
|
||||
"no-keywords": "off",
|
||||
|
||||
/** eslint */
|
||||
"no-var": "off"
|
||||
|
||||
@ -11,6 +11,7 @@ src
|
||||
tests
|
||||
Jakefile.js
|
||||
.eslintrc
|
||||
.eslintignore
|
||||
.editorconfig
|
||||
.failed-tests
|
||||
.git
|
||||
|
||||
23
Gulpfile.js
23
Gulpfile.js
@ -318,20 +318,39 @@ task("clean-tests").description = "Cleans the outputs for the test infrastructur
|
||||
|
||||
const watchTests = () => watchProject("src/testRunner", cmdLineOptions);
|
||||
|
||||
const buildRules = () => buildProject("scripts/eslint");
|
||||
task("build-rules", buildRules);
|
||||
task("build-rules").description = "Compiles eslint rules to js";
|
||||
|
||||
const cleanRules = () => cleanProject("scripts/eslint");
|
||||
cleanTasks.push(cleanRules);
|
||||
task("clean-rules", cleanRules);
|
||||
task("clean-rules").description = "Cleans the outputs for the eslint rules";
|
||||
|
||||
const runRulesTests = () => runConsoleTests("built/eslint/tests", "mocha-fivemat-progress-reporter", /*runInParallel*/ false, /*watchMode*/ false);
|
||||
task("run-rules-tests", series(buildRules, runRulesTests));
|
||||
task("run-rules-tests").description = "Runs the eslint rule tests";
|
||||
|
||||
const lintFoldStart = async () => { if (fold.isTravis()) console.log(fold.start("lint")); };
|
||||
const lintFoldEnd = async () => { if (fold.isTravis()) console.log(fold.end("lint")); };
|
||||
const eslint = async () => {
|
||||
const args = [
|
||||
"node_modules/eslint/bin/eslint", "-f", "autolinkable-stylish", "-c", ".eslintrc", "--ext", ".ts", "."
|
||||
"node_modules/eslint/bin/eslint",
|
||||
"--format", "autolinkable-stylish",
|
||||
"--config", ".eslintrc",
|
||||
"--ext", ".ts", ".",
|
||||
"--rulesdir", "built/eslint/rules/",
|
||||
];
|
||||
|
||||
if (cmdLineOptions.fix) {
|
||||
args.push("--fix");
|
||||
}
|
||||
|
||||
log(`Linting: ${args.join(" ")}`);
|
||||
return exec(process.execPath, args);
|
||||
}
|
||||
const lint = series([lintFoldStart, eslint, lintFoldEnd]);
|
||||
|
||||
const lint = series([buildRules, lintFoldStart, eslint, lintFoldEnd]);
|
||||
lint.displayName = "lint";
|
||||
task("lint", lint);
|
||||
task("lint").description = "Runs eslint on the compiler sources.";
|
||||
|
||||
@ -55,6 +55,7 @@
|
||||
"@types/travis-fold": "latest",
|
||||
"@types/xml2js": "^0.4.0",
|
||||
"@typescript-eslint/eslint-plugin": "1.13.0",
|
||||
"@typescript-eslint/experimental-utils": "1.13.0",
|
||||
"@typescript-eslint/parser": "1.13.0",
|
||||
"async": "latest",
|
||||
"azure-devops-node-api": "^8.0.0",
|
||||
@ -68,7 +69,6 @@
|
||||
"eslint-formatter-autolinkable-stylish": "1.0.0",
|
||||
"eslint-plugin-import": "2.18.2",
|
||||
"eslint-plugin-jsdoc": "15.6.1",
|
||||
"eslint-plugin-microsoft-typescript": "0.2.0",
|
||||
"eslint-plugin-no-null": "1.0.2",
|
||||
"fancy-log": "latest",
|
||||
"fs-extra": "^6.0.1",
|
||||
@ -112,6 +112,7 @@
|
||||
"gulp": "gulp",
|
||||
"jake": "gulp",
|
||||
"lint": "gulp lint",
|
||||
"lint:test": "gulp run-rules-tests",
|
||||
"setup-hooks": "node scripts/link-hooks.js",
|
||||
"update-costly-tests": "node scripts/costly-tests.js"
|
||||
},
|
||||
|
||||
105
scripts/eslint/rules/boolean-trivia.ts
Normal file
105
scripts/eslint/rules/boolean-trivia.ts
Normal 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,
|
||||
};
|
||||
},
|
||||
});
|
||||
60
scripts/eslint/rules/debug-assert.ts
Normal file
60
scripts/eslint/rules/debug-assert.ts
Normal 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,
|
||||
};
|
||||
},
|
||||
});
|
||||
81
scripts/eslint/rules/no-double-space.ts
Normal file
81
scripts/eslint/rules/no-double-space.ts
Normal 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,
|
||||
};
|
||||
},
|
||||
});
|
||||
32
scripts/eslint/rules/no-in-operator.ts
Normal file
32
scripts/eslint/rules/no-in-operator.ts
Normal 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,
|
||||
};
|
||||
},
|
||||
});
|
||||
70
scripts/eslint/rules/no-keywords.ts
Normal file
70
scripts/eslint/rules/no-keywords.ts
Normal 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 };
|
||||
},
|
||||
});
|
||||
43
scripts/eslint/rules/no-type-assertion-whitespace.ts
Normal file
43
scripts/eslint/rules/no-type-assertion-whitespace.ts
Normal 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,
|
||||
};
|
||||
},
|
||||
});
|
||||
71
scripts/eslint/rules/object-literal-surrounding-space.ts
Normal file
71
scripts/eslint/rules/object-literal-surrounding-space.ts
Normal 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,
|
||||
};
|
||||
},
|
||||
});
|
||||
91
scripts/eslint/rules/only-arrow-functions.ts
Normal file
91
scripts/eslint/rules/only-arrow-functions.ts
Normal 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,
|
||||
};
|
||||
},
|
||||
});
|
||||
44
scripts/eslint/rules/type-operator-spacing.ts
Normal file
44
scripts/eslint/rules/type-operator-spacing.ts
Normal 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,
|
||||
};
|
||||
},
|
||||
});
|
||||
18
scripts/eslint/rules/utils.ts
Normal file
18
scripts/eslint/rules/utils.ts
Normal 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;
|
||||
};
|
||||
73
scripts/eslint/tests/boolean-trivia.test.ts
Normal file
73
scripts/eslint/tests/boolean-trivia.test.ts
Normal 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" }],
|
||||
}],
|
||||
});
|
||||
40
scripts/eslint/tests/debug-assert.test.ts
Normal file
40
scripts/eslint/tests/debug-assert.test.ts
Normal 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",
|
||||
}],
|
||||
}],
|
||||
});
|
||||
115
scripts/eslint/tests/no-double-space.test.ts
Normal file
115
scripts/eslint/tests/no-double-space.test.ts
Normal 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 }],
|
||||
}],
|
||||
});
|
||||
26
scripts/eslint/tests/no-in-operator.test.ts
Normal file
26
scripts/eslint/tests/no-in-operator.test.ts
Normal 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" }],
|
||||
}],
|
||||
});
|
||||
77
scripts/eslint/tests/no-keywords.test.ts
Normal file
77
scripts/eslint/tests/no-keywords.test.ts
Normal 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" }],
|
||||
}],
|
||||
});
|
||||
31
scripts/eslint/tests/no-type-assertion-whitespace.test.ts
Normal file
31
scripts/eslint/tests/no-type-assertion-whitespace.test.ts
Normal 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 }],
|
||||
}],
|
||||
});
|
||||
@ -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" }],
|
||||
}],
|
||||
});
|
||||
101
scripts/eslint/tests/only-arrow-functions.test.ts
Normal file
101
scripts/eslint/tests/only-arrow-functions.test.ts
Normal 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" }],
|
||||
}],
|
||||
});
|
||||
|
||||
5
scripts/eslint/tests/support/RuleTester.ts
Normal file
5
scripts/eslint/tests/support/RuleTester.ts
Normal 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;
|
||||
9
scripts/eslint/tests/support/tsconfig.json
Normal file
9
scripts/eslint/tests/support/tsconfig.json
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"esModuleInterop": true,
|
||||
"target": "es5",
|
||||
"module": "commonjs",
|
||||
"strict": true,
|
||||
"lib": ["es2015", "es2017", "esnext"]
|
||||
}
|
||||
}
|
||||
35
scripts/eslint/tests/type-operator-spacing.test.ts
Normal file
35
scripts/eslint/tests/type-operator-spacing.test.ts
Normal 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" }],
|
||||
}],
|
||||
});
|
||||
28
scripts/eslint/tsconfig.json
Normal file
28
scripts/eslint/tsconfig.json
Normal 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"
|
||||
]
|
||||
}
|
||||
@ -927,7 +927,7 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
function errorOrSuggestion(isError: boolean, location: Node, message: DiagnosticMessage | DiagnosticMessageChain, arg0?: string | number, arg1?: string | number, arg2?: string | number, arg3?: string | number): void {
|
||||
addErrorOrSuggestion(isError, "message" in message ? createDiagnosticForNode(location, message, arg0, arg1, arg2, arg3) : createDiagnosticForNodeFromMessageChain(location, message)); // eslint-disable-line microsoft-typescript/no-in-operator
|
||||
addErrorOrSuggestion(isError, "message" in message ? createDiagnosticForNode(location, message, arg0, arg1, arg2, arg3) : createDiagnosticForNodeFromMessageChain(location, message)); // eslint-disable-line no-in-operator
|
||||
}
|
||||
|
||||
function errorAndMaybeSuggestAwait(
|
||||
|
||||
@ -115,7 +115,7 @@ namespace ts {
|
||||
// The global Map object. This may not be available, so we must test for it.
|
||||
declare const Map: (new <T>() => Map<T>) | undefined;
|
||||
// Internet Explorer's Map doesn't support iteration, so don't use it.
|
||||
// eslint-disable-next-line microsoft-typescript/no-in-operator
|
||||
// eslint-disable-next-line no-in-operator
|
||||
export const MapCtr = typeof Map !== "undefined" && "entries" in Map.prototype ? Map : shimMap();
|
||||
|
||||
// Keep the class inside a function so it doesn't get compiled if it's not used.
|
||||
@ -222,7 +222,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
has(key: string): boolean {
|
||||
// eslint-disable-next-line microsoft-typescript/no-in-operator
|
||||
// eslint-disable-next-line no-in-operator
|
||||
return key in this.data;
|
||||
}
|
||||
|
||||
|
||||
@ -75,7 +75,7 @@ namespace ts {
|
||||
if (typeof value === "number") {
|
||||
return createNumericLiteral(value + "");
|
||||
}
|
||||
// eslint-disable-next-line microsoft-typescript/no-in-operator
|
||||
// eslint-disable-next-line no-in-operator
|
||||
if (typeof value === "object" && "base10Value" in value) { // PseudoBigInt
|
||||
return createBigIntLiteral(pseudoBigIntToString(value) + "n");
|
||||
}
|
||||
|
||||
@ -68,7 +68,7 @@ namespace ts {
|
||||
return sourceIndex;
|
||||
}
|
||||
|
||||
/* eslint-disable microsoft-typescript/boolean-trivia, no-null/no-null */
|
||||
/* eslint-disable boolean-trivia, no-null/no-null */
|
||||
function setSourceContent(sourceIndex: number, content: string | null) {
|
||||
enter();
|
||||
if (content !== null) {
|
||||
@ -80,7 +80,7 @@ namespace ts {
|
||||
}
|
||||
exit();
|
||||
}
|
||||
/* eslint-enable microsoft-typescript/boolean-trivia, no-null/no-null */
|
||||
/* eslint-enable boolean-trivia, no-null/no-null */
|
||||
|
||||
function addName(name: string) {
|
||||
enter();
|
||||
|
||||
@ -1205,7 +1205,7 @@ namespace FourSlash {
|
||||
const sort = (locations: ReadonlyArray<ts.RenameLocation> | undefined) =>
|
||||
locations && ts.sort(locations, (r1, r2) => ts.compareStringsCaseSensitive(r1.fileName, r2.fileName) || r1.textSpan.start - r2.textSpan.start);
|
||||
assert.deepEqual(sort(references), sort(ranges.map((rangeOrOptions): ts.RenameLocation => {
|
||||
const { range, ...prefixSuffixText } = "range" in rangeOrOptions ? rangeOrOptions : { range: rangeOrOptions }; // eslint-disable-line microsoft-typescript/no-in-operator
|
||||
const { range, ...prefixSuffixText } = "range" in rangeOrOptions ? rangeOrOptions : { range: rangeOrOptions }; // eslint-disable-line no-in-operator
|
||||
const { contextRangeIndex } = (range.marker && range.marker.data || {}) as { contextRangeIndex?: number; };
|
||||
return {
|
||||
fileName: range.fileName,
|
||||
@ -3160,7 +3160,7 @@ namespace FourSlash {
|
||||
return this.getApplicableRefactorsWorker(this.getSelection(), this.activeFile.fileName);
|
||||
}
|
||||
private getApplicableRefactors(rangeOrMarker: Range | Marker, preferences = ts.emptyOptions): ReadonlyArray<ts.ApplicableRefactorInfo> {
|
||||
return this.getApplicableRefactorsWorker("position" in rangeOrMarker ? rangeOrMarker.position : rangeOrMarker, rangeOrMarker.fileName, preferences); // eslint-disable-line microsoft-typescript/no-in-operator
|
||||
return this.getApplicableRefactorsWorker("position" in rangeOrMarker ? rangeOrMarker.position : rangeOrMarker, rangeOrMarker.fileName, preferences); // eslint-disable-line no-in-operator
|
||||
}
|
||||
private getApplicableRefactorsWorker(positionOrRange: number | ts.TextRange, fileName: string, preferences = ts.emptyOptions): ReadonlyArray<ts.ApplicableRefactorInfo> {
|
||||
return this.languageService.getApplicableRefactors(fileName, positionOrRange, preferences) || ts.emptyArray;
|
||||
|
||||
@ -4,7 +4,7 @@ namespace Harness.LanguageService {
|
||||
const proxy = Object.create(/*prototype*/ null); // eslint-disable-line no-null/no-null
|
||||
const langSvc: any = info.languageService;
|
||||
for (const k of Object.keys(langSvc)) {
|
||||
// eslint-disable-next-line microsoft-typescript/only-arrow-functions
|
||||
// eslint-disable-next-line only-arrow-functions
|
||||
proxy[k] = function () {
|
||||
return langSvc[k].apply(langSvc, arguments);
|
||||
};
|
||||
@ -799,7 +799,7 @@ namespace Harness.LanguageService {
|
||||
create(info: ts.server.PluginCreateInfo) {
|
||||
const proxy = makeDefaultProxy(info);
|
||||
const langSvc: any = info.languageService;
|
||||
// eslint-disable-next-line microsoft-typescript/only-arrow-functions
|
||||
// eslint-disable-next-line only-arrow-functions
|
||||
proxy.getQuickInfoAtPosition = function () {
|
||||
const parts = langSvc.getQuickInfoAtPosition.apply(langSvc, arguments);
|
||||
if (parts.displayParts.length > 0) {
|
||||
|
||||
@ -363,7 +363,7 @@ namespace Playback {
|
||||
|
||||
function recordReplay<T extends ts.AnyFunction>(original: T, underlying: any) {
|
||||
function createWrapper(record: T, replay: T): T {
|
||||
// eslint-disable-next-line microsoft-typescript/only-arrow-functions
|
||||
// eslint-disable-next-line only-arrow-functions
|
||||
return <any>(function () {
|
||||
if (replayLog !== undefined) {
|
||||
return replay.apply(undefined, arguments);
|
||||
|
||||
@ -1290,7 +1290,7 @@ namespace ts.server {
|
||||
const pluginModule = pluginModuleFactory({ typescript: ts });
|
||||
const newLS = pluginModule.create(info);
|
||||
for (const k of Object.keys(this.languageService)) {
|
||||
// eslint-disable-next-line microsoft-typescript/no-in-operator
|
||||
// eslint-disable-next-line no-in-operator
|
||||
if (!(k in newLS)) {
|
||||
this.projectService.logger.info(`Plugin activation warning: Missing proxied method ${k} in created LS. Patching.`);
|
||||
(newLS as any)[k] = (this.languageService as any)[k];
|
||||
|
||||
@ -607,7 +607,7 @@ namespace ts.codefix {
|
||||
}
|
||||
|
||||
// return undefined argName when arg is null or undefined
|
||||
// eslint-disable-next-line microsoft-typescript/no-in-operator
|
||||
// eslint-disable-next-line no-in-operator
|
||||
if (!name || "identifier" in name && name.identifier.text === "undefined") {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@ -1439,7 +1439,7 @@ namespace ts.FindAllReferences.Core {
|
||||
}
|
||||
|
||||
function addReference(referenceLocation: Node, relatedSymbol: Symbol | RelatedSymbol, state: State): void {
|
||||
const { kind, symbol } = "kind" in relatedSymbol ? relatedSymbol : { kind: undefined, symbol: relatedSymbol }; // eslint-disable-line microsoft-typescript/no-in-operator
|
||||
const { kind, symbol } = "kind" in relatedSymbol ? relatedSymbol : { kind: undefined, symbol: relatedSymbol }; // eslint-disable-line no-in-operator
|
||||
const addRef = state.referenceAdder(symbol);
|
||||
if (state.options.implementations) {
|
||||
addImplementationReferences(referenceLocation, addRef, state);
|
||||
|
||||
@ -35,7 +35,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
private assertHasRealPosition(message?: string) {
|
||||
// eslint-disable-next-line microsoft-typescript/debug-assert
|
||||
// eslint-disable-next-line debug-assert
|
||||
Debug.assert(!positionIsSynthesized(this.pos) && !positionIsSynthesized(this.end), message || "Node must have a real position for this operation");
|
||||
}
|
||||
|
||||
|
||||
@ -17,7 +17,7 @@
|
||||
let debugObjectHost: { CollectGarbage(): void } = (function (this: any) { return this; })(); // eslint-disable-line prefer-const
|
||||
|
||||
// We need to use 'null' to interface with the managed side.
|
||||
/* eslint-disable microsoft-typescript/no-in-operator */
|
||||
/* eslint-disable no-in-operator */
|
||||
|
||||
/* @internal */
|
||||
namespace ts {
|
||||
@ -1275,7 +1275,7 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
/* eslint-enable microsoft-typescript/no-in-operator */
|
||||
/* eslint-enable no-in-operator */
|
||||
|
||||
/// TODO: this is used by VS, clean this up on both sides of the interface
|
||||
/* @internal */
|
||||
|
||||
@ -394,11 +394,11 @@ namespace ts.SignatureHelp {
|
||||
// not enough to put us in the substitution expression; we should consider ourselves part of
|
||||
// the *next* span's expression by offsetting the index (argIndex = (spanIndex + 1) + 1).
|
||||
//
|
||||
/* eslint-disable microsoft-typescript/no-double-space */
|
||||
/* eslint-disable no-double-space */
|
||||
// Example: f `# abcd $#{# 1 + 1# }# efghi ${ #"#hello"# } # `
|
||||
// ^ ^ ^ ^ ^ ^ ^ ^ ^
|
||||
// Case: 1 1 3 2 1 3 2 2 1
|
||||
/* eslint-enable microsoft-typescript/no-double-space */
|
||||
/* eslint-enable no-double-space */
|
||||
Debug.assert(position >= node.getStart(), "Assumed 'position' could not occur before node.");
|
||||
if (isTemplateLiteralToken(node)) {
|
||||
if (isInsideTemplateLiteral(node, position, sourceFile)) {
|
||||
|
||||
@ -47,7 +47,7 @@ namespace Harness.Parallel.Host {
|
||||
constructor(info: ErrorInfo | TestInfo) {
|
||||
super(info.name[info.name.length - 1]);
|
||||
this.info = info;
|
||||
this.state = "error" in info ? "failed" : "passed"; // eslint-disable-line microsoft-typescript/no-in-operator
|
||||
this.state = "error" in info ? "failed" : "passed"; // eslint-disable-line no-in-operator
|
||||
this.pending = false;
|
||||
}
|
||||
}
|
||||
@ -540,7 +540,7 @@ namespace Harness.Parallel.Host {
|
||||
function replayTest(runner: Mocha.Runner, test: RemoteTest) {
|
||||
runner.emit("test", test);
|
||||
if (test.isFailed()) {
|
||||
runner.emit("fail", test, "error" in test.info ? rebuildError(test.info) : new Error("Unknown error")); // eslint-disable-line microsoft-typescript/no-in-operator
|
||||
runner.emit("fail", test, "error" in test.info ? rebuildError(test.info) : new Error("Unknown error")); // eslint-disable-line no-in-operator
|
||||
}
|
||||
else {
|
||||
runner.emit("pass", test);
|
||||
|
||||
@ -665,7 +665,7 @@ namespace ts.projectSystem {
|
||||
export function openFilesForSession(files: ReadonlyArray<File | { readonly file: File | string, readonly projectRootPath: string }>, session: server.Session): void {
|
||||
for (const file of files) {
|
||||
session.executeCommand(makeSessionRequest<protocol.OpenRequestArgs>(CommandNames.Open,
|
||||
"projectRootPath" in file ? { file: typeof file.file === "string" ? file.file : file.file.path, projectRootPath: file.projectRootPath } : { file: file.path })); // eslint-disable-line microsoft-typescript/no-in-operator
|
||||
"projectRootPath" in file ? { file: typeof file.file === "string" ? file.file : file.file.path, projectRootPath: file.projectRootPath } : { file: file.path })); // eslint-disable-line no-in-operator
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user