@ui5/task-adaptation 1.4.2 → 1.5.0

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.
Files changed (70) hide show
  1. package/CHANGELOG.md +16 -1
  2. package/README.md +16 -12
  3. package/REUSE.toml +10 -0
  4. package/dist/annotationManager.d.ts +1 -1
  5. package/dist/annotationManager.js +5 -8
  6. package/dist/annotations/dataSource/dataSource.js +0 -1
  7. package/dist/annotations/serviceRequestor.js +3 -15
  8. package/dist/appVariantManager.d.ts +16 -11
  9. package/dist/appVariantManager.js +56 -83
  10. package/dist/baseAppManager.d.ts +16 -16
  11. package/dist/baseAppManager.js +58 -81
  12. package/dist/bundle.d.ts +2 -3
  13. package/dist/bundle.js +3834 -3490
  14. package/dist/cache/cacheHolder.d.ts +18 -0
  15. package/dist/cache/cacheHolder.js +80 -0
  16. package/dist/i18nManager.js +11 -9
  17. package/dist/index.js +34 -9
  18. package/dist/model/appVariantIdHierarchyItem.d.ts +5 -0
  19. package/dist/model/appVariantIdHierarchyItem.js +2 -0
  20. package/dist/model/configuration.d.ts +2 -2
  21. package/dist/model/language.d.ts +3 -3
  22. package/dist/model/language.js +11 -4
  23. package/dist/model/types.d.ts +3 -1
  24. package/dist/processors/abapProcessor.d.ts +5 -5
  25. package/dist/processors/abapProcessor.js +19 -7
  26. package/dist/processors/cfProcessor.d.ts +4 -4
  27. package/dist/processors/cfProcessor.js +29 -13
  28. package/dist/processors/processor.d.ts +4 -2
  29. package/dist/processors/processor.js +3 -5
  30. package/dist/repositories/abapRepoManager.d.ts +3 -1
  31. package/dist/repositories/abapRepoManager.js +24 -5
  32. package/dist/repositories/html5RepoManager.js +3 -2
  33. package/dist/util/cfUtil.d.ts +1 -0
  34. package/dist/util/cfUtil.js +27 -23
  35. package/dist/util/commonUtil.d.ts +5 -3
  36. package/dist/util/commonUtil.js +104 -31
  37. package/dist/util/filesUtil.d.ts +17 -0
  38. package/dist/util/filesUtil.js +49 -0
  39. package/dist/util/i18nMerger.d.ts +15 -20
  40. package/dist/util/i18nMerger.js +46 -64
  41. package/dist/util/renamingHandlers/manifestHandler.d.ts +6 -0
  42. package/dist/util/renamingHandlers/manifestHandler.js +20 -0
  43. package/dist/util/renamingHandlers/renamingHandler.d.ts +4 -0
  44. package/dist/util/renamingHandlers/renamingHandler.js +2 -0
  45. package/dist/util/requestUtil.d.ts +2 -1
  46. package/dist/util/resourceUtil.d.ts +3 -1
  47. package/dist/util/resourceUtil.js +15 -2
  48. package/eslint.config.js +20 -4
  49. package/package.json +28 -22
  50. package/scripts/rollup/bundle.d.ts +2 -3
  51. package/scripts/rollup/bundleDefinition.js +2 -2
  52. package/scripts/rollup/overrides/sap/ui/fl/Utils.js +13 -0
  53. package/scripts/rollup/overrides/sap/ui/fl/apply/_internal/changes/FlexCustomData.js +13 -0
  54. package/scripts/rollup/overrides/sap/ui/fl/apply/_internal/flexObjects/AnnotationChange.js +11 -0
  55. package/scripts/rollup/overrides/sap/ui/fl/apply/_internal/flexObjects/AppDescriptorChange.js +68 -0
  56. package/scripts/rollup/overrides/sap/ui/fl/apply/_internal/flexObjects/VariantChange.js +11 -0
  57. package/scripts/rollup/overrides/sap/ui/fl/apply/_internal/flexObjects/VariantManagementChange.js +11 -0
  58. package/scripts/rollup/overrides/sap/ui/fl/apply/_internal/flexState/controlVariants/VariantManagementState.js +13 -0
  59. package/scripts/rollup/overrides/sap/ui/fl/initial/_internal/changeHandlers/ChangeHandlerRegistration.js +13 -0
  60. package/scripts/rollup/overrides/sap/ui/fl/initial/_internal/changeHandlers/ChangeHandlerStorage.js +14 -0
  61. package/scripts/rollup/project/ui5.yaml +1 -1
  62. package/scripts/test-integration-prep.sh +4 -0
  63. package/types/ui5.d.ts +10 -0
  64. package/dist/cache/annotationsCacheManager.d.ts +0 -8
  65. package/dist/cache/annotationsCacheManager.js +0 -16
  66. package/dist/cache/baseAppFilesCacheManager.d.ts +0 -6
  67. package/dist/cache/baseAppFilesCacheManager.js +0 -12
  68. package/dist/cache/cacheManager.d.ts +0 -16
  69. package/dist/cache/cacheManager.js +0 -65
  70. package/scripts/rollup/overrides/sap/ui/fl/Change.js +0 -74
package/CHANGELOG.md CHANGED
@@ -2,7 +2,20 @@
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.4.2...HEAD).
5
+ A list of unreleased changes can be found [here](https://github.com/SAP/ui5-task-adaptation/compare/v1.5.0...HEAD).
6
+
7
+ <a name="v1.5.0"></a>
8
+ ## [v1.5.0] - 2025-07-17
9
+
10
+ <a name="v1.4.3"></a>
11
+ ## [v1.4.3] - 2025-04-17
12
+ ### Bug Fixes
13
+ - Handling undefined oAuthScopes ([#122](https://github.com/SAP/ui5-task-adaptation/issues/122)) [`ea6296b`](https://github.com/SAP/ui5-task-adaptation/commit/ea6296b143e7fcbf8ce3902499be2d4c2fea61a5)
14
+ - Better error handling in CF ([#121](https://github.com/SAP/ui5-task-adaptation/issues/121)) [`8ce564f`](https://github.com/SAP/ui5-task-adaptation/commit/8ce564fa847a512f4de341f14b1788bca3075da8)
15
+ - Get space guid correctly ([#120](https://github.com/SAP/ui5-task-adaptation/issues/120)) [`64a28ee`](https://github.com/SAP/ui5-task-adaptation/commit/64a28eefc19ecded18bd363c65a8417180200945)
16
+ - Create html5 repo service instance ([#118](https://github.com/SAP/ui5-task-adaptation/issues/118)) [`92a6769`](https://github.com/SAP/ui5-task-adaptation/commit/92a6769ea8704ad81bf7e65766c260bb710ab043)
17
+ - Audit and eslint issues [`34508cb`](https://github.com/SAP/ui5-task-adaptation/commit/34508cb013f951d68527d7a733a409ed343fe43c)
18
+
6
19
 
7
20
  <a name="v1.4.2"></a>
8
21
  ## [v1.4.2] - 2025-02-25
@@ -106,6 +119,8 @@ A list of unreleased changes can be found [here](https://github.com/SAP/ui5-task
106
119
  <a name="v1.0.0"></a>
107
120
  ## v1.0.0 - 2020-12-09
108
121
 
122
+ [v1.5.0]: https://github.com/SAP/ui5-task-adaptation/compare/v1.4.3...v1.5.0
123
+ [v1.4.3]: https://github.com/SAP/ui5-task-adaptation/compare/v1.4.2...v1.4.3
109
124
  [v1.4.2]: https://github.com/SAP/ui5-task-adaptation/compare/v1.4.0...v1.4.2
110
125
  [v1.4.0]: https://github.com/SAP/ui5-task-adaptation/compare/v1.3.3...v1.4.0
111
126
  [v1.3.3]: https://github.com/SAP/ui5-task-adaptation/compare/v1.3.2...v1.3.3
package/README.md CHANGED
@@ -4,28 +4,29 @@
4
4
  [![npm version](https://badge.fury.io/js/@ui5%2Ftask-adaptation.svg)](https://badge.fury.io/js/@ui5%2Ftask-adaptation)
5
5
 
6
6
  ## Description
7
- A custom task for [ui5-builder](https://github.com/SAP/ui5-builder) that allows building SAPUI5 adaptation projects for [SAP S/4HANA Cloud](https://help.sap.com/docs/bas/584e0bcbfd4a4aff91c815cefa0bce2d/6fc4e11a4b1941efa8e37a428d046f8f.html?locale=en-US&state=PRODUCTION&version=Cloud) and [SAP BTP, Cloud Foundry environment](https://help.sap.com/viewer/584e0bcbfd4a4aff91c815cefa0bce2d/Cloud/en-US/019b0c38a6b043d1a66b11d992eed290.html).
7
+ A custom task for [ui5-builder](https://github.com/SAP/ui5-builder) that allows you to build SAPUI5 adaptation projects for [SAP S/4HANA Cloud](https://help.sap.com/docs/bas/584e0bcbfd4a4aff91c815cefa0bce2d/6fc4e11a4b1941efa8e37a428d046f8f.html?locale=en-US&state=PRODUCTION&version=Cloud) and [SAP BTP, Cloud Foundry environment](https://help.sap.com/viewer/584e0bcbfd4a4aff91c815cefa0bce2d/Cloud/en-US/019b0c38a6b043d1a66b11d992eed290.html).
8
8
 
9
9
  ### Configuration
10
- #### ABAP Connection
11
- The following connection configuration format is used to connect to ABAP repository in IDE:
10
+ #### Connection
11
+ When creating an adaptation project, the Yeoman generator automatically generates the following connection configurations in the `ui5.yaml` file, depending on your IDE:
12
+
13
+ For local IDEs like VS Code:
12
14
  ```yaml
13
15
  target:
14
16
  url: example.com,
15
- authenticationType: basic | reentranceTicket,
17
+ authenticationType: reentranceTicket,
16
18
  ignoreCertErrors: true | false
17
19
  ```
18
- and in SAP Business Application Studio:
20
+ In SAP Business Application Studio:
19
21
  ```yaml
20
22
  target:
21
23
  destination: abc
22
24
  ```
23
- OnPremise ABAP repository requires `authenticationType: basic`, credentials should be provided in project root `.env` file:
24
- ```
25
- FIORI_TOOLS_USER=<username>
26
- FIORI_TOOLS_PASSWORD=<password>
27
- ```
28
- Whereas S4/Hana uses `authenticationType: reentranceTicket` to authentificate. A browser window will be opened.
25
+
26
+ For more information, see [Create an Adaptation Project](https://help.sap.com/docs/bas/developing-sap-fiori-app-in-sap-business-application-studio/create-project) (on SAP S/4HANA Cloud) or [Create an Adaptation Project](https://help.sap.com/docs/bas/developing-sap-fiori-app-in-sap-business-application-studio/create-adaptation-project-c7b455d488bc4229af7efe0311546752) (on SAP BTP, Cloud Foundry environment).
27
+ > [!NOTE]
28
+ > For SAP S/4HANA (on-premise) systems, a different builder is used.
29
+
29
30
 
30
31
  ## How to obtain support
31
32
  In case you need any support, please create a [GitHub issue](https://github.com/SAP/ui5-task-adaptation/issues).
@@ -34,4 +35,7 @@ In case you need any support, please create a [GitHub issue](https://github.com/
34
35
  Copyright (c) 2020 SAP SE or an SAP affiliate company. All rights reserved. This file and all other files in this repository are licensed under the Apache License, v 2.0 except as noted otherwise in the [LICENSE file](LICENSE).
35
36
 
36
37
  ## Release History
37
- See [CHANGELOG.md](CHANGELOG.md).
38
+ See [CHANGELOG.md](CHANGELOG.md).
39
+
40
+ ## Contributing
41
+ Please refer to the [SAP Contribution Guidelines](https://github.com/SAP/.github/blob/main/CONTRIBUTING.md) for instructions on how to contribute to ui5-task-adaptation.
package/REUSE.toml ADDED
@@ -0,0 +1,10 @@
1
+ version = 1
2
+ SPDX-PackageName = "ui5-task-adaptation"
3
+ SPDX-PackageSupplier = "Aleksandr Suvorov <aleksandr.suvorov@sap.com>, Matthias Schmalz <matthias.schmalz@sap.com>"
4
+ SPDX-PackageDownloadLocation = "https://github.com/SAP/ui5-task-adaptation"
5
+
6
+ [[annotations]]
7
+ path = "**.**"
8
+ precedence = "aggregate"
9
+ SPDX-FileCopyrightText = "2009-2025 SAP SE or an SAP affiliate company"
10
+ SPDX-License-Identifier = "Apache-2.0"
@@ -10,7 +10,7 @@ export default class AnnotationManager {
10
10
  private configuration;
11
11
  constructor(configuration: IConfiguration, abapRepoManager: AbapRepoManager);
12
12
  ANNOTATIONS_FOLDER: string;
13
- process(renamedBaseAppManifest: any, languages: Language[]): Promise<Map<string, string>>;
13
+ process(baseAppManifest: any, languages: Language[], id: string): Promise<Map<string, string>>;
14
14
  private normalizeAppVariantId;
15
15
  private updateManifestModel;
16
16
  private createManifestModel;
@@ -1,4 +1,3 @@
1
- import BaseAppManager from "./baseAppManager.js";
2
1
  import DataSourceManager from "./annotations/dataSource/dataSourceManager.js";
3
2
  import I18nManager from "./i18nManager.js";
4
3
  import ServiceRequestor from "./annotations/serviceRequestor.js";
@@ -14,9 +13,7 @@ export default class AnnotationManager {
14
13
  this.abapRepoManager = abapRepoManager;
15
14
  }
16
15
  ANNOTATIONS_FOLDER = "annotations";
17
- async process(renamedBaseAppManifest, languages) {
18
- const { id } = BaseAppManager.getIdVersion(renamedBaseAppManifest);
19
- BaseAppManager.validateProperty(id, "sap.app/id");
16
+ async process(baseAppManifest, languages, id) {
20
17
  const normalisedId = this.normalizeAppVariantId(id);
21
18
  //TODO: switch to this after resolving @i18n custom model
22
19
  const modelName = I18N_DEFAULT_MODEL_NAME; //`i18n_a9n_${normalisedId}`;
@@ -24,20 +21,20 @@ export default class AnnotationManager {
24
21
  const i18nManager = new I18nManager(modelName, id, languages);
25
22
  const serviceRequestor = new ServiceRequestor(this.configuration, this.abapRepoManager);
26
23
  const dataSourceManager = new DataSourceManager();
27
- dataSourceManager.addDataSources(renamedBaseAppManifest["sap.app"]?.dataSources);
24
+ dataSourceManager.addDataSources(baseAppManifest["sap.app"]?.dataSources);
28
25
  const annotationFiles = await dataSourceManager.createAnnotationFiles(languages, i18nManager, serviceRequestor);
29
26
  const i18nFiles = i18nManager.createFiles(i18nPathName);
30
27
  if (i18nManager.hasTranslations()) {
31
- this.updateManifestModel(renamedBaseAppManifest, modelName, i18nPathName);
28
+ this.updateManifestModel(baseAppManifest, modelName, i18nPathName);
32
29
  }
33
30
  return new Map([...annotationFiles, ...i18nFiles]);
34
31
  }
35
32
  normalizeAppVariantId(id, replaceWith = "") {
36
33
  return id.replace(/[.\W]+/gi, replaceWith);
37
34
  }
38
- updateManifestModel(renamedBaseAppManifest, modelName, i18nPathName) {
35
+ updateManifestModel(baseAppManifest, modelName, i18nPathName) {
39
36
  const uri = `${i18nPathName}/i18n.properties`;
40
- this.enhanceManifestModel(renamedBaseAppManifest, modelName, uri);
37
+ this.enhanceManifestModel(baseAppManifest, modelName, uri);
41
38
  //TODO: switch to this after resolving @i18n custom model
42
39
  //this.createManifestModel(renamedBaseAppManifest, modelName, uri);
43
40
  }
@@ -10,7 +10,6 @@ export default class DataSource {
10
10
  /**
11
11
  * Update the json of the dataSources in manifest.json
12
12
  */
13
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
14
13
  updateManifest(_) {
15
14
  // to be overriden in children
16
15
  }
@@ -4,7 +4,6 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
4
4
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
5
  return c > 3 && r && Object.defineProperty(target, key, r), r;
6
6
  };
7
- import AnnotationsCacheManager from "../cache/annotationsCacheManager.js";
8
7
  import ServerError from "../model/serverError.js";
9
8
  import { getLogger } from "@ui5/logger";
10
9
  import { writeTempAnnotations } from "../util/commonUtil.js";
@@ -46,20 +45,9 @@ export default class ServiceRequestor {
46
45
  //old decorators are already subject of compiler error, but it works. So we
47
46
  //wait till esbuild implement it correctly.
48
47
  async downloadAnnotation(uri, name, language) {
49
- let cacheName = name;
50
- if (language.sap) {
51
- uri += `?sap-language=${language.sap}`;
52
- cacheName += `-${language.sap}`;
53
- }
54
- const cacheManager = new AnnotationsCacheManager(this.configuration, cacheName);
55
- log.verbose(`Getting annotation '${cacheName}' ${language} by '${uri}'`);
56
- let files;
57
- if (this.configuration.enableAnnotationCache) {
58
- files = await cacheManager.getFiles(() => this.abapRepoManager.getAnnotationMetadata(uri), () => this.abapRepoManager.downloadAnnotationFile(uri));
59
- }
60
- else {
61
- files = await this.abapRepoManager.downloadAnnotationFile(uri);
62
- }
48
+ uri += `?sap-language=${language.sap}`;
49
+ log.verbose(`Getting annotation '${name}' ${language} by '${uri}'`);
50
+ let files = await this.abapRepoManager.downloadAnnotationFile(uri);
63
51
  if (!files || files.size === 0) {
64
52
  throw new Error(`No files were fetched for '${name}' by '${uri}'`);
65
53
  }
@@ -1,12 +1,17 @@
1
- import { IAppVariantInfo } from "./model/types.js";
2
- export default class AppVariantManager {
3
- static process(appVariantResources: any[], projectNamespace: string, taskUtil: any): Promise<IAppVariantInfo>;
4
- static getAppVariantResourcesToProcess(workspace: any): Promise<any[]>;
5
- static updateChanges(appVariantResources: any[], projectNamespace: string): Promise<void>;
6
- private static isManifestChange;
7
- private static isManifestAppVariant;
8
- static getAppVariantInfo(appVariantResources: any[]): Promise<IAppVariantInfo>;
9
- private static validateManifest;
10
- private static updateRelativePaths;
11
- private static omitFiles;
1
+ import { IChange } from "./model/types.js";
2
+ import TaskUtil from "@ui5/project/build/helpers/TaskUtil";
3
+ export default class AppVariant {
4
+ readonly files: ReadonlyMap<string, string>;
5
+ readonly resources?: ReadonlyArray<Resource>;
6
+ readonly id: string;
7
+ readonly reference: string;
8
+ readonly layer: any;
9
+ readonly content: any;
10
+ static fromWorkspace(workspace: IWorkspace, projectNamespace: string): Promise<AppVariant>;
11
+ static fromFiles(files: ReadonlyMap<string, string>): AppVariant;
12
+ private constructor();
13
+ getProcessedManifestChanges(): IChange[];
14
+ private validateManifest;
15
+ private updateRelativePaths;
16
+ omitDeletedResources(files: ReadonlyMap<string, string>, projectNamespace: string, taskUtil: TaskUtil): void;
12
17
  }
@@ -1,101 +1,74 @@
1
1
  import ResourceUtil from "./util/resourceUtil.js";
2
2
  import { posix as path } from "path";
3
- import { renameResources } from "./util/commonUtil.js";
4
- const EXTENSIONS_TO_PROCESS = "js,json,xml,html,properties,change,appdescr_variant,ctrl_variant,ctrl_variant_change,ctrl_variant_management_change,variant,fioriversion,codeChange,xmlViewChange,context";
5
- export default class AppVariantManager {
6
- static async process(appVariantResources, projectNamespace, taskUtil) {
7
- for (const resource of appVariantResources) {
8
- this.omitFiles(resource, taskUtil);
9
- }
10
- await this.updateChanges(appVariantResources, projectNamespace);
11
- return this.getAppVariantInfo(appVariantResources);
3
+ const MANIFEST_CHANGES_DIR = "changes/manifest/";
4
+ export default class AppVariant {
5
+ files;
6
+ resources;
7
+ id;
8
+ reference;
9
+ layer;
10
+ content;
11
+ static async fromWorkspace(workspace, projectNamespace) {
12
+ const EXTENSIONS_TO_PROCESS = "js,json,xml,html,properties,change,appdescr_variant,ctrl_variant,ctrl_variant_change,ctrl_variant_management_change,variant,fioriversion,codeChange,xmlViewChange,context";
13
+ const resources = await workspace.byGlob(`/**/*.{${EXTENSIONS_TO_PROCESS}}`);
14
+ const files = await ResourceUtil.toFileMap(resources, projectNamespace);
15
+ return new AppVariant(files, resources);
12
16
  }
13
- static getAppVariantResourcesToProcess(workspace) {
14
- return workspace.byGlob(`/**/*.{${EXTENSIONS_TO_PROCESS}}`);
17
+ static fromFiles(files) {
18
+ return new AppVariant(files);
15
19
  }
16
- static async updateChanges(appVariantResources, projectNamespace) {
17
- const changesFolder = ResourceUtil.getResourcePath(projectNamespace, "changes");
18
- const changes = new Map();
19
- const resourcesByPath = new Map();
20
- let manifest;
21
- for (const resource of appVariantResources) {
22
- if (this.isManifestAppVariant(resource)) {
23
- manifest = await ResourceUtil.getJson(resource);
24
- }
25
- const resourcePath = resource.getPath();
26
- const basename = path.dirname(resourcePath);
27
- if (basename.startsWith(changesFolder)) {
28
- changes.set(resourcePath, await ResourceUtil.getString(resource));
29
- resourcesByPath.set(resourcePath, resource);
30
- }
31
- }
32
- this.updateRelativePaths(changes, projectNamespace);
33
- this.validateManifest(manifest);
34
- const renamedChanges = renameResources(changes, manifest.reference, manifest.id);
35
- renamedChanges.forEach((renamedContent, resourcePath) => {
36
- const resource = resourcesByPath.get(resourcePath);
37
- ResourceUtil.setString(resource, renamedContent);
38
- });
20
+ constructor(files, resources) {
21
+ this.files = files;
22
+ this.resources = resources;
23
+ const manifestString = files.get("manifest.appdescr_variant");
24
+ this.validateManifest(manifestString);
25
+ const { reference, id, layer, content } = JSON.parse(manifestString);
26
+ this.reference = reference;
27
+ this.id = id;
28
+ this.layer = layer;
29
+ this.content = content;
39
30
  }
40
- static isManifestChange(resource) {
41
- const changesManifestFolder = path.join("changes", "manifest");
42
- const resourcePath = typeof resource === "string" ? resource : resource.getPath();
43
- const dirname = path.dirname(resourcePath);
44
- return dirname.endsWith(changesManifestFolder);
45
- }
46
- static isManifestAppVariant(resource) {
47
- const MANIFEST_APP_VARIANT = "manifest.appdescr_variant";
48
- const basename = path.basename(resource.getPath());
49
- return basename === MANIFEST_APP_VARIANT;
50
- }
51
- static async getAppVariantInfo(appVariantResources) {
52
- let manifest;
53
- const manifestChanges = [];
54
- for (const resource of appVariantResources) {
55
- if (this.isManifestAppVariant(resource)) {
56
- manifest = await ResourceUtil.getJson(resource);
57
- }
58
- else if (this.isManifestChange(resource)) {
59
- const content = await ResourceUtil.getString(resource);
60
- manifestChanges.push(JSON.parse(content));
61
- }
62
- }
63
- this.validateManifest(manifest);
31
+ getProcessedManifestChanges() {
64
32
  // Order is important: apply manifest.json changes first, then *.change
65
33
  // files. UI5 does the same.
66
- const changes = (manifest.content ?? []).concat(manifestChanges);
67
- return {
68
- id: manifest.id,
69
- reference: manifest.reference,
70
- layer: manifest.layer,
71
- changes
72
- };
34
+ const manifestChanges = structuredClone(this.content) ?? [];
35
+ this.files.forEach((content, filename) => {
36
+ if (filename.startsWith(MANIFEST_CHANGES_DIR)) {
37
+ const change = JSON.parse(content);
38
+ this.updateRelativePaths(change, filename);
39
+ manifestChanges.push(change);
40
+ }
41
+ });
42
+ if (this.layer) {
43
+ manifestChanges.forEach(change => change.layer = this.layer ?? change.layer);
44
+ }
45
+ return manifestChanges;
73
46
  }
74
- static validateManifest(manifest) {
47
+ validateManifest(manifest) {
75
48
  if (!manifest) {
76
49
  throw new Error("Adaptation project should contain manifest.appdescr_variant");
77
50
  }
78
51
  }
79
- // TODO In future this should be handled by merger which needs to know change and target location
80
- static updateRelativePaths(changes, projectNamespace) {
81
- changes.forEach((jsonString, resourcePath) => {
82
- if (this.isManifestChange(resourcePath)) {
83
- const change = JSON.parse(jsonString);
84
- if (change.changeType === "appdescr_app_addAnnotationsToOData") {
85
- for (const dataSource of Object.values(change.content.dataSource)) {
86
- if (!dataSource.uri.startsWith("/")) {
87
- const basepath = path.dirname(ResourceUtil.relativeToRoot(resourcePath, projectNamespace));
88
- dataSource.uri = path.join(basepath.replace(/^\//, ""), dataSource.uri);
89
- }
90
- }
91
- changes.set(resourcePath, JSON.stringify(change));
52
+ updateRelativePaths(change, filename) {
53
+ // TODO In future this should be handled by merger which needs to know change and target location
54
+ if (change.changeType === "appdescr_app_addAnnotationsToOData") {
55
+ for (const dataSource of Object.values(change?.content?.dataSource)) {
56
+ if (!dataSource.uri.startsWith("/")) {
57
+ const basepath = path.dirname(filename);
58
+ dataSource.uri = path.join(basepath.replace(/^\//, ""), dataSource.uri);
92
59
  }
93
60
  }
94
- });
61
+ }
95
62
  }
96
- static omitFiles(resource, taskUtil) {
97
- if (this.isManifestAppVariant(resource) || this.isManifestChange(resource)) {
98
- taskUtil.setTag(resource, taskUtil.STANDARD_TAGS.OmitFromBuildResult, true);
63
+ omitDeletedResources(files, projectNamespace, taskUtil) {
64
+ if (!this.resources) {
65
+ return;
66
+ }
67
+ for (const resource of this.resources) {
68
+ const relativePath = ResourceUtil.relativeToRoot(resource.getPath(), projectNamespace);
69
+ if (!files.has(relativePath)) {
70
+ taskUtil.setTag(resource, taskUtil.STANDARD_TAGS.OmitFromBuildResult, true);
71
+ }
99
72
  }
100
73
  }
101
74
  }
@@ -1,4 +1,4 @@
1
- import { IAppVariantInfo, IProjectOptions } from "./model/types.js";
1
+ import AppVariant from "./appVariantManager.js";
2
2
  import IProcessor from "./processors/processor.js";
3
3
  export interface IBaseAppResources {
4
4
  resources: any[];
@@ -11,19 +11,19 @@ export interface IManifestIdVersion {
11
11
  export interface IManifestInfo extends IManifestIdVersion {
12
12
  i18nPath: string;
13
13
  }
14
- export default class BaseAppManager {
15
- static process(baseAppFiles: Map<string, string>, appVariantInfo: IAppVariantInfo, options: IProjectOptions, processor: IProcessor): Promise<IBaseAppResources>;
16
- private static updateAdaptationProperties;
17
- static getIdVersion(manifest: any): IManifestIdVersion;
18
- static getManifestInfo(manifest: any): IManifestInfo;
19
- private static extractI18nPathFromManifest;
20
- private static extractI18NFromBundleName;
21
- private static extractI18NFromBundleUrl;
22
- private static getBaseAppManifest;
23
- private static fillAppVariantIdHierarchy;
24
- private static VALIDATION_RULES;
25
- static validateProperty(value: string, property: string): void;
26
- static applyDescriptorChanges(baseAppManifest: any, { layer, changes, id }: IAppVariantInfo): Promise<void>;
27
- private static adjustAddNewModelEnhanceWith;
28
- static writeToWorkspace(baseAppFiles: Map<string, string>, projectNamespace: string): any[];
14
+ export default class BaseApp {
15
+ readonly id: string;
16
+ readonly version: string;
17
+ readonly i18nPath: string;
18
+ readonly files: ReadonlyMap<string, string>;
19
+ static fromFiles(files: ReadonlyMap<string, string>): BaseApp;
20
+ private constructor();
21
+ adapt(appVariant: AppVariant, processor: IProcessor): Promise<ReadonlyMap<string, string>>;
22
+ private updateAdaptationProperties;
23
+ private extractI18nPathFromManifest;
24
+ private fillAppVariantIdHierarchy;
25
+ private VALIDATION_RULES;
26
+ private validateProperty;
27
+ private applyDescriptorChanges;
28
+ private adjustAddNewModelEnhanceWith;
29
29
  }
@@ -1,29 +1,45 @@
1
- import { Applier, Change, RegistrationBuild } from "../dist/bundle.js";
2
- import { dotToUnderscore, removePropertiesExtension } from "./util/commonUtil.js";
1
+ import { AppDescriptorChange, Applier, RegistrationBuild } from "../dist/bundle.js";
2
+ import { dotToUnderscore, trimExtension } from "./util/commonUtil.js";
3
3
  import BuildStrategy from "./buildStrategy.js";
4
- import ResourceUtil from "./util/resourceUtil.js";
5
4
  import { getLogger } from "@ui5/logger";
6
- import { renameResources } from "./util/commonUtil.js";
7
5
  const log = getLogger("@ui5/task-adaptation::BaseAppManager");
8
- export default class BaseAppManager {
9
- static async process(baseAppFiles, appVariantInfo, options, processor) {
10
- const baseAppManifest = this.getBaseAppManifest(baseAppFiles);
11
- const { id, version } = this.getIdVersion(baseAppManifest.content);
12
- this.validateProperty(id, "sap.app/id");
13
- this.validateProperty(version, "sap.app/applicationVersion/version");
14
- const renamedBaseAppFiles = renameResources(baseAppFiles, appVariantInfo.reference, appVariantInfo.id);
15
- const { filepath, content } = this.getBaseAppManifest(renamedBaseAppFiles);
16
- await processor.updateLandscapeSpecificContent(content, renamedBaseAppFiles);
17
- this.fillAppVariantIdHierarchy(processor, id, version, content);
18
- this.updateAdaptationProperties(content);
19
- await this.applyDescriptorChanges(content, appVariantInfo);
20
- renamedBaseAppFiles.set(filepath, JSON.stringify(content));
21
- return {
22
- resources: this.writeToWorkspace(renamedBaseAppFiles, options.projectNamespace),
23
- manifestInfo: this.getManifestInfo(content)
24
- };
6
+ const IGNORE_FILES = [
7
+ "manifest-bundle.zip",
8
+ "Component-preload.js",
9
+ "sap-ui-cachebuster-info.json"
10
+ ];
11
+ export default class BaseApp {
12
+ id;
13
+ version;
14
+ i18nPath;
15
+ files;
16
+ static fromFiles(files) {
17
+ return new BaseApp(files);
25
18
  }
26
- static updateAdaptationProperties(content) {
19
+ constructor(files) {
20
+ this.files = new Map([...files].filter(([filename]) => !IGNORE_FILES.includes(filename)));
21
+ const manifestString = files.get("manifest.json");
22
+ if (!manifestString) {
23
+ throw new Error("Original application should have manifest.json in root folder");
24
+ }
25
+ const manifest = JSON.parse(manifestString);
26
+ this.id = manifest["sap.app"]?.id;
27
+ this.version = manifest["sap.app"]?.applicationVersion?.version;
28
+ this.validateProperty(this.id, "sap.app/id");
29
+ this.validateProperty(this.version, "sap.app/applicationVersion/version");
30
+ this.i18nPath = this.extractI18nPathFromManifest(this.id, manifest["sap.app"]?.i18n);
31
+ }
32
+ async adapt(appVariant, processor) {
33
+ const files = new Map(this.files);
34
+ const manifest = JSON.parse(files.get("manifest.json"));
35
+ await processor.updateLandscapeSpecificContent(manifest, files, appVariant.id);
36
+ this.fillAppVariantIdHierarchy(processor, this.id, this.version, manifest);
37
+ this.updateAdaptationProperties(manifest);
38
+ await this.applyDescriptorChanges(manifest, appVariant);
39
+ files.set("manifest.json", JSON.stringify(manifest));
40
+ return files;
41
+ }
42
+ updateAdaptationProperties(content) {
27
43
  if (content["sap.fiori"]?.cloudDevAdaptationStatus) {
28
44
  delete content["sap.fiori"].cloudDevAdaptationStatus;
29
45
  }
@@ -32,42 +48,21 @@ export default class BaseAppManager {
32
48
  }
33
49
  content["sap.ui5"].isCloudDevAdaptation = true;
34
50
  }
35
- static getIdVersion(manifest) {
36
- const id = manifest["sap.app"]?.id;
37
- const version = manifest["sap.app"]?.applicationVersion?.version;
38
- return { id, version };
39
- }
40
- static getManifestInfo(manifest) {
41
- const { id, version } = this.getIdVersion(manifest);
42
- const i18nNode = manifest["sap.app"]?.i18n;
43
- const i18nPath = this.extractI18nPathFromManifest(id, i18nNode);
44
- return { id, version, i18nPath };
45
- }
46
- static extractI18nPathFromManifest(sapAppId, i18nNode) {
47
- if (typeof i18nNode === "object") {
48
- return i18nNode["bundleUrl"] ? this.extractI18NFromBundleUrl(i18nNode) : this.extractI18NFromBundleName(i18nNode, sapAppId);
49
- }
50
- else {
51
- return `${sapAppId?.replaceAll(".", "/")}/${i18nNode}`;
52
- }
53
- }
54
- static extractI18NFromBundleName(i18nNode, sapAppId) {
55
- return i18nNode["bundleName"].replace(sapAppId, "").replaceAll(".", "/").substring(1);
56
- }
57
- static extractI18NFromBundleUrl(i18nNode) {
58
- return removePropertiesExtension(i18nNode["bundleUrl"]);
59
- }
60
- static getBaseAppManifest(baseAppFiles) {
61
- const manifestContent = baseAppFiles.get("manifest.json");
62
- if (manifestContent) {
63
- return {
64
- content: JSON.parse(manifestContent),
65
- filepath: "manifest.json"
66
- };
51
+ extractI18nPathFromManifest(sapAppId, i18nNode) {
52
+ if (i18nNode) {
53
+ if (i18nNode["bundleUrl"]) {
54
+ return trimExtension(i18nNode["bundleUrl"]);
55
+ }
56
+ else if (i18nNode["bundleName"]) {
57
+ return i18nNode["bundleName"].replace(sapAppId, "").replaceAll(".", "/").substring(1);
58
+ }
59
+ else if (typeof i18nNode === "string") {
60
+ return trimExtension(i18nNode);
61
+ }
67
62
  }
68
- throw new Error("Original application should have manifest.json in root folder");
63
+ return "i18n/i18n";
69
64
  }
70
- static fillAppVariantIdHierarchy(processor, id, version, baseAppManifest) {
65
+ fillAppVariantIdHierarchy(processor, id, version, baseAppManifest) {
71
66
  log.verbose("Filling up app variant hierarchy in manifest.json");
72
67
  if (baseAppManifest["sap.ui5"] == null) {
73
68
  baseAppManifest["sap.ui5"] = {};
@@ -78,12 +73,12 @@ export default class BaseAppManager {
78
73
  const appVariantIdHierarchyItem = processor.createAppVariantHierarchyItem(id, version);
79
74
  baseAppManifest["sap.ui5"].appVariantIdHierarchy.unshift(appVariantIdHierarchyItem);
80
75
  }
81
- static VALIDATION_RULES = new Map([["sap.app/id", (value) => {
76
+ VALIDATION_RULES = new Map([["sap.app/id", (value) => {
82
77
  if (!value.includes(".")) {
83
78
  throw new Error(`The original application id '${value}' should consist of multiple segments split by dot, e.g.: original.id`);
84
79
  }
85
80
  }]]);
86
- static validateProperty(value, property) {
81
+ validateProperty(value, property) {
87
82
  if (!value) {
88
83
  throw new Error(`Original application manifest should have ${property}`);
89
84
  }
@@ -92,15 +87,12 @@ export default class BaseAppManager {
92
87
  validatationRule(value);
93
88
  }
94
89
  }
95
- static async applyDescriptorChanges(baseAppManifest, { layer, changes, id }) {
90
+ async applyDescriptorChanges(baseAppManifest, appVariant) {
96
91
  log.verbose("Applying appVariant changes");
97
92
  const changesContent = new Array();
98
- const i18nBundleName = dotToUnderscore(id);
99
- for (const change of structuredClone(changes)) {
100
- if (layer) {
101
- change.layer = layer;
102
- }
103
- changesContent.push(new Change(change));
93
+ const i18nBundleName = dotToUnderscore(appVariant.id);
94
+ for (const change of appVariant.getProcessedManifestChanges()) {
95
+ changesContent.push(new AppDescriptorChange(change));
104
96
  this.adjustAddNewModelEnhanceWith(change, i18nBundleName);
105
97
  }
106
98
  if (changesContent.length > 0) {
@@ -108,7 +100,7 @@ export default class BaseAppManager {
108
100
  await Applier.applyChanges(baseAppManifest, changesContent, strategy);
109
101
  }
110
102
  }
111
- static adjustAddNewModelEnhanceWith(change, i18nBundleName) {
103
+ adjustAddNewModelEnhanceWith(change, i18nBundleName) {
112
104
  if (change.changeType === "appdescr_ui5_addNewModelEnhanceWith") {
113
105
  if (change.texts == null) {
114
106
  // We need to add texts properties to changes because not all
@@ -120,20 +112,5 @@ export default class BaseAppManager {
120
112
  change.texts.i18n = i18nBundleName + "/" + change.texts.i18n;
121
113
  }
122
114
  }
123
- static writeToWorkspace(baseAppFiles, projectNamespace) {
124
- const IGNORE_FILES = [
125
- "manifest-bundle.zip",
126
- "Component-preload.js",
127
- "sap-ui-cachebuster-info.json"
128
- ];
129
- const resources = [];
130
- for (let filename of baseAppFiles.keys()) {
131
- if (!IGNORE_FILES.includes(filename)) {
132
- const resource = ResourceUtil.createResource(filename, projectNamespace, baseAppFiles.get(filename));
133
- resources.push(resource);
134
- }
135
- }
136
- return resources;
137
- }
138
115
  }
139
116
  //# sourceMappingURL=baseAppManager.js.map
package/dist/bundle.d.ts CHANGED
@@ -1,13 +1,12 @@
1
1
  export declare const RegistrationBuild: () => void;
2
2
 
3
3
  export declare class Applier {
4
- static applyChanges(manifest: any, changes: Change[], strategy: any): Promise<void>;
4
+ static applyChanges(manifest: any, changes: AppDescriptorChange[], strategy: any): Promise<void>;
5
5
  }
6
6
 
7
- export declare class Change {
7
+ export declare class AppDescriptorChange {
8
8
  constructor(change: any);
9
9
  getLayer(): string;
10
- _oDefinition: any;
11
10
  }
12
11
 
13
12
  export declare class V2MetadataConverter {