From bf00c630911d419803ba99c387e93dcc43ca0b19 Mon Sep 17 00:00:00 2001 From: Brad Zacher Date: Mon, 13 May 2019 21:57:01 -0700 Subject: [PATCH 1/4] feat: make utils/TSESLint export typed classes instead of just types --- .eslintrc.json | 7 +- packages/eslint-plugin-tslint/package.json | 3 +- packages/eslint-plugin-tslint/src/index.ts | 159 +--------------- .../eslint-plugin-tslint/src/rules/config.ts | 176 ++++++++++++++++++ .../eslint-plugin-tslint/tests/index.spec.ts | 84 +++++---- packages/eslint-plugin/package.json | 2 +- .../src/configs/eslint-recommended.ts | 2 + .../src/util/getParserServices.ts | 6 +- packages/eslint-plugin/tests/RuleTester.ts | 4 +- .../tests/rules/array-type.test.ts | 4 +- .../tests/rules/await-thenable.test.ts | 12 +- .../no-unnecessary-type-assertion.test.ts | 11 +- .../tests/rules/prefer-function-type.test.ts | 2 +- .../rules/promise-function-async.test.ts | 12 +- .../tests/rules/unified-signatures.test.ts | 2 +- packages/experimental-utils/package.json | 1 + .../src/ts-eslint/CLIEngine.ts | 90 +++++++++ .../src/ts-eslint/Linter.ts | 26 ++- .../src/ts-eslint/ParserOptions.ts | 2 +- .../experimental-utils/src/ts-eslint/Rule.ts | 18 +- .../src/ts-eslint/RuleTester.ts | 9 +- .../src/ts-eslint/SourceCode.ts | 98 +++++----- .../experimental-utils/tsconfig.build.json | 2 +- .../experimental-utils/typings/eslint.d.ts | 15 ++ packages/parser/src/parser-options.ts | 24 +-- packages/parser/tests/lib/basics.ts | 12 +- packages/parser/tests/lib/parser.ts | 5 +- packages/parser/tests/lib/tsx.ts | 4 +- packages/typescript-estree/src/convert.ts | 4 +- .../typescript-estree/src/tsconfig-parser.ts | 2 +- yarn.lock | 15 +- 31 files changed, 474 insertions(+), 339 deletions(-) create mode 100644 packages/eslint-plugin-tslint/src/rules/config.ts create mode 100644 packages/experimental-utils/src/ts-eslint/CLIEngine.ts create mode 100644 packages/experimental-utils/typings/eslint.d.ts diff --git a/.eslintrc.json b/.eslintrc.json index 00e766eaaef1..649b9ec25021 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -5,11 +5,14 @@ "es6": true, "node": true }, - "extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"], + "extends": [ + "eslint:recommended", + "plugin:@typescript-eslint/eslint-recommended", + "plugin:@typescript-eslint/recommended" + ], "rules": { "comma-dangle": ["error", "always-multiline"], "curly": ["error", "all"], - "no-dupe-class-members": "off", "no-mixed-operators": "error", "no-console": "off", "no-dupe-class-members": "off", diff --git a/packages/eslint-plugin-tslint/package.json b/packages/eslint-plugin-tslint/package.json index b6bfa14be9e9..fba222f6dbf3 100644 --- a/packages/eslint-plugin-tslint/package.json +++ b/packages/eslint-plugin-tslint/package.json @@ -27,6 +27,7 @@ "typecheck": "tsc --noEmit" }, "dependencies": { + "@typescript-eslint/experimental-utils": "1.9.0", "lodash.memoize": "^4.1.2" }, "peerDependencies": { @@ -34,7 +35,7 @@ "tslint": "^5.0.0" }, "devDependencies": { - "@types/eslint": "^4.16.3", + "@types/json-schema": "^7.0.3", "@types/lodash.memoize": "^4.1.4", "@typescript-eslint/parser": "1.9.0" } diff --git a/packages/eslint-plugin-tslint/src/index.ts b/packages/eslint-plugin-tslint/src/index.ts index 46574a106c8c..a638ae2ba566 100644 --- a/packages/eslint-plugin-tslint/src/index.ts +++ b/packages/eslint-plugin-tslint/src/index.ts @@ -1,160 +1,9 @@ -import { Rule } from 'eslint'; -import memoize from 'lodash.memoize'; -import { Configuration, RuleSeverity } from 'tslint'; -import { Program } from 'typescript'; -import { CustomLinter } from './custom-linter'; -import { ParserServices } from '@typescript-eslint/typescript-estree'; - -//------------------------------------------------------------------------------ -// Plugin Definition -//------------------------------------------------------------------------------ - -type RawRuleConfig = - | null - | undefined - | boolean - | any[] - | { - severity?: RuleSeverity | 'warn' | 'none' | 'default'; - options?: any; - }; - -interface RawRulesConfig { - [key: string]: RawRuleConfig; -} +import configRule from './rules/config'; /** - * Construct a configFile for TSLint + * Expose a single rule called "config", which will be accessed in the user's eslint config files + * via "tslint/config" */ -const tslintConfig = memoize( - ( - lintFile: string, - tslintRules: RawRulesConfig, - tslintRulesDirectory: string[], - ) => { - if (lintFile != null) { - return Configuration.loadConfigurationFromPath(lintFile); - } - return Configuration.parseConfigFile({ - rules: tslintRules || {}, - rulesDirectory: tslintRulesDirectory || [], - }); - }, - (lintFile: string | undefined, tslintRules = {}, tslintRulesDirectory = []) => - `${lintFile}_${Object.keys(tslintRules).join(',')}_${ - tslintRulesDirectory.length - }`, -); - export const rules = { - /** - * Expose a single rule called "config", which will be accessed in the user's eslint config files - * via "tslint/config" - */ - config: { - meta: { - docs: { - description: - 'Wraps a TSLint configuration and lints the whole source using TSLint', - category: 'TSLint', - }, - schema: [ - { - type: 'object', - properties: { - rules: { - type: 'object', - /** - * No fixed schema properties for rules, as this would be a permanently moving target - */ - additionalProperties: true, - }, - rulesDirectory: { - type: 'array', - items: { - type: 'string', - }, - }, - lintFile: { - type: 'string', - }, - }, - additionalProperties: false, - }, - ], - }, - create(context: Rule.RuleContext) { - const fileName = context.getFilename(); - const sourceCode = context.getSourceCode().text; - const parserServices: ParserServices | undefined = context.parserServices; - - /** - * The user needs to have configured "project" in their parserOptions - * for @typescript-eslint/parser - */ - if (!parserServices || !parserServices.program) { - throw new Error( - `You must provide a value for the "parserOptions.project" property for @typescript-eslint/parser`, - ); - } - - /** - * The TSLint rules configuration passed in by the user - */ - const { - rules: tslintRules, - rulesDirectory: tslintRulesDirectory, - lintFile, - } = context.options[0]; - - const program: Program = parserServices.program; - - /** - * Create an instance of TSLint - * Lint the source code using the configured TSLint instance, and the rules which have been - * passed via the ESLint rule options for this rule (using "tslint/config") - */ - const tslintOptions = { - formatter: 'json', - fix: false, - }; - const tslint = new CustomLinter(tslintOptions, program); - const configuration = tslintConfig( - lintFile, - tslintRules, - tslintRulesDirectory, - ); - tslint.lint(fileName, sourceCode, configuration); - - const result = tslint.getResult(); - - /** - * Format the TSLint results for ESLint - */ - if (result.failures && result.failures.length) { - result.failures.forEach(failure => { - const start = failure.getStartPosition().getLineAndCharacter(); - const end = failure.getEndPosition().getLineAndCharacter(); - context.report({ - message: `${failure.getFailure()} (tslint:${failure.getRuleName()})`, - loc: { - start: { - line: start.line + 1, - column: start.character, - }, - end: { - line: end.line + 1, - column: end.character, - }, - }, - }); - }); - } - - /** - * Return an empty object for the ESLint rule - */ - return {}; - }, - }, + config: configRule, }; diff --git a/packages/eslint-plugin-tslint/src/rules/config.ts b/packages/eslint-plugin-tslint/src/rules/config.ts new file mode 100644 index 000000000000..e9cd3f53bb5f --- /dev/null +++ b/packages/eslint-plugin-tslint/src/rules/config.ts @@ -0,0 +1,176 @@ +import { + ESLintUtils, + ParserServices, +} from '@typescript-eslint/experimental-utils'; +import memoize from 'lodash.memoize'; +import { Configuration, RuleSeverity } from 'tslint'; +import { CustomLinter } from '../custom-linter'; + +// note - cannot migrate this to an import statement because it will make TSC copy the package.json to the dist folder +const version = require('../../package.json').version; + +const createRule = ESLintUtils.RuleCreator( + () => + `https://github.com/typescript-eslint/typescript-eslint/blob/v${version}/packages/eslint-plugin-tslint/README.md`, +); +export type RawRulesConfig = Record< + string, + | null + | undefined + | boolean + | any[] + | { + severity?: RuleSeverity | 'warn' | 'none' | 'default'; + options?: any; + } +>; + +export type MessageIds = 'failure'; +export type Options = [ + { + rules?: RawRulesConfig; + rulesDirectory?: string[]; + lintFile?: string; + } +]; + +/** + * Construct a configFile for TSLint + */ +const tslintConfig = memoize( + ( + lintFile?: string, + tslintRules?: RawRulesConfig, + tslintRulesDirectory?: string[], + ) => { + if (lintFile != null) { + return Configuration.loadConfigurationFromPath(lintFile); + } + return Configuration.parseConfigFile({ + rules: tslintRules || {}, + rulesDirectory: tslintRulesDirectory || [], + }); + }, + (lintFile: string | undefined, tslintRules = {}, tslintRulesDirectory = []) => + `${lintFile}_${Object.keys(tslintRules).join(',')}_${ + tslintRulesDirectory.length + }`, +); + +export default createRule({ + name: 'config', + meta: { + docs: { + description: + 'Wraps a TSLint configuration and lints the whole source using TSLint', + category: 'TSLint' as any, + recommended: false, + }, + type: 'problem', + messages: { + failure: '{{message}} (tslint:{{ruleName}})`', + }, + schema: [ + { + type: 'object', + properties: { + rules: { + type: 'object', + /** + * No fixed schema properties for rules, as this would be a permanently moving target + */ + additionalProperties: true, + }, + rulesDirectory: { + type: 'array', + items: { + type: 'string', + }, + }, + lintFile: { + type: 'string', + }, + }, + additionalProperties: false, + }, + ], + }, + defaultOptions: [] as any, + create(context) { + const fileName = context.getFilename(); + const sourceCode = context.getSourceCode().text; + const parserServices: ParserServices | undefined = context.parserServices; + + /** + * The user needs to have configured "project" in their parserOptions + * for @typescript-eslint/parser + */ + if (!parserServices || !parserServices.program) { + throw new Error( + `You must provide a value for the "parserOptions.project" property for @typescript-eslint/parser`, + ); + } + + /** + * The TSLint rules configuration passed in by the user + */ + const { + rules: tslintRules, + rulesDirectory: tslintRulesDirectory, + lintFile, + } = context.options[0]; + + const program = parserServices.program; + + /** + * Create an instance of TSLint + * Lint the source code using the configured TSLint instance, and the rules which have been + * passed via the ESLint rule options for this rule (using "tslint/config") + */ + const tslintOptions = { + formatter: 'json', + fix: false, + }; + const tslint = new CustomLinter(tslintOptions, program); + const configuration = tslintConfig( + lintFile, + tslintRules, + tslintRulesDirectory, + ); + tslint.lint(fileName, sourceCode, configuration); + + const result = tslint.getResult(); + + /** + * Format the TSLint results for ESLint + */ + if (result.failures && result.failures.length) { + result.failures.forEach(failure => { + const start = failure.getStartPosition().getLineAndCharacter(); + const end = failure.getEndPosition().getLineAndCharacter(); + context.report({ + messageId: 'failure', + data: { + message: failure.getFailure(), + ruleName: failure.getRuleName(), + }, + loc: { + start: { + line: start.line + 1, + column: start.character, + }, + end: { + line: end.line + 1, + column: end.character, + }, + }, + }); + }); + } + + /** + * Return an empty object for the ESLint rule + */ + return {}; + }, +}); diff --git a/packages/eslint-plugin-tslint/tests/index.spec.ts b/packages/eslint-plugin-tslint/tests/index.spec.ts index c62980fb398a..516f5a741b44 100644 --- a/packages/eslint-plugin-tslint/tests/index.spec.ts +++ b/packages/eslint-plugin-tslint/tests/index.spec.ts @@ -1,8 +1,8 @@ -import { rules } from '../src'; -import { Linter, RuleTester } from 'eslint'; +import { TSESLint } from '@typescript-eslint/experimental-utils'; import { readFileSync } from 'fs'; +import rule, { Options } from '../src/rules/config'; -const ruleTester = new RuleTester({ +const ruleTester = new TSESLint.RuleTester({ parserOptions: { ecmaVersion: 6, sourceType: 'module', @@ -19,29 +19,33 @@ const ruleTester = new RuleTester({ /** * Inline rules should be supported */ -const tslintRulesConfig = { - rules: { - semicolon: [true, 'always'], +const tslintRulesConfig: Options = [ + { + rules: { + semicolon: [true, 'always'], + }, }, -}; +]; /** * Custom rules directories should be supported */ -const tslintRulesDirectoryConfig = { - rulesDirectory: ['./tests/test-tslint-rules-directory'], - rules: { - 'always-fail': { - severity: 'error', +const tslintRulesDirectoryConfig: Options = [ + { + rulesDirectory: ['./tests/test-tslint-rules-directory'], + rules: { + 'always-fail': { + severity: 'error', + }, }, }, -}; +]; -ruleTester.run('tslint/config', rules.config, { +ruleTester.run('tslint/config', rule, { valid: [ { code: 'var foo = true;', - options: [tslintRulesConfig], + options: tslintRulesConfig, }, { filename: './tests/test-project/file-spec.ts', @@ -52,15 +56,11 @@ ruleTester.run('tslint/config', rules.config, { parserOptions: { project: `${__dirname}/test-project/tsconfig.json`, }, - options: [ - { - ...tslintRulesConfig, - }, - ], + options: tslintRulesConfig, }, { code: 'throw "should be ok because rule is not loaded";', - options: [tslintRulesConfig], + options: tslintRulesConfig, }, ], @@ -70,18 +70,26 @@ ruleTester.run('tslint/config', rules.config, { code: 'throw "err" // no-string-throw', errors: [ { - message: - 'Throwing plain strings (not instances of Error) gives no stack traces (tslint:no-string-throw)', + messageId: 'failure', + data: { + message: + 'Throwing plain strings (not instances of Error) gives no stack traces', + ruleName: 'no-string-throw', + }, }, ], }, { code: 'var foo = true // semicolon', - options: [tslintRulesConfig], + options: tslintRulesConfig, output: 'var foo = true // semicolon', errors: [ { - message: 'Missing semicolon (tslint:semicolon)', + messageId: 'failure', + data: { + message: 'Missing semicolon', + ruleName: 'semicolon', + }, line: 1, column: 15, }, @@ -89,11 +97,15 @@ ruleTester.run('tslint/config', rules.config, { }, { code: 'var foo = true // fail', - options: [tslintRulesDirectoryConfig], + options: tslintRulesDirectoryConfig, output: 'var foo = true // fail', errors: [ { - message: 'failure (tslint:always-fail)', + messageId: 'failure', + data: { + message: 'failure', + ruleName: 'always-fail', + }, line: 1, column: 1, }, @@ -118,8 +130,12 @@ ruleTester.run('tslint/config', rules.config, { ], errors: [ { - message: - 'Operands of \'+\' operation must either be both strings or both numbers, but found 1 + "2". Consider using template literals. (tslint:restrict-plus-operands)', + messageId: 'failure', + data: { + message: + 'Operands of \'+\' operation must either be both strings or both numbers, but found 1 + "2". Consider using template literals.', + ruleName: 'restrict-plus-operands', + }, }, ], }, @@ -127,9 +143,9 @@ ruleTester.run('tslint/config', rules.config, { }); describe('tslint/error', () => { - function testOutput(code: string, config: Linter.Config): void { - const linter = new Linter(); - linter.defineRule('tslint/config', rules.config); + function testOutput(code: string, config: TSESLint.Linter.Config): void { + const linter = new TSESLint.Linter(); + linter.defineRule('tslint/config', rule); expect(() => linter.verify(code, config)).toThrow( `You must provide a value for the "parserOptions.project" property for @typescript-eslint/parser`, @@ -157,9 +173,9 @@ describe('tslint/error', () => { }); it('should not crash if there is no tslint rules specified', () => { - const linter = new Linter(); + const linter = new TSESLint.Linter(); jest.spyOn(console, 'warn').mockImplementation(); - linter.defineRule('tslint/config', rules.config); + linter.defineRule('tslint/config', rule); expect(() => linter.verify('foo;', { parserOptions: { diff --git a/packages/eslint-plugin/package.json b/packages/eslint-plugin/package.json index 383c08dd392e..7f4d04b7b39c 100644 --- a/packages/eslint-plugin/package.json +++ b/packages/eslint-plugin/package.json @@ -37,7 +37,6 @@ }, "dependencies": { "@typescript-eslint/experimental-utils": "1.9.0", - "@typescript-eslint/parser": "1.9.0", "eslint-utils": "^1.3.1", "functional-red-black-tree": "^1.0.1", "regexpp": "^2.0.1", @@ -48,6 +47,7 @@ "eslint-docs": "^0.2.6" }, "peerDependencies": { + "@typescript-eslint/parser": "1.9.0", "eslint": "^5.0.0" } } diff --git a/packages/eslint-plugin/src/configs/eslint-recommended.ts b/packages/eslint-plugin/src/configs/eslint-recommended.ts index a0f66c7a201b..283cd46aa2f2 100644 --- a/packages/eslint-plugin/src/configs/eslint-recommended.ts +++ b/packages/eslint-plugin/src/configs/eslint-recommended.ts @@ -34,6 +34,8 @@ export default { 'no-undef': 'off', // This is already checked by Typescript. 'no-dupe-class-members': 'off', + // This is already checked by Typescript. + 'no-redeclare': 'off', /** * 2. Enable more ideomatic code */ diff --git a/packages/eslint-plugin/src/util/getParserServices.ts b/packages/eslint-plugin/src/util/getParserServices.ts index 2cc8b4981596..84a9dea98740 100644 --- a/packages/eslint-plugin/src/util/getParserServices.ts +++ b/packages/eslint-plugin/src/util/getParserServices.ts @@ -1,5 +1,7 @@ -import { ParserServices } from '@typescript-eslint/parser'; -import { TSESLint } from '@typescript-eslint/experimental-utils'; +import { + ParserServices, + TSESLint, +} from '@typescript-eslint/experimental-utils'; type RequiredParserServices = { [k in keyof ParserServices]: Exclude diff --git a/packages/eslint-plugin/tests/RuleTester.ts b/packages/eslint-plugin/tests/RuleTester.ts index 4db8bf3909c7..34e99972bf7c 100644 --- a/packages/eslint-plugin/tests/RuleTester.ts +++ b/packages/eslint-plugin/tests/RuleTester.ts @@ -1,8 +1,8 @@ import { TSESLint, ESLintUtils } from '@typescript-eslint/experimental-utils'; -import { RuleTester as ESLintRuleTester } from 'eslint'; import * as path from 'path'; -const RuleTester: TSESLint.RuleTester = ESLintRuleTester as any; +// re-export the RuleTester from here to make it easier to do the tests +const RuleTester = TSESLint.RuleTester; function getFixturesRootDir() { return path.join(process.cwd(), 'tests/fixtures/'); diff --git a/packages/eslint-plugin/tests/rules/array-type.test.ts b/packages/eslint-plugin/tests/rules/array-type.test.ts index 31d0b018d322..70343f688fcf 100644 --- a/packages/eslint-plugin/tests/rules/array-type.test.ts +++ b/packages/eslint-plugin/tests/rules/array-type.test.ts @@ -1,6 +1,6 @@ +import { TSESLint } from '@typescript-eslint/experimental-utils'; import rule from '../../src/rules/array-type'; import { RuleTester } from '../RuleTester'; -import { Linter } from 'eslint'; const ruleTester = new RuleTester({ parser: '@typescript-eslint/parser', @@ -889,7 +889,7 @@ describe('array-type (nested)', () => { describe('should deeply fix correctly', () => { function testOutput(option: string, code: string, output: string): void { it(code, () => { - const linter = new Linter(); + const linter = new TSESLint.Linter(); linter.defineRule('array-type', Object.assign({}, rule) as any); const result = linter.verifyAndFix( diff --git a/packages/eslint-plugin/tests/rules/await-thenable.test.ts b/packages/eslint-plugin/tests/rules/await-thenable.test.ts index deca521aefa8..5ee0f7d03410 100644 --- a/packages/eslint-plugin/tests/rules/await-thenable.test.ts +++ b/packages/eslint-plugin/tests/rules/await-thenable.test.ts @@ -2,16 +2,14 @@ import rule from '../../src/rules/await-thenable'; import { RuleTester, getFixturesRootDir } from '../RuleTester'; const rootDir = getFixturesRootDir(); -const parserOptions = { - ecmaVersion: 2018, - tsconfigRootDir: rootDir, - project: './tsconfig.json', -}; - const messageId = 'await'; const ruleTester = new RuleTester({ - parserOptions, + parserOptions: { + ecmaVersion: 2018, + tsconfigRootDir: rootDir, + project: './tsconfig.json', + }, parser: '@typescript-eslint/parser', }); diff --git a/packages/eslint-plugin/tests/rules/no-unnecessary-type-assertion.test.ts b/packages/eslint-plugin/tests/rules/no-unnecessary-type-assertion.test.ts index 21428ba7256e..7106fc461b4a 100644 --- a/packages/eslint-plugin/tests/rules/no-unnecessary-type-assertion.test.ts +++ b/packages/eslint-plugin/tests/rules/no-unnecessary-type-assertion.test.ts @@ -3,13 +3,12 @@ import rule from '../../src/rules/no-unnecessary-type-assertion'; import { RuleTester } from '../RuleTester'; const rootDir = path.join(process.cwd(), 'tests/fixtures'); -const parserOptions = { - ecmaVersion: 2015, - tsconfigRootDir: rootDir, - project: './tsconfig.json', -}; const ruleTester = new RuleTester({ - parserOptions, + parserOptions: { + ecmaVersion: 2015, + tsconfigRootDir: rootDir, + project: './tsconfig.json', + }, parser: '@typescript-eslint/parser', }); diff --git a/packages/eslint-plugin/tests/rules/prefer-function-type.test.ts b/packages/eslint-plugin/tests/rules/prefer-function-type.test.ts index a472d8619fe3..b4c6b4a3de85 100644 --- a/packages/eslint-plugin/tests/rules/prefer-function-type.test.ts +++ b/packages/eslint-plugin/tests/rules/prefer-function-type.test.ts @@ -2,7 +2,7 @@ import { AST_NODE_TYPES } from '@typescript-eslint/experimental-utils'; import rule from '../../src/rules/prefer-function-type'; import { RuleTester } from '../RuleTester'; -var ruleTester = new RuleTester({ +const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 2015, }, diff --git a/packages/eslint-plugin/tests/rules/promise-function-async.test.ts b/packages/eslint-plugin/tests/rules/promise-function-async.test.ts index 4f38105989e3..3910cea56042 100644 --- a/packages/eslint-plugin/tests/rules/promise-function-async.test.ts +++ b/packages/eslint-plugin/tests/rules/promise-function-async.test.ts @@ -2,16 +2,14 @@ import rule from '../../src/rules/promise-function-async'; import { RuleTester, getFixturesRootDir } from '../RuleTester'; const rootDir = getFixturesRootDir(); -const parserOptions = { - ecmaVersion: 2018, - tsconfigRootDir: rootDir, - project: './tsconfig.json', -}; - const messageId = 'missingAsync'; const ruleTester = new RuleTester({ - parserOptions, + parserOptions: { + ecmaVersion: 2018, + tsconfigRootDir: rootDir, + project: './tsconfig.json', + }, parser: '@typescript-eslint/parser', }); diff --git a/packages/eslint-plugin/tests/rules/unified-signatures.test.ts b/packages/eslint-plugin/tests/rules/unified-signatures.test.ts index 94b6520de811..5d65e2f18013 100644 --- a/packages/eslint-plugin/tests/rules/unified-signatures.test.ts +++ b/packages/eslint-plugin/tests/rules/unified-signatures.test.ts @@ -5,7 +5,7 @@ import { RuleTester } from '../RuleTester'; // Tests //------------------------------------------------------------------------------ -var ruleTester = new RuleTester({ parser: '@typescript-eslint/parser' }); +const ruleTester = new RuleTester({ parser: '@typescript-eslint/parser' }); ruleTester.run('unified-signatures', rule, { valid: [ diff --git a/packages/experimental-utils/package.json b/packages/experimental-utils/package.json index 5bafdb42c7db..8d7cb94092fd 100644 --- a/packages/experimental-utils/package.json +++ b/packages/experimental-utils/package.json @@ -34,6 +34,7 @@ "@typescript-eslint/typescript-estree": "1.9.0" }, "peerDependencies": { + "eslint": "*", "typescript": "*" } } diff --git a/packages/experimental-utils/src/ts-eslint/CLIEngine.ts b/packages/experimental-utils/src/ts-eslint/CLIEngine.ts new file mode 100644 index 000000000000..0a64a3d67344 --- /dev/null +++ b/packages/experimental-utils/src/ts-eslint/CLIEngine.ts @@ -0,0 +1,90 @@ +/* eslint-disable @typescript-eslint/no-namespace, no-redeclare */ + +import { CLIEngine as ESLintCLIEngine } from 'eslint'; +import { Linter } from './Linter'; +import { RuleModule, RuleListener } from './Rule'; + +interface CLIEngine { + version: string; + + executeOnFiles(patterns: string[]): CLIEngine.LintReport; + + resolveFileGlobPatterns(patterns: string[]): string[]; + + getConfigForFile(filePath: string): Linter.Config; + + executeOnText(text: string, filename?: string): CLIEngine.LintReport; + + addPlugin(name: string, pluginObject: any): void; + + isPathIgnored(filePath: string): boolean; + + getFormatter(format?: string): CLIEngine.Formatter; + + getRules< + TMessageIds extends string = any, + TOptions extends readonly any[] = any[], + // for extending base rules + TRuleListener extends RuleListener = RuleListener + >(): Map>; +} + +namespace CLIEngine { + export interface Options { + allowInlineConfig?: boolean; + baseConfig?: false | { [name: string]: any }; + cache?: boolean; + cacheFile?: string; + cacheLocation?: string; + configFile?: string; + cwd?: string; + envs?: string[]; + extensions?: string[]; + fix?: boolean; + globals?: string[]; + ignore?: boolean; + ignorePath?: string; + ignorePattern?: string | string[]; + useEslintrc?: boolean; + parser?: string; + parserOptions?: Linter.ParserOptions; + plugins?: string[]; + rules?: { + [name: string]: Linter.RuleLevel | Linter.RuleLevelAndOptions; + }; + rulePaths?: string[]; + reportUnusedDisableDirectives?: boolean; + } + + export interface LintResult { + filePath: string; + messages: Linter.LintMessage[]; + errorCount: number; + warningCount: number; + fixableErrorCount: number; + fixableWarningCount: number; + output?: string; + source?: string; + } + + export interface LintReport { + results: LintResult[]; + errorCount: number; + warningCount: number; + fixableErrorCount: number; + fixableWarningCount: number; + } + + export type Formatter = (results: LintResult[]) => string; +} + +const CLIEngine = ESLintCLIEngine as { + new (options: CLIEngine.Options): CLIEngine; + + // static methods + getErrorResults(results: CLIEngine.LintResult[]): CLIEngine.LintResult[]; + + outputFixes(report: CLIEngine.LintReport): void; +}; + +export { CLIEngine }; diff --git a/packages/experimental-utils/src/ts-eslint/Linter.ts b/packages/experimental-utils/src/ts-eslint/Linter.ts index cff921e048cc..dde85a07f2b4 100644 --- a/packages/experimental-utils/src/ts-eslint/Linter.ts +++ b/packages/experimental-utils/src/ts-eslint/Linter.ts @@ -1,11 +1,13 @@ /* eslint-disable @typescript-eslint/no-namespace, no-redeclare */ import { TSESTree, ParserServices } from '@typescript-eslint/typescript-estree'; +import { Linter as ESLintLinter } from 'eslint'; +import { ParserOptions as TSParserOptions } from './ParserOptions'; import { RuleModule, RuleFix } from './Rule'; import { Scope } from './Scope'; import { SourceCode } from './SourceCode'; -declare class Linter { +interface Linter { version: string; verify( @@ -34,7 +36,10 @@ declare class Linter { defineRule( name: string, - rule: RuleModule, + rule: { + meta?: RuleModule['meta']; + create: RuleModule['create']; + }, ): void; defineRules( @@ -68,18 +73,7 @@ namespace Linter { globals?: { [name: string]: boolean }; } - export interface ParserOptions { - ecmaVersion?: 3 | 5 | 6 | 7 | 8 | 9 | 2015 | 2016 | 2017 | 2018; - sourceType?: 'script' | 'module'; - ecmaFeatures?: { - globalReturn?: boolean; - impliedStrict?: boolean; - jsx?: boolean; - experimentalObjectRestSpread?: boolean; - [key: string]: any; - }; - [key: string]: any; - } + export type ParserOptions = TSParserOptions; export interface LintOptions { filename?: string; @@ -129,4 +123,8 @@ namespace Linter { } } +const Linter = ESLintLinter as { + new (): Linter; +}; + export { Linter }; diff --git a/packages/experimental-utils/src/ts-eslint/ParserOptions.ts b/packages/experimental-utils/src/ts-eslint/ParserOptions.ts index d374ac57b912..915e6726172d 100644 --- a/packages/experimental-utils/src/ts-eslint/ParserOptions.ts +++ b/packages/experimental-utils/src/ts-eslint/ParserOptions.ts @@ -4,7 +4,7 @@ export interface ParserOptions { range?: boolean; tokens?: boolean; sourceType?: 'script' | 'module'; - ecmaVersion?: number; + ecmaVersion?: 3 | 5 | 6 | 7 | 8 | 9 | 2015 | 2016 | 2017 | 2018; ecmaFeatures?: { globalReturn?: boolean; jsx?: boolean; diff --git a/packages/experimental-utils/src/ts-eslint/Rule.ts b/packages/experimental-utils/src/ts-eslint/Rule.ts index 48162df0867f..388f64e99fcd 100644 --- a/packages/experimental-utils/src/ts-eslint/Rule.ts +++ b/packages/experimental-utils/src/ts-eslint/Rule.ts @@ -105,7 +105,7 @@ type ReportFixFunction = ( fixer: RuleFixer, ) => null | RuleFix | RuleFix[] | IterableIterator; -interface ReportDescriptor { +interface ReportDescriptorBase { /** * The parameters for the message string associated with `messageId`. */ @@ -118,6 +118,8 @@ interface ReportDescriptor { * The messageId which is being reported. */ messageId: TMessageIds; +} +interface ReportDescriptorNodeOptionalLoc { /** * The Node or AST Token which the report is being attached to */ @@ -127,10 +129,20 @@ interface ReportDescriptor { */ loc?: TSESTree.SourceLocation | TSESTree.LineAndColumnData; } +interface ReportDescriptorLocOnly { + /** + * An override of the location of the report + */ + loc: TSESTree.SourceLocation | TSESTree.LineAndColumnData; +} +type ReportDescriptor = ReportDescriptorBase< + TMessageIds +> & + (ReportDescriptorNodeOptionalLoc | ReportDescriptorLocOnly); interface RuleContext< TMessageIds extends string, - TOptions extends Readonly + TOptions extends readonly any[] > { /** * The rule ID. @@ -370,7 +382,7 @@ interface RuleListener { interface RuleModule< TMessageIds extends string, - TOptions extends Readonly, + TOptions extends readonly any[], // for extending base rules TRuleListener extends RuleListener = RuleListener > { diff --git a/packages/experimental-utils/src/ts-eslint/RuleTester.ts b/packages/experimental-utils/src/ts-eslint/RuleTester.ts index ea677806cf31..4478abca8dd2 100644 --- a/packages/experimental-utils/src/ts-eslint/RuleTester.ts +++ b/packages/experimental-utils/src/ts-eslint/RuleTester.ts @@ -2,6 +2,7 @@ import { AST_NODE_TYPES, AST_TOKEN_TYPES, } from '@typescript-eslint/typescript-estree'; +import { RuleTester as ESLintRuleTester } from 'eslint'; import { ParserOptions } from './ParserOptions'; import { RuleModule } from './Rule'; @@ -57,16 +58,16 @@ interface RuleTesterConfig { parser: '@typescript-eslint/parser'; parserOptions?: ParserOptions; } -interface RuleTester { - // eslint-disable-next-line @typescript-eslint/no-misused-new - new (config?: RuleTesterConfig): RuleTester; - +declare interface RuleTester { run>( name: string, rule: RuleModule, tests: RunTests, ): void; } +const RuleTester = ESLintRuleTester as { + new (config?: RuleTesterConfig): RuleTester; +}; export { InvalidTestCase, diff --git a/packages/experimental-utils/src/ts-eslint/SourceCode.ts b/packages/experimental-utils/src/ts-eslint/SourceCode.ts index abf3c3e6e8f5..2fb2e0b3cab9 100644 --- a/packages/experimental-utils/src/ts-eslint/SourceCode.ts +++ b/packages/experimental-utils/src/ts-eslint/SourceCode.ts @@ -1,50 +1,10 @@ /* eslint-disable @typescript-eslint/no-namespace, no-redeclare */ import { ParserServices, TSESTree } from '@typescript-eslint/typescript-estree'; +import { SourceCode as ESLintSourceCode } from 'eslint'; import { Scope } from './Scope'; -namespace SourceCode { - export interface Program extends TSESTree.Program { - comments: TSESTree.Comment[]; - tokens: TSESTree.Token[]; - } - - export interface Config { - text: string; - ast: Program; - parserServices?: ParserServices; - scopeManager?: Scope.ScopeManager; - visitorKeys?: VisitorKeys; - } - - export interface VisitorKeys { - [nodeType: string]: string[]; - } - - export type FilterPredicate = ( - tokenOrComment: TSESTree.Token | TSESTree.Comment, - ) => boolean; - - export type CursorWithSkipOptions = - | number - | FilterPredicate - | { - includeComments?: boolean; - filter?: FilterPredicate; - skip?: number; - }; - - export type CursorWithCountOptions = - | number - | FilterPredicate - | { - includeComments?: boolean; - filter?: FilterPredicate; - count?: number; - }; -} - -declare class SourceCode { +declare interface SourceCode { text: string; ast: SourceCode.Program; lines: string[]; @@ -54,11 +14,6 @@ declare class SourceCode { visitorKeys: SourceCode.VisitorKeys; tokensAndComments: (TSESTree.Comment | TSESTree.Token)[]; - constructor(text: string, ast: SourceCode.Program); - constructor(config: SourceCode.Config); - - static splitLines(text: string): string[]; - getText( node?: TSESTree.Node, beforeCount?: number, @@ -190,4 +145,53 @@ declare class SourceCode { getCommentsInside(node: TSESTree.Node): TSESTree.Comment[]; } +namespace SourceCode { + export interface Program extends TSESTree.Program { + comments: TSESTree.Comment[]; + tokens: TSESTree.Token[]; + } + + export interface Config { + text: string; + ast: Program; + parserServices?: ParserServices; + scopeManager?: Scope.ScopeManager; + visitorKeys?: VisitorKeys; + } + + export interface VisitorKeys { + [nodeType: string]: string[]; + } + + export type FilterPredicate = ( + tokenOrComment: TSESTree.Token | TSESTree.Comment, + ) => boolean; + + export type CursorWithSkipOptions = + | number + | FilterPredicate + | { + includeComments?: boolean; + filter?: FilterPredicate; + skip?: number; + }; + + export type CursorWithCountOptions = + | number + | FilterPredicate + | { + includeComments?: boolean; + filter?: FilterPredicate; + count?: number; + }; +} + +const SourceCode = ESLintSourceCode as { + new (text: string, ast: SourceCode.Program): SourceCode; + new (config: SourceCode.Config): SourceCode; + + // static methods + splitLines(text: string): string[]; +}; + export { SourceCode }; diff --git a/packages/experimental-utils/tsconfig.build.json b/packages/experimental-utils/tsconfig.build.json index 0ce1565b0d05..c052e5211304 100644 --- a/packages/experimental-utils/tsconfig.build.json +++ b/packages/experimental-utils/tsconfig.build.json @@ -5,5 +5,5 @@ "rootDir": "./src", "resolveJsonModule": true }, - "include": ["src"] + "include": ["src", "typings"] } diff --git a/packages/experimental-utils/typings/eslint.d.ts b/packages/experimental-utils/typings/eslint.d.ts new file mode 100644 index 000000000000..a32b469a977a --- /dev/null +++ b/packages/experimental-utils/typings/eslint.d.ts @@ -0,0 +1,15 @@ +/* +We intentionally do not include @types/eslint. + +This is to ensure that nobody accidentally uses those incorrect types +instead of the ones declared within this package +*/ + +declare module 'eslint' { + const Linter: unknown; + const RuleTester: unknown; + const SourceCode: unknown; + const CLIEngine: unknown; + + export { Linter, RuleTester, SourceCode, CLIEngine }; +} diff --git a/packages/parser/src/parser-options.ts b/packages/parser/src/parser-options.ts index d374ac57b912..9848d54ba709 100644 --- a/packages/parser/src/parser-options.ts +++ b/packages/parser/src/parser-options.ts @@ -1,21 +1,3 @@ -export interface ParserOptions { - loc?: boolean; - comment?: boolean; - range?: boolean; - tokens?: boolean; - sourceType?: 'script' | 'module'; - ecmaVersion?: number; - ecmaFeatures?: { - globalReturn?: boolean; - jsx?: boolean; - }; - // ts-estree specific - filePath?: string; - project?: string | string[]; - useJSXTextNode?: boolean; - errorOnUnknownASTType?: boolean; - errorOnTypeScriptSyntacticAndSemanticIssues?: boolean; - tsconfigRootDir?: string; - extraFileExtensions?: string[]; - warnOnUnsupportedTypeScriptVersion?: boolean; -} +import { TSESLint } from '@typescript-eslint/experimental-utils'; + +export type ParserOptions = TSESLint.ParserOptions; diff --git a/packages/parser/tests/lib/basics.ts b/packages/parser/tests/lib/basics.ts index 042e3fd731c6..4b237a7dc2a3 100644 --- a/packages/parser/tests/lib/basics.ts +++ b/packages/parser/tests/lib/basics.ts @@ -1,4 +1,4 @@ -import { Linter } from 'eslint'; +import { TSESLint } from '@typescript-eslint/experimental-utils'; import fs from 'fs'; import glob from 'glob'; import * as parser from '../../src/parser'; @@ -24,11 +24,11 @@ describe('basics', () => { }); it('https://github.com/eslint/typescript-eslint-parser/issues/476', () => { - const linter = new Linter(); + const linter = new TSESLint.Linter(); const code = ` export const Price: React.SFC = function Price(props) {} `; - const config: Linter.Config = { + const config: TSESLint.Linter.Config = { parser: '@typescript-eslint/parser', rules: { test: 'error', @@ -37,15 +37,15 @@ export const Price: React.SFC = function Price(props) {} linter.defineParser('@typescript-eslint/parser', parser); linter.defineRule('test', { - create(context: any) { + create(context) { return { - TSTypeReference(node: any) { + TSTypeReference(node) { const name = context.getSourceCode().getText(node.typeName); context.report({ node, message: 'called on {{name}}', data: { name }, - }); + } as any); }, }; }, diff --git a/packages/parser/tests/lib/parser.ts b/packages/parser/tests/lib/parser.ts index c3c205509dd9..9545633cd6e3 100644 --- a/packages/parser/tests/lib/parser.ts +++ b/packages/parser/tests/lib/parser.ts @@ -1,3 +1,4 @@ +import { TSESLint } from '@typescript-eslint/experimental-utils'; import * as typescriptESTree from '@typescript-eslint/typescript-estree'; import { parse, parseForESLint, Syntax } from '../../src/parser'; import * as scope from '../../src/analyze-scope'; @@ -35,13 +36,13 @@ describe('parser', () => { it('parseAndGenerateServices() should be called with options', () => { const code = 'const valid = true;'; const spy = jest.spyOn(typescriptESTree, 'parseAndGenerateServices'); - const config = { + const config: TSESLint.ParserOptions = { loc: false, comment: false, range: false, tokens: false, sourceType: 'module' as 'module', - ecmaVersion: 10, + ecmaVersion: 2018, ecmaFeatures: { globalReturn: false, jsx: false, diff --git a/packages/parser/tests/lib/tsx.ts b/packages/parser/tests/lib/tsx.ts index eed70b17e8d0..21937886b5dc 100644 --- a/packages/parser/tests/lib/tsx.ts +++ b/packages/parser/tests/lib/tsx.ts @@ -1,4 +1,4 @@ -import { Linter } from 'eslint'; +import { TSESLint } from '@typescript-eslint/experimental-utils'; import fs from 'fs'; import glob from 'glob'; import * as parser from '../../src/parser'; @@ -31,7 +31,7 @@ describe('TSX', () => { }); describe("if the filename ends with '.tsx', enable jsx option automatically.", () => { - const linter = new Linter(); + const linter = new TSESLint.Linter(); linter.defineParser('@typescript-eslint/parser', parser); it('filePath was not provided', () => { diff --git a/packages/typescript-estree/src/convert.ts b/packages/typescript-estree/src/convert.ts index e0e535c774aa..f8151e1b268c 100644 --- a/packages/typescript-estree/src/convert.ts +++ b/packages/typescript-estree/src/convert.ts @@ -102,7 +102,7 @@ export class Converter { this.allowPattern = allowPattern; } - let result = this.convertNode(node as TSNode, parent || node.parent); + const result = this.convertNode(node as TSNode, parent || node.parent); this.registerTSNodeInNodeMap(node, result); @@ -1390,7 +1390,7 @@ export class Converter { case SyntaxKind.ClassDeclaration: case SyntaxKind.ClassExpression: { const heritageClauses = node.heritageClauses || []; - let classNodeType = + const classNodeType = node.kind === SyntaxKind.ClassDeclaration ? AST_NODE_TYPES.ClassDeclaration : AST_NODE_TYPES.ClassExpression; diff --git a/packages/typescript-estree/src/tsconfig-parser.ts b/packages/typescript-estree/src/tsconfig-parser.ts index 44e1f13b28bd..641af07a77a4 100644 --- a/packages/typescript-estree/src/tsconfig-parser.ts +++ b/packages/typescript-estree/src/tsconfig-parser.ts @@ -82,7 +82,7 @@ export function calculateProjectParserOptions( watchCallback(filePath, ts.FileWatcherEventKind.Changed); } - for (let rawTsconfigPath of extra.projects) { + for (const rawTsconfigPath of extra.projects) { const tsconfigPath = getTsconfigPath(rawTsconfigPath, extra); const existingWatch = knownWatchProgramMap.get(tsconfigPath); diff --git a/yarn.lock b/yarn.lock index b296e145ad2c..3f3ce5f61a9c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1187,19 +1187,6 @@ resolved "https://registry.yarnpkg.com/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#1ee30d79544ca84d68d4b3cdb0af4f205663dd2d" integrity sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag== -"@types/eslint@^4.16.3": - version "4.16.6" - resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-4.16.6.tgz#96d4ecddbea618ab0b55eaf0dffedf387129b06c" - integrity sha512-GL7tGJig55FeclpOytU7nCCqtR143jBoC7AUdH0DO9xBSIFiNNUFCY/S3KNWsHeQJuU3hjw/OC1+kRTFNXqUZQ== - dependencies: - "@types/estree" "*" - "@types/json-schema" "*" - -"@types/estree@*": - version "0.0.39" - resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.39.tgz#e177e699ee1b8c22d23174caaa7422644389509f" - integrity sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw== - "@types/events@*": version "3.0.0" resolved "https://registry.yarnpkg.com/@types/events/-/events-3.0.0.tgz#2862f3f58a9a7f7c3e78d79f130dd4d71c25c2a7" @@ -1231,7 +1218,7 @@ dependencies: "@types/jest-diff" "*" -"@types/json-schema@*": +"@types/json-schema@^7.0.3": version "7.0.3" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.3.tgz#bdfd69d61e464dcc81b25159c270d75a73c1a636" integrity sha512-Il2DtDVRGDcqjDtE+rF8iqg1CArehSK84HZJCT7AMITlyXRBpuPhqGLDQMowraqqu1coEaimg4ZOqggt6L6L+A== From 1012935bdfea3af5f8894778acbeaaf1c58e64c0 Mon Sep 17 00:00:00 2001 From: Brad Zacher Date: Mon, 13 May 2019 23:31:11 -0700 Subject: [PATCH 2/4] feat(experimental-utils): Add types for eslint-scope --- packages/eslint-plugin/tsconfig.json | 9 +- packages/experimental-utils/package.json | 4 +- packages/experimental-utils/src/index.ts | 3 +- .../src/ts-eslint-scope/Definition.ts | 39 ++ .../src/ts-eslint-scope/Options.ts | 21 + .../src/ts-eslint-scope/PatternVisitor.ts | 38 ++ .../src/ts-eslint-scope/Reference.ts | 27 + .../src/ts-eslint-scope/Referencer.ts | 80 +++ .../src/ts-eslint-scope/Scope.ts | 193 +++++++ .../src/ts-eslint-scope/ScopeManager.ts | 57 ++ .../src/ts-eslint-scope/Variable.ts | 18 + .../src/ts-eslint-scope/analyze.ts | 19 + .../src/ts-eslint-scope/index.ts | 12 + .../typings/eslint-scope.d.ts | 63 +++ packages/parser/package.json | 3 +- packages/parser/src/analyze-scope.ts | 85 ++- packages/parser/src/parser.ts | 4 +- packages/parser/src/scope/scope-manager.ts | 14 +- packages/parser/src/scope/scopes.ts | 11 +- packages/parser/typings/eslint-scope.d.ts | 494 ------------------ packages/parser/typings/eslint.d.ts | 14 + 21 files changed, 662 insertions(+), 546 deletions(-) create mode 100644 packages/experimental-utils/src/ts-eslint-scope/Definition.ts create mode 100644 packages/experimental-utils/src/ts-eslint-scope/Options.ts create mode 100644 packages/experimental-utils/src/ts-eslint-scope/PatternVisitor.ts create mode 100644 packages/experimental-utils/src/ts-eslint-scope/Reference.ts create mode 100644 packages/experimental-utils/src/ts-eslint-scope/Referencer.ts create mode 100644 packages/experimental-utils/src/ts-eslint-scope/Scope.ts create mode 100644 packages/experimental-utils/src/ts-eslint-scope/ScopeManager.ts create mode 100644 packages/experimental-utils/src/ts-eslint-scope/Variable.ts create mode 100644 packages/experimental-utils/src/ts-eslint-scope/analyze.ts create mode 100644 packages/experimental-utils/src/ts-eslint-scope/index.ts create mode 100644 packages/experimental-utils/typings/eslint-scope.d.ts delete mode 100644 packages/parser/typings/eslint-scope.d.ts create mode 100644 packages/parser/typings/eslint.d.ts diff --git a/packages/eslint-plugin/tsconfig.json b/packages/eslint-plugin/tsconfig.json index fc93e91c26d3..fb7e21da237d 100644 --- a/packages/eslint-plugin/tsconfig.json +++ b/packages/eslint-plugin/tsconfig.json @@ -1,11 +1,4 @@ { "extends": "./tsconfig.build.json", - "include": [ - "src", - "typings", - // include the parser's ambient typings because the parser exports them in its type defs - "../parser/typings", - "tests", - "tools" - ] + "include": ["src", "typings", "tests", "tools"] } diff --git a/packages/experimental-utils/package.json b/packages/experimental-utils/package.json index 8d7cb94092fd..9d51a32c4fd1 100644 --- a/packages/experimental-utils/package.json +++ b/packages/experimental-utils/package.json @@ -28,10 +28,12 @@ "prebuild": "npm run clean", "build": "tsc -p tsconfig.build.json", "clean": "rimraf dist/", + "format": "prettier --write \"./**/*.{ts,js,json,md}\" --ignore-path ../../.prettierignore", "typecheck": "tsc --noEmit" }, "dependencies": { - "@typescript-eslint/typescript-estree": "1.9.0" + "@typescript-eslint/typescript-estree": "1.9.0", + "eslint-scope": "^4.0.0" }, "peerDependencies": { "eslint": "*", diff --git a/packages/experimental-utils/src/index.ts b/packages/experimental-utils/src/index.ts index 8b3a7f039ff3..93b3831817c7 100644 --- a/packages/experimental-utils/src/index.ts +++ b/packages/experimental-utils/src/index.ts @@ -1,7 +1,8 @@ import * as ESLintUtils from './eslint-utils'; import * as TSESLint from './ts-eslint'; +import * as TSESLintScope from './ts-eslint-scope'; -export { ESLintUtils, TSESLint }; +export { ESLintUtils, TSESLint, TSESLintScope }; // for convenience's sake - export the types directly from here so consumers // don't need to reference/install both packages in their code diff --git a/packages/experimental-utils/src/ts-eslint-scope/Definition.ts b/packages/experimental-utils/src/ts-eslint-scope/Definition.ts new file mode 100644 index 000000000000..b2f4b91383e5 --- /dev/null +++ b/packages/experimental-utils/src/ts-eslint-scope/Definition.ts @@ -0,0 +1,39 @@ +import { TSESTree } from '@typescript-eslint/typescript-estree'; +import { + Definition as ESLintDefinition, + ParameterDefinition as ESLintParameterDefinition, +} from 'eslint-scope/lib/definition'; + +interface Definition { + type: string; + name: TSESTree.BindingName; + node: TSESTree.Node; + parent?: TSESTree.Node | null; + index?: number | null; + kind?: string | null; + rest?: boolean; +} +interface DefinitionConstructor { + new ( + type: string, + name: TSESTree.BindingName | TSESTree.PropertyName, + node: TSESTree.Node, + parent?: TSESTree.Node | null, + index?: number | null, + kind?: string | null, + ): Definition; +} +const Definition = ESLintDefinition as DefinitionConstructor; + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +interface ParameterDefinition extends Definition {} +const ParameterDefinition = ESLintParameterDefinition as DefinitionConstructor & { + new ( + name: TSESTree.Node, + node: TSESTree.Node, + index?: number | null, + rest?: boolean, + ): ParameterDefinition; +}; + +export { Definition, ParameterDefinition }; diff --git a/packages/experimental-utils/src/ts-eslint-scope/Options.ts b/packages/experimental-utils/src/ts-eslint-scope/Options.ts new file mode 100644 index 000000000000..f06fe4e42e8d --- /dev/null +++ b/packages/experimental-utils/src/ts-eslint-scope/Options.ts @@ -0,0 +1,21 @@ +import { TSESTree } from '@typescript-eslint/typescript-estree'; + +type PatternVisitorCallback = ( + pattern: TSESTree.Identifier, + info: { + rest: boolean; + topLevel: boolean; + assignments: TSESTree.AssignmentPattern[]; + }, +) => void; + +interface PatternVisitorOptions { + processRightHandNodes?: boolean; +} + +interface Visitor { + visitChildren(node?: T): void; + visit(node?: T): void; +} + +export { PatternVisitorCallback, PatternVisitorOptions, Visitor }; diff --git a/packages/experimental-utils/src/ts-eslint-scope/PatternVisitor.ts b/packages/experimental-utils/src/ts-eslint-scope/PatternVisitor.ts new file mode 100644 index 000000000000..a31645b12285 --- /dev/null +++ b/packages/experimental-utils/src/ts-eslint-scope/PatternVisitor.ts @@ -0,0 +1,38 @@ +import { TSESTree } from '@typescript-eslint/typescript-estree'; +import ESLintPatternVisitor from 'eslint-scope/lib/pattern-visitor'; +import { ScopeManager } from './ScopeManager'; +import { + PatternVisitorCallback, + PatternVisitorOptions, + Visitor, +} from './Options'; + +interface PatternVisitor extends Visitor { + options: any; + scopeManager: ScopeManager; + parent?: TSESTree.Node; + rightHandNodes: TSESTree.Node[]; + + Identifier(pattern: TSESTree.Node): void; + Property(property: TSESTree.Node): void; + ArrayPattern(pattern: TSESTree.Node): void; + AssignmentPattern(pattern: TSESTree.Node): void; + RestElement(pattern: TSESTree.Node): void; + MemberExpression(node: TSESTree.Node): void; + SpreadElement(node: TSESTree.Node): void; + ArrayExpression(node: TSESTree.Node): void; + AssignmentExpression(node: TSESTree.Node): void; + CallExpression(node: TSESTree.Node): void; +} +const PatternVisitor = ESLintPatternVisitor as { + new ( + options: PatternVisitorOptions, + rootPattern: any, + callback: PatternVisitorCallback, + ): PatternVisitor; + + // static methods + isPattern(node: TSESTree.Node): boolean; +}; + +export { PatternVisitor }; diff --git a/packages/experimental-utils/src/ts-eslint-scope/Reference.ts b/packages/experimental-utils/src/ts-eslint-scope/Reference.ts new file mode 100644 index 000000000000..15afc7dcdc14 --- /dev/null +++ b/packages/experimental-utils/src/ts-eslint-scope/Reference.ts @@ -0,0 +1,27 @@ +import { TSESTree } from '@typescript-eslint/typescript-estree'; +import ESLintReference from 'eslint-scope/lib/reference'; +import { Scope } from './Scope'; +import { Variable } from './Variable'; + +interface Reference { + identifier: TSESTree.Identifier; + from: Scope; + resolved: Variable | null; + writeExpr: TSESTree.Node | null; + init: boolean; + + isWrite(): boolean; + isRead(): boolean; + isWriteOnly(): boolean; + isReadOnly(): boolean; + isReadWrite(): boolean; +} +const Reference = ESLintReference as { + new (): Reference; + + READ: 0x1; + WRITE: 0x2; + RW: 0x3; +}; + +export { Reference }; diff --git a/packages/experimental-utils/src/ts-eslint-scope/Referencer.ts b/packages/experimental-utils/src/ts-eslint-scope/Referencer.ts new file mode 100644 index 000000000000..b430047c01ec --- /dev/null +++ b/packages/experimental-utils/src/ts-eslint-scope/Referencer.ts @@ -0,0 +1,80 @@ +import { TSESTree } from '@typescript-eslint/typescript-estree'; +import ESLintReferencer from 'eslint-scope/lib/referencer'; +import { + PatternVisitorCallback, + PatternVisitorOptions, + Visitor, +} from './Options'; +import { Scope } from './Scope'; +import { ScopeManager } from './ScopeManager'; + +interface Referencer extends Visitor { + isInnerMethodDefinition: boolean; + options: any; + scopeManager: SM; + parent?: TSESTree.Node; + + currentScope(): Scope; + close(node: TSESTree.Node): void; + pushInnerMethodDefinition(isInnerMethodDefinition: boolean): boolean; + popInnerMethodDefinition(isInnerMethodDefinition: boolean): void; + + referencingDefaultValue( + pattern: any, + assignments: any, + maybeImplicitGlobal: any, + init: boolean, + ): void; + visitPattern( + node: TSESTree.Node, + options: PatternVisitorOptions, + callback: PatternVisitorCallback, + ): void; + visitFunction(node: TSESTree.Node): void; + visitClass(node: TSESTree.Node): void; + visitProperty(node: TSESTree.Node): void; + visitForIn(node: TSESTree.Node): void; + visitVariableDeclaration( + variableTargetScope: any, + type: any, + node: TSESTree.Node, + index: any, + ): void; + + AssignmentExpression(node: TSESTree.Node): void; + CatchClause(node: TSESTree.Node): void; + Program(node: TSESTree.Node): void; + Identifier(node: TSESTree.Node): void; + UpdateExpression(node: TSESTree.Node): void; + MemberExpression(node: TSESTree.Node): void; + Property(node: TSESTree.Node): void; + MethodDefinition(node: TSESTree.Node): void; + BreakStatement(): void; + ContinueStatement(): void; + LabeledStatement(node: TSESTree.Node): void; + ForStatement(node: TSESTree.Node): void; + ClassExpression(node: TSESTree.Node): void; + ClassDeclaration(node: TSESTree.Node): void; + CallExpression(node: TSESTree.Node): void; + BlockStatement(node: TSESTree.Node): void; + ThisExpression(): void; + WithStatement(node: TSESTree.Node): void; + VariableDeclaration(node: TSESTree.Node): void; + SwitchStatement(node: TSESTree.Node): void; + FunctionDeclaration(node: TSESTree.Node): void; + FunctionExpression(node: TSESTree.Node): void; + ForOfStatement(node: TSESTree.Node): void; + ForInStatement(node: TSESTree.Node): void; + ArrowFunctionExpression(node: TSESTree.Node): void; + ImportDeclaration(node: TSESTree.Node): void; + visitExportDeclaration(node: TSESTree.Node): void; + ExportDeclaration(node: TSESTree.Node): void; + ExportNamedDeclaration(node: TSESTree.Node): void; + ExportSpecifier(node: TSESTree.Node): void; + MetaProperty(): void; +} +const Referencer = ESLintReferencer as { + new (options: any, scopeManager: SM): Referencer; +}; + +export { Referencer }; diff --git a/packages/experimental-utils/src/ts-eslint-scope/Scope.ts b/packages/experimental-utils/src/ts-eslint-scope/Scope.ts new file mode 100644 index 000000000000..71b8dbf42a4c --- /dev/null +++ b/packages/experimental-utils/src/ts-eslint-scope/Scope.ts @@ -0,0 +1,193 @@ +/* eslint-disable @typescript-eslint/no-empty-interface */ + +import { TSESTree } from '@typescript-eslint/typescript-estree'; +import { + Scope as ESLintScope, + GlobalScope as ESLintGlobalScope, + ModuleScope as ESLintModuleScope, + FunctionExpressionNameScope as ESLintFunctionExpressionNameScope, + CatchScope as ESLintCatchScope, + WithScope as ESLintWithScope, + BlockScope as ESLintBlockScope, + SwitchScope as ESLintSwitchScope, + FunctionScope as ESLintFunctionScope, + ForScope as ESLintForScope, + ClassScope as ESLintClassScope, +} from 'eslint-scope/lib/scope'; +import { Definition } from './Definition'; +import { Reference } from './Reference'; +import { ScopeManager } from './ScopeManager'; +import { Variable } from './Variable'; + +type ScopeType = + | 'block' + | 'catch' + | 'class' + | 'for' + | 'function' + | 'function-expression-name' + | 'global' + | 'module' + | 'switch' + | 'with' + | 'TDZ' + | 'enum' + | 'empty-function'; + +interface Scope { + type: ScopeType; + isStrict: boolean; + upper: Scope | null; + childScopes: Scope[]; + variableScope: Scope; + block: TSESTree.Node; + variables: Variable[]; + set: Map; + references: Reference[]; + through: Reference[]; + thisFound?: boolean; + functionExpressionScope: boolean; + + __shouldStaticallyClose(scopeManager: ScopeManager): boolean; + __shouldStaticallyCloseForGlobal(ref: any): boolean; + __staticCloseRef(ref: any): void; + __dynamicCloseRef(ref: any): void; + __globalCloseRef(ref: any): void; + __close(scopeManager: ScopeManager): Scope; + __isValidResolution(ref: any, variable: any): boolean; + __resolve(ref: any): boolean; + __delegateToUpperScope(ref: any): void; + __addDeclaredVariablesOfNode(variable: any, node: TSESTree.Node): void; + __defineGeneric( + name: any, + set: any, + variables: any, + node: any, + def: Definition, + ): void; + + __define(node: TSESTree.Node, def: Definition): void; + + __referencing( + node: TSESTree.Node, + assign: number, + writeExpr: TSESTree.Node, + maybeImplicitGlobal: any, + partial: any, + init: any, + ): void; + + __detectEval(): void; + __detectThis(): void; + __isClosed(): boolean; + /** + * returns resolved {Reference} + * @method Scope#resolve + * @param {Espree.Identifier} ident - identifier to be resolved. + * @returns {Reference} reference + */ + resolve(ident: TSESTree.Node): Reference; + + /** + * returns this scope is static + * @method Scope#isStatic + * @returns {boolean} static + */ + isStatic(): boolean; + + /** + * returns this scope has materialized arguments + * @method Scope#isArgumentsMaterialized + * @returns {boolean} arguemnts materialized + */ + isArgumentsMaterialized(): boolean; + + /** + * returns this scope has materialized `this` reference + * @method Scope#isThisMaterialized + * @returns {boolean} this materialized + */ + isThisMaterialized(): boolean; + + isUsedName(name: any): boolean; +} +interface ScopeConstructor { + new ( + scopeManager: ScopeManager, + type: ScopeType, + upperScope: Scope | null, + block: TSESTree.Node | null, + isMethodDefinition: boolean, + ): Scope; +} +const Scope = ESLintScope as ScopeConstructor; + +interface ScopeChildConstructorWithUpperScope { + new ( + scopeManager: ScopeManager, + upperScope: Scope, + block: TSESTree.Node | null, + ): T; +} + +interface GlobalScope extends Scope {} +const GlobalScope = ESLintGlobalScope as ScopeConstructor & { + new (scopeManager: ScopeManager, block: TSESTree.Node | null): GlobalScope; +}; + +interface ModuleScope extends Scope {} +const ModuleScope = ESLintModuleScope as ScopeConstructor & + ScopeChildConstructorWithUpperScope; + +interface FunctionExpressionNameScope extends Scope {} +const FunctionExpressionNameScope = ESLintFunctionExpressionNameScope as ScopeConstructor & + ScopeChildConstructorWithUpperScope; + +interface CatchScope extends Scope {} +const CatchScope = ESLintCatchScope as ScopeConstructor & + ScopeChildConstructorWithUpperScope; + +interface WithScope extends Scope {} +const WithScope = ESLintWithScope as ScopeConstructor & + ScopeChildConstructorWithUpperScope; + +interface BlockScope extends Scope {} +const BlockScope = ESLintBlockScope as ScopeConstructor & + ScopeChildConstructorWithUpperScope; + +interface SwitchScope extends Scope {} +const SwitchScope = ESLintSwitchScope as ScopeConstructor & + ScopeChildConstructorWithUpperScope; + +interface FunctionScope extends Scope {} +const FunctionScope = ESLintFunctionScope as ScopeConstructor & { + new ( + scopeManager: ScopeManager, + upperScope: Scope, + block: TSESTree.Node | null, + isMethodDefinition: boolean, + ): FunctionScope; +}; + +interface ForScope extends Scope {} +const ForScope = ESLintForScope as ScopeConstructor & + ScopeChildConstructorWithUpperScope; + +interface ClassScope extends Scope {} +const ClassScope = ESLintClassScope as ScopeConstructor & + ScopeChildConstructorWithUpperScope; + +export { + ScopeType, + Scope, + GlobalScope, + ModuleScope, + FunctionExpressionNameScope, + CatchScope, + WithScope, + BlockScope, + SwitchScope, + FunctionScope, + ForScope, + ClassScope, +}; diff --git a/packages/experimental-utils/src/ts-eslint-scope/ScopeManager.ts b/packages/experimental-utils/src/ts-eslint-scope/ScopeManager.ts new file mode 100644 index 000000000000..e90c3cf4b11f --- /dev/null +++ b/packages/experimental-utils/src/ts-eslint-scope/ScopeManager.ts @@ -0,0 +1,57 @@ +import { TSESTree } from '@typescript-eslint/typescript-estree'; +import ESLintScopeManager from 'eslint-scope/lib/scope-manager'; +import { Scope } from './Scope'; +import { Variable } from './Variable'; + +interface ScopeManagerOptions { + directive?: boolean; + optimistic?: boolean; + ignoreEval?: boolean; + nodejsScope?: boolean; + sourceType?: 'module' | 'script'; + impliedStrict?: boolean; + ecmaVersion?: number; +} + +interface ScopeManager { + __options: ScopeManagerOptions; + __currentScope: Scope; + scopes: Scope[]; + globalScope: Scope; + + __useDirective(): boolean; + __isOptimistic(): boolean; + __ignoreEval(): boolean; + __isNodejsScope(): boolean; + isModule(): boolean; + isImpliedStrict(): boolean; + isStrictModeSupported(): boolean; + + // Returns appropriate scope for this node. + __get(node: TSESTree.Node): Scope; + getDeclaredVariables(node: TSESTree.Node): Variable[]; + acquire(node: TSESTree.Node, inner?: boolean): Scope | null; + acquireAll(node: TSESTree.Node): Scope | null; + release(node: TSESTree.Node, inner?: boolean): Scope | null; + attach(): void; + detach(): void; + + __nestScope(scope: Scope): Scope; + __nestGlobalScope(node: TSESTree.Node): Scope; + __nestBlockScope(node: TSESTree.Node): Scope; + __nestFunctionScope(node: TSESTree.Node, isMethodDefinition: boolean): Scope; + __nestForScope(node: TSESTree.Node): Scope; + __nestCatchScope(node: TSESTree.Node): Scope; + __nestWithScope(node: TSESTree.Node): Scope; + __nestClassScope(node: TSESTree.Node): Scope; + __nestSwitchScope(node: TSESTree.Node): Scope; + __nestModuleScope(node: TSESTree.Node): Scope; + __nestFunctionExpressionNameScope(node: TSESTree.Node): Scope; + + __isES6(): boolean; +} +const ScopeManager = ESLintScopeManager as { + new (options: ScopeManagerOptions): ScopeManager; +}; + +export { ScopeManager, ScopeManagerOptions }; diff --git a/packages/experimental-utils/src/ts-eslint-scope/Variable.ts b/packages/experimental-utils/src/ts-eslint-scope/Variable.ts new file mode 100644 index 000000000000..306d5bfca498 --- /dev/null +++ b/packages/experimental-utils/src/ts-eslint-scope/Variable.ts @@ -0,0 +1,18 @@ +import { TSESTree } from '@typescript-eslint/typescript-estree'; +import ESLintVariable from 'eslint-scope/lib/variable'; +import { Reference } from './Reference'; +import { Definition } from './Definition'; + +interface Variable { + name: string; + identifiers: TSESTree.Identifier[]; + references: Reference[]; + defs: Definition[]; + eslintUsed?: boolean; +} + +const Variable = ESLintVariable as { + new (): Variable; +}; + +export { Variable }; diff --git a/packages/experimental-utils/src/ts-eslint-scope/analyze.ts b/packages/experimental-utils/src/ts-eslint-scope/analyze.ts new file mode 100644 index 000000000000..c4ab4514c8cd --- /dev/null +++ b/packages/experimental-utils/src/ts-eslint-scope/analyze.ts @@ -0,0 +1,19 @@ +import { analyze as ESLintAnalyze } from 'eslint-scope'; +import { ScopeManager } from './ScopeManager'; + +interface AnalysisOptions { + optimistic?: boolean; + directive?: boolean; + ignoreEval?: boolean; + nodejsScope?: boolean; + impliedStrict?: boolean; + fallback?: string | ((node: {}) => string[]); + sourceType?: 'script' | 'module'; + ecmaVersion?: number; +} +const analyze = ESLintAnalyze as ( + ast: {}, + options?: AnalysisOptions, +) => ScopeManager; + +export { analyze, AnalysisOptions }; diff --git a/packages/experimental-utils/src/ts-eslint-scope/index.ts b/packages/experimental-utils/src/ts-eslint-scope/index.ts new file mode 100644 index 000000000000..d713845f9f46 --- /dev/null +++ b/packages/experimental-utils/src/ts-eslint-scope/index.ts @@ -0,0 +1,12 @@ +import { version as ESLintVersion } from 'eslint-scope'; + +export * from './analyze'; +export * from './Definition'; +export * from './Options'; +export * from './PatternVisitor'; +export * from './Reference'; +export * from './Referencer'; +export * from './Scope'; +export * from './ScopeManager'; +export * from './Variable'; +export const version: string = ESLintVersion; diff --git a/packages/experimental-utils/typings/eslint-scope.d.ts b/packages/experimental-utils/typings/eslint-scope.d.ts new file mode 100644 index 000000000000..7b4d0bc1b2e9 --- /dev/null +++ b/packages/experimental-utils/typings/eslint-scope.d.ts @@ -0,0 +1,63 @@ +/* +We intentionally do not include @types/eslint-scope. + +This is to ensure that nobody accidentally uses those incorrect types +instead of the ones declared within this package +*/ + +declare module 'eslint-scope/lib/variable' { + const Variable: unknown; + export = Variable; +} +declare module 'eslint-scope/lib/definition' { + const Definition: unknown; + const ParameterDefinition: unknown; + export { Definition, ParameterDefinition }; +} +declare module 'eslint-scope/lib/pattern-visitor' { + const PatternVisitor: unknown; + export = PatternVisitor; +} +declare module 'eslint-scope/lib/referencer' { + const Referencer: unknown; + export = Referencer; +} +declare module 'eslint-scope/lib/scope' { + const Scope: unknown; + const GlobalScope: unknown; + const ModuleScope: unknown; + const FunctionExpressionNameScope: unknown; + const CatchScope: unknown; + const WithScope: unknown; + const BlockScope: unknown; + const SwitchScope: unknown; + const FunctionScope: unknown; + const ForScope: unknown; + const ClassScope: unknown; + export { + Scope, + GlobalScope, + ModuleScope, + FunctionExpressionNameScope, + CatchScope, + WithScope, + BlockScope, + SwitchScope, + FunctionScope, + ForScope, + ClassScope, + }; +} +declare module 'eslint-scope/lib/reference' { + const Reference: unknown; + export = Reference; +} +declare module 'eslint-scope/lib/scope-manager' { + const ScopeManager: unknown; + export = ScopeManager; +} +declare module 'eslint-scope' { + const version: string; + const analyze: unknown; + export { analyze, version }; +} diff --git a/packages/parser/package.json b/packages/parser/package.json index 0389785b401f..d33fa890f503 100644 --- a/packages/parser/package.json +++ b/packages/parser/package.json @@ -37,13 +37,12 @@ "eslint": "^5.0.0" }, "dependencies": { + "@types/eslint-visitor-keys": "^1.0.0", "@typescript-eslint/experimental-utils": "1.9.0", "@typescript-eslint/typescript-estree": "1.9.0", - "eslint-scope": "^4.0.0", "eslint-visitor-keys": "^1.0.0" }, "devDependencies": { - "@types/eslint-visitor-keys": "^1.0.0", "@typescript-eslint/shared-fixtures": "1.9.0" } } diff --git a/packages/parser/src/analyze-scope.ts b/packages/parser/src/analyze-scope.ts index b131104ce87b..165d560583f2 100644 --- a/packages/parser/src/analyze-scope.ts +++ b/packages/parser/src/analyze-scope.ts @@ -1,12 +1,8 @@ -import { TSESTree, AST_NODE_TYPES } from '@typescript-eslint/typescript-estree'; -import { Definition, ParameterDefinition } from 'eslint-scope/lib/definition'; import { - PatternVisitorCallback, - PatternVisitorOptions, -} from 'eslint-scope/lib/options'; -import OriginalPatternVisitor from 'eslint-scope/lib/pattern-visitor'; -import Reference from 'eslint-scope/lib/reference'; -import OriginalReferencer from 'eslint-scope/lib/referencer'; + TSESTree, + TSESLintScope, + AST_NODE_TYPES, +} from '@typescript-eslint/experimental-utils'; import { getKeys as fallback } from 'eslint-visitor-keys'; import { ParserOptions } from './parser-options'; @@ -30,11 +26,11 @@ function overrideDefine(define: any) { }; } -class PatternVisitor extends OriginalPatternVisitor { +class PatternVisitor extends TSESLintScope.PatternVisitor { constructor( - options: PatternVisitorOptions, + options: TSESLintScope.PatternVisitorOptions, rootPattern: any, - callback: PatternVisitorCallback, + callback: TSESLintScope.PatternVisitorCallback, ) { super(options, rootPattern, callback); } @@ -87,7 +83,7 @@ class PatternVisitor extends OriginalPatternVisitor { } } -class Referencer extends OriginalReferencer { +class Referencer extends TSESLintScope.Referencer { protected typeMode: boolean; constructor(options: any, scopeManager: ScopeManager) { @@ -103,8 +99,8 @@ class Referencer extends OriginalReferencer { */ visitPattern( node: T, - options: PatternVisitorOptions, - callback: PatternVisitorCallback, + options: TSESLintScope.PatternVisitorOptions, + callback: TSESLintScope.PatternVisitorCallback, ): void { if (!node) { return; @@ -143,7 +139,14 @@ class Referencer extends OriginalReferencer { if (type === 'FunctionDeclaration' && id) { upperScope.__define( id, - new Definition('FunctionName', id, node, null, null, null), + new TSESLintScope.Definition( + 'FunctionName', + id, + node, + null, + null, + null, + ), ); // Remove overload definition to avoid confusion of no-redeclare rule. @@ -183,7 +186,12 @@ class Referencer extends OriginalReferencer { ) { innerScope.__define( pattern, - new ParameterDefinition(pattern, node, i, info.rest), + new TSESLintScope.ParameterDefinition( + pattern, + node, + i, + info.rest, + ), ); this.referencingDefaultValue(pattern, info.assignments, null, true); } @@ -344,7 +352,14 @@ class Referencer extends OriginalReferencer { if (!existed) { upperScope.__define( id, - new Definition('FunctionName', id, node, null, null, null), + new TSESLintScope.Definition( + 'FunctionName', + id, + node, + null, + null, + null, + ), ); } } @@ -364,7 +379,7 @@ class Referencer extends OriginalReferencer { (pattern, info) => { innerScope.__define( pattern, - new ParameterDefinition(pattern, node, i, info.rest), + new TSESLintScope.ParameterDefinition(pattern, node, i, info.rest), ); // Set `variable.eslintUsed` to tell ESLint that the variable is used. @@ -657,7 +672,7 @@ class Referencer extends OriginalReferencer { const scope = this.currentScope(); if (id) { - scope.__define(id, new Definition('EnumName', id, node)); + scope.__define(id, new TSESLintScope.Definition('EnumName', id, node)); } scopeManager.__nestEnumScope(node); @@ -677,9 +692,19 @@ class Referencer extends OriginalReferencer { const { id, initializer } = node; const scope = this.currentScope(); - scope.__define(id, new Definition('EnumMemberName', id, node)); + scope.__define( + id, + new TSESLintScope.Definition('EnumMemberName', id, node), + ); if (initializer) { - scope.__referencing(id, Reference.WRITE, initializer, null, false, true); + scope.__referencing( + id, + TSESLintScope.Reference.WRITE, + initializer, + null, + false, + true, + ); this.visit(initializer); } } @@ -700,7 +725,14 @@ class Referencer extends OriginalReferencer { if (id && id.type === 'Identifier') { scope.__define( id, - new Definition('NamespaceName', id, node, null, null, null), + new TSESLintScope.Definition( + 'NamespaceName', + id, + node, + null, + null, + null, + ), ); } this.visit(body); @@ -738,7 +770,14 @@ class Referencer extends OriginalReferencer { if (id && id.type === 'Identifier') { this.currentScope().__define( id, - new Definition('ImportBinding', id, node, null, null, null), + new TSESLintScope.Definition( + 'ImportBinding', + id, + node, + null, + null, + null, + ), ); } this.visit(moduleReference); diff --git a/packages/parser/src/parser.ts b/packages/parser/src/parser.ts index 71478ee2ad62..edc5d12bf67d 100644 --- a/packages/parser/src/parser.ts +++ b/packages/parser/src/parser.ts @@ -88,12 +88,12 @@ export function parseForESLint( ast.sourceType = options.sourceType; traverser.traverse(ast, { - enter(node: any) { + enter(node) { switch (node.type) { // Function#body cannot be null in ESTree spec. case 'FunctionExpression': if (!node.body) { - node.type = `TSEmptyBody${node.type}` as AST_NODE_TYPES; + node.type = `TSEmptyBody${node.type}` as any; } break; // no default diff --git a/packages/parser/src/scope/scope-manager.ts b/packages/parser/src/scope/scope-manager.ts index 7b7e53c9e84c..648e24b77f8b 100644 --- a/packages/parser/src/scope/scope-manager.ts +++ b/packages/parser/src/scope/scope-manager.ts @@ -1,18 +1,14 @@ -import { TSESTree } from '@typescript-eslint/typescript-estree'; -import EslintScopeManager, { - ScopeManagerOptions, -} from 'eslint-scope/lib/scope-manager'; -import { Scope } from 'eslint-scope/lib/scope'; +import { TSESTree, TSESLintScope } from '@typescript-eslint/experimental-utils'; import { EmptyFunctionScope, EnumScope } from './scopes'; /** * based on eslint-scope */ -export class ScopeManager extends EslintScopeManager { - scopes!: Scope[]; - globalScope!: Scope; +export class ScopeManager extends TSESLintScope.ScopeManager { + scopes!: TSESLintScope.Scope[]; + globalScope!: TSESLintScope.Scope; - constructor(options: ScopeManagerOptions) { + constructor(options: TSESLintScope.ScopeManagerOptions) { super(options); } diff --git a/packages/parser/src/scope/scopes.ts b/packages/parser/src/scope/scopes.ts index 9dff225a7213..4ddaa297d535 100644 --- a/packages/parser/src/scope/scopes.ts +++ b/packages/parser/src/scope/scopes.ts @@ -1,12 +1,11 @@ -import { TSESTree } from '@typescript-eslint/typescript-estree'; -import { Scope } from 'eslint-scope/lib/scope'; +import { TSESTree, TSESLintScope } from '@typescript-eslint/experimental-utils'; import { ScopeManager } from './scope-manager'; /** The scope class for enum. */ -export class EnumScope extends Scope { +export class EnumScope extends TSESLintScope.Scope { constructor( scopeManager: ScopeManager, - upperScope: Scope, + upperScope: TSESLintScope.Scope, block: TSESTree.TSEnumDeclaration | null, ) { super(scopeManager, 'enum', upperScope, block, false); @@ -14,10 +13,10 @@ export class EnumScope extends Scope { } /** The scope class for empty functions. */ -export class EmptyFunctionScope extends Scope { +export class EmptyFunctionScope extends TSESLintScope.Scope { constructor( scopeManager: ScopeManager, - upperScope: Scope, + upperScope: TSESLintScope.Scope, block: TSESTree.TSDeclareFunction | null, ) { super(scopeManager, 'empty-function', upperScope, block, false); diff --git a/packages/parser/typings/eslint-scope.d.ts b/packages/parser/typings/eslint-scope.d.ts deleted file mode 100644 index ec876c63716a..000000000000 --- a/packages/parser/typings/eslint-scope.d.ts +++ /dev/null @@ -1,494 +0,0 @@ -// Type definitions for eslint-scope 4.0.0 -// Project: http://github.com/eslint/eslint-scope -// Definitions by: Armano - -//----------------------------------------------------------------------- -// TODO - figure out how to make ScopeManager exportable so that -// the module's type declaration files don't break -//----------------------------------------------------------------------- - -declare module 'eslint-scope/lib/options' { - import { TSESTree } from '@typescript-eslint/experimental-utils'; - export type PatternVisitorCallback = ( - pattern: TSESTree.Identifier, - info: { - rest: boolean; - topLevel: boolean; - assignments: TSESTree.AssignmentPattern[]; - }, - ) => void; - - export interface PatternVisitorOptions { - processRightHandNodes?: boolean; - } - - export abstract class Visitor { - visitChildren( - node?: T, - ): void; - visit(node?: T): void; - } -} - -declare module 'eslint-scope/lib/variable' { - import { TSESTree } from '@typescript-eslint/experimental-utils'; - import Reference from 'eslint-scope/lib/reference'; - import { Definition } from 'eslint-scope/lib/definition'; - - export default class Variable { - name: string; - identifiers: TSESTree.Identifier[]; - references: Reference[]; - defs: Definition[]; - eslintUsed?: boolean; - } -} - -declare module 'eslint-scope/lib/definition' { - import { TSESTree } from '@typescript-eslint/experimental-utils'; - - export class Definition { - type: string; - name: TSESTree.BindingName; - node: TSESTree.Node; - parent?: TSESTree.Node | null; - index?: number | null; - kind?: string | null; - rest?: boolean; - - constructor( - type: string, - name: TSESTree.BindingName | TSESTree.PropertyName, - node: TSESTree.Node, - parent?: TSESTree.Node | null, - index?: number | null, - kind?: string | null, - ); - } - - export class ParameterDefinition extends Definition { - constructor( - name: TSESTree.Node, - node: TSESTree.Node, - index?: number | null, - rest?: boolean, - ); - } -} - -declare module 'eslint-scope/lib/pattern-visitor' { - import ScopeManager from 'eslint-scope/lib/scope-manager'; - import { TSESTree } from '@typescript-eslint/experimental-utils'; - import { - PatternVisitorCallback, - PatternVisitorOptions, - Visitor, - } from 'eslint-scope/lib/options'; - - export default class PatternVisitor extends Visitor { - protected options: any; - protected scopeManager: ScopeManager; - protected parent?: TSESTree.Node; - public rightHandNodes: TSESTree.Node[]; - - static isPattern(node: TSESTree.Node): boolean; - - constructor( - options: PatternVisitorOptions, - rootPattern: any, - callback: PatternVisitorCallback, - ); - - Identifier(pattern: TSESTree.Node): void; - Property(property: TSESTree.Node): void; - ArrayPattern(pattern: TSESTree.Node): void; - AssignmentPattern(pattern: TSESTree.Node): void; - RestElement(pattern: TSESTree.Node): void; - MemberExpression(node: TSESTree.Node): void; - SpreadElement(node: TSESTree.Node): void; - ArrayExpression(node: TSESTree.Node): void; - AssignmentExpression(node: TSESTree.Node): void; - CallExpression(node: TSESTree.Node): void; - } -} - -declare module 'eslint-scope/lib/referencer' { - import { Scope } from 'eslint-scope/lib/scope'; - import ScopeManager from 'eslint-scope/lib/scope-manager'; - import { TSESTree } from '@typescript-eslint/experimental-utils'; - import { - PatternVisitorCallback, - PatternVisitorOptions, - Visitor, - } from 'eslint-scope/lib/options'; - - export default class Referencer extends Visitor { - protected isInnerMethodDefinition: boolean; - protected options: any; - protected scopeManager: SM; - protected parent?: TSESTree.Node; - - constructor(options: any, scopeManager: SM); - - currentScope(): Scope; - close(node: TSESTree.Node): void; - pushInnerMethodDefinition(isInnerMethodDefinition: boolean): boolean; - popInnerMethodDefinition(isInnerMethodDefinition: boolean): void; - - referencingDefaultValue( - pattern: any, - assignments: any, - maybeImplicitGlobal: any, - init: boolean, - ): void; - visitPattern( - node: TSESTree.Node, - options: PatternVisitorOptions, - callback: PatternVisitorCallback, - ): void; - visitFunction(node: TSESTree.Node): void; - visitClass(node: TSESTree.Node): void; - visitProperty(node: TSESTree.Node): void; - visitForIn(node: TSESTree.Node): void; - visitVariableDeclaration( - variableTargetScope: any, - type: any, - node: TSESTree.Node, - index: any, - ): void; - - AssignmentExpression(node: TSESTree.Node): void; - CatchClause(node: TSESTree.Node): void; - Program(node: TSESTree.Node): void; - Identifier(node: TSESTree.Node): void; - UpdateExpression(node: TSESTree.Node): void; - MemberExpression(node: TSESTree.Node): void; - Property(node: TSESTree.Node): void; - MethodDefinition(node: TSESTree.Node): void; - BreakStatement(): void; - ContinueStatement(): void; - LabeledStatement(node: TSESTree.Node): void; - ForStatement(node: TSESTree.Node): void; - ClassExpression(node: TSESTree.Node): void; - ClassDeclaration(node: TSESTree.Node): void; - CallExpression(node: TSESTree.Node): void; - BlockStatement(node: TSESTree.Node): void; - ThisExpression(): void; - WithStatement(node: TSESTree.Node): void; - VariableDeclaration(node: TSESTree.Node): void; - SwitchStatement(node: TSESTree.Node): void; - FunctionDeclaration(node: TSESTree.Node): void; - FunctionExpression(node: TSESTree.Node): void; - ForOfStatement(node: TSESTree.Node): void; - ForInStatement(node: TSESTree.Node): void; - ArrowFunctionExpression(node: TSESTree.Node): void; - ImportDeclaration(node: TSESTree.Node): void; - visitExportDeclaration(node: TSESTree.Node): void; - ExportDeclaration(node: TSESTree.Node): void; - ExportNamedDeclaration(node: TSESTree.Node): void; - ExportSpecifier(node: TSESTree.Node): void; - MetaProperty(): void; - } -} - -declare module 'eslint-scope/lib/scope' { - import { TSESTree } from '@typescript-eslint/experimental-utils'; - import Reference from 'eslint-scope/lib/reference'; - import Variable from 'eslint-scope/lib/variable'; - import ScopeManager from 'eslint-scope/lib/scope-manager'; - import { Definition } from 'eslint-scope/lib/definition'; - - export type ScopeType = - | 'block' - | 'catch' - | 'class' - | 'for' - | 'function' - | 'function-expression-name' - | 'global' - | 'module' - | 'switch' - | 'with' - | 'TDZ' - | 'enum' - | 'empty-function'; - - export class Scope { - type: ScopeType; - isStrict: boolean; - upper: Scope | null; - childScopes: Scope[]; - variableScope: Scope; - block: TSESTree.Node; - variables: Variable[]; - set: Map; - references: Reference[]; - through: Reference[]; - thisFound?: boolean; - functionExpressionScope: boolean; - - constructor( - scopeManager: ScopeManager, - type: ScopeType, - upperScope: Scope | null, - block: TSESTree.Node | null, - isMethodDefinition: boolean, - ); - - __shouldStaticallyClose(scopeManager: ScopeManager): boolean; - __shouldStaticallyCloseForGlobal(ref: any): boolean; - __staticCloseRef(ref: any): void; - __dynamicCloseRef(ref: any): void; - __globalCloseRef(ref: any): void; - __close(scopeManager: ScopeManager): Scope; - __isValidResolution(ref: any, variable: any): boolean; - __resolve(ref: any): boolean; - __delegateToUpperScope(ref: any): void; - __addDeclaredVariablesOfNode(variable: any, node: TSESTree.Node): void; - __defineGeneric( - name: any, - set: any, - variables: any, - node: any, - def: Definition, - ): void; - - __define(node: TSESTree.Node, def: Definition): void; - - __referencing( - node: TSESTree.Node, - assign: number, - writeExpr: TSESTree.Node, - maybeImplicitGlobal: any, - partial: any, - init: any, - ): void; - - __detectEval(): void; - __detectThis(): void; - __isClosed(): boolean; - /** - * returns resolved {Reference} - * @method Scope#resolve - * @param {Espree.Identifier} ident - identifier to be resolved. - * @returns {Reference} reference - */ - resolve(ident: TSESTree.Node): Reference; - - /** - * returns this scope is static - * @method Scope#isStatic - * @returns {boolean} static - */ - isStatic(): boolean; - - /** - * returns this scope has materialized arguments - * @method Scope#isArgumentsMaterialized - * @returns {boolean} arguemnts materialized - */ - isArgumentsMaterialized(): boolean; - - /** - * returns this scope has materialized `this` reference - * @method Scope#isThisMaterialized - * @returns {boolean} this materialized - */ - isThisMaterialized(): boolean; - - isUsedName(name: any): boolean; - } - - export class GlobalScope extends Scope { - constructor(scopeManager: ScopeManager, block: TSESTree.Node | null); - } - - export class ModuleScope extends Scope { - constructor( - scopeManager: ScopeManager, - upperScope: Scope, - block: TSESTree.Node | null, - ); - } - - export class FunctionExpressionNameScope extends Scope { - constructor( - scopeManager: ScopeManager, - upperScope: Scope, - block: TSESTree.Node | null, - ); - } - - export class CatchScope extends Scope { - constructor( - scopeManager: ScopeManager, - upperScope: Scope, - block: TSESTree.Node | null, - ); - } - - export class WithScope extends Scope { - constructor( - scopeManager: ScopeManager, - upperScope: Scope, - block: TSESTree.Node | null, - ); - } - - export class BlockScope extends Scope { - constructor( - scopeManager: ScopeManager, - upperScope: Scope, - block: TSESTree.Node | null, - ); - } - - export class SwitchScope extends Scope { - constructor( - scopeManager: ScopeManager, - upperScope: Scope, - block: TSESTree.Node | null, - ); - } - - export class FunctionScope extends Scope { - constructor( - scopeManager: ScopeManager, - upperScope: Scope, - block: TSESTree.Node | null, - isMethodDefinition: boolean, - ); - } - - export class ForScope extends Scope { - constructor( - scopeManager: ScopeManager, - upperScope: Scope, - block: TSESTree.Node | null, - ); - } - - export class ClassScope extends Scope { - constructor( - scopeManager: ScopeManager, - upperScope: Scope, - block: TSESTree.Node | null, - ); - } -} - -declare module 'eslint-scope/lib/reference' { - import { TSESTree } from '@typescript-eslint/experimental-utils'; - import { Scope } from 'eslint-scope/lib/scope'; - import Variable from 'eslint-scope/lib/variable'; - - export default class Reference { - identifier: TSESTree.Identifier; - from: Scope; - resolved: Variable | null; - writeExpr: TSESTree.Node | null; - init: boolean; - - isWrite(): boolean; - isRead(): boolean; - isWriteOnly(): boolean; - isReadOnly(): boolean; - isReadWrite(): boolean; - - static READ: 0x1; - static WRITE: 0x2; - static RW: 0x3; - } -} - -declare module 'eslint-scope/lib/scope-manager' { - import { TSESTree } from '@typescript-eslint/experimental-utils'; - import { Scope } from 'eslint-scope/lib/scope'; - import Variable from 'eslint-scope/lib/variable'; - - export interface ScopeManagerOptions { - directive?: boolean; - optimistic?: boolean; - ignoreEval?: boolean; - nodejsScope?: boolean; - sourceType?: 'module' | 'script'; - impliedStrict?: boolean; - ecmaVersion?: number; - } - - export default class ScopeManager { - __options: ScopeManagerOptions; - __currentScope: Scope; - scopes: Scope[]; - globalScope: Scope; - - constructor(options: ScopeManagerOptions); - - __useDirective(): boolean; - __isOptimistic(): boolean; - __ignoreEval(): boolean; - __isNodejsScope(): boolean; - isModule(): boolean; - isImpliedStrict(): boolean; - isStrictModeSupported(): boolean; - - // Returns appropriate scope for this node. - __get(node: TSESTree.Node): Scope; - getDeclaredVariables(node: TSESTree.Node): Variable[]; - acquire(node: TSESTree.Node, inner?: boolean): Scope | null; - acquireAll(node: TSESTree.Node): Scope | null; - release(node: TSESTree.Node, inner?: boolean): Scope | null; - attach(): void; - detach(): void; - - __nestScope(scope: Scope): Scope; - __nestGlobalScope(node: TSESTree.Node): Scope; - __nestBlockScope(node: TSESTree.Node): Scope; - __nestFunctionScope( - node: TSESTree.Node, - isMethodDefinition: boolean, - ): Scope; - __nestForScope(node: TSESTree.Node): Scope; - __nestCatchScope(node: TSESTree.Node): Scope; - __nestWithScope(node: TSESTree.Node): Scope; - __nestClassScope(node: TSESTree.Node): Scope; - __nestSwitchScope(node: TSESTree.Node): Scope; - __nestModuleScope(node: TSESTree.Node): Scope; - __nestFunctionExpressionNameScope(node: TSESTree.Node): Scope; - - __isES6(): boolean; - } -} - -declare module 'eslint-scope' { - import ScopeManager from 'eslint-scope/lib/scope-manager'; - import Reference from 'eslint-scope/lib/reference'; - import Scope from 'eslint-scope/lib/scope'; - import Variable from 'eslint-scope/lib/variable'; - - interface AnalysisOptions { - optimistic?: boolean; - directive?: boolean; - ignoreEval?: boolean; - nodejsScope?: boolean; - impliedStrict?: boolean; - fallback?: string | ((node: {}) => string[]); - sourceType?: 'script' | 'module'; - ecmaVersion?: number; - } - function analyze(ast: {}, options?: AnalysisOptions): ScopeManager; - - const version: string; - - export { - AnalysisOptions, - version, - Reference, - Variable, - Scope, - ScopeManager, - analyze, - }; -} - -declare module 'eslint/lib/util/traverser'; diff --git a/packages/parser/typings/eslint.d.ts b/packages/parser/typings/eslint.d.ts new file mode 100644 index 000000000000..15a965cd48d1 --- /dev/null +++ b/packages/parser/typings/eslint.d.ts @@ -0,0 +1,14 @@ +declare module 'eslint/lib/util/traverser' { + import { TSESTree } from '@typescript-eslint/experimental-utils'; + const traverser: { + traverse( + node: TSESTree.Node, + options: { + enter?: (node: TSESTree.Node, parent: TSESTree.Node) => void; + leave?: (node: TSESTree.Node, parent: TSESTree.Node) => void; + visitorKeys?: Record; + }, + ): void; + }; + export = traverser; +} From 9027cfa1ba8d45d5dc1b1bdc8a7a6a5e2dab01ac Mon Sep 17 00:00:00 2001 From: Brad Zacher Date: Tue, 14 May 2019 10:07:24 -0700 Subject: [PATCH 3/4] chore: linting: add eslint-plugin-import --- .eslintrc.js | 133 ++++++++++++++++++ .eslintrc.json | 73 ---------- package.json | 13 +- packages/eslint-plugin-tslint/package.json | 3 +- packages/eslint-plugin/package.json | 3 +- .../src/rules/no-magic-numbers.ts | 2 +- .../src/rules/prefer-regexp-exec.ts | 4 +- packages/experimental-utils/package.json | 5 +- .../experimental-utils/src/ts-eslint/Rule.ts | 2 +- packages/parser/package.json | 4 +- packages/parser/typings/eslint.d.ts | 1 + packages/typescript-estree/package.json | 13 +- .../typescript-estree/src/ast-converter.ts | 2 +- packages/typescript-estree/src/parser.ts | 6 +- packages/typescript-estree/tests/lib/parse.ts | 2 +- .../utils/generate-package-json.js | 1 + yarn.lock | 123 +++++++++++++++- 17 files changed, 289 insertions(+), 101 deletions(-) create mode 100644 .eslintrc.js delete mode 100644 .eslintrc.json diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 000000000000..02945034be33 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,133 @@ +module.exports = { + root: true, + plugins: [ + 'eslint-plugin', + '@typescript-eslint', + 'jest', + 'import', + 'eslint-comments', + ], + env: { + es6: true, + node: true, + }, + extends: [ + 'eslint:recommended', + 'plugin:@typescript-eslint/eslint-recommended', + 'plugin:@typescript-eslint/recommended', + ], + rules: { + // + // eslint base + // + + 'comma-dangle': ['error', 'always-multiline'], + curly: ['error', 'all'], + 'no-mixed-operators': 'error', + 'no-console': 'off', + 'no-dupe-class-members': 'off', + 'no-undef': 'off', + + // + // our plugin :D + // + + '@typescript-eslint/indent': 'off', + '@typescript-eslint/no-explicit-any': 'off', + '@typescript-eslint/no-non-null-assertion': 'off', + '@typescript-eslint/explicit-function-return-type': 'off', + '@typescript-eslint/explicit-member-accessibility': 'off', + '@typescript-eslint/no-var-requires': 'off', + '@typescript-eslint/no-use-before-define': 'off', + '@typescript-eslint/no-object-literal-type-assertion': 'off', + '@typescript-eslint/no-parameter-properties': 'off', + + // + // eslint-plugin-import + // + + // disallow non-import statements appearing before import statements + 'import/first': 'error', + // Require a newline after the last import/require in a group + 'import/newline-after-import': 'error', + // Forbid import of modules using absolute paths + 'import/no-absolute-path': 'error', + // disallow AMD require/define + 'import/no-amd': 'error', + // forbid default exports + 'import/no-default-export': 'error', + // Forbid the use of extraneous packages + 'import/no-extraneous-dependencies': [ + 'error', + { + devDependencies: true, + peerDependencies: true, + optionalDependencies: false, + }, + ], + // Forbid mutable exports + 'import/no-mutable-exports': 'error', + // Prevent importing the default as if it were named + 'import/no-named-default': 'error', + // Prohibit named exports // we want everything to be a named export + 'import/no-named-export': 'off', + // Forbid a module from importing itself + 'import/no-self-import': 'error', + // Require modules with a single export to use a default export // we want everything to be named + 'import/prefer-default-export': 'off', + }, + parserOptions: { + sourceType: 'module', + ecmaFeatures: { + jsx: false, + }, + project: './tsconfig.base.json', + }, + overrides: [ + { + files: [ + 'packages/eslint-plugin-tslint/tests/**/*.ts', + 'packages/eslint-plugin/tests/**/*.test.ts', + 'packages/parser/tests/**/*.ts', + 'packages/typescript-estree/tests/**/*.ts', + ], + env: { + 'jest/globals': true, + }, + rules: { + 'jest/no-disabled-tests': 'warn', + 'jest/no-focused-tests': 'error', + 'jest/no-alias-methods': 'error', + 'jest/no-identical-title': 'error', + 'jest/no-jasmine-globals': 'error', + 'jest/no-jest-import': 'error', + 'jest/no-test-prefixes': 'error', + 'jest/no-test-callback': 'error', + 'jest/no-test-return-statement': 'error', + 'jest/prefer-to-have-length': 'warn', + 'jest/prefer-spy-on': 'error', + 'jest/valid-expect': 'error', + }, + }, + { + files: [ + 'packages/eslint-plugin/tests/**/*.test.ts', + 'packages/eslint-plugin-tslint/tests/**/*.spec.ts', + ], + rules: { + 'eslint-plugin/no-identical-tests': 'error', + }, + }, + { + files: [ + 'packages/eslint-plugin/src/rules/**/*.ts', + 'packages/eslint-plugin/src/configs/**/*.ts', + 'packages/eslint-plugin-tslint/src/rules/**/*.ts', + ], + rules: { + // specifically for rules - default exports makes the tooling easier + 'import/no-default-export': 'off', + }, + }, + ], +}; diff --git a/.eslintrc.json b/.eslintrc.json deleted file mode 100644 index 649b9ec25021..000000000000 --- a/.eslintrc.json +++ /dev/null @@ -1,73 +0,0 @@ -{ - "root": true, - "plugins": ["eslint-plugin", "@typescript-eslint", "jest"], - "env": { - "es6": true, - "node": true - }, - "extends": [ - "eslint:recommended", - "plugin:@typescript-eslint/eslint-recommended", - "plugin:@typescript-eslint/recommended" - ], - "rules": { - "comma-dangle": ["error", "always-multiline"], - "curly": ["error", "all"], - "no-mixed-operators": "error", - "no-console": "off", - "no-dupe-class-members": "off", - "no-undef": "off", - "@typescript-eslint/indent": "off", - "@typescript-eslint/no-explicit-any": "off", - "@typescript-eslint/no-non-null-assertion": "off", - "@typescript-eslint/explicit-function-return-type": "off", - "@typescript-eslint/explicit-member-accessibility": "off", - "@typescript-eslint/no-var-requires": "off", - "@typescript-eslint/no-use-before-define": "off", - "@typescript-eslint/no-object-literal-type-assertion": "off", - "@typescript-eslint/no-parameter-properties": "off" - }, - "parserOptions": { - "sourceType": "module", - "ecmaFeatures": { - "jsx": false - }, - "project": "./tsconfig.base.json" - }, - "overrides": [ - { - "files": [ - "packages/eslint-plugin-tslint/tests/**/*.ts", - "packages/eslint-plugin/tests/**/*.test.ts", - "packages/parser/tests/**/*.ts", - "packages/typescript-estree/tests/**/*.ts" - ], - "env": { - "jest/globals": true - }, - "rules": { - "jest/no-disabled-tests": "warn", - "jest/no-focused-tests": "error", - "jest/no-alias-methods": "error", - "jest/no-identical-title": "error", - "jest/no-jasmine-globals": "error", - "jest/no-jest-import": "error", - "jest/no-test-prefixes": "error", - "jest/no-test-callback": "error", - "jest/no-test-return-statement": "error", - "jest/prefer-to-have-length": "warn", - "jest/prefer-spy-on": "error", - "jest/valid-expect": "error" - } - }, - { - "files": [ - "packages/eslint-plugin/test/**/*.ts", - "packages/eslint-plugin-tslint/tests/**/*.spec.ts" - ], - "rules": { - "eslint-plugin/no-identical-tests": "error" - } - } - ] -} diff --git a/package.json b/package.json index 8d9760c2c25b..cb6467250538 100644 --- a/package.json +++ b/package.json @@ -47,20 +47,12 @@ "node": ">=6.14.0" }, "devDependencies": { - "@babel/code-frame": "7.0.0", - "@babel/parser": "7.3.2", "@commitlint/cli": "^7.1.2", "@commitlint/config-conventional": "^7.1.2", "@commitlint/travis-cli": "^7.1.2", - "@types/babel-code-frame": "^6.20.1", - "@types/glob": "^7.1.1", "@types/jest": "^24.0.6", - "@types/lodash.isplainobject": "^4.0.4", - "@types/lodash.unescape": "^4.0.4", "@types/node": "^10.12.2", - "@types/semver": "^5.5.0", "all-contributors-cli": "^6.0.0", - "babel-code-frame": "^6.26.0", "cz-conventional-changelog": "2.1.0", "eslint": "^5.12.1", "eslint-plugin-eslint-plugin": "^2.0.1", @@ -71,12 +63,15 @@ "jest": "24.3.0", "lerna": "^3.10.5", "lint-staged": "8.1.0", - "lodash.isplainobject": "4.0.6", "prettier": "^1.17.0", "rimraf": "^2.6.3", "ts-jest": "^24.0.0", "ts-node": "^8.0.1", "tslint": "^5.11.0", "typescript": ">=3.2.1 <3.5.0" + }, + "dependencies": { + "eslint-plugin-eslint-comments": "^3.1.1", + "eslint-plugin-import": "^2.17.2" } } diff --git a/packages/eslint-plugin-tslint/package.json b/packages/eslint-plugin-tslint/package.json index fba222f6dbf3..9139313a3c39 100644 --- a/packages/eslint-plugin-tslint/package.json +++ b/packages/eslint-plugin-tslint/package.json @@ -32,7 +32,8 @@ }, "peerDependencies": { "eslint": "^5.0.0", - "tslint": "^5.0.0" + "tslint": "^5.0.0", + "typescript": "*" }, "devDependencies": { "@types/json-schema": "^7.0.3", diff --git a/packages/eslint-plugin/package.json b/packages/eslint-plugin/package.json index 7f4d04b7b39c..ea76fc317b2e 100644 --- a/packages/eslint-plugin/package.json +++ b/packages/eslint-plugin/package.json @@ -44,7 +44,8 @@ "tsutils": "^3.7.0" }, "devDependencies": { - "eslint-docs": "^0.2.6" + "eslint-docs": "^0.2.6", + "typescript": "*" }, "peerDependencies": { "@typescript-eslint/parser": "1.9.0", diff --git a/packages/eslint-plugin/src/rules/no-magic-numbers.ts b/packages/eslint-plugin/src/rules/no-magic-numbers.ts index 49689213992d..75812ffb4d2d 100644 --- a/packages/eslint-plugin/src/rules/no-magic-numbers.ts +++ b/packages/eslint-plugin/src/rules/no-magic-numbers.ts @@ -8,8 +8,8 @@ import { AST_NODE_TYPES, } from '@typescript-eslint/experimental-utils'; import baseRule from 'eslint/lib/rules/no-magic-numbers'; +import { JSONSchema4 } from 'json-schema'; // eslint-disable-line import/no-extraneous-dependencies import * as util from '../util'; -import { JSONSchema4 } from 'json-schema'; type Options = util.InferOptionsTypeFromRule; type MessageIds = util.InferMessageIdsTypeFromRule; diff --git a/packages/eslint-plugin/src/rules/prefer-regexp-exec.ts b/packages/eslint-plugin/src/rules/prefer-regexp-exec.ts index ea4e6742d05a..b9f18fe51561 100644 --- a/packages/eslint-plugin/src/rules/prefer-regexp-exec.ts +++ b/packages/eslint-plugin/src/rules/prefer-regexp-exec.ts @@ -1,6 +1,6 @@ -import { TSESTree } from '@typescript-eslint/typescript-estree'; -import { createRule, getParserServices, getTypeName } from '../util'; +import { TSESTree } from '@typescript-eslint/experimental-utils'; import { getStaticValue } from 'eslint-utils'; +import { createRule, getParserServices, getTypeName } from '../util'; export default createRule({ name: 'prefer-regexp-exec', diff --git a/packages/experimental-utils/package.json b/packages/experimental-utils/package.json index 9d51a32c4fd1..a177f3cc3bc2 100644 --- a/packages/experimental-utils/package.json +++ b/packages/experimental-utils/package.json @@ -32,11 +32,14 @@ "typecheck": "tsc --noEmit" }, "dependencies": { + "@types/json-schema": "^7.0.3", "@typescript-eslint/typescript-estree": "1.9.0", "eslint-scope": "^4.0.0" }, "peerDependencies": { - "eslint": "*", + "eslint": "*" + }, + "devDependencies": { "typescript": "*" } } diff --git a/packages/experimental-utils/src/ts-eslint/Rule.ts b/packages/experimental-utils/src/ts-eslint/Rule.ts index 388f64e99fcd..ebd376caa804 100644 --- a/packages/experimental-utils/src/ts-eslint/Rule.ts +++ b/packages/experimental-utils/src/ts-eslint/Rule.ts @@ -1,5 +1,5 @@ import { ParserServices, TSESTree } from '@typescript-eslint/typescript-estree'; -import { JSONSchema4 } from 'json-schema'; +import { JSONSchema4 } from 'json-schema'; // eslint-disable-line import/no-extraneous-dependencies import { AST } from './AST'; import { Linter } from './Linter'; import { Scope } from './Scope'; diff --git a/packages/parser/package.json b/packages/parser/package.json index d33fa890f503..1febddba3725 100644 --- a/packages/parser/package.json +++ b/packages/parser/package.json @@ -43,6 +43,8 @@ "eslint-visitor-keys": "^1.0.0" }, "devDependencies": { - "@typescript-eslint/shared-fixtures": "1.9.0" + "@types/glob": "^7.1.1", + "@typescript-eslint/shared-fixtures": "1.9.0", + "glob": "^7.1.4" } } diff --git a/packages/parser/typings/eslint.d.ts b/packages/parser/typings/eslint.d.ts index 15a965cd48d1..c90febb3c47a 100644 --- a/packages/parser/typings/eslint.d.ts +++ b/packages/parser/typings/eslint.d.ts @@ -1,5 +1,6 @@ declare module 'eslint/lib/util/traverser' { import { TSESTree } from '@typescript-eslint/experimental-utils'; + const traverser: { traverse( node: TSESTree.Node, diff --git a/packages/typescript-estree/package.json b/packages/typescript-estree/package.json index 65b6b41440fe..9c676a297f12 100644 --- a/packages/typescript-estree/package.json +++ b/packages/typescript-estree/package.json @@ -41,7 +41,18 @@ "semver": "5.5.0" }, "devDependencies": { + "@babel/code-frame": "7.0.0", + "@babel/parser": "7.3.2", "@babel/types": "^7.3.2", - "@typescript-eslint/shared-fixtures": "1.9.0" + "@types/glob": "^7.1.1", + "@types/babel-code-frame": "^6.20.1", + "@types/lodash.unescape": "^4.0.4", + "@types/lodash.isplainobject": "^4.0.4", + "@types/semver": "^5.5.0", + "@typescript-eslint/shared-fixtures": "1.9.0", + "babel-code-frame": "^6.26.0", + "lodash.isplainobject": "4.0.6", + "typescript": "*", + "glob": "^7.1.4" } } diff --git a/packages/typescript-estree/src/ast-converter.ts b/packages/typescript-estree/src/ast-converter.ts index 25d291dee4c2..8488fa6f7d84 100644 --- a/packages/typescript-estree/src/ast-converter.ts +++ b/packages/typescript-estree/src/ast-converter.ts @@ -4,7 +4,7 @@ import { convertComments } from './convert-comments'; import { convertTokens } from './node-utils'; import { Extra } from './parser-options'; -export default function astConverter( +export function astConverter( ast: SourceFile, extra: Extra, shouldPreserveNodeMaps: boolean, diff --git a/packages/typescript-estree/src/parser.ts b/packages/typescript-estree/src/parser.ts index 1a963859d969..16e180ccc53a 100644 --- a/packages/typescript-estree/src/parser.ts +++ b/packages/typescript-estree/src/parser.ts @@ -1,6 +1,6 @@ import semver from 'semver'; import * as ts from 'typescript'; // leave this as * as ts so people using util package don't need syntheticDefaultImports -import convert from './ast-converter'; +import { astConverter } from './ast-converter'; import { convertError } from './convert'; import { firstDefined } from './node-utils'; import { Extra, TSESTreeOptions, ParserServices } from './parser-options'; @@ -339,7 +339,7 @@ export function parse( /** * Convert the TypeScript AST to an ESTree-compatible one */ - const { estree } = convert(ast, extra, false); + const { estree } = astConverter(ast, extra, false); return estree as AST; } @@ -397,7 +397,7 @@ export function parseAndGenerateServices< * Convert the TypeScript AST to an ESTree-compatible one, and optionally preserve * mappings between converted and original AST nodes */ - const { estree, astMaps } = convert(ast, extra, shouldPreserveNodeMaps); + const { estree, astMaps } = astConverter(ast, extra, shouldPreserveNodeMaps); /** * Even if TypeScript parsed the source code ok, and we had no problems converting the AST, * there may be other syntactic or semantic issues in the code that we can optionally report on. diff --git a/packages/typescript-estree/tests/lib/parse.ts b/packages/typescript-estree/tests/lib/parse.ts index 3c34f7055565..3af2a91d327e 100644 --- a/packages/typescript-estree/tests/lib/parse.ts +++ b/packages/typescript-estree/tests/lib/parse.ts @@ -58,7 +58,7 @@ describe('parse()', () => { describe('loggerFn should be propagated to ast-converter', () => { it('output tokens, comments, locs, and ranges when called with those options', () => { - const spy = jest.spyOn(astConverter, 'default'); + const spy = jest.spyOn(astConverter, 'astConverter'); const loggerFn = jest.fn(() => true); diff --git a/tests/integration/utils/generate-package-json.js b/tests/integration/utils/generate-package-json.js index bf173d5e35c4..1f6af28df63e 100644 --- a/tests/integration/utils/generate-package-json.js +++ b/tests/integration/utils/generate-package-json.js @@ -1,4 +1,5 @@ const fs = require('fs'); +// eslint-disable-next-line import/no-absolute-path const rootPackageJSON = require('/usr/root-package.json'); /** diff --git a/yarn.lock b/yarn.lock index 3f3ce5f61a9c..1b1b0e2075d9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1485,6 +1485,14 @@ array-ify@^1.0.0: resolved "https://registry.yarnpkg.com/array-ify/-/array-ify-1.0.0.tgz#9e528762b4a9066ad163a6962a364418e9626ece" integrity sha1-nlKHYrSpBmrRY6aWKjZEGOlibs4= +array-includes@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.0.3.tgz#184b48f62d92d7452bb31b323165c7f8bd02266d" + integrity sha1-GEtI9i2S10UrsxsyMWXH+L0CJm0= + dependencies: + define-properties "^1.1.2" + es-abstract "^1.7.0" + array-union@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39" @@ -2076,6 +2084,11 @@ console-control-strings@^1.0.0, console-control-strings@~1.1.0: resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4= +contains-path@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/contains-path/-/contains-path-0.1.0.tgz#fe8cf184ff6670b6baef01a9d4861a5cbec4120a" + integrity sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo= + conventional-changelog-angular@^1.3.3: version "1.6.6" resolved "https://registry.yarnpkg.com/conventional-changelog-angular/-/conventional-changelog-angular-1.6.6.tgz#b27f2b315c16d0a1f23eb181309d0e6a4698ea0f" @@ -2343,7 +2356,7 @@ debug@3.1.0: dependencies: ms "2.0.0" -debug@^2.2.0, debug@^2.3.3: +debug@^2.2.0, debug@^2.3.3, debug@^2.6.8, debug@^2.6.9: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== @@ -2518,6 +2531,14 @@ dir-glob@2.0.0: arrify "^1.0.1" path-type "^3.0.0" +doctrine@1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-1.5.0.tgz#379dce730f6166f76cefa4e6707a159b02c5a6fa" + integrity sha1-N53Ocw9hZvds76TmcHoVmwLFpvo= + dependencies: + esutils "^2.0.2" + isarray "^1.0.0" + doctrine@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" @@ -2605,7 +2626,7 @@ error-ex@^1.2.0, error-ex@^1.3.1: dependencies: is-arrayish "^0.2.1" -es-abstract@^1.5.1: +es-abstract@^1.5.1, es-abstract@^1.7.0: version "1.13.0" resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.13.0.tgz#ac86145fdd5099d8dd49558ccba2eaf9b88e24e9" integrity sha512-vDZfg/ykNxQVwup/8E1BZhVzFfBxs9NqMzGcvIJrqg5k2/5Za2bWo40dK2J1pgLngZ7c+Shh8lwYtLGyrwPutg== @@ -2667,11 +2688,52 @@ eslint-docs@^0.2.6: ora "^3.0.0" read-pkg-up "^4.0.0" +eslint-import-resolver-node@^0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.2.tgz#58f15fb839b8d0576ca980413476aab2472db66a" + integrity sha512-sfmTqJfPSizWu4aymbPr4Iidp5yKm8yDkHp+Ir3YiTHiiDfxh69mOUsmiqW6RZ9zRXFaF64GtYmN7e+8GHBv6Q== + dependencies: + debug "^2.6.9" + resolve "^1.5.0" + +eslint-module-utils@^2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.4.0.tgz#8b93499e9b00eab80ccb6614e69f03678e84e09a" + integrity sha512-14tltLm38Eu3zS+mt0KvILC3q8jyIAH518MlG+HO0p+yK885Lb1UHTY/UgR91eOyGdmxAPb+OLoW4znqIT6Ndw== + dependencies: + debug "^2.6.8" + pkg-dir "^2.0.0" + +eslint-plugin-eslint-comments@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-eslint-comments/-/eslint-plugin-eslint-comments-3.1.1.tgz#32ff0afba8a48e17073817e6d03fbc5622f735b7" + integrity sha512-GZDKhOFqJLKlaABX+kdoLskcTINMrVOWxGca54KcFb1QCPd0CLmqgAMRxkkUfGSmN+5NJUMGh7NGccIMcWPSfQ== + dependencies: + escape-string-regexp "^1.0.5" + ignore "^5.0.5" + eslint-plugin-eslint-plugin@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/eslint-plugin-eslint-plugin/-/eslint-plugin-eslint-plugin-2.0.1.tgz#d275434969dbde3da1d4cb7a121dc8d88457c786" integrity sha512-kJ5TZsRJH/xYstG07v3YeOy/W5SDAEzV+bvvoL0aiG1HtqDmg4mJvNPnn/JngANMmsx8oXlJrIcBTCpJzm+9kg== +eslint-plugin-import@^2.17.2: + version "2.17.2" + resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.17.2.tgz#d227d5c6dc67eca71eb590d2bb62fb38d86e9fcb" + integrity sha512-m+cSVxM7oLsIpmwNn2WXTJoReOF9f/CtLMo7qOVmKd1KntBy0hEcuNZ3erTmWjx+DxRO0Zcrm5KwAvI9wHcV5g== + dependencies: + array-includes "^3.0.3" + contains-path "^0.1.0" + debug "^2.6.9" + doctrine "1.5.0" + eslint-import-resolver-node "^0.3.2" + eslint-module-utils "^2.4.0" + has "^1.0.3" + lodash "^4.17.11" + minimatch "^3.0.4" + read-pkg-up "^2.0.0" + resolve "^1.10.0" + eslint-plugin-jest@^22.2.2: version "22.5.1" resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-22.5.1.tgz#a31dfe9f9513c6af7c17ece4c65535a1370f060b" @@ -3310,6 +3372,18 @@ glob@^7.0.3, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3: once "^1.3.0" path-is-absolute "^1.0.0" +glob@^7.1.4: + version "7.1.4" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.4.tgz#aa608a2f6c577ad357e1ae5a5c26d9a8d1969255" + integrity sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + global-dirs@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-0.1.1.tgz#b319c0dd4607f353f3be9cca4c72fc148c49f445" @@ -3534,6 +3608,11 @@ ignore@^4.0.6: resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== +ignore@^5.0.5: + version "5.1.1" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.1.tgz#2fc6b8f518aff48fef65a7f348ed85632448e4a5" + integrity sha512-DWjnQIFLenVrwyRCKZT+7a7/U4Cqgar4WG8V++K3hw+lrW1hc/SIwdiGmtxKCVACmHULTuGeBbHJmbwW7/sAvA== + import-fresh@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-2.0.0.tgz#d81355c15612d386c61f9ddd3922d4304822a546" @@ -3911,7 +3990,7 @@ is-wsl@^1.1.0: resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d" integrity sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0= -isarray@1.0.0, isarray@~1.0.0: +isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= @@ -4691,6 +4770,16 @@ load-json-file@^1.0.0: pinkie-promise "^2.0.0" strip-bom "^2.0.0" +load-json-file@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-2.0.0.tgz#7947e42149af80d696cbf797bcaabcfe1fe29ca8" + integrity sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg= + dependencies: + graceful-fs "^4.1.2" + parse-json "^2.2.0" + pify "^2.0.0" + strip-bom "^3.0.0" + load-json-file@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-4.0.0.tgz#2f5f45ab91e33216234fd53adab668eb4ec0993b" @@ -5805,6 +5894,13 @@ path-type@^1.0.0: pify "^2.0.0" pinkie-promise "^2.0.0" +path-type@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-2.0.0.tgz#f012ccb8415b7096fc2daa1054c3d72389594c73" + integrity sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM= + dependencies: + pify "^2.0.0" + path-type@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f" @@ -6078,6 +6174,14 @@ read-pkg-up@^1.0.1: find-up "^1.0.0" read-pkg "^1.0.0" +read-pkg-up@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-2.0.0.tgz#6b72a8048984e0c41e79510fd5e9fa99b3b549be" + integrity sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4= + dependencies: + find-up "^2.0.0" + read-pkg "^2.0.0" + read-pkg-up@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-3.0.0.tgz#3ed496685dba0f8fe118d0691dc51f4a1ff96f07" @@ -6103,6 +6207,15 @@ read-pkg@^1.0.0: normalize-package-data "^2.3.2" path-type "^1.0.0" +read-pkg@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-2.0.0.tgz#8ef1c0623c6a6db0dc6713c4bfac46332b2368f8" + integrity sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg= + dependencies: + load-json-file "^2.0.0" + normalize-package-data "^2.3.2" + path-type "^2.0.0" + read-pkg@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-3.0.0.tgz#9cbc686978fee65d16c00e2b19c237fcf6e38389" @@ -6334,7 +6447,7 @@ resolve@1.1.7: resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" integrity sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs= -resolve@1.x, resolve@^1.10.0, resolve@^1.3.2: +resolve@1.x, resolve@^1.10.0, resolve@^1.3.2, resolve@^1.5.0: version "1.10.1" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.10.1.tgz#664842ac960795bbe758221cdccda61fb64b5f18" integrity sha512-KuIe4mf++td/eFb6wkaPbMDnP6kObCaEtIDuHOUED6MNUo4K670KZUHuuvYPZDxNF0WVLw49n06M2m2dXphEzA== @@ -7195,7 +7308,7 @@ typedarray@^0.0.6: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= -"typescript@>=3.2.1 <3.5.0": +typescript@*, "typescript@>=3.2.1 <3.5.0": version "3.4.5" resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.4.5.tgz#2d2618d10bb566572b8d7aad5180d84257d70a99" integrity sha512-YycBxUb49UUhdNMU5aJ7z5Ej2XGmaIBL0x34vZ82fn3hGvD+bgrMrVDpatgz2f7YxUMJxMkbWxJZeAvDxVe7Vw== From 75e0f317f082e6217fc712047c475fcc3f781101 Mon Sep 17 00:00:00 2001 From: Brad Zacher Date: Tue, 14 May 2019 10:18:48 -0700 Subject: [PATCH 4/4] chore: linting: add eslint-plugin-eslint-comment --- .eslintrc.js | 46 +++++++++++++++++-- package.json | 6 +-- packages/eslint-plugin/package.json | 2 +- .../eslint-plugin/tools/update-recommended.ts | 2 - .../src/ts-eslint/CLIEngine.ts | 2 +- .../src/ts-eslint/Linter.ts | 2 +- .../src/ts-eslint/SourceCode.ts | 2 +- packages/typescript-estree/src/parser.ts | 2 +- .../typescript-estree/src/semantic-errors.ts | 2 +- 9 files changed, 51 insertions(+), 15 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 02945034be33..84ce4321e0dd 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -24,9 +24,8 @@ module.exports = { 'comma-dangle': ['error', 'always-multiline'], curly: ['error', 'all'], 'no-mixed-operators': 'error', - 'no-console': 'off', - 'no-dupe-class-members': 'off', - 'no-undef': 'off', + 'no-console': 'error', + 'no-process-exit': 'error', // // our plugin :D @@ -75,6 +74,40 @@ module.exports = { 'import/no-self-import': 'error', // Require modules with a single export to use a default export // we want everything to be named 'import/prefer-default-export': 'off', + + // + // eslint-plugin-eslint-comment + // + + // require a eslint-enable comment for every eslint-disable comment + 'eslint-comments/disable-enable-pair': [ + 'error', + { + allowWholeFile: true, + }, + ], + // disallow a eslint-enable comment for multiple eslint-disable comments + 'eslint-comments/no-aggregating-enable': 'error', + // disallow duplicate eslint-disable comments + 'eslint-comments/no-duplicate-disable': 'error', + // disallow eslint-disable comments without rule names + 'eslint-comments/no-unlimited-disable': 'error', + // disallow unused eslint-disable comments + 'eslint-comments/no-unused-disable': 'error', + // disallow unused eslint-enable comments + 'eslint-comments/no-unused-enable': 'error', + // disallow ESLint directive-comments + 'eslint-comments/no-use': [ + 'error', + { + allow: [ + 'eslint-disable', + 'eslint-disable-line', + 'eslint-disable-next-line', + 'eslint-enable', + ], + }, + ], }, parserOptions: { sourceType: 'module', @@ -129,5 +162,12 @@ module.exports = { 'import/no-default-export': 'off', }, }, + { + files: ['**/tools/**/*.ts', '**/tests/**/*.ts'], + rules: { + // allow console logs in tools and tests + 'no-console': 'off', + }, + }, ], }; diff --git a/package.json b/package.json index cb6467250538..b05e48b7fec9 100644 --- a/package.json +++ b/package.json @@ -55,7 +55,9 @@ "all-contributors-cli": "^6.0.0", "cz-conventional-changelog": "2.1.0", "eslint": "^5.12.1", + "eslint-plugin-eslint-comments": "^3.1.1", "eslint-plugin-eslint-plugin": "^2.0.1", + "eslint-plugin-import": "^2.17.2", "eslint-plugin-jest": "^22.2.2", "glob": "7.1.2", "husky": "^1.3.1", @@ -69,9 +71,5 @@ "ts-node": "^8.0.1", "tslint": "^5.11.0", "typescript": ">=3.2.1 <3.5.0" - }, - "dependencies": { - "eslint-plugin-eslint-comments": "^3.1.1", - "eslint-plugin-import": "^2.17.2" } } diff --git a/packages/eslint-plugin/package.json b/packages/eslint-plugin/package.json index ea76fc317b2e..cc9f384bd67e 100644 --- a/packages/eslint-plugin/package.json +++ b/packages/eslint-plugin/package.json @@ -44,11 +44,11 @@ "tsutils": "^3.7.0" }, "devDependencies": { + "@typescript-eslint/parser": "1.9.0", "eslint-docs": "^0.2.6", "typescript": "*" }, "peerDependencies": { - "@typescript-eslint/parser": "1.9.0", "eslint": "^5.0.0" } } diff --git a/packages/eslint-plugin/tools/update-recommended.ts b/packages/eslint-plugin/tools/update-recommended.ts index 39861d35ee74..408f41e172ca 100644 --- a/packages/eslint-plugin/tools/update-recommended.ts +++ b/packages/eslint-plugin/tools/update-recommended.ts @@ -1,5 +1,3 @@ -/* eslint-disable no-console */ - import path from 'path'; import fs from 'fs'; import requireIndex from 'requireindex'; diff --git a/packages/experimental-utils/src/ts-eslint/CLIEngine.ts b/packages/experimental-utils/src/ts-eslint/CLIEngine.ts index 0a64a3d67344..76b3983d370e 100644 --- a/packages/experimental-utils/src/ts-eslint/CLIEngine.ts +++ b/packages/experimental-utils/src/ts-eslint/CLIEngine.ts @@ -1,4 +1,4 @@ -/* eslint-disable @typescript-eslint/no-namespace, no-redeclare */ +/* eslint-disable @typescript-eslint/no-namespace */ import { CLIEngine as ESLintCLIEngine } from 'eslint'; import { Linter } from './Linter'; diff --git a/packages/experimental-utils/src/ts-eslint/Linter.ts b/packages/experimental-utils/src/ts-eslint/Linter.ts index dde85a07f2b4..854b7d461551 100644 --- a/packages/experimental-utils/src/ts-eslint/Linter.ts +++ b/packages/experimental-utils/src/ts-eslint/Linter.ts @@ -1,4 +1,4 @@ -/* eslint-disable @typescript-eslint/no-namespace, no-redeclare */ +/* eslint-disable @typescript-eslint/no-namespace */ import { TSESTree, ParserServices } from '@typescript-eslint/typescript-estree'; import { Linter as ESLintLinter } from 'eslint'; diff --git a/packages/experimental-utils/src/ts-eslint/SourceCode.ts b/packages/experimental-utils/src/ts-eslint/SourceCode.ts index 2fb2e0b3cab9..ac8331166063 100644 --- a/packages/experimental-utils/src/ts-eslint/SourceCode.ts +++ b/packages/experimental-utils/src/ts-eslint/SourceCode.ts @@ -1,4 +1,4 @@ -/* eslint-disable @typescript-eslint/no-namespace, no-redeclare */ +/* eslint-disable @typescript-eslint/no-namespace */ import { ParserServices, TSESTree } from '@typescript-eslint/typescript-estree'; import { SourceCode as ESLintSourceCode } from 'eslint'; diff --git a/packages/typescript-estree/src/parser.ts b/packages/typescript-estree/src/parser.ts index 16e180ccc53a..389c819d2a0c 100644 --- a/packages/typescript-estree/src/parser.ts +++ b/packages/typescript-estree/src/parser.ts @@ -50,7 +50,7 @@ function resetExtra(): void { strict: false, jsx: false, useJSXTextNode: false, - log: console.log, + log: console.log, // eslint-disable-line no-console projects: [], errorOnUnknownASTType: false, errorOnTypeScriptSyntacticAndSemanticIssues: false, diff --git a/packages/typescript-estree/src/semantic-errors.ts b/packages/typescript-estree/src/semantic-errors.ts index f31eb6340682..c580697a185f 100644 --- a/packages/typescript-estree/src/semantic-errors.ts +++ b/packages/typescript-estree/src/semantic-errors.ts @@ -45,7 +45,7 @@ export function getFirstSemanticOrSyntacticError( * and log a a warning. */ /* istanbul ignore next */ - console.warn(`Warning From TSC: "${e.message}`); + console.warn(`Warning From TSC: "${e.message}`); // eslint-disable-line no-console /* istanbul ignore next */ return undefined; }