oxlint-plugin-react-doctor 0.2.14-dev.b9e9bcb → 0.2.14-dev.bdb9e36
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 +216 -0
- package/dist/index.js +367 -0
- package/package.json +4 -4
package/dist/index.d.ts
CHANGED
|
@@ -677,6 +677,24 @@ declare const REACT_DOCTOR_RULES: readonly [{
|
|
|
677
677
|
readonly recommendation?: string;
|
|
678
678
|
readonly create: (context: RuleContext) => RuleVisitors;
|
|
679
679
|
};
|
|
680
|
+
}, {
|
|
681
|
+
readonly key: "react-doctor/expo-no-non-inlined-env";
|
|
682
|
+
readonly id: "expo-no-non-inlined-env";
|
|
683
|
+
readonly source: "react-doctor";
|
|
684
|
+
readonly originallyExternal: false;
|
|
685
|
+
readonly rule: {
|
|
686
|
+
readonly framework: "react-native";
|
|
687
|
+
readonly category: "Bugs";
|
|
688
|
+
readonly tags: readonly string[];
|
|
689
|
+
readonly id: string;
|
|
690
|
+
readonly title?: string;
|
|
691
|
+
readonly severity: RuleSeverity;
|
|
692
|
+
readonly requires?: ReadonlyArray<string>;
|
|
693
|
+
readonly disabledBy?: ReadonlyArray<string>;
|
|
694
|
+
readonly defaultEnabled?: boolean;
|
|
695
|
+
readonly recommendation?: string;
|
|
696
|
+
readonly create: (context: RuleContext) => RuleVisitors;
|
|
697
|
+
};
|
|
680
698
|
}, {
|
|
681
699
|
readonly key: "react-doctor/forbid-component-props";
|
|
682
700
|
readonly id: "forbid-component-props";
|
|
@@ -4817,6 +4835,24 @@ declare const REACT_DOCTOR_RULES: readonly [{
|
|
|
4817
4835
|
readonly recommendation?: string;
|
|
4818
4836
|
readonly create: (context: RuleContext) => RuleVisitors;
|
|
4819
4837
|
};
|
|
4838
|
+
}, {
|
|
4839
|
+
readonly key: "react-doctor/rn-detox-missing-await";
|
|
4840
|
+
readonly id: "rn-detox-missing-await";
|
|
4841
|
+
readonly source: "react-doctor";
|
|
4842
|
+
readonly originallyExternal: false;
|
|
4843
|
+
readonly rule: {
|
|
4844
|
+
readonly framework: "react-native";
|
|
4845
|
+
readonly category: "Bugs";
|
|
4846
|
+
readonly tags: readonly string[];
|
|
4847
|
+
readonly id: string;
|
|
4848
|
+
readonly title?: string;
|
|
4849
|
+
readonly severity: RuleSeverity;
|
|
4850
|
+
readonly requires?: ReadonlyArray<string>;
|
|
4851
|
+
readonly disabledBy?: ReadonlyArray<string>;
|
|
4852
|
+
readonly defaultEnabled?: boolean;
|
|
4853
|
+
readonly recommendation?: string;
|
|
4854
|
+
readonly create: (context: RuleContext) => RuleVisitors;
|
|
4855
|
+
};
|
|
4820
4856
|
}, {
|
|
4821
4857
|
readonly key: "react-doctor/rn-list-callback-per-row";
|
|
4822
4858
|
readonly id: "rn-list-callback-per-row";
|
|
@@ -4889,6 +4925,24 @@ declare const REACT_DOCTOR_RULES: readonly [{
|
|
|
4889
4925
|
readonly recommendation?: string;
|
|
4890
4926
|
readonly create: (context: RuleContext) => RuleVisitors;
|
|
4891
4927
|
};
|
|
4928
|
+
}, {
|
|
4929
|
+
readonly key: "react-doctor/rn-no-deep-imports";
|
|
4930
|
+
readonly id: "rn-no-deep-imports";
|
|
4931
|
+
readonly source: "react-doctor";
|
|
4932
|
+
readonly originallyExternal: false;
|
|
4933
|
+
readonly rule: {
|
|
4934
|
+
readonly framework: "react-native";
|
|
4935
|
+
readonly category: "Bugs";
|
|
4936
|
+
readonly tags: readonly string[];
|
|
4937
|
+
readonly id: string;
|
|
4938
|
+
readonly title?: string;
|
|
4939
|
+
readonly severity: RuleSeverity;
|
|
4940
|
+
readonly requires?: ReadonlyArray<string>;
|
|
4941
|
+
readonly disabledBy?: ReadonlyArray<string>;
|
|
4942
|
+
readonly defaultEnabled?: boolean;
|
|
4943
|
+
readonly recommendation?: string;
|
|
4944
|
+
readonly create: (context: RuleContext) => RuleVisitors;
|
|
4945
|
+
};
|
|
4892
4946
|
}, {
|
|
4893
4947
|
readonly key: "react-doctor/rn-no-deprecated-modules";
|
|
4894
4948
|
readonly id: "rn-no-deprecated-modules";
|
|
@@ -4943,6 +4997,24 @@ declare const REACT_DOCTOR_RULES: readonly [{
|
|
|
4943
4997
|
readonly recommendation?: string;
|
|
4944
4998
|
readonly create: (context: RuleContext) => RuleVisitors;
|
|
4945
4999
|
};
|
|
5000
|
+
}, {
|
|
5001
|
+
readonly key: "react-doctor/rn-no-image-children";
|
|
5002
|
+
readonly id: "rn-no-image-children";
|
|
5003
|
+
readonly source: "react-doctor";
|
|
5004
|
+
readonly originallyExternal: false;
|
|
5005
|
+
readonly rule: {
|
|
5006
|
+
readonly framework: "react-native";
|
|
5007
|
+
readonly category: "Bugs";
|
|
5008
|
+
readonly tags: readonly string[];
|
|
5009
|
+
readonly id: string;
|
|
5010
|
+
readonly title?: string;
|
|
5011
|
+
readonly severity: RuleSeverity;
|
|
5012
|
+
readonly requires?: ReadonlyArray<string>;
|
|
5013
|
+
readonly disabledBy?: ReadonlyArray<string>;
|
|
5014
|
+
readonly defaultEnabled?: boolean;
|
|
5015
|
+
readonly recommendation?: string;
|
|
5016
|
+
readonly create: (context: RuleContext) => RuleVisitors;
|
|
5017
|
+
};
|
|
4946
5018
|
}, {
|
|
4947
5019
|
readonly key: "react-doctor/rn-no-inline-flatlist-renderitem";
|
|
4948
5020
|
readonly id: "rn-no-inline-flatlist-renderitem";
|
|
@@ -5033,6 +5105,24 @@ declare const REACT_DOCTOR_RULES: readonly [{
|
|
|
5033
5105
|
readonly recommendation?: string;
|
|
5034
5106
|
readonly create: (context: RuleContext) => RuleVisitors;
|
|
5035
5107
|
};
|
|
5108
|
+
}, {
|
|
5109
|
+
readonly key: "react-doctor/rn-no-panresponder";
|
|
5110
|
+
readonly id: "rn-no-panresponder";
|
|
5111
|
+
readonly source: "react-doctor";
|
|
5112
|
+
readonly originallyExternal: false;
|
|
5113
|
+
readonly rule: {
|
|
5114
|
+
readonly framework: "react-native";
|
|
5115
|
+
readonly category: "Bugs";
|
|
5116
|
+
readonly tags: readonly string[];
|
|
5117
|
+
readonly id: string;
|
|
5118
|
+
readonly title?: string;
|
|
5119
|
+
readonly severity: RuleSeverity;
|
|
5120
|
+
readonly requires?: ReadonlyArray<string>;
|
|
5121
|
+
readonly disabledBy?: ReadonlyArray<string>;
|
|
5122
|
+
readonly defaultEnabled?: boolean;
|
|
5123
|
+
readonly recommendation?: string;
|
|
5124
|
+
readonly create: (context: RuleContext) => RuleVisitors;
|
|
5125
|
+
};
|
|
5036
5126
|
}, {
|
|
5037
5127
|
readonly key: "react-doctor/rn-no-raw-text";
|
|
5038
5128
|
readonly id: "rn-no-raw-text";
|
|
@@ -5105,6 +5195,24 @@ declare const REACT_DOCTOR_RULES: readonly [{
|
|
|
5105
5195
|
readonly recommendation?: string;
|
|
5106
5196
|
readonly create: (context: RuleContext) => RuleVisitors;
|
|
5107
5197
|
};
|
|
5198
|
+
}, {
|
|
5199
|
+
readonly key: "react-doctor/rn-no-set-native-props";
|
|
5200
|
+
readonly id: "rn-no-set-native-props";
|
|
5201
|
+
readonly source: "react-doctor";
|
|
5202
|
+
readonly originallyExternal: false;
|
|
5203
|
+
readonly rule: {
|
|
5204
|
+
readonly framework: "react-native";
|
|
5205
|
+
readonly category: "Bugs";
|
|
5206
|
+
readonly tags: readonly string[];
|
|
5207
|
+
readonly id: string;
|
|
5208
|
+
readonly title?: string;
|
|
5209
|
+
readonly severity: RuleSeverity;
|
|
5210
|
+
readonly requires?: ReadonlyArray<string>;
|
|
5211
|
+
readonly disabledBy?: ReadonlyArray<string>;
|
|
5212
|
+
readonly defaultEnabled?: boolean;
|
|
5213
|
+
readonly recommendation?: string;
|
|
5214
|
+
readonly create: (context: RuleContext) => RuleVisitors;
|
|
5215
|
+
};
|
|
5108
5216
|
}, {
|
|
5109
5217
|
readonly key: "react-doctor/rn-no-single-element-style-array";
|
|
5110
5218
|
readonly id: "rn-no-single-element-style-array";
|
|
@@ -6539,6 +6647,24 @@ declare const RULES: readonly [{
|
|
|
6539
6647
|
readonly recommendation?: string;
|
|
6540
6648
|
readonly create: (context: RuleContext) => RuleVisitors;
|
|
6541
6649
|
};
|
|
6650
|
+
}, {
|
|
6651
|
+
readonly key: "react-doctor/expo-no-non-inlined-env";
|
|
6652
|
+
readonly id: "expo-no-non-inlined-env";
|
|
6653
|
+
readonly source: "react-doctor";
|
|
6654
|
+
readonly originallyExternal: false;
|
|
6655
|
+
readonly rule: {
|
|
6656
|
+
readonly framework: "react-native";
|
|
6657
|
+
readonly category: "Bugs";
|
|
6658
|
+
readonly tags: readonly string[];
|
|
6659
|
+
readonly id: string;
|
|
6660
|
+
readonly title?: string;
|
|
6661
|
+
readonly severity: RuleSeverity;
|
|
6662
|
+
readonly requires?: ReadonlyArray<string>;
|
|
6663
|
+
readonly disabledBy?: ReadonlyArray<string>;
|
|
6664
|
+
readonly defaultEnabled?: boolean;
|
|
6665
|
+
readonly recommendation?: string;
|
|
6666
|
+
readonly create: (context: RuleContext) => RuleVisitors;
|
|
6667
|
+
};
|
|
6542
6668
|
}, {
|
|
6543
6669
|
readonly key: "react-doctor/forbid-component-props";
|
|
6544
6670
|
readonly id: "forbid-component-props";
|
|
@@ -10679,6 +10805,24 @@ declare const RULES: readonly [{
|
|
|
10679
10805
|
readonly recommendation?: string;
|
|
10680
10806
|
readonly create: (context: RuleContext) => RuleVisitors;
|
|
10681
10807
|
};
|
|
10808
|
+
}, {
|
|
10809
|
+
readonly key: "react-doctor/rn-detox-missing-await";
|
|
10810
|
+
readonly id: "rn-detox-missing-await";
|
|
10811
|
+
readonly source: "react-doctor";
|
|
10812
|
+
readonly originallyExternal: false;
|
|
10813
|
+
readonly rule: {
|
|
10814
|
+
readonly framework: "react-native";
|
|
10815
|
+
readonly category: "Bugs";
|
|
10816
|
+
readonly tags: readonly string[];
|
|
10817
|
+
readonly id: string;
|
|
10818
|
+
readonly title?: string;
|
|
10819
|
+
readonly severity: RuleSeverity;
|
|
10820
|
+
readonly requires?: ReadonlyArray<string>;
|
|
10821
|
+
readonly disabledBy?: ReadonlyArray<string>;
|
|
10822
|
+
readonly defaultEnabled?: boolean;
|
|
10823
|
+
readonly recommendation?: string;
|
|
10824
|
+
readonly create: (context: RuleContext) => RuleVisitors;
|
|
10825
|
+
};
|
|
10682
10826
|
}, {
|
|
10683
10827
|
readonly key: "react-doctor/rn-list-callback-per-row";
|
|
10684
10828
|
readonly id: "rn-list-callback-per-row";
|
|
@@ -10751,6 +10895,24 @@ declare const RULES: readonly [{
|
|
|
10751
10895
|
readonly recommendation?: string;
|
|
10752
10896
|
readonly create: (context: RuleContext) => RuleVisitors;
|
|
10753
10897
|
};
|
|
10898
|
+
}, {
|
|
10899
|
+
readonly key: "react-doctor/rn-no-deep-imports";
|
|
10900
|
+
readonly id: "rn-no-deep-imports";
|
|
10901
|
+
readonly source: "react-doctor";
|
|
10902
|
+
readonly originallyExternal: false;
|
|
10903
|
+
readonly rule: {
|
|
10904
|
+
readonly framework: "react-native";
|
|
10905
|
+
readonly category: "Bugs";
|
|
10906
|
+
readonly tags: readonly string[];
|
|
10907
|
+
readonly id: string;
|
|
10908
|
+
readonly title?: string;
|
|
10909
|
+
readonly severity: RuleSeverity;
|
|
10910
|
+
readonly requires?: ReadonlyArray<string>;
|
|
10911
|
+
readonly disabledBy?: ReadonlyArray<string>;
|
|
10912
|
+
readonly defaultEnabled?: boolean;
|
|
10913
|
+
readonly recommendation?: string;
|
|
10914
|
+
readonly create: (context: RuleContext) => RuleVisitors;
|
|
10915
|
+
};
|
|
10754
10916
|
}, {
|
|
10755
10917
|
readonly key: "react-doctor/rn-no-deprecated-modules";
|
|
10756
10918
|
readonly id: "rn-no-deprecated-modules";
|
|
@@ -10805,6 +10967,24 @@ declare const RULES: readonly [{
|
|
|
10805
10967
|
readonly recommendation?: string;
|
|
10806
10968
|
readonly create: (context: RuleContext) => RuleVisitors;
|
|
10807
10969
|
};
|
|
10970
|
+
}, {
|
|
10971
|
+
readonly key: "react-doctor/rn-no-image-children";
|
|
10972
|
+
readonly id: "rn-no-image-children";
|
|
10973
|
+
readonly source: "react-doctor";
|
|
10974
|
+
readonly originallyExternal: false;
|
|
10975
|
+
readonly rule: {
|
|
10976
|
+
readonly framework: "react-native";
|
|
10977
|
+
readonly category: "Bugs";
|
|
10978
|
+
readonly tags: readonly string[];
|
|
10979
|
+
readonly id: string;
|
|
10980
|
+
readonly title?: string;
|
|
10981
|
+
readonly severity: RuleSeverity;
|
|
10982
|
+
readonly requires?: ReadonlyArray<string>;
|
|
10983
|
+
readonly disabledBy?: ReadonlyArray<string>;
|
|
10984
|
+
readonly defaultEnabled?: boolean;
|
|
10985
|
+
readonly recommendation?: string;
|
|
10986
|
+
readonly create: (context: RuleContext) => RuleVisitors;
|
|
10987
|
+
};
|
|
10808
10988
|
}, {
|
|
10809
10989
|
readonly key: "react-doctor/rn-no-inline-flatlist-renderitem";
|
|
10810
10990
|
readonly id: "rn-no-inline-flatlist-renderitem";
|
|
@@ -10895,6 +11075,24 @@ declare const RULES: readonly [{
|
|
|
10895
11075
|
readonly recommendation?: string;
|
|
10896
11076
|
readonly create: (context: RuleContext) => RuleVisitors;
|
|
10897
11077
|
};
|
|
11078
|
+
}, {
|
|
11079
|
+
readonly key: "react-doctor/rn-no-panresponder";
|
|
11080
|
+
readonly id: "rn-no-panresponder";
|
|
11081
|
+
readonly source: "react-doctor";
|
|
11082
|
+
readonly originallyExternal: false;
|
|
11083
|
+
readonly rule: {
|
|
11084
|
+
readonly framework: "react-native";
|
|
11085
|
+
readonly category: "Bugs";
|
|
11086
|
+
readonly tags: readonly string[];
|
|
11087
|
+
readonly id: string;
|
|
11088
|
+
readonly title?: string;
|
|
11089
|
+
readonly severity: RuleSeverity;
|
|
11090
|
+
readonly requires?: ReadonlyArray<string>;
|
|
11091
|
+
readonly disabledBy?: ReadonlyArray<string>;
|
|
11092
|
+
readonly defaultEnabled?: boolean;
|
|
11093
|
+
readonly recommendation?: string;
|
|
11094
|
+
readonly create: (context: RuleContext) => RuleVisitors;
|
|
11095
|
+
};
|
|
10898
11096
|
}, {
|
|
10899
11097
|
readonly key: "react-doctor/rn-no-raw-text";
|
|
10900
11098
|
readonly id: "rn-no-raw-text";
|
|
@@ -10967,6 +11165,24 @@ declare const RULES: readonly [{
|
|
|
10967
11165
|
readonly recommendation?: string;
|
|
10968
11166
|
readonly create: (context: RuleContext) => RuleVisitors;
|
|
10969
11167
|
};
|
|
11168
|
+
}, {
|
|
11169
|
+
readonly key: "react-doctor/rn-no-set-native-props";
|
|
11170
|
+
readonly id: "rn-no-set-native-props";
|
|
11171
|
+
readonly source: "react-doctor";
|
|
11172
|
+
readonly originallyExternal: false;
|
|
11173
|
+
readonly rule: {
|
|
11174
|
+
readonly framework: "react-native";
|
|
11175
|
+
readonly category: "Bugs";
|
|
11176
|
+
readonly tags: readonly string[];
|
|
11177
|
+
readonly id: string;
|
|
11178
|
+
readonly title?: string;
|
|
11179
|
+
readonly severity: RuleSeverity;
|
|
11180
|
+
readonly requires?: ReadonlyArray<string>;
|
|
11181
|
+
readonly disabledBy?: ReadonlyArray<string>;
|
|
11182
|
+
readonly defaultEnabled?: boolean;
|
|
11183
|
+
readonly recommendation?: string;
|
|
11184
|
+
readonly create: (context: RuleContext) => RuleVisitors;
|
|
11185
|
+
};
|
|
10970
11186
|
}, {
|
|
10971
11187
|
readonly key: "react-doctor/rn-no-single-element-style-array";
|
|
10972
11188
|
readonly id: "rn-no-single-element-style-array";
|
package/dist/index.js
CHANGED
|
@@ -6177,6 +6177,42 @@ If the missing value is recreated every render, move it inside the hook or stabi
|
|
|
6177
6177
|
}
|
|
6178
6178
|
});
|
|
6179
6179
|
//#endregion
|
|
6180
|
+
//#region src/plugin/rules/react-native/expo-no-non-inlined-env.ts
|
|
6181
|
+
const EMPTY_VISITORS$3 = {};
|
|
6182
|
+
const NODE_OR_BUILD_FILE = /(\.config\.[cm]?[jt]sx?$)|((^|\/)(scripts|tools|tooling|cli|bin)\/)|(\+(api|html)\.[cm]?[jt]sx?$)|(\.server\.[cm]?[jt]sx?$)|(\.(test|spec)\.[cm]?[jt]sx?$)|((^|\/)__tests__\/)|(\.e2e\.[cm]?[jt]sx?$)/;
|
|
6183
|
+
const isNonExpoPublicLiteralKey = (key) => isNodeOfType(key, "Literal") && typeof key.value === "string" && !key.value.startsWith("EXPO_PUBLIC_");
|
|
6184
|
+
const isProcessEnv = (node) => isNodeOfType(node, "MemberExpression") && !node.computed && isNodeOfType(node.object, "Identifier") && node.object.name === "process" && isNodeOfType(node.property, "Identifier") && node.property.name === "env";
|
|
6185
|
+
const expoNoNonInlinedEnv = defineRule({
|
|
6186
|
+
id: "expo-no-non-inlined-env",
|
|
6187
|
+
title: "Non-inlinable process.env access (Expo)",
|
|
6188
|
+
requires: ["expo"],
|
|
6189
|
+
severity: "warn",
|
|
6190
|
+
recommendation: "Read env vars with static dotted access (`process.env.EXPO_PUBLIC_NAME`). Computed access and destructuring aren't inlined by babel-preset-expo and resolve to `undefined` at runtime.",
|
|
6191
|
+
create: (context) => {
|
|
6192
|
+
const filename = normalizeFilename$1(context.filename ?? "");
|
|
6193
|
+
if (filename && NODE_OR_BUILD_FILE.test(filename)) return EMPTY_VISITORS$3;
|
|
6194
|
+
return {
|
|
6195
|
+
MemberExpression(node) {
|
|
6196
|
+
if (!node.computed) return;
|
|
6197
|
+
if (!isProcessEnv(node.object)) return;
|
|
6198
|
+
if (isNonExpoPublicLiteralKey(node.property)) return;
|
|
6199
|
+
context.report({
|
|
6200
|
+
node,
|
|
6201
|
+
message: "Computed `process.env[...]` access isn't inlined by babel-preset-expo and is `undefined` at runtime. Use static `process.env.EXPO_PUBLIC_NAME`."
|
|
6202
|
+
});
|
|
6203
|
+
},
|
|
6204
|
+
VariableDeclarator(node) {
|
|
6205
|
+
if (!isNodeOfType(node.id, "ObjectPattern")) return;
|
|
6206
|
+
if (!isProcessEnv(node.init)) return;
|
|
6207
|
+
context.report({
|
|
6208
|
+
node,
|
|
6209
|
+
message: "Destructuring `process.env` isn't inlined by babel-preset-expo, so the values are `undefined` at runtime. Read each var via `process.env.EXPO_PUBLIC_NAME`."
|
|
6210
|
+
});
|
|
6211
|
+
}
|
|
6212
|
+
};
|
|
6213
|
+
}
|
|
6214
|
+
});
|
|
6215
|
+
//#endregion
|
|
6180
6216
|
//#region src/plugin/utils/compile-glob.ts
|
|
6181
6217
|
/**
|
|
6182
6218
|
* Compiles a simple glob pattern (only `*` as a wildcard) into an
|
|
@@ -26867,6 +26903,99 @@ const rnBottomSheetPreferNative = defineRule({
|
|
|
26867
26903
|
} })
|
|
26868
26904
|
});
|
|
26869
26905
|
//#endregion
|
|
26906
|
+
//#region src/plugin/rules/react-native/rn-detox-missing-await.ts
|
|
26907
|
+
const EMPTY_VISITORS$2 = {};
|
|
26908
|
+
const DETOX_TEST_FILE = /(\.e2e\.[cm]?[jt]sx?$)|((^|\/)e2e\/)/;
|
|
26909
|
+
const DETOX_ELEMENT_ACTIONS = new Set([
|
|
26910
|
+
"tap",
|
|
26911
|
+
"multiTap",
|
|
26912
|
+
"longPress",
|
|
26913
|
+
"longPressAndDrag",
|
|
26914
|
+
"swipe",
|
|
26915
|
+
"scroll",
|
|
26916
|
+
"scrollTo",
|
|
26917
|
+
"scrollToIndex",
|
|
26918
|
+
"scrollToElement",
|
|
26919
|
+
"typeText",
|
|
26920
|
+
"replaceText",
|
|
26921
|
+
"clearText",
|
|
26922
|
+
"tapReturnKey",
|
|
26923
|
+
"tapBackspaceKey",
|
|
26924
|
+
"pinch",
|
|
26925
|
+
"setColumnToValue",
|
|
26926
|
+
"setDatePickerDate",
|
|
26927
|
+
"performAccessibilityAction",
|
|
26928
|
+
"adjustSliderToPosition"
|
|
26929
|
+
]);
|
|
26930
|
+
const PROMISE_SETTLE_METHODS = new Set([
|
|
26931
|
+
"then",
|
|
26932
|
+
"catch",
|
|
26933
|
+
"finally"
|
|
26934
|
+
]);
|
|
26935
|
+
const findChainRoot = (node) => {
|
|
26936
|
+
if (isNodeOfType(node, "CallExpression")) {
|
|
26937
|
+
if (isNodeOfType(node.callee, "Identifier")) return {
|
|
26938
|
+
calleeName: node.callee.name,
|
|
26939
|
+
rootCall: node
|
|
26940
|
+
};
|
|
26941
|
+
if (isNodeOfType(node.callee, "MemberExpression")) return findChainRoot(node.callee.object);
|
|
26942
|
+
return null;
|
|
26943
|
+
}
|
|
26944
|
+
if (isNodeOfType(node, "MemberExpression")) return findChainRoot(node.object);
|
|
26945
|
+
return null;
|
|
26946
|
+
};
|
|
26947
|
+
const isDetoxExpectSubject = (rootCall) => {
|
|
26948
|
+
const firstArgument = rootCall.arguments?.[0];
|
|
26949
|
+
if (!firstArgument || !isNodeOfType(firstArgument, "CallExpression")) return false;
|
|
26950
|
+
if (!isNodeOfType(firstArgument.callee, "Identifier")) return false;
|
|
26951
|
+
return firstArgument.callee.name === "element" || firstArgument.callee.name === "web";
|
|
26952
|
+
};
|
|
26953
|
+
const getTerminalMethodName = (callExpression) => {
|
|
26954
|
+
const callee = callExpression.callee;
|
|
26955
|
+
if (!isNodeOfType(callee, "MemberExpression")) return null;
|
|
26956
|
+
if (callee.computed || !isNodeOfType(callee.property, "Identifier")) return null;
|
|
26957
|
+
return callee.property.name;
|
|
26958
|
+
};
|
|
26959
|
+
const rnDetoxMissingAwait = defineRule({
|
|
26960
|
+
id: "rn-detox-missing-await",
|
|
26961
|
+
title: "Un-awaited Detox action",
|
|
26962
|
+
requires: ["react-native"],
|
|
26963
|
+
severity: "warn",
|
|
26964
|
+
recommendation: "Prepend `await` to Detox actions, `waitFor(...)` chains, and `expect(element(...))` assertions. They return promises tied to Detox's synchronization, so a missing await runs steps out of order.",
|
|
26965
|
+
create: (context) => {
|
|
26966
|
+
const filename = normalizeFilename$1(context.filename ?? "");
|
|
26967
|
+
if (!filename || !DETOX_TEST_FILE.test(filename)) return EMPTY_VISITORS$2;
|
|
26968
|
+
return { ExpressionStatement(node) {
|
|
26969
|
+
const expression = node.expression;
|
|
26970
|
+
if (!isNodeOfType(expression, "CallExpression")) return;
|
|
26971
|
+
const terminalMethod = getTerminalMethodName(expression);
|
|
26972
|
+
if (terminalMethod === null) return;
|
|
26973
|
+
if (PROMISE_SETTLE_METHODS.has(terminalMethod)) return;
|
|
26974
|
+
const root = findChainRoot(expression);
|
|
26975
|
+
if (!root) return;
|
|
26976
|
+
if (root.calleeName === "element") {
|
|
26977
|
+
if (!DETOX_ELEMENT_ACTIONS.has(terminalMethod)) return;
|
|
26978
|
+
context.report({
|
|
26979
|
+
node,
|
|
26980
|
+
message: `This Detox action (\`${terminalMethod}\`) isn't awaited, so it runs out of order and can race. Prepend \`await\`.`
|
|
26981
|
+
});
|
|
26982
|
+
return;
|
|
26983
|
+
}
|
|
26984
|
+
if (root.calleeName === "waitFor") {
|
|
26985
|
+
context.report({
|
|
26986
|
+
node,
|
|
26987
|
+
message: "This Detox `waitFor(...)` chain isn't awaited. Prepend `await`."
|
|
26988
|
+
});
|
|
26989
|
+
return;
|
|
26990
|
+
}
|
|
26991
|
+
if (root.calleeName === "expect" && isDetoxExpectSubject(root.rootCall)) context.report({
|
|
26992
|
+
node,
|
|
26993
|
+
message: "This Detox `expect(element(...))` assertion isn't awaited. Prepend `await`."
|
|
26994
|
+
});
|
|
26995
|
+
} };
|
|
26996
|
+
}
|
|
26997
|
+
});
|
|
26998
|
+
//#endregion
|
|
26870
26999
|
//#region src/plugin/constants/react-native.ts
|
|
26871
27000
|
const REACT_NATIVE_TEXT_COMPONENTS = new Set([
|
|
26872
27001
|
"Text",
|
|
@@ -27139,6 +27268,96 @@ const rnListRecyclableWithoutTypes = defineRule({
|
|
|
27139
27268
|
} })
|
|
27140
27269
|
});
|
|
27141
27270
|
//#endregion
|
|
27271
|
+
//#region src/plugin/rules/react-native/rn-no-deep-imports.ts
|
|
27272
|
+
const DEEP_IMPORT_PREFIX = "react-native/Libraries/";
|
|
27273
|
+
const NEW_APP_SCREEN_PATH = "react-native/Libraries/NewAppScreen";
|
|
27274
|
+
const PUBLIC_RN_ROOT_EXPORTS = new Set([
|
|
27275
|
+
"View",
|
|
27276
|
+
"Text",
|
|
27277
|
+
"Image",
|
|
27278
|
+
"ImageBackground",
|
|
27279
|
+
"ScrollView",
|
|
27280
|
+
"FlatList",
|
|
27281
|
+
"SectionList",
|
|
27282
|
+
"VirtualizedList",
|
|
27283
|
+
"TextInput",
|
|
27284
|
+
"Pressable",
|
|
27285
|
+
"TouchableOpacity",
|
|
27286
|
+
"TouchableHighlight",
|
|
27287
|
+
"TouchableWithoutFeedback",
|
|
27288
|
+
"TouchableNativeFeedback",
|
|
27289
|
+
"Button",
|
|
27290
|
+
"Switch",
|
|
27291
|
+
"Modal",
|
|
27292
|
+
"ActivityIndicator",
|
|
27293
|
+
"RefreshControl",
|
|
27294
|
+
"KeyboardAvoidingView",
|
|
27295
|
+
"StyleSheet",
|
|
27296
|
+
"Alert",
|
|
27297
|
+
"Animated",
|
|
27298
|
+
"Platform",
|
|
27299
|
+
"Dimensions",
|
|
27300
|
+
"AppRegistry",
|
|
27301
|
+
"AppState",
|
|
27302
|
+
"Linking",
|
|
27303
|
+
"Appearance",
|
|
27304
|
+
"Keyboard",
|
|
27305
|
+
"StatusBar",
|
|
27306
|
+
"PixelRatio",
|
|
27307
|
+
"PanResponder",
|
|
27308
|
+
"BackHandler",
|
|
27309
|
+
"InteractionManager"
|
|
27310
|
+
]);
|
|
27311
|
+
const lastPathSegment = (source) => {
|
|
27312
|
+
const segments = source.split("/");
|
|
27313
|
+
return segments[segments.length - 1] ?? "";
|
|
27314
|
+
};
|
|
27315
|
+
const classifyDeepImport = (source) => {
|
|
27316
|
+
if (typeof source !== "string") return null;
|
|
27317
|
+
if (!source.startsWith(DEEP_IMPORT_PREFIX)) return null;
|
|
27318
|
+
if (source === NEW_APP_SCREEN_PATH || source.startsWith(`${NEW_APP_SCREEN_PATH}/`)) return { message: "`react-native/Libraries/NewAppScreen` was moved out of core in React Native 0.80; import from `@react-native/new-app-screen` instead." };
|
|
27319
|
+
const exportName = lastPathSegment(source);
|
|
27320
|
+
if (PUBLIC_RN_ROOT_EXPORTS.has(exportName)) return { message: `Deep import from "${source}" is a deprecated React Native internal subpath (RFC 0894) and breaks on upgrade. Import from "react-native" instead.` };
|
|
27321
|
+
return null;
|
|
27322
|
+
};
|
|
27323
|
+
const isEverySpecifierInlineType = (specifiers, specifierType, kindField) => {
|
|
27324
|
+
if (!specifiers || specifiers.length === 0) return false;
|
|
27325
|
+
return specifiers.every((specifier) => isNodeOfType(specifier, specifierType) && specifier[kindField] === "type");
|
|
27326
|
+
};
|
|
27327
|
+
const rnNoDeepImports = defineRule({
|
|
27328
|
+
id: "rn-no-deep-imports",
|
|
27329
|
+
title: "Deep import into react-native internals",
|
|
27330
|
+
requires: ["react-native"],
|
|
27331
|
+
severity: "warn",
|
|
27332
|
+
recommendation: "Import the symbol from `react-native` (the package root) instead of the deprecated `react-native/Libraries/...` subpath, which RFC 0894 removes on upgrade.",
|
|
27333
|
+
create: (context) => {
|
|
27334
|
+
const reportFinding = (node, source) => {
|
|
27335
|
+
const finding = classifyDeepImport(source);
|
|
27336
|
+
if (finding) context.report({
|
|
27337
|
+
node,
|
|
27338
|
+
message: finding.message
|
|
27339
|
+
});
|
|
27340
|
+
};
|
|
27341
|
+
return {
|
|
27342
|
+
ImportDeclaration(node) {
|
|
27343
|
+
if (node.importKind === "type") return;
|
|
27344
|
+
if (isEverySpecifierInlineType(node.specifiers, "ImportSpecifier", "importKind")) return;
|
|
27345
|
+
reportFinding(node, node.source?.value);
|
|
27346
|
+
},
|
|
27347
|
+
ExportNamedDeclaration(node) {
|
|
27348
|
+
if (node.exportKind === "type") return;
|
|
27349
|
+
if (!node.source) return;
|
|
27350
|
+
if (isEverySpecifierInlineType(node.specifiers, "ExportSpecifier", "exportKind")) return;
|
|
27351
|
+
reportFinding(node, node.source.value);
|
|
27352
|
+
},
|
|
27353
|
+
ExportAllDeclaration(node) {
|
|
27354
|
+
if (node.exportKind === "type") return;
|
|
27355
|
+
reportFinding(node, node.source?.value);
|
|
27356
|
+
}
|
|
27357
|
+
};
|
|
27358
|
+
}
|
|
27359
|
+
});
|
|
27360
|
+
//#endregion
|
|
27142
27361
|
//#region src/plugin/rules/react-native/rn-no-deprecated-modules.ts
|
|
27143
27362
|
const rnNoDeprecatedModules = defineRule({
|
|
27144
27363
|
id: "rn-no-deprecated-modules",
|
|
@@ -27278,6 +27497,39 @@ const rnNoFalsyAndRender = defineRule({
|
|
|
27278
27497
|
}
|
|
27279
27498
|
});
|
|
27280
27499
|
//#endregion
|
|
27500
|
+
//#region src/plugin/rules/react-native/rn-no-image-children.ts
|
|
27501
|
+
const isMeaningfulImageChild = (child) => {
|
|
27502
|
+
if (isNodeOfType(child, "JSXElement") || isNodeOfType(child, "JSXFragment")) return true;
|
|
27503
|
+
if (isNodeOfType(child, "JSXText")) return (child.value ?? "").trim().length > 0;
|
|
27504
|
+
if (isNodeOfType(child, "JSXExpressionContainer")) {
|
|
27505
|
+
const expression = child.expression;
|
|
27506
|
+
if (isNodeOfType(expression, "JSXEmptyExpression")) return false;
|
|
27507
|
+
if (isNodeOfType(expression, "Literal") && (expression.value === null || expression.value === false)) return false;
|
|
27508
|
+
if (isNodeOfType(expression, "Identifier") && expression.name === "undefined") return false;
|
|
27509
|
+
return true;
|
|
27510
|
+
}
|
|
27511
|
+
return false;
|
|
27512
|
+
};
|
|
27513
|
+
const rnNoImageChildren = defineRule({
|
|
27514
|
+
id: "rn-no-image-children",
|
|
27515
|
+
title: "Children inside react-native <Image>",
|
|
27516
|
+
requires: ["react-native"],
|
|
27517
|
+
severity: "error",
|
|
27518
|
+
recommendation: "React Native's `<Image>` can't render children. Use `<ImageBackground>` (same `source`/`style` props) to layer content over an image.",
|
|
27519
|
+
create: (context) => ({ JSXElement(node) {
|
|
27520
|
+
const openingElement = node.openingElement;
|
|
27521
|
+
if (!openingElement) return;
|
|
27522
|
+
const localName = resolveJsxElementName(openingElement);
|
|
27523
|
+
if (!localName) return;
|
|
27524
|
+
if (getImportedNameFromModule(openingElement, localName, "react-native") !== "Image") return;
|
|
27525
|
+
if (!(node.children ?? []).some(isMeaningfulImageChild)) return;
|
|
27526
|
+
context.report({
|
|
27527
|
+
node: openingElement,
|
|
27528
|
+
message: "React Native's <Image> does not render children, so this content silently disappears. Use <ImageBackground> to layer content over an image."
|
|
27529
|
+
});
|
|
27530
|
+
} })
|
|
27531
|
+
});
|
|
27532
|
+
//#endregion
|
|
27281
27533
|
//#region src/plugin/rules/react-native/rn-no-inline-flatlist-renderitem.ts
|
|
27282
27534
|
const rnNoInlineFlatlistRenderitem = defineRule({
|
|
27283
27535
|
id: "rn-no-inline-flatlist-renderitem",
|
|
@@ -27444,6 +27696,29 @@ const rnNoNonNativeNavigator = defineRule({
|
|
|
27444
27696
|
} })
|
|
27445
27697
|
});
|
|
27446
27698
|
//#endregion
|
|
27699
|
+
//#region src/plugin/rules/react-native/rn-no-panresponder.ts
|
|
27700
|
+
const rnNoPanresponder = defineRule({
|
|
27701
|
+
id: "rn-no-panresponder",
|
|
27702
|
+
title: "PanResponder over react-native-gesture-handler",
|
|
27703
|
+
tags: ["test-noise"],
|
|
27704
|
+
requires: ["react-native"],
|
|
27705
|
+
severity: "warn",
|
|
27706
|
+
recommendation: "Use `react-native-gesture-handler` (`Gesture.Pan()`) instead of `PanResponder`. It runs gestures on the native UI thread, so they stay smooth even when the JS thread is busy.",
|
|
27707
|
+
create: (context) => ({ ImportDeclaration(node) {
|
|
27708
|
+
if (node.source?.value !== "react-native") return;
|
|
27709
|
+
if (node.importKind === "type") return;
|
|
27710
|
+
for (const specifier of node.specifiers ?? []) {
|
|
27711
|
+
if (!isNodeOfType(specifier, "ImportSpecifier")) continue;
|
|
27712
|
+
if (specifier.importKind === "type") continue;
|
|
27713
|
+
if (getImportedName$1(specifier) !== "PanResponder") continue;
|
|
27714
|
+
context.report({
|
|
27715
|
+
node: specifier,
|
|
27716
|
+
message: "PanResponder runs gesture handling on the JS thread, which stutters under load. Use react-native-gesture-handler (`Gesture.Pan()`) so gestures run on the native UI thread."
|
|
27717
|
+
});
|
|
27718
|
+
}
|
|
27719
|
+
} })
|
|
27720
|
+
});
|
|
27721
|
+
//#endregion
|
|
27447
27722
|
//#region src/plugin/utils/is-inside-platform-os-web-branch.ts
|
|
27448
27723
|
const unwrapAccessor = (node) => {
|
|
27449
27724
|
let current = node;
|
|
@@ -27820,6 +28095,26 @@ const rnNoScrollviewMappedList = defineRule({
|
|
|
27820
28095
|
} })
|
|
27821
28096
|
});
|
|
27822
28097
|
//#endregion
|
|
28098
|
+
//#region src/plugin/rules/react-native/rn-no-set-native-props.ts
|
|
28099
|
+
const isStaticMemberNamed = (node, name) => isNodeOfType(node, "MemberExpression") && !node.computed && isNodeOfType(node.property, "Identifier") && node.property.name === name;
|
|
28100
|
+
const rnNoSetNativeProps = defineRule({
|
|
28101
|
+
id: "rn-no-set-native-props",
|
|
28102
|
+
title: "Imperative setNativeProps (no-op under Fabric)",
|
|
28103
|
+
requires: ["react-native"],
|
|
28104
|
+
severity: "warn",
|
|
28105
|
+
recommendation: "Drive the prop through React state, an `Animated.Value` (with `useNativeDriver: true`), or a Reanimated shared value. `setNativeProps` is a silent no-op under the New Architecture.",
|
|
28106
|
+
create: (context) => ({ CallExpression(node) {
|
|
28107
|
+
const callee = node.callee;
|
|
28108
|
+
if (!isStaticMemberNamed(callee, "setNativeProps")) return;
|
|
28109
|
+
if (!isNodeOfType(callee, "MemberExpression")) return;
|
|
28110
|
+
if (!isStaticMemberNamed(callee.object, "current")) return;
|
|
28111
|
+
context.report({
|
|
28112
|
+
node,
|
|
28113
|
+
message: "`setNativeProps` is a silent no-op under the New Architecture (Fabric), so this imperative update won't change the view. Drive the prop via state, an Animated.Value, or a Reanimated shared value."
|
|
28114
|
+
});
|
|
28115
|
+
} })
|
|
28116
|
+
});
|
|
28117
|
+
//#endregion
|
|
27823
28118
|
//#region src/plugin/rules/react-native/rn-no-single-element-style-array.ts
|
|
27824
28119
|
const rnNoSingleElementStyleArray = defineRule({
|
|
27825
28120
|
id: "rn-no-single-element-style-array",
|
|
@@ -34410,6 +34705,18 @@ const reactDoctorRules = [
|
|
|
34410
34705
|
category: "Bugs"
|
|
34411
34706
|
}
|
|
34412
34707
|
},
|
|
34708
|
+
{
|
|
34709
|
+
key: "react-doctor/expo-no-non-inlined-env",
|
|
34710
|
+
id: "expo-no-non-inlined-env",
|
|
34711
|
+
source: "react-doctor",
|
|
34712
|
+
originallyExternal: false,
|
|
34713
|
+
rule: {
|
|
34714
|
+
...expoNoNonInlinedEnv,
|
|
34715
|
+
framework: "react-native",
|
|
34716
|
+
category: "Bugs",
|
|
34717
|
+
tags: [...new Set(["react-native", ...expoNoNonInlinedEnv.tags ?? []])]
|
|
34718
|
+
}
|
|
34719
|
+
},
|
|
34413
34720
|
{
|
|
34414
34721
|
key: "react-doctor/forbid-component-props",
|
|
34415
34722
|
id: "forbid-component-props",
|
|
@@ -36943,6 +37250,18 @@ const reactDoctorRules = [
|
|
|
36943
37250
|
tags: [...new Set(["react-native", ...rnBottomSheetPreferNative.tags ?? []])]
|
|
36944
37251
|
}
|
|
36945
37252
|
},
|
|
37253
|
+
{
|
|
37254
|
+
key: "react-doctor/rn-detox-missing-await",
|
|
37255
|
+
id: "rn-detox-missing-await",
|
|
37256
|
+
source: "react-doctor",
|
|
37257
|
+
originallyExternal: false,
|
|
37258
|
+
rule: {
|
|
37259
|
+
...rnDetoxMissingAwait,
|
|
37260
|
+
framework: "react-native",
|
|
37261
|
+
category: "Bugs",
|
|
37262
|
+
tags: [...new Set(["react-native", ...rnDetoxMissingAwait.tags ?? []])]
|
|
37263
|
+
}
|
|
37264
|
+
},
|
|
36946
37265
|
{
|
|
36947
37266
|
key: "react-doctor/rn-list-callback-per-row",
|
|
36948
37267
|
id: "rn-list-callback-per-row",
|
|
@@ -36991,6 +37310,18 @@ const reactDoctorRules = [
|
|
|
36991
37310
|
tags: [...new Set(["react-native", ...rnListRecyclableWithoutTypes.tags ?? []])]
|
|
36992
37311
|
}
|
|
36993
37312
|
},
|
|
37313
|
+
{
|
|
37314
|
+
key: "react-doctor/rn-no-deep-imports",
|
|
37315
|
+
id: "rn-no-deep-imports",
|
|
37316
|
+
source: "react-doctor",
|
|
37317
|
+
originallyExternal: false,
|
|
37318
|
+
rule: {
|
|
37319
|
+
...rnNoDeepImports,
|
|
37320
|
+
framework: "react-native",
|
|
37321
|
+
category: "Bugs",
|
|
37322
|
+
tags: [...new Set(["react-native", ...rnNoDeepImports.tags ?? []])]
|
|
37323
|
+
}
|
|
37324
|
+
},
|
|
36994
37325
|
{
|
|
36995
37326
|
key: "react-doctor/rn-no-deprecated-modules",
|
|
36996
37327
|
id: "rn-no-deprecated-modules",
|
|
@@ -37027,6 +37358,18 @@ const reactDoctorRules = [
|
|
|
37027
37358
|
tags: [...new Set(["react-native", ...rnNoFalsyAndRender.tags ?? []])]
|
|
37028
37359
|
}
|
|
37029
37360
|
},
|
|
37361
|
+
{
|
|
37362
|
+
key: "react-doctor/rn-no-image-children",
|
|
37363
|
+
id: "rn-no-image-children",
|
|
37364
|
+
source: "react-doctor",
|
|
37365
|
+
originallyExternal: false,
|
|
37366
|
+
rule: {
|
|
37367
|
+
...rnNoImageChildren,
|
|
37368
|
+
framework: "react-native",
|
|
37369
|
+
category: "Bugs",
|
|
37370
|
+
tags: [...new Set(["react-native", ...rnNoImageChildren.tags ?? []])]
|
|
37371
|
+
}
|
|
37372
|
+
},
|
|
37030
37373
|
{
|
|
37031
37374
|
key: "react-doctor/rn-no-inline-flatlist-renderitem",
|
|
37032
37375
|
id: "rn-no-inline-flatlist-renderitem",
|
|
@@ -37087,6 +37430,18 @@ const reactDoctorRules = [
|
|
|
37087
37430
|
tags: [...new Set(["react-native", ...rnNoNonNativeNavigator.tags ?? []])]
|
|
37088
37431
|
}
|
|
37089
37432
|
},
|
|
37433
|
+
{
|
|
37434
|
+
key: "react-doctor/rn-no-panresponder",
|
|
37435
|
+
id: "rn-no-panresponder",
|
|
37436
|
+
source: "react-doctor",
|
|
37437
|
+
originallyExternal: false,
|
|
37438
|
+
rule: {
|
|
37439
|
+
...rnNoPanresponder,
|
|
37440
|
+
framework: "react-native",
|
|
37441
|
+
category: "Bugs",
|
|
37442
|
+
tags: [...new Set(["react-native", ...rnNoPanresponder.tags ?? []])]
|
|
37443
|
+
}
|
|
37444
|
+
},
|
|
37090
37445
|
{
|
|
37091
37446
|
key: "react-doctor/rn-no-raw-text",
|
|
37092
37447
|
id: "rn-no-raw-text",
|
|
@@ -37135,6 +37490,18 @@ const reactDoctorRules = [
|
|
|
37135
37490
|
tags: [...new Set(["react-native", ...rnNoScrollviewMappedList.tags ?? []])]
|
|
37136
37491
|
}
|
|
37137
37492
|
},
|
|
37493
|
+
{
|
|
37494
|
+
key: "react-doctor/rn-no-set-native-props",
|
|
37495
|
+
id: "rn-no-set-native-props",
|
|
37496
|
+
source: "react-doctor",
|
|
37497
|
+
originallyExternal: false,
|
|
37498
|
+
rule: {
|
|
37499
|
+
...rnNoSetNativeProps,
|
|
37500
|
+
framework: "react-native",
|
|
37501
|
+
category: "Bugs",
|
|
37502
|
+
tags: [...new Set(["react-native", ...rnNoSetNativeProps.tags ?? []])]
|
|
37503
|
+
}
|
|
37504
|
+
},
|
|
37138
37505
|
{
|
|
37139
37506
|
key: "react-doctor/rn-no-single-element-style-array",
|
|
37140
37507
|
id: "rn-no-single-element-style-array",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "oxlint-plugin-react-doctor",
|
|
3
|
-
"version": "0.2.14-dev.
|
|
3
|
+
"version": "0.2.14-dev.bdb9e36",
|
|
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",
|
|
@@ -43,11 +43,11 @@
|
|
|
43
43
|
"dependencies": {
|
|
44
44
|
"@typescript-eslint/types": "^8.59.3",
|
|
45
45
|
"eslint-scope": "^9.1.2",
|
|
46
|
-
"eslint-visitor-keys": "^5.0.1"
|
|
46
|
+
"eslint-visitor-keys": "^5.0.1",
|
|
47
|
+
"oxc-parser": "^0.132.0"
|
|
47
48
|
},
|
|
48
49
|
"devDependencies": {
|
|
49
|
-
"@types/node": "^25.6.0"
|
|
50
|
-
"oxc-parser": "^0.132.0"
|
|
50
|
+
"@types/node": "^25.6.0"
|
|
51
51
|
},
|
|
52
52
|
"engines": {
|
|
53
53
|
"node": "^20.19.0 || >=22.12.0"
|