@sap-ux/preview-middleware 0.4.2 → 0.4.4

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/README.md CHANGED
@@ -1,6 +1,7 @@
1
1
  # `@sap-ux/preview-middleware`
2
2
 
3
3
  The `@sap-ux/preview-middleware` is a [Custom UI5 Server Middleware](https://sap.github.io/ui5-tooling/pages/extensibility/CustomServerMiddleware) for previewing an application in a local Fiori launchpad . It can be used either with the `ui5 serve` or the `fiori run` commands.
4
+ It hosts a local Fiori launchpad based on your configuration as well as offers an API to modify flex changes in your project. The API is available at `/preview/api` and additional client code required for the preview is available at `/preview/client`.
4
5
 
5
6
  ## Configuration Options
6
7
  | Option | Type | Default Value | Description |
@@ -11,7 +12,7 @@ The `@sap-ux/preview-middleware` is a [Custom UI5 Server Middleware](https://sap
11
12
  | `flp.intent.object` | `string` | `app` | Optional intent object |
12
13
  | `flp.intent.action` | `string` | `preview` | Optional intent action |
13
14
  | `flp.apps` | `array` | `[]` | Optional additional local apps that are available in local Fiori launchpad |
14
- | `flp.libs` | `boolean` | `false` | Optional flag to add a generic script fetching the paths of used libraries not available in UI5 |
15
+ | `flp.libs` | `boolean` | `undefined` | Optional flag to add a generic script fetching the paths of used libraries not available in UI5. To disable set it to `false`, if not set, then the project is checked for a `load-reuse-libs` script and if available the libraries are fetched as well. |
15
16
  | `adp.target` | | | Required configuration for adaptation projects defining the connected backend |
16
17
  | `adp.ignoreCertErrors` | `boolean` | `false` | Optional setting to ignore certification validation errors when working with e.g. development systems with self signed certificates |
17
18
  | `debug` | `boolean` | `false` | Enables debug output |
@@ -36,7 +36,7 @@ export interface TemplateConfig {
36
36
  layer: UI5FlexLayer;
37
37
  developerMode: boolean;
38
38
  };
39
- locateReuseLibsScript?: string;
39
+ locateReuseLibsScript?: boolean;
40
40
  }
41
41
  /**
42
42
  * Class handling preview of a sandbox FLP.
@@ -74,7 +74,7 @@ export declare class FlpSandbox {
74
74
  *
75
75
  * @returns the location of the locate-reuse-libs script or undefined.
76
76
  */
77
- private findLocateReuseLibsScript;
77
+ private hasLocateReuseLibsScript;
78
78
  /**
79
79
  * Add additional routes for apps also to be shown in the local FLP.
80
80
  */
package/dist/base/flp.js CHANGED
@@ -23,10 +23,6 @@ const DEFAULT_THEME = 'sap_horizon';
23
23
  * Default path for mounting the local FLP.
24
24
  */
25
25
  const DEFAULT_PATH = '/test/flp.html';
26
- /**
27
- * Default name of the locate reuse libs script.
28
- */
29
- const DEFAULT_LOCATE_LIBS_FILENAME = 'locate-reuse-libs.js';
30
26
  /**
31
27
  * Default intent
32
28
  */
@@ -34,6 +30,17 @@ const DEFAULT_INTENT = {
34
30
  object: 'app',
35
31
  action: 'preview'
36
32
  };
33
+ /**
34
+ * Static settings
35
+ */
36
+ const PREVIEW_URL = {
37
+ client: {
38
+ url: '/preview/client',
39
+ local: (0, path_1.join)(__dirname, '../../dist/client'),
40
+ ns: 'open.ux.preview.client'
41
+ },
42
+ api: '/preview/api'
43
+ };
37
44
  /**
38
45
  * Class handling preview of a sandbox FLP.
39
46
  */
@@ -72,7 +79,7 @@ class FlpSandbox {
72
79
  * @param resources optional additional resource mappings
73
80
  */
74
81
  init(manifest, componentId, resources = {}) {
75
- var _a, _b, _c, _d, _e, _f, _g;
82
+ var _a, _b, _c, _d, _e, _f, _g, _h;
76
83
  return __awaiter(this, void 0, void 0, function* () {
77
84
  const flex = this.createFlexHandler();
78
85
  const supportedThemes = (_b = (_a = manifest['sap.ui5']) === null || _a === void 0 ? void 0 : _a.supportedThemes) !== null && _b !== void 0 ? _b : [DEFAULT_THEME];
@@ -83,15 +90,13 @@ class FlpSandbox {
83
90
  libs: Object.keys((_f = (_e = (_d = manifest['sap.ui5']) === null || _d === void 0 ? void 0 : _d.dependencies) === null || _e === void 0 ? void 0 : _e.libs) !== null && _f !== void 0 ? _f : {}).join(','),
84
91
  theme: supportedThemes.includes(DEFAULT_THEME) ? DEFAULT_THEME : supportedThemes[0],
85
92
  flex,
86
- resources: Object.assign({}, resources)
93
+ resources: Object.assign(Object.assign({}, resources), { [PREVIEW_URL.client.ns]: PREVIEW_URL.client.url })
87
94
  },
88
- locateReuseLibsScript: this.config.libs
89
- ? `./${DEFAULT_LOCATE_LIBS_FILENAME}`
90
- : yield this.findLocateReuseLibsScript()
95
+ locateReuseLibsScript: (_g = this.config.libs) !== null && _g !== void 0 ? _g : (yield this.hasLocateReuseLibsScript())
91
96
  };
92
97
  this.addApp(manifest, {
93
98
  componentId,
94
- target: (_g = resources[componentId !== null && componentId !== void 0 ? componentId : manifest['sap.app'].id]) !== null && _g !== void 0 ? _g : this.templateConfig.basePath,
99
+ target: (_h = resources[componentId !== null && componentId !== void 0 ? componentId : manifest['sap.app'].id]) !== null && _h !== void 0 ? _h : this.templateConfig.basePath,
95
100
  local: '.',
96
101
  intent: this.config.intent
97
102
  });
@@ -105,6 +110,8 @@ class FlpSandbox {
105
110
  * Add routes for html and scripts required for a local FLP.
106
111
  */
107
112
  addStandardRoutes() {
113
+ // register static client sources
114
+ this.router.use(PREVIEW_URL.client.url, (0, express_1.static)(PREVIEW_URL.client.local));
108
115
  // add route for the sandbox.html
109
116
  this.router.get(this.config.path, ((req, res) => __awaiter(this, void 0, void 0, function* () {
110
117
  var _a, _b;
@@ -137,31 +144,16 @@ class FlpSandbox {
137
144
  res.status(200).contentType('html').send(html);
138
145
  }
139
146
  })));
140
- // add route for locate-reuse-libs if requested
141
- if (this.config.libs && this.templateConfig.locateReuseLibsScript) {
142
- const pathParts = this.config.path.split('/');
143
- pathParts.pop();
144
- pathParts.push(DEFAULT_LOCATE_LIBS_FILENAME);
145
- this.router.get(pathParts.join('/'), (_req, res) => {
146
- const script = (0, fs_1.readFileSync)((0, path_1.join)(__dirname, '../../templates/flp/locate-reuse-libs.js'), 'utf-8');
147
- res.status(200).contentType('text/javascript').send(script);
148
- });
149
- }
150
147
  }
151
148
  /**
152
149
  * Try finding a locate-reuse-libs script in the project.
153
150
  *
154
151
  * @returns the location of the locate-reuse-libs script or undefined.
155
152
  */
156
- findLocateReuseLibsScript() {
153
+ hasLocateReuseLibsScript() {
157
154
  return __awaiter(this, void 0, void 0, function* () {
158
155
  const files = yield this.project.byGlob('**/locate-reuse-libs.js');
159
- if (files.length > 0) {
160
- return files[0].getPath();
161
- }
162
- else {
163
- return undefined;
164
- }
156
+ return files.length > 0;
165
157
  });
166
158
  }
167
159
  /**
@@ -189,7 +181,7 @@ class FlpSandbox {
189
181
  .contentType('text/javascript')
190
182
  .send((0, fs_1.readFileSync)((0, path_1.join)(__dirname, '../../templates/flp/workspaceConnector.js'), 'utf-8'));
191
183
  });
192
- const api = '/preview/api/changes';
184
+ const api = `${PREVIEW_URL.api}/changes`;
193
185
  this.router.use(api, (0, express_1.json)());
194
186
  this.router.get(api, ((_req, res) => __awaiter(this, void 0, void 0, function* () {
195
187
  res.status(200)
@@ -0,0 +1,216 @@
1
+ "use strict";
2
+
3
+ sap.ui.define(["sap/base/Log", "sap/ui/core/IconPool", "sap/base/i18n/ResourceBundle", "sap/base/util/UriParameters"], function (Log, IconPool, ResourceBundle, UriParameters) {
4
+ "use strict";
5
+
6
+ /**
7
+ * Fetch the manifest from the given application urls, then parse them for custom libs, and finally request their urls.
8
+ *
9
+ * @param appUrls application urls
10
+ * @returns returns a promise when the registration is completed.
11
+ */
12
+ const registerComponentDependencyPaths = function (appUrls) {
13
+ try {
14
+ return Promise.resolve(getManifestLibs(appUrls)).then(function (libs) {
15
+ const _temp = function () {
16
+ if (libs && libs.length > 0) {
17
+ let url = '/sap/bc/ui2/app_index/ui5_app_info?id=' + libs;
18
+ const sapClient = UriParameters.fromQuery(window.location.search).get('sap-client');
19
+ if (sapClient && sapClient.length === 3) {
20
+ url = url + '&sap-client=' + sapClient;
21
+ }
22
+ return Promise.resolve(fetch(url)).then(function (response) {
23
+ return Promise.resolve(response.json()).then(function (_response$json) {
24
+ registerModules(_response$json);
25
+ });
26
+ });
27
+ }
28
+ }();
29
+ if (_temp && _temp.then) return _temp.then(function () {});
30
+ });
31
+ } catch (e) {
32
+ return Promise.reject(e);
33
+ }
34
+ };
35
+ /**
36
+ * Register SAP fonts that are also registered in a productive Fiori launchpad.
37
+ */
38
+ /**
39
+ * SAPUI5 delivered namespaces from https://ui5.sap.com/#/api/sap
40
+ */
41
+ const UI5_LIBS = ['sap.apf', 'sap.base', 'sap.chart', 'sap.collaboration', 'sap.f', 'sap.fe', 'sap.fileviewer', 'sap.gantt', 'sap.landvisz', 'sap.m', 'sap.ndc', 'sap.ovp', 'sap.rules', 'sap.suite', 'sap.tnt', 'sap.ui', 'sap.uiext', 'sap.ushell', 'sap.uxap', 'sap.viz', 'sap.webanalytics', 'sap.zen'];
42
+
43
+ /**
44
+ * Check whether a specific dependency is a custom library, and if yes, add it to the map.
45
+ *
46
+ * @param dependency dependency from the manifest
47
+ * @param customLibs map containing the required custom libraries
48
+ */
49
+ function addKeys(dependency, customLibs) {
50
+ Object.keys(dependency).forEach(function (key) {
51
+ // ignore libs or Components that start with SAPUI5 delivered namespaces
52
+ if (!UI5_LIBS.some(function (substring) {
53
+ return key === substring || key.startsWith(substring + '.');
54
+ })) {
55
+ customLibs[key] = true;
56
+ }
57
+ });
58
+ }
59
+
60
+ /**
61
+ * Fetch the manifest for all the given application urls and generate a string containing all required custom library ids.
62
+ *
63
+ * @param appUrls urls pointing to included applications
64
+ * @returns Promise of a comma separated list of all required libraries.
65
+ */
66
+ function getManifestLibs(appUrls) {
67
+ const result = {};
68
+ const promises = [];
69
+ for (const url of appUrls) {
70
+ promises.push(fetch(`${url}/manifest.json`).then(function (resp) {
71
+ try {
72
+ return Promise.resolve(resp.json()).then(function (manifest) {
73
+ if (manifest) {
74
+ if (manifest['sap.ui5'] && manifest['sap.ui5'].dependencies) {
75
+ if (manifest['sap.ui5'].dependencies.libs) {
76
+ addKeys(manifest['sap.ui5'].dependencies.libs, result);
77
+ }
78
+ if (manifest['sap.ui5'].dependencies.components) {
79
+ addKeys(manifest['sap.ui5'].dependencies.components, result);
80
+ }
81
+ }
82
+ if (manifest['sap.ui5'] && manifest['sap.ui5'].componentUsages) {
83
+ addKeys(manifest['sap.ui5'].componentUsages, result);
84
+ }
85
+ }
86
+ });
87
+ } catch (e) {
88
+ return Promise.reject(e);
89
+ }
90
+ }));
91
+ }
92
+ return Promise.all(promises).then(() => Object.keys(result).join(','));
93
+ }
94
+
95
+ /**
96
+ * Register the custom libraries and their url with the UI5 loader.
97
+ *
98
+ * @param dataFromAppIndex data returned from the app index service
99
+ */
100
+ function registerModules(dataFromAppIndex) {
101
+ Object.keys(dataFromAppIndex).forEach(function (moduleDefinitionKey) {
102
+ const moduleDefinition = dataFromAppIndex[moduleDefinitionKey];
103
+ if (moduleDefinition && moduleDefinition.dependencies) {
104
+ moduleDefinition.dependencies.forEach(function (dependency) {
105
+ if (dependency.url && dependency.url.length > 0 && dependency.type === 'UI5LIB') {
106
+ Log.info('Registering Library ' + dependency.componentId + ' from server ' + dependency.url);
107
+ const compId = dependency.componentId.replace(/\./g, '/');
108
+ const config = {
109
+ paths: {}
110
+ };
111
+ config.paths[compId] = dependency.url;
112
+ sap.ui.loader.config(config);
113
+ }
114
+ });
115
+ }
116
+ });
117
+ }
118
+ function registerSAPFonts() {
119
+ //Fiori Theme font family and URI
120
+ const fioriTheme = {
121
+ fontFamily: 'SAP-icons-TNT',
122
+ fontURI: sap.ui.require.toUrl('sap/tnt/themes/base/fonts/')
123
+ };
124
+ //Registering to the icon pool
125
+ IconPool.registerFont(fioriTheme);
126
+ //SAP Business Suite Theme font family and URI
127
+ const bSuiteTheme = {
128
+ fontFamily: 'BusinessSuiteInAppSymbols',
129
+ fontURI: sap.ui.require.toUrl('sap/ushell/themes/base/fonts/')
130
+ };
131
+ //Registering to the icon pool
132
+ IconPool.registerFont(bSuiteTheme);
133
+ }
134
+
135
+ /**
136
+ * Read the application title from the resource bundle and set it as document title.
137
+ */
138
+ function setI18nTitle() {
139
+ const locale = sap.ui.getCore().getConfiguration().getLanguage();
140
+ const resourceBundle = ResourceBundle.create({
141
+ url: 'i18n/i18n.properties',
142
+ locale
143
+ });
144
+ document.title = resourceBundle.getText('appTitle') ?? document.title;
145
+ }
146
+
147
+ /**
148
+ * Apply additional configuration.
149
+ *
150
+ * @param params init parameters read from the script tag
151
+ * @param params.appUrls JSON containing a string array of application urls
152
+ * @param params.flex JSON containing the flex configuration
153
+ * @returns promise
154
+ */
155
+ function configure(_ref) {
156
+ let {
157
+ appUrls,
158
+ flex
159
+ } = _ref;
160
+ // Register RTA if configured
161
+ if (flex) {
162
+ sap.ushell.Container.attachRendererCreatedEvent(function () {
163
+ try {
164
+ return Promise.resolve(sap.ushell.Container.getServiceAsync('AppLifeCycle')).then(function (serviceInstance) {
165
+ serviceInstance.attachAppLoaded(event => {
166
+ const oView = event.getParameter('componentInstance');
167
+ sap.ui.require(['sap/ui/rta/api/startAdaptation'], function (startAdaptation) {
168
+ const options = {
169
+ rootControl: oView,
170
+ validateAppVersion: false,
171
+ flexSettings: JSON.parse(flex)
172
+ };
173
+ startAdaptation(options);
174
+ });
175
+ });
176
+ });
177
+ } catch (e) {
178
+ return Promise.reject(e);
179
+ }
180
+ });
181
+ }
182
+
183
+ // Load custom library paths if configured
184
+ if (appUrls) {
185
+ return registerComponentDependencyPaths(JSON.parse(appUrls));
186
+ } else {
187
+ return Promise.resolve();
188
+ }
189
+ }
190
+
191
+ /**
192
+ * Initialize the FLP sandbox.
193
+ */
194
+ function init() {
195
+ setI18nTitle();
196
+ registerSAPFonts();
197
+ sap.ushell.Container.createRenderer().placeAt('content');
198
+ }
199
+ const bootstrapConfig = document.getElementById('sap-ui-bootstrap');
200
+ if (bootstrapConfig) {
201
+ configure({
202
+ appUrls: bootstrapConfig.getAttribute('data-open-ux-preview-libs-manifests'),
203
+ flex: bootstrapConfig.getAttribute('data-open-ux-preview-flex-settings')
204
+ }).then(init).catch(() => Log.error('Sandbox initialization failed.'));
205
+ }
206
+ var __exports = {
207
+ __esModule: true
208
+ };
209
+ __exports.registerComponentDependencyPaths = registerComponentDependencyPaths;
210
+ __exports.registerSAPFonts = registerSAPFonts;
211
+ __exports.setI18nTitle = setI18nTitle;
212
+ __exports.configure = configure;
213
+ __exports.init = init;
214
+ return __exports;
215
+ });
216
+ //# sourceMappingURL=init.js.map
@@ -0,0 +1,224 @@
1
+ import Log from 'sap/base/Log';
2
+ import type AppLifeCycle from 'sap/ushell/services/AppLifeCycle';
3
+ import IconPool from 'sap/ui/core/IconPool';
4
+ import ResourceBundle from 'sap/base/i18n/ResourceBundle';
5
+ import UriParameters from 'sap/base/util/UriParameters';
6
+
7
+ /**
8
+ * SAPUI5 delivered namespaces from https://ui5.sap.com/#/api/sap
9
+ */
10
+ const UI5_LIBS = [
11
+ 'sap.apf',
12
+ 'sap.base',
13
+ 'sap.chart',
14
+ 'sap.collaboration',
15
+ 'sap.f',
16
+ 'sap.fe',
17
+ 'sap.fileviewer',
18
+ 'sap.gantt',
19
+ 'sap.landvisz',
20
+ 'sap.m',
21
+ 'sap.ndc',
22
+ 'sap.ovp',
23
+ 'sap.rules',
24
+ 'sap.suite',
25
+ 'sap.tnt',
26
+ 'sap.ui',
27
+ 'sap.uiext',
28
+ 'sap.ushell',
29
+ 'sap.uxap',
30
+ 'sap.viz',
31
+ 'sap.webanalytics',
32
+ 'sap.zen'
33
+ ];
34
+
35
+ /**
36
+ * Check whether a specific dependency is a custom library, and if yes, add it to the map.
37
+ *
38
+ * @param dependency dependency from the manifest
39
+ * @param customLibs map containing the required custom libraries
40
+ */
41
+ function addKeys(dependency: Record<string, unknown>, customLibs: Record<string, true>): void {
42
+ Object.keys(dependency).forEach(function (key) {
43
+ // ignore libs or Components that start with SAPUI5 delivered namespaces
44
+ if (
45
+ !UI5_LIBS.some(function (substring) {
46
+ return key === substring || key.startsWith(substring + '.');
47
+ })
48
+ ) {
49
+ customLibs[key] = true;
50
+ }
51
+ });
52
+ }
53
+
54
+ /**
55
+ * Fetch the manifest for all the given application urls and generate a string containing all required custom library ids.
56
+ *
57
+ * @param appUrls urls pointing to included applications
58
+ * @returns Promise of a comma separated list of all required libraries.
59
+ */
60
+ function getManifestLibs(appUrls: string[]): Promise<string> {
61
+ const result = {} as Record<string, true>;
62
+ const promises = [];
63
+ for (const url of appUrls) {
64
+ promises.push(
65
+ fetch(`${url}/manifest.json`).then(async (resp) => {
66
+ const manifest = await resp.json();
67
+ if (manifest) {
68
+ if (manifest['sap.ui5'] && manifest['sap.ui5'].dependencies) {
69
+ if (manifest['sap.ui5'].dependencies.libs) {
70
+ addKeys(manifest['sap.ui5'].dependencies.libs, result);
71
+ }
72
+ if (manifest['sap.ui5'].dependencies.components) {
73
+ addKeys(manifest['sap.ui5'].dependencies.components, result);
74
+ }
75
+ }
76
+ if (manifest['sap.ui5'] && manifest['sap.ui5'].componentUsages) {
77
+ addKeys(manifest['sap.ui5'].componentUsages, result);
78
+ }
79
+ }
80
+ })
81
+ );
82
+ }
83
+ return Promise.all(promises).then(() => Object.keys(result).join(','));
84
+ }
85
+
86
+ /**
87
+ * Register the custom libraries and their url with the UI5 loader.
88
+ *
89
+ * @param dataFromAppIndex data returned from the app index service
90
+ */
91
+ function registerModules(
92
+ dataFromAppIndex: Record<
93
+ string,
94
+ {
95
+ dependencies?: {
96
+ url?: string;
97
+ type?: string;
98
+ componentId: string;
99
+ }[];
100
+ }
101
+ >
102
+ ) {
103
+ Object.keys(dataFromAppIndex).forEach(function (moduleDefinitionKey) {
104
+ const moduleDefinition = dataFromAppIndex[moduleDefinitionKey];
105
+ if (moduleDefinition && moduleDefinition.dependencies) {
106
+ moduleDefinition.dependencies.forEach(function (dependency) {
107
+ if (dependency.url && dependency.url.length > 0 && dependency.type === 'UI5LIB') {
108
+ Log.info('Registering Library ' + dependency.componentId + ' from server ' + dependency.url);
109
+ const compId = dependency.componentId.replace(/\./g, '/');
110
+ const config = {
111
+ paths: {} as Record<string, string>
112
+ };
113
+ config.paths[compId] = dependency.url;
114
+ sap.ui.loader.config(config);
115
+ }
116
+ });
117
+ }
118
+ });
119
+ }
120
+
121
+ /**
122
+ * Fetch the manifest from the given application urls, then parse them for custom libs, and finally request their urls.
123
+ *
124
+ * @param appUrls application urls
125
+ * @returns returns a promise when the registration is completed.
126
+ */
127
+ export async function registerComponentDependencyPaths(appUrls: string[]): Promise<void> {
128
+ const libs = await getManifestLibs(appUrls);
129
+ if (libs && libs.length > 0) {
130
+ let url = '/sap/bc/ui2/app_index/ui5_app_info?id=' + libs;
131
+ const sapClient = UriParameters.fromQuery(window.location.search).get('sap-client');
132
+ if (sapClient && sapClient.length === 3) {
133
+ url = url + '&sap-client=' + sapClient;
134
+ }
135
+ const response = await fetch(url);
136
+ registerModules(await response.json());
137
+ }
138
+ }
139
+
140
+ /**
141
+ * Register SAP fonts that are also registered in a productive Fiori launchpad.
142
+ */
143
+ export function registerSAPFonts() {
144
+ //Fiori Theme font family and URI
145
+ const fioriTheme = {
146
+ fontFamily: 'SAP-icons-TNT',
147
+ fontURI: sap.ui.require.toUrl('sap/tnt/themes/base/fonts/')
148
+ };
149
+ //Registering to the icon pool
150
+ IconPool.registerFont(fioriTheme);
151
+ //SAP Business Suite Theme font family and URI
152
+ const bSuiteTheme = {
153
+ fontFamily: 'BusinessSuiteInAppSymbols',
154
+ fontURI: sap.ui.require.toUrl('sap/ushell/themes/base/fonts/')
155
+ };
156
+ //Registering to the icon pool
157
+ IconPool.registerFont(bSuiteTheme);
158
+ }
159
+
160
+ /**
161
+ * Read the application title from the resource bundle and set it as document title.
162
+ */
163
+ export function setI18nTitle() {
164
+ const locale = sap.ui.getCore().getConfiguration().getLanguage();
165
+ const resourceBundle = ResourceBundle.create({
166
+ url: 'i18n/i18n.properties',
167
+ locale
168
+ }) as ResourceBundle;
169
+ document.title = resourceBundle.getText('appTitle') ?? document.title;
170
+ }
171
+
172
+ /**
173
+ * Apply additional configuration.
174
+ *
175
+ * @param params init parameters read from the script tag
176
+ * @param params.appUrls JSON containing a string array of application urls
177
+ * @param params.flex JSON containing the flex configuration
178
+ * @returns promise
179
+ */
180
+ export function configure({ appUrls, flex }: { appUrls?: string | null; flex?: string | null }): Promise<void> {
181
+ // Register RTA if configured
182
+ if (flex) {
183
+ sap.ushell.Container.attachRendererCreatedEvent(async function () {
184
+ const serviceInstance = await sap.ushell.Container.getServiceAsync<AppLifeCycle>('AppLifeCycle');
185
+ serviceInstance.attachAppLoaded(event => {
186
+ const oView = event.getParameter('componentInstance');
187
+ sap.ui.require(['sap/ui/rta/api/startAdaptation'], function (startAdaptation: (opts: object) => void) {
188
+ const options = {
189
+ rootControl: oView,
190
+ validateAppVersion: false,
191
+ flexSettings: JSON.parse(flex)
192
+ };
193
+ startAdaptation(options);
194
+ });
195
+ });
196
+ });
197
+ }
198
+
199
+ // Load custom library paths if configured
200
+ if (appUrls) {
201
+ return registerComponentDependencyPaths(JSON.parse(appUrls));
202
+ } else {
203
+ return Promise.resolve();
204
+ }
205
+ }
206
+
207
+ /**
208
+ * Initialize the FLP sandbox.
209
+ */
210
+ export function init() {
211
+ setI18nTitle();
212
+ registerSAPFonts();
213
+ sap.ushell.Container.createRenderer().placeAt('content');
214
+ }
215
+
216
+ const bootstrapConfig = document.getElementById('sap-ui-bootstrap');
217
+ if (bootstrapConfig) {
218
+ configure({
219
+ appUrls: bootstrapConfig.getAttribute('data-open-ux-preview-libs-manifests'),
220
+ flex: bootstrapConfig.getAttribute('data-open-ux-preview-flex-settings')
221
+ })
222
+ .then(init)
223
+ .catch(() => Log.error('Sandbox initialization failed.'));
224
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "sap.app": {
3
+ "id": "open.ux.preview.client"
4
+ }
5
+ }
package/package.json CHANGED
@@ -9,7 +9,7 @@
9
9
  "bugs": {
10
10
  "url": "https://github.com/SAP/open-ux-tools/issues?q=is%3Aopen+is%3Aissue+label%3Abug+label%3Apreview-middleware"
11
11
  },
12
- "version": "0.4.2",
12
+ "version": "0.4.4",
13
13
  "license": "Apache-2.0",
14
14
  "author": "@SAP/ux-tools-team",
15
15
  "main": "dist/index.js",
@@ -31,11 +31,14 @@
31
31
  "@types/express": "4.17.17",
32
32
  "@types/prompts": "2.0.14",
33
33
  "@types/supertest": "2.0.12",
34
+ "copyfiles": "2.4.1",
35
+ "npm-run-all": "4.1.5",
34
36
  "express": "4.18.2",
35
37
  "nock": "13.2.1",
36
38
  "supertest": "6.3.3",
37
- "@sap-ux/store": "0.3.12",
38
- "@sap-ux/project-access": "1.13.2"
39
+ "@private/preview-middleware-client": "npm:@sap-ux-private/preview-middleware-client@0.1.0",
40
+ "@sap-ux/project-access": "1.13.2",
41
+ "@sap-ux/store": "0.3.12"
39
42
  },
40
43
  "peerDependencies": {
41
44
  "express": "4"
@@ -46,7 +49,9 @@
46
49
  },
47
50
  "scripts": {
48
51
  "start:fixture": "ui5 serve --config test/fixtures/simple-app/ui5.yaml",
49
- "build": "tsc --build",
52
+ "build": "npm-run-all -l -p build:middleware build:client",
53
+ "build:middleware": "tsc --build",
54
+ "build:client": "copyfiles --exclude **/*-dbg.js --up 4 \"./node_modules/@private/preview-middleware-client/dist/**/*\" \"./dist/client\"",
50
55
  "watch": "tsc --watch",
51
56
  "clean": "rimraf dist test/test-output coverage *.tsbuildinfo",
52
57
  "format": "prettier --write '**/*.{js,json,ts,yaml,yml}' --ignore-path ../../.prettierignore",
@@ -53,33 +53,10 @@
53
53
  data-sap-ui-flexibilityServices='<%- JSON.stringify(ui5.flex) %>'
54
54
  data-sap-ui-resourceroots='<%- JSON.stringify(ui5.resources) %>'
55
55
  data-sap-ui-frameOptions="allow"
56
- data-sap-ui-xx-componentPreload="off">
57
- </script><% if (locals.locateReuseLibsScript) { %>
58
- <script id="locate-reuse-libs" src="<%- locateReuseLibsScript %>" data-sap-ui-manifest-uri="/manifest.json">
59
- </script><% } %>
60
- <script>
61
- sap.ui.getCore().attachInit(function () {<% if (locals.flex) { %>
62
- sap.ushell.Container.attachRendererCreatedEvent(function () {
63
- sap.ushell.Container.getServiceAsync('AppLifeCycle').then((serviceInstance) => {
64
- serviceInstance.attachAppLoaded((event) => {
65
- var oView = event.getParameter('componentInstance');
66
- sap.ui.require(["sap/ui/rta/api/startAdaptation"], async function (startAdaptation) {
67
- var options = {
68
- rootControl: oView,
69
- validateAppVersion: false,
70
- flexSettings: {
71
- layer: '<%- flex.layer %>',
72
- developerMode: <%- flex.developerMode %>
73
- }
74
- };
75
- await startAdaptation(options);
76
- });
77
- })
78
- });
79
- });<% } %>
80
- <% if (!locals.locateReuseLibsScript) { %>
81
- sap.ushell.Container.createRenderer().placeAt("content");<% } %>
82
- });
56
+ data-sap-ui-xx-componentPreload="off"
57
+ data-sap-ui-oninit="module:open/ux/preview/client/flp/init"<% if (locals.flex) { %>
58
+ data-open-ux-preview-flex-settings='<%- JSON.stringify(flex) %>'<% } if (locals.locateReuseLibsScript) { %>
59
+ data-open-ux-preview-libs-manifests='<%- JSON.stringify(Object.values(apps).map(app => app.url)) %>'<% } %>>
83
60
  </script>
84
61
  </head>
85
62
 
@@ -1,217 +0,0 @@
1
- (function (sap) {
2
- var fioriToolsGetManifestLibs = function (manifestPath) {
3
- var url = manifestPath;
4
- var result = "";
5
- // SAPUI5 delivered namespaces from https://ui5.sap.com/#/api/sap
6
- var ui5Libs = [
7
- "sap.apf",
8
- "sap.base",
9
- "sap.chart",
10
- "sap.collaboration",
11
- "sap.f",
12
- "sap.fe",
13
- "sap.fileviewer",
14
- "sap.gantt",
15
- "sap.landvisz",
16
- "sap.m",
17
- "sap.ndc",
18
- "sap.ovp",
19
- "sap.rules",
20
- "sap.suite",
21
- "sap.tnt",
22
- "sap.ui",
23
- "sap.uiext",
24
- "sap.ushell",
25
- "sap.uxap",
26
- "sap.viz",
27
- "sap.webanalytics",
28
- "sap.zen"
29
- ];
30
- function getKeys(libOrComp, libOrCompKeysString) {
31
- var libOrCompKeysStringTmp = libOrCompKeysString;
32
- Object.keys(libOrComp).forEach(function (libOrCompKey) {
33
- // ignore libs or Components that start with SAPUI5 delivered namespaces
34
- if (!ui5Libs.some(function (substring) { return libOrCompKey === substring || libOrCompKey.startsWith(substring + "."); })) {
35
- if (libOrCompKeysStringTmp.length > 0) {
36
- libOrCompKeysStringTmp = libOrCompKeysStringTmp + "," + libOrCompKey;
37
- } else {
38
- libOrCompKeysStringTmp = libOrCompKey;
39
- }
40
- }
41
- });
42
- return libOrCompKeysStringTmp;
43
- }
44
- return new Promise(function (resolve, reject) {
45
- $.ajax(url)
46
- .done(function (manifest) {
47
- if (manifest) {
48
- if (
49
- manifest["sap.ui5"] &&
50
- manifest["sap.ui5"].dependencies
51
- ) {
52
- if (manifest["sap.ui5"].dependencies.libs) {
53
- result = getKeys(manifest["sap.ui5"].dependencies.libs, result);
54
- }
55
- if (manifest["sap.ui5"].dependencies.components) {
56
- result = getKeys(manifest["sap.ui5"].dependencies.components, result);
57
- }
58
- }
59
- if (
60
- manifest["sap.ui5"] &&
61
- manifest["sap.ui5"].componentUsages
62
- ) {
63
- result = getKeys(manifest["sap.ui5"].componentUsages, result);
64
- }
65
- }
66
- resolve(result);
67
- })
68
- .fail(function () {
69
- reject(new Error("Could not fetch manifest at '" + manifestPath));
70
- });
71
- });
72
- };
73
- function registerModules(dataFromAppIndex) {
74
- Object.keys(dataFromAppIndex).forEach(function (moduleDefinitionKey) {
75
- var moduleDefinition = dataFromAppIndex[moduleDefinitionKey];
76
- if (moduleDefinition && moduleDefinition.dependencies) {
77
- moduleDefinition.dependencies.forEach(function (dependency) {
78
- if (dependency.url && dependency.url.length > 0 && dependency.type === "UI5LIB") {
79
- sap.ui.require(["sap/base/Log"], function (Log) {
80
- Log.info("Registering Library " +
81
- dependency.componentId +
82
- " from server " +
83
- dependency.url);
84
- });
85
- var compId = dependency.componentId.replace(/\./g, "/");
86
- var config = {
87
- paths: {
88
- }
89
- };
90
- config.paths[compId] = dependency.url;
91
- sap.ui.loader.config(config);
92
- }
93
- });
94
- }
95
- });
96
- }
97
- /**
98
- * Registers the module paths for dependencies of the given component.
99
- * @param {string} manifestPath The the path to the app manifest path
100
- * for which the dependencies should be registered.
101
- * @returns {Promise} A promise which is resolved when the ajax request for
102
- * the app-index was successful and the module paths were registered.
103
- */
104
- sap.registerComponentDependencyPaths = function (manifestPath) {
105
-
106
- return fioriToolsGetManifestLibs(manifestPath).then(function (libs) {
107
- if (libs && libs.length > 0) {
108
- var url = "/sap/bc/ui2/app_index/ui5_app_info?id=" + libs;
109
- var sapClient = "";
110
-
111
- return new Promise(
112
- function (resolve) {
113
- sap.ui.require(["sap/base/util/UriParameters"], function (UriParameters) {
114
- sapClient = UriParameters.fromQuery(window.location.search).get("sap-client");
115
- if (sapClient && sapClient.length === 3) {
116
- url = url + "&sap-client=" + sapClient;
117
- }
118
- resolve(url);
119
- });
120
- }).then(function (url2) {
121
- return $.ajax(url2).done(function (data) {
122
- if (data) {
123
- registerModules(data);
124
- }
125
- });
126
- });
127
- } else {
128
- return undefined;
129
- }
130
- });
131
- };
132
- })(sap);
133
-
134
- function registerSAPFonts() {
135
- sap.ui.require(["sap/ui/core/IconPool"], function (IconPool) {
136
- //Fiori Theme font family and URI
137
- var fioriTheme = {
138
- fontFamily: "SAP-icons-TNT",
139
- fontURI: sap.ui.require.toUrl("sap/tnt/themes/base/fonts/")
140
- };
141
- //Registering to the icon pool
142
- IconPool.registerFont(fioriTheme);
143
- //SAP Business Suite Theme font family and URI
144
- var bSuiteTheme = {
145
- fontFamily: "BusinessSuiteInAppSymbols",
146
- fontURI: sap.ui.require.toUrl("sap/ushell/themes/base/fonts/")
147
- };
148
- //Registering to the icon pool
149
- IconPool.registerFont(bSuiteTheme);
150
- });
151
- }
152
-
153
- /*eslint-disable fiori-custom/sap-browser-api-warning, fiori-custom/sap-no-dom-access*/
154
- var currentScript = document.getElementById("locate-reuse-libs");
155
- if (!currentScript) {
156
- currentScript = document.currentScript;
157
- }
158
- var manifestUri = currentScript.getAttribute("data-sap-ui-manifest-uri");
159
- var componentName = currentScript.getAttribute("data-sap-ui-componentName");
160
- var useMockserver = currentScript.getAttribute("data-sap-ui-use-mockserver");
161
-
162
- sap.registerComponentDependencyPaths(manifestUri)
163
- .catch(function (error) {
164
- sap.ui.require(["sap/base/Log"], function (Log) {
165
- Log.error(error);
166
- });
167
- })
168
- .finally(function () {
169
-
170
- // setting the app title with internationalization
171
- sap.ui.getCore().attachInit(function () {
172
- var sLocale = sap.ui.getCore().getConfiguration().getLanguage();
173
- sap.ui.require(["sap/base/i18n/ResourceBundle"], function (ResourceBundle) {
174
- var oResourceBundle = ResourceBundle.create({
175
- url: "i18n/i18n.properties",
176
- locale: sLocale
177
- });
178
- document.title = oResourceBundle.getText("appTitle");
179
- });
180
- });
181
-
182
- if (componentName && componentName.length > 0) {
183
- if (useMockserver && useMockserver === "true") {
184
- sap.ui.getCore().attachInit(function () {
185
- registerSAPFonts();
186
- sap.ui.require([componentName.replace(/\./g, "/") + "/localService/mockserver"], function (server) {
187
- // set up test service for local testing
188
- server.init();
189
- // initialize the ushell sandbox component
190
- sap.ushell.Container.createRenderer().placeAt("content");
191
- });
192
- });
193
- } else {
194
- // Requiring the ComponentSupport module automatically executes the component initialisation for all declaratively defined components
195
- sap.ui.require(["sap/ui/core/ComponentSupport"]);
196
-
197
- // setting the app title with the i18n text
198
- sap.ui.getCore().attachInit(function () {
199
- registerSAPFonts();
200
- var sLocale = sap.ui.getCore().getConfiguration().getLanguage();
201
- sap.ui.require(["sap/base/i18n/ResourceBundle"], function (ResourceBundle) {
202
- var oResourceBundle = ResourceBundle.create({
203
- url: "i18n/i18n.properties",
204
- locale: sLocale
205
- });
206
- document.title = oResourceBundle.getText("appTitle");
207
- });
208
- });
209
- }
210
- } else {
211
- sap.ui.getCore().attachInit(function () {
212
- registerSAPFonts();
213
- // initialize the ushell sandbox component
214
- sap.ushell.Container.createRenderer().placeAt("content");
215
- });
216
- }
217
- });