@sap-ux/fe-fpm-writer 1.2.1 → 1.3.1

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.
@@ -7,13 +7,13 @@ import { DOMParser, XMLSerializer } from '@xmldom/xmldom';
7
7
  import format from 'xml-formatter';
8
8
  import * as xpath from 'xpath';
9
9
  import { getMinimumUI5Version } from '@sap-ux/project-access';
10
- import { BuildingBlockType, PAGE_TEMPLATE_TYPE_FULL, bindingContextAbsolute } from './types.js';
10
+ import { BuildingBlockType, PageTemplateType, bindingContextAbsolute } from './types.js';
11
11
  import { getErrorMessage, validateBasePath, validateDependenciesLibs } from '../common/validate.js';
12
12
  import { getTemplatePath } from '../templates.js';
13
13
  import { CodeSnippetLanguage } from '../prompts/types.js';
14
14
  import { CONFIG, createIdGenerator, detectTabSpacing, extendJSON, getRelativeTemplateComponentPath } from '../common/file.js';
15
15
  import { getManifest, getManifestPath } from '../common/utils.js';
16
- import { getOrAddNamespace } from './prompts/utils/xml.js';
16
+ import { getDOMParserOptions, getOrAddNamespace, TEMPLATE_NAMESPACES } from './prompts/utils/xml.js';
17
17
  import { i18nNamespaces, translate } from '../i18n.js';
18
18
  import { processBuildingBlock, resolveAggregationPath } from './processor.js';
19
19
  import { appendPageAggregations, ensureMissingAggregation, getPageAggregationNames, getUI5XmlDocument, validateFullPageTemplateVersion } from './processAggregation.js';
