summaryrefslogtreecommitdiff
path: root/tools/eslint/lib/util
diff options
context:
space:
mode:
Diffstat (limited to 'tools/eslint/lib/util')
-rw-r--r--tools/eslint/lib/util/comment-event-generator.js1
-rw-r--r--tools/eslint/lib/util/estraverse.js55
-rw-r--r--tools/eslint/lib/util/glob-util.js82
-rw-r--r--tools/eslint/lib/util/hash.js37
-rw-r--r--tools/eslint/lib/util/module-resolver.js87
-rw-r--r--tools/eslint/lib/util/npm-util.js4
-rw-r--r--tools/eslint/lib/util/path-util.js77
-rw-r--r--tools/eslint/lib/util/source-code-fixer.js3
-rw-r--r--tools/eslint/lib/util/source-code-util.js10
-rw-r--r--tools/eslint/lib/util/source-code.js12
-rw-r--r--tools/eslint/lib/util/traverser.js56
11 files changed, 335 insertions, 89 deletions
diff --git a/tools/eslint/lib/util/comment-event-generator.js b/tools/eslint/lib/util/comment-event-generator.js
index fb56ee1c05..90bbfe9f2f 100644
--- a/tools/eslint/lib/util/comment-event-generator.js
+++ b/tools/eslint/lib/util/comment-event-generator.js
@@ -24,6 +24,7 @@ function emitComments(comments, emitter, locs, eventName) {
if (comments.length > 0) {
comments.forEach(function(node) {
var index = locs.indexOf(node.loc);
+
if (index >= 0) {
locs.splice(index, 1);
} else {
diff --git a/tools/eslint/lib/util/estraverse.js b/tools/eslint/lib/util/estraverse.js
deleted file mode 100644
index d2e0d9a8f1..0000000000
--- a/tools/eslint/lib/util/estraverse.js
+++ /dev/null
@@ -1,55 +0,0 @@
-/**
- * @fileoverview Patch for estraverse
- * @author Toru Nagashima
- * @copyright 2015 Toru Nagashima. All rights reserved.
- * See LICENSE file in root directory for full license.
- */
-"use strict";
-
-//------------------------------------------------------------------------------
-// Requirements
-//------------------------------------------------------------------------------
-
-var estraverse = require("estraverse"),
- jsxKeys = require("estraverse-fb/keys");
-
-//------------------------------------------------------------------------------
-// Helers
-//------------------------------------------------------------------------------
-
-var experimentalKeys = {
- ExperimentalRestProperty: ["argument"],
- ExperimentalSpreadProperty: ["argument"]
-};
-
-/**
- * Adds a given keys to Syntax and VisitorKeys of estraverse.
- *
- * @param {object} keys - Key definitions to add.
- * This is an object as map.
- * Keys are the node type.
- * Values are an array of property names to visit.
- * @returns {void}
- */
-function installKeys(keys) {
- for (var key in keys) {
- /* istanbul ignore else */
- if (keys.hasOwnProperty(key)) {
- estraverse.Syntax[key] = key;
- if (keys[key]) {
- estraverse.VisitorKeys[key] = keys[key];
- }
- }
- }
-}
-
-// Add JSX node types.
-installKeys(jsxKeys);
-// Add Experimental node types.
-installKeys(experimentalKeys);
-
-//------------------------------------------------------------------------------
-// Public Interface
-//------------------------------------------------------------------------------
-
-module.exports = estraverse;
diff --git a/tools/eslint/lib/util/glob-util.js b/tools/eslint/lib/util/glob-util.js
index 68e64cba64..9a704d6308 100644
--- a/tools/eslint/lib/util/glob-util.js
+++ b/tools/eslint/lib/util/glob-util.js
@@ -12,9 +12,11 @@
var debug = require("debug"),
fs = require("fs"),
+ path = require("path"),
glob = require("glob"),
shell = require("shelljs"),
+ pathUtil = require("./path-util"),
IgnoredPaths = require("../ignored-paths");
debug = debug("eslint:glob-util");
@@ -33,12 +35,21 @@ debug = debug("eslint:glob-util");
*
* Also makes sure all path separators are POSIX style for `glob` compatibility.
*
- * @param {string[]} extensions An array of accepted extensions
+ * @param {object} [options] An options object
+ * @param {string[]} [options.extensions=[".js"]] An array of accepted extensions
+ * @param {string} [options.cwd=process.cwd()] The cwd to use to resolve relative pathnames
* @returns {Function} A function that takes a pathname and returns a glob that
* matches all files with the provided extensions if
* pathname is a directory.
*/
-function processPath(extensions) {
+function processPath(options) {
+ var cwd = (options && options.cwd) || process.cwd();
+ var extensions = (options && options.extensions) || [".js"];
+
+ extensions = extensions.map(function(ext) {
+ return ext.charAt(0) === "." ? ext.substr(1) : ext;
+ });
+
var suffix = "/**";
if (extensions.length === 1) {
@@ -56,12 +67,13 @@ function processPath(extensions) {
*/
return function(pathname) {
var newPath = pathname;
+ var resolvedPath = path.resolve(cwd, pathname);
- if (shell.test("-d", pathname)) {
+ if (shell.test("-d", resolvedPath)) {
newPath = pathname.replace(/[\/\\]$/, "") + suffix;
}
- return newPath.replace(/\\/g, "/").replace(/^\.\//, "");
+ return pathUtil.convertPathToPosix(newPath);
};
}
@@ -70,19 +82,15 @@ function processPath(extensions) {
//------------------------------------------------------------------------------
/**
- * Resolves the patterns into glob-based patterns for easier handling.
+ * Resolves any directory patterns into glob-based patterns for easier handling.
* @param {string[]} patterns File patterns (such as passed on the command line).
- * @param {string[]} extensions List of valid file extensions.
- * @returns {string[]} The equivalent glob patterns.
+ * @param {Object} options An options object.
+ * @returns {string[]} The equivalent glob patterns and filepath strings.
*/
-function resolveFileGlobPatterns(patterns, extensions) {
- extensions = extensions || [".js"];
+function resolveFileGlobPatterns(patterns, options) {
- extensions = extensions.map(function(ext) {
- return ext.charAt(0) === "." ? ext.substr(1) : ext;
- });
+ var processPathExtensions = processPath(options);
- var processPathExtensions = processPath(extensions);
return patterns.map(processPathExtensions);
}
@@ -92,6 +100,7 @@ function resolveFileGlobPatterns(patterns, extensions) {
*
* @param {string[]} globPatterns Glob patterns.
* @param {Object} [options] An options object.
+ * @param {string} [options.cwd] CWD (considered for relative filenames)
* @param {boolean} [options.ignore] False disables use of .eslintignore.
* @param {string} [options.ignorePath] The ignore file to use instead of .eslintignore.
* @param {string} [options.ignorePattern] A pattern of files to ignore.
@@ -99,46 +108,63 @@ function resolveFileGlobPatterns(patterns, extensions) {
*/
function listFilesToProcess(globPatterns, options) {
var ignoredPaths,
- ignoredPathsList,
files = [],
added = {},
- globOptions,
- rulesKey = "_rules";
+ globOptions;
+
+ var cwd = (options && options.cwd) || process.cwd();
/**
* 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 processed
+ * @param {boolean} shouldWarnIgnored Whether or not a report should be made if
+ * the file is ignored
* @returns {void}
*/
- function addFile(filename) {
- if (ignoredPaths.contains(filename)) {
- return;
+ function addFile(filename, shouldWarnIgnored) {
+ var ignored = false;
+ var isSilentlyIgnored;
+
+ if (options.ignore !== false) {
+ if (ignoredPaths.contains(filename, "default")) {
+ isSilentlyIgnored = true;
+ }
+ if (ignoredPaths.contains(filename, "custom")) {
+ if (shouldWarnIgnored) {
+ ignored = true;
+ } else {
+ isSilentlyIgnored = true;
+ }
+ }
+ if (isSilentlyIgnored && !ignored) {
+ return;
+ }
}
- filename = fs.realpathSync(filename);
if (added[filename]) {
return;
}
- files.push(filename);
+ files.push({filename: filename, ignored: ignored});
added[filename] = true;
}
options = options || { ignore: true, dotfiles: true };
ignoredPaths = new IgnoredPaths(options);
- ignoredPathsList = ignoredPaths.ig.custom[rulesKey].map(function(rule) {
- return rule.pattern;
- });
globOptions = {
nodir: true,
- ignore: ignoredPathsList
+ cwd: cwd
};
debug("Creating list of files to process.");
globPatterns.forEach(function(pattern) {
- if (shell.test("-f", pattern)) {
- addFile(pattern);
+ var file = path.resolve(cwd, pattern);
+
+ if (shell.test("-f", file)) {
+ addFile(fs.realpathSync(file), !shell.test("-d", file));
} else {
- glob.sync(pattern, globOptions).forEach(addFile);
+ glob.sync(pattern, globOptions).forEach(function(globMatch) {
+ addFile(path.resolve(cwd, globMatch), false);
+ });
}
});
diff --git a/tools/eslint/lib/util/hash.js b/tools/eslint/lib/util/hash.js
new file mode 100644
index 0000000000..b0271bbf03
--- /dev/null
+++ b/tools/eslint/lib/util/hash.js
@@ -0,0 +1,37 @@
+/**
+ * @fileoverview Defining the hashing function in one place.
+ * @author Michael Ficarra
+ * @copyright 2016 Michael Ficarra. All rights reserved.
+ * See LICENSE file in root directory for full license.
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+var murmur = require("imurmurhash");
+
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+//------------------------------------------------------------------------------
+// Private
+//------------------------------------------------------------------------------
+
+/**
+ * hash the given string
+ * @param {string} str the string to hash
+ * @returns {string} the hash
+ */
+function hash(str) {
+ return murmur(str).result().toString(36);
+}
+
+//------------------------------------------------------------------------------
+// Public Interface
+//------------------------------------------------------------------------------
+
+module.exports = hash;
diff --git a/tools/eslint/lib/util/module-resolver.js b/tools/eslint/lib/util/module-resolver.js
new file mode 100644
index 0000000000..9df544cf2b
--- /dev/null
+++ b/tools/eslint/lib/util/module-resolver.js
@@ -0,0 +1,87 @@
+/**
+ * @fileoverview Implements the Node.js require.resolve algorithm
+ * @author Nicholas C. Zakas
+ * @copyright 2016 Nicholas C. Zakas. All rights reserved.
+ * See LICENSE file in root directory for full license.
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+var lodash = require("lodash"),
+ Module = require("module");
+
+//------------------------------------------------------------------------------
+// Private
+//------------------------------------------------------------------------------
+
+var DEFAULT_OPTIONS = {
+
+ /*
+ * module.paths is an array of paths to search for resolving things relative
+ * to this file. Module.globalPaths contains all of the special Node.js
+ * directories that can also be searched for modules.
+ */
+ lookupPaths: module.paths.concat(Module.globalPaths)
+};
+
+/**
+ * Resolves modules based on a set of options.
+ * @param {Object} options The options for resolving modules.
+ * @param {string[]} options.lookupPaths An array of paths to include in the
+ * lookup with the highest priority paths coming first.
+ * @constructor
+ */
+function ModuleResolver(options) {
+ options = options || {};
+
+ this.options = lodash.assign({}, DEFAULT_OPTIONS, options);
+}
+
+ModuleResolver.prototype = {
+
+ /**
+ * Resolves the file location of a given module relative to the configured
+ * lookup paths.
+ * @param {string} name The module name to resolve.
+ * @param {string} extraLookupPath An extra path to look into for the module.
+ * This path is used with the highest priority.
+ * @returns {string} The resolved file path for the module.
+ * @throws {Error} If the module cannot be resolved.
+ */
+ resolve: function(name, extraLookupPath) {
+
+ /*
+ * First, clone the lookup paths so we're not messing things up for
+ * subsequent calls to this function. Then, move the extraLookupPath to the
+ * top of the lookup paths list so it will be searched first.
+ */
+ var lookupPaths = this.options.lookupPaths.concat();
+
+ lookupPaths.unshift(extraLookupPath);
+
+ /**
+ * Module._findPath is an internal method to Node.js, then one they use to
+ * lookup file paths when require() is called. So, we are hooking into the
+ * exact same logic that Node.js uses.
+ */
+ var result = Module._findPath(name, lookupPaths); // eslint-disable-line no-underscore-dangle
+
+ if (!result) {
+ throw new Error("Cannot find module '" + name + "'");
+ }
+
+ return result;
+
+ }
+
+};
+
+//------------------------------------------------------------------------------
+// Public API
+//------------------------------------------------------------------------------
+
+module.exports = ModuleResolver;
diff --git a/tools/eslint/lib/util/npm-util.js b/tools/eslint/lib/util/npm-util.js
index fc9d3a6c70..c45f079011 100644
--- a/tools/eslint/lib/util/npm-util.js
+++ b/tools/eslint/lib/util/npm-util.js
@@ -28,8 +28,10 @@ var fs = require("fs"),
*/
function findPackageJson(startDir) {
var dir = path.resolve(startDir || process.cwd());
+
do {
var pkgfile = path.join(dir, "package.json");
+
if (!fs.existsSync(pkgfile)) {
dir = path.join(dir, "..");
continue;
@@ -69,10 +71,12 @@ function installSyncSaveDev(packages) {
function check(packages, opt) {
var deps = [];
var pkgJson = (opt) ? findPackageJson(opt.startDir) : findPackageJson();
+
if (!pkgJson) {
throw new Error("Could not find a package.json file. Run 'npm init' to create one.");
}
var fileJson = JSON.parse(fs.readFileSync(pkgJson, "utf8"));
+
if (opt.devDependencies && typeof fileJson.devDependencies === "object") {
deps = deps.concat(Object.keys(fileJson.devDependencies));
}
diff --git a/tools/eslint/lib/util/path-util.js b/tools/eslint/lib/util/path-util.js
new file mode 100644
index 0000000000..d1c8197adf
--- /dev/null
+++ b/tools/eslint/lib/util/path-util.js
@@ -0,0 +1,77 @@
+/**
+ * @fileoverview Common helpers for operations on filenames and paths
+ * @author Ian VanSchooten
+ * @copyright 2016 Ian VanSchooten. All rights reserved.
+ * See LICENSE in root directory for full license.
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+var path = require("path"),
+ isAbsolute = require("path-is-absolute");
+
+//------------------------------------------------------------------------------
+// Private
+//------------------------------------------------------------------------------
+
+/**
+ * Replace Windows with posix style paths
+ *
+ * @param {string} filepath Path to convert
+ * @returns {string} Converted filepath
+ */
+function convertPathToPosix(filepath) {
+ var normalizedFilepath = path.normalize(filepath);
+ var posixFilepath = normalizedFilepath.replace(/\\/g, "/");
+
+ return posixFilepath;
+}
+
+/**
+ * Converts an absolute filepath to a relative path from a given base path
+ *
+ * For example, if the filepath is `/my/awesome/project/foo.bar`,
+ * and the base directory is `/my/awesome/project/`,
+ * then this function should return `foo.bar`.
+ *
+ * path.relative() does something similar, but it requires a baseDir (`from` argument).
+ * This function makes it optional and just removes a leading slash if the baseDir is not given.
+ *
+ * It does not take into account symlinks (for now).
+ *
+ * @param {string} filepath Path to convert to relative path. If already relative,
+ * it will be assumed to be relative to process.cwd(),
+ * converted to absolute, and then processed.
+ * @param {string} [baseDir] Absolute base directory to resolve the filepath from.
+ * If not provided, all this function will do is remove
+ * a leading slash.
+ * @returns {string} Relative filepath
+ */
+function getRelativePath(filepath, baseDir) {
+ var relativePath;
+
+ if (!isAbsolute(filepath)) {
+ filepath = path.resolve(filepath);
+ }
+ if (baseDir) {
+ if (!isAbsolute(baseDir)) {
+ throw new Error("baseDir should be an absolute path");
+ }
+ relativePath = path.relative(baseDir, filepath);
+ } else {
+ relativePath = filepath.replace(/^\//, "");
+ }
+ return relativePath;
+}
+
+//------------------------------------------------------------------------------
+// Public Interface
+//------------------------------------------------------------------------------
+
+module.exports = {
+ convertPathToPosix: convertPathToPosix,
+ getRelativePath: getRelativePath
+};
diff --git a/tools/eslint/lib/util/source-code-fixer.js b/tools/eslint/lib/util/source-code-fixer.js
index da7a1f98d0..0ea68d382b 100644
--- a/tools/eslint/lib/util/source-code-fixer.js
+++ b/tools/eslint/lib/util/source-code-fixer.js
@@ -105,11 +105,14 @@ SourceCodeFixer.applyFixes = function(sourceCode, messages) {
if (end < lastFixPos) {
if (start < 0) {
+
// Remove BOM.
prefix = "";
start = 0;
}
+
if (start === 0 && insertionText[0] === BOM) {
+
// Set BOM.
prefix = BOM;
insertionText = insertionText.slice(1);
diff --git a/tools/eslint/lib/util/source-code-util.js b/tools/eslint/lib/util/source-code-util.js
index 3a5396fdc0..b12b095f17 100644
--- a/tools/eslint/lib/util/source-code-util.js
+++ b/tools/eslint/lib/util/source-code-util.js
@@ -36,11 +36,14 @@ function getSourceCodeOfFile(filename, options) {
var opts = lodash.assign({}, options, { rules: {}});
var cli = new CLIEngine(opts);
var results = cli.executeOnFiles([filename]);
+
if (results && results.results[0] && results.results[0].messages[0] && results.results[0].messages[0].fatal) {
var msg = results.results[0].messages[0];
+
throw new Error("(" + filename + ":" + msg.line + ":" + msg.column + ") " + msg.message);
}
var sourceCode = eslint.getSourceCode();
+
return sourceCode;
}
@@ -84,14 +87,17 @@ function getSourceCodeOfFiles(patterns, options, cb) {
opts = lodash.assign({}, defaultOptions, options);
}
debug("constructed options:", opts);
+ patterns = globUtil.resolveFileGlobPatterns(patterns, opts);
- patterns = globUtil.resolveFileGlobPatterns(patterns, opts.extensions);
- filenames = globUtil.listFilesToProcess(patterns, opts);
+ filenames = globUtil.listFilesToProcess(patterns, opts).reduce(function(files, fileInfo) {
+ return !fileInfo.ignored ? files.concat(fileInfo.filename) : files;
+ }, []);
if (filenames.length === 0) {
debug("Did not find any files matching pattern(s): " + patterns);
}
filenames.forEach(function(filename) {
var sourceCode = getSourceCodeOfFile(filename, opts);
+
if (sourceCode) {
debug("got sourceCode of", filename);
sourceCodes[filename] = sourceCode;
diff --git a/tools/eslint/lib/util/source-code.js b/tools/eslint/lib/util/source-code.js
index 9dd0a45120..a79bd5b531 100644
--- a/tools/eslint/lib/util/source-code.js
+++ b/tools/eslint/lib/util/source-code.js
@@ -12,7 +12,7 @@
var lodash = require("lodash"),
createTokenStore = require("../token-store.js"),
- estraverse = require("./estraverse");
+ Traverser = require("./traverser");
//------------------------------------------------------------------------------
// Private
@@ -126,11 +126,13 @@ function SourceCode(text, ast) {
// create token store methods
var tokenStore = createTokenStore(ast.tokens);
+
Object.keys(tokenStore).forEach(function(methodName) {
this[methodName] = tokenStore[methodName];
}, this);
var tokensAndCommentsStore = createTokenStore(this.tokensAndComments);
+
this.getTokenOrCommentBefore = tokensAndCommentsStore.getTokenBefore;
this.getTokenOrCommentAfter = tokensAndCommentsStore.getTokenAfter;
@@ -261,10 +263,11 @@ SourceCode.prototype = {
* @returns {ASTNode} The node if found or null if not found.
*/
getNodeByRangeIndex: function(index) {
- var result = null;
- var resultParent = null;
+ var result = null,
+ resultParent = null,
+ traverser = new Traverser();
- estraverse.traverse(this.ast, {
+ traverser.traverse(this.ast, {
enter: function(node, parent) {
if (node.range[0] <= index && index < node.range[1]) {
result = node;
@@ -294,6 +297,7 @@ SourceCode.prototype = {
*/
isSpaceBetweenTokens: function(first, second) {
var text = this.text.slice(first.range[1], second.range[0]);
+
return /\s/.test(text.replace(/\/\*.*?\*\//g, ""));
}
};
diff --git a/tools/eslint/lib/util/traverser.js b/tools/eslint/lib/util/traverser.js
new file mode 100644
index 0000000000..74cfbec2b2
--- /dev/null
+++ b/tools/eslint/lib/util/traverser.js
@@ -0,0 +1,56 @@
+/**
+ * @fileoverview Wrapper around estraverse
+ * @author Nicholas C. Zakas
+ * @copyright 2016 Nicholas C. Zakas. All rights reserved.
+ * See LICENSE in root directory for full license.
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+var estraverse = require("estraverse");
+
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+var KEY_BLACKLIST = [
+ "parent",
+ "leadingComments",
+ "trailingComments"
+];
+
+/**
+ * Wrapper around an estraverse controller that ensures the correct keys
+ * are visited.
+ * @constructor
+ */
+function Traverser() {
+
+ var controller = Object.create(new estraverse.Controller()),
+ originalTraverse = controller.traverse;
+
+ // intercept call to traverse() and add the fallback key to the visitor
+ controller.traverse = function(node, visitor) {
+ visitor.fallback = Traverser.getKeys;
+ return originalTraverse.call(this, node, visitor);
+ };
+
+ return controller;
+}
+
+/**
+ * Calculates the keys to use for traversal.
+ * @param {ASTNode} node The node to read keys from.
+ * @returns {string[]} An array of keys to visit on the node.
+ * @private
+ */
+Traverser.getKeys = function(node) {
+ return Object.keys(node).filter(function(key) {
+ return KEY_BLACKLIST.indexOf(key) === -1;
+ });
+};
+
+module.exports = Traverser;