Skip to content

Commit

Permalink
Merge 5c1b153 into 973fec6
Browse files Browse the repository at this point in the history
  • Loading branch information
Masquerade-Circus committed Nov 29, 2019
2 parents 973fec6 + 5c1b153 commit d258092
Show file tree
Hide file tree
Showing 10 changed files with 453 additions and 39 deletions.
6 changes: 3 additions & 3 deletions .size-snapshot.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"dist/valyrian.min.js": {
"bundled": 11255,
"minified": 4493,
"gzipped": 1927
"bundled": 11764,
"minified": 4808,
"gzipped": 2038
},
"dist/store.min.js": {
"bundled": 11415,
Expand Down
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
language: node_js

node_js:
- stable
- lts/*

install:
- yarn
Expand Down
2 changes: 1 addition & 1 deletion dist/valyrian.min.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion dist/valyrian.min.js.map

Large diffs are not rendered by default.

80 changes: 49 additions & 31 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,9 @@ function Vnode(name, props, children) {
this.children = children;
this.name = name;
};
Vnode.prototype = {
isSVG: FALSE
};

function TextVnode() {}
TextVnode.prototype = {
isSVG: FALSE,
props: emptyObject,
children: emptyArray
};
Expand Down Expand Up @@ -53,19 +49,16 @@ v.dom2vnode = function (dom) {
vnode = new TextVnode();
vnode.dom = dom;
} else if (dom.nodeType === 1) {
let name = dom.nodeName;
let props = {};
emptyArray.forEach.call(dom.attributes, ({nodeName, nodeValue}) => props[nodeName] = nodeValue);

vnode = new Vnode(
name,
{},
dom.nodeName,
props,
[]
);
vnode.dom = dom;

for (let l = dom.attributes.length; l--;) {
let property = dom.attributes[l];
vnode.props[property.nodeName] = property.nodeValue;
}

for (let i = 0, l = dom.childNodes.length; i < l; i++) {
let childVnode = v.dom2vnode(dom.childNodes[i]);
childVnode && vnode.children.push(childVnode);
Expand Down Expand Up @@ -112,6 +105,7 @@ function eventListener(e) {

function lifecycleCall(vnode, methodName, oldNode) {
if (methodName === onremove) {
cleanupVnode(vnode);
for (let i = 0, l = vnode.children.length; i < l; i++) {
lifecycleCall(vnode.children[i], onremove);
}
Expand Down Expand Up @@ -203,10 +197,36 @@ function getKeys(list) {
return keys;
}

v.onCleanup = function (callback) {
if (v.current.parentVnode.onCleanup === UND) {
v.current.parentVnode.onCleanup = [];
v.current.parentVnode.cleanUp = TRUE;
}
v.current.parentVnode.onCleanup.push(callback);
};

function cleanupVnode(vnode) {
if (vnode.cleanUp) {
for (let i = 0, l = vnode.onCleanup.length; i < l; i++) {
vnode.onCleanup[i]();
}
}
}

v.current = {
parentVnode: NIL,
oldParentVnode: NIL,
component: NIL
};

// eslint-disable-next-line complexity,sonarjs/cognitive-complexity
function patch(parentNode, oldParentNode, isSVG) {
let {dom: $parent, children: newTree} = parentNode;
let oldTree = oldParentNode.children;
v.current.parentVnode = parentNode;
v.current.oldParentVnode = oldParentNode;
cleanupVnode(oldParentNode);

if (Array.isArray(newTree) === FALSE) {
newTree = [newTree];
}
Expand All @@ -221,18 +241,21 @@ function patch(parentNode, oldParentNode, isSVG) {

// Flatten children
for (let i = 0; i < newTree.length; i++) {
let currentNode = newTree[i];
let childVnode = newTree[i];

if (currentNode instanceof Vnode) {
if (typeof currentNode.name !== 'string') {
let component = currentNode.name.view || currentNode.name;
newTree.splice(i, 1, ...[component.call(component, currentNode.props, ...currentNode.children)]);
if (childVnode instanceof Vnode) {
if (typeof childVnode.name !== 'string') {
v.current.component = childVnode;
let viewMethod = 'view' in childVnode.name ? childVnode.name.view : childVnode.name;
newTree.splice(i, 1, ...[viewMethod.call(viewMethod, childVnode.props, ...childVnode.children)]);
i--;
} else if (currentNode.name === 'svg') {
currentNode.isSVG = TRUE;
} else {
if (childVnode.name === 'svg') {
childVnode.isSVG = TRUE;
}
}
} else if (Array.isArray(currentNode)) {
newTree.splice(i, 1, ...currentNode);
} else if (Array.isArray(childVnode)) {
newTree.splice(i, 1, ...childVnode);
i--;
}
}
Expand Down Expand Up @@ -289,6 +312,7 @@ function patch(parentNode, oldParentNode, isSVG) {
let newNode = newTree[i];
let oldNode = oldTree[i];


// Is vnode
if (newNode instanceof Vnode) {
isSVG = isSVG || newNode.isSVG;
Expand All @@ -310,7 +334,6 @@ function patch(parentNode, oldParentNode, isSVG) {
// Is text
} else {
let dom;

// Added
if (oldNode === UND) {
dom = document.createTextNode(newNode);
Expand All @@ -323,6 +346,7 @@ function patch(parentNode, oldParentNode, isSVG) {
// Updated
} else {
dom = oldNode.dom;
newNode = String(newNode);
if (newNode !== dom.nodeValue) {
dom.nodeValue = newNode;
}
Expand All @@ -342,7 +366,7 @@ v.update = (props, ...children) => {
if (mainNode) {
if (mountedComponent !== UND) {
oldMainNode = mainNode;
mainNode = new Vnode(mainNode.name, mainNode.props, [v(mountedComponent, props, children)]);
mainNode = new Vnode(mainNode.name, mainNode.props, v(mountedComponent, props, children));
mainNode.dom = oldMainNode.dom;
patch(mainNode, oldMainNode, mainNode.isSVG);
v.isMounted = TRUE;
Expand All @@ -366,21 +390,15 @@ v.mount = (container, component, props, ...children) => {
};

v.unmount = () => {
mountedComponent = () => {};
mountedComponent = () => '';
let result = v.update();
mountedComponent = UND;
v.isMounted = FALSE;
return result;
};

v.directive = (directive, handler) => directive in v.reservedWords === FALSE && (v.reservedWords[directive] = handler);
v.directive('v-for', (set, vnode) => {
let handler = vnode.children[0];
vnode.children = [];
for (let i = 0, l = set.length; i < l; i++) {
vnode.children[i] = handler(set[i], i);
}
});
v.directive('v-for', (set, vnode) => vnode.children = set.map(vnode.children[0]));

let hideDirective = (test) => (bool, vnode, oldnode) => {
if (bool === test) {
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
],
"scripts": {
"dev:source": "cross-env NODE_ENV=development node rollupSource.js",
"dev:bench": "cross-env NODE_ENV=development node dev.js -w",
"dev:scratchpad": "cross-env NODE_ENV=development nodemon -w ./test -w ./lib -w ./plugins -w ./scratchpad.js --exec 'mocha --timeout 20000 --slow 0 --require @babel/register --require esm \"scratchpad.js\"'",
"dev:test": "cross-env NODE_ENV=development nodemon -w ./test -w ./lib -w ./plugins --exec 'mocha --timeout 20000 --slow 0 --require @babel/register --require esm \"test/**/*_test.js\"'",
"dev:test:nyc": "cross-env NODE_ENV=development nodemon -w ./test -w ./lib -w ./plugins --exec 'nyc --reporter=text --reporter=lcov mocha --timeout 20000 --slow 0 --require @babel/register --require esm \"test/**/*_test.js\"'",
"build": "yarn build:source && yarn remark",
Expand Down
116 changes: 116 additions & 0 deletions plugins/hooks.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
let plugin = (v) => {
let UND;

v.createHook = function ({name, init, update, response}) {
name = `use${name.charAt(0).toUpperCase()}${name.slice(1).toLowerCase()}`;
if (!v[name]) {
v[name] = (...args) => {
let currentComponent = v.current.component;

if (v.current.parentVnode.components === UND) {
v.current.parentVnode.components = [];
}

if (v.current.parentVnode.components.indexOf(currentComponent) === -1) {
v.current.parentVnode.components.push(currentComponent);
}

let hook;
let oldComponentNode = v.current.oldParentVnode.components &&
v.current.oldParentVnode.components[v.current.parentVnode.components.length - 1];
let oldMethod = oldComponentNode && ('view' in oldComponentNode.name ? oldComponentNode.name.view : oldComponentNode.name);
let currentMethod = 'view' in currentComponent.name ? currentComponent.name.view : currentComponent.name;

if (currentComponent.hooks === UND) {
currentComponent.hooks = [];
}
let hookIndex = currentComponent.hooks.length;

if (oldMethod === currentMethod && 'hooks' in oldComponentNode && oldComponentNode.hooks[hookIndex] !== UND) {
currentComponent.hooks = oldComponentNode.hooks;
hook = oldComponentNode.hooks[hookIndex];
if (update) {
update(hook, ...args);
}
} else {
hook = init(...args);
currentComponent.hooks.push(hook);
}

if (response) {
return response(hook);
}
};
}
};

// State hook
function createStateHook(value) {
let method = function (value) {
if (value !== UND) {
hook.state = value;
}
return hook.state;
};
method.setState = method.toJSON = method.toString = method.valueOf = method;

let hook = new Proxy(method, {
set(hook, prop, val) {
if (prop === 'state') {
hook.state = val;
return true;
}
},
get(hook, prop) {
if (prop === 'state') {
return typeof hook.state === 'function' ? hook.state() : hook.state;
}

return hook[prop];
}
});

hook(value);
return hook;
}
v.createHook({
name: 'state',
init: (initial) => createStateHook(initial),
response: (hook) => [hook, hook.setState]
});


// Effect hook
function callHook(hook, changes) {
let {prev} = hook;
if (!changes) {
hook.onCleanup = hook.effect();
} else if (changes.length > 0) {
for (let i = 0, l = changes.length; i < l; i++) {
if (changes[i] !== prev[i]) {
hook.prev = changes;
hook.onCleanup = hook.effect();
break;
}
}
}

if (hook.onCleanup) {
v.onCleanup(hook.onCleanup);
}
}
v.createHook({
name: 'effect',
init: (effect, changes) => {
let hook = {effect, prev: changes};
callHook(hook);
return hook;
},
update: (hook, effect, changes) => {
callHook(hook, changes);
}
});

};

export default plugin;
Loading

0 comments on commit d258092

Please sign in to comment.