diff --git a/.gitignore b/.gitignore index 8e9f2d6d..ef7d85ff 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ .DS_Store *.swp node_modules +.vscode diff --git a/lib/App.js b/lib/App.js index 174e2d66..9e82a3cf 100644 --- a/lib/App.js +++ b/lib/App.js @@ -18,6 +18,17 @@ var serializedViews = require('./_views'); module.exports = App; +globalThis.APPS = globalThis.APPS || new Map(); + +App.register = function register(name) { + if (!globalThis.APPS.has(name)) { + globalThis.APPS.set(name, { + attached: false, + initialState: null, + }); + } +} + function App(derby, name, filename, options) { EventEmitter.call(this); this.derby = derby; @@ -33,6 +44,7 @@ function App(derby, name, filename, options) { this.page = null; this._pendingComponentMap = {}; this._init(options); + App.register(name); } function createAppPage(derby) { @@ -58,9 +70,10 @@ App.prototype._init = function() { // Must also wait for content ready so that bundle is fully downloaded. this._contentReady(); }; + App.prototype._finishInit = function() { - var script = this._getScript(); - var data = App._parseInitialData(script.nextSibling.innerHTML); + var script = this._getAppStateScript(); + var data = App._parseInitialData(script.textContent); this.model.createConnection(data); this.emit('model', this.model); util.isProduction = data.nodeEnv === 'production'; @@ -72,7 +85,7 @@ App.prototype._finishInit = function() { this._waitForAttach = false; // Instead of attaching, do a route and render if a link was clicked before // the page finished attaching - if (this._cancelAttach) { + if (this._cancelAttach || this._isAttached()) { this.history.refresh(); return; } @@ -91,6 +104,27 @@ App.prototype._finishInit = function() { } this.emit('load', page); }; + +App.prototype._isAttached = function isInitialized() { + const { attached } = globalThis.APPS.get(this.name); + return attached; +} + +App.prototype._persistInitialState = function persistInitialState(state) { + if (this._isAttached()) { + return; + } + globalThis.APPS.set(this.name, { + attached: true, + initialState: state, + }); +} + +App.prototype._initialState = function initialState() { + const { initialState } = globalThis.APPS.get(this.name); + return initialState; +} + // Modified from: https://github.com/addyosmani/jquery.parts/blob/master/jquery.documentReady.js App.prototype._contentReady = function() { // Is the DOM ready to be used? Set to true once it occurs. @@ -166,8 +200,8 @@ App.prototype._contentReady = function() { } }; -App.prototype._getScript = function() { - return document.querySelector('script[data-derby-app]'); +App.prototype._getAppStateScript = function() { + return document.querySelector('script[data-derby-app-state]'); }; App.prototype.use = util.use; diff --git a/lib/AppForServer.js b/lib/AppForServer.js index abf8dd1a..1abdc8ae 100644 --- a/lib/AppForServer.js +++ b/lib/AppForServer.js @@ -128,9 +128,9 @@ AppForServer.prototype.writeScripts = function(backend, dir, options, cb) { }; AppForServer.prototype._viewsSource = function(options) { - return '/*DERBY_SERIALIZED_VIEWS*/' + - 'module.exports = ' + this.views.serialize(options) + ';' + - '/*DERBY_SERIALIZED_VIEWS_END*/'; + return `/*DERBY_SERIALIZED_VIEWS ${this.name}*/\n` + + 'module.exports = ' + this.views.serialize(options) + ';\n' + + `/*DERBY_SERIALIZED_VIEWS_END ${this.name}*/\n`; }; AppForServer.prototype.serialize = function() { diff --git a/lib/PageForServer.js b/lib/PageForServer.js index bc3b4a7d..20d44b08 100644 --- a/lib/PageForServer.js +++ b/lib/PageForServer.js @@ -31,7 +31,7 @@ PageForServer.prototype.render = function(status, ns) { this.res.write(pageHtml); this.app.emit('htmlDone', this); - this.res.write('