@sap-ux/fe-fpm-writer 0.43.11 → 0.43.13

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.
@@ -75,7 +75,7 @@ async function generateBuildingBlock(basePath, config, fs) {
75
75
  // Validate the base and view paths
76
76
  fs ??= (0, mem_fs_editor_1.create)((0, mem_fs_1.create)());
77
77
  await (0, validate_1.validateBasePath)(basePath, fs, []);
78
- const fnGenerateId = config.buildingBlockData.generateId ?? (await (0, file_1.createIdGenerator)(basePath, fs));
78
+ const fnGenerateId = config.buildingBlockData.generateId ?? (await (0, file_1.createIdGenerator)({ basePath, fsEditor: fs }));
79
79
  if (!fs.exists((0, node_path_1.join)(basePath, viewOrFragmentPath))) {
80
80
  throw new Error(`Invalid view path ${viewOrFragmentPath}.`);
81
81
  }
@@ -71,7 +71,7 @@ async function generateCustomColumn(basePath, customColumn, fs) {
71
71
  (0, validate_1.validateVersion)(customColumn.minUI5Version);
72
72
  fs ??= (0, mem_fs_editor_1.create)((0, mem_fs_1.create)());
73
73
  await (0, validate_1.validateBasePath)(basePath, fs);
74
- const fnGenerateId = await (0, file_1.createIdGenerator)(basePath, fs);
74
+ const fnGenerateId = await (0, file_1.createIdGenerator)({ basePath, fsEditor: fs });
75
75
  const { path: manifestPath, content: manifest } = await (0, utils_1.getManifest)(basePath, fs);
76
76
  // merge with defaults
77
77
  const completeColumn = enhanceConfig(fs, customColumn, manifestPath, manifest, fnGenerateId);
@@ -1,5 +1,43 @@
1
1
  import type { CopyOptions, Editor } from 'mem-fs-editor';
2
2
  import type { TabInfo } from '../common/types';
3
+ /**
4
+ * Options for creating an ID generator with cached file contents.
5
+ */
6
+ export interface CreateIdGeneratorWithCacheOptions {
7
+ basePath?: never;
8
+ fsEditor?: never;
9
+ /**
10
+ *
11
+ * cache for file contents to optimize ID generation.
12
+ */
13
+ fileContentCache: string[];
14
+ }
15
+ /**
16
+ * Options for creating an ID generator with filesystem access.
17
+ */
18
+ export interface CreateIdGeneratorWithFilesystemOptions {
19
+ /**
20
+ * Base path of the project.
21
+ */
22
+ basePath: string;
23
+ /**
24
+ * mem-fs-editor instance.
25
+ */
26
+ fsEditor: Editor;
27
+ fileContentCache?: string[];
28
+ }
29
+ /**
30
+ * Options for creating an empty ID generator (no validation).
31
+ */
32
+ export interface CreateIdGeneratorEmptyOptions {
33
+ basePath?: never;
34
+ fsEditor?: never;
35
+ fileContentCache?: never;
36
+ }
37
+ /**
38
+ * Options for creating an ID generator.
39
+ */
40
+ export type CreateIdGeneratorOptions = CreateIdGeneratorWithCacheOptions | CreateIdGeneratorWithFilesystemOptions | CreateIdGeneratorEmptyOptions;
3
41
  interface ButtonGroup {
4
42
  id?: string;
5
43
  }
@@ -65,11 +103,10 @@ export declare function getFragmentAndViewFiles(appPath: string, fs: Editor): Pr
65
103
  * Creates an ID generator function for a given base path and editor.
66
104
  * The generator ensures unique IDs across all fragment and view files in the project.
67
105
  *
68
- * @param basePath - Base path of the project
69
- * @param fsEditor - mem-fs-editor instance
70
- * @returns A function that generates unique IDs based on a base ID string
106
+ * @param options - Options for creating the ID generator
107
+ * @returns A function that generates unique IDs based on a base ID string, or a Promise resolving to such function
71
108
  */
72
- export declare function createIdGenerator(basePath: string | undefined, fsEditor: Editor): Promise<(baseId: string) => string>;
109
+ export declare function createIdGenerator(options?: CreateIdGeneratorOptions): Promise<IdGeneratorFunction> | IdGeneratorFunction;
73
110
  export declare const COPY_TEMPLATE_OPTIONS: CopyOptions & {
74
111
  noGlob: boolean;
75
112
  };
@@ -10,7 +10,7 @@ exports.copyTpl = copyTpl;
10
10
  exports.getRelativeTemplateComponentPath = getRelativeTemplateComponentPath;
11
11
  const node_path_1 = require("node:path");
12
12
  const file_1 = require("@sap-ux/project-access/dist/file");
13
- const utils_1 = require("./utils");
13
+ const xmldom_1 = require("@xmldom/xmldom");
14
14
  const CHAR_SPACE = ' ';
15
15
  const CHAR_TAB = '\t';
16
16
  exports.CONFIG = {
@@ -105,20 +105,24 @@ exports.CONFIG = {
105
105
  * Generates a unique element ID that is not already used in any view or fragment file.
106
106
  * Uses an incremental counter for predictable, readable IDs.
107
107
  *
108
- * @param fs - The file system object for reading files
109
108
  * @param baseId - The base name for the ID (e.g., 'filterBar', 'chart')
110
- * @param filteredFiles - The list of files to check for ID availability
109
+ * @param filteredFilesContent - The list of files to check for ID availability
111
110
  * @param validatedIds - A list of IDs that have already been validated in the current session to avoid duplicates
112
111
  * @returns A unique ID that is available across all view and fragment files
113
112
  */
114
- function generateUniqueElementId(fs, baseId, filteredFiles, validatedIds = []) {
113
+ function generateUniqueElementId(baseId, filteredFilesContent, validatedIds = []) {
115
114
  const maxAttempts = 1000;
116
- if (filteredFiles.every((file) => (0, utils_1.isElementIdAvailable)(fs, file, baseId)) && !validatedIds.includes(baseId)) {
115
+ function checkElementIdAvailable(id, xmlContent) {
116
+ const xmlDocument = new xmldom_1.DOMParser({ errorHandler: () => { } }).parseFromString(xmlContent);
117
+ return xmlDocument.documentElement ? !xmlDocument.getElementById(id) : true;
118
+ }
119
+ if (filteredFilesContent.every((content) => content === '' || checkElementIdAvailable(baseId, content)) &&
120
+ !validatedIds.includes(baseId)) {
117
121
  return baseId;
118
122
  }
119
123
  for (let counter = 1; counter < maxAttempts; counter++) {
120
124
  const candidateId = `${baseId}${counter}`;
121
- if (filteredFiles.every((file) => (0, utils_1.isElementIdAvailable)(fs, file, candidateId)) &&
125
+ if (filteredFilesContent.every((content) => content === '' || checkElementIdAvailable(candidateId, content)) &&
122
126
  !validatedIds.includes(candidateId)) {
123
127
  return candidateId;
124
128
  }
@@ -142,18 +146,26 @@ async function getFragmentAndViewFiles(appPath, fs) {
142
146
  * Creates an ID generator function for a given base path and editor.
143
147
  * The generator ensures unique IDs across all fragment and view files in the project.
144
148
  *
145
- * @param basePath - Base path of the project
146
- * @param fsEditor - mem-fs-editor instance
147
- * @returns A function that generates unique IDs based on a base ID string
149
+ * @param options - Options for creating the ID generator
150
+ * @returns A function that generates unique IDs based on a base ID string, or a Promise resolving to such function
148
151
  */
149
- async function createIdGenerator(basePath, fsEditor) {
150
- let files = [];
151
- if (basePath) {
152
- files = await getFragmentAndViewFiles(basePath, fsEditor);
153
- }
154
- return (baseId, validatedIds = []) => {
155
- return generateUniqueElementId(fsEditor, baseId, files, validatedIds);
152
+ function createIdGenerator(options = {}) {
153
+ const { basePath, fsEditor, fileContentCache = [] } = options;
154
+ const createGenerator = (fileContents) => {
155
+ return (baseId, validatedIds = []) => {
156
+ return generateUniqueElementId(baseId, fileContents, validatedIds);
157
+ };
156
158
  };
159
+ if (fileContentCache.length > 0) {
160
+ return createGenerator(fileContentCache);
161
+ }
162
+ if (fsEditor && basePath) {
163
+ return getFragmentAndViewFiles(basePath, fsEditor).then((filePaths) => {
164
+ const fileContents = filePaths.map((path) => fsEditor.read(path).toString());
165
+ return createGenerator(fileContents);
166
+ });
167
+ }
168
+ return createGenerator([]);
157
169
  }
158
170
  // `noGlob` is supported in `mem-fs-editor` v9,
159
171
  // but is missing from `@types/mem-fs-editor` (no v9 typings), so we extend the type here.
@@ -53,7 +53,7 @@ async function generateCustomField(basePath, customField, fs) {
53
53
  (0, validate_1.validateVersion)(customField.minUI5Version);
54
54
  fs ??= (0, mem_fs_editor_1.create)((0, mem_fs_1.create)());
55
55
  await (0, validate_1.validateBasePath)(basePath, fs);
56
- const generateId = await (0, file_1.createIdGenerator)(basePath, fs);
56
+ const generateId = await (0, file_1.createIdGenerator)({ basePath, fsEditor: fs });
57
57
  const { path: manifestPath, content: manifest } = await (0, utils_1.getManifest)(basePath, fs);
58
58
  // merge with defaults
59
59
  const completeField = enhanceConfig(fs, customField, manifestPath, manifest, generateId);
@@ -48,7 +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
+ const fnGenerateId = await (0, file_1.createIdGenerator)({ basePath, fsEditor: fs });
52
52
  const { path: manifestPath, content: manifest } = await (0, utils_1.getManifest)(basePath, fs);
53
53
  const config = enhanceConfig(filterConfig, manifestPath, manifest);
54
54
  // Apply event handler
package/dist/index.d.ts CHANGED
@@ -14,6 +14,7 @@ export { CustomView } from './view/types';
14
14
  export { generateCustomView } from './view';
15
15
  export { enableFPM, FPMConfig } from './app';
16
16
  export { validateBasePath, validateVersion } from './common/validate';
17
+ export { createIdGenerator, type IdGeneratorFunction, getRelativeTemplateComponentPath } from './common/file';
17
18
  export { BuildingBlockType, FilterBar, Form, Chart, Field, FieldFormatOptions, Table, BuildingBlockConfig, Page, CustomColumn, CustomFilterField, RichTextEditor, ButtonGroupConfig, Action } from './building-block/types';
18
19
  export { generateBuildingBlock, getSerializedFileContent } from './building-block';
19
20
  export { ChartPromptsAnswer, FilterBarPromptsAnswer, FormPromptsAnswer, TablePromptsAnswer, PagePromptsAnswer, RichTextEditorPromptsAnswer, RichTextEditorButtonGroupsPromptsAnswer, BuildingBlockTypePromptsAnswer } from './building-block/prompts/questions';
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.initI18n = exports.generateCustomField = exports.generateControllerExtension = exports.ControllerExtensionPageType = exports.PromptsAPI = exports.PromptsType = exports.getSerializedFileContent = exports.generateBuildingBlock = exports.BuildingBlockType = exports.validateVersion = exports.validateBasePath = exports.enableFPM = exports.generateCustomView = exports.generateCustomFilter = exports.generateCustomHeaderSection = exports.generateCustomSubSection = exports.generateCustomSection = exports.DesignTime = exports.RequestGroupId = exports.generateCustomColumn = exports.generateActionMenu = exports.ActionMenuTargetControl = exports.generateCustomAction = exports.TargetControl = exports.generateListReport = exports.generateObjectPage = exports.generateCustomPage = void 0;
3
+ exports.initI18n = exports.generateCustomField = exports.generateControllerExtension = exports.ControllerExtensionPageType = exports.PromptsAPI = exports.PromptsType = exports.getSerializedFileContent = exports.generateBuildingBlock = exports.BuildingBlockType = exports.getRelativeTemplateComponentPath = exports.createIdGenerator = exports.validateVersion = exports.validateBasePath = exports.enableFPM = exports.generateCustomView = exports.generateCustomFilter = exports.generateCustomHeaderSection = exports.generateCustomSubSection = exports.generateCustomSection = exports.DesignTime = exports.RequestGroupId = exports.generateCustomColumn = exports.generateActionMenu = exports.ActionMenuTargetControl = exports.generateCustomAction = exports.TargetControl = exports.generateListReport = exports.generateObjectPage = exports.generateCustomPage = void 0;
4
4
  var page_1 = require("./page");
5
5
  Object.defineProperty(exports, "generateCustomPage", { enumerable: true, get: function () { return page_1.generateCustomPage; } });
6
6
  Object.defineProperty(exports, "generateObjectPage", { enumerable: true, get: function () { return page_1.generateObjectPage; } });
@@ -31,6 +31,9 @@ Object.defineProperty(exports, "enableFPM", { enumerable: true, get: function ()
31
31
  var validate_1 = require("./common/validate");
32
32
  Object.defineProperty(exports, "validateBasePath", { enumerable: true, get: function () { return validate_1.validateBasePath; } });
33
33
  Object.defineProperty(exports, "validateVersion", { enumerable: true, get: function () { return validate_1.validateVersion; } });
34
+ var file_1 = require("./common/file");
35
+ Object.defineProperty(exports, "createIdGenerator", { enumerable: true, get: function () { return file_1.createIdGenerator; } });
36
+ Object.defineProperty(exports, "getRelativeTemplateComponentPath", { enumerable: true, get: function () { return file_1.getRelativeTemplateComponentPath; } });
34
37
  var types_4 = require("./building-block/types");
35
38
  Object.defineProperty(exports, "BuildingBlockType", { enumerable: true, get: function () { return types_4.BuildingBlockType; } });
36
39
  var building_block_1 = require("./building-block");
@@ -118,7 +118,7 @@ async function generate(basePath, data, fs, log) {
118
118
  (0, validate_1.validateVersion)(data.minUI5Version);
119
119
  await (0, common_1.validatePageConfig)(basePath, data, fs, []);
120
120
  const manifestPath = await (0, utils_1.getManifestPath)(basePath, fs);
121
- const fnGenerateId = await (0, file_1.createIdGenerator)(basePath, fs);
121
+ const fnGenerateId = await (0, file_1.createIdGenerator)({ basePath, fsEditor: fs });
122
122
  const config = enhanceData(data, manifestPath, fs);
123
123
  // merge content into existing files
124
124
  const root = getTemplateRoot(data.minUI5Version);
@@ -85,7 +85,7 @@ async function generate(basePath, customSection, manifestTemplateRoot, fs) {
85
85
  (0, validate_1.validateVersion)(customSection.minUI5Version);
86
86
  fs ??= (0, mem_fs_editor_1.create)((0, mem_fs_1.create)());
87
87
  await (0, validate_1.validateBasePath)(basePath, fs);
88
- const fnGenerateId = await (0, file_1.createIdGenerator)(basePath, fs);
88
+ const fnGenerateId = await (0, file_1.createIdGenerator)({ basePath, fsEditor: fs });
89
89
  const { path: manifestPath, content: manifest } = await (0, utils_1.getManifest)(basePath, fs);
90
90
  // merge with defaults
91
91
  const completeSection = enhanceConfig(fs, customSection, manifestPath, manifest, fnGenerateId);
@@ -113,7 +113,7 @@ async function generate(basePath, customSection, manifestTemplateRoot, fs) {
113
113
  */
114
114
  async function generateCustomHeaderSection(basePath, customHeaderSection, fs) {
115
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
116
+ const fnGenerateId = await (0, file_1.createIdGenerator)({ basePath, fsEditor }); // initialize ID generator to ensure unique IDs across both section and edit fragment
117
117
  const manifestRoot = getManifestRoot('header-section', customHeaderSection.minUI5Version);
118
118
  const minVersion = (0, semver_1.coerce)(customHeaderSection.minUI5Version);
119
119
  let editSection;
@@ -85,7 +85,7 @@ async function generateCustomView(basePath, customView, fs) {
85
85
  (0, validate_1.validateVersion)(customView.minUI5Version);
86
86
  fs ??= (0, mem_fs_editor_1.create)((0, mem_fs_1.create)());
87
87
  await (0, validate_1.validateBasePath)(basePath, fs);
88
- const fnGenerateId = await (0, file_1.createIdGenerator)(basePath, fs);
88
+ const fnGenerateId = await (0, file_1.createIdGenerator)({ basePath, fsEditor: fs });
89
89
  const { path: manifestPath, content: manifest } = await (0, utils_1.getManifest)(basePath, fs);
90
90
  // merge with defaults
91
91
  const completeView = enhanceConfig(fs, customView, manifestPath, manifest, fnGenerateId);
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.43.11",
4
+ "version": "0.43.13",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "https://github.com/SAP/open-ux-tools.git",
@@ -30,9 +30,9 @@
30
30
  "semver": "7.7.4",
31
31
  "xml-formatter": "2.6.1",
32
32
  "xpath": "0.0.33",
33
- "@sap-ux/fiori-annotation-api": "0.9.34",
33
+ "@sap-ux/fiori-annotation-api": "0.9.35",
34
34
  "@sap-ux/i18n": "0.3.9",
35
- "@sap-ux/project-access": "1.35.13",
35
+ "@sap-ux/project-access": "1.35.14",
36
36
  "@sap-ux/logger": "0.8.2"
37
37
  },
38
38
  "devDependencies": {