Skip to content

Instantly share code, notes, and snippets.

@fuunnx
Last active September 4, 2018 07:46
Show Gist options
  • Save fuunnx/24dc7745e4d71111e4f83eb2de6255ab to your computer and use it in GitHub Desktop.
Save fuunnx/24dc7745e4d71111e4f83eb2de6255ab to your computer and use it in GitHub Desktop.
Snabbdom - Shallow copy duplicated vnode on DOME node creation
```sh
mkdir patches
mv snabbdom+0.7.0.patch patches/snabbdom+0.7.0.patch
npm install snabbdom@0.7.0 patch-package@5.1.1 && ./node_modules/.bin/patch-package
```
patch-package
--- a/node_modules/snabbdom/snabbdom.js
+++ b/node_modules/snabbdom/snabbdom.js
@@ -88,7 +88,8 @@ function init(modules, domApi) {
for (i = 0; i < children.length; ++i) {
var ch = children[i];
if (ch != null) {
- api.appendChild(elm, createElm(ch, insertedVnodeQueue));
+ children[i] = shallowCopy(children[i])
+ api.appendChild(elm, createElm(children[i], insertedVnodeQueue));
}
}
}
@@ -112,7 +113,21 @@ function init(modules, domApi) {
for (; startIdx <= endIdx; ++startIdx) {
var ch = vnodes[startIdx];
if (ch != null) {
- api.insertBefore(parentElm, createElm(ch, insertedVnodeQueue), before);
+ vnodes[startIdx] = shallowCopy(vnodes[startIdx])
+ try {
+ api.insertBefore(parentElm, createElm(vnodes[startIdx], insertedVnodeQueue), before);
+ } catch (e) {
+ console.log({parentElm, before, child: vnodes[startIdx]})
+ console.error(e)
+
+ console.log(elms(tree))
+
+ function elms(node) {
+ return node && [node.elm, (node.children || []).map(elms)]
+ }
+ debugger
+ // throw e
+ }
}
}
}
@@ -151,7 +166,12 @@ function init(modules, domApi) {
}
}
else {
+ try {
api.removeChild(parentElm, ch.elm);
+ } catch (e) {
+ console.log({parentElm, elm: ch.elm, ch})
+ console.error(e)
+ }
}
}
}
@@ -209,13 +229,15 @@ function init(modules, domApi) {
}
idxInOld = oldKeyToIdx[newStartVnode.key];
if (isUndef(idxInOld)) {
- api.insertBefore(parentElm, createElm(newStartVnode, insertedVnodeQueue), oldStartVnode.elm);
+ newCh[newStartIdx] = shallowCopy(newCh[newStartIdx])
+ api.insertBefore(parentElm, createElm(newCh[newStartIdx], insertedVnodeQueue), oldStartVnode.elm);
newStartVnode = newCh[++newStartIdx];
}
else {
elmToMove = oldCh[idxInOld];
if (elmToMove.sel !== newStartVnode.sel) {
- api.insertBefore(parentElm, createElm(newStartVnode, insertedVnodeQueue), oldStartVnode.elm);
+ newCh[newStartIdx] = shallowCopy(newCh[newStartIdx])
+ api.insertBefore(parentElm, createElm(newCh[newStartIdx], insertedVnodeQueue), oldStartVnode.elm);
}
else {
patchVnode(elmToMove, newStartVnode, insertedVnodeQueue);
@@ -228,6 +250,9 @@ function init(modules, domApi) {
}
if (oldStartIdx > oldEndIdx) {
before = newCh[newEndIdx + 1] == null ? null : newCh[newEndIdx + 1].elm;
+ if(before && before.parentNode !== parentElm) {
+ before = parentElm.childNodes[newEndIdx] || null
+ }
addVnodes(parentElm, before, newCh, newStartIdx, newEndIdx, insertedVnodeQueue);
}
else if (newStartIdx > newEndIdx) {
@@ -303,5 +328,15 @@ function init(modules, domApi) {
return vnode;
};
}
+
+function shallowCopy(node) {
+ if(!node || !node.elm) {return node;}
+ return {
+ ...node,
+ elm: undefined,
+ children: node.children && node.children.slice(),
+ }
+}
+
exports.init = init;
//# sourceMappingURL=snabbdom.js.map
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var vnode_1 = require("./vnode");
var is = require("./is");
var htmldomapi_1 = require("./htmldomapi");
function isUndef(s) { return s === undefined; }
function isDef(s) { return s !== undefined; }
var emptyNode = vnode_1.default('', {}, [], undefined, undefined);
function sameVnode(vnode1, vnode2) {
return vnode1.key === vnode2.key && vnode1.sel === vnode2.sel;
}
function isVnode(vnode) {
return vnode.sel !== undefined;
}
function createKeyToOldIdx(children, beginIdx, endIdx) {
var i, map = {}, key, ch;
for (i = beginIdx; i <= endIdx; ++i) {
ch = children[i];
if (ch != null) {
key = ch.key;
if (key !== undefined)
map[key] = i;
}
}
return map;
}
var hooks = ['create', 'update', 'remove', 'destroy', 'pre', 'post'];
var h_1 = require("./h");
exports.h = h_1.h;
var thunk_1 = require("./thunk");
exports.thunk = thunk_1.thunk;
function init(modules, domApi) {
var i, j, cbs = {};
var api = domApi !== undefined ? domApi : htmldomapi_1.default;
for (i = 0; i < hooks.length; ++i) {
cbs[hooks[i]] = [];
for (j = 0; j < modules.length; ++j) {
var hook = modules[j][hooks[i]];
if (hook !== undefined) {
cbs[hooks[i]].push(hook);
}
}
}
function emptyNodeAt(elm) {
var id = elm.id ? '#' + elm.id : '';
var c = elm.className ? '.' + elm.className.split(' ').join('.') : '';
return vnode_1.default(api.tagName(elm).toLowerCase() + id + c, {}, [], undefined, elm);
}
function createRmCb(childElm, listeners) {
return function rmCb() {
if (--listeners === 0) {
var parent_1 = api.parentNode(childElm);
api.removeChild(parent_1, childElm);
}
};
}
function createElm(vnode, insertedVnodeQueue) {
var i, data = vnode.data;
if (data !== undefined) {
if (isDef(i = data.hook) && isDef(i = i.init)) {
i(vnode);
data = vnode.data;
}
}
var children = vnode.children, sel = vnode.sel;
if (sel === '!') {
if (isUndef(vnode.text)) {
vnode.text = '';
}
vnode.elm = api.createComment(vnode.text);
}
else if (sel !== undefined) {
// Parse selector
var hashIdx = sel.indexOf('#');
var dotIdx = sel.indexOf('.', hashIdx);
var hash = hashIdx > 0 ? hashIdx : sel.length;
var dot = dotIdx > 0 ? dotIdx : sel.length;
var tag = hashIdx !== -1 || dotIdx !== -1 ? sel.slice(0, Math.min(hash, dot)) : sel;
var elm = vnode.elm = isDef(data) && isDef(i = data.ns) ? api.createElementNS(i, tag)
: api.createElement(tag);
if (hash < dot)
elm.setAttribute('id', sel.slice(hash + 1, dot));
if (dotIdx > 0)
elm.setAttribute('class', sel.slice(dot + 1).replace(/\./g, ' '));
for (i = 0; i < cbs.create.length; ++i)
cbs.create[i](emptyNode, vnode);
if (is.array(children)) {
for (i = 0; i < children.length; ++i) {
var ch = children[i];
if (ch != null) {
children[i] = shallowCopy(children[i])
api.appendChild(elm, createElm(children[i], insertedVnodeQueue));
}
}
}
else if (is.primitive(vnode.text)) {
api.appendChild(elm, api.createTextNode(vnode.text));
}
i = vnode.data.hook; // Reuse variable
if (isDef(i)) {
if (i.create)
i.create(emptyNode, vnode);
if (i.insert)
insertedVnodeQueue.push(vnode);
}
}
else {
vnode.elm = api.createTextNode(vnode.text);
}
return vnode.elm;
}
function addVnodes(parentElm, before, vnodes, startIdx, endIdx, insertedVnodeQueue) {
for (; startIdx <= endIdx; ++startIdx) {
var ch = vnodes[startIdx];
if (ch != null) {
vnodes[startIdx] = shallowCopy(vnodes[startIdx])
try {
api.insertBefore(parentElm, createElm(vnodes[startIdx], insertedVnodeQueue), before);
} catch (e) {
console.log({parentElm, before, child: vnodes[startIdx]})
console.error(e)
console.log(elms(tree))
function elms(node) {
return node && [node.elm, (node.children || []).map(elms)]
}
debugger
// throw e
}
}
}
}
function invokeDestroyHook(vnode) {
var i, j, data = vnode.data;
if (data !== undefined) {
if (isDef(i = data.hook) && isDef(i = i.destroy))
i(vnode);
for (i = 0; i < cbs.destroy.length; ++i)
cbs.destroy[i](vnode);
if (vnode.children !== undefined) {
for (j = 0; j < vnode.children.length; ++j) {
i = vnode.children[j];
if (i != null && typeof i !== "string") {
invokeDestroyHook(i);
}
}
}
}
}
function removeVnodes(parentElm, vnodes, startIdx, endIdx) {
for (; startIdx <= endIdx; ++startIdx) {
var i_1 = void 0, listeners = void 0, rm = void 0, ch = vnodes[startIdx];
if (ch != null) {
if (isDef(ch.sel)) {
invokeDestroyHook(ch);
listeners = cbs.remove.length + 1;
rm = createRmCb(ch.elm, listeners);
for (i_1 = 0; i_1 < cbs.remove.length; ++i_1)
cbs.remove[i_1](ch, rm);
if (isDef(i_1 = ch.data) && isDef(i_1 = i_1.hook) && isDef(i_1 = i_1.remove)) {
i_1(ch, rm);
}
else {
rm();
}
}
else {
try {
api.removeChild(parentElm, ch.elm);
} catch (e) {
console.log({parentElm, elm: ch.elm, ch})
console.error(e)
}
}
}
}
}
function updateChildren(parentElm, oldCh, newCh, insertedVnodeQueue) {
var oldStartIdx = 0, newStartIdx = 0;
var oldEndIdx = oldCh.length - 1;
var oldStartVnode = oldCh[0];
var oldEndVnode = oldCh[oldEndIdx];
var newEndIdx = newCh.length - 1;
var newStartVnode = newCh[0];
var newEndVnode = newCh[newEndIdx];
var oldKeyToIdx;
var idxInOld;
var elmToMove;
var before;
while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) {
if (oldStartVnode == null) {
oldStartVnode = oldCh[++oldStartIdx]; // Vnode might have been moved left
}
else if (oldEndVnode == null) {
oldEndVnode = oldCh[--oldEndIdx];
}
else if (newStartVnode == null) {
newStartVnode = newCh[++newStartIdx];
}
else if (newEndVnode == null) {
newEndVnode = newCh[--newEndIdx];
}
else if (sameVnode(oldStartVnode, newStartVnode)) {
patchVnode(oldStartVnode, newStartVnode, insertedVnodeQueue);
oldStartVnode = oldCh[++oldStartIdx];
newStartVnode = newCh[++newStartIdx];
}
else if (sameVnode(oldEndVnode, newEndVnode)) {
patchVnode(oldEndVnode, newEndVnode, insertedVnodeQueue);
oldEndVnode = oldCh[--oldEndIdx];
newEndVnode = newCh[--newEndIdx];
}
else if (sameVnode(oldStartVnode, newEndVnode)) {
patchVnode(oldStartVnode, newEndVnode, insertedVnodeQueue);
api.insertBefore(parentElm, oldStartVnode.elm, api.nextSibling(oldEndVnode.elm));
oldStartVnode = oldCh[++oldStartIdx];
newEndVnode = newCh[--newEndIdx];
}
else if (sameVnode(oldEndVnode, newStartVnode)) {
patchVnode(oldEndVnode, newStartVnode, insertedVnodeQueue);
api.insertBefore(parentElm, oldEndVnode.elm, oldStartVnode.elm);
oldEndVnode = oldCh[--oldEndIdx];
newStartVnode = newCh[++newStartIdx];
}
else {
if (oldKeyToIdx === undefined) {
oldKeyToIdx = createKeyToOldIdx(oldCh, oldStartIdx, oldEndIdx);
}
idxInOld = oldKeyToIdx[newStartVnode.key];
if (isUndef(idxInOld)) {
newCh[newStartIdx] = shallowCopy(newCh[newStartIdx])
api.insertBefore(parentElm, createElm(newCh[newStartIdx], insertedVnodeQueue), oldStartVnode.elm);
newStartVnode = newCh[++newStartIdx];
}
else {
elmToMove = oldCh[idxInOld];
if (elmToMove.sel !== newStartVnode.sel) {
newCh[newStartIdx] = shallowCopy(newCh[newStartIdx])
api.insertBefore(parentElm, createElm(newCh[newStartIdx], insertedVnodeQueue), oldStartVnode.elm);
}
else {
patchVnode(elmToMove, newStartVnode, insertedVnodeQueue);
oldCh[idxInOld] = undefined;
api.insertBefore(parentElm, elmToMove.elm, oldStartVnode.elm);
}
newStartVnode = newCh[++newStartIdx];
}
}
}
if (oldStartIdx > oldEndIdx) {
before = newCh[newEndIdx + 1] == null ? null : newCh[newEndIdx + 1].elm;
if(before && before.parentNode !== parentElm) {
before = parentElm.childNodes[newEndIdx] || null
}
addVnodes(parentElm, before, newCh, newStartIdx, newEndIdx, insertedVnodeQueue);
}
else if (newStartIdx > newEndIdx) {
removeVnodes(parentElm, oldCh, oldStartIdx, oldEndIdx);
}
}
function patchVnode(oldVnode, vnode, insertedVnodeQueue) {
var i, hook;
if (isDef(i = vnode.data) && isDef(hook = i.hook) && isDef(i = hook.prepatch)) {
i(oldVnode, vnode);
}
var elm = vnode.elm = oldVnode.elm;
var oldCh = oldVnode.children;
var ch = vnode.children;
if (oldVnode === vnode)
return;
if (vnode.data !== undefined) {
for (i = 0; i < cbs.update.length; ++i)
cbs.update[i](oldVnode, vnode);
i = vnode.data.hook;
if (isDef(i) && isDef(i = i.update))
i(oldVnode, vnode);
}
if (isUndef(vnode.text)) {
if (isDef(oldCh) && isDef(ch)) {
if (oldCh !== ch)
updateChildren(elm, oldCh, ch, insertedVnodeQueue);
}
else if (isDef(ch)) {
if (isDef(oldVnode.text))
api.setTextContent(elm, '');
addVnodes(elm, null, ch, 0, ch.length - 1, insertedVnodeQueue);
}
else if (isDef(oldCh)) {
removeVnodes(elm, oldCh, 0, oldCh.length - 1);
}
else if (isDef(oldVnode.text)) {
api.setTextContent(elm, '');
}
}
else if (oldVnode.text !== vnode.text) {
api.setTextContent(elm, vnode.text);
}
if (isDef(hook) && isDef(i = hook.postpatch)) {
i(oldVnode, vnode);
}
}
return function patch(oldVnode, vnode) {
var i, elm, parent;
var insertedVnodeQueue = [];
for (i = 0; i < cbs.pre.length; ++i)
cbs.pre[i]();
if (!isVnode(oldVnode)) {
oldVnode = emptyNodeAt(oldVnode);
}
if (sameVnode(oldVnode, vnode)) {
patchVnode(oldVnode, vnode, insertedVnodeQueue);
}
else {
elm = oldVnode.elm;
parent = api.parentNode(elm);
createElm(vnode, insertedVnodeQueue);
if (parent !== null) {
api.insertBefore(parent, vnode.elm, api.nextSibling(elm));
removeVnodes(parent, [oldVnode], 0, 0);
}
}
for (i = 0; i < insertedVnodeQueue.length; ++i) {
insertedVnodeQueue[i].data.hook.insert(insertedVnodeQueue[i]);
}
for (i = 0; i < cbs.post.length; ++i)
cbs.post[i]();
return vnode;
};
}
function shallowCopy(node) {
if(!node || !node.elm) {return node;}
return {
...node,
elm: undefined,
children: node.children && node.children.slice(),
}
}
exports.init = init;
//# sourceMappingURL=snabbdom.js.map
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment