eslint 4.18.0 → 4.19.1

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.
Files changed (65) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/README.md +2 -2
  3. package/conf/environments.js +3 -1
  4. package/conf/eslint-recommended.js +0 -0
  5. package/lib/ast-utils.js +25 -29
  6. package/lib/cli-engine.js +29 -28
  7. package/lib/code-path-analysis/code-path-state.js +5 -5
  8. package/lib/code-path-analysis/code-path.js +11 -6
  9. package/lib/code-path-analysis/fork-context.js +10 -12
  10. package/lib/config/config-file.js +20 -11
  11. package/lib/config/config-ops.js +8 -10
  12. package/lib/config/config-rule.js +2 -3
  13. package/lib/config/plugins.js +20 -0
  14. package/lib/config.js +7 -8
  15. package/lib/file-finder.js +9 -10
  16. package/lib/ignored-paths.js +4 -4
  17. package/lib/linter.js +397 -406
  18. package/lib/load-rules.js +6 -7
  19. package/lib/rules/accessor-pairs.js +4 -4
  20. package/lib/rules/array-callback-return.js +8 -6
  21. package/lib/rules/array-element-newline.js +4 -4
  22. package/lib/rules/curly.js +11 -10
  23. package/lib/rules/generator-star-spacing.js +1 -2
  24. package/lib/rules/indent-legacy.js +7 -10
  25. package/lib/rules/indent.js +51 -29
  26. package/lib/rules/keyword-spacing.js +6 -18
  27. package/lib/rules/max-len.js +12 -5
  28. package/lib/rules/no-await-in-loop.js +1 -1
  29. package/lib/rules/no-buffer-constructor.js +1 -1
  30. package/lib/rules/no-control-regex.js +51 -72
  31. package/lib/rules/no-else-return.js +7 -6
  32. package/lib/rules/no-empty-character-class.js +1 -1
  33. package/lib/rules/no-eval.js +7 -8
  34. package/lib/rules/no-extra-parens.js +5 -4
  35. package/lib/rules/no-implicit-coercion.js +6 -9
  36. package/lib/rules/no-invalid-regexp.js +53 -36
  37. package/lib/rules/no-irregular-whitespace.js +1 -1
  38. package/lib/rules/no-loop-func.js +9 -11
  39. package/lib/rules/no-magic-numbers.js +17 -10
  40. package/lib/rules/no-return-assign.js +4 -3
  41. package/lib/rules/no-unexpected-multiline.js +1 -1
  42. package/lib/rules/no-unsafe-finally.js +7 -4
  43. package/lib/rules/no-useless-escape.js +2 -2
  44. package/lib/rules/no-useless-return.js +10 -9
  45. package/lib/rules/no-var.js +8 -8
  46. package/lib/rules/object-curly-newline.js +2 -1
  47. package/lib/rules/one-var.js +140 -97
  48. package/lib/rules/padding-line-between-statements.js +6 -4
  49. package/lib/rules/prefer-arrow-callback.js +5 -4
  50. package/lib/rules/prefer-template.js +5 -3
  51. package/lib/rules/space-unary-ops.js +1 -3
  52. package/lib/rules/spaced-comment.js +3 -7
  53. package/lib/rules/template-tag-spacing.js +0 -0
  54. package/lib/rules/valid-jsdoc.js +6 -6
  55. package/lib/rules/vars-on-top.js +7 -17
  56. package/lib/timing.js +3 -5
  57. package/lib/util/glob-util.js +11 -11
  58. package/lib/util/interpolate.js +5 -1
  59. package/lib/util/naming.js +11 -10
  60. package/lib/util/npm-util.js +4 -6
  61. package/lib/util/path-util.js +6 -8
  62. package/lib/util/source-code-util.js +23 -26
  63. package/lib/util/source-code.js +4 -3
  64. package/package.json +4 -3
  65. package/conf/default-config-options.js +0 -29
