Skip to content

Commit 0ed6079

Browse files
authored
Merge pull request #99 from jviide/native-object-spread
Add native object spread support to babel-plugin-htm
2 parents 8c3b084 + d06ef4a commit 0ed6079

File tree

2 files changed

+92
-9
lines changed

2 files changed

+92
-9
lines changed

packages/babel-plugin-htm/index.mjs

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,13 @@ import htm from 'htm';
77
* @param {string} [options.tag=html] The tagged template "tag" function name to process.
88
* @param {boolean} [options.monomorphic=false] Output monomorphic inline objects instead of using String literals.
99
* @param {boolean} [options.useBuiltIns=false] Use the native Object.assign instead of trying to polyfill it.
10+
* @param {boolean} [options.useNativeSpread=false] Use the native { ...a, ...b } syntax for prop spreads.
1011
* @param {boolean} [options.variableArity=true] If `false`, always passes exactly 3 arguments to the pragma function.
1112
*/
1213
export default function htmBabelPlugin({ types: t }, options = {}) {
1314
const pragma = options.pragma===false ? false : dottedIdentifier(options.pragma || 'h');
1415
const useBuiltIns = options.useBuiltIns;
16+
const useNativeSpread = options.useNativeSpread;
1517
const inlineVNodes = options.monomorphic || pragma===false;
1618

1719
const symbol = Symbol();
@@ -31,7 +33,11 @@ export default function htmBabelPlugin({ types: t }, options = {}) {
3133
const end = parts.pop() || '';
3234
return new RegExp(parts.join('/'), end);
3335
}
34-
36+
37+
function propertyValue(valueOrNode) {
38+
return t.isNode(valueOrNode) ? valueOrNode : t.valueToNode(valueOrNode);
39+
}
40+
3541
function propertyName(key) {
3642
if (key.match(/(^\d|[^a-z0-9_$])/i)) return t.stringLiteral(key);
3743
return t.identifier(key);
@@ -99,21 +105,32 @@ export default function htmBabelPlugin({ types: t }, options = {}) {
99105
if (args.length === 2 && !t.isNode(args[0]) && Object.keys(args[0]).length === 0) {
100106
return propsNode(args[1]);
101107
}
108+
109+
if (useNativeSpread) {
110+
const properties = [];
111+
args.forEach(arg => {
112+
if (t.isNode(arg)) {
113+
properties.push(t.spreadElement(arg));
114+
}
115+
else {
116+
Object.keys(arg).forEach(key => {
117+
const value = arg[key];
118+
properties.push(t.objectProperty(propertyName(key), propertyValue(value)));
119+
});
120+
}
121+
});
122+
return t.objectExpression(properties);
123+
}
124+
102125
const helper = useBuiltIns ? dottedIdentifier('Object.assign') : state.addHelper('extends');
103126
return t.callExpression(helper, args.map(propsNode));
104127
}
105128

106129
function propsNode(props) {
107130
return t.isNode(props) ? props : t.objectExpression(
108131
Object.keys(props).map(key => {
109-
let value = props[key];
110-
if (typeof value==='string') {
111-
value = t.stringLiteral(value);
112-
}
113-
else if (typeof value==='boolean') {
114-
value = t.booleanLiteral(value);
115-
}
116-
return t.objectProperty(propertyName(key), value);
132+
const value = props[key];
133+
return t.objectProperty(propertyName(key), propertyValue(value));
117134
})
118135
);
119136
}

test/babel.test.mjs

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,17 @@ describe('htm/babel', () => {
4646
]
4747
}).code
4848
).toBe(`h("a",Object.assign({b:2},{c:3}),"d: ",4);`);
49+
50+
expect(
51+
transform('html`<a b=${2} ...${{ c: 3 }}>d: ${4}</a>`;', {
52+
...options,
53+
plugins: [
54+
[htmBabelPlugin, {
55+
useNativeSpread: true
56+
}]
57+
]
58+
}).code
59+
).toBe(`h("a",{b:2,...{c:3}},"d: ",4);`);
4960
});
5061

5162
test('spread a single variable', () => {
@@ -57,6 +68,17 @@ describe('htm/babel', () => {
5768
]
5869
}).code
5970
).toBe(`h("a",foo);`);
71+
72+
expect(
73+
transform('html`<a ...${foo}></a>`;', {
74+
...options,
75+
plugins: [
76+
[htmBabelPlugin, {
77+
useNativeSpread: true
78+
}]
79+
]
80+
}).code
81+
).toBe(`h("a",foo);`);
6082
});
6183

6284
test('spread two variables', () => {
@@ -70,6 +92,17 @@ describe('htm/babel', () => {
7092
]
7193
}).code
7294
).toBe(`h("a",Object.assign({},foo,bar));`);
95+
96+
expect(
97+
transform('html`<a ...${foo} ...${bar}></a>`;', {
98+
...options,
99+
plugins: [
100+
[htmBabelPlugin, {
101+
useNativeSpread: true
102+
}]
103+
]
104+
}).code
105+
).toBe(`h("a",{...foo,...bar});`);
73106
});
74107

75108
test('property followed by a spread', () => {
@@ -83,6 +116,17 @@ describe('htm/babel', () => {
83116
]
84117
}).code
85118
).toBe(`h("a",Object.assign({b:"1"},foo));`);
119+
120+
expect(
121+
transform('html`<a b="1" ...${foo}></a>`;', {
122+
...options,
123+
plugins: [
124+
[htmBabelPlugin, {
125+
useNativeSpread: true
126+
}]
127+
]
128+
}).code
129+
).toBe(`h("a",{b:"1",...foo});`);
86130
});
87131

88132
test('spread followed by a property', () => {
@@ -96,6 +140,17 @@ describe('htm/babel', () => {
96140
]
97141
}).code
98142
).toBe(`h("a",Object.assign({},foo,{b:"1"}));`);
143+
144+
expect(
145+
transform('html`<a ...${foo} b="1"></a>`;', {
146+
...options,
147+
plugins: [
148+
[htmBabelPlugin, {
149+
useNativeSpread: true
150+
}]
151+
]
152+
}).code
153+
).toBe(`h("a",{...foo,b:"1"});`);
99154
});
100155

101156
test('mix-and-match spreads', () => {
@@ -109,6 +164,17 @@ describe('htm/babel', () => {
109164
]
110165
}).code
111166
).toBe(`h("a",Object.assign({b:"1"},foo,{c:2},{d:3}));`);
167+
168+
expect(
169+
transform('html`<a b="1" ...${foo} c=${2} ...${{d:3}}></a>`;', {
170+
...options,
171+
plugins: [
172+
[htmBabelPlugin, {
173+
useNativeSpread: true
174+
}]
175+
]
176+
}).code
177+
).toBe(`h("a",{b:"1",...foo,c:2,...{d:3}});`);
112178
});
113179

114180
describe('{variableArity:false}', () => {

0 commit comments

Comments
 (0)