@ui5/task-adaptation 1.6.5-rc.3 → 1.6.5-rc.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +5 -1
- package/dist/adapters/abapAdapter.d.ts +7 -5
- package/dist/adapters/abapAdapter.js +9 -11
- package/dist/adapters/adapter.d.ts +9 -5
- package/dist/adapters/adapter.js +20 -0
- package/dist/adapters/cfAdapter.d.ts +8 -5
- package/dist/adapters/cfAdapter.js +16 -10
- package/dist/adapters/commands/command.d.ts +26 -23
- package/dist/adapters/commands/command.js +73 -46
- package/dist/adapters/commands/fetchEndpointsCommand.d.ts +9 -0
- package/dist/adapters/commands/fetchEndpointsCommand.js +21 -0
- package/dist/adapters/commands/fetchPreviewResourcesCommand.d.ts +15 -0
- package/dist/adapters/commands/fetchPreviewResourcesCommand.js +103 -0
- package/dist/adapters/commands/filterFilesCommand.d.ts +4 -0
- package/dist/adapters/commands/filterFilesCommand.js +22 -0
- package/dist/adapters/commands/omitDeletedResourcesCommand.d.ts +10 -0
- package/dist/adapters/commands/omitDeletedResourcesCommand.js +31 -0
- package/dist/adapters/commands/processPreviewResourcesCommand.d.ts +9 -0
- package/dist/adapters/commands/processPreviewResourcesCommand.js +55 -0
- package/dist/adapters/commands/renameFilesCommand.d.ts +20 -0
- package/dist/{util/filesUtil.js → adapters/commands/renameFilesCommand.js} +34 -43
- package/dist/adapters/commands/writeResourcesCommand.d.ts +7 -0
- package/dist/adapters/commands/writeResourcesCommand.js +20 -0
- package/dist/adapters/commands/xsAppJsonEnhanceRoutesCommand.d.ts +3 -3
- package/dist/adapters/commands/xsAppJsonEnhanceRoutesCommand.js +4 -4
- package/dist/adapters/previewAdapter.d.ts +12 -6
- package/dist/adapters/previewAdapter.js +28 -7
- package/dist/annotations/abapAnnotationManager.js +1 -1
- package/dist/annotations/dataSource/dataSourceManager.d.ts +2 -1
- package/dist/annotations/dataSource/dataSourceManager.js +16 -7
- package/dist/{appVariantManager.d.ts → appVariant.d.ts} +2 -9
- package/dist/{appVariantManager.js → appVariant.js} +16 -28
- package/dist/{baseAppManager.js → baseApp.js} +6 -12
- package/dist/bundle.js +53 -12
- package/dist/cache/cacheHolder.d.ts +3 -3
- package/dist/index.d.ts +1 -1
- package/dist/index.js +44 -69
- package/dist/landscapeConfiguration.d.ts +2 -2
- package/dist/landscapeConfiguration.js +33 -31
- package/dist/model/configuration.d.ts +1 -0
- package/dist/model/configuration.js +1 -0
- package/dist/model/types.d.ts +9 -2
- package/dist/repositories/abapProvider.js +2 -2
- package/dist/repositories/abapRepository.d.ts +1 -1
- package/dist/repositories/html5Repository.d.ts +1 -1
- package/dist/repositories/localRepository.d.ts +3 -4
- package/dist/repositories/localRepository.js +50 -30
- package/dist/repositories/repository.d.ts +1 -1
- package/dist/util/cf/xsAppJsonUtil.d.ts +16 -3
- package/dist/util/cf/xsAppJsonUtil.js +7 -18
- package/dist/util/commonUtil.d.ts +6 -0
- package/dist/util/commonUtil.js +17 -0
- package/dist/util/fsUtil.d.ts +2 -3
- package/dist/util/fsUtil.js +12 -33
- package/dist/util/renamingUtil.js +50 -82
- package/dist/util/resourceUtil.d.ts +21 -5
- package/dist/util/resourceUtil.js +52 -11
- package/dist/util/validator/abapConnectionValidator.d.ts +5 -0
- package/dist/util/validator/abapConnectionValidator.js +22 -0
- package/dist/util/validator/abapValidator.d.ts +2 -4
- package/dist/util/validator/abapValidator.js +4 -16
- package/dist/util/validator/validator.d.ts +1 -0
- package/dist/util/validator/validator.js +11 -0
- package/dist/util/workspaceManager.d.ts +9 -0
- package/dist/util/workspaceManager.js +46 -0
- package/dist/util/zipUtil.d.ts +1 -1
- package/dist/util/zipUtil.js +1 -1
- package/package.json +11 -12
- package/.hyperspace/pull_request_bot.json +0 -19
- package/dist/previewManager.d.ts +0 -14
- package/dist/previewManager.js +0 -147
- package/dist/util/filesUtil.d.ts +0 -19
- package/scripts/metadataDownloadHelper.ts +0 -88
- package/scripts/publish.ts +0 -256
- package/scripts/test-integration-prep.sh +0 -4
- /package/dist/{baseAppManager.d.ts → baseApp.d.ts} +0 -0
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { getLogger } from "@ui5/logger";
|
|
2
|
-
import CFUtil from "../cfUtil.js";
|
|
3
2
|
const log = getLogger("@ui5/task-adaptation::XSAppJsonUtil");
|
|
4
3
|
export const XSAPP_JSON_FILENAME = "xs-app.json";
|
|
5
4
|
/**
|
|
@@ -63,7 +62,7 @@ export function enhanceRoutes(serviceCredentials, baseRoutes) {
|
|
|
63
62
|
return acc;
|
|
64
63
|
}, {});
|
|
65
64
|
return baseRoutes.map((route) => {
|
|
66
|
-
const endpointName = destinationToEndpoint[route.destination];
|
|
65
|
+
const endpointName = route.destination && destinationToEndpoint[route.destination];
|
|
67
66
|
if (endpointName) {
|
|
68
67
|
// There is a matching endpoint: remove destination and add endpoint/service
|
|
69
68
|
const { destination: _destination, ...rest } = route;
|
|
@@ -79,22 +78,12 @@ export function enhanceRoutes(serviceCredentials, baseRoutes) {
|
|
|
79
78
|
}
|
|
80
79
|
});
|
|
81
80
|
}
|
|
82
|
-
export async function fetchCredentialsAndEnhanceRoutes(xsAppJson,
|
|
83
|
-
|
|
84
|
-
|
|
81
|
+
export async function fetchCredentialsAndEnhanceRoutes(xsAppJson, serviceCredentialsPromise) {
|
|
82
|
+
const serviceCredentials = await serviceCredentialsPromise;
|
|
83
|
+
if (!serviceCredentials) {
|
|
84
|
+
log.verbose(`No service credentials available. Skipping xs-app.json update.`);
|
|
85
|
+
return xsAppJson;
|
|
85
86
|
}
|
|
86
|
-
|
|
87
|
-
try {
|
|
88
|
-
// Get valid service keys with proper endpoints structure
|
|
89
|
-
serviceCredentials = await CFUtil.getOrCreateServiceKeyWithEndpoints(serviceInstanceName, space);
|
|
90
|
-
}
|
|
91
|
-
catch (error) {
|
|
92
|
-
throw new Error(`Failed to get valid service keys for app '${appName}': ${error.message}`);
|
|
93
|
-
}
|
|
94
|
-
if (serviceCredentials) {
|
|
95
|
-
return enhanceRoutesWithEndpointAndService(xsAppJson, serviceCredentials);
|
|
96
|
-
}
|
|
97
|
-
log.info(`No endpoints found for app '${appName}'. xs-app.json will not be updated.`);
|
|
98
|
-
return xsAppJson;
|
|
87
|
+
return enhanceRoutesWithEndpointAndService(xsAppJson, serviceCredentials);
|
|
99
88
|
}
|
|
100
89
|
//# sourceMappingURL=xsAppJsonUtil.js.map
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { IConfiguration } from "../model/types.js";
|
|
2
2
|
import Language from "../model/language.js";
|
|
3
|
+
import AppVariant from "../appVariant.js";
|
|
3
4
|
export declare function dotToUnderscore(value: string): string;
|
|
4
5
|
export declare function validateConfiguration<T extends object>(options: T, properties: Array<keyof T>): void;
|
|
5
6
|
export declare function validateObject<T extends object>(options: T, properties: Array<keyof T>, message: string): void;
|
|
@@ -12,3 +13,8 @@ export declare function logBuilderVersion(): void;
|
|
|
12
13
|
export declare function logBetaUsage(): void;
|
|
13
14
|
export declare function getUniqueName(existingNames: string[], template: string): string;
|
|
14
15
|
export declare function isManifestChange(filename: string, content: string): boolean;
|
|
16
|
+
/**
|
|
17
|
+
* 4p. Reference map contains searchTerm as key and replacement as value. Base
|
|
18
|
+
* id and app variant ids are renamed to adaptation project id.
|
|
19
|
+
*/
|
|
20
|
+
export declare function getReferences(appVariants: AppVariant[], adaptationProjectId: string): Map<string, string>;
|
package/dist/util/commonUtil.js
CHANGED
|
@@ -98,4 +98,21 @@ export function isManifestChange(filename, content) {
|
|
|
98
98
|
}
|
|
99
99
|
return false;
|
|
100
100
|
}
|
|
101
|
+
/**
|
|
102
|
+
* 4p. Reference map contains searchTerm as key and replacement as value. Base
|
|
103
|
+
* id and app variant ids are renamed to adaptation project id.
|
|
104
|
+
*/
|
|
105
|
+
export function getReferences(appVariants, adaptationProjectId) {
|
|
106
|
+
const references = new Map();
|
|
107
|
+
appVariants.forEach((variant, i) => {
|
|
108
|
+
if (i === 0) {
|
|
109
|
+
references.set(variant.reference, adaptationProjectId);
|
|
110
|
+
}
|
|
111
|
+
if (variant.id !== adaptationProjectId) {
|
|
112
|
+
references.set(variant.id, adaptationProjectId);
|
|
113
|
+
}
|
|
114
|
+
variant.getRenamingForMovedFiles().forEach((value, key) => references.set(key, value));
|
|
115
|
+
});
|
|
116
|
+
return references;
|
|
117
|
+
}
|
|
101
118
|
//# sourceMappingURL=commonUtil.js.map
|
package/dist/util/fsUtil.d.ts
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
export default class FsUtil {
|
|
2
|
-
static readInProject(filepath: string): Promise<string>;
|
|
2
|
+
static readInProject(filepath: string, encoding?: BufferEncoding): Promise<Buffer | string>;
|
|
3
3
|
/**
|
|
4
4
|
* Check whether a file or directory exists (non-throwing).
|
|
5
5
|
* @param filePath Absolute or relative path
|
|
6
6
|
* @returns true if the path exists, false if not
|
|
7
7
|
*/
|
|
8
8
|
private static fileExists;
|
|
9
|
-
static readFilesRecursively(rootDirectory: string): Promise<Map<string,
|
|
10
|
-
static exists(filePath: string): Promise<boolean>;
|
|
9
|
+
static readFilesRecursively(rootDirectory: string): Promise<Map<string, Buffer>>;
|
|
11
10
|
}
|
package/dist/util/fsUtil.js
CHANGED
|
@@ -7,9 +7,9 @@ export default class FsUtil {
|
|
|
7
7
|
* @param filepath The relative file path from the project root (e.g. 'ui5AppInfo.json').
|
|
8
8
|
* @returns A promise that resolves to the file content as a string.
|
|
9
9
|
*/
|
|
10
|
-
static async readInProject(filepath) {
|
|
10
|
+
static async readInProject(filepath, encoding) {
|
|
11
11
|
try {
|
|
12
|
-
return await fs.readFile(path.join(process.cwd(), filepath),
|
|
12
|
+
return await fs.readFile(path.join(process.cwd(), filepath), encoding);
|
|
13
13
|
}
|
|
14
14
|
catch (error) {
|
|
15
15
|
const isProjectRoot = await FsUtil.fileExists(path.join(process.cwd(), "ui5.yaml"));
|
|
@@ -37,38 +37,17 @@ export default class FsUtil {
|
|
|
37
37
|
}
|
|
38
38
|
}
|
|
39
39
|
static async readFilesRecursively(rootDirectory) {
|
|
40
|
-
const
|
|
41
|
-
const fileReadTasks =
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
if (entry.isDirectory()) {
|
|
51
|
-
directoriesToScan.push(entryPath);
|
|
52
|
-
}
|
|
53
|
-
else if (entry.isFile()) {
|
|
54
|
-
fileReadTasks.push((async () => {
|
|
55
|
-
const relativeFilePath = path.relative(rootDirectory, entryPath);
|
|
56
|
-
const content = await fs.readFile(entryPath, "utf-8");
|
|
57
|
-
return [relativeFilePath, content];
|
|
58
|
-
})());
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
}
|
|
40
|
+
const entries = await fs.readdir(rootDirectory, { withFileTypes: true, recursive: true });
|
|
41
|
+
const fileReadTasks = entries
|
|
42
|
+
.filter(entry => entry.isFile())
|
|
43
|
+
.map(async (entry) => {
|
|
44
|
+
const parentPath = entry.parentPath ?? entry.path;
|
|
45
|
+
const entryPath = path.join(parentPath, entry.name);
|
|
46
|
+
const relativeFilePath = path.relative(rootDirectory, entryPath).replaceAll("\\", "/");
|
|
47
|
+
const content = await fs.readFile(entryPath);
|
|
48
|
+
return [relativeFilePath, content];
|
|
49
|
+
});
|
|
62
50
|
return new Map(await Promise.all(fileReadTasks));
|
|
63
51
|
}
|
|
64
|
-
static async exists(filePath) {
|
|
65
|
-
try {
|
|
66
|
-
await fs.access(filePath);
|
|
67
|
-
return true;
|
|
68
|
-
}
|
|
69
|
-
catch {
|
|
70
|
-
return false;
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
52
|
}
|
|
74
53
|
//# sourceMappingURL=fsUtil.js.map
|
|
@@ -14,98 +14,66 @@ export function renameMap(content, references, ignoreInStrings) {
|
|
|
14
14
|
if (ignoredReferenceKeys.length > 0) {
|
|
15
15
|
log.info(`Ignored renaming: ${ignoredReferenceKeys.join(", ")}`);
|
|
16
16
|
}
|
|
17
|
-
const searchTerms = [...references.keys()];
|
|
17
|
+
const searchTerms = [...references.keys()].filter(key => key.length > 0);
|
|
18
18
|
if (!content || !searchTerms || searchTerms.length === 0) {
|
|
19
19
|
return content;
|
|
20
20
|
}
|
|
21
|
-
const dotToSlash = (str) => str.replaceAll(".", "
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
return findAllOccurrences(content, string, start).map(i => ({ start: i, end: i + string.length }));
|
|
30
|
-
}).filter(arr => arr.length > 0) || [];
|
|
31
|
-
// We find the next search index with dots and slashes. Then we replace
|
|
32
|
-
// the nearest one and start search again in the next loop step.
|
|
33
|
-
const indices = new Array();
|
|
34
|
-
for (const searchTerm of searchTerms) {
|
|
35
|
-
const searchTermSlash = dotToSlash(searchTerm);
|
|
36
|
-
indices.push({
|
|
37
|
-
i: content.indexOf(searchTerm, start),
|
|
38
|
-
replacement: references.get(searchTerm),
|
|
39
|
-
searchTerm
|
|
40
|
-
}, {
|
|
41
|
-
i: content.indexOf(searchTermSlash, start),
|
|
42
|
-
replacement: dotToSlash(references.get(searchTerm)),
|
|
43
|
-
searchTerm: searchTermSlash
|
|
44
|
-
});
|
|
21
|
+
const dotToSlash = (str) => str.replaceAll(".", "/");
|
|
22
|
+
const escapeRegex = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
23
|
+
// Build replacement lookup: pattern → replacement string (both dot and slash variants).
|
|
24
|
+
// Empty keys are skipped to avoid infinite zero-width regex matches.
|
|
25
|
+
const replacementMap = new Map();
|
|
26
|
+
for (const term of searchTerms) {
|
|
27
|
+
if (!term) {
|
|
28
|
+
continue;
|
|
45
29
|
}
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
|
|
30
|
+
const repl = references.get(term);
|
|
31
|
+
replacementMap.set(term, repl);
|
|
32
|
+
const slashTerm = dotToSlash(term);
|
|
33
|
+
if (slashTerm !== term) {
|
|
34
|
+
replacementMap.set(slashTerm, dotToSlash(repl));
|
|
49
35
|
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
}
|
|
58
|
-
};
|
|
59
|
-
// If we found two strings with the same index, we take the longest one
|
|
60
|
-
// to replace, e.g.: app.variant and app.variant2 has the same index,
|
|
61
|
-
// but we don't want to replace the first one with customer.app.variant,
|
|
62
|
-
// otherwise we get customer.app.variant2, which we don't need. We need
|
|
63
|
-
// to replace the whole app.variant2 with customer.app.variant.
|
|
64
|
-
const findCurrentReplace = (found) => {
|
|
65
|
-
const result = new Map();
|
|
66
|
-
for (const entry of found) {
|
|
67
|
-
const existing = result.get(entry.i);
|
|
68
|
-
if (!existing || entry.searchTerm.length >= existing.searchTerm.length) {
|
|
69
|
-
result.set(entry.i, entry);
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
return [...result.values()].sort((a, b) => a.i - b.i)[0];
|
|
73
|
-
};
|
|
74
|
-
// Ignore if search is in i18n key: replace "id" in "{{id.key}}" with
|
|
75
|
-
// "customer.id" and we need only the next one in string
|
|
76
|
-
found.forEach(index => index.inBetween = inBetween(ignoredStrings, index.i));
|
|
77
|
-
// There might be a situation when we found something in ignored
|
|
78
|
-
// substrings and the index could be the nearest, but after that the
|
|
79
|
-
// next index might be the actual find to replace, so we first of all
|
|
80
|
-
// ignore all the ignored substring findings and get the next actual
|
|
81
|
-
// replacement. But if there are only ignored substrings found, we take
|
|
82
|
-
// the first one just to skip it and go further.
|
|
83
|
-
const currentReplace = findCurrentReplace(found);
|
|
84
|
-
if (currentReplace.inBetween) {
|
|
85
|
-
start = currentReplace.inBetween.end;
|
|
36
|
+
}
|
|
37
|
+
// Build deduplicated ignore set with slash variants
|
|
38
|
+
const ignoreSet = new Set();
|
|
39
|
+
for (const ignoredString of ignoreInStrings) {
|
|
40
|
+
if (ignoredString) {
|
|
41
|
+
ignoreSet.add(ignoredString);
|
|
42
|
+
ignoreSet.add(dotToSlash(ignoredString));
|
|
86
43
|
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
44
|
+
}
|
|
45
|
+
// When a string appears in both replacementMap and ignoreSet, ignoreSet
|
|
46
|
+
// takes precedence — the match is skipped rather than replaced. Remove such
|
|
47
|
+
// keys from replacementMap so the intent is explicit instead of relying on
|
|
48
|
+
// runtime check order.
|
|
49
|
+
for (const ignored of ignoreSet) {
|
|
50
|
+
replacementMap.delete(ignored);
|
|
51
|
+
}
|
|
52
|
+
// Collect all unique patterns sorted by length descending (longest match first).
|
|
53
|
+
// V8 compiles alternation of literal strings into an Aho-Corasick automaton
|
|
54
|
+
// internally, giving us O(n + P + z) single-pass matching in native C++ code.
|
|
55
|
+
const allPatterns = new Set([...replacementMap.keys(), ...ignoreSet]);
|
|
56
|
+
const sorted = [...allPatterns].sort((a, b) => b.length - a.length);
|
|
57
|
+
const regex = new RegExp(sorted.map(escapeRegex).join("|"), "g");
|
|
58
|
+
// Single-pass: find all matches, skip ignored ones, batch-replace the rest
|
|
59
|
+
const parts = [];
|
|
60
|
+
let lastEnd = 0;
|
|
61
|
+
let m;
|
|
62
|
+
while ((m = regex.exec(content)) !== null) {
|
|
63
|
+
const matched = m[0];
|
|
64
|
+
if (ignoreSet.has(matched))
|
|
65
|
+
continue;
|
|
66
|
+
const replacement = replacementMap.get(matched);
|
|
67
|
+
if (replacement !== undefined) {
|
|
68
|
+
parts.push(content.substring(lastEnd, m.index));
|
|
69
|
+
parts.push(replacement);
|
|
70
|
+
lastEnd = m.index + matched.length;
|
|
92
71
|
}
|
|
93
72
|
}
|
|
73
|
+
parts.push(content.substring(lastEnd));
|
|
74
|
+
return parts.join("");
|
|
94
75
|
}
|
|
95
76
|
export function renameResources(files, search, replacement) {
|
|
96
77
|
return new Map([...files].map(([filepath, content]) => [filepath, rename(content, search, replacement)]));
|
|
97
78
|
}
|
|
98
|
-
function findAllOccurrences(string, substring, start) {
|
|
99
|
-
if (!substring) {
|
|
100
|
-
return [];
|
|
101
|
-
}
|
|
102
|
-
const indices = [];
|
|
103
|
-
let index = start;
|
|
104
|
-
while ((index = string.indexOf(substring, index)) !== -1) {
|
|
105
|
-
indices.push(index);
|
|
106
|
-
index += substring.length; // shift from current finding
|
|
107
|
-
}
|
|
108
|
-
return indices;
|
|
109
|
-
}
|
|
110
|
-
;
|
|
111
79
|
//# sourceMappingURL=renamingUtil.js.map
|
|
@@ -1,14 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extensions of files whose contents are processed by the adaptation
|
|
3
|
+
* pipeline as text. Anything outside this whitelist is treated as binary
|
|
4
|
+
* (e.g. images, fonts) and is saved to the workspace verbatim by WorkspaceManager.
|
|
5
|
+
*
|
|
6
|
+
* Kept in sync with EXTENSIONS_TO_PROCESS in appVariant.ts.
|
|
7
|
+
*/
|
|
8
|
+
export declare const TEXT_EXTENSIONS: ReadonlySet<string>;
|
|
1
9
|
export default class ResourceUtil {
|
|
2
10
|
static getRootFolder(projectNamespace?: string): string;
|
|
3
11
|
static relativeToRoot(resourcePath: string, projectNamespace?: string): string;
|
|
4
12
|
static getResourcePath(projectNamespace?: string, ...paths: string[]): string;
|
|
5
|
-
static write(dir: string, files: Map<string,
|
|
6
|
-
static writeInProject(dir: string, files: Map<string,
|
|
7
|
-
static byGlob(dir: string, pattern: string, excludes?: string[]): Promise<Map<string,
|
|
8
|
-
static byGlobInProject(pattern: string, excludes?: string[]): Promise<Map<string,
|
|
13
|
+
static write(dir: string, files: Map<string, Buffer>): Promise<void[]>;
|
|
14
|
+
static writeInProject(dir: string, files: Map<string, Buffer>): Promise<void[]>;
|
|
15
|
+
static byGlob(dir: string, pattern: string, excludes?: string[]): Promise<Map<string, Buffer>>;
|
|
16
|
+
static byGlobInProject(pattern: string, excludes?: string[]): Promise<Map<string, Buffer>>;
|
|
9
17
|
static getString(resource: any): Promise<string>;
|
|
10
18
|
static getJson(resource: any): Promise<any>;
|
|
11
19
|
static setString(resource: any, str: string): void;
|
|
12
|
-
static createResource(filename: string, projectNamespace: string, content: string): any;
|
|
20
|
+
static createResource(filename: string, projectNamespace: string, content: string | Buffer): any;
|
|
21
|
+
private static createResourceFromContent;
|
|
13
22
|
static toFileMap(resources: ReadonlyArray<Resource>, projectNamespace?: string): Promise<Map<string, string>>;
|
|
23
|
+
/**
|
|
24
|
+
* Decodes the text-extension files of a buffer map to UTF-8 strings.
|
|
25
|
+
* Binary files (e.g. images, fonts) are dropped — they are expected to
|
|
26
|
+
* have been written to the workspace separately and are not relevant
|
|
27
|
+
* to the in-memory text pipeline.
|
|
28
|
+
*/
|
|
29
|
+
static toTextMap(buffers: ReadonlyMap<string, Buffer>): ReadonlyMap<string, string>;
|
|
14
30
|
}
|
|
@@ -1,6 +1,30 @@
|
|
|
1
1
|
import * as resourceFactory from "@ui5/fs/resourceFactory";
|
|
2
2
|
import { posix as path } from "path";
|
|
3
3
|
const UTF8 = "utf8";
|
|
4
|
+
/**
|
|
5
|
+
* Extensions of files whose contents are processed by the adaptation
|
|
6
|
+
* pipeline as text. Anything outside this whitelist is treated as binary
|
|
7
|
+
* (e.g. images, fonts) and is saved to the workspace verbatim by WorkspaceManager.
|
|
8
|
+
*
|
|
9
|
+
* Kept in sync with EXTENSIONS_TO_PROCESS in appVariant.ts.
|
|
10
|
+
*/
|
|
11
|
+
export const TEXT_EXTENSIONS = new Set([
|
|
12
|
+
"js",
|
|
13
|
+
"json",
|
|
14
|
+
"xml",
|
|
15
|
+
"html",
|
|
16
|
+
"properties",
|
|
17
|
+
"change",
|
|
18
|
+
"appdescr_variant",
|
|
19
|
+
"ctrl_variant",
|
|
20
|
+
"ctrl_variant_change",
|
|
21
|
+
"ctrl_variant_management_change",
|
|
22
|
+
"variant",
|
|
23
|
+
"fioriversion",
|
|
24
|
+
"codeChange",
|
|
25
|
+
"xmlViewChange",
|
|
26
|
+
"context",
|
|
27
|
+
]);
|
|
4
28
|
export default class ResourceUtil {
|
|
5
29
|
static getRootFolder(projectNamespace) {
|
|
6
30
|
const newPath = ["/resources"];
|
|
@@ -21,7 +45,7 @@ export default class ResourceUtil {
|
|
|
21
45
|
* NOTE: to write in project folder (folder where 'webapp',
|
|
22
46
|
* 'package.json', 'ui5.yaml' located), use writeInProject method.
|
|
23
47
|
* @param dir The directory to write files to.
|
|
24
|
-
* @param files A Map of file paths and their contents.
|
|
48
|
+
* @param files A Map of file paths and their contents as Buffer.
|
|
25
49
|
* @returns A promise that resolves when all files have been written.
|
|
26
50
|
*/
|
|
27
51
|
static write(dir, files) {
|
|
@@ -30,8 +54,8 @@ export default class ResourceUtil {
|
|
|
30
54
|
virBasePath: "/"
|
|
31
55
|
});
|
|
32
56
|
const promises = [];
|
|
33
|
-
files.forEach((
|
|
34
|
-
const resource =
|
|
57
|
+
files.forEach((content, filename) => {
|
|
58
|
+
const resource = ResourceUtil.createResourceFromContent("/" + filename, content);
|
|
35
59
|
promises.push(fsTarget.write(resource));
|
|
36
60
|
});
|
|
37
61
|
return Promise.all(promises);
|
|
@@ -40,7 +64,7 @@ export default class ResourceUtil {
|
|
|
40
64
|
* Write files to the project folder (folder where 'webapp',
|
|
41
65
|
* 'package.json', 'ui5.yaml' located), use writeInProject method.
|
|
42
66
|
* @param dir The directory to write files to.
|
|
43
|
-
* @param files A Map of file paths and their contents.
|
|
67
|
+
* @param files A Map of file paths and their contents as Buffer.
|
|
44
68
|
* @returns A promise that resolves when all files have been written.
|
|
45
69
|
*/
|
|
46
70
|
static writeInProject(dir, files) {
|
|
@@ -53,7 +77,7 @@ export default class ResourceUtil {
|
|
|
53
77
|
* @param dir The directory to search in.
|
|
54
78
|
* @param pattern The glob pattern to match files, e.g., "/ui5AppInfo.json".
|
|
55
79
|
* @param excludes An array of glob patterns to exclude from the search.
|
|
56
|
-
* @returns A promise that resolves to a Map of file paths and their contents.
|
|
80
|
+
* @returns A promise that resolves to a Map of file paths and their contents as Buffer.
|
|
57
81
|
*/
|
|
58
82
|
static byGlob(dir, pattern, excludes = []) {
|
|
59
83
|
const adapter = resourceFactory.createAdapter({
|
|
@@ -68,7 +92,7 @@ export default class ResourceUtil {
|
|
|
68
92
|
* 'package.json', 'ui5.yaml' located), excluding specified glob patterns.
|
|
69
93
|
* @param pattern The glob pattern to match files, e.g., "/ui5AppInfo.json".
|
|
70
94
|
* @param excludes An array of glob patterns to exclude from the search.
|
|
71
|
-
* @returns A promise that resolves to a Map of file paths and their contents.
|
|
95
|
+
* @returns A promise that resolves to a Map of file paths and their contents as Buffer.
|
|
72
96
|
*/
|
|
73
97
|
static byGlobInProject(pattern, excludes = []) {
|
|
74
98
|
return ResourceUtil.byGlob("./", pattern, [
|
|
@@ -78,7 +102,7 @@ export default class ResourceUtil {
|
|
|
78
102
|
].concat(excludes));
|
|
79
103
|
}
|
|
80
104
|
static getString(resource) {
|
|
81
|
-
return resource.getBuffer().then((buffer) => buffer.toString(UTF8));
|
|
105
|
+
return resource.getBuffer().then((buffer) => buffer.toString(UTF8).replaceAll("\r\n", "\n"));
|
|
82
106
|
}
|
|
83
107
|
static getJson(resource) {
|
|
84
108
|
return resource.getBuffer().then((buffer) => JSON.parse(buffer.toString(UTF8)));
|
|
@@ -87,10 +111,12 @@ export default class ResourceUtil {
|
|
|
87
111
|
resource.setBuffer(Buffer.from(str, UTF8));
|
|
88
112
|
}
|
|
89
113
|
static createResource(filename, projectNamespace, content) {
|
|
90
|
-
return
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
114
|
+
return ResourceUtil.createResourceFromContent(this.getResourcePath(projectNamespace, filename), content);
|
|
115
|
+
}
|
|
116
|
+
static createResourceFromContent(resourcePath, content) {
|
|
117
|
+
return resourceFactory.createResource(Buffer.isBuffer(content)
|
|
118
|
+
? { path: resourcePath, buffer: content }
|
|
119
|
+
: { path: resourcePath, string: content });
|
|
94
120
|
}
|
|
95
121
|
static async toFileMap(resources, projectNamespace) {
|
|
96
122
|
const files = new Map();
|
|
@@ -100,5 +126,20 @@ export default class ResourceUtil {
|
|
|
100
126
|
}
|
|
101
127
|
return files;
|
|
102
128
|
}
|
|
129
|
+
/**
|
|
130
|
+
* Decodes the text-extension files of a buffer map to UTF-8 strings.
|
|
131
|
+
* Binary files (e.g. images, fonts) are dropped — they are expected to
|
|
132
|
+
* have been written to the workspace separately and are not relevant
|
|
133
|
+
* to the in-memory text pipeline.
|
|
134
|
+
*/
|
|
135
|
+
static toTextMap(buffers) {
|
|
136
|
+
const text = new Map();
|
|
137
|
+
buffers.forEach((buffer, filename) => {
|
|
138
|
+
if (TEXT_EXTENSIONS.has(path.extname(filename).slice(1))) {
|
|
139
|
+
text.set(filename, buffer.toString(UTF8));
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
return text;
|
|
143
|
+
}
|
|
103
144
|
}
|
|
104
145
|
//# sourceMappingURL=resourceUtil.js.map
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { AbapTarget } from "@sap-ux/system-access";
|
|
2
|
+
import { IAbapTargetMeta } from "../../model/configuration.js";
|
|
3
|
+
export default class AbapConnectionValidator {
|
|
4
|
+
validateAndGetAbapTarget(target: AbapTarget & IAbapTargetMeta | undefined, destination: string | undefined): AbapTarget & IAbapTargetMeta;
|
|
5
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { validateObject } from "../commonUtil.js";
|
|
2
|
+
import { isAppStudio } from "@sap-ux/btp-utils";
|
|
3
|
+
import { getLogger } from "@ui5/logger";
|
|
4
|
+
const log = getLogger("@ui5/task-adaptation::AbapConnectionValidator");
|
|
5
|
+
export default class AbapConnectionValidator {
|
|
6
|
+
validateAndGetAbapTarget(target, destination) {
|
|
7
|
+
if (target) {
|
|
8
|
+
// if target configuration appears, we validate that it should be
|
|
9
|
+
// either target/destination for BAS or target/url for IDE we're
|
|
10
|
+
// trying to detect is it destination for BAS or url for IDE
|
|
11
|
+
const abapTargetProperties = isAppStudio() ? ["destination"] : ["url"];
|
|
12
|
+
validateObject(target, abapTargetProperties, "should be specified in ui5.yaml configuration/target");
|
|
13
|
+
return target;
|
|
14
|
+
}
|
|
15
|
+
else if (destination) {
|
|
16
|
+
log.warn("Destination is deprecated, use target/destination configuration instead");
|
|
17
|
+
return { destination };
|
|
18
|
+
}
|
|
19
|
+
throw new Error("Target should be specified in ui5.yaml configuration to connect the ABAP system");
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=abapConnectionValidator.js.map
|
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { IAbapTargetMeta, IConfiguration, LandscapeType } from "../../model/configuration.js";
|
|
1
|
+
import { IConfiguration, LandscapeType } from "../../model/configuration.js";
|
|
3
2
|
import IValidator from "./validator.js";
|
|
4
3
|
export default class AbapValidator implements IValidator {
|
|
5
4
|
type: LandscapeType;
|
|
6
|
-
|
|
7
|
-
validateConfiguration({ target, destination }: IConfiguration): void;
|
|
5
|
+
validateConfiguration({ destination, target }: IConfiguration): void;
|
|
8
6
|
}
|
|
@@ -1,26 +1,14 @@
|
|
|
1
|
-
import { validateObject } from "../commonUtil.js";
|
|
2
|
-
import BtpUtils from "@sap-ux/btp-utils";
|
|
3
1
|
import { getLogger } from "@ui5/logger";
|
|
4
2
|
const log = getLogger("@ui5/task-adaptation::AbapValidator");
|
|
5
3
|
export default class AbapValidator {
|
|
6
4
|
type = "abap";
|
|
7
|
-
|
|
8
|
-
if (target) {
|
|
9
|
-
|
|
10
|
-
// either target/destination for BAS or target/url for IDE we're
|
|
11
|
-
// trying to detect is it destination for BAS or url for IDE
|
|
12
|
-
const abapTargetProperties = BtpUtils.isAppStudio() ? ["destination"] : ["url"];
|
|
13
|
-
validateObject(target, abapTargetProperties, "should be specified in ui5.yaml configuration/target");
|
|
14
|
-
return target;
|
|
5
|
+
validateConfiguration({ destination, target }) {
|
|
6
|
+
if (!destination && !target) {
|
|
7
|
+
throw new Error("'target' should be specified in ui5.yaml configuration");
|
|
15
8
|
}
|
|
16
|
-
else if (destination) {
|
|
9
|
+
else if (destination && !target) {
|
|
17
10
|
log.warn("Destination is deprecated, use target/destination configuration instead");
|
|
18
|
-
return { destination };
|
|
19
11
|
}
|
|
20
|
-
throw new Error("Target should be specified in ui5.yaml configuration to connect the ABAP system");
|
|
21
|
-
}
|
|
22
|
-
validateConfiguration({ target, destination }) {
|
|
23
|
-
this.validateAndGetAbapTarget(target, destination);
|
|
24
12
|
}
|
|
25
13
|
}
|
|
26
14
|
//# sourceMappingURL=abapValidator.js.map
|
|
@@ -1,4 +1,15 @@
|
|
|
1
1
|
export function isOneOf(values, value) {
|
|
2
2
|
return values.includes(value);
|
|
3
3
|
}
|
|
4
|
+
export function validateAppId(id) {
|
|
5
|
+
if (!id) {
|
|
6
|
+
throw new Error(`The application id must not be empty.`);
|
|
7
|
+
}
|
|
8
|
+
// https://help.sap.com/docs/bas/developing-sap-fiori-app-in-sap-business-application-studio/releasing-sap-fiori-application-to-be-extensible-in-adaptation-projects-on-sap-s-4hana-cloud
|
|
9
|
+
// In the manifest.json file, make sure that the attribute
|
|
10
|
+
// sap.app/id has at least 2 segments.
|
|
11
|
+
if (id.split(".").filter(Boolean).length < 2) {
|
|
12
|
+
throw new Error(`The application id '${id}' must have at least two parts, separated by a period.`);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
4
15
|
//# sourceMappingURL=validator.js.map
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export default class WorkspaceManager {
|
|
2
|
+
private readonly workspace;
|
|
3
|
+
private readonly projectNamespace;
|
|
4
|
+
private snapshot;
|
|
5
|
+
constructor(workspace: IWorkspace, projectNamespace: string);
|
|
6
|
+
createSnapshot(): Promise<void>;
|
|
7
|
+
restoreSnapshot(): Promise<void>;
|
|
8
|
+
saveAndConvert(buffersPromise: Promise<ReadonlyMap<string, Buffer>>): Promise<ReadonlyMap<string, string>>;
|
|
9
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import ResourceUtil, { TEXT_EXTENSIONS } from "./resourceUtil.js";
|
|
2
|
+
import { posix as path } from "path";
|
|
3
|
+
const UTF8 = "utf8";
|
|
4
|
+
export default class WorkspaceManager {
|
|
5
|
+
workspace;
|
|
6
|
+
projectNamespace;
|
|
7
|
+
snapshot;
|
|
8
|
+
constructor(workspace, projectNamespace) {
|
|
9
|
+
this.workspace = workspace;
|
|
10
|
+
this.projectNamespace = projectNamespace;
|
|
11
|
+
}
|
|
12
|
+
async createSnapshot() {
|
|
13
|
+
const resources = await this.workspace.byGlob("/**/*");
|
|
14
|
+
const rootFolderLength = ResourceUtil.getRootFolder(this.projectNamespace).length;
|
|
15
|
+
const entries = await Promise.all(resources.map(async (resource) => {
|
|
16
|
+
const filename = resource.getPath().substring(rootFolderLength + 1);
|
|
17
|
+
return [filename, await resource.getBuffer()];
|
|
18
|
+
}));
|
|
19
|
+
this.snapshot = new Map(entries);
|
|
20
|
+
}
|
|
21
|
+
async restoreSnapshot() {
|
|
22
|
+
if (!this.snapshot) {
|
|
23
|
+
throw new Error("No snapshot to restore — call createSnapshot() first");
|
|
24
|
+
}
|
|
25
|
+
const writes = [];
|
|
26
|
+
this.snapshot.forEach((buffer, filename) => {
|
|
27
|
+
const resource = ResourceUtil.createResource(filename, this.projectNamespace, buffer);
|
|
28
|
+
writes.push(this.workspace.write(resource));
|
|
29
|
+
});
|
|
30
|
+
await Promise.all(writes);
|
|
31
|
+
}
|
|
32
|
+
async saveAndConvert(buffersPromise) {
|
|
33
|
+
const buffers = await buffersPromise;
|
|
34
|
+
const writes = [];
|
|
35
|
+
const text = new Map();
|
|
36
|
+
buffers.forEach((buffer, filename) => {
|
|
37
|
+
if (TEXT_EXTENSIONS.has(path.extname(filename).slice(1))) {
|
|
38
|
+
text.set(filename, buffer.toString(UTF8));
|
|
39
|
+
}
|
|
40
|
+
writes.push(this.workspace.write(ResourceUtil.createResource(filename, this.projectNamespace, buffer)));
|
|
41
|
+
});
|
|
42
|
+
await Promise.all(writes);
|
|
43
|
+
return text;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=workspaceManager.js.map
|
package/dist/util/zipUtil.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare function unzipZipEntries(zip: Buffer): Promise<Map<string,
|
|
1
|
+
export declare function unzipZipEntries(zip: Buffer): Promise<Map<string, Buffer>>;
|
package/dist/util/zipUtil.js
CHANGED
|
@@ -11,6 +11,6 @@ export async function unzipZipEntries(zip) {
|
|
|
11
11
|
}
|
|
12
12
|
}
|
|
13
13
|
function mapEntries(entries) {
|
|
14
|
-
return new Map(entries.filter(entry => !entry.isDirectory).map(entry => [entry.entryName, entry.getData()
|
|
14
|
+
return new Map(entries.filter(entry => !entry.isDirectory).map(entry => [entry.entryName, entry.getData()]));
|
|
15
15
|
}
|
|
16
16
|
//# sourceMappingURL=zipUtil.js.map
|