vite-plugin-solid-undestructure 0.1.1 → 0.2.0
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/README.md +51 -0
- package/dist/eslint/index.d.ts +28 -0
- package/dist/eslint/index.js +1 -0
- package/dist/eslint/modules/processor.d.ts +15 -0
- package/dist/eslint/modules/transform.d.ts +11 -0
- package/dist/index.js +1 -1
- package/package.json +24 -3
package/README.md
CHANGED
|
@@ -202,3 +202,54 @@ function TestComponent(_props) {
|
|
|
202
202
|
```bash
|
|
203
203
|
bun test
|
|
204
204
|
```
|
|
205
|
+
|
|
206
|
+
## ESLint Integration
|
|
207
|
+
|
|
208
|
+
Since `eslint-plugin-solid`'s `solid/reactivity` rule doesn't know about this plugin, it will flag destructured props as non-reactive. The bundled ESLint processor fixes this by teaching the rule about destructured props.
|
|
209
|
+
|
|
210
|
+
### Setup
|
|
211
|
+
|
|
212
|
+
Install `eslint-plugin-solid`:
|
|
213
|
+
|
|
214
|
+
```bash
|
|
215
|
+
bun add -D eslint-plugin-solid
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
Add the processor to your ESLint config:
|
|
219
|
+
|
|
220
|
+
```js
|
|
221
|
+
// eslint.config.js
|
|
222
|
+
import solid from 'eslint-plugin-solid'
|
|
223
|
+
import solidUndestructure from 'vite-plugin-solid-undestructure/eslint'
|
|
224
|
+
|
|
225
|
+
export default [
|
|
226
|
+
solidUndestructure.configs.recommended,
|
|
227
|
+
solid.configs['flat/typescript'],
|
|
228
|
+
{
|
|
229
|
+
rules: {
|
|
230
|
+
'solid/no-destructure': 'off'
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
]
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
### How it works
|
|
237
|
+
|
|
238
|
+
The processor transparently rewrites destructured props into `props.X` member expressions before the linter runs, so the existing `solid/reactivity` rule can correctly identify untracked reactive usages. Error messages are adjusted to reference the original destructured name.
|
|
239
|
+
|
|
240
|
+
```tsx
|
|
241
|
+
// Without the processor, solid/reactivity ignores `size` (not recognized as reactive)
|
|
242
|
+
// With the processor, it correctly warns:
|
|
243
|
+
function ExampleComponent({ size }: { size: 'sm' | 'lg' }) {
|
|
244
|
+
const dimensions =
|
|
245
|
+
// ↓ The reactive variable 'size' should be used within JSX, a tracked scope
|
|
246
|
+
// (like createEffect), or inside an event handler. [solid/reactivity]
|
|
247
|
+
size === 'sm' ? { width: 4, height: 4 } : { width: 8, height: 8 }
|
|
248
|
+
|
|
249
|
+
// Correct usages that don't cause warnings:
|
|
250
|
+
// const dimensions = () => size === 'sm' ? ...
|
|
251
|
+
// const dimensions = createMemo(() => size === 'sm' ? ...)
|
|
252
|
+
|
|
253
|
+
return <img src="..." alt="..." {...dimensions()} />
|
|
254
|
+
}
|
|
255
|
+
```
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
declare const plugin: {
|
|
2
|
+
meta: {
|
|
3
|
+
name: string;
|
|
4
|
+
version: string;
|
|
5
|
+
};
|
|
6
|
+
processors: {
|
|
7
|
+
'solid-undestructure': {
|
|
8
|
+
meta: {
|
|
9
|
+
name: string;
|
|
10
|
+
version: string;
|
|
11
|
+
};
|
|
12
|
+
preprocess(text: string, filename: string): string[];
|
|
13
|
+
postprocess(messages: {
|
|
14
|
+
ruleId: string | null;
|
|
15
|
+
message: string;
|
|
16
|
+
}[][], filename: string): {
|
|
17
|
+
ruleId: string | null;
|
|
18
|
+
message: string;
|
|
19
|
+
}[];
|
|
20
|
+
supportsAutofix: false;
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
configs: Record<string, {
|
|
24
|
+
plugins: Record<string, unknown>;
|
|
25
|
+
processor: string;
|
|
26
|
+
}>;
|
|
27
|
+
};
|
|
28
|
+
export default plugin;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{checkIfComponent as E}from"@/modules/component-detector";import X from"@babel/generator";import{parse as T}from"@babel/parser";import H from"@babel/traverse";import*as u from"@babel/types";var v=H.default??H,C=X.default??X;function L(q){if(!/\(\s*\{/.test(q))return null;let z=new Map,B=!1;try{let Z=T(q,{sourceType:"module",plugins:["typescript","jsx"]});if(v(Z,{Function(G){let _=G.node.params;if(_.length!==1)return;let U=_[0];if(!u.isObjectPattern(U))return;if(!E(G))return;I(G,U,z),B=!0}}),!B)return null;return{code:C(Z,{retainLines:!0,compact:!1}).code,propMappings:z}}catch(Q){return console.warn("Failed to transform:",Q),null}}function I(q,z,B){let Q=u.identifier("props");if(z.typeAnnotation)Q.typeAnnotation=z.typeAnnotation;let Z=[],W=new Map,G=new Map;function _($,V=[]){for(let R of $.properties){if(u.isRestElement(R))continue;if(!u.isObjectProperty(R))continue;let J=null;if(u.isIdentifier(R.key))J=R.key.name;else if(u.isStringLiteral(R.key))J=R.key.value;if(!J)continue;let Y=[...V,J];if(u.isObjectPattern(R.value)){_(R.value,Y);continue}let w=null;if(u.isIdentifier(R.value))w=R.value.name;else if(u.isAssignmentPattern(R.value)&&u.isIdentifier(R.value.left))w=R.value.left.name;if(!w)continue;if(V.length===0)Z.push(w),W.set(w,J),B.set(w,J);else G.set(w,Y),B.set(w,Y.join("."))}}_(z),q.node.params[0]=Q;let U=q.get("body");if(Array.isArray(U))return;let D={Identifier($){let V=$.parent;if(u.isMemberExpression(V)&&V.property===$.node&&!V.computed||u.isObjectProperty(V)&&V.key===$.node&&!V.computed)return;if($.isBindingIdentifier())return;let R=$,J=R.node.name,Y=G.get(J);if(Y){let w=u.memberExpression(u.identifier("props"),u.identifier(Y[0]));for(let F=1;F<Y.length;F++)w=u.memberExpression(w,u.identifier(Y[F]));R.replaceWith(w)}else if(Z.includes(J)){let w=W.get(J)??J;R.replaceWith(u.memberExpression(u.identifier("props"),u.identifier(w)))}}};U.traverse(D)}var A=new Map,O={meta:{name:"solid-undestructure",version:"0.1.1"},preprocess(q,z){if(!/\.(tsx?|jsx?)$/.test(z))return[q];let B=L(q);if(!B)return A.delete(z),[q];return A.set(z,B),[B.code]},postprocess(q,z){let B=A.get(z);if(!B)return q[0];A.delete(z);let{propMappings:Q}=B;if(Q.size===0)return q[0];let Z=new Map;for(let[W,G]of Q)Z.set(G,W);return q[0].map((W)=>{let{message:G}=W;for(let[_,U]of Q){let D=U.includes(".")?`props.${U}`:`props.${U}`;G=G.replaceAll(`'${D}'`,`'${_}'`)}return{...W,message:G}})},supportsAutofix:!1};var S={meta:{name:"eslint-plugin-solid-undestructure",version:"0.1.1"},processors:{"solid-undestructure":O},configs:{}};S.configs.recommended={plugins:{"solid-undestructure":S},processor:"solid-undestructure/solid-undestructure"};var i=S;export{i as default};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export declare const processor: {
|
|
2
|
+
meta: {
|
|
3
|
+
name: string;
|
|
4
|
+
version: string;
|
|
5
|
+
};
|
|
6
|
+
preprocess(text: string, filename: string): string[];
|
|
7
|
+
postprocess(messages: {
|
|
8
|
+
ruleId: string | null;
|
|
9
|
+
message: string;
|
|
10
|
+
}[][], filename: string): {
|
|
11
|
+
ruleId: string | null;
|
|
12
|
+
message: string;
|
|
13
|
+
}[];
|
|
14
|
+
supportsAutofix: false;
|
|
15
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export type TransformResult = {
|
|
2
|
+
code: string;
|
|
3
|
+
/** Maps original local name → prop key (e.g. "localName" → "propKey") */
|
|
4
|
+
propMappings: Map<string, string>;
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* Transforms destructured component props into `props.X` member expressions
|
|
8
|
+
* for linting purposes. This is a minimal transformation — no mergeProps/splitProps/imports
|
|
9
|
+
* are added, only the patterns the eslint-plugin-solid reactivity rule needs to see.
|
|
10
|
+
*/
|
|
11
|
+
export declare function transformForLinting(code: string): TransformResult | null;
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import S from"@babel/generator";import{parse as
|
|
1
|
+
import S from"@babel/generator";import{parse as f}from"@babel/parser";import k from"@babel/traverse";import*as I from"@babel/types";import*as H from"@babel/types";function v(Q){if(H.isArrowFunctionExpression(Q.node)&&!H.isBlockStatement(Q.node.body))return H.isJSXElement(Q.node.body)||H.isJSXFragment(Q.node.body);let $=!1;return Q.traverse({ReturnStatement(W){if(W.getFunctionParent()?.node!==Q.node)return;let L=W.node.argument;if(!L)return;if(H.isJSXElement(L)||H.isJSXFragment(L))$=!0,W.stop();if(H.isConditionalExpression(L)){if(H.isJSXElement(L.consequent)||H.isJSXFragment(L.consequent)||H.isJSXElement(L.alternate)||H.isJSXFragment(L.alternate))$=!0,W.stop()}if(H.isLogicalExpression(L)){if(H.isJSXElement(L.left)||H.isJSXFragment(L.left)||H.isJSXElement(L.right)||H.isJSXFragment(L.right))$=!0,W.stop()}if(H.isParenthesizedExpression(L)){let U=L.expression;if(H.isJSXElement(U)||H.isJSXFragment(U))$=!0,W.stop()}}}),$}import*as q from"@babel/types";import*as _ from"@babel/types";function T(Q,$,W){let L=[];if($)L.push("mergeProps");if(W)L.push("splitProps");if(L.length===0)return;let U=Q.node.body,A=new Set;for(let Z of U){if(!_.isImportDeclaration(Z)||Z.source.value!=="solid-js")continue;for(let O of Z.specifiers)if(_.isImportSpecifier(O)&&_.isIdentifier(O.imported))A.add(O.imported.name)}let D=L.filter((Z)=>!A.has(Z));if(D.length===0)return;let R=-1;for(let Z=0;Z<U.length;Z++)if(_.isImportDeclaration(U[Z]))R=Z;let M=D.map((Z)=>_.importDeclaration([_.importSpecifier(_.identifier(Z),_.identifier(Z))],_.stringLiteral("solid-js")));if(R>=0)U.splice(R+1,0,...M);else U.unshift(...M)}function N(Q,$){let W=Q.scope.generateUidIdentifier("props"),L=$.properties,U=[],A=[],D=new Map,R=new Map,M={},Z=!1,O=null;function K(B,F=[]){B.properties.forEach((G)=>{if(q.isObjectProperty(G)){let Y=null;if(q.isIdentifier(G.key))Y=G.key.name;else if(q.isStringLiteral(G.key))Y=G.key.value;if(!Y)return;let C=[...F,Y];if(q.isObjectPattern(G.value)){if(F.length===0)U.push(Y);K(G.value,C)}else{let z=null;if(q.isIdentifier(G.value))z=G.value.name;else if(q.isAssignmentPattern(G.value)){if(q.isIdentifier(G.value.left)){if(z=G.value.left.name,q.isExpression(G.value.right)){if(F.length===0)M[Y]=G.value.right}}}if(F.length===0)if(U.push(Y),z)A.push(z),D.set(z,Y);else A.push(Y),D.set(Y,Y);else if(z)R.set(z,C);else R.set(Y,C)}}})}for(let B of L)if(q.isRestElement(B)){if(Z=!0,q.isIdentifier(B.argument))O=B.argument}else if(q.isObjectProperty(B)){let F=null,G=null;if(q.isIdentifier(B.key))F=B.key.name;else if(q.isStringLiteral(B.key))F=B.key.value;if(!F)continue;if(q.isObjectPattern(B.value)){U.push(F),K(B.value,[F]);continue}if(q.isIdentifier(B.value))G=B.value.name;else if(q.isAssignmentPattern(B.value)&&q.isIdentifier(B.value.left))G=B.value.left.name;if(U.push(F),G)A.push(G),D.set(G,F);else A.push(F),D.set(F,F);if(q.isAssignmentPattern(B.value)&&q.isExpression(B.value.right))M[F]=B.value.right}Q.node.params[0]=W;let j=[],X=Object.keys(M).length>0,E=U.length>0||Z,V;if(X){let B=q.objectExpression(Object.entries(M).map(([F,G])=>q.objectProperty(q.identifier(F),G)));if(E)V=Q.scope.generateUidIdentifier("merged"),j.push(q.variableDeclaration("const",[q.variableDeclarator(V,q.callExpression(q.identifier("mergeProps"),[B,W]))]));else V=Q.scope.generateUidIdentifier("merged"),j.push(q.variableDeclaration("const",[q.variableDeclarator(V,q.callExpression(q.identifier("mergeProps"),[B,W]))]));if(E&&O){let F=q.arrayPattern([null,O]);j.push(q.variableDeclaration("const",[q.variableDeclarator(F,q.callExpression(q.identifier("splitProps"),[V,q.arrayExpression(U.map((G)=>q.stringLiteral(G)))]))]))}}else if(E){if(V=W,O){let B=q.arrayPattern([null,O]);j.push(q.variableDeclaration("const",[q.variableDeclarator(B,q.callExpression(q.identifier("splitProps"),[W,q.arrayExpression(U.map((F)=>q.stringLiteral(F)))]))]))}}else V=W;let w=Q.get("body");if(Array.isArray(w))return;let b={Identifier(B){let F=B.parent;if(q.isMemberExpression(F)&&F.property===B.node&&!F.computed||q.isObjectProperty(F)&&F.key===B.node&&!F.computed)return;let G=B,Y=G.node.name;if(B.isBindingIdentifier())return;let C=R.get(Y);if(C){let z=q.memberExpression(q.identifier(V.name),q.identifier(C[0]));for(let J=1;J<C.length;J++)z=q.memberExpression(z,q.identifier(C[J]));G.replaceWith(z)}else if(A.includes(Y)){let z=D.get(Y)??Y;G.replaceWith(q.memberExpression(q.identifier(V.name),q.identifier(z)))}}};if(w.traverse(b),q.isBlockStatement(Q.node.body))Q.node.body.body.unshift(...j);else if(q.isExpression(Q.node.body)){let B=q.returnStatement(Q.node.body);Q.node.body=q.blockStatement([...j,B])}let x=Q.findParent((B)=>B.isProgram());if(x)T(x,X,E)}var g=k.default??k,y=S.default??S,o=()=>({name:"solid-undestructure",enforce:"pre",transform(Q,$){if(!/\.(tsx?|jsx?)$/.test($))return null;if($.includes("node_modules"))return null;if(!/\(\s*\{/.test(Q))return null;let W=!1;try{let U=f(Q,{sourceType:"module",plugins:["typescript","jsx"]});if(g(U,{Function(D){let R=D.node.params;if(R.length!==1)return;let M=R[0];if(!I.isObjectPattern(M))return;if(!v(D))return;N(D,M),W=!0}}),!W)return null;let A=y(U,{retainLines:!0,compact:!1});return{code:A.code,map:A.map}}catch(L){return console.warn(`Failed to transform ${$}:`,L),null}}});export{o as default};
|
package/package.json
CHANGED
|
@@ -1,11 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vite-plugin-solid-undestructure",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Automatically transforms props destructuring in SolidJS components",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
7
7
|
"module": "./dist/index.js",
|
|
8
8
|
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"import": "./dist/index.js",
|
|
12
|
+
"types": "./dist/index.d.ts"
|
|
13
|
+
},
|
|
14
|
+
"./eslint": {
|
|
15
|
+
"import": "./dist/eslint/index.js",
|
|
16
|
+
"types": "./dist/eslint/index.d.ts"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
9
19
|
"files": [
|
|
10
20
|
"dist"
|
|
11
21
|
],
|
|
@@ -21,12 +31,13 @@
|
|
|
21
31
|
],
|
|
22
32
|
"license": "MIT",
|
|
23
33
|
"scripts": {
|
|
24
|
-
"build": "bun build src --minify --packages=external --outfile=dist/index.js && tsc --declaration --emitDeclarationOnly --outDir dist",
|
|
34
|
+
"build": "bun build src/index.ts --minify --packages=external --outfile=dist/index.js && bun build src/eslint/index.ts --minify --packages=external --outfile=dist/eslint/index.js && tsc --declaration --emitDeclarationOnly --outDir dist",
|
|
25
35
|
"type": "tsc --noEmit",
|
|
26
36
|
"lint": "eslint --cache src",
|
|
27
37
|
"format": "prettier --write *",
|
|
28
38
|
"test": "bun test tests",
|
|
29
|
-
"check:format": "prettier --check *"
|
|
39
|
+
"check:format": "prettier --check *",
|
|
40
|
+
"pre": "bun type && bun lint && bun format"
|
|
30
41
|
},
|
|
31
42
|
"dependencies": {
|
|
32
43
|
"@babel/generator": "^7.29.1",
|
|
@@ -47,6 +58,16 @@
|
|
|
47
58
|
"typescript-eslint": "^8.56.1"
|
|
48
59
|
},
|
|
49
60
|
"peerDependencies": {
|
|
61
|
+
"eslint": "^9.0.0",
|
|
62
|
+
"eslint-plugin-solid": "^0.14.5",
|
|
50
63
|
"vite": "^7.3.1"
|
|
64
|
+
},
|
|
65
|
+
"peerDependenciesMeta": {
|
|
66
|
+
"eslint": {
|
|
67
|
+
"optional": true
|
|
68
|
+
},
|
|
69
|
+
"eslint-plugin-solid": {
|
|
70
|
+
"optional": true
|
|
71
|
+
}
|
|
51
72
|
}
|
|
52
73
|
}
|