From 529fe617bee359527390875cdff69c3bb65cad12 Mon Sep 17 00:00:00 2001 From: Toru Nagashima Date: Tue, 17 Sep 2019 07:01:25 +0900 Subject: [PATCH 01/13] New: ignorePatterns in config files (refs eslint/rfcs#22) --- conf/config-schema.js | 1 + conf/default-cli-options.js | 2 +- .../cascading-config-array-factory.js | 51 ++- lib/cli-engine/cli-engine.js | 54 ++- lib/cli-engine/config-array-factory.js | 114 +++++- lib/cli-engine/config-array/config-array.js | 13 + .../config-array/extracted-config.js | 27 ++ lib/cli-engine/config-array/ignore-pattern.js | 234 +++++++++++ lib/cli-engine/config-array/index.js | 2 + lib/cli-engine/file-enumerator.js | 73 ++-- lib/cli-engine/ignored-paths.js | 362 ------------------ lib/shared/types.js | 1 + tests/lib/cli-engine/_utils.js | 15 +- .../cascading-config-array-factory.js | 79 ++-- tests/lib/cli-engine/cli-engine.js | 10 +- tests/lib/cli-engine/config-array-factory.js | 2 + .../cli-engine/config-array/config-array.js | 5 +- .../cli-engine/config-array/ignore-pattern.js | 125 ++++++ tests/lib/cli-engine/file-enumerator.js | 44 ++- 19 files changed, 730 insertions(+), 484 deletions(-) create mode 100644 lib/cli-engine/config-array/ignore-pattern.js delete mode 100644 lib/cli-engine/ignored-paths.js create mode 100644 tests/lib/cli-engine/config-array/ignore-pattern.js diff --git a/conf/config-schema.js b/conf/config-schema.js index 164f0b4219f..83addff8781 100644 --- a/conf/config-schema.js +++ b/conf/config-schema.js @@ -55,6 +55,7 @@ const configSchema = { type: "object", properties: { root: { type: "boolean" }, + ignorePatterns: { $ref: "#/definitions/stringOrStrings" }, ...baseConfigProperties }, additionalProperties: false diff --git a/conf/default-cli-options.js b/conf/default-cli-options.js index abbd9184e21..0f7b377fb36 100644 --- a/conf/default-cli-options.js +++ b/conf/default-cli-options.js @@ -14,7 +14,7 @@ module.exports = { globals: [], extensions: [".js"], ignore: true, - ignorePath: null, + ignorePath: void 0, cache: false, /* diff --git a/lib/cli-engine/cascading-config-array-factory.js b/lib/cli-engine/cascading-config-array-factory.js index 13b240f12b3..ffd5e62fa4f 100644 --- a/lib/cli-engine/cascading-config-array-factory.js +++ b/lib/cli-engine/cascading-config-array-factory.js @@ -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"); @@ -45,8 +45,9 @@ const debug = require("debug")("eslint:cascading-config-array-factory"); * @typedef {Object} CascadingConfigArrayFactoryOptions * @property {Map} [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. @@ -62,6 +63,7 @@ const debug = require("debug")("eslint:cascading-config-array-factory"); * @property {Map} configCache The cache from directory paths to config arrays. * @property {string} cwd The base directory to start lookup. * @property {WeakMap} 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. @@ -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: "", @@ -128,6 +138,7 @@ function createBaseConfigArray({ function createCLIConfigArray({ cliConfigData, configArrayFactory, + ignorePath, specificConfigPath }) { const cliConfigArray = configArrayFactory.create( @@ -135,6 +146,12 @@ function createCLIConfigArray({ { name: "CLIOptions" } ); + cliConfigArray.unshift( + ...(ignorePath + ? configArrayFactory.loadESLintIgnore(ignorePath) + : configArrayFactory.loadDefaultESLintIgnore()) + ); + if (specificConfigPath) { cliConfigArray.unshift( ...configArrayFactory.loadFile( @@ -177,6 +194,7 @@ class CascadingConfigArrayFactory { baseConfig: baseConfigData = null, cliConfig: cliConfigData = null, cwd = process.cwd(), + ignorePath, resolvePluginsRelativeTo = cwd, rulePaths = [], specificConfigPath = null, @@ -199,6 +217,7 @@ class CascadingConfigArrayFactory { cliConfigArray: createCLIConfigArray({ cliConfigData, configArrayFactory, + ignorePath, specificConfigPath }), cliConfigData, @@ -206,6 +225,7 @@ class CascadingConfigArrayFactory { configCache: new Map(), cwd, finalizeCache: new WeakMap(), + ignorePath, rulePaths, specificConfigPath, useEslintrc @@ -228,9 +248,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, @@ -247,7 +269,8 @@ class CascadingConfigArrayFactory { return this._finalizeConfigArray( this._loadConfigInAncestors(directoryPath), - directoryPath + directoryPath, + ignoreNotFoundError ); } @@ -353,10 +376,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, @@ -402,7 +426,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); } diff --git a/lib/cli-engine/cli-engine.js b/lib/cli-engine/cli-engine.js index 7f80887f086..ac5cac2d742 100644 --- a/lib/cli-engine/cli-engine.js +++ b/lib/cli-engine/cli-engine.js @@ -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"); @@ -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. @@ -113,8 +112,8 @@ const validFixTypes = new Set(["problem", "suggestion", "layout"]); * @property {Map} 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. @@ -488,13 +487,20 @@ 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 && @@ -502,7 +508,15 @@ function createConfigDataFromOptions(options) { ) { return null; } - return { env, globals, parser, parserOptions, plugins, rules }; + return { + env, + globals, + ignorePatterns: ignorePattern, + parser, + parserOptions, + plugins, + rules + }; } /** @@ -554,19 +568,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; @@ -580,8 +593,8 @@ class CLIEngine { additionalPluginPool, cacheFilePath, configArrayFactory, + defaultIgnores: IgnorePattern.createDefaultIgnore(options.cwd), fileEnumerator, - ignoredPaths, lastConfigArrays, lintResultCache, linter, @@ -835,7 +848,6 @@ class CLIEngine { const { configArrayFactory, fileEnumerator, - ignoredPaths, lastConfigArrays, linter, options: { @@ -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)); } @@ -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); } /** diff --git a/lib/cli-engine/config-array-factory.js b/lib/cli-engine/config-array-factory.js index 6e1ba1e02b9..d396c00a3ea 100644 --- a/lib/cli-engine/config-array-factory.js +++ b/lib/cli-engine/config-array-factory.js @@ -17,6 +17,12 @@ * Create a `ConfigArray` instance from a config file which is on a given * directory. This tries to load `.eslintrc.*` or `package.json`. If not * found, returns an empty `ConfigArray`. + * - `loadESLintIgnore(filePath)` + * Create a `ConfigArray` instance from a config file that is `.eslintignore` + * format. This is to handle `--ignore-path` option. + * - `loadDefaultESLintIgnore()` + * Create a `ConfigArray` instance from `.eslintignore` or `package.json` in + * the current working directory. * * `ConfigArrayFactory` class has the responsibility that loads configuration * files, including loading `extends`, `parser`, and `plugins`. The created @@ -40,7 +46,12 @@ const stripComments = require("strip-json-comments"); const { validateConfigSchema } = require("../shared/config-validator"); const naming = require("../shared/naming"); const ModuleResolver = require("../shared/relative-module-resolver"); -const { ConfigArray, ConfigDependency, OverrideTester } = require("./config-array"); +const { + ConfigArray, + ConfigDependency, + IgnorePattern, + OverrideTester +} = require("./config-array"); const debug = require("debug")("eslint:config-array-factory"); //------------------------------------------------------------------------------ @@ -221,6 +232,26 @@ function loadPackageJSONConfigFile(filePath) { } } +/** + * Loads a `.eslintignore` from a file. + * @param {string} filePath The filename to load. + * @returns {string[]} The ignore patterns from the file. + * @private + */ +function loadESLintIgnoreFile(filePath) { + debug(`Loading .eslintignore file: ${filePath}`); + + try { + return readFile(filePath) + .split(/\r?\n/gu) + .filter(line => line.trim() !== "" && !line.startsWith("#")); + } catch (e) { + debug(`Error reading .eslintignore file: ${filePath}`); + e.message = `Cannot read .eslintignore file: ${filePath}\nError: ${e.message}`; + throw e; + } +} + /** * Creates an error to notify about a missing config to extend from. * @param {string} configName The name of the missing config. @@ -403,6 +434,54 @@ class ConfigArrayFactory { ); } + /** + * Load `.eslintignore` file. + * @param {string} filePath The path to a `.eslintignore` file to load. + * @returns {ConfigArray} Loaded config. An empty `ConfigArray` if any config doesn't exist. + */ + loadESLintIgnore(filePath) { + const { cwd } = internalSlotsMap.get(this); + const absolutePath = path.resolve(cwd, filePath); + const name = path.relative(cwd, absolutePath); + const ignorePatterns = loadESLintIgnoreFile(absolutePath); + + return createConfigArray( + this._normalizeESLintIgnoreData(ignorePatterns, absolutePath, name) + ); + } + + /** + * Load `.eslintignore` file in the current working directory. + * @returns {ConfigArray} Loaded config. An empty `ConfigArray` if any config doesn't exist. + */ + loadDefaultESLintIgnore() { + const { cwd } = internalSlotsMap.get(this); + const eslintIgnorePath = path.resolve(cwd, ".eslintignore"); + const packageJsonPath = path.resolve(cwd, "package.json"); + + if (fs.existsSync(eslintIgnorePath)) { + return this.loadESLintIgnore(eslintIgnorePath); + } + if (fs.existsSync(packageJsonPath)) { + const data = loadJSONConfigFile(packageJsonPath); + + if (Object.hasOwnProperty.call(data, "eslintIgnore")) { + if (!Array.isArray(data.eslintIgnore)) { + throw new Error("Package.json eslintIgnore property requires an array of paths"); + } + return createConfigArray( + this._normalizeESLintIgnoreData( + data.eslintIgnore, + packageJsonPath, + "eslintIgnore in package.json" + ) + ); + } + } + + return new ConfigArray(); + } + /** * Load a given config file. * @param {string} filePath The path to a config file. @@ -451,6 +530,30 @@ class ConfigArrayFactory { return null; } + /** + * Normalize a given `.eslintignore` data to config array elements. + * @param {string[]} ignorePatterns The patterns to ignore files. + * @param {string|undefined} filePath The file path of this config. + * @param {string|undefined} name The name of this config. + * @returns {IterableIterator} The normalized config. + * @private + */ + *_normalizeESLintIgnoreData(ignorePatterns, filePath, name) { + const elements = this._normalizeObjectConfigData( + { ignorePatterns }, + filePath, + name + ); + + // Set `ignorePattern.loose` flag for backward compatibility. + for (const element of elements) { + if (element.ignorePattern) { + element.ignorePattern.loose = true; + } + yield element; + } + } + /** * Normalize a given config to an array. * @param {ConfigData} configData The config data to normalize. @@ -494,6 +597,9 @@ class ConfigArrayFactory { if (element.criteria) { element.criteria.basePath = basePath; } + if (element.ignorePattern) { + element.ignorePattern.basePath = basePath; + } /* * Merge the criteria; this is for only file extension processors in @@ -526,6 +632,7 @@ class ConfigArrayFactory { env, extends: extend, globals, + ignorePatterns, noInlineConfig, parser: parserName, parserOptions, @@ -541,6 +648,10 @@ class ConfigArrayFactory { name ) { const extendList = Array.isArray(extend) ? extend : [extend]; + const ignorePattern = ignorePatterns && new IgnorePattern( + Array.isArray(ignorePatterns) ? ignorePatterns : [ignorePatterns], + filePath ? path.dirname(filePath) : internalSlotsMap.get(this).cwd + ); // Flatten `extends`. for (const extendName of extendList.filter(Boolean)) { @@ -569,6 +680,7 @@ class ConfigArrayFactory { criteria: null, env, globals, + ignorePattern, noInlineConfig, parser, parserOptions, diff --git a/lib/cli-engine/config-array/config-array.js b/lib/cli-engine/config-array/config-array.js index 6383c02115f..ef064d937f9 100644 --- a/lib/cli-engine/config-array/config-array.js +++ b/lib/cli-engine/config-array/config-array.js @@ -31,6 +31,7 @@ //------------------------------------------------------------------------------ const { ExtractedConfig } = require("./extracted-config"); +const { IgnorePattern } = require("./ignore-pattern"); //------------------------------------------------------------------------------ // Helpers @@ -54,6 +55,7 @@ const { ExtractedConfig } = require("./extracted-config"); * @property {InstanceType|null} criteria The tester for the `files` and `excludedFiles` of this config element. * @property {Record|undefined} env The environment settings. * @property {Record|undefined} globals The global variable settings. + * @property {IgnorePattern|undefined} ignorePattern The ignore patterns. * @property {boolean|undefined} noInlineConfig The flag that disables directive comments. * @property {DependentParser|undefined} parser The parser loader. * @property {Object|undefined} parserOptions The parser options. @@ -234,6 +236,7 @@ function mergeRuleConfigs(target, source) { */ function createConfig(instance, indices) { const config = new ExtractedConfig(); + const ignorePatterns = []; // Merge elements. for (const index of indices) { @@ -263,6 +266,11 @@ function createConfig(instance, indices) { config.reportUnusedDisableDirectives = element.reportUnusedDisableDirectives; } + // Collect ignorePatterns + if (element.ignorePattern) { + ignorePatterns.push(element.ignorePattern); + } + // Merge others. mergeWithoutOverwrite(config.env, element.env); mergeWithoutOverwrite(config.globals, element.globals); @@ -272,6 +280,11 @@ function createConfig(instance, indices) { mergeRuleConfigs(config.rules, element.rules); } + // Create the predicate function for ignore patterns. + if (ignorePatterns.length > 0) { + config.ignores = IgnorePattern.createIgnore(ignorePatterns.reverse()); + } + return config; } diff --git a/lib/cli-engine/config-array/extracted-config.js b/lib/cli-engine/config-array/extracted-config.js index 66858313ba6..b27d6ffb188 100644 --- a/lib/cli-engine/config-array/extracted-config.js +++ b/lib/cli-engine/config-array/extracted-config.js @@ -16,6 +16,8 @@ */ "use strict"; +const { IgnorePattern } = require("./ignore-pattern"); + // For VSCode intellisense /** @typedef {import("../../shared/types").ConfigData} ConfigData */ /** @typedef {import("../../shared/types").GlobalConf} GlobalConf */ @@ -23,6 +25,17 @@ /** @typedef {import("./config-dependency").DependentParser} DependentParser */ /** @typedef {import("./config-dependency").DependentPlugin} DependentPlugin */ +/** + * Check if `xs` starts with `ys`. + * @template T + * @param {T[]} xs The array to check. + * @param {T[]} ys The array that may be the first part of `xs`. + * @returns {boolean} `true` if `xs` starts with `ys`. + */ +function startsWith(xs, ys) { + return xs.length >= ys.length && ys.every((y, i) => y === xs[i]); +} + /** * The class for extracted config data. */ @@ -47,6 +60,12 @@ class ExtractedConfig { */ this.globals = {}; + /** + * The glob patterns that ignore to lint. + * @type {(((filePath:string, dot?:boolean) => boolean) & { basePath:string; patterns:string[] }) | undefined} + */ + this.ignores = void 0; + /** * The flag that disables directive comments. * @type {boolean|undefined} @@ -106,11 +125,19 @@ class ExtractedConfig { configNameOfNoInlineConfig: _ignore1, processor: _ignore2, /* eslint-enable no-unused-vars */ + ignores, ...config } = this; config.parser = config.parser && config.parser.filePath; config.plugins = Object.keys(config.plugins).filter(Boolean).reverse(); + config.ignorePatterns = ignores ? ignores.patterns : []; + + // Strip the default patterns from `ignorePatterns`. + if (startsWith(config.ignorePatterns, IgnorePattern.DefaultPatterns)) { + config.ignorePatterns = + config.ignorePatterns.slice(IgnorePattern.DefaultPatterns.length); + } return config; } diff --git a/lib/cli-engine/config-array/ignore-pattern.js b/lib/cli-engine/config-array/ignore-pattern.js new file mode 100644 index 00000000000..3f28982a99c --- /dev/null +++ b/lib/cli-engine/config-array/ignore-pattern.js @@ -0,0 +1,234 @@ +/** + * @fileoverview `IgnorePattern` class. + * + * `IgnorePattern` class has the set of glob patterns and the base path. + * + * It provides two static methods. + * + * - `IgnorePattern.createDefaultIgnore(cwd)` + * Create the default predicate function. + * - `IgnorePattern.createIgnore(ignorePatterns)` + * Create the predicate function from multiple `IgnorePattern` objects. + * + * It provides two properties and a method. + * + * - `patterns` + * The glob patterns that ignore to lint. + * - `basePath` + * The base path of the glob patterns. If absolute paths existed in the + * glob patterns, those are handled as relative paths to the base path. + * - `getPatternsRelativeTo(basePath)` + * Get `patterns` as modified for a given base path. It modifies the + * absolute paths in the patterns as prepending the difference of two base + * paths. + * + * `ConfigArrayFactory` creates `IgnorePattern` objects when it processes + * `ignorePatterns` properties. + * + * @author Toru Nagashima + */ +"use strict"; + +//------------------------------------------------------------------------------ +// Requirements +//------------------------------------------------------------------------------ + +const assert = require("assert"); +const path = require("path"); +const ignore = require("ignore"); +const debug = require("debug")("eslint:ignore-pattern"); + +// debug.enabled = true; + +/** @typedef {ReturnType} Ignore */ + +//------------------------------------------------------------------------------ +// Helpers +//------------------------------------------------------------------------------ + +/** + * Get the path to the common ancestor directory of given paths. + * @param {string[]} sourcePaths The paths to calculate the common ancestor. + * @returns {string} The path tp the common ancestor directory. + */ +function getCommonAncestorPath(sourcePaths) { + let result = sourcePaths[0]; + + for (let i = 1; i < sourcePaths.length; ++i) { + const a = result; + const b = sourcePaths[i]; + + // Set the shorter one (it's the common ancestor if one includes the other). + result = a.length < b.length ? a : b; + + // Set the common ancestor. + for (let j = 0, lastSepPos = 0; j < a.length && j < b.length; ++j) { + if (a[j] !== b[j]) { + result = a.slice(0, lastSepPos); + break; + } + if (a[j] === path.sep) { + lastSepPos = j; + } + } + } + + return result; +} + +/** + * Make relative path. + * @param {string} from The source path to get relative path. + * @param {string} to The destination path to get relative path. + * @returns {string} The relative path. + */ +function relative(from, to) { + const relPath = path.relative(from, to); + + if (path.sep === "/") { + return relPath; + } + return relPath.split(path.sep).join("/"); +} + +/** + * Get the trailing slash if existed. + * @param {string} filePath The path to check. + * @returns {string} The trailing slash if existed. + */ +function dirSuffix(filePath) { + const isDir = ( + filePath.endsWith(path.sep) || + (process.platform === "win32" && filePath.endsWith("/")) + ); + + return isDir ? "/" : ""; +} + +const DefaultPatterns = Object.freeze(["/node_modules/*", "/bower_components/*"]); +const DotPatterns = Object.freeze([".*", "!../"]); + +//------------------------------------------------------------------------------ +// Public +//------------------------------------------------------------------------------ + +class IgnorePattern { + + /** + * The default patterns. + * @type {string[]} + */ + static get DefaultPatterns() { + return DefaultPatterns; + } + + // eslint-disable-next-line valid-jsdoc + /** + * Create the default predicate function. + * @param {string} cwd The current working directory. + * @returns {((filePath:string, dot:boolean) => boolean) & {basePath:string; patterns:string[]}} + * The preficate function. + * The first argument is an absolute path that is checked. + * The second argument is the flag to not ignore dotfiles. + * If the predicate function returned `true`, it means the path should be ignored. + */ + static createDefaultIgnore(cwd) { + return this.createIgnore([new IgnorePattern(DefaultPatterns, cwd)]); + } + + // eslint-disable-next-line valid-jsdoc + /** + * Create the predicate function from multiple `IgnorePattern` objects. + * @param {IgnorePattern[]} ignorePatterns The list of ignore patterns. + * @returns {((filePath:string, dot?:boolean) => boolean) & {basePath:string; patterns:string[]}} + * The preficate function. + * The first argument is an absolute path that is checked. + * The second argument is the flag to not ignore dotfiles. + * If the predicate function returned `true`, it means the path should be ignored. + */ + static createIgnore(ignorePatterns) { + debug("Create with: %o", ignorePatterns); + + const basePath = getCommonAncestorPath(ignorePatterns.map(p => p.basePath)); + const patterns = [].concat( + ...ignorePatterns.map(p => p.getPatternsRelativeTo(basePath)) + ); + const ig = ignore().add([...DotPatterns, ...patterns]); + const dotIg = ignore().add(patterns); + + debug(" processed: %o", { basePath, patterns }); + + return Object.assign( + (filePath, dot = false) => { + assert(path.isAbsolute(filePath), "'filePath' should be an absolute path."); + const relPathRaw = relative(basePath, filePath); + const relPath = relPathRaw && (relPathRaw + dirSuffix(filePath)); + const adoptedIg = dot ? dotIg : ig; + const result = relPath !== "" && adoptedIg.ignores(relPath); + + debug("Check", { filePath, dot, relativePath: relPath, result }); + return result; + }, + { basePath, patterns } + ); + } + + /** + * @param {string[]} patterns The glob patterns that ignore to lint. + * @param {string} basePath The base path of `patterns`. + */ + constructor(patterns, basePath) { + assert(path.isAbsolute(basePath), "'basePath' should be an absolute path."); + + /** + * The glob patterns that ignore to lint. + * @type {string[]} + */ + this.patterns = patterns; + + /** + * The base path of `patterns`. + * @type {string} + */ + this.basePath = basePath; + + /** + * If `true` then patterns which don't start with `/` will match the paths to the outside of `basePath`. Defaults to `false`. + * + * It's set `true` for `.eslintignore`, `package.json`, and `--ignore-path` for backward compatibility. + * It's `false` as-is for `ignorePatterns` property in config files. + * @type {boolean} + */ + this.loose = false; + } + + /** + * Get `patterns` as modified for a given base path. It modifies the + * absolute paths in the patterns as prepending the difference of two base + * paths. + * @param {string} newBasePath The base path. + * @returns {string[]} Modifired patterns. + */ + getPatternsRelativeTo(newBasePath) { + assert(path.isAbsolute(newBasePath), "'newBasePath' should be an absolute path."); + const { basePath, loose, patterns } = this; + + if (newBasePath === basePath) { + return patterns; + } + const prefix = `/${relative(newBasePath, basePath)}`; + + return patterns.map(pattern => { + const negative = pattern.startsWith("!"); + const head = negative ? "!" : ""; + const body = negative ? pattern.slice(1) : pattern; + + if (body.startsWith("/") || body.startsWith("../")) { + return `${head}${prefix}${body}`; + } + return loose ? pattern : `${head}${prefix}/**/${body}`; + }); + } +} + +module.exports = { IgnorePattern }; diff --git a/lib/cli-engine/config-array/index.js b/lib/cli-engine/config-array/index.js index de8831906fd..928d76c83ab 100644 --- a/lib/cli-engine/config-array/index.js +++ b/lib/cli-engine/config-array/index.js @@ -7,12 +7,14 @@ const { ConfigArray, getUsedExtractedConfigs } = require("./config-array"); const { ConfigDependency } = require("./config-dependency"); const { ExtractedConfig } = require("./extracted-config"); +const { IgnorePattern } = require("./ignore-pattern"); const { OverrideTester } = require("./override-tester"); module.exports = { ConfigArray, ConfigDependency, ExtractedConfig, + IgnorePattern, OverrideTester, getUsedExtractedConfigs }; diff --git a/lib/cli-engine/file-enumerator.js b/lib/cli-engine/file-enumerator.js index a027359ae51..1c73505aada 100644 --- a/lib/cli-engine/file-enumerator.js +++ b/lib/cli-engine/file-enumerator.js @@ -40,8 +40,8 @@ const getGlobParent = require("glob-parent"); const isGlob = require("is-glob"); const { escapeRegExp } = require("lodash"); const { Minimatch } = require("minimatch"); +const { IgnorePattern } = require("./config-array"); const { CascadingConfigArrayFactory } = require("./cascading-config-array-factory"); -const { IgnoredPaths } = require("./ignored-paths"); const debug = require("debug")("eslint:file-enumerator"); //------------------------------------------------------------------------------ @@ -64,7 +64,6 @@ const IGNORED = 2; * @property {string[]} [extensions] The extensions to match files for directory patterns. * @property {boolean} [globInputPaths] Set to false to skip glob resolution of input file paths to lint (default: true). If false, each input file paths is assumed to be a non-glob path to an existing file. * @property {boolean} [ignore] The flag to check ignored files. - * @property {IgnoredPaths} [ignoredPaths] The ignored paths. * @property {string[]} [rulePaths] The value of `--rulesdir` option. */ @@ -92,8 +91,7 @@ const IGNORED = 2; * @property {RegExp} extensionRegExp The RegExp to test if a string ends with specific file extensions. * @property {boolean} globInputPaths Set to false to skip glob resolution of input file paths to lint (default: true). If false, each input file paths is assumed to be a non-glob path to an existing file. * @property {boolean} ignoreFlag The flag to check ignored files. - * @property {IgnoredPaths} ignoredPathsWithDotfiles The ignored paths but don't include dot files. - * @property {IgnoredPaths} ignoredPaths The ignored paths. + * @property {(filePath:string, dot:boolean) => boolean} defaultIgnores The default predicate function to ignore files. */ /** @type {WeakMap} */ @@ -190,12 +188,12 @@ class FileEnumerator { configArrayFactory = new CascadingConfigArrayFactory({ cwd }), extensions = [".js"], globInputPaths = true, - ignore = true, - ignoredPaths = new IgnoredPaths({ cwd, ignore }) + ignore = true } = {}) { internalSlotsMap.set(this, { configArrayFactory, cwd, + defaultIgnores: IgnorePattern.createDefaultIgnore(cwd), extensionRegExp: new RegExp( `.\\.(?:${extensions .map(ext => escapeRegExp( @@ -208,12 +206,7 @@ class FileEnumerator { "u" ), globInputPaths, - ignoreFlag: ignore, - ignoredPaths, - ignoredPathsWithDotfiles: new IgnoredPaths({ - ...ignoredPaths.options, - dotfiles: true - }) + ignoreFlag: ignore }); } @@ -319,7 +312,7 @@ class FileEnumerator { const { configArrayFactory } = internalSlotsMap.get(this); const config = configArrayFactory.getConfigArrayForFile(filePath); - const ignored = this._isIgnoredFile(filePath, { direct: true }); + const ignored = this._isIgnoredFile(filePath, { config, direct: true }); const flag = ignored ? IGNORED : NONE; return [{ config, filePath, flag }]; @@ -351,7 +344,7 @@ class FileEnumerator { _iterateFilesWithGlob(pattern, dotfiles) { debug(`Glob: ${pattern}`); - const directoryPath = getGlobParent(pattern); + const directoryPath = path.resolve(getGlobParent(pattern)); const globPart = pattern.slice(directoryPath.length + 1); /* @@ -397,9 +390,18 @@ class FileEnumerator { // Check if the file is matched. if (stat && stat.isFile()) { if (!config) { - config = configArrayFactory.getConfigArrayForFile(filePath); + config = configArrayFactory.getConfigArrayForFile( + filePath, + + /* + * We must ignore `ConfigurationNotFoundError` at this + * point because we don't know if target files exist in + * this directory. + */ + { ignoreNotFoundError: true } + ); } - const ignored = this._isIgnoredFile(filePath, options); + const ignored = this._isIgnoredFile(filePath, { ...options, config }); const flag = ignored ? IGNORED_SILENTLY : NONE; const matched = options.selector @@ -411,7 +413,11 @@ class FileEnumerator { if (matched) { debug(`Yield: ${filename}${ignored ? " but ignored" : ""}`); - yield { config, filePath, flag }; + yield { + config: configArrayFactory.getConfigArrayForFile(filePath), + filePath, + flag + }; } else { debug(`Didn't match: ${filename}`); } @@ -429,24 +435,37 @@ class FileEnumerator { * Check if a given file should be ignored. * @param {string} filePath The path to a file to check. * @param {Object} options Options + * @param {ConfigArray} [options.config] The config for this file. * @param {boolean} [options.dotfiles] If `true` then this is not ignore dot files by default. * @param {boolean} [options.direct] If `true` then this is a direct specified file. * @returns {boolean} `true` if the file should be ignored. * @private */ - _isIgnoredFile(filePath, { dotfiles = false, direct = false }) { + _isIgnoredFile(filePath, { + config: providedConfig, + dotfiles = false, + direct = false + }) { const { - ignoreFlag, - ignoredPaths, - ignoredPathsWithDotfiles + configArrayFactory, + defaultIgnores, + ignoreFlag } = internalSlotsMap.get(this); - const adoptedIgnoredPaths = dotfiles - ? ignoredPathsWithDotfiles - : ignoredPaths; - return ignoreFlag - ? adoptedIgnoredPaths.contains(filePath) - : (!direct && adoptedIgnoredPaths.contains(filePath, "default")); + if (ignoreFlag) { + const config = + providedConfig || + configArrayFactory.getConfigArrayForFile( + filePath, + { ignoreNotFoundError: true } + ); + const ignores = + config.extractConfig(filePath).ignores || defaultIgnores; + + return ignores(filePath, dotfiles); + } + + return !direct && defaultIgnores(filePath, dotfiles); } } diff --git a/lib/cli-engine/ignored-paths.js b/lib/cli-engine/ignored-paths.js deleted file mode 100644 index 137d156e9c0..00000000000 --- a/lib/cli-engine/ignored-paths.js +++ /dev/null @@ -1,362 +0,0 @@ -/** - * @fileoverview Responsible for loading ignore config files and managing ignore patterns - * @author Jonathan Rajavuori - */ - -"use strict"; - -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - -const fs = require("fs"), - path = require("path"), - ignore = require("ignore"); - -const debug = require("debug")("eslint:ignored-paths"); - -//------------------------------------------------------------------------------ -// Constants -//------------------------------------------------------------------------------ - -const ESLINT_IGNORE_FILENAME = ".eslintignore"; - -/** - * Adds `"*"` at the end of `"node_modules/"`, - * so that subtle directories could be re-included by .gitignore patterns - * such as `"!node_modules/should_not_ignored"` - */ -const DEFAULT_IGNORE_DIRS = [ - "/node_modules/*", - "/bower_components/*" -]; -const DEFAULT_OPTIONS = { - dotfiles: false, - cwd: process.cwd() -}; - -//------------------------------------------------------------------------------ -// Helpers -//------------------------------------------------------------------------------ - -/** - * Find a file in the current directory. - * @param {string} cwd Current working directory - * @param {string} name File name - * @returns {string} Path of ignore file or an empty string. - */ -function findFile(cwd, name) { - const ignoreFilePath = path.resolve(cwd, name); - - return fs.existsSync(ignoreFilePath) && fs.statSync(ignoreFilePath).isFile() ? ignoreFilePath : ""; -} - -/** - * Find an ignore file in the current directory. - * @param {string} cwd Current working directory - * @returns {string} Path of ignore file or an empty string. - */ -function findIgnoreFile(cwd) { - return findFile(cwd, ESLINT_IGNORE_FILENAME); -} - -/** - * Find an package.json file in the current directory. - * @param {string} cwd Current working directory - * @returns {string} Path of package.json file or an empty string. - */ -function findPackageJSONFile(cwd) { - return findFile(cwd, "package.json"); -} - -/** - * Merge options with defaults - * @param {Object} options Options to merge with DEFAULT_OPTIONS constant - * @returns {Object} Merged options - */ -function mergeDefaultOptions(options) { - return Object.assign({}, DEFAULT_OPTIONS, options); -} - -/* eslint-disable valid-jsdoc */ -/** - * Normalize the path separators in a given string. - * On Windows environment, this replaces `\` by `/`. - * Otherwrise, this does nothing. - * @param {string} str The path string to normalize. - * @returns {string} The normalized path. - */ -const normalizePathSeps = path.sep === "/" - ? (str => str) - : ((seps, str) => str.replace(seps, "/")).bind(null, new RegExp(`\\${path.sep}`, "gu")); -/* eslint-enable valid-jsdoc */ - -/** - * Converts a glob pattern to a new glob pattern relative to a different directory - * @param {string} globPattern The glob pattern, relative the the old base directory - * @param {string} relativePathToOldBaseDir A relative path from the new base directory to the old one - * @returns {string} A glob pattern relative to the new base directory - */ -function relativize(globPattern, relativePathToOldBaseDir) { - if (relativePathToOldBaseDir === "") { - return globPattern; - } - - const prefix = globPattern.startsWith("!") ? "!" : ""; - const globWithoutPrefix = globPattern.replace(/^!/u, ""); - - if (globWithoutPrefix.startsWith("/")) { - return `${prefix}/${normalizePathSeps(relativePathToOldBaseDir)}${globWithoutPrefix}`; - } - - return globPattern; -} - -//------------------------------------------------------------------------------ -// Public Interface -//------------------------------------------------------------------------------ - -/** - * IgnoredPaths class - */ -class IgnoredPaths { - - /** - * @param {Object} providedOptions object containing 'ignore', 'ignorePath' and 'patterns' properties - */ - constructor(providedOptions) { - const options = mergeDefaultOptions(providedOptions); - - this.cache = {}; - - this.defaultPatterns = [].concat(DEFAULT_IGNORE_DIRS, options.patterns || []); - - this.ignoreFileDir = options.ignore !== false && options.ignorePath - ? path.dirname(path.resolve(options.cwd, options.ignorePath)) - : options.cwd; - this.options = options; - this._baseDir = null; - - this.ig = { - custom: ignore(), - default: ignore() - }; - - this.defaultPatterns.forEach(pattern => this.addPatternRelativeToCwd(this.ig.default, pattern)); - if (options.dotfiles !== true) { - - /* - * ignore files beginning with a dot, but not files in a parent or - * ancestor directory (which in relative format will begin with `../`). - */ - this.addPatternRelativeToCwd(this.ig.default, ".*"); - this.addPatternRelativeToCwd(this.ig.default, "!../"); - } - - /* - * Add a way to keep track of ignored files. This was present in node-ignore - * 2.x, but dropped for now as of 3.0.10. - */ - this.ig.custom.ignoreFiles = []; - this.ig.default.ignoreFiles = []; - - if (options.ignore !== false) { - let ignorePath; - - if (options.ignorePath) { - debug("Using specific ignore file"); - - try { - const stat = fs.statSync(options.ignorePath); - - if (!stat.isFile()) { - throw new Error(`${options.ignorePath} is not a file`); - } - ignorePath = options.ignorePath; - } catch (e) { - e.message = `Cannot read ignore file: ${options.ignorePath}\nError: ${e.message}`; - throw e; - } - } else { - debug(`Looking for ignore file in ${options.cwd}`); - ignorePath = findIgnoreFile(options.cwd); - - try { - fs.statSync(ignorePath); - debug(`Loaded ignore file ${ignorePath}`); - } catch (e) { - debug("Could not find ignore file in cwd"); - } - } - - if (ignorePath) { - debug(`Adding ${ignorePath}`); - this.addIgnoreFile(this.ig.custom, ignorePath); - this.addIgnoreFile(this.ig.default, ignorePath); - } else { - try { - - // if the ignoreFile does not exist, check package.json for eslintIgnore - const packageJSONPath = findPackageJSONFile(options.cwd); - - if (packageJSONPath) { - let packageJSONOptions; - - try { - packageJSONOptions = JSON.parse(fs.readFileSync(packageJSONPath, "utf8")); - } catch (e) { - debug("Could not read package.json file to check eslintIgnore property"); - e.messageTemplate = "failed-to-read-json"; - e.messageData = { - path: packageJSONPath, - message: e.message - }; - throw e; - } - - if (packageJSONOptions.eslintIgnore) { - if (Array.isArray(packageJSONOptions.eslintIgnore)) { - packageJSONOptions.eslintIgnore.forEach(pattern => { - this.addPatternRelativeToIgnoreFile(this.ig.custom, pattern); - this.addPatternRelativeToIgnoreFile(this.ig.default, pattern); - }); - } else { - throw new TypeError("Package.json eslintIgnore property requires an array of paths"); - } - } - } - } catch (e) { - debug("Could not find package.json to check eslintIgnore property"); - throw e; - } - } - - if (options.ignorePattern) { - this.addPatternRelativeToCwd(this.ig.custom, options.ignorePattern); - this.addPatternRelativeToCwd(this.ig.default, options.ignorePattern); - } - } - } - - /* - * If `ignoreFileDir` is a subdirectory of `cwd`, all paths will be normalized to be relative to `cwd`. - * Otherwise, all paths will be normalized to be relative to `ignoreFileDir`. - * This ensures that the final normalized ignore rule will not contain `..`, which is forbidden in - * ignore rules. - */ - - addPatternRelativeToCwd(ig, pattern) { - const baseDir = this.getBaseDir(); - const cookedPattern = baseDir === this.options.cwd - ? pattern - : relativize(pattern, path.relative(baseDir, this.options.cwd)); - - ig.addPattern(cookedPattern); - debug("addPatternRelativeToCwd:\n original = %j\n cooked = %j", pattern, cookedPattern); - } - - addPatternRelativeToIgnoreFile(ig, pattern) { - const baseDir = this.getBaseDir(); - const cookedPattern = baseDir === this.ignoreFileDir - ? pattern - : relativize(pattern, path.relative(baseDir, this.ignoreFileDir)); - - ig.addPattern(cookedPattern); - debug("addPatternRelativeToIgnoreFile:\n original = %j\n cooked = %j", pattern, cookedPattern); - } - - // Detect the common ancestor - getBaseDir() { - if (!this._baseDir) { - const a = path.resolve(this.options.cwd); - const b = path.resolve(this.ignoreFileDir); - let lastSepPos = 0; - - // Set the shorter one (it's the common ancestor if one includes the other). - this._baseDir = a.length < b.length ? a : b; - - // Set the common ancestor. - for (let i = 0; i < a.length && i < b.length; ++i) { - if (a[i] !== b[i]) { - this._baseDir = a.slice(0, lastSepPos); - break; - } - if (a[i] === path.sep) { - lastSepPos = i; - } - } - - // If it's only Windows drive letter, it needs \ - if (/^[A-Z]:$/u.test(this._baseDir)) { - this._baseDir += "\\"; - } - - debug("baseDir = %j", this._baseDir); - } - return this._baseDir; - } - - /** - * read ignore filepath - * @param {string} filePath, file to add to ig - * @returns {Array} raw ignore rules - */ - readIgnoreFile(filePath) { - if (typeof this.cache[filePath] === "undefined") { - this.cache[filePath] = fs.readFileSync(filePath, "utf8").split(/\r?\n/gu).filter(Boolean); - } - return this.cache[filePath]; - } - - /** - * add ignore file to node-ignore instance - * @param {Object} ig instance of node-ignore - * @param {string} filePath file to add to ig - * @returns {void} - */ - addIgnoreFile(ig, filePath) { - ig.ignoreFiles.push(filePath); - this - .readIgnoreFile(filePath) - .forEach(ignoreRule => this.addPatternRelativeToIgnoreFile(ig, ignoreRule)); - } - - /** - * Determine whether a file path is included in the default or custom ignore patterns - * @param {string} filepath Path to check - * @param {string} [category=undefined] check 'default', 'custom' or both (undefined) - * @returns {boolean} true if the file path matches one or more patterns, false otherwise - */ - contains(filepath, category) { - const isDir = filepath.endsWith(path.sep) || - (path.sep === "\\" && filepath.endsWith("/")); - let result = false; - const basePath = this.getBaseDir(); - const absolutePath = path.resolve(this.options.cwd, filepath); - let relativePath = path.relative(basePath, absolutePath); - - if (relativePath) { - if (isDir) { - relativePath += path.sep; - } - if (typeof category === "undefined") { - result = - (this.ig.default.filter([relativePath]).length === 0) || - (this.ig.custom.filter([relativePath]).length === 0); - } else { - result = - (this.ig[category].filter([relativePath]).length === 0); - } - } - debug("contains:"); - debug(" target = %j", filepath); - debug(" base = %j", basePath); - debug(" relative = %j", relativePath); - debug(" result = %j", result); - - return result; - - } -} - -module.exports = { IgnoredPaths }; diff --git a/lib/shared/types.js b/lib/shared/types.js index 12bd0aed8c6..57740b6c951 100644 --- a/lib/shared/types.js +++ b/lib/shared/types.js @@ -30,6 +30,7 @@ module.exports = {}; * @property {Record} [env] The environment settings. * @property {string | string[]} [extends] The path to other config files or the package name of shareable configs. * @property {Record} [globals] The global variable settings. + * @property {string | string[]} [ignorePatterns] The glob patterns that ignore to lint. * @property {boolean} [noInlineConfig] The flag that disables directive comments. * @property {OverrideConfigData[]} [overrides] The override settings per kind of files. * @property {string} [parser] The path to a parser or the package name of a parser. diff --git a/tests/lib/cli-engine/_utils.js b/tests/lib/cli-engine/_utils.js index a3c56484c64..12e779f060c 100644 --- a/tests/lib/cli-engine/_utils.js +++ b/tests/lib/cli-engine/_utils.js @@ -69,8 +69,6 @@ const ConfigArrayFactoryPath = require.resolve("../../../lib/cli-engine/config-array-factory"); const FileEnumeratorPath = require.resolve("../../../lib/cli-engine/file-enumerator"); -const IgnoredPathsPath = - require.resolve("../../../lib/cli-engine/ignored-paths"); const LoadRulesPath = require.resolve("../../../lib/cli-engine/load-rules"); const ESLintAllPath = @@ -83,7 +81,6 @@ require(CascadingConfigArrayFactoryPath); require(CLIEnginePath); require(ConfigArrayFactoryPath); require(FileEnumeratorPath); -require(IgnoredPathsPath); require(LoadRulesPath); require("js-yaml"); require("espree"); @@ -366,7 +363,7 @@ function defineCascadingConfigArrayFactoryWithInMemoryFileSystem({ * @param {Object} options The options. * @param {() => string} [options.cwd] The current working directory. * @param {Object} [options.files] The initial files definition in the in-memory file system. - * @returns {{ fs: import("fs"), RelativeModuleResolver: import("../../../lib/shared/relative-module-resolver"), ConfigArrayFactory: import("../../../lib/cli-engine/config-array-factory")["ConfigArrayFactory"], CascadingConfigArrayFactory: import("../../../lib/cli-engine/cascading-config-array-factory")["CascadingConfigArrayFactory"], IgnoredPaths: import("../../../lib/cli-engine/ignored-paths")["IgnoredPaths"], FileEnumerator: import("../../../lib/cli-engine/file-enumerator")["FileEnumerator"] }} The stubbed `FileEnumerator` class. + * @returns {{ fs: import("fs"), RelativeModuleResolver: import("../../../lib/shared/relative-module-resolver"), ConfigArrayFactory: import("../../../lib/cli-engine/config-array-factory")["ConfigArrayFactory"], CascadingConfigArrayFactory: import("../../../lib/cli-engine/cascading-config-array-factory")["CascadingConfigArrayFactory"], FileEnumerator: import("../../../lib/cli-engine/file-enumerator")["FileEnumerator"] }} The stubbed `FileEnumerator` class. */ function defineFileEnumeratorWithInMemoryFileSystem({ cwd = process.cwd, @@ -379,11 +376,9 @@ function defineFileEnumeratorWithInMemoryFileSystem({ CascadingConfigArrayFactory } = defineCascadingConfigArrayFactoryWithInMemoryFileSystem({ cwd, files }); - const { IgnoredPaths } = proxyquire(IgnoredPathsPath, { fs }); const { FileEnumerator } = proxyquire(FileEnumeratorPath, { fs, - "./cascading-config-array-factory": { CascadingConfigArrayFactory }, - "./ignored-paths": { IgnoredPaths } + "./cascading-config-array-factory": { CascadingConfigArrayFactory } }); // Override the default cwd. @@ -392,7 +387,6 @@ function defineFileEnumeratorWithInMemoryFileSystem({ RelativeModuleResolver, ConfigArrayFactory, CascadingConfigArrayFactory, - IgnoredPaths, FileEnumerator: cwd === process.cwd ? FileEnumerator : class extends FileEnumerator { @@ -408,7 +402,7 @@ function defineFileEnumeratorWithInMemoryFileSystem({ * @param {Object} options The options. * @param {() => string} [options.cwd] The current working directory. * @param {Object} [options.files] The initial files definition in the in-memory file system. - * @returns {{ fs: import("fs"), RelativeModuleResolver: import("../../../lib/shared/relative-module-resolver"), ConfigArrayFactory: import("../../../lib/cli-engine/config-array-factory")["ConfigArrayFactory"], CascadingConfigArrayFactory: import("../../../lib/cli-engine/cascading-config-array-factory")["CascadingConfigArrayFactory"], IgnoredPaths: import("../../../lib/cli-engine/ignored-paths")["IgnoredPaths"], FileEnumerator: import("../../../lib/cli-engine/file-enumerator")["FileEnumerator"], CLIEngine: import("../../../lib/cli-engine/cli-engine")["CLIEngine"], getCLIEngineInternalSlots: import("../../../lib/cli-engine/cli-engine")["getCLIEngineInternalSlots"] }} The stubbed `CLIEngine` class. + * @returns {{ fs: import("fs"), RelativeModuleResolver: import("../../../lib/shared/relative-module-resolver"), ConfigArrayFactory: import("../../../lib/cli-engine/config-array-factory")["ConfigArrayFactory"], CascadingConfigArrayFactory: import("../../../lib/cli-engine/cascading-config-array-factory")["CascadingConfigArrayFactory"], FileEnumerator: import("../../../lib/cli-engine/file-enumerator")["FileEnumerator"], CLIEngine: import("../../../lib/cli-engine/cli-engine")["CLIEngine"], getCLIEngineInternalSlots: import("../../../lib/cli-engine/cli-engine")["getCLIEngineInternalSlots"] }} The stubbed `CLIEngine` class. */ function defineCLIEngineWithInMemoryFileSystem({ cwd = process.cwd, @@ -419,7 +413,6 @@ function defineCLIEngineWithInMemoryFileSystem({ RelativeModuleResolver, ConfigArrayFactory, CascadingConfigArrayFactory, - IgnoredPaths, FileEnumerator } = defineFileEnumeratorWithInMemoryFileSystem({ cwd, files }); @@ -427,7 +420,6 @@ function defineCLIEngineWithInMemoryFileSystem({ fs, "./cascading-config-array-factory": { CascadingConfigArrayFactory }, "./file-enumerator": { FileEnumerator }, - "./ignored-paths": { IgnoredPaths }, "../shared/relative-module-resolver": RelativeModuleResolver }); @@ -437,7 +429,6 @@ function defineCLIEngineWithInMemoryFileSystem({ RelativeModuleResolver, ConfigArrayFactory, CascadingConfigArrayFactory, - IgnoredPaths, FileEnumerator, CLIEngine: cwd === process.cwd ? CLIEngine diff --git a/tests/lib/cli-engine/cascading-config-array-factory.js b/tests/lib/cli-engine/cascading-config-array-factory.js index e0ca4f06378..814c7a33513 100644 --- a/tests/lib/cli-engine/cascading-config-array-factory.js +++ b/tests/lib/cli-engine/cascading-config-array-factory.js @@ -14,6 +14,11 @@ const { ConfigArrayFactory } = require("../../../lib/cli-engine/config-array-fac const { ExtractedConfig } = require("../../../lib/cli-engine/config-array/extracted-config"); const { defineCascadingConfigArrayFactoryWithInMemoryFileSystem } = require("./_utils"); +const cwdIgnorePatterns = new ConfigArrayFactory() + .loadDefaultESLintIgnore()[0] + .ignorePattern + .patterns; + describe("CascadingConfigArrayFactory", () => { describe("'getConfigArrayForFile(filePath)' method should retrieve the proper configuration.", () => { describe("with three directories ('lib', 'lib/nested', 'test') that contains 'one.js' and 'two.js'", () => { @@ -56,23 +61,29 @@ describe("CascadingConfigArrayFactory", () => { it("should retrieve the config '.eslintrc.json' if 'lib/one.js' was given.", () => { const config = factory.getConfigArrayForFile("lib/one.js"); - assert.strictEqual(config.length, 1); - assert.strictEqual(config[0].filePath, path.join(root, ".eslintrc.json")); + assert.strictEqual(config.length, 3); + assert.strictEqual(config[0].name, "DefaultIgnorePattern"); + assert.strictEqual(config[1].filePath, path.join(root, ".eslintrc.json")); + assert.strictEqual(config[2].filePath, path.join(root, ".eslintignore")); }); it("should retrieve the merged config of '.eslintrc.json' and 'lib/nested/.eslintrc.yml' if 'lib/nested/one.js' was given.", () => { const config = factory.getConfigArrayForFile("lib/nested/one.js"); - assert.strictEqual(config.length, 2); - assert.strictEqual(config[0].filePath, path.join(root, ".eslintrc.json")); - assert.strictEqual(config[1].filePath, path.join(root, "lib/nested/.eslintrc.yml")); + assert.strictEqual(config.length, 4); + assert.strictEqual(config[0].name, "DefaultIgnorePattern"); + assert.strictEqual(config[1].filePath, path.join(root, ".eslintrc.json")); + assert.strictEqual(config[2].filePath, path.join(root, "lib/nested/.eslintrc.yml")); + assert.strictEqual(config[3].filePath, path.join(root, ".eslintignore")); }); it("should retrieve the config '.eslintrc.json' if 'lib/non-exist.js' was given.", () => { const config = factory.getConfigArrayForFile("lib/non-exist.js"); - assert.strictEqual(config.length, 1); - assert.strictEqual(config[0].filePath, path.join(root, ".eslintrc.json")); + assert.strictEqual(config.length, 3); + assert.strictEqual(config[0].name, "DefaultIgnorePattern"); + assert.strictEqual(config[1].filePath, path.join(root, ".eslintrc.json")); + assert.strictEqual(config[2].filePath, path.join(root, ".eslintignore")); }); }); @@ -282,7 +293,8 @@ describe("CascadingConfigArrayFactory", () => { env: { browser: true, node: false - } + }, + ignorePatterns: cwdIgnorePatterns }; const actual = getConfig(factory, file); @@ -296,7 +308,8 @@ describe("CascadingConfigArrayFactory", () => { const expected = { rules: {}, globals: {}, - env: {} + env: {}, + ignorePatterns: cwdIgnorePatterns }; const actual = getConfig(factory, file); @@ -309,7 +322,8 @@ describe("CascadingConfigArrayFactory", () => { const expected = { rules: {}, globals: {}, - env: {} + env: {}, + ignorePatterns: cwdIgnorePatterns }; const actual = getConfig(factory, file); @@ -322,7 +336,7 @@ describe("CascadingConfigArrayFactory", () => { const file = getFixturePath("broken", "console-wrong-quotes.js"); const actual = getConfig(factory, file); - assertConfigsEqual(actual, {}); + assertConfigsEqual(actual, { ignorePatterns: cwdIgnorePatterns }); }); it("should return a modified config when baseConfig is set to an object and no .eslintrc", () => { @@ -344,7 +358,8 @@ describe("CascadingConfigArrayFactory", () => { }, rules: { quotes: [2, "single"] - } + }, + ignorePatterns: cwdIgnorePatterns }; const actual = getConfig(factory, file); @@ -391,7 +406,8 @@ describe("CascadingConfigArrayFactory", () => { rules: { "no-console": [1], quotes: [2, "single"] - } + }, + ignorePatterns: cwdIgnorePatterns }; const actual = getConfig(factory, file); @@ -409,7 +425,8 @@ describe("CascadingConfigArrayFactory", () => { rules: { "no-console": [0], quotes: [1, "double"] - } + }, + ignorePatterns: cwdIgnorePatterns }; const actual = getConfig(factory, file); @@ -423,7 +440,8 @@ describe("CascadingConfigArrayFactory", () => { const expected = { rules: { semi: [2, "never"] - } + }, + ignorePatterns: cwdIgnorePatterns }; const actual = getConfig(factory, file); @@ -457,7 +475,8 @@ describe("CascadingConfigArrayFactory", () => { rules: { quotes: [2, "double"], semi: [1, "never"] - } + }, + ignorePatterns: cwdIgnorePatterns }; const actual = getConfig(factory, file); @@ -476,7 +495,8 @@ describe("CascadingConfigArrayFactory", () => { }, rules: { quotes: [0, "double"] - } + }, + ignorePatterns: cwdIgnorePatterns }; const actual = getConfig(factory, file); @@ -497,7 +517,8 @@ describe("CascadingConfigArrayFactory", () => { quotes: [2, "single"], "no-console": [1], semi: [1, "never"] - } + }, + ignorePatterns: cwdIgnorePatterns }; const actual = getConfig(factory, file); @@ -517,7 +538,8 @@ describe("CascadingConfigArrayFactory", () => { rules: { quotes: [0, "single"], "no-console": [1] - } + }, + ignorePatterns: cwdIgnorePatterns }; const actual = getConfig(factory, file); @@ -541,7 +563,8 @@ describe("CascadingConfigArrayFactory", () => { }, rules: { quotes: [1, "double"] - } + }, + ignorePatterns: cwdIgnorePatterns }; const actual = getConfig(factory, file); @@ -585,7 +608,8 @@ describe("CascadingConfigArrayFactory", () => { rules: { semi: [2, "always"], eqeqeq: [2] - } + }, + ignorePatterns: cwdIgnorePatterns }; const actual = getConfig(factory, file); @@ -599,7 +623,8 @@ describe("CascadingConfigArrayFactory", () => { const expected = { globals: { foo: true - } + }, + ignorePatterns: cwdIgnorePatterns }; const actual = getConfig(factory, configPath); @@ -638,7 +663,8 @@ describe("CascadingConfigArrayFactory", () => { const factory = new CascadingConfigArrayFactory({ useEslintrc: false, specificConfigPath: configPath }); const expected = { rules: { "no-empty": [1], "comma-dangle": [2], "no-console": [2] }, - env: { browser: false, node: true, es6: true } + env: { browser: false, node: true, es6: true }, + ignorePatterns: cwdIgnorePatterns }; const actual = getConfig(factory, configPath); @@ -652,7 +678,8 @@ describe("CascadingConfigArrayFactory", () => { const expected = { rules: {}, env: { commonjs: true }, - parserOptions: { ecmaFeatures: { globalReturn: false } } + parserOptions: { ecmaFeatures: { globalReturn: false } }, + ignorePatterns: cwdIgnorePatterns }; const actual = getConfig(factory, targetPath); @@ -1183,7 +1210,7 @@ describe("CascadingConfigArrayFactory", () => { it("should have a loading error in CLI config.", () => { const config = factory.getConfigArrayForFile("a.js"); - assert.strictEqual(config[1].plugins.test.definition, null); + assert.strictEqual(config[2].plugins.test.definition, null); }); it("should not have a loading error in CLI config after adding 'test' plugin to the additional plugin pool then calling 'clearCache()'.", () => { @@ -1196,7 +1223,7 @@ describe("CascadingConfigArrayFactory", () => { const config = factory.getConfigArrayForFile("a.js"); assert.deepStrictEqual( - config[1].plugins.test.definition, + config[2].plugins.test.definition, { configs: { name: "test" }, environments: {}, diff --git a/tests/lib/cli-engine/cli-engine.js b/tests/lib/cli-engine/cli-engine.js index 0c6db3ff93b..5028f6abaf9 100644 --- a/tests/lib/cli-engine/cli-engine.js +++ b/tests/lib/cli-engine/cli-engine.js @@ -111,7 +111,7 @@ describe("CLIEngine", () => { assert.throws(() => { // eslint-disable-next-line no-new new CLIEngine({ ignorePath: fixtureDir }); - }, `Cannot read ignore file: ${fixtureDir}\nError: ${fixtureDir} is not a file`); + }, `Cannot read .eslintignore file: ${fixtureDir}\nError: EISDIR: illegal operation on a directory, read`); }); // https://github.com/eslint/eslint/issues/2380 @@ -3673,14 +3673,6 @@ describe("CLIEngine", () => { }); describe("isPathIgnored", () => { - beforeEach(() => { - sinon.stub(console, "info").returns(void 0); - }); - - afterEach(() => { - sinon.restore(); - }); - it("should check if the given path is ignored", () => { const engine = new CLIEngine({ ignorePath: getFixturePath(".eslintignore2"), diff --git a/tests/lib/cli-engine/config-array-factory.js b/tests/lib/cli-engine/config-array-factory.js index 8b86b0f84ff..4f3ade1e60a 100644 --- a/tests/lib/cli-engine/config-array-factory.js +++ b/tests/lib/cli-engine/config-array-factory.js @@ -30,6 +30,7 @@ function assertConfigArrayElement(actual, providedExpected) { criteria: null, env: void 0, globals: void 0, + ignorePattern: void 0, noInlineConfig: void 0, parser: void 0, parserOptions: void 0, @@ -55,6 +56,7 @@ function assertConfig(actual, providedExpected) { const expected = { env: {}, globals: {}, + ignorePatterns: [], noInlineConfig: void 0, parser: null, parserOptions: {}, diff --git a/tests/lib/cli-engine/config-array/config-array.js b/tests/lib/cli-engine/config-array/config-array.js index 4e248af9f43..c5c300681b7 100644 --- a/tests/lib/cli-engine/config-array/config-array.js +++ b/tests/lib/cli-engine/config-array/config-array.js @@ -426,6 +426,7 @@ describe("ConfigArray", () => { configNameOfNoInlineConfig: "", env: {}, globals: {}, + ignores: void 0, noInlineConfig: void 0, parser: null, parserOptions: { @@ -458,6 +459,7 @@ describe("ConfigArray", () => { configNameOfNoInlineConfig: "", env: {}, globals: {}, + ignores: void 0, noInlineConfig: void 0, parser: null, parserOptions: { @@ -610,7 +612,8 @@ describe("ConfigArray", () => { settings: {}, processor: null, noInlineConfig: void 0, - reportUnusedDisableDirectives: void 0 + reportUnusedDisableDirectives: void 0, + ignores: void 0 }); assert.deepStrictEqual(config[0], { rules: { diff --git a/tests/lib/cli-engine/config-array/ignore-pattern.js b/tests/lib/cli-engine/config-array/ignore-pattern.js new file mode 100644 index 00000000000..443d350a2ed --- /dev/null +++ b/tests/lib/cli-engine/config-array/ignore-pattern.js @@ -0,0 +1,125 @@ +/** + * @fileoverview Tests for IgnorePattern class. + * @author Toru Nagashima + */ +"use strict"; + +const assert = require("assert"); +const path = require("path"); +const { IgnorePattern } = require("../../../../lib/cli-engine/config-array/ignore-pattern"); + +describe("IgnorePattern", () => { + describe("constructor(patterns, basePath)", () => { + it("should bind the first argument to 'patterns' property.", () => { + const p = new IgnorePattern(["a.js"], process.cwd()); + + assert.deepStrictEqual(p.patterns, ["a.js"]); + }); + + it("should bind the second argument to 'basePath' property.", () => { + const p = new IgnorePattern(["a.js"], process.cwd()); + + assert.strictEqual(p.basePath, process.cwd()); + }); + + it("should throw an error if the second argument was not an absolute path.", () => { + assert.throws(() => new IgnorePattern([], "a.js"), ""); + }); + }); + + describe("getPatternsRelativeTo(newBasePath)", () => { + it("should return 'patterns' as-is if the argument is the same as 'basePath'.", () => { + const basePath1 = path.join(process.cwd(), "foo/bar"); + const p = new IgnorePattern(["a.js", "/b.js", "!c.js", "!/d.js"], basePath1); + + assert.deepStrictEqual( + p.getPatternsRelativeTo(basePath1), + ["a.js", "/b.js", "!c.js", "!/d.js"] + ); + }); + + it("should return modified 'patterns' if the argument is different from 'basePath'.", () => { + const basePath1 = path.join(process.cwd(), "foo/bar"); + const basePath2 = process.cwd(); + const p = new IgnorePattern(["a.js", "/b.js", "!c.js", "!/d.js"], basePath1); + + assert.deepStrictEqual( + p.getPatternsRelativeTo(basePath2), + ["/foo/bar/**/a.js", "/foo/bar/b.js", "!/foo/bar/**/c.js", "!/foo/bar/d.js"] + ); + }); + }); + + describe("static createIgnore(ignorePatterns)", () => { + describe("with two patterns should return a function, and the function", () => { + const cwd = process.cwd(); + const basePath1 = path.join(cwd, "foo/bar"); + const basePath2 = path.join(cwd, "abc/"); + const ignores = IgnorePattern.createIgnore([ + new IgnorePattern(["*.js", "/*.ts", "!a.*", "!/b.*"], basePath1), + new IgnorePattern(["*.js", "/*.ts", "!a.*", "!/b.*"], basePath2) + ]); + const patterns = [ + ["a.js", false], + ["a.ts", false], + ["b.js", false], + ["b.ts", false], + ["c.js", false], + ["c.ts", false], + ["dir/a.js", false], + ["dir/a.ts", false], + ["dir/b.js", false], + ["dir/b.ts", false], + ["dir/c.js", false], + ["dir/c.ts", false], + ["foo/bar/a.js", false], + ["foo/bar/a.ts", false], + ["foo/bar/b.js", false], + ["foo/bar/b.ts", false], + ["foo/bar/c.js", true], + ["foo/bar/c.ts", true], + ["foo/bar/dir/a.js", false], + ["foo/bar/dir/a.ts", false], + ["foo/bar/dir/b.js", true], + ["foo/bar/dir/b.ts", false], + ["foo/bar/dir/c.js", true], + ["foo/bar/dir/c.ts", false], + ["abc/a.js", false], + ["abc/a.ts", false], + ["abc/b.js", false], + ["abc/b.ts", false], + ["abc/c.js", true], + ["abc/c.ts", true], + ["abc/dir/a.js", false], + ["abc/dir/a.ts", false], + ["abc/dir/b.js", true], + ["abc/dir/b.ts", false], + ["abc/dir/c.js", true], + ["abc/dir/c.ts", false] + ]; + + for (const [filename, expected] of patterns) { + it(`should return ${expected} if '${filename}' was given.`, () => { + assert.strictEqual(ignores(path.join(cwd, filename)), expected); + }); + } + + it("should return false if '.dot.js' and false was given.", () => { + assert.strictEqual(ignores(path.join(cwd, ".dot.js"), false), true); + }); + + it("should return true if '.dot.js' and true were given.", () => { + assert.strictEqual(ignores(path.join(cwd, ".dot.js"), true), false); + }); + + + it("should return false if '.dot/foo.js' and false was given.", () => { + assert.strictEqual(ignores(path.join(cwd, ".dot/foo.js"), false), true); + }); + + it("should return true if '.dot/foo.js' and true were given.", () => { + assert.strictEqual(ignores(path.join(cwd, ".dot/foo.js"), true), false); + }); + }); + }); +}); diff --git a/tests/lib/cli-engine/file-enumerator.js b/tests/lib/cli-engine/file-enumerator.js index 13ff479dba2..39a3981089b 100644 --- a/tests/lib/cli-engine/file-enumerator.js +++ b/tests/lib/cli-engine/file-enumerator.js @@ -11,7 +11,6 @@ const { assert } = require("chai"); const sh = require("shelljs"); const { CascadingConfigArrayFactory } = require("../../../lib/cli-engine/cascading-config-array-factory"); -const { IgnoredPaths } = require("../../../lib/cli-engine/ignored-paths"); const { defineFileEnumeratorWithInMemoryFileSystem } = require("./_utils"); describe("FileEnumerator", () => { @@ -82,8 +81,10 @@ describe("FileEnumerator", () => { it("should use the config '.eslintrc.json' for both files.", () => { assert.strictEqual(list[0].config, list[1].config); - assert.strictEqual(list[0].config.length, 1); - assert.strictEqual(list[0].config[0].filePath, path.join(root, ".eslintrc.json")); + assert.strictEqual(list[0].config.length, 3); + assert.strictEqual(list[0].config[0].name, "DefaultIgnorePattern"); + assert.strictEqual(list[0].config[1].filePath, path.join(root, ".eslintrc.json")); + assert.strictEqual(list[0].config[2].filePath, path.join(root, ".eslintignore")); }); }); @@ -114,15 +115,19 @@ describe("FileEnumerator", () => { it("should use the merged config of '.eslintrc.json' and 'lib/nested/.eslintrc.yml' for 'lib/nested/one.js' and 'lib/nested/two.js'.", () => { assert.strictEqual(list[0].config, list[1].config); - assert.strictEqual(list[0].config.length, 2); - assert.strictEqual(list[0].config[0].filePath, path.join(root, ".eslintrc.json")); - assert.strictEqual(list[0].config[1].filePath, path.join(root, "lib/nested/.eslintrc.yml")); + assert.strictEqual(list[0].config.length, 4); + assert.strictEqual(list[0].config[0].name, "DefaultIgnorePattern"); + assert.strictEqual(list[0].config[1].filePath, path.join(root, ".eslintrc.json")); + assert.strictEqual(list[0].config[2].filePath, path.join(root, "lib/nested/.eslintrc.yml")); + assert.strictEqual(list[0].config[3].filePath, path.join(root, ".eslintignore")); }); it("should use the config '.eslintrc.json' for 'lib/one.js' and 'lib/two.js'.", () => { assert.strictEqual(list[2].config, list[3].config); - assert.strictEqual(list[2].config.length, 1); - assert.strictEqual(list[2].config[0].filePath, path.join(root, ".eslintrc.json")); + assert.strictEqual(list[2].config.length, 3); + assert.strictEqual(list[2].config[0].name, "DefaultIgnorePattern"); + assert.strictEqual(list[2].config[1].filePath, path.join(root, ".eslintrc.json")); + assert.strictEqual(list[2].config[2].filePath, path.join(root, ".eslintignore")); }); }); @@ -153,15 +158,19 @@ describe("FileEnumerator", () => { it("should use the config '.eslintrc.json' for 'lib/one.js' and 'lib/two.js'.", () => { assert.strictEqual(list[0].config, list[1].config); - assert.strictEqual(list[0].config.length, 1); - assert.strictEqual(list[0].config[0].filePath, path.join(root, ".eslintrc.json")); + assert.strictEqual(list[0].config.length, 3); + assert.strictEqual(list[0].config[0].name, "DefaultIgnorePattern"); + assert.strictEqual(list[0].config[1].filePath, path.join(root, ".eslintrc.json")); + assert.strictEqual(list[0].config[2].filePath, path.join(root, ".eslintignore")); }); it("should use the merged config of '.eslintrc.json' and 'test/.eslintrc.yml' for 'test/one.js' and 'test/two.js'.", () => { assert.strictEqual(list[2].config, list[3].config); - assert.strictEqual(list[2].config.length, 2); - assert.strictEqual(list[2].config[0].filePath, path.join(root, ".eslintrc.json")); - assert.strictEqual(list[2].config[1].filePath, path.join(root, "test/.eslintrc.yml")); + assert.strictEqual(list[2].config.length, 4); + assert.strictEqual(list[2].config[0].name, "DefaultIgnorePattern"); + assert.strictEqual(list[2].config[1].filePath, path.join(root, ".eslintrc.json")); + assert.strictEqual(list[2].config[2].filePath, path.join(root, "test/.eslintrc.yml")); + assert.strictEqual(list[2].config[3].filePath, path.join(root, ".eslintignore")); }); }); }); @@ -195,8 +204,7 @@ describe("FileEnumerator", () => { // Disable "No Configuration Found" error. useEslintrc: false - }), - ignoredPaths: new IgnoredPaths(options) + }) }).iterateFiles(patterns), ({ filePath, ignored }) => ({ filename: filePath, ignored }) ); @@ -392,7 +400,7 @@ describe("FileEnumerator", () => { }); it("should ignore a file from a glob if matching a specified ignore pattern", () => { - const options = { ignore: true, ignorePattern: "foo.js", cwd: getFixturePath() }; + const options = { ignore: true, cliConfig: { ignorePatterns: ["foo.js"] }, cwd: getFixturePath() }; const patterns = [getFixturePath("glob-util", "ignored", "**/*.js")]; assert.throws(() => { @@ -418,7 +426,7 @@ describe("FileEnumerator", () => { }); it("should set 'ignored: true' for files that are explicitly specified but ignored", () => { - const options = { ignore: true, ignorePattern: "foo.js", cwd: getFixturePath() }; + const options = { ignore: true, cliConfig: { ignorePatterns: ["foo.js"] }, cwd: getFixturePath() }; const filename = getFixturePath("glob-util", "ignored", "foo.js"); const patterns = [filename]; const result = listFiles(patterns, options); @@ -440,7 +448,7 @@ describe("FileEnumerator", () => { }); it("should return unignored files from default ignored folders", () => { - const options = { ignorePattern: "!/node_modules/dependency.js", cwd: getFixturePath("glob-util") }; + const options = { cliConfig: { ignorePatterns: ["!/node_modules/dependency.js"] }, cwd: getFixturePath("glob-util") }; const glob = getFixturePath("glob-util", "**/*.js"); const patterns = [glob]; const result = listFiles(patterns, options); From a630d9e3f948018c41cfca00777517f0f614093a Mon Sep 17 00:00:00 2001 From: Toru Nagashima Date: Tue, 17 Sep 2019 07:02:26 +0900 Subject: [PATCH 02/13] move IgnoredPaths tests to cli-engine.js --- tests/lib/cli-engine/cli-engine.js | 410 ++++++++++++++++ tests/lib/cli-engine/ignored-paths.js | 658 -------------------------- 2 files changed, 410 insertions(+), 658 deletions(-) delete mode 100644 tests/lib/cli-engine/ignored-paths.js diff --git a/tests/lib/cli-engine/cli-engine.js b/tests/lib/cli-engine/cli-engine.js index 5028f6abaf9..95f0ef079f2 100644 --- a/tests/lib/cli-engine/cli-engine.js +++ b/tests/lib/cli-engine/cli-engine.js @@ -3703,6 +3703,416 @@ describe("CLIEngine", () => { assert.isTrue(engine.isPathIgnored("node_modules/foo.js")); }); + describe("about the default ignore patterns", () => { + it("should always apply defaultPatterns if ignore option is true", () => { + const cwd = getFixturePath("ignored-paths"); + const engine = new CLIEngine({ cwd }); + + assert(engine.isPathIgnored(getFixturePath("ignored-paths", "bower_components/package/file.js"))); + assert(engine.isPathIgnored(getFixturePath("ignored-paths", "node_modules/package/file.js"))); + }); + + it("should still apply defaultPatterns if ignore option is is false", () => { + const cwd = getFixturePath("ignored-paths"); + const engine = new CLIEngine({ ignore: false, cwd }); + + assert(engine.isPathIgnored(getFixturePath("ignored-paths", "bower_components/package/file.js"))); + assert(engine.isPathIgnored(getFixturePath("ignored-paths", "node_modules/package/file.js"))); + }); + + it("should not ignore files in defaultPatterns within a subdirectory", () => { + const cwd = getFixturePath("ignored-paths"); + const engine = new CLIEngine({ cwd }); + + assert(!engine.isPathIgnored(getFixturePath("ignored-paths", "subdir/bower_components/package/file.js"))); + assert(!engine.isPathIgnored(getFixturePath("ignored-paths", "subdir/node_modules/package/file.js"))); + }); + + it("should allow subfolders of defaultPatterns to be unignored by ignorePattern", () => { + const cwd = getFixturePath("ignored-paths"); + const engine = new CLIEngine({ cwd, ignorePattern: "!/node_modules/package" }); + + assert(!engine.isPathIgnored(getFixturePath("ignored-paths", "node_modules", "package", "file.js"))); + }); + + it("should allow subfolders of defaultPatterns to be unignored by ignorePath", () => { + const cwd = getFixturePath("ignored-paths"); + const engine = new CLIEngine({ cwd, ignorePath: getFixturePath("ignored-paths", ".eslintignoreWithUnignoredDefaults") }); + + assert(!engine.isPathIgnored(getFixturePath("ignored-paths", "node_modules", "package", "file.js"))); + }); + + it("should ignore dotfiles", () => { + const cwd = getFixturePath("ignored-paths"); + const engine = new CLIEngine({ cwd }); + + assert(engine.isPathIgnored(getFixturePath("ignored-paths", ".foo"))); + assert(engine.isPathIgnored(getFixturePath("ignored-paths", "foo/.bar"))); + }); + + it("should ignore directories beginning with a dot", () => { + const cwd = getFixturePath("ignored-paths"); + const engine = new CLIEngine({ cwd }); + + assert(engine.isPathIgnored(getFixturePath("ignored-paths", ".foo/bar"))); + assert(engine.isPathIgnored(getFixturePath("ignored-paths", "foo/.bar/baz"))); + }); + + it("should still ignore dotfiles when ignore option disabled", () => { + const cwd = getFixturePath("ignored-paths"); + const engine = new CLIEngine({ ignore: false, cwd }); + + assert(engine.isPathIgnored(getFixturePath("ignored-paths", ".foo"))); + assert(engine.isPathIgnored(getFixturePath("ignored-paths", "foo/.bar"))); + }); + + it("should still ignore directories beginning with a dot when ignore option disabled", () => { + const cwd = getFixturePath("ignored-paths"); + const engine = new CLIEngine({ ignore: false, cwd }); + + assert(engine.isPathIgnored(getFixturePath("ignored-paths", ".foo/bar"))); + assert(engine.isPathIgnored(getFixturePath("ignored-paths", "foo/.bar/baz"))); + }); + + it("should not ignore absolute paths containing '..'", () => { + const cwd = getFixturePath("ignored-paths"); + const engine = new CLIEngine({ cwd }); + + assert(!engine.isPathIgnored(`${getFixturePath("ignored-paths", "foo")}/../unignored.js`)); + }); + + it("should ignore /node_modules/ at top level relative to .eslintignore when loaded", () => { + const cwd = getFixturePath("ignored-paths"); + const engine = new CLIEngine({ ignorePath: getFixturePath("ignored-paths", ".eslintignore"), cwd }); + + assert(engine.isPathIgnored(getFixturePath("ignored-paths", "node_modules", "existing.js"))); + assert(!engine.isPathIgnored(getFixturePath("ignored-paths", "foo", "node_modules", "existing.js"))); + }); + + it("should ignore /node_modules/ at top level relative to cwd without an .eslintignore", () => { + const cwd = getFixturePath("ignored-paths", "no-ignore-file"); + const engine = new CLIEngine({ cwd }); + + assert(engine.isPathIgnored(getFixturePath("ignored-paths", "no-ignore-file", "node_modules", "existing.js"))); + assert(!engine.isPathIgnored(getFixturePath("ignored-paths", "no-ignore-file", "foo", "node_modules", "existing.js"))); + }); + }); + + describe("with no .eslintignore file", () => { + it("should not travel to parent directories to find .eslintignore when it's missing and cwd is provided", () => { + const cwd = getFixturePath("ignored-paths", "configurations"); + const engine = new CLIEngine({ cwd }); + + // a .eslintignore in parent directories includes `*.js`, but don't load it. + assert(!engine.isPathIgnored("foo.js")); + assert(engine.isPathIgnored("node_modules/foo.js")); + }); + + it("should return false for files outside of the cwd (with no ignore file provided)", () => { + + // Default ignore patterns should not inadvertantly ignore files in parent directories + const engine = new CLIEngine({ cwd: getFixturePath("ignored-paths", "no-ignore-file") }); + + assert(!engine.isPathIgnored(getFixturePath("ignored-paths", "undef.js"))); + }); + }); + + describe("with .eslintignore file or package.json file", () => { + it("should load .eslintignore from cwd when explicitly passed", () => { + const cwd = getFixturePath("ignored-paths"); + const engine = new CLIEngine({ cwd }); + + // `${cwd}/.eslintignore` includes `sampleignorepattern`. + assert(engine.isPathIgnored("sampleignorepattern")); + }); + + it("should use package.json's eslintIgnore files if no specified .eslintignore file", () => { + const cwd = getFixturePath("ignored-paths", "package-json-ignore"); + const engine = new CLIEngine({ cwd }); + + assert(engine.isPathIgnored("hello.js")); + assert(engine.isPathIgnored("world.js")); + }); + + it("should use correct message template if failed to parse package.json", () => { + const cwd = getFixturePath("ignored-paths", "broken-package-json"); + + assert.throw(() => { + try { + // eslint-disable-next-line no-new + new CLIEngine({ cwd }); + } catch (error) { + assert.strictEqual(error.messageTemplate, "failed-to-read-json"); + throw error; + } + }); + }); + + it("should not use package.json's eslintIgnore files if specified .eslintignore file", () => { + const cwd = getFixturePath("ignored-paths"); + const engine = new CLIEngine({ cwd }); + + /* + * package.json includes `hello.js` and `world.js`. + * .eslintignore includes `sampleignorepattern`. + */ + assert(!engine.isPathIgnored("hello.js")); + assert(!engine.isPathIgnored("world.js")); + assert(engine.isPathIgnored("sampleignorepattern")); + }); + + it("should error if package.json's eslintIgnore is not an array of file paths", () => { + const cwd = getFixturePath("ignored-paths", "bad-package-json-ignore"); + + assert.throws(() => { + // eslint-disable-next-line no-new + new CLIEngine({ cwd }); + }, "Package.json eslintIgnore property requires an array of paths"); + }); + }); + + describe("with --ignore-pattern option", () => { + it("should accept an array for options.ignorePattern", () => { + const cwd = getFixturePath("ignored-paths", "ignore-pattern"); + const engine = new CLIEngine({ + ignorePattern: "ignore-me.txt", + cwd + }); + + assert(engine.isPathIgnored("ignore-me.txt")); + }); + + it("should accept an array for options.ignorePattern", () => { + const engine = new CLIEngine({ + ignorePattern: ["a", "b"], + useEslintrc: false + }); + + assert(engine.isPathIgnored("a")); + assert(engine.isPathIgnored("b")); + assert(!engine.isPathIgnored("c")); + }); + + it("should return true for files which match an ignorePattern even if they do not exist on the filesystem", () => { + const cwd = getFixturePath("ignored-paths"); + const engine = new CLIEngine({ + ignorePattern: "not-a-file", + cwd + }); + + assert(engine.isPathIgnored(getFixturePath("ignored-paths", "not-a-file"))); + }); + + it("should return true for file matching an ignore pattern exactly", () => { + const cwd = getFixturePath("ignored-paths"); + const engine = new CLIEngine({ ignorePattern: "undef.js", cwd }); + + assert(engine.isPathIgnored(getFixturePath("ignored-paths", "undef.js"))); + }); + + it("should return false for file matching an invalid ignore pattern with leading './'", () => { + const cwd = getFixturePath("ignored-paths"); + const engine = new CLIEngine({ ignorePattern: "./undef.js", cwd }); + + assert(!engine.isPathIgnored(getFixturePath("ignored-paths", "undef.js"))); + }); + + it("should return false for file in subfolder of cwd matching an ignore pattern with leading '/'", () => { + const cwd = getFixturePath("ignored-paths"); + const engine = new CLIEngine({ ignorePattern: "/undef.js", cwd }); + + assert(!engine.isPathIgnored(getFixturePath("ignored-paths", "subdir", "undef.js"))); + }); + + it("should return true for file matching a child of an ignore pattern", () => { + const cwd = getFixturePath("ignored-paths"); + const engine = new CLIEngine({ ignorePattern: "ignore-pattern", cwd }); + + assert(engine.isPathIgnored(getFixturePath("ignored-paths", "ignore-pattern", "ignore-me.txt"))); + }); + + it("should return true for file matching a grandchild of an ignore pattern", () => { + const cwd = getFixturePath("ignored-paths"); + const engine = new CLIEngine({ ignorePattern: "ignore-pattern", cwd }); + + assert(engine.isPathIgnored(getFixturePath("ignored-paths", "ignore-pattern", "subdir", "ignore-me.txt"))); + }); + + it("should return false for file not matching any ignore pattern", () => { + const cwd = getFixturePath("ignored-paths"); + const engine = new CLIEngine({ ignorePattern: "failing.js", cwd }); + + assert(!engine.isPathIgnored(getFixturePath("ignored-paths", "unignored.js"))); + }); + + it("two globstar '**' ignore pattern should ignore files in nested directories", () => { + const cwd = getFixturePath("ignored-paths"); + const engine = new CLIEngine({ ignorePattern: "**/*.js", cwd }); + + assert(engine.isPathIgnored(getFixturePath("ignored-paths", "foo.js"))); + assert(engine.isPathIgnored(getFixturePath("ignored-paths", "foo/bar.js"))); + assert(engine.isPathIgnored(getFixturePath("ignored-paths", "foo/bar/baz.js"))); + assert(!engine.isPathIgnored(getFixturePath("ignored-paths", "foo.j2"))); + assert(!engine.isPathIgnored(getFixturePath("ignored-paths", "foo/bar.j2"))); + assert(!engine.isPathIgnored(getFixturePath("ignored-paths", "foo/bar/baz.j2"))); + }); + }); + + describe("with --ignore-path option", () => { + it("should load empty array with ignorePath set to false", () => { + const cwd = getFixturePath("ignored-paths", "no-ignore-file"); + const engine = new CLIEngine({ ignorePath: false, cwd }); + + // a .eslintignore in parent directories includes `*.js`, but don't load it. + assert(!engine.isPathIgnored("foo.js")); + assert(engine.isPathIgnored("node_modules/foo.js")); + }); + + it("initialization with ignorePath should work when cwd is a parent directory", () => { + const cwd = getFixturePath("ignored-paths"); + const ignorePath = getFixturePath("ignored-paths", "custom-name", "ignore-file"); + const engine = new CLIEngine({ ignorePath, cwd }); + + assert(engine.isPathIgnored("custom-name/foo.js")); + }); + + it("initialization with ignorePath should work when the file is in the cwd", () => { + const cwd = getFixturePath("ignored-paths", "custom-name"); + const ignorePath = getFixturePath("ignored-paths", "custom-name", "ignore-file"); + const engine = new CLIEngine({ ignorePath, cwd }); + + assert(engine.isPathIgnored("foo.js")); + }); + + it("initialization with ignorePath should work when cwd is a subdirectory", () => { + const cwd = getFixturePath("ignored-paths", "custom-name", "subdirectory"); + const ignorePath = getFixturePath("ignored-paths", "custom-name", "ignore-file"); + const engine = new CLIEngine({ ignorePath, cwd }); + + assert(engine.isPathIgnored("../custom-name/foo.js")); + }); + + it("initialization with invalid file should throw error", () => { + const cwd = getFixturePath("ignored-paths"); + const ignorePath = getFixturePath("ignored-paths", "not-a-directory", ".foobaz"); + + assert.throws(() => { + // eslint-disable-next-line no-new + new CLIEngine({ ignorePath, cwd }); + }, "Cannot read .eslintignore file"); + }); + + it("should return false for files outside of ignorePath's directory", () => { + const cwd = getFixturePath("ignored-paths"); + const ignorePath = getFixturePath("ignored-paths", "custom-name", "ignore-file"); + const engine = new CLIEngine({ ignorePath, cwd }); + + assert(!engine.isPathIgnored(getFixturePath("ignored-paths", "undef.js"))); + }); + + it("should resolve relative paths from the ignorePath, not cwd", () => { + const cwd = getFixturePath("ignored-paths", "subdir"); + const ignorePath = getFixturePath("ignored-paths", ".eslintignoreForDifferentCwd"); + const engine = new CLIEngine({ ignorePath, cwd }); + + assert(!engine.isPathIgnored(getFixturePath("ignored-paths", "subdir/undef.js"))); + assert(engine.isPathIgnored(getFixturePath("ignored-paths", "undef.js"))); + }); + + it("should resolve relative paths from the ignorePath when it's in a child directory", () => { + const cwd = getFixturePath("ignored-paths"); + const ignorePath = getFixturePath("ignored-paths", "subdir/.eslintignoreInChildDir"); + const engine = new CLIEngine({ ignorePath, cwd }); + + assert(engine.isPathIgnored(getFixturePath("ignored-paths", "subdir/undef.js"))); + assert(!engine.isPathIgnored(getFixturePath("ignored-paths", "undef.js"))); + assert(engine.isPathIgnored(getFixturePath("ignored-paths", "foo.js"))); + assert(engine.isPathIgnored(getFixturePath("ignored-paths", "subdir/foo.js"))); + + assert(engine.isPathIgnored(getFixturePath("ignored-paths", "node_modules/bar.js"))); + }); + + it("should resolve relative paths from the ignorePath when it contains negated globs", () => { + const cwd = getFixturePath("ignored-paths"); + const ignorePath = getFixturePath("ignored-paths", "subdir/.eslintignoreInChildDir"); + const engine = new CLIEngine({ ignorePath, cwd }); + + assert(engine.isPathIgnored("subdir/blah.txt")); + assert(engine.isPathIgnored("blah.txt")); + assert(!engine.isPathIgnored("subdir/bar.txt")); + assert(engine.isPathIgnored("bar.txt")); + assert(!engine.isPathIgnored("subdir/baz.txt")); + assert(!engine.isPathIgnored("baz.txt")); + }); + + it("should resolve default ignore patterns from the CWD even when the ignorePath is in a subdirectory", () => { + const cwd = getFixturePath("ignored-paths"); + const ignorePath = getFixturePath("ignored-paths", "subdir/.eslintignoreInChildDir"); + const engine = new CLIEngine({ ignorePath, cwd }); + + assert(engine.isPathIgnored("node_modules/blah.js")); + }); + + it("should resolve default ignore patterns from the CWD even when the ignorePath is in a parent directory", () => { + const cwd = getFixturePath("ignored-paths", "subdir"); + const ignorePath = getFixturePath("ignored-paths", ".eslintignoreForDifferentCwd"); + const engine = new CLIEngine({ ignorePath, cwd }); + + assert(engine.isPathIgnored("node_modules/blah.js")); + }); + + it("should handle .eslintignore which contains CRLF correctly.", () => { + const ignoreFileContent = fs.readFileSync(getFixturePath("ignored-paths", "crlf/.eslintignore"), "utf8"); + + assert(ignoreFileContent.includes("\r"), "crlf/.eslintignore should contains CR."); + + const cwd = getFixturePath("ignored-paths"); + const ignorePath = getFixturePath("ignored-paths", "crlf/.eslintignore"); + const engine = new CLIEngine({ ignorePath, cwd }); + + assert(engine.isPathIgnored(getFixturePath("ignored-paths", "crlf/hide1/a.js"))); + assert(engine.isPathIgnored(getFixturePath("ignored-paths", "crlf/hide2/a.js"))); + assert(!engine.isPathIgnored(getFixturePath("ignored-paths", "crlf/hide3/a.js"))); + }); + + it("should not include comments in ignore rules", () => { + const cwd = getFixturePath("ignored-paths"); + const ignorePath = getFixturePath("ignored-paths", ".eslintignoreWithComments"); + const engine = new CLIEngine({ ignorePath, cwd }); + + assert(!engine.isPathIgnored("# should be ignored")); + assert(engine.isPathIgnored("this_one_not")); + }); + + it("should ignore a non-negated pattern", () => { + const cwd = getFixturePath("ignored-paths"); + const ignorePath = getFixturePath("ignored-paths", ".eslintignoreWithNegation"); + const engine = new CLIEngine({ ignorePath, cwd }); + + assert(engine.isPathIgnored(getFixturePath("ignored-paths", "negation", "ignore.js"))); + }); + + it("should not ignore a negated pattern", () => { + const cwd = getFixturePath("ignored-paths"); + const ignorePath = getFixturePath("ignored-paths", ".eslintignoreWithNegation"); + const engine = new CLIEngine({ ignorePath, cwd }); + + assert(!engine.isPathIgnored(getFixturePath("ignored-paths", "negation", "unignore.js"))); + }); + }); + + describe("with --ignore-path option and --ignore-pattern option", () => { + it("should return false for ignored file when unignored with ignore pattern", () => { + const cwd = getFixturePath("ignored-paths"); + const engine = new CLIEngine({ + ignorePath: getFixturePath("ignored-paths", ".eslintignore"), + ignorePattern: "!sampleignorepattern", + cwd + }); + + assert(!engine.isPathIgnored(getFixturePath("ignored-paths", "sampleignorepattern"))); + }); + }); }); describe("getFormatter()", () => { diff --git a/tests/lib/cli-engine/ignored-paths.js b/tests/lib/cli-engine/ignored-paths.js deleted file mode 100644 index 4870244270a..00000000000 --- a/tests/lib/cli-engine/ignored-paths.js +++ /dev/null @@ -1,658 +0,0 @@ -/** - * @fileoverview Tests for IgnoredPaths object. - * @author Jonathan Rajavuori - */ - -"use strict"; - -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - -const assert = require("chai").assert, - path = require("path"), - os = require("os"), - { IgnoredPaths } = require("../../../lib/cli-engine/ignored-paths.js"), - sinon = require("sinon"), - fs = require("fs"), - includes = require("lodash").includes; - -require("shelljs/global"); - -/* global mkdir, rm, cp */ - -//------------------------------------------------------------------------------ -// Helper -//------------------------------------------------------------------------------ - -let fixtureDir; - -/** - * get raw rules from IgnorePaths instance - * @param {IgnoredPaths} ignoredPaths instance of IgnoredPaths - * @returns {string[]} raw ignore rules - */ -function getIgnoreRules(ignoredPaths) { - const ignoreRulesProperty = "_rules"; - let ignoreRules = []; - - Object.keys(ignoredPaths.ig).forEach(key => { - const rules = ignoredPaths.ig[key][ignoreRulesProperty]; - - rules.forEach(rule => { - const ruleOrigins = ignoreRules.map(ruleObj => ruleObj.origin); - - /* - * Don't include duplicate ignore rules. - * (Duplicates occur because we add custom ignore patterns to the - * defaults as well, to allow unignoring default ignores) - */ - if (!includes(ruleOrigins, rule.origin)) { - ignoreRules = ignoreRules.concat(rule); - } - }); - }); - - return ignoreRules; -} - -/** - * Get a list of paths of loaded ignore files (e.g. .eslintignore) from IgnorePaths instance - * @param {IgnoredPaths} ignoredPaths Instance of IgnoredPaths - * @returns {string[]} loaded ignore files - */ -function getIgnoreFiles(ignoredPaths) { - return ignoredPaths.ig.custom.ignoreFiles; -} - -/** - * Get a list of ignore patterns that are loaded - * @param {Object[]} ignoredPaths Instance of IgnoredPaths - * @returns {string[]} Ignore patterns - */ -function getIgnorePatterns(ignoredPaths) { - const ignoreRules = getIgnoreRules(ignoredPaths); - - return ignoreRules.map(rule => rule.origin); -} - -/** - * count the number of default patterns applied to IgnoredPaths instance - * @param {IgnoredPaths} ignoredPaths instance of IgnoredPaths - * @returns {integer} count of default patterns - */ -function countDefaultPatterns(ignoredPaths) { - let count = ignoredPaths.defaultPatterns.length; - - if (!ignoredPaths.options || (ignoredPaths.options.dotfiles !== true)) { - count += 2; // Two patterns for ignoring dotfiles - } - return count; -} - -/** - * Returns the path inside of the fixture directory. - * @returns {string} The path inside the fixture directory. - * @private - */ -function getFixturePath(...args) { - return path.join(fs.realpathSync(fixtureDir), ...args); -} - -//------------------------------------------------------------------------------ -// Tests -//------------------------------------------------------------------------------ - -describe("IgnoredPaths", () => { - - // copy into clean area so as not to get "infected" by this project's .eslintrc files - before(() => { - fixtureDir = path.join(os.tmpdir(), "/eslint/fixtures/ignored-paths/"); - mkdir("-p", fixtureDir); - cp("-r", "./tests/fixtures/ignored-paths/.", fixtureDir); - }); - - after(() => { - rm("-r", fixtureDir); - }); - - describe("initialization", () => { - - it("should load .eslintignore from cwd when explicitly passed", () => { - const expectedIgnoreFile = getFixturePath(".eslintignore"); - const ignoredPaths = new IgnoredPaths({ ignore: true, cwd: getFixturePath() }); - const ignorePatterns = getIgnorePatterns(ignoredPaths); - - assert.isNotNull(ignoredPaths.ignoreFileDir); - assert.deepStrictEqual(getIgnoreFiles(ignoredPaths), [expectedIgnoreFile]); - assert.include(ignorePatterns, "sampleignorepattern"); - }); - - it("should set baseDir to cwd when no ignore file was loaded", () => { - const ignoredPaths = new IgnoredPaths({ cwd: getFixturePath("no-ignore-file") }); - - assert.strictEqual(ignoredPaths.ignoreFileDir, getFixturePath("no-ignore-file")); - }); - - it("should not travel to parent directories to find .eslintignore when it's missing and cwd is provided", () => { - const ignoredPaths = new IgnoredPaths({ ignore: true, cwd: getFixturePath("configurations") }); - - assert.lengthOf(getIgnoreRules(ignoredPaths), 4); - assert.lengthOf(getIgnoreFiles(ignoredPaths), 0); - }); - - it("should load empty array with ignorePath set to false", () => { - const ignoredPaths = new IgnoredPaths({ ignore: true, ignorePath: false, cwd: getFixturePath("no-ignore-file") }); - - assert.isArray(getIgnoreRules(ignoredPaths)); - assert.lengthOf(getIgnoreRules(ignoredPaths), countDefaultPatterns(ignoredPaths)); - }); - - it("should accept an array for options.ignorePattern", () => { - const ignorePattern = ["a", "b"]; - - const ignoredPaths = new IgnoredPaths({ - ignorePattern - }); - - assert.ok( - ignorePattern.every(pattern => ( - getIgnoreRules(ignoredPaths).some(rule => rule.pattern === pattern) - )) - ); - }); - - it("should use package.json's eslintIgnore files if no specified .eslintignore file", () => { - const ignoredPaths = new IgnoredPaths({ ignore: true, cwd: getFixturePath("package-json-ignore") }); - - assert.isTrue(ignoredPaths.contains("hello.js")); - assert.isTrue(ignoredPaths.contains("world.js")); - }); - - it("should use correct message template if failed to parse package.json", () => { - assert.throw(() => { - try { - // eslint-disable-next-line no-new - new IgnoredPaths({ ignore: true, cwd: getFixturePath("broken-package-json") }); - } catch (error) { - assert.strictEqual(error.messageTemplate, "failed-to-read-json"); - throw error; - } - }); - }); - - it("should not use package.json's eslintIgnore files if specified .eslintignore file", () => { - const ignoredPaths = new IgnoredPaths({ ignore: true, cwd: getFixturePath() }); - - assert.isFalse(ignoredPaths.contains("hello.js")); - assert.isFalse(ignoredPaths.contains("world.js")); - assert.isTrue(ignoredPaths.contains("sampleignorepattern")); - }); - - it("should error if package.json's eslintIgnore is not an array of file paths", () => { - assert.throws(() => { - const ignoredPaths = new IgnoredPaths({ ignore: true, cwd: getFixturePath("bad-package-json-ignore") }); - - assert.ok(ignoredPaths); - }, "Package.json eslintIgnore property requires an array of paths"); - }); - }); - - describe("caching file reads", () => { - - let readFileSyncCount; - - before(() => { - readFileSyncCount = sinon.spy(fs, "readFileSync"); - }); - - after(() => { - readFileSyncCount.restore(); - }); - - it("should cache readFileSync on same file paths", () => { - const ignoreFilePath = getFixturePath(".eslintignore"); - const ignoredPaths = new IgnoredPaths({ ignore: true, cwd: getFixturePath() }); - - ignoredPaths.readIgnoreFile(ignoreFilePath); - assert.isTrue(ignoredPaths.contains(ignoreFilePath)); - sinon.assert.calledOnce(readFileSyncCount); - }); - }); - - describe("initialization with ignorePattern", () => { - - it("should ignore a normal pattern", () => { - const ignoredPaths = new IgnoredPaths({ ignore: true, ignorePattern: "ignore-me.txt", cwd: getFixturePath("ignore-pattern") }); - - assert.isTrue(ignoredPaths.contains(getFixturePath("ignore-pattern", "ignore-me.txt"))); - }); - - }); - - describe("initialization with ignorePath", () => { - - let ignoreFilePath; - - before(() => { - ignoreFilePath = getFixturePath(".eslintignore"); - }); - - it("should set baseDir to directory containing ignorePath if provided", () => { - const ignoredPaths = new IgnoredPaths({ ignore: true, ignorePath: ignoreFilePath, cwd: getFixturePath() }); - - assert.strictEqual(ignoredPaths.ignoreFileDir, path.dirname(ignoreFilePath)); - }); - - it("should set the common ancestor directory of cwd and ignorePath to baseDir (in the case that 'ignoreFilePath' and 'cwd' are siblings)", () => { - const baseDir = path.dirname(ignoreFilePath); - const ignoredPaths = new IgnoredPaths({ ignore: true, ignorePath: ignoreFilePath, cwd: path.resolve(baseDir, "testcwd") }); - - assert.strictEqual(ignoredPaths.getBaseDir(), baseDir); - }); - - it("should set the common ancestor directory of cwd and ignorePath to baseDir", () => { - const baseDir = path.resolve(ignoreFilePath, "../../.."); - const ignoredPaths = new IgnoredPaths({ ignore: true, ignorePath: ignoreFilePath, cwd: path.resolve(baseDir, "fix/testcwd") }); - - assert.strictEqual(ignoredPaths.getBaseDir(), baseDir); - }); - - }); - - describe("initialization with ignorePath file not named .eslintignore", () => { - - let ignoreFilePath; - - before(() => { - ignoreFilePath = getFixturePath("custom-name", "ignore-file"); - }); - - it("should work when cwd is a parent directory", () => { - const ignoredPaths = new IgnoredPaths({ ignore: true, ignorePath: ignoreFilePath, cwd: getFixturePath() }); - - assert.notStrictEqual(getIgnoreRules(ignoredPaths).length, countDefaultPatterns(ignoredPaths)); - }); - - it("should work when the file is in the cwd", () => { - const ignoredPaths = new IgnoredPaths({ ignore: true, ignorePath: ignoreFilePath, cwd: getFixturePath("custom-name") }); - - assert.notStrictEqual(getIgnoreRules(ignoredPaths).length, countDefaultPatterns(ignoredPaths)); - }); - - it("should work when cwd is a subdirectory", () => { - const ignoredPaths = new IgnoredPaths({ ignore: true, ignorePath: ignoreFilePath, cwd: getFixturePath("custom-name", "subdirectory") }); - - assert.notStrictEqual(getIgnoreRules(ignoredPaths).length, countDefaultPatterns(ignoredPaths)); - }); - - }); - - describe("initialization without ignorePath", () => { - - it("should not load an ignore file if none is in cwd", () => { - const ignoredPaths = new IgnoredPaths({ ignore: true, cwd: getFixturePath("no-ignore-file") }); - - assert.lengthOf(getIgnoreFiles(ignoredPaths), 0); - assert.lengthOf(getIgnoreRules(ignoredPaths), countDefaultPatterns(ignoredPaths)); - }); - - }); - - describe("initialization with invalid file", () => { - - let invalidFilepath; - - before(() => { - invalidFilepath = getFixturePath("not-a-directory", ".foobaz"); - }); - - it("should throw error", () => { - assert.throws(() => { - const ignoredPaths = new IgnoredPaths({ ignore: true, ignorePath: invalidFilepath, cwd: getFixturePath() }); - - assert.ok(ignoredPaths); - }, "Cannot read ignore file"); - }); - - }); - - describe("contains", () => { - - it("should throw if initialized with invalid options", () => { - const ignoredPaths = new IgnoredPaths(null); - - assert.throw(ignoredPaths.contains, Error); - }); - - it("should not throw if given a relative filename", () => { - const ignoredPaths = new IgnoredPaths({ ignore: true, ignorePattern: "undef.js", cwd: getFixturePath() }); - - ignoredPaths.contains("undef.js"); - }); - - it("should return true for files which match an ignorePattern even if they do not exist on the filesystem", () => { - const ignoredPaths = new IgnoredPaths({ ignore: true, ignorePattern: "not-a-file", cwd: getFixturePath() }); - - assert.isTrue(ignoredPaths.contains(getFixturePath("not-a-file"))); - }); - - it("should return false for files outside of the cwd (with no ignore file provided)", () => { - - // Default ignore patterns should not inadvertantly ignore files in parent directories - const ignoredPaths = new IgnoredPaths({ ignore: true, cwd: getFixturePath("no-ignore-file") }); - - assert.isFalse(ignoredPaths.contains(getFixturePath("undef.js"))); - }); - - it("should return false for files outside of ignorePath's directory", () => { - const ignoredPaths = new IgnoredPaths({ ignore: true, ignorePath: getFixturePath("custom-name", "ignore-file"), cwd: getFixturePath() }); - - assert.isFalse(ignoredPaths.contains(getFixturePath("undef.js"))); - }); - - it("should return true for file matching an ignore pattern exactly", () => { - const ignoredPaths = new IgnoredPaths({ ignore: true, ignorePattern: "undef.js", cwd: getFixturePath() }); - - assert.isTrue(ignoredPaths.contains(getFixturePath("undef.js"))); - }); - - it("should return false for file matching an invalid ignore pattern with leading './'", () => { - const ignoredPaths = new IgnoredPaths({ ignore: true, ignorePattern: "./undef.js", cwd: getFixturePath() }); - - assert.isFalse(ignoredPaths.contains(getFixturePath("undef.js"))); - }); - - it("should return false for file in subfolder of cwd matching an ignore pattern with leading '/'", () => { - const ignoredPaths = new IgnoredPaths({ ignore: true, ignorePattern: "/undef.js", cwd: getFixturePath() }); - - assert.isFalse(ignoredPaths.contains(getFixturePath("subdir", "undef.js"))); - }); - - it("should return true for file matching a child of an ignore pattern", () => { - const ignoredPaths = new IgnoredPaths({ ignore: true, ignorePattern: "ignore-pattern", cwd: getFixturePath() }); - - assert.isTrue(ignoredPaths.contains(getFixturePath("ignore-pattern", "ignore-me.txt"))); - }); - - it("should return true for file matching a grandchild of an ignore pattern", () => { - const ignoredPaths = new IgnoredPaths({ ignore: true, ignorePattern: "ignore-pattern", cwd: getFixturePath() }); - - assert.isTrue(ignoredPaths.contains(getFixturePath("ignore-pattern", "subdir", "ignore-me.txt"))); - }); - - it("should return true for file matching a child of an ignore pattern with windows line termination", () => { - sinon.stub(fs, "readFileSync") - .withArgs(".eslintignore") - .returns("subdir\r\n"); - sinon.stub(fs, "statSync") - .withArgs(".eslintignore") - .returns({ - isFile() { - return true; - } - }); - - try { - const ignoredPaths = new IgnoredPaths({ ignore: true, ignorePath: ".eslintignore", cwd: getFixturePath() }); - - assert.isTrue(ignoredPaths.contains(getFixturePath("subdir/undef.js"))); - } finally { - fs.readFileSync.restore(); - fs.statSync.restore(); - } - }); - - it("should return false for file not matching any ignore pattern", () => { - const ignoredPaths = new IgnoredPaths({ ignore: true, ignorePattern: "failing.js", cwd: getFixturePath() }); - - assert.isFalse(ignoredPaths.contains(getFixturePath("unignored.js"))); - }); - - it("should return false for ignored file when unignored with ignore pattern", () => { - const ignoredPaths = new IgnoredPaths({ ignore: true, ignorePath: getFixturePath(".eslintignore"), ignorePattern: "!sampleignorepattern", cwd: getFixturePath() }); - - assert.isFalse(ignoredPaths.contains(getFixturePath("sampleignorepattern"))); - - }); - - it("should resolve relative paths from the ignorePath, not cwd", () => { - const ignoredPaths = new IgnoredPaths({ ignore: true, ignorePath: getFixturePath(".eslintignoreForDifferentCwd"), cwd: getFixturePath("subdir") }); - - assert.isFalse(ignoredPaths.contains(getFixturePath("subdir/undef.js"))); - assert.isTrue(ignoredPaths.contains(getFixturePath("undef.js"))); - }); - - it("should resolve relative paths from the ignorePath when it's in a child directory", () => { - const ignoredPaths = new IgnoredPaths({ ignore: true, ignorePath: getFixturePath("subdir/.eslintignoreInChildDir"), cwd: getFixturePath() }); - - assert.isTrue(ignoredPaths.contains(getFixturePath("subdir/undef.js"))); - assert.isFalse(ignoredPaths.contains(getFixturePath("undef.js"))); - assert.isTrue(ignoredPaths.contains(getFixturePath("foo.js"))); - assert.isTrue(ignoredPaths.contains(getFixturePath("subdir/foo.js"))); - - assert.isTrue(ignoredPaths.contains(getFixturePath("node_modules/bar.js"))); - }); - - it("should resolve relative paths from the ignorePath when it contains negated globs", () => { - const ignoredPaths = new IgnoredPaths({ ignore: true, ignorePath: getFixturePath("subdir/.eslintignoreInChildDir"), cwd: getFixturePath() }); - - assert.isTrue(ignoredPaths.contains("subdir/blah.txt")); - assert.isTrue(ignoredPaths.contains("blah.txt")); - assert.isFalse(ignoredPaths.contains("subdir/bar.txt")); - assert.isTrue(ignoredPaths.contains("bar.txt")); - assert.isFalse(ignoredPaths.contains("subdir/baz.txt")); - assert.isFalse(ignoredPaths.contains("baz.txt")); - }); - - it("should resolve default ignore patterns from the CWD even when the ignorePath is in a subdirectory", () => { - const ignoredPaths = new IgnoredPaths({ ignore: true, ignorePath: getFixturePath("subdir/.eslintignoreInChildDir"), cwd: getFixturePath() }); - - assert.isTrue(ignoredPaths.contains("node_modules/blah.js")); - }); - - it("should resolve default ignore patterns from the CWD even when the ignorePath is in a parent directory", () => { - const ignoredPaths = new IgnoredPaths({ ignore: true, ignorePath: getFixturePath(".eslintignoreForDifferentCwd"), cwd: getFixturePath("subdir") }); - - assert.isTrue(ignoredPaths.contains("node_modules/blah.js")); - }); - - it("should handle .eslintignore which contains CRLF correctly.", () => { - const ignoreFileContent = fs.readFileSync(getFixturePath("crlf/.eslintignore"), "utf8"); - - assert.isTrue(ignoreFileContent.includes("\r"), "crlf/.eslintignore should contains CR."); - - const ignoredPaths = new IgnoredPaths({ - ignore: true, - ignorePath: getFixturePath("crlf/.eslintignore"), - cwd: getFixturePath() - }); - - assert.isTrue(ignoredPaths.contains(getFixturePath("crlf/hide1/a.js"))); - assert.isTrue(ignoredPaths.contains(getFixturePath("crlf/hide2/a.js"))); - assert.isFalse(ignoredPaths.contains(getFixturePath("crlf/hide3/a.js"))); - }); - }); - - describe("initialization with ignorePath containing commented lines", () => { - - let ignoreFilePath; - - before(() => { - ignoreFilePath = getFixturePath(".eslintignoreWithComments"); - }); - - it("should not include comments in ignore rules", () => { - const ignoredPaths = new IgnoredPaths({ ignore: true, ignorePath: ignoreFilePath, cwd: getFixturePath() }); - const ignorePatterns = getIgnorePatterns(ignoredPaths); - - assert.strictEqual(getIgnoreRules(ignoredPaths).length, countDefaultPatterns(ignoredPaths) + 1); - assert.include(ignorePatterns, "this_one_not"); - }); - - }); - - describe("initialization with ignorePath containing negations", () => { - let ignoreFilePath; - - before(() => { - ignoreFilePath = getFixturePath(".eslintignoreWithNegation"); - }); - - it("should ignore a non-negated pattern", () => { - const ignoredPaths = new IgnoredPaths({ ignore: true, ignorePath: ignoreFilePath, cwd: getFixturePath() }); - - assert.isTrue(ignoredPaths.contains(getFixturePath("negation", "ignore.js"))); - }); - - it("should not ignore a negated pattern", () => { - const ignoredPaths = new IgnoredPaths({ ignore: true, ignorePath: ignoreFilePath, cwd: getFixturePath() }); - - assert.isFalse(ignoredPaths.contains(getFixturePath("negation", "unignore.js"))); - }); - - }); - - describe("default ignores", () => { - - it("should contain /bower_components/*", () => { - const ignoredPaths = new IgnoredPaths(); - - assert.include(ignoredPaths.defaultPatterns, "/bower_components/*"); - }); - - it("should contain /node_modules/*", () => { - const ignoredPaths = new IgnoredPaths(); - - assert.include(ignoredPaths.defaultPatterns, "/node_modules/*"); - }); - - it("should always apply defaultPatterns if ignore option is true", () => { - const ignoredPaths = new IgnoredPaths({ ignore: true, cwd: getFixturePath() }); - - assert.isTrue(ignoredPaths.contains(getFixturePath("bower_components/package/file.js"))); - assert.isTrue(ignoredPaths.contains(getFixturePath("node_modules/package/file.js"))); - }); - - it("should still apply defaultPatterns if ignore option is is false", () => { - const ignoredPaths = new IgnoredPaths({ ignore: false, cwd: getFixturePath() }); - - assert.isTrue(ignoredPaths.contains(getFixturePath("bower_components/package/file.js"))); - assert.isTrue(ignoredPaths.contains(getFixturePath("node_modules/package/file.js"))); - }); - - it("should not ignore files in defaultPatterns within a subdirectory", () => { - const ignoredPaths = new IgnoredPaths({ ignore: true, cwd: getFixturePath() }); - - assert.isFalse(ignoredPaths.contains(getFixturePath("subdir/bower_components/package/file.js"))); - assert.isFalse(ignoredPaths.contains(getFixturePath("subdir/node_modules/package/file.js"))); - }); - - it("should allow subfolders of defaultPatterns to be unignored by ignorePattern", () => { - const ignoredPaths = new IgnoredPaths({ ignore: true, cwd: getFixturePath(), ignorePattern: "!/node_modules/package" }); - - assert.isFalse(ignoredPaths.contains(getFixturePath("node_modules", "package", "file.js"))); - }); - - it("should allow subfolders of defaultPatterns to be unignored by ignorePath", () => { - const ignoredPaths = new IgnoredPaths({ ignore: true, cwd: getFixturePath(), ignorePath: getFixturePath(".eslintignoreWithUnignoredDefaults") }); - - assert.isFalse(ignoredPaths.contains(getFixturePath("node_modules", "package", "file.js"))); - }); - - it("should ignore dotfiles", () => { - const ignoredPaths = new IgnoredPaths({ ignore: true, cwd: getFixturePath() }); - - assert.isTrue(ignoredPaths.contains(getFixturePath(".foo"))); - assert.isTrue(ignoredPaths.contains(getFixturePath("foo/.bar"))); - }); - - it("should ignore directories beginning with a dot", () => { - const ignoredPaths = new IgnoredPaths({ ignore: true, cwd: getFixturePath() }); - - assert.isTrue(ignoredPaths.contains(getFixturePath(".foo/bar"))); - assert.isTrue(ignoredPaths.contains(getFixturePath("foo/.bar/baz"))); - }); - - it("should still ignore dotfiles when ignore option disabled", () => { - const ignoredPaths = new IgnoredPaths({ ignore: false, cwd: getFixturePath() }); - - assert.isTrue(ignoredPaths.contains(getFixturePath(".foo"))); - assert.isTrue(ignoredPaths.contains(getFixturePath("foo/.bar"))); - }); - - it("should still ignore directories beginning with a dot when ignore option disabled", () => { - const ignoredPaths = new IgnoredPaths({ ignore: false, cwd: getFixturePath() }); - - assert.isTrue(ignoredPaths.contains(getFixturePath(".foo/bar"))); - assert.isTrue(ignoredPaths.contains(getFixturePath("foo/.bar/baz"))); - }); - - it("should not ignore absolute paths containing '..'", () => { - const ignoredPaths = new IgnoredPaths({ ignore: true, cwd: getFixturePath() }); - - assert.isFalse(ignoredPaths.contains(`${getFixturePath("foo")}/../unignored.js`)); - }); - - it("should ignore /node_modules/ at top level relative to .eslintignore when loaded", () => { - const ignoredPaths = new IgnoredPaths({ ignore: true, ignorePath: getFixturePath(".eslintignore"), cwd: getFixturePath() }); - - assert.isTrue(ignoredPaths.contains(getFixturePath("node_modules", "existing.js"))); - assert.isFalse(ignoredPaths.contains(getFixturePath("foo", "node_modules", "existing.js"))); - }); - - it("should ignore /node_modules/ at top level relative to cwd without an .eslintignore", () => { - const ignoredPaths = new IgnoredPaths({ ignore: true, cwd: getFixturePath("no-ignore-file") }); - - assert.isTrue(ignoredPaths.contains(getFixturePath("no-ignore-file", "node_modules", "existing.js"))); - assert.isFalse(ignoredPaths.contains(getFixturePath("no-ignore-file", "foo", "node_modules", "existing.js"))); - }); - - }); - - describe("two globstar '**' ignore pattern", () => { - - it("should ignore files in nested directories", () => { - const ignoredPaths = new IgnoredPaths({ ignore: true, ignorePattern: "**/*.js", cwd: getFixturePath() }); - - assert.isTrue(ignoredPaths instanceof IgnoredPaths); - assert.isTrue(ignoredPaths.contains(getFixturePath("foo.js"))); - assert.isTrue(ignoredPaths.contains(getFixturePath("foo/bar.js"))); - assert.isTrue(ignoredPaths.contains(getFixturePath("foo/bar/baz.js"))); - assert.isFalse(ignoredPaths.contains(getFixturePath("foo.j2"))); - assert.isFalse(ignoredPaths.contains(getFixturePath("foo/bar.j2"))); - assert.isFalse(ignoredPaths.contains(getFixturePath("foo/bar/baz.j2"))); - }); - }); - - describe("dotfiles option", () => { - - it("should add at least one pattern when false", () => { - const ignoredPaths = new IgnoredPaths({ ignore: true, dotfiles: false, cwd: getFixturePath("no-ignore-file") }); - - assert(getIgnoreRules(ignoredPaths).length > ignoredPaths.defaultPatterns.length); - }); - - it("should add no patterns when true", () => { - const ignoredPaths = new IgnoredPaths({ ignore: true, dotfiles: true, cwd: getFixturePath("no-ignore-file") }); - - assert.lengthOf(getIgnoreRules(ignoredPaths), ignoredPaths.defaultPatterns.length); - }); - - it("should not ignore dotfiles when true", () => { - const ignoredPaths = new IgnoredPaths({ ignore: true, dotfiles: true, cwd: getFixturePath() }); - - assert.isFalse(ignoredPaths.contains(getFixturePath(".foo"))); - assert.isFalse(ignoredPaths.contains(getFixturePath("foo/.bar"))); - }); - - it("should not ignore directories beginning with a dot when true", () => { - const ignoredPaths = new IgnoredPaths({ ignore: true, dotfiles: true, cwd: getFixturePath() }); - - assert.isFalse(ignoredPaths.contains(getFixturePath(".foo/bar"))); - assert.isFalse(ignoredPaths.contains(getFixturePath("foo/.bar/baz"))); - }); - - }); - -}); From 1012ef4bcf8f9ede787e75ad415fed0999e86814 Mon Sep 17 00:00:00 2001 From: Toru Nagashima Date: Tue, 17 Sep 2019 10:16:03 +0900 Subject: [PATCH 03/13] add more tests --- tests/lib/cli-engine/cli-engine.js | 565 +++++++++++++++++++++++++++++ 1 file changed, 565 insertions(+) diff --git a/tests/lib/cli-engine/cli-engine.js b/tests/lib/cli-engine/cli-engine.js index 95f0ef079f2..0e85bbcf33e 100644 --- a/tests/lib/cli-engine/cli-engine.js +++ b/tests/lib/cli-engine/cli-engine.js @@ -4691,4 +4691,569 @@ describe("CLIEngine", () => { }); }); }); + + describe.only("with ignorePatterns config", () => { + const root = getFixturePath("cli-engine/ignore-patterns"); + + describe("ignorePatterns can add an ignore pattern ('foo.js').", () => { + const InMemoryCLIEngine = defineCLIEngineWithInMemoryFileSystem({ + cwd: () => root, + files: { + ".eslintrc.json": JSON.stringify({ + ignorePatterns: "foo.js" + }), + "foo.js": "", + "bar.js": "", + "subdir/foo.js": "", + "subdir/bar.js": "" + } + }).CLIEngine; + + it("'isPathIgnored()' should return 'true' for 'foo.js'.", () => { + const engine = new InMemoryCLIEngine(); + + assert.strictEqual(engine.isPathIgnored("foo.js"), true); + assert.strictEqual(engine.isPathIgnored("subdir/foo.js"), true); + }); + + it("'isPathIgnored()' should return 'false' for 'bar.js'.", () => { + const engine = new InMemoryCLIEngine(); + + assert.strictEqual(engine.isPathIgnored("bar.js"), false); + assert.strictEqual(engine.isPathIgnored("subdir/bar.js"), false); + }); + + it("'executeOnFiles()' should not verify 'foo.js'.", () => { + const engine = new InMemoryCLIEngine(); + const filePaths = engine.executeOnFiles("**/*.js").results.map(r => r.filePath); + + assert.deepStrictEqual(filePaths, [ + path.join(root, "bar.js"), + path.join(root, "subdir/bar.js") + ]); + }); + }); + + describe("ignorePatterns can add ignore patterns ('foo.js', '/bar.js').", () => { + const InMemoryCLIEngine = defineCLIEngineWithInMemoryFileSystem({ + cwd: () => root, + files: { + ".eslintrc.json": JSON.stringify({ + ignorePatterns: ["foo.js", "/bar.js"] + }), + "foo.js": "", + "bar.js": "", + "baz.js": "", + "subdir/foo.js": "", + "subdir/bar.js": "", + "subdir/baz.js": "" + } + }).CLIEngine; + + it("'isPathIgnored()' should return 'true' for 'foo.js'.", () => { + const engine = new InMemoryCLIEngine(); + + assert.strictEqual(engine.isPathIgnored("foo.js"), true); + assert.strictEqual(engine.isPathIgnored("subdir/foo.js"), true); + }); + + it("'isPathIgnored()' should return 'true' for '/bar.js'.", () => { + const engine = new InMemoryCLIEngine(); + + assert.strictEqual(engine.isPathIgnored("bar.js"), true); + assert.strictEqual(engine.isPathIgnored("subdir/bar.js"), false); + }); + + it("'executeOnFiles()' should not verify 'foo.js' and '/bar.js'.", () => { + const engine = new InMemoryCLIEngine(); + const filePaths = engine.executeOnFiles("**/*.js").results.map(r => r.filePath); + + assert.deepStrictEqual(filePaths, [ + path.join(root, "baz.js"), + path.join(root, "subdir/bar.js"), + path.join(root, "subdir/baz.js") + ]); + }); + }); + + describe("ignorePatterns can unignore '/node_modules/foo'.", () => { + const InMemoryCLIEngine = defineCLIEngineWithInMemoryFileSystem({ + cwd: () => root, + files: { + ".eslintrc.json": JSON.stringify({ + ignorePatterns: "!/node_modules/foo" + }), + "node_modules/foo/index.js": "", + "node_modules/foo/.dot.js": "", + "node_modules/bar/index.js": "", + "foo.js": "" + } + }).CLIEngine; + + it("'isPathIgnored()' should return 'false' for 'node_modules/foo/index.js'.", () => { + const engine = new InMemoryCLIEngine(); + + assert.strictEqual(engine.isPathIgnored("node_modules/foo/index.js"), false); + }); + + it("'isPathIgnored()' should return 'true' for 'node_modules/foo/.dot.js'.", () => { + const engine = new InMemoryCLIEngine(); + + assert.strictEqual(engine.isPathIgnored("node_modules/foo/.dot.js"), true); + }); + + it("'isPathIgnored()' should return 'true' for 'node_modules/bar/index.js'.", () => { + const engine = new InMemoryCLIEngine(); + + assert.strictEqual(engine.isPathIgnored("node_modules/bar/index.js"), true); + }); + + it("'executeOnFiles()' should verify 'node_modules/foo/index.js'.", () => { + const engine = new InMemoryCLIEngine(); + const filePaths = engine.executeOnFiles("**/*.js").results.map(r => r.filePath); + + assert.deepStrictEqual(filePaths, [ + path.join(root, "node_modules/foo/index.js"), + path.join(root, "foo.js") + ]); + }); + }); + + describe("ignorePatterns can unignore '.eslintrc.js'.", () => { + const InMemoryCLIEngine = defineCLIEngineWithInMemoryFileSystem({ + cwd: () => root, + files: { + ".eslintrc.js": `module.exports = ${JSON.stringify({ + ignorePatterns: "!.eslintrc.js" + })}`, + "foo.js": "" + } + }).CLIEngine; + + it("'isPathIgnored()' should return 'false' for '.eslintrc.js'.", () => { + const engine = new InMemoryCLIEngine(); + + assert.strictEqual(engine.isPathIgnored(".eslintrc.js"), false); + }); + + it("'executeOnFiles()' should verify '.eslintrc.js'.", () => { + const engine = new InMemoryCLIEngine(); + const filePaths = engine.executeOnFiles("**/*.js").results.map(r => r.filePath); + + assert.deepStrictEqual(filePaths, [ + path.join(root, ".eslintrc.js"), + path.join(root, "foo.js") + ]); + }); + }); + + describe(".eslintignore can re-ignore files that are unignored by ignorePatterns.", () => { + const InMemoryCLIEngine = defineCLIEngineWithInMemoryFileSystem({ + cwd: () => root, + files: { + ".eslintrc.js": `module.exports = ${JSON.stringify({ + ignorePatterns: "!.*" + })}`, + ".eslintignore": ".foo*", + ".foo.js": "", + ".bar.js": "" + } + }).CLIEngine; + + it("'isPathIgnored()' should return 'true' for re-ignored '.foo.js'.", () => { + const engine = new InMemoryCLIEngine(); + + assert.strictEqual(engine.isPathIgnored(".foo.js"), true); + }); + + it("'isPathIgnored()' should return 'false' for unignored '.bar.js'.", () => { + const engine = new InMemoryCLIEngine(); + + assert.strictEqual(engine.isPathIgnored(".bar.js"), false); + }); + + it("'executeOnFiles()' should not verify re-ignored '.foo.js'.", () => { + const engine = new InMemoryCLIEngine(); + const filePaths = engine.executeOnFiles("**/*.js").results.map(r => r.filePath); + + assert.deepStrictEqual(filePaths, [ + path.join(root, ".eslintrc.js"), + path.join(root, ".bar.js") + ]); + }); + }); + + describe(".eslintignore can unignore files that are ignored by ignorePatterns.", () => { + const InMemoryCLIEngine = defineCLIEngineWithInMemoryFileSystem({ + cwd: () => root, + files: { + ".eslintrc.js": `module.exports = ${JSON.stringify({ + ignorePatterns: "*.js" + })}`, + ".eslintignore": "!foo.js", + "foo.js": "", + "bar.js": "" + } + }).CLIEngine; + + it("'isPathIgnored()' should return 'false' for unignored 'foo.js'.", () => { + const engine = new InMemoryCLIEngine(); + + assert.strictEqual(engine.isPathIgnored("foo.js"), false); + }); + + it("'isPathIgnored()' should return 'true' for ignored 'bar.js'.", () => { + const engine = new InMemoryCLIEngine(); + + assert.strictEqual(engine.isPathIgnored("bar.js"), true); + }); + + it("'executeOnFiles()' should verify unignored 'foo.js'.", () => { + const engine = new InMemoryCLIEngine(); + const filePaths = engine.executeOnFiles("**/*.js").results.map(r => r.filePath); + + assert.deepStrictEqual(filePaths, [ + path.join(root, "foo.js") + ]); + }); + }); + + describe("ignorePatterns in the config file in a child directory affects to only in the directory.", () => { + const InMemoryCLIEngine = defineCLIEngineWithInMemoryFileSystem({ + cwd: () => root, + files: { + ".eslintrc.js": `module.exports = ${JSON.stringify({ + ignorePatterns: "foo.js" + })}`, + "subdir/.eslintrc.js": `module.exports = ${JSON.stringify({ + ignorePatterns: "bar.js" + })}`, + "foo.js": "", + "bar.js": "", + "subdir/foo.js": "", + "subdir/bar.js": "", + "subdir/subsubdir/foo.js": "", + "subdir/subsubdir/bar.js": "" + } + }).CLIEngine; + + it("'isPathIgnored()' should return 'true' for 'foo.js'.", () => { + const engine = new InMemoryCLIEngine(); + + assert.strictEqual(engine.isPathIgnored("foo.js"), true); + assert.strictEqual(engine.isPathIgnored("subdir/foo.js"), true); + assert.strictEqual(engine.isPathIgnored("subdir/subsubdir/foo.js"), true); + }); + + it("'isPathIgnored()' should return 'true' for 'bar.js' in 'subdir'.", () => { + const engine = new InMemoryCLIEngine(); + + assert.strictEqual(engine.isPathIgnored("subdir/bar.js"), true); + assert.strictEqual(engine.isPathIgnored("subdir/subsubdir/bar.js"), true); + }); + + it("'isPathIgnored()' should return 'false' for 'bar.js' in the outside of 'subdir'.", () => { + const engine = new InMemoryCLIEngine(); + + assert.strictEqual(engine.isPathIgnored("bar.js"), false); + }); + + it("'executeOnFiles()' should verify 'bar.js' in the outside of 'subdir'.", () => { + const engine = new InMemoryCLIEngine(); + const filePaths = engine.executeOnFiles("**/*.js").results.map(r => r.filePath); + + assert.deepStrictEqual(filePaths, [ + path.join(root, "bar.js") + ]); + }); + }); + + describe("ignorePatterns in the config file in a child directory can unignore the ignored files in the parent directory's config.", () => { + const InMemoryCLIEngine = defineCLIEngineWithInMemoryFileSystem({ + cwd: () => root, + files: { + ".eslintrc.js": `module.exports = ${JSON.stringify({ + ignorePatterns: "foo.js" + })}`, + "subdir/.eslintrc.js": `module.exports = ${JSON.stringify({ + ignorePatterns: "!foo.js" + })}`, + "foo.js": "", + "subdir/foo.js": "" + } + }).CLIEngine; + + it("'isPathIgnored()' should return 'true' for 'foo.js' in the root directory.", () => { + const engine = new InMemoryCLIEngine(); + + assert.strictEqual(engine.isPathIgnored("foo.js"), true); + }); + + it("'isPathIgnored()' should return 'false' for 'foo.js' in the child directory.", () => { + const engine = new InMemoryCLIEngine(); + + assert.strictEqual(engine.isPathIgnored("subdir/foo.js"), false); + }); + + it("'executeOnFiles()' should verify 'foo.js' in the child directory.", () => { + const engine = new InMemoryCLIEngine(); + const filePaths = engine.executeOnFiles("**/*.js").results.map(r => r.filePath); + + assert.deepStrictEqual(filePaths, [ + path.join(root, "subdir/foo.js") + ]); + }); + }); + + describe(".eslintignore can unignore files that are ignored by ignorePatterns in the config file in the child directory.", () => { + const InMemoryCLIEngine = defineCLIEngineWithInMemoryFileSystem({ + cwd: () => root, + files: { + ".eslintrc.json": JSON.stringify({}), + "subdir/.eslintrc.json": JSON.stringify({ + ignorePatterns: "*.js" + }), + ".eslintignore": "!foo.js", + "foo.js": "", + "subdir/foo.js": "", + "subdir/bar.js": "" + } + }).CLIEngine; + + it("'isPathIgnored()' should return 'false' for unignored 'foo.js'.", () => { + const engine = new InMemoryCLIEngine(); + + assert.strictEqual(engine.isPathIgnored("foo.js"), false); + assert.strictEqual(engine.isPathIgnored("subdir/foo.js"), false); + }); + + it("'isPathIgnored()' should return 'true' for ignored 'bar.js'.", () => { + const engine = new InMemoryCLIEngine(); + + assert.strictEqual(engine.isPathIgnored("subdir/bar.js"), true); + }); + + it("'executeOnFiles()' should verify unignored 'foo.js'.", () => { + const engine = new InMemoryCLIEngine(); + const filePaths = engine.executeOnFiles("**/*.js").results.map(r => r.filePath); + + assert.deepStrictEqual(filePaths, [ + path.join(root, "subdir/foo.js"), + path.join(root, "foo.js") + ]); + }); + }); + + describe("if the config in a child directory has 'root:true', ignorePatterns in the config file in the parent directory should not be used.", () => { + const InMemoryCLIEngine = defineCLIEngineWithInMemoryFileSystem({ + cwd: () => root, + files: { + ".eslintrc.js": `module.exports = ${JSON.stringify({ + ignorePatterns: "foo.js" + })}`, + "subdir/.eslintrc.js": `module.exports = ${JSON.stringify({ + root: true, + ignorePatterns: "bar.js" + })}`, + "foo.js": "", + "bar.js": "", + "subdir/foo.js": "", + "subdir/bar.js": "" + } + }).CLIEngine; + + it("'isPathIgnored()' should return 'true' for 'foo.js' in the root directory.", () => { + const engine = new InMemoryCLIEngine(); + + assert.strictEqual(engine.isPathIgnored("foo.js"), true); + }); + + it("'isPathIgnored()' should return 'false' for 'bar.js' in the root directory.", () => { + const engine = new InMemoryCLIEngine(); + + assert.strictEqual(engine.isPathIgnored("bar.js"), false); + }); + + it("'isPathIgnored()' should return 'false' for 'foo.js' in the child directory.", () => { + const engine = new InMemoryCLIEngine(); + + assert.strictEqual(engine.isPathIgnored("subdir/foo.js"), false); + }); + + it("'isPathIgnored()' should return 'true' for 'bar.js' in the child directory.", () => { + const engine = new InMemoryCLIEngine(); + + assert.strictEqual(engine.isPathIgnored("subdir/bar.js"), true); + }); + + it("'executeOnFiles()' should verify 'bar.js' in the root directory and 'foo.js' in the child directory.", () => { + const engine = new InMemoryCLIEngine(); + const filePaths = engine.executeOnFiles("**/*.js").results.map(r => r.filePath); + + assert.deepStrictEqual(filePaths, [ + path.join(root, "subdir/foo.js"), + path.join(root, "bar.js") + ]); + }); + }); + + describe("even if the config in a child directory has 'root:true', .eslintignore should be used.", () => { + const InMemoryCLIEngine = defineCLIEngineWithInMemoryFileSystem({ + cwd: () => root, + files: { + ".eslintrc.js": `module.exports = ${JSON.stringify({})}`, + "subdir/.eslintrc.js": `module.exports = ${JSON.stringify({ + root: true, + ignorePatterns: "bar.js" + })}`, + ".eslintignore": "foo.js", + "foo.js": "", + "bar.js": "", + "subdir/foo.js": "", + "subdir/bar.js": "" + } + }).CLIEngine; + + it("'isPathIgnored()' should return 'true' for 'foo.js'.", () => { + const engine = new InMemoryCLIEngine(); + + assert.strictEqual(engine.isPathIgnored("foo.js"), true); + assert.strictEqual(engine.isPathIgnored("subdir/foo.js"), true); + }); + + it("'isPathIgnored()' should return 'false' for 'bar.js' in the root directory.", () => { + const engine = new InMemoryCLIEngine(); + + assert.strictEqual(engine.isPathIgnored("bar.js"), false); + }); + + it("'isPathIgnored()' should return 'true' for 'bar.js' in the child directory.", () => { + const engine = new InMemoryCLIEngine(); + + assert.strictEqual(engine.isPathIgnored("subdir/bar.js"), true); + }); + + it("'executeOnFiles()' should verify 'bar.js' in the root directory.", () => { + const engine = new InMemoryCLIEngine(); + const filePaths = engine.executeOnFiles("**/*.js").results.map(r => r.filePath); + + assert.deepStrictEqual(filePaths, [ + path.join(root, "bar.js") + ]); + }); + }); + + describe("ignorePatterns in the shareable config should be used.", () => { + const InMemoryCLIEngine = defineCLIEngineWithInMemoryFileSystem({ + cwd: () => root, + files: { + "node_modules/eslint-config-one/index.js": `module.exports = ${JSON.stringify({ + ignorePatterns: "foo.js" + })}`, + ".eslintrc.js": `module.exports = ${JSON.stringify({ + extends: "one" + })}`, + "foo.js": "", + "bar.js": "" + } + }).CLIEngine; + + it("'isPathIgnored()' should return 'true' for 'foo.js'.", () => { + const engine = new InMemoryCLIEngine(); + + assert.strictEqual(engine.isPathIgnored("foo.js"), true); + }); + + it("'isPathIgnored()' should return 'false' for 'bar.js'.", () => { + const engine = new InMemoryCLIEngine(); + + assert.strictEqual(engine.isPathIgnored("bar.js"), false); + }); + + it("'executeOnFiles()' should verify 'bar.js'.", () => { + const engine = new InMemoryCLIEngine(); + const filePaths = engine.executeOnFiles("**/*.js").results.map(r => r.filePath); + + assert.deepStrictEqual(filePaths, [ + path.join(root, "bar.js") + ]); + }); + }); + + describe("ignorePatterns in the shareable config should be relative to the entry config file.", () => { + const InMemoryCLIEngine = defineCLIEngineWithInMemoryFileSystem({ + cwd: () => root, + files: { + "node_modules/eslint-config-one/index.js": `module.exports = ${JSON.stringify({ + ignorePatterns: "/foo.js" + })}`, + ".eslintrc.js": `module.exports = ${JSON.stringify({ + extends: "one" + })}`, + "foo.js": "", + "subdir/foo.js": "" + } + }).CLIEngine; + + it("'isPathIgnored()' should return 'true' for 'foo.js'.", () => { + const engine = new InMemoryCLIEngine(); + + assert.strictEqual(engine.isPathIgnored("foo.js"), true); + }); + + it("'isPathIgnored()' should return 'false' for 'subdir/foo.js'.", () => { + const engine = new InMemoryCLIEngine(); + + assert.strictEqual(engine.isPathIgnored("subdir/foo.js"), false); + }); + + it("'executeOnFiles()' should verify 'subdir/foo.js'.", () => { + const engine = new InMemoryCLIEngine(); + const filePaths = engine.executeOnFiles("**/*.js").results.map(r => r.filePath); + + assert.deepStrictEqual(filePaths, [ + path.join(root, "subdir/foo.js") + ]); + }); + }); + + describe("ignorePatterns in a config file can unignore the files which are ignored by ignorePatterns in the shareable config should be used.", () => { + const InMemoryCLIEngine = defineCLIEngineWithInMemoryFileSystem({ + cwd: () => root, + files: { + "node_modules/eslint-config-one/index.js": `module.exports = ${JSON.stringify({ + ignorePatterns: "*.js" + })}`, + ".eslintrc.js": `module.exports = ${JSON.stringify({ + extends: "one", + ignorePatterns: "!bar.js" + })}`, + "foo.js": "", + "bar.js": "" + } + }).CLIEngine; + + it("'isPathIgnored()' should return 'true' for 'foo.js'.", () => { + const engine = new InMemoryCLIEngine(); + + assert.strictEqual(engine.isPathIgnored("foo.js"), true); + }); + + it("'isPathIgnored()' should return 'false' for 'bar.js'.", () => { + const engine = new InMemoryCLIEngine(); + + assert.strictEqual(engine.isPathIgnored("bar.js"), false); + }); + + it("'executeOnFiles()' should verify 'bar.js'.", () => { + const engine = new InMemoryCLIEngine(); + const filePaths = engine.executeOnFiles("**/*.js").results.map(r => r.filePath); + + assert.deepStrictEqual(filePaths, [ + path.join(root, "bar.js") + ]); + }); + }); + + }); }); From 58a5455cbebaaff1a133576fbf1f750b8794af38 Mon Sep 17 00:00:00 2001 From: Toru Nagashima Date: Tue, 17 Sep 2019 10:39:29 +0900 Subject: [PATCH 04/13] update document --- docs/user-guide/command-line-interface.md | 2 +- docs/user-guide/configuring.md | 21 +++++++++++++++++++-- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/docs/user-guide/command-line-interface.md b/docs/user-guide/command-line-interface.md index 532d8f94735..9e64c06279c 100644 --- a/docs/user-guide/command-line-interface.md +++ b/docs/user-guide/command-line-interface.md @@ -265,7 +265,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: diff --git a/docs/user-guide/configuring.md b/docs/user-guide/configuring.md index 0818f85e2a5..3f46231fcf5 100644 --- a/docs/user-guide/configuring.md +++ b/docs/user-guide/configuring.md @@ -854,8 +854,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. @@ -925,6 +925,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: From 6b09bd28c88f31137f900fcb63c2344bc891ab50 Mon Sep 17 00:00:00 2001 From: Toru Nagashima Date: Tue, 17 Sep 2019 11:00:53 +0900 Subject: [PATCH 05/13] fix test problem --- tests/lib/cli-engine/cli-engine.js | 397 ++++++++++++++++------------- 1 file changed, 213 insertions(+), 184 deletions(-) diff --git a/tests/lib/cli-engine/cli-engine.js b/tests/lib/cli-engine/cli-engine.js index 0e85bbcf33e..fb5672e2ab5 100644 --- a/tests/lib/cli-engine/cli-engine.js +++ b/tests/lib/cli-engine/cli-engine.js @@ -4692,22 +4692,25 @@ describe("CLIEngine", () => { }); }); - describe.only("with ignorePatterns config", () => { + describe("with ignorePatterns config", () => { const root = getFixturePath("cli-engine/ignore-patterns"); + let InMemoryCLIEngine; describe("ignorePatterns can add an ignore pattern ('foo.js').", () => { - const InMemoryCLIEngine = defineCLIEngineWithInMemoryFileSystem({ - cwd: () => root, - files: { - ".eslintrc.json": JSON.stringify({ - ignorePatterns: "foo.js" - }), - "foo.js": "", - "bar.js": "", - "subdir/foo.js": "", - "subdir/bar.js": "" - } - }).CLIEngine; + beforeEach(() => { + InMemoryCLIEngine = defineCLIEngineWithInMemoryFileSystem({ + cwd: () => root, + files: { + ".eslintrc.json": JSON.stringify({ + ignorePatterns: "foo.js" + }), + "foo.js": "", + "bar.js": "", + "subdir/foo.js": "", + "subdir/bar.js": "" + } + }).CLIEngine; + }); it("'isPathIgnored()' should return 'true' for 'foo.js'.", () => { const engine = new InMemoryCLIEngine(); @@ -4735,20 +4738,22 @@ describe("CLIEngine", () => { }); describe("ignorePatterns can add ignore patterns ('foo.js', '/bar.js').", () => { - const InMemoryCLIEngine = defineCLIEngineWithInMemoryFileSystem({ - cwd: () => root, - files: { - ".eslintrc.json": JSON.stringify({ - ignorePatterns: ["foo.js", "/bar.js"] - }), - "foo.js": "", - "bar.js": "", - "baz.js": "", - "subdir/foo.js": "", - "subdir/bar.js": "", - "subdir/baz.js": "" - } - }).CLIEngine; + beforeEach(() => { + InMemoryCLIEngine = defineCLIEngineWithInMemoryFileSystem({ + cwd: () => root, + files: { + ".eslintrc.json": JSON.stringify({ + ignorePatterns: ["foo.js", "/bar.js"] + }), + "foo.js": "", + "bar.js": "", + "baz.js": "", + "subdir/foo.js": "", + "subdir/bar.js": "", + "subdir/baz.js": "" + } + }).CLIEngine; + }); it("'isPathIgnored()' should return 'true' for 'foo.js'.", () => { const engine = new InMemoryCLIEngine(); @@ -4777,18 +4782,20 @@ describe("CLIEngine", () => { }); describe("ignorePatterns can unignore '/node_modules/foo'.", () => { - const InMemoryCLIEngine = defineCLIEngineWithInMemoryFileSystem({ - cwd: () => root, - files: { - ".eslintrc.json": JSON.stringify({ - ignorePatterns: "!/node_modules/foo" - }), - "node_modules/foo/index.js": "", - "node_modules/foo/.dot.js": "", - "node_modules/bar/index.js": "", - "foo.js": "" - } - }).CLIEngine; + beforeEach(() => { + InMemoryCLIEngine = defineCLIEngineWithInMemoryFileSystem({ + cwd: () => root, + files: { + ".eslintrc.json": JSON.stringify({ + ignorePatterns: "!/node_modules/foo" + }), + "node_modules/foo/index.js": "", + "node_modules/foo/.dot.js": "", + "node_modules/bar/index.js": "", + "foo.js": "" + } + }).CLIEngine; + }); it("'isPathIgnored()' should return 'false' for 'node_modules/foo/index.js'.", () => { const engine = new InMemoryCLIEngine(); @@ -4820,15 +4827,17 @@ describe("CLIEngine", () => { }); describe("ignorePatterns can unignore '.eslintrc.js'.", () => { - const InMemoryCLIEngine = defineCLIEngineWithInMemoryFileSystem({ - cwd: () => root, - files: { - ".eslintrc.js": `module.exports = ${JSON.stringify({ - ignorePatterns: "!.eslintrc.js" - })}`, - "foo.js": "" - } - }).CLIEngine; + beforeEach(() => { + InMemoryCLIEngine = defineCLIEngineWithInMemoryFileSystem({ + cwd: () => root, + files: { + ".eslintrc.js": `module.exports = ${JSON.stringify({ + ignorePatterns: "!.eslintrc.js" + })}`, + "foo.js": "" + } + }).CLIEngine; + }); it("'isPathIgnored()' should return 'false' for '.eslintrc.js'.", () => { const engine = new InMemoryCLIEngine(); @@ -4848,17 +4857,19 @@ describe("CLIEngine", () => { }); describe(".eslintignore can re-ignore files that are unignored by ignorePatterns.", () => { - const InMemoryCLIEngine = defineCLIEngineWithInMemoryFileSystem({ - cwd: () => root, - files: { - ".eslintrc.js": `module.exports = ${JSON.stringify({ - ignorePatterns: "!.*" - })}`, - ".eslintignore": ".foo*", - ".foo.js": "", - ".bar.js": "" - } - }).CLIEngine; + beforeEach(() => { + InMemoryCLIEngine = defineCLIEngineWithInMemoryFileSystem({ + cwd: () => root, + files: { + ".eslintrc.js": `module.exports = ${JSON.stringify({ + ignorePatterns: "!.*" + })}`, + ".eslintignore": ".foo*", + ".foo.js": "", + ".bar.js": "" + } + }).CLIEngine; + }); it("'isPathIgnored()' should return 'true' for re-ignored '.foo.js'.", () => { const engine = new InMemoryCLIEngine(); @@ -4884,17 +4895,19 @@ describe("CLIEngine", () => { }); describe(".eslintignore can unignore files that are ignored by ignorePatterns.", () => { - const InMemoryCLIEngine = defineCLIEngineWithInMemoryFileSystem({ - cwd: () => root, - files: { - ".eslintrc.js": `module.exports = ${JSON.stringify({ - ignorePatterns: "*.js" - })}`, - ".eslintignore": "!foo.js", - "foo.js": "", - "bar.js": "" - } - }).CLIEngine; + beforeEach(() => { + InMemoryCLIEngine = defineCLIEngineWithInMemoryFileSystem({ + cwd: () => root, + files: { + ".eslintrc.js": `module.exports = ${JSON.stringify({ + ignorePatterns: "*.js" + })}`, + ".eslintignore": "!foo.js", + "foo.js": "", + "bar.js": "" + } + }).CLIEngine; + }); it("'isPathIgnored()' should return 'false' for unignored 'foo.js'.", () => { const engine = new InMemoryCLIEngine(); @@ -4919,23 +4932,25 @@ describe("CLIEngine", () => { }); describe("ignorePatterns in the config file in a child directory affects to only in the directory.", () => { - const InMemoryCLIEngine = defineCLIEngineWithInMemoryFileSystem({ - cwd: () => root, - files: { - ".eslintrc.js": `module.exports = ${JSON.stringify({ - ignorePatterns: "foo.js" - })}`, - "subdir/.eslintrc.js": `module.exports = ${JSON.stringify({ - ignorePatterns: "bar.js" - })}`, - "foo.js": "", - "bar.js": "", - "subdir/foo.js": "", - "subdir/bar.js": "", - "subdir/subsubdir/foo.js": "", - "subdir/subsubdir/bar.js": "" - } - }).CLIEngine; + beforeEach(() => { + InMemoryCLIEngine = defineCLIEngineWithInMemoryFileSystem({ + cwd: () => root, + files: { + ".eslintrc.js": `module.exports = ${JSON.stringify({ + ignorePatterns: "foo.js" + })}`, + "subdir/.eslintrc.js": `module.exports = ${JSON.stringify({ + ignorePatterns: "bar.js" + })}`, + "foo.js": "", + "bar.js": "", + "subdir/foo.js": "", + "subdir/bar.js": "", + "subdir/subsubdir/foo.js": "", + "subdir/subsubdir/bar.js": "" + } + }).CLIEngine; + }); it("'isPathIgnored()' should return 'true' for 'foo.js'.", () => { const engine = new InMemoryCLIEngine(); @@ -4969,19 +4984,21 @@ describe("CLIEngine", () => { }); describe("ignorePatterns in the config file in a child directory can unignore the ignored files in the parent directory's config.", () => { - const InMemoryCLIEngine = defineCLIEngineWithInMemoryFileSystem({ - cwd: () => root, - files: { - ".eslintrc.js": `module.exports = ${JSON.stringify({ - ignorePatterns: "foo.js" - })}`, - "subdir/.eslintrc.js": `module.exports = ${JSON.stringify({ - ignorePatterns: "!foo.js" - })}`, - "foo.js": "", - "subdir/foo.js": "" - } - }).CLIEngine; + beforeEach(() => { + InMemoryCLIEngine = defineCLIEngineWithInMemoryFileSystem({ + cwd: () => root, + files: { + ".eslintrc.js": `module.exports = ${JSON.stringify({ + ignorePatterns: "foo.js" + })}`, + "subdir/.eslintrc.js": `module.exports = ${JSON.stringify({ + ignorePatterns: "!foo.js" + })}`, + "foo.js": "", + "subdir/foo.js": "" + } + }).CLIEngine; + }); it("'isPathIgnored()' should return 'true' for 'foo.js' in the root directory.", () => { const engine = new InMemoryCLIEngine(); @@ -5006,19 +5023,21 @@ describe("CLIEngine", () => { }); describe(".eslintignore can unignore files that are ignored by ignorePatterns in the config file in the child directory.", () => { - const InMemoryCLIEngine = defineCLIEngineWithInMemoryFileSystem({ - cwd: () => root, - files: { - ".eslintrc.json": JSON.stringify({}), - "subdir/.eslintrc.json": JSON.stringify({ - ignorePatterns: "*.js" - }), - ".eslintignore": "!foo.js", - "foo.js": "", - "subdir/foo.js": "", - "subdir/bar.js": "" - } - }).CLIEngine; + beforeEach(() => { + InMemoryCLIEngine = defineCLIEngineWithInMemoryFileSystem({ + cwd: () => root, + files: { + ".eslintrc.json": JSON.stringify({}), + "subdir/.eslintrc.json": JSON.stringify({ + ignorePatterns: "*.js" + }), + ".eslintignore": "!foo.js", + "foo.js": "", + "subdir/foo.js": "", + "subdir/bar.js": "" + } + }).CLIEngine; + }); it("'isPathIgnored()' should return 'false' for unignored 'foo.js'.", () => { const engine = new InMemoryCLIEngine(); @@ -5045,22 +5064,24 @@ describe("CLIEngine", () => { }); describe("if the config in a child directory has 'root:true', ignorePatterns in the config file in the parent directory should not be used.", () => { - const InMemoryCLIEngine = defineCLIEngineWithInMemoryFileSystem({ - cwd: () => root, - files: { - ".eslintrc.js": `module.exports = ${JSON.stringify({ - ignorePatterns: "foo.js" - })}`, - "subdir/.eslintrc.js": `module.exports = ${JSON.stringify({ - root: true, - ignorePatterns: "bar.js" - })}`, - "foo.js": "", - "bar.js": "", - "subdir/foo.js": "", - "subdir/bar.js": "" - } - }).CLIEngine; + beforeEach(() => { + InMemoryCLIEngine = defineCLIEngineWithInMemoryFileSystem({ + cwd: () => root, + files: { + ".eslintrc.js": `module.exports = ${JSON.stringify({ + ignorePatterns: "foo.js" + })}`, + "subdir/.eslintrc.js": `module.exports = ${JSON.stringify({ + root: true, + ignorePatterns: "bar.js" + })}`, + "foo.js": "", + "bar.js": "", + "subdir/foo.js": "", + "subdir/bar.js": "" + } + }).CLIEngine; + }); it("'isPathIgnored()' should return 'true' for 'foo.js' in the root directory.", () => { const engine = new InMemoryCLIEngine(); @@ -5098,21 +5119,23 @@ describe("CLIEngine", () => { }); describe("even if the config in a child directory has 'root:true', .eslintignore should be used.", () => { - const InMemoryCLIEngine = defineCLIEngineWithInMemoryFileSystem({ - cwd: () => root, - files: { - ".eslintrc.js": `module.exports = ${JSON.stringify({})}`, - "subdir/.eslintrc.js": `module.exports = ${JSON.stringify({ - root: true, - ignorePatterns: "bar.js" - })}`, - ".eslintignore": "foo.js", - "foo.js": "", - "bar.js": "", - "subdir/foo.js": "", - "subdir/bar.js": "" - } - }).CLIEngine; + beforeEach(() => { + InMemoryCLIEngine = defineCLIEngineWithInMemoryFileSystem({ + cwd: () => root, + files: { + ".eslintrc.js": `module.exports = ${JSON.stringify({})}`, + "subdir/.eslintrc.js": `module.exports = ${JSON.stringify({ + root: true, + ignorePatterns: "bar.js" + })}`, + ".eslintignore": "foo.js", + "foo.js": "", + "bar.js": "", + "subdir/foo.js": "", + "subdir/bar.js": "" + } + }).CLIEngine; + }); it("'isPathIgnored()' should return 'true' for 'foo.js'.", () => { const engine = new InMemoryCLIEngine(); @@ -5144,19 +5167,21 @@ describe("CLIEngine", () => { }); describe("ignorePatterns in the shareable config should be used.", () => { - const InMemoryCLIEngine = defineCLIEngineWithInMemoryFileSystem({ - cwd: () => root, - files: { - "node_modules/eslint-config-one/index.js": `module.exports = ${JSON.stringify({ - ignorePatterns: "foo.js" - })}`, - ".eslintrc.js": `module.exports = ${JSON.stringify({ - extends: "one" - })}`, - "foo.js": "", - "bar.js": "" - } - }).CLIEngine; + beforeEach(() => { + InMemoryCLIEngine = defineCLIEngineWithInMemoryFileSystem({ + cwd: () => root, + files: { + "node_modules/eslint-config-one/index.js": `module.exports = ${JSON.stringify({ + ignorePatterns: "foo.js" + })}`, + ".eslintrc.js": `module.exports = ${JSON.stringify({ + extends: "one" + })}`, + "foo.js": "", + "bar.js": "" + } + }).CLIEngine; + }); it("'isPathIgnored()' should return 'true' for 'foo.js'.", () => { const engine = new InMemoryCLIEngine(); @@ -5181,19 +5206,21 @@ describe("CLIEngine", () => { }); describe("ignorePatterns in the shareable config should be relative to the entry config file.", () => { - const InMemoryCLIEngine = defineCLIEngineWithInMemoryFileSystem({ - cwd: () => root, - files: { - "node_modules/eslint-config-one/index.js": `module.exports = ${JSON.stringify({ - ignorePatterns: "/foo.js" - })}`, - ".eslintrc.js": `module.exports = ${JSON.stringify({ - extends: "one" - })}`, - "foo.js": "", - "subdir/foo.js": "" - } - }).CLIEngine; + beforeEach(() => { + InMemoryCLIEngine = defineCLIEngineWithInMemoryFileSystem({ + cwd: () => root, + files: { + "node_modules/eslint-config-one/index.js": `module.exports = ${JSON.stringify({ + ignorePatterns: "/foo.js" + })}`, + ".eslintrc.js": `module.exports = ${JSON.stringify({ + extends: "one" + })}`, + "foo.js": "", + "subdir/foo.js": "" + } + }).CLIEngine; + }); it("'isPathIgnored()' should return 'true' for 'foo.js'.", () => { const engine = new InMemoryCLIEngine(); @@ -5218,20 +5245,22 @@ describe("CLIEngine", () => { }); describe("ignorePatterns in a config file can unignore the files which are ignored by ignorePatterns in the shareable config should be used.", () => { - const InMemoryCLIEngine = defineCLIEngineWithInMemoryFileSystem({ - cwd: () => root, - files: { - "node_modules/eslint-config-one/index.js": `module.exports = ${JSON.stringify({ - ignorePatterns: "*.js" - })}`, - ".eslintrc.js": `module.exports = ${JSON.stringify({ - extends: "one", - ignorePatterns: "!bar.js" - })}`, - "foo.js": "", - "bar.js": "" - } - }).CLIEngine; + beforeEach(() => { + InMemoryCLIEngine = defineCLIEngineWithInMemoryFileSystem({ + cwd: () => root, + files: { + "node_modules/eslint-config-one/index.js": `module.exports = ${JSON.stringify({ + ignorePatterns: "*.js" + })}`, + ".eslintrc.js": `module.exports = ${JSON.stringify({ + extends: "one", + ignorePatterns: "!bar.js" + })}`, + "foo.js": "", + "bar.js": "" + } + }).CLIEngine; + }); it("'isPathIgnored()' should return 'true' for 'foo.js'.", () => { const engine = new InMemoryCLIEngine(); From 83721c973d0e6f9a6d4e8470af64ebe9b078f7b0 Mon Sep 17 00:00:00 2001 From: Toru Nagashima Date: Tue, 17 Sep 2019 12:58:13 +0900 Subject: [PATCH 06/13] add more tests --- tests/lib/cli-engine/cli-engine.js | 58 +++++++++++++++++++++++++++++- 1 file changed, 57 insertions(+), 1 deletion(-) diff --git a/tests/lib/cli-engine/cli-engine.js b/tests/lib/cli-engine/cli-engine.js index fb5672e2ab5..75f8a8fdba5 100644 --- a/tests/lib/cli-engine/cli-engine.js +++ b/tests/lib/cli-engine/cli-engine.js @@ -5244,7 +5244,7 @@ describe("CLIEngine", () => { }); }); - describe("ignorePatterns in a config file can unignore the files which are ignored by ignorePatterns in the shareable config should be used.", () => { + describe("ignorePatterns in a config file can unignore the files which are ignored by ignorePatterns in the shareable config.", () => { beforeEach(() => { InMemoryCLIEngine = defineCLIEngineWithInMemoryFileSystem({ cwd: () => root, @@ -5284,5 +5284,61 @@ describe("CLIEngine", () => { }); }); + describe("ignorePatterns in a config file should not be used if --no-ignore option was given.", () => { + beforeEach(() => { + InMemoryCLIEngine = defineCLIEngineWithInMemoryFileSystem({ + cwd: () => root, + files: { + ".eslintrc.js": `module.exports = ${JSON.stringify({ + ignorePatterns: "*.js" + })}`, + "foo.js": "" + } + }).CLIEngine; + }); + + it("'isPathIgnored()' should return 'false' for 'foo.js'.", () => { + const engine = new InMemoryCLIEngine({ ignore: false }); + + assert.strictEqual(engine.isPathIgnored("foo.js"), false); + }); + + it("'executeOnFiles()' should verify 'foo.js'.", () => { + const engine = new InMemoryCLIEngine({ ignore: false }); + const filePaths = engine.executeOnFiles("**/*.js").results.map(r => r.filePath); + + assert.deepStrictEqual(filePaths, [ + path.join(root, "foo.js") + ]); + }); + }); + + describe("ignorePatterns in overrides section is not allowed.", () => { + beforeEach(() => { + InMemoryCLIEngine = defineCLIEngineWithInMemoryFileSystem({ + cwd: () => root, + files: { + ".eslintrc.js": `module.exports = ${JSON.stringify({ + overrides: [ + { + files: "*.js", + ignorePatterns: "foo.js" + } + ] + })}`, + "foo.js": "" + } + }).CLIEngine; + }); + + it("should throw a configuration error.", () => { + assert.throws(() => { + const engine = new InMemoryCLIEngine(); + + engine.executeOnFiles("*.js"); + }, "Unexpected top-level property \"overrides[0].ignorePatterns\""); + }); + }); + }); }); From e59611d8cbbb54607425a2c7efee0500515b3194 Mon Sep 17 00:00:00 2001 From: Toru Nagashima Date: Tue, 17 Sep 2019 13:25:09 +0900 Subject: [PATCH 07/13] fix npm run perf --- Makefile.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile.js b/Makefile.js index a01d13bde30..14d65229ba3 100644 --- a/Makefile.js +++ b/Makefile.js @@ -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 From 869f96aa87c4f990f54e1eeccb0e3f7dbd66e6c2 Mon Sep 17 00:00:00 2001 From: Toru Nagashima Date: Tue, 17 Sep 2019 15:33:43 +0900 Subject: [PATCH 08/13] fix problem when the base path was the root --- lib/cli-engine/config-array/ignore-pattern.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/cli-engine/config-array/ignore-pattern.js b/lib/cli-engine/config-array/ignore-pattern.js index 3f28982a99c..b385aa923e9 100644 --- a/lib/cli-engine/config-array/ignore-pattern.js +++ b/lib/cli-engine/config-array/ignore-pattern.js @@ -38,8 +38,6 @@ const path = require("path"); const ignore = require("ignore"); const debug = require("debug")("eslint:ignore-pattern"); -// debug.enabled = true; - /** @typedef {ReturnType} Ignore */ //------------------------------------------------------------------------------ @@ -73,7 +71,7 @@ function getCommonAncestorPath(sourcePaths) { } } - return result; + return result || path.sep; } /** From 20c3b3664a3e54d1f3f9b91d00b5db62a5a48a14 Mon Sep 17 00:00:00 2001 From: Toru Nagashima Date: Sun, 29 Sep 2019 13:09:47 +0900 Subject: [PATCH 09/13] Update tests/lib/cli-engine/cli-engine.js Co-Authored-By: Kevin Partington --- tests/lib/cli-engine/cli-engine.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/lib/cli-engine/cli-engine.js b/tests/lib/cli-engine/cli-engine.js index b06a5be3190..b422edbecff 100644 --- a/tests/lib/cli-engine/cli-engine.js +++ b/tests/lib/cli-engine/cli-engine.js @@ -3874,7 +3874,7 @@ describe("CLIEngine", () => { }); describe("with --ignore-pattern option", () => { - it("should accept an array for options.ignorePattern", () => { + it("should accept a string for options.ignorePattern", () => { const cwd = getFixturePath("ignored-paths", "ignore-pattern"); const engine = new CLIEngine({ ignorePattern: "ignore-me.txt", From 425d3cdf11d9db5594ae67040a2002403f787e3e Mon Sep 17 00:00:00 2001 From: Toru Nagashima Date: Sun, 29 Sep 2019 13:09:54 +0900 Subject: [PATCH 10/13] Update lib/cli-engine/config-array/ignore-pattern.js Co-Authored-By: Kevin Partington --- lib/cli-engine/config-array/ignore-pattern.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/cli-engine/config-array/ignore-pattern.js b/lib/cli-engine/config-array/ignore-pattern.js index b385aa923e9..bdd7ef09cde 100644 --- a/lib/cli-engine/config-array/ignore-pattern.js +++ b/lib/cli-engine/config-array/ignore-pattern.js @@ -47,7 +47,7 @@ const debug = require("debug")("eslint:ignore-pattern"); /** * Get the path to the common ancestor directory of given paths. * @param {string[]} sourcePaths The paths to calculate the common ancestor. - * @returns {string} The path tp the common ancestor directory. + * @returns {string} The path to the common ancestor directory. */ function getCommonAncestorPath(sourcePaths) { let result = sourcePaths[0]; From e9db02b13c1f1fc9b8ccdb16463ac14a15fc9cfa Mon Sep 17 00:00:00 2001 From: Toru Nagashima Date: Tue, 8 Oct 2019 18:02:29 +0900 Subject: [PATCH 11/13] =?UTF-8?q?=E2=9D=A4=EF=B8=8F=20eslint-plugin-jsdoc?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/cli-engine/config-array/ignore-pattern.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/cli-engine/config-array/ignore-pattern.js b/lib/cli-engine/config-array/ignore-pattern.js index bdd7ef09cde..690a1901f58 100644 --- a/lib/cli-engine/config-array/ignore-pattern.js +++ b/lib/cli-engine/config-array/ignore-pattern.js @@ -120,7 +120,6 @@ class IgnorePattern { return DefaultPatterns; } - // eslint-disable-next-line valid-jsdoc /** * Create the default predicate function. * @param {string} cwd The current working directory. @@ -134,7 +133,6 @@ class IgnorePattern { return this.createIgnore([new IgnorePattern(DefaultPatterns, cwd)]); } - // eslint-disable-next-line valid-jsdoc /** * Create the predicate function from multiple `IgnorePattern` objects. * @param {IgnorePattern[]} ignorePatterns The list of ignore patterns. From 8e24579e9c870104539f1a4de5077c9aeab5fb02 Mon Sep 17 00:00:00 2001 From: Toru Nagashima Date: Fri, 11 Oct 2019 06:58:15 +0900 Subject: [PATCH 12/13] add description --- lib/cli-engine/config-array/ignore-pattern.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/cli-engine/config-array/ignore-pattern.js b/lib/cli-engine/config-array/ignore-pattern.js index 690a1901f58..6140194433e 100644 --- a/lib/cli-engine/config-array/ignore-pattern.js +++ b/lib/cli-engine/config-array/ignore-pattern.js @@ -170,6 +170,7 @@ class IgnorePattern { } /** + * Initialize a new `IgnorePattern` instance. * @param {string[]} patterns The glob patterns that ignore to lint. * @param {string} basePath The base path of `patterns`. */ From 3861ee0d2f960b135a514a60241fa8e7e368df92 Mon Sep 17 00:00:00 2001 From: Toru Nagashima Date: Wed, 20 Nov 2019 13:24:34 +0900 Subject: [PATCH 13/13] Update tests/lib/cli-engine/cli-engine.js Co-Authored-By: Kevin Partington --- tests/lib/cli-engine/cli-engine.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/lib/cli-engine/cli-engine.js b/tests/lib/cli-engine/cli-engine.js index e93a3b0f6b0..188be504624 100644 --- a/tests/lib/cli-engine/cli-engine.js +++ b/tests/lib/cli-engine/cli-engine.js @@ -3845,7 +3845,7 @@ describe("CLIEngine", () => { assert.throw(() => { try { - // eslint-disable-next-line no-new + // eslint-disable-next-line no-new new CLIEngine({ cwd }); } catch (error) { assert.strictEqual(error.messageTemplate, "failed-to-read-json");