@ui5/task-adaptation 1.6.8 → 1.7.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 (71) hide show
  1. package/.github/workflows/codeql.yml +35 -0
  2. package/.github/workflows/release-please.yml +53 -0
  3. package/.release-please-manifest.json +3 -0
  4. package/CHANGELOG.md +41 -39
  5. package/dist/adapters/abapAdapter.d.ts +3 -3
  6. package/dist/adapters/abapAdapter.js +19 -8
  7. package/dist/adapters/adapter.d.ts +3 -2
  8. package/dist/adapters/adapter.js +9 -3
  9. package/dist/adapters/cfAdapter.d.ts +2 -2
  10. package/dist/adapters/cfAdapter.js +12 -12
  11. package/dist/adapters/commands/command.js +0 -27
  12. package/dist/adapters/commands/downloadAnnotationsCommand.d.ts +3 -4
  13. package/dist/adapters/commands/downloadAnnotationsCommand.js +4 -4
  14. package/dist/adapters/commands/fetchPreviewResourcesCommand.js +1 -1
  15. package/dist/adapters/commands/omitDeletedResourcesCommand.d.ts +1 -1
  16. package/dist/adapters/commands/renameFilesCommand.d.ts +0 -22
  17. package/dist/adapters/commands/renameFilesCommand.js +2 -114
  18. package/dist/adapters/commands/renameManifestCommand.js +2 -2
  19. package/dist/adapters/previewAdapter.d.ts +2 -2
  20. package/dist/adapters/previewAdapter.js +4 -4
  21. package/dist/annotations/abapAnnotationManager.js +1 -3
  22. package/dist/annotations/dataSource/dataSource.d.ts +6 -5
  23. package/dist/annotations/dataSource/dataSource.js +36 -12
  24. package/dist/annotations/dataSource/dataSourceManager.d.ts +2 -2
  25. package/dist/annotations/dataSource/dataSourceManager.js +2 -2
  26. package/dist/annotations/dataSource/dataSourceOData.d.ts +2 -2
  27. package/dist/annotations/transformers/transformer.d.ts +2 -2
  28. package/dist/annotations/transformers/traverseReferences.d.ts +1 -1
  29. package/dist/annotations/transformers/traverseReferences.js +2 -2
  30. package/dist/appVariant.d.ts +5 -6
  31. package/dist/appVariant.js +9 -16
  32. package/dist/baseApp.d.ts +1 -0
  33. package/dist/baseApp.js +10 -0
  34. package/dist/bundle.js +39 -27
  35. package/dist/cache/cacheHolder.d.ts +6 -12
  36. package/dist/cache/cacheHolder.js +27 -39
  37. package/dist/cache/cachedResource.d.ts +2 -1
  38. package/dist/index.d.ts +5 -6
  39. package/dist/index.js +47 -40
  40. package/dist/landscapeConfiguration.d.ts +4 -4
  41. package/dist/landscapeConfiguration.js +48 -44
  42. package/dist/model/configuration.d.ts +0 -1
  43. package/dist/model/types.d.ts +0 -3
  44. package/dist/repositories/abapRepository.d.ts +1 -1
  45. package/dist/repositories/abapRepository.js +3 -3
  46. package/dist/repositories/cacheRepository.d.ts +4 -5
  47. package/dist/repositories/cacheRepository.js +10 -18
  48. package/dist/repositories/html5Repository.d.ts +1 -1
  49. package/dist/repositories/html5Repository.js +2 -2
  50. package/dist/repositories/localRepository.d.ts +1 -1
  51. package/dist/repositories/localRepository.js +2 -2
  52. package/dist/repositories/repository.d.ts +1 -1
  53. package/dist/util/commonUtil.d.ts +1 -9
  54. package/dist/util/commonUtil.js +27 -29
  55. package/dist/util/fsUtil.js +1 -2
  56. package/dist/util/movingHandler/changeFileMoveHandler.d.ts +1 -0
  57. package/dist/util/movingHandler/changeFileMoveHandler.js +11 -3
  58. package/dist/util/referencesBuilder.d.ts +15 -0
  59. package/dist/util/referencesBuilder.js +35 -0
  60. package/dist/util/renaming/fileRenamer.d.ts +23 -0
  61. package/dist/util/renaming/fileRenamer.js +120 -0
  62. package/dist/util/{renamingHandlers → renaming/handlers}/jsonRenamingHandler.d.ts +0 -2
  63. package/dist/util/{renamingHandlers → renaming/handlers}/jsonRenamingHandler.js +0 -6
  64. package/dist/util/{renamingUtil.d.ts → renaming/referenceRenaming.d.ts} +4 -4
  65. package/dist/util/{renamingUtil.js → renaming/referenceRenaming.js} +8 -8
  66. package/package.json +4 -3
  67. package/release-please-config.json +66 -0
  68. package/dist/annotations/serviceRequestor.d.ts +0 -9
  69. package/dist/annotations/serviceRequestor.js +0 -62
  70. /package/dist/util/{renamingHandlers → renaming/handlers}/manifestRenamingHandler.d.ts +0 -0
  71. /package/dist/util/{renamingHandlers → renaming/handlers}/manifestRenamingHandler.js +0 -0
