From 3777b770670440c3e47b451d862e5cb57b79e40d Mon Sep 17 00:00:00 2001 From: Ben Lichtman Date: Sun, 28 Jul 2019 11:15:07 -0700 Subject: [PATCH] feat(typescript-estree)!: throw error on file not in project when `project` set (#760) BREAKING CHANGE: by default we will now throw when a file is not in the `project` provided --- .prettierignore | 1 + .../tests/fixture-project/1.ts | 1 + .../tests/fixture-project/2.ts | 1 + .../tests/fixture-project/3.ts | 1 + .../tests/fixture-project/4.ts | 1 + .../tests/fixture-project/5.ts | 1 + .../tests/fixture-project/6.ts | 1 + .../tests/fixture-project/tsconfig.json | 1 + .../eslint-plugin-tslint/tests/index.spec.ts | 31 ++++++++---- .../tests/test-project/extra.ts | 1 + packages/eslint-plugin/tests/RuleTester.ts | 25 ++++++++++ packages/eslint-plugin/tests/fixtures/file.ts | 0 .../rules/no-unnecessary-qualifier.test.ts | 1 - packages/parser/README.md | 15 ++++++ packages/parser/tests/lib/parser.ts | 4 +- .../typescript-estree/src/parser-options.ts | 2 + packages/typescript-estree/src/parser.ts | 41 ++++++++++----- .../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 | 26 +++++++--- .../tests/lib/semanticInfo.ts | 50 ++++++++++++------- tests/integration/utils/.eslintrc.js | 5 ++ tests/integration/utils/jsconfig.json | 3 ++ 24 files changed, 173 insertions(+), 50 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 create mode 100644 packages/eslint-plugin-tslint/tests/test-project/extra.ts create mode 100644 packages/eslint-plugin/tests/fixtures/file.ts create mode 100644 packages/typescript-estree/tests/fixtures/simpleProject/file.ts create mode 100644 packages/typescript-estree/tests/fixtures/simpleProject/tsconfig.json create mode 100644 tests/integration/utils/.eslintrc.js create mode 100644 tests/integration/utils/jsconfig.json diff --git a/.prettierignore b/.prettierignore index 1dd0c399667..a86a2f04fc9 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/1.ts b/packages/eslint-plugin-tslint/tests/fixture-project/1.ts new file mode 100644 index 00000000000..0870b5cd282 --- /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 00000000000..3da535b2cde --- /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 00000000000..e71f830c391 --- /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 00000000000..1ca8bbace36 --- /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 00000000000..2fc07810720 --- /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 00000000000..e901f01b487 --- /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 00000000000..0967ef424bc --- /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 88a3a648b07..0de1046ecf3 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,26 +179,30 @@ describe('tslint/error', () => { }); }); - it('should not crash if there is no tslint rules specified', () => { + 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 00000000000..e901f01b487 --- /dev/null +++ b/packages/eslint-plugin-tslint/tests/test-project/extra.ts @@ -0,0 +1 @@ +foo; diff --git a/packages/eslint-plugin/tests/RuleTester.ts b/packages/eslint-plugin/tests/RuleTester.ts index 0733e1887cb..fe3d06d4fc8 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 00000000000..e69de29bb2d 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 53a349e552e..75236704c85 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, diff --git a/packages/parser/README.md b/packages/parser/README.md index 78d17e6388f..45c620280f1 100644 --- a/packages/parser/README.md +++ b/packages/parser/README.md @@ -50,8 +50,23 @@ The following additional configuration options are available by specifying them - **`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/parser/tests/lib/parser.ts b/packages/parser/tests/lib/parser.ts index 0a00ef5ad15..782d474afc3 100644 --- a/packages/parser/tests/lib/parser.ts +++ b/packages/parser/tests/lib/parser.ts @@ -50,12 +50,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); diff --git a/packages/typescript-estree/src/parser-options.ts b/packages/typescript-estree/src/parser-options.ts index c224b7da565..77a649f6f30 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 aed8f04b8d2..564114d7ccb 100644 --- a/packages/typescript-estree/src/parser.ts +++ b/packages/typescript-estree/src/parser.ts @@ -58,6 +58,7 @@ function resetExtra(): void { tsconfigRootDir: process.cwd(), extraFileExtensions: [], preserveNodeMaps: undefined, + createDefaultProgram: false, }; } @@ -66,20 +67,27 @@ 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) { - return firstDefined( - calculateProjectParserOptions( - code, - options.filePath || getFileName(options), - extra, - ), +function getASTFromProject( + code: string, + options: TSESTreeOptions, + createDefaultProgram: boolean, +) { + const filePath = options.filePath || getFileName(options); + const astAndProgram = firstDefined( + 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 && !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.`, + ); + } + + return astAndProgram; } /** @@ -161,10 +169,14 @@ function getProgramAndAST( code: string, options: TSESTreeOptions, shouldProvideParserServices: boolean, + createDefaultProgram: boolean, ) { return ( - (shouldProvideParserServices && getASTFromProject(code, options)) || - (shouldProvideParserServices && getASTAndDefaultProject(code, options)) || + (shouldProvideParserServices && + getASTFromProject(code, options, createDefaultProgram)) || + (shouldProvideParserServices && + createDefaultProgram && + getASTAndDefaultProject(code, options)) || createNewProgram(code) ); } @@ -254,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 { @@ -386,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/src/tsconfig-parser.ts b/packages/typescript-estree/src/tsconfig-parser.ts index 855355fe689..09199053349 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 00000000000..e69de29bb2d 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 00000000000..0967ef424bc --- /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 3d8dfe222e4..57a4bc05742 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', () => { @@ -91,6 +94,7 @@ describe('parse()', () => { tsconfigRootDir: expect.any(String), useJSXTextNode: false, preserveNodeMaps: false, + createDefaultProgram: false, }, false, ); @@ -137,6 +141,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()', () => { @@ -167,10 +177,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), @@ -194,9 +204,8 @@ describe('parse()', () => { ); const withProjectOptionSetToTrue = parser.parseAndGenerateServices(code, { - ...baseConfig, + ...projectConfig, preserveNodeMaps: true, - project: './tsconfig.json', }); expect(withProjectOptionSetToTrue.services.esTreeNodeToTSNodeMap).toEqual( @@ -218,7 +227,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 3f0a69b3053..7e5c634db9d 100644 --- a/packages/typescript-estree/tests/lib/semanticInfo.ts +++ b/packages/typescript-estree/tests/lib/semanticInfo.ts @@ -13,6 +13,7 @@ import { ParseAndGenerateServicesResult, } 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`); @@ -28,11 +29,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 => { @@ -193,12 +197,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.body[0] as TSESTree.VariableDeclaration) @@ -210,8 +216,6 @@ describe('semanticInfo', () => { ); expect(tsBoundName).toBeDefined(); - checkNumberArrayType(checker, tsBoundName); - expect(parseResult.services.tsNodeToESTreeNodeMap!.get(tsBoundName)).toBe( boundName, ); @@ -220,18 +224,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', () => { @@ -260,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( diff --git a/tests/integration/utils/.eslintrc.js b/tests/integration/utils/.eslintrc.js new file mode 100644 index 00000000000..8ca32766a12 --- /dev/null +++ b/tests/integration/utils/.eslintrc.js @@ -0,0 +1,5 @@ +module.exports = { + parserOptions: { + project: `${__dirname}/jsconfig.json`, + }, +}; diff --git a/tests/integration/utils/jsconfig.json b/tests/integration/utils/jsconfig.json new file mode 100644 index 00000000000..d53d21eadfb --- /dev/null +++ b/tests/integration/utils/jsconfig.json @@ -0,0 +1,3 @@ +{ + "exclude": [".eslintrc.js"] +}