Skip to content

Commit 0a11112

Browse files
committed
More improvements
- Fixes babel transform to handle whitespace and interpolated attributes - Make middleware use dependency injection instead of directly importing - Validates memory is always returned to the correct pool size
1 parent c190ae4 commit 0a11112

File tree

62 files changed

+1723
-818
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

62 files changed

+1723
-818
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
{
22
"plugins": [
3+
["module:.", { "createTree": "createTree" }],
34
"@babel/plugin-transform-modules-commonjs"
45
]
56
}

packages/babel-plugin-transform-diffhtml/README.md

+8-4
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,15 @@ class HelloWorld extends Component {
3939
into `test.built.js`:
4040

4141
``` js
42-
var _vtree = diff.createTree("#text", null, "Hello world");
42+
var _vtree = diff.createTree("#text", "Hello world");
4343

4444
class HelloWorld extends Component {
4545
render() {
46-
return diff.createTree("div", {}, [_vtree]);
46+
return diff.createTree('#document-fragment', null, [
47+
diff.createTree('#text', '\n'),
48+
diff.createTree('div', {}, [_vtree]),
49+
diff.createTree('#text', '\n'),
50+
]);
4751
}
4852
}
4953
```
@@ -161,7 +165,7 @@ const { html, innerHTML } = require('diffhtml/runtime');
161165

162166
// Render a div with dynamic children and onclick
163167
function renderTime(time) {
164-
innerHTML(document.body, [diff.createTree("button", { "onclick": e => renderTime(new Date()) }, [diff.createTree('#text', null, "Get time")]), diff.createTree('#text', null, "\n "), diff.createTree("output", {}, [diff.createTree(time)])]);
168+
innerHTML(document.body, [diff.createTree("button", { "onclick": e => renderTime(new Date()) }, [diff.createTree('#text', "Get time")]), diff.createTree('#text', "\n "), diff.createTree("output", {}, [diff.createTree(time)])]);
165169
}
166170

167171
renderTime(new Date());
@@ -207,7 +211,7 @@ The output would look like:
207211
const { innerHTML, html, createTree } = require('diffhtml/runtime');
208212

209213
function render() {
210-
return createTree("div", null, "Hello world");
214+
return createTree("div", "Hello world");
211215
}
212216

213217
innerHTML(document.body, render());

packages/babel-plugin-transform-diffhtml/lib/index.js

+91-22
Original file line numberDiff line numberDiff line change
@@ -31,34 +31,51 @@ export default function({ types: t }) {
3131
for (let i = 0; i < length; i++) {
3232
const value = parts[i];
3333

34-
if (!value) { continue; }
35-
3634
// When we split on the token expression, the capture group will replace
3735
// the token's position. So all we do is ensure that we're on an odd
3836
// index and then we can source the correct value.
3937
if (i % 2 === 1) {
4038
const innerTree = supplemental.children[value];
41-
if (!innerTree) { continue; }
4239
const isFragment = innerTree.nodeType === 11;
4340

4441
if (typeof innerTree.rawNodeName === 'string' && isFragment) {
4542
childNodes.push(...innerTree.childNodes);
4643
}
4744
else {
4845
if (innerTree.type === 'StringLiteral' || innerTree.type === 'NumberLiteral') {
49-
childNodes.push(t.callExpression(createTree, [t.stringLiteral('#text'), t.nullLiteral(), innerTree]));
46+
childNodes.push(t.callExpression(createTree, [t.stringLiteral('#text'), innerTree]));
5047
}
5148
else {
5249
childNodes.push(innerTree);
5350
}
5451
}
5552
}
56-
else if (!doctypeEx.test(value) && hasNonWhitespaceEx.test(value) || (i !== 0 && i !== length -1)) {
53+
else if (!doctypeEx.test(value)) {
5754
childNodes.push(t.callExpression(createTree, [t.stringLiteral('#text'), t.stringLiteral(value)]));
5855
}
5956
}
6057

61-
return t.arrayExpression(childNodes);
58+
let retVal = null;
59+
60+
// If no children were present, send back an empty text node.
61+
if (childNodes.length === 0) {
62+
retVal = t.callExpression(
63+
createTree,
64+
[t.stringLiteral('#text'), t.stringLiteral('')],
65+
);
66+
}
67+
// Wrap multiple nodes in a fragment.
68+
else if (childNodes.length > 1) {
69+
retVal = t.callExpression(createTree, [
70+
t.stringLiteral('#document-fragment'),
71+
t.arrayExpression(childNodes),
72+
]);
73+
}
74+
else {
75+
retVal = childNodes[0];
76+
}
77+
78+
return t.arrayExpression([retVal]);
6279
};
6380

6481
// Takes in a dot-notation identifier and breaks it up into a
@@ -216,7 +233,7 @@ export default function({ types: t }) {
216233
if (isProp) {
217234
supplemental.attributes[i] = expression;
218235
}
219-
else if (i) {
236+
else {
220237
supplemental.children[i] = expression;
221238
}
222239
}
@@ -278,15 +295,62 @@ export default function({ types: t }) {
278295

279296
const args = [];
280297

281-
// Replace attribute values.
298+
// Remaps the supplemental values inline. This will strip out the
299+
// tokens and inject.
282300
attributes.properties.forEach((property, i, properties) => {
283-
const keyToken = property.key.value.match(tokenEx);
284-
const valueToken = property.value.value.match(tokenEx);
301+
const keysMatched = property.key.value.split(tokenEx);
302+
const valuesMatched = property.value.value.split(tokenEx);
303+
304+
const keys = [];
305+
const values = [];
306+
307+
let key;
308+
let value;
309+
310+
// Build up the key.
311+
for (let i = 0; i < keysMatched.length; i++) {
312+
const match = keysMatched[i];
313+
314+
if (i % 2 === 1) {
315+
keys.push(supplemental.attributes[match]);
316+
}
317+
else if (match) {
318+
keys.push(t.stringLiteral(match));
319+
}
320+
}
321+
322+
key = keys.reduce((memo, curr) => {
323+
if (!memo) {
324+
return curr;
325+
}
326+
327+
return t.binaryExpression('+', memo, curr);
328+
}, null);
329+
330+
// Build up the value.
331+
for (let i = 0; i < valuesMatched.length; i++) {
332+
const match = valuesMatched[i];
333+
334+
if (i % 2 === 1) {
335+
values.push(supplemental.attributes[match]);
336+
}
337+
else if (match) {
338+
values.push(t.stringLiteral(match));
339+
}
340+
}
341+
342+
value = values.reduce((memo, curr) => {
343+
if (!memo) {
344+
return curr;
345+
}
346+
347+
return t.binaryExpression('+', memo, curr);
348+
}, null);
285349

286350
properties[i] = t.objectProperty(
287-
keyToken ? supplemental.attributes[keyToken[1]] : property.key,
288-
valueToken ? supplemental.attributes[valueToken[1]] : property.value,
289-
Boolean(keyToken) // isComputed
351+
keys.length ? key : property.key,
352+
values.length ? value : property.value,
353+
Boolean(keys.length),
290354
);
291355
});
292356

@@ -324,7 +388,7 @@ export default function({ types: t }) {
324388
args.replacement = values.elements[0];
325389
}
326390
else {
327-
args.push(createTree, [t.stringLiteral('#document-fragment'), t.nullLiteral(), values]);
391+
args.push(createTree, [t.stringLiteral('#document-fragment'), values]);
328392
}
329393

330394
isDynamic = true;
@@ -336,14 +400,12 @@ export default function({ types: t }) {
336400
id,
337401
init: t.callExpression(createTree, [
338402
t.stringLiteral('#text'),
339-
t.nullLiteral(),
340403
nodeValue,
341404
]),
342405
});
343406

344407
args.replacement = t.callExpression(createTree, [
345408
t.stringLiteral('#text'),
346-
t.nullLiteral(),
347409
nodeValue,
348410
]);
349411
}
@@ -369,12 +431,19 @@ export default function({ types: t }) {
369431
return isDynamic;
370432
}
371433

372-
replaceDynamicBits([].concat(vTree.program.body));
373-
374-
if (vTree.program.body.length > 1) {
375-
path.replaceWith(t.arrayExpression(vTree.program.body.map(
376-
e => e.expression
377-
)));
434+
replaceDynamicBits([...vTree.program.body]);
435+
436+
//if (vTree.program.body.length > 1) {
437+
if (t.isArrayExpression(vTree.program.body[0].expression)) {
438+
path.replaceWith(
439+
t.callExpression(
440+
createTree,
441+
[
442+
t.stringLiteral('#document-fragment'),
443+
vTree.program.body[0].expression,
444+
],
445+
),
446+
);
378447
}
379448
else {
380449
path.replaceWith(vTree.program.body[0]);

packages/babel-plugin-transform-diffhtml/package.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "babel-plugin-transform-diffhtml",
33
"version": "1.0.0-beta.14",
4-
"description": "Transforms tagged tempalates into createTree calls",
4+
"description": "Compiles tagged templates into createTree calls",
55
"main": "dist/index.js",
66
"scripts": {
77
"build": "NODE_ENV=umd browserify --im -x diffhtml -u diffhtml -s transform -g babelify lib/index.js -o dist/index.js",
@@ -28,6 +28,6 @@
2828
"watchify": "^3.11.1"
2929
},
3030
"peerDependencies": {
31-
"diffhtml": "^1.0.0-beta.10"
31+
"diffhtml": "^1.0.0-beta.14"
3232
}
3333
}

packages/babel-plugin-transform-diffhtml/test/assertions.js

+64-1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@ describe('diffHTML Tagged Template Babel Plugin', function() {
2424
fixtures.warnsOnInvalidMarkup();
2525
});
2626
});
27+
28+
it('will render a single element', () => {
29+
const actual = fixtures.willRenderSingleElement();
30+
assert.deepEqual(actual, diff.createTree('div'));
31+
});
2732
});
2833

