eslint 9.32.0 → 9.33.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/README.md CHANGED
@@ -328,7 +328,7 @@ to get your logo on our READMEs and [website](https://eslint.org/sponsors).
328
328
  <p><a href="https://automattic.com"><img src="https://images.opencollective.com/automattic/d0ef3e1/logo.png" alt="Automattic" height="128"></a> <a href="https://www.airbnb.com/"><img src="https://images.opencollective.com/airbnb/d327d66/logo.png" alt="Airbnb" height="128"></a></p><h3>Gold Sponsors</h3>
329
329
  <p><a href="https://qlty.sh/"><img src="https://images.opencollective.com/qltysh/33d157d/logo.png" alt="Qlty Software" height="96"></a> <a href="https://trunk.io/"><img src="https://images.opencollective.com/trunkio/fb92d60/avatar.png" alt="trunk.io" height="96"></a> <a href="https://shopify.engineering/"><img src="https://avatars.githubusercontent.com/u/8085" alt="Shopify" height="96"></a></p><h3>Silver Sponsors</h3>
330
330
  <p><a href="https://vite.dev/"><img src="https://images.opencollective.com/vite/e6d15e1/logo.png" alt="Vite" 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" alt="American Express" height="64"></a> <a href="https://stackblitz.com"><img src="https://avatars.githubusercontent.com/u/28635252" alt="StackBlitz" height="64"></a></p><h3>Bronze Sponsors</h3>
331
- <p><a href="https://cybozu.co.jp/"><img src="https://images.opencollective.com/cybozu/933e46d/logo.png" alt="Cybozu" height="32"></a> <a href="https://sentry.io"><img src="https://github.com/getsentry.png" alt="Sentry" 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://www.gitbook.com"><img src="https://avatars.githubusercontent.com/u/7111340" alt="GitBook" height="32"></a> <a href="https://nx.dev"><img src="https://avatars.githubusercontent.com/u/23692104" alt="Nx" height="32"></a> <a href="https://opensource.mercedes-benz.com/"><img src="https://avatars.githubusercontent.com/u/34240465" alt="Mercedes-Benz Group" height="32"></a> <a href="https://herocoders.com"><img src="https://avatars.githubusercontent.com/u/37549774" alt="HeroCoders" height="32"></a> <a href="https://www.lambdatest.com"><img src="https://avatars.githubusercontent.com/u/171592363" alt="LambdaTest" height="32"></a></p>
331
+ <p><a href="https://cybozu.co.jp/"><img src="https://images.opencollective.com/cybozu/933e46d/logo.png" alt="Cybozu" 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://www.gitbook.com"><img src="https://avatars.githubusercontent.com/u/7111340" alt="GitBook" height="32"></a> <a href="https://nx.dev"><img src="https://avatars.githubusercontent.com/u/23692104" alt="Nx" height="32"></a> <a href="https://opensource.mercedes-benz.com/"><img src="https://avatars.githubusercontent.com/u/34240465" alt="Mercedes-Benz Group" height="32"></a> <a href="https://herocoders.com"><img src="https://avatars.githubusercontent.com/u/37549774" alt="HeroCoders" height="32"></a> <a href="https://www.lambdatest.com"><img src="https://avatars.githubusercontent.com/u/171592363" alt="LambdaTest" height="32"></a></p>
332
332
  <h3>Technology Sponsors</h3>
333
333
  Technology sponsors allow us to use their products and services for free as part of a contribution to the open source ecosystem and our work.
334
334
  <p><a href="https://netlify.com"><img src="https://raw.githubusercontent.com/eslint/eslint.org/main/src/assets/images/techsponsors/netlify-icon.svg" alt="Netlify" height="32"></a> <a href="https://algolia.com"><img src="https://raw.githubusercontent.com/eslint/eslint.org/main/src/assets/images/techsponsors/algolia-icon.svg" alt="Algolia" height="32"></a> <a href="https://1password.com"><img src="https://raw.githubusercontent.com/eslint/eslint.org/main/src/assets/images/techsponsors/1password-icon.svg" alt="1Password" height="32"></a></p>
@@ -4,6 +4,12 @@
4
4
  */
5
5
  "use strict";
6
6
 
7
+ //------------------------------------------------------------------------------
8
+ // Requirements
9
+ //------------------------------------------------------------------------------
10
+
11
+ const astUtils = require("./utils/ast-utils");
12
+
7
13
  //------------------------------------------------------------------------------
8
14
  // Helpers
9
15
  //------------------------------------------------------------------------------
@@ -16,10 +22,34 @@ const TYPE_NODES = new Set([
16
22
  "TSQualifiedName",
17
23
  ]);
18
24
 
25
+ const GLOBAL_OBJECTS = new Set(["globalThis", "self", "window"]);
26
+
19
27
  //------------------------------------------------------------------------------
20
28
  // Rule Definition
21
29
  //------------------------------------------------------------------------------
22
30
 
31
+ const arrayOfGlobals = {
32
+ type: "array",
33
+ items: {
34
+ oneOf: [
35
+ {
36
+ type: "string",
37
+ },
38
+ {
39
+ type: "object",
40
+ properties: {
41
+ name: { type: "string" },
42
+ message: { type: "string" },
43
+ },
44
+ required: ["name"],
45
+ additionalProperties: false,
46
+ },
47
+ ],
48
+ },
49
+ uniqueItems: true,
50
+ minItems: 0,
51
+ };
52
+
23
53
  /** @type {import('../types').Rule.RuleModule} */
24
54
  module.exports = {
25
55
  meta: {
@@ -34,25 +64,33 @@ module.exports = {
34
64
  },
35
65
 
36
66
  schema: {
37
- type: "array",
38
- items: {
39
- oneOf: [
40
- {
41
- type: "string",
42
- },
43
- {
44
- type: "object",
45
- properties: {
46
- name: { type: "string" },
47
- message: { type: "string" },
67
+ anyOf: [
68
+ arrayOfGlobals,
69
+ {
70
+ type: "array",
71
+ items: [
72
+ {
73
+ type: "object",
74
+ properties: {
75
+ globals: arrayOfGlobals,
76
+ checkGlobalObject: {
77
+ type: "boolean",
78
+ },
79
+ globalObjects: {
80
+ type: "array",
81
+ items: {
82
+ type: "string",
83
+ },
84
+ uniqueItems: true,
85
+ },
86
+ },
87
+ required: ["globals"],
88
+ additionalProperties: false,
48
89
  },
49
- required: ["name"],
50
- additionalProperties: false,
51
- },
52
- ],
53
- },
54
- uniqueItems: true,
55
- minItems: 0,
90
+ ],
91
+ additionalItems: false,
92
+ },
93
+ ],
56
94
  },
57
95
 
58
96
  messages: {
@@ -63,14 +101,33 @@ module.exports = {
63
101
  },
64
102
 
65
103
  create(context) {
66
- const sourceCode = context.sourceCode;
104
+ const { sourceCode, options } = context;
105
+
106
+ const isGlobalsObject =
107
+ typeof options[0] === "object" &&
108
+ Object.hasOwn(options[0], "globals");
109
+
110
+ const restrictedGlobals = isGlobalsObject
111
+ ? options[0].globals
112
+ : options;
113
+ const checkGlobalObject = isGlobalsObject
114
+ ? options[0].checkGlobalObject
115
+ : false;
116
+ const userGlobalObjects = isGlobalsObject
117
+ ? options[0].globalObjects || []
118
+ : [];
119
+
120
+ const globalObjects = new Set([
121
+ ...GLOBAL_OBJECTS,
122
+ ...userGlobalObjects,
123
+ ]);
67
124
 
68
125
  // If no globals are restricted, we don't need to do anything
69
- if (context.options.length === 0) {
126
+ if (restrictedGlobals.length === 0) {
70
127
  return {};
71
128
  }
72
129
 
73
- const restrictedGlobalMessages = context.options.reduce(
130
+ const restrictedGlobalMessages = restrictedGlobals.reduce(
74
131
  (memo, option) => {
75
132
  if (typeof option === "string") {
76
133
  memo[option] = null;
@@ -151,6 +208,59 @@ module.exports = {
151
208
  }
152
209
  });
153
210
  },
211
+
212
+ "Program:exit"(node) {
213
+ if (!checkGlobalObject) {
214
+ return;
215
+ }
216
+
217
+ const globalScope = sourceCode.getScope(node);
218
+ globalObjects.forEach(globalObjectName => {
219
+ const variable = astUtils.getVariableByName(
220
+ globalScope,
221
+ globalObjectName,
222
+ );
223
+
224
+ if (!variable) {
225
+ return;
226
+ }
227
+
228
+ variable.references.forEach(reference => {
229
+ const identifier = reference.identifier;
230
+ let parent = identifier.parent;
231
+
232
+ // To detect code like `window.window.Promise`.
233
+ while (
234
+ astUtils.isSpecificMemberAccess(
235
+ parent,
236
+ null,
237
+ globalObjectName,
238
+ )
239
+ ) {
240
+ parent = parent.parent;
241
+ }
242
+
243
+ const propertyName =
244
+ astUtils.getStaticPropertyName(parent);
245
+ if (propertyName && isRestricted(propertyName)) {
246
+ const customMessage =
247
+ restrictedGlobalMessages[propertyName];
248
+ const messageId = customMessage
249
+ ? "customMessage"
250
+ : "defaultMessage";
251
+
252
+ context.report({
253
+ node: parent.property,
254
+ messageId,
255
+ data: {
256
+ name: propertyName,
257
+ customMessage,
258
+ },
259
+ });
260
+ }
261
+ });
262
+ });
263
+ },
154
264
  };
155
265
  },
156
266
  };
@@ -64,6 +64,12 @@ module.exports = {
64
64
  const: {
65
65
  enum: ["always", "never", "consecutive"],
66
66
  },
67
+ using: {
68
+ enum: ["always", "never", "consecutive"],
69
+ },
70
+ awaitUsing: {
71
+ enum: ["always", "never", "consecutive"],
72
+ },
67
73
  },
68
74
  additionalProperties: false,
69
75
  },
@@ -112,6 +118,8 @@ module.exports = {
112
118
  options.var = { uninitialized: mode, initialized: mode };
113
119
  options.let = { uninitialized: mode, initialized: mode };
114
120
  options.const = { uninitialized: mode, initialized: mode };
121
+ options.using = { uninitialized: mode, initialized: mode };
122
+ options.awaitUsing = { uninitialized: mode, initialized: mode };
115
123
  } else if (typeof mode === "object") {
116
124
  // options configuration is an object
117
125
  options.separateRequires = !!mode.separateRequires;
@@ -121,15 +129,27 @@ module.exports = {
121
129
  uninitialized: mode.const,
122
130
  initialized: mode.const,
123
131
  };
132
+ options.using = {
133
+ uninitialized: mode.using,
134
+ initialized: mode.using,
135
+ };
136
+ options.awaitUsing = {
137
+ uninitialized: mode.awaitUsing,
138
+ initialized: mode.awaitUsing,
139
+ };
124
140
  if (Object.hasOwn(mode, "uninitialized")) {
125
141
  options.var.uninitialized = mode.uninitialized;
126
142
  options.let.uninitialized = mode.uninitialized;
127
143
  options.const.uninitialized = mode.uninitialized;
144
+ options.using.uninitialized = mode.uninitialized;
145
+ options.awaitUsing.uninitialized = mode.uninitialized;
128
146
  }
129
147
  if (Object.hasOwn(mode, "initialized")) {
130
148
  options.var.initialized = mode.initialized;
131
149
  options.let.initialized = mode.initialized;
132
150
  options.const.initialized = mode.initialized;
151
+ options.using.initialized = mode.initialized;
152
+ options.awaitUsing.initialized = mode.initialized;
133
153
  }
134
154
  }
135
155
 
@@ -151,6 +171,8 @@ module.exports = {
151
171
  blockStack.push({
152
172
  let: { initialized: false, uninitialized: false },
153
173
  const: { initialized: false, uninitialized: false },
174
+ using: { initialized: false, uninitialized: false },
175
+ awaitUsing: { initialized: false, uninitialized: false },
154
176
  });
155
177
  }
156
178
 
@@ -199,7 +221,7 @@ module.exports = {
199
221
 
200
222
  /**
201
223
  * Records whether initialized/uninitialized/required variables are defined in current scope.
202
- * @param {string} statementType node.kind, one of: "var", "let", or "const"
224
+ * @param {string} statementType one of: "var", "let", "const", "using", or "awaitUsing"
203
225
  * @param {ASTNode[]} declarations List of declarations
204
226
  * @param {Object} currentScope The scope being investigated
205
227
  * @returns {void}
@@ -234,7 +256,7 @@ module.exports = {
234
256
 
235
257
  /**
236
258
  * Determines the current scope (function or block)
237
- * @param {string} statementType node.kind, one of: "var", "let", or "const"
259
+ * @param {string} statementType one of: "var", "let", "const", "using", or "awaitUsing"
238
260
  * @returns {Object} The scope associated with statementType
239
261
  */
240
262
  function getCurrentScope(statementType) {
@@ -246,6 +268,10 @@ module.exports = {
246
268
  currentScope = blockStack.at(-1).let;
247
269
  } else if (statementType === "const") {
248
270
  currentScope = blockStack.at(-1).const;
271
+ } else if (statementType === "using") {
272
+ currentScope = blockStack.at(-1).using;
273
+ } else if (statementType === "awaitUsing") {
274
+ currentScope = blockStack.at(-1).awaitUsing;
249
275
  }
250
276
  return currentScope;
251
277
  }
@@ -271,7 +297,7 @@ module.exports = {
271
297
 
272
298
  /**
273
299
  * Determines if there is more than one var statement in the current scope.
274
- * @param {string} statementType node.kind, one of: "var", "let", or "const"
300
+ * @param {string} statementType one of: "var", "let", "const", "using", or "awaitUsing"
275
301
  * @param {ASTNode[]} declarations List of declarations
276
302
  * @returns {boolean} Returns true if it is the first var declaration, false if not.
277
303
  * @private
@@ -333,24 +359,27 @@ module.exports = {
333
359
  );
334
360
  const previousNode = body[currentIndex - 1];
335
361
 
336
- return fixer => {
337
- const type = sourceCode.getTokenBefore(declaration);
338
- const prevSemi = sourceCode.getTokenBefore(type);
339
- const res = [];
362
+ return function* joinDeclarationsFixer(fixer) {
363
+ const type = sourceCode.getFirstToken(declaration.parent);
364
+ const beforeType = sourceCode.getTokenBefore(type);
340
365
 
341
366
  if (
342
367
  previousNode &&
343
- previousNode.kind === sourceCode.getText(type)
368
+ previousNode.kind === declaration.parent.kind
344
369
  ) {
345
- if (prevSemi.value === ";") {
346
- res.push(fixer.replaceText(prevSemi, ","));
370
+ if (beforeType.value === ";") {
371
+ yield fixer.replaceText(beforeType, ",");
347
372
  } else {
348
- res.push(fixer.insertTextAfter(prevSemi, ","));
373
+ yield fixer.insertTextAfter(beforeType, ",");
374
+ }
375
+
376
+ if (declaration.parent.kind === "await using") {
377
+ const usingToken = sourceCode.getTokenAfter(type);
378
+ yield fixer.remove(usingToken);
349
379
  }
350
- res.push(fixer.replaceText(type, ""));
351
- }
352
380
 
353
- return res;
381
+ yield fixer.replaceText(type, "");
382
+ }
354
383
  };
355
384
  }
356
385
 
@@ -440,7 +469,10 @@ module.exports = {
440
469
  tokenAfterDeclarator.range[0],
441
470
  lastComment.range[0],
442
471
  ],
443
- `;${sourceCode.text.slice(tokenAfterDeclarator.range[1], lastComment.range[0])}${exportPlacement}${declaration.kind} `,
472
+ `;${sourceCode.text.slice(
473
+ tokenAfterDeclarator.range[1],
474
+ lastComment.range[0],
475
+ )}${exportPlacement}${declaration.kind} `,
444
476
  );
445
477
  }
446
478
 
@@ -461,8 +493,9 @@ module.exports = {
461
493
  function checkVariableDeclaration(node) {
462
494
  const parent = node.parent;
463
495
  const type = node.kind;
496
+ const key = type === "await using" ? "awaitUsing" : type;
464
497
 
465
- if (!options[type]) {
498
+ if (!options[key]) {
466
499
  return;
467
500
  }
468
501
 
@@ -471,7 +504,7 @@ module.exports = {
471
504
  const mixedRequires =
472
505
  declarations.some(isRequire) && !declarations.every(isRequire);
473
506
 
474
- if (options[type].initialized === MODE_ALWAYS) {
507
+ if (options[key].initialized === MODE_ALWAYS) {
475
508
  if (options.separateRequires && mixedRequires) {
476
509
  context.report({
477
510
  node,
@@ -508,8 +541,8 @@ module.exports = {
508
541
  );
509
542
 
510
543
  if (
511
- options[type].initialized === MODE_CONSECUTIVE &&
512
- options[type].uninitialized === MODE_CONSECUTIVE
544
+ options[key].initialized === MODE_CONSECUTIVE &&
545
+ options[key].uninitialized === MODE_CONSECUTIVE
513
546
  ) {
514
547
  context.report({
515
548
  node,
@@ -520,7 +553,7 @@ module.exports = {
520
553
  fix: joinDeclarations(declarations),
521
554
  });
522
555
  } else if (
523
- options[type].initialized === MODE_CONSECUTIVE &&
556
+ options[key].initialized === MODE_CONSECUTIVE &&
524
557
  declarationCounts.initialized > 0 &&
525
558
  previousDeclCounts.initialized > 0
526
559
  ) {
@@ -533,7 +566,7 @@ module.exports = {
533
566
  fix: joinDeclarations(declarations),
534
567
  });
535
568
  } else if (
536
- options[type].uninitialized === MODE_CONSECUTIVE &&
569
+ options[key].uninitialized === MODE_CONSECUTIVE &&
537
570
  declarationCounts.uninitialized > 0 &&
538
571
  previousDeclCounts.uninitialized > 0
539
572
  ) {
@@ -550,10 +583,10 @@ module.exports = {
550
583
  }
551
584
 
552
585
  // always
553
- if (!hasOnlyOneStatement(type, declarations)) {
586
+ if (!hasOnlyOneStatement(key, declarations)) {
554
587
  if (
555
- options[type].initialized === MODE_ALWAYS &&
556
- options[type].uninitialized === MODE_ALWAYS
588
+ options[key].initialized === MODE_ALWAYS &&
589
+ options[key].uninitialized === MODE_ALWAYS
557
590
  ) {
558
591
  context.report({
559
592
  node,
@@ -565,7 +598,7 @@ module.exports = {
565
598
  });
566
599
  } else {
567
600
  if (
568
- options[type].initialized === MODE_ALWAYS &&
601
+ options[key].initialized === MODE_ALWAYS &&
569
602
  declarationCounts.initialized > 0
570
603
  ) {
571
604
  context.report({
@@ -578,7 +611,7 @@ module.exports = {
578
611
  });
579
612
  }
580
613
  if (
581
- options[type].uninitialized === MODE_ALWAYS &&
614
+ options[key].uninitialized === MODE_ALWAYS &&
582
615
  declarationCounts.uninitialized > 0
583
616
  ) {
584
617
  if (
@@ -608,8 +641,8 @@ module.exports = {
608
641
 
609
642
  if (totalDeclarations > 1) {
610
643
  if (
611
- options[type].initialized === MODE_NEVER &&
612
- options[type].uninitialized === MODE_NEVER
644
+ options[key].initialized === MODE_NEVER &&
645
+ options[key].uninitialized === MODE_NEVER
613
646
  ) {
614
647
  // both initialized and uninitialized
615
648
  context.report({
@@ -621,7 +654,7 @@ module.exports = {
621
654
  fix: splitDeclarations(node),
622
655
  });
623
656
  } else if (
624
- options[type].initialized === MODE_NEVER &&
657
+ options[key].initialized === MODE_NEVER &&
625
658
  declarationCounts.initialized > 0
626
659
  ) {
627
660
  // initialized
@@ -634,7 +667,7 @@ module.exports = {
634
667
  fix: splitDeclarations(node),
635
668
  });
636
669
  } else if (
637
- options[type].uninitialized === MODE_NEVER &&
670
+ options[key].uninitialized === MODE_NEVER &&
638
671
  declarationCounts.uninitialized > 0
639
672
  ) {
640
673
  // uninitialized
@@ -3403,13 +3403,26 @@ export interface ESLintRules extends Linter.RulesRecord {
3403
3403
  */
3404
3404
  "no-restricted-globals": Linter.RuleEntry<
3405
3405
  [
3406
- ...Array<
3407
- | string
3408
- | {
3409
- name: string;
3410
- message?: string | undefined;
3411
- }
3412
- >,
3406
+ ...(
3407
+ | Array<
3408
+ | string
3409
+ | {
3410
+ name: string;
3411
+ message?: string | undefined;
3412
+ }
3413
+ >
3414
+ | Array<{
3415
+ globals: Array<
3416
+ | string
3417
+ | {
3418
+ name: string;
3419
+ message?: string | undefined;
3420
+ }
3421
+ >;
3422
+ checkGlobalObject?: boolean;
3423
+ globalObjects?: string[];
3424
+ }>
3425
+ ),
3413
3426
  ]
3414
3427
  >;
3415
3428
 
@@ -4509,7 +4522,7 @@ export interface ESLintRules extends Linter.RulesRecord {
4509
4522
  */
4510
4523
  separateRequires: boolean;
4511
4524
  } & Record<
4512
- "var" | "let" | "const",
4525
+ "var" | "let" | "const" | "using" | "awaitUsing",
4513
4526
  "always" | "never" | "consecutive"
4514
4527
  >
4515
4528
  >
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint",
3
- "version": "9.32.0",
3
+ "version": "9.33.0",
4
4
  "author": "Nicholas C. Zakas <nicholas+npm@nczconsulting.com>",
5
5
  "description": "An AST-based pattern checker for JavaScript.",
6
6
  "type": "commonjs",
@@ -107,11 +107,11 @@
107
107
  "@eslint-community/eslint-utils": "^4.2.0",
108
108
  "@eslint-community/regexpp": "^4.12.1",
109
109
  "@eslint/config-array": "^0.21.0",
110
- "@eslint/config-helpers": "^0.3.0",
111
- "@eslint/core": "^0.15.0",
110
+ "@eslint/config-helpers": "^0.3.1",
111
+ "@eslint/core": "^0.15.2",
112
112
  "@eslint/eslintrc": "^3.3.1",
113
- "@eslint/js": "9.32.0",
114
- "@eslint/plugin-kit": "^0.3.4",
113
+ "@eslint/js": "9.33.0",
114
+ "@eslint/plugin-kit": "^0.3.5",
115
115
  "@humanfs/node": "^0.16.6",
116
116
  "@humanwhocodes/module-importer": "^1.0.1",
117
117
  "@humanwhocodes/retry": "^0.4.2",