@sap-ux/fe-fpm-writer 1.3.0 → 1.3.2

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.
@@ -13,7 +13,7 @@ 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';
@@ -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;
@@ -12,7 +12,7 @@ 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)}`);
@@ -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,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,27 @@
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
+ xmlns
23
+ };
24
+ }
4
25
  /**
5
26
  * Converts the provided xpath string from `/mvc:View/Page/content` to
6
27
  * `/mvc:View/*[local-name()='Page']/*[local-name()='content']`.
@@ -50,10 +71,7 @@ export function getXPathStringsForXmlFile(xmlFilePath, fs) {
50
71
  let pageMacroDefinition;
51
72
  try {
52
73
  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');
74
+ const xmlDocument = new DOMParser(getDOMParserOptions()).parseFromString(xmlContent, 'text/xml');
57
75
  const nodes = [{ parentNode: '', node: xmlDocument.firstChild }];
58
76
  // check macros namespace and page macro definition
59
77
  const macrosNamespace = getOrAddNamespace(xmlDocument);
@@ -109,10 +127,7 @@ export async function getFilterBarIdsInFile(viewOrFragmentPath, fs) {
109
127
  const ids = [];
110
128
  const buildingBlockSelector = 'macros:FilterBar';
111
129
  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');
130
+ const xmlDocument = new DOMParser(getDOMParserOptions()).parseFromString(xmlContent, 'text/xml');
116
131
  const elements = Array.from(xmlDocument.getElementsByTagName(buildingBlockSelector));
117
132
  for (const element of elements) {
118
133
  const id = element.getAttributeNode('id')?.value;
@@ -134,10 +149,7 @@ export async function getExistingButtonGroups(xmlFilePath, aggregationPath, fs)
134
149
  const existingButtonGroups = new Set();
135
150
  try {
136
151
  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');
152
+ const xmlDocument = new DOMParser(getDOMParserOptions()).parseFromString(xmlContent, 'text/xml');
141
153
  // Get namespace map and create xpath selector
142
154
  const nsMap = xmlDocument.firstChild?._nsMap || {};
143
155
  const xpathSelect = xpath.useNamespaces(nsMap);
@@ -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/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.3.0",
4
+ "version": "1.3.2",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "https://github.com/SAP/open-ux-tools.git",
@@ -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"