From 74330d58ac1afae5ad952f341582d97713ea70e9 Mon Sep 17 00:00:00 2001 From: Ben Lichtman Date: Mon, 22 Jul 2019 16:50:12 -0700 Subject: [PATCH 1/9] feat(typescript-estree): throw error on external file --- packages/typescript-estree/src/parser.ts | 29 ++++++------- .../typescript-estree/src/tsconfig-parser.ts | 10 +++++ .../tests/fixtures/simpleProject/file.ts | 0 .../fixtures/simpleProject/tsconfig.json | 1 + packages/typescript-estree/tests/lib/parse.ts | 25 +++++++---- .../tests/lib/semanticInfo.ts | 41 +++++++++++-------- 6 files changed, 65 insertions(+), 41 deletions(-) create mode 100644 packages/typescript-estree/tests/fixtures/simpleProject/file.ts create mode 100644 packages/typescript-estree/tests/fixtures/simpleProject/tsconfig.json diff --git a/packages/typescript-estree/src/parser.ts b/packages/typescript-estree/src/parser.ts index 81825267fcb6..1abb8d91d6db 100644 --- a/packages/typescript-estree/src/parser.ts +++ b/packages/typescript-estree/src/parser.ts @@ -6,10 +6,7 @@ import { firstDefined } from './node-utils'; import { Extra, TSESTreeOptions, ParserServices } from './parser-options'; import { getFirstSemanticOrSyntacticError } from './semantic-errors'; import { TSESTree } from './ts-estree'; -import { - calculateProjectParserOptions, - createProgram, -} from './tsconfig-parser'; +import { calculateProjectParserOptions } from './tsconfig-parser'; /** * This needs to be kept in sync with the top-level README.md in the @@ -67,7 +64,7 @@ function resetExtra(): void { * @returns If found, returns the source file corresponding to the code and the containing program */ function getASTFromProject(code: string, options: TSESTreeOptions) { - return firstDefined( + const astAndProgram = firstDefined( calculateProjectParserOptions( code, options.filePath || getFileName(options), @@ -80,18 +77,17 @@ function getASTFromProject(code: string, options: TSESTreeOptions) { return ast && { ast, program: currentProgram }; }, ); -} -/** - * @param code The code of the file being linted - * @param options The config object - * @returns If found, returns the source file corresponding to the code and the containing program - */ -function getASTAndDefaultProject(code: string, options: TSESTreeOptions) { - const fileName = options.filePath || getFileName(options); - const program = createProgram(code, fileName, extra); - const ast = program && program.getSourceFile(fileName); - return ast && { ast, program }; + if (!astAndProgram) { + throw new Error( + `If "parserOptions.project" has been set for @typescript-eslint/parser, ${options.filePath || + getFileName( + options, + )} must be included in at least one of the projects provided.`, + ); + } + + return astAndProgram; } /** @@ -164,7 +160,6 @@ function getProgramAndAST( ) { return ( (shouldProvideParserServices && getASTFromProject(code, options)) || - (shouldProvideParserServices && getASTAndDefaultProject(code, options)) || createNewProgram(code) ); } diff --git a/packages/typescript-estree/src/tsconfig-parser.ts b/packages/typescript-estree/src/tsconfig-parser.ts index 641af07a77a4..c329f7ce4bbf 100644 --- a/packages/typescript-estree/src/tsconfig-parser.ts +++ b/packages/typescript-estree/src/tsconfig-parser.ts @@ -30,6 +30,16 @@ const watchCallbackTrackingMap = new Map(); const parsedFilesSeen = new Set(); +/** + * Clear tsconfig caches. + * Primarily used for testing. + */ +export function clearCaches() { + knownWatchProgramMap.clear(); + watchCallbackTrackingMap.clear(); + parsedFilesSeen.clear(); +} + /** * Holds information about the file currently being linted */ diff --git a/packages/typescript-estree/tests/fixtures/simpleProject/file.ts b/packages/typescript-estree/tests/fixtures/simpleProject/file.ts new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/packages/typescript-estree/tests/fixtures/simpleProject/tsconfig.json b/packages/typescript-estree/tests/fixtures/simpleProject/tsconfig.json new file mode 100644 index 000000000000..0967ef424bce --- /dev/null +++ b/packages/typescript-estree/tests/fixtures/simpleProject/tsconfig.json @@ -0,0 +1 @@ +{} diff --git a/packages/typescript-estree/tests/lib/parse.ts b/packages/typescript-estree/tests/lib/parse.ts index 3c34f7055565..8fbac2e33a96 100644 --- a/packages/typescript-estree/tests/lib/parse.ts +++ b/packages/typescript-estree/tests/lib/parse.ts @@ -2,6 +2,9 @@ import * as parser from '../../src/parser'; import * as astConverter from '../../src/ast-converter'; import { TSESTreeOptions } from '../../src/parser-options'; import { createSnapshotTestBlock } from '../../tools/test-utils'; +import { join } from 'path'; + +const FIXTURES_DIR = './tests/fixtures/simpleProject'; describe('parse()', () => { describe('basic functionality', () => { @@ -135,6 +138,12 @@ describe('parse()', () => { tokens: true, range: true, loc: true, + filePath: 'tests/fixtures/simpleProject/file.ts', + }; + const projectConfig: TSESTreeOptions = { + ...baseConfig, + tsconfigRootDir: join(process.cwd(), FIXTURES_DIR), + project: './tsconfig.json', }; it('should not impact the use of parse()', () => { @@ -165,10 +174,10 @@ describe('parse()', () => { expect(noOptionSet.services.esTreeNodeToTSNodeMap).toBeUndefined(); expect(noOptionSet.services.tsNodeToESTreeNodeMap).toBeUndefined(); - const withProjectNoOptionSet = parser.parseAndGenerateServices(code, { - ...baseConfig, - project: './tsconfig.json', - }); + const withProjectNoOptionSet = parser.parseAndGenerateServices( + code, + projectConfig, + ); expect(withProjectNoOptionSet.services.esTreeNodeToTSNodeMap).toEqual( expect.any(WeakMap), @@ -192,9 +201,8 @@ describe('parse()', () => { ); const withProjectOptionSetToTrue = parser.parseAndGenerateServices(code, { - ...baseConfig, + ...projectConfig, preserveNodeMaps: true, - project: './tsconfig.json', }); expect(withProjectOptionSetToTrue.services.esTreeNodeToTSNodeMap).toEqual( @@ -216,7 +224,10 @@ describe('parse()', () => { const withProjectOptionSetToFalse = parser.parseAndGenerateServices( code, - { ...baseConfig, preserveNodeMaps: false, project: './tsconfig.json' }, + { + ...projectConfig, + preserveNodeMaps: false, + }, ); expect( diff --git a/packages/typescript-estree/tests/lib/semanticInfo.ts b/packages/typescript-estree/tests/lib/semanticInfo.ts index 189816f08c64..906ad5456cfe 100644 --- a/packages/typescript-estree/tests/lib/semanticInfo.ts +++ b/packages/typescript-estree/tests/lib/semanticInfo.ts @@ -10,6 +10,7 @@ import { } from '../../tools/test-utils'; import { parseAndGenerateServices } from '../../src/parser'; import { TSESTree } from '../../src/ts-estree'; +import { clearCaches } from '../../src/tsconfig-parser'; const FIXTURES_DIR = './tests/fixtures/semanticInfo'; const testFiles = glob.sync(`${FIXTURES_DIR}/**/*.src.ts`); @@ -25,11 +26,14 @@ function createOptions(fileName: string): TSESTreeOptions & { cwd?: string } { errorOnUnknownASTType: true, filePath: fileName, tsconfigRootDir: join(process.cwd(), FIXTURES_DIR), - project: './tsconfig.json', + project: `./tsconfig.json`, loggerFn: false, }; } +// ensure tsconfig-parser caches are clean for each test +beforeEach(() => clearCaches()); + describe('semanticInfo', () => { // test all AST snapshots testFiles.forEach(filename => { @@ -188,12 +192,14 @@ describe('semanticInfo', () => { it('non-existent file tests', () => { const parseResult = parseCodeAndGenerateServices( `const x = [parseInt("5")];`, - createOptions(''), + { + ...createOptions(''), + project: undefined, + preserveNodeMaps: true, + }, ); - // get type checker - expect(parseResult).toHaveProperty('services.program.getTypeChecker'); - const checker = parseResult.services.program!.getTypeChecker(); + expect(parseResult.services.program).toBeUndefined(); // get bound name const boundName = (parseResult.ast as any).body[0].declarations[0].id; @@ -204,8 +210,6 @@ describe('semanticInfo', () => { ); expect(tsBoundName).toBeDefined(); - checkNumberArrayType(checker, tsBoundName!); - expect(parseResult.services.tsNodeToESTreeNodeMap!.get(tsBoundName!)).toBe( boundName, ); @@ -214,18 +218,21 @@ describe('semanticInfo', () => { it('non-existent file should provide parents nodes', () => { const parseResult = parseCodeAndGenerateServices( `function M() { return Base }`, - createOptions(''), + { ...createOptions(''), project: undefined }, ); - // https://github.com/JamesHenry/typescript-estree/issues/77 - expect(parseResult.services.program).toBeDefined(); - expect( - parseResult.services.program!.getSourceFile(''), - ).toBeDefined(); - expect( - parseResult.services.program!.getSourceFile('')!.statements[0] - .parent, - ).toBeDefined(); + expect(parseResult.services.program).toBeUndefined(); + }); + + it(`non-existent file should throw error when project provided`, () => { + expect(() => + parseCodeAndGenerateServices( + `function M() { return Base }`, + createOptions(''), + ), + ).toThrow( + `If "parserOptions.project" has been set for @typescript-eslint/parser, must be included in at least one of the projects provided.`, + ); }); it('non-existent project file', () => { From 5a361abe1d8b95be311ddac696f150b77f674d63 Mon Sep 17 00:00:00 2001 From: Ben Lichtman Date: Mon, 22 Jul 2019 17:45:13 -0700 Subject: [PATCH 2/9] test(eslint-plugin): update tests to handle new constraint --- packages/eslint-plugin/tests/RuleTester.ts | 25 +++++++++++++++++++ packages/eslint-plugin/tests/fixtures/file.ts | 0 .../rules/no-unnecessary-qualifier.test.ts | 1 - 3 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 packages/eslint-plugin/tests/fixtures/file.ts diff --git a/packages/eslint-plugin/tests/RuleTester.ts b/packages/eslint-plugin/tests/RuleTester.ts index 769d7bc1aae1..226a33ea1edd 100644 --- a/packages/eslint-plugin/tests/RuleTester.ts +++ b/packages/eslint-plugin/tests/RuleTester.ts @@ -7,6 +7,8 @@ type RuleTesterConfig = Omit & { parser: typeof parser; }; class RuleTester extends TSESLint.RuleTester { + private filename: string | undefined = undefined; + // as of eslint 6 you have to provide an absolute path to the parser // but that's not as clean to type, this saves us trying to manually enforce // that contributors require.resolve everything @@ -15,6 +17,10 @@ class RuleTester extends TSESLint.RuleTester { ...options, parser: require.resolve(options.parser), }); + + if (options.parserOptions && options.parserOptions.project) { + this.filename = path.join(getFixturesRootDir(), 'file.ts'); + } } // as of eslint 6 you have to provide an absolute path to the parser @@ -26,17 +32,36 @@ class RuleTester extends TSESLint.RuleTester { tests: TSESLint.RunTests, ): void { const errorMessage = `Do not set the parser at the test level unless you want to use a parser other than ${parser}`; + + if (this.filename) { + tests.valid = tests.valid.map(test => { + if (typeof test === 'string') { + return { + code: test, + filename: this.filename, + }; + } + return test; + }); + } + tests.valid.forEach(test => { if (typeof test !== 'string') { if (test.parser === parser) { throw new Error(errorMessage); } + if (!test.filename) { + test.filename = this.filename; + } } }); tests.invalid.forEach(test => { if (test.parser === parser) { throw new Error(errorMessage); } + if (!test.filename) { + test.filename = this.filename; + } }); super.run(name, rule, tests); diff --git a/packages/eslint-plugin/tests/fixtures/file.ts b/packages/eslint-plugin/tests/fixtures/file.ts new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/packages/eslint-plugin/tests/rules/no-unnecessary-qualifier.test.ts b/packages/eslint-plugin/tests/rules/no-unnecessary-qualifier.test.ts index 53a349e552ee..75236704c853 100644 --- a/packages/eslint-plugin/tests/rules/no-unnecessary-qualifier.test.ts +++ b/packages/eslint-plugin/tests/rules/no-unnecessary-qualifier.test.ts @@ -199,7 +199,6 @@ import * as Foo from './foo'; declare module './foo' { const x: Foo.T = 3; }`, - filename: path.join(rootPath, 'bar.ts'), errors: [ { messageId, From 22a5b8a56cc8236651c3d11e98d24332e49477ce Mon Sep 17 00:00:00 2001 From: Ben Lichtman Date: Tue, 23 Jul 2019 16:25:32 -0700 Subject: [PATCH 3/9] test(parser, eslint-plugin-tslint): fix more tests --- packages/eslint-plugin-tslint/tests/fixture-project/1.ts | 1 + packages/eslint-plugin-tslint/tests/fixture-project/2.ts | 1 + packages/eslint-plugin-tslint/tests/fixture-project/3.ts | 1 + packages/eslint-plugin-tslint/tests/fixture-project/4.ts | 1 + packages/eslint-plugin-tslint/tests/fixture-project/5.ts | 1 + packages/eslint-plugin-tslint/tests/fixture-project/6.ts | 1 + .../tests/fixture-project/tsconfig.json | 1 + packages/eslint-plugin-tslint/tests/index.spec.ts | 9 +++++++-- packages/parser/tests/lib/parser.ts | 4 ++-- 9 files changed, 16 insertions(+), 4 deletions(-) create mode 100644 packages/eslint-plugin-tslint/tests/fixture-project/1.ts create mode 100644 packages/eslint-plugin-tslint/tests/fixture-project/2.ts create mode 100644 packages/eslint-plugin-tslint/tests/fixture-project/3.ts create mode 100644 packages/eslint-plugin-tslint/tests/fixture-project/4.ts create mode 100644 packages/eslint-plugin-tslint/tests/fixture-project/5.ts create mode 100644 packages/eslint-plugin-tslint/tests/fixture-project/6.ts create mode 100644 packages/eslint-plugin-tslint/tests/fixture-project/tsconfig.json diff --git a/packages/eslint-plugin-tslint/tests/fixture-project/1.ts b/packages/eslint-plugin-tslint/tests/fixture-project/1.ts new file mode 100644 index 000000000000..0870b5cd2825 --- /dev/null +++ b/packages/eslint-plugin-tslint/tests/fixture-project/1.ts @@ -0,0 +1 @@ +var foo = true; diff --git a/packages/eslint-plugin-tslint/tests/fixture-project/2.ts b/packages/eslint-plugin-tslint/tests/fixture-project/2.ts new file mode 100644 index 000000000000..3da535b2cde3 --- /dev/null +++ b/packages/eslint-plugin-tslint/tests/fixture-project/2.ts @@ -0,0 +1 @@ +throw 'should be ok because rule is not loaded'; diff --git a/packages/eslint-plugin-tslint/tests/fixture-project/3.ts b/packages/eslint-plugin-tslint/tests/fixture-project/3.ts new file mode 100644 index 000000000000..e71f830c3915 --- /dev/null +++ b/packages/eslint-plugin-tslint/tests/fixture-project/3.ts @@ -0,0 +1 @@ +throw 'err'; // no-string-throw diff --git a/packages/eslint-plugin-tslint/tests/fixture-project/4.ts b/packages/eslint-plugin-tslint/tests/fixture-project/4.ts new file mode 100644 index 000000000000..e1173e87a223 --- /dev/null +++ b/packages/eslint-plugin-tslint/tests/fixture-project/4.ts @@ -0,0 +1 @@ +var foo = true; // semicolon diff --git a/packages/eslint-plugin-tslint/tests/fixture-project/5.ts b/packages/eslint-plugin-tslint/tests/fixture-project/5.ts new file mode 100644 index 000000000000..2fc07810720c --- /dev/null +++ b/packages/eslint-plugin-tslint/tests/fixture-project/5.ts @@ -0,0 +1 @@ +var foo = true; // fail diff --git a/packages/eslint-plugin-tslint/tests/fixture-project/6.ts b/packages/eslint-plugin-tslint/tests/fixture-project/6.ts new file mode 100644 index 000000000000..e901f01b4874 --- /dev/null +++ b/packages/eslint-plugin-tslint/tests/fixture-project/6.ts @@ -0,0 +1 @@ +foo; diff --git a/packages/eslint-plugin-tslint/tests/fixture-project/tsconfig.json b/packages/eslint-plugin-tslint/tests/fixture-project/tsconfig.json new file mode 100644 index 000000000000..0967ef424bce --- /dev/null +++ b/packages/eslint-plugin-tslint/tests/fixture-project/tsconfig.json @@ -0,0 +1 @@ +{} diff --git a/packages/eslint-plugin-tslint/tests/index.spec.ts b/packages/eslint-plugin-tslint/tests/index.spec.ts index 88a3a648b076..458120891024 100644 --- a/packages/eslint-plugin-tslint/tests/index.spec.ts +++ b/packages/eslint-plugin-tslint/tests/index.spec.ts @@ -12,7 +12,7 @@ const ruleTester = new TSESLint.RuleTester({ * Project is needed to generate the parserServices * within @typescript-eslint/parser */ - project: './tests/tsconfig.json', + project: './tests/fixture-project/tsconfig.json', }, parser: require.resolve('@typescript-eslint/parser'), }); @@ -47,6 +47,7 @@ ruleTester.run('tslint/config', rule, { { code: 'var foo = true;', options: tslintRulesConfig, + filename: './tests/fixture-project/1.ts', }, { filename: './tests/test-project/file-spec.ts', @@ -62,6 +63,7 @@ ruleTester.run('tslint/config', rule, { { code: 'throw "should be ok because rule is not loaded";', options: tslintRulesConfig, + filename: './tests/fixture-project/2.ts', }, ], @@ -69,6 +71,7 @@ ruleTester.run('tslint/config', rule, { { options: [{ lintFile: './tests/test-project/tslint.json' }], code: 'throw "err" // no-string-throw', + filename: './tests/fixture-project/3.ts', errors: [ { messageId: 'failure', @@ -84,6 +87,7 @@ ruleTester.run('tslint/config', rule, { code: 'var foo = true // semicolon', options: tslintRulesConfig, output: 'var foo = true // semicolon', + filename: './tests/fixture-project/4.ts', errors: [ { messageId: 'failure', @@ -100,6 +104,7 @@ ruleTester.run('tslint/config', rule, { code: 'var foo = true // fail', options: tslintRulesDirectoryConfig, output: 'var foo = true // fail', + filename: './tests/fixture-project/5.ts', errors: [ { messageId: 'failure', @@ -174,7 +179,7 @@ describe('tslint/error', () => { }); }); - it('should not crash if there is no tslint rules specified', () => { + it('barf', () => { const linter = new TSESLint.Linter(); jest.spyOn(console, 'warn').mockImplementation(); linter.defineRule('tslint/config', rule); diff --git a/packages/parser/tests/lib/parser.ts b/packages/parser/tests/lib/parser.ts index 9545633cd6e3..350d69946be0 100644 --- a/packages/parser/tests/lib/parser.ts +++ b/packages/parser/tests/lib/parser.ts @@ -48,12 +48,12 @@ describe('parser', () => { jsx: false, }, // ts-estree specific - filePath: 'test/foo', + filePath: 'tests/fixtures/services/isolated-file.src.ts', project: 'tsconfig.json', useJSXTextNode: false, errorOnUnknownASTType: false, errorOnTypeScriptSyntacticAndSemanticIssues: false, - tsconfigRootDir: './', + tsconfigRootDir: 'tests/fixtures/services', extraFileExtensions: ['foo'], }; parseForESLint(code, config); From dd811ab63b8b5875e4c013f8d74985ae51d7ff52 Mon Sep 17 00:00:00 2001 From: Ben Lichtman Date: Wed, 24 Jul 2019 21:29:00 -0700 Subject: [PATCH 4/9] test(eslint-plugin-tslint): fix remaining tests --- .../eslint-plugin-tslint/tests/index.spec.ts | 24 +++++++++++-------- .../tests/test-project/extra.ts | 1 + 2 files changed, 15 insertions(+), 10 deletions(-) create mode 100644 packages/eslint-plugin-tslint/tests/test-project/extra.ts diff --git a/packages/eslint-plugin-tslint/tests/index.spec.ts b/packages/eslint-plugin-tslint/tests/index.spec.ts index 458120891024..0de1046ecf3f 100644 --- a/packages/eslint-plugin-tslint/tests/index.spec.ts +++ b/packages/eslint-plugin-tslint/tests/index.spec.ts @@ -179,26 +179,30 @@ describe('tslint/error', () => { }); }); - it('barf', () => { + it('should not crash if there are no tslint rules specified', () => { const linter = new TSESLint.Linter(); jest.spyOn(console, 'warn').mockImplementation(); linter.defineRule('tslint/config', rule); linter.defineParser('@typescript-eslint/parser', parser); expect(() => - linter.verify('foo;', { - parserOptions: { - project: `${__dirname}/test-project/tsconfig.json`, - }, - rules: { - 'tslint/config': [2, {}], + linter.verify( + 'foo;', + { + parserOptions: { + project: `${__dirname}/test-project/tsconfig.json`, + }, + rules: { + 'tslint/config': [2, {}], + }, + parser: '@typescript-eslint/parser', }, - parser: '@typescript-eslint/parser', - }), + `${__dirname}/test-project/extra.ts`, + ), ).not.toThrow(); expect(console.warn).toHaveBeenCalledWith( expect.stringContaining( - 'Tried to lint but found no valid, enabled rules for this file type and file path in the resolved configuration.', + `Tried to lint ${__dirname}/test-project/extra.ts but found no valid, enabled rules for this file type and file path in the resolved configuration.`, ), ); jest.resetAllMocks(); diff --git a/packages/eslint-plugin-tslint/tests/test-project/extra.ts b/packages/eslint-plugin-tslint/tests/test-project/extra.ts new file mode 100644 index 000000000000..e901f01b4874 --- /dev/null +++ b/packages/eslint-plugin-tslint/tests/test-project/extra.ts @@ -0,0 +1 @@ +foo; From ae7c1293e97ef7002b163906f342d776db1498bd Mon Sep 17 00:00:00 2001 From: Ben Lichtman Date: Wed, 24 Jul 2019 21:35:10 -0700 Subject: [PATCH 5/9] test(eslint-plugin-tslint): fix test that prettier was breaking --- .prettierignore | 1 + packages/eslint-plugin-tslint/tests/fixture-project/4.ts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.prettierignore b/.prettierignore index 1dd0c3996671..a86a2f04fc90 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,4 +1,5 @@ **/tests/fixtures/**/* +**/tests/fixture-project/**/* **/dist **/coverage **/shared-fixtures diff --git a/packages/eslint-plugin-tslint/tests/fixture-project/4.ts b/packages/eslint-plugin-tslint/tests/fixture-project/4.ts index e1173e87a223..1ca8bbace361 100644 --- a/packages/eslint-plugin-tslint/tests/fixture-project/4.ts +++ b/packages/eslint-plugin-tslint/tests/fixture-project/4.ts @@ -1 +1 @@ -var foo = true; // semicolon +var foo = true // semicolon From 2c02bd1d9444fa6f082393e69dfeae3cf6ee8402 Mon Sep 17 00:00:00 2001 From: Ben Lichtman Date: Wed, 24 Jul 2019 21:42:49 -0700 Subject: [PATCH 6/9] docs(parser): update README with new error --- packages/parser/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/parser/README.md b/packages/parser/README.md index 78d17e6388fb..479f07a26c27 100644 --- a/packages/parser/README.md +++ b/packages/parser/README.md @@ -48,7 +48,7 @@ The following additional configuration options are available by specifying them - **`useJSXTextNode`** - default `true`. Please set `false` if you use this parser on ESLint v4. If this is `false`, the parser creates the AST of JSX texts as the legacy style. -- **`project`** - default `undefined`. This option allows you to provide a path to your project's `tsconfig.json`. **This setting is required if you want to use rules which require type information**. You may want to use this setting in tandem with the `tsconfigRootDir` option below. +- **`project`** - default `undefined`. This option allows you to provide a path to your project's `tsconfig.json`. **This setting is required if you want to use rules which require type information**. You may want to use this setting in tandem with the `tsconfigRootDir` option below. Note that if this setting is specified, you must only lint files that are included in the projects as defined by the provided `tsconfig.json` files. - **`tsconfigRootDir`** - default `undefined`. This option allows you to provide the root directory for relative tsconfig paths specified in the `project` option above. From 335be688ed46218dee046dec24fe4ac8add5dd70 Mon Sep 17 00:00:00 2001 From: Ben Lichtman Date: Wed, 24 Jul 2019 21:52:18 -0700 Subject: [PATCH 7/9] fix(typescript-estree): extract to constant --- packages/typescript-estree/src/parser.ts | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/packages/typescript-estree/src/parser.ts b/packages/typescript-estree/src/parser.ts index 1abb8d91d6db..02ea61a1de4b 100644 --- a/packages/typescript-estree/src/parser.ts +++ b/packages/typescript-estree/src/parser.ts @@ -64,26 +64,18 @@ function resetExtra(): void { * @returns If found, returns the source file corresponding to the code and the containing program */ function getASTFromProject(code: string, options: TSESTreeOptions) { + const filePath = options.filePath || getFileName(options); const astAndProgram = firstDefined( - calculateProjectParserOptions( - code, - options.filePath || getFileName(options), - extra, - ), + calculateProjectParserOptions(code, filePath, extra), currentProgram => { - const ast = currentProgram.getSourceFile( - options.filePath || getFileName(options), - ); + const ast = currentProgram.getSourceFile(filePath); return ast && { ast, program: currentProgram }; }, ); if (!astAndProgram) { throw new Error( - `If "parserOptions.project" has been set for @typescript-eslint/parser, ${options.filePath || - getFileName( - options, - )} must be included in at least one of the projects provided.`, + `If "parserOptions.project" has been set for @typescript-eslint/parser, ${filePath} must be included in at least one of the projects provided.`, ); } From b8e54171a0596dfd5ae5935fdb3e3835c2485679 Mon Sep 17 00:00:00 2001 From: Ben Lichtman Date: Wed, 24 Jul 2019 22:08:35 -0700 Subject: [PATCH 8/9] test: introduce local eslintrc for js files --- tests/integration/utils/.eslintrc.json | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 tests/integration/utils/.eslintrc.json diff --git a/tests/integration/utils/.eslintrc.json b/tests/integration/utils/.eslintrc.json new file mode 100644 index 000000000000..23278ae06213 --- /dev/null +++ b/tests/integration/utils/.eslintrc.json @@ -0,0 +1,5 @@ +{ + "parserOptions": { + "project": null + } +} From 9f03dd0fc0e261f2165e4e391eeaece1a2ecd2be Mon Sep 17 00:00:00 2001 From: Ben Lichtman Date: Thu, 25 Jul 2019 22:46:09 -0700 Subject: [PATCH 9/9] fix: add back default program --- packages/parser/README.md | 17 ++++++++- .../typescript-estree/src/parser-options.ts | 2 + packages/typescript-estree/src/parser.ts | 38 +++++++++++++++++-- packages/typescript-estree/tests/lib/parse.ts | 1 + .../tests/lib/semanticInfo.ts | 9 +++++ 5 files changed, 62 insertions(+), 5 deletions(-) diff --git a/packages/parser/README.md b/packages/parser/README.md index 479f07a26c27..45c620280f15 100644 --- a/packages/parser/README.md +++ b/packages/parser/README.md @@ -48,10 +48,25 @@ The following additional configuration options are available by specifying them - **`useJSXTextNode`** - default `true`. Please set `false` if you use this parser on ESLint v4. If this is `false`, the parser creates the AST of JSX texts as the legacy style. -- **`project`** - default `undefined`. This option allows you to provide a path to your project's `tsconfig.json`. **This setting is required if you want to use rules which require type information**. You may want to use this setting in tandem with the `tsconfigRootDir` option below. Note that if this setting is specified, you must only lint files that are included in the projects as defined by the provided `tsconfig.json` files. +- **`project`** - default `undefined`. This option allows you to provide a path to your project's `tsconfig.json`. **This setting is required if you want to use rules which require type information**. You may want to use this setting in tandem with the `tsconfigRootDir` option below. + + - Note that if this setting is specified and `createDefaultProgram` is not, you must only lint files that are included in the projects as defined by the provided `tsconfig.json` files. If your existing configuration does not include all of the files you would like to lint, you can create a separate `tsconfig.eslint.json` as follows: + + ```ts + { + "extends": "./tsconfig.json", // path to existing tsconfig + "includes": [ + "src/**/*.ts", + "test/**/*.ts", + // etc + ] + } + ``` - **`tsconfigRootDir`** - default `undefined`. This option allows you to provide the root directory for relative tsconfig paths specified in the `project` option above. +- **`createDefaultProgram`** - default `false`. This option allows you to request that when the `project` setting is specified, files will be allowed when not included in the projects defined by the provided `tsconfig.json` files. However, this may incur significant performance costs, so this option is primarily included for backwards-compatibility. See the **`project`** section for more information. + - **`extraFileExtensions`** - default `undefined`. This option allows you to provide one or more additional file extensions which should be considered in the TypeScript Program compilation. E.g. a `.vue` file - **`warnOnUnsupportedTypeScriptVersion`** - default `true`. This option allows you to toggle the warning that the parser will give you if you use a version of TypeScript which is not explicitly supported diff --git a/packages/typescript-estree/src/parser-options.ts b/packages/typescript-estree/src/parser-options.ts index c224b7da5659..77a649f6f309 100644 --- a/packages/typescript-estree/src/parser-options.ts +++ b/packages/typescript-estree/src/parser-options.ts @@ -18,6 +18,7 @@ export interface Extra { tsconfigRootDir: string; extraFileExtensions: string[]; preserveNodeMaps?: boolean; + createDefaultProgram: boolean; } export interface TSESTreeOptions { @@ -35,6 +36,7 @@ export interface TSESTreeOptions { tsconfigRootDir?: string; extraFileExtensions?: string[]; preserveNodeMaps?: boolean; + createDefaultProgram?: boolean; } // This lets us use generics to type the return value, and removes the need to diff --git a/packages/typescript-estree/src/parser.ts b/packages/typescript-estree/src/parser.ts index 5f9bdc0209c7..564114d7ccb7 100644 --- a/packages/typescript-estree/src/parser.ts +++ b/packages/typescript-estree/src/parser.ts @@ -6,7 +6,10 @@ import { firstDefined } from './node-utils'; import { Extra, TSESTreeOptions, ParserServices } from './parser-options'; import { getFirstSemanticOrSyntacticError } from './semantic-errors'; import { TSESTree } from './ts-estree'; -import { calculateProjectParserOptions } from './tsconfig-parser'; +import { + calculateProjectParserOptions, + createProgram, +} from './tsconfig-parser'; /** * This needs to be kept in sync with the top-level README.md in the @@ -55,6 +58,7 @@ function resetExtra(): void { tsconfigRootDir: process.cwd(), extraFileExtensions: [], preserveNodeMaps: undefined, + createDefaultProgram: false, }; } @@ -63,7 +67,11 @@ function resetExtra(): void { * @param options The config object * @returns If found, returns the source file corresponding to the code and the containing program */ -function getASTFromProject(code: string, options: TSESTreeOptions) { +function getASTFromProject( + code: string, + options: TSESTreeOptions, + createDefaultProgram: boolean, +) { const filePath = options.filePath || getFileName(options); const astAndProgram = firstDefined( calculateProjectParserOptions(code, filePath, extra), @@ -73,7 +81,7 @@ function getASTFromProject(code: string, options: TSESTreeOptions) { }, ); - if (!astAndProgram) { + if (!astAndProgram && !createDefaultProgram) { throw new Error( `If "parserOptions.project" has been set for @typescript-eslint/parser, ${filePath} must be included in at least one of the projects provided.`, ); @@ -82,6 +90,18 @@ function getASTFromProject(code: string, options: TSESTreeOptions) { return astAndProgram; } +/** + * @param code The code of the file being linted + * @param options The config object + * @returns If found, returns the source file corresponding to the code and the containing program + */ +function getASTAndDefaultProject(code: string, options: TSESTreeOptions) { + const fileName = options.filePath || getFileName(options); + const program = createProgram(code, fileName, extra); + const ast = program && program.getSourceFile(fileName); + return ast && { ast, program }; +} + /** * @param code The code of the file being linted * @returns Returns a new source file and program corresponding to the linted code @@ -149,9 +169,14 @@ function getProgramAndAST( code: string, options: TSESTreeOptions, shouldProvideParserServices: boolean, + createDefaultProgram: boolean, ) { return ( - (shouldProvideParserServices && getASTFromProject(code, options)) || + (shouldProvideParserServices && + getASTFromProject(code, options, createDefaultProgram)) || + (shouldProvideParserServices && + createDefaultProgram && + getASTAndDefaultProject(code, options)) || createNewProgram(code) ); } @@ -241,6 +266,10 @@ function applyParserOptionsToExtra(options: TSESTreeOptions): void { if (options.preserveNodeMaps === undefined && extra.projects.length > 0) { extra.preserveNodeMaps = true; } + + extra.createDefaultProgram = + typeof options.createDefaultProgram === 'boolean' && + options.createDefaultProgram; } function warnAboutTSVersion(): void { @@ -373,6 +402,7 @@ export function parseAndGenerateServices< code, options, shouldProvideParserServices, + extra.createDefaultProgram, ); /** * Determine whether or not two-way maps of converted AST nodes should be preserved diff --git a/packages/typescript-estree/tests/lib/parse.ts b/packages/typescript-estree/tests/lib/parse.ts index 1a05ed3dbfe5..57a4bc057424 100644 --- a/packages/typescript-estree/tests/lib/parse.ts +++ b/packages/typescript-estree/tests/lib/parse.ts @@ -94,6 +94,7 @@ describe('parse()', () => { tsconfigRootDir: expect.any(String), useJSXTextNode: false, preserveNodeMaps: false, + createDefaultProgram: false, }, false, ); diff --git a/packages/typescript-estree/tests/lib/semanticInfo.ts b/packages/typescript-estree/tests/lib/semanticInfo.ts index 4c432b3b5f7b..7e5c634db9d3 100644 --- a/packages/typescript-estree/tests/lib/semanticInfo.ts +++ b/packages/typescript-estree/tests/lib/semanticInfo.ts @@ -267,6 +267,15 @@ describe('semanticInfo', () => { parseCodeAndGenerateServices(readFileSync(fileName, 'utf8'), badConfig), ).toThrowErrorMatchingSnapshot(); }); + + it('default program produced with option', () => { + const parseResult = parseCodeAndGenerateServices('var foo = 5;', { + ...createOptions(''), + createDefaultProgram: true, + }); + + expect(parseResult.services.program).toBeDefined(); + }); }); function testIsolatedFile( pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy