From 133f622f38a286eac45288a9789d2ee529d5e582 Mon Sep 17 00:00:00 2001 From: Brad Zacher Date: Wed, 11 Mar 2020 10:04:13 -0700 Subject: [PATCH 01/26] feat(typescript-estree): support 3.8 `export * as ns` (#1698) --- .../lib/__snapshots__/typescript.ts.snap | 50 +++++ .../basics/export-star-as-ns-from.src.ts | 1 + packages/typescript-estree/src/convert.ts | 9 +- .../src/ts-estree/ts-estree.ts | 1 + .../tests/ast-alignment/fixtures-to-test.ts | 5 + .../tests/ast-alignment/utils.ts | 12 +- .../lib/__snapshots__/javascript.ts.snap | 1 + .../semantic-diagnostics-enabled.ts.snap | 2 + .../lib/__snapshots__/typescript.ts.snap | 207 ++++++++++++++++++ 9 files changed, 281 insertions(+), 7 deletions(-) create mode 100644 packages/shared-fixtures/fixtures/typescript/basics/export-star-as-ns-from.src.ts diff --git a/packages/parser/tests/lib/__snapshots__/typescript.ts.snap b/packages/parser/tests/lib/__snapshots__/typescript.ts.snap index 9eea694d46d..6357445c265 100644 --- a/packages/parser/tests/lib/__snapshots__/typescript.ts.snap +++ b/packages/parser/tests/lib/__snapshots__/typescript.ts.snap @@ -16384,6 +16384,56 @@ Object { } `; +exports[`typescript fixtures/basics/export-star-as-ns-from.src 1`] = ` +Object { + "$id": 1, + "block": Object { + "range": Array [ + 0, + 28, + ], + "type": "Program", + }, + "childScopes": Array [ + Object { + "$id": 0, + "block": Object { + "range": Array [ + 0, + 28, + ], + "type": "Program", + }, + "childScopes": Array [], + "functionExpressionScope": false, + "isStrict": true, + "references": Array [], + "throughReferences": Array [], + "type": "module", + "upperScope": Object { + "$ref": 1, + }, + "variableMap": Object {}, + "variableScope": Object { + "$ref": 0, + }, + "variables": Array [], + }, + ], + "functionExpressionScope": false, + "isStrict": false, + "references": Array [], + "throughReferences": Array [], + "type": "global", + "upperScope": null, + "variableMap": Object {}, + "variableScope": Object { + "$ref": 1, + }, + "variables": Array [], +} +`; + exports[`typescript fixtures/basics/export-type.src 1`] = ` Object { "$id": 2, diff --git a/packages/shared-fixtures/fixtures/typescript/basics/export-star-as-ns-from.src.ts b/packages/shared-fixtures/fixtures/typescript/basics/export-star-as-ns-from.src.ts new file mode 100644 index 00000000000..ee2c09704ba --- /dev/null +++ b/packages/shared-fixtures/fixtures/typescript/basics/export-star-as-ns-from.src.ts @@ -0,0 +1 @@ +export * as foo from 'bar'; diff --git a/packages/typescript-estree/src/convert.ts b/packages/typescript-estree/src/convert.ts index 97dc43a5ee2..74c701b7fc4 100644 --- a/packages/typescript-estree/src/convert.ts +++ b/packages/typescript-estree/src/convert.ts @@ -1586,10 +1586,7 @@ export class Converter { }); case SyntaxKind.ExportDeclaration: - if (node.exportClause) { - if (node.exportClause.kind !== SyntaxKind.NamedExports) { - throw new Error('`export * as ns` is not yet supported.'); - } + if (node.exportClause?.kind === SyntaxKind.NamedExports) { return this.createNode(node, { type: AST_NODE_TYPES.ExportNamedDeclaration, source: this.convertChild(node.moduleSpecifier), @@ -1604,6 +1601,10 @@ export class Converter { type: AST_NODE_TYPES.ExportAllDeclaration, source: this.convertChild(node.moduleSpecifier), exportKind: node.isTypeOnly ? 'type' : 'value', + exported: + node.exportClause?.kind === SyntaxKind.NamespaceExport + ? this.convertChild(node.exportClause.name) + : null, }); } diff --git a/packages/typescript-estree/src/ts-estree/ts-estree.ts b/packages/typescript-estree/src/ts-estree/ts-estree.ts index 10685a507b9..cbf7787c805 100644 --- a/packages/typescript-estree/src/ts-estree/ts-estree.ts +++ b/packages/typescript-estree/src/ts-estree/ts-estree.ts @@ -869,6 +869,7 @@ export interface ExportAllDeclaration extends BaseNode { type: AST_NODE_TYPES.ExportAllDeclaration; source: Expression | null; exportKind: 'type' | 'value'; + exported: Identifier | null; } export interface ExportDefaultDeclaration extends BaseNode { diff --git a/packages/typescript-estree/tests/ast-alignment/fixtures-to-test.ts b/packages/typescript-estree/tests/ast-alignment/fixtures-to-test.ts index b970aa38f41..c2f1834baeb 100644 --- a/packages/typescript-estree/tests/ast-alignment/fixtures-to-test.ts +++ b/packages/typescript-estree/tests/ast-alignment/fixtures-to-test.ts @@ -436,6 +436,11 @@ tester.addFixturePatternConfig('typescript/basics', { 'import-type-named-as', 'import-type-named', 'import-type-star-as-ns', + /** + * TS 3.8 export * as namespace + * babel uses a representation that does not match the ESTree spec: https://github.com/estree/estree/pull/205 + */ + 'export-star-as-ns-from', ], ignoreSourceType: [ /** diff --git a/packages/typescript-estree/tests/ast-alignment/utils.ts b/packages/typescript-estree/tests/ast-alignment/utils.ts index 6a2426b147f..8f031b534f8 100644 --- a/packages/typescript-estree/tests/ast-alignment/utils.ts +++ b/packages/typescript-estree/tests/ast-alignment/utils.ts @@ -249,8 +249,7 @@ export function preprocessBabylonAST(ast: BabelTypes.File): any { } }, /** - * TS 3.8 import/export type - * babel coming soon https://github.com/babel/babel/pull/11171 + * TS 3.8 features */ ExportNamedDeclaration(node: any) { /** @@ -281,10 +280,17 @@ export function preprocessBabylonAST(ast: BabelTypes.File): any { node.exportKind = 'value'; } } + /** + * TS 3.8 export * as namespace + * babel uses a representation that does not match the ESTree spec: https://github.com/estree/estree/pull/205 + */ + if (!node.exported) { + node.exported = null; + } }, ImportDeclaration(node) { /** - * TS 3.8: export type + * TS 3.8: import type */ if (!node.importKind) { node.importKind = 'value'; diff --git a/packages/typescript-estree/tests/lib/__snapshots__/javascript.ts.snap b/packages/typescript-estree/tests/lib/__snapshots__/javascript.ts.snap index ef4ac4c58c1..4a07dceeaab 100644 --- a/packages/typescript-estree/tests/lib/__snapshots__/javascript.ts.snap +++ b/packages/typescript-estree/tests/lib/__snapshots__/javascript.ts.snap @@ -111896,6 +111896,7 @@ Object { "body": Array [ Object { "exportKind": "value", + "exported": null, "loc": Object { "end": Object { "column": 20, diff --git a/packages/typescript-estree/tests/lib/__snapshots__/semantic-diagnostics-enabled.ts.snap b/packages/typescript-estree/tests/lib/__snapshots__/semantic-diagnostics-enabled.ts.snap index f3189765268..8d197deb452 100644 --- a/packages/typescript-estree/tests/lib/__snapshots__/semantic-diagnostics-enabled.ts.snap +++ b/packages/typescript-estree/tests/lib/__snapshots__/semantic-diagnostics-enabled.ts.snap @@ -1878,6 +1878,8 @@ Object { } `; +exports[`Parse all fixtures with "errorOnTypeScriptSyntacticAndSemanticIssues" enabled fixtures/typescript/basics/export-star-as-ns-from.src 1`] = `"TEST OUTPUT: No semantic or syntactic issues found"`; + exports[`Parse all fixtures with "errorOnTypeScriptSyntacticAndSemanticIssues" enabled fixtures/typescript/basics/export-type.src 1`] = `"TEST OUTPUT: No semantic or syntactic issues found"`; exports[`Parse all fixtures with "errorOnTypeScriptSyntacticAndSemanticIssues" enabled fixtures/typescript/basics/export-type-as.src 1`] = `"TEST OUTPUT: No semantic or syntactic issues found"`; diff --git a/packages/typescript-estree/tests/lib/__snapshots__/typescript.ts.snap b/packages/typescript-estree/tests/lib/__snapshots__/typescript.ts.snap index dc2275c4cd3..f098a7d851e 100644 --- a/packages/typescript-estree/tests/lib/__snapshots__/typescript.ts.snap +++ b/packages/typescript-estree/tests/lib/__snapshots__/typescript.ts.snap @@ -46829,6 +46829,212 @@ Object { } `; +exports[`typescript fixtures/basics/export-star-as-ns-from.src 1`] = ` +Object { + "body": Array [ + Object { + "exportKind": "value", + "exported": Object { + "loc": Object { + "end": Object { + "column": 15, + "line": 1, + }, + "start": Object { + "column": 12, + "line": 1, + }, + }, + "name": "foo", + "range": Array [ + 12, + 15, + ], + "type": "Identifier", + }, + "loc": Object { + "end": Object { + "column": 27, + "line": 1, + }, + "start": Object { + "column": 0, + "line": 1, + }, + }, + "range": Array [ + 0, + 27, + ], + "source": Object { + "loc": Object { + "end": Object { + "column": 26, + "line": 1, + }, + "start": Object { + "column": 21, + "line": 1, + }, + }, + "range": Array [ + 21, + 26, + ], + "raw": "'bar'", + "type": "Literal", + "value": "bar", + }, + "type": "ExportAllDeclaration", + }, + ], + "loc": Object { + "end": Object { + "column": 0, + "line": 2, + }, + "start": Object { + "column": 0, + "line": 1, + }, + }, + "range": Array [ + 0, + 28, + ], + "sourceType": "module", + "tokens": Array [ + Object { + "loc": Object { + "end": Object { + "column": 6, + "line": 1, + }, + "start": Object { + "column": 0, + "line": 1, + }, + }, + "range": Array [ + 0, + 6, + ], + "type": "Keyword", + "value": "export", + }, + Object { + "loc": Object { + "end": Object { + "column": 8, + "line": 1, + }, + "start": Object { + "column": 7, + "line": 1, + }, + }, + "range": Array [ + 7, + 8, + ], + "type": "Punctuator", + "value": "*", + }, + Object { + "loc": Object { + "end": Object { + "column": 11, + "line": 1, + }, + "start": Object { + "column": 9, + "line": 1, + }, + }, + "range": Array [ + 9, + 11, + ], + "type": "Identifier", + "value": "as", + }, + Object { + "loc": Object { + "end": Object { + "column": 15, + "line": 1, + }, + "start": Object { + "column": 12, + "line": 1, + }, + }, + "range": Array [ + 12, + 15, + ], + "type": "Identifier", + "value": "foo", + }, + Object { + "loc": Object { + "end": Object { + "column": 20, + "line": 1, + }, + "start": Object { + "column": 16, + "line": 1, + }, + }, + "range": Array [ + 16, + 20, + ], + "type": "Identifier", + "value": "from", + }, + Object { + "loc": Object { + "end": Object { + "column": 26, + "line": 1, + }, + "start": Object { + "column": 21, + "line": 1, + }, + }, + "range": Array [ + 21, + 26, + ], + "type": "String", + "value": "'bar'", + }, + Object { + "loc": Object { + "end": Object { + "column": 27, + "line": 1, + }, + "start": Object { + "column": 26, + "line": 1, + }, + }, + "range": Array [ + 26, + 27, + ], + "type": "Punctuator", + "value": ";", + }, + ], + "type": "Program", +} +`; + exports[`typescript fixtures/basics/export-type.src 1`] = ` Object { "body": Array [ @@ -47846,6 +48052,7 @@ Object { "body": Array [ Object { "exportKind": "type", + "exported": null, "loc": Object { "end": Object { "column": 25, From 4ab3bf0e77a5ee8040ac36ed17f987ba2f498f14 Mon Sep 17 00:00:00 2001 From: Dave Lunny <4298089+himynameisdave@users.noreply.github.com> Date: Wed, 11 Mar 2020 11:36:45 -0700 Subject: [PATCH 02/26] docs(eslint-plugin): typo in no-unsafe-member-access (#1720) --- packages/eslint-plugin/docs/rules/no-unsafe-member-access.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/eslint-plugin/docs/rules/no-unsafe-member-access.md b/packages/eslint-plugin/docs/rules/no-unsafe-member-access.md index 29c8ea3dd97..2c2d8831f45 100644 --- a/packages/eslint-plugin/docs/rules/no-unsafe-member-access.md +++ b/packages/eslint-plugin/docs/rules/no-unsafe-member-access.md @@ -24,7 +24,7 @@ nestedAny.prop['a']; const key = 'a'; nestedAny.prop[key]; -// usng an any to access a member is unsafe +// Using an any to access a member is unsafe const arr = [1, 2, 3]; arr[anyVar]; nestedAny[anyVar]; From 2ccd66b920816d54cc1a639059f60410df665900 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Thu, 12 Mar 2020 12:40:40 -0700 Subject: [PATCH 03/26] fix(typescript-estree): unnecessary program updates by removing timeout methods (#1693) --- .../src/create-program/createWatchProgram.ts | 68 ++++++++++++------- 1 file changed, 43 insertions(+), 25 deletions(-) diff --git a/packages/typescript-estree/src/create-program/createWatchProgram.ts b/packages/typescript-estree/src/create-program/createWatchProgram.ts index 143219998fa..682e1d7c731 100644 --- a/packages/typescript-estree/src/create-program/createWatchProgram.ts +++ b/packages/typescript-estree/src/create-program/createWatchProgram.ts @@ -1,5 +1,6 @@ import debug from 'debug'; import fs from 'fs'; +import semver from 'semver'; import * as ts from 'typescript'; import { Extra } from '../parser-options'; import { WatchCompilerHostOfConfigFile } from './WatchCompilerHostOfConfigFile'; @@ -18,7 +19,7 @@ const log = debug('typescript-eslint:typescript-estree:createWatchProgram'); */ const knownWatchProgramMap = new Map< CanonicalPath, - ts.WatchOfConfigFile + ts.WatchOfConfigFile >(); /** @@ -226,10 +227,14 @@ function getProgramsForProjects( return results; } +const isRunningNoTimeoutFix = semver.satisfies(ts.version, '>=3.9.0-beta', { + includePrerelease: true, +}); + function createWatchProgram( tsconfigPath: string, extra: Extra, -): ts.WatchOfConfigFile { +): ts.WatchOfConfigFile { log('Creating watch program for %s.', tsconfigPath); // create compiler host @@ -237,10 +242,10 @@ function createWatchProgram( tsconfigPath, createDefaultCompilerOptionsFromExtra(extra), ts.sys, - ts.createSemanticDiagnosticsBuilderProgram, + ts.createAbstractBuilder, diagnosticReporter, /*reportWatchStatus*/ () => {}, - ) as WatchCompilerHostOfConfigFile; + ) as WatchCompilerHostOfConfigFile; // ensure readFile reads the code being linted instead of the copy on disk const oldReadFile = watchCompilerHost.readFile; @@ -250,7 +255,7 @@ function createWatchProgram( filePath === currentLintOperationState.filePath ? currentLintOperationState.code : oldReadFile(filePath, encoding); - if (fileContent) { + if (fileContent !== undefined) { parsedFilesSeenHash.set(filePath, createHash(fileContent)); } return fileContent; @@ -309,25 +314,38 @@ function createWatchProgram( ); oldOnDirectoryStructureHostCreate(host); }; - - /* - * The watch change callbacks TS provides us all have a 250ms delay before firing - * https://github.com/microsoft/TypeScript/blob/b845800bdfcc81c8c72e2ac6fdc2c1df0cdab6f9/src/compiler/watch.ts#L1013 - * - * We live in a synchronous world, so we can't wait for that. - * This is a bit of a hack, but it lets us immediately force updates when we detect a tsconfig or directory change - */ - const oldSetTimeout = watchCompilerHost.setTimeout; - watchCompilerHost.setTimeout = (cb, ms, ...args): unknown => { - if (ms === 250) { - cb(); - return null; - } - - return oldSetTimeout?.(cb, ms, ...args); - }; - - return ts.createWatchProgram(watchCompilerHost); + watchCompilerHost.trace = log; + + // Since we don't want to asynchronously update program we want to disable timeout methods + // So any changes in the program will be delayed and updated when getProgram is called on watch + let callback: (() => void) | undefined; + if (isRunningNoTimeoutFix) { + watchCompilerHost.setTimeout = undefined; + watchCompilerHost.clearTimeout = undefined; + } else { + log('Running without timeout fix'); + // But because of https://github.com/microsoft/TypeScript/pull/37308 we cannot just set it to undefined + // instead save it and call before getProgram is called + watchCompilerHost.setTimeout = (cb, _ms, ...args): unknown => { + callback = cb.bind(/*this*/ undefined, ...args); + return callback; + }; + watchCompilerHost.clearTimeout = (): void => { + callback = undefined; + }; + } + const watch = ts.createWatchProgram(watchCompilerHost); + if (!isRunningNoTimeoutFix) { + const originalGetProgram = watch.getProgram; + watch.getProgram = (): ts.BuilderProgram => { + if (callback) { + callback(); + } + callback = undefined; + return originalGetProgram.call(watch); + }; + } + return watch; } function hasTSConfigChanged(tsconfigPath: CanonicalPath): boolean { @@ -347,7 +365,7 @@ function hasTSConfigChanged(tsconfigPath: CanonicalPath): boolean { } function maybeInvalidateProgram( - existingWatch: ts.WatchOfConfigFile, + existingWatch: ts.WatchOfConfigFile, filePath: CanonicalPath, tsconfigPath: CanonicalPath, ): ts.Program | null { From 0b65f5e126811b74db69767a887b200b01d272c3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 14 Mar 2020 10:59:20 -0700 Subject: [PATCH 04/26] chore: bump acorn from 6.4.0 to 6.4.1 (#1730) Bumps [acorn](https://github.com/acornjs/acorn) from 6.4.0 to 6.4.1. - [Release notes](https://github.com/acornjs/acorn/releases) - [Commits](https://github.com/acornjs/acorn/compare/6.4.0...6.4.1) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 20ca893a392..5a972a6cc18 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1564,9 +1564,9 @@ acorn-walk@^6.0.1: integrity sha512-7evsyfH1cLOCdAzZAd43Cic04yKydNx0cF+7tiA19p1XnLLPU4dpCQOqpjqwokFe//vS0QqfqqjCS2JkiIs0cA== acorn@^6.0.1: - version "6.4.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.0.tgz#b659d2ffbafa24baf5db1cdbb2c94a983ecd2784" - integrity sha512-gac8OEcQ2Li1dxIEWGZzsp2BitJxwkwcOm0zHAJLcPJaVvm58FRnk6RkuLRpU1EujipU2ZFODv2P9DLMfnV8mw== + version "6.4.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.1.tgz#531e58ba3f51b9dacb9a6646ca4debf5b14ca474" + integrity sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA== acorn@^7.1.0: version "7.1.0" From cb33eba393d1670b1bd3ada413121498a1cdf9d2 Mon Sep 17 00:00:00 2001 From: Brad Zacher Date: Sat, 14 Mar 2020 11:06:25 -0700 Subject: [PATCH 05/26] chore: add code of conduct --- CODE_OF_CONDUCT.md | 76 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 CODE_OF_CONDUCT.md diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 00000000000..65cf5ffee97 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,76 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, sex characteristics, gender identity and expression, +level of experience, education, socio-economic status, nationality, personal +appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or + advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see +https://www.contributor-covenant.org/faq From 4a0e886b710a47c7e3dc84583d175561e0165242 Mon Sep 17 00:00:00 2001 From: Brad Zacher Date: Sat, 14 Mar 2020 11:11:30 -0700 Subject: [PATCH 06/26] docs: add netlify to the readme --- README.md | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index c71797e98ba..c23634df344 100644 --- a/README.md +++ b/README.md @@ -260,22 +260,14 @@ TypeScript ESLint inherits from the the original TypeScript ESLint Parser licens This project exists thanks to every one of the awesome people who contribute code and documentation: -
- -
- 🙏 An extra special thanks goes out to the wonderful people listed in [`CONTRIBUTORS.md`](./CONTRIBUTORS.md) -
- ## Financial Contributors In addition to submitting code and documentation updates, you can help us sustain our community by becoming a financial contributor [[Click here to contribute - every little bit helps!](https://opencollective.com/typescript-eslint/contribute)] -
- ### Individuals @@ -297,6 +289,10 @@ Support this project with your organization. Your logo will show up here with a
+ + Deploys by Netlify + + ## Contributing [See the contributing guide here](./CONTRIBUTING.md) From 970cfbd9bb644fc364361afdbaf13c874e1ee3a3 Mon Sep 17 00:00:00 2001 From: Brad Zacher Date: Sat, 14 Mar 2020 11:29:20 -0700 Subject: [PATCH 07/26] docs: prettier the code of conduct --- CODE_OF_CONDUCT.md | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 65cf5ffee97..b16e98be434 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -14,22 +14,22 @@ appearance, race, religion, or sexual identity and orientation. Examples of behavior that contributes to creating a positive environment include: -* Using welcoming and inclusive language -* Being respectful of differing viewpoints and experiences -* Gracefully accepting constructive criticism -* Focusing on what is best for the community -* Showing empathy towards other community members +- Using welcoming and inclusive language +- Being respectful of differing viewpoints and experiences +- Gracefully accepting constructive criticism +- Focusing on what is best for the community +- Showing empathy towards other community members Examples of unacceptable behavior by participants include: -* The use of sexualized language or imagery and unwelcome sexual attention or - advances -* Trolling, insulting/derogatory comments, and personal or political attacks -* Public or private harassment -* Publishing others' private information, such as a physical or electronic - address, without explicit permission -* Other conduct which could reasonably be considered inappropriate in a - professional setting +- The use of sexualized language or imagery and unwelcome sexual attention or + advances +- Trolling, insulting/derogatory comments, and personal or political attacks +- Public or private harassment +- Publishing others' private information, such as a physical or electronic + address, without explicit permission +- Other conduct which could reasonably be considered inappropriate in a + professional setting ## Our Responsibilities From 71ef2670959af0672ac5a44ee962190e19f15f50 Mon Sep 17 00:00:00 2001 From: Brad Zacher Date: Sat, 14 Mar 2020 11:43:32 -0700 Subject: [PATCH 08/26] docs: code of conduct spelling --- CODE_OF_CONDUCT.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index b16e98be434..1de67c066c8 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -6,7 +6,7 @@ In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, sex characteristics, gender identity and expression, -level of experience, education, socio-economic status, nationality, personal +level of experience, education, socioeconomic status, nationality, personal appearance, race, religion, or sexual identity and orientation. ## Our Standards From 56e1e160a36cecc298dd1187ea035e1482489dd0 Mon Sep 17 00:00:00 2001 From: James Henry Date: Mon, 16 Mar 2020 17:01:54 +0000 Subject: [PATCH 09/26] chore: publish v2.24.0 --- CHANGELOG.md | 16 ++++++++++++++++ lerna.json | 2 +- packages/eslint-plugin-internal/CHANGELOG.md | 8 ++++++++ packages/eslint-plugin-internal/package.json | 4 ++-- packages/eslint-plugin-tslint/CHANGELOG.md | 8 ++++++++ packages/eslint-plugin-tslint/package.json | 6 +++--- packages/eslint-plugin/CHANGELOG.md | 8 ++++++++ packages/eslint-plugin/package.json | 4 ++-- packages/experimental-utils/CHANGELOG.md | 8 ++++++++ packages/experimental-utils/package.json | 4 ++-- packages/parser/CHANGELOG.md | 11 +++++++++++ packages/parser/package.json | 8 ++++---- packages/shared-fixtures/CHANGELOG.md | 11 +++++++++++ packages/shared-fixtures/package.json | 2 +- packages/typescript-estree/CHANGELOG.md | 16 ++++++++++++++++ packages/typescript-estree/package.json | 4 ++-- 16 files changed, 103 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 24b66c8e88b..9d1da108f54 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,22 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [2.24.0](https://github.com/typescript-eslint/typescript-eslint/compare/v2.23.0...v2.24.0) (2020-03-16) + + +### Bug Fixes + +* **typescript-estree:** unnecessary program updates by removing timeout methods ([#1693](https://github.com/typescript-eslint/typescript-eslint/issues/1693)) ([2ccd66b](https://github.com/typescript-eslint/typescript-eslint/commit/2ccd66b920816d54cc1a639059f60410df665900)) + + +### Features + +* **typescript-estree:** support 3.8 `export * as ns` ([#1698](https://github.com/typescript-eslint/typescript-eslint/issues/1698)) ([133f622](https://github.com/typescript-eslint/typescript-eslint/commit/133f622f38a286eac45288a9789d2ee529d5e582)) + + + + + # [2.23.0](https://github.com/typescript-eslint/typescript-eslint/compare/v2.22.0...v2.23.0) (2020-03-09) diff --git a/lerna.json b/lerna.json index 0c6d9da656c..b6ba0964df8 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "2.23.0", + "version": "2.24.0", "npmClient": "yarn", "useWorkspaces": true, "stream": true diff --git a/packages/eslint-plugin-internal/CHANGELOG.md b/packages/eslint-plugin-internal/CHANGELOG.md index 9160fae9e2e..e8e1b2cb841 100644 --- a/packages/eslint-plugin-internal/CHANGELOG.md +++ b/packages/eslint-plugin-internal/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [2.24.0](https://github.com/typescript-eslint/typescript-eslint/compare/v2.23.0...v2.24.0) (2020-03-16) + +**Note:** Version bump only for package @typescript-eslint/eslint-plugin-internal + + + + + # [2.23.0](https://github.com/typescript-eslint/typescript-eslint/compare/v2.22.0...v2.23.0) (2020-03-09) **Note:** Version bump only for package @typescript-eslint/eslint-plugin-internal diff --git a/packages/eslint-plugin-internal/package.json b/packages/eslint-plugin-internal/package.json index 38f95dadac0..a318b9d4571 100644 --- a/packages/eslint-plugin-internal/package.json +++ b/packages/eslint-plugin-internal/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/eslint-plugin-internal", - "version": "2.23.0", + "version": "2.24.0", "private": true, "main": "dist/index.js", "scripts": { @@ -12,6 +12,6 @@ "typecheck": "tsc -p tsconfig.json --noEmit" }, "dependencies": { - "@typescript-eslint/experimental-utils": "2.23.0" + "@typescript-eslint/experimental-utils": "2.24.0" } } diff --git a/packages/eslint-plugin-tslint/CHANGELOG.md b/packages/eslint-plugin-tslint/CHANGELOG.md index 029236988fe..a2fd2d00412 100644 --- a/packages/eslint-plugin-tslint/CHANGELOG.md +++ b/packages/eslint-plugin-tslint/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [2.24.0](https://github.com/typescript-eslint/typescript-eslint/compare/v2.23.0...v2.24.0) (2020-03-16) + +**Note:** Version bump only for package @typescript-eslint/eslint-plugin-tslint + + + + + # [2.23.0](https://github.com/typescript-eslint/typescript-eslint/compare/v2.22.0...v2.23.0) (2020-03-09) **Note:** Version bump only for package @typescript-eslint/eslint-plugin-tslint diff --git a/packages/eslint-plugin-tslint/package.json b/packages/eslint-plugin-tslint/package.json index 225d803421d..10ea4787726 100644 --- a/packages/eslint-plugin-tslint/package.json +++ b/packages/eslint-plugin-tslint/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/eslint-plugin-tslint", - "version": "2.23.0", + "version": "2.24.0", "main": "dist/index.js", "typings": "src/index.ts", "description": "TSLint wrapper plugin for ESLint", @@ -31,7 +31,7 @@ "typecheck": "tsc -p tsconfig.json --noEmit" }, "dependencies": { - "@typescript-eslint/experimental-utils": "2.23.0", + "@typescript-eslint/experimental-utils": "2.24.0", "lodash": "^4.17.15" }, "peerDependencies": { @@ -41,6 +41,6 @@ }, "devDependencies": { "@types/lodash": "^4.14.149", - "@typescript-eslint/parser": "2.23.0" + "@typescript-eslint/parser": "2.24.0" } } diff --git a/packages/eslint-plugin/CHANGELOG.md b/packages/eslint-plugin/CHANGELOG.md index 6e837af6b5f..56a3265aba7 100644 --- a/packages/eslint-plugin/CHANGELOG.md +++ b/packages/eslint-plugin/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [2.24.0](https://github.com/typescript-eslint/typescript-eslint/compare/v2.23.0...v2.24.0) (2020-03-16) + +**Note:** Version bump only for package @typescript-eslint/eslint-plugin + + + + + # [2.23.0](https://github.com/typescript-eslint/typescript-eslint/compare/v2.22.0...v2.23.0) (2020-03-09) diff --git a/packages/eslint-plugin/package.json b/packages/eslint-plugin/package.json index a8dffa55c69..1083d0b5aa8 100644 --- a/packages/eslint-plugin/package.json +++ b/packages/eslint-plugin/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/eslint-plugin", - "version": "2.23.0", + "version": "2.24.0", "description": "TypeScript plugin for ESLint", "keywords": [ "eslint", @@ -41,7 +41,7 @@ "typecheck": "tsc -p tsconfig.json --noEmit" }, "dependencies": { - "@typescript-eslint/experimental-utils": "2.23.0", + "@typescript-eslint/experimental-utils": "2.24.0", "eslint-utils": "^1.4.3", "functional-red-black-tree": "^1.0.1", "regexpp": "^3.0.0", diff --git a/packages/experimental-utils/CHANGELOG.md b/packages/experimental-utils/CHANGELOG.md index eecd8151141..33f3bce56c0 100644 --- a/packages/experimental-utils/CHANGELOG.md +++ b/packages/experimental-utils/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [2.24.0](https://github.com/typescript-eslint/typescript-eslint/compare/v2.23.0...v2.24.0) (2020-03-16) + +**Note:** Version bump only for package @typescript-eslint/experimental-utils + + + + + # [2.23.0](https://github.com/typescript-eslint/typescript-eslint/compare/v2.22.0...v2.23.0) (2020-03-09) **Note:** Version bump only for package @typescript-eslint/experimental-utils diff --git a/packages/experimental-utils/package.json b/packages/experimental-utils/package.json index 22237081437..a8797df8a9c 100644 --- a/packages/experimental-utils/package.json +++ b/packages/experimental-utils/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/experimental-utils", - "version": "2.23.0", + "version": "2.24.0", "description": "(Experimental) Utilities for working with TypeScript + ESLint together", "keywords": [ "eslint", @@ -37,7 +37,7 @@ }, "dependencies": { "@types/json-schema": "^7.0.3", - "@typescript-eslint/typescript-estree": "2.23.0", + "@typescript-eslint/typescript-estree": "2.24.0", "eslint-scope": "^5.0.0" }, "peerDependencies": { diff --git a/packages/parser/CHANGELOG.md b/packages/parser/CHANGELOG.md index ce0d935eabe..6f5b20e66e8 100644 --- a/packages/parser/CHANGELOG.md +++ b/packages/parser/CHANGELOG.md @@ -3,6 +3,17 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [2.24.0](https://github.com/typescript-eslint/typescript-eslint/compare/v2.23.0...v2.24.0) (2020-03-16) + + +### Features + +* **typescript-estree:** support 3.8 `export * as ns` ([#1698](https://github.com/typescript-eslint/typescript-eslint/issues/1698)) ([133f622](https://github.com/typescript-eslint/typescript-eslint/commit/133f622f38a286eac45288a9789d2ee529d5e582)) + + + + + # [2.23.0](https://github.com/typescript-eslint/typescript-eslint/compare/v2.22.0...v2.23.0) (2020-03-09) diff --git a/packages/parser/package.json b/packages/parser/package.json index aae36c07ded..da6fbd2b5e8 100644 --- a/packages/parser/package.json +++ b/packages/parser/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/parser", - "version": "2.23.0", + "version": "2.24.0", "description": "An ESLint custom parser which leverages TypeScript ESTree", "main": "dist/parser.js", "types": "dist/parser.d.ts", @@ -43,13 +43,13 @@ }, "dependencies": { "@types/eslint-visitor-keys": "^1.0.0", - "@typescript-eslint/experimental-utils": "2.23.0", - "@typescript-eslint/typescript-estree": "2.23.0", + "@typescript-eslint/experimental-utils": "2.24.0", + "@typescript-eslint/typescript-estree": "2.24.0", "eslint-visitor-keys": "^1.1.0" }, "devDependencies": { "@types/glob": "^7.1.1", - "@typescript-eslint/shared-fixtures": "2.23.0", + "@typescript-eslint/shared-fixtures": "2.24.0", "glob": "*" }, "peerDependenciesMeta": { diff --git a/packages/shared-fixtures/CHANGELOG.md b/packages/shared-fixtures/CHANGELOG.md index 0d817b532b6..70196052327 100644 --- a/packages/shared-fixtures/CHANGELOG.md +++ b/packages/shared-fixtures/CHANGELOG.md @@ -3,6 +3,17 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [2.24.0](https://github.com/typescript-eslint/typescript-eslint/compare/v2.23.0...v2.24.0) (2020-03-16) + + +### Features + +* **typescript-estree:** support 3.8 `export * as ns` ([#1698](https://github.com/typescript-eslint/typescript-eslint/issues/1698)) ([133f622](https://github.com/typescript-eslint/typescript-eslint/commit/133f622f38a286eac45288a9789d2ee529d5e582)) + + + + + # [2.23.0](https://github.com/typescript-eslint/typescript-eslint/compare/v2.22.0...v2.23.0) (2020-03-09) diff --git a/packages/shared-fixtures/package.json b/packages/shared-fixtures/package.json index abe5c65bb61..360b466615b 100644 --- a/packages/shared-fixtures/package.json +++ b/packages/shared-fixtures/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/shared-fixtures", - "version": "2.23.0", + "version": "2.24.0", "private": true, "scripts": { "build": "tsc -b tsconfig.build.json", diff --git a/packages/typescript-estree/CHANGELOG.md b/packages/typescript-estree/CHANGELOG.md index a163e32428e..b52cddede33 100644 --- a/packages/typescript-estree/CHANGELOG.md +++ b/packages/typescript-estree/CHANGELOG.md @@ -3,6 +3,22 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [2.24.0](https://github.com/typescript-eslint/typescript-eslint/compare/v2.23.0...v2.24.0) (2020-03-16) + + +### Bug Fixes + +* **typescript-estree:** unnecessary program updates by removing timeout methods ([#1693](https://github.com/typescript-eslint/typescript-eslint/issues/1693)) ([2ccd66b](https://github.com/typescript-eslint/typescript-eslint/commit/2ccd66b920816d54cc1a639059f60410df665900)) + + +### Features + +* **typescript-estree:** support 3.8 `export * as ns` ([#1698](https://github.com/typescript-eslint/typescript-eslint/issues/1698)) ([133f622](https://github.com/typescript-eslint/typescript-eslint/commit/133f622f38a286eac45288a9789d2ee529d5e582)) + + + + + # [2.23.0](https://github.com/typescript-eslint/typescript-eslint/compare/v2.22.0...v2.23.0) (2020-03-09) diff --git a/packages/typescript-estree/package.json b/packages/typescript-estree/package.json index 921adecc251..9375c156787 100644 --- a/packages/typescript-estree/package.json +++ b/packages/typescript-estree/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/typescript-estree", - "version": "2.23.0", + "version": "2.24.0", "description": "A parser that converts TypeScript source code into an ESTree compatible form", "main": "dist/parser.js", "types": "dist/parser.d.ts", @@ -58,7 +58,7 @@ "@types/lodash": "^4.14.149", "@types/semver": "^6.2.0", "@types/tmp": "^0.1.0", - "@typescript-eslint/shared-fixtures": "2.23.0", + "@typescript-eslint/shared-fixtures": "2.24.0", "tmp": "^0.1.0", "typescript": "*" }, From c96988433e7fda6684a575961203c44f116cd2f3 Mon Sep 17 00:00:00 2001 From: Armano Date: Mon, 16 Mar 2020 18:28:56 +0100 Subject: [PATCH 10/26] chore: add start of github actions (#1383) --- .github/workflows/ci.yml | 106 +++++++++++++++++++++++++++++++++++++++ README.md | 4 +- 2 files changed, 108 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000000..1be9fde0e33 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,106 @@ +name: CI + +on: + push: + branches: + - master + pull_request: + branches: + - '**' + +jobs: + primary_code_validation_and_tests: + name: Primary code validation and tests + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Use Node.js 12 + uses: actions/setup-node@v1 + with: + node-version: 12 + + # This also runs a build as part of the postinstall bootstrap + - name: install and build + run: | + yarn --ignore-engines --frozen-lockfile + yarn check-clean-workspace-after-install + + # Note that this command *also* typechecks tests/tools, + # whereas the build only checks src files + - name: Typecheck all packages + run: yarn typecheck + + - name: Check code formatting + run: yarn format-check + + - name: Run linting + run: yarn lint + + - name: Validate spelling + run: yarn check:spelling + + - name: Run unit tests + run: yarn test + env: + CI: true + + - name: Run integrations tests + run: yarn integration-tests + env: + CI: true + + - name: Publish code coverage report + uses: codecov/codecov-action@v1 + with: + yml: ./codecov.yml + token: ${{ secrets.CODECOV_TOKEN }} + flags: unittest + name: codecov + + unit_tests_on_other_node_versions: + name: Run unit tests on other Node.js versions + runs-on: ubuntu-latest + strategy: + matrix: + node-version: [8.x, 10.x] + steps: + - uses: actions/checkout@v2 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v1 + with: + node-version: ${{ matrix.node-version }} + + # This also runs a build as part of the postinstall bootstrap + - name: install and build + run: | + yarn --ignore-engines --frozen-lockfile + yarn check-clean-workspace-after-install + + - name: Run unit tests + run: yarn test + env: + CI: true + + publish_canary_version: + name: Publish the latest code as a canary version + runs-on: ubuntu-latest + needs: [primary_code_validation_and_tests, unit_tests_on_other_node_versions] + if: github.event_name == 'push' && github.ref == 'refs/head/master' + steps: + - uses: actions/checkout@v1 + - name: Use Node.js 12 + uses: actions/setup-node@v1 + with: + node-version: 12 + registry-url: https://registry.npmjs.org/ + + # This also runs a build as part of the postinstall bootstrap + - name: install and build + run: | + yarn --ignore-engines --frozen-lockfile + yarn check-clean-workspace-after-install + +# - name: Publish all packages to npm +# run: npx lerna publish --canary --exact --force-publish --yes +# env: +# NODE_AUTH_TOKEN: ${{ secrets.npm_token }} diff --git a/README.md b/README.md index c23634df344..95cfe76c5eb 100644 --- a/README.md +++ b/README.md @@ -3,8 +3,8 @@

Monorepo for all the tooling which enables ESLint to support TypeScript

- Azure Pipelines - + GitHub Workflow Status + Financial Contributors on Open Collective GitHub license NPM Downloads Codecov From d12183883aa880810560ef7a2447d002f440ebc5 Mon Sep 17 00:00:00 2001 From: James Henry Date: Mon, 16 Mar 2020 18:51:10 +0000 Subject: [PATCH 11/26] chore: switch to github actions for ci (#1745) --- .github/workflows/ci.yml | 101 ++++++++++++++++++++++++++++++------- azure-pipelines.yml | 104 --------------------------------------- 2 files changed, 84 insertions(+), 121 deletions(-) delete mode 100644 azure-pipelines.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1be9fde0e33..fb2e31ef8ea 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,24 +8,39 @@ on: branches: - '**' +env: + PRIMARY_NODE_VERSION: 12 + jobs: primary_code_validation_and_tests: name: Primary code validation and tests runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - - name: Use Node.js 12 + - name: Use Node.js ${{ env.PRIMARY_NODE_VERSION }} uses: actions/setup-node@v1 with: - node-version: 12 + node-version: ${{ env.PRIMARY_NODE_VERSION }} + + - name: Get yarn cache directory path + id: yarn-cache-dir-path + run: echo "::set-output name=dir::$(yarn cache dir)" + + - uses: actions/cache@v1 + id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`) + with: + path: ${{ steps.yarn-cache-dir-path.outputs.dir }} + key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} + restore-keys: | + ${{ runner.os }}-yarn- # This also runs a build as part of the postinstall bootstrap - - name: install and build + - name: Install dependencies and build run: | yarn --ignore-engines --frozen-lockfile yarn check-clean-workspace-after-install - # Note that this command *also* typechecks tests/tools, + # Note that this command *also* type checks tests/tools, # whereas the build only checks src files - name: Typecheck all packages run: yarn typecheck @@ -44,11 +59,6 @@ jobs: env: CI: true - - name: Run integrations tests - run: yarn integration-tests - env: - CI: true - - name: Publish code coverage report uses: codecov/codecov-action@v1 with: @@ -57,6 +67,39 @@ jobs: flags: unittest name: codecov + integration_tests: + name: Run integration tests on primary Node.js version + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Use Node.js ${{ env.PRIMARY_NODE_VERSION }} + uses: actions/setup-node@v1 + with: + node-version: ${{ env.PRIMARY_NODE_VERSION }} + + - name: Get yarn cache directory path + id: yarn-cache-dir-path + run: echo "::set-output name=dir::$(yarn cache dir)" + + - uses: actions/cache@v1 + id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`) + with: + path: ${{ steps.yarn-cache-dir-path.outputs.dir }} + key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} + restore-keys: | + ${{ runner.os }}-yarn- + + # This also runs a build as part of the postinstall bootstrap + - name: Install dependencies and build + run: | + yarn --ignore-engines --frozen-lockfile + yarn check-clean-workspace-after-install + + - name: Run integrations tests + run: yarn integration-tests + env: + CI: true + unit_tests_on_other_node_versions: name: Run unit tests on other Node.js versions runs-on: ubuntu-latest @@ -70,8 +113,20 @@ jobs: with: node-version: ${{ matrix.node-version }} + - name: Get yarn cache directory path + id: yarn-cache-dir-path + run: echo "::set-output name=dir::$(yarn cache dir)" + + - uses: actions/cache@v1 + id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`) + with: + path: ${{ steps.yarn-cache-dir-path.outputs.dir }} + key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} + restore-keys: | + ${{ runner.os }}-yarn- + # This also runs a build as part of the postinstall bootstrap - - name: install and build + - name: Install dependencies and build run: | yarn --ignore-engines --frozen-lockfile yarn check-clean-workspace-after-install @@ -88,19 +143,31 @@ jobs: if: github.event_name == 'push' && github.ref == 'refs/head/master' steps: - uses: actions/checkout@v1 - - name: Use Node.js 12 + - name: Use Node.js ${{ env.PRIMARY_NODE_VERSION }} uses: actions/setup-node@v1 with: - node-version: 12 + node-version: ${{ env.PRIMARY_NODE_VERSION }} registry-url: https://registry.npmjs.org/ + - name: Get yarn cache directory path + id: yarn-cache-dir-path + run: echo "::set-output name=dir::$(yarn cache dir)" + + - uses: actions/cache@v1 + id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`) + with: + path: ${{ steps.yarn-cache-dir-path.outputs.dir }} + key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} + restore-keys: | + ${{ runner.os }}-yarn- + # This also runs a build as part of the postinstall bootstrap - - name: install and build + - name: Install dependencies and build run: | yarn --ignore-engines --frozen-lockfile yarn check-clean-workspace-after-install -# - name: Publish all packages to npm -# run: npx lerna publish --canary --exact --force-publish --yes -# env: -# NODE_AUTH_TOKEN: ${{ secrets.npm_token }} + - name: Publish all packages to npm + run: npx lerna publish --canary --exact --force-publish --yes + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/azure-pipelines.yml b/azure-pipelines.yml deleted file mode 100644 index 172a64beab7..00000000000 --- a/azure-pipelines.yml +++ /dev/null @@ -1,104 +0,0 @@ -trigger: - - master - -jobs: - - job: primary_code_validation_and_tests - displayName: Primary code validation and tests - pool: - vmImage: 'Ubuntu-18.04' - steps: - - task: NodeTool@0 - inputs: - versionSpec: 12 - displayName: 'Install Node.js 12' - - - script: | - # This also runs a build as part of the postinstall - # bootstrap - yarn --ignore-engines --frozen-lockfile - yarn check-clean-workspace-after-install - - - script: | - # Note that this command *also* typechecks tests/tools, - # whereas the build only checks src files - yarn typecheck - displayName: 'Typecheck all packages' - - - script: | - yarn format-check - displayName: 'Check code formatting' - - - script: | - yarn lint - displayName: 'Run linting' - - - script: | - yarn check:spelling - displayName: 'Validate documentation spelling' - - - script: | - yarn test - displayName: 'Run unit tests' - - - script: | - yarn integration-tests - displayName: 'Run integrations tests' - - - bash: bash <(curl -s https://codecov.io/bash) -P "$SYSTEM_PULLREQUEST_PULLREQUESTNUMBER" - env: - CODECOV_TOKEN: $(CODECOV_TOKEN) - displayName: 'Publish code coverage report' - - - job: unit_tests_on_other_node_versions - displayName: Run unit tests on other Node.js versions - pool: - vmImage: 'Ubuntu-18.04' - strategy: - maxParallel: 3 - matrix: - node_10_x: - node_version: 10.x - node_8_x: - node_version: 8.x - steps: - - task: NodeTool@0 - inputs: - versionSpec: $(node_version) - displayName: 'Install Node.js $(node_version)' - - - script: | - # This also runs a build as part of the postinstall - # bootstrap - yarn --ignore-engines --frozen-lockfile - yarn check-clean-workspace-after-install - - - script: | - yarn test - displayName: 'Run unit tests' - - - job: publish_canary_version - displayName: Publish the latest code as a canary version - dependsOn: - - primary_code_validation_and_tests - - unit_tests_on_other_node_versions - condition: and(succeeded(), eq(variables['Build.SourceBranchName'], 'master'), ne(variables['Build.Reason'], 'PullRequest')) - pool: - vmImage: 'Ubuntu-18.04' - steps: - - task: NodeTool@0 - inputs: - versionSpec: 12 - displayName: 'Install Node.js 12' - - - script: | - # This also runs a build as part of the postinstall - # bootstrap - yarn --ignore-engines --frozen-lockfile - yarn check-clean-workspace-after-install - - - script: | - npm config set //registry.npmjs.org/:_authToken=$(NPM_TOKEN) - - - script: | - npx lerna publish --canary --exact --force-publish --yes - displayName: 'Publish all packages to npm' From 4c7270b58ad065a96a9104c0e863dedc2c80ac75 Mon Sep 17 00:00:00 2001 From: James Henry Date: Mon, 16 Mar 2020 19:19:31 +0000 Subject: [PATCH 12/26] chore: fix config for canary releases --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fb2e31ef8ea..e4d4077d14a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -139,8 +139,8 @@ jobs: publish_canary_version: name: Publish the latest code as a canary version runs-on: ubuntu-latest - needs: [primary_code_validation_and_tests, unit_tests_on_other_node_versions] - if: github.event_name == 'push' && github.ref == 'refs/head/master' + needs: [primary_code_validation_and_tests, unit_tests_on_other_node_versions, integration_tests] + if: github.ref == 'refs/head/master' steps: - uses: actions/checkout@v1 - name: Use Node.js ${{ env.PRIMARY_NODE_VERSION }} From 713e9d3b439e7dfaf8bc83055f7f4ea9c1a779e4 Mon Sep 17 00:00:00 2001 From: James Henry Date: Mon, 16 Mar 2020 19:36:39 +0000 Subject: [PATCH 13/26] chore: debug publish_canary_version --- .github/workflows/ci.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e4d4077d14a..2ad7cf351e5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,6 +17,10 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 + + - name: echo github.ref + run: echo ${{ github.ref }} + - name: Use Node.js ${{ env.PRIMARY_NODE_VERSION }} uses: actions/setup-node@v1 with: @@ -140,9 +144,9 @@ jobs: name: Publish the latest code as a canary version runs-on: ubuntu-latest needs: [primary_code_validation_and_tests, unit_tests_on_other_node_versions, integration_tests] - if: github.ref == 'refs/head/master' + # if: github.ref == 'refs/head/master' steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v2 - name: Use Node.js ${{ env.PRIMARY_NODE_VERSION }} uses: actions/setup-node@v1 with: From 3814d4e3b3c1552c7601b5d722b2a37c5a570841 Mon Sep 17 00:00:00 2001 From: James Henry Date: Mon, 16 Mar 2020 19:48:42 +0000 Subject: [PATCH 14/26] fix: only run publish_canary_version on master --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2ad7cf351e5..8af2a8f2823 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -144,7 +144,7 @@ jobs: name: Publish the latest code as a canary version runs-on: ubuntu-latest needs: [primary_code_validation_and_tests, unit_tests_on_other_node_versions, integration_tests] - # if: github.ref == 'refs/head/master' + if: github.ref == 'refs/heads/master' steps: - uses: actions/checkout@v2 - name: Use Node.js ${{ env.PRIMARY_NODE_VERSION }} From abf1a2fa5574e41af8070be3d79a886ea2f989cc Mon Sep 17 00:00:00 2001 From: Konstantin Pelepelin Date: Mon, 16 Mar 2020 23:06:32 +0300 Subject: [PATCH 15/26] fix(eslint-plugin-tslint): fix tslintConfig memoization key (#1719) Fixes typescript-eslint#1692 --- packages/eslint-plugin-tslint/src/rules/config.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/eslint-plugin-tslint/src/rules/config.ts b/packages/eslint-plugin-tslint/src/rules/config.ts index fcfaa7e2b8f..32198d0fc8c 100644 --- a/packages/eslint-plugin-tslint/src/rules/config.ts +++ b/packages/eslint-plugin-tslint/src/rules/config.ts @@ -48,10 +48,12 @@ const tslintConfig = memoize( rulesDirectory: tslintRulesDirectory ?? [], }); }, - (lintFile: string | undefined, tslintRules = {}, tslintRulesDirectory = []) => - `${lintFile}_${Object.keys(tslintRules).join(',')}_${ - tslintRulesDirectory.length - }`, + ( + lintFile: string | undefined, + tslintRules = {}, + tslintRulesDirectory: string[] = [], + ) => + `${lintFile}_${JSON.stringify(tslintRules)}_${tslintRulesDirectory.join()}`, ); export default createRule({ From d6e273d9e72abbcccba0e6a725520ec0849b27b4 Mon Sep 17 00:00:00 2001 From: Brad Zacher Date: Tue, 17 Mar 2020 15:19:57 -0700 Subject: [PATCH 16/26] chore: standardise issue templates (#1760) --- .../ISSUE_TEMPLATE/eslint-plugin-tslint.md | 55 +++++++++++++++++-- .../eslint-plugin-typescript.md | 7 ++- .../typescript-eslint-parser.md | 47 ++++++++++++++-- .../ISSUE_TEMPLATE/typescript-eslint-utils.md | 49 ++++++++++++++++- .github/ISSUE_TEMPLATE/typescript-estree.md | 51 +++++++++++++++-- 5 files changed, 189 insertions(+), 20 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/eslint-plugin-tslint.md b/.github/ISSUE_TEMPLATE/eslint-plugin-tslint.md index 68a68b98575..b94f19fb5b8 100644 --- a/.github/ISSUE_TEMPLATE/eslint-plugin-tslint.md +++ b/.github/ISSUE_TEMPLATE/eslint-plugin-tslint.md @@ -6,15 +6,60 @@ labels: 'package: eslint-plugin-tslint, triage' assignees: '' --- -**What code were you trying to parse?** + + + + +**Repro** + + + +```JSON +{ + "rules": { + "@typescript-eslint/tslint/config": ["warn", { + "rules": { + "rule": "setting", + }, + "rulesDirectory": [ + "node_modules/foo" + ] + }], + } +} +``` + + + +```TS +// your repro code case ``` -**What did you expect to happen?** +**Expected Result** + +**Actual Result** + +**Additional Info** + + **Versions** diff --git a/.github/ISSUE_TEMPLATE/eslint-plugin-typescript.md b/.github/ISSUE_TEMPLATE/eslint-plugin-typescript.md index ad7d8fb4944..3c0fb548f13 100644 --- a/.github/ISSUE_TEMPLATE/eslint-plugin-typescript.md +++ b/.github/ISSUE_TEMPLATE/eslint-plugin-typescript.md @@ -1,7 +1,7 @@ --- name: '@typescript-eslint/eslint-plugin' about: Report an issue with the '@typescript-eslint/eslint-plugin' package -title: '[rulename] ' +title: '[rulename] issue title' labels: 'package: eslint-plugin, triage' assignees: '' --- @@ -26,6 +26,11 @@ Are you opening an issue because the rule you're trying to use is not found? 3) If ESLint still can't find the rule, then consider reporting an issue. --> + + **Repro** + + + +**Repro** + + + +```JSON +{ + "rules": { + "@typescript-eslint/": [""] + }, + "parserOptions": { + "...": "something" + } +} ``` -**What did you expect to happen?** +```TS +// your repro code case +``` + +**Expected Result** + +**Actual Result** + +**Additional Info** + + **Versions** diff --git a/.github/ISSUE_TEMPLATE/typescript-eslint-utils.md b/.github/ISSUE_TEMPLATE/typescript-eslint-utils.md index 8403568e4ac..5b365a85ea8 100644 --- a/.github/ISSUE_TEMPLATE/typescript-eslint-utils.md +++ b/.github/ISSUE_TEMPLATE/typescript-eslint-utils.md @@ -1,14 +1,57 @@ --- name: '@typescript-eslint/experimental-utils' -about: Report an issue with the `@typescript-eslint/experimental-utils` package +about: Report an issue with the '@typescript-eslint/experimental-utils' package title: '' labels: 'package: utils, triage' assignees: '' --- -**What did you expect to happen?** + + + + +**Repro** + + + +```JSON +{ + "rules": { + "@typescript-eslint/": [""] + }, + "parserOptions": { + "...": "something" + } +} +``` + +```TS +// your repro code case +``` + +**Expected Result** + +**Actual Result** + +**Additional Info** + + **Versions** diff --git a/.github/ISSUE_TEMPLATE/typescript-estree.md b/.github/ISSUE_TEMPLATE/typescript-estree.md index fc8cca3d9a3..21f4c800d54 100644 --- a/.github/ISSUE_TEMPLATE/typescript-estree.md +++ b/.github/ISSUE_TEMPLATE/typescript-estree.md @@ -1,20 +1,59 @@ --- name: '@typescript-eslint/typescript-estree' -about: Report an issue with the `@typescript-eslint/typescript-estree` package +about: Report an issue with the '@typescript-eslint/typescript-estree' package title: '' labels: 'package: typescript-estree, triage' assignees: '' --- -**What code were you trying to parse?** + + + + +**Repro** + + + +```JSON +{ + "rules": { + "@typescript-eslint/": [""] + }, + "parserOptions": { + "...": "something" + } +} ``` -**What did you expect to happen?** +```TS +// your repro code case +``` + +**Expected Result** + +**Actual Result** + +**Additional Info** + + **Versions** From 61a779c1828ca8347a74d11e44e2ef94206bac9e Mon Sep 17 00:00:00 2001 From: James Henry Date: Wed, 18 Mar 2020 17:25:01 +0000 Subject: [PATCH 17/26] chore: try fetching all history in canary job --- .github/workflows/ci.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8af2a8f2823..cb32daf3501 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -147,6 +147,12 @@ jobs: if: github.ref == 'refs/heads/master' steps: - uses: actions/checkout@v2 + with: + fetch-depth: 0 # fetch all history for this job, because lerna needs it + - run: | + git status + git log -n 2 + - name: Use Node.js ${{ env.PRIMARY_NODE_VERSION }} uses: actions/setup-node@v1 with: From 19cc9a936e8c0e318606f3ebb92e0170d775490f Mon Sep 17 00:00:00 2001 From: James Henry Date: Wed, 18 Mar 2020 17:47:33 +0000 Subject: [PATCH 18/26] chore: try fetching all tags and history in canary job --- .github/workflows/ci.yml | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cb32daf3501..5c7945fd179 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -147,10 +147,16 @@ jobs: if: github.ref == 'refs/heads/master' steps: - uses: actions/checkout@v2 - with: - fetch-depth: 0 # fetch all history for this job, because lerna needs it + # Fetch all history for all tags and branches in this job because lerna needs it - run: | + git fetch --prune --unshallow + echo "fetched" + git status + echo "end status 1" + git checkout master git status + echo "end status 2" + echo "git log:" git log -n 2 - name: Use Node.js ${{ env.PRIMARY_NODE_VERSION }} @@ -178,6 +184,6 @@ jobs: yarn check-clean-workspace-after-install - name: Publish all packages to npm - run: npx lerna publish --canary --exact --force-publish --yes + run: npx lerna publish --log-level=verbose --canary --exact --force-publish --yes env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} From 52b061e44d1148b04b64acc00b477089d3be4267 Mon Sep 17 00:00:00 2001 From: James Henry Date: Wed, 18 Mar 2020 18:04:23 +0000 Subject: [PATCH 19/26] chore: try fetching all tags and history in canary job --- .github/workflows/ci.yml | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5c7945fd179..933b4895837 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -150,14 +150,6 @@ jobs: # Fetch all history for all tags and branches in this job because lerna needs it - run: | git fetch --prune --unshallow - echo "fetched" - git status - echo "end status 1" - git checkout master - git status - echo "end status 2" - echo "git log:" - git log -n 2 - name: Use Node.js ${{ env.PRIMARY_NODE_VERSION }} uses: actions/setup-node@v1 @@ -184,6 +176,6 @@ jobs: yarn check-clean-workspace-after-install - name: Publish all packages to npm - run: npx lerna publish --log-level=verbose --canary --exact --force-publish --yes + run: npx lerna publish --loglevel=verbose --canary --exact --force-publish --yes env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} From 09d8afca684635b5ac604bc1794240484a70ce91 Mon Sep 17 00:00:00 2001 From: Brad Zacher Date: Wed, 18 Mar 2020 12:06:32 -0700 Subject: [PATCH 20/26] fix(typescript-estree): export * regression from 133f622f (#1751) --- packages/typescript-estree/src/convert.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/typescript-estree/src/convert.ts b/packages/typescript-estree/src/convert.ts index 74c701b7fc4..c622e11c14f 100644 --- a/packages/typescript-estree/src/convert.ts +++ b/packages/typescript-estree/src/convert.ts @@ -1602,7 +1602,12 @@ export class Converter { source: this.convertChild(node.moduleSpecifier), exportKind: node.isTypeOnly ? 'type' : 'value', exported: - node.exportClause?.kind === SyntaxKind.NamespaceExport + // note - for compat with 3.7.x, where node.exportClause is always undefined and + // SyntaxKind.NamespaceExport does not exist yet (i.e. is undefined), this + // cannot be shortened to an optional chain, or else you end up with + // undefined === undefined, and the true path will hard error at runtime + node.exportClause && + node.exportClause.kind === SyntaxKind.NamespaceExport ? this.convertChild(node.exportClause.name) : null, }); From f76a1b3e63afda9f239e46f4ad5b36c1d7a6e8da Mon Sep 17 00:00:00 2001 From: Alexander T Date: Wed, 18 Mar 2020 21:06:57 +0200 Subject: [PATCH 21/26] feat(eslint-plugin): [no-unnec-type-assertion] allow const assertions (#1741) --- .../rules/no-unnecessary-type-assertion.md | 4 +++ .../rules/no-unnecessary-type-assertion.ts | 16 ++++++++-- .../no-unnecessary-type-assertion.test.ts | 32 +++++++++++++++++++ 3 files changed, 50 insertions(+), 2 deletions(-) diff --git a/packages/eslint-plugin/docs/rules/no-unnecessary-type-assertion.md b/packages/eslint-plugin/docs/rules/no-unnecessary-type-assertion.md index 14819c56bf7..4464b6483ef 100644 --- a/packages/eslint-plugin/docs/rules/no-unnecessary-type-assertion.md +++ b/packages/eslint-plugin/docs/rules/no-unnecessary-type-assertion.md @@ -43,6 +43,10 @@ const foo = 3; const foo = 3 as number; ``` +```ts +const foo = 'foo' as const; +``` + ```ts function foo(x: number | undefined): number { return x!; diff --git a/packages/eslint-plugin/src/rules/no-unnecessary-type-assertion.ts b/packages/eslint-plugin/src/rules/no-unnecessary-type-assertion.ts index dd4395b290b..4fe9f9a59d2 100644 --- a/packages/eslint-plugin/src/rules/no-unnecessary-type-assertion.ts +++ b/packages/eslint-plugin/src/rules/no-unnecessary-type-assertion.ts @@ -1,4 +1,7 @@ -import { TSESTree } from '@typescript-eslint/experimental-utils'; +import { + TSESTree, + AST_NODE_TYPES, +} from '@typescript-eslint/experimental-utils'; import { isObjectType, isObjectFlagSet, @@ -122,6 +125,14 @@ export default util.createRule({ return false; } + function isConstAssertion(node: TSESTree.TypeNode): boolean { + return ( + node.type === AST_NODE_TYPES.TSTypeReference && + node.typeName.type === AST_NODE_TYPES.Identifier && + node.typeName.name === 'const' + ); + } + return { TSNonNullExpression(node): void { const originalNode = parserServices.esTreeNodeToTSNodeMap.get(node); @@ -201,7 +212,8 @@ export default util.createRule({ if ( options.typesToIgnore?.includes( sourceCode.getText(node.typeAnnotation), - ) + ) || + isConstAssertion(node.typeAnnotation) ) { return; } 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 e7248a9693e..4d1ae880e84 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 @@ -130,6 +130,38 @@ function Test(props: { `, filename: 'react.tsx', }, + { + code: ` +const a = [1, 2]; +const b = [3, 4]; +const c = [...a, ...b] as const; + `, + }, + { + code: `const a = [1, 2] as const;`, + }, + { + code: `const a = 'a' as const;`, + }, + { + code: `const a = { foo: 'foo' } as const`, + }, + { + code: ` +const a = [1, 2]; +const b = [3, 4]; +const c = [...a, ...b]; + `, + }, + { + code: `const a = [1, 2];`, + }, + { + code: `const a = 'a';`, + }, + { + code: `const a = { foo: 'foo' };`, + }, ], invalid: [ From 6646959b255b08afe5175bba6621bad11b9e1d5e Mon Sep 17 00:00:00 2001 From: Susisu Date: Thu, 19 Mar 2020 04:18:46 +0900 Subject: [PATCH 22/26] fix(eslint-plugin): fix message of no-base-to-string (#1755) --- packages/eslint-plugin/src/rules/no-base-to-string.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/eslint-plugin/src/rules/no-base-to-string.ts b/packages/eslint-plugin/src/rules/no-base-to-string.ts index fa404272895..98a37c74084 100644 --- a/packages/eslint-plugin/src/rules/no-base-to-string.ts +++ b/packages/eslint-plugin/src/rules/no-base-to-string.ts @@ -24,7 +24,7 @@ export default util.createRule({ }, messages: { baseToString: - "'{{name}} {{certainty}} evaluate to '[Object object]' when stringified.", + "'{{name}} {{certainty}} evaluate to '[object Object]' when stringified.", }, schema: [], type: 'suggestion', From 199863d35cb36bdb7178b8116d146258506644c7 Mon Sep 17 00:00:00 2001 From: Regev Brody Date: Thu, 19 Mar 2020 03:10:10 +0200 Subject: [PATCH 23/26] fix(eslint-plugin): [quotes] false positive with backtick in import equals statement (#1769) --- packages/eslint-plugin/src/rules/quotes.ts | 1 + packages/eslint-plugin/tests/rules/quotes.test.ts | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/eslint-plugin/src/rules/quotes.ts b/packages/eslint-plugin/src/rules/quotes.ts index a266e912fdf..5d4d44028b1 100644 --- a/packages/eslint-plugin/src/rules/quotes.ts +++ b/packages/eslint-plugin/src/rules/quotes.ts @@ -42,6 +42,7 @@ export default util.createRule({ case AST_NODE_TYPES.TSPropertySignature: case AST_NODE_TYPES.TSModuleDeclaration: case AST_NODE_TYPES.TSLiteralType: + case AST_NODE_TYPES.TSExternalModuleReference: return true; case AST_NODE_TYPES.TSEnumMember: diff --git a/packages/eslint-plugin/tests/rules/quotes.test.ts b/packages/eslint-plugin/tests/rules/quotes.test.ts index c6bf01f3e77..ad1487b8506 100644 --- a/packages/eslint-plugin/tests/rules/quotes.test.ts +++ b/packages/eslint-plugin/tests/rules/quotes.test.ts @@ -310,7 +310,11 @@ ruleTester.run('quotes', rule, { code: `export * from "a"; export * from 'b';`, options: ['backtick'], }, - + // `backtick` should not warn import with require. + { + code: `import moment = require('moment');`, + options: ['backtick'], + }, // `backtick` should not warn property/method names (not computed). { code: `var obj = {"key0": 0, 'key1': 1};`, From c82d1216ece92e7f099971dd6d669f277314dcc0 Mon Sep 17 00:00:00 2001 From: Maxim Mazurok Date: Thu, 19 Mar 2020 16:40:09 +1100 Subject: [PATCH 24/26] chore(typescript-estree): remove unfinished comment (#1770) First introduced in https://github.com/typescript-eslint/typescript-eslint/commit/25092fdc5bd9d1bd90cf3cb8d37e420b6c2efef3#diff-77692ea88ea59620ee45bd60f573b4bcR150 --- packages/typescript-estree/src/parser-options.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/typescript-estree/src/parser-options.ts b/packages/typescript-estree/src/parser-options.ts index 2317a683abf..73ea6ba3c04 100644 --- a/packages/typescript-estree/src/parser-options.ts +++ b/packages/typescript-estree/src/parser-options.ts @@ -146,8 +146,6 @@ export interface TSESTreeOptions { * When passed with `project`, this allows the parser to create a catch-all, default program. * This means that if the parser encounters a file not included in any of the provided `project`s, * it will not error, but will instead parse the file and its dependencies in a new program. - * - * This */ createDefaultProgram?: boolean; } From 2b9603d868c57556d8cd6087685e798d74cb6f26 Mon Sep 17 00:00:00 2001 From: Retsam Date: Thu, 19 Mar 2020 23:47:48 -0400 Subject: [PATCH 25/26] feat(eslint-plugin): [no-unnecessary-condition] ignore basic array indexing false positives (#1534) --- .../src/rules/no-unnecessary-condition.ts | 69 ++++++++++++++++++- .../rules/no-unnecessary-condition.test.ts | 64 +++++++++++++++++ 2 files changed, 131 insertions(+), 2 deletions(-) diff --git a/packages/eslint-plugin/src/rules/no-unnecessary-condition.ts b/packages/eslint-plugin/src/rules/no-unnecessary-condition.ts index 45af1e80706..07bb3d56c5c 100644 --- a/packages/eslint-plugin/src/rules/no-unnecessary-condition.ts +++ b/packages/eslint-plugin/src/rules/no-unnecessary-condition.ts @@ -143,7 +143,25 @@ export default createRule({ function nodeIsArrayType(node: TSESTree.Expression): boolean { const nodeType = getNodeType(node); - return checker.isArrayType(nodeType) || checker.isTupleType(nodeType); + return checker.isArrayType(nodeType); + } + function nodeIsTupleType(node: TSESTree.Expression): boolean { + const nodeType = getNodeType(node); + return checker.isTupleType(nodeType); + } + + function isArrayIndexExpression(node: TSESTree.Expression): boolean { + return ( + // Is an index signature + node.type === AST_NODE_TYPES.MemberExpression && + node.computed && + // ...into an array type + (nodeIsArrayType(node.object) || + // ... or a tuple type + (nodeIsTupleType(node.object) && + // Exception: literal index into a tuple - will have a sound type + node.property.type !== AST_NODE_TYPES.Literal)) + ); } /** @@ -151,6 +169,13 @@ export default createRule({ * if the type of the node is always true or always false, it's not necessary. */ function checkNode(node: TSESTree.Expression): void { + // Since typescript array index signature types don't represent the + // possibility of out-of-bounds access, if we're indexing into an array + // just skip the check, to avoid false positives + if (isArrayIndexExpression(node)) { + return; + } + const type = getNodeType(node); // Conditional is always necessary if it involves: @@ -181,6 +206,12 @@ export default createRule({ } function checkNodeForNullish(node: TSESTree.Expression): void { + // Since typescript array index signature types don't represent the + // possibility of out-of-bounds access, if we're indexing into an array + // just skip the check, to avoid false positives + if (isArrayIndexExpression(node)) { + return; + } const type = getNodeType(node); // Conditional is always necessary if it involves `any` or `unknown` if (isTypeFlagSet(type, ts.TypeFlags.Any | ts.TypeFlags.Unknown)) { @@ -306,7 +337,7 @@ export default createRule({ callee.property.type === AST_NODE_TYPES.Identifier && ARRAY_PREDICATE_FUNCTIONS.has(callee.property.name) && // and the left-hand side is an array, according to the types - nodeIsArrayType(callee.object) + (nodeIsArrayType(callee.object) || nodeIsTupleType(callee.object)) ); } function checkCallExpression(node: TSESTree.CallExpression): void { @@ -361,6 +392,33 @@ export default createRule({ } } + // Recursively searches an optional chain for an array index expression + // Has to search the entire chain, because an array index will "infect" the rest of the types + // Example: + // ``` + // [{x: {y: "z"} }][n] // type is {x: {y: "z"}} + // ?.x // type is {y: "z"} + // ?.y // This access is considered "unnecessary" according to the types + // ``` + function optionChainContainsArrayIndex( + node: TSESTree.OptionalMemberExpression | TSESTree.OptionalCallExpression, + ): boolean { + const lhsNode = + node.type === AST_NODE_TYPES.OptionalCallExpression + ? node.callee + : node.object; + if (isArrayIndexExpression(lhsNode)) { + return true; + } + if ( + lhsNode.type === AST_NODE_TYPES.OptionalMemberExpression || + lhsNode.type === AST_NODE_TYPES.OptionalCallExpression + ) { + return optionChainContainsArrayIndex(lhsNode); + } + return false; + } + function checkOptionalChain( node: TSESTree.OptionalMemberExpression | TSESTree.OptionalCallExpression, beforeOperator: TSESTree.Node, @@ -372,6 +430,13 @@ export default createRule({ return; } + // Since typescript array index signature types don't represent the + // possibility of out-of-bounds access, if we're indexing into an array + // just skip the check, to avoid false positives + if (optionChainContainsArrayIndex(node)) { + return; + } + const type = getNodeType(node); if ( isTypeFlagSet(type, ts.TypeFlags.Any) || diff --git a/packages/eslint-plugin/tests/rules/no-unnecessary-condition.test.ts b/packages/eslint-plugin/tests/rules/no-unnecessary-condition.test.ts index 5af22f00ca0..f3adbc3a849 100644 --- a/packages/eslint-plugin/tests/rules/no-unnecessary-condition.test.ts +++ b/packages/eslint-plugin/tests/rules/no-unnecessary-condition.test.ts @@ -150,6 +150,39 @@ function test(a: string | null | undefined) { function test(a: unknown) { return a ?? "default"; }`, + // Indexing cases + ` +declare const arr: object[]; +if(arr[42]) {} // looks unnecessary from the types, but isn't + +const tuple = [{}] as [object]; +declare const n: number; +if(tuple[n]) {} +`, + // Optional-chaining indexing + ` +declare const arr: Array<{value: string} & (() => void)>; +if(arr[42]?.value) {} +arr[41]?.(); + +// An array access can "infect" deeper into the chain +declare const arr2: Array<{x: {y: {z: object}}}>; +arr2[42]?.x?.y?.z; + +const tuple = ["foo"] as const; +declare const n: number; +tuple[n]?.toUpperCase(); + `, + `if(arr?.[42]) {}`, + ` +declare const returnsArr: undefined | (() => string[]); +if(returnsArr?.()[42]) {} +returnsArr?.()[42]?.toUpperCase()`, + // nullish + array index + ` +declare const arr: string[][]; +arr[x] ?? []; +`, // Supports ignoring the RHS { code: ` @@ -363,6 +396,37 @@ function nothing3(x: [string, string]) { ruleError(15, 25, 'alwaysFalsy'), ], }, + // Indexing cases + { + // This is an error because 'dict' doesn't represent + // the potential for undefined in its types + code: ` +declare const dict: Record; +if(dict["mightNotExist"]) {} +`, + errors: [ruleError(3, 4, 'alwaysTruthy')], + }, + { + // Should still check tuples when accessed with literal numbers, since they don't have + // unsound index signatures + code: ` +const x = [{}] as [{foo: string}]; +if(x[0]) {} +if(x[0]?.foo) {} +`, + errors: [ + ruleError(3, 4, 'alwaysTruthy'), + ruleError(4, 8, 'neverOptionalChain'), + ], + }, + { + // Shouldn't mistake this for an array indexing case + code: ` +declare const arr: object[]; +if(arr.filter) {} +`, + errors: [ruleError(3, 4, 'alwaysTruthy')], + }, { options: [{ checkArrayPredicates: true }], code: ` From 3eb5d4525e95c8ab990f55588b8d830a02ce5a9c Mon Sep 17 00:00:00 2001 From: Brad Zacher Date: Thu, 19 Mar 2020 20:50:29 -0700 Subject: [PATCH 26/26] feat(experimental-utils): expose ast utility functions (#1670) --- .cspell.json | 3 +- packages/eslint-plugin/package.json | 1 - .../eslint-plugin/src/rules/comma-spacing.ts | 8 +- .../src/rules/func-call-spacing.ts | 5 +- .../src/rules/indent-new-do-not-use/index.ts | 13 +- .../src/rules/no-array-constructor.ts | 2 +- .../src/rules/prefer-includes.ts | 3 +- .../src/rules/prefer-optional-chain.ts | 3 +- .../src/rules/prefer-regexp-exec.ts | 8 +- .../rules/prefer-string-starts-ends-with.ts | 8 +- .../eslint-plugin/src/rules/require-await.ts | 13 +- .../src/rules/space-before-function-paren.ts | 5 +- .../src/rules/switch-exhaustiveness-check.ts | 5 +- packages/eslint-plugin/src/util/astUtils.ts | 267 +----------------- packages/eslint-plugin/src/util/misc.ts | 5 +- .../eslint-plugin/typings/eslint-utils.d.ts | 180 ------------ packages/experimental-utils/README.md | 23 +- packages/experimental-utils/package.json | 3 +- .../ast-utils/eslint-utils/PatternMatcher.ts | 56 ++++ .../eslint-utils/ReferenceTracker.ts | 94 ++++++ .../ast-utils/eslint-utils/astUtilities.ts | 127 +++++++++ .../src/ast-utils/eslint-utils/index.ts | 5 + .../src/ast-utils/eslint-utils/predicates.ts | 106 +++++++ .../ast-utils/eslint-utils/scopeAnalysis.ts | 27 ++ .../experimental-utils/src/ast-utils/index.ts | 3 + .../experimental-utils/src/ast-utils/misc.ts | 15 + .../src/ast-utils/predicates.ts | 247 ++++++++++++++++ .../src/eslint-utils/RuleCreator.ts | 4 +- .../src/eslint-utils/applyDefault.ts | 4 +- .../src/eslint-utils/deepMerge.ts | 4 +- .../src/eslint-utils/getParserServices.ts | 7 +- packages/experimental-utils/src/index.ts | 19 +- .../src/ts-eslint-scope/Definition.ts | 2 +- .../src/ts-eslint-scope/Options.ts | 2 +- .../src/ts-eslint-scope/PatternVisitor.ts | 2 +- .../src/ts-eslint-scope/Reference.ts | 2 +- .../src/ts-eslint-scope/Referencer.ts | 2 +- .../src/ts-eslint-scope/Scope.ts | 2 +- .../src/ts-eslint-scope/ScopeManager.ts | 2 +- .../src/ts-eslint-scope/Variable.ts | 2 +- .../experimental-utils/src/ts-eslint/AST.ts | 5 +- .../src/ts-eslint/Linter.ts | 2 +- .../src/ts-eslint/ParserOptions.ts | 4 +- .../experimental-utils/src/ts-eslint/Rule.ts | 2 +- .../src/ts-eslint/RuleTester.ts | 5 +- .../experimental-utils/src/ts-eslint/Scope.ts | 2 +- .../src/ts-eslint/SourceCode.ts | 4 +- packages/experimental-utils/src/ts-estree.ts | 12 + .../typings/eslint-utils.d.ts | 40 +++ yarn.lock | 7 + 50 files changed, 835 insertions(+), 537 deletions(-) delete mode 100644 packages/eslint-plugin/typings/eslint-utils.d.ts create mode 100644 packages/experimental-utils/src/ast-utils/eslint-utils/PatternMatcher.ts create mode 100644 packages/experimental-utils/src/ast-utils/eslint-utils/ReferenceTracker.ts create mode 100644 packages/experimental-utils/src/ast-utils/eslint-utils/astUtilities.ts create mode 100644 packages/experimental-utils/src/ast-utils/eslint-utils/index.ts create mode 100644 packages/experimental-utils/src/ast-utils/eslint-utils/predicates.ts create mode 100644 packages/experimental-utils/src/ast-utils/eslint-utils/scopeAnalysis.ts create mode 100644 packages/experimental-utils/src/ast-utils/index.ts create mode 100644 packages/experimental-utils/src/ast-utils/misc.ts create mode 100644 packages/experimental-utils/src/ast-utils/predicates.ts create mode 100644 packages/experimental-utils/src/ts-estree.ts create mode 100644 packages/experimental-utils/typings/eslint-utils.d.ts diff --git a/.cspell.json b/.cspell.json index 33f7b70c5f2..c65ff294c24 100644 --- a/.cspell.json +++ b/.cspell.json @@ -10,7 +10,8 @@ "**/**/CONTRIBUTORS.md", "**/**/ROADMAP.md", "**/*.{json,snap}", - ".cspell.json" + ".cspell.json", + "yarn.lock" ], "dictionaries": [ "typescript", diff --git a/packages/eslint-plugin/package.json b/packages/eslint-plugin/package.json index 1083d0b5aa8..4855beab6a2 100644 --- a/packages/eslint-plugin/package.json +++ b/packages/eslint-plugin/package.json @@ -42,7 +42,6 @@ }, "dependencies": { "@typescript-eslint/experimental-utils": "2.24.0", - "eslint-utils": "^1.4.3", "functional-red-black-tree": "^1.0.1", "regexpp": "^3.0.0", "tsutils": "^3.17.1" diff --git a/packages/eslint-plugin/src/rules/comma-spacing.ts b/packages/eslint-plugin/src/rules/comma-spacing.ts index d8ab4b42b8d..665eeffd029 100644 --- a/packages/eslint-plugin/src/rules/comma-spacing.ts +++ b/packages/eslint-plugin/src/rules/comma-spacing.ts @@ -2,8 +2,12 @@ import { TSESTree, AST_TOKEN_TYPES, } from '@typescript-eslint/experimental-utils'; -import { isClosingParenToken, isCommaToken } from 'eslint-utils'; -import { isTokenOnSameLine, createRule } from '../util'; +import { + isClosingParenToken, + isCommaToken, + isTokenOnSameLine, + createRule, +} from '../util'; type Options = [ { diff --git a/packages/eslint-plugin/src/rules/func-call-spacing.ts b/packages/eslint-plugin/src/rules/func-call-spacing.ts index 8e11d58592b..53858ad18ab 100644 --- a/packages/eslint-plugin/src/rules/func-call-spacing.ts +++ b/packages/eslint-plugin/src/rules/func-call-spacing.ts @@ -1,5 +1,4 @@ import { TSESTree } from '@typescript-eslint/experimental-utils'; -import { isOpeningParenToken } from 'eslint-utils'; import * as util from '../util'; export type Options = [ @@ -79,7 +78,7 @@ export default util.createRule({ | TSESTree.OptionalCallExpression | TSESTree.NewExpression, ): void { - const isOptionalCall = util.isOptionalOptionalChain(node); + const isOptionalCall = util.isOptionalOptionalCallExpression(node); const closingParenToken = sourceCode.getLastToken(node)!; const lastCalleeTokenWithoutPossibleParens = sourceCode.getLastToken( @@ -88,7 +87,7 @@ export default util.createRule({ const openingParenToken = sourceCode.getFirstTokenBetween( lastCalleeTokenWithoutPossibleParens, closingParenToken, - isOpeningParenToken, + util.isOpeningParenToken, ); if (!openingParenToken || openingParenToken.range[1] >= node.range[1]) { // new expression with no parens... diff --git a/packages/eslint-plugin/src/rules/indent-new-do-not-use/index.ts b/packages/eslint-plugin/src/rules/indent-new-do-not-use/index.ts index 923423dbc14..3c24f29ad9e 100644 --- a/packages/eslint-plugin/src/rules/indent-new-do-not-use/index.ts +++ b/packages/eslint-plugin/src/rules/indent-new-do-not-use/index.ts @@ -8,7 +8,13 @@ import { TSESLint, TSESTree, } from '@typescript-eslint/experimental-utils'; + +import { TokenOrComment } from './BinarySearchTree'; +import { OffsetStorage } from './OffsetStorage'; +import { TokenInfo } from './TokenInfo'; import { + createRule, + ExcludeKeys, isClosingBraceToken, isClosingBracketToken, isClosingParenToken, @@ -19,11 +25,8 @@ import { isOpeningBraceToken, isOpeningParenToken, isSemicolonToken, -} from 'eslint-utils'; -import { TokenOrComment } from './BinarySearchTree'; -import { OffsetStorage } from './OffsetStorage'; -import { TokenInfo } from './TokenInfo'; -import { createRule, ExcludeKeys, RequireKeys } from '../../util'; + RequireKeys, +} from '../../util'; const GLOBAL_LINEBREAK_REGEX = /\r\n|[\r\n\u2028\u2029]/gu; const WHITESPACE_REGEX = /\s*$/u; diff --git a/packages/eslint-plugin/src/rules/no-array-constructor.ts b/packages/eslint-plugin/src/rules/no-array-constructor.ts index dddae97f663..c9465e70799 100644 --- a/packages/eslint-plugin/src/rules/no-array-constructor.ts +++ b/packages/eslint-plugin/src/rules/no-array-constructor.ts @@ -37,7 +37,7 @@ export default util.createRule({ node.callee.type === AST_NODE_TYPES.Identifier && node.callee.name === 'Array' && !node.typeParameters && - !util.isOptionalOptionalChain(node) + !util.isOptionalOptionalCallExpression(node) ) { context.report({ node, diff --git a/packages/eslint-plugin/src/rules/prefer-includes.ts b/packages/eslint-plugin/src/rules/prefer-includes.ts index 0cd54d6c786..b31280b4d46 100644 --- a/packages/eslint-plugin/src/rules/prefer-includes.ts +++ b/packages/eslint-plugin/src/rules/prefer-includes.ts @@ -2,10 +2,9 @@ import { AST_NODE_TYPES, TSESTree, } from '@typescript-eslint/experimental-utils'; -import { getStaticValue } from 'eslint-utils'; import { AST as RegExpAST, parseRegExpLiteral } from 'regexpp'; import * as ts from 'typescript'; -import { createRule, getParserServices } from '../util'; +import { createRule, getParserServices, getStaticValue } from '../util'; export default createRule({ name: 'prefer-includes', diff --git a/packages/eslint-plugin/src/rules/prefer-optional-chain.ts b/packages/eslint-plugin/src/rules/prefer-optional-chain.ts index e0740c2e913..0ec33df8180 100644 --- a/packages/eslint-plugin/src/rules/prefer-optional-chain.ts +++ b/packages/eslint-plugin/src/rules/prefer-optional-chain.ts @@ -2,7 +2,6 @@ import { AST_NODE_TYPES, TSESTree, } from '@typescript-eslint/experimental-utils'; -import { isOpeningParenToken } from 'eslint-utils'; import * as util from '../util'; type ValidChainTarget = @@ -203,7 +202,7 @@ export default util.createRule({ sourceCode.getFirstTokenBetween( node.callee, closingParenToken, - isOpeningParenToken, + util.isOpeningParenToken, ), util.NullThrowsReasons.MissingToken('opening parenthesis', node.type), ); diff --git a/packages/eslint-plugin/src/rules/prefer-regexp-exec.ts b/packages/eslint-plugin/src/rules/prefer-regexp-exec.ts index 599e0f2a6da..dda6f15d3a5 100644 --- a/packages/eslint-plugin/src/rules/prefer-regexp-exec.ts +++ b/packages/eslint-plugin/src/rules/prefer-regexp-exec.ts @@ -1,6 +1,10 @@ import { TSESTree } from '@typescript-eslint/experimental-utils'; -import { getStaticValue } from 'eslint-utils'; -import { createRule, getParserServices, getTypeName } from '../util'; +import { + createRule, + getParserServices, + getStaticValue, + getTypeName, +} from '../util'; export default createRule({ name: 'prefer-regexp-exec', diff --git a/packages/eslint-plugin/src/rules/prefer-string-starts-ends-with.ts b/packages/eslint-plugin/src/rules/prefer-string-starts-ends-with.ts index 2b4946b2a14..217eada2e85 100644 --- a/packages/eslint-plugin/src/rules/prefer-string-starts-ends-with.ts +++ b/packages/eslint-plugin/src/rules/prefer-string-starts-ends-with.ts @@ -3,13 +3,15 @@ import { TSESLint, TSESTree, } from '@typescript-eslint/experimental-utils'; +import { AST as RegExpAST, RegExpParser } from 'regexpp'; import { + createRule, + getParserServices, getPropertyName, getStaticValue, + getTypeName, isNotClosingParenToken, -} from 'eslint-utils'; -import { AST as RegExpAST, RegExpParser } from 'regexpp'; -import { createRule, getParserServices, getTypeName } from '../util'; +} from '../util'; const EQ_OPERATORS = /^[=!]=/; const regexpp = new RegExpParser(); diff --git a/packages/eslint-plugin/src/rules/require-await.ts b/packages/eslint-plugin/src/rules/require-await.ts index 4688373dc38..e7b6a5f5959 100644 --- a/packages/eslint-plugin/src/rules/require-await.ts +++ b/packages/eslint-plugin/src/rules/require-await.ts @@ -3,11 +3,6 @@ import { TSESLint, TSESTree, } from '@typescript-eslint/experimental-utils'; -import { - isArrowToken, - getFunctionNameWithKind, - isOpeningParenToken, -} from 'eslint-utils'; import * as tsutils from 'tsutils'; import * as ts from 'typescript'; import * as util from '../util'; @@ -73,7 +68,7 @@ export default util.createRule({ loc: getFunctionHeadLoc(node, sourceCode), messageId: 'missingAwait', data: { - name: util.upperCaseFirst(getFunctionNameWithKind(node)), + name: util.upperCaseFirst(util.getFunctionNameWithKind(node)), }, }); } @@ -157,8 +152,8 @@ function getOpeningParenOfParams( ): TSESTree.Token { return util.nullThrows( node.id - ? sourceCode.getTokenAfter(node.id, isOpeningParenToken) - : sourceCode.getFirstToken(node, isOpeningParenToken), + ? sourceCode.getTokenAfter(node.id, util.isOpeningParenToken) + : sourceCode.getFirstToken(node, util.isOpeningParenToken), util.NullThrowsReasons.MissingToken('(', node.type), ); } @@ -180,7 +175,7 @@ function getFunctionHeadLoc( if (node.type === AST_NODE_TYPES.ArrowFunctionExpression) { const arrowToken = util.nullThrows( - sourceCode.getTokenBefore(node.body, isArrowToken), + sourceCode.getTokenBefore(node.body, util.isArrowToken), util.NullThrowsReasons.MissingToken('=>', node.type), ); diff --git a/packages/eslint-plugin/src/rules/space-before-function-paren.ts b/packages/eslint-plugin/src/rules/space-before-function-paren.ts index e4b4cb2f960..bc2ad8b0d04 100644 --- a/packages/eslint-plugin/src/rules/space-before-function-paren.ts +++ b/packages/eslint-plugin/src/rules/space-before-function-paren.ts @@ -2,7 +2,6 @@ import { AST_NODE_TYPES, TSESTree, } from '@typescript-eslint/experimental-utils'; -import { isOpeningParenToken } from 'eslint-utils'; import * as util from '../util'; type Option = 'never' | 'always'; @@ -106,7 +105,7 @@ export default util.createRule({ // Always ignore non-async functions and arrow functions without parens, e.g. async foo => bar if ( node.async && - isOpeningParenToken(sourceCode.getFirstToken(node, { skip: 1 })!) + util.isOpeningParenToken(sourceCode.getFirstToken(node, { skip: 1 })!) ) { return overrideConfig.asyncArrow ?? baseConfig; } @@ -143,7 +142,7 @@ export default util.createRule({ leftToken = sourceCode.getLastToken(node.typeParameters)!; rightToken = sourceCode.getTokenAfter(leftToken)!; } else { - rightToken = sourceCode.getFirstToken(node, isOpeningParenToken)!; + rightToken = sourceCode.getFirstToken(node, util.isOpeningParenToken)!; leftToken = sourceCode.getTokenBefore(rightToken)!; } const hasSpacing = sourceCode.isSpaceBetweenTokens(leftToken, rightToken); diff --git a/packages/eslint-plugin/src/rules/switch-exhaustiveness-check.ts b/packages/eslint-plugin/src/rules/switch-exhaustiveness-check.ts index 2cf394d2026..414dbbcdd03 100644 --- a/packages/eslint-plugin/src/rules/switch-exhaustiveness-check.ts +++ b/packages/eslint-plugin/src/rules/switch-exhaustiveness-check.ts @@ -2,11 +2,12 @@ import { TSESLint, TSESTree } from '@typescript-eslint/experimental-utils'; import * as ts from 'typescript'; import { createRule, - getParserServices, getConstrainedTypeAtLocation, + getParserServices, + isClosingBraceToken, + isOpeningBraceToken, } from '../util'; import { isTypeFlagSet, unionTypeParts } from 'tsutils'; -import { isClosingBraceToken, isOpeningBraceToken } from 'eslint-utils'; export default createRule({ name: 'switch-exhaustiveness-check', diff --git a/packages/eslint-plugin/src/util/astUtils.ts b/packages/eslint-plugin/src/util/astUtils.ts index 03f917e4ffb..12f003e98d0 100644 --- a/packages/eslint-plugin/src/util/astUtils.ts +++ b/packages/eslint-plugin/src/util/astUtils.ts @@ -1,265 +1,2 @@ -import { - AST_NODE_TYPES, - AST_TOKEN_TYPES, - TSESTree, -} from '@typescript-eslint/experimental-utils'; - -const LINEBREAK_MATCHER = /\r\n|[\r\n\u2028\u2029]/; - -function isOptionalChainPunctuator( - token: TSESTree.Token | TSESTree.Comment, -): token is TSESTree.PunctuatorToken & { value: '?.' } { - return token.type === AST_TOKEN_TYPES.Punctuator && token.value === '?.'; -} -function isNotOptionalChainPunctuator( - token: TSESTree.Token | TSESTree.Comment, -): boolean { - return !isOptionalChainPunctuator(token); -} - -function isNonNullAssertionPunctuator( - token: TSESTree.Token | TSESTree.Comment, -): token is TSESTree.PunctuatorToken & { value: '!' } { - return token.type === AST_TOKEN_TYPES.Punctuator && token.value === '!'; -} -function isNotNonNullAssertionPunctuator( - token: TSESTree.Token | TSESTree.Comment, -): boolean { - return !isNonNullAssertionPunctuator(token); -} - -/** - * Returns true if and only if the node represents: foo?.() or foo.bar?.() - */ -function isOptionalOptionalChain( - node: TSESTree.Node, -): node is TSESTree.OptionalCallExpression & { optional: true } { - return ( - node.type === AST_NODE_TYPES.OptionalCallExpression && - // this flag means the call expression itself is option - // i.e. it is foo.bar?.() and not foo?.bar() - node.optional - ); -} - -/** - * Returns true if and only if the node represents logical OR - */ -function isLogicalOrOperator( - node: TSESTree.Node, -): node is TSESTree.LogicalExpression & { operator: '||' } { - return ( - node.type === AST_NODE_TYPES.LogicalExpression && node.operator === '||' - ); -} - -/** - * Determines whether two adjacent tokens are on the same line - */ -function isTokenOnSameLine( - left: TSESTree.Token | TSESTree.Comment, - right: TSESTree.Token | TSESTree.Comment, -): boolean { - return left.loc.end.line === right.loc.start.line; -} - -/** - * Checks if a node is a type assertion: - * ``` - * x as foo - * x - * ``` - */ -function isTypeAssertion( - node: TSESTree.Node | undefined | null, -): node is TSESTree.TSAsExpression | TSESTree.TSTypeAssertion { - if (!node) { - return false; - } - return ( - node.type === AST_NODE_TYPES.TSAsExpression || - node.type === AST_NODE_TYPES.TSTypeAssertion - ); -} - -function isVariableDeclarator( - node: TSESTree.Node | undefined, -): node is TSESTree.VariableDeclarator { - return node?.type === AST_NODE_TYPES.VariableDeclarator; -} - -function isFunction( - node: TSESTree.Node | undefined, -): node is - | TSESTree.ArrowFunctionExpression - | TSESTree.FunctionDeclaration - | TSESTree.FunctionExpression { - if (!node) { - return false; - } - - return [ - AST_NODE_TYPES.ArrowFunctionExpression, - AST_NODE_TYPES.FunctionDeclaration, - AST_NODE_TYPES.FunctionExpression, - ].includes(node.type); -} - -function isFunctionType( - node: TSESTree.Node | undefined, -): node is - | TSESTree.TSCallSignatureDeclaration - | TSESTree.TSConstructorType - | TSESTree.TSConstructSignatureDeclaration - | TSESTree.TSEmptyBodyFunctionExpression - | TSESTree.TSFunctionType - | TSESTree.TSMethodSignature { - if (!node) { - return false; - } - - return [ - AST_NODE_TYPES.TSCallSignatureDeclaration, - AST_NODE_TYPES.TSConstructorType, - AST_NODE_TYPES.TSConstructSignatureDeclaration, - AST_NODE_TYPES.TSEmptyBodyFunctionExpression, - AST_NODE_TYPES.TSFunctionType, - AST_NODE_TYPES.TSMethodSignature, - ].includes(node.type); -} - -function isFunctionOrFunctionType( - node: TSESTree.Node | undefined, -): node is - | TSESTree.ArrowFunctionExpression - | TSESTree.FunctionDeclaration - | TSESTree.FunctionExpression - | TSESTree.TSCallSignatureDeclaration - | TSESTree.TSConstructorType - | TSESTree.TSConstructSignatureDeclaration - | TSESTree.TSEmptyBodyFunctionExpression - | TSESTree.TSFunctionType - | TSESTree.TSMethodSignature { - return isFunction(node) || isFunctionType(node); -} - -function isTSFunctionType( - node: TSESTree.Node | undefined, -): node is TSESTree.TSFunctionType { - return node?.type === AST_NODE_TYPES.TSFunctionType; -} - -function isTSConstructorType( - node: TSESTree.Node | undefined, -): node is TSESTree.TSConstructorType { - return node?.type === AST_NODE_TYPES.TSConstructorType; -} - -function isClassOrTypeElement( - node: TSESTree.Node | undefined, -): node is TSESTree.ClassElement | TSESTree.TypeElement { - if (!node) { - return false; - } - - return [ - // ClassElement - AST_NODE_TYPES.ClassProperty, - AST_NODE_TYPES.FunctionExpression, - AST_NODE_TYPES.MethodDefinition, - AST_NODE_TYPES.TSAbstractClassProperty, - AST_NODE_TYPES.TSAbstractMethodDefinition, - AST_NODE_TYPES.TSEmptyBodyFunctionExpression, - AST_NODE_TYPES.TSIndexSignature, - // TypeElement - AST_NODE_TYPES.TSCallSignatureDeclaration, - AST_NODE_TYPES.TSConstructSignatureDeclaration, - // AST_NODE_TYPES.TSIndexSignature, - AST_NODE_TYPES.TSMethodSignature, - AST_NODE_TYPES.TSPropertySignature, - ].includes(node.type); -} - -/** - * Checks if a node is a constructor method. - */ -function isConstructor( - node: TSESTree.Node | undefined, -): node is TSESTree.MethodDefinition { - return ( - node?.type === AST_NODE_TYPES.MethodDefinition && - node.kind === 'constructor' - ); -} - -/** - * Checks if a node is a setter method. - */ -function isSetter( - node: TSESTree.Node | undefined, -): node is TSESTree.MethodDefinition | TSESTree.Property { - return ( - !!node && - (node.type === AST_NODE_TYPES.MethodDefinition || - node.type === AST_NODE_TYPES.Property) && - node.kind === 'set' - ); -} - -function isIdentifier( - node: TSESTree.Node | undefined, -): node is TSESTree.Identifier { - return node?.type === AST_NODE_TYPES.Identifier; -} - -/** - * Checks if a node represents an `await …` expression. - */ -function isAwaitExpression( - node: TSESTree.Node | undefined | null, -): node is TSESTree.AwaitExpression { - return node?.type === AST_NODE_TYPES.AwaitExpression; -} - -/** - * Checks if a possible token is the `await` keyword. - */ -function isAwaitKeyword( - node: TSESTree.Token | TSESTree.Comment | undefined | null, -): node is TSESTree.KeywordToken & { value: 'await' } { - return node?.type === AST_TOKEN_TYPES.Identifier && node.value === 'await'; -} - -function isMemberOrOptionalMemberExpression( - node: TSESTree.Node, -): node is TSESTree.MemberExpression | TSESTree.OptionalMemberExpression { - return ( - node.type === AST_NODE_TYPES.MemberExpression || - node.type === AST_NODE_TYPES.OptionalMemberExpression - ); -} - -export { - isAwaitExpression, - isAwaitKeyword, - isConstructor, - isClassOrTypeElement, - isFunction, - isFunctionOrFunctionType, - isFunctionType, - isIdentifier, - isLogicalOrOperator, - isMemberOrOptionalMemberExpression, - isNonNullAssertionPunctuator, - isNotNonNullAssertionPunctuator, - isNotOptionalChainPunctuator, - isOptionalChainPunctuator, - isOptionalOptionalChain, - isSetter, - isTokenOnSameLine, - isTSConstructorType, - isTSFunctionType, - isTypeAssertion, - isVariableDeclarator, - LINEBREAK_MATCHER, -}; +// deeply re-export, for convenience +export * from '@typescript-eslint/experimental-utils/dist/ast-utils'; diff --git a/packages/eslint-plugin/src/util/misc.ts b/packages/eslint-plugin/src/util/misc.ts index c024b6a845c..e9f50fd2107 100644 --- a/packages/eslint-plugin/src/util/misc.ts +++ b/packages/eslint-plugin/src/util/misc.ts @@ -82,9 +82,7 @@ function findFirstResult( /** * Gets a string representation of the name of the index signature. */ -export function getNameFromIndexSignature( - node: TSESTree.TSIndexSignature, -): string { +function getNameFromIndexSignature(node: TSESTree.TSIndexSignature): string { const propName: TSESTree.PropertyName | undefined = node.parameters.find( (parameter: TSESTree.Parameter): parameter is TSESTree.Identifier => parameter.type === AST_NODE_TYPES.Identifier, @@ -136,6 +134,7 @@ export { ExcludeKeys, findFirstResult, getEnumNames, + getNameFromIndexSignature, getNameFromMember, InferMessageIdsTypeFromRule, InferOptionsTypeFromRule, diff --git a/packages/eslint-plugin/typings/eslint-utils.d.ts b/packages/eslint-plugin/typings/eslint-utils.d.ts deleted file mode 100644 index f2fc090c7a6..00000000000 --- a/packages/eslint-plugin/typings/eslint-utils.d.ts +++ /dev/null @@ -1,180 +0,0 @@ -declare module 'eslint-utils' { - import { TSESLint, TSESTree } from '@typescript-eslint/experimental-utils'; - - export function getFunctionHeadLocation( - node: - | TSESTree.FunctionDeclaration - | TSESTree.FunctionExpression - | TSESTree.ArrowFunctionExpression, - sourceCode: TSESLint.SourceCode, - ): TSESTree.SourceLocation; - - export function getFunctionNameWithKind( - node: - | TSESTree.FunctionDeclaration - | TSESTree.FunctionExpression - | TSESTree.ArrowFunctionExpression, - ): string; - - export function getPropertyName( - node: - | TSESTree.MemberExpression - | TSESTree.OptionalMemberExpression - | TSESTree.Property - | TSESTree.MethodDefinition, - initialScope?: TSESLint.Scope.Scope, - ): string | null; - - export function getStaticValue( - node: TSESTree.Node, - initialScope?: TSESLint.Scope.Scope, - ): { value: unknown } | null; - - export function getStringIfConstant( - node: TSESTree.Node, - initialScope?: TSESLint.Scope.Scope, - ): string | null; - - export function hasSideEffect( - node: TSESTree.Node, - sourceCode: TSESLint.SourceCode, - options?: { - considerGetters?: boolean; - considerImplicitTypeConversion?: boolean; - }, - ): boolean; - - export function isParenthesized( - node: TSESTree.Node, - sourceCode: TSESLint.SourceCode, - ): boolean; - - export class PatternMatcher { - constructor(pattern: RegExp, options?: { escaped?: boolean }); - execAll(str: string): IterableIterator; - test(str: string): boolean; - } - - export function findVariable( - initialScope: TSESLint.Scope.Scope, - name: string, - ): TSESLint.Scope.Variable | null; - - export function getInnermostScope( - initialScope: TSESLint.Scope.Scope, - node: TSESTree.Node, - ): TSESLint.Scope.Scope; - - export class ReferenceTracker { - static readonly READ: unique symbol; - static readonly CALL: unique symbol; - static readonly CONSTRUCT: unique symbol; - - constructor( - globalScope: TSESLint.Scope.Scope, - options?: { - mode: 'strict' | 'legacy'; - globalObjectNames: readonly string[]; - }, - ); - - iterateGlobalReferences( - traceMap: ReferenceTracker.TraceMap, - ): IterableIterator>; - iterateCjsReferences( - traceMap: ReferenceTracker.TraceMap, - ): IterableIterator>; - iterateEsmReferences( - traceMap: ReferenceTracker.TraceMap, - ): IterableIterator>; - } - - export namespace ReferenceTracker { - export type READ = typeof ReferenceTracker.READ; - export type CALL = typeof ReferenceTracker.READ; - export type CONSTRUCT = typeof ReferenceTracker.READ; - export type ReferenceType = READ | CALL | CONSTRUCT; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - export type TraceMap = Record>; - export interface TraceMapElement { - [ReferenceTracker.READ]?: T; - [ReferenceTracker.CALL]?: T; - [ReferenceTracker.CONSTRUCT]?: T; - [key: string]: TraceMapElement; - } - // eslint-disable-next-line @typescript-eslint/no-explicit-any - export interface FoundReference { - node: TSESTree.Node; - path: readonly string[]; - type: ReferenceType; - entry: T; - } - } - - export function isArrowToken( - token: TSESTree.Token | TSESTree.Comment, - ): token is TSESTree.PunctuatorToken & { value: '=>' }; - export function isNotArrowToken( - token: TSESTree.Token | TSESTree.Comment, - ): boolean; - export function isClosingBraceToken( - token: TSESTree.Token | TSESTree.Comment, - ): token is TSESTree.PunctuatorToken & { value: '}' }; - export function isNotClosingBraceToken( - token: TSESTree.Token | TSESTree.Comment, - ): boolean; - export function isClosingBracketToken( - token: TSESTree.Token | TSESTree.Comment, - ): token is TSESTree.PunctuatorToken & { value: ']' }; - export function isNotClosingBracketToken( - token: TSESTree.Token | TSESTree.Comment, - ): boolean; - export function isClosingParenToken( - token: TSESTree.Token | TSESTree.Comment, - ): token is TSESTree.PunctuatorToken & { value: ')' }; - export function isNotClosingParenToken( - token: TSESTree.Token | TSESTree.Comment, - ): boolean; - export function isColonToken( - token: TSESTree.Token | TSESTree.Comment, - ): token is TSESTree.PunctuatorToken & { value: ':' }; - export function isNotColonToken( - token: TSESTree.Token | TSESTree.Comment, - ): boolean; - export function isCommaToken( - token: TSESTree.Token | TSESTree.Comment, - ): token is TSESTree.PunctuatorToken & { value: ',' }; - export function isNotCommaToken( - token: TSESTree.Token | TSESTree.Comment, - ): boolean; - export function isCommentToken( - token: TSESTree.Token | TSESTree.Comment, - ): token is TSESTree.Comment; - export function isNotCommentToken< - T extends TSESTree.Token | TSESTree.Comment - >(token: T): token is Exclude; - export function isOpeningBraceToken( - token: TSESTree.Token | TSESTree.Comment, - ): token is TSESTree.PunctuatorToken & { value: '{' }; - export function isNotOpeningBraceToken( - token: TSESTree.Token | TSESTree.Comment, - ): boolean; - export function isOpeningBracketToken( - token: TSESTree.Token | TSESTree.Comment, - ): token is TSESTree.PunctuatorToken & { value: '[' }; - export function isNotOpeningBracketToken( - token: TSESTree.Token | TSESTree.Comment, - ): boolean; - export function isOpeningParenToken( - token: TSESTree.Token | TSESTree.Comment, - ): token is TSESTree.PunctuatorToken & { value: '(' }; - export function isNotOpeningParenToken( - token: TSESTree.Token | TSESTree.Comment, - ): boolean; - export function isSemicolonToken( - token: TSESTree.Token | TSESTree.Comment, - ): token is TSESTree.PunctuatorToken & { value: ';' }; - export function isNotSemicolonToken( - token: TSESTree.Token | TSESTree.Comment, - ): boolean; -} diff --git a/packages/experimental-utils/README.md b/packages/experimental-utils/README.md index 2f02ef10615..97d9d5c057b 100644 --- a/packages/experimental-utils/README.md +++ b/packages/experimental-utils/README.md @@ -5,23 +5,26 @@ ## Note This package has inherited its version number from the `@typescript-eslint` project. -Meaning that even though this package is `1.x.y`, you shouldn't expect 100% stability between minor version bumps. +Meaning that even though this package is `2.x.y`, you shouldn't expect 100% stability between minor version bumps. i.e. treat it as a `0.x.y` package. Feel free to use it now, and let us know what utilities you need or send us PRs with utilities you build on top of it. -Once it is stable, it will be renamed to `@typescript-eslint/util` for a `2.0.0` release. +Once it is stable, it will be renamed to `@typescript-eslint/util` for a `3.0.0` release. ## Exports -| Name | Description | -| ---------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- | -| [`TSESTree`](../packages/typescript-estree/src/ts-estree/ts-estree.ts) | Types for the TypeScript flavor of ESTree created by `@typescript-eslint/typescript-estree`. | -| [`AST_NODE_TYPES`](../packages/typescript-estree/src/ts-estree/ast-node-types.ts) | An enum with the names of every single _node_ found in `TSESTree`. | -| [`AST_TOKEN_TYPES`](../packages/typescript-estree/src/ts-estree/ast-node-types.ts) | An enum with the names of every single _token_ found in `TSESTree`. | -| [`TSESLint`](./src/ts-eslint) | Types for ESLint, correctly typed to work with the types found in `TSESTree`. | -| [`ESLintUtils`](./src/eslint-utils) | Tools for creating ESLint rules with TypeScript. | -| [`ParserServices`](../packages/typescript-estree/src/ts-estree/parser.ts) | The parser services provided when parsing a file using `@typescript-eslint/typescript-estree`. | +| Name | Description | +| ---------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| [`ASTUtils`](./src/ast-utils) | Tools for operating on the ESTree AST. Also includes the [`eslint-utils`](https://www.npmjs.com/package/eslint-utils) package, correctly typed to work with the types found in `TSESTree` | +| [`ESLintUtils`](./src/eslint-utils) | Tools for creating ESLint rules with TypeScript. | +| `JSONSchema` | Types from the [`@types/json-schema`](https://www.npmjs.com/package/@types/json-schema) package, re-exported to save you having to manually import them. Also ensures you're using the same version of the types as this package. | +| [`TSESLint`](./src/ts-eslint) | Types for ESLint, correctly typed to work with the types found in `TSESTree`. | +| [`TSESLintScope`](./src/ts-eslint-scope) | The [`eslint-scope`](https://www.npmjs.com/package/eslint-scope) package, correctly typed to work with the types found in both `TSESTree` and `TSESLint` | +| [`TSESTree`](../packages/typescript-estree/src/ts-estree/ts-estree.ts) | Types for the TypeScript flavor of ESTree created by `@typescript-eslint/typescript-estree`. | +| [`AST_NODE_TYPES`](../packages/typescript-estree/src/ts-estree/ast-node-types.ts) | An enum with the names of every single _node_ found in `TSESTree`. | +| [`AST_TOKEN_TYPES`](../packages/typescript-estree/src/ts-estree/ast-node-types.ts) | An enum with the names of every single _token_ found in `TSESTree`. | +| [`ParserServices`](../packages/typescript-estree/src/ts-estree/parser.ts) | Typing for the parser services provided when parsing a file using `@typescript-eslint/typescript-estree`. | ## Contributing diff --git a/packages/experimental-utils/package.json b/packages/experimental-utils/package.json index a8797df8a9c..287b52001c6 100644 --- a/packages/experimental-utils/package.json +++ b/packages/experimental-utils/package.json @@ -38,7 +38,8 @@ "dependencies": { "@types/json-schema": "^7.0.3", "@typescript-eslint/typescript-estree": "2.24.0", - "eslint-scope": "^5.0.0" + "eslint-scope": "^5.0.0", + "eslint-utils": "^2.0.0" }, "peerDependencies": { "eslint": "*" diff --git a/packages/experimental-utils/src/ast-utils/eslint-utils/PatternMatcher.ts b/packages/experimental-utils/src/ast-utils/eslint-utils/PatternMatcher.ts new file mode 100644 index 00000000000..3f45db7db46 --- /dev/null +++ b/packages/experimental-utils/src/ast-utils/eslint-utils/PatternMatcher.ts @@ -0,0 +1,56 @@ +import * as eslintUtils from 'eslint-utils'; + +interface PatternMatcher { + /** + * Iterate all matched parts in a given string. + * + * @see {@link https://eslint-utils.mysticatea.dev/api/ast-utils.html#matcher-execall} + */ + execAll(str: string): IterableIterator; + + /** + * Check whether this pattern matches a given string or not. + * + * @see {@link https://eslint-utils.mysticatea.dev/api/ast-utils.html#matcher-test} + */ + test(str: string): boolean; + + /** + * Replace all matched parts by a given replacer. + * + * @see {@link https://eslint-utils.mysticatea.dev/api/ast-utils.html#matcher-symbol-replace} + * @example + * const { PatternMatcher } = require("eslint-utils") + * const matcher = new PatternMatcher(/\\p{Script=Greek}/g) + * + * module.exports = { + * meta: {}, + * create(context) { + * return { + * "Literal[regex]"(node) { + * const replacedPattern = node.regex.pattern.replace( + * matcher, + * "[\\u0370-\\u0373\\u0375-\\u0377\\u037A-\\u037D\\u037F\\u0384\\u0386\\u0388-\\u038A\\u038C\\u038E-\\u03A1\\u03A3-\\u03E1\\u03F0-\\u03FF\\u1D26-\\u1D2A\\u1D5D-\\u1D61\\u1D66-\\u1D6A\\u1DBF\\u1F00-\\u1F15\\u1F18-\\u1F1D\\u1F20-\\u1F45\\u1F48-\\u1F4D\\u1F50-\\u1F57\\u1F59\\u1F5B\\u1F5D\\u1F5F-\\u1F7D\\u1F80-\\u1FB4\\u1FB6-\\u1FC4\\u1FC6-\\u1FD3\\u1FD6-\\u1FDB\\u1FDD-\\u1FEF\\u1FF2-\\u1FF4\\u1FF6-\\u1FFE\\u2126\\uAB65]|\\uD800[\\uDD40-\\uDD8E\\uDDA0]|\\uD834[\\uDE00-\\uDE45]" + * ) + * }, + * } + * }, + * } + */ + [Symbol.replace]( + str: string, + replacer: string | ((...strs: string[]) => string), + ): string; +} + +/** + * The class to find a pattern in strings as handling escape sequences. + * It ignores the found pattern if it's escaped with `\`. + * + * @see {@link https://eslint-utils.mysticatea.dev/api/ast-utils.html#patternmatcher-class} + */ +const PatternMatcher = eslintUtils.PatternMatcher as { + new (pattern: RegExp, options?: { escaped?: boolean }): PatternMatcher; +}; + +export { PatternMatcher }; diff --git a/packages/experimental-utils/src/ast-utils/eslint-utils/ReferenceTracker.ts b/packages/experimental-utils/src/ast-utils/eslint-utils/ReferenceTracker.ts new file mode 100644 index 00000000000..2298ac1fb1b --- /dev/null +++ b/packages/experimental-utils/src/ast-utils/eslint-utils/ReferenceTracker.ts @@ -0,0 +1,94 @@ +/* eslint-disable @typescript-eslint/no-namespace */ + +import * as eslintUtils from 'eslint-utils'; +import { TSESTree } from '../../ts-estree'; +import * as TSESLint from '../../ts-eslint'; + +const ReferenceTrackerREAD: unique symbol = eslintUtils.ReferenceTracker.READ; +const ReferenceTrackerCALL: unique symbol = eslintUtils.ReferenceTracker.CALL; +const ReferenceTrackerCONSTRUCT: unique symbol = + eslintUtils.ReferenceTracker.CONSTRUCT; + +interface ReferenceTracker { + /** + * Iterate the references that the given `traceMap` determined. + * This method starts to search from global variables. + * + * @see {@link https://eslint-utils.mysticatea.dev/api/scope-utils.html#tracker-iterateglobalreferences} + */ + iterateGlobalReferences( + traceMap: ReferenceTracker.TraceMap, + ): IterableIterator>; + + /** + * Iterate the references that the given `traceMap` determined. + * This method starts to search from `require()` expression. + * + * @see {@link https://eslint-utils.mysticatea.dev/api/scope-utils.html#tracker-iteratecjsreferences} + */ + iterateCjsReferences( + traceMap: ReferenceTracker.TraceMap, + ): IterableIterator>; + + /** + * Iterate the references that the given `traceMap` determined. + * This method starts to search from `import`/`export` declarations. + * + * @see {@link https://eslint-utils.mysticatea.dev/api/scope-utils.html#tracker-iterateesmreferences} + */ + iterateEsmReferences( + traceMap: ReferenceTracker.TraceMap, + ): IterableIterator>; +} +interface ReferenceTrackerStatic { + new ( + globalScope: TSESLint.Scope.Scope, + options?: { + /** + * The mode which determines how the `tracker.iterateEsmReferences()` method scans CommonJS modules. + * If this is `"strict"`, the method binds CommonJS modules to the default export. Otherwise, the method binds + * CommonJS modules to both the default export and named exports. Optional. Default is `"strict"`. + */ + mode: 'strict' | 'legacy'; + /** + * The name list of Global Object. Optional. Default is `["global", "globalThis", "self", "window"]`. + */ + globalObjectNames: readonly string[]; + }, + ): ReferenceTracker; + + readonly READ: typeof ReferenceTrackerREAD; + readonly CALL: typeof ReferenceTrackerCALL; + readonly CONSTRUCT: typeof ReferenceTrackerCONSTRUCT; +} + +namespace ReferenceTracker { + export type READ = ReferenceTrackerStatic['READ']; + export type CALL = ReferenceTrackerStatic['CALL']; + export type CONSTRUCT = ReferenceTrackerStatic['CONSTRUCT']; + export type ReferenceType = READ | CALL | CONSTRUCT; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + export type TraceMap = Record>; + export interface TraceMapElement { + [ReferenceTrackerREAD]?: T; + [ReferenceTrackerCALL]?: T; + [ReferenceTrackerCONSTRUCT]?: T; + [key: string]: TraceMapElement; + } + // eslint-disable-next-line @typescript-eslint/no-explicit-any + export interface FoundReference { + node: TSESTree.Node; + path: readonly string[]; + type: ReferenceType; + entry: T; + } +} + +/** + * The tracker for references. This provides reference tracking for global variables, CommonJS modules, and ES modules. + * + * @see {@link https://eslint-utils.mysticatea.dev/api/scope-utils.html#referencetracker-class} + */ +const ReferenceTracker = eslintUtils.ReferenceTracker as ReferenceTrackerStatic; + +export { ReferenceTracker }; diff --git a/packages/experimental-utils/src/ast-utils/eslint-utils/astUtilities.ts b/packages/experimental-utils/src/ast-utils/eslint-utils/astUtilities.ts new file mode 100644 index 00000000000..429b5a59cee --- /dev/null +++ b/packages/experimental-utils/src/ast-utils/eslint-utils/astUtilities.ts @@ -0,0 +1,127 @@ +import * as eslintUtils from 'eslint-utils'; +import { TSESTree } from '../../ts-estree'; +import * as TSESLint from '../../ts-eslint'; + +/** + * Get the proper location of a given function node to report. + * + * @see {@link https://eslint-utils.mysticatea.dev/api/ast-utils.html#getfunctionheadlocation} + */ +const getFunctionHeadLocation = eslintUtils.getFunctionHeadLocation as ( + node: + | TSESTree.FunctionDeclaration + | TSESTree.FunctionExpression + | TSESTree.ArrowFunctionExpression, + sourceCode: TSESLint.SourceCode, +) => TSESTree.SourceLocation; + +/** + * Get the name and kind of a given function node. + * + * @see {@link https://eslint-utils.mysticatea.dev/api/ast-utils.html#getfunctionnamewithkind} + */ +const getFunctionNameWithKind = eslintUtils.getFunctionNameWithKind as ( + node: + | TSESTree.FunctionDeclaration + | TSESTree.FunctionExpression + | TSESTree.ArrowFunctionExpression, +) => string; + +/** + * Get the property name of a given property node. + * If the node is a computed property, this tries to compute the property name by the getStringIfConstant function. + * + * @see {@link https://eslint-utils.mysticatea.dev/api/ast-utils.html#getpropertyname} + * @returns The property name of the node. If the property name is not constant then it returns `null`. + */ +const getPropertyName = eslintUtils.getPropertyName as ( + node: + | TSESTree.MemberExpression + | TSESTree.OptionalMemberExpression + | TSESTree.Property + | TSESTree.MethodDefinition, + initialScope?: TSESLint.Scope.Scope, +) => string | null; + +/** + * Get the value of a given node if it can decide the value statically. + * If the 2nd parameter `initialScope` was given, this function tries to resolve identifier references which are in the + * given node as much as possible. In the resolving way, it does on the assumption that built-in global objects have + * not been modified. + * For example, it considers `Symbol.iterator`, ` String.raw``hello`` `, and `Object.freeze({a: 1}).a` as static. + * + * @see {@link https://eslint-utils.mysticatea.dev/api/ast-utils.html#getstaticvalue} + * @returns The `{ value: any }` shaped object. The `value` property is the static value. If it couldn't compute the + * static value of the node, it returns `null`. + */ +const getStaticValue = eslintUtils.getStaticValue as ( + node: TSESTree.Node, + initialScope?: TSESLint.Scope.Scope, +) => { value: unknown } | null; + +/** + * Get the string value of a given node. + * This function is a tiny wrapper of the getStaticValue function. + * + * @see {@link https://eslint-utils.mysticatea.dev/api/ast-utils.html#getstringifconstant} + */ +const getStringIfConstant = eslintUtils.getStringIfConstant as ( + node: TSESTree.Node, + initialScope?: TSESLint.Scope.Scope, +) => string | null; + +/** + * Check whether a given node has any side effect or not. + * The side effect means that it may modify a certain variable or object member. This function considers the node which + * contains the following types as the node which has side effects: + * - `AssignmentExpression` + * - `AwaitExpression` + * - `CallExpression` + * - `ImportExpression` + * - `NewExpression` + * - `UnaryExpression([operator = "delete"])` + * - `UpdateExpression` + * - `YieldExpression` + * - When `options.considerGetters` is `true`: + * - `MemberExpression` + * - When `options.considerImplicitTypeConversion` is `true`: + * - `BinaryExpression([operator = "==" | "!=" | "<" | "<=" | ">" | ">=" | "<<" | ">>" | ">>>" | "+" | "-" | "*" | "/" | "%" | "|" | "^" | "&" | "in"])` + * - `MemberExpression([computed = true])` + * - `MethodDefinition([computed = true])` + * - `Property([computed = true])` + * - `UnaryExpression([operator = "-" | "+" | "!" | "~"])` + * + * @see {@link https://eslint-utils.mysticatea.dev/api/ast-utils.html#hassideeffect} + */ +const hasSideEffect = eslintUtils.hasSideEffect as ( + node: TSESTree.Node, + sourceCode: TSESLint.SourceCode, + options?: { + considerGetters?: boolean; + considerImplicitTypeConversion?: boolean; + }, +) => boolean; + +/** + * Check whether a given node is parenthesized or not. + * This function detects it correctly even if it's parenthesized by specific syntax. + * + * @see {@link https://eslint-utils.mysticatea.dev/api/ast-utils.html#isparenthesized} + * @returns `true` if the node is parenthesized. + * If `times` was given, it returns `true` only if the node is parenthesized the `times` times. + * For example, `isParenthesized(2, node, sourceCode)` returns true for `((foo))`, but not for `(foo)`. + */ +const isParenthesized = eslintUtils.isParenthesized as ( + node: TSESTree.Node, + sourceCode: TSESLint.SourceCode, +) => boolean; + +export { + getFunctionHeadLocation, + getFunctionNameWithKind, + getPropertyName, + getStaticValue, + getStringIfConstant, + hasSideEffect, + isParenthesized, +}; diff --git a/packages/experimental-utils/src/ast-utils/eslint-utils/index.ts b/packages/experimental-utils/src/ast-utils/eslint-utils/index.ts new file mode 100644 index 00000000000..4a41363d1a3 --- /dev/null +++ b/packages/experimental-utils/src/ast-utils/eslint-utils/index.ts @@ -0,0 +1,5 @@ +export * from './astUtilities'; +export * from './PatternMatcher'; +export * from './predicates'; +export * from './ReferenceTracker'; +export * from './scopeAnalysis'; diff --git a/packages/experimental-utils/src/ast-utils/eslint-utils/predicates.ts b/packages/experimental-utils/src/ast-utils/eslint-utils/predicates.ts new file mode 100644 index 00000000000..cbf8377127c --- /dev/null +++ b/packages/experimental-utils/src/ast-utils/eslint-utils/predicates.ts @@ -0,0 +1,106 @@ +import * as eslintUtils from 'eslint-utils'; +import { TSESTree } from '../../ts-estree'; + +const isArrowToken = eslintUtils.isArrowToken as ( + token: TSESTree.Token | TSESTree.Comment, +) => token is TSESTree.PunctuatorToken & { value: '=>' }; +const isNotArrowToken = eslintUtils.isNotArrowToken as ( + token: TSESTree.Token | TSESTree.Comment, +) => boolean; + +const isClosingBraceToken = eslintUtils.isClosingBraceToken as ( + token: TSESTree.Token | TSESTree.Comment, +) => token is TSESTree.PunctuatorToken & { value: '}' }; +const isNotClosingBraceToken = eslintUtils.isNotClosingBraceToken as ( + token: TSESTree.Token | TSESTree.Comment, +) => boolean; + +const isClosingBracketToken = eslintUtils.isClosingBracketToken as ( + token: TSESTree.Token | TSESTree.Comment, +) => token is TSESTree.PunctuatorToken & { value: ']' }; +const isNotClosingBracketToken = eslintUtils.isNotClosingBracketToken as ( + token: TSESTree.Token | TSESTree.Comment, +) => boolean; + +const isClosingParenToken = eslintUtils.isClosingParenToken as ( + token: TSESTree.Token | TSESTree.Comment, +) => token is TSESTree.PunctuatorToken & { value: ')' }; +const isNotClosingParenToken = eslintUtils.isNotClosingParenToken as ( + token: TSESTree.Token | TSESTree.Comment, +) => boolean; + +const isColonToken = eslintUtils.isColonToken as ( + token: TSESTree.Token | TSESTree.Comment, +) => token is TSESTree.PunctuatorToken & { value: ':' }; +const isNotColonToken = eslintUtils.isNotColonToken as ( + token: TSESTree.Token | TSESTree.Comment, +) => boolean; + +const isCommaToken = eslintUtils.isCommaToken as ( + token: TSESTree.Token | TSESTree.Comment, +) => token is TSESTree.PunctuatorToken & { value: ',' }; +const isNotCommaToken = eslintUtils.isNotCommaToken as ( + token: TSESTree.Token | TSESTree.Comment, +) => boolean; + +const isCommentToken = eslintUtils.isCommentToken as ( + token: TSESTree.Token | TSESTree.Comment, +) => token is TSESTree.Comment; +const isNotCommentToken = eslintUtils.isNotCommentToken as < + T extends TSESTree.Token | TSESTree.Comment +>( + token: T, +) => token is Exclude; + +const isOpeningBraceToken = eslintUtils.isOpeningBraceToken as ( + token: TSESTree.Token | TSESTree.Comment, +) => token is TSESTree.PunctuatorToken & { value: '{' }; +const isNotOpeningBraceToken = eslintUtils.isNotOpeningBraceToken as ( + token: TSESTree.Token | TSESTree.Comment, +) => boolean; + +const isOpeningBracketToken = eslintUtils.isOpeningBracketToken as ( + token: TSESTree.Token | TSESTree.Comment, +) => token is TSESTree.PunctuatorToken & { value: '[' }; +const isNotOpeningBracketToken = eslintUtils.isNotOpeningBracketToken as ( + token: TSESTree.Token | TSESTree.Comment, +) => boolean; + +const isOpeningParenToken = eslintUtils.isOpeningParenToken as ( + token: TSESTree.Token | TSESTree.Comment, +) => token is TSESTree.PunctuatorToken & { value: '(' }; +const isNotOpeningParenToken = eslintUtils.isNotOpeningParenToken as ( + token: TSESTree.Token | TSESTree.Comment, +) => boolean; + +const isSemicolonToken = eslintUtils.isSemicolonToken as ( + token: TSESTree.Token | TSESTree.Comment, +) => token is TSESTree.PunctuatorToken & { value: ';' }; +const isNotSemicolonToken = eslintUtils.isNotSemicolonToken as ( + token: TSESTree.Token | TSESTree.Comment, +) => boolean; + +export { + isArrowToken, + isClosingBraceToken, + isClosingBracketToken, + isClosingParenToken, + isColonToken, + isCommaToken, + isCommentToken, + isNotArrowToken, + isNotClosingBraceToken, + isNotClosingBracketToken, + isNotClosingParenToken, + isNotColonToken, + isNotCommaToken, + isNotCommentToken, + isNotOpeningBraceToken, + isNotOpeningBracketToken, + isNotOpeningParenToken, + isNotSemicolonToken, + isOpeningBraceToken, + isOpeningBracketToken, + isOpeningParenToken, + isSemicolonToken, +}; diff --git a/packages/experimental-utils/src/ast-utils/eslint-utils/scopeAnalysis.ts b/packages/experimental-utils/src/ast-utils/eslint-utils/scopeAnalysis.ts new file mode 100644 index 00000000000..15f9325a582 --- /dev/null +++ b/packages/experimental-utils/src/ast-utils/eslint-utils/scopeAnalysis.ts @@ -0,0 +1,27 @@ +import * as eslintUtils from 'eslint-utils'; +import { TSESTree } from '../../ts-estree'; +import * as TSESLint from '../../ts-eslint'; + +/** + * Get the variable of a given name. + * + * @see {@link https://eslint-utils.mysticatea.dev/api/scope-utils.html#findvariable} + */ +const findVariable = eslintUtils.findVariable as ( + initialScope: TSESLint.Scope.Scope, + name: string, +) => TSESLint.Scope.Variable | null; + +/** + * Get the innermost scope which contains a given node. + * + * @see {@link https://eslint-utils.mysticatea.dev/api/scope-utils.html#getinnermostscope} + * @returns The innermost scope which contains the given node. + * If such scope doesn't exist then it returns the 1st argument `initialScope`. + */ +const getInnermostScope = eslintUtils.getInnermostScope as ( + initialScope: TSESLint.Scope.Scope, + node: TSESTree.Node, +) => TSESLint.Scope.Scope; + +export { findVariable, getInnermostScope }; diff --git a/packages/experimental-utils/src/ast-utils/index.ts b/packages/experimental-utils/src/ast-utils/index.ts new file mode 100644 index 00000000000..c0ab325299a --- /dev/null +++ b/packages/experimental-utils/src/ast-utils/index.ts @@ -0,0 +1,3 @@ +export * from './misc'; +export * from './predicates'; +export * from './eslint-utils'; diff --git a/packages/experimental-utils/src/ast-utils/misc.ts b/packages/experimental-utils/src/ast-utils/misc.ts new file mode 100644 index 00000000000..cf20f99a4cc --- /dev/null +++ b/packages/experimental-utils/src/ast-utils/misc.ts @@ -0,0 +1,15 @@ +import { TSESTree } from '../ts-estree'; + +const LINEBREAK_MATCHER = /\r\n|[\r\n\u2028\u2029]/; + +/** + * Determines whether two adjacent tokens are on the same line + */ +function isTokenOnSameLine( + left: TSESTree.Token | TSESTree.Comment, + right: TSESTree.Token | TSESTree.Comment, +): boolean { + return left.loc.end.line === right.loc.start.line; +} + +export { isTokenOnSameLine, LINEBREAK_MATCHER }; diff --git a/packages/experimental-utils/src/ast-utils/predicates.ts b/packages/experimental-utils/src/ast-utils/predicates.ts new file mode 100644 index 00000000000..b12f5d9ad6f --- /dev/null +++ b/packages/experimental-utils/src/ast-utils/predicates.ts @@ -0,0 +1,247 @@ +import { AST_NODE_TYPES, AST_TOKEN_TYPES, TSESTree } from '../ts-estree'; + +function isOptionalChainPunctuator( + token: TSESTree.Token | TSESTree.Comment, +): token is TSESTree.PunctuatorToken & { value: '?.' } { + return token.type === AST_TOKEN_TYPES.Punctuator && token.value === '?.'; +} +function isNotOptionalChainPunctuator( + token: TSESTree.Token | TSESTree.Comment, +): boolean { + return !isOptionalChainPunctuator(token); +} + +function isNonNullAssertionPunctuator( + token: TSESTree.Token | TSESTree.Comment, +): token is TSESTree.PunctuatorToken & { value: '!' } { + return token.type === AST_TOKEN_TYPES.Punctuator && token.value === '!'; +} +function isNotNonNullAssertionPunctuator( + token: TSESTree.Token | TSESTree.Comment, +): boolean { + return !isNonNullAssertionPunctuator(token); +} + +/** + * Returns true if and only if the node represents: foo?.() or foo.bar?.() + */ +function isOptionalOptionalCallExpression( + node: TSESTree.Node, +): node is TSESTree.OptionalCallExpression & { optional: true } { + return ( + node.type === AST_NODE_TYPES.OptionalCallExpression && + // this flag means the call expression itself is option + // i.e. it is foo.bar?.() and not foo?.bar() + node.optional + ); +} + +/** + * Returns true if and only if the node represents logical OR + */ +function isLogicalOrOperator( + node: TSESTree.Node, +): node is TSESTree.LogicalExpression & { operator: '||' } { + return ( + node.type === AST_NODE_TYPES.LogicalExpression && node.operator === '||' + ); +} + +/** + * Checks if a node is a type assertion: + * ``` + * x as foo + * x + * ``` + */ +function isTypeAssertion( + node: TSESTree.Node | undefined | null, +): node is TSESTree.TSAsExpression | TSESTree.TSTypeAssertion { + if (!node) { + return false; + } + return ( + node.type === AST_NODE_TYPES.TSAsExpression || + node.type === AST_NODE_TYPES.TSTypeAssertion + ); +} + +function isVariableDeclarator( + node: TSESTree.Node | undefined, +): node is TSESTree.VariableDeclarator { + return node?.type === AST_NODE_TYPES.VariableDeclarator; +} + +function isFunction( + node: TSESTree.Node | undefined, +): node is + | TSESTree.ArrowFunctionExpression + | TSESTree.FunctionDeclaration + | TSESTree.FunctionExpression { + if (!node) { + return false; + } + + return [ + AST_NODE_TYPES.ArrowFunctionExpression, + AST_NODE_TYPES.FunctionDeclaration, + AST_NODE_TYPES.FunctionExpression, + ].includes(node.type); +} + +function isFunctionType( + node: TSESTree.Node | undefined, +): node is + | TSESTree.TSCallSignatureDeclaration + | TSESTree.TSConstructorType + | TSESTree.TSConstructSignatureDeclaration + | TSESTree.TSEmptyBodyFunctionExpression + | TSESTree.TSFunctionType + | TSESTree.TSMethodSignature { + if (!node) { + return false; + } + + return [ + AST_NODE_TYPES.TSCallSignatureDeclaration, + AST_NODE_TYPES.TSConstructorType, + AST_NODE_TYPES.TSConstructSignatureDeclaration, + AST_NODE_TYPES.TSEmptyBodyFunctionExpression, + AST_NODE_TYPES.TSFunctionType, + AST_NODE_TYPES.TSMethodSignature, + ].includes(node.type); +} + +function isFunctionOrFunctionType( + node: TSESTree.Node | undefined, +): node is + | TSESTree.ArrowFunctionExpression + | TSESTree.FunctionDeclaration + | TSESTree.FunctionExpression + | TSESTree.TSCallSignatureDeclaration + | TSESTree.TSConstructorType + | TSESTree.TSConstructSignatureDeclaration + | TSESTree.TSEmptyBodyFunctionExpression + | TSESTree.TSFunctionType + | TSESTree.TSMethodSignature { + return isFunction(node) || isFunctionType(node); +} + +function isTSFunctionType( + node: TSESTree.Node | undefined, +): node is TSESTree.TSFunctionType { + return node?.type === AST_NODE_TYPES.TSFunctionType; +} + +function isTSConstructorType( + node: TSESTree.Node | undefined, +): node is TSESTree.TSConstructorType { + return node?.type === AST_NODE_TYPES.TSConstructorType; +} + +function isClassOrTypeElement( + node: TSESTree.Node | undefined, +): node is TSESTree.ClassElement | TSESTree.TypeElement { + if (!node) { + return false; + } + + return [ + // ClassElement + AST_NODE_TYPES.ClassProperty, + AST_NODE_TYPES.FunctionExpression, + AST_NODE_TYPES.MethodDefinition, + AST_NODE_TYPES.TSAbstractClassProperty, + AST_NODE_TYPES.TSAbstractMethodDefinition, + AST_NODE_TYPES.TSEmptyBodyFunctionExpression, + AST_NODE_TYPES.TSIndexSignature, + // TypeElement + AST_NODE_TYPES.TSCallSignatureDeclaration, + AST_NODE_TYPES.TSConstructSignatureDeclaration, + // AST_NODE_TYPES.TSIndexSignature, + AST_NODE_TYPES.TSMethodSignature, + AST_NODE_TYPES.TSPropertySignature, + ].includes(node.type); +} + +/** + * Checks if a node is a constructor method. + */ +function isConstructor( + node: TSESTree.Node | undefined, +): node is TSESTree.MethodDefinition { + return ( + node?.type === AST_NODE_TYPES.MethodDefinition && + node.kind === 'constructor' + ); +} + +/** + * Checks if a node is a setter method. + */ +function isSetter( + node: TSESTree.Node | undefined, +): node is TSESTree.MethodDefinition | TSESTree.Property { + return ( + !!node && + (node.type === AST_NODE_TYPES.MethodDefinition || + node.type === AST_NODE_TYPES.Property) && + node.kind === 'set' + ); +} + +function isIdentifier( + node: TSESTree.Node | undefined, +): node is TSESTree.Identifier { + return node?.type === AST_NODE_TYPES.Identifier; +} + +/** + * Checks if a node represents an `await …` expression. + */ +function isAwaitExpression( + node: TSESTree.Node | undefined | null, +): node is TSESTree.AwaitExpression { + return node?.type === AST_NODE_TYPES.AwaitExpression; +} + +/** + * Checks if a possible token is the `await` keyword. + */ +function isAwaitKeyword( + node: TSESTree.Token | TSESTree.Comment | undefined | null, +): node is TSESTree.KeywordToken & { value: 'await' } { + return node?.type === AST_TOKEN_TYPES.Identifier && node.value === 'await'; +} + +function isMemberOrOptionalMemberExpression( + node: TSESTree.Node, +): node is TSESTree.MemberExpression | TSESTree.OptionalMemberExpression { + return ( + node.type === AST_NODE_TYPES.MemberExpression || + node.type === AST_NODE_TYPES.OptionalMemberExpression + ); +} + +export { + isAwaitExpression, + isAwaitKeyword, + isConstructor, + isClassOrTypeElement, + isFunction, + isFunctionOrFunctionType, + isFunctionType, + isIdentifier, + isLogicalOrOperator, + isMemberOrOptionalMemberExpression, + isNonNullAssertionPunctuator, + isNotNonNullAssertionPunctuator, + isNotOptionalChainPunctuator, + isOptionalChainPunctuator, + isOptionalOptionalCallExpression, + isSetter, + isTSConstructorType, + isTSFunctionType, + isTypeAssertion, + isVariableDeclarator, +}; diff --git a/packages/experimental-utils/src/eslint-utils/RuleCreator.ts b/packages/experimental-utils/src/eslint-utils/RuleCreator.ts index 3edb71e5a55..f9278dc8154 100644 --- a/packages/experimental-utils/src/eslint-utils/RuleCreator.ts +++ b/packages/experimental-utils/src/eslint-utils/RuleCreator.ts @@ -13,7 +13,7 @@ type CreateRuleMeta = { docs: CreateRuleMetaDocs; } & Omit, 'docs'>; -export function RuleCreator(urlCreator: (ruleName: string) => string) { +function RuleCreator(urlCreator: (ruleName: string) => string) { // This function will get much easier to call when this is merged https://github.com/Microsoft/TypeScript/pull/26349 // TODO - when the above PR lands; add type checking for the context.report `data` property return function createRule< @@ -52,3 +52,5 @@ export function RuleCreator(urlCreator: (ruleName: string) => string) { }; }; } + +export { RuleCreator }; diff --git a/packages/experimental-utils/src/eslint-utils/applyDefault.ts b/packages/experimental-utils/src/eslint-utils/applyDefault.ts index f9f9c8f3a41..142c8d1a746 100644 --- a/packages/experimental-utils/src/eslint-utils/applyDefault.ts +++ b/packages/experimental-utils/src/eslint-utils/applyDefault.ts @@ -7,7 +7,7 @@ import { deepMerge, isObjectNotArray } from './deepMerge'; * @param userOptions the user opts * @returns the options with defaults */ -export function applyDefault( +function applyDefault( defaultOptions: TDefault, userOptions: TUser | null, ): TDefault { @@ -32,3 +32,5 @@ export function applyDefault( return options; } + +export { applyDefault }; diff --git a/packages/experimental-utils/src/eslint-utils/deepMerge.ts b/packages/experimental-utils/src/eslint-utils/deepMerge.ts index 3603b662a9c..5ac6509203f 100644 --- a/packages/experimental-utils/src/eslint-utils/deepMerge.ts +++ b/packages/experimental-utils/src/eslint-utils/deepMerge.ts @@ -5,7 +5,7 @@ type ObjectLike = Record; * @param obj an object * @returns `true` if obj is an object */ -export function isObjectNotArray( +function isObjectNotArray( obj: unknown | unknown[], ): obj is T { return typeof obj === 'object' && !Array.isArray(obj); @@ -48,3 +48,5 @@ export function deepMerge( return acc; }, {} as ObjectLike); } + +export { isObjectNotArray }; diff --git a/packages/experimental-utils/src/eslint-utils/getParserServices.ts b/packages/experimental-utils/src/eslint-utils/getParserServices.ts index 65607e77290..57b6bc59f65 100644 --- a/packages/experimental-utils/src/eslint-utils/getParserServices.ts +++ b/packages/experimental-utils/src/eslint-utils/getParserServices.ts @@ -1,4 +1,5 @@ -import { ParserServices, TSESLint } from '../'; +import * as TSESLint from '../ts-eslint'; +import { ParserServices } from '../ts-estree'; const ERROR_MESSAGE = 'You have used a rule which requires parserServices to be generated. You must therefore provide a value for the "parserOptions.project" property for @typescript-eslint/parser.'; @@ -10,7 +11,7 @@ type RequiredParserServices = { /** * Try to retrieve typescript parser service from context */ -export function getParserServices< +function getParserServices< TMessageIds extends string, TOptions extends unknown[] >( @@ -29,3 +30,5 @@ export function getParserServices< } return context.parserServices as RequiredParserServices; } + +export { getParserServices }; diff --git a/packages/experimental-utils/src/index.ts b/packages/experimental-utils/src/index.ts index 5a1fdb90bad..31328386269 100644 --- a/packages/experimental-utils/src/index.ts +++ b/packages/experimental-utils/src/index.ts @@ -1,19 +1,8 @@ +import * as ASTUtils from './ast-utils'; import * as ESLintUtils from './eslint-utils'; +import * as JSONSchema from './json-schema'; import * as TSESLint from './ts-eslint'; import * as TSESLintScope from './ts-eslint-scope'; -import * as JSONSchema from './json-schema'; - -export { ESLintUtils, JSONSchema, 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 -// NOTE - this uses hard links inside ts-estree to avoid initialization of entire package -// via its main file (which imports typescript at runtime). -// Not every eslint-plugin written in typescript requires typescript at runtime. -export { - AST_NODE_TYPES, - AST_TOKEN_TYPES, - TSESTree, -} from '@typescript-eslint/typescript-estree/dist/ts-estree'; -export { ParserServices } from '@typescript-eslint/typescript-estree/dist/parser-options'; +export { ASTUtils, ESLintUtils, JSONSchema, TSESLint, TSESLintScope }; +export * from './ts-estree'; diff --git a/packages/experimental-utils/src/ts-eslint-scope/Definition.ts b/packages/experimental-utils/src/ts-eslint-scope/Definition.ts index b2f4b91383e..15c69bbf86c 100644 --- a/packages/experimental-utils/src/ts-eslint-scope/Definition.ts +++ b/packages/experimental-utils/src/ts-eslint-scope/Definition.ts @@ -1,8 +1,8 @@ -import { TSESTree } from '@typescript-eslint/typescript-estree'; import { Definition as ESLintDefinition, ParameterDefinition as ESLintParameterDefinition, } from 'eslint-scope/lib/definition'; +import { TSESTree } from '../ts-estree'; interface Definition { type: string; diff --git a/packages/experimental-utils/src/ts-eslint-scope/Options.ts b/packages/experimental-utils/src/ts-eslint-scope/Options.ts index f06fe4e42e8..a450e225b7f 100644 --- a/packages/experimental-utils/src/ts-eslint-scope/Options.ts +++ b/packages/experimental-utils/src/ts-eslint-scope/Options.ts @@ -1,4 +1,4 @@ -import { TSESTree } from '@typescript-eslint/typescript-estree'; +import { TSESTree } from '../ts-estree'; type PatternVisitorCallback = ( pattern: TSESTree.Identifier, diff --git a/packages/experimental-utils/src/ts-eslint-scope/PatternVisitor.ts b/packages/experimental-utils/src/ts-eslint-scope/PatternVisitor.ts index c53ceb69f7a..8563f6d934b 100644 --- a/packages/experimental-utils/src/ts-eslint-scope/PatternVisitor.ts +++ b/packages/experimental-utils/src/ts-eslint-scope/PatternVisitor.ts @@ -1,5 +1,5 @@ -import { TSESTree } from '@typescript-eslint/typescript-estree'; import ESLintPatternVisitor from 'eslint-scope/lib/pattern-visitor'; +import { TSESTree } from '../ts-estree'; import { ScopeManager } from './ScopeManager'; import { PatternVisitorCallback, diff --git a/packages/experimental-utils/src/ts-eslint-scope/Reference.ts b/packages/experimental-utils/src/ts-eslint-scope/Reference.ts index 96d2fab4df5..8d5c295f910 100644 --- a/packages/experimental-utils/src/ts-eslint-scope/Reference.ts +++ b/packages/experimental-utils/src/ts-eslint-scope/Reference.ts @@ -1,5 +1,5 @@ -import { TSESTree } from '@typescript-eslint/typescript-estree'; import ESLintReference from 'eslint-scope/lib/reference'; +import { TSESTree } from '../ts-estree'; import { Scope } from './Scope'; import { Variable } from './Variable'; diff --git a/packages/experimental-utils/src/ts-eslint-scope/Referencer.ts b/packages/experimental-utils/src/ts-eslint-scope/Referencer.ts index 6169d9def9a..1d1180f21f8 100644 --- a/packages/experimental-utils/src/ts-eslint-scope/Referencer.ts +++ b/packages/experimental-utils/src/ts-eslint-scope/Referencer.ts @@ -1,6 +1,6 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -import { TSESTree } from '@typescript-eslint/typescript-estree'; import ESLintReferencer from 'eslint-scope/lib/referencer'; +import { TSESTree } from '../ts-estree'; import { PatternVisitorCallback, PatternVisitorOptions, diff --git a/packages/experimental-utils/src/ts-eslint-scope/Scope.ts b/packages/experimental-utils/src/ts-eslint-scope/Scope.ts index 6c5481aa4d7..49f1e11c795 100644 --- a/packages/experimental-utils/src/ts-eslint-scope/Scope.ts +++ b/packages/experimental-utils/src/ts-eslint-scope/Scope.ts @@ -1,6 +1,5 @@ /* eslint-disable @typescript-eslint/no-empty-interface, @typescript-eslint/no-explicit-any */ -import { TSESTree } from '@typescript-eslint/typescript-estree'; import { Scope as ESLintScope, GlobalScope as ESLintGlobalScope, @@ -14,6 +13,7 @@ import { ForScope as ESLintForScope, ClassScope as ESLintClassScope, } from 'eslint-scope/lib/scope'; +import { TSESTree } from '../ts-estree'; import { Definition } from './Definition'; import { Reference, ReferenceFlag } from './Reference'; import { ScopeManager } from './ScopeManager'; diff --git a/packages/experimental-utils/src/ts-eslint-scope/ScopeManager.ts b/packages/experimental-utils/src/ts-eslint-scope/ScopeManager.ts index 6d05fea395f..d1b469a6d4f 100644 --- a/packages/experimental-utils/src/ts-eslint-scope/ScopeManager.ts +++ b/packages/experimental-utils/src/ts-eslint-scope/ScopeManager.ts @@ -1,5 +1,5 @@ -import { TSESTree } from '@typescript-eslint/typescript-estree'; import ESLintScopeManager from 'eslint-scope/lib/scope-manager'; +import { TSESTree } from '../ts-estree'; import { Scope } from './Scope'; import { Variable } from './Variable'; diff --git a/packages/experimental-utils/src/ts-eslint-scope/Variable.ts b/packages/experimental-utils/src/ts-eslint-scope/Variable.ts index 49bbf3d9b4a..c9ff75f95d5 100644 --- a/packages/experimental-utils/src/ts-eslint-scope/Variable.ts +++ b/packages/experimental-utils/src/ts-eslint-scope/Variable.ts @@ -1,5 +1,5 @@ -import { TSESTree } from '@typescript-eslint/typescript-estree'; import ESLintVariable from 'eslint-scope/lib/variable'; +import { TSESTree } from '../ts-estree'; import { Reference } from './Reference'; import { Definition } from './Definition'; import { Scope } from './Scope'; diff --git a/packages/experimental-utils/src/ts-eslint/AST.ts b/packages/experimental-utils/src/ts-eslint/AST.ts index 1c77caafedf..5a0ea09d843 100644 --- a/packages/experimental-utils/src/ts-eslint/AST.ts +++ b/packages/experimental-utils/src/ts-eslint/AST.ts @@ -1,9 +1,6 @@ /* eslint-disable @typescript-eslint/no-namespace */ -import { - TSESTree, - AST_TOKEN_TYPES, -} from '@typescript-eslint/typescript-estree'; +import { TSESTree, AST_TOKEN_TYPES } from '../ts-estree'; namespace AST { export type TokenType = AST_TOKEN_TYPES; diff --git a/packages/experimental-utils/src/ts-eslint/Linter.ts b/packages/experimental-utils/src/ts-eslint/Linter.ts index 93c5565ff4d..0233f2f8408 100644 --- a/packages/experimental-utils/src/ts-eslint/Linter.ts +++ b/packages/experimental-utils/src/ts-eslint/Linter.ts @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/no-namespace */ -import { TSESTree, ParserServices } from '@typescript-eslint/typescript-estree'; import { Linter as ESLintLinter } from 'eslint'; +import { TSESTree, ParserServices } from '../ts-estree'; import { ParserOptions as TSParserOptions } from './ParserOptions'; import { RuleModule, RuleFix } from './Rule'; import { Scope } from './Scope'; diff --git a/packages/experimental-utils/src/ts-eslint/ParserOptions.ts b/packages/experimental-utils/src/ts-eslint/ParserOptions.ts index 12c121989dc..d6bf57bb585 100644 --- a/packages/experimental-utils/src/ts-eslint/ParserOptions.ts +++ b/packages/experimental-utils/src/ts-eslint/ParserOptions.ts @@ -1,6 +1,6 @@ import { TSESTreeOptions } from '@typescript-eslint/typescript-estree'; -export interface ParserOptions { +interface ParserOptions { comment?: boolean; ecmaFeatures?: { globalReturn?: boolean; @@ -23,3 +23,5 @@ export interface ParserOptions { useJSXTextNode?: boolean; warnOnUnsupportedTypeScriptVersion?: boolean; } + +export { ParserOptions }; diff --git a/packages/experimental-utils/src/ts-eslint/Rule.ts b/packages/experimental-utils/src/ts-eslint/Rule.ts index 6e7733da663..1f74150292e 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 { ParserServices, TSESTree } from '../ts-estree'; import { AST } from './AST'; import { Linter } from './Linter'; import { Scope } from './Scope'; diff --git a/packages/experimental-utils/src/ts-eslint/RuleTester.ts b/packages/experimental-utils/src/ts-eslint/RuleTester.ts index ce441603bf1..9c63709f37f 100644 --- a/packages/experimental-utils/src/ts-eslint/RuleTester.ts +++ b/packages/experimental-utils/src/ts-eslint/RuleTester.ts @@ -1,8 +1,5 @@ -import { - AST_NODE_TYPES, - AST_TOKEN_TYPES, -} from '@typescript-eslint/typescript-estree'; import { RuleTester as ESLintRuleTester } from 'eslint'; +import { AST_NODE_TYPES, AST_TOKEN_TYPES } from '../ts-estree'; import { ParserOptions } from './ParserOptions'; import { RuleModule } from './Rule'; diff --git a/packages/experimental-utils/src/ts-eslint/Scope.ts b/packages/experimental-utils/src/ts-eslint/Scope.ts index 03c6bc66395..bb5e5accc51 100644 --- a/packages/experimental-utils/src/ts-eslint/Scope.ts +++ b/packages/experimental-utils/src/ts-eslint/Scope.ts @@ -1,6 +1,6 @@ /* eslint-disable @typescript-eslint/no-namespace */ -import { TSESTree } from '@typescript-eslint/typescript-estree'; +import { TSESTree } from '../ts-estree'; namespace Scope { export interface ScopeManager { diff --git a/packages/experimental-utils/src/ts-eslint/SourceCode.ts b/packages/experimental-utils/src/ts-eslint/SourceCode.ts index eeaa906f37d..0ea39973a25 100644 --- a/packages/experimental-utils/src/ts-eslint/SourceCode.ts +++ b/packages/experimental-utils/src/ts-eslint/SourceCode.ts @@ -1,10 +1,10 @@ /* eslint-disable @typescript-eslint/no-namespace */ -import { ParserServices, TSESTree } from '@typescript-eslint/typescript-estree'; import { SourceCode as ESLintSourceCode } from 'eslint'; +import { ParserServices, TSESTree } from '../ts-estree'; import { Scope } from './Scope'; -declare interface SourceCode { +interface SourceCode { text: string; ast: SourceCode.Program; lines: string[]; diff --git a/packages/experimental-utils/src/ts-estree.ts b/packages/experimental-utils/src/ts-estree.ts new file mode 100644 index 00000000000..a7f18377e37 --- /dev/null +++ b/packages/experimental-utils/src/ts-estree.ts @@ -0,0 +1,12 @@ +// for convenience's sake - export the types directly from here so consumers +// don't need to reference/install both packages in their code + +// NOTE - this uses hard links inside ts-estree to avoid initialization of entire package +// via its main file (which imports typescript at runtime). +// Not every eslint-plugin written in typescript requires typescript at runtime. +export { + AST_NODE_TYPES, + AST_TOKEN_TYPES, + TSESTree, +} from '@typescript-eslint/typescript-estree/dist/ts-estree'; +export { ParserServices } from '@typescript-eslint/typescript-estree/dist/parser-options'; diff --git a/packages/experimental-utils/typings/eslint-utils.d.ts b/packages/experimental-utils/typings/eslint-utils.d.ts new file mode 100644 index 00000000000..f12326c0b4a --- /dev/null +++ b/packages/experimental-utils/typings/eslint-utils.d.ts @@ -0,0 +1,40 @@ +declare module 'eslint-utils' { + export const findVariable: unknown; + export const getFunctionHeadLocation: unknown; + export const getFunctionNameWithKind: unknown; + export const getInnermostScope: unknown; + export const getPropertyName: unknown; + export const getStaticValue: unknown; + export const getStringIfConstant: unknown; + export const hasSideEffect: unknown; + export const isArrowToken: unknown; + export const isClosingBraceToken: unknown; + export const isClosingBracketToken: unknown; + export const isClosingParenToken: unknown; + export const isColonToken: unknown; + export const isCommaToken: unknown; + export const isCommentToken: unknown; + export const isNotArrowToken: unknown; + export const isNotClosingBraceToken: unknown; + export const isNotClosingBracketToken: unknown; + export const isNotClosingParenToken: unknown; + export const isNotColonToken: unknown; + export const isNotCommaToken: unknown; + export const isNotCommentToken: unknown; + export const isNotOpeningBraceToken: unknown; + export const isNotOpeningBracketToken: unknown; + export const isNotOpeningParenToken: unknown; + export const isNotSemicolonToken: unknown; + export const isOpeningBraceToken: unknown; + export const isOpeningBracketToken: unknown; + export const isOpeningParenToken: unknown; + export const isParenthesized: unknown; + export const isSemicolonToken: unknown; + export const PatternMatcher: unknown; + export const ReferenceTracker: { + READ: never; + CALL: never; + CONSTRUCT: never; + new (): never; + }; +} diff --git a/yarn.lock b/yarn.lock index 5a972a6cc18..4e2a78cbd7e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3373,6 +3373,13 @@ eslint-utils@^1.4.3: dependencies: eslint-visitor-keys "^1.1.0" +eslint-utils@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-2.0.0.tgz#7be1cc70f27a72a76cd14aa698bcabed6890e1cd" + integrity sha512-0HCPuJv+7Wv1bACm8y5/ECVfYdfsAm9xmVb7saeFlxjPYALefjhbYoCkBjPdPzGH8wWyTpAez82Fh3VKYEZ8OA== + dependencies: + eslint-visitor-keys "^1.1.0" + eslint-visitor-keys@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz#e2a82cea84ff246ad6fb57f9bde5b46621459ec2"