@skirtle/vue-vnode-utils
Version:
Utilities for manipulating Vue 3 VNodes
438 lines (437 loc) • 13.7 kB
JavaScript
var VueVNodeUtils = (function(exports, vue) {
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
//#region src/base.ts
const isComment = (vnode) => {
return getType(vnode) === "comment";
};
const isComponent = (vnode) => {
return getType(vnode) === "component";
};
const isElement = (vnode) => {
return getType(vnode) === "element";
};
const isFragment = (vnode) => {
return getType(vnode) === "fragment";
};
const isFunctionalComponent = (vnode) => {
return isComponent(vnode) && typeof vnode.type === "function";
};
const isStatefulComponent = (vnode) => {
return isComponent(vnode) && typeof vnode.type === "object";
};
const isStatic = (vnode) => {
return getType(vnode) === "static";
};
const isText = (vnode) => {
return getType(vnode) === "text";
};
const getText = (vnode) => {
if (typeof vnode === "string") return vnode;
if (typeof vnode === "number") return String(vnode);
if ((0, vue.isVNode)(vnode) && vnode.type === vue.Text) return String(vnode.children);
};
const getType = (vnode) => {
const typeofVNode = typeof vnode;
if (vnode == null || typeofVNode === "boolean") return "comment";
else if (typeofVNode === "string" || typeofVNode === "number") return "text";
else if (Array.isArray(vnode)) return "fragment";
if ((0, vue.isVNode)(vnode)) {
const { type } = vnode;
const typeofType = typeof type;
if (typeofType === "symbol") {
if (type === vue.Fragment) return "fragment";
else if (type === vue.Text) return "text";
else if (type === vue.Comment) return "comment";
else if (type === vue.Static) return "static";
} else if (typeofType === "string") return "element";
else if (typeofType === "object" || typeofType === "function") return "component";
}
};
//#endregion
//#region src/iterators.ts
const warn = (method, msg) => {
console.warn(`[${method}] ${msg}`);
};
const checkArguments = (method, passed, expected) => {
for (let index = 0; index < passed.length; ++index) {
const t = typeOf(passed[index]);
const expect = expected[index];
if (expect !== t) warn(method, `Argument ${index + 1} was ${t}, should be ${expect}`);
}
};
const isEmptyObject = (obj) => {
for (const prop in obj) return false;
return true;
};
const typeOf = (value) => {
let t = typeof value;
if (t === "object") {
if (value === null) t = "null";
else if (Array.isArray(value)) t = "array";
else if ((0, vue.isVNode)(value)) t = "vnode";
else if (value instanceof Date) t = "date";
else if (value instanceof RegExp) t = "regexp";
}
return t;
};
const getFragmentChildren = (fragmentVNode) => {
if (Array.isArray(fragmentVNode)) return fragmentVNode;
const { children } = fragmentVNode;
if (Array.isArray(children)) return children;
warn("getFragmentChildren", `Unknown children for fragment: ${typeOf(children)}`);
return [];
};
function freeze(obj) {
return Object.freeze(obj);
}
const COMPONENTS_AND_ELEMENTS = /* @__PURE__ */ freeze({
element: true,
component: true
});
const SKIP_COMMENTS = /* @__PURE__ */ freeze({
element: true,
component: true,
text: true,
static: true
});
const ALL_VNODES = /* @__PURE__ */ freeze({
element: true,
component: true,
text: true,
static: true,
comment: true
});
const promoteToVNode = (node, options) => {
const type = getType(node);
if (!type || type === "fragment" || !options[type]) return null;
if ((0, vue.isVNode)(node)) return node;
if (type === "text") return (0, vue.createTextVNode)(getText(node));
return (0, vue.createCommentVNode)();
};
const addProps$1 = (children, callback, options = COMPONENTS_AND_ELEMENTS) => {
checkArguments("addProps", [
children,
callback,
options
], [
"array",
"function",
"object"
]);
return replaceChildrenInternal(children, (vnode) => {
const props = callback(vnode);
{
const typeofProps = typeOf(props);
if (![
"object",
"null",
"undefined"
].includes(typeofProps)) warn("addProps", `Callback returned unexpected ${typeofProps}: ${String(props)}`);
}
if (props && !isEmptyObject(props)) return (0, vue.cloneVNode)(vnode, props, true);
}, options);
};
const replaceChildren$1 = (children, callback, options = SKIP_COMMENTS) => {
checkArguments("replaceChildren", [
children,
callback,
options
], [
"array",
"function",
"object"
]);
return replaceChildrenInternal(children, callback, options);
};
const replaceChildrenInternal = (children, callback, options) => {
var _nc3;
let nc = null;
for (let index = 0; index < children.length; ++index) {
const child = children[index];
if (isFragment(child)) {
const oldFragmentChildren = getFragmentChildren(child);
const newFragmentChildren = replaceChildrenInternal(oldFragmentChildren, callback, options);
let newChild = child;
if (oldFragmentChildren !== newFragmentChildren) {
var _nc;
(_nc = nc) !== null && _nc !== void 0 || (nc = children.slice(0, index));
if (Array.isArray(child)) newChild = newFragmentChildren;
else {
newChild = (0, vue.cloneVNode)(child);
newChild.children = newFragmentChildren;
}
}
nc && nc.push(newChild);
} else {
const vnode = promoteToVNode(child, options);
if (vnode) {
var _callback;
const newNodes = (_callback = callback(vnode)) !== null && _callback !== void 0 ? _callback : vnode;
{
const typeOfNewNodes = typeOf(newNodes);
if (![
"array",
"vnode",
"string",
"number",
"undefined"
].includes(typeOfNewNodes)) warn("replaceChildren", `Callback returned unexpected ${typeOfNewNodes} ${String(newNodes)}`);
}
if (newNodes !== child) {
var _nc2;
(_nc2 = nc) !== null && _nc2 !== void 0 || (nc = children.slice(0, index));
}
if (Array.isArray(newNodes)) nc && nc.push(...newNodes);
else nc && nc.push(newNodes);
} else nc && nc.push(child);
}
}
return (_nc3 = nc) !== null && _nc3 !== void 0 ? _nc3 : children;
};
const betweenChildren$1 = (children, callback, options = SKIP_COMMENTS) => {
checkArguments("betweenChildren", [
children,
callback,
options
], [
"array",
"function",
"object"
]);
let previousVNode = null;
return replaceChildrenInternal(children, (vnode) => {
let insertedNodes = void 0;
if (previousVNode) {
insertedNodes = callback(previousVNode, vnode);
{
const typeOfInsertedNodes = typeOf(insertedNodes);
if (![
"array",
"vnode",
"string",
"number",
"undefined"
].includes(typeOfInsertedNodes)) warn("betweenChildren", `Callback returned unexpected ${typeOfInsertedNodes} ${String(insertedNodes)}`);
}
}
previousVNode = vnode;
if (insertedNodes == null || Array.isArray(insertedNodes) && insertedNodes.length === 0) return;
if (Array.isArray(insertedNodes)) return [...insertedNodes, vnode];
return [insertedNodes, vnode];
}, options);
};
const someChild$1 = (children, callback, options = ALL_VNODES) => {
checkArguments("someChild", [
children,
callback,
options
], [
"array",
"function",
"object"
]);
return someChildInternal(children, callback, options);
};
const someChildInternal = (children, callback, options) => {
for (const child of children) if (isFragment(child)) {
if (someChild$1(getFragmentChildren(child), callback, options)) return true;
} else {
const vnode = promoteToVNode(child, options);
if (vnode && callback(vnode)) return true;
}
return false;
};
const everyChild$1 = (children, callback, options = ALL_VNODES) => {
checkArguments("everyChild", [
children,
callback,
options
], [
"array",
"function",
"object"
]);
return !someChildInternal(children, (vnode) => !callback(vnode), options);
};
const eachChild$1 = (children, callback, options = ALL_VNODES) => {
checkArguments("eachChild", [
children,
callback,
options
], [
"array",
"function",
"object"
]);
someChildInternal(children, (vnode) => {
callback(vnode);
}, options);
};
const findChild$1 = (children, callback, options = ALL_VNODES) => {
checkArguments("findChild", [
children,
callback,
options
], [
"array",
"function",
"object"
]);
let node = void 0;
someChildInternal(children, (vnode) => {
if (callback(vnode)) {
node = vnode;
return true;
}
}, options);
return node;
};
const reduceChildren$1 = (children, callback, initialValue, options = ALL_VNODES) => {
checkArguments("reduceChildren", [
children,
callback,
null,
options
], [
"array",
"function",
"null",
"object"
]);
someChildInternal(children, (vnode) => {
initialValue = callback(initialValue, vnode);
}, options);
return initialValue;
};
const COLLAPSIBLE_WHITESPACE_RE = /\S|\u00a0/;
const isEmpty = (children) => {
checkArguments("isEmpty", [children], ["array"]);
return !someChildInternal(children, (vnode) => {
if (isText(vnode)) {
const text = getText(vnode) || "";
return COLLAPSIBLE_WHITESPACE_RE.test(text);
}
return true;
}, SKIP_COMMENTS);
};
const extractSingleChild = (children) => {
checkArguments("extractSingleChild", [children], ["array"]);
const node = findChild$1(children, () => {
return true;
}, COMPONENTS_AND_ELEMENTS);
someChildInternal(children, (vnode) => {
let warning = "";
if (vnode === node) return false;
if (isElement(vnode) || isComponent(vnode)) warning = "Multiple root nodes found, only one expected";
else if (isText(vnode)) {
const text = getText(vnode) || "";
if (COLLAPSIBLE_WHITESPACE_RE.test(text)) warning = `Non-empty text node:\n'${text}'`;
} else warning = `Encountered unexpected ${getType(vnode)} VNode`;
if (warning) {
warn("extractSingleChild", warning);
return true;
}
}, SKIP_COMMENTS);
return node;
};
const countChildren = (children, options = ALL_VNODES) => {
checkArguments("count", [children, options], ["array", "object"]);
let count = 0;
someChildInternal(children, () => {
++count;
}, options);
return count;
};
//#endregion
//#region src/with-meta/iterators.ts
function checkFunction(method, callback) {
checkArguments(method, [callback], ["function"]);
}
function setPropertyValue(obj, key, value) {
return Object.defineProperty(obj, key, {
value,
enumerable: true
});
}
function createMetaFactory(children, options) {
let index = -1;
const baseMeta = { get length() {
const length = countChildren(children, options);
setPropertyValue(baseMeta, "length", length);
return length;
} };
return () => {
return setPropertyValue(Object.create(baseMeta), "index", ++index);
};
}
function withMeta(iterator, children, callback, options) {
const metaFactory = createMetaFactory(children, options);
return iterator(children, (vnode) => {
return callback(vnode, metaFactory());
}, options);
}
const addProps = (children, callback, options = COMPONENTS_AND_ELEMENTS) => {
checkFunction("addProps", callback);
return withMeta(addProps$1, children, callback, options);
};
const replaceChildren = (children, callback, options = SKIP_COMMENTS) => {
checkFunction("replaceChildren", callback);
return withMeta(replaceChildren$1, children, callback, options);
};
const betweenChildren = (children, callback, options = SKIP_COMMENTS) => {
checkFunction("betweenChildren", callback);
const metaFactory = createMetaFactory(children, options);
return betweenChildren$1(children, (previousVNode, nextVNode) => {
return callback(previousVNode, nextVNode, metaFactory());
}, options);
};
const someChild = (children, callback, options = ALL_VNODES) => {
checkFunction("someChild", callback);
return withMeta(someChild$1, children, callback, options);
};
const everyChild = (children, callback, options = ALL_VNODES) => {
checkFunction("everyChild", callback);
return withMeta(everyChild$1, children, callback, options);
};
const eachChild = (children, callback, options = ALL_VNODES) => {
checkFunction("eachChild", callback);
return withMeta(eachChild$1, children, callback, options);
};
const findChild = (children, callback, options = ALL_VNODES) => {
checkFunction("findChild", callback);
return withMeta(findChild$1, children, callback, options);
};
const reduceChildren = (children, callback, initialValue, options = ALL_VNODES) => {
checkFunction("reduceChildren", callback);
const metaFactory = createMetaFactory(children, options);
return reduceChildren$1(children, (previousValue, vnode) => {
return callback(previousValue, vnode, metaFactory());
}, initialValue, options);
};
//#endregion
exports.ALL_VNODES = ALL_VNODES;
exports.COMPONENTS_AND_ELEMENTS = COMPONENTS_AND_ELEMENTS;
exports.SKIP_COMMENTS = SKIP_COMMENTS;
exports.addProps = addProps;
exports.betweenChildren = betweenChildren;
exports.countChildren = countChildren;
exports.eachChild = eachChild;
exports.everyChild = everyChild;
exports.extractSingleChild = extractSingleChild;
exports.findChild = findChild;
exports.getText = getText;
exports.getType = getType;
exports.isComment = isComment;
exports.isComponent = isComponent;
exports.isElement = isElement;
exports.isEmpty = isEmpty;
exports.isFragment = isFragment;
exports.isFunctionalComponent = isFunctionalComponent;
exports.isStatefulComponent = isStatefulComponent;
exports.isStatic = isStatic;
exports.isText = isText;
exports.reduceChildren = reduceChildren;
exports.replaceChildren = replaceChildren;
exports.someChild = someChild;
return exports;
})({}, Vue);