@lexical/eslint-plugin 0.41.1-nightly.20260319.0 → 0.42.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.
@@ -24,17 +24,17 @@ function getDefaultExportFromCjs (x) {
24
24
  return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
25
25
  }
26
26
 
27
- var name$1 = "@lexical/eslint-plugin";
28
- var version$1 = "0.41.1-nightly.20260319.0";
27
+ var name = "@lexical/eslint-plugin";
28
+ var version = "0.42.0";
29
29
  var require$$0 = {
30
- name: name$1,
31
- version: version$1};
30
+ name: name,
31
+ version: version};
32
32
 
33
- var rulesOfLexical$1 = {};
33
+ var rulesOfLexical = {};
34
34
 
35
- var getFunctionName$1 = {};
35
+ var getFunctionName = {};
36
36
 
37
- var getParentAssignmentName$2 = {};
37
+ var getParentAssignmentName = {};
38
38
 
39
39
  /**
40
40
  * Copyright (c) Meta Platforms, Inc. and affiliates.
@@ -44,31 +44,39 @@ var getParentAssignmentName$2 = {};
44
44
  *
45
45
  */
46
46
 
47
- /**
48
- * Gets the static name of an AST node's parent, used to determine the name of an
49
- * anonymous function declaration, possibly through a higher order function call.
50
- * This was extracted from the body of getFunctionName so it could also be used
51
- * in the context of `useCallback` or `useMemo`, e.g.
52
- * `const $fun = useCallback(() => {}, [])` where the name is not the direct
53
- * parent of the anonymous function.
54
- *
55
- * @param {import('eslint').Rule.Node} node
56
- */
57
- getParentAssignmentName$2.getParentAssignmentName = function getParentAssignmentName(node) {
58
- // Unlike React's rules of hooks, this does not check property assignment.
59
- // The rules of lexical $function convention only applies to functions,
60
- // not methods or properties.
61
- const parentNode = node.parent;
62
- if (parentNode.type === 'VariableDeclarator' && parentNode.init === node) {
63
- // const $function = () => {};
64
- return parentNode.id;
65
- } else if (parentNode.type === 'AssignmentExpression' && parentNode.right === node && parentNode.operator === '=') {
66
- // $function = () => {};
67
- return parentNode.left;
68
- } else {
69
- return undefined;
70
- }
71
- };
47
+ var hasRequiredGetParentAssignmentName;
48
+
49
+ function requireGetParentAssignmentName () {
50
+ if (hasRequiredGetParentAssignmentName) return getParentAssignmentName;
51
+ hasRequiredGetParentAssignmentName = 1;
52
+
53
+ /**
54
+ * Gets the static name of an AST node's parent, used to determine the name of an
55
+ * anonymous function declaration, possibly through a higher order function call.
56
+ * This was extracted from the body of getFunctionName so it could also be used
57
+ * in the context of `useCallback` or `useMemo`, e.g.
58
+ * `const $fun = useCallback(() => {}, [])` where the name is not the direct
59
+ * parent of the anonymous function.
60
+ *
61
+ * @param {import('eslint').Rule.Node} node
62
+ */
63
+ getParentAssignmentName.getParentAssignmentName = function getParentAssignmentName(node) {
64
+ // Unlike React's rules of hooks, this does not check property assignment.
65
+ // The rules of lexical $function convention only applies to functions,
66
+ // not methods or properties.
67
+ const parentNode = node.parent;
68
+ if (parentNode.type === 'VariableDeclarator' && parentNode.init === node) {
69
+ // const $function = () => {};
70
+ return parentNode.id;
71
+ } else if (parentNode.type === 'AssignmentExpression' && parentNode.right === node && parentNode.operator === '=') {
72
+ // $function = () => {};
73
+ return parentNode.left;
74
+ } else {
75
+ return undefined;
76
+ }
77
+ };
78
+ return getParentAssignmentName;
79
+ }
72
80
 
73
81
  /**
74
82
  * Copyright (c) Meta Platforms, Inc. and affiliates.
@@ -78,40 +86,47 @@ getParentAssignmentName$2.getParentAssignmentName = function getParentAssignment
78
86
  *
79
87
  */
80
88
 
81
- // @ts-check
89
+ var hasRequiredGetFunctionName;
90
+
91
+ function requireGetFunctionName () {
92
+ if (hasRequiredGetFunctionName) return getFunctionName;
93
+ hasRequiredGetFunctionName = 1;
94
+ // @ts-check
95
+
96
+ const {
97
+ getParentAssignmentName
98
+ } = /*@__PURE__*/ requireGetParentAssignmentName();
99
+
100
+ /**
101
+ * Gets the static name of a function AST node. For function declarations it is
102
+ * easy. For anonymous function expressions it is much harder. If you search for
103
+ * `IsAnonymousFunctionDefinition()` in the ECMAScript spec you'll find places
104
+ * where JS gives anonymous function expressions names. We roughly detect the
105
+ * same AST nodes with some exceptions to better fit our use case.
106
+ *
107
+ * @param {import('eslint').Rule.Node} node
108
+ */
109
+ getFunctionName.getFunctionName = function getFunctionName(node) {
110
+ if (node.type === 'FunctionDeclaration' || node.type === 'FunctionExpression' && node.id) {
111
+ // function $function() {}
112
+ // const whatever = function $function() {};
113
+ //
114
+ // Function declaration or function expression names win over any
115
+ // assignment statements or other renames.
116
+ return node.id;
117
+ } else if (node.type === 'FunctionExpression' || node.type === 'ArrowFunctionExpression') {
118
+ // This checks for assignments such as
119
+ // const $function = function () {};
120
+ // const $function = () => {};
121
+ return getParentAssignmentName(node);
122
+ } else {
123
+ return undefined;
124
+ }
125
+ };
126
+ return getFunctionName;
127
+ }
82
128
 