@@ -1,10 +1,9 @@
1
1
  import ICachedResource from "../cache/cachedResource.js";
2
- import { IConfiguration } from "../model/types.js";
3
2
  import IRepository from "./repository.js";
4
3
  export default class CacheRepository implements IRepository {
5
- private configuration;
6
- constructor(configuration: IConfiguration);
7
- getAppVariantIdHierarchy(_appId: string): Promise<ICachedResource[]>;
4
+ private repository;
5
+ constructor(repository: IRepository);
6
+ getAppVariantIdHierarchy(appId: string): Promise<ICachedResource[]>;
8
7
  fetch(resource: ICachedResource): Promise<Map<string, Buffer>>;
9
- downloadAnnotationFile(uri: string): Promise<Map<string, string>>;
8
+ downloadAnnotationFile(uri: string): Promise<string>;
10
9
  }
@@ -1,29 +1,21 @@
1
1
  import CacheHolder from "../cache/cacheHolder.js";
2
2
  export default class CacheRepository {
3
- configuration;
4
- constructor(configuration) {
5
- this.configuration = configuration;
3
+ repository;
4
+ constructor(repository) {
5
+ this.repository = repository;
6
6
  }
7
- async getAppVariantIdHierarchy(_appId) {
8
- const { appName } = this.configuration;
9
- if (!appName) {
10
- throw new Error("previewManifest requires 'appName' in the ui5.yaml configuration");
7
+ async getAppVariantIdHierarchy(appId) {
8
+ const mergedResource = CacheHolder.mergedResource(appId);
9
+ if (await CacheHolder.hasDirectory(mergedResource)) {
10
+ return [mergedResource];
11
11
  }
12
- return [{
13
- appName,
14
- cacheBusterToken: Promise.resolve("")
15
- }];
12
+ return this.repository.getAppVariantIdHierarchy(appId);
16
13
  }
17
14
  async fetch(resource) {
18
- const appName = resource.appName;
19
- const cachedFiles = await CacheHolder.readLatest(appName);
20
- if (cachedFiles.size === 0) {
21
- throw new Error(`No cached base app found for '${appName}'. Please check the appName or run a full build first to populate the cache.`);
22
- }
23
- return cachedFiles;
15
+ return this.repository.fetch(resource);
24
16
  }
25
17
  downloadAnnotationFile(uri) {
26
- throw new Error(`Download annotation file for uri: ${uri} is not available for CacheRepository.`);
18
+ return this.repository.downloadAnnotationFile(uri);
27
19
  }
28
20
  }
29
21
  //# sourceMappingURL=cacheRepository.js.map
@@ -18,5 +18,5 @@ export default class HTML5Repository implements IRepository {
18
18
  private requestMetadata;
19
19
  private getAppZipEntries;
20
20
  private download;
21
- downloadAnnotationFile(_uri: string): Promise<Map<string, string>>;
21
+ downloadAnnotationFile(_uri: string): Promise<string>;
22
22
  }
@@ -25,7 +25,7 @@ export default class HTML5Repository {
25
25
  }
26
26
  return [{
27
27
  appName,
28
- cacheBusterToken: this.getMetadata().then(metadata => metadata.changedOn),
28
+ token: this.getMetadata().then(metadata => metadata.changedOn),
29
29
  appVersion,
30
30
  appHostId,
31
31
  }];
@@ -118,7 +118,7 @@ export default class HTML5Repository {
118
118
  }
119
119
  downloadAnnotationFile(_uri) {
120
120
  log.verbose("No annotation processing in CF");
121
- return Promise.resolve(new Map());
121
+ return Promise.resolve("");
122
122
  }
123
123
  }
124
124
  __decorate([
@@ -4,7 +4,7 @@ export interface ILocalResource extends ICachedResource {
4
4
  absolutePath: string;
5
5
  }
6
6
  export default class LocalRepository implements IRepository {
7
- downloadAnnotationFile(_uri: string): Promise<Map<string, string>>;
7
+ downloadAnnotationFile(_uri: string): Promise<string>;
8
8
  getAppVariantIdHierarchy(appId: string): Promise<ILocalResource[]>;
9
9
  fetch(resource: ILocalResource): Promise<Map<string, Buffer>>;
10
10
  private buildManifestIndex;
@@ -7,7 +7,7 @@ const log = getLogger("@ui5/task-adaptation::LocalRepository");
7
7
  export default class LocalRepository {
8
8
  async downloadAnnotationFile(_uri) {
9
9
  log.verbose("Downloading annotation files is not supported in LocalRepository.");
10
- return new Map();
10
+ return "";
11
11
  }
12
12
  async getAppVariantIdHierarchy(appId) {
13
13
  const items = [];
@@ -26,7 +26,7 @@ export default class LocalRepository {
26
26
  }
27
27
  items.push({
28
28
  appName: currentAppId,
29
- cacheBusterToken: Promise.resolve("local"),
29
+ token: Promise.resolve("local"),
30
30
  absolutePath: entry.absolutePath,
31
31
  });
32
32
  if (entry.isVariant) {
@@ -2,5 +2,5 @@ import ICachedResource from "../cache/cachedResource.js";
2
2
  export default interface IRepository {
3
3
  getAppVariantIdHierarchy(appId: string): Promise<ICachedResource[]>;
4
4
  fetch(resource: ICachedResource): Promise<Map<string, Buffer>>;
5
- downloadAnnotationFile(uri: string): Promise<Map<string, string>>;
5
+ downloadAnnotationFile(uri: string): Promise<string>;
6
6
  }
@@ -1,6 +1,4 @@
1
- import { IConfiguration } from "../model/types.js";
2
- import Language from "../model/language.js";
3
- import AppVariant from "../appVariant.js";
1
+ export declare function retryOnError(maxRetries: number): MethodDecorator;
4
2
  export declare function dotToUnderscore(value: string): string;
5
3
  export declare function bufferToString(buffer: Buffer): string;
6
4
  export declare function bufferToJson(buffer: Buffer): any;
@@ -11,15 +9,9 @@ export declare function validateConfiguration<T extends object>(options: T, prop
11
9
  export declare function validateObject<T extends object>(options: T, properties: Array<keyof T>, message: string): void;
12
10
  export declare function escapeRegex(update: string): string;
13
11
  export declare function insertInArray<T>(array: T[], index: number, insert: T): void;
14
- export declare function writeTempAnnotations({ writeTempFiles }: IConfiguration, name: string, language: Language, content: string): void;
15
12
  export declare function trimExtension(filePath: string): string;
16
13
  export declare function traverse(json: any, paths: string[], callback: (json: any, key: string | number, paths: string[]) => void): void;
17
14
  export declare function logBuilderVersion(): void;
18
15
  export declare function logBetaUsage(): void;
19
16
  export declare function getUniqueName(existingNames: string[], template: string): string;
20
17
  export declare function isManifestChange(filename: string, content: Buffer): boolean;
21
- /**
22
- * 4p. Reference map contains searchTerm as key and replacement as value. Base
23
- * id and app variant ids are renamed to adaptation project id.
24
- */
25
- export declare function getReferences(appVariants: AppVariant[], adaptationProjectId: string): Map<string, string>;
@@ -2,10 +2,37 @@ import * as Log from "@ui5/logger";
2
2
  import * as fs from "fs";
3
3
  import { fileURLToPath } from "url";
4
4
  import { posix as path } from "path";
5
+ import ServerError from "../model/serverError.js";
5
6
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
6
7
  const log = Log.getLogger("rollup-plugin-ui5-resolve-task-adaptation");
7
8
  const CHANGES_EXT = ".change";
8
9
  const MANIFEST_CHANGE = "appdescr_";
10
+ export function retryOnError(maxRetries) {
11
+ return (_target, _propertyKey, descriptor) => {
12
+ const originalMethod = descriptor.value;
13
+ descriptor.value = async function (...args) {
14
+ let retries = 0;
15
+ while (true) {
16
+ try {
17
+ return await originalMethod.apply(this, args);
18
+ }
19
+ catch (error) {
20
+ if (error instanceof ServerError) {
21
+ if (retries === maxRetries) {
22
+ throw new Error(`Error occurred: ${error.message}. Please try again if this is a temporary issue. If not, please create a ticket on CA-UI5-ABA-AIDX`);
23
+ }
24
+ retries++;
25
+ }
26
+ else {
27
+ const message = error?.response?.data ?? error.message;
28
+ throw new Error(`Failed to fetch annotation by '${args[0]}': ${message}`);
29
+ }
30
+ }
31
+ }
32
+ };
33
+ return descriptor;
34
+ };
35
+ }
9
36
  export function dotToUnderscore(value) {
10
37
  return value.replace(/\./g, "_");
11
38
  }
@@ -40,18 +67,6 @@ export function escapeRegex(update) {
40
67
  export function insertInArray(array, index, insert) {
41
68
  array.splice(index, 0, insert);
42
69
  }
43
- export function writeTempAnnotations({ writeTempFiles }, name, language, content) {
44
- const TEMP_DIST_FOLDER = path.join(process.cwd(), "dist-debug", name);
45
- if (writeTempFiles) {
46
- if (!fs.existsSync(TEMP_DIST_FOLDER)) {
47
- fs.mkdirSync(TEMP_DIST_FOLDER, { recursive: true });
48
- }
49
- if (language) {
50
- name += "-" + language.i18n;
51
- }
52
- fs.writeFileSync(path.join(TEMP_DIST_FOLDER, name + ".xml"), content);
53
- }
54
- }
55
70
  export function trimExtension(filePath) {
56
71
  return filePath.replace(/\.[^/.]+$/, "");
57
72
  }
@@ -113,21 +128,4 @@ export function isManifestChange(filename, content) {
113
128
  }
114
129
  return false;
115
130
  }
116
- /**
117
- * 4p. Reference map contains searchTerm as key and replacement as value. Base
118
- * id and app variant ids are renamed to adaptation project id.
119
- */
120
- export function getReferences(appVariants, adaptationProjectId) {
121
- const references = new Map();
122
- appVariants.forEach((variant, i) => {
123
- if (i === 0) {
124
- references.set(variant.reference, adaptationProjectId);
125
- }
126
- if (variant.id !== adaptationProjectId) {
127
- references.set(variant.id, adaptationProjectId);
128
- }
129
- variant.getRenamingForMovedFiles().forEach((value, key) => references.set(key, value));
130
- });
131
- return references;
132
- }
133
131
  //# sourceMappingURL=commonUtil.js.map
@@ -41,8 +41,7 @@ export default class FsUtil {
41
41
  const fileReadTasks = entries
42
42
  .filter(entry => entry.isFile())
43
43
  .map(async (entry) => {
44
- const parentPath = entry.parentPath ?? entry.path; // node v20.11.0 fallback
45
- const entryPath = path.join(parentPath, entry.name);
44
+ const entryPath = path.join(entry.parentPath, entry.name);
46
45
  const relativeFilePath = path.relative(rootDirectory, entryPath).replaceAll("\\", "/");
47
46
  const content = await fs.readFile(entryPath);
48
47
  return [relativeFilePath, content];
@@ -1,6 +1,7 @@
1
1
  export declare const moveFiles: (inputFiles: ReadonlyMap<string, Buffer>, prefix: string, id: string) => {
2
2
  files: Map<string, Buffer<ArrayBufferLike>>;
3
3
  renamingPaths: Map<string, string>;
4
+ movedFiles: Map<string, string>;
4
5
  };
5
6
  export declare const moveFile: (filename: string, content: Buffer, prefix: string, id: string) => {
6
7
  newFilename: string;
@@ -49,13 +49,21 @@ function getPathWithoutExtensions(filename) {
49
49
  }
50
50
  export const moveFiles = (inputFiles, prefix, id) => {
51
51
  const files = new Map();
52
- let renamingPaths = new Map();
52
+ const renamingPaths = new Map();
53
+ // Maps original filename -> new filename for files that were actually
54
+ // moved, so callers can resolve the new location of an omitted resource.
55
+ const movedFiles = new Map();
53
56
  inputFiles.forEach((content, filename) => {
54
57
  const { newFilename, renamingPath } = moveFile(filename, content, prefix, id);
55
58
  files.set(newFilename, content);
56
- renamingPaths = new Map([...renamingPaths, ...renamingPath]);
59
+ for (const [from, to] of renamingPath) {
60
+ renamingPaths.set(from, to);
61
+ }
62
+ if (newFilename !== filename) {
63
+ movedFiles.set(filename, newFilename);
64
+ }
57
65
  });
58
- return { files, renamingPaths };
66
+ return { files, renamingPaths, movedFiles };
59
67
  };
60
68
  export const moveFile = (filename, content, prefix, id) => {
61
69
  let newFilename = filename;
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Fluent builder for the reference map (old id → adaptation project id) used to
3
+ * rename files. Chain `addReferences` and finish with `build`.
4
+ */
5
+ export default class ReferencesBuilder {
6
+ private readonly references;
7
+ /**
8
+ * Adds the references derived from the app variant id hierarchy stored in
9
+ * the given files' manifest.
10
+ */
11
+ addReferences(files: ReadonlyMap<string, Buffer>, adaptationProjectId: string): this;
12
+ addReference(from: string, to: string): this;
13
+ build(): Map<string, string>;
14
+ private getReferences;
15
+ }
@@ -0,0 +1,35 @@
1
+ import BaseApp from "../baseApp.js";
2
+ /**
3
+ * Fluent builder for the reference map (old id → adaptation project id) used to
4
+ * rename files. Chain `addReferences` and finish with `build`.
5
+ */
6
+ export default class ReferencesBuilder {
7
+ references = new Map();
8
+ /**
9
+ * Adds the references derived from the app variant id hierarchy stored in
10
+ * the given files' manifest.
11
+ */
12
+ addReferences(files, adaptationProjectId) {
13
+ const hierarchy = BaseApp.getAppVariantHierarchy(files);
14
+ const references = this.getReferences(hierarchy, adaptationProjectId);
15
+ references.forEach((value, key) => this.references.set(key, value));
16
+ return this;
17
+ }
18
+ addReference(from, to) {
19
+ this.references.set(from, to);
20
+ return this;
21
+ }
22
+ build() {
23
+ return this.references;
24
+ }
25
+ getReferences(appVariantIdHierarchy, adaptationProjectId) {
26
+ const references = new Map();
27
+ for (const appVariantId of appVariantIdHierarchy) {
28
+ if (appVariantId !== adaptationProjectId && !references.has(appVariantId)) {
29
+ references.set(appVariantId, adaptationProjectId);
30
+ }
31
+ }
32
+ return references;
33
+ }
34
+ }
35
+ //# sourceMappingURL=referencesBuilder.js.map
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Renames references (old id → adaptation project id) across all text files in
3
+ * place, leaving i18n .properties files, their keys and SAPUI5 dependency ids
4
+ * untouched.
5
+ */
6
+ export default class FileRenamer {
7
+ /**
8
+ * 1p. i18n.properties are not renamed and property keys are ignored during
9
+ * the renaming across the files. Stateless: does not use `this`.
10
+ * @param files - all files both base and appVariants
11
+ * @param references - Reference maps of the app variant
12
+ */
13
+ static rename(files: Map<string, Buffer>, references: Map<string, string>): void;
14
+ }
15
+ /**
16
+ * Snapshots the JSON paths that must survive renaming (e.g.
17
+ * sap.ui5/appVariantIdHierarchy, dependencies), runs the decorated method, then
18
+ * restores them. The decorated method receives a Map of files (filename →
19
+ * Buffer): every file a handler `accept`s is parsed, snapshotted before the
20
+ * renaming and restored + written back after. Each matching file gets its own
21
+ * handler instance so one file's snapshot can't clobber another's.
22
+ */
23
+ export declare function restoreWhatShouldntBeRenamed(): (_target: any, _propertyKey: string, descriptor: PropertyDescriptor) => void;
@@ -0,0 +1,120 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
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
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ import ManifestRenamingHandler from "./handlers/manifestRenamingHandler.js";
8
+ import { stringToBuffer, bufferToString, bufferToJson, jsonToBuffer } from "../commonUtil.js";
9
+ import { getSAPUI5DependencyIds } from "../manifestUtil.js";
10
+ import { renameMap } from "./referenceRenaming.js";
11
+ import { TEXT_EXTENSIONS } from "../resourceUtil.js";
12
+ import { posix as path } from "path";
13
+ function isTextFile(filename) {
14
+ return TEXT_EXTENSIONS.has(path.extname(filename).slice(1));
15
+ }
16
+ function getManifestSAPUI5DependencyIds(files) {
17
+ const manifestFile = files.get("manifest.json");
18
+ return manifestFile ? getSAPUI5DependencyIds(bufferToJson(manifestFile)) : [];
19
+ }
20
+ function getI18nPropertyKeys(files) {
21
+ const keys = new Set();
22
+ files.forEach((content, filename) => {
23
+ if (filename.endsWith(".properties")) {
24
+ const lines = bufferToString(content).split("\n").filter(line => !line.startsWith("#"));
25
+ for (const line of lines) {
26
+ const [key] = line.split("=");
27
+ if (key) {
28
+ keys.add(key);
29
+ }
30
+ }
31
+ }
32
+ });
33
+ return [...keys];
34
+ }
35
+ /**
36
+ * Renames references (old id → adaptation project id) across all text files in
37
+ * place, leaving i18n .properties files, their keys and SAPUI5 dependency ids
38
+ * untouched.
39
+ */
40
+ export default class FileRenamer {
41
+ /**
42
+ * 1p. i18n.properties are not renamed and property keys are ignored during
43
+ * the renaming across the files. Stateless: does not use `this`.
44
+ * @param files - all files both base and appVariants
45
+ * @param references - Reference maps of the app variant
46
+ */
47
+ static rename(files, references) {
48
+ const IGNORE_EXTENSIONS = [".properties"];
49
+ const ignoreInString = [
50
+ ...getI18nPropertyKeys(files),
51
+ ...getManifestSAPUI5DependencyIds(files)
52
+ ];
53
+ const updates = new Map();
54
+ for (const [filename, content] of files) {
55
+ if (!isTextFile(filename)) {
56
+ continue;
57
+ }
58
+ if (!IGNORE_EXTENSIONS.some(ext => filename.endsWith(ext))) {
59
+ // 5p. We pass replacements as ignores since we don't want to
60
+ // rename them again. E.g. we replace app.id with
61
+ // customer.app.id, but if we found customer.app.id somewhere,
62
+ // because we applied changes or something, we don't want to
63
+ // rename it again to customer.customer.app.id.
64
+ const renamed = renameMap(bufferToString(content), references, [...references.values(), ...ignoreInString]);
65
+ updates.set(filename, stringToBuffer(renamed));
66
+ }
67
+ }
68
+ for (const [filename, content] of updates) {
69
+ files.set(filename, content);
70
+ }
71
+ }
72
+ }
73
+ __decorate([
74
+ restoreWhatShouldntBeRenamed()
75
+ ], FileRenamer, "rename", null);
76
+ /**
77
+ * Snapshots the JSON paths that must survive renaming (e.g.
78
+ * sap.ui5/appVariantIdHierarchy, dependencies), runs the decorated method, then
79
+ * restores them. The decorated method receives a Map of files (filename →
80
+ * Buffer): every file a handler `accept`s is parsed, snapshotted before the
81
+ * renaming and restored + written back after. Each matching file gets its own
82
+ * handler instance so one file's snapshot can't clobber another's.
83
+ */
84
+ export function restoreWhatShouldntBeRenamed() {
85
+ return function (_target, _propertyKey, descriptor) {
86
+ const handlerFactories = [() => new ManifestRenamingHandler()];
87
+ const originalValue = descriptor.value;
88
+ descriptor.value = function (...args) {
89
+ const files = args[0];
90
+ // Snapshot the protected paths with a dedicated handler instance per
91
+ // file, so one file's snapshot can't clobber another's. Multiple
92
+ // files can match a handler (e.g. the root manifest.json and a
93
+ // nested cards/.../manifest.json both end with "manifest.json"); a
94
+ // shared handler would let the last-snapshotted file (which may lack
95
+ // appVariantIdHierarchy) wipe the root manifest's value on restore.
96
+ const snapshots = [];
97
+ for (const [filename, content] of files) {
98
+ for (const createHandler of handlerFactories) {
99
+ const handler = createHandler();
100
+ if (handler.accept(filename)) {
101
+ handler.snapshot(bufferToJson(content));
102
+ snapshots.push({ filename, handler });
103
+ }
104
+ }
105
+ }
106
+ originalValue.apply(this, args);
107
+ for (const { filename, handler } of snapshots) {
108
+ const content = files.get(filename);
109
+ if (content) {
110
+ const json = bufferToJson(content);
111
+ handler.restore(json);
112
+ files.set(filename, jsonToBuffer(json));
113
+ }
114
+ }
115
+ return files;
116
+ };
117
+ };
118
+ }
119
+ ;
120
+ //# sourceMappingURL=fileRenamer.js.map
@@ -3,8 +3,6 @@ export default abstract class JsonRenamingHandler {
3
3
  protected abstract filePath: string;
4
4
  protected abstract jsonPathsToRestore: string[];
5
5
  accept(filename: string): boolean;
6
- before(json: any): void;
7
- after(json: any): void;
8
6
  snapshot(json: any): void;
9
7
  restore(obj: any): void;
10
8
  protected store(obj: any, path: string): void;
@@ -3,12 +3,6 @@ export default class JsonRenamingHandler {
3
3
  accept(filename) {
4
4
  return filename.endsWith(this.filePath);
5
5
  }
6
- before(json) {
7
- this.jsonPathsToRestore.forEach(path => this.store(json, path));
8
- }
9
- after(json) {
10
- this.restore(json);
11
- }
12
6
  snapshot(json) {
13
7
  this.jsonPathsToRestore.forEach(path => this.store(json, path));
14
8
  }
@@ -1,20 +1,20 @@
1
1
  export declare function rename(content: string, references: string[], replacement: string): string;
2
2
  /**
3
- * A precompiled renamer: the regex, replacement lookup and ignore set are built
3
+ * A precompiled replacer: the regex, replacement lookup and ignore set are built
4
4
  * once and can be applied to many strings via `replace` without recompilation.
5
5
  */
6
- export interface Renamer {
6
+ export interface CompiledReplacer {
7
7
  replace(content: string): string;
8
8
  }
9
9
  /**
10
- * Builds a reusable {@link Renamer} from the given references and ignore list.
10
+ * Builds a reusable {@link CompiledReplacer} from the given references and ignore list.
11
11
  * The expensive work (regex compilation, lookup construction) happens here once;
12
12
  * the returned `replace` is a cheap single pass over each input string. Prefer
13
13
  * this over calling {@link renameMap} repeatedly with the same references.
14
14
  *
15
15
  * Note: mutates `references` by dropping entries whose dot-count doesn't match.
16
16
  */
17
- export declare function compileRenamer(references: Map<string, string>, ignoreInStrings: string[]): Renamer;
17
+ export declare function compileReplacer(references: Map<string, string>, ignoreInStrings: string[]): CompiledReplacer;
18
18
  export declare function renameMap(content: string, references: Map<string, string>, ignoreInStrings: string[]): string;
19
19
  export declare function renameJson(json: Record<string, unknown>, references: Map<string, string>, ignoreInStrings: string[]): void;
20
20
  export declare function renameResources(files: ReadonlyMap<string, string>, search: string[], replacement: string): Map<string, string>;
@@ -1,19 +1,19 @@
1
1
  import { getLogger } from "@ui5/logger";
2
- const log = getLogger("@ui5/task-adaptation::RenamingUtil");
2
+ const log = getLogger("@ui5/task-adaptation::ReferenceRenaming");
3
3
  const dotToSlash = (string) => string.replaceAll(".", "/");
4
4
  const escapeRegex = (string) => string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
5
5
  export function rename(content, references, replacement) {
6
6
  return renameMap(content, new Map(references.map(ref => [ref, replacement])), [replacement]);
7
7
  }
8
8
  /**
9
- * Builds a reusable {@link Renamer} from the given references and ignore list.
9
+ * Builds a reusable {@link CompiledReplacer} from the given references and ignore list.
10
10
  * The expensive work (regex compilation, lookup construction) happens here once;
11
11
  * the returned `replace` is a cheap single pass over each input string. Prefer
12
12
  * this over calling {@link renameMap} repeatedly with the same references.
13
13
  *
14
14
  * Note: mutates `references` by dropping entries whose dot-count doesn't match.
15
15
  */
16
- export function compileRenamer(references, ignoreInStrings) {
16
+ export function compileReplacer(references, ignoreInStrings) {
17
17
  const ignoredReferenceKeys = [];
18
18
  for (const [key, value] of references) {
19
19
  if (value.includes(".") !== key.includes(".")) {
@@ -96,13 +96,13 @@ export function compileRenamer(references, ignoreInStrings) {
96
96
  };
97
97
  }
98
98
  export function renameMap(content, references, ignoreInStrings) {
99
- return compileRenamer(references, ignoreInStrings).replace(content);
99
+ return compileReplacer(references, ignoreInStrings).replace(content);
100
100
  }
101
101
  export function renameJson(json, references, ignoreInStrings) {
102
- const renamer = compileRenamer(references, ignoreInStrings);
102
+ const replacer = compileReplacer(references, ignoreInStrings);
103
103
  function processObject(obj) {
104
104
  for (const key of Object.keys(obj)) {
105
- const renamedKey = renamer.replace(key);
105
+ const renamedKey = replacer.replace(key);
106
106
  const value = obj[key];
107
107
  if (renamedKey !== key) {
108
108
  obj[renamedKey] = value;
@@ -115,7 +115,7 @@ export function renameJson(json, references, ignoreInStrings) {
115
115
  const target = container;
116
116
  const value = target[index];
117
117
  if (typeof value === "string") {
118
- target[index] = renamer.replace(value);
118
+ target[index] = replacer.replace(value);
119
119
  }
120
120
  else if (Array.isArray(value)) {
121
121
  value.forEach((_, i) => processValue(value, i));
@@ -129,4 +129,4 @@ export function renameJson(json, references, ignoreInStrings) {
129
129
  export function renameResources(files, search, replacement) {
130
130
  return new Map([...files].map(([filepath, content]) => [filepath, rename(content, search, replacement)]));
131
131
  }
132
- //# sourceMappingURL=renamingUtil.js.map
132
+ //# sourceMappingURL=referenceRenaming.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ui5/task-adaptation",
3
- "version": "1.6.8",
3
+ "version": "1.7.0",
4
4
  "description": "Custom task for ui5-builder which allows building UI5 Flexibility Adaptation Projects for SAP BTP, Cloud Foundry environment",
5
5
  "main": "dist/index.js",
6
6
  "exports": {
@@ -10,8 +10,9 @@
10
10
  }
11
11
  },
12
12
  "scripts": {
13
- "test": "npm run lint && npm run build && npm run coverage",
13
+ "test": "npm run lint && npm run build && npm run typecheck:test && npm run coverage",
14
14
  "lint": "eslint ./src ./test/lib",
15
+ "typecheck:test": "tsc -p ./test/lib/tsconfig.json --noEmit",
15
16
  "dev": "mocha --no-timeouts --no-warnings --import=tsx \"test/lib/**/*.spec.ts\"",
16
17
  "perf": "mocha --no-timeouts --no-warnings --import=tsx \"test/lib/**/*.perf.ts\"",
17
18
  "coverage": "c8 npm run dev",
@@ -132,4 +133,4 @@
132
133
  "@rollup/rollup-linux-x64-gnu": "^4.28.1",
133
134
  "@rollup/rollup-linux-x64-musl": "^4.28.1"
134
135
  }
135
- }
136
+ }