eslint 6.7.2 → 6.8.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,33 @@
1
+ v6.8.0 - December 20, 2019
2
+
3
+ * [`c5c7086`](https://github.com/eslint/eslint/commit/c5c708666b450fb69522a55aa375626f9297dc6f) Fix: ignore aligning single line in key-spacing (fixes #11414) (#12652) (YeonJuan)
4
+ * [`9986d9e`](https://github.com/eslint/eslint/commit/9986d9e0baed0d3586bbee472fe2fae2ed625f5d) Chore: add object option test cases in yield-star-spacing (#12679) (YeonJuan)
5
+ * [`1713d07`](https://github.com/eslint/eslint/commit/1713d0758b083f3840d724505f997a7cb20ff384) New: Add no-error-on-unmatched-pattern flag (fixes #10587) (#12377) (ncraley)
6
+ * [`5c25a26`](https://github.com/eslint/eslint/commit/5c25a26608fbd9a1d0127c9a3653609aa4b63e86) Update: autofix bug in lines-between-class-members (fixes #12391) (#12632) (YeonJuan)
7
+ * [`4b3cc5c`](https://github.com/eslint/eslint/commit/4b3cc5cd2459f04eae149faea0651785d7f9db0b) Chore: enable prefer-regex-literals in eslint codebase (#12268) (薛定谔的猫)
8
+ * [`05faebb`](https://github.com/eslint/eslint/commit/05faebb943456ad2b20117f3c8b3eccbe2e2fb03) Update: improve suggestion testing experience (#12602) (Brad Zacher)
9
+ * [`05f7dd5`](https://github.com/eslint/eslint/commit/05f7dd53ed91a6e3be9eb40825fb6d2207f82209) Update: Add suggestions for no-unsafe-negation (fixes #12591) (#12609) (Milos Djermanovic)
10
+ * [`d3e43f1`](https://github.com/eslint/eslint/commit/d3e43f1c10c5e19f40e7b3d3944b87f1b0c9c075) Docs: Update no-multi-assign explanation (#12615) (Yuping Zuo)
11
+ * [`272e4db`](https://github.com/eslint/eslint/commit/272e4db6074283bc01cc6ec72c9e396bb3c110e6) Fix: no-multiple-empty-lines: Adjust reported `loc` (#12594) (Tobias Bieniek)
12
+ * [`a258039`](https://github.com/eslint/eslint/commit/a258039e556075d7d1f955a79d094ea103ec165a) Fix: no-restricted-imports schema allows multiple paths/patterns objects (#12639) (Milos Djermanovic)
13
+ * [`51f9620`](https://github.com/eslint/eslint/commit/51f9620cc55cc091fe38dbe68e4633de06297b8c) Fix: improve report location for array-bracket-spacing (#12653) (Milos Djermanovic)
14
+ * [`45364af`](https://github.com/eslint/eslint/commit/45364afc9c7f0251348cd1a7a13656c3816435d7) Fix: prefer-numeric-literals doesn't check types of literal arguments (#12655) (Milos Djermanovic)
15
+ * [`e3c570e`](https://github.com/eslint/eslint/commit/e3c570eaf3d1d44fb57bf42f1870887856e4c5a0) Docs: Add example for expression option (#12694) (Arnaud Barré)
16
+ * [`6b774ef`](https://github.com/eslint/eslint/commit/6b774ef0d849ccf5c1127b25e1fe7c3e438d586b) Docs: Add spacing in comments for no-console rule (#12696) (Nikki Nikkhoui)
17
+ * [`7171fca`](https://github.com/eslint/eslint/commit/7171fca6ef4e0e8f267658fc7d8f603f00eddd84) Chore: refactor regex in config comment parser (#12662) (Milos Djermanovic)
18
+ * [`1600648`](https://github.com/eslint/eslint/commit/1600648d2880ffb1e9e414b31ff0f66ead7167f9) Update: Allow $schema in config (#12612) (Yordis Prieto)
19
+ * [`acc0e47`](https://github.com/eslint/eslint/commit/acc0e47572a9390292b4e313b4a4bf360d236358) Update: support .eslintrc.cjs (refs eslint/rfcs#43) (#12321) (Evan Plaice)
20
+ * [`49c1658`](https://github.com/eslint/eslint/commit/49c1658544ace24b9aaaa301af0fc07a2ef3bf30) Chore: remove bundling of ESLint during release (#12676) (Kai Cataldo)
21
+ * [`257f3d6`](https://github.com/eslint/eslint/commit/257f3d67905a52bf8602a5a5707c893cc90d7ca7) Chore: complete to move to GitHub Actions (#12625) (Toru Nagashima)
22
+ * [`ab912f0`](https://github.com/eslint/eslint/commit/ab912f0ef709a916ab9a27ea09d9d7adf046fb2d) Docs: 1tbs with allowSingleLine edge cases (refs #12284) (#12314) (Ari Kardasis)
23
+ * [`dd1c30e`](https://github.com/eslint/eslint/commit/dd1c30e35f05ed332e2abbd3d4d53635efde74b8) Sponsors: Sync README with website (ESLint Jenkins)
24
+ * [`a230f84`](https://github.com/eslint/eslint/commit/a230f8404e4f2423dd79378b065d24c12776775b) Update: include node version in cache (#12582) (Eric Wang)
25
+ * [`8b65f17`](https://github.com/eslint/eslint/commit/8b65f175dfb4fac11ed7184537be400ed14996fb) Chore: remove references to parser demo (#12644) (Kai Cataldo)
26
+ * [`e9cef99`](https://github.com/eslint/eslint/commit/e9cef99e6ebec1faefdb576ca597e81ae4f04afd) Docs: wrap {{}} in raw liquid tags to prevent interpolation (#12643) (Kai Cataldo)
27
+ * [`e707453`](https://github.com/eslint/eslint/commit/e70745325ff9e085acc6843dd8bfae5550645d4f) Docs: Fix configuration example in no-restricted-imports (fixes #11717) (#12638) (Milos Djermanovic)
28
+ * [`19194ce`](https://github.com/eslint/eslint/commit/19194cec724e016df02376bbeae31171be6f0bdf) Chore: Add tests to cover default object options in comma-dangle (#12627) (YeonJuan)
29
+ * [`6e36d12`](https://github.com/eslint/eslint/commit/6e36d12d95e76022172fd0ec8a5e85c22fde6a8a) Update: do not recommend require-atomic-updates (refs #11899) (#12599) (Kai Cataldo)
30
+
1
31
  v6.7.2 - November 30, 2019
2
32
 
3
33
  * [`bc435a9`](https://github.com/eslint/eslint/commit/bc435a93afd6ba4def1b53993ef7cf8220f3f070) Fix: isSpaceBetweenTokens() recognizes spaces in JSXText (fixes #12614) (#12616) (Toru Nagashima)
package/README.md CHANGED
@@ -1,5 +1,4 @@
1
1
  [![NPM version][npm-image]][npm-url]
2
- [![Build Status](https://dev.azure.com/eslint/eslint/_apis/build/status/eslint.eslint?branchName=master)](https://dev.azure.com/eslint/eslint/_build/latest?definitionId=1&branchName=master)
3
2
  [![Build Status](https://github.com/eslint/eslint/workflows/CI/badge.svg)](https://github.com/eslint/eslint/actions)
4
3
  [![Downloads][downloads-image]][downloads-url]
5
4
  [![Bountysource](https://www.bountysource.com/badge/tracker?tracker_id=282608)](https://www.bountysource.com/trackers/282608-eslint?utm_source=282608&utm_medium=shield&utm_campaign=TRACKER_BADGE)
@@ -265,7 +264,7 @@ The following companies, organizations, and individuals support ESLint's ongoing
265
264
  <h3>Gold Sponsors</h3>
266
265
  <p><a href="https://www.shopify.com"><img src="https://images.opencollective.com/shopify/eeb91aa/logo.png" alt="Shopify" height="96"></a> <a href="http://engineering.salesforce.com"><img src="https://images.opencollective.com/salesforce/ca8f997/logo.png" alt="Salesforce" height="96"></a> <a href="https://badoo.com/team?utm_source=eslint"><img src="https://images.opencollective.com/badoo/2826a3b/logo.png" alt="Badoo" height="96"></a> <a href="https://www.airbnb.com/"><img src="https://images.opencollective.com/airbnb/d327d66/logo.png" alt="Airbnb" height="96"></a> <a href="https://opensource.facebook.com"><img src="https://images.opencollective.com/fbopensource/fbb8a5b/logo.png" alt="Facebook Open Source" height="96"></a></p><h3>Silver Sponsors</h3>
267
266
  <p><a href="https://www.ampproject.org/"><img src="https://images.opencollective.com/amp/c8a3b25/logo.png" alt="AMP Project" height="64"></a></p><h3>Bronze Sponsors</h3>
268
- <p><a href="https://uxplanet.org/top-ui-ux-design-agencies-user-experience-firms-8c54697e290"><img src="https://images.opencollective.com/ui-ux-design-agencies/cae5dfe/logo.png" alt="UI UX Design Agencies" height="32"></a> <a href="https://edubirdie.com/"><img src="https://images.opencollective.com/edubirdie2/b1d51ab/logo.png" alt="EduBirdie" height="32"></a> <a href="https://www.crosswordsolver.com"><img src="https://images.opencollective.com/crosswordsolver/d4481d6/logo.png" alt="Crosswordsolver" height="32"></a> <a href="https://www.codacy.com/?utm_source=eslint&utm_medium=cpm&utm_campaign=eslint-sponsorship"><img src="https://images.opencollective.com/codacy/ed22716/logo.png" alt="Codacy" height="32"></a> <a href="https://mixpanel.com"><img src="https://images.opencollective.com/mixpanel/cd682f7/logo.png" alt="Mixpanel" height="32"></a> <a href="https://www.vpsserver.com"><img src="https://images.opencollective.com/vpsservercom/logo.png" alt="VPS Server" height="32"></a> <a href="https://icons8.com"><img src="https://images.opencollective.com/icons8/0b37d14/logo.png" alt="Free Icons by Icons8" height="32"></a> <a href="https://www.bugsnag.com/platforms?utm_source=Open Collective&utm_medium=Website&utm_content=open-source&utm_campaign=2019-community&utm_term="><img src="https://images.opencollective.com/bugsnag-stability-monitoring/c2cef36/logo.png" alt="Bugsnag Stability Monitoring" height="32"></a> <a href="https://clay.global"><img src="https://images.opencollective.com/clayglobal/2468f34/logo.png" alt="clay" height="32"></a> <a href="https://discordapp.com"><img src="https://images.opencollective.com/discordapp/7e3d9a9/logo.png" alt="Discord" height="32"></a> <a href="https://themeisle.com"><img src="https://images.opencollective.com/themeisle/d5592fe/logo.png" alt="ThemeIsle" height="32"></a> <a href="https://tekhattan.com"><img src="https://images.opencollective.com/tekhattan/bc73c28/logo.png" alt="TekHattan" height="32"></a> <a href="https://www.marfeel.com/"><img src="https://images.opencollective.com/marfeel/4b88e30/logo.png" alt="Marfeel" height="32"></a> <a href="http://www.firesticktricks.com"><img src="https://images.opencollective.com/fire-stick-tricks/b8fbe2c/logo.png" alt="Fire Stick Tricks" height="32"></a> <a href="https://jsheroes.io/"><img src="https://images.opencollective.com/jsheroes1/9fedf0b/logo.png" alt="JSHeroes " height="32"></a></p>
267
+ <p><a href="https://uxplanet.org/top-ui-ux-design-agencies-user-experience-firms-8c54697e290"><img src="https://images.opencollective.com/ui-ux-design-agencies/cae5dfe/logo.png" alt="UI UX Design Agencies" height="32"></a> <a href="https://medium.com/@niksundin/best-web-design-companies-1872e445775f"><img src="https://images.opencollective.com/top-web-design-agencies/d92d747/logo.png" alt="Top Web Design Agencies" height="32"></a> <a href="https://www.bugsnag.com/platforms?utm_source=Open Collective&utm_medium=Website&utm_content=open-source&utm_campaign=2019-community&utm_term="><img src="https://images.opencollective.com/bugsnag-stability-monitoring/c2cef36/logo.png" alt="Bugsnag Stability Monitoring" height="32"></a> <a href="https://www.crosswordsolver.com"><img src="https://images.opencollective.com/crosswordsolver/d4481d6/logo.png" alt="Crosswordsolver" height="32"></a> <a href="https://www.codacy.com/?utm_source=eslint&utm_medium=cpm&utm_campaign=eslint-sponsorship"><img src="https://images.opencollective.com/codacy/ed22716/logo.png" alt="Codacy" height="32"></a> <a href="https://mixpanel.com"><img src="https://images.opencollective.com/mixpanel/cd682f7/logo.png" alt="Mixpanel" height="32"></a> <a href="https://www.vpsserver.com"><img src="https://images.opencollective.com/vpsservercom/logo.png" alt="VPS Server" height="32"></a> <a href="https://icons8.com"><img src="https://images.opencollective.com/icons8/0b37d14/logo.png" alt="Free Icons by Icons8" height="32"></a> <a href="https://edubirdie.com/"><img src="https://images.opencollective.com/edubirdie2/b1d51ab/logo.png" alt="EduBirdie" height="32"></a> <a href="https://clay.global"><img src="https://images.opencollective.com/clayglobal/2468f34/logo.png" alt="clay" height="32"></a> <a href="https://discordapp.com"><img src="https://images.opencollective.com/discordapp/7e3d9a9/logo.png" alt="Discord" height="32"></a> <a href="https://themeisle.com"><img src="https://images.opencollective.com/themeisle/d5592fe/logo.png" alt="ThemeIsle" height="32"></a> <a href="https://tekhattan.com"><img src="https://images.opencollective.com/tekhattan/bc73c28/logo.png" alt="TekHattan" height="32"></a> <a href="https://www.marfeel.com/"><img src="https://images.opencollective.com/marfeel/4b88e30/logo.png" alt="Marfeel" height="32"></a> <a href="http://www.firesticktricks.com"><img src="https://images.opencollective.com/fire-stick-tricks/b8fbe2c/logo.png" alt="Fire Stick Tricks" height="32"></a> <a href="https://jsheroes.io/"><img src="https://images.opencollective.com/jsheroes1/9fedf0b/logo.png" alt="JSHeroes " height="32"></a></p>
269
268
  <!--sponsorsend-->
270
269
 
271
270
  ## <a name="technology-sponsors"></a>Technology Sponsors
@@ -6,6 +6,7 @@
6
6
  "use strict";
7
7
 
8
8
  const baseConfigProperties = {
9
+ $schema: { type: "string" },
9
10
  env: { type: "object" },
10
11
  extends: { $ref: "#/definitions/stringOrStrings" },
11
12
  globals: { type: "object" },
@@ -62,7 +62,6 @@ module.exports = {
62
62
  "no-useless-catch": "error",
63
63
  "no-useless-escape": "error",
64
64
  "no-with": "error",
65
- "require-atomic-updates": "error",
66
65
  "require-yield": "error",
67
66
  "use-isnan": "error",
68
67
  "valid-typeof": "error"
@@ -576,6 +576,7 @@ class CLIEngine {
576
576
  cwd: options.cwd,
577
577
  extensions: options.extensions,
578
578
  globInputPaths: options.globInputPaths,
579
+ errorOnUnmatchedPattern: options.errorOnUnmatchedPattern,
579
580
  ignore: options.ignore
580
581
  });
581
582
  const lintResultCache =
@@ -62,6 +62,7 @@ const eslintRecommendedPath = path.resolve(__dirname, "../../conf/eslint-recomme
62
62
  const eslintAllPath = path.resolve(__dirname, "../../conf/eslint-all.js");
63
63
  const configFilenames = [
64
64
  ".eslintrc.js",
65
+ ".eslintrc.cjs",
65
66
  ".eslintrc.yaml",
66
67
  ".eslintrc.yml",
67
68
  ".eslintrc.json",
@@ -279,6 +280,7 @@ function configMissingError(configName, importerName) {
279
280
  function loadConfigFile(filePath) {
280
281
  switch (path.extname(filePath)) {
281
282
  case ".js":
283
+ case ".cjs":
282
284
  return loadJSConfigFile(filePath);
283
285
 
284
286
  case ".json":
@@ -190,6 +190,7 @@ class FileEnumerator {
190
190
  configArrayFactory = new CascadingConfigArrayFactory({ cwd }),
191
191
  extensions = [".js"],
192
192
  globInputPaths = true,
193
+ errorOnUnmatchedPattern = true,
193
194
  ignore = true
194
195
  } = {}) {
195
196
  internalSlotsMap.set(this, {
@@ -208,6 +209,7 @@ class FileEnumerator {
208
209
  "u"
209
210
  ),
210
211
  globInputPaths,
212
+ errorOnUnmatchedPattern,
211
213
  ignoreFlag: ignore
212
214
  });
213
215
  }
@@ -226,7 +228,7 @@ class FileEnumerator {
226
228
  * @returns {IterableIterator<FileAndConfig>} The found files.
227
229
  */
228
230
  *iterateFiles(patternOrPatterns) {
229
- const { globInputPaths } = internalSlotsMap.get(this);
231
+ const { globInputPaths, errorOnUnmatchedPattern } = internalSlotsMap.get(this);
230
232
  const patterns = Array.isArray(patternOrPatterns)
231
233
  ? patternOrPatterns
232
234
  : [patternOrPatterns];
@@ -265,14 +267,16 @@ class FileEnumerator {
265
267
  }
266
268
 
267
269
  // Raise an error if any files were not found.
268
- if (!foundRegardlessOfIgnored) {
269
- throw new NoFilesFoundError(
270
- pattern,
271
- !globInputPaths && isGlob(pattern)
272
- );
273
- }
274
- if (!found) {
275
- throw new AllFilesIgnoredError(pattern);
270
+ if (errorOnUnmatchedPattern) {
271
+ if (!foundRegardlessOfIgnored) {
272
+ throw new NoFilesFoundError(
273
+ pattern,
274
+ !globInputPaths && isGlob(pattern)
275
+ );
276
+ }
277
+ if (!found) {
278
+ throw new AllFilesIgnoredError(pattern);
279
+ }
276
280
  }
277
281
  }
278
282
 
@@ -20,6 +20,7 @@ const hash = require("./hash");
20
20
  //-----------------------------------------------------------------------------
21
21
 
22
22
  const configHashCache = new WeakMap();
23
+ const nodeVersion = process && process.version;
23
24
 
24
25
  /**
25
26
  * Calculates the hash of the config
@@ -28,7 +29,7 @@ const configHashCache = new WeakMap();
28
29
  */
29
30
  function hashOfConfigFor(config) {
30
31
  if (!configHashCache.has(config)) {
31
- configHashCache.set(config, hash(`${pkg.version}_${stringify(config)}`));
32
+ configHashCache.set(config, hash(`${pkg.version}_${nodeVersion}_${stringify(config)}`));
32
33
  }
33
34
 
34
35
  return configHashCache.get(config);
package/lib/cli.js CHANGED
@@ -68,7 +68,8 @@ function translateOptions(cliOptions) {
68
68
  fixTypes: cliOptions.fixType,
69
69
  allowInlineConfig: cliOptions.inlineConfig,
70
70
  reportUnusedDisableDirectives: cliOptions.reportUnusedDisableDirectives,
71
- resolvePluginsRelativeTo: cliOptions.resolvePluginsRelativeTo
71
+ resolvePluginsRelativeTo: cliOptions.resolvePluginsRelativeTo,
72
+ errorOnUnmatchedPattern: cliOptions.errorOnUnmatchedPattern
72
73
  };
73
74
  }
74
75
 
@@ -90,7 +90,7 @@ module.exports = class ConfigCommentParser {
90
90
  * But we are supporting that. So this is a fallback for that.
91
91
  */
92
92
  items = {};
93
- const normalizedString = string.replace(/([a-zA-Z0-9\-/]+):/gu, "\"$1\":").replace(/(\]|[0-9])\s+(?=")/u, "$1,");
93
+ const normalizedString = string.replace(/([-a-zA-Z0-9/]+):/gu, "\"$1\":").replace(/(\]|[0-9])\s+(?=")/u, "$1,");
94
94
 
95
95
  try {
96
96
  items = JSON.parse(`{${normalizedString}}`);
package/lib/options.js CHANGED
@@ -230,6 +230,12 @@ module.exports = optionator({
230
230
  default: "false",
231
231
  description: "Output execution environment information"
232
232
  },
233
+ {
234
+ option: "error-on-unmatched-pattern",
235
+ type: "Boolean",
236
+ default: "true",
237
+ description: "Prevent errors when pattern is unmatched"
238
+ },
233
239
  {
234
240
  option: "debug",
235
241
  type: "Boolean",
@@ -596,8 +596,10 @@ class RuleTester {
596
596
  if (hasOwnProperty(error, "suggestions")) {
597
597
 
598
598
  // Support asserting there are no suggestions
599
- if (!error.suggestions) {
600
- assert.strictEqual(message.suggestions, error.suggestions, `Error should have no suggestions on error with message: "${message.message}"`);
599
+ if (!error.suggestions || (Array.isArray(error.suggestions) && error.suggestions.length === 0)) {
600
+ if (Array.isArray(message.suggestions) && message.suggestions.length > 0) {
601
+ assert.fail(`Error should have no suggestions on error with message: "${message.message}"`);
602
+ }
601
603
  } else {
602
604
  assert.strictEqual(Array.isArray(message.suggestions), true, `Error should have an array of suggestions. Instead received "${message.suggestions}" on error with message: "${message.message}"`);
603
605
  assert.strictEqual(message.suggestions.length, error.suggestions.length, `Error should have ${error.suggestions.length} suggestions. Instead found ${message.suggestions.length} suggestions`);
@@ -84,16 +84,16 @@ module.exports = {
84
84
  * @returns {void}
85
85
  */
86
86
  function reportNoBeginningSpace(node, token) {
87
+ const nextToken = sourceCode.getTokenAfter(token);
88
+
87
89
  context.report({
88
90
  node,
89
- loc: token.loc.start,
91
+ loc: { start: token.loc.end, end: nextToken.loc.start },
90
92
  messageId: "unexpectedSpaceAfter",
91
93
  data: {
92
94
  tokenValue: token.value
93
95
  },
94
96
  fix(fixer) {
95
- const nextToken = sourceCode.getTokenAfter(token);
96
-
97
97
  return fixer.removeRange([token.range[1], nextToken.range[0]]);
98
98
  }
99
99
  });
@@ -106,16 +106,16 @@ module.exports = {
106
106
  * @returns {void}
107
107
  */
108
108
  function reportNoEndingSpace(node, token) {
109
+ const previousToken = sourceCode.getTokenBefore(token);
110
+
109
111
  context.report({
110
112
  node,
111
- loc: token.loc.start,
113
+ loc: { start: previousToken.loc.end, end: token.loc.start },
112
114
  messageId: "unexpectedSpaceBefore",
113
115
  data: {
114
116
  tokenValue: token.value
115
117
  },
116
118
  fix(fixer) {
117
- const previousToken = sourceCode.getTokenBefore(token);
118
-
119
119
  return fixer.removeRange([previousToken.range[1], token.range[0]]);
120
120
  }
121
121
  });
@@ -130,7 +130,7 @@ module.exports = {
130
130
  function reportRequiredBeginningSpace(node, token) {
131
131
  context.report({
132
132
  node,
133
- loc: token.loc.start,
133
+ loc: token.loc,
134
134
  messageId: "missingSpaceAfter",
135
135
  data: {
136
136
  tokenValue: token.value
@@ -150,7 +150,7 @@ module.exports = {
150
150
  function reportRequiredEndingSpace(node, token) {
151
151
  context.report({
152
152
  node,
153
- loc: token.loc.start,
153
+ loc: token.loc,
154
154
  messageId: "missingSpaceBefore",
155
155
  data: {
156
156
  tokenValue: token.value
@@ -42,6 +42,18 @@ function isSingleLine(node) {
42
42
  return (node.loc.end.line === node.loc.start.line);
43
43
  }
44
44
 
45
+ /**
46
+ * Checks whether the properties on a single line.
47
+ * @param {ASTNode[]} properties List of Property AST nodes.
48
+ * @returns {boolean} True if all properies is on a single line.
49
+ */
50
+ function isSingleLineProperties(properties) {
51
+ const [firstProp] = properties,
52
+ lastProp = last(properties);
53
+
54
+ return firstProp.loc.start.line === lastProp.loc.end.line;
55
+ }
56
+
45
57
  /**
46
58
  * Initializes a single option property from the configuration with defaults for undefined values
47
59
  * @param {Object} toOptions Object to be initialized
@@ -583,17 +595,6 @@ module.exports = {
583
595
  }
584
596
  }
585
597
 
586
- /**
587
- * Verifies vertical alignment, taking into account groups of properties.
588
- * @param {ASTNode} node ObjectExpression node being evaluated.
589
- * @returns {void}
590
- */
591
- function verifyAlignment(node) {
592
- createGroups(node).forEach(group => {
593
- verifyGroupAlignment(group.filter(isKeyValueProperty));
594
- });
595
- }
596
-
597
598
  /**
598
599
  * Verifies spacing of property conforms to specified options.
599
600
  * @param {ASTNode} node Property node being evaluated.
@@ -611,17 +612,35 @@ module.exports = {
611
612
 
612
613
  /**
613
614
  * Verifies spacing of each property in a list.
614
- * @param {ASTNode[]} properties List of Property AST nodes.
615
+ * @param {ASTNode[]} properties List of Property AST nodes.
616
+ * @param {Object} lineOptions Configured singleLine or multiLine options
615
617
  * @returns {void}
616
618
  */
617
- function verifyListSpacing(properties) {
619
+ function verifyListSpacing(properties, lineOptions) {
618
620
  const length = properties.length;
619
621
 
620
622
  for (let i = 0; i < length; i++) {
621
- verifySpacing(properties[i], singleLineOptions);
623
+ verifySpacing(properties[i], lineOptions);
622
624
  }
623
625
  }
624
626
 
627
+ /**
628
+ * Verifies vertical alignment, taking into account groups of properties.
629
+ * @param {ASTNode} node ObjectExpression node being evaluated.
630
+ * @returns {void}
631
+ */
632
+ function verifyAlignment(node) {
633
+ createGroups(node).forEach(group => {
634
+ const properties = group.filter(isKeyValueProperty);
635
+
636
+ if (properties.length > 0 && isSingleLineProperties(properties)) {
637
+ verifyListSpacing(properties, multiLineOptions);
638
+ } else {
639
+ verifyGroupAlignment(properties);
640
+ }
641
+ });
642
+ }
643
+
625
644
  //--------------------------------------------------------------------------
626
645
  // Public API
627
646
  //--------------------------------------------------------------------------
@@ -631,7 +650,7 @@ module.exports = {
631
650
  return {
632
651
  ObjectExpression(node) {
633
652
  if (isSingleLine(node)) {
634
- verifyListSpacing(node.properties.filter(isKeyValueProperty));
653
+ verifyListSpacing(node.properties.filter(isKeyValueProperty), singleLineOptions);
635
654
  } else {
636
655
  verifyAlignment(node);
637
656
  }
@@ -54,62 +54,45 @@ module.exports = {
54
54
  const sourceCode = context.getSourceCode();
55
55
 
56
56
  /**
57
- * Checks if there is padding between two tokens
58
- * @param {Token} first The first token
59
- * @param {Token} second The second token
60
- * @returns {boolean} True if there is at least a line between the tokens
57
+ * Return the last token among the consecutive tokens that have no exceed max line difference in between, before the first token in the next member.
58
+ * @param {Token} prevLastToken The last token in the previous member node.
59
+ * @param {Token} nextFirstToken The first token in the next member node.
60
+ * @param {number} maxLine The maximum number of allowed line difference between consecutive tokens.
61
+ * @returns {Token} The last token among the consecutive tokens.
61
62
  */
62
- function isPaddingBetweenTokens(first, second) {
63
- const comments = sourceCode.getCommentsBefore(second);
64
- const len = comments.length;
63
+ function findLastConsecutiveTokenAfter(prevLastToken, nextFirstToken, maxLine) {
64
+ const after = sourceCode.getTokenAfter(prevLastToken, { includeComments: true });
65
65
 
66
- // If there is no comments
67
- if (len === 0) {
68
- const linesBetweenFstAndSnd = second.loc.start.line - first.loc.end.line - 1;
69
-
70
- return linesBetweenFstAndSnd >= 1;
71
- }
72
-
73
-
74
- // If there are comments
75
- let sumOfCommentLines = 0; // the numbers of lines of comments
76
- let prevCommentLineNum = -1; // line number of the end of the previous comment
77
-
78
- for (let i = 0; i < len; i++) {
79
- const commentLinesOfThisComment = comments[i].loc.end.line - comments[i].loc.start.line + 1;
80
-
81
- sumOfCommentLines += commentLinesOfThisComment;
82
-
83
- /*
84
- * If this comment and the previous comment are in the same line,
85
- * the count of comment lines is duplicated. So decrement sumOfCommentLines.
86
- */
87
- if (prevCommentLineNum === comments[i].loc.start.line) {
88
- sumOfCommentLines -= 1;
89
- }
90
-
91
- prevCommentLineNum = comments[i].loc.end.line;
66
+ if (after !== nextFirstToken && after.loc.start.line - prevLastToken.loc.end.line <= maxLine) {
67
+ return findLastConsecutiveTokenAfter(after, nextFirstToken, maxLine);
92
68
  }
69
+ return prevLastToken;
70
+ }
93
71
 
94
- /*
95
- * If the first block and the first comment are in the same line,
96
- * the count of comment lines is duplicated. So decrement sumOfCommentLines.
97
- */
98
- if (first.loc.end.line === comments[0].loc.start.line) {
99
- sumOfCommentLines -= 1;
100
- }
72
+ /**
73
+ * Return the first token among the consecutive tokens that have no exceed max line difference in between, after the last token in the previous member.
74
+ * @param {Token} nextFirstToken The first token in the next member node.
75
+ * @param {Token} prevLastToken The last token in the previous member node.
76
+ * @param {number} maxLine The maximum number of allowed line difference between consecutive tokens.
77
+ * @returns {Token} The first token among the consecutive tokens.
78
+ */
79
+ function findFirstConsecutiveTokenBefore(nextFirstToken, prevLastToken, maxLine) {
80
+ const before = sourceCode.getTokenBefore(nextFirstToken, { includeComments: true });
101
81
 
102
- /*
103
- * If the last comment and the second block are in the same line,
104
- * the count of comment lines is duplicated. So decrement sumOfCommentLines.
105
- */
106
- if (comments[len - 1].loc.end.line === second.loc.start.line) {
107
- sumOfCommentLines -= 1;
82
+ if (before !== prevLastToken && nextFirstToken.loc.start.line - before.loc.end.line <= maxLine) {
83
+ return findFirstConsecutiveTokenBefore(before, prevLastToken, maxLine);
108
84
  }
85
+ return nextFirstToken;
86
+ }
109
87
 
110
- const linesBetweenFstAndSnd = second.loc.start.line - first.loc.end.line - 1;
111
-
112
- return linesBetweenFstAndSnd - sumOfCommentLines >= 1;
88
+ /**
89
+ * Checks if there is a token or comment between two tokens.
90
+ * @param {Token} before The token before.
91
+ * @param {Token} after The token after.
92
+ * @returns {boolean} True if there is a token or comment between two tokens.
93
+ */
94
+ function hasTokenOrCommentBetween(before, after) {
95
+ return sourceCode.getTokensBetween(before, after, { includeComments: true }).length !== 0;
113
96
  }
114
97
 
115
98
  return {
@@ -120,10 +103,13 @@ module.exports = {
120
103
  const curFirst = sourceCode.getFirstToken(body[i]);
121
104
  const curLast = sourceCode.getLastToken(body[i]);
122
105
  const nextFirst = sourceCode.getFirstToken(body[i + 1]);
123
- const isPadded = isPaddingBetweenTokens(curLast, nextFirst);
124
106
  const isMulti = !astUtils.isTokenOnSameLine(curFirst, curLast);
125
107
  const skip = !isMulti && options[1].exceptAfterSingleLine;
126
-
108
+ const beforePadding = findLastConsecutiveTokenAfter(curLast, nextFirst, 1);
109
+ const afterPadding = findFirstConsecutiveTokenBefore(nextFirst, curLast, 1);
110
+ const isPadded = afterPadding.loc.start.line - beforePadding.loc.end.line > 1;
111
+ const hasTokenInPadding = hasTokenOrCommentBetween(beforePadding, afterPadding);
112
+ const curLineLastToken = findLastConsecutiveTokenAfter(curLast, nextFirst, 0);
127
113
 
128
114
  if ((options[0] === "always" && !skip && !isPadded) ||
129
115
  (options[0] === "never" && isPadded)) {
@@ -131,9 +117,12 @@ module.exports = {
131
117
  node: body[i + 1],
132
118
  messageId: isPadded ? "never" : "always",
133
119
  fix(fixer) {
120
+ if (hasTokenInPadding) {
121
+ return null;
122
+ }
134
123
  return isPadded
135
- ? fixer.replaceTextRange([curLast.range[1], nextFirst.range[0]], "\n")
136
- : fixer.insertTextAfter(curLast, "\n");
124
+ ? fixer.replaceTextRange([beforePadding.range[1], afterPadding.range[0]], "\n")
125
+ : fixer.insertTextAfter(curLineLastToken, "\n");
137
126
  }
138
127
  });
139
128
  }
@@ -110,7 +110,7 @@ module.exports = {
110
110
  if (lineNumber - lastLineNumber - 1 > maxAllowed) {
111
111
  context.report({
112
112
  node,
113
- loc: { start: { line: lastLineNumber + 1, column: 0 }, end: { line: lineNumber, column: 0 } },
113
+ loc: { start: { line: lastLineNumber + maxAllowed + 1, column: 0 }, end: { line: lineNumber, column: 0 } },
114
114
  message,
115
115
  data: { max: maxAllowed, pluralizedLines: maxAllowed === 1 ? "line" : "lines" },
116
116
  fix(fixer) {
@@ -72,14 +72,14 @@ module.exports = {
72
72
  arrayOfStringsOrObjects,
73
73
  {
74
74
  type: "array",
75
- items: {
75
+ items: [{
76
76
  type: "object",
77
77
  properties: {
78
78
  paths: arrayOfStringsOrObjects,
79
79
  patterns: arrayOfStrings
80
80
  },
81
81
  additionalProperties: false
82
- },
82
+ }],
83
83
  additionalItems: false
84
84
  }
85
85
  ]
@@ -54,7 +54,8 @@ module.exports = {
54
54
  description: "disallow negating the left operand of relational operators",
55
55
  category: "Possible Errors",
56
56
  recommended: true,
57
- url: "https://eslint.org/docs/rules/no-unsafe-negation"
57
+ url: "https://eslint.org/docs/rules/no-unsafe-negation",
58
+ suggestion: true
58
59
  },
59
60
 
60
61
  schema: [
@@ -69,9 +70,13 @@ module.exports = {
69
70
  additionalProperties: false
70
71
  }
71
72
  ],
73
+
72
74
  fixable: null,
75
+
73
76
  messages: {
74
- unexpected: "Unexpected negating the left operand of '{{operator}}' operator."
77
+ unexpected: "Unexpected negating the left operand of '{{operator}}' operator.",
78
+ suggestNegatedExpression: "Negate '{{operator}}' expression instead of its left operand. This changes the current behavior.",
79
+ suggestParenthesisedNegation: "Wrap negation in '()' to make the intention explicit. This preserves the current behavior."
75
80
  }
76
81
  },
77
82
 
@@ -82,10 +87,11 @@ module.exports = {
82
87
 
83
88
  return {
84
89
  BinaryExpression(node) {
85
- const orderingRelationRuleApplies = enforceForOrderingRelations && isOrderingRelationalOperator(node.operator);
90
+ const operator = node.operator;
91
+ const orderingRelationRuleApplies = enforceForOrderingRelations && isOrderingRelationalOperator(operator);
86
92
 
87
93
  if (
88
- (isInOrInstanceOfOperator(node.operator) || orderingRelationRuleApplies) &&
94
+ (isInOrInstanceOfOperator(operator) || orderingRelationRuleApplies) &&
89
95
  isNegation(node.left) &&
90
96
  !astUtils.isParenthesised(sourceCode, node.left)
91
97
  ) {
@@ -93,7 +99,26 @@ module.exports = {
93
99
  node,
94
100
  loc: node.left.loc,
95
101
  messageId: "unexpected",
96
- data: { operator: node.operator }
102
+ data: { operator },
103
+ suggest: [
104
+ {
105
+ messageId: "suggestNegatedExpression",
106
+ data: { operator },
107
+ fix(fixer) {
108
+ const negationToken = sourceCode.getFirstToken(node.left);
109
+ const fixRange = [negationToken.range[1], node.range[1]];
110
+ const text = sourceCode.text.slice(fixRange[0], fixRange[1]);
111
+
112
+ return fixer.replaceTextRange(fixRange, `(${text})`);
113
+ }
114
+ },
115
+ {
116
+ messageId: "suggestParenthesisedNegation",
117
+ fix(fixer) {
118
+ return fixer.replaceText(node.left, `(${sourceCode.getText(node.left)})`);
119
+ }
120
+ }
121
+ ]
97
122
  });
98
123
  }
99
124
  }
@@ -15,6 +15,12 @@ const astUtils = require("./utils/ast-utils");
15
15
  // Helpers
16
16
  //------------------------------------------------------------------------------
17
17
 
18
+ const radixMap = new Map([
19
+ [2, { system: "binary", literalPrefix: "0b" }],
20
+ [8, { system: "octal", literalPrefix: "0o" }],
21
+ [16, { system: "hexadecimal", literalPrefix: "0x" }]
22
+ ]);
23
+
18
24
  /**
19
25
  * Checks to see if a CallExpression's callee node is `parseInt` or
20
26
  * `Number.parseInt`.
@@ -54,49 +60,44 @@ module.exports = {
54
60
  },
55
61
 
56
62
  schema: [],
63
+
64
+ messages: {
65
+ useLiteral: "Use {{system}} literals instead of {{functionName}}()."
66
+ },
67
+
57
68
  fixable: "code"
58
69
  },
59
70
 
60
71
  create(context) {
61
72
  const sourceCode = context.getSourceCode();
62
73
 
63
- const radixMap = {
64
- 2: "binary",
65
- 8: "octal",
66
- 16: "hexadecimal"
67
- };
68
-
69
- const prefixMap = {
70
- 2: "0b",
71
- 8: "0o",
72
- 16: "0x"
73
- };
74
-
75
74
  //----------------------------------------------------------------------
76
75
  // Public
77
76
  //----------------------------------------------------------------------
78
77
 
79
78
  return {
80
79
 
81
- CallExpression(node) {
82
-
83
- // doesn't check parseInt() if it doesn't have a radix argument
84
- if (node.arguments.length !== 2) {
85
- return;
86
- }
80
+ "CallExpression[arguments.length=2]"(node) {
81
+ const [strNode, radixNode] = node.arguments,
82
+ str = strNode.value,
83
+ radix = radixNode.value;
84
+
85
+ if (
86
+ strNode.type === "Literal" &&
87
+ radixNode.type === "Literal" &&
88
+ typeof str === "string" &&
89
+ typeof radix === "number" &&
90
+ radixMap.has(radix) &&
91
+ isParseInt(node.callee)
92
+ ) {
87
93
 
88
- // only error if the radix is 2, 8, or 16
89
- const radixName = radixMap[node.arguments[1].value];
94
+ const { system, literalPrefix } = radixMap.get(radix);
90
95
 
91
- if (isParseInt(node.callee) &&
92
- radixName &&
93
- node.arguments[0].type === "Literal"
94
- ) {
95
96
  context.report({
96
97
  node,
97
- message: "Use {{radixName}} literals instead of {{functionName}}().",
98
+ messageId: "useLiteral",
98
99
  data: {
99
- radixName,
100
+ system,
100
101
  functionName: sourceCode.getText(node.callee)
101
102
  },
102
103
  fix(fixer) {
@@ -104,9 +105,9 @@ module.exports = {
104
105
  return null;
105
106
  }
106
107
 
107
- const replacement = `${prefixMap[node.arguments[1].value]}${node.arguments[0].value}`;
108
+ const replacement = `${literalPrefix}${str}`;
108
109
 
109
- if (+replacement !== parseInt(node.arguments[0].value, node.arguments[1].value)) {
110
+ if (+replacement !== parseInt(str, radix)) {
110
111
 
111
112
  /*
112
113
  * If the newly-produced literal would be invalid, (e.g. 0b1234),
@@ -162,7 +162,7 @@ module.exports = {
162
162
  docs: {
163
163
  description: "disallow assignments that can lead to race conditions due to usage of `await` or `yield`",
164
164
  category: "Possible Errors",
165
- recommended: true,
165
+ recommended: false,
166
166
  url: "https://eslint.org/docs/rules/require-atomic-updates"
167
167
  },
168
168
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint",
3
- "version": "6.7.2",
3
+ "version": "6.8.0",
4
4
  "author": "Nicholas C. Zakas <nicholas+npm@nczconsulting.com>",
5
5
  "description": "An AST-based pattern checker for JavaScript.",
6
6
  "bin": {