@soleil-se/app-util 3.0.2 → 4.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.
@@ -0,0 +1,56 @@
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,3 +1,5 @@
1
+ import getCurrentScript from './getCurrentScript';
2
+
1
3
  /**
2
4
  * JSON decode an attribute on the currentScript element.
3
5
  * Use if attribute contains a JSON-object.
@@ -5,7 +7,8 @@
5
7
  * @returns {Object} Decoded JSON object.
6
8
  */
7
9
  export const decodeAttribute = (attribute) => {
8
- const encoded = document.currentScript.getAttribute(attribute);
10
+ const currentScript = getCurrentScript();
11
+ const encoded = currentScript.getAttribute(attribute);
9
12
  if (encoded) {
10
13
  return decodeURIComponent(encoded);
11
14
  }
@@ -1,14 +1,9 @@
1
1
  /* eslint-disable import/prefer-default-export */
2
- import 'current-script-polyfill';
3
-
4
- import { decodeAttribute, parseAttribute } from '../attribute-util';
5
- import { setAppData } from '../app-data';
6
-
7
- const props = parseAttribute('data-app');
8
- setAppData(props);
2
+ import { decodeAttribute } from '../attribute-util';
3
+ import { getAppData } from '../../common';
9
4
 
10
5
  /**
11
- * Render a Svelte application
6
+ * Renders a Svelte application
12
7
  * @param {Svelte} App Svelte application to be started.
13
8
  */
14
9
  export function render(App) {
@@ -16,6 +11,7 @@ export function render(App) {
16
11
 
17
12
  const mountElement = document.querySelector(selector);
18
13
  const hydrate = mountElement.childElementCount > 0;
14
+ const props = getAppData();
19
15
  if (mountElement) {
20
16
  return new App({
21
17
  hydrate,
@@ -1,15 +1,13 @@
1
- import 'current-script-polyfill';
1
+ /* eslint-disable import/prefer-default-export */
2
+ /* eslint-disable-next-line import/no-unresolved */
2
3
  import Vue from 'vue';
3
4
 
4
- import { decodeAttribute, parseAttribute } from '../attribute-util';
5
- import { setAppData } from '../app-data';
6
-
7
- const options = parseAttribute('data-app');
8
- setAppData(options);
5
+ import { decodeAttribute } from '../attribute-util';
6
+ import { getAppData, isOffline } from '../../common';
9
7
 
10
8
  const offlineModeMixin = {
11
9
  mounted() {
12
- if (this.$options.isOffline) {
10
+ if (isOffline) {
13
11
  const $anchors = this.$el.querySelectorAll('a');
14
12
  $anchors.forEach(($anchor) => {
15
13
  $anchor.addEventListener('click', (e) => {
@@ -24,8 +22,9 @@ const offlineModeMixin = {
24
22
  * Render a Vue application
25
23
  * @param {Vue} App Vue application to be started.
26
24
  */
27
- export default function render(App) {
25
+ export function render(App) {
28
26
  const selector = decodeAttribute('data-selector');
27
+ const options = getAppData();
29
28
 
30
29
  Object.assign(App, options, {
31
30
  mixins: [offlineModeMixin],
@@ -0,0 +1,144 @@
1
+ /* eslint-disable global-require */
2
+ import { parseAttribute } from '../client/attribute-util';
3
+ import getLegacyRouteUri from './legacy/getRouteUri';
4
+ import getLegacyViewUri from './legacy/getViewUri';
5
+
6
+ const appMetadata = process.browser ? parseAttribute('data-metadata') : {};
7
+
8
+ /**
9
+ * Get an ID for the app.
10
+ * @return {String} ID
11
+ */
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
+ };
22
+
23
+ /**
24
+ * Regex for selecting leading slashes
25
+ * @constant {Regex}
26
+ */
27
+ const leadingSlashes = /^(\/)*/g;
28
+
29
+ /**
30
+ * Regex for selecting trailing slashes
31
+ * @constant {Regex}
32
+ */
33
+ const trailingSlashes = /(\/)*$/g;
34
+
35
+ /**
36
+ * DOM friendly unique identifier for the WebApp.
37
+ * @constant {String}
38
+ */
39
+ export const appId = process.server ? getAppId() : appMetadata.id;
40
+
41
+ /**
42
+ * If the WebApp is running in offline mode or not.
43
+ * @constant {Boolean}
44
+ */
45
+ export const isOffline = process.server
46
+ ? require('VersionUtil').getCurrentVersion() === require('VersionUtil').OFFLINE_VERSION
47
+ : appMetadata.isOffline;
48
+
49
+ /**
50
+ * If the WebApp is running in online mode or not.
51
+ * @constant {Boolean}
52
+ */
53
+ export const isOnline = !isOffline;
54
+
55
+ /**
56
+ * Get a prefixed namespace unique for app.
57
+ * @param {string} [prefix='app']
58
+ * @return {String} - Prefixed namespace.
59
+ */
60
+ export function getNamespace(prefix = 'app') {
61
+ return `${prefix}_${appId}`;
62
+ }
63
+
64
+ /**
65
+ * Get URI for a route, same as `getStandaloneUrl` in SiteVision template.
66
+ * @param {String} route A route.
67
+ * @returns {String} URI for route.
68
+ */
69
+ export function getRouteUri(route = '') {
70
+ 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}`;
82
+ }
83
+
84
+ /**
85
+ * Get URI for a view, same as `getUrl` in SiteVision template.
86
+ * @param {String} route A route.
87
+ * @returns {String} URI for view.
88
+ */
89
+ 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;
99
+ }
100
+
101
+ /**
102
+ * Get URI for a resource.
103
+ * @param {String} resource A resource.
104
+ * @returns {String} URI for a resource.
105
+ */
106
+ export function getResourceUri(resource = '') {
107
+ 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
+ };
125
+ }
126
+
127
+ let appData = process.browser ? parseAttribute('data-app-data') : {};
128
+
129
+ export function setAppData(data) {
130
+ appData = data;
131
+ }
132
+
133
+ /**
134
+ * Get appData value or object that is passed to app when rendering.
135
+ * @export
136
+ * @param {String} [key] - Key for value.
137
+ * @return {*|Object} - Value or object.
138
+ */
139
+ export function getAppData(key) {
140
+ if (process.browser && !Object.keys(appData).length) {
141
+ setAppData(parseAttribute('data-options'));
142
+ }
143
+ return key ? appData[key] : appData;
144
+ }
@@ -0,0 +1,19 @@
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
+ }
@@ -0,0 +1,21 @@
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
+ }
@@ -0,0 +1,53 @@
1
+ # Render
2
+ Returns HTML for a script tag with server data avaliable as attributes on the tag.
3
+ Framework independent
4
+
5
+ ## index.js
6
+ ### render([data], [settings]) ⇒ <code>String</code>
7
+ `@soleil-api/webapp-util/server`
8
+
9
+ Get a HTML string for rendering an application.
10
+
11
+ **Returns**: <code>String</code> - HTML for rendering an application.
12
+
13
+ | Param | Type | Default | Description |
14
+ | --- | --- | --- | --- |
15
+ | [data] | <code>Object</code> | <code>{}</code> | Server data that will be available in the attribute. `data-app-data` on the script tag. |
16
+ | [settings] | <code>Object</code> | <code>{}</code> | Settings object. |
17
+ | [settings.html] | <code>String</code> | <code>&#x27;&#x27;</code> | HTML that will be rendered inside mount element. |
18
+ | [settings.selector] | <code>String</code> | <code>&#x60;[data-portlet-id&#x3D;&quot;${portletId}&quot;]&#x60;</code> | Query selector for where the app should be mounted. |
19
+ | [settings.async] | <code>Boolean</code> | <code>false</code> | If the app script should be loaded asynchronously. |
20
+ | [settings.defer] | <code>Boolean</code> | <code>true</code> | If the app script should be loaded after DOM is ready. |
21
+ | [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. |
22
+
23
+ [Read more about async and defer.](https://flaviocopes.com/javascript-async-defer/)
24
+
25
+ ### Example
26
+
27
+ ```js
28
+ import router from 'router';
29
+ import { render } from '@soleil-api/webapp-util/server';
30
+
31
+ router.get('/', (req, res) => {
32
+ const data = { foo: 'bar' };
33
+ res.send(render(data));
34
+ };
35
+ ```
36
+
37
+ **All settings**
38
+ ```js
39
+ import router from 'router';
40
+ import { render } from '@soleil-api/webapp-util/server';
41
+
42
+ router.get('/', (req, res) => {
43
+ const data = { foo: 'bar' };
44
+ const settings = {
45
+ html: '<noscript>You can put a noscript message here for example!</noscript>',
46
+ selector: '#mount_me_here',
47
+ async: false,
48
+ defer: true,
49
+ req,
50
+ }
51
+ res.send(render(data, settings));
52
+ };
53
+ ```
@@ -0,0 +1,52 @@
1
+ # Svelte
2
+ SiteVision supports both server and client side rendering with Svelte.
3
+
4
+ ## index.js
5
+ For rendering a client side only app use the framework independent [client renderer](./1.render.md).
6
+
7
+ ### `render(App, [props], [settings])` ⇒ `String`
8
+ `@soleil-api/webapp-util/server/svelte`
9
+
10
+ Get a HTML string for rendering a universal Svelte application.
11
+ If a client bundle is available the server rendered HTML will be hydrated and not completely re-rendered.
12
+
13
+ **Returns**: <code>String</code> - HTML for rendering a Svelte application.
14
+
15
+ | Param | Type | Default | Description |
16
+ | --- | --- | --- | --- |
17
+ | [props] | <code>Object</code> | <code>{}</code> | Server data that will be available as props and as app data. |
18
+ | [settings] | <code>Object</code> | <code>{}</code> | Settings object. Forwarded to |
19
+ | [settings.selector] | <code>String</code> | <code>&#x60;[data-portlet-id&#x3D;&quot;${portletId}&quot;]&#x60;</code> | Query selector for where the app should be mounted. |
20
+ | [settings.async] | <code>Boolean</code> | <code>false</code> | If the app script should be loaded asynchronously. |
21
+ | [settings.defer] | <code>Boolean</code> | <code>true</code> | If the app script should be loaded after DOM is ready. |
22
+ | [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. |
23
+
24
+ [Read more about async and defer.](https://flaviocopes.com/javascript-async-defer/)
25
+
26
+ In `app_src/index.js` or `app_src/server/index.js`.
27
+ ```javascript
28
+ import router from 'router';
29
+ import { render } from '@soleil-api/webapp-util/server/svelte';
30
+
31
+ import App from './App.svelte';
32
+
33
+ router.get('/', (req, res) => {
34
+ const props = { foo: 'bar' };
35
+ res.send(render(App, data));
36
+ });
37
+ ```
38
+
39
+ ## main.js
40
+
41
+ ### `render(App)`
42
+ `@soleil-api/webapp-util/client/svelte`
43
+
44
+ Renders the client side app.
45
+
46
+ In `app_src/main.js` or `app_src/client/index.js`.
47
+ ```javascript
48
+ import { render } from '@soleil-api/webapp-util/client/svelte';
49
+ import App from './App.svelte';
50
+
51
+ render(App);
52
+ ```
package/docs/3.vue.md ADDED
@@ -0,0 +1,19 @@
1
+ # Vue
2
+ SiteVision supports client side rendering with Vue.
3
+
4
+ ## index.js
5
+ For rendering a client side only app use the framework independent [client renderer](./1.render.md).
6
+
7
+ ## main.js
8
+ ### `render(App)`
9
+ `@soleil-api/webapp-util/client/vue`
10
+
11
+ Renders the client side app.
12
+
13
+ In `app_src/main.js` or `app_src/client/index.js`.
14
+ ```javascript
15
+ import { render } from '@soleil-api/webapp-util/client/vue';
16
+ import App from './App.vue';
17
+
18
+ render(App);
19
+ ```
@@ -0,0 +1,48 @@
1
+ # Underscore
2
+ Render a Underscore template.
3
+
4
+ ## `renderTemplate(template, [values])` ⇒ `String`
5
+ Renders a Underscore template and returns a string.
6
+ This function is also available inside the template.
7
+
8
+ **Returns**: `String` - Rendered template
9
+
10
+ | Param | Type | Default | Description |
11
+ | --- | --- | --- | --- |
12
+ | template | `String` | | Underscore template. |
13
+ | [values] | `Object` | `{}` | Values. |
14
+
15
+ ### Examples
16
+ **Simple example**
17
+ ```javascript
18
+ import { renderTemplate } from '@soleil-api/webapp-util/server';
19
+
20
+ const string = renderTemplate('<div><%= foo %></div>', {
21
+ foo: 'bar',
22
+ });
23
+ ```
24
+ **Multiple templates**
25
+ ```html
26
+ /* views/item.html */
27
+ <li>
28
+ <%- name %>
29
+ </li>
30
+ ```
31
+ ```html
32
+ /* views/main.html */
33
+ <ul>
34
+ <% items.forEach(function(item) { %>
35
+ <%= renderTemplate(itemTemplate, item) %>
36
+ <% }); %>
37
+ </ul>
38
+ ```
39
+ ```javascript
40
+ import { renderTemplate } from '@soleil-api/webapp-util/server';
41
+ import mainTemplate from './views/main.html';
42
+ import itemTemplate from './views/item.html';
43
+
44
+ const items = [{ name: 'Foo' }, { name: 'Bar' }, { name: 'Baz' }];
45
+ const string = renderTemplate(mainTemplate, { items, itemTemplate });
46
+ ```
47
+ > **NOTE**
48
+ > Remember that the second argument must be an object and that objects properties are accessed directly in any child templates!
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@soleil-se/app-util",
3
- "version": "3.0.2",
3
+ "version": "4.0.0",
4
4
  "description": "Utility functions for Webapps.",
5
- "main": "./src/index.js",
5
+ "main": "./common/index.js",
6
6
  "author": "Soleil AB",
7
7
  "contributors": [
8
8
  "Kimmy Monassar",
@@ -10,13 +10,11 @@
10
10
  ],
11
11
  "license": "UNLICENSED",
12
12
  "private": false,
13
- "homepage": "https://github.com/soleilit/server-monorepo/tree/master/packages/app-util#readme",
14
- "dependencies": {
15
- "current-script-polyfill": "^1.0.0"
16
- },
17
- "peerDependencies": {
13
+ "homepage": "https://docs.soleilit.se/03.packages/@soleil-api&app-util",
14
+ "optionalDependencies": {
18
15
  "vue": "^2.6.11"
19
16
  },
20
- "gitHead": "70fb8e9913e3cee064f5987a0e8157a5115ff29a",
17
+ "gitHead": "a59289650f6f1c5277566106c29fb8db01fbebd1",
18
+ "dependencies": {},
21
19
  "devDependencies": {}
22
20
  }
@@ -0,0 +1,78 @@
1
+ import { appImportDate } from 'appInfo';
2
+ /* Underscore is provided by SiteVision */
3
+ /* eslint-disable-next-line import/no-extraneous-dependencies */
4
+ import _ from 'underscore';
5
+ import {
6
+ appId, isOffline, getResourceUri, getAppMetadata,
7
+ } from '../common';
8
+
9
+ const isIE = (req) => {
10
+ if (!req) return true;
11
+ const userAgent = req.header('user-agent');
12
+ return /Trident\/|MSIE/.test(userAgent);
13
+ };
14
+
15
+ /**
16
+ * Get a HTML string for rendering an application.
17
+ * @param {Object} [data={}] Server data that will be available in the attribute.
18
+ * `data-app-data` on the script tag.
19
+ * @param {Object} [settings={}] Settings object.
20
+ * @param {String} [settings.html=''] HTML that will be rendered inside mount element.
21
+ * @param {String} [settings.selector=`[data-portlet-id="${portletId}"]`] Query selector for
22
+ * where the app should be mounted.
23
+ * @param {Boolean} [settings.async=false] If the app script should be loaded asynchronously.
24
+ * [Read more about async and defer.](https://flaviocopes.com/javascript-async-defer/)
25
+ * @param {Boolean} [settings.defer=true] If the app script should be loaded after DOM is ready.
26
+ * [Read more about async and defer.](https://flaviocopes.com/javascript-async-defer/)
27
+ * @param {Object} [settings.req] The req object from SiteVision, pass this to optimize browser
28
+ * specific script loading if you have multiple instances of the app.
29
+ * @returns {String} HTML for rendering an application.
30
+ */
31
+ export function render(data, {
32
+ html = '',
33
+ selector = `[data-portlet-id="${appId}"]`,
34
+ async = false,
35
+ defer = true,
36
+ req,
37
+ } = {}) {
38
+ const appMetadata = getAppMetadata();
39
+
40
+ if (isOffline) {
41
+ return `
42
+ <div data-portlet-id="${appId}">${html}</div>
43
+ <script>
44
+ window.svDocReady(function() {
45
+ var targetElement = document.querySelector('[data-portlet-id="${appId}"]');
46
+ var script = document.createElement("script");
47
+ script.src = "${getResourceUri('client/index.js')}?${appImportDate}${appId}";
48
+ script.setAttribute("data-metadata", "${encodeURIComponent(JSON.stringify(appMetadata))}");
49
+ script.setAttribute("data-app-data", "${encodeURIComponent(JSON.stringify(data))}");
50
+ script.setAttribute("data-selector", "${encodeURIComponent(selector)}");
51
+ targetElement.parentNode.insertBefore(script, targetElement.nextSibling);
52
+ });
53
+ </script>`;
54
+ }
55
+ return `
56
+ <div data-portlet-id="${appId}">${html}</div>
57
+ <script
58
+ src="${getResourceUri('client/index.js')}?${appImportDate}${isIE(req) ? appId : ''}"
59
+ data-metadata="${encodeURIComponent(JSON.stringify(appMetadata))}"
60
+ data-app-data="${encodeURIComponent(JSON.stringify(data))}"
61
+ data-selector="${encodeURIComponent(selector)}"
62
+ ${async && !defer ? 'async' : ''}
63
+ ${defer ? 'defer' : ''}>
64
+ </script>`;
65
+ }
66
+
67
+ /**
68
+ * Renders a Underscore template and returns a string.
69
+ * @param {String} template Underscore template.
70
+ * @param {Object} [values={}] Values.
71
+ * @returns {String} Rendered template
72
+ */
73
+ export function renderTemplate(template, values = {}) {
74
+ if (typeof template === 'function') {
75
+ return template({ ...values, renderTemplate });
76
+ }
77
+ return _.template(template)(({ ...values, renderTemplate }));
78
+ }
@@ -0,0 +1,33 @@
1
+ /* eslint-disable import/prefer-default-export */
2
+ import appResource from 'appResource';
3
+ import { setAppData } from '../../common';
4
+ import { render as renderClient } from '../index';
5
+
6
+ /**
7
+ * SSR Svelte App.
8
+ * @export
9
+ * @param {SvelteComponent} App - Svelte component that is root of app.
10
+ * @param {Object} [props] - Properties available on root component. Also forwarded as appData.
11
+ * @return {String} - HTML for rendering a Svelte application.
12
+ */
13
+ export function renderServer(App, props = {}) {
14
+ setAppData(props);
15
+ const { html } = App.render(props);
16
+ return html;
17
+ }
18
+
19
+ /**
20
+ * Render a Svelte App.
21
+ * @export
22
+ * @param {SvelteComponent} App - Svelte component that is root of app.
23
+ * @param {Object} [props] - Properties available on root component. Also forwarded as appData.
24
+ * @param {Object} [settings] - Settings forwarded to render function for client app.
25
+ * @return {String} - HTML for rendering a Svelte application.
26
+ */
27
+ export function render(App, props = {}, settings) {
28
+ const html = renderServer(App, props);
29
+ if (appResource.getNode('client/index.js')) {
30
+ return renderClient(props, { ...settings, html });
31
+ }
32
+ return html;
33
+ }
@@ -1,4 +0,0 @@
1
- {
2
- "extends": "@soleil/eslint-config-sitevision/client",
3
- "root": true
4
- }
package/app-data/index.js DELETED
@@ -1,16 +0,0 @@
1
- import { parseAttribute } from '../attribute-util';
2
-
3
- let appData = {};
4
-
5
- export function setAppData(data) {
6
- appData = data;
7
- }
8
-
9
- export function getAppData(key) {
10
- if (!Object.keys(appData).length && process && process.browser) {
11
- setAppData(parseAttribute('data-app'));
12
- }
13
- return key ? appData[key] : appData;
14
- }
15
-
16
- export default process && process.browser ? parseAttribute('data-app') : appData;
@@ -1,4 +0,0 @@
1
- {
2
- "extends": "@soleil/eslint-config-sitevision/client",
3
- "root": true
4
- }