@webqit/webflo 0.10.2 → 0.10.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/README.md +750 -3
  2. package/package.json +3 -3
  3. package/src/Context.js +8 -4
  4. package/src/config-pi/deployment/Env.js +2 -2
  5. package/src/config-pi/deployment/Layout.js +2 -2
  6. package/src/config-pi/deployment/Origins.js +2 -2
  7. package/src/config-pi/deployment/Virtualization.js +2 -2
  8. package/src/config-pi/runtime/Client.js +45 -14
  9. package/src/config-pi/runtime/Server.js +26 -10
  10. package/src/config-pi/runtime/client/Worker.js +2 -2
  11. package/src/config-pi/runtime/server/Headers.js +2 -2
  12. package/src/config-pi/runtime/server/Redirects.js +2 -2
  13. package/src/config-pi/static/Manifest.js +2 -2
  14. package/src/config-pi/static/Ssg.js +2 -2
  15. package/src/runtime-pi/client/Runtime.js +28 -23
  16. package/src/runtime-pi/client/RuntimeClient.js +28 -15
  17. package/src/runtime-pi/client/generate.js +251 -77
  18. package/src/runtime-pi/client/{generate.oohtml.js → oohtml/full.js} +0 -0
  19. package/src/runtime-pi/client/oohtml/namespacing.js +7 -0
  20. package/src/runtime-pi/client/oohtml/scripting.js +8 -0
  21. package/src/runtime-pi/client/oohtml/templating.js +8 -0
  22. package/src/runtime-pi/client/worker/Worker.js +21 -23
  23. package/src/runtime-pi/server/Router.js +2 -2
  24. package/src/runtime-pi/server/Runtime.js +8 -3
  25. package/src/runtime-pi/server/RuntimeClient.js +21 -11
  26. package/src/webflo.js +7 -9
  27. package/test/site/public/bundle.html +3 -0
  28. package/test/site/public/bundle.html.json +3 -0
  29. package/test/site/public/bundle.js +2 -0
  30. package/test/site/public/bundle.js.gz +0 -0
  31. package/test/site/public/bundle.webflo.js +15 -0
  32. package/test/site/public/bundle.webflo.js.gz +0 -0
  33. package/test/site/public/index.html +30 -0
  34. package/test/site/public/page-2/bundle.html +5 -0
  35. package/test/site/public/page-2/bundle.html.json +1 -0
  36. package/test/site/public/page-2/bundle.js +2 -0
  37. package/test/site/public/page-2/bundle.js.gz +0 -0
  38. package/test/site/public/page-2/index.html +47 -0
  39. package/test/site/public/page-2/logo-130x130.png +0 -0
  40. package/test/site/public/page-2/main.html +3 -0
  41. package/test/site/public/page-4/subpage/bundle.html +0 -0
  42. package/test/site/public/page-4/subpage/bundle.html.json +1 -0
  43. package/test/site/public/page-4/subpage/bundle.js +2 -0
  44. package/test/site/public/page-4/subpage/bundle.js.gz +0 -0
  45. package/test/site/public/page-4/subpage/index.html +31 -0
  46. package/test/site/public/worker.js +3 -0
  47. package/test/site/public/worker.js.gz +0 -0
  48. package/test/site/server/index.js +8 -0
  49. package/src/Cli.js +0 -131
  50. package/src/Configurator.js +0 -97
package/package.json CHANGED
@@ -12,7 +12,7 @@
12
12
  "vanila-javascript"
13
13
  ],
14
14
  "homepage": "https://webqit.io/tooling/webflo",
15
- "version": "0.10.2",
15
+ "version": "0.10.5",
16
16
  "license": "MIT",
17
17
  "repository": {
18
18
  "type": "git",
@@ -36,8 +36,8 @@
36
36
  },
37
37
  "dependencies": {
38
38
  "@octokit/webhooks": "^7.15.1",
39
- "@webqit/backpack": "^0.0.37",
40
- "@webqit/oohtml-ssr": "^1.0.3",
39
+ "@webqit/backpack": "^0.1.0",
40
+ "@webqit/oohtml-ssr": "^1.0.6",
41
41
  "@webqit/util": "^0.8.9",
42
42
  "client-sessions": "^0.8.0",
43
43
  "esbuild": "^0.14.38",
package/src/Context.js CHANGED
@@ -17,10 +17,14 @@ export default class Context {
17
17
  this[prop] = this.dict[prop];
18
18
  }
19
19
  if (arguments.length > 1) {
20
- this.dict.CWD = CD;
20
+ Object.defineProperty(this.dict, 'CWD', { get: () => CD });
21
21
  }
22
22
  }
