Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

eslint

Package Overview
Dependencies
Maintainers
1
Versions
373
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

eslint - npm Package Compare versions

Comparing version 0.20.0 to 0.21.0

lib/config-initializer.js

15

bin/eslint.js
#!/usr/bin/env node
var concat = require("concat-stream"),
configInit = require("../lib/config-initializer"),
cli = require("../lib/cli");
var exitCode = 0,
useStdIn = (process.argv.indexOf("--stdin") > -1);
useStdIn = (process.argv.indexOf("--stdin") > -1),
init = (process.argv.indexOf("--init") > -1);

@@ -18,2 +20,13 @@ if (useStdIn) {

}));
} else if (init) {
configInit.initializeConfig(function(err) {
if (err) {
exitCode = 1;
console.error(err.message);
console.error(err.stack);
} else {
console.log("Successfully created .eslintrc file in " + process.cwd());
exitCode = 0;
}
});
} else {

@@ -20,0 +33,0 @@ exitCode = cli.execute(process.argv);

@@ -59,2 +59,3 @@ {

"no-mixed-spaces-and-tabs": [2, false],
"linebreak-style": [0, "unix"],
"no-multi-spaces": 2,

@@ -101,2 +102,3 @@ "no-multi-str": 2,

"no-underscore-dangle": 2,
"no-unneeded-ternary": 0,
"no-unreachable": 2,

@@ -123,2 +125,3 @@ "no-unused-expressions": 2,

"default-case": 0,
"dot-location": 0,
"dot-notation": [2, { "allowKeywords": true }],

@@ -125,0 +128,0 @@ "eol-last": 2,

64

lib/cli-engine.js

@@ -243,2 +243,23 @@ /**

/**
* Returns result with warning by ignore settings
* @param {string} filePath File path of checked code
* @returns {Result} Result with single warning
* @private
*/
function createIgnoreResult(filePath) {
return {
filePath: filePath,
messages: [
{
fatal: false,
severity: 1,
message: "File ignored because of your .eslintignore file. Use --no-ignore to override."
}
],
errorCount: 0,
warningCount: 1
};
}
//------------------------------------------------------------------------------

@@ -268,4 +289,2 @@ // Public Interface

}
loadPlugins(this.options.plugins);
}

@@ -278,2 +297,16 @@

/**
* Add a plugin by passing it's configuration
* @param {string} name Name of the plugin.
* @param {Object} pluginobject Plugin configuration object.
* @returns {void}
*/
addPlugin: function(name, pluginobject) {
var pluginNameWithoutPrefix = util.removePluginPrefix(util.removeNameSpace(name));
if (pluginobject.rules) {
rules.import(pluginobject.rules, pluginNameWithoutPrefix);
}
loadedPlugins[pluginNameWithoutPrefix] = pluginobject;
},
/**
* Executes the current configuration on an array of file and directory names.

@@ -309,14 +342,3 @@ * @param {string[]} files An array of file and directory names.

if (fs.statSync(path.resolve(file)).isFile() && processed.indexOf(file) === -1) {
results.push({
filePath: file,
messages: [
{
fatal: false,
severity: 1,
message: "File ignored because of your .eslintignore file. Use --no-ignore to override."
}
],
errorCount: 0,
warningCount: 1
});
results.push(createIgnoreResult(file));
}

@@ -345,5 +367,13 @@ });

results = [],
stats;
stats,
options = this.options,
ignoredPaths = IgnoredPaths.load(options),
exclude = ignoredPaths.contains.bind(ignoredPaths);
results.push(processText(text, configHelper, filename));
if (filename && options.ignore && exclude(filename)) {
results.push(createIgnoreResult(filename));
} else {
results.push(processText(text, configHelper, filename));
}
stats = calculateStatsPerRun(results);

@@ -422,4 +452,2 @@

}
}

@@ -426,0 +454,0 @@

@@ -23,3 +23,4 @@ /**

yaml = require("js-yaml"),
userHome = require("user-home");
userHome = require("user-home"),
isAbsolutePath = require("path-is-absolute");

@@ -48,5 +49,18 @@ //------------------------------------------------------------------------------

/**
* Determines if a given string represents a filepath or not using the same
* conventions as require(), meaning that the first character must be nonalphanumeric
* to be considered a file path.
* @param {string} filePath The string to check.
* @returns {boolean} True if it's a filepath, false if not.
* @private
*/
function isFilePath(filePath) {
return isAbsolutePath(filePath) || !/\w/.test(filePath[0]);
}
/**
* Load and parse a JSON config object from a file.
* @param {string} filePath the path to the JSON config file
* @returns {Object} the parsed config object (empty object if there was a parse error)
* @private
*/

