@colrealpro/react-luau-doctor 0.20.1 → 0.20.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/CHANGELOG.md +18 -0
- package/THIRD_PARTY_NOTICES.md +2 -2
- package/dist/cli.js +1224 -426
- package/dist/cli.js.map +13 -11
- package/dist/config.schema.json +1 -1
- package/dist/scan-worker.js +20 -9
- package/dist/scan-worker.js.map +4 -4
- package/package.json +66 -66
package/dist/config.schema.json
CHANGED
|
@@ -329,7 +329,7 @@
|
|
|
329
329
|
"suggestion"
|
|
330
330
|
],
|
|
331
331
|
"default": "warning",
|
|
332
|
-
"description": "Do not read binding snapshots during render except
|
|
332
|
+
"description": "Do not read binding snapshots during render except in hook initialization arguments"
|
|
333
333
|
},
|
|
334
334
|
"react-luau/usememo-must-return": {
|
|
335
335
|
"type": "string",
|
package/dist/scan-worker.js
CHANGED
|
@@ -1168,7 +1168,13 @@ function isLazyRefInitialization(context, call) {
|
|
|
1168
1168
|
}
|
|
1169
1169
|
return false;
|
|
1170
1170
|
}
|
|
1171
|
-
|
|
1171
|
+
var hookInitializerArguments = new Map([
|
|
1172
|
+
["React.useRef", 0],
|
|
1173
|
+
["React.useState", 0],
|
|
1174
|
+
["React.useBinding", 0],
|
|
1175
|
+
["React.useReducer", 1]
|
|
1176
|
+
]);
|
|
1177
|
+
function isHookInitializer(context, call) {
|
|
1172
1178
|
let node = call;
|
|
1173
1179
|
while (node.parent) {
|
|
1174
1180
|
const parent = node.parent;
|
|
@@ -1176,8 +1182,11 @@ function isRefInitializer(context, call) {
|
|
|
1176
1182
|
return false;
|
|
1177
1183
|
if (parent.type === "arguments") {
|
|
1178
1184
|
const hook = parent.parent;
|
|
1179
|
-
if (hook?.type === "function_call"
|
|
1180
|
-
|
|
1185
|
+
if (hook?.type === "function_call") {
|
|
1186
|
+
const argumentIndex = hookInitializerArguments.get(reactApiPath(context, hook) ?? "");
|
|
1187
|
+
if (argumentIndex !== undefined && context.callArguments(hook)[argumentIndex]?.id === node.id)
|
|
1188
|
+
return true;
|
|
1189
|
+
}
|
|
1181
1190
|
}
|
|
1182
1191
|
node = parent;
|
|
1183
1192
|
}
|
|
@@ -1211,18 +1220,18 @@ var noBindingGetValueInRender = {
|
|
|
1211
1220
|
id: "react-luau/no-binding-getvalue-in-render",
|
|
1212
1221
|
category: "Correctness",
|
|
1213
1222
|
severity: "warning",
|
|
1214
|
-
description: "Do not read binding snapshots during render except
|
|
1223
|
+
description: "Do not read binding snapshots during render except in hook initialization arguments",
|
|
1215
1224
|
guidance: {
|
|
1216
1225
|
explanation: "getValue returns a snapshot without subscribing \u2014 later binding updates do not rerender the component or update props built from that snapshot",
|
|
1217
1226
|
help: "Pass the binding directly to the prop or use binding:map(function(value) return ... end)",
|
|
1218
|
-
caveat: "
|
|
1227
|
+
caveat: "Snapshots in the first argument to React.useRef, React.useState, or React.useBinding, or the second argument to React.useReducer, are allowed because those arguments are used only for initialization"
|
|
1219
1228
|
},
|
|
1220
1229
|
run(context) {
|
|
1221
1230
|
return context.findCalls().flatMap((call) => {
|
|
1222
1231
|
const owner = context.nearestFunction(call);
|
|
1223
1232
|
const name = call.childForFieldName("name");
|
|
1224
1233
|
const receiver = name?.namedChildren[0];
|
|
1225
|
-
if (!owner || !owner.isComponent && !owner.isHook || name?.type !== "method_index_expression" || name.namedChildren.at(-1)?.text !== "getValue" || !receiver || !isBinding(context, receiver) ||
|
|
1234
|
+
if (!owner || !owner.isComponent && !owner.isHook || name?.type !== "method_index_expression" || name.namedChildren.at(-1)?.text !== "getValue" || !receiver || !isBinding(context, receiver) || isHookInitializer(context, call))
|
|
1226
1235
|
return [];
|
|
1227
1236
|
return [
|
|
1228
1237
|
{
|
|
@@ -9329,6 +9338,7 @@ async function analyzeReactFile(input, tree, selectedRules = rules) {
|
|
|
9329
9338
|
project: input.project
|
|
9330
9339
|
};
|
|
9331
9340
|
const context = createRuleContext(file);
|
|
9341
|
+
const syntaxFindings = parsedTree.rootNode.hasError ? parseErrors.run(context) : [];
|
|
9332
9342
|
const categorySet = input.categories && input.categories.length > 0 ? new Set(input.categories) : null;
|
|
9333
9343
|
const isSuppressed = input.respectInlineDisables ? createInlineSuppressionChecker(input.source) : () => false;
|
|
9334
9344
|
const diagnostics = [];
|
|
@@ -9340,7 +9350,7 @@ async function analyzeReactFile(input, tree, selectedRules = rules) {
|
|
|
9340
9350
|
const severity = effectiveSeverity(rule.severity, rule.id, input.config);
|
|
9341
9351
|
if (!severity)
|
|
9342
9352
|
continue;
|
|
9343
|
-
const findings = rule.run(context);
|
|
9353
|
+
const findings = rule.id === parseErrors.id ? syntaxFindings : rule.run(context);
|
|
9344
9354
|
for (const finding of findings) {
|
|
9345
9355
|
const configuredSeverity = input.config.rules?.[rule.id];
|
|
9346
9356
|
const diagnostic = toDiagnostic(file, rule.id, rule.category, severity, {
|
|
@@ -9361,7 +9371,8 @@ async function analyzeReactFile(input, tree, selectedRules = rules) {
|
|
|
9361
9371
|
relativePath: input.relativePath,
|
|
9362
9372
|
isReactFile: model.isReactFile,
|
|
9363
9373
|
scanned: true,
|
|
9364
|
-
diagnostics: filteredDiagnostics
|
|
9374
|
+
diagnostics: filteredDiagnostics,
|
|
9375
|
+
unverified: syntaxFindings.length > 0
|
|
9365
9376
|
};
|
|
9366
9377
|
} finally {
|
|
9367
9378
|
if (!tree)
|
|
@@ -10348,5 +10359,5 @@ parentPort.on("message", async (request) => {
|
|
|
10348
10359
|
}
|
|
10349
10360
|
});
|
|
10350
10361
|
|
|
10351
|
-
//# debugId=
|
|
10362
|
+
//# debugId=3E81A30ECFF40BE164756E2164756E21
|
|
10352
10363
|
//# sourceMappingURL=scan-worker.js.map
|