@ui5/task-adaptation 1.0.16 → 1.0.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +9 -1
- package/dist/annotationManager.d.ts +0 -10
- package/dist/annotationManager.js +18 -145
- package/dist/appVariantManager.d.ts +1 -0
- package/dist/appVariantManager.js +46 -17
- package/dist/baseAppManager.d.ts +12 -8
- package/dist/baseAppManager.js +20 -32
- package/dist/bundle.js +1 -23
- package/dist/cache/cacheManager.js +1 -0
- package/dist/i18nManager.d.ts +43 -0
- package/dist/i18nManager.js +203 -0
- package/dist/index.js +3 -3
- package/dist/model/annotationDiffStructureError.d.ts +3 -0
- package/dist/model/annotationDiffStructureError.js +9 -0
- package/dist/model/types.d.ts +3 -1
- package/dist/processors/abapProcessor.js +1 -1
- package/dist/processors/cfProcessor.js +1 -1
- package/dist/repositories/abapRepoManager.js +1 -1
- package/dist/repositories/html5RepoManager.js +1 -1
- package/dist/util/commonUtil.d.ts +1 -0
- package/dist/util/commonUtil.js +33 -1
- package/dist/util/jsonDiffUtil.d.ts +17 -0
- package/dist/util/jsonDiffUtil.js +54 -0
- package/dist/util/resourceUtil.d.ts +3 -0
- package/dist/util/resourceUtil.js +13 -3
- package/dist/util/xmlUtil.d.ts +4 -0
- package/dist/util/xmlUtil.js +24 -0
- package/package.json +4 -4
- package/scripts/bundler.ts +1 -1
- package/scripts/rollup/overrides/sap/ui/thirdparty/URI.js +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -2,7 +2,13 @@
|
|
|
2
2
|
All notable changes to this project will be documented in this file.
|
|
3
3
|
This project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
|
|
4
4
|
|
|
5
|
-
A list of unreleased changes can be found [here](https://github.com/SAP/ui5-task-adaptation/compare/v1.0.
|
|
5
|
+
A list of unreleased changes can be found [here](https://github.com/SAP/ui5-task-adaptation/compare/v1.0.18...HEAD).
|
|
6
|
+
|
|
7
|
+
<a name="v1.0.18"></a>
|
|
8
|
+
## [v1.0.18] - 2023-11-10
|
|
9
|
+
|
|
10
|
+
<a name="v1.0.17"></a>
|
|
11
|
+
## [v1.0.17] - 2023-08-16
|
|
6
12
|
|
|
7
13
|
<a name="v1.0.16"></a>
|
|
8
14
|
## [v1.0.16] - 2023-08-15
|
|
@@ -46,6 +52,8 @@ A list of unreleased changes can be found [here](https://github.com/SAP/ui5-task
|
|
|
46
52
|
<a name="v1.0.0"></a>
|
|
47
53
|
## v1.0.0 - 2020-12-09
|
|
48
54
|
|
|
55
|
+
[v1.0.18]: https://github.com/SAP/ui5-task-adaptation/compare/v1.0.17...v1.0.18
|
|
56
|
+
[v1.0.17]: https://github.com/SAP/ui5-task-adaptation/compare/v1.0.16...v1.0.17
|
|
49
57
|
[v1.0.16]: https://github.com/SAP/ui5-task-adaptation/compare/v1.0.11...v1.0.16
|
|
50
58
|
[v1.0.11]: https://github.com/SAP/ui5-task-adaptation/compare/v1.0.10...v1.0.11
|
|
51
59
|
[v1.0.10]: https://github.com/SAP/ui5-task-adaptation/compare/v1.0.9...v1.0.10
|
|
@@ -10,12 +10,8 @@ export default class AnnotationManager {
|
|
|
10
10
|
constructor(configuration: IConfiguration, abapRepoManager: AbapRepoManager);
|
|
11
11
|
ANNOTATIONS_FOLDER: string;
|
|
12
12
|
process(renamedBaseAppManifest: any, languages: string[]): Promise<Map<string, string>>;
|
|
13
|
-
private createI18nFiles;
|
|
14
13
|
private normalizeAppVariantId;
|
|
15
14
|
private createAnnotationFile;
|
|
16
|
-
private getNormalisedLanguage;
|
|
17
|
-
private getAdaptedAnnotation;
|
|
18
|
-
private getUniqueKeyForPath;
|
|
19
15
|
private downloadAnnotations;
|
|
20
16
|
private updateManifest;
|
|
21
17
|
private updateManifestDataSources;
|
|
@@ -23,10 +19,4 @@ export default class AnnotationManager {
|
|
|
23
19
|
private enhanceManifestModel;
|
|
24
20
|
private downloadAnnotation;
|
|
25
21
|
private getODataAnnotations;
|
|
26
|
-
static traverseJson(obj: any, path: string): {
|
|
27
|
-
subject: any;
|
|
28
|
-
property: string;
|
|
29
|
-
};
|
|
30
|
-
private getDiffJsonPaths;
|
|
31
|
-
private traverseDiff;
|
|
32
22
|
}
|
|
@@ -1,17 +1,14 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const convert = require("xml-js");
|
|
4
|
-
const path = require("path");
|
|
5
3
|
const annotationsCacheManager_1 = require("./cache/annotationsCacheManager");
|
|
6
4
|
const baseAppManager_1 = require("./baseAppManager");
|
|
7
|
-
const
|
|
8
|
-
const
|
|
9
|
-
const
|
|
5
|
+
const i18nManager_1 = require("./i18nManager");
|
|
6
|
+
const xmlUtil_1 = require("./util/xmlUtil");
|
|
7
|
+
const path_1 = require("path");
|
|
10
8
|
const I18N_DEFAULT_PATH = "i18n/annotations";
|
|
11
9
|
const I18N_DEFAULT_MODEL_NAME = "@i18n";
|
|
12
10
|
const SAPUI5 = "sap.ui5";
|
|
13
11
|
const SAPAPP = "sap.app";
|
|
14
|
-
const XML_OPTIONS = { compact: true, spaces: 4 };
|
|
15
12
|
const log = require("@ui5/logger").getLogger("@ui5/task-adaptation::AnnotationManager");
|
|
16
13
|
class AnnotationManager {
|
|
17
14
|
constructor(configuration, abapRepoManager) {
|
|
@@ -27,13 +24,12 @@ class AnnotationManager {
|
|
|
27
24
|
const oDataAnnotations = this.getODataAnnotations(renamedBaseAppManifest);
|
|
28
25
|
const promises = this.downloadAnnotations(oDataAnnotations, languages);
|
|
29
26
|
const modelName = I18N_DEFAULT_MODEL_NAME; //`i18n_a9n_${normalisedId}`;
|
|
30
|
-
const i18nPathName =
|
|
31
|
-
const keys = new Array();
|
|
32
|
-
const propertiesPerLanguage = new Map();
|
|
27
|
+
const i18nPathName = path_1.posix.join(I18N_DEFAULT_PATH, normalisedId);
|
|
33
28
|
const annotationFiles = new Map();
|
|
34
29
|
const metaInfo = new Array();
|
|
30
|
+
const i18nManager = new i18nManager_1.default(modelName, id, languages);
|
|
35
31
|
for (const { promisesPerLanguage, annotationName } of promises) {
|
|
36
|
-
const annotationXml = await this.createAnnotationFile(promisesPerLanguage,
|
|
32
|
+
const annotationXml = await this.createAnnotationFile(promisesPerLanguage, i18nManager);
|
|
37
33
|
const annotationFileName = `annotations/annotation_${annotationName}.xml`;
|
|
38
34
|
annotationFiles.set(annotationFileName, annotationXml);
|
|
39
35
|
metaInfo.push({ annotationFileName, annotationName });
|
|
@@ -41,82 +37,20 @@ class AnnotationManager {
|
|
|
41
37
|
if (metaInfo.length > 0) {
|
|
42
38
|
this.updateManifest(renamedBaseAppManifest, metaInfo, modelName, i18nPathName);
|
|
43
39
|
}
|
|
44
|
-
const i18nFiles =
|
|
40
|
+
const i18nFiles = i18nManager.createFiles(i18nPathName);
|
|
45
41
|
return new Map([...annotationFiles, ...i18nFiles]);
|
|
46
42
|
}
|
|
47
|
-
createI18nFiles(propertiesPerLanguage, i18nPathName) {
|
|
48
|
-
const files = new Map();
|
|
49
|
-
propertiesPerLanguage.forEach((i18nLines, language) => {
|
|
50
|
-
i18nLines.sort();
|
|
51
|
-
let filename = "i18n";
|
|
52
|
-
const normalisedLanguage = this.getNormalisedLanguage(language);
|
|
53
|
-
if (normalisedLanguage) {
|
|
54
|
-
filename += "_" + normalisedLanguage;
|
|
55
|
-
}
|
|
56
|
-
const filepath = path.join(i18nPathName, filename + ".properties");
|
|
57
|
-
files.set(filepath, i18nLines.join("\n"));
|
|
58
|
-
});
|
|
59
|
-
return files;
|
|
60
|
-
}
|
|
61
43
|
normalizeAppVariantId(id, replaceWith = "") {
|
|
62
44
|
return id.replace(/[.\W]+/gi, replaceWith);
|
|
63
45
|
}
|
|
64
|
-
async createAnnotationFile(promisesPerLanguage,
|
|
46
|
+
async createAnnotationFile(promisesPerLanguage, i18nManager) {
|
|
65
47
|
const annotationJsons = new Map();
|
|
66
48
|
for (const promise of promisesPerLanguage) {
|
|
67
49
|
const { language, xml } = await promise;
|
|
68
|
-
annotationJsons.set(language,
|
|
69
|
-
}
|
|
70
|
-
const paths = this.getDiffJsonPaths([...annotationJsons.values()]);
|
|
71
|
-
const { annotationJson, languageI18nContentMap } = this.getAdaptedAnnotation(paths, keys, annotationJsons, modelName, appVariantId);
|
|
72
|
-
languageI18nContentMap.forEach((i18nLines, language) => {
|
|
73
|
-
const properties = propertiesPerLanguage.get(language);
|
|
74
|
-
if (properties) {
|
|
75
|
-
properties.push(...i18nLines);
|
|
76
|
-
}
|
|
77
|
-
else {
|
|
78
|
-
propertiesPerLanguage.set(language, i18nLines);
|
|
79
|
-
}
|
|
80
|
-
});
|
|
81
|
-
return convert.json2xml(annotationJson, XML_OPTIONS);
|
|
82
|
-
}
|
|
83
|
-
getNormalisedLanguage(language) {
|
|
84
|
-
const normalisedLanguages = ResourceBundle._getFallbackLocales(language);
|
|
85
|
-
if (normalisedLanguages?.length > 0 && normalisedLanguages[0] != null && normalisedLanguages[0].length > 0) {
|
|
86
|
-
return normalisedLanguages[0].toLowerCase();
|
|
50
|
+
annotationJsons.set(language, xmlUtil_1.default.xmlToJson(xml));
|
|
87
51
|
}
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
const pathKeyMap = new Map();
|
|
91
|
-
const languageI18nContentMap = new Map();
|
|
92
|
-
for (const [language, json] of [...annotationJsons]) {
|
|
93
|
-
const lines = [];
|
|
94
|
-
for (const path of paths) {
|
|
95
|
-
const { subject, property } = AnnotationManager.traverseJson(json, path);
|
|
96
|
-
const value = subject[property];
|
|
97
|
-
const key = appVariantId + "_" + this.getUniqueKeyForPath(pathKeyMap, path, keys, value);
|
|
98
|
-
lines.push(key + "=" + value);
|
|
99
|
-
subject[property] = `{${modelName}>${key}}`;
|
|
100
|
-
}
|
|
101
|
-
if (lines.length > 0) {
|
|
102
|
-
languageI18nContentMap.set(language, lines);
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
return { annotationJson: [...annotationJsons][0][1], languageI18nContentMap };
|
|
106
|
-
}
|
|
107
|
-
getUniqueKeyForPath(pathKeyMap, path, keys, value) {
|
|
108
|
-
if (pathKeyMap.has(path)) {
|
|
109
|
-
return pathKeyMap.get(path);
|
|
110
|
-
}
|
|
111
|
-
const propertyName = value.replace(/\W/gi, "_").toUpperCase();
|
|
112
|
-
let suffix = "";
|
|
113
|
-
while (keys.includes(propertyName + suffix)) {
|
|
114
|
-
suffix = suffix === "" ? "0" : (parseInt(suffix) + 1).toString();
|
|
115
|
-
}
|
|
116
|
-
const key = propertyName + suffix;
|
|
117
|
-
pathKeyMap.set(path, key);
|
|
118
|
-
keys.push(key);
|
|
119
|
-
return key;
|
|
52
|
+
const annotationJson = i18nManager.populateTranslations(annotationJsons);
|
|
53
|
+
return xmlUtil_1.default.jsonToXml(annotationJson.json);
|
|
120
54
|
}
|
|
121
55
|
downloadAnnotations(annotations, languages) {
|
|
122
56
|
return [...annotations].map(([name, { uri }]) => ({
|
|
@@ -181,7 +115,13 @@ class AnnotationManager {
|
|
|
181
115
|
const cacheManager = new annotationsCacheManager_1.default(this.configuration, cacheFilename);
|
|
182
116
|
log.verbose(`Getting annotation '${name}' ${language} by '${annotationUri}'`);
|
|
183
117
|
try {
|
|
184
|
-
|
|
118
|
+
let files;
|
|
119
|
+
if (this.configuration.enableAnnotationCache) {
|
|
120
|
+
files = await cacheManager.getFiles(() => this.abapRepoManager.getAnnotationMetadata(annotationUri), () => this.abapRepoManager.downloadAnnotationFile(annotationUri));
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
files = await this.abapRepoManager.downloadAnnotationFile(annotationUri);
|
|
124
|
+
}
|
|
185
125
|
if (!files || files.size === 0) {
|
|
186
126
|
throw new Error(`No files were fetched for '${name}' by '${annotationUri}'`);
|
|
187
127
|
}
|
|
@@ -204,73 +144,6 @@ class AnnotationManager {
|
|
|
204
144
|
}
|
|
205
145
|
return oDataAnnotations;
|
|
206
146
|
}
|
|
207
|
-
static traverseJson(obj, path) {
|
|
208
|
-
const checkArrayIndex = (part) => {
|
|
209
|
-
if (Array.isArray(subject)) {
|
|
210
|
-
const item = parseInt(part);
|
|
211
|
-
if (isNaN(item)) {
|
|
212
|
-
throw new Error(`Array index '${part}' is not a number in path '${path}' for ${json}`);
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
};
|
|
216
|
-
const json = util_1.inspect(obj, true, 100, false);
|
|
217
|
-
const pathParts = path.split("/");
|
|
218
|
-
if (path.endsWith("/")) {
|
|
219
|
-
pathParts.pop();
|
|
220
|
-
}
|
|
221
|
-
let property = pathParts.pop();
|
|
222
|
-
let subject = obj;
|
|
223
|
-
for (const part of pathParts) {
|
|
224
|
-
let item = part;
|
|
225
|
-
checkArrayIndex(part);
|
|
226
|
-
subject = subject[item];
|
|
227
|
-
if (subject == null) {
|
|
228
|
-
throw new Error(`Property '${item}' is undefined in path '${path}' for ${json}`);
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
checkArrayIndex(property);
|
|
232
|
-
if (subject[property] == null) {
|
|
233
|
-
throw new Error(`Target property '${property}' is undefined in path '${path}' for ${json}`);
|
|
234
|
-
}
|
|
235
|
-
return { subject, property };
|
|
236
|
-
}
|
|
237
|
-
getDiffJsonPaths(jsons) {
|
|
238
|
-
const paths = new Set();
|
|
239
|
-
if (jsons.length > 1) {
|
|
240
|
-
for (let j = 1; j < jsons.length; j++) {
|
|
241
|
-
const diffResult = json_diff_1.diff(jsons[0], jsons[j], { full: false, sort: false });
|
|
242
|
-
// if diffResult is undefined -> jsons are the same
|
|
243
|
-
if (diffResult) {
|
|
244
|
-
for (const path of this.traverseDiff(diffResult)) {
|
|
245
|
-
paths.add(path);
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
return [...paths];
|
|
251
|
-
}
|
|
252
|
-
traverseDiff(obj, path = "") {
|
|
253
|
-
const branches = [];
|
|
254
|
-
if (Array.isArray(obj)) {
|
|
255
|
-
for (let i = 0; i < obj.length; i++) {
|
|
256
|
-
const item = obj[i];
|
|
257
|
-
if (item[0] == "~") {
|
|
258
|
-
branches.push(...this.traverseDiff(item[1], path + `${i}/`));
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
else {
|
|
263
|
-
for (const key of Object.keys(obj)) {
|
|
264
|
-
if (key == "__old" || key == "__new") {
|
|
265
|
-
branches.push(path);
|
|
266
|
-
}
|
|
267
|
-
else {
|
|
268
|
-
branches.push(...this.traverseDiff(obj[key], path + `${key}/`));
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
return branches;
|
|
273
|
-
}
|
|
274
147
|
}
|
|
275
148
|
exports.default = AnnotationManager;
|
|
276
149
|
//# sourceMappingURL=annotationManager.js.map
|
|
@@ -2,6 +2,7 @@ import { IAppVariantInfo } from "./model/types";
|
|
|
2
2
|
export default class AppVariantManager {
|
|
3
3
|
static process(appVariantResources: any[], projectNamespace: string, taskUtil: any): Promise<IAppVariantInfo>;
|
|
4
4
|
static getAppVariantResources(workspace: any): Promise<any[]>;
|
|
5
|
+
static renameChanges(appVariantResources: any[], projectNamespace: string, appVariantInfo: IAppVariantInfo): Promise<void>;
|
|
5
6
|
static getAppVariantInfo(appVariantResources: any[]): Promise<IAppVariantInfo>;
|
|
6
7
|
static writeI18nToModule(resource: any, projectNamespace: string, i18nBundleName: string): void;
|
|
7
8
|
private static omitFiles;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const path = require("path");
|
|
4
|
-
const resourceUtil_1 = require("./util/resourceUtil");
|
|
5
3
|
const commonUtil_1 = require("./util/commonUtil");
|
|
4
|
+
const resourceUtil_1 = require("./util/resourceUtil");
|
|
5
|
+
const path_1 = require("path");
|
|
6
6
|
const log = require("@ui5/logger").getLogger("@ui5/task-adaptation::AppVariantManager");
|
|
7
7
|
const OMIT_FILES = ["manifest.appdescr_variant"];
|
|
8
8
|
const OMIT_FOLDERS = [];
|
|
@@ -11,42 +11,71 @@ const MANIFEST_APP_VARIANT = "manifest.appdescr_variant";
|
|
|
11
11
|
class AppVariantManager {
|
|
12
12
|
static async process(appVariantResources, projectNamespace, taskUtil) {
|
|
13
13
|
const appVariantInfo = await this.getAppVariantInfo(appVariantResources);
|
|
14
|
-
const i18nBundleName = commonUtil_1.replaceDots(appVariantInfo.id);
|
|
14
|
+
const i18nBundleName = (0, commonUtil_1.replaceDots)(appVariantInfo.id);
|
|
15
15
|
for (const resource of appVariantResources) {
|
|
16
16
|
this.writeI18nToModule(resource, projectNamespace, i18nBundleName);
|
|
17
17
|
this.omitFiles(resource, taskUtil);
|
|
18
18
|
}
|
|
19
19
|
this.adjustAddNewModelEnhanceWith(appVariantInfo?.manifest?.content ?? [], i18nBundleName);
|
|
20
|
+
await this.renameChanges(appVariantResources, projectNamespace, appVariantInfo);
|
|
20
21
|
return appVariantInfo;
|
|
21
22
|
}
|
|
22
23
|
static getAppVariantResources(workspace) {
|
|
23
24
|
return workspace.byGlob(`/**/*.{${EXTENSIONS}}`);
|
|
24
25
|
}
|
|
26
|
+
static async renameChanges(appVariantResources, projectNamespace, appVariantInfo) {
|
|
27
|
+
const changesFolder = resourceUtil_1.default.getResourcePath(projectNamespace, "changes");
|
|
28
|
+
const changes = new Map();
|
|
29
|
+
const resourcesByPath = new Map();
|
|
30
|
+
for (const resource of appVariantResources) {
|
|
31
|
+
const resourcePath = resource.getPath();
|
|
32
|
+
const basename = path_1.posix.dirname(resourcePath);
|
|
33
|
+
if (changesFolder === basename) {
|
|
34
|
+
changes.set(resourcePath, await resourceUtil_1.default.getString(resource));
|
|
35
|
+
resourcesByPath.set(resourcePath, resource);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
const renamedChanges = (0, commonUtil_1.renameResources)(changes, appVariantInfo.reference, appVariantInfo.id);
|
|
39
|
+
renamedChanges.forEach((renamedContent, resourcePath) => {
|
|
40
|
+
const resource = resourcesByPath.get(resourcePath);
|
|
41
|
+
resourceUtil_1.default.setString(resource, renamedContent);
|
|
42
|
+
});
|
|
43
|
+
}
|
|
25
44
|
static async getAppVariantInfo(appVariantResources) {
|
|
45
|
+
const changesManifestFolder = path_1.posix.join("changes", "manifest");
|
|
46
|
+
let manifest;
|
|
47
|
+
const manifestChanges = [];
|
|
26
48
|
for (const resource of appVariantResources) {
|
|
27
|
-
const
|
|
49
|
+
const resourcePath = resource.getPath();
|
|
50
|
+
const dirname = path_1.posix.dirname(resource.getPath());
|
|
51
|
+
const basename = path_1.posix.basename(resourcePath);
|
|
28
52
|
if (basename === MANIFEST_APP_VARIANT) {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
53
|
+
manifest = await resourceUtil_1.default.getString(resource).then(JSON.parse);
|
|
54
|
+
}
|
|
55
|
+
else if (dirname.endsWith(changesManifestFolder)) {
|
|
56
|
+
const str = await resourceUtil_1.default.getString(resource);
|
|
57
|
+
manifestChanges.push(JSON.parse(str));
|
|
32
58
|
}
|
|
33
59
|
}
|
|
34
|
-
|
|
60
|
+
if (manifest) {
|
|
61
|
+
return {
|
|
62
|
+
id: manifest.id,
|
|
63
|
+
reference: manifest.reference,
|
|
64
|
+
manifest,
|
|
65
|
+
manifestChanges
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
throw new Error("Adaptation project should contain manifest.appdescr_variant");
|
|
35
69
|
}
|
|
36
70
|
static writeI18nToModule(resource, projectNamespace, i18nBundleName) {
|
|
37
|
-
if (
|
|
71
|
+
if (path_1.posix.extname(resource.getPath()) === ".properties") {
|
|
38
72
|
let rootFolder = resourceUtil_1.default.getRootFolder(projectNamespace);
|
|
39
|
-
|
|
40
|
-
resource.setPath(path.join(rootFolder, i18nBundleName, resource.getPath().substring(rootFolder.length)));
|
|
41
|
-
}
|
|
42
|
-
else {
|
|
43
|
-
resource.setPath(path.join("/", i18nBundleName, resource.getPath()));
|
|
44
|
-
}
|
|
73
|
+
resource.setPath(path_1.posix.join(rootFolder, i18nBundleName, resource.getPath().substring(rootFolder.length)));
|
|
45
74
|
}
|
|
46
75
|
}
|
|
47
76
|
static omitFiles(resource, taskUtil) {
|
|
48
|
-
const dirname =
|
|
49
|
-
const filename =
|
|
77
|
+
const dirname = path_1.posix.dirname(resource.getPath());
|
|
78
|
+
const filename = path_1.posix.basename(resource.getPath());
|
|
50
79
|
if (OMIT_FILES.includes(filename) ||
|
|
51
80
|
OMIT_FOLDERS.some(folder => dirname.endsWith(folder))) {
|
|
52
81
|
taskUtil.setTag(resource, taskUtil.STANDARD_TAGS.OmitFromBuildResult, true);
|
package/dist/baseAppManager.d.ts
CHANGED
|
@@ -1,16 +1,20 @@
|
|
|
1
|
-
import { IAppVariantInfo,
|
|
1
|
+
import { IAppVariantInfo, IProjectOptions } from "./model/types";
|
|
2
2
|
import IProcessor from "./processors/processor";
|
|
3
|
+
export interface IBaseAppResources {
|
|
4
|
+
resources: any[];
|
|
5
|
+
manifestInfo: IManifestInfo;
|
|
6
|
+
}
|
|
7
|
+
export interface IManifestInfo {
|
|
8
|
+
id: string;
|
|
9
|
+
version: string;
|
|
10
|
+
}
|
|
3
11
|
export default class BaseAppManager {
|
|
4
|
-
static process(baseAppFiles: Map<string, string>, appVariantInfo: IAppVariantInfo, options: IProjectOptions, processor: IProcessor): Promise<
|
|
12
|
+
static process(baseAppFiles: Map<string, string>, appVariantInfo: IAppVariantInfo, options: IProjectOptions, processor: IProcessor): Promise<IBaseAppResources>;
|
|
5
13
|
private static updateAdaptationProperties;
|
|
6
|
-
static getManifestInfo(manifest: any):
|
|
7
|
-
id: string;
|
|
8
|
-
version: string;
|
|
9
|
-
};
|
|
10
|
-
static renameBaseApp(baseAppFiles: Map<string, string>, search: string, replacement: string): Map<string, string>;
|
|
14
|
+
static getManifestInfo(manifest: any): IManifestInfo;
|
|
11
15
|
private static getBaseAppManifest;
|
|
12
16
|
private static fillAppVariantIdHierarchy;
|
|
13
17
|
static validateProperty(value: string, property: string): void;
|
|
14
|
-
static applyDescriptorChanges(baseAppManifest: any,
|
|
18
|
+
static applyDescriptorChanges(baseAppManifest: any, appVariantInfo: IAppVariantInfo, i18nBundleName: string): Promise<void>;
|
|
15
19
|
static writeToWorkspace(baseAppFiles: Map<string, string>, projectNamespace: string): any[];
|
|
16
20
|
}
|
package/dist/baseAppManager.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const
|
|
3
|
+
const commonUtil_1 = require("./util/commonUtil");
|
|
4
4
|
const buildStrategy_1 = require("./buildStrategy");
|
|
5
5
|
const resourceUtil_1 = require("./util/resourceUtil");
|
|
6
|
-
const
|
|
6
|
+
const path_1 = require("path");
|
|
7
7
|
const { RegistrationBuild, ApplyUtil, Applier, Change } = require("../dist/bundle");
|
|
8
8
|
const resourceFactory = require("@ui5/fs/lib/resourceFactory");
|
|
9
9
|
const log = require("@ui5/logger").getLogger("@ui5/task-adaptation::BaseAppManager");
|
|
@@ -11,15 +11,18 @@ class BaseAppManager {
|
|
|
11
11
|
static async process(baseAppFiles, appVariantInfo, options, processor) {
|
|
12
12
|
const baseAppManifest = this.getBaseAppManifest(baseAppFiles);
|
|
13
13
|
const { id, version } = this.getManifestInfo(baseAppManifest.content);
|
|
14
|
-
const renamedBaseAppFiles =
|
|
14
|
+
const renamedBaseAppFiles = (0, commonUtil_1.renameResources)(baseAppFiles, appVariantInfo.reference, appVariantInfo.id);
|
|
15
15
|
const { filepath, content } = this.getBaseAppManifest(renamedBaseAppFiles);
|
|
16
16
|
await processor.updateLandscapeSpecificContent(content, renamedBaseAppFiles);
|
|
17
17
|
this.fillAppVariantIdHierarchy(processor, id, version, content);
|
|
18
18
|
this.updateAdaptationProperties(content);
|
|
19
|
-
const i18nBundleName = commonUtil_1.replaceDots(appVariantInfo.id);
|
|
20
|
-
await this.applyDescriptorChanges(content, appVariantInfo
|
|
19
|
+
const i18nBundleName = (0, commonUtil_1.replaceDots)(appVariantInfo.id);
|
|
20
|
+
await this.applyDescriptorChanges(content, appVariantInfo, i18nBundleName);
|
|
21
21
|
renamedBaseAppFiles.set(filepath, JSON.stringify(content));
|
|
22
|
-
return
|
|
22
|
+
return {
|
|
23
|
+
resources: this.writeToWorkspace(renamedBaseAppFiles, options.projectNamespace),
|
|
24
|
+
manifestInfo: this.getManifestInfo(content)
|
|
25
|
+
};
|
|
23
26
|
}
|
|
24
27
|
static updateAdaptationProperties(content) {
|
|
25
28
|
if (content["sap.fiori"]?.cloudDevAdaptationStatus) {
|
|
@@ -35,26 +38,6 @@ class BaseAppManager {
|
|
|
35
38
|
const version = manifest["sap.app"]?.applicationVersion?.version;
|
|
36
39
|
return { id, version };
|
|
37
40
|
}
|
|
38
|
-
static renameBaseApp(baseAppFiles, search, replacement) {
|
|
39
|
-
log.verbose("Renaming base app resources to appVariant id");
|
|
40
|
-
const dotToSlash = (update) => update.split(".").join("\/");
|
|
41
|
-
const dotsEscape = (update) => update.split(".").join("\\.");
|
|
42
|
-
const replaces = [
|
|
43
|
-
{
|
|
44
|
-
regexp: new RegExp(dotsEscape(search), "g"),
|
|
45
|
-
replacement
|
|
46
|
-
},
|
|
47
|
-
{
|
|
48
|
-
regexp: new RegExp(dotToSlash(search), "g"),
|
|
49
|
-
replacement: dotToSlash(replacement)
|
|
50
|
-
}
|
|
51
|
-
];
|
|
52
|
-
const renamed = new Map();
|
|
53
|
-
baseAppFiles.forEach((content, filepath) => {
|
|
54
|
-
renamed.set(filepath, replaces.reduce((p, c) => p.replace(c.regexp, c.replacement), content));
|
|
55
|
-
});
|
|
56
|
-
return renamed;
|
|
57
|
-
}
|
|
58
41
|
static getBaseAppManifest(baseAppFiles) {
|
|
59
42
|
let filepath = [...baseAppFiles.keys()].find(filepath => filepath.endsWith("manifest.json"));
|
|
60
43
|
if (filepath) {
|
|
@@ -83,14 +66,19 @@ class BaseAppManager {
|
|
|
83
66
|
throw new Error(`Original application manifest should have ${property}`);
|
|
84
67
|
}
|
|
85
68
|
}
|
|
86
|
-
static async applyDescriptorChanges(baseAppManifest,
|
|
69
|
+
static async applyDescriptorChanges(baseAppManifest, appVariantInfo, i18nBundleName) {
|
|
87
70
|
log.verbose("Applying appVariant changes");
|
|
88
71
|
const strategy = new buildStrategy_1.default(RegistrationBuild, ApplyUtil, i18nBundleName);
|
|
89
|
-
|
|
90
|
-
|
|
72
|
+
const { manifest } = appVariantInfo;
|
|
73
|
+
const allChanges = [
|
|
74
|
+
...manifest.content,
|
|
75
|
+
...appVariantInfo.manifestChanges
|
|
76
|
+
];
|
|
77
|
+
if (manifest.layer) {
|
|
78
|
+
allChanges.forEach(item => item.layer = manifest.layer);
|
|
91
79
|
}
|
|
92
|
-
const changesContent =
|
|
93
|
-
if (changesContent) {
|
|
80
|
+
const changesContent = allChanges.map(change => new Change(change));
|
|
81
|
+
if (changesContent.length > 0) {
|
|
94
82
|
await Applier.applyChanges(baseAppManifest, changesContent, strategy);
|
|
95
83
|
}
|
|
96
84
|
}
|
|
@@ -116,6 +104,6 @@ class BaseAppManager {
|
|
|
116
104
|
exports.default = BaseAppManager;
|
|
117
105
|
const getPath = (filename, projectNamespace) => {
|
|
118
106
|
const rootFolder = resourceUtil_1.default.getRootFolder(projectNamespace);
|
|
119
|
-
return
|
|
107
|
+
return path_1.posix.join(rootFolder, filename);
|
|
120
108
|
};
|
|
121
109
|
//# sourceMappingURL=baseAppManager.js.map
|
package/dist/bundle.js
CHANGED
|
@@ -6,28 +6,6 @@ Object.defineProperty(exports, '__esModule', { value: true });
|
|
|
6
6
|
|
|
7
7
|
var path = require('path');
|
|
8
8
|
|
|
9
|
-
function _interopNamespace(e) {
|
|
10
|
-
if (e && e.__esModule) return e;
|
|
11
|
-
var n = Object.create(null);
|
|
12
|
-
if (e) {
|
|
13
|
-
Object.keys(e).forEach(function (k) {
|
|
14
|
-
if (k !== 'default') {
|
|
15
|
-
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
16
|
-
Object.defineProperty(n, k, d.get ? d : {
|
|
17
|
-
enumerable: true,
|
|
18
|
-
get: function () {
|
|
19
|
-
return e[k];
|
|
20
|
-
}
|
|
21
|
-
});
|
|
22
|
-
}
|
|
23
|
-
});
|
|
24
|
-
}
|
|
25
|
-
n['default'] = e;
|
|
26
|
-
return Object.freeze(n);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
var path__namespace = /*#__PURE__*/_interopNamespace(path);
|
|
30
|
-
|
|
31
9
|
var fnIsEmptyObject = function isEmptyObject(obj) {
|
|
32
10
|
for (var sName in obj) {
|
|
33
11
|
return false;
|
|
@@ -85,7 +63,7 @@ class URI {
|
|
|
85
63
|
}
|
|
86
64
|
|
|
87
65
|
normalize() {
|
|
88
|
-
this.value =
|
|
66
|
+
this.value = path.posix.normalize(this.value).replace(/\\/g, "/");
|
|
89
67
|
return this;
|
|
90
68
|
}
|
|
91
69
|
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { IDiffProperty, IJsonContent } from "./util/jsonDiffUtil";
|
|
2
|
+
export declare class PropertyValue {
|
|
3
|
+
value: string;
|
|
4
|
+
isReference: boolean;
|
|
5
|
+
qualifier: string;
|
|
6
|
+
language: string;
|
|
7
|
+
private static OLD;
|
|
8
|
+
private static NEW;
|
|
9
|
+
private constructor();
|
|
10
|
+
get isOld(): boolean;
|
|
11
|
+
set(diff: any, references: Map<string, string>): void;
|
|
12
|
+
static oldFrom(language: string, value?: string): PropertyValue;
|
|
13
|
+
static newFrom(language: string, value?: string): PropertyValue;
|
|
14
|
+
}
|
|
15
|
+
export declare class I18nFileContent {
|
|
16
|
+
private properties;
|
|
17
|
+
constructor(languages: string[]);
|
|
18
|
+
add(prop: PropertyValue, key: string): void;
|
|
19
|
+
private initKeyValuesForOtherLanguages;
|
|
20
|
+
hasTranslations(): boolean;
|
|
21
|
+
private getOrCreateLanguageContent;
|
|
22
|
+
createFiles(i18nPathName: string): Map<string, string>;
|
|
23
|
+
}
|
|
24
|
+
export default class I18nManager {
|
|
25
|
+
private modelName;
|
|
26
|
+
private appVariantId;
|
|
27
|
+
private references;
|
|
28
|
+
private existingKeys;
|
|
29
|
+
private i18nFileContent;
|
|
30
|
+
constructor(modelName: string, appVariantId: string, languages: string[]);
|
|
31
|
+
processDiff(properties: Set<IDiffProperty>, previousLanguage: string, currentLanguage: string): void;
|
|
32
|
+
createFiles(i18nPathName: string): Map<string, string>;
|
|
33
|
+
populateTranslations(annotationJsons: Map<string, any>): IJsonContent;
|
|
34
|
+
populate(annotationJsons: [string, any][], defaultAnnotation: IJsonContent): {
|
|
35
|
+
language: string;
|
|
36
|
+
json: any;
|
|
37
|
+
};
|
|
38
|
+
static extractDefaultLanguageAnnotation(annotationJsons: Map<string, any>): IJsonContent;
|
|
39
|
+
private replaceWithModelReference;
|
|
40
|
+
private createReference;
|
|
41
|
+
getUniqueKeyForValue(value: string): string;
|
|
42
|
+
private initPropertyValues;
|
|
43
|
+
}
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.I18nFileContent = exports.PropertyValue = void 0;
|
|
4
|
+
const jsonDiffUtil_1 = require("./util/jsonDiffUtil");
|
|
5
|
+
const annotationDiffStructureError_1 = require("./model/annotationDiffStructureError");
|
|
6
|
+
const path_1 = require("path");
|
|
7
|
+
// To generate keys, english language is more common, so compare all other
|
|
8
|
+
// languages with it
|
|
9
|
+
const DEFAULT_LANGUAGES = ["", "EN", "EN_US", "EN_GB"];
|
|
10
|
+
class PropertyValue {
|
|
11
|
+
constructor(qualifier, language, value) {
|
|
12
|
+
this.isReference = false;
|
|
13
|
+
this.qualifier = qualifier;
|
|
14
|
+
this.language = language;
|
|
15
|
+
this.value = value;
|
|
16
|
+
}
|
|
17
|
+
get isOld() {
|
|
18
|
+
return this.qualifier === PropertyValue.OLD;
|
|
19
|
+
}
|
|
20
|
+
set(diff, references) {
|
|
21
|
+
this.value = diff[this.qualifier];
|
|
22
|
+
this.isReference = references.has(diff[this.qualifier]);
|
|
23
|
+
}
|
|
24
|
+
static oldFrom(language, value = "") {
|
|
25
|
+
return new PropertyValue(PropertyValue.OLD, language, value);
|
|
26
|
+
}
|
|
27
|
+
static newFrom(language, value = "") {
|
|
28
|
+
return new PropertyValue(PropertyValue.NEW, language, value);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
exports.PropertyValue = PropertyValue;
|
|
32
|
+
PropertyValue.OLD = "__old";
|
|
33
|
+
PropertyValue.NEW = "__new";
|
|
34
|
+
class I18nFileContent {
|
|
35
|
+
constructor(languages) {
|
|
36
|
+
this.properties = new Map();
|
|
37
|
+
languages.forEach(language => this.properties.set(language, new Map()));
|
|
38
|
+
}
|
|
39
|
+
add(prop, key) {
|
|
40
|
+
this.getOrCreateLanguageContent(prop.language).set(key, prop.value);
|
|
41
|
+
this.initKeyValuesForOtherLanguages(prop, key);
|
|
42
|
+
}
|
|
43
|
+
initKeyValuesForOtherLanguages(prop, key) {
|
|
44
|
+
// "", EN, EN_US, DE, FR
|
|
45
|
+
// If we already compared 2 languages and saved translations, we assume
|
|
46
|
+
// that we have generated all property keys, but suddenly 3d language or
|
|
47
|
+
// further contain translation which was not present in previous
|
|
48
|
+
// languages we need to update all other languages then. These values
|
|
49
|
+
// will be overwritten later by actual values of the language:
|
|
50
|
+
// this.getOrCreateLanguageContent(prop.language).set(key, prop.value);
|
|
51
|
+
if (prop.isOld) {
|
|
52
|
+
this.properties.forEach((keyValue, language) => {
|
|
53
|
+
if (language !== prop.language && !keyValue.has(key)) {
|
|
54
|
+
keyValue.set(key, prop.value);
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
hasTranslations() {
|
|
60
|
+
// this.properties.size can be 0 or 2+, it can't be 1
|
|
61
|
+
return [...this.properties.values()].some(keyValue => keyValue.size > 0);
|
|
62
|
+
}
|
|
63
|
+
getOrCreateLanguageContent(language) {
|
|
64
|
+
if (!this.properties.has(language)) {
|
|
65
|
+
this.properties.set(language, new Map());
|
|
66
|
+
}
|
|
67
|
+
return this.properties.get(language);
|
|
68
|
+
}
|
|
69
|
+
createFiles(i18nPathName) {
|
|
70
|
+
const files = new Map();
|
|
71
|
+
if (this.hasTranslations()) {
|
|
72
|
+
this.properties.forEach((i18nLines, language) => {
|
|
73
|
+
let filename = "i18n";
|
|
74
|
+
if (language) {
|
|
75
|
+
filename += "_" + language.toLowerCase();
|
|
76
|
+
}
|
|
77
|
+
const filepath = (0, path_1.join)(i18nPathName, filename + ".properties");
|
|
78
|
+
files.set(filepath, [...i18nLines].map(([key, value]) => `${key}=${value}`).join("\n"));
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
return files;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
exports.I18nFileContent = I18nFileContent;
|
|
85
|
+
class I18nManager {
|
|
86
|
+
constructor(modelName, appVariantId, languages) {
|
|
87
|
+
this.references = new Map();
|
|
88
|
+
this.existingKeys = new Set();
|
|
89
|
+
this.modelName = modelName;
|
|
90
|
+
this.appVariantId = appVariantId;
|
|
91
|
+
this.i18nFileContent = new I18nFileContent(languages);
|
|
92
|
+
}
|
|
93
|
+
processDiff(properties, previousLanguage, currentLanguage) {
|
|
94
|
+
// json-diff uses __old and __new value as diff,
|
|
95
|
+
// so we assign it with languages which were in comparison
|
|
96
|
+
const propertyValues = [
|
|
97
|
+
PropertyValue.oldFrom(previousLanguage),
|
|
98
|
+
PropertyValue.newFrom(currentLanguage)
|
|
99
|
+
];
|
|
100
|
+
for (const property of properties) {
|
|
101
|
+
this.replaceWithModelReference(property, propertyValues);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
createFiles(i18nPathName) {
|
|
105
|
+
return this.i18nFileContent.createFiles(i18nPathName);
|
|
106
|
+
}
|
|
107
|
+
populateTranslations(annotationJsons) {
|
|
108
|
+
/* We compare annotations. Diff gives us:
|
|
109
|
+
{
|
|
110
|
+
"string": {
|
|
111
|
+
"__old": "A"
|
|
112
|
+
"__new": "B"
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
we generate key and extract to properties file. Now it looks so:
|
|
116
|
+
{
|
|
117
|
+
"string": "{@i18n>id_KEY}"
|
|
118
|
+
}
|
|
119
|
+
and we compare this with next language and diff gives us:
|
|
120
|
+
{
|
|
121
|
+
"string": {
|
|
122
|
+
"__old": "{@i18n>id_KEY}"
|
|
123
|
+
"__new": "C"
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
we use already generate key id_KEY and extract translation value to
|
|
127
|
+
properties file again. And then compare with the next language and so on
|
|
128
|
+
and so on */
|
|
129
|
+
let defaultAnnotation = I18nManager.extractDefaultLanguageAnnotation(annotationJsons);
|
|
130
|
+
if (annotationJsons.size > 0 && defaultAnnotation) {
|
|
131
|
+
defaultAnnotation = this.populate([...annotationJsons], defaultAnnotation);
|
|
132
|
+
}
|
|
133
|
+
return defaultAnnotation;
|
|
134
|
+
}
|
|
135
|
+
populate(annotationJsons, defaultAnnotation) {
|
|
136
|
+
return annotationJsons
|
|
137
|
+
.map(([language, json]) => ({ language, json }))
|
|
138
|
+
.reduce((p, c) => {
|
|
139
|
+
try {
|
|
140
|
+
const pDiff = jsonDiffUtil_1.default.diff(p.json, c.json);
|
|
141
|
+
this.processDiff(pDiff.properties, p.language, c.language);
|
|
142
|
+
return { json: pDiff.json, language: p.language };
|
|
143
|
+
}
|
|
144
|
+
catch (error) {
|
|
145
|
+
if (error instanceof annotationDiffStructureError_1.default) {
|
|
146
|
+
throw new Error(`The structure of the oData annotation xml of language '${p.language}' is different from xml of language '${c.language}' near element: ${error.message}`);
|
|
147
|
+
}
|
|
148
|
+
throw error;
|
|
149
|
+
}
|
|
150
|
+
}, defaultAnnotation);
|
|
151
|
+
}
|
|
152
|
+
static extractDefaultLanguageAnnotation(annotationJsons) {
|
|
153
|
+
let json = null;
|
|
154
|
+
for (const language of DEFAULT_LANGUAGES) {
|
|
155
|
+
json = annotationJsons.get(language);
|
|
156
|
+
if (json != null) {
|
|
157
|
+
annotationJsons.delete(language);
|
|
158
|
+
return { json, language };
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
const language = [...annotationJsons.keys()][0];
|
|
162
|
+
json = annotationJsons.get(language);
|
|
163
|
+
annotationJsons.delete(language);
|
|
164
|
+
return { json, language };
|
|
165
|
+
}
|
|
166
|
+
replaceWithModelReference({ object, property }, propertyValues) {
|
|
167
|
+
const diff = object[property];
|
|
168
|
+
this.initPropertyValues(diff, propertyValues);
|
|
169
|
+
// If there are already generated key, like on step 3. above comment, we
|
|
170
|
+
// take it (from __old), so we don't need to generate new
|
|
171
|
+
const propReference = propertyValues.find(prop => prop.isReference);
|
|
172
|
+
let reference = propReference?.value ?? this.createReference(diff.__old);
|
|
173
|
+
object[property] = reference;
|
|
174
|
+
// Other values, which are not references, are extracted to .properties
|
|
175
|
+
const key = this.references.get(reference);
|
|
176
|
+
if (key) {
|
|
177
|
+
propertyValues.filter(prop => !prop.isReference).forEach(prop => this.i18nFileContent.add(prop, key));
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
createReference(value) {
|
|
181
|
+
const key = this.appVariantId + "_" + this.getUniqueKeyForValue(value);
|
|
182
|
+
const reference = `{${this.modelName}>${key}}`;
|
|
183
|
+
this.references.set(reference, key);
|
|
184
|
+
return reference;
|
|
185
|
+
}
|
|
186
|
+
getUniqueKeyForValue(value) {
|
|
187
|
+
const propertyName = value.replace(/\W/gi, "_").toUpperCase();
|
|
188
|
+
let suffix = -1;
|
|
189
|
+
let suffixString;
|
|
190
|
+
do {
|
|
191
|
+
suffixString = suffix === -1 ? "" : suffix;
|
|
192
|
+
suffix++;
|
|
193
|
+
} while (this.existingKeys.has(propertyName + suffixString));
|
|
194
|
+
const key = propertyName + suffixString;
|
|
195
|
+
this.existingKeys.add(key);
|
|
196
|
+
return key;
|
|
197
|
+
}
|
|
198
|
+
initPropertyValues(diff, propertyValues) {
|
|
199
|
+
propertyValues.forEach(prop => prop.set(diff, this.references));
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
exports.default = I18nManager;
|
|
203
|
+
//# sourceMappingURL=i18nManager.js.map
|
package/dist/index.js
CHANGED
|
@@ -10,12 +10,12 @@ const processor_1 = require("./processors/processor");
|
|
|
10
10
|
module.exports = ({ workspace, options, taskUtil }) => {
|
|
11
11
|
dotenv.config();
|
|
12
12
|
async function process(workspace, taskUtil) {
|
|
13
|
-
const processor = processor_1.determineProcessor(options.configuration);
|
|
13
|
+
const processor = (0, processor_1.determineProcessor)(options.configuration);
|
|
14
14
|
const appVariantResources = await appVariantManager_1.default.getAppVariantResources(workspace);
|
|
15
15
|
const appVariantInfo = await appVariantManager_1.default.process(appVariantResources, options.projectNamespace, taskUtil);
|
|
16
16
|
const baseAppFiles = await processor.getBaseAppFiles(appVariantInfo.reference);
|
|
17
|
-
const
|
|
18
|
-
await Promise.all(appVariantResources.concat(
|
|
17
|
+
const { resources } = await baseAppManager_1.default.process(baseAppFiles, appVariantInfo, options, processor);
|
|
18
|
+
await Promise.all(appVariantResources.concat(resources).map(resource => workspace.write(resource)));
|
|
19
19
|
}
|
|
20
20
|
return process(workspace, taskUtil);
|
|
21
21
|
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
class AnnotationDiffStructureError extends Error {
|
|
4
|
+
constructor(json) {
|
|
5
|
+
super(JSON.stringify(json));
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
exports.default = AnnotationDiffStructureError;
|
|
9
|
+
//# sourceMappingURL=annotationDiffStructureError.js.map
|
package/dist/model/types.d.ts
CHANGED
|
@@ -10,6 +10,7 @@ export interface IConfiguration {
|
|
|
10
10
|
credentials?: IAuth;
|
|
11
11
|
type?: "cf" | "abap";
|
|
12
12
|
languages?: string[];
|
|
13
|
+
enableAnnotationCache?: boolean;
|
|
13
14
|
}
|
|
14
15
|
export interface IProjectOptions {
|
|
15
16
|
configuration: IConfiguration;
|
|
@@ -58,6 +59,7 @@ export interface IAppVariantInfo {
|
|
|
58
59
|
id: string;
|
|
59
60
|
reference: string;
|
|
60
61
|
manifest: IAppVariantManifest;
|
|
62
|
+
manifestChanges: Array<IChange>;
|
|
61
63
|
}
|
|
62
64
|
export interface IAppVariantManifest {
|
|
63
65
|
id: string;
|
|
@@ -94,6 +96,6 @@ export interface IMetadata {
|
|
|
94
96
|
changedOn: string;
|
|
95
97
|
id?: string;
|
|
96
98
|
}
|
|
97
|
-
export
|
|
99
|
+
export type KeyedMap<T, K extends keyof T, V> = {
|
|
98
100
|
[k in K]: V;
|
|
99
101
|
};
|
|
@@ -12,7 +12,7 @@ class AbapProcessor {
|
|
|
12
12
|
return this.cacheManager.getFiles(() => this.abapRepoManager.getMetadata(baseAppId), () => this.abapRepoManager.downloadBaseAppFiles());
|
|
13
13
|
}
|
|
14
14
|
validateConfiguration() {
|
|
15
|
-
commonUtil_1.validateObject(this.configuration, ["destination", "appName"], "should be specified in ui5.yaml configuration");
|
|
15
|
+
(0, commonUtil_1.validateObject)(this.configuration, ["destination", "appName"], "should be specified in ui5.yaml configuration");
|
|
16
16
|
}
|
|
17
17
|
async updateLandscapeSpecificContent(renamedBaseAppManifest, baseAppFiles) {
|
|
18
18
|
const { languages } = this.configuration;
|
|
@@ -11,7 +11,7 @@ class CFProcessor {
|
|
|
11
11
|
return this.cacheManager.getFiles(() => html5RepoManager_1.default.getMetadata(this.configuration), () => html5RepoManager_1.default.getBaseAppFiles(this.configuration));
|
|
12
12
|
}
|
|
13
13
|
validateConfiguration() {
|
|
14
|
-
commonUtil_1.validateObject(this.configuration, ["appHostId", "appName", "appVersion"], "should be specified in ui5.yaml configuration");
|
|
14
|
+
(0, commonUtil_1.validateObject)(this.configuration, ["appHostId", "appName", "appVersion"], "should be specified in ui5.yaml configuration");
|
|
15
15
|
}
|
|
16
16
|
async updateLandscapeSpecificContent(renamedBaseAppManifest) {
|
|
17
17
|
this.updateCloudPlatform(renamedBaseAppManifest);
|
|
@@ -50,7 +50,7 @@ class AbapRepoManager {
|
|
|
50
50
|
const data = await requestUtil_1.default.get(uri, REQUEST_OPTIONS_XML, auth);
|
|
51
51
|
if (data?.d?.ZipArchive.length > 0) {
|
|
52
52
|
const buffer = Buffer.from(data.d.ZipArchive, "base64");
|
|
53
|
-
return zipUtil_1.unzipZipEntries(buffer);
|
|
53
|
+
return (0, zipUtil_1.unzipZipEntries)(buffer);
|
|
54
54
|
}
|
|
55
55
|
throw new Error(`App '${appName}' from destination '${destination}' doesn't contain files`);
|
|
56
56
|
}
|
|
@@ -69,7 +69,7 @@ class HTML5RepoManager {
|
|
|
69
69
|
const { appHostId, appName, appVersion } = options;
|
|
70
70
|
const uri = `${html5RepoBaseUri}/applications/content/${appName}-${appVersion}/`;
|
|
71
71
|
const zip = await this.download(token, appHostId, uri);
|
|
72
|
-
return zipUtil_1.unzipZipEntries(zip);
|
|
72
|
+
return (0, zipUtil_1.unzipZipEntries)(zip);
|
|
73
73
|
}
|
|
74
74
|
static async download(token, appHostId, uri) {
|
|
75
75
|
if (!token) {
|
|
@@ -1,2 +1,3 @@
|
|
|
1
1
|
export declare function replaceDots(value: string): string;
|
|
2
2
|
export declare function validateObject<T extends Object>(options: T, properties: Array<keyof T>, message: string): void;
|
|
3
|
+
export declare function renameResources(files: Map<string, string>, search: string, replacement: string): Map<string, string>;
|
package/dist/util/commonUtil.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.validateObject = exports.replaceDots = void 0;
|
|
3
|
+
exports.renameResources = exports.validateObject = exports.replaceDots = void 0;
|
|
4
4
|
function replaceDots(value) {
|
|
5
5
|
return value.replace(/\./g, "_");
|
|
6
6
|
}
|
|
@@ -13,4 +13,36 @@ function validateObject(options, properties, message) {
|
|
|
13
13
|
}
|
|
14
14
|
}
|
|
15
15
|
exports.validateObject = validateObject;
|
|
16
|
+
function renameResources(files, search, replacement) {
|
|
17
|
+
const escapeRegexSpecialChars = (update) => update.replaceAll(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
|
|
18
|
+
// The current regex works if the old Id is contained in the new Id, given
|
|
19
|
+
// that they do not have the same beginning.
|
|
20
|
+
// more complete alternative: /((?<!newIdStart)|(?!newIdEnd))oldId/g
|
|
21
|
+
if (replacement.includes(search)) {
|
|
22
|
+
const [before, _] = replacement.split(search);
|
|
23
|
+
// Matches a position in the string that is not immediately preceded by
|
|
24
|
+
// the string "before".
|
|
25
|
+
search = `(?<!${escapeRegexSpecialChars(before)})${escapeRegexSpecialChars(search)}`;
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
search = escapeRegexSpecialChars(search);
|
|
29
|
+
}
|
|
30
|
+
const dotToSlash = (update) => update.replaceAll(".", "\/");
|
|
31
|
+
const replaces = [
|
|
32
|
+
{
|
|
33
|
+
regexp: new RegExp(search, "g"),
|
|
34
|
+
replacement
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
regexp: new RegExp(dotToSlash(search), "g"),
|
|
38
|
+
replacement: dotToSlash(replacement)
|
|
39
|
+
}
|
|
40
|
+
];
|
|
41
|
+
const renamed = new Map();
|
|
42
|
+
files.forEach((content, filepath) => {
|
|
43
|
+
renamed.set(filepath, replaces.reduce((p, c) => p.replace(c.regexp, c.replacement), content));
|
|
44
|
+
});
|
|
45
|
+
return renamed;
|
|
46
|
+
}
|
|
47
|
+
exports.renameResources = renameResources;
|
|
16
48
|
//# sourceMappingURL=commonUtil.js.map
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export interface IJsonContent {
|
|
2
|
+
language: string;
|
|
3
|
+
json: any;
|
|
4
|
+
}
|
|
5
|
+
export interface IDiffProperty {
|
|
6
|
+
object: any;
|
|
7
|
+
property: string;
|
|
8
|
+
}
|
|
9
|
+
export interface IDiffJson {
|
|
10
|
+
json: any;
|
|
11
|
+
properties: Set<IDiffProperty>;
|
|
12
|
+
}
|
|
13
|
+
export default class JsonDiffUtil {
|
|
14
|
+
static diff(jsonA: any, jsonB: any): IDiffJson;
|
|
15
|
+
private static findDiffsAndRestoreStructure;
|
|
16
|
+
private static traverseDiffRecursive;
|
|
17
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const jsonDiff = require("json-diff");
|
|
4
|
+
const annotationDiffStructureError_1 = require("../model/annotationDiffStructureError");
|
|
5
|
+
class JsonDiffUtil {
|
|
6
|
+
static diff(jsonA, jsonB) {
|
|
7
|
+
const json = jsonDiff.diff(jsonA, jsonB, { full: true, sort: false });
|
|
8
|
+
const properties = this.findDiffsAndRestoreStructure(json);
|
|
9
|
+
return {
|
|
10
|
+
json,
|
|
11
|
+
properties
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
static findDiffsAndRestoreStructure(object) {
|
|
15
|
+
const properties = new Set();
|
|
16
|
+
this.traverseDiffRecursive(properties, { object }, "object");
|
|
17
|
+
return properties;
|
|
18
|
+
}
|
|
19
|
+
static traverseDiffRecursive(properties, object, property) {
|
|
20
|
+
const current = object[property];
|
|
21
|
+
if (typeof current !== "object") {
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
if (Array.isArray(current)) {
|
|
25
|
+
for (let i = 0; i < current.length; i++) {
|
|
26
|
+
const item = current[i];
|
|
27
|
+
if (["~", " ", "-", "+"].some(sign => item[0] === sign)) {
|
|
28
|
+
current[i] = item[1];
|
|
29
|
+
// This is a sign from json-diff plugin, that the property contains differences.
|
|
30
|
+
// We will remove these signs, to restore the original structure
|
|
31
|
+
if (item[0] === "~") {
|
|
32
|
+
this.traverseDiffRecursive(properties, item, 1);
|
|
33
|
+
}
|
|
34
|
+
else if (item[0] === "+" || item[0] === "-") {
|
|
35
|
+
throw new annotationDiffStructureError_1.default(item[1]);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
for (const key of Object.keys(current)) {
|
|
42
|
+
if (key == "__old" || key == "__new") {
|
|
43
|
+
properties.add({ object, property });
|
|
44
|
+
break;
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
this.traverseDiffRecursive(properties, current, key);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
exports.default = JsonDiffUtil;
|
|
54
|
+
//# sourceMappingURL=jsonDiffUtil.js.map
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
export default class ResourceUtil {
|
|
2
2
|
static getRootFolder(projectNamespace?: string): string;
|
|
3
|
+
static getResourcePath(projectNamespace?: string, ...paths: string[]): string;
|
|
3
4
|
static write(dir: string, files: Map<string, string>): Promise<void[]>;
|
|
4
5
|
static read(rootFolder: string, folder: string, files: Map<string, string>, exclude?: string[]): void;
|
|
6
|
+
static getString(resource: any): Promise<string>;
|
|
7
|
+
static setString(resource: any, str: string): void;
|
|
5
8
|
}
|
|
@@ -1,15 +1,19 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const fs = require("fs");
|
|
4
|
-
const
|
|
4
|
+
const path_1 = require("path");
|
|
5
5
|
const resourceFactory = require("@ui5/fs/lib/resourceFactory");
|
|
6
|
+
const UTF8 = "utf8";
|
|
6
7
|
class ResourceUtil {
|
|
7
8
|
static getRootFolder(projectNamespace) {
|
|
8
9
|
const newPath = ["/resources"];
|
|
9
10
|
if (projectNamespace) {
|
|
10
11
|
newPath.push(projectNamespace);
|
|
11
12
|
}
|
|
12
|
-
return
|
|
13
|
+
return path_1.posix.join(...newPath);
|
|
14
|
+
}
|
|
15
|
+
static getResourcePath(projectNamespace, ...paths) {
|
|
16
|
+
return path_1.posix.join(this.getRootFolder(projectNamespace), ...paths);
|
|
13
17
|
}
|
|
14
18
|
static write(dir, files) {
|
|
15
19
|
const fsTarget = resourceFactory.createAdapter({
|
|
@@ -26,7 +30,7 @@ class ResourceUtil {
|
|
|
26
30
|
static read(rootFolder, folder, files, exclude = []) {
|
|
27
31
|
const entries = fs.readdirSync(folder);
|
|
28
32
|
for (let entry of entries) {
|
|
29
|
-
const entryPath =
|
|
33
|
+
const entryPath = path_1.posix.join(folder, entry);
|
|
30
34
|
const stats = fs.lstatSync(entryPath);
|
|
31
35
|
if (stats.isFile() && !exclude.some(filepath => entryPath.endsWith(filepath))) {
|
|
32
36
|
const normalized = entryPath.substring(rootFolder.length);
|
|
@@ -37,6 +41,12 @@ class ResourceUtil {
|
|
|
37
41
|
}
|
|
38
42
|
}
|
|
39
43
|
}
|
|
44
|
+
static getString(resource) {
|
|
45
|
+
return resource.getBuffer().then((buffer) => buffer.toString(UTF8));
|
|
46
|
+
}
|
|
47
|
+
static setString(resource, str) {
|
|
48
|
+
resource.setBuffer(Buffer.from(str, UTF8));
|
|
49
|
+
}
|
|
40
50
|
}
|
|
41
51
|
exports.default = ResourceUtil;
|
|
42
52
|
//# sourceMappingURL=resourceUtil.js.map
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const convert = require("xml-js");
|
|
4
|
+
const XML_OPTIONS = { compact: true, spaces: 4 };
|
|
5
|
+
class XmlUtil {
|
|
6
|
+
static jsonToXml(json) {
|
|
7
|
+
return convert.json2xml(json, {
|
|
8
|
+
...XML_OPTIONS, attributeValueFn: (val) => val
|
|
9
|
+
.replaceAll(""", "\"")
|
|
10
|
+
.replaceAll("&", "&")
|
|
11
|
+
.replaceAll("<", "<")
|
|
12
|
+
.replaceAll("\"", """)
|
|
13
|
+
.replaceAll("'", "'")
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
static xmlToJson(xml) {
|
|
17
|
+
return JSON.parse(convert.xml2json(xml, {
|
|
18
|
+
...XML_OPTIONS, attributeValueFn: (val) => val
|
|
19
|
+
.replaceAll(""", "\"")
|
|
20
|
+
}));
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
exports.default = XmlUtil;
|
|
24
|
+
//# sourceMappingURL=xmlUtil.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ui5/task-adaptation",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.18",
|
|
4
4
|
"description": "Custom task for ui5-builder which allows building UI5 Flexibility Adaptation Projects for SAP BTP, Cloud Foundry environment",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"version": "git-chglog --next-tag v$npm_package_version -o CHANGELOG.md && git add CHANGELOG.md",
|
|
14
14
|
"postversion": "git push --follow-tags",
|
|
15
15
|
"release-note": "git-chglog -c .chglog/release-config.yml v$npm_package_version",
|
|
16
|
-
"rollup": "ts-node scripts/rollup.ts",
|
|
16
|
+
"rollup": "npx ts-node scripts/rollup.ts",
|
|
17
17
|
"build": "npm run rollup && tsc -p ./"
|
|
18
18
|
},
|
|
19
19
|
"repository": {
|
|
@@ -62,14 +62,14 @@
|
|
|
62
62
|
"@types/mocha": "^9.1.0",
|
|
63
63
|
"@types/rimraf": "^3.0.2",
|
|
64
64
|
"@types/semver": "^7.3.8",
|
|
65
|
-
"@types/sinon": "^10.0.
|
|
65
|
+
"@types/sinon": "^10.0.16",
|
|
66
66
|
"chai": "^4.3.4",
|
|
67
67
|
"chai-as-promised": "^7.1.1",
|
|
68
68
|
"chalk": "^4.1.2",
|
|
69
69
|
"mocha": "^9.2.0",
|
|
70
70
|
"mock-require": "^3.0.3",
|
|
71
71
|
"nyc": "^15.1.0",
|
|
72
|
-
"sinon": "^
|
|
72
|
+
"sinon": "^15.2.0",
|
|
73
73
|
"source-map-support": "^0.5.19",
|
|
74
74
|
"ts-node": "^10.4.0",
|
|
75
75
|
"typescript": "^4.3.5"
|
package/scripts/bundler.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { posix as path } from "path";
|
|
2
2
|
|
|
3
3
|
export default class URI {
|
|
4
4
|
constructor(value) {
|
|
@@ -6,7 +6,7 @@ export default class URI {
|
|
|
6
6
|
}
|
|
7
7
|
|
|
8
8
|
normalize() {
|
|
9
|
-
this.value = path.normalize(this.value);
|
|
9
|
+
this.value = path.normalize(this.value).replace(/\\/g, "/");
|
|
10
10
|
return this;
|
|
11
11
|
}
|
|
12
12
|
|