Skip to content

Commit

Permalink
feat(typescript-estree): add option to ignore certain folders from gl…
Browse files Browse the repository at this point in the history
…ob resolution (#1802)
  • Loading branch information
bradzacher committed Mar 27, 2020
1 parent 7d963fd commit 1e29e69
Show file tree
Hide file tree
Showing 11 changed files with 213 additions and 64 deletions.
1 change: 1 addition & 0 deletions packages/experimental-utils/src/ts-eslint/ParserOptions.ts
Expand Up @@ -16,6 +16,7 @@ interface ParserOptions {
loc?: boolean;
noWatch?: boolean;
project?: string | string[];
projectFolderIgnoreList?: (string | RegExp)[];
range?: boolean;
sourceType?: 'script' | 'module';
tokens?: boolean;
Expand Down
19 changes: 15 additions & 4 deletions packages/parser/README.md
Expand Up @@ -54,6 +54,7 @@ interface ParserOptions {
jsx?: boolean;
};
project?: string | string[];
projectFolderIgnoreList?: (string | RegExp)[];
tsconfigRootDir?: string;
extraFileExtensions?: string[];
warnOnUnsupportedTypeScriptVersion?: boolean;
Expand Down Expand Up @@ -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`.

Expand Down
11 changes: 11 additions & 0 deletions packages/typescript-estree/README.md
Expand Up @@ -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.
*/
Expand All @@ -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(),
};

Expand Down
91 changes: 55 additions & 36 deletions packages/typescript-estree/src/parser-options.ts
Expand Up @@ -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).
Expand All @@ -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;

Expand Down Expand Up @@ -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.
Expand All @@ -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.
*/
range?: boolean;

/**
* Set to true to create a top-level array containing all tokens from the file.
* 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/"]
*/
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. *
Expand All @@ -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> {
Expand Down
100 changes: 77 additions & 23 deletions packages/typescript-estree/src/parser.ts
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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) &&
Expand Down
@@ -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"]
}

0 comments on commit 1e29e69

Please sign in to comment.