Skip to content

Commit 755ec29

Browse files
committed
Add variableArity:false option.
If set to `false`, calls to the pragma function are always generated with exactly 3 arguments. Also: never pass `children:[[]]`.
1 parent 1d2297d commit 755ec29

File tree

2 files changed

+77
-43
lines changed

2 files changed

+77
-43
lines changed

packages/babel-plugin-htm/index.mjs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ 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.variableArity=true] If `false`, always passes exactly 3 arguments to the pragma function.
1011
*/
1112
export default function htmBabelPlugin({ types: t }, options = {}) {
1213
const pragma = options.pragma===false ? false : dottedIdentifier(options.pragma || 'h');
@@ -48,6 +49,11 @@ export default function htmBabelPlugin({ types: t }, options = {}) {
4849
}
4950

5051
function createVNode(tag, props, children) {
52+
// Never pass children=[[]].
53+
if (children.elements.length === 1 && t.isArrayExpression(children.elements[0]) && children.elements[0].elements.length === 0) {
54+
children = children.elements[0];
55+
}
56+
5157
if (inlineVNodes) {
5258
return t.objectExpression([
5359
options.monomorphic && t.objectProperty(propertyName('type'), t.numericLiteral(1)),
@@ -58,7 +64,13 @@ export default function htmBabelPlugin({ types: t }, options = {}) {
5864
].filter(Boolean));
5965
}
6066

61-
return t.callExpression(pragma, [tag, props, children]);
67+
// Passing `{variableArity:false}` always produces `h(tag, props, children)` - where `children` is always an Array.
68+
// Otherwise, the default is `h(tag, props, ...children)`.
69+
if (options.variableArity !== false) {
70+
children = children.elements;
71+
}
72+
73+
return t.callExpression(pragma, [tag, props].concat(children));
6274
}
6375

6476
function spreadNode(args, state) {

test/babel.test.mjs

Lines changed: 64 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ describe('htm/babel', () => {
1515
htmBabelPlugin
1616
]
1717
}).code
18-
).toBe(`h("div",{id:"hello"},["hello"]);`);
18+
).toBe(`h("div",{id:"hello"},"hello");`);
1919
});
2020

2121
test('basic transformation with variable', () => {
@@ -27,7 +27,7 @@ describe('htm/babel', () => {
2727
htmBabelPlugin
2828
]
2929
}).code
30-
).toBe(`var name="world";h("div",{id:"hello"},["hello, ",name]);`);
30+
).toBe(`var name="world";h("div",{id:"hello"},"hello, ",name);`);
3131
});
3232

3333
test('basic nested transformation', () => {
@@ -41,7 +41,7 @@ describe('htm/babel', () => {
4141
}]
4242
]
4343
}).code
44-
).toBe(`h("a",Object.assign({b:2},{c:3}),["d: ",4]);`);
44+
).toBe(`h("a",Object.assign({b:2},{c:3}),"d: ",4);`);
4545
});
4646

4747
test('spread a single variable', () => {
@@ -53,7 +53,7 @@ describe('htm/babel', () => {
5353
htmBabelPlugin
5454
]
5555
}).code
56-
).toBe(`h("a",foo,[]);`);
56+
).toBe(`h("a",foo);`);
5757
});
5858

5959
test('spread two variables', () => {
@@ -67,7 +67,7 @@ describe('htm/babel', () => {
6767
}]
6868
]
6969
}).code
70-
).toBe(`h("a",Object.assign({},foo,bar),[]);`);
70+
).toBe(`h("a",Object.assign({},foo,bar));`);
7171
});
7272

7373
test('property followed by a spread', () => {
@@ -81,7 +81,7 @@ describe('htm/babel', () => {
8181
}]
8282
]
8383
}).code
84-
).toBe(`h("a",Object.assign({b:"1"},foo),[]);`);
84+
).toBe(`h("a",Object.assign({b:"1"},foo));`);
8585
});
8686

8787
test('spread followed by a property', () => {
@@ -95,7 +95,7 @@ describe('htm/babel', () => {
9595
}]
9696
]
9797
}).code
98-
).toBe(`h("a",Object.assign({},foo,{b:"1"}),[]);`);
98+
).toBe(`h("a",Object.assign({},foo,{b:"1"}));`);
9999
});
100100