@@ -217,8 +217,6 @@ module.exports = {
217
217
  */
218
218
  function checkIfWithoutElse(node) {
219
219
  const parent = node.parent;
220
- let consequents,
221
- alternate;
222
220
 
223
221
  /*
224
222
  * Fixing this would require splitting one statement into two, so no error should
@@ -228,12 +226,15 @@ module.exports = {
228
226
  return;
229
227
  }
230
228
 
231
- for (consequents = []; node.type === "IfStatement"; node = node.alternate) {
232
- if (!node.alternate) {
229
+ const consequents = [];
230
+ let alternate;
231
+
232
+ for (let currentNode = node; currentNode.type === "IfStatement"; currentNode = currentNode.alternate) {
233
+ if (!currentNode.alternate) {
233
234
  return;
234
235
  }
235
- consequents.push(node.consequent);
236
- alternate = node.alternate;
236
+ consequents.push(currentNode.consequent);
237
+ alternate = currentNode.alternate;
237
238
  }
238
239
 
239
240
  if (consequents.every(alwaysReturns)) {
@@ -21,7 +21,7 @@
21
21
  * 4. `[gimuy]*`: optional regexp flags
22
22
  * 5. `$`: fix the match at the end of the string
23
23
  */
24
- const regex = /^\/([^\\[]|\\.|\[([^\\\]]|\\.)+])*\/[gimuy]*$/;
24
+ const regex = /^\/([^\\[]|\\.|\[([^\\\]]|\\.)+])*\/[gimuys]*$/;
25
25
 
26
26
  //------------------------------------------------------------------------------
27
27
  // Rule Definition
@@ -151,18 +151,17 @@ module.exports = {
151
151
  * @returns {void}
152
152
  */
153
153
  function report(node) {
154
- let locationNode = node;
155
154
  const parent = node.parent;
155
+ const locationNode = node.type === "MemberExpression"
156
+ ? node.property
157
+ : node;
156
158
 
157
- if (node.type === "MemberExpression") {
158
- locationNode = node.property;
159
- }
160
- if (parent.type === "CallExpression" && parent.callee === node) {
161
- node = parent;
162
- }
159
+ const reportNode = parent.type === "CallExpression" && parent.callee === node
160
+ ? parent
161
+ : node;
163
162
 
164
163
  context.report({
165
- node,
164
+ node: reportNode,
166
165
  loc: locationNode.loc.start,
167
166
  messageId: "unexpected"
168
167
  });
@@ -167,12 +167,13 @@ module.exports = {
167
167
  * @private
168
168
  */
169
169
  function isInReturnStatement(node) {
170
- while (node) {
171
- if (node.type === "ReturnStatement" ||
172
- (node.type === "ArrowFunctionExpression" && node.body.type !== "BlockStatement")) {
170
+ for (let currentNode = node; currentNode; currentNode = currentNode.parent) {
171
+ if (
172
+ currentNode.type === "ReturnStatement" ||
173
+ (currentNode.type === "ArrowFunctionExpression" && currentNode.body.type !== "BlockStatement")
174
+ ) {
173
175
  return true;
174
176
  }
175
- node = node.parent;
176
177
  }
177
178
 
178
179
  return false;
@@ -20,7 +20,6 @@ const ALLOWABLE_OPERATORS = ["~", "!!", "+", "*"];
20
20
  * @returns {Object} The parsed and normalized option object.
21
21
  */
22
22
  function parseOptions(options) {
23
- options = options || {};
24
23
  return {
25
24
  boolean: "boolean" in options ? Boolean(options.boolean) : true,
26
25
  number: "number" in options ? Boolean(options.number) : true,
@@ -186,7 +185,7 @@ module.exports = {
186
185
  },
187
186
 
188
187
  create(context) {
189
- const options = parseOptions(context.options[0]);
188
+ const options = parseOptions(context.options[0] || {});
190
189
  const sourceCode = context.getSourceCode();
191
190
 
192
191
  /**
@@ -197,8 +196,6 @@ module.exports = {
197
196
  * @returns {void}
198
197
  */
199
198
  function report(node, recommendation, shouldFix) {
200
- shouldFix = typeof shouldFix === "undefined" ? true : shouldFix;
201
-
202
199
  context.report({
203
200
  node,
204
201
  message: "use `{{recommendation}}` instead.",
@@ -233,7 +230,7 @@ module.exports = {
233
230
  if (!operatorAllowed && options.boolean && isDoubleLogicalNegating(node)) {
234
231
  const recommendation = `Boolean(${sourceCode.getText(node.argument.argument)})`;
235
232
 
236
- report(node, recommendation);
233
+ report(node, recommendation, true);
237
234
  }
238
235
 
239
236
  // ~foo.indexOf(bar)
@@ -249,7 +246,7 @@ module.exports = {
249
246
  if (!operatorAllowed && options.number && node.operator === "+" && !isNumeric(node.argument)) {
250
247
  const recommendation = `Number(${sourceCode.getText(node.argument)})`;
251
248
 
252
- report(node, recommendation);
249
+ report(node, recommendation, true);
253
250
  }
254
251
  },
255
252
 
@@ -264,7 +261,7 @@ module.exports = {
264
261
  if (nonNumericOperand) {
265
262
  const recommendation = `Number(${sourceCode.getText(nonNumericOperand)})`;
266
263
 
267
- report(node, recommendation);
264
+ report(node, recommendation, true);
268
265
  }
269
266
 
270
267
  // "" + foo
@@ -272,7 +269,7 @@ module.exports = {
272
269
  if (!operatorAllowed && options.string && isConcatWithEmptyString(node)) {
273
270
  const recommendation = `String(${sourceCode.getText(getNonEmptyOperand(node))})`;
274
271
 
275
- report(node, recommendation);
272
+ report(node, recommendation, true);
276
273
  }
277
274
  },
278
275
 
@@ -285,7 +282,7 @@ module.exports = {
285
282
  const code = sourceCode.getText(getNonEmptyOperand(node));
286
283
  const recommendation = `${code} = String(${code})`;
287
284
 
288
- report(node, recommendation);
285
+ report(node, recommendation, true);
289
286
  }
290
287
  }
291
288
  };
@@ -8,7 +8,10 @@
8
8
  // Requirements
9
9
  //------------------------------------------------------------------------------
10
10
 
11
- const espree = require("espree");
11
+ const RegExpValidator = require("regexpp").RegExpValidator;
12
+ const validator = new RegExpValidator({ ecmaVersion: 2018 });
13
+ const validFlags = /[gimuys]/g;
14
+ const undefined1 = void 0;
12
15
 
13
16
  //------------------------------------------------------------------------------
14
17
  // Rule Definition
@@ -40,10 +43,14 @@ module.exports = {
40
43
  create(context) {
41
44
 
42
45
  const options = context.options[0];
43
- let allowedFlags = "";
46
+ let allowedFlags = null;
44
47
 
45
48
  if (options && options.allowConstructorFlags) {
46
- allowedFlags = options.allowConstructorFlags.join("");
49
+ const temp = options.allowConstructorFlags.join("").replace(validFlags, "");
50
+
51
+ if (temp) {
52
+ allowedFlags = new RegExp(`[${temp}]`, "gi");
53
+ }
47
54
  }
48
55
 
49
56
  /**
@@ -57,51 +64,61 @@ module.exports = {
57
64
  }
58
65
 
59
66
  /**
60
- * Validate strings passed to the RegExp constructor
61
- * @param {ASTNode} node node to evaluate
62
- * @returns {void}
63
- * @private
67
+ * Check syntax error in a given pattern.
68
+ * @param {string} pattern The RegExp pattern to validate.
69
+ * @param {boolean} uFlag The Unicode flag.
70
+ * @returns {string|null} The syntax error.
71
+ */
72
+ function validateRegExpPattern(pattern, uFlag) {
73
+ try {
74
+ validator.validatePattern(pattern, undefined1, undefined1, uFlag);
75
+ return null;
76
+ } catch (err) {
77
+ return err.message;
78
+ }
79
+ }
80
+
81
+ /**
82
+ * Check syntax error in a given flags.
83
+ * @param {string} flags The RegExp flags to validate.
84
+ * @returns {string|null} The syntax error.
64
85
  */
65
- function check(node) {
66
- if (node.callee.type === "Identifier" && node.callee.name === "RegExp" && isString(node.arguments[0])) {
86
+ function validateRegExpFlags(flags) {
87
+ try {
88
+ validator.validateFlags(flags);
89
+ return null;
90
+ } catch (err) {
91
+ return `Invalid flags supplied to RegExp constructor '${flags}'`;
92
+ }
93
+ }
94
+
95
+ return {
96
+ "CallExpression, NewExpression"(node) {
97
+ if (node.callee.type !== "Identifier" || node.callee.name !== "RegExp" || !isString(node.arguments[0])) {
98
+ return;
99
+ }
100
+ const pattern = node.arguments[0].value;
67
101
  let flags = isString(node.arguments[1]) ? node.arguments[1].value : "";
68
102
 
69
103
  if (allowedFlags) {
70
- flags = flags.replace(new RegExp(`[${allowedFlags}]`, "gi"), "");
104
+ flags = flags.replace(allowedFlags, "");
71
105
  }
72
106
 
73
- try {
74
- void new RegExp(node.arguments[0].value);
75
- } catch (e) {
107
+ // If flags are unknown, check both are errored or not.
108
+ const message = validateRegExpFlags(flags) || (
109
+ flags
110
+ ? validateRegExpPattern(pattern, flags.indexOf("u") !== -1)
111
+ : validateRegExpPattern(pattern, true) && validateRegExpPattern(pattern, false)
112
+ );
113
+
114
+ if (message) {
76
115
  context.report({
77
116
  node,
78
117
  message: "{{message}}.",
79
- data: e
118
+ data: { message }
80
119
  });
81
120
  }
82
-
83
- if (flags) {
84
-
85
- try {
86
- espree.parse(`/./${flags}`, context.parserOptions);
87
- } catch (ex) {
88
- context.report({
89
- node,
90
- message: "Invalid flags supplied to RegExp constructor '{{flags}}'.",
91
- data: {
92
- flags
93
- }
94
- });
95
- }
96
- }
97
-
98
121
  }
99
- }
100
-
101
- return {
102
- CallExpression: check,
103
- NewExpression: check
104
122
  };
105
-
106
123
  }
107
124
  };
@@ -101,7 +101,7 @@ module.exports = {
101
101
  */
102
102
  function removeInvalidNodeErrorsInIdentifierOrLiteral(node) {
103
103
  const shouldCheckStrings = skipStrings && (typeof node.value === "string");
104
- const shouldCheckRegExps = skipRegExps && (node.value instanceof RegExp);
104
+ const shouldCheckRegExps = skipRegExps && Boolean(node.regex);
105
105
 
106
106
  if (shouldCheckStrings || shouldCheckRegExps) {
107
107
 
@@ -20,9 +20,9 @@
20
20
  * `null`.
21
21
  */
22
22
  function getContainingLoopNode(node) {
23
- let parent = node.parent;
23
+ for (let currentNode = node; currentNode.parent; currentNode = currentNode.parent) {
24
+ const parent = currentNode.parent;
24
25
 
25
- while (parent) {
26
26
  switch (parent.type) {
27
27
  case "WhileStatement":
28
28
  case "DoWhileStatement":
@@ -31,7 +31,7 @@ function getContainingLoopNode(node) {
31
31
  case "ForStatement":
32
32
 
33
33
  // `init` is outside of the loop.
34
- if (parent.init !== node) {
34
+ if (parent.init !== currentNode) {
35
35
  return parent;
36
36
  }
37
37
  break;
@@ -40,7 +40,7 @@ function getContainingLoopNode(node) {
40
40
  case "ForOfStatement":
41
41
 
42
42
  // `right` is outside of the loop.
43
- if (parent.right !== node) {
43
+ if (parent.right !== currentNode) {
44
44
  return parent;
45
45
  }
46
46
  break;
@@ -55,9 +55,6 @@ function getContainingLoopNode(node) {
55
55
  default:
56
56
  break;
57
57
  }
58
-
59
- node = parent;
60
- parent = node.parent;
61
58
  }
62
59
 
63
60
  return null;
@@ -73,12 +70,13 @@ function getContainingLoopNode(node) {
73
70
  * @returns {ASTNode} The most outer loop node.
74
71
  */
75
72
  function getTopLoopNode(node, excludedNode) {
76
- let retv = node;
77
73
  const border = excludedNode ? excludedNode.range[1] : 0;
74
+ let retv = node;
75
+ let containingLoopNode = node;
78
76
 
79
- while (node && node.range[0] >= border) {
80
- retv = node;
81
- node = getContainingLoopNode(node);
77
+ while (containingLoopNode && containingLoopNode.range[0] >= border) {
78
+ retv = containingLoopNode;
79
+ containingLoopNode = getContainingLoopNode(containingLoopNode);
82
80
  }
83
81
 
84
82
  return retv;
@@ -101,25 +101,32 @@ module.exports = {
101
101
 
102
102
  return {
103
103
  Literal(node) {
104
- let parent = node.parent,
105
- value = node.value,
106
- raw = node.raw;
107
104
  const okTypes = detectObjects ? [] : ["ObjectExpression", "Property", "AssignmentExpression"];
108
105
 
109
106
  if (!isNumber(node)) {
110
107
  return;
111
108
  }
112
109
 
110
+ let fullNumberNode;
111
+ let parent;
112
+ let value;
113
+ let raw;
114
+
113
115
  // For negative magic numbers: update the value and parent node
114
- if (parent.type === "UnaryExpression" && parent.operator === "-") {
115
- node = parent;
116
+ if (node.parent.type === "UnaryExpression" && node.parent.operator === "-") {
117
+ fullNumberNode = node.parent;
118
+ parent = fullNumberNode.parent;
119
+ value = -node.value;
120
+ raw = `-${node.raw}`;
121
+ } else {
122
+ fullNumberNode = node;
116
123
  parent = node.parent;
117
- value = -value;
118
- raw = `-${raw}`;
124
+ value = node.value;
125
+ raw = node.raw;
119
126
  }
120
127
 
121
128
  if (shouldIgnoreNumber(value) ||
122
- shouldIgnoreParseInt(parent, node) ||
129
+ shouldIgnoreParseInt(parent, fullNumberNode) ||
123
130
  shouldIgnoreArrayIndexes(parent) ||
124
131
  shouldIgnoreJSXNumbers(parent)) {
125
132
  return;
@@ -128,7 +135,7 @@ module.exports = {
128
135
  if (parent.type === "VariableDeclarator") {
129
136
  if (enforceConst && parent.parent.kind !== "const") {
130
137
  context.report({
131
- node,
138
+ node: fullNumberNode,
132
139
  message: "Number constants declarations must use 'const'."
133
140
  });
134
141
  }
@@ -137,7 +144,7 @@ module.exports = {
137
144
  (parent.type === "AssignmentExpression" && parent.left.type === "Identifier")
138
145
  ) {
139
146
  context.report({
140
- node,
147
+ node: fullNumberNode,
141
148
  message: "No magic number: {{raw}}.",
142
149
  data: {
143
150
  raw
@@ -46,11 +46,12 @@ module.exports = {
46
46
  return;
47
47
  }
48
48
 
49
- let parent = node.parent;
49
+ let currentChild = node;
50
+ let parent = currentChild.parent;
50
51
 
51
52
  // Find ReturnStatement or ArrowFunctionExpression in ancestors.
52
53
  while (parent && !SENTINEL_TYPE.test(parent.type)) {
53
- node = parent;
54
+ currentChild = parent;
54
55
  parent = parent.parent;
55
56
  }
56
57
 
@@ -60,7 +61,7 @@ module.exports = {
60
61
  node: parent,
61
62
  message: "Return statement should not contain assignment."
62
63
  });
63
- } else if (parent && parent.type === "ArrowFunctionExpression" && parent.body === node) {
64
+ } else if (parent && parent.type === "ArrowFunctionExpression" && parent.body === currentChild) {
64
65
  context.report({
65
66
  node: parent,
66
67
  message: "Arrow function should not return assignment."
@@ -33,7 +33,7 @@ module.exports = {
33
33
  const TAGGED_TEMPLATE_MESSAGE = "Unexpected newline between template tag and template literal.";
34
34
  const DIVISION_MESSAGE = "Unexpected newline between numerator and division operator.";
35
35
 
36
- const REGEX_FLAG_MATCHER = /^[gimuy]+$/;
36
+ const REGEX_FLAG_MATCHER = /^[gimsuy]+$/;
37
37
 
38
38
  const sourceCode = context.getSourceCode();
39
39
 
@@ -60,17 +60,20 @@ module.exports = {
60
60
  sentinelNodeType = SENTINEL_NODE_TYPE_RETURN_THROW;
61
61
  }
62
62
 
63
- while (node && !sentinelNodeType.test(node.type)) {
64
- if (node.parent.label && label && (node.parent.label.name === label.name)) {
63
+ for (
64
+ let currentNode = node;
65
+ currentNode && !sentinelNodeType.test(currentNode.type);
66
+ currentNode = currentNode.parent
67
+ ) {
68
+ if (currentNode.parent.label && label && (currentNode.parent.label.name === label.name)) {
65
69
  labelInside = true;
66
70
  }
67
- if (isFinallyBlock(node)) {
71
+ if (isFinallyBlock(currentNode)) {
68
72
  if (label && labelInside) {
69
73
  return false;
70
74
  }
71
75
  return true;
72
76
  }
73
- node = node.parent;
74
77
  }
75
78
  return false;
76
79
  }
@@ -25,8 +25,8 @@ function union(setA, setB) {
25
25
  }
26
26
 
27
27
  const VALID_STRING_ESCAPES = union(new Set("\\nrvtbfux"), astUtils.LINEBREAKS);
28
- const REGEX_GENERAL_ESCAPES = new Set("\\bcdDfnrsStvwWxu0123456789]");
29
- const REGEX_NON_CHARCLASS_ESCAPES = union(REGEX_GENERAL_ESCAPES, new Set("^/.$*+?[{}|()B"));
28
+ const REGEX_GENERAL_ESCAPES = new Set("\\bcdDfnpPrsStvwWxu0123456789]");
29
+ const REGEX_NON_CHARCLASS_ESCAPES = union(REGEX_GENERAL_ESCAPES, new Set("^/.$*+?[{}|()Bk"));
30
30
 
31
31
  /**
32
32
  * Parses a regular expression into a list of characters with character class info.
@@ -56,12 +56,14 @@ function isRemovable(node) {
56
56
  * @returns {boolean} `true` if the node is in a `finally` block.
57
57
  */
58
58
  function isInFinally(node) {
59
- while (node && node.parent && !astUtils.isFunction(node)) {
60
- if (node.parent.type === "TryStatement" && node.parent.finalizer === node) {
59
+ for (
60
+ let currentNode = node;
61
+ currentNode && currentNode.parent && !astUtils.isFunction(currentNode);
62
+ currentNode = currentNode.parent
63
+ ) {
64
+ if (currentNode.parent.type === "TryStatement" && currentNode.parent.finalizer === currentNode) {
61
65
  return true;
62
66
  }
63
-
64
- node = node.parent;
65
67
  }
66
68
 
67
69
  return false;
@@ -116,13 +118,12 @@ module.exports = {
116
118
  *
117
119
  * @param {ASTNode[]} uselessReturns - The collected return statements.
118
120
  * @param {CodePathSegment[]} prevSegments - The previous segments to traverse.
119
- * @param {WeakSet<CodePathSegment>} [traversedSegments] A set of segments that have already been traversed in this call
121
+ * @param {WeakSet<CodePathSegment>} [providedTraversedSegments] A set of segments that have already been traversed in this call
120
122
  * @returns {ASTNode[]} `uselessReturns`.
121
123
  */
122
- function getUselessReturns(uselessReturns, prevSegments, traversedSegments) {
123
- if (!traversedSegments) {
124
- traversedSegments = new WeakSet();
125
- }
124
+ function getUselessReturns(uselessReturns, prevSegments, providedTraversedSegments) {
125
+ const traversedSegments = providedTraversedSegments || new WeakSet();
126
+
126
127
  for (const segment of prevSegments) {
127
128
  if (!segment.reachable) {
128
129
  if (!traversedSegments.has(segment)) {
@@ -33,10 +33,12 @@ function isGlobal(variable) {
33
33
  * scope.
34
34
  */
35
35
  function getEnclosingFunctionScope(scope) {
36
- while (scope.type !== "function" && scope.type !== "global") {
37
- scope = scope.upper;
36
+ let currentScope = scope;
37
+
38
+ while (currentScope.type !== "function" && currentScope.type !== "global") {
39
+ currentScope = currentScope.upper;
38
40
  }
39
- return scope;
41
+ return currentScope;
40
42
  }
41
43
 
42
44
  /**
@@ -87,12 +89,10 @@ const SCOPE_NODE_TYPE = /^(?:Program|BlockStatement|SwitchStatement|ForStatement
87
89
  * `ForOfStatement`.
88
90
  */
89
91
  function getScopeNode(node) {
90
- while (node) {
91
- if (SCOPE_NODE_TYPE.test(node.type)) {
92
- return node;
92
+ for (let currentNode = node; currentNode; currentNode = currentNode.parent) {
93
+ if (SCOPE_NODE_TYPE.test(currentNode.type)) {
94
+ return currentNode;
93
95
  }
94
-
95
- node = node.parent;
96
96
  }
97
97
 
98
98
  /* istanbul ignore next : unreachable */
@@ -116,7 +116,8 @@ function areLineBreaksRequired(node, options, first, last) {
116
116
  } else {
117
117
 
118
118
  // is ImportDeclaration or ExportNamedDeclaration
119
- objectProperties = node.specifiers;
119
+ objectProperties = node.specifiers
120
+ .filter(s => s.type === "ImportSpecifier" || s.type === "ExportSpecifier");
120
121
  }
121
122
 
122
123
  return objectProperties.length >= options.minProperties ||