@salesforce/metadata-plugins 1.0.0
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/LICENSE.txt +27 -0
- package/README.md +121 -0
- package/dist/index.d.ts +25 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +45 -0
- package/dist/plugins/flexipage/FlexipageParser.d.ts +39 -0
- package/dist/plugins/flexipage/FlexipageParser.d.ts.map +1 -0
- package/dist/plugins/flexipage/FlexipageParser.js +124 -0
- package/dist/plugins/flexipage/FlexipageVisualizer.d.ts +19 -0
- package/dist/plugins/flexipage/FlexipageVisualizer.d.ts.map +1 -0
- package/dist/plugins/flexipage/FlexipageVisualizer.js +27 -0
- package/dist/plugins/flexipage/transformer/FlexipageToUemTransformer.d.ts +55 -0
- package/dist/plugins/flexipage/transformer/FlexipageToUemTransformer.d.ts.map +1 -0
- package/dist/plugins/flexipage/transformer/FlexipageToUemTransformer.js +302 -0
- package/dist/plugins/flexipage/transformer/FlexipageTransformerUtils.d.ts +34 -0
- package/dist/plugins/flexipage/transformer/FlexipageTransformerUtils.d.ts.map +1 -0
- package/dist/plugins/flexipage/transformer/FlexipageTransformerUtils.js +73 -0
- package/dist/plugins/flexipage/types/FlexipageMetadata.d.ts +76 -0
- package/dist/plugins/flexipage/types/FlexipageMetadata.d.ts.map +1 -0
- package/dist/plugins/flexipage/types/FlexipageMetadata.js +7 -0
- package/dist/plugins/flexipage/ui/assets/index-DUDC29Wu.js +55 -0
- package/dist/plugins/flexipage/ui/assets/index-U3SB2gLS.css +1 -0
- package/dist/plugins/flexipage/ui/index.html +16 -0
- package/dist/plugins/schema/SchemaParser.d.ts +80 -0
- package/dist/plugins/schema/SchemaParser.d.ts.map +1 -0
- package/dist/plugins/schema/SchemaParser.js +474 -0
- package/dist/plugins/schema/SchemaVisualizer.d.ts +42 -0
- package/dist/plugins/schema/SchemaVisualizer.d.ts.map +1 -0
- package/dist/plugins/schema/SchemaVisualizer.js +46 -0
- package/dist/plugins/schema/types/SchemaERDData.d.ts +91 -0
- package/dist/plugins/schema/types/SchemaERDData.d.ts.map +1 -0
- package/dist/plugins/schema/types/SchemaERDData.js +7 -0
- package/dist/plugins/schema/ui/assets/index-BbY653nW.js +62 -0
- package/dist/plugins/schema/ui/assets/index-CbNHuvPl.css +1 -0
- package/dist/plugins/schema/ui/index.html +14 -0
- package/dist/shared/uem/transformer/ITransformer.d.ts +58 -0
- package/dist/shared/uem/transformer/ITransformer.d.ts.map +1 -0
- package/dist/shared/uem/transformer/ITransformer.js +7 -0
- package/dist/shared/uem/transformer/TransformerRegistry.d.ts +54 -0
- package/dist/shared/uem/transformer/TransformerRegistry.d.ts.map +1 -0
- package/dist/shared/uem/transformer/TransformerRegistry.js +74 -0
- package/dist/shared/uem/transformer/UemBasedVisualizer.d.ts +43 -0
- package/dist/shared/uem/transformer/UemBasedVisualizer.d.ts.map +1 -0
- package/dist/shared/uem/transformer/UemBasedVisualizer.js +62 -0
- package/dist/shared/uem/types/UemMetadata.d.ts +84 -0
- package/dist/shared/uem/types/UemMetadata.d.ts.map +1 -0
- package/dist/shared/uem/types/UemMetadata.js +7 -0
- package/dist/shared/utils/uemTreeUtils.d.ts +51 -0
- package/dist/shared/utils/uemTreeUtils.d.ts.map +1 -0
- package/dist/shared/utils/uemTreeUtils.js +89 -0
- package/dist/shared/utils/vizDependentPathsHelper.d.ts +53 -0
- package/dist/shared/utils/vizDependentPathsHelper.d.ts.map +1 -0
- package/dist/shared/utils/vizDependentPathsHelper.js +108 -0
- package/package.json +111 -0
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025, Salesforce, Inc.
|
|
3
|
+
* All rights reserved.
|
|
4
|
+
* Licensed under the BSD 3-Clause license.
|
|
5
|
+
* For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
|
|
6
|
+
**/
|
|
7
|
+
import { SalesforceXMLParser, ErrorBuilder, ErrorCategory } from '@salesforce/metadata-core-sdk';
|
|
8
|
+
import { transformVisibilityRule, resolvePropertyValue, buildFacetIndex, denormalizeFqn, ensureArray, extractApiName, } from './FlexipageTransformerUtils.js';
|
|
9
|
+
/**
|
|
10
|
+
* Comprehensive FlexiPage to UEM transformer that correctly handles:
|
|
11
|
+
* - Facet regions (nested components via property references)
|
|
12
|
+
* - Visibility rules on components and fields
|
|
13
|
+
* - Field instance properties
|
|
14
|
+
* - ComponentInstance vs FieldInstance distinction
|
|
15
|
+
*/
|
|
16
|
+
export class FlexipageToUemTransformer {
|
|
17
|
+
constructor() {
|
|
18
|
+
this.warnings = [];
|
|
19
|
+
this.blockCount = 0;
|
|
20
|
+
// Configure parser to always treat these elements as arrays for consistency
|
|
21
|
+
const alwaysArray = new Set([
|
|
22
|
+
'flexiPageRegions',
|
|
23
|
+
'itemInstances',
|
|
24
|
+
'componentInstanceProperties',
|
|
25
|
+
'fieldInstanceProperties',
|
|
26
|
+
'criteria',
|
|
27
|
+
'valueListItems',
|
|
28
|
+
'properties',
|
|
29
|
+
]);
|
|
30
|
+
this.parser = SalesforceXMLParser.createCustomParser({
|
|
31
|
+
ignoreAttributes: false,
|
|
32
|
+
attributeNamePrefix: '',
|
|
33
|
+
isArray: (name) => alwaysArray.has(name),
|
|
34
|
+
parseAttributeValue: false, // Keep as strings for consistent handling
|
|
35
|
+
trimValues: true,
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Transform Flexipage XML content to UEM fragment
|
|
40
|
+
*
|
|
41
|
+
* @param xmlContent - The Flexipage XML content as a string
|
|
42
|
+
* @param filePath - The file path of the source file (for extracting API name)
|
|
43
|
+
* @returns Promise resolving to the UEM fragment and transformation metadata
|
|
44
|
+
*/
|
|
45
|
+
async transform(xmlContent, filePath) {
|
|
46
|
+
this.warnings = [];
|
|
47
|
+
this.blockCount = 0;
|
|
48
|
+
let parsedFlexipage;
|
|
49
|
+
try {
|
|
50
|
+
parsedFlexipage = this.parser.parse(xmlContent);
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
const pluginError = ErrorBuilder.fromError(ErrorCategory.Parsing, error, {
|
|
54
|
+
filePath,
|
|
55
|
+
}).build();
|
|
56
|
+
throw pluginError;
|
|
57
|
+
}
|
|
58
|
+
if (!parsedFlexipage) {
|
|
59
|
+
const pluginError = ErrorBuilder.fromError(ErrorCategory.Parsing, new Error('Failed to parse FlexiPage XML: parsed result is null'), { filePath }).build();
|
|
60
|
+
throw pluginError;
|
|
61
|
+
}
|
|
62
|
+
const flexiPage = parsedFlexipage.FlexiPage;
|
|
63
|
+
if (!flexiPage || !flexiPage.template || !flexiPage.type) {
|
|
64
|
+
const pluginError = ErrorBuilder.fromError(ErrorCategory.Parsing, new Error('Invalid FlexiPage XML: missing required elements'), { filePath }).build();
|
|
65
|
+
throw pluginError;
|
|
66
|
+
}
|
|
67
|
+
try {
|
|
68
|
+
// Extract API name from file path
|
|
69
|
+
const apiName = extractApiName(filePath);
|
|
70
|
+
// Build index of Facet regions for property-based embedding
|
|
71
|
+
const allRegions = flexiPage.flexiPageRegions ? ensureArray(flexiPage.flexiPageRegions) : [];
|
|
72
|
+
const facetByName = buildFacetIndex(allRegions);
|
|
73
|
+
const embeddedFacets = new Set();
|
|
74
|
+
// Transform root template to UEM block
|
|
75
|
+
const fragment = this.transformTemplate(flexiPage.template, flexiPage, apiName);
|
|
76
|
+
// Process only top-level "Region" type regions as direct children
|
|
77
|
+
const topLevelRegions = allRegions.filter((r) => !r.type || r.type.toLowerCase() === 'region');
|
|
78
|
+
let topLevelRegionCount = 0;
|
|
79
|
+
for (const region of topLevelRegions) {
|
|
80
|
+
const regionBlock = this.transformRegion(region, facetByName, embeddedFacets);
|
|
81
|
+
if (regionBlock) {
|
|
82
|
+
fragment.children = fragment.children ?? [];
|
|
83
|
+
fragment.children.push(regionBlock);
|
|
84
|
+
topLevelRegionCount++;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
// Add region count as top-level attribute
|
|
88
|
+
if (topLevelRegionCount > 0) {
|
|
89
|
+
fragment.attributes = fragment.attributes ?? {};
|
|
90
|
+
fragment.attributes.regions = topLevelRegionCount;
|
|
91
|
+
}
|
|
92
|
+
const metadata = {
|
|
93
|
+
sourceType: 'FlexiPage',
|
|
94
|
+
transformedAt: new Date().toISOString(),
|
|
95
|
+
blockCount: this.blockCount,
|
|
96
|
+
warnings: this.warnings.length > 0 ? this.warnings : undefined,
|
|
97
|
+
};
|
|
98
|
+
return { fragment, metadata };
|
|
99
|
+
}
|
|
100
|
+
catch (error) {
|
|
101
|
+
const pluginError = ErrorBuilder.fromError(ErrorCategory.Parsing, error, {
|
|
102
|
+
filePath,
|
|
103
|
+
}).build();
|
|
104
|
+
throw pluginError;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
// ---------------------------------------------------------------------------
|
|
108
|
+
// Private transformation methods
|
|
109
|
+
// ---------------------------------------------------------------------------
|
|
110
|
+
/**
|
|
111
|
+
* Transform <template> to root UEM block
|
|
112
|
+
*/
|
|
113
|
+
transformTemplate(template, flexiPage, apiName) {
|
|
114
|
+
const definition = denormalizeFqn(template.name);
|
|
115
|
+
const attributes = {};
|
|
116
|
+
// API name (from file path)
|
|
117
|
+
if (apiName) {
|
|
118
|
+
attributes.apiName = apiName;
|
|
119
|
+
}
|
|
120
|
+
// masterLabel
|
|
121
|
+
if (flexiPage.masterLabel) {
|
|
122
|
+
attributes.masterLabel = flexiPage.masterLabel;
|
|
123
|
+
}
|
|
124
|
+
// Type
|
|
125
|
+
if (flexiPage.type) {
|
|
126
|
+
attributes.type = flexiPage.type;
|
|
127
|
+
}
|
|
128
|
+
// Sobject
|
|
129
|
+
if (flexiPage.sobjectType) {
|
|
130
|
+
attributes.sobjectType = flexiPage.sobjectType;
|
|
131
|
+
}
|
|
132
|
+
// Template
|
|
133
|
+
if (template.name) {
|
|
134
|
+
attributes.template = template.name;
|
|
135
|
+
}
|
|
136
|
+
// Description
|
|
137
|
+
if (flexiPage.description) {
|
|
138
|
+
attributes.description = flexiPage.description;
|
|
139
|
+
}
|
|
140
|
+
this.blockCount++; // Root block
|
|
141
|
+
return {
|
|
142
|
+
definition,
|
|
143
|
+
type: 'block',
|
|
144
|
+
attributes: Object.keys(attributes).length > 0 ? attributes : undefined,
|
|
145
|
+
children: [],
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Transform <flexiPageRegions> to RegionBlockType
|
|
150
|
+
*/
|
|
151
|
+
transformRegion(region, facetByName, embeddedFacets) {
|
|
152
|
+
if (!region.name) {
|
|
153
|
+
this.warnings.push({
|
|
154
|
+
type: 'missing-property',
|
|
155
|
+
message: 'Region missing name property',
|
|
156
|
+
severity: 'warning',
|
|
157
|
+
});
|
|
158
|
+
return null;
|
|
159
|
+
}
|
|
160
|
+
const children = [];
|
|
161
|
+
const items = region.itemInstances ? ensureArray(region.itemInstances) : [];
|
|
162
|
+
for (const item of items) {
|
|
163
|
+
if (item?.componentInstance) {
|
|
164
|
+
const child = this.transformComponentInstance(item.componentInstance, facetByName, embeddedFacets);
|
|
165
|
+
if (child) {
|
|
166
|
+
children.push(child);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
else if (item?.fieldInstance) {
|
|
170
|
+
const child = this.transformFieldInstance(item.fieldInstance);
|
|
171
|
+
if (child) {
|
|
172
|
+
children.push(child);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
// denormalize region name
|
|
177
|
+
return {
|
|
178
|
+
type: 'region',
|
|
179
|
+
name: denormalizeFqn(region.name),
|
|
180
|
+
children: children.length > 0 ? children : undefined,
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Transform <componentInstance> to Block or Field
|
|
185
|
+
*
|
|
186
|
+
* CRITICAL: Handles Facet embedding - when a property value references a Facet
|
|
187
|
+
* region name, that Facet is embedded as a nested RegionBlockType child.
|
|
188
|
+
*
|
|
189
|
+
*/
|
|
190
|
+
transformComponentInstance(instance, facetByName, embeddedFacets) {
|
|
191
|
+
if (!instance.componentName) {
|
|
192
|
+
this.warnings.push({
|
|
193
|
+
type: 'missing-property',
|
|
194
|
+
message: 'Component missing componentName property',
|
|
195
|
+
severity: 'warning',
|
|
196
|
+
});
|
|
197
|
+
return null;
|
|
198
|
+
}
|
|
199
|
+
const definition = denormalizeFqn(instance.componentName);
|
|
200
|
+
// Extract properties as attributes
|
|
201
|
+
const rawProps = instance.componentInstanceProperties
|
|
202
|
+
? ensureArray(instance.componentInstanceProperties)
|
|
203
|
+
: [];
|
|
204
|
+
const attributes = {};
|
|
205
|
+
for (const prop of rawProps) {
|
|
206
|
+
attributes[prop.name] = resolvePropertyValue(prop);
|
|
207
|
+
}
|
|
208
|
+
// ── FieldBlockType detection ────────────────────────────────────────────
|
|
209
|
+
// if componentInstance has "field" property, it's a FieldBlockType
|
|
210
|
+
if ('field' in attributes) {
|
|
211
|
+
const fieldItem = String(attributes['field']);
|
|
212
|
+
const fieldAttrs = { ...attributes, fieldItem };
|
|
213
|
+
delete fieldAttrs['field'];
|
|
214
|
+
this.blockCount++;
|
|
215
|
+
const field = {
|
|
216
|
+
type: 'field',
|
|
217
|
+
definition,
|
|
218
|
+
id: instance.identifier,
|
|
219
|
+
attributes: fieldAttrs,
|
|
220
|
+
};
|
|
221
|
+
if (instance.visibilityRule) {
|
|
222
|
+
field.visibilityRule = transformVisibilityRule(instance.visibilityRule);
|
|
223
|
+
}
|
|
224
|
+
return field;
|
|
225
|
+
}
|
|
226
|
+
// ── BlockType with Facet embedding ──────────────────────────────────────
|
|
227
|
+
this.blockCount++;
|
|
228
|
+
const block = {
|
|
229
|
+
type: 'block',
|
|
230
|
+
definition,
|
|
231
|
+
id: instance.identifier,
|
|
232
|
+
attributes: {},
|
|
233
|
+
children: [],
|
|
234
|
+
};
|
|
235
|
+
// Walk properties: scalar values → attributes; Facet references → child regions
|
|
236
|
+
for (const [key, val] of Object.entries(attributes)) {
|
|
237
|
+
if (typeof val === 'string' && facetByName.has(val)) {
|
|
238
|
+
// Property references a Facet region → embed as nested RegionBlockType child
|
|
239
|
+
// RegionBlockType children are added directly to components
|
|
240
|
+
const facetRegion = facetByName.get(val);
|
|
241
|
+
if (!embeddedFacets.has(val)) {
|
|
242
|
+
embeddedFacets.add(val);
|
|
243
|
+
// Use property key as the region name (semantic naming)
|
|
244
|
+
const childRegion = this.transformRegion({ ...facetRegion, name: key }, facetByName, embeddedFacets);
|
|
245
|
+
if (childRegion) {
|
|
246
|
+
block.children.push(childRegion);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
// Property consumed by child region; don't add to attributes
|
|
250
|
+
}
|
|
251
|
+
else {
|
|
252
|
+
block.attributes[key] = val;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
if (instance.visibilityRule) {
|
|
256
|
+
block.visibilityRule = transformVisibilityRule(instance.visibilityRule);
|
|
257
|
+
}
|
|
258
|
+
// Clean up empty children array
|
|
259
|
+
if (block.children?.length === 0) {
|
|
260
|
+
delete block.children;
|
|
261
|
+
}
|
|
262
|
+
return block;
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Transform <fieldInstance> to FieldBlockType
|
|
266
|
+
*
|
|
267
|
+
* Note: fieldInstance has NO componentName; identified by fieldItem alone
|
|
268
|
+
*/
|
|
269
|
+
transformFieldInstance(instance) {
|
|
270
|
+
if (!instance.fieldItem) {
|
|
271
|
+
this.warnings.push({
|
|
272
|
+
type: 'missing-property',
|
|
273
|
+
message: 'Field instance missing fieldItem property',
|
|
274
|
+
severity: 'warning',
|
|
275
|
+
});
|
|
276
|
+
return null;
|
|
277
|
+
}
|
|
278
|
+
this.blockCount++;
|
|
279
|
+
// Extract field instance properties (e.g., uiBehavior)
|
|
280
|
+
const fieldProps = instance.fieldInstanceProperties
|
|
281
|
+
? ensureArray(instance.fieldInstanceProperties)
|
|
282
|
+
: [];
|
|
283
|
+
const attributes = { fieldItem: instance.fieldItem };
|
|
284
|
+
for (const prop of fieldProps) {
|
|
285
|
+
if (prop.value !== undefined) {
|
|
286
|
+
attributes[prop.name] = String(prop.value);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
// For native fieldInstance, definition is empty (no componentName)
|
|
290
|
+
// For componentInstance with "field" property, definition comes from componentName
|
|
291
|
+
const field = {
|
|
292
|
+
type: 'field',
|
|
293
|
+
definition: '', // Native fieldInstance has no componentName
|
|
294
|
+
id: instance.identifier,
|
|
295
|
+
attributes,
|
|
296
|
+
};
|
|
297
|
+
if (instance.visibilityRule) {
|
|
298
|
+
field.visibilityRule = transformVisibilityRule(instance.visibilityRule);
|
|
299
|
+
}
|
|
300
|
+
return field;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025, Salesforce, Inc.
|
|
3
|
+
* All rights reserved.
|
|
4
|
+
* Licensed under the BSD 3-Clause license.
|
|
5
|
+
* For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
|
|
6
|
+
**/
|
|
7
|
+
import { FlexiPageRegion, VisibilityRule, ComponentInstanceProperty } from '../types/FlexipageMetadata.js';
|
|
8
|
+
/**
|
|
9
|
+
* Transform <visibilityRule> to VisibilityRule
|
|
10
|
+
*/
|
|
11
|
+
export declare function transformVisibilityRule(raw: VisibilityRule): VisibilityRule;
|
|
12
|
+
/**
|
|
13
|
+
* Resolve a single <componentInstanceProperties> entry to a value
|
|
14
|
+
*/
|
|
15
|
+
export declare function resolvePropertyValue(prop: ComponentInstanceProperty): unknown;
|
|
16
|
+
/**
|
|
17
|
+
* Build map of Facet region name → region object
|
|
18
|
+
* Facets can be embedded as nested children when referenced by component properties
|
|
19
|
+
*/
|
|
20
|
+
export declare function buildFacetIndex(regions: FlexiPageRegion[]): Map<string, FlexiPageRegion>;
|
|
21
|
+
/**
|
|
22
|
+
* Denormalize FQN: "flexipage:recentItems" → "flexipage/recentItems"
|
|
23
|
+
*/
|
|
24
|
+
export declare function denormalizeFqn(definition: string): string;
|
|
25
|
+
/**
|
|
26
|
+
* Ensure value is an array (for consistent handling of single vs multiple elements)
|
|
27
|
+
*/
|
|
28
|
+
export declare function ensureArray<T>(value: T | T[]): T[];
|
|
29
|
+
/**
|
|
30
|
+
* Extract API name from file path
|
|
31
|
+
* Example: "/path/to/MyPage.flexipage-meta.xml" → "MyPage"
|
|
32
|
+
*/
|
|
33
|
+
export declare function extractApiName(filePath: string): string | undefined;
|
|
34
|
+
//# sourceMappingURL=FlexipageTransformerUtils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FlexipageTransformerUtils.d.ts","sourceRoot":"","sources":["../../../../src/plugins/flexipage/transformer/FlexipageTransformerUtils.ts"],"names":[],"mappings":"AAAA;;;;;IAKI;AAEJ,OAAO,EACL,eAAe,EACf,cAAc,EACd,yBAAyB,EAC1B,MAAM,+BAA+B,CAAC;AAEvC;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,cAAc,GAAG,cAAc,CAS3E;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,yBAAyB,GAAG,OAAO,CAO7E;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,eAAe,EAAE,GAAG,GAAG,CAAC,MAAM,EAAE,eAAe,CAAC,CAQxF;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAKzD;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,CAAC,EAAE,GAAG,CAAC,EAAE,CAElD;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAUnE"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025, Salesforce, Inc.
|
|
3
|
+
* All rights reserved.
|
|
4
|
+
* Licensed under the BSD 3-Clause license.
|
|
5
|
+
* For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
|
|
6
|
+
**/
|
|
7
|
+
/**
|
|
8
|
+
* Transform <visibilityRule> to VisibilityRule
|
|
9
|
+
*/
|
|
10
|
+
export function transformVisibilityRule(raw) {
|
|
11
|
+
return {
|
|
12
|
+
booleanFilter: raw.booleanFilter,
|
|
13
|
+
criteria: raw.criteria.map((c) => ({
|
|
14
|
+
leftValue: String(c.leftValue),
|
|
15
|
+
operator: String(c.operator),
|
|
16
|
+
rightValue: String(c.rightValue),
|
|
17
|
+
})),
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Resolve a single <componentInstanceProperties> entry to a value
|
|
22
|
+
*/
|
|
23
|
+
export function resolvePropertyValue(prop) {
|
|
24
|
+
if (prop.valueList !== undefined) {
|
|
25
|
+
// <valueList> → string[] (flatten all valueListItems)
|
|
26
|
+
const items = ensureArray(prop.valueList.valueListItems);
|
|
27
|
+
return items.flatMap((item) => ensureArray(item.value));
|
|
28
|
+
}
|
|
29
|
+
return prop.value !== undefined ? String(prop.value) : undefined;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Build map of Facet region name → region object
|
|
33
|
+
* Facets can be embedded as nested children when referenced by component properties
|
|
34
|
+
*/
|
|
35
|
+
export function buildFacetIndex(regions) {
|
|
36
|
+
const map = new Map();
|
|
37
|
+
for (const region of regions) {
|
|
38
|
+
if (region.type?.toLowerCase() === 'facet') {
|
|
39
|
+
map.set(region.name, region);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return map;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Denormalize FQN: "flexipage:recentItems" → "flexipage/recentItems"
|
|
46
|
+
*/
|
|
47
|
+
export function denormalizeFqn(definition) {
|
|
48
|
+
if (!definition) {
|
|
49
|
+
return definition;
|
|
50
|
+
}
|
|
51
|
+
return definition.replace(/:/g, '/');
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Ensure value is an array (for consistent handling of single vs multiple elements)
|
|
55
|
+
*/
|
|
56
|
+
export function ensureArray(value) {
|
|
57
|
+
return Array.isArray(value) ? value : [value];
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Extract API name from file path
|
|
61
|
+
* Example: "/path/to/MyPage.flexipage-meta.xml" → "MyPage"
|
|
62
|
+
*/
|
|
63
|
+
export function extractApiName(filePath) {
|
|
64
|
+
if (!filePath) {
|
|
65
|
+
return undefined;
|
|
66
|
+
}
|
|
67
|
+
const filename = filePath.split('/').pop() || filePath.split('\\').pop();
|
|
68
|
+
if (!filename) {
|
|
69
|
+
return undefined;
|
|
70
|
+
}
|
|
71
|
+
// Remove .flexipage-meta.xml or .flexipage extension
|
|
72
|
+
return filename.replace(/\.flexipage(-meta)?\.xml$/i, '');
|
|
73
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025, Salesforce, Inc.
|
|
3
|
+
* All rights reserved.
|
|
4
|
+
* Licensed under the BSD 3-Clause license.
|
|
5
|
+
* For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
|
|
6
|
+
**/
|
|
7
|
+
/**
|
|
8
|
+
* Type definitions for Salesforce FlexiPage metadata
|
|
9
|
+
*/
|
|
10
|
+
export interface SalesforceFlexipage {
|
|
11
|
+
FlexiPage: FlexipageMetadata;
|
|
12
|
+
}
|
|
13
|
+
export interface FlexipageMetadata {
|
|
14
|
+
type: string;
|
|
15
|
+
sobjectType?: string;
|
|
16
|
+
masterLabel?: string;
|
|
17
|
+
description?: string;
|
|
18
|
+
template: Template;
|
|
19
|
+
flexiPageRegions?: FlexiPageRegion | FlexiPageRegion[];
|
|
20
|
+
}
|
|
21
|
+
export interface Template {
|
|
22
|
+
name: string;
|
|
23
|
+
properties?: TemplateProperty | TemplateProperty[];
|
|
24
|
+
}
|
|
25
|
+
export interface TemplateProperty {
|
|
26
|
+
name: string;
|
|
27
|
+
value: string;
|
|
28
|
+
}
|
|
29
|
+
export interface FlexiPageRegion {
|
|
30
|
+
name: string;
|
|
31
|
+
type?: string;
|
|
32
|
+
mode?: string;
|
|
33
|
+
itemInstances?: ItemInstance | ItemInstance[];
|
|
34
|
+
}
|
|
35
|
+
export interface ItemInstance {
|
|
36
|
+
componentInstance?: ComponentInstance;
|
|
37
|
+
fieldInstance?: FieldInstance;
|
|
38
|
+
}
|
|
39
|
+
export interface ComponentInstance {
|
|
40
|
+
componentName: string;
|
|
41
|
+
identifier: string;
|
|
42
|
+
componentInstanceProperties?: ComponentInstanceProperty | ComponentInstanceProperty[];
|
|
43
|
+
visibilityRule?: VisibilityRule;
|
|
44
|
+
}
|
|
45
|
+
export interface ComponentInstanceProperty {
|
|
46
|
+
name: string;
|
|
47
|
+
value: string;
|
|
48
|
+
type?: string;
|
|
49
|
+
valueList?: ValueList;
|
|
50
|
+
}
|
|
51
|
+
export interface ValueList {
|
|
52
|
+
valueListItems: ValueListItem | ValueListItem[];
|
|
53
|
+
}
|
|
54
|
+
export interface ValueListItem {
|
|
55
|
+
value: string | string[];
|
|
56
|
+
}
|
|
57
|
+
export interface FieldInstance {
|
|
58
|
+
fieldItem: string;
|
|
59
|
+
identifier: string;
|
|
60
|
+
visibilityRule?: VisibilityRule;
|
|
61
|
+
fieldInstanceProperties?: FieldInstanceProperty | FieldInstanceProperty[];
|
|
62
|
+
}
|
|
63
|
+
export interface FieldInstanceProperty {
|
|
64
|
+
name: string;
|
|
65
|
+
value: string;
|
|
66
|
+
}
|
|
67
|
+
export interface VisibilityRule {
|
|
68
|
+
booleanFilter?: string;
|
|
69
|
+
criteria: VisibilityRuleCriteria[];
|
|
70
|
+
}
|
|
71
|
+
export interface VisibilityRuleCriteria {
|
|
72
|
+
leftValue: string;
|
|
73
|
+
operator: string;
|
|
74
|
+
rightValue: string;
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=FlexipageMetadata.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FlexipageMetadata.d.ts","sourceRoot":"","sources":["../../../../src/plugins/flexipage/types/FlexipageMetadata.ts"],"names":[],"mappings":"AAAA;;;;;IAKI;AAEJ;;GAEG;AAEH,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,iBAAiB,CAAC;CAC9B;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,QAAQ,CAAC;IACnB,gBAAgB,CAAC,EAAE,eAAe,GAAG,eAAe,EAAE,CAAC;CACxD;AAED,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,gBAAgB,GAAG,gBAAgB,EAAE,CAAC;CACpD;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,aAAa,CAAC,EAAE,YAAY,GAAG,YAAY,EAAE,CAAC;CAC/C;AAED,MAAM,WAAW,YAAY;IAC3B,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;IACtC,aAAa,CAAC,EAAE,aAAa,CAAC;CAC/B;AAED,MAAM,WAAW,iBAAiB;IAChC,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,2BAA2B,CAAC,EAAE,yBAAyB,GAAG,yBAAyB,EAAE,CAAC;IACtF,cAAc,CAAC,EAAE,cAAc,CAAC;CACjC;AAED,MAAM,WAAW,yBAAyB;IACxC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,SAAS,CAAC;CACvB;AAED,MAAM,WAAW,SAAS;IACxB,cAAc,EAAE,aAAa,GAAG,aAAa,EAAE,CAAC;CACjD;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;CAC1B;AAED,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,uBAAuB,CAAC,EAAE,qBAAqB,GAAG,qBAAqB,EAAE,CAAC;CAC3E;AAED,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,cAAc;IAC7B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,EAAE,sBAAsB,EAAE,CAAC;CACpC;AAED,MAAM,WAAW,sBAAsB;IACrC,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;CACpB"}
|