eslint 5.0.0-alpha.4 → 5.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -58,6 +58,9 @@ const alwaysOrNever = { enum: ["always", "never"] };
58
58
  const optionsObject = {
59
59
  type: "object",
60
60
  properties: {
61
+ considerPropertyDescriptor: {
62
+ type: "boolean"
63
+ },
61
64
  includeCommonJSModuleExports: {
62
65
  type: "boolean"
63
66
  }
@@ -90,9 +93,26 @@ module.exports = {
90
93
  create(context) {
91
94
  const options = (typeof context.options[0] === "object" ? context.options[0] : context.options[1]) || {};
92
95
  const nameMatches = typeof context.options[0] === "string" ? context.options[0] : "always";
96
+ const considerPropertyDescriptor = options.considerPropertyDescriptor;
93
97
  const includeModuleExports = options.includeCommonJSModuleExports;
94
98
  const ecmaVersion = context.parserOptions && context.parserOptions.ecmaVersion ? context.parserOptions.ecmaVersion : 5;
95
99
 
100
+ /**
101
+ * Check whether node is a certain CallExpression.
102
+ * @param {string} objName object name
103
+ * @param {string} funcName function name
104
+ * @param {ASTNode} node The node to check
105
+ * @returns {boolean} `true` if node matches CallExpression
106
+ */
107
+ function isPropertyCall(objName, funcName, node) {
108
+ if (!node) {
109
+ return false;
110
+ }
111
+ return node.type === "CallExpression" &&
112
+ node.callee.object.name === objName &&
113
+ node.callee.property.name === funcName;
114
+ }
115
+
96
116
  /**
97
117
  * Compares identifiers based on the nameMatches option
98
118
  * @param {string} x the first identifier
@@ -147,7 +167,6 @@ module.exports = {
147
167
  //--------------------------------------------------------------------------
148
168
 
149
169
  return {
150
-
151
170
  VariableDeclarator(node) {
152
171
  if (!node.init || node.init.type !== "FunctionExpression" || node.id.type !== "Identifier") {
153
172
  return;
@@ -179,9 +198,38 @@ module.exports = {
179
198
  if (node.value.type !== "FunctionExpression" || !node.value.id || node.computed && !isStringLiteral(node.key)) {
180
199
  return;
181
200
  }
182
- if (node.key.type === "Identifier" && shouldWarn(node.key.name, node.value.id.name)) {
183
- report(node, node.key.name, node.value.id.name, true);
184
- } else if (
201
+
202
+ if (node.key.type === "Identifier") {
203
+ const functionName = node.value.id.name;
204
+ let propertyName = node.key.name;
205
+
206
+ if (considerPropertyDescriptor && propertyName === "value") {
207
+ if (isPropertyCall("Object", "defineProperty", node.parent.parent) || isPropertyCall("Reflect", "defineProperty", node.parent.parent)) {
208
+ const property = node.parent.parent.arguments[1];
209
+
210
+ if (isStringLiteral(property) && shouldWarn(property.value, functionName)) {
211
+ report(node, property.value, functionName, true);
212
+ }
213
+ } else if (isPropertyCall("Object", "defineProperties", node.parent.parent.parent.parent)) {
214
+ propertyName = node.parent.parent.key.name;
215
+ if (!node.parent.parent.computed && shouldWarn(propertyName, functionName)) {
216
+ report(node, propertyName, functionName, true);
217
+ }
218
+ } else if (isPropertyCall("Object", "create", node.parent.parent.parent.parent)) {
219
+ propertyName = node.parent.parent.key.name;
220
+ if (!node.parent.parent.computed && shouldWarn(propertyName, functionName)) {
221
+ report(node, propertyName, functionName, true);
222
+ }
223
+ } else if (shouldWarn(propertyName, functionName)) {
224
+ report(node, propertyName, functionName, true);
225
+ }
226
+ } else if (shouldWarn(propertyName, functionName)) {
227
+ report(node, propertyName, functionName, true);
228
+ }
229
+ return;
230
+ }
231
+
232
+ if (
185
233
  isStringLiteral(node.key) &&
186
234
  isIdentifier(node.key.value, ecmaVersion) &&
187
235
  shouldWarn(node.key.value, node.value.id.name)
@@ -0,0 +1,218 @@
1
+ /**
2
+ * @fileoverview A rule to set the maximum number of line of code in a function.
3
+ * @author Pete Ward <peteward44@gmail.com>
4
+ */
5
+ "use strict";
6
+
7
+ //------------------------------------------------------------------------------
8
+ // Requirements
9
+ //------------------------------------------------------------------------------
10
+
11
+ const astUtils = require("../ast-utils");
12
+
13
+ //------------------------------------------------------------------------------
14
+ // Constants
15
+ //------------------------------------------------------------------------------
16
+
17
+ const OPTIONS_SCHEMA = {
18
+ type: "object",
19
+ properties: {
20
+ max: {
21
+ type: "integer",
22
+ minimum: 0
23
+ },
24
+ skipComments: {
25
+ type: "boolean"
26
+ },
27
+ skipBlankLines: {
28
+ type: "boolean"
29
+ },
30
+ IIFEs: {
31
+ type: "boolean"
32
+ }
33
+ },
34
+ additionalProperties: false
35
+ };
36
+
37
+ const OPTIONS_OR_INTEGER_SCHEMA = {
38
+ oneOf: [
39
+ OPTIONS_SCHEMA,
40
+ {
41
+ type: "integer",
42
+ minimum: 1
43
+ }
44
+ ]
45
+ };
46
+
47
+ /**
48
+ * Given a list of comment nodes, return a map with numeric keys (source code line numbers) and comment token values.
49
+ * @param {Array} comments An array of comment nodes.
50
+ * @returns {Map.<string,Node>} A map with numeric keys (source code line numbers) and comment token values.
51
+ */
52
+ function getCommentLineNumbers(comments) {
53
+ const map = new Map();
54
+
55
+ if (!comments) {
56
+ return map;
57
+ }
58
+ comments.forEach(comment => {
59
+ for (let i = comment.loc.start.line; i <= comment.loc.end.line; i++) {
60
+ map.set(i, comment);
61
+ }
62
+ });
63
+ return map;
64
+ }
65
+
66
+ //------------------------------------------------------------------------------
67
+ // Rule Definition
68
+ //------------------------------------------------------------------------------
69
+
70
+ module.exports = {
71
+ meta: {
72
+ docs: {
73
+ description: "enforce a maximum number of line of code in a function",
74
+ category: "Stylistic Issues",
75
+ recommended: false,
76
+ url: "https://eslint.org/docs/rules/max-lines-per-function"
77
+ },
78
+
79
+ schema: [
80
+ OPTIONS_OR_INTEGER_SCHEMA
81
+ ]
82
+ },
83
+
84
+ create(context) {
85
+ const sourceCode = context.getSourceCode();
86
+ const lines = sourceCode.lines;
87
+
88
+ const option = context.options[0];
89
+ let maxLines = 50;
90
+ let skipComments = false;
91
+ let skipBlankLines = false;
92
+ let IIFEs = false;
93
+
94
+ if (typeof option === "object") {
95
+ if (typeof option.max === "number") {
96
+ maxLines = option.max;
97
+ }
98
+ if (typeof option.skipComments === "boolean") {
99
+ skipComments = option.skipComments;
100
+ }
101
+ if (typeof option.skipBlankLines === "boolean") {
102
+ skipBlankLines = option.skipBlankLines;
103
+ }
104
+ if (typeof option.IIFEs === "boolean") {
105
+ IIFEs = option.IIFEs;
106
+ }
107
+ } else if (typeof option === "number") {
108
+ maxLines = option;
109
+ }
110
+
111
+ const commentLineNumbers = getCommentLineNumbers(sourceCode.getAllComments());
112
+
113
+ //--------------------------------------------------------------------------
114
+ // Helpers
115
+ //--------------------------------------------------------------------------
116
+
117
+ /**
118
+ * Tells if a comment encompasses the entire line.
119
+ * @param {string} line The source line with a trailing comment
120
+ * @param {number} lineNumber The one-indexed line number this is on
121
+ * @param {ASTNode} comment The comment to remove
122
+ * @returns {boolean} If the comment covers the entire line
123
+ */
124
+ function isFullLineComment(line, lineNumber, comment) {
125
+ const start = comment.loc.start,
126
+ end = comment.loc.end,
127
+ isFirstTokenOnLine = start.line === lineNumber && !line.slice(0, start.column).trim(),
128
+ isLastTokenOnLine = end.line === lineNumber && !line.slice(end.column).trim();
129
+
130
+ return comment &&
131
+ (start.line < lineNumber || isFirstTokenOnLine) &&
132
+ (end.line > lineNumber || isLastTokenOnLine);
133
+ }
134
+
135
+ /**
136
+ * Identifies is a node is a FunctionExpression which is part of an IIFE
137
+ * @param {ASTNode} node Node to test
138
+ * @returns {boolean} True if it's an IIFE
139
+ */
140
+ function isIIFE(node) {
141
+ return node.type === "FunctionExpression" && node.parent && node.parent.type === "CallExpression" && node.parent.callee === node;
142
+ }
143
+
144
+ /**
145
+ * Identifies is a node is a FunctionExpression which is embedded within a MethodDefinition or Property
146
+ * @param {ASTNode} node Node to test
147
+ * @returns {boolean} True if it's a FunctionExpression embedded within a MethodDefinition or Property
148
+ */
149
+ function isEmbedded(node) {
150
+ if (!node.parent) {
151
+ return false;
152
+ }
153
+ if (node !== node.parent.value) {
154
+ return false;
155
+ }
156
+ if (node.parent.type === "MethodDefinition") {
157
+ return true;
158
+ }
159
+ if (node.parent.type === "Property") {
160
+ return node.parent.method === true || node.parent.kind === "get" || node.parent.kind === "set";
161
+ }
162
+ return false;
163
+ }
164
+
165
+ /**
166
+ * Count the lines in the function
167
+ * @param {ASTNode} funcNode Function AST node
168
+ * @returns {void}
169
+ * @private
170
+ */
171
+ function processFunction(funcNode) {
172
+ const node = isEmbedded(funcNode) ? funcNode.parent : funcNode;
173
+
174
+ if (!IIFEs && isIIFE(node)) {
175
+ return;
176
+ }
177
+ let lineCount = 0;
178
+
179
+ for (let i = node.loc.start.line - 1; i < node.loc.end.line; ++i) {
180
+ const line = lines[i];
181
+
182
+ if (skipComments) {
183
+ if (commentLineNumbers.has(i + 1) && isFullLineComment(line, i + 1, commentLineNumbers.get(i + 1))) {
184
+ continue;
185
+ }
186
+ }
187
+
188
+ if (skipBlankLines) {
189
+ if (line.match(/^\s*$/)) {
190
+ continue;
191
+ }
192
+ }
193
+
194
+ lineCount++;
195
+ }
196
+
197
+ if (lineCount > maxLines) {
198
+ const name = astUtils.getFunctionNameWithKind(funcNode);
199
+
200
+ context.report({
201
+ node,
202
+ message: "{{name}} has too many lines ({{lineCount}}). Maximum allowed is {{maxLines}}.",
203
+ data: { name, lineCount, maxLines }
204
+ });
205
+ }
206
+ }
207
+
208
+ //--------------------------------------------------------------------------
209
+ // Public API
210
+ //--------------------------------------------------------------------------
211
+
212
+ return {
213
+ FunctionDeclaration: processFunction,
214
+ FunctionExpression: processFunction,
215
+ ArrowFunctionExpression: processFunction
216
+ };
217
+ }
218
+ };
@@ -1,6 +1,7 @@
1
1
  /**
2
2
  * @fileoverview Rule to flag variable leak in CatchClauses in IE 8 and earlier
3
3
  * @author Ian Christian Myers
4
+ * @deprecated in ESLint v5.1.0
4
5
  */
5
6
 
6
7
  "use strict";
@@ -21,8 +22,10 @@ module.exports = {
21
22
  description: "disallow `catch` clause parameters from shadowing variables in the outer scope",
22
23
  category: "Variables",
23
24
  recommended: false,
24
- url: "https://eslint.org/docs/rules/no-catch-shadow"
25
+ url: "https://eslint.org/docs/rules/no-catch-shadow",
26
+ replacedBy: ["no-shadow"]
25
27
  },
28
+ deprecated: true,
26
29
 
27
30
  schema: [],
28
31
 
@@ -53,7 +56,7 @@ module.exports = {
53
56
 
54
57
  return {
55
58
 
56
- CatchClause(node) {
59
+ "CatchClause[param!=null]"(node) {
57
60
  let scope = context.getScope();
58
61
 
59
62
  /*
@@ -5,8 +5,6 @@
5
5
 
6
6
  "use strict";
7
7
 
8
- const astUtils = require("../ast-utils");
9
-
10
8
  //------------------------------------------------------------------------------
11
9
  // Rule Definition
12
10
  //------------------------------------------------------------------------------
@@ -19,7 +17,7 @@ module.exports = {
19
17
  recommended: true,
20
18
  url: "https://eslint.org/docs/rules/no-debugger"
21
19
  },
22
- fixable: "code",
20
+ fixable: null,
23
21
  schema: [],
24
22
  messages: {
25
23
  unexpected: "Unexpected 'debugger' statement."
@@ -32,13 +30,7 @@ module.exports = {
32
30
  DebuggerStatement(node) {
33
31
  context.report({
34
32
  node,
35
- messageId: "unexpected",
36
- fix(fixer) {
37
- if (astUtils.STATEMENT_LIST_PARENTS.has(node.parent.type)) {
38
- return fixer.remove(node);
39
- }
40
- return null;
41
- }
33
+ messageId: "unexpected"
42
34
  });
43
35
  }
44
36
  };
@@ -24,45 +24,19 @@ module.exports = {
24
24
 
25
25
  const RESTRICTED = ["undefined", "NaN", "Infinity", "arguments", "eval"];
26
26
 
27
- /**
28
- * Check if the node name is present inside the restricted list
29
- * @param {ASTNode} id id to evaluate
30
- * @returns {void}
31
- * @private
32
- */
33
- function checkForViolation(id) {
34
- if (RESTRICTED.indexOf(id.name) > -1) {
35
- context.report({
36
- node: id,
37
- message: "Shadowing of global property '{{idName}}'.",
38
- data: {
39
- idName: id.name
40
- }
41
- });
42
- }
43
- }
44
-
45
27
  return {
46
- VariableDeclarator(node) {
47
- checkForViolation(node.id);
48
- },
49
- ArrowFunctionExpression(node) {
50
- [].map.call(node.params, checkForViolation);
51
- },
52
- FunctionExpression(node) {
53
- if (node.id) {
54
- checkForViolation(node.id);
55
- }
56
- [].map.call(node.params, checkForViolation);
57
- },
58
- FunctionDeclaration(node) {
59
- if (node.id) {
60
- checkForViolation(node.id);
61
- [].map.call(node.params, checkForViolation);
28
+ "VariableDeclaration, :function, CatchClause"(node) {
29
+ for (const variable of context.getDeclaredVariables(node)) {
30
+ if (variable.defs.length > 0 && RESTRICTED.includes(variable.name)) {
31
+ context.report({
32
+ node: variable.defs[0].name,
33
+ message: "Shadowing of global property '{{idName}}'.",
34
+ data: {
35
+ idName: variable.name
36
+ }
37
+ });
38
+ }
62
39
  }
63
- },
64
- CatchClause(node) {
65
- checkForViolation(node.param);
66
40
  }
67
41
  };
68
42
 
@@ -140,7 +140,7 @@ module.exports = {
140
140
  fix: fixer => {
141
141
  let nodeAlternate = astUtils.getParenthesisedText(sourceCode, node.alternate);
142
142
 
143
- if (node.alternate.type === "ConditionalExpression") {
143
+ if (node.alternate.type === "ConditionalExpression" || node.alternate.type === "YieldExpression") {
144
144
  const isAlternateParenthesised = astUtils.isParenthesised(sourceCode, node.alternate);
145
145
 
146
146
  nodeAlternate = isAlternateParenthesised ? nodeAlternate : `(${nodeAlternate})`;
@@ -219,49 +219,14 @@ module.exports = {
219
219
  data: reference.identifier
220
220
  });
221
221
  });
222
- }
223
222
 
224
- /**
225
- * Validates variables inside of a node's scope.
226
- * @param {ASTNode} node The node to check.
227
- * @returns {void}
228
- * @private
229
- */
230
- function findVariables() {
231
- const scope = context.getScope();
232
-
233
- findVariablesInScope(scope);
223
+ scope.childScopes.forEach(findVariablesInScope);
234
224
  }
235
225
 
236
- const ruleDefinition = {
237
- "Program:exit"(node) {
238
- const scope = context.getScope(),
239
- ecmaFeatures = context.parserOptions.ecmaFeatures || {};
240
-
241
- findVariablesInScope(scope);
242
-
243
- // both Node.js and Modules have an extra scope
244
- if (ecmaFeatures.globalReturn || node.sourceType === "module") {
245
- findVariablesInScope(scope.childScopes[0]);
246
- }
226
+ return {
227
+ Program() {
228
+ findVariablesInScope(context.getScope());
247
229
  }
248
230
  };
249
-
250
- if (context.parserOptions.ecmaVersion >= 6) {
251
- ruleDefinition["BlockStatement:exit"] =
252
- ruleDefinition["SwitchStatement:exit"] = findVariables;
253
-
254
- ruleDefinition["ArrowFunctionExpression:exit"] = function(node) {
255
- if (node.body.type !== "BlockStatement") {
256
- findVariables();
257
- }
258
- };
259
- } else {
260
- ruleDefinition["FunctionExpression:exit"] =
261
- ruleDefinition["FunctionDeclaration:exit"] =
262
- ruleDefinition["ArrowFunctionExpression:exit"] = findVariables;
263
- }
264
-
265
- return ruleDefinition;
266
231
  }
267
232
  };
@@ -57,6 +57,8 @@ module.exports = {
57
57
  */
58
58
  function convertToRegExp(term) {
59
59
  const escaped = term.replace(/[-/\\$^*+?.()|[\]{}]/g, "\\$&");
60
+ const wordBoundary = "\\b";
61
+ const eitherOrWordBoundary = `|${wordBoundary}`;
60
62
  let prefix;
61
63
 
62
64
  /*
@@ -79,12 +81,27 @@ module.exports = {
79
81
  */
80
82
  prefix = "^\\s*";
81
83
  } else if (/^\w/.test(term)) {
82
- prefix = "\\b";
84
+ prefix = wordBoundary;
83
85
  } else {
84
86
  prefix = "";
85
87
  }
86
88
 
87
- return new RegExp(prefix + escaped + suffix, "i");
89
+ if (location === "start") {
90
+
91
+ /*
92
+ * For location "start" the regex should be
93
+ * ^\s*TERM\b. This checks the word boundary
94
+ * at the beginning of the comment.
95
+ */
96
+ return new RegExp(prefix + escaped + suffix, "i");
97
+ }
98
+
99
+ /*
100
+ * For location "anywhere" the regex should be
101
+ * \bTERM\b|\bTERM\b, this checks the entire comment
102
+ * for the term.
103
+ */
104
+ return new RegExp(prefix + escaped + suffix + eitherOrWordBoundary + term + wordBoundary, "i");
88
105
  }
89
106
 
90
107
  const warningRegExps = warningTerms.map(convertToRegExp);
@@ -170,7 +170,7 @@ function getIdentifierIfShouldBeConst(variable, ignoreReadBeforeAssign) {
170
170
  const elements = leftNode.elements;
171
171
 
172
172
  hasOuterVariables = elements
173
- .map(element => element.name)
173
+ .map(element => element && element.name)
174
174
  .some(name => isOuterVariableInDestructing(name, variable.scope));
175
175
  }
176
176
  if (hasOuterVariables) {