Skip to content

Commit 4aca0d2

Browse files
committed
Change attribute handling, fix unowned removal
1 parent 895e847 commit 4aca0d2

File tree

10 files changed

+76
-67
lines changed

10 files changed

+76
-67
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "dom-expressions-build",
33
"description": "A Fine-Grained Runtime for Performant DOM Rendering",
4-
"version": "0.18.0",
4+
"version": "0.19.0",
55
"author": "Ryan Carniato",
66
"license": "MIT",
77
"repository": {

packages/babel-plugin-jsx-dom-expressions/src/dom/element.js

Lines changed: 32 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -99,11 +99,10 @@ export function setAttr(path, elem, name, value, isSVG, dynamic, prevId) {
9999
if (attribute.alias) name = attribute.alias;
100100
} else if (isSVG) name = name.replace(/([A-Z])/g, g => `-${g[0].toLowerCase()}`);
101101

102-
if (isAttribute)
103-
return t.callExpression(t.memberExpression(elem, t.identifier("setAttribute")), [
104-
t.stringLiteral(name),
105-
value
106-
]);
102+
if (isAttribute) {
103+
registerImportMethod(path, "setAttribute");
104+
return t.callExpression(t.identifier("_$setAttribute"), [elem, t.stringLiteral(name), value]);
105+
}
107106
return t.assignmentExpression("=", t.memberExpression(elem, t.identifier(name)), value);
108107
}
109108

