@sap-ux/preview-middleware 0.6.3 → 0.7.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.
Files changed (41) hide show
  1. package/dist/base/flp.d.ts +1 -0
  2. package/dist/base/flp.js +49 -8
  3. package/dist/client/adp/controllers/AddFragment.controller.js +1 -27
  4. package/dist/client/adp/controllers/AddFragment.controller.ts +2 -24
  5. package/dist/client/cpe/changes/flex-change.js +46 -0
  6. package/dist/client/cpe/changes/flex-change.ts +49 -0
  7. package/dist/client/cpe/changes/index.js +12 -0
  8. package/dist/client/cpe/changes/index.ts +1 -0
  9. package/dist/client/cpe/changes/service.js +164 -0
  10. package/dist/client/cpe/changes/service.ts +264 -0
  11. package/dist/client/cpe/control-data.js +201 -0
  12. package/dist/client/cpe/control-data.ts +304 -0
  13. package/dist/client/cpe/documentation.js +166 -0
  14. package/dist/client/cpe/documentation.ts +166 -0
  15. package/dist/client/cpe/facade.js +95 -0
  16. package/dist/client/cpe/facade.ts +95 -0
  17. package/dist/client/cpe/init.js +62 -12
  18. package/dist/client/cpe/init.ts +70 -5
  19. package/dist/client/cpe/logger.js +32 -0
  20. package/dist/client/cpe/logger.ts +26 -0
  21. package/dist/client/cpe/outline/index.js +26 -0
  22. package/dist/client/cpe/outline/index.ts +34 -0
  23. package/dist/client/cpe/outline/nodes.js +58 -0
  24. package/dist/client/cpe/outline/nodes.ts +52 -0
  25. package/dist/client/cpe/outline/utils.js +38 -0
  26. package/dist/client/cpe/outline/utils.ts +29 -0
  27. package/dist/client/cpe/selection.js +169 -0
  28. package/dist/client/cpe/selection.ts +237 -0
  29. package/dist/client/cpe/types.js +6 -0
  30. package/dist/client/cpe/types.ts +53 -0
  31. package/dist/client/cpe/utils.js +50 -0
  32. package/dist/client/cpe/utils.ts +62 -0
  33. package/dist/client/flp/WorkspaceConnector.js +65 -0
  34. package/dist/client/flp/WorkspaceConnector.ts +66 -0
  35. package/dist/client/thirdparty/@sap-ux-private/control-property-editor-common.js +272 -0
  36. package/dist/types/index.d.ts +4 -0
  37. package/dist/ui5/middleware.js +2 -1
  38. package/package.json +5 -7
  39. package/templates/flp/editor.html +11 -15
  40. package/templates/flp/sandbox.html +2 -1
  41. package/templates/flp/workspaceConnector.js +0 -76
