Skip to content
Merged

Next #226

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
119 changes: 61 additions & 58 deletions client/client.js
Original file line number Diff line number Diff line change
@@ -1,82 +1,85 @@
import generateTree from '../shared/generateTree';
import { loadPlugins } from '../shared/plugins';
import context, { generateContext } from './context';
import rerender from './rerender';
import router from './router';
import generateTree from '../shared/generateTree'
import { loadPlugins } from '../shared/plugins'
import context, { generateContext } from './context'
import rerender from './rerender'
import router from './router'

const client = {}

const client = {};
client.initialized = false
client.hydrated = false
client.initializer = null
client.instances = {}
context.instances = client.instances
client.initiationQueue = []
client.renewalQueue = []
client.hydrationQueue = []
client.realHydrationQueue = []
client.virtualDom = {}
client.selector = null
client.events = {}
client.generateContext = generateContext
client.renderQueue = null

client.initialized = false;
client.hydrated = false;
client.initializer = null;

client.instances = {};
context.instances = client.instances;
client.initiationQueue = [];
client.renewalQueue = [];
client.hydrationQueue = [];
client.virtualDom = {};
client.selector = null;
client.events = {};
client.generateContext = generateContext;

client.renderQueue = null;

client.update = async function () {
client.update = async function update() {
if (client.initialized) {
clearInterval(client.renderQueue);
clearInterval(client.renderQueue)
client.renderQueue = setTimeout(async () => {
const scope = client;
scope.context = context;
scope.plugins = loadPlugins(scope);
client.initialized = false;
client.initiationQueue = [];
client.renewalQueue = [];
client.hydrationQueue = [];
client.nextVirtualDom = await generateTree(client.initializer(), scope);
rerender(client.selector);
client.virtualDom = client.nextVirtualDom;
client.nextVirtualDom = null;
client.processLifecycleQueues();
}, 16);
const scope = client
scope.context = context
scope.plugins = loadPlugins(scope)
client.initialized = false
client.renewalQueue = []
client.nextVirtualDom = await generateTree(client.initializer(), scope)
rerender(client.selector)
client.virtualDom = client.nextVirtualDom
client.nextVirtualDom = null
client.processLifecycleQueues()
}, 16)
}
}

