feat(typescript-estree): add option to ignore certain folders from glob resolution (#1802)

This commit is contained in:
Brad Zacher
2020-03-27 09:31:51 -07:00
committed by GitHub
parent 1075383d0d
commit a644a57571
11 changed files with 213 additions and 64 deletions
@@ -16,6 +16,7 @@ interface ParserOptions {
loc?: boolean;
noWatch?: boolean;
project?: string | string[];
projectFolderIgnoreList?: (string | RegExp)[];
range?: boolean;
sourceType?: 'script' | 'module';
tokens?: boolean;
+15 -4
View File
@@ -54,6 +54,7 @@ interface ParserOptions {
jsx?: boolean;
};
project?: string | string[];
projectFolderIgnoreList?: (string | RegExp)[];
tsconfigRootDir?: string;
extraFileExtensions?: string[];
warnOnUnsupportedTypeScriptVersion?: boolean;
@@ -118,26 +119,36 @@ This option allows you to provide a path to your project's `tsconfig.json`. **Th
}
```
### `tsconfigRootDir`
### `parserOptions.tsconfigRootDir`
Default `undefined`.
This option allows you to provide the root directory for relative tsconfig paths specified in the `project` option above.
### `extraFileExtensions`
### `parserOptions.projectFolderIgnoreList`
Default `["/node_modules/"]`.
This option allows you to ignore folders from being included in your provided list of `project`s.
Any resolved project path that matches one or more of the provided regular expressions will be removed from the list.
This is useful if you have configured glob patterns, but want to make sure you ignore certain folders.
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).
### `parserOptions.extraFileExtensions`
Default `undefined`.
This option allows you to provide one or more additional file extensions which should be considered in the TypeScript Program compilation.
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"]`.
### `warnOnUnsupportedTypeScriptVersion`
### `parserOptions.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
### `createDefaultProgram`
### `parserOptions.createDefaultProgram`
Default `false`.
+11
View File
@@ -182,6 +182,16 @@ interface ParseAndGenerateServicesOptions extends ParseOptions {
*/
project?: string | string[];
/**
* If you provide a glob (or globs) to the project option, you can use this option to blacklist
* certain folders from being matched by the globs.
* Any project path that matches one or more of the provided regular expressions will be removed from the list.
*
* Accepts an array of strings that are passed to new RegExp(), or an array of regular expressions.
* By default, this is set to ["/node_modules/"]
*/
projectFolderIgnoreList?: (string | RegExp)[];
/**
* The absolute path to the root directory for all provided `project`s.
*/
@@ -205,6 +215,7 @@ const PARSE_AND_GENERATE_SERVICES_DEFAULT_OPTIONS: ParseOptions = {
extraFileExtensions: [],
preserveNodeMaps: false, // or true, if you do not set this, but pass `project`
project: undefined,
projectFolderIgnoreList: ['/node_modules/'],
tsconfigRootDir: process.cwd(),
};
@@ -29,28 +29,23 @@ export interface Extra {
// MAKE SURE THIS IS KEPT IN SYNC WITH THE README //
////////////////////////////////////////////////////
export interface TSESTreeOptions {
interface ParseOptions {
/**
* create a top-level comments array containing all comments
*/
comment?: boolean;
/**
* For convenience:
* - true === ['typescript-eslint']
* - false === []
*
* An array of modules to turn explicit debugging on for.
* - 'typescript-eslint' is the same as setting the env var `DEBUG=typescript-eslint:*`
* - 'eslint' is the same as setting the env var `DEBUG=eslint:*`
* - 'typescript' is the same as setting `extendedDiagnostics: true` in your tsconfig compilerOptions
*
* For convenience, also supports a boolean:
* - true === ['typescript-eslint']
* - false === []
*/
debugLevel?: boolean | DebugModule[];
/**
* Causes the parser to error if the TypeScript compiler returns any unexpected syntax/semantic errors.
*/
errorOnTypeScriptSyntacticAndSemanticIssues?: boolean;
debugLevel?: boolean | ('typescript-eslint' | 'eslint' | 'typescript')[];
/**
* Cause the parser to error if it encounters an unknown AST node type (useful for testing).
@@ -59,14 +54,7 @@ export interface TSESTreeOptions {
errorOnUnknownASTType?: boolean;
/**
* When `project` is provided, this controls the non-standard file extensions which will be parsed.
* It accepts an array of file extensions, each preceded by a `.`.
*/
extraFileExtensions?: string[];
/**
* Absolute (or relative to `tsconfigRootDir`) path to the file being parsed.
* When `project` is provided, this is required, as it is used to fetch the file from the TypeScript compiler's cache.
* Absolute (or relative to `cwd`) path to the file being parsed.
*/
filePath?: string;
@@ -95,6 +83,45 @@ export interface TSESTreeOptions {
*/
loggerFn?: Function | false;
/**
* Controls whether the `range` property is included on AST nodes.
* The `range` property is a [number, number] which indicates the start/end index of the node in the file contents.
* This is similar to the `loc` property, except this is the absolute index.
*/
range?: boolean;
/**
* Set to true to create a top-level array containing all tokens from the file.
*/
tokens?: boolean;
/*
* The JSX AST changed the node type for string literals
* inside a JSX Element from `Literal` to `JSXText`.
* When value is `true`, these nodes will be parsed as type `JSXText`.
* When value is `false`, these nodes will be parsed as type `Literal`.
*/
useJSXTextNode?: boolean;
}
interface ParseAndGenerateServicesOptions extends ParseOptions {
/**
* Causes the parser to error if the TypeScript compiler returns any unexpected syntax/semantic errors.
*/
errorOnTypeScriptSyntacticAndSemanticIssues?: boolean;
/**
* When `project` is provided, this controls the non-standard file extensions which will be parsed.
* It accepts an array of file extensions, each preceded by a `.`.
*/
extraFileExtensions?: string[];
/**
* Absolute (or relative to `tsconfigRootDir`) path to the file being parsed.
* When `project` is provided, this is required, as it is used to fetch the file from the TypeScript compiler's cache.
*/
filePath?: string;
/**
* Allows the user to control whether or not two-way AST node maps are preserved
* during the AST conversion process.
@@ -114,30 +141,20 @@ export interface TSESTreeOptions {
project?: string | string[];
/**
* Controls whether the `range` property is included on AST nodes.
* The `range` property is a [number, number] which indicates the start/end index of the node in the file contents.
* This is similar to the `loc` property, except this is the absolute index.
* If you provide a glob (or globs) to the project option, you can use this option to blacklist
* certain folders from being matched by the globs.
* Any project path that matches one or more of the provided regular expressions will be removed from the list.
*
* Accepts an array of strings that are passed to new RegExp(), or an array of regular expressions.
* By default, this is set to ["/node_modules/"]
*/
range?: boolean;
/**
* Set to true to create a top-level array containing all tokens from the file.
*/
tokens?: boolean;
projectFolderIgnoreList?: (string | RegExp)[];
/**
* The absolute path to the root directory for all provided `project`s.
*/
tsconfigRootDir?: string;
/*
* The JSX AST changed the node type for string literals
* inside a JSX Element from `Literal` to `JSXText`.
* When value is `true`, these nodes will be parsed as type `JSXText`.
* When value is `false`, these nodes will be parsed as type `Literal`.
*/
useJSXTextNode?: boolean;
/**
***************************************************************************************
* IT IS RECOMMENDED THAT YOU DO NOT USE THIS OPTION, AS IT CAUSES PERFORMANCE ISSUES. *
@@ -150,6 +167,8 @@ export interface TSESTreeOptions {
createDefaultProgram?: boolean;
}
export type TSESTreeOptions = ParseAndGenerateServicesOptions;
// This lets us use generics to type the return value, and removes the need to
// handle the undefined type in the get method
export interface ParserWeakMap<TKey, TValueBase> {
+77 -23
View File
@@ -14,6 +14,8 @@ import { getFirstSemanticOrSyntacticError } from './semantic-or-syntactic-errors
import { TSESTree } from './ts-estree';
import { ensureAbsolutePath } from './create-program/shared';
const log = debug('typescript-eslint:typescript-estree:parser');
/**
* This needs to be kept in sync with the top-level README.md in the
* typescript-eslint monorepo
@@ -111,6 +113,74 @@ function resetExtra(): void {
};
}
/**
* Normalizes, sanitizes, resolves and filters the provided
*/
function prepareAndTransformProjects(
projectsInput: string | string[] | undefined,
ignoreListInput: (string | RegExp)[] | undefined,
): string[] {
let projects: string[] = [];
// Normalize and sanitize the project paths
if (typeof projectsInput === 'string') {
projects.push(projectsInput);
} else if (Array.isArray(projectsInput)) {
for (const project of projectsInput) {
if (typeof project === 'string') {
projects.push(project);
}
}
}
if (projects.length === 0) {
return projects;
}
// Transform glob patterns into paths
projects = projects.reduce<string[]>(
(projects, project) =>
projects.concat(
isGlob(project)
? globSync(project, {
cwd: extra.tsconfigRootDir,
})
: project,
),
[],
);
// Normalize and sanitize the ignore regex list
const ignoreRegexes: RegExp[] = [];
if (Array.isArray(ignoreListInput)) {
for (const ignore of ignoreListInput) {
if (ignore instanceof RegExp) {
ignoreRegexes.push(ignore);
} else if (typeof ignore === 'string') {
ignoreRegexes.push(new RegExp(ignore));
}
}
} else {
ignoreRegexes.push(/\/node_modules\//);
}
// Remove any paths that match the ignore list
const filtered = projects.filter(project => {
for (const ignore of ignoreRegexes) {
if (ignore.test(project)) {
return false;
}
}
return true;
});
log('parserOptions.project matched projects: %s', projects);
log('ignore list applied to parserOptions.project: %s', filtered);
return filtered;
}
function applyParserOptionsToExtra(options: TSESTreeOptions): void {
/**
* Configure Debug logging
@@ -205,34 +275,18 @@ function applyParserOptionsToExtra(options: TSESTreeOptions): void {
extra.log = Function.prototype;
}
if (typeof options.project === 'string') {
extra.projects = [options.project];
} else if (
Array.isArray(options.project) &&
options.project.every(projectPath => typeof projectPath === 'string')
) {
extra.projects = options.project;
}
if (typeof options.tsconfigRootDir === 'string') {
extra.tsconfigRootDir = options.tsconfigRootDir;
}
// NOTE - ensureAbsolutePath relies upon having the correct tsconfigRootDir in extra
extra.filePath = ensureAbsolutePath(extra.filePath, extra);
// Transform glob patterns into paths
if (extra.projects) {
extra.projects = extra.projects.reduce<string[]>(
(projects, project) =>
projects.concat(
isGlob(project)
? globSync(project, {
cwd: extra.tsconfigRootDir || process.cwd(),
})
: project,
),
[],
);
}
// NOTE - prepareAndTransformProjects relies upon having the correct tsconfigRootDir in extra
extra.projects = prepareAndTransformProjects(
options.project,
options.projectFolderIgnoreList,
);
if (
Array.isArray(options.extraFileExtensions) &&
@@ -0,0 +1 @@
export const x = 1;
@@ -0,0 +1,3 @@
{
"include": ["./file.ts"]
}
@@ -0,0 +1 @@
export const x = 2;
@@ -0,0 +1,3 @@
{
"include": ["./file.ts"]
}
@@ -557,4 +557,49 @@ describe('parse()', () => {
);
});
});
describe('projectFolderIgnoreList', () => {
beforeEach(() => {
parser.clearCaches();
});
const PROJECT_DIR = resolve(FIXTURES_DIR, '../projectFolderIgnoreList');
const code = 'var a = true';
const config: TSESTreeOptions = {
comment: true,
tokens: true,
range: true,
loc: true,
tsconfigRootDir: PROJECT_DIR,
project: './**/tsconfig.json',
};
const testParse = (
filePath: 'ignoreme' | 'includeme',
projectFolderIgnoreList: TSESTreeOptions['projectFolderIgnoreList'] = [],
) => (): void => {
parser.parseAndGenerateServices(code, {
...config,
projectFolderIgnoreList,
filePath: join(PROJECT_DIR, filePath, './file.ts'),
});
};
it('ignores nothing when given nothing', () => {
expect(testParse('ignoreme')).not.toThrow();
expect(testParse('includeme')).not.toThrow();
});
it('ignores a folder when given a string regexp', () => {
const ignore = ['/ignoreme/'];
expect(testParse('ignoreme', ignore)).toThrow();
expect(testParse('includeme', ignore)).not.toThrow();
});
it('ignores a folder when given a RegExp', () => {
const ignore = [/\/ignoreme\//];
expect(testParse('ignoreme', ignore)).toThrow();
expect(testParse('includeme', ignore)).not.toThrow();
});
});
});
+1 -1
View File
@@ -1,4 +1,4 @@
{
"extends": "./tsconfig.base.json",
"include": ["tests/**/*.ts", "tools/**/*.ts"]
"include": ["tests/**/*.ts", "tools/**/*.ts", ".eslintrc.js"]
}