@@ -30,7 +30,7 @@ const PLACEHOLDERS = {
30
30
  * @returns true if full Page template
31
31
  */
32
32
  function isFullPageTemplate(data) {
33
- return data.buildingBlockType === BuildingBlockType.Page && data.templateType === PAGE_TEMPLATE_TYPE_FULL;
33
+ return data.buildingBlockType === BuildingBlockType.Page && data.templateType === PageTemplateType.Full;
34
34
  }
35
35
  /**
36
36
  * Generates a building block into the provided xml view file.
@@ -225,13 +225,10 @@ export async function getManifestContent(fs, library = 'sap.fe.macros') {
225
225
  */
226
226
  function getTemplateDocument(buildingBlockData, viewDocument, fs, manifest, templateConfig) {
227
227
  const templateContent = getTemplateContent(buildingBlockData, viewDocument, manifest, fs, undefined, templateConfig);
228
- const errorHandler = (level, message) => {
229
- throw new Error(`Unable to parse template file with building block data. Details: [${level}] - ${message}`);
230
- };
231
228
  // Parse the rendered template content
232
229
  let templateDocument;
233
230
  try {
234
- templateDocument = new DOMParser({ errorHandler }).parseFromString(templateContent, 'text/xml');
231
+ templateDocument = new DOMParser(getDOMParserOptions(TEMPLATE_NAMESPACES)).parseFromString(templateContent, 'text/xml');
235
232
  }
236
233
  catch (error) {
237
234
  throw new Error(`Unable to parse template file with building block data. Details: ${getErrorMessage(error)}`);
@@ -331,12 +328,11 @@ export async function getSerializedFileContent(basePath, config, fs) {
331
328
  new DOMParser().parseFromString('<mvc:View xmlns:mvc="sap.ui.core.mvc" xmlns:macros="sap.fe.macros" xmlns="sap.m"/>', 'text/xml');
332
329
  // Parse content directly so documentElement IS the <macros:Page> element,
333
330
  // matching what appendPageAggregations expects as templateDocument.documentElement.
334
- const snippetErrorHandler = (level, message) => {
335
- throw new Error(`Unable to parse Page building block snippet. Details: [${level}] - ${message}`);
336
- };
337
331
  const snippetMacrosNS = getOrAddNamespace(nsDoc, 'sap.fe.macros', 'macros') || 'macros';
338
332
  const snippetContent = `${content}`.replace(new RegExp(`^<(${snippetMacrosNS}:Page)`), `<$1 xmlns:${snippetMacrosNS}="sap.fe.macros"`);
339
- const snippetDoc = new DOMParser({ errorHandler: snippetErrorHandler }).parseFromString(snippetContent, 'text/xml');
333
+ const snippetDoc = new DOMParser(getDOMParserOptions(TEMPLATE_NAMESPACES, (level, message) => {
334
+ throw new Error(`Unable to parse Page building block snippet. Details: [${level}] - ${message}`);
335
+ })).parseFromString(snippetContent, 'text/xml');
340
336
  appendPageAggregations(fs, nsDoc, snippetDoc, fnGenerateId, pageAggNames, isFullPageTemplate(buildingBlockData));
341
337
  const resultNode = snippetDoc.documentElement;
342
338
  viewOrFragmentContent = resultNode ? format(new XMLSerializer().serializeToString(resultNode)) : content;
@@ -7,12 +7,12 @@ import format from 'xml-formatter';
7
7
  import * as xpath from 'xpath';
8
8
  import { coerce, lt } from 'semver';
9
9
  import { getMinimumUI5Version } from '@sap-ux/project-access';
10
- import { BuildingBlockType, PAGE_AGGREGATIONS, PAGE_FULL_TEMPLATE_MIN_UI5_VERSION, PAGE_TEMPLATE_COMMENT, PAGE_TEMPLATE_TYPE_FULL } from './types.js';
10
+ import { BuildingBlockType, PAGE_AGGREGATIONS, MIN_UI5_VERSION_PAGE_BUILDING_BLOCK_FULL_LAYOUT, PAGE_TEMPLATE_COMMENT, PageTemplateType } from './types.js';
11
11
  import { getTemplatePath } from '../templates.js';
12
12
  import { createIdGenerator } from '../common/file.js';
13
13
  import { getErrorMessage } from '../common/validate.js';
14
14
  import { i18nNamespaces, translate } from '../i18n.js';
15
- import { getOrAddNamespace } from './prompts/utils/xml.js';
15
+ import { getDOMParserOptions, getOrAddNamespace, TEMPLATE_NAMESPACES } from './prompts/utils/xml.js';
16
16
  import { resolveAggregationPath } from './processor.js';
17
17
  /**
18
18
  * Returns the UI5 xml file document (view/fragment).
@@ -30,12 +30,9 @@ export function getUI5XmlDocument(basePath, viewPath, fs) {
30
30
  catch (error) {
31
31
  throw new Error(`Unable to read xml view file. Details: ${getErrorMessage(error)}`);
32
32
  }
33
- const errorHandler = (level, message) => {
34
- throw new Error(`Unable to parse xml view file. Details: [${level}] - ${message}`);
35
- };
36
33
  let viewDocument;
37
34
  try {
38
- viewDocument = new DOMParser({ errorHandler }).parseFromString(viewContent, 'text/xml');
35
+ viewDocument = new DOMParser(getDOMParserOptions()).parseFromString(viewContent, 'text/xml');
39
36
  }
40
37
  catch (error) {
41
38
  throw new Error(`Unable to parse xml view file. Details: ${getErrorMessage(error)}`);
@@ -53,7 +50,7 @@ export function getPageAggregationNames(data) {
53
50
  if (data.buildingBlockType !== BuildingBlockType.Page) {
54
51
  return undefined;
55
52
  }
56
- return data.templateType === PAGE_TEMPLATE_TYPE_FULL ? PAGE_AGGREGATIONS : undefined;
53
+ return data.templateType === PageTemplateType.Full ? PAGE_AGGREGATIONS : undefined;
57
54
  }
58
55
  /**
59
56
  * Throws if the manifest's minUI5Version is set and does not meet the 1.145.0 requirement for the full Page template.
@@ -63,7 +60,7 @@ export function getPageAggregationNames(data) {
63
60
  */
64
61
  export function validateFullPageTemplateVersion(manifest) {
65
62
  const minUI5Version = manifest ? coerce(getMinimumUI5Version(manifest)) : undefined;
66
- if (minUI5Version && lt(minUI5Version, PAGE_FULL_TEMPLATE_MIN_UI5_VERSION)) {
63
+ if (minUI5Version && lt(minUI5Version, MIN_UI5_VERSION_PAGE_BUILDING_BLOCK_FULL_LAYOUT)) {
67
64
  const t = translate(i18nNamespaces.buildingBlock, 'pageBuildingBlock.');
68
65
  throw new Error(`${t('fullTemplateMinUi5VersionRequirement', { minUI5Version: minUI5Version.version })}`);
69
66
  }
@@ -152,10 +149,9 @@ function buildPageAggregationFragment(fs, aggName, aggContext, fragMacrosNS, xml
152
149
  .map((a) => `${a.name}="${a.value}"`)
153
150
  .join(' ');
154
151
  const wrapped = `<root xmlns:${fragMacrosNS}="sap.fe.macros" xmlns="sap.m" xmlns:m="sap.m" ${extraNamespaces}>${aggContent}</root>`;
155
- const errorHandler = (level, message) => {
152
+ return new DOMParser(getDOMParserOptions(TEMPLATE_NAMESPACES, (level, message) => {
156
153
  throw new Error(`Unable to parse page aggregation fragment '${aggName}'. Details: [${level}] - ${message}`);
157
- };
158
- return new DOMParser({ errorHandler }).parseFromString(wrapped, 'text/xml');
154
+ })).parseFromString(wrapped, 'text/xml');
159
155
  }
160
156
  /**
161
157
  *
@@ -1,6 +1,6 @@
1
1
  import { i18nNamespaces, translate } from '../../../i18n.js';
2
2
  import { getBuildingBlockIdPrompt, getViewOrFragmentPathPrompt, getAggregationPathPrompt } from '../utils/index.js';
3
- import { BuildingBlockType, PAGE_FULL_TEMPLATE_MIN_UI5_VERSION, PAGE_TEMPLATE_TYPE_BASIC, PAGE_TEMPLATE_TYPE_FULL } from '../../types.js';
3
+ import { BuildingBlockType, MIN_UI5_VERSION_PAGE_BUILDING_BLOCK_FULL_LAYOUT, PageTemplateType } from '../../types.js';
4
4
  import { SapShortTextType, SapLongTextType } from '@sap-ux/i18n';
5
5
  import { getMinimumUI5Version } from '@sap-ux/project-access';
6
6
  import { coerce, lt } from 'semver';
@@ -15,7 +15,7 @@ export async function getPageBuildingBlockPrompts(context) {
15
15
  const t = translate(i18nNamespaces.buildingBlock, 'prompts.page.');
16
16
  const { content: manifest } = await getManifest(context.appPath, context.fs, false);
17
17
  const minUI5Version = manifest ? coerce(getMinimumUI5Version(manifest)) : undefined;
18
- const hideTemplateType = !!minUI5Version && lt(minUI5Version, PAGE_FULL_TEMPLATE_MIN_UI5_VERSION);
18
+ const hideTemplateType = !!minUI5Version && lt(minUI5Version, MIN_UI5_VERSION_PAGE_BUILDING_BLOCK_FULL_LAYOUT);
19
19
  return {
20
20
  questions: [
21
21
  ...(hideTemplateType
@@ -25,10 +25,10 @@ export async function getPageBuildingBlockPrompts(context) {
25
25
  type: 'list',
26
26
  name: 'buildingBlockData.templateType',
27
27
  message: t('templateType.message'),
28
- default: PAGE_TEMPLATE_TYPE_BASIC,
28
+ default: PageTemplateType.Basic,
29
29
  choices: [
30
- { value: PAGE_TEMPLATE_TYPE_BASIC, name: t('templateType.basic') },
31
- { value: PAGE_TEMPLATE_TYPE_FULL, name: t('templateType.full') }
30
+ { value: PageTemplateType.Basic, name: t('templateType.basic') },
31
+ { value: PageTemplateType.Full, name: t('templateType.full') }
32
32
  ],
33
33
  guiOptions: { mandatory: true }
34
34
  }
@@ -80,7 +80,7 @@ export async function getPageBuildingBlockPrompts(context) {
80
80
  initialAnswers: {
81
81
  buildingBlockData: {
82
82
  buildingBlockType: BuildingBlockType.Page,
83
- ...(hideTemplateType ? { templateType: PAGE_TEMPLATE_TYPE_BASIC } : {})
83
+ ...(hideTemplateType ? { templateType: PageTemplateType.Basic } : {})
84
84
  }
85
85
  }
86
86
  };
@@ -1,4 +1,19 @@
1
+ import { type Options } from '@xmldom/xmldom';
1
2
  import type { Editor } from 'mem-fs-editor';
3
+ /** Namespace map for `@xmldom/xmldom`: maps unprefixed elements to XHTML and `macros:*` to `sap.fe.macros`. */
4
+ export declare const TEMPLATE_NAMESPACES: Record<string, string>;
5
+ /** `Options` extended with `xmlns` and `onError` */
6
+ type DOMParserOptions = Options & {
7
+ xmlns?: Record<string, string | null | undefined>;
8
+ onError?: (level: string, message: string) => void;
9
+ };
10
+ /**
11
+ * Builds a `DOMParser` options object.
12
+ *
13
+ * @param xmlns - Fallback prefix→URI map (use `TEMPLATE_NAMESPACES` for templates and UI5 view/fragment files).
14
+ * @param onError - Parse error handler. Defaults to throwing. Pass `() => {}` for a silent partial DOM.
15
+ */
16
+ export declare function getDOMParserOptions(xmlns?: Record<string, string | null | undefined>, onError?: (level: string, message: string) => void): DOMParserOptions;
2
17
  /**
3
18
  * Converts the provided xpath string from `/mvc:View/Page/content` to
4
19
  * `/mvc:View/*[local-name()='Page']/*[local-name()='content']`.
@@ -45,4 +60,5 @@ export declare function getExistingButtonGroups(xmlFilePath: string, aggregation
45
60
  * @returns The prefix bound to the namespace URI ('' for default, or the prefix)
46
61
  */
47
62
  export declare function getOrAddNamespace(ui5XmlDocument: Document, namespaceUri?: string, prefix?: string): string;
63
+ export {};
48
64
  //# sourceMappingURL=xml.d.ts.map
@@ -1,6 +1,28 @@
1
1
  import { DOMParser } from '@xmldom/xmldom';
2
2
  import * as xpath from 'xpath';
3
3
  import { MACROS_NAMESPACE_URI } from '../../types.js';
4
+ /** Namespace map for `@xmldom/xmldom`: maps unprefixed elements to XHTML and `macros:*` to `sap.fe.macros`. */
5
+ export const TEMPLATE_NAMESPACES = {
6
+ '': 'http://www.w3.org/1999/xhtml',
7
+ 'macros': 'sap.fe.macros'
8
+ };
9
+ /**
10
+ * Builds a `DOMParser` options object.
11
+ *
12
+ * @param xmlns - Fallback prefix→URI map (use `TEMPLATE_NAMESPACES` for templates and UI5 view/fragment files).
13
+ * @param onError - Parse error handler. Defaults to throwing. Pass `() => {}` for a silent partial DOM.
14
+ */
15
+ export function getDOMParserOptions(xmlns, onError) {
16
+ const handler = onError ??
17
+ ((level, message) => {
18
+ throw new Error(`Unable to parse template file with building block data. Details: [${level}] - ${message}`);
19
+ });
20
+ return {
21
+ onError: handler,
22
+ errorHandler: handler,
23
+ xmlns
24
+ };
25
+ }
4
26
  /**
5
27
  * Converts the provided xpath string from `/mvc:View/Page/content` to
6
28
  * `/mvc:View/*[local-name()='Page']/*[local-name()='content']`.
@@ -50,10 +72,7 @@ export function getXPathStringsForXmlFile(xmlFilePath, fs) {
50
72
  let pageMacroDefinition;
51
73
  try {
52
74
  const xmlContent = fs.read(xmlFilePath);
53
- const errorHandler = (level, message) => {
54
- throw new Error(`Unable to parse the xml view file. Details: [${level}] - ${message}`);
55
- };
56
- const xmlDocument = new DOMParser({ errorHandler }).parseFromString(xmlContent, 'text/xml');
75
+ const xmlDocument = new DOMParser(getDOMParserOptions()).parseFromString(xmlContent, 'text/xml');
57
76
  const nodes = [{ parentNode: '', node: xmlDocument.firstChild }];
58
77
  // check macros namespace and page macro definition
59
78
  const macrosNamespace = getOrAddNamespace(xmlDocument);
@@ -109,10 +128,7 @@ export async function getFilterBarIdsInFile(viewOrFragmentPath, fs) {
109
128
  const ids = [];
110
129
  const buildingBlockSelector = 'macros:FilterBar';
111
130
  const xmlContent = fs.read(viewOrFragmentPath);
112
- const errorHandler = (level, message) => {
113
- throw new Error(`Unable to parse the xml view file. Details: [${level}] - ${message}`);
114
- };
115
- const xmlDocument = new DOMParser({ errorHandler }).parseFromString(xmlContent, 'text/xml');
131
+ const xmlDocument = new DOMParser(getDOMParserOptions()).parseFromString(xmlContent, 'text/xml');
116
132
  const elements = Array.from(xmlDocument.getElementsByTagName(buildingBlockSelector));
117
133
  for (const element of elements) {
118
134
  const id = element.getAttributeNode('id')?.value;
@@ -134,10 +150,7 @@ export async function getExistingButtonGroups(xmlFilePath, aggregationPath, fs)
134
150
  const existingButtonGroups = new Set();
135
151
  try {
136
152
  const xmlContent = fs.read(xmlFilePath);
137
- const errorHandler = (level, message) => {
138
- throw new Error(`Unable to parse the xml view file. Details: [${level}] - ${message}`);
139
- };
140
- const xmlDocument = new DOMParser({ errorHandler }).parseFromString(xmlContent, 'text/xml');
153
+ const xmlDocument = new DOMParser(getDOMParserOptions()).parseFromString(xmlContent, 'text/xml');
141
154
  // Get namespace map and create xpath selector
142
155
  const nsMap = xmlDocument.firstChild?._nsMap || {};
143
156
  const xpathSelect = xpath.useNamespaces(nsMap);
@@ -1,5 +1,14 @@
1
1
  import type { IdGeneratorFunction } from '../common/file.js';
2
2
  import type { CustomElement, CustomFragment, EventHandler, FragmentContentData, Position } from '../common/types.js';
3
+ export declare const PageTemplateType: {
4
+ readonly Full: "full";
5
+ readonly Basic: "basic";
6
+ };
7
+ export type PageTemplateType = (typeof PageTemplateType)[keyof typeof PageTemplateType];
8
+ /** Minimum UI5 version required for page building blocks. */
9
+ export declare const MIN_UI5_VERSION_PAGE_BUILDING_BLOCK: "1.136.0";
10
+ /** Minimum UI5 version required for full layout page building blocks. */
11
+ export declare const MIN_UI5_VERSION_PAGE_BUILDING_BLOCK_FULL_LAYOUT: "1.145.0";
3
12
  /**
4
13
  * Building block type.
5
14
  *
@@ -390,10 +399,6 @@ export interface Page extends BuildingBlock {
390
399
  */
391
400
  aggregations?: Partial<Record<PageAggregationName, string>>;
392
401
  }
393
- export declare const PAGE_TEMPLATE_TYPE_FULL: "full";
394
- export declare const PAGE_TEMPLATE_TYPE_BASIC: "basic";
395
- export type PageTemplateType = typeof PAGE_TEMPLATE_TYPE_FULL | typeof PAGE_TEMPLATE_TYPE_BASIC;
396
- export declare const PAGE_FULL_TEMPLATE_MIN_UI5_VERSION = "1.145.0";
397
402
  export declare const PAGE_TEMPLATE_COMMENT = "This is a sample template, event handlers should be added for implementation";
398
403
  export declare const MACROS_NAMESPACE_URI = "sap.fe.macros";
399
404
  /**
@@ -1,3 +1,11 @@
1
+ export const PageTemplateType = {
2
+ Full: 'full',
3
+ Basic: 'basic'
4
+ };
5
+ /** Minimum UI5 version required for page building blocks. */
6
+ export const MIN_UI5_VERSION_PAGE_BUILDING_BLOCK = '1.136.0';
7
+ /** Minimum UI5 version required for full layout page building blocks. */
8
+ export const MIN_UI5_VERSION_PAGE_BUILDING_BLOCK_FULL_LAYOUT = '1.145.0';
1
9
  /**
2
10
  * Building block type.
3
11
  *
@@ -37,9 +45,6 @@ export const PAGE_AGGREGATIONS = [
37
45
  'items',
38
46
  'footer'
39
47
  ];
40
- export const PAGE_TEMPLATE_TYPE_FULL = 'full';
41
- export const PAGE_TEMPLATE_TYPE_BASIC = 'basic';
42
- export const PAGE_FULL_TEMPLATE_MIN_UI5_VERSION = '1.145.0';
43
48
  export const PAGE_TEMPLATE_COMMENT = 'This is a sample template, event handlers should be added for implementation';
44
49
  export const MACROS_NAMESPACE_URI = 'sap.fe.macros';
45
50
  //# sourceMappingURL=types.js.map
@@ -5,6 +5,7 @@ import { getWebappPath } from '@sap-ux/project-access';
5
5
  import { getTemplatePath } from '../templates.js';
6
6
  import { copyTpl } from './file.js';
7
7
  import { DOMParser } from '@xmldom/xmldom';
8
+ import { getDOMParserOptions } from '../building-block/prompts/utils/xml.js';
8
9
  /**
9
10
  * Method inserts passed text into content by char index position.
10
11
  * In case if position is out of range, then whitespaces would be created.
@@ -104,7 +105,7 @@ export async function getManifest(basePath, fs, validate = true) {
104
105
  */
105
106
  export function isElementIdAvailable(fs, viewOrFragmentPath, id) {
106
107
  const xmlContent = fs.read(viewOrFragmentPath).toString();
107
- const xmlDocument = new DOMParser({ errorHandler: () => { } }).parseFromString(xmlContent, 'text/xml');
108
+ const xmlDocument = new DOMParser(getDOMParserOptions(undefined, () => { })).parseFromString(xmlContent, 'text/xml');
108
109
  return xmlDocument.documentElement ? !xmlDocument.getElementById(id) : true;
109
110
  }
110
111
  //# sourceMappingURL=utils.js.map
package/dist/index.d.ts CHANGED
@@ -21,9 +21,8 @@ export { enableFPM } from './app/index.js';
21
21
  export type { FPMConfig } from './app/index.js';
22
22
  export { validateBasePath, validateVersion } from './common/validate.js';
23
23
  export { createIdGenerator, type IdGeneratorFunction, getRelativeTemplateComponentPath } from './common/file.js';
24
- export { BuildingBlockType } from './building-block/types.js';
25
- export type { FilterBar, Form, Chart, Field, FieldFormatOptions, Table, BuildingBlockConfig, Page, CustomColumn, CustomFilterField, CustomFormField, RichTextEditor, ButtonGroupConfig, Action, PageTemplateType } from './building-block/types.js';
26
- export { PAGE_TEMPLATE_TYPE_FULL, PAGE_TEMPLATE_TYPE_BASIC, PAGE_AGGREGATIONS } from './building-block/types.js';
24
+ export { BuildingBlockType, PageTemplateType, PAGE_AGGREGATIONS, MIN_UI5_VERSION_PAGE_BUILDING_BLOCK, MIN_UI5_VERSION_PAGE_BUILDING_BLOCK_FULL_LAYOUT } from './building-block/types.js';
25
+ export type { FilterBar, Form, Chart, Field, FieldFormatOptions, Table, BuildingBlockConfig, Page, CustomColumn, CustomFilterField, CustomFormField, RichTextEditor, ButtonGroupConfig, Action } from './building-block/types.js';
27
26
  export type { PageAggregationName, GenerateBuildingBlockAggregationConfig } from './building-block/types.js';
28
27
  export { generateBuildingBlock, getSerializedFileContent, generateBuildingBlockAggregation } from './building-block/index.js';
29
28
  export type { ChartPromptsAnswer, FilterBarPromptsAnswer, FormPromptsAnswer, TablePromptsAnswer, PagePromptsAnswer, RichTextEditorPromptsAnswer, RichTextEditorButtonGroupsPromptsAnswer, BuildingBlockTypePromptsAnswer } from './building-block/prompts/questions/index.js';
package/dist/index.js CHANGED
@@ -12,8 +12,7 @@ export { generateCustomView } from './view/index.js';
12
12
  export { enableFPM } from './app/index.js';
13
13
  export { validateBasePath, validateVersion } from './common/validate.js';
14
14
  export { createIdGenerator, getRelativeTemplateComponentPath } from './common/file.js';
15
- export { BuildingBlockType } from './building-block/types.js';
16
- export { PAGE_TEMPLATE_TYPE_FULL, PAGE_TEMPLATE_TYPE_BASIC, PAGE_AGGREGATIONS } from './building-block/types.js';
15
+ export { BuildingBlockType, PageTemplateType, PAGE_AGGREGATIONS, MIN_UI5_VERSION_PAGE_BUILDING_BLOCK, MIN_UI5_VERSION_PAGE_BUILDING_BLOCK_FULL_LAYOUT } from './building-block/types.js';
17
16
  export { generateBuildingBlock, getSerializedFileContent, generateBuildingBlockAggregation } from './building-block/index.js';
18
17
  export { PromptsType, PromptsAPI } from './prompts/index.js';
19
18
  export { ControllerExtensionPageType } from './controller-extension/types.js';
@@ -11,7 +11,7 @@ import { coerce, gte, lt } from 'semver';
11
11
  import { addExtensionTypes, getManifestPath } from '../common/utils.js';
12
12
  import { copyTpl, extendJSON, createIdGenerator } from '../common/file.js';
13
13
  import { generateBuildingBlock } from '../building-block/index.js';
14
- import { BuildingBlockType } from '../building-block/types.js';
14
+ import { BuildingBlockType, PageTemplateType, MIN_UI5_VERSION_PAGE_BUILDING_BLOCK, MIN_UI5_VERSION_PAGE_BUILDING_BLOCK_FULL_LAYOUT } from '../building-block/types.js';
15
15
  import { augmentXpathWithLocalNames } from '../building-block/prompts/utils/index.js';
16
16
  import { i18nNamespaces, translate } from '../i18n.js';
17
17
  /**
@@ -70,9 +70,10 @@ export function getTemplateRoot(ui5Version) {
70
70
  * Handles the creation of a page building block for a custom page.
71
71
  *
72
72
  * @param {string} basePath - The base path of the UI5 application.
73
- * @param {{ pageBuildingBlockTitle: string; minUI5Version?: string }} data - Object containing the building block title and optional minimum UI5 version.
74
- * @param data.pageBuildingBlockTitle
75
- * @param data.minUI5Version
73
+ * @param data - Object containing the building block configuration.
74
+ * @param {string} data.pageBuildingBlockTitle - The title for the page building block.
75
+ * @param {PageTemplateType} [data.pageBuildingBlockTemplateType] - The layout template type ('full' or 'basic'). Defaults to 'basic' if the UI5 version does not meet the full layout requirement.
76
+ * @param {string} [data.minUI5Version] - The minimum UI5 version of the application. Used to validate layout compatibility.
76
77
  * @param {string} viewPath - The path to the view XML file.
77
78
  * @param {Editor} fs - The memfs editor instance.
78
79
  * @param {(baseId: string) => Promise<string>} generateId - Function to generate unique IDs for the building block elements.
@@ -82,10 +83,23 @@ export function getTemplateRoot(ui5Version) {
82
83
  async function handlePageBuildingBlock(basePath, data, viewPath, fs, generateId, log) {
83
84
  const minVersion = coerce(data.minUI5Version);
84
85
  const t = translate(i18nNamespaces.buildingBlock, 'pageBuildingBlock.');
85
- if (minVersion && lt(minVersion.version, '1.136.0')) {
86
- log?.warn(t('minUi5VersionRequirement', { minUI5Version: data.minUI5Version }));
86
+ if (minVersion && lt(minVersion.version, MIN_UI5_VERSION_PAGE_BUILDING_BLOCK)) {
87
+ log?.warn(t('minUi5VersionRequirement', {
88
+ minUI5Version: data.minUI5Version,
89
+ minUi5VersionForPageBuildingBlock: MIN_UI5_VERSION_PAGE_BUILDING_BLOCK
90
+ }));
87
91
  return;
88
92
  }
93
+ let templateType = data.pageBuildingBlockTemplateType;
94
+ if (templateType === PageTemplateType.Full &&
95
+ minVersion &&
96
+ lt(minVersion.version, MIN_UI5_VERSION_PAGE_BUILDING_BLOCK_FULL_LAYOUT)) {
97
+ log?.warn(t('minUi5VersionRequirementFullLayout', {
98
+ minUI5Version: data.minUI5Version,
99
+ minUi5VersionForFullLayout: MIN_UI5_VERSION_PAGE_BUILDING_BLOCK_FULL_LAYOUT
100
+ }));
101
+ templateType = PageTemplateType.Basic;
102
+ }
89
103
  const pageId = generateId('Page');
90
104
  await generateBuildingBlock(basePath, {
91
105
  viewOrFragmentPath: relative(basePath, viewPath),
@@ -95,7 +109,8 @@ async function handlePageBuildingBlock(basePath, data, viewPath, fs, generateId,
95
109
  id: pageId,
96
110
  buildingBlockType: BuildingBlockType.Page,
97
111
  generateId,
98
- title: data.pageBuildingBlockTitle
112
+ title: data.pageBuildingBlockTitle,
113
+ templateType: templateType
99
114
  }
100
115
  }, fs);
101
116
  }
@@ -142,7 +157,11 @@ export async function generate(basePath, data, fs, log) {
142
157
  }
143
158
  }
144
159
  if (data.pageBuildingBlockTitle) {
145
- await handlePageBuildingBlock(basePath, { pageBuildingBlockTitle: data.pageBuildingBlockTitle, minUI5Version: data.minUI5Version }, viewPath, fs, fnGenerateId, log);
160
+ await handlePageBuildingBlock(basePath, {
161
+ pageBuildingBlockTitle: data.pageBuildingBlockTitle,
162
+ pageBuildingBlockTemplateType: data.pageBuildingBlockTemplateType,
163
+ minUI5Version: data.minUI5Version
164
+ }, viewPath, fs, fnGenerateId, log);
146
165
  }
147
166
  const ext = data.typescript ? 'ts' : 'js';
148
167
  const controllerPath = join(config.path, `${config.name}.controller.${ext}`);
@@ -1,4 +1,5 @@
1
1
  import type { CustomElement, InternalCustomElement, WriterConfig } from '../common/types.js';
2
+ import type { PageTemplateType } from '../building-block/types.js';
2
3
  /**
3
4
  * Incoming navigation configuration.
4
5
  */
@@ -111,6 +112,10 @@ export interface CustomPage extends FpmPage, CustomElement {
111
112
  * If page building block title is defined then it will be used to automatically add a macro page to the generated custom page.
112
113
  */
113
114
  pageBuildingBlockTitle?: string;
115
+ /**
116
+ * The template type for the page building block. 'full' generates an enhanced layout with all aggregations; 'basic' is the default minimal tag.
117
+ */
118
+ pageBuildingBlockTemplateType?: PageTemplateType;
114
119
  }
115
120
  export interface FCL {
116
121
  /**
@@ -277,7 +277,7 @@ declare const ns1: {
277
277
  };
278
278
  pageBuildingBlock: {
279
279
  minUi5VersionRequirement: string;
280
- fullTemplateMinUi5VersionRequirement: string;
280
+ minUi5VersionRequirementFullLayout: string;
281
281
  };
282
282
  richTextEditorBuildingBlock: {
283
283
  minUi5VersionRequirement: string;
@@ -305,8 +305,8 @@ const ns1 = {
305
305
  }
306
306
  },
307
307
  'pageBuildingBlock': {
308
- 'minUi5VersionRequirement': 'The Page building block feature requires SAPUI5 1.136.0 or higher. The current version is {{ minUI5Version }}, so the Page building block will not be added.',
309
- 'fullTemplateMinUi5VersionRequirement': 'The full Page building block feature requires SAPUI5 1.145.0 or higher. The current version is {{ minUI5Version }}, so the full template will not be added.'
308
+ 'minUi5VersionRequirement': 'The Page building block feature requires SAPUI5 {{ minUi5VersionForPageBuildingBlock }} or higher. The current version is {{ minUI5Version }}, so the Page building block will not be added.',
309
+ 'minUi5VersionRequirementFullLayout': 'Full Layout requires SAPUI5 {{ minUi5VersionForFullLayout }} or higher. The current version is {{ minUI5Version }}, so Basic Layout will be applied instead.'
310
310
  },
311
311
  'richTextEditorBuildingBlock': {
312
312
  'minUi5VersionRequirement': 'The Rich Text Editor building block feature requires SAPUI5 1.117.0 or higher. The current version is {{ minUI5Version }}, so the Rich Text Editor building block will not be added.'
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": "1.2.1",
4
+ "version": "1.3.1",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "https://github.com/SAP/open-ux-tools.git",
@@ -33,8 +33,8 @@
33
33
  "xpath": "0.0.34",
34
34
  "@sap-ux/fiori-annotation-api": "1.0.15",
35
35
  "@sap-ux/i18n": "1.0.2",
36
- "@sap-ux/logger": "1.0.3",
37
- "@sap-ux/project-access": "2.1.6"
36
+ "@sap-ux/project-access": "2.1.6",
37
+ "@sap-ux/logger": "1.0.3"
38
38
  },
39
39
  "devDependencies": {
40
40
  "@jest/globals": "30.4.1",
@@ -44,7 +44,7 @@
44
44
  "@types/mem-fs-editor": "7.0.1",
45
45
  "@types/semver": "7.7.1",
46
46
  "@types/vinyl": "2.0.12",
47
- "@sap-ux/ui-prompting": "1.0.7"
47
+ "@sap-ux/ui-prompting": "1.0.8"
48
48
  },
49
49
  "engines": {
50
50
  "node": ">=22.x"