@@ -57,11 +71,52 @@ function loadConfig(filePath) {

if (filePath) {
try {
config = yaml.safeLoad(stripComments(fs.readFileSync(filePath, "utf8"))) || {};
} catch (e) {
debug("Error reading YAML file: " + filePath);
e.message = "Cannot read config file: " + filePath + "\nError: " + e.message;
throw e;
if (isFilePath(filePath)) {
try {
config = yaml.safeLoad(stripComments(fs.readFileSync(filePath, "utf8"))) || {};
} catch (e) {
debug("Error reading YAML file: " + filePath);
e.message = "Cannot read config file: " + filePath + "\nError: " + e.message;
throw e;
}
} else {
// it's a package
if (filePath.indexOf("eslint-config-") === -1) {
filePath = "eslint-config-" + filePath;
}
config = require(filePath);
}
// If an `extends` property is defined, it represents a configuration file to use as
// a "parent". Load the referenced file and merge the configuration recursively.
if (config.extends) {
var parentPath = config.extends;
if (isFilePath(parentPath)) {
// If the `extends` path is relative, use the directory of the current configuration
// file as the reference point. Otherwise, use as-is.
parentPath = (!isAbsolutePath(parentPath) ?
path.join(path.dirname(filePath), parentPath) :
parentPath
);
}
// Merge the configuration, ensuring children get preference over the parent
try {
config = util.mergeConfigs(loadConfig(parentPath), config);
} catch (e) {
// If the file referenced by `extends` failed to load, add the path to the
// configuration file that referenced it to the error message so the user is
// able to see where it was referenced from, then re-throw
e.message += "\nReferenced from: " + filePath;
throw e;
}
}
}
return config;

@@ -153,2 +208,3 @@ }

debug("Loading " + localConfigFile);
localConfig = loadConfig(localConfigFile);

@@ -249,3 +305,2 @@

this.baseConfig.format = options.format;
this.useEslintrc = (options.useEslintrc !== false);

@@ -252,0 +307,0 @@

@@ -961,3 +961,3 @@ /**

if (["FunctionDeclaration", "FunctionExpression",
"ArrowFunctionExpression"].indexOf(current.type) >= 0) {
"ArrowFunctionExpression", "SwitchStatement"].indexOf(current.type) >= 0) {
parents.push(current);

@@ -964,0 +964,0 @@ }

@@ -36,2 +36,11 @@ /**

/**
* Check if the token is an punctuator with a value of curly brace
* @param {object} token - Token to check
* @returns {boolean} true if its a curly punctuator
* @private
*/
function isCurlyPunctuator(token) {
return token.value === "{" || token.value === "}";
}

@@ -101,3 +110,3 @@ /**

if (style === "1tbs") {
if (tokens[0].loc.start.line !== tokens[1].loc.start.line) {
if (tokens[0].loc.start.line !== tokens[1].loc.start.line && isCurlyPunctuator(tokens[0]) ) {
context.report(node.alternate, CLOSE_MESSAGE);

@@ -104,0 +113,0 @@ }

@@ -51,3 +51,3 @@ /**

function checkFunction(node) {
var isMethod, starToken, tokenBefore, tokenAfter;
var prevToken, starToken, nextToken;

@@ -58,5 +58,3 @@ if (!node.generator) {

isMethod = !!context.getAncestors().pop().method;
if (isMethod) {
if (node.parent.method || node.parent.type === "MethodDefinition") {
starToken = context.getTokenBefore(node, 1);

@@ -68,11 +66,11 @@ } else {

// Only check before when preceded by `function` keyword
tokenBefore = context.getTokenBefore(starToken);
if (tokenBefore.value === "function") {
checkSpacing("before", tokenBefore, starToken);
prevToken = context.getTokenBefore(starToken);
if (prevToken.value === "function" || prevToken.value === "static") {
checkSpacing("before", prevToken, starToken);
}
// Only check after when followed by an identifier
tokenAfter = context.getTokenAfter(starToken);
if (tokenAfter.type === "Identifier") {
checkSpacing("after", starToken, tokenAfter);
nextToken = context.getTokenAfter(starToken);
if (nextToken.type === "Identifier") {
checkSpacing("after", starToken, nextToken);
}

@@ -79,0 +77,0 @@ }

@@ -38,3 +38,3 @@ /**

function continuesPropertyGroup(lastMember, candidate) {
var groupEndLine = lastMember.loc.end.line,
var groupEndLine = lastMember.loc.start.line,
candidateStartLine = candidate.loc.start.line,

@@ -41,0 +41,0 @@ comments, i;

@@ -70,4 +70,4 @@ /**

var config = context.options[0] || {};
config.newIsCap = config.newIsCap === false ? false : true;
config.capIsNew = config.capIsNew === false ? false : true;
config.newIsCap = config.newIsCap !== false;
config.capIsNew = config.capIsNew !== false;

@@ -74,0 +74,0 @@ var newIsCapExceptions = checkArray(config, "newIsCapExceptions", []).reduce(invert, {});

@@ -24,2 +24,12 @@ /**

var config = context.options[0] || {};
var exceptions = config.exceptions || [];
var modifiedBuiltins = BUILTINS;
if (exceptions.length) {
modifiedBuiltins = BUILTINS.filter(function(builtIn) {
return exceptions.indexOf(builtIn) === -1;
});
}
return {

@@ -43,3 +53,3 @@

BUILTINS.forEach(function(builtin) {
modifiedBuiltins.forEach(function(builtin) {
if (lhs.object.object.name === builtin) {

@@ -69,3 +79,3 @@ context.report(node, builtin + " prototype is read only, properties should not be added.");

object.type === "Identifier" &&
(BUILTINS.indexOf(object.name) > -1) &&
(modifiedBuiltins.indexOf(object.name) > -1) &&
subject.property.name === "prototype") {

@@ -72,0 +82,0 @@

@@ -15,3 +15,4 @@ /**

var irregularWhitespace = /[\u0085\u00A0\ufeff\f\v\u00a0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u200b\u2028\u2029\u202f\u205f\u3000]+/mg;
var irregularWhitespace = /[\u0085\u00A0\ufeff\f\v\u00a0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u200b\u202f\u205f\u3000]+/mg,
irregularLineTerminators = /[\u2028\u2029]/mg;

@@ -34,3 +35,3 @@ // Module store of errors that we have found

if (errorLoc.line >= locStart.line && errorLoc.line <= locEnd.line) {
if (errorLoc.column >= locStart.column && errorLoc.column <= locEnd.column) {
if (errorLoc.column >= locStart.column && (errorLoc.column <= locEnd.column || errorLoc.line < locEnd.line)) {
return false;

@@ -44,3 +45,3 @@ }

/**
* Checks nodes for errors that we are choosing to ignore and calls the relevent methods to remove the errors
* Checks nodes for errors that we are choosing to ignore and calls the relevant methods to remove the errors
* @param {ASTNode} node to check for matching errors.

@@ -53,3 +54,3 @@ * @returns {void}

// If we have irregular characters remove them from the errors list
if (node.value.match(irregularWhitespace)) {
if (node.raw.match(irregularWhitespace) || node.raw.match(irregularLineTerminators)) {
removeStringError(node);

@@ -60,2 +61,55 @@ }

/**
* Checks the program source for irregular whitespace
* @param {ASTNode} node The program node
* @returns {void}
* @private
*/
function checkForIrregularWhitespace(node) {
var sourceLines = context.getSourceLines();
sourceLines.forEach(function (sourceLine, lineIndex) {
var lineNumber = lineIndex + 1,
location,
match;
while ((match = irregularWhitespace.exec(sourceLine)) !== null) {
location = {
line: lineNumber,
column: match.index
};
errors.push([node, location, "Irregular whitespace not allowed"]);
}
});
}
/**
* Checks the program source for irregular line terminators
* @param {ASTNode} node The program node
* @returns {void}
* @private
*/
function checkForIrregularLineTerminators(node) {
var source = context.getSource(),
sourceLines = context.getSourceLines(),
linebreaks = source.match(/\r\n|\r|\n|\u2028|\u2029/g),
lastLineIndex = -1,
lineIndex,
location,
match;
while ((match = irregularLineTerminators.exec(source)) !== null) {
lineIndex = linebreaks.indexOf(match[0], lastLineIndex + 1) || 0;
location = {
line: lineIndex + 1,
column: sourceLines[lineIndex].length
};
errors.push([node, location, "Irregular whitespace not allowed"]);
lastLineIndex = lineIndex;
}
}
return {

@@ -69,22 +123,9 @@ "Program": function (node) {

*/
var sourceLines = context.getSourceLines();
sourceLines.forEach(function (sourceLine, lineIndex) {
var location,
match = irregularWhitespace.exec(sourceLine);
checkForIrregularWhitespace(node);
checkForIrregularLineTerminators(node);
},
if (match !== null) {
location = {
line: lineIndex + 1,
column: match.index
};
errors.push([node, location, "Irregular whitespace not allowed"]);
}
});
},
"Identifier": removeInvalidNodeErrors,
"Literal": removeInvalidNodeErrors,
"Statement": removeInvalidNodeErrors,
"Expression": removeInvalidNodeErrors,
"Program:exit": function () {

@@ -91,0 +132,0 @@

@@ -14,20 +14,3 @@ /**

module.exports = function(context) {
var loopNodeTypes = [
"ForStatement",
"WhileStatement",
"ForInStatement",
"ForOfStatement",
"DoWhileStatement"
];
/**
* Checks if the given node is a loop.
* @param {ASTNode} node The AST node to check.
* @returns {boolean} Whether or not the node is a loop.
*/
function isLoop(node) {
return loopNodeTypes.indexOf(node.type) > -1;
}
/**
* Reports if the given node has an ancestor node which is a loop.

@@ -40,3 +23,27 @@ * @param {ASTNode} node The AST node to check.

if (ancestors.some(isLoop)) {
/**
* Checks if the given node is a loop and current context is in the loop.
* @param {ASTNode} ancestor The AST node to check.
* @param {number} index The index of ancestor in ancestors.
* @returns {boolean} Whether or not the node is a loop and current context is in the loop.
*/
function isInLoop(ancestor, index) {
switch (ancestor.type) {
case "ForStatement":
return ancestor.init !== ancestors[index + 1];
case "ForInStatement":
case "ForOfStatement":
return ancestor.right !== ancestors[index + 1];
case "WhileStatement":
case "DoWhileStatement":
return true;
default:
return false;
}
}
if (ancestors.some(isInLoop)) {
context.report(node, "Don't make functions within a loop");

@@ -43,0 +50,0 @@ }

@@ -37,5 +37,12 @@ /**

});
// swallow the final newline, as some editors add it automatically
// and we don't want it to cause an issue
if (trimmedLines[trimmedLines.length - 1] === "") {
trimmedLines = trimmedLines.slice(0, -1);
}
// Aggregate and count blank lines
do {
lastLocation = currentLocation;
currentLocation = trimmedLines.indexOf("", currentLocation + 1);
while (currentLocation !== -1) {
lastLocation = currentLocation;

@@ -57,3 +64,3 @@ currentLocation = trimmedLines.indexOf("", currentLocation + 1);

}
} while (currentLocation !== -1);
}
}

@@ -60,0 +67,0 @@ };

@@ -14,3 +14,3 @@ /**

var nativeObjects = ["Array", "Boolean", "Date", "decodeURI",
var NATIVE_OBJECTS = ["Array", "Boolean", "Date", "decodeURI",
"decodeURIComponent", "encodeURI", "encodeURIComponent",

@@ -22,7 +22,16 @@ "Error", "eval", "EvalError", "Function", "isFinite",

"Map", "NaN", "Set", "WeakMap", "Infinity", "undefined"];
var config = context.options[0] || {};
var exceptions = config.exceptions || [];
var modifiedNativeObjects = NATIVE_OBJECTS;
if (exceptions.length) {
modifiedNativeObjects = NATIVE_OBJECTS.filter(function(builtIn) {
return exceptions.indexOf(builtIn) === -1;
});
}
return {
"AssignmentExpression": function(node) {
if (nativeObjects.indexOf(node.left.name) >= 0) {
if (modifiedNativeObjects.indexOf(node.left.name) >= 0) {
context.report(node, node.left.name + " is a read-only native object.");

@@ -33,3 +42,3 @@ }

"VariableDeclarator": function(node) {
if (nativeObjects.indexOf(node.id.name) >= 0) {
if (modifiedNativeObjects.indexOf(node.id.name) >= 0) {
context.report(node, "Redefinition of '{{nativeObject}}'.", { nativeObject: node.id.name });

@@ -36,0 +45,0 @@ }

@@ -22,3 +22,2 @@ /**

scope.variables.forEach(function(variable) {
if (variable.identifiers && variable.identifiers.length > 1) {

@@ -54,7 +53,16 @@ variable.identifiers.sort(function(a, b) {

return {
"Program": findVariables,
"FunctionExpression": findVariables,
"FunctionDeclaration": findVariables
};
if (context.ecmaFeatures.blockBindings) {
return {
"Program": findVariables,
"BlockStatement": findVariables,
"SwitchStatement": findVariables
};
} else {
return {
"Program": findVariables,
"FunctionDeclaration": findVariables,
"FunctionExpression": findVariables,
"ArrowFunctionExpression": findVariables
};
}
};

@@ -13,4 +13,9 @@ /**

var TRAILER = "[ \t\u00a0\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u200b\u2028\u2029\u3000]+$";
var BLANK_PREFIX = "[^( |\t)]",
TRAILER = "[ \t\u00a0\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u200b\u2028\u2029\u3000]$";
var options = context.options[0] || {},
skipBlankLines = options.skipBlankLines || false;
//--------------------------------------------------------------------------

@@ -28,3 +33,3 @@ // Public

var src = context.getSource(),
re = new RegExp(TRAILER, "mg"),
re = new RegExp((skipBlankLines ? BLANK_PREFIX : "") + TRAILER, "mg"),
match, lines, location;

@@ -31,0 +36,0 @@

@@ -30,5 +30,10 @@ /**

//--------------------------------------------------------------------------
// Helpers
//--------------------------------------------------------------------------
/**
* Determines if a given variable is being exported from a module.
* @param {Variable} variable EScope variable object.
* @param {Variable} variable - EScope variable object.
* @returns {boolean} True if the variable is exported, false if not.

@@ -56,3 +61,3 @@ * @private

* Determines if a reference is a read operation.
* @param {Reference} ref - an escope Reference
* @param {Reference} ref - An escope Reference
* @returns {Boolean} whether the given reference represents a read operation

@@ -66,11 +71,17 @@ * @private

/**
* Determine if an identifier is referencing the enclosing function name.
* @param {Reference} ref The reference to check.
* Determine if an identifier is referencing an enclosing function name.
* @param {Reference} ref - The reference to check.
* @param {ASTNode[]} nodes - The candidate function nodes.
* @returns {boolean} True if it's a self-reference, false if not.
* @private
*/
function isSelfReference(ref) {
function isSelfReference(ref, nodes) {
var scope = ref.from;
if (ref.from.type === "function" && ref.from.block.id) {
return ref.identifier.name === ref.from.block.id.name;
while (scope != null) {
if (nodes.indexOf(scope.block) >= 0) {
return true;
}
scope = scope.upper;
}

@@ -82,11 +93,22 @@

/**
* Determines if a reference should be counted as a read. A reference should
* be counted only if it's a read and it's not a reference to the containing
* function declaration name.
* @param {Reference} ref The reference to check.
* @returns {boolean} True if it's a value read reference, false if not.
* @private
* Determines if the variable is used.
* @param {Variable} variable - The variable to check.
* @param {Reference[]} [references=variable.references] - The variable references to check.
* @returns {boolean} True if the variable is used
*/
function isValidReadRef(ref) {
return isReadRef(ref) && !isSelfReference(ref);
function isUsedVariable(variable, references) {
var functionNodes = variable.defs.filter(function (def) {
return def.type === "FunctionName";
}).map(function (def) {
return def.node;
}),
isFunctionDefinition = functionNodes.length > 0;
if (!references) {
references = variable.references;
}
return references.some(function (ref) {
return isReadRef(ref) && !(isFunctionDefinition && isSelfReference(ref, functionNodes));
});
}

@@ -139,3 +161,3 @@

if (variables[i].references.filter(isValidReadRef).length === 0 && !isExported(variables[i])) {
if (!isUsedVariable(variables[i]) && !isExported(variables[i])) {
unused.push(variables[i]);

@@ -149,2 +171,6 @@ }

//--------------------------------------------------------------------------
// Public
//--------------------------------------------------------------------------
return {

@@ -158,11 +184,30 @@ "Program:exit": function(programNode) {

if (config.vars === "all") {
var unresolvedRefs = globalScope.through.filter(isValidReadRef).map(function(ref) {
return ref.identifier.name;
});
var ref, name;
// Avoid inherited properties that could induce false positives (e.g. "constructor")
var unresolvedRefs = Object.create(null);
// Search for read references, and store them in a dictionary by name
for (i = 0, l = globalScope.through.length; i < l; ++i) {
ref = globalScope.through[i];
name = ref.identifier.name;
if (isReadRef(ref)) {
if (!unresolvedRefs[name]) {
unresolvedRefs[name] = [];
}
unresolvedRefs[name].push(ref);
}
}
for (i = 0, l = globalScope.variables.length; i < l; ++i) {
if (unresolvedRefs.indexOf(globalScope.variables[i].name) < 0 &&
!globalScope.variables[i].eslintUsed && !isExported(globalScope.variables[i])) {
name = globalScope.variables[i].name;
var isUsed = unresolvedRefs[name] &&
isUsedVariable(globalScope.variables[i], unresolvedRefs[name]);
if (!isUsed && !globalScope.variables[i].eslintUsed &&
!isExported(globalScope.variables[i])) {
unused.push(globalScope.variables[i]);
}
}

@@ -169,0 +214,0 @@ }

@@ -21,2 +21,9 @@ /**

/**
* Finds variable declarations in a given scope.
* @param {string} name The variable name to find.
* @param {Scope} scope The scope to search in.
* @returns {Object} The variable declaration object.
* @private
*/
function findDeclaration(name, scope) {

@@ -35,4 +42,9 @@ // try searching in the current scope first

function findVariables() {
var scope = context.getScope();
/**
* Finds and validates all variables in a given scope.
* @param {Scope} scope The scope object.
* @returns {void}
* @private
*/
function findVariablesInScope(scope) {
var typeOption = context.options[0];

@@ -63,4 +75,24 @@

/**
* Validates variables inside of a node's scope.
* @param {ASTNode} node The node to check.
* @returns {void}
* @private
*/
function findVariables() {
var scope = context.getScope();
findVariablesInScope(scope);
}
return {
"Program": findVariables,
"Program": function() {
var scope = context.getScope();
findVariablesInScope(scope);
// both Node.js and Modules have an extra scope
if (context.ecmaFeatures.globalReturn || context.ecmaFeatures.modules) {
findVariablesInScope(scope.childScopes[0]);
}
},
"FunctionExpression": findVariables,

@@ -67,0 +99,0 @@ "FunctionDeclaration": findVariables,

@@ -22,3 +22,3 @@ /**

function checkFunction(node) {
var previousToken, nextToken;
var previousToken, nextToken, isCall;

@@ -31,9 +31,28 @@ if (node.type === "ArrowFunctionExpression" &&

if (!/CallExpression|NewExpression/.test(node.parent.type)) {
previousToken = context.getTokenBefore(node);
nextToken = context.getTokenAfter(node);
if (previousToken.value === "(" && nextToken.value === ")") {
context.report(node, "Wrapping non-IIFE function literals in parens is unnecessary.");
// (function() {}).foo
if (node.parent.type === "MemberExpression" && node.parent.object === node) {
return;
}
// (function(){})()
isCall = /CallExpression|NewExpression/.test(node.parent.type);
if (isCall && node.parent.callee === node) {
return;
}
previousToken = context.getTokenBefore(node);
nextToken = context.getTokenAfter(node);
// f(function(){}) and new f(function(){})
if (isCall) {
// if the previousToken is right after the callee
if (node.parent.callee.range[1] === previousToken.range[0]) {
return;
}
}
if (previousToken.value === "(" && nextToken.value === ")") {
context.report(node, "Wrapping non-IIFE function literals in parens is unnecessary.");
}
}

@@ -40,0 +59,0 @@

@@ -47,14 +47,14 @@ /**

if (node.value.type === "ArrowFunctionExpression" && APPLY_TO_METHODS) {
if (node.value.type === "FunctionExpression" && APPLY_TO_METHODS) {
// {x: ()=>{}} should be written as {x() {}}
context.report(node, "Expected method shorthand.");
} else if (node.value.type === "FunctionExpression" && APPLY_TO_METHODS) {
// {x: function(){}} should be written as {x() {}}
context.report(node, "Expected method shorthand.");
} else if (node.key.name === node.value.name && APPLY_TO_PROPS) {
} else if (node.value.type === "Identifier" && node.key.name === node.value.name && APPLY_TO_PROPS) {
// {x: x} should be written as {x}
context.report(node, "Expected property shorthand.");
} else if (node.value.type === "Identifier" && node.key.type === "Literal" && node.key.value === node.value.name && APPLY_TO_PROPS) {
// {"x": x} should be written as {x}
context.report(node, "Expected property shorthand.");
}

@@ -61,0 +61,0 @@ }

/**
* @fileoverview A rule to ensure the use of a single variable declaration.
* @fileoverview A rule to control the use of single variable declarations.
* @author Ian Christian Myers
* @copyright 2015 Ian VanSchooten. All rights reserved.
* @copyright 2015 Joey Baker. All rights reserved.

@@ -17,12 +18,52 @@ * @copyright 2015 Danny Fritz. All rights reserved.

var MODE = context.options[0] || "always";
var options = {};
var MODE_ALWAYS = "always",
MODE_NEVER = "never";
// simple options configuration with just a string or no option
if (typeof context.options[0] === "string" || context.options[0] == null) {
options.var = MODE;
options.let = MODE;
options.const = MODE;
} else {
options = context.options[0];
var mode = context.options[0];
var options = {
};
if (typeof mode === "string") { // simple options configuration with just a string
options.var = { uninitialized: mode, initialized: mode};
options.let = { uninitialized: mode, initialized: mode};
options.const = { uninitialized: mode, initialized: mode};
} else if (typeof mode === "object") { // options configuration is an object
if (mode.hasOwnProperty("var") && typeof mode.var === "string") {
options.var = { uninitialized: mode.var, initialized: mode.var};
}
if (mode.hasOwnProperty("let") && typeof mode.let === "string") {
options.let = { uninitialized: mode.let, initialized: mode.let};
}
if (mode.hasOwnProperty("const") && typeof mode.const === "string") {
options.const = { uninitialized: mode.const, initialized: mode.const};
}
if (mode.hasOwnProperty("uninitialized")) {
if (!options.var) {
options.var = {};
}
if (!options.let) {
options.let = {};
}
if (!options.const) {
options.const = {};
}
options.var.uninitialized = mode.uninitialized;
options.let.uninitialized = mode.uninitialized;
options.const.uninitialized = mode.uninitialized;
}
if (mode.hasOwnProperty("initialized")) {
if (!options.var) {
options.var = {};
}
if (!options.let) {
options.let = {};
}
if (!options.const) {
options.const = {};
}
options.var.initialized = mode.initialized;
options.let.initialized = mode.initialized;
options.const.initialized = mode.initialized;
}
}

@@ -43,3 +84,6 @@

function startBlock() {
blockStack.push({let: false, const: false});
blockStack.push({
let: {initialized: false, uninitialized: false},
const: {initialized: false, uninitialized: false}
});
}

@@ -53,3 +97,3 @@

function startFunction() {
functionStack.push(false);
functionStack.push({initialized: false, uninitialized: false});
startBlock();

@@ -78,12 +122,20 @@ }

/**
* Determines if there is more than one var statement in the current scope.
* @returns {boolean} Returns true if it is the first var declaration, false if not.
* Records whether initialized or uninitialized variables are defined in current scope.
* @param {string} statementType node.kind, one of: "var", "let", or "const"
* @param {ASTNode[]} declarations List of declarations
* @param {Object} currentScope The scope being investigated
* @returns {void}
* @private
*/
function hasOnlyOneVar() {
if (functionStack[functionStack.length - 1]) {
return true;
} else {
functionStack[functionStack.length - 1] = true;
return false;
function recordTypes(statementType, declarations, currentScope) {
for (var i = 0; i < declarations.length; i++) {
if (declarations[i].init === null) {
if (options[statementType] && options[statementType].uninitialized === MODE_ALWAYS) {
currentScope.uninitialized = true;
}
} else {
if (options[statementType] && options[statementType].initialized === MODE_ALWAYS) {
currentScope.initialized = true;
}
}
}

@@ -93,29 +145,52 @@ }

/**
* Determines if there is more than one let statement in the current scope.
* @returns {boolean} Returns true if it is the first let declaration, false if not.
* Counts the number of initialized and uninitialized declarations in a list of declarations
* @param {ASTNode[]} declarations List of declarations
* @returns {Object} Counts of 'uninitialized' and 'initialized' declarations
* @private
*/
function hasOnlyOneLet() {
if (blockStack[blockStack.length - 1].let) {
return true;
} else {
blockStack[blockStack.length - 1].let = true;
return false;
function countDeclarations(declarations) {
var counts = { uninitialized: 0, initialized: 0 };
for (var i = 0; i < declarations.length; i++) {
if (declarations[i].init === null) {
counts.uninitialized++;
} else {
counts.initialized++;
}
}
return counts;
}
/**
* Determines if there is more than one const statement in the current scope.
* @returns {boolean} Returns true if it is the first const declaration, false if not.
* Determines if there is more than one var statement in the current scope.
* @param {string} statementType node.kind, one of: "var", "let", or "const"
* @param {ASTNode[]} declarations List of declarations
* @returns {boolean} Returns true if it is the first var declaration, false if not.
* @private
*/
function hasOnlyOneConst() {
if (blockStack[blockStack.length - 1].const) {
return true;
} else {
blockStack[blockStack.length - 1].const = true;
return false;
function hasOnlyOneStatement(statementType, declarations) {
var currentScope;
var declarationCounts = countDeclarations(declarations);
if (statementType === "var") {
currentScope = functionStack[functionStack.length - 1];
} else if (statementType === "let") {
currentScope = blockStack[blockStack.length - 1].let;
} else if (statementType === "const") {
currentScope = blockStack[blockStack.length - 1].const;
}
if (declarationCounts.uninitialized > 0) {
if (options[statementType] && options[statementType].uninitialized === MODE_ALWAYS && currentScope.uninitialized) {
return false;
}
}
if (declarationCounts.initialized > 0) {
if (options[statementType] && options[statementType].initialized === MODE_ALWAYS && currentScope.initialized) {
return false;
}
}
recordTypes(statementType, declarations, currentScope);
return true;
}
//--------------------------------------------------------------------------

@@ -135,35 +210,35 @@ // Public API

"VariableDeclaration": function(node) {
var declarationCount = node.declarations.length;
var type = node.kind;
var declarations = node.declarations;
var declarationCounts = countDeclarations(declarations);
if (node.kind === "var") {
if (options.var === "never") {
if (declarationCount > 1) {
context.report(node, "Split 'var' declaration into multiple statements.");
}
// always
if (!hasOnlyOneStatement(type, declarations)) {
if (options[type] && options[type].initialized === MODE_ALWAYS && options[type].uninitialized === MODE_ALWAYS) {
context.report(node, "Combine this with the previous '" + type + "' statement.");
} else {
if (hasOnlyOneVar()) {
context.report(node, "Combine this with the previous 'var' statement.");
if (options[type] && options[type].initialized === MODE_ALWAYS) {
context.report(node, "Combine this with the previous '" + type + "' statement with initialized variables.");
}
if (options[type] && options[type].uninitialized === MODE_ALWAYS) {
context.report(node, "Combine this with the previous '" + type + "' statement with uninitialized variables.");
}
}
} else if (node.kind === "let") {
if (options.let === "never") {
if (declarationCount > 1) {
context.report(node, "Split 'let' declaration into multiple statements.");
}
// never
if (options[type] && options[type].initialized === MODE_NEVER && options[type].uninitialized === MODE_NEVER) {
if ((declarationCounts.uninitialized + declarationCounts.initialized) > 1) {
context.report(node, "Split '" + type + "' declarations into multiple statements.");
}
} else {
if (options[type] && options[type].initialized === MODE_NEVER) {
if (declarationCounts.initialized > 1) {
context.report(node, "Split initialized '" + type + "' declarations into multiple statements.");
}
} else {
if (hasOnlyOneLet()) {
context.report(node, "Combine this with the previous 'let' statement.");
}
}
} else if (node.kind === "const") {
if (options.const === "never") {
if (declarationCount > 1) {
context.report(node, "Split 'const' declaration into multiple statements.");
if (options[type] && options[type].uninitialized === MODE_NEVER) {
if (declarationCounts.uninitialized > 1) {
context.report(node, "Split uninitialized '" + type + "' declarations into multiple statements.");
}
} else {
if (hasOnlyOneConst()) {
context.report(node, "Combine this with the previous 'const' statement.");
}
}
}

@@ -170,0 +245,0 @@ },

@@ -83,2 +83,10 @@ /**

}, "'" + operator + "' should be placed at the end of the line.");
} else if (style === "none") {
context.report(node, {
line: operatorToken.loc.end.line,
column: operatorToken.loc.end.column
}, "There should be no line break before or after '" + operator + "'");
}

@@ -85,0 +93,0 @@ }

@@ -29,3 +29,3 @@ /**

/**
* Determines whether two adjacent tokens are have whitespace between them.
* Determines whether two adjacent tokens have whitespace between them.
* @param {Object} left - The left token object.

@@ -32,0 +32,0 @@ * @param {Object} right - The right token object.

@@ -233,3 +233,3 @@ /**

// don't do anything for namespace or default imports
if (firstSpecifier.type === "ImportSpecifier" && lastSpecifier.type === "ImportSpecifier") {
if (firstSpecifier && lastSpecifier && firstSpecifier.type === "ImportSpecifier" && lastSpecifier.type === "ImportSpecifier") {
var first = context.getTokenBefore(firstSpecifier),

@@ -246,2 +246,5 @@ second = context.getFirstToken(firstSpecifier),

ExportNamedDeclaration: function(node) {
if (!node.specifiers.length) {
return;
}

@@ -248,0 +251,0 @@ var firstSpecifier = node.specifiers[0],

@@ -24,3 +24,3 @@ /**

// these both default to true, so you have to explicitly make them false
requireReturn = options.requireReturn === false ? false : true,
requireReturn = options.requireReturn !== false,
requireParamDescription = options.requireParamDescription !== false,

@@ -27,0 +27,0 @@ requireReturnDescription = options.requireReturnDescription !== false;

{
"name": "eslint",
"version": "0.20.0",
"version": "0.21.0",
"author": "Nicholas C. Zakas <nicholas+npm@nczconsulting.com>",

@@ -20,3 +20,3 @@ "description": "An AST-based pattern checker for JavaScript.",

"perf": "node Makefile.js perf",
"profile": "beefy tests/bench/bench.js --open -- -t brfs -t ./tests/bench/xform-rules.js",
"profile": "beefy tests/bench/bench.js --open -- -t brfs -t ./tests/bench/xform-rules.js -r espree",
"coveralls": "cat ./coverage/lcov.info | coveralls"

@@ -43,3 +43,3 @@ },

"escape-string-regexp": "^1.0.2",
"escope": "^3.0.0",
"escope": "^3.0.1",
"espree": "^2.0.1",

@@ -49,2 +49,3 @@ "estraverse": "^2.0.0",

"globals": "^6.1.0",
"inquirer": "^0.8.2",
"js-yaml": "^3.2.5",

@@ -55,2 +56,3 @@ "minimatch": "^2.0.1",

"optionator": "^0.5.0",
"path-is-absolute": "^1.0.0",
"strip-json-comments": "~1.0.1",

@@ -57,0 +59,0 @@ "text-table": "~0.2.0",

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc