@sap-ux/preview-middleware 0.4.3 → 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 +1 -0
- package/dist/base/flp.js +15 -10
- package/dist/client/flp/init.js +216 -0
- package/dist/client/flp/init.ts +224 -0
- package/dist/client/manifest.json +5 -0
- package/package.json +9 -4
- package/templates/flp/sandbox.html +1 -1
- package/templates/flp/sandboxInit.js +0 -210
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 |
|
package/dist/base/flp.js
CHANGED
|
@@ -30,6 +30,17 @@ const DEFAULT_INTENT = {
|
|
|
30
30
|
object: 'app',
|
|
31
31
|
action: 'preview'
|
|
32
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
|
+
};
|
|
33
44
|
/**
|
|
34
45
|
* Class handling preview of a sandbox FLP.
|
|
35
46
|
*/
|
|
@@ -79,7 +90,7 @@ class FlpSandbox {
|
|
|
79
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(','),
|
|
80
91
|
theme: supportedThemes.includes(DEFAULT_THEME) ? DEFAULT_THEME : supportedThemes[0],
|
|
81
92
|
flex,
|
|
82
|
-
resources: Object.assign(Object.assign({}, resources), {
|
|
93
|
+
resources: Object.assign(Object.assign({}, resources), { [PREVIEW_URL.client.ns]: PREVIEW_URL.client.url })
|
|
83
94
|
},
|
|
84
95
|
locateReuseLibsScript: (_g = this.config.libs) !== null && _g !== void 0 ? _g : (yield this.hasLocateReuseLibsScript())
|
|
85
96
|
};
|
|
@@ -99,14 +110,8 @@ class FlpSandbox {
|
|
|
99
110
|
* Add routes for html and scripts required for a local FLP.
|
|
100
111
|
*/
|
|
101
112
|
addStandardRoutes() {
|
|
102
|
-
//
|
|
103
|
-
|
|
104
|
-
this.router.get(`${previewPath === '/' ? '' : previewPath}/sandboxInit.js`, (_req, res) => {
|
|
105
|
-
const initjs = (0, fs_1.readFileSync)((0, path_1.join)(__dirname, '../../templates/flp/sandboxInit.js'), 'utf-8');
|
|
106
|
-
res.writeHead(200, { 'Content-Type': 'text/javascript' });
|
|
107
|
-
res.write(initjs);
|
|
108
|
-
res.end();
|
|
109
|
-
});
|
|
113
|
+
// register static client sources
|
|
114
|
+
this.router.use(PREVIEW_URL.client.url, (0, express_1.static)(PREVIEW_URL.client.local));
|
|
110
115
|
// add route for the sandbox.html
|
|
111
116
|
this.router.get(this.config.path, ((req, res) => __awaiter(this, void 0, void 0, function* () {
|
|
112
117
|
var _a, _b;
|
|
@@ -176,7 +181,7 @@ class FlpSandbox {
|
|
|
176
181
|
.contentType('text/javascript')
|
|
177
182
|
.send((0, fs_1.readFileSync)((0, path_1.join)(__dirname, '../../templates/flp/workspaceConnector.js'), 'utf-8'));
|
|
178
183
|
});
|
|
179
|
-
const api =
|
|
184
|
+
const api = `${PREVIEW_URL.api}/changes`;
|
|
180
185
|
this.router.use(api, (0, express_1.json)());
|
|
181
186
|
this.router.get(api, ((_req, res) => __awaiter(this, void 0, void 0, function* () {
|
|
182
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
|
+
}
|
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.
|
|
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
|
-
"@
|
|
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": "
|
|
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",
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
data-sap-ui-resourceroots='<%- JSON.stringify(ui5.resources) %>'
|
|
55
55
|
data-sap-ui-frameOptions="allow"
|
|
56
56
|
data-sap-ui-xx-componentPreload="off"
|
|
57
|
-
data-sap-ui-oninit="module:open/ux/preview/
|
|
57
|
+
data-sap-ui-oninit="module:open/ux/preview/client/flp/init"<% if (locals.flex) { %>
|
|
58
58
|
data-open-ux-preview-flex-settings='<%- JSON.stringify(flex) %>'<% } if (locals.locateReuseLibsScript) { %>
|
|
59
59
|
data-open-ux-preview-libs-manifests='<%- JSON.stringify(Object.values(apps).map(app => app.url)) %>'<% } %>>
|
|
60
60
|
</script>
|
|
@@ -1,210 +0,0 @@
|
|
|
1
|
-
/* global sap document */
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Fetch the manifest for all the given application urls and generate a string containing all required custom library ids.
|
|
5
|
-
*
|
|
6
|
-
* @param {string[]} appUrls
|
|
7
|
-
* @returns {Promise<string>} Promise of a comma separated list of all required libraries.
|
|
8
|
-
*/
|
|
9
|
-
function fioriToolsGetManifestLibs(appUrls) {
|
|
10
|
-
var result = {};
|
|
11
|
-
// SAPUI5 delivered namespaces from https://ui5.sap.com/#/api/sap
|
|
12
|
-
var ui5Libs = [
|
|
13
|
-
"sap.apf",
|
|
14
|
-
"sap.base",
|
|
15
|
-
"sap.chart",
|
|
16
|
-
"sap.collaboration",
|
|
17
|
-
"sap.f",
|
|
18
|
-
"sap.fe",
|
|
19
|
-
"sap.fileviewer",
|
|
20
|
-
"sap.gantt",
|
|
21
|
-
"sap.landvisz",
|
|
22
|
-
"sap.m",
|
|
23
|
-
"sap.ndc",
|
|
24
|
-
"sap.ovp",
|
|
25
|
-
"sap.rules",
|
|
26
|
-
"sap.suite",
|
|
27
|
-
"sap.tnt",
|
|
28
|
-
"sap.ui",
|
|
29
|
-
"sap.uiext",
|
|
30
|
-
"sap.ushell",
|
|
31
|
-
"sap.uxap",
|
|
32
|
-
"sap.viz",
|
|
33
|
-
"sap.webanalytics",
|
|
34
|
-
"sap.zen"
|
|
35
|
-
];
|
|
36
|
-
function addKeys(libOrComp, reuseLibsObj) {
|
|
37
|
-
Object.keys(libOrComp).forEach(function (libOrCompKey) {
|
|
38
|
-
// ignore libs or Components that start with SAPUI5 delivered namespaces
|
|
39
|
-
if (!ui5Libs.some(function (substring) { return libOrCompKey === substring || libOrCompKey.startsWith(substring + "."); })) {
|
|
40
|
-
reuseLibsObj[libOrCompKey] = true;
|
|
41
|
-
}
|
|
42
|
-
});
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
var promises = [];
|
|
46
|
-
for (const url of appUrls) {
|
|
47
|
-
promises.push(new Promise(function (resolve, _reject) {
|
|
48
|
-
$.ajax(`${url}/manifest.json`)
|
|
49
|
-
.done(function (manifest) {
|
|
50
|
-
if (manifest) {
|
|
51
|
-
if (
|
|
52
|
-
manifest["sap.ui5"] &&
|
|
53
|
-
manifest["sap.ui5"].dependencies
|
|
54
|
-
) {
|
|
55
|
-
if (manifest["sap.ui5"].dependencies.libs) {
|
|
56
|
-
addKeys(manifest["sap.ui5"].dependencies.libs, result);
|
|
57
|
-
}
|
|
58
|
-
if (manifest["sap.ui5"].dependencies.components) {
|
|
59
|
-
addKeys(manifest["sap.ui5"].dependencies.components, result);
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
if (
|
|
63
|
-
manifest["sap.ui5"] &&
|
|
64
|
-
manifest["sap.ui5"].componentUsages
|
|
65
|
-
) {
|
|
66
|
-
addKeys(manifest["sap.ui5"].componentUsages, result);
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
resolve();
|
|
70
|
-
})
|
|
71
|
-
})
|
|
72
|
-
);
|
|
73
|
-
}
|
|
74
|
-
return Promise.all(promises).then(() => Object.keys(result).join(','));
|
|
75
|
-
};
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* Register the custom libraries and their url with the UI5 loader.
|
|
79
|
-
* @param {*} dataFromAppIndex
|
|
80
|
-
*/
|
|
81
|
-
function registerModules(dataFromAppIndex) {
|
|
82
|
-
Object.keys(dataFromAppIndex).forEach(function (moduleDefinitionKey) {
|
|
83
|
-
var moduleDefinition = dataFromAppIndex[moduleDefinitionKey];
|
|
84
|
-
if (moduleDefinition && moduleDefinition.dependencies) {
|
|
85
|
-
moduleDefinition.dependencies.forEach(function (dependency) {
|
|
86
|
-
if (dependency.url && dependency.url.length > 0 && dependency.type === "UI5LIB") {
|
|
87
|
-
sap.ui.require(["sap/base/Log"], function (Log) {
|
|
88
|
-
Log.info("Registering Library " +
|
|
89
|
-
dependency.componentId +
|
|
90
|
-
" from server " +
|
|
91
|
-
dependency.url);
|
|
92
|
-
});
|
|
93
|
-
var compId = dependency.componentId.replace(/\./g, "/");
|
|
94
|
-
var config = {
|
|
95
|
-
paths: {
|
|
96
|
-
}
|
|
97
|
-
};
|
|
98
|
-
config.paths[compId] = dependency.url;
|
|
99
|
-
sap.ui.loader.config(config);
|
|
100
|
-
}
|
|
101
|
-
});
|
|
102
|
-
}
|
|
103
|
-
});
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
/**
|
|
107
|
-
* Fetch the manifest from the given application urls, then parse them for custom libs, and finally request their urls.
|
|
108
|
-
*
|
|
109
|
-
* @param {string[]} appUrls application urls
|
|
110
|
-
* @returns {Promise<void>} returns a promise when the registration is completed.
|
|
111
|
-
*/
|
|
112
|
-
function registerComponentDependencyPaths(appUrls) {
|
|
113
|
-
return fioriToolsGetManifestLibs(appUrls).then(function (libs) {
|
|
114
|
-
if (libs && libs.length > 0) {
|
|
115
|
-
var url = "/sap/bc/ui2/app_index/ui5_app_info?id=" + libs;
|
|
116
|
-
var sapClient = "";
|
|
117
|
-
|
|
118
|
-
return new Promise(
|
|
119
|
-
function (resolve) {
|
|
120
|
-
sap.ui.require(["sap/base/util/UriParameters"], function (UriParameters) {
|
|
121
|
-
sapClient = UriParameters.fromQuery(window.location.search).get("sap-client");
|
|
122
|
-
if (sapClient && sapClient.length === 3) {
|
|
123
|
-
url = url + "&sap-client=" + sapClient;
|
|
124
|
-
}
|
|
125
|
-
resolve(url);
|
|
126
|
-
});
|
|
127
|
-
}).then(function (url2) {
|
|
128
|
-
return $.ajax(url2).done(function (data) {
|
|
129
|
-
if (data) {
|
|
130
|
-
registerModules(data);
|
|
131
|
-
}
|
|
132
|
-
});
|
|
133
|
-
});
|
|
134
|
-
} else {
|
|
135
|
-
return undefined;
|
|
136
|
-
}
|
|
137
|
-
});
|
|
138
|
-
};
|
|
139
|
-
|
|
140
|
-
/**
|
|
141
|
-
* Reigster SAP fonts that are also registered in a productive Fiori launchpad
|
|
142
|
-
*/
|
|
143
|
-
function registerSAPFonts() {
|
|
144
|
-
sap.ui.require(["sap/ui/core/IconPool"], function (IconPool) {
|
|
145
|
-
//Fiori Theme font family and URI
|
|
146
|
-
var fioriTheme = {
|
|
147
|
-
fontFamily: "SAP-icons-TNT",
|
|
148
|
-
fontURI: sap.ui.require.toUrl("sap/tnt/themes/base/fonts/")
|
|
149
|
-
};
|
|
150
|
-
//Registering to the icon pool
|
|
151
|
-
IconPool.registerFont(fioriTheme);
|
|
152
|
-
//SAP Business Suite Theme font family and URI
|
|
153
|
-
var bSuiteTheme = {
|
|
154
|
-
fontFamily: "BusinessSuiteInAppSymbols",
|
|
155
|
-
fontURI: sap.ui.require.toUrl("sap/ushell/themes/base/fonts/")
|
|
156
|
-
};
|
|
157
|
-
//Registering to the icon pool
|
|
158
|
-
IconPool.registerFont(bSuiteTheme);
|
|
159
|
-
});
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
/**
|
|
163
|
-
* Read the application title from the resource bundle and set it as document title.
|
|
164
|
-
*/
|
|
165
|
-
function setI18nTitle() {
|
|
166
|
-
var sLocale = sap.ui.getCore().getConfiguration().getLanguage();
|
|
167
|
-
sap.ui.require(["sap/base/i18n/ResourceBundle"], function (ResourceBundle) {
|
|
168
|
-
var oResourceBundle = ResourceBundle.create({
|
|
169
|
-
url: "i18n/i18n.properties",
|
|
170
|
-
locale: sLocale
|
|
171
|
-
});
|
|
172
|
-
document.title = oResourceBundle.getText("appTitle");
|
|
173
|
-
});
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
// Register RTA if configured
|
|
177
|
-
var bootstrapConfig = document.getElementById("sap-ui-bootstrap");
|
|
178
|
-
var flex = bootstrapConfig.getAttribute("data-open-ux-preview-flex-settings");
|
|
179
|
-
if (flex) {
|
|
180
|
-
sap.ushell.Container.attachRendererCreatedEvent(function () {
|
|
181
|
-
sap.ushell.Container.getServiceAsync('AppLifeCycle').then((serviceInstance) => {
|
|
182
|
-
serviceInstance.attachAppLoaded((event) => {
|
|
183
|
-
var oView = event.getParameter('componentInstance');
|
|
184
|
-
sap.ui.require(["sap/ui/rta/api/startAdaptation"], function (startAdaptation) {
|
|
185
|
-
var options = {
|
|
186
|
-
rootControl: oView,
|
|
187
|
-
validateAppVersion: false,
|
|
188
|
-
flexSettings: JSON.parse(flex)
|
|
189
|
-
};
|
|
190
|
-
startAdaptation(options);
|
|
191
|
-
});
|
|
192
|
-
})
|
|
193
|
-
});
|
|
194
|
-
});
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
// Load custom library paths if configured
|
|
198
|
-
var appUrls = bootstrapConfig.getAttribute("data-open-ux-preview-libs-manifests");
|
|
199
|
-
var initPromise = appUrls ? registerComponentDependencyPaths(JSON.parse(appUrls)) : Promise.resolve();
|
|
200
|
-
initPromise.catch(function (error) {
|
|
201
|
-
sap.ui.require(["sap/base/Log"], function (Log) {
|
|
202
|
-
Log.error(error);
|
|
203
|
-
});
|
|
204
|
-
})
|
|
205
|
-
.finally(function() {
|
|
206
|
-
// always set title, register fonts and place the renderer
|
|
207
|
-
setI18nTitle();
|
|
208
|
-
registerSAPFonts();
|
|
209
|
-
sap.ushell.Container.createRenderer().placeAt("content");
|
|
210
|
-
});
|