Comparing version 0.3.0 to 0.4.0
#!/usr/bin/env node | ||
var cli = require("../lib/cli"); | ||
var exitCode = cli.execute(Array.prototype.slice.call(process.argv, 2)); | ||
var exitCode = cli.execute(process.argv); | ||
process.exit(exitCode); |
@@ -360,5 +360,7 @@ { | ||
"rules": { | ||
"no-console": 0, | ||
"no-global-strict": 0, | ||
"no-mixed-requires": 2, | ||
"no-console": 0 | ||
"no-path-concat": 2, | ||
"no-process-exit": 2 | ||
} | ||
@@ -365,0 +367,0 @@ }, |
@@ -10,10 +10,12 @@ { | ||
"no-alert": 2, | ||
"no-array-constructor": 2, | ||
"no-bitwise": 0, | ||
"no-caller": 2, | ||
"no-bitwise": 0, | ||
"no-catch-shadow": 2, | ||
"no-console": 2, | ||
"no-comma-dangle": 2, | ||
"no-cond-assign": 2, | ||
"no-console": 2, | ||
"no-control-regex": 2, | ||
"no-debugger": 2, | ||
"no-delete-var": 2, | ||
"no-div-regex": 0, | ||
@@ -24,62 +26,70 @@ "no-dupe-keys": 2, | ||
"no-empty-class": 2, | ||
"no-empty-label": 2, | ||
"no-eq-null": 0, | ||
"no-eval": 2, | ||
"no-ex-assign": 2, | ||
"no-extend-native": 2, | ||
"no-extra-boolean-cast": 2, | ||
"no-extra-parens": 0, | ||
"no-extra-semi": 2, | ||
"no-extra-strict": 2, | ||
"no-fallthrough": 2, | ||
"no-floating-decimal": 0, | ||
"no-func-assign": 2, | ||
"no-floating-decimal": 0, | ||
"no-global-strict": 2, | ||
"no-implied-eval": 2, | ||
"no-invalid-regexp": 2, | ||
"no-with": 2, | ||
"no-fallthrough": 2, | ||
"no-global-strict": 2, | ||
"no-unreachable": 2, | ||
"no-undef": 2, | ||
"no-undef-init": 2, | ||
"no-unused-expressions": 2, | ||
"no-octal": 2, | ||
"no-octal-escape": 2, | ||
"no-obj-calls": 2, | ||
"no-iterator": 2, | ||
"no-label-var": 2, | ||
"no-labels": 2, | ||
"no-lone-blocks": 2, | ||
"no-loop-func": 2, | ||
"no-mixed-requires": [0, false], | ||
"no-multi-str": 2, | ||
"no-new-wrappers": 2, | ||
"no-native-reassign": 2, | ||
"no-negated-in-lhs": 2, | ||
"no-nested-ternary": 0, | ||
"no-new": 2, | ||
"no-new-func": 2, | ||
"no-native-reassign": 2, | ||
"no-new-object": 2, | ||
"no-new-wrappers": 2, | ||
"no-obj-calls": 2, | ||
"no-octal": 2, | ||
"no-octal-escape": 2, | ||
"no-path-concat": 0, | ||
"no-plusplus": 0, | ||
"no-delete-var": 2, | ||
"no-process-exit": 2, | ||
"no-proto": 2, | ||
"no-redeclare": 2, | ||
"no-regex-spaces": 2, | ||
"no-return-assign": 2, | ||
"no-nested-ternary": 0, | ||
"no-new-array": 2, | ||
"no-new-object": 2, | ||
"no-label-var": 2, | ||
"no-ternary": 0, | ||
"no-script-url": 2, | ||
"no-self-compare": 0, | ||
"no-spaced-func": 1, | ||
"no-shadow": 2, | ||
"no-shadow-restricted-names": 2, | ||
"no-spaced-func": 2, | ||
"no-sparse-arrays": 2, | ||
"no-sync": 0, | ||
"no-ternary": 0, | ||
"no-undef": 2, | ||
"no-undef-init": 2, | ||
"no-underscore-dangle": 2, | ||
"no-loop-func": 2, | ||
"no-empty-label": 2, | ||
"no-unreachable": 2, | ||
"no-unused-expressions": 2, | ||
"no-unused-vars": [2, "local"], | ||
"no-script-url": 2, | ||
"no-proto": 2, | ||
"no-iterator": 2, | ||
"no-mixed-requires": [0, false], | ||
"no-use-before-define": 2, | ||
"no-with": 2, | ||
"no-wrap-func": 2, | ||
"no-shadow": 2, | ||
"no-shadow-restricted-names": 2, | ||
"no-use-before-define": 2, | ||
"no-redeclare": 2, | ||
"no-negated-in-lhs": 2, | ||
"no-extend-native": 2, | ||
"no-yoda": 2, | ||
"block-scoped-var": 0, | ||
"brace-style": 0, | ||
"block-scoped-var": 0, | ||
"camelcase": 2, | ||
"complexity": [0, 11], | ||
"consistent-return": 2, | ||
"consistent-this": [0, "that"], | ||
"curly": 2, | ||
"curly": [2, "all"], | ||
"dot-notation": 2, | ||
"eqeqeq": 2, | ||
"func-names": 0, | ||
"func-style": [0, "declaration"], | ||
@@ -95,13 +105,13 @@ "guard-for-in": 0, | ||
"one-var": 0, | ||
"quote-props": 0, | ||
"quotes": [2, "double"], | ||
"quote-props": 0, | ||
"radix": 0, | ||
"regex-spaces": 2, | ||
"semi": 2, | ||
"sort-vars": 0, | ||
"space-infix-ops": 2, | ||
"space-return-throw-case": 2, | ||
"space-infix-ops": 2, | ||
"space-unary-word-ops": 0, | ||
"strict": 2, | ||
"use-isnan": 2, | ||
"valid-jsdoc": 0, | ||
"wrap-iife": 0, | ||
@@ -108,0 +118,0 @@ "wrap-regex": 0 |
@@ -89,5 +89,12 @@ /** | ||
/** | ||
* Outputs the results of the linting. | ||
* @param {Config} config The configuration options for the results. | ||
* @returns {boolean} True if the printing succeeds, false if not. | ||
* @private | ||
*/ | ||
function printResults(config) { | ||
var formatter, | ||
formatterPath; | ||
formatterPath, | ||
output; | ||
@@ -102,6 +109,9 @@ if (existsSync(path.resolve(process.cwd(), config.format))) { | ||
formatter = require(formatterPath); | ||
console.log(formatter(results, config)); | ||
output = formatter(results, config); | ||
if (output) { | ||
console.log(output); | ||
} | ||
return true; | ||
} catch (ex) { | ||
console.log("Could not find formatter '%s'.", config.format); | ||
console.error("Could not find formatter '%s'.", config.format); | ||
return false; | ||
@@ -203,22 +213,31 @@ } | ||
* Executes the CLI based on an array of arguments that is passed in. | ||
* @param {String[]} argv The array of arguments to process. | ||
* @param {string|Array|Object} args The arguments to process. | ||
* @returns {int} The exit code for the operation. | ||
*/ | ||
execute: function(argv) { | ||
execute: function(args) { | ||
var currentOptions = options.parse(argv), | ||
files = currentOptions._, | ||
var currentOptions, | ||
files, | ||
configHelper, | ||
result; | ||
try { | ||
currentOptions = options.parse(args); | ||
} catch (error) { | ||
console.error(error.message); | ||
return 1; | ||
} | ||
files = currentOptions._; | ||
// Ensure results from previous execution are not printed. | ||
results = []; | ||
if (currentOptions.v) { // version from package.json | ||
if (currentOptions.version) { // version from package.json | ||
console.log("v" + require("../package.json").version); | ||
} else if (currentOptions.h || !files.length) { | ||
} else if (currentOptions.help || !files.length) { | ||
options.help(); | ||
console.log(options.generateHelp()); | ||
@@ -225,0 +244,0 @@ } else { |
@@ -16,3 +16,4 @@ /** | ||
glob = require("glob"), | ||
stripComments = require("strip-json-comments"); | ||
stripComments = require("strip-json-comments"), | ||
yaml = require("js-yaml"); | ||
@@ -37,3 +38,4 @@ var existsSync = fs.existsSync || path.existsSync; | ||
* files and if so, load and parse them | ||
* @param {string} directory path | ||
* @param {string} directory The path to load from. | ||
* @returns {string[]} An array of paths to exclude or an empty array. | ||
*/ | ||
@@ -58,3 +60,3 @@ function loadIgnoreFile(directory) { | ||
* @param {string} filePath the path to the JSON config file | ||
* @return {object} the parsed config object (empty object if there was a parse error) | ||
* @returns {Object} the parsed config object (empty object if there was a parse error) | ||
*/ | ||
@@ -66,3 +68,3 @@ function loadConfig(filePath) { | ||
try { | ||
config = JSON.parse(stripComments(fs.readFileSync(filePath, "utf8"))); | ||
config = yaml.safeLoad(stripComments(fs.readFileSync(filePath, "utf8"))); | ||
} catch (e) { | ||
@@ -80,3 +82,3 @@ console.error("Cannot read config file:", filePath); | ||
* @param {string} directory the directory to start looking for a local config | ||
* @return {object} the local config object (empty object if there is no local config) | ||
* @returns {Object} the local config object (empty object if there is no local config) | ||
*/ | ||
@@ -102,4 +104,4 @@ function getLocalConfig(helper, directory) { | ||
* @constructor | ||
* @class Confi | ||
* @param {Object} options | ||
* @class Config | ||
* @param {Object} options Options to be passed in | ||
* @param {string} [cwd] current working directory. Defaults to process.cwd() | ||
@@ -115,3 +117,3 @@ */ | ||
this.baseConfig.format = options.format; | ||
useConfig = options.c || options.config; | ||
useConfig = options.config; | ||
@@ -127,3 +129,3 @@ if (useConfig) { | ||
* @param {string} filePath a file in whose directory we start looking for a local config | ||
* @return {object} config object | ||
* @returns {Object} config object | ||
*/ | ||
@@ -172,5 +174,5 @@ Config.prototype.getConfig = function (filePath) { | ||
* specify all options in a custom config. | ||
* @param {object} base the base config | ||
* @param {object} custom the custom config | ||
* @return {object} the merged config | ||
* @param {Object} base the base config | ||
* @param {Object} custom the custom config | ||
* @returns {Object} the merged config | ||
*/ | ||
@@ -183,3 +185,4 @@ Config.prototype.mergeConfigs = function (base, custom) { | ||
* Process given directory or file and cache ignore file if there's any | ||
* @param {string} path to file or directory | ||
* @param {string} directory The path to file or directory | ||
* @returns {void} | ||
*/ | ||
@@ -209,4 +212,4 @@ Config.prototype.cacheExclusions = function(directory) { | ||
* Check if the current file should not be processed | ||
* @param {string} resolved file path | ||
* return {boolean} if the file should be excluded | ||
* @param {string} filePath file path | ||
* @returns {boolean} if the file should be excluded | ||
*/ | ||
@@ -224,3 +227,3 @@ Config.prototype.checkForExclusion = function(filePath) { | ||
* @param {string} directory the directory to start searching from | ||
* @return {string|boolean} returns path of config file if found, or false if no config is found | ||
* @returns {string|boolean} returns path of config file if found, or false if no config is found | ||
*/ | ||
@@ -227,0 +230,0 @@ Config.prototype.findLocalConfigFile = function (directory) { |
@@ -60,9 +60,9 @@ /** | ||
* Parses info about globals from a special block comment and adds them to the `declaredGlobals` map. | ||
* @param {ASTNode} comment | ||
* @param {object} declaredGlobals | ||
* @returns {void} | ||
* @param {ASTNode} comment The comment node to parse. | ||
* @param {Object} declaredGlobals The already-declared globals. | ||
* @returns {boolean} True if globals were added, false if not. | ||
*/ | ||
function parseComment(comment, declaredGlobals) { | ||
if (comment.type !== "Block") { | ||
return; | ||
return false; | ||
} | ||
@@ -185,2 +185,4 @@ var text = comment.value; | ||
//------------------------------------------------------------------------------ | ||
@@ -205,2 +207,36 @@ // Public Interface | ||
/** | ||
* Parses text into an AST. Moved out here because the try-catch prevents | ||
* optimization of functions, so it's best to keep the try-catch as isolated | ||
* as possible | ||
* @param {string} text The text to parse. | ||
* @returns {ASTNode} The AST if successful or null if not. | ||
* @private | ||
*/ | ||
function parse(text) { | ||
/* | ||
* Check for parsing errors first. If there's a parsing error, nothing | ||
* else can happen. However, a parsing error does not throw an error | ||
* from this method - it's just considered a fatal error message, a | ||
* problem that ESLint identified just like any other. | ||
*/ | ||
try { | ||
return esprima.parse(text, { loc: true, range: true, raw: true, tokens: true, comment: true }); | ||
} catch (ex) { | ||
messages.push({ | ||
fatal: true, | ||
// messages come as "Line X: Unexpected token foo", so strip off leading part | ||
message: ex.message.substring(ex.message.indexOf(":") + 1).trim(), | ||
line: ex.lineNumber, | ||
column: ex.column | ||
}); | ||
return null; | ||
} | ||
} | ||
// set unlimited listeners (see https://github.com/eslint/eslint/issues/524) | ||
@@ -228,2 +264,4 @@ api.setMaxListeners(0); | ||
* @param {Object} config An object whose keys specify the rules to use. | ||
* @param {boolean=} saveState Indicates if the state from the last run should be saved. | ||
* Mostly useful for testing purposes. | ||
* @returns {Object[]} The results as an array of messages or null if no messages. | ||
@@ -233,4 +271,3 @@ */ | ||
var parseError = false, | ||
ast; | ||
var ast; | ||
@@ -241,27 +278,6 @@ if (!saveState) { | ||
/* | ||
* Check for parsing errors first. If there's a parsing error, nothing | ||
* else can happen. However, a parsing error does not throw an error | ||
* from this method - it's just considered a fatal error message, a | ||
* problem that ESLint identified just like any other. | ||
*/ | ||
try { | ||
ast = esprima.parse(text, { loc: true, range: true, raw: true, tokens: true, comment: true }); | ||
} catch (ex) { | ||
ast = parse(text); | ||
parseError = true; | ||
messages.push({ | ||
fatal: true, | ||
// messages come as "Line X: Unexpected token foo", so strip off leading part | ||
message: ex.message.substring(ex.message.indexOf(":") + 1).trim(), | ||
line: ex.lineNumber, | ||
column: ex.column | ||
}); | ||
} | ||
//if Esprima failed to parse the file, there's no sense in setting up rules | ||
if (!parseError) { | ||
if (ast) { | ||
// parse global comments and modify config rules | ||
@@ -333,3 +349,3 @@ config.rules = modifyRulesFromComments(ast, config); | ||
controller.traverse(ast, { | ||
enter: function(node) { | ||
enter: function(node, parent) { | ||
var comments = api.getComments(node), | ||
@@ -345,2 +361,4 @@ leadingComments = comments.leading, | ||
node.parent = parent; | ||
api.emit(node.type, node); | ||
@@ -363,11 +381,11 @@ | ||
trailingComments.forEach(function(node) { | ||
api.emit(node.type + "Comment:after", node); | ||
api.emit(node.type + "Comment:exit", node); | ||
}); | ||
} | ||
api.emit(node.type + ":after", node); | ||
api.emit(node.type + ":exit", node); | ||
if (leadingComments) { | ||
leadingComments.forEach(function(node) { | ||
api.emit(node.type + "Comment:after", node); | ||
api.emit(node.type + "Comment:exit", node); | ||
}); | ||
@@ -387,3 +405,3 @@ } | ||
* @param {ASTNode} node The AST node that the message relates to. | ||
* @param {Object} [location] An object containing the error line and column | ||
* @param {Object=} location An object containing the error line and column | ||
* numbers. If location is not provided the node's start location will | ||
@@ -421,5 +439,5 @@ * be used. | ||
* Gets the source code for the given node. | ||
* @param {ASTNode} [node] The AST node to get the text for. | ||
* @param {int} [beforeCount] The number of characters before the node to retrieve. | ||
* @param {int} [afterCount] The number of characters after the node to retrieve. | ||
* @param {ASTNode=} node The AST node to get the text for. | ||
* @param {int=} beforeCount The number of characters before the node to retrieve. | ||
* @param {int=} afterCount The number of characters after the node to retrieve. | ||
* @returns {string} The text representing the AST node. | ||
@@ -429,6 +447,6 @@ */ | ||
if (node) { | ||
return currentText ? currentText.slice(node.range[0] - (beforeCount || 0), | ||
return (currentText !== null) ? currentText.slice(node.range[0] - (beforeCount || 0), | ||
node.range[1] + (afterCount || 0)) : null; | ||
} else { | ||
return currentText || null; | ||
return currentText; | ||
} | ||
@@ -439,10 +457,2 @@ | ||
/** | ||
* Returns all comments. | ||
* @returns {Object[]} | ||
*/ | ||
api.getAllComments = function () { | ||
return controller.root.comments || []; | ||
}; | ||
/** | ||
* Gets all comments for the given node. | ||
@@ -452,3 +462,3 @@ * @param {ASTNode} node The AST node to get the comments for. | ||
*/ | ||
api.getComments = function (node) { | ||
api.getComments = function(node) { | ||
var ast = controller.root; | ||
@@ -469,6 +479,64 @@ | ||
/** | ||
* Retrieves the JSDoc comment for a given node. | ||
* @param {ASTNode} node The AST node to get the comment for. | ||
* @returns {ASTNode} The BlockComment node containing the JSDoc for the | ||
* given node or null if not found. | ||
*/ | ||
api.getJSDocComment = function(node) { | ||
var parent = node.parent, | ||
line = node.loc.start.line; | ||
/** | ||
* Finds a JSDoc comment node in an array of comment nodes. | ||
* @param {ASTNode[]} comments The array of comment nodes to search. | ||
* @returns {ASTNode} The node if found, null if not. | ||
* @private | ||
*/ | ||
function findJSDocComment(comments) { | ||
if (comments) { | ||
for (var i = comments.length - 1; i >= 0; i--) { | ||
if (comments[i].type === "Block" && comments[i].value.charAt(0) === "*") { | ||
if (line - comments[i].loc.end.line <= 1) { | ||
return comments[i]; | ||
} else { | ||
break; | ||
} | ||
} | ||
} | ||
} | ||
return null; | ||
} | ||
switch(node.type) { | ||
case "FunctionDeclaration": | ||
// first global function has its comments stolen by Program | ||
return findJSDocComment(((parent.type === "Program") ? parent : node).leadingComments); | ||
case "FunctionExpression": | ||
if (parent.type !== "CallExpression" || parent.callee !== node) { | ||
while (parent && !parent.leadingComments) { | ||
parent = parent.parent; | ||
} | ||
return parent ? findJSDocComment(parent.leadingComments) : null; | ||
} | ||
// falls through | ||
default: | ||
return null; | ||
} | ||
}; | ||
/** | ||
* Gets all tokens that are related to the given node. | ||
* @param {ASTNode} [node] The AST node to get the text for. | ||
* @param {int} [beforeCount] The number of tokens before the node to retrieve. | ||
* @param {int} [afterCount] The number of tokens after the node to retrieve. | ||
* @param {ASTNode=} node The AST node to get the text for. | ||
* @param {int=} beforeCount The number of tokens before the node to retrieve. | ||
* @param {int=} afterCount The number of tokens after the node to retrieve. | ||
* @returns {Object[]} Array of objects representing tokens. | ||
@@ -562,4 +630,4 @@ */ | ||
* Defines a new linting rule. | ||
* @param {string} unique rule identifier | ||
* @param {function} function from context to object mapping AST node types to event handlers | ||
* @param {string} ruleId A unique rule identifier | ||
* @param {Function} ruleModule Function from context to object mapping AST node types to event handlers | ||
* @returns {void} | ||
@@ -573,3 +641,3 @@ */ | ||
* Defines many new linting rules. | ||
* @param {object} map from unique rule identifier to rule | ||
* @param {object} rules map from unique rule identifier to rule | ||
* @returns {void} | ||
@@ -576,0 +644,0 @@ */ |
/** | ||
* @fileoverview Wrapper around Optimist to preconfigure CLI options output. | ||
* @author Nicholas C. Zakas | ||
* @fileoverview Options configuration for optionator. | ||
* @author George Zahariev | ||
*/ | ||
@@ -11,41 +11,39 @@ "use strict"; | ||
var optimist = require("optimist"); | ||
var optionator = require("optionator"); | ||
//------------------------------------------------------------------------------ | ||
// Initialization | ||
// Initialization and Public Interface | ||
//------------------------------------------------------------------------------ | ||
optimist.usage("eslint [options] file.js [file.js] [dir]"); | ||
// Help | ||
optimist.boolean("h"); | ||
optimist.alias("h", "help"); | ||
optimist.describe("h", "Show help."); | ||
// Config | ||
optimist.alias("c", "config"); | ||
optimist.describe("c", "Load configuration data from this file."); | ||
// RulesDir | ||
optimist.describe("rulesdir", "Load additional rules from this directory."); | ||
// Format | ||
optimist.alias("f", "format"); | ||
optimist.default("f", "stylish"); | ||
optimist.describe("f", "Use a specific output format."); | ||
// Version | ||
optimist.alias("v", "version"); | ||
optimist.describe("v", "Outputs the version number."); | ||
//------------------------------------------------------------------------------ | ||
// Public Interface | ||
//------------------------------------------------------------------------------ | ||
exports.help = function() { | ||
console.log(optimist.help()); | ||
}; | ||
exports.parse = function(argv) { | ||
return optimist.parse(argv); | ||
}; | ||
// exports "parse(args)", "generateHelp()", and "generateHelpForOption(optionName)" | ||
module.exports = optionator({ | ||
prepend: "eslint [options] file.js [file.js] [dir]", | ||
options: [{ | ||
heading: "Options" | ||
}, { | ||
option: "help", | ||
alias: "h", | ||
type: "Boolean", | ||
description: "Show help." | ||
}, { | ||
option: "config", | ||
alias: "c", | ||
type: "path::String", | ||
description: "Load configuration data from this file." | ||
}, { | ||
option: "rulesdir", | ||
type: "path::String", | ||
description: "Load additional rules from this directory." | ||
}, { | ||
option: "format", | ||
alias: "f", | ||
type: "String", | ||
default: "stylish", | ||
description: "Use a specific output format." | ||
}, { | ||
option: "version", | ||
alias: "v", | ||
type: "Boolean", | ||
description: "Outputs the version number." | ||
}] | ||
}); |
@@ -15,5 +15,5 @@ /** | ||
"getComments", | ||
"getAllComments", | ||
"getAncestors", | ||
"getScope" | ||
"getScope", | ||
"getJSDocComment" | ||
]; | ||
@@ -20,0 +20,0 @@ |
@@ -88,3 +88,3 @@ /** | ||
"BlockStatement": pushBlock, | ||
"BlockStatement:after": popBlock, | ||
"BlockStatement:exit": popBlock, | ||
"CatchClause": addCommonDeclaration, | ||
@@ -91,0 +91,0 @@ "VariableDeclaration": addCommonDeclaration, |
@@ -67,4 +67,4 @@ /** | ||
"FunctionExpression": startFunction, | ||
"FunctionDeclaration:after": endFunction, | ||
"FunctionExpression:after": endFunction, | ||
"FunctionDeclaration:exit": endFunction, | ||
"FunctionExpression:exit": endFunction, | ||
@@ -71,0 +71,0 @@ "CatchClause": increaseComplexity, |
@@ -5,17 +5,53 @@ /** | ||
*/ | ||
"use strict"; | ||
//------------------------------------------------------------------------------ | ||
// Helpers | ||
// Rule Definition | ||
//------------------------------------------------------------------------------ | ||
module.exports = function(context) { | ||
var multiOnly = (context.options[0] === "multi"); | ||
//------------------------------------------------------------------------------ | ||
// Rule Definition | ||
//------------------------------------------------------------------------------ | ||
//-------------------------------------------------------------------------- | ||
// Helpers | ||
//-------------------------------------------------------------------------- | ||
module.exports = function(context) { | ||
/** | ||
* Checks the body of a node to see if it's a block statement. Depending on | ||
* the rule options, reports the appropriate problems. | ||
* @param {ASTNode} node The node to report if there's a problem. | ||
* @param {ASTNode} body The body node to check for blocks. | ||
* @param {string} name The name to report if there's a problem. | ||
* @param {string} suffix Additional string to add to the end of a report. | ||
* @returns {void} | ||
*/ | ||
function checkBody(node, body, name, suffix) { | ||
var hasBlock = (body.type === "BlockStatement"); | ||
"use strict"; | ||
if (multiOnly) { | ||
if (hasBlock && body.body.length === 1) { | ||
context.report(node, "Unnecessary { after '{{name}}'{{suffix}}.", | ||
{ | ||
name: name, | ||
suffix: (suffix ? " " + suffix : "") | ||
} | ||
); | ||
} | ||
} else { | ||
if (!hasBlock) { | ||
context.report(node, "Expected { after '{{name}}'{{suffix}}.", | ||
{ | ||
name: name, | ||
suffix: (suffix ? " " + suffix : "") | ||
} | ||
); | ||
} | ||
} | ||
} | ||
//-------------------------------------------------------------------------- | ||
// Public | ||
//-------------------------------------------------------------------------- | ||
return { | ||
@@ -25,34 +61,20 @@ | ||
if (node.consequent.type !== "BlockStatement") { | ||
context.report(node, "Expected { after 'if' condition."); | ||
checkBody(node, node.consequent, "if", "condition"); | ||
if (node.alternate && node.alternate.type !== "IfStatement") { | ||
checkBody(node, node.alternate, "else"); | ||
} | ||
if (node.alternate && node.alternate.type !== "BlockStatement" && | ||
node.alternate.type !== "IfStatement") { | ||
context.report(node, "Expected { after 'else'."); | ||
} | ||
}, | ||
"WhileStatement": function(node) { | ||
if (node.body.type !== "BlockStatement") { | ||
context.report(node, "Expected { after 'while' condition."); | ||
} | ||
checkBody(node, node.body, "while", "condition"); | ||
}, | ||
"DoWhileStatement": function (node) { | ||
if (node.body.type !== "BlockStatement") { | ||
context.report(node, "Expected { after 'do'."); | ||
} | ||
checkBody(node, node.body, "do"); | ||
}, | ||
"ForStatement": function(node) { | ||
if (node.body.type !== "BlockStatement") { | ||
context.report(node, "Expected { after 'for' condition."); | ||
} | ||
checkBody(node, node.body, "for", "condition"); | ||
} | ||
@@ -59,0 +81,0 @@ }; |
@@ -60,16 +60,16 @@ /** | ||
"IfStatement:after": popBlock, | ||
"SwitchStatement:after": popBlock, | ||
"TryStatement:after": popBlock, | ||
"DoWhileStatement:after": popBlock, | ||
"WhileStatement:after": popBlock, | ||
"WithStatement:after": popBlock, | ||
"ForStatement:after": popBlock, | ||
"ForInStatement:after": popBlock, | ||
"IfStatement:exit": popBlock, | ||
"SwitchStatement:exit": popBlock, | ||
"TryStatement:exit": popBlock, | ||
"DoWhileStatement:exit": popBlock, | ||
"WhileStatement:exit": popBlock, | ||
"WithStatement:exit": popBlock, | ||
"ForStatement:exit": popBlock, | ||
"ForInStatement:exit": popBlock, | ||
"FunctionDeclaration:after": endFunction, | ||
"FunctionExpression:after": endFunction, | ||
"Program:after": endFunction | ||
"FunctionDeclaration:exit": endFunction, | ||
"FunctionExpression:exit": endFunction, | ||
"Program:exit": endFunction | ||
}; | ||
}; |
@@ -46,7 +46,3 @@ /** | ||
node.range[0] = 0; | ||
var lines = context.getSource(node); | ||
// getSource might return null if fed an empty string | ||
if(!lines) { | ||
return; | ||
} | ||
var lines = context.getSource(node) || ""; // necessary for backwards compat | ||
@@ -53,0 +49,0 @@ // Replace the tabs |
@@ -42,3 +42,3 @@ /** | ||
"FunctionExpression:after": function() { | ||
"FunctionExpression:exit": function() { | ||
callbackStack.pop(); | ||
@@ -49,2 +49,2 @@ } | ||
}; | ||
}; |
@@ -48,6 +48,6 @@ /** | ||
"FunctionDeclaration:after": endFunction, | ||
"FunctionExpression:after": endFunction | ||
"FunctionDeclaration:exit": endFunction, | ||
"FunctionExpression:exit": endFunction | ||
}; | ||
}; |
@@ -5,2 +5,3 @@ /** | ||
*/ | ||
"use strict"; | ||
@@ -13,4 +14,2 @@ //------------------------------------------------------------------------------ | ||
"use strict"; | ||
function isParenthesised(node) { | ||
@@ -20,2 +19,3 @@ var tokens = context.getTokens(node, 1, 1), | ||
lastToken = tokens[tokens.length - 1]; | ||
return firstToken.value === "(" && firstToken.range[1] <= node.range[0] && | ||
@@ -29,2 +29,3 @@ lastToken.value === ")" && lastToken.range[0] >= node.range[1]; | ||
lastToken = tokens[tokens.length - 1]; | ||
return isParenthesised(node) && | ||
@@ -36,3 +37,3 @@ firstToken.value === "(" && firstToken.range[1] <= node.range[0] && | ||
function testForAssign(node) { | ||
if ("AssignmentExpression" === node.test.type && !isParenthesisedTwice(node.test)) { | ||
if (node.test && (node.test.type === "AssignmentExpression") && !isParenthesisedTwice(node.test)) { | ||
context.report(node, "Expected a conditional expression and instead saw an assignment."); | ||
@@ -39,0 +40,0 @@ } |
@@ -24,3 +24,3 @@ /** | ||
"CatchClause:after": function() { | ||
"CatchClause:exit": function() { | ||
inCatch = false; | ||
@@ -27,0 +27,0 @@ exceptionName = null; |
@@ -75,3 +75,3 @@ /** | ||
"SwitchStatement:after": function() { | ||
"SwitchStatement:exit": function() { | ||
switches.pop(); | ||
@@ -78,0 +78,0 @@ } |
@@ -16,3 +16,3 @@ /** | ||
* | ||
* @returns {string[]} | ||
* @returns {string[]} An array of built-in Node.js modules. | ||
*/ | ||
@@ -45,3 +45,3 @@ function getBuiltinModules() { | ||
* @param {ASTNode} initExpression The init node of the VariableDeclarator. | ||
* @returns {string} | ||
* @returns {string} The type of declaration represented by the expression. | ||
*/ | ||
@@ -72,3 +72,3 @@ function getDeclarationType(initExpression) { | ||
* @param {ASTNode} initExpression The init node of the VariableDeclarator. | ||
* @returns {string} | ||
* @returns {string} The module type. | ||
*/ | ||
@@ -107,3 +107,3 @@ function inferModuleType(initExpression) { | ||
* @param {ASTNode} declarations The list of VariableDeclarators. | ||
* @returns {boolean} | ||
* @returns {boolean} True if the declarations are mixed, false if not. | ||
*/ | ||
@@ -128,3 +128,3 @@ function isMixed(declarations) { | ||
* @param {ASTNode} declarations The list of VariableDeclarators. | ||
* @returns {boolean} | ||
* @returns {boolean} True if the declarations are grouped, false if not. | ||
*/ | ||
@@ -131,0 +131,0 @@ function isGrouped(declarations) { |
@@ -17,2 +17,5 @@ /** | ||
"Literal": function(node) { | ||
if (typeof node.value !== "string") { | ||
return; | ||
} | ||
var match = node.raw.match(/^([^\\]|\\[^0-7])*\\([0-7])/), | ||
@@ -19,0 +22,0 @@ octalDigit; |
@@ -5,2 +5,3 @@ /** | ||
*/ | ||
"use strict"; | ||
@@ -13,4 +14,2 @@ //------------------------------------------------------------------------------ | ||
"use strict"; | ||
return { | ||
@@ -20,3 +19,3 @@ | ||
if (node.argument && node.argument.type === "AssignmentExpression") { | ||
context.report(node, "Return statement should not contain assigment."); | ||
context.report(node, "Return statement should not contain assignment."); | ||
} | ||
@@ -23,0 +22,0 @@ } |
@@ -38,21 +38,40 @@ /** | ||
/** | ||
* Checks if a node is an exception for no-unreachable because of variable/function hoisting | ||
* @param {ASTNode} node The AST node to check. | ||
* @returns {boolean} if the node doesn't trigger unreachable | ||
* @private | ||
*/ | ||
function isUnreachableAllowed(node) { | ||
return node.type === "FunctionDeclaration" || | ||
node.type === "VariableDeclaration" && | ||
node.declarations.every(function(declaration){ | ||
return declaration.type === "VariableDeclarator" && declaration.init === null; | ||
}); | ||
} | ||
/** | ||
* Loops through a field of a node and checks if its children fulfill conditions to trigger the unreachable report. | ||
* @param {ASTNode} node The AST node to check. | ||
* @param {string} field The field that represents the children of the node. | ||
* @returns {void} | ||
* @private | ||
*/ | ||
function checkNodeFieldForUnreachable(node, field) { | ||
var i, unreachableType = false; | ||
for (i = 1; i < node[field].length; i++) { | ||
unreachableType = unreachableType || checkForUnreachable(node[field][i - 1]); | ||
if (unreachableType && !isUnreachableAllowed(node[field][i])) { | ||
report(context, node[field][i], unreachableType); | ||
} | ||
} | ||
} | ||
return { | ||
"BlockStatement": function(node) { | ||
var i, unreachableType = false; | ||
for (i = 1; i < node.body.length; i++) { | ||
unreachableType = unreachableType || checkForUnreachable(node.body[i - 1]); | ||
if (unreachableType) { | ||
report(context, node.body[i], unreachableType); | ||
} | ||
} | ||
checkNodeFieldForUnreachable(node, "body"); | ||
}, | ||
"SwitchCase": function(node) { | ||
var i, unreachableType = false; | ||
for (i = 1; i < node.consequent.length; i++) { | ||
unreachableType = unreachableType || checkForUnreachable(node.consequent[i - 1]); | ||
if (unreachableType) { | ||
report(context, node.consequent[i], unreachableType); | ||
} | ||
} | ||
checkNodeFieldForUnreachable(node, "consequent"); | ||
} | ||
@@ -59,0 +78,0 @@ }; |
@@ -31,3 +31,3 @@ /** | ||
!/^(?:Assignment|Call|New|Update)Expression$/.test(type) && | ||
("UnaryExpression" !== type || ["delete", "void"].indexOf(node.expression.operator) < 0) && | ||
(type !== "UnaryExpression" || ["delete", "void"].indexOf(node.expression.operator) < 0) && | ||
!isPragma(node.expression, parent) | ||
@@ -34,0 +34,0 @@ ) { |
@@ -14,6 +14,26 @@ /** | ||
var allowUnusedGlobals = context.options[0] === "all" ? false : true; | ||
var allowUnusedGlobals = (context.options[0] !== "all"), | ||
variables = {}; | ||
var variables = []; | ||
function lookupVariableName(name) { | ||
// Convoluted check in case name is "hasOwnProperty" | ||
if (!Object.prototype.hasOwnProperty.call(variables, name)) { | ||
return null; | ||
} | ||
return variables[name]; | ||
} | ||
function lookupVariable(variable) { | ||
var candidates = lookupVariableName(variable.name); | ||
if (candidates) { | ||
return candidates.filter(function (candidate) { | ||
return variable.identifiers.some(function (identifier) { | ||
return candidate.node === identifier; | ||
}); | ||
})[0]; | ||
} | ||
return candidates; | ||
} | ||
function populateVariables(node) { | ||
@@ -33,7 +53,8 @@ var scope = context.getScope(), | ||
//make sure that this variable is not already in the array | ||
if (!variables.some(function(storedVariable) { | ||
return storedVariable.name === variable.name && storedVariable.node === variable.identifiers[0]; | ||
})) { | ||
if (!lookupVariable(variable)) { | ||
variables.push({ | ||
if (!lookupVariableName(variable.name)) { | ||
variables[variable.name] = []; | ||
} | ||
variables[variable.name].push({ | ||
name: variable.name, | ||
@@ -71,7 +92,4 @@ node: variable.identifiers[0], | ||
} | ||
return variables.filter(function(variable) { | ||
return variable.name === scopeVariable[0].name && scopeVariable[0].identifiers.some(function(identifier) { | ||
return variable.node === identifier; | ||
}); | ||
})[0]; | ||
return lookupVariable(scopeVariable[0]); | ||
} | ||
@@ -83,11 +101,4 @@ | ||
function findFirstAncestorThatIsFunctionExpressionOrDeclaration(ancestors) { | ||
var currentAncestor; | ||
while (ancestors.length !== 0 && !isFunction(currentAncestor)) { | ||
currentAncestor = ancestors.pop(); | ||
} | ||
return isFunction(currentAncestor) ? currentAncestor : undefined; | ||
} | ||
function markIgnorableUnusedVariables(usedVariable) { | ||
function markIgnorableUnusedVariables(usedVariable, ancestors) { | ||
/* When variables are declared as parameters in a FunctionExpression or | ||
@@ -100,6 +111,6 @@ * FunctionDeclaration, they can go unused so long as at least one | ||
// may be a parameter | ||
var ancestorFunctionNode = findFirstAncestorThatIsFunctionExpressionOrDeclaration(ancestors); | ||
if (ancestorFunctionNode) { | ||
var parent = usedVariable.node.parent; | ||
if (isFunction(parent)) { | ||
// Get a list of the param names used in the ancestor Function | ||
var fnParamNames = ancestorFunctionNode.params.map(function(param){ | ||
var fnParamNames = parent.params.map(function(param){ | ||
return param.name; | ||
@@ -116,6 +127,5 @@ }); | ||
ignorableVariables.forEach(function(ignorableVariable){ | ||
variables.forEach(function(variable){ | ||
if (variable.name === ignorableVariable) { | ||
variable.ignorable = true; | ||
} | ||
(lookupVariableName(ignorableVariable) || []).forEach(function(variable){ | ||
variable.ignorable = true; | ||
}); | ||
@@ -145,2 +155,3 @@ }); | ||
var variable = findVariable(node.name); | ||
if (variable) { | ||
@@ -154,9 +165,12 @@ variable.used = true; | ||
"Program:after": function() { | ||
var unused = variables.filter(function(variable) { | ||
return !variable.used && !variable.ignorable; | ||
}); | ||
unused.forEach(function(variable) { | ||
context.report(variable.node, "{{var}} is defined but never used", {"var": variable.name}); | ||
}); | ||
"Program:exit": function() { | ||
Object.keys(variables) | ||
.forEach(function (name) { | ||
variables[name].forEach(function (variable) { | ||
if (variable.used || variable.ignorable) { | ||
return; | ||
} | ||
context.report(variable.node, "{{var}} is defined but never used", {"var": variable.name}); | ||
}); | ||
}); | ||
} | ||
@@ -163,0 +177,0 @@ }; |
@@ -47,7 +47,7 @@ /** | ||
"Program:after": endFunction, | ||
"FunctionDeclaration:after": endFunction, | ||
"FunctionExpression:after": endFunction | ||
"Program:exit": endFunction, | ||
"FunctionDeclaration:exit": endFunction, | ||
"FunctionExpression:exit": endFunction | ||
}; | ||
}; |
@@ -35,7 +35,7 @@ /** | ||
* Validate that a string passed in is surrounded by the specified character | ||
* @param {string} val | ||
* @param {string} character | ||
* @return {bool} | ||
* @param {string} val The text to check. | ||
* @param {string} character The character to see if it's surrounded by. | ||
* @returns {boolean} True if the text is surrounded by the character, false if not. | ||
*/ | ||
function surroundedBy(val, character) { | ||
function isSurroundedBy(val, character) { | ||
return val[0] === character && val[val.length - 1] === character; | ||
@@ -55,6 +55,6 @@ } | ||
if (settings && typeof val === "string") { | ||
isValid = surroundedBy(rawVal, settings.quote); | ||
isValid = isSurroundedBy(rawVal, settings.quote); | ||
if (!isValid && avoidEscape) { | ||
isValid = surroundedBy(rawVal, settings.alternateQuote) && rawVal.indexOf(settings.quote) >= 0; | ||
isValid = isSurroundedBy(rawVal, settings.alternateQuote) && rawVal.indexOf(settings.quote) >= 0; | ||
} | ||
@@ -61,0 +61,0 @@ |
@@ -17,4 +17,20 @@ /** | ||
if (node.callee.name === "parseInt" && node.arguments.length === 1) { | ||
context.report(node, "Missing radix parameter."); | ||
var radix; | ||
if (node.callee.name === "parseInt") { | ||
if (node.arguments.length === 1) { | ||
context.report(node, "Missing radix parameter."); | ||
} else { | ||
radix = node.arguments[1]; | ||
// don't allow non-numeric literals or undefined | ||
if ((radix.type === "Literal" && typeof radix.value !== "number") || | ||
(radix.type === "Identifier" && radix.name === "undefined") | ||
) { | ||
context.report(node, "Invalid radix parameter."); | ||
} | ||
} | ||
} | ||
@@ -21,0 +37,0 @@ } |
@@ -18,2 +18,3 @@ /** | ||
* onto the stack. | ||
* @returns {void} | ||
* @private | ||
@@ -46,5 +47,5 @@ */ | ||
"Program:after": exitScope, | ||
"FunctionDeclaration:after": exitScope, | ||
"FunctionExpression:after": exitScope, | ||
"Program:exit": exitScope, | ||
"FunctionDeclaration:exit": exitScope, | ||
"FunctionExpression:exit": exitScope, | ||
@@ -51,0 +52,0 @@ "ExpressionStatement": function(node) { |
@@ -23,4 +23,4 @@ /** | ||
* Merges two config objects. This will not only add missing keys, but will also modify values to match. | ||
* @param {Object} initial config object | ||
* @param {Object} second config object. Overrides in this config object will take priority over base. | ||
* @param {Object} base config object | ||
* @param {Object} custom config object. Overrides in this config object will take priority over base. | ||
* @returns {Object} merged config object. | ||
@@ -27,0 +27,0 @@ */ |
{ | ||
"name": "eslint", | ||
"version": "0.3.0", | ||
"version": "0.4.0", | ||
"author": "Nicholas C. Zakas <nicholas+npm@nczconsulting.com>", | ||
@@ -17,3 +17,4 @@ "description": "An Esprima-based pattern checker for JavaScript.", | ||
"gensite": "node Makefile.js gensite", | ||
"browserify": "node Makefile.js browserify" | ||
"browserify": "node Makefile.js browserify", | ||
"profile": "beefy tests/bench/bench.js --open -- -t brfs -t ./tests/bench/xform-rules.js" | ||
}, | ||
@@ -32,3 +33,3 @@ "files": [ | ||
"dependencies": { | ||
"optimist": "*", | ||
"optionator": "~0.1.1", | ||
"estraverse": "~1.3.0", | ||
@@ -40,3 +41,5 @@ "esprima": "*", | ||
"chalk": "~0.4.0", | ||
"strip-json-comments": "~0.1.1" | ||
"strip-json-comments": "~0.1.1", | ||
"js-yaml": "~3.0.1", | ||
"doctrine": "~0.3.0" | ||
}, | ||
@@ -53,3 +56,7 @@ "devDependencies": { | ||
"mocha-phantomjs": "~3.3.1", | ||
"phantomjs": "~1.9.2-6" | ||
"phantomjs": "~1.9.2-6", | ||
"eslint-tester": "latest", | ||
"brfs": "0.0.9", | ||
"through": "~2.3.4", | ||
"beefy": "~1.0.0" | ||
}, | ||
@@ -56,0 +63,0 @@ "keywords": [ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Wildcard dependency
QualityPackage has a dependency with a floating version range. This can cause issues if the dependency publishes a new major version.
Found 1 instance in 1 package
226118
126
5757
1
10
14
+ Addeddoctrine@~0.3.0
+ Addedjs-yaml@~3.0.1
+ Addedoptionator@~0.1.1
+ Addedargparse@0.1.16(transitive)
+ Addeddeep-is@0.1.4(transitive)
+ Addeddoctrine@0.3.0(transitive)
+ Addedesprima@1.0.4(transitive)
+ Addedjs-yaml@3.0.2(transitive)
+ Addedlevenshtein-damerau@0.1.0(transitive)
+ Addedlevn@0.2.5(transitive)
+ Addedoptionator@0.1.1(transitive)
+ Addedprelude-ls@1.0.31.1.2(transitive)
+ Addedtype-check@0.3.2(transitive)
+ Addedunderscore@1.7.0(transitive)
+ Addedunderscore.string@2.4.0(transitive)
- Removedoptimist@*
- Removedminimist@0.0.10(transitive)
- Removedoptimist@0.6.1(transitive)