Skip to content

Commit 3790c6d

Browse files
authored
feat(shared): add version detection logic; fix(plugins/x): 'no-leaked-conditi… (#864)
1 parent 3d9c663 commit 3790c6d

File tree

7 files changed

+119
-34
lines changed

7 files changed

+119
-34
lines changed

packages/plugins/eslint-plugin-react-x/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
"@typescript-eslint/type-utils": "^8.15.0",
6060
"@typescript-eslint/types": "^8.15.0",
6161
"@typescript-eslint/utils": "^8.15.0",
62+
"compare-versions": "^6.1.1",
6263
"is-immutable-type": "5.0.0",
6364
"ts-pattern": "^5.5.0"
6465
},

packages/plugins/eslint-plugin-react-x/src/rules/no-leaked-conditional-rendering.spec.ts

+55
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@ ruleTesterWithTypes.run(RULE_NAME, rule, {
1313
errors: [
1414
{ messageId: "noLeakedConditionalRendering" },
1515
],
16+
settings: {
17+
"react-x": {
18+
version: "17.0.0",
19+
},
20+
},
1621
},
1722
{
1823
code: /* tsx */ `
@@ -25,6 +30,28 @@ ruleTesterWithTypes.run(RULE_NAME, rule, {
2530
errors: [
2631
{ messageId: "noLeakedConditionalRendering" },
2732
],
33+
settings: {
34+
"react-x": {
35+
version: "17.0.0",
36+
},
37+
},
38+
},
39+
{
40+
code: /* tsx */ `
41+
/// <reference types="react" />
42+
/// <reference types="react-dom" />
43+
44+
const anyString = Math.random() > 0.5 ? "" : "foo";
45+
const a = <>{anyString && <Something />}</>;
46+
`,
47+
errors: [
48+
{ messageId: "noLeakedConditionalRendering" },
49+
],
50+
settings: {
51+
"react-x": {
52+
version: "17.0.0",
53+
},
54+
},
2855
},
2956
{
3057
code: /* tsx */ `
@@ -683,5 +710,33 @@ ruleTesterWithTypes.run(RULE_NAME, rule, {
683710
);
684711
};
685712
`,
713+
{
714+
code: /* tsx */ `
715+
/// <reference types="react" />
716+
/// <reference types="react-dom" />
717+
718+
const someString = "";
719+
const a = <>{someString && <Something />}</>;
720+
`,
721+
settings: {
722+
"react-x": {
723+
version: "18.0.0",
724+
},
725+
},
726+
},
727+
{
728+
code: /* tsx */ `
729+
/// <reference types="react" />
730+
/// <reference types="react-dom" />
731+
732+
const anyString = Math.random() > 0.5 ? "" : "foo";
733+
const a = <>{anyString && <Something />}</>;
734+
`,
735+
settings: {
736+
"react-x": {
737+
version: "18.0.0",
738+
},
739+
},
740+
},
686741
],
687742
});

packages/plugins/eslint-plugin-react-x/src/rules/no-leaked-conditional-rendering.ts

+21-14
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import * as AST from "@eslint-react/ast";
2+
import { decodeSettings, normalizeSettings } from "@eslint-react/shared";
23
import { F, O } from "@eslint-react/tools";
34
import * as VAR from "@eslint-react/var";
45
import type { Variable } from "@typescript-eslint/scope-manager";
@@ -8,6 +9,7 @@ import { AST_NODE_TYPES } from "@typescript-eslint/types";
89
import { ESLintUtils } from "@typescript-eslint/utils";
910
import { getStaticValue } from "@typescript-eslint/utils/ast-utils";
1011
import type { ReportDescriptor } from "@typescript-eslint/utils/ts-eslint";
12+
import { compare } from "compare-versions";
1113
import type { CamelCase } from "string-ts";
1214
import { isFalseLiteralType, isTrueLiteralType, isTypeFlagSet, unionTypeParts } from "ts-api-utils";
1315
import { isMatching, match, P } from "ts-pattern";
@@ -48,20 +50,6 @@ type VariantType =
4850
| "truthy string";
4951
/* eslint-enable perfectionist/sort-union-types */
5052

51-
// Allowed left node type variants
52-
const allowedVariants = [
53-
"any",
54-
"boolean",
55-
"nullish",
56-
"object",
57-
"string",
58-
"falsy boolean",
59-
"truthy bigint",
60-
"truthy boolean",
61-
"truthy number",
62-
"truthy string",
63-
] as const satisfies VariantType[];
64-
6553
// #endregion
6654

6755
// #region Helpers
@@ -223,6 +211,25 @@ export default createRule<[], MessageID>({
223211
name: RULE_NAME,
224212
create(context) {
225213
if (!context.sourceCode.text.includes("&&") && !context.sourceCode.text.includes("?")) return {};
214+
215+
const { version } = normalizeSettings(decodeSettings(context.settings));
216+
217+
// Allowed left node type variants
218+
const allowedVariants = [
219+
"any",
220+
"boolean",
221+
"nullish",
222+
"object",
223+
"falsy boolean",
224+
"truthy bigint",
225+
"truthy boolean",
226+
"truthy number",
227+
"truthy string",
228+
...compare(version, "18.0.0", "<")
229+
? []
230+
: ["string", "falsy string"] as const,
231+
] as const satisfies VariantType[];
232+
226233
const services = ESLintUtils.getParserServices(context, false);
227234
function getReportDescriptor(node: TSESTree.Expression): O.Option<ReportDescriptor<MessageID>> {
228235
return match<typeof node, O.Option<ReportDescriptor<MessageID>>>(node)

packages/shared/package.json

+3-1
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,9 @@
4444
"dependencies": {
4545
"@eslint-react/tools": "workspace:*",
4646
"@typescript-eslint/utils": "^8.15.0",
47-
"picomatch": "^4.0.2"
47+
"local-pkg": "^0.5.1",
48+
"picomatch": "^4.0.2",
49+
"ts-pattern": "^5.5.0"
4850
},
4951
"devDependencies": {
5052
"@types/picomatch": "^3.0.1",

packages/shared/src/schemas.ts

+1
Original file line numberDiff line numberDiff line change
@@ -195,4 +195,5 @@ export type ESLintSettings = InferOutput<typeof ESLintSettingsSchema>;
195195
export interface ESLintReactSettingsNormalized extends ESLintReactSettings {
196196
additionalComponents: CustomComponentNormalized[];
197197
components: Map<string, string>;
198+
version: string;
198199
}

packages/shared/src/settings.ts

+5
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import { Data, F } from "@eslint-react/tools";
22
import { shallowEqual } from "fast-equals";
3+
import { getPackageInfoSync } from "local-pkg";
34
import memoize from "micro-memoize";
45
import pm from "picomatch";
6+
import { match, P } from "ts-pattern";
57
import type { PartialDeep } from "type-fest";
68
import { parse } from "valibot";
79

@@ -72,6 +74,9 @@ export const normalizeSettings = memoize((settings: ESLintReactSettings) => {
7274
if (!/^[\w-]+$/u.test(name)) return acc;
7375
return acc.set(name, as);
7476
}, new Map<string, string>()),
77+
version: match(settings.version)
78+
.with(P.union(P.nullish, "", "detect"), () => getPackageInfoSync("react")?.version)
79+
.otherwise(F.identity) ?? "18.3.1",
7580
});
7681
}, { isEqual: shallowEqual });
7782

pnpm-lock.yaml

+33-19
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)