diff --git a/package.json b/package.json
index b373743b..9a1e7d38 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "dom-expressions-build",
"description": "A Fine-Grained Runtime for Performant DOM Rendering",
- "version": "0.18.0",
+ "version": "0.19.0",
"author": "Ryan Carniato",
"license": "MIT",
"repository": {
diff --git a/packages/babel-plugin-jsx-dom-expressions/src/dom/element.js b/packages/babel-plugin-jsx-dom-expressions/src/dom/element.js
index d55934fc..11c7c982 100644
--- a/packages/babel-plugin-jsx-dom-expressions/src/dom/element.js
+++ b/packages/babel-plugin-jsx-dom-expressions/src/dom/element.js
@@ -99,11 +99,10 @@ export function setAttr(path, elem, name, value, isSVG, dynamic, prevId) {
if (attribute.alias) name = attribute.alias;
} else if (isSVG) name = name.replace(/([A-Z])/g, g => `-${g[0].toLowerCase()}`);
- if (isAttribute)
- return t.callExpression(t.memberExpression(elem, t.identifier("setAttribute")), [
- t.stringLiteral(name),
- value
- ]);
+ if (isAttribute) {
+ registerImportMethod(path, "setAttribute");
+ return t.callExpression(t.identifier("_$setAttribute"), [elem, t.stringLiteral(name), value]);
+ }
return t.assignmentExpression("=", t.memberExpression(elem, t.identifier(name)), value);
}
@@ -152,7 +151,7 @@ function transformAttributes(path, results) {
a.node.name.name === "style" &&
t.isJSXExpressionContainer(a.node.value) &&
t.isObjectExpression(a.node.value.expression) &&
- !(a.node.value.expression.properties.some(p => t.isSpreadElement(p)))
+ !a.node.value.expression.properties.some(p => t.isSpreadElement(p))
);
if (styleAttribute) {
let i = 0,
@@ -210,8 +209,13 @@ function transformAttributes(path, results) {
key = t.isJSXNamespacedName(node.name)
? `${node.name.namespace.name}:${node.name.name.name}`
: node.name.name,
- reservedNameSpace = t.isJSXNamespacedName(node.name) && reservedNameSpaces[node.name.namespace.name];
- if (t.isJSXNamespacedName(node.name) && reservedNameSpace && !t.isJSXExpressionContainer(value)) {
+ reservedNameSpace =
+ t.isJSXNamespacedName(node.name) && reservedNameSpaces[node.name.namespace.name];
+ if (
+ t.isJSXNamespacedName(node.name) &&
+ reservedNameSpace &&
+ !t.isJSXExpressionContainer(value)
+ ) {
node.value = value = t.JSXExpressionContainer(value);
}
if (
@@ -237,9 +241,7 @@ function transformAttributes(path, results) {
);
} else if (t.isFunction(value.expression)) {
results.exprs.unshift(
- t.expressionStatement(
- t.callExpression(value.expression, [elem])
- )
+ t.expressionStatement(t.callExpression(value.expression, [elem]))
);
}
} else if (key === "children") {
@@ -374,7 +376,7 @@ function wrappedByText(list, startIndex) {
let index = startIndex,
wrapped;
while (--index >= 0) {
- const node = list[index]
+ const node = list[index];
if (!node) continue;
if (node.text) {
wrapped = true;
@@ -385,7 +387,7 @@ function wrappedByText(list, startIndex) {
if (!wrapped) return false;
index = startIndex;
while (++index < list.length) {
- const node = list[index]
+ const node = list[index];
if (!node) continue;
if (node.text) return true;
if (node.id) return false;
@@ -399,18 +401,21 @@ function transformChildren(path, results) {
nextPlaceholder,
i = 0;
const filteredChildren = filterChildren(path.get("children"), true),
- childNodes = filteredChildren.map((child, index) =>
- transformNode(child, {
- skipId: !results.id || !detectExpressions(filteredChildren, index)
- })
- // combine adjacent textNodes
- ).reduce((memo, child) => {
- const i = memo.length
- if (child.text && i && memo[i -1].text) {
- memo[i - 1].template += child.template;
- } else memo.push(child);
- return memo;
- }, []);
+ childNodes = filteredChildren
+ .map(
+ (child, index) =>
+ transformNode(child, {
+ skipId: !results.id || !detectExpressions(filteredChildren, index)
+ })
+ // combine adjacent textNodes
+ )
+ .reduce((memo, child) => {
+ const i = memo.length;
+ if (child.text && i && memo[i - 1].text) {
+ memo[i - 1].template += child.template;
+ } else memo.push(child);
+ return memo;
+ }, []);
childNodes.forEach((child, index) => {
if (!child) return;
@@ -437,22 +442,13 @@ function transformChildren(path, results) {
const multi = checkLength(filteredChildren),
markers = (generate === "dom-ssr" || hydratable) && multi;
// boxed by textNodes
- if (
- markers ||
- wrappedByText(childNodes, index)
- ) {
+ if (markers || wrappedByText(childNodes, index)) {
let exprId, contentId;
if (markers) tempPath = createPlaceholder(path, results, tempPath, i++, "#")[0].name;
if (nextPlaceholder) {
exprId = nextPlaceholder;
} else {
- [exprId, contentId] = createPlaceholder(
- path,
- results,
- tempPath,
- i++,
- markers ? "/" : ""
- );
+ [exprId, contentId] = createPlaceholder(path, results, tempPath, i++, markers ? "/" : "");
}
if (!markers) nextPlaceholder = exprId;
results.exprs.push(
diff --git a/packages/babel-plugin-jsx-dom-expressions/test/__dom_fixtures__/SVG/output.js b/packages/babel-plugin-jsx-dom-expressions/test/__dom_fixtures__/SVG/output.js
index 0894e27b..3d5272eb 100644
--- a/packages/babel-plugin-jsx-dom-expressions/test/__dom_fixtures__/SVG/output.js
+++ b/packages/babel-plugin-jsx-dom-expressions/test/__dom_fixtures__/SVG/output.js
@@ -1,6 +1,7 @@
import { template as _$template } from "r-dom";
import { createComponent as _$createComponent } from "r-dom";
import { spread as _$spread } from "r-dom";
+import { setAttribute as _$setAttribute } from "r-dom";
import { effect as _$effect } from "r-dom";
const _tmpl$ = _$template(
@@ -35,10 +36,10 @@ const template2 = (() => {
_v$3 = state.x,
_v$4 = state.y,
_v$5 = props.stroke;
- _v$ !== _p$._v$ && _el$3.setAttribute("class", (_p$._v$ = _v$));
- _v$2 !== _p$._v$2 && _el$3.setAttribute("stroke-width", (_p$._v$2 = _v$2));
- _v$3 !== _p$._v$3 && _el$3.setAttribute("x", (_p$._v$3 = _v$3));
- _v$4 !== _p$._v$4 && _el$3.setAttribute("y", (_p$._v$4 = _v$4));
+ _v$ !== _p$._v$ && _$setAttribute(_el$3, "class", (_p$._v$ = _v$));
+ _v$2 !== _p$._v$2 && _$setAttribute(_el$3, "stroke-width", (_p$._v$2 = _v$2));
+ _v$3 !== _p$._v$3 && _$setAttribute(_el$3, "x", (_p$._v$3 = _v$3));
+ _v$4 !== _p$._v$4 && _$setAttribute(_el$3, "y", (_p$._v$4 = _v$4));
_v$5 !== _p$._v$5 && _el$3.style.setProperty("stroke-width", (_p$._v$5 = _v$5));
return _p$;
},
diff --git a/packages/babel-plugin-jsx-dom-expressions/test/__dom_fixtures__/customElements/output.js b/packages/babel-plugin-jsx-dom-expressions/test/__dom_fixtures__/customElements/output.js
index e68e01cb..22605018 100644
--- a/packages/babel-plugin-jsx-dom-expressions/test/__dom_fixtures__/customElements/output.js
+++ b/packages/babel-plugin-jsx-dom-expressions/test/__dom_fixtures__/customElements/output.js
@@ -1,6 +1,7 @@
import { template as _$template } from "r-dom";
import { effect as _$effect } from "r-dom";
import { currentContext as _$currentContext } from "r-dom";
+import { setAttribute as _$setAttribute } from "r-dom";
const _tmpl$ = _$template(``, 2),
_tmpl$2 = _$template(``, 4),
@@ -9,7 +10,7 @@ const _tmpl$ = _$template(``, 2),
const template = (() => {
const _el$ = _tmpl$.cloneNode(true);
- _el$.setAttribute("some-attr", name);
+ _$setAttribute(_el$, "some-attr", name);
_el$.someProp = data;
_el$._context = _$currentContext();
@@ -25,7 +26,7 @@ const template2 = (() => {
_p$ => {
const _v$ = state.name,
_v$2 = state.data;
- _v$ !== _p$._v$ && _el$2.setAttribute("some-attr", (_p$._v$ = _v$));
+ _v$ !== _p$._v$ && _$setAttribute(_el$2, "some-attr", (_p$._v$ = _v$));
_v$2 !== _p$._v$2 && (_el$2.someProp = _p$._v$2 = _v$2);
return _p$;
},
diff --git a/packages/babel-plugin-jsx-dom-expressions/test/__dom_hydratable_fixtures__/SVG/output.js b/packages/babel-plugin-jsx-dom-expressions/test/__dom_hydratable_fixtures__/SVG/output.js
index 302fab2b..708e6965 100644
--- a/packages/babel-plugin-jsx-dom-expressions/test/__dom_hydratable_fixtures__/SVG/output.js
+++ b/packages/babel-plugin-jsx-dom-expressions/test/__dom_hydratable_fixtures__/SVG/output.js
@@ -2,6 +2,7 @@ import { template as _$template } from "r-dom";
import { createComponent as _$createComponent } from "r-dom";
import { runHydrationEvents as _$runHydrationEvents } from "r-dom";
import { spread as _$spread } from "r-dom";
+import { setAttribute as _$setAttribute } from "r-dom";
import { effect as _$effect } from "r-dom";
import { getNextElement as _$getNextElement } from "r-dom";
@@ -37,10 +38,10 @@ const template2 = (() => {
_v$3 = state.x,
_v$4 = state.y,
_v$5 = props.stroke;
- _v$ !== _p$._v$ && _el$3.setAttribute("class", (_p$._v$ = _v$));
- _v$2 !== _p$._v$2 && _el$3.setAttribute("stroke-width", (_p$._v$2 = _v$2));
- _v$3 !== _p$._v$3 && _el$3.setAttribute("x", (_p$._v$3 = _v$3));
- _v$4 !== _p$._v$4 && _el$3.setAttribute("y", (_p$._v$4 = _v$4));
+ _v$ !== _p$._v$ && _$setAttribute(_el$3, "class", (_p$._v$ = _v$));
+ _v$2 !== _p$._v$2 && _$setAttribute(_el$3, "stroke-width", (_p$._v$2 = _v$2));
+ _v$3 !== _p$._v$3 && _$setAttribute(_el$3, "x", (_p$._v$3 = _v$3));
+ _v$4 !== _p$._v$4 && _$setAttribute(_el$3, "y", (_p$._v$4 = _v$4));
_v$5 !== _p$._v$5 && _el$3.style.setProperty("stroke-width", (_p$._v$5 = _v$5));
return _p$;
},
diff --git a/packages/babel-plugin-jsx-dom-expressions/test/__dom_hydratable_fixtures__/customElements/output.js b/packages/babel-plugin-jsx-dom-expressions/test/__dom_hydratable_fixtures__/customElements/output.js
index 4b10ea30..523a25ee 100644
--- a/packages/babel-plugin-jsx-dom-expressions/test/__dom_hydratable_fixtures__/customElements/output.js
+++ b/packages/babel-plugin-jsx-dom-expressions/test/__dom_hydratable_fixtures__/customElements/output.js
@@ -2,6 +2,7 @@ import { template as _$template } from "r-dom";
import { effect as _$effect } from "r-dom";
import { getNextElement as _$getNextElement } from "r-dom";
import { currentContext as _$currentContext } from "r-dom";
+import { setAttribute as _$setAttribute } from "r-dom";
const _tmpl$ = _$template(``, 2),
_tmpl$2 = _$template(``, 4),
@@ -10,7 +11,7 @@ const _tmpl$ = _$template(``, 2),
const template = (() => {
const _el$ = _$getNextElement(_tmpl$);
- _el$.setAttribute("some-attr", name);
+ _$setAttribute(_el$, "some-attr", name);
_el$.someProp = data;
_el$._context = _$currentContext();
@@ -26,7 +27,7 @@ const template2 = (() => {
_p$ => {
const _v$ = state.name,
_v$2 = state.data;
- _v$ !== _p$._v$ && _el$2.setAttribute("some-attr", (_p$._v$ = _v$));
+ _v$ !== _p$._v$ && _$setAttribute(_el$2, "some-attr", (_p$._v$ = _v$));
_v$2 !== _p$._v$2 && (_el$2.someProp = _p$._v$2 = _v$2);
return _p$;
},
diff --git a/packages/babel-plugin-jsx-dom-expressions/test/__dom_ssr_fixtures__/SVG/output.js b/packages/babel-plugin-jsx-dom-expressions/test/__dom_ssr_fixtures__/SVG/output.js
index 31f39e1d..61491ded 100644
--- a/packages/babel-plugin-jsx-dom-expressions/test/__dom_ssr_fixtures__/SVG/output.js
+++ b/packages/babel-plugin-jsx-dom-expressions/test/__dom_ssr_fixtures__/SVG/output.js
@@ -1,6 +1,7 @@
import { template as _$template } from "r-dom";
import { createComponent as _$createComponent } from "r-dom";
import { spread as _$spread } from "r-dom";
+import { setAttribute as _$setAttribute } from "r-dom";
import { effect as _$effect } from "r-dom";
import { getNextElement as _$getNextElement } from "r-dom";
@@ -36,10 +37,10 @@ const template2 = (() => {
_v$3 = state.x,
_v$4 = state.y,
_v$5 = props.stroke;
- _v$ !== _p$._v$ && _el$3.setAttribute("class", (_p$._v$ = _v$));
- _v$2 !== _p$._v$2 && _el$3.setAttribute("stroke-width", (_p$._v$2 = _v$2));
- _v$3 !== _p$._v$3 && _el$3.setAttribute("x", (_p$._v$3 = _v$3));
- _v$4 !== _p$._v$4 && _el$3.setAttribute("y", (_p$._v$4 = _v$4));
+ _v$ !== _p$._v$ && _$setAttribute(_el$3, "class", (_p$._v$ = _v$));
+ _v$2 !== _p$._v$2 && _$setAttribute(_el$3, "stroke-width", (_p$._v$2 = _v$2));
+ _v$3 !== _p$._v$3 && _$setAttribute(_el$3, "x", (_p$._v$3 = _v$3));
+ _v$4 !== _p$._v$4 && _$setAttribute(_el$3, "y", (_p$._v$4 = _v$4));
_v$5 !== _p$._v$5 && _el$3.style.setProperty("stroke-width", (_p$._v$5 = _v$5));
return _p$;
},
diff --git a/packages/babel-plugin-jsx-dom-expressions/test/__dom_ssr_fixtures__/customElements/output.js b/packages/babel-plugin-jsx-dom-expressions/test/__dom_ssr_fixtures__/customElements/output.js
index 5a97c16d..0c387242 100644
--- a/packages/babel-plugin-jsx-dom-expressions/test/__dom_ssr_fixtures__/customElements/output.js
+++ b/packages/babel-plugin-jsx-dom-expressions/test/__dom_ssr_fixtures__/customElements/output.js
@@ -2,6 +2,7 @@ import { template as _$template } from "r-dom";
import { effect as _$effect } from "r-dom";
import { getNextElement as _$getNextElement } from "r-dom";
import { currentContext as _$currentContext } from "r-dom";
+import { setAttribute as _$setAttribute } from "r-dom";
const _tmpl$ = _$template(``, 2),
_tmpl$2 = _$template(``, 4),
@@ -10,7 +11,7 @@ const _tmpl$ = _$template(``, 2),
const template = (() => {
const _el$ = _$getNextElement(_tmpl$, true);
- _el$.setAttribute("some-attr", name);
+ _$setAttribute(_el$, "some-attr", name);
_el$.someProp = data;
_el$._context = _$currentContext();
@@ -26,7 +27,7 @@ const template2 = (() => {
_p$ => {
const _v$ = state.name,
_v$2 = state.data;
- _v$ !== _p$._v$ && _el$2.setAttribute("some-attr", (_p$._v$ = _v$));
+ _v$ !== _p$._v$ && _$setAttribute(_el$2, "some-attr", (_p$._v$ = _v$));
_v$2 !== _p$._v$2 && (_el$2.someProp = _p$._v$2 = _v$2);
return _p$;
},
diff --git a/packages/dom-expressions/src/runtime.d.ts b/packages/dom-expressions/src/runtime.d.ts
index ee26e5d3..619a290a 100644
--- a/packages/dom-expressions/src/runtime.d.ts
+++ b/packages/dom-expressions/src/runtime.d.ts
@@ -31,6 +31,7 @@ export function delegateEvents(eventNames: string[]): void;
export function clearDelegatedEvents(): void;
export function spread(node: Element, accessor: any, isSVG?: Boolean, skipChildren?: Boolean): void;
export function assign(node: Element, props: any, isSVG?: Boolean, skipChildren?: Boolean): void;
+export function setAttribute(node : Element, name : string, value : any): void;
export function classList(
node: Element,
value: { [k: string]: boolean },
diff --git a/packages/dom-expressions/src/runtime.js b/packages/dom-expressions/src/runtime.js
index d6a3865e..66e1e8f7 100644
--- a/packages/dom-expressions/src/runtime.js
+++ b/packages/dom-expressions/src/runtime.js
@@ -1,5 +1,5 @@
import { Attributes, SVGAttributes, NonComposedEvents } from "./constants";
-import { root, effect, memo, currentContext, createComponent } from "rxcore";
+import { root, effect, memo, currentContext, createComponent } from "rxcore";
import reconcileArrays from "./reconcile";
const eventRegistry = new Set(),
@@ -21,7 +21,7 @@ export function renderToString(code, options = {}) {
hydration.context = { id: "0", count: 0 };
return root(() => {
const rendered = code();
- if (typeof rendered === "object" && 'then' in rendered) {
+ if (typeof rendered === "object" && "then" in rendered) {
const timeout = new Promise((_, reject) =>
setTimeout(() => reject("renderToString timed out"), options.timeoutMs)
);
@@ -46,7 +46,7 @@ export function renderDOMToString(code, options = {}) {
return html;
}
- if (typeof rendered === "object" && 'then' in rendered) {
+ if (typeof rendered === "object" && "then" in rendered) {
const timeout = new Promise((_, reject) =>
setTimeout(() => reject("renderToString timed out"), options.timeoutMs)
);
@@ -99,6 +99,11 @@ export function clearDelegatedEvents() {
eventRegistry.clear();
}
+export function setAttribute(node, name, value) {
+ if (value === false || value == null) node.removeAttribute(name);
+ else node.setAttribute(name, value);
+}
+
export function classList(node, value, prev) {
const classKeys = Object.keys(value);
for (let i = 0, len = classKeys.length; i < len; i++) {
@@ -200,13 +205,13 @@ export function ssr(template, ...nodes) {
}
const t = () => {
let result = "";
- for(let i = 0; i < template.length; i++) {
+ for (let i = 0; i < template.length; i++) {
result += template[i];
const node = rNodes[i];
if (node !== undefined) result += resolveSSRNode(node);
}
return result;
- }
+ };
t.isTemplate = true;
return t;
}
@@ -265,13 +270,13 @@ export function escape(html, attr) {
if (typeof html !== "string") return html;
return html.replace(attr ? ATTR_REGEX : CONTENT_REGEX, m => {
switch (m) {
- case '&':
- return '&';
- case '<':
- return '<';
+ case "&":
+ return "&";
+ case "<":
+ return "<";
case '"':
- return '"';
- }
+ return """;
+ }
});
}
@@ -471,7 +476,8 @@ function cleanChildren(parent, current, marker, replacement) {
const node = replacement || document.createTextNode("");
if (current.length) {
node !== current[0] && parent.replaceChild(node, current[0]);
- for (let i = current.length - 1; i > 0; i--) parent.removeChild(current[i]);
+ for (let i = current.length - 1; i > 0; i--)
+ current[i].parentNode === parent && parent.removeChild(current[i]);
} else parent.insertBefore(node, marker);
return [node];
}