eslint 9.25.0 → 9.26.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.
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @fileoverview Disallow shadowing of NaN, undefined, and Infinity (ES5 section 15.1.1)
2
+ * @fileoverview Disallow shadowing of globalThis, NaN, undefined, and Infinity (ES2020 section 18.1)
3
3
  * @author Michael Ficarra
4
4
  */
5
5
  "use strict";
@@ -32,13 +32,29 @@ module.exports = {
32
32
  meta: {
33
33
  type: "suggestion",
34
34
 
35
+ defaultOptions: [
36
+ {
37
+ reportGlobalThis: false,
38
+ },
39
+ ],
40
+
35
41
  docs: {
36
42
  description: "Disallow identifiers from shadowing restricted names",
37
43
  recommended: true,
38
44
  url: "https://eslint.org/docs/latest/rules/no-shadow-restricted-names",
39
45
  },
40
46
 
41
- schema: [],
47
+ schema: [
48
+ {
49
+ type: "object",
50
+ properties: {
51
+ reportGlobalThis: {
52
+ type: "boolean",
53
+ },
54
+ },
55
+ additionalProperties: false,
56
+ },
57
+ ],
42
58
 
43
59
  messages: {
44
60
  shadowingRestrictedName: "Shadowing of global property '{{name}}'.",
@@ -46,6 +62,8 @@ module.exports = {
46
62
  },
47
63
 
48
64
  create(context) {
65
+ const [{ reportGlobalThis }] = context.options;
66
+
49
67
  const RESTRICTED = new Set([
50
68
  "undefined",
51
69
  "NaN",
@@ -53,6 +71,11 @@ module.exports = {
53
71
  "arguments",
54
72
  "eval",
55
73
  ]);
74
+
75
+ if (reportGlobalThis) {
76
+ RESTRICTED.add("globalThis");
77
+ }
78
+
56
79
  const sourceCode = context.sourceCode;
57
80
 
58
81
  // Track reported nodes to avoid duplicate reports. For example, on class declarations.
@@ -29,6 +29,8 @@ function alwaysFalse() {
29
29
  /** @type {import('../types').Rule.RuleModule} */
30
30
  module.exports = {
31
31
  meta: {
32
+ dialects: ["javascript", "typescript"],
33
+ language: "javascript",
32
34
  type: "suggestion",
33
35
 
34
36
  docs: {
@@ -53,6 +55,9 @@ module.exports = {
53
55
  enforceForJSX: {
54
56
  type: "boolean",
55
57
  },
58
+ ignoreDirectives: {
59
+ type: "boolean",
60
+ },
56
61
  },
57
62
  additionalProperties: false,
58
63
  },
@@ -64,6 +69,7 @@ module.exports = {
64
69
  allowTernary: false,
65
70
  allowTaggedTemplates: false,
66
71
  enforceForJSX: false,
72
+ ignoreDirectives: false,
67
73
  },
68
74
  ],
69
75
 
@@ -80,9 +86,65 @@ module.exports = {
80
86
  allowTernary,
81
87
  allowTaggedTemplates,
82
88
  enforceForJSX,
89
+ ignoreDirectives,
83
90
  },
84
91
  ] = context.options;
85
92
 
93
+ /**
94
+ * Has AST suggesting a directive.
95
+ * @param {ASTNode} node any node
96
+ * @returns {boolean} whether the given node structurally represents a directive
97
+ */
98
+ function looksLikeDirective(node) {
99
+ return (
100
+ node.type === "ExpressionStatement" &&
101
+ node.expression.type === "Literal" &&
102
+ typeof node.expression.value === "string"
103
+ );
104
+ }
105
+
106
+ /**
107
+ * Gets the leading sequence of members in a list that pass the predicate.
108
+ * @param {Function} predicate ([a] -> Boolean) the function used to make the determination
109
+ * @param {a[]} list the input list
110
+ * @returns {a[]} the leading sequence of members in the given list that pass the given predicate
111
+ */
112
+ function takeWhile(predicate, list) {
113
+ for (let i = 0; i < list.length; ++i) {
114
+ if (!predicate(list[i])) {
115
+ return list.slice(0, i);
116
+ }
117
+ }
118
+ return list.slice();
119
+ }
120
+
121
+ /**
122
+ * Gets leading directives nodes in a Node body.
123
+ * @param {ASTNode} node a Program or BlockStatement node
124
+ * @returns {ASTNode[]} the leading sequence of directive nodes in the given node's body
125
+ */
126
+ function directives(node) {
127
+ return takeWhile(looksLikeDirective, node.body);
128
+ }
129
+
130
+ /**
131
+ * Detect if a Node is a directive.
132
+ * @param {ASTNode} node any node
133
+ * @returns {boolean} whether the given node is considered a directive in its current position
134
+ */
135
+ function isDirective(node) {
136
+ /**
137
+ * https://tc39.es/ecma262/#directive-prologue
138
+ *
139
+ * Only `FunctionBody`, `ScriptBody` and `ModuleBody` can have directive prologue.
140
+ * Class static blocks do not have directive prologue.
141
+ */
142
+ return (
143
+ astUtils.isTopLevelExpressionStatement(node) &&
144
+ directives(node.parent).includes(node)
145
+ );
146
+ }
147
+
86
148
  /**
87
149
  * The member functions return `true` if the type has no side-effects.
88
150
  * Unknown nodes are handled as `false`, then this rule ignores those.
@@ -154,7 +216,8 @@ module.exports = {
154
216
  ExpressionStatement(node) {
155
217
  if (
156
218
  Checker.isDisallowed(node.expression) &&
157
- !astUtils.isDirective(node)
219
+ !astUtils.isDirective(node) &&
220
+ !(ignoreDirectives && isDirective(node))
158
221
  ) {
159
222
  context.report({ node, messageId: "unusedExpression" });
160
223
  }
@@ -1088,13 +1088,13 @@ function isConstant(scope, node, inBooleanPosition) {
1088
1088
  }
1089
1089
 
1090
1090
  /**
1091
- * Checks whether a node is an ExpressionStatement at the top level of a file or function body.
1091
+ * Checks whether a node is an ExpressionStatement at the top level of a file, function body, or TypeScript module block.
1092
1092
  * A top-level ExpressionStatement node is a directive if it contains a single unparenthesized
1093
1093
  * string literal and if it occurs either as the first sibling or immediately after another
1094
1094
  * directive.
1095
1095
  * @param {ASTNode} node The node to check.
1096
1096
  * @returns {boolean} Whether or not the node is an ExpressionStatement at the top level of a
1097
- * file or function body.
1097
+ * file, function body, or TypeScript module block.
1098
1098
  */
1099
1099
  function isTopLevelExpressionStatement(node) {
1100
1100
  if (node.type !== "ExpressionStatement") {
@@ -1104,6 +1104,7 @@ function isTopLevelExpressionStatement(node) {
1104
1104
 
1105
1105
  return (
1106
1106
  parent.type === "Program" ||
1107
+ parent.type === "TSModuleBlock" ||
1107
1108
  (parent.type === "BlockStatement" && isFunction(parent.parent))
1108
1109
  );
1109
1110
  }
@@ -6,7 +6,7 @@
6
6
 
7
7
  const debug = require("debug")("eslint:rules");
8
8
 
9
- /** @typedef {import("../../shared/types").Rule} Rule */
9
+ /** @typedef {import("../../types").Rule.RuleModule} Rule */
10
10
 
11
11
  /**
12
12
  * The `Map` object that loads each rule when it's accessed.
@@ -19,7 +19,7 @@ const debug = require("debug")("eslint:rules");
19
19
  *
20
20
  * rules.get("semi"); // call `() => require("semi")` here.
21
21
  *
22
- * @extends {Map<string, () => Rule>}
22
+ * @extends {Map<string, Rule>}
23
23
  */
24
24
  class LazyLoadingRuleMap extends Map {
25
25
  /**
@@ -20,7 +20,6 @@ const { VFile } = require("../linter/vfile.js");
20
20
  /** @typedef {import("../shared/types.js").LintMessage} LintMessage */
21
21
  /** @typedef {import("../linter/vfile.js").VFile} VFile */
22
22
  /** @typedef {import("@eslint/core").Language} Language */
23
- /** @typedef {import("@eslint/core").LanguageOptions} LanguageOptions */
24
23
  /** @typedef {import("eslint").Linter.Processor} Processor */
25
24
 
26
25
  //-----------------------------------------------------------------------------
@@ -26,21 +26,44 @@ function isSerializablePrimitiveOrPlainObject(val) {
26
26
  * Check if a value is serializable.
27
27
  * Functions or objects like RegExp cannot be serialized by JSON.stringify().
28
28
  * Inspired by: https://stackoverflow.com/questions/30579940/reliable-way-to-check-if-objects-is-serializable-in-javascript
29
- * @param {any} val the value
30
- * @returns {boolean} true if the value is serializable
29
+ * @param {any} val The value
30
+ * @param {Set<Object>} seenObjects Objects already seen in this path from the root object.
31
+ * @returns {boolean} `true` if the value is serializable
31
32
  */
32
- function isSerializable(val) {
33
+ function isSerializable(val, seenObjects = new Set()) {
33
34
  if (!isSerializablePrimitiveOrPlainObject(val)) {
34
35
  return false;
35
36
  }
36
- if (typeof val === "object") {
37
+ if (typeof val === "object" && val !== null) {
38
+ if (seenObjects.has(val)) {
39
+ /*
40
+ * Since this is a depth-first traversal, encountering
41
+ * the same object again means there is a circular reference.
42
+ * Objects with circular references are not serializable.
43
+ */
44
+ return false;
45
+ }
37
46
  for (const property in val) {
38
47
  if (Object.hasOwn(val, property)) {
39
48
  if (!isSerializablePrimitiveOrPlainObject(val[property])) {
40
49
  return false;
41
50
  }
42
- if (typeof val[property] === "object") {
43
- if (!isSerializable(val[property])) {
51
+ if (
52
+ typeof val[property] === "object" &&
53
+ val[property] !== null
54
+ ) {
55
+ if (
56
+ /*
57
+ * We're creating a new Set of seen objects because we want to
58
+ * ensure that `val` doesn't appear again in this path, but it can appear
59
+ * in other paths. This allows for resuing objects in the graph, as long as
60
+ * there are no cycles.
61
+ */
62
+ !isSerializable(
63
+ val[property],
64
+ new Set([...seenObjects, val]),
65
+ )
66
+ ) {
44
67
  return false;
45
68
  }
46
69
  }
@@ -26,15 +26,6 @@ module.exports = {};
26
26
  * @property {boolean} [allowReserved] Allowing the use of reserved words as identifiers in ES3.
27
27
  */
28
28
 
29
- /**
30
- * @typedef {Object} LanguageOptions
31
- * @property {number|"latest"} [ecmaVersion] The ECMAScript version (or revision number).
32
- * @property {Record<string, GlobalConf>} [globals] The global variable settings.
33
- * @property {"script"|"module"|"commonjs"} [sourceType] The source code type.
34
- * @property {string|Object} [parser] The parser to use.
35
- * @property {Object} [parserOptions] The parser options to use.
36
- */
37
-
38
29
  /**
39
30
  * @typedef {Object} ConfigData
40
31
  * @property {Record<string, boolean>} [env] The environment settings.
@@ -166,14 +157,6 @@ module.exports = {};
166
157
  * @property {{ name?: string, url?: string }} [rule] Name and information of the replacement rule
167
158
  */
168
159
 
169
- /**
170
- * @typedef {Object} Plugin
171
- * @property {Record<string, ConfigData>} [configs] The definition of plugin configs.
172
- * @property {Record<string, Environment>} [environments] The definition of plugin environments.
173
- * @property {Record<string, Processor>} [processors] The definition of plugin processors.
174
- * @property {Record<string, Rule>} [rules] The definition of plugin rules.
175
- */
176
-
177
160
  /**
178
161
  * Information of deprecated rules.
179
162
  * @typedef {Object} DeprecatedRuleInfo
@@ -35,7 +35,6 @@ import type {
35
35
  LanguageOptions as GenericLanguageOptions,
36
36
  RuleDefinition,
37
37
  RuleContext as CoreRuleContext,
38
- RuleContextTypeOptions,
39
38
  DeprecatedInfo,
40
39
  } from "@eslint/core";
41
40
  import { JSONSchema4 } from "json-schema";
@@ -1934,6 +1933,9 @@ export namespace ESLint {
1934
1933
  }
1935
1934
 
1936
1935
  interface Plugin extends ObjectMetaProperties {
1936
+ meta?: ObjectMetaProperties["meta"] & {
1937
+ namespace?: string | undefined;
1938
+ };
1937
1939
  configs?:
1938
1940
  | Record<
1939
1941
  string,
@@ -2121,7 +2123,7 @@ export class RuleTester {
2121
2123
 
2122
2124
  run(
2123
2125
  name: string,
2124
- rule: Rule.RuleModule,
2126
+ rule: RuleDefinition,
2125
2127
  tests: {
2126
2128
  valid: Array<string | RuleTester.ValidTestCase>;
2127
2129
  invalid: RuleTester.InvalidTestCase[];
@@ -3578,7 +3578,16 @@ export interface ESLintRules extends Linter.RulesRecord {
3578
3578
  * @since 0.1.4
3579
3579
  * @see https://eslint.org/docs/latest/rules/no-shadow-restricted-names
3580
3580
  */
3581
- "no-shadow-restricted-names": Linter.RuleEntry<[]>;
3581
+ "no-shadow-restricted-names": Linter.RuleEntry<
3582
+ [
3583
+ Partial<{
3584
+ /**
3585
+ * @default false
3586
+ */
3587
+ reportGlobalThis: boolean;
3588
+ }>,
3589
+ ]
3590
+ >;
3582
3591
 
3583
3592
  /**
3584
3593
  * Rule to disallow spacing between function identifiers and their applications (deprecated).
@@ -3935,6 +3944,10 @@ export interface ESLintRules extends Linter.RulesRecord {
3935
3944
  * @default false
3936
3945
  */
3937
3946
  enforceForJSX: boolean;
3947
+ /**
3948
+ * @default false
3949
+ */
3950
+ ignoreDirectives: boolean;
3938
3951
  }>,
3939
3952
  ]
3940
3953
  >;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint",
3
- "version": "9.25.0",
3
+ "version": "9.26.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",
@@ -110,11 +110,12 @@
110
110
  "@eslint/config-helpers": "^0.2.1",
111
111
  "@eslint/core": "^0.13.0",
112
112
  "@eslint/eslintrc": "^3.3.1",
113
- "@eslint/js": "9.25.0",
113
+ "@eslint/js": "9.26.0",
114
114
  "@eslint/plugin-kit": "^0.2.8",
115
115
  "@humanfs/node": "^0.16.6",
116
116
  "@humanwhocodes/module-importer": "^1.0.1",
117
117
  "@humanwhocodes/retry": "^0.4.2",
118
+ "@modelcontextprotocol/sdk": "^1.8.0",
118
119
  "@types/estree": "^1.0.6",
119
120
  "@types/json-schema": "^7.0.15",
120
121
  "ajv": "^6.12.4",
@@ -138,15 +139,17 @@
138
139
  "lodash.merge": "^4.6.2",
139
140
  "minimatch": "^3.1.2",
140
141
  "natural-compare": "^1.4.0",
141
- "optionator": "^0.9.3"
142
+ "optionator": "^0.9.3",
143
+ "zod": "^3.24.2"
142
144
  },
143
145
  "devDependencies": {
144
146
  "@arethetypeswrong/cli": "^0.17.0",
145
147
  "@babel/core": "^7.4.3",
146
148
  "@babel/preset-env": "^7.4.3",
147
149
  "@cypress/webpack-preprocessor": "^6.0.2",
148
- "@eslint/json": "^0.11.0",
150
+ "@eslint/json": "^0.12.0",
149
151
  "@trunkio/launcher": "^1.3.4",
152
+ "@types/esquery": "^1.5.4",
150
153
  "@types/node": "^22.13.14",
151
154
  "@typescript-eslint/parser": "^8.4.0",
152
155
  "babel-loader": "^8.0.5",