2934
describe('Quasis', () => {
@@ -34,7 +39,7 @@ describe('diffHTML Tagged Template Babel Plugin', function() {
3439

3540
it('can render a nested quasi literal', () => {
3641
diff.outerHTML(this.fixture, fixtures.renderNestedQuasi());
37-
assert.equal(this.fixture.innerHTML.trim(), 'Hello world');
42+
assert.equal(this.fixture.innerHTML.trim(), '<div>Hello world</div>');
3843
});
3944

4045
it('can render a nested quasi literal w/ concat text', () => {
@@ -88,11 +93,69 @@ describe('diffHTML Tagged Template Babel Plugin', function() {
8893
});
8994
});
9095

96+
describe('Attributes', () => {
97+
it('will set both a key and value dynamically', () => {
98+
const vTree = fixtures.dynamicKeyAndValue('id', 'test');
99+
100+
assert.equal(vTree.childNodes.length, 3);
101+
assert.equal(vTree.childNodes[0].nodeType, 3);
102+
assert.equal(vTree.childNodes[1].attributes.id, 'test');
103+
assert.equal(vTree.childNodes[2].nodeType, 3);
104+
});
105+
106+
it('will set a single attribute value', () => {
107+
const vTree = fixtures.setSingleValue('test');
108+
109+
assert.equal(vTree.childNodes.length, 3);
110+
assert.equal(vTree.childNodes[0].nodeType, 3);
111+
assert.equal(vTree.childNodes[1].attributes.class, 'test');
112+
assert.equal(vTree.childNodes[2].nodeType, 3);
113+
});
114+
115+
it('will set an interpolated value after static', () => {
116+
const vTree = fixtures.setInterpolatedValueAfter('test2');
117+
118+
assert.equal(vTree.childNodes.length, 3);
119+
assert.equal(vTree.childNodes[0].nodeType, 3);
120+
assert.equal(vTree.childNodes[1].attributes.class, 'test test2');
121+
assert.equal(vTree.childNodes[2].nodeType, 3);
122+
});
123+
124+
it('will set an interpolated value before static', () => {
125+
const vTree = fixtures.setInterpolatedValueBefore('test2');
126+
127+
assert.equal(vTree.childNodes.length, 3);
128+
assert.equal(vTree.childNodes[0].nodeType, 3);
129+
assert.equal(vTree.childNodes[1].attributes.class, 'test2 test');
130+
assert.equal(vTree.childNodes[2].nodeType, 3);
131+
});
132+
133+
//exports.setInterpolatedValueAfter = value => html`
134+
// <div class="test ${value}" />
135+
//`;
136+
137+
//exports.setInterpolatedValueBefore = value => html`
138+
// <div class="${value} test" />
139+
//`;
140+
});
141+
91142
describe('Bug Fixes', () => {
92143
it('will not concat neighbor nodes', () => {
93144
const vTree = fixtures.interpolatedValuesAreConcat();
94145
diff.innerHTML(this.fixture, vTree);
95146
assert.equal(this.fixture.textContent.trim(), 'Text node second');
96147
});
97148
});
149+
150+
describe('Fragments', () => {
151+
it('will create a fragment of elements', () => {
152+
const vTree = fixtures.willCreateFragments();
153+
154+
assert.equal(vTree.nodeType, 11);
155+
assert.deepEqual(vTree.childNodes, [
156+
diff.createTree('div'),
157+
diff.createTree('span'),
158+
]);
159+
});
160+
});
98161
});

0 commit comments

Comments
 (0)