oxlint-plugin-react-doctor 0.8.3-dev.01820bb → 0.8.3-dev.3d7ea66
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.d.ts +2963 -3875
- package/dist/index.js +212 -1442
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -13065,7 +13065,7 @@ const getOutermostTarget = (node) => {
|
|
|
13065
13065
|
}
|
|
13066
13066
|
return current;
|
|
13067
13067
|
};
|
|
13068
|
-
const getExecutionOwner
|
|
13068
|
+
const getExecutionOwner = (node) => {
|
|
13069
13069
|
let current = node;
|
|
13070
13070
|
while (current) {
|
|
13071
13071
|
if (isFunctionLike$1(current) || isNodeOfType(current, "Program")) return current;
|
|
@@ -13207,7 +13207,7 @@ const getSymbolMutationInspector = (scopes) => {
|
|
|
13207
13207
|
const eventsBySymbolId = /* @__PURE__ */ new Map();
|
|
13208
13208
|
walkAst(scopes.rootScope.node, (node) => {
|
|
13209
13209
|
if (isNodeOfType(node, "CallExpression")) {
|
|
13210
|
-
const owner = getExecutionOwner
|
|
13210
|
+
const owner = getExecutionOwner(node);
|
|
13211
13211
|
const targetOwner = getLocalCallTarget(node);
|
|
13212
13212
|
if (targetOwner && !isStaticallyUnreachable$1(node, owner)) calls.push({
|
|
13213
13213
|
call: node,
|
|
@@ -13220,7 +13220,7 @@ const getSymbolMutationInspector = (scopes) => {
|
|
|
13220
13220
|
if (propertyNames === void 0) return;
|
|
13221
13221
|
const symbol = resolveConstIdentifierAlias(node, scopes);
|
|
13222
13222
|
if (!symbol) return;
|
|
13223
|
-
const owner = getExecutionOwner
|
|
13223
|
+
const owner = getExecutionOwner(node);
|
|
13224
13224
|
if (isStaticallyUnreachable$1(node, owner)) return;
|
|
13225
13225
|
const events = eventsBySymbolId.get(symbol.id) ?? [];
|
|
13226
13226
|
events.push({
|
|
@@ -13231,7 +13231,7 @@ const getSymbolMutationInspector = (scopes) => {
|
|
|
13231
13231
|
eventsBySymbolId.set(symbol.id, events);
|
|
13232
13232
|
});
|
|
13233
13233
|
const getInvokedOwnersBefore = (checkpoint) => {
|
|
13234
|
-
const checkpointOwner = getExecutionOwner
|
|
13234
|
+
const checkpointOwner = getExecutionOwner(checkpoint);
|
|
13235
13235
|
const checkpointStartIndex = getNodeStartIndex(checkpoint);
|
|
13236
13236
|
const invokedOwners = /* @__PURE__ */ new Set();
|
|
13237
13237
|
const visitOwner = (owner, cutoffIndex) => {
|
|
@@ -13328,14 +13328,14 @@ const getSymbolMutationInspector = (scopes) => {
|
|
|
13328
13328
|
return false;
|
|
13329
13329
|
};
|
|
13330
13330
|
const isExecutionOrderAmbiguous = (usageNode) => {
|
|
13331
|
-
const usageOwner = getExecutionOwner
|
|
13331
|
+
const usageOwner = getExecutionOwner(usageNode);
|
|
13332
13332
|
if (isNodeOfType(usageOwner, "Program")) return false;
|
|
13333
13333
|
const reachingProgramCalls = calls.filter((call) => isNodeOfType(call.owner, "Program") && canOwnerReach(call.targetOwner, usageOwner));
|
|
13334
13334
|
if (reachingProgramCalls.length === 0) return false;
|
|
13335
13335
|
return reachingProgramCalls.length !== 1 || reachingProgramCalls[0]?.targetOwner !== usageOwner;
|
|
13336
13336
|
};
|
|
13337
13337
|
const isMutationOrderAmbiguous = (symbol, usageNode, relevantPropertyName) => {
|
|
13338
|
-
const usageOwner = getExecutionOwner
|
|
13338
|
+
const usageOwner = getExecutionOwner(usageNode);
|
|
13339
13339
|
const usageStartIndex = getNodeStartIndex(usageNode);
|
|
13340
13340
|
return (eventsBySymbolId.get(symbol.id) ?? []).some((event) => {
|
|
13341
13341
|
if (relevantPropertyName !== null && event.propertyNames !== null && !event.propertyNames.has(relevantPropertyName)) return false;
|
|
@@ -13370,7 +13370,7 @@ const getSymbolMutationInspector = (scopes) => {
|
|
|
13370
13370
|
visitOwner(operation.call.targetOwner, Number.POSITIVE_INFINITY, nextActiveOwners, isConditionalPath || isConditionallyExecuted(operation.call.call, operation.call.owner));
|
|
13371
13371
|
}
|
|
13372
13372
|
};
|
|
13373
|
-
const usageOwner = getExecutionOwner
|
|
13373
|
+
const usageOwner = getExecutionOwner(usageNode);
|
|
13374
13374
|
if (!isNodeOfType(usageOwner, "Program")) visitOwner(scopes.rootScope.node, getProgramCutoffIndex(usageOwner), /* @__PURE__ */ new Set(), false);
|
|
13375
13375
|
visitOwner(usageOwner, getNodeStartIndex(usageNode), /* @__PURE__ */ new Set(), false);
|
|
13376
13376
|
return mutationEvents;
|
|
@@ -13379,7 +13379,7 @@ const getSymbolMutationInspector = (scopes) => {
|
|
|
13379
13379
|
const events = eventsBySymbolId.get(symbol.id);
|
|
13380
13380
|
if (!events) return false;
|
|
13381
13381
|
const usageStartIndex = getNodeStartIndex(usageNode);
|
|
13382
|
-
const usageOwner = getExecutionOwner
|
|
13382
|
+
const usageOwner = getExecutionOwner(usageNode);
|
|
13383
13383
|
const invokedOwners = getInvokedOwnersBefore(usageNode);
|
|
13384
13384
|
return events.some((event) => {
|
|
13385
13385
|
if (relevantPropertyName !== null && event.propertyNames !== null && !event.propertyNames.has(relevantPropertyName)) return false;
|
|
@@ -24018,26 +24018,22 @@ const isInsideTryStatement = (node, options) => {
|
|
|
24018
24018
|
return false;
|
|
24019
24019
|
};
|
|
24020
24020
|
//#endregion
|
|
24021
|
-
//#region src/plugin/utils/
|
|
24022
|
-
const
|
|
24023
|
-
const regions = /* @__PURE__ */ new Set();
|
|
24021
|
+
//#region src/plugin/utils/is-node-conditionally-executed.ts
|
|
24022
|
+
const isNodeConditionallyExecuted = (node, boundary) => {
|
|
24024
24023
|
let child = node;
|
|
24025
24024
|
let parent = child.parent ?? null;
|
|
24026
24025
|
while (parent && parent !== boundary) {
|
|
24027
|
-
if (isNodeOfType(parent, "IfStatement") && parent.test !== child)
|
|
24028
|
-
if (isNodeOfType(parent, "ConditionalExpression") && (parent.consequent === child || parent.alternate === child))
|
|
24029
|
-
if (isNodeOfType(parent, "LogicalExpression") && parent.right === child)
|
|
24030
|
-
if (isNodeOfType(parent, "AssignmentPattern") && parent.right === child)
|
|
24031
|
-
if (isNodeOfType(parent, "SwitchCase"))
|
|
24026
|
+
if (isNodeOfType(parent, "IfStatement") && parent.test !== child) return true;
|
|
24027
|
+
if (isNodeOfType(parent, "ConditionalExpression") && (parent.consequent === child || parent.alternate === child)) return true;
|
|
24028
|
+
if (isNodeOfType(parent, "LogicalExpression") && parent.right === child) return true;
|
|
24029
|
+
if (isNodeOfType(parent, "AssignmentPattern") && parent.right === child) return true;
|
|
24030
|
+
if (isNodeOfType(parent, "SwitchCase")) return true;
|
|
24032
24031
|
child = parent;
|
|
24033
24032
|
parent = child.parent ?? null;
|
|
24034
24033
|
}
|
|
24035
|
-
return
|
|
24034
|
+
return false;
|
|
24036
24035
|
};
|
|
24037
24036
|
//#endregion
|
|
24038
|
-
//#region src/plugin/utils/is-node-conditionally-executed.ts
|
|
24039
|
-
const isNodeConditionallyExecuted = (node, boundary) => getConditionalExecutionRegions(node, boundary).size > 0;
|
|
24040
|
-
//#endregion
|
|
24041
24037
|
//#region src/plugin/utils/is-non-source-filename.ts
|
|
24042
24038
|
const NON_SOURCE_FILENAME_MARKERS = [
|
|
24043
24039
|
"/dist/",
|
|
@@ -91334,35 +91330,6 @@ const getModuleNamespaceSource = (expression, scopes, visitedSymbolIds = /* @__P
|
|
|
91334
91330
|
return getModuleNamespaceSource(symbol.initializer, scopes, visitedSymbolIds);
|
|
91335
91331
|
};
|
|
91336
91332
|
//#endregion
|
|
91337
|
-
//#region src/plugin/rules/r3f/utils/r3f-public-modules.ts
|
|
91338
|
-
const R3F_PUBLIC_MODULES = new Set([
|
|
91339
|
-
"@react-three/fiber",
|
|
91340
|
-
"@react-three/fiber/legacy",
|
|
91341
|
-
"@react-three/fiber/native",
|
|
91342
|
-
"@react-three/fiber/webgpu",
|
|
91343
|
-
"react-three-fiber"
|
|
91344
|
-
]);
|
|
91345
|
-
//#endregion
|
|
91346
|
-
//#region src/plugin/rules/r3f/utils/has-r3f-runtime-import.ts
|
|
91347
|
-
const isR3fRuntimeModule = (moduleSource) => R3F_PUBLIC_MODULES.has(moduleSource) || moduleSource.startsWith("@react-three/");
|
|
91348
|
-
const hasR3fRuntimeImport = (program, scopes) => program.body.some((statement) => {
|
|
91349
|
-
if (isNodeOfType(statement, "ImportDeclaration") && !isTypeOnlyImport(statement) && typeof statement.source.value === "string") return isR3fRuntimeModule(statement.source.value);
|
|
91350
|
-
if (isNodeOfType(statement, "TSImportEqualsDeclaration")) {
|
|
91351
|
-
const moduleSource = getModuleNamespaceSource(statement.id, scopes);
|
|
91352
|
-
return moduleSource !== null && isR3fRuntimeModule(moduleSource);
|
|
91353
|
-
}
|
|
91354
|
-
if (isNodeOfType(statement, "ExpressionStatement")) {
|
|
91355
|
-
const moduleSource = getGlobalRequireModuleSource(statement.expression, scopes);
|
|
91356
|
-
return moduleSource !== null && isR3fRuntimeModule(moduleSource);
|
|
91357
|
-
}
|
|
91358
|
-
if (!isNodeOfType(statement, "VariableDeclaration")) return false;
|
|
91359
|
-
return statement.declarations.some((declaration) => {
|
|
91360
|
-
if (!declaration.init) return false;
|
|
91361
|
-
const moduleSource = getGlobalRequireModuleSource(declaration.init, scopes);
|
|
91362
|
-
return moduleSource !== null && isR3fRuntimeModule(moduleSource);
|
|
91363
|
-
});
|
|
91364
|
-
});
|
|
91365
|
-
//#endregion
|
|
91366
91333
|
//#region src/plugin/rules/r3f/utils/get-api-reference-provenance.ts
|
|
91367
91334
|
const getApiReferenceProvenance = (reference, scopes, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
|
|
91368
91335
|
const candidate = stripParenExpression(reference);
|
|
@@ -91418,6 +91385,35 @@ const getApiReferenceProvenance = (reference, scopes, visitedSymbolIds = /* @__P
|
|
|
91418
91385
|
return getApiReferenceProvenance(symbol.initializer, scopes, visitedSymbolIds);
|
|
91419
91386
|
};
|
|
91420
91387
|
//#endregion
|
|
91388
|
+
//#region src/plugin/rules/r3f/utils/r3f-public-modules.ts
|
|
91389
|
+
const R3F_PUBLIC_MODULES = new Set([
|
|
91390
|
+
"@react-three/fiber",
|
|
91391
|
+
"@react-three/fiber/legacy",
|
|
91392
|
+
"@react-three/fiber/native",
|
|
91393
|
+
"@react-three/fiber/webgpu",
|
|
91394
|
+
"react-three-fiber"
|
|
91395
|
+
]);
|
|
91396
|
+
//#endregion
|
|
91397
|
+
//#region src/plugin/rules/r3f/utils/has-r3f-runtime-import.ts
|
|
91398
|
+
const isR3fRuntimeModule = (moduleSource) => R3F_PUBLIC_MODULES.has(moduleSource) || moduleSource.startsWith("@react-three/");
|
|
91399
|
+
const hasR3fRuntimeImport = (program, scopes) => program.body.some((statement) => {
|
|
91400
|
+
if (isNodeOfType(statement, "ImportDeclaration") && !isTypeOnlyImport(statement) && typeof statement.source.value === "string") return isR3fRuntimeModule(statement.source.value);
|
|
91401
|
+
if (isNodeOfType(statement, "TSImportEqualsDeclaration")) {
|
|
91402
|
+
const moduleSource = getModuleNamespaceSource(statement.id, scopes);
|
|
91403
|
+
return moduleSource !== null && isR3fRuntimeModule(moduleSource);
|
|
91404
|
+
}
|
|
91405
|
+
if (isNodeOfType(statement, "ExpressionStatement")) {
|
|
91406
|
+
const moduleSource = getGlobalRequireModuleSource(statement.expression, scopes);
|
|
91407
|
+
return moduleSource !== null && isR3fRuntimeModule(moduleSource);
|
|
91408
|
+
}
|
|
91409
|
+
if (!isNodeOfType(statement, "VariableDeclaration")) return false;
|
|
91410
|
+
return statement.declarations.some((declaration) => {
|
|
91411
|
+
if (!declaration.init) return false;
|
|
91412
|
+
const moduleSource = getGlobalRequireModuleSource(declaration.init, scopes);
|
|
91413
|
+
return moduleSource !== null && isR3fRuntimeModule(moduleSource);
|
|
91414
|
+
});
|
|
91415
|
+
});
|
|
91416
|
+
//#endregion
|
|
91421
91417
|
//#region src/plugin/rules/r3f/utils/is-r3f-canvas.ts
|
|
91422
91418
|
const isR3fCanvas = (node, context) => {
|
|
91423
91419
|
const provenance = getApiReferenceProvenance(node.name, context.scopes);
|
|
@@ -91505,43 +91501,6 @@ const resolveLocalReactCallback = (expression, scopes) => {
|
|
|
91505
91501
|
return resolveExactLocalFunction(wrappedCallback, scopes);
|
|
91506
91502
|
};
|
|
91507
91503
|
//#endregion
|
|
91508
|
-
//#region src/plugin/rules/r3f/utils/resolve-raw-device-pixel-ratio.ts
|
|
91509
|
-
const resolveRawDevicePixelRatio = (expression, scopes, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
|
|
91510
|
-
const candidate = stripParenExpression(expression);
|
|
91511
|
-
if (isNodeOfType(candidate, "UnaryExpression") && candidate.operator === "+") return resolveRawDevicePixelRatio(candidate.argument, scopes, visitedSymbolIds);
|
|
91512
|
-
if (isNodeOfType(candidate, "BinaryExpression")) {
|
|
91513
|
-
const rawLeft = resolveRawDevicePixelRatio(candidate.left, scopes, new Set(visitedSymbolIds));
|
|
91514
|
-
const rawRight = resolveRawDevicePixelRatio(candidate.right, scopes, new Set(visitedSymbolIds));
|
|
91515
|
-
if (rawLeft && !rawRight) {
|
|
91516
|
-
const rightOperand = stripParenExpression(candidate.right);
|
|
91517
|
-
if (isNodeOfType(rightOperand, "Literal") && typeof rightOperand.value === "number" && Number.isFinite(rightOperand.value) && (candidate.operator === "+" || candidate.operator === "-" || (candidate.operator === "*" || candidate.operator === "/" || candidate.operator === "**") && rightOperand.value > 0)) return rawLeft;
|
|
91518
|
-
}
|
|
91519
|
-
if (rawRight && !rawLeft) {
|
|
91520
|
-
const leftOperand = stripParenExpression(candidate.left);
|
|
91521
|
-
if (isNodeOfType(leftOperand, "Literal") && typeof leftOperand.value === "number" && Number.isFinite(leftOperand.value) && (candidate.operator === "+" || candidate.operator === "*" && leftOperand.value > 0)) return rawRight;
|
|
91522
|
-
}
|
|
91523
|
-
return null;
|
|
91524
|
-
}
|
|
91525
|
-
if (isNodeOfType(candidate, "ArrayExpression") && candidate.elements.length === 2) {
|
|
91526
|
-
const upperBound = candidate.elements[1];
|
|
91527
|
-
return upperBound && !isNodeOfType(upperBound, "SpreadElement") ? resolveRawDevicePixelRatio(upperBound, scopes, new Set(visitedSymbolIds)) : null;
|
|
91528
|
-
}
|
|
91529
|
-
if (isNodeOfType(candidate, "MemberExpression")) {
|
|
91530
|
-
const receiver = stripParenExpression(candidate.object);
|
|
91531
|
-
return getStaticPropertyName(candidate) === "devicePixelRatio" && isNodeOfType(receiver, "Identifier") && (receiver.name === "window" || receiver.name === "globalThis") && scopes.isGlobalReference(receiver) ? candidate : null;
|
|
91532
|
-
}
|
|
91533
|
-
if (!isNodeOfType(candidate, "Identifier")) return null;
|
|
91534
|
-
const symbol = scopes.symbolFor(candidate);
|
|
91535
|
-
if (symbol?.kind !== "const" || !symbol.initializer || visitedSymbolIds.has(symbol.id) || !isNodeOfType(symbol.declarationNode, "VariableDeclarator")) return null;
|
|
91536
|
-
visitedSymbolIds.add(symbol.id);
|
|
91537
|
-
if (getDestructuredBindingPropertyName(symbol.bindingIdentifier) === "devicePixelRatio") {
|
|
91538
|
-
const initializer = stripParenExpression(symbol.initializer);
|
|
91539
|
-
if (isNodeOfType(initializer, "Identifier") && (initializer.name === "window" || initializer.name === "globalThis") && scopes.isGlobalReference(initializer)) return candidate;
|
|
91540
|
-
}
|
|
91541
|
-
if (symbol.declarationNode.id !== symbol.bindingIdentifier) return null;
|
|
91542
|
-
return resolveRawDevicePixelRatio(symbol.initializer, scopes, visitedSymbolIds);
|
|
91543
|
-
};
|
|
91544
|
-
//#endregion
|
|
91545
91504
|
//#region src/plugin/rules/r3f/utils/walk-function-execution.ts
|
|
91546
91505
|
const isUseTransitionCall = (expression, scopes) => {
|
|
91547
91506
|
const candidate = stripParenExpression(expression);
|
|
@@ -91616,6 +91575,43 @@ const walkFunctionExecution = (functionNode, scopes, visitor) => {
|
|
|
91616
91575
|
};
|
|
91617
91576
|
//#endregion
|
|
91618
91577
|
//#region src/plugin/rules/r3f/r3f-cap-device-pixel-ratio.ts
|
|
91578
|
+
const THREE_RENDERER_CONSTRUCTOR_NAMES = new Set(["WebGLRenderer", "WebGPURenderer"]);
|
|
91579
|
+
const isThreeModuleSource$4 = (moduleSource) => moduleSource === "three" || moduleSource.startsWith("three/") || moduleSource === "three-stdlib";
|
|
91580
|
+
const resolveRawDevicePixelRatio = (expression, context, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
|
|
91581
|
+
const candidate = stripParenExpression(expression);
|
|
91582
|
+
if (isNodeOfType(candidate, "UnaryExpression") && candidate.operator === "+") return resolveRawDevicePixelRatio(candidate.argument, context, visitedSymbolIds);
|
|
91583
|
+
if (isNodeOfType(candidate, "BinaryExpression")) {
|
|
91584
|
+
const rawLeft = resolveRawDevicePixelRatio(candidate.left, context, new Set(visitedSymbolIds));
|
|
91585
|
+
const rawRight = resolveRawDevicePixelRatio(candidate.right, context, new Set(visitedSymbolIds));
|
|
91586
|
+
if (rawLeft && !rawRight) {
|
|
91587
|
+
const rightOperand = stripParenExpression(candidate.right);
|
|
91588
|
+
if (isNodeOfType(rightOperand, "Literal") && typeof rightOperand.value === "number" && Number.isFinite(rightOperand.value) && (candidate.operator === "+" || candidate.operator === "-" || (candidate.operator === "*" || candidate.operator === "/" || candidate.operator === "**") && rightOperand.value > 0)) return rawLeft;
|
|
91589
|
+
}
|
|
91590
|
+
if (rawRight && !rawLeft) {
|
|
91591
|
+
const leftOperand = stripParenExpression(candidate.left);
|
|
91592
|
+
if (isNodeOfType(leftOperand, "Literal") && typeof leftOperand.value === "number" && Number.isFinite(leftOperand.value) && (candidate.operator === "+" || candidate.operator === "*" && leftOperand.value > 0)) return rawRight;
|
|
91593
|
+
}
|
|
91594
|
+
return null;
|
|
91595
|
+
}
|
|
91596
|
+
if (isNodeOfType(candidate, "ArrayExpression") && candidate.elements.length === 2) {
|
|
91597
|
+
const upperBound = candidate.elements[1];
|
|
91598
|
+
return upperBound && !isNodeOfType(upperBound, "SpreadElement") ? resolveRawDevicePixelRatio(upperBound, context, new Set(visitedSymbolIds)) : null;
|
|
91599
|
+
}
|
|
91600
|
+
if (isNodeOfType(candidate, "MemberExpression")) {
|
|
91601
|
+
const receiver = stripParenExpression(candidate.object);
|
|
91602
|
+
return getStaticPropertyName(candidate) === "devicePixelRatio" && isNodeOfType(receiver, "Identifier") && (receiver.name === "window" || receiver.name === "globalThis") && context.scopes.isGlobalReference(receiver) ? candidate : null;
|
|
91603
|
+
}
|
|
91604
|
+
if (!isNodeOfType(candidate, "Identifier")) return null;
|
|
91605
|
+
const symbol = context.scopes.symbolFor(candidate);
|
|
91606
|
+
if (symbol?.kind !== "const" || !symbol.initializer || visitedSymbolIds.has(symbol.id) || !isNodeOfType(symbol.declarationNode, "VariableDeclarator")) return null;
|
|
91607
|
+
visitedSymbolIds.add(symbol.id);
|
|
91608
|
+
if (getDestructuredBindingPropertyName(symbol.bindingIdentifier) === "devicePixelRatio") {
|
|
91609
|
+
const initializer = stripParenExpression(symbol.initializer);
|
|
91610
|
+
if (isNodeOfType(initializer, "Identifier") && (initializer.name === "window" || initializer.name === "globalThis") && context.scopes.isGlobalReference(initializer)) return candidate;
|
|
91611
|
+
}
|
|
91612
|
+
if (symbol.declarationNode.id !== symbol.bindingIdentifier) return null;
|
|
91613
|
+
return resolveRawDevicePixelRatio(symbol.initializer, context, visitedSymbolIds);
|
|
91614
|
+
};
|
|
91619
91615
|
const getExplicitObjectPropertyValue = (expression, propertyName) => {
|
|
91620
91616
|
const candidate = stripParenExpression(expression);
|
|
91621
91617
|
if (!isNodeOfType(candidate, "ObjectExpression")) return null;
|
|
@@ -91648,6 +91644,18 @@ const isR3fRootReceiver = (expression, context, visitedSymbolIds = /* @__PURE__
|
|
|
91648
91644
|
if (symbol.kind !== "const" || !symbol.initializer || !isNodeOfType(symbol.declarationNode, "VariableDeclarator") || symbol.declarationNode.id !== symbol.bindingIdentifier) return false;
|
|
91649
91645
|
return isR3fRootReceiver(symbol.initializer, context, visitedSymbolIds);
|
|
91650
91646
|
};
|
|
91647
|
+
const isThreeRendererReceiver = (expression, context, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
|
|
91648
|
+
const candidate = stripParenExpression(expression);
|
|
91649
|
+
if (isNodeOfType(candidate, "NewExpression")) {
|
|
91650
|
+
const provenance = getApiReferenceProvenance(candidate.callee, context.scopes);
|
|
91651
|
+
return Boolean(provenance && isThreeModuleSource$4(provenance.moduleSource) && THREE_RENDERER_CONSTRUCTOR_NAMES.has(provenance.apiName));
|
|
91652
|
+
}
|
|
91653
|
+
if (!isNodeOfType(candidate, "Identifier")) return false;
|
|
91654
|
+
const symbol = context.scopes.symbolFor(candidate);
|
|
91655
|
+
if (symbol?.kind !== "const" || !symbol.initializer || visitedSymbolIds.has(symbol.id) || !isNodeOfType(symbol.declarationNode, "VariableDeclarator") || symbol.declarationNode.id !== symbol.bindingIdentifier) return false;
|
|
91656
|
+
visitedSymbolIds.add(symbol.id);
|
|
91657
|
+
return isThreeRendererReceiver(symbol.initializer, context, visitedSymbolIds);
|
|
91658
|
+
};
|
|
91651
91659
|
const useThreeSelectsSetDpr = (call, context) => {
|
|
91652
91660
|
if (!isR3fApiCall(call, "useThree", context.scopes)) return false;
|
|
91653
91661
|
const selectorExpression = call.arguments[0];
|
|
@@ -91679,7 +91687,7 @@ const r3fCapDevicePixelRatio = defineRule({
|
|
|
91679
91687
|
create: (context) => {
|
|
91680
91688
|
let importsReactThreeFiber = false;
|
|
91681
91689
|
const reportRawDpr = (expression) => {
|
|
91682
|
-
const rawDpr = resolveRawDevicePixelRatio(expression, context
|
|
91690
|
+
const rawDpr = resolveRawDevicePixelRatio(expression, context);
|
|
91683
91691
|
if (!rawDpr) return;
|
|
91684
91692
|
context.report({
|
|
91685
91693
|
node: rawDpr,
|
|
@@ -91708,6 +91716,10 @@ const r3fCapDevicePixelRatio = defineRule({
|
|
|
91708
91716
|
if (dprValue) reportRawDpr(dprValue);
|
|
91709
91717
|
return;
|
|
91710
91718
|
}
|
|
91719
|
+
if (methodName === "setPixelRatio" && isThreeRendererReceiver(node.callee.object, context)) {
|
|
91720
|
+
reportRawDpr(firstArgument);
|
|
91721
|
+
return;
|
|
91722
|
+
}
|
|
91711
91723
|
},
|
|
91712
91724
|
Identifier(node) {
|
|
91713
91725
|
const parent = node.parent;
|
|
@@ -91721,14 +91733,6 @@ const r3fCapDevicePixelRatio = defineRule({
|
|
|
91721
91733
|
});
|
|
91722
91734
|
//#endregion
|
|
91723
91735
|
//#region src/plugin/rules/r3f/constants.ts
|
|
91724
|
-
const THREE_INTERPOLATION_FACTOR_ARGUMENT_BY_METHOD = new Map([
|
|
91725
|
-
["lerp", 1],
|
|
91726
|
-
["lerpColors", 2],
|
|
91727
|
-
["lerpHSL", 1],
|
|
91728
|
-
["lerpVectors", 2],
|
|
91729
|
-
["slerp", 1],
|
|
91730
|
-
["slerpQuaternions", 2]
|
|
91731
|
-
]);
|
|
91732
91736
|
const THREE_POSTPROCESSING_PASS_DISPOSAL_RELEASES = new Map([
|
|
91733
91737
|
["RenderPixelatedPass", 147],
|
|
91734
91738
|
["OutputPass", 153],
|
|
@@ -92007,7 +92011,7 @@ const THREE_OBJECT_MEMBER_PROPERTIES = new Set([
|
|
|
92007
92011
|
"rotation",
|
|
92008
92012
|
"scale"
|
|
92009
92013
|
]);
|
|
92010
|
-
const hasThreeObjectProvenance
|
|
92014
|
+
const hasThreeObjectProvenance = (expression, callback, managedRefSymbolIds, context, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
|
|
92011
92015
|
const firstCallbackParameter = isFunctionLike$1(callback) ? callback.params[0] : null;
|
|
92012
92016
|
const callbackParameter = isNodeOfType(firstCallbackParameter, "AssignmentPattern") ? firstCallbackParameter.left : firstCallbackParameter;
|
|
92013
92017
|
let current = stripParenExpression(expression);
|
|
@@ -92028,7 +92032,7 @@ const hasThreeObjectProvenance$1 = (expression, callback, managedRefSymbolIds, c
|
|
|
92028
92032
|
const symbol = context.scopes.symbolFor(current);
|
|
92029
92033
|
if (symbol?.kind !== "const" || !symbol.initializer || visitedSymbolIds.has(symbol.id) || symbol.references.some((reference) => reference.flag !== "read")) return false;
|
|
92030
92034
|
visitedSymbolIds.add(symbol.id);
|
|
92031
|
-
return hasThreeObjectProvenance
|
|
92035
|
+
return hasThreeObjectProvenance(symbol.initializer, callback, managedRefSymbolIds, context, visitedSymbolIds);
|
|
92032
92036
|
};
|
|
92033
92037
|
const r3fNoCloneInUseFrame = defineRule({
|
|
92034
92038
|
id: "r3f-no-clone-in-use-frame",
|
|
@@ -92045,7 +92049,7 @@ const r3fNoCloneInUseFrame = defineRule({
|
|
|
92045
92049
|
const callback = resolveR3fCallback(node, "useFrame", context.scopes);
|
|
92046
92050
|
if (!callback) return;
|
|
92047
92051
|
walkFunctionExecution(callback, context.scopes, (candidate, isConditionallyExecuted) => {
|
|
92048
|
-
if (isConditionallyExecuted || !isNodeOfType(candidate, "CallExpression") || !isNodeOfType(candidate.callee, "MemberExpression") || getStaticPropertyName(candidate.callee) !== "clone" || !hasThreeObjectProvenance
|
|
92052
|
+
if (isConditionallyExecuted || !isNodeOfType(candidate, "CallExpression") || !isNodeOfType(candidate.callee, "MemberExpression") || getStaticPropertyName(candidate.callee) !== "clone" || !hasThreeObjectProvenance(candidate.callee.object, callback, managedRefSymbolIds, context)) return;
|
|
92049
92053
|
context.report({
|
|
92050
92054
|
node: candidate,
|
|
92051
92055
|
message: "This clone allocates a new Three.js object every executed frame. Reuse a scratch object or clone once outside useFrame"
|
|
@@ -92716,9 +92720,6 @@ const r3fNoFreshUseThreeSelector = defineRule({
|
|
|
92716
92720
|
} })
|
|
92717
92721
|
});
|
|
92718
92722
|
//#endregion
|
|
92719
|
-
//#region src/plugin/rules/r3f/utils/is-three-module-source.ts
|
|
92720
|
-
const isThreeModuleSource$1 = (moduleSource) => moduleSource === "three" || moduleSource === "three-stdlib" || moduleSource.startsWith("three/");
|
|
92721
|
-
//#endregion
|
|
92722
92723
|
//#region src/plugin/rules/r3f/r3f-no-imperative-attach-of-managed-ref.ts
|
|
92723
92724
|
const IMPERATIVE_ATTACH_METHOD_NAMES = new Set(["add", "attach"]);
|
|
92724
92725
|
const THREE_OBJECT3D_CONSTRUCTOR_NAMES = new Set([
|
|
@@ -92747,11 +92748,12 @@ const THREE_OBJECT3D_CONSTRUCTOR_NAMES = new Set([
|
|
|
92747
92748
|
"SpotLight",
|
|
92748
92749
|
"Sprite"
|
|
92749
92750
|
]);
|
|
92751
|
+
const isThreeModuleSource$3 = (moduleSource) => moduleSource === "three" || moduleSource === "three-stdlib" || moduleSource.startsWith("three/");
|
|
92750
92752
|
const hasThreeObject3DProvenance = (expression, scopes, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
|
|
92751
92753
|
const candidate = stripParenExpression(expression);
|
|
92752
92754
|
if (isNodeOfType(candidate, "NewExpression")) {
|
|
92753
92755
|
const provenance = getApiReferenceProvenance(candidate.callee, scopes);
|
|
92754
|
-
return Boolean(provenance && isThreeModuleSource$
|
|
92756
|
+
return Boolean(provenance && isThreeModuleSource$3(provenance.moduleSource) && THREE_OBJECT3D_CONSTRUCTOR_NAMES.has(provenance.apiName));
|
|
92755
92757
|
}
|
|
92756
92758
|
if (!isNodeOfType(candidate, "Identifier")) return false;
|
|
92757
92759
|
const symbol = scopes.symbolFor(candidate);
|
|
@@ -92916,15 +92918,15 @@ const GEOMETRY_OWNER_CONSTRUCTORS = new Set([
|
|
|
92916
92918
|
"SkinnedMesh"
|
|
92917
92919
|
]);
|
|
92918
92920
|
const MATERIAL_OWNER_CONSTRUCTORS = new Set([...GEOMETRY_OWNER_CONSTRUCTORS, "Sprite"]);
|
|
92919
|
-
const isThreeModuleSource = (source) => typeof source === "string" && (source === "three" || source.startsWith("three/") || source === "three-stdlib");
|
|
92920
|
-
const getThreeConstructorName
|
|
92921
|
+
const isThreeModuleSource$2 = (source) => typeof source === "string" && (source === "three" || source.startsWith("three/") || source === "three-stdlib");
|
|
92922
|
+
const getThreeConstructorName = (constructorExpression, scopes) => {
|
|
92921
92923
|
const provenance = getApiReferenceProvenance(stripParenExpression(constructorExpression), scopes);
|
|
92922
|
-
return provenance && isThreeModuleSource(provenance.moduleSource) ? provenance.apiName : null;
|
|
92924
|
+
return provenance && isThreeModuleSource$2(provenance.moduleSource) ? provenance.apiName : null;
|
|
92923
92925
|
};
|
|
92924
92926
|
const hasThreeResourceOwnerProvenance = (expression, ownerConstructors, scopes, visitedSymbolIds) => {
|
|
92925
92927
|
const candidate = stripParenExpression(expression);
|
|
92926
92928
|
if (isNodeOfType(candidate, "NewExpression")) {
|
|
92927
|
-
const constructorName = getThreeConstructorName
|
|
92929
|
+
const constructorName = getThreeConstructorName(candidate.callee, scopes);
|
|
92928
92930
|
return Boolean(constructorName && ownerConstructors.has(constructorName));
|
|
92929
92931
|
}
|
|
92930
92932
|
if (isNodeOfType(candidate, "Identifier")) {
|
|
@@ -92938,7 +92940,7 @@ const hasThreeResourceOwnerProvenance = (expression, ownerConstructors, scopes,
|
|
|
92938
92940
|
const getResourceMethods = (constructorSuffix) => constructorSuffix === "Geometry" ? GEOMETRY_RESOURCE_METHODS : MATERIAL_RESOURCE_METHODS;
|
|
92939
92941
|
const hasThreeResourceProvenance = (expression, constructorSuffix, scopes, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
|
|
92940
92942
|
const candidate = stripParenExpression(expression);
|
|
92941
|
-
if (isNodeOfType(candidate, "NewExpression")) return getThreeConstructorName
|
|
92943
|
+
if (isNodeOfType(candidate, "NewExpression")) return getThreeConstructorName(candidate.callee, scopes)?.endsWith(constructorSuffix) ?? false;
|
|
92942
92944
|
if (isNodeOfType(candidate, "Identifier")) {
|
|
92943
92945
|
const symbol = scopes.symbolFor(candidate);
|
|
92944
92946
|
if (symbol?.kind !== "const" || !symbol.initializer || visitedSymbolIds.has(symbol.id) || !isNodeOfType(symbol.declarationNode, "VariableDeclarator") || symbol.declarationNode.id !== symbol.bindingIdentifier) return false;
|
|
@@ -92978,7 +92980,7 @@ const hasProvenIndexedThreeGeometry = (expression, scopes, visitedSymbolIds = /*
|
|
|
92978
92980
|
};
|
|
92979
92981
|
const hasFreshThreeResource = (expression, constructorSuffix, scopes, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
|
|
92980
92982
|
const candidate = stripParenExpression(expression);
|
|
92981
|
-
if (isNodeOfType(candidate, "NewExpression")) return getThreeConstructorName
|
|
92983
|
+
if (isNodeOfType(candidate, "NewExpression")) return getThreeConstructorName(candidate.callee, scopes)?.endsWith(constructorSuffix) ?? false;
|
|
92982
92984
|
if (isNodeOfType(candidate, "Identifier")) {
|
|
92983
92985
|
const symbol = scopes.symbolFor(candidate);
|
|
92984
92986
|
if (symbol?.kind !== "const" || symbol.scope.kind === "module" || !symbol.initializer || visitedSymbolIds.has(symbol.id) || !isNodeOfType(symbol.declarationNode, "VariableDeclarator") || symbol.declarationNode.id !== symbol.bindingIdentifier) return false;
|
|
@@ -93597,39 +93599,32 @@ const isGlobalBrowserFunctionCall = (call, functionName, scopes) => {
|
|
|
93597
93599
|
return isNodeOfType(receiver, "Identifier") && (receiver.name === "globalThis" || receiver.name === "window") && scopes.isGlobalReference(receiver);
|
|
93598
93600
|
};
|
|
93599
93601
|
//#endregion
|
|
93600
|
-
//#region src/plugin/
|
|
93602
|
+
//#region src/plugin/rules/r3f/r3f-no-recursive-raf-with-use-frame.ts
|
|
93603
|
+
const EFFECT_HOOK_NAMES$3 = new Set([
|
|
93604
|
+
"useEffect",
|
|
93605
|
+
"useInsertionEffect",
|
|
93606
|
+
"useLayoutEffect"
|
|
93607
|
+
]);
|
|
93601
93608
|
const getAnimationFrameCallback = (call, scopes) => {
|
|
93602
93609
|
const callbackArgument = call.arguments[0];
|
|
93603
|
-
|
|
93610
|
+
if (!callbackArgument || isNodeOfType(callbackArgument, "SpreadElement")) return null;
|
|
93611
|
+
return resolveExactLocalFunction(callbackArgument, scopes);
|
|
93604
93612
|
};
|
|
93605
|
-
const
|
|
93613
|
+
const callbackDirectlySchedulesItself = (callback, scopes) => {
|
|
93606
93614
|
let doesScheduleItself = false;
|
|
93607
93615
|
walkAst(callback, (candidate) => {
|
|
93608
93616
|
if (doesScheduleItself || candidate !== callback && isFunctionLike$1(candidate)) return false;
|
|
93609
|
-
if (isNodeOfType(candidate, "CallExpression")
|
|
93610
|
-
|
|
93611
|
-
return false;
|
|
93612
|
-
}
|
|
93617
|
+
if (!isNodeOfType(candidate, "CallExpression") || !isGlobalBrowserFunctionCall(candidate, "requestAnimationFrame", scopes)) return;
|
|
93618
|
+
doesScheduleItself = getAnimationFrameCallback(candidate, scopes) === callback;
|
|
93613
93619
|
});
|
|
93614
93620
|
return doesScheduleItself;
|
|
93615
93621
|
};
|
|
93616
|
-
const resolveRecursiveAnimationFrameCallback = (call, scopes) => {
|
|
93617
|
-
if (!isGlobalBrowserFunctionCall(call, "requestAnimationFrame", scopes)) return null;
|
|
93618
|
-
const callback = getAnimationFrameCallback(call, scopes);
|
|
93619
|
-
return callback && callbackSchedulesItself(callback, scopes) ? callback : null;
|
|
93620
|
-
};
|
|
93621
|
-
//#endregion
|
|
93622
|
-
//#region src/plugin/rules/r3f/r3f-no-recursive-raf-with-use-frame.ts
|
|
93623
|
-
const EFFECT_HOOK_NAMES$3 = new Set([
|
|
93624
|
-
"useEffect",
|
|
93625
|
-
"useInsertionEffect",
|
|
93626
|
-
"useLayoutEffect"
|
|
93627
|
-
]);
|
|
93628
93622
|
const collectRecursiveAnimationFrameStarts = (executedFunction, scopes) => {
|
|
93629
93623
|
const starts = /* @__PURE__ */ new Set();
|
|
93630
93624
|
walkFunctionExecution(executedFunction, scopes, (candidate) => {
|
|
93631
93625
|
if (!isNodeOfType(candidate, "CallExpression") || !isGlobalBrowserFunctionCall(candidate, "requestAnimationFrame", scopes)) return;
|
|
93632
|
-
|
|
93626
|
+
const callback = getAnimationFrameCallback(candidate, scopes);
|
|
93627
|
+
if (callback && callbackDirectlySchedulesItself(callback, scopes)) starts.add(candidate);
|
|
93633
93628
|
});
|
|
93634
93629
|
return starts;
|
|
93635
93630
|
};
|
|
@@ -94133,7 +94128,7 @@ const r3fNoStateInPointerMove = defineRule({
|
|
|
94133
94128
|
}
|
|
94134
94129
|
});
|
|
94135
94130
|
//#endregion
|
|
94136
|
-
//#region src/plugin/
|
|
94131
|
+
//#region src/plugin/rules/r3f/r3f-no-sync-readback-in-use-frame.ts
|
|
94137
94132
|
const CPU_TYPED_ARRAY_CONSTRUCTORS = new Set([
|
|
94138
94133
|
"BigInt64Array",
|
|
94139
94134
|
"BigUint64Array",
|
|
@@ -94147,61 +94142,44 @@ const CPU_TYPED_ARRAY_CONSTRUCTORS = new Set([
|
|
|
94147
94142
|
"Uint16Array",
|
|
94148
94143
|
"Uint32Array"
|
|
94149
94144
|
]);
|
|
94150
|
-
const
|
|
94151
|
-
const candidate = stripParenExpression(expression);
|
|
94152
|
-
if (isNodeOfType(candidate, "Identifier")) {
|
|
94153
|
-
const symbol = scopes.symbolFor(candidate);
|
|
94154
|
-
if (symbol?.kind !== "const" || !symbol.initializer || visitedSymbolIds.has(symbol.id) || symbol.references.some((reference) => reference.flag !== "read")) return false;
|
|
94155
|
-
visitedSymbolIds.add(symbol.id);
|
|
94156
|
-
return isCpuTypedArray(symbol.initializer, scopes, visitedSymbolIds);
|
|
94157
|
-
}
|
|
94158
|
-
if (!isNodeOfType(candidate, "NewExpression")) return false;
|
|
94159
|
-
const callee = stripParenExpression(candidate.callee);
|
|
94160
|
-
return isNodeOfType(callee, "Identifier") && CPU_TYPED_ARRAY_CONSTRUCTORS.has(callee.name) && scopes.isGlobalReference(callee);
|
|
94161
|
-
};
|
|
94162
|
-
//#endregion
|
|
94163
|
-
//#region src/plugin/utils/is-webgl-context-reference.ts
|
|
94145
|
+
const CANVAS_2D_CONTEXT_NAMES = new Set(["2d"]);
|
|
94164
94146
|
const WEBGL_CONTEXT_NAMES = new Set([
|
|
94165
94147
|
"experimental-webgl",
|
|
94166
94148
|
"webgl",
|
|
94167
94149
|
"webgl2"
|
|
94168
94150
|
]);
|
|
94169
|
-
const
|
|
94151
|
+
const isContextFromGetContext = (expression, contextNames, scopes, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
|
|
94170
94152
|
const candidate = stripParenExpression(expression);
|
|
94171
94153
|
if (isNodeOfType(candidate, "Identifier")) {
|
|
94172
94154
|
const symbol = scopes.symbolFor(candidate);
|
|
94173
94155
|
if (symbol?.kind !== "const" || !symbol.initializer || visitedSymbolIds.has(symbol.id) || symbol.references.some((reference) => reference.flag !== "read")) return false;
|
|
94174
94156
|
visitedSymbolIds.add(symbol.id);
|
|
94175
|
-
return
|
|
94157
|
+
return isContextFromGetContext(symbol.initializer, contextNames, scopes, visitedSymbolIds);
|
|
94176
94158
|
}
|
|
94177
94159
|
if (!isNodeOfType(candidate, "CallExpression") || !isNodeOfType(candidate.callee, "MemberExpression") || getStaticPropertyName(candidate.callee) !== "getContext") return false;
|
|
94178
94160
|
const contextName = candidate.arguments[0];
|
|
94179
|
-
|
|
94180
|
-
|
|
94161
|
+
if (!contextName || isNodeOfType(contextName, "SpreadElement")) return false;
|
|
94162
|
+
const staticContextName = stripParenExpression(contextName);
|
|
94163
|
+
return Boolean(isNodeOfType(staticContextName, "Literal") && typeof staticContextName.value === "string" && contextNames.has(staticContextName.value));
|
|
94181
94164
|
};
|
|
94182
|
-
|
|
94183
|
-
//#region src/plugin/rules/r3f/r3f-no-sync-readback-in-use-frame.ts
|
|
94184
|
-
const CANVAS_2D_CONTEXT_NAMES = new Set(["2d"]);
|
|
94185
|
-
const isContextFromGetContext = (expression, contextNames, scopes, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
|
|
94165
|
+
const isCpuTypedArray = (expression, scopes, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
|
|
94186
94166
|
const candidate = stripParenExpression(expression);
|
|
94187
94167
|
if (isNodeOfType(candidate, "Identifier")) {
|
|
94188
94168
|
const symbol = scopes.symbolFor(candidate);
|
|
94189
94169
|
if (symbol?.kind !== "const" || !symbol.initializer || visitedSymbolIds.has(symbol.id) || symbol.references.some((reference) => reference.flag !== "read")) return false;
|
|
94190
94170
|
visitedSymbolIds.add(symbol.id);
|
|
94191
|
-
return
|
|
94171
|
+
return isCpuTypedArray(symbol.initializer, scopes, visitedSymbolIds);
|
|
94192
94172
|
}
|
|
94193
|
-
if (!isNodeOfType(candidate, "
|
|
94194
|
-
const
|
|
94195
|
-
|
|
94196
|
-
const staticContextName = stripParenExpression(contextName);
|
|
94197
|
-
return Boolean(isNodeOfType(staticContextName, "Literal") && typeof staticContextName.value === "string" && contextNames.has(staticContextName.value));
|
|
94173
|
+
if (!isNodeOfType(candidate, "NewExpression")) return false;
|
|
94174
|
+
const callee = stripParenExpression(candidate.callee);
|
|
94175
|
+
return isNodeOfType(callee, "Identifier") && CPU_TYPED_ARRAY_CONSTRUCTORS.has(callee.name) && scopes.isGlobalReference(callee);
|
|
94198
94176
|
};
|
|
94199
94177
|
const getReadbackKind = (node, callback, context) => {
|
|
94200
94178
|
if (!isNodeOfType(node.callee, "MemberExpression")) return null;
|
|
94201
94179
|
const methodName = getStaticPropertyName(node.callee);
|
|
94202
94180
|
if (methodName === "readRenderTargetPixels" && (isR3fCallbackStateProperty(node.callee.object, callback, "gl", context.scopes) || isR3fCallbackStateProperty(node.callee.object, callback, "renderer", context.scopes))) return "three";
|
|
94203
94181
|
if (methodName === "getImageData" && isContextFromGetContext(node.callee.object, CANVAS_2D_CONTEXT_NAMES, context.scopes)) return "canvas";
|
|
94204
|
-
if (methodName === "readPixels" &&
|
|
94182
|
+
if (methodName === "readPixels" && isContextFromGetContext(node.callee.object, WEBGL_CONTEXT_NAMES, context.scopes)) {
|
|
94205
94183
|
const destination = node.arguments[6];
|
|
94206
94184
|
return destination && !isNodeOfType(destination, "SpreadElement") && isCpuTypedArray(destination, context.scopes) ? "webgl" : null;
|
|
94207
94185
|
}
|
|
@@ -94396,36 +94374,6 @@ const r3fPreferUseLoader = defineRule({
|
|
|
94396
94374
|
}
|
|
94397
94375
|
});
|
|
94398
94376
|
//#endregion
|
|
94399
|
-
//#region src/plugin/rules/r3f/utils/resolve-static-number.ts
|
|
94400
|
-
const resolveStaticNumber = (expression, scopes, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
|
|
94401
|
-
const candidate = stripParenExpression(expression);
|
|
94402
|
-
if (isNodeOfType(candidate, "Literal") && typeof candidate.value === "number") return Number.isFinite(candidate.value) ? candidate.value : null;
|
|
94403
|
-
if (isNodeOfType(candidate, "Identifier")) {
|
|
94404
|
-
const symbol = scopes.symbolFor(candidate);
|
|
94405
|
-
if (symbol?.kind !== "const" || !symbol.initializer || visitedSymbolIds.has(symbol.id) || symbol.references.some((reference) => reference.flag !== "read")) return null;
|
|
94406
|
-
visitedSymbolIds.add(symbol.id);
|
|
94407
|
-
return resolveStaticNumber(symbol.initializer, scopes, visitedSymbolIds);
|
|
94408
|
-
}
|
|
94409
|
-
if (isNodeOfType(candidate, "UnaryExpression")) {
|
|
94410
|
-
const argument = resolveStaticNumber(candidate.argument, scopes, visitedSymbolIds);
|
|
94411
|
-
if (argument === null) return null;
|
|
94412
|
-
if (candidate.operator === "+") return argument;
|
|
94413
|
-
if (candidate.operator === "-") return -argument;
|
|
94414
|
-
return null;
|
|
94415
|
-
}
|
|
94416
|
-
if (!isNodeOfType(candidate, "BinaryExpression")) return null;
|
|
94417
|
-
const left = resolveStaticNumber(candidate.left, scopes, new Set(visitedSymbolIds));
|
|
94418
|
-
const right = resolveStaticNumber(candidate.right, scopes, new Set(visitedSymbolIds));
|
|
94419
|
-
if (left === null || right === null) return null;
|
|
94420
|
-
let result = null;
|
|
94421
|
-
if (candidate.operator === "+") result = left + right;
|
|
94422
|
-
if (candidate.operator === "-") result = left - right;
|
|
94423
|
-
if (candidate.operator === "*") result = left * right;
|
|
94424
|
-
if (candidate.operator === "/") result = left / right;
|
|
94425
|
-
if (candidate.operator === "**") result = left ** right;
|
|
94426
|
-
return result !== null && Number.isFinite(result) ? result : null;
|
|
94427
|
-
};
|
|
94428
|
-
//#endregion
|
|
94429
94377
|
//#region src/plugin/rules/r3f/r3f-require-frame-delta.ts
|
|
94430
94378
|
const TRANSFORM_PROPERTIES = new Set([
|
|
94431
94379
|
"position",
|
|
@@ -94488,6 +94436,34 @@ const expressionReferencesDelta = (expression, callback, context, visitedSymbolI
|
|
|
94488
94436
|
});
|
|
94489
94437
|
return referencesDelta;
|
|
94490
94438
|
};
|
|
94439
|
+
const resolveStaticNumber = (expression, context, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
|
|
94440
|
+
const candidate = stripParenExpression(expression);
|
|
94441
|
+
if (isNodeOfType(candidate, "Literal") && typeof candidate.value === "number") return Number.isFinite(candidate.value) ? candidate.value : null;
|
|
94442
|
+
if (isNodeOfType(candidate, "Identifier")) {
|
|
94443
|
+
const symbol = context.scopes.symbolFor(candidate);
|
|
94444
|
+
if (symbol?.kind !== "const" || !symbol.initializer || visitedSymbolIds.has(symbol.id) || symbol.references.some((reference) => reference.flag !== "read")) return null;
|
|
94445
|
+
visitedSymbolIds.add(symbol.id);
|
|
94446
|
+
return resolveStaticNumber(symbol.initializer, context, visitedSymbolIds);
|
|
94447
|
+
}
|
|
94448
|
+
if (isNodeOfType(candidate, "UnaryExpression")) {
|
|
94449
|
+
const argument = resolveStaticNumber(candidate.argument, context, visitedSymbolIds);
|
|
94450
|
+
if (argument === null) return null;
|
|
94451
|
+
if (candidate.operator === "+") return argument;
|
|
94452
|
+
if (candidate.operator === "-") return -argument;
|
|
94453
|
+
return null;
|
|
94454
|
+
}
|
|
94455
|
+
if (!isNodeOfType(candidate, "BinaryExpression")) return null;
|
|
94456
|
+
const left = resolveStaticNumber(candidate.left, context, new Set(visitedSymbolIds));
|
|
94457
|
+
const right = resolveStaticNumber(candidate.right, context, new Set(visitedSymbolIds));
|
|
94458
|
+
if (left === null || right === null) return null;
|
|
94459
|
+
let result = null;
|
|
94460
|
+
if (candidate.operator === "+") result = left + right;
|
|
94461
|
+
if (candidate.operator === "-") result = left - right;
|
|
94462
|
+
if (candidate.operator === "*") result = left * right;
|
|
94463
|
+
if (candidate.operator === "/") result = left / right;
|
|
94464
|
+
if (candidate.operator === "**") result = left ** right;
|
|
94465
|
+
return result !== null && Number.isFinite(result) ? result : null;
|
|
94466
|
+
};
|
|
94491
94467
|
const isThreeMathUtils = (expression, context) => {
|
|
94492
94468
|
return getApiReferenceModuleSource(expression, "MathUtils", context.scopes) === "three";
|
|
94493
94469
|
};
|
|
@@ -94547,7 +94523,7 @@ const isConditionallyExecutedOnlyByReactRefAvailability = (node, callback, conte
|
|
|
94547
94523
|
}
|
|
94548
94524
|
return didFindRefAvailabilityCondition;
|
|
94549
94525
|
};
|
|
94550
|
-
const getFixedInterpolationFactor
|
|
94526
|
+
const getFixedInterpolationFactor = (node, callback, managedRefSymbolIds, context) => {
|
|
94551
94527
|
if (!isNodeOfType(node.callee, "MemberExpression")) return null;
|
|
94552
94528
|
const methodName = getStaticPropertyName(node.callee);
|
|
94553
94529
|
let factorArgumentIndex;
|
|
@@ -94556,7 +94532,7 @@ const getFixedInterpolationFactor$1 = (node, callback, managedRefSymbolIds, cont
|
|
|
94556
94532
|
if (factorArgumentIndex === void 0) return null;
|
|
94557
94533
|
const factor = node.arguments[factorArgumentIndex];
|
|
94558
94534
|
if (!factor || isNodeOfType(factor, "SpreadElement")) return null;
|
|
94559
|
-
const staticFactor = resolveStaticNumber(factor, context
|
|
94535
|
+
const staticFactor = resolveStaticNumber(factor, context);
|
|
94560
94536
|
return staticFactor !== null && staticFactor > 0 && staticFactor < 1 ? factor : null;
|
|
94561
94537
|
};
|
|
94562
94538
|
const getRotationOwner = (expression) => {
|
|
@@ -94630,7 +94606,7 @@ const r3fRequireFrameDelta = defineRule({
|
|
|
94630
94606
|
return;
|
|
94631
94607
|
}
|
|
94632
94608
|
if (!isNodeOfType(candidate, "CallExpression")) return;
|
|
94633
|
-
const factor = getFixedInterpolationFactor
|
|
94609
|
+
const factor = getFixedInterpolationFactor(candidate, callback, managedRefSymbolIds, context);
|
|
94634
94610
|
if (!factor || expressionReferencesDelta(factor, callback, context) || isConditionallyExecuted && !isBehindReactRefAvailabilityGuard) return;
|
|
94635
94611
|
context.report({
|
|
94636
94612
|
node: factor,
|
|
@@ -95670,30 +95646,20 @@ const ownedResourceHasMethodCall = (analysis, methodName, scopes, matchesCall =
|
|
|
95670
95646
|
return false;
|
|
95671
95647
|
};
|
|
95672
95648
|
//#endregion
|
|
95673
|
-
//#region src/plugin/rules/r3f/utils/create-owned-three-resource-cleanup-visitors.ts
|
|
95674
|
-
const createOwnedThreeResourceCleanupVisitors = ({ analysisOptions, constructorNameSuffix, context, message }) => ({ NewExpression(node) {
|
|
95675
|
-
const provenance = getApiReferenceProvenance(node.callee, context.scopes);
|
|
95676
|
-
if (!provenance || !isThreeModuleSource$1(provenance.moduleSource) || !provenance.apiName.endsWith(constructorNameSuffix)) return;
|
|
95677
|
-
const ownership = analyzeOwnedLifecycleResource(node, context, analysisOptions);
|
|
95678
|
-
if (!ownership || ownership.hasUnknownOwnershipTransfer) return;
|
|
95679
|
-
const allocationFunction = findEnclosingFunction$1(node);
|
|
95680
|
-
if (allocationFunction && ownedResourceHasMethodCall(ownership, "dispose", context.scopes, (call) => call.range[0] > node.range[1] && findEnclosingFunction$1(call) === allocationFunction && !isNodeConditionallyExecuted(call, allocationFunction))) return;
|
|
95681
|
-
const cleanup = analyzeOwnedLifecycleCleanup(ownership, context, (cleanupFunction) => functionInvokesOwnedResourceMethod(cleanupFunction, ownership, "dispose", context.scopes));
|
|
95682
|
-
if (cleanup.isProven || cleanup.isUnknown) return;
|
|
95683
|
-
context.report({
|
|
95684
|
-
node,
|
|
95685
|
-
message
|
|
95686
|
-
});
|
|
95687
|
-
} });
|
|
95688
|
-
//#endregion
|
|
95689
95649
|
//#region src/plugin/rules/r3f/r3f-require-owned-texture-cleanup.ts
|
|
95650
|
+
const OWNED_TEXTURE_CONSTRUCTOR_NAMES = new Set([
|
|
95651
|
+
"CanvasTexture",
|
|
95652
|
+
"DataTexture",
|
|
95653
|
+
"Texture",
|
|
95654
|
+
"VideoTexture"
|
|
95655
|
+
]);
|
|
95690
95656
|
const TEXTURE_BORROWING_METHOD_NAMES = /* @__PURE__ */ new Set();
|
|
95691
95657
|
const isMaterialTexturePropertyName = (propertyName) => propertyName === "map" || propertyName === "matcap" || Boolean(propertyName?.endsWith("Map"));
|
|
95692
95658
|
const isThreeMaterialAllocation = (expression, context) => {
|
|
95693
95659
|
const candidate = stripParenExpression(expression);
|
|
95694
95660
|
if (!isNodeOfType(candidate, "NewExpression")) return false;
|
|
95695
95661
|
const provenance = getApiReferenceProvenance(candidate.callee, context.scopes);
|
|
95696
|
-
return Boolean(provenance
|
|
95662
|
+
return Boolean(provenance?.moduleSource === "three" && provenance.apiName.endsWith("Material"));
|
|
95697
95663
|
};
|
|
95698
95664
|
const expressionResolvesToThreeMaterial = (expression, context, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
|
|
95699
95665
|
const candidate = stripParenExpression(expression);
|
|
@@ -95756,21 +95722,27 @@ const r3fRequireOwnedTextureCleanup = defineRule({
|
|
|
95756
95722
|
category: "Performance",
|
|
95757
95723
|
severity: "warn",
|
|
95758
95724
|
recommendation: "Dispose locally constructed textures in a React effect cleanup",
|
|
95759
|
-
create: (context) =>
|
|
95760
|
-
|
|
95725
|
+
create: (context) => ({ NewExpression(node) {
|
|
95726
|
+
const provenance = getApiReferenceProvenance(node.callee, context.scopes);
|
|
95727
|
+
if (provenance?.moduleSource !== "three" || !OWNED_TEXTURE_CONSTRUCTOR_NAMES.has(provenance.apiName)) return;
|
|
95728
|
+
const ownership = analyzeOwnedLifecycleResource(node, context, {
|
|
95761
95729
|
borrowedArgumentMethodNames: TEXTURE_BORROWING_METHOD_NAMES,
|
|
95762
95730
|
isBorrowedReference: (reference) => isBorrowedByThreeMaterial(reference, context),
|
|
95763
95731
|
retainsOwnershipInJsx: true
|
|
95764
|
-
}
|
|
95765
|
-
|
|
95766
|
-
context,
|
|
95767
|
-
|
|
95768
|
-
|
|
95732
|
+
});
|
|
95733
|
+
if (!ownership || ownership.hasUnknownOwnershipTransfer) return;
|
|
95734
|
+
const cleanup = analyzeOwnedLifecycleCleanup(ownership, context, (cleanupFunction) => functionInvokesOwnedResourceMethod(cleanupFunction, ownership, "dispose", context.scopes));
|
|
95735
|
+
if (cleanup.isProven || cleanup.isUnknown) return;
|
|
95736
|
+
context.report({
|
|
95737
|
+
node,
|
|
95738
|
+
message: "This locally constructed Three.js texture owns GPU resources but has no provable React cleanup. Dispose it when the owning component or hook releases it"
|
|
95739
|
+
});
|
|
95740
|
+
} })
|
|
95769
95741
|
});
|
|
95770
95742
|
//#endregion
|
|
95771
95743
|
//#region src/plugin/rules/r3f/r3f-require-projection-matrix-update.ts
|
|
95772
95744
|
const CAMERA_HOST_NAMES = new Set(["orthographicCamera", "perspectiveCamera"]);
|
|
95773
|
-
const PROJECTION_PROPERTY_NAMES
|
|
95745
|
+
const PROJECTION_PROPERTY_NAMES = new Set([
|
|
95774
95746
|
"aspect",
|
|
95775
95747
|
"bottom",
|
|
95776
95748
|
"far",
|
|
@@ -95833,7 +95805,7 @@ const hasManagedCameraRefProvenance = (expression, managedCameraRefSymbolIds, co
|
|
|
95833
95805
|
const hasR3fCameraProvenance = (expression, frameCallbacks, managedCameraRefSymbolIds, context) => hasStableRootBinding(expression, context.scopes) && (hasUseThreeCameraProvenance(expression, context) || hasFrameCameraProvenance(expression, frameCallbacks, context) || hasManagedCameraRefProvenance(expression, managedCameraRefSymbolIds, context));
|
|
95834
95806
|
const getProjectionMutationReceiver = (node) => {
|
|
95835
95807
|
const mutationTarget = isNodeOfType(node, "AssignmentExpression") ? stripParenExpression(node.left) : isNodeOfType(node, "UpdateExpression") ? stripParenExpression(node.argument) : null;
|
|
95836
|
-
return mutationTarget && isNodeOfType(mutationTarget, "MemberExpression") && PROJECTION_PROPERTY_NAMES
|
|
95808
|
+
return mutationTarget && isNodeOfType(mutationTarget, "MemberExpression") && PROJECTION_PROPERTY_NAMES.has(getStaticPropertyName(mutationTarget) ?? "") ? mutationTarget.object : null;
|
|
95837
95809
|
};
|
|
95838
95810
|
const getOnlyCallExpression = (statement) => {
|
|
95839
95811
|
if (isNodeOfType(statement, "BlockStatement")) return statement.body.length === 1 ? getOnlyCallExpression(statement.body[0]) : null;
|
|
@@ -96257,16 +96229,14 @@ const r3fWebgpuNoGlState = defineRule({
|
|
|
96257
96229
|
})
|
|
96258
96230
|
});
|
|
96259
96231
|
//#endregion
|
|
96260
|
-
//#region src/plugin/rules/r3f/
|
|
96261
|
-
const
|
|
96262
|
-
|
|
96263
|
-
|
|
96264
|
-
|
|
96265
|
-
|
|
96266
|
-
|
|
96267
|
-
|
|
96268
|
-
//#endregion
|
|
96269
|
-
//#region src/plugin/rules/r3f/utils/resolves-to-tsl-uniform.ts
|
|
96232
|
+
//#region src/plugin/rules/r3f/r3f-webgpu-no-js-uniform-branch.ts
|
|
96233
|
+
const WEBGPU_GRAPH_HOOKS = new Set([
|
|
96234
|
+
"useLocalNodes",
|
|
96235
|
+
"useNodes",
|
|
96236
|
+
"usePostProcessing",
|
|
96237
|
+
"useRenderPipeline"
|
|
96238
|
+
]);
|
|
96239
|
+
const WEBGPU_TWO_CALLBACK_HOOKS = new Set(["usePostProcessing", "useRenderPipeline"]);
|
|
96270
96240
|
const TSL_UNIFORM_MODULES = new Set(["three/tsl", "three/webgpu"]);
|
|
96271
96241
|
const resolvesToTslUniform = (expression, scopes, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
|
|
96272
96242
|
const candidate = stripParenExpression(expression);
|
|
@@ -96277,15 +96247,6 @@ const resolvesToTslUniform = (expression, scopes, visitedSymbolIds = /* @__PURE_
|
|
|
96277
96247
|
visitedSymbolIds.add(symbol.id);
|
|
96278
96248
|
return resolvesToTslUniform(symbol.initializer, scopes, visitedSymbolIds);
|
|
96279
96249
|
};
|
|
96280
|
-
//#endregion
|
|
96281
|
-
//#region src/plugin/rules/r3f/r3f-webgpu-no-js-uniform-branch.ts
|
|
96282
|
-
const WEBGPU_GRAPH_HOOKS = new Set([
|
|
96283
|
-
"useLocalNodes",
|
|
96284
|
-
"useNodes",
|
|
96285
|
-
"usePostProcessing",
|
|
96286
|
-
"useRenderPipeline"
|
|
96287
|
-
]);
|
|
96288
|
-
const WEBGPU_TWO_CALLBACK_HOOKS = new Set(["usePostProcessing", "useRenderPipeline"]);
|
|
96289
96250
|
const isUniformValueMember = (expression, callback, scopes) => {
|
|
96290
96251
|
const candidate = stripParenExpression(expression);
|
|
96291
96252
|
if (!isNodeOfType(candidate, "MemberExpression") || getStaticPropertyName(candidate) !== "value") return false;
|
|
@@ -96316,6 +96277,13 @@ const expressionReferencesUniformValue = (expression, callback, scopes, visitedS
|
|
|
96316
96277
|
});
|
|
96317
96278
|
return didFindUniformValue;
|
|
96318
96279
|
};
|
|
96280
|
+
const getControlFlowTest = (node) => {
|
|
96281
|
+
if (isNodeOfType(node, "IfStatement") || isNodeOfType(node, "WhileStatement") || isNodeOfType(node, "DoWhileStatement") || isNodeOfType(node, "ConditionalExpression")) return node.test;
|
|
96282
|
+
if (isNodeOfType(node, "SwitchStatement")) return node.discriminant;
|
|
96283
|
+
if (isNodeOfType(node, "ForStatement")) return node.test;
|
|
96284
|
+
if (isNodeOfType(node, "LogicalExpression")) return node.left;
|
|
96285
|
+
return null;
|
|
96286
|
+
};
|
|
96319
96287
|
const getWebgpuGraphHookName = (node, context) => {
|
|
96320
96288
|
for (const hookName of WEBGPU_GRAPH_HOOKS) if (isApiCallFromModules(node, hookName, R3F_WEBGPU_MODULES, context.scopes)) return hookName;
|
|
96321
96289
|
return null;
|
|
@@ -113839,363 +113807,6 @@ const tenantStaticProxyRisk = defineRule({
|
|
|
113839
113807
|
})
|
|
113840
113808
|
});
|
|
113841
113809
|
//#endregion
|
|
113842
|
-
//#region src/plugin/rules/r3f/utils/get-three-constructor-name.ts
|
|
113843
|
-
const getThreeConstructorName = (expression, scopes, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
|
|
113844
|
-
const candidate = stripParenExpression(expression);
|
|
113845
|
-
if (isNodeOfType(candidate, "NewExpression")) {
|
|
113846
|
-
const provenance = getApiReferenceProvenance(candidate.callee, scopes);
|
|
113847
|
-
return provenance && isThreeModuleSource$1(provenance.moduleSource) ? provenance.apiName : null;
|
|
113848
|
-
}
|
|
113849
|
-
if (!isNodeOfType(candidate, "Identifier")) return null;
|
|
113850
|
-
const symbol = scopes.symbolFor(candidate);
|
|
113851
|
-
if (symbol?.kind !== "const" || !symbol.initializer || visitedSymbolIds.has(symbol.id) || !isNodeOfType(symbol.declarationNode, "VariableDeclarator") || symbol.declarationNode.id !== symbol.bindingIdentifier) return null;
|
|
113852
|
-
visitedSymbolIds.add(symbol.id);
|
|
113853
|
-
return getThreeConstructorName(symbol.initializer, scopes, visitedSymbolIds);
|
|
113854
|
-
};
|
|
113855
|
-
//#endregion
|
|
113856
|
-
//#region src/plugin/rules/r3f/utils/is-three-renderer-reference.ts
|
|
113857
|
-
const THREE_RENDERER_CONSTRUCTOR_NAMES = new Set(["WebGLRenderer", "WebGPURenderer"]);
|
|
113858
|
-
const isThreeRendererReference = (expression, scopes) => THREE_RENDERER_CONSTRUCTOR_NAMES.has(getThreeConstructorName(expression, scopes) ?? "");
|
|
113859
|
-
//#endregion
|
|
113860
|
-
//#region src/plugin/rules/r3f/three-cap-device-pixel-ratio.ts
|
|
113861
|
-
const threeCapDevicePixelRatio = defineRule({
|
|
113862
|
-
id: "three-cap-device-pixel-ratio",
|
|
113863
|
-
title: "Unbounded Three.js device pixel ratio",
|
|
113864
|
-
category: "Performance",
|
|
113865
|
-
severity: "warn",
|
|
113866
|
-
recommendation: "Cap renderer pixel ratio, commonly at 2, so high-density displays do not multiply GPU work without a bound",
|
|
113867
|
-
create: (context) => ({ CallExpression(node) {
|
|
113868
|
-
if (!isNodeOfType(node.callee, "MemberExpression") || getStaticPropertyName(node.callee) !== "setPixelRatio" || !isThreeRendererReference(node.callee.object, context.scopes)) return;
|
|
113869
|
-
const pixelRatio = node.arguments[0];
|
|
113870
|
-
if (!pixelRatio || isNodeOfType(pixelRatio, "SpreadElement")) return;
|
|
113871
|
-
const rawPixelRatio = resolveRawDevicePixelRatio(pixelRatio, context.scopes);
|
|
113872
|
-
if (!rawPixelRatio) return;
|
|
113873
|
-
context.report({
|
|
113874
|
-
node: rawPixelRatio,
|
|
113875
|
-
message: "This renderer uses the device's raw pixel ratio without a cap. Bound the ratio to limit the rendered pixel count on high-density displays"
|
|
113876
|
-
});
|
|
113877
|
-
} })
|
|
113878
|
-
});
|
|
113879
|
-
//#endregion
|
|
113880
|
-
//#region src/plugin/rules/r3f/three-limit-shadowed-point-lights.ts
|
|
113881
|
-
const getExecutionOwner = (node, program) => findEnclosingFunction$1(node) ?? program;
|
|
113882
|
-
const threeLimitShadowedPointLights = defineRule({
|
|
113883
|
-
id: "three-limit-shadowed-point-lights",
|
|
113884
|
-
title: "Too many shadow-casting Three.js point lights",
|
|
113885
|
-
category: "Performance",
|
|
113886
|
-
severity: "warn",
|
|
113887
|
-
recommendation: "Keep at most two shadow-casting point lights in one scene, or use cheaper directional, spot, baked, or fake shadows",
|
|
113888
|
-
create: (context) => {
|
|
113889
|
-
const sceneLightFacts = [];
|
|
113890
|
-
const shadowedPointLights = [];
|
|
113891
|
-
let program = null;
|
|
113892
|
-
return {
|
|
113893
|
-
Program(node) {
|
|
113894
|
-
program = node;
|
|
113895
|
-
},
|
|
113896
|
-
CallExpression(node) {
|
|
113897
|
-
if (!program || !isNodeOfType(node.callee, "MemberExpression") || getStaticPropertyName(node.callee) !== "add" || getThreeConstructorName(node.callee.object, context.scopes) !== "Scene") return;
|
|
113898
|
-
const owner = getExecutionOwner(node, program);
|
|
113899
|
-
if (isNodeConditionallyExecuted(node, owner)) return;
|
|
113900
|
-
const sceneKey = resolveExpressionKey$1(node.callee.object, context);
|
|
113901
|
-
if (!sceneKey) return;
|
|
113902
|
-
for (const light of node.arguments) {
|
|
113903
|
-
if (isNodeOfType(light, "SpreadElement") || getThreeConstructorName(light, context.scopes) !== "PointLight") continue;
|
|
113904
|
-
const lightKey = resolveExpressionKey$1(light, context);
|
|
113905
|
-
if (lightKey) sceneLightFacts.push({
|
|
113906
|
-
lightKey,
|
|
113907
|
-
owner,
|
|
113908
|
-
sceneKey
|
|
113909
|
-
});
|
|
113910
|
-
}
|
|
113911
|
-
},
|
|
113912
|
-
AssignmentExpression(node) {
|
|
113913
|
-
const assignedValue = stripParenExpression(node.right);
|
|
113914
|
-
if (!program || node.operator !== "=" || !isNodeOfType(node.left, "MemberExpression") || getStaticPropertyName(node.left) !== "castShadow" || getThreeConstructorName(node.left.object, context.scopes) !== "PointLight" || !isNodeOfType(assignedValue, "Literal") || assignedValue.value !== true) return;
|
|
113915
|
-
const owner = getExecutionOwner(node, program);
|
|
113916
|
-
if (isNodeConditionallyExecuted(node, owner)) return;
|
|
113917
|
-
const lightKey = resolveExpressionKey$1(node.left.object, context);
|
|
113918
|
-
if (lightKey) shadowedPointLights.push({
|
|
113919
|
-
lightKey,
|
|
113920
|
-
node,
|
|
113921
|
-
owner
|
|
113922
|
-
});
|
|
113923
|
-
},
|
|
113924
|
-
"Program:exit"() {
|
|
113925
|
-
const countByOwnerAndScene = /* @__PURE__ */ new Map();
|
|
113926
|
-
const countedLights = /* @__PURE__ */ new Map();
|
|
113927
|
-
for (const shadowedLight of shadowedPointLights) {
|
|
113928
|
-
const matchingScene = sceneLightFacts.find((sceneLight) => sceneLight.owner === shadowedLight.owner && sceneLight.lightKey === shadowedLight.lightKey);
|
|
113929
|
-
if (!matchingScene) continue;
|
|
113930
|
-
const ownerCounts = countByOwnerAndScene.get(shadowedLight.owner) ?? /* @__PURE__ */ new Map();
|
|
113931
|
-
const ownerLights = countedLights.get(shadowedLight.owner) ?? /* @__PURE__ */ new Map();
|
|
113932
|
-
countByOwnerAndScene.set(shadowedLight.owner, ownerCounts);
|
|
113933
|
-
countedLights.set(shadowedLight.owner, ownerLights);
|
|
113934
|
-
const sceneLights = ownerLights.get(matchingScene.sceneKey) ?? /* @__PURE__ */ new Set();
|
|
113935
|
-
ownerLights.set(matchingScene.sceneKey, sceneLights);
|
|
113936
|
-
if (sceneLights.has(shadowedLight.lightKey)) continue;
|
|
113937
|
-
sceneLights.add(shadowedLight.lightKey);
|
|
113938
|
-
const nextCount = (ownerCounts.get(matchingScene.sceneKey) ?? 0) + 1;
|
|
113939
|
-
ownerCounts.set(matchingScene.sceneKey, nextCount);
|
|
113940
|
-
if (nextCount <= 2) continue;
|
|
113941
|
-
context.report({
|
|
113942
|
-
node: shadowedLight.node,
|
|
113943
|
-
message: "This is the third or later shadow-casting point light added to the same scene. Each point-light shadow renders six cube faces, multiplying shadow passes"
|
|
113944
|
-
});
|
|
113945
|
-
}
|
|
113946
|
-
}
|
|
113947
|
-
};
|
|
113948
|
-
}
|
|
113949
|
-
});
|
|
113950
|
-
//#endregion
|
|
113951
|
-
//#region src/plugin/rules/r3f/utils/has-three-object-provenance.ts
|
|
113952
|
-
const hasThreeObjectProvenance = (expression, scopes) => {
|
|
113953
|
-
let candidate = stripParenExpression(expression);
|
|
113954
|
-
while (isNodeOfType(candidate, "MemberExpression")) candidate = stripParenExpression(candidate.object);
|
|
113955
|
-
return getThreeConstructorName(candidate, scopes) !== null;
|
|
113956
|
-
};
|
|
113957
|
-
//#endregion
|
|
113958
|
-
//#region src/plugin/rules/r3f/utils/resolve-three-pointer-move-callback.ts
|
|
113959
|
-
const callbackExecutesThreeWork = (callback, context) => {
|
|
113960
|
-
let executesThreeWork = false;
|
|
113961
|
-
walkFunctionExecution(callback, context.scopes, (candidate) => {
|
|
113962
|
-
if (executesThreeWork) return;
|
|
113963
|
-
if (isNodeOfType(candidate, "NewExpression")) {
|
|
113964
|
-
const provenance = getApiReferenceProvenance(candidate.callee, context.scopes);
|
|
113965
|
-
executesThreeWork = Boolean(provenance && isThreeModuleSource$1(provenance.moduleSource));
|
|
113966
|
-
return;
|
|
113967
|
-
}
|
|
113968
|
-
if (isNodeOfType(candidate, "CallExpression") && isNodeOfType(candidate.callee, "MemberExpression")) {
|
|
113969
|
-
executesThreeWork = hasThreeObjectProvenance(candidate.callee.object, context.scopes);
|
|
113970
|
-
return;
|
|
113971
|
-
}
|
|
113972
|
-
const target = isNodeOfType(candidate, "AssignmentExpression") ? stripParenExpression(candidate.left) : isNodeOfType(candidate, "UpdateExpression") ? stripParenExpression(candidate.argument) : null;
|
|
113973
|
-
executesThreeWork = Boolean(target && isNodeOfType(target, "MemberExpression") && hasThreeObjectProvenance(target.object, context.scopes));
|
|
113974
|
-
});
|
|
113975
|
-
return executesThreeWork;
|
|
113976
|
-
};
|
|
113977
|
-
const getPointerMoveListenerCallback = (node, context) => {
|
|
113978
|
-
if (!isNodeOfType(node, "CallExpression") || !isNodeOfType(node.callee, "MemberExpression") || getStaticPropertyName(node.callee) !== "addEventListener") return null;
|
|
113979
|
-
const eventName = node.arguments[0];
|
|
113980
|
-
const callback = node.arguments[1];
|
|
113981
|
-
const listenerTarget = stripParenExpression(node.callee.object);
|
|
113982
|
-
if (!eventName || isNodeOfType(eventName, "SpreadElement") || !isNodeOfType(eventName, "Literal") || eventName.value !== "pointermove" || !callback || isNodeOfType(callback, "SpreadElement") || !isNodeOfType(listenerTarget, "MemberExpression") || getStaticPropertyName(listenerTarget) !== "domElement" || !isThreeRendererReference(listenerTarget.object, context.scopes)) return null;
|
|
113983
|
-
return resolveLocalReactCallback(callback, context.scopes);
|
|
113984
|
-
};
|
|
113985
|
-
const resolveThreePointerMoveCallback = (node, context) => {
|
|
113986
|
-
if (isNodeOfType(node, "CallExpression")) return getPointerMoveListenerCallback(node, context);
|
|
113987
|
-
if (!isNodeOfType(node, "JSXOpeningElement") || resolveJsxElementType(node) !== "canvas") return null;
|
|
113988
|
-
const attribute = getAuthoritativeJsxAttribute(node.attributes, "onPointerMove");
|
|
113989
|
-
if (!attribute?.value || !isNodeOfType(attribute.value, "JSXExpressionContainer") || isNodeOfType(attribute.value.expression, "JSXEmptyExpression")) return null;
|
|
113990
|
-
const callback = resolveLocalReactCallback(attribute.value.expression, context.scopes);
|
|
113991
|
-
return callback && callbackExecutesThreeWork(callback, context) ? callback : null;
|
|
113992
|
-
};
|
|
113993
|
-
//#endregion
|
|
113994
|
-
//#region src/plugin/rules/r3f/three-no-allocation-in-pointer-move.ts
|
|
113995
|
-
const threeNoAllocationInPointerMove = defineRule({
|
|
113996
|
-
id: "three-no-allocation-in-pointer-move",
|
|
113997
|
-
title: "Three.js allocation inside pointer-move handler",
|
|
113998
|
-
severity: "warn",
|
|
113999
|
-
recommendation: "Reuse vectors, raycasters, and other Three.js objects while handling continuous pointer movement",
|
|
114000
|
-
create: (context) => {
|
|
114001
|
-
const analyzedCallbacks = /* @__PURE__ */ new Set();
|
|
114002
|
-
const analyzeCallback = (node) => {
|
|
114003
|
-
const callback = resolveThreePointerMoveCallback(node, context);
|
|
114004
|
-
if (!callback || analyzedCallbacks.has(callback)) return;
|
|
114005
|
-
analyzedCallbacks.add(callback);
|
|
114006
|
-
walkFunctionExecution(callback, context.scopes, (candidate, isConditionallyExecuted) => {
|
|
114007
|
-
if (isConditionallyExecuted) return;
|
|
114008
|
-
if (isNodeOfType(candidate, "NewExpression")) {
|
|
114009
|
-
const provenance = getApiReferenceProvenance(candidate.callee, context.scopes);
|
|
114010
|
-
if (!provenance || !isThreeModuleSource$1(provenance.moduleSource)) return;
|
|
114011
|
-
context.report({
|
|
114012
|
-
node: candidate,
|
|
114013
|
-
message: "This Three.js constructor allocates on every pointer movement. Reuse an object created outside the handler"
|
|
114014
|
-
});
|
|
114015
|
-
return;
|
|
114016
|
-
}
|
|
114017
|
-
if (!isNodeOfType(candidate, "CallExpression") || !isNodeOfType(candidate.callee, "MemberExpression") || getStaticPropertyName(candidate.callee) !== "clone" || !hasThreeObjectProvenance(candidate.callee.object, context.scopes)) return;
|
|
114018
|
-
context.report({
|
|
114019
|
-
node: candidate,
|
|
114020
|
-
message: "This clone allocates a Three.js object on every pointer movement. Copy into a reusable object instead"
|
|
114021
|
-
});
|
|
114022
|
-
});
|
|
114023
|
-
};
|
|
114024
|
-
return {
|
|
114025
|
-
CallExpression(node) {
|
|
114026
|
-
analyzeCallback(node);
|
|
114027
|
-
},
|
|
114028
|
-
JSXOpeningElement(node) {
|
|
114029
|
-
analyzeCallback(node);
|
|
114030
|
-
}
|
|
114031
|
-
};
|
|
114032
|
-
}
|
|
114033
|
-
});
|
|
114034
|
-
//#endregion
|
|
114035
|
-
//#region src/plugin/rules/r3f/utils/resolve-three-animation-loop-callback.ts
|
|
114036
|
-
const callbackRendersWithThree = (callback, scopes) => {
|
|
114037
|
-
let doesRenderWithThree = false;
|
|
114038
|
-
walkFunctionExecution(callback, scopes, (candidate) => {
|
|
114039
|
-
if (!doesRenderWithThree && isNodeOfType(candidate, "CallExpression") && isNodeOfType(candidate.callee, "MemberExpression") && THREE_RENDER_METHOD_NAMES.has(getStaticPropertyName(candidate.callee) ?? "") && isThreeRendererReference(candidate.callee.object, scopes)) doesRenderWithThree = true;
|
|
114040
|
-
});
|
|
114041
|
-
return doesRenderWithThree;
|
|
114042
|
-
};
|
|
114043
|
-
const resolveThreeAnimationLoopCallback = (call, scopes) => {
|
|
114044
|
-
if (isNodeOfType(call.callee, "MemberExpression") && getStaticPropertyName(call.callee) === "setAnimationLoop" && isThreeRendererReference(call.callee.object, scopes)) {
|
|
114045
|
-
const callbackArgument = call.arguments[0];
|
|
114046
|
-
return callbackArgument && !isNodeOfType(callbackArgument, "SpreadElement") ? resolveLocalReactCallback(callbackArgument, scopes) : null;
|
|
114047
|
-
}
|
|
114048
|
-
const callback = resolveRecursiveAnimationFrameCallback(call, scopes);
|
|
114049
|
-
return callback && callbackRendersWithThree(callback, scopes) ? callback : null;
|
|
114050
|
-
};
|
|
114051
|
-
//#endregion
|
|
114052
|
-
//#region src/plugin/rules/r3f/three-no-async-animation-loop.ts
|
|
114053
|
-
const threeNoAsyncAnimationLoop = defineRule({
|
|
114054
|
-
id: "three-no-async-animation-loop",
|
|
114055
|
-
title: "Async Three.js animation callback",
|
|
114056
|
-
category: "Correctness",
|
|
114057
|
-
severity: "warn",
|
|
114058
|
-
recommendation: "Keep animation callbacks synchronous; start asynchronous work outside the loop and consume completed state during frames",
|
|
114059
|
-
create: (context) => {
|
|
114060
|
-
const reportedCallbacks = /* @__PURE__ */ new Set();
|
|
114061
|
-
return { CallExpression(node) {
|
|
114062
|
-
const callback = resolveThreeAnimationLoopCallback(node, context.scopes);
|
|
114063
|
-
if (!isFunctionLike$1(callback) || !callback.async || reportedCallbacks.has(callback)) return;
|
|
114064
|
-
reportedCallbacks.add(callback);
|
|
114065
|
-
context.report({
|
|
114066
|
-
node: callback,
|
|
114067
|
-
message: "The animation scheduler ignores this Promise, so rejected work can become unhandled and awaited work can overlap across frames. Keep the callback synchronous"
|
|
114068
|
-
});
|
|
114069
|
-
} };
|
|
114070
|
-
}
|
|
114071
|
-
});
|
|
114072
|
-
//#endregion
|
|
114073
|
-
//#region src/plugin/rules/r3f/three-no-clone-in-animation-loop.ts
|
|
114074
|
-
const threeNoCloneInAnimationLoop = defineRule({
|
|
114075
|
-
id: "three-no-clone-in-animation-loop",
|
|
114076
|
-
title: "Three.js clone inside animation loop",
|
|
114077
|
-
severity: "warn",
|
|
114078
|
-
recommendation: "Clone once before the animation loop or copy values into a reusable scratch object during frames",
|
|
114079
|
-
create: (context) => {
|
|
114080
|
-
const analyzedCallbacks = /* @__PURE__ */ new Set();
|
|
114081
|
-
return { CallExpression(node) {
|
|
114082
|
-
const callback = resolveThreeAnimationLoopCallback(node, context.scopes);
|
|
114083
|
-
if (!callback || analyzedCallbacks.has(callback)) return;
|
|
114084
|
-
analyzedCallbacks.add(callback);
|
|
114085
|
-
walkFunctionExecution(callback, context.scopes, (candidate, isConditionallyExecuted) => {
|
|
114086
|
-
if (isConditionallyExecuted || !isNodeOfType(candidate, "CallExpression") || !isNodeOfType(candidate.callee, "MemberExpression") || getStaticPropertyName(candidate.callee) !== "clone" || !hasThreeObjectProvenance(candidate.callee.object, context.scopes)) return;
|
|
114087
|
-
context.report({
|
|
114088
|
-
node: candidate,
|
|
114089
|
-
message: "This clone allocates a new Three.js object every executed frame. Copy into a reusable object or clone once outside the animation loop"
|
|
114090
|
-
});
|
|
114091
|
-
});
|
|
114092
|
-
} };
|
|
114093
|
-
}
|
|
114094
|
-
});
|
|
114095
|
-
//#endregion
|
|
114096
|
-
//#region src/plugin/rules/r3f/three-no-new-in-animation-loop.ts
|
|
114097
|
-
const threeNoNewInAnimationLoop = defineRule({
|
|
114098
|
-
id: "three-no-new-in-animation-loop",
|
|
114099
|
-
title: "Allocation inside Three.js animation loop",
|
|
114100
|
-
severity: "warn",
|
|
114101
|
-
recommendation: "Allocate reusable objects before the animation loop and mutate them in place during each frame",
|
|
114102
|
-
create: (context) => {
|
|
114103
|
-
const analyzedCallbacks = /* @__PURE__ */ new Set();
|
|
114104
|
-
return { CallExpression(node) {
|
|
114105
|
-
const callback = resolveThreeAnimationLoopCallback(node, context.scopes);
|
|
114106
|
-
if (!callback || analyzedCallbacks.has(callback)) return;
|
|
114107
|
-
analyzedCallbacks.add(callback);
|
|
114108
|
-
walkFunctionExecution(callback, context.scopes, (candidate, isConditionallyExecuted) => {
|
|
114109
|
-
if (candidate.type !== "NewExpression" || isConditionallyExecuted) return;
|
|
114110
|
-
context.report({
|
|
114111
|
-
node: candidate,
|
|
114112
|
-
message: "This constructor allocates a new object every executed frame. Reuse an object allocated outside the Three.js animation loop"
|
|
114113
|
-
});
|
|
114114
|
-
});
|
|
114115
|
-
} };
|
|
114116
|
-
}
|
|
114117
|
-
});
|
|
114118
|
-
//#endregion
|
|
114119
|
-
//#region src/plugin/utils/function-is-referenced-as-jsx-element.ts
|
|
114120
|
-
const functionIsReferencedAsJsxElement = (functionNode, scopes) => getFunctionBindingSymbols(functionNode, scopes).some((symbol) => symbol.references.some((reference) => {
|
|
114121
|
-
const referenceNode = reference.identifier;
|
|
114122
|
-
const parentNode = referenceNode.parent;
|
|
114123
|
-
return Boolean(isNodeOfType(referenceNode, "JSXIdentifier") && parentNode && isNodeOfType(parentNode, "JSXOpeningElement") && parentNode.name === referenceNode);
|
|
114124
|
-
}));
|
|
114125
|
-
//#endregion
|
|
114126
|
-
//#region src/plugin/rules/r3f/three-no-object-construction-in-render.ts
|
|
114127
|
-
const threeNoObjectConstructionInRender = defineRule({
|
|
114128
|
-
id: "three-no-object-construction-in-render",
|
|
114129
|
-
title: "Three.js object constructed during React render",
|
|
114130
|
-
severity: "warn",
|
|
114131
|
-
recommendation: "Construct mutable Three.js objects in a stable initializer, effect, event, or module scope instead of recreating them during React render",
|
|
114132
|
-
create: (context) => ({ NewExpression(node) {
|
|
114133
|
-
const provenance = getApiReferenceProvenance(node.callee, context.scopes);
|
|
114134
|
-
const renderOwner = findRenderPhaseComponentOrHook(node, context.scopes);
|
|
114135
|
-
if (!provenance || !isThreeModuleSource$1(provenance.moduleSource) || !renderOwner || isInsideStableReactInitializer(node, context.scopes)) return;
|
|
114136
|
-
const renderOwnerName = componentOrHookDisplayNameForFunction(renderOwner);
|
|
114137
|
-
if (!renderOwnerName || !isReactHookName(renderOwnerName) && !functionHasReactComponentEvidence(renderOwner, context.scopes, context.cfg) && !functionIsReferencedAsJsxElement(renderOwner, context.scopes)) return;
|
|
114138
|
-
context.report({
|
|
114139
|
-
node,
|
|
114140
|
-
message: `new ${provenance.apiName}() creates a fresh mutable Three.js object during this render. Move it to useMemo, a lazy useState initializer, an initialized-once ref, or module scope`
|
|
114141
|
-
});
|
|
114142
|
-
} })
|
|
114143
|
-
});
|
|
114144
|
-
//#endregion
|
|
114145
|
-
//#region src/plugin/rules/r3f/three-no-state-in-animation-loop.ts
|
|
114146
|
-
const threeNoStateInAnimationLoop = defineRule({
|
|
114147
|
-
id: "three-no-state-in-animation-loop",
|
|
114148
|
-
title: "React state update inside Three.js animation loop",
|
|
114149
|
-
severity: "warn",
|
|
114150
|
-
recommendation: "Mutate Three.js objects or refs during frames; reserve React state for guarded, infrequent transitions",
|
|
114151
|
-
create: (context) => {
|
|
114152
|
-
const analyzedCallbacks = /* @__PURE__ */ new Set();
|
|
114153
|
-
return { CallExpression(node) {
|
|
114154
|
-
const callback = resolveThreeAnimationLoopCallback(node, context.scopes);
|
|
114155
|
-
if (!callback || analyzedCallbacks.has(callback)) return;
|
|
114156
|
-
analyzedCallbacks.add(callback);
|
|
114157
|
-
walkFunctionExecution(callback, context.scopes, (candidate) => {
|
|
114158
|
-
if (!isNodeOfType(candidate, "CallExpression") || !resolveStateSetterBinding(candidate.callee, context.scopes) || isGuardedStateTransition(candidate, callback, context.scopes)) return;
|
|
114159
|
-
context.report({
|
|
114160
|
-
node: candidate,
|
|
114161
|
-
message: "This React state update can schedule a component render every frame. Mutate a Three.js object or ref, or guard an infrequent state transition"
|
|
114162
|
-
});
|
|
114163
|
-
});
|
|
114164
|
-
} };
|
|
114165
|
-
}
|
|
114166
|
-
});
|
|
114167
|
-
//#endregion
|
|
114168
|
-
//#region src/plugin/rules/r3f/three-no-state-in-pointer-move.ts
|
|
114169
|
-
const threeNoStateInPointerMove = defineRule({
|
|
114170
|
-
id: "three-no-state-in-pointer-move",
|
|
114171
|
-
title: "React state update inside Three.js pointer-move handler",
|
|
114172
|
-
severity: "warn",
|
|
114173
|
-
recommendation: "Keep continuous pointer previews in Three.js objects or refs and publish React state when the interaction commits",
|
|
114174
|
-
create: (context) => {
|
|
114175
|
-
const analyzedCallbacks = /* @__PURE__ */ new Set();
|
|
114176
|
-
const analyzeCallback = (node) => {
|
|
114177
|
-
const callback = resolveThreePointerMoveCallback(node, context);
|
|
114178
|
-
if (!callback || analyzedCallbacks.has(callback)) return;
|
|
114179
|
-
analyzedCallbacks.add(callback);
|
|
114180
|
-
walkFunctionExecution(callback, context.scopes, (candidate) => {
|
|
114181
|
-
if (!isNodeOfType(candidate, "CallExpression") || !resolveStateSetterBinding(candidate.callee, context.scopes) || isGuardedStateTransition(candidate, callback, context.scopes)) return;
|
|
114182
|
-
context.report({
|
|
114183
|
-
node: candidate,
|
|
114184
|
-
message: "This React state update can render on every pointer movement. Mutate a Three.js object or ref and publish state on pointer-up"
|
|
114185
|
-
});
|
|
114186
|
-
});
|
|
114187
|
-
};
|
|
114188
|
-
return {
|
|
114189
|
-
CallExpression(node) {
|
|
114190
|
-
analyzeCallback(node);
|
|
114191
|
-
},
|
|
114192
|
-
JSXOpeningElement(node) {
|
|
114193
|
-
analyzeCallback(node);
|
|
114194
|
-
}
|
|
114195
|
-
};
|
|
114196
|
-
}
|
|
114197
|
-
});
|
|
114198
|
-
//#endregion
|
|
114199
113810
|
//#region src/plugin/rules/r3f/three-require-animation-mixer-cleanup.ts
|
|
114200
113811
|
const MIXER_BORROWING_METHOD_NAMES = /* @__PURE__ */ new Set();
|
|
114201
113812
|
const FINE_GRAINED_UNCACHE_METHOD_NAMES = ["uncacheAction", "uncacheClip"];
|
|
@@ -114303,247 +113914,6 @@ const threeRequireControlsCleanup = defineRule({
|
|
|
114303
113914
|
} })
|
|
114304
113915
|
});
|
|
114305
113916
|
//#endregion
|
|
114306
|
-
//#region src/plugin/rules/r3f/three-require-frame-delta.ts
|
|
114307
|
-
const TRANSFORM_PROPERTY_NAMES = new Set([
|
|
114308
|
-
"position",
|
|
114309
|
-
"quaternion",
|
|
114310
|
-
"rotation",
|
|
114311
|
-
"scale"
|
|
114312
|
-
]);
|
|
114313
|
-
const isThreeTransformMember = (expression, context) => {
|
|
114314
|
-
let candidate = stripParenExpression(expression);
|
|
114315
|
-
let hasTransformProperty = false;
|
|
114316
|
-
while (isNodeOfType(candidate, "MemberExpression")) {
|
|
114317
|
-
if (TRANSFORM_PROPERTY_NAMES.has(getStaticPropertyName(candidate) ?? "")) hasTransformProperty = true;
|
|
114318
|
-
candidate = stripParenExpression(candidate.object);
|
|
114319
|
-
}
|
|
114320
|
-
return hasTransformProperty && hasThreeObjectProvenance(expression, context.scopes);
|
|
114321
|
-
};
|
|
114322
|
-
const expressionUsesThreeClockDelta = (expression, context, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
|
|
114323
|
-
let usesDelta = false;
|
|
114324
|
-
walkAst(expression, (candidate) => {
|
|
114325
|
-
if (isNodeOfType(candidate, "CallExpression") && isNodeOfType(candidate.callee, "MemberExpression") && getStaticPropertyName(candidate.callee) === "getDelta" && getThreeConstructorName(candidate.callee.object, context.scopes) === "Clock") {
|
|
114326
|
-
usesDelta = true;
|
|
114327
|
-
return false;
|
|
114328
|
-
}
|
|
114329
|
-
if (!isNodeOfType(candidate, "Identifier")) return;
|
|
114330
|
-
const symbol = context.scopes.symbolFor(candidate);
|
|
114331
|
-
if (symbol?.kind !== "const" || !symbol.initializer || visitedSymbolIds.has(symbol.id) || symbol.references.some((reference) => reference.flag !== "read")) return;
|
|
114332
|
-
visitedSymbolIds.add(symbol.id);
|
|
114333
|
-
if (expressionUsesThreeClockDelta(symbol.initializer, context, visitedSymbolIds)) {
|
|
114334
|
-
usesDelta = true;
|
|
114335
|
-
return false;
|
|
114336
|
-
}
|
|
114337
|
-
});
|
|
114338
|
-
return usesDelta;
|
|
114339
|
-
};
|
|
114340
|
-
const getFixedInterpolationFactor = (node, context) => {
|
|
114341
|
-
if (!isNodeOfType(node.callee, "MemberExpression")) return null;
|
|
114342
|
-
const methodName = getStaticPropertyName(node.callee);
|
|
114343
|
-
let factorArgumentIndex;
|
|
114344
|
-
if (methodName === "lerp" && getApiReferenceModuleSource(node.callee.object, "MathUtils", context.scopes) === "three") factorArgumentIndex = 2;
|
|
114345
|
-
else if (methodName && hasThreeObjectProvenance(node.callee.object, context.scopes)) factorArgumentIndex = THREE_INTERPOLATION_FACTOR_ARGUMENT_BY_METHOD.get(methodName);
|
|
114346
|
-
if (factorArgumentIndex === void 0) return null;
|
|
114347
|
-
const factor = node.arguments[factorArgumentIndex];
|
|
114348
|
-
if (!factor || isNodeOfType(factor, "SpreadElement")) return null;
|
|
114349
|
-
const staticFactor = resolveStaticNumber(factor, context.scopes);
|
|
114350
|
-
return staticFactor !== null && staticFactor > 0 && staticFactor < 1 ? factor : null;
|
|
114351
|
-
};
|
|
114352
|
-
const threeRequireFrameDelta = defineRule({
|
|
114353
|
-
id: "three-require-frame-delta",
|
|
114354
|
-
title: "Frame-rate-dependent Three.js animation",
|
|
114355
|
-
category: "Correctness",
|
|
114356
|
-
severity: "warn",
|
|
114357
|
-
recommendation: "Scale incremental transforms and interpolation by Clock.getDelta(), use delta-aware damping, or assign from absolute animation time",
|
|
114358
|
-
create: (context) => {
|
|
114359
|
-
const analyzedCallbacks = /* @__PURE__ */ new Set();
|
|
114360
|
-
return { CallExpression(node) {
|
|
114361
|
-
const callback = resolveThreeAnimationLoopCallback(node, context.scopes);
|
|
114362
|
-
if (!callback || analyzedCallbacks.has(callback)) return;
|
|
114363
|
-
analyzedCallbacks.add(callback);
|
|
114364
|
-
walkFunctionExecution(callback, context.scopes, (candidate, isConditionallyExecuted) => {
|
|
114365
|
-
if (isConditionallyExecuted) return;
|
|
114366
|
-
if (isNodeOfType(candidate, "UpdateExpression") && isThreeTransformMember(candidate.argument, context)) {
|
|
114367
|
-
context.report({
|
|
114368
|
-
node: candidate,
|
|
114369
|
-
message: "This transform changes by a fixed amount per frame, so animation speed depends on refresh rate. Use a Three.js Clock delta instead of an update operator"
|
|
114370
|
-
});
|
|
114371
|
-
return;
|
|
114372
|
-
}
|
|
114373
|
-
if (isNodeOfType(candidate, "AssignmentExpression") && (candidate.operator === "+=" || candidate.operator === "-=") && isThreeTransformMember(candidate.left, context) && !expressionUsesThreeClockDelta(candidate.right, context)) {
|
|
114374
|
-
context.report({
|
|
114375
|
-
node: candidate,
|
|
114376
|
-
message: "This transform changes by a fixed amount per frame, so animation speed depends on refresh rate. Multiply the increment by Clock.getDelta()"
|
|
114377
|
-
});
|
|
114378
|
-
return;
|
|
114379
|
-
}
|
|
114380
|
-
if (!isNodeOfType(candidate, "CallExpression")) return;
|
|
114381
|
-
const factor = getFixedInterpolationFactor(candidate, context);
|
|
114382
|
-
if (!factor || expressionUsesThreeClockDelta(factor, context)) return;
|
|
114383
|
-
context.report({
|
|
114384
|
-
node: factor,
|
|
114385
|
-
message: "This fixed interpolation factor converges once per frame, so its speed changes with refresh rate. Derive the factor from Clock.getDelta() or use delta-aware damping"
|
|
114386
|
-
});
|
|
114387
|
-
});
|
|
114388
|
-
} };
|
|
114389
|
-
}
|
|
114390
|
-
});
|
|
114391
|
-
//#endregion
|
|
114392
|
-
//#region src/plugin/rules/r3f/three-require-instanced-buffer-update.ts
|
|
114393
|
-
const getInstancedBufferMutation = (node, context) => {
|
|
114394
|
-
const callee = stripParenExpression(node.callee);
|
|
114395
|
-
if (!isNodeOfType(callee, "MemberExpression")) return null;
|
|
114396
|
-
const methodName = getStaticPropertyName(callee);
|
|
114397
|
-
if (methodName !== "setMatrixAt" && methodName !== "setColorAt") return null;
|
|
114398
|
-
if (getThreeConstructorName(callee.object, context.scopes) !== "InstancedMesh") return null;
|
|
114399
|
-
const receiverKey = resolveExpressionKey$1(callee.object, context);
|
|
114400
|
-
if (!receiverKey) return null;
|
|
114401
|
-
return {
|
|
114402
|
-
bufferPropertyName: methodName === "setMatrixAt" ? "instanceMatrix" : "instanceColor",
|
|
114403
|
-
methodName,
|
|
114404
|
-
node,
|
|
114405
|
-
receiverKey
|
|
114406
|
-
};
|
|
114407
|
-
};
|
|
114408
|
-
const getInstancedBufferCompletion = (node, context) => {
|
|
114409
|
-
const assignedValue = stripParenExpression(node.right);
|
|
114410
|
-
const needsUpdateMember = stripParenExpression(node.left);
|
|
114411
|
-
if (node.operator !== "=" || !isNodeOfType(assignedValue, "Literal") || assignedValue.value !== true || !isNodeOfType(needsUpdateMember, "MemberExpression") || getStaticPropertyName(needsUpdateMember) !== "needsUpdate") return null;
|
|
114412
|
-
const bufferMember = stripParenExpression(needsUpdateMember.object);
|
|
114413
|
-
if (!isNodeOfType(bufferMember, "MemberExpression")) return null;
|
|
114414
|
-
const bufferPropertyName = getStaticPropertyName(bufferMember);
|
|
114415
|
-
if (bufferPropertyName !== "instanceMatrix" && bufferPropertyName !== "instanceColor") return null;
|
|
114416
|
-
if (getThreeConstructorName(bufferMember.object, context.scopes) !== "InstancedMesh") return null;
|
|
114417
|
-
const receiverKey = resolveExpressionKey$1(bufferMember.object, context);
|
|
114418
|
-
return receiverKey ? {
|
|
114419
|
-
bufferPropertyName,
|
|
114420
|
-
node,
|
|
114421
|
-
receiverKey
|
|
114422
|
-
} : null;
|
|
114423
|
-
};
|
|
114424
|
-
const getOpaqueInstancedBufferCompletions = (node, context) => {
|
|
114425
|
-
if (!isImportedOrStableParameterCall(node, context.scopes)) return [];
|
|
114426
|
-
const completions = [];
|
|
114427
|
-
for (const argument of node.arguments) {
|
|
114428
|
-
if (isNodeOfType(argument, "SpreadElement")) continue;
|
|
114429
|
-
const candidate = stripParenExpression(argument);
|
|
114430
|
-
if (isNodeOfType(candidate, "MemberExpression")) {
|
|
114431
|
-
const bufferPropertyName = getStaticPropertyName(candidate);
|
|
114432
|
-
if ((bufferPropertyName === "instanceMatrix" || bufferPropertyName === "instanceColor") && getThreeConstructorName(candidate.object, context.scopes) === "InstancedMesh") {
|
|
114433
|
-
const receiverKey = resolveExpressionKey$1(candidate.object, context);
|
|
114434
|
-
if (receiverKey) completions.push({
|
|
114435
|
-
bufferPropertyName,
|
|
114436
|
-
node,
|
|
114437
|
-
receiverKey
|
|
114438
|
-
});
|
|
114439
|
-
continue;
|
|
114440
|
-
}
|
|
114441
|
-
}
|
|
114442
|
-
if (getThreeConstructorName(candidate, context.scopes) !== "InstancedMesh") continue;
|
|
114443
|
-
const receiverKey = resolveExpressionKey$1(candidate, context);
|
|
114444
|
-
if (!receiverKey) continue;
|
|
114445
|
-
completions.push({
|
|
114446
|
-
bufferPropertyName: "instanceMatrix",
|
|
114447
|
-
node,
|
|
114448
|
-
receiverKey
|
|
114449
|
-
});
|
|
114450
|
-
completions.push({
|
|
114451
|
-
bufferPropertyName: "instanceColor",
|
|
114452
|
-
node,
|
|
114453
|
-
receiverKey
|
|
114454
|
-
});
|
|
114455
|
-
}
|
|
114456
|
-
return completions;
|
|
114457
|
-
};
|
|
114458
|
-
const completionCoversMutation = (mutation, completions, program, context) => {
|
|
114459
|
-
const owner = context.cfg.enclosingFunction(mutation.node);
|
|
114460
|
-
const matchingCompletions = completions.filter((completion) => completion.receiverKey === mutation.receiverKey && completion.bufferPropertyName === mutation.bufferPropertyName && context.cfg.enclosingFunction(completion.node) === owner);
|
|
114461
|
-
if (owner) return doNodesCoverEveryPathAfterNode(mutation.node, matchingCompletions.map((completion) => completion.node), context);
|
|
114462
|
-
const mutationStart = getRangeStart(mutation.node);
|
|
114463
|
-
return matchingCompletions.some((completion) => {
|
|
114464
|
-
const completionStart = getRangeStart(completion.node);
|
|
114465
|
-
return mutationStart !== null && completionStart !== null && completionStart > mutationStart && !isNodeConditionallyExecuted(completion.node, program);
|
|
114466
|
-
});
|
|
114467
|
-
};
|
|
114468
|
-
const threeRequireInstancedBufferUpdate = defineRule({
|
|
114469
|
-
id: "three-require-instanced-buffer-update",
|
|
114470
|
-
title: "Three.js instanced mesh buffer is not marked for upload",
|
|
114471
|
-
category: "Correctness",
|
|
114472
|
-
severity: "error",
|
|
114473
|
-
recommendation: "After setMatrixAt or setColorAt, set the matching instance buffer's needsUpdate flag to true",
|
|
114474
|
-
create: (context) => {
|
|
114475
|
-
const mutations = [];
|
|
114476
|
-
const completions = [];
|
|
114477
|
-
let program = null;
|
|
114478
|
-
return {
|
|
114479
|
-
Program(node) {
|
|
114480
|
-
program = node;
|
|
114481
|
-
},
|
|
114482
|
-
AssignmentExpression(node) {
|
|
114483
|
-
const completion = getInstancedBufferCompletion(node, context);
|
|
114484
|
-
if (completion) completions.push(completion);
|
|
114485
|
-
},
|
|
114486
|
-
CallExpression(node) {
|
|
114487
|
-
const mutation = getInstancedBufferMutation(node, context);
|
|
114488
|
-
if (mutation) {
|
|
114489
|
-
mutations.push(mutation);
|
|
114490
|
-
return;
|
|
114491
|
-
}
|
|
114492
|
-
completions.push(...getOpaqueInstancedBufferCompletions(node, context));
|
|
114493
|
-
},
|
|
114494
|
-
"Program:exit"() {
|
|
114495
|
-
if (!program) return;
|
|
114496
|
-
for (const mutation of mutations) {
|
|
114497
|
-
if (completionCoversMutation(mutation, completions, program, context)) continue;
|
|
114498
|
-
context.report({
|
|
114499
|
-
node: mutation.node,
|
|
114500
|
-
message: `After ${mutation.methodName}, set ${mutation.bufferPropertyName}.needsUpdate to true so Three.js uploads the changed instance data`
|
|
114501
|
-
});
|
|
114502
|
-
}
|
|
114503
|
-
}
|
|
114504
|
-
};
|
|
114505
|
-
}
|
|
114506
|
-
});
|
|
114507
|
-
//#endregion
|
|
114508
|
-
//#region src/plugin/rules/r3f/three-require-owned-geometry-cleanup.ts
|
|
114509
|
-
const threeRequireOwnedGeometryCleanup = defineRule({
|
|
114510
|
-
id: "three-require-owned-geometry-cleanup",
|
|
114511
|
-
title: "Locally owned Three.js geometry is not disposed",
|
|
114512
|
-
category: "Performance",
|
|
114513
|
-
severity: "warn",
|
|
114514
|
-
recommendation: "Dispose locally constructed geometries when their React owner releases them",
|
|
114515
|
-
create: (context) => createOwnedThreeResourceCleanupVisitors({
|
|
114516
|
-
constructorNameSuffix: "Geometry",
|
|
114517
|
-
context,
|
|
114518
|
-
message: "This locally constructed Three.js geometry owns GPU buffers but has no provable React cleanup. Dispose it when the owning component or hook releases it"
|
|
114519
|
-
})
|
|
114520
|
-
});
|
|
114521
|
-
//#endregion
|
|
114522
|
-
//#region src/plugin/rules/r3f/three-require-owned-material-cleanup.ts
|
|
114523
|
-
const threeRequireOwnedMaterialCleanup = defineRule({
|
|
114524
|
-
id: "three-require-owned-material-cleanup",
|
|
114525
|
-
title: "Locally owned Three.js material is not disposed",
|
|
114526
|
-
category: "Performance",
|
|
114527
|
-
severity: "warn",
|
|
114528
|
-
recommendation: "Dispose locally constructed materials when their React owner releases them",
|
|
114529
|
-
create: (context) => createOwnedThreeResourceCleanupVisitors({
|
|
114530
|
-
constructorNameSuffix: "Material",
|
|
114531
|
-
context,
|
|
114532
|
-
message: "This locally constructed Three.js material owns a GPU shader program but has no provable React cleanup. Dispose it when the owning component or hook releases it"
|
|
114533
|
-
})
|
|
114534
|
-
});
|
|
114535
|
-
//#endregion
|
|
114536
|
-
//#region src/plugin/rules/r3f/three-require-owned-texture-cleanup.ts
|
|
114537
|
-
const threeRequireOwnedTextureCleanup = defineRule({
|
|
114538
|
-
id: "three-require-owned-texture-cleanup",
|
|
114539
|
-
title: "Locally owned Three.js texture is not disposed",
|
|
114540
|
-
category: "Performance",
|
|
114541
|
-
severity: "warn",
|
|
114542
|
-
disabledWhen: ["r3f"],
|
|
114543
|
-
recommendation: "Dispose locally constructed textures when their React owner releases them",
|
|
114544
|
-
create: r3fRequireOwnedTextureCleanup.create
|
|
114545
|
-
});
|
|
114546
|
-
//#endregion
|
|
114547
113917
|
//#region src/plugin/rules/r3f/three-require-postprocessing-cleanup.ts
|
|
114548
113918
|
const POSTPROCESSING_BORROWING_METHOD_NAMES = /* @__PURE__ */ new Set();
|
|
114549
113919
|
const THREE_COMPOSER_BORROWING_METHOD_NAMES = new Set(["addPass", "insertPass"]);
|
|
@@ -114663,110 +114033,6 @@ const threeRequirePostprocessingCleanup = defineRule({
|
|
|
114663
114033
|
} })
|
|
114664
114034
|
});
|
|
114665
114035
|
//#endregion
|
|
114666
|
-
//#region src/plugin/rules/r3f/three-require-projection-matrix-update.ts
|
|
114667
|
-
const CAMERA_CONSTRUCTOR_NAMES = new Set(["OrthographicCamera", "PerspectiveCamera"]);
|
|
114668
|
-
const PROJECTION_PROPERTY_NAMES = new Set([
|
|
114669
|
-
"aspect",
|
|
114670
|
-
"bottom",
|
|
114671
|
-
"far",
|
|
114672
|
-
"filmGauge",
|
|
114673
|
-
"filmOffset",
|
|
114674
|
-
"fov",
|
|
114675
|
-
"left",
|
|
114676
|
-
"near",
|
|
114677
|
-
"right",
|
|
114678
|
-
"top",
|
|
114679
|
-
"zoom"
|
|
114680
|
-
]);
|
|
114681
|
-
const getProjectionMutation = (node, context) => {
|
|
114682
|
-
const target = isNodeOfType(node, "AssignmentExpression") ? stripParenExpression(node.left) : isNodeOfType(node, "UpdateExpression") ? stripParenExpression(node.argument) : null;
|
|
114683
|
-
if (!target || !isNodeOfType(target, "MemberExpression") || !PROJECTION_PROPERTY_NAMES.has(getStaticPropertyName(target) ?? "") || !CAMERA_CONSTRUCTOR_NAMES.has(getThreeConstructorName(target.object, context.scopes) ?? "")) return null;
|
|
114684
|
-
const receiverKey = resolveExpressionKey$1(target.object, context);
|
|
114685
|
-
return receiverKey ? {
|
|
114686
|
-
node,
|
|
114687
|
-
receiverKey
|
|
114688
|
-
} : null;
|
|
114689
|
-
};
|
|
114690
|
-
const getProjectionUpdate = (node, context) => {
|
|
114691
|
-
const callee = stripParenExpression(node.callee);
|
|
114692
|
-
if (!isNodeOfType(callee, "MemberExpression") || getStaticPropertyName(callee) !== "updateProjectionMatrix" || !CAMERA_CONSTRUCTOR_NAMES.has(getThreeConstructorName(callee.object, context.scopes) ?? "")) return null;
|
|
114693
|
-
const receiverKey = resolveExpressionKey$1(callee.object, context);
|
|
114694
|
-
return receiverKey ? {
|
|
114695
|
-
node,
|
|
114696
|
-
receiverKey
|
|
114697
|
-
} : null;
|
|
114698
|
-
};
|
|
114699
|
-
const getOpaqueProjectionUpdates = (node, context) => {
|
|
114700
|
-
if (!isImportedOrStableParameterCall(node, context.scopes)) return [];
|
|
114701
|
-
const updates = [];
|
|
114702
|
-
for (const argument of node.arguments) {
|
|
114703
|
-
if (isNodeOfType(argument, "SpreadElement") || !CAMERA_CONSTRUCTOR_NAMES.has(getThreeConstructorName(argument, context.scopes) ?? "")) continue;
|
|
114704
|
-
const receiverKey = resolveExpressionKey$1(argument, context);
|
|
114705
|
-
if (receiverKey) updates.push({
|
|
114706
|
-
node,
|
|
114707
|
-
receiverKey
|
|
114708
|
-
});
|
|
114709
|
-
}
|
|
114710
|
-
return updates;
|
|
114711
|
-
};
|
|
114712
|
-
const moduleUpdateCoversMutation = (mutation, update, program) => {
|
|
114713
|
-
const mutationStart = getRangeStart(mutation.node);
|
|
114714
|
-
const updateStart = getRangeStart(update.node);
|
|
114715
|
-
if (mutationStart === null || updateStart === null || updateStart <= mutationStart) return false;
|
|
114716
|
-
if (!isNodeConditionallyExecuted(update.node, program)) return true;
|
|
114717
|
-
const mutationRegions = getConditionalExecutionRegions(mutation.node, program);
|
|
114718
|
-
return [...getConditionalExecutionRegions(update.node, program)].every((region) => mutationRegions.has(region));
|
|
114719
|
-
};
|
|
114720
|
-
const updateCoversMutation = (mutation, updates, program, context) => {
|
|
114721
|
-
const owner = context.cfg.enclosingFunction(mutation.node);
|
|
114722
|
-
const matchingUpdates = updates.filter((update) => update.receiverKey === mutation.receiverKey && context.cfg.enclosingFunction(update.node) === owner);
|
|
114723
|
-
if (owner) return doNodesCoverEveryPathAfterNode(mutation.node, matchingUpdates.map((update) => update.node), context);
|
|
114724
|
-
return matchingUpdates.some((update) => moduleUpdateCoversMutation(mutation, update, program));
|
|
114725
|
-
};
|
|
114726
|
-
const threeRequireProjectionMatrixUpdate = defineRule({
|
|
114727
|
-
id: "three-require-projection-matrix-update",
|
|
114728
|
-
title: "Missing Three.js camera projection-matrix update",
|
|
114729
|
-
category: "Correctness",
|
|
114730
|
-
severity: "error",
|
|
114731
|
-
recommendation: "Call camera.updateProjectionMatrix() after changing projection properties so Three.js renders the new frustum",
|
|
114732
|
-
create: (context) => {
|
|
114733
|
-
const mutations = [];
|
|
114734
|
-
const updates = [];
|
|
114735
|
-
let program = null;
|
|
114736
|
-
return {
|
|
114737
|
-
Program(node) {
|
|
114738
|
-
program = node;
|
|
114739
|
-
},
|
|
114740
|
-
AssignmentExpression(node) {
|
|
114741
|
-
const mutation = getProjectionMutation(node, context);
|
|
114742
|
-
if (mutation) mutations.push(mutation);
|
|
114743
|
-
},
|
|
114744
|
-
UpdateExpression(node) {
|
|
114745
|
-
const mutation = getProjectionMutation(node, context);
|
|
114746
|
-
if (mutation) mutations.push(mutation);
|
|
114747
|
-
},
|
|
114748
|
-
CallExpression(node) {
|
|
114749
|
-
const update = getProjectionUpdate(node, context);
|
|
114750
|
-
if (update) {
|
|
114751
|
-
updates.push(update);
|
|
114752
|
-
return;
|
|
114753
|
-
}
|
|
114754
|
-
updates.push(...getOpaqueProjectionUpdates(node, context));
|
|
114755
|
-
},
|
|
114756
|
-
"Program:exit"() {
|
|
114757
|
-
if (!program) return;
|
|
114758
|
-
for (const mutation of mutations) {
|
|
114759
|
-
if (updateCoversMutation(mutation, updates, program, context)) continue;
|
|
114760
|
-
context.report({
|
|
114761
|
-
node: mutation.node,
|
|
114762
|
-
message: "This camera projection property changes without a later updateProjectionMatrix() call on every path, so Three.js can keep rendering a stale projection matrix"
|
|
114763
|
-
});
|
|
114764
|
-
}
|
|
114765
|
-
}
|
|
114766
|
-
};
|
|
114767
|
-
}
|
|
114768
|
-
});
|
|
114769
|
-
//#endregion
|
|
114770
114036
|
//#region src/plugin/rules/r3f/three-require-render-target-cleanup.ts
|
|
114771
114037
|
const RENDER_TARGET_CONSTRUCTORS = new Set([
|
|
114772
114038
|
"RenderTarget",
|
|
@@ -114779,6 +114045,7 @@ const RENDER_TARGET_BORROWING_METHODS = new Set([
|
|
|
114779
114045
|
"setRenderTarget",
|
|
114780
114046
|
"setRenderTargetTextures"
|
|
114781
114047
|
]);
|
|
114048
|
+
const isThreeModuleSource$1 = (moduleSource) => moduleSource === "three" || moduleSource === "three-stdlib" || moduleSource.startsWith("three/");
|
|
114782
114049
|
const threeRequireRenderTargetCleanup = defineRule({
|
|
114783
114050
|
id: "three-require-render-target-cleanup",
|
|
114784
114051
|
title: "Undisposed Three.js render target",
|
|
@@ -114803,6 +114070,7 @@ const threeRequireRenderTargetCleanup = defineRule({
|
|
|
114803
114070
|
//#endregion
|
|
114804
114071
|
//#region src/plugin/rules/r3f/three-require-renderer-cleanup.ts
|
|
114805
114072
|
const RENDERER_CONSTRUCTORS = new Set(["WebGLRenderer", "WebGPURenderer"]);
|
|
114073
|
+
const isThreeModuleSource = (moduleSource) => moduleSource === "three" || moduleSource === "three-stdlib" || moduleSource.startsWith("three/");
|
|
114806
114074
|
const isNullArgument = (call) => {
|
|
114807
114075
|
const argument = call.arguments[0];
|
|
114808
114076
|
return Boolean(isNodeOfType(argument, "Literal") && argument.value === null);
|
|
@@ -114931,7 +114199,7 @@ const threeRequireRendererCleanup = defineRule({
|
|
|
114931
114199
|
recommendation: "Dispose component-owned renderers and stop their animation loop or animation frame in matching React cleanup",
|
|
114932
114200
|
create: (context) => ({ NewExpression(node) {
|
|
114933
114201
|
const provenance = getApiReferenceProvenance(node.callee, context.scopes);
|
|
114934
|
-
if (!provenance || !RENDERER_CONSTRUCTORS.has(provenance.apiName) || !isThreeModuleSource
|
|
114202
|
+
if (!provenance || !RENDERER_CONSTRUCTORS.has(provenance.apiName) || !isThreeModuleSource(provenance.moduleSource)) return;
|
|
114935
114203
|
const analysis = analyzeOwnedLifecycleResource(node, context, { isBorrowedReference: (reference) => isRetainedByUnusedLocalReactRef(reference, context.scopes) });
|
|
114936
114204
|
if (!analysis || analysis.hasUnknownOwnershipTransfer || isRendererSuppliedToR3fCanvas(analysis, context)) return;
|
|
114937
114205
|
const disposeCleanup = analyzeOwnedLifecycleCleanup(analysis, context, (cleanupFunction) => functionInvokesOwnedResourceMethod(cleanupFunction, analysis, "dispose", context.scopes));
|
|
@@ -114953,124 +114221,6 @@ const threeRequireRendererCleanup = defineRule({
|
|
|
114953
114221
|
} })
|
|
114954
114222
|
});
|
|
114955
114223
|
//#endregion
|
|
114956
|
-
//#region src/plugin/rules/r3f/three-tsl-no-js-uniform-branch.ts
|
|
114957
|
-
const TSL_MODULES = new Set(["three/tsl", "three/webgpu"]);
|
|
114958
|
-
const expressionReferencesTslUniformValue = (expression, scopes, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
|
|
114959
|
-
let referencesUniformValue = false;
|
|
114960
|
-
walkAst(expression, (candidate) => {
|
|
114961
|
-
if (referencesUniformValue) return false;
|
|
114962
|
-
if (isNodeOfType(candidate, "MemberExpression") && getStaticPropertyName(candidate) === "value" && resolvesToTslUniform(candidate.object, scopes)) {
|
|
114963
|
-
referencesUniformValue = true;
|
|
114964
|
-
return false;
|
|
114965
|
-
}
|
|
114966
|
-
if (!isNodeOfType(candidate, "Identifier")) return;
|
|
114967
|
-
const symbol = scopes.symbolFor(candidate);
|
|
114968
|
-
if (symbol?.kind !== "const" || !symbol.initializer || visitedSymbolIds.has(symbol.id) || symbol.references.some((reference) => reference.flag !== "read")) return;
|
|
114969
|
-
visitedSymbolIds.add(symbol.id);
|
|
114970
|
-
if (expressionReferencesTslUniformValue(symbol.initializer, scopes, visitedSymbolIds)) {
|
|
114971
|
-
referencesUniformValue = true;
|
|
114972
|
-
return false;
|
|
114973
|
-
}
|
|
114974
|
-
});
|
|
114975
|
-
return referencesUniformValue;
|
|
114976
|
-
};
|
|
114977
|
-
const threeTslNoJsUniformBranch = defineRule({
|
|
114978
|
-
id: "three-tsl-no-js-uniform-branch",
|
|
114979
|
-
title: "JavaScript branch reads a TSL uniform value",
|
|
114980
|
-
category: "Correctness",
|
|
114981
|
-
severity: "warn",
|
|
114982
|
-
recommendation: "Express uniform-dependent shader control flow with TSL If, select, or Loop nodes so it runs on the GPU",
|
|
114983
|
-
create: (context) => ({ CallExpression(node) {
|
|
114984
|
-
if (!isApiCallFromModules(node, "Fn", TSL_MODULES, context.scopes)) return;
|
|
114985
|
-
const callbackArgument = node.arguments[0];
|
|
114986
|
-
if (!callbackArgument || isNodeOfType(callbackArgument, "SpreadElement")) return;
|
|
114987
|
-
const callback = resolveExactLocalFunction(callbackArgument, context.scopes);
|
|
114988
|
-
if (!isFunctionLike$1(callback)) return;
|
|
114989
|
-
const reportedTests = /* @__PURE__ */ new Set();
|
|
114990
|
-
walkFunctionExecution(callback, context.scopes, (candidate) => {
|
|
114991
|
-
const controlFlowTest = getControlFlowTest(candidate);
|
|
114992
|
-
if (!controlFlowTest || !expressionReferencesTslUniformValue(controlFlowTest, context.scopes)) return;
|
|
114993
|
-
for (const reportedTest of reportedTests) if (isAstDescendant(controlFlowTest, reportedTest)) return;
|
|
114994
|
-
reportedTests.add(controlFlowTest);
|
|
114995
|
-
context.report({
|
|
114996
|
-
node: controlFlowTest,
|
|
114997
|
-
message: "This JavaScript branch reads a TSL uniform while the shader graph is built, so later uniform changes cannot change the branch. Use TSL control flow"
|
|
114998
|
-
});
|
|
114999
|
-
});
|
|
115000
|
-
} })
|
|
115001
|
-
});
|
|
115002
|
-
//#endregion
|
|
115003
|
-
//#region src/plugin/rules/r3f/utils/program-constructs-three-webgpu-renderer.ts
|
|
115004
|
-
const programConstructsThreeWebgpuRenderer = (program, scopes) => {
|
|
115005
|
-
let doesConstructRenderer = false;
|
|
115006
|
-
walkAst(program, (candidate) => {
|
|
115007
|
-
if (!doesConstructRenderer && isNodeOfType(candidate, "NewExpression") && getThreeConstructorName(candidate, scopes) === "WebGPURenderer") {
|
|
115008
|
-
doesConstructRenderer = true;
|
|
115009
|
-
return false;
|
|
115010
|
-
}
|
|
115011
|
-
});
|
|
115012
|
-
return doesConstructRenderer;
|
|
115013
|
-
};
|
|
115014
|
-
//#endregion
|
|
115015
|
-
//#region src/plugin/rules/r3f/three-webgpu-no-legacy-effect-composer.ts
|
|
115016
|
-
const threeWebgpuNoLegacyEffectComposer = defineRule({
|
|
115017
|
-
id: "three-webgpu-no-legacy-effect-composer",
|
|
115018
|
-
title: "Legacy EffectComposer used with WebGPURenderer",
|
|
115019
|
-
category: "Correctness",
|
|
115020
|
-
severity: "error",
|
|
115021
|
-
recommendation: "Use WebGPURenderer's node-based post-processing pipeline instead of the legacy WebGL EffectComposer",
|
|
115022
|
-
create: (context) => {
|
|
115023
|
-
let constructsWebgpuRenderer = false;
|
|
115024
|
-
return {
|
|
115025
|
-
Program(node) {
|
|
115026
|
-
constructsWebgpuRenderer = programConstructsThreeWebgpuRenderer(node, context.scopes);
|
|
115027
|
-
},
|
|
115028
|
-
NewExpression(node) {
|
|
115029
|
-
if (!constructsWebgpuRenderer || getThreeConstructorName(node, context.scopes) !== "EffectComposer") return;
|
|
115030
|
-
context.report({
|
|
115031
|
-
node,
|
|
115032
|
-
message: "Legacy EffectComposer does not support Three.js WebGPURenderer. Build post-processing with the renderer's node-based pipeline"
|
|
115033
|
-
});
|
|
115034
|
-
}
|
|
115035
|
-
};
|
|
115036
|
-
}
|
|
115037
|
-
});
|
|
115038
|
-
//#endregion
|
|
115039
|
-
//#region src/plugin/rules/r3f/three-webgpu-no-legacy-material-api.ts
|
|
115040
|
-
const LEGACY_SHADER_MATERIAL_NAMES = new Set(["RawShaderMaterial", "ShaderMaterial"]);
|
|
115041
|
-
const threeWebgpuNoLegacyMaterialApi = defineRule({
|
|
115042
|
-
id: "three-webgpu-no-legacy-material-api",
|
|
115043
|
-
title: "Legacy material API used with WebGPURenderer",
|
|
115044
|
-
category: "Correctness",
|
|
115045
|
-
severity: "error",
|
|
115046
|
-
recommendation: "Use Three.js node materials and TSL for custom shaders rendered by WebGPURenderer",
|
|
115047
|
-
create: (context) => {
|
|
115048
|
-
let constructsWebgpuRenderer = false;
|
|
115049
|
-
return {
|
|
115050
|
-
Program(node) {
|
|
115051
|
-
constructsWebgpuRenderer = programConstructsThreeWebgpuRenderer(node, context.scopes);
|
|
115052
|
-
},
|
|
115053
|
-
NewExpression(node) {
|
|
115054
|
-
const constructorName = getThreeConstructorName(node, context.scopes);
|
|
115055
|
-
if (!constructsWebgpuRenderer || !constructorName) return;
|
|
115056
|
-
if (!LEGACY_SHADER_MATERIAL_NAMES.has(constructorName)) return;
|
|
115057
|
-
context.report({
|
|
115058
|
-
node,
|
|
115059
|
-
message: "ShaderMaterial and RawShaderMaterial are not supported by Three.js WebGPURenderer. Build this shader with a node material and TSL"
|
|
115060
|
-
});
|
|
115061
|
-
},
|
|
115062
|
-
AssignmentExpression(node) {
|
|
115063
|
-
if (!constructsWebgpuRenderer || !isNodeOfType(node.left, "MemberExpression") || getStaticPropertyName(node.left) !== "onBeforeCompile") return;
|
|
115064
|
-
if (!getThreeConstructorName(node.left.object, context.scopes)?.endsWith("Material")) return;
|
|
115065
|
-
context.report({
|
|
115066
|
-
node,
|
|
115067
|
-
message: "onBeforeCompile patches WebGL shader source and is not supported by Three.js WebGPURenderer. Use a node material and TSL"
|
|
115068
|
-
});
|
|
115069
|
-
}
|
|
115070
|
-
};
|
|
115071
|
-
}
|
|
115072
|
-
});
|
|
115073
|
-
//#endregion
|
|
115074
114224
|
//#region src/plugin/rules/security-scan/unsafe-json-in-html.ts
|
|
115075
114225
|
const SINK_JSON_STRINGIFY_PATTERNS = [/dangerouslySetInnerHTML\s*=\s*\{\{\s*__html\s*:[\s\S]{0,300}?\bJSON\.stringify\s*\(/gi, /<script\b[^>]*>(?:(?!<\/script>)[\s\S]){0,300}?\bJSON\.stringify\s*\(/gi];
|
|
115076
114226
|
const INLINE_SCRIPT_PATTERN_INDEX = 1;
|
|
@@ -115828,44 +114978,6 @@ const webAnimationOffsetsValid = defineRule({
|
|
|
115828
114978
|
} })
|
|
115829
114979
|
});
|
|
115830
114980
|
//#endregion
|
|
115831
|
-
//#region src/plugin/rules/webgl/webgl-no-sync-readback-in-animation-loop.ts
|
|
115832
|
-
const getBlockingReadbackKind = (call, context) => {
|
|
115833
|
-
if (!isNodeOfType(call.callee, "MemberExpression")) return null;
|
|
115834
|
-
const methodName = getStaticPropertyName(call.callee);
|
|
115835
|
-
if (methodName === "readRenderTargetPixels" && isThreeRendererReference(call.callee.object, context.scopes)) return "three";
|
|
115836
|
-
if (!isWebglContextReference(call.callee.object, context.scopes)) return null;
|
|
115837
|
-
if (methodName === "finish") return "finish";
|
|
115838
|
-
let destinationArgumentIndex = null;
|
|
115839
|
-
if (methodName === "readPixels") destinationArgumentIndex = 6;
|
|
115840
|
-
else if (methodName === "getBufferSubData") destinationArgumentIndex = 2;
|
|
115841
|
-
if (destinationArgumentIndex === null) return null;
|
|
115842
|
-
const destination = call.arguments[destinationArgumentIndex];
|
|
115843
|
-
return destination && !isNodeOfType(destination, "SpreadElement") && isCpuTypedArray(destination, context.scopes) ? "raw" : null;
|
|
115844
|
-
};
|
|
115845
|
-
const webglNoSyncReadbackInAnimationLoop = defineRule({
|
|
115846
|
-
id: "webgl-no-sync-readback-in-animation-loop",
|
|
115847
|
-
title: "Synchronous GPU readback inside animation loop",
|
|
115848
|
-
severity: "warn",
|
|
115849
|
-
recommendation: "Move GPU readback to a discrete or asynchronous path and reuse the latest completed result during frames",
|
|
115850
|
-
create: (context) => {
|
|
115851
|
-
const analyzedCallbacks = /* @__PURE__ */ new Set();
|
|
115852
|
-
return { CallExpression(node) {
|
|
115853
|
-
const callback = resolveThreeAnimationLoopCallback(node, context.scopes) ?? resolveRecursiveAnimationFrameCallback(node, context.scopes);
|
|
115854
|
-
if (!callback || analyzedCallbacks.has(callback)) return;
|
|
115855
|
-
analyzedCallbacks.add(callback);
|
|
115856
|
-
walkFunctionExecution(callback, context.scopes, (candidate, isConditionallyExecuted) => {
|
|
115857
|
-
if (!isNodeOfType(candidate, "CallExpression") || isConditionallyExecuted) return;
|
|
115858
|
-
const readbackKind = getBlockingReadbackKind(candidate, context);
|
|
115859
|
-
if (!readbackKind) return;
|
|
115860
|
-
context.report({
|
|
115861
|
-
node: candidate,
|
|
115862
|
-
message: readbackKind === "finish" ? "finish blocks the calling thread until queued GPU work completes. Synchronize outside the animation loop" : "Synchronous GPU readback can stall the frame until prior GPU work completes. Use an asynchronous or event-driven readback path"
|
|
115863
|
-
});
|
|
115864
|
-
});
|
|
115865
|
-
} };
|
|
115866
|
-
}
|
|
115867
|
-
});
|
|
115868
|
-
//#endregion
|
|
115869
114981
|
//#region src/plugin/rules/security-scan/webhook-signature-risk.ts
|
|
115870
114982
|
const WEBHOOK_HANDLER_PATTERN = /(?:^|\/)[^/]*webhook[^/]*\/|(?:^|\/)[^/]*webhook[^/]*\.[cm]?[jt]s$|\bwebhook\b/i;
|
|
115871
114983
|
const WEBHOOK_ENTRYPOINT_PATTERN = /\b(?:export\s+(?:async\s+)?function\s+POST|export\s+const\s+(?:POST|handler|webhook)|webhookHandler|webhookRoute)\b/i;
|
|
@@ -127685,171 +126797,6 @@ const reactDoctorRules = [
|
|
|
127685
126797
|
tags: [...new Set(["security-scan", ...tenantStaticProxyRisk.tags ?? []])]
|
|
127686
126798
|
}
|
|
127687
126799
|
},
|
|
127688
|
-
{
|
|
127689
|
-
key: "react-doctor/three-cap-device-pixel-ratio",
|
|
127690
|
-
id: "three-cap-device-pixel-ratio",
|
|
127691
|
-
source: "react-doctor",
|
|
127692
|
-
originallyExternal: false,
|
|
127693
|
-
rule: {
|
|
127694
|
-
...threeCapDevicePixelRatio,
|
|
127695
|
-
framework: "global",
|
|
127696
|
-
category: "Performance",
|
|
127697
|
-
tags: [...new Set([
|
|
127698
|
-
"three",
|
|
127699
|
-
"webgl",
|
|
127700
|
-
...threeCapDevicePixelRatio.tags ?? []
|
|
127701
|
-
])],
|
|
127702
|
-
requires: [...new Set(["three", ...threeCapDevicePixelRatio.requires ?? []])]
|
|
127703
|
-
}
|
|
127704
|
-
},
|
|
127705
|
-
{
|
|
127706
|
-
key: "react-doctor/three-limit-shadowed-point-lights",
|
|
127707
|
-
id: "three-limit-shadowed-point-lights",
|
|
127708
|
-
source: "react-doctor",
|
|
127709
|
-
originallyExternal: false,
|
|
127710
|
-
rule: {
|
|
127711
|
-
...threeLimitShadowedPointLights,
|
|
127712
|
-
framework: "global",
|
|
127713
|
-
category: "Performance",
|
|
127714
|
-
tags: [...new Set([
|
|
127715
|
-
"three",
|
|
127716
|
-
"webgl",
|
|
127717
|
-
...threeLimitShadowedPointLights.tags ?? []
|
|
127718
|
-
])],
|
|
127719
|
-
requires: [...new Set(["three", ...threeLimitShadowedPointLights.requires ?? []])]
|
|
127720
|
-
}
|
|
127721
|
-
},
|
|
127722
|
-
{
|
|
127723
|
-
key: "react-doctor/three-no-allocation-in-pointer-move",
|
|
127724
|
-
id: "three-no-allocation-in-pointer-move",
|
|
127725
|
-
source: "react-doctor",
|
|
127726
|
-
originallyExternal: false,
|
|
127727
|
-
rule: {
|
|
127728
|
-
...threeNoAllocationInPointerMove,
|
|
127729
|
-
framework: "global",
|
|
127730
|
-
category: "Performance",
|
|
127731
|
-
tags: [...new Set([
|
|
127732
|
-
"three",
|
|
127733
|
-
"webgl",
|
|
127734
|
-
...threeNoAllocationInPointerMove.tags ?? []
|
|
127735
|
-
])],
|
|
127736
|
-
requires: [...new Set(["three", ...threeNoAllocationInPointerMove.requires ?? []])]
|
|
127737
|
-
}
|
|
127738
|
-
},
|
|
127739
|
-
{
|
|
127740
|
-
key: "react-doctor/three-no-async-animation-loop",
|
|
127741
|
-
id: "three-no-async-animation-loop",
|
|
127742
|
-
source: "react-doctor",
|
|
127743
|
-
originallyExternal: false,
|
|
127744
|
-
rule: {
|
|
127745
|
-
...threeNoAsyncAnimationLoop,
|
|
127746
|
-
framework: "global",
|
|
127747
|
-
category: "Bugs",
|
|
127748
|
-
tags: [...new Set([
|
|
127749
|
-
"three",
|
|
127750
|
-
"webgl",
|
|
127751
|
-
...threeNoAsyncAnimationLoop.tags ?? []
|
|
127752
|
-
])],
|
|
127753
|
-
requires: [...new Set(["three", ...threeNoAsyncAnimationLoop.requires ?? []])]
|
|
127754
|
-
}
|
|
127755
|
-
},
|
|
127756
|
-
{
|
|
127757
|
-
key: "react-doctor/three-no-clone-in-animation-loop",
|
|
127758
|
-
id: "three-no-clone-in-animation-loop",
|
|
127759
|
-
source: "react-doctor",
|
|
127760
|
-
originallyExternal: false,
|
|
127761
|
-
rule: {
|
|
127762
|
-
...threeNoCloneInAnimationLoop,
|
|
127763
|
-
framework: "global",
|
|
127764
|
-
category: "Performance",
|
|
127765
|
-
tags: [...new Set([
|
|
127766
|
-
"three",
|
|
127767
|
-
"webgl",
|
|
127768
|
-
...threeNoCloneInAnimationLoop.tags ?? []
|
|
127769
|
-
])],
|
|
127770
|
-
requires: [...new Set(["three", ...threeNoCloneInAnimationLoop.requires ?? []])]
|
|
127771
|
-
}
|
|
127772
|
-
},
|
|
127773
|
-
{
|
|
127774
|
-
key: "react-doctor/three-no-new-in-animation-loop",
|
|
127775
|
-
id: "three-no-new-in-animation-loop",
|
|
127776
|
-
source: "react-doctor",
|
|
127777
|
-
originallyExternal: false,
|
|
127778
|
-
rule: {
|
|
127779
|
-
...threeNoNewInAnimationLoop,
|
|
127780
|
-
framework: "global",
|
|
127781
|
-
category: "Performance",
|
|
127782
|
-
tags: [...new Set([
|
|
127783
|
-
"three",
|
|
127784
|
-
"webgl",
|
|
127785
|
-
...threeNoNewInAnimationLoop.tags ?? []
|
|
127786
|
-
])],
|
|
127787
|
-
requires: [...new Set(["three", ...threeNoNewInAnimationLoop.requires ?? []])]
|
|
127788
|
-
}
|
|
127789
|
-
},
|
|
127790
|
-
{
|
|
127791
|
-
key: "react-doctor/three-no-object-construction-in-render",
|
|
127792
|
-
id: "three-no-object-construction-in-render",
|
|
127793
|
-
source: "react-doctor",
|
|
127794
|
-
originallyExternal: false,
|
|
127795
|
-
rule: {
|
|
127796
|
-
...threeNoObjectConstructionInRender,
|
|
127797
|
-
framework: "global",
|
|
127798
|
-
category: "Performance",
|
|
127799
|
-
tags: [...new Set([
|
|
127800
|
-
"three",
|
|
127801
|
-
"webgl",
|
|
127802
|
-
...threeNoObjectConstructionInRender.tags ?? []
|
|
127803
|
-
])],
|
|
127804
|
-
requires: [...new Set([
|
|
127805
|
-
"react",
|
|
127806
|
-
"three",
|
|
127807
|
-
...threeNoObjectConstructionInRender.requires ?? []
|
|
127808
|
-
])]
|
|
127809
|
-
}
|
|
127810
|
-
},
|
|
127811
|
-
{
|
|
127812
|
-
key: "react-doctor/three-no-state-in-animation-loop",
|
|
127813
|
-
id: "three-no-state-in-animation-loop",
|
|
127814
|
-
source: "react-doctor",
|
|
127815
|
-
originallyExternal: false,
|
|
127816
|
-
rule: {
|
|
127817
|
-
...threeNoStateInAnimationLoop,
|
|
127818
|
-
framework: "global",
|
|
127819
|
-
category: "Performance",
|
|
127820
|
-
tags: [...new Set([
|
|
127821
|
-
"three",
|
|
127822
|
-
"webgl",
|
|
127823
|
-
...threeNoStateInAnimationLoop.tags ?? []
|
|
127824
|
-
])],
|
|
127825
|
-
requires: [...new Set([
|
|
127826
|
-
"react",
|
|
127827
|
-
"three",
|
|
127828
|
-
...threeNoStateInAnimationLoop.requires ?? []
|
|
127829
|
-
])]
|
|
127830
|
-
}
|
|
127831
|
-
},
|
|
127832
|
-
{
|
|
127833
|
-
key: "react-doctor/three-no-state-in-pointer-move",
|
|
127834
|
-
id: "three-no-state-in-pointer-move",
|
|
127835
|
-
source: "react-doctor",
|
|
127836
|
-
originallyExternal: false,
|
|
127837
|
-
rule: {
|
|
127838
|
-
...threeNoStateInPointerMove,
|
|
127839
|
-
framework: "global",
|
|
127840
|
-
category: "Performance",
|
|
127841
|
-
tags: [...new Set([
|
|
127842
|
-
"three",
|
|
127843
|
-
"webgl",
|
|
127844
|
-
...threeNoStateInPointerMove.tags ?? []
|
|
127845
|
-
])],
|
|
127846
|
-
requires: [...new Set([
|
|
127847
|
-
"react",
|
|
127848
|
-
"three",
|
|
127849
|
-
...threeNoStateInPointerMove.requires ?? []
|
|
127850
|
-
])]
|
|
127851
|
-
}
|
|
127852
|
-
},
|
|
127853
126800
|
{
|
|
127854
126801
|
key: "react-doctor/three-require-animation-mixer-cleanup",
|
|
127855
126802
|
id: "three-require-animation-mixer-cleanup",
|
|
@@ -127860,7 +126807,7 @@ const reactDoctorRules = [
|
|
|
127860
126807
|
framework: "global",
|
|
127861
126808
|
category: "Bugs",
|
|
127862
126809
|
tags: [...new Set([
|
|
127863
|
-
"
|
|
126810
|
+
"r3f",
|
|
127864
126811
|
"webgl",
|
|
127865
126812
|
...threeRequireAnimationMixerCleanup.tags ?? []
|
|
127866
126813
|
])],
|
|
@@ -127881,7 +126828,7 @@ const reactDoctorRules = [
|
|
|
127881
126828
|
framework: "global",
|
|
127882
126829
|
category: "Bugs",
|
|
127883
126830
|
tags: [...new Set([
|
|
127884
|
-
"
|
|
126831
|
+
"r3f",
|
|
127885
126832
|
"webgl",
|
|
127886
126833
|
...threeRequireControlsCleanup.tags ?? []
|
|
127887
126834
|
])],
|
|
@@ -127892,103 +126839,6 @@ const reactDoctorRules = [
|
|
|
127892
126839
|
])]
|
|
127893
126840
|
}
|
|
127894
126841
|
},
|
|
127895
|
-
{
|
|
127896
|
-
key: "react-doctor/three-require-frame-delta",
|
|
127897
|
-
id: "three-require-frame-delta",
|
|
127898
|
-
source: "react-doctor",
|
|
127899
|
-
originallyExternal: false,
|
|
127900
|
-
rule: {
|
|
127901
|
-
...threeRequireFrameDelta,
|
|
127902
|
-
framework: "global",
|
|
127903
|
-
category: "Bugs",
|
|
127904
|
-
tags: [...new Set([
|
|
127905
|
-
"three",
|
|
127906
|
-
"webgl",
|
|
127907
|
-
...threeRequireFrameDelta.tags ?? []
|
|
127908
|
-
])],
|
|
127909
|
-
requires: [...new Set(["three", ...threeRequireFrameDelta.requires ?? []])]
|
|
127910
|
-
}
|
|
127911
|
-
},
|
|
127912
|
-
{
|
|
127913
|
-
key: "react-doctor/three-require-instanced-buffer-update",
|
|
127914
|
-
id: "three-require-instanced-buffer-update",
|
|
127915
|
-
source: "react-doctor",
|
|
127916
|
-
originallyExternal: false,
|
|
127917
|
-
rule: {
|
|
127918
|
-
...threeRequireInstancedBufferUpdate,
|
|
127919
|
-
framework: "global",
|
|
127920
|
-
category: "Bugs",
|
|
127921
|
-
tags: [...new Set([
|
|
127922
|
-
"three",
|
|
127923
|
-
"webgl",
|
|
127924
|
-
...threeRequireInstancedBufferUpdate.tags ?? []
|
|
127925
|
-
])],
|
|
127926
|
-
requires: [...new Set(["three", ...threeRequireInstancedBufferUpdate.requires ?? []])]
|
|
127927
|
-
}
|
|
127928
|
-
},
|
|
127929
|
-
{
|
|
127930
|
-
key: "react-doctor/three-require-owned-geometry-cleanup",
|
|
127931
|
-
id: "three-require-owned-geometry-cleanup",
|
|
127932
|
-
source: "react-doctor",
|
|
127933
|
-
originallyExternal: false,
|
|
127934
|
-
rule: {
|
|
127935
|
-
...threeRequireOwnedGeometryCleanup,
|
|
127936
|
-
framework: "global",
|
|
127937
|
-
category: "Performance",
|
|
127938
|
-
tags: [...new Set([
|
|
127939
|
-
"three",
|
|
127940
|
-
"webgl",
|
|
127941
|
-
...threeRequireOwnedGeometryCleanup.tags ?? []
|
|
127942
|
-
])],
|
|
127943
|
-
requires: [...new Set([
|
|
127944
|
-
"react",
|
|
127945
|
-
"three",
|
|
127946
|
-
...threeRequireOwnedGeometryCleanup.requires ?? []
|
|
127947
|
-
])]
|
|
127948
|
-
}
|
|
127949
|
-
},
|
|
127950
|
-
{
|
|
127951
|
-
key: "react-doctor/three-require-owned-material-cleanup",
|
|
127952
|
-
id: "three-require-owned-material-cleanup",
|
|
127953
|
-
source: "react-doctor",
|
|
127954
|
-
originallyExternal: false,
|
|
127955
|
-
rule: {
|
|
127956
|
-
...threeRequireOwnedMaterialCleanup,
|
|
127957
|
-
framework: "global",
|
|
127958
|
-
category: "Performance",
|
|
127959
|
-
tags: [...new Set([
|
|
127960
|
-
"three",
|
|
127961
|
-
"webgl",
|
|
127962
|
-
...threeRequireOwnedMaterialCleanup.tags ?? []
|
|
127963
|
-
])],
|
|
127964
|
-
requires: [...new Set([
|
|
127965
|
-
"react",
|
|
127966
|
-
"three",
|
|
127967
|
-
...threeRequireOwnedMaterialCleanup.requires ?? []
|
|
127968
|
-
])]
|
|
127969
|
-
}
|
|
127970
|
-
},
|
|
127971
|
-
{
|
|
127972
|
-
key: "react-doctor/three-require-owned-texture-cleanup",
|
|
127973
|
-
id: "three-require-owned-texture-cleanup",
|
|
127974
|
-
source: "react-doctor",
|
|
127975
|
-
originallyExternal: false,
|
|
127976
|
-
rule: {
|
|
127977
|
-
...threeRequireOwnedTextureCleanup,
|
|
127978
|
-
framework: "global",
|
|
127979
|
-
category: "Performance",
|
|
127980
|
-
tags: [...new Set([
|
|
127981
|
-
"three",
|
|
127982
|
-
"webgl",
|
|
127983
|
-
...threeRequireOwnedTextureCleanup.tags ?? []
|
|
127984
|
-
])],
|
|
127985
|
-
requires: [...new Set([
|
|
127986
|
-
"react",
|
|
127987
|
-
"three",
|
|
127988
|
-
...threeRequireOwnedTextureCleanup.requires ?? []
|
|
127989
|
-
])]
|
|
127990
|
-
}
|
|
127991
|
-
},
|
|
127992
126842
|
{
|
|
127993
126843
|
key: "react-doctor/three-require-postprocessing-cleanup",
|
|
127994
126844
|
id: "three-require-postprocessing-cleanup",
|
|
@@ -127999,7 +126849,7 @@ const reactDoctorRules = [
|
|
|
127999
126849
|
framework: "global",
|
|
128000
126850
|
category: "Bugs",
|
|
128001
126851
|
tags: [...new Set([
|
|
128002
|
-
"
|
|
126852
|
+
"r3f",
|
|
128003
126853
|
"webgl",
|
|
128004
126854
|
...threeRequirePostprocessingCleanup.tags ?? []
|
|
128005
126855
|
])],
|
|
@@ -128010,23 +126860,6 @@ const reactDoctorRules = [
|
|
|
128010
126860
|
])]
|
|
128011
126861
|
}
|
|
128012
126862
|
},
|
|
128013
|
-
{
|
|
128014
|
-
key: "react-doctor/three-require-projection-matrix-update",
|
|
128015
|
-
id: "three-require-projection-matrix-update",
|
|
128016
|
-
source: "react-doctor",
|
|
128017
|
-
originallyExternal: false,
|
|
128018
|
-
rule: {
|
|
128019
|
-
...threeRequireProjectionMatrixUpdate,
|
|
128020
|
-
framework: "global",
|
|
128021
|
-
category: "Bugs",
|
|
128022
|
-
tags: [...new Set([
|
|
128023
|
-
"three",
|
|
128024
|
-
"webgl",
|
|
128025
|
-
...threeRequireProjectionMatrixUpdate.tags ?? []
|
|
128026
|
-
])],
|
|
128027
|
-
requires: [...new Set(["three", ...threeRequireProjectionMatrixUpdate.requires ?? []])]
|
|
128028
|
-
}
|
|
128029
|
-
},
|
|
128030
126863
|
{
|
|
128031
126864
|
key: "react-doctor/three-require-render-target-cleanup",
|
|
128032
126865
|
id: "three-require-render-target-cleanup",
|
|
@@ -128037,7 +126870,7 @@ const reactDoctorRules = [
|
|
|
128037
126870
|
framework: "global",
|
|
128038
126871
|
category: "Bugs",
|
|
128039
126872
|
tags: [...new Set([
|
|
128040
|
-
"
|
|
126873
|
+
"r3f",
|
|
128041
126874
|
"webgl",
|
|
128042
126875
|
...threeRequireRenderTargetCleanup.tags ?? []
|
|
128043
126876
|
])],
|
|
@@ -128058,7 +126891,7 @@ const reactDoctorRules = [
|
|
|
128058
126891
|
framework: "global",
|
|
128059
126892
|
category: "Bugs",
|
|
128060
126893
|
tags: [...new Set([
|
|
128061
|
-
"
|
|
126894
|
+
"r3f",
|
|
128062
126895
|
"webgl",
|
|
128063
126896
|
...threeRequireRendererCleanup.tags ?? []
|
|
128064
126897
|
])],
|
|
@@ -128069,57 +126902,6 @@ const reactDoctorRules = [
|
|
|
128069
126902
|
])]
|
|
128070
126903
|
}
|
|
128071
126904
|
},
|
|
128072
|
-
{
|
|
128073
|
-
key: "react-doctor/three-tsl-no-js-uniform-branch",
|
|
128074
|
-
id: "three-tsl-no-js-uniform-branch",
|
|
128075
|
-
source: "react-doctor",
|
|
128076
|
-
originallyExternal: false,
|
|
128077
|
-
rule: {
|
|
128078
|
-
...threeTslNoJsUniformBranch,
|
|
128079
|
-
framework: "global",
|
|
128080
|
-
category: "Bugs",
|
|
128081
|
-
tags: [...new Set([
|
|
128082
|
-
"three",
|
|
128083
|
-
"webgl",
|
|
128084
|
-
...threeTslNoJsUniformBranch.tags ?? []
|
|
128085
|
-
])],
|
|
128086
|
-
requires: [...new Set(["three", ...threeTslNoJsUniformBranch.requires ?? []])]
|
|
128087
|
-
}
|
|
128088
|
-
},
|
|
128089
|
-
{
|
|
128090
|
-
key: "react-doctor/three-webgpu-no-legacy-effect-composer",
|
|
128091
|
-
id: "three-webgpu-no-legacy-effect-composer",
|
|
128092
|
-
source: "react-doctor",
|
|
128093
|
-
originallyExternal: false,
|
|
128094
|
-
rule: {
|
|
128095
|
-
...threeWebgpuNoLegacyEffectComposer,
|
|
128096
|
-
framework: "global",
|
|
128097
|
-
category: "Bugs",
|
|
128098
|
-
tags: [...new Set([
|
|
128099
|
-
"three",
|
|
128100
|
-
"webgl",
|
|
128101
|
-
...threeWebgpuNoLegacyEffectComposer.tags ?? []
|
|
128102
|
-
])],
|
|
128103
|
-
requires: [...new Set(["three", ...threeWebgpuNoLegacyEffectComposer.requires ?? []])]
|
|
128104
|
-
}
|
|
128105
|
-
},
|
|
128106
|
-
{
|
|
128107
|
-
key: "react-doctor/three-webgpu-no-legacy-material-api",
|
|
128108
|
-
id: "three-webgpu-no-legacy-material-api",
|
|
128109
|
-
source: "react-doctor",
|
|
128110
|
-
originallyExternal: false,
|
|
128111
|
-
rule: {
|
|
128112
|
-
...threeWebgpuNoLegacyMaterialApi,
|
|
128113
|
-
framework: "global",
|
|
128114
|
-
category: "Bugs",
|
|
128115
|
-
tags: [...new Set([
|
|
128116
|
-
"three",
|
|
128117
|
-
"webgl",
|
|
128118
|
-
...threeWebgpuNoLegacyMaterialApi.tags ?? []
|
|
128119
|
-
])],
|
|
128120
|
-
requires: [...new Set(["three", ...threeWebgpuNoLegacyMaterialApi.requires ?? []])]
|
|
128121
|
-
}
|
|
128122
|
-
},
|
|
128123
126905
|
{
|
|
128124
126906
|
key: "react-doctor/unsafe-json-in-html",
|
|
128125
126907
|
id: "unsafe-json-in-html",
|
|
@@ -128225,18 +127007,6 @@ const reactDoctorRules = [
|
|
|
128225
127007
|
category: "Bugs"
|
|
128226
127008
|
}
|
|
128227
127009
|
},
|
|
128228
|
-
{
|
|
128229
|
-
key: "react-doctor/webgl-no-sync-readback-in-animation-loop",
|
|
128230
|
-
id: "webgl-no-sync-readback-in-animation-loop",
|
|
128231
|
-
source: "react-doctor",
|
|
128232
|
-
originallyExternal: false,
|
|
128233
|
-
rule: {
|
|
128234
|
-
...webglNoSyncReadbackInAnimationLoop,
|
|
128235
|
-
framework: "global",
|
|
128236
|
-
category: "Performance",
|
|
128237
|
-
tags: [...new Set(["webgl", ...webglNoSyncReadbackInAnimationLoop.tags ?? []])]
|
|
128238
|
-
}
|
|
128239
|
-
},
|
|
128240
127010
|
{
|
|
128241
127011
|
key: "react-doctor/webhook-signature-risk",
|
|
128242
127012
|
id: "webhook-signature-risk",
|