83
- const {
84
- getParentAssignmentName: getParentAssignmentName$1
85
- } = getParentAssignmentName$2;
86
-
87
- /**
88
- * Gets the static name of a function AST node. For function declarations it is
89
- * easy. For anonymous function expressions it is much harder. If you search for
90
- * `IsAnonymousFunctionDefinition()` in the ECMAScript spec you'll find places
91
- * where JS gives anonymous function expressions names. We roughly detect the
92
- * same AST nodes with some exceptions to better fit our use case.
93
- *
94
- * @param {import('eslint').Rule.Node} node
95
- */
96
- getFunctionName$1.getFunctionName = function getFunctionName(node) {
97
- if (node.type === 'FunctionDeclaration' || node.type === 'FunctionExpression' && node.id) {
98
- // function $function() {}
99
- // const whatever = function $function() {};
100
- //
101
- // Function declaration or function expression names win over any
102
- // assignment statements or other renames.
103
- return node.id;
104
- } else if (node.type === 'FunctionExpression' || node.type === 'ArrowFunctionExpression') {
105
- // This checks for assignments such as
106
- // const $function = function () {};
107
- // const $function = () => {};
108
- return getParentAssignmentName$1(node);
109
- } else {
110
- return undefined;
111
- }
112
- };
113
-
114
- var buildMatcher$1 = {};
129
+ var buildMatcher = {};
115
130
 
116
131
  /**
117
132
  * Copyright (c) Meta Platforms, Inc. and affiliates.
@@ -121,76 +136,83 @@ var buildMatcher$1 = {};
121
136
  *
122
137
  */
123
138
 
124
- /**
125
- * @typedef {import('estree').Node} Node
126
- * @typedef {import('estree').Identifier} Identifier
127
- * @typedef {(name: string, node: Identifier) => boolean} NameIdentifierMatcher
128
- * @typedef {NameIdentifierMatcher | string | RegExp | undefined} ToMatcher
129
- * @typedef {(node: Identifier | undefined) => boolean} IdentifierMatcher
130
- */
139
+ var hasRequiredBuildMatcher;
140
+
141
+ function requireBuildMatcher () {
142
+ if (hasRequiredBuildMatcher) return buildMatcher;
143
+ hasRequiredBuildMatcher = 1;
144
+ /**
145
+ * @typedef {import('estree').Node} Node
146
+ * @typedef {import('estree').Identifier} Identifier
147
+ * @typedef {(name: string, node: Identifier) => boolean} NameIdentifierMatcher
148
+ * @typedef {NameIdentifierMatcher | string | RegExp | undefined} ToMatcher
149
+ * @typedef {(node: Identifier | undefined) => boolean} IdentifierMatcher
150
+ */
151
+
152
+ /**
153
+ * Escape a string for exact match in a RegExp
154
+ * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_expressions#escaping
155
+ *
156
+ * @param {string} string
157
+ * @returns {string}
158
+ */
159
+ function escapeRegExp(string) {
160
+ return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
161
+ }
131
162
 
132
- /**
133
- * Escape a string for exact match in a RegExp
134
- * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_expressions#escaping
135
- *
136
- * @param {string} string
137
- * @returns {string}
138
- */
139
- function escapeRegExp(string) {
140
- return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
163
+ /**
164
+ * Build an Identifier Node Matcher from the given ToMatcher arguments.
165
+ * The Matcher is roughly equivalent to building RegExp from all of the
166
+ * sources and or-ing them together. String arguments are treated as
167
+ * RegExp sources and will be escaped with an implicit '^...$' wrapper
168
+ * unless it starts with a '(' or '^'
169
+ *
170
+ * @param {(ToMatcher | ToMatcher[])[]} args
171
+ * @returns {IdentifierMatcher}
172
+ */
173
+ buildMatcher.buildMatcher = function buildMatcher(...toMatchers) {
174
+ /** @type {Matcher[]} */
175
+ const matchFuns = [];
176
+ /** @type {string[]} */
177
+ const regExpSources = [];
178
+ for (const arg of toMatchers.flat(1)) {
179
+ if (!arg) {
180
+ continue;
181
+ } else if (typeof arg === 'string') {
182
+ regExpSources.push(/^[(^]/.test(arg) ? arg : `^${escapeRegExp(arg)}$`);
183
+ } else if (arg && arg instanceof RegExp) {
184
+ if (arg.flags) {
185
+ matchFuns.push(s => arg.test(s));
186
+ } else {
187
+ regExpSources.push(arg.source);
188
+ }
189
+ } else if (typeof arg === 'function') {
190
+ matchFuns.push(arg);
191
+ }
192
+ }
193
+ const pattern = regExpSources.map(s => `(?:${s})`).join('|');
194
+ if (pattern) {
195
+ const re = new RegExp(pattern);
196
+ matchFuns.push(s => re.test(s));
197
+ }
198
+ return node => {
199
+ if (node) {
200
+ if (node.type !== 'Identifier') {
201
+ // Runtime type invariant check
202
+ throw new Error(`Expecting Identifier, not ${node.type}`);
203
+ }
204
+ for (const matcher of matchFuns) {
205
+ if (matcher(node.name, node)) {
206
+ return true;
207
+ }
208
+ }
209
+ }
210
+ return false;
211
+ };
212
+ };
213
+ return buildMatcher;
141
214
  }
142
215
 
143
- /**
144
- * Build an Identifier Node Matcher from the given ToMatcher arguments.
145
- * The Matcher is roughly equivalent to building RegExp from all of the
146
- * sources and or-ing them together. String arguments are treated as
147
- * RegExp sources and will be escaped with an implicit '^...$' wrapper
148
- * unless it starts with a '(' or '^'
149
- *
150
- * @param {(ToMatcher | ToMatcher[])[]} args
151
- * @returns {IdentifierMatcher}
152
- */
153
- buildMatcher$1.buildMatcher = function buildMatcher(...toMatchers) {
154
- /** @type {Matcher[]} */
155
- const matchFuns = [];
156
- /** @type {string[]} */
157
- const regExpSources = [];
158
- for (const arg of toMatchers.flat(1)) {
159
- if (!arg) {
160
- continue;
161
- } else if (typeof arg === 'string') {
162
- regExpSources.push(/^[(^]/.test(arg) ? arg : `^${escapeRegExp(arg)}$`);
163
- } else if (arg && arg instanceof RegExp) {
164
- if (arg.flags) {
165
- matchFuns.push(s => arg.test(s));
166
- } else {
167
- regExpSources.push(arg.source);
168
- }
169
- } else if (typeof arg === 'function') {
170
- matchFuns.push(arg);
171
- }
172
- }
173
- const pattern = regExpSources.map(s => `(?:${s})`).join('|');
174
- if (pattern) {
175
- const re = new RegExp(pattern);
176
- matchFuns.push(s => re.test(s));
177
- }
178
- return node => {
179
- if (node) {
180
- if (node.type !== 'Identifier') {
181
- // Runtime type invariant check
182
- throw new Error(`Expecting Identifier, not ${node.type}`);
183
- }
184
- for (const matcher of matchFuns) {
185
- if (matcher(node.name, node)) {
186
- return true;
187
- }
188
- }
189
- }
190
- return false;
191
- };
192
- };
193
-
194
216
  /**
195
217
  * Copyright (c) Meta Platforms, Inc. and affiliates.
196
218
  *
@@ -199,392 +221,399 @@ buildMatcher$1.buildMatcher = function buildMatcher(...toMatchers) {
199
221
  *
200
222
  */
201
223
 
202
- // @ts-check
203
-
204
- const {
205
- getFunctionName
206
- } = getFunctionName$1;
207
- const {
208
- getParentAssignmentName
209
- } = getParentAssignmentName$2;
210
- const {
211
- buildMatcher
212
- } = buildMatcher$1;
213
-
214
- /**
215
- * @typedef {import('eslint').Rule.NodeParentExtension} NodeParentExtension
216
- * @typedef {import('estree').CallExpression & NodeParentExtension} CallExpression
217
- * @typedef {import('estree').Identifier & NodeParentExtension} Identifier
218
- * @typedef {import('eslint').Rule.RuleContext} RuleContext
219
- * @typedef {import('eslint').Rule.Fix} Fix
220
- * @typedef {import('eslint').Rule.Node} Node
221
- * @typedef {import('eslint').Rule.RuleModule} RuleModule
222
- * @typedef {import('eslint').Rule.ReportFixer} ReportFixer
223
- * @typedef {import('eslint').SourceCode} SourceCode
224
- * @typedef {import('eslint').Scope.Variable} Variable
225
- * @typedef {import('eslint').Scope.Scope} Scope
226
- */
227
-
228
- /**
229
- * Find the variable associated with the given Identifier
230
- *
231
- * @param {SourceCode} sourceCode
232
- * @param {Identifier} identifier
233
- */
234
- function getIdentifierVariable(sourceCode, identifier) {
235
- const scopeManager = sourceCode.scopeManager;
236
- for (let node = /** @type {Node | null} */identifier; node; node = /** @type {Node | null}*/node.parent) {
237
- const variable = scopeManager.getDeclaredVariables(node).find(v => v.identifiers.includes(identifier));
238
- if (variable) {
239
- return variable;
240
- }
241
- const scope = scopeManager.acquire(node);
242
- if (scope) {
243
- return scope.set.get(identifier.name) || (scope.upper ? scope.upper.set.get(identifier.name) : undefined);
244
- }
245
- }
246
- return undefined;
247
- }
248
-
249
- /**
250
- * @typedef {import('../util/buildMatcher.js').ToMatcher} ToMatcher
251
- * @typedef {import('../util/buildMatcher.js').IdentifierMatcher} IdentifierMatcher
252
- */
253
-
254
- /**
255
- * @template T
256
- * @typedef {Object} BaseMatchers<T>
257
- * @property {T} isDollarFunction Catch all identifiers that begin with '$' or 'INTERNAL_$' followed by a lowercase Latin character or underscore
258
- * @property {T} isIgnoredFunction These functions may call any $functions even though they do not have the isDollarFunction naming convention
259
- * @property {T} isLexicalProvider Certain calls through the editor or editorState allow for implicit access to call $functions: read, registerCommand, registerNodeTransform, update.
260
- * @property {T} isSafeDollarFunction It's usually safe to call $isNode functions, so any '$is' or 'INTERNAL_$is' function may be called in any context.
261
- */
262
-
263
- /** @type {BaseMatchers<Exclude<ToMatcher, undefined>[]>} */
264
- const BaseMatchers = {
265
- isDollarFunction: [/^\$[a-z_]/],
266
- isIgnoredFunction: [],
267
- isLexicalProvider: ['parseEditorState', 'read', 'registerCommand', 'registerNodeTransform', 'update'],
268
- isSafeDollarFunction: [/^\$is[A-Z_]/]
269
- };
270
-
271
- /**
272
- * @typedef {Partial<BaseMatchers<ToMatcher | ToMatcher[]>>} RulesOfLexicalOptions
273
- * @typedef {BaseMatchers<IdentifierMatcher>} Matchers
274
- */
224
+ var hasRequiredRulesOfLexical;
225
+
226
+ function requireRulesOfLexical () {
227
+ if (hasRequiredRulesOfLexical) return rulesOfLexical;
228
+ hasRequiredRulesOfLexical = 1;
229
+ // @ts-check
230
+
231
+ const {
232
+ getFunctionName
233
+ } = /*@__PURE__*/ requireGetFunctionName();
234
+ const {
235
+ getParentAssignmentName
236
+ } = /*@__PURE__*/ requireGetParentAssignmentName();
237
+ const {
238
+ buildMatcher
239
+ } = /*@__PURE__*/ requireBuildMatcher();
240
+
241
+ /**
242
+ * @typedef {import('eslint').Rule.NodeParentExtension} NodeParentExtension
243
+ * @typedef {import('estree').CallExpression & NodeParentExtension} CallExpression
244
+ * @typedef {import('estree').Identifier & NodeParentExtension} Identifier
245
+ * @typedef {import('eslint').Rule.RuleContext} RuleContext
246
+ * @typedef {import('eslint').Rule.Fix} Fix
247
+ * @typedef {import('eslint').Rule.Node} Node
248
+ * @typedef {import('eslint').Rule.RuleModule} RuleModule
249
+ * @typedef {import('eslint').Rule.ReportFixer} ReportFixer
250
+ * @typedef {import('eslint').SourceCode} SourceCode
251
+ * @typedef {import('eslint').Scope.Variable} Variable
252
+ * @typedef {import('eslint').Scope.Scope} Scope
253
+ */
254
+
255
+ /**
256
+ * Find the variable associated with the given Identifier
257
+ *
258
+ * @param {SourceCode} sourceCode
259
+ * @param {Identifier} identifier
260
+ */
261
+ function getIdentifierVariable(sourceCode, identifier) {
262
+ const scopeManager = sourceCode.scopeManager;
263
+ for (let node = /** @type {Node | null} */identifier; node; node = /** @type {Node | null}*/node.parent) {
264
+ const variable = scopeManager.getDeclaredVariables(node).find(v => v.identifiers.includes(identifier));
265
+ if (variable) {
266
+ return variable;
267
+ }
268
+ const scope = scopeManager.acquire(node);
269
+ if (scope) {
270
+ return scope.set.get(identifier.name) || (scope.upper ? scope.upper.set.get(identifier.name) : undefined);
271
+ }
272
+ }
273
+ return undefined;
274
+ }
275
275
 
276
- /**
277
- * @param {RuleContext} context
278
- * @returns {Matchers}
279
- */
280
- function compileMatchers(context) {
281
- const rval = /** @type {Matchers} */{};
282
- for (const k_ in BaseMatchers) {
283
- const k = /** @type {keyof Matchers} */k_;
284
- rval[k] = buildMatcher(BaseMatchers[k], parseMatcherOption(context, k));
285
- }
286
- return rval;
287
- }
276
+ /**
277
+ * @typedef {import('../util/buildMatcher.js').ToMatcher} ToMatcher
278
+ * @typedef {import('../util/buildMatcher.js').IdentifierMatcher} IdentifierMatcher
279
+ */
280
+
281
+ /**
282
+ * @template T
283
+ * @typedef {Object} BaseMatchers<T>
284
+ * @property {T} isDollarFunction Catch all identifiers that begin with '$' or 'INTERNAL_$' followed by a lowercase Latin character or underscore
285
+ * @property {T} isIgnoredFunction These functions may call any $functions even though they do not have the isDollarFunction naming convention
286
+ * @property {T} isLexicalProvider Certain calls through the editor or editorState allow for implicit access to call $functions: read, registerCommand, registerNodeTransform, update.
287
+ * @property {T} isSafeDollarFunction It's usually safe to call $isNode functions, so any '$is' or 'INTERNAL_$is' function may be called in any context.
288
+ */
289
+
290
+ /** @type {BaseMatchers<Exclude<ToMatcher, undefined>[]>} */
291
+ const BaseMatchers = {
292
+ isDollarFunction: [/^\$[a-z_]/],
293
+ isIgnoredFunction: [],
294
+ isLexicalProvider: ['parseEditorState', 'read', 'registerCommand', 'registerNodeTransform', 'update'],
295
+ isSafeDollarFunction: [/^\$is[A-Z_]/]
296
+ };
297
+
298
+ /**
299
+ * @typedef {Partial<BaseMatchers<ToMatcher | ToMatcher[]>>} RulesOfLexicalOptions
300
+ * @typedef {BaseMatchers<IdentifierMatcher>} Matchers
301
+ */
302
+
303
+ /**
304
+ * @param {RuleContext} context
305
+ * @returns {Matchers}
306
+ */
307
+ function compileMatchers(context) {
308
+ const rval = /** @type {Matchers} */{};
309
+ for (const k_ in BaseMatchers) {
310
+ const k = /** @type {keyof Matchers} */k_;
311
+ rval[k] = buildMatcher(BaseMatchers[k], parseMatcherOption(context, k));
312
+ }
313
+ return rval;
314
+ }
288
315
 
289
- /**
290
- * Hook functions start with use followed by a capital latin letter.
291
- *
292
- * @param {Node | undefined} node
293
- */
294
- function isHookFunctionIdentifier(node) {
295
- return node && node.type === 'Identifier' && /^use([A-Z]|$)/.test(node.name);
296
- }
316
+ /**
317
+ * Hook functions start with use followed by a capital latin letter.
318
+ *
319
+ * @param {Node | undefined} node
320
+ */
321
+ function isHookFunctionIdentifier(node) {
322
+ return node && node.type === 'Identifier' && /^use([A-Z]|$)/.test(node.name);
323
+ }
297
324
 
298
- /**
299
- * Return this node if is an Identifier, otherwise if it is a MemberExpression such as
300
- * `editor.read` return the Identifier of its property ('read' in this case).
301
- *
302
- * @param {Node | undefined} node
303
- * @returns {Identifier | undefined}
304
- */
305
- function getFunctionNameIdentifier(node) {
306
- if (!node) {
307
- return;
308
- } else if (node.type === 'Identifier') {
309
- return node;
310
- } else if (node.type === 'MemberExpression' && !node.computed) {
311
- return getFunctionNameIdentifier(/** @type {Node} */node.property);
312
- }
313
- }
325
+ /**
326
+ * Return this node if is an Identifier, otherwise if it is a MemberExpression such as
327
+ * `editor.read` return the Identifier of its property ('read' in this case).
328
+ *
329
+ * @param {Node | undefined} node
330
+ * @returns {Identifier | undefined}
331
+ */
332
+ function getFunctionNameIdentifier(node) {
333
+ if (!node) {
334
+ return;
335
+ } else if (node.type === 'Identifier') {
336
+ return node;
337
+ } else if (node.type === 'MemberExpression' && !node.computed) {
338
+ return getFunctionNameIdentifier(/** @type {Node} */node.property);
339
+ }
340
+ }
314
341
 
315
- /**
316
- * Get the function's name, or if it is defined with a hook
317
- * (e.g. useMemo, useCallback), then get the name of the variable the result
318
- * is assigned to.
319
- *
320
- * @param {Node} node
321
- */
322
- function getLexicalFunctionName(node) {
323
- const name = getFunctionName(node);
324
- if (name) {
325
- return name;
326
- }
327
- const nodeParent = node.parent;
328
- if (nodeParent.type === 'CallExpression' && nodeParent.arguments[0] === node) {
329
- const parentName = getFunctionNameIdentifier(/** @type {Node} */nodeParent.callee);
330
- if (isHookFunctionIdentifier(parentName)) {
331
- return getParentAssignmentName(nodeParent);
332
- }
333
- }
334
- }
342
+ /**
343
+ * Get the function's name, or if it is defined with a hook
344
+ * (e.g. useMemo, useCallback), then get the name of the variable the result
345
+ * is assigned to.
346
+ *
347
+ * @param {Node} node
348
+ */
349
+ function getLexicalFunctionName(node) {
350
+ const name = getFunctionName(node);
351
+ if (name) {
352
+ return name;
353
+ }
354
+ const nodeParent = node.parent;
355
+ if (nodeParent.type === 'CallExpression' && nodeParent.arguments[0] === node) {
356
+ const parentName = getFunctionNameIdentifier(/** @type {Node} */nodeParent.callee);
357
+ if (isHookFunctionIdentifier(parentName)) {
358
+ return getParentAssignmentName(nodeParent);
359
+ }
360
+ }
361
+ }
335
362
 
336
- /**
337
- * Return a name suitable for a suggestion.
338
- * isDollarFunction(getFirstSuggestion(name)) should
339
- * be true for any given name.
340
- *
341
- * @param {string} name
342
- * @returns {string}
343
- */
344
- function getFirstSuggestion(name) {
345
- if (/^[a-z]/.test(name)) {
346
- return '$' + name;
347
- } else if (/^[A-Z][a-z]/.test(name)) {
348
- return '$' + name.slice(0, 1).toLowerCase() + name.slice(1);
349
- } else {
350
- return `$_${name}`;
351
- }
352
- }
363
+ /**
364
+ * Return a name suitable for a suggestion.
365
+ * isDollarFunction(getFirstSuggestion(name)) should
366
+ * be true for any given name.
367
+ *
368
+ * @param {string} name
369
+ * @returns {string}
370
+ */
371
+ function getFirstSuggestion(name) {
372
+ if (/^[a-z]/.test(name)) {
373
+ return '$' + name;
374
+ } else if (/^[A-Z][a-z]/.test(name)) {
375
+ return '$' + name.slice(0, 1).toLowerCase() + name.slice(1);
376
+ } else {
377
+ return `$_${name}`;
378
+ }
379
+ }
353
380
 
354
- /**
355
- * Get the suggested name for a variable and add an underscore if it shadows
356
- * or conflicts with an existing variable.
357
- *
358
- * @param {Identifier} nameIdentifier
359
- * @param {Variable | undefined} variable
360
- */
361
- function getSuggestName(nameIdentifier, variable) {
362
- const suggestName = getFirstSuggestion(nameIdentifier.name);
363
- // Add an underscore if this would shadow an existing name
364
- if (variable) {
365
- for (let scope = /** @type {Scope | null} */variable.scope; scope; scope = scope.upper) {
366
- if (scope.set.has(suggestName)) {
367
- return suggestName + '_';
368
- }
369
- }
370
- }
371
- return suggestName;
372
- }
381
+ /**
382
+ * Get the suggested name for a variable and add an underscore if it shadows
383
+ * or conflicts with an existing variable.
384
+ *
385
+ * @param {Identifier} nameIdentifier
386
+ * @param {Variable | undefined} variable
387
+ */
388
+ function getSuggestName(nameIdentifier, variable) {
389
+ const suggestName = getFirstSuggestion(nameIdentifier.name);
390
+ // Add an underscore if this would shadow an existing name
391
+ if (variable) {
392
+ for (let scope = /** @type {Scope | null} */variable.scope; scope; scope = scope.upper) {
393
+ if (scope.set.has(suggestName)) {
394
+ return suggestName + '_';
395
+ }
396
+ }
397
+ }
398
+ return suggestName;
399
+ }
373
400
 
374
- /**
375
- * Get the export declaration for a variable, if it has one.
376
- *
377
- * @param {Variable | undefined} variable
378
- */
379
- function getExportDeclaration(variable) {
380
- if (variable && variable.defs.length === 1) {
381
- const [{
382
- node
383
- }] = variable.defs;
384
- if (node.parent.type === 'ExportNamedDeclaration') {
385
- // export function foo();
386
- return node.parent;
387
- } else if (node.parent.type === 'VariableDeclaration' && node.parent.parent.type === 'ExportNamedDeclaration') {
388
- // export const foo = () => {};
389
- return node.parent.parent;
390
- }
391
- }
392
- }
401
+ /**
402
+ * Get the export declaration for a variable, if it has one.
403
+ *
404
+ * @param {Variable | undefined} variable
405
+ */
406
+ function getExportDeclaration(variable) {
407
+ if (variable && variable.defs.length === 1) {
408
+ const [{
409
+ node
410
+ }] = variable.defs;
411
+ if (node.parent.type === 'ExportNamedDeclaration') {
412
+ // export function foo();
413
+ return node.parent;
414
+ } else if (node.parent.type === 'VariableDeclaration' && node.parent.parent.type === 'ExportNamedDeclaration') {
415
+ // export const foo = () => {};
416
+ return node.parent.parent;
417
+ }
418
+ }
419
+ }
393
420
 
394
- /**
395
- * The comment we insert when an export is renamed.
396
- *
397
- * @param {Record<'caller'|'suggestName', string>} data
398
- */
399
- function renameExportText({
400
- caller,
401
- suggestName
402
- }) {
403
- return `\n/** @deprecated renamed to {@link ${suggestName}} by @lexical/eslint-plugin rules-of-lexical */\nexport const ${caller} = ${suggestName};`;
404
- }
421
+ /**
422
+ * The comment we insert when an export is renamed.
423
+ *
424
+ * @param {Record<'caller'|'suggestName', string>} data
425
+ */
426
+ function renameExportText({
427
+ caller,
428
+ suggestName
429
+ }) {
430
+ return `\n/** @deprecated renamed to {@link ${suggestName}} by @lexical/eslint-plugin rules-of-lexical */\nexport const ${caller} = ${suggestName};`;
431
+ }
405
432
 
406
- /**
407
- * @param {RuleContext} context
408
- * @param {string} optionName
409
- * @returns {ToMatcher}
410
- */
411
- function parseMatcherOption(context, optionName) {
412
- const options = Array.isArray(context.options) ? context.options[0] : undefined;
413
- return options && optionName in options ? options[optionName] : undefined;
414
- }
433
+ /**
434
+ * @param {RuleContext} context
435
+ * @param {string} optionName
436
+ * @returns {ToMatcher}
437
+ */
438
+ function parseMatcherOption(context, optionName) {
439
+ const options = Array.isArray(context.options) ? context.options[0] : undefined;
440
+ return options && optionName in options ? options[optionName] : undefined;
441
+ }
415
442
 
416
- /** @param {RuleContext} context */
417
- function getSourceCode(context) {
418
- // ESLint 9+ provides sourceCode directly on context (required in ESLint 10+)
419
- // ESLint 7-8 requires calling getSourceCode() method
420
- // This maintains compatibility across ESLint 7, 8, 9, and 10+
421
- return context.sourceCode || context.getSourceCode();
443
+ /** @param {RuleContext} context */
444
+ function getSourceCode(context) {
445
+ // ESLint 9+ provides sourceCode directly on context (required in ESLint 10+)
446
+ // ESLint 7-8 requires calling getSourceCode() method
447
+ // This maintains compatibility across ESLint 7, 8, 9, and 10+
448
+ return context.sourceCode || context.getSourceCode();
449
+ }
450
+ const matcherSchema = {
451
+ oneOf: [{
452
+ type: 'string'
453
+ }, {
454
+ contains: {
455
+ type: 'string'
456
+ },
457
+ type: 'array'
458
+ }]
459
+ };
460
+
461
+ /** @type {RuleModule} */
462
+ rulesOfLexical.rulesOfLexical = {
463
+ create(context) {
464
+ const sourceCode = getSourceCode(context);
465
+ const matchers = compileMatchers(context);
466
+
467
+ /**
468
+ * When this set is non-empty it means that we are visiting a node
469
+ * that should not be analyzed.
470
+ *
471
+ * @type {Set<Node>}
472
+ */
473
+ const ignoreSet = new Set();
474
+ /**
475
+ * The set of Identifier nodes that have been reported, we do not
476
+ * want to report the same node more than once (a function making several
477
+ * calls to $functions only needs to be renamed once!)
478
+ *
479
+ * @type {Set<Identifier>}
480
+ */
481
+ const reportedSet = new Set();
482
+ /**
483
+ * The current stack of functions
484
+ *
485
+ * @type {{ name?: Identifier, node: Node }[]} funStack
486
+ */
487
+ const funStack = [];
488
+ const shouldIgnore = () => {
489
+ if (ignoreSet.size > 0) {
490
+ return true;
491
+ }
492
+ // Ignore property assignments
493
+ const lastFunction = funStack[funStack.length - 1];
494
+ return lastFunction && lastFunction.node.parent.type === 'Property';
495
+ };
496
+ const pushIgnoredNode = (/** @type {Node} */node) => ignoreSet.add(node);
497
+ const popIgnoredNode = (/** @type {Node} */node) => ignoreSet.delete(node);
498
+ const pushFunction = (/** @type {Node} */node) => {
499
+ const name = getFunctionNameIdentifier(/** @type {Node | undefined} */getLexicalFunctionName(node));
500
+ funStack.push({
501
+ name,
502
+ node
503
+ });
504
+ if (matchers.isDollarFunction(name) || matchers.isIgnoredFunction(name) || matchers.isLexicalProvider(name)) {
505
+ pushIgnoredNode(node);
506
+ }
507
+ };
508
+ const popFunction = (/** @type {Node} */node) => {
509
+ funStack.pop();
510
+ popIgnoredNode(node);
511
+ };
512
+ const getParentLexicalFunctionNameIdentifier = (/** @type {Node} */_node) => {
513
+ const pair = funStack[funStack.length - 1];
514
+ return pair ? pair.name : undefined;
515
+ };
516
+ // Find all $function calls that are not inside a class or inside a $function
517
+ // by visiting all function definitions and calls
518
+ return {
519
+ ArrowFunctionExpression: pushFunction,
520
+ 'ArrowFunctionExpression:exit': popFunction,
521
+ CallExpression: node => {
522
+ if (shouldIgnore()) {
523
+ return;
524
+ }
525
+ const calleeName = getFunctionNameIdentifier(/** @type {Node} */node.callee);
526
+ if (matchers.isLexicalProvider(calleeName) || matchers.isSafeDollarFunction(calleeName)) {
527
+ pushIgnoredNode(node);
528
+ return;
529
+ }
530
+ if (!matchers.isDollarFunction(calleeName)) {
531
+ return;
532
+ }
533
+ const nameIdentifier = getParentLexicalFunctionNameIdentifier();
534
+ if (!nameIdentifier || reportedSet.has(nameIdentifier)) {
535
+ return;
536
+ }
537
+ reportedSet.add(nameIdentifier);
538
+ const variable = getIdentifierVariable(sourceCode, nameIdentifier);
539
+ const suggestName = getSuggestName(nameIdentifier, variable);
540
+ const exportDeclaration = getExportDeclaration(variable);
541
+ const data = {
542
+ callee: sourceCode.getText(node.callee),
543
+ caller: sourceCode.getText(nameIdentifier),
544
+ suggestName
545
+ };
546
+ /** @type {ReportFixer} */
547
+ const fix = fixer => {
548
+ /** @type {Set<Identifier>} */
549
+ const replaced = new Set();
550
+ /** @type {Fix[]} */
551
+ const fixes = [];
552
+ const renameIdentifier = (/** @type {Identifier} */identifier) => {
553
+ if (!replaced.has(identifier)) {
554
+ replaced.add(identifier);
555
+ fixes.push(fixer.replaceText(identifier, suggestName));
556
+ }
557
+ };
558
+ renameIdentifier(nameIdentifier);
559
+ if (exportDeclaration) {
560
+ fixes.push(fixer.insertTextAfter(exportDeclaration, renameExportText(data)));
561
+ }
562
+ if (variable) {
563
+ for (const ref of variable.references) {
564
+ renameIdentifier(/** @type {Identifier} */ref.identifier);
565
+ }
566
+ }
567
+ return fixes;
568
+ };
569
+ context.report({
570
+ data,
571
+ fix,
572
+ messageId: 'rulesOfLexicalReport',
573
+ node: nameIdentifier,
574
+ suggest: [{
575
+ data,
576
+ fix,
577
+ messageId: 'rulesOfLexicalSuggestion'
578
+ }]
579
+ });
580
+ },
581
+ 'CallExpression:exit': popIgnoredNode,
582
+ ClassBody: pushIgnoredNode,
583
+ 'ClassBody:exit': popIgnoredNode,
584
+ FunctionDeclaration: pushFunction,
585
+ 'FunctionDeclaration:exit': popFunction,
586
+ FunctionExpression: pushFunction,
587
+ 'FunctionExpression:exit': popFunction
588
+ };
589
+ },
590
+ meta: {
591
+ docs: {
592
+ description: 'enforces the Rules of Lexical',
593
+ recommended: true,
594
+ url: 'https://lexical.dev/docs/packages/lexical-eslint-plugin'
595
+ },
596
+ fixable: 'code',
597
+ hasSuggestions: true,
598
+ messages: {
599
+ rulesOfLexicalReport: '{{ callee }} called from {{ caller }}, without $ prefix or read/update context',
600
+ rulesOfLexicalSuggestion: 'Rename {{ caller }} to {{ suggestName }}'
601
+ },
602
+ schema: [{
603
+ additionalProperties: false,
604
+ properties: {
605
+ isDollarFunction: matcherSchema,
606
+ isIgnoredFunction: matcherSchema,
607
+ isLexicalProvider: matcherSchema,
608
+ isSafeDollarFunction: matcherSchema
609
+ },
610
+ type: 'object'
611
+ }],
612
+ type: 'suggestion'
613
+ }
614
+ };
615
+ return rulesOfLexical;
422
616
  }
423
- const matcherSchema = {
424
- oneOf: [{
425
- type: 'string'
426
- }, {
427
- contains: {
428
- type: 'string'
429
- },
430
- type: 'array'
431
- }]
432
- };
433
-
434
- /** @type {RuleModule} */
435
- rulesOfLexical$1.rulesOfLexical = {
436
- create(context) {
437
- const sourceCode = getSourceCode(context);
438
- const matchers = compileMatchers(context);
439
-
440
- /**
441
- * When this set is non-empty it means that we are visiting a node
442
- * that should not be analyzed.
443
- *
444
- * @type {Set<Node>}
445
- */
446
- const ignoreSet = new Set();
447
- /**
448
- * The set of Identifier nodes that have been reported, we do not
449
- * want to report the same node more than once (a function making several
450
- * calls to $functions only needs to be renamed once!)
451
- *
452
- * @type {Set<Identifier>}
453
- */
454
- const reportedSet = new Set();
455
- /**
456
- * The current stack of functions
457
- *
458
- * @type {{ name?: Identifier, node: Node }[]} funStack
459
- */
460
- const funStack = [];
461
- const shouldIgnore = () => {
462
- if (ignoreSet.size > 0) {
463
- return true;
464
- }
465
- // Ignore property assignments
466
- const lastFunction = funStack[funStack.length - 1];
467
- return lastFunction && lastFunction.node.parent.type === 'Property';
468
- };
469
- const pushIgnoredNode = (/** @type {Node} */node) => ignoreSet.add(node);
470
- const popIgnoredNode = (/** @type {Node} */node) => ignoreSet.delete(node);
471
- const pushFunction = (/** @type {Node} */node) => {
472
- const name = getFunctionNameIdentifier(/** @type {Node | undefined} */getLexicalFunctionName(node));
473
- funStack.push({
474
- name,
475
- node
476
- });
477
- if (matchers.isDollarFunction(name) || matchers.isIgnoredFunction(name) || matchers.isLexicalProvider(name)) {
478
- pushIgnoredNode(node);
479
- }
480
- };
481
- const popFunction = (/** @type {Node} */node) => {
482
- funStack.pop();
483
- popIgnoredNode(node);
484
- };
485
- const getParentLexicalFunctionNameIdentifier = (/** @type {Node} */_node) => {
486
- const pair = funStack[funStack.length - 1];
487
- return pair ? pair.name : undefined;
488
- };
489
- // Find all $function calls that are not inside a class or inside a $function
490
- // by visiting all function definitions and calls
491
- return {
492
- ArrowFunctionExpression: pushFunction,
493
- 'ArrowFunctionExpression:exit': popFunction,
494
- CallExpression: node => {
495
- if (shouldIgnore()) {
496
- return;
497
- }
498
- const calleeName = getFunctionNameIdentifier(/** @type {Node} */node.callee);
499
- if (matchers.isLexicalProvider(calleeName) || matchers.isSafeDollarFunction(calleeName)) {
500
- pushIgnoredNode(node);
501
- return;
502
- }
503
- if (!matchers.isDollarFunction(calleeName)) {
504
- return;
505
- }
506
- const nameIdentifier = getParentLexicalFunctionNameIdentifier();
507
- if (!nameIdentifier || reportedSet.has(nameIdentifier)) {
508
- return;
509
- }
510
- reportedSet.add(nameIdentifier);
511
- const variable = getIdentifierVariable(sourceCode, nameIdentifier);
512
- const suggestName = getSuggestName(nameIdentifier, variable);
513
- const exportDeclaration = getExportDeclaration(variable);
514
- const data = {
515
- callee: sourceCode.getText(node.callee),
516
- caller: sourceCode.getText(nameIdentifier),
517
- suggestName
518
- };
519
- /** @type {ReportFixer} */
520
- const fix = fixer => {
521
- /** @type {Set<Identifier>} */
522
- const replaced = new Set();
523
- /** @type {Fix[]} */
524
- const fixes = [];
525
- const renameIdentifier = (/** @type {Identifier} */identifier) => {
526
- if (!replaced.has(identifier)) {
527
- replaced.add(identifier);
528
- fixes.push(fixer.replaceText(identifier, suggestName));
529
- }
530
- };
531
- renameIdentifier(nameIdentifier);
532
- if (exportDeclaration) {
533
- fixes.push(fixer.insertTextAfter(exportDeclaration, renameExportText(data)));
534
- }
535
- if (variable) {
536
- for (const ref of variable.references) {
537
- renameIdentifier(/** @type {Identifier} */ref.identifier);
538
- }
539
- }
540
- return fixes;
541
- };
542
- context.report({
543
- data,
544
- fix,
545
- messageId: 'rulesOfLexicalReport',
546
- node: nameIdentifier,
547
- suggest: [{
548
- data,
549
- fix,
550
- messageId: 'rulesOfLexicalSuggestion'
551
- }]
552
- });
553
- },
554
- 'CallExpression:exit': popIgnoredNode,
555
- ClassBody: pushIgnoredNode,
556
- 'ClassBody:exit': popIgnoredNode,
557
- FunctionDeclaration: pushFunction,
558
- 'FunctionDeclaration:exit': popFunction,
559
- FunctionExpression: pushFunction,
560
- 'FunctionExpression:exit': popFunction
561
- };
562
- },
563
- meta: {
564
- docs: {
565
- description: 'enforces the Rules of Lexical',
566
- recommended: true,
567
- url: 'https://lexical.dev/docs/packages/lexical-eslint-plugin'
568
- },
569
- fixable: 'code',
570
- hasSuggestions: true,
571
- messages: {
572
- rulesOfLexicalReport: '{{ callee }} called from {{ caller }}, without $ prefix or read/update context',
573
- rulesOfLexicalSuggestion: 'Rename {{ caller }} to {{ suggestName }}'
574
- },
575
- schema: [{
576
- additionalProperties: false,
577
- properties: {
578
- isDollarFunction: matcherSchema,
579
- isIgnoredFunction: matcherSchema,
580
- isLexicalProvider: matcherSchema,
581
- isSafeDollarFunction: matcherSchema
582
- },
583
- type: 'object'
584
- }],
585
- type: 'suggestion'
586
- }
587
- };
588
617
 
589
618
  /**
590
619
  * Copyright (c) Meta Platforms, Inc. and affiliates.
@@ -594,63 +623,72 @@ rulesOfLexical$1.rulesOfLexical = {
594
623
  *
595
624
  */
596
625
 
597
- // @ts-check
598
-
599
- const {
600
- name,
601
- version
602
- } = require$$0;
603
- const {
604
- rulesOfLexical
605
- } = rulesOfLexical$1;
606
-
607
- // Legacy config format (ESLint 7-8)
608
- const legacyAll = {
609
- plugins: ['@lexical'],
610
- rules: {
611
- '@lexical/rules-of-lexical': (/** @type {'warn'|'error'|'off'}*/'warn')
612
- }
613
- };
614
- const plugin$1 = {
615
- configs: {
616
- // Legacy configs (ESLint 7-8) - available under multiple names for compatibility
617
- all: legacyAll,
618
- // Flat configs (ESLint 9-10+) - placeholders, will be set below
619
- 'flat/all': (/** @type {any} */null),
620
- 'flat/recommended': (/** @type {any} */null),
621
- 'legacy-all': legacyAll,
622
- 'legacy-recommended': legacyAll,
623
- recommended: legacyAll
624
- },
625
- meta: {
626
- name,
627
- version
628
- },
629
- rules: {
630
- 'rules-of-lexical': rulesOfLexical
631
- }
632
- };
626
+ var LexicalEslintPlugin$1;
627
+ var hasRequiredLexicalEslintPlugin;
628
+
629
+ function requireLexicalEslintPlugin () {
630
+ if (hasRequiredLexicalEslintPlugin) return LexicalEslintPlugin$1;
631
+ hasRequiredLexicalEslintPlugin = 1;
632
+ // @ts-check
633
+
634
+ const {
635
+ name,
636
+ version
637
+ } = require$$0;
638
+ const {
639
+ rulesOfLexical
640
+ } = /*@__PURE__*/ requireRulesOfLexical();
641
+
642
+ // Legacy config format (ESLint 7-8)
643
+ const legacyAll = {
644
+ plugins: ['@lexical'],
645
+ rules: {
646
+ '@lexical/rules-of-lexical': (/** @type {'warn'|'error'|'off'}*/'warn')
647
+ }
648
+ };
649
+ const plugin = {
650
+ configs: {
651
+ // Legacy configs (ESLint 7-8) - available under multiple names for compatibility
652
+ all: legacyAll,
653
+ // Flat configs (ESLint 9-10+) - placeholders, will be set below
654
+ 'flat/all': (/** @type {any} */null),
655
+ 'flat/recommended': (/** @type {any} */null),
656
+ 'legacy-all': legacyAll,
657
+ 'legacy-recommended': legacyAll,
658
+ recommended: legacyAll
659
+ },
660
+ meta: {
661
+ name,
662
+ version
663
+ },
664
+ rules: {
665
+ 'rules-of-lexical': rulesOfLexical
666
+ }
667
+ };
668
+
669
+ // Flat config format (ESLint 9-10+)
670
+ // Must be created after plugin is defined to avoid circular reference
671
+ const flatAll = {
672
+ plugins: {
673
+ '@lexical': plugin
674
+ },
675
+ rules: {
676
+ '@lexical/rules-of-lexical': 'warn' /** @type {'warn'|'error'|'off'}*/
677
+ }
678
+ };
679
+ plugin.configs['flat/all'] = flatAll;
680
+ plugin.configs['flat/recommended'] = flatAll;
681
+ LexicalEslintPlugin$1 = plugin;
682
+ return LexicalEslintPlugin$1;
683
+ }
633
684
 
634
- // Flat config format (ESLint 9-10+)
635
- // Must be created after plugin is defined to avoid circular reference
636
- const flatAll = {
637
- plugins: {
638
- '@lexical': plugin$1
639
- },
640
- rules: {
641
- '@lexical/rules-of-lexical': 'warn' /** @type {'warn'|'error'|'off'}*/
642
- }
643
- };
644
- plugin$1.configs['flat/all'] = flatAll;
645
- plugin$1.configs['flat/recommended'] = flatAll;
646
- var LexicalEslintPlugin = plugin$1;
647
-
648
- var LexicalEslintPlugin_default = /*@__PURE__*/getDefaultExportFromCjs(LexicalEslintPlugin);
685
+ var LexicalEslintPluginExports = /*@__PURE__*/ requireLexicalEslintPlugin();
686
+ var LexicalEslintPlugin = /*@__PURE__*/getDefaultExportFromCjs(LexicalEslintPluginExports);
649
687
 
650
688
  var jsPlugin = /*#__PURE__*/_mergeNamespaces({
651
689
  __proto__: null,
652
- default: LexicalEslintPlugin_default
653
- }, [LexicalEslintPlugin]);
690
+ default: LexicalEslintPlugin
691
+ }, [LexicalEslintPluginExports]);
654
692
 
655
693
  /**
656
694
  * Copyright (c) Meta Platforms, Inc. and affiliates.