eslint-plugin-react-rsc 5.10.1 → 5.10.3
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/dist/index.js +108 -111
- package/package.json +8 -8
package/dist/index.js
CHANGED
|
@@ -34,7 +34,7 @@ const rules$4 = { "react-rsc/function-definition": "off" };
|
|
|
34
34
|
//#endregion
|
|
35
35
|
//#region package.json
|
|
36
36
|
var name$4 = "eslint-plugin-react-rsc";
|
|
37
|
-
var version = "5.10.
|
|
37
|
+
var version = "5.10.3";
|
|
38
38
|
|
|
39
39
|
//#endregion
|
|
40
40
|
//#region src/utils/create-rule.ts
|
|
@@ -66,148 +66,145 @@ var function_definition_default = createRule({
|
|
|
66
66
|
create,
|
|
67
67
|
defaultOptions: []
|
|
68
68
|
});
|
|
69
|
+
function isDirectiveName(value) {
|
|
70
|
+
return value === "use client" || value === "use server";
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Match a statement against directive-like expressions (`'use client'`, `"use server"`, `` `use server` ``, etc.)
|
|
74
|
+
* @param stmt The statement to match
|
|
75
|
+
* @returns The match result, or `null` if the statement is not directive-like
|
|
76
|
+
*/
|
|
77
|
+
function matchDirective(stmt) {
|
|
78
|
+
if (stmt.type !== AST_NODE_TYPES.ExpressionStatement) return null;
|
|
79
|
+
const { expression } = stmt;
|
|
80
|
+
if (Check.isStringLiteral(expression)) {
|
|
81
|
+
if (!isDirectiveName(expression.value)) return null;
|
|
82
|
+
return {
|
|
83
|
+
kind: stmt.directive != null ? "well-formed" : "misplaced",
|
|
84
|
+
name: expression.value,
|
|
85
|
+
node: stmt
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
if (expression.type === AST_NODE_TYPES.TemplateLiteral && expression.expressions.length === 0 && expression.quasis.length === 1) {
|
|
89
|
+
const value = expression.quasis[0]?.value.cooked;
|
|
90
|
+
if (!isDirectiveName(value)) return null;
|
|
91
|
+
return {
|
|
92
|
+
kind: "backtick",
|
|
93
|
+
name: value,
|
|
94
|
+
node: stmt
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
69
99
|
function create(context) {
|
|
70
|
-
const
|
|
71
|
-
|
|
72
|
-
if (!hasUseServer && !hasUseClient) return {};
|
|
100
|
+
const text = context.sourceCode.text;
|
|
101
|
+
if (!text.includes("use server") && !text.includes("use client")) return {};
|
|
73
102
|
const hasFileLevelUseServerDirective = context.sourceCode.ast.body.some(Check.isDirective("use server"));
|
|
74
103
|
/**
|
|
75
|
-
*
|
|
76
|
-
* @param node The function node to
|
|
77
|
-
* @returns
|
|
104
|
+
* Build a fix that makes `node` an async function
|
|
105
|
+
* @param node The function node to fix
|
|
106
|
+
* @returns The fix function, or `null` if no fix is available
|
|
78
107
|
*/
|
|
79
108
|
function getAsyncFix(node) {
|
|
80
|
-
if (node.type === AST_NODE_TYPES.FunctionDeclaration || node.type === AST_NODE_TYPES.FunctionExpression) {
|
|
81
|
-
const fnToken = context.sourceCode.getFirstToken(node);
|
|
82
|
-
if (fnToken != null) return (fixer) => fixer.insertTextBefore(fnToken, "async ");
|
|
83
|
-
return null;
|
|
84
|
-
}
|
|
85
109
|
if (node.type === AST_NODE_TYPES.ArrowFunctionExpression) return (fixer) => fixer.insertTextBefore(node, "async ");
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
if (node == null) return false;
|
|
90
|
-
const unwrapped = Extract.unwrap(node);
|
|
91
|
-
if (!Check.isFunction(unwrapped)) return false;
|
|
92
|
-
if (!unwrapped.async) {
|
|
93
|
-
context.report({
|
|
94
|
-
fix: getAsyncFix(unwrapped),
|
|
95
|
-
messageId,
|
|
96
|
-
node: unwrapped
|
|
97
|
-
});
|
|
98
|
-
return true;
|
|
99
|
-
}
|
|
100
|
-
return false;
|
|
101
|
-
}
|
|
102
|
-
/**
|
|
103
|
-
* Check non-exported local functions for 'use server' directives, and report if they are not async
|
|
104
|
-
* @param node The function node to check
|
|
105
|
-
*/
|
|
106
|
-
function checkLocalServerFunction(node) {
|
|
107
|
-
if (core.getFunctionDirectives(node).some((d) => d.directive === "use server")) reportNonAsyncFunction(node, "local");
|
|
110
|
+
const functionToken = context.sourceCode.getFirstToken(node);
|
|
111
|
+
if (functionToken == null) return null;
|
|
112
|
+
return (fixer) => fixer.insertTextBefore(functionToken, "async ");
|
|
108
113
|
}
|
|
109
114
|
/**
|
|
110
|
-
*
|
|
111
|
-
* @param
|
|
115
|
+
* Report `node` if it resolves to a non-async function
|
|
116
|
+
* @param node The node to check, may be `null` for convenience at call sites
|
|
117
|
+
* @param messageId The message to report with
|
|
112
118
|
*/
|
|
113
|
-
function
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
+
function reportNonAsyncFunction(node, messageId) {
|
|
120
|
+
if (node == null) return;
|
|
121
|
+
const fn = Extract.unwrap(node);
|
|
122
|
+
if (!Check.isFunction(fn) || fn.async) return;
|
|
123
|
+
context.report({
|
|
124
|
+
fix: getAsyncFix(fn),
|
|
125
|
+
messageId,
|
|
126
|
+
node: fn
|
|
127
|
+
});
|
|
119
128
|
}
|
|
120
129
|
/**
|
|
121
130
|
* Check file-level directives for correct position and quote style.
|
|
122
|
-
*
|
|
123
|
-
* If they appear after other code, the parser will not set `directive`.
|
|
131
|
+
* @param program The Program node
|
|
124
132
|
*/
|
|
125
|
-
function
|
|
126
|
-
for (const
|
|
127
|
-
|
|
128
|
-
if (
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
});
|
|
135
|
-
continue;
|
|
136
|
-
}
|
|
137
|
-
if (node.expression.type === AST_NODE_TYPES.TemplateLiteral && node.expression.quasis.length === 1 && node.expression.expressions.length === 0) {
|
|
138
|
-
const value = node.expression.quasis[0]?.value.cooked;
|
|
139
|
-
if (value === "use server" || value === "use client") context.report({
|
|
140
|
-
data: { name: value },
|
|
141
|
-
messageId: "fileDirectiveQuote",
|
|
142
|
-
node
|
|
143
|
-
});
|
|
144
|
-
}
|
|
133
|
+
function checkFileDirectives(program) {
|
|
134
|
+
for (const stmt of program.body) {
|
|
135
|
+
const match = matchDirective(stmt);
|
|
136
|
+
if (match == null || match.kind === "well-formed") continue;
|
|
137
|
+
context.report({
|
|
138
|
+
data: { name: match.name },
|
|
139
|
+
messageId: match.kind === "backtick" ? "fileDirectiveQuote" : "fileDirectivePosition",
|
|
140
|
+
node: match.node
|
|
141
|
+
});
|
|
145
142
|
}
|
|
146
143
|
}
|
|
147
144
|
/**
|
|
148
|
-
* Check function-level directives for correct position and quote style
|
|
145
|
+
* Check function-level directives for correct position and quote style,
|
|
146
|
+
* and report if a `use server` function is not async.
|
|
149
147
|
* @param node The function node to check
|
|
150
148
|
*/
|
|
151
|
-
function
|
|
149
|
+
function checkFunction(node) {
|
|
152
150
|
if (node.body.type !== AST_NODE_TYPES.BlockStatement) return;
|
|
153
151
|
for (const stmt of node.body.body) {
|
|
154
|
-
|
|
155
|
-
if (
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
152
|
+
const match = matchDirective(stmt);
|
|
153
|
+
if (match == null) continue;
|
|
154
|
+
switch (true) {
|
|
155
|
+
case match.kind === "backtick":
|
|
156
|
+
context.report({
|
|
157
|
+
data: { name: match.name },
|
|
158
|
+
messageId: "localDirectiveQuote",
|
|
159
|
+
node: match.node
|
|
160
|
+
});
|
|
161
|
+
break;
|
|
162
|
+
case match.name === "use client":
|
|
163
|
+
context.report({
|
|
164
|
+
data: { name: match.name },
|
|
165
|
+
messageId: "localDirectiveUnexpected",
|
|
166
|
+
node: match.node
|
|
167
|
+
});
|
|
168
|
+
break;
|
|
169
|
+
case match.kind === "misplaced":
|
|
170
|
+
context.report({
|
|
171
|
+
data: { name: match.name },
|
|
172
|
+
messageId: "localDirectivePosition",
|
|
173
|
+
node: match.node
|
|
174
|
+
});
|
|
175
|
+
break;
|
|
176
176
|
}
|
|
177
177
|
}
|
|
178
|
+
if (core.isFunctionHasDirective(node, "use server")) reportNonAsyncFunction(node, "local");
|
|
178
179
|
}
|
|
179
180
|
return {
|
|
180
|
-
ArrowFunctionExpression
|
|
181
|
-
checkFunctionDirectives(node);
|
|
182
|
-
checkLocalServerFunction(node);
|
|
183
|
-
},
|
|
181
|
+
ArrowFunctionExpression: checkFunction,
|
|
184
182
|
ExportDefaultDeclaration(node) {
|
|
185
183
|
if (!hasFileLevelUseServerDirective) return;
|
|
186
|
-
const decl = node.declaration;
|
|
187
|
-
if (
|
|
188
|
-
|
|
184
|
+
const decl = Extract.unwrap(node.declaration);
|
|
185
|
+
if (decl.type === AST_NODE_TYPES.Identifier) {
|
|
186
|
+
reportNonAsyncFunction(resolve(context, decl), "file");
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
reportNonAsyncFunction(decl, "file");
|
|
189
190
|
},
|
|
190
191
|
ExportNamedDeclaration(node) {
|
|
191
192
|
if (!hasFileLevelUseServerDirective) return;
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
if (
|
|
195
|
-
|
|
193
|
+
const decl = node.declaration;
|
|
194
|
+
if (decl != null) {
|
|
195
|
+
if (decl.type === AST_NODE_TYPES.VariableDeclaration) {
|
|
196
|
+
for (const declarator of decl.declarations) reportNonAsyncFunction(declarator.init, "file");
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
reportNonAsyncFunction(decl, "file");
|
|
196
200
|
return;
|
|
197
201
|
}
|
|
198
|
-
if (node.source
|
|
202
|
+
if (node.source != null) return;
|
|
203
|
+
for (const spec of node.specifiers) reportNonAsyncFunction(resolve(context, spec.local), "file");
|
|
199
204
|
},
|
|
200
|
-
FunctionDeclaration
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
},
|
|
204
|
-
FunctionExpression(node) {
|
|
205
|
-
checkFunctionDirectives(node);
|
|
206
|
-
checkLocalServerFunction(node);
|
|
207
|
-
},
|
|
208
|
-
Program() {
|
|
209
|
-
checkFileLevelDirectives();
|
|
210
|
-
}
|
|
205
|
+
FunctionDeclaration: checkFunction,
|
|
206
|
+
FunctionExpression: checkFunction,
|
|
207
|
+
Program: checkFileDirectives
|
|
211
208
|
};
|
|
212
209
|
}
|
|
213
210
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eslint-plugin-react-rsc",
|
|
3
|
-
"version": "5.10.
|
|
3
|
+
"version": "5.10.3",
|
|
4
4
|
"description": "ESLint React's ESLint plugin for RSC related rules.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react",
|
|
@@ -38,11 +38,11 @@
|
|
|
38
38
|
"dependencies": {
|
|
39
39
|
"@typescript-eslint/types": "^8.62.1",
|
|
40
40
|
"@typescript-eslint/utils": "^8.62.1",
|
|
41
|
-
"@eslint-react/ast": "5.10.
|
|
42
|
-
"@eslint-react/
|
|
43
|
-
"@eslint-react/
|
|
44
|
-
"@eslint-react/
|
|
45
|
-
"@eslint-react/
|
|
41
|
+
"@eslint-react/ast": "5.10.3",
|
|
42
|
+
"@eslint-react/core": "5.10.3",
|
|
43
|
+
"@eslint-react/shared": "5.10.3",
|
|
44
|
+
"@eslint-react/eslint": "5.10.3",
|
|
45
|
+
"@eslint-react/var": "5.10.3"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
48
|
"@types/react": "^19.2.17",
|
|
@@ -51,8 +51,8 @@
|
|
|
51
51
|
"eslint": "^10.6.0",
|
|
52
52
|
"tsdown": "^0.22.3",
|
|
53
53
|
"typescript": "6.0.3",
|
|
54
|
-
"@local/
|
|
55
|
-
"@local/
|
|
54
|
+
"@local/eff": "0.0.0",
|
|
55
|
+
"@local/configs": "0.0.0"
|
|
56
56
|
},
|
|
57
57
|
"peerDependencies": {
|
|
58
58
|
"eslint": "*",
|