client.processLifecycleQueues = async function () {
client.processLifecycleQueues = async function processLifecycleQueues() {
if (!client.initialized) {
client.initialized = true;
client.hydrated = true;
client.initialized = true
client.hydrated = true
}
const initiationQueue = client.initiationQueue;
const hydrationQueue = client.hydrationQueue;
for (const instance of initiationQueue) {
instance.initiate && await instance.initiate();
instance._self.initiated = true;
let shouldUpdate = false
while (client.initiationQueue.length) {
const instance = client.initiationQueue.shift()
instance.initiate && await instance.initiate()
instance._self.initiated = true
instance.launch && instance.launch()
shouldUpdate = true
}
if (initiationQueue.length) {
client.update();
}
for (const instance of hydrationQueue) {
instance.hydrate && await instance.hydrate();
instance._self.hydrated = true;
shouldUpdate && client.update()
shouldUpdate = false
while (client.realHydrationQueue.length) {
shouldUpdate = true
const instance = client.realHydrationQueue.shift()
instance.hydrate && await instance.hydrate()
instance._self.hydrated = true
}
if (hydrationQueue.length) {
client.update();
shouldUpdate && client.update()
shouldUpdate = false
while (client.hydrationQueue.length) {
shouldUpdate = true
const instance = client.hydrationQueue.shift()
client.realHydrationQueue.push(instance)
}
shouldUpdate && client.update()
for (const key in client.instances) {
const instance = client.instances[key];
const instance = client.instances[key]
if (!client.renewalQueue.includes(instance) && !instance._self.terminated) {
instance.terminate && await instance.terminate();
instance.terminate && await instance.terminate()
if (instance._self.persistent) {
instance._self.terminated = true
} else {
delete client.instances[key];
delete client.instances[key]
}
}
}
router._changed = false;
router._changed = false
}

export default client;
export default client
2 changes: 1 addition & 1 deletion client/invoke.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export default function invoke(name, hash) {
}
if (/get[A-Z]([*]*)/.test(name)) {
options.method = 'GET';
url += `?payload=${body}`;
url += `?payload=${encodeURIComponent(body)}`;
} else {
options.body = body;
if (/patch[A-Z]([*]*)/.test(name)) {
Expand Down
2 changes: 1 addition & 1 deletion loaders/register-static-from-server.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ module.exports = function (source) {
klassName = path.node.id.name;
},
ClassMethod(path) {
if (path.node.static && path.node.async && path.node.key.name.split(/[A-Z]/)[0] !== 'start') {
if (path.node.static && path.node.async && !path.node.key.name.startsWith('_')) {
methodNames.push(path.node.key.name);
}
}
Expand Down
2 changes: 1 addition & 1 deletion loaders/remove-static-from-client.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ module.exports = function removeStaticFromClient(source) {
const injection = injections[position];
if (position && injection) {
const location = injection.end - position;
if (injection.name.split(/[A-Z]/)[0] === 'start') {
if (injection.name.startsWith('_')) {
code = code.substring(location).trimStart();
} else {
code = `static ${injection.name} = Nullstack.invoke('${injection.name}', '${hash}');` + code.substring(location);
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "nullstack",
"version": "0.15.5",
"version": "0.15.6",
"description": "Full-stack Javascript Components for one-dev armies",
"main": "nullstack.js",
"author": "Mortaro",
Expand Down
4 changes: 3 additions & 1 deletion tests/src/Application.njs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ import PluginAttributes from './PluginAttributes';
import PublicServerFunctions from './PublicServerFunctions.njs';
import PureComponents from './PureComponents';
import WebpackCustomPlugin from './WebpackCustomPlugin';
import RemoveStart from './RemoveStart';
import RenderableComponent from './RenderableComponent';
import RoutesAndParams from './RoutesAndParams';
import ServerFunctions from './ServerFunctions';
Expand All @@ -47,6 +46,7 @@ import WorkerVerbs from './WorkerVerbs';
import MetatagState from './MetatagState';
import TypeScriptExtension from './TypeScriptExtension';
import JavaScriptExtension from './JavaScriptExtension';
import HydrateElement from './HydrateElement';

class Application extends Nullstack {

Expand All @@ -69,6 +69,7 @@ class Application extends Nullstack {
<a href="/routes-and-params/a"> router with params </a>
<a href="/undefined-nodes"> undefined nodes </a>
<a href="/full-stack-lifecycle"> lifecycle </a>
<a href="/hydrate-element"> hydrate element </a>
</div>
<RenderableComponent route="/renderable-component" />
<StatefulComponent route="/stateful-component" />
Expand Down Expand Up @@ -115,6 +116,7 @@ class Application extends Nullstack {
<MetatagState route="/metatag-state" />
<JavaScriptExtension route="/javascript-extension" />
<TypeScriptExtension route="/typescript-extension" generic />
<HydrateElement route="/hydrate-element" />
<ErrorPage route="*" />
</main>
)
Expand Down
17 changes: 17 additions & 0 deletions tests/src/HydrateElement.njs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import Nullstack from 'nullstack';

class HydrateElement extends Nullstack {

hydrate({ self }) {
this.id = self.element.id
}

render() {
return (
<div id="hydrate-element" data-id={this.id}> HydrateElement </div>
)
}

}

export default HydrateElement;
15 changes: 15 additions & 0 deletions tests/src/HydrateElement.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
describe('FullStackLifecycle ssr', () => {

beforeAll(async () => {
await page.goto('http://localhost:6969/');
await page.click('[href="/hydrate-element"]')
});

test('prepare should run', async () => {
await page.waitForSelector('[data-id="hydrate-element"]');
const element = await page.$('[data-id="hydrate-element"]');
expect(element).toBeTruthy();
});


});
32 changes: 0 additions & 32 deletions tests/src/RemoveStart.njs

This file was deleted.

19 changes: 0 additions & 19 deletions tests/src/RemoveStart.test.js

This file was deleted.

5 changes: 2 additions & 3 deletions tests/src/RoutesAndParams.njs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import Nullstack from 'nullstack';
class RoutesAndParams extends Nullstack {

eventTriggered = false;
paramHydrated = false;

hydrate(context) {
Expand Down Expand Up @@ -49,13 +48,13 @@ class RoutesAndParams extends Nullstack {
router.url = 'https://nullstack.app';
}

render({ router, params, eventTriggered }) {
render({ router, params, eventTriggered, self }) {
return (
<div>
<a href="https://nullstack.app"> Nullstack</a>
<button data-absolute onclick={this.goToDocs}> Nullstack </button>
<a href="/routes-and-params/no-hash#hash">hash</a>
<div data-event-triggered={eventTriggered} />
<div data-hydrated={self.hydrated} data-event-triggered={eventTriggered} />
<div data-router={!!router} />
<div route="/routes-and-params" data-route="/routes-and-params" />
<InnerHTML route="/routes-and-params/inner-html" />
Expand Down
22 changes: 17 additions & 5 deletions tests/src/RoutesAndParams.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,6 @@ describe('RoutesAndParams /routes-and-params/a', () => {
expect(element).toBeTruthy();
});

test('a custom event is triggered when the url changes', async () => {
const element = await page.$('[data-event-triggered]');
expect(element).toBeTruthy();
});

test('params are available when coming from external', async () => {
await page.goto('http://localhost:6969/');
await page.click('[href="/routes-and-params/a"]');
Expand All @@ -71,6 +66,23 @@ describe('RoutesAndParams /routes-and-params/a', () => {

});

describe('RoutesAndParams event', () => {

beforeAll(async () => {
await page.goto('http://localhost:6969/');
await page.click('[href="/routes-and-params/a"]');
await page.waitForSelector('[data-hydrated]');
await page.click('[href="/routes-and-params/a?framework=nullstack"]');
});

test('a custom event is triggered when the url changes', async () => {
await page.waitForSelector('[data-event-triggered]');
const element = await page.$('[data-event-triggered]');
expect(element).toBeTruthy();
});

});

describe('RoutesAndParams /routes-and-params/?boolean=true', () => {

beforeAll(async () => {
Expand Down
Loading