From bf904ec72db57174fec531f61e9427230662553e Mon Sep 17 00:00:00 2001 From: Brad Zacher Date: Thu, 26 Nov 2020 13:32:40 -0800 Subject: [PATCH] fix(typescript-estree): add default value for `parserOptions.projectFolderIgnoreList` and deduplicate resolved projects (#2819) In #2418 I introduced a regression - I forgot to add in the default value for `projectFolderIgnoreList`. This means that globs have been matching `node_modules` since the v4.0 release! Oops :( This PR fixes that. It also hoists the tsconfig path canonicalisation up so that we can deduplicate early. Previously if you provided a config like `projects: ['./tsconfig.json', './**/tsconfig.json']`, then we would resolve this to `./tsconfig.json` and `tsconfig.json`, then later canonicalise them. This meant we'd check that same tsconfig twice! By hoisting the canonicalisation, we can deduplicate this early. Fixes #2814 --- .../create-program/createDefaultProgram.ts | 4 +- .../src/create-program/createWatchProgram.ts | 5 +- .../src/create-program/shared.ts | 5 -- .../typescript-estree/src/parser-options.ts | 3 +- packages/typescript-estree/src/parser.ts | 54 ++++++++++++------- 5 files changed, 39 insertions(+), 32 deletions(-) diff --git a/packages/typescript-estree/src/create-program/createDefaultProgram.ts b/packages/typescript-estree/src/create-program/createDefaultProgram.ts index e0aaf6253d7..c912a44418a 100644 --- a/packages/typescript-estree/src/create-program/createDefaultProgram.ts +++ b/packages/typescript-estree/src/create-program/createDefaultProgram.ts @@ -4,7 +4,7 @@ import * as ts from 'typescript'; import { Extra } from '../parser-options'; import { ASTAndProgram, - getTsconfigPath, + CanonicalPath, createDefaultCompilerOptionsFromExtra, } from './shared'; @@ -27,7 +27,7 @@ function createDefaultProgram( return undefined; } - const tsconfigPath = getTsconfigPath(extra.projects[0], extra); + const tsconfigPath: CanonicalPath = extra.projects[0]; const commandLine = ts.getParsedCommandLineOfConfigFile( tsconfigPath, diff --git a/packages/typescript-estree/src/create-program/createWatchProgram.ts b/packages/typescript-estree/src/create-program/createWatchProgram.ts index da9ff8384b7..e00663a114e 100644 --- a/packages/typescript-estree/src/create-program/createWatchProgram.ts +++ b/packages/typescript-estree/src/create-program/createWatchProgram.ts @@ -9,7 +9,6 @@ import { CanonicalPath, createDefaultCompilerOptionsFromExtra, getCanonicalFileName, - getTsconfigPath, } from './shared'; const log = debug('typescript-eslint:typescript-estree:createWatchProgram'); @@ -197,9 +196,7 @@ function getProgramsForProjects( * - the required program hasn't been created yet, or * - the file is new/renamed, and the program hasn't been updated. */ - for (const rawTsconfigPath of extra.projects) { - const tsconfigPath = getTsconfigPath(rawTsconfigPath, extra); - + for (const tsconfigPath of extra.projects) { const existingWatch = knownWatchProgramMap.get(tsconfigPath); if (existingWatch) { diff --git a/packages/typescript-estree/src/create-program/shared.ts b/packages/typescript-estree/src/create-program/shared.ts index 702e7884ccb..ae296252804 100644 --- a/packages/typescript-estree/src/create-program/shared.ts +++ b/packages/typescript-estree/src/create-program/shared.ts @@ -60,10 +60,6 @@ function ensureAbsolutePath(p: string, extra: Extra): string { : path.join(extra.tsconfigRootDir || process.cwd(), p); } -function getTsconfigPath(tsconfigPath: string, extra: Extra): CanonicalPath { - return getCanonicalFileName(ensureAbsolutePath(tsconfigPath, extra)); -} - function canonicalDirname(p: CanonicalPath): CanonicalPath { return path.dirname(p) as CanonicalPath; } @@ -105,5 +101,4 @@ export { ensureAbsolutePath, getCanonicalFileName, getScriptKind, - getTsconfigPath, }; diff --git a/packages/typescript-estree/src/parser-options.ts b/packages/typescript-estree/src/parser-options.ts index 33195b0c8d6..36ca3f80cca 100644 --- a/packages/typescript-estree/src/parser-options.ts +++ b/packages/typescript-estree/src/parser-options.ts @@ -1,6 +1,7 @@ import { DebugLevel } from '@typescript-eslint/types'; import { Program } from 'typescript'; import { TSESTree, TSNode, TSESTreeToTSNode, TSToken } from './ts-estree'; +import { CanonicalPath } from './create-program/shared'; type DebugModule = 'typescript-eslint' | 'eslint' | 'typescript'; @@ -19,7 +20,7 @@ export interface Extra { loc: boolean; log: (message: string) => void; preserveNodeMaps?: boolean; - projects: string[]; + projects: CanonicalPath[]; range: boolean; strict: boolean; tokens: null | TSESTree.Token[]; diff --git a/packages/typescript-estree/src/parser.ts b/packages/typescript-estree/src/parser.ts index 3dc22e262c8..dd765eb55aa 100644 --- a/packages/typescript-estree/src/parser.ts +++ b/packages/typescript-estree/src/parser.ts @@ -12,7 +12,12 @@ import { createSourceFile } from './create-program/createSourceFile'; import { Extra, TSESTreeOptions, ParserServices } from './parser-options'; import { getFirstSemanticOrSyntacticError } from './semantic-or-syntactic-errors'; import { TSESTree } from './ts-estree'; -import { ASTAndProgram, ensureAbsolutePath } from './create-program/shared'; +import { + ASTAndProgram, + CanonicalPath, + ensureAbsolutePath, + getCanonicalFileName, +} from './create-program/shared'; const log = debug('typescript-eslint:typescript-estree:parser'); @@ -109,46 +114,53 @@ function resetExtra(): void { }; } +function getTsconfigPath(tsconfigPath: string, extra: Extra): CanonicalPath { + return getCanonicalFileName(ensureAbsolutePath(tsconfigPath, extra)); +} + /** - * Normalizes, sanitizes, resolves and filters the provided + * Normalizes, sanitizes, resolves and filters the provided project paths */ function prepareAndTransformProjects( projectsInput: string | string[] | undefined, ignoreListInput: string[], -): string[] { - let projects: string[] = []; +): CanonicalPath[] { + const sanitizedProjects: string[] = []; // Normalize and sanitize the project paths if (typeof projectsInput === 'string') { - projects.push(projectsInput); + sanitizedProjects.push(projectsInput); } else if (Array.isArray(projectsInput)) { for (const project of projectsInput) { if (typeof project === 'string') { - projects.push(project); + sanitizedProjects.push(project); } } } - if (projects.length === 0) { - return projects; + if (sanitizedProjects.length === 0) { + return []; } // Transform glob patterns into paths - const globbedProjects = projects.filter(project => isGlob(project)); - projects = projects - .filter(project => !isGlob(project)) - .concat( - globSync([...globbedProjects, ...ignoreListInput], { - cwd: extra.tsconfigRootDir, - }), - ); + const nonGlobProjects = sanitizedProjects.filter(project => !isGlob(project)); + const globProjects = sanitizedProjects.filter(project => isGlob(project)); + const uniqueCanonicalProjectPaths = new Set( + nonGlobProjects + .concat( + globSync([...globProjects, ...ignoreListInput], { + cwd: extra.tsconfigRootDir, + }), + ) + .map(project => getTsconfigPath(project, extra)), + ); log( 'parserOptions.project (excluding ignored) matched projects: %s', - projects, + uniqueCanonicalProjectPaths, ); - return projects; + return Array.from(uniqueCanonicalProjectPaths); } function applyParserOptionsToExtra(options: TSESTreeOptions): void { @@ -252,8 +264,9 @@ function applyParserOptionsToExtra(options: TSESTreeOptions): void { // NOTE - ensureAbsolutePath relies upon having the correct tsconfigRootDir in extra extra.filePath = ensureAbsolutePath(extra.filePath, extra); - // NOTE - prepareAndTransformProjects relies upon having the correct tsconfigRootDir in extra - const projectFolderIgnoreList = (options.projectFolderIgnoreList ?? []) + const projectFolderIgnoreList = ( + options.projectFolderIgnoreList ?? ['**/node_modules/**'] + ) .reduce((acc, folder) => { if (typeof folder === 'string') { acc.push(folder); @@ -262,6 +275,7 @@ function applyParserOptionsToExtra(options: TSESTreeOptions): void { }, []) // prefix with a ! for not match glob .map(folder => (folder.startsWith('!') ? folder : `!${folder}`)); + // NOTE - prepareAndTransformProjects relies upon having the correct tsconfigRootDir in extra extra.projects = prepareAndTransformProjects( options.project, projectFolderIgnoreList,