tsl-react 0.0.3-next.3 → 0.0.3-next.4
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 +5 -3
- package/dist/index.js +115 -63
- package/package.json +8 -8
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import { Rule } from
|
|
1
|
+
import { Rule } from "tsl";
|
|
2
|
+
|
|
3
|
+
//#region src/rules/no-leaked-conditional-rendering.d.ts
|
|
2
4
|
|
|
3
5
|
/**
|
|
4
6
|
* Prevents problematic leaked values from being rendered.
|
|
@@ -52,5 +54,5 @@ import { Rule } from 'tsl';
|
|
|
52
54
|
* @since 0.0.0
|
|
53
55
|
*/
|
|
54
56
|
declare const noLeakedConditionalRendering: (options?: "off" | undefined) => Rule<unknown>;
|
|
55
|
-
|
|
56
|
-
export { noLeakedConditionalRendering };
|
|
57
|
+
//#endregion
|
|
58
|
+
export { noLeakedConditionalRendering };
|
package/dist/index.js
CHANGED
|
@@ -1,66 +1,118 @@
|
|
|
1
|
-
import { compare } from
|
|
2
|
-
import { defineRule } from
|
|
3
|
-
import { SyntaxKind } from
|
|
4
|
-
import { isLogicalNegationExpression } from
|
|
5
|
-
import { unit } from
|
|
6
|
-
import { Report } from
|
|
7
|
-
import { getAnalyzerOptions } from
|
|
8
|
-
import * as RA from
|
|
1
|
+
import { compare } from "compare-versions";
|
|
2
|
+
import { defineRule } from "tsl";
|
|
3
|
+
import { SyntaxKind } from "typescript";
|
|
4
|
+
import { isLogicalNegationExpression } from "@react-analyzer/ast";
|
|
5
|
+
import { unit } from "@react-analyzer/eff";
|
|
6
|
+
import { Report } from "@react-analyzer/kit";
|
|
7
|
+
import { getAnalyzerOptions } from "@react-analyzer/shared";
|
|
8
|
+
import * as RA from "@react-analyzer/core";
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
10
|
+
//#region src/rules/no-leaked-conditional-rendering.ts
|
|
11
|
+
/** @internal */
|
|
12
|
+
const messages = { noLeakedConditionalRendering: (p) => `Potential leaked value ${p.value} that might cause unintentionally rendered values or rendering crashes.` };
|
|
13
|
+
/**
|
|
14
|
+
* Prevents problematic leaked values from being rendered.
|
|
15
|
+
*
|
|
16
|
+
* Using the && operator to render some element conditionally in JSX can cause unexpected values being rendered, or even crashing the rendering.
|
|
17
|
+
*
|
|
18
|
+
* **Examples**
|
|
19
|
+
*
|
|
20
|
+
* ```tsx
|
|
21
|
+
* import React from "react";
|
|
22
|
+
*
|
|
23
|
+
* interface MyComponentProps {
|
|
24
|
+
* count: number;
|
|
25
|
+
* }
|
|
26
|
+
*
|
|
27
|
+
* function MyComponent({ count }: MyComponentProps) {
|
|
28
|
+
* return <div>{count && <span>There are {count} results</span>}</div>;
|
|
29
|
+
* // ^^^^^
|
|
30
|
+
* // - Potential leaked value 'count' that might cause unintentionally rendered values or rendering crashes.
|
|
31
|
+
* }
|
|
32
|
+
* ```
|
|
33
|
+
*
|
|
34
|
+
* ```tsx
|
|
35
|
+
* import React from "react";
|
|
36
|
+
*
|
|
37
|
+
* interface MyComponentProps {
|
|
38
|
+
* items: string[];
|
|
39
|
+
* }
|
|
40
|
+
*
|
|
41
|
+
* function MyComponent({ items }: MyComponentProps) {
|
|
42
|
+
* return <div>{items.length && <List items={items} />}</div>;
|
|
43
|
+
* // ^^^^^^^^^^^^
|
|
44
|
+
* // - Potential leaked value 'items.length' that might cause unintentionally rendered values or rendering crashes.
|
|
45
|
+
* }
|
|
46
|
+
* ```
|
|
47
|
+
*
|
|
48
|
+
* ```tsx
|
|
49
|
+
* import React from "react";
|
|
50
|
+
*
|
|
51
|
+
* interface MyComponentProps {
|
|
52
|
+
* items: string[];
|
|
53
|
+
* }
|
|
54
|
+
*
|
|
55
|
+
* function MyComponent({ items }: MyComponentProps) {
|
|
56
|
+
* return <div>{items[0] && <List items={items} />}</div>;
|
|
57
|
+
* // ^^^^^^^^
|
|
58
|
+
* // - Potential leaked value 'items[0]' that might cause unintentionally rendered values or rendering crashes.
|
|
59
|
+
* }
|
|
60
|
+
* ```
|
|
61
|
+
*
|
|
62
|
+
* @since 0.0.0
|
|
63
|
+
*/
|
|
64
|
+
const noLeakedConditionalRendering = defineRule(() => {
|
|
65
|
+
return {
|
|
66
|
+
name: "@react-analyzer/noLeakedConditionalRendering",
|
|
67
|
+
createData(ctx) {
|
|
68
|
+
const { version } = getAnalyzerOptions();
|
|
69
|
+
const state = { isWithinJsxExpression: false };
|
|
70
|
+
const allowedVariants = [
|
|
71
|
+
"any",
|
|
72
|
+
"boolean",
|
|
73
|
+
"nullish",
|
|
74
|
+
"object",
|
|
75
|
+
"falsy boolean",
|
|
76
|
+
"truthy bigint",
|
|
77
|
+
"truthy boolean",
|
|
78
|
+
"truthy number",
|
|
79
|
+
"truthy string",
|
|
80
|
+
...compare(version, "18.0.0", "<") ? [] : ["string", "falsy string"]
|
|
81
|
+
];
|
|
82
|
+
function getReportDescriptor(node) {
|
|
83
|
+
if (isLogicalNegationExpression(node.left)) return unit;
|
|
84
|
+
const leftType = ctx.utils.getConstrainedTypeAtLocation(node.left);
|
|
85
|
+
const leftTypeVariants = RA.getVariantsOfTypes(ctx.utils.unionConstituents(leftType));
|
|
86
|
+
const areAllLeftTypeVariantsAllowed = Array.from(leftTypeVariants.values()).every((type) => allowedVariants.some((allowed) => allowed === type));
|
|
87
|
+
if (!areAllLeftTypeVariantsAllowed) return {
|
|
88
|
+
node: node.left,
|
|
89
|
+
message: messages.noLeakedConditionalRendering({ value: node.left.getText() })
|
|
90
|
+
};
|
|
91
|
+
return unit;
|
|
92
|
+
}
|
|
93
|
+
return {
|
|
94
|
+
state,
|
|
95
|
+
version,
|
|
96
|
+
allowedVariants,
|
|
97
|
+
getReportDescriptor
|
|
98
|
+
};
|
|
99
|
+
},
|
|
100
|
+
visitor: {
|
|
101
|
+
JsxExpression(ctx) {
|
|
102
|
+
ctx.data.state.isWithinJsxExpression = true;
|
|
103
|
+
},
|
|
104
|
+
JsxExpression_exit(ctx) {
|
|
105
|
+
ctx.data.state.isWithinJsxExpression = false;
|
|
106
|
+
},
|
|
107
|
+
BinaryExpression(ctx, node) {
|
|
108
|
+
const { state, getReportDescriptor } = ctx.data;
|
|
109
|
+
if (!state.isWithinJsxExpression) return;
|
|
110
|
+
if (node.operatorToken.kind !== SyntaxKind.AmpersandAmpersandToken) return;
|
|
111
|
+
Report.report(ctx, getReportDescriptor(node));
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
};
|
|
64
115
|
});
|
|
65
116
|
|
|
66
|
-
|
|
117
|
+
//#endregion
|
|
118
|
+
export { noLeakedConditionalRendering };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tsl-react",
|
|
3
|
-
"version": "0.0.3-next.
|
|
3
|
+
"version": "0.0.3-next.4",
|
|
4
4
|
"description": "A unified plugin that combines all individual plugins from the react-analyzer monorepo into one.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react",
|
|
@@ -28,14 +28,14 @@
|
|
|
28
28
|
],
|
|
29
29
|
"dependencies": {
|
|
30
30
|
"compare-versions": "^6.1.1",
|
|
31
|
-
"@react-analyzer/
|
|
32
|
-
"@react-analyzer/
|
|
33
|
-
"@react-analyzer/eff": "0.0.3-next.
|
|
34
|
-
"@react-analyzer/kit": "0.0.3-next.
|
|
35
|
-
"@react-analyzer/shared": "0.0.3-next.
|
|
31
|
+
"@react-analyzer/core": "0.0.3-next.4",
|
|
32
|
+
"@react-analyzer/ast": "0.0.3-next.4",
|
|
33
|
+
"@react-analyzer/eff": "0.0.3-next.4",
|
|
34
|
+
"@react-analyzer/kit": "0.0.3-next.4",
|
|
35
|
+
"@react-analyzer/shared": "0.0.3-next.4"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
|
-
"
|
|
38
|
+
"tsdown": "^0.14.1",
|
|
39
39
|
"@local/configs": "0.0.0"
|
|
40
40
|
},
|
|
41
41
|
"peerDependencies": {
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
"access": "public"
|
|
51
51
|
},
|
|
52
52
|
"scripts": {
|
|
53
|
-
"build": "
|
|
53
|
+
"build": "tsdown --dts-resolve",
|
|
54
54
|
"build:docs": "typedoc",
|
|
55
55
|
"lint:publish": "pnpm publint",
|
|
56
56
|
"lint:ts": "tsl"
|