@sap-ux/fe-fpm-writer 0.42.19 → 0.42.20
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/dist/building-block/index.js +12 -6
- package/dist/building-block/processor.js +9 -2
- package/dist/building-block/prompts/utils/questions.js +3 -6
- package/dist/building-block/types.d.ts +8 -0
- package/dist/column/index.js +7 -7
- package/dist/common/defaults.d.ts +5 -2
- package/dist/common/defaults.js +13 -7
- package/dist/common/file.d.ts +81 -1
- package/dist/common/file.js +167 -2
- package/dist/field/index.js +7 -7
- package/dist/filter/index.js +2 -1
- package/dist/page/custom.js +11 -9
- package/dist/prompts/api.d.ts +1 -1
- package/dist/prompts/api.js +4 -5
- package/dist/section/index.js +13 -14
- package/dist/view/index.js +8 -8
- package/package.json +2 -2
- package/templates/building-block/rich-text-editor-button-groups/View.xml +2 -4
- package/templates/common/FragmentWithForm.xml +1 -1
- package/templates/common/FragmentWithVBox.xml +1 -1
- package/templates/filter/fragment.xml +5 -5
- package/templates/page/custom/1.84/ext/View.xml +1 -1
- package/templates/page/custom/1.94/ext/View.xml +1 -1
- package/templates/view/ext/CustomViewWithTable.xml +1 -1
|
@@ -73,10 +73,9 @@ const PLACEHOLDERS = {
|
|
|
73
73
|
async function generateBuildingBlock(basePath, config, fs) {
|
|
74
74
|
const { viewOrFragmentPath, aggregationPath, buildingBlockData, allowAutoAddDependencyLib = true } = config;
|
|
75
75
|
// Validate the base and view paths
|
|
76
|
-
|
|
77
|
-
fs = (0, mem_fs_editor_1.create)((0, mem_fs_1.create)());
|
|
78
|
-
}
|
|
76
|
+
fs ??= (0, mem_fs_editor_1.create)((0, mem_fs_1.create)());
|
|
79
77
|
await (0, validate_1.validateBasePath)(basePath, fs, []);
|
|
78
|
+
const fnGenerateId = config.buildingBlockData.generateId ?? (await (0, file_1.createIdGenerator)(basePath, fs));
|
|
80
79
|
if (!fs.exists((0, node_path_1.join)(basePath, viewOrFragmentPath))) {
|
|
81
80
|
throw new Error(`Invalid view path ${viewOrFragmentPath}.`);
|
|
82
81
|
}
|
|
@@ -88,7 +87,7 @@ async function generateBuildingBlock(basePath, config, fs) {
|
|
|
88
87
|
hasAggregation,
|
|
89
88
|
aggregationNamespace
|
|
90
89
|
};
|
|
91
|
-
const templateDocument = getTemplateDocument(processedBuildingBlockData, xmlDocument, fs, manifest, templateConfig);
|
|
90
|
+
const templateDocument = getTemplateDocument({ ...processedBuildingBlockData, generateId: fnGenerateId }, xmlDocument, fs, manifest, templateConfig);
|
|
92
91
|
if (buildingBlockData.buildingBlockType === types_1.BuildingBlockType.RichTextEditor ||
|
|
93
92
|
buildingBlockData.buildingBlockType === types_1.BuildingBlockType.RichTextEditorButtonGroups) {
|
|
94
93
|
const minUI5Version = manifest ? (0, semver_1.coerce)((0, project_access_1.getMinimumUI5Version)(manifest)) : undefined;
|
|
@@ -235,11 +234,18 @@ function getTemplateContent(buildingBlockData, viewDocument, manifest, fs, usePl
|
|
|
235
234
|
if (!buildingBlockData.id) {
|
|
236
235
|
buildingBlockData.id = PLACEHOLDERS.id;
|
|
237
236
|
}
|
|
238
|
-
|
|
237
|
+
const configKey = (0, file_1.getRelativeTemplateComponentPath)(templateFilePath);
|
|
238
|
+
const config = file_1.CONFIG[configKey];
|
|
239
|
+
let context = {
|
|
239
240
|
macrosNamespace: viewDocument ? (0, xml_1.getOrAddNamespace)(viewDocument, 'sap.fe.macros', 'macros') : 'macros',
|
|
240
241
|
data: buildingBlockData,
|
|
241
242
|
config: templateConfig
|
|
242
|
-
}
|
|
243
|
+
};
|
|
244
|
+
if (config?.getData) {
|
|
245
|
+
const additionalContext = config.getData(buildingBlockData.generateId, buildingBlockData);
|
|
246
|
+
context = { ...context, ...additionalContext };
|
|
247
|
+
}
|
|
248
|
+
return (0, ejs_1.render)(fs.read(templateFilePath), context, {});
|
|
243
249
|
}
|
|
244
250
|
/**
|
|
245
251
|
* Method returns the manifest content for the required dependency library.
|
|
@@ -42,6 +42,7 @@ const templates_1 = require("../templates");
|
|
|
42
42
|
const event_handler_1 = require("../common/event-handler");
|
|
43
43
|
const defaults_1 = require("../common/defaults");
|
|
44
44
|
const xml_1 = require("./prompts/utils/xml");
|
|
45
|
+
const file_1 = require("../common/file");
|
|
45
46
|
/**
|
|
46
47
|
* Button group configurations used for validation and providing available button groups.
|
|
47
48
|
*/
|
|
@@ -124,7 +125,7 @@ function processCustomColumn(buildingBlockData, context) {
|
|
|
124
125
|
});
|
|
125
126
|
columnConfig.eventHandler = processedEventHandler;
|
|
126
127
|
}
|
|
127
|
-
columnConfig.content = (0, defaults_1.getDefaultFragmentContent)('Sample Text', processedEventHandler);
|
|
128
|
+
columnConfig.content = (0, defaults_1.getDefaultFragmentContent)('Sample Text', buildingBlockData.generateId, processedEventHandler);
|
|
128
129
|
if (viewPath && !fs.exists(viewPath)) {
|
|
129
130
|
fs.copyTpl((0, templates_1.getTemplatePath)(config.templateFile), viewPath, columnConfig);
|
|
130
131
|
}
|
|
@@ -153,7 +154,7 @@ function processCustomFilterField(buildingBlockData, context) {
|
|
|
153
154
|
throw new Error('EmbeddedFragment is required for CustomFilterField');
|
|
154
155
|
}
|
|
155
156
|
const config = getBuildingBlockConfig(types_1.BuildingBlockType.CustomFilterField);
|
|
156
|
-
|
|
157
|
+
let filterConfig = {
|
|
157
158
|
label: buildingBlockData.label,
|
|
158
159
|
property: buildingBlockData.property,
|
|
159
160
|
required: buildingBlockData.required ?? false,
|
|
@@ -171,6 +172,12 @@ function processCustomFilterField(buildingBlockData, context) {
|
|
|
171
172
|
templatePath: 'filter/Controller'
|
|
172
173
|
});
|
|
173
174
|
}
|
|
175
|
+
const configKey = config.templateFile;
|
|
176
|
+
const additionnalDataConfig = file_1.CONFIG[configKey];
|
|
177
|
+
if (additionnalDataConfig?.getData) {
|
|
178
|
+
const additionalContext = additionnalDataConfig.getData(buildingBlockData.generateId, buildingBlockData);
|
|
179
|
+
filterConfig = { ...filterConfig, ...additionalContext };
|
|
180
|
+
}
|
|
174
181
|
if (viewPath && !fs.exists(viewPath)) {
|
|
175
182
|
fs.copyTpl((0, templates_1.getTemplatePath)(config.templateFile), viewPath, filterConfig);
|
|
176
183
|
}
|
|
@@ -15,11 +15,11 @@ exports.getBuildingBlockIdPrompt = getBuildingBlockIdPrompt;
|
|
|
15
15
|
const node_path_1 = require("node:path");
|
|
16
16
|
const service_1 = require("./service");
|
|
17
17
|
const project_access_1 = require("@sap-ux/project-access");
|
|
18
|
-
const file_1 = require("@sap-ux/project-access/dist/file");
|
|
19
18
|
const types_1 = require("../../types");
|
|
20
19
|
const xml_1 = require("./xml");
|
|
21
20
|
const i18n_1 = require("../../../i18n");
|
|
22
21
|
const prompt_helpers_1 = require("./prompt-helpers");
|
|
22
|
+
const file_1 = require("../../../common/file");
|
|
23
23
|
/* eslint-disable @typescript-eslint/no-floating-promises */
|
|
24
24
|
(0, i18n_1.initI18n)();
|
|
25
25
|
const t = (0, i18n_1.translate)(i18n_1.i18nNamespaces.buildingBlock, 'prompts.common.');
|
|
@@ -101,11 +101,8 @@ function getViewOrFragmentPathPrompt(context, validationErrorMessage, properties
|
|
|
101
101
|
name: 'viewOrFragmentPath',
|
|
102
102
|
choices: project
|
|
103
103
|
? async () => {
|
|
104
|
-
const
|
|
105
|
-
|
|
106
|
-
return transformChoices(files
|
|
107
|
-
.filter((fileName) => lookupFiles.some((lookupFile) => fileName.endsWith(lookupFile)))
|
|
108
|
-
.map((file) => (0, node_path_1.relative)(appPath, file)));
|
|
104
|
+
const fragmentAndViewFiles = await (0, file_1.getFragmentAndViewFiles)(appPath, fs);
|
|
105
|
+
return transformChoices(fragmentAndViewFiles.map((file) => (0, node_path_1.relative)(appPath, file)));
|
|
109
106
|
}
|
|
110
107
|
: [],
|
|
111
108
|
validate: (value) => (!project || value ? true : validationErrorMessage),
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { IdGeneratorFunction } from '../common/file';
|
|
1
2
|
import type { CustomElement, CustomFragment, EventHandler, FragmentContentData, Position } from '../common/types';
|
|
2
3
|
/**
|
|
3
4
|
* Building block type.
|
|
@@ -63,6 +64,13 @@ export interface BuildingBlock {
|
|
|
63
64
|
* Defines the relative path of the property in the metamodel, based on the current contextPath.
|
|
64
65
|
*/
|
|
65
66
|
metaPath?: string | BuildingBlockMetaPath;
|
|
67
|
+
/**
|
|
68
|
+
* Generates a unique ID for the building block based on the provided base ID.
|
|
69
|
+
*
|
|
70
|
+
* @param baseId - The base ID to generate from, usually related to the building block type.
|
|
71
|
+
* @returns A unique ID string.
|
|
72
|
+
*/
|
|
73
|
+
generateId: IdGeneratorFunction;
|
|
66
74
|
}
|
|
67
75
|
/**
|
|
68
76
|
* Represents a Chart building block control.
|
package/dist/column/index.js
CHANGED
|
@@ -38,9 +38,10 @@ function getManifestRoot(ui5Version) {
|
|
|
38
38
|
* @param {CustomTableColumn} data - a custom column configuration object
|
|
39
39
|
* @param {string} manifestPath - path to the project's manifest.json
|
|
40
40
|
* @param {Manifest} manifest - the application manifest
|
|
41
|
+
* @param {(baseId: string) => string} generateId - Function to generate unique IDs for the building block elements.
|
|
41
42
|
* @returns enhanced configuration
|
|
42
43
|
*/
|
|
43
|
-
function enhanceConfig(fs, data, manifestPath, manifest) {
|
|
44
|
+
function enhanceConfig(fs, data, manifestPath, manifest, generateId) {
|
|
44
45
|
// clone input and set defaults
|
|
45
46
|
const config = { ...data };
|
|
46
47
|
(0, defaults_1.setCommonDefaults)(config, manifestPath, manifest);
|
|
@@ -55,7 +56,7 @@ function enhanceConfig(fs, data, manifestPath, manifest) {
|
|
|
55
56
|
const content = config.properties && config.properties.length > 0
|
|
56
57
|
? `{=%{${config.properties.join("} + ' ' + %{")}}}`
|
|
57
58
|
: 'Sample Text';
|
|
58
|
-
config.content = config.control || (0, defaults_1.getDefaultFragmentContent)(content, config.eventHandler);
|
|
59
|
+
config.content = config.control || (0, defaults_1.getDefaultFragmentContent)(content, generateId, config.eventHandler);
|
|
59
60
|
return config;
|
|
60
61
|
}
|
|
61
62
|
/**
|
|
@@ -68,17 +69,16 @@ function enhanceConfig(fs, data, manifestPath, manifest) {
|
|
|
68
69
|
*/
|
|
69
70
|
async function generateCustomColumn(basePath, customColumn, fs) {
|
|
70
71
|
(0, validate_1.validateVersion)(customColumn.minUI5Version);
|
|
71
|
-
|
|
72
|
-
fs = (0, mem_fs_editor_1.create)((0, mem_fs_1.create)());
|
|
73
|
-
}
|
|
72
|
+
fs ??= (0, mem_fs_editor_1.create)((0, mem_fs_1.create)());
|
|
74
73
|
await (0, validate_1.validateBasePath)(basePath, fs);
|
|
74
|
+
const fnGenerateId = await (0, file_1.createIdGenerator)(basePath, fs);
|
|
75
75
|
const { path: manifestPath, content: manifest } = await (0, utils_1.getManifest)(basePath, fs);
|
|
76
76
|
// merge with defaults
|
|
77
|
-
const completeColumn = enhanceConfig(fs, customColumn, manifestPath, manifest);
|
|
77
|
+
const completeColumn = enhanceConfig(fs, customColumn, manifestPath, manifest, fnGenerateId);
|
|
78
78
|
// add fragment
|
|
79
79
|
const viewPath = (0, node_path_1.join)(completeColumn.path, `${completeColumn.fragmentFile ?? completeColumn.name}.fragment.xml`);
|
|
80
80
|
if (completeColumn.control || !fs.exists(viewPath)) {
|
|
81
|
-
(0, file_1.copyTpl)(fs, (0, templates_1.getTemplatePath)('common/Fragment.xml'), viewPath, completeColumn);
|
|
81
|
+
(0, file_1.copyTpl)(fs, (0, templates_1.getTemplatePath)('common/Fragment.xml'), viewPath, completeColumn, fnGenerateId);
|
|
82
82
|
}
|
|
83
83
|
// enhance manifest with column definition
|
|
84
84
|
const manifestRoot = getManifestRoot(customColumn.minUI5Version);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { IdGeneratorFunction } from './file';
|
|
1
2
|
import type { CustomElement, FragmentContentData, InternalCustomElement, Manifest } from './types';
|
|
2
3
|
export declare const FCL_ROUTER = "sap.f.routing.Router";
|
|
3
4
|
/**
|
|
@@ -13,6 +14,7 @@ export declare function setCommonDefaults<T extends CustomElement & Partial<Inte
|
|
|
13
14
|
* Method to generate default content data for xml fragment.
|
|
14
15
|
*
|
|
15
16
|
* @param {string} text - text of button or label
|
|
17
|
+
* @param {(baseId: string) => string} generateId - Function to generate unique IDs for the building block elements.
|
|
16
18
|
* @param {string} [eventHandler] - event handler path
|
|
17
19
|
* if value is passed then "Button" control with 'press' event would be generated
|
|
18
20
|
* if value is not passed then "Text" control would be generated
|
|
@@ -21,11 +23,12 @@ export declare function setCommonDefaults<T extends CustomElement & Partial<Inte
|
|
|
21
23
|
* @param {boolean} includeRequireInContent - controls if `core:require` attribute should be included to fragment content
|
|
22
24
|
* @returns default content for fragment
|
|
23
25
|
*/
|
|
24
|
-
export declare function getDefaultFragmentContentData(text: string, eventHandler?: string, isController?: boolean, prefferInput?: boolean, includeRequireInContent?: boolean): FragmentContentData;
|
|
26
|
+
export declare function getDefaultFragmentContentData(text: string, generateId: IdGeneratorFunction, eventHandler?: string, isController?: boolean, prefferInput?: boolean, includeRequireInContent?: boolean): FragmentContentData;
|
|
25
27
|
/**
|
|
26
28
|
* Method to generate default content for xml fragment.
|
|
27
29
|
*
|
|
28
30
|
* @param {string} text - text of button or label
|
|
31
|
+
* @param {(baseId: string) => string} generateId - Function to generate unique IDs for the building block elements.
|
|
29
32
|
* @param {string} [eventHandler] - event handler path
|
|
30
33
|
* if value is passed then "Button" control with 'press' event would be generated
|
|
31
34
|
* if value is not passed then "Text" control would be generated
|
|
@@ -33,5 +36,5 @@ export declare function getDefaultFragmentContentData(text: string, eventHandler
|
|
|
33
36
|
* @param {boolean} prefferInput - controls if `input` element should be added to default fragment content
|
|
34
37
|
* @returns default content for fragment
|
|
35
38
|
*/
|
|
36
|
-
export declare function getDefaultFragmentContent(text: string, eventHandler?: string, isController?: boolean, prefferInput?: boolean): string;
|
|
39
|
+
export declare function getDefaultFragmentContent(text: string, generateId: IdGeneratorFunction, eventHandler?: string, isController?: boolean, prefferInput?: boolean): string;
|
|
37
40
|
//# sourceMappingURL=defaults.d.ts.map
|
package/dist/common/defaults.js
CHANGED
|
@@ -26,6 +26,7 @@ function setCommonDefaults(config, manifestPath, manifest) {
|
|
|
26
26
|
* Method to generate default content data for xml fragment.
|
|
27
27
|
*
|
|
28
28
|
* @param {string} text - text of button or label
|
|
29
|
+
* @param {(baseId: string) => string} generateId - Function to generate unique IDs for the building block elements.
|
|
29
30
|
* @param {string} [eventHandler] - event handler path
|
|
30
31
|
* if value is passed then "Button" control with 'press' event would be generated
|
|
31
32
|
* if value is not passed then "Text" control would be generated
|
|
@@ -34,7 +35,7 @@ function setCommonDefaults(config, manifestPath, manifest) {
|
|
|
34
35
|
* @param {boolean} includeRequireInContent - controls if `core:require` attribute should be included to fragment content
|
|
35
36
|
* @returns default content for fragment
|
|
36
37
|
*/
|
|
37
|
-
function getDefaultFragmentContentData(text, eventHandler, isController = false, prefferInput = false, includeRequireInContent = true) {
|
|
38
|
+
function getDefaultFragmentContentData(text, generateId, eventHandler, isController = false, prefferInput = false, includeRequireInContent = true) {
|
|
38
39
|
let content;
|
|
39
40
|
let requireAttribute;
|
|
40
41
|
if (eventHandler) {
|
|
@@ -49,19 +50,23 @@ function getDefaultFragmentContentData(text, eventHandler, isController = false,
|
|
|
49
50
|
if (prefferInput) {
|
|
50
51
|
attributes.push(`value="${text}"`);
|
|
51
52
|
attributes.push(`change="handler.${method}"`);
|
|
52
|
-
|
|
53
|
+
const id = generateId('Input');
|
|
54
|
+
content = `<Input id="${id}" ${attributes.join(' ')} />`;
|
|
53
55
|
}
|
|
54
56
|
else {
|
|
55
57
|
attributes.push(`text="${text}"`);
|
|
56
58
|
attributes.push(`press="handler.${method}"`);
|
|
57
|
-
|
|
59
|
+
const id = generateId('Button');
|
|
60
|
+
content = `<Button id="${id}" ${attributes.join(' ')} />`;
|
|
58
61
|
}
|
|
59
62
|
}
|
|
60
63
|
else if (prefferInput) {
|
|
61
|
-
|
|
64
|
+
const id = generateId('Input');
|
|
65
|
+
content = `<Input id="${id}" value="${text}" />`;
|
|
62
66
|
}
|
|
63
67
|
else {
|
|
64
|
-
|
|
68
|
+
const id = generateId('Text');
|
|
69
|
+
content = `<Text id="${id}" text="${text}" />`;
|
|
65
70
|
}
|
|
66
71
|
return {
|
|
67
72
|
content,
|
|
@@ -72,6 +77,7 @@ function getDefaultFragmentContentData(text, eventHandler, isController = false,
|
|
|
72
77
|
* Method to generate default content for xml fragment.
|
|
73
78
|
*
|
|
74
79
|
* @param {string} text - text of button or label
|
|
80
|
+
* @param {(baseId: string) => string} generateId - Function to generate unique IDs for the building block elements.
|
|
75
81
|
* @param {string} [eventHandler] - event handler path
|
|
76
82
|
* if value is passed then "Button" control with 'press' event would be generated
|
|
77
83
|
* if value is not passed then "Text" control would be generated
|
|
@@ -79,8 +85,8 @@ function getDefaultFragmentContentData(text, eventHandler, isController = false,
|
|
|
79
85
|
* @param {boolean} prefferInput - controls if `input` element should be added to default fragment content
|
|
80
86
|
* @returns default content for fragment
|
|
81
87
|
*/
|
|
82
|
-
function getDefaultFragmentContent(text, eventHandler, isController = false, prefferInput = false) {
|
|
83
|
-
const contentData = getDefaultFragmentContentData(text, eventHandler, isController, prefferInput);
|
|
88
|
+
function getDefaultFragmentContent(text, generateId, eventHandler, isController = false, prefferInput = false) {
|
|
89
|
+
const contentData = getDefaultFragmentContentData(text, generateId, eventHandler, isController, prefferInput);
|
|
84
90
|
return contentData.content;
|
|
85
91
|
}
|
|
86
92
|
//# sourceMappingURL=defaults.js.map
|
package/dist/common/file.d.ts
CHANGED
|
@@ -1,5 +1,76 @@
|
|
|
1
1
|
import type { CopyOptions, Editor } from 'mem-fs-editor';
|
|
2
2
|
import type { TabInfo } from '../common/types';
|
|
3
|
+
interface ButtonGroup {
|
|
4
|
+
name: string;
|
|
5
|
+
buttons: string[];
|
|
6
|
+
visible?: boolean;
|
|
7
|
+
priority?: number;
|
|
8
|
+
customToolbarPriority?: number;
|
|
9
|
+
row?: number;
|
|
10
|
+
id?: string;
|
|
11
|
+
}
|
|
12
|
+
export type IdGeneratorFunction = (baseId: string, validatedIds?: string[]) => string;
|
|
13
|
+
interface TemplateContext {
|
|
14
|
+
buttonGroups?: ButtonGroup[];
|
|
15
|
+
name?: string;
|
|
16
|
+
data?: {
|
|
17
|
+
buttonGroups?: ButtonGroup[];
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
export declare const CONFIG: {
|
|
21
|
+
"page/custom/1.94/ext/View.xml": {
|
|
22
|
+
getData: (generateId: IdGeneratorFunction, context?: TemplateContext) => {
|
|
23
|
+
ids: Record<string, string>;
|
|
24
|
+
};
|
|
25
|
+
};
|
|
26
|
+
"page/custom/1.84/ext/View.xml": {
|
|
27
|
+
getData: (generateId: IdGeneratorFunction, context?: TemplateContext) => {
|
|
28
|
+
ids: Record<string, string>;
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
"common/FragmentWithForm.xml": {
|
|
32
|
+
getData: (generateId: IdGeneratorFunction) => {
|
|
33
|
+
ids: Record<string, string>;
|
|
34
|
+
};
|
|
35
|
+
};
|
|
36
|
+
"common/FragmentWithVBox.xml": {
|
|
37
|
+
getData: (generateId: IdGeneratorFunction) => {
|
|
38
|
+
ids: Record<string, string>;
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
"view/ext/CustomViewWithTable.xml": {
|
|
42
|
+
getData: (generateId: IdGeneratorFunction) => {
|
|
43
|
+
ids: Record<string, string>;
|
|
44
|
+
};
|
|
45
|
+
};
|
|
46
|
+
"filter/fragment.xml": {
|
|
47
|
+
getData: (generateId: IdGeneratorFunction) => {
|
|
48
|
+
ids: Record<string, string>;
|
|
49
|
+
};
|
|
50
|
+
};
|
|
51
|
+
"building-block/rich-text-editor-button-groups/View.xml": {
|
|
52
|
+
getData: (generateId: IdGeneratorFunction, context?: TemplateContext) => {
|
|
53
|
+
ids: Record<string, string>;
|
|
54
|
+
};
|
|
55
|
+
};
|
|
56
|
+
};
|
|
57
|
+
/**
|
|
58
|
+
* Retrieves all view and fragment files in the application.
|
|
59
|
+
*
|
|
60
|
+
* @param appPath - The root path of the application
|
|
61
|
+
* @param fs - The file system object for reading files
|
|
62
|
+
* @returns A list of view and fragment files
|
|
63
|
+
*/
|
|
64
|
+
export declare function getFragmentAndViewFiles(appPath: string, fs: Editor): Promise<string[]>;
|
|
65
|
+
/**
|
|
66
|
+
* Creates an ID generator function for a given base path and editor.
|
|
67
|
+
* The generator ensures unique IDs across all fragment and view files in the project.
|
|
68
|
+
*
|
|
69
|
+
* @param basePath - Base path of the project
|
|
70
|
+
* @param fsEditor - mem-fs-editor instance
|
|
71
|
+
* @returns A function that generates unique IDs based on a base ID string
|
|
72
|
+
*/
|
|
73
|
+
export declare function createIdGenerator(basePath: string | undefined, fsEditor: Editor): Promise<(baseId: string) => string>;
|
|
3
74
|
export declare const COPY_TEMPLATE_OPTIONS: CopyOptions & {
|
|
4
75
|
noGlob: boolean;
|
|
5
76
|
};
|
|
@@ -44,7 +115,16 @@ export declare function extendJSON(fs: Editor, params: ExtendJsonParams): void;
|
|
|
44
115
|
* @param from - Source path of the template file or directory.
|
|
45
116
|
* @param to - Destination path where the rendered files will be written.
|
|
46
117
|
* @param context - Optional template context used for interpolation.
|
|
118
|
+
* @param {(baseId: string) => string} generateId - Function to generate unique IDs for the building block elements.
|
|
119
|
+
*/
|
|
120
|
+
export declare function copyTpl(fs: Editor, from: string, to: string, context?: object, generateId?: (baseId: string) => string): void;
|
|
121
|
+
/**
|
|
122
|
+
* Extracts the relative path from the templates directory.
|
|
123
|
+
* Works cross-platform by normalizing path separators.
|
|
124
|
+
*
|
|
125
|
+
* @param absolutePath - Absolute path to a template file or directory
|
|
126
|
+
* @returns Relative path from templates directory with forward slashes, or original path if 'templates' not found
|
|
47
127
|
*/
|
|
48
|
-
export declare function
|
|
128
|
+
export declare function getRelativeTemplateComponentPath(absolutePath: string): string;
|
|
49
129
|
export {};
|
|
50
130
|
//# sourceMappingURL=file.d.ts.map
|
package/dist/common/file.js
CHANGED
|
@@ -1,12 +1,151 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.COPY_TEMPLATE_OPTIONS = void 0;
|
|
3
|
+
exports.COPY_TEMPLATE_OPTIONS = exports.CONFIG = void 0;
|
|
4
|
+
exports.getFragmentAndViewFiles = getFragmentAndViewFiles;
|
|
5
|
+
exports.createIdGenerator = createIdGenerator;
|
|
4
6
|
exports.detectTabSpacing = detectTabSpacing;
|
|
5
7
|
exports.getJsonSpace = getJsonSpace;
|
|
6
8
|
exports.extendJSON = extendJSON;
|
|
7
9
|
exports.copyTpl = copyTpl;
|
|
10
|
+
exports.getRelativeTemplateComponentPath = getRelativeTemplateComponentPath;
|
|
11
|
+
const node_path_1 = require("node:path");
|
|
12
|
+
const file_1 = require("@sap-ux/project-access/dist/file");
|
|
13
|
+
const utils_1 = require("../building-block/prompts/utils");
|
|
8
14
|
const CHAR_SPACE = ' ';
|
|
9
15
|
const CHAR_TAB = '\t';
|
|
16
|
+
exports.CONFIG = {
|
|
17
|
+
['page/custom/1.94/ext/View.xml']: {
|
|
18
|
+
getData: (generateId, context) => {
|
|
19
|
+
return {
|
|
20
|
+
ids: {
|
|
21
|
+
page: generateId(context?.name ?? 'Page')
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
['page/custom/1.84/ext/View.xml']: {
|
|
27
|
+
getData: (generateId, context) => {
|
|
28
|
+
return {
|
|
29
|
+
ids: {
|
|
30
|
+
page: generateId(context?.name ?? 'Page')
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
['common/FragmentWithForm.xml']: {
|
|
36
|
+
getData: (generateId) => {
|
|
37
|
+
return {
|
|
38
|
+
ids: {
|
|
39
|
+
formElement: generateId('FormElement')
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
['common/FragmentWithVBox.xml']: {
|
|
45
|
+
getData: (generateId) => {
|
|
46
|
+
return {
|
|
47
|
+
ids: {
|
|
48
|
+
vbox: generateId('VBox')
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
['view/ext/CustomViewWithTable.xml']: {
|
|
54
|
+
getData: (generateId) => {
|
|
55
|
+
return {
|
|
56
|
+
ids: {
|
|
57
|
+
table: generateId('Table')
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
['filter/fragment.xml']: {
|
|
63
|
+
getData: (generateId) => {
|
|
64
|
+
const item1 = generateId('Item');
|
|
65
|
+
const item2 = generateId('Item', [item1]);
|
|
66
|
+
const item3 = generateId('Item', [item1, item2]);
|
|
67
|
+
return {
|
|
68
|
+
ids: {
|
|
69
|
+
comboBox: generateId('ComboBox'),
|
|
70
|
+
item1,
|
|
71
|
+
item2,
|
|
72
|
+
item3
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
['building-block/rich-text-editor-button-groups/View.xml']: {
|
|
78
|
+
getData: (generateId, context) => {
|
|
79
|
+
// Get buttonGroups from context
|
|
80
|
+
const buttonGroups = context?.buttonGroups || context?.data?.buttonGroups || [];
|
|
81
|
+
// Generate IDs for each button group and store in ids object
|
|
82
|
+
const ids = {};
|
|
83
|
+
const validatedIds = [];
|
|
84
|
+
buttonGroups.forEach((group, index) => {
|
|
85
|
+
const id = generateId(`${'ButtonGroup'}`, validatedIds);
|
|
86
|
+
ids[index] = group.id ?? id;
|
|
87
|
+
if (!group.id) {
|
|
88
|
+
validatedIds.push(id);
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
return { ids };
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
/**
|
|
96
|
+
* Generates a unique element ID that is not already used in any view or fragment file.
|
|
97
|
+
* Uses an incremental counter for predictable, readable IDs.
|
|
98
|
+
*
|
|
99
|
+
* @param fs - The file system object for reading files
|
|
100
|
+
* @param baseId - The base name for the ID (e.g., 'filterBar', 'chart')
|
|
101
|
+
* @param filteredFiles - The list of files to check for ID availability
|
|
102
|
+
* @param validatedIds - A list of IDs that have already been validated in the current session to avoid duplicates
|
|
103
|
+
* @returns A unique ID that is available across all view and fragment files
|
|
104
|
+
*/
|
|
105
|
+
function generateUniqueElementId(fs, baseId, filteredFiles, validatedIds = []) {
|
|
106
|
+
const maxAttempts = 1000;
|
|
107
|
+
if (filteredFiles.every((file) => (0, utils_1.isElementIdAvailable)(fs, file, baseId)) && !validatedIds.includes(baseId)) {
|
|
108
|
+
return baseId;
|
|
109
|
+
}
|
|
110
|
+
for (let counter = 1; counter < maxAttempts; counter++) {
|
|
111
|
+
const candidateId = `${baseId}${counter}`;
|
|
112
|
+
if (filteredFiles.every((file) => (0, utils_1.isElementIdAvailable)(fs, file, candidateId)) &&
|
|
113
|
+
!validatedIds.includes(candidateId)) {
|
|
114
|
+
return candidateId;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
// If we couldn't find an available ID after maxAttempts
|
|
118
|
+
throw new Error(`Failed to generate unique ID for base '${baseId}' after ${maxAttempts} attempts`);
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Retrieves all view and fragment files in the application.
|
|
122
|
+
*
|
|
123
|
+
* @param appPath - The root path of the application
|
|
124
|
+
* @param fs - The file system object for reading files
|
|
125
|
+
* @returns A list of view and fragment files
|
|
126
|
+
*/
|
|
127
|
+
async function getFragmentAndViewFiles(appPath, fs) {
|
|
128
|
+
const files = await (0, file_1.findFilesByExtension)('.xml', appPath, ['.git', 'node_modules', 'dist', 'annotations', 'localService'], fs);
|
|
129
|
+
const lookupFiles = ['.fragment.xml', '.view.xml'];
|
|
130
|
+
return files.filter((fileName) => lookupFiles.some((lookupFile) => fileName.endsWith(lookupFile)));
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Creates an ID generator function for a given base path and editor.
|
|
134
|
+
* The generator ensures unique IDs across all fragment and view files in the project.
|
|
135
|
+
*
|
|
136
|
+
* @param basePath - Base path of the project
|
|
137
|
+
* @param fsEditor - mem-fs-editor instance
|
|
138
|
+
* @returns A function that generates unique IDs based on a base ID string
|
|
139
|
+
*/
|
|
140
|
+
async function createIdGenerator(basePath, fsEditor) {
|
|
141
|
+
let files = [];
|
|
142
|
+
if (basePath) {
|
|
143
|
+
files = await getFragmentAndViewFiles(basePath, fsEditor);
|
|
144
|
+
}
|
|
145
|
+
return (baseId, validatedIds = []) => {
|
|
146
|
+
return generateUniqueElementId(fsEditor, baseId, files, validatedIds);
|
|
147
|
+
};
|
|
148
|
+
}
|
|
10
149
|
// `noGlob` is supported in `mem-fs-editor` v9,
|
|
11
150
|
// but is missing from `@types/mem-fs-editor` (no v9 typings), so we extend the type here.
|
|
12
151
|
exports.COPY_TEMPLATE_OPTIONS = {
|
|
@@ -102,8 +241,34 @@ function extendJSON(fs, params) {
|
|
|
102
241
|
* @param from - Source path of the template file or directory.
|
|
103
242
|
* @param to - Destination path where the rendered files will be written.
|
|
104
243
|
* @param context - Optional template context used for interpolation.
|
|
244
|
+
* @param {(baseId: string) => string} generateId - Function to generate unique IDs for the building block elements.
|
|
105
245
|
*/
|
|
106
|
-
function copyTpl(fs, from, to, context) {
|
|
246
|
+
function copyTpl(fs, from, to, context, generateId) {
|
|
247
|
+
const configKey = getRelativeTemplateComponentPath(from);
|
|
248
|
+
const config = exports.CONFIG[configKey];
|
|
249
|
+
if (generateId && config?.getData) {
|
|
250
|
+
const additionalContext = config.getData(generateId, context);
|
|
251
|
+
context = { ...context, ...additionalContext };
|
|
252
|
+
}
|
|
107
253
|
fs.copyTpl(from, to, context, undefined, exports.COPY_TEMPLATE_OPTIONS);
|
|
108
254
|
}
|
|
255
|
+
/**
|
|
256
|
+
* Extracts the relative path from the templates directory.
|
|
257
|
+
* Works cross-platform by normalizing path separators.
|
|
258
|
+
*
|
|
259
|
+
* @param absolutePath - Absolute path to a template file or directory
|
|
260
|
+
* @returns Relative path from templates directory with forward slashes, or original path if 'templates' not found
|
|
261
|
+
*/
|
|
262
|
+
function getRelativeTemplateComponentPath(absolutePath) {
|
|
263
|
+
const normalizedPath = (0, node_path_1.normalize)(absolutePath);
|
|
264
|
+
const templatesMarker = `${node_path_1.sep}templates${node_path_1.sep}`;
|
|
265
|
+
const templatesIndex = normalizedPath.indexOf(templatesMarker);
|
|
266
|
+
if (templatesIndex === -1) {
|
|
267
|
+
return absolutePath;
|
|
268
|
+
}
|
|
269
|
+
// Extract everything after '/templates/' or '\\templates\\'
|
|
270
|
+
const relativePath = normalizedPath.substring(templatesIndex + templatesMarker.length);
|
|
271
|
+
// Normalize to forward slashes for consistency
|
|
272
|
+
return relativePath.split(node_path_1.sep).join('/');
|
|
273
|
+
}
|
|
109
274
|
//# sourceMappingURL=file.js.map
|
package/dist/field/index.js
CHANGED
|
@@ -18,9 +18,10 @@ const utils_1 = require("../common/utils");
|
|
|
18
18
|
* @param {CustomField} data - a custom field configuration object
|
|
19
19
|
* @param {string} manifestPath - path to the project's manifest.json
|
|
20
20
|
* @param {Manifest} manifest - the application manifest
|
|
21
|
+
* @param {(baseId: string) => string} generateId - Function to generate unique IDs for the building block elements.
|
|
21
22
|
* @returns enhanced configuration
|
|
22
23
|
*/
|
|
23
|
-
function enhanceConfig(fs, data, manifestPath, manifest) {
|
|
24
|
+
function enhanceConfig(fs, data, manifestPath, manifest, generateId) {
|
|
24
25
|
// clone input and set defaults
|
|
25
26
|
const config = { ...data };
|
|
26
27
|
(0, defaults_1.setCommonDefaults)(config, manifestPath, manifest);
|
|
@@ -36,7 +37,7 @@ function enhanceConfig(fs, data, manifestPath, manifest) {
|
|
|
36
37
|
config.content = config.control;
|
|
37
38
|
}
|
|
38
39
|
else {
|
|
39
|
-
Object.assign(config, (0, defaults_1.getDefaultFragmentContentData)(config.name, config.eventHandler));
|
|
40
|
+
Object.assign(config, (0, defaults_1.getDefaultFragmentContentData)(config.name, generateId, config.eventHandler));
|
|
40
41
|
}
|
|
41
42
|
return config;
|
|
42
43
|
}
|
|
@@ -50,17 +51,16 @@ function enhanceConfig(fs, data, manifestPath, manifest) {
|
|
|
50
51
|
*/
|
|
51
52
|
async function generateCustomField(basePath, customField, fs) {
|
|
52
53
|
(0, validate_1.validateVersion)(customField.minUI5Version);
|
|
53
|
-
|
|
54
|
-
fs = (0, mem_fs_editor_1.create)((0, mem_fs_1.create)());
|
|
55
|
-
}
|
|
54
|
+
fs ??= (0, mem_fs_editor_1.create)((0, mem_fs_1.create)());
|
|
56
55
|
await (0, validate_1.validateBasePath)(basePath, fs);
|
|
56
|
+
const generateId = await (0, file_1.createIdGenerator)(basePath, fs);
|
|
57
57
|
const { path: manifestPath, content: manifest } = await (0, utils_1.getManifest)(basePath, fs);
|
|
58
58
|
// merge with defaults
|
|
59
|
-
const completeField = enhanceConfig(fs, customField, manifestPath, manifest);
|
|
59
|
+
const completeField = enhanceConfig(fs, customField, manifestPath, manifest, generateId);
|
|
60
60
|
// add fragment
|
|
61
61
|
const viewPath = (0, node_path_1.join)(completeField.path, `${completeField.fragmentFile ?? completeField.name}.fragment.xml`);
|
|
62
62
|
if (!fs.exists(viewPath)) {
|
|
63
|
-
(0, file_1.copyTpl)(fs, (0, templates_1.getTemplatePath)('common/Fragment.xml'), viewPath, completeField);
|
|
63
|
+
(0, file_1.copyTpl)(fs, (0, templates_1.getTemplatePath)('common/Fragment.xml'), viewPath, completeField, generateId);
|
|
64
64
|
}
|
|
65
65
|
// enhance manifest with field definition
|
|
66
66
|
const templatePath = (0, templates_1.getTemplatePath)('/field/manifest.json');
|
package/dist/filter/index.js
CHANGED
|
@@ -48,6 +48,7 @@ async function generateCustomFilter(basePath, filterConfig, fs) {
|
|
|
48
48
|
fs = (0, mem_fs_editor_1.create)((0, mem_fs_1.create)());
|
|
49
49
|
}
|
|
50
50
|
await (0, validate_1.validateBasePath)(basePath, fs);
|
|
51
|
+
const fnGenerateId = await (0, file_1.createIdGenerator)(basePath, fs);
|
|
51
52
|
const { path: manifestPath, content: manifest } = await (0, utils_1.getManifest)(basePath, fs);
|
|
52
53
|
const config = enhanceConfig(filterConfig, manifestPath, manifest);
|
|
53
54
|
// Apply event handler
|
|
@@ -65,7 +66,7 @@ async function generateCustomFilter(basePath, filterConfig, fs) {
|
|
|
65
66
|
// create a fragment file
|
|
66
67
|
const fragmentPath = (0, node_path_1.join)(config.path, `${config.fragmentFile}.fragment.xml`);
|
|
67
68
|
if (!fs.exists(fragmentPath)) {
|
|
68
|
-
(0, file_1.copyTpl)(fs, (0, templates_1.getTemplatePath)(`filter/fragment.xml`), fragmentPath, config);
|
|
69
|
+
(0, file_1.copyTpl)(fs, (0, templates_1.getTemplatePath)(`filter/fragment.xml`), fragmentPath, config, fnGenerateId);
|
|
69
70
|
}
|
|
70
71
|
return fs;
|
|
71
72
|
}
|
package/dist/page/custom.js
CHANGED
|
@@ -17,7 +17,7 @@ const utils_1 = require("../common/utils");
|
|
|
17
17
|
const file_1 = require("../common/file");
|
|
18
18
|
const building_block_1 = require("../building-block");
|
|
19
19
|
const types_2 = require("../building-block/types");
|
|
20
|
-
const
|
|
20
|
+
const utils_2 = require("../building-block/prompts/utils");
|
|
21
21
|
const i18n_1 = require("../i18n");
|
|
22
22
|
/**
|
|
23
23
|
* Enhances the provided custom page configuration with default data.
|
|
@@ -80,23 +80,26 @@ function getTemplateRoot(ui5Version) {
|
|
|
80
80
|
* @param data.minUI5Version
|
|
81
81
|
* @param {string} viewPath - The path to the view XML file.
|
|
82
82
|
* @param {Editor} fs - The memfs editor instance.
|
|
83
|
+
* @param {(baseId: string) => Promise<string>} generateId - Function to generate unique IDs for the building block elements.
|
|
83
84
|
* @param {Logger} [log] - Logger instance.
|
|
84
85
|
* @returns {Promise<void>} Resolves when the building block is handled or skipped due to version constraints.
|
|
85
86
|
*/
|
|
86
|
-
async function handlePageBuildingBlock(basePath, data, viewPath, fs, log) {
|
|
87
|
+
async function handlePageBuildingBlock(basePath, data, viewPath, fs, generateId, log) {
|
|
87
88
|
const minVersion = (0, semver_1.coerce)(data.minUI5Version);
|
|
88
89
|
const t = (0, i18n_1.translate)(i18n_1.i18nNamespaces.buildingBlock, 'pageBuildingBlock.');
|
|
89
90
|
if (minVersion && (0, semver_1.lt)(minVersion.version, '1.136.0')) {
|
|
90
91
|
log?.warn(t('minUi5VersionRequirement', { minUI5Version: data.minUI5Version }));
|
|
91
92
|
return;
|
|
92
93
|
}
|
|
94
|
+
const pageId = await generateId('Page');
|
|
93
95
|
await (0, building_block_1.generateBuildingBlock)(basePath, {
|
|
94
96
|
viewOrFragmentPath: (0, node_path_1.relative)(basePath, viewPath),
|
|
95
|
-
aggregationPath: (0,
|
|
97
|
+
aggregationPath: (0, utils_2.augmentXpathWithLocalNames)(`/mvc:View/Page`),
|
|
96
98
|
replace: true,
|
|
97
99
|
buildingBlockData: {
|
|
98
|
-
id:
|
|
100
|
+
id: pageId,
|
|
99
101
|
buildingBlockType: types_2.BuildingBlockType.Page,
|
|
102
|
+
generateId,
|
|
100
103
|
title: data.pageBuildingBlockTitle
|
|
101
104
|
}
|
|
102
105
|
}, fs);
|
|
@@ -111,12 +114,11 @@ async function handlePageBuildingBlock(basePath, data, viewPath, fs, log) {
|
|
|
111
114
|
* @returns {Promise<Editor>} the updated memfs editor instance
|
|
112
115
|
*/
|
|
113
116
|
async function generate(basePath, data, fs, log) {
|
|
114
|
-
|
|
115
|
-
fs = (0, mem_fs_editor_1.create)((0, mem_fs_1.create)());
|
|
116
|
-
}
|
|
117
|
+
fs ??= (0, mem_fs_editor_1.create)((0, mem_fs_1.create)());
|
|
117
118
|
(0, validate_1.validateVersion)(data.minUI5Version);
|
|
118
119
|
await (0, common_1.validatePageConfig)(basePath, data, fs, []);
|
|
119
120
|
const manifestPath = await (0, utils_1.getManifestPath)(basePath, fs);
|
|
121
|
+
const fnGenerateId = await (0, file_1.createIdGenerator)(basePath, fs);
|
|
120
122
|
const config = enhanceData(data, manifestPath, fs);
|
|
121
123
|
// merge content into existing files
|
|
122
124
|
const root = getTemplateRoot(data.minUI5Version);
|
|
@@ -130,7 +132,7 @@ async function generate(basePath, data, fs, log) {
|
|
|
130
132
|
// add extension content
|
|
131
133
|
const viewPath = (0, node_path_1.join)(config.path, `${config.name}.view.xml`);
|
|
132
134
|
if (!fs.exists(viewPath)) {
|
|
133
|
-
(0, file_1.copyTpl)(fs, (0, node_path_1.join)(root, 'ext/View.xml'), viewPath, config);
|
|
135
|
+
(0, file_1.copyTpl)(fs, (0, node_path_1.join)(root, 'ext/View.xml'), viewPath, config, fnGenerateId);
|
|
134
136
|
// i18n.properties
|
|
135
137
|
const manifest = fs.readJSON(manifestPath);
|
|
136
138
|
const defaultI18nPath = 'i18n/i18n.properties';
|
|
@@ -145,7 +147,7 @@ async function generate(basePath, data, fs, log) {
|
|
|
145
147
|
}
|
|
146
148
|
}
|
|
147
149
|
if (data.pageBuildingBlockTitle) {
|
|
148
|
-
await handlePageBuildingBlock(basePath, { pageBuildingBlockTitle: data.pageBuildingBlockTitle, minUI5Version: data.minUI5Version }, viewPath, fs, log);
|
|
150
|
+
await handlePageBuildingBlock(basePath, { pageBuildingBlockTitle: data.pageBuildingBlockTitle, minUI5Version: data.minUI5Version }, viewPath, fs, fnGenerateId, log);
|
|
149
151
|
}
|
|
150
152
|
const ext = data.typescript ? 'ts' : 'js';
|
|
151
153
|
const controllerPath = (0, node_path_1.join)(config.path, `${config.name}.controller.${ext}`);
|
package/dist/prompts/api.d.ts
CHANGED
package/dist/prompts/api.js
CHANGED
|
@@ -20,7 +20,7 @@ class PromptsAPI {
|
|
|
20
20
|
// Cached questions
|
|
21
21
|
cache = {};
|
|
22
22
|
/**
|
|
23
|
-
*
|
|
23
|
+
* Constructor of prompt API.
|
|
24
24
|
*
|
|
25
25
|
* @param fs the file system object for reading files
|
|
26
26
|
* @param project
|
|
@@ -46,9 +46,7 @@ class PromptsAPI {
|
|
|
46
46
|
* @returns Instance of prompt api.
|
|
47
47
|
*/
|
|
48
48
|
static async init(projectPath, appId, fs, options) {
|
|
49
|
-
|
|
50
|
-
fs = (0, mem_fs_editor_1.create)((0, mem_fs_1.create)());
|
|
51
|
-
}
|
|
49
|
+
fs = fs ?? (0, mem_fs_editor_1.create)((0, mem_fs_1.create)());
|
|
52
50
|
await (0, i18n_1.initI18n)();
|
|
53
51
|
const project = projectPath ? await (0, project_access_1.getProject)(projectPath) : undefined;
|
|
54
52
|
return new PromptsAPI(fs, project, appId, options);
|
|
@@ -147,7 +145,8 @@ class PromptsAPI {
|
|
|
147
145
|
const generator = map_1.PromptsGeneratorsMap.hasOwnProperty(config.type)
|
|
148
146
|
? map_1.PromptsGeneratorsMap[config.type]
|
|
149
147
|
: undefined;
|
|
150
|
-
|
|
148
|
+
config.answers.buildingBlockData = { ...config.answers.buildingBlockData };
|
|
149
|
+
return generator?.(this.context.appPath, { ...config.answers }, this.context.fs) ?? this.context.fs;
|
|
151
150
|
}
|
|
152
151
|
/**
|
|
153
152
|
* Method returns code snippet for passed answers and prompt type.
|
package/dist/section/index.js
CHANGED
|
@@ -48,9 +48,10 @@ function getAdditionalDependencies(ui5Version) {
|
|
|
48
48
|
* @param {CustomSection} data - a custom section configuration object
|
|
49
49
|
* @param {string} manifestPath - path to the project's manifest.json
|
|
50
50
|
* @param {Manifest} manifest - the application manifest
|
|
51
|
+
* @param {(baseId: string) => string} generateId - Function to generate unique IDs for the building block elements.
|
|
51
52
|
* @returns enhanced configuration
|
|
52
53
|
*/
|
|
53
|
-
function enhanceConfig(fs, data, manifestPath, manifest) {
|
|
54
|
+
function enhanceConfig(fs, data, manifestPath, manifest, generateId) {
|
|
54
55
|
const config = { ...data };
|
|
55
56
|
(0, defaults_1.setCommonDefaults)(config, manifestPath, manifest);
|
|
56
57
|
// Apply event handler
|
|
@@ -65,7 +66,7 @@ function enhanceConfig(fs, data, manifestPath, manifest) {
|
|
|
65
66
|
config.content = config.control;
|
|
66
67
|
}
|
|
67
68
|
else {
|
|
68
|
-
Object.assign(config, (0, defaults_1.getDefaultFragmentContentData)(config.name, config.eventHandler, undefined, undefined, false));
|
|
69
|
+
Object.assign(config, (0, defaults_1.getDefaultFragmentContentData)(config.name, generateId, config.eventHandler, undefined, undefined, false));
|
|
69
70
|
}
|
|
70
71
|
// Additional dependencies to include into 'Fragment.xml'
|
|
71
72
|
config.dependencies = getAdditionalDependencies(config.minUI5Version);
|
|
@@ -82,13 +83,12 @@ function enhanceConfig(fs, data, manifestPath, manifest) {
|
|
|
82
83
|
*/
|
|
83
84
|
async function generate(basePath, customSection, manifestTemplateRoot, fs) {
|
|
84
85
|
(0, validate_1.validateVersion)(customSection.minUI5Version);
|
|
85
|
-
|
|
86
|
-
fs = (0, mem_fs_editor_1.create)((0, mem_fs_1.create)());
|
|
87
|
-
}
|
|
86
|
+
fs ??= (0, mem_fs_editor_1.create)((0, mem_fs_1.create)());
|
|
88
87
|
await (0, validate_1.validateBasePath)(basePath, fs);
|
|
88
|
+
const fnGenerateId = await (0, file_1.createIdGenerator)(basePath, fs);
|
|
89
89
|
const { path: manifestPath, content: manifest } = await (0, utils_1.getManifest)(basePath, fs);
|
|
90
90
|
// merge with defaults
|
|
91
|
-
const completeSection = enhanceConfig(fs, customSection, manifestPath, manifest);
|
|
91
|
+
const completeSection = enhanceConfig(fs, customSection, manifestPath, manifest, fnGenerateId);
|
|
92
92
|
// enhance manifest with section definition
|
|
93
93
|
const filledTemplate = (0, ejs_1.render)(fs.read((0, node_path_1.join)(manifestTemplateRoot, `manifest.json`)), completeSection, {});
|
|
94
94
|
(0, file_1.extendJSON)(fs, {
|
|
@@ -99,7 +99,7 @@ async function generate(basePath, customSection, manifestTemplateRoot, fs) {
|
|
|
99
99
|
// add fragment
|
|
100
100
|
const viewPath = (0, node_path_1.join)(completeSection.path, `${completeSection.fragmentFile ?? completeSection.name}.fragment.xml`);
|
|
101
101
|
if (!fs.exists(viewPath)) {
|
|
102
|
-
(0, file_1.copyTpl)(fs, (0, templates_1.getTemplatePath)('common/FragmentWithVBox.xml'), viewPath, completeSection);
|
|
102
|
+
(0, file_1.copyTpl)(fs, (0, templates_1.getTemplatePath)('common/FragmentWithVBox.xml'), viewPath, completeSection, fnGenerateId);
|
|
103
103
|
}
|
|
104
104
|
return { editor: fs, section: completeSection };
|
|
105
105
|
}
|
|
@@ -112,21 +112,20 @@ async function generate(basePath, customSection, manifestTemplateRoot, fs) {
|
|
|
112
112
|
* @returns {Promise<Editor>} the updated mem-fs editor instance
|
|
113
113
|
*/
|
|
114
114
|
async function generateCustomHeaderSection(basePath, customHeaderSection, fs) {
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
}
|
|
115
|
+
const fsEditor = fs ?? (0, mem_fs_editor_1.create)((0, mem_fs_1.create)());
|
|
116
|
+
const fnGenerateId = await (0, file_1.createIdGenerator)(basePath, fsEditor); // initialize ID generator to ensure unique IDs across both section and edit fragment
|
|
118
117
|
const manifestRoot = getManifestRoot('header-section', customHeaderSection.minUI5Version);
|
|
119
118
|
const minVersion = (0, semver_1.coerce)(customHeaderSection.minUI5Version);
|
|
120
119
|
let editSection;
|
|
121
120
|
// Prepare 'templateEdit' - apply namespace and folder path resolution
|
|
122
121
|
if (customHeaderSection.edit && (!minVersion || (0, semver_1.gte)(minVersion, '1.86.0'))) {
|
|
123
122
|
editSection = customHeaderSection.edit;
|
|
124
|
-
const { path: manifestPath, content: manifest } = await (0, utils_1.getManifest)(basePath,
|
|
123
|
+
const { path: manifestPath, content: manifest } = await (0, utils_1.getManifest)(basePath, fsEditor);
|
|
125
124
|
// Set folder, ns and path for edit fragment
|
|
126
125
|
(0, defaults_1.setCommonDefaults)(editSection, manifestPath, manifest);
|
|
127
126
|
}
|
|
128
127
|
// Call standard custom section generation
|
|
129
|
-
const { editor, section } = await generate(basePath, customHeaderSection, manifestRoot,
|
|
128
|
+
const { editor, section } = await generate(basePath, customHeaderSection, manifestRoot, fsEditor);
|
|
130
129
|
// Handle 'templateEdit' - edit fragment details
|
|
131
130
|
if (editSection) {
|
|
132
131
|
// Apply event handler for edit fragment
|
|
@@ -142,12 +141,12 @@ async function generateCustomHeaderSection(basePath, customHeaderSection, fs) {
|
|
|
142
141
|
editSection.content = editSection.control;
|
|
143
142
|
}
|
|
144
143
|
else {
|
|
145
|
-
Object.assign(editSection, (0, defaults_1.getDefaultFragmentContentData)(editSection.name, editSection.eventHandler, false, true, false));
|
|
144
|
+
Object.assign(editSection, (0, defaults_1.getDefaultFragmentContentData)(editSection.name, fnGenerateId, editSection.eventHandler, false, true, false));
|
|
146
145
|
}
|
|
147
146
|
if (editSection.path) {
|
|
148
147
|
const viewPath = (0, node_path_1.join)(editSection.path, `${editSection.name}.fragment.xml`);
|
|
149
148
|
if (!editor.exists(viewPath)) {
|
|
150
|
-
(0, file_1.copyTpl)(editor, (0, templates_1.getTemplatePath)('common/FragmentWithForm.xml'), viewPath, editSection);
|
|
149
|
+
(0, file_1.copyTpl)(editor, (0, templates_1.getTemplatePath)('common/FragmentWithForm.xml'), viewPath, editSection, fnGenerateId);
|
|
151
150
|
}
|
|
152
151
|
}
|
|
153
152
|
}
|
package/dist/view/index.js
CHANGED
|
@@ -49,9 +49,10 @@ function mergeViews(config, manifest) {
|
|
|
49
49
|
* @param {CustomView} data - a custom view configuration object
|
|
50
50
|
* @param {string} manifestPath - path to the project's manifest.json
|
|
51
51
|
* @param {Manifest} manifest - the application manifest
|
|
52
|
+
* @param {IdGeneratorFunction} generateId - Function to generate unique IDs for the building block elements.
|
|
52
53
|
* @returns enhanced configuration
|
|
53
54
|
*/
|
|
54
|
-
function enhanceConfig(fs, data, manifestPath, manifest) {
|
|
55
|
+
function enhanceConfig(fs, data, manifestPath, manifest, generateId) {
|
|
55
56
|
const config = { ...data };
|
|
56
57
|
(0, defaults_1.setCommonDefaults)(config, manifestPath, manifest);
|
|
57
58
|
// apply event handler
|
|
@@ -68,7 +69,7 @@ function enhanceConfig(fs, data, manifestPath, manifest) {
|
|
|
68
69
|
config.content = config.control;
|
|
69
70
|
}
|
|
70
71
|
else {
|
|
71
|
-
config.content = (0, defaults_1.getDefaultFragmentContent)(config.name, config.eventHandler, true);
|
|
72
|
+
config.content = (0, defaults_1.getDefaultFragmentContent)(config.name, generateId, config.eventHandler, true);
|
|
72
73
|
}
|
|
73
74
|
return config;
|
|
74
75
|
}
|
|
@@ -82,13 +83,12 @@ function enhanceConfig(fs, data, manifestPath, manifest) {
|
|
|
82
83
|
*/
|
|
83
84
|
async function generateCustomView(basePath, customView, fs) {
|
|
84
85
|
(0, validate_1.validateVersion)(customView.minUI5Version);
|
|
85
|
-
|
|
86
|
-
fs = (0, mem_fs_editor_1.create)((0, mem_fs_1.create)());
|
|
87
|
-
}
|
|
86
|
+
fs ??= (0, mem_fs_editor_1.create)((0, mem_fs_1.create)());
|
|
88
87
|
await (0, validate_1.validateBasePath)(basePath, fs);
|
|
88
|
+
const fnGenerateId = await (0, file_1.createIdGenerator)(basePath, fs);
|
|
89
89
|
const { path: manifestPath, content: manifest } = await (0, utils_1.getManifest)(basePath, fs);
|
|
90
90
|
// merge with defaults
|
|
91
|
-
const completeView = enhanceConfig(fs, customView, manifestPath, manifest);
|
|
91
|
+
const completeView = enhanceConfig(fs, customView, manifestPath, manifest, fnGenerateId);
|
|
92
92
|
// enhance manifest with view definition
|
|
93
93
|
const filledTemplate = (0, ejs_1.render)(fs.read((0, templates_1.getTemplatePath)('view/manifest.json')), completeView, {});
|
|
94
94
|
(0, file_1.extendJSON)(fs, {
|
|
@@ -100,10 +100,10 @@ async function generateCustomView(basePath, customView, fs) {
|
|
|
100
100
|
if (customView.viewUpdate !== false) {
|
|
101
101
|
const viewPath = (0, node_path_1.join)(completeView.path, `${completeView.name}.fragment.xml`);
|
|
102
102
|
if (completeView.control === true) {
|
|
103
|
-
(0, file_1.copyTpl)(fs, (0, templates_1.getTemplatePath)('view/ext/CustomViewWithTable.xml'), viewPath, completeView);
|
|
103
|
+
(0, file_1.copyTpl)(fs, (0, templates_1.getTemplatePath)('view/ext/CustomViewWithTable.xml'), viewPath, completeView, fnGenerateId);
|
|
104
104
|
}
|
|
105
105
|
else if (!fs.exists(viewPath)) {
|
|
106
|
-
(0, file_1.copyTpl)(fs, (0, templates_1.getTemplatePath)('common/Fragment.xml'), viewPath, completeView);
|
|
106
|
+
(0, file_1.copyTpl)(fs, (0, templates_1.getTemplatePath)('common/Fragment.xml'), viewPath, completeView, fnGenerateId);
|
|
107
107
|
}
|
|
108
108
|
}
|
|
109
109
|
return fs;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sap-ux/fe-fpm-writer",
|
|
3
3
|
"description": "SAP Fiori elements flexible programming model writer",
|
|
4
|
-
"version": "0.42.
|
|
4
|
+
"version": "0.42.20",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
7
7
|
"url": "https://github.com/SAP/open-ux-tools.git",
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
"@types/semver": "7.7.1",
|
|
43
43
|
"@types/vinyl": "2.0.7",
|
|
44
44
|
"@sap-ux/i18n": "0.3.9",
|
|
45
|
-
"@sap-ux/ui-prompting": "0.6.
|
|
45
|
+
"@sap-ux/ui-prompting": "0.6.8"
|
|
46
46
|
},
|
|
47
47
|
"engines": {
|
|
48
48
|
"node": ">=20.x"
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<<%- config.aggregationNamespace %>:buttonGroups>
|
|
2
|
-
<% (data.buttonGroups || []).forEach(function(buttonGroup) { %>
|
|
2
|
+
<% (data.buttonGroups || []).forEach(function(buttonGroup, idKey) { %>
|
|
3
3
|
<richtexteditor:ButtonGroup
|
|
4
|
+
id="<%- buttonGroup.id || ids[idKey] %>"
|
|
4
5
|
name="<%- buttonGroup.name %>"
|
|
5
6
|
visible="<%- buttonGroup.visible !== undefined ? buttonGroup.visible : true %>"
|
|
6
7
|
buttons="<%- buttonGroup.buttons %>"<%
|
|
@@ -12,9 +13,6 @@
|
|
|
12
13
|
} %><%
|
|
13
14
|
if (buttonGroup.row !== undefined) { %>
|
|
14
15
|
row="<%- buttonGroup.row %>"<%
|
|
15
|
-
} %><%
|
|
16
|
-
if (buttonGroup.id !== undefined) { %>
|
|
17
|
-
id="<%- buttonGroup.id %>"<%
|
|
18
16
|
} %>/>
|
|
19
17
|
<% }); %>
|
|
20
18
|
</<%- config.aggregationNamespace %>:buttonGroups>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<core:FragmentDefinition xmlns:core="sap.ui.core" xmlns="sap.m" xmlns:f="sap.ui.layout.form"<%- typeof dependencies !== 'undefined' ? ` ${dependencies}` : '' %>>
|
|
2
|
-
<f:FormElement<%- typeof requireAttribute !== 'undefined' ? ` ${requireAttribute}` : '' %>>
|
|
2
|
+
<f:FormElement id="<%- ids.formElement %>"<%- typeof requireAttribute !== 'undefined' ? ` ${requireAttribute}` : '' %>>
|
|
3
3
|
<f:fields>
|
|
4
4
|
<%- content %>
|
|
5
5
|
</f:fields>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<core:FragmentDefinition xmlns:core="sap.ui.core" xmlns="sap.m"<%- typeof dependencies !== 'undefined' ? ` ${dependencies}` : '' %>>
|
|
2
|
-
<VBox<%- typeof requireAttribute !== 'undefined' ? ` ${requireAttribute}` : '' %>>
|
|
2
|
+
<VBox id="<%- ids.vbox %>"<%- typeof requireAttribute !== 'undefined' ? ` ${requireAttribute}` : '' %>>
|
|
3
3
|
<%- content %>
|
|
4
4
|
</VBox>
|
|
5
5
|
</core:FragmentDefinition>
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
<core:FragmentDefinition xmlns:core="sap.ui.core" xmlns="sap.m"><%if (typeof eventHandler !== 'undefined') {%>
|
|
2
2
|
<ComboBox
|
|
3
|
-
<% if (typeof controlID !== 'undefined') {
|
|
3
|
+
id="<% if (typeof controlID !== 'undefined' && controlID) { %><%- controlID %><% } else { %><%- ids.comboBox %><% } %>"
|
|
4
4
|
width="100%"
|
|
5
5
|
core:require="{handler: '<%- eventHandler.split('.').slice(0, -1).join('/') %>'}"
|
|
6
6
|
selectedKey="{path: 'filterValues>', type: 'sap.fe.macros.filter.type.Value', formatOptions: { operator: '<%- eventHandler %>' }}"
|
|
7
7
|
><% } else { %>
|
|
8
|
-
<ComboBox<% if (typeof controlID !== 'undefined') {
|
|
8
|
+
<ComboBox id="<% if (typeof controlID !== 'undefined' && controlID) { %><%- controlID %><% } else { %><%- ids.comboBox %><% } %>" width="100%"><% } %>
|
|
9
9
|
<items>
|
|
10
|
-
<core:Item key="0" text="Item1"/>
|
|
11
|
-
<core:Item key="1" text="Item2"/>
|
|
12
|
-
<core:Item key="2" text="Item3"/>
|
|
10
|
+
<core:Item id="<%- ids.item1 %>" key="0" text="Item1"/>
|
|
11
|
+
<core:Item id="<%- ids.item2 %>" key="1" text="Item2"/>
|
|
12
|
+
<core:Item id="<%- ids.item3 %>" key="2" text="Item3"/>
|
|
13
13
|
</items>
|
|
14
14
|
</ComboBox>
|
|
15
15
|
</core:FragmentDefinition>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<mvc:View xmlns:core="sap.ui.core" xmlns:mvc="sap.ui.core.mvc" xmlns="sap.m"
|
|
2
2
|
xmlns:html="http://www.w3.org/1999/xhtml" controllerName="<%- ns %>.<%- name %>">
|
|
3
|
-
<Page id="<%-
|
|
3
|
+
<Page id="<%- ids.page %>" title="{i18n><%- name %>Title}">
|
|
4
4
|
<content></content>
|
|
5
5
|
</Page>
|
|
6
6
|
</mvc:View>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<mvc:View xmlns:core="sap.ui.core" xmlns:mvc="sap.ui.core.mvc" xmlns="sap.m" xmlns:macros="sap.fe.macros"
|
|
2
2
|
xmlns:html="http://www.w3.org/1999/xhtml" controllerName="<%- ns %>.<%- name %>">
|
|
3
|
-
<Page id="<%-
|
|
3
|
+
<Page id="<%- ids.page %>" title="{i18n><%- name %>Title}">
|
|
4
4
|
<content></content>
|
|
5
5
|
</Page>
|
|
6
6
|
</mvc:View>
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
<core:FragmentDefinition xmlns="sap.m" xmlns:core="sap.ui.core" xmlns:macros="sap.fe.macros" xmlns:u="sap.ui.unified">
|
|
2
|
-
<macros:Table id="<%-
|
|
2
|
+
<macros:Table id="<%- ids.table %>" metaPath="@com.sap.vocabularies.UI.v1.LineItem" displayMode="true" />
|
|
3
3
|
</core:FragmentDefinition>
|