Skip to content

Commit 1e29e69

Browse files
authored
feat(typescript-estree): add option to ignore certain folders from glob resolution (#1802)
1 parent 7d963fd commit 1e29e69

File tree

11 files changed

+213
-64
lines changed

11 files changed

+213
-64
lines changed

Diff for: packages/experimental-utils/src/ts-eslint/ParserOptions.ts

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ interface ParserOptions {
1616
loc?: boolean;
1717
noWatch?: boolean;
1818
project?: string | string[];
19+
projectFolderIgnoreList?: (string | RegExp)[];
1920
range?: boolean;
2021
sourceType?: 'script' | 'module';
2122
tokens?: boolean;

Diff for: packages/parser/README.md

+15-4
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ interface ParserOptions {
5454
jsx?: boolean;
5555
};
5656
project?: string | string[];
57+
projectFolderIgnoreList?: (string | RegExp)[];
5758
tsconfigRootDir?: string;
5859
extraFileExtensions?: string[];
5960
warnOnUnsupportedTypeScriptVersion?: boolean;
@@ -118,26 +119,36 @@ This option allows you to provide a path to your project's `tsconfig.json`. **Th
118119
}
119120
```
120121

121-
### `tsconfigRootDir`
122+
### `parserOptions.tsconfigRootDir`
122123

123124
Default `undefined`.
124125

125126
This option allows you to provide the root directory for relative tsconfig paths specified in the `project` option above.
126127

127-
### `extraFileExtensions`
128+
### `parserOptions.projectFolderIgnoreList`
129+
130+
Default `["/node_modules/"]`.
131+
132+
This option allows you to ignore folders from being included in your provided list of `project`s.
133+
Any resolved project path that matches one or more of the provided regular expressions will be removed from the list.
134+
This is useful if you have configured glob patterns, but want to make sure you ignore certain folders.
135+
136+
For example, by default it will ensure that a glob like `./**/tsconfig.json` will not match any `tsconfig`s within your `node_modules` folder (some npm packages do not exclude their source files from their published packages).
137+
138+
### `parserOptions.extraFileExtensions`
128139

129140
Default `undefined`.
130141

131142
This option allows you to provide one or more additional file extensions which should be considered in the TypeScript Program compilation.
132143
The default extensions are `.ts`, `.tsx`, `.js`, and `.jsx`. Add extensions starting with `.`, followed by the file extension. E.g. for a `.vue` file use `"extraFileExtensions: [".vue"]`.
133144

134-
### `warnOnUnsupportedTypeScriptVersion`
145+
### `parserOptions.warnOnUnsupportedTypeScriptVersion`
135146

136147
Default `true`.
137148

138149
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
139150

140-
### `createDefaultProgram`
151+
### `parserOptions.createDefaultProgram`
141152

142153
Default `false`.
143154

Diff for: packages/typescript-estree/README.md

+11
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,16 @@ interface ParseAndGenerateServicesOptions extends ParseOptions {
182182
*/
183183
project?: string | string[];
184184

185+
/**
186+
* If you provide a glob (or globs) to the project option, you can use this option to blacklist
187+
* certain folders from being matched by the globs.
188+
* Any project path that matches one or more of the provided regular expressions will be removed from the list.
189+
*
190+
* Accepts an array of strings that are passed to new RegExp(), or an array of regular expressions.
191+
* By default, this is set to ["/node_modules/"]
192+
*/
193+
projectFolderIgnoreList?: (string | RegExp)[];
194+
185195
/**
186196
* The absolute path to the root directory for all provided `project`s.
187197
*/
@@ -205,6 +215,7 @@ const PARSE_AND_GENERATE_SERVICES_DEFAULT_OPTIONS: ParseOptions = {
205215
extraFileExtensions: [],
206216
preserveNodeMaps: false, // or true, if you do not set this, but pass `project`
207217
project: undefined,
218+
projectFolderIgnoreList: ['/node_modules/'],
208219
tsconfigRootDir: process.cwd(),
209220
};
210221

Diff for: packages/typescript-estree/src/parser-options.ts

+55-36
Original file line numberDiff line numberDiff line change
@@ -29,28 +29,23 @@ export interface Extra {
2929
// MAKE SURE THIS IS KEPT IN SYNC WITH THE README //
3030
////////////////////////////////////////////////////
3131

32-
export interface TSESTreeOptions {
32+
interface ParseOptions {
3333
/**
3434
* create a top-level comments array containing all comments
3535
*/
3636
comment?: boolean;
3737

3838
/**
39-
* For convenience:
40-
* - true === ['typescript-eslint']
41-
* - false === []
42-
*
4339
* An array of modules to turn explicit debugging on for.
4440
* - 'typescript-eslint' is the same as setting the env var `DEBUG=typescript-eslint:*`
4541
* - 'eslint' is the same as setting the env var `DEBUG=eslint:*`
4642
* - 'typescript' is the same as setting `extendedDiagnostics: true` in your tsconfig compilerOptions
43+
*
44+
* For convenience, also supports a boolean:
45+
* - true === ['typescript-eslint']
46+
* - false === []
4747
*/
48-
debugLevel?: boolean | DebugModule[];
49-
50-
/**
51-
* Causes the parser to error if the TypeScript compiler returns any unexpected syntax/semantic errors.
52-
*/
53-
errorOnTypeScriptSyntacticAndSemanticIssues?: boolean;
48+
debugLevel?: boolean | ('typescript-eslint' | 'eslint' | 'typescript')[];
5449

5550
/**
5651
* Cause the parser to error if it encounters an unknown AST node type (useful for testing).
@@ -59,14 +54,7 @@ export interface TSESTreeOptions {
5954
errorOnUnknownASTType?: boolean;
6055

6156
/**
62-
* When `project` is provided, this controls the non-standard file extensions which will be parsed.
63-
* It accepts an array of file extensions, each preceded by a `.`.
64-
*/
65-
extraFileExtensions?: string[];
66-
67-
/**
68-
* Absolute (or relative to `tsconfigRootDir`) path to the file being parsed.
69-
* When `project` is provided, this is required, as it is used to fetch the file from the TypeScript compiler's cache.
57+
* Absolute (or relative to `cwd`) path to the file being parsed.
7058
*/
7159
filePath?: string;
7260

@@ -95,6 +83,45 @@ export interface TSESTreeOptions {
9583
*/
9684
loggerFn?: Function | false;
9785

86+
/**
87+
* Controls whether the `range` property is included on AST nodes.
88+
* The `range` property is a [number, number] which indicates the start/end index of the node in the file contents.
89+
* This is similar to the `loc` property, except this is the absolute index.
90+
*/
91+
range?: boolean;
92+
93+
/**
94+
* Set to true to create a top-level array containing all tokens from the file.
95+
*/
96+
tokens?: boolean;
97+
98+
/*
99+
* The JSX AST changed the node type for string literals
100+
* inside a JSX Element from `Literal` to `JSXText`.
101+
* When value is `true`, these nodes will be parsed as type `JSXText`.
102+
* When value is `false`, these nodes will be parsed as type `Literal`.
103+
*/
104+
useJSXTextNode?: boolean;
105+
}
106+
107+
interface ParseAndGenerateServicesOptions extends ParseOptions {
108+
/**
109+
* Causes the parser to error if the TypeScript compiler returns any unexpected syntax/semantic errors.
110+
*/
111+
errorOnTypeScriptSyntacticAndSemanticIssues?: boolean;
112+
113+
/**
114+
* When `project` is provided, this controls the non-standard file extensions which will be parsed.
115+
* It accepts an array of file extensions, each preceded by a `.`.
116+
*/
117+
extraFileExtensions?: string[];
118+
119+
/**
120+
* Absolute (or relative to `tsconfigRootDir`) path to the file being parsed.
121+
* When `project` is provided, this is required, as it is used to fetch the file from the TypeScript compiler's cache.
122+
*/
123+
filePath?: string;
124+
98125
/**
99126
* Allows the user to control whether or not two-way AST node maps are preserved
100127
* during the AST conversion process.
@@ -114,30 +141,20 @@ export interface TSESTreeOptions {
114141
project?: string | string[];
115142

116143
/**
117-
* Controls whether the `range` property is included on AST nodes.
118-
* The `range` property is a [number, number] which indicates the start/end index of the node in the file contents.
119-
* This is similar to the `loc` property, except this is the absolute index.
120-
*/
121-
range?: boolean;
122-
123-
/**
124-
* Set to true to create a top-level array containing all tokens from the file.
144+
* If you provide a glob (or globs) to the project option, you can use this option to blacklist
145+
* certain folders from being matched by the globs.
146+
* Any project path that matches one or more of the provided regular expressions will be removed from the list.
147+
*
148+
* Accepts an array of strings that are passed to new RegExp(), or an array of regular expressions.
149+
* By default, this is set to ["/node_modules/"]
125150
*/
126-
tokens?: boolean;
151+
projectFolderIgnoreList?: (string | RegExp)[];
127152

128153
/**
129154
* The absolute path to the root directory for all provided `project`s.
130155
*/
131156
tsconfigRootDir?: string;
132157

133-
/*
134-
* The JSX AST changed the node type for string literals
135-
* inside a JSX Element from `Literal` to `JSXText`.
136-
* When value is `true`, these nodes will be parsed as type `JSXText`.
137-
* When value is `false`, these nodes will be parsed as type `Literal`.
138-
*/
139-
useJSXTextNode?: boolean;
140-
141158
/**
142159
***************************************************************************************
143160
* IT IS RECOMMENDED THAT YOU DO NOT USE THIS OPTION, AS IT CAUSES PERFORMANCE ISSUES. *
@@ -150,6 +167,8 @@ export interface TSESTreeOptions {
150167
createDefaultProgram?: boolean;
151168
}
152169

170+
export type TSESTreeOptions = ParseAndGenerateServicesOptions;
171+
153172
// This lets us use generics to type the return value, and removes the need to
154173
// handle the undefined type in the get method
155174
export interface ParserWeakMap<TKey, TValueBase> {

Diff for: packages/typescript-estree/src/parser.ts

+77-23
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ import { getFirstSemanticOrSyntacticError } from './semantic-or-syntactic-errors
1414
import { TSESTree } from './ts-estree';
1515
import { ensureAbsolutePath } from './create-program/shared';
1616

17+
const log = debug('typescript-eslint:typescript-estree:parser');
18+
1719
/**
1820
* This needs to be kept in sync with the top-level README.md in the
1921
* typescript-eslint monorepo
@@ -111,6 +113,74 @@ function resetExtra(): void {
111113
};
112114
}
113115

116+
/**
117+
* Normalizes, sanitizes, resolves and filters the provided
118+
*/
119+
function prepareAndTransformProjects(
120+
projectsInput: string | string[] | undefined,
121+
ignoreListInput: (string | RegExp)[] | undefined,
122+
): string[] {
123+
let projects: string[] = [];
124+
125+
// Normalize and sanitize the project paths
126+
if (typeof projectsInput === 'string') {
127+
projects.push(projectsInput);
128+
} else if (Array.isArray(projectsInput)) {
129+
for (const project of projectsInput) {
130+
if (typeof project === 'string') {
131+
projects.push(project);
132+
}
133+
}
134+
}
135+
136+
if (projects.length === 0) {
137+
return projects;
138+
}
139+
140+
// Transform glob patterns into paths
141+
projects = projects.reduce<string[]>(
142+
(projects, project) =>
143+
projects.concat(
144+
isGlob(project)
145+
? globSync(project, {
146+
cwd: extra.tsconfigRootDir,
147+
})
148+
: project,
149+
),
150+
[],
151+
);
152+
153+
// Normalize and sanitize the ignore regex list
154+
const ignoreRegexes: RegExp[] = [];
155+
if (Array.isArray(ignoreListInput)) {
156+
for (const ignore of ignoreListInput) {
157+
if (ignore instanceof RegExp) {
158+
ignoreRegexes.push(ignore);
159+
} else if (typeof ignore === 'string') {
160+
ignoreRegexes.push(new RegExp(ignore));
161+
}
162+
}
163+
} else {
164+
ignoreRegexes.push(/\/node_modules\//);
165+
}
166+
167+
// Remove any paths that match the ignore list
168+
const filtered = projects.filter(project => {
169+
for (const ignore of ignoreRegexes) {
170+
if (ignore.test(project)) {
171+
return false;
172+
}
173+
}
174+
175+
return true;
176+
});
177+
178+
log('parserOptions.project matched projects: %s', projects);
179+
log('ignore list applied to parserOptions.project: %s', filtered);
180+
181+
return filtered;
182+
}
183+
114184
function applyParserOptionsToExtra(options: TSESTreeOptions): void {
115185
/**
116186
* Configure Debug logging
@@ -205,34 +275,18 @@ function applyParserOptionsToExtra(options: TSESTreeOptions): void {
205275
extra.log = Function.prototype;
206276
}
207277

208-
if (typeof options.project === 'string') {
209-
extra.projects = [options.project];
210-
} else if (
211-
Array.isArray(options.project) &&
212-
options.project.every(projectPath => typeof projectPath === 'string')
213-
) {
214-
extra.projects = options.project;
215-
}
216-
217278
if (typeof options.tsconfigRootDir === 'string') {
218279
extra.tsconfigRootDir = options.tsconfigRootDir;
219280
}
281+
282+
// NOTE - ensureAbsolutePath relies upon having the correct tsconfigRootDir in extra
220283
extra.filePath = ensureAbsolutePath(extra.filePath, extra);
221284

222-
// Transform glob patterns into paths
223-
if (extra.projects) {
224-
extra.projects = extra.projects.reduce<string[]>(
225-
(projects, project) =>
226-
projects.concat(
227-
isGlob(project)
228-
? globSync(project, {
229-
cwd: extra.tsconfigRootDir || process.cwd(),
230-
})
231-
: project,
232-
),
233-
[],
234-
);
235-
}
285+
// NOTE - prepareAndTransformProjects relies upon having the correct tsconfigRootDir in extra
286+
extra.projects = prepareAndTransformProjects(
287+
options.project,
288+
options.projectFolderIgnoreList,
289+
);
236290

237291
if (
238292
Array.isArray(options.extraFileExtensions) &&
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const x = 1;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"include": ["./file.ts"]
3+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const x = 2;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"include": ["./file.ts"]
3+
}

0 commit comments

Comments
 (0)