@ui5/task-adaptation 1.6.6 → 1.6.7

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 (76) hide show
  1. package/CHANGELOG.md +5 -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 +4 -3
  35. package/dist/appVariant.js +14 -7
  36. package/dist/baseApp.d.ts +3 -3
  37. package/dist/baseApp.js +4 -4
  38. package/dist/bundle.js +24 -8
  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 +4 -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/commonUtil.d.ts +6 -1
  54. package/dist/util/commonUtil.js +16 -1
  55. package/dist/util/fsUtil.d.ts +2 -2
  56. package/dist/util/fsUtil.js +4 -4
  57. package/dist/util/manifestUtil.d.ts +6 -0
  58. package/dist/util/manifestUtil.js +15 -0
  59. package/dist/util/movingHandler/changeFileMoveHandler.d.ts +3 -3
  60. package/dist/util/movingHandler/changeFileMoveHandler.js +5 -4
  61. package/dist/util/renamingHandlers/jsonRenamingHandler.d.ts +6 -5
  62. package/dist/util/renamingHandlers/jsonRenamingHandler.js +15 -15
  63. package/dist/util/renamingUtil.d.ts +17 -0
  64. package/dist/util/renamingUtil.js +102 -81
  65. package/dist/util/resourceUtil.d.ts +15 -6
  66. package/dist/util/resourceUtil.js +45 -13
  67. package/dist/util/validator/abapConnectionValidator.d.ts +5 -0
  68. package/dist/util/validator/abapConnectionValidator.js +22 -0
  69. package/dist/util/validator/abapValidator.d.ts +2 -4
  70. package/dist/util/validator/abapValidator.js +4 -16
  71. package/dist/util/zipUtil.d.ts +1 -1
  72. package/dist/util/zipUtil.js +1 -1
  73. package/package.json +7 -7
  74. package/types/ui5.d.ts +2 -0
  75. package/dist/util/renamingHandlers/renamingHandler.d.ts +0 -4
  76. 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,10 @@ 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 fromProject(): Promise<AppVariant>;
14
+ static fromFiles(files: ReadonlyMap<string, Buffer>): AppVariant;
14
15
  private constructor();
