Skip to content

Commit 9e92624

Browse files
committed
Tested and finished no-destructure rule! Now part of recommended config.
1 parent 990209b commit 9e92624

File tree

6 files changed

+474
-65
lines changed

6 files changed

+474
-65
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ would like to use.
7373
| :---: | :---: | :--- | :--- |
7474
|| 🔧 | [solid/jsx-no-undef](docs/jsx-no-undef.md) | Disallow references to undefined variables in JSX. Handles custom directives. |
7575
|| | [solid/jsx-uses-vars](docs/jsx-uses-vars.md) | Prevent variables used in JSX from being marked as unused. |
76+
|| 🔧 | [solid/no-destructure](docs/no-destructure.md) | Prevent destructuring props. In Solid, props must be used with property accesses (`props.foo`) to preserve reactivity. This rule only tracks destructuring in the parameter list. |
7677
|| 🔧 | [solid/no-innerhtml](docs/no-innerhtml.md) | Disallow usage of the innerHTML attribute, which can often lead to security vulnerabilities. |
7778
|| 🔧 | [solid/no-react-specific-props](docs/no-react-specific-props.md) | Disallow usage of React-specific `className`/`htmlFor` props (though they are supported for compatibility). |
7879
|| | [solid/no-unknown-namespaces](docs/no-unknown-namespaces.md) | Enforce using only Solid-specific namespaced attribute names (i.e. `'on:'` in `<div on:click={...} />`). |

docs/no-destructure.md

Lines changed: 166 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
11
<!-- AUTO-GENERATED-CONTENT:START (HEADER) -->
2+
# solid/no-destructure
3+
Prevent destructuring props. In Solid, props must be used with property accesses (`props.foo`) to preserve reactivity. This rule only tracks destructuring in the parameter list.
4+
This rule is **an error** by default.
5+
6+
[View source](../src/rules/no-destructure.ts) · [View tests](../test/rules/no-destructure.test.ts)
27

38
<!-- AUTO-GENERATED-CONTENT:END -->
49

@@ -26,12 +31,150 @@ let Component = (props) => <div a={props.a} />;
2631

2732
let Component = ({ a: A }) => <div a={A} />;
2833
// after eslint --fix:
29-
let Component = (props) => <div a={props.a} />;
34+
let Component = (props) => <div a={props["a"]} />;
3035

3136
let Component = ({ ["a" + ""]: a }) => <div a={a} />;
3237
// after eslint --fix:
3338
let Component = (props) => <div a={props["a" + ""]} />;
3439

