eslint 8.21.0 → 8.23.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 (40) hide show
  1. package/bin/eslint.js +2 -4
  2. package/conf/globals.js +6 -1
  3. package/lib/cli-engine/file-enumerator.js +4 -2
  4. package/lib/cli.js +123 -29
  5. package/lib/config/flat-config-array.js +53 -12
  6. package/lib/eslint/eslint-helpers.js +39 -16
  7. package/lib/eslint/flat-eslint.js +29 -17
  8. package/lib/linter/code-path-analysis/code-path-segment.js +2 -2
  9. package/lib/linter/code-path-analysis/code-path-state.js +7 -7
  10. package/lib/linter/code-path-analysis/debug-helpers.js +3 -3
  11. package/lib/linter/code-path-analysis/id-generator.js +2 -2
  12. package/lib/linter/config-comment-parser.js +1 -2
  13. package/lib/linter/timing.js +4 -4
  14. package/lib/options.js +290 -242
  15. package/lib/rule-tester/flat-rule-tester.js +1 -1
  16. package/lib/rule-tester/rule-tester.js +1 -1
  17. package/lib/rules/array-callback-return.js +1 -1
  18. package/lib/rules/global-require.js +2 -1
  19. package/lib/rules/indent-legacy.js +4 -4
  20. package/lib/rules/indent.js +23 -15
  21. package/lib/rules/new-cap.js +2 -2
  22. package/lib/rules/no-extra-boolean-cast.js +1 -1
  23. package/lib/rules/no-extra-parens.js +2 -2
  24. package/lib/rules/no-fallthrough.js +8 -3
  25. package/lib/rules/no-labels.js +1 -1
  26. package/lib/rules/no-lone-blocks.js +1 -1
  27. package/lib/rules/no-useless-computed-key.js +1 -1
  28. package/lib/rules/no-var.js +1 -1
  29. package/lib/rules/no-warning-comments.js +24 -5
  30. package/lib/rules/object-shorthand.js +15 -0
  31. package/lib/rules/padded-blocks.js +1 -1
  32. package/lib/rules/prefer-arrow-callback.js +2 -2
  33. package/lib/rules/prefer-const.js +13 -1
  34. package/lib/rules/prefer-rest-params.js +1 -1
  35. package/lib/rules/require-yield.js +0 -1
  36. package/lib/rules/utils/ast-utils.js +10 -4
  37. package/lib/shared/logging.js +1 -1
  38. package/lib/shared/types.js +1 -1
  39. package/lib/source-code/token-store/cursor.js +1 -1
  40. package/package.json +9 -8
@@ -12,7 +12,7 @@
12
12
  // Requirements
13
13
  //------------------------------------------------------------------------------
14
14
 
15
- const createTree = require("functional-red-black-tree");
15
+ const { OrderedMap } = require("js-sdsl");
16
16
 
17
17
  const astUtils = require("./utils/ast-utils");
18
18
 