101101
test('mix-and-match spreads', () => {
@@ -109,47 +109,69 @@ describe('htm/babel', () => {
109109
}]
110110
]
111111
}).code
112-
).toBe(`h("a",Object.assign({b:"1"},foo,{c:2},{d:3}),[]);`);
112+
).toBe(`h("a",Object.assign({b:"1"},foo,{c:2},{d:3}));`);
113113
});
114114

115-
test('inline vnode transformation: (pragma:false)', () => {
116-
expect(
117-
transform('var name="world",vnode=html`<div id=hello>hello, ${name}</div>`;', {
118-
babelrc: false,
119-
compact: true,
120-
plugins: [
121-
[htmBabelPlugin, {
122-
pragma: false
123-
}]
124-
]
125-
}).code
126-
).toBe(`var name="world",vnode={tag:"div",props:{id:"hello"},children:["hello, ",name]};`);
115+
describe('{variableArity:false}', () => {
116+
test('should pass no children as an empty Array', () => {
117+
expect(
118+
transform('html`<div />`;', {
119+
babelrc: false,
120+
compact: true,
121+
plugins: [
122+
[htmBabelPlugin, {
123+
variableArity: false
124+
}]
125+
]
126+
}).code
127+
).toBe(`h("div",null,[]);`);
128+
});
129+
130+
test('should pass children as an Array', () => {
131+
expect(
132+
transform('html`<div id=hello>hello</div>`;', {
133+
babelrc: false,
134+
compact: true,
135+
plugins: [
136+
[htmBabelPlugin, {
137+
variableArity: false
138+
}]
139+
]
140+
}).code
141+
).toBe(`h("div",{id:"hello"},["hello"]);`);
142+
});
127143
});
128144

129-
test('monomorphic transformation', () => {
130-
expect(
131-
transform('var name="world",vnode=html`<div id=hello>hello, ${name}</div>`;', {
132-
babelrc: false,
133-
compact: true,
134-
plugins: [
135-
[htmBabelPlugin, {
136-
monomorphic: true
137-
}]
138-
]
139-
}).code
140-
).toBe(`var name="world",vnode={type:1,tag:"div",props:{id:"hello"},children:[{type:3,tag:null,props:null,children:null,text:"hello, "},name],text:null};`);
145+
describe('{pragma:false}', () => {
146+
test('should transform to inline vnodes', () => {
147+
expect(
148+
transform('var name="world",vnode=html`<div id=hello>hello, ${name}</div>`;', {
149+
babelrc: false,
150+
compact: true,
151+
plugins: [
152+
[htmBabelPlugin, {
153+
pragma: false
154+
}]
155+
]
156+
}).code
157+
).toBe(`var name="world",vnode={tag:"div",props:{id:"hello"},children:["hello, ",name]};`);
158+
});
141159
});
142160

143-
test('preserves placeholder-looking strings in attributes and text', () => {
144-
expect(
145-
transform('html`<div $$$_h_[1]=$$$_h_[2]>$$$_h_[3]</div>`;', {
146-
babelrc: false,
147-
compact: true,
148-
plugins: [
149-
htmBabelPlugin
150-
]
151-
}).code
152-
).toBe(`h("div",{"$$$_h_[1]":"$$$_h_[2]"},["$$$_h_[3]"]);`);
161+
describe('{monomorphic:true}', () => {
162+
test('should transform to monomorphic inline vnodes', () => {
163+
expect(
164+
transform('var name="world",vnode=html`<div id=hello>hello, ${name}</div>`;', {
165+
babelrc: false,
166+
compact: true,
167+
plugins: [
168+
[htmBabelPlugin, {
169+
monomorphic: true
170+
}]
171+
]
172+
}).code
173+
).toBe(`var name="world",vnode={type:1,tag:"div",props:{id:"hello"},children:[{type:3,tag:null,props:null,children:null,text:"hello, "},name],text:null};`);
174+
});
153175
});
154176

155177
describe('main test suite', () => {

0 commit comments

Comments
 (0)