40+
let Component = ({ ["a" + ""]: a, b }) => <div a={a} b={b} />;
41+
// after eslint --fix:
42+
let Component = (props) => <div a={props["a" + ""]} b={props.b} />;
43+
44+
let Component = ({ a = 5 }) => <div a={a} />;
45+
// after eslint --fix:
46+
let Component = (props) => {
47+
props = mergeProps({ a: 5 }, props);
48+
return <div a={props.a} />;
49+
};
50+
51+
let Component = ({ a: A = 5 }) => <div a={A} />;
52+
// after eslint --fix:
53+
let Component = (props) => {
54+
props = mergeProps({ a: 5 }, props);
55+
return <div a={props.a} />;
56+
};
57+
58+
let Component = ({ a: A = 5 }) => <div a={A} />;
59+
// after eslint --fix:
60+
let Component = (props) => {
61+
props = mergeProps({ a: 5 }, props);
62+
return <div a={props["a"]} />;
63+
};
64+
65+
let Component = ({ ["a" + ""]: a = 5 }) => <div a={a} />;
66+
// after eslint --fix:
67+
let Component = (props) => {
68+
props = mergeProps({ ["a" + ""]: 5 }, props);
69+
return <div a={props["a" + ""]} />;
70+
};
71+
72+
let Component = ({ ["a" + ""]: a = 5, b = 10, c }) => <div a={a} b={b} c={c} />;
73+
// after eslint --fix:
74+
let Component = (props) => {
75+
props = mergeProps({ ["a" + ""]: 5, b: 10 }, props);
76+
return <div a={props["a" + ""]} b={props.b} c={props.c} />;
77+
};
78+
79+
let Component = ({ a = 5 }) => {
80+
return <div a={a} />;
81+
};
82+
// after eslint --fix:
83+
let Component = (props) => {
84+
props = mergeProps({ a: 5 }, props);
85+
86+
return <div a={props.a} />;
87+
};
88+
89+
let Component = ({ a = 5 }) => {
90+
various();
91+
statements();
92+
return <div a={a} />;
93+
};
94+
// after eslint --fix:
95+
let Component = (props) => {
96+
props = mergeProps({ a: 5 }, props);
97+
98+
various();
99+
statements();
100+
return <div a={props.a} />;
101+
};
102+
103+
let Component = ({ ...rest }) => <div a={rest.a} />;
104+
// after eslint --fix:
105+
let Component = (_props) => {
106+
const [props, rest] = splitProps(_props, []);
107+
return <div a={rest.a} />;
108+
};
109+
110+
let Component = ({ a, ...rest }) => <div a={a} />;
111+
// after eslint --fix:
112+
let Component = (_props) => {
113+
const [props, rest] = splitProps(_props, ["a"]);
114+
return <div a={props.a} />;
115+
};
116+
117+
let Component = ({ a, ...other }) => <div a={a} />;
118+
// after eslint --fix:
119+
let Component = (_props) => {
120+
const [props, other] = splitProps(_props, ["a"]);
121+
return <div a={props.a} />;
122+
};
123+
124+
let Component = ({ a, ...rest }) => <div a={a} b={rest.b} />;
125+
// after eslint --fix:
126+
let Component = (_props) => {
127+
const [props, rest] = splitProps(_props, ["a"]);
128+
return <div a={props.a} b={rest.b} />;
129+
};
130+
131+
let Component = ({ a: A, ...rest }) => <div a={A} />;
132+
// after eslint --fix:
133+
let Component = (_props) => {
134+
const [props, rest] = splitProps(_props, ["a"]);
135+
return <div a={props.a} />;
136+
};
137+
138+
let Component = ({ a: A, ...rest }) => <div a={A} />;
139+
// after eslint --fix:
140+
let Component = (_props) => {
141+
const [props, rest] = splitProps(_props, ["a"]);
142+
return <div a={props["a"]} />;
143+
};
144+
145+
let Component = ({ ["a" + ""]: A, ...rest }) => <div a={A} />;
146+
// after eslint --fix:
147+
let Component = (_props) => {
148+
const [props, rest] = splitProps(_props, ["a" + ""]);
149+
return <div a={props["a" + ""]} />;
150+
};
151+
152+
let Component = ({ ["a" + ""]: A, ...rest }) => <div a={A} b={rest.b} />;
153+
// after eslint --fix:
154+
let Component = (_props) => {
155+
const [props, rest] = splitProps(_props, ["a" + ""]);
156+
return <div a={props["a" + ""]} b={rest.b} />;
157+
};
158+
159+
let Component = ({ a = 5, ...rest }) => {
160+
return <div a={a} b={rest.b} />;
161+
};
162+
// after eslint --fix:
163+
let Component = (_props) => {
164+
_props = mergeProps({ a: 5 }, _props);
165+
const [props, rest] = splitProps(_props, ["a"]);
166+
167+
return <div a={props.a} b={rest.b} />;
168+
};
169+
170+
let Component = ({ ["a" + ""]: A = 5, ...rest }) => <div a={A} b={rest.b} />;
171+
// after eslint --fix:
172+
let Component = (_props) => {
173+
_props = mergeProps({ ["a" + ""]: 5 }, _props);
174+
const [props, rest] = splitProps(_props, ["a" + ""]);
175+
return <div a={props["a" + ""]} b={rest.b} />;
176+
};
177+
35178
```
36179

37180
### Valid Examples
@@ -43,6 +186,10 @@ let Component = (props) => <div />;
43186

44187
let Component = (props) => <div />;
45188

189+
let Component = (props) => {
190+
return <div />;
191+
};
192+
46193
let Component = (props) => null;
47194

48195
let Component = (props) => <div a={props.a} />;
@@ -57,7 +204,24 @@ let Component = (props) => {
57204
return <div a={a} />;
58205
};
59206

60-
let Component = ({ a }, more, params) => <div a={a} />;
207+
let NotAComponent = ({ a }, more, params) => <div a={a} />;
208+
209+
let Component = (props) => {
210+
let inner = ({ a, ...rest }) => a;
211+
let a = inner({ a: 5 });
212+
return <div a={a} />;
213+
};
214+
215+
// This one might be surprising, since we're clearly destructuring props!
216+
// But this will be caught as a reactive expression use outside of
217+
// a tracked scope, in the "solid/reactivity" rule. There's really
218+
// nothing wrong with destructuring props in tracked scopes when done
219+
// correctly, but catching it in the params covers the most common
220+
// cases with good DX.
221+
let Component = (props) => {
222+
let { a } = props;
223+
return <div a={a} />;
224+
};
61225

62226
```
63227
<!-- AUTO-GENERATED-CONTENT:END -->

src/index.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import jsxNoUndef from "./rules/jsx-no-undef";
22
import jsxUsesVars from "./rules/jsx-uses-vars";
3-
// import noDestructure from './rules/no-destructure';
3+
import noDestructure from "./rules/no-destructure";
44
import noInnerHTML from "./rules/no-innerhtml";
55
import noReactSpecificProps from "./rules/no-react-specific-props";
66
import noUnknownNamespaces from "./rules/no-unknown-namespaces";
@@ -13,7 +13,7 @@ import styleProp from "./rules/style-prop";
1313
const allRules = {
1414
"jsx-no-undef": jsxNoUndef,
1515
"jsx-uses-vars": jsxUsesVars,
16-
// 'no-destructure': noDestructure,
16+
"no-destructure": noDestructure,
1717
"no-innerhtml": noInnerHTML,
1818
"no-react-specific-props": noReactSpecificProps,
1919
"no-unknown-namespaces": noUnknownNamespaces,
@@ -50,6 +50,8 @@ module.exports = {
5050
"solid/no-innerhtml": [2, { allowStatic: true }],
5151
"solid/prefer-for": 2,
5252
"solid/style-prop": 2,
53+
// reactivity
54+
"solid/no-destructure": 2,
5355
// these rules are mostly style suggestions
5456
"solid/no-react-specific-props": 1,
5557
"solid/prefer-classlist": 1,

0 commit comments

Comments
 (0)