@@ -152,7 +151,7 @@ function transformAttributes(path, results) {
152151
a.node.name.name === "style" &&
153152
t.isJSXExpressionContainer(a.node.value) &&
154153
t.isObjectExpression(a.node.value.expression) &&
155-
!(a.node.value.expression.properties.some(p => t.isSpreadElement(p)))
154+
!a.node.value.expression.properties.some(p => t.isSpreadElement(p))
156155
);
157156
if (styleAttribute) {
158157
let i = 0,
@@ -210,8 +209,13 @@ function transformAttributes(path, results) {
210209
key = t.isJSXNamespacedName(node.name)
211210
? `${node.name.namespace.name}:${node.name.name.name}`
212211
: node.name.name,
213-
reservedNameSpace = t.isJSXNamespacedName(node.name) && reservedNameSpaces[node.name.namespace.name];
214-
if (t.isJSXNamespacedName(node.name) && reservedNameSpace && !t.isJSXExpressionContainer(value)) {
212+
reservedNameSpace =
213+
t.isJSXNamespacedName(node.name) && reservedNameSpaces[node.name.namespace.name];
214+
if (
215+
t.isJSXNamespacedName(node.name) &&
216+
reservedNameSpace &&
217+
!t.isJSXExpressionContainer(value)
218+
) {
215219
node.value = value = t.JSXExpressionContainer(value);
216220
}
217221
if (
@@ -237,9 +241,7 @@ function transformAttributes(path, results) {
237241
);
238242
} else if (t.isFunction(value.expression)) {
239243
results.exprs.unshift(
240-
t.expressionStatement(
241-
t.callExpression(value.expression, [elem])
242-
)
244+
t.expressionStatement(t.callExpression(value.expression, [elem]))
243245
);
244246
}
245247
} else if (key === "children") {
@@ -374,7 +376,7 @@ function wrappedByText(list, startIndex) {
374376
let index = startIndex,
375377
wrapped;
376378
while (--index >= 0) {
377-
const node = list[index]
379+
const node = list[index];
378380
if (!node) continue;
379381
if (node.text) {
380382
wrapped = true;
@@ -385,7 +387,7 @@ function wrappedByText(list, startIndex) {
385387
if (!wrapped) return false;
386388
index = startIndex;
387389
while (++index < list.length) {
388-
const node = list[index]
390+
const node = list[index];
389391
if (!node) continue;
390392
if (node.text) return true;
391393
if (node.id) return false;
@@ -399,18 +401,21 @@ function transformChildren(path, results) {
399401
nextPlaceholder,
400402
i = 0;
401403
const filteredChildren = filterChildren(path.get("children"), true),
402-
childNodes = filteredChildren.map((child, index) =>
403-
transformNode(child, {
404-
skipId: !results.id || !detectExpressions(filteredChildren, index)
405-
})
406-
// combine adjacent textNodes
407-
).reduce((memo, child) => {
408-
const i = memo.length
409-
if (child.text && i && memo[i -1].text) {
410-
memo[i - 1].template += child.template;
411-
} else memo.push(child);
412-
return memo;
413-
}, []);
404+
childNodes = filteredChildren
405+
.map(
406+
(child, index) =>
407+
transformNode(child, {
408+
skipId: !results.id || !detectExpressions(filteredChildren, index)
409+
})
410+
// combine adjacent textNodes
411+
)
412+
.reduce((memo, child) => {
413+
const i = memo.length;
414+
if (child.text && i && memo[i - 1].text) {
415+
memo[i - 1].template += child.template;
416+
} else memo.push(child);
417+
return memo;
418+
}, []);
414419

415420
childNodes.forEach((child, index) => {
416421
if (!child) return;
@@ -437,22 +442,13 @@ function transformChildren(path, results) {
437442
const multi = checkLength(filteredChildren),
438443
markers = (generate === "dom-ssr" || hydratable) && multi;
439444
// boxed by textNodes
440-
if (
441-
markers ||
442-
wrappedByText(childNodes, index)
443-
) {
445+
if (markers || wrappedByText(childNodes, index)) {
444446
let exprId, contentId;
445447
if (markers) tempPath = createPlaceholder(path, results, tempPath, i++, "#")[0].name;
446448
if (nextPlaceholder) {
447449
exprId = nextPlaceholder;
448450
} else {
449-
[exprId, contentId] = createPlaceholder(
450-
path,
451-
results,
452-
tempPath,
453-
i++,
454-
markers ? "/" : ""
455-
);
451+
[exprId, contentId] = createPlaceholder(path, results, tempPath, i++, markers ? "/" : "");
456452
}
457453
if (!markers) nextPlaceholder = exprId;
458454
results.exprs.push(

packages/babel-plugin-jsx-dom-expressions/test/__dom_fixtures__/SVG/output.js

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { template as _$template } from "r-dom";
22
import { createComponent as _$createComponent } from "r-dom";
33
import { spread as _$spread } from "r-dom";
4+
import { setAttribute as _$setAttribute } from "r-dom";
45
import { effect as _$effect } from "r-dom";
56

67
const _tmpl$ = _$template(
@@ -35,10 +36,10 @@ const template2 = (() => {
3536
_v$3 = state.x,
3637
_v$4 = state.y,
3738
_v$5 = props.stroke;
38-
_v$ !== _p$._v$ && _el$3.setAttribute("class", (_p$._v$ = _v$));
39-
_v$2 !== _p$._v$2 && _el$3.setAttribute("stroke-width", (_p$._v$2 = _v$2));
40-
_v$3 !== _p$._v$3 && _el$3.setAttribute("x", (_p$._v$3 = _v$3));
41-
_v$4 !== _p$._v$4 && _el$3.setAttribute("y", (_p$._v$4 = _v$4));
39+
_v$ !== _p$._v$ && _$setAttribute(_el$3, "class", (_p$._v$ = _v$));
40+
_v$2 !== _p$._v$2 && _$setAttribute(_el$3, "stroke-width", (_p$._v$2 = _v$2));
41+
_v$3 !== _p$._v$3 && _$setAttribute(_el$3, "x", (_p$._v$3 = _v$3));
42+
_v$4 !== _p$._v$4 && _$setAttribute(_el$3, "y", (_p$._v$4 = _v$4));
4243
_v$5 !== _p$._v$5 && _el$3.style.setProperty("stroke-width", (_p$._v$5 = _v$5));
4344
return _p$;
4445
},

packages/babel-plugin-jsx-dom-expressions/test/__dom_fixtures__/customElements/output.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { template as _$template } from "r-dom";
22
import { effect as _$effect } from "r-dom";
33
import { currentContext as _$currentContext } from "r-dom";
4+
import { setAttribute as _$setAttribute } from "r-dom";
45

56
const _tmpl$ = _$template(`<my-element></my-element>`, 2),
67
_tmpl$2 = _$template(`<my-element><header slot="head">Title</header></my-element>`, 4),
@@ -9,7 +10,7 @@ const _tmpl$ = _$template(`<my-element></my-element>`, 2),
910
const template = (() => {
1011
const _el$ = _tmpl$.cloneNode(true);
1112

12-
_el$.setAttribute("some-attr", name);
13+
_$setAttribute(_el$, "some-attr", name);
1314

1415
_el$.someProp = data;
1516
_el$._context = _$currentContext();
@@ -25,7 +26,7 @@ const template2 = (() => {
2526
_p$ => {
2627
const _v$ = state.name,
2728
_v$2 = state.data;
28-
_v$ !== _p$._v$ && _el$2.setAttribute("some-attr", (_p$._v$ = _v$));
29+
_v$ !== _p$._v$ && _$setAttribute(_el$2, "some-attr", (_p$._v$ = _v$));
2930
_v$2 !== _p$._v$2 && (_el$2.someProp = _p$._v$2 = _v$2);
3031
return _p$;
3132
},

packages/babel-plugin-jsx-dom-expressions/test/__dom_hydratable_fixtures__/SVG/output.js

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { template as _$template } from "r-dom";
22
import { createComponent as _$createComponent } from "r-dom";
33
import { runHydrationEvents as _$runHydrationEvents } from "r-dom";
44
import { spread as _$spread } from "r-dom";
5+
import { setAttribute as _$setAttribute } from "r-dom";
56
import { effect as _$effect } from "r-dom";
67
import { getNextElement as _$getNextElement } from "r-dom";
78

@@ -37,10 +38,10 @@ const template2 = (() => {
3738
_v$3 = state.x,
3839
_v$4 = state.y,
3940
_v$5 = props.stroke;
40-
_v$ !== _p$._v$ && _el$3.setAttribute("class", (_p$._v$ = _v$));
41-
_v$2 !== _p$._v$2 && _el$3.setAttribute("stroke-width", (_p$._v$2 = _v$2));
42-
_v$3 !== _p$._v$3 && _el$3.setAttribute("x", (_p$._v$3 = _v$3));
43-
_v$4 !== _p$._v$4 && _el$3.setAttribute("y", (_p$._v$4 = _v$4));
41+
_v$ !== _p$._v$ && _$setAttribute(_el$3, "class", (_p$._v$ = _v$));
42+
_v$2 !== _p$._v$2 && _$setAttribute(_el$3, "stroke-width", (_p$._v$2 = _v$2));
43+
_v$3 !== _p$._v$3 && _$setAttribute(_el$3, "x", (_p$._v$3 = _v$3));
44+
_v$4 !== _p$._v$4 && _$setAttribute(_el$3, "y", (_p$._v$4 = _v$4));
4445
_v$5 !== _p$._v$5 && _el$3.style.setProperty("stroke-width", (_p$._v$5 = _v$5));
4546
return _p$;
4647
},

packages/babel-plugin-jsx-dom-expressions/test/__dom_hydratable_fixtures__/customElements/output.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { template as _$template } from "r-dom";
22
import { effect as _$effect } from "r-dom";
33
import { getNextElement as _$getNextElement } from "r-dom";
44
import { currentContext as _$currentContext } from "r-dom";
5+
import { setAttribute as _$setAttribute } from "r-dom";
56

67
const _tmpl$ = _$template(`<my-element></my-element>`, 2),
78
_tmpl$2 = _$template(`<my-element><header slot="head">Title</header></my-element>`, 4),
@@ -10,7 +11,7 @@ const _tmpl$ = _$template(`<my-element></my-element>`, 2),
1011
const template = (() => {
1112
const _el$ = _$getNextElement(_tmpl$);
1213

13-
_el$.setAttribute("some-attr", name);
14+
_$setAttribute(_el$, "some-attr", name);
1415

1516
_el$.someProp = data;
1617
_el$._context = _$currentContext();
@@ -26,7 +27,7 @@ const template2 = (() => {
2627
_p$ => {
2728
const _v$ = state.name,
2829
_v$2 = state.data;
29-
_v$ !== _p$._v$ && _el$2.setAttribute("some-attr", (_p$._v$ = _v$));
30+
_v$ !== _p$._v$ && _$setAttribute(_el$2, "some-attr", (_p$._v$ = _v$));
3031
_v$2 !== _p$._v$2 && (_el$2.someProp = _p$._v$2 = _v$2);
3132
return _p$;
3233
},

packages/babel-plugin-jsx-dom-expressions/test/__dom_ssr_fixtures__/SVG/output.js

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { template as _$template } from "r-dom";
22
import { createComponent as _$createComponent } from "r-dom";
33
import { spread as _$spread } from "r-dom";
4+
import { setAttribute as _$setAttribute } from "r-dom";
45
import { effect as _$effect } from "r-dom";
56
import { getNextElement as _$getNextElement } from "r-dom";
67

@@ -36,10 +37,10 @@ const template2 = (() => {
3637
_v$3 = state.x,
3738
_v$4 = state.y,
3839
_v$5 = props.stroke;
39-
_v$ !== _p$._v$ && _el$3.setAttribute("class", (_p$._v$ = _v$));
40-
_v$2 !== _p$._v$2 && _el$3.setAttribute("stroke-width", (_p$._v$2 = _v$2));
41-
_v$3 !== _p$._v$3 && _el$3.setAttribute("x", (_p$._v$3 = _v$3));
42-
_v$4 !== _p$._v$4 && _el$3.setAttribute("y", (_p$._v$4 = _v$4));
40+
_v$ !== _p$._v$ && _$setAttribute(_el$3, "class", (_p$._v$ = _v$));
41+
_v$2 !== _p$._v$2 && _$setAttribute(_el$3, "stroke-width", (_p$._v$2 = _v$2));
42+
_v$3 !== _p$._v$3 && _$setAttribute(_el$3, "x", (_p$._v$3 = _v$3));
43+
_v$4 !== _p$._v$4 && _$setAttribute(_el$3, "y", (_p$._v$4 = _v$4));
4344
_v$5 !== _p$._v$5 && _el$3.style.setProperty("stroke-width", (_p$._v$5 = _v$5));
4445
return _p$;
4546
},

packages/babel-plugin-jsx-dom-expressions/test/__dom_ssr_fixtures__/customElements/output.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { template as _$template } from "r-dom";
22
import { effect as _$effect } from "r-dom";
33
import { getNextElement as _$getNextElement } from "r-dom";
44
import { currentContext as _$currentContext } from "r-dom";
5+
import { setAttribute as _$setAttribute } from "r-dom";
56

67
const _tmpl$ = _$template(`<my-element></my-element>`, 2),
78
_tmpl$2 = _$template(`<my-element><header slot="head">Title</header></my-element>`, 4),
@@ -10,7 +11,7 @@ const _tmpl$ = _$template(`<my-element></my-element>`, 2),
1011
const template = (() => {
1112
const _el$ = _$getNextElement(_tmpl$, true);
1213

13-
_el$.setAttribute("some-attr", name);
14+
_$setAttribute(_el$, "some-attr", name);
1415

1516
_el$.someProp = data;
1617
_el$._context = _$currentContext();
@@ -26,7 +27,7 @@ const template2 = (() => {
2627
_p$ => {
2728
const _v$ = state.name,
2829
_v$2 = state.data;
29-
_v$ !== _p$._v$ && _el$2.setAttribute("some-attr", (_p$._v$ = _v$));
30+
_v$ !== _p$._v$ && _$setAttribute(_el$2, "some-attr", (_p$._v$ = _v$));
3031
_v$2 !== _p$._v$2 && (_el$2.someProp = _p$._v$2 = _v$2);
3132
return _p$;
3233
},

packages/dom-expressions/src/runtime.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ export function delegateEvents(eventNames: string[]): void;
3131
export function clearDelegatedEvents(): void;
3232
export function spread(node: Element, accessor: any, isSVG?: Boolean, skipChildren?: Boolean): void;
3333
export function assign(node: Element, props: any, isSVG?: Boolean, skipChildren?: Boolean): void;
34+
export function setAttribute(node : Element, name : string, value : any): void;
3435
export function classList(
3536
node: Element,
3637
value: { [k: string]: boolean },

packages/dom-expressions/src/runtime.js

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Attributes, SVGAttributes, NonComposedEvents } from "./constants";
2-
import { root, effect, memo, currentContext, createComponent } from "rxcore";
2+
import { root, effect, memo, currentContext, createComponent } from "rxcore";
33
import reconcileArrays from "./reconcile";
44

55
const eventRegistry = new Set(),
@@ -21,7 +21,7 @@ export function renderToString(code, options = {}) {
2121
hydration.context = { id: "0", count: 0 };
2222
return root(() => {
2323
const rendered = code();
24-
if (typeof rendered === "object" && 'then' in rendered) {
24+
if (typeof rendered === "object" && "then" in rendered) {
2525
const timeout = new Promise((_, reject) =>
2626
setTimeout(() => reject("renderToString timed out"), options.timeoutMs)
2727
);
@@ -46,7 +46,7 @@ export function renderDOMToString(code, options = {}) {
4646
return html;
4747
}
4848

49-
if (typeof rendered === "object" && 'then' in rendered) {
49+
if (typeof rendered === "object" && "then" in rendered) {
5050
const timeout = new Promise((_, reject) =>
5151
setTimeout(() => reject("renderToString timed out"), options.timeoutMs)
5252
);
@@ -99,6 +99,11 @@ export function clearDelegatedEvents() {
9999
eventRegistry.clear();
100100
}
101101

102+
export function setAttribute(node, name, value) {
103+
if (value === false || value == null) node.removeAttribute(name);
104+
else node.setAttribute(name, value);
105+
}
106+
102107
export function classList(node, value, prev) {
103108
const classKeys = Object.keys(value);
104109
for (let i = 0, len = classKeys.length; i < len; i++) {
@@ -200,13 +205,13 @@ export function ssr(template, ...nodes) {
200205
}
201206
const t = () => {
202207
let result = "";
203-
for(let i = 0; i < template.length; i++) {
208+
for (let i = 0; i < template.length; i++) {
204209
result += template[i];
205210
const node = rNodes[i];
206211
if (node !== undefined) result += resolveSSRNode(node);
207212
}
208213
return result;
209-
}
214+
};
210215
t.isTemplate = true;
211216
return t;
212217
}
@@ -265,13 +270,13 @@ export function escape(html, attr) {
265270
if (typeof html !== "string") return html;
266271
return html.replace(attr ? ATTR_REGEX : CONTENT_REGEX, m => {
267272
switch (m) {
268-
case '&':
269-
return '&amp;';
270-
case '<':
271-
return '&lt;';
273+
case "&":
274+
return "&amp;";
275+
case "<":
276+
return "&lt;";
272277
case '"':
273-
return '&quot;';
274-
}
278+
return "&quot;";
279+
}
275280
});
276281
}
277282

@@ -471,7 +476,8 @@ function cleanChildren(parent, current, marker, replacement) {
471476
const node = replacement || document.createTextNode("");
472477
if (current.length) {
473478
node !== current[0] && parent.replaceChild(node, current[0]);
474-
for (let i = current.length - 1; i > 0; i--) parent.removeChild(current[i]);
479+
for (let i = current.length - 1; i > 0; i--)
480+
current[i].parentNode === parent && parent.removeChild(current[i]);
475481
} else parent.insertBefore(node, marker);
476482
return [node];
477483
}

0 commit comments

Comments
 (0)