23
23
 
24
+ get name() {
25
+ return 'webflo';
26
+ }
27
+
24
28
  // create
25
29
  static create(...args) {
26
30
  return new this(...args);
@@ -31,9 +35,9 @@ export default class Context {
31
35
  return this.dict.CWD || '';
32
36
  }
33
37
 
34
- // webflo
35
- get webflo() {
36
- return this.dict.webflo || {};
38
+ // meta
39
+ get meta() {
40
+ return this.dict.meta || {};
37
41
  }
38
42
 
39
43
  // app
@@ -3,9 +3,9 @@
3
3
  * imports
4
4
  */
5
5
  import { _merge } from '@webqit/util/obj/index.js';
6
- import Configurator from '../../Configurator.js';
6
+ import { Dotfile } from '@webqit/backpack';
7
7
 
8
- export default class Env extends Configurator {
8
+ export default class Env extends Dotfile {
9
9
 
10
10
  // Base name
11
11
  get name() {
@@ -3,9 +3,9 @@
3
3
  * imports
4
4
  */
5
5
  import { _merge } from '@webqit/util/obj/index.js';
6
- import Configurator from '../../Configurator.js';
6
+ import { Dotfile } from '@webqit/backpack';
7
7
 
8
- export default class Layout extends Configurator {
8
+ export default class Layout extends Dotfile {
9
9
 
10
10
  // Base name
11
11
  get name() {
@@ -6,9 +6,9 @@ import Url from 'url';
6
6
  import { _merge } from '@webqit/util/obj/index.js';
7
7
  import { _before } from '@webqit/util/str/index.js';
8
8
  import { _isObject, _isTypeObject } from '@webqit/util/js/index.js';
9
- import Configurator from '../../Configurator.js';
9
+ import { Dotfile } from '@webqit/backpack';
10
10
 
11
- export default class Origins extends Configurator {
11
+ export default class Origins extends Dotfile {
12
12
 
13
13
  // Base name
14
14
  get name() {
@@ -4,9 +4,9 @@
4
4
  */
5
5
  import { _merge } from '@webqit/util/obj/index.js';
6
6
  import { _isObject } from '@webqit/util/js/index.js';
7
- import Configurator from '../../Configurator.js';
7
+ import { Dotfile } from '@webqit/backpack';
8
8
 
9
- export default class Virtualization extends Configurator {
9
+ export default class Virtualization extends Dotfile {
10
10
 
11
11
  // Base name
12
12
  get name() {
@@ -5,9 +5,9 @@
5
5
  import { _merge } from '@webqit/util/obj/index.js';
6
6
  import { _isNumeric } from '@webqit/util/js/index.js';
7
7
  import { _before, _after } from '@webqit/util/str/index.js';
8
- import Configurator from '../../Configurator.js';
8
+ import { Dotfile } from '@webqit/backpack';
9
9
 
10
- export default class Client extends Configurator {
10
+ export default class Client extends Dotfile {
11
11
 
12
12
  // Base name
13
13
  get name() {
@@ -23,8 +23,10 @@ export default class Client extends Configurator {
23
23
  withDefaults(config) {
24
24
  return _merge(true, {
25
25
  bundle_filename: 'bundle.js',
26
- support_oohtml: true,
27
- support_service_worker: true,
26
+ public_base_url: '/',
27
+ address_bar_synchrony: 'standard',
28
+ oohtml_support: 'full',
29
+ service_worker_support: true,
28
30
  worker_scope: '/',
29
31
  worker_filename: 'worker.js',
30
32
  }, config);
@@ -32,6 +34,20 @@ export default class Client extends Configurator {
32
34
 
33
35
  // Questions generator
34
36
  questions(config, choices = {}) {
37
+ // Choices
38
+ const CHOICES = _merge({
39
+ address_bar_synchrony: [
40
+ {value: 'standard', title: 'standard - on response'},
41
+ {value: 'instant', title: 'instant - on request'},
42
+ ],
43
+ oohtml_support: [
44
+ {value: 'full', title: 'Full'},
45
+ {value: 'namespacing', title: 'namespacing'},
46
+ {value: 'scripting', title: 'scripting'},
47
+ {value: 'templating', title: 'templating'},
48
+ {value: 'none', title: 'none'},
49
+ ],
50
+ }, choices);
35
51
  // Questions
36
52
  return [
37
53
  {
@@ -41,30 +57,45 @@ export default class Client extends Configurator {
41
57
  initial: config.bundle_filename,
42
58
  },
43
59
  {
44
- name: 'support_oohtml',
45
- type: 'toggle',
46
- message: 'Support rendering with OOHTML? (Adds OOHTML to your app\'s bundle.)',
47
- active: 'YES',
48
- inactive: 'NO',
49
- initial: config.support_oohtml,
60
+ name: 'public_base_url',
61
+ type: 'text',
62
+ message: '[public_base_url]: Enter the base-URL for public resource URLs',
63
+ initial: DATA.public_base_url,
64
+ validation: ['important'],
65
+ },
66
+ {
67
+ name: 'address_bar_synchrony',
68
+ type: 'select',
69
+ message: '[address_bar_synchrony]: Specify how the address bar synchronizes with navigation',
70
+ choices: CHOICES.address_bar_synchrony,
71
+ initial: this.indexOfInitial(CHOICES.address_bar_synchrony, config.address_bar_synchrony),
72
+ validation: ['important'],
73
+ },
74
+ {
75
+ name: 'oohtml_support',
76
+ type: 'select',
77
+ message: '[oohtml_support]: (Adds OOHTML to your app\'s bundle.) Specify OOHTML support level',
78
+ choices: CHOICES.oohtml_support,
79
+ initial: this.indexOfInitial(CHOICES.oohtml_support, config.oohtml_support),
80
+ validation: ['important'],
50
81
  },
51
82
  {
52
- name: 'support_service_worker',
83
+ name: 'service_worker_support',
53
84
  type: 'toggle',
54
85
  message: 'Support Service Worker?',
55
86
  active: 'YES',
56
87
  inactive: 'NO',
57
- initial: config.support_service_worker,
88
+ initial: config.service_worker_support,
58
89
  },
59
90
  {
60
91
  name: 'worker_scope',
61
- type: (prev, answers) => answers.support_service_worker ? 'text' : null,
92
+ type: (prev, answers) => answers.service_worker_support ? 'text' : null,
62
93
  message: 'Specify the Service Worker scope',
63
94
  initial: config.worker_scope,
64
95
  },
65
96
  {
66
97
  name: 'worker_filename',
67
- type: (prev, answers) => answers.support_service_worker ? 'text' : null,
98
+ type: (prev, answers) => answers.service_worker_support ? 'text' : null,
68
99
  message: 'Specify the Service Worker filename',
69
100
  initial: config.worker_filename,
70
101
  },
@@ -3,9 +3,9 @@
3
3
  * imports
4
4
  */
5
5
  import { _merge } from '@webqit/util/obj/index.js';
6
- import Configurator from '../../Configurator.js';
6
+ import { Dotfile } from '@webqit/backpack';
7
7
 
8
- export default class Server extends Configurator {
8
+ export default class Server extends Dotfile {
9
9
 
10
10
  // Base name
11
11
  get name() {
@@ -29,6 +29,7 @@ export default class Server extends Configurator {
29
29
  force: false,
30
30
  },
31
31
  force_www: '',
32
+ oohtml_support: 'full',
32
33
  shared: false,
33
34
  }, config);
34
35
  }
@@ -42,13 +43,20 @@ export default class Server extends Configurator {
42
43
  {value: 'add',},
43
44
  {value: 'remove',},
44
45
  ],
46
+ oohtml_support: [
47
+ {value: 'full', title: 'full'},
48
+ {value: 'namespacing', title: 'namespacing'},
49
+ {value: 'scripting', title: 'scripting'},
50
+ {value: 'templating', title: 'templating'},
51
+ {value: 'none', title: 'none'},
52
+ ],
45
53
  }, choices);
46
54
  // Questions
47
55
  return [
48
56
  {
49
57
  name: 'port',
50
58
  type: 'number',
51
- message: 'Enter port number',
59
+ message: '[port]: Enter port number',
52
60
  initial: config.port,
53
61
  validation: ['important'],
54
62
  },
@@ -62,31 +70,31 @@ export default class Server extends Configurator {
62
70
  {
63
71
  name: 'port',
64
72
  type: 'number',
65
- message: 'Enter HTTPS port number',
73
+ message: '[port]: Enter HTTPS port number',
66
74
  validation: ['important'],
67
75
  },
68
76
  {
69
77
  name: 'keyfile',
70
78
  type: 'text',
71
- message: 'Enter SSL KEY file',
79
+ message: '[keyfile]: Enter SSL KEY file',
72
80
  validation: ['important'],
73
81
  },
74
82
  {
75
83
  name: 'certfile',
76
84
  type: 'text',
77
- message: 'Enter SSL CERT file',
85
+ message: '[certfile]: Enter SSL CERT file',
78
86
  validation: ['important'],
79
87
  },
80
88
  {
81
89
  name: 'certdoms',
82
90
  type: 'list',
83
- message: 'Enter the CERT domains (comma-separated)',
91
+ message: '[certdoms]: Enter the CERT domains (comma-separated)',
84
92
  validation: ['important'],
85
93
  },
86
94
  {
87
95
  name: 'force',
88
96
  type: 'toggle',
89
- message: 'Force HTTPS?',
97
+ message: '[force]: Force HTTPS?',
90
98
  active: 'YES',
91
99
  inactive: 'NO',
92
100
  },
@@ -95,14 +103,22 @@ export default class Server extends Configurator {
95
103
  {
96
104
  name: 'force_www',
97
105
  type: 'select',
98
- message: 'Force add/remove "www" on hostname?',
106
+ message: '[force_www]: Force add/remove "www" on hostname?',
99
107
  choices: CHOICES.force_www,
100
108
  initial: this.indexOfInitial(CHOICES.force_www, config.force_www),
101
109
  },
110
+ {
111
+ name: 'oohtml_support',
112
+ type: 'select',
113
+ message: '[oohtml_support]: Specify OOHTML support level',
114
+ choices: CHOICES.oohtml_support,
115
+ initial: this.indexOfInitial(CHOICES.oohtml_support, config.oohtml_support),
116
+ validation: ['important'],
117
+ },
102
118
  {
103
119
  name: 'shared',
104
120
  type: 'toggle',
105
- message: 'Shared server?',
121
+ message: '[shared]: Shared server?',
106
122
  active: 'YES',
107
123
  inactive: 'NO',
108
124
  initial: config.shared,
@@ -5,9 +5,9 @@
5
5
  import { _merge } from '@webqit/util/obj/index.js';
6
6
  import { _isNumeric } from '@webqit/util/js/index.js';
7
7
  import { _before, _after } from '@webqit/util/str/index.js';
8
- import Configurator from '../../../Configurator.js';
8
+ import { Dotfile } from '@webqit/backpack';
9
9
 
10
- export default class Worker extends Configurator {
10
+ export default class Worker extends Dotfile {
11
11
 
12
12
  // Base name
13
13
  get name() {
@@ -5,9 +5,9 @@
5
5
  import Url from 'url';
6
6
  import { _merge } from '@webqit/util/obj/index.js';
7
7
  import { _isObject } from '@webqit/util/js/index.js';
8
- import Configurator from '../../../Configurator.js';
8
+ import { Dotfile } from '@webqit/backpack';
9
9
 
10
- export default class Headers extends Configurator {
10
+ export default class Headers extends Dotfile {
11
11
 
12
12
  // Base name
13
13
  get name() {
@@ -6,9 +6,9 @@ import Url from 'url';
6
6
  import { _merge } from '@webqit/util/obj/index.js';
7
7
  import { _after } from '@webqit/util/str/index.js';
8
8
  import { _isObject, _isNumeric } from '@webqit/util/js/index.js';
9
- import Configurator from '../../../Configurator.js';
9
+ import { Dotfile } from '@webqit/backpack';
10
10
 
11
- export default class Redirects extends Configurator {
11
+ export default class Redirects extends Dotfile {
12
12
 
13
13
  // Base name
14
14
  get name() {
@@ -8,9 +8,9 @@ import { _merge } from '@webqit/util/obj/index.js';
8
8
  import { _isNumeric } from '@webqit/util/js/index.js';
9
9
  import { _before, _after } from '@webqit/util/str/index.js';
10
10
  import { initialGetIndex } from '@webqit/backpack/src/cli/Promptx.js';
11
- import Configurator from '../../Configurator.js';
11
+ import { Dotfile } from '@webqit/backpack';
12
12
 
13
- export default class Manifest extends Configurator {
13
+ export default class Manifest extends Dotfile {
14
14
 
15
15
  // Base name
16
16
  get name() {
@@ -4,9 +4,9 @@
4
4
  */
5
5
  import { _merge } from '@webqit/util/obj/index.js';
6
6
  import { _isObject } from '@webqit/util/js/index.js';
7
- import Configurator from '../../Configurator.js';
7
+ import { Dotfile } from '@webqit/backpack';
8
8
 
9
- export default class Ssg extends Configurator {
9
+ export default class Ssg extends Dotfile {
10
10
 
11
11
  // Base name
12
12
  get name() {
@@ -88,7 +88,7 @@ export default class Runtime {
88
88
  // Needed to allow window.document.location
89
89
  // to update to window.location
90
90
  window.setTimeout(() => {
91
- this.go(Url.copy(window.document.location), { src: window.document.location, srcType: 'history', });
91
+ this.go(Url.copy(window.document.location), {}, { src: window.document.location, srcType: 'history', });
92
92
  }, 0);
93
93
  });
94
94
 
@@ -98,8 +98,7 @@ export default class Runtime {
98
98
  window.addEventListener('click', e => {
99
99
  var anchor = e.target.closest('a');
100
100
  if (!anchor || !anchor.href) return;
101
- if (!anchor.target && !anchor.download && (!anchor.origin || anchor.origin === this.location.origin)) {
102
- if (e.metaKey || e.altKey || e.ctrlKey || e.shiftKey) return;
101
+ if (!anchor.target && !anchor.download && this.isSpaRoute(anchor, e)) {
103
102
  // Publish everything, including hash
104
103
  this.go(Url.copy(anchor), {}, { src: anchor, srcType: 'link', });
105
104
  // URLs with # will cause a natural navigation
@@ -129,8 +128,7 @@ export default class Runtime {
129
128
  actionEl.href = submitParams.action;
130
129
  // ---------------
131
130
  // If not targeted and same origin...
132
- if (!submitParams.target && (!actionEl.origin || actionEl.origin === this.location.origin)) {
133
- if (e.metaKey || e.altKey || e.ctrlKey || e.shiftKey) return;
131
+ if (!submitParams.target && this.isSpaRoute(actionEl, e)) {
134
132
  // Build data
135
133
  var formData = new FormData(form);
136
134
  if ((submitter || {}).name) {
@@ -176,6 +174,22 @@ export default class Runtime {
176
174
  return window.history;
177
175
  }
178
176
 
177
+ // Check is-route
178
+ isSpaRoute(url, e) {
179
+ url = typeof url === 'string' ? new whatwag.URL(url) : url;
180
+ if (url.origin && url.origin !== this.location.origin) return false;
181
+ if (e && (e.metaKey || e.altKey || e.ctrlKey || e.shiftKey)) return false;
182
+ if (!this.cx.params.routing) return true;
183
+ let b = url.pathname.split('/').filter(s => s);
184
+ const match = a => {
185
+ a = a.split('/').filter(s => s);
186
+ return a.reduce((prev, s, i) => prev && (s === b[i] || [s, b[i]].includes('-')), true);
187
+ };
188
+ return match(this.cx.params.routing.scope) && this.cx.params.routing.subscopes.reduce((prev, subscope) => {
189
+ return prev && !match(subscope);
190
+ }, true);
191
+ }
192
+
179
193
  /**
180
194
  * Performs a request.
181
195
  *
@@ -206,8 +220,10 @@ export default class Runtime {
206
220
  Observer.set(detail.submitter || {}, 'active', true);
207
221
  }
208
222
  // ------------
209
- Observer.set(this.location, url, { detail: { ...init, ...detail }, });
210
223
  Observer.set(this.network, 'redirecting', null);
224
+ if (this.cx.params.address_bar_synchrony === 'instant') {
225
+ Observer.set(this.location, url, { detail: { ...init, ...detail }, });
226
+ }
211
227
  // ------------
212
228
  // The request object
213
229
  let request = this.generateRequest(url.href, init);
@@ -216,6 +232,11 @@ export default class Runtime {
216
232
  // Response
217
233
  let response = await this.clients.get('*').handle(httpEvent, ( ...args ) => this.remoteFetch( ...args ));
218
234
  let finalResponse = this.handleResponse(httpEvent, response);
235
+ // ------------
236
+ if (this.cx.params.address_bar_synchrony !== 'instant') {
237
+ Observer.set(this.location, url, { detail: { ...init, ...detail }, });
238
+ }
239
+ // ------------
219
240
  // Return value
220
241
  return finalResponse;
221
242
  }
@@ -263,7 +284,7 @@ export default class Runtime {
263
284
  Observer.set(e.detail.src, 'active', false);
264
285
  Observer.set(e.detail.submitter || {}, 'active', false);
265
286
  }
266
- if (response.redirected && this.isSameOrigin(response.url)) {
287
+ if (response.redirected && (new whatwag.URL(response.url)).origin === this.location.origin) {
267
288
  Observer.set(this.location, { href: response.url }, {
268
289
  detail: { isRedirect: true },
269
290
  });
@@ -277,20 +298,4 @@ export default class Runtime {
277
298
  return response;
278
299
  }
279
300
 
280
- /**
281
- * Checks if an URL is same origin.
282
- *
283
- * @param object|string url
284
- *
285
- * @return Bool
286
- */
287
- isSameOrigin(url) {
288
- if (typeof url === 'string') {
289
- let href = url;
290
- url = window.document.createElement('a');
291
- url.href = href
292
- }
293
- return !url.origin || url.origin === this.location.origin;
294
- }
295
-
296
301
  }
@@ -15,7 +15,7 @@ export default class RuntimeClient {
15
15
  */
16
16
  constructor(cx) {
17
17
  this.cx = cx;
18
- if (this.cx.support_service_worker) {
18
+ if (this.cx.service_worker_support) {
19
19
  const workerComm = new WorkerComm(this.cx.worker_filename, { scope: this.cx.worker_scope, startMessages: true });
20
20
  Observer.observe(workerComm, changes => {
21
21
  //console.log('SERVICE_WORKER_STATE_CHANGE', changes[0].name, changes[0].value);
@@ -35,6 +35,9 @@ export default class RuntimeClient {
35
35
  // The app router
36
36
  const router = new Router(this.cx, httpEvent.url.pathname);
37
37
  const handle = async () => {
38
+ if (this.cx.params.address_bar_synchrony === 'instant') {
39
+ await this.render(httpEvent, {}, router);
40
+ }
38
41
  // --------
39
42
  // ROUTE FOR DATA
40
43
  // --------
@@ -69,30 +72,40 @@ export default class RuntimeClient {
69
72
 
70
73
  // Renderer
71
74
  async render(httpEvent, response, router) {
72
- let data = await response.json();
75
+ let data = response.json ? await response.json() : response;
73
76
  return router.route('render', httpEvent, data, async (httpEvent, data) => {
74
77
  // --------
75
78
  // OOHTML would waiting for DOM-ready in order to be initialized
76
- await new Promise(res => window.WebQit.DOM.ready(res));
77
- if (!window.document.state.env) {
78
- window.document.setState({
79
- env: 'client',
80
- onHydration: (httpEvent.detail || {}).srcType === 'init',
81
- network: this.cx.runtime.network,
82
- url: this.cx.runtime.location,
83
- }, { update: true });
79
+ if (window.WebQit.DOM) {
80
+ await new Promise(res => window.WebQit.DOM.ready(res));
81
+ }
82
+ if (window.document.state) {
83
+ if (!window.document.state.env) {
84
+ window.document.setState({
85
+ env: 'client',
86
+ onHydration: (httpEvent.detail || {}).srcType === 'init',
87
+ network: this.cx.runtime.network,
88
+ url: this.cx.runtime.location,
89
+ }, { update: true });
90
+ }
91
+ window.document.setState({ page: data }, { update: 'merge' });
92
+ }
93
+ if (window.document.templates) {
94
+ window.document.body.setAttribute('template', 'page/' + httpEvent.url.pathname.split('/').filter(a => a).map(a => a + '+-').join('/'));
95
+ await new Promise(res => (window.document.templatesReadyState === 'complete' && res(), window.document.addEventListener('templatesreadystatechange', res)));
84
96
  }
85
- window.document.setState({ page: data }, { update: 'merge' });
86
- window.document.body.setAttribute('template', 'page/' + httpEvent.url.pathname.split('/').filter(a => a).map(a => a + '+-').join('/'));
87
- await new Promise(res => (window.document.templatesReadyState === 'complete' && res(), window.document.addEventListener('templatesreadystatechange', res)));
88
97
  return window;
89
98
  });
90
99
  }
91
100
 
92
101
  // Unrender
93
102
  async unrender() {
94
- window.document.setState({ page: {} }, { update: 'merge' });
95
- window.document.body.setAttribute('template', '');
103
+ if (window.document.state) {
104
+ window.document.setState({ page: {} }, { update: 'merge' });
105
+ }
106
+ if (window.document.templates) {
107
+ window.document.body.setAttribute('template', '');
108
+ }
96
109
  }
97
110
 
98
111
  // Normalize scroll position