eslint 9.0.0-beta.0 → 9.0.0-beta.2

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/README.md CHANGED
@@ -103,7 +103,7 @@ We are now at or near 100% compatibility with JSCS. If you try ESLint and believ
103
103
 
104
104
  ### Does Prettier replace ESLint?
105
105
 
106
- No, ESLint and Prettier have diffent jobs: ESLint is a linter (looking for problematic patterns) and Prettier is a code formatter. Using both tools is common, refer to [Prettier's documentation](https://prettier.io/docs/en/install#eslint-and-other-linters) to learn how to configure them to work well with each other.
106
+ No, ESLint and Prettier have different jobs: ESLint is a linter (looking for problematic patterns) and Prettier is a code formatter. Using both tools is common, refer to [Prettier's documentation](https://prettier.io/docs/en/install#eslint-and-other-linters) to learn how to configure them to work well with each other.
107
107
 
108
108
  ### Why can't ESLint find my plugins?
109
109
 
@@ -127,6 +127,18 @@ In other cases (including if rules need to warn on more or fewer cases due to ne
127
127
 
128
128
  Once a language feature has been adopted into the ECMAScript standard (stage 4 according to the [TC39 process](https://tc39.github.io/process-document/)), we will accept issues and pull requests related to the new feature, subject to our [contributing guidelines](https://eslint.org/docs/latest/contribute). Until then, please use the appropriate parser and plugin(s) for your experimental feature.
129
129
 
130
+ ### Which Node.js versions does ESLint support?
131
+
132
+ ESLint updates the supported Node.js versions with each major release of ESLint. At that time, ESLint's supported Node.js versions are updated to be:
133
+
134
+ 1. The most recent maintenance release of Node.js
135
+ 1. The lowest minor version of the Node.js LTS release that includes the features the ESLint team wants to use.
136
+ 1. The Node.js Current release
137
+
138
+ ESLint is also expected to work with Node.js versions released after the Node.js Current release.
139
+
140
+ Refer to the [Quick Start Guide](https://eslint.org/docs/latest/use/getting-started#prerequisites) for the officially supported Node.js versions for a given ESLint release.
141
+
130
142
  ### Where to ask for help?
131
143
 
132
144
  Open a [discussion](https://github.com/eslint/eslint/discussions) or stop by our [Discord server](https://eslint.org/chat).
@@ -213,6 +225,11 @@ The people who manage releases, review feature requests, and meet regularly to e
213
225
  Nicholas C. Zakas
214
226
  </a>
215
227
  </td><td align="center" valign="top" width="11%">
228
+ <a href="https://github.com/fasttime">
229
+ <img src="https://github.com/fasttime.png?s=75" width="75" height="75" alt="Francesco Trotta's Avatar"><br />
230
+ Francesco Trotta
231
+ </a>
232
+ </td><td align="center" valign="top" width="11%">
216
233
  <a href="https://github.com/mdjermanovic">
217
234
  <img src="https://github.com/mdjermanovic.png?s=75" width="75" height="75" alt="Milos Djermanovic's Avatar"><br />
218
235
  Milos Djermanovic
@@ -250,11 +267,6 @@ Bryan Mishkin
250
267
  Josh Goldberg ✨
251
268
  </a>
252
269
  </td><td align="center" valign="top" width="11%">
253
- <a href="https://github.com/fasttime">
254
- <img src="https://github.com/fasttime.png?s=75" width="75" height="75" alt="Francesco Trotta's Avatar"><br />
255
- Francesco Trotta
256
- </a>
257
- </td><td align="center" valign="top" width="11%">
258
270
  <a href="https://github.com/Tanujkanti4441">
259
271
  <img src="https://github.com/Tanujkanti4441.png?s=75" width="75" height="75" alt="Tanuj Kanti's Avatar"><br />
260
272
  Tanuj Kanti
@@ -294,7 +306,7 @@ The following companies, organizations, and individuals support ESLint's ongoing
294
306
  <p><a href="#"><img src="https://images.opencollective.com/2021-frameworks-fund/logo.png" alt="Chrome Frameworks Fund" height="undefined"></a> <a href="https://automattic.com"><img src="https://images.opencollective.com/automattic/d0ef3e1/logo.png" alt="Automattic" height="undefined"></a></p><h3>Gold Sponsors</h3>
295
307
  <p><a href="https://engineering.salesforce.com"><img src="https://images.opencollective.com/salesforce/ca8f997/logo.png" alt="Salesforce" height="96"></a> <a href="https://www.airbnb.com/"><img src="https://images.opencollective.com/airbnb/d327d66/logo.png" alt="Airbnb" height="96"></a></p><h3>Silver Sponsors</h3>
296
308
  <p><a href="https://www.jetbrains.com/"><img src="https://images.opencollective.com/jetbrains/eb04ddc/logo.png" alt="JetBrains" height="64"></a> <a href="https://liftoff.io/"><img src="https://images.opencollective.com/liftoff/5c4fa84/logo.png" alt="Liftoff" height="64"></a> <a href="https://americanexpress.io"><img src="https://avatars.githubusercontent.com/u/3853301?v=4" alt="American Express" height="64"></a> <a href="https://www.workleap.com"><img src="https://avatars.githubusercontent.com/u/53535748?u=d1e55d7661d724bf2281c1bfd33cb8f99fe2465f&v=4" alt="Workleap" height="64"></a></p><h3>Bronze Sponsors</h3>
297
- <p><a href="https://themeisle.com"><img src="https://images.opencollective.com/themeisle/d5592fe/logo.png" alt="ThemeIsle" height="32"></a> <a href="https://www.crosswordsolver.org/anagram-solver/"><img src="https://images.opencollective.com/anagram-solver/2666271/logo.png" alt="Anagram Solver" height="32"></a> <a href="https://icons8.com/"><img src="https://images.opencollective.com/icons8/7fa1641/logo.png" alt="Icons8" height="32"></a> <a href="https://discord.com"><img src="https://images.opencollective.com/discordapp/f9645d9/logo.png" alt="Discord" height="32"></a> <a href="https://transloadit.com/"><img src="https://avatars.githubusercontent.com/u/125754?v=4" alt="Transloadit" height="32"></a> <a href="https://www.ignitionapp.com"><img src="https://avatars.githubusercontent.com/u/5753491?v=4" alt="Ignition" height="32"></a> <a href="https://nx.dev"><img src="https://avatars.githubusercontent.com/u/23692104?v=4" alt="Nx" height="32"></a> <a href="https://herocoders.com"><img src="https://avatars.githubusercontent.com/u/37549774?v=4" alt="HeroCoders" height="32"></a> <a href="https://usenextbase.com"><img src="https://avatars.githubusercontent.com/u/145838380?v=4" alt="Nextbase Starter Kit" height="32"></a></p>
309
+ <p><a href="https://www.notion.so"><img src="https://images.opencollective.com/notion/bf3b117/logo.png" alt="notion" 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://www.crosswordsolver.org/anagram-solver/"><img src="https://images.opencollective.com/anagram-solver/2666271/logo.png" alt="Anagram Solver" height="32"></a> <a href="https://icons8.com/"><img src="https://images.opencollective.com/icons8/7fa1641/logo.png" alt="Icons8" height="32"></a> <a href="https://discord.com"><img src="https://images.opencollective.com/discordapp/f9645d9/logo.png" alt="Discord" height="32"></a> <a href="https://transloadit.com/"><img src="https://avatars.githubusercontent.com/u/125754?v=4" alt="Transloadit" height="32"></a> <a href="https://www.ignitionapp.com"><img src="https://avatars.githubusercontent.com/u/5753491?v=4" alt="Ignition" height="32"></a> <a href="https://nx.dev"><img src="https://avatars.githubusercontent.com/u/23692104?v=4" alt="Nx" height="32"></a> <a href="https://herocoders.com"><img src="https://avatars.githubusercontent.com/u/37549774?v=4" alt="HeroCoders" height="32"></a> <a href="https://usenextbase.com"><img src="https://avatars.githubusercontent.com/u/145838380?v=4" alt="Nextbase Starter Kit" height="32"></a></p>
298
310
  <!--sponsorsend-->
299
311
 
300
312
  ## Technology Sponsors
@@ -167,9 +167,22 @@ class RuleValidator {
167
167
  validateRule(ruleOptions.slice(1));
168
168
 
169
169
  if (validateRule.errors) {
170
- throw new Error(`Key "rules": Key "${ruleId}": ${
170
+ throw new Error(`Key "rules": Key "${ruleId}":\n${
171
171
  validateRule.errors.map(
172
- error => `\tValue ${JSON.stringify(error.data)} ${error.message}.\n`
172
+ error => {
173
+ if (
174
+ error.keyword === "additionalProperties" &&
175
+ error.schema === false &&
176
+ typeof error.parentSchema?.properties === "object" &&
177
+ typeof error.params?.additionalProperty === "string"
178
+ ) {
179
+ const expectedProperties = Object.keys(error.parentSchema.properties).map(property => `"${property}"`);
180
+
181
+ return `\tValue ${JSON.stringify(error.data)} ${error.message}.\n\t\tUnexpected property "${error.params.additionalProperty}". Expected properties: ${expectedProperties.join(", ")}.\n`;
182
+ }
183
+
184
+ return `\tValue ${JSON.stringify(error.data)} ${error.message}.\n`;
185
+ }
173
186
  ).join("")
174
187
  }`);
175
188
  }
@@ -838,6 +838,7 @@ class ESLint {
838
838
  configs,
839
839
  errorOnUnmatchedPattern
840
840
  });
841
+ const controller = new AbortController();
841
842
 
842
843
  debug(`${filePaths.length} files found in: ${Date.now() - startTime}ms`);
843
844
 
@@ -906,9 +907,12 @@ class ESLint {
906
907
  fixer = message => shouldMessageBeFixed(message, config, fixTypesSet) && originalFix(message);
907
908
  }
908
909
 
909
- return fs.readFile(filePath, "utf8")
910
+ return fs.readFile(filePath, { encoding: "utf8", signal: controller.signal })
910
911
  .then(text => {
911
912
 
913
+ // fail immediately if an error occurred in another file
914
+ controller.signal.throwIfAborted();
915
+
912
916
  // do the linting
913
917
  const result = verifyText({
914
918
  text,
@@ -932,6 +936,9 @@ class ESLint {
932
936
  }
933
937
 
934
938
  return result;
939
+ }).catch(error => {
940
+ controller.abort(error);
941
+ throw error;
935
942
  });
936
943
 
937
944
  })
@@ -43,7 +43,7 @@ const
43
43
  const { getRuleFromConfig } = require("../config/flat-config-helpers");
44
44
  const { FlatConfigArray } = require("../config/flat-config-array");
45
45
  const { RuleValidator } = require("../config/rule-validator");
46
- const { assertIsRuleOptions, assertIsRuleSeverity } = require("../config/flat-config-schema");
46
+ const { assertIsRuleSeverity } = require("../config/flat-config-schema");
47
47
  const { normalizeSeverityToString } = require("../shared/severity");
48
48
  const debug = require("debug")("eslint:linter");
49
49
  const MAX_AUTOFIX_PASSES = 10;
@@ -326,10 +326,11 @@ function createDisableDirectives(options) {
326
326
  * @param {SourceCode} sourceCode The SourceCode object to get comments from.
327
327
  * @param {function(string): {create: Function}} ruleMapper A map from rule IDs to defined rules
328
328
  * @param {string|null} warnInlineConfig If a string then it should warn directive comments as disabled. The string value is the config name what the setting came from.
329
+ * @param {ConfigData} config Provided config.
329
330
  * @returns {{configuredRules: Object, enabledGlobals: {value:string,comment:Token}[], exportedVariables: Object, problems: LintMessage[], disableDirectives: DisableDirective[]}}
330
331
  * A collection of the directive comments that were found, along with any problems that occurred when parsing
331
332
  */
332
- function getDirectiveComments(sourceCode, ruleMapper, warnInlineConfig) {
333
+ function getDirectiveComments(sourceCode, ruleMapper, warnInlineConfig, config) {
333
334
  const configuredRules = {};
334
335
  const enabledGlobals = Object.create(null);
335
336
  const exportedVariables = {};
@@ -438,8 +439,58 @@ function getDirectiveComments(sourceCode, ruleMapper, warnInlineConfig) {
438
439
  return;
439
440
  }
440
441
 
442
+ if (Object.hasOwn(configuredRules, name)) {
443
+ problems.push(createLintingProblem({
444
+ message: `Rule "${name}" is already configured by another configuration comment in the preceding code. This configuration is ignored.`,
445
+ loc: comment.loc
446
+ }));
447
+ return;
448
+ }
449
+
450
+ let ruleOptions = Array.isArray(ruleValue) ? ruleValue : [ruleValue];
451
+
452
+ /*
453
+ * If the rule was already configured, inline rule configuration that
454
+ * only has severity should retain options from the config and just override the severity.
455
+ *
456
+ * Example:
457
+ *
458
+ * {
459
+ * rules: {
460
+ * curly: ["error", "multi"]
461
+ * }
462
+ * }
463
+ *
464
+ * /* eslint curly: ["warn"] * /
465
+ *
466
+ * Results in:
467
+ *
468
+ * curly: ["warn", "multi"]
469
+ */
470
+ if (
471
+
472
+ /*
473
+ * If inline config for the rule has only severity
474
+ */
475
+ ruleOptions.length === 1 &&
476
+
477
+ /*
478
+ * And the rule was already configured
479
+ */
480
+ config.rules && Object.hasOwn(config.rules, name)
481
+ ) {
482
+
483
+ /*
484
+ * Then use severity from the inline config and options from the provided config
485
+ */
486
+ ruleOptions = [
487
+ ruleOptions[0], // severity from the inline config
488
+ ...Array.isArray(config.rules[name]) ? config.rules[name].slice(1) : [] // options from the provided config
489
+ ];
490
+ }
491
+
441
492
  try {
442
- validator.validateRuleOptions(rule, name, ruleValue);
493
+ validator.validateRuleOptions(rule, name, ruleOptions);
443
494
  } catch (err) {
444
495
 
445
496
  /*
@@ -460,7 +511,7 @@ function getDirectiveComments(sourceCode, ruleMapper, warnInlineConfig) {
460
511
  return;
461
512
  }
462
513
 
463
- configuredRules[name] = ruleValue;
514
+ configuredRules[name] = ruleOptions;
464
515
  });
465
516
  } else {
466
517
  problems.push(parseResult.error);
@@ -1322,7 +1373,7 @@ class Linter {
1322
1373
 
1323
1374
  const sourceCode = slots.lastSourceCode;
1324
1375
  const commentDirectives = options.allowInlineConfig
1325
- ? getDirectiveComments(sourceCode, ruleId => getRule(slots, ruleId), options.warnInlineConfig)
1376
+ ? getDirectiveComments(sourceCode, ruleId => getRule(slots, ruleId), options.warnInlineConfig, config)
1326
1377
  : { configuredRules: {}, enabledGlobals: {}, exportedVariables: {}, problems: [], disableDirectives: [] };
1327
1378
 
1328
1379
  // augment global scope with declared global variables
@@ -1332,56 +1383,8 @@ class Linter {
1332
1383
  { exportedVariables: commentDirectives.exportedVariables, enabledGlobals: commentDirectives.enabledGlobals }
1333
1384
  );
1334
1385
 
1335
- /*
1336
- * Now we determine the final configurations for rules.
1337
- * First, let all inline rule configurations override those from the config.
1338
- * Then, check for a special case: if a rule is configured in both places,
1339
- * inline rule configuration that only has severity should retain options from
1340
- * the config and just override the severity.
1341
- *
1342
- * Example:
1343
- *
1344
- * {
1345
- * rules: {
1346
- * curly: ["error", "multi"]
1347
- * }
1348
- * }
1349
- *
1350
- * /* eslint curly: ["warn"] * /
1351
- *
1352
- * Results in:
1353
- *
1354
- * curly: ["warn", "multi"]
1355
- */
1356
1386
  const configuredRules = Object.assign({}, config.rules, commentDirectives.configuredRules);
1357
1387
 
1358
- if (config.rules) {
1359
- for (const [ruleId, ruleInlineConfig] of Object.entries(commentDirectives.configuredRules)) {
1360
- if (
1361
-
1362
- /*
1363
- * If inline config for the rule has only severity
1364
- */
1365
- (!Array.isArray(ruleInlineConfig) || ruleInlineConfig.length === 1) &&
1366
-
1367
- /*
1368
- * And provided config for the rule has options
1369
- */
1370
- Object.hasOwn(config.rules, ruleId) &&
1371
- (Array.isArray(config.rules[ruleId]) && config.rules[ruleId].length > 1)
1372
- ) {
1373
-
1374
- /*
1375
- * Then use severity from the inline config and options from the provided config
1376
- */
1377
- configuredRules[ruleId] = [
1378
- Array.isArray(ruleInlineConfig) ? ruleInlineConfig[0] : ruleInlineConfig, // severity from the inline config
1379
- ...config.rules[ruleId].slice(1) // options from the provided config
1380
- ];
1381
- }
1382
- }
1383
- }
1384
-
1385
1388
  let lintingProblems;
1386
1389
 
1387
1390
  try {
@@ -1711,19 +1714,77 @@ class Linter {
1711
1714
  return;
1712
1715
  }
1713
1716
 
1717
+ if (Object.hasOwn(mergedInlineConfig.rules, ruleId)) {
1718
+ inlineConfigProblems.push(createLintingProblem({
1719
+ message: `Rule "${ruleId}" is already configured by another configuration comment in the preceding code. This configuration is ignored.`,
1720
+ loc: node.loc
1721
+ }));
1722
+ return;
1723
+ }
1724
+
1714
1725
  try {
1715
1726
 
1716
- const ruleOptions = Array.isArray(ruleValue) ? ruleValue : [ruleValue];
1727
+ let ruleOptions = Array.isArray(ruleValue) ? ruleValue : [ruleValue];
1717
1728
 
1718
- assertIsRuleOptions(ruleId, ruleValue);
1719
1729
  assertIsRuleSeverity(ruleId, ruleOptions[0]);
1720
1730
 
1721
- ruleValidator.validate({
1722
- plugins: config.plugins,
1723
- rules: {
1724
- [ruleId]: ruleOptions
1731
+ /*
1732
+ * If the rule was already configured, inline rule configuration that
1733
+ * only has severity should retain options from the config and just override the severity.
1734
+ *
1735
+ * Example:
1736
+ *
1737
+ * {
1738
+ * rules: {
1739
+ * curly: ["error", "multi"]
1740
+ * }
1741
+ * }
1742
+ *
1743
+ * /* eslint curly: ["warn"] * /
1744
+ *
1745
+ * Results in:
1746
+ *
1747
+ * curly: ["warn", "multi"]
1748
+ */
1749
+
1750
+ let shouldValidateOptions = true;
1751
+
1752
+ if (
1753
+
1754
+ /*
1755
+ * If inline config for the rule has only severity
1756
+ */
1757
+ ruleOptions.length === 1 &&
1758
+
1759
+ /*
1760
+ * And the rule was already configured
1761
+ */
1762
+ config.rules && Object.hasOwn(config.rules, ruleId)
1763
+ ) {
1764
+
1765
+ /*
1766
+ * Then use severity from the inline config and options from the provided config
1767
+ */
1768
+ ruleOptions = [
1769
+ ruleOptions[0], // severity from the inline config
1770
+ ...config.rules[ruleId].slice(1) // options from the provided config
1771
+ ];
1772
+
1773
+ // if the rule was enabled, the options have already been validated
1774
+ if (config.rules[ruleId][0] > 0) {
1775
+ shouldValidateOptions = false;
1725
1776
  }
1726
- });
1777
+ }
1778
+
1779
+ if (shouldValidateOptions) {
1780
+ ruleValidator.validate({
1781
+ plugins: config.plugins,
1782
+ rules: {
1783
+ [ruleId]: ruleOptions
1784
+ }
1785
+ });
1786
+ }
1787
+
1727
1788
  mergedInlineConfig.rules[ruleId] = ruleOptions;
1728
1789
  } catch (err) {
1729
1790
 
@@ -1763,58 +1824,8 @@ class Linter {
1763
1824
  )
1764
1825
  : { problems: [], disableDirectives: [] };
1765
1826
 
1766
- /*
1767
- * Now we determine the final configurations for rules.
1768
- * First, let all inline rule configurations override those from the config.
1769
- * Then, check for a special case: if a rule is configured in both places,
1770
- * inline rule configuration that only has severity should retain options from
1771
- * the config and just override the severity.
1772
- *
1773
- * Example:
1774
- *
1775
- * {
1776
- * rules: {
1777
- * curly: ["error", "multi"]
1778
- * }
1779
- * }
1780
- *
1781
- * /* eslint curly: ["warn"] * /
1782
- *
1783
- * Results in:
1784
- *
1785
- * curly: ["warn", "multi"]
1786
- *
1787
- * At this point, all rule configurations are normalized to arrays.
1788
- */
1789
1827
  const configuredRules = Object.assign({}, config.rules, mergedInlineConfig.rules);
1790
1828
 
1791
- if (config.rules) {
1792
- for (const [ruleId, ruleInlineConfig] of Object.entries(mergedInlineConfig.rules)) {
1793
- if (
1794
-
1795
- /*
1796
- * If inline config for the rule has only severity
1797
- */
1798
- ruleInlineConfig.length === 1 &&
1799
-
1800
- /*
1801
- * And provided config for the rule has options
1802
- */
1803
- Object.hasOwn(config.rules, ruleId) &&
1804
- config.rules[ruleId].length > 1
1805
- ) {
1806
-
1807
- /*
1808
- * Then use severity from the inline config and options from the provided config
1809
- */
1810
- configuredRules[ruleId] = [
1811
- ruleInlineConfig[0], // severity from the inline config
1812
- ...config.rules[ruleId].slice(1) // options from the provided config
1813
- ];
1814
- }
1815
- }
1816
- }
1817
-
1818
1829
  let lintingProblems;
1819
1830
 
1820
1831
  sourceCode.finalize();
@@ -109,6 +109,7 @@ module.exports = {
109
109
  IfStatement: increaseComplexity,
110
110
  WhileStatement: increaseComplexity,
111
111
  DoWhileStatement: increaseComplexity,
112
+ AssignmentPattern: increaseComplexity,
112
113
 
113
114
  // Avoid `default`
114
115
  "SwitchCase[test]": increaseComplexity,
@@ -120,6 +121,18 @@ module.exports = {
120
121
  }
121
122
  },
122
123
 
124
+ MemberExpression(node) {
125
+ if (node.optional === true) {
126
+ increaseComplexity();
127
+ }
128
+ },
129
+
130
+ CallExpression(node) {
131
+ if (node.optional === true) {
132
+ increaseComplexity();
133
+ }
134
+ },
135
+
123
136
  onCodePathEnd(codePath, node) {
124
137
  const complexity = complexities.pop();
125
138
 
@@ -5,8 +5,7 @@
5
5
 
6
6
  "use strict";
7
7
 
8
- const globals = require("globals");
9
- const { isNullLiteral, isConstant, isReferenceToGlobalVariable, isLogicalAssignmentOperator } = require("./utils/ast-utils");
8
+ const { isNullLiteral, isConstant, isReferenceToGlobalVariable, isLogicalAssignmentOperator, ECMASCRIPT_GLOBALS } = require("./utils/ast-utils");
10
9
 
11
10
  const NUMERIC_OR_STRING_BINARY_OPERATORS = new Set(["+", "-", "*", "/", "%", "|", "^", "&", "**", "<<", ">>", ">>>"]);
12
11
 
@@ -376,7 +375,7 @@ function isAlwaysNew(scope, node) {
376
375
  * Catching these is especially useful for primitive constructors
377
376
  * which return boxed values, a surprising gotcha' in JavaScript.
378
377
  */
379
- return Object.hasOwn(globals.builtin, node.callee.name) &&
378
+ return Object.hasOwn(ECMASCRIPT_GLOBALS, node.callee.name) &&
380
379
  isReferenceToGlobalVariable(scope, node.callee);
381
380
  }
382
381
  case "Literal":
@@ -10,7 +10,6 @@
10
10
  //------------------------------------------------------------------------------
11
11
 
12
12
  const astUtils = require("./utils/ast-utils");
13
- const globals = require("globals");
14
13
 
15
14
  //------------------------------------------------------------------------------
16
15
  // Rule Definition
@@ -54,7 +53,7 @@ module.exports = {
54
53
  const sourceCode = context.sourceCode;
55
54
  const exceptions = new Set(config.exceptions || []);
56
55
  const modifiedBuiltins = new Set(
57
- Object.keys(globals.builtin)
56
+ Object.keys(astUtils.ECMASCRIPT_GLOBALS)
58
57
  .filter(builtin => builtin[0].toUpperCase() === builtin[0])
59
58
  .filter(builtin => !exceptions.has(builtin))
60
59
  );