mirror of https://github.com/lukechilds/node.git
Browse Source
PR-URL: https://github.com/nodejs/io.js/pull/2286 Reviewed-By: Roman Reiss <me@silverwind.io>process-exit-stdio-flushing
Michaël Zasso
9 years ago
committed by
Roman Reiss
4293 changed files with 155072 additions and 160046 deletions
@ -0,0 +1,21 @@ |
|||
{ |
|||
"type": "Program", |
|||
"body": [], |
|||
"sourceType": "script", |
|||
"range": [ |
|||
0, |
|||
0 |
|||
], |
|||
"loc": { |
|||
"start": { |
|||
"line": 0, |
|||
"column": 0 |
|||
}, |
|||
"end": { |
|||
"line": 0, |
|||
"column": 0 |
|||
} |
|||
}, |
|||
"comments": [], |
|||
"tokens": [] |
|||
} |
@ -1,186 +1,196 @@ |
|||
{ |
|||
"ecmaFeatures": {}, |
|||
"parser": "espree", |
|||
"env": { |
|||
"browser": false, |
|||
"node": false, |
|||
"amd": false, |
|||
"mocha": false, |
|||
"jasmine": false |
|||
}, |
|||
|
|||
"ecmaFeatures": {}, |
|||
"rules": { |
|||
"no-alert": 2, |
|||
"no-array-constructor": 2, |
|||
"no-alert": 0, |
|||
"no-array-constructor": 0, |
|||
"no-arrow-condition": 0, |
|||
"no-bitwise": 0, |
|||
"no-caller": 2, |
|||
"no-catch-shadow": 2, |
|||
"no-comma-dangle": 0, |
|||
"no-caller": 0, |
|||
"no-case-declarations": 0, |
|||
"no-catch-shadow": 0, |
|||
"no-class-assign": 0, |
|||
"no-cond-assign": 2, |
|||
"no-console": 2, |
|||
"no-const-assign": 0, |
|||
"no-constant-condition": 2, |
|||
"no-continue": 0, |
|||
"no-control-regex": 2, |
|||
"no-debugger": 2, |
|||
"no-delete-var": 2, |
|||
"no-div-regex": 0, |
|||
"no-dupe-class-members": 0, |
|||
"no-dupe-keys": 2, |
|||
"no-dupe-args": 2, |
|||
"no-duplicate-case": 2, |
|||
"no-else-return": 0, |
|||
"no-empty": 2, |
|||
"no-empty-class": 0, |
|||
"no-empty-character-class": 2, |
|||
"no-empty-label": 2, |
|||
"no-empty-label": 0, |
|||
"no-empty-pattern": 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-implicit-coercion": 0, |
|||
"no-implied-eval": 0, |
|||
"no-inline-comments": 0, |
|||
"no-inner-declarations": [2, "functions"], |
|||
"no-invalid-regexp": 2, |
|||
"no-invalid-this": 0, |
|||
"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-condition": 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, |
|||
"no-path-concat": 0, |
|||
"no-plusplus": 0, |
|||
"no-process-env": 0, |
|||
"no-process-exit": 2, |
|||
"no-proto": 2, |
|||
"no-process-exit": 0, |
|||
"no-proto": 0, |
|||
"no-redeclare": 2, |
|||
"no-regex-spaces": 2, |
|||
"no-reserved-keys": 0, |
|||
"no-restricted-modules": 0, |
|||
"no-return-assign": 2, |
|||
"no-script-url": 2, |
|||
"no-restricted-syntax": 0, |
|||
"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-useless-concat": 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, |
|||
"no-magic-numbers": 0, |
|||
|
|||
"array-bracket-spacing": [0, "never"], |
|||
"arrow-body-style": [0, "as-needed"], |
|||
"arrow-parens": 0, |
|||
"arrow-spacing": 0, |
|||
"accessor-pairs": 0, |
|||
"block-scoped-var": 0, |
|||
"block-spacing": 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"], |
|||
"global-require": 0, |
|||
"guard-for-in": 0, |
|||
"handle-callback-err": 0, |
|||
"id-length": 0, |
|||
"indent": 0, |
|||
"key-spacing": [2, { "beforeColon": false, "afterColon": true }], |
|||
"init-declarations": 0, |
|||
"jsx-quotes": [0, "prefer-double"], |
|||
"key-spacing": [0, { "beforeColon": false, "afterColon": true }], |
|||
"lines-around-comment": 0, |
|||
"max-depth": [0, 4], |
|||
"max-len": [0, 80, 4], |
|||
"max-nested-callbacks": [0, 2], |
|||
"max-params": [0, 3], |
|||
"max-statements": [0, 10], |
|||
"new-cap": 2, |
|||
"new-parens": 2, |
|||
"new-cap": 0, |
|||
"new-parens": 0, |
|||
"newline-after-var": 0, |
|||
"object-curly-spacing": [0, "never"], |
|||
"object-shorthand": 0, |
|||
"one-var": 0, |
|||
"one-var": [0, "always"], |
|||
"operator-assignment": [0, "always"], |
|||
"operator-linebreak": 0, |
|||
"padded-blocks": 0, |
|||
"prefer-arrow-callback": 0, |
|||
"prefer-const": 0, |
|||
"prefer-spread": 0, |
|||
"prefer-reflect": 0, |
|||
"prefer-template": 0, |
|||
"quote-props": 0, |
|||
"quotes": [2, "double"], |
|||
"quotes": [0, "double"], |
|||
"radix": 0, |
|||
"semi": 2, |
|||
"semi-spacing": [2, {"before": false, "after": true}], |
|||
"id-match": 0, |
|||
"require-jsdoc": 0, |
|||
"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-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, |
|||
"valid-jsdoc": 0, |
|||
"valid-typeof": 2, |
|||
"vars-on-top": 0, |
|||
"wrap-iife": 0, |
|||
"wrap-regex": 0, |
|||
"yoda": [2, "never"] |
|||
"yoda": [0, "never"] |
|||
} |
|||
} |
|||
|
@ -0,0 +1,150 @@ |
|||
{ |
|||
"id": "http://json-schema.org/draft-04/schema#", |
|||
"$schema": "http://json-schema.org/draft-04/schema#", |
|||
"description": "Core schema meta-schema", |
|||
"definitions": { |
|||
"schemaArray": { |
|||
"type": "array", |
|||
"minItems": 1, |
|||
"items": { "$ref": "#" } |
|||
}, |
|||
"positiveInteger": { |
|||
"type": "integer", |
|||
"minimum": 0 |
|||
}, |
|||
"positiveIntegerDefault0": { |
|||
"allOf": [ { "$ref": "#/definitions/positiveInteger" }, { "default": 0 } ] |
|||
}, |
|||
"simpleTypes": { |
|||
"enum": [ "array", "boolean", "integer", "null", "number", "object", "string" ] |
|||
}, |
|||
"stringArray": { |
|||
"type": "array", |
|||
"items": { "type": "string" }, |
|||
"minItems": 1, |
|||
"uniqueItems": true |
|||
} |
|||
}, |
|||
"type": "object", |
|||
"properties": { |
|||
"id": { |
|||
"type": "string", |
|||
"format": "uri" |
|||
}, |
|||
"$schema": { |
|||
"type": "string", |
|||
"format": "uri" |
|||
}, |
|||
"title": { |
|||
"type": "string" |
|||
}, |
|||
"description": { |
|||
"type": "string" |
|||
}, |
|||
"default": {}, |
|||
"multipleOf": { |
|||
"type": "number", |
|||
"minimum": 0, |
|||
"exclusiveMinimum": true |
|||
}, |
|||
"maximum": { |
|||
"type": "number" |
|||
}, |
|||
"exclusiveMaximum": { |
|||
"type": "boolean", |
|||
"default": false |
|||
}, |
|||
"minimum": { |
|||
"type": "number" |
|||
}, |
|||
"exclusiveMinimum": { |
|||
"type": "boolean", |
|||
"default": false |
|||
}, |
|||
"maxLength": { "$ref": "#/definitions/positiveInteger" }, |
|||
"minLength": { "$ref": "#/definitions/positiveIntegerDefault0" }, |
|||
"pattern": { |
|||
"type": "string", |
|||
"format": "regex" |
|||
}, |
|||
"additionalItems": { |
|||
"anyOf": [ |
|||
{ "type": "boolean" }, |
|||
{ "$ref": "#" } |
|||
], |
|||
"default": {} |
|||
}, |
|||
"items": { |
|||
"anyOf": [ |
|||
{ "$ref": "#" }, |
|||
{ "$ref": "#/definitions/schemaArray" } |
|||
], |
|||
"default": {} |
|||
}, |
|||
"maxItems": { "$ref": "#/definitions/positiveInteger" }, |
|||
"minItems": { "$ref": "#/definitions/positiveIntegerDefault0" }, |
|||
"uniqueItems": { |
|||
"type": "boolean", |
|||
"default": false |
|||
}, |
|||
"maxProperties": { "$ref": "#/definitions/positiveInteger" }, |
|||
"minProperties": { "$ref": "#/definitions/positiveIntegerDefault0" }, |
|||
"required": { "$ref": "#/definitions/stringArray" }, |
|||
"additionalProperties": { |
|||
"anyOf": [ |
|||
{ "type": "boolean" }, |
|||
{ "$ref": "#" } |
|||
], |
|||
"default": {} |
|||
}, |
|||
"definitions": { |
|||
"type": "object", |
|||
"additionalProperties": { "$ref": "#" }, |
|||
"default": {} |
|||
}, |
|||
"properties": { |
|||
"type": "object", |
|||
"additionalProperties": { "$ref": "#" }, |
|||
"default": {} |
|||
}, |
|||
"patternProperties": { |
|||
"type": "object", |
|||
"additionalProperties": { "$ref": "#" }, |
|||
"default": {} |
|||
}, |
|||
"dependencies": { |
|||
"type": "object", |
|||
"additionalProperties": { |
|||
"anyOf": [ |
|||
{ "$ref": "#" }, |
|||
{ "$ref": "#/definitions/stringArray" } |
|||
] |
|||
} |
|||
}, |
|||
"enum": { |
|||
"type": "array", |
|||
"minItems": 1, |
|||
"uniqueItems": true |
|||
}, |
|||
"type": { |
|||
"anyOf": [ |
|||
{ "$ref": "#/definitions/simpleTypes" }, |
|||
{ |
|||
"type": "array", |
|||
"items": { "$ref": "#/definitions/simpleTypes" }, |
|||
"minItems": 1, |
|||
"uniqueItems": true |
|||
} |
|||
] |
|||
}, |
|||
"allOf": { "$ref": "#/definitions/schemaArray" }, |
|||
"anyOf": { "$ref": "#/definitions/schemaArray" }, |
|||
"oneOf": { "$ref": "#/definitions/schemaArray" }, |
|||
"not": { "$ref": "#" } |
|||
}, |
|||
"dependencies": { |
|||
"exclusiveMaximum": [ "maximum" ], |
|||
"exclusiveMinimum": [ "minimum" ] |
|||
}, |
|||
"default": {} |
|||
} |
@ -0,0 +1,17 @@ |
|||
{ |
|||
"rules": { |
|||
"generator-star": ["generator-star-spacing"], |
|||
"global-strict": ["strict"], |
|||
"no-comma-dangle": ["comma-dangle"], |
|||
"no-empty-class": ["no-empty-character-class"], |
|||
"no-extra-strict": ["strict"], |
|||
"no-reserved-keys": ["quote-props"], |
|||
"no-space-before-semi": ["semi-spacing"], |
|||
"no-wrap-func": ["no-extra-parens"], |
|||
"space-after-function-name": ["space-before-function-paren"], |
|||
"space-before-function-parentheses": ["space-before-function-paren"], |
|||
"space-in-brackets": ["object-curly-spacing", "array-bracket-spacing", "computed-property-spacing"], |
|||
"space-unary-word-ops": ["space-unary-ops"], |
|||
"spaced-line-comment": ["spaced-comment"] |
|||
} |
|||
} |
@ -0,0 +1,154 @@ |
|||
/** |
|||
* @fileoverview Common utils for AST. |
|||
* @author Gyandeep Singh |
|||
* @copyright 2015 Gyandeep Singh. All rights reserved. |
|||
* See LICENSE file in root directory for full license. |
|||
*/ |
|||
|
|||
"use strict"; |
|||
|
|||
//------------------------------------------------------------------------------
|
|||
// Requirements
|
|||
//------------------------------------------------------------------------------
|
|||
|
|||
var esutils = require("esutils"); |
|||
|
|||
//------------------------------------------------------------------------------
|
|||
// Helpers
|
|||
//------------------------------------------------------------------------------
|
|||
|
|||
/** |
|||
* Checks reference if is non initializer and writable. |
|||
* @param {Reference} reference - A reference to check. |
|||
* @param {int} index - The index of the reference in the references. |
|||
* @param {Reference[]} references - The array that the reference belongs to. |
|||
* @returns {boolean} Success/Failure |
|||
* @private |
|||
*/ |
|||
function isModifyingReference(reference, index, references) { |
|||
var identifier = reference.identifier; |
|||
|
|||
return (identifier && |
|||
reference.init === false && |
|||
reference.isWrite() && |
|||
// Destructuring assignments can have multiple default value,
|
|||
// so possibly there are multiple writeable references for the same identifier.
|
|||
(index === 0 || references[index - 1].identifier !== identifier) |
|||
); |
|||
} |
|||
|
|||
//------------------------------------------------------------------------------
|
|||
// Public Interface
|
|||
//------------------------------------------------------------------------------
|
|||
|
|||
module.exports = { |
|||
|
|||
/** |
|||
* Determines whether two adjacent tokens are on the same line. |
|||
* @param {Object} left - The left token object. |
|||
* @param {Object} right - The right token object. |
|||
* @returns {boolean} Whether or not the tokens are on the same line. |
|||
* @public |
|||
*/ |
|||
isTokenOnSameLine: function(left, right) { |
|||
return left.loc.end.line === right.loc.start.line; |
|||
}, |
|||
|
|||
/** |
|||
* Checks whether or not a node is `null` or `undefined`. |
|||
* @param {ASTNode} node - A node to check. |
|||
* @returns {boolean} Whether or not the node is a `null` or `undefined`. |
|||
* @public |
|||
*/ |
|||
isNullOrUndefined: function(node) { |
|||
return ( |
|||
(node.type === "Literal" && node.value === null) || |
|||
(node.type === "Identifier" && node.name === "undefined") || |
|||
(node.type === "UnaryExpression" && node.operator === "void") |
|||
); |
|||
}, |
|||
|
|||
/** |
|||
* Checks whether or not a given node is a string literal. |
|||
* @param {ASTNode} node - A node to check. |
|||
* @returns {boolean} `true` if the node is a string literal. |
|||
*/ |
|||
isStringLiteral: function(node) { |
|||
return ( |
|||
(node.type === "Literal" && typeof node.value === "string") || |
|||
node.type === "TemplateLiteral" |
|||
); |
|||
}, |
|||
|
|||
/** |
|||
* Gets references which are non initializer and writable. |
|||
* @param {Reference[]} references - An array of references. |
|||
* @returns {Reference[]} An array of only references which are non initializer and writable. |
|||
* @public |
|||
*/ |
|||
getModifyingReferences: function(references) { |
|||
return references.filter(isModifyingReference); |
|||
}, |
|||
|
|||
/** |
|||
* Validate that a string passed in is surrounded by the specified character |
|||
* @param {string} val The text to check. |
|||
* @param {string} character The character to see if it's surrounded by. |
|||
* @returns {boolean} True if the text is surrounded by the character, false if not. |
|||
* @private |
|||
*/ |
|||
isSurroundedBy: function(val, character) { |
|||
return val[0] === character && val[val.length - 1] === character; |
|||
}, |
|||
|
|||
/** |
|||
* Returns whether the provided node is an ESLint directive comment or not |
|||
* @param {LineComment|BlockComment} node The node to be checked |
|||
* @returns {boolean} `true` if the node is an ESLint directive comment |
|||
*/ |
|||
isDirectiveComment: function(node) { |
|||
var comment = node.value.trim(); |
|||
return ( |
|||
node.type === "Line" && comment.indexOf("eslint-") === 0 || |
|||
node.type === "Block" && ( |
|||
comment.indexOf("global ") === 0 || |
|||
comment.indexOf("eslint ") === 0 || |
|||
comment.indexOf("eslint-") === 0 |
|||
) |
|||
); |
|||
}, |
|||
|
|||
/** |
|||
* Gets the trailing statement of a given node. |
|||
* |
|||
* if (code) |
|||
* consequent; |
|||
* |
|||
* When taking this `IfStatement`, returns `consequent;` statement. |
|||
* |
|||
* @param {ASTNode} A node to get. |
|||
* @returns {ASTNode|null} The trailing statement's node. |
|||
*/ |
|||
getTrailingStatement: esutils.ast.trailingStatement, |
|||
|
|||
/** |
|||
* Finds the variable by a given name in a given scope and its upper scopes. |
|||
* |
|||
* @param {escope.Scope} initScope - A scope to start find. |
|||
* @param {string} name - A variable name to find. |
|||
* @returns {escope.Variable|null} A found variable or `null`. |
|||
*/ |
|||
getVariableByName: function(initScope, name) { |
|||
var scope = initScope; |
|||
while (scope) { |
|||
var variable = scope.set.get(name); |
|||
if (variable) { |
|||
return variable; |
|||
} |
|||
|
|||
scope = scope.upper; |
|||
} |
|||
|
|||
return null; |
|||
} |
|||
}; |
@ -1,145 +0,0 @@ |
|||
/** |
|||
* @fileoverview Config initialization wizard. |
|||
* @author Ilya Volodin |
|||
* @copyright 2015 Ilya Volodin. All rights reserved. |
|||
*/ |
|||
|
|||
"use strict"; |
|||
|
|||
var exec = require("child_process").exec, |
|||
fs = require("fs"), |
|||
inquirer = require("inquirer"), |
|||
yaml = require("js-yaml"); |
|||
|
|||
/* istanbul ignore next: hard to test fs function */ |
|||
/** |
|||
* Create .eslintrc file in the current working directory |
|||
* @param {object} config object that contains user's answers |
|||
* @param {bool} isJson should config file be json or yaml |
|||
* @param {function} callback function to call once the file is written. |
|||
* @returns {void} |
|||
*/ |
|||
function writeFile(config, isJson, callback) { |
|||
try { |
|||
fs.writeFileSync("./.eslintrc", isJson ? JSON.stringify(config, null, 4) : yaml.safeDump(config)); |
|||
} catch (e) { |
|||
callback(e); |
|||
return; |
|||
} |
|||
if (config.plugins && config.plugins.indexOf("react") >= 0) { |
|||
exec("npm i eslint-plugin-react --save-dev", callback); |
|||
} else { |
|||
callback(); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* process user's answers and create config object |
|||
* @param {object} answers answers received from inquirer |
|||
* @returns {object} config object |
|||
*/ |
|||
function processAnswers(answers) { |
|||
var config = {rules: {}, env: {}}; |
|||
config.rules.indent = [2, answers.indent]; |
|||
config.rules.quotes = [2, answers.quotes]; |
|||
config.rules["linebreak-style"] = [2, answers.linebreak]; |
|||
config.rules.semi = [2, answers.semi ? "always" : "never"]; |
|||
if (answers.es6) { |
|||
config.env.es6 = true; |
|||
} |
|||
answers.env.forEach(function(env) { |
|||
config.env[env] = true; |
|||
}); |
|||
if (answers.jsx) { |
|||
config.ecmaFeatures = {jsx: true}; |
|||
if (answers.react) { |
|||
config.plugins = ["react"]; |
|||
} |
|||
} |
|||
return config; |
|||
} |
|||
|
|||
/* istanbul ignore next: no need to test inquirer*/ |
|||
/** |
|||
* Ask use a few questions on command prompt |
|||
* @param {function} callback callback function when file has been written |
|||
* @returns {void} |
|||
*/ |
|||
function promptUser(callback) { |
|||
inquirer.prompt([ |
|||
{ |
|||
type: "list", |
|||
name: "indent", |
|||
message: "What style of indentation do you use?", |
|||
default: "tabs", |
|||
choices: [{name: "Tabs", value: "tab"}, {name: "Spaces", value: 4}] |
|||
}, |
|||
{ |
|||
type: "list", |
|||
name: "quotes", |
|||
message: "What quotes do you use for strings?", |
|||
default: "double", |
|||
choices: [{name: "Double", value: "double"}, {name: "Single", value: "single"}] |
|||
}, |
|||
{ |
|||
type: "list", |
|||
name: "linebreak", |
|||
message: "What line endings do you use?", |
|||
default: "unix", |
|||
choices: [{name: "Unix", value: "unix"}, {name: "Windows", value: "windows"}] |
|||
}, |
|||
{ |
|||
type: "confirm", |
|||
name: "semi", |
|||
message: "Do you require semicolons?", |
|||
default: true |
|||
}, |
|||
{ |
|||
type: "confirm", |
|||
name: "es6", |
|||
message: "Are you using ECMAScript 6 features?", |
|||
default: false |
|||
}, |
|||
{ |
|||
type: "checkbox", |
|||
name: "env", |
|||
message: "Where will your code run?", |
|||
default: ["browser"], |
|||
choices: [{name: "Node", value: "node"}, {name: "Browser", value: "browser"}] |
|||
}, |
|||
{ |
|||
type: "confirm", |
|||
name: "jsx", |
|||
message: "Do you use JSX?", |
|||
default: false |
|||
}, |
|||
{ |
|||
type: "confirm", |
|||
name: "react", |
|||
message: "Do you use React", |
|||
default: false, |
|||
when: function (answers) { |
|||
return answers.jsx; |
|||
} |
|||
}, |
|||
{ |
|||
type: "list", |
|||
name: "format", |
|||
message: "What format do you want your config file to be in?", |
|||
default: "JSON", |
|||
choices: ["JSON", "YAML"] |
|||
} |
|||
], function(answers) { |
|||
var config = processAnswers(answers); |
|||
writeFile(config, answers.format === "JSON", callback); |
|||
}); |
|||
} |
|||
|
|||
var init = { |
|||
processAnswers: processAnswers, |
|||
initializeConfig: /* istanbul ignore next */ function(callback) { |
|||
promptUser(callback); |
|||
} |
|||
}; |
|||
|
|||
module.exports = init; |
@ -1,110 +0,0 @@ |
|||
/** |
|||
* @fileoverview Validates configs. |
|||
* @author Brandon Mills |
|||
* @copyright 2015 Brandon Mills |
|||
*/ |
|||
|
|||
"use strict"; |
|||
|
|||
var rules = require("./rules"), |
|||
schemaValidator = require("is-my-json-valid"); |
|||
|
|||
var validators = { |
|||
rules: Object.create(null) |
|||
}; |
|||
|
|||
/** |
|||
* Gets a complete options schema for a rule. |
|||
* @param {string} id The rule's unique name. |
|||
* @returns {object} JSON Schema for the rule's options. |
|||
*/ |
|||
function getRuleOptionsSchema(id) { |
|||
var rule = rules.get(id), |
|||
schema = rule && rule.schema; |
|||
|
|||
if (!schema) { |
|||
return { |
|||
"type": "array", |
|||
"items": [ |
|||
{ |
|||
"enum": [0, 1, 2] |
|||
} |
|||
], |
|||
"minItems": 1 |
|||
}; |
|||
} |
|||
|
|||
// Given a tuple of schemas, insert warning level at the beginning
|
|||
if (Array.isArray(schema)) { |
|||
return { |
|||
"type": "array", |
|||
"items": [ |
|||
{ |
|||
"enum": [0, 1, 2] |
|||
} |
|||
].concat(schema), |
|||
"minItems": 1, |
|||
"maxItems": schema.length + 1 |
|||
}; |
|||
} |
|||
|
|||
// Given a full schema, leave it alone
|
|||
return schema; |
|||
} |
|||
|
|||
/** |
|||
* Validates a rule's options against its schema. |
|||
* @param {string} id The rule's unique name. |
|||
* @param {array|number} options The given options for the rule. |
|||
* @param {string} source The name of the configuration source. |
|||
* @returns {void} |
|||
*/ |
|||
function validateRuleOptions(id, options, source) { |
|||
var validateRule = validators.rules[id], |
|||
message; |
|||
|
|||
if (!validateRule) { |
|||
validateRule = schemaValidator(getRuleOptionsSchema(id), { verbose: true }); |
|||
validators.rules[id] = validateRule; |
|||
} |
|||
|
|||
if (typeof options === "number") { |
|||
options = [options]; |
|||
} |
|||
|
|||
validateRule(options); |
|||
|
|||
if (validateRule.errors) { |
|||
message = [ |
|||
source, ":\n", |
|||
"\tConfiguration for rule \"", id, "\" is invalid:\n" |
|||
]; |
|||
validateRule.errors.forEach(function (error) { |
|||
message.push( |
|||
"\tValue \"", error.value, "\" ", error.message, ".\n" |
|||
); |
|||
}); |
|||
|
|||
throw new Error(message.join("")); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* Validates an entire config object. |
|||
* @param {object} config The config object to validate. |
|||
* @param {string} source The location to report with any errors. |
|||
* @returns {void} |
|||
*/ |
|||
function validate(config, source) { |
|||
if (typeof config.rules === "object") { |
|||
Object.keys(config.rules).forEach(function (id) { |
|||
validateRuleOptions(id, config.rules[id], source); |
|||
}); |
|||
} |
|||
} |
|||
|
|||
module.exports = { |
|||
getRuleOptionsSchema: getRuleOptionsSchema, |
|||
validate: validate, |
|||
validateRuleOptions: validateRuleOptions |
|||
}; |
@ -0,0 +1,440 @@ |
|||
/** |
|||
* @fileoverview Helper to locate and load configuration files. |
|||
* @author Nicholas C. Zakas |
|||
* @copyright 2015 Nicholas C. Zakas. All rights reserved. |
|||
* See LICENSE file in root directory for full license. |
|||
*/ |
|||
/* eslint no-use-before-define: 0 */ |
|||
"use strict"; |
|||
|
|||
//------------------------------------------------------------------------------
|
|||
// Requirements
|
|||
//------------------------------------------------------------------------------
|
|||
|
|||
var debug = require("debug"), |
|||
fs = require("fs"), |
|||
path = require("path"), |
|||
ConfigOps = require("./config-ops"), |
|||
validator = require("./config-validator"), |
|||
stripComments = require("strip-json-comments"), |
|||
isAbsolutePath = require("path-is-absolute"); |
|||
|
|||
//------------------------------------------------------------------------------
|
|||
// Private
|
|||
//------------------------------------------------------------------------------
|
|||
|
|||
var CONFIG_FILES = [ |
|||
".eslintrc.js", |
|||
".eslintrc.yaml", |
|||
".eslintrc.yml", |
|||
".eslintrc.json", |
|||
".eslintrc" |
|||
]; |
|||
|
|||
debug = debug("eslint:config-file"); |
|||
|
|||
/** |
|||
* Convenience wrapper for synchronously reading file contents. |
|||
* @param {string} filePath The filename to read. |
|||
* @returns {string} The file contents. |
|||
* @private |
|||
*/ |
|||
function readFile(filePath) { |
|||
return fs.readFileSync(filePath, "utf8"); |
|||
} |
|||
|
|||
/** |
|||
* Determines if a given string represents a filepath or not using the same |
|||
* conventions as require(), meaning that the first character must be nonalphanumeric |
|||
* and not the @ sign which is used for scoped packages to be considered a file path. |
|||
* @param {string} filePath The string to check. |
|||
* @returns {boolean} True if it's a filepath, false if not. |
|||
* @private |
|||
*/ |
|||
function isFilePath(filePath) { |
|||
return isAbsolutePath(filePath) || !/\w|@/.test(filePath.charAt(0)); |
|||
} |
|||
|
|||
/** |
|||
* Loads a YAML configuration from a file. |
|||
* @param {string} filePath The filename to load. |
|||
* @returns {Object} The configuration object from the file. |
|||
* @throws {Error} If the file cannot be read. |
|||
* @private |
|||
*/ |
|||
function loadYAMLConfigFile(filePath) { |
|||
debug("Loading YAML config file: " + filePath); |
|||
|
|||
// lazy load YAML to improve performance when not used
|
|||
var yaml = require("js-yaml"); |
|||
|
|||
try { |
|||
// empty YAML file can be null, so always use
|
|||
return yaml.safeLoad(readFile(filePath)) || {}; |
|||
} catch (e) { |
|||
debug("Error reading YAML file: " + filePath); |
|||
e.message = "Cannot read config file: " + filePath + "\nError: " + e.message; |
|||
throw e; |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* Loads a JSON configuration from a file. |
|||
* @param {string} filePath The filename to load. |
|||
* @returns {Object} The configuration object from the file. |
|||
* @throws {Error} If the file cannot be read. |
|||
* @private |
|||
*/ |
|||
function loadJSONConfigFile(filePath) { |
|||
debug("Loading JSON config file: " + filePath); |
|||
|
|||
try { |
|||
return JSON.parse(stripComments(readFile(filePath))); |
|||
} catch (e) { |
|||
debug("Error reading JSON file: " + filePath); |
|||
e.message = "Cannot read config file: " + filePath + "\nError: " + e.message; |
|||
throw e; |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* Loads a legacy (.eslintrc) configuration from a file. |
|||
* @param {string} filePath The filename to load. |
|||
* @returns {Object} The configuration object from the file. |
|||
* @throws {Error} If the file cannot be read. |
|||
* @private |
|||
*/ |
|||
function loadLegacyConfigFile(filePath) { |
|||
debug("Loading config file: " + filePath); |
|||
|
|||
// lazy load YAML to improve performance when not used
|
|||
var yaml = require("js-yaml"); |
|||
|
|||
try { |
|||
return yaml.safeLoad(stripComments(readFile(filePath))) || {}; |
|||
} catch (e) { |
|||
debug("Error reading YAML file: " + filePath); |
|||
e.message = "Cannot read config file: " + filePath + "\nError: " + e.message; |
|||
throw e; |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* Loads a JavaScript configuration from a file. |
|||
* @param {string} filePath The filename to load. |
|||
* @returns {Object} The configuration object from the file. |
|||
* @throws {Error} If the file cannot be read. |
|||
* @private |
|||
*/ |
|||
function loadJSConfigFile(filePath) { |
|||
debug("Loading JS config file: " + filePath); |
|||
try { |
|||
return require(filePath); |
|||
} catch (e) { |
|||
debug("Error reading JavaScript file: " + filePath); |
|||
e.message = "Cannot read config file: " + filePath + "\nError: " + e.message; |
|||
throw e; |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* Loads a configuration from a package.json file. |
|||
* @param {string} filePath The filename to load. |
|||
* @returns {Object} The configuration object from the file. |
|||
* @throws {Error} If the file cannot be read. |
|||
* @private |
|||
*/ |
|||
function loadPackageJSONConfigFile(filePath) { |
|||
debug("Loading package.json config file: " + filePath); |
|||
try { |
|||
return require(filePath).eslintConfig || null; |
|||
} catch (e) { |
|||
debug("Error reading package.json file: " + filePath); |
|||
e.message = "Cannot read config file: " + filePath + "\nError: " + e.message; |
|||
throw e; |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* Loads a JavaScript configuration from a package. |
|||
* @param {string} filePath The package name to load. |
|||
* @returns {Object} The configuration object from the package. |
|||
* @throws {Error} If the package cannot be read. |
|||
* @private |
|||
*/ |
|||
function loadPackage(filePath) { |
|||
debug("Loading config package: " + filePath); |
|||
try { |
|||
return require(filePath); |
|||
} catch (e) { |
|||
debug("Error reading package: " + filePath); |
|||
e.message = "Cannot read config package: " + filePath + "\nError: " + e.message; |
|||
throw e; |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* Loads a configuration file regardless of the source. Inspects the file path |
|||
* to determine the correctly way to load the config file. |
|||
* @param {string} filePath The path to the configuration. |
|||
* @returns {Object} The configuration information. |
|||
* @private |
|||
*/ |
|||
function loadConfigFile(filePath) { |
|||
var config; |
|||
|
|||
if (isFilePath(filePath)) { |
|||
switch (path.extname(filePath)) { |
|||
case ".js": |
|||
config = loadJSConfigFile(filePath); |
|||
break; |
|||
|
|||
case ".json": |
|||
if (path.basename(filePath) === "package.json") { |
|||
config = loadPackageJSONConfigFile(filePath); |
|||
if (config === null) { |
|||
return null; |
|||
} |
|||
} else { |
|||
config = loadJSONConfigFile(filePath); |
|||
} |
|||
break; |
|||
|
|||
case ".yaml": |
|||
case ".yml": |
|||
config = loadYAMLConfigFile(filePath); |
|||
break; |
|||
|
|||
default: |
|||
config = loadLegacyConfigFile(filePath); |
|||
} |
|||
} else { |
|||
config = loadPackage(filePath); |
|||
} |
|||
|
|||
return ConfigOps.merge(ConfigOps.createEmptyConfig(), config); |
|||
} |
|||
|
|||
/** |
|||
* Writes a configuration file in JSON format. |
|||
* @param {Object} config The configuration object to write. |
|||
* @param {string} filePath The filename to write to. |
|||
* @returns {void} |
|||
* @private |
|||
*/ |
|||
function writeJSONConfigFile(config, filePath) { |
|||
debug("Writing JSON config file: " + filePath); |
|||
|
|||
var content = JSON.stringify(config, null, 4); |
|||
fs.writeFileSync(filePath, content, "utf8"); |
|||
} |
|||
|
|||
/** |
|||
* Writes a configuration file in YAML format. |
|||
* @param {Object} config The configuration object to write. |
|||
* @param {string} filePath The filename to write to. |
|||
* @returns {void} |
|||
* @private |
|||
*/ |
|||
function writeYAMLConfigFile(config, filePath) { |
|||
debug("Writing YAML config file: " + filePath); |
|||
|
|||
// lazy load YAML to improve performance when not used
|
|||
var yaml = require("js-yaml"); |
|||
|
|||
var content = yaml.safeDump(config); |
|||
fs.writeFileSync(filePath, content, "utf8"); |
|||
} |
|||
|
|||
/** |
|||
* Writes a configuration file in JavaScript format. |
|||
* @param {Object} config The configuration object to write. |
|||
* @param {string} filePath The filename to write to. |
|||
* @returns {void} |
|||
* @private |
|||
*/ |
|||
function writeJSConfigFile(config, filePath) { |
|||
debug("Writing JS config file: " + filePath); |
|||
|
|||
var content = "module.exports = " + JSON.stringify(config, null, 4) + ";"; |
|||
fs.writeFileSync(filePath, content, "utf8"); |
|||
} |
|||
|
|||
/** |
|||
* Writes a configuration file. |
|||
* @param {Object} config The configuration object to write. |
|||
* @param {string} filePath The filename to write to. |
|||
* @returns {void} |
|||
* @throws {Error} When an unknown file type is specified. |
|||
* @private |
|||
*/ |
|||
function write(config, filePath) { |
|||
switch (path.extname(filePath)) { |
|||
case ".js": |
|||
writeJSConfigFile(config, filePath); |
|||
break; |
|||
|
|||
case ".json": |
|||
writeJSONConfigFile(config, filePath); |
|||
break; |
|||
|
|||
case ".yaml": |
|||
case ".yml": |
|||
writeYAMLConfigFile(config, filePath); |
|||
break; |
|||
|
|||
default: |
|||
throw new Error("Can't write to unknown file type."); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* Applies values from the "extends" field in a configuration file. |
|||
* @param {Object} config The configuration information. |
|||
* @param {string} filePath The file path from which the configuration information |
|||
* was loaded. |
|||
* @returns {Object} A new configuration object with all of the "extends" fields |
|||
* loaded and merged. |
|||
* @private |
|||
*/ |
|||
function applyExtends(config, filePath) { |
|||
var configExtends = config.extends; |
|||
|
|||
// normalize into an array for easier handling
|
|||
if (!Array.isArray(config.extends)) { |
|||
configExtends = [config.extends]; |
|||
} |
|||
|
|||
// Make the last element in an array take the highest precedence
|
|||
config = configExtends.reduceRight(function(previousValue, 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
|
|||
// file as the reference point. Otherwise, use as-is.
|
|||
parentPath = (!isAbsolutePath(parentPath) ? |
|||
path.join(path.dirname(filePath), parentPath) : |
|||
parentPath |
|||
); |
|||
} |
|||
|
|||
try { |
|||
debug("Loading " + parentPath); |
|||
return ConfigOps.merge(load(parentPath), previousValue); |
|||
} catch (e) { |
|||
// If the file referenced by `extends` failed to load, add the path to the
|
|||
// configuration file that referenced it to the error message so the user is
|
|||
// able to see where it was referenced from, then re-throw
|
|||
e.message += "\nReferenced from: " + filePath; |
|||
throw e; |
|||
} |
|||
|
|||
}, config); |
|||
|
|||
return config; |
|||
} |
|||
|
|||
/** |
|||
* Resolves a configuration file path into the fully-formed path, whether filename |
|||
* or package name. |
|||
* @param {string} filePath The filepath to resolve. |
|||
* @returns {string} A path that can be used directly to load the configuration. |
|||
* @private |
|||
*/ |
|||
function resolve(filePath) { |
|||
|
|||
if (isFilePath(filePath)) { |
|||
return path.resolve(filePath); |
|||
} 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; |
|||
} |
|||
|
|||
return filePath; |
|||
} |
|||
|
|||
} |
|||
|
|||
/** |
|||
* Loads a configuration file from the given file path. |
|||
* @param {string} filePath The filename or package name to load the configuration |
|||
* information from. |
|||
* @returns {Object} The configuration information. |
|||
* @private |
|||
*/ |
|||
function load(filePath) { |
|||
|
|||
var resolvedPath = resolve(filePath), |
|||
config = loadConfigFile(resolvedPath); |
|||
|
|||
if (config) { |
|||
|
|||
// validate the configuration before continuing
|
|||
validator.validate(config, filePath); |
|||
|
|||
// If an `extends` property is defined, it represents a configuration file to use as
|
|||
// a "parent". Load the referenced file and merge the configuration recursively.
|
|||
if (config.extends) { |
|||
config = applyExtends(config, filePath); |
|||
} |
|||
|
|||
if (config.env) { |
|||
// Merge in environment-specific globals and ecmaFeatures.
|
|||
config = ConfigOps.applyEnvironments(config); |
|||
} |
|||
|
|||
} |
|||
|
|||
return config; |
|||
} |
|||
|
|||
//------------------------------------------------------------------------------
|
|||
// Public Interface
|
|||
//------------------------------------------------------------------------------
|
|||
|
|||
module.exports = { |
|||
|
|||
load: load, |
|||
resolve: resolve, |
|||
write: write, |
|||
applyExtends: applyExtends, |
|||
CONFIG_FILES: CONFIG_FILES, |
|||
|
|||
/** |
|||
* Retrieves the configuration filename for a given directory. It loops over all |
|||
* of the valid configuration filenames in order to find the first one that exists. |
|||
* @param {string} directory The directory to check for a config file. |
|||
* @returns {?string} The filename of the configuration file for the directory |
|||
* or null if there is no configuration file in the directory. |
|||
*/ |
|||
getFilenameForDirectory: function(directory) { |
|||
|
|||
var filename; |
|||
|
|||
for (var i = 0, len = CONFIG_FILES.length; i < len; i++) { |
|||
filename = path.join(directory, CONFIG_FILES[i]); |
|||
if (fs.existsSync(filename)) { |
|||
return filename; |
|||
} |
|||
} |
|||
|
|||
return null; |
|||
} |
|||
}; |
@ -0,0 +1,241 @@ |
|||
/** |
|||
* @fileoverview Config initialization wizard. |
|||
* @author Ilya Volodin |
|||
* @copyright 2015 Ilya Volodin. All rights reserved. |
|||
*/ |
|||
|
|||
"use strict"; |
|||
|
|||
//------------------------------------------------------------------------------
|
|||
// Requirements
|
|||
//------------------------------------------------------------------------------
|
|||
|
|||
var exec = require("child_process").exec, |
|||
inquirer = require("inquirer"), |
|||
ConfigFile = require("./config-file"); |
|||
|
|||
//------------------------------------------------------------------------------
|
|||
// Private
|
|||
//------------------------------------------------------------------------------
|
|||
|
|||
/* istanbul ignore next: hard to test fs function */ |
|||
/** |
|||
* Create .eslintrc file in the current working directory |
|||
* @param {object} config object that contains user's answers |
|||
* @param {string} format The file format to write to. |
|||
* @param {function} callback function to call once the file is written. |
|||
* @returns {void} |
|||
*/ |
|||
function writeFile(config, format, callback) { |
|||
|
|||
// default is .js
|
|||
var extname = ".js"; |
|||
if (format === "YAML") { |
|||
extname = ".yml"; |
|||
} else if (format === "JSON") { |
|||
extname = ".json"; |
|||
} |
|||
|
|||
|
|||
try { |
|||
ConfigFile.write(config, "./.eslintrc" + extname); |
|||
console.log("Successfully created .eslintrc" + extname + " file in " + process.cwd()); |
|||
} catch (e) { |
|||
callback(e); |
|||
return; |
|||
} |
|||
|
|||
// install any external configs as well as any included plugins
|
|||
if (config.extends && config.extends.indexOf("eslint") === -1) { |
|||
console.log("Installing additional dependencies"); |
|||
exec("npm i eslint-config-" + config.extends + " --save-dev", function(err) { |
|||
|
|||
if (err) { |
|||
return callback(err); |
|||
} |
|||
|
|||
// TODO: consider supporting more than 1 plugin though it's required yet.
|
|||
exec("npm i eslint-plugin-" + config.plugins[0] + " --save-dev", callback); |
|||
}); |
|||
return; |
|||
} |
|||
|
|||
// install the react plugin if it was explictly chosen
|
|||
if (config.plugins && config.plugins.indexOf("react") >= 0) { |
|||
console.log("Installing React plugin"); |
|||
exec("npm i eslint-plugin-react --save-dev", callback); |
|||
return; |
|||
} |
|||
callback(); |
|||
} |
|||
|
|||
/** |
|||
* process user's answers and create config object |
|||
* @param {object} answers answers received from inquirer |
|||
* @returns {object} config object |
|||
*/ |
|||
function processAnswers(answers) { |
|||
var config = {rules: {}, env: {}, extends: "eslint:recommended"}; |
|||
config.rules.indent = [2, answers.indent]; |
|||
config.rules.quotes = [2, answers.quotes]; |
|||
config.rules["linebreak-style"] = [2, answers.linebreak]; |
|||
config.rules.semi = [2, answers.semi ? "always" : "never"]; |
|||
if (answers.es6) { |
|||
config.env.es6 = true; |
|||
} |
|||
answers.env.forEach(function(env) { |
|||
config.env[env] = true; |
|||
}); |
|||
if (answers.jsx) { |
|||
config.ecmaFeatures = {jsx: true}; |
|||
if (answers.react) { |
|||
config.plugins = ["react"]; |
|||
config.ecmaFeatures.experimentalObjectRestSpread = true; |
|||
} |
|||
} |
|||
return config; |
|||
} |
|||
|
|||
/** |
|||
* process user's style guide of choice and return an appropriate config object. |
|||
* @param {string} guide name of the chosen style guide |
|||
* @returns {object} config object |
|||
*/ |
|||
function getConfigForStyleGuide(guide) { |
|||
var guides = { |
|||
google: {extends: "google"}, |
|||
airbnb: {extends: "airbnb", plugins: ["react"]}, |
|||
standard: {extends: "standard", plugins: ["standard"]} |
|||
}; |
|||
if (!guides[guide]) { |
|||
throw new Error("You referenced an unsupported guide."); |
|||
} |
|||
return guides[guide]; |
|||
} |
|||
|
|||
/* istanbul ignore next: no need to test inquirer*/ |
|||
/** |
|||
* Ask use a few questions on command prompt |
|||
* @param {function} callback callback function when file has been written |
|||
* @returns {void} |
|||
*/ |
|||
function promptUser(callback) { |
|||
inquirer.prompt([ |
|||
{ |
|||
type: "list", |
|||
name: "source", |
|||
message: "How would you like to configure ESLint?", |
|||
default: "prompt", |
|||
choices: [{name: "Answer questions about your style", value: "prompt"}, {name: "Use a popular style guide", value: "guide"}] |
|||
}, |
|||
{ |
|||
type: "list", |
|||
name: "styleguide", |
|||
message: "Which style guide do you want to follow?", |
|||
choices: [{name: "Google", value: "google"}, {name: "AirBnB", value: "airbnb"}, {name: "Standard", value: "standard"}], |
|||
when: function(answers) { |
|||
return answers.source === "guide"; |
|||
} |
|||
}, |
|||
{ |
|||
type: "list", |
|||
name: "format", |
|||
message: "What format do you want your config file to be in?", |
|||
default: "JavaScript", |
|||
choices: ["JavaScript", "YAML", "JSON"], |
|||
when: function(answers) { |
|||
return answers.source === "guide"; |
|||
} |
|||
} |
|||
], function(earlyAnswers) { |
|||
|
|||
// early exit if you are using a style guide
|
|||
if (earlyAnswers.source === "guide") { |
|||
writeFile(getConfigForStyleGuide(earlyAnswers.styleguide), earlyAnswers.format, callback); |
|||
return; |
|||
} |
|||
|
|||
// continue with the style questions otherwise...
|
|||
inquirer.prompt([ |
|||
{ |
|||
type: "list", |
|||
name: "indent", |
|||
message: "What style of indentation do you use?", |
|||
default: "tabs", |
|||
choices: [{name: "Tabs", value: "tab"}, {name: "Spaces", value: 4}] |
|||
}, |
|||
{ |
|||
type: "list", |
|||
name: "quotes", |
|||
message: "What quotes do you use for strings?", |
|||
default: "double", |
|||
choices: [{name: "Double", value: "double"}, {name: "Single", value: "single"}] |
|||
}, |
|||
{ |
|||
type: "list", |
|||
name: "linebreak", |
|||
message: "What line endings do you use?", |
|||
default: "unix", |
|||
choices: [{name: "Unix", value: "unix"}, {name: "Windows", value: "windows"}] |
|||
}, |
|||
{ |
|||
type: "confirm", |
|||
name: "semi", |
|||
message: "Do you require semicolons?", |
|||
default: true |
|||
}, |
|||
{ |
|||
type: "confirm", |
|||
name: "es6", |
|||
message: "Are you using ECMAScript 6 features?", |
|||
default: false |
|||
}, |
|||
{ |
|||
type: "checkbox", |
|||
name: "env", |
|||
message: "Where will your code run?", |
|||
default: ["browser"], |
|||
choices: [{name: "Node", value: "node"}, {name: "Browser", value: "browser"}] |
|||
}, |
|||
{ |
|||
type: "confirm", |
|||
name: "jsx", |
|||
message: "Do you use JSX?", |
|||
default: false |
|||
}, |
|||
{ |
|||
type: "confirm", |
|||
name: "react", |
|||
message: "Do you use React", |
|||
default: false, |
|||
when: function(answers) { |
|||
return answers.jsx; |
|||
} |
|||
}, |
|||
{ |
|||
type: "list", |
|||
name: "format", |
|||
message: "What format do you want your config file to be in?", |
|||
default: "JavaScript", |
|||
choices: ["JavaScript", "YAML", "JSON"] |
|||
} |
|||
], function(answers) { |
|||
var config = processAnswers(answers); |
|||
writeFile(config, answers.format, callback); |
|||
}); |
|||
}); |
|||
} |
|||
|
|||
//------------------------------------------------------------------------------
|
|||
// Public Interface
|
|||
//------------------------------------------------------------------------------
|
|||
|
|||
var init = { |
|||
getConfigForStyleGuide: getConfigForStyleGuide, |
|||
processAnswers: processAnswers, |
|||
initializeConfig: /* istanbul ignore next */ function(callback) { |
|||
promptUser(callback); |
|||
} |
|||
}; |
|||
|
|||
module.exports = init; |
@ -0,0 +1,186 @@ |
|||
/** |
|||
* @fileoverview Config file operations. This file must be usable in the browser, |
|||
* so no Node-specific code can be here. |
|||
* @author Nicholas C. Zakas |
|||
* @copyright 2015 Nicholas C. Zakas. All rights reserved. |
|||
* See LICENSE file in root directory for full license. |
|||
*/ |
|||
"use strict"; |
|||
|
|||
//------------------------------------------------------------------------------
|
|||
// Requirements
|
|||
//------------------------------------------------------------------------------
|
|||
|
|||
var debug = require("debug"), |
|||
environments = require("../../conf/environments"), |
|||
assign = require("object-assign"); |
|||
|
|||
//------------------------------------------------------------------------------
|
|||
// Private
|
|||
//------------------------------------------------------------------------------
|
|||
|
|||
debug = debug("eslint:config-ops"); |
|||
|
|||
//------------------------------------------------------------------------------
|
|||
// Public Interface
|
|||
//------------------------------------------------------------------------------
|
|||
|
|||
module.exports = { |
|||
|
|||
/** |
|||
* Creates an empty configuration object suitable for merging as a base. |
|||
* @returns {Object} A configuration object. |
|||
*/ |
|||
createEmptyConfig: function() { |
|||
return { |
|||
globals: {}, |
|||
env: {}, |
|||
rules: {}, |
|||
ecmaFeatures: {} |
|||
}; |
|||
}, |
|||
|
|||
/** |
|||
* Creates an environment config based on the specified environments. |
|||
* @param {Object<string,boolean>} env The environment settings. |
|||
* @returns {Object} A configuration object with the appropriate rules and globals |
|||
* set. |
|||
*/ |
|||
createEnvironmentConfig: function(env) { |
|||
|
|||
var envConfig = this.createEmptyConfig(); |
|||
|
|||
if (env) { |
|||
|
|||
envConfig.env = env; |
|||
|
|||
Object.keys(env).filter(function(name) { |
|||
return env[name]; |
|||
}).forEach(function(name) { |
|||
var environment = environments[name]; |
|||
|
|||
if (environment) { |
|||
debug("Creating config for environment " + name); |
|||
if (environment.globals) { |
|||
assign(envConfig.globals, environment.globals); |
|||
} |
|||
|
|||
if (environment.ecmaFeatures) { |
|||
assign(envConfig.ecmaFeatures, environment.ecmaFeatures); |
|||
} |
|||
} |
|||
}); |
|||
} |
|||
|
|||
return envConfig; |
|||
}, |
|||
|
|||
/** |
|||
* Given a config with environment settings, applies the globals and |
|||
* ecmaFeatures to the configuration and returns the result. |
|||
* @param {Object} config The configuration information. |
|||
* @returns {Object} The updated configuration information. |
|||
*/ |
|||
applyEnvironments: function(config) { |
|||
if (config.env && typeof config.env === "object") { |
|||
debug("Apply environment settings to config"); |
|||
return this.merge(this.createEnvironmentConfig(config.env), config); |
|||
} |
|||
|
|||
return config; |
|||
}, |
|||
|
|||
/** |
|||
* Merges two config objects. This will not only add missing keys, but will also modify values to match. |
|||
* @param {Object} target config object |
|||
* @param {Object} src config object. Overrides in this config object will take priority over base. |
|||
* @param {boolean} [combine] Whether to combine arrays or not |
|||
* @param {boolean} [isRule] Whether its a rule |
|||
* @returns {Object} merged config object. |
|||
*/ |
|||
merge: function deepmerge(target, src, combine, isRule) { |
|||
/* |
|||
The MIT License (MIT) |
|||
|
|||
Copyright (c) 2012 Nicholas Fisher |
|||
|
|||
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. |
|||
*/ |
|||
// This code is taken from deepmerge repo (https://github.com/KyleAMathews/deepmerge) and modified to meet our needs.
|
|||
var array = Array.isArray(src) || Array.isArray(target); |
|||
var dst = array && [] || {}; |
|||
|
|||
combine = !!combine; |
|||
isRule = !!isRule; |
|||
if (array) { |
|||
target = target || []; |
|||
if (isRule && src.length > 1) { |
|||
dst = dst.concat(src); |
|||
} else { |
|||
dst = dst.concat(target); |
|||
} |
|||
if (typeof src !== "object" && !Array.isArray(src)) { |
|||
src = [src]; |
|||
} |
|||
Object.keys(src).forEach(function(e, i) { |
|||
e = src[i]; |
|||
if (typeof dst[i] === "undefined") { |
|||
dst[i] = e; |
|||
} else if (typeof e === "object") { |
|||
if (isRule) { |
|||
dst[i] = e; |
|||
} else { |
|||
dst[i] = deepmerge(target[i], e, combine, isRule); |
|||
} |
|||
} else { |
|||
if (!combine) { |
|||
dst[i] = e; |
|||
} else { |
|||
if (dst.indexOf(e) === -1) { |
|||
dst.push(e); |
|||
} |
|||
} |
|||
} |
|||
}); |
|||
} else { |
|||
if (target && typeof target === "object") { |
|||
Object.keys(target).forEach(function(key) { |
|||
dst[key] = target[key]; |
|||
}); |
|||
} |
|||
Object.keys(src).forEach(function(key) { |
|||
if (Array.isArray(src[key]) || Array.isArray(target[key])) { |
|||
dst[key] = deepmerge(target[key], src[key], key === "plugins", isRule); |
|||
} else if (typeof src[key] !== "object" || !src[key]) { |
|||
dst[key] = src[key]; |
|||
} else { |
|||
if (!target[key]) { |
|||
dst[key] = src[key]; |
|||
} else { |
|||
dst[key] = deepmerge(target[key], src[key], combine, key === "rules"); |
|||
} |
|||
} |
|||
}); |
|||
} |
|||
|
|||
return dst; |
|||
} |
|||
|
|||
|
|||
}; |
@ -0,0 +1,163 @@ |
|||
/** |
|||
* @fileoverview Validates configs. |
|||
* @author Brandon Mills |
|||
* @copyright 2015 Brandon Mills |
|||
*/ |
|||
|
|||
"use strict"; |
|||
|
|||
//------------------------------------------------------------------------------
|
|||
// Requirements
|
|||
//------------------------------------------------------------------------------
|
|||
|
|||
var rules = require("../rules"), |
|||
environments = require("../../conf/environments"), |
|||
schemaValidator = require("is-my-json-valid"); |
|||
|
|||
var validators = { |
|||
rules: Object.create(null) |
|||
}; |
|||
|
|||
//------------------------------------------------------------------------------
|
|||
// Private
|
|||
//------------------------------------------------------------------------------
|
|||
|
|||
/** |
|||
* Gets a complete options schema for a rule. |
|||
* @param {string} id The rule's unique name. |
|||
* @returns {object} JSON Schema for the rule's options. |
|||
*/ |
|||
function getRuleOptionsSchema(id) { |
|||
var rule = rules.get(id), |
|||
schema = rule && rule.schema; |
|||
|
|||
if (!schema) { |
|||
return { |
|||
"type": "array", |
|||
"items": [ |
|||
{ |
|||
"enum": [0, 1, 2] |
|||
} |
|||
], |
|||
"minItems": 1 |
|||
}; |
|||
} |
|||
|
|||
// Given a tuple of schemas, insert warning level at the beginning
|
|||
if (Array.isArray(schema)) { |
|||
return { |
|||
"type": "array", |
|||
"items": [ |
|||
{ |
|||
"enum": [0, 1, 2] |
|||
} |
|||
].concat(schema), |
|||
"minItems": 1, |
|||
"maxItems": schema.length + 1 |
|||
}; |
|||
} |
|||
|
|||
// Given a full schema, leave it alone
|
|||
return schema; |
|||
} |
|||
|
|||
/** |
|||
* Validates a rule's options against its schema. |
|||
* @param {string} id The rule's unique name. |
|||
* @param {array|number} options The given options for the rule. |
|||
* @param {string} source The name of the configuration source. |
|||
* @returns {void} |
|||
*/ |
|||
function validateRuleOptions(id, options, source) { |
|||
var validateRule = validators.rules[id], |
|||
message; |
|||
|
|||
if (!validateRule) { |
|||
validateRule = schemaValidator(getRuleOptionsSchema(id), { verbose: true }); |
|||
validators.rules[id] = validateRule; |
|||
} |
|||
|
|||
if (typeof options === "number") { |
|||
options = [options]; |
|||
} |
|||
|
|||
validateRule(options); |
|||
|
|||
if (validateRule.errors) { |
|||
message = [ |
|||
source, ":\n", |
|||
"\tConfiguration for rule \"", id, "\" is invalid:\n" |
|||
]; |
|||
validateRule.errors.forEach(function(error) { |
|||
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" |
|||
); |
|||
} |
|||
}); |
|||
|
|||
throw new Error(message.join("")); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* Validates an environment object |
|||
* @param {object} environment The environment config object to validate. |
|||
* @param {string} source The location to report with any errors. |
|||
* @returns {void} |
|||
*/ |
|||
function validateEnvironment(environment, source) { |
|||
|
|||
// not having an environment is ok
|
|||
if (!environment) { |
|||
return; |
|||
} |
|||
|
|||
if (Array.isArray(environment)) { |
|||
throw new Error("Environment must not be an array"); |
|||
} |
|||
|
|||
if (typeof environment === "object") { |
|||
Object.keys(environment).forEach(function(env) { |
|||
if (!environments[env]) { |
|||
var message = [ |
|||
source, ":\n", |
|||
"\tEnvironment key \"", env, "\" is unknown\n" |
|||
]; |
|||
throw new Error(message.join("")); |
|||
} |
|||
}); |
|||
} else { |
|||
throw new Error("Environment must be an object"); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* Validates an entire config object. |
|||
* @param {object} config The config object to validate. |
|||
* @param {string} source The location to report with any errors. |
|||
* @returns {void} |
|||
*/ |
|||
function validate(config, source) { |
|||
|
|||
if (typeof config.rules === "object") { |
|||
Object.keys(config.rules).forEach(function(id) { |
|||
validateRuleOptions(id, config.rules[id], source); |
|||
}); |
|||
} |
|||
|
|||
validateEnvironment(config.env, source); |
|||
} |
|||
|
|||
//------------------------------------------------------------------------------
|
|||
// Public Interface
|
|||
//------------------------------------------------------------------------------
|
|||
|
|||
module.exports = { |
|||
getRuleOptionsSchema: getRuleOptionsSchema, |
|||
validate: validate, |
|||
validateRuleOptions: validateRuleOptions |
|||
}; |
@ -0,0 +1,130 @@ |
|||
<!DOCTYPE html> |
|||
<head> |
|||
<title>ESLint Report</title> |
|||
<style> |
|||
body { |
|||
font-family:Arial, "Helvetica Neue", Helvetica, sans-serif; |
|||
font-size:16px; |
|||
font-weight:normal; |
|||
margin:0; |
|||
padding:0; |
|||
color:#333 |
|||
} |
|||
#overview { |
|||
padding:20px 30px |
|||
} |
|||
td, th { |
|||
padding:5px 10px |
|||
} |
|||
h1 { |
|||
margin:0 |
|||
} |
|||
table { |
|||
margin:30px; |
|||
width:calc(100% - 60px); |
|||
max-width:1000px; |
|||
border-radius:5px; |
|||
border:1px solid #ddd; |
|||
border-spacing:0px; |
|||
} |
|||
th { |
|||
font-weight:400; |
|||
font-size:normal; |
|||
text-align:left; |
|||
cursor:pointer |
|||
} |
|||
td.clr-1, td.clr-2, th span { |
|||
font-weight:700 |
|||
} |
|||
th span { |
|||
float:right; |
|||
margin-left:20px |
|||
} |
|||
th span:after { |
|||
content:""; |
|||
clear:both; |
|||
display:block |
|||
} |
|||
tr:last-child td { |
|||
border-bottom:none |
|||
} |
|||
tr td:first-child, tr td:last-child { |
|||
color:#9da0a4 |
|||
} |
|||
#overview.bg-0, tr.bg-0 th { |
|||
color:#468847; |
|||
background:#dff0d8; |
|||
border-bottom:1px solid #d6e9c6 |
|||
} |
|||
#overview.bg-1, tr.bg-1 th { |
|||
color:#f0ad4e; |
|||
background:#fcf8e3; |
|||
border-bottom:1px solid #fbeed5 |
|||
} |
|||
#overview.bg-2, tr.bg-2 th { |
|||
color:#b94a48; |
|||
background:#f2dede; |
|||
border-bottom:1px solid #eed3d7 |
|||
} |
|||
td { |
|||
border-bottom:1px solid #ddd |
|||
} |
|||
td.clr-1 { |
|||
color:#f0ad4e |
|||
} |
|||
td.clr-2 { |
|||
color:#b94a48 |
|||
} |
|||
td a { |
|||
color:#3a33d1; |
|||
text-decoration:none |
|||
} |
|||
td a:hover { |
|||
color:#272296; |
|||
text-decoration:underline |
|||
} |
|||
</style> |
|||
</head> |
|||
<body> |
|||
<div id="overview" class="bg-{{getColor totalErrors totalWarnings}}"> |
|||
<h1>ESLint Report</h1> |
|||
<div> |
|||
<span>{{renderText totalErrors totalWarnings}}</span> - Generated on {{date}} |
|||
</div> |
|||
</div> |
|||
<table> |
|||
<tbody> |
|||
{{#each results}} |
|||
<tr class="bg-{{getColor this.errorCount this.warningCount}}" data-group="f-{{@index}}"> |
|||
<th colspan="4"> |
|||
[+] {{this.filePath}} |
|||
<span>{{renderText this.errorCount this.warningCount}}</span> |
|||
</th> |
|||
</tr> |
|||
{{#each this.messages}} |
|||
<tr class="f-{{@../index}}" style="display:none"> |
|||
<td>{{#if this.line}}{{this.line}}{{else}}0{{/if}}:{{#if this.column}}{{this.column}}{{else}}0{{/if}}</td> |
|||
{{getSeverity this.severity}} |
|||
<td>{{this.message}}</td> |
|||
<td> |
|||
<a href="http://eslint.org/docs/rules/{{this.ruleId}}" target="_blank">{{this.ruleId}}</a> |
|||
</td> |
|||
</tr> |
|||
{{/each}} |
|||
{{/each}} |
|||
</tbody> |
|||
</table> |
|||
<script type="text/javascript"> |
|||
var groups = document.querySelectorAll("tr[data-group]"); |
|||
for (i = 0; i < groups.length; i++) { |
|||
groups[i].addEventListener("click", function() { |
|||
var inGroup = document.getElementsByClassName(this.getAttribute("data-group")); |
|||
this.innerHTML = (this.innerHTML.indexOf("+") > -1) ? this.innerHTML.replace("+", "-") : this.innerHTML.replace("-", "+"); |
|||
for (var j = 0; j < inGroup.length; j++) { |
|||
inGroup[j].style.display = (inGroup[j].style.display !== "none") ? "none" : "table-row"; |
|||
} |
|||
}); |
|||
} |
|||
</script> |
|||
</body> |
|||
</html> |
@ -0,0 +1,88 @@ |
|||
/** |
|||
* @fileoverview HTML reporter |
|||
* @author Julian Laval |
|||
* @copyright 2015 Julian Laval. All rights reserved. |
|||
*/ |
|||
"use strict"; |
|||
|
|||
var handlebars = require("handlebars").create(); |
|||
var fs = require("fs"); |
|||
var path = require("path"); |
|||
|
|||
//------------------------------------------------------------------------------
|
|||
// Helpers
|
|||
//------------------------------------------------------------------------------
|
|||
|
|||
/** |
|||
* Given a word and a count, append an s if count is not one. |
|||
* @param {string} word A word in its singular form. |
|||
* @param {int} count A number controlling whether word should be pluralized. |
|||
* @returns {string} The original word with an s on the end if count is not one. |
|||
*/ |
|||
function pluralize(word, count) { |
|||
return (count === 1 ? word : word + "s"); |
|||
} |
|||
|
|||
/** |
|||
* Renders text along the template of x problems (x errors, x warnings) |
|||
* @param {string} totalErrors Total errors |
|||
* @param {string} totalWarnings Total warnings |
|||
* @returns {string} The formatted string, pluralized where necessary |
|||
*/ |
|||
handlebars.registerHelper("renderText", function(totalErrors, totalWarnings) { |
|||
var totalProblems = totalErrors + totalWarnings; |
|||
var renderedText = totalProblems + " " + pluralize("problem", totalProblems); |
|||
if (totalProblems !== 0) { |
|||
renderedText += " (" + totalErrors + " " + pluralize("error", totalErrors) + ", " + totalWarnings + " " + pluralize("warning", totalWarnings) + ")"; |
|||
} |
|||
return renderedText; |
|||
}); |
|||
|
|||
/** |
|||
* Get the color based on whether there are errors/warnings... |
|||
* @param {string} totalErrors Total errors |
|||
* @param {string} totalWarnings Total warnings |
|||
* @returns {int} The color code (0 = green, 1 = yellow, 2 = red) |
|||
*/ |
|||
handlebars.registerHelper("getColor", function(totalErrors, totalWarnings) { |
|||
if (totalErrors !== 0) { |
|||
return 2; |
|||
} else if (totalWarnings !== 0) { |
|||
return 1; |
|||
} |
|||
return 0; |
|||
}); |
|||
|
|||
/** |
|||
* Get the HTML row content based on the severity of the message |
|||
* @param {int} severity Severity of the message |
|||
* @returns {string} The generated HTML row |
|||
*/ |
|||
handlebars.registerHelper("getSeverity", function(severity) { |
|||
// Return warning else error
|
|||
return new handlebars.SafeString((severity === 1) ? "<td class=\"clr-1\">Warning</td>" : "<td class=\"clr-2\">Error</td>"); |
|||
}); |
|||
|
|||
//------------------------------------------------------------------------------
|
|||
// Public Interface
|
|||
//------------------------------------------------------------------------------
|
|||
|
|||
module.exports = function(results) { |
|||
|
|||
var template = fs.readFileSync(path.join(__dirname, "html-template.html"), "utf-8"); |
|||
|
|||
var data = { |
|||
date: new Date(), |
|||
totalErrors: 0, |
|||
totalWarnings: 0, |
|||
results: results |
|||
}; |
|||
|
|||
// Iterate over results to get totals
|
|||
results.forEach(function(result) { |
|||
data.totalErrors += result.errorCount; |
|||
data.totalWarnings += result.warningCount; |
|||
}); |
|||
|
|||
return handlebars.compile(template)(data); |
|||
}; |
@ -0,0 +1,14 @@ |
|||
/** |
|||
* @fileoverview JSON reporter |
|||
* @author Burak Yigit Kaya aka BYK |
|||
* @copyright 2015 Burak Yigit Kaya. All rights reserved. |
|||
*/ |
|||
"use strict"; |
|||
|
|||
//------------------------------------------------------------------------------
|
|||
// Public Interface
|
|||
//------------------------------------------------------------------------------
|
|||
|
|||
module.exports = function(results) { |
|||
return JSON.stringify(results); |
|||
}; |
@ -0,0 +1,59 @@ |
|||
/** |
|||
* @fileoverview unix-style formatter. |
|||
* @author oshi-shinobu |
|||
* @copyright 2015 oshi-shinobu. All rights reserved. |
|||
*/ |
|||
"use strict"; |
|||
|
|||
//------------------------------------------------------------------------------
|
|||
// Helper Functions
|
|||
//------------------------------------------------------------------------------
|
|||
|
|||
/** |
|||
* Returns a canonical error level string based upon the error message passed in. |
|||
* @param {object} message Individual error message provided by eslint |
|||
* @returns {String} Error level string |
|||
*/ |
|||
function getMessageType(message) { |
|||
if (message.fatal || message.severity === 2) { |
|||
return "Error"; |
|||
} else { |
|||
return "Warning"; |
|||
} |
|||
} |
|||
|
|||
|
|||
//------------------------------------------------------------------------------
|
|||
// Public Interface
|
|||
//------------------------------------------------------------------------------
|
|||
|
|||
module.exports = function(results) { |
|||
|
|||
var output = "", |
|||
total = 0; |
|||
|
|||
results.forEach(function(result) { |
|||
|
|||
var messages = result.messages; |
|||
total += messages.length; |
|||
|
|||
messages.forEach(function(message) { |
|||
|
|||
output += result.filePath + ":"; |
|||
output += (message.line || 0) + ":"; |
|||
output += (message.column || 0) + ":"; |
|||
output += " " + message.message + " "; |
|||
output += "[" + getMessageType(message) + |
|||
(message.ruleId ? "/" + message.ruleId : "") + "]"; |
|||
output += "\n"; |
|||
|
|||
}); |
|||
|
|||
}); |
|||
|
|||
if (total > 0) { |
|||
output += "\n" + total + " problem" + (total !== 1 ? "s" : ""); |
|||
} |
|||
|
|||
return output; |
|||
}; |
@ -0,0 +1,25 @@ |
|||
/** |
|||
* @fileoverview Handle logging for Eslint |
|||
* @author Gyandeep Singh |
|||
* @copyright 2015 Gyandeep Singh. All rights reserved. |
|||
*/ |
|||
"use strict"; |
|||
|
|||
/* istanbul ignore next */ |
|||
module.exports = { |
|||
/** |
|||
* Cover for console.log |
|||
* @returns {void} |
|||
*/ |
|||
info: function() { |
|||
console.log.apply(console, Array.prototype.slice.call(arguments)); |
|||
}, |
|||
|
|||
/** |
|||
* Cover for console.error |
|||
* @returns {void} |
|||
*/ |
|||
error: function() { |
|||
console.error.apply(console, Array.prototype.slice.call(arguments)); |
|||
} |
|||
}; |
@ -0,0 +1,71 @@ |
|||
/** |
|||
* @fileoverview Rule to require braces in arrow function body. |
|||
* @author Alberto Rodríguez |
|||
* @copyright 2015 Alberto Rodríguez. All rights reserved. |
|||
* See LICENSE file in root directory for full license. |
|||
*/ |
|||
"use strict"; |
|||
|
|||
//------------------------------------------------------------------------------
|
|||
// Rule Definition
|
|||
//------------------------------------------------------------------------------
|
|||
|
|||
module.exports = function(context) { |
|||
var always = context.options[0] === "always"; |
|||
var asNeeded = !context.options[0] || context.options[0] === "as-needed"; |
|||
|
|||
/** |
|||
* Determines whether a arrow function body needs braces |
|||
* @param {ASTNode} node The arrow function node. |
|||
* @returns {void} |
|||
*/ |
|||
function validate(node) { |
|||
var arrowBody = node.body; |
|||
if (arrowBody.type === "BlockStatement") { |
|||
var blockBody = arrowBody.body; |
|||
|
|||
if (blockBody.length > 1) { |
|||
return; |
|||
} |
|||
|
|||
if (blockBody.length === 0) { |
|||
var hasComments = context.getComments(arrowBody).trailing.length > 0; |
|||
if (hasComments) { |
|||
return; |
|||
} |
|||
|
|||
context.report({ |
|||
node: node, |
|||
loc: arrowBody.loc.start, |
|||
message: "Unexpected empty block in arrow body." |
|||
}); |
|||
} else { |
|||
if (asNeeded && blockBody[0].type === "ReturnStatement") { |
|||
context.report({ |
|||
node: node, |
|||
loc: arrowBody.loc.start, |
|||
message: "Unexpected block statement surrounding arrow body." |
|||
}); |
|||
} |
|||
} |
|||
} else { |
|||
if (always) { |
|||
context.report({ |
|||
node: node, |
|||
loc: arrowBody.loc.start, |
|||
message: "Expected block statement surrounding arrow body." |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
|
|||
return { |
|||
"ArrowFunctionExpression": validate |
|||
}; |
|||
}; |
|||
|
|||
module.exports.schema = [ |
|||
{ |
|||
"enum": ["always", "as-needed"] |
|||
} |
|||
]; |
@ -0,0 +1,52 @@ |
|||
/** |
|||
* @fileoverview Rule to require parens in arrow function arguments. |
|||
* @author Jxck |
|||
* @copyright 2015 Jxck. All rights reserved. |
|||
*/ |
|||
"use strict"; |
|||
|
|||
//------------------------------------------------------------------------------
|
|||
// Rule Definition
|
|||
//------------------------------------------------------------------------------
|
|||
|
|||
module.exports = function(context) { |
|||
var message = "Expected parentheses around arrow function argument."; |
|||
var asNeededMessage = "Unexpected parentheses around single function argument"; |
|||
var asNeeded = context.options[0] === "as-needed"; |
|||
|
|||
/** |
|||
* Determines whether a arrow function argument end with `)` |
|||
* @param {ASTNode} node The arrow function node. |
|||
* @returns {void} |
|||
*/ |
|||
function parens(node) { |
|||
var token = context.getFirstToken(node); |
|||
|
|||
// as-needed: x => x
|
|||
if (asNeeded && node.params.length === 1 && node.params[0].type === "Identifier") { |
|||
if (token.type === "Punctuator" && token.value === "(") { |
|||
context.report(node, asNeededMessage); |
|||
} |
|||
return; |
|||
} |
|||
|
|||
if (token.type === "Identifier") { |
|||
var after = context.getTokenAfter(token); |
|||
|
|||
// (x) => x
|
|||
if (after.value !== ")") { |
|||
context.report(node, message); |
|||
} |
|||
} |
|||
} |
|||
|
|||
return { |
|||
"ArrowFunctionExpression": parens |
|||
}; |
|||
}; |
|||
|
|||
module.exports.schema = [ |
|||
{ |
|||
"enum": ["always", "as-needed"] |
|||
} |
|||
]; |
@ -0,0 +1,124 @@ |
|||
/** |
|||
* @fileoverview Rule to require parens in arrow function arguments. |
|||
* @author Jxck |
|||
* @copyright 2015 Jxck. All rights reserved. |
|||
*/ |
|||
"use strict"; |
|||
|
|||
//------------------------------------------------------------------------------
|
|||
// Rule Definition
|
|||
//------------------------------------------------------------------------------
|
|||
|
|||
module.exports = function(context) { |
|||
// merge rules with default
|
|||
var rule = { before: true, after: true }; |
|||
var option = context.options[0] || {}; |
|||
rule.before = option.before !== false; |
|||
rule.after = option.after !== false; |
|||
|
|||
/** |
|||
* Get tokens of arrow(`=>`) and before/after arrow. |
|||
* @param {ASTNode} node The arrow function node. |
|||
* @returns {Object} Tokens of arrow and before/after arrow. |
|||
*/ |
|||
function getTokens(node) { |
|||
var t = context.getFirstToken(node); |
|||
var before; |
|||
while (t.type !== "Punctuator" || t.value !== "=>") { |
|||
before = t; |
|||
t = context.getTokenAfter(t); |
|||
} |
|||
var after = context.getTokenAfter(t); |
|||
return { before: before, arrow: t, after: after }; |
|||
} |
|||
|
|||
/** |
|||
* Count spaces before/after arrow(`=>`) token. |
|||
* @param {Object} tokens Tokens before/after arrow. |
|||
* @returns {Object} count of space before/after arrow. |
|||
*/ |
|||
function countSpaces(tokens) { |
|||
var before = tokens.arrow.range[0] - tokens.before.range[1]; |
|||
var after = tokens.after.range[0] - tokens.arrow.range[1]; |
|||
return { before: before, after: after }; |
|||
} |
|||
|
|||
/** |
|||
* Determines whether space(s) before after arrow(`=>`) is satisfy rule. |
|||
* if before/after value is `true`, there should be space(s). |
|||
* if before/after value is `false`, there should be no space. |
|||
* @param {ASTNode} node The arrow function node. |
|||
* @returns {void} |
|||
*/ |
|||
function spaces(node) { |
|||
var tokens = getTokens(node); |
|||
var countSpace = countSpaces(tokens); |
|||
|
|||
if (rule.before) { |
|||
// should be space(s) before arrow
|
|||
if (countSpace.before === 0) { |
|||
context.report({ |
|||
node: tokens.before, |
|||
message: "Missing space before =>", |
|||
fix: function(fixer) { |
|||
return fixer.insertTextBefore(tokens.arrow, " "); |
|||
} |
|||
}); |
|||
} |
|||
} else { |
|||
// should be no space before arrow
|
|||
if (countSpace.before > 0) { |
|||
context.report({ |
|||
node: tokens.before, |
|||
message: "Unexpected space before =>", |
|||
fix: function(fixer) { |
|||
return fixer.removeRange([tokens.before.range[1], tokens.arrow.range[0]]); |
|||
} |
|||
}); |
|||
} |
|||
} |
|||
|
|||
if (rule.after) { |
|||
// should be space(s) after arrow
|
|||
if (countSpace.after === 0) { |
|||
context.report({ |
|||
node: tokens.after, |
|||
message: "Missing space after =>", |
|||
fix: function(fixer) { |
|||
return fixer.insertTextAfter(tokens.arrow, " "); |
|||
} |
|||
}); |
|||
} |
|||
} else { |
|||
// should be no space after arrow
|
|||
if (countSpace.after > 0) { |
|||
context.report({ |
|||
node: tokens.after, |
|||
message: "Unexpected space after =>", |
|||
fix: function(fixer) { |
|||
return fixer.removeRange([tokens.arrow.range[1], tokens.after.range[0]]); |
|||
} |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
|
|||
return { |
|||
"ArrowFunctionExpression": spaces |
|||
}; |
|||
}; |
|||
|
|||
module.exports.schema = [ |
|||
{ |
|||
"type": "object", |
|||
"properties": { |
|||
"before": { |
|||
"type": "boolean" |
|||
}, |
|||
"after": { |
|||
"type": "boolean" |
|||
} |
|||
}, |
|||
"additionalProperties": false |
|||
} |
|||
]; |
@ -0,0 +1,119 @@ |
|||
/** |
|||
* @fileoverview A rule to disallow or enforce spaces inside of single line blocks. |
|||
* @author Toru Nagashima |
|||
* @copyright 2015 Toru Nagashima. All rights reserved. |
|||
*/ |
|||
|
|||
"use strict"; |
|||
|
|||
var util = require("../ast-utils"); |
|||
|
|||
//------------------------------------------------------------------------------
|
|||
// Rule Definition
|
|||
//------------------------------------------------------------------------------
|
|||
|
|||
module.exports = function(context) { |
|||
var always = (context.options[0] !== "never"), |
|||
message = always ? "Requires a space" : "Unexpected space(s)", |
|||
sourceCode = context.getSourceCode(); |
|||
|
|||
/** |
|||
* Gets the open brace token from a given node. |
|||
* @param {ASTNode} node - A BlockStatement/SwitchStatement node to get. |
|||
* @returns {Token} The token of the open brace. |
|||
*/ |
|||
function getOpenBrace(node) { |
|||
if (node.type === "SwitchStatement") { |
|||
if (node.cases.length > 0) { |
|||
return context.getTokenBefore(node.cases[0]); |
|||
} |
|||
return context.getLastToken(node, 1); |
|||
} |
|||
return context.getFirstToken(node); |
|||
} |
|||
|
|||
/** |
|||
* Checks whether or not: |
|||
* - given tokens are on same line. |
|||
* - there is/isn't a space between given tokens. |
|||
* @param {Token} left - A token to check. |
|||
* @param {Token} right - The token which is next to `left`. |
|||
* @returns {boolean} |
|||
* When the option is `"always"`, `true` if there are one or more spaces between given tokens. |
|||
* When the option is `"never"`, `true` if there are not any spaces between given tokens. |
|||
* If given tokens are not on same line, it's always `true`. |
|||
*/ |
|||
function isValid(left, right) { |
|||
return ( |
|||
!util.isTokenOnSameLine(left, right) || |
|||
sourceCode.isSpaceBetweenTokens(left, right) === always |
|||
); |
|||
} |
|||
|
|||
/** |
|||
* Reports invalid spacing style inside braces. |
|||
* @param {ASTNode} node - A BlockStatement/SwitchStatement node to get. |
|||
* @returns {void} |
|||
*/ |
|||
function checkSpacingInsideBraces(node) { |
|||
// Gets braces and the first/last token of content.
|
|||
var openBrace = getOpenBrace(node); |
|||
var closeBrace = context.getLastToken(node); |
|||
var firstToken = sourceCode.getTokenOrCommentAfter(openBrace); |
|||
var lastToken = sourceCode.getTokenOrCommentBefore(closeBrace); |
|||
|
|||
// Skip if the node is invalid or empty.
|
|||
if (openBrace.type !== "Punctuator" || |
|||
openBrace.value !== "{" || |
|||
closeBrace.type !== "Punctuator" || |
|||
closeBrace.value !== "}" || |
|||
firstToken === closeBrace |
|||
) { |
|||
return; |
|||
} |
|||
|
|||
// Skip line comments for option never
|
|||
if (!always && firstToken.type === "Line") { |
|||
return; |
|||
} |
|||
|
|||
// Check.
|
|||
if (!isValid(openBrace, firstToken)) { |
|||
context.report({ |
|||
node: node, |
|||
loc: openBrace.loc.start, |
|||
message: message + " after \"{\".", |
|||
fix: function(fixer) { |
|||
if (always) { |
|||
return fixer.insertTextBefore(firstToken, " "); |
|||
} |
|||
|
|||
return fixer.removeRange([openBrace.range[1], firstToken.range[0]]); |
|||
} |
|||
}); |
|||
} |
|||
if (!isValid(lastToken, closeBrace)) { |
|||
context.report({ |
|||
node: node, |
|||
loc: closeBrace.loc.start, |
|||
message: message + " before \"}\".", |
|||
fix: function(fixer) { |
|||
if (always) { |
|||
return fixer.insertTextAfter(lastToken, " "); |
|||
} |
|||
|
|||
return fixer.removeRange([lastToken.range[1], closeBrace.range[0]]); |
|||
} |
|||
}); |
|||
} |
|||
} |
|||
|
|||
return { |
|||
BlockStatement: checkSpacingInsideBraces, |
|||
SwitchStatement: checkSpacingInsideBraces |
|||
}; |
|||
}; |
|||
|
|||
module.exports.schema = [ |
|||
{enum: ["always", "never"]} |
|||
]; |
@ -0,0 +1,142 @@ |
|||
/** |
|||
* @fileoverview Enforce return after a callback. |
|||
* @author Jamund Ferguson |
|||
* @copyright 2015 Jamund Ferguson. All rights reserved. |
|||
*/ |
|||
"use strict"; |
|||
|
|||
//------------------------------------------------------------------------------
|
|||
// Rule Definition
|
|||
//------------------------------------------------------------------------------
|
|||
|
|||
module.exports = function(context) { |
|||
|
|||
var callbacks = context.options[0] || ["callback", "cb", "next"]; |
|||
|
|||
//--------------------------------------------------------------------------
|
|||
// Helpers
|
|||
//--------------------------------------------------------------------------
|
|||
|
|||
/** |
|||
* Find the closest parent matching a list of types. |
|||
* @param {ASTNode} node The node whose parents we are searching |
|||
* @param {Array} types The node types to match |
|||
* @returns {ASTNode} The matched node or undefined. |
|||
*/ |
|||
function findClosestParentOfType(node, types) { |
|||
if (!node.parent) { |
|||
return null; |
|||
} |
|||
if (types.indexOf(node.parent.type) === -1) { |
|||
return findClosestParentOfType(node.parent, types); |
|||
} |
|||
return node.parent; |
|||
} |
|||
|
|||
/** |
|||
* Check to see if a CallExpression is in our callback list. |
|||
* @param {ASTNode} node The node to check against our callback names list. |
|||
* @returns {Boolean} Whether or not this function matches our callback name. |
|||
*/ |
|||
function isCallback(node) { |
|||
return node.callee.type === "Identifier" && callbacks.indexOf(node.callee.name) > -1; |
|||
} |
|||
|
|||
/** |
|||
* Determines whether or not the callback is part of a callback expression. |
|||
* @param {ASTNode} node The callback node |
|||
* @param {ASTNode} parentNode The expression node |
|||
* @returns {boolean} Whether or not this is part of a callback expression |
|||
*/ |
|||
function isCallbackExpression(node, parentNode) { |
|||
|
|||
// ensure the parent node exists and is an expression
|
|||
if (!parentNode || parentNode.type !== "ExpressionStatement") { |
|||
return false; |
|||
} |
|||
|
|||
// cb()
|
|||
if (parentNode.expression === node) { |
|||
return true; |
|||
} |
|||
|
|||
// special case for cb && cb() and similar
|
|||
if (parentNode.expression.type === "BinaryExpression" || parentNode.expression.type === "LogicalExpression") { |
|||
if (parentNode.expression.right === node) { |
|||
return true; |
|||
} |
|||
} |
|||
} |
|||
|
|||
//--------------------------------------------------------------------------
|
|||
// Public
|
|||
//--------------------------------------------------------------------------
|
|||
|
|||
return { |
|||
"CallExpression": function(node) { |
|||
|
|||
// if we"re not a callback we can return
|
|||
if (!isCallback(node)) { |
|||
return; |
|||
} |
|||
|
|||
// find the closest block, return or loop
|
|||
var closestBlock = findClosestParentOfType(node, ["BlockStatement", "ReturnStatement", "ArrowFunctionExpression"]) || {}, |
|||
lastItem, parentType; |
|||
|
|||
// if our parent is a return we know we're ok
|
|||
if (closestBlock.type === "ReturnStatement" ) { |
|||
return; |
|||
} |
|||
|
|||
// arrow functions don't always have blocks and implicitly return
|
|||
if (closestBlock.type === "ArrowFunctionExpression") { |
|||
return; |
|||
} |
|||
|
|||
// block statements are part of functions and most if statements
|
|||
if (closestBlock.type === "BlockStatement") { |
|||
|
|||
// find the last item in the block
|
|||
lastItem = closestBlock.body[closestBlock.body.length - 1]; |
|||
|
|||
// if the callback is the last thing in a block that might be ok
|
|||
if (isCallbackExpression(node, lastItem)) { |
|||
|
|||
parentType = closestBlock.parent.type; |
|||
|
|||
// but only if the block is part of a function
|
|||
if (parentType === "FunctionExpression" || |
|||
parentType === "FunctionDeclaration" || |
|||
parentType === "ArrowFunctionExpression" |
|||
) { |
|||
return; |
|||
} |
|||
|
|||
} |
|||
|
|||
// ending a block with a return is also ok
|
|||
if (lastItem.type === "ReturnStatement") { |
|||
|
|||
// but only if the callback is immediately before
|
|||
if (isCallbackExpression(node, closestBlock.body[closestBlock.body.length - 2])) { |
|||
return; |
|||
} |
|||
} |
|||
|
|||
} |
|||
|
|||
// as long as you're the child of a function at this point you should be asked to return
|
|||
if (findClosestParentOfType(node, ["FunctionDeclaration", "FunctionExpression", "ArrowFunctionExpression"])) { |
|||
context.report(node, "Expected return with your callback function."); |
|||
} |
|||
|
|||
} |
|||
|
|||
}; |
|||
}; |
|||
|
|||
module.exports.schema = [{ |
|||
type: "array", |
|||
items: { type: "string" } |
|||
}]; |
@ -1,76 +0,0 @@ |
|||
/** |
|||
* @fileoverview Rule to check for the position of the * in your generator functions |
|||
* @author Jamund Ferguson |
|||
* @copyright 2014 Jamund Ferguson. All rights reserved. |
|||
*/ |
|||
|
|||
"use strict"; |
|||
|
|||
//------------------------------------------------------------------------------
|
|||
// Rule Definition
|
|||
//------------------------------------------------------------------------------
|
|||
|
|||
module.exports = function(context) { |
|||
|
|||
var position = context.options[0] || "end"; |
|||
|
|||
/** |
|||
* Check the position of the star compared to the expected position. |
|||
* @param {ASTNode} node - the entire function node |
|||
* @returns {void} |
|||
*/ |
|||
function checkStarPosition(node) { |
|||
var starToken; |
|||
|
|||
if (!node.generator) { |
|||
return; |
|||
} |
|||
|
|||
// Blocked, pending decision to fix or work around in eslint/espree#36
|
|||
if (context.getAncestors().pop().method) { |
|||
return; |
|||
} |
|||
|
|||
starToken = context.getFirstToken(node, 1); |
|||
|
|||
// check for function *name() {}
|
|||
if (position === "end") { |
|||
|
|||
// * starts where the next identifier begins
|
|||
if (starToken.range[1] !== context.getTokenAfter(starToken).range[0]) { |
|||
context.report(node, "Expected a space before *."); |
|||
} |
|||
} |
|||
|
|||
// check for function* name() {}
|
|||
if (position === "start") { |
|||
|
|||
// * begins where the previous identifier ends
|
|||
if (starToken.range[0] !== context.getTokenBefore(starToken).range[1]) { |
|||
context.report(node, "Expected no space before *."); |
|||
} |
|||
} |
|||
|
|||
// check for function * name() {}
|
|||
if (position === "middle") { |
|||
|
|||
// must be a space before and afer the *
|
|||
if (starToken.range[0] <= context.getTokenBefore(starToken).range[1] || |
|||
starToken.range[1] >= context.getTokenAfter(starToken).range[0]) { |
|||
context.report(node, "Expected spaces around *."); |
|||
} |
|||
} |
|||
} |
|||
|
|||
return { |
|||
"FunctionDeclaration": checkStarPosition, |
|||
"FunctionExpression": checkStarPosition |
|||
}; |
|||
|
|||
}; |
|||
|
|||
module.exports.schema = [ |
|||
{ |
|||
"enum": ["start", "middle", "end"] |
|||
} |
|||
]; |
@ -0,0 +1,35 @@ |
|||
/** |
|||
* @fileoverview Rule for disallowing require() outside of the top-level module context |
|||
* @author Jamund Ferguson |
|||
* @copyright 2015 Jamund Ferguson. All rights reserved. |
|||
*/ |
|||
|
|||
"use strict"; |
|||
|
|||
var ACCEPTABLE_PARENTS = [ |
|||
"AssignmentExpression", |
|||
"VariableDeclarator", |
|||
"MemberExpression", |
|||
"ExpressionStatement", |
|||
"CallExpression", |
|||
"ConditionalExpression", |
|||
"Program", |
|||
"VariableDeclaration" |
|||
]; |
|||
|
|||
module.exports = function(context) { |
|||
return { |
|||
"CallExpression": function(node) { |
|||
if (node.callee.name === "require") { |
|||
var isGoodRequire = context.getAncestors().every(function(parent) { |
|||
return ACCEPTABLE_PARENTS.indexOf(parent.type) > -1; |
|||
}); |
|||
if (!isGoodRequire) { |
|||
context.report(node, "Unexpected require()."); |
|||
} |
|||
} |
|||
} |
|||
}; |
|||
}; |
|||
|
|||
module.exports.schema = []; |
@ -1,49 +0,0 @@ |
|||
/** |
|||
* @fileoverview Rule to flag or require global strict mode. |
|||
* @author Nicholas C. Zakas |
|||
*/ |
|||
"use strict"; |
|||
|
|||
//------------------------------------------------------------------------------
|
|||
// Rule Definition
|
|||
//------------------------------------------------------------------------------
|
|||
|
|||
module.exports = function(context) { |
|||
|
|||
var mode = context.options[0]; |
|||
|
|||
if (mode === "always") { |
|||
|
|||
return { |
|||
"Program": function(node) { |
|||
if (node.body.length > 0) { |
|||
var statement = node.body[0]; |
|||
|
|||
if (!(statement.type === "ExpressionStatement" && statement.expression.value === "use strict")) { |
|||
context.report(node, "Use the global form of \"use strict\"."); |
|||
} |
|||
} |
|||
} |
|||
}; |
|||
|
|||
} else { // mode = "never"
|
|||
|
|||
return { |
|||
"ExpressionStatement": function(node) { |
|||
var parent = context.getAncestors().pop(); |
|||
|
|||
if (node.expression.value === "use strict" && parent.type === "Program") { |
|||
context.report(node, "Use the function form of \"use strict\"."); |
|||
} |
|||
} |
|||
}; |
|||
|
|||
} |
|||
|
|||
}; |
|||
|
|||
module.exports.schema = [ |
|||
{ |
|||
"enum": ["always", "never"] |
|||
} |
|||
]; |
@ -0,0 +1,106 @@ |
|||
/** |
|||
* @fileoverview Rule that warns when identifier names are shorter or longer |
|||
* than the values provided in configuration. |
|||
* @author Burak Yigit Kaya aka BYK |
|||
* @copyright 2015 Burak Yigit Kaya. All rights reserved. |
|||
* @copyright 2015 Mathieu M-Gosselin. All rights reserved. |
|||
*/ |
|||
|
|||
"use strict"; |
|||
|
|||
//------------------------------------------------------------------------------
|
|||
// Rule Definition
|
|||
//------------------------------------------------------------------------------
|
|||
|
|||
module.exports = function(context) { |
|||
var options = context.options[0] || {}; |
|||
var minLength = typeof options.min !== "undefined" ? options.min : 2; |
|||
var maxLength = typeof options.max !== "undefined" ? options.max : Infinity; |
|||
var properties = options.properties !== "never"; |
|||
var exceptions = (options.exceptions ? options.exceptions : []) |
|||
.reduce(function(obj, item) { |
|||
obj[item] = true; |
|||
|
|||
return obj; |
|||
}, {}); |
|||
|
|||
var SUPPORTED_EXPRESSIONS = { |
|||
"MemberExpression": properties && 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; |
|||
}, |
|||
"Property": properties && function(parent, node) { |
|||
return parent.key === node; |
|||
}, |
|||
"ImportDefaultSpecifier": true, |
|||
"RestElement": true, |
|||
"FunctionExpression": true, |
|||
"ArrowFunctionExpression": true, |
|||
"ClassDeclaration": true, |
|||
"FunctionDeclaration": true, |
|||
"MethodDefinition": true, |
|||
"CatchClause": true |
|||
}; |
|||
|
|||
return { |
|||
Identifier: function(node) { |
|||
var name = node.name; |
|||
var parent = node.parent; |
|||
|
|||
var isShort = name.length < minLength; |
|||
var isLong = name.length > maxLength; |
|||
if (!(isShort || isLong) || exceptions[name]) { |
|||
return; // Nothing to report
|
|||
} |
|||
|
|||
var isValidExpression = SUPPORTED_EXPRESSIONS[parent.type]; |
|||
|
|||
if (isValidExpression && (isValidExpression === true || isValidExpression(parent, node))) { |
|||
context.report( |
|||
node, |
|||
isShort ? |
|||
"Identifier name '{{name}}' is too short. (< {{min}})" : |
|||
"Identifier name '{{name}}' is too long. (> {{max}})", |
|||
{ name: name, min: minLength, max: maxLength } |
|||
); |
|||
} |
|||
} |
|||
}; |
|||
}; |
|||
|
|||
module.exports.schema = [ |
|||
{ |
|||
"type": "object", |
|||
"properties": { |
|||
"min": { |
|||
"type": "number" |
|||
}, |
|||
"max": { |
|||
"type": "number" |
|||
}, |
|||
"exceptions": { |
|||
"type": "array", |
|||
"uniqueItems": true, |
|||
"items": { |
|||
"type": "string" |
|||
} |
|||
}, |
|||
"properties": { |
|||
"enum": ["always", "never"] |
|||
} |
|||
}, |
|||
"additionalProperties": false |
|||
} |
|||
]; |
@ -0,0 +1,129 @@ |
|||
/** |
|||
* @fileoverview Rule to flag non-matching identifiers |
|||
* @author Matthieu Larcher |
|||
* @copyright 2015 Matthieu Larcher. All rights reserved. |
|||
* See LICENSE in root directory for full license. |
|||
*/ |
|||
|
|||
"use strict"; |
|||
|
|||
//------------------------------------------------------------------------------
|
|||
// Rule Definition
|
|||
//------------------------------------------------------------------------------
|
|||
|
|||
module.exports = function(context) { |
|||
|
|||
//--------------------------------------------------------------------------
|
|||
// Helpers
|
|||
//--------------------------------------------------------------------------
|
|||
|
|||
var pattern = context.options[0] || "^.+$", |
|||
regexp = new RegExp(pattern); |
|||
|
|||
var options = context.options[1] || {}, |
|||
properties = options.properties; |
|||
|
|||
// cast to boolean and default to false
|
|||
properties = !!properties; |
|||
|
|||
|
|||
/** |
|||
* Checks if a string matches the provided pattern |
|||
* @param {String} name The string to check. |
|||
* @returns {boolean} if the string is a match |
|||
* @private |
|||
*/ |
|||
function isInvalid(name) { |
|||
return !regexp.test(name); |
|||
} |
|||
|
|||
/** |
|||
* Verifies if we should report an error or not based on the effective |
|||
* parent node and the identifier name. |
|||
* @param {ASTNode} effectiveParent The effective parent node of the node to be reported |
|||
* @param {String} name The identifier name of the identifier node |
|||
* @returns {boolean} whether an error should be reported or not |
|||
*/ |
|||
function shouldReport(effectiveParent, name) { |
|||
return effectiveParent.type !== "CallExpression" |
|||
&& effectiveParent.type !== "NewExpression" && |
|||
isInvalid(name); |
|||
} |
|||
|
|||
/** |
|||
* Reports an AST node as a rule violation. |
|||
* @param {ASTNode} node The node to report. |
|||
* @returns {void} |
|||
* @private |
|||
*/ |
|||
function report(node) { |
|||
context.report(node, "Identifier '{{name}}' does not match the pattern '{{pattern}}'.", { |
|||
name: node.name, |
|||
pattern: pattern |
|||
}); |
|||
} |
|||
|
|||
return { |
|||
|
|||
"Identifier": function(node) { |
|||
var name = node.name, |
|||
effectiveParent = (node.parent.type === "MemberExpression") ? node.parent.parent : node.parent; |
|||
|
|||
// MemberExpressions get special rules
|
|||
if (node.parent.type === "MemberExpression") { |
|||
// return early if properties is false
|
|||
if (!properties) { |
|||
return; |
|||
} |
|||
|
|||
// Always check object names
|
|||
if (node.parent.object.type === "Identifier" && |
|||
node.parent.object.name === node.name) { |
|||
if (isInvalid(name)) { |
|||
report(node); |
|||
} |
|||
|
|||
// Report AssignmentExpressions only if they are the left side of the assignment
|
|||
} else if (effectiveParent.type === "AssignmentExpression" && |
|||
(effectiveParent.right.type !== "MemberExpression" || |
|||
effectiveParent.left.type === "MemberExpression" && |
|||
effectiveParent.left.property.name === node.name)) { |
|||
if (isInvalid(name)) { |
|||
report(node); |
|||
} |
|||
} |
|||
|
|||
// Properties have their own rules
|
|||
} else if (node.parent.type === "Property") { |
|||
// return early if properties is false
|
|||
if (!properties) { |
|||
return; |
|||
} |
|||
|
|||
if (shouldReport(effectiveParent, name)) { |
|||
report(node); |
|||
} |
|||
|
|||
// Report anything that is a match and not a CallExpression
|
|||
} else if (shouldReport(effectiveParent, name)) { |
|||
report(node); |
|||
} |
|||
} |
|||
|
|||
}; |
|||
|
|||
}; |
|||
|
|||
module.exports.schema = [ |
|||
{ |
|||
"type": "string" |
|||
}, |
|||
{ |
|||
"type": "object", |
|||
"properties": { |
|||
"properties": { |
|||
"type": "boolean" |
|||
} |
|||
} |
|||
} |
|||
]; |
File diff suppressed because it is too large
@ -0,0 +1,120 @@ |
|||
/** |
|||
* @fileoverview A rule to control the style of variable initializations. |
|||
* @author Colin Ihrig |
|||
* @copyright 2015 Colin Ihrig. All rights reserved. |
|||
*/ |
|||
|
|||
"use strict"; |
|||
|
|||
//------------------------------------------------------------------------------
|
|||
// Helpers
|
|||
//------------------------------------------------------------------------------
|
|||
|
|||
/** |
|||
* Checks whether or not a given node is a for loop. |
|||
* @param {ASTNode} block - A node to check. |
|||
* @returns {boolean} `true` when the node is a for loop. |
|||
*/ |
|||
function isForLoop(block) { |
|||
return block.type === "ForInStatement" || |
|||
block.type === "ForOfStatement" || |
|||
block.type === "ForStatement"; |
|||
} |
|||
|
|||
/** |
|||
* Checks whether or not a given declarator node has its initializer. |
|||
* @param {ASTNode} node - A declarator node to check. |
|||
* @returns {boolean} `true` when the node has its initializer. |
|||
*/ |
|||
function isInitialized(node) { |
|||
var declaration = node.parent; |
|||
var block = declaration.parent; |
|||
|
|||
if (isForLoop(block)) { |
|||
if (block.type === "ForStatement") { |
|||
return block.init === declaration; |
|||
} |
|||
return block.left === declaration; |
|||
} |
|||
return Boolean(node.init); |
|||
} |
|||
|
|||
//------------------------------------------------------------------------------
|
|||
// Rule Definition
|
|||
//------------------------------------------------------------------------------
|
|||
|
|||
module.exports = function(context) { |
|||
|
|||
var MODE_ALWAYS = "always", |
|||
MODE_NEVER = "never"; |
|||
|
|||
var mode = context.options[0] || MODE_ALWAYS; |
|||
var params = context.options[1] || {}; |
|||
//--------------------------------------------------------------------------
|
|||
// Public API
|
|||
//--------------------------------------------------------------------------
|
|||
|
|||
return { |
|||
"VariableDeclaration:exit": function(node) { |
|||
|
|||
var kind = node.kind, |
|||
declarations = node.declarations; |
|||
|
|||
for (var i = 0; i < declarations.length; ++i) { |
|||
var declaration = declarations[i], |
|||
id = declaration.id, |
|||
initialized = isInitialized(declaration), |
|||
isIgnoredForLoop = params.ignoreForLoopInit && isForLoop(node.parent); |
|||
if (id.type !== "Identifier") { |
|||
continue; |
|||
} |
|||
|
|||
if (mode === MODE_ALWAYS && !initialized) { |
|||
context.report(declaration, "Variable '" + id.name + "' should be initialized on declaration."); |
|||
} else if (mode === MODE_NEVER && kind !== "const" && initialized && !isIgnoredForLoop) { |
|||
context.report(declaration, "Variable '" + id.name + "' should not be initialized on declaration."); |
|||
} |
|||
} |
|||
} |
|||
}; |
|||
}; |
|||
|
|||
module.exports.schema = { |
|||
"anyOf": [ |
|||
{ |
|||
"type": "array", |
|||
"items": [ |
|||
{ |
|||
"enum": [0, 1, 2] |
|||
}, |
|||
{ |
|||
"enum": ["always"] |
|||
} |
|||
], |
|||
"minItems": 1, |
|||
"maxItems": 2 |
|||
}, |
|||
{ |
|||
"type": "array", |
|||
"items": [ |
|||
{ |
|||
"enum": [0, 1, 2] |
|||
}, |
|||
{ |
|||
"enum": ["never"] |
|||
}, |
|||
{ |
|||
"type": "object", |
|||
"properties": { |
|||
"ignoreForLoopInit": { |
|||
"type": "boolean" |
|||
} |
|||
}, |
|||
"additionalProperties": false |
|||
} |
|||
], |
|||
"minItems": 1, |
|||
"maxItems": 3 |
|||
} |
|||
] |
|||
}; |
@ -0,0 +1,63 @@ |
|||
/** |
|||
* @fileoverview A rule to ensure consistent quotes used in jsx syntax. |
|||
* @author Mathias Schreck <https://github.com/lo1tuma>
|
|||
* @copyright 2015 Mathias Schreck |
|||
*/ |
|||
|
|||
"use strict"; |
|||
|
|||
//------------------------------------------------------------------------------
|
|||
// Requirements
|
|||
//------------------------------------------------------------------------------
|
|||
|
|||
var astUtils = require("../ast-utils"); |
|||
|
|||
//------------------------------------------------------------------------------
|
|||
// Constants
|
|||
//------------------------------------------------------------------------------
|
|||
|
|||
var QUOTE_SETTINGS = { |
|||
"prefer-double": { |
|||
quote: "\"", |
|||
description: "singlequote" |
|||
}, |
|||
"prefer-single": { |
|||
quote: "'", |
|||
description: "doublequote" |
|||
} |
|||
}; |
|||
|
|||
//------------------------------------------------------------------------------
|
|||
// Rule Definition
|
|||
//------------------------------------------------------------------------------
|
|||
|
|||
module.exports = function(context) { |
|||
var quoteOption = context.options[0] || "prefer-double", |
|||
setting = QUOTE_SETTINGS[quoteOption]; |
|||
|
|||
/** |
|||
* Checks if the given string literal node uses the expected quotes |
|||
* @param {ASTNode} node - A string literal node. |
|||
* @returns {boolean} Whether or not the string literal used the expected quotes. |
|||
* @public |
|||
*/ |
|||
function usesExpectedQuotes(node) { |
|||
return node.value.indexOf(setting.quote) !== -1 || astUtils.isSurroundedBy(node.raw, setting.quote); |
|||
} |
|||
|
|||
return { |
|||
"JSXAttribute": function(node) { |
|||
var attributeValue = node.value; |
|||
|
|||
if (attributeValue && astUtils.isStringLiteral(attributeValue) && !usesExpectedQuotes(attributeValue)) { |
|||
context.report(attributeValue, "Unexpected usage of {{description}}.", setting); |
|||
} |
|||
} |
|||
}; |
|||
}; |
|||
|
|||
module.exports.schema = [ |
|||
{ |
|||
"enum": [ "prefer-single", "prefer-double" ] |
|||
} |
|||
]; |
@ -0,0 +1,88 @@ |
|||
/** |
|||
* @fileoverview A rule to warn against using arrow functions in conditions. |
|||
* @author Jxck <https://github.com/Jxck>
|
|||
* @copyright 2015 Luke Karrys. All rights reserved. |
|||
* The MIT License (MIT) |
|||
|
|||
* Copyright (c) 2015 Jxck |
|||
|
|||
* 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"; |
|||
|
|||
//------------------------------------------------------------------------------
|
|||
// Helpers
|
|||
//------------------------------------------------------------------------------
|
|||
|
|||
/** |
|||
* Checks whether or not a node is an arrow function expression. |
|||
* @param {ASTNode} node - node to test |
|||
* @returns {boolean} `true` if the node is an arrow function expression. |
|||
*/ |
|||
function isArrowFunction(node) { |
|||
return node.test && node.test.type === "ArrowFunctionExpression"; |
|||
} |
|||
|
|||
/** |
|||
* Checks whether or not a node is a conditional expression. |
|||
* @param {ASTNode} node - node to test |
|||
* @returns {boolean} `true` if the node is a conditional expression. |
|||
*/ |
|||
function isConditional(node) { |
|||
return node.body && node.body.type === "ConditionalExpression"; |
|||
} |
|||
|
|||
//------------------------------------------------------------------------------
|
|||
// Rule Definition
|
|||
//------------------------------------------------------------------------------
|
|||
|
|||
module.exports = function(context) { |
|||
/** |
|||
* Reports if a conditional statement is an arrow function. |
|||
* @param {ASTNode} node - A node to check and report. |
|||
* @returns {void} |
|||
*/ |
|||
function checkCondition(node) { |
|||
if (isArrowFunction(node)) { |
|||
context.report(node, "Arrow function `=>` used inside {{statementType}} instead of comparison operator.", {statementType: node.type}); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* Reports if an arrow function contains an ambiguous conditional. |
|||
* @param {ASTNode} node - A node to check and report. |
|||
* @returns {void} |
|||
*/ |
|||
function checkArrowFunc(node) { |
|||
if (isConditional(node)) { |
|||
context.report(node, "Arrow function used ambiguously with a conditional expression."); |
|||
} |
|||
} |
|||
|
|||
return { |
|||
"IfStatement": checkCondition, |
|||
"WhileStatement": checkCondition, |
|||
"ForStatement": checkCondition, |
|||
"ConditionalExpression": checkCondition, |
|||
"ArrowFunctionExpression": checkArrowFunc |
|||
}; |
|||
}; |
|||
|
|||
module.exports.schema = []; |
@ -0,0 +1,47 @@ |
|||
/** |
|||
* @fileoverview Rule to flag use of an lexical declarations inside a case clause |
|||
* @author Erik Arvidsson |
|||
* @copyright 2015 Erik Arvidsson. All rights reserved. |
|||
*/ |
|||
"use strict"; |
|||
|
|||
//------------------------------------------------------------------------------
|
|||
// Rule Definition
|
|||
//------------------------------------------------------------------------------
|
|||
|
|||
module.exports = function(context) { |
|||
|
|||
/** |
|||
* Checks whether or not a node is a lexical declaration. |
|||
* @param {ASTNode} node A direct child statement of a switch case. |
|||
* @returns {boolean} Whether or not the node is a lexical declaration. |
|||
*/ |
|||
function isLexicalDeclaration(node) { |
|||
switch (node.type) { |
|||
case "FunctionDeclaration": |
|||
case "ClassDeclaration": |
|||
return true; |
|||
case "VariableDeclaration": |
|||
return node.kind !== "var"; |
|||
default: |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
return { |
|||
"SwitchCase": function(node) { |
|||
for (var i = 0; i < node.consequent.length; i++) { |
|||
var statement = node.consequent[i]; |
|||
if (isLexicalDeclaration(statement)) { |
|||
context.report({ |
|||
node: node, |
|||
message: "Unexpected lexical declaration in case block." |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
}; |
|||
|
|||
}; |
|||
|
|||
module.exports.schema = []; |
@ -0,0 +1,48 @@ |
|||
/** |
|||
* @fileoverview A rule to disallow modifying variables of class declarations |
|||
* @author Toru Nagashima |
|||
* @copyright 2015 Toru Nagashima. All rights reserved. |
|||
*/ |
|||
|
|||
"use strict"; |
|||
|
|||
var astUtils = require("../ast-utils"); |
|||
|
|||
//------------------------------------------------------------------------------
|
|||
// Rule Definition
|
|||
//------------------------------------------------------------------------------
|
|||
|
|||
module.exports = function(context) { |
|||
|
|||
/** |
|||
* Finds and reports references that are non initializer and writable. |
|||
* @param {Variable} variable - A variable to check. |
|||
* @returns {void} |
|||
*/ |
|||
function checkVariable(variable) { |
|||
astUtils.getModifyingReferences(variable.references).forEach(function(reference) { |
|||
context.report( |
|||
reference.identifier, |
|||
"`{{name}}` is a class.", |
|||
{name: reference.identifier.name}); |
|||
|
|||
}); |
|||
} |
|||
|
|||
/** |
|||
* Finds and reports references that are non initializer and writable. |
|||
* @param {ASTNode} node - A ClassDeclaration/ClassExpression node to check. |
|||
* @returns {void} |
|||
*/ |
|||
function checkForClass(node) { |
|||
context.getDeclaredVariables(node).forEach(checkVariable); |
|||
} |
|||
|
|||
return { |
|||
"ClassDeclaration": checkForClass, |
|||
"ClassExpression": checkForClass |
|||
}; |
|||
|
|||
}; |
|||
|
|||
module.exports.schema = []; |
@ -1,45 +0,0 @@ |
|||
/** |
|||
* @fileoverview Rule to flag trailing commas in object literals. |
|||
* @author Ian Christian Myers |
|||
*/ |
|||
|
|||
"use strict"; |
|||
|
|||
//------------------------------------------------------------------------------
|
|||
// Rule Definition
|
|||
//------------------------------------------------------------------------------
|
|||
|
|||
module.exports = function(context) { |
|||
|
|||
//-------------------------------------------------------------------------
|
|||
// Helpers
|
|||
//-------------------------------------------------------------------------
|
|||
|
|||
function checkForTrailingComma(node) { |
|||
var items = node.properties || node.elements, |
|||
length = items.length, |
|||
lastItem, penultimateToken; |
|||
|
|||
if (length) { |
|||
lastItem = items[length - 1]; |
|||
if (lastItem) { |
|||
penultimateToken = context.getLastToken(node, 1); |
|||
if (penultimateToken.value === ",") { |
|||
context.report(lastItem, penultimateToken.loc.start, "Trailing comma."); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
//--------------------------------------------------------------------------
|
|||
// Public API
|
|||
//--------------------------------------------------------------------------
|
|||
|
|||
return { |
|||
"ObjectExpression": checkForTrailingComma, |
|||
"ArrayExpression": checkForTrailingComma |
|||
}; |
|||
|
|||
}; |
|||
|
|||
module.exports.schema = []; |
@ -0,0 +1,41 @@ |
|||
/** |
|||
* @fileoverview A rule to disallow modifying variables that are declared using `const` |
|||
* @author Toru Nagashima |
|||
* @copyright 2015 Toru Nagashima. All rights reserved. |
|||
*/ |
|||
|
|||
"use strict"; |
|||
|
|||
var astUtils = require("../ast-utils"); |
|||
|
|||
//------------------------------------------------------------------------------
|
|||
// Rule Definition
|
|||
//------------------------------------------------------------------------------
|
|||
|
|||
module.exports = function(context) { |
|||
|
|||
/** |
|||
* Finds and reports references that are non initializer and writable. |
|||
* @param {Variable} variable - A variable to check. |
|||
* @returns {void} |
|||
*/ |
|||
function checkVariable(variable) { |
|||
astUtils.getModifyingReferences(variable.references).forEach(function(reference) { |
|||
context.report( |
|||
reference.identifier, |
|||
"`{{name}}` is constant.", |
|||
{name: reference.identifier.name}); |
|||
}); |
|||
} |
|||
|
|||
return { |
|||
"VariableDeclaration": function(node) { |
|||
if (node.kind === "const") { |
|||
context.getDeclaredVariables(node).forEach(checkVariable); |
|||
} |
|||
} |
|||
}; |
|||
|
|||
}; |
|||
|
|||
module.exports.schema = []; |
@ -0,0 +1,82 @@ |
|||
/** |
|||
* @fileoverview A rule to disallow duplicate name in class members. |
|||
* @author Toru Nagashima |
|||
* @copyright 2015 Toru Nagashima. All rights reserved. |
|||
*/ |
|||
|
|||
"use strict"; |
|||
|
|||
//------------------------------------------------------------------------------
|
|||
// Rule Definition
|
|||
//------------------------------------------------------------------------------
|
|||
|
|||
module.exports = function(context) { |
|||
var stack = []; |
|||
|
|||
/** |
|||
* Gets state of a given member name. |
|||
* @param {string} name - A name of a member. |
|||
* @param {boolean} isStatic - A flag which specifies that is a static member. |
|||
* @returns {object} A state of a given member name. |
|||
* - retv.init {boolean} A flag which shows the name is declared as normal member. |
|||
* - retv.get {boolean} A flag which shows the name is declared as getter. |
|||
* - retv.set {boolean} A flag which shows the name is declared as setter. |
|||
*/ |
|||
function getState(name, isStatic) { |
|||
var stateMap = stack[stack.length - 1]; |
|||
var key = "$" + name; // to avoid "__proto__".
|
|||
|
|||
if (!stateMap[key]) { |
|||
stateMap[key] = { |
|||
nonStatic: {init: false, get: false, set: false}, |
|||
static: {init: false, get: false, set: false} |
|||
}; |
|||
} |
|||
|
|||
return stateMap[key][isStatic ? "static" : "nonStatic"]; |
|||
} |
|||
|
|||
return { |
|||
// Initializes the stack of state of member declarations.
|
|||
"Program": function() { |
|||
stack = []; |
|||
}, |
|||
|
|||
// Initializes state of member declarations for the class.
|
|||
"ClassBody": function() { |
|||
stack.push(Object.create(null)); |
|||
}, |
|||
|
|||
// Disposes the state for the class.
|
|||
"ClassBody:exit": function() { |
|||
stack.pop(); |
|||
}, |
|||
|
|||
// Reports the node if its name has been declared already.
|
|||
"MethodDefinition": function(node) { |
|||
if (node.computed) { |
|||
return; |
|||
} |
|||
|
|||
var name = node.key.name; |
|||
var state = getState(name, node.static); |
|||
var isDuplicate = false; |
|||
if (node.kind === "get") { |
|||
isDuplicate = (state.init || state.get); |
|||
state.get = true; |
|||
} else if (node.kind === "set") { |
|||
isDuplicate = (state.init || state.set); |
|||
state.set = true; |
|||
} else { |
|||
isDuplicate = (state.init || state.get || state.set); |
|||
state.init = true; |
|||
} |
|||
|
|||
if (isDuplicate) { |
|||
context.report(node, "Duplicate name \"{{name}}\".", {name: name}); |
|||
} |
|||
} |
|||
}; |
|||
}; |
|||
|
|||
module.exports.schema = []; |
@ -1,45 +0,0 @@ |
|||
/** |
|||
* @fileoverview Rule to flag the use of empty character classes in regular expressions |
|||
* @author Ian Christian Myers |
|||
*/ |
|||
|
|||
"use strict"; |
|||
|
|||
//------------------------------------------------------------------------------
|
|||
// Helpers
|
|||
//------------------------------------------------------------------------------
|
|||
|
|||
/* |
|||
plain-English description of the following regexp: |
|||
0. `^` fix the match at the beginning of the string |
|||
1. `\/`: the `/` that begins the regexp |
|||
2. `([^\\[]|\\.|\[([^\\\]]|\\.)+\])*`: regexp contents; 0 or more of the following |
|||
2.0. `[^\\[]`: any character that's not a `\` or a `[` (anything but escape sequences and character classes)
|
|||
2.1. `\\.`: an escape sequence |
|||
2.2. `\[([^\\\]]|\\.)+\]`: a character class that isn't empty |
|||
3. `\/` the `/` that ends the regexp |
|||
4. `[gimy]*`: optional regexp flags |
|||
5. `$`: fix the match at the end of the string |
|||
*/ |
|||
var regex = /^\/([^\\[]|\\.|\[([^\\\]]|\\.)+\])*\/[gimy]*$/; |
|||
|
|||
//------------------------------------------------------------------------------
|
|||
// Rule Definition
|
|||
//------------------------------------------------------------------------------
|
|||
|
|||
module.exports = function(context) { |
|||
|
|||
return { |
|||
|
|||
"Literal": function(node) { |
|||
var token = context.getFirstToken(node); |
|||
if (token.type === "RegularExpression" && !regex.test(token.value)) { |
|||
context.report(node, "Empty class."); |
|||
} |
|||
} |
|||
|
|||
}; |
|||
|
|||
}; |
|||
|
|||
module.exports.schema = []; |
@ -0,0 +1,28 @@ |
|||
/** |
|||
* @fileoverview Rule to disallow an empty pattern |
|||
* @author Alberto Rodríguez |
|||
* @copyright 2015 Alberto Rodríguez. All rights reserved. |
|||
* See LICENSE file in root directory for full license. |
|||
*/ |
|||
"use strict"; |
|||
|
|||
//------------------------------------------------------------------------------
|
|||
// Rule Definition
|
|||
//------------------------------------------------------------------------------
|
|||
|
|||
module.exports = function(context) { |
|||
return { |
|||
"ObjectPattern": function(node) { |
|||
if (node.properties.length === 0) { |
|||
context.report(node, "Unexpected empty object pattern."); |
|||
} |
|||
}, |
|||
"ArrayPattern": function(node) { |
|||
if (node.elements.length === 0) { |
|||
context.report(node, "Unexpected empty array pattern."); |
|||
} |
|||
} |
|||
}; |
|||
}; |
|||
|
|||
module.exports.schema = []; |
@ -1,86 +0,0 @@ |
|||
/** |
|||
* @fileoverview Rule to flag unnecessary strict directives. |
|||
* @author Ian Christian Myers |
|||
* @copyright 2014 Ian Christian Myers. All rights reserved. |
|||
*/ |
|||
"use strict"; |
|||
|
|||
//------------------------------------------------------------------------------
|
|||
// Rule Definition
|
|||
//------------------------------------------------------------------------------
|
|||
|
|||
module.exports = function(context) { |
|||
|
|||
function directives(block) { |
|||
var ds = [], body = block.body, e, i, l; |
|||
|
|||
if (body) { |
|||
for (i = 0, l = body.length; i < l; ++i) { |
|||
e = body[i]; |
|||
|
|||
if ( |
|||
e.type === "ExpressionStatement" && |
|||
e.expression.type === "Literal" && |
|||
typeof e.expression.value === "string" |
|||
) { |
|||
ds.push(e.expression); |
|||
} else { |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
|
|||
return ds; |
|||
} |
|||
|
|||
function isStrict(directive) { |
|||
return directive.value === "use strict"; |
|||
} |
|||
|
|||
function checkForUnnecessaryUseStrict(node) { |
|||
var useStrictDirectives = directives(node).filter(isStrict), |
|||
scope, |
|||
upper; |
|||
|
|||
switch (useStrictDirectives.length) { |
|||
case 0: |
|||
break; |
|||
|
|||
case 1: |
|||
scope = context.getScope(); |
|||
upper = scope.upper; |
|||
|
|||
if (upper && upper.functionExpressionScope) { |
|||
upper = upper.upper; |
|||
} |
|||
|
|||
if (upper && upper.isStrict) { |
|||
context.report(useStrictDirectives[0], "Unnecessary 'use strict'."); |
|||
} |
|||
break; |
|||
|
|||
default: |
|||
context.report(useStrictDirectives[1], "Multiple 'use strict' directives."); |
|||
} |
|||
} |
|||
|
|||
return { |
|||
|
|||
"Program": checkForUnnecessaryUseStrict, |
|||
|
|||
"ArrowFunctionExpression": function(node) { |
|||
checkForUnnecessaryUseStrict(node.body); |
|||
}, |
|||
|
|||
"FunctionExpression": function(node) { |
|||
checkForUnnecessaryUseStrict(node.body); |
|||
}, |
|||
|
|||
"FunctionDeclaration": function(node) { |
|||
checkForUnnecessaryUseStrict(node.body); |
|||
} |
|||
}; |
|||
|
|||
}; |
|||
|
|||
module.exports.schema = []; |
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue