@ui5/task-adaptation 1.0.18 → 1.0.20
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 +1 -1
- package/dist/annotationManager.js +6 -4
- package/dist/appVariantManager.d.ts +9 -2
- package/dist/appVariantManager.js +25 -28
- package/dist/baseAppManager.d.ts +9 -2
- package/dist/baseAppManager.js +27 -7
- package/dist/buildStrategy.d.ts +2 -5
- package/dist/buildStrategy.js +2 -15
- package/dist/i18nManager.d.ts +1 -0
- package/dist/i18nManager.js +5 -2
- package/dist/index.js +4 -2
- package/dist/model/types.d.ts +8 -1
- package/dist/util/commonUtil.d.ts +3 -1
- package/dist/util/commonUtil.js +32 -17
- package/dist/util/i18nMerger.d.ts +28 -0
- package/dist/util/i18nMerger.js +104 -0
- package/package.json +3 -3
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.20...HEAD).
|
|
6
|
+
|
|
7
|
+
<a name="v1.0.20"></a>
|
|
8
|
+
## [v1.0.20] - 2024-01-02
|
|
9
|
+
|
|
10
|
+
<a name="v1.0.19"></a>
|
|
11
|
+
## [v1.0.19] - 2023-11-16
|
|
6
12
|
|
|
7
13
|
<a name="v1.0.18"></a>
|
|
8
14
|
## [v1.0.18] - 2023-11-10
|
|
@@ -52,6 +58,8 @@ A list of unreleased changes can be found [here](https://github.com/SAP/ui5-task
|
|
|
52
58
|
<a name="v1.0.0"></a>
|
|
53
59
|
## v1.0.0 - 2020-12-09
|
|
54
60
|
|
|
61
|
+
[v1.0.20]: https://github.com/SAP/ui5-task-adaptation/compare/v1.0.19...v1.0.20
|
|
62
|
+
[v1.0.19]: https://github.com/SAP/ui5-task-adaptation/compare/v1.0.18...v1.0.19
|
|
55
63
|
[v1.0.18]: https://github.com/SAP/ui5-task-adaptation/compare/v1.0.17...v1.0.18
|
|
56
64
|
[v1.0.17]: https://github.com/SAP/ui5-task-adaptation/compare/v1.0.16...v1.0.17
|
|
57
65
|
[v1.0.16]: https://github.com/SAP/ui5-task-adaptation/compare/v1.0.11...v1.0.16
|
|
@@ -13,7 +13,7 @@ export default class AnnotationManager {
|
|
|
13
13
|
private normalizeAppVariantId;
|
|
14
14
|
private createAnnotationFile;
|
|
15
15
|
private downloadAnnotations;
|
|
16
|
-
private
|
|
16
|
+
private updateManifestModel;
|
|
17
17
|
private updateManifestDataSources;
|
|
18
18
|
private createManifestModel;
|
|
19
19
|
private enhanceManifestModel;
|
|
@@ -17,7 +17,7 @@ class AnnotationManager {
|
|
|
17
17
|
this.abapRepoManager = abapRepoManager;
|
|
18
18
|
}
|
|
19
19
|
async process(renamedBaseAppManifest, languages) {
|
|
20
|
-
const { id } = baseAppManager_1.default.
|
|
20
|
+
const { id } = baseAppManager_1.default.getIdVersion(renamedBaseAppManifest);
|
|
21
21
|
baseAppManager_1.default.validateProperty(id, "sap.app/id");
|
|
22
22
|
const normalisedId = this.normalizeAppVariantId(id);
|
|
23
23
|
//TODO: switch to this after resolving @i18n custom model
|
|
@@ -35,7 +35,10 @@ class AnnotationManager {
|
|
|
35
35
|
metaInfo.push({ annotationFileName, annotationName });
|
|
36
36
|
}
|
|
37
37
|
if (metaInfo.length > 0) {
|
|
38
|
-
this.
|
|
38
|
+
this.updateManifestDataSources(renamedBaseAppManifest, metaInfo);
|
|
39
|
+
if (i18nManager.hasTranslations()) {
|
|
40
|
+
this.updateManifestModel(renamedBaseAppManifest, modelName, i18nPathName);
|
|
41
|
+
}
|
|
39
42
|
}
|
|
40
43
|
const i18nFiles = i18nManager.createFiles(i18nPathName);
|
|
41
44
|
return new Map([...annotationFiles, ...i18nFiles]);
|
|
@@ -58,12 +61,11 @@ class AnnotationManager {
|
|
|
58
61
|
annotationName: name
|
|
59
62
|
}));
|
|
60
63
|
}
|
|
61
|
-
|
|
64
|
+
updateManifestModel(renamedBaseAppManifest, modelName, i18nPathName) {
|
|
62
65
|
const uri = `${i18nPathName}/i18n.properties`;
|
|
63
66
|
this.enhanceManifestModel(renamedBaseAppManifest, modelName, uri);
|
|
64
67
|
//TODO: switch to this after resolving @i18n custom model
|
|
65
68
|
//this.createManifestModel(renamedBaseAppManifest, modelName, uri);
|
|
66
|
-
this.updateManifestDataSources(renamedBaseAppManifest, metaInfo);
|
|
67
69
|
}
|
|
68
70
|
updateManifestDataSources(renamedBaseAppManifest, metaInfo) {
|
|
69
71
|
const dataSources = renamedBaseAppManifest[SAPAPP]?.dataSources;
|
|
@@ -3,8 +3,15 @@ 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
5
|
static renameChanges(appVariantResources: any[], projectNamespace: string, appVariantInfo: IAppVariantInfo): Promise<void>;
|
|
6
|
+
private static isManifestChange;
|
|
7
|
+
private static isManifestAppVariant;
|
|
6
8
|
static getAppVariantInfo(appVariantResources: any[]): Promise<IAppVariantInfo>;
|
|
7
|
-
static writeI18nToModule(resource: any, projectNamespace: string, i18nBundleName: string): void;
|
|
8
9
|
private static omitFiles;
|
|
9
|
-
|
|
10
|
+
/**
|
|
11
|
+
* We need to add texts properties to changes because not all have texts property.
|
|
12
|
+
* Changes without texts property can causes issues in bundle.js
|
|
13
|
+
* This is needed for now, and will be removed as soon as change merger in openUI5 is updated
|
|
14
|
+
* @param changes
|
|
15
|
+
*/
|
|
16
|
+
private static patchMissingTextsNode;
|
|
10
17
|
}
|
|
@@ -4,19 +4,14 @@ const commonUtil_1 = require("./util/commonUtil");
|
|
|
4
4
|
const resourceUtil_1 = require("./util/resourceUtil");
|
|
5
5
|
const path_1 = require("path");
|
|
6
6
|
const log = require("@ui5/logger").getLogger("@ui5/task-adaptation::AppVariantManager");
|
|
7
|
-
const OMIT_FILES = ["manifest.appdescr_variant"];
|
|
8
|
-
const OMIT_FOLDERS = [];
|
|
9
7
|
const EXTENSIONS = "js,json,xml,html,properties,change,appdescr_variant";
|
|
10
|
-
const MANIFEST_APP_VARIANT = "manifest.appdescr_variant";
|
|
11
8
|
class AppVariantManager {
|
|
12
9
|
static async process(appVariantResources, projectNamespace, taskUtil) {
|
|
13
10
|
const appVariantInfo = await this.getAppVariantInfo(appVariantResources);
|
|
14
|
-
const i18nBundleName = (0, commonUtil_1.replaceDots)(appVariantInfo.id);
|
|
15
11
|
for (const resource of appVariantResources) {
|
|
16
|
-
this.writeI18nToModule(resource, projectNamespace, i18nBundleName);
|
|
17
12
|
this.omitFiles(resource, taskUtil);
|
|
18
13
|
}
|
|
19
|
-
this.
|
|
14
|
+
this.patchMissingTextsNode(appVariantInfo?.manifest?.content ?? []);
|
|
20
15
|
await this.renameChanges(appVariantResources, projectNamespace, appVariantInfo);
|
|
21
16
|
return appVariantInfo;
|
|
22
17
|
}
|
|
@@ -41,20 +36,26 @@ class AppVariantManager {
|
|
|
41
36
|
resourceUtil_1.default.setString(resource, renamedContent);
|
|
42
37
|
});
|
|
43
38
|
}
|
|
44
|
-
static
|
|
39
|
+
static isManifestChange(resource) {
|
|
45
40
|
const changesManifestFolder = path_1.posix.join("changes", "manifest");
|
|
41
|
+
const dirname = path_1.posix.dirname(resource.getPath());
|
|
42
|
+
return dirname.endsWith(changesManifestFolder);
|
|
43
|
+
}
|
|
44
|
+
static isManifestAppVariant(resource) {
|
|
45
|
+
const MANIFEST_APP_VARIANT = "manifest.appdescr_variant";
|
|
46
|
+
const basename = path_1.posix.basename(resource.getPath());
|
|
47
|
+
return basename === MANIFEST_APP_VARIANT;
|
|
48
|
+
}
|
|
49
|
+
static async getAppVariantInfo(appVariantResources) {
|
|
46
50
|
let manifest;
|
|
47
51
|
const manifestChanges = [];
|
|
48
52
|
for (const resource of appVariantResources) {
|
|
49
|
-
|
|
50
|
-
const dirname = path_1.posix.dirname(resource.getPath());
|
|
51
|
-
const basename = path_1.posix.basename(resourcePath);
|
|
52
|
-
if (basename === MANIFEST_APP_VARIANT) {
|
|
53
|
+
if (this.isManifestAppVariant(resource)) {
|
|
53
54
|
manifest = await resourceUtil_1.default.getString(resource).then(JSON.parse);
|
|
54
55
|
}
|
|
55
|
-
else if (
|
|
56
|
-
const
|
|
57
|
-
manifestChanges.push(JSON.parse(
|
|
56
|
+
else if (this.isManifestChange(resource)) {
|
|
57
|
+
const content = await resourceUtil_1.default.getString(resource);
|
|
58
|
+
manifestChanges.push(JSON.parse(content));
|
|
58
59
|
}
|
|
59
60
|
}
|
|
60
61
|
if (manifest) {
|
|
@@ -67,28 +68,24 @@ class AppVariantManager {
|
|
|
67
68
|
}
|
|
68
69
|
throw new Error("Adaptation project should contain manifest.appdescr_variant");
|
|
69
70
|
}
|
|
70
|
-
static writeI18nToModule(resource, projectNamespace, i18nBundleName) {
|
|
71
|
-
if (path_1.posix.extname(resource.getPath()) === ".properties") {
|
|
72
|
-
let rootFolder = resourceUtil_1.default.getRootFolder(projectNamespace);
|
|
73
|
-
resource.setPath(path_1.posix.join(rootFolder, i18nBundleName, resource.getPath().substring(rootFolder.length)));
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
71
|
static omitFiles(resource, taskUtil) {
|
|
77
|
-
|
|
78
|
-
const filename = path_1.posix.basename(resource.getPath());
|
|
79
|
-
if (OMIT_FILES.includes(filename) ||
|
|
80
|
-
OMIT_FOLDERS.some(folder => dirname.endsWith(folder))) {
|
|
72
|
+
if (this.isManifestAppVariant(resource) || this.isManifestChange(resource)) {
|
|
81
73
|
taskUtil.setTag(resource, taskUtil.STANDARD_TAGS.OmitFromBuildResult, true);
|
|
82
74
|
}
|
|
83
75
|
}
|
|
84
|
-
|
|
76
|
+
/**
|
|
77
|
+
* We need to add texts properties to changes because not all have texts property.
|
|
78
|
+
* Changes without texts property can causes issues in bundle.js
|
|
79
|
+
* This is needed for now, and will be removed as soon as change merger in openUI5 is updated
|
|
80
|
+
* @param changes
|
|
81
|
+
*/
|
|
82
|
+
static patchMissingTextsNode(changes) {
|
|
85
83
|
log.verbose("Adjusting appdescr_ui5_addNewModelEnhanceWith with module");
|
|
86
84
|
for (const change of changes) {
|
|
87
85
|
if (change.changeType === "appdescr_ui5_addNewModelEnhanceWith") {
|
|
88
|
-
if (!change.texts) {
|
|
89
|
-
change.texts = { i18n:
|
|
86
|
+
if (!change.texts && change.content?.bundleUrl) {
|
|
87
|
+
change.texts = { i18n: change.content.bundleUrl };
|
|
90
88
|
}
|
|
91
|
-
change.texts.i18n = i18nBundleName + "/" + change.texts.i18n;
|
|
92
89
|
}
|
|
93
90
|
}
|
|
94
91
|
}
|
package/dist/baseAppManager.d.ts
CHANGED
|
@@ -4,17 +4,24 @@ export interface IBaseAppResources {
|
|
|
4
4
|
resources: any[];
|
|
5
5
|
manifestInfo: IManifestInfo;
|
|
6
6
|
}
|
|
7
|
-
export interface
|
|
7
|
+
export interface IManifestIdVersion {
|
|
8
8
|
id: string;
|
|
9
9
|
version: string;
|
|
10
10
|
}
|
|
11
|
+
export interface IManifestInfo extends IManifestIdVersion {
|
|
12
|
+
i18nPath: string;
|
|
13
|
+
}
|
|
11
14
|
export default class BaseAppManager {
|
|
12
15
|
static process(baseAppFiles: Map<string, string>, appVariantInfo: IAppVariantInfo, options: IProjectOptions, processor: IProcessor): Promise<IBaseAppResources>;
|
|
13
16
|
private static updateAdaptationProperties;
|
|
17
|
+
static getIdVersion(manifest: any): IManifestIdVersion;
|
|
14
18
|
static getManifestInfo(manifest: any): IManifestInfo;
|
|
19
|
+
private static extractI18nPathFromManifest;
|
|
20
|
+
private static extractI18NFromBundleName;
|
|
21
|
+
private static extractI18NFromBundleUrl;
|
|
15
22
|
private static getBaseAppManifest;
|
|
16
23
|
private static fillAppVariantIdHierarchy;
|
|
17
24
|
static validateProperty(value: string, property: string): void;
|
|
18
|
-
static applyDescriptorChanges(baseAppManifest: any, appVariantInfo: IAppVariantInfo
|
|
25
|
+
static applyDescriptorChanges(baseAppManifest: any, appVariantInfo: IAppVariantInfo): Promise<void>;
|
|
19
26
|
static writeToWorkspace(baseAppFiles: Map<string, string>, projectNamespace: string): any[];
|
|
20
27
|
}
|
package/dist/baseAppManager.js
CHANGED
|
@@ -4,20 +4,20 @@ const commonUtil_1 = require("./util/commonUtil");
|
|
|
4
4
|
const buildStrategy_1 = require("./buildStrategy");
|
|
5
5
|
const resourceUtil_1 = require("./util/resourceUtil");
|
|
6
6
|
const path_1 = require("path");
|
|
7
|
-
const
|
|
7
|
+
const commonUtil_2 = require("./util/commonUtil");
|
|
8
|
+
const { RegistrationBuild, Applier, Change } = require("../dist/bundle");
|
|
8
9
|
const resourceFactory = require("@ui5/fs/lib/resourceFactory");
|
|
9
10
|
const log = require("@ui5/logger").getLogger("@ui5/task-adaptation::BaseAppManager");
|
|
10
11
|
class BaseAppManager {
|
|
11
12
|
static async process(baseAppFiles, appVariantInfo, options, processor) {
|
|
12
13
|
const baseAppManifest = this.getBaseAppManifest(baseAppFiles);
|
|
13
|
-
const { id, version } = this.
|
|
14
|
+
const { id, version } = this.getIdVersion(baseAppManifest.content);
|
|
14
15
|
const renamedBaseAppFiles = (0, commonUtil_1.renameResources)(baseAppFiles, appVariantInfo.reference, appVariantInfo.id);
|
|
15
16
|
const { filepath, content } = this.getBaseAppManifest(renamedBaseAppFiles);
|
|
16
17
|
await processor.updateLandscapeSpecificContent(content, renamedBaseAppFiles);
|
|
17
18
|
this.fillAppVariantIdHierarchy(processor, id, version, content);
|
|
18
19
|
this.updateAdaptationProperties(content);
|
|
19
|
-
|
|
20
|
-
await this.applyDescriptorChanges(content, appVariantInfo, i18nBundleName);
|
|
20
|
+
await this.applyDescriptorChanges(content, appVariantInfo);
|
|
21
21
|
renamedBaseAppFiles.set(filepath, JSON.stringify(content));
|
|
22
22
|
return {
|
|
23
23
|
resources: this.writeToWorkspace(renamedBaseAppFiles, options.projectNamespace),
|
|
@@ -33,11 +33,31 @@ class BaseAppManager {
|
|
|
33
33
|
}
|
|
34
34
|
content["sap.ui5"].isCloudDevAdaptation = true;
|
|
35
35
|
}
|
|
36
|
-
static
|
|
36
|
+
static getIdVersion(manifest) {
|
|
37
37
|
const id = manifest["sap.app"]?.id;
|
|
38
38
|
const version = manifest["sap.app"]?.applicationVersion?.version;
|
|
39
39
|
return { id, version };
|
|
40
40
|
}
|
|
41
|
+
static getManifestInfo(manifest) {
|
|
42
|
+
const { id, version } = this.getIdVersion(manifest);
|
|
43
|
+
const i18nNode = manifest["sap.app"]?.i18n;
|
|
44
|
+
const i18nPath = this.extractI18nPathFromManifest(id, i18nNode);
|
|
45
|
+
return { id, version, i18nPath };
|
|
46
|
+
}
|
|
47
|
+
static extractI18nPathFromManifest(sapAppId, i18nNode) {
|
|
48
|
+
if (typeof i18nNode === "object") {
|
|
49
|
+
return i18nNode["bundleUrl"] ? this.extractI18NFromBundleUrl(i18nNode) : this.extractI18NFromBundleName(i18nNode, sapAppId);
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
return `${sapAppId?.replaceAll(".", "/")}/${i18nNode}`;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
static extractI18NFromBundleName(i18nNode, sapAppId) {
|
|
56
|
+
return i18nNode["bundleName"].replace(sapAppId, "").replaceAll(".", "/").substring(1);
|
|
57
|
+
}
|
|
58
|
+
static extractI18NFromBundleUrl(i18nNode) {
|
|
59
|
+
return (0, commonUtil_2.removePropertiesExtension)(i18nNode["bundleUrl"]);
|
|
60
|
+
}
|
|
41
61
|
static getBaseAppManifest(baseAppFiles) {
|
|
42
62
|
let filepath = [...baseAppFiles.keys()].find(filepath => filepath.endsWith("manifest.json"));
|
|
43
63
|
if (filepath) {
|
|
@@ -66,9 +86,9 @@ class BaseAppManager {
|
|
|
66
86
|
throw new Error(`Original application manifest should have ${property}`);
|
|
67
87
|
}
|
|
68
88
|
}
|
|
69
|
-
static async applyDescriptorChanges(baseAppManifest, appVariantInfo
|
|
89
|
+
static async applyDescriptorChanges(baseAppManifest, appVariantInfo) {
|
|
70
90
|
log.verbose("Applying appVariant changes");
|
|
71
|
-
const strategy = new buildStrategy_1.default(RegistrationBuild
|
|
91
|
+
const strategy = new buildStrategy_1.default(RegistrationBuild);
|
|
72
92
|
const { manifest } = appVariantInfo;
|
|
73
93
|
const allChanges = [
|
|
74
94
|
...manifest.content,
|
package/dist/buildStrategy.d.ts
CHANGED
|
@@ -1,10 +1,7 @@
|
|
|
1
|
-
import { IChangeText } from "./model/types";
|
|
2
1
|
export default class BuildStrategy {
|
|
3
2
|
private registrationBuild;
|
|
4
|
-
|
|
5
|
-
private i18nBundleName;
|
|
6
|
-
constructor(registrationBuild: any, applyUtil: any, i18nBundleName: string);
|
|
3
|
+
constructor(registrationBuild: any);
|
|
7
4
|
registry(): Promise<any>;
|
|
8
5
|
handleError(error: any): void;
|
|
9
|
-
processTexts(manifest: any
|
|
6
|
+
processTexts(manifest: any): any;
|
|
10
7
|
}
|
package/dist/buildStrategy.js
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
class BuildStrategy {
|
|
4
|
-
constructor(registrationBuild
|
|
4
|
+
constructor(registrationBuild) {
|
|
5
5
|
this.registrationBuild = registrationBuild;
|
|
6
|
-
this.applyUtil = applyUtil;
|
|
7
|
-
this.i18nBundleName = i18nBundleName;
|
|
8
6
|
}
|
|
9
7
|
registry() {
|
|
10
8
|
return Promise.resolve(this.registrationBuild);
|
|
@@ -12,21 +10,10 @@ class BuildStrategy {
|
|
|
12
10
|
handleError(error) {
|
|
13
11
|
throw error;
|
|
14
12
|
}
|
|
15
|
-
processTexts(manifest
|
|
13
|
+
processTexts(manifest) {
|
|
16
14
|
if (typeof manifest["sap.app"].i18n === "string") {
|
|
17
15
|
manifest["sap.app"].i18n = { bundleUrl: manifest["sap.app"].i18n };
|
|
18
16
|
}
|
|
19
|
-
if (manifest["sap.app"].i18n == null) {
|
|
20
|
-
manifest["sap.app"].i18n = {};
|
|
21
|
-
}
|
|
22
|
-
if (manifest["sap.app"].i18n.enhanceWith == null) {
|
|
23
|
-
manifest["sap.app"].i18n.enhanceWith = [];
|
|
24
|
-
}
|
|
25
|
-
const bundleName = this.applyUtil.formatBundleName(manifest["sap.app"].id + "." + this.i18nBundleName, changeTexts.i18n);
|
|
26
|
-
const doubles = manifest["sap.app"].i18n.enhanceWith.some((entry) => entry.bundleName === bundleName);
|
|
27
|
-
if (!doubles) {
|
|
28
|
-
manifest["sap.app"].i18n.enhanceWith.push({ bundleName: bundleName });
|
|
29
|
-
}
|
|
30
17
|
return manifest;
|
|
31
18
|
}
|
|
32
19
|
}
|
package/dist/i18nManager.d.ts
CHANGED
package/dist/i18nManager.js
CHANGED
|
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.I18nFileContent = exports.PropertyValue = void 0;
|
|
4
4
|
const jsonDiffUtil_1 = require("./util/jsonDiffUtil");
|
|
5
5
|
const annotationDiffStructureError_1 = require("./model/annotationDiffStructureError");
|
|
6
|
-
const
|
|
6
|
+
const posix_1 = require("path/posix"); // Ensure standardized dir separators to ensure Windows compatibility
|
|
7
7
|
// To generate keys, english language is more common, so compare all other
|
|
8
8
|
// languages with it
|
|
9
9
|
const DEFAULT_LANGUAGES = ["", "EN", "EN_US", "EN_GB"];
|
|
@@ -74,7 +74,7 @@ class I18nFileContent {
|
|
|
74
74
|
if (language) {
|
|
75
75
|
filename += "_" + language.toLowerCase();
|
|
76
76
|
}
|
|
77
|
-
const filepath = (0,
|
|
77
|
+
const filepath = (0, posix_1.join)(i18nPathName, filename + ".properties");
|
|
78
78
|
files.set(filepath, [...i18nLines].map(([key, value]) => `${key}=${value}`).join("\n"));
|
|
79
79
|
});
|
|
80
80
|
}
|
|
@@ -195,6 +195,9 @@ class I18nManager {
|
|
|
195
195
|
this.existingKeys.add(key);
|
|
196
196
|
return key;
|
|
197
197
|
}
|
|
198
|
+
hasTranslations() {
|
|
199
|
+
return this.i18nFileContent.hasTranslations();
|
|
200
|
+
}
|
|
198
201
|
initPropertyValues(diff, propertyValues) {
|
|
199
202
|
propertyValues.forEach(prop => prop.set(diff, this.references));
|
|
200
203
|
}
|
package/dist/index.js
CHANGED
|
@@ -4,6 +4,7 @@ const dotenv = require("dotenv");
|
|
|
4
4
|
const appVariantManager_1 = require("./appVariantManager");
|
|
5
5
|
const baseAppManager_1 = require("./baseAppManager");
|
|
6
6
|
const processor_1 = require("./processors/processor");
|
|
7
|
+
const i18nMerger_1 = require("./util/i18nMerger");
|
|
7
8
|
/**
|
|
8
9
|
* Creates an appVariant bundle from the provided resources.
|
|
9
10
|
*/
|
|
@@ -14,8 +15,9 @@ module.exports = ({ workspace, options, taskUtil }) => {
|
|
|
14
15
|
const appVariantResources = await appVariantManager_1.default.getAppVariantResources(workspace);
|
|
15
16
|
const appVariantInfo = await appVariantManager_1.default.process(appVariantResources, options.projectNamespace, taskUtil);
|
|
16
17
|
const baseAppFiles = await processor.getBaseAppFiles(appVariantInfo.reference);
|
|
17
|
-
const { resources } = await baseAppManager_1.default.process(baseAppFiles, appVariantInfo, options, processor);
|
|
18
|
-
await
|
|
18
|
+
const { resources, manifestInfo } = await baseAppManager_1.default.process(baseAppFiles, appVariantInfo, options, processor);
|
|
19
|
+
const mergedResources = await i18nMerger_1.default.mergeI18NFiles(resources, appVariantResources, options.projectNamespace, manifestInfo.i18nPath, appVariantInfo, taskUtil);
|
|
20
|
+
await Promise.all(mergedResources.map(resource => workspace.write(resource)));
|
|
19
21
|
}
|
|
20
22
|
return process(workspace, taskUtil);
|
|
21
23
|
};
|
package/dist/model/types.d.ts
CHANGED
|
@@ -69,12 +69,19 @@ export interface IAppVariantManifest {
|
|
|
69
69
|
}
|
|
70
70
|
export interface IChange {
|
|
71
71
|
changeType: string;
|
|
72
|
-
texts
|
|
72
|
+
texts?: IChangeText;
|
|
73
73
|
layer?: string;
|
|
74
|
+
content?: IChangeContent;
|
|
74
75
|
}
|
|
75
76
|
export interface IChangeText {
|
|
76
77
|
i18n: string;
|
|
77
78
|
}
|
|
79
|
+
export interface IChangeContent {
|
|
80
|
+
bundleUrl?: string;
|
|
81
|
+
fallbackLocale?: string;
|
|
82
|
+
modelId?: string;
|
|
83
|
+
supportedLocales?: string[];
|
|
84
|
+
}
|
|
78
85
|
export interface ITaskParameters {
|
|
79
86
|
workspace: any;
|
|
80
87
|
options: IProjectOptions;
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
-
export declare function
|
|
1
|
+
export declare function dotToUnderscore(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 escapeRegex(update: string): string;
|
|
3
4
|
export declare function renameResources(files: Map<string, string>, search: string, replacement: string): Map<string, string>;
|
|
5
|
+
export declare function removePropertiesExtension(filePath: string): string;
|
package/dist/util/commonUtil.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.renameResources = exports.validateObject = exports.
|
|
4
|
-
function
|
|
3
|
+
exports.removePropertiesExtension = exports.renameResources = exports.escapeRegex = exports.validateObject = exports.dotToUnderscore = void 0;
|
|
4
|
+
function dotToUnderscore(value) {
|
|
5
5
|
return value.replace(/\./g, "_");
|
|
6
6
|
}
|
|
7
|
-
exports.
|
|
7
|
+
exports.dotToUnderscore = dotToUnderscore;
|
|
8
8
|
function validateObject(options, properties, message) {
|
|
9
9
|
for (const property of properties) {
|
|
10
10
|
if (!options[property]) {
|
|
@@ -13,36 +13,51 @@ function validateObject(options, properties, message) {
|
|
|
13
13
|
}
|
|
14
14
|
}
|
|
15
15
|
exports.validateObject = validateObject;
|
|
16
|
+
function escapeRegex(update) {
|
|
17
|
+
return update.replaceAll(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
|
|
18
|
+
}
|
|
19
|
+
exports.escapeRegex = escapeRegex;
|
|
16
20
|
function renameResources(files, search, replacement) {
|
|
17
|
-
const escapeRegexSpecialChars = (update) => update.replaceAll(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
|
|
18
21
|
// The current regex works if the old Id is contained in the new Id, given
|
|
19
22
|
// that they do not have the same beginning.
|
|
20
23
|
// more complete alternative: /((?<!newIdStart)|(?!newIdEnd))oldId/g
|
|
24
|
+
let escapedSearch;
|
|
21
25
|
if (replacement.includes(search)) {
|
|
22
26
|
const [before, _] = replacement.split(search);
|
|
23
27
|
// Matches a position in the string that is not immediately preceded by
|
|
24
|
-
// the string "before".
|
|
25
|
-
|
|
28
|
+
// the string "before". Since we won't replace anyway, we should also
|
|
29
|
+
// ignore one with the slashes.
|
|
30
|
+
const escapedBefore = escapeRegex(before).replaceAll("\\.", "[\\./]");
|
|
31
|
+
escapedSearch = `(?<!${escapedBefore})${escapeRegex(search)}`;
|
|
26
32
|
}
|
|
27
33
|
else {
|
|
28
|
-
|
|
34
|
+
escapedSearch = escapeRegex(search);
|
|
29
35
|
}
|
|
30
36
|
const dotToSlash = (update) => update.replaceAll(".", "\/");
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
},
|
|
36
|
-
{
|
|
37
|
-
regexp: new RegExp(dotToSlash(search), "g"),
|
|
38
|
-
replacement: dotToSlash(replacement)
|
|
37
|
+
const replace = (content) => content.replace(new RegExp(escapedSearch, "g"), replacement);
|
|
38
|
+
const replaceWithSlashesOnly = (content) => {
|
|
39
|
+
if (!search.includes(".")) {
|
|
40
|
+
return content;
|
|
39
41
|
}
|
|
40
|
-
|
|
42
|
+
let searchWithSlashes = dotToSlash(escapedSearch);
|
|
43
|
+
return content.replace(new RegExp(searchWithSlashes, "g"), dotToSlash(replacement));
|
|
44
|
+
};
|
|
41
45
|
const renamed = new Map();
|
|
42
46
|
files.forEach((content, filepath) => {
|
|
43
|
-
|
|
47
|
+
// Finds the id with dots (test.id) or without dots (id) and replaces it
|
|
48
|
+
content = replace(content);
|
|
49
|
+
// Only if the id has dots, these dots will be replaced with slashes
|
|
50
|
+
// first, and then it will search for the id with slashes and replace
|
|
51
|
+
// with the appVariantId also with slashes
|
|
52
|
+
content = replaceWithSlashesOnly(content);
|
|
53
|
+
renamed.set(filepath, content);
|
|
44
54
|
});
|
|
45
55
|
return renamed;
|
|
46
56
|
}
|
|
47
57
|
exports.renameResources = renameResources;
|
|
58
|
+
function removePropertiesExtension(filePath) {
|
|
59
|
+
const lastIndexOf = filePath.lastIndexOf(".properties");
|
|
60
|
+
return filePath.substring(0, lastIndexOf);
|
|
61
|
+
}
|
|
62
|
+
exports.removePropertiesExtension = removePropertiesExtension;
|
|
48
63
|
//# sourceMappingURL=commonUtil.js.map
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { IAppVariantInfo } from "../model/types";
|
|
2
|
+
export default class I18NMerger {
|
|
3
|
+
static analyzeAppVariantManifestChanges(rootFolder: string, tranlsationRegexPattern: string, appVariantInfo: IAppVariantInfo): {
|
|
4
|
+
mergePathsRegex: RegExp[];
|
|
5
|
+
copyPathsRegex: RegExp[];
|
|
6
|
+
};
|
|
7
|
+
static mergeI18NFiles(baseAppResources: any[], appVariantResources: any[], projectNamespace: string, baseAppManifestI18NPath: string, appVariantInfo: IAppVariantInfo, taskUtil: any): Promise<any[]>;
|
|
8
|
+
/**
|
|
9
|
+
* Merge/Append base property file with property file from app variant
|
|
10
|
+
* FIXME Currently merge could duplicate keys which causes undefined
|
|
11
|
+
* behavior => Existing keys which are in merge content must be removed =>
|
|
12
|
+
* Actually only descriptor texts are relevant for merge which always have
|
|
13
|
+
* app variant Id as prefix => If we filter on them we do not need to remove
|
|
14
|
+
* existing overwritten keys (as there should be none)
|
|
15
|
+
*/
|
|
16
|
+
private static mergePropertiesFiles;
|
|
17
|
+
/**
|
|
18
|
+
* update the path of app variant property file so it will be copied into
|
|
19
|
+
<app_variant_id> folder
|
|
20
|
+
*/
|
|
21
|
+
private static moveToAppVarSubfolder;
|
|
22
|
+
/**
|
|
23
|
+
* create new i18n file in case e.g. translation file does not exist in base
|
|
24
|
+
* app but in variant and copy of translation file is needed
|
|
25
|
+
*/
|
|
26
|
+
private static createFile;
|
|
27
|
+
private static mergeFiles;
|
|
28
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const commonUtil_1 = require("./commonUtil");
|
|
4
|
+
const resourceUtil_1 = require("./resourceUtil");
|
|
5
|
+
const path_1 = require("path");
|
|
6
|
+
const Resource = require("@ui5/fs/lib/Resource");
|
|
7
|
+
class I18NMerger {
|
|
8
|
+
static analyzeAppVariantManifestChanges(rootFolder, tranlsationRegexPattern, appVariantInfo) {
|
|
9
|
+
// check which files need to be copied and which files need to be merged and copied
|
|
10
|
+
// this is necessary because lrep does not support multiple enhanceWith with multiple locations
|
|
11
|
+
const mergePaths = new Set();
|
|
12
|
+
const copyPaths = new Set();
|
|
13
|
+
const { manifest, manifestChanges } = appVariantInfo;
|
|
14
|
+
manifestChanges.concat(manifest.content).forEach((change) => {
|
|
15
|
+
const i18nPathWithExtension = change.content?.bundleUrl || change.texts?.i18n;
|
|
16
|
+
if (i18nPathWithExtension) {
|
|
17
|
+
// build regex to match specific + language related files
|
|
18
|
+
const i18nPath = (0, commonUtil_1.removePropertiesExtension)(i18nPathWithExtension);
|
|
19
|
+
const resourcePath = path_1.posix.join(rootFolder, i18nPath);
|
|
20
|
+
const regex = new RegExp((0, commonUtil_1.escapeRegex)(resourcePath) + tranlsationRegexPattern);
|
|
21
|
+
if (change.changeType.includes("addNewModelEnhanceWith")) {
|
|
22
|
+
copyPaths.add(regex);
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
mergePaths.add(regex);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
return { mergePathsRegex: [...mergePaths.values()], copyPathsRegex: [...copyPaths.values()] };
|
|
30
|
+
}
|
|
31
|
+
static async mergeI18NFiles(baseAppResources, appVariantResources, projectNamespace, baseAppManifestI18NPath, appVariantInfo, taskUtil) {
|
|
32
|
+
const aggregatedResourceFilesMap = new Map(baseAppResources.map(baseAppResource => [baseAppResource.getPath(), baseAppResource]));
|
|
33
|
+
const i18nTargetFolder = (0, commonUtil_1.dotToUnderscore)(appVariantInfo.id);
|
|
34
|
+
const rootFolder = resourceUtil_1.default.getRootFolder(projectNamespace);
|
|
35
|
+
const tranlsationRegexPattern = "((_[a-z]{2,3})?(_[a-zA-Z]{2,3}(_[a-zA-Z]{2,20})?)?)\.properties$";
|
|
36
|
+
const { copyPathsRegex: copyPathsValues, mergePathsRegex: mergePathsValues } = this.analyzeAppVariantManifestChanges(rootFolder, tranlsationRegexPattern, appVariantInfo);
|
|
37
|
+
for (const appVariantResource of appVariantResources) {
|
|
38
|
+
const appVariantResourcePath = appVariantResource.getPath();
|
|
39
|
+
if (appVariantResourcePath.endsWith(".properties")) {
|
|
40
|
+
// merge/copy logic
|
|
41
|
+
// check if file matches with regex in merge/copy
|
|
42
|
+
const mergePathMatch = mergePathsValues.map(path => appVariantResourcePath.match(path)).find(match => match);
|
|
43
|
+
const shouldMergeFile = !!mergePathMatch;
|
|
44
|
+
const shouldCopyFile = copyPathsValues.map(path => appVariantResourcePath.match(path)).find(match => match);
|
|
45
|
+
if (shouldMergeFile) {
|
|
46
|
+
let baseAppI18NPath = `${rootFolder}/${baseAppManifestI18NPath}${mergePathMatch[1] || ""}.properties`;
|
|
47
|
+
await this.mergePropertiesFiles(aggregatedResourceFilesMap, appVariantResource, baseAppI18NPath);
|
|
48
|
+
}
|
|
49
|
+
// Resource for to be copied file already exists so we only have to adjust path
|
|
50
|
+
// Otherwise we have to omit it. We always change the path to avoid omitting a base app file
|
|
51
|
+
this.moveToAppVarSubfolder(appVariantResource, rootFolder, i18nTargetFolder);
|
|
52
|
+
if (!shouldCopyFile) {
|
|
53
|
+
taskUtil.setTag(appVariantResource, taskUtil.STANDARD_TAGS.OmitFromBuildResult, true);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
aggregatedResourceFilesMap.set(appVariantResource.getPath(), appVariantResource);
|
|
57
|
+
}
|
|
58
|
+
return Array.from(aggregatedResourceFilesMap.values());
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Merge/Append base property file with property file from app variant
|
|
62
|
+
* FIXME Currently merge could duplicate keys which causes undefined
|
|
63
|
+
* behavior => Existing keys which are in merge content must be removed =>
|
|
64
|
+
* Actually only descriptor texts are relevant for merge which always have
|
|
65
|
+
* app variant Id as prefix => If we filter on them we do not need to remove
|
|
66
|
+
* existing overwritten keys (as there should be none)
|
|
67
|
+
*/
|
|
68
|
+
static async mergePropertiesFiles(aggregatedResourceFilesMap, variantResource, baseAppI18NPath) {
|
|
69
|
+
const baseAppI18NFile = aggregatedResourceFilesMap.get(baseAppI18NPath);
|
|
70
|
+
if (baseAppI18NFile) {
|
|
71
|
+
await this.mergeFiles(baseAppI18NFile, variantResource);
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
// create the merge target file if it missing in base app. Maybe the language does not exist in the base app.
|
|
75
|
+
// Since the file might also be copied we do not just change the path of it but create another resource
|
|
76
|
+
await this.createFile(aggregatedResourceFilesMap, baseAppI18NPath, variantResource);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* update the path of app variant property file so it will be copied into
|
|
81
|
+
<app_variant_id> folder
|
|
82
|
+
*/
|
|
83
|
+
static moveToAppVarSubfolder(variantResource, rootFolder, i18nBundleName) {
|
|
84
|
+
const relativeFilePath = variantResource.getPath().substring(rootFolder.length);
|
|
85
|
+
const newResourcePath = path_1.posix.join(rootFolder, i18nBundleName, relativeFilePath);
|
|
86
|
+
variantResource.setPath(newResourcePath);
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* create new i18n file in case e.g. translation file does not exist in base
|
|
90
|
+
* app but in variant and copy of translation file is needed
|
|
91
|
+
*/
|
|
92
|
+
static async createFile(aggregatedResourceFilesMap, path, resource) {
|
|
93
|
+
const createdFile = await resource.clone();
|
|
94
|
+
createdFile.setPath(path);
|
|
95
|
+
aggregatedResourceFilesMap.set(path, createdFile);
|
|
96
|
+
}
|
|
97
|
+
static async mergeFiles(baseFile, variantFile) {
|
|
98
|
+
const variantFileContent = await variantFile.getString();
|
|
99
|
+
const mergedFileContent = await baseFile.getString();
|
|
100
|
+
baseFile.setString(`${mergedFileContent}\n\n#App variant specific text file\n\n${variantFileContent}`);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
exports.default = I18NMerger;
|
|
104
|
+
//# sourceMappingURL=i18nMerger.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.20",
|
|
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": {
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"coverage-verbose": "nyc --reporter=html --reporter=text mocha -r ts-node/register 'test/**/*.spec.ts'",
|
|
12
12
|
"preversion": "npm test",
|
|
13
13
|
"version": "git-chglog --next-tag v$npm_package_version -o CHANGELOG.md && git add CHANGELOG.md",
|
|
14
|
-
"
|
|
14
|
+
"prepublishOnly": "git push --follow-tags",
|
|
15
15
|
"release-note": "git-chglog -c .chglog/release-config.yml v$npm_package_version",
|
|
16
16
|
"rollup": "npx ts-node scripts/rollup.ts",
|
|
17
17
|
"build": "npm run rollup && tsc -p ./"
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"@ui5/project": "^2.6.0",
|
|
41
41
|
"adm-zip": "^0.5.5",
|
|
42
42
|
"amdextract": "^3.0.0",
|
|
43
|
-
"axios": "^
|
|
43
|
+
"axios": "^1.6.0",
|
|
44
44
|
"builtin-modules": "^3.2.0",
|
|
45
45
|
"dotenv": "^16.0.3",
|
|
46
46
|
"js-yaml": "^4.1.0",
|