Skip to content

Commit

Permalink
New: ignorePatterns in config files (refs eslint/rfcs#22) (#12274)
Browse files Browse the repository at this point in the history
* New: ignorePatterns in config files (refs eslint/rfcs#22)

* move IgnoredPaths tests to cli-engine.js

* add more tests

* update document

* fix test problem

* add more tests

* fix npm run perf

* fix problem when the base path was the root

* Update tests/lib/cli-engine/cli-engine.js

Co-Authored-By: Kevin Partington <platinum.azure@kernelpanicstudios.com>

* Update lib/cli-engine/config-array/ignore-pattern.js

Co-Authored-By: Kevin Partington <platinum.azure@kernelpanicstudios.com>

* ❤️ eslint-plugin-jsdoc

* add description

* Update tests/lib/cli-engine/cli-engine.js

Co-Authored-By: Kevin Partington <platinum.azure@kernelpanicstudios.com>
  • Loading branch information
2 people authored and kaicataldo committed Nov 20, 2019
1 parent 60204a3 commit ca3b2a6
Show file tree
Hide file tree
Showing 23 changed files with 1,808 additions and 1,148 deletions.
2 changes: 1 addition & 1 deletion Makefile.js
Expand Up @@ -988,7 +988,7 @@ function createConfigForPerformanceTest() {
function time(cmd, runs, runNumber, results, cb) {
const start = process.hrtime();

exec(cmd, { silent: true }, (code, stdout, stderr) => {
exec(cmd, { maxBuffer: 64 * 1024 * 1024, silent: true }, (code, stdout, stderr) => {
const diff = process.hrtime(start),
actual = (diff[0] * 1e3 + diff[1] / 1e6); // ms

Expand Down
1 change: 1 addition & 0 deletions conf/config-schema.js
Expand Up @@ -55,6 +55,7 @@ const configSchema = {
type: "object",
properties: {
root: { type: "boolean" },
ignorePatterns: { $ref: "#/definitions/stringOrStrings" },
...baseConfigProperties
},
additionalProperties: false
Expand Down
2 changes: 1 addition & 1 deletion conf/default-cli-options.js
Expand Up @@ -14,7 +14,7 @@ module.exports = {
globals: [],
extensions: [".js"],
ignore: true,
ignorePath: null,
ignorePath: void 0,
cache: false,

/*
Expand Down
2 changes: 1 addition & 1 deletion docs/user-guide/command-line-interface.md
Expand Up @@ -266,7 +266,7 @@ Example:

#### `--no-ignore`

Disables excluding of files from `.eslintignore`, `--ignore-path` and `--ignore-pattern`.
Disables excluding of files from `.eslintignore`, `--ignore-path`, `--ignore-pattern`, and `ignorePatterns` property in config files.

Example:

Expand Down
21 changes: 19 additions & 2 deletions docs/user-guide/configuring.md
Expand Up @@ -915,8 +915,8 @@ module.exports = {

* The patterns are applied against the file path relative to the directory of the config file. For example, if your config file has the path `/Users/john/workspace/any-project/.eslintrc.js` and the file you want to lint has the path `/Users/john/workspace/any-project/lib/util.js`, then the pattern provided in `.eslintrc.js` will be executed against the relative path `lib/util.js`.
* Glob pattern overrides have higher precedence than the regular configuration in the same config file. Multiple overrides within the same config are applied in order. That is, the last override block in a config file always has the highest precedence.
* A glob specific configuration works almost the same as any other ESLint config. Override blocks can contain any configuration options that are valid in a regular config, with the exception of `root`.
* A glob specific configuration can have `extends` setting, but the `root` property in the extended configs is ignored.
* A glob specific configuration works almost the same as any other ESLint config. Override blocks can contain any configuration options that are valid in a regular config, with the exception of `root` and `ignorePatterns`.
* A glob specific configuration can have `extends` setting, but the `root` property in the extended configs is ignored. The `ignorePatterns` property in the extended configs is used only for the files the glob specific configuration matched.
* Nested `overrides` setting will be applied only if the glob patterns of both of the parent config and the child config matched. This is the same when the extended configs have `overrides` setting.
* Multiple glob patterns can be provided within a single override block. A file must match at least one of the supplied patterns for the configuration to apply.
* Override blocks can also specify patterns to exclude from matches. If a file matches any of the excluded patterns, the configuration won't apply.
Expand Down Expand Up @@ -986,6 +986,23 @@ Currently the sole method for telling ESLint which file extensions to lint is by

## Ignoring Files and Directories

### `ignorePatterns` in config files

You can tell ESLint to ignore specific files and directories by `ignorePatterns` in your config files. Each value of `ignorePatterns` is the same pattern as each line of `.eslintignore` in the next section.

```json
{
"ignorePatterns": ["temp.js", "node_modules/"],
"rules": {
//...
}
}
```

* The `ignorePatterns` property affects only the directory that the config file placed.
* You cannot write `ignorePatterns` property under `overrides` property.
* `.eslintignore` can override `ignorePatterns` property of config files.

### `.eslintignore`

You can tell ESLint to ignore specific files and directories by creating an `.eslintignore` file in your project's root directory. The `.eslintignore` file is a plain text file where each line is a glob pattern indicating which paths should be omitted from linting. For example, the following will omit all JavaScript files:
Expand Down
51 changes: 38 additions & 13 deletions lib/cli-engine/cascading-config-array-factory.js
Expand Up @@ -27,7 +27,7 @@ const os = require("os");
const path = require("path");
const { validateConfigArray } = require("../shared/config-validator");
const { ConfigArrayFactory } = require("./config-array-factory");
const { ConfigArray, ConfigDependency } = require("./config-array");
const { ConfigArray, ConfigDependency, IgnorePattern } = require("./config-array");
const loadRules = require("./load-rules");
const debug = require("debug")("eslint:cascading-config-array-factory");

Expand All @@ -45,8 +45,9 @@ const debug = require("debug")("eslint:cascading-config-array-factory");
* @typedef {Object} CascadingConfigArrayFactoryOptions
* @property {Map<string,Plugin>} [additionalPluginPool] The map for additional plugins.
* @property {ConfigData} [baseConfig] The config by `baseConfig` option.
* @property {ConfigData} [cliConfig] The config by CLI options (`--env`, `--global`, `--parser`, `--parser-options`, `--plugin`, and `--rule`). CLI options overwrite the setting in config files.
* @property {ConfigData} [cliConfig] The config by CLI options (`--env`, `--global`, `--ignore-pattern`, `--parser`, `--parser-options`, `--plugin`, and `--rule`). CLI options overwrite the setting in config files.
* @property {string} [cwd] The base directory to start lookup.
* @property {string} [ignorePath] The path to the alternative file of `.eslintignore`.
* @property {string[]} [rulePaths] The value of `--rulesdir` option.
* @property {string} [specificConfigPath] The value of `--config` option.
* @property {boolean} [useEslintrc] if `false` then it doesn't load config files.
Expand All @@ -62,6 +63,7 @@ const debug = require("debug")("eslint:cascading-config-array-factory");
* @property {Map<string, ConfigArray>} configCache The cache from directory paths to config arrays.
* @property {string} cwd The base directory to start lookup.
* @property {WeakMap<ConfigArray, ConfigArray>} finalizeCache The cache from config arrays to finalized config arrays.
* @property {string} [ignorePath] The path to the alternative file of `.eslintignore`.
* @property {string[]|null} rulePaths The value of `--rulesdir` option. This is used to reset `baseConfigArray`.
* @property {string|null} specificConfigPath The value of `--config` option. This is used to reset `cliConfigArray`.
* @property {boolean} useEslintrc if `false` then it doesn't load config files.
Expand All @@ -86,14 +88,22 @@ function createBaseConfigArray({
{ name: "BaseConfig" }
);

/*
* Create the config array element for the default ignore patterns.
* This element has `ignorePattern` property that ignores the default
* patterns in the current working directory.
*/
baseConfigArray.unshift(configArrayFactory.create(
{ ignorePatterns: IgnorePattern.DefaultPatterns },
{ name: "DefaultIgnorePattern" }
)[0]);

/*
* Load rules `--rulesdir` option as a pseudo plugin.
* Use a pseudo plugin to define rules of `--rulesdir`, so we can validate
* the rule's options with only information in the config array.
*/
if (rulePaths && rulePaths.length > 0) {

/*
* Load rules `--rulesdir` option as a pseudo plugin.
* Use a pseudo plugin to define rules of `--rulesdir`, so we can
* validate the rule's options with only information in the config
* array.
*/
baseConfigArray.push({
name: "--rulesdir",
filePath: "",
Expand Down Expand Up @@ -128,13 +138,20 @@ function createBaseConfigArray({
function createCLIConfigArray({
cliConfigData,
configArrayFactory,
ignorePath,
specificConfigPath
}) {
const cliConfigArray = configArrayFactory.create(
cliConfigData,
{ name: "CLIOptions" }
);

cliConfigArray.unshift(
...(ignorePath
? configArrayFactory.loadESLintIgnore(ignorePath)
: configArrayFactory.loadDefaultESLintIgnore())
);

if (specificConfigPath) {
cliConfigArray.unshift(
...configArrayFactory.loadFile(
Expand Down Expand Up @@ -178,6 +195,7 @@ class CascadingConfigArrayFactory {
baseConfig: baseConfigData = null,
cliConfig: cliConfigData = null,
cwd = process.cwd(),
ignorePath,
resolvePluginsRelativeTo = cwd,
rulePaths = [],
specificConfigPath = null,
Expand All @@ -200,13 +218,15 @@ class CascadingConfigArrayFactory {
cliConfigArray: createCLIConfigArray({
cliConfigData,
configArrayFactory,
ignorePath,
specificConfigPath
}),
cliConfigData,
configArrayFactory,
configCache: new Map(),
cwd,
finalizeCache: new WeakMap(),
ignorePath,
rulePaths,
specificConfigPath,
useEslintrc
Expand All @@ -229,9 +249,11 @@ class CascadingConfigArrayFactory {
* If `filePath` was not given, it returns the config which contains only
* `baseConfigData` and `cliConfigData`.
* @param {string} [filePath] The file path to a file.
* @param {Object} [options] The options.
* @param {boolean} [options.ignoreNotFoundError] If `true` then it doesn't throw `ConfigurationNotFoundError`.
* @returns {ConfigArray} The config array of the file.
*/
getConfigArrayForFile(filePath) {
getConfigArrayForFile(filePath, { ignoreNotFoundError = false } = {}) {
const {
baseConfigArray,
cliConfigArray,
Expand All @@ -248,7 +270,8 @@ class CascadingConfigArrayFactory {

return this._finalizeConfigArray(
this._loadConfigInAncestors(directoryPath),
directoryPath
directoryPath,
ignoreNotFoundError
);
}

Expand Down Expand Up @@ -354,10 +377,11 @@ class CascadingConfigArrayFactory {
* Concatenate `--config` and other CLI options.
* @param {ConfigArray} configArray The parent config array.
* @param {string} directoryPath The path to the leaf directory to find config files.
* @param {boolean} ignoreNotFoundError If `true` then it doesn't throw `ConfigurationNotFoundError`.
* @returns {ConfigArray} The loaded config.
* @private
*/
_finalizeConfigArray(configArray, directoryPath) {
_finalizeConfigArray(configArray, directoryPath, ignoreNotFoundError) {
const {
cliConfigArray,
configArrayFactory,
Expand Down Expand Up @@ -403,7 +427,8 @@ class CascadingConfigArrayFactory {
);
}

if (useEslintrc && finalConfigArray.length === 0) {
// At least one element (the default ignore patterns) exists.
if (!ignoreNotFoundError && useEslintrc && finalConfigArray.length <= 1) {
throw new ConfigurationNotFoundError(directoryPath);
}

Expand Down
54 changes: 40 additions & 14 deletions lib/cli-engine/cli-engine.js
Expand Up @@ -25,10 +25,9 @@ const ModuleResolver = require("../shared/relative-module-resolver");
const { Linter } = require("../linter");
const builtInRules = require("../rules");
const { CascadingConfigArrayFactory } = require("./cascading-config-array-factory");
const { getUsedExtractedConfigs } = require("./config-array");
const { IgnorePattern, getUsedExtractedConfigs } = require("./config-array");
const { FileEnumerator } = require("./file-enumerator");
const hash = require("./hash");
const { IgnoredPaths } = require("./ignored-paths");
const LintResultCache = require("./lint-result-cache");

const debug = require("debug")("eslint:cli-engine");
Expand Down Expand Up @@ -64,7 +63,7 @@ const validFixTypes = new Set(["problem", "suggestion", "layout"]);
* @property {string[]} globals An array of global variables to declare.
* @property {boolean} ignore False disables use of .eslintignore.
* @property {string} ignorePath The ignore file to use instead of .eslintignore.
* @property {string} ignorePattern A glob pattern of files to ignore.
* @property {string|string[]} ignorePattern One or more glob patterns to ignore.
* @property {boolean} useEslintrc False disables looking for .eslintrc
* @property {string} parser The name of the parser to use.
* @property {ParserOptions} parserOptions An object of parserOption settings to use.
Expand Down Expand Up @@ -113,8 +112,8 @@ const validFixTypes = new Set(["problem", "suggestion", "layout"]);
* @property {Map<string, Plugin>} additionalPluginPool The map for additional plugins.
* @property {string} cacheFilePath The path to the cache of lint results.
* @property {CascadingConfigArrayFactory} configArrayFactory The factory of configs.
* @property {(filePath: string) => boolean} defaultIgnores The default predicate function to check if a file ignored or not.
* @property {FileEnumerator} fileEnumerator The file enumerator.
* @property {IgnoredPaths} ignoredPaths The ignored paths.
* @property {ConfigArray[]} lastConfigArrays The list of config arrays that the last `executeOnFiles` or `executeOnText` used.
* @property {LintResultCache|null} lintResultCache The cache of lint results.
* @property {Linter} linter The linter instance which has loaded rules.
Expand Down Expand Up @@ -486,21 +485,36 @@ function toBooleanMap(keys, defaultValue, displayName) {
* @returns {ConfigData|null} The created config data.
*/
function createConfigDataFromOptions(options) {
const { parser, parserOptions, plugins, rules } = options;
const {
ignorePattern,
parser,
parserOptions,
plugins,
rules
} = options;
const env = toBooleanMap(options.envs, true, "envs");
const globals = toBooleanMap(options.globals, false, "globals");

if (
env === void 0 &&
globals === void 0 &&
(ignorePattern === void 0 || ignorePattern.length === 0) &&
parser === void 0 &&
parserOptions === void 0 &&
plugins === void 0 &&
rules === void 0
) {
return null;
}
return { env, globals, parser, parserOptions, plugins, rules };
return {
env,
globals,
ignorePatterns: ignorePattern,
parser,
parserOptions,
plugins,
rules
};
}

/**
Expand Down Expand Up @@ -551,19 +565,18 @@ class CLIEngine {
baseConfig: options.baseConfig || null,
cliConfig: createConfigDataFromOptions(options),
cwd: options.cwd,
ignorePath: options.ignorePath,
resolvePluginsRelativeTo: options.resolvePluginsRelativeTo,
rulePaths: options.rulePaths,
specificConfigPath: options.configFile,
useEslintrc: options.useEslintrc
});
const ignoredPaths = new IgnoredPaths(options);
const fileEnumerator = new FileEnumerator({
configArrayFactory,
cwd: options.cwd,
extensions: options.extensions,
globInputPaths: options.globInputPaths,
ignore: options.ignore,
ignoredPaths
ignore: options.ignore
});
const lintResultCache =
options.cache ? new LintResultCache(cacheFilePath) : null;
Expand All @@ -577,8 +590,8 @@ class CLIEngine {
additionalPluginPool,
cacheFilePath,
configArrayFactory,
defaultIgnores: IgnorePattern.createDefaultIgnore(options.cwd),
fileEnumerator,
ignoredPaths,
lastConfigArrays,
lintResultCache,
linter,
Expand Down Expand Up @@ -835,7 +848,6 @@ class CLIEngine {
const {
configArrayFactory,
fileEnumerator,
ignoredPaths,
lastConfigArrays,
linter,
options: {
Expand All @@ -852,7 +864,7 @@ class CLIEngine {
// Clear the last used config arrays.
lastConfigArrays.length = 0;

if (resolvedFilename && ignoredPaths.contains(resolvedFilename)) {
if (resolvedFilename && this.isPathIgnored(resolvedFilename)) {
if (warnIgnored) {
results.push(createIgnoreResult(resolvedFilename, cwd));
}
Expand Down Expand Up @@ -926,9 +938,23 @@ class CLIEngine {
* @returns {boolean} Whether or not the given path is ignored.
*/
isPathIgnored(filePath) {
const { ignoredPaths } = internalSlotsMap.get(this);
const {
configArrayFactory,
defaultIgnores,
options: { cwd, ignore }
} = internalSlotsMap.get(this);
const absolutePath = path.resolve(cwd, filePath);

if (ignore) {
const config = configArrayFactory
.getConfigArrayForFile(absolutePath)
.extractConfig(absolutePath);
const ignores = config.ignores || defaultIgnores;

return ignores(absolutePath);
}

return ignoredPaths.contains(filePath);
return defaultIgnores(absolutePath);
}

/**
Expand Down

0 comments on commit ca3b2a6

Please sign in to comment.