Skip to content

Commit 7979cc2

Browse files
committed
[INTERNAL] lib/processors/jsdoc: add hierarchical subsection support for API
reference sections Implement support for multi-level section structure in API documentation, allowing sections to contain subsections organized in folder hierarchies. - Sections can now have subsections (e.g., FAQ.md + FAQ/ folder) - Main section content displays as "Overview" subsection - Subsection files automatically loaded from matching directories - Dropdown navigation shows only subsection names, not parent section - The new dynamic sections/subsections are bookmark-able the page automatically scrolls down to the section Example structure: sections/Component/FAQ.md "Overview" subsection sections/Component/FAQ/Example1.md "Example1" subsection sections/Component/FAQ/Example2.md "Example2" subsection JIRA: BGSOFUIPIRIN-6925 Cherry-picked from UI5/openui5@05f6fef35.
1 parent 939d0bd commit 7979cc2

1 file changed

Lines changed: 44 additions & 43 deletions

File tree

lib/processors/jsdoc/lib/transformApiJson.js

Lines changed: 44 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -156,10 +156,9 @@ function transformer(sInputFile, sOutputFile, sLibraryFile, vDependencyAPIFiles,
156156
* UI5Types: ["sap.ui.core.Control"] // skip template if its value is "${0}" (default value)
157157
* }
158158
* @param {string} sComplexType
159-
* @param {Object<string,object>} oAllDependentAPIs Map of entity names to their symbol
160159
* @returns {{template=: string, UI5Types=: string[]}}
161160
*/
162-
function parseUI5Types(sComplexType, oAllDependentAPIs) {
161+
function parseUI5Types(sComplexType) {
163162
let oParsed;
164163
try {
165164
oParsed = typeParser.parseSimpleTypes(sComplexType, isUI5Type);
@@ -177,14 +176,6 @@ function transformer(sInputFile, sOutputFile, sLibraryFile, vDependencyAPIFiles,
177176

178177
if (oParsed.simpleTypes?.length) { // it can be empty if none of the included simple types satisfied the filter function
179178
result.UI5Types = oParsed.simpleTypes;
180-
181-
// check whether the type refers to a typedef and set a marker
182-
if (result.template == null && result.UI5Types.length === 1) {
183-
const symbol = oAllDependentAPIs[result.UI5Types[0]];
184-
if (symbol?.kind === "typedef" && Array.isArray(symbol.properties)) {
185-
result.refersToTypedef = true;
186-
}
187-
}
188179
}
189180

190181
return result;
@@ -275,8 +266,6 @@ function transformer(sInputFile, sOutputFile, sLibraryFile, vDependencyAPIFiles,
275266
* @param {object} oChainObject chain object
276267
*/
277268
const transformApiJson = function (oChainObject) {
278-
const {oAllDependentAPIs} = oChainObject;
279-
280269
// Function is a copy from: LibraryInfo.js => LibraryInfo.prototype._getActualComponent => "match" inline method
281270
function matchComponent(sModuleName, sPattern) {
282271
sModuleName = sModuleName.toLowerCase();
@@ -446,7 +435,7 @@ function transformer(sInputFile, sOutputFile, sLibraryFile, vDependencyAPIFiles,
446435
// Types
447436
oParameter.types = [];
448437
if (oParameter.type) {
449-
oParameter.typeInfo = parseUI5Types(oParameter.type, oAllDependentAPIs);
438+
oParameter.typeInfo = parseUI5Types(oParameter.type);
450439
// Keep file size in check
451440
delete oParameter.type;
452441
}
@@ -531,7 +520,7 @@ function transformer(sInputFile, sOutputFile, sLibraryFile, vDependencyAPIFiles,
531520
// Type
532521
if (oSymbol.kind !== "enum") { // enum properties don't have an own type
533522
if (oProperty.type) {
534-
oProperty.typeInfo = parseUI5Types(oProperty.type, oAllDependentAPIs);
523+
oProperty.typeInfo = parseUI5Types(oProperty.type);
535524
// Keep file size in check
536525
delete oProperty.type;
537526
}
@@ -571,7 +560,7 @@ function transformer(sInputFile, sOutputFile, sLibraryFile, vDependencyAPIFiles,
571560

572561
// Type
573562
if (oProperty.type) {
574-
oProperty.typeInfo = parseUI5Types(oProperty.type, oAllDependentAPIs);
563+
oProperty.typeInfo = parseUI5Types(oProperty.type);
575564
// Keep file size in check
576565
delete oProperty.type;
577566
}
@@ -698,7 +687,7 @@ function transformer(sInputFile, sOutputFile, sLibraryFile, vDependencyAPIFiles,
698687

699688
// Link Enabled
700689
if (oSetting.type) {
701-
oSetting.typeInfo = parseUI5Types(oSetting.type, oAllDependentAPIs);
690+
oSetting.typeInfo = parseUI5Types(oSetting.type);
702691
delete oSetting.type; // Keep file size in check
703692
}
704693

@@ -767,7 +756,7 @@ function transformer(sInputFile, sOutputFile, sLibraryFile, vDependencyAPIFiles,
767756
oEvent.parameters.forEach((oParameter) => {
768757

769758
if (oParameter.type) {
770-
oParameter.typeInfo = parseUI5Types(oParameter.type, oAllDependentAPIs);
759+
oParameter.typeInfo = parseUI5Types(oParameter.type);
771760
delete oParameter.type; // Keep file size in check
772761
}
773762

@@ -800,7 +789,7 @@ function transformer(sInputFile, sOutputFile, sLibraryFile, vDependencyAPIFiles,
800789
if (oSymbol.methods) {
801790

802791
// Pre-process methods
803-
methods.buildMethodsModel(oSymbol.methods, oAllDependentAPIs);
792+
methods.buildMethodsModel(oSymbol.methods);
804793

805794
oSymbol.methods.forEach((oMethod) => {
806795

@@ -961,14 +950,6 @@ function transformer(sInputFile, sOutputFile, sLibraryFile, vDependencyAPIFiles,
961950
}
962951

963952
function loadDependencyLibraryFiles (oChainObject) {
964-
const oAllDependentAPIs = oChainObject.oAllDependentAPIs = Object.create(null);
965-
// add symbols from current library
966-
if (oChainObject.fileData.symbols) {
967-
for (const oSymbol of oChainObject.fileData.symbols) {
968-
oAllDependentAPIs[oSymbol.name] = oSymbol;
969-
}
970-
}
971-
972953
if (!oChainObject.aDependentLibraryFiles) {
973954
return oChainObject;
974955
}
@@ -997,12 +978,6 @@ function transformer(sInputFile, sOutputFile, sLibraryFile, vDependencyAPIFiles,
997978
// In this case we don't add it to the dependency list to skip double iteration.
998979
if (oData && oChainObject.fileData.library !== oData.library) {
999980
oDependentAPIs[oData.library] = oData.symbols;
1000-
1001-
if (Array.isArray(oData.symbols)) {
1002-
for (const oSymbol of oData.symbols) {
1003-
oAllDependentAPIs[oSymbol.name] = oSymbol;
1004-
}
1005-
}
1006981
}
1007982
});
1008983

@@ -1034,15 +1009,41 @@ function transformer(sInputFile, sOutputFile, sLibraryFile, vDependencyAPIFiles,
10341009
const componentDir = path.join(sSectionsDir, sComponentPath);
10351010
if (fs.existsSync(componentDir)) {
10361011
try {
1012+
const dirContents = fs.readdirSync(componentDir, { withFileTypes: true });
1013+
1014+
// Separate files and directories in one pass
1015+
const mdFiles = [];
1016+
const dirNames = new Set();
1017+
dirContents.forEach((dirent) => {
1018+
if (dirent.isFile() && dirent.name.endsWith('.md')) {
1019+
mdFiles.push(dirent.name.replace(/\.md$/, ''));
1020+
} else if (dirent.isDirectory()) {
1021+
dirNames.add(dirent.name);
1022+
}
1023+
});
10371024

1038-
const customSections = fs.readdirSync(componentDir, { withFileTypes: true })
1039-
.filter((dirent) => dirent.isFile() && dirent.name.endsWith('.md'))
1040-
.map((dirent) => dirent.name.replace(/\.md$/, ''));
1041-
1042-
// Store list of available sections for this symbol
1043-
if (customSections.length > 0) {
1044-
symbol.customSections = customSections;
1025+
// Build customSections array with subsection support
1026+
const customSections = mdFiles.map(function(sectionName) {
1027+
// Check if there's a matching directory for this section
1028+
if (dirNames.has(sectionName)) {
1029+
try {
1030+
const subsectionFiles = fs.readdirSync(path.join(componentDir, sectionName), { withFileTypes: true })
1031+
.filter((dirent) => dirent.isFile() && dirent.name.endsWith('.md'))
1032+
.map((dirent) => dirent.name.replace(/\.md$/, ''));
1033+
1034+
if (subsectionFiles.length > 0) {
1035+
return { name: sectionName, hasSubsections: true, subsections: subsectionFiles };
1036+
}
1037+
} catch (error) {
1038+
log.error('Error scanning subsection directory:', path.join(componentDir, sectionName), error);
1039+
}
10451040
}
1041+
return sectionName;
1042+
});
1043+
1044+
if (customSections.length > 0) {
1045+
symbol.customSections = customSections;
1046+
}
10461047

10471048
} catch (error) {
10481049
log.error('Error scanning component sections directory:', componentDir, error);
@@ -2221,7 +2222,7 @@ function transformer(sInputFile, sOutputFile, sLibraryFile, vDependencyAPIFiles,
22212222
* Adjusts methods info so that it can be easily displayed in a table
22222223
* @param aMethods - the methods array initially coming from the server
22232224
*/
2224-
buildMethodsModel: function (aMethods, oAllDependentAPIs) {
2225+
buildMethodsModel: function (aMethods) {
22252226
var fnExtractParameterProperties = function (oParameter, aParameters, iDepth, aPhoneName) {
22262227
if (oParameter.parameterProperties) {
22272228
Object.keys(oParameter.parameterProperties).forEach(function (sProperty) {
@@ -2231,7 +2232,7 @@ function transformer(sInputFile, sOutputFile, sLibraryFile, vDependencyAPIFiles,
22312232

22322233
// Handle types
22332234
if (oProperty.type) {
2234-
oProperty.typeInfo = parseUI5Types(oProperty.type, oAllDependentAPIs);
2235+
oProperty.typeInfo = parseUI5Types(oProperty.type);
22352236
// Keep file size in check
22362237
delete oProperty.type;
22372238
}
@@ -2263,7 +2264,7 @@ function transformer(sInputFile, sOutputFile, sLibraryFile, vDependencyAPIFiles,
22632264
if (oMethod.parameters) {
22642265
oMethod.parameters.forEach(function (oParameter) {
22652266
if (oParameter.type) {
2266-
oParameter.typeInfo = parseUI5Types(oParameter.type, oAllDependentAPIs);
2267+
oParameter.typeInfo = parseUI5Types(oParameter.type);
22672268
// Keep file size in check
22682269
delete oParameter.type;
22692270
}
@@ -2284,7 +2285,7 @@ function transformer(sInputFile, sOutputFile, sLibraryFile, vDependencyAPIFiles,
22842285
// Handle return values
22852286
if (oMethod.returnValue && oMethod.returnValue.type) {
22862287
// Handle types
2287-
oMethod.returnValue.typeInfo = parseUI5Types(oMethod.returnValue.type, oAllDependentAPIs);
2288+
oMethod.returnValue.typeInfo = parseUI5Types(oMethod.returnValue.type);
22882289
}
22892290

22902291
});

0 commit comments

Comments
 (0)