@sap-ux/odata-service-writer 0.27.0 → 0.27.2
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/dist/data/defaults.d.ts +2 -1
- package/dist/data/defaults.js +46 -14
- package/dist/data/manifest.js +10 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +4 -3
- package/dist/translations/odata-service-writer.i18n.json +1 -0
- package/dist/types.d.ts +4 -0
- package/dist/update.d.ts +0 -1
- package/dist/update.js +49 -34
- package/package.json +4 -4
package/dist/data/defaults.d.ts
CHANGED
|
@@ -7,6 +7,7 @@ import type { Editor } from 'mem-fs-editor';
|
|
|
7
7
|
* @param {string} basePath - the root path of an existing UI5 application
|
|
8
8
|
* @param {OdataService} service - the OData service instance
|
|
9
9
|
* @param {Editor} fs - the memfs editor instance
|
|
10
|
+
* @param {boolean} update - whether the service update is running (if true, skips unique service name generation and makes sure that '' model is updated for the mainService)
|
|
10
11
|
*/
|
|
11
|
-
export declare function enhanceData(basePath: string, service: OdataService, fs: Editor): Promise<void>;
|
|
12
|
+
export declare function enhanceData(basePath: string, service: OdataService, fs: Editor, update?: boolean): Promise<void>;
|
|
12
13
|
//# sourceMappingURL=defaults.d.ts.map
|
package/dist/data/defaults.js
CHANGED
|
@@ -16,15 +16,37 @@ const ui5_config_1 = require("@sap-ux/ui5-config");
|
|
|
16
16
|
function setDefaultServicePath(service) {
|
|
17
17
|
service.path = service.path?.endsWith('/') ? service.path : (service.path ?? '') + '/';
|
|
18
18
|
}
|
|
19
|
+
/**
|
|
20
|
+
* Generates a unique name for the service.
|
|
21
|
+
*
|
|
22
|
+
* @param {OdataService} dataSources - dataSources from manifest.json.
|
|
23
|
+
* @param {string} serviceName - The service name whose name needs to be modified.
|
|
24
|
+
* @returns Unique service name.
|
|
25
|
+
*/
|
|
26
|
+
function generateUniqueServiceName(dataSources, serviceName) {
|
|
27
|
+
let tmpSrvName = serviceName;
|
|
28
|
+
let uniqueNameCount = 1;
|
|
29
|
+
let doesExist = true; // Ensure data source name is unique
|
|
30
|
+
do {
|
|
31
|
+
if (dataSources?.[tmpSrvName]) {
|
|
32
|
+
tmpSrvName = `${serviceName}${uniqueNameCount++}`; // Try with a new name
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
doesExist = false;
|
|
36
|
+
}
|
|
37
|
+
} while (doesExist);
|
|
38
|
+
return tmpSrvName;
|
|
39
|
+
}
|
|
19
40
|
/**
|
|
20
41
|
* Sets the default name for a given service.
|
|
21
42
|
* Default serivce name is used only for first service.
|
|
22
43
|
*
|
|
23
44
|
* @param {string} basePath - the root path of an existing UI5 application
|
|
24
45
|
* @param {OdataService} service - The service object whose name needs to be set or modified.
|
|
25
|
-
* @param fs - the memfs editor instance
|
|
46
|
+
* @param {Editor} fs - the memfs editor instance
|
|
47
|
+
* @param {boolean} update - whether the service update is running
|
|
26
48
|
*/
|
|
27
|
-
async function setDefaultServiceName(basePath, service, fs) {
|
|
49
|
+
async function setDefaultServiceName(basePath, service, fs, update) {
|
|
28
50
|
const manifestPath = (0, path_1.join)(await (0, project_access_1.getWebappPath)(basePath, fs), project_access_1.FileName.Manifest);
|
|
29
51
|
const manifest = fs.readJSON(manifestPath);
|
|
30
52
|
// Check if manifest has already any dataSources defined, DEFAULT_DATASOURCE_NAME should be used for the first service
|
|
@@ -35,6 +57,9 @@ async function setDefaultServiceName(basePath, service, fs) {
|
|
|
35
57
|
if (oDataSources.length === 0) {
|
|
36
58
|
service.name = constants_1.DEFAULT_DATASOURCE_NAME;
|
|
37
59
|
}
|
|
60
|
+
else if (service.name && !update) {
|
|
61
|
+
service.name = generateUniqueServiceName(dataSources, service.name);
|
|
62
|
+
}
|
|
38
63
|
}
|
|
39
64
|
else {
|
|
40
65
|
// No existing dataSources - no existing services, use default name
|
|
@@ -48,9 +73,10 @@ async function setDefaultServiceName(basePath, service, fs) {
|
|
|
48
73
|
*
|
|
49
74
|
* @param {string} basePath - the root path of an existing UI5 application
|
|
50
75
|
* @param {OdataService} service - The service object whose model needs to be set or modified
|
|
51
|
-
* @param fs - the memfs editor instance
|
|
76
|
+
* @param {Editor} fs - the memfs editor instance
|
|
77
|
+
* @param {boolean} update - whether the service update is running (if true, makes sure that '' model is updated for the mainService)
|
|
52
78
|
*/
|
|
53
|
-
async function setDefaultServiceModel(basePath, service, fs) {
|
|
79
|
+
async function setDefaultServiceModel(basePath, service, fs, update) {
|
|
54
80
|
const manifestPath = (0, path_1.join)(await (0, project_access_1.getWebappPath)(basePath, fs), 'manifest.json');
|
|
55
81
|
const manifest = fs.readJSON(manifestPath);
|
|
56
82
|
if (!service.model) {
|
|
@@ -59,12 +85,15 @@ async function setDefaultServiceModel(basePath, service, fs) {
|
|
|
59
85
|
if (models) {
|
|
60
86
|
// Filter dataSource models by dataSource property
|
|
61
87
|
const servicesModels = Object.values(models).filter((model) => model.dataSource);
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
88
|
+
if (servicesModels.length === 0 ||
|
|
89
|
+
(update &&
|
|
90
|
+
servicesModels.find((serviceModel) => serviceModel.dataSource === constants_1.DEFAULT_DATASOURCE_NAME) &&
|
|
91
|
+
service.name === constants_1.DEFAULT_DATASOURCE_NAME)) {
|
|
92
|
+
service.model = '';
|
|
93
|
+
}
|
|
94
|
+
else if (service.name) {
|
|
95
|
+
service.model = service.name;
|
|
96
|
+
}
|
|
68
97
|
}
|
|
69
98
|
else {
|
|
70
99
|
// No models defined, that means first one is being added, set model to ''
|
|
@@ -147,11 +176,14 @@ async function setDefaultPreviewSettings(basePath, service, fs) {
|
|
|
147
176
|
* @param {string} basePath - the root path of an existing UI5 application
|
|
148
177
|
* @param {OdataService} service - the OData service instance
|
|
149
178
|
* @param {Editor} fs - the memfs editor instance
|
|
179
|
+
* @param {boolean} update - whether the service update is running (if true, skips unique service name generation and makes sure that '' model is updated for the mainService)
|
|
150
180
|
*/
|
|
151
|
-
async function enhanceData(basePath, service, fs) {
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
181
|
+
async function enhanceData(basePath, service, fs, update = false) {
|
|
182
|
+
if (!update) {
|
|
183
|
+
setDefaultServicePath(service);
|
|
184
|
+
}
|
|
185
|
+
await setDefaultServiceName(basePath, service, fs, update);
|
|
186
|
+
await setDefaultServiceModel(basePath, service, fs, update);
|
|
155
187
|
// set service type to EDMX if not defined
|
|
156
188
|
service.type = service.type ?? types_1.ServiceType.EDMX;
|
|
157
189
|
/**
|
package/dist/data/manifest.js
CHANGED
|
@@ -403,17 +403,26 @@ async function updateManifest(basePath, service, fs, forceServiceUpdate = false)
|
|
|
403
403
|
const manifest = fs.readJSON(manifestPath);
|
|
404
404
|
const appProp = 'sap.app';
|
|
405
405
|
const appid = manifest?.[appProp]?.id;
|
|
406
|
+
const dataSources = manifest[appProp]?.dataSources ?? {};
|
|
406
407
|
// Throw if required property is not found manifest.json
|
|
407
408
|
if (!appid) {
|
|
408
409
|
throw new Error((0, i18n_1.t)('error.requiredProjectPropertyNotFound', { property: `'${appProp}'.id`, path: manifestPath }));
|
|
409
410
|
}
|
|
410
411
|
// Throw if only update is required and service is not found in manifest.json
|
|
411
|
-
if (forceServiceUpdate && service.name && !
|
|
412
|
+
if (forceServiceUpdate && service.name && !dataSources?.[service.name]) {
|
|
412
413
|
throw new Error((0, i18n_1.t)('error.requiredProjectPropertyNotFound', {
|
|
413
414
|
property: `'${appProp}.dataSources.${service.name}'`,
|
|
414
415
|
path: manifestPath
|
|
415
416
|
}));
|
|
416
417
|
}
|
|
418
|
+
// Throw if service is being added, but service with the same URI already exists
|
|
419
|
+
if (!forceServiceUpdate &&
|
|
420
|
+
service.path &&
|
|
421
|
+
Object.values(dataSources).find((dataSource) => dataSource.uri === service.path)) {
|
|
422
|
+
throw new Error((0, i18n_1.t)('error.requiredServiceAlreadyExists', {
|
|
423
|
+
uri: service.path
|
|
424
|
+
}));
|
|
425
|
+
}
|
|
417
426
|
// Check and update existing services in a way that is supported by multiple services
|
|
418
427
|
const convertedManifest = await addMultipleServiceSupportToManifest(webappPath, manifest, fs);
|
|
419
428
|
// Update manifest.json services
|
package/dist/index.d.ts
CHANGED
|
@@ -17,10 +17,11 @@ declare function generate(basePath: string, service: OdataService, fs?: Editor):
|
|
|
17
17
|
* @param {string} basePath - the root path of an existing UI5 application
|
|
18
18
|
* @param {OdataService} service - the OData service instance
|
|
19
19
|
* @param {Editor} [fs] - the memfs editor instance
|
|
20
|
+
* @param {boolean} updateMiddlewares - whether the YAML files for the service (mock-server and fiori-tools-proxy middlewares) should be updated
|
|
20
21
|
* @throws {Error} - if required UI5 project files are not found
|
|
21
22
|
* @returns {Promise<Editor>} the updated memfs editor instance
|
|
22
23
|
*/
|
|
23
|
-
declare function update(basePath: string, service: OdataService, fs?: Editor): Promise<Editor>;
|
|
24
|
+
declare function update(basePath: string, service: OdataService, fs?: Editor, updateMiddlewares?: boolean): Promise<Editor>;
|
|
24
25
|
/**
|
|
25
26
|
* Removes service related data from project files for an existing UI5 project specified by the base path.
|
|
26
27
|
* How the method works:
|
package/dist/index.js
CHANGED
|
@@ -97,22 +97,23 @@ async function generate(basePath, service, fs) {
|
|
|
97
97
|
* @param {string} basePath - the root path of an existing UI5 application
|
|
98
98
|
* @param {OdataService} service - the OData service instance
|
|
99
99
|
* @param {Editor} [fs] - the memfs editor instance
|
|
100
|
+
* @param {boolean} updateMiddlewares - whether the YAML files for the service (mock-server and fiori-tools-proxy middlewares) should be updated
|
|
100
101
|
* @throws {Error} - if required UI5 project files are not found
|
|
101
102
|
* @returns {Promise<Editor>} the updated memfs editor instance
|
|
102
103
|
*/
|
|
103
|
-
async function update(basePath, service, fs) {
|
|
104
|
+
async function update(basePath, service, fs, updateMiddlewares = true) {
|
|
104
105
|
if (!fs) {
|
|
105
106
|
fs = (0, mem_fs_editor_1.create)((0, mem_fs_1.create)());
|
|
106
107
|
}
|
|
107
108
|
const paths = await findProjectFiles(basePath, fs);
|
|
108
109
|
const webappPath = await (0, project_access_1.getWebappPath)(basePath, fs);
|
|
109
110
|
ensureExists(webappPath, ['manifest.json'], fs);
|
|
110
|
-
await (0, data_1.enhanceData)(basePath, service, fs);
|
|
111
|
+
await (0, data_1.enhanceData)(basePath, service, fs, true);
|
|
111
112
|
// Set isServiceTypeEdmx true if service is EDMX
|
|
112
113
|
const isServiceTypeEdmx = service.type === types_1.ServiceType.EDMX;
|
|
113
114
|
await (0, manifest_1.updateManifest)(basePath, service, fs, true);
|
|
114
115
|
// Dont extend/update backend and mockserver middlewares if service type is CDS
|
|
115
|
-
if (isServiceTypeEdmx) {
|
|
116
|
+
if (isServiceTypeEdmx && updateMiddlewares) {
|
|
116
117
|
await (0, update_1.updateServicesData)(basePath, paths, service, fs);
|
|
117
118
|
}
|
|
118
119
|
return fs;
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
"error": {
|
|
3
3
|
"requiredProjectFileNotFound": "Invalid project structure. Cannot find required file {{-path}}",
|
|
4
4
|
"requiredProjectPropertyNotFound": "Required project property: {{-property}} was not found in file: {{-path}}",
|
|
5
|
+
"requiredServiceAlreadyExists": "A service with the {{-uri}} URI already exists. Choose another service.",
|
|
5
6
|
"unparseableXML": "Unparseable XML was specified: {{-error}}"
|
|
6
7
|
}
|
|
7
8
|
}
|
package/dist/types.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ManifestNamespace } from '@sap-ux/project-access';
|
|
1
2
|
import type { FioriToolsProxyConfigBackend as ProxyBackend } from '@sap-ux/ui5-config';
|
|
2
3
|
export declare enum OdataVersion {
|
|
3
4
|
v2 = "2",
|
|
@@ -82,4 +83,7 @@ export interface ProjectPaths {
|
|
|
82
83
|
ui5LocalYaml?: string;
|
|
83
84
|
ui5MockYaml?: string;
|
|
84
85
|
}
|
|
86
|
+
export type DataSources = {
|
|
87
|
+
[k: string]: ManifestNamespace.DataSource;
|
|
88
|
+
};
|
|
85
89
|
//# sourceMappingURL=types.d.ts.map
|
package/dist/update.d.ts
CHANGED
|
@@ -15,7 +15,6 @@ export declare function addServicesData(basePath: string, paths: ProjectPaths, t
|
|
|
15
15
|
/**
|
|
16
16
|
* Updates services data in ui5-*.yaml files.
|
|
17
17
|
* Mockserver configuration for services and annotations are updated using dataSources from manifest.json.
|
|
18
|
-
* At the end, older XML files for service annotations are removed and new annotations are generated.
|
|
19
18
|
*
|
|
20
19
|
* @param {string} basePath - the root path of an existing UI5 application
|
|
21
20
|
* @param {ProjectPaths} paths - paths to the project files (package.json, ui5.yaml, ui5-local.yaml and ui5-mock.yaml)
|
package/dist/update.js
CHANGED
|
@@ -27,28 +27,36 @@ async function generateMockserverMiddlewareBasedOnUi5MockYaml(fs, ui5YamlPath, u
|
|
|
27
27
|
}
|
|
28
28
|
}
|
|
29
29
|
/**
|
|
30
|
-
* Extends backend middleware for UI5Config with service data.
|
|
30
|
+
* Extends backend middleware for UI5Config with the service data.
|
|
31
31
|
*
|
|
32
32
|
* @param {Editor} fs - the memfs editor instance
|
|
33
33
|
* @param {OdataService} service - the OData service instance data
|
|
34
34
|
* @param {UI5Config} ui5Config - UI5 configuration
|
|
35
35
|
* @param {string} ui5ConfigPath - path to the YAML config file
|
|
36
|
+
* @param {boolean} update - whether the service update is running
|
|
36
37
|
* @throws {Error} - if required UI5 project files are not found
|
|
37
38
|
*/
|
|
38
|
-
function extendBackendMiddleware(fs, service, ui5Config, ui5ConfigPath) {
|
|
39
|
-
|
|
40
|
-
ui5Config.
|
|
39
|
+
function extendBackendMiddleware(fs, service, ui5Config, ui5ConfigPath, update = false) {
|
|
40
|
+
if (update) {
|
|
41
|
+
ui5Config.updateBackendToFioriToolsProxydMiddleware(service.previewSettings);
|
|
41
42
|
}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
ui5Config.
|
|
46
|
-
backend: [service.previewSettings],
|
|
47
|
-
ignoreCertError: service.ignoreCertError
|
|
48
|
-
});
|
|
43
|
+
else {
|
|
44
|
+
// Try to add backend
|
|
45
|
+
try {
|
|
46
|
+
ui5Config.addBackendToFioriToolsProxydMiddleware(service.previewSettings, service.ignoreCertError);
|
|
49
47
|
}
|
|
50
|
-
|
|
51
|
-
|
|
48
|
+
catch (error) {
|
|
49
|
+
if ((error instanceof ui5_config_1.YAMLError && error.code === ui5_config_1.yamlErrorCode.nodeNotFound) ||
|
|
50
|
+
error.message === 'Could not find fiori-tools-proxy') {
|
|
51
|
+
// Middleware is missing, add it along with the service backend
|
|
52
|
+
ui5Config.addFioriToolsProxydMiddleware({
|
|
53
|
+
backend: [service.previewSettings],
|
|
54
|
+
ignoreCertError: service.ignoreCertError
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
throw error;
|
|
59
|
+
}
|
|
52
60
|
}
|
|
53
61
|
}
|
|
54
62
|
fs.write(ui5ConfigPath, ui5Config.toString());
|
|
@@ -108,7 +116,6 @@ async function addServicesData(basePath, paths, templateRoot, service, fs) {
|
|
|
108
116
|
/**
|
|
109
117
|
* Updates services data in ui5-*.yaml files.
|
|
110
118
|
* Mockserver configuration for services and annotations are updated using dataSources from manifest.json.
|
|
111
|
-
* At the end, older XML files for service annotations are removed and new annotations are generated.
|
|
112
119
|
*
|
|
113
120
|
* @param {string} basePath - the root path of an existing UI5 application
|
|
114
121
|
* @param {ProjectPaths} paths - paths to the project files (package.json, ui5.yaml, ui5-local.yaml and ui5-mock.yaml)
|
|
@@ -118,37 +125,45 @@ async function addServicesData(basePath, paths, templateRoot, service, fs) {
|
|
|
118
125
|
async function updateServicesData(basePath, paths, service, fs) {
|
|
119
126
|
let ui5Config;
|
|
120
127
|
let ui5LocalConfig;
|
|
128
|
+
let ui5MockConfig;
|
|
121
129
|
if (paths.ui5Yaml) {
|
|
122
130
|
ui5Config = await ui5_config_1.UI5Config.newInstance(fs.read(paths.ui5Yaml));
|
|
131
|
+
// Update ui5.yaml with backend middleware
|
|
132
|
+
extendBackendMiddleware(fs, service, ui5Config, paths.ui5Yaml, true);
|
|
123
133
|
}
|
|
134
|
+
// Update ui5-local.yaml with backend middleware
|
|
124
135
|
if (paths.ui5LocalYaml) {
|
|
125
136
|
ui5LocalConfig = await ui5_config_1.UI5Config.newInstance(fs.read(paths.ui5LocalYaml));
|
|
137
|
+
extendBackendMiddleware(fs, service, ui5LocalConfig, paths.ui5LocalYaml, true);
|
|
126
138
|
}
|
|
127
139
|
// For update, updatable files should already exist
|
|
128
140
|
if (service.metadata) {
|
|
129
141
|
const webappPath = await (0, project_access_1.getWebappPath)(basePath, fs);
|
|
130
142
|
// Generate mockserver only when ui5-mock.yaml already exists
|
|
131
|
-
if (paths.ui5MockYaml) {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
overwrite: true
|
|
142
|
-
}
|
|
143
|
-
};
|
|
144
|
-
// Regenerate mockserver middleware for ui5-mock.yaml by overwriting
|
|
145
|
-
await (0, mockserver_config_writer_1.generateMockserverConfig)(basePath, config, fs);
|
|
146
|
-
// Update ui5-local.yaml with mockserver middleware from updated ui5-mock.yaml
|
|
147
|
-
await generateMockserverMiddlewareBasedOnUi5MockYaml(fs, paths.ui5Yaml, paths.ui5LocalYaml, ui5LocalConfig);
|
|
148
|
-
if (paths.ui5LocalYaml && ui5LocalConfig) {
|
|
149
|
-
// write ui5 local yaml if service type is not CDS
|
|
150
|
-
fs.write(paths.ui5LocalYaml, ui5LocalConfig.toString());
|
|
143
|
+
if (paths.ui5MockYaml && paths.ui5Yaml && ui5Config) {
|
|
144
|
+
const config = {
|
|
145
|
+
webappPath: webappPath,
|
|
146
|
+
// Since ui5-mock.yaml already exists, set 'skip' to skip package.json file updates
|
|
147
|
+
packageJsonConfig: {
|
|
148
|
+
skip: true
|
|
149
|
+
},
|
|
150
|
+
// Set 'overwrite' to true to overwrite services data in YAML files
|
|
151
|
+
ui5MockYamlConfig: {
|
|
152
|
+
overwrite: true
|
|
151
153
|
}
|
|
154
|
+
};
|
|
155
|
+
// Regenerate mockserver middleware for ui5-mock.yaml by overwriting
|
|
156
|
+
await (0, mockserver_config_writer_1.generateMockserverConfig)(basePath, config, fs);
|
|
157
|
+
// Update ui5-local.yaml with mockserver middleware from updated ui5-mock.yaml
|
|
158
|
+
await generateMockserverMiddlewareBasedOnUi5MockYaml(fs, paths.ui5Yaml, paths.ui5LocalYaml, ui5LocalConfig);
|
|
159
|
+
// Update ui5-mock.yaml with backend middleware
|
|
160
|
+
if (paths.ui5MockYaml) {
|
|
161
|
+
ui5MockConfig = await ui5_config_1.UI5Config.newInstance(fs.read(paths.ui5MockYaml));
|
|
162
|
+
extendBackendMiddleware(fs, service, ui5MockConfig, paths.ui5MockYaml, true);
|
|
163
|
+
}
|
|
164
|
+
if (paths.ui5LocalYaml && ui5LocalConfig) {
|
|
165
|
+
// write ui5 local yaml if service type is not CDS
|
|
166
|
+
fs.write(paths.ui5LocalYaml, ui5LocalConfig.toString());
|
|
152
167
|
}
|
|
153
168
|
}
|
|
154
169
|
// Write metadata.xml file
|
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%3Aodata-service-writer"
|
|
11
11
|
},
|
|
12
|
-
"version": "0.27.
|
|
12
|
+
"version": "0.27.2",
|
|
13
13
|
"license": "Apache-2.0",
|
|
14
14
|
"main": "dist/index.js",
|
|
15
15
|
"files": [
|
|
@@ -27,9 +27,9 @@
|
|
|
27
27
|
"mem-fs-editor": "9.4.0",
|
|
28
28
|
"prettify-xml": "1.2.0",
|
|
29
29
|
"semver": "7.5.4",
|
|
30
|
-
"@sap-ux/mockserver-config-writer": "0.9.
|
|
31
|
-
"@sap-ux/project-access": "1.30.
|
|
32
|
-
"@sap-ux/ui5-config": "0.28.
|
|
30
|
+
"@sap-ux/mockserver-config-writer": "0.9.1",
|
|
31
|
+
"@sap-ux/project-access": "1.30.1",
|
|
32
|
+
"@sap-ux/ui5-config": "0.28.1"
|
|
33
33
|
},
|
|
34
34
|
"devDependencies": {
|
|
35
35
|
"@types/ejs": "3.1.2",
|