eslint
Advanced tools
Comparing version 0.0.7 to 0.1.0-dev
{ | ||
"env": { | ||
"browser": false, | ||
"node": false, | ||
"amd": false | ||
}, | ||
"rules": { | ||
"no-alert": 1, | ||
"no-caller": 1, | ||
"no-alert": 2, | ||
"no-caller": 2, | ||
"no-bitwise": 0, | ||
"no-console": 1, | ||
"no-dangle": 1, | ||
"no-debugger": 1, | ||
"no-empty": 1, | ||
"no-eval": 1, | ||
"no-catch-shadow": 2, | ||
"no-console": 2, | ||
"no-comma-dangle": 2, | ||
"no-debugger": 2, | ||
"no-dupe-keys": 2, | ||
"no-else-return": 0, | ||
"no-empty": 2, | ||
"no-empty-class": 2, | ||
"no-eq-null": 0, | ||
"no-eval": 2, | ||
"no-ex-assign": 2, | ||
"no-func-assign": 2, | ||
"no-floating-decimal": 0, | ||
"no-implied-eval": 1, | ||
"no-with": 1, | ||
"no-fallthrough": 1, | ||
"no-unreachable": 1, | ||
"no-undef-init": 1, | ||
"no-octal": 1, | ||
"no-new-wrappers": 1, | ||
"no-new": 1, | ||
"no-new-func": 1, | ||
"no-implied-eval": 2, | ||
"no-with": 2, | ||
"no-fallthrough": 2, | ||
"no-global-strict": 2, | ||
"no-unreachable": 2, | ||
"no-undef": 2, | ||
"no-undef-init": 2, | ||
"no-octal": 2, | ||
"no-octal-escape": 2, | ||
"no-obj-calls": 2, | ||
"no-new-wrappers": 2, | ||
"no-new": 2, | ||
"no-new-func": 2, | ||
"no-native-reassign": 2, | ||
"no-plusplus": 0, | ||
"no-delete-var": 2, | ||
"no-return-assign": 2, | ||
"no-new-array": 2, | ||
"no-new-object": 2, | ||
"no-label-var": 2, | ||
"no-ternary": 0, | ||
"no-self-compare": 0, | ||
"no-sync": 0, | ||
"no-underscore-dangle": 2, | ||
"no-loop-func": 2, | ||
"no-empty-label": 2, | ||
"no-unused-vars": 2, | ||
"no-script-url": 2, | ||
"no-proto": 2, | ||
"no-iterator": 2, | ||
"no-mixed-requires": [0, false], | ||
"no-wrap-func": 2, | ||
"no-shadow": 2, | ||
"no-use-before-define": 2, | ||
"no-redeclare": 2, | ||
"smarter-eqeqeq": 0, | ||
"brace-style": 0, | ||
"camelcase": 1, | ||
"curly": 1, | ||
"dot-notation": 1, | ||
"eqeqeq": 1, | ||
"new-parens": 1, | ||
"camelcase": 2, | ||
"curly": 2, | ||
"dot-notation": 2, | ||
"eqeqeq": 2, | ||
"new-parens": 2, | ||
"guard-for-in": 0, | ||
"radix": 0, | ||
"new-cap": 1, | ||
"new-cap": 2, | ||
"one-var": 0, | ||
"quote-props": 0, | ||
"semi": 1, | ||
"use-isnan": 1, | ||
"quotes": [1, "double"] | ||
"semi": 2, | ||
"use-isnan": 2, | ||
"quotes": [2, "double"], | ||
"max-depth": [0, 4], | ||
"max-params": [0, 3], | ||
"max-statements": [0, 10], | ||
"regex-spaces": 2, | ||
"complexity": [0, 11], | ||
"wrap-iife": 0, | ||
"no-multi-str": 2, | ||
"consistent-this": [0, "that"], | ||
"unnecessary-strict": 2, | ||
"max-len": [0, 80, 4] | ||
} | ||
} |
# Home | ||
## [About](About.md) | ||
## [About](about/README.md) | ||
Learn more about ESLint and why it came about and the general philosophy behind it. | ||
## [Architecture](Architecture.md) | ||
## [Architecture](architecture/README.md) | ||
Explains how the code is organized and why it is organized in that way. | ||
## [Rules](Rules.md) | ||
## [Rules](rules/README.md) | ||
ESLint comes with some default rules to get you started. This is the complete list. | ||
## [Command Line Interface](Command-line-interface.md) | ||
## [Command Line Interface](command-line-interface/README.md) | ||
ESLint is written to be used primarily for the command line. Learn about its usage here. | ||
## [Developer Guide](Developer-Guide.md) | ||
## [Developer Guide](developer-guide/README.md) | ||
The developer guide contains information for ESLint developers. If you want to contribute to the project, or even just tinker on your own, this guide explains how to get the source and work with it. |
104
lib/cli.js
@@ -6,2 +6,8 @@ /** | ||
/* | ||
* The CLI object should *not* call process.exit() directly. It should only return | ||
* exit codes. This allows other programs to use the CLI object and still control | ||
* when the program exits. | ||
*/ | ||
//------------------------------------------------------------------------------ | ||
@@ -15,9 +21,7 @@ // Requirements | ||
rules = require("./rules"), | ||
eslint = require("./eslint"); // TODO: More formatters | ||
eslint = require("./eslint"), // TODO: More formatters | ||
existsSync = fs.existsSync || path.existsSync, | ||
Config = require("./config"); | ||
//------------------------------------------------------------------------------ | ||
// Constants | ||
//------------------------------------------------------------------------------ | ||
var DEFAULT_CONFIG = "../conf/eslint.json"; | ||
@@ -30,13 +34,2 @@ //------------------------------------------------------------------------------ | ||
function readConfig(options) { | ||
var configLocation = null; | ||
if (options.c || options.config) { | ||
configLocation = path.resolve(process.cwd(), options.c || | ||
options.config); | ||
} | ||
return require(configLocation || DEFAULT_CONFIG); | ||
} | ||
function isDirectory(name){ | ||
@@ -62,4 +55,4 @@ try { | ||
fs.readdirSync(stack.join("/")).forEach(function(file){ | ||
var path = stack.concat([file]).join("/"), | ||
stat = fs.statSync(path); | ||
var filePath = stack.concat([file]).join("/"), | ||
stat = fs.statSync(filePath); | ||
@@ -69,3 +62,3 @@ if (file[0] === ".") { | ||
} else if (stat.isFile() && /\.js$/.test(file)){ | ||
files.push(path); | ||
files.push(filePath); | ||
} else if (stat.isDirectory()){ | ||
@@ -88,10 +81,20 @@ traverse(file, stack); | ||
function printResults(config) { | ||
var formatter; | ||
var formatter, | ||
formatterPath; | ||
if (existsSync(path.resolve(process.cwd(), config.format))) { | ||
formatterPath = path.resolve(process.cwd(), config.format); | ||
} else { | ||
formatterPath = "./formatters/" + config.format; | ||
} | ||
try { | ||
formatter = require("./formatters/" + config.format); | ||
formatter = require(formatterPath); | ||
console.log(formatter(results, config)); | ||
return true; | ||
} catch (ex) { | ||
console.log("Could not find formatter '%s'.", config.format); | ||
process.exit(1); | ||
return false; | ||
} | ||
console.log(formatter(results, config)); | ||
} | ||
@@ -102,6 +105,6 @@ | ||
* @param {string} filename The filename of the file being checked. | ||
* @param {Object} config The configuration object for ESLint. | ||
* @param {Object} configHelper The configuration options for ESLint. | ||
* @returns {int} The total number of errors. | ||
*/ | ||
function processFile(filename, config) { | ||
function processFile(filename, configHelper) { | ||
@@ -112,3 +115,3 @@ // clear all existing settings for a new file | ||
var filePath = path.resolve(filename), | ||
existsSync = fs.existsSync || path.existsSync, | ||
config, | ||
text, | ||
@@ -118,7 +121,10 @@ messages; | ||
if (existsSync(filePath)) { | ||
config = configHelper.getConfig(filePath); | ||
text = fs.readFileSync(path.resolve(filename), "utf8"); | ||
messages = eslint.verify(text, config); | ||
} else { | ||
console.log("Could not find file at '%s'.", filePath); | ||
process.exit(1); | ||
messages = [{ | ||
fatal: true, | ||
message: "Could not find file at '" + filePath + "'." | ||
}]; | ||
} | ||
@@ -131,7 +137,16 @@ | ||
return messages.reduce(function(previous, message) { | ||
if (message.fatal || config.rules[message.ruleId] === 2) { | ||
var severity = null; | ||
if (message.fatal) { | ||
return previous + 1; | ||
} else { | ||
return previous; | ||
} | ||
severity = config.rules[message.ruleId][0] || | ||
config.rules[message.ruleId]; | ||
if (severity === 2) { | ||
return previous + 1; | ||
} | ||
return previous; | ||
}, 0); | ||
@@ -143,6 +158,6 @@ } | ||
* @param {string[]} files All of the filenames to process. | ||
* @param {Object} config The configuration options for ESLint. | ||
* @param {Object} configHelper The configuration options for ESLint. | ||
* @returns {int} The total number of errors. | ||
*/ | ||
function processFiles(files, config) { | ||
function processFiles(files, configHelper) { | ||
@@ -162,9 +177,9 @@ var fullFileList = [], | ||
errors = fullFileList.reduce(function(previous, file) { | ||
return previous + processFile(file, config); | ||
return previous + processFile(file, configHelper); | ||
}, 0); | ||
printResults(config); | ||
// If the formatter succeeds return validation errors count, otherwise | ||
// return 1 for the formatter error. | ||
return printResults(configHelper.getConfig()) ? errors : 1; | ||
return errors; | ||
} | ||
@@ -191,3 +206,3 @@ | ||
files = currentOptions._, | ||
config, | ||
configHelper, | ||
result; | ||
@@ -208,3 +223,3 @@ | ||
config = readConfig(currentOptions); | ||
configHelper = new Config(currentOptions); | ||
@@ -217,9 +232,4 @@ // TODO: Figure out correct option vs. config for this | ||
// assign format information | ||
if (currentOptions.f) { | ||
config.format = currentOptions.f; | ||
} | ||
result = processFiles(files, configHelper); | ||
result = processFiles(files, config); | ||
// result is the number of errors (not warnings) | ||
@@ -230,9 +240,5 @@ return result > 0 ? 1 : 0; | ||
return 0; | ||
} | ||
}; | ||
module.exports = cli; |
@@ -12,3 +12,6 @@ /** | ||
estraverse = require("estraverse"), | ||
escope = require("escope"), | ||
environments = require("../conf/environments.json"), | ||
rules = require("./rules"), | ||
util = require("./util"), | ||
RuleContext = require("./rule-context"), | ||
@@ -25,2 +28,114 @@ EventEmitter = require("events").EventEmitter; | ||
/** | ||
* Parses a list of "option:value" options from a string and invokes the callback | ||
* on each option-value pair. | ||
* @param {string} string | ||
* @param {function} callback | ||
* @returns {void} | ||
*/ | ||
function forEachOption(string, callback) { | ||
// Collapse whitespace around : to make parsing easier | ||
string = string.replace(/\s*:\s*/g, ":"); | ||
string.split(/\s+/).forEach(function(name) { | ||
if (!name) { | ||
return; | ||
} | ||
var pos = name.indexOf(":"), | ||
value; | ||
if (pos !== -1) { | ||
value = name.substring(pos + 1, name.length); | ||
name = name.substring(0, pos); | ||
} | ||
callback(name, value); | ||
}); | ||
} | ||
function parseBoolean(str) { | ||
return str === "true"; | ||
} | ||
/** | ||
* Parses info about globals from a special block comment and adds them to the `declaredGlobals` map. | ||
* @param {ASTNode} comment | ||
* @param {object} declaredGlobals | ||
* @returns {void} | ||
*/ | ||
function parseComment(comment, declaredGlobals) { | ||
if (comment.type !== "Block") { | ||
return; | ||
} | ||
var text = comment.value; | ||
var match; | ||
if ((match = /^\s*(globals?)/.exec(text))) { | ||
forEachOption(text.substring(match.index + match[1].length), function(name, value) { | ||
declaredGlobals[name] = parseBoolean(value); | ||
}); | ||
return true; | ||
} else if ((match = /^\s*(js[lh]int)/.exec(text))) { | ||
forEachOption(text.substring(match.index + match[1].length), function(name, value) { | ||
if (parseBoolean(value) && Object.hasOwnProperty.call(environments, name)) { | ||
util.mixin(declaredGlobals, environments[name]); | ||
} | ||
}); | ||
} | ||
} | ||
/** | ||
* @param {Scope} scope | ||
* @param {string} name | ||
* @returns {Variable} | ||
*/ | ||
function getVariable(scope, name) { | ||
var variable = null; | ||
scope.variables.some(function(v) { | ||
if (v.name === name) { | ||
variable = v; | ||
return true; | ||
} | ||
return false; | ||
}); | ||
return variable; | ||
} | ||
/** | ||
* Ensures that variables representing built-in properties of the Global Object, | ||
* and any globals declared by special block comments, are present in the global | ||
* scope. | ||
* @param {ASTNode} program The top node of the AST. | ||
* @param {Scope} globalScope The global scope. | ||
* @returns {void} | ||
*/ | ||
function addDeclaredGlobals(program, globalScope, config) { | ||
var declaredGlobals = {}, | ||
builtin = environments.builtin; | ||
Object.keys(builtin).forEach(function(name) { | ||
declaredGlobals[name] = builtin[name]; | ||
}); | ||
if (config.env) { | ||
Object.keys(config.env).forEach(function (name) { | ||
var environment = environments[name]; | ||
if (config.env[name] && environment) { | ||
Object.keys(environment).forEach(function(name) { | ||
declaredGlobals[name] = environment[name]; | ||
}); | ||
} | ||
}); | ||
} | ||
program.comments.forEach(function(comment) { | ||
parseComment(comment, declaredGlobals); | ||
}); | ||
Object.keys(declaredGlobals).forEach(function(name) { | ||
var variable = getVariable(globalScope, name); | ||
if (!variable) { | ||
variable = new escope.Variable(name, globalScope); | ||
globalScope.variables.push(variable); | ||
} | ||
variable.writeable = declaredGlobals[name]; | ||
}); | ||
} | ||
//------------------------------------------------------------------------------ | ||
@@ -30,2 +145,6 @@ // Public Interface | ||
/** | ||
* Object that is responsible for verifying JavaScript text | ||
* @name eslint | ||
*/ | ||
module.exports = (function() { | ||
@@ -35,5 +154,7 @@ | ||
messages = [], | ||
commentsAttached = false, | ||
currentText = null, | ||
currentConfig = null, | ||
currentTokens = null, | ||
currentScopes = null, | ||
controller = null; | ||
@@ -48,5 +169,7 @@ | ||
messages = []; | ||
commentsAttached = false; | ||
currentConfig = null; | ||
currentText = null; | ||
currentTokens = null; | ||
currentScopes = null; | ||
controller = null; | ||
@@ -63,2 +186,5 @@ }; | ||
var ast, | ||
parseError = false; | ||
if (!saveState) { | ||
@@ -82,2 +208,3 @@ this.reset(); | ||
if (Array.isArray(config.rules[key])) { | ||
// The additional config data is after the bool value | ||
@@ -105,19 +232,13 @@ options = config.rules[key].slice(1); | ||
/* | ||
* Each node has a type property. Whenever a particular type of node is found, | ||
* an event is fired. This allows any listeners to automatically be informed | ||
* that this type of node has been found and react accordingly. | ||
* 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 { | ||
var ast = esprima.parse(text, { loc: true, range: true, raw: true, tokens: true }); | ||
currentTokens = ast.tokens; | ||
controller.traverse(ast, { | ||
enter: function(node) { | ||
api.emit(node.type, node); | ||
}, | ||
leave: function(node) { | ||
api.emit(node.type + ":after", node); | ||
} | ||
}); | ||
ast = esprima.parse(text, { loc: true, range: true, raw: true, tokens: true, comment: true }); | ||
} catch (ex) { | ||
} catch (ex) { | ||
parseError = true; | ||
messages.push({ | ||
@@ -134,2 +255,34 @@ fatal: true, | ||
if (!parseError) { | ||
// gather data that may be needed by the rules | ||
currentScopes = escope.analyze(ast).scopes; | ||
/* 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; | ||
}); | ||
// augment global scope with declared global variables | ||
addDeclaredGlobals(ast, currentScopes[0], currentConfig); | ||
/* | ||
* Each node has a type property. Whenever a particular type of node is found, | ||
* an event is fired. This allows any listeners to automatically be informed | ||
* that this type of node has been found and react accordingly. | ||
*/ | ||
controller.traverse(ast, { | ||
enter: function(node) { | ||
api.emit(node.type, node); | ||
}, | ||
leave: function(node) { | ||
api.emit(node.type + ":after", node); | ||
} | ||
}); | ||
} | ||
return messages; | ||
@@ -159,3 +312,4 @@ }; | ||
line: node.loc.start.line, | ||
column: node.loc.start.column | ||
column: node.loc.start.column, | ||
source: api.getSource(node) | ||
}); | ||
@@ -192,2 +346,30 @@ }; | ||
/** | ||
* Returns all comments. | ||
* @returns {Object[]} | ||
*/ | ||
api.getAllComments = function () { | ||
return controller.root.comments || []; | ||
}; | ||
/** | ||
* Gets all comments for the given node. | ||
* @param {ASTNode} node The AST node to get the comments for. | ||
* @returns {Object} The list of comments indexed by their position. | ||
*/ | ||
api.getComments = function (node) { | ||
var ast = controller.root; | ||
if (!commentsAttached) { | ||
// Attaching comments is a potentially expensive operation, so we do this lazily. | ||
estraverse.attachComments(ast, ast.comments, ast.tokens); | ||
commentsAttached = true; | ||
} | ||
return { | ||
leading: node.leadingComments || [], | ||
trailing: node.trailingComments || [] | ||
}; | ||
}; | ||
/** | ||
* Gets all tokens that are related to the given node. | ||
@@ -203,10 +385,16 @@ * @param {ASTNode} [node] The AST node to get the text for. | ||
var endLocation = node.range[1] + (afterCount || 0) ; | ||
return currentTokens.filter(function(token) { | ||
return (token.range[0] >= startLocation && | ||
token.range[0] <= endLocation && | ||
token.range[1] >= startLocation && | ||
token.range[1] <= endLocation); | ||
}); | ||
var tokens = []; | ||
while (startLocation < endLocation) { | ||
if (currentTokens[startLocation]) { | ||
tokens.push(currentTokens[startLocation]); | ||
startLocation = currentTokens[startLocation].range[1]; | ||
} else { | ||
startLocation += 1; | ||
} | ||
} | ||
return tokens; | ||
} else { | ||
return currentTokens || null; | ||
return Object.keys(currentTokens).map(function(item) { | ||
return item; | ||
}) || null; | ||
} | ||
@@ -223,4 +411,77 @@ }; | ||
/** | ||
* Gets the scope for the current node. | ||
* @returns {Object} An object representing the current node's scope. | ||
*/ | ||
api.getScope = function() { | ||
var parents = controller.parents().reverse(), | ||
innerScope = null; | ||
// Don't do this for Program nodes - they have no parents | ||
if (parents.length) { | ||
// if current node is function declaration, add it to the list | ||
var current = controller.current(); | ||
if (current.type === "FunctionDeclaration" || current.type === "FunctionExpression") { | ||
parents.splice(0, 0, current); | ||
} | ||
// Ascend the current node's parents | ||
for (var i = 0; i < parents.length; i++) { | ||
// The first node that requires a scope is the node that will be | ||
// our current node's innermost scope. | ||
if (escope.Scope.isScopeRequired(parents[i])) { | ||
innerScope = parents[i]; | ||
break; | ||
} | ||
} | ||
// Loop through the scopes returned by escope to find the innermost | ||
// scope and return that scope. | ||
for (var j = 0; j < currentScopes.length; j++) { | ||
if (innerScope.type === currentScopes[j].block.type && | ||
innerScope.range[0] === currentScopes[j].block.range[0] && | ||
innerScope.range[1] === currentScopes[j].block.range[1]) { | ||
return currentScopes[j]; | ||
} | ||
} | ||
} else { | ||
return currentScopes[0]; // global scope | ||
} | ||
}; | ||
/** | ||
* Defines a new linting rule. | ||
* @param {string} unique rule identifier | ||
* @param {function} function from context to object mapping AST node types to event handlers | ||
* @returns {void} | ||
*/ | ||
var defineRule = api.defineRule = function(ruleId, ruleModule) { | ||
rules.define(ruleId, ruleModule); | ||
}; | ||
/** | ||
* Defines many new linting rules. | ||
* @param {object} map from unique rule identifier to rule | ||
* @returns {void} | ||
*/ | ||
api.defineRules = function(rules) { | ||
Object.getOwnPropertyNames(rules).forEach(function(ruleId){ | ||
defineRule(ruleId, rules[ruleId]); | ||
}); | ||
}; | ||
/** | ||
* Gets the default eslint configuration. | ||
* @returns {Object} Object mapping rule IDs to their default configurations | ||
*/ | ||
api.defaults = function() { | ||
return require("./conf/eslint.json"); | ||
}; | ||
return api; | ||
}()); |
@@ -12,8 +12,16 @@ /** | ||
if (message.fatal || rules[message.ruleId] === 2) { | ||
// TODO: Get rule severity in a better way | ||
var severity = null; | ||
if (message.fatal) { | ||
return "Error"; | ||
} else { | ||
return "Warning"; | ||
} | ||
severity = rules[message.ruleId][0] || rules[message.ruleId]; | ||
if (severity === 2) { | ||
return "Error"; | ||
} | ||
return "Warning"; | ||
} | ||
@@ -40,4 +48,4 @@ | ||
output += result.filePath + ": "; | ||
output += "line " + message.line + ", col " + | ||
message.column + ", " + getMessageType(message, rules); | ||
output += "line " + (message.line || 0) + ", col " + | ||
(message.column || 0) + ", " + getMessageType(message, rules); | ||
output += " - " + message.message + "\n"; | ||
@@ -48,5 +56,5 @@ }); | ||
output += "\n" + total + " problems"; | ||
output += "\n" + total + " problem" + (total !== 1 ? "s" : ""); | ||
return output; | ||
}; |
@@ -13,4 +13,7 @@ /** | ||
"getTokens", | ||
"getComments", | ||
"getAllComments", | ||
"isNodeJS", | ||
"getAncestors" | ||
"getAncestors", | ||
"getScope" | ||
]; | ||
@@ -17,0 +20,0 @@ |
@@ -7,20 +7,7 @@ /** | ||
//------------------------------------------------------------------------------ | ||
// Requirements | ||
//------------------------------------------------------------------------------ | ||
var fs = require("fs"), | ||
path = require("path"); | ||
//------------------------------------------------------------------------------ | ||
// Constants | ||
//------------------------------------------------------------------------------ | ||
var JS_EXT = ".js"; | ||
//------------------------------------------------------------------------------ | ||
// Privates | ||
//------------------------------------------------------------------------------ | ||
var rules = {}; | ||
var rules = Object.create(null), | ||
loadRules = require("./load-rules"); | ||
@@ -30,22 +17,14 @@ //------------------------------------------------------------------------------ | ||
//------------------------------------------------------------------------------ | ||
function define(ruleId, ruleModule) { | ||
rules[ruleId] = ruleModule; | ||
} | ||
exports.load = function(directory) { | ||
function load(rulesDir) { | ||
var newRules = loadRules(rulesDir); | ||
Object.keys(newRules).forEach(function(ruleId) { | ||
define(ruleId, newRules[ruleId]); | ||
}); | ||
} | ||
exports.load = load; | ||
try { | ||
var fullPath = path.resolve(process.cwd(), directory), | ||
files = fs.readdirSync(fullPath); | ||
files.forEach(function(file) { | ||
if (path.extname(file) === JS_EXT) { | ||
var ruleId = file.replace(JS_EXT, ""); | ||
rules[ruleId] = require(path.join(fullPath, ruleId)); | ||
} | ||
}); | ||
} catch (ex) { | ||
console.error("Couldn't load rules from " + directory + ": " + ex.message); | ||
process.exit(1); | ||
} | ||
}; | ||
exports.get = function(ruleId) { | ||
@@ -55,2 +34,4 @@ return rules[ruleId]; | ||
exports.define = define; | ||
//------------------------------------------------------------------------------ | ||
@@ -61,2 +42,2 @@ // Initialization | ||
// loads built-in rules | ||
exports.load(path.join(__dirname, "./rules")); | ||
load(); |
@@ -12,2 +12,4 @@ /** | ||
"use strict"; | ||
//-------------------------------------------------------------------------- | ||
@@ -14,0 +16,0 @@ // Helpers |
@@ -12,2 +12,4 @@ /** | ||
"use strict"; | ||
return { | ||
@@ -21,3 +23,3 @@ | ||
if (name.indexOf("_") > -1 && name !== name.toUpperCase()) { | ||
context.report(node, "Non-camelcased identifier '{{name}}' found.", { name: node.name }); | ||
context.report(node, "Identifier '{{name}}' is not in camel case.", { name: node.name }); | ||
} | ||
@@ -24,0 +26,0 @@ } |
@@ -18,2 +18,4 @@ /** | ||
"use strict"; | ||
return { | ||
@@ -41,2 +43,10 @@ | ||
"DoWhileStatement": function (node) { | ||
if (node.body.type !== "BlockStatement") { | ||
context.report(node, "Expected { after 'do'."); | ||
} | ||
}, | ||
"ForStatement": function(node) { | ||
@@ -43,0 +53,0 @@ |
@@ -12,2 +12,4 @@ /** | ||
"use strict"; | ||
return { | ||
@@ -18,5 +20,5 @@ "BinaryExpression": function(node) { | ||
if (operator === "==") { | ||
context.report(node, "Unexpected use of ==, use === instead."); | ||
context.report(node, "Expected '===' and instead saw '=='."); | ||
} else if (operator === "!=") { | ||
context.report(node, "Unexpected use of !=, use !== instead."); | ||
context.report(node, "Expected '!==' and instead saw '!='."); | ||
} | ||
@@ -23,0 +25,0 @@ } |
@@ -12,2 +12,4 @@ /** | ||
"use strict"; | ||
return { | ||
@@ -14,0 +16,0 @@ |
@@ -13,2 +13,4 @@ /** | ||
"use strict"; | ||
return { | ||
@@ -15,0 +17,0 @@ "NewExpression": function(node) { |
@@ -12,2 +12,4 @@ /** | ||
"use strict"; | ||
return { | ||
@@ -14,0 +16,0 @@ |
@@ -25,2 +25,4 @@ /** | ||
"use strict"; | ||
return { | ||
@@ -27,0 +29,0 @@ |
@@ -12,11 +12,25 @@ /** | ||
"use strict"; | ||
function report(node) { | ||
context.report(node, "Unexpected use of '{{operator}}'.", { operator: node.operator }); | ||
} | ||
return { | ||
"BinaryExpression": function(node) { | ||
// warn for ^ | & | ||
if (node.operator.match(/^[\^&\|]$/)) { | ||
context.report(node, "Unexpected use of {{operator}} found.", { operator: node.operator }); | ||
// warn for ^ | & ~ << >> >>> | ||
if (node.operator.match(/^(?:[\^&\|~]|<<|>>>?)$/)) { | ||
report(node); | ||
} | ||
}, | ||
"UnaryExpression": function(node) { | ||
// warn for ~ | ||
if (node.operator === "~") { | ||
report(node); | ||
} | ||
} | ||
@@ -23,0 +37,0 @@ }; |
@@ -12,2 +12,4 @@ /** | ||
"use strict"; | ||
return { | ||
@@ -19,3 +21,3 @@ | ||
if (objectName === "arguments" && propertyName.match(/^calle[er]$/)) { | ||
if (objectName === "arguments" && !node.computed && propertyName && propertyName.match(/^calle[er]$/)) { | ||
context.report(node, "Avoid arguments.{{property}}.", { property: propertyName }); | ||
@@ -22,0 +24,0 @@ } |
@@ -13,2 +13,4 @@ /** | ||
"use strict"; | ||
return { | ||
@@ -15,0 +17,0 @@ |
@@ -13,2 +13,4 @@ /** | ||
"use strict"; | ||
return { | ||
@@ -15,0 +17,0 @@ "DebuggerStatement": function(node) { |
@@ -12,2 +12,4 @@ /** | ||
"use strict"; | ||
return { | ||
@@ -29,9 +31,5 @@ "BlockStatement": function(node) { | ||
if (typeof node.cases === "undefined") { | ||
if (typeof node.cases === "undefined" || node.cases.length === 0) { | ||
context.report(node, "Empty switch statement."); | ||
} | ||
}, | ||
"EmptyStatement": function(node) { | ||
context.report(node, "Empty statement."); | ||
} | ||
@@ -38,0 +36,0 @@ }; |
@@ -12,6 +12,8 @@ /** | ||
"use strict"; | ||
return { | ||
"CallExpression": function(node) { | ||
if (node.callee.name === "eval") { | ||
context.report(node, "Unexpected use of 'eval()'."); | ||
context.report(node, "eval can be harmful."); | ||
} | ||
@@ -18,0 +20,0 @@ } |
@@ -14,2 +14,4 @@ /** | ||
"use strict"; | ||
return { | ||
@@ -16,0 +18,0 @@ |
@@ -12,2 +12,4 @@ /** | ||
"use strict"; | ||
return { | ||
@@ -14,0 +16,0 @@ "Literal": function(node) { |
@@ -12,2 +12,4 @@ /** | ||
"use strict"; | ||
return { | ||
@@ -14,0 +16,0 @@ "CallExpression": function(node) { |
@@ -12,2 +12,4 @@ /** | ||
"use strict"; | ||
return { | ||
@@ -17,3 +19,3 @@ | ||
if (node.callee.name === "Function") { | ||
context.report(node, "The Function constructor is eval"); | ||
context.report(node, "The Function constructor is eval."); | ||
} | ||
@@ -20,0 +22,0 @@ } |
@@ -12,2 +12,4 @@ /** | ||
"use strict"; | ||
return { | ||
@@ -18,3 +20,3 @@ | ||
if (wrapperObjects.indexOf(node.callee.name) > -1) { | ||
context.report(node, "Do not use {{fn}} as a constructor", { fn: node.callee.name }); | ||
context.report(node, "Do not use {{fn}} as a constructor.", { fn: node.callee.name }); | ||
} | ||
@@ -21,0 +23,0 @@ } |
@@ -13,2 +13,4 @@ /** | ||
"use strict"; | ||
return { | ||
@@ -19,3 +21,3 @@ | ||
if (node.expression.type === "NewExpression") { | ||
context.report(node, "Do not use 'new' for side effects"); | ||
context.report(node, "Do not use 'new' for side effects."); | ||
} | ||
@@ -22,0 +24,0 @@ } |
@@ -12,6 +12,8 @@ /** | ||
"use strict"; | ||
return { | ||
"Literal": function(node) { | ||
if (typeof node.value === "number" && node.raw[0] === "0" && node.raw.length > 1 && node.raw.indexOf("x") < 0) { | ||
if (typeof node.value === "number" && /^0[0-7]/.test(node.raw)) { | ||
context.report(node, "Octal literals should not be used."); | ||
@@ -18,0 +20,0 @@ } |
@@ -12,2 +12,4 @@ /** | ||
"use strict"; | ||
return { | ||
@@ -20,3 +22,3 @@ | ||
if (init === "undefined") { | ||
context.report(node, "Variable '{{name}}' initialized to undefined.", { name: name }); | ||
context.report(node, "It's not necessary to initialize '{{name}}' to undefined.", { name: name }); | ||
} | ||
@@ -23,0 +25,0 @@ } |
@@ -22,2 +22,4 @@ /** | ||
"use strict"; | ||
function checkForUnreachable(node) { | ||
@@ -24,0 +26,0 @@ switch (node.type) { |
@@ -13,2 +13,4 @@ /** | ||
"use strict"; | ||
return { | ||
@@ -15,0 +17,0 @@ "WithStatement": function(node) { |
@@ -12,2 +12,4 @@ /** | ||
"use strict"; | ||
return { | ||
@@ -14,0 +16,0 @@ |
@@ -19,2 +19,4 @@ /** | ||
"use strict"; | ||
/** | ||
@@ -50,3 +52,3 @@ * Validate that a string passed in is surrounded by double quotes | ||
if (!validDoubleQuotes(rawVal)) { | ||
context.report(node, "Use double quotes for string literals."); | ||
context.report(node, "Strings must use doublequote."); | ||
} | ||
@@ -56,3 +58,3 @@ break; | ||
if (!validSingleQuotes(rawVal)) { | ||
context.report(node, "Use single quotes for string literals."); | ||
context.report(node, "Strings must use singlequote."); | ||
} | ||
@@ -59,0 +61,0 @@ break; |
@@ -12,2 +12,4 @@ /** | ||
"use strict"; | ||
return { | ||
@@ -14,0 +16,0 @@ "CallExpression": function(node) { |
@@ -10,5 +10,6 @@ /** | ||
//------------------------------------------------------------------------------ | ||
module.exports = function(context) { | ||
"use strict"; | ||
//-------------------------------------------------------------------------- | ||
@@ -18,10 +19,26 @@ // Helpers | ||
function checkTokenForSemicolon(node, token) { | ||
if (token.type !== "Punctuator" || token.value !== ";") { | ||
context.report(node, "Missing semicolon."); | ||
} | ||
} | ||
function checkForSemicolon(node) { | ||
// get tokens for the node plus one more token at the end | ||
var tokens = context.getTokens(node, 0, 1), | ||
var tokens = context.getTokens(node), | ||
nextToken = tokens.pop(); | ||
if (nextToken.type !== "Punctuator" || nextToken.value !== ";") { | ||
context.report(node, "Missing semicolon."); | ||
checkTokenForSemicolon(node, nextToken); | ||
} | ||
function checkForSemicolonForVariableDeclaration(node) { | ||
// get tokens for the node plus one more token at the end | ||
var ancestors = context.getAncestors(), | ||
parentIndex = ancestors.length-1, | ||
parent = ancestors[parentIndex]; | ||
if ((parent.type !== "ForStatement" || parent.init !== node) && | ||
(parent.type !== "ForInStatement" || parent.left !== node) | ||
) { | ||
checkForSemicolon(node); | ||
} | ||
@@ -36,6 +53,7 @@ } | ||
"VariableDeclaration": checkForSemicolon, | ||
"VariableDeclaration": checkForSemicolonForVariableDeclaration, | ||
"ExpressionStatement": checkForSemicolon | ||
}; | ||
}; |
@@ -22,2 +22,4 @@ /** | ||
"use strict"; | ||
return { | ||
@@ -32,5 +34,5 @@ "BinaryExpression": function(node) { | ||
if (operator === "==") { | ||
context.report(node, "Unexpected use of ==, use === instead."); | ||
context.report(node, "Expected '===' and instead saw '=='."); | ||
} else if (operator === "!=") { | ||
context.report(node, "Unexpected use of !=, use !== instead."); | ||
context.report(node, "Expected '!==' and instead saw '!='."); | ||
} | ||
@@ -37,0 +39,0 @@ } |
@@ -12,2 +12,4 @@ /** | ||
"use strict"; | ||
return { | ||
@@ -14,0 +16,0 @@ "BinaryExpression": function(node) { |
{ | ||
"name": "eslint", | ||
"version": "0.0.7", | ||
"version": "0.1.0-dev", | ||
"author": "Nicholas C. Zakas <nicholas+npm@nczconsulting.com>", | ||
@@ -11,5 +11,6 @@ "description": "An Esprima-based pattern checker for JavaScript.", | ||
"scripts": { | ||
"changelog": "bash ./scripts/changelog-update.sh", | ||
"test": "bash ./scripts/test.sh", | ||
"lint": "node node_modules/jshint/bin/jshint ./conf/eslint.json ./lib" | ||
"changelog": "bash scripts/changelog-update.sh", | ||
"test": "bash scripts/test.sh", | ||
"lint": "node node_modules/jshint/bin/jshint ./conf/eslint.json ./lib", | ||
"bundle": "bash scripts/bundle.sh" | ||
}, | ||
@@ -22,5 +23,6 @@ "repository": { | ||
"optimist": "*", | ||
"estraverse": "~1.2.0", | ||
"estraverse": "~1.3.0", | ||
"esprima": "*", | ||
"jshint": "*" | ||
"jshint": "*", | ||
"escope": "1.0.0" | ||
}, | ||
@@ -31,3 +33,4 @@ "devDependencies": { | ||
"sinon": "*", | ||
"jshint": "~2.1.4" | ||
"jshint": "~2.1.4", | ||
"commonjs-everywhere": "~0.9.0" | ||
}, | ||
@@ -34,0 +37,0 @@ "keywords": [ |
@@ -36,4 +36,8 @@ [![Build Status](https://secure.travis-ci.org/nzakas/eslint.png?branch=master)](http://travis-ci.org/nzakas/eslint) | ||
### What are the plans for ESLint? | ||
Our first goal for ESLint is to hit feature/rule and stability parity with JSHint so that developers can start using ESLint as part of their production toolchain. The master list of JSHint features to be implemented is maintained in this [Google Docs spreadsheet](https://docs.google.com/spreadsheet/lv?key=0Ap5QGaRT4AJ_dGV6VXBlMEw3NHhVRl9vQ0lIX2FnVlE&usp=sharing). To read about plans beyond parity with JSHint, check out the [ESLint Roadmap](https://github.com/nzakas/eslint/wiki/Release-goals). | ||
### Where to ask for help? | ||
Join our [Mailing List](https://groups.google.com/group/eslint) | ||
Join our [Mailing List](https://groups.google.com/group/eslint) |
@@ -1,1 +0,1 @@ | ||
var foo = "bar"; | ||
var foo = "bar"; if (foo) { foo = "bar2"; } |
@@ -12,3 +12,4 @@ /** | ||
assert = require("assert"), | ||
cli = require("../../lib/cli"); | ||
cli = require("../../lib/cli"), | ||
path = require("path"); | ||
@@ -26,3 +27,3 @@ //------------------------------------------------------------------------------ | ||
"should load the specified config file": function(topic) { | ||
var _log = console.log; | ||
var log = console.log; | ||
@@ -33,6 +34,6 @@ // Assign console.log to noop to skip CLI output | ||
assert.doesNotThrow(function () { | ||
cli.execute(["-c", topic, "lib/cli.js"]); | ||
cli.execute(["-c", topic, "lib/cli.js"]); | ||
}); | ||
console.log = _log; | ||
console.log = log; | ||
} | ||
@@ -42,2 +43,187 @@ | ||
"when there is a local config file": { | ||
topic: ["lib/cli.js"], | ||
"should load the local config file": function(topic) { | ||
var log = console.log; | ||
// Assign console.log to noop to skip CLI output | ||
console.log = function() {}; | ||
// Mock CWD | ||
process.eslintCwd = path.resolve(__dirname, "..", "fixtures", "configurations", "single-quotes"); | ||
assert.doesNotThrow(function () { | ||
exitStatus = cli.execute(topic); | ||
}); | ||
cli.execute(topic); | ||
process.eslintCwd = null; | ||
console.log = log; | ||
} | ||
}, | ||
"when given a config with rules with options and severity level set to error": { | ||
topic: ["--config", "tests/fixtures/configurations/quotes-error.json", "single-quoted.js"], | ||
"should exit with an error status (1)": function(topic) { | ||
var log = console.log, | ||
exitStatus; | ||
// Assign console.log to noop to skip CLI output | ||
console.log = function() {}; | ||
assert.doesNotThrow(function () { | ||
exitStatus = cli.execute(topic); | ||
}); | ||
console.log = log; | ||
assert.equal(exitStatus, 1); | ||
}, | ||
}, | ||
"when given a config file and a directory of files": { | ||
topic: ["--config","tests/fixtures/configurations/semi-error.json", "tests/fixtures/formatters"], | ||
"should load and execute without error": function(topic) { | ||
var log = console.log, | ||
exitStatus; | ||
// Assign console.log to noop to skip CLI output | ||
console.log = function() {}; | ||
assert.doesNotThrow(function () { | ||
exitStatus = cli.execute(topic); | ||
}); | ||
console.log = log; | ||
assert.equal(exitStatus, 0); | ||
} | ||
}, | ||
"when given a config with environment set to browser": { | ||
topic: ["--config", "tests/fixtures/configurations/env-browser.json", "tests/fixtures/globals-browser.js"], | ||
"should execute without any errors": function(topic) { | ||
var log = console.log; | ||
// Assign console.log to noop to skip CLI output | ||
console.log = function() {}; | ||
var exit = cli.execute(topic); | ||
assert.equal(exit, 0); | ||
console.log = log; | ||
} | ||
}, | ||
"when given a config with environment set to Node.js": { | ||
topic: ["--config", "tests/fixtures/configurations/env-node.json", "tests/fixtures/globals-node.js"], | ||
"should execute without any errors": function(topic) { | ||
var log = console.log; | ||
// Assign console.log to noop to skip CLI output | ||
console.log = function() {}; | ||
var exit = cli.execute(topic); | ||
assert.equal(exit, 0); | ||
console.log = log; | ||
} | ||
}, | ||
"when given a valid built-in formatter name": { | ||
topic: "checkstyle", | ||
"should execute without any errors": function(topic) { | ||
var log = console.log; | ||
// Assign console.log to noop to skip CLI output | ||
console.log = function() {}; | ||
var exit = cli.execute(["-f", topic, "tests/fixtures/passing.js"]); | ||
assert.equal(exit, 0); | ||
console.log = log; | ||
} | ||
}, | ||
"when given an invalid built-in formatter name": { | ||
topic: "fakeformatter", | ||
"should execute with error": function(topic) { | ||
var log = console.log; | ||
// Assign console.log to noop to skip CLI output | ||
console.log = function() {}; | ||
var exit = cli.execute(["-f", topic, "tests/fixtures/passing.js"]); | ||
assert.equal(exit, 1); | ||
console.log = log; | ||
} | ||
}, | ||
"when given a valid formatter path": { | ||
topic: "tests/fixtures/formatters/simple.js", | ||
"should execute without any errors": function(topic) { | ||
var log = console.log; | ||
// Assign console.log to noop to skip CLI output | ||
console.log = function() {}; | ||
var exit = cli.execute(["-f", topic, "tests/fixtures/passing.js"]); | ||
assert.equal(exit, 0); | ||
console.log = log; | ||
} | ||
}, | ||
"when given an invalid formatter path": { | ||
topic: "tests/fixtures/formatters/file-does-not-exist.js", | ||
"should execute with error": function(topic) { | ||
var log = console.log; | ||
// Assign console.log to noop to skip CLI output | ||
console.log = function() {}; | ||
var exit = cli.execute(["-f", topic, "tests/fixtures/passing.js"]); | ||
assert.equal(exit, 1); | ||
console.log = log; | ||
} | ||
}, | ||
"when executing a file with an error": { | ||
topic: "tests/fixtures/configurations/semi-error.js", | ||
"should execute with error": function(topic) { | ||
var log = console.log; | ||
// Assign console.log to noop to skip CLI output | ||
console.log = function() {}; | ||
var exit = cli.execute([topic]); | ||
assert.equal(exit, 1); | ||
console.log = log; | ||
} | ||
}, | ||
"when calling execute more than once": { | ||
@@ -49,3 +235,3 @@ | ||
var results = '', | ||
_log = console.log; | ||
log = console.log; | ||
@@ -66,3 +252,3 @@ // Collect the CLI output. | ||
console.log = _log; | ||
console.log = log; | ||
} | ||
@@ -69,0 +255,0 @@ |
@@ -22,2 +22,18 @@ /** | ||
//------------------------------------------------------------------------------ | ||
// Helpers | ||
//------------------------------------------------------------------------------ | ||
function getVariable(scope, name) { | ||
var variable = null; | ||
scope.variables.some(function(v) { | ||
if (v.name === name) { | ||
variable = v; | ||
return true; | ||
} | ||
return false; | ||
}); | ||
return variable; | ||
} | ||
//------------------------------------------------------------------------------ | ||
// Tests | ||
@@ -28,2 +44,23 @@ //------------------------------------------------------------------------------ | ||
"when using events": { | ||
topic: TEST_CODE, | ||
"an error should be thrown when an error occurs inside of an event handler": function(topic) { | ||
var config = { rules: {} }; | ||
eslint.reset(); | ||
eslint.on("Program", function() { | ||
throw new Error("Intentional error."); | ||
}); | ||
assert.throws(function() { | ||
eslint.verify(topic, config, true); | ||
}, Error); | ||
} | ||
}, | ||
"when calling toSource()": { | ||
@@ -195,2 +232,61 @@ | ||
"when retrieving comments": { | ||
topic: [ | ||
"// my line comment", | ||
"var a = 42;", | ||
"/* my block comment */" | ||
].join("\n"), | ||
"should retrieve all comments": function(topic) { | ||
var config = { rules: {} }; | ||
eslint.reset(); | ||
eslint.on("Program", function(/*node*/) { | ||
var comments = eslint.getAllComments(); | ||
assert.equal(comments.length, 2); | ||
}); | ||
eslint.verify(topic, config, true); | ||
}, | ||
"should attach them to all nodes": function(topic) { | ||
function assertCommentCount(leading, trailing) { | ||
return function (node) { | ||
var comments = eslint.getComments(node); | ||
assert.equal(comments.leading.length, leading); | ||
assert.equal(comments.trailing.length, trailing); | ||
}; | ||
} | ||
var config = { rules: {} }; | ||
eslint.reset(); | ||
eslint.on("Program", assertCommentCount(1, 0)); | ||
eslint.on("VariableDeclaration", assertCommentCount(0, 1)); | ||
eslint.on("VariableDeclarator", assertCommentCount(0, 0)); | ||
eslint.on("Identifier", assertCommentCount(0, 0)); | ||
eslint.on("Literal", assertCommentCount(0, 0)); | ||
eslint.verify(topic, config, true); | ||
}, | ||
"should attach them lazily": function(topic) { | ||
var config = { rules: {} }; | ||
eslint.reset(); | ||
eslint.on("VariableDeclaration", function (node) { | ||
assert.equal(node.hasOwnProperty("leadingComments"), false); | ||
assert.equal(node.hasOwnProperty("trailingComments"), false); | ||
eslint.getComments(node); | ||
assert.equal(node.hasOwnProperty("leadingComments"), false); | ||
assert.equal(node.hasOwnProperty("trailingComments"), true); | ||
}); | ||
eslint.verify(topic, config, true); | ||
} | ||
}, | ||
"when calling getAncestors": { | ||
@@ -228,3 +324,45 @@ | ||
"when calling getScope": { | ||
topic: "function foo() { q: for(;;) { break q; } } function bar () { var q = t; }", | ||
"should retrieve the global scope correctly from a Program": function(topic) { | ||
var config = { rules: {} }; | ||
eslint.reset(); | ||
eslint.on("Program", function(node) { | ||
var scope = eslint.getScope(); | ||
assert.equal(scope.type, "global"); | ||
}); | ||
eslint.verify(topic, config, true); | ||
}, | ||
"should retrieve the function scope correctly from a FunctionDeclaration": function(topic) { | ||
var config = { rules: {} }; | ||
eslint.reset(); | ||
eslint.on("FunctionDeclaration", function(node) { | ||
var scope = eslint.getScope(); | ||
assert.equal(scope.type, "function"); | ||
}); | ||
eslint.verify(topic, config, true); | ||
}, | ||
"should retrieve the function scope correctly from a LabeledStatement": function(topic) { | ||
var config = { rules: {} }; | ||
eslint.reset(); | ||
eslint.on("LabeledStatement", function(node) { | ||
var scope = eslint.getScope(); | ||
assert.equal(scope.type, "function"); | ||
assert.equal(scope.block.id.name, "foo"); | ||
}); | ||
eslint.verify(topic, config, true); | ||
} | ||
}, | ||
"when evaluating code": { | ||
@@ -353,4 +491,205 @@ | ||
}, | ||
"when evaluating code containing /*global */ and /*globals */ blocks": { | ||
topic: "/*global a b:true c:false*/ function foo() {} /*globals d:true*/", | ||
"variables should be available in global scope": function(topic) { | ||
var config = { rules: {} }; | ||
eslint.reset(); | ||
eslint.on("Program", function(node) { | ||
var scope = eslint.getScope(); | ||
var a = getVariable(scope, "a"), | ||
b = getVariable(scope, "b"), | ||
c = getVariable(scope, "c"), | ||
d = getVariable(scope, "d"); | ||
assert.equal(a.name, "a"); | ||
assert.equal(a.writeable, false); | ||
assert.equal(b.name, "b"); | ||
assert.equal(b.writeable, true); | ||
assert.equal(c.name, "c"); | ||
assert.equal(c.writeable, false); | ||
assert.equal(d.name, "d"); | ||
assert.equal(d.writeable, true); | ||
}); | ||
eslint.verify(topic, config, true); | ||
} | ||
}, | ||
"when evaluating code containing a /*global */ block with sloppy whitespace": { | ||
topic: "/* global a b : true c: false*/", | ||
"variables should be available in global scope": function(topic) { | ||
var config = { rules: {} }; | ||
eslint.reset(); | ||
eslint.on("Program", function(node) { | ||
var scope = eslint.getScope(), | ||
a = getVariable(scope, "a"), | ||
b = getVariable(scope, "b"), | ||
c = getVariable(scope, "c"); | ||
assert.equal(a.name, "a"); | ||
assert.equal(a.writeable, false); | ||
assert.equal(b.name, "b"); | ||
assert.equal(b.writeable, true); | ||
assert.equal(c.name, "c"); | ||
assert.equal(c.writeable, false); | ||
}); | ||
eslint.verify(topic, config, true); | ||
} | ||
}, | ||
"when evaluating code containing /*jshint */ block": { | ||
topic: "/*jslint node:true*/ function f() {} /*jshint browser:true foo:bar*/", | ||
"variables should be available in global scope": function(topic) { | ||
var config = { rules: {} }; | ||
eslint.reset(); | ||
eslint.on("Program", function(node) { | ||
var scope = eslint.getScope(), | ||
exports = getVariable(scope, "exports"), | ||
window = getVariable(scope, "window"); | ||
assert.equal(exports.writeable, true); | ||
assert.equal(window.writeable, false); | ||
}); | ||
eslint.verify(topic, config, true); | ||
} | ||
}, | ||
"when evaluating code containing a /*jshint */ block with sloppy whitespace": { | ||
topic: "/* jshint node : true browser : false*/", | ||
"variables should be available in global scope": function(topic) { | ||
var config = { rules: {} }; | ||
eslint.reset(); | ||
eslint.on("Program", function(node) { | ||
var scope = eslint.getScope(), | ||
exports = getVariable(scope, "exports"), | ||
window = getVariable(scope, "window"); | ||
assert.equal(exports.writeable, true); | ||
assert.equal(window, null); | ||
}); | ||
eslint.verify(topic, config, true); | ||
} | ||
}, | ||
"when evaluating code containing a line comment": { | ||
topic: "//global a \n function f() {}", | ||
"should not introduce a global variable": function(topic) { | ||
var config = { rules: {} }; | ||
eslint.reset(); | ||
eslint.on("Program", function(node) { | ||
var scope = eslint.getScope(); | ||
assert.equal(getVariable(scope, "a"), null); | ||
}); | ||
eslint.verify(topic, config, true); | ||
} | ||
}, | ||
"when evaluating code containing normal block comments": { | ||
topic: "/**/ /*a*/ /*b:true*/ /*foo c:false*/", | ||
"should not introduce a global variable": function(topic) { | ||
var config = { rules: {} }; | ||
eslint.reset(); | ||
eslint.on("Program", function(node) { | ||
var scope = eslint.getScope(); | ||
assert.equal(getVariable(scope, "a"), null); | ||
assert.equal(getVariable(scope, "b"), null); | ||
assert.equal(getVariable(scope, "foo"), null); | ||
assert.equal(getVariable(scope, "c"), null); | ||
}); | ||
eslint.verify(topic, config, true); | ||
} | ||
}, | ||
"when evaluating any code": { | ||
topic: "", | ||
"builtin global variables should be available in the global scope": function(topic) { | ||
var config = { rules: {} }; | ||
eslint.reset(); | ||
eslint.on("Program", function(node) { | ||
var scope = eslint.getScope(); | ||
assert.notEqual(getVariable(scope, "Object"), null); | ||
assert.notEqual(getVariable(scope, "Array"), null); | ||
assert.notEqual(getVariable(scope, "undefined"), null); | ||
}); | ||
eslint.verify(topic, config, true); | ||
} | ||
}, | ||
"at any time": { | ||
topic: "new-rule", | ||
"can add a rule dynamically": function(topic) { | ||
eslint.reset(); | ||
eslint.defineRule(topic, function(context) { | ||
return {"Literal": function(node) { context.report(node, "message"); }}; | ||
}); | ||
var config = { rules: {} }; | ||
config.rules[topic] = 1; | ||
var messages = eslint.verify("0", config); | ||
assert.equal(messages.length, 1); | ||
assert.equal(messages[0].ruleId, topic); | ||
assert.equal(messages[0].node.type, "Literal"); | ||
} | ||
}, | ||
"at any time": { | ||
topic: ["new-rule-0", "new-rule-1"], | ||
"can add multiple rules dynamically": function(topics) { | ||
eslint.reset(); | ||
var config = { rules: {} }; | ||
var newRules = {}; | ||
topics.forEach(function(topic){ | ||
config.rules[topic] = 1; | ||
newRules[topic] = function(context) { | ||
return {"Literal": function(node) { context.report(node, "message"); }}; | ||
}; | ||
}); | ||
eslint.defineRules(newRules); | ||
var messages = eslint.verify("0", config); | ||
assert.equal(messages.length, topics.length); | ||
topics.forEach(function(topic){ | ||
assert.ok(messages.some(function(message){ return message.ruleId === topic; })); | ||
}); | ||
messages.forEach(function(message){ assert.equal(message.node.type, "Literal"); }); | ||
} | ||
} | ||
}).export(module); |
@@ -38,3 +38,3 @@ /** | ||
var result = formatter(topic, config); | ||
assert.equal("foo.js: line 5, col 10, Error - Unexpected foo.\n\n1 problems", result); | ||
assert.equal("foo.js: line 5, col 10, Error - Unexpected foo.\n\n1 problem", result); | ||
}, | ||
@@ -48,5 +48,14 @@ | ||
var result = formatter(topic, config); | ||
assert.equal("foo.js: line 5, col 10, Warning - Unexpected foo.\n\n1 problems", result); | ||
} | ||
assert.equal("foo.js: line 5, col 10, Warning - Unexpected foo.\n\n1 problem", result); | ||
}, | ||
"should return a string in the format filename: line x, col y, Error - z for errors with options config": function(topic) { | ||
var config = { | ||
rules: { foo: [2, "option"] } | ||
}; | ||
var result = formatter(topic, config); | ||
assert.equal("foo.js: line 5, col 10, Error - Unexpected foo.\n\n1 problem", result); | ||
}, | ||
}, | ||
@@ -71,3 +80,3 @@ | ||
var result = formatter(topic, config); | ||
assert.equal("foo.js: line 5, col 10, Error - Unexpected foo.\n\n1 problems", result); | ||
assert.equal("foo.js: line 5, col 10, Error - Unexpected foo.\n\n1 problem", result); | ||
} | ||
@@ -130,4 +139,25 @@ }, | ||
} | ||
}, | ||
"when passed one file not found message": { | ||
topic: [{ | ||
filePath: "foo.js", | ||
messages: [{ | ||
fatal: true, | ||
message: "Couldn't find foo.js." | ||
}] | ||
}], | ||
"should return a string without line and column": function(topic) { | ||
var config = { | ||
rules: { foo: 2, bar: 1 } | ||
}; | ||
var result = formatter(topic, config); | ||
assert.equal("foo.js: line 0, col 0, Error - Couldn't find foo.js.\n\n1 problem", result); | ||
} | ||
} | ||
}).export(module); | ||
}).export(module); |
@@ -126,7 +126,25 @@ /** | ||
} | ||
}, | ||
"when asking for help": { | ||
topic: "", | ||
"should log the help content to the console": function(topic) { | ||
var log = console.log; | ||
var loggedMessages = []; | ||
console.log = function(message) { | ||
loggedMessages.push(message); | ||
}; | ||
options.help() | ||
assert.equal(loggedMessages.length, 1); | ||
console.log = log; | ||
} | ||
} | ||
}).export(module); |
@@ -39,3 +39,3 @@ /** | ||
assert.equal(messages[0].ruleId, RULE_ID); | ||
assert.equal(messages[0].message, "Non-camelcased identifier 'first_name' found."); | ||
assert.equal(messages[0].message, "Identifier 'first_name' is not in camel case."); | ||
assert.include(messages[0].node.type, "Identifier"); | ||
@@ -119,3 +119,3 @@ assert.include(messages[0].node.name, "first_name"); | ||
assert.equal(messages[0].ruleId, RULE_ID); | ||
assert.equal(messages[0].message, "Non-camelcased identifier '__private_first_name' found."); | ||
assert.equal(messages[0].message, "Identifier '__private_first_name' is not in camel case."); | ||
assert.include(messages[0].node.type, "Identifier"); | ||
@@ -122,0 +122,0 @@ assert.include(messages[0].node.name, "__private_first_name"); |
@@ -120,2 +120,33 @@ /** | ||
"when evaluating 'do bar(); while (foo)'": { | ||
topic: "do bar(); while (foo)", | ||
"should report a violation": function(topic) { | ||
var config = { rules: {} }; | ||
config.rules[RULE_ID] = 1; | ||
var messages = eslint.verify(topic, config); | ||
assert.equal(messages.length, 1); | ||
assert.equal(messages[0].ruleId, RULE_ID); | ||
assert.equal(messages[0].message, "Expected { after 'do'."); | ||
assert.include(messages[0].node.type, "DoWhileStatement"); | ||
} | ||
}, | ||
"when evaluating 'do { bar(); } while (foo)'": { | ||
topic: "do { bar(); } while (foo)", | ||
"should not report a violation": function(topic) { | ||
var config = { rules: {} }; | ||
config.rules[RULE_ID] = 1; | ||
var messages = eslint.verify(topic, config); | ||
assert.equal(messages.length, 0); | ||
} | ||
}, | ||
"when evaluating 'for (;foo;) bar()'": { | ||
@@ -122,0 +153,0 @@ |
@@ -38,3 +38,3 @@ /** | ||
assert.equal(messages[0].ruleId, RULE_ID); | ||
assert.equal(messages[0].message, "Unexpected use of ==, use === instead."); | ||
assert.equal(messages[0].message, "Expected '===' and instead saw '=='."); | ||
assert.include(messages[0].node.type, "BinaryExpression"); | ||
@@ -56,3 +56,3 @@ } | ||
assert.equal(messages[0].ruleId, RULE_ID); | ||
assert.equal(messages[0].message, "Unexpected use of !=, use !== instead."); | ||
assert.equal(messages[0].message, "Expected '!==' and instead saw '!='."); | ||
assert.include(messages[0].node.type, "BinaryExpression"); | ||
@@ -59,0 +59,0 @@ } |
@@ -38,3 +38,3 @@ /** | ||
assert.equal(messages[0].ruleId, RULE_ID); | ||
assert.equal(messages[0].message, "Unexpected use of ^ found."); | ||
assert.equal(messages[0].message, "Unexpected use of '^'."); | ||
assert.include(messages[0].node.type, "BinaryExpression"); | ||
@@ -57,3 +57,3 @@ assert.include(messages[0].node.operator, "^"); | ||
assert.equal(messages[0].ruleId, RULE_ID); | ||
assert.equal(messages[0].message, "Unexpected use of | found."); | ||
assert.equal(messages[0].message, "Unexpected use of '|'."); | ||
assert.include(messages[0].node.type, "BinaryExpression"); | ||
@@ -76,3 +76,3 @@ assert.include(messages[0].node.operator, "|"); | ||
assert.equal(messages[0].ruleId, RULE_ID); | ||
assert.equal(messages[0].message, "Unexpected use of & found."); | ||
assert.equal(messages[0].message, "Unexpected use of '&'."); | ||
assert.include(messages[0].node.type, "BinaryExpression"); | ||
@@ -83,2 +83,56 @@ assert.include(messages[0].node.operator, "&"); | ||
"when evaluating '<<": { | ||
topic: "a << b", | ||
"should report a violation": function(topic) { | ||
var config = { rules: {} }; | ||
config.rules[RULE_ID] = 1; | ||
var messages = eslint.verify(topic, config); | ||
assert.equal(messages.length, 1); | ||
assert.equal(messages[0].ruleId, RULE_ID); | ||
assert.equal(messages[0].message, "Unexpected use of '<<'."); | ||
assert.include(messages[0].node.type, "BinaryExpression"); | ||
assert.include(messages[0].node.operator, "<<"); | ||
} | ||
}, | ||
"when evaluating '>>": { | ||
topic: "a >> b", | ||
"should report a violation": function(topic) { | ||
var config = { rules: {} }; | ||
config.rules[RULE_ID] = 1; | ||
var messages = eslint.verify(topic, config); | ||
assert.equal(messages.length, 1); | ||
assert.equal(messages[0].ruleId, RULE_ID); | ||
assert.equal(messages[0].message, "Unexpected use of '>>'."); | ||
assert.include(messages[0].node.type, "BinaryExpression"); | ||
assert.include(messages[0].node.operator, ">>"); | ||
} | ||
}, | ||
"when evaluating '>>>": { | ||
topic: "a >>> b", | ||
"should report a violation": function(topic) { | ||
var config = { rules: {} }; | ||
config.rules[RULE_ID] = 1; | ||
var messages = eslint.verify(topic, config); | ||
assert.equal(messages.length, 1); | ||
assert.equal(messages[0].ruleId, RULE_ID); | ||
assert.equal(messages[0].message, "Unexpected use of '>>>'."); | ||
assert.include(messages[0].node.type, "BinaryExpression"); | ||
assert.include(messages[0].node.operator, ">>>"); | ||
} | ||
}, | ||
"when evaluating '+": { | ||
@@ -96,6 +150,38 @@ | ||
} | ||
} | ||
}, | ||
"when evaluating '~": { | ||
topic: "~a", | ||
"should report a violation": function(topic) { | ||
var config = { rules: {} }; | ||
config.rules[RULE_ID] = 1; | ||
var messages = eslint.verify(topic, config); | ||
assert.equal(messages.length, 1); | ||
assert.equal(messages[0].ruleId, RULE_ID); | ||
assert.equal(messages[0].message, "Unexpected use of '~'."); | ||
assert.include(messages[0].node.type, "UnaryExpression"); | ||
assert.include(messages[0].node.operator, "~"); | ||
} | ||
}, | ||
"when evaluating '!": { | ||
topic: "!a", | ||
"should not report a violation": function(topic) { | ||
var config = { rules: {} }; | ||
config.rules[RULE_ID] = 1; | ||
var messages = eslint.verify(topic, config); | ||
assert.equal(messages.length, 0); | ||
} | ||
}, | ||
}).export(module); |
@@ -90,4 +90,34 @@ /** | ||
} | ||
}, | ||
"when evaluating 'var x = arguments[0]'": { | ||
topic: "var x = arguments[0]", | ||
"should not report a violation": function(topic) { | ||
var config = { rules: {} }; | ||
config.rules[RULE_ID] = 1; | ||
var messages = eslint.verify(topic, config); | ||
assert.equal(messages.length, 0); | ||
} | ||
}, | ||
"when evaluating 'var x = arguments[caller]'": { | ||
topic: "var x = arguments[caller]", | ||
"should not report a violation": function(topic) { | ||
var config = { rules: {} }; | ||
config.rules[RULE_ID] = 1; | ||
var messages = eslint.verify(topic, config); | ||
assert.equal(messages.length, 0); | ||
} | ||
} | ||
}).export(module); |
@@ -201,19 +201,2 @@ /** | ||
"when evaluating ';'": { | ||
topic: ";", | ||
"should report a violation": function(topic) { | ||
var config = { rules: {} }; | ||
config.rules[RULE_ID] = 1; | ||
var messages = eslint.verify(topic, config); | ||
assert.equal(messages.length, 1); | ||
assert.equal(messages[0].ruleId, RULE_ID); | ||
assert.equal(messages[0].message, "Empty statement."); | ||
assert.include(messages[0].node.type, "EmptyStatement"); | ||
} | ||
}, | ||
"when evaluating '(function() { }())'": { | ||
@@ -220,0 +203,0 @@ |
@@ -38,3 +38,3 @@ /** | ||
assert.equal(messages[0].ruleId, RULE_ID); | ||
assert.equal(messages[0].message, "Unexpected use of 'eval()'."); | ||
assert.equal(messages[0].message, "eval can be harmful."); | ||
assert.include(messages[0].node.type, "CallExpression"); | ||
@@ -41,0 +41,0 @@ assert.include(messages[0].node.callee.name, "eval"); |
@@ -37,3 +37,3 @@ /** | ||
assert.equal(messages[0].ruleId, RULE_ID); | ||
assert.equal(messages[0].message, "The Function constructor is eval"); | ||
assert.equal(messages[0].message, "The Function constructor is eval."); | ||
assert.include(messages[0].node.type, "NewExpression"); | ||
@@ -40,0 +40,0 @@ } |
@@ -40,3 +40,3 @@ /** | ||
assert.equal(messages[0].ruleId, RULE_ID); | ||
assert.equal(messages[0].message, "Do not use String as a constructor"); | ||
assert.equal(messages[0].message, "Do not use String as a constructor."); | ||
assert.include(messages[0].node.type, "NewExpression"); | ||
@@ -59,3 +59,3 @@ } | ||
assert.equal(messages[0].ruleId, RULE_ID); | ||
assert.equal(messages[0].message, "Do not use Number as a constructor"); | ||
assert.equal(messages[0].message, "Do not use Number as a constructor."); | ||
assert.include(messages[0].node.type, "NewExpression"); | ||
@@ -78,3 +78,3 @@ } | ||
assert.equal(messages[0].ruleId, RULE_ID); | ||
assert.equal(messages[0].message, "Do not use Boolean as a constructor"); | ||
assert.equal(messages[0].message, "Do not use Boolean as a constructor."); | ||
assert.include(messages[0].node.type, "NewExpression"); | ||
@@ -97,3 +97,3 @@ } | ||
assert.equal(messages[0].ruleId, RULE_ID); | ||
assert.equal(messages[0].message, "Do not use Math as a constructor"); | ||
assert.equal(messages[0].message, "Do not use Math as a constructor."); | ||
assert.include(messages[0].node.type, "NewExpression"); | ||
@@ -116,3 +116,3 @@ } | ||
assert.equal(messages[0].ruleId, RULE_ID); | ||
assert.equal(messages[0].message, "Do not use JSON as a constructor"); | ||
assert.equal(messages[0].message, "Do not use JSON as a constructor."); | ||
assert.include(messages[0].node.type, "NewExpression"); | ||
@@ -119,0 +119,0 @@ } |
@@ -38,3 +38,3 @@ /** | ||
assert.equal(messages[0].ruleId, RULE_ID); | ||
assert.equal(messages[0].message, "Do not use 'new' for side effects"); | ||
assert.equal(messages[0].message, "Do not use 'new' for side effects."); | ||
assert.include(messages[0].node.type, "ExpressionStatement"); | ||
@@ -41,0 +41,0 @@ } |
@@ -54,5 +54,5 @@ /** | ||
"when evaluating 'var a = 0x1234'": { | ||
"when evaluating '0x1234'": { | ||
topic: "var a = 0x1234;", | ||
topic: "0x1234", | ||
@@ -70,2 +70,17 @@ "should not report a violation": function(topic) { | ||
"when evaluating '0X5'": { | ||
topic: "0X5;", | ||
"should not report a violation": function(topic) { | ||
var config = { rules: {} }; | ||
config.rules[RULE_ID] = 1; | ||
var messages = eslint.verify(topic, config); | ||
assert.equal(messages.length, 0); | ||
} | ||
}, | ||
"when evaluating 'a = 1 + 01234'": { | ||
@@ -103,2 +118,50 @@ | ||
}, | ||
"when evaluating the literal 00": { | ||
topic: "00", | ||
"should report a violation": function(topic) { | ||
var config = { rules: {} }; | ||
config.rules[RULE_ID] = 1; | ||
var messages = eslint.verify(topic, config); | ||
assert.equal(messages.length, 1); | ||
assert.equal(messages[0].ruleId, RULE_ID); | ||
assert.equal(messages[0].message, "Octal literals should not be used."); | ||
assert.include(messages[0].node.type, "Literal"); | ||
} | ||
}, | ||
"when evaluating numbers between 0 and 1": { | ||
topic: "0.1", | ||
"should not report a violation": function(topic) { | ||
var config = { rules: {} }; | ||
config.rules[RULE_ID] = 1; | ||
var messages = eslint.verify(topic, config); | ||
assert.equal(messages.length, 0); | ||
} | ||
}, | ||
"when evaluating 0-prefixed numbers in scientific notation": { | ||
topic: "0.5e1", | ||
"should not report a violation": function(topic) { | ||
var config = { rules: {} }; | ||
config.rules[RULE_ID] = 1; | ||
var messages = eslint.verify(topic, config); | ||
assert.equal(messages.length, 0); | ||
} | ||
} | ||
}).export(module); |
@@ -51,3 +51,3 @@ /** | ||
assert.equal(messages[0].ruleId, RULE_ID); | ||
assert.equal(messages[0].message, "Variable 'a' initialized to undefined."); | ||
assert.equal(messages[0].message, "It's not necessary to initialize 'a' to undefined."); | ||
assert.include(messages[0].node.type, "VariableDeclarator"); | ||
@@ -54,0 +54,0 @@ } |
@@ -54,3 +54,3 @@ /** | ||
assert.equal(messages[0].ruleId, RULE_ID); | ||
assert.equal(messages[0].message, "Use single quotes for string literals."); | ||
assert.equal(messages[0].message, "Strings must use singlequote."); | ||
} | ||
@@ -72,3 +72,3 @@ }, | ||
assert.equal(messages[0].ruleId, RULE_ID); | ||
assert.equal(messages[0].message, "Use double quotes for string literals."); | ||
assert.equal(messages[0].message, "Strings must use doublequote."); | ||
} | ||
@@ -75,0 +75,0 @@ }, |
@@ -156,5 +156,124 @@ /** | ||
} | ||
}, | ||
"when evaluation 'setTimeout(function() {foo = \"bar\"; });'": { | ||
topic: "setTimeout(function() {foo = \"bar\"; });", | ||
"should not report a violation": function(topic) { | ||
var config = { rules: {} }; | ||
config.rules[RULE_ID] = 1; | ||
var messages = eslint.verify(topic, config); | ||
assert.equal(messages.length, 0); | ||
} | ||
}, | ||
"when evaluation 'setTimeout(function() {foo = \"bar\";});'": { | ||
topic: "setTimeout(function() {foo = \"bar\";});", | ||
"should not report a violation": function(topic) { | ||
var config = { rules: {} }; | ||
config.rules[RULE_ID] = 1; | ||
var messages = eslint.verify(topic, config); | ||
assert.equal(messages.length, 0); | ||
} | ||
}, | ||
"when evaluating 'for (var a in b){}": { | ||
topic: "for (var a in b){}", | ||
"should not report a violation": function(topic) { | ||
var config = { rules: {} }; | ||
config.rules[RULE_ID] = 1; | ||
var messages = eslint.verify(topic, config); | ||
assert.equal(messages.length, 0); | ||
} | ||
}, | ||
"when evaluating 'for (var a in b) var i": { | ||
topic: "for (var a in b) var i ", | ||
"should report a violation": function(topic) { | ||
var config = { rules: {} }; | ||
config.rules[RULE_ID] = 1; | ||
var messages = eslint.verify(topic, config); | ||
assert.equal(messages.length, 1); | ||
assert.equal(messages[0].ruleId, RULE_ID); | ||
assert.equal(messages[0].message, "Missing semicolon."); | ||
assert.include(messages[0].node.type, "VariableDeclaration"); | ||
} | ||
}, | ||
"when evaluating 'for (var i;;){}": { | ||
topic: "for (var i;;){}", | ||
"should not report a violation": function(topic) { | ||
var config = { rules: {} }; | ||
config.rules[RULE_ID] = 1; | ||
var messages = eslint.verify(topic, config); | ||
assert.equal(messages.length, 0); | ||
} | ||
}, | ||
"when evaluating 'for (;;){var i}": { | ||
topic: "for (;;){var i}", | ||
"should report a violation": function(topic) { | ||
var config = { rules: {} }; | ||
config.rules[RULE_ID] = 1; | ||
var messages = eslint.verify(topic, config); | ||
assert.equal(messages.length, 1); | ||
assert.equal(messages[0].ruleId, RULE_ID); | ||
assert.equal(messages[0].message, "Missing semicolon."); | ||
assert.include(messages[0].node.type, "VariableDeclaration"); | ||
} | ||
}, | ||
"when evaluating 'for (;;) var i": { | ||
topic: "for (;;) var i ", | ||
"should report a violation": function(topic) { | ||
var config = { rules: {} }; | ||
config.rules[RULE_ID] = 1; | ||
var messages = eslint.verify(topic, config); | ||
assert.equal(messages.length, 1); | ||
assert.equal(messages[0].ruleId, RULE_ID); | ||
assert.equal(messages[0].message, "Missing semicolon."); | ||
assert.include(messages[0].node.type, "VariableDeclaration"); | ||
} | ||
}, | ||
"when evaluating 'for (var j;;) {var i}": { | ||
topic: "for (var j;;) {var i}", | ||
"should report a violation": function(topic) { | ||
var config = { rules: {} }; | ||
config.rules[RULE_ID] = 1; | ||
var messages = eslint.verify(topic, config); | ||
assert.equal(messages.length, 1); | ||
assert.equal(messages[0].ruleId, RULE_ID); | ||
assert.equal(messages[0].message, "Missing semicolon."); | ||
assert.include(messages[0].node.type, "VariableDeclaration"); | ||
} | ||
} | ||
}).export(module); |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Uses eval
Supply chain riskPackage uses eval() which is a dangerous function. This prevents the code from running in certain environments and increases the risk that the code may contain exploits or malicious behavior.
Found 1 instance in 1 package
Debug access
Supply chain riskUses debug, reflection and dynamic code execution features.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
953360
289
22068
43
5
5
12
2
+ Addedescope@1.0.0
+ Addedescope@1.0.0(transitive)
+ Addedestraverse@1.3.2(transitive)
- Removedestraverse@1.2.0(transitive)
Updatedestraverse@~1.3.0