Skip to content

Commit d99a195

Browse files
committed
[INTERNAL] lib/processors/jsdoc: Enhance visualization of multiple types
Background: The jsdoc allows to specify multiple types for: - function parameters (including constructor parameters) - function return values - properties of typedefs e.g. typedef sap/ui/performance/Measurement.Entry - UI5 metadata properties Then transformApiJson.js adds those multiple types into the "types" field of the entries of the api.json files for each library Problem: The UI5 Demokit did not correctly display multiple types for UI5 metadata properties e.g. it created a single link for all types that opened the Not Found page Solution: - in transformApiJson.js, assign the "linkedEnabled" flag per type (instead of per group of types). Further, set "linkedEnabled"=true only if the type is a UI5 symbol. - in the UI5 Demokit ApiRef section, for each displayed control property, bind the view to the "types" model field, to ensure all types are covered. Cherry-picked from UI5/openui5@4cd0cc12d.
1 parent 81b3f91 commit d99a195

File tree

1 file changed

+90
-125
lines changed

1 file changed

+90
-125
lines changed

lib/processors/jsdoc/lib/transformApiJson.js

Lines changed: 90 additions & 125 deletions
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,6 @@ function normalizeToUI5GlobalNotation(sModuleName){
3737
}
3838

3939

40-
function fnCreateTypesArr(sTypes) {
41-
return sTypes.split("|").map(function (sType) {
42-
return { value: sType }
43-
});
44-
}
45-
4640
/**
4741
* Transforms the api.json as created by the JSDoc build into a pre-processed api.json file suitable for the SDK.
4842
*
@@ -116,83 +110,98 @@ function transformer(sInputFile, sOutputFile, sLibraryFile, vDependencyAPIFiles,
116110
return func;
117111
}
118112

119-
/**
120-
* Transforms api.json file
121-
* @param {object} oChainObject chain object
122-
*/
123-
let transformApiJson = function (oChainObject) {
124-
/**
125-
* Check if a type is a built-in type, handling both simple
126-
* and compound types gracefully.
127-
*
128-
* @param {string} type A type to check
129-
* @returns {boolean} true if the type is built-in, otherwise false
130-
*/
131-
function isBuiltInType(type) {
132-
const builtInTypes = formatters._baseTypes;
133-
134-
// Early return if the type is directly in baseTypes
135-
if (builtInTypes.includes(type)) {
136-
return true;
113+
function fnCreateTypesArr(sTypes) {
114+
return sTypes.split("|").map(function (sType) {
115+
const bLinkEnabled = !isBuiltInType(sType) && possibleUI5Symbol(sType.replace(/\[\]$/, ""));
116+
const oTypeInfo = { value: sType };
117+
if (bLinkEnabled) { // default is false, so omit if not required
118+
oTypeInfo.linkEnabled = true;
137119
}
120+
return oTypeInfo;
121+
});
122+
}
138123

139-
// Check if the type is a union type
140-
if (type.includes("|")) {
141-
const unionParts = type.split("|").map(part => part.trim());
142-
return unionParts.every(part => isBuiltInType(part));
143-
}
124+
/**
125+
* Check if a type is a built-in type, handling both simple
126+
* and compound types gracefully.
127+
*
128+
* @param {string} type A type to check
129+
* @returns {boolean} true if the type is built-in, otherwise false
130+
*/
131+
function isBuiltInType(type) {
132+
const builtInTypes = formatters._baseTypes;
144133

145-
// Handle array notation directly
146-
if (type.endsWith("[]")) {
147-
return builtInTypes.includes(type.slice(0, -2));
148-
}
134+
// Early return if the type is directly in baseTypes
135+
if (builtInTypes.includes(type)) {
136+
return true;
137+
}
149138

150-
// Predefined regex patterns for reuse
151-
const arrayRegex = /Array<(.+)>/;
152-
const arrayDotRegex = /Array\.<(.+)>/;
153-
const objectRegex = /Object<([^,]+),([^>]+)>/;
154-
const objectDotRegex = /Object\.<([^,]+),([^>]+)>/;
155-
156-
// Check if the type is a generic Array type
157-
const arrayMatch = arrayRegex.exec(type);
158-
if (arrayMatch) {
159-
const innerType = arrayMatch[1];
160-
return isBuiltInType(innerType);
161-
}
139+
// Check if the type is a union type
140+
if (type.includes("|")) {
141+
const unionParts = type.split("|").map(part => part.trim());
142+
return unionParts.every(part => isBuiltInType(part));
143+
}
162144

163-
const arrayDotMatch = arrayDotRegex.exec(type);
164-
if (arrayDotMatch) {
165-
const innerType = arrayDotMatch[1];
166-
return isBuiltInType(innerType);
167-
}
145+
// Handle array notation directly
146+
if (type.endsWith("[]")) {
147+
return builtInTypes.includes(type.slice(0, -2));
148+
}
168149

169-
// Check if the type is a generic Object type
170-
const objectMatch = objectRegex.exec(type);
171-
if (objectMatch) {
172-
const innerTypes = [objectMatch[1], objectMatch[2]].map(t => t.trim());
173-
return innerTypes.every(innerType => isBuiltInType(innerType));
174-
}
150+
// Predefined regex patterns for reuse
151+
const arrayRegex = /Array<(.+)>/;
152+
const arrayDotRegex = /Array\.<(.+)>/;
153+
const objectRegex = /Object<([^,]+),([^>]+)>/;
154+
const objectDotRegex = /Object\.<([^,]+),([^>]+)>/;
155+
156+
// Check if the type is a generic Array type
157+
const arrayMatch = arrayRegex.exec(type);
158+
if (arrayMatch) {
159+
const innerType = arrayMatch[1];
160+
return isBuiltInType(innerType);
161+
}
175162

176-
const objectDotMatch = objectDotRegex.exec(type);
177-
if (objectDotMatch) {
178-
const innerTypes = [objectDotMatch[1], objectDotMatch[2]].map(t => t.trim());
179-
return innerTypes.every(innerType => isBuiltInType(innerType));
180-
}
163+
const arrayDotMatch = arrayDotRegex.exec(type);
164+
if (arrayDotMatch) {
165+
const innerType = arrayDotMatch[1];
166+
return isBuiltInType(innerType);
167+
}
181168

182-
// Fallback case: if none of the above matched, return false
183-
return false;
169+
// Check if the type is a generic Object type
170+
const objectMatch = objectRegex.exec(type);
171+
if (objectMatch) {
172+
const innerTypes = [objectMatch[1], objectMatch[2]].map(t => t.trim());
173+
return innerTypes.every(innerType => isBuiltInType(innerType));
184174
}
185175

186-
/**
187-
* Heuristically determining if there is a possibility the given input string
188-
* to be a UI5 symbol
189-
* @param {string} sName
190-
* @returns {boolean}
191-
*/
192-
function possibleUI5Symbol(sName) {
193-
return /^[a-zA-Z][a-zA-Z0-9.]*[a-zA-Z0-9]$/.test(sName);
176+
const objectDotMatch = objectDotRegex.exec(type);
177+
if (objectDotMatch) {
178+
const innerTypes = [objectDotMatch[1], objectDotMatch[2]].map(t => t.trim());
179+
return innerTypes.every(innerType => isBuiltInType(innerType));
194180
}
195181

182+
// Fallback case: if none of the above matched, return false
183+
return false;
184+
}
185+
186+
/**
187+
* Heuristically determining if there is a possibility the given input string
188+
* to be a UI5 symbol
189+
* Examples of UI5 symbols:
190+
* -"sap.ui.core.date.UI5Date"
191+
* -"module:sap/ui/core/date/UI5Date"
192+
* @param {string} sName
193+
* @returns {boolean}
194+
*/
195+
function possibleUI5Symbol(sName) {
196+
const ui5SymbolRegex = /^(module:)?[a-zA-Z][a-zA-Z0-9/.]*[a-zA-Z0-9]$/;
197+
return ui5SymbolRegex.test(sName);
198+
}
199+
200+
/**
201+
* Transforms api.json file
202+
* @param {object} oChainObject chain object
203+
*/
204+
let transformApiJson = function (oChainObject) {
196205
// Function is a copy from: LibraryInfo.js => LibraryInfo.prototype._getActualComponent => "match" inline method
197206
function matchComponent(sModuleName, sPattern) {
198207
sModuleName = sModuleName.toLowerCase();
@@ -364,15 +373,7 @@ function transformer(sInputFile, sOutputFile, sLibraryFile, vDependencyAPIFiles,
364373
// Types
365374
oParameter.types = [];
366375
if (oParameter.type) {
367-
let aTypes = oParameter.type.split("|");
368-
369-
for (let i = 0; i < aTypes.length; i++) {
370-
oParameter.types.push({
371-
name: aTypes[i],
372-
linkEnabled: !isBuiltInType(aTypes[i])
373-
});
374-
}
375-
376+
oParameter.types = fnCreateTypesArr(oParameter.type);
376377
// Keep file size in check
377378
delete oParameter.type;
378379
}
@@ -457,17 +458,9 @@ function transformer(sInputFile, sOutputFile, sLibraryFile, vDependencyAPIFiles,
457458
// Type
458459
if (oSymbol.kind !== "enum") { // enum properties don't have an own type
459460
if (oProperty.type) {
460-
oProperty.types = oProperty.type.split("|").map((sType) => {
461-
const oType = {
462-
value: sType
463-
};
464-
// Link Enabled
465-
if (!isBuiltInType(oType.value) && possibleUI5Symbol(oType.value)) {
466-
oType.linkEnabled = true;
467-
oType.href = "api/" + oType.value.replace("[]", "");
468-
}
469-
return oType;
470-
});
461+
oProperty.types = fnCreateTypesArr(oProperty.type);
462+
// Keep file size in check
463+
delete oProperty.type;
471464
}
472465
}
473466

@@ -506,16 +499,13 @@ function transformer(sInputFile, sOutputFile, sLibraryFile, vDependencyAPIFiles,
506499
// Type
507500
if (oProperty.type) {
508501
oProperty.types = fnCreateTypesArr(oProperty.type);
502+
// Keep file size in check
503+
delete oProperty.type;
509504
}
510505

511506
// Description
512507
oProperty.description = formatters.formatDescriptionSince(oProperty.description, oProperty.since);
513508

514-
// Link Enabled
515-
if (!isBuiltInType(oProperty.type)) {
516-
oProperty.linkEnabled = true;
517-
}
518-
519509
// Default value
520510
oProperty.defaultValue = formatters.formatDefaultValue(oProperty.defaultValue);
521511

@@ -756,19 +746,6 @@ function transformer(sInputFile, sOutputFile, sLibraryFile, vDependencyAPIFiles,
756746
if (oMethod.parameters) {
757747
oMethod.parameters.forEach(oParameter => {
758748

759-
// Types
760-
if (oParameter.types) {
761-
oParameter.types.forEach(oType => {
762-
763-
// Link Enabled
764-
if (!isBuiltInType(oType.value) && possibleUI5Symbol(oType.value)) {
765-
oType.linkEnabled = true;
766-
oType.href = "api/" + oType.value.replace("[]", "");
767-
}
768-
769-
});
770-
}
771-
772749
// Default value
773750
oParameter.defaultValue = formatters.formatDefaultValue(oParameter.defaultValue);
774751

@@ -789,19 +766,6 @@ function transformer(sInputFile, sOutputFile, sLibraryFile, vDependencyAPIFiles,
789766
// Description
790767
oMethod.returnValue.description = formatters.formatDescription(oMethod.returnValue.description);
791768

792-
// Types
793-
if (oMethod.returnValue.types) {
794-
oMethod.returnValue.types.forEach(oType => {
795-
796-
// Link Enabled
797-
if (!isBuiltInType(oType.value) && possibleUI5Symbol(oType.value)) {
798-
oType.href = "api/" + oType.value.replace("[]", "");
799-
oType.linkEnabled = true;
800-
}
801-
802-
});
803-
}
804-
805769
}
806770

807771
// Throws
@@ -2129,6 +2093,8 @@ function transformer(sInputFile, sOutputFile, sLibraryFile, vDependencyAPIFiles,
21292093
// Handle types
21302094
if (oProperty.type) {
21312095
oProperty.types = fnCreateTypesArr(oProperty.type);
2096+
// Keep file size in check
2097+
delete oProperty.type;
21322098
}
21332099

21342100
// Phone name - available only for parameters
@@ -2159,11 +2125,10 @@ function transformer(sInputFile, sOutputFile, sLibraryFile, vDependencyAPIFiles,
21592125
oMethod.parameters.forEach(function (oParameter) {
21602126
if (oParameter.type) {
21612127
oParameter.types = fnCreateTypesArr(oParameter.type);
2128+
// Keep file size in check
2129+
delete oParameter.type;
21622130
}
21632131

2164-
// Keep file size in check
2165-
delete oParameter.type;
2166-
21672132
// Add the parameter before the properties
21682133
aParameters.push(oParameter);
21692134

0 commit comments

Comments
 (0)