Skip to content

Commit 3df93f0

Browse files
committed
v-0.0.9
1 parent 848567d commit 3df93f0

4 files changed

Lines changed: 140 additions & 52 deletions

File tree

example.png

342 KB
Loading

index.cjs

Lines changed: 131 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,96 +1,176 @@
1-
const { babel } = require("@rollup/plugin-babel")
1+
function addImportDeclarationToProgram(types, program, specifier, imported, source) {
2+
program.node.body.unshift(
3+
types.importDeclaration(
4+
[types.importSpecifier(specifier, types.identifier(imported))],
5+
types.stringLiteral(source)
6+
)
7+
)
8+
}
29

10+
function addStatementToFunction(types, path, statement) {
11+
if (path.node.body.type === 'BlockStatement') {
12+
path.node.body.body.unshift(statement)
13+
} else {
14+
path.node.body = types.blockStatement([
15+
statement,
16+
types.returnStatement(path.node.body)
17+
])
18+
}
19+
}
320

421
const functionVisitor = types => (path, { opts }) => {
522
{
6-
const { mode = 'ts' } = opts
7-
23+
const { mode = 'vanilla-js' } = opts
24+
825
if (mode !== 'ts' && mode !== 'vanilla-js')
9-
throw new Error("Invalid configuration: mode must be 'ts' or 'vanilla-js'.")
26+
throw new Error("babel-plugin-solid-undestructure error: Invalid configuration - mode must be either 'ts' or 'vanilla-js'.")
1027

11-
const type = path?.type
12-
28+
const type = path.type
29+
1330
if (mode === 'ts') {
1431
if (type !== "ArrowFunctionExpression") return
15-
if (path.parent?.type !== "VariableDeclarator") return
16-
const bindings = path.context?.scope?.bindings
17-
if (!path.parent?.id?.typeAnnotation) return
32+
if (path.parent.type !== "VariableDeclarator") return
33+
const bindings = path.context.scope.bindings
34+
if (!path.parent.id.typeAnnotation) return
1835
const typeAnnotation = path.parent.id.typeAnnotation.typeAnnotation
19-
if (typeAnnotation?.type !== "TSTypeReference") return
20-
if (typeAnnotation?.typeName?.type === "Identifier") {
36+
if (typeAnnotation.type !== "TSTypeReference") return
37+
if (typeAnnotation.typeName.type === "Identifier") {
2138
const typeName = typeAnnotation.typeName.name
22-
const typeBinding = bindings?.[typeName]
39+
const typeBinding = bindings[typeName]
2340
if (!typeBinding) return
24-
const importSpecifier = typeBinding.path?.node
25-
if (importSpecifier?.type !== "ImportSpecifier") return
26-
if (importSpecifier?.imported?.name !== "Component") return
27-
if (typeBinding?.path?.parent?.source?.value !== "solid-js") return
41+
const importSpecifier = typeBinding.path.node
42+
if (importSpecifier.type !== "ImportSpecifier") return
43+
if (importSpecifier.imported.name !== "Component") return
44+
if (typeBinding.path.parent.source.value !== "solid-js") return
2845
}
29-
else if (typeAnnotation?.typeName?.type === "TSQualifiedName") {
30-
if (typeAnnotation.typeName?.right?.name !== "Component") return
31-
const typeQualification = typeAnnotation.typeName?.left
32-
if (typeQualification?.type !== "Identifier") return
33-
const typeQualificationName = typeQualification?.name
34-
const typeQualificationBinding = bindings?.[typeQualificationName]
46+
else if (typeAnnotation.typeName.type === "TSQualifiedName") {
47+
if (typeAnnotation.typeName.right.name !== "Component") return
48+
const typeQualification = typeAnnotation.typeName.left
49+
if (typeQualification.type !== "Identifier") return
50+
const typeQualificationName = typeQualification.name
51+
const typeQualificationBinding = bindings[typeQualificationName]
3552
if (!typeQualificationBinding) return
36-
const importSpecifier = typeQualificationBinding?.path?.node
37-
if (importSpecifier?.type !== "ImportDefaultSpecifier") return
38-
if (typeQualificationBinding?.path?.parent?.source?.value !== "solid-js") return
53+
const importSpecifier = typeQualificationBinding.path.node
54+
if (importSpecifier.type !== "ImportDefaultSpecifier") return
55+
if (typeQualificationBinding.path.parent.source.value !== "solid-js") return
3956
}
4057
else return
4158
}
4259

4360
if (mode === 'vanilla-js') {
44-
if (path.parent?.type !== "CallExpression") return
45-
const wrappingFunctionName = path.parent.callee?.name
46-
const bindings = path.context?.scope?.bindings
47-
const wrappingFunctionBinding = bindings?.[wrappingFunctionName]
61+
if (path.parent.type !== "CallExpression") return
62+
const wrappingFunctionName = path.parent.callee.name
63+
const bindings = path.context.scope.bindings
64+
const wrappingFunctionBinding = bindings[wrappingFunctionName]
4865
if (!wrappingFunctionBinding) return
49-
const importSpecifier = wrappingFunctionBinding.path?.node
50-
if (importSpecifier?.type !== "ImportSpecifier") return
51-
if (importSpecifier?.imported?.name !== "component") return
66+
const importSpecifier = wrappingFunctionBinding.path.node
67+
if (importSpecifier.type !== "ImportSpecifier") return
68+
if (importSpecifier.imported.name !== "component") return
5269

53-
if (wrappingFunctionBinding.path.parent?.source?.value !== 'babel-plugin-solid-undestructure') return
70+
if (wrappingFunctionBinding.path.parent.source.value !== 'babel-plugin-solid-undestructure') return
5471
if (wrappingFunctionBinding.references === 1) wrappingFunctionBinding.path.parentPath.remove()
55-
else wrappingFunctionBinding.references--
56-
path.parentPath.replaceWith(path)
72+
else wrappingFunctionBinding.references--
73+
path.parentPath.replaceWith(path)
5774
}
5875
}
5976

60-
const firstParam = path.node?.params?.[0]
61-
if (!firstParam || firstParam.type !== "ObjectPattern") return
77+
let firstParam = path.node.params[0]
78+
if (
79+
!firstParam
80+
|| (
81+
firstParam.type !== "ObjectPattern"
82+
&& firstParam.type !== "AssignmentPattern"
83+
)
84+
) return
85+
86+
let defaultPropsWhole = types.objectExpression([])
87+
if (firstParam.type == "AssignmentPattern") {
88+
defaultPropsWhole = firstParam.right
89+
firstParam = firstParam.left
90+
}
6291

63-
const program = path?.findParent(path => path.isProgram())
64-
const newPropsIdentifier =
65-
program?.scope?.generateUidIdentifier("props")
92+
const program = path.findParent(path => path.isProgram())
93+
const newPropsIdentifier = program.scope.generateUidIdentifier("props")
6694

67-
const propsDestructredProperties = firstParam?.properties
68-
const componentScopeBindings = path.scope?.bindings
95+
const propsDestructredProperties = firstParam.properties
96+
const componentScopeBindings = path.scope.bindings
6997

70-
for (const DestructredProperty of propsDestructredProperties)
71-
if (!DestructredProperty?.value?.name) return
98+
let defaultPropsObject
7299

73100
for (const DestructredProperty of propsDestructredProperties) {
101+
if (DestructredProperty.type === "RestElement") throw new Error("babel-plugin-solid-undestructure error: Rest elements are not supported.")
102+
if (
103+
(
104+
// Nested destructuring
105+
DestructredProperty.value.type !== "Identifier"
106+
&& DestructredProperty.value.type !== "AssignmentPattern"
107+
)
108+
|| (
109+
// Nested destructuring + default value
110+
DestructredProperty.value.type !== "Identifier"
111+
&& DestructredProperty.value.left.type !== "Identifier"
112+
)
113+
)
114+
throw new Error("babel-plugin-solid-undestructure error: Nested destructuring is not supported.")
115+
116+
// Handle default props
117+
if (DestructredProperty.value.type === "AssignmentPattern") {
118+
if (!defaultPropsObject) {
119+
// Look for `mergeProps` import
120+
let mergePropsUniqueName
121+
program.traverse({
122+
ImportDeclaration(path) {
123+
if (mergePropsUniqueName || path.node.source.value !== "solid-js") return
124+
for (const specifier of path.node.specifiers)
125+
if (
126+
specifier.imported
127+
&& specifier.imported.name === "mergeProps"
128+
&& specifier.local.unique
129+
) {
130+
mergePropsUniqueName = specifier.local
131+
return
132+
}
133+
}
134+
})
135+
136+
// If not found, create one
137+
if (!mergePropsUniqueName) {
138+
mergePropsUniqueName = program.scope.generateUidIdentifier("mergeProps")
139+
mergePropsUniqueName.unique = true
140+
addImportDeclarationToProgram(types, program, mergePropsUniqueName, "mergeProps", "solid-js")
141+
}
142+
143+
defaultPropsObject = types.objectExpression([types.objectProperty(DestructredProperty.value.left ,DestructredProperty.value.right)])
144+
const callExpression = types.callExpression(mergePropsUniqueName, [defaultPropsWhole, defaultPropsObject, newPropsIdentifier])
145+
const assignmentStatement = types.expressionStatement(types.assignmentExpression("=", newPropsIdentifier, callExpression))
146+
addStatementToFunction(types, path, assignmentStatement)
147+
}
148+
149+
else defaultPropsObject.properties.push(types.objectProperty(DestructredProperty.value.left ,DestructredProperty.value.right))
150+
}
151+
74152
const DestructredKeyIdentifier = DestructredProperty.key
75153
const undestructuredPropExpression =
76-
types.memberExpression(newPropsIdentifier, DestructredKeyIdentifier)
154+
types.memberExpression(newPropsIdentifier, DestructredKeyIdentifier)
77155

78-
const DestructredName = DestructredProperty.value?.name
156+
const DestructredName = DestructredProperty.value.name || DestructredProperty.value.left.name
79157

80-
const { referencePaths, constantViolations } = componentScopeBindings?.[DestructredName]
158+
path.scope.crawl()
159+
const componentScopeBindings = path.scope.bindings
160+
const { referencePaths, constantViolations } = componentScopeBindings[DestructredName]
81161

82162
for (const referencePath of referencePaths)
83-
referencePath?.replaceWith(undestructuredPropExpression)
163+
referencePath.replaceWith(undestructuredPropExpression)
84164

85165
for (const constantViolation of constantViolations)
86166
constantViolation.node && (constantViolation.node.left = undestructuredPropExpression)
87167
}
88168

89169
path.node.params[0] = newPropsIdentifier
90170
}
91-
92-
93-
module.exports = function babelPluginUndestructure ({ types }) {
171+
172+
173+
module.exports = function babelPluginUndestructure ({ types }) {
94174
const visitor = {
95175
FunctionDeclaration: functionVisitor(types),
96176
FunctionExpression: functionVisitor(types),

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "babel-plugin-solid-undestructure",
3-
"version": "0.0.8",
3+
"version": "0.0.9",
44
"main": "index.cjs",
55
"module": "index.js",
66
"types": "index.d.ts",

readme.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
# babel-plugin-solid-undestrcture
22

3+
<p align="center">
4+
<img
5+
src="https://github.com/orenelbaum/babel-plugin-solid-undestructure/blob/main/example.png?raw=true"
6+
alt="Usage example"
7+
style="width: 80%; height: auto;"
8+
/>
9+
</p>
10+
311
This babel plugin allows you to destructure your props in your Solid components without losing reactivity.
412

513
The plugin will "un-destructure" your props at build time, so the code you pass into the Solid compiler will not have destructured props at runtime. Instead the props will be accessed the normal way with `props.someProp`.

0 commit comments

Comments
 (0)