15
- getProcessedFiles(): ReadonlyMap<string, string>;
16
+ getProcessedFiles(): ReadonlyMap<string, Buffer>;
16
17
  /**
17
18
  * Since we moved files, we need to update paths where they were referenced.
18
19
  * 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,11 +16,18 @@ 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);
23
23
  }
24
+ static async fromProject() {
25
+ // previewManifest only merges manifest changes, that is why we
26
+ // just need the app variant descriptor and *.change files from the workspace.
27
+ const EXTENSIONS_TO_PROCESS = "change,appdescr_variant";
28
+ const files = await ResourceUtil.byGlobInProject(`/**/*.{${EXTENSIONS_TO_PROCESS}}`);
29
+ return new AppVariant(files);
30
+ }
24
31
  static fromFiles(files) {
25
32
  return new AppVariant(files);
26
33
  }
@@ -30,9 +37,9 @@ export default class AppVariant {
30
37
  }
31
38
  this.files = files;
32
39
  this.resources = resources;
33
- const manifestString = files.get("manifest.appdescr_variant");
34
- this.validateManifest(manifestString);
35
- const { reference, id, layer, content } = JSON.parse(manifestString);
40
+ const manifestBuffer = files.get("manifest.appdescr_variant");
41
+ this.validateManifest(manifestBuffer);
42
+ const { reference, id, layer, content } = bufferToJson(manifestBuffer);
36
43
  this.reference = reference;
37
44
  this.id = id;
38
45
  validateAppId(id);
@@ -70,9 +77,9 @@ export default class AppVariant {
70
77
  const changeFileChanges = [];
71
78
  this.files.forEach((content, filename) => {
72
79
  if (filename.endsWith(CHANGES_EXT)) {
73
- const change = JSON.parse(content);
74
80
  if (isManifestChange(filename, content)) {
75
81
  const { newFilename } = moveFile(filename, content, this.prefix, this.id);
82
+ const change = bufferToJson(content);
76
83
  this.updateRelativePaths(change, newFilename);
77
84
  changeFileChanges.push(change);
78
85
  }
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
@@ -6323,7 +6323,8 @@ _Helper = {
6323
6323
  aSegments = sPath.split("/");
6324
6324
  aSegments.pop();
6325
6325
  while (aSegments.length) {
6326
- if (aAncestors.indexOf(aSegments.join("/")) >= 0) {
6326
+ const sPrefix = aSegments.join("/");
6327
+ if (aAncestors.includes(sPrefix) || aAncestors.includes(sPrefix + "/*")) {
6327
6328
  mChildren[sPath] = true;
6328
6329
  break;
6329
6330
  }
@@ -6503,8 +6504,20 @@ _Helper = {
6503
6504
  for (const sKey in oSource) {
6504
6505
  const vSourceProperty = oSource[sKey];
6505
6506
  const vTargetProperty = oTarget[sKey];
6506
- if (vSourceProperty && vTargetProperty && typeof vSourceProperty === "object" && !Array.isArray(vSourceProperty)) {
6507
- _Helper.copyETags(vSourceProperty, vTargetProperty);
6507
+ if (vSourceProperty && vTargetProperty) {
6508
+ if (Array.isArray(vSourceProperty)) {
6509
+ if (vTargetProperty.$byPredicate) {
6510
+ vSourceProperty.forEach(function (oSourceElement) {
6511
+ const sPredicate = _Helper.getPrivateAnnotation(oSourceElement, "predicate");
6512
+ const oTargetElement = vTargetProperty.$byPredicate[sPredicate];
6513
+ if (oTargetElement) {
6514
+ _Helper.copyETags(oSourceElement, oTargetElement);
6515
+ }
6516
+ });
6517
+ }
6518
+ } else if (typeof vSourceProperty === "object") {
6519
+ _Helper.copyETags(vSourceProperty, vTargetProperty);
6520
+ }
6508
6521
  }
6509
6522
  }
6510
6523
  },
@@ -6583,6 +6596,9 @@ _Helper = {
6583
6596
  },
6584
6597
  createMissing: function (oObject, aSegments) {
6585
6598
  aSegments.reduce(function (oCurrent, sSegment, i) {
6599
+ if (oCurrent === null) {
6600
+ return null;
6601
+ }
6586
6602
  if (!((sSegment in oCurrent))) {
6587
6603
  if (i + 1 < aSegments.length) {
6588
6604
  oCurrent[sSegment] = {};
@@ -7037,7 +7053,7 @@ _Helper = {
7037
7053
  },
7038
7054
  getUsedPaths: function (aPaths, mQueryOptions) {
7039
7055
  function isRelated(sPath0, sPath1) {
7040
- return _Helper.hasPathPrefix(sPath0, sPath1) || _Helper.hasPathPrefix(sPath1, sPath0);
7056
+ return _Helper.isAffectedBy(sPath0, sPath1) || _Helper.isAffectedBy(sPath1, sPath0);
7041
7057
  }
7042
7058
  function isUsed(sPath, mQueryOptionsForPath) {
7043
7059
  let bPathMatchedExpand = false;
@@ -7119,15 +7135,15 @@ _Helper = {
7119
7135
  return i === 0 && bSkipFirstSegment || sSegment === "$count" || fnFetchMetadata(sRootMetaPath + "/" + aSegments.slice(0, i + 1).join("/")).getResult().$kind === "Property";
7120
7136
  });
7121
7137
  }
7122
- if (aPaths.indexOf("") >= 0) {
7138
+ if (aPaths.includes("")) {
7123
7139
  throw new Error("Unsupported empty navigation property path");
7124
7140
  }
7125
- if (aPaths.indexOf("*") >= 0) {
7141
+ if (aPaths.includes("*")) {
7126
7142
  aSelects = (mCacheQueryOptions && mCacheQueryOptions.$select || []).slice();
7127
7143
  if (sMessagesPath && !aSelects.includes(sMessagesPath)) {
7128
7144
  aSelects.push(sMessagesPath);
7129
7145
  }
7130
- } else if (mCacheQueryOptions && mCacheQueryOptions.$select && mCacheQueryOptions.$select.indexOf("*") < 0) {
7146
+ } else if (mCacheQueryOptions && mCacheQueryOptions.$select && !mCacheQueryOptions.$select.includes("*")) {
7131
7147
  _Helper.addChildrenWithAncestor(aPaths, mCacheQueryOptions.$select, mSelects);
7132
7148
  _Helper.addChildrenWithAncestor(mCacheQueryOptions.$select, aPaths, mSelects);
7133
7149
  if (sMessagesPath && aPaths.includes(sMessagesPath)) {
@@ -7608,7 +7624,7 @@ _Helper = {
7608
7624
  }
7609
7625
  if (fnCheckKeyPredicate && fnCheckKeyPredicate(sPath)) {
7610
7626
  sTargetPredicate = _Helper.getPrivateAnnotation(oTarget, "predicate");
7611
- if (sSourcePredicate !== sTargetPredicate) {
7627
+ if (sTargetPredicate && sTargetPredicate !== sSourcePredicate) {
7612
7628
  throw new Error("Key predicate of '" + sPath + "' changed from " + sTargetPredicate + " to " + sSourcePredicate);
7613
7629
  }
7614
7630
  } 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({ 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({ options }) {
54
+ const { repository, adapter } = await initialize(options.configuration, { useCacheRepository: true });
55
+ const adaptationProject = await AppVariant.fromProject();
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
+ }>;
@@ -1,39 +1,31 @@
1
- import AbapAdapter from "./adapters/abapAdapter.js";
2
- import CFAdapter from "./adapters/cfAdapter.js";
3
- import PreviewAdapter from "./adapters/previewAdapter.js";
4
- import AbapRepository from "./repositories/abapRepository.js";
5
- import HTML5Repository from "./repositories/html5Repository.js";
6
- import LocalRepository from "./repositories/localRepository.js";
7
- import AbapAnnotationManager from "./annotations/abapAnnotationManager.js";
8
- import CFAnnotationManager from "./annotations/cfAnnotationManager.js";
9
1
  import CFValidator from "./util/validator/cfValidator.js";
10
- import AbapValidator from "./util/validator/abapValidator.js";
11
2
  import { isOneOf } from "./util/validator/validator.js";
12
3
  import { LANDSCAPE_TYPES } from "./model/configuration.js";
13
4
  import { getLogger } from "@ui5/logger";
5
+ import AbapValidator from "./util/validator/abapValidator.js";
14
6
  import LocalAnnotationManager from "./annotations/localAnnotationManager.js";
15
7
  const log = getLogger("@ui5/task-adaptation::LandscapeConfiguration");
16
- export function initialize(configuration) {
17
- const type = getTypeByConfiguration(configuration);
8
+ export async function initialize(configuration, options = {}) {
9
+ const type = await getTypeByConfiguration(configuration);
18
10
  const enhancedConfig = { ...configuration, type };
19
- const repository = getRepository(enhancedConfig);
20
- const annotationManager = getAnnotationManager(enhancedConfig, repository);
11
+ const repository = await getRepository(enhancedConfig, options);
12
+ const annotationManager = await getAnnotationManager(enhancedConfig, repository);
21
13
  return {
22
- adapter: getAdapter(enhancedConfig, annotationManager),
14
+ adapter: await getAdapter(enhancedConfig, annotationManager),
23
15
  repository,
24
16
  annotationManager
25
17
  };
26
18
  }
27
- function getTypeByConfiguration(configuration) {
28
- const validators = [
29
- new CFValidator(),
30
- new AbapValidator(),
31
- ];
19
+ async function getTypeByConfiguration(configuration) {
32
20
  if (isOneOf(LANDSCAPE_TYPES, configuration.type)) {
33
21
  return configuration.type;
34
22
  }
35
23
  else {
36
24
  log.warn("Deprecated behavior: type is not specified or invalid. Should be either 'cf' or 'abap'. Trying to detect the type based on provided configuration");
25
+ const validators = [
26
+ new CFValidator(),
27
+ new AbapValidator(),
28
+ ];
37
29
  for (const validator of validators) {
38
30
  try {
39
31
  validator.validateConfiguration(configuration);
@@ -46,41 +38,59 @@ function getTypeByConfiguration(configuration) {
46
38
  throw new Error("'type' should be specified in ui5.yaml configuration: 'cf' or 'abap'");
47
39
  }
48
40
  }
49
- function getAdapter(configuration, annotationManager) {
41
+ async function getAdapter(configuration, annotationManager) {
50
42
  if (isPreview()) {
43
+ const { default: PreviewAdapter } = await import("./adapters/previewAdapter.js");
51
44
  return new PreviewAdapter(configuration);
52
45
  }
53
46
  switch (configuration.type) {
54
- case "abap":
47
+ case "abap": {
48
+ const { default: AbapAdapter } = await import("./adapters/abapAdapter.js");
55
49
  return new AbapAdapter(annotationManager);
56
- case "cf":
50
+ }
51
+ case "cf": {
52
+ const { default: CFAdapter } = await import("./adapters/cfAdapter.js");
57
53
  return new CFAdapter(configuration);
54
+ }
58
55
  default:
59
56
  throw new Error(`No adapter found for the given configuration type '${configuration.type}'`);
60
57
  }
61
58
  }
62
- function getRepository(configuration) {
59
+ async function getRepository(configuration, options) {
60
+ if (options.useCacheRepository) {
61
+ const { default: CacheRepository } = await import("./repositories/cacheRepository.js");
62
+ return new CacheRepository(configuration);
63
+ }
63
64
  if (isLocal()) {
65
+ const { default: LocalRepository } = await import("./repositories/localRepository.js");
64
66
  return new LocalRepository();
65
67
  }
66
68
  switch (configuration.type) {
67
- case "abap":
69
+ case "abap": {
70
+ const { default: AbapRepository } = await import("./repositories/abapRepository.js");
68
71
  return new AbapRepository(configuration);
69
- case "cf":
72
+ }
73
+ case "cf": {
74
+ const { default: HTML5Repository } = await import("./repositories/html5Repository.js");
70
75
  return new HTML5Repository(configuration);
76
+ }
71
77
  default:
72
78
  throw new Error(`No repository found for the given configuration type '${configuration.type}'`);
73
79
  }
74
80
  }
75
- function getAnnotationManager(configuration, repository) {
81
+ async function getAnnotationManager(configuration, repository) {
76
82
  if (isLocal()) {
77
83
  return new LocalAnnotationManager();
78
84
  }
79
85
  switch (configuration.type) {
80
- case "abap":
86
+ case "abap": {
87
+ const { default: AbapAnnotationManager } = await import("./annotations/abapAnnotationManager.js");
81
88
  return new AbapAnnotationManager(configuration, repository);
82
- case "cf":
89
+ }
90
+ case "cf": {
91
+ const { default: CFAnnotationManager } = await import("./annotations/cfAnnotationManager.js");
83
92
  return new CFAnnotationManager();
93
+ }
84
94
  default:
85
95
  throw new Error(`No annotation manager found for the given configuration type '${configuration.type}'`);
86
96
  }
@@ -129,5 +129,8 @@ export type ServiceCredentials = {
129
129
  } | string>;
130
130
  "sap.cloud.service"?: string;
131
131
  };
132
- export type FetchFilesPromise = Map<string, Promise<ReadonlyMap<string, string>>>;
132
+ export type FetchFilesPromise = Map<string, Promise<ReadonlyMap<string, Buffer>>>;
133
133
  export { IConfiguration };
134
+ export interface IInitializeOptions {
135
+ useCacheRepository?: boolean;
136
+ }
@@ -1,11 +1,11 @@
1
1
  import { createAbapServiceProvider } from "@sap-ux/system-access";
2
2
  import { getLogger } from "@ui5/logger";
3
- import AbapValidator from "../util/validator/abapValidator.js";
3
+ import AbapConnectionValidator from "../util/validator/abapConnectionValidator.js";
4
4
  const log = getLogger("@ui5/task-adaptation::AbapProvider");
5
5
  log.debug = (message) => log.verbose(message);
6
6
  export default class AbapProvider {
7
7
  provider = null;
8
- abapValidator = new AbapValidator();
8
+ abapValidator = new AbapConnectionValidator();
9
9
  async get({ target, destination }) {
10
10
  if (!this.provider) {
11
11
  const abapTarget = this.abapValidator.validateAndGetAbapTarget(target, destination);
@@ -11,6 +11,6 @@ export default class AbapRepository implements IRepository {
11
11
  getAnnotationMetadata(uri: string): Promise<IMetadata>;
12
12
  downloadAnnotationFile(uri: string): Promise<Map<string, any>>;
13
13
  getMetadata(id: string): Promise<IMetadata>;
14
- fetch(resource: IAbapResource): Promise<Map<string, string>>;
14
+ fetch(resource: IAbapResource): Promise<Map<string, Buffer>>;
15
15
  }
16
16
  export {};
@@ -0,0 +1,10 @@
1
+ import ICachedResource from "../cache/cachedResource.js";
2
+ import { IConfiguration } from "../model/types.js";
3
+ import IRepository from "./repository.js";
4
+ export default class CacheRepository implements IRepository {
5
+ private configuration;
6
+ constructor(configuration: IConfiguration);
7
+ getAppVariantIdHierarchy(_appId: string): Promise<ICachedResource[]>;
8
+ fetch(resource: ICachedResource): Promise<Map<string, Buffer>>;
9
+ downloadAnnotationFile(uri: string): Promise<Map<string, string>>;
10
+ }