@ui5/task-adaptation 1.6.6 → 1.6.8

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 (78) hide show
  1. package/CHANGELOG.md +9 -1
  2. package/dist/adapters/abapAdapter.d.ts +2 -1
  3. package/dist/adapters/abapAdapter.js +3 -0
  4. package/dist/adapters/adapter.d.ts +2 -1
  5. package/dist/adapters/cfAdapter.d.ts +2 -1
  6. package/dist/adapters/cfAdapter.js +14 -0
  7. package/dist/adapters/commands/command.d.ts +7 -7
  8. package/dist/adapters/commands/command.js +3 -2
  9. package/dist/adapters/commands/downloadAnnotationsCommand.d.ts +1 -1
  10. package/dist/adapters/commands/downloadAnnotationsCommand.js +4 -3
  11. package/dist/adapters/commands/fetchPreviewResourcesCommand.js +4 -2
  12. package/dist/adapters/commands/filterFilesCommand.d.ts +1 -1
  13. package/dist/adapters/commands/filterFilesCommand.js +2 -1
  14. package/dist/adapters/commands/i18nPropertiesMergeCommand.d.ts +1 -1
  15. package/dist/adapters/commands/i18nPropertiesMergeCommand.js +9 -6
  16. package/dist/adapters/commands/omitDeletedResourcesCommand.d.ts +1 -1
  17. package/dist/adapters/commands/omitDeletedResourcesCommand.js +1 -1
  18. package/dist/adapters/commands/processPreviewResourcesCommand.d.ts +1 -1
  19. package/dist/adapters/commands/processPreviewResourcesCommand.js +14 -7
  20. package/dist/adapters/commands/renameFilesCommand.d.ts +10 -2
  21. package/dist/adapters/commands/renameFilesCommand.js +45 -17
  22. package/dist/adapters/commands/renameManifestCommand.d.ts +14 -0
  23. package/dist/adapters/commands/renameManifestCommand.js +32 -0
  24. package/dist/adapters/commands/writeResourcesCommand.d.ts +1 -1
  25. package/dist/adapters/commands/xsAppJsonEnhanceRoutesCommand.d.ts +1 -1
  26. package/dist/adapters/commands/xsAppJsonEnhanceRoutesCommand.js +4 -3
  27. package/dist/adapters/commands/xsAppJsonMergeCommand.d.ts +1 -1
  28. package/dist/adapters/commands/xsAppJsonMergeCommand.js +4 -3
  29. package/dist/adapters/previewAdapter.d.ts +2 -1
  30. package/dist/adapters/previewAdapter.js +3 -0
  31. package/dist/annotations/abapAnnotationManager.js +1 -1
  32. package/dist/annotations/dataSource/dataSourceManager.d.ts +2 -1
  33. package/dist/annotations/dataSource/dataSourceManager.js +16 -8
  34. package/dist/appVariant.d.ts +3 -3
  35. package/dist/appVariant.js +7 -7
  36. package/dist/baseApp.d.ts +3 -3
  37. package/dist/baseApp.js +4 -4
  38. package/dist/bundle.js +71 -34
  39. package/dist/cache/cacheHolder.d.ts +11 -2
  40. package/dist/cache/cacheHolder.js +23 -1
  41. package/dist/index.d.ts +10 -1
  42. package/dist/index.js +24 -4
  43. package/dist/landscapeConfiguration.d.ts +3 -3
  44. package/dist/landscapeConfiguration.js +38 -28
  45. package/dist/model/types.d.ts +5 -1
  46. package/dist/repositories/abapProvider.js +2 -2
  47. package/dist/repositories/abapRepository.d.ts +1 -1
  48. package/dist/repositories/cacheRepository.d.ts +10 -0
  49. package/dist/repositories/cacheRepository.js +29 -0
  50. package/dist/repositories/html5Repository.d.ts +1 -1
  51. package/dist/repositories/localRepository.d.ts +1 -1
  52. package/dist/repositories/repository.d.ts +1 -1
  53. package/dist/util/cfUtil.d.ts +6 -24
  54. package/dist/util/cfUtil.js +36 -93
  55. package/dist/util/commonUtil.d.ts +6 -1
  56. package/dist/util/commonUtil.js +16 -1
  57. package/dist/util/fsUtil.d.ts +2 -2
  58. package/dist/util/fsUtil.js +4 -4
  59. package/dist/util/manifestUtil.d.ts +6 -0
  60. package/dist/util/manifestUtil.js +15 -0
  61. package/dist/util/movingHandler/changeFileMoveHandler.d.ts +3 -3
  62. package/dist/util/movingHandler/changeFileMoveHandler.js +5 -4
  63. package/dist/util/renamingHandlers/jsonRenamingHandler.d.ts +6 -5
  64. package/dist/util/renamingHandlers/jsonRenamingHandler.js +15 -15
  65. package/dist/util/renamingUtil.d.ts +17 -0
  66. package/dist/util/renamingUtil.js +102 -81
  67. package/dist/util/resourceUtil.d.ts +15 -6
  68. package/dist/util/resourceUtil.js +50 -14
  69. package/dist/util/validator/abapConnectionValidator.d.ts +5 -0
  70. package/dist/util/validator/abapConnectionValidator.js +22 -0
  71. package/dist/util/validator/abapValidator.d.ts +2 -4
  72. package/dist/util/validator/abapValidator.js +4 -16
  73. package/dist/util/zipUtil.d.ts +1 -1
  74. package/dist/util/zipUtil.js +1 -1
  75. package/package.json +14 -8
  76. package/types/ui5.d.ts +2 -0
  77. package/dist/util/renamingHandlers/renamingHandler.d.ts +0 -4
  78. package/dist/util/renamingHandlers/renamingHandler.js +0 -2
