Comparing version 0.24.1 to 1.0.0-rc-1
@@ -19,12 +19,2 @@ /** | ||
globalReturn: true | ||
}, | ||
rules: { | ||
"no-catch-shadow": 0, | ||
"no-console": 0, | ||
"no-mixed-requires": 2, | ||
"no-new-require": 2, | ||
"no-path-concat": 2, | ||
"no-process-exit": 2, | ||
"global-strict": [0, "always"], | ||
"handle-callback-err": [2, "err"] | ||
} | ||
@@ -65,2 +55,5 @@ }, | ||
}, | ||
serviceworker: { | ||
globals: globals.serviceworker | ||
}, | ||
es6: { | ||
@@ -67,0 +60,0 @@ ecmaFeatures: { |
{ | ||
"ecmaFeatures": {}, | ||
"parser": "espree", | ||
"env": { | ||
"browser": false, | ||
"node": false, | ||
"amd": false, | ||
"mocha": false, | ||
"jasmine": false | ||
}, | ||
"rules": { | ||
"no-alert": 2, | ||
"no-array-constructor": 2, | ||
"no-alert": 0, | ||
"no-array-constructor": 0, | ||
"no-bitwise": 0, | ||
"no-caller": 2, | ||
"no-catch-shadow": 2, | ||
"no-comma-dangle": 0, | ||
"no-caller": 0, | ||
"no-catch-shadow": 0, | ||
"no-class-assign": 0, | ||
"no-cond-assign": 2, | ||
"no-console": 2, | ||
"no-const-assign": 0, | ||
"no-constant-condition": 2, | ||
@@ -32,18 +24,16 @@ "no-continue": 0, | ||
"no-empty": 2, | ||
"no-empty-class": 0, | ||
"no-empty-character-class": 2, | ||
"no-empty-label": 2, | ||
"no-empty-label": 0, | ||
"no-eq-null": 0, | ||
"no-eval": 2, | ||
"no-eval": 0, | ||
"no-ex-assign": 2, | ||
"no-extend-native": 2, | ||
"no-extra-bind": 2, | ||
"no-extend-native": 0, | ||
"no-extra-bind": 0, | ||
"no-extra-boolean-cast": 2, | ||
"no-extra-parens": 0, | ||
"no-extra-semi": 2, | ||
"no-extra-strict": 2, | ||
"no-fallthrough": 2, | ||
"no-floating-decimal": 0, | ||
"no-func-assign": 2, | ||
"no-implied-eval": 2, | ||
"no-implied-eval": 0, | ||
"no-inline-comments": 0, | ||
@@ -53,25 +43,25 @@ "no-inner-declarations": [2, "functions"], | ||
"no-irregular-whitespace": 2, | ||
"no-iterator": 2, | ||
"no-label-var": 2, | ||
"no-labels": 2, | ||
"no-lone-blocks": 2, | ||
"no-iterator": 0, | ||
"no-label-var": 0, | ||
"no-labels": 0, | ||
"no-lone-blocks": 0, | ||
"no-lonely-if": 0, | ||
"no-loop-func": 2, | ||
"no-loop-func": 0, | ||
"no-mixed-requires": [0, false], | ||
"no-mixed-spaces-and-tabs": [2, false], | ||
"linebreak-style": [0, "unix"], | ||
"no-multi-spaces": 2, | ||
"no-multi-str": 2, | ||
"no-multi-spaces": 0, | ||
"no-multi-str": 0, | ||
"no-multiple-empty-lines": [0, {"max": 2}], | ||
"no-native-reassign": 2, | ||
"no-native-reassign": 0, | ||
"no-negated-in-lhs": 2, | ||
"no-nested-ternary": 0, | ||
"no-new": 2, | ||
"no-new-func": 2, | ||
"no-new-object": 2, | ||
"no-new": 0, | ||
"no-new-func": 0, | ||
"no-new-object": 0, | ||
"no-new-require": 0, | ||
"no-new-wrappers": 2, | ||
"no-new-wrappers": 0, | ||
"no-obj-calls": 2, | ||
"no-octal": 2, | ||
"no-octal-escape": 2, | ||
"no-octal-escape": 0, | ||
"no-param-reassign": 0, | ||
@@ -81,4 +71,4 @@ "no-path-concat": 0, | ||
"no-process-env": 0, | ||
"no-process-exit": 2, | ||
"no-proto": 2, | ||
"no-process-exit": 0, | ||
"no-proto": 0, | ||
"no-redeclare": 2, | ||
@@ -88,61 +78,61 @@ "no-regex-spaces": 2, | ||
"no-restricted-modules": 0, | ||
"no-return-assign": 2, | ||
"no-script-url": 2, | ||
"no-return-assign": 0, | ||
"no-script-url": 0, | ||
"no-self-compare": 0, | ||
"no-sequences": 2, | ||
"no-shadow": 2, | ||
"no-shadow-restricted-names": 2, | ||
"no-space-before-semi": 0, | ||
"no-spaced-func": 2, | ||
"no-sequences": 0, | ||
"no-shadow": 0, | ||
"no-shadow-restricted-names": 0, | ||
"no-spaced-func": 0, | ||
"no-sparse-arrays": 2, | ||
"no-sync": 0, | ||
"no-ternary": 0, | ||
"no-trailing-spaces": 2, | ||
"no-trailing-spaces": 0, | ||
"no-this-before-super": 0, | ||
"no-throw-literal": 0, | ||
"no-undef": 2, | ||
"no-undef-init": 2, | ||
"no-undef-init": 0, | ||
"no-undefined": 0, | ||
"no-unexpected-multiline": 0, | ||
"no-underscore-dangle": 2, | ||
"no-underscore-dangle": 0, | ||
"no-unneeded-ternary": 0, | ||
"no-unreachable": 2, | ||
"no-unused-expressions": 2, | ||
"no-unused-expressions": 0, | ||
"no-unused-vars": [2, {"vars": "all", "args": "after-used"}], | ||
"no-use-before-define": 2, | ||
"no-use-before-define": 0, | ||
"no-useless-call": 0, | ||
"no-void": 0, | ||
"no-var": 0, | ||
"prefer-const": 0, | ||
"no-warning-comments": [0, { "terms": ["todo", "fixme", "xxx"], "location": "start" }], | ||
"no-with": 2, | ||
"no-wrap-func": 2, | ||
"no-with": 0, | ||
"array-bracket-spacing": [0, "never"], | ||
"arrow-parens": 0, | ||
"arrow-spacing": 0, | ||
"accessor-pairs": 0, | ||
"block-scoped-var": 0, | ||
"brace-style": [0, "1tbs"], | ||
"camelcase": 2, | ||
"callback-return": 0, | ||
"camelcase": 0, | ||
"comma-dangle": [2, "never"], | ||
"comma-spacing": 2, | ||
"comma-spacing": 0, | ||
"comma-style": 0, | ||
"complexity": [0, 11], | ||
"computed-property-spacing": [0, "never"], | ||
"consistent-return": 2, | ||
"consistent-return": 0, | ||
"consistent-this": [0, "that"], | ||
"constructor-super": 0, | ||
"curly": [2, "all"], | ||
"curly": [0, "all"], | ||
"default-case": 0, | ||
"dot-location": 0, | ||
"dot-notation": [2, { "allowKeywords": true }], | ||
"eol-last": 2, | ||
"eqeqeq": 2, | ||
"dot-notation": [0, { "allowKeywords": true }], | ||
"eol-last": 0, | ||
"eqeqeq": 0, | ||
"func-names": 0, | ||
"func-style": [0, "declaration"], | ||
"generator-star": 0, | ||
"generator-star-spacing": 0, | ||
"global-strict": [2, "never"], | ||
"guard-for-in": 0, | ||
"handle-callback-err": 0, | ||
"indent": 0, | ||
"key-spacing": [2, { "beforeColon": false, "afterColon": true }], | ||
"init-declarations": 0, | ||
"key-spacing": [0, { "beforeColon": false, "afterColon": true }], | ||
"lines-around-comment": 0, | ||
@@ -154,4 +144,4 @@ "max-depth": [0, 4], | ||
"max-statements": [0, 10], | ||
"new-cap": 2, | ||
"new-parens": 2, | ||
"new-cap": 0, | ||
"new-parens": 0, | ||
"newline-after-var": 0, | ||
@@ -164,21 +154,20 @@ "object-curly-spacing": [0, "never"], | ||
"padded-blocks": 0, | ||
"prefer-const": 0, | ||
"prefer-spread": 0, | ||
"quote-props": 0, | ||
"quotes": [2, "double"], | ||
"quotes": [0, "double"], | ||
"radix": 0, | ||
"semi": 2, | ||
"semi-spacing": [2, {"before": false, "after": true}], | ||
"require-yield": 0, | ||
"semi": 0, | ||
"semi-spacing": [0, {"before": false, "after": true}], | ||
"sort-vars": 0, | ||
"space-after-function-name": [0, "never"], | ||
"space-after-keywords": [0, "always"], | ||
"space-before-blocks": [0, "always"], | ||
"space-before-function-paren": [0, "always"], | ||
"space-before-function-parentheses": [0, "always"], | ||
"space-in-brackets": [0, "never"], | ||
"space-in-parens": [0, "never"], | ||
"space-infix-ops": 2, | ||
"space-return-throw-case": 2, | ||
"space-unary-ops": [2, { "words": true, "nonwords": false }], | ||
"space-infix-ops": 0, | ||
"space-return-throw-case": 0, | ||
"space-unary-ops": [0, { "words": true, "nonwords": false }], | ||
"spaced-comment": 0, | ||
"spaced-line-comment": [0, "always"], | ||
"strict": 2, | ||
"strict": 0, | ||
"use-isnan": 2, | ||
@@ -190,4 +179,4 @@ "valid-jsdoc": 0, | ||
"wrap-regex": 0, | ||
"yoda": [2, "never"] | ||
"yoda": [0, "never"] | ||
} | ||
} |
@@ -10,5 +10,4 @@ /** | ||
linter: require("./eslint"), | ||
cli: require("./cli"), | ||
CLIEngine: require("./cli-engine"), | ||
validator: require("./config-validator") | ||
}; |
@@ -41,4 +41,3 @@ /** | ||
* @property {string} configFile The configuration file to use. | ||
* @property {boolean} reset True disables all default rules and environments. | ||
* @property {boolean|object} baseConfig Base config object. False disables all default rules and environments. | ||
* @property {boolean|object} baseConfig Base config object. True enables recommend rules and environments. | ||
* @property {boolean} ignore False disables use of .eslintignore. | ||
@@ -74,4 +73,3 @@ * @property {string[]} rulePaths An array of directories to load custom rules from. | ||
configFile: null, | ||
reset: false, | ||
baseConfig: require(path.resolve(__dirname, "..", "conf", "eslint.json")), | ||
baseConfig: false, | ||
rulePaths: [], | ||
@@ -101,3 +99,3 @@ useEslintrc: true, | ||
if (pluginNames) { | ||
pluginNames.forEach(function (pluginName) { | ||
pluginNames.forEach(function(pluginName) { | ||
var pluginNamespace = util.getNamespace(pluginName), | ||
@@ -393,3 +391,3 @@ pluginNameWithoutNamespace = util.removeNameSpace(pluginName), | ||
*/ | ||
isPathIgnored: function (filePath) { | ||
isPathIgnored: function(filePath) { | ||
var ignoredPaths; | ||
@@ -396,0 +394,0 @@ |
@@ -51,3 +51,2 @@ /** | ||
rulePaths: cliOptions.rulesdir, | ||
reset: cliOptions.reset, | ||
useEslintrc: cliOptions.eslintrc | ||
@@ -121,3 +120,3 @@ }; | ||
results.forEach(function (result) { | ||
results.forEach(function(result) { | ||
var filteredMessages = result.messages.filter(isErrorMessage); | ||
@@ -124,0 +123,0 @@ |
@@ -31,5 +31,5 @@ /** | ||
exec("npm i eslint-plugin-react --save-dev", callback); | ||
} else { | ||
callback(); | ||
return; | ||
} | ||
callback(); | ||
} | ||
@@ -43,3 +43,3 @@ | ||
function processAnswers(answers) { | ||
var config = {rules: {}, env: {}}; | ||
var config = {rules: {}, env: {}, extends: "eslint:recommended"}; | ||
config.rules.indent = [2, answers.indent]; | ||
@@ -59,2 +59,3 @@ config.rules.quotes = [2, answers.quotes]; | ||
config.plugins = ["react"]; | ||
config.ecmaFeatures.experimentalObjectRestSpread = true; | ||
} | ||
@@ -124,3 +125,3 @@ } | ||
default: false, | ||
when: function (answers) { | ||
when: function(answers) { | ||
return answers.jsx; | ||
@@ -127,0 +128,0 @@ } |
@@ -82,3 +82,3 @@ /** | ||
]; | ||
validateRule.errors.forEach(function (error) { | ||
validateRule.errors.forEach(function(error) { | ||
message.push( | ||
@@ -101,3 +101,3 @@ "\tValue \"", error.value, "\" ", error.message, ".\n" | ||
if (typeof config.rules === "object") { | ||
Object.keys(config.rules).forEach(function (id) { | ||
Object.keys(config.rules).forEach(function(id) { | ||
validateRuleOptions(id, config.rules[id], source); | ||
@@ -104,0 +104,0 @@ }); |
@@ -25,3 +25,4 @@ /** | ||
isAbsolutePath = require("path-is-absolute"), | ||
validator = require("./config-validator"); | ||
validator = require("./config-validator"), | ||
pathIsInside = require("path-is-inside"); | ||
@@ -112,5 +113,9 @@ //------------------------------------------------------------------------------ | ||
// Make the last element in an array take the highest precedence | ||
config = configExtends.reduceRight(function (previousValue, parentPath) { | ||
config = configExtends.reduceRight(function(previousValue, parentPath) { | ||
if (isFilePath(parentPath)) { | ||
if (parentPath === "eslint:recommended") { | ||
// Add an explicit substitution for eslint:recommended to conf/eslint.json | ||
// this lets us use the eslint.json file as the recommended rules | ||
parentPath = path.resolve(__dirname, "../conf/eslint.json"); | ||
} else if (isFilePath(parentPath)) { | ||
// If the `extends` path is relative, use the directory of the current configuration | ||
@@ -153,3 +158,3 @@ // file as the reference point. Otherwise, use as-is. | ||
pluginNames.forEach(function (pluginName) { | ||
pluginNames.forEach(function(pluginName) { | ||
var pluginNamespace = util.getNamespace(pluginName), | ||
@@ -216,3 +221,4 @@ pluginNameWithoutNamespace = util.removeNameSpace(pluginName), | ||
localConfigFiles = thisConfig.findLocalConfigFiles(directory), | ||
numFiles = localConfigFiles.length; | ||
numFiles = localConfigFiles.length, | ||
rootPath; | ||
@@ -236,2 +242,12 @@ for (i = 0; i < numFiles; i++) { | ||
// Check for root flag | ||
if (localConfig.root === true) { | ||
rootPath = path.dirname(localConfigFile); | ||
} | ||
// If root flag is set, don't consider file if it is above root | ||
if (rootPath && !pathIsInside(path.dirname(localConfigFile), rootPath)) { | ||
continue; | ||
} | ||
found = true; | ||
@@ -249,4 +265,2 @@ debug("Using " + localConfigFile); | ||
* @param {Object<string,boolean>} envs The environment settings. | ||
* @param {boolean} reset The value of the command line reset option. If true, | ||
* rules are not automatically merged into the config. | ||
* @returns {Object} A configuration object with the appropriate rules and globals | ||
@@ -256,3 +270,3 @@ * set. | ||
*/ | ||
function createEnvironmentConfig(envs, reset) { | ||
function createEnvironmentConfig(envs) { | ||
@@ -267,3 +281,3 @@ var envConfig = { | ||
if (envs) { | ||
Object.keys(envs).filter(function (name) { | ||
Object.keys(envs).filter(function(name) { | ||
return envs[name]; | ||
@@ -275,6 +289,2 @@ }).forEach(function(name) { | ||
if (!reset && environment.rules) { | ||
assign(envConfig.rules, environment.rules); | ||
} | ||
if (environment.globals) { | ||
@@ -314,16 +324,7 @@ assign(envConfig.globals, environment.globals); | ||
if (options.reset || options.baseConfig === false) { | ||
// If `options.reset` is truthy or `options.baseConfig` is set to `false`, | ||
// disable all default rules and environments | ||
this.baseConfig = { rules: {} }; | ||
} else { | ||
// If `options.baseConfig` is an object, just use it, | ||
// otherwise use default base config from `conf/eslint.json` | ||
this.baseConfig = options.baseConfig || | ||
require(path.resolve(__dirname, "..", "conf", "eslint.json")); | ||
} | ||
this.baseConfig = options.baseConfig || { rules: {} }; | ||
this.useEslintrc = (options.useEslintrc !== false); | ||
this.env = (options.envs || []).reduce(function (envs, name) { | ||
this.env = (options.envs || []).reduce(function(envs, name) { | ||
envs[name] = true; | ||
@@ -333,3 +334,3 @@ return envs; | ||
this.globals = (options.globals || []).reduce(function (globals, def) { | ||
this.globals = (options.globals || []).reduce(function(globals, def) { | ||
// Default "foo" to false and handle "foo:false" and "foo:true" | ||
@@ -356,3 +357,3 @@ var parts = def.split(":"); | ||
*/ | ||
Config.prototype.getConfig = function (filePath) { | ||
Config.prototype.getConfig = function(filePath) { | ||
var config, | ||
@@ -385,3 +386,3 @@ userConfig, | ||
// Step 3: Merge in environment-specific globals and rules from .eslintrc files | ||
config = util.mergeConfigs(config, createEnvironmentConfig(userConfig.env, this.options.reset)); | ||
config = util.mergeConfigs(config, createEnvironmentConfig(userConfig.env)); | ||
@@ -396,3 +397,3 @@ // Step 4: Merge in the user-specified configuration from .eslintrc and package.json | ||
if (this.useSpecificConfig.env) { | ||
config = util.mergeConfigs(config, createEnvironmentConfig(this.useSpecificConfig.env, this.options.reset)); | ||
config = util.mergeConfigs(config, createEnvironmentConfig(this.useSpecificConfig.env)); | ||
} | ||
@@ -405,3 +406,3 @@ | ||
debug("Merging command line environment settings"); | ||
config = util.mergeConfigs(config, createEnvironmentConfig(this.env, this.options.reset)); | ||
config = util.mergeConfigs(config, createEnvironmentConfig(this.env)); | ||
@@ -434,3 +435,3 @@ // Step 7: Merge in command line rules | ||
*/ | ||
Config.prototype.findLocalConfigFiles = function (directory) { | ||
Config.prototype.findLocalConfigFiles = function(directory) { | ||
@@ -437,0 +438,0 @@ if (!this.localConfigFinder) { |
@@ -22,3 +22,4 @@ /** | ||
escapeRegExp = require("escape-string-regexp"), | ||
validator = require("./config-validator"); | ||
validator = require("./config-validator"), | ||
replacements = require("../conf/replacements.json"); | ||
@@ -29,2 +30,9 @@ //------------------------------------------------------------------------------ | ||
// additional changes to make estraverse happy | ||
estraverse.Syntax.ExperimentalSpreadProperty = "ExperimentalSpreadProperty"; | ||
estraverse.Syntax.ExperimentalRestProperty = "ExperimentalRestProperty"; | ||
estraverse.VisitorKeys.ExperimentalSpreadProperty = ["argument"]; | ||
estraverse.VisitorKeys.ExperimentalRestProperty = ["argument"]; | ||
/** | ||
@@ -78,3 +86,3 @@ * Parses a list of "name:boolean_value" or/and "name" options divided by comma or | ||
line: location.start.line, | ||
column: location.start.column | ||
column: location.start.column + 1 | ||
}); | ||
@@ -136,2 +144,3 @@ | ||
var declaredGlobals = {}, | ||
exportedGlobals = {}, | ||
explicitGlobals = {}, | ||
@@ -142,3 +151,3 @@ builtin = environments.builtin; | ||
Object.keys(config.env).forEach(function (name) { | ||
Object.keys(config.env).forEach(function(name) { | ||
if (config.env[name]) { | ||
@@ -152,2 +161,3 @@ var environmentGlobals = environments[name] && environments[name].globals; | ||
assign(exportedGlobals, config.exported); | ||
assign(declaredGlobals, config.globals); | ||
@@ -175,2 +185,10 @@ assign(explicitGlobals, config.astGlobals); | ||
}); | ||
// mark all exported variables as such | ||
Object.keys(exportedGlobals).forEach(function(name) { | ||
var variable = getVariable(globalScope, name); | ||
if (variable) { | ||
variable.eslintUsed = true; | ||
} | ||
}); | ||
} | ||
@@ -255,2 +273,3 @@ | ||
var commentConfig = { | ||
exported: {}, | ||
astGlobals: {}, | ||
@@ -265,3 +284,3 @@ rules: {}, | ||
var value = comment.value.trim(); | ||
var match = /^(eslint-\w+|eslint-\w+-\w+|eslint|globals?)(\s|$)/.exec(value); | ||
var match = /^(eslint-\w+|eslint-\w+-\w+|eslint|exported|globals?)(\s|$)/.exec(value); | ||
@@ -273,2 +292,6 @@ if (match) { | ||
switch (match[1]) { | ||
case "exported": | ||
assign(commentConfig.exported, parseBooleanConfig(value)); | ||
break; | ||
case "globals": | ||
@@ -313,3 +336,3 @@ case "global": | ||
// apply environment configs | ||
Object.keys(commentConfig.env).forEach(function (name) { | ||
Object.keys(commentConfig.env).forEach(function(name) { | ||
if (environments[name]) { | ||
@@ -400,2 +423,36 @@ commentConfig = util.mergeConfigs(commentConfig, environments[name]); | ||
/** | ||
* Provide a stub rule with a given message | ||
* @param {string} message The message to be displayed for the rule | ||
* @returns {Function} Stub rule function | ||
*/ | ||
function createStubRule(message) { | ||
function createRuleModule(context) { | ||
return { | ||
Program: function(node) { | ||
context.report(node, message); | ||
} | ||
}; | ||
} | ||
if (message) { | ||
return createRuleModule; | ||
} else { | ||
throw new Error("No message passed to stub rule"); | ||
} | ||
} | ||
/** | ||
* Provide a rule replacement message | ||
* @param {string} ruleId Name of the rule | ||
* @returns {string} Message detailing rule replacement | ||
*/ | ||
function getRuleReplacementMessage(ruleId) { | ||
if (ruleId in replacements.rules) { | ||
var newRules = replacements.rules[ruleId]; | ||
return "Rule \'" + ruleId + "\' was removed and replaced by: " + newRules.join(", "); | ||
} | ||
} | ||
//------------------------------------------------------------------------------ | ||
@@ -482,3 +539,3 @@ // Public Interface | ||
line: ex.lineNumber, | ||
column: ex.column | ||
column: ex.column + 1 | ||
}); | ||
@@ -633,29 +690,38 @@ | ||
}).forEach(function(key) { | ||
var ruleCreator = rules.get(key), | ||
severity = getRuleSeverity(config.rules[key]), | ||
options = getRuleOptions(config.rules[key]), | ||
var ruleCreator, | ||
severity, | ||
options, | ||
rule; | ||
if (ruleCreator) { | ||
try { | ||
rule = ruleCreator(new RuleContext( | ||
key, api, severity, options, | ||
config.settings, config.ecmaFeatures | ||
)); | ||
// add all the node types as listeners | ||
Object.keys(rule).forEach(function(nodeType) { | ||
api.on(nodeType, timing.enabled | ||
? timing.time(key, rule[nodeType]) | ||
: rule[nodeType] | ||
); | ||
}); | ||
} catch(ex) { | ||
ex.message = "Error while loading rule '" + key + "': " + ex.message; | ||
throw ex; | ||
if (rules.get(key)) { | ||
ruleCreator = rules.get(key); | ||
} else { | ||
var replacementMsg = getRuleReplacementMessage(key); | ||
if (replacementMsg) { | ||
ruleCreator = createStubRule(replacementMsg); | ||
} else { | ||
ruleCreator = createStubRule("Definition for rule '" + key + "' was not found"); | ||
} | ||
rules.define(key, ruleCreator); | ||
} | ||
} else { | ||
throw new Error("Definition for rule '" + key + "' was not found."); | ||
severity = getRuleSeverity(config.rules[key]); | ||
options = getRuleOptions(config.rules[key]); | ||
try { | ||
rule = ruleCreator(new RuleContext( | ||
key, api, severity, options, | ||
config.settings, config.ecmaFeatures | ||
)); | ||
// add all the node types as listeners | ||
Object.keys(rule).forEach(function(nodeType) { | ||
api.on(nodeType, timing.enabled | ||
? timing.time(key, rule[nodeType]) | ||
: rule[nodeType] | ||
); | ||
}); | ||
} catch(ex) { | ||
ex.message = "Error while loading rule '" + key + "': " + ex.message; | ||
throw ex; | ||
} | ||
@@ -689,3 +755,3 @@ }); | ||
scopeMap = []; | ||
currentScopes.forEach(function (scope, index) { | ||
currentScopes.forEach(function(scope, index) { | ||
var range = scope.block.range[0]; | ||
@@ -789,3 +855,3 @@ | ||
Object.keys(opts || {}).forEach(function (key) { | ||
Object.keys(opts || {}).forEach(function(key) { | ||
var rx = new RegExp(escapeRegExp("{{" + key + "}}"), "g"); | ||
@@ -804,3 +870,3 @@ message = message.replace(rx, opts[key]); | ||
line: location.line, | ||
column: location.column, | ||
column: location.column + 1, // switch to 1-base instead of 0-base | ||
nodeType: node.type, | ||
@@ -960,5 +1026,5 @@ source: currentTextLines[location.line - 1] || "" | ||
estraverse.traverse(controller.root, { | ||
enter: function (node) { | ||
enter: function(node, parent) { | ||
if (node.range[0] <= index && index < node.range[1]) { | ||
result = node; | ||
result = assign({ parent: parent }, node); | ||
} else { | ||
@@ -968,3 +1034,3 @@ this.skip(); | ||
}, | ||
leave: function (node) { | ||
leave: function(node) { | ||
if (node === result) { | ||
@@ -1089,4 +1155,27 @@ this.break(); | ||
/** | ||
* Gets variables that are declared by a specified node. | ||
* | ||
* The variables are its `defs[].node` or `defs[].parent` is same as the specified node. | ||
* Specifically, below: | ||
* | ||
* - `VariableDeclaration` - variables of its all declarators. | ||
* - `VariableDeclarator` - variables. | ||
* - `FunctionDeclaration`/`FunctionExpression` - its function name and parameters. | ||
* - `ArrowFunctionExpression` - its parameters. | ||
* - `ClassDeclaration`/`ClassExpression` - its class name. | ||
* - `CatchClause` - variables of its exception. | ||
* - `ImportDeclaration` - variables of its all specifiers. | ||
* - `ImportSpecifier`/`ImportDefaultSpecifier`/`ImportNamespaceSpecifier` - a variable. | ||
* - others - always an empty array. | ||
* | ||
* @param {ASTNode} node A node to get. | ||
* @returns {escope.Variable[]} Variables that are declared by the node. | ||
*/ | ||
api.getDeclaredVariables = function(node) { | ||
return (scopeManager && scopeManager.getDeclaredVariables(node)) || []; | ||
}; | ||
return api; | ||
}()); |
@@ -57,3 +57,3 @@ /** | ||
*/ | ||
FileFinder.prototype.findInDirectoryOrParents = function (directory) { | ||
FileFinder.prototype.findInDirectoryOrParents = function(directory) { | ||
var cache = this.cache, | ||
@@ -109,3 +109,3 @@ child, | ||
*/ | ||
FileFinder.prototype.findAllInDirectoryAndParents = function (directory) { | ||
FileFinder.prototype.findAllInDirectoryAndParents = function(directory) { | ||
var cache = this.cache, | ||
@@ -112,0 +112,0 @@ child, |
@@ -88,3 +88,3 @@ /** | ||
*/ | ||
IgnoredPaths.load = function (options) { | ||
IgnoredPaths.load = function(options) { | ||
var patterns; | ||
@@ -112,3 +112,3 @@ | ||
*/ | ||
IgnoredPaths.prototype.contains = function (filepath) { | ||
IgnoredPaths.prototype.contains = function(filepath) { | ||
if (this.patterns === null) { | ||
@@ -115,0 +115,0 @@ throw new Error("No ignore patterns loaded, call 'load' first"); |
@@ -50,7 +50,2 @@ /** | ||
}, { | ||
option: "reset", | ||
type: "Boolean", | ||
default: "false", | ||
description: "Set all default rules to off" | ||
}, { | ||
option: "eslintrc", | ||
@@ -57,0 +52,0 @@ type: "Boolean", |
@@ -15,2 +15,3 @@ /** | ||
"getComments", | ||
"getDeclaredVariables", | ||
"getFilename", | ||
@@ -17,0 +18,0 @@ "getFirstToken", |
@@ -56,4 +56,4 @@ /** | ||
*/ | ||
exports.import = function (pluginRules, pluginName) { | ||
Object.keys(pluginRules).forEach(function (ruleId) { | ||
exports.import = function(pluginRules, pluginName) { | ||
Object.keys(pluginRules).forEach(function(ruleId) { | ||
var qualifiedRuleId = pluginName + "/" + ruleId, | ||
@@ -60,0 +60,0 @@ rule = pluginRules[ruleId]; |
@@ -58,3 +58,3 @@ /** | ||
return { | ||
"ObjectExpression": function (node) { | ||
"ObjectExpression": function(node) { | ||
if (checkSetWithoutGet || checkGetWithoutSet) { | ||
@@ -67,1 +67,16 @@ checkLonelySetGet(node); | ||
}; | ||
module.exports.schema = [ | ||
{ | ||
"type": "object", | ||
"properties": { | ||
"getWithoutSet": { | ||
"type": "boolean" | ||
}, | ||
"setWithoutGet": { | ||
"type": "boolean" | ||
} | ||
}, | ||
"additionalProperties": false | ||
} | ||
]; |
@@ -275,3 +275,3 @@ /** | ||
"VariableDeclaration": function (node) { | ||
"VariableDeclaration": function(node) { | ||
variableDeclarationHandler(node); | ||
@@ -278,0 +278,0 @@ }, |
@@ -14,3 +14,3 @@ /** | ||
module.exports = function (context) { | ||
module.exports = function(context) { | ||
var allowDangle = context.options[0]; | ||
@@ -17,0 +17,0 @@ var forbidDangle = allowDangle !== "always-multiline" && allowDangle !== "always"; |
@@ -57,7 +57,7 @@ /** | ||
scope.variables.some(function (variable) { | ||
scope.variables.some(function(variable) { | ||
var lookup; | ||
if (variable.name === alias) { | ||
if (variable.defs.some(function (def) { | ||
if (variable.defs.some(function(def) { | ||
return def.node.type === "VariableDeclarator" && | ||
@@ -73,3 +73,3 @@ def.node.init !== null; | ||
// assigned later in the same scope. | ||
if (!lookup.references.some(function (reference) { | ||
if (!lookup.references.some(function(reference) { | ||
var write = reference.writeExpr; | ||
@@ -83,3 +83,3 @@ | ||
})) { | ||
variable.defs.map(function (def) { | ||
variable.defs.map(function(def) { | ||
return def.node; | ||
@@ -99,3 +99,3 @@ }).forEach(reportBadAssignment); | ||
"VariableDeclarator": function (node) { | ||
"VariableDeclarator": function(node) { | ||
var id = node.id; | ||
@@ -110,3 +110,3 @@ var isDestructuring = | ||
"AssignmentExpression": function (node) { | ||
"AssignmentExpression": function(node) { | ||
if (node.left.type === "Identifier") { | ||
@@ -113,0 +113,0 @@ checkAssignment(node, node.left.name, node.right); |
@@ -94,3 +94,3 @@ /** | ||
"DoWhileStatement": function (node) { | ||
"DoWhileStatement": function(node) { | ||
checkBody(node, node.body, "do"); | ||
@@ -97,0 +97,0 @@ }, |
@@ -13,3 +13,3 @@ /** | ||
module.exports = function (context) { | ||
module.exports = function(context) { | ||
@@ -65,1 +65,7 @@ var config = context.options[0], | ||
}; | ||
module.exports.schema = [ | ||
{ | ||
"enum": ["object", "property"] | ||
} | ||
]; |
@@ -47,3 +47,3 @@ /** | ||
function getParameters(scope) { | ||
return scope.variables.filter(function (variable) { | ||
return scope.variables.filter(function(variable) { | ||
return variable.defs[0] && variable.defs[0].type === "Parameter"; | ||
@@ -50,0 +50,0 @@ }); |
/** | ||
* @fileoverview This option sets a specific tab width for your code | ||
* This rule has been ported and modified from JSCS. | ||
* @author Dmitriy Shekhovtsov | ||
* @copyright 2015 Dmitriy Shekhovtsov. All rights reserved. | ||
* @copyright 2013 Dulin Marat and other contributors. | ||
* | ||
* Permission is hereby granted, free of charge, to any person obtaining | ||
* a copy of this software and associated documentation files (the | ||
* "Software"), to deal in the Software without restriction, including | ||
* without limitation the rights to use, copy, modify, merge, publish, | ||
* distribute, sublicense, and/or sell copies of the Software, and to | ||
* permit persons to whom the Software is furnished to do so, subject to | ||
* the following conditions: | ||
* | ||
* The above copyright notice and this permission notice shall be | ||
* included in all copies or substantial portions of the Software. | ||
* | ||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | ||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | ||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | ||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | ||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
*/ | ||
/*eslint no-use-before-define:[2, "nofunc"]*/ | ||
* This rule has been ported and modified from nodeca. | ||
* @author Vitaly Puzrin | ||
* @author Gyandeep Singh | ||
* @copyright 2015 Vitaly Puzrin. All rights reserved. | ||
* @copyright 2015 Gyandeep Singh. All rights reserved. | ||
Copyright (C) 2014 by Vitaly Puzrin | ||
Permission is hereby granted, free of charge, to any person obtaining a copy | ||
of this software and associated documentation files (the "Software"), to deal | ||
in the Software without restriction, including without limitation the rights | ||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
copies of the Software, and to permit persons to whom the Software is | ||
furnished to do so, subject to the following conditions: | ||
The above copyright notice and this permission notice shall be included in | ||
all copies or substantial portions of the Software. | ||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
THE SOFTWARE. | ||
*/ | ||
"use strict"; | ||
@@ -33,20 +34,20 @@ | ||
//------------------------------------------------------------------------------ | ||
var util = require("util"); | ||
module.exports = function (context) { | ||
// indentation defaults: 4 spaces | ||
var indentChar = " "; | ||
module.exports = function(context) { | ||
var MESSAGE = "Expected indentation of {{needed}} characters."; | ||
var extraColumnStart = 0; | ||
var indentType = "spaces"; | ||
var indentSize = 4; | ||
var options = {indentSwitchCase: false}; | ||
var lines = null; | ||
var indentStack = [0]; | ||
var linesToCheck = null; | ||
var breakIndents = null; | ||
if (context.options.length) { | ||
if (context.options[0] === "tab") { | ||
indentChar = "\t"; | ||
indentSize = 1; | ||
indentType = "tab"; | ||
} else /* istanbul ignore else : this will be caught by options validation */ if (typeof context.options[0] === "number") { | ||
indentSize = context.options[0]; | ||
indentType = "spaces"; | ||
} | ||
@@ -60,404 +61,347 @@ | ||
var blockParents = [ | ||
"IfStatement", | ||
"WhileStatement", | ||
"DoWhileStatement", | ||
"ForStatement", | ||
"ForInStatement", | ||
"ForOfStatement", | ||
"FunctionDeclaration", | ||
"FunctionExpression", | ||
"ArrowExpression", | ||
"CatchClause", | ||
"WithStatement" | ||
]; | ||
var caseIndentStore = {}; | ||
var indentableNodes = { | ||
BlockStatement: "body", | ||
Program: "body", | ||
ObjectExpression: "properties", | ||
ArrayExpression: "elements", | ||
SwitchStatement: "cases" | ||
}; | ||
/** | ||
* 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 | ||
* @returns {int} Indent | ||
*/ | ||
function getNodeIndent(node, byLastLine, excludeCommas) { | ||
byLastLine = byLastLine || false; | ||
excludeCommas = excludeCommas || false; | ||
if (options.indentSwitchCase) { | ||
indentableNodes.SwitchCase = "consequent"; | ||
} | ||
var src = context.getSource(node, node.loc.start.column + extraColumnStart); | ||
var lines = src.split("\n"); | ||
if (byLastLine) { | ||
src = lines[lines.length - 1]; | ||
} else { | ||
src = lines[0]; | ||
} | ||
//-------------------------------------------------------------------------- | ||
// Helpers | ||
//-------------------------------------------------------------------------- | ||
var skip = excludeCommas ? "," : ""; | ||
/** | ||
* Mark line to be checked | ||
* @param {Number} line - line number | ||
* @returns {void} | ||
*/ | ||
function markCheckLine(line) { | ||
linesToCheck[line].check = true; | ||
} | ||
var regExp; | ||
if (indentType === "spaces") { | ||
regExp = new RegExp("^[ " + skip + "]+"); | ||
} else { | ||
regExp = new RegExp("^[\t" + skip + "]+"); | ||
} | ||
/** | ||
* Mark line with targeted node to be checked | ||
* @param {ASTNode} checkNode - targeted node | ||
* @returns {void} | ||
*/ | ||
function markCheck(checkNode) { | ||
markCheckLine(checkNode.loc.start.line - 1); | ||
var indent = regExp.exec(src); | ||
return indent ? indent[0].length : 0; | ||
} | ||
/** | ||
* Sets pushing indent of current node | ||
* @param {ASTNode} node - targeted node | ||
* @param {Number} indents - indents count to push | ||
* Check indent for nodes list | ||
* @param {ASTNode[]} nodes list of node objects | ||
* @param {int} indent needed indent | ||
* @param {boolean} excludeCommas skip comma on start of line | ||
* @returns {void} | ||
*/ | ||
function markPush(node, indents) { | ||
linesToCheck[node.loc.start.line - 1].push.push(indents); | ||
function checkNodesIndent(nodes, indent, excludeCommas) { | ||
nodes.forEach(function(node) { | ||
var nodeIndent = getNodeIndent(node, false, excludeCommas); | ||
if (nodeIndent !== indent) { | ||
context.report(node, MESSAGE, { gotten: nodeIndent, needed: indent }); | ||
} | ||
}); | ||
} | ||
/** | ||
* Marks line as outdent, end of block statement for example | ||
* @param {ASTNode} node - targeted node | ||
* @param {Number} outdents - count of outedents in targeted line | ||
* Check last node line indent this detects, that block closed correctly | ||
* @param {ASTNode} node Node to examine | ||
* @param {int} lastLineIndent needed indent | ||
* @returns {void} | ||
*/ | ||
function markPop(node, outdents) { | ||
linesToCheck[node.loc.end.line - 1].pop.push(outdents); | ||
function checkLastNodeLineIndent(node, lastLineIndent) { | ||
var endIndent = getNodeIndent(node, true); | ||
if (endIndent !== lastLineIndent) { | ||
context.report( | ||
node, | ||
{ line: node.loc.end.line, column: node.loc.end.column }, | ||
MESSAGE, | ||
{ gotten: endIndent, needed: lastLineIndent } | ||
); | ||
} | ||
} | ||
/** | ||
* Set alt push for current node | ||
* @param {ASTNode} node - targeted node | ||
* Check indent for function block content | ||
* @param {ASTNode} node node to examine | ||
* @returns {void} | ||
*/ | ||
function markPushAlt(node) { | ||
linesToCheck[node.loc.start.line - 1].pushAltLine.push(node.loc.end.line - 1); | ||
} | ||
function checkIndentInFunctionBlock(node) { | ||
/** | ||
* Marks end of node block to be checked | ||
* and marks targeted node as indent pushing | ||
* @param {ASTNode} pushNode - targeted node | ||
* @param {Number} indents - indent count to push | ||
* @returns {void} | ||
*/ | ||
function markPushAndEndCheck(pushNode, indents) { | ||
markPush(pushNode, indents); | ||
markCheckLine(pushNode.loc.end.line - 1); | ||
} | ||
// Search first caller in chain. | ||
// Ex.: | ||
// | ||
// Models <- Identifier | ||
// .User | ||
// .find() | ||
// .exec(function() { | ||
// // function body | ||
// }); | ||
// | ||
// Looks for 'Models' | ||
var calleeNode = node.parent; // FunctionExpression | ||
while (calleeNode.parent && | ||
calleeNode.parent.type === "CallExpression") { | ||
calleeNode = calleeNode.parent; | ||
} | ||
/** | ||
* Mark node as switch case statement | ||
* and set push\pop indentation changes | ||
* @param {ASTNode} caseNode - targeted node | ||
* @param {ASTNode[]} children - consequent child nodes of case node | ||
* @returns {void} | ||
*/ | ||
function markCase(caseNode, children) { | ||
var outdentNode = getCaseOutdent(children); | ||
var indent; | ||
if (outdentNode) { | ||
// If a case statement has a `break` as a direct child and it is the | ||
// first one encountered, use it as the example for all future case indentation | ||
if (breakIndents === null) { | ||
breakIndents = (caseNode.loc.start.column === outdentNode.loc.start.column) ? 1 : 0; | ||
} | ||
markPop(outdentNode, breakIndents); | ||
if (calleeNode.parent && | ||
(calleeNode.parent.type === "Property" || | ||
calleeNode.parent.type === "ArrayExpression")) { | ||
// If function is part of array or object, comma can be put at left | ||
indent = getNodeIndent(calleeNode, false, true); | ||
} else { | ||
markPop(caseNode, 0); | ||
// If function is standalone, simple calculate indent | ||
indent = getNodeIndent(calleeNode); | ||
} | ||
indent += indentSize; | ||
// If function content is not empty | ||
if (node.body.length > 0) { | ||
// Calculate left shift position don't require strict indent | ||
// allow function body allign to (indentSize * X) | ||
while (getNodeIndent(node.body[0]) > indent) { | ||
indent += indentSize; | ||
} | ||
} | ||
checkNodesIndent(node.body, indent); | ||
checkLastNodeLineIndent(node, indent - indentSize); | ||
} | ||
/** | ||
* Mark child nodes to be checked later of targeted node, | ||
* only if child node not in same line as targeted one | ||
* (if child and parent nodes wrote in single line) | ||
* @param {ASTNode} node - targeted node | ||
* @returns {void} | ||
* Checks if the given node starts and ends on the same line | ||
* @param {ASTNode} node The node to check | ||
* @returns {boolean} Whether or not the block starts and ends on the same line. | ||
*/ | ||
function markChildren(node) { | ||
getChildren(node).forEach(function(childNode) { | ||
if (childNode.loc.start.line !== node.loc.start.line || node.type === "Program") { | ||
markCheck(childNode); | ||
} | ||
}); | ||
function isSingleLineNode(node) { | ||
var lastToken = context.getLastToken(node), | ||
startLine = node.loc.start.line, | ||
endLine = lastToken.loc.end.line; | ||
return startLine === endLine; | ||
} | ||
/** | ||
* Mark child block as scope pushing and mark to check | ||
* @param {ASTNode} node - target node | ||
* @param {String} property - target node property containing child | ||
* Check indent for array block content or object block content | ||
* @param {ASTNode} node node to examine | ||
* @returns {void} | ||
*/ | ||
function markAlternateBlockStatement(node, property) { | ||
var child = node[property]; | ||
if (child && child.type === "BlockStatement") { | ||
markCheck(child); | ||
function checkIndentInArrayOrObjectBlock(node) { | ||
// Skip inline | ||
if (isSingleLineNode(node)) { | ||
return; | ||
} | ||
} | ||
/** | ||
* Checks whether node is multiline or single line | ||
* @param {ASTNode} node - target node | ||
* @returns {boolean} - is multiline node | ||
*/ | ||
function isMultiline(node) { | ||
return node.loc.start.line !== node.loc.end.line; | ||
} | ||
var elements = (node.type === "ArrayExpression") ? node.elements : node.properties; | ||
/** | ||
* Get switch case statement outdent node if any | ||
* @param {ASTNode[]} caseChildren - case statement childs | ||
* @returns {ASTNode} - outdent node | ||
*/ | ||
function getCaseOutdent(caseChildren) { | ||
var outdentNode; | ||
caseChildren.some(function(node) { | ||
if (node.type === "BreakStatement") { | ||
outdentNode = node; | ||
return true; | ||
// Skip if first element is in same line with this node | ||
if (elements.length > 0 && elements[0].loc.start.line === node.loc.start.line) { | ||
return; | ||
} | ||
var nodeIndent = getNodeIndent(node); | ||
var elementsIndent = nodeIndent + indentSize; | ||
// Elements can have double indent (detected by first item) | ||
if (elements.length > 0 && | ||
getNodeIndent(elements[0]) === elementsIndent + indentSize) { | ||
elementsIndent = elementsIndent + indentSize; | ||
} | ||
// Comma can be placed before property name | ||
checkNodesIndent(elements, elementsIndent, true); | ||
if (elements.length > 0) { | ||
// Skip last block line check if last item in same line | ||
if (elements[elements.length - 1].loc.end.line === node.loc.end.line) { | ||
return; | ||
} | ||
}); | ||
} | ||
return outdentNode; | ||
checkLastNodeLineIndent(node, elementsIndent - indentSize); | ||
} | ||
/** | ||
* Returns block containing node | ||
* @param {ASTNode} node - targeted node | ||
* @returns {ASTNode} - block node | ||
* Check indentation for blocks | ||
* @param {ASTNode} node node to check | ||
* @returns {void} | ||
*/ | ||
function getBlockNodeToMark(node) { | ||
var parent = node.parent; | ||
function blockIndentationCheck(node) { | ||
// Skip inline blocks | ||
if (isSingleLineNode(node)) { | ||
return; | ||
} | ||
// The parent of an else is the entire if/else block. To avoid over indenting | ||
// in the case of a non-block if with a block else, mark push where the else starts, | ||
// not where the if starts! | ||
if (parent.type === "IfStatement" && parent.alternate === node) { | ||
return node; | ||
if (node.parent && (node.parent.type === "FunctionExpression")) { | ||
checkIndentInFunctionBlock(node); | ||
return; | ||
} | ||
// The end line to check of a do while statement needs to be the location of the | ||
// closing curly brace, not the while statement, to avoid marking the last line of | ||
// a multiline while as a line to check. | ||
if (parent.type === "DoWhileStatement") { | ||
return node; | ||
var indent; | ||
var nodesToCheck = []; | ||
// For this statements we should check indent from statement begin | ||
// (not from block begin) | ||
var statementsWithProperties = [ | ||
"IfStatement", "WhileStatement", "ForStatement", "ForInStatement", "ForOfStatement", "DoWhileStatement" | ||
]; | ||
if (node.parent && statementsWithProperties.indexOf(node.parent.type) !== -1) { | ||
indent = getNodeIndent(node.parent); | ||
} else { | ||
indent = getNodeIndent(node); | ||
} | ||
// Detect bare blocks: a block whose parent doesn"t expect blocks in its syntax specifically. | ||
if (blockParents.indexOf(parent.type) === -1) { | ||
return node; | ||
if (node.type === "IfStatement" && node.consequent.type !== "BlockStatement") { | ||
nodesToCheck = [node.consequent]; | ||
} else if (util.isArray(node.body)) { | ||
nodesToCheck = node.body; | ||
} else { | ||
nodesToCheck = [node.body]; | ||
} | ||
return parent; | ||
} | ||
if (nodesToCheck.length > 0) { | ||
checkNodesIndent(nodesToCheck, indent + indentSize); | ||
} | ||
/** | ||
* Get node's children | ||
* @param {ASTNode} node - current node | ||
* @returns {ASTNode[]} - children | ||
*/ | ||
function getChildren(node) { | ||
var childrenProperty = indentableNodes[node.type]; | ||
return node[childrenProperty]; | ||
} | ||
/** | ||
* Gets indentation in line `i` | ||
* @param {Number} i - number of line to get indentation | ||
* @returns {Number} - count of indentation symbols | ||
*/ | ||
function getIndentationFromLine(i) { | ||
var rNotIndentChar = new RegExp("[^" + indentChar + "]"); | ||
var firstContent = lines[i].search(rNotIndentChar); | ||
if (firstContent === -1) { | ||
firstContent = lines[i].length; | ||
if (node.type === "BlockStatement") { | ||
checkLastNodeLineIndent(node, indent); | ||
} | ||
return firstContent; | ||
} | ||
/** | ||
* Compares expected and actual indentation | ||
* and reports any violations | ||
* @param {ASTNode} node - node used only for reporting | ||
* Check indentation for variable decalartions | ||
* @param {ASTNode} node node to examine | ||
* @returns {void} | ||
*/ | ||
function checkIndentations(node) { | ||
linesToCheck.forEach(function(line, i) { | ||
var actualIndentation = getIndentationFromLine(i); | ||
var expectedIndentation = getExpectedIndentation(line, actualIndentation); | ||
function checkIndentInVariableDeclarations(node) { | ||
var elements = node.declarations; | ||
if (line.check) { | ||
// Skip if first element is in same line with this node | ||
if (elements.length > 0 && elements[0].loc.start.line === node.loc.start.line) { | ||
elements = elements.slice(1); // remove the first element | ||
} | ||
var nodeIndent = getNodeIndent(node); | ||
var elementsIndent = nodeIndent + indentSize; | ||
if (actualIndentation !== expectedIndentation) { | ||
context.report(node, | ||
{line: i + 1, column: expectedIndentation}, | ||
"Expected indentation of " + expectedIndentation + " characters."); | ||
// correct the indentation so that future lines | ||
// can be validated appropriately | ||
actualIndentation = expectedIndentation; | ||
} | ||
// Comma can be placed before decalartion | ||
checkNodesIndent(elements, elementsIndent, true); | ||
if (elements.length > 0) { | ||
// Skip last block line check if last item in same line | ||
if (elements[elements.length - 1].loc.end.line === node.loc.end.line) { | ||
return; | ||
} | ||
} | ||
if (line.push.length) { | ||
pushExpectedIndentations(line, actualIndentation); | ||
} | ||
}); | ||
checkLastNodeLineIndent(node, elementsIndent - indentSize); | ||
} | ||
/** | ||
* Counts expected indentation for given line number | ||
* @param {Number} line - line number | ||
* @param {Number} actual - actual indentation | ||
* @returns {number} - expected indentation | ||
* Check and decide wheteher to check for indentation for blockless nodes | ||
* Scenarios are for or while statements without brances around them | ||
* @param {ASTNode} node node to examine | ||
* @returns {void} | ||
*/ | ||
function getExpectedIndentation(line, actual) { | ||
var outdent = indentSize * Math.max.apply(null, line.pop); | ||
function blockLessNodes(node) { | ||
if (node.body.type !== "BlockStatement") { | ||
blockIndentationCheck(node); | ||
} | ||
} | ||
var idx = indentStack.length - 1; | ||
var expected = indentStack[idx]; | ||
/** | ||
* Returns the expected indentation for the case statement | ||
* @param {ASTNode} node node to examine | ||
* @param {int} switchIndent indent for switch statement | ||
* @returns {int} indent size | ||
*/ | ||
function expectedCaseIndent(node, switchIndent) { | ||
var switchNode = (node.type === "SwitchStatement") ? node : node.parent; | ||
var caseIndent; | ||
if (!Array.isArray(expected)) { | ||
expected = [expected]; | ||
} | ||
if (caseIndentStore[switchNode.loc.start.line]) { | ||
return caseIndentStore[switchNode.loc.start.line]; | ||
} else { | ||
if (typeof switchIndent === "undefined") { | ||
switchIndent = getNodeIndent(switchNode); | ||
} | ||
expected = expected.map(function(value) { | ||
if (line.pop.length) { | ||
value -= outdent; | ||
caseIndent = switchIndent + indentSize; | ||
if (switchNode.cases.length > 0 && !options.indentSwitchCase) { | ||
caseIndent = switchIndent; | ||
} | ||
return value; | ||
}).reduce(function(previous, current) { | ||
// when the expected is an array, resolve the value | ||
// back into a Number by checking both values are the actual indentation | ||
return actual === current ? current : previous; | ||
}); | ||
caseIndentStore[switchNode.loc.start.line] = caseIndent; | ||
return caseIndent; | ||
} | ||
} | ||
indentStack[idx] = expected; | ||
return { | ||
"Program": function(node) { | ||
var nodeIndent = getNodeIndent(node); | ||
line.pop.forEach(function() { | ||
indentStack.pop(); | ||
}); | ||
// Root nodes should have no indent | ||
checkNodesIndent(node.body, nodeIndent); | ||
}, | ||
return expected; | ||
} | ||
"BlockStatement": blockIndentationCheck, | ||
/** | ||
* Store in stack expected indentations | ||
* @param {Number} line - current line | ||
* @param {Number} actualIndentation - actual indentation at current line | ||
* @returns {void} | ||
*/ | ||
function pushExpectedIndentations(line, actualIndentation) { | ||
var indents = Math.max.apply(null, line.push); | ||
var expected = actualIndentation + (indentSize * indents); | ||
"WhileStatement": blockLessNodes, | ||
// when a line has alternate indentations, push an array of possible values | ||
// on the stack, to be resolved when checked against an actual indentation | ||
if (line.pushAltLine.length) { | ||
expected = [expected]; | ||
line.pushAltLine.forEach(function(altLine) { | ||
expected.push(getIndentationFromLine(altLine) + (indentSize * indents)); | ||
}); | ||
} | ||
"ForStatement": blockLessNodes, | ||
line.push.forEach(function() { | ||
indentStack.push(expected); | ||
}); | ||
} | ||
"ForInStatement": blockLessNodes, | ||
//-------------------------------------------------------------------------- | ||
// Public | ||
//-------------------------------------------------------------------------- | ||
"ForOfStatement": blockLessNodes, | ||
return { | ||
"Program": function (node) { | ||
lines = context.getSourceLines(); | ||
linesToCheck = lines.map(function () { | ||
return { | ||
push: [], | ||
pushAltLine: [], | ||
pop: [], | ||
check: false | ||
}; | ||
}); | ||
"DoWhileStatement": blockLessNodes, | ||
if (!isMultiline(node)) { | ||
return; | ||
"IfStatement": function(node) { | ||
if (node.consequent.type !== "BlockStatement" && node.consequent.loc.start.line > node.loc.start.line) { | ||
blockIndentationCheck(node); | ||
} | ||
markChildren(node); | ||
}, | ||
"Program:exit": function (node) { | ||
checkIndentations(node); | ||
}, | ||
"BlockStatement": function (node) { | ||
if (!isMultiline(node)) { | ||
return; | ||
"VariableDeclaration": function(node) { | ||
if (node.declarations[node.declarations.length - 1].loc.start.line > node.declarations[0].loc.start.line) { | ||
checkIndentInVariableDeclarations(node); | ||
} | ||
markChildren(node); | ||
markPop(node, 1); | ||
markPushAndEndCheck(getBlockNodeToMark(node), 1); | ||
}, | ||
"IfStatement": function (node) { | ||
markAlternateBlockStatement(node, "alternate"); | ||
"ObjectExpression": function(node) { | ||
checkIndentInArrayOrObjectBlock(node); | ||
}, | ||
"TryStatement": function (node) { | ||
markAlternateBlockStatement(node, "handler"); | ||
markAlternateBlockStatement(node, "finalizer"); | ||
"ArrayExpression": function(node) { | ||
checkIndentInArrayOrObjectBlock(node); | ||
}, | ||
"SwitchStatement": function (node) { | ||
if (!isMultiline(node)) { | ||
return; | ||
} | ||
"SwitchStatement": function(node) { | ||
// Switch is not a 'BlockStatement' | ||
var switchIndent = getNodeIndent(node); | ||
var caseIndent = expectedCaseIndent(node, switchIndent); | ||
checkNodesIndent(node.cases, caseIndent); | ||
var indents = 1; | ||
var children = getChildren(node); | ||
if (children.length && node.loc.start.column === children[0].loc.start.column) { | ||
indents = 0; | ||
} | ||
markChildren(node); | ||
markPop(node, indents); | ||
markPushAndEndCheck(node, indents); | ||
checkLastNodeLineIndent(node, switchIndent); | ||
}, | ||
"SwitchCase": function (node) { | ||
if (!options.indentSwitchCase) { | ||
"SwitchCase": function(node) { | ||
// Skip inline cases | ||
if (isSingleLineNode(node)) { | ||
return; | ||
} | ||
if (!isMultiline(node)) { | ||
return; | ||
} | ||
var children = getChildren(node); | ||
if (children.length === 1 && children[0].type === "BlockStatement") { | ||
return; | ||
} | ||
markPush(node, 1); | ||
markCheck(node); | ||
markChildren(node); | ||
markCase(node, children); | ||
}, | ||
// indentations inside of function expressions can be offset from | ||
// either the start of the function or the end of the function, therefore | ||
// mark all starting lines of functions as potential indentations | ||
"FunctionDeclaration": function (node) { | ||
markPushAlt(node); | ||
}, | ||
"FunctionExpression": function (node) { | ||
markPushAlt(node); | ||
var caseIndent = expectedCaseIndent(node); | ||
checkNodesIndent(node.consequent, caseIndent + indentSize); | ||
} | ||
@@ -464,0 +408,0 @@ }; |
@@ -300,3 +300,3 @@ /** | ||
return { | ||
"Property": function (node) { | ||
"Property": function(node) { | ||
verifySpacing(node); | ||
@@ -303,0 +303,0 @@ } |
@@ -15,3 +15,3 @@ /** | ||
module.exports = function (context) { | ||
module.exports = function(context) { | ||
var EXPECTED_LF_MSG = "Expected linebreaks to be 'LF' but found 'CRLF'.", | ||
@@ -18,0 +18,0 @@ EXPECTED_CRLF_MSG = "Expected linebreaks to be 'CRLF' but found 'LF'."; |
@@ -36,3 +36,3 @@ /** | ||
var lines = []; | ||
comments.forEach(function (token) { | ||
comments.forEach(function(token) { | ||
var start = token.loc.start.line; | ||
@@ -39,0 +39,0 @@ var end = token.loc.end.line; |
@@ -24,3 +24,3 @@ /** | ||
// Cache line numbers of comments for faster lookup | ||
var comments = context.getAllComments().map(function (token) { | ||
var comments = context.getAllComments().map(function(token) { | ||
return token.loc.start.line; | ||
@@ -27,0 +27,0 @@ }); |
@@ -55,3 +55,3 @@ /** | ||
function findReference(scope, node) { | ||
var references = scope.references.filter(function (reference) { | ||
var references = scope.references.filter(function(reference) { | ||
return reference.identifier.range[0] === node.range[0] && | ||
@@ -73,3 +73,3 @@ reference.identifier.range[1] === node.range[1]; | ||
function isGloballyShadowed(globalScope, identifierName) { | ||
return globalScope.variables.some(function (variable) { | ||
return globalScope.variables.some(function(variable) { | ||
return variable.name === identifierName && variable.defs.length > 0; | ||
@@ -125,3 +125,3 @@ }); | ||
"Program": function () { | ||
"Program": function() { | ||
globalScope = context.getScope(); | ||
@@ -128,0 +128,0 @@ }, |
@@ -41,7 +41,7 @@ /** | ||
"SwitchStatement": function(/*node*/) { | ||
"SwitchStatement": function(/* node */) { | ||
switchStatement.push({}); | ||
}, | ||
"SwitchStatement:exit": function(/*node*/) { | ||
"SwitchStatement:exit": function(/* node */) { | ||
switchStatement.pop(); | ||
@@ -48,0 +48,0 @@ }, |
@@ -101,3 +101,3 @@ /** | ||
"IfStatement": function (node) { | ||
"IfStatement": function(node) { | ||
// Don't bother finding a ReturnStatement, if there's no `else` | ||
@@ -111,3 +111,3 @@ // or if the alternate is also an if (indicating an else if). | ||
var body = consequent.body; | ||
body.forEach(function (bodyNode) { | ||
body.forEach(function(bodyNode) { | ||
checkForReturnOrIf(bodyNode, alternate); | ||
@@ -114,0 +114,0 @@ }); |
@@ -44,1 +44,3 @@ /** | ||
}; | ||
module.exports.schema = []; |
@@ -15,3 +15,3 @@ /** | ||
return { | ||
"UnaryExpression": function (node) { | ||
"UnaryExpression": function(node) { | ||
var ancestors = context.getAncestors(), | ||
@@ -18,0 +18,0 @@ parent = ancestors.pop(), |
@@ -8,3 +8,3 @@ /** | ||
var FALLTHROUGH_COMMENT = /falls\sthrough/; | ||
var FALLTHROUGH_COMMENT = /falls?\s?through/i; | ||
@@ -11,0 +11,0 @@ //------------------------------------------------------------------------------ |
@@ -31,3 +31,3 @@ /** | ||
errors = errors.filter(function (error) { | ||
errors = errors.filter(function(error) { | ||
var errorLoc = error[1]; | ||
@@ -67,3 +67,3 @@ if (errorLoc.line >= locStart.line && errorLoc.line <= locEnd.line) { | ||
sourceLines.forEach(function (sourceLine, lineIndex) { | ||
sourceLines.forEach(function(sourceLine, lineIndex) { | ||
var lineNumber = lineIndex + 1, | ||
@@ -113,3 +113,3 @@ location, | ||
return { | ||
"Program": function (node) { | ||
"Program": function(node) { | ||
/** | ||
@@ -128,6 +128,6 @@ * As we can easily fire warnings for all white space issues with all the source its simpler to fire them here | ||
"Literal": removeInvalidNodeErrors, | ||
"Program:exit": function () { | ||
"Program:exit": function() { | ||
// If we have any errors remaining report on them | ||
errors.forEach(function (error) { | ||
errors.forEach(function(error) { | ||
context.report.apply(this, error); | ||
@@ -134,0 +134,0 @@ }); |
@@ -10,2 +10,78 @@ /** | ||
//------------------------------------------------------------------------------ | ||
// Helpers | ||
//------------------------------------------------------------------------------ | ||
/** | ||
* Gets the containing loop node of a specified node. | ||
* | ||
* We don't need to check nested functions, so this ignores those. | ||
* `Scope.through` contains references of nested functions. | ||
* | ||
* @param {ASTNode} node - An AST node to get. | ||
* @returns {ASTNode|null} The containing loop node of the specified node, or `null`. | ||
*/ | ||
function getContainingLoopNode(node) { | ||
var parent = node.parent; | ||
while (parent != null) { | ||
switch (parent.type) { | ||
case "WhileStatement": | ||
case "DoWhileStatement": | ||
return parent; | ||
case "ForStatement": | ||
// `init` is outside of the loop. | ||
if (parent.init !== node) { | ||
return parent; | ||
} | ||
break; | ||
case "ForInStatement": | ||
case "ForOfStatement": | ||
// `right` is outside of the loop. | ||
if (parent.right !== node) { | ||
return parent; | ||
} | ||
break; | ||
case "ArrowFunctionExpression": | ||
case "FunctionExpression": | ||
case "FunctionDeclaration": | ||
// We don't need to check nested functions. | ||
return null; | ||
default: | ||
break; | ||
} | ||
node = parent; | ||
parent = node.parent; | ||
} | ||
return null; | ||
} | ||
/** | ||
* Checks whether or not a reference refers to a variable that is block-binding in the loop. | ||
* @param {ASTNode} loopNode - A containing loop node. | ||
* @param {escope.Reference} reference - A reference to check. | ||
* @returns {boolean} Whether or not a reference refers to a variable that is block-binding in the loop. | ||
*/ | ||
function isBlockBindingsInLoop(loopNode, reference) { | ||
// A reference to a `let`/`const` variable always has a resolved variable. | ||
var variable = reference.resolved; | ||
var definition = variable && variable.defs[0]; | ||
var declaration = definition && definition.parent; | ||
return ( | ||
// Checks whether this is `let`/`const`. | ||
declaration != null && | ||
declaration.type === "VariableDeclaration" && | ||
(declaration.kind === "let" || declaration.kind === "const") && | ||
// Checks whether this is in the loop. | ||
declaration.range[0] > loopNode.range[0] && | ||
declaration.range[1] < loopNode.range[1] | ||
); | ||
} | ||
//------------------------------------------------------------------------------ | ||
// Rule Definition | ||
@@ -16,3 +92,7 @@ //------------------------------------------------------------------------------ | ||
/** | ||
* Reports if the given node has an ancestor node which is a loop. | ||
* Reports such functions: | ||
* | ||
* - has an ancestor node which is a loop. | ||
* - has a reference that refers to a variable that is block-binding in the loop. | ||
* | ||
* @param {ASTNode} node The AST node to check. | ||
@@ -22,29 +102,9 @@ * @returns {boolean} Whether or not the node is within a loop. | ||
function checkForLoops(node) { | ||
var ancestors = context.getAncestors(); | ||
/** | ||
* 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; | ||
} | ||
var loopNode = getContainingLoopNode(node); | ||
if (loopNode == null) { | ||
return; | ||
} | ||
if (ancestors.some(isInLoop)) { | ||
var references = context.getScope().through; | ||
if (references.length > 0 && !references.every(isBlockBindingsInLoop.bind(null, loopNode))) { | ||
context.report(node, "Don't make functions within a loop"); | ||
@@ -51,0 +111,0 @@ } |
@@ -110,3 +110,3 @@ /** | ||
declarations.forEach(function (declaration) { | ||
declarations.forEach(function(declaration) { | ||
var type = getDeclarationType(declaration.init); | ||
@@ -131,3 +131,3 @@ contains[type] = true; | ||
declarations.forEach(function (declaration) { | ||
declarations.forEach(function(declaration) { | ||
if (getDeclarationType(declaration.init) === DECL_REQUIRE) { | ||
@@ -134,0 +134,0 @@ found[inferModuleType(declaration.init)] = true; |
@@ -23,3 +23,3 @@ /** | ||
if (options && options.exceptions) { | ||
Object.keys(options.exceptions).forEach(function (key) { | ||
Object.keys(options.exceptions).forEach(function(key) { | ||
if (options.exceptions[key]) { | ||
@@ -26,0 +26,0 @@ exceptions[key] = true; |
@@ -11,3 +11,3 @@ /** | ||
module.exports = function (context) { | ||
module.exports = function(context) { | ||
// trim restricted module names | ||
@@ -61,3 +61,3 @@ var restrictedModules = context.options; | ||
return { | ||
"CallExpression": function (node) { | ||
"CallExpression": function(node) { | ||
if (isRequireCall(node)) { | ||
@@ -64,0 +64,0 @@ var restrictedModuleName = getRestrictedModuleName(node); |
@@ -5,4 +5,4 @@ /** | ||
*/ | ||
/*jshint scripturl: true */ | ||
/*eslint no-script-url: 0*/ | ||
/* jshint scripturl: true */ | ||
/* eslint no-script-url: 0 */ | ||
@@ -9,0 +9,0 @@ "use strict"; |
@@ -97,3 +97,3 @@ /** | ||
function isContainedInScopeVars(variable, scopeVars) { | ||
return scopeVars.some(function (scopeVar) { | ||
return scopeVars.some(function(scopeVar) { | ||
return ( | ||
@@ -119,3 +119,3 @@ scopeVar.identifiers.length > 0 && | ||
variables.forEach(function (variable) { | ||
variables.forEach(function(variable) { | ||
// "arguments" is a special case that has no identifiers (#1759) | ||
@@ -159,3 +159,3 @@ if (variable.identifiers.length > 0 && isContainedInScopeVars(variable, scope.variables)) { | ||
return { | ||
"Program:exit": function () { | ||
"Program:exit": function() { | ||
var globalScope = context.getScope(), | ||
@@ -162,0 +162,0 @@ stack = globalScope.childScopes.slice(), |
@@ -6,3 +6,3 @@ /** | ||
/*jshint node:true*/ | ||
/* jshint node:true */ | ||
@@ -9,0 +9,0 @@ "use strict"; |
@@ -67,3 +67,3 @@ /** | ||
"Program:exit": function(/*node*/) { | ||
"Program:exit": function(/* node */) { | ||
@@ -70,0 +70,0 @@ var globalScope = context.getScope(); |
@@ -98,5 +98,5 @@ /** | ||
function isUsedVariable(variable, references) { | ||
var functionNodes = variable.defs.filter(function (def) { | ||
var functionNodes = variable.defs.filter(function(def) { | ||
return def.type === "FunctionName"; | ||
}).map(function (def) { | ||
}).map(function(def) { | ||
return def.node; | ||
@@ -106,3 +106,3 @@ }), | ||
return references.some(function (ref) { | ||
return references.some(function(ref) { | ||
return isReadRef(ref) && !(isFunctionDefinition && isSelfReference(ref, functionNodes)); | ||
@@ -161,3 +161,3 @@ }); | ||
} | ||
// skip function expression names | ||
// skip function expression names and variables marked with markVariableAsUsed() | ||
if (scope.functionExpressionScope || variable.eslintUsed) { | ||
@@ -225,3 +225,5 @@ continue; | ||
if (unusedVar.eslintExplicitGlobal) { | ||
if (unusedVar.eslintUsed) { | ||
continue; // explicitly exported variables | ||
} else if (unusedVar.eslintExplicitGlobal) { | ||
context.report(programNode, MESSAGE, unusedVar); | ||
@@ -228,0 +230,0 @@ } else if (unusedVar.defs.length > 0) { |
@@ -16,3 +16,3 @@ /** | ||
return { | ||
"VariableDeclaration": function (node) { | ||
"VariableDeclaration": function(node) { | ||
if (node.kind === "var") { | ||
@@ -19,0 +19,0 @@ context.report(node, "Unexpected var, use let or const instead."); |
@@ -12,3 +12,3 @@ /** | ||
module.exports = function (context) { | ||
module.exports = function(context) { | ||
@@ -58,3 +58,3 @@ var configuration = context.options[0] || {}, | ||
warningRegExps.forEach(function (regex, index) { | ||
warningRegExps.forEach(function(regex, index) { | ||
if (regex.test(comment)) { | ||
@@ -76,3 +76,3 @@ matches.push(warningTerms[index]); | ||
matches.forEach(function (matchedTerm) { | ||
matches.forEach(function(matchedTerm) { | ||
context.report(node, "Unexpected " + matchedTerm + " comment."); | ||
@@ -79,0 +79,0 @@ }); |
@@ -102,3 +102,3 @@ /** | ||
"AssignmentExpression": validateBinaryExpression, | ||
"VariableDeclarator": function (node) { | ||
"VariableDeclarator": function(node) { | ||
if (node.init) { | ||
@@ -105,0 +105,0 @@ validateBinaryExpression(node); |
@@ -13,3 +13,3 @@ /** | ||
module.exports = function (context) { | ||
module.exports = function(context) { | ||
var requirePadding = context.options[0] !== "never"; | ||
@@ -21,2 +21,26 @@ | ||
/** | ||
* Retrieves an array of all comments defined inside the given node. | ||
* @param {ASTNode} node The AST node. | ||
* @returns {ASTNode[]} An array of comment nodes. | ||
*/ | ||
function getCommentsInNode(node) { | ||
var allComments = context.getAllComments(); | ||
return allComments.filter(function(comment) { | ||
return node.range[0] < comment.range[0] && | ||
node.range[1] > comment.range[1]; | ||
}); | ||
} | ||
/** | ||
* Checks if the location of a node or token is before the location of another node or token | ||
* @param {ASTNode|Token} a The node or token to check if its location is before b. | ||
* @param {ASTNode|Token} b The node or token which will be compared with a. | ||
* @returns {boolean} True if a is located before b. | ||
*/ | ||
function isLocatedBefore(a, b) { | ||
return a.range[1] < b.range[0]; | ||
} | ||
/** | ||
* Checks if the given non empty block node has a blank line before its first child node. | ||
@@ -31,6 +55,7 @@ * @param {ASTNode} node The AST node of a BlockStatement. | ||
expectedFirstLine = blockStart + 2, | ||
leadingComments = context.getComments(first).leading; | ||
comments = getCommentsInNode(node), | ||
firstComment = comments[0]; | ||
if (leadingComments.length > 0) { | ||
firstLine = leadingComments[0].loc.start.line; | ||
if (firstComment && isLocatedBefore(firstComment, first)) { | ||
firstLine = firstComment.loc.start.line; | ||
} | ||
@@ -49,8 +74,10 @@ | ||
last = node.body[node.body.length - 1], | ||
lastLine = context.getLastToken(last).loc.end.line, | ||
lastToken = context.getLastToken(last), | ||
lastLine = lastToken.loc.end.line, | ||
expectedLastLine = blockEnd - 2, | ||
trailingComments = context.getComments(last).trailing; | ||
comments = getCommentsInNode(node), | ||
lastComment = comments[comments.length - 1]; | ||
if (trailingComments.length > 0) { | ||
lastLine = trailingComments[trailingComments.length - 1].loc.end.line; | ||
if (lastComment && isLocatedBefore(lastToken, lastComment)) { | ||
lastLine = lastComment.loc.end.line; | ||
} | ||
@@ -57,0 +84,0 @@ |
@@ -78,3 +78,3 @@ /** | ||
return { | ||
"Program:exit": function () { | ||
"Program:exit": function() { | ||
var stack = [context.getScope()]; | ||
@@ -81,0 +81,0 @@ while (stack.length) { |
@@ -13,3 +13,3 @@ /** | ||
module.exports = function (context) { | ||
module.exports = function(context) { | ||
@@ -143,3 +143,3 @@ var config = context.options[0], | ||
"ThrowStatement": checkNode, | ||
"ForStatement": function (node) { | ||
"ForStatement": function(node) { | ||
if (node.init) { | ||
@@ -146,0 +146,0 @@ checkSemicolonSpacing(context.getTokenAfter(node.init), node); |
@@ -118,3 +118,3 @@ /** | ||
"ExportAllDeclaration": checkForSemicolon, | ||
"ExportNamedDeclaration": function (node) { | ||
"ExportNamedDeclaration": function(node) { | ||
if (!node.declaration) { | ||
@@ -124,3 +124,3 @@ checkForSemicolon(node); | ||
}, | ||
"ExportDefaultDeclaration": function (node) { | ||
"ExportDefaultDeclaration": function(node) { | ||
if (!/(?:Class|Function)Declaration/.test(node.declaration.type)) { | ||
@@ -127,0 +127,0 @@ checkForSemicolon(node); |
@@ -48,3 +48,3 @@ /** | ||
return { | ||
"IfStatement": function (node) { | ||
"IfStatement": function(node) { | ||
check(node); | ||
@@ -60,3 +60,3 @@ // check the `else` | ||
"WhileStatement": check, | ||
"DoWhileStatement": function (node) { | ||
"DoWhileStatement": function(node) { | ||
check(node); | ||
@@ -68,3 +68,3 @@ // check the `while` | ||
"SwitchStatement": check, | ||
"TryStatement": function (node) { | ||
"TryStatement": function(node) { | ||
check(node); | ||
@@ -71,0 +71,0 @@ // check the `finally` |
@@ -13,3 +13,3 @@ /** | ||
module.exports = function (context) { | ||
module.exports = function(context) { | ||
var requireSpace = context.options[0] !== "never"; | ||
@@ -16,0 +16,0 @@ |
@@ -182,3 +182,3 @@ /** | ||
function sortSkipRanges() { | ||
skipRanges.sort(function (a, b) { | ||
skipRanges.sort(function(a, b) { | ||
return a[0] - b[0]; | ||
@@ -185,0 +185,0 @@ }); |
@@ -21,3 +21,3 @@ /** | ||
var markerMatcher = new RegExp(" "); | ||
var jsDocMatcher = new RegExp("((^(\\*)))[ \\n]"); | ||
var jsDocMatcher = new RegExp("((^(\\*)))[ \\r\\n]"); | ||
@@ -51,3 +51,3 @@ // Fetch the options dict | ||
// the markerMatcher includes any markers in the list, followed by space/tab | ||
markerMatcher = new RegExp("((^(" + markers.join("))|(^(") + ")))[ \\t\\n]"); | ||
markerMatcher = new RegExp("((^(" + markers.join("))|(^(") + ")))[ \\t\\r\\n]"); | ||
} | ||
@@ -88,3 +88,3 @@ | ||
// Space expected and not found | ||
if (node.value.indexOf(" ") !== 0 && node.value.indexOf("\t") !== 0 && node.value.indexOf("\n") !== 0) { | ||
if (node.value.indexOf(" ") !== 0 && node.value.indexOf("\t") !== 0 && node.value.indexOf("\n") !== 0 && node.value.indexOf("\r") !== 0) { | ||
@@ -91,0 +91,0 @@ /* |
@@ -80,70 +80,2 @@ /** | ||
//-------------------------------------------------------------------------- | ||
// "deprecated" mode (default) | ||
//-------------------------------------------------------------------------- | ||
/** | ||
* Determines if a given node is "use strict". | ||
* @param {ASTNode} node The node to check. | ||
* @returns {boolean} True if the node is a strict pragma, false if not. | ||
* @void | ||
*/ | ||
function isStrictPragma(node) { | ||
return (node && node.type === "ExpressionStatement" && | ||
node.expression.value === "use strict"); | ||
} | ||
/** | ||
* When you enter a scope, push the strict value from the previous scope | ||
* onto the stack. | ||
* @param {ASTNode} node The AST node being checked. | ||
* @returns {void} | ||
* @private | ||
*/ | ||
function enterScope(node) { | ||
var isStrict = false, | ||
isProgram = (node.type === "Program"), | ||
isParentGlobal = scopes.length === 1, | ||
isParentStrict = scopes.length ? scopes[scopes.length - 1] : false; | ||
// look for the "use strict" pragma | ||
if (isModule) { | ||
isStrict = true; | ||
} else if (isProgram) { | ||
isStrict = isStrictPragma(node.body[0]) || isParentStrict; | ||
} else { | ||
isStrict = node.body.body && isStrictPragma(node.body.body[0]) || isParentStrict; | ||
} | ||
scopes.push(isStrict); | ||
// never warn if the parent is strict or the function is strict | ||
if (!isParentStrict && !isStrict && isParentGlobal) { | ||
context.report(node, "Missing \"use strict\" statement."); | ||
} | ||
} | ||
/** | ||
* When you exit a scope, pop off the top scope and see if it's true or | ||
* false. | ||
* @returns {void} | ||
* @private | ||
*/ | ||
function exitScope() { | ||
scopes.pop(); | ||
} | ||
modes.deprecated = { | ||
"Program": enterScope, | ||
"FunctionDeclaration": enterScope, | ||
"FunctionExpression": enterScope, | ||
"ArrowFunctionExpression": enterScope, | ||
"Program:exit": exitScope, | ||
"FunctionDeclaration:exit": exitScope, | ||
"FunctionExpression:exit": exitScope, | ||
"ArrowFunctionExpression:exit": exitScope | ||
}; | ||
//-------------------------------------------------------------------------- | ||
// "never" mode | ||
@@ -189,3 +121,3 @@ //-------------------------------------------------------------------------- | ||
//-------------------------------------------------------------------------- | ||
// "function" mode | ||
// "function" mode (Default) | ||
//-------------------------------------------------------------------------- | ||
@@ -232,7 +164,11 @@ | ||
"FunctionExpression": enterFunction, | ||
"ArrowFunctionExpression": enterFunction, | ||
"FunctionDeclaration:exit": exitFunction, | ||
"FunctionExpression:exit": exitFunction | ||
"FunctionExpression:exit": exitFunction, | ||
"ArrowFunctionExpression:exit": exitFunction | ||
}; | ||
return modes[mode || "deprecated"]; | ||
return modes[mode || "function"]; | ||
@@ -239,0 +175,0 @@ }; |
@@ -14,6 +14,6 @@ /** | ||
module.exports = function (context) { | ||
module.exports = function(context) { | ||
return { | ||
"BinaryExpression": function (node) { | ||
"BinaryExpression": function(node) { | ||
if (/^(?:[<>]|[!=]=)=?$/.test(node.operator) && (node.left.name === "NaN" || node.right.name === "NaN")) { | ||
@@ -20,0 +20,0 @@ context.report(node, "Use the isNaN function to compare with NaN."); |
@@ -37,7 +37,10 @@ /** | ||
* When parsing a new function, store it in our function stack. | ||
* @param {ASTNode} node A function node to check. | ||
* @returns {void} | ||
* @private | ||
*/ | ||
function startFunction() { | ||
fns.push({returnPresent: false}); | ||
function startFunction(node) { | ||
fns.push({ | ||
returnPresent: (node.type === "ArrowFunctionExpression" && node.body.type !== "BlockStatement") | ||
}); | ||
} | ||
@@ -44,0 +47,0 @@ |
@@ -22,3 +22,3 @@ /** | ||
"UnaryExpression": function (node) { | ||
"UnaryExpression": function(node) { | ||
var parent, sibling; | ||
@@ -25,0 +25,0 @@ |
@@ -14,3 +14,3 @@ /** | ||
module.exports = function (context) { | ||
module.exports = function(context) { | ||
var errorMessage = "All \"var\" declarations must be at the top of the function scope."; | ||
@@ -99,3 +99,3 @@ | ||
return { | ||
"VariableDeclaration": function (node) { | ||
"VariableDeclaration": function(node) { | ||
var ancestors = context.getAncestors(); | ||
@@ -102,0 +102,0 @@ var parent = ancestors.pop(); |
@@ -117,3 +117,3 @@ /** | ||
module.exports = function (context) { | ||
module.exports = function(context) { | ||
@@ -120,0 +120,0 @@ // Default to "never" (!always) if no option |
@@ -77,7 +77,7 @@ /** | ||
if (target && typeof target === "object") { | ||
Object.keys(target).forEach(function (key) { | ||
Object.keys(target).forEach(function(key) { | ||
dst[key] = target[key]; | ||
}); | ||
} | ||
Object.keys(src).forEach(function (key) { | ||
Object.keys(src).forEach(function(key) { | ||
if (Array.isArray(src[key]) || Array.isArray(target[key])) { | ||
@@ -84,0 +84,0 @@ dst[key] = deepmerge(target[key], src[key], key === "plugins"); |
@@ -62,2 +62,3 @@ /** | ||
callback(filePath); | ||
return; | ||
} else if (fileStat.isDirectory()) { | ||
@@ -83,2 +84,3 @@ traverse(file, stack); | ||
callback(name); | ||
return; | ||
} else { | ||
@@ -85,0 +87,0 @@ traverse(name, []); |
{ | ||
"name": "eslint", | ||
"version": "0.24.1", | ||
"version": "1.0.0-rc-1", | ||
"author": "Nicholas C. Zakas <nicholas+npm@nczconsulting.com>", | ||
@@ -42,7 +42,7 @@ "description": "An AST-based pattern checker for JavaScript.", | ||
"escape-string-regexp": "^1.0.2", | ||
"escope": "^3.1.0", | ||
"espree": "^2.0.1", | ||
"escope": "^3.2.0", | ||
"espree": "^2.2.0", | ||
"estraverse": "^4.1.0", | ||
"estraverse-fb": "^1.3.1", | ||
"globals": "^8.0.0", | ||
"globals": "^8.2.0", | ||
"inquirer": "^0.8.2", | ||
@@ -56,2 +56,3 @@ "is-my-json-valid": "^2.10.0", | ||
"path-is-absolute": "^1.0.0", | ||
"path-is-inside": "^1.0.1", | ||
"strip-json-comments": "~1.0.1", | ||
@@ -58,0 +59,0 @@ "text-table": "~0.2.0", |
[![NPM version][npm-image]][npm-url] | ||
[![build status][travis-image]][travis-url] | ||
[![Build status][appveyor-image]][appveyor-url] | ||
[![Test coverage][coveralls-image]][coveralls-url] | ||
@@ -108,2 +109,4 @@ [![Downloads][downloads-image]][downloads-url] | ||
[travis-url]: https://travis-ci.org/eslint/eslint | ||
[appveyor-image]: https://ci.appveyor.com/api/projects/status/nnjjujvp9535u3cl/branch/master?svg=true | ||
[appveyor-url]: https://ci.appveyor.com/project/nzakas/eslint/branch/master | ||
[coveralls-image]: https://img.shields.io/coveralls/eslint/eslint/master.svg?style=flat-square | ||
@@ -110,0 +113,0 @@ [coveralls-url]: https://coveralls.io/r/eslint/eslint?branch=master |
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
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
115
14
620642
23
199
16053
+ Addedpath-is-inside@^1.0.1
+ Addedpath-is-inside@1.0.2(transitive)
Updatedescope@^3.2.0
Updatedespree@^2.2.0
Updatedglobals@^8.2.0