@@ -135,7 +135,8 @@ class BinarySearchTree {
135
135
  * Creates an empty tree
136
136
  */
137
137
  constructor() {
138
- this._rbTree = createTree();
138
+ this._orderedMap = new OrderedMap();
139
+ this._orderedMapEnd = this._orderedMap.end();
139
140
  }
140
141
 
141
142
  /**
@@ -145,13 +146,7 @@ class BinarySearchTree {
145
146
  * @returns {void}
146
147
  */
147
148
  insert(key, value) {
148
- const iterator = this._rbTree.find(key);
149
-
150
- if (iterator.valid) {
151
- this._rbTree = iterator.update(value);
152
- } else {
153
- this._rbTree = this._rbTree.insert(key, value);
154
- }
149
+ this._orderedMap.setElement(key, value);
155
150
  }
156
151
 
157
152
  /**
@@ -160,9 +155,13 @@ class BinarySearchTree {
160
155
  * @returns {{key: number, value: *}|null} The found entry, or null if no such entry exists.
161
156
  */
162
157
  findLe(key) {
163
- const iterator = this._rbTree.le(key);
158
+ const iterator = this._orderedMap.reverseLowerBound(key);
164
159
 
165
- return iterator && { key: iterator.key, value: iterator.value };
160
+ if (iterator.equals(this._orderedMapEnd)) {
161
+ return {};
162
+ }
163
+
164
+ return { key: iterator.pointer[0], value: iterator.pointer[1] };
166
165
  }
167
166
 
168
167
  /**
@@ -177,11 +176,20 @@ class BinarySearchTree {
177
176
  if (start === end) {
178
177
  return;
179
178
  }
180
- const iterator = this._rbTree.ge(start);
179
+ const iterator = this._orderedMap.lowerBound(start);
181
180
 
182
- while (iterator.valid && iterator.key < end) {
183
- this._rbTree = this._rbTree.remove(iterator.key);
184
- iterator.next();
181
+ if (iterator.equals(this._orderedMapEnd)) {
182
+ return;
183
+ }
184
+
185
+ if (end > this._orderedMap.back()[0]) {
186
+ while (!iterator.equals(this._orderedMapEnd)) {
187
+ this._orderedMap.eraseElementByIterator(iterator);
188
+ }
189
+ } else {
190
+ while (iterator.pointer[0] < end) {
191
+ this._orderedMap.eraseElementByIterator(iterator);
192
+ }
185
193
  }
186
194
  }
187
195
  }
@@ -39,10 +39,10 @@ const CAPS_ALLOWED = [
39
39
  */
40
40
  function checkArray(obj, key, fallback) {
41
41
 
42
- /* istanbul ignore if */
42
+ /* c8 ignore start */
43
43
  if (Object.prototype.hasOwnProperty.call(obj, key) && !Array.isArray(obj[key])) {
44
44
  throw new TypeError(`${key}, if provided, must be an Array`);
45
- }
45
+ }/* c8 ignore stop */
46
46
  return obj[key] || fallback;
47
47
  }
48
48
 
@@ -188,7 +188,7 @@ module.exports = {
188
188
  }
189
189
  return precedence(node) <= precedence(parent);
190
190
 
191
- /* istanbul ignore next */
191
+ /* c8 ignore next */
192
192
  default:
193
193
  throw new Error(`Unexpected parent type: ${parent.type}`);
194
194
  }
@@ -634,10 +634,10 @@ module.exports = {
634
634
 
635
635
  currentNode = currentNode.parent;
636
636
 
637
- /* istanbul ignore if */
637
+ /* c8 ignore start */
638
638
  if (currentNode === null) {
639
639
  throw new Error("Nodes are not in the ancestor-descendant relationship.");
640
- }
640
+ }/* c8 ignore stop */
641
641
 
642
642
  path.push(currentNode);
643
643
  }
@@ -76,6 +76,10 @@ module.exports = {
76
76
  commentPattern: {
77
77
  type: "string",
78
78
  default: ""
79
+ },
80
+ allowEmptyCase: {
81
+ type: "boolean",
82
+ default: false
79
83
  }
80
84
  },
81
85
  additionalProperties: false