@@ -1,16 +1,17 @@
1
1
  import { merge } from "../../util/cf/xsAppJsonUtil.js";
2
2
  import { MergeCommand } from "./command.js";
3
+ import { stringToBuffer, bufferToString } from "../../util/commonUtil.js";
3
4
  export default class XsAppJsonMergeCommand extends MergeCommand {
4
5
  accept = (filename) => filename === "xs-app.json";
5
6
  async execute(files, filename, appVariantContent) {
6
7
  const baseContent = files.get(filename);
7
8
  const xsAppJsonFiles = [
8
- appVariantContent,
9
- baseContent // base app xs-app.json comes last
9
+ bufferToString(appVariantContent),
10
+ baseContent ? bufferToString(baseContent) : undefined // base app xs-app.json comes last
10
11
  ].filter(file => file !== undefined);
11
12
  const result = merge(xsAppJsonFiles);
12
13
  if (result) {
13
- files.set(filename, result);
14
+ files.set(filename, stringToBuffer(result));
14
15
  }
15
16
  }
16
17
  }
@@ -5,7 +5,7 @@ import { UI5BuilderTools } from "../model/types.js";
5
5
  import IRepository from "../repositories/repository.js";
6
6
  import { IAdapter } from "./adapter.js";
7
7
  import CFAdapter from "./cfAdapter.js";
