woby
Version:
A high-performance framework with fine-grained observable/signal-based reactivity for building rich applications.
168 lines (167 loc) • 6.44 kB
JavaScript
;
const setters = require("./setters-CyO2VUT6.cjs");
const wrap_clone_element = require("./wrap_clone_element-C4XivH3I.cjs");
const template = (fn) => {
const safePropertyRe = /^[a-z0-9-_]+$/i;
const checkValidProperty = (property) => {
if (setters.isString(property) && safePropertyRe.test(property)) return true;
throw new Error(`Invalid property, only alphanumeric properties are allowed inside templates, received: "${property}"`);
};
const makeAccessor = (actionsWithNodes) => {
return new Proxy({}, {
get(target, prop) {
checkValidProperty(prop);
const accessor = (node, method, key, targetNode) => {
if (key) checkValidProperty(key);
actionsWithNodes.push([node, method, prop, key, targetNode]);
};
const metadata = { [setters.SYMBOL_TEMPLATE_ACCESSOR]: true };
return setters.assign(accessor, metadata);
}
});
};
const makeActionsWithNodesAndTemplate = () => {
const actionsWithNodes = [];
const accessor = makeAccessor(actionsWithNodes);
const component = fn(accessor);
if (setters.isFunction(component)) {
const root = component();
if (root instanceof Element) {
return { actionsWithNodes, root };
}
}
throw new Error("Invalid template, it must return a function that returns an Element");
};
const makeActionsWithPaths = (actionsWithNodes) => {
const actionsWithPaths = [];
for (let i = 0, l = actionsWithNodes.length; i < l; i++) {
const [node, method, prop, key, targetNode] = actionsWithNodes[i];
const nodePath = makeNodePath(node);
const targetNodePath = targetNode ? makeNodePath(targetNode) : void 0;
actionsWithPaths.push([nodePath, method, prop, key, targetNodePath]);
}
return actionsWithPaths;
};
const makeNodePath = /* @__PURE__ */ (() => {
let prevNode = null;
let prevPath;
return (node) => {
if (node === prevNode) return prevPath;
const path = [];
let child = node;
let parent = child.parentNode;
while (parent) {
const index = !child.previousSibling ? 0 : !child.nextSibling ? -0 : setters.indexOf(parent.childNodes, child);
path.push(index);
child = parent;
parent = parent.parentNode;
}
prevNode = node;
prevPath = path;
return path;
};
})();
const makeNodePathProperties = (path) => {
const properties = ["root"];
const parts = path.slice().reverse();
for (let i = 0, l = parts.length; i < l; i++) {
const part = parts[i];
if (Object.is(0, part)) {
properties.push("firstChild");
} else if (Object.is(-0, part)) {
properties.push("lastChild");
} else {
properties.push("firstChild");
for (let nsi = 0; nsi < part; nsi++) {
properties.push("nextSibling");
}
}
}
return properties;
};
const makeReviverPaths = (actionsWithPaths) => {
const paths = [];
for (let i = 0, l = actionsWithPaths.length; i < l; i++) {
const action = actionsWithPaths[i];
const nodePath = action[0];
const targetNodePath = action[4];
paths.push(nodePath);
if (targetNodePath) {
paths.push(targetNodePath);
}
}
return paths;
};
const makeReviverVariablesData = (paths, properties) => {
const data = new Array(paths.length);
for (let i = 0, l = paths.length; i < l; i++) {
data[i] = {
path: paths[i],
properties: properties[i]
};
}
return data;
};
const makeReviverVariables = (actionsWithPaths) => {
const paths = makeReviverPaths(actionsWithPaths);
const properties = paths.map(makeNodePathProperties);
const data = makeReviverVariablesData(paths, properties);
const assignments = [];
const map = /* @__PURE__ */ new Map();
let variableId = 0;
while (true) {
const datum = data.find((datum2) => datum2.properties.length > 1);
if (!datum) break;
const [current, next] = datum.properties;
const variable = `$${variableId++}`;
const assignment = `const ${variable} = ${current}.${next};`;
assignments.push(assignment);
for (let i = 0, l = data.length; i < l; i++) {
const datum2 = data[i];
const [otherCurrent, otherNext] = datum2.properties;
if (otherCurrent !== current || otherNext !== next) continue;
datum2.properties[0] = variable;
datum2.properties.splice(1, 1);
}
}
for (let i = 0, l = data.length; i < l; i++) {
const datum = data[i];
map.set(datum.path, datum.properties[0]);
}
return { assignments, map };
};
const makeReviverActions = (actionsWithPaths, variables) => {
const actions = [];
for (let i = 0, l = actionsWithPaths.length; i < l; i++) {
const [nodePath, method, prop, key, targetNodePath] = actionsWithPaths[i];
if (targetNodePath) {
actions.push(`this.${method} ( props["${prop}"], ${variables.get(targetNodePath)} );`);
} else if (key) {
actions.push(`this.${method} ( ${variables.get(nodePath)}, "${key}", props["${prop}"] );`);
} else {
actions.push(`this.${method} ( ${variables.get(nodePath)}, props["${prop}"] );`);
}
}
return actions;
};
const makeReviver = (actionsWithPaths) => {
const { assignments, map } = makeReviverVariables(actionsWithPaths);
const actions = makeReviverActions(actionsWithPaths, map);
const fn2 = new Function("root", "props", `${assignments.join("")}${actions.join("")}return root;`);
const apis = { setAttribute: setters.setAttribute, setChildReplacement: setters.setChildReplacement, setClasses: setters.setClasses, setEvent: setters.setEvent, setHTML: setters.setHTML, setProperty: setters.setProperty, setRef: setters.setRef, setStyles: setters.setStyles };
const reviver = fn2.bind(apis);
return reviver;
};
const makeComponent = () => {
const { actionsWithNodes, root } = makeActionsWithNodesAndTemplate();
const actionsWithPaths = makeActionsWithPaths(actionsWithNodes);
const reviver = makeReviver(actionsWithPaths);
return (props) => {
const clone = root.cloneNode(true);
return wrap_clone_element.wrapElement(reviver.bind(void 0, clone, props));
};
};
return makeComponent();
};
exports.template = template;
//# sourceMappingURL=template-C8XYMS58.cjs.map