Skip to content

Commit 29ab3fb

Browse files
authored
Merge pull request #136 from nullstack/next
Next
2 parents 49ddf43 + 7458af5 commit 29ab3fb

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

54 files changed

+1229
-252
lines changed

client/instanceProxyHandler.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1-
import { generateObjectProxy } from './objectProxyHandler';
21
import client from './client';
32
import { generateContext } from './context';
3+
import { generateObjectProxy } from './objectProxyHandler';
44

55
const instanceProxyHandler = {
66
get(target, name) {
77
if (name === '_isProxy') return true;
8+
if (target.constructor[name]?.name === '_invoke') return target.constructor[name].bind(target.constructor)
89
if (!target[name]?.name?.startsWith('_') && !name.startsWith('_') && typeof (target[name]) == 'function' && name !== 'constructor') {
910
const { [name]: named } = {
1011
[name]: (args) => {
@@ -21,7 +22,7 @@ const instanceProxyHandler = {
2122
target[name] = generateObjectProxy(name, value);
2223
client.update();
2324
} else {
24-
target[name] = value
25+
target[name] = value;
2526
}
2627
return true;
2728
}

client/invoke.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,9 @@ export default function invoke(name, hash) {
1212
} else {
1313
worker.queues[name] = [...worker.queues[name], params];
1414
}
15-
const finalHash = hash === this.constructor.hash ? hash : `${hash}-${this.constructor.hash}`;
15+
const finalHash = hash === this.hash ? hash : `${hash}-${this.hash}`;
1616
let url = `${worker.api}/${prefix}/${finalHash}/${name}.json`;
1717
let body = JSON.stringify(params || {});
18-
1918
const options = {
2019
headers: worker.headers,
2120
mode: 'cors',

client/rerender.js

Lines changed: 44 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,42 @@
1-
import {isFalse, isText} from '../shared/nodes';
1+
import { isFalse, isText } from '../shared/nodes';
2+
import { anchorableElement } from './anchorableNode';
23
import client from './client';
34
import render from './render';
4-
import {anchorableElement} from './anchorableNode';
55

66
export default function rerender(selector, current, next) {
77

88
current = current === undefined ? client.virtualDom : current;
99
next = next === undefined ? client.nextVirtualDom : next;
1010

11-
if(next.instance) {
11+
if (next.instance) {
1212
next.instance._self.element = selector;
1313
}
1414

15-
if(!client.hydrated && selector) {
16-
for(const element of selector.childNodes) {
17-
if(element.tagName && element.tagName.toLowerCase() == 'textarea' && element.childNodes.length == 0) {
15+
if (!client.hydrated && selector) {
16+
for (const element of selector.childNodes) {
17+
if (element.tagName && element.tagName.toLowerCase() == 'textarea' && element.childNodes.length == 0) {
1818
element.appendChild(document.createTextNode(''));
1919
}
20-
if(element.COMMENT_NODE === 8 && element.textContent === '#') {
20+
if (element.COMMENT_NODE === 8 && element.textContent === '#') {
2121
selector.removeChild(element);
2222
}
2323
}
2424
}
2525

26-
if(isFalse(current) && isFalse(next)) {
26+
if (isFalse(current) && isFalse(next)) {
2727
return;
2828
}
2929

30-
if((isFalse(current) || isFalse(next)) && current != next) {
30+
if ((isFalse(current) || isFalse(next)) && current != next) {
3131
const nextSelector = render(next);
3232
return selector.replaceWith(nextSelector);
3333
}
3434

35-
if(current.type == 'head' && next.type == 'head') {
35+
if (current.type == 'head' && next.type == 'head') {
3636
return;
3737
}
3838

39-
if(current.type == 'head' || next.type == 'head') {
39+
if (current.type == 'head' || next.type == 'head') {
4040
const nextSelector = render(next);
4141
return selector.replaceWith(nextSelector);
4242
}
@@ -47,51 +47,51 @@ export default function rerender(selector, current, next) {
4747
}
4848

4949
if (isText(current) && isText(next)) {
50-
if(current != next) {
50+
if (current != next) {
5151
selector.nodeValue = next;
5252
}
5353
return;
5454
}
5555

5656
if (current.type === next.type) {
5757

58-
const attributeNames = Object.keys({...current.attributes, ...next.attributes});
59-
for(const name of attributeNames) {
60-
if(name === 'html') {
61-
if(next.attributes[name] !== current.attributes[name]) {
58+
const attributeNames = Object.keys({ ...current.attributes, ...next.attributes });
59+
for (const name of attributeNames) {
60+
if (name === 'html') {
61+
if (next.attributes[name] !== current.attributes[name]) {
6262
selector.innerHTML = next.attributes[name];
6363
anchorableElement(selector);
64-
}
65-
} else if(name === 'checked') {
66-
if(next.attributes[name] !== selector.value) {
64+
}
65+
} else if (name === 'checked') {
66+
if (next.attributes[name] !== selector.value) {
6767
selector.checked = next.attributes[name];
6868
}
69-
} else if(name === 'value') {
70-
if(next.attributes[name] !== selector.value) {
69+
} else if (name === 'value') {
70+
if (next.attributes[name] !== selector.value) {
7171
selector.value = next.attributes[name];
7272
}
73-
} else if(name.startsWith('on')) {
73+
} else if (name.startsWith('on')) {
7474
const eventName = name.replace('on', '');
7575
const key = '_event.' + eventName;
7676
selector.removeEventListener(eventName, current[key]);
77-
if(next.attributes[name]) {
77+
if (next.attributes[name]) {
7878
next[key] = (event) => {
79-
if(next.attributes.default !== true) {
79+
if (next.attributes.default !== true) {
8080
event.preventDefault();
8181
}
82-
next.attributes[name]({...next.attributes, event});
82+
next.attributes[name]({ ...next.attributes, event });
8383
};
8484
selector.addEventListener(eventName, next[key]);
8585
}
8686
} else {
87-
const type = typeof(next.attributes[name]);
88-
if(type !== 'object' && type !== 'function') {
89-
if(current.attributes[name] !== undefined && next.attributes[name] === undefined) {
87+
const type = typeof (next.attributes[name]);
88+
if (type !== 'object' && type !== 'function') {
89+
if (current.attributes[name] !== undefined && next.attributes[name] === undefined) {
9090
selector.removeAttribute(name);
91-
} else if(current.attributes[name] !== next.attributes[name]) {
92-
if(name != 'value' && next.attributes[name] === false || next.attributes[name] === null || next.attributes[name] === undefined) {
91+
} else if (current.attributes[name] !== next.attributes[name]) {
92+
if (name != 'value' && next.attributes[name] === false || next.attributes[name] === null || next.attributes[name] === undefined) {
9393
selector.removeAttribute(name);
94-
} else if(name != 'value' && next.attributes[name] === true) {
94+
} else if (name != 'value' && next.attributes[name] === true) {
9595
selector.setAttribute(name, '');
9696
} else {
9797
selector.setAttribute(name, next.attributes[name]);
@@ -101,39 +101,39 @@ export default function rerender(selector, current, next) {
101101
}
102102
}
103103

104-
if(next.attributes.html) return;
104+
if (next.attributes.html) return;
105105

106106
const limit = Math.max(current.children.length, next.children.length);
107-
if(next.children.length > current.children.length) {
108-
for(let i = 0; i < current.children.length; i++) {
107+
if (next.children.length > current.children.length) {
108+
for (let i = 0; i < current.children.length; i++) {
109109
rerender(selector.childNodes[i], current.children[i], next.children[i]);
110110
}
111-
for(let i = current.children.length; i < next.children.length; i++) {
111+
for (let i = current.children.length; i < next.children.length; i++) {
112112
const nextSelector = render(next.children[i]);
113113
selector.appendChild(nextSelector);
114114
}
115-
} else if(current.children.length > next.children.length) {
116-
for(let i = 0; i < next.children.length; i++) {
115+
} else if (current.children.length > next.children.length) {
116+
for (let i = 0; i < next.children.length; i++) {
117117
rerender(selector.childNodes[i], current.children[i], next.children[i]);
118118
}
119-
for(let i = current.children.length - 1; i >= next.children.length; i--) {
120-
selector.removeChild(selector.childNodes[i]);
119+
for (let i = current.children.length - 1; i >= next.children.length; i--) {
120+
selector.removeChild(selector.childNodes[i]);
121121
}
122122
} else {
123-
for(let i = limit - 1; i > -1; i--) {
124-
if(typeof selector.childNodes[i] === 'undefined') {
125-
console.error(`Virtual DOM does not match the DOM. Expected tag ${current.type} but instead found undefined. This error usually happens because of an invalid HTML hierarchy like nested forms or tables without tr.`);
123+
for (let i = limit - 1; i > -1; i--) {
124+
if (typeof selector.childNodes[i] === 'undefined') {
125+
throw new Error(`Virtual DOM does not match the DOM. Expected tag ${current.type} but instead found undefined. This error usually happens because of an invalid HTML hierarchy like nested forms or tables without tr.`);
126126
return;
127127
}
128128
rerender(selector.childNodes[i], current.children[i], next.children[i]);
129129
}
130130
}
131131

132-
if(next.type == 'textarea') {
132+
if (next.type == 'textarea') {
133133
selector.value = next.children.join("");
134134
}
135135

136-
if(next.type == 'select') {
136+
if (next.type == 'select') {
137137
selector.value = next.attributes.value;
138138
}
139139

client/router.js

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,56 @@
1-
import {updateParams} from './params';
2-
import environment from './environment';
31
import extractLocation from '../shared/extractLocation';
4-
import worker from './worker';
5-
import page from './page';
6-
import windowEvent from './windowEvent';
72
import client from './client';
3+
import environment from './environment';
4+
import page from './page';
5+
import { updateParams } from './params';
86
import segments from './segments';
7+
import windowEvent from './windowEvent';
8+
import worker from './worker';
99

1010
let redirectTimer = null;
1111

1212
class Router {
1313

1414
event = 'nullstack.router';
15-
15+
previous = null;
1616
_changed = false;
1717
_segments = segments;
1818

1919
constructor() {
20-
const {hash, url} = extractLocation(window.location.pathname+window.location.search);
20+
const { hash, url } = extractLocation(window.location.pathname + window.location.search);
2121
this._url = url;
2222
this._hash = hash;
2323
}
2424

2525
async _popState() {
26-
const {urlWithHash} = extractLocation(window.location.pathname+window.location.search);
26+
const { urlWithHash } = extractLocation(window.location.pathname + window.location.search);
2727
await this._update(urlWithHash, false);
2828
}
2929

3030
async _update(target, push) {
31-
const {url, path, hash, urlWithHash} = extractLocation(target);
31+
this.previous = this.url;
32+
const { url, path, hash, urlWithHash } = extractLocation(target);
3233
clearTimeout(redirectTimer);
3334
redirectTimer = setTimeout(async () => {
3435
page.status = 200;
35-
if(environment.mode === 'ssg') {
36+
if (environment.mode === 'ssg') {
3637
worker.fetching = true;
3738
const api = '/index.json';
38-
const endpoint = path === '/' ? api : path+api;
39+
const endpoint = path === '/' ? api : path + api;
3940
try {
4041
const response = await fetch(endpoint);
4142
const payload = await response.json(url);
4243
client.memory = payload.instances;
43-
for(const key in payload.page) {
44+
for (const key in payload.page) {
4445
page[key] = payload.page[key];
4546
}
4647
worker.responsive = true;
47-
} catch(error) {
48+
} catch (error) {
4849
worker.responsive = false;
4950
}
5051
worker.fetching = false;
5152
}
52-
if(push) {
53+
if (push) {
5354
history.pushState({}, document.title, urlWithHash);
5455
}
5556
this._url = url;
@@ -65,11 +66,11 @@ class Router {
6566
if (target.startsWith('http')) {
6667
return (window.location.href = target);
6768
}
68-
const {url, hash, urlWithHash} = extractLocation(target);
69-
if(url !== this._url || this._hash !== hash) {
69+
const { url, hash, urlWithHash } = extractLocation(target);
70+
if (url !== this._url || this._hash !== hash) {
7071
await this._update(urlWithHash, true);
7172
}
72-
if(!hash) {
73+
if (!hash) {
7374
window.scroll(0, 0);
7475
}
7576
}
@@ -87,7 +88,7 @@ class Router {
8788
}
8889

8990
set path(target) {
90-
this._redirect(target+window.location.search);
91+
this._redirect(target + window.location.search);
9192
}
9293

9394
}

loaders/register-inner-components.js

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,28 @@
11
const parse = require('@babel/parser').parse;
22
const traverse = require("@babel/traverse").default;
33

4-
module.exports = function(source) {
4+
module.exports = function (source) {
55
const injections = {};
66
const positions = [];
77
const ast = parse(source, {
88
sourceType: 'module',
9-
plugins: ['classProperties', 'jsx']
9+
plugins: ['classProperties', 'jsx', 'typescript']
1010
});
1111
traverse(ast, {
1212
ClassMethod(path) {
13-
if(path.node.key.name.startsWith('render')) {
13+
if (path.node.key.name.startsWith('render')) {
1414
traverse(path.node, {
1515
JSXIdentifier(subpath) {
16-
if(/^[A-Z]/.test(subpath.node.name)) {
17-
if(!path.scope.hasBinding(subpath.node.name)) {
16+
if (/^[A-Z]/.test(subpath.node.name)) {
17+
if (!path.scope.hasBinding(subpath.node.name)) {
1818
const start = path.node.body.body[0].start;
19-
if(!positions.includes(start)) {
19+
if (!positions.includes(start)) {
2020
positions.push(start);
2121
}
22-
if(!injections[start]) {
22+
if (!injections[start]) {
2323
injections[start] = [];
2424
}
25-
if(!injections[start].includes(subpath.node.name)) {
25+
if (!injections[start].includes(subpath.node.name)) {
2626
injections[start].push(subpath.node.name);
2727
}
2828
}
@@ -36,13 +36,13 @@ module.exports = function(source) {
3636
positions.push(0);
3737
let outputs = [];
3838
let last;
39-
for(const position of positions) {
39+
for (const position of positions) {
4040
let code = source.slice(position, last);
4141
last = position;
4242
outputs.push(code);
43-
if(position) {
44-
for(const injection of injections[position]) {
45-
if(injection) {
43+
if (position) {
44+
for (const injection of injections[position]) {
45+
if (injection) {
4646
outputs.push(`const ${injection} = this.render${injection};\n `)
4747
}
4848
}

loaders/register-static-from-server.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ const crypto = require('crypto');
22
const parse = require('@babel/parser').parse;
33
const traverse = require("@babel/traverse").default;
44

5-
module.exports = function(source) {
5+
module.exports = function (source) {
66
let hasClass = false;
77
const hash = crypto.createHash('md5').update(source).digest("hex");
88
let klassName;
@@ -19,18 +19,18 @@ module.exports = function(source) {
1919
klassName = path.node.id.name;
2020
},
2121
ClassMethod(path) {
22-
if(path.node.static && path.node.async && path.node.key.name.split(/[A-Z]/)[0] !== 'start') {
22+
if (path.node.static && path.node.async && path.node.key.name.split(/[A-Z]/)[0] !== 'start') {
2323
methodNames.push(path.node.key.name);
2424
}
2525
}
2626
});
27-
if(!hasClass) return source;
27+
if (!hasClass) return source;
2828
let output = source.substring(0, klassEnd);
29-
for(const methodName of methodNames) {
29+
for (const methodName of methodNames) {
3030
output += `${methodName} = Nullstack.invoke('${methodName}');\n`
3131
}
3232
output += source.substring(klassEnd);
33-
for(const methodName of methodNames) {
33+
for (const methodName of methodNames) {
3434
output += `\nNullstack.registry["${hash}.${methodName}"] = ${klassName}.${methodName};`
3535
}
3636
output += `\nNullstack.registry["${hash}"] = ${klassName};`

0 commit comments

Comments
 (0)