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
@@ -22,7 +22,7 @@ module.exports = {
22
22
  {
23
23
  oneOf: [
24
24
  {
25
- enum: ["always", "never"]
25
+ enum: ["always", "never", "consecutive"]
26
26
  },
27
27
  {
28
28
  type: "object",
@@ -31,13 +31,13 @@ module.exports = {
31
31
  type: "boolean"
32
32
  },
33
33
  var: {
34
- enum: ["always", "never"]
34
+ enum: ["always", "never", "consecutive"]
35
35
  },
36
36
  let: {
37
- enum: ["always", "never"]
37
+ enum: ["always", "never", "consecutive"]
38
38
  },
39
39
  const: {
40
- enum: ["always", "never"]
40
+ enum: ["always", "never", "consecutive"]
41
41
  }
42
42
  },
43
43
  additionalProperties: false
@@ -46,10 +46,10 @@ module.exports = {
46
46
  type: "object",
47
47
  properties: {
48
48
  initialized: {
49
- enum: ["always", "never"]
49
+ enum: ["always", "never", "consecutive"]
50
50
  },
51
51
  uninitialized: {
52
- enum: ["always", "never"]
52
+ enum: ["always", "never", "consecutive"]
53
53
  }
54
54
  },
55
55
  additionalProperties: false
@@ -60,10 +60,9 @@ module.exports = {
60
60
  },
61
61
 
62
62
  create(context) {
63
-
64
- const MODE_ALWAYS = "always",
65
- MODE_NEVER = "never";
66
-
63
+ const MODE_ALWAYS = "always";
64
+ const MODE_NEVER = "never";
65
+ const MODE_CONSECUTIVE = "consecutive";
67
66
  const mode = context.options[0] || MODE_ALWAYS;
68
67
 
69
68
  const options = {};
@@ -273,119 +272,163 @@ module.exports = {
273
272
  return true;
274
273
  }
275
274
 
275
+ /**
276
+ * Checks a given VariableDeclaration node for errors.
277
+ * @param {ASTNode} node The VariableDeclaration node to check
278
+ * @returns {void}
279
+ * @private
280
+ */
281
+ function checkVariableDeclaration(node) {
282
+ const parent = node.parent;
283
+ const type = node.kind;
276
284
 
277
- //--------------------------------------------------------------------------
278
- // Public API
279
- //--------------------------------------------------------------------------
285
+ if (!options[type]) {
286
+ return;
287
+ }
280
288
 
281
- return {
282
- Program: startFunction,
283
- FunctionDeclaration: startFunction,
284
- FunctionExpression: startFunction,
285
- ArrowFunctionExpression: startFunction,
286
- BlockStatement: startBlock,
287
- ForStatement: startBlock,
288
- ForInStatement: startBlock,
289
- ForOfStatement: startBlock,
290
- SwitchStatement: startBlock,
289
+ const declarations = node.declarations;
290
+ const declarationCounts = countDeclarations(declarations);
291
+ const mixedRequires = declarations.some(isRequire) && !declarations.every(isRequire);
292
+
293
+ if (options[type].initialized === MODE_ALWAYS) {
294
+ if (options.separateRequires && mixedRequires) {
295
+ context.report({
296
+ node,
297
+ message: "Split requires to be separated into a single block."
298
+ });
299
+ }
300
+ }
291
301
 
292
- VariableDeclaration(node) {
293
- const parent = node.parent;
294
- const type = node.kind;
302
+ // consecutive
303
+ const nodeIndex = (parent.body && parent.body.length > 0 && parent.body.indexOf(node)) || 0;
295
304
 
296
- if (!options[type]) {
297
- return;
298
- }
305
+ if (nodeIndex > 0) {
306
+ const previousNode = parent.body[nodeIndex - 1];
307
+ const isPreviousNodeDeclaration = previousNode.type === "VariableDeclaration";
299
308
 
300
- const declarations = node.declarations;
301
- const declarationCounts = countDeclarations(declarations);
302
- const mixedRequires = declarations.some(isRequire) && !declarations.every(isRequire);
309
+ if (isPreviousNodeDeclaration && previousNode.kind === type) {
310
+ const previousDeclCounts = countDeclarations(previousNode.declarations);
303
311
 
304
- if (options[type].initialized === MODE_ALWAYS) {
305
- if (options.separateRequires && mixedRequires) {
312
+ if (options[type].initialized === MODE_CONSECUTIVE && options[type].uninitialized === MODE_CONSECUTIVE) {
313
+ context.report({
314
+ node,
315
+ message: "Combine this with the previous '{{type}}' statement.",
316
+ data: {
317
+ type
318
+ }
319
+ });
320
+ } else if (options[type].initialized === MODE_CONSECUTIVE && declarationCounts.initialized > 0 && previousDeclCounts.initialized > 0) {
306
321
  context.report({
307
322
  node,
308
- message: "Split requires to be separated into a single block."
323
+ message: "Combine this with the previous '{{type}}' statement with initialized variables.",
324
+ data: {
325
+ type
326
+ }
327
+ });
328
+ } else if (options[type].uninitialized === MODE_CONSECUTIVE &&
329
+ declarationCounts.uninitialized > 0 &&
330
+ previousDeclCounts.uninitialized > 0) {
331
+ context.report({
332
+ node,
333
+ message: "Combine this with the previous '{{type}}' statement with uninitialized variables.",
334
+ data: {
335
+ type
336
+ }
309
337
  });
310
338
  }
311
339
  }
340
+ }
312
341
 
313
- // always
314
- if (!hasOnlyOneStatement(type, declarations)) {
315
- if (options[type].initialized === MODE_ALWAYS && options[type].uninitialized === MODE_ALWAYS) {
342
+ // always
343
+ if (!hasOnlyOneStatement(type, declarations)) {
344
+ if (options[type].initialized === MODE_ALWAYS && options[type].uninitialized === MODE_ALWAYS) {
345
+ context.report({
346
+ node,
347
+ message: "Combine this with the previous '{{type}}' statement.",
348
+ data: {
349
+ type
350
+ }
351
+ });
352
+ } else {
353
+ if (options[type].initialized === MODE_ALWAYS && declarationCounts.initialized > 0) {
316
354
  context.report({
317
355
  node,
318
- message: "Combine this with the previous '{{type}}' statement.",
356
+ message: "Combine this with the previous '{{type}}' statement with initialized variables.",
319
357
  data: {
320
358
  type
321
359
  }
322
360
  });
323
- } else {
324
- if (options[type].initialized === MODE_ALWAYS) {
325
- context.report({
326
- node,
327
- message: "Combine this with the previous '{{type}}' statement with initialized variables.",
328
- data: {
329
- type
330
- }
331
- });
361
+ }
362
+ if (options[type].uninitialized === MODE_ALWAYS && declarationCounts.uninitialized > 0) {
363
+ if (node.parent.left === node && (node.parent.type === "ForInStatement" || node.parent.type === "ForOfStatement")) {
364
+ return;
332
365
  }
333
- if (options[type].uninitialized === MODE_ALWAYS) {
334
- if (node.parent.left === node && (node.parent.type === "ForInStatement" || node.parent.type === "ForOfStatement")) {
335
- return;
366
+ context.report({
367
+ node,
368
+ message: "Combine this with the previous '{{type}}' statement with uninitialized variables.",
369
+ data: {
370
+ type
336
371
  }
337
- context.report({
338
- node,
339
- message: "Combine this with the previous '{{type}}' statement with uninitialized variables.",
340
- data: {
341
- type
342
- }
343
- });
344
- }
372
+ });
345
373
  }
346
374
  }
375
+ }
347
376
 
348
- // never
349
- if (parent.type !== "ForStatement" || parent.init !== node) {
350
- const totalDeclarations = declarationCounts.uninitialized + declarationCounts.initialized;
351
-
352
- if (totalDeclarations > 1) {
353
-
354
- if (options[type].initialized === MODE_NEVER && options[type].uninitialized === MODE_NEVER) {
355
-
356
- // both initialized and uninitialized
357
- context.report({
358
- node,
359
- message: "Split '{{type}}' declarations into multiple statements.",
360
- data: {
361
- type
362
- }
363
- });
364
- } else if (options[type].initialized === MODE_NEVER && declarationCounts.initialized > 0) {
365
-
366
- // initialized
367
- context.report({
368
- node,
369
- message: "Split initialized '{{type}}' declarations into multiple statements.",
370
- data: {
371
- type
372
- }
373
- });
374
- } else if (options[type].uninitialized === MODE_NEVER && declarationCounts.uninitialized > 0) {
375
-
376
- // uninitialized
377
- context.report({
378
- node,
379
- message: "Split uninitialized '{{type}}' declarations into multiple statements.",
380
- data: {
381
- type
382
- }
383
- });
384
- }
377
+ // never
378
+ if (parent.type !== "ForStatement" || parent.init !== node) {
379
+ const totalDeclarations = declarationCounts.uninitialized + declarationCounts.initialized;
380
+
381
+ if (totalDeclarations > 1) {
382
+ if (options[type].initialized === MODE_NEVER && options[type].uninitialized === MODE_NEVER) {
383
+
384
+ // both initialized and uninitialized
385
+ context.report({
386
+ node,
387
+ message: "Split '{{type}}' declarations into multiple statements.",
388
+ data: {
389
+ type
390
+ }
391
+ });
392
+ } else if (options[type].initialized === MODE_NEVER && declarationCounts.initialized > 0) {
393
+
394
+ // initialized
395
+ context.report({
396
+ node,
397
+ message: "Split initialized '{{type}}' declarations into multiple statements.",
398
+ data: {
399
+ type
400
+ }
401
+ });
402
+ } else if (options[type].uninitialized === MODE_NEVER && declarationCounts.uninitialized > 0) {
403
+
404
+ // uninitialized
405
+ context.report({
406
+ node,
407
+ message: "Split uninitialized '{{type}}' declarations into multiple statements.",
408
+ data: {
409
+ type
410
+ }
411
+ });
385
412
  }
386
413
  }
387
- },
414
+ }
415
+ }
416
+
417
+ //--------------------------------------------------------------------------
418
+ // Public API
419
+ //--------------------------------------------------------------------------
388
420
 
421
+ return {
422
+ Program: startFunction,
423
+ FunctionDeclaration: startFunction,
424
+ FunctionExpression: startFunction,
425
+ ArrowFunctionExpression: startFunction,
426
+ BlockStatement: startBlock,
427
+ ForStatement: startBlock,
428
+ ForInStatement: startBlock,
429
+ ForOfStatement: startBlock,
430
+ SwitchStatement: startBlock,
431
+ VariableDeclaration: checkVariableDeclaration,
389
432
  "ForStatement:exit": endBlock,
390
433
  "ForOfStatement:exit": endBlock,
391
434
  "ForInStatement:exit": endBlock,
@@ -473,13 +473,15 @@ module.exports = {
473
473
  * @private
474
474
  */
475
475
  function match(node, type) {
476
- while (node.type === "LabeledStatement") {
477
- node = node.body;
476
+ let innerStatementNode = node;
477
+
478
+ while (innerStatementNode.type === "LabeledStatement") {
479
+ innerStatementNode = innerStatementNode.body;
478
480
  }
479
481
  if (Array.isArray(type)) {
480
- return type.some(match.bind(null, node));
482
+ return type.some(match.bind(null, innerStatementNode));
481
483
  }
482
- return StatementTypes[type].test(node, sourceCode);
484
+ return StatementTypes[type].test(innerStatementNode, sourceCode);
483
485
  }
484
486
 
485
487
  /**
@@ -64,9 +64,10 @@ function getVariableOfArguments(scope) {
64
64
  */
65
65
  function getCallbackInfo(node) {
66
66
  const retv = { isCallback: false, isLexicalThis: false };
67
+ let currentNode = node;
67
68
  let parent = node.parent;
68
69
 
69
- while (node) {
70
+ while (currentNode) {
70
71
  switch (parent.type) {
71
72
 
72
73
  // Checks parents recursively.
@@ -77,7 +78,7 @@ function getCallbackInfo(node) {
77
78
 
78
79
  // Checks whether the parent node is `.bind(this)` call.
79
80
  case "MemberExpression":
80
- if (parent.object === node &&
81
+ if (parent.object === currentNode &&
81
82
  !parent.property.computed &&
82
83
  parent.property.type === "Identifier" &&
83
84
  parent.property.name === "bind" &&
@@ -97,7 +98,7 @@ function getCallbackInfo(node) {
97
98
  // Checks whether the node is a callback.
98
99
  case "CallExpression":
99
100
  case "NewExpression":
100
- if (parent.callee !== node) {
101
+ if (parent.callee !== currentNode) {
101
102
  retv.isCallback = true;
102
103
  }
103
104
  return retv;
@@ -106,7 +107,7 @@ function getCallbackInfo(node) {
106
107
  return retv;
107
108
  }
108
109
 
109
- node = parent;
110
+ currentNode = parent;
110
111
  parent = parent.parent;
111
112
  }
112
113
 
@@ -30,10 +30,12 @@ function isConcatenation(node) {
30
30
  * @returns {ASTNode} the top binary expression node in parents of a given node.
31
31
  */
32
32
  function getTopConcatBinaryExpression(node) {
33
- while (isConcatenation(node.parent)) {
34
- node = node.parent;
33
+ let currentNode = node;
34
+
35
+ while (isConcatenation(currentNode.parent)) {
36
+ currentNode = currentNode.parent;
35
37
  }
36
- return node;
38
+ return currentNode;
37
39
  }
38
40
 
39
41
  /**
@@ -141,8 +141,6 @@ module.exports = {
141
141
  * @returns {void}
142
142
  */
143
143
  function checkUnaryWordOperatorForSpaces(node, firstToken, secondToken, word) {
144
- word = word || firstToken.value;
145
-
146
144
  if (overrideExistsForOperator(word)) {
147
145
  if (overrideEnforcesSpaces(word)) {
148
146
  verifyWordHasSpaces(node, firstToken, secondToken, word);
@@ -276,7 +274,7 @@ module.exports = {
276
274
  const secondToken = tokens[1];
277
275
 
278
276
  if ((node.type === "NewExpression" || node.prefix) && firstToken.type === "Keyword") {
279
- checkUnaryWordOperatorForSpaces(node, firstToken, secondToken);
277
+ checkUnaryWordOperatorForSpaces(node, firstToken, secondToken, firstToken.value);
280
278
  return;
281
279
  }
282
280
 
@@ -17,10 +17,7 @@ const astUtils = require("../ast-utils");
17
17
  * @returns {string} An escaped string.
18
18
  */
19
19
  function escape(s) {
20
- const isOneChar = s.length === 1;
21
-
22
- s = lodash.escapeRegExp(s);
23
- return isOneChar ? s : `(?:${s})`;
20
+ return `(?:${lodash.escapeRegExp(s)})`;
24
21
  }
25
22
 
26
23
  /**
@@ -40,11 +37,10 @@ function escapeAndRepeat(s) {
40
37
  * @returns {string[]} A marker list.
41
38
  */
42
39
  function parseMarkersOption(markers) {
43
- markers = markers ? markers.slice(0) : [];
44
40
 
45
41
  // `*` is a marker for JSDoc comments.
46
42
  if (markers.indexOf("*") === -1) {
47
- markers.push("*");
43
+ return markers.concat("*");
48
44
  }
49
45
 
50
46
  return markers;
@@ -244,7 +240,7 @@ module.exports = {
244
240
  const balanced = config.block && config.block.balanced;
245
241
 
246
242
  const styleRules = ["block", "line"].reduce((rule, type) => {
247
- const markers = parseMarkersOption(config[type] && config[type].markers || config.markers);
243
+ const markers = parseMarkersOption(config[type] && config[type].markers || config.markers || []);
248
244
  const exceptions = config[type] && config[type].exceptions || config.exceptions || [];
249
245
  const endNeverPattern = "[ \t]+$";
250
246
 
File without changes
@@ -420,14 +420,14 @@ module.exports = {
420
420
 
421
421
  if (node.params) {
422
422
  node.params.forEach((param, paramsIndex) => {
423
- if (param.type === "AssignmentPattern") {
424
- param = param.left;
425
- }
426
-
427
- const name = param.name;
423
+ const bindingParam = param.type === "AssignmentPattern"
424
+ ? param.left
425
+ : param;
428
426
 
429
427
  // TODO(nzakas): Figure out logical things to do with destructured, default, rest params
430
- if (param.type === "Identifier") {
428
+ if (bindingParam.type === "Identifier") {
429
+ const name = bindingParam.name;
430
+
431
431
  if (jsdocParamNames[paramsIndex] && (name !== jsdocParamNames[paramsIndex])) {
432
432
  context.report({
433
433
  node: jsdocNode,
@@ -125,23 +125,13 @@ module.exports = {
125
125
  //--------------------------------------------------------------------------
126
126
 
127
127
  return {
128
- VariableDeclaration(node) {
129
- const ancestors = context.getAncestors();
130
- let parent = ancestors.pop();
131
- let grandParent = ancestors.pop();
132
-
133
- if (node.kind === "var") { // check variable is `var` type and not `let` or `const`
134
- if (parent.type === "ExportNamedDeclaration") {
135
- node = parent;
136
- parent = grandParent;
137
- grandParent = ancestors.pop();
138
- }
139
-
140
- if (parent.type === "Program") { // That means its a global variable
141
- globalVarCheck(node, parent);
142
- } else {
143
- blockScopeVarCheck(node, parent, grandParent);
144
- }
128
+ "VariableDeclaration[kind='var']"(node) {
129
+ if (node.parent.type === "ExportNamedDeclaration") {
130
+ globalVarCheck(node.parent, node.parent.parent);
131
+ } else if (node.parent.type === "Program") {
132
+ globalVarCheck(node, node.parent);
133
+ } else {
134
+ blockScopeVarCheck(node, node.parent, node.parent.parent);
145
135
  }
146
136
  }
147
137
  };
package/lib/timing.js CHANGED
@@ -90,12 +90,10 @@ function display(data) {
90
90
  .join(" | ")
91
91
  ));
92
92
 
93
- table.splice(1, 0, widths.map((w, index) => {
94
- if (index !== 0 && index !== widths.length - 1) {
95
- w++;
96
- }
93
+ table.splice(1, 0, widths.map((width, index) => {
94
+ const extraAlignment = index !== 0 && index !== widths.length - 1 ? 2 : 1;
97
95
 
98
- return ALIGN[index](":", w + 1, "-");
96
+ return ALIGN[index](":", width + extraAlignment, "-");
99
97
  }).join("|"));
100
98
 
101
99
  console.log(table.join("\n")); // eslint-disable-line no-console
@@ -95,20 +95,20 @@ const dotfilesPattern = /(?:(?:^\.)|(?:[/\\]\.))[^/\\.].*/;
95
95
  * Build a list of absolute filesnames on which ESLint will act.
96
96
  * Ignored files are excluded from the results, as are duplicates.
97
97
  *
98
- * @param {string[]} globPatterns Glob patterns.
99
- * @param {Object} [options] An options object.
100
- * @param {string} [options.cwd] CWD (considered for relative filenames)
101
- * @param {boolean} [options.ignore] False disables use of .eslintignore.
102
- * @param {string} [options.ignorePath] The ignore file to use instead of .eslintignore.
103
- * @param {string} [options.ignorePattern] A pattern of files to ignore.
98
+ * @param {string[]} globPatterns Glob patterns.
99
+ * @param {Object} [providedOptions] An options object.
100
+ * @param {string} [providedOptions.cwd] CWD (considered for relative filenames)
101
+ * @param {boolean} [providedOptions.ignore] False disables use of .eslintignore.
102
+ * @param {string} [providedOptions.ignorePath] The ignore file to use instead of .eslintignore.
103
+ * @param {string} [providedOptions.ignorePattern] A pattern of files to ignore.
104
104
  * @returns {string[]} Resolved absolute filenames.
105
105
  */
106
- function listFilesToProcess(globPatterns, options) {
107
- options = options || { ignore: true };
108
- const files = [],
109
- added = {};
106
+ function listFilesToProcess(globPatterns, providedOptions) {
107
+ const options = providedOptions || { ignore: true };
108
+ const files = [];
109
+ const added = {};
110
110
 
111
- const cwd = (options && options.cwd) || process.cwd();
111
+ const cwd = options.cwd || process.cwd();
112
112
 
113
113
  const getIgnorePaths = lodash.memoize(
114
114
  optionsObj =>
@@ -13,7 +13,11 @@ module.exports = (text, data) => {
13
13
  if (!data) {
14
14
  return text;
15
15
  }
16
- return text.replace(/\{\{\s*([^{}]+?)\s*\}\}/g, (fullMatch, term) => {
16
+
17
+ // Substitution content for any {{ }} markers.
18
+ return text.replace(/\{\{([^{}]+?)\}\}/g, (fullMatch, termWithWhitespace) => {
19
+ const term = termWithWhitespace.trim();
20
+
17
21
  if (term in data) {
18
22
  return data[term];
19
23
  }
@@ -23,17 +23,18 @@ const NAMESPACE_REGEX = /^@.*\//i;
23
23
  * @private
24
24
  */
25
25
  function normalizePackageName(name, prefix) {
26
+ let normalizedName = name;
26
27
 
27
28
  /**
28
29
  * On Windows, name can come in with Windows slashes instead of Unix slashes.
29
30
  * Normalize to Unix first to avoid errors later on.
30
31
  * https://github.com/eslint/eslint/issues/5644
31
32
  */
32
- if (name.indexOf("\\") > -1) {
33
- name = pathUtil.convertPathToPosix(name);
33
+ if (normalizedName.indexOf("\\") > -1) {
34
+ normalizedName = pathUtil.convertPathToPosix(normalizedName);
34
35
  }
35
36
 
36
- if (name.charAt(0) === "@") {
37
+ if (normalizedName.charAt(0) === "@") {
37
38
 
38
39
  /**
39
40
  * it's a scoped package
@@ -42,21 +43,21 @@ function normalizePackageName(name, prefix) {
42
43
  const scopedPackageShortcutRegex = new RegExp(`^(@[^/]+)(?:/(?:${prefix})?)?$`),
43
44
  scopedPackageNameRegex = new RegExp(`^${prefix}(-|$)`);
44
45
 
45
- if (scopedPackageShortcutRegex.test(name)) {
46
- name = name.replace(scopedPackageShortcutRegex, `$1/${prefix}`);
47
- } else if (!scopedPackageNameRegex.test(name.split("/")[1])) {
46
+ if (scopedPackageShortcutRegex.test(normalizedName)) {
47
+ normalizedName = normalizedName.replace(scopedPackageShortcutRegex, `$1/${prefix}`);
48
+ } else if (!scopedPackageNameRegex.test(normalizedName.split("/")[1])) {
48
49
 
49
50
  /**
50
51
  * for scoped packages, insert the prefix after the first / unless
51
52
  * the path is already @scope/eslint or @scope/eslint-xxx-yyy
52
53
  */
53
- name = name.replace(/^@([^/]+)\/(.*)$/, `@$1/${prefix}-$2`);
54
+ normalizedName = normalizedName.replace(/^@([^/]+)\/(.*)$/, `@$1/${prefix}-$2`);
54
55
  }
55
- } else if (name.indexOf(`${prefix}-`) !== 0) {
56
- name = `${prefix}-${name}`;
56
+ } else if (normalizedName.indexOf(`${prefix}-`) !== 0) {
57
+ normalizedName = `${prefix}-${normalizedName}`;
57
58
  }
58
59
 
59
- return name;
60
+ return normalizedName;
60
61
  }
61
62
 
62
63
  /**
@@ -50,17 +50,15 @@ function findPackageJson(startDir) {
50
50
  * @returns {void}
51
51
  */
52
52
  function installSyncSaveDev(packages) {
53
- if (!Array.isArray(packages)) {
54
- packages = [packages];
55
- }
56
- const npmProcess = spawn.sync("npm", ["i", "--save-dev"].concat(packages),
53
+ const packageList = Array.isArray(packages) ? packages : [packages];
54
+ const npmProcess = spawn.sync("npm", ["i", "--save-dev"].concat(packageList),
57
55
  { stdio: "inherit" });
58
56
  const error = npmProcess.error;
59
57
 
60
58
  if (error && error.code === "ENOENT") {
61
- const pluralS = packages.length > 1 ? "s" : "";
59
+ const pluralS = packageList.length > 1 ? "s" : "";
62
60
 
63
- log.error(`Could not execute npm. Please install the following package${pluralS} with a package manager of your choice: ${packages.join(", ")}`);
61
+ log.error(`Could not execute npm. Please install the following package${pluralS} with a package manager of your choice: ${packageList.join(", ")}`);
64
62
  }
65
63
  }
66
64