From 0059763d8f5b600080fc1abef3b3d23929fb56e8 Mon Sep 17 00:00:00 2001 From: Alexander Date: Wed, 24 Jul 2019 07:17:16 +0300 Subject: [PATCH] move eslint rules from eslint-plugin-microsoft-typescript to scripts/eslint --- .eslintrc | 26 ++-- .npmignore | 1 + Gulpfile.js | 23 +++- package.json | 3 +- scripts/eslint/rules/boolean-trivia.ts | 105 ++++++++++++++++ scripts/eslint/rules/debug-assert.ts | 60 +++++++++ scripts/eslint/rules/no-double-space.ts | 81 ++++++++++++ scripts/eslint/rules/no-in-operator.ts | 32 +++++ scripts/eslint/rules/no-keywords.ts | 70 +++++++++++ .../rules/no-type-assertion-whitespace.ts | 43 +++++++ .../rules/object-literal-surrounding-space.ts | 71 +++++++++++ scripts/eslint/rules/only-arrow-functions.ts | 91 ++++++++++++++ scripts/eslint/rules/type-operator-spacing.ts | 44 +++++++ scripts/eslint/rules/utils.ts | 18 +++ scripts/eslint/tests/boolean-trivia.test.ts | 73 +++++++++++ scripts/eslint/tests/debug-assert.test.ts | 40 ++++++ scripts/eslint/tests/no-double-space.test.ts | 115 ++++++++++++++++++ scripts/eslint/tests/no-in-operator.test.ts | 26 ++++ scripts/eslint/tests/no-keywords.test.ts | 77 ++++++++++++ .../no-type-assertion-whitespace.test.ts | 31 +++++ .../object-literal-surrounding-space.test.ts | 40 ++++++ .../eslint/tests/only-arrow-functions.test.ts | 101 +++++++++++++++ scripts/eslint/tests/support/RuleTester.ts | 5 + scripts/eslint/tests/support/tsconfig.json | 9 ++ .../tests/type-operator-spacing.test.ts | 35 ++++++ scripts/eslint/tsconfig.json | 28 +++++ src/compiler/checker.ts | 2 +- src/compiler/core.ts | 4 +- src/compiler/factory.ts | 2 +- src/compiler/sourcemap.ts | 4 +- src/harness/fourslash.ts | 4 +- src/harness/harnessLanguageService.ts | 4 +- src/harness/loggedIO.ts | 2 +- src/server/project.ts | 2 +- .../codefixes/convertToAsyncFunction.ts | 2 +- src/services/findAllReferences.ts | 2 +- src/services/services.ts | 2 +- src/services/shims.ts | 4 +- src/services/signatureHelp.ts | 4 +- src/testRunner/parallel/host.ts | 4 +- src/testRunner/unittests/tsserver/helpers.ts | 2 +- 41 files changed, 1254 insertions(+), 38 deletions(-) create mode 100644 scripts/eslint/rules/boolean-trivia.ts create mode 100644 scripts/eslint/rules/debug-assert.ts create mode 100644 scripts/eslint/rules/no-double-space.ts create mode 100644 scripts/eslint/rules/no-in-operator.ts create mode 100644 scripts/eslint/rules/no-keywords.ts create mode 100644 scripts/eslint/rules/no-type-assertion-whitespace.ts create mode 100644 scripts/eslint/rules/object-literal-surrounding-space.ts create mode 100644 scripts/eslint/rules/only-arrow-functions.ts create mode 100644 scripts/eslint/rules/type-operator-spacing.ts create mode 100644 scripts/eslint/rules/utils.ts create mode 100644 scripts/eslint/tests/boolean-trivia.test.ts create mode 100644 scripts/eslint/tests/debug-assert.test.ts create mode 100644 scripts/eslint/tests/no-double-space.test.ts create mode 100644 scripts/eslint/tests/no-in-operator.test.ts create mode 100644 scripts/eslint/tests/no-keywords.test.ts create mode 100644 scripts/eslint/tests/no-type-assertion-whitespace.test.ts create mode 100644 scripts/eslint/tests/object-literal-surrounding-space.test.ts create mode 100644 scripts/eslint/tests/only-arrow-functions.test.ts create mode 100644 scripts/eslint/tests/support/RuleTester.ts create mode 100644 scripts/eslint/tests/support/tsconfig.json create mode 100644 scripts/eslint/tests/type-operator-spacing.test.ts create mode 100644 scripts/eslint/tsconfig.json diff --git a/.eslintrc b/.eslintrc index e61f20d6871..87df41e69a1 100644 --- a/.eslintrc +++ b/.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" diff --git a/.npmignore b/.npmignore index 67fa1d5ad89..a0769e52a54 100644 --- a/.npmignore +++ b/.npmignore @@ -11,6 +11,7 @@ src tests Jakefile.js .eslintrc +.eslintignore .editorconfig .failed-tests .git diff --git a/Gulpfile.js b/Gulpfile.js index 48692c61133..d234e393f26 100644 --- a/Gulpfile.js +++ b/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."; diff --git a/package.json b/package.json index d702a2bab78..6e25ee46e19 100644 --- a/package.json +++ b/package.json @@ -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" }, diff --git a/scripts/eslint/rules/boolean-trivia.ts b/scripts/eslint/rules/boolean-trivia.ts new file mode 100644 index 00000000000..a9712513964 --- /dev/null +++ b/scripts/eslint/rules/boolean-trivia.ts @@ -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, + }; + }, +}); diff --git a/scripts/eslint/rules/debug-assert.ts b/scripts/eslint/rules/debug-assert.ts new file mode 100644 index 00000000000..0e8769f1fe5 --- /dev/null +++ b/scripts/eslint/rules/debug-assert.ts @@ -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, + }; + }, +}); diff --git a/scripts/eslint/rules/no-double-space.ts b/scripts/eslint/rules/no-double-space.ts new file mode 100644 index 00000000000..69f65d4eb8b --- /dev/null +++ b/scripts/eslint/rules/no-double-space.ts @@ -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, + }; + }, +}); diff --git a/scripts/eslint/rules/no-in-operator.ts b/scripts/eslint/rules/no-in-operator.ts new file mode 100644 index 00000000000..99bf5fbeb7b --- /dev/null +++ b/scripts/eslint/rules/no-in-operator.ts @@ -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, + }; + }, +}); diff --git a/scripts/eslint/rules/no-keywords.ts b/scripts/eslint/rules/no-keywords.ts new file mode 100644 index 00000000000..b7aa08336c4 --- /dev/null +++ b/scripts/eslint/rules/no-keywords.ts @@ -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 }; + }, +}); diff --git a/scripts/eslint/rules/no-type-assertion-whitespace.ts b/scripts/eslint/rules/no-type-assertion-whitespace.ts new file mode 100644 index 00000000000..339c7c86ff2 --- /dev/null +++ b/scripts/eslint/rules/no-type-assertion-whitespace.ts @@ -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, + }; + }, +}); diff --git a/scripts/eslint/rules/object-literal-surrounding-space.ts b/scripts/eslint/rules/object-literal-surrounding-space.ts new file mode 100644 index 00000000000..6cfea3cfac7 --- /dev/null +++ b/scripts/eslint/rules/object-literal-surrounding-space.ts @@ -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, + }; + }, +}); diff --git a/scripts/eslint/rules/only-arrow-functions.ts b/scripts/eslint/rules/only-arrow-functions.ts new file mode 100644 index 00000000000..feac5a82fcd --- /dev/null +++ b/scripts/eslint/rules/only-arrow-functions.ts @@ -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({ + 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, + }; + }, +}); diff --git a/scripts/eslint/rules/type-operator-spacing.ts b/scripts/eslint/rules/type-operator-spacing.ts new file mode 100644 index 00000000000..4c6dad00f4c --- /dev/null +++ b/scripts/eslint/rules/type-operator-spacing.ts @@ -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, + }; + }, +}); diff --git a/scripts/eslint/rules/utils.ts b/scripts/eslint/rules/utils.ts new file mode 100644 index 00000000000..2e14fd22f79 --- /dev/null +++ b/scripts/eslint/rules/utils.ts @@ -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; +}; diff --git a/scripts/eslint/tests/boolean-trivia.test.ts b/scripts/eslint/tests/boolean-trivia.test.ts new file mode 100644 index 00000000000..0758bdbb12b --- /dev/null +++ b/scripts/eslint/tests/boolean-trivia.test.ts @@ -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" }], + }], +}); diff --git a/scripts/eslint/tests/debug-assert.test.ts b/scripts/eslint/tests/debug-assert.test.ts new file mode 100644 index 00000000000..f6cbbca856d --- /dev/null +++ b/scripts/eslint/tests/debug-assert.test.ts @@ -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", + }], + }], +}); diff --git a/scripts/eslint/tests/no-double-space.test.ts b/scripts/eslint/tests/no-double-space.test.ts new file mode 100644 index 00000000000..de9b25a80dc --- /dev/null +++ b/scripts/eslint/tests/no-double-space.test.ts @@ -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 }], + }], +}); diff --git a/scripts/eslint/tests/no-in-operator.test.ts b/scripts/eslint/tests/no-in-operator.test.ts new file mode 100644 index 00000000000..6cd5b43b2ef --- /dev/null +++ b/scripts/eslint/tests/no-in-operator.test.ts @@ -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" }], + }], +}); diff --git a/scripts/eslint/tests/no-keywords.test.ts b/scripts/eslint/tests/no-keywords.test.ts new file mode 100644 index 00000000000..5b6e0819b3e --- /dev/null +++ b/scripts/eslint/tests/no-keywords.test.ts @@ -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" }], + }], +}); diff --git a/scripts/eslint/tests/no-type-assertion-whitespace.test.ts b/scripts/eslint/tests/no-type-assertion-whitespace.test.ts new file mode 100644 index 00000000000..d37968214e0 --- /dev/null +++ b/scripts/eslint/tests/no-type-assertion-whitespace.test.ts @@ -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 = 1`, + }, { + code: `const s = (new Symbol(1, 'name'));`, + }], + + invalid: [{ + code: `const a = 1`, + errors: [{ messageId: "noTypeAssertionWhitespace", column: 19, line: 1 }], + }, { + code: `const a = 1`, + errors: [{ messageId: "noTypeAssertionWhitespace", column: 19, line: 1 }], + }, { + code: `const a = 1`, + errors: [{ messageId: "noTypeAssertionWhitespace", column: 19, line: 1 }], + }, { + code: `const s = (new Symbol(1, 'name'));`, + errors: [{ messageId: "noTypeAssertionWhitespace", column: 17, line: 1 }], + }], +}); diff --git a/scripts/eslint/tests/object-literal-surrounding-space.test.ts b/scripts/eslint/tests/object-literal-surrounding-space.test.ts new file mode 100644 index 00000000000..16ec59cdd19 --- /dev/null +++ b/scripts/eslint/tests/object-literal-surrounding-space.test.ts @@ -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" }], + }], +}); diff --git a/scripts/eslint/tests/only-arrow-functions.test.ts b/scripts/eslint/tests/only-arrow-functions.test.ts new file mode 100644 index 00000000000..0f1e70a43a4 --- /dev/null +++ b/scripts/eslint/tests/only-arrow-functions.test.ts @@ -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" }], + }], +}); + diff --git a/scripts/eslint/tests/support/RuleTester.ts b/scripts/eslint/tests/support/RuleTester.ts new file mode 100644 index 00000000000..56a06427fc7 --- /dev/null +++ b/scripts/eslint/tests/support/RuleTester.ts @@ -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; diff --git a/scripts/eslint/tests/support/tsconfig.json b/scripts/eslint/tests/support/tsconfig.json new file mode 100644 index 00000000000..9a059bababf --- /dev/null +++ b/scripts/eslint/tests/support/tsconfig.json @@ -0,0 +1,9 @@ +{ + "compilerOptions": { + "esModuleInterop": true, + "target": "es5", + "module": "commonjs", + "strict": true, + "lib": ["es2015", "es2017", "esnext"] + } +} \ No newline at end of file diff --git a/scripts/eslint/tests/type-operator-spacing.test.ts b/scripts/eslint/tests/type-operator-spacing.test.ts new file mode 100644 index 00000000000..9e18d5796a4 --- /dev/null +++ b/scripts/eslint/tests/type-operator-spacing.test.ts @@ -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" }], + }], +}); diff --git a/scripts/eslint/tsconfig.json b/scripts/eslint/tsconfig.json new file mode 100644 index 00000000000..edeac25c7f3 --- /dev/null +++ b/scripts/eslint/tsconfig.json @@ -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" + ] +} diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 91a44dd7c10..fd0b1dbcece 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -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( diff --git a/src/compiler/core.ts b/src/compiler/core.ts index ae223031840..f029a74c1e2 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -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 () => Map) | 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; } diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index 13bf7562434..54b08ad0fc9 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -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"); } diff --git a/src/compiler/sourcemap.ts b/src/compiler/sourcemap.ts index 63f2c449215..9491c700d39 100644 --- a/src/compiler/sourcemap.ts +++ b/src/compiler/sourcemap.ts @@ -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(); diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index 89e722342b3..a987e45664a 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -1205,7 +1205,7 @@ namespace FourSlash { const sort = (locations: ReadonlyArray | 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 { - 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 { return this.languageService.getApplicableRefactors(fileName, positionOrRange, preferences) || ts.emptyArray; diff --git a/src/harness/harnessLanguageService.ts b/src/harness/harnessLanguageService.ts index 3c995d172c8..edd27361489 100644 --- a/src/harness/harnessLanguageService.ts +++ b/src/harness/harnessLanguageService.ts @@ -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) { diff --git a/src/harness/loggedIO.ts b/src/harness/loggedIO.ts index 64c8ad297ef..ffa9344a863 100644 --- a/src/harness/loggedIO.ts +++ b/src/harness/loggedIO.ts @@ -363,7 +363,7 @@ namespace Playback { function recordReplay(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 (function () { if (replayLog !== undefined) { return replay.apply(undefined, arguments); diff --git a/src/server/project.ts b/src/server/project.ts index be6fa965873..3d52ea6f607 100644 --- a/src/server/project.ts +++ b/src/server/project.ts @@ -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]; diff --git a/src/services/codefixes/convertToAsyncFunction.ts b/src/services/codefixes/convertToAsyncFunction.ts index a537c0c40d3..406e0f5f589 100644 --- a/src/services/codefixes/convertToAsyncFunction.ts +++ b/src/services/codefixes/convertToAsyncFunction.ts @@ -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; } diff --git a/src/services/findAllReferences.ts b/src/services/findAllReferences.ts index e213db9dbd9..ec098b3b853 100644 --- a/src/services/findAllReferences.ts +++ b/src/services/findAllReferences.ts @@ -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); diff --git a/src/services/services.ts b/src/services/services.ts index eff02cc821c..de380384d76 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -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"); } diff --git a/src/services/shims.ts b/src/services/shims.ts index 3c8f83521cd..a4f1131812b 100644 --- a/src/services/shims.ts +++ b/src/services/shims.ts @@ -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 */ diff --git a/src/services/signatureHelp.ts b/src/services/signatureHelp.ts index 40d4a703dd2..8226e3c38bf 100644 --- a/src/services/signatureHelp.ts +++ b/src/services/signatureHelp.ts @@ -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)) { diff --git a/src/testRunner/parallel/host.ts b/src/testRunner/parallel/host.ts index 0db1a49c26c..49c9b3c66ef 100644 --- a/src/testRunner/parallel/host.ts +++ b/src/testRunner/parallel/host.ts @@ -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); diff --git a/src/testRunner/unittests/tsserver/helpers.ts b/src/testRunner/unittests/tsserver/helpers.ts index fcc3a299ffb..37165307322 100644 --- a/src/testRunner/unittests/tsserver/helpers.ts +++ b/src/testRunner/unittests/tsserver/helpers.ts @@ -665,7 +665,7 @@ namespace ts.projectSystem { export function openFilesForSession(files: ReadonlyArray, session: server.Session): void { for (const file of files) { session.executeCommand(makeSessionRequest(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 } }