@@ -91,6 +95,7 @@ module.exports = {
91
95
  const options = context.options[0] || {};
92
96
  let currentCodePath = null;
93
97
  const sourceCode = context.getSourceCode();
98
+ const allowEmptyCase = options.allowEmptyCase || false;
94
99
 
95
100
  /*
96
101
  * We need to use leading comments of the next SwitchCase node because
@@ -104,7 +109,6 @@ module.exports = {
104
109
  } else {
105
110
  fallthroughCommentPattern = DEFAULT_FALLTHROUGH_COMMENT;
106
111
  }
107
-
108
112
  return {
109
113
  onCodePathStart(codePath) {
110
114
  currentCodePath = codePath;
@@ -119,7 +123,8 @@ module.exports = {
119
123
  * Checks whether or not there is a fallthrough comment.
120
124
  * And reports the previous fallthrough node if that does not exist.
121
125
  */
122
- if (fallthroughCase && !hasFallthroughComment(fallthroughCase, node, context, fallthroughCommentPattern)) {
126
+
127
+ if (fallthroughCase && (!hasFallthroughComment(fallthroughCase, node, context, fallthroughCommentPattern))) {
123
128
  context.report({
124
129
  messageId: node.test ? "case" : "default",
125
130
  node
@@ -137,7 +142,7 @@ module.exports = {
137
142
  * And allows empty cases and the last case.
138
143
  */
139
144
  if (currentCodePath.currentSegments.some(isReachable) &&
140
- (node.consequent.length > 0 || hasBlankLinesBetween(node, nextToken)) &&
145
+ (node.consequent.length > 0 || (!allowEmptyCase && hasBlankLinesBetween(node, nextToken))) &&
141
146
  node.parent.cases[node.parent.cases.length - 1] !== node) {
142
147
  fallthroughCase = node;
143
148
  }
@@ -98,7 +98,7 @@ module.exports = {
98
98
  info = info.upper;
99
99
  }
100
100
 
101
- /* istanbul ignore next: syntax error */
101
+ /* c8 ignore next */
102
102
  return "other";
103
103
  }
104
104
 
@@ -91,7 +91,7 @@ module.exports = {
91
91
  };
92
92
 
93
93
  // ES6: report blocks without block-level bindings, or that's only child of another block
94
- if (context.parserOptions.ecmaVersion >= 6) {
94
+ if (context.languageOptions.ecmaVersion >= 2015) {
95
95
  ruleDef = {
96
96
  BlockStatement(node) {
97
97
  if (isLoneBlock(node)) {
@@ -74,7 +74,7 @@ function hasUselessComputedKey(node) {
74
74
 
75
75
  return value !== "constructor";
76
76
 
77
- /* istanbul ignore next */
77
+ /* c8 ignore next */
78
78
  default:
79
79
  throw new Error(`Unexpected node type: ${node.type}`);
80
80
  }
@@ -90,7 +90,7 @@ function getScopeNode(node) {
90
90
  }
91
91
  }
92
92
 
93
- /* istanbul ignore next : unreachable */
93
+ /* c8 ignore next */
94
94
  return null;
95
95
  }
96
96
 
@@ -37,6 +37,15 @@ module.exports = {
37
37
  },
38
38
  location: {
39
39
  enum: ["start", "anywhere"]
40
+ },
41
+ decoration: {
42
+ type: "array",
43
+ items: {
44
+ type: "string",
45
+ pattern: "^\\S$"
46
+ },
47
+ minItems: 1,
48
+ uniqueItems: true
40
49
  }
41
50
  },
42
51
  additionalProperties: false
@@ -53,6 +62,7 @@ module.exports = {
53
62
  configuration = context.options[0] || {},
54
63
  warningTerms = configuration.terms || ["todo", "fixme", "xxx"],
55
64
  location = configuration.location || "start",
65
+ decoration = [...configuration.decoration || []].join(""),
56
66
  selfConfigRegEx = /\bno-warning-comments\b/u;
57
67
 
58
68
  /**
@@ -64,6 +74,7 @@ module.exports = {
64
74
  */
65
75
  function convertToRegExp(term) {
66
76
  const escaped = escapeRegExp(term);
77
+ const escapedDecoration = escapeRegExp(decoration);
67
78
 
68
79
  /*
69
80
  * When matching at the start, ignore leading whitespace, and
@@ -74,18 +85,23 @@ module.exports = {
74
85
  * e.g. terms ["TODO"] matches `//TODO something`
75
86
  * $ handles any terms at the end of a comment
76
87
  * e.g. terms ["TODO"] matches `// something TODO`
77
- * \s* handles optional leading spaces (for "start" location only)
78
- * e.g. terms ["TODO"] matches `// TODO something`
79
88
  * \b handles terms preceded/followed by word boundary
80
89
  * e.g. terms: ["!FIX", "FIX!"] matches `// FIX!something` or `// something!FIX`
81
90
  * terms: ["FIX"] matches `// FIX!` or `// !FIX`, but not `// fixed or affix`
91
+ *
92
+ * For location start:
93
+ * [\s]* handles optional leading spaces
94
+ * e.g. terms ["TODO"] matches `// TODO something`
95
+ * [\s\*]* (where "\*" is the escaped string of decoration)
96
+ * handles optional leading spaces or decoration characters (for "start" location only)
97
+ * e.g. terms ["TODO"] matches `/**** TODO something ... `
82
98
  */
83
99
  const wordBoundary = "\\b";
84
100
 
85
101
  let prefix = "";
86
102
 
87
103
  if (location === "start") {
88
- prefix = "^\\s*";
104
+ prefix = `^[\\s${escapedDecoration}]*`;
89
105
  } else if (/^\w/u.test(term)) {
90
106
  prefix = wordBoundary;
91
107
  }
@@ -95,12 +111,15 @@ module.exports = {
95
111
 
96
112
  /*
97
113
  * For location "start", the typical regex is:
98
- * /^\s*ESCAPED_TERM\b/iu.
114
+ * /^[\s]*ESCAPED_TERM\b/iu.
115
+ * Or if decoration characters are specified (e.g. "*"), then any of
116
+ * those characters may appear in any order at the start:
117
+ * /^[\s\*]*ESCAPED_TERM\b/iu.
99
118
  *
100
119
  * For location "anywhere" the typical regex is
101
120
  * /\bESCAPED_TERM\b/iu
102
121
  *
103
- * If it starts or ends with non-word character, the prefix and suffix empty, respectively.
122
+ * If it starts or ends with non-word character, the prefix and suffix are empty, respectively.
104
123
  */
105
124
  return new RegExp(`${prefix}${escaped}${suffix}`, flags);
106
125
  }
@@ -78,6 +78,9 @@ module.exports = {
78
78
  ignoreConstructors: {
79
79
  type: "boolean"
80
80
  },
81
+ methodsIgnorePattern: {
82
+ type: "string"
83
+ },
81
84
  avoidQuotes: {
82
85
  type: "boolean"
83
86
  },
@@ -115,6 +118,9 @@ module.exports = {
115
118
 
116
119
  const PARAMS = context.options[1] || {};
117
120
  const IGNORE_CONSTRUCTORS = PARAMS.ignoreConstructors;
121
+ const METHODS_IGNORE_PATTERN = PARAMS.methodsIgnorePattern
122
+ ? new RegExp(PARAMS.methodsIgnorePattern, "u")
123
+ : null;
118
124
  const AVOID_QUOTES = PARAMS.avoidQuotes;
119
125
  const AVOID_EXPLICIT_RETURN_ARROWS = !!PARAMS.avoidExplicitReturnArrows;
120
126
  const sourceCode = context.getSourceCode();
@@ -457,6 +463,15 @@ module.exports = {
457
463
  if (IGNORE_CONSTRUCTORS && node.key.type === "Identifier" && isConstructor(node.key.name)) {
458
464
  return;
459
465
  }
466
+
467
+ if (METHODS_IGNORE_PATTERN) {
468
+ const propertyName = astUtils.getStaticPropertyName(node);
469
+
470
+ if (propertyName !== null && METHODS_IGNORE_PATTERN.test(propertyName)) {
471
+ return;
472
+ }
473
+ }
474
+
460
475
  if (AVOID_QUOTES && isStringLiteral(node.key)) {
461
476
  return;
462
477
  }
@@ -186,7 +186,7 @@ module.exports = {
186
186
  case "ClassBody":
187
187
  return options.classes;
188
188
 
189
- /* istanbul ignore next */
189
+ /* c8 ignore next */
190
190
  default:
191
191
  throw new Error("unreachable");
192
192
  }
@@ -53,7 +53,7 @@ function getVariableOfArguments(scope) {
53
53
  }
54
54
  }
55
55
 
56
- /* istanbul ignore next */
56
+ /* c8 ignore next */
57
57
  return null;
58
58
  }
59
59
 
@@ -126,7 +126,7 @@ function getCallbackInfo(node) {
126
126
  parent = parent.parent;
127
127
  }
128
128
 
129
- /* istanbul ignore next */
129
+ /* c8 ignore next */
130
130
  throw new Error("unreachable");
131
131
  }
132
132
 
@@ -446,7 +446,19 @@ module.exports = {
446
446
 
447
447
  reportCount += nodesToReport.length;
448
448
 
449
- shouldFix = shouldFix && (reportCount === varDeclParent.declarations.length);
449
+ let totalDeclarationsCount = 0;
450
+
451
+ varDeclParent.declarations.forEach(declaration => {
452
+ if (declaration.id.type === "ObjectPattern") {
453
+ totalDeclarationsCount += declaration.id.properties.length;
454
+ } else if (declaration.id.type === "ArrayPattern") {
455
+ totalDeclarationsCount += declaration.id.elements.length;
456
+ } else {
457
+ totalDeclarationsCount += 1;
458
+ }
459
+ });
460
+
461
+ shouldFix = shouldFix && (reportCount === totalDeclarationsCount);
450
462
  }
451
463
  }
452
464
 
@@ -30,7 +30,7 @@ function getVariableOfArguments(scope) {
30
30
  }
31
31
  }
32
32
 
33
- /* istanbul ignore next : unreachable */
33
+ /* c8 ignore next */
34
34
  return null;
35
35
  }
36
36
 
@@ -68,7 +68,6 @@ module.exports = {
68
68
  // Increases the count of `yield` keyword.
69
69
  YieldExpression() {
70
70
 
71
- /* istanbul ignore else */
72
71
  if (stack.length > 0) {
73
72
  stack[stack.length - 1] += 1;
74
73
  }
@@ -1350,7 +1350,7 @@ module.exports = {
1350
1350
  }
1351
1351
  }
1352
1352
 
1353
- /* istanbul ignore next */
1353
+ /* c8 ignore next */
1354
1354
  return true;
1355
1355
  },
1356
1356
 
@@ -1978,7 +1978,7 @@ module.exports = {
1978
1978
  if (comments.length) {
1979
1979
  const lastComment = comments[comments.length - 1];
1980
1980
 
1981
- if (lastComment.range[0] > leftToken.range[0]) {
1981
+ if (!leftToken || lastComment.range[0] > leftToken.range[0]) {
1982
1982
  leftToken = lastComment;
1983
1983
  }
1984
1984
  }
@@ -1986,7 +1986,13 @@ module.exports = {
1986
1986
  leftToken = leftValue;
1987
1987
  }
1988
1988
 
1989
- if (leftToken.type === "Shebang") {
1989
+ /*
1990
+ * If a hashbang comment was passed as a token object from SourceCode,
1991
+ * its type will be "Shebang" because of the way ESLint itself handles hashbangs.
1992
+ * If a hashbang comment was passed in a string and then tokenized in this function,
1993
+ * its type will be "Hashbang" because of the way Espree tokenizes hashbangs.
1994
+ */
1995
+ if (leftToken.type === "Shebang" || leftToken.type === "Hashbang") {
1990
1996
  return false;
1991
1997
  }
1992
1998
 
@@ -2007,7 +2013,7 @@ module.exports = {
2007
2013
  if (comments.length) {
2008
2014
  const firstComment = comments[0];
2009
2015
 
2010
- if (firstComment.range[0] < rightToken.range[0]) {
2016
+ if (!rightToken || firstComment.range[0] < rightToken.range[0]) {
2011
2017
  rightToken = firstComment;
2012
2018
  }
2013
2019
  }
@@ -7,7 +7,7 @@
7
7
 
8
8
  /* eslint no-console: "off" -- Logging util */
9
9
 
10
- /* istanbul ignore next */
10
+ /* c8 ignore next */
11
11
  module.exports = {
12
12
 
13
13
  /**
@@ -21,7 +21,7 @@ module.exports = {};
21
21
  /**
22
22
  * @typedef {Object} ParserOptions
23
23
  * @property {EcmaFeatures} [ecmaFeatures] The optional features.
24
- * @property {3|5|6|7|8|9|10|11|12|13|2015|2016|2017|2018|2019|2020|2021|2022} [ecmaVersion] The ECMAScript version (or revision number).
24
+ * @property {3|5|6|7|8|9|10|11|12|13|14|2015|2016|2017|2018|2019|2020|2021|2022|2023} [ecmaVersion] The ECMAScript version (or revision number).
25
25
  * @property {"script"|"module"} [sourceType] The source code type.
26
26
  * @property {boolean} [allowReserved] Allowing the use of reserved words as identifiers in ES3.
27
27
  */
@@ -69,7 +69,7 @@ module.exports = class Cursor {
69
69
  * @returns {boolean} `true` if the next token exists.
70
70
  * @abstract
71
71
  */
72
- /* istanbul ignore next */
72
+ /* c8 ignore next */
73
73
  moveNext() { // eslint-disable-line class-methods-use-this -- Unused
74
74
  throw new Error("Not implemented.");
75
75
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint",
3
- "version": "8.21.0",
3
+ "version": "8.23.1",
4
4
  "author": "Nicholas C. Zakas <nicholas+npm@nczconsulting.com>",
5
5
  "description": "An AST-based pattern checker for JavaScript.",
6
6
  "bin": {
@@ -39,7 +39,8 @@
39
39
  "docs/src/rules/*.md": [
40
40
  "node tools/fetch-docs-links.js",
41
41
  "git add docs/src/_data/further_reading_links.json"
42
- ]
42
+ ],
43
+ "docs/**/*.svg": "npx svgo -r --multipass"
43
44
  },
44
45
  "files": [
45
46
  "LICENSE",
@@ -54,9 +55,10 @@
54
55
  "homepage": "https://eslint.org",
55
56
  "bugs": "https://github.com/eslint/eslint/issues/",
56
57
  "dependencies": {
57
- "@eslint/eslintrc": "^1.3.0",
58
+ "@eslint/eslintrc": "^1.3.2",
58
59
  "@humanwhocodes/config-array": "^0.10.4",
59
60
  "@humanwhocodes/gitignore-to-minimatch": "^1.0.2",
61
+ "@humanwhocodes/module-importer": "^1.0.1",
60
62
  "ajv": "^6.10.0",
61
63
  "chalk": "^4.0.0",
62
64
  "cross-spawn": "^7.0.2",
@@ -66,13 +68,12 @@
66
68
  "eslint-scope": "^7.1.1",
67
69
  "eslint-utils": "^3.0.0",
68
70
  "eslint-visitor-keys": "^3.3.0",
69
- "espree": "^9.3.3",
71
+ "espree": "^9.4.0",
70
72
  "esquery": "^1.4.0",
71
73
  "esutils": "^2.0.2",
72
74
  "fast-deep-equal": "^3.1.3",
73
75
  "file-entry-cache": "^6.0.1",
74
76
  "find-up": "^5.0.0",
75
- "functional-red-black-tree": "^1.0.1",
76
77
  "glob-parent": "^6.0.1",
77
78
  "globals": "^13.15.0",
78
79
  "globby": "^11.1.0",
@@ -81,6 +82,7 @@
81
82
  "import-fresh": "^3.0.0",
82
83
  "imurmurhash": "^0.1.4",
83
84
  "is-glob": "^4.0.0",
85
+ "js-sdsl": "^4.1.4",
84
86
  "js-yaml": "^4.1.0",
85
87
  "json-stable-stringify-without-jsonify": "^1.0.1",
86
88
  "levn": "^0.4.1",
@@ -91,13 +93,13 @@
91
93
  "regexpp": "^3.2.0",
92
94
  "strip-ansi": "^6.0.1",
93
95
  "strip-json-comments": "^3.1.0",
94
- "text-table": "^0.2.0",
95
- "v8-compile-cache": "^2.0.3"
96
+ "text-table": "^0.2.0"
96
97
  },
97
98
  "devDependencies": {
98
99
  "@babel/core": "^7.4.3",
99
100
  "@babel/preset-env": "^7.4.3",
100
101
  "babel-loader": "^8.0.5",
102
+ "c8": "^7.12.0",
101
103
  "chai": "^4.0.1",
102
104
  "cheerio": "^0.22.0",
103
105
  "common-tags": "^1.8.0",
@@ -141,7 +143,6 @@
141
143
  "mocha-junit-reporter": "^2.0.0",
142
144
  "node-polyfill-webpack-plugin": "^1.0.3",
143
145
  "npm-license": "^0.3.3",
144
- "nyc": "^15.0.1",
145
146
  "pirates": "^4.0.5",
146
147
  "progress": "^2.0.3",
147
148
  "proxyquire": "^2.0.1",