oxlint-plugin-react-doctor 0.5.0 → 0.5.1-dev.b819e5f
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +100 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -432,6 +432,7 @@ const SETTER_PATTERN = /^set[A-Z]/;
|
|
|
432
432
|
const RENDER_FUNCTION_PATTERN = /^render[A-Z]/;
|
|
433
433
|
const UPPERCASE_PATTERN = /^[A-Z]/;
|
|
434
434
|
const REACT_HANDLER_PROP_PATTERN = /^on[A-Z]/;
|
|
435
|
+
const HANDLER_FUNCTION_NAME_PATTERN = /^(?:on|handle)[A-Z]/;
|
|
435
436
|
const EFFECT_HOOK_NAMES$1 = new Set(["useEffect", "useLayoutEffect"]);
|
|
436
437
|
const HOOKS_WITH_DEPS = new Set([
|
|
437
438
|
"useEffect",
|
|
@@ -24499,6 +24500,54 @@ const UTILITY_FILE_BASENAMES = new Set([
|
|
|
24499
24500
|
"defaults.tsx",
|
|
24500
24501
|
"defaults.jsx"
|
|
24501
24502
|
]);
|
|
24503
|
+
const ROUTE_FACTORY_CALLEE_NAMES = new Set([
|
|
24504
|
+
...TANSTACK_ROUTE_CREATION_FUNCTIONS,
|
|
24505
|
+
"createLazyFileRoute",
|
|
24506
|
+
"createLazyRoute",
|
|
24507
|
+
"createAPIFileRoute",
|
|
24508
|
+
"createServerFileRoute",
|
|
24509
|
+
"createServerRootRoute",
|
|
24510
|
+
"createServerRoute",
|
|
24511
|
+
"createBrowserRouter",
|
|
24512
|
+
"createHashRouter",
|
|
24513
|
+
"createMemoryRouter",
|
|
24514
|
+
"createStaticRouter",
|
|
24515
|
+
"createRouter"
|
|
24516
|
+
]);
|
|
24517
|
+
const ROUTE_MODULE_ALLOWED_EXPORT_NAMES = new Set([
|
|
24518
|
+
"loader",
|
|
24519
|
+
"clientLoader",
|
|
24520
|
+
"action",
|
|
24521
|
+
"clientAction",
|
|
24522
|
+
"headers",
|
|
24523
|
+
"meta",
|
|
24524
|
+
"links",
|
|
24525
|
+
"handle",
|
|
24526
|
+
"shouldRevalidate",
|
|
24527
|
+
"middleware",
|
|
24528
|
+
"unstable_middleware",
|
|
24529
|
+
"getServerSideProps",
|
|
24530
|
+
"getStaticProps",
|
|
24531
|
+
"getStaticPaths",
|
|
24532
|
+
"getInitialProps",
|
|
24533
|
+
"reportWebVitals",
|
|
24534
|
+
"metadata",
|
|
24535
|
+
"generateMetadata",
|
|
24536
|
+
"generateStaticParams",
|
|
24537
|
+
"generateImageMetadata",
|
|
24538
|
+
"generateSitemaps",
|
|
24539
|
+
"viewport",
|
|
24540
|
+
"generateViewport",
|
|
24541
|
+
"revalidate",
|
|
24542
|
+
"dynamic",
|
|
24543
|
+
"dynamicParams",
|
|
24544
|
+
"fetchCache",
|
|
24545
|
+
"runtime",
|
|
24546
|
+
"preferredRegion",
|
|
24547
|
+
"maxDuration",
|
|
24548
|
+
"experimental_ppr",
|
|
24549
|
+
"unstable_settings"
|
|
24550
|
+
]);
|
|
24502
24551
|
//#endregion
|
|
24503
24552
|
//#region src/plugin/rules/react-builtins/only-export-components.ts
|
|
24504
24553
|
const NAMED_EXPORT_MESSAGE = "This file exports non-components, so Fast Refresh can't safely preserve component state.";
|
|
@@ -24542,6 +24591,18 @@ const isReactCreateContext = (initializer) => {
|
|
|
24542
24591
|
if (isNodeOfType(callee, "MemberExpression") && isNodeOfType(callee.property, "Identifier") && callee.property.name === "createContext") return true;
|
|
24543
24592
|
return false;
|
|
24544
24593
|
};
|
|
24594
|
+
const isRouteFactoryName = (name) => ROUTE_FACTORY_CALLEE_NAMES.has(name);
|
|
24595
|
+
const isRouteFactoryCall = (expression) => {
|
|
24596
|
+
let currentCall = expression;
|
|
24597
|
+
while (isNodeOfType(currentCall, "CallExpression")) {
|
|
24598
|
+
const callee = currentCall.callee;
|
|
24599
|
+
if (isNodeOfType(callee, "Identifier") && isRouteFactoryName(callee.name)) return true;
|
|
24600
|
+
if (isNodeOfType(callee, "MemberExpression") && isNodeOfType(callee.property, "Identifier") && isRouteFactoryName(callee.property.name)) return true;
|
|
24601
|
+
if (!isNodeOfType(callee, "CallExpression")) return false;
|
|
24602
|
+
currentCall = callee;
|
|
24603
|
+
}
|
|
24604
|
+
return false;
|
|
24605
|
+
};
|
|
24545
24606
|
const isReactHocName = (name, state) => state.customHocs.has(name);
|
|
24546
24607
|
const isHocCallee = (callee, state) => {
|
|
24547
24608
|
if (isNodeOfType(callee, "Identifier")) return isReactHocName(callee.name, state);
|
|
@@ -24575,10 +24636,12 @@ const isReactComponentInitializer = (expression, state) => {
|
|
|
24575
24636
|
const classifyExport = (name, reportNode, isFunction, initializer, state) => {
|
|
24576
24637
|
if (initializer) {
|
|
24577
24638
|
const expression = skipTsExpression(initializer);
|
|
24639
|
+
if (isRouteFactoryCall(expression)) return { kind: "react-component" };
|
|
24578
24640
|
if (isNodeOfType(expression, "CallExpression") && isHocCallee(expression.callee, state) && expression.arguments.length > 0 && isReactComponentName(name)) return { kind: "react-component" };
|
|
24579
24641
|
if (isNodeOfType(expression, "ConditionalExpression") && isReactComponentName(name) && isReactComponentInitializer(expression.consequent, state) && isReactComponentInitializer(expression.alternate, state)) return { kind: "react-component" };
|
|
24580
24642
|
}
|
|
24581
24643
|
if (state.allowExportNames.has(name)) return { kind: "allowed" };
|
|
24644
|
+
if (ROUTE_MODULE_ALLOWED_EXPORT_NAMES.has(name)) return { kind: "allowed" };
|
|
24582
24645
|
if (/^use[A-Z]/.test(name)) return { kind: "allowed" };
|
|
24583
24646
|
if (state.allowConstantExport && initializer) {
|
|
24584
24647
|
const expression = skipTsExpression(initializer);
|
|
@@ -24724,6 +24787,10 @@ const onlyExportComponents = defineRule({
|
|
|
24724
24787
|
continue;
|
|
24725
24788
|
}
|
|
24726
24789
|
if (isNodeOfType(stripped, "CallExpression")) {
|
|
24790
|
+
if (isRouteFactoryCall(stripped)) {
|
|
24791
|
+
hasReactExport = true;
|
|
24792
|
+
continue;
|
|
24793
|
+
}
|
|
24727
24794
|
const isHoc = isHocCallee(stripped.callee, state);
|
|
24728
24795
|
const firstArg = stripped.arguments[0];
|
|
24729
24796
|
const firstArgIsValid = Boolean(firstArg) && (() => {
|
|
@@ -34290,6 +34357,9 @@ const tanstackStartNoNavigateInRender = defineRule({
|
|
|
34290
34357
|
let eventHandlerDepth = 0;
|
|
34291
34358
|
const isDeferredHookCall = (node) => isHookCall$1(node, EFFECT_HOOK_NAMES$1) || isHookCall$1(node, "useCallback") || isHookCall$1(node, "useMemo");
|
|
34292
34359
|
const isEventHandlerAttribute = (node) => isNodeOfType(node, "JSXAttribute") && isNodeOfType(node.name, "JSXIdentifier") && typeof node.name.name === "string" && node.name.name.startsWith("on") && UPPERCASE_PATTERN.test(node.name.name.charAt(2));
|
|
34360
|
+
const isEventHandlerProperty = (node) => isNodeOfType(node, "Property") && isFunctionLike$2(node.value) && (isNodeOfType(node.key, "Identifier") && typeof node.key.name === "string" && REACT_HANDLER_PROP_PATTERN.test(node.key.name) || isNodeOfType(node.key, "Literal") && typeof node.key.value === "string" && REACT_HANDLER_PROP_PATTERN.test(node.key.value));
|
|
34361
|
+
const isHandlerNamedVariableDeclarator = (node) => isNodeOfType(node, "VariableDeclarator") && isNodeOfType(node.id, "Identifier") && typeof node.id.name === "string" && HANDLER_FUNCTION_NAME_PATTERN.test(node.id.name) && isFunctionLike$2(node.init);
|
|
34362
|
+
const isHandlerNamedFunctionDeclaration = (node) => isNodeOfType(node, "FunctionDeclaration") && isNodeOfType(node.id, "Identifier") && typeof node.id.name === "string" && HANDLER_FUNCTION_NAME_PATTERN.test(node.id.name);
|
|
34293
34363
|
return {
|
|
34294
34364
|
CallExpression(node) {
|
|
34295
34365
|
const filename = normalizeFilename$1(context.filename ?? "");
|
|
@@ -34315,6 +34385,36 @@ const tanstackStartNoNavigateInRender = defineRule({
|
|
|
34315
34385
|
const filename = normalizeFilename$1(context.filename ?? "");
|
|
34316
34386
|
if (!TANSTACK_ROUTE_FILE_PATTERN.test(filename)) return;
|
|
34317
34387
|
if (isEventHandlerAttribute(node)) eventHandlerDepth = Math.max(0, eventHandlerDepth - 1);
|
|
34388
|
+
},
|
|
34389
|
+
Property(node) {
|
|
34390
|
+
const filename = normalizeFilename$1(context.filename ?? "");
|
|
34391
|
+
if (!TANSTACK_ROUTE_FILE_PATTERN.test(filename)) return;
|
|
34392
|
+
if (isEventHandlerProperty(node)) eventHandlerDepth++;
|
|
34393
|
+
},
|
|
34394
|
+
"Property:exit"(node) {
|
|
34395
|
+
const filename = normalizeFilename$1(context.filename ?? "");
|
|
34396
|
+
if (!TANSTACK_ROUTE_FILE_PATTERN.test(filename)) return;
|
|
34397
|
+
if (isEventHandlerProperty(node)) eventHandlerDepth = Math.max(0, eventHandlerDepth - 1);
|
|
34398
|
+
},
|
|
34399
|
+
VariableDeclarator(node) {
|
|
34400
|
+
const filename = normalizeFilename$1(context.filename ?? "");
|
|
34401
|
+
if (!TANSTACK_ROUTE_FILE_PATTERN.test(filename)) return;
|
|
34402
|
+
if (isHandlerNamedVariableDeclarator(node)) eventHandlerDepth++;
|
|
34403
|
+
},
|
|
34404
|
+
"VariableDeclarator:exit"(node) {
|
|
34405
|
+
const filename = normalizeFilename$1(context.filename ?? "");
|
|
34406
|
+
if (!TANSTACK_ROUTE_FILE_PATTERN.test(filename)) return;
|
|
34407
|
+
if (isHandlerNamedVariableDeclarator(node)) eventHandlerDepth = Math.max(0, eventHandlerDepth - 1);
|
|
34408
|
+
},
|
|
34409
|
+
FunctionDeclaration(node) {
|
|
34410
|
+
const filename = normalizeFilename$1(context.filename ?? "");
|
|
34411
|
+
if (!TANSTACK_ROUTE_FILE_PATTERN.test(filename)) return;
|
|
34412
|
+
if (isHandlerNamedFunctionDeclaration(node)) eventHandlerDepth++;
|
|
34413
|
+
},
|
|
34414
|
+
"FunctionDeclaration:exit"(node) {
|
|
34415
|
+
const filename = normalizeFilename$1(context.filename ?? "");
|
|
34416
|
+
if (!TANSTACK_ROUTE_FILE_PATTERN.test(filename)) return;
|
|
34417
|
+
if (isHandlerNamedFunctionDeclaration(node)) eventHandlerDepth = Math.max(0, eventHandlerDepth - 1);
|
|
34318
34418
|
}
|
|
34319
34419
|
};
|
|
34320
34420
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "oxlint-plugin-react-doctor",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.1-dev.b819e5f",
|
|
4
4
|
"description": "oxlint plugin for React Doctor: diagnose React codebases for security, performance, correctness, accessibility, bundle-size, and architecture issues",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"accessibility",
|