Comparing version 1.1.0 to 1.2.0
@@ -29,2 +29,12 @@ /** | ||
}, | ||
commonjs: { | ||
globals: { | ||
module: false, | ||
require: false, | ||
exports: false | ||
}, | ||
ecmaFeatures: { | ||
globalReturn: true | ||
} | ||
}, | ||
worker: { | ||
@@ -31,0 +41,0 @@ globals: globals.worker |
@@ -19,2 +19,3 @@ { | ||
"no-div-regex": 0, | ||
"no-dupe-class-members": 0, | ||
"no-dupe-keys": 2, | ||
@@ -111,2 +112,3 @@ "no-dupe-args": 2, | ||
"block-scoped-var": 0, | ||
"block-spacing": 0, | ||
"brace-style": [0, "1tbs"], | ||
@@ -153,5 +155,7 @@ "callback-return": 0, | ||
"padded-blocks": 0, | ||
"prefer-arrow-callback": 0, | ||
"prefer-const": 0, | ||
"prefer-spread": 0, | ||
"prefer-reflect": 0, | ||
"prefer-template": 0, | ||
"quote-props": 0, | ||
@@ -158,0 +162,0 @@ "quotes": [0, "double"], |
@@ -83,5 +83,10 @@ /** | ||
validateRule.errors.forEach(function(error) { | ||
message.push( | ||
"\tValue \"", error.value, "\" ", error.message, ".\n" | ||
); | ||
if (error.field === "data[\"0\"]") { // better error for severity | ||
message.push( | ||
"\tSeverity should be one of the following: 0 = off, 1 = warning, 2 = error (you passed \"", error.value, "\").\n"); | ||
} else { | ||
message.push( | ||
"\tValue \"", error.value, "\" ", error.message, ".\n" | ||
); | ||
} | ||
}); | ||
@@ -88,0 +93,0 @@ |
@@ -25,2 +25,3 @@ /** | ||
isAbsolutePath = require("path-is-absolute"), | ||
isResolvable = require("is-resolvable"), | ||
validator = require("./config-validator"), | ||
@@ -59,49 +60,78 @@ pathIsInside = require("path-is-inside"); | ||
function isFilePath(filePath) { | ||
return isAbsolutePath(filePath) || !/\w|@/.test(filePath[0]); | ||
return isAbsolutePath(filePath) || !/\w|@/.test(filePath.charAt(0)); | ||
} | ||
/** | ||
* Load and parse a JSON config object from a file. | ||
* Check if item is an javascript object | ||
* @param {*} item object to check for | ||
* @returns {boolean} True if its an object | ||
* @private | ||
*/ | ||
function isObject(item) { | ||
return typeof item === "object" && !Array.isArray(item) && item !== null; | ||
} | ||
/** | ||
* Read the config from the config JSON 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) | ||
* @returns {Object} config object | ||
* @private | ||
*/ | ||
function loadConfig(filePath) { | ||
function readConfigFromFile(filePath) { | ||
var config = {}; | ||
if (filePath) { | ||
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; | ||
} | ||
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; | ||
} | ||
if (path.basename(filePath) === PACKAGE_CONFIG_FILENAME) { | ||
config = config[PACKAGE_CONFIG_FIELD_NAME] || {}; | ||
} | ||
if (path.basename(filePath) === PACKAGE_CONFIG_FILENAME) { | ||
config = config[PACKAGE_CONFIG_FIELD_NAME] || {}; | ||
} else { | ||
// it's a package | ||
if (filePath.charAt(0) === "@") { | ||
// it's a scoped package | ||
// package name is "eslint-config", or just a username | ||
var scopedPackageShortcutRegex = /^(@[^\/]+)(?:\/(?:eslint-config)?)?$/; | ||
if (scopedPackageShortcutRegex.test(filePath)) { | ||
filePath = filePath.replace(scopedPackageShortcutRegex, "$1/eslint-config"); | ||
} else if (filePath.split("/")[1].indexOf("eslint-config-") !== 0) { | ||
// for scoped packages, insert the eslint-config after the first / | ||
filePath = filePath.replace(/^@([^\/]+)\/(.*)$/, "@$1/eslint-config-$2"); | ||
} | ||
} else if (filePath.indexOf("eslint-config-") !== 0) { | ||
filePath = "eslint-config-" + filePath; | ||
} | ||
} else { | ||
config = util.mergeConfigs(config, require(filePath)); | ||
} | ||
return config; | ||
} | ||
// it's a package | ||
/** | ||
* Load and parse a JSON config object from a file. | ||
* @param {string|Object} configToLoad the path to the JSON config file or the config object itself. | ||
* @returns {Object} the parsed config object (empty object if there was a parse error) | ||
* @private | ||
*/ | ||
function loadConfig(configToLoad) { | ||
var config = {}; | ||
var filePath = ""; | ||
if (filePath.charAt(0) === "@") { | ||
// it's a scoped package | ||
if (configToLoad) { | ||
// package name is "eslint-config", or just a username | ||
var scopedPackageShortcutRegex = /^(@[^\/]+)(?:\/(?:eslint-config)?)?$/; | ||
if (scopedPackageShortcutRegex.test(filePath)) { | ||
filePath = filePath.replace(scopedPackageShortcutRegex, "$1/eslint-config"); | ||
} else if (filePath.split("/")[1].indexOf("eslint-config-") !== 0) { | ||
// for scoped packages, insert the eslint-config after the first / | ||
filePath = filePath.replace(/^@([^\/]+)\/(.*)$/, "@$1/eslint-config-$2"); | ||
} | ||
} else if (filePath.indexOf("eslint-config-") !== 0) { | ||
filePath = "eslint-config-" + filePath; | ||
} | ||
config = util.mergeConfigs(config, require(filePath)); | ||
if (isObject(configToLoad)) { | ||
config = configToLoad; | ||
} else { | ||
filePath = configToLoad; | ||
config = readConfigFromFile(filePath); | ||
} | ||
@@ -324,3 +354,3 @@ | ||
this.baseConfig = options.baseConfig || { rules: {} }; | ||
this.baseConfig = options.baseConfig ? loadConfig(options.baseConfig) : { rules: {} }; | ||
@@ -346,3 +376,7 @@ this.useEslintrc = (options.useEslintrc !== false); | ||
debug("Using command line config " + useConfig); | ||
this.useSpecificConfig = loadConfig(path.resolve(process.cwd(), useConfig)); | ||
if (isResolvable(useConfig) || isResolvable("eslint-config-" + useConfig) || useConfig.charAt(0) === "@") { | ||
this.useSpecificConfig = loadConfig(useConfig); | ||
} else { | ||
this.useSpecificConfig = loadConfig(path.resolve(process.cwd(), useConfig)); | ||
} | ||
} | ||
@@ -414,4 +448,10 @@ } | ||
// Step 9: Merge in command line plugins | ||
if (this.options.plugins) { | ||
debug("Merging command line plugins"); | ||
pluginConfig = getPluginsConfig(this.options.plugins); | ||
config = util.mergeConfigs(config, { plugins: this.options.plugins }); | ||
} | ||
// Step 9: Merge in plugin specific rules in reverse | ||
// Step 10: Merge in plugin specific rules in reverse | ||
if (config.plugins) { | ||
@@ -418,0 +458,0 @@ pluginConfig = getPluginsConfig(config.plugins); |
@@ -58,3 +58,4 @@ /** | ||
"message=\"" + xmlEscape(message.message) + | ||
(message.ruleId ? " (" + message.ruleId + ")" : "") + "\" />"; | ||
(message.ruleId ? " (" + message.ruleId + ")" : "") + "\" " + | ||
"source=\"" + (message.ruleId ? xmlEscape("eslint.rules." + message.ruleId) : "") + "\" />"; | ||
}); | ||
@@ -61,0 +62,0 @@ |
@@ -33,3 +33,3 @@ /** | ||
type: "path::String", | ||
description: "Use configuration from this file" | ||
description: "Use configuration from this file or sharable config" | ||
}, { | ||
@@ -36,0 +36,0 @@ option: "rulesdir", |
@@ -26,3 +26,3 @@ /** | ||
// as-needed: x => x | ||
if (asNeeded && node.params.length === 1) { | ||
if (asNeeded && node.params.length === 1 && node.params[0].type === "Identifier") { | ||
if (token.type === "Punctuator" && token.value === "(") { | ||
@@ -29,0 +29,0 @@ context.report(node, asNeededMessage); |
@@ -17,2 +17,3 @@ /** | ||
var OPEN_MESSAGE = "Opening curly brace does not appear on the same line as controlling statement.", | ||
OPEN_MESSAGE_ALLMAN = "Opening curly brace appears on the same line as controlling statement.", | ||
BODY_MESSAGE = "Statement inside of curly braces should be on next line.", | ||
@@ -61,3 +62,2 @@ CLOSE_MESSAGE = "Closing curly brace does not appear on the same line as the subsequent block.", | ||
var block = node[blockProp], previousToken, curlyToken, curlyTokenEnd, curlyTokensOnSameLine; | ||
block = node[blockProp]; | ||
@@ -71,4 +71,6 @@ if (isBlock(block)) { | ||
if (previousToken.loc.start.line !== curlyToken.loc.start.line) { | ||
if (style !== "allman" && previousToken.loc.start.line !== curlyToken.loc.start.line) { | ||
context.report(node, OPEN_MESSAGE); | ||
} else if (style === "allman" && previousToken.loc.start.line === curlyToken.loc.start.line && !params.allowSingleLine) { | ||
context.report(node, OPEN_MESSAGE_ALLMAN); | ||
} else if (block.body.length && params.allowSingleLine) { | ||
@@ -220,3 +222,3 @@ | ||
{ | ||
"enum": ["1tbs", "stroustrup"] | ||
"enum": ["1tbs", "stroustrup", "allman"] | ||
}, | ||
@@ -223,0 +225,0 @@ { |
@@ -26,15 +26,28 @@ /** | ||
var SUPPORTED_EXPRESSIONS = { | ||
"AssignmentExpression": function(parent, node) { | ||
return parent.left.type === "MemberExpression" && | ||
!parent.left.computed && parent.left.property === node; | ||
"MemberExpression": function(parent) { | ||
return !parent.computed && ( | ||
// regular property assignment | ||
parent.parent.left === parent || ( | ||
// or the last identifier in an ObjectPattern destructuring | ||
parent.parent.type === "Property" && parent.parent.value === parent && | ||
parent.parent.parent.type === "ObjectPattern" && parent.parent.parent.parent.left === parent.parent.parent | ||
) | ||
); | ||
}, | ||
"AssignmentPattern": function(parent, node) { | ||
return parent.left === node; | ||
}, | ||
"VariableDeclarator": function(parent, node) { | ||
return parent.id === node; | ||
}, | ||
"ObjectExpression": function(parent, node) { | ||
return node.parent.key === node; | ||
"Property": function(parent, node) { | ||
return parent.key === node; | ||
}, | ||
"ImportDefaultSpecifier": true, | ||
"RestElement": true, | ||
"FunctionExpression": true, | ||
"ArrowFunctionExpression": true, | ||
"ClassDeclaration": true, | ||
"FunctionDeclaration": true, | ||
"MethodDefinition": true, | ||
"CatchClause": true | ||
@@ -46,4 +59,3 @@ }; | ||
var name = node.name; | ||
var effectiveParent = (node.parent.type === "MemberExpression" || node.parent.type === "Property") ? | ||
node.parent.parent : node.parent; | ||
var parent = node.parent; | ||
@@ -56,5 +68,5 @@ var isShort = name.length < minLength; | ||
var isValidExpression = SUPPORTED_EXPRESSIONS[effectiveParent.type]; | ||
var isValidExpression = SUPPORTED_EXPRESSIONS[parent.type]; | ||
if (isValidExpression && (isValidExpression === true || isValidExpression(effectiveParent, node))) { | ||
if (isValidExpression && (isValidExpression === true || isValidExpression(parent, node))) { | ||
context.report( | ||
@@ -61,0 +73,0 @@ node, |
@@ -39,10 +39,15 @@ /** | ||
var MESSAGE = "Expected indentation of {{needed}} characters but found {{gotten}}."; | ||
var MESSAGE = "Expected indentation of {{needed}} {{type}} {{characters}} but found {{gotten}}."; | ||
var DEFAULT_VARIABLE_INDENT = 1; | ||
var extraColumnStart = 0; | ||
var indentType = "spaces"; | ||
var indentType = "space"; | ||
var indentSize = 4; | ||
var options = { | ||
SwitchCase: 0, | ||
VariableDeclarator: 1 | ||
VariableDeclarator: { | ||
var: DEFAULT_VARIABLE_INDENT, | ||
let: DEFAULT_VARIABLE_INDENT, | ||
const: DEFAULT_VARIABLE_INDENT | ||
} | ||
}; | ||
@@ -56,3 +61,3 @@ | ||
indentSize = context.options[0]; | ||
indentType = "spaces"; | ||
indentType = "space"; | ||
} | ||
@@ -62,3 +67,13 @@ | ||
var opts = context.options[1]; | ||
assign(options, opts); | ||
options.SwitchCase = opts.SwitchCase || 0; | ||
var variableDeclaratorRules = opts.VariableDeclarator; | ||
if (typeof variableDeclaratorRules === "number") { | ||
options.VariableDeclarator = { | ||
var: variableDeclaratorRules, | ||
let: variableDeclaratorRules, | ||
const: variableDeclaratorRules | ||
}; | ||
} else if (typeof variableDeclaratorRules === "object") { | ||
assign(options.VariableDeclarator, variableDeclaratorRules); | ||
} | ||
} | ||
@@ -70,6 +85,29 @@ } | ||
/** | ||
* Reports a given indent violation and properly pluralizes the message | ||
* @param {ASTNode} node Node violating the indent rule | ||
* @param {int} needed Expected indentation character count | ||
* @param {int} gotten Indentation character count in the actual node/code | ||
* @param {Object=} loc Error line and column location | ||
* @returns {void} | ||
*/ | ||
function report(node, needed, gotten, loc) { | ||
var msgContext = { | ||
needed: needed, | ||
type: indentType, | ||
characters: needed === 1 ? "character" : "characters", | ||
gotten: gotten | ||
}; | ||
if (loc) { | ||
context.report(node, loc, MESSAGE, msgContext); | ||
} else { | ||
context.report(node, MESSAGE, msgContext); | ||
} | ||
} | ||
/** | ||
* Get node indent | ||
* @param {ASTNode} node Node to examine | ||
* @param {boolean} byLastLine get indent of node's last line | ||
* @param {boolean} excludeCommas skip comma on start of line | ||
* @param {ASTNode|Token} node Node to examine | ||
* @param {boolean} [byLastLine=false] get indent of node's last line | ||
* @param {boolean} [excludeCommas=false] skip comma on start of line | ||
* @returns {int} Indent | ||
@@ -92,3 +130,3 @@ */ | ||
var regExp; | ||
if (indentType === "spaces") { | ||
if (indentType === "space") { | ||
regExp = new RegExp("^[ " + skip + "]+"); | ||
@@ -104,9 +142,10 @@ } else { | ||
/** | ||
* Checks node is the first in its own start line. | ||
* Checks node is the first in its own start line. By default it looks by start line. | ||
* @param {ASTNode} node The node to check | ||
* @param {boolean} [byEndLocation=false] Lookup based on start position or end | ||
* @returns {boolean} true if its the first in the its start line | ||
*/ | ||
function isNodeFirstInLine(node) { | ||
var firstToken = context.getTokenBefore(node), | ||
startLine = node.loc.start.line, | ||
function isNodeFirstInLine(node, byEndLocation) { | ||
var firstToken = byEndLocation === true ? context.getLastToken(node, 1) : context.getTokenBefore(node), | ||
startLine = byEndLocation === true ? node.loc.end.line : node.loc.start.line, | ||
endLine = firstToken ? firstToken.loc.end.line : -1; | ||
@@ -121,3 +160,3 @@ | ||
* @param {int} indent needed indent | ||
* @param {boolean} excludeCommas skip comma on start of line | ||
* @param {boolean} [excludeCommas=false] skip comma on start of line | ||
* @returns {void} | ||
@@ -128,4 +167,7 @@ */ | ||
var nodeIndent = getNodeIndent(node, false, excludeCommas); | ||
if (nodeIndent !== indent && isNodeFirstInLine(node)) { | ||
context.report(node, MESSAGE, { gotten: nodeIndent, needed: indent }); | ||
if ( | ||
node.type !== "ArrayExpression" && node.type !== "ObjectExpression" && | ||
nodeIndent !== indent && isNodeFirstInLine(node) | ||
) { | ||
report(node, indent, nodeIndent); | ||
} | ||
@@ -143,8 +185,8 @@ }); | ||
var endIndent = getNodeIndent(node, true); | ||
if (endIndent !== lastLineIndent) { | ||
context.report( | ||
if (endIndent !== lastLineIndent && isNodeFirstInLine(node, true)) { | ||
report( | ||
node, | ||
{ line: node.loc.end.line, column: node.loc.end.column }, | ||
MESSAGE, | ||
{ gotten: endIndent, needed: lastLineIndent } | ||
lastLineIndent, | ||
endIndent, | ||
{ line: node.loc.end.line, column: node.loc.end.column } | ||
); | ||
@@ -163,7 +205,7 @@ } | ||
if (startIndent !== firstLineIndent && isNodeFirstInLine(node)) { | ||
context.report( | ||
report( | ||
node, | ||
{ line: node.loc.start.line, column: node.loc.start.column }, | ||
MESSAGE, | ||
{ gotten: startIndent, needed: firstLineIndent } | ||
firstLineIndent, | ||
startIndent, | ||
{ line: node.loc.start.line, column: node.loc.start.column } | ||
); | ||
@@ -239,17 +281,32 @@ } | ||
/** | ||
* Returns the VariableDeclarator based on the current node | ||
* if not present then return null | ||
* @param {ASTNode} node node to examine | ||
* @returns {ASTNode|void} if found then node otherwise null | ||
*/ | ||
function getVariableDeclaratorNode(node) { | ||
var parent = node.parent; | ||
while (parent.type !== "VariableDeclarator" && parent.type !== "Program") { | ||
parent = parent.parent; | ||
} | ||
return parent.type === "VariableDeclarator" ? parent : null; | ||
} | ||
/** | ||
* Check to see if the node is part of the multi-line variable declaration. | ||
* Also if its on the same line as the parent | ||
* Also if its on the same line as the varNode | ||
* @param {ASTNode} node node to check | ||
* @returns {boolean} True if all the above condition staisfy | ||
* @param {ASTNode} varNode variable declaration node to check against | ||
* @returns {boolean} True if all the above condition satisfy | ||
*/ | ||
function isNodeInVarOnTop(node) { | ||
var parent = node.parent.type === "VariableDeclarator" ? node.parent : node.parent.parent; | ||
return parent.type === "VariableDeclarator" && | ||
parent.parent.loc.start.line === node.loc.start.line && | ||
parent.parent.declarations.length > 1; | ||
function isNodeInVarOnTop(node, varNode) { | ||
return varNode && | ||
varNode.parent.loc.start.line === node.loc.start.line && | ||
varNode.parent.declarations.length > 1; | ||
} | ||
/** | ||
* Check to see if the first element inside an array ia an object and on the same line as the node | ||
* Check to see if the first element inside an array is an object and on the same line as the node | ||
* If the node is not an array then it will return false. | ||
@@ -268,18 +325,2 @@ * @param {ASTNode} node node to check | ||
/** | ||
* Returns the VariableDeclarator based on the current node | ||
* if not present then return null | ||
* @param {ASTNode} node node to examine | ||
* @returns {ASTNode|void} if found then node otherwise null | ||
*/ | ||
function getVariableDeclaratorNode(node) { | ||
var parent = node.parent; | ||
while (parent.type !== "VariableDeclarator" && parent.type !== "Program") { | ||
parent = parent.parent; | ||
} | ||
return parent.type === "VariableDeclarator" ? parent : null; | ||
} | ||
/** | ||
* Check indent for array block content or object block content | ||
@@ -309,26 +350,24 @@ * @param {ASTNode} node node to examine | ||
var elementsIndent; | ||
var parentVarNode = getVariableDeclaratorNode(node); | ||
// TODO - come up with a better strategy in future | ||
if (isNodeFirstInLine(node)) { | ||
var parentVarNode = getVariableDeclaratorNode(node); | ||
var parent = node.parent; | ||
var effectiveParent = parent; | ||
if (parent.type === "MemberExpression") { | ||
parent = node.parent.parent; | ||
if (isNodeFirstInLine(parent)) { | ||
nodeIndent = getNodeIndent(parent.parent); | ||
effectiveParent = parent.parent.parent; | ||
} else { | ||
nodeIndent = getNodeIndent(parent); | ||
effectiveParent = parent.parent; | ||
} | ||
} else { | ||
nodeIndent = getNodeIndent(parent); | ||
} | ||
nodeIndent = getNodeIndent(effectiveParent); | ||
if (parentVarNode && parentVarNode.loc.start.line !== node.loc.start.line) { | ||
if (parent.type !== "VariableDeclarator" || parentVarNode === parentVarNode.parent.declarations[0]) { | ||
nodeIndent = nodeIndent + (indentSize * options.VariableDeclarator); | ||
nodeIndent = nodeIndent + (indentSize * options.VariableDeclarator[parentVarNode.parent.kind]); | ||
} else if (parent.loc.start.line !== node.loc.start.line && parentVarNode === parentVarNode.parent.declarations[0]) { | ||
nodeIndent = nodeIndent + indentSize; | ||
} | ||
} else if (!parentVarNode && !isFirstArrayElementOnSameLine(parent)) { | ||
} else if (!parentVarNode && !isFirstArrayElementOnSameLine(parent) && effectiveParent.type !== "ExpressionStatement" && effectiveParent.type !== "AssignmentExpression" && effectiveParent.type !== "Property") { | ||
nodeIndent = nodeIndent + indentSize; | ||
@@ -338,2 +377,3 @@ } | ||
elementsIndent = nodeIndent + indentSize; | ||
checkFirstNodeLineIndent(node, nodeIndent); | ||
@@ -345,6 +385,6 @@ } else { | ||
// check if the node is a multiple variable declartion, if yes then make sure indetation takes into account | ||
// check if the node is a multiple variable declaration, if yes then make sure indentation takes into account | ||
// variable indentation concept | ||
if (isNodeInVarOnTop(node)) { | ||
elementsIndent += indentSize * options.VariableDeclarator; | ||
if (isNodeInVarOnTop(node, parentVarNode)) { | ||
elementsIndent += indentSize * options.VariableDeclarator[parentVarNode.parent.kind]; | ||
} | ||
@@ -426,4 +466,4 @@ | ||
* basically have only 1 elements from each line except the variable declaration line. | ||
* @param {ASTNodes} node Variable declaration node | ||
* @returns {ASTNodes[]} Filtered elements | ||
* @param {ASTNode} node Variable declaration node | ||
* @returns {ASTNode[]} Filtered elements | ||
*/ | ||
@@ -451,5 +491,7 @@ function filterOutSameLineVars(node) { | ||
var nodeIndent = getNodeIndent(node); | ||
var elementsIndent = nodeIndent + (indentSize * options.VariableDeclarator); | ||
var lastElement = elements[elements.length - 1]; | ||
// Comma can be placed before decalartion | ||
var elementsIndent = nodeIndent + indentSize * options.VariableDeclarator[node.kind]; | ||
// Comma can be placed before declaration | ||
checkNodesIndent(elements, elementsIndent, true); | ||
@@ -459,3 +501,3 @@ | ||
// Skip last block line check if last item in same line | ||
if (elements[elements.length - 1].loc.end.line === node.loc.end.line) { | ||
if (lastElement.loc.end.line === node.loc.end.line) { | ||
return; | ||
@@ -465,8 +507,14 @@ } | ||
checkLastNodeLineIndent(node, elementsIndent - indentSize); | ||
var tokenBeforeLastElement = context.getTokenBefore(lastElement); | ||
if (tokenBeforeLastElement.value === ",") { | ||
// Special case for comma-first syntax where the semicolon is indented | ||
checkLastNodeLineIndent(node, getNodeIndent(tokenBeforeLastElement)); | ||
} else { | ||
checkLastNodeLineIndent(node, elementsIndent - indentSize); | ||
} | ||
} | ||
/** | ||
* Check and decide wheteher to check for indentation for blockless nodes | ||
* Scenarios are for or while statements without brances around them | ||
* Check and decide whether to check for indentation for blockless nodes | ||
* Scenarios are for or while statements without braces around them | ||
* @param {ASTNode} node node to examine | ||
@@ -484,3 +532,3 @@ * @returns {void} | ||
* @param {ASTNode} node node to examine | ||
* @param {int} switchIndent indent for switch statement | ||
* @param {int} [switchIndent] indent for switch statement | ||
* @returns {int} indent size | ||
@@ -590,3 +638,14 @@ */ | ||
"VariableDeclarator": { | ||
"type": "integer" | ||
"type": ["integer", "object"], | ||
"properties": { | ||
"var": { | ||
"type": "integer" | ||
}, | ||
"let": { | ||
"type": "integer" | ||
}, | ||
"const": { | ||
"type": "integer" | ||
} | ||
} | ||
} | ||
@@ -593,0 +652,0 @@ }, |
@@ -101,2 +101,37 @@ /** | ||
/** | ||
* Gets the first node on the same line with the provided node. | ||
* @param {ASTNode} node The node to check find from | ||
* @returns {ASTNode} the first node on the given node's line | ||
*/ | ||
function getFirstNodeInLine(node) { | ||
var startLine, endLine, firstToken = node; | ||
do { | ||
node = firstToken; | ||
firstToken = context.getTokenBefore(node); | ||
startLine = node.loc.start.line; | ||
endLine = firstToken ? firstToken.loc.end.line : -1; | ||
} while (startLine === endLine); | ||
return node; | ||
} | ||
/** | ||
* Starting from the given a node (a property.key node here) looks forward | ||
* until it finds the first node before a colon punctuator and returns it. | ||
* @param {ASTNode} node The node to start looking from | ||
* @returns {ASTNode} the first node before a colon punctuator | ||
*/ | ||
function getFirstNodeBeforeColon(node) { | ||
var prevNode; | ||
while (node.type !== "Punctuator" || node.value !== ":") { | ||
prevNode = node; | ||
node = context.getTokenAfter(node); | ||
} | ||
return prevNode; | ||
} | ||
/** | ||
* Gets an object literal property's key as the identifier name or string value. | ||
@@ -147,22 +182,7 @@ * @param {ASTNode} property Property node whose key to retrieve. | ||
function getKeyWidth(property) { | ||
var key = property.key, | ||
startToken, endToken; | ||
var key = property.key; | ||
var startToken = getFirstNodeInLine(key); | ||
var endToken = getFirstNodeBeforeColon(key); | ||
// [computed]: value | ||
if (property.computed) { | ||
startToken = context.getTokenBefore(key); | ||
endToken = context.getTokenAfter(key); | ||
return endToken.range[1] - startToken.range[0]; | ||
} | ||
// name: value | ||
if (key.type === "Identifier") { | ||
return key.name.length; | ||
} | ||
// "literal": value | ||
// 42: value | ||
if (key.type === "Literal") { | ||
return key.raw.length; | ||
} | ||
return endToken.range[1] - startToken.range[0]; | ||
} | ||
@@ -169,0 +189,0 @@ |
@@ -73,2 +73,3 @@ /** | ||
config.capIsNew = config.capIsNew !== false; | ||
var skipProperties = config.properties === false; | ||
@@ -148,3 +149,4 @@ var newIsCapExceptions = checkArray(config, "newIsCapExceptions", []).reduce(invert, {}); | ||
} | ||
return false; | ||
return skipProperties && node.callee.type === "MemberExpression"; | ||
} | ||
@@ -224,2 +226,5 @@ | ||
} | ||
}, | ||
"properties": { | ||
"type": "boolean" | ||
} | ||
@@ -226,0 +231,0 @@ }, |
@@ -43,2 +43,8 @@ /** | ||
// When blockBindings is enabled, CatchClause creates its own scope | ||
// so start from one upper scope to exclude the current node | ||
if (scope.block === node) { | ||
scope = scope.upper; | ||
} | ||
if (paramIsShadowing(scope, node.param.name)) { | ||
@@ -45,0 +51,0 @@ context.report(node, "Value of '{{name}}' may be overwritten in IE 8 and earlier.", |
@@ -16,2 +16,20 @@ /** | ||
/** | ||
* Strips source code position informations from the node | ||
* @param {ASTNode} node The node. | ||
* @returns {ASTNode} a copy of the node with the position stripped away | ||
* @private | ||
*/ | ||
function stripsPosition(node) { | ||
var result = {}; | ||
for (var prop in node) { | ||
if (!~["loc", "start", "end", "range"].indexOf(prop)) { | ||
result[prop] = node[prop]; | ||
if (typeof result[prop] === "object") { | ||
result[prop] = stripsPosition(result[prop]); | ||
} | ||
} | ||
} | ||
return result; | ||
} | ||
/** | ||
* Get a hash value for the node | ||
@@ -23,15 +41,3 @@ * @param {ASTNode} node The node. | ||
function getHash(node) { | ||
if (node.type === "Literal") { | ||
return node.type + typeof node.value + node.value; | ||
} else if (node.type === "Identifier") { | ||
return node.type + typeof node.name + node.name; | ||
} else if (node.type === "MemberExpression") { | ||
return node.type + getHash(node.object) + getHash(node.property); | ||
} else if (node.type === "CallExpression") { | ||
return node.type + getHash(node.callee) + node.arguments.map(getHash).join(""); | ||
} else if (node.type === "BinaryExpression") { | ||
return node.type + getHash(node.left) + node.operator + getHash(node.right); | ||
} else if (node.type === "ConditionalExpression") { | ||
return node.type + getHash(node.test) + getHash(node.consequent) + getHash(node.alternate); | ||
} | ||
return JSON.stringify(stripsPosition(node)); | ||
} | ||
@@ -38,0 +44,0 @@ |
@@ -16,3 +16,3 @@ /** | ||
SKIP_BLANK = "^" + BLANK_CLASS + "*$", | ||
NONBLANK = BLANK_CLASS + "$"; | ||
NONBLANK = BLANK_CLASS + "+$"; | ||
@@ -51,3 +51,3 @@ var options = context.options[0] || {}, | ||
line: i + 1, | ||
column: lines[i].length - matches[0].length + 1 | ||
column: matches.index | ||
}; | ||
@@ -54,0 +54,0 @@ |
@@ -152,2 +152,5 @@ /** | ||
lastSpecifier = node.specifiers[node.specifiers.length - 1], | ||
importSpecifiers = node.specifiers.filter(function(specifier) { | ||
return specifier.type === "ImportSpecifier"; | ||
}), | ||
first, second, penultimate, last; | ||
@@ -161,2 +164,9 @@ | ||
last = context.getTokenAfter(lastSpecifier); | ||
// support trailing commas | ||
if (last.value === ",") { | ||
penultimate = last; | ||
last = context.getTokenAfter(last); | ||
} | ||
validateBraceSpacing(node, first, second, penultimate, last); | ||
@@ -168,6 +178,13 @@ return; | ||
if (lastSpecifier && lastSpecifier.type === "ImportSpecifier") { | ||
first = context.getTokenBefore(lastSpecifier); | ||
second = context.getFirstToken(lastSpecifier); | ||
penultimate = context.getLastToken(lastSpecifier); | ||
last = context.getTokenAfter(lastSpecifier); | ||
first = context.getTokenBefore(importSpecifiers[0]); | ||
second = context.getFirstToken(importSpecifiers[0]); | ||
penultimate = context.getLastToken(importSpecifiers[importSpecifiers.length - 1]); | ||
last = context.getTokenAfter(importSpecifiers[importSpecifiers.length - 1]); | ||
// support trailing commas | ||
if (last.value === ",") { | ||
penultimate = last; | ||
last = context.getTokenAfter(last); | ||
} | ||
validateBraceSpacing(node, first, second, penultimate, last); | ||
@@ -174,0 +191,0 @@ } |
@@ -19,19 +19,25 @@ /** | ||
if (node.callee.name === "parseInt") { | ||
if (!(node.callee.name === "parseInt" || ( | ||
node.callee.type === "MemberExpression" && | ||
node.callee.object.name === "Number" && | ||
node.callee.property.name === "parseInt" | ||
) | ||
)) { | ||
return; | ||
} | ||
if (node.arguments.length < 2) { | ||
context.report(node, "Missing radix parameter."); | ||
} else { | ||
if (node.arguments.length < 2) { | ||
context.report(node, "Missing radix parameter."); | ||
} else { | ||
radix = node.arguments[1]; | ||
radix = node.arguments[1]; | ||
// don't allow non-numeric literals or undefined | ||
if ((radix.type === "Literal" && typeof radix.value !== "number") || | ||
(radix.type === "Identifier" && radix.name === "undefined") | ||
) { | ||
context.report(node, "Invalid radix parameter."); | ||
} | ||
// don't allow non-numeric literals or undefined | ||
if ((radix.type === "Literal" && typeof radix.value !== "number") || | ||
(radix.type === "Identifier" && radix.name === "undefined") | ||
) { | ||
context.report(node, "Invalid radix parameter."); | ||
} | ||
} | ||
} | ||
} | ||
@@ -38,0 +44,0 @@ }; |
@@ -26,4 +26,14 @@ /** | ||
function report(node) { | ||
var message = always ? "Missing semicolon." : "Extra semicolon."; | ||
context.report(node, context.getLastToken(node).loc.end, message); | ||
var message; | ||
var loc = context.getLastToken(node).loc; | ||
if (always) { | ||
message = "Missing semicolon."; | ||
loc = loc.end; | ||
} else { | ||
message = "Extra semicolon."; | ||
loc = loc.start; | ||
} | ||
context.report(node, loc, message); | ||
} | ||
@@ -30,0 +40,0 @@ |
@@ -26,2 +26,6 @@ /** | ||
function checkTokens(node, left, right) { | ||
if (right.type !== "Punctuator") { | ||
return; | ||
} | ||
var hasSpace = left.range[1] < right.range[0], | ||
@@ -28,0 +32,0 @@ value = left.value; |
@@ -88,2 +88,3 @@ /** | ||
"AssignmentExpression": checkBinary, | ||
"AssignmentPattern": checkBinary, | ||
"BinaryExpression": checkBinary, | ||
@@ -90,0 +91,0 @@ "LogicalExpression": checkBinary, |
@@ -114,5 +114,4 @@ /** | ||
} | ||
pattern += "$)"; // the sequence continues until the end. | ||
pattern += "(?:$|[\n\r]))"; // the sequence continues until the end. | ||
} | ||
return new RegExp(pattern); | ||
@@ -119,0 +118,0 @@ } |
@@ -100,2 +100,4 @@ /** | ||
case "param": | ||
case "arg": | ||
case "argument": | ||
if (!tag.type) { | ||
@@ -102,0 +104,0 @@ context.report(jsdocNode, "Missing JSDoc parameter type for '{{name}}'.", { name: tag.name }); |
{ | ||
"name": "eslint", | ||
"version": "1.1.0", | ||
"version": "1.2.0", | ||
"author": "Nicholas C. Zakas <nicholas+npm@nczconsulting.com>", | ||
@@ -44,3 +44,3 @@ "description": "An AST-based pattern checker for JavaScript.", | ||
"escope": "^3.2.0", | ||
"espree": "^2.2.0", | ||
"espree": "^2.2.4", | ||
"estraverse": "^4.1.0", | ||
@@ -51,2 +51,3 @@ "estraverse-fb": "^1.3.1", | ||
"is-my-json-valid": "^2.10.0", | ||
"is-resolvable": "^1.0.0", | ||
"js-yaml": "^3.2.5", | ||
@@ -80,2 +81,3 @@ "lodash.clonedeep": "^3.0.1", | ||
"jsonlint": "^1.6.2", | ||
"linefix": "^0.1.1", | ||
"markdownlint": "^0.0.6", | ||
@@ -82,0 +84,0 @@ "mocha": "^2.1.0", |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
713925
213
18460
27
25
+ Addedis-resolvable@^1.0.0
+ Addedis-resolvable@1.1.0(transitive)
Updatedespree@^2.2.4