Comparing version 0.10.0-alpha.2 to 0.10.0
@@ -83,2 +83,3 @@ { | ||
"Element": false, | ||
"Event": false, | ||
"FileReader": false, | ||
@@ -421,2 +422,3 @@ "find": false, | ||
"pending": false, | ||
"runs": false, | ||
"spyOn": false, | ||
@@ -423,0 +425,0 @@ "waits": false, |
/** | ||
* @fileoverview Main CLI object. | ||
* @author Nicholas C. Zakas | ||
* @copyright 2014 Nicholas C. Zakas. All rights reserved. | ||
*/ | ||
@@ -45,2 +46,3 @@ | ||
* @property {string[]} globals An array of global variables to declare. | ||
* @property {string[]} extensions An array of file extensions to check. | ||
* @property {Object<string,*>} rules An object of rules to use. | ||
@@ -76,2 +78,3 @@ * @property {string} ignorePath The ignore file to use instead of .eslintignore. | ||
rules: {}, | ||
extensions: [".js"], | ||
ignore: true, | ||
@@ -215,4 +218,5 @@ ignorePath: null | ||
processed = [], | ||
configHelper = new Config(this.options), | ||
ignoredPaths = IgnoredPaths.load(this.options), | ||
options = this.options, | ||
configHelper = new Config(options), | ||
ignoredPaths = IgnoredPaths.load(options), | ||
exclude = ignoredPaths.contains.bind(ignoredPaths); | ||
@@ -222,3 +226,4 @@ | ||
files: files, | ||
exclude: this.options.ignore ? exclude : false | ||
extensions: options.extensions, | ||
exclude: options.ignore ? exclude : false | ||
}, function(filename) { | ||
@@ -228,13 +233,10 @@ | ||
if (path.extname(filename) === ".js") { | ||
processed.push(filename); | ||
results.push(processFile(filename, configHelper)); | ||
} | ||
processed.push(filename); | ||
results.push(processFile(filename, configHelper)); | ||
}); | ||
// only warn for files explicitly passes on the command line | ||
if (this.options.ignore) { | ||
// only warn for files explicitly passed on the command line | ||
if (options.ignore) { | ||
files.forEach(function(file) { | ||
if (path.extname(file) === ".js" && processed.indexOf(file) === -1) { | ||
if (fs.statSync(path.resolve(file)).isFile() && processed.indexOf(file) === -1) { | ||
results.push({ | ||
@@ -241,0 +243,0 @@ filePath: file, |
@@ -42,2 +42,3 @@ /** | ||
envs: cliOptions.env, | ||
extensions: cliOptions.ext, | ||
rules: cliOptions.rule, | ||
@@ -44,0 +45,0 @@ plugins: cliOptions.plugin, |
/** | ||
* @fileoverview Main ESLint object. | ||
* @author Nicholas C. Zakas | ||
* @copyright 2013 Nicholas C. Zakas. All rights reserved. | ||
*/ | ||
@@ -12,4 +11,4 @@ "use strict"; | ||
var esprima = require("esprima-fb"), | ||
estraverse = require("estraverse-fb"), | ||
var esprima = require("esprima"), | ||
estraverse = require("estraverse"), | ||
escope = require("escope"), | ||
@@ -20,8 +19,6 @@ environments = require("../conf/environments.json"), | ||
util = require("./util"), | ||
attachComments = require("./util/attach-comments"), | ||
Validator = require("./util/validator"), | ||
RuleContext = require("./rule-context"), | ||
timing = require("./timing"), | ||
EventEmitter = require("events").EventEmitter, | ||
createDebug = require("debug"); | ||
createTokenStore = require("./token-store.js"), | ||
EventEmitter = require("events").EventEmitter; | ||
@@ -32,4 +29,2 @@ //------------------------------------------------------------------------------ | ||
var debug = createDebug("eslint:linter"); | ||
function escapeRegExp(rx) { | ||
@@ -381,4 +376,3 @@ return rx.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"); | ||
commentLocsEnter = [], | ||
commentLocsExit = [], | ||
validator; | ||
commentLocsExit = []; | ||
@@ -401,4 +395,3 @@ /** | ||
try { | ||
debug("Parsing text"); | ||
var ast = esprima.parse(text, { | ||
return esprima.parse(text, { | ||
loc: true, | ||
@@ -408,10 +401,7 @@ range: true, | ||
tokens: true, | ||
comment: true | ||
comment: true, | ||
attachComment: true | ||
}); | ||
} catch (ex) { | ||
attachComments(ast, ast.comments, ast.tokens); | ||
return ast; | ||
} catch (ex) { | ||
debug("Parse failed: " + ex.message); | ||
messages.push({ | ||
@@ -521,3 +511,2 @@ fatal: true, | ||
commentLocsExit = []; | ||
validator = null; | ||
}; | ||
@@ -538,4 +527,3 @@ | ||
var ast, | ||
shebang, | ||
validatorOptions = {}; | ||
shebang; | ||
@@ -559,10 +547,2 @@ // set the current parsed filename | ||
// get settings information for ECMAScript version and JSX | ||
if (typeof config.settings.ecmascript === "number" && config.settings.ecmascript > 5) { | ||
validatorOptions.ecmascript = config.settings.ecmascript; | ||
} | ||
validatorOptions.jsx = !!config.settings.jsx; | ||
validator = new Validator(validatorOptions); | ||
// parse global comments and modify config | ||
@@ -635,9 +615,5 @@ modifyConfigsFromComments(ast, config, reportingConfig); | ||
/* get all tokens from the ast and store them as a hashtable to | ||
* improve traversal speed when wanting to find tokens for a given | ||
* node | ||
*/ | ||
currentTokens = []; | ||
ast.tokens.forEach(function(token) { | ||
currentTokens[token.range[0]] = token; | ||
currentTokens = createTokenStore(ast.tokens); | ||
Object.keys(currentTokens).forEach(function(method) { | ||
api[method] = currentTokens[method]; | ||
}); | ||
@@ -665,26 +641,6 @@ | ||
var comments = api.getComments(node), | ||
validation; | ||
var comments = api.getComments(node); | ||
debug("Starting traversal"); | ||
emitCommentsEnter(comments.leading); | ||
node.parent = parent; | ||
validation = validator.validate(node); | ||
if (!validation.valid) { | ||
messages.push({ | ||
fatal: true, | ||
severity: 2, | ||
message: validation.message, | ||
node: node, | ||
line: validation.line, | ||
column: validation.column | ||
}); | ||
// no further parsing should happen | ||
this.break(); | ||
} | ||
api.emit(node.type, node); | ||
@@ -869,173 +825,2 @@ emitCommentsEnter(comments.trailing); | ||
/** | ||
* Gets a number of tokens that precede a given node's tokens in the token stream. | ||
* @param {ASTNode} node The AST node. | ||
* @param {int} [beforeCount=0] The number of tokens before the node to retrieve. | ||
* @returns {[Token]} Array of objects representing tokens. | ||
*/ | ||
api.getTokensBefore = function(node, beforeCount) { | ||
var beforeTokens = [], cursor = node.range[0] - 1; | ||
while (beforeCount > 0 && cursor >= 0) { | ||
if (currentTokens[cursor]) { | ||
beforeTokens.unshift(currentTokens[cursor]); | ||
--beforeCount; | ||
} | ||
--cursor; | ||
} | ||
return beforeTokens; | ||
}; | ||
/** | ||
* Gets the token that precedes a given node's tokens in the token stream. | ||
* @param {ASTNode} node The AST node. | ||
* @param {int} [skip=0] A number of tokens to skip before the given node. | ||
* @returns {Token} An object representing the token. | ||
*/ | ||
api.getTokenBefore = function(node, skip) { | ||
for (var cursor = node.range[0] - 1; cursor >= 0; --cursor) { | ||
if (currentTokens[cursor]) { | ||
if (skip > 0) { | ||
--skip; | ||
} else { | ||
return currentTokens[cursor]; | ||
} | ||
} | ||
} | ||
}; | ||
/** | ||
* Gets a number of tokens that precede a given node's tokens in the token stream. | ||
* @param {ASTNode} node The AST node. | ||
* @param {int} [afterCount=0] The number of tokens after the node to retrieve. | ||
* @returns {[Token]} Array of objects representing tokens. | ||
*/ | ||
api.getTokensAfter = function(node, afterCount) { | ||
var afterTokens = [], cursor = node.range[1]; | ||
while (afterCount > 0 && cursor < currentTokens.length) { | ||
if (currentTokens[cursor]) { | ||
afterTokens.push(currentTokens[cursor]); | ||
--afterCount; | ||
cursor = currentTokens[cursor].range[1]; | ||
} else { | ||
++cursor; | ||
} | ||
} | ||
return afterTokens; | ||
}; | ||
/** | ||
* Gets the token that follows a given node's tokens in the token stream. | ||
* @param {ASTNode} node The AST node. | ||
* @param {int} [skip=0] A number of tokens to skip after the given node. | ||
* @returns {Token} An object representing the token. | ||
*/ | ||
api.getTokenAfter = function(node, skip) { | ||
for (var cursor = node.range[1]; cursor < currentTokens.length; ++cursor) { | ||
if (currentTokens[cursor]) { | ||
if (skip > 0) { | ||
--skip; | ||
} else { | ||
return currentTokens[cursor]; | ||
} | ||
} | ||
} | ||
}; | ||
/** | ||
* Gets all tokens that are related to the given node. | ||
* @param {ASTNode} node The AST node. | ||
* @param {int} [beforeCount=0] The number of tokens before the node to retrieve. | ||
* @param {int} [afterCount=0] The number of tokens after the node to retrieve. | ||
* @returns {[Token]} Array of objects representing tokens. | ||
*/ | ||
api.getTokens = function(node, beforeCount, afterCount) { | ||
var beforeTokens = api.getTokensBefore(node, beforeCount), | ||
afterTokens = api.getTokensAfter(node, afterCount), | ||
tokens = [], | ||
cursor = node.range[0]; | ||
while (cursor < node.range[1]) { | ||
if (currentTokens[cursor]) { | ||
tokens.push(currentTokens[cursor]); | ||
cursor = currentTokens[cursor].range[1]; | ||
} else { | ||
++cursor; | ||
} | ||
} | ||
return beforeTokens.concat(tokens, afterTokens); | ||
}; | ||
/** | ||
* Gets the first `count` tokens of the given node's token stream. | ||
* @param {ASTNode} node The AST node. | ||
* @param {int} [count=0] The number of tokens of the node to retrieve. | ||
* @returns {[Token]} Array of objects representing tokens. | ||
*/ | ||
api.getFirstTokens = function(node, count) { | ||
var tokens = [], cursor = node.range[0]; | ||
while (count > 0 && cursor < node.range[1]) { | ||
if (currentTokens[cursor]) { | ||
tokens.push(currentTokens[cursor]); | ||
--count; | ||
cursor = currentTokens[cursor].range[1]; | ||
} else { | ||
++cursor; | ||
} | ||
} | ||
return tokens; | ||
}; | ||
/** | ||
* Gets the first token of the given node's token stream. | ||
* @param {ASTNode} node The AST node. | ||
* @param {int} [skip=0] A number of tokens to skip. | ||
* @returns {Token} An object representing the token. | ||
*/ | ||
api.getFirstToken = function(node, skip) { | ||
for (var cursor = node.range[0]; cursor < node.range[1]; ++cursor) { | ||
if (currentTokens[cursor]) { | ||
if (skip > 0) { | ||
--skip; | ||
} else { | ||
return currentTokens[cursor]; | ||
} | ||
} | ||
} | ||
}; | ||
/** | ||
* Gets the last `count` tokens of the given node. | ||
* @param {ASTNode} node The AST node. | ||
* @param {int} [count=0] The number of tokens of the node to retrieve. | ||
* @returns {[Token]} Array of objects representing tokens. | ||
*/ | ||
api.getLastTokens = function(node, count) { | ||
var tokens = [], cursor = node.range[1] - 1; | ||
while (count > 0 && cursor >= node.range[0]) { | ||
if (currentTokens[cursor]) { | ||
tokens.unshift(currentTokens[cursor]); | ||
--count; | ||
} | ||
--cursor; | ||
} | ||
return tokens; | ||
}; | ||
/** | ||
* Gets the last token of the given node's token stream. | ||
* @param {ASTNode} node The AST node. | ||
* @param {int} [skip=0] A number of tokens to skip. | ||
* @returns {Token} An object representing the token. | ||
*/ | ||
api.getLastToken = function(node, skip) { | ||
for (var cursor = node.range[1] - 1; cursor >= node.range[0]; --cursor) { | ||
if (currentTokens[cursor]) { | ||
if (skip > 0) { | ||
--skip; | ||
} else { | ||
return currentTokens[cursor]; | ||
} | ||
} | ||
} | ||
}; | ||
/** | ||
* Gets nodes that are ancestors of current node. | ||
@@ -1042,0 +827,0 @@ * @returns {ASTNode[]} Array of objects representing ancestors. |
@@ -15,2 +15,23 @@ /** | ||
//------------------------------------------------------------------------------ | ||
// Helpers | ||
//------------------------------------------------------------------------------ | ||
/** | ||
* Returns an array of entries for a directory. If there's an error, then an | ||
* empty array is returned instead. The try-catch is moved out here to avoid | ||
* performance penalty of having it in the findInDirectory() method. | ||
* @param {string} directory The directory to search in. | ||
* @returns {string[]} The filenames in the directory or an empty array on error. | ||
* @private | ||
*/ | ||
function getDirectoryEntries(directory) { | ||
try { | ||
return fs.readdirSync(directory); | ||
} catch (ex) { | ||
return []; | ||
} | ||
} | ||
//------------------------------------------------------------------------------ | ||
// API | ||
@@ -54,4 +75,5 @@ //------------------------------------------------------------------------------ | ||
files = fs.readdirSync(lookInDirectory); | ||
files = getDirectoryEntries(lookInDirectory); | ||
if (files.indexOf(lookFor) !== -1) { | ||
@@ -58,0 +80,0 @@ isFound = true; |
@@ -63,2 +63,7 @@ /** | ||
}, { | ||
option: "ext", | ||
type: "[String]", | ||
default: ".js", | ||
description: "Specify JavaScript file extensions." | ||
}, { | ||
option: "plugin", | ||
@@ -65,0 +70,0 @@ type: "[String]", |
@@ -23,2 +23,3 @@ /** | ||
"getLastToken", | ||
"getTokensBetween", | ||
"getComments", | ||
@@ -25,0 +26,0 @@ "getAncestors", |
@@ -142,2 +142,8 @@ /** | ||
validateCommaSpacing(node, "params"); | ||
}, | ||
"CallExpression": function(node) { | ||
validateCommaSpacing(node, "arguments"); | ||
}, | ||
"NewExpression": function(node) { | ||
validateCommaSpacing(node, "arguments"); | ||
} | ||
@@ -144,0 +150,0 @@ }; |
@@ -56,3 +56,3 @@ /** | ||
function getOperatorLocation(node) { | ||
var opToken = context.getTokens(node)[context.getTokens(node.left).length]; | ||
var opToken = context.getTokenAfter(node.left); | ||
return {line: opToken.loc.start.line, column: opToken.loc.start.column}; | ||
@@ -59,0 +59,0 @@ } |
@@ -10,2 +10,4 @@ /** | ||
var CAPS_ALLOWED = [ | ||
"Object", | ||
"Function", | ||
"Number", | ||
@@ -12,0 +14,0 @@ "String", |
@@ -19,10 +19,14 @@ /** | ||
function checkForTrailingComma(node) { | ||
var secondToLastToken = context.getLastTokens(node, 2)[0]; | ||
var items = node.properties || node.elements, | ||
length = items.length, | ||
lastItem, penultimateToken; | ||
var items = node.properties || node.elements, | ||
lastItem = items[items.length - 1]; | ||
// The last token in an object/array literal will always be a closing | ||
// curly, so we check the second to last token for a comma. | ||
if (secondToLastToken.value === "," && items.length && lastItem) { | ||
context.report(lastItem, secondToLastToken.loc.start, "Trailing comma."); | ||
if (length) { | ||
lastItem = items[length - 1]; | ||
if (lastItem) { | ||
penultimateToken = context.getLastToken(node, 1); | ||
if (penultimateToken.value === ",") { | ||
context.report(lastItem, penultimateToken.loc.start, "Trailing comma."); | ||
} | ||
} | ||
} | ||
@@ -29,0 +33,0 @@ } |
/** | ||
* @fileoverview Rule to flag assignment in a conditional expression | ||
* @fileoverview Rule to flag assignment in a conditional statement's test expression | ||
* @author Stephen Murray <spmurrayzzz> | ||
@@ -7,2 +7,9 @@ */ | ||
var NODE_DESCRIPTIONS = { | ||
"DoWhileStatement": "a 'do...while' statement", | ||
"ForStatement": "a 'for' statement", | ||
"IfStatement": "an 'if' statement", | ||
"WhileStatement": "a 'while' statement" | ||
}; | ||
//------------------------------------------------------------------------------ | ||
@@ -14,2 +21,37 @@ // Rule Definition | ||
var prohibitAssign = (context.options[0] || "except-parens"); | ||
/** | ||
* Check whether an AST node is the test expression for a conditional statement. | ||
* @param {!Object} node The node to test. | ||
* @returns {boolean} `true` if the node is the text expression for a conditional statement; otherwise, `false`. | ||
*/ | ||
function isConditionalTestExpression(node) { | ||
return node.parent && | ||
node.parent.test && | ||
node === node.parent.test; | ||
} | ||
/** | ||
* Given an AST node, perform a bottom-up search for the first ancestor that represents a conditional statement. | ||
* @param {!Object} node The node to use at the start of the search. | ||
* @returns {?Object} The closest ancestor node that represents a conditional statement. | ||
*/ | ||
function findConditionalAncestor(node) { | ||
var currentAncestor = node; | ||
while ((currentAncestor = currentAncestor.parent)) { | ||
if (isConditionalTestExpression(currentAncestor)) { | ||
return currentAncestor.parent; | ||
} | ||
} | ||
return null; | ||
} | ||
/** | ||
* Check whether the code represented by an AST node is enclosed in parentheses. | ||
* @param {!Object} node The node to test. | ||
* @returns {boolean} `true` if the code is enclosed in parentheses; otherwise, `false`. | ||
*/ | ||
function isParenthesised(node) { | ||
@@ -23,2 +65,7 @@ var previousToken = context.getTokenBefore(node), | ||
/** | ||
* Check whether the code represented by an AST node is enclosed in two sets of parentheses. | ||
* @param {!Object} node The node to test. | ||
* @returns {boolean} `true` if the code is enclosed in two sets of parentheses; otherwise, `false`. | ||
*/ | ||
function isParenthesisedTwice(node) { | ||
@@ -33,4 +80,10 @@ var previousToken = context.getTokenBefore(node, 1), | ||
/** | ||
* Check a conditional statement's test expression for top-level assignments that are not enclosed in parentheses. | ||
* @param {!Object} node The node for the conditional statement. | ||
* @returns {void} | ||
*/ | ||
function testForAssign(node) { | ||
if (node.test && (node.test.type === "AssignmentExpression") && !isParenthesisedTwice(node.test)) { | ||
// must match JSHint's error message | ||
context.report(node, "Expected a conditional expression and instead saw an assignment."); | ||
@@ -40,9 +93,30 @@ } | ||
/** | ||
* Check whether an assignment expression is descended from a conditional statement's test expression. | ||
* @param {!Object} node The node for the assignment expression. | ||
* @returns {void} | ||
*/ | ||
function testForConditionalAncestor(node) { | ||
var ancestor = findConditionalAncestor(node); | ||
if (ancestor) { | ||
context.report(ancestor, "Unexpected assignment within {{type}}.", { | ||
type: NODE_DESCRIPTIONS[ancestor.type] || ancestor.type | ||
}); | ||
} | ||
} | ||
if (prohibitAssign === "always") { | ||
return { | ||
"AssignmentExpression": testForConditionalAncestor | ||
}; | ||
} | ||
return { | ||
"DoWhileStatement": testForAssign, | ||
"ForStatement": testForAssign, | ||
"IfStatement": testForAssign, | ||
"WhileStatement": testForAssign, | ||
"DoWhileStatement": testForAssign, | ||
"ForStatement": testForAssign | ||
"WhileStatement": testForAssign | ||
}; | ||
}; |
@@ -27,17 +27,17 @@ /** | ||
switch (node.type) { | ||
case "Literal": | ||
case "FunctionExpression": | ||
case "ObjectExpression": | ||
case "ArrayExpression": | ||
return true; | ||
case "UnaryExpression": | ||
return isConstant(node.argument); | ||
case "BinaryExpression": | ||
case "LogicalExpression": | ||
return isConstant(node.left) && isConstant(node.right); | ||
case "AssignmentExpression": | ||
return isConstant(node.right); | ||
case "SequenceExpression": | ||
return isConstant(node.expressions[node.expressions.length - 1]); | ||
// no default | ||
case "Literal": | ||
case "FunctionExpression": | ||
case "ObjectExpression": | ||
case "ArrayExpression": | ||
return true; | ||
case "UnaryExpression": | ||
return isConstant(node.argument); | ||
case "BinaryExpression": | ||
case "LogicalExpression": | ||
return isConstant(node.left) && isConstant(node.right); | ||
case "AssignmentExpression": | ||
return isConstant(node.right); | ||
case "SequenceExpression": | ||
return isConstant(node.expressions[node.expressions.length - 1]); | ||
// no default | ||
} | ||
@@ -44,0 +44,0 @@ return false; |
@@ -17,12 +17,6 @@ /** | ||
"Literal": function(node) { | ||
var token = context.getFirstTokens(node, 1)[0], | ||
nodeType = token.type, | ||
source; | ||
var token = context.getFirstToken(node); | ||
if (nodeType === "RegularExpression") { | ||
source = context.getFirstTokens(node, 1)[0].value; | ||
if (source[1] === "=") { | ||
context.report(node, "A regular expression literal can be confused with '/='."); | ||
} | ||
if (token.type === "RegularExpression" && token.value[1] === "=") { | ||
context.report(node, "A regular expression literal can be confused with '/='."); | ||
} | ||
@@ -29,0 +23,0 @@ } |
@@ -7,2 +7,20 @@ /** | ||
//------------------------------------------------------------------------------ | ||
// Helpers | ||
//------------------------------------------------------------------------------ | ||
/* | ||
plain-English description of the following regexp: | ||
0. `^` fix the match at the beginning of the string | ||
1. `\/`: the `/` that begins the regexp | ||
2. `([^\\[]|\\.|\[([^\\\]]|\\.)+\])*`: regexp contents; 0 or more of the following | ||
2.0. `[^\\[]`: any character that's not a `\` or a `[` (anything but escape sequences and character classes) | ||
2.1. `\\.`: an escape sequence | ||
2.2. `\[([^\\\]]|\\.)+\]`: a character class that isn't empty | ||
3. `\/` the `/` that ends the regexp | ||
4. `[gimy]*`: optional regexp flags | ||
5. `$`: fix the match at the end of the string | ||
*/ | ||
var regex = /^\/([^\\[]|\\.|\[([^\\\]]|\\.)+\])*\/[gimy]*$/; | ||
//------------------------------------------------------------------------------ | ||
// Rule Definition | ||
@@ -18,21 +36,6 @@ //------------------------------------------------------------------------------ | ||
"Literal": function(node) { | ||
var tokens = context.getTokens(node); | ||
tokens.forEach(function (token) { | ||
/* | ||
plain-English description of the following regexp: | ||
0. `^` fix the match at the beginning of the string | ||
1. `\/`: the `/` that begins the regexp | ||
2. `([^\\[]|\\.|\[([^\\\]]|\\.)+\])*`: regexp contents; 0 or more of the following | ||
2.0. `[^\\[]`: any character that's not a `\` or a `[` (anything but escape sequences and character classes) | ||
2.1. `\\.`: an escape sequence | ||
2.2. `\[([^\\\]]|\\.)+\]`: a character class that isn't empty | ||
3. `\/` the `/` that ends the regexp | ||
4. `[gimy]*`: optional regexp flags | ||
5. `$`: fix the match at the end of the string | ||
*/ | ||
if (token.type === "RegularExpression" && | ||
!/^\/([^\\[]|\\.|\[([^\\\]]|\\.)+\])*\/[gimy]*$/.test(token.value)) { | ||
context.report(node, "Empty class."); | ||
} | ||
}); | ||
var token = context.getFirstToken(node); | ||
if (token.type === "RegularExpression" && !regex.test(token.value)) { | ||
context.report(node, "Empty class."); | ||
} | ||
} | ||
@@ -39,0 +42,0 @@ |
@@ -43,4 +43,11 @@ /** | ||
// unless the user doesn't like semicolons, in which case it's first leading comment of this case | ||
if (!comment) { | ||
comments = context.getComments(node).leading; | ||
comment = comments[comments.length - 1]; | ||
} | ||
// check for comment | ||
if (!comment || !FALLTHROUGH_COMMENT.test(comment.value)) { | ||
context.report(switchData.lastCase, | ||
@@ -47,0 +54,0 @@ "Expected a \"break\" statement before \"{{code}}\".", |
@@ -26,3 +26,3 @@ /** | ||
var preamble = startLine.slice(0,node.loc.start.column).trim(); | ||
var preamble = startLine.slice(0, node.loc.start.column).trim(); | ||
@@ -29,0 +29,0 @@ // Also check after the comment |
@@ -5,2 +5,3 @@ /** | ||
* @copyright 2014 Vignesh Anand. All rights reserved. | ||
* @copyright 2014 Brandon Mills. All rights reserved. | ||
*/ | ||
@@ -10,2 +11,50 @@ "use strict"; | ||
//------------------------------------------------------------------------------ | ||
// Helpers | ||
//------------------------------------------------------------------------------ | ||
var OPERATORS = { | ||
"Punctuator": { | ||
"*": true, "/": true, "%": true, "+": true, "-": true, "<<": true, | ||
">>": true, ">>>": true, "<": true, "<=": true, ">": true, ">=": true, | ||
"==": true, "!=": true, "===": true, "!==": true, "&": true, "^": true, | ||
"|": true, "&&": true, "||": true, "=": true, "+=": true, "-=": true, | ||
"*=": true, "/=": true, "%=": true, "<<=": true, ">>=": true, | ||
">>>=": true, "&=": true, "^=": true, "|=": true, "?": true, ":": true, | ||
",": true | ||
}, | ||
"Keyword": { | ||
"in": true, | ||
"instanceof": true | ||
} | ||
}; | ||
/** | ||
* Determines whether a token is a binary operator. | ||
* @param {Token} token The token. | ||
* @returns {Boolean} Whether the token is a binary operator. | ||
*/ | ||
function isOperator(token) { | ||
var type = OPERATORS[token.type]; | ||
return (type && type[token.value]) || false; | ||
} | ||
/** | ||
* Gets the index of the first element of an array that satisfies a predicate. | ||
* Like Array.prototype.indexOf(), but with a predicate instead of equality. | ||
* @param {Array} array An array to search. | ||
* @param {Function} predicate A function returning true if its argument | ||
* satisfies a condition. | ||
* @returns {Number} The index of the first element that satisifies | ||
* the predicate, or -1 if no elements match. | ||
*/ | ||
function findIndex(array, predicate) { | ||
for (var i = 0, l = array.length; i < l; i++) { | ||
if (predicate(array[i])) { | ||
return i; | ||
} | ||
} | ||
return -1; | ||
} | ||
//------------------------------------------------------------------------------ | ||
// Rule Definition | ||
@@ -16,76 +65,38 @@ //------------------------------------------------------------------------------ | ||
var OPERATORS = [ | ||
"*", "/", "%", "+", "-", "<<", ">>", ">>>", "<", "<=", ">", ">=", "in", | ||
"instanceof", "==", "!=", "===", "!==", "&", "^", "|", "&&", "||", "=", | ||
"+=", "-=", "*=", "/=", "%=", "<<=", ">>=", ">>>=", "&=", "^=", "|=", | ||
"?", ":", "," | ||
], errOps = []; | ||
//-------------------------------------------------------------------------- | ||
// Helpers | ||
//-------------------------------------------------------------------------- | ||
/** | ||
* Reports an AST node as a rule violation. | ||
* @param {ASTNode} node The node to report. | ||
* Validates that two adjacent tokens are separated by less than two spaces. | ||
* @param {Token} left Token on the left side. | ||
* @param {Token} right Token on the right side. | ||
* @returns {void} | ||
* @private | ||
*/ | ||
function report(node) { | ||
context.report(node, "Multiple spaces found around '" + errOps.shift() + "'."); | ||
} | ||
function validate(left, right) { | ||
var op; | ||
/** | ||
* Checks whether the operator is in same line as two adjacent tokens. | ||
* @param {ASTNode} left The token left to the operator. | ||
* @param {ASTNode} right The token right to the operator. | ||
* @param {ASTNode} operator The operator. | ||
* @returns {boolean} Whether the operator is in same line as two adjacent tokens. | ||
* @private | ||
*/ | ||
function isSameLine(left, right, operator) { | ||
return operator.loc.end.line === left.loc.end.line && | ||
operator.loc.end.line === right.loc.start.line; | ||
if ( | ||
left.loc.end.line === right.loc.start.line && | ||
right.range[0] - left.range[1] > 1 | ||
) { | ||
op = isOperator(left) ? left : right; | ||
context.report(op, "Multiple spaces found {{side}} '{{op}}'.", { | ||
side: op === right ? "before" : "after", | ||
op: op.value | ||
}); | ||
} | ||
} | ||
/** | ||
* Check whether there are multiple spaces around the operator. | ||
* @param {ASTNode} left The token left to the operator. | ||
* @param {ASTNode} right The token right to the operator. | ||
* @param {ASTNode} operator The operator. | ||
* @returns {boolean} Whether there are multiple spaces. | ||
* @private | ||
* Validates the spacing of two nodes with an operator between them. | ||
* @param {ASTNode} left Node left of the operator. | ||
* @param {ASTNode} right Node right of the operator. | ||
* @returns {void} | ||
*/ | ||
function isMultiSpaced(left, right, operator) { | ||
return operator.range[0] - left.range[1] > 1 || | ||
right.range[0] - operator.range[1] > 1; | ||
} | ||
function validateBetween(left, right) { | ||
var tokens = context.getTokensBetween(left, right, 1), | ||
operatorIndex = findIndex(tokens, isOperator), | ||
op = tokens[operatorIndex]; | ||
/** | ||
* Get tokens and validate the spacing. | ||
* @param {ASTNode} left The token left to the operator. | ||
* @param {ASTNode} right The token right to the operator. | ||
* @returns {boolean} Whether multiple spaces were found. | ||
* @private | ||
*/ | ||
function validateSpacing(left, right) { | ||
var tokens = context.getTokens({range: [left.range[1], right.range[0]]}, 1, 1), | ||
operator; | ||
for (var i = 1, l = tokens.length - 1; i < l; ++i) { | ||
left = tokens[i - 1]; | ||
operator = tokens[i]; | ||
right = tokens[i + 1]; | ||
if (operator && operator.type === "Punctuator" && OPERATORS.indexOf(operator.value) >= 0 && | ||
isSameLine(left, right, operator) && isMultiSpaced(left, right, operator)) { | ||
errOps.push(operator.value); | ||
return true; | ||
} | ||
} | ||
return false; | ||
validate(tokens[operatorIndex - 1], op); | ||
validate(op, tokens[operatorIndex + 1]); | ||
} | ||
/** | ||
@@ -98,5 +109,3 @@ * Report if there are multiple spaces on the expression. | ||
function checkExpression(node) { | ||
if (validateSpacing(node.left, node.right)) { | ||
report(node); | ||
} | ||
validateBetween(node.left, node.right); | ||
} | ||
@@ -111,8 +120,4 @@ | ||
function checkConditional(node) { | ||
if (validateSpacing(node.test, node.consequent)) { | ||
report(node); | ||
} | ||
if (validateSpacing(node.consequent, node.alternate)) { | ||
report(node); | ||
} | ||
validateBetween(node.test, node.consequent); | ||
validateBetween(node.consequent, node.alternate); | ||
} | ||
@@ -127,4 +132,4 @@ | ||
function checkVar(node) { | ||
if (node.init && validateSpacing(node.id, node.init)) { | ||
report(node); | ||
if (node.init) { | ||
validateBetween(node.id, node.init); | ||
} | ||
@@ -134,22 +139,62 @@ } | ||
/** | ||
* Report if there are multiple spaces around list of items in objects, arrays, | ||
* function parameters, sequences and declarations. | ||
* @param {ASTNode} node The node to check. | ||
* @param {string} property The property of node. | ||
* Generates a function to check object literals and other list-like nodes. | ||
* @param {String} property Name of the node's collection property. | ||
* @returns {Function} A function that checks spacing within list-like node. | ||
* @private | ||
*/ | ||
function checkList(property) { | ||
/** | ||
* Report if there are multiple spaces around list of items in objects, | ||
* function parameters, sequences and declarations. | ||
* @param {ASTNode} node The node to check. | ||
* @returns {void} | ||
* @private | ||
*/ | ||
return function(node) { | ||
var items = node[property]; | ||
for (var i = 0, l = items.length; i < l - 1; i++) { | ||
validateBetween(items[i], items[i + 1]); | ||
} | ||
}; | ||
} | ||
/** | ||
* Checks arrays literals, allowing that some elements may be empty. | ||
* @param {ASTNode} node An ArrayExpression. | ||
* @returns {void} | ||
* @private | ||
*/ | ||
function checkList(node, property) { | ||
var items = node[property]; | ||
function checkArray(node) { | ||
var elements = node.elements, | ||
elementIndex = 0, | ||
tokens = context.getTokens(node), | ||
tokenIndex = 0, | ||
length = tokens.length, | ||
dest; | ||
for (var i = 0, l = items.length; i < l; i++) { | ||
var left = items[i - 1], | ||
right = items[i], | ||
operator = context.getTokenBefore(right); | ||
// Check spacing at the beginning, around commas, and at the end | ||
while (tokenIndex < length - 1) { | ||
validate(tokens[tokenIndex], tokens[++tokenIndex]); | ||
if (operator && operator.type === "Punctuator" && operator.value === ",") { | ||
if (isSameLine(left, right, operator) && isMultiSpaced(left, right, operator)) { | ||
errOps.push(operator.value); | ||
report(right); | ||
// Don't advance to the next element until both sides of the comma | ||
// have been checked | ||
if (tokens[tokenIndex].value !== ",") { | ||
// Skip to just before the end of the array or the next comma | ||
if (elements[elementIndex]) { | ||
dest = elements[elementIndex].range[1]; | ||
// Move to the end of the element | ||
while (tokens[tokenIndex].range[1] < dest) { | ||
++tokenIndex; | ||
} | ||
// Move past anything (like closing parens) between the end | ||
// of the node and the following comma or end brace. | ||
while ( | ||
tokenIndex + 1 < tokens.length && | ||
tokens[tokenIndex + 1].value !== "," | ||
) { | ||
++tokenIndex; | ||
} | ||
} | ||
++elementIndex; | ||
} | ||
@@ -169,22 +214,10 @@ } | ||
"VariableDeclarator": checkVar, | ||
"ArrayExpression": function(node) { | ||
checkList(node, "elements"); | ||
}, | ||
"ObjectExpression": function(node) { | ||
checkList(node, "properties"); | ||
}, | ||
"SequenceExpression": function(node) { | ||
checkList(node, "expressions"); | ||
}, | ||
"FunctionExpression": function(node) { | ||
checkList(node, "params"); | ||
}, | ||
"FunctionDeclaration": function(node) { | ||
checkList(node, "params"); | ||
}, | ||
"VariableDeclaration": function(node) { | ||
checkList(node, "declarations"); | ||
} | ||
"ArrayExpression": checkArray, | ||
"ObjectExpression": checkList("properties"), | ||
"SequenceExpression": checkList("expressions"), | ||
"FunctionExpression": checkList("params"), | ||
"FunctionDeclaration": checkList("params"), | ||
"VariableDeclaration": checkList("declarations") | ||
}; | ||
}; |
@@ -41,3 +41,3 @@ /** | ||
lastLocation = currentLocation; | ||
currentLocation = trimmedLines.indexOf("",currentLocation + 1); | ||
currentLocation = trimmedLines.indexOf("", currentLocation + 1); | ||
if (lastLocation === currentLocation - 1) { | ||
@@ -44,0 +44,0 @@ blankCounter++; |
@@ -17,3 +17,3 @@ /** | ||
"Literal": function(node) { | ||
var token = context.getFirstTokens(node, 1)[0], | ||
var token = context.getFirstToken(node), | ||
nodeType = token.type, | ||
@@ -20,0 +20,0 @@ nodeValue = token.value, |
@@ -38,2 +38,12 @@ /** | ||
/** | ||
* Checks if the given node is the argument of a typeof operator. | ||
* @param {ASTNode} node The AST node being checked. | ||
* @returns {boolean} Whether or not the node is the argument of a typeof operator. | ||
*/ | ||
function hasTypeOfOperator(node) { | ||
var parent = node.parent; | ||
return parent.type === "UnaryExpression" && parent.operator === "typeof"; | ||
} | ||
//------------------------------------------------------------------------------ | ||
@@ -47,3 +57,3 @@ // Rule Definition | ||
"Program": function(/*node*/) { | ||
"Program:exit": function(/*node*/) { | ||
@@ -55,2 +65,7 @@ var globalScope = context.getScope(); | ||
name = ref.identifier.name; | ||
if (hasTypeOfOperator(ref.identifier)) { | ||
return; | ||
} | ||
if (!variable) { | ||
@@ -57,0 +72,0 @@ context.report(ref.identifier, "'{{name}}' is not defined.", { name: name }); |
/** | ||
* @fileoverview Rule to flag wrapping none-iffe in parens | ||
* @fileoverview Rule to flag wrapping non-iife in parens | ||
* @author Ilya Volodin | ||
@@ -4,0 +4,0 @@ */ |
@@ -65,8 +65,8 @@ /** | ||
* @param {ASTNode} node - The node to report in the event of an error. | ||
* @param {Object[]} tokens - The tokens to be checked for spacing. | ||
* @param {Token} token - The token to use for the report. | ||
* @returns {void} | ||
*/ | ||
function reportNoBeginningSpace(node, tokens) { | ||
context.report(node, tokens[0].loc.start, | ||
"There should be no space after '" + tokens[0].value + "'"); | ||
function reportNoBeginningSpace(node, token) { | ||
context.report(node, token.loc.start, | ||
"There should be no space after '" + token.value + "'"); | ||
} | ||
@@ -77,8 +77,8 @@ | ||
* @param {ASTNode} node - The node to report in the event of an error. | ||
* @param {Object[]} tokens - The tokens to be checked for spacing. | ||
* @param {Token} token - The token to use for the report. | ||
* @returns {void} | ||
*/ | ||
function reportNoEndingSpace(node, tokens) { | ||
context.report(node, tokens[tokens.length - 1].loc.start, | ||
"There should be no space before '" + tokens[tokens.length - 1].value + "'"); | ||
function reportNoEndingSpace(node, token) { | ||
context.report(node, token.loc.start, | ||
"There should be no space before '" + token.value + "'"); | ||
} | ||
@@ -89,8 +89,8 @@ | ||
* @param {ASTNode} node - The node to report in the event of an error. | ||
* @param {Object[]} tokens - The tokens to be checked for spacing. | ||
* @param {Token} token - The token to use for the report. | ||
* @returns {void} | ||
*/ | ||
function reportRequiredBeginningSpace(node, tokens) { | ||
context.report(node, tokens[0].loc.start, | ||
"A space is required after '" + tokens[0].value + "'"); | ||
function reportRequiredBeginningSpace(node, token) { | ||
context.report(node, token.loc.start, | ||
"A space is required after '" + token.value + "'"); | ||
} | ||
@@ -101,8 +101,8 @@ | ||
* @param {ASTNode} node - The node to report in the event of an error. | ||
* @param {Object[]} tokens - The tokens to be checked for spacing. | ||
* @param {Token} token - The token to use for the report. | ||
* @returns {void} | ||
*/ | ||
function reportRequiredEndingSpace(node, tokens) { | ||
context.report(node, tokens[tokens.length - 1].loc.start, | ||
"A space is required before '" + tokens[tokens.length - 1].value + "'"); | ||
function reportRequiredEndingSpace(node, token) { | ||
context.report(node, token.loc.start, | ||
"A space is required before '" + token.value + "'"); | ||
} | ||
@@ -117,27 +117,30 @@ | ||
MemberExpression: function checkMember(node) { | ||
MemberExpression: function(node) { | ||
if (!node.computed) { | ||
return; | ||
} | ||
var tokens = context.getTokens(node.property, 1, 1); | ||
var tokenA = tokens[0], tokenB = tokens[1], | ||
tokenC = tokens[tokens.length - 2], tokenD = tokens[tokens.length - 1]; | ||
var property = node.property, | ||
before = context.getTokenBefore(property), | ||
first = context.getFirstToken(property), | ||
last = context.getLastToken(property), | ||
after = context.getTokenAfter(property); | ||
var propertyNameMustBeSpaced = options.propertyNameException ? | ||
!options.spaced : options.spaced; | ||
if (isSameLine(tokenA, tokenB) || isSameLine(tokenC, tokenD)) { | ||
if (isSameLine(before, first) || isSameLine(last, after)) { | ||
if (propertyNameMustBeSpaced) { | ||
if (!isSpaced(tokenA, tokenB) && isSameLine(tokenA, tokenB)) { | ||
reportRequiredBeginningSpace(node, tokens); | ||
if (!isSpaced(before, first) && isSameLine(before, first)) { | ||
reportRequiredBeginningSpace(node, before); | ||
} | ||
if (!isSpaced(tokenC, tokenD) && isSameLine(tokenC, tokenD)) { | ||
reportRequiredEndingSpace(node, tokens); | ||
if (!isSpaced(last, after) && isSameLine(last, after)) { | ||
reportRequiredEndingSpace(node, after); | ||
} | ||
} else { | ||
if (isSpaced(tokenA, tokenB)) { | ||
reportNoBeginningSpace(node, tokens); | ||
if (isSpaced(before, first)) { | ||
reportNoBeginningSpace(node, before); | ||
} | ||
if (isSpaced(tokenC, tokenD)) { | ||
reportNoEndingSpace(node, tokens); | ||
if (isSpaced(last, after)) { | ||
reportNoEndingSpace(node, after); | ||
} | ||
@@ -147,2 +150,3 @@ } | ||
}, | ||
ArrayExpression: function(node) { | ||
@@ -152,9 +156,11 @@ if (node.elements.length === 0) { | ||
} | ||
var tokens = context.getTokens(node); | ||
var tokenA = tokens[0], tokenB = tokens[1], | ||
tokenC = tokens[tokens.length - 2], tokenD = tokens[tokens.length - 1]; | ||
var first = context.getFirstToken(node), | ||
second = context.getFirstToken(node, 1), | ||
penultimate = context.getLastToken(node, 1), | ||
last = context.getLastToken(node); | ||
var openingBracketMustBeSpaced = | ||
options.objectsInArraysException && tokenB.value === "{" || | ||
options.arraysInArraysException && tokenB.value === "[" || | ||
options.objectsInArraysException && second.value === "{" || | ||
options.arraysInArraysException && second.value === "[" || | ||
options.singleElementException && node.elements.length === 1 | ||
@@ -164,19 +170,19 @@ ? !options.spaced : options.spaced; | ||
var closingBracketMustBeSpaced = | ||
options.objectsInArraysException && tokenC.value === "}" || | ||
options.arraysInArraysException && tokenC.value === "]" || | ||
options.objectsInArraysException && penultimate.value === "}" || | ||
options.arraysInArraysException && penultimate.value === "]" || | ||
options.singleElementException && node.elements.length === 1 | ||
? !options.spaced : options.spaced; | ||
if (isSameLine(tokenA, tokenB) || isSameLine(tokenC, tokenD)) { | ||
if (openingBracketMustBeSpaced && !isSpaced(tokenA, tokenB)) { | ||
reportRequiredBeginningSpace(node, tokens); | ||
if (isSameLine(first, second) || isSameLine(penultimate, last)) { | ||
if (openingBracketMustBeSpaced && !isSpaced(first, second)) { | ||
reportRequiredBeginningSpace(node, first); | ||
} | ||
if (!openingBracketMustBeSpaced && isSpaced(tokenA, tokenB)) { | ||
reportNoBeginningSpace(node, tokens); | ||
if (!openingBracketMustBeSpaced && isSpaced(first, second)) { | ||
reportNoBeginningSpace(node, first); | ||
} | ||
if (closingBracketMustBeSpaced && !isSpaced(tokenC, tokenD)) { | ||
reportRequiredEndingSpace(node, tokens); | ||
if (closingBracketMustBeSpaced && !isSpaced(penultimate, last)) { | ||
reportRequiredEndingSpace(node, last); | ||
} | ||
if (!closingBracketMustBeSpaced && isSpaced(tokenC, tokenD)) { | ||
reportNoEndingSpace(node, tokens); | ||
if (!closingBracketMustBeSpaced && isSpaced(penultimate, last)) { | ||
reportNoEndingSpace(node, last); | ||
} | ||
@@ -190,23 +196,25 @@ } | ||
} | ||
var tokens = context.getTokens(node); | ||
var tokenA = tokens[0], tokenB = tokens[1], | ||
tokenC = tokens[tokens.length - 2], tokenD = tokens[tokens.length - 1]; | ||
var first = context.getFirstToken(node), | ||
second = context.getFirstToken(node, 1), | ||
penultimate = context.getLastToken(node, 1), | ||
last = context.getLastToken(node); | ||
var closingCurlyBraceMustBeSpaced = | ||
options.arraysInObjectsException && tokenC.value === "]" || | ||
options.objectsInObjectsException && tokenC.value === "}" | ||
options.arraysInObjectsException && penultimate.value === "]" || | ||
options.objectsInObjectsException && penultimate.value === "}" | ||
? !options.spaced : options.spaced; | ||
if (isSameLine(tokenA, tokenB) || isSameLine(tokenC, tokenD)) { | ||
if (options.spaced && !isSpaced(tokenA, tokenB)) { | ||
reportRequiredBeginningSpace(node, tokens); | ||
if (isSameLine(first, second) || isSameLine(penultimate, last)) { | ||
if (options.spaced && !isSpaced(first, second)) { | ||
reportRequiredBeginningSpace(node, first); | ||
} | ||
if (!options.spaced && isSpaced(tokenA, tokenB)) { | ||
reportNoBeginningSpace(node, tokens); | ||
if (!options.spaced && isSpaced(first, second)) { | ||
reportNoBeginningSpace(node, first); | ||
} | ||
if (closingCurlyBraceMustBeSpaced && !isSpaced(tokenC, tokenD)) { | ||
reportRequiredEndingSpace(node, tokens); | ||
if (closingCurlyBraceMustBeSpaced && !isSpaced(penultimate, last)) { | ||
reportRequiredEndingSpace(node, last); | ||
} | ||
if (!closingCurlyBraceMustBeSpaced && isSpaced(tokenC, tokenD)) { | ||
reportNoEndingSpace(node, tokens); | ||
if (!closingCurlyBraceMustBeSpaced && isSpaced(penultimate, last)) { | ||
reportNoEndingSpace(node, last); | ||
} | ||
@@ -213,0 +221,0 @@ } |
@@ -21,3 +21,3 @@ /** | ||
function isSpaced(left, right) { | ||
var op, tokens = context.getTokens({range: [left.range[1], right.range[0]]}, 1, 1); | ||
var op, tokens = context.getTokensBetween(left, right, 1); | ||
for (var i = 1, l = tokens.length - 1; i < l; ++i) { | ||
@@ -24,0 +24,0 @@ op = tokens[i]; |
@@ -43,2 +43,7 @@ /** | ||
// If length is zero, ignore it | ||
if (node.value.length === 0) { | ||
return; | ||
} | ||
// Space expected and not found | ||
@@ -45,0 +50,0 @@ if (node.value.indexOf(" ") !== 0) { |
@@ -25,2 +25,4 @@ /** | ||
* @param {string} name The file or directory path. | ||
* @param {string[]} extensions The file extensions that should cause the callback | ||
* to be called. | ||
* @param {Function} exclude The function to check if file/path should be excluded. | ||
@@ -31,3 +33,3 @@ * @param {Function} callback The function to call on each file. | ||
*/ | ||
function walk(name, exclude, callback) { | ||
function walk(name, extensions, exclude, callback) { | ||
@@ -59,3 +61,4 @@ var stat, basename; | ||
if (fileStat.isFile()) { | ||
// only call callback for files with correct extensions | ||
if (fileStat.isFile() && extensions.indexOf(path.extname(filePath)) > -1) { | ||
callback(filePath); | ||
@@ -79,2 +82,3 @@ } else if (fileStat.isDirectory()) { | ||
// always call callback for any files that are passed on the command line | ||
if (stat.isFile()) { | ||
@@ -98,8 +102,9 @@ callback(name); | ||
var files = options.files, | ||
exclude = options.exclude; | ||
exclude = options.exclude, | ||
extensions = options.extensions; | ||
files.forEach(function(file) { | ||
walk(file, exclude, callback); | ||
walk(file, extensions, exclude, callback); | ||
}); | ||
}; |
{ | ||
"name": "eslint", | ||
"version": "0.10.0-alpha.2", | ||
"version": "0.10.0", | ||
"author": "Nicholas C. Zakas <nicholas+npm@nczconsulting.com>", | ||
@@ -39,9 +39,6 @@ "description": "An Esprima-based pattern checker for JavaScript.", | ||
"debug": "^2.0.0", | ||
"deepcopy": "^0.3.3", | ||
"doctrine": "^0.5.2", | ||
"doctrine": "^0.6.2", | ||
"escope": "~1.0.0", | ||
"esprima": "^1.2.2", | ||
"esprima-fb": "^8001.1.0-dev-harmony-fb", | ||
"estraverse": "~1.5.1", | ||
"estraverse-fb": "^1.0.0", | ||
"js-yaml": "~3.2.2", | ||
@@ -67,3 +64,2 @@ "minimatch": "^1.0.0", | ||
"jsonlint": "^1.6.2", | ||
"leche": "^1.0.1", | ||
"mocha": "~1.13.0", | ||
@@ -70,0 +66,0 @@ "mocha-phantomjs": "^3.5.0", |
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
16
19
12
404028
167
10328
+ Addeddoctrine@0.6.4(transitive)
+ Addedesutils@1.1.6(transitive)
+ Addedisarray@0.0.1(transitive)
- Removeddeepcopy@^0.3.3
- Removedesprima-fb@^8001.1.0-dev-harmony-fb
- Removedestraverse-fb@^1.0.0
- Removeddeepcopy@0.3.3(transitive)
- Removeddoctrine@0.5.2(transitive)
- Removedesprima-fb@8001.1.0-dev-harmony-fb(transitive)
- Removedestraverse-fb@1.3.2(transitive)
Updateddoctrine@^0.6.2