eslint 3.17.1 → 3.18.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.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,29 @@
1
+ v3.18.0 - March 17, 2017
2
+
3
+ * 85f74ca Fix: broken code path of direct nested loops (fixes #8248) (#8274) (Toru Nagashima)
4
+ * a61c359 Fix: Ignore hidden folders when resolving globs (fixes #8259) (#8270) (Ian VanSchooten)
5
+ * 6f05546 Chore: convert StubModuleResolver in config tests to ES6 class (#8265) (Teddy Katz)
6
+ * 0c0fc31 Fix: false positive of no-extra-parens about spread and sequense (#8275) (Toru Nagashima)
7
+ * e104973 Docs: remove self-reference in no-restricted-syntax docs (#8277) (Vitor Balocco)
8
+ * 23eca51 Update: Add allowTaggedTemplates to no-unused-expressions (fixes #7632) (#8253) (Kevin Partington)
9
+ * f9ede3f Upgrade: doctrine to 2.0.0 (#8269) (alberto)
10
+ * 1b678a6 New: allow rules to listen for AST selectors (fixes #5407) (#7833) (Teddy Katz)
11
+ * 63ca0c5 Chore: use precalculated counts in stylish formatter (#8251) (alberto)
12
+ * 47c3171 Fix: typo in console.error (#8258) (Jan Peer Stöcklmair)
13
+ * e74ed6d Chore: convert Traverser to ES6 class (refs #7849) (#8232) (Teddy Katz)
14
+ * 13eead9 Fix: sort-vars crash on mixed destructuring declarations (#8245) (Teddy Katz)
15
+ * 133f489 Fix: func-name-matching crash on destructuring assignment to functions (#8247) (Teddy Katz)
16
+ * a34b9c4 Fix: func-name-matching crash on non-string literal computed keys (#8246) (Teddy Katz)
17
+ * 7276e6d Docs: remove unneeded semicolons in arrow-parens.md (#8249) (Dmitry Gershun)
18
+ * 8c40a25 concat-stream known to be vulnerable prior 1.5.2 (#8228) (Samuel)
19
+ * 149c055 Upgrade: mock-fs to v4.2.0 (fixes #8194) (#8243) (Teddy Katz)
20
+ * a83bff9 Build: remove unneeded json config in demo (fixes #8237) (#8242) (alberto)
21
+ * df12137 Docs: fix typos (#8235) (Gyandeep Singh)
22
+ * b5e9788 Chore: rename no-extra-parens methods (#8225) (Vitor Balocco)
23
+ * 7f8afe6 Update: no-extra-parens overlooked spread and superClass (fixes #8175) (#8209) (Toru Nagashima)
24
+ * ce6ff56 Docs: set recommended true for no-global-assign (fixes #8215) (#8218) (BinYi LIU)
25
+ * 5b5c236 Fix: wrong comment when module not found in config (fixes #8192) (#8196) (alberto)
26
+
1
27
  v3.17.1 - March 6, 2017
2
28
 
3
29
  * f8c8e6e Build: change mock-fs path without SSH (fixes #8207) (#8208) (Toru Nagashima)
@@ -512,13 +512,8 @@ function processCodePathToExit(analyzer, node) {
512
512
  break;
513
513
  }
514
514
 
515
- /*
516
- * Skip updating the current segment to avoid creating useless segments if
517
- * the node type is the same as the parent node type.
518
- */
519
- if (!dontForward && (!node.parent || node.type !== node.parent.type)) {
520
-
521
- // Emits onCodePathSegmentStart events if updated.
515
+ // Emits onCodePathSegmentStart events if updated.
516
+ if (!dontForward) {
522
517
  forwardCurrentToHead(analyzer, node);
523
518
  }
524
519
  debug.dumpState(node, state, true);
@@ -107,22 +107,23 @@ module.exports = {
107
107
  text += "style=\"rounded,dashed,filled\",fillcolor=\"#FF9800\",label=\"<<unreachable>>\\n";
108
108
  }
109
109
 
110
- if (segment.internal.nodes.length > 0) {
111
- text += segment.internal.nodes.map(node => {
112
- switch (node.type) {
113
- case "Identifier": return `${node.type} (${node.name})`;
114
- case "Literal": return `${node.type} (${node.value})`;
115
- default: return node.type;
116
- }
117
- }).join("\\n");
118
- } else if (segment.internal.exitNodes.length > 0) {
119
- text += segment.internal.exitNodes.map(node => {
120
- switch (node.type) {
121
- case "Identifier": return `${node.type}:exit (${node.name})`;
122
- case "Literal": return `${node.type}:exit (${node.value})`;
123
- default: return `${node.type}:exit`;
124
- }
125
- }).join("\\n");
110
+ if (segment.internal.nodes.length > 0 || segment.internal.exitNodes.length > 0) {
111
+ text += [].concat(
112
+ segment.internal.nodes.map(node => {
113
+ switch (node.type) {
114
+ case "Identifier": return `${node.type} (${node.name})`;
115
+ case "Literal": return `${node.type} (${node.value})`;
116
+ default: return node.type;
117
+ }
118
+ }),
119
+ segment.internal.exitNodes.map(node => {
120
+ switch (node.type) {
121
+ case "Identifier": return `${node.type}:exit (${node.name})`;
122
+ case "Literal": return `${node.type}:exit (${node.value})`;
123
+ default: return `${node.type}:exit`;
124
+ }
125
+ })
126
+ ).join("\\n");
126
127
  } else {
127
128
  text += "????";
128
129
  }
@@ -183,6 +183,22 @@ function loadPackageJSONConfigFile(filePath) {
183
183
  }
184
184
  }
185
185
 
186
+ /**
187
+ * Creates an error to notify about a missing config to extend from.
188
+ * @param {string} configName The name of the missing config.
189
+ * @returns {Error} The error object to throw
190
+ * @private
191
+ */
192
+ function configMissingError(configName) {
193
+ const error = new Error(`Failed to load config "${configName}" to extend from.`);
194
+
195
+ error.messageTemplate = "extend-config-missing";
196
+ error.messageData = {
197
+ configName
198
+ };
199
+ return error;
200
+ }
201
+
186
202
  /**
187
203
  * Loads a configuration file regardless of the source. Inspects the file path
188
204
  * to determine the correctly way to load the config file.
@@ -199,6 +215,9 @@ function loadConfigFile(file) {
199
215
  config = loadJSConfigFile(filePath);
200
216
  if (file.configName) {
201
217
  config = config.configs[file.configName];
218
+ if (!config) {
219
+ throw configMissingError(file.configFullName);
220
+ }
202
221
  }
203
222
  break;
204
223
 
@@ -340,6 +359,33 @@ function getLookupPath(configFilePath) {
340
359
  return path.join(basedir, "node_modules");
341
360
  }
342
361
 
362
+ /**
363
+ * Resolves a eslint core config path
364
+ * @param {string} name The eslint config name.
365
+ * @returns {string} The resolved path of the config.
366
+ * @private
367
+ */
368
+ function getEslintCoreConfigPath(name) {
369
+ if (name === "eslint:recommended") {
370
+
371
+ /*
372
+ * Add an explicit substitution for eslint:recommended to
373
+ * conf/eslint-recommended.js.
374
+ */
375
+ return path.resolve(__dirname, "../../conf/eslint-recommended.js");
376
+ }
377
+
378
+ if (name === "eslint:all") {
379
+
380
+ /*
381
+ * Add an explicit substitution for eslint:all to conf/eslint-all.js
382
+ */
383
+ return path.resolve(__dirname, "../../conf/eslint-all.js");
384
+ }
385
+
386
+ throw configMissingError(name);
387
+ }
388
+
343
389
  /**
344
390
  * Applies values from the "extends" field in a configuration file.
345
391
  * @param {Object} config The configuration information.
@@ -360,43 +406,23 @@ function applyExtends(config, filePath, relativeTo) {
360
406
 
361
407
  // Make the last element in an array take the highest precedence
362
408
  config = configExtends.reduceRight((previousValue, parentPath) => {
363
-
364
- if (parentPath === "eslint:recommended") {
365
-
366
- /*
367
- * Add an explicit substitution for eslint:recommended to
368
- * conf/eslint-recommended.js.
369
- */
370
- parentPath = path.resolve(__dirname, "../../conf/eslint-recommended.js");
371
- } else if (parentPath === "eslint:all") {
372
-
373
- /*
374
- * Add an explicit substitution for eslint:all to conf/eslint-all.js
375
- */
376
- parentPath = path.resolve(__dirname, "../../conf/eslint-all.js");
377
- } else if (isFilePath(parentPath)) {
378
-
379
- /*
380
- * If the `extends` path is relative, use the directory of the current configuration
381
- * file as the reference point. Otherwise, use as-is.
382
- */
383
- parentPath = (!path.isAbsolute(parentPath)
384
- ? path.join(relativeTo || path.dirname(filePath), parentPath)
385
- : parentPath
386
- );
387
- }
388
-
389
409
  try {
410
+ if (parentPath.startsWith("eslint:")) {
411
+ parentPath = getEslintCoreConfigPath(parentPath);
412
+ } else if (isFilePath(parentPath)) {
413
+
414
+ /*
415
+ * If the `extends` path is relative, use the directory of the current configuration
416
+ * file as the reference point. Otherwise, use as-is.
417
+ */
418
+ parentPath = (path.isAbsolute(parentPath)
419
+ ? parentPath
420
+ : path.join(relativeTo || path.dirname(filePath), parentPath)
421
+ );
422
+ }
390
423
  debug(`Loading ${parentPath}`);
391
424
  return ConfigOps.merge(load(parentPath, false, relativeTo), previousValue);
392
425
  } catch (e) {
393
- if (parentPath.indexOf("plugin:") === 0 || parentPath.indexOf("eslint:") === 0) {
394
- e.message = `Failed to load config "${parentPath}" to extend from.`;
395
- e.messageTemplate = "extend-config-missing";
396
- e.messageData = {
397
- configName: parentPath
398
- };
399
- }
400
426
 
401
427
  /*
402
428
  * If the file referenced by `extends` failed to load, add the path
@@ -462,7 +488,10 @@ function normalizePackageName(name, prefix) {
462
488
  * or package name.
463
489
  * @param {string} filePath The filepath to resolve.
464
490
  * @param {string} [relativeTo] The path to resolve relative to.
465
- * @returns {Object} A path that can be used directly to load the configuration.
491
+ * @returns {Object} An object containing 3 properties:
492
+ * - 'filePath' (required) the resolved path that can be used directly to load the configuration.
493
+ * - 'configName' the name of the configuration inside the plugin.
494
+ * - 'configFullName' the name of the configuration as used in the eslint config (e.g. 'plugin:node/recommended').
466
495
  * @private
467
496
  */
468
497
  function resolve(filePath, relativeTo) {
@@ -471,14 +500,15 @@ function resolve(filePath, relativeTo) {
471
500
  }
472
501
  let normalizedPackageName;
473
502
 
474
- if (filePath.indexOf("plugin:") === 0) {
475
- const packagePath = filePath.substr(7, filePath.lastIndexOf("/") - 7);
503
+ if (filePath.startsWith("plugin:")) {
504
+ const configFullName = filePath;
505
+ const pluginName = filePath.substr(7, filePath.lastIndexOf("/") - 7);
476
506
  const configName = filePath.substr(filePath.lastIndexOf("/") + 1, filePath.length - filePath.lastIndexOf("/") - 1);
477
507
 
478
- normalizedPackageName = normalizePackageName(packagePath, "eslint-plugin");
508
+ normalizedPackageName = normalizePackageName(pluginName, "eslint-plugin");
479
509
  debug(`Attempting to resolve ${normalizedPackageName}`);
480
510
  filePath = resolver.resolve(normalizedPackageName, getLookupPath(relativeTo));
481
- return { filePath, configName };
511
+ return { filePath, configName, configFullName };
482
512
  }
483
513
  normalizedPackageName = normalizePackageName(filePath, "eslint-config");
484
514
  debug(`Attempting to resolve ${normalizedPackageName}`);
package/lib/eslint.js CHANGED
@@ -867,11 +867,11 @@ module.exports = (function() {
867
867
  const rule = ruleCreator.create ? ruleCreator.create(ruleContext)
868
868
  : ruleCreator(ruleContext);
869
869
 
870
- // add all the node types as listeners
871
- Object.keys(rule).forEach(nodeType => {
872
- api.on(nodeType, timing.enabled
873
- ? timing.time(key, rule[nodeType])
874
- : rule[nodeType]
870
+ // add all the selectors from the rule as listeners
871
+ Object.keys(rule).forEach(selector => {
872
+ api.on(selector, timing.enabled
873
+ ? timing.time(key, rule[selector])
874
+ : rule[selector]
875
875
  );
876
876
  });
877
877
  } catch (ex) {
@@ -28,7 +28,6 @@ function pluralize(word, count) {
28
28
  module.exports = function(results) {
29
29
 
30
30
  let output = "\n",
31
- total = 0,
32
31
  errors = 0,
33
32
  warnings = 0,
34
33
  summaryColor = "yellow";
@@ -40,7 +39,9 @@ module.exports = function(results) {
40
39
  return;
41
40
  }
42
41
 
43
- total += messages.length;
42
+ errors += result.errorCount;
43
+ warnings += result.warningCount;
44
+
44
45
  output += `${chalk.underline(result.filePath)}\n`;
45
46
 
46
47
  output += `${table(
@@ -50,10 +51,8 @@ module.exports = function(results) {
50
51
  if (message.fatal || message.severity === 2) {
51
52
  messageType = chalk.red("error");
52
53
  summaryColor = "red";
53
- errors++;
54
54
  } else {
55
55
  messageType = chalk.yellow("warning");
56
- warnings++;
57
56
  }
58
57
 
59
58
  return [
@@ -74,6 +73,8 @@ module.exports = function(results) {
74
73
  ).split("\n").map(el => el.replace(/(\d+)\s+(\d+)/, (m, p1, p2) => chalk.dim(`${p1}:${p2}`))).join("\n")}\n\n`;
75
74
  });
76
75
 
76
+ const total = errors + warnings;
77
+
77
78
  if (total > 0) {
78
79
  output += chalk[summaryColor].bold([
79
80
  "\u2716 ", total, pluralize(" problem", total),
@@ -201,6 +201,12 @@ class IgnoredPaths {
201
201
 
202
202
  const ig = ignore().add(DEFAULT_IGNORE_DIRS);
203
203
 
204
+ if (this.options.dotfiles !== true) {
205
+
206
+ // Ignore hidden folders. (This cannot be ".*", or else it's not possible to unignore hidden files)
207
+ ig.add([".*/*", "!../"]);
208
+ }
209
+
204
210
  if (this.options.ignore) {
205
211
  ig.add(this.ig.custom);
206
212
  }
@@ -132,6 +132,15 @@ module.exports = {
132
132
  });
133
133
  }
134
134
 
135
+ /**
136
+ * Determines whether a given node is a string literal
137
+ * @param {ASTNode} node The node to check
138
+ * @returns {boolean} `true` if the node is a string literal
139
+ */
140
+ function isStringLiteral(node) {
141
+ return node.type === "Literal" && typeof node.value === "string";
142
+ }
143
+
135
144
  //--------------------------------------------------------------------------
136
145
  // Public
137
146
  //--------------------------------------------------------------------------
@@ -139,7 +148,7 @@ module.exports = {
139
148
  return {
140
149
 
141
150
  VariableDeclarator(node) {
142
- if (!node.init || node.init.type !== "FunctionExpression") {
151
+ if (!node.init || node.init.type !== "FunctionExpression" || node.id.type !== "Identifier") {
143
152
  return;
144
153
  }
145
154
  if (node.init.id && shouldWarn(node.id.name, node.init.id.name)) {
@@ -148,10 +157,12 @@ module.exports = {
148
157
  },
149
158
 
150
159
  AssignmentExpression(node) {
151
- if (node.right.type !== "FunctionExpression" ||
152
- (node.left.computed && node.left.property.type !== "Literal") ||
153
- (!includeModuleExports && isModuleExports(node.left))
154
- ) {
160
+ if (
161
+ node.right.type !== "FunctionExpression" ||
162
+ (node.left.computed && node.left.property.type !== "Literal") ||
163
+ (!includeModuleExports && isModuleExports(node.left)) ||
164
+ (node.left.type !== "Identifier" && node.left.type !== "MemberExpression")
165
+ ) {
155
166
  return;
156
167
  }
157
168
 
@@ -164,13 +175,13 @@ module.exports = {
164
175
  },
165
176
 
166
177
  Property(node) {
167
- if (node.value.type !== "FunctionExpression" || !node.value.id || node.computed && node.key.type !== "Literal") {
178
+ if (node.value.type !== "FunctionExpression" || !node.value.id || node.computed && !isStringLiteral(node.key)) {
168
179
  return;
169
180
  }
170
181
  if (node.key.type === "Identifier" && shouldWarn(node.key.name, node.value.id.name)) {
171
182
  report(node, node.key.name, node.value.id.name, true);
172
183
  } else if (
173
- node.key.type === "Literal" &&
184
+ isStringLiteral(node.key) &&
174
185
  isIdentifier(node.key.value, ecmaVersion) &&
175
186
  shouldWarn(node.key.value, node.value.id.name)
176
187
  ) {
@@ -67,6 +67,8 @@ module.exports = {
67
67
  const NESTED_BINARY = ALL_NODES && context.options[1] && context.options[1].nestedBinaryExpressions === false;
68
68
  const EXCEPT_RETURN_ASSIGN = ALL_NODES && context.options[1] && context.options[1].returnAssign === false;
69
69
  const IGNORE_JSX = ALL_NODES && context.options[1] && context.options[1].ignoreJSX;
70
+ const PRECEDENCE_OF_ASSIGNMENT_EXPR = precedence({ type: "AssignmentExpression" });
71
+ const PRECEDENCE_OF_UPDATE_EXPR = precedence({ type: "UpdateExpression" });
70
72
 
71
73
  /**
72
74
  * Determines if this rule should be enforced for a node given the current configuration.
@@ -365,7 +367,7 @@ module.exports = {
365
367
  * @returns {void}
366
368
  * @private
367
369
  */
368
- function dryUnaryUpdate(node) {
370
+ function checkUnaryUpdate(node) {
369
371
  if (node.type === "UnaryExpression" && node.argument.type === "BinaryExpression" && node.argument.operator === "**") {
370
372
  return;
371
373
  }
@@ -381,7 +383,7 @@ module.exports = {
381
383
  * @returns {void}
382
384
  * @private
383
385
  */
384
- function dryCallNew(node) {
386
+ function checkCallNew(node) {
385
387
  if (hasExcessParens(node.callee) && precedence(node.callee) >= precedence(node) && !(
386
388
  node.type === "CallExpression" &&
387
389
  (node.callee.type === "FunctionExpression" ||
@@ -393,12 +395,12 @@ module.exports = {
393
395
  report(node.callee);
394
396
  }
395
397
  if (node.arguments.length === 1) {
396
- if (hasDoubleExcessParens(node.arguments[0]) && precedence(node.arguments[0]) >= precedence({ type: "AssignmentExpression" })) {
398
+ if (hasDoubleExcessParens(node.arguments[0]) && precedence(node.arguments[0]) >= PRECEDENCE_OF_ASSIGNMENT_EXPR) {
397
399
  report(node.arguments[0]);
398
400
  }
399
401
  } else {
400
402
  [].forEach.call(node.arguments, arg => {
401
- if (hasExcessParens(arg) && precedence(arg) >= precedence({ type: "AssignmentExpression" })) {
403
+ if (hasExcessParens(arg) && precedence(arg) >= PRECEDENCE_OF_ASSIGNMENT_EXPR) {
402
404
  report(arg);
403
405
  }
404
406
  });
@@ -411,7 +413,7 @@ module.exports = {
411
413
  * @returns {void}
412
414
  * @private
413
415
  */
414
- function dryBinaryLogical(node) {
416
+ function checkBinaryLogical(node) {
415
417
  const prec = precedence(node);
416
418
  const leftPrecedence = precedence(node.left);
417
419
  const rightPrecedence = precedence(node.right);
@@ -428,10 +430,46 @@ module.exports = {
428
430
  }
429
431
  }
430
432
 
433
+ /**
434
+ * Check the parentheses around the super class of the given class definition.
435
+ * @param {ASTNode} node The node of class declarations to check.
436
+ * @returns {void}
437
+ */
438
+ function checkClass(node) {
439
+ if (!node.superClass) {
440
+ return;
441
+ }
442
+
443
+ // If `node.superClass` is a LeftHandSideExpression, parentheses are extra.
444
+ // Otherwise, parentheses are needed.
445
+ const hasExtraParens = precedence(node.superClass) > PRECEDENCE_OF_UPDATE_EXPR
446
+ ? hasExcessParens(node.superClass)
447
+ : hasDoubleExcessParens(node.superClass);
448
+
449
+ if (hasExtraParens) {
450
+ report(node.superClass);
451
+ }
452
+ }
453
+
454
+ /**
455
+ * Check the parentheses around the argument of the given spread operator.
456
+ * @param {ASTNode} node The node of spread elements/properties to check.
457
+ * @returns {void}
458
+ */
459
+ function checkSpreadOperator(node) {
460
+ const hasExtraParens = precedence(node.argument) >= PRECEDENCE_OF_ASSIGNMENT_EXPR
461
+ ? hasExcessParens(node.argument)
462
+ : hasDoubleExcessParens(node.argument);
463
+
464
+ if (hasExtraParens) {
465
+ report(node.argument);
466
+ }
467
+ }
468
+
431
469
  return {
432
470
  ArrayExpression(node) {
433
471
  [].forEach.call(node.elements, e => {
434
- if (e && hasExcessParens(e) && precedence(e) >= precedence({ type: "AssignmentExpression" })) {
472
+ if (e && hasExcessParens(e) && precedence(e) >= PRECEDENCE_OF_ASSIGNMENT_EXPR) {
435
473
  report(e);
436
474
  }
437
475
  });
@@ -443,7 +481,7 @@ module.exports = {
443
481
  }
444
482
 
445
483
  if (node.body.type !== "BlockStatement") {
446
- if (sourceCode.getFirstToken(node.body).value !== "{" && hasExcessParens(node.body) && precedence(node.body) >= precedence({ type: "AssignmentExpression" })) {
484
+ if (sourceCode.getFirstToken(node.body).value !== "{" && hasExcessParens(node.body) && precedence(node.body) >= PRECEDENCE_OF_ASSIGNMENT_EXPR) {
447
485
  report(node.body);
448
486
  return;
449
487
  }
@@ -465,8 +503,8 @@ module.exports = {
465
503
  }
466
504
  },
467
505
 
468
- BinaryExpression: dryBinaryLogical,
469
- CallExpression: dryCallNew,
506
+ BinaryExpression: checkBinaryLogical,
507
+ CallExpression: checkCallNew,
470
508
 
471
509
  ConditionalExpression(node) {
472
510
  if (isReturnAssignException(node)) {
@@ -477,11 +515,11 @@ module.exports = {
477
515
  report(node.test);
478
516
  }
479
517
 
480
- if (hasExcessParens(node.consequent) && precedence(node.consequent) >= precedence({ type: "AssignmentExpression" })) {
518
+ if (hasExcessParens(node.consequent) && precedence(node.consequent) >= PRECEDENCE_OF_ASSIGNMENT_EXPR) {
481
519
  report(node.consequent);
482
520
  }
483
521
 
484
- if (hasExcessParens(node.alternate) && precedence(node.alternate) >= precedence({ type: "AssignmentExpression" })) {
522
+ if (hasExcessParens(node.alternate) && precedence(node.alternate) >= PRECEDENCE_OF_ASSIGNMENT_EXPR) {
485
523
  report(node.alternate);
486
524
  }
487
525
  },
@@ -546,7 +584,7 @@ module.exports = {
546
584
  }
547
585
  },
548
586
 
549
- LogicalExpression: dryBinaryLogical,
587
+ LogicalExpression: checkBinaryLogical,
550
588
 
551
589
  MemberExpression(node) {
552
590
  if (
@@ -576,13 +614,13 @@ module.exports = {
576
614
  }
577
615
  },
578
616
 
579
- NewExpression: dryCallNew,
617
+ NewExpression: checkCallNew,
580
618
 
581
619
  ObjectExpression(node) {
582
620
  [].forEach.call(node.properties, e => {
583
621
  const v = e.value;
584
622
 
585
- if (v && hasExcessParens(v) && precedence(v) >= precedence({ type: "AssignmentExpression" })) {
623
+ if (v && hasExcessParens(v) && precedence(v) >= PRECEDENCE_OF_ASSIGNMENT_EXPR) {
586
624
  report(v);
587
625
  }
588
626
  });
@@ -632,13 +670,13 @@ module.exports = {
632
670
  }
633
671
  },
634
672
 
635
- UnaryExpression: dryUnaryUpdate,
636
- UpdateExpression: dryUnaryUpdate,
637
- AwaitExpression: dryUnaryUpdate,
673
+ UnaryExpression: checkUnaryUpdate,
674
+ UpdateExpression: checkUnaryUpdate,
675
+ AwaitExpression: checkUnaryUpdate,
638
676
 
639
677
  VariableDeclarator(node) {
640
678
  if (node.init && hasExcessParens(node.init) &&
641
- precedence(node.init) >= precedence({ type: "AssignmentExpression" }) &&
679
+ precedence(node.init) >= PRECEDENCE_OF_ASSIGNMENT_EXPR &&
642
680
 
643
681
  // RegExp literal is allowed to have parens (#1589)
644
682
  !(node.init.type === "Literal" && node.init.regex)) {
@@ -668,7 +706,14 @@ module.exports = {
668
706
  report(node.argument);
669
707
  }
670
708
  }
671
- }
709
+ },
710
+
711
+ ClassDeclaration: checkClass,
712
+ ClassExpression: checkClass,
713
+
714
+ SpreadElement: checkSpreadOperator,
715
+ SpreadProperty: checkSpreadOperator,
716
+ ExperimentalSpreadProperty: checkSpreadOperator
672
717
  };
673
718
 
674
719
  }
@@ -14,7 +14,7 @@ module.exports = {
14
14
  docs: {
15
15
  description: "disallow assignments to native objects or read-only global variables",
16
16
  category: "Best Practices",
17
- recommended: false
17
+ recommended: true
18
18
  },
19
19
 
20
20
  schema: [
@@ -27,20 +27,18 @@ module.exports = {
27
27
  //--------------------------------------------------------------------------
28
28
 
29
29
  /**
30
- * Checks if the callee is the Function constructor, and if so, reports an issue.
31
- * @param {ASTNode} node The node to check and report on
30
+ * Reports a node.
31
+ * @param {ASTNode} node The node to report
32
32
  * @returns {void}
33
33
  * @private
34
34
  */
35
- function validateCallee(node) {
36
- if (node.callee.name === "Function") {
37
- context.report({ node, message: "The Function constructor is eval." });
38
- }
35
+ function report(node) {
36
+ context.report({ node, message: "The Function constructor is eval." });
39
37
  }
40
38
 
41
39
  return {
42
- NewExpression: validateCallee,
43
- CallExpression: validateCallee
40
+ "NewExpression[callee.name = 'Function']": report,
41
+ "CallExpression[callee.name = 'Function']": report
44
42
  };
45
43
 
46
44
  }
@@ -24,12 +24,8 @@ module.exports = {
24
24
  create(context) {
25
25
 
26
26
  return {
27
-
28
- ExpressionStatement(node) {
29
-
30
- if (node.expression.type === "NewExpression") {
31
- context.report({ node, message: "Do not use 'new' for side effects." });
32
- }
27
+ "ExpressionStatement > NewExpression"(node) {
28
+ context.report({ node: node.parent, message: "Do not use 'new' for side effects." });
33
29
  }
34
30
  };
35
31
 
@@ -26,17 +26,9 @@ module.exports = {
26
26
  //--------------------------------------------------------------------------
27
27
 
28
28
  return {
29
-
30
- CallExpression(node) {
31
- const callee = node.callee;
32
-
33
- if (callee.type === "MemberExpression" && callee.object.name === "process" &&
34
- callee.property.name === "exit"
35
- ) {
36
- context.report({ node, message: "Don't use process.exit(); throw an error instead." });
37
- }
29
+ "CallExpression > MemberExpression.callee[object.name = 'process'][property.name = 'exit']"(node) {
30
+ context.report({ node: node.parent, message: "Don't use process.exit(); throw an error instead." });
38
31
  }
39
-
40
32
  };
41
33
 
42
34
  }
@@ -8,8 +8,6 @@
8
8
  // Rule Definition
9
9
  //------------------------------------------------------------------------------
10
10
 
11
- const nodeTypes = require("espree").Syntax;
12
-
13
11
  module.exports = {
14
12
  meta: {
15
13
  docs: {
@@ -20,32 +18,18 @@ module.exports = {
20
18
 
21
19
  schema: {
22
20
  type: "array",
23
- items: [
24
- {
25
- enum: Object.keys(nodeTypes).map(k => nodeTypes[k])
26
- }
27
- ],
21
+ items: [{ type: "string" }],
28
22
  uniqueItems: true,
29
23
  minItems: 0
30
24
  }
31
25
  },
32
26
 
33
27
  create(context) {
34
-
35
- /**
36
- * Generates a warning from the provided node, saying that node type is not allowed.
37
- * @param {ASTNode} node The node to warn on
38
- * @returns {void}
39
- */
40
- function warn(node) {
41
- context.report({ node, message: "Using '{{type}}' is not allowed.", data: node });
42
- }
43
-
44
- return context.options.reduce((result, nodeType) => {
45
- result[nodeType] = warn;
46
-
47
- return result;
48
- }, {});
28
+ return context.options.reduce((result, selector) => Object.assign(result, {
29
+ [selector](node) {
30
+ context.report({ node, message: "Using '{{selector}}' is not allowed.", data: { selector } });
31
+ }
32
+ }), {});
49
33
 
50
34
  }
51
35
  };
@@ -26,19 +26,14 @@ module.exports = {
26
26
 
27
27
  return {
28
28
 
29
- MemberExpression(node) {
30
- const propertyName = node.property.name,
31
- syncRegex = /.*Sync$/;
32
-
33
- if (syncRegex.exec(propertyName) !== null) {
34
- context.report({
35
- node,
36
- message: "Unexpected sync method: '{{propertyName}}'.",
37
- data: {
38
- propertyName
39
- }
40
- });
41
- }
29
+ "MemberExpression[property.name=/.*Sync$/]"(node) {
30
+ context.report({
31
+ node,
32
+ message: "Unexpected sync method: '{{propertyName}}'.",
33
+ data: {
34
+ propertyName: node.property.name
35
+ }
36
+ });
42
37
  }
43
38
  };
44
39
 
@@ -25,6 +25,9 @@ module.exports = {
25
25
  },
26
26
  allowTernary: {
27
27
  type: "boolean"
28
+ },
29
+ allowTaggedTemplates: {
30
+ type: "boolean"
28
31
  }
29
32
  },
30
33
  additionalProperties: false
@@ -35,7 +38,8 @@ module.exports = {
35
38
  create(context) {
36
39
  const config = context.options[0] || {},
37
40
  allowShortCircuit = config.allowShortCircuit || false,
38
- allowTernary = config.allowTernary || false;
41
+ allowTernary = config.allowTernary || false,
42
+ allowTaggedTemplates = config.allowTaggedTemplates || false;
39
43
 
40
44
  /**
41
45
  * @param {ASTNode} node - any node
@@ -95,12 +99,17 @@ module.exports = {
95
99
  return isValidExpression(node.consequent) && isValidExpression(node.alternate);
96
100
  }
97
101
  }
102
+
98
103
  if (allowShortCircuit) {
99
104
  if (node.type === "LogicalExpression") {
100
105
  return isValidExpression(node.right);
101
106
  }
102
107
  }
103
108
 
109
+ if (allowTaggedTemplates && node.type === "TaggedTemplateExpression") {
110
+ return true;
111
+ }
112
+
104
113
  return /^(?:Assignment|Call|New|Update|Yield|Await)Expression$/.test(node.type) ||
105
114
  (node.type === "UnaryExpression" && ["delete", "void"].indexOf(node.operator) >= 0);
106
115
  }
@@ -147,7 +147,13 @@ module.exports = {
147
147
  function check(node) {
148
148
  const isTemplateElement = node.type === "TemplateElement";
149
149
 
150
- if (isTemplateElement && node.parent && node.parent.parent && node.parent.parent.type === "TaggedTemplateExpression") {
150
+ if (
151
+ isTemplateElement &&
152
+ node.parent &&
153
+ node.parent.parent &&
154
+ node.parent.parent.type === "TaggedTemplateExpression" &&
155
+ node.parent === node.parent.parent.quasi
156
+ ) {
151
157
 
152
158
  // Don't report tagged template literals, because the backslash character is accessible to the tag function.
153
159
  return;
@@ -37,11 +37,9 @@ module.exports = {
37
37
 
38
38
  return {
39
39
  VariableDeclaration(node) {
40
- node.declarations.reduce((memo, decl) => {
41
- if (decl.id.type === "ObjectPattern" || decl.id.type === "ArrayPattern") {
42
- return memo;
43
- }
40
+ const idDeclarations = node.declarations.filter(decl => decl.id.type === "Identifier");
44
41
 
42
+ idDeclarations.slice(1).reduce((memo, decl) => {
45
43
  let lastVariableName = memo.id.name,
46
44
  currenVariableName = decl.id.name;
47
45
 
@@ -56,7 +54,7 @@ module.exports = {
56
54
  }
57
55
  return decl;
58
56
 
59
- }, node.declarations[0]);
57
+ }, idDeclarations[0]);
60
58
  }
61
59
  };
62
60
  }
@@ -343,12 +343,13 @@ RuleTester.prototype = {
343
343
  * running the rule under test.
344
344
  */
345
345
  eslint.reset();
346
+
346
347
  eslint.on("Program", node => {
347
348
  beforeAST = cloneDeeplyExcludesParent(node);
349
+ });
348
350
 
349
- eslint.on("Program:exit", node => {
350
- afterAST = cloneDeeplyExcludesParent(node);
351
- });
351
+ eslint.on("Program:exit", node => {
352
+ afterAST = node;
352
353
  });
353
354
 
354
355
  // Freezes rule-context properties.
@@ -385,7 +386,7 @@ RuleTester.prototype = {
385
386
  return {
386
387
  messages: eslint.verify(code, config, filename, true),
387
388
  beforeAST,
388
- afterAST
389
+ afterAST: cloneDeeplyExcludesParent(afterAST)
389
390
  };
390
391
  } finally {
391
392
  rules.get = originalGet;
@@ -5,6 +5,185 @@
5
5
 
6
6
  "use strict";
7
7
 
8
+ //------------------------------------------------------------------------------
9
+ // Requirements
10
+ //------------------------------------------------------------------------------
11
+
12
+ const esquery = require("esquery");
13
+ const lodash = require("lodash");
14
+
15
+ //------------------------------------------------------------------------------
16
+ // Typedefs
17
+ //------------------------------------------------------------------------------
18
+
19
+ /**
20
+ * An object describing an AST selector
21
+ * @typedef {Object} ASTSelector
22
+ * @property {string} rawSelector The string that was parsed into this selector
23
+ * @property {boolean} isExit `true` if this should be emitted when exiting the node rather than when entering
24
+ * @property {Object} parsedSelector An object (from esquery) describing the matching behavior of the selector
25
+ * @property {string[]|null} listenerTypes A list of node types that could possibly cause the selector to match,
26
+ * or `null` if all node types could cause a match
27
+ * @property {number} attributeCount The total number of classes, pseudo-classes, and attribute queries in this selector
28
+ * @property {number} identifierCount The total number of identifier queries in this selector
29
+ */
30
+
31
+ //------------------------------------------------------------------------------
32
+ // Helpers
33
+ //------------------------------------------------------------------------------
34
+
35
+ /**
36
+ * Gets the possible types of a selector
37
+ * @param {Object} parsedSelector An object (from esquery) describing the matching behavior of the selector
38
+ * @returns {string[]|null} The node types that could possibly trigger this selector, or `null` if all node types could trigger it
39
+ */
40
+ function getPossibleTypes(parsedSelector) {
41
+ switch (parsedSelector.type) {
42
+ case "identifier":
43
+ return [parsedSelector.value];
44
+
45
+ case "matches": {
46
+ const typesForComponents = parsedSelector.selectors.map(getPossibleTypes);
47
+
48
+ if (typesForComponents.every(typesForComponent => typesForComponent)) {
49
+ return lodash.union.apply(null, typesForComponents);
50
+ }
51
+ return null;
52
+ }
53
+
54
+ case "compound": {
55
+ const typesForComponents = parsedSelector.selectors.map(getPossibleTypes).filter(typesForComponent => typesForComponent);
56
+
57
+ // If all of the components could match any type, then the compound could also match any type.
58
+ if (!typesForComponents.length) {
59
+ return null;
60
+ }
61
+
62
+ /*
63
+ * If at least one of the components could only match a particular type, the compound could only match
64
+ * the intersection of those types.
65
+ */
66
+ return lodash.intersection.apply(null, typesForComponents);
67
+ }
68
+
69
+ case "child":
70
+ case "descendant":
71
+ case "sibling":
72
+ case "adjacent":
73
+ return getPossibleTypes(parsedSelector.right);
74
+
75
+ default:
76
+ return null;
77
+
78
+ }
79
+ }
80
+
81
+ /**
82
+ * Counts the number of class, pseudo-class, and attribute queries in this selector
83
+ * @param {Object} parsedSelector An object (from esquery) describing the selector's matching behavior
84
+ * @returns {number} The number of class, pseudo-class, and attribute queries in this selector
85
+ */
86
+ function countClassAttributes(parsedSelector) {
87
+ switch (parsedSelector.type) {
88
+ case "child":
89
+ case "descendant":
90
+ case "sibling":
91
+ case "adjacent":
92
+ return countClassAttributes(parsedSelector.left) + countClassAttributes(parsedSelector.right);
93
+
94
+ case "compound":
95
+ case "not":
96
+ case "matches":
97
+ return parsedSelector.selectors.reduce((sum, childSelector) => sum + countClassAttributes(childSelector), 0);
98
+
99
+ case "attribute":
100
+ case "field":
101
+ case "nth-child":
102
+ case "nth-last-child":
103
+ return 1;
104
+
105
+ default:
106
+ return 0;
107
+ }
108
+ }
109
+
110
+ /**
111
+ * Counts the number of identifier queries in this selector
112
+ * @param {Object} parsedSelector An object (from esquery) describing the selector's matching behavior
113
+ * @returns {number} The number of identifier queries
114
+ */
115
+ function countIdentifiers(parsedSelector) {
116
+ switch (parsedSelector.type) {
117
+ case "child":
118
+ case "descendant":
119
+ case "sibling":
120
+ case "adjacent":
121
+ return countIdentifiers(parsedSelector.left) + countIdentifiers(parsedSelector.right);
122
+
123
+ case "compound":
124
+ case "not":
125
+ case "matches":
126
+ return parsedSelector.selectors.reduce((sum, childSelector) => sum + countIdentifiers(childSelector), 0);
127
+
128
+ case "identifier":
129
+ return 1;
130
+
131
+ default:
132
+ return 0;
133
+ }
134
+ }
135
+
136
+ /**
137
+ * Compares the specificity of two selector objects, with CSS-like rules.
138
+ * @param {ASTSelector} selectorA An AST selector descriptor
139
+ * @param {ASTSelector} selectorB Another AST selector descriptor
140
+ * @returns {number}
141
+ * a value less than 0 if selectorA is less specific than selectorB
142
+ * a value greater than 0 if selectorA is more specific than selectorB
143
+ * a value less than 0 if selectorA and selectorB have the same specificity, and selectorA <= selectorB alphabetically
144
+ * a value greater than 0 if selectorA and selectorB have the same specificity, and selectorA > selectorB alphabetically
145
+ */
146
+ function compareSpecificity(selectorA, selectorB) {
147
+ return selectorA.attributeCount - selectorB.attributeCount ||
148
+ selectorA.identifierCount - selectorB.identifierCount ||
149
+ (selectorA.rawSelector <= selectorB.rawSelector ? -1 : 1);
150
+ }
151
+
152
+ /**
153
+ * Parses a raw selector string, and throws a useful error if parsing fails.
154
+ * @param {string} rawSelector A raw AST selector
155
+ * @returns {Object} An object (from esquery) describing the matching behavior of this selector
156
+ * @throws {Error} An error if the selector is invalid
157
+ */
158
+ function tryParseSelector(rawSelector) {
159
+ try {
160
+ return esquery.parse(rawSelector.replace(/:exit$/, ""));
161
+ } catch (err) {
162
+ if (typeof err.offset === "number") {
163
+ throw new Error(`Syntax error in selector "${rawSelector}" at position ${err.offset}: ${err.message}`);
164
+ }
165
+ throw err;
166
+ }
167
+ }
168
+
169
+ /**
170
+ * Parses a raw selector string, and returns the parsed selector along with specificity and type information.
171
+ * @param {string} rawSelector A raw AST selector
172
+ * @returns {ASTSelector} A selector descriptor
173
+ */
174
+ const parseSelector = lodash.memoize(rawSelector => {
175
+ const parsedSelector = tryParseSelector(rawSelector);
176
+
177
+ return {
178
+ rawSelector,
179
+ isExit: rawSelector.endsWith(":exit"),
180
+ parsedSelector,
181
+ listenerTypes: getPossibleTypes(parsedSelector),
182
+ attributeCount: countClassAttributes(parsedSelector),
183
+ identifierCount: countIdentifiers(parsedSelector)
184
+ };
185
+ });
186
+
8
187
  //------------------------------------------------------------------------------
9
188
  // Public Interface
10
189
  //------------------------------------------------------------------------------
@@ -24,10 +203,97 @@
24
203
  class NodeEventGenerator {
25
204
 
26
205
  /**
27
- * @param {EventEmitter} emitter - An event emitter which is the destination of events.
28
- */
206
+ * @param {EventEmitter} emitter - An event emitter which is the destination of events. This emitter must already
207
+ * have registered listeners for all of the events that it needs to listen for.
208
+ * @returns {NodeEventGenerator} new instance
209
+ */
29
210
  constructor(emitter) {
30
211
  this.emitter = emitter;
212
+ this.currentAncestry = [];
213
+ this.enterSelectorsByNodeType = new Map();
214
+ this.exitSelectorsByNodeType = new Map();
215
+ this.anyTypeEnterSelectors = [];
216
+ this.anyTypeExitSelectors = [];
217
+
218
+ const eventNames = typeof emitter.eventNames === "function"
219
+
220
+ // Use the built-in eventNames() function if available (Node 6+)
221
+ ? emitter.eventNames()
222
+
223
+ /*
224
+ * Otherwise, use the private _events property.
225
+ * Using a private property isn't ideal here, but this seems to
226
+ * be the best way to get a list of event names without overriding
227
+ * addEventListener, which would hurt performance. This property
228
+ * is widely used and unlikely to be removed in a future version
229
+ * (see https://github.com/nodejs/node/issues/1817). Also, future
230
+ * node versions will have eventNames() anyway.
231
+ */
232
+ : Object.keys(emitter._events); // eslint-disable-line no-underscore-dangle
233
+
234
+ eventNames.forEach(rawSelector => {
235
+ const selector = parseSelector(rawSelector);
236
+
237
+ if (selector.listenerTypes) {
238
+ selector.listenerTypes.forEach(nodeType => {
239
+ const typeMap = selector.isExit ? this.exitSelectorsByNodeType : this.enterSelectorsByNodeType;
240
+
241
+ if (!typeMap.has(nodeType)) {
242
+ typeMap.set(nodeType, []);
243
+ }
244
+ typeMap.get(nodeType).push(selector);
245
+ });
246
+ } else {
247
+ (selector.isExit ? this.anyTypeExitSelectors : this.anyTypeEnterSelectors).push(selector);
248
+ }
249
+ });
250
+
251
+ this.anyTypeEnterSelectors.sort(compareSpecificity);
252
+ this.anyTypeExitSelectors.sort(compareSpecificity);
253
+ this.enterSelectorsByNodeType.forEach(selectorList => selectorList.sort(compareSpecificity));
254
+ this.exitSelectorsByNodeType.forEach(selectorList => selectorList.sort(compareSpecificity));
255
+ }
256
+
257
+ /**
258
+ * Checks a selector against a node, and emits it if it matches
259
+ * @param {ASTNode} node The node to check
260
+ * @param {ASTSelector} selector An AST selector descriptor
261
+ * @returns {void}
262
+ */
263
+ applySelector(node, selector) {
264
+ if (esquery.matches(node, selector.parsedSelector, this.currentAncestry)) {
265
+ this.emitter.emit(selector.rawSelector, node);
266
+ }
267
+ }
268
+
269
+ /**
270
+ * Applies all appropriate selectors to a node, in specificity order
271
+ * @param {ASTNode} node The node to check
272
+ * @param {boolean} isExit `false` if the node is currently being entered, `true` if it's currently being exited
273
+ * @returns {void}
274
+ */
275
+ applySelectors(node, isExit) {
276
+ const selectorsByNodeType = (isExit ? this.exitSelectorsByNodeType : this.enterSelectorsByNodeType).get(node.type) || [];
277
+ const anyTypeSelectors = isExit ? this.anyTypeExitSelectors : this.anyTypeEnterSelectors;
278
+
279
+ /*
280
+ * selectorsByNodeType and anyTypeSelectors were already sorted by specificity in the constructor.
281
+ * Iterate through each of them, applying selectors in the right order.
282
+ */
283
+ let selectorsByTypeIndex = 0;
284
+ let anyTypeSelectorsIndex = 0;
285
+
286
+ while (selectorsByTypeIndex < selectorsByNodeType.length || anyTypeSelectorsIndex < anyTypeSelectors.length) {
287
+ if (
288
+ selectorsByTypeIndex >= selectorsByNodeType.length ||
289
+ anyTypeSelectorsIndex < anyTypeSelectors.length &&
290
+ compareSpecificity(anyTypeSelectors[anyTypeSelectorsIndex], selectorsByNodeType[selectorsByTypeIndex]) < 0
291
+ ) {
292
+ this.applySelector(node, anyTypeSelectors[anyTypeSelectorsIndex++]);
293
+ } else {
294
+ this.applySelector(node, selectorsByNodeType[selectorsByTypeIndex++]);
295
+ }
296
+ }
31
297
  }
32
298
 
33
299
  /**
@@ -36,7 +302,10 @@ class NodeEventGenerator {
36
302
  * @returns {void}
37
303
  */
38
304
  enterNode(node) {
39
- this.emitter.emit(node.type, node);
305
+ if (node.parent) {
306
+ this.currentAncestry.unshift(node.parent);
307
+ }
308
+ this.applySelectors(node, false);
40
309
  }
41
310
 
42
311
  /**
@@ -45,7 +314,8 @@ class NodeEventGenerator {
45
314
  * @returns {void}
46
315
  */
47
316
  leaveNode(node) {
48
- this.emitter.emit(`${node.type}:exit`, node);
317
+ this.applySelectors(node, true);
318
+ this.currentAncestry.shift();
49
319
  }
50
320
  }
51
321
 
@@ -14,41 +14,32 @@ const estraverse = require("estraverse");
14
14
  // Helpers
15
15
  //------------------------------------------------------------------------------
16
16
 
17
- const KEY_BLACKLIST = [
17
+ const KEY_BLACKLIST = new Set([
18
18
  "parent",
19
19
  "leadingComments",
20
20
  "trailingComments"
21
- ];
21
+ ]);
22
22
 
23
23
  /**
24
24
  * Wrapper around an estraverse controller that ensures the correct keys
25
25
  * are visited.
26
26
  * @constructor
27
27
  */
28
- function Traverser() {
29
-
30
- const controller = Object.create(new estraverse.Controller()),
31
- originalTraverse = controller.traverse;
32
-
33
- // intercept call to traverse() and add the fallback key to the visitor
34
- controller.traverse = function(node, visitor) {
28
+ class Traverser extends estraverse.Controller {
29
+ traverse(node, visitor) {
35
30
  visitor.fallback = Traverser.getKeys;
36
- return originalTraverse.call(this, node, visitor);
37
- };
38
-
39
- return controller;
31
+ return super.traverse(node, visitor);
32
+ }
33
+
34
+ /**
35
+ * Calculates the keys to use for traversal.
36
+ * @param {ASTNode} node The node to read keys from.
37
+ * @returns {string[]} An array of keys to visit on the node.
38
+ * @private
39
+ */
40
+ static getKeys(node) {
41
+ return Object.keys(node).filter(key => !KEY_BLACKLIST.has(key));
42
+ }
40
43
  }
41
44
 
42
- /**
43
- * Calculates the keys to use for traversal.
44
- * @param {ASTNode} node The node to read keys from.
45
- * @returns {string[]} An array of keys to visit on the node.
46
- * @private
47
- */
48
- Traverser.getKeys = function(node) {
49
- return Object.keys(node).filter(key => KEY_BLACKLIST.indexOf(key) === -1);
50
- };
51
-
52
45
  module.exports = Traverser;
53
-
54
-
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint",
3
- "version": "3.17.1",
3
+ "version": "3.18.0",
4
4
  "author": "Nicholas C. Zakas <nicholas+npm@nczconsulting.com>",
5
5
  "description": "An AST-based pattern checker for JavaScript.",
6
6
  "bin": {
@@ -36,11 +36,12 @@
36
36
  "dependencies": {
37
37
  "babel-code-frame": "^6.16.0",
38
38
  "chalk": "^1.1.3",
39
- "concat-stream": "^1.4.6",
39
+ "concat-stream": "^1.5.2",
40
40
  "debug": "^2.1.1",
41
- "doctrine": "^1.2.2",
41
+ "doctrine": "^2.0.0",
42
42
  "escope": "^3.6.0",
43
43
  "espree": "^3.4.0",
44
+ "esquery": "^1.0.0",
44
45
  "estraverse": "^4.2.0",
45
46
  "esutils": "^2.0.2",
46
47
  "file-entry-cache": "^2.0.0",
@@ -97,7 +98,7 @@
97
98
  "load-perf": "^0.2.0",
98
99
  "markdownlint": "^0.3.1",
99
100
  "mocha": "^2.4.5",
100
- "mock-fs": "not-an-aardvark/mock-fs#06868bbd7724707f9324b237bdde28f05f7a01d5",
101
+ "mock-fs": "^4.2.0",
101
102
  "npm-license": "^0.3.2",
102
103
  "phantomjs-prebuilt": "^2.1.7",
103
104
  "proxyquire": "^1.7.10",