@@ -0,0 +1,304 @@
1
+ import type { Control, ControlProperty } from '@sap-ux-private/control-property-editor-common';
2
+ import {
3
+ BOOLEAN_VALUE_TYPE,
4
+ CHECKBOX_EDITOR_TYPE,
5
+ DROPDOWN_EDITOR_TYPE,
6
+ FLOAT_VALUE_TYPE,
7
+ INPUT_EDITOR_TYPE,
8
+ INTEGER_VALUE_TYPE,
9
+ STRING_VALUE_TYPE,
10
+ convertCamelCaseToPascalCase
11
+ } from '@sap-ux-private/control-property-editor-common';
12
+
13
+ import Utils from 'sap/ui/fl/Utils';
14
+ import type ManagedObject from 'sap/ui/base/ManagedObject';
15
+ import type ElementOverlay from 'sap/ui/dt/ElementOverlay';
16
+ import type { ManagedObjectMetadataProperties } from './utils';
17
+ import { UI5ControlProperty } from './types';
18
+ import DataType from 'sap/ui/base/DataType';
19
+
20
+ type AnalyzedType = Pick<UI5ControlProperty, 'isArray' | 'primitiveType' | 'ui5Type' | 'enumValues'>;
21
+ /**
22
+ * A property is disabled if it is an array or the type is 'any'
23
+ * - since we currently don't have a good editor for it Otherwise, it is enabled.
24
+ *
25
+ * @param analyzedType - analyzed property type
26
+ * @returns boolean
27
+ */
28
+ function isPropertyEnabled(analyzedType: AnalyzedType): boolean {
29
+ return !(analyzedType.isArray || analyzedType.primitiveType === 'any');
30
+ }
31
+
32
+ /**
33
+ * Return if a control is enabled or not.
34
+ *
35
+ * @param analyzedType - analyzed property type
36
+ * @param hasStableId - given control has stable id.
37
+ * @param ignore - property that is ignored during design time
38
+ * @param controlOverlay - element overlay
39
+ * @returns boolean
40
+ */
41
+ function isControlEnabled(
42
+ analyzedType: AnalyzedType,
43
+ hasStableId: boolean,
44
+ ignore: boolean,
45
+ controlOverlay?: ElementOverlay
46
+ ): boolean {
47
+ return (controlOverlay?.isSelectable() ?? false) && isPropertyEnabled(analyzedType) && hasStableId && !ignore;
48
+ }
49
+
50
+ /**
51
+ * Match 'src' or any string starting or ending with 'icon' (case insensitive;).
52
+ *
53
+ * @param name icon name
54
+ * @returns boolean
55
+ */
56
+ function testIconPattern(name: string): boolean {
57
+ // replace `/src|.*icon$|^icon.*/i`.test(property.name);
58
+ // match 'src' or any string starting or ending with 'icon' (case insensitive;)
59
+ const nameLc = (name || '').toLowerCase();
60
+ return nameLc.indexOf('src') >= 0 || nameLc.startsWith('icon') || nameLc.endsWith('icon');
61
+ }
62
+
63
+ /**
64
+ * Analyze property type string | boolean | enum.
65
+ *
66
+ * @param property control property
67
+ * @returns AnalyzedType|undefined
68
+ */
69
+ function analyzePropertyType(property: ManagedObjectMetadataProperties): AnalyzedType | undefined {
70
+ const analyzedType: AnalyzedType = {
71
+ primitiveType: 'any',
72
+ ui5Type: null,
73
+ enumValues: null,
74
+ isArray: false
75
+ };
76
+ const propertyType = property?.getType();
77
+ const typeName = propertyType?.getName();
78
+
79
+ if (!property || !propertyType || !typeName) {
80
+ return undefined;
81
+ }
82
+
83
+ // Check if array and determine property type (or component type)
84
+ if (typeName.indexOf('[]') > 0) {
85
+ analyzedType.primitiveType = typeName.substring(0, typeName.indexOf('[]'));
86
+ analyzedType.isArray = true;
87
+ }
88
+ // Return if object or void type
89
+ else if (['object', 'void', 'any'].includes(typeName)) {
90
+ analyzedType.primitiveType = typeName;
91
+ }
92
+ // Type of control property is an elementary simple type
93
+ else if (['boolean', 'string', 'int', 'float'].includes(typeName)) {
94
+ analyzedType.primitiveType = typeName;
95
+ }
96
+ // Control type is a sap.ui.base.DataType or an enumeration type
97
+ else {
98
+ // Determine type from iFrame
99
+ const propertyDataType = DataType.getType(typeName);
100
+
101
+ // type which is not a DataType such as Control is not supported
102
+ if (propertyDataType && !(propertyDataType instanceof DataType)) {
103
+ return analyzedType;
104
+ }
105
+ const name = Object.getPrototypeOf(propertyDataType).getName();
106
+ if (!name) {
107
+ analyzedType.primitiveType = 'enum';
108
+ } else {
109
+ analyzedType.primitiveType = name;
110
+ }
111
+ analyzedType.ui5Type = typeName;
112
+
113
+ // Determine base type for SAP types
114
+ if (analyzedType.primitiveType === 'enum') {
115
+ analyzedType.enumValues = sap.ui.require(analyzedType.ui5Type.split('.').join('/'));
116
+ }
117
+ }
118
+
119
+ return analyzedType;
120
+ }
121
+
122
+ /**
123
+ * If rawValue is anything except an object like {} or a function, return it as-is,
124
+ * If it is an object, stringify it.
125
+ * If it is a function, return empty string.
126
+ *
127
+ * @param rawValue property value
128
+ * @returns string
129
+ */
130
+ function normalizeObjectPropertyValue(rawValue: unknown): unknown {
131
+ if (typeof rawValue === 'object' && rawValue instanceof Object && !Array.isArray(rawValue)) {
132
+ try {
133
+ return JSON.stringify(rawValue);
134
+ } catch (e) {
135
+ if (e instanceof Error && e.message.toLowerCase().includes('converting circular structure to json')) {
136
+ // some objects can be circular, e.g.:
137
+ // var obj = {
138
+ // key1: value,
139
+ // key2: obj
140
+ // }
141
+ // and JSON.stringify can't handle that so we reach here.
142
+ // however, postMessage can't handle that either, and throws:
143
+ // "Failed to execute 'postMessage' on 'Window': An object could not be cloned".
144
+ // so we need to check whether this is the failure and if so, don't return the rawValue,
145
+ // but some default string to act as the property value.
146
+ return '<Circular JSON cannot be displayed>';
147
+ }
148
+
149
+ return rawValue;
150
+ }
151
+ } else if (typeof rawValue === 'function') {
152
+ return '';
153
+ } else {
154
+ return rawValue;
155
+ }
156
+ }
157
+
158
+ interface NewControlData {
159
+ id: string;
160
+ name: string;
161
+ newValue: unknown;
162
+ }
163
+
164
+ /**
165
+ * Build control data.
166
+ *
167
+ * @param control - ui5 control
168
+ * @param controlOverlay - element overlay
169
+ * @param includeDocumentation - include documentation flag
170
+ * @returns Promise<Control>
171
+ */
172
+ export function buildControlData(control: ManagedObject, controlOverlay?: ElementOverlay): Control {
173
+ const controlMetadata = control.getMetadata();
174
+ const selectedControlName = controlMetadata.getName();
175
+ const hasStableId = Utils.checkControlId(control);
176
+ const controlProperties = controlOverlay ? controlOverlay.getDesignTimeMetadata().getData().properties : undefined;
177
+
178
+ // Add the control's properties
179
+ const allProperties = controlMetadata.getAllProperties() as unknown as {
180
+ [name: string]: ManagedObjectMetadataProperties;
181
+ };
182
+ const propertyNames = Object.keys(allProperties);
183
+ const properties: ControlProperty[] = [];
184
+ for (const propertyName of propertyNames) {
185
+ const property = allProperties[propertyName];
186
+
187
+ const analyzedType = analyzePropertyType(property);
188
+ if (!analyzedType) {
189
+ continue;
190
+ }
191
+ // the default behavior is that the property is enabled
192
+ // meaning it's not ignored during design time
193
+ let ignore = false;
194
+ if (controlProperties?.[property.name]) {
195
+ // check whether the property should be ignored in design time or not
196
+ // if it's 'undefined' then it's not considered when building isEnabled because it's 'true'
197
+ ignore = controlProperties[property.name].ignore;
198
+ }
199
+
200
+ //updating i18n text for the control if bindingInfo has bindingString
201
+ const controlNewData: NewControlData = {
202
+ id: control.getId(),
203
+ name: property.name,
204
+ newValue: control.getProperty(property.name)
205
+ };
206
+ const bindingInfo: { bindingString?: string } = control.getBindingInfo(controlNewData.name) as {
207
+ bindingString?: string;
208
+ };
209
+ if (bindingInfo?.bindingString !== undefined) {
210
+ controlNewData.newValue = bindingInfo.bindingString;
211
+ }
212
+
213
+ // A property is enabled if:
214
+ // 1. The property supports changes
215
+ // 2. The control has stable ID
216
+ // 3. It is not configured to be ignored in design time
217
+ // 4. And control overlay is selectable
218
+ const isEnabled = isControlEnabled(analyzedType, hasStableId, ignore, controlOverlay);
219
+
220
+ const value = normalizeObjectPropertyValue(controlNewData.newValue);
221
+ const isIcon =
222
+ testIconPattern(property.name) &&
223
+ selectedControlName !== 'sap.m.Image' &&
224
+ analyzedType.ui5Type === 'sap.ui.core.URI';
225
+ const ui5Type = analyzedType.ui5Type || undefined;
226
+ const readableName = convertCamelCaseToPascalCase(property.name);
227
+ switch (analyzedType.primitiveType) {
228
+ case 'enum': {
229
+ const values = analyzedType.enumValues ?? {};
230
+ const options: { key: string; text: string }[] = Object.keys(values).map((key) => ({
231
+ key,
232
+ text: values[key]
233
+ }));
234
+ properties.push({
235
+ type: STRING_VALUE_TYPE,
236
+ editor: DROPDOWN_EDITOR_TYPE,
237
+ name: property.name,
238
+ readableName,
239
+ value: value as string,
240
+ isEnabled,
241
+ ui5Type,
242
+ options
243
+ });
244
+ break;
245
+ }
246
+ case 'string': {
247
+ properties.push({
248
+ type: STRING_VALUE_TYPE,
249
+ editor: INPUT_EDITOR_TYPE,
250
+ name: property.name,
251
+ readableName,
252
+ value: value as string,
253
+ isEnabled,
254
+ isIcon,
255
+ ui5Type
256
+ });
257
+ break;
258
+ }
259
+ case 'int': {
260
+ properties.push({
261
+ type: INTEGER_VALUE_TYPE,
262
+ editor: INPUT_EDITOR_TYPE,
263
+ name: property.name,
264
+ readableName,
265
+ value: value as number,
266
+ isEnabled,
267
+ ui5Type
268
+ });
269
+ break;
270
+ }
271
+ case 'float': {
272
+ properties.push({
273
+ type: FLOAT_VALUE_TYPE,
274
+ editor: INPUT_EDITOR_TYPE,
275
+ name: property.name,
276
+ readableName,
277
+ value: value as number,
278
+ isEnabled,
279
+ ui5Type
280
+ });
281
+ break;
282
+ }
283
+ case 'boolean': {
284
+ properties.push({
285
+ type: BOOLEAN_VALUE_TYPE,
286
+ editor: CHECKBOX_EDITOR_TYPE,
287
+ name: property.name,
288
+ readableName,
289
+ value: value as boolean,
290
+ isEnabled,
291
+ ui5Type
292
+ });
293
+ break;
294
+ }
295
+ default:
296
+ }
297
+ }
298
+ return {
299
+ id: control.getId(), //the id of the underlying control/aggregation
300
+ type: selectedControlName, //the name of the ui5 class of the control/aggregation
301
+ properties: [...properties].sort((a, b) => (a.name > b.name ? 1 : -1)),
302
+ name: selectedControlName
303
+ };
304
+ }
@@ -0,0 +1,166 @@
1
+ "use strict";
2
+
3
+ sap.ui.define(["./utils", "sap/base/Log"], function (___utils, Log) {
4
+ "use strict";
5
+
6
+ const getLibrary = ___utils["getLibrary"];
7
+ /**
8
+ * Get ui5 metadata of given library name.
9
+ *
10
+ * @param libName library name for eg: sap.m
11
+ * @returns Promise<SchemaForApiJsonFiles>
12
+ */
13
+ async function getUi5ApiDtMetadata(libName) {
14
+ const libUrl = '/test-resources/' + libName.split('.').join('/') + '/designtime/api.json';
15
+ return fetch(libUrl).then(res => res.json());
16
+ }
17
+
18
+ /**
19
+ * load ui5 controls design time metadata api.json for following libraries
20
+ * sap.m, sap.ui.comp, sap.f, sap.ui.core
21
+ * loading libraries(more in file size) in parallel during initialization.
22
+ * Others (less in file size) are loaded dynamically in getControlMetadata method
23
+ */
24
+ const ui5ApiDtMetadata = new Map();
25
+ function loadDefaultLibraries() {
26
+ const allData = Promise.all([getUi5ApiDtMetadata('sap.m'), getUi5ApiDtMetadata('sap.ui.comp'), getUi5ApiDtMetadata('sap.ui.core'), getUi5ApiDtMetadata('sap.f')]);
27
+ allData.then(res => {
28
+ res.forEach(api => {
29
+ if (api.library) {
30
+ ui5ApiDtMetadata.set(api.library, api);
31
+ }
32
+ });
33
+ }).catch(reason => Log.error('Loading Library Failed: ' + reason));
34
+ }
35
+
36
+ /**
37
+ * Format html text.
38
+ *
39
+ * @param sHtml - html string
40
+ * @returns string
41
+ */
42
+ function formatHtmlText(sHtml) {
43
+ // replaced "sHtml.replace(new RegExp('<[^>]*>', 'g')" due to regex runtime vulnerability
44
+ const parts = (sHtml || '').split('<');
45
+ let result = '';
46
+ for (const part of parts) {
47
+ if (!result) {
48
+ result = part;
49
+ } else {
50
+ const indexClosingBracket = part.indexOf('>');
51
+ result += indexClosingBracket >= 0 ? part.substring(indexClosingBracket + 1) : `<${part}`;
52
+ }
53
+ }
54
+ return result;
55
+ }
56
+
57
+ /**
58
+ * Method to parse ui5 control metadata.
59
+ *
60
+ * @param controlLibMetadata control library metadata
61
+ * @param controlName name of the control
62
+ * @returns ControlMetadata
63
+ */
64
+ function parseControlMetaModel(controlLibMetadata, controlName) {
65
+ const controlInfo = {
66
+ baseType: '',
67
+ doc: '',
68
+ properties: {}
69
+ };
70
+ const selectedControlMetadata = (controlLibMetadata.symbols ?? []).find(control => control.name === controlName);
71
+ if (selectedControlMetadata) {
72
+ // base type info of control is available on property 'extends'
73
+ controlInfo.baseType = selectedControlMetadata.extends;
74
+ controlInfo.doc = selectedControlMetadata.description ?? '';
75
+ const properties = selectedControlMetadata['ui5-metadata'].properties;
76
+ if (properties) {
77
+ properties.forEach(prop => {
78
+ prop.description = formatHtmlText(prop.description || '');
79
+ prop.propertyName = prop.name;
80
+ prop.propertyType = prop.type;
81
+ if (prop.defaultValue === null || prop.defaultValue === '') {
82
+ prop.defaultValue = '-';
83
+ }
84
+ controlInfo.properties[prop.name] = {
85
+ ...prop
86
+ };
87
+ });
88
+ }
89
+ }
90
+ return controlInfo;
91
+ }
92
+
93
+ /**
94
+ * Get control metadata for a given control.
95
+ *
96
+ * @param controlName name of the control
97
+ * @param contLibName library name of the control
98
+ * @returns Promise<ControlMetadata | undefined>
99
+ */
100
+ async function getControlMetadata(controlName, contLibName) {
101
+ let result;
102
+ let controlLibMetadata = ui5ApiDtMetadata.get(contLibName);
103
+ if (controlLibMetadata) {
104
+ result = parseControlMetaModel(controlLibMetadata, controlName);
105
+ } else {
106
+ controlLibMetadata = await getUi5ApiDtMetadata(contLibName);
107
+ ui5ApiDtMetadata.set(contLibName, controlLibMetadata);
108
+ result = parseControlMetaModel(controlLibMetadata, controlName);
109
+ }
110
+ return result;
111
+ }
112
+
113
+ /**
114
+ * Get Control Property Documentation for a give control name and control library.
115
+ *
116
+ * @param controlName name of the control
117
+ * @param contLibName library name of the control
118
+ * @returns Promise<Properties | undefined>
119
+ */
120
+ async function getControlPropertyDocumentation(controlName, contLibName) {
121
+ const doc = await getControlMetadata(controlName, contLibName);
122
+ if (doc) {
123
+ const baseControlType = doc.baseType;
124
+ if (baseControlType) {
125
+ const baseContLibName = await getLibrary(baseControlType);
126
+ if (baseContLibName) {
127
+ const baseControlProps = await getControlPropertyDocumentation(baseControlType, baseContLibName);
128
+ return {
129
+ ...baseControlProps,
130
+ ...doc.properties
131
+ };
132
+ }
133
+ }
134
+ return {
135
+ ...doc.properties
136
+ };
137
+ } else {
138
+ return undefined;
139
+ }
140
+ }
141
+
142
+ /**
143
+ * Get documentation of a given control of a ui5 library.
144
+ *
145
+ * @param controlName name of the control.
146
+ * @param contLibName library name of the control
147
+ * @returns Promise<Properties | undefined>
148
+ */
149
+ async function getDocumentation(controlName, contLibName) {
150
+ let doc;
151
+ try {
152
+ doc = await getControlPropertyDocumentation(controlName, contLibName);
153
+ } catch (err) {
154
+ Log.error(`Error in getting documentation for ${contLibName}`);
155
+ }
156
+ return doc;
157
+ }
158
+ var __exports = {
159
+ __esModule: true
160
+ };
161
+ __exports.getUi5ApiDtMetadata = getUi5ApiDtMetadata;
162
+ __exports.loadDefaultLibraries = loadDefaultLibraries;
163
+ __exports.getDocumentation = getDocumentation;
164
+ return __exports;
165
+ });
166
+ //# sourceMappingURL=documentation.js.map
@@ -0,0 +1,166 @@
1
+ import { getLibrary } from './utils';
2
+ import type { SchemaForApiJsonFiles, Ui5Property } from './api-json';
3
+ import type { Properties } from './utils';
4
+ import Log from 'sap/base/Log';
5
+ import { PropertiesInfo } from '@sap-ux-private/control-property-editor-common';
6
+
7
+ export interface ControlMetadata {
8
+ baseType: string | undefined;
9
+ doc: string;
10
+ properties: Properties;
11
+ }
12
+
13
+ /**
14
+ * Get ui5 metadata of given library name.
15
+ *
16
+ * @param libName library name for eg: sap.m
17
+ * @returns Promise<SchemaForApiJsonFiles>
18
+ */
19
+ export async function getUi5ApiDtMetadata(libName: string): Promise<SchemaForApiJsonFiles> {
20
+ const libUrl = '/test-resources/' + libName.split('.').join('/') + '/designtime/api.json';
21
+ return fetch(libUrl).then((res) => res.json());
22
+ }
23
+
24
+ /**
25
+ * load ui5 controls design time metadata api.json for following libraries
26
+ * sap.m, sap.ui.comp, sap.f, sap.ui.core
27
+ * loading libraries(more in file size) in parallel during initialization.
28
+ * Others (less in file size) are loaded dynamically in getControlMetadata method
29
+ */
30
+ const ui5ApiDtMetadata: Map<string, SchemaForApiJsonFiles> = new Map();
31
+ export function loadDefaultLibraries(): void {
32
+ const allData = Promise.all([
33
+ getUi5ApiDtMetadata('sap.m'),
34
+ getUi5ApiDtMetadata('sap.ui.comp'),
35
+ getUi5ApiDtMetadata('sap.ui.core'),
36
+ getUi5ApiDtMetadata('sap.f')
37
+ ]);
38
+ allData
39
+ .then((res) => {
40
+ res.forEach((api: SchemaForApiJsonFiles) => {
41
+ if (api.library) {
42
+ ui5ApiDtMetadata.set(api.library, api);
43
+ }
44
+ });
45
+ })
46
+ .catch((reason) => Log.error('Loading Library Failed: ' + reason));
47
+ }
48
+
49
+ /**
50
+ * Format html text.
51
+ *
52
+ * @param sHtml - html string
53
+ * @returns string
54
+ */
55
+ function formatHtmlText(sHtml: string): string {
56
+ // replaced "sHtml.replace(new RegExp('<[^>]*>', 'g')" due to regex runtime vulnerability
57
+ const parts = (sHtml || '').split('<');
58
+ let result = '';
59
+ for (const part of parts) {
60
+ if (!result) {
61
+ result = part;
62
+ } else {
63
+ const indexClosingBracket = part.indexOf('>');
64
+ result += indexClosingBracket >= 0 ? part.substring(indexClosingBracket + 1) : `<${part}`;
65
+ }
66
+ }
67
+ return result;
68
+ }
69
+
70
+ /**
71
+ * Method to parse ui5 control metadata.
72
+ *
73
+ * @param controlLibMetadata control library metadata
74
+ * @param controlName name of the control
75
+ * @returns ControlMetadata
76
+ */
77
+ function parseControlMetaModel(controlLibMetadata: SchemaForApiJsonFiles, controlName: string): ControlMetadata {
78
+ const controlInfo: ControlMetadata = {
79
+ baseType: '',
80
+ doc: '',
81
+ properties: {}
82
+ };
83
+ const selectedControlMetadata = (controlLibMetadata.symbols ?? []).find((control) => control.name === controlName);
84
+ if (selectedControlMetadata) {
85
+ // base type info of control is available on property 'extends'
86
+ controlInfo.baseType = selectedControlMetadata.extends;
87
+ controlInfo.doc = selectedControlMetadata.description ?? '';
88
+ const properties = selectedControlMetadata['ui5-metadata'].properties;
89
+ if (properties) {
90
+ properties.forEach((prop: Ui5Property & PropertiesInfo) => {
91
+ prop.description = formatHtmlText(prop.description || '');
92
+ prop.propertyName = prop.name;
93
+ prop.propertyType = prop.type;
94
+ if (prop.defaultValue === null || prop.defaultValue === '') {
95
+ prop.defaultValue = '-';
96
+ }
97
+ controlInfo.properties[prop.name] = { ...prop };
98
+ });
99
+ }
100
+ }
101
+ return controlInfo;
102
+ }
103
+
104
+ /**
105
+ * Get control metadata for a given control.
106
+ *
107
+ * @param controlName name of the control
108
+ * @param contLibName library name of the control
109
+ * @returns Promise<ControlMetadata | undefined>
110
+ */
111
+ async function getControlMetadata(controlName: string, contLibName: string): Promise<ControlMetadata | undefined> {
112
+ let result: ControlMetadata | undefined;
113
+ let controlLibMetadata = ui5ApiDtMetadata.get(contLibName);
114
+ if (controlLibMetadata) {
115
+ result = parseControlMetaModel(controlLibMetadata, controlName);
116
+ } else {
117
+ controlLibMetadata = await getUi5ApiDtMetadata(contLibName);
118
+ ui5ApiDtMetadata.set(contLibName, controlLibMetadata);
119
+ result = parseControlMetaModel(controlLibMetadata, controlName);
120
+ }
121
+ return result;
122
+ }
123
+
124
+ /**
125
+ * Get Control Property Documentation for a give control name and control library.
126
+ *
127
+ * @param controlName name of the control
128
+ * @param contLibName library name of the control
129
+ * @returns Promise<Properties | undefined>
130
+ */
131
+ async function getControlPropertyDocumentation(
132
+ controlName: string,
133
+ contLibName: string
134
+ ): Promise<Properties | undefined> {
135
+ const doc = await getControlMetadata(controlName, contLibName);
136
+ if (doc) {
137
+ const baseControlType = doc.baseType;
138
+ if (baseControlType) {
139
+ const baseContLibName = await getLibrary(baseControlType);
140
+ if (baseContLibName) {
141
+ const baseControlProps = await getControlPropertyDocumentation(baseControlType, baseContLibName);
142
+ return { ...baseControlProps, ...doc.properties };
143
+ }
144
+ }
145
+ return { ...doc.properties };
146
+ } else {
147
+ return undefined;
148
+ }
149
+ }
150
+
151
+ /**
152
+ * Get documentation of a given control of a ui5 library.
153
+ *
154
+ * @param controlName name of the control.
155
+ * @param contLibName library name of the control
156
+ * @returns Promise<Properties | undefined>
157
+ */
158
+ export async function getDocumentation(controlName: string, contLibName: string): Promise<Properties | undefined> {
159
+ let doc: Properties | undefined;
160
+ try {
161
+ doc = await getControlPropertyDocumentation(controlName, contLibName);
162
+ } catch (err) {
163
+ Log.error(`Error in getting documentation for ${contLibName}`);
164
+ }
165
+ return doc;
166
+ }