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
package/lib/linter.js CHANGED
@@ -14,7 +14,6 @@ const eslintScope = require("eslint-scope"),
14
14
  levn = require("levn"),
15
15
  lodash = require("lodash"),
16
16
  blankScriptAST = require("../conf/blank-script.json"),
17
- defaultConfig = require("../conf/default-config-options.js"),
18
17
  CodePathAnalyzer = require("./code-path-analysis/code-path-analyzer"),
19
18
  ConfigOps = require("./config/config-ops"),
20
19
  validator = require("./config/config-validator"),
@@ -33,6 +32,7 @@ const eslintScope = require("eslint-scope"),
33
32
 
34
33
  const debug = require("debug")("eslint:linter");
35
34
  const MAX_AUTOFIX_PASSES = 10;
35
+ const DEFAULT_PARSER_NAME = "espree";
36
36
 
37
37
  //------------------------------------------------------------------------------
38
38
  // Typedefs
@@ -71,25 +71,25 @@ function parseBooleanConfig(string, comment) {
71
71
  const items = {};
72
72
 
73
73
  // Collapse whitespace around `:` and `,` to make parsing easier
74
- string = string.replace(/\s*([:,])\s*/g, "$1");
74
+ const trimmedString = string.replace(/\s*([:,])\s*/g, "$1");
75
75
 
76
- string.split(/\s|,+/).forEach(name => {
76
+ trimmedString.split(/\s|,+/).forEach(name => {
77
77
  if (!name) {
78
78
  return;
79
79
  }
80
80
  const pos = name.indexOf(":");
81
- let value;
82
81
 
83
- if (pos !== -1) {
84
- value = name.slice(pos + 1);
85
- name = name.slice(0, pos);
82
+ if (pos === -1) {
83
+ items[name] = {
84
+ value: false,
85
+ comment
86
+ };
87
+ } else {
88
+ items[name.slice(0, pos)] = {
89
+ value: name.slice(pos + 1) === "true",
90
+ comment
91
+ };
86
92
  }
87
-
88
- items[name] = {
89
- value: (value === "true"),
90
- comment
91
- };
92
-
93
93
  });
94
94
  return items;
95
95
  }
@@ -127,9 +127,10 @@ function parseJsonConfig(string, location) {
127
127
  * But we are supporting that. So this is a fallback for that.
128
128
  */
129
129
  items = {};
130
- string = string.replace(/([a-zA-Z0-9\-/]+):/g, "\"$1\":").replace(/(]|[0-9])\s+(?=")/, "$1,");
130
+ const normalizedString = string.replace(/([a-zA-Z0-9\-/]+):/g, "\"$1\":").replace(/(]|[0-9])\s+(?=")/, "$1,");
131
+
131
132
  try {
132
- items = JSON.parse(`{${string}}`);
133
+ items = JSON.parse(`{${normalizedString}}`);
133
134
  } catch (ex) {
134
135
  return {
135
136
  success: false,
@@ -138,7 +139,7 @@ function parseJsonConfig(string, location) {
138
139
  fatal: true,
139
140
  severity: 2,
140
141
  source: null,
141
- message: `Failed to parse JSON from '${string}': ${ex.message}`,
142
+ message: `Failed to parse JSON from '${normalizedString}': ${ex.message}`,
142
143
  line: location.start.line,
143
144
  column: location.start.column + 1
144
145
  }
@@ -161,14 +162,12 @@ function parseListConfig(string) {
161
162
  const items = {};
162
163
 
163
164
  // Collapse whitespace around ,
164
- string = string.replace(/\s*,\s*/g, ",");
165
+ string.replace(/\s*,\s*/g, ",").split(/,+/).forEach(name => {
166
+ const trimmedName = name.trim();
165
167
 
166
- string.split(/,+/).forEach(name => {
167
- name = name.trim();
168
- if (!name) {
169
- return;
168
+ if (trimmedName) {
169
+ items[trimmedName] = true;
170
170
  }
171
- items[name] = true;
172
171
  });
173
172
  return items;
174
173
  }
@@ -178,32 +177,12 @@ function parseListConfig(string) {
178
177
  * and any globals declared by special block comments, are present in the global
179
178
  * scope.
180
179
  * @param {Scope} globalScope The global scope.
181
- * @param {Object} config The existing configuration data.
182
- * @param {Environments} envContext Env context
180
+ * @param {Object} configGlobals The globals declared in configuration
181
+ * @param {{exportedVariables: Object, enabledGlobals: Object}} commentDirectives Directives from comment configuration
183
182
  * @returns {void}
184
183
  */
185
- function addDeclaredGlobals(globalScope, config, envContext) {
186
- const declaredGlobals = {},
187
- exportedGlobals = {},
188
- explicitGlobals = {},
189
- builtin = envContext.get("builtin");
190
-
191
- Object.assign(declaredGlobals, builtin);
192
-
193
- Object.keys(config.env).filter(name => config.env[name]).forEach(name => {
194
- const env = envContext.get(name),
195
- environmentGlobals = env && env.globals;
196
-
197
- if (environmentGlobals) {
198
- Object.assign(declaredGlobals, environmentGlobals);
199
- }
200
- });
201
-
202
- Object.assign(exportedGlobals, config.exported);
203
- Object.assign(declaredGlobals, config.globals);
204
- Object.assign(explicitGlobals, config.astGlobals);
205
-
206
- Object.keys(declaredGlobals).forEach(name => {
184
+ function addDeclaredGlobals(globalScope, configGlobals, commentDirectives) {
185
+ Object.keys(configGlobals).forEach(name => {
207
186
  let variable = globalScope.set.get(name);
208
187
 
209
188
  if (!variable) {
@@ -212,24 +191,24 @@ function addDeclaredGlobals(globalScope, config, envContext) {
212
191
  globalScope.variables.push(variable);
213
192
  globalScope.set.set(name, variable);
214
193
  }
215
- variable.writeable = declaredGlobals[name];
194
+ variable.writeable = configGlobals[name];
216
195
  });
217
196
 
218
- Object.keys(explicitGlobals).forEach(name => {
197
+ Object.keys(commentDirectives.enabledGlobals).forEach(name => {
219
198
  let variable = globalScope.set.get(name);
220
199
 
221
200
  if (!variable) {
222
201
  variable = new eslintScope.Variable(name, globalScope);
223
202
  variable.eslintExplicitGlobal = true;
224
- variable.eslintExplicitGlobalComment = explicitGlobals[name].comment;
203
+ variable.eslintExplicitGlobalComment = commentDirectives.enabledGlobals[name].comment;
225
204
  globalScope.variables.push(variable);
226
205
  globalScope.set.set(name, variable);
227
206
  }
228
- variable.writeable = explicitGlobals[name].value;
207
+ variable.writeable = commentDirectives.enabledGlobals[name].value;
229
208
  });
230
209
 
231
210
  // mark all exported variables as such
232
- Object.keys(exportedGlobals).forEach(name => {
211
+ Object.keys(commentDirectives.exportedVariables).forEach(name => {
233
212
  const variable = globalScope.set.get(name);
234
213
 
235
214
  if (variable) {
@@ -283,108 +262,90 @@ function createDisableDirectives(type, loc, value) {
283
262
  * where reporting is disabled or enabled and merges them with reporting config.
284
263
  * @param {string} filename The file being checked.
285
264
  * @param {ASTNode} ast The top node of the AST.
286
- * @param {Object} config The existing configuration data.
287
265
  * @param {function(string): {create: Function}} ruleMapper A map from rule IDs to defined rules
288
- * @returns {{config: Object, problems: Problem[], disableDirectives: DisableDirective[]}}
289
- * Modified config object, along with any problems encountered while parsing config comments
266
+ * @returns {{configuredRules: Object, enabledGlobals: Object, exportedVariables: Object, problems: Problem[], disableDirectives: DisableDirective[]}}
267
+ * A collection of the directive comments that were found, along with any problems that occurred when parsing
290
268
  */
291
- function modifyConfigsFromComments(filename, ast, config, ruleMapper) {
292
-
293
- const commentConfig = {
294
- exported: {},
295
- astGlobals: {},
296
- rules: {},
297
- env: {}
298
- };
299
- const commentRules = {};
269
+ function getDirectiveComments(filename, ast, ruleMapper) {
270
+ const configuredRules = {};
271
+ const enabledGlobals = {};
272
+ const exportedVariables = {};
300
273
  const problems = [];
301
274
  const disableDirectives = [];
302
275
 
303
276
  ast.comments.filter(token => token.type !== "Shebang").forEach(comment => {
277
+ const trimmedCommentText = comment.value.trim();
278
+ const match = /^(eslint(-\w+){0,3}|exported|globals?)(\s|$)/.exec(trimmedCommentText);
304
279
 
305
- let value = comment.value.trim();
306
- const match = /^(eslint(-\w+){0,3}|exported|globals?)(\s|$)/.exec(value);
307
-
308
- if (match) {
309
- value = value.slice(match.index + match[1].length);
310
- if (comment.type === "Block") {
311
- switch (match[1]) {
312
- case "exported":
313
- Object.assign(commentConfig.exported, parseBooleanConfig(value, comment));
314
- break;
315
-
316
- case "globals":
317
- case "global":
318
- Object.assign(commentConfig.astGlobals, parseBooleanConfig(value, comment));
319
- break;
320
-
321
- case "eslint-disable":
322
- [].push.apply(disableDirectives, createDisableDirectives("disable", comment.loc.start, value));
323
- break;
324
-
325
- case "eslint-disable-line":
326
- if (comment.loc.start.line === comment.loc.end.line) {
327
- [].push.apply(disableDirectives, createDisableDirectives("disable-line", comment.loc.start, value));
328
- }
329
- break;
330
-
331
- case "eslint-disable-next-line":
332
- if (comment.loc.start.line === comment.loc.end.line) {
333
- [].push.apply(disableDirectives, createDisableDirectives("disable-next-line", comment.loc.start, value));
334
- }
335
- break;
336
-
337
- case "eslint-enable":
338
- [].push.apply(disableDirectives, createDisableDirectives("enable", comment.loc.start, value));
339
- break;
340
-
341
- case "eslint": {
342
- const parseResult = parseJsonConfig(value, comment.loc);
343
-
344
- if (parseResult.success) {
345
- Object.keys(parseResult.config).forEach(name => {
346
- const ruleValue = parseResult.config[name];
347
-
348
- try {
349
- validator.validateRuleOptions(ruleMapper(name), name, ruleValue);
350
- } catch (err) {
351
- problems.push({
352
- ruleId: name,
353
- severity: 2,
354
- source: null,
355
- message: err.message,
356
- line: comment.loc.start.line,
357
- column: comment.loc.start.column + 1,
358
- endLine: comment.loc.end.line,
359
- endColumn: comment.loc.end.column + 1,
360
- nodeType: null
361
- });
362
- }
363
- commentRules[name] = ruleValue;
364
- });
365
- } else {
366
- problems.push(parseResult.error);
367
- }
280
+ if (!match) {
281
+ return;
282
+ }
368
283
 
369
- break;
284
+ const directiveValue = trimmedCommentText.slice(match.index + match[1].length);
285
+
286
+ if (/^eslint-disable-(next-)?line$/.test(match[1]) && comment.loc.start.line === comment.loc.end.line) {
287
+ const directiveType = match[1].slice("eslint-".length);
288
+
289
+ [].push.apply(disableDirectives, createDisableDirectives(directiveType, comment.loc.start, directiveValue));
290
+ } else if (comment.type === "Block") {
291
+ switch (match[1]) {
292
+ case "exported":
293
+ Object.assign(exportedVariables, parseBooleanConfig(directiveValue, comment));
294
+ break;
295
+
296
+ case "globals":
297
+ case "global":
298
+ Object.assign(enabledGlobals, parseBooleanConfig(directiveValue, comment));
299
+ break;
300
+
301
+ case "eslint-disable":
302
+ [].push.apply(disableDirectives, createDisableDirectives("disable", comment.loc.start, directiveValue));
303
+ break;
304
+
305
+ case "eslint-enable":
306
+ [].push.apply(disableDirectives, createDisableDirectives("enable", comment.loc.start, directiveValue));
307
+ break;
308
+
309
+ case "eslint": {
310
+ const parseResult = parseJsonConfig(directiveValue, comment.loc);
311
+
312
+ if (parseResult.success) {
313
+ Object.keys(parseResult.config).forEach(name => {
314
+ const ruleValue = parseResult.config[name];
315
+
316
+ try {
317
+ validator.validateRuleOptions(ruleMapper(name), name, ruleValue);
318
+ } catch (err) {
319
+ problems.push({
320
+ ruleId: name,
321
+ severity: 2,
322
+ source: null,
323
+ message: err.message,
324
+ line: comment.loc.start.line,
325
+ column: comment.loc.start.column + 1,
326
+ endLine: comment.loc.end.line,
327
+ endColumn: comment.loc.end.column + 1,
328
+ nodeType: null
329
+ });
330
+ }
331
+ configuredRules[name] = ruleValue;
332
+ });
333
+ } else {
334
+ problems.push(parseResult.error);
370
335
  }
371
336
 
372
- // no default
373
- }
374
- } else { // comment.type === "Line"
375
- if (match[1] === "eslint-disable-line") {
376
- [].push.apply(disableDirectives, createDisableDirectives("disable-line", comment.loc.start, value));
377
- } else if (match[1] === "eslint-disable-next-line") {
378
- [].push.apply(disableDirectives, createDisableDirectives("disable-next-line", comment.loc.start, value));
337
+ break;
379
338
  }
339
+
340
+ // no default
380
341
  }
381
342
  }
382
343
  });
383
344
 
384
- Object.assign(commentConfig.rules, commentRules);
385
-
386
345
  return {
387
- config: ConfigOps.merge(config, commentConfig),
346
+ configuredRules,
347
+ enabledGlobals,
348
+ exportedVariables,
388
349
  problems,
389
350
  disableDirectives
390
351
  };
@@ -400,7 +361,7 @@ function normalizeEcmaVersion(ecmaVersion, isModule) {
400
361
 
401
362
  // Need at least ES6 for modules
402
363
  if (isModule && (!ecmaVersion || ecmaVersion < 6)) {
403
- ecmaVersion = 6;
364
+ return 6;
404
365
  }
405
366
 
406
367
  /*
@@ -408,87 +369,87 @@ function normalizeEcmaVersion(ecmaVersion, isModule) {
408
369
  * ES2015, which corresponds with ES6 (or a difference of 2009).
409
370
  */
410
371
  if (ecmaVersion >= 2015) {
411
- ecmaVersion -= 2009;
372
+ return ecmaVersion - 2009;
412
373
  }
413
374
 
414
375
  return ecmaVersion;
415
376
  }
416
377
 
378
+ const eslintEnvPattern = /\/\*\s*eslint-env\s(.+?)\*\//g;
379
+
417
380
  /**
418
- * Process initial config to make it safe to extend by file comment config
419
- * @param {Object} config Initial config
420
- * @param {Environments} envContext Env context
421
- * @returns {Object} Processed config
381
+ * Checks whether or not there is a comment which has "eslint-env *" in a given text.
382
+ * @param {string} text - A source code text to check.
383
+ * @returns {Object|null} A result of parseListConfig() with "eslint-env *" comment.
422
384
  */
423
- function prepareConfig(config, envContext) {
424
- config.globals = config.globals || {};
425
- const copiedRules = {};
426
- let parserOptions = {};
385
+ function findEslintEnv(text) {
386
+ let match, retv;
427
387
 
428
- if (typeof config.rules === "object") {
429
- Object.keys(config.rules).forEach(k => {
430
- const rule = config.rules[k];
388
+ eslintEnvPattern.lastIndex = 0;
431
389
 
432
- if (rule === null) {
433
- throw new Error(`Invalid config for rule '${k}'.`);
434
- }
435
- if (Array.isArray(rule)) {
436
- copiedRules[k] = rule.slice();
437
- } else {
438
- copiedRules[k] = rule;
439
- }
440
- });
390
+ while ((match = eslintEnvPattern.exec(text))) {
391
+ retv = Object.assign(retv || {}, parseListConfig(match[1]));
441
392
  }
442
393
 
443
- // merge in environment parserOptions
444
- if (typeof config.env === "object") {
445
- Object.keys(config.env).forEach(envName => {
446
- const env = envContext.get(envName);
394
+ return retv;
395
+ }
447
396
 
448
- if (config.env[envName] && env && env.parserOptions) {
449
- parserOptions = ConfigOps.merge(parserOptions, env.parserOptions);
450
- }
451
- });
452
- }
397
+ /**
398
+ * Normalizes the possible options for `linter.verify` and `linter.verifyAndFix` to a
399
+ * consistent shape.
400
+ * @param {(string|{reportUnusedDisableDirectives: boolean, filename: string, allowInlineConfig: boolean})} providedOptions Options
401
+ * @returns {{reportUnusedDisableDirectives: boolean, filename: string, allowInlineConfig: boolean}} Normalized options
402
+ */
403
+ function normalizeVerifyOptions(providedOptions) {
404
+ const isObjectOptions = typeof providedOptions === "object";
405
+ const providedFilename = isObjectOptions ? providedOptions.filename : providedOptions;
453
406
 
454
- const preparedConfig = {
455
- rules: copiedRules,
456
- parser: config.parser || defaultConfig.parser,
457
- globals: ConfigOps.merge(defaultConfig.globals, config.globals),
458
- env: ConfigOps.merge(defaultConfig.env, config.env || {}),
459
- settings: ConfigOps.merge(defaultConfig.settings, config.settings || {}),
460
- parserOptions: ConfigOps.merge(parserOptions, config.parserOptions || {})
407
+ return {
408
+ filename: typeof providedFilename === "string" ? providedFilename : "<input>",
409
+ allowInlineConfig: !isObjectOptions || providedOptions.allowInlineConfig !== false,
410
+ reportUnusedDisableDirectives: isObjectOptions && !!providedOptions.reportUnusedDisableDirectives
461
411
  };
462
- const isModule = preparedConfig.parserOptions.sourceType === "module";
412
+ }
413
+
414
+ /**
415
+ * Combines the provided parserOptions with the options from environments
416
+ * @param {Object} providedOptions The provided 'parserOptions' key in a config
417
+ * @param {Environment[]} enabledEnvironments The environments enabled in configuration and with inline comments
418
+ * @returns {Object} Resulting parser options after merge
419
+ */
420
+ function resolveParserOptions(providedOptions, enabledEnvironments) {
421
+ const parserOptionsFromEnv = enabledEnvironments
422
+ .filter(env => env.parserOptions)
423
+ .reduce((parserOptions, env) => ConfigOps.merge(parserOptions, env.parserOptions), {});
424
+
425
+ const mergedParserOptions = ConfigOps.merge(parserOptionsFromEnv, providedOptions || {});
426
+
427
+ const isModule = mergedParserOptions.sourceType === "module";
463
428
 
464
429
  if (isModule) {
465
430
 
466
431
  // can't have global return inside of modules
467
- preparedConfig.parserOptions.ecmaFeatures = Object.assign({}, preparedConfig.parserOptions.ecmaFeatures, { globalReturn: false });
432
+ mergedParserOptions.ecmaFeatures = Object.assign({}, mergedParserOptions.ecmaFeatures, { globalReturn: false });
468
433
  }
469
434
 
470
- preparedConfig.parserOptions.ecmaVersion = normalizeEcmaVersion(preparedConfig.parserOptions.ecmaVersion, isModule);
435
+ mergedParserOptions.ecmaVersion = normalizeEcmaVersion(mergedParserOptions.ecmaVersion, isModule);
471
436
 
472
- return preparedConfig;
437
+ return mergedParserOptions;
473
438
  }
474
439
 
475
- const eslintEnvPattern = /\/\*\s*eslint-env\s(.+?)\*\//g;
476
-
477
440
  /**
478
- * Checks whether or not there is a comment which has "eslint-env *" in a given text.
479
- * @param {string} text - A source code text to check.
480
- * @returns {Object|null} A result of parseListConfig() with "eslint-env *" comment.
441
+ * Combines the provided globals object with the globals from environments
442
+ * @param {Object} providedGlobals The 'globals' key in a config
443
+ * @param {Environments[]} enabledEnvironments The environments enabled in configuration and with inline comments
444
+ * @returns {Object} The resolved globals object
481
445
  */
482
- function findEslintEnv(text) {
483
- let match, retv;
484
-
485
- eslintEnvPattern.lastIndex = 0;
486
-
487
- while ((match = eslintEnvPattern.exec(text))) {
488
- retv = Object.assign(retv || {}, parseListConfig(match[1]));
489
- }
490
-
491
- return retv;
446
+ function resolveGlobals(providedGlobals, enabledEnvironments) {
447
+ return Object.assign.apply(
448
+ null,
449
+ [{}]
450
+ .concat(enabledEnvironments.filter(env => env.globals).map(env => env.globals))
451
+ .concat(providedGlobals)
452
+ );
492
453
  }
493
454
 
494
455
  /**
@@ -551,13 +512,16 @@ function analyzeScope(ast, parserOptions, visitorKeys) {
551
512
  * as possible
552
513
  * @param {string} text The text to parse.
553
514
  * @param {Object} providedParserOptions Options to pass to the parser
554
- * @param {Object} parser The parser module
515
+ * @param {string} parserName The name of the parser
516
+ * @param {Map<string, Object>} parserMap A map from names to loaded parsers
555
517
  * @param {string} filePath The path to the file being parsed.
556
518
  * @returns {{success: false, error: Problem}|{success: true, sourceCode: SourceCode}}
557
519
  * An object containing the AST and parser services if parsing was successful, or the error if parsing failed
558
520
  * @private
559
521
  */
560
- function parse(text, providedParserOptions, parser, filePath) {
522
+ function parse(text, providedParserOptions, parserName, parserMap, filePath) {
523
+
524
+
561
525
  const textToParse = stripUnicodeBOM(text).replace(astUtils.SHEBANG_MATCHER, (match, captured) => `//${captured}`);
562
526
  const parserOptions = Object.assign({}, providedParserOptions, {
563
527
  loc: true,
@@ -570,6 +534,25 @@ function parse(text, providedParserOptions, parser, filePath) {
570
534
  filePath
571
535
  });
572
536
 
537
+ let parser;
538
+
539
+ try {
540
+ parser = parserMap.get(parserName) || require(parserName);
541
+ } catch (ex) {
542
+ return {
543
+ success: false,
544
+ error: {
545
+ ruleId: null,
546
+ fatal: true,
547
+ severity: 2,
548
+ source: null,
549
+ message: ex.message,
550
+ line: 0,
551
+ column: 0
552
+ }
553
+ };
554
+ }
555
+
573
556
  /*
574
557
  * Check for parsing errors first. If there's a parsing error, nothing
575
558
  * else can happen. However, a parsing error does not throw an error
@@ -688,6 +671,21 @@ function markVariableAsUsed(scopeManager, currentNode, parserOptions, name) {
688
671
  return false;
689
672
  }
690
673
 
674
+ /**
675
+ * Runs a rule, and gets its listeners
676
+ * @param {Rule} rule A normalized rule with a `create` method
677
+ * @param {Context} ruleContext The context that should be passed to the rule
678
+ * @returns {Object} A map of selector listeners provided by the rule
679
+ */
680
+ function createRuleListeners(rule, ruleContext) {
681
+ try {
682
+ return rule.create(ruleContext);
683
+ } catch (ex) {
684
+ ex.message = `Error while loading rule '${ruleContext.id}': ${ex.message}`;
685
+ throw ex;
686
+ }
687
+ }
688
+
691
689
  // methods that exist on SourceCode object
692
690
  const DEPRECATED_SOURCECODE_PASSTHROUGHS = {
693
691
  getSource: "getText",
@@ -726,7 +724,157 @@ const BASE_TRAVERSAL_CONTEXT = Object.freeze(
726
724
  )
727
725
  );
728
726
 
727
+ /**
728
+ * Runs the given rules on the given SourceCode object
729
+ * @param {SourceCode} sourceCode A SourceCode object for the given text
730
+ * @param {Object} configuredRules The rules configuration
731
+ * @param {function(string): Rule} ruleMapper A mapper function from rule names to rules
732
+ * @param {Object} parserOptions The options that were passed to the parser
733
+ * @param {string} parserName The name of the parser in the config
734
+ * @param {Object} settings The settings that were enabled in the config
735
+ * @param {string} filename The reported filename of the code
736
+ * @returns {Problem[]} An array of reported problems
737
+ */
738
+ function runRules(sourceCode, configuredRules, ruleMapper, parserOptions, parserName, settings, filename) {
739
+ const emitter = createEmitter();
740
+ const traverser = new Traverser();
741
+
742
+ /*
743
+ * Create a frozen object with the ruleContext properties and methods that are shared by all rules.
744
+ * All rule contexts will inherit from this object. This avoids the performance penalty of copying all the
745
+ * properties once for each rule.
746
+ */
747
+ const sharedTraversalContext = Object.freeze(
748
+ Object.assign(
749
+ Object.create(BASE_TRAVERSAL_CONTEXT),
750
+ {
751
+ getAncestors: () => traverser.parents(),
752
+ getDeclaredVariables: sourceCode.scopeManager.getDeclaredVariables.bind(sourceCode.scopeManager),
753
+ getFilename: () => filename,
754
+ getScope: () => getScope(sourceCode.scopeManager, traverser.current(), parserOptions.ecmaVersion),
755
+ getSourceCode: () => sourceCode,
756
+ markVariableAsUsed: name => markVariableAsUsed(sourceCode.scopeManager, traverser.current(), parserOptions, name),
757
+ parserOptions,
758
+ parserPath: parserName,
759
+ parserServices: sourceCode.parserServices,
760
+ settings,
761
+
762
+ /**
763
+ * This is used to avoid breaking rules that used to monkeypatch the `Linter#report` method
764
+ * by using the `_linter` property on rule contexts.
765
+ *
766
+ * This should be removed in a major release after we create a better way to
767
+ * lint for unused disable comments.
768
+ * https://github.com/eslint/eslint/issues/9193
769
+ */
770
+ _linter: {
771
+ report() {},
772
+ on: emitter.on
773
+ }
774
+ }
775
+ )
776
+ );
777
+
778
+
779
+ const lintingProblems = [];
780
+
781
+ Object.keys(configuredRules).forEach(ruleId => {
782
+ const severity = ConfigOps.getRuleSeverity(configuredRules[ruleId]);
783
+
784
+ if (severity === 0) {
785
+ return;
786
+ }
787
+
788
+ const rule = ruleMapper(ruleId);
789
+ const messageIds = rule.meta && rule.meta.messages;
790
+ let reportTranslator = null;
791
+ const ruleContext = Object.freeze(
792
+ Object.assign(
793
+ Object.create(sharedTraversalContext),
794
+ {
795
+ id: ruleId,
796
+ options: getRuleOptions(configuredRules[ruleId]),
797
+ report() {
798
+
799
+ /*
800
+ * Create a report translator lazily.
801
+ * In a vast majority of cases, any given rule reports zero errors on a given
802
+ * piece of code. Creating a translator lazily avoids the performance cost of
803
+ * creating a new translator function for each rule that usually doesn't get
804
+ * called.
805
+ *
806
+ * Using lazy report translators improves end-to-end performance by about 3%
807
+ * with Node 8.4.0.
808
+ */
809
+ if (reportTranslator === null) {
810
+ reportTranslator = createReportTranslator({ ruleId, severity, sourceCode, messageIds });
811
+ }
812
+ const problem = reportTranslator.apply(null, arguments);
813
+
814
+ if (problem.fix && rule.meta && !rule.meta.fixable) {
815
+ throw new Error("Fixable rules should export a `meta.fixable` property.");
816
+ }
817
+ lintingProblems.push(problem);
818
+
819
+ /*
820
+ * This is used to avoid breaking rules that used monkeypatch Linter, and relied on
821
+ * `linter.report` getting called with report info every time a rule reports a problem.
822
+ * To continue to support this, make sure that `context._linter.report` is called every
823
+ * time a problem is reported by a rule, even though `context._linter` is no longer a
824
+ * `Linter` instance.
825
+ *
826
+ * This should be removed in a major release after we create a better way to
827
+ * lint for unused disable comments.
828
+ * https://github.com/eslint/eslint/issues/9193
829
+ */
830
+ sharedTraversalContext._linter.report( // eslint-disable-line no-underscore-dangle
831
+ problem.ruleId,
832
+ problem.severity,
833
+ { loc: { start: { line: problem.line, column: problem.column - 1 } } },
834
+ problem.message
835
+ );
836
+ }
837
+ }
838
+ )
839
+ );
840
+
841
+ const ruleListeners = createRuleListeners(rule, ruleContext);
842
+
843
+ // add all the selectors from the rule as listeners
844
+ Object.keys(ruleListeners).forEach(selector => {
845
+ emitter.on(
846
+ selector,
847
+ timing.enabled
848
+ ? timing.time(ruleId, ruleListeners[selector])
849
+ : ruleListeners[selector]
850
+ );
851
+ });
852
+ });
853
+
854
+ const eventGenerator = new CodePathAnalyzer(new NodeEventGenerator(emitter));
855
+
856
+ /*
857
+ * Each node has a type property. Whenever a particular type of
858
+ * node is found, an event is fired. This allows any listeners to
859
+ * automatically be informed that this type of node has been found
860
+ * and react accordingly.
861
+ */
862
+ traverser.traverse(sourceCode.ast, {
863
+ enter(node, parent) {
864
+ node.parent = parent;
865
+ eventGenerator.enterNode(node);
866
+ },
867
+ leave(node) {
868
+ eventGenerator.leaveNode(node);
869
+ },
870
+ visitorKeys: sourceCode.visitorKeys
871
+ });
872
+
873
+ return lintingProblems;
874
+ }
875
+
729
876
  const lastSourceCodes = new WeakMap();
877
+ const loadedParserMaps = new WeakMap();
730
878
 
731
879
  //------------------------------------------------------------------------------
732
880
  // Public Interface
@@ -740,10 +888,10 @@ module.exports = class Linter {
740
888
 
741
889
  constructor() {
742
890
  lastSourceCodes.set(this, null);
891
+ loadedParserMaps.set(this, new Map());
743
892
  this.version = pkg.version;
744
893
 
745
894
  this.rules = new Rules();
746
- this._parsers = new Map();
747
895
  this.environments = new Environments();
748
896
  }
749
897
 
@@ -761,7 +909,7 @@ module.exports = class Linter {
761
909
  /**
762
910
  * Same as linter.verify, except without support for processors.
763
911
  * @param {string|SourceCode} textOrSourceCode The text to parse or a SourceCode object.
764
- * @param {ESLintConfig} config An ESLintConfig instance to configure everything.
912
+ * @param {ESLintConfig} providedConfig An ESLintConfig instance to configure everything.
765
913
  * @param {(string|Object)} [filenameOrOptions] The optional filename of the file being checked.
766
914
  * If this is not set, the filename will default to '<input>' in the rule context. If
767
915
  * an object, then it has "filename", "saveState", and "allowInlineConfig" properties.
@@ -769,23 +917,14 @@ module.exports = class Linter {
769
917
  * Useful if you want to validate JS without comments overriding rules.
770
918
  * @param {boolean} [filenameOrOptions.reportUnusedDisableDirectives=false] Adds reported errors for unused
771
919
  * eslint-disable directives
772
- * @returns {Object[]} The results as an array of messages or null if no messages.
920
+ * @returns {Object[]} The results as an array of messages or an empty array if no messages.
773
921
  */
774
- _verifyWithoutProcessors(textOrSourceCode, config, filenameOrOptions) {
775
- let text,
776
- allowInlineConfig,
777
- providedFilename,
778
- reportUnusedDisableDirectives;
922
+ _verifyWithoutProcessors(textOrSourceCode, providedConfig, filenameOrOptions) {
923
+ const config = providedConfig || {};
924
+ const options = normalizeVerifyOptions(filenameOrOptions);
925
+ let text;
779
926
 
780
927
  // evaluate arguments
781
- if (typeof filenameOrOptions === "object") {
782
- providedFilename = filenameOrOptions.filename;
783
- allowInlineConfig = filenameOrOptions.allowInlineConfig;
784
- reportUnusedDisableDirectives = filenameOrOptions.reportUnusedDisableDirectives;
785
- } else {
786
- providedFilename = filenameOrOptions;
787
- }
788
-
789
928
  if (typeof textOrSourceCode === "string") {
790
929
  lastSourceCodes.set(this, null);
791
930
  text = textOrSourceCode;
@@ -794,23 +933,18 @@ module.exports = class Linter {
794
933
  text = textOrSourceCode.text;
795
934
  }
796
935
 
797
- const filename = typeof providedFilename === "string" ? providedFilename : "<input>";
798
-
799
936
  // search and apply "eslint-env *".
800
937
  const envInFile = findEslintEnv(text);
938
+ const resolvedEnvConfig = Object.assign({ builtin: true }, config.env, envInFile);
939
+ const enabledEnvs = Object.keys(resolvedEnvConfig)
940
+ .filter(envName => resolvedEnvConfig[envName])
941
+ .map(envName => this.environments.get(envName))
942
+ .filter(env => env);
801
943
 
802
- config = Object.assign({}, config);
803
-
804
- if (envInFile) {
805
- if (config.env) {
806
- config.env = Object.assign({}, config.env, envInFile);
807
- } else {
808
- config.env = envInFile;
809
- }
810
- }
811
-
812
- // process initial config to make it safe to extend
813
- config = prepareConfig(config, this.environments);
944
+ const parserOptions = resolveParserOptions(config.parserOptions || {}, enabledEnvs);
945
+ const configuredGlobals = resolveGlobals(config.globals || {}, enabledEnvs);
946
+ const parserName = config.parser || DEFAULT_PARSER_NAME;
947
+ const settings = config.settings || {};
814
948
 
815
949
  if (!lastSourceCodes.get(this)) {
816
950
 
@@ -820,26 +954,12 @@ module.exports = class Linter {
820
954
  return [];
821
955
  }
822
956
 
823
- let parser;
824
-
825
- try {
826
- parser = this._parsers.get(config.parser) || require(config.parser);
827
- } catch (ex) {
828
- return [{
829
- ruleId: null,
830
- fatal: true,
831
- severity: 2,
832
- source: null,
833
- message: ex.message,
834
- line: 0,
835
- column: 0
836
- }];
837
- }
838
957
  const parseResult = parse(
839
958
  text,
840
- config.parserOptions,
841
- parser,
842
- filename
959
+ parserOptions,
960
+ parserName,
961
+ loadedParserMaps.get(this),
962
+ options.filename
843
963
  );
844
964
 
845
965
  if (!parseResult.success) {
@@ -861,171 +981,41 @@ module.exports = class Linter {
861
981
  ast: lastSourceCode.ast,
862
982
  parserServices: lastSourceCode.parserServices,
863
983
  visitorKeys: lastSourceCode.visitorKeys,
864
- scopeManager: analyzeScope(lastSourceCode.ast, config.parserOptions)
984
+ scopeManager: analyzeScope(lastSourceCode.ast, parserOptions)
865
985
  }));
866
986
  }
867
987
  }
868
988
 
869
- const problems = [];
870
989
  const sourceCode = lastSourceCodes.get(this);
871
- let disableDirectives;
872
-
873
- // parse global comments and modify config
874
- if (allowInlineConfig !== false) {
875
- const modifyConfigResult = modifyConfigsFromComments(filename, sourceCode.ast, config, ruleId => this.rules.get(ruleId));
876
-
877
- config = modifyConfigResult.config;
878
- modifyConfigResult.problems.forEach(problem => problems.push(problem));
879
- disableDirectives = modifyConfigResult.disableDirectives;
880
- } else {
881
- disableDirectives = [];
882
- }
883
-
884
- const emitter = createEmitter();
885
- const traverser = new Traverser();
886
- const scopeManager = sourceCode.scopeManager;
887
-
888
- /*
889
- * Create a frozen object with the ruleContext properties and methods that are shared by all rules.
890
- * All rule contexts will inherit from this object. This avoids the performance penalty of copying all the
891
- * properties once for each rule.
892
- */
893
- const sharedTraversalContext = Object.freeze(
894
- Object.assign(
895
- Object.create(BASE_TRAVERSAL_CONTEXT),
896
- {
897
- getAncestors: () => traverser.parents(),
898
- getDeclaredVariables: scopeManager.getDeclaredVariables.bind(scopeManager),
899
- getFilename: () => filename,
900
- getScope: () => getScope(scopeManager, traverser.current(), config.parserOptions.ecmaVersion),
901
- getSourceCode: () => sourceCode,
902
- markVariableAsUsed: name => markVariableAsUsed(scopeManager, traverser.current(), config.parserOptions, name),
903
- parserOptions: config.parserOptions,
904
- parserPath: config.parser,
905
- parserServices: sourceCode.parserServices,
906
- settings: config.settings,
907
-
908
- /**
909
- * This is used to avoid breaking rules that used to monkeypatch the `Linter#report` method
910
- * by using the `_linter` property on rule contexts.
911
- *
912
- * This should be removed in a major release after we create a better way to
913
- * lint for unused disable comments.
914
- * https://github.com/eslint/eslint/issues/9193
915
- */
916
- _linter: {
917
- report() {},
918
- on: emitter.on
919
- }
920
- }
921
- )
922
- );
923
-
924
- // enable appropriate rules
925
- Object.keys(config.rules).forEach(ruleId => {
926
- const severity = ConfigOps.getRuleSeverity(config.rules[ruleId]);
927
-
928
- if (severity === 0) {
929
- return;
930
- }
931
-
932
- const rule = this.rules.get(ruleId);
933
- const messageIds = rule && rule.meta && rule.meta.messages;
934
- let reportTranslator = null;
935
- const ruleContext = Object.freeze(
936
- Object.assign(
937
- Object.create(sharedTraversalContext),
938
- {
939
- id: ruleId,
940
- options: getRuleOptions(config.rules[ruleId]),
941
- report() {
942
-
943
- /*
944
- * Create a report translator lazily.
945
- * In a vast majority of cases, any given rule reports zero errors on a given
946
- * piece of code. Creating a translator lazily avoids the performance cost of
947
- * creating a new translator function for each rule that usually doesn't get
948
- * called.
949
- *
950
- * Using lazy report translators improves end-to-end performance by about 3%
951
- * with Node 8.4.0.
952
- */
953
- if (reportTranslator === null) {
954
- reportTranslator = createReportTranslator({ ruleId, severity, sourceCode, messageIds });
955
- }
956
- const problem = reportTranslator.apply(null, arguments);
957
-
958
- if (problem.fix && rule.meta && !rule.meta.fixable) {
959
- throw new Error("Fixable rules should export a `meta.fixable` property.");
960
- }
961
- problems.push(problem);
962
-
963
- /*
964
- * This is used to avoid breaking rules that used monkeypatch Linter, and relied on
965
- * `linter.report` getting called with report info every time a rule reports a problem.
966
- * To continue to support this, make sure that `context._linter.report` is called every
967
- * time a problem is reported by a rule, even though `context._linter` is no longer a
968
- * `Linter` instance.
969
- *
970
- * This should be removed in a major release after we create a better way to
971
- * lint for unused disable comments.
972
- * https://github.com/eslint/eslint/issues/9193
973
- */
974
- sharedTraversalContext._linter.report( // eslint-disable-line no-underscore-dangle
975
- problem.ruleId,
976
- problem.severity,
977
- { loc: { start: { line: problem.line, column: problem.column - 1 } } },
978
- problem.message
979
- );
980
- }
981
- }
982
- )
983
- );
984
-
985
- try {
986
- const ruleListeners = rule.create(ruleContext);
987
-
988
- // add all the selectors from the rule as listeners
989
- Object.keys(ruleListeners).forEach(selector => {
990
- emitter.on(
991
- selector,
992
- timing.enabled
993
- ? timing.time(ruleId, ruleListeners[selector])
994
- : ruleListeners[selector]
995
- );
996
- });
997
- } catch (ex) {
998
- ex.message = `Error while loading rule '${ruleId}': ${ex.message}`;
999
- throw ex;
1000
- }
1001
- });
990
+ const commentDirectives = options.allowInlineConfig
991
+ ? getDirectiveComments(options.filename, sourceCode.ast, ruleId => this.rules.get(ruleId))
992
+ : { configuredRules: {}, enabledGlobals: {}, exportedVariables: {}, problems: [], disableDirectives: [] };
1002
993
 
1003
994
  // augment global scope with declared global variables
1004
- addDeclaredGlobals(scopeManager.scopes[0], config, this.environments);
995
+ addDeclaredGlobals(
996
+ sourceCode.scopeManager.scopes[0],
997
+ configuredGlobals,
998
+ { exportedVariables: commentDirectives.exportedVariables, enabledGlobals: commentDirectives.enabledGlobals }
999
+ );
1005
1000
 
1006
- const eventGenerator = new CodePathAnalyzer(new NodeEventGenerator(emitter));
1001
+ const configuredRules = Object.assign({}, config.rules, commentDirectives.configuredRules);
1007
1002
 
1008
- /*
1009
- * Each node has a type property. Whenever a particular type of
1010
- * node is found, an event is fired. This allows any listeners to
1011
- * automatically be informed that this type of node has been found
1012
- * and react accordingly.
1013
- */
1014
- traverser.traverse(sourceCode.ast, {
1015
- enter(node, parent) {
1016
- node.parent = parent;
1017
- eventGenerator.enterNode(node);
1018
- },
1019
- leave(node) {
1020
- eventGenerator.leaveNode(node);
1021
- },
1022
- visitorKeys: sourceCode.visitorKeys
1023
- });
1003
+ const lintingProblems = runRules(
1004
+ sourceCode,
1005
+ configuredRules,
1006
+ ruleId => this.rules.get(ruleId),
1007
+ parserOptions,
1008
+ parserName,
1009
+ settings,
1010
+ options.filename
1011
+ );
1024
1012
 
1025
1013
  return applyDisableDirectives({
1026
- directives: disableDirectives,
1027
- problems: problems.sort((problemA, problemB) => problemA.line - problemB.line || problemA.column - problemB.column),
1028
- reportUnusedDisableDirectives
1014
+ directives: commentDirectives.disableDirectives,
1015
+ problems: lintingProblems
1016
+ .concat(commentDirectives.problems)
1017
+ .sort((problemA, problemB) => problemA.line - problemB.line || problemA.column - problemB.column),
1018
+ reportUnusedDisableDirectives: options.reportUnusedDisableDirectives
1029
1019
  });
1030
1020
  }
1031
1021
 
@@ -1045,7 +1035,7 @@ module.exports = class Linter {
1045
1035
  * @param {function(Array<Object[]>): Object[]} [filenameOrOptions.postprocess] postprocessor for report messages. If provided,
1046
1036
  * this should accept an array of the message lists for each code block returned from the preprocessor,
1047
1037
  * apply a mapping to the messages as appropriate, and return a one-dimensional array of messages
1048
- * @returns {Object[]} The results as an array of messages or null if no messages.
1038
+ * @returns {Object[]} The results as an array of messages or an empty array if no messages.
1049
1039
  */
1050
1040
  verify(textOrSourceCode, config, filenameOrOptions) {
1051
1041
  const preprocess = filenameOrOptions && filenameOrOptions.preprocess || (rawText => [rawText]);
@@ -1102,7 +1092,7 @@ module.exports = class Linter {
1102
1092
  * @returns {void}
1103
1093
  */
1104
1094
  defineParser(parserId, parserModule) {
1105
- this._parsers.set(parserId, parserModule);
1095
+ loadedParserMaps.get(this).set(parserId, parserModule);
1106
1096
  }
1107
1097
 
1108
1098
  /**
@@ -1127,7 +1117,8 @@ module.exports = class Linter {
1127
1117
  let messages = [],
1128
1118
  fixedResult,
1129
1119
  fixed = false,
1130
- passNumber = 0;
1120
+ passNumber = 0,
1121
+ currentText = text;
1131
1122
  const debugTextDescription = options && options.filename || `${text.slice(0, 10)}...`;
1132
1123
  const shouldFix = options && typeof options.fix !== "undefined" ? options.fix : true;
1133
1124
 
@@ -1144,10 +1135,10 @@ module.exports = class Linter {
1144
1135
  passNumber++;
1145
1136
 
1146
1137
  debug(`Linting code for ${debugTextDescription} (pass ${passNumber})`);
1147
- messages = this.verify(text, config, options);
1138
+ messages = this.verify(currentText, config, options);
1148
1139
 
1149
1140
  debug(`Generating fixed text for ${debugTextDescription} (pass ${passNumber})`);
1150
- fixedResult = SourceCodeFixer.applyFixes(text, messages, shouldFix);
1141
+ fixedResult = SourceCodeFixer.applyFixes(currentText, messages, shouldFix);
1151
1142
 
1152
1143
  /*
1153
1144
  * stop if there are any syntax errors.
@@ -1161,7 +1152,7 @@ module.exports = class Linter {
1161
1152
  fixed = fixed || fixedResult.fixed;
1162
1153
 
1163
1154
  // update to use the fixed output instead of the original text
1164
- text = fixedResult.output;
1155
+ currentText = fixedResult.output;
1165
1156
 
1166
1157
  } while (
1167
1158
  fixedResult.fixed &&
@@ -1173,12 +1164,12 @@ module.exports = class Linter {
1173
1164
  * the most up-to-date information.
1174
1165
  */
1175
1166
  if (fixedResult.fixed) {
1176
- fixedResult.messages = this.verify(text, config, options);
1167
+ fixedResult.messages = this.verify(currentText, config, options);
1177
1168
  }
1178
1169
 
1179
1170
  // ensure the last result properly reflects if fixes were done
1180
1171
  fixedResult.fixed = fixed;
1181
- fixedResult.output = text;
1172
+ fixedResult.output = currentText;
1182
1173
 
1183
1174
  return fixedResult;
1184
1175
  }