@soleil-se/app-util 4.2.0 → 5.0.0

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.
package/CHANGELOG.md CHANGED
@@ -5,7 +5,25 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
- ## [4.1.4] - 2021-08-20
8
+ ## [5.0.0] - 2021-xx-xx
9
+
10
+ ### Changed
11
+
12
+ - Using WebApps 2 for rendering apps.
13
+
14
+ ### Removed
15
+
16
+ - `getAppData`, use `getAppProps` instead.
17
+ - `server/render`, use `res.agnosticRender` for framework agnostic rendering.
18
+ - `server/renderTemplate`, used for rendering underscore templates.
19
+
20
+ ## [4.2.1] - 2021-09-06
21
+
22
+ ### Fixed
23
+
24
+ - Underscore is misspelled in `renderTemplate`.
25
+
26
+ ## [4.2.0] - 2021-08-20
9
27
 
10
28
  ### Added
11
29
 
package/MIGRATION.md CHANGED
@@ -1,156 +1,149 @@
1
1
  # Migration
2
2
 
3
- Migration from version 2 or 3 to 4.
3
+ Migration from version 4 to 5.
4
4
 
5
5
  <!-- TOC -->
6
6
 
7
- - [renderApp](#renderapp)
8
- - [Export](#export)
9
- - [Settings](#settings)
10
- - [renderTemplate](#rendertemplate)
11
- - [getRouteUri](#getrouteuri)
12
- - [AppData](#appdata)
13
- - [Get data](#get-data)
14
- - [Standard values](#standard-values)
15
- - [Svelte](#svelte)
16
- - [Server](#server)
17
- - [Client](#client)
18
- - [Vue](#vue)
7
+ - [Manifest](#manifest)
8
+ - [index.js](#indexjs)
9
+ - [Universal app](#universal-app)
10
+ - [SSR app](#ssr-app)
11
+ - [CSR app](#csr-app)
12
+ - [main.js](#mainjs)
19
13
 
20
14
  <!-- /TOC -->
21
15
 
22
- ## renderApp
23
- ### Export
24
- Export moved and renamed.
16
+ ## Manifest
25
17
 
26
- Old:
27
- ```js
28
- import { renderApp } from '@soleil-api/webapp-util';
29
- ```
30
- New:
31
- ```js
32
- import { render } from '@soleil-api/webapp-util/server';
33
- ```
18
+ Add `"bundled": true` to the app manifest file.
34
19
 
35
- ### Settings
36
- Selector is moved to client side rendering functions.
20
+ manifest.json
21
+
22
+ ```json
23
+ {
24
+ "id": "se.soleil.myApp",
25
+ "version": "1.0.0",
26
+ "name": "My app",
27
+ "author": "Soleil AB",
28
+ "description": "",
29
+ "helpUrl": "https://soleil.se/support",
30
+ "type": "WebApp",
31
+ "bundled": true
32
+ }
37
33
 
38
- Old (index.js):
39
- ```js
40
- res.send(renderApp(data, { selector: '#mount_app_here' }));
41
34
  ```
42
35
 
43
- New (main.js):
44
- ```js
45
- import { render } from '@soleil-api/webapp-util/client/vue';
46
- import App from './App.vue';
36
+ ## index.js
47
37
 
48
- render(App, { selector: '#mount_app_here' });
49
- ```
38
+ Rendering is now using Webapps 2 render functions.
50
39
 
51
- ## renderTemplate
52
- Export moved.
40
+ ### Universal app
53
41
 
54
- Old:
55
- ```js
56
- import { renderTemplate } from '@soleil-api/webapp-util';
57
- ```
58
- New:
59
- ```js
60
- import { renderTemplate } from '@soleil-api/webapp-util/server';
61
- ```
62
- ## getRouteUri
63
- URIs for routes was previously passed as props when rendering an application. This is no longer needed.
64
- Now you can use `getRouteUri` in a client context as well.
42
+ Old
65
43
 
66
- Old:
67
44
  ```js
68
- import AppData from '@soleil-api/webapp-util/app-data';
45
+ import router from '@sitevision/api/common/router';
46
+ import { render } from '@soleil-api/webapp-util/server/svelte';
69
47
 
70
- const getItems = async () => {
71
- return fetch(AppData.itemsRoute)
72
- .then((res) => res.json());
73
- };
74
- ```
75
- New:
76
- ```js
77
- import { getRouteUri } from '@soleil-api/webapp-util';
48
+ import App from './App.svelte';
78
49
 
79
- const getItems = async () => {
80
- return fetch(getRouteUri('/items'))
81
- .then((res) => res.json());
82
- };
50
+ router.get('/', (req, res) => {
51
+ const props = { foo: 'bar' };
52
+ res.send(render(App, props));
53
+ });
83
54
  ```
84
- ## AppData
85
55
 
86
- ### Get data
87
- The default export as object and `@soleil-api/webapp-util/app-data` import is removed.
56
+ New
88
57
 
89
- Old:
90
58
  ```js
91
- import AppData from '@soleil-api/webapp-util/app-data';
59
+ import router from '@sitevision/api/common/router';
60
+ import { render } from '@soleil-api/webapp-util/server/svelte';
92
61
 
93
- const { myValue } = AppData;
94
- ```
95
- New:
96
- ```js
97
- import { getAppData } from '@soleil-api/webapp-util';
62
+ import App from './App.svelte';
98
63
 
99
- // Get value with key
100
- const myValue = getAppData('myValue');
101
- // Or with destructuring
102
- const { myValue } = getAppData();
64
+ router.get('/', (req, res) => {
65
+ const props = { foo: 'bar' };
66
+ const html = render(App, props);
67
+ res.agnosticRender(html, props);
68
+ });
103
69
  ```
104
- ### Standard values
105
- Values that was set as sandard are now moved.
106
70
 
107
- Old:
108
- ```js
109
- import AppData from '@soleil-api/webapp-util/app-data';
71
+ ### SSR app
72
+
73
+ Old
110
74
 
111
- const { isOnline, isOffline, uniqueId } = AppData;
112
- ```
113
- New:
114
75
  ```js
115
- import { isOffline, isOnline, getNamespace } from '@soleil-api/webapp-util';
76
+ import router from '@sitevision/api/common/router';
77
+ import { renderServer } from '@soleil-api/webapp-util/server/svelte';
116
78
 
117
- const uniqueId = getNamespace();
79
+ import App from './App.svelte';
80
+
81
+ router.get('/', (req, res) => {
82
+ const props = { foo: 'bar' };
83
+ res.send(renderServer(App, props));
84
+ });
118
85
  ```
119
86
 
120
- ## Svelte
121
- ### Server
122
- Export moved.
123
- Export `renderClient` is removed, use `render` from `'@soleil-api/webapp-util/server`
87
+ New
124
88
 
125
- Old:
126
- ```js
127
- import { render } from '@soleil-api/webapp-util/svelte-server';
128
- ```
129
- New:
130
89
  ```js
90
+ import router from '@sitevision/api/common/router';
131
91
  import { render } from '@soleil-api/webapp-util/server/svelte';
92
+
93
+ import App from './App.svelte';
94
+
95
+ router.get('/', (req, res) => {
96
+ const props = { foo: 'bar' };
97
+ const html = render(App, props);
98
+ res.send(html);
99
+ });
132
100
  ```
133
- ### Client
134
- Export moved.
135
101
 
136
- Old:
102
+ ### CSR app
103
+
104
+ Old
105
+
137
106
  ```js
138
- import { render } from '@soleil-api/webapp-util/svelte-client';
107
+ import router from '@sitevision/api/common/router';
108
+ import { render } from '@soleil-api/webapp-util/server';
109
+
110
+ router.get('/', (req, res) => {
111
+ const props = { foo: 'bar' };
112
+ res.send(render(props));
113
+ });
139
114
  ```
140
- New:
115
+
116
+ New
117
+
141
118
  ```js
142
- import { render } from '@soleil-api/webapp-util/client/svelte';
119
+ import router from '@sitevision/api/common/router';
120
+
121
+ router.get('/', (req, res) => {
122
+ const props = { foo: 'bar' };
123
+ res.agnosticRender('', props)
124
+ });
143
125
  ```
144
126
 
145
- ## Vue
146
- Export moved.
127
+ ## main.js
128
+
129
+ Is now expecting a default export containing a function
130
+
131
+ Old
147
132
 
148
- Old:
149
133
  ```js
150
- import render from '@soleil-api/webapp-util/render-vue';
134
+ import { render } from '@soleil-api/webapp-util/client/svelte';
135
+ import App from './App.svelte';
136
+
137
+ render(App);
151
138
  ```
152
- New:
139
+
140
+ New
141
+
153
142
  ```js
154
- import { render } from '@soleil-api/webapp-util/client/vue';
155
- ```
143
+ import { render } from '@soleil-api/webapp-util/client/svelte';
144
+ import App from './App.svelte';
156
145
 
146
+ export default (props, target) => {
147
+ render(App, { props, target });
148
+ };
149
+ ```
package/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
 
2
- # `@soleil-se/webapp-util@^4.0.0`
2
+ # `@soleil-se/webapp-util@^5.0.0`
3
3
 
4
- Utility functions for Webapps.
4
+ Utility functions for WebApps.
5
5
 
6
6
  [GitHub](https://github.com/soleilit/server-monorepo/tree/master/packages/webapp-util)
7
7
  [NPM](https://www.npmjs.com/package/@soleil-se/webapp-util)
@@ -10,7 +10,27 @@ Utility functions for Webapps.
10
10
 
11
11
  ## Requirements
12
12
 
13
- `@soleil-se/sv-gulp-build` 4 or later (Works best with ^4.3.0).
13
+ * Sitevision 9.1.0 or later.
14
+ * `@soleil-se/sv-app-build` 1.0.0 or later.
15
+ * WebApps 2 enabled app.
16
+
17
+ ### Manifest
18
+
19
+ `bundled` needs to be enabled in [WebApp manifest](https://developer.sitevision.se/docs/webapps/webapps-2/manifest) for WebApps 2.
20
+
21
+ ```json
22
+ {
23
+ "id": "se.soleil.myApp",
24
+ "version": "1.0.0",
25
+ "name": "My app",
26
+ "author": "Soleil AB",
27
+ "description": "",
28
+ "helpUrl": "https://soleil.se/support",
29
+ "type": "WebApp",
30
+ "bundled": true
31
+ }
32
+
33
+ ```
14
34
 
15
35
  ## Install
16
36
 
@@ -18,7 +38,7 @@ Utility functions for Webapps.
18
38
 
19
39
  ## Migration
20
40
 
21
- Migrating from version 2 or 3?
41
+ Migrating from version 4?
22
42
  See [MIGRATION](./MIGRATION.md).
23
43
 
24
44
  ## API
@@ -57,9 +77,6 @@ All imports from base package are available both on the server and client.
57
77
  <dt><a href="#getAppProps">getAppProps([key])</a> ⇒ <code>*</code> | <code>Object</code></dt>
58
78
  <dd><p>Get props that are passed to app when rendering.</p>
59
79
  </dd>
60
- <dt><del><a href="#getAppData">getAppData([key])</a> ⇒ <code>*</code> | <code>Object</code></del></dt>
61
- <dd><p>Get props that are passed to app when rendering.</p>
62
- </dd>
63
80
  </dl>
64
81
 
65
82
  <a name="appId"></a>
@@ -200,32 +217,7 @@ const myValue = getAppProps('myValue');
200
217
  const { myValue } = getAppProps();
201
218
  ```
202
219
 
203
- <a name="getAppData"></a>
204
-
205
- ### getAppData([key]) ⇒ `*` | `Object` (DEPRECATED)
206
-
207
- Use [getAppProps](#getAppProps) instead.
208
-
209
- Get props that are passed to app when rendering.
210
-
211
- **Returns**: `*` | `Object` - Value or object.
212
-
213
- | Param | Type | Description |
214
- | --- | --- | --- |
215
- | [key] | `String` | Key for value. |
216
-
217
- ```js
218
- import { getAppData } from '@soleil-se/webapp-util';
219
-
220
- // Get value with key
221
- const myValue = getAppData('myValue');
222
- // Or with destructuring
223
- const { myValue } = getAppData();
224
- ```
225
-
226
220
  ## Rendering
227
221
 
228
- - [Render](./docs/1.render.md)
229
- - [Svelte](./docs/2.svelte.md)
230
- - [Vue](./docs/3.vue.md)
231
- - [Underscore](./docs/4.underscore.md)
222
+ * [Svelte](./docs/1.svelte.md)
223
+ * [Vue (DEPRECATED)](./docs/2.vue.md)
@@ -1,22 +1,24 @@
1
- /* eslint-disable import/prefer-default-export */
2
- import { appId, getAppProps } from '../../common';
1
+ import { setAppProps } from '../../common';
3
2
 
4
3
  /**
5
4
  * Renders a client side Svelte application.
6
5
  * @param {*} App Svelte app root component.
7
- * @param {Object} [settings={}] Settings object.
8
- * @param {String} [settings.selector=`#app_mount_${appId}`] Query selector for mount element.
9
- * @param {String} [settings.intro=false] If true, will play transitions on initial render,
6
+ * @param {object} [settings={}] Settings object.
7
+ * @param {string} [settings.target] Target where app should be mounted.
8
+ * @param {string} [settings.props] Root component props.
9
+ * @param {string} [settings.intro=false] If true, will play transitions on initial render,
10
10
  * rather than waiting for subsequent state changes.
11
11
  * @return {*} Initialized Svelte component.
12
12
  */
13
13
  export function render(App, {
14
- selector = `#app_mount_${appId}`,
14
+ target,
15
+ props,
15
16
  intro = false,
16
17
  } = {}) {
17
- const target = document.querySelector(selector);
18
+ setAppProps(props);
19
+
18
20
  const hydrate = target.childElementCount > 0;
19
- const props = getAppProps();
21
+
20
22
  return new App({
21
23
  hydrate,
22
24
  target,
package/common/index.js CHANGED
@@ -1,24 +1,14 @@
1
+ /* global document */
1
2
  /* eslint-disable global-require */
2
- import { parseJson } from '../client/utils';
3
- import getLegacyRouteUri from './legacy/getRouteUri';
4
- import getLegacyViewUri from './legacy/getViewUri';
5
3
 
6
- const appMetadata = process.browser ? parseJson('app_meta') : {};
4
+ import router from '@sitevision/api/common/router';
5
+ import app from '@sitevision/api/common/app';
7
6
 
8
7
  /**
9
8
  * Get an ID for the app.
10
9
  * @return {String} ID
11
10
  */
12
- const getAppId = () => {
13
- const PortletContextUtil = require('PortletContextUtil');
14
- const id = (PortletContextUtil.getPortletNamespace('') || 'ADDON_PREVIEW').replace('.', '_');
15
- const decoratedNode = PortletContextUtil.getCurrentDecoratedNode();
16
-
17
- if (decoratedNode) {
18
- return `${decoratedNode.getIdentifier().replace('.', '_')}_${id}`;
19
- }
20
- return id;
21
- };
11
+ const getAppId = () => app.portletId.replace('.', '_');
22
12
 
23
13
  /**
24
14
  * Regex for selecting leading slashes
@@ -34,21 +24,21 @@ const trailingSlashes = /(\/)*$/g;
34
24
 
35
25
  /**
36
26
  * DOM friendly unique identifier for the WebApp.
37
- * @constant {String}
27
+ * @constant {string}
38
28
  */
39
- export const appId = process.server ? getAppId() : appMetadata.id;
29
+ export const appId = getAppId();
40
30
 
41
31
  /**
42
32
  * If the WebApp is running in offline mode or not.
43
- * @constant {Boolean}
33
+ * @constant {boolean}
44
34
  */
45
35
  export const isOffline = process.server
46
36
  ? require('VersionUtil').getCurrentVersion() === require('VersionUtil').OFFLINE_VERSION
47
- : appMetadata.isOffline;
37
+ : document.documentElement.classList.contains('sv-edit-mode');
48
38
 
49
39
  /**
50
40
  * If the WebApp is running in online mode or not.
51
- * @constant {Boolean}
41
+ * @constant {boolean}
52
42
  */
53
43
  export const isOnline = !isOffline;
54
44
 
@@ -62,92 +52,48 @@ export function getNamespace(prefix = 'app') {
62
52
  }
63
53
 
64
54
  /**
65
- * Get URI for a route.
66
- * @param {String} route A route.
67
- * @returns {String} URI for route.
55
+ * Get URI for a route, same as `getStandaloneUrl` in Sitevision router.
56
+ * @param {string} route A route.
57
+ * @param {object} query Query parameters.
58
+ * @returns {string} URI for route.
68
59
  */
69
- export function getRouteUri(route = '') {
60
+ export function getRouteUri(route = '', query) {
70
61
  const path = route.replace(leadingSlashes, '');
71
- if (process.server) {
72
- const router = require('router');
73
- if (router.getStandaloneUrl) {
74
- return router
75
- .getStandaloneUrl(path !== '' ? `/${path}` : path)
76
- .replace(trailingSlashes, '');
77
- }
78
- // If Sitevision 7 or older.
79
- return getLegacyRouteUri(path);
80
- }
81
- return `${appMetadata.baseRouteUri}/${path}`;
62
+ return router.getStandaloneUrl(path !== '' ? `/${path}` : path, query).replace(trailingSlashes, '');
82
63
  }
83
64
 
84
65
  /**
85
- * Get URI for a view.
86
- * @param {String} route A route.
87
- * @returns {String} URI for view.
66
+ * Get URI for a view, same as `getUrl` in Sitevision router.
67
+ * @param {string} route A route.
68
+ * @returns {string} URI for view.
88
69
  */
89
70
  export function getViewUri(route = '') {
90
- if (process.server) {
91
- const router = require('router');
92
- if (router.getUrl) {
93
- return router.getUrl(route);
94
- }
95
- // If Sitevision 7 or older.
96
- return getLegacyViewUri(route, { isOffline });
97
- }
98
- return appMetadata.baseViewUri + route;
71
+ return router.getUrl(route);
99
72
  }
100
73
 
101
74
  /**
102
75
  * Get URI for a resource.
103
- * @param {String} resource A resource.
104
- * @returns {String} URI for a resource.
76
+ * @param {string} resource A resource.
77
+ * @returns {string} URI for a resource.
105
78
  */
106
79
  export function getResourceUri(resource = '') {
80
+ const { webAppId, webAppVersion } = app;
107
81
  const path = resource.replace(leadingSlashes, '');
108
- if (process.server) {
109
- const appInfo = require('appInfo');
110
- return `/webapp-files/${appInfo.appIdentifier}/${appInfo.appVersion}/${path}`
111
- .replace(trailingSlashes, '');
112
- }
113
- return `${appMetadata.baseResourceUri}/${path}`;
114
- }
115
-
116
- export function getAppMetadata() {
117
- return {
118
- id: appId,
119
- isOffline,
120
- isOnline,
121
- baseRouteUri: getRouteUri(),
122
- baseViewUri: getViewUri(),
123
- baseResourceUri: getResourceUri(),
124
- };
82
+ return `/webapp-files/${webAppId}/${webAppVersion}/${path}`;
125
83
  }
126
84
 
127
- let appProps = process.browser ? parseJson('app_props') : {};
85
+ let appProps = {};
128
86
 
129
87
  export function setAppProps(data) {
130
88
  appProps = data;
131
89
  }
132
90
 
133
91
  /**
134
- * Get props that are passed to app when rendering.
135
- * @param {String} [key] Key for value.
136
- * @returns {*|Object} Value or object.
92
+ * Get appData value or object that is passed to app when rendering.
93
+ * @export
94
+ * @param {string} [key] - Key for value.
95
+ * @return {*|object} - Value or object.
137
96
  */
138
97
  export function getAppProps(key) {
139
- if (process.browser && !appProps) {
140
- setAppProps(parseJson('app_props'));
141
- }
142
98
  return key ? appProps[key] : appProps;
143
99
  }
144
-
145
- /**
146
- * Get props that are passed to app when rendering.
147
- * @deprecated Use `getAppProps` instead.
148
- * @param {String} [key] - Key for value.
149
- * @return {*|Object} Value or object.
150
- */
151
- export function getAppData(key) {
152
- return getAppProps(key);
153
- }
@@ -0,0 +1,115 @@
1
+ # Svelte
2
+
3
+ Sitevision supports both server and client side rendering with Svelte.
4
+
5
+ ## index.js
6
+
7
+ For rendering a client side only app use the framework agnostic [client renderer](./1.render.md).
8
+
9
+ ### `render(App, [props])`
10
+
11
+ `@soleil-api/webapp-util/server/svelte`
12
+
13
+ Get a HTML string for rendering a universal Svelte application.
14
+ If a client bundle is available the server rendered HTML will be hydrated and not completely re-rendered.
15
+
16
+ **Returns**: <code>String</code> - HTML for rendering a Svelte application.
17
+
18
+ | Param | Type | Default | Description |
19
+ | --- | --- | --- | --- |
20
+ | [App] | <code>Svelte</code> | | Svelte app to be rendered. |
21
+ | [props] | <code>Object</code> | <code>{}</code> | Props that will be passed to the app. |
22
+
23
+ ### Universal
24
+
25
+ Render an app with both server and client code.
26
+
27
+ `index.js`
28
+
29
+ ```javascript
30
+ import router from '@sitevision/api/common/router';
31
+ import { render } from '@soleil-api/webapp-util/server/svelte';
32
+
33
+ import App from './App.svelte';
34
+
35
+ router.get('/', (req, res) => {
36
+ const props = { foo: 'bar' };
37
+ const html = render(App, props);
38
+ res.agnosticRender(html, props);
39
+ });
40
+ ```
41
+
42
+ ### SSR
43
+
44
+ Render an app with only server side code.
45
+
46
+ `index.js`
47
+
48
+ ```javascript
49
+ import router from '@sitevision/api/common/router';
50
+ import { render } from '@soleil-api/webapp-util/server/svelte';
51
+
52
+ import App from './App.svelte';
53
+
54
+ router.get('/', (req, res) => {
55
+ const props = { foo: 'bar' };
56
+ const html = render(App, props);
57
+ res.send(html);
58
+ });
59
+ ```
60
+
61
+ ### CSR
62
+
63
+ Render an app with only client side code.
64
+
65
+ `index.js`
66
+
67
+ ```javascript
68
+ import router from '@sitevision/api/common/router';
69
+
70
+ router.get('/', (req, res) => {
71
+ const props = { foo: 'bar' };
72
+ res.agnosticRender('', props)
73
+ });
74
+ ```
75
+
76
+ ## main.js
77
+
78
+ ### `render(App, { target, props })`
79
+
80
+ `@soleil-api/webapp-util/client/svelte`
81
+
82
+ Renders a client side Svelte application.
83
+
84
+ **Returns**: <code>\*</code> - Initialized Svelte component.
85
+
86
+ | Param | Type | Default | Description |
87
+ | --- | --- | --- | --- |
88
+ | App | <code>\*</code> | | Svelte app root component. |
89
+ | [settings] | <code>Object</code> | <code>{}</code> | Settings object. |
90
+ | [settings.target] | <code>Element</code> | | Target where app should be mounted. |
91
+ | [settings.props] | <code>Object</code> | | Root component props. |
92
+ | [settings.intro] | <code>String</code> | <code>false</code> | If true, will play transitions on initial render, rather than waiting for subsequent state changes. |
93
+
94
+ `main.js`
95
+
96
+ ```javascript
97
+ import { render } from '@soleil-api/webapp-util/client/svelte';
98
+ import App from './App.svelte';
99
+
100
+ export default (props, target) => {
101
+ render(App, { props, target });
102
+ };
103
+ ```
104
+
105
+ Mount the app in another element:
106
+ `main.js`
107
+
108
+ ```javascript
109
+ import { render } from '@soleil-api/webapp-util/client/svelte';
110
+ import App from './App.svelte';
111
+
112
+ export default (props) => {
113
+ render(App, { props, target: document.querySelector('#mount_app_here') });
114
+ };
115
+ ```
package/docs/2.vue.md ADDED
@@ -0,0 +1,57 @@
1
+ # Vue (DEPRECATED)
2
+
3
+ Sitevision only supports client side rendering with Vue.
4
+
5
+ ## index.js
6
+
7
+ Render an app with only client code.
8
+ `index.js`
9
+
10
+ ```javascript
11
+ import router from '@sitevision/api/common/router';
12
+
13
+ router.get('/', (req, res) => {
14
+ const props = { foo: 'bar' };
15
+ res.agnosticRender('', props)
16
+ });
17
+ ```
18
+
19
+ ## main.js
20
+
21
+ ### `render(App, { target, props })`
22
+
23
+ `@soleil-api/webapp-util/client/vue`
24
+
25
+ Renders a client side Vue application.
26
+
27
+ **Returns**: <code>\*</code> - Initialized Vue app.
28
+
29
+ | Param | Type | Default | Description |
30
+ | --- | --- | --- | --- |
31
+ | App | <code>\*</code> | | Svelte app root component. |
32
+ | [settings] | <code>Object</code> | <code>{}</code> | Settings object. |
33
+ | [settings.target] | <code>Element</code> | | Target where app should be mounted. |
34
+ | [settings.props] | <code>Object</code> | | Root component props. |
35
+
36
+ `main.js`
37
+
38
+ ```javascript
39
+ import { render } from '@soleil-api/webapp-util/client/vue';
40
+ import App from './App.vue';
41
+
42
+ export default (props, target) => {
43
+ render(App, { props, target });
44
+ };
45
+ ```
46
+
47
+ Mount the app in another element:
48
+ `main.js`
49
+
50
+ ```javascript
51
+ import { render } from '@soleil-api/webapp-util/client/vue';
52
+ import App from './App.vue';
53
+
54
+ export default (props) => {
55
+ render(App, { props, target: document.querySelector('#mount_app_here') });
56
+ };
57
+ ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@soleil-se/app-util",
3
- "version": "4.2.0",
3
+ "version": "5.0.0",
4
4
  "description": "Utility and rendering functions for WebApps.",
5
5
  "main": "./common/index.js",
6
6
  "author": "Soleil AB",
@@ -11,7 +11,12 @@
11
11
  "license": "UNLICENSED",
12
12
  "private": false,
13
13
  "homepage": "https://docs.soleilit.se/03.packages/@soleil-api&app-util",
14
- "gitHead": "56b2bb3947dc881fa4dbed17b6cb9cf90a8d0a85",
15
- "dependencies": {},
16
- "devDependencies": {}
14
+ "devDependencies": {
15
+ "@sitevision/api": "^1.0.10",
16
+ "svelte": "^3.44.1"
17
+ },
18
+ "peerDependencies": {
19
+ "@sitevision/api": "^1.0.10"
20
+ },
21
+ "dependencies": {}
17
22
  }
@@ -1,36 +1,13 @@
1
- import appResource from 'appResource';
2
- import { appId, setAppProps } from '../../common';
3
- import { render as renderClient } from '../index';
4
-
5
- const initSSR = (App, props) => {
6
- setAppProps(props);
7
- return App.render(props).html;
8
- };
9
-
10
- /**
11
- * SSR Svelte App.
12
- * @export
13
- * @param {SvelteComponent} App - Svelte component that is root of app.
14
- * @param {Object} [props] - Props available on root component. Also avaliable with getAppProps.
15
- * @return {String} - HTML for rendering a Svelte application.
16
- */
17
- export function renderServer(App, props = {}) {
18
- const html = initSSR(App, props);
19
- return `<div id="app_mount_${appId}">${html}</div>`;
20
- }
1
+ import { setAppProps } from '../../common';
21
2
 
22
3
  /**
23
- * Render a Svelte App.
24
- * @export
4
+ * Returns HTML for a server rendered Svelte app.
25
5
  * @param {SvelteComponent} App - Svelte component that is root of app.
26
- * @param {Object} [props] - Props available on root component. Also avaliable with getAppProps.
27
- * @param {Object} [settings] - Settings for rendering a client app.
28
- * @return {String} - HTML for rendering a Svelte application.
6
+ * @param {object} props - Props passed to root component.
7
+ * @return {string} - HTML for the server rendered app.
29
8
  */
30
- export function render(App, props = {}, settings) {
31
- const html = initSSR(App, props);
32
- if (appResource.getNode('client/index.js')) {
33
- return renderClient(props, { ...settings, html });
34
- }
35
- return `<div id="app_mount_${appId}">${html}</div>`;
9
+ export function render(App, props) {
10
+ setAppProps(props);
11
+ const { html } = App.render(props);
12
+ return html;
36
13
  }
@@ -1,56 +0,0 @@
1
- /* Scoped version of https://github.com/amiller-gh/currentScript-polyfill */
2
- // document.currentScript polyfill by Adam Miller
3
- // MIT license
4
-
5
- export default function getCurrentScript() {
6
- if (!('currentScript' in document)) {
7
- // IE 8-10 support script readyState
8
- // IE 11+ support stack trace
9
- try {
10
- throw new Error();
11
- } catch (err) {
12
- // Find the second match for the "at" string to get file src url from stack.
13
- // Specifically works with the format of stack traces in IE.
14
- let i = 0;
15
- const stackDetails = (/.*at [^(]*\((.*):(.+):(.+)\)$/ig).exec(err.stack);
16
- const scriptLocation = (stackDetails && stackDetails[1]) || false;
17
- const line = (stackDetails && stackDetails[2]) || false;
18
- const currentLocation = document.location.href.replace(document.location.hash, '');
19
- let pageSource;
20
- let inlineScriptSourceRegExp;
21
- let inlineScriptSource;
22
- const scripts = document.getElementsByTagName('script'); // Live NodeList collection
23
-
24
- if (scriptLocation === currentLocation) {
25
- pageSource = document.documentElement.outerHTML;
26
- inlineScriptSourceRegExp = new RegExp(`(?:[^\\n]+?\\n){0,${line - 2}}[^<]*<script>([\\d\\D]*?)<\\/script>[\\d\\D]*`, 'i');
27
- inlineScriptSource = pageSource.replace(inlineScriptSourceRegExp, '$1').trim();
28
- }
29
-
30
- for (; i < scripts.length; i += 1) {
31
- // If ready state is interactive, return the script tag
32
- if (scripts[i].readyState === 'interactive') {
33
- return scripts[i];
34
- }
35
-
36
- // If src matches, return the script tag
37
- if (scripts[i].src === scriptLocation) {
38
- return scripts[i];
39
- }
40
-
41
- // If inline source matches, return the script tag
42
- if (
43
- scriptLocation === currentLocation
44
- && scripts[i].innerHTML
45
- && scripts[i].innerHTML.trim() === inlineScriptSource
46
- ) {
47
- return scripts[i];
48
- }
49
- }
50
-
51
- // If no match, return null
52
- return null;
53
- }
54
- }
55
- return document.currentScript;
56
- }
@@ -1,27 +0,0 @@
1
- import getCurrentScript from './getCurrentScript';
2
-
3
- const unescapeTags = (unsafe) => unsafe
4
- .replace(/&lt;/g, '<')
5
- .replace(/&gt;/g, '>');
6
-
7
- /**
8
- * JSON decode an attribute on the currentScript element.
9
- * Use if attribute contains a JSON-object.
10
- * @param {String} attribute Attribute to decode.
11
- * @returns {Object} Decoded JSON object.
12
- */
13
- export const getAttribute = (attribute) => {
14
- const currentScript = getCurrentScript();
15
- const encoded = currentScript.getAttribute(attribute);
16
- if (encoded) {
17
- return decodeURIComponent(encoded);
18
- }
19
- return undefined;
20
- };
21
-
22
- export const parseJson = (type) => {
23
- const id = getAttribute('data-app-id');
24
- let json = document.querySelector(`[data-id="${type}_${id}"]`)?.textContent || '{}';
25
- json = unescapeTags(json);
26
- return JSON.parse(json);
27
- };
@@ -1,33 +0,0 @@
1
- /* eslint-disable import/prefer-default-export */
2
- /* eslint-disable-next-line import/no-unresolved, import/no-extraneous-dependencies */
3
- import Vue from 'vue';
4
-
5
- import { appId, getAppProps, isOffline } from '../../common';
6
-
7
- const offlineModeMixin = {
8
- mounted() {
9
- const $anchors = this.$el.querySelectorAll('a');
10
- $anchors.forEach(($anchor) => {
11
- $anchor.addEventListener('click', (e) => {
12
- e.preventDefault();
13
- window.$svjq($anchor).trigger('click');
14
- }, false);
15
- });
16
- },
17
- };
18
- /**
19
- * Renders a client side Vue application.
20
- * @param {*} App Vue app root component.
21
- * @param {Object} [settings={}] Settings object.
22
- * @param {String} [settings.selector=`#app_mount_${appId}`] Query selector for mount element.
23
- */
24
- export function render(App, { selector = `#app_mount_${appId}` } = {}) {
25
- const options = getAppProps();
26
-
27
- Object.assign(App, options, {
28
- propsData: options,
29
- mixins: isOffline ? [offlineModeMixin] : [],
30
- });
31
-
32
- new Vue(App).$mount(selector);
33
- }
@@ -1,19 +0,0 @@
1
- /* eslint-disable global-require */
2
-
3
- /**
4
- * Regex for selecting trailing slashes
5
- * @constant {Regex}
6
- */
7
- const trailingSlashes = /(\/)*$/g;
8
-
9
- export default function getRouteUri(route) {
10
- const PortletContextUtil = require('PortletContextUtil');
11
- const currentPage = PortletContextUtil.getCurrentPage();
12
- const currentPortlet = PortletContextUtil.getCurrentPortlet();
13
- if (currentPage && currentPortlet) {
14
- const currentPageId = currentPage.getIdentifier().replace('_sitePage', '');
15
- const currentPortletId = currentPortlet.getIdentifier();
16
- return `/appresource/${currentPageId}/${currentPortletId}/${route}`.replace(trailingSlashes, '');
17
- }
18
- return '/';
19
- }
@@ -1,21 +0,0 @@
1
- /* eslint-disable global-require */
2
-
3
- export default function getViewUri(route, { isOffline }) {
4
- const PortletContextUtil = require('PortletContextUtil');
5
- const PropertyUtil = require('PropertyUtil');
6
- const appInfo = require('appInfo');
7
- const currentPage = PortletContextUtil.getCurrentPage();
8
- const currentPortlet = PortletContextUtil.getCurrentPortlet();
9
-
10
- if (currentPage && currentPortlet) {
11
- const currentPageId = currentPage.getIdentifier().replace('_sitePage', '');
12
- const currentPageUri = PropertyUtil.getString(currentPage, 'URI');
13
- const currentPortletId = PortletContextUtil.getCurrentPortlet().getIdentifier();
14
- if (isOffline) {
15
- return `/edit-offline/${currentPageId}?sv.target=${currentPortletId}&sv.${currentPortletId}.route=${encodeURI(route)}`;
16
- }
17
- return `${currentPageUri}?sv.target=${currentPortletId}&sv.${currentPortletId}.route=${encodeURI(route)}`;
18
- }
19
- const addonId = appInfo['jcr:uuid'];
20
- return `/edit-web-app-offline/${addonId}?sv.target=${addonId}&sv.${addonId}.route=${encodeURI(route)}`;
21
- }
package/docs/1.render.md DELETED
@@ -1,58 +0,0 @@
1
- # Render
2
-
3
- Returns HTML for a script tag with the possibility to pass data from the server.
4
- Framework agnostic.
5
-
6
- Server data is embedded as JSON in script tags.
7
- <https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#embedding_data_in_html>
8
-
9
- ## index.js
10
-
11
- ### render([props], [settings]) ⇒ <code>String</code>
12
-
13
- `@soleil-api/webapp-util/server`
14
-
15
- Get a HTML string for rendering an application.
16
-
17
- **Returns**: <code>String</code> - HTML for rendering an application.
18
-
19
- | Param | Type | Default | Description |
20
- | --- | --- | --- | --- |
21
- | [props] | <code>Object</code> | <code>{}</code> | Props that will be passed to the app |
22
- | [settings] | <code>Object</code> | <code>{}</code> | Settings object. |
23
- | [settings.html] | <code>String</code> | <code>&#x27;&#x27;</code> | HTML that will be rendered inside mount element. |
24
- | [settings.async] | <code>Boolean</code> | <code>false</code> | If the app script should be loaded asynchronously. |
25
- | [settings.defer] | <code>Boolean</code> | <code>true</code> | If the app script should be loaded after DOM is ready. |
26
- | [settings.req] | <code>Object</code> | | The req object from Sitevision, pass this to optimize browser specific script loading if you have multiple instances of the app. |
27
-
28
- [Read more about async and defer.](https://flaviocopes.com/javascript-async-defer/)
29
-
30
- ### Example
31
-
32
- ```js
33
- import router from '@Sitevision/api/common/router';
34
- import { render } from '@soleil-api/webapp-util/server';
35
-
36
- router.get('/', (req, res) => {
37
- const props = { foo: 'bar' };
38
- res.send(render(props));
39
- };
40
- ```
41
-
42
- **All settings**
43
-
44
- ```js
45
- import router from '@Sitevision/api/common/router';
46
- import { render } from '@soleil-api/webapp-util/server';
47
-
48
- router.get('/', (req, res) => {
49
- const props = { foo: 'bar' };
50
- const settings = {
51
- html: '<noscript>You can put a noscript message here for example!</noscript>',
52
- async: false,
53
- defer: true,
54
- req,
55
- }
56
- res.send(render(props, settings));
57
- };
58
- ```
package/docs/2.svelte.md DELETED
@@ -1,103 +0,0 @@
1
- # Svelte
2
-
3
- Sitevision supports both server and client side rendering with Svelte.
4
-
5
- ## index.js
6
-
7
- For rendering a client side only app use the framework agnostic [client renderer](./1.render.md).
8
-
9
- ### `render(App, [props], [settings])`
10
-
11
- `@soleil-api/webapp-util/server/svelte`
12
-
13
- Get a HTML string for rendering a universal Svelte application.
14
- If a client bundle is available the server rendered HTML will be hydrated and not completely re-rendered.
15
-
16
- **Returns**: <code>String</code> - HTML for rendering a Svelte application.
17
-
18
- | Param | Type | Default | Description |
19
- | --- | --- | --- | --- |
20
- | [App] | <code>Svelte</code> | | Svelte app to be rendered. |
21
- | [props] | <code>Object</code> | <code>{}</code> | Props that will be passed to the app. |
22
- | [settings] | <code>Object</code> | <code>{}</code> | Settings object. Forwarded to |
23
- | [settings.async] | <code>Boolean</code> | <code>false</code> | If the app script should be loaded asynchronously. |
24
- | [settings.defer] | <code>Boolean</code> | <code>true</code> | If the app script should be loaded after DOM is ready. |
25
- | [settings.req] | <code>Object</code> | | The req object from Sitevision, pass this to optimize browser specific script loading if you have multiple instances of the app. |
26
-
27
- [Read more about async and defer.](https://flaviocopes.com/javascript-async-defer/)
28
-
29
- In `app_src/index.js` or `app_src/server/index.js`.
30
-
31
- ```javascript
32
- import router from '@Sitevision/api/common/router';
33
- import { render } from '@soleil-api/webapp-util/server/svelte';
34
-
35
- import App from './App.svelte';
36
-
37
- router.get('/', (req, res) => {
38
- const props = { foo: 'bar' };
39
- res.send(render(App, props));
40
- });
41
- ```
42
-
43
- ### `renderServer(App, [props])`
44
-
45
- `@soleil-api/webapp-util/server/svelte`
46
-
47
- Get a HTML string for rendering a server side Svelte application.
48
-
49
- **Returns**: <code>String</code> - HTML for rendering a Svelte application.
50
-
51
- | Param | Type | Default | Description |
52
- | --- | --- | --- | --- |
53
- | [App] | <code>Svelte</code> | | Svelte app to be rendered. |
54
- | [props] | <code>Object</code> | <code>{}</code> | Server data that will be available as props and as app data. |
55
-
56
- In `app_src/index.js` or `app_src/server/index.js`.
57
-
58
- ```javascript
59
- import router from '@Sitevision/api/common/router';
60
- import { renderServer } from '@soleil-api/webapp-util/server/svelte';
61
-
62
- import App from './App.svelte';
63
-
64
- router.get('/', (req, res) => {
65
- const props = { foo: 'bar' };
66
- res.send(render(App, props));
67
- });
68
- ```
69
-
70
- ## main.js
71
-
72
- ### `render(App, [settings])`
73
-
74
- `@soleil-api/webapp-util/client/svelte`
75
-
76
- Renders a client side Svelte application.
77
-
78
- **Returns**: <code>\*</code> - Initialized Svelte component.
79
-
80
- | Param | Type | Default | Description |
81
- | --- | --- | --- | --- |
82
- | App | <code>\*</code> | | Svelte app root component. |
83
- | [settings] | <code>Object</code> | <code>{}</code> | Settings object. |
84
- | [settings.selector] | <code>String</code> | <code>&#x60;#app_mount_${appId}&#x60;</code> | Query selector for mount element. |
85
- | [settings.intro] | <code>String</code> | <code>false</code> | If true, will play transitions on initial render, rather than waiting for subsequent state changes. |
86
-
87
- In `app_src/main.js` or `app_src/client/index.js`.
88
-
89
- ```javascript
90
- import { render } from '@soleil-api/webapp-util/client/svelte';
91
- import App from './App.svelte';
92
-
93
- render(App);
94
- ```
95
-
96
- Mount the app in another element:
97
-
98
- ```javascript
99
- import { render } from '@soleil-api/webapp-util/client/svelte';
100
- import App from './App.svelte';
101
-
102
- render(App, { selector: '#mount_app_here' });
103
- ```
package/docs/3.vue.md DELETED
@@ -1,38 +0,0 @@
1
- # Vue
2
-
3
- Sitevision supports client side rendering with Vue.
4
-
5
- ## index.js
6
-
7
- For rendering a client side only app use the framework agnostic [client renderer](./1.render.md).
8
-
9
- ## main.js
10
-
11
- ### `render(App, [settings])`
12
-
13
- `@soleil-api/webapp-util/client/vue`
14
- Renders a client side Vue application.
15
-
16
- | Param | Type | Default | Description |
17
- | --- | --- | --- | --- |
18
- | App | <code>\*</code> | | Vue app root component. |
19
- | [settings] | <code>Object</code> | <code>{}</code> | Settings object. |
20
- | [settings.selector] | <code>String</code> | <code>&#x60;#app_mount_${appId}&#x60;</code> | Query selector for mount element. |
21
-
22
- In `app_src/main.js` or `app_src/client/index.js`.
23
-
24
- ```javascript
25
- import { render } from '@soleil-api/webapp-util/client/vue';
26
- import App from './App.vue';
27
-
28
- render(App);
29
- ```
30
-
31
- Mount the app in another element:
32
-
33
- ```javascript
34
- import { render } from '@soleil-api/webapp-util/client/vue';
35
- import App from './App.vue';
36
-
37
- render(App, { selector: '#mount_app_here' });
38
- ```
@@ -1,57 +0,0 @@
1
- # Underscore
2
-
3
- Render a Underscore template.
4
-
5
- ## `renderTemplate(template, [values])` ⇒ `String`
6
-
7
- Renders a Underscore template and returns a string.
8
- This function is also available inside the template.
9
-
10
- **Returns**: `String` - Rendered template
11
-
12
- | Param | Type | Default | Description |
13
- | --- | --- | --- | --- |
14
- | template | `String` | | Underscore template. |
15
- | [values] | `Object` | `{}` | Values. |
16
-
17
- ### Examples
18
-
19
- **Simple example**
20
-
21
- ```javascript
22
- import { renderTemplate } from '@soleil-api/webapp-util/server';
23
-
24
- const string = renderTemplate('<div><%= foo %></div>', {
25
- foo: 'bar',
26
- });
27
- ```
28
-
29
- **Multiple templates**
30
-
31
- ```html
32
- /* views/item.html */
33
- <li>
34
- <%- name %>
35
- </li>
36
- ```
37
-
38
- ```html
39
- /* views/main.html */
40
- <ul>
41
- <% items.forEach(function(item) { %>
42
- <%= renderTemplate(itemTemplate, item) %>
43
- <% }); %>
44
- </ul>
45
- ```
46
-
47
- ```javascript
48
- import { renderTemplate } from '@soleil-api/webapp-util/server';
49
- import mainTemplate from './views/main.html';
50
- import itemTemplate from './views/item.html';
51
-
52
- const items = [{ name: 'Foo' }, { name: 'Bar' }, { name: 'Baz' }];
53
- const string = renderTemplate(mainTemplate, { items, itemTemplate });
54
- ```
55
-
56
- > **NOTE**
57
- > Remember that the second argument must be an object and that objects properties are accessed directly in any child templates!
package/server/index.js DELETED
@@ -1,83 +0,0 @@
1
- import { appImportDate } from 'appInfo';
2
- import {
3
- appId, isOffline, getResourceUri, getAppMetadata,
4
- } from '../common';
5
-
6
- const isIE = (req) => {
7
- if (!req) return true;
8
- const userAgent = req.header('user-agent');
9
- return /Trident\/|MSIE/.test(userAgent);
10
- };
11
-
12
- const escapeTags = (unsafe) => unsafe
13
- .replace(/</g, '&lt;')
14
- .replace(/>/g, '&gt;');
15
-
16
- /**
17
- * Get a HTML string for rendering an application.
18
- * @param {Object} [props={}] Props that will be passed to the app.
19
- * @param {Object} [settings={}] Settings object.
20
- * @param {String} [settings.html=''] HTML that will be rendered inside mount element.
21
- * where the app should be mounted.
22
- * @param {Boolean} [settings.async=false] If the app script should be loaded asynchronously.
23
- * [Read more about async and defer.](https://flaviocopes.com/javascript-async-defer/)
24
- * @param {Boolean} [settings.defer=true] If the app script should be loaded after DOM is ready.
25
- * [Read more about async and defer.](https://flaviocopes.com/javascript-async-defer/)
26
- * @param {Object} [settings.req] The req object from Sitevision, pass this to optimize browser
27
- * specific script loading if you have multiple instances of the app.
28
- * @returns {String} HTML for rendering an application.
29
- */
30
- export function render(props, {
31
- html = '',
32
- async = false,
33
- defer = true,
34
- req,
35
- } = {}) {
36
- const appMetadata = getAppMetadata();
37
-
38
- const mountElement = `<div id="app_mount_${appId}">${html}</div>`;
39
- const metaScriptTag = `<script data-id="app_meta_${appId}" type="application/json">${JSON.stringify(appMetadata)}</script>`;
40
- const propsScriptTag = props ? `<script data-id="app_props_${appId}" type="application/json">${escapeTags(JSON.stringify(props))}</script>` : '';
41
-
42
- if (isOffline) {
43
- return `
44
- ${mountElement}
45
- ${metaScriptTag}
46
- ${propsScriptTag}
47
- <script>
48
- window.svDocReady(function() {
49
- var targetElement = document.getElementById('app_mount_${appId}');
50
- var script = document.createElement("script");
51
- script.src = "${getResourceUri('client/index.js')}?${appImportDate}${appId}";
52
- script.setAttribute("data-app-id", "${appId}");
53
- targetElement.parentNode.insertBefore(script, targetElement.nextSibling);
54
- });
55
- </script>`;
56
- }
57
- return `
58
- ${mountElement}
59
- ${metaScriptTag}
60
- ${propsScriptTag}
61
- <script
62
- src="${getResourceUri('client/index.js')}?${appImportDate}${isIE(req) ? appId : ''}"
63
- data-app-id="${appId}"
64
- ${async && !defer ? 'async' : ''}
65
- ${defer ? 'defer' : ''}>
66
- </script>`;
67
- }
68
-
69
- /**
70
- * Renders a Underscore template and returns a string.
71
- * @param {String} template Underscore template.
72
- * @param {Object} [values={}] Values.
73
- * @returns {String} Rendered template
74
- */
75
- export function renderTemplate(template, values = {}) {
76
- // Import undercore dynamically in the rare case this function is still used.
77
- // eslint-disable-next-line global-require
78
- const _ = require('undercore');
79
- if (typeof template === 'function') {
80
- return template({ ...values, renderTemplate });
81
- }
82
- return _.template(template)(({ ...values, renderTemplate }));
83
- }