8
- import { AdaptCommandChain, PostCommandChain, SetupCommandChain } from "./commands/command.js";
8
+ import { AdaptCommandChain, ManifestUpdateCommandChain, PostCommandChain, SetupCommandChain } from "./commands/command.js";
9
9
  export default class PreviewAdapter implements IAdapter {
10
10
  private configuration;
11
11
  private cfAdapter;
@@ -14,5 +14,6 @@ export default class PreviewAdapter implements IAdapter {
14
14
  constructor(configuration: IConfiguration, cfAdapter?: CFAdapter);
15
15
  createSetupCommandChain(appId: string, repository: IRepository): SetupCommandChain;
16
16
  createAdaptCommandChain(baseApp: BaseApp, appVariant: AppVariant): AdaptCommandChain;
17
+ createManifestPreviewCommandChain(baseApp: BaseApp, appVariant: AppVariant): ManifestUpdateCommandChain;
17
18
  createPostCommandChain(references: Map<string, string>, adaptationProject: AppVariant, ui5BuilderTools: UI5BuilderTools): PostCommandChain;
18
19
  }
@@ -25,6 +25,9 @@ export default class PreviewAdapter {
25
25
  createAdaptCommandChain(baseApp, appVariant) {
26
26
  return this.cfAdapter.createAdaptCommandChain(baseApp, appVariant);
27
27
  }
28
+ createManifestPreviewCommandChain(baseApp, appVariant) {
29
+ return this.cfAdapter.createManifestPreviewCommandChain(baseApp, appVariant);
30
+ }
28
31
  createPostCommandChain(references, adaptationProject, ui5BuilderTools) {
29
32
  const serviceCredentialsPromise = dependsOn(this.fetchEndpointsCommand);
30
33
  const previewPromise = dependsOn(this.fetchPreviewResourcesCommand);
@@ -22,7 +22,7 @@ export default class AbapAnnotationManager {
22
22
  const i18nManager = new I18nManager(modelName, appVariantId, languages);
23
23
  const serviceRequestor = new ServiceRequestor(this.configuration, this.repository);
24
24
  const dataSourceManager = new DataSourceManager();
25
- dataSourceManager.addDataSources(baseAppManifest["sap.app"]?.dataSources);
25
+ await dataSourceManager.addDataSources(baseAppManifest["sap.app"]?.dataSources);
26
26
  const annotationFiles = await dataSourceManager.createAnnotationFiles(languages, i18nManager, serviceRequestor);
27
27
  const i18nFiles = i18nManager.createFiles(i18nPathName);
28
28
  if (i18nManager.hasTranslations()) {
@@ -3,10 +3,11 @@ import Language from "../../model/language.js";
3
3
  import ServiceRequestor from "../serviceRequestor.js";
4
4
  export default class DataSourceManager {
5
5
  private dataSources;
6
+ private shouldDownload;
6
7
  /**
7
8
  * Parses dataSources from manifest.json.
8
9
  * @param dataSourcesJson manifest.json/sap.app/dataSources node
9
10
  */
10
- addDataSources(dataSourcesJson: any): void;
11
+ addDataSources(dataSourcesJson: any): Promise<void>;
11
12
  createAnnotationFiles(languages: Language[], i18nManager: I18nManager, serviceRequestor: ServiceRequestor): Promise<Map<string, string>>;
12
13
  }
@@ -1,20 +1,26 @@
1
- import DataSourceOData from "./dataSourceOData.js";
2
- import DataSourceODataAnnotation from "./dataSourceODataAnnotation.js";
3
1
  import path from "path/posix";
4
2
  export default class DataSourceManager {
5
3
  dataSources = new Array();
4
+ shouldDownload(dataSource, type) {
5
+ // is remote service, not local file
6
+ return dataSource.uri?.startsWith("/") &&
7
+ // no local metadata file and wasn't downloaded yet
8
+ !dataSource.settings?.ignoreAnnotationsFromMetadata &&
9
+ dataSource.type === type;
10
+ }
6
11
  /**
7
12
  * Parses dataSources from manifest.json.
8
13
  * @param dataSourcesJson manifest.json/sap.app/dataSources node
9
14
  */
10
- addDataSources(dataSourcesJson) {
15
+ async addDataSources(dataSourcesJson) {
11
16
  if (!dataSourcesJson) {
12
17
  return;
13
18
  }
14
19
  const odataAnnotationMap = new Map();
15
20
  // Loop over OData first to collect linked annotation names
16
21
  for (const [name, dataSource] of Object.entries(dataSourcesJson)) {
17
- if (dataSource.uri?.startsWith("/") && dataSource.type === "OData") {
22
+ if (this.shouldDownload(dataSource, "OData")) {
23
+ const { default: DataSourceOData } = await import("./dataSourceOData.js");
18
24
  const uri = path.normalize(dataSource.uri + "/$metadata");
19
25
  const odata = new DataSourceOData(name, uri, dataSource);
20
26
  odata.getAnnotations().forEach(annotation => odataAnnotationMap.set(annotation, uri));
@@ -23,10 +29,12 @@ export default class DataSourceManager {
23
29
  }
24
30
  // If ODataAnnotation is in OData annotations, pass metadata url to it
25
31
  for (const [name, dataSource] of Object.entries(dataSourcesJson)) {
26
- const uri = dataSource.uri;
27
- const metadataUrl = odataAnnotationMap.get(name);
28
- if (uri?.startsWith("/") && dataSource.type === "ODataAnnotation") {
29
- this.dataSources.push(new DataSourceODataAnnotation(name, uri, dataSource, metadataUrl));
32
+ if (this.shouldDownload(dataSource, "ODataAnnotation")) {
33
+ const { default: DataSourceODataAnnotation } = await import("./dataSourceODataAnnotation.js");
34
+ const uri = dataSource.uri;
35
+ const metadataUrl = odataAnnotationMap.get(name);
36
+ const odata = new DataSourceODataAnnotation(name, uri, dataSource, metadataUrl);
37
+ this.dataSources.push(odata);
30
38
  }
31
39
  }
32
40
  for (const dataSource of this.dataSources) {
@@ -1,6 +1,6 @@
1
1
  import { IChange } from "./model/types.js";
2
2
  export default class AppVariant {
3
- readonly files: ReadonlyMap<string, string>;
3
+ readonly files: ReadonlyMap<string, Buffer>;
4
4
  readonly resources?: ReadonlyArray<Resource>;
5
5
  readonly id: string;
6
6
  readonly reference: string;
@@ -10,9 +10,9 @@ export default class AppVariant {
10
10
  movedFiles: Map<string, string>;
11
11
  private renaming;
12
12
  static fromWorkspace(workspace: IWorkspace, projectNamespace: string): Promise<AppVariant>;
13
- static fromFiles(files: ReadonlyMap<string, string>): AppVariant;
13
+ static fromFiles(files: ReadonlyMap<string, Buffer>): AppVariant;
14
14
  private constructor();
15
- getProcessedFiles(): ReadonlyMap<string, string>;
15
+ getProcessedFiles(): ReadonlyMap<string, Buffer>;
16
16
  /**
17
17
  * Since we moved files, we need to update paths where they were referenced.
18
18
  * To do this we use renameMap function along with renaming ids.
@@ -1,5 +1,5 @@
1
- import { dotToUnderscore, isManifestChange } from "./util/commonUtil.js";
2
- import ResourceUtil from "./util/resourceUtil.js";
1
+ import { bufferToJson, dotToUnderscore, isManifestChange } from "./util/commonUtil.js";
2
+ import ResourceUtil, { TEXT_EXTENSIONS } from "./util/resourceUtil.js";
3
3
  import { posix as path } from "path";
4
4
  import { moveFile, moveFiles } from "./util/movingHandler/changeFileMoveHandler.js";
5
5
  import RenameFilesCommand from "./adapters/commands/renameFilesCommand.js";
@@ -16,7 +16,7 @@ export default class AppVariant {
16
16
  movedFiles = new Map();
17
17
  renaming = new Map();
18
18
  static async fromWorkspace(workspace, projectNamespace) {
19
- 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";
19
+ const EXTENSIONS_TO_PROCESS = Array.from(TEXT_EXTENSIONS).join(",");
20
20
  const resources = await workspace.byGlob(`/**/*.{${EXTENSIONS_TO_PROCESS}}`);
21
21
  const files = await ResourceUtil.toFileMap(resources, projectNamespace);
22
22
  return new AppVariant(files, resources);
@@ -30,9 +30,9 @@ export default class AppVariant {
30
30
  }
31
31
  this.files = files;
32
32
  this.resources = resources;
33
- const manifestString = files.get("manifest.appdescr_variant");
34
- this.validateManifest(manifestString);
35
- const { reference, id, layer, content } = JSON.parse(manifestString);
33
+ const manifestBuffer = files.get("manifest.appdescr_variant");
34
+ this.validateManifest(manifestBuffer);
35
+ const { reference, id, layer, content } = bufferToJson(manifestBuffer);
36
36
  this.reference = reference;
37
37
  this.id = id;
38
38
  validateAppId(id);
@@ -70,9 +70,9 @@ export default class AppVariant {
70
70
  const changeFileChanges = [];
71
71
  this.files.forEach((content, filename) => {
72
72
  if (filename.endsWith(CHANGES_EXT)) {
73
- const change = JSON.parse(content);
74
73
  if (isManifestChange(filename, content)) {
75
74
  const { newFilename } = moveFile(filename, content, this.prefix, this.id);
75
+ const change = bufferToJson(content);
76
76
  this.updateRelativePaths(change, newFilename);
77
77
  changeFileChanges.push(change);
78
78
  }
package/dist/baseApp.d.ts CHANGED
@@ -15,13 +15,13 @@ export interface IManifestInfo extends IManifestIdVersion {
15
15
  * @param files - Map of all files
16
16
  * @returns Map with .js files replaced by -dbg.js content where applicable
17
17
  */
18
- export declare function preProcessFiles(files: ReadonlyMap<string, string>): Map<string, string>;
18
+ export declare function preProcessFiles(files: ReadonlyMap<string, Buffer>): Map<string, Buffer>;
19
19
  export default class BaseApp {
20
20
  readonly id: string;
21
21
  readonly version: string;
22
22
  readonly i18nPath: string;
23
- readonly files: ReadonlyMap<string, string>;
24
- static fromFiles(files: ReadonlyMap<string, string>): BaseApp;
23
+ readonly files: ReadonlyMap<string, Buffer>;
24
+ static fromFiles(files: ReadonlyMap<string, Buffer>): BaseApp;
25
25
  private constructor();
26
26
  private extractI18nPathFromManifest;
27
27
  private VALIDATION_RULES;
package/dist/baseApp.js CHANGED
@@ -1,4 +1,4 @@
1
- import { trimExtension } from "./util/commonUtil.js";
1
+ import { bufferToJson, trimExtension } from "./util/commonUtil.js";
2
2
  import { validateAppId } from "./util/validator/validator.js";
3
3
  const IGNORE_FILES = [
4
4
  "manifest-bundle.zip",
@@ -48,11 +48,11 @@ export default class BaseApp {
48
48
  throw new Error("Original application sources are empty");
49
49
  }
50
50
  this.files = preProcessFiles(files);
51
- const manifestString = files.get("manifest.json");
52
- if (!manifestString) {
51
+ const manifestBuffer = this.files.get("manifest.json");
52
+ if (!manifestBuffer) {
53
53
  throw new Error("Original application should have manifest.json in root folder");
54
54
  }
55
- const manifest = JSON.parse(manifestString);
55
+ const manifest = bufferToJson(manifestBuffer);
56
56
  this.id = manifest["sap.app"]?.id;
57
57
  this.version = manifest["sap.app"]?.applicationVersion?.version;
58
58
  this.validateProperty(this.id, "sap.app/id");
package/dist/bundle.js CHANGED
@@ -2183,34 +2183,52 @@ function processTerminologies(oChangeContent, sAppId) {
2183
2183
  });
2184
2184
  }
2185
2185
  }
2186
+ function getEnhancementSettings(oChange, oChangeContent, sAppId) {
2187
+ const sI18n = oChange.getTexts()?.i18n;
2188
+ if (sI18n) {
2189
+ return {
2190
+ bundleName: ApplyUtil.formatBundleName(sAppId, sI18n)
2191
+ };
2192
+ }
2193
+ const oEnhancementSettings = Object.assign({}, oChangeContent);
2194
+ oEnhancementSettings.terminologies &&= Object.keys(oEnhancementSettings.terminologies).reduce((oResult, sTerminology) => {
2195
+ oResult[sTerminology] = Object.assign({}, oEnhancementSettings.terminologies[sTerminology]);
2196
+ return oResult;
2197
+ }, {});
2198
+ if (hasBundleUrlAndNotBundleName(oEnhancementSettings)) {
2199
+ oEnhancementSettings.bundleName = ApplyUtil.formatBundleName(sAppId, oEnhancementSettings.bundleUrl);
2200
+ delete oEnhancementSettings.bundleUrl;
2201
+ }
2202
+ processTerminologies(oEnhancementSettings, sAppId);
2203
+ delete oEnhancementSettings.modelId;
2204
+ delete oEnhancementSettings.createIfMissing;
2205
+ return oEnhancementSettings;
2206
+ }
2186
2207
  const AddNewModelEnhanceWith = {
2187
2208
  applyChange(oManifest, oChange) {
2188
2209
  const oChangeContent = oChange.getContent();
2189
2210
  const sModelId = oChangeContent.modelId;
2190
- const oModel = oManifest["sap.ui5"].models[sModelId];
2191
- if (oModel) {
2192
- if (oModel.type && oModel.type === "sap.ui.model.resource.ResourceModel") {
2193
- if (!(oModel.settings && oModel.settings.enhanceWith)) {
2194
- ObjectPath.set("settings.enhanceWith", [], oModel);
2195
- }
2196
- const sAppId = oManifest["sap.app"].id;
2197
- const sI18n = oChange.getTexts()?.i18n;
2198
- const oEnhanceWith = oModel.settings.enhanceWith;
2199
- if (sI18n) {
2200
- oEnhanceWith.push({
2201
- bundleName: ApplyUtil.formatBundleName(sAppId, sI18n)
2202
- });
2203
- return oManifest;
2204
- }
2205
- if (hasBundleUrlAndNotBundleName(oChangeContent)) {
2206
- oChangeContent.bundleName = ApplyUtil.formatBundleName(sAppId, oChangeContent.bundleUrl);
2207
- delete oChangeContent.bundleUrl;
2208
- }
2209
- processTerminologies(oChangeContent, sAppId);
2210
- delete oChangeContent.modelId;
2211
- if (Object.keys(oChangeContent).length > 0) {
2212
- oEnhanceWith.push(oChangeContent);
2213
- }
2211
+ const oModels = oManifest["sap.ui5"].models;
2212
+ const sAppId = oManifest["sap.app"].id;
2213
+ const oEnhancementSettings = getEnhancementSettings(oChange, oChangeContent, sAppId);
2214
+ const oModel = oModels[sModelId];
2215
+ if (!oModel) {
2216
+ if (!oChangeContent.createIfMissing) {
2217
+ throw Error(`Model with ID '${sModelId}' does not exist. Set createIfMissing to true to create it.`);
2218
+ }
2219
+ oModels[sModelId] = {
2220
+ type: "sap.ui.model.resource.ResourceModel",
2221
+ settings: oEnhancementSettings
2222
+ };
2223
+ return oManifest;
2224
+ }
2225
+ if (oModel.type && oModel.type === "sap.ui.model.resource.ResourceModel") {
2226
+ if (!(oModel.settings && oModel.settings.enhanceWith)) {
2227
+ ObjectPath.set("settings.enhanceWith", [], oModel);
2228
+ }
2229
+ const oEnhanceWith = oModel.settings.enhanceWith;
2230
+ if (!fnIsEmptyObject(oEnhancementSettings)) {
2231
+ oEnhanceWith.push(oEnhancementSettings);
2214
2232
  }
2215
2233
  }
2216
2234
  return oManifest;
@@ -2303,7 +2321,7 @@ function Version(vMajor, iMinor, iPatch, sSuffix) {
2303
2321
  return sSuffix;
2304
2322
  };
2305
2323
  this.compareTo = function (vOtherMajor, iOtherMinor, iOtherPatch, sOtherSuffix) {
2306
- var vOther = Version.apply(window, arguments);
2324
+ var vOther = Version.apply(null, arguments);
2307
2325
  return vMajor - vOther.getMajor() || iMinor - vOther.getMinor() || iPatch - vOther.getPatch() || (sSuffix < vOther.getSuffix() ? -1 : sSuffix === vOther.getSuffix() ? 0 : 1);
2308
2326
  };
2309
2327
  }
@@ -4598,7 +4616,7 @@ var fnEqual = function (a, b, maxDepth, contains, depth) {
4598
4616
  if (!contains && Object.keys(a).length !== Object.keys(b).length) {
4599
4617
  return false;
4600
4618
  }
4601
- if (a instanceof Node) {
4619
+ if (typeof Node !== "undefined" && a instanceof Node) {
4602
4620
  return a.isEqualNode(b);
4603
4621
  }
4604
4622
  if (a instanceof Date) {
@@ -6323,7 +6341,8 @@ _Helper = {
6323
6341
  aSegments = sPath.split("/");
6324
6342
  aSegments.pop();
6325
6343
  while (aSegments.length) {
6326
- if (aAncestors.indexOf(aSegments.join("/")) >= 0) {
6344
+ const sPrefix = aSegments.join("/");
6345
+ if (aAncestors.includes(sPrefix) || aAncestors.includes(sPrefix + "/*")) {
6327
6346
  mChildren[sPath] = true;
6328
6347
  break;
6329
6348
  }
@@ -6503,8 +6522,20 @@ _Helper = {
6503
6522
  for (const sKey in oSource) {
6504
6523
  const vSourceProperty = oSource[sKey];
6505
6524
  const vTargetProperty = oTarget[sKey];
6506
- if (vSourceProperty && vTargetProperty && typeof vSourceProperty === "object" && !Array.isArray(vSourceProperty)) {
6507
- _Helper.copyETags(vSourceProperty, vTargetProperty);
6525
+ if (vSourceProperty && vTargetProperty) {
6526
+ if (Array.isArray(vSourceProperty)) {
6527
+ if (vTargetProperty.$byPredicate) {
6528
+ vSourceProperty.forEach(function (oSourceElement) {
6529
+ const sPredicate = _Helper.getPrivateAnnotation(oSourceElement, "predicate");
6530
+ const oTargetElement = vTargetProperty.$byPredicate[sPredicate];
6531
+ if (oTargetElement) {
6532
+ _Helper.copyETags(oSourceElement, oTargetElement);
6533
+ }
6534
+ });
6535
+ }
6536
+ } else if (typeof vSourceProperty === "object") {
6537
+ _Helper.copyETags(vSourceProperty, vTargetProperty);
6538
+ }
6508
6539
  }
6509
6540
  }
6510
6541
  },
@@ -6583,6 +6614,9 @@ _Helper = {
6583
6614
  },
6584
6615
  createMissing: function (oObject, aSegments) {
6585
6616
  aSegments.reduce(function (oCurrent, sSegment, i) {
6617
+ if (oCurrent === null) {
6618
+ return null;
6619
+ }
6586
6620
  if (!((sSegment in oCurrent))) {
6587
6621
  if (i + 1 < aSegments.length) {
6588
6622
  oCurrent[sSegment] = {};
@@ -6697,6 +6731,9 @@ _Helper = {
6697
6731
  return oCurrent && (sSegment in oCurrent) ? oCurrent[sSegment] : undefined;
6698
6732
  }, oObject);
6699
6733
  },
6734
+ dropQuery: function (sResourcePathWithQuery) {
6735
+ return sResourcePathWithQuery.split("?")[0];
6736
+ },
6700
6737
  encode: function (sPart, bEncodeEquals) {
6701
6738
  var sEncoded = encodeURI(sPart).replace(rAmpersand, "%26").replace(rHash, "%23").replace(rPlus, "%2B");
6702
6739
  if (bEncodeEquals) {
@@ -7037,7 +7074,7 @@ _Helper = {
7037
7074
  },
7038
7075
  getUsedPaths: function (aPaths, mQueryOptions) {
7039
7076
  function isRelated(sPath0, sPath1) {
7040
- return _Helper.hasPathPrefix(sPath0, sPath1) || _Helper.hasPathPrefix(sPath1, sPath0);
7077
+ return _Helper.isAffectedBy(sPath0, sPath1) || _Helper.isAffectedBy(sPath1, sPath0);
7041
7078
  }
7042
7079
  function isUsed(sPath, mQueryOptionsForPath) {
7043
7080
  let bPathMatchedExpand = false;
@@ -7119,15 +7156,15 @@ _Helper = {
7119
7156
  return i === 0 && bSkipFirstSegment || sSegment === "$count" || fnFetchMetadata(sRootMetaPath + "/" + aSegments.slice(0, i + 1).join("/")).getResult().$kind === "Property";
7120
7157
  });
7121
7158
  }
7122
- if (aPaths.indexOf("") >= 0) {
7159
+ if (aPaths.includes("")) {
7123
7160
  throw new Error("Unsupported empty navigation property path");
7124
7161
  }
7125
- if (aPaths.indexOf("*") >= 0) {
7162
+ if (aPaths.includes("*")) {
7126
7163
  aSelects = (mCacheQueryOptions && mCacheQueryOptions.$select || []).slice();
7127
7164
  if (sMessagesPath && !aSelects.includes(sMessagesPath)) {
7128
7165
  aSelects.push(sMessagesPath);
7129
7166
  }
7130
- } else if (mCacheQueryOptions && mCacheQueryOptions.$select && mCacheQueryOptions.$select.indexOf("*") < 0) {
7167
+ } else if (mCacheQueryOptions && mCacheQueryOptions.$select && !mCacheQueryOptions.$select.includes("*")) {
7131
7168
  _Helper.addChildrenWithAncestor(aPaths, mCacheQueryOptions.$select, mSelects);
7132
7169
  _Helper.addChildrenWithAncestor(mCacheQueryOptions.$select, aPaths, mSelects);
7133
7170
  if (sMessagesPath && aPaths.includes(sMessagesPath)) {
@@ -7608,7 +7645,7 @@ _Helper = {
7608
7645
  }
7609
7646
  if (fnCheckKeyPredicate && fnCheckKeyPredicate(sPath)) {
7610
7647
  sTargetPredicate = _Helper.getPrivateAnnotation(oTarget, "predicate");
7611
- if (sSourcePredicate !== sTargetPredicate) {
7648
+ if (sTargetPredicate && sTargetPredicate !== sSourcePredicate) {
7612
7649
  throw new Error("Key predicate of '" + sPath + "' changed from " + sTargetPredicate + " to " + sSourcePredicate);
7613
7650
  }
7614
7651
  } else {
@@ -1,8 +1,17 @@
1
1
  export default class CacheHolder {
2
2
  private static TEMP_TASK_DIR;
3
3
  static getTempDir(...paths: string[]): string;
4
- static read(repoName: string, token: string): Promise<Map<string, string>>;
5
- static write(repoName: string, token: string, files: Map<string, string>): Promise<void>;
4
+ static read(repoName: string, token: string): Promise<Map<string, Buffer>>;
5
+ /**
6
+ * Read the most recently cached files for the given repo, without knowing
7
+ * the cachebuster token up front. The @cached() decorator on
8
+ * IRepository.fetch calls delete(repoName) before every write, so there is
9
+ * at most one token subdirectory under <repoName>/ at any time.
10
+ * Returns an empty map if the repo has never been fetched
11
+ * (i.e. no full build has run yet).
12
+ */
13
+ static readLatest(repoName: string): Promise<Map<string, Buffer>>;
14
+ static write(repoName: string, token: string, files: Map<string, Buffer>): Promise<void>;
6
15
  private static isValid;
7
16
  /**
8
17
  * Clears cached files by repo name and token
@@ -18,6 +18,28 @@ export default class CacheHolder {
18
18
  }
19
19
  return Promise.resolve(new Map());
20
20
  }
21
+ /**
22
+ * Read the most recently cached files for the given repo, without knowing
23
+ * the cachebuster token up front. The @cached() decorator on
24
+ * IRepository.fetch calls delete(repoName) before every write, so there is
25
+ * at most one token subdirectory under <repoName>/ at any time.
26
+ * Returns an empty map if the repo has never been fetched
27
+ * (i.e. no full build has run yet).
28
+ */
29
+ static async readLatest(repoName) {
30
+ if (!this.isValid(repoName, "repoName")) {
31
+ throw new Error(`Cache read requires 'repoName' to be provided`);
32
+ }
33
+ const repoDir = this.getTempDir(repoName);
34
+ if (!fs.existsSync(repoDir)) {
35
+ throw new Error(`No cache found for '${repoName}'. Run a full build first.`);
36
+ }
37
+ const [tokenDir] = await fsPromises.readdir(repoDir);
38
+ if (!tokenDir) {
39
+ throw new Error(`Cache directory for '${repoName}' is empty. Run a full build first.`);
40
+ }
41
+ return ResourceUtil.byGlob(path.join(repoDir, tokenDir), "**/*");
42
+ }
21
43
  static async write(repoName, token, files) {
22
44
  this.delete(repoName);
23
45
  if (this.isValid(repoName, "repoName") && this.isValid(token, "token")) {
@@ -74,7 +96,7 @@ export function cached() {
74
96
  let files = await CacheHolder.read(appName, token);
75
97
  CacheHolder.clearOutdatedExcept(appName);
76
98
  if (files.size === 0) {
77
- log.verbose(`No cache for repo '${appName}' with token '${token}'. Fetching from HTML5 Repository.`);
99
+ log.verbose(`No cache for repo '${appName}' with token '${token}'. Fetching from repository.`);
78
100
  files = await originalValue.apply(this, args);
79
101
  await CacheHolder.write(appName, token, files);
80
102
  }
package/dist/index.d.ts CHANGED
@@ -2,5 +2,14 @@ import { ITaskParameters } from "./model/types.js";
2
2
  /**
3
3
  * Creates an appVariant bundle from the provided resources.
4
4
  */
5
- declare const _default: ({ workspace, options, taskUtil }: ITaskParameters) => Promise<ReadonlyMap<string, string>>;
5
+ declare const _default: ({ workspace, options, taskUtil }: ITaskParameters) => Promise<ReadonlyMap<string, Buffer<ArrayBufferLike>>>;
6
6
  export default _default;
7
+ /**
8
+ * Preview-time entry point (CF only). Produces the same manifest.json a
9
+ * full build would produce, without going to the HTML5 Repo.
10
+ * Precondition is a full build must have run at least once so the base app
11
+ * files are cached by the @cached() decorator on HTML5Repository.fetch. If
12
+ * the cache is empty, this throws with a message directing the user to run
13
+ * a full build first.
14
+ */
15
+ export declare function previewManifest({ workspace, options }: ITaskParameters): Promise<any>;
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as dotenv from "dotenv";
2
- import { getReferences, logBuilderVersion } from "./util/commonUtil.js";
2
+ import { bufferToString, getReferences, logBuilderVersion } from "./util/commonUtil.js";
3
3
  import AppVariant from "./appVariant.js";
4
4
  import BaseApp from "./baseApp.js";
5
5
  import { initialize } from "./landscapeConfiguration.js";
@@ -14,7 +14,7 @@ export default async ({ workspace, options, taskUtil }) => {
14
14
  };
15
15
  dotenv.config();
16
16
  logBuilderVersion();
17
- const { repository, adapter } = initialize(options.configuration);
17
+ const { repository, adapter } = await initialize(options.configuration);
18
18
  const adaptationProject = await AppVariant.fromWorkspace(workspace, options.projectNamespace);
19
19
  const setupCommandChain = adapter.createSetupCommandChain(adaptationProject.reference, repository);
20
20
  await setupCommandChain.execute();
@@ -34,11 +34,31 @@ export default async ({ workspace, options, taskUtil }) => {
34
34
  return adapter.createAdaptCommandChain(baseApp, appVariant).execute();
35
35
  };
36
36
  let files = await fetchFilesPromises.shift(); // original app files
37
- for (const appVariantFiles of fetchFilesPromises) {
38
- files = await adapt(files, AppVariant.fromFiles(await appVariantFiles));
37
+ for (const appVariantFilesPromise of fetchFilesPromises) {
38
+ const appVariantFiles = await appVariantFilesPromise;
39
+ files = await adapt(files, AppVariant.fromFiles(appVariantFiles));
39
40
  }
40
41
  files = await adapt(files, adaptationProject);
41
42
  const references = getReferences(appVariants, adaptationProject.id);
42
43
  return adapter.createPostCommandChain(references, adaptationProject, ui5BuilderTools).execute(files);
43
44
  };
45
+ /**
46
+ * Preview-time entry point (CF only). Produces the same manifest.json a
47
+ * full build would produce, without going to the HTML5 Repo.
48
+ * Precondition is a full build must have run at least once so the base app
49
+ * files are cached by the @cached() decorator on HTML5Repository.fetch. If
50
+ * the cache is empty, this throws with a message directing the user to run
51
+ * a full build first.
52
+ */
53
+ export async function previewManifest({ workspace, options }) {
54
+ const { repository, adapter } = await initialize(options.configuration, { useCacheRepository: true });
55
+ const adaptationProject = await AppVariant.fromWorkspace(workspace, options.projectNamespace);
56
+ const [baseAppResource] = await repository.getAppVariantIdHierarchy(adaptationProject.reference);
57
+ const cachedBaseAppFiles = await repository.fetch(baseAppResource);
58
+ const baseApp = BaseApp.fromFiles(cachedBaseAppFiles);
59
+ const files = new Map(cachedBaseAppFiles);
60
+ const manifestPreviewCommandChain = adapter.createManifestPreviewCommandChain(baseApp, adaptationProject);
61
+ await manifestPreviewCommandChain.execute(files, "manifest.json");
62
+ return JSON.parse(bufferToString(files.get("manifest.json")));
63
+ }
44
64
  //# sourceMappingURL=index.js.map
@@ -1,9 +1,9 @@
1
- import { IConfiguration } from "./model/types.js";
1
+ import { IConfiguration, IInitializeOptions } from "./model/types.js";
2
2
  import { IAdapter } from "./adapters/adapter.js";
3
3
  import IRepository from "./repositories/repository.js";
4
4
  import IAnnotationManager from "./annotations/annotationManager.js";
5
- export declare function initialize(configuration: IConfiguration): {
5
+ export declare function initialize(configuration: IConfiguration, options?: IInitializeOptions): Promise<{
6
6
  adapter: IAdapter;
7
7
  repository: IRepository;
8
8
  annotationManager: IAnnotationManager;
9
- };
9
+ }>;