@ui5/task-adaptation 1.0.19 → 1.0.21

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 CHANGED
@@ -2,7 +2,16 @@
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.19...HEAD).
5
+ A list of unreleased changes can be found [here](https://github.com/SAP/ui5-task-adaptation/compare/v1.0.21...HEAD).
6
+
7
+ <a name="v1.0.21"></a>
8
+ ## [v1.0.21] - 2024-01-03
9
+ ### Bug Fixes
10
+ - Download app source did not parse json [`dbe7bbb`](https://github.com/SAP/ui5-task-adaptation/commit/dbe7bbba4ac14a36431986658881e087d40e1541)
11
+
12
+
13
+ <a name="v1.0.20"></a>
14
+ ## [v1.0.20] - 2024-01-02
6
15
 
7
16
  <a name="v1.0.19"></a>
8
17
  ## [v1.0.19] - 2023-11-16
@@ -55,6 +64,8 @@ A list of unreleased changes can be found [here](https://github.com/SAP/ui5-task
55
64
  <a name="v1.0.0"></a>
56
65
  ## v1.0.0 - 2020-12-09
57
66
 
67
+ [v1.0.21]: https://github.com/SAP/ui5-task-adaptation/compare/v1.0.20...v1.0.21
68
+ [v1.0.20]: https://github.com/SAP/ui5-task-adaptation/compare/v1.0.19...v1.0.20
58
69
  [v1.0.19]: https://github.com/SAP/ui5-task-adaptation/compare/v1.0.18...v1.0.19
59
70
  [v1.0.18]: https://github.com/SAP/ui5-task-adaptation/compare/v1.0.17...v1.0.18
60
71
  [v1.0.17]: https://github.com/SAP/ui5-task-adaptation/compare/v1.0.16...v1.0.17
@@ -13,7 +13,7 @@ export default class AnnotationManager {
13
13
  private normalizeAppVariantId;
14
14
  private createAnnotationFile;
15
15
  private downloadAnnotations;
16
- private updateManifest;
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.getManifestInfo(renamedBaseAppManifest);
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.updateManifest(renamedBaseAppManifest, metaInfo, modelName, i18nPathName);
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
- updateManifest(renamedBaseAppManifest, metaInfo, modelName, i18nPathName) {
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;
@@ -6,7 +6,12 @@ export default class AppVariantManager {
6
6
  private static isManifestChange;
7
7
  private static isManifestAppVariant;
8
8
  static getAppVariantInfo(appVariantResources: any[]): Promise<IAppVariantInfo>;
9
- static writeI18nToModule(resource: any, projectNamespace: string, i18nBundleName: string): void;
10
9
  private static omitFiles;
11
- private static adjustAddNewModelEnhanceWith;
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;
12
17
  }
@@ -8,12 +8,10 @@ const EXTENSIONS = "js,json,xml,html,properties,change,appdescr_variant";
8
8
  class AppVariantManager {
9
9
  static async process(appVariantResources, projectNamespace, taskUtil) {
10
10
  const appVariantInfo = await this.getAppVariantInfo(appVariantResources);
11
- const i18nBundleName = (0, commonUtil_1.replaceDots)(appVariantInfo.id);
12
11
  for (const resource of appVariantResources) {
13
- this.writeI18nToModule(resource, projectNamespace, i18nBundleName);
14
12
  this.omitFiles(resource, taskUtil);
15
13
  }
16
- this.adjustAddNewModelEnhanceWith(appVariantInfo?.manifest?.content ?? [], i18nBundleName);
14
+ this.patchMissingTextsNode(appVariantInfo?.manifest?.content ?? []);
17
15
  await this.renameChanges(appVariantResources, projectNamespace, appVariantInfo);
18
16
  return appVariantInfo;
19
17
  }
@@ -70,25 +68,24 @@ class AppVariantManager {
70
68
  }
71
69
  throw new Error("Adaptation project should contain manifest.appdescr_variant");
72
70
  }
73
- static writeI18nToModule(resource, projectNamespace, i18nBundleName) {
74
- if (path_1.posix.extname(resource.getPath()) === ".properties") {
75
- let rootFolder = resourceUtil_1.default.getRootFolder(projectNamespace);
76
- resource.setPath(path_1.posix.join(rootFolder, i18nBundleName, resource.getPath().substring(rootFolder.length)));
77
- }
78
- }
79
71
  static omitFiles(resource, taskUtil) {
80
72
  if (this.isManifestAppVariant(resource) || this.isManifestChange(resource)) {
81
73
  taskUtil.setTag(resource, taskUtil.STANDARD_TAGS.OmitFromBuildResult, true);
82
74
  }
83
75
  }
84
- static adjustAddNewModelEnhanceWith(changes, i18nBundleName) {
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: "i18n/i18n.properties" };
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
  }
@@ -4,17 +4,24 @@ export interface IBaseAppResources {
4
4
  resources: any[];
5
5
  manifestInfo: IManifestInfo;
6
6
  }
7
- export interface IManifestInfo {
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, i18nBundleName: string): Promise<void>;
25
+ static applyDescriptorChanges(baseAppManifest: any, appVariantInfo: IAppVariantInfo): Promise<void>;
19
26
  static writeToWorkspace(baseAppFiles: Map<string, string>, projectNamespace: string): any[];
20
27
  }
@@ -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 { RegistrationBuild, ApplyUtil, Applier, Change } = require("../dist/bundle");
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.getManifestInfo(baseAppManifest.content);
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
- const i18nBundleName = (0, commonUtil_1.replaceDots)(appVariantInfo.id);
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 getManifestInfo(manifest) {
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, i18nBundleName) {
89
+ static async applyDescriptorChanges(baseAppManifest, appVariantInfo) {
70
90
  log.verbose("Applying appVariant changes");
71
- const strategy = new buildStrategy_1.default(RegistrationBuild, ApplyUtil, i18nBundleName);
91
+ const strategy = new buildStrategy_1.default(RegistrationBuild);
72
92
  const { manifest } = appVariantInfo;
73
93
  const allChanges = [
74
94
  ...manifest.content,
@@ -1,10 +1,7 @@
1
- import { IChangeText } from "./model/types";
2
1
  export default class BuildStrategy {
3
2
  private registrationBuild;
4
- private applyUtil;
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, changeTexts: IChangeText): any;
6
+ processTexts(manifest: any): any;
10
7
  }
@@ -1,10 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  class BuildStrategy {
4
- constructor(registrationBuild, applyUtil, i18nBundleName) {
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, changeTexts) {
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
  }
@@ -39,5 +39,6 @@ export default class I18nManager {
39
39
  private replaceWithModelReference;
40
40
  private createReference;
41
41
  getUniqueKeyForValue(value: string): string;
42
+ hasTranslations(): boolean;
42
43
  private initPropertyValues;
43
44
  }
@@ -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 path_1 = require("path");
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, path_1.join)(i18nPathName, filename + ".properties");
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 Promise.all(appVariantResources.concat(resources).map(resource => workspace.write(resource)));
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
  };
@@ -69,12 +69,19 @@ export interface IAppVariantManifest {
69
69
  }
70
70
  export interface IChange {
71
71
  changeType: string;
72
- texts: IChangeText;
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;
@@ -47,7 +47,7 @@ class AbapRepoManager {
47
47
  const { destination, appName } = this.configuration;
48
48
  const encodedAppName = encodeURIComponent(appName);
49
49
  const uri = `https://${destination}.dest/sap/opu/odata/UI5/ABAP_REPOSITORY_SRV/Repositories('${encodedAppName}')?DownloadFiles=RUNTIME&CodePage=UTF8`;
50
- const data = await requestUtil_1.default.get(uri, REQUEST_OPTIONS_XML, auth);
50
+ const data = await requestUtil_1.default.get(uri, {}, auth);
51
51
  if (data?.d?.ZipArchive.length > 0) {
52
52
  const buffer = Buffer.from(data.d.ZipArchive, "base64");
53
53
  return (0, zipUtil_1.unzipZipEntries)(buffer);
@@ -1,3 +1,5 @@
1
- export declare function replaceDots(value: string): string;
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;
@@ -1,10 +1,10 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.renameResources = exports.validateObject = exports.replaceDots = void 0;
4
- function replaceDots(value) {
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.replaceDots = replaceDots;
7
+ exports.dotToUnderscore = dotToUnderscore;
8
8
  function validateObject(options, properties, message) {
9
9
  for (const property of properties) {
10
10
  if (!options[property]) {
@@ -13,8 +13,11 @@ 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
@@ -22,11 +25,13 @@ function renameResources(files, search, replacement) {
22
25
  if (replacement.includes(search)) {
23
26
  const [before, _] = replacement.split(search);
24
27
  // Matches a position in the string that is not immediately preceded by
25
- // the string "before".
26
- escapedSearch = `(?<!${escapeRegexSpecialChars(before)})${escapeRegexSpecialChars(search)}`;
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)}`;
27
32
  }
28
33
  else {
29
- escapedSearch = escapeRegexSpecialChars(search);
34
+ escapedSearch = escapeRegex(search);
30
35
  }
31
36
  const dotToSlash = (update) => update.replaceAll(".", "\/");
32
37
  const replace = (content) => content.replace(new RegExp(escapedSearch, "g"), replacement);
@@ -50,4 +55,9 @@ function renameResources(files, search, replacement) {
50
55
  return renamed;
51
56
  }
52
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;
53
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.19",
3
+ "version": "1.0.21",
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
- "postversion": "git push --follow-tags",
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": "^0.25.0",
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",