Skip to content

Commit edab3dc

Browse files
authored
Merge pull request #140 from jviide/staticness-bits
Add helpers for tracking subtree staticness inside `h` functions
2 parents 1ec0588 + 6b17de9 commit edab3dc

File tree

2 files changed

+62
-9
lines changed

2 files changed

+62
-9
lines changed

src/build.mjs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -82,8 +82,8 @@ export const evaluate = (h, built, fields, args) => {
8282
for (let i = 1; i < built.length; i++) {
8383
const type = built[i++];
8484

85-
// Set `built[0]` to truthy if this element depends on a dynamic value.
86-
const value = built[i] ? fields[built[0] = built[i++]] : built[++i];
85+
// Set `built[0]`'s appropriate bits if this element depends on a dynamic value.
86+
const value = built[i] ? ((built[0] |= type ? 1 : 2), fields[built[i++]]) : built[++i];
8787

8888
if (type === TAG_SET) {
8989
args[0] = value;
@@ -97,14 +97,15 @@ export const evaluate = (h, built, fields, args) => {
9797
else if (type === PROP_APPEND) {
9898
args[1][built[++i]] += (value + '');
9999
}
100-
else if (type) {
101-
// type === CHILD_RECURSE
102-
tmp = h.apply(0, evaluate(h, value, fields, ['', null]));
100+
else if (type) { // type === CHILD_RECURSE
101+
// Set the operation list (including the staticness bits) as
102+
// `this` for the `h` call.
103+
tmp = h.apply(value, evaluate(h, value, fields, ['', null]));
103104
args.push(tmp);
104105

105106
if (value[0]) {
106-
// If the child element is dynamic, then so is the current element.
107-
built[0] = 1;
107+
// Set the 2nd lowest bit it the child element is dynamic.
108+
built[0] |= 2;
108109
}
109110
else {
110111
// Rewrite the operation list in-place if the child element is static.
@@ -116,8 +117,7 @@ export const evaluate = (h, built, fields, args) => {
116117
built[i] = tmp;
117118
}
118119
}
119-
else {
120-
// type === CHILD_APPEND
120+
else { // type === CHILD_APPEND
121121
args.push(value);
122122
}
123123
}

test/statics-caching.test.mjs

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,4 +36,57 @@ describe('htm', () => {
3636
expect(a).toBe(1);
3737
expect(b).toBe(2);
3838
});
39+
40+
describe('`this` in the h function', () => {
41+
const html = htm.bind(function() {
42+
return this;
43+
});
44+
45+
test('stays the same for each call site)', () => {
46+
const x = () => html`<div>a</div>`;
47+
const a = x();
48+
const b = x();
49+
expect(a).toBe(b);
50+
});
51+
52+
test('is different for each call site', () => {
53+
const a = html`<div>a</div>`;
54+
const b = html`<div>a</div>`;
55+
expect(a).not.toBe(b);
56+
});
57+
58+
test('is specific to each h function', () => {
59+
let tmp = htm.bind(function() { return this; });
60+
const x = () => tmp`<div>a</div>`;
61+
const a = x();
62+
tmp = htm.bind(function() { return this; });
63+
const b = x();
64+
expect(a).not.toBe(b);
65+
});
66+
});
67+
68+
describe('`this[0]` in the h function contains the staticness bits', () => {
69+
const html = htm.bind(function() {
70+
return this[0];
71+
});
72+
73+
test('should be 0 for static subtrees', () => {
74+
expect(html`<div></div>`).toBe(0);
75+
expect(html`<div>a</div>`).toBe(0);
76+
expect(html`<div><a /></div>`).toBe(0);
77+
});
78+
79+
test('should be 2 for static nodes with some dynamic children', () => {
80+
expect(html`<div>${'a'}<b /></div>`).toBe(2);
81+
expect(html`<div><a y=${2} /><b /></div>`).toBe(2);
82+
});
83+
84+
test('should be 1 for dynamic nodes with all static children', () => {
85+
expect(html`<div x=${1}><a /><b /></div>`).toBe(1);
86+
});
87+
88+
test('should be 3 for dynamic nodes with some dynamic children', () => {
89+
expect(html`<div x=${1}><a y=${2} /><b /></div>`).toBe(3);
90+
});
91+
});
3992
});

0 commit comments

Comments
 (0)