Skip to content

Commit

Permalink
Breaking: migrate from minimatch to node-ignore (fixes eslint#2365)
Browse files Browse the repository at this point in the history
also addresses eslint#3707 and eslint#3898 and eslint#3550 and eslint#4041
  • Loading branch information
gronke committed Oct 8, 2015
1 parent 1260e60 commit 5838c76
Show file tree
Hide file tree
Showing 19 changed files with 546 additions and 244 deletions.
22 changes: 11 additions & 11 deletions .eslintignore
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
bin/**
build/**
conf/**
coverage/**
docs/**
scripts/**
templates/**
tests/bench/**
tests/fixtures/**
tests/performance/**
tmp/**
bin/*
build/*
conf/*
coverage/*
docs/*
scripts/*
templates/*
tests/bench/*
tests/fixtures/*
tests/performance/*
tmp/*
test.js
2 changes: 1 addition & 1 deletion docs/user-guide/command-line-interface.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Command line Interface
# Command Line Interface

To run ESLint on Node.js, you must have npm installed. If npm is not installed, follow the instructions here: https://www.npmjs.com/

Expand Down
6 changes: 3 additions & 3 deletions docs/user-guide/configuring.md
Original file line number Diff line number Diff line change
Expand Up @@ -546,12 +546,12 @@ Globs are matched using [minimatch](https://github.com/isaacs/minimatch), so a n
* Lines preceded by `!` are negated patterns that re-include a pattern that was ignored by an earlier pattern.
* Brace expansion can refer to multiple files in a pattern. For example, `file.{js,ts,coffee}` will ignore `file.js`, `file.ts`, and `file.coffee`.

In addition to any patterns in a `.eslintignore` file, ESLint always ignores files in `node_modules/**`.
In addition to any patterns in a `.eslintignore` file, ESLint always ignores files in `/node_modules/` and `/bower_components/`.

For example, placing the following `.eslintignore` file in the current working directory will ignore all of `node_modules`, any files with the extensions `.ts.js` or `.coffee.js` extension that might have been transpiled, and anything in the `build/` directory except `build/index.js`:
For example, placing the following `.eslintignore` file in the current working directory will ignore all of `node_modules`, `bower_components`, any files with the extensions `.ts.js` or `.coffee.js` extension that might have been transpiled, and anything in the `build/` directory except `build/index.js`:

```text
# node_modules ignored by default
# node_modules and bower_components ignored by default
# Ignore files compiled from TypeScript and CoffeeScript
**/*.{ts,coffee}.js
Expand Down
52 changes: 29 additions & 23 deletions lib/cli-engine.js
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,7 @@ function createIgnoreResult(filePath) {
{
fatal: false,
severity: 1,
message: "File ignored because of your .eslintignore file. Use --no-ignore to override."
message: "File ignored because of a matching ignore pattern. Use --no-ignore to override."
}
],
errorCount: 0,
Expand Down Expand Up @@ -491,24 +491,24 @@ CLIEngine.prototype = {
* @returns {Object} The results for all files that were linted.
*/
executeOnFiles: function(patterns) {

var results = [],
processed = {},
options = this.options,
fileCache = this._fileCache, // eslint-disable-line no-underscore-dangle
configHelper = new Config(options),
ignoredPaths = IgnoredPaths.load(options),
ignoredPathsList = ignoredPaths.patterns || [],
ignoredPaths = new IgnoredPaths(options),
globOptions,
stats,
startTime,
prevConfig; // the previous configuration used

startTime = Date.now();

patterns = this.resolveFileGlobPatterns(patterns);

globOptions = {
nodir: true,
ignore: ignoredPathsList
nodir: true
};

/**
Expand Down Expand Up @@ -553,15 +553,26 @@ CLIEngine.prototype = {
* Executes the linter on a file defined by the `filename`. Skips
* unsupported file extensions and any files that are already linted.
* @param {string} filename The file to be linted
* @param {boolean} warnWhenIgnored Report warning when supplied pattern is ignored
* @returns {void}
*/
function executeOnFile(filename) {
function executeOnFile(filename, warnWhenIgnored) {

// if (filename.indexOf("passing.js") !== -1) {
// console.info("file", filename, warnWhenIgnored);
// }

warnWhenIgnored = warnWhenIgnored || false;

var shouldIgnore = ignoredPaths.contains(filename);
if (shouldIgnore) {
if ((options.ignore !== false) && shouldIgnore) {
if (warnWhenIgnored) {
results.push(createIgnoreResult(filename));
}
return;
}

filename = fs.realpathSync(filename);
filename = path.resolve(filename);
if (processed[filename]) {
return;
}
Expand Down Expand Up @@ -624,23 +635,18 @@ CLIEngine.prototype = {
}

patterns.forEach(function(pattern) {
if (shell.test("-f", pattern) && !ignoredPaths.contains(pattern)) {
executeOnFile(pattern);

var file = path.resolve(pattern);

if (shell.test("-f", file)) {
executeOnFile(pattern, !shell.test("-d", file));
} else {
glob.sync(pattern, globOptions).forEach(executeOnFile);
glob.sync(pattern, globOptions).forEach(function(globMatch) {
executeOnFile(globMatch, false);
});
}
});

// only warn for files explicitly passed on the command line
if (options.ignore) {
patterns.forEach(function(file) {
var fullPath = path.resolve(file);
if (shell.test("-f", fullPath) && !processed[fullPath]) {
results.push(createIgnoreResult(file));
}
});
}

stats = calculateStatsPerRun(results);

if (options.cache) {
Expand Down Expand Up @@ -669,7 +675,7 @@ CLIEngine.prototype = {
stats,
options = this.options,
configHelper = new Config(options),
ignoredPaths = IgnoredPaths.load(options),
ignoredPaths = new IgnoredPaths(options),
exclude = ignoredPaths.contains.bind(ignoredPaths);

if (filename && options.ignore && exclude(filename)) {
Expand Down Expand Up @@ -708,7 +714,7 @@ CLIEngine.prototype = {
var ignoredPaths;

if (this.options.ignore) {
ignoredPaths = IgnoredPaths.load(this.options);
ignoredPaths = new IgnoredPaths(this.options);
return ignoredPaths.contains(filePath);
}

Expand Down
136 changes: 67 additions & 69 deletions lib/ignored-paths.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@

var fs = require("fs"),
debug = require("debug"),
minimatch = require("minimatch"),
path = require("path"),
ignore = require("ignore"),
FileFinder = require("./file-finder");

debug = debug("eslint:ignored-paths");
Expand All @@ -21,42 +22,16 @@ debug = debug("eslint:ignored-paths");
//------------------------------------------------------------------------------

var ESLINT_IGNORE_FILENAME = ".eslintignore";
var DEFAULT_IGNORE_PATTERNS = [
"/node_modules/",
"/bower_components/"
];


//------------------------------------------------------------------------------
// Helpers
//------------------------------------------------------------------------------

/**
* Load and parse ignore patterns from the file at the given path
* @param {string} filepath Path to the ignore file.
* @returns {string[]} An array of ignore patterns or an empty array if no ignore file.
*/
function loadIgnoreFile(filepath) {
var ignorePatterns = [];

/**
* Check if string is not empty
* @param {string} line string to examine
* @returns {boolean} True is its not empty
* @private
*/
function nonEmpty(line) {
return line.trim() !== "" && line[0] !== "#";
}

if (filepath) {
try {
ignorePatterns = fs.readFileSync(filepath, "utf8").split(/\r?\n/).filter(nonEmpty);
} catch (e) {
e.message = "Cannot read ignore file: " + filepath + "\nError: " + e.message;
throw e;
}
}

return ["node_modules/**"].concat(ignorePatterns);
}

var ignoreFileFinder;

/**
Expand All @@ -71,7 +46,6 @@ function findIgnoreFile() {
return ignoreFileFinder.findInDirectoryOrParents();
}


//------------------------------------------------------------------------------
// Public Interface
//------------------------------------------------------------------------------
Expand All @@ -80,64 +54,88 @@ function findIgnoreFile() {
* IgnoredPaths
* @constructor
* @class IgnoredPaths
* @param {Array} patterns to be matched against file paths
*/
function IgnoredPaths(patterns) {
this.patterns = patterns;
}

/**
* IgnoredPaths initializer
* @param {Object} options object containing 'ignore' and 'ignorePath' properties
* @returns {IgnoredPaths} object, with patterns loaded from the ignore file
* @param {Object} options object containing 'ignore', 'ignorePath' and 'patterns' properties
*/
IgnoredPaths.load = function(options) {
var patterns;
function IgnoredPaths(options) {

options = options || {};

if (options.ignore) {
patterns = loadIgnoreFile(options.ignorePath || findIgnoreFile());
} else {
patterns = [];
this.baseDir = null;
this.ig = new ignore.Ignore({
twoGlobstars: true,
ignore: []
});

this.defaultPatterns = DEFAULT_IGNORE_PATTERNS.concat(options.patterns || []);
if (options.dotfiles !== true) {
this.ig.addPattern(".*");
}

if (options.ignorePattern) {
patterns.push(options.ignorePattern);
this.ig.on("warn", function(e) {
var originInfo = (e.data && e.data.origin) ? " (" + e.data.origin + ")" : "";
console.info("IgnoredPaths: " + e.message + originInfo);
});

if (options) {

if (options.ignore) {

this.ig.addPattern(this.defaultPatterns);

var ignorePath;

if (options.ignorePattern) {
this.ig.addPattern(options.ignorePattern);
}

if (options.ignorePath) {
try {
fs.statSync(options.ignorePath);
ignorePath = options.ignorePath;
} catch (e) {
e.message = "Cannot read ignore file: " + options.ignorePath + "\nError: " + e.message;
throw e;
}
} else if (options.ignorePath !== false) {
ignorePath = findIgnoreFile();
try {
fs.statSync(ignorePath);
} catch (e) {
return this;
}
}

this.baseDir = path.dirname(ignorePath);
this.ig.addIgnoreFile(ignorePath);

}

this.options = options;

}

return new IgnoredPaths(patterns);
};
return this;
}

/**
* Determine whether a file path is included in the configured ignore patterns
* @param {string} filepath Path to check
* @returns {boolean} true if the file path matches one or more patterns, false otherwise
*/
IgnoredPaths.prototype.contains = function(filepath) {
if (this.patterns === null) {
throw new Error("No ignore patterns loaded, call 'load' first");
}

filepath = filepath.replace("\\", "/");
filepath = filepath.replace(/^\.\//, "");
return this.patterns.reduce(function(ignored, pattern) {
var negated = pattern[0] === "!",
matches;

if (negated) {
pattern = pattern.slice(1);
}

// Remove leading "current folder" prefix
if (pattern.indexOf("./") === 0) {
pattern = pattern.slice(2);
if (this.baseDir) {
var base = path.resolve(this.baseDir) + "/";
if (filepath.indexOf(base) === 0) {
filepath = filepath.substr(base.length);
}
}

matches = minimatch(filepath, pattern) || minimatch(filepath, pattern + "/**");
filepath = filepath.replace(/^\//, "");

return matches ? !negated : ignored;
}, false);
return (this.ig.filter([filepath]).length === 0);
};

module.exports = IgnoredPaths;
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
"glob": "^5.0.14",
"globals": "^8.11.0",
"handlebars": "^4.0.0",
"ignore": "^2.2.16",
"inquirer": "^0.9.0",
"is-my-json-valid": "^2.10.0",
"is-resolvable": "^1.0.0",
Expand All @@ -57,7 +58,6 @@
"lodash.clonedeep": "^3.0.1",
"lodash.merge": "^3.3.2",
"lodash.omit": "^3.1.0",
"minimatch": "^2.0.1",
"mkdirp": "^0.5.0",
"object-assign": "^2.0.0",
"optionator": "^0.5.0",
Expand Down
Empty file added tests/.eslintignore
Empty file.
4 changes: 2 additions & 2 deletions tests/fixtures/.eslintignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
**/*.json
**/*.js
*.json
*.js
2 changes: 2 additions & 0 deletions tests/fixtures/.eslintignore_twoGlobstar
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
**/*.json
**/*.js
2 changes: 1 addition & 1 deletion tests/fixtures/cli-engine/.eslintignore
Original file line number Diff line number Diff line change
@@ -1 +1 @@
**/_node_modules
_node_modules/
2 changes: 1 addition & 1 deletion tests/fixtures/cli-engine/.eslintignore2
Original file line number Diff line number Diff line change
@@ -1 +1 @@
tests/fixtures
fixtures/
1 change: 1 addition & 0 deletions tests/fixtures/cli/.eslintignore_foo
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
foo/*
4 changes: 4 additions & 0 deletions tests/fixtures/cli/foo/.bar/passing.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
var foo = "bar";
if (foo) {
foo = "bar2";
}
4 changes: 4 additions & 0 deletions tests/fixtures/cli/node_modules/passing.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Empty file.
Empty file.
Loading

0 comments on commit 5838c76

Please sign in to comment.