@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.
- package/LexicalEslintPlugin.dev.js +598 -560
- package/LexicalEslintPlugin.dev.mjs +598 -560
- package/LexicalEslintPlugin.prod.js +1 -1
- package/LexicalEslintPlugin.prod.mjs +1 -1
- package/package.json +1 -1
|
@@ -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
|
|
28
|
-
var version
|
|
27
|
+
var name = "@lexical/eslint-plugin";
|
|
28
|
+
var version = "0.42.0";
|
|
29
29
|
var require$$0 = {
|
|
30
|
-
name: name
|
|
31
|
-
version: version
|
|
30
|
+
name: name,
|
|
31
|
+
version: version};
|
|
32
32
|
|
|
33
|
-
var rulesOfLexical
|
|
33
|
+
var rulesOfLexical = {};
|
|
34
34
|
|
|
35
|
-
var getFunctionName
|
|
35
|
+
var getFunctionName = {};
|
|
36
36
|
|
|
37
|
-
var getParentAssignmentName
|
|
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
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
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
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
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
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
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
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
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
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
function isHookFunctionIdentifier(node) {
|
|
295
|
-
|
|
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
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
function getFunctionNameIdentifier(node) {
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
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
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
function getLexicalFunctionName(node) {
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
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
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
function getFirstSuggestion(name) {
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
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
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
function getSuggestName(nameIdentifier, variable) {
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
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
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
function getExportDeclaration(variable) {
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
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
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
function renameExportText({
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
}) {
|
|
403
|
-
|
|
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
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
function parseMatcherOption(context, optionName) {
|
|
412
|
-
|
|
413
|
-
|
|
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
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
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
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
const
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
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
|
-
|
|
635
|
-
|
|
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:
|
|
653
|
-
}, [
|
|
690
|
+
default: LexicalEslintPlugin
|
|
691
|
+
}, [LexicalEslintPluginExports]);
|
|
654
692
|
|
|
655
693
|
/**
|
|
656
694
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|