From 0162fb99d84e79cb698a9ad6567453724ec48b22 Mon Sep 17 00:00:00 2001 From: Matthias Osswald Date: Wed, 21 Jan 2026 14:35:58 +0100 Subject: [PATCH 01/15] docs: Add copy of docdash JSDoc template This commit adds a copy of original files from docdash 2.0.2 [1]. [1] https://github.com/clenemt/docdash/tree/bee5d0789068be6a1e01ce02968b23dd021b4fb6 --- REUSE.toml | 5 + .../documentation/jsdoc/docdash/LICENSE.md | 61 ++ .../documentation/jsdoc/docdash/README.md | 10 + .../documentation/jsdoc/docdash/publish.js | 831 ++++++++++++++++++ .../jsdoc/docdash/tmpl/augments.tmpl | 10 + .../jsdoc/docdash/tmpl/container.tmpl | 203 +++++ .../jsdoc/docdash/tmpl/details.tmpl | 152 ++++ .../jsdoc/docdash/tmpl/example.tmpl | 2 + .../jsdoc/docdash/tmpl/examples.tmpl | 13 + .../jsdoc/docdash/tmpl/exceptions.tmpl | 32 + .../jsdoc/docdash/tmpl/layout.tmpl | 102 +++ .../jsdoc/docdash/tmpl/mainpage.tmpl | 15 + .../jsdoc/docdash/tmpl/members.tmpl | 38 + .../jsdoc/docdash/tmpl/method.tmpl | 132 +++ .../jsdoc/docdash/tmpl/modifies.tmpl | 14 + .../jsdoc/docdash/tmpl/namespace.tmpl | 133 +++ .../jsdoc/docdash/tmpl/params.tmpl | 132 +++ .../jsdoc/docdash/tmpl/properties.tmpl | 107 +++ .../jsdoc/docdash/tmpl/returns.tmpl | 19 + .../jsdoc/docdash/tmpl/source.tmpl | 8 + .../jsdoc/docdash/tmpl/tutorial.tmpl | 19 + .../jsdoc/docdash/tmpl/type.tmpl | 26 + 22 files changed, 2064 insertions(+) create mode 100644 internal/documentation/jsdoc/docdash/LICENSE.md create mode 100644 internal/documentation/jsdoc/docdash/README.md create mode 100644 internal/documentation/jsdoc/docdash/publish.js create mode 100644 internal/documentation/jsdoc/docdash/tmpl/augments.tmpl create mode 100644 internal/documentation/jsdoc/docdash/tmpl/container.tmpl create mode 100644 internal/documentation/jsdoc/docdash/tmpl/details.tmpl create mode 100644 internal/documentation/jsdoc/docdash/tmpl/example.tmpl create mode 100644 internal/documentation/jsdoc/docdash/tmpl/examples.tmpl create mode 100644 internal/documentation/jsdoc/docdash/tmpl/exceptions.tmpl create mode 100644 internal/documentation/jsdoc/docdash/tmpl/layout.tmpl create mode 100644 internal/documentation/jsdoc/docdash/tmpl/mainpage.tmpl create mode 100644 internal/documentation/jsdoc/docdash/tmpl/members.tmpl create mode 100644 internal/documentation/jsdoc/docdash/tmpl/method.tmpl create mode 100644 internal/documentation/jsdoc/docdash/tmpl/modifies.tmpl create mode 100644 internal/documentation/jsdoc/docdash/tmpl/namespace.tmpl create mode 100644 internal/documentation/jsdoc/docdash/tmpl/params.tmpl create mode 100644 internal/documentation/jsdoc/docdash/tmpl/properties.tmpl create mode 100644 internal/documentation/jsdoc/docdash/tmpl/returns.tmpl create mode 100644 internal/documentation/jsdoc/docdash/tmpl/source.tmpl create mode 100644 internal/documentation/jsdoc/docdash/tmpl/tutorial.tmpl create mode 100644 internal/documentation/jsdoc/docdash/tmpl/type.tmpl diff --git a/REUSE.toml b/REUSE.toml index 2fc7b1cffda..fbeaa448342 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -10,3 +10,8 @@ precedence = "aggregate" SPDX-FileCopyrightText = "2025 SAP SE or an SAP affiliate company and UI5 CLI contributors" SPDX-License-Identifier = "Apache-2.0" +[[annotations]] +path = "internal/documentation/jsdoc/docdash/**" +precedence = "aggregate" +SPDX-FileCopyrightText = "Copyright (c) 2016 Clement Moron clenemt@gmail.com and the contributors to docdash" +SPDX-License-Identifier = "Apache-2.0" diff --git a/internal/documentation/jsdoc/docdash/LICENSE.md b/internal/documentation/jsdoc/docdash/LICENSE.md new file mode 100644 index 00000000000..ff66af581b1 --- /dev/null +++ b/internal/documentation/jsdoc/docdash/LICENSE.md @@ -0,0 +1,61 @@ +# License + +Docdash is free software, licensed under the Apache License, Version 2.0 (the +"License"). Commercial and non-commercial use are permitted in compliance with +the License. + +Copyright (c) 2016 Clement Moron and the +[contributors to docdash](https://github.com/clenemt/docdash/graphs/contributors). +All rights reserved. + +You may obtain a copy of the License at: +http://www.apache.org/licenses/LICENSE-2.0 + +In addition, a copy of the License is included with this distribution. + +As stated in Section 7, "Disclaimer of Warranty," of the License: + +> Licensor provides the Work (and each Contributor provides its Contributions) +> on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +> express or implied, including, without limitation, any warranties or +> conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A +> PARTICULAR PURPOSE. You are solely responsible for determining the +> appropriateness of using or redistributing the Work and assume any risks +> associated with Your exercise of permissions under this License. + +The source code for docdash is available at: +https://github.com/clenemt/docdash + +# Third-Party Software + +Docdash includes or depends upon the following third-party software, either in +whole or in part. Each third-party software package is provided under its own +license. + +## JSDoc 3 + +JSDoc 3 is free software, licensed under the Apache License, Version 2.0 (the +"License"). Commercial and non-commercial use are permitted in compliance with +the License. + +Copyright (c) 2011-2016 Michael Mathews and the +[contributors to JSDoc](https://github.com/jsdoc3/jsdoc/graphs/contributors). +All rights reserved. + +You may obtain a copy of the License at: +http://www.apache.org/licenses/LICENSE-2.0 + +In addition, a copy of the License is included with this distribution. + +As stated in Section 7, "Disclaimer of Warranty," of the License: + +> Licensor provides the Work (and each Contributor provides its Contributions) +> on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +> express or implied, including, without limitation, any warranties or +> conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A +> PARTICULAR PURPOSE. You are solely responsible for determining the +> appropriateness of using or redistributing the Work and assume any risks +> associated with Your exercise of permissions under this License. + +The source code for JSDoc 3 is available at: +https://github.com/jsdoc3/jsdoc diff --git a/internal/documentation/jsdoc/docdash/README.md b/internal/documentation/jsdoc/docdash/README.md new file mode 100644 index 00000000000..b91027191cf --- /dev/null +++ b/internal/documentation/jsdoc/docdash/README.md @@ -0,0 +1,10 @@ +# Docdash + +This folder includes files from [docdash 2.0.2](https://github.com/clenemt/docdash/tree/bee5d0789068be6a1e01ce02968b23dd021b4fb6), which is a documentation template for JSDoc. + +## Contributors + +Thanks to [lodash](https://lodash.com) and [minami](https://github.com/nijikokun/minami). + +## License +Licensed under the Apache License, version 2.0. (see [Apache-2.0](LICENSE.md)). diff --git a/internal/documentation/jsdoc/docdash/publish.js b/internal/documentation/jsdoc/docdash/publish.js new file mode 100644 index 00000000000..5894d832722 --- /dev/null +++ b/internal/documentation/jsdoc/docdash/publish.js @@ -0,0 +1,831 @@ +/*global env: true */ +'use strict'; + +var doop = require('jsdoc/util/doop'); +var fs = require('jsdoc/fs'); +var helper = require('jsdoc/util/templateHelper'); +var logger = require('jsdoc/util/logger'); +var path = require('jsdoc/path'); +var taffy = require('@jsdoc/salty').taffy; +var template = require('jsdoc/template'); +var util = require('util'); + +var htmlsafe = helper.htmlsafe; +var linkto = helper.linkto; +var resolveAuthorLinks = helper.resolveAuthorLinks; +var scopeToPunc = helper.scopeToPunc; +var hasOwnProp = Object.prototype.hasOwnProperty; + +var data; +var view; + +var outdir = path.normalize(env.opts.destination); + +function copyFile(source, target, cb) { + var cbCalled = false; + + var rd = fs.createReadStream(source); + rd.on("error", function(err) { + done(err); + }); + var wr = fs.createWriteStream(target); + wr.on("error", function(err) { + done(err); + }); + wr.on("close", function(ex) { + done(); + }); + rd.pipe(wr); + + function done(err) { + if (!cbCalled) { + cb(err); + cbCalled = true; + } + } +} + +function find(spec) { + return helper.find(data, spec); +} + +function tutoriallink(tutorial) { + return helper.toTutorial(tutorial, null, { tag: 'em', classname: 'disabled', prefix: 'Tutorial: ' }); +} + +function getAncestorLinks(doclet) { + return helper.getAncestorLinks(data, doclet); +} + +function hashToLink(doclet, hash) { + if ( !/^(#.+)/.test(hash) ) { return hash; } + + var url = helper.createLink(doclet); + + url = url.replace(/(#.+|$)/, hash); + return '' + hash + ''; +} + +function needsSignature(doclet) { + var needsSig = false; + + // function and class definitions always get a signature + if (doclet.kind === 'function' || doclet.kind === 'class' && !doclet.hideconstructor) { + needsSig = true; + } + // typedefs that contain functions get a signature, too + else if (doclet.kind === 'typedef' && doclet.type && doclet.type.names && + doclet.type.names.length) { + for (var i = 0, l = doclet.type.names.length; i < l; i++) { + if (doclet.type.names[i].toLowerCase() === 'function') { + needsSig = true; + break; + } + } + } + // and namespaces that are functions get a signature (but finding them is a + // bit messy) + else if (doclet.kind === 'namespace' && doclet.meta && doclet.meta.code && + doclet.meta.code.type && doclet.meta.code.type.match(/[Ff]unction/)) { + needsSig = true; + } + + return needsSig; +} + +function getSignatureAttributes(item) { + var attributes = []; + + if (item.optional) { + attributes.push('opt'); + } + + if (item.nullable === true) { + attributes.push('nullable'); + } + else if (item.nullable === false) { + attributes.push('non-null'); + } + + return attributes; +} + +function updateItemName(item) { + var attributes = getSignatureAttributes(item); + var itemName = item.name || ''; + + if (item.variable) { + itemName = '…' + itemName; + } + + if (attributes && attributes.length) { + itemName = util.format( '%s%s', itemName, + attributes.join(', ') ); + } + + return itemName; +} + +function addParamAttributes(params) { + return params.filter(function(param) { + return param.name && param.name.indexOf('.') === -1; + }).map(updateItemName); +} + +function buildItemTypeStrings(item) { + var types = []; + + if (item && item.type && item.type.names) { + item.type.names.forEach(function(name) { + types.push( linkto(name, htmlsafe(name)) ); + }); + } + + return types; +} + +function buildAttribsString(attribs) { + var attribsString = ''; + + if (attribs && attribs.length) { + attribsString = htmlsafe( util.format('(%s) ', attribs.join(', ')) ); + } + + return attribsString; +} + +function addNonParamAttributes(items) { + var types = []; + + items.forEach(function(item) { + types = types.concat( buildItemTypeStrings(item) ); + }); + + return types; +} + +function addSignatureParams(f) { + var params = f.params ? addParamAttributes(f.params) : []; + f.signature = util.format( '%s(%s)', (f.signature || ''), params.join(', ') ); +} + +function addSignatureReturns(f) { + var attribs = []; + var attribsString = ''; + var returnTypes = []; + var returnTypesString = ''; + var source = f.yields || f.returns; + + // jam all the return-type attributes into an array. this could create odd results (for example, + // if there are both nullable and non-nullable return types), but let's assume that most people + // who use multiple @return tags aren't using Closure Compiler type annotations, and vice-versa. + if (source) { + source.forEach(function(item) { + helper.getAttribs(item).forEach(function(attrib) { + if (attribs.indexOf(attrib) === -1) { + attribs.push(attrib); + } + }); + }); + + attribsString = buildAttribsString(attribs); + } + + if (source) { + returnTypes = addNonParamAttributes(source); + } + if (returnTypes.length) { + returnTypesString = util.format( ' → %s{%s}', attribsString, returnTypes.join('|') ); + } + + f.signature = '' + (f.signature || '') + '' + + '' + returnTypesString + ''; +} + +function addSignatureTypes(f) { + var types = f.type ? buildItemTypeStrings(f) : []; + + f.signature = (f.signature || '') + '' + + (types.length ? ' :' + types.join('|') : '') + ''; +} + +function addAttribs(f) { + var attribs = helper.getAttribs(f); + var attribsString = buildAttribsString(attribs); + if (attribsString && attribsString.length) { + f.attribs = util.format('%s', attribsString); + } + else { + f.attribs = util.format('%s', attribsString); + } +} + +function shortenPaths(files, commonPrefix) { + Object.keys(files).forEach(function(file) { + files[file].shortened = files[file].resolved.replace(commonPrefix, '') + // always use forward slashes + .replace(/\\/g, '/'); + }); + + return files; +} + +function getPathFromDoclet(doclet) { + if (!doclet.meta) { + return null; + } + + return doclet.meta.path && doclet.meta.path !== 'null' ? + path.join(doclet.meta.path, doclet.meta.filename) : + doclet.meta.filename; +} + +function generate(type, title, docs, filename, resolveLinks) { + resolveLinks = resolveLinks === false ? false : true; + + var docData = { + type: type, + title: title, + docs: docs + }; + + var outpath = path.join(outdir, filename), + html = view.render('container.tmpl', docData); + + if (resolveLinks) { + html = helper.resolveLinks(html); // turn {@link foo} into foo + } + + fs.writeFileSync(outpath, html, 'utf8'); +} + +function generateSourceFiles(sourceFiles, encoding) { + encoding = encoding || 'utf8'; + Object.keys(sourceFiles).forEach(function(file) { + var source; + // links are keyed to the shortened path in each doclet's `meta.shortpath` property + var sourceOutfile = helper.getUniqueFilename(sourceFiles[file].shortened); + helper.registerLink(sourceFiles[file].shortened, sourceOutfile); + + try { + source = { + kind: 'source', + code: helper.htmlsafe( fs.readFileSync(sourceFiles[file].resolved, encoding) ) + }; + } + catch(e) { + logger.error('Error while generating source file %s: %s', file, e.message); + } + + generate('Source', sourceFiles[file].shortened, [source], sourceOutfile, false); + }); +} + +/** + * Look for classes or functions with the same name as modules (which indicates that the module + * exports only that class or function), then attach the classes or functions to the `module` + * property of the appropriate module doclets. The name of each class or function is also updated + * for display purposes. This function mutates the original arrays. + * + * @private + * @param {Array.} doclets - The array of classes and functions to + * check. + * @param {Array.} modules - The array of module doclets to search. + */ +function attachModuleSymbols(doclets, modules) { + var symbols = {}; + + // build a lookup table + doclets.forEach(function(symbol) { + symbols[symbol.longname] = symbols[symbol.longname] || []; + symbols[symbol.longname].push(symbol); + }); + + return modules.map(function(module) { + if (symbols[module.longname]) { + module.modules = symbols[module.longname] + // Only show symbols that have a description. Make an exception for classes, because + // we want to show the constructor-signature heading no matter what. + .filter(function(symbol) { + return symbol.description || symbol.kind === 'class'; + }) + .map(function(symbol) { + symbol = doop(symbol); + + if (symbol.kind === 'class' || symbol.kind === 'function' && !symbol.hideconstructor) { + symbol.name = symbol.name.replace('module:', '(require("') + '"))'; + } + + return symbol; + }); + } + }); +} + +function buildMemberNav(items, itemHeading, itemsSeen, linktoFn) { + var nav = ''; + + if (items && items.length) { + var itemsNav = ''; + var docdash = env && env.conf && env.conf.docdash || {}; + var level = typeof docdash.navLevel === 'number' && docdash.navLevel >= 0 ? + docdash.navLevel : + Infinity; + + items.forEach(function(item) { + var displayName; + var methods = find({kind:'function', memberof: item.longname}); + var members = find({kind:'member', memberof: item.longname}); + var conf = env && env.conf || {}; + var classes = ''; + + // show private class? + if (docdash.private === false && item.access === 'private') return; + + // depth to show? + if (item.ancestors && item.ancestors.length > level) { + classes += 'level-hide'; + } + + classes = classes ? ' class="'+ classes + '"' : ''; + itemsNav += ''; + if ( !hasOwnProp.call(item, 'longname') ) { + itemsNav += linktoFn('', item.name); + } else if ( !hasOwnProp.call(itemsSeen, item.longname) ) { + if (conf.templates.default.useLongnameInNav) { + displayName = item.longname; + } else { + displayName = item.name; + } + itemsNav += linktoFn(item.longname, displayName.replace(/\b(module|event):/g, '')); + + if (docdash.static && members.find(function (m) { return m.scope === 'static'; } )) { + itemsNav += "
    "; + + members.forEach(function (member) { + if (!member.scope === 'static') return; + itemsNav += "
    • ' + itemsNav + '
    '; + } + else { + nav += '

    ' + itemHeading + '

      ' + itemsNav + '
    '; + } + } + } + + return nav; +} + +function linktoTutorial(longName, name) { + return tutoriallink(name); +} + +function linktoExternal(longName, name) { + return linkto(longName, name.replace(/(^"|"$)/g, '')); +} + +/** + * Create the navigation sidebar. + * @param {object} members The members that will be used to create the sidebar. + * @param {array} members.classes + * @param {array} members.externals + * @param {array} members.globals + * @param {array} members.mixins + * @param {array} members.modules + * @param {array} members.namespaces + * @param {array} members.tutorials + * @param {array} members.events + * @param {array} members.interfaces + * @return {string} The HTML for the navigation sidebar. + */ + +function buildNav(members) { + var nav = '

    Home

    '; + var seen = {}; + var seenTutorials = {}; + var docdash = env && env.conf && env.conf.docdash || {}; + if(docdash.menu){ + for(var menu in docdash.menu){ + nav += '

    '; + } + } + + function buildMemberNavGlobal() { + var ret = ""; + if (members.globals.length) { + var globalNav = ''; + + members.globals.forEach(function(g) { + if ( (docdash.typedefs || g.kind !== 'typedef') && !hasOwnProp.call(seen, g.longname) ) { + globalNav += '
  • ' + linkto(g.longname, g.name) + '
  • '; + } + seen[g.longname] = true; + }); + + if (!globalNav) { + // turn the heading into a link so you can actually get to the global page + ret += '

    ' + linkto('global', 'Global') + '

    '; + } + else { + if(docdash.collapse === "top") { + ret += '

    Global

      ' + globalNav + '
    '; + } + else { + ret += '

    Global

      ' + globalNav + '
    '; + } + } + } + return ret; + } + var defaultOrder = [ + 'Classes', 'Modules', 'Externals', 'Events', 'Namespaces', 'Mixins', 'Tutorials', 'Interfaces', 'Global' + ]; + var order = docdash.sectionOrder || defaultOrder; + var sections = { + Classes: buildMemberNav(members.classes, 'Classes', seen, linkto), + Modules: buildMemberNav(members.modules, 'Modules', {}, linkto), + Externals: buildMemberNav(members.externals, 'Externals', seen, linktoExternal), + Events: buildMemberNav(members.events, 'Events', seen, linkto), + Namespaces: buildMemberNav(members.namespaces, 'Namespaces', seen, linkto), + Mixins: buildMemberNav(members.mixins, 'Mixins', seen, linkto), + Tutorials: buildMemberNav(members.tutorials, 'Tutorials', seenTutorials, linktoTutorial), + Interfaces: buildMemberNav(members.interfaces, 'Interfaces', seen, linkto), + Global: buildMemberNavGlobal() + }; + order.forEach(member => nav += sections[member]); + + return nav; +} + +/** + @param {TAFFY} taffyData See . + @param {object} opts + @param {Tutorial} tutorials + */ +exports.publish = function(taffyData, opts, tutorials) { + var docdash = env && env.conf && env.conf.docdash || {}; + data = taffyData; + + var conf = env.conf.templates || {}; + conf.default = conf.default || {}; + + var templatePath = path.normalize(opts.template); + view = new template.Template( path.join(templatePath, 'tmpl') ); + + // claim some special filenames in advance, so the All-Powerful Overseer of Filename Uniqueness + // doesn't try to hand them out later + var indexUrl = helper.getUniqueFilename('index'); + // don't call registerLink() on this one! 'index' is also a valid longname + + var globalUrl = helper.getUniqueFilename('global'); + helper.registerLink('global', globalUrl); + + // set up templating + view.layout = conf.default.layoutFile ? + path.getResourcePath(path.dirname(conf.default.layoutFile), + path.basename(conf.default.layoutFile) ) : + 'layout.tmpl'; + + // set up tutorials for helper + helper.setTutorials(tutorials); + + data = helper.prune(data); + + docdash.sort !== false && data.sort('longname, version, since'); + helper.addEventListeners(data); + + var sourceFiles = {}; + var sourceFilePaths = []; + data().each(function(doclet) { + if(docdash.removeQuotes){ + if(docdash.removeQuotes === "all"){ + if(doclet.name){ + doclet.name = doclet.name.replace(/"/g, ''); + doclet.name = doclet.name.replace(/'/g, ''); + } + if(doclet.longname){ + doclet.longname = doclet.longname.replace(/"/g, ''); + doclet.longname = doclet.longname.replace(/'/g, ''); + } + } + else if(docdash.removeQuotes === "trim"){ + if(doclet.name){ + doclet.name = doclet.name.replace(/^"(.*)"$/, '$1'); + doclet.name = doclet.name.replace(/^'(.*)'$/, '$1'); + } + if(doclet.longname){ + doclet.longname = doclet.longname.replace(/^"(.*)"$/, '$1'); + doclet.longname = doclet.longname.replace(/^'(.*)'$/, '$1'); + } + } + } + doclet.attribs = ''; + + if (doclet.examples) { + doclet.examples = doclet.examples.map(function(example) { + var caption, code; + + if (example && example.match(/^\s*([\s\S]+?)<\/caption>(\s*[\n\r])([\s\S]+)$/i)) { + caption = RegExp.$1; + code = RegExp.$3; + } + + return { + caption: caption || '', + code: code || example || '' + }; + }); + } + if (doclet.see) { + doclet.see.forEach(function(seeItem, i) { + doclet.see[i] = hashToLink(doclet, seeItem); + }); + } + + // build a list of source files + var sourcePath; + if (doclet.meta) { + sourcePath = getPathFromDoclet(doclet); + sourceFiles[sourcePath] = { + resolved: sourcePath, + shortened: null + }; + if (sourceFilePaths.indexOf(sourcePath) === -1) { + sourceFilePaths.push(sourcePath); + } + } + }); + + // update outdir if necessary, then create outdir + var packageInfo = ( find({kind: 'package'}) || [] ) [0]; + if (packageInfo) { + var subdirs = [outdir]; + + if (packageInfo.name) { + var packageName = packageInfo.name.split('/'); + + if (packageName.length > 1 && docdash.scopeInOutputPath !== false) { + subdirs.push(packageName[0]); + } + + if (docdash.nameInOutputPath !== false) { + subdirs.push((packageName.length > 1 ? packageName[1] : packageName[0])); + } + } + + if (packageInfo.version && docdash.versionInOutputPath !== false) { + subdirs.push(packageInfo.version); + } + + if (subdirs.length > 1) { + outdir = path.join.apply(null, subdirs); + } + } + + fs.mkPath(outdir); + + // copy the template's static files to outdir + var fromDir = path.join(templatePath, 'static'); + var staticFiles = fs.ls(fromDir, 3); + + staticFiles.forEach(function(fileName) { + var toDir = fs.toDir( fileName.replace(fromDir, outdir) ); + fs.mkPath(toDir); + copyFile(fileName, path.join(toDir, path.basename(fileName)), function(err){if(err) console.err(err);}); + }); + + // copy user-specified static files to outdir + var staticFilePaths; + var staticFileFilter; + var staticFileScanner; + if (conf.default.staticFiles) { + // The canonical property name is `include`. We accept `paths` for backwards compatibility + // with a bug in JSDoc 3.2.x. + staticFilePaths = conf.default.staticFiles.include || + conf.default.staticFiles.paths || + []; + staticFileFilter = new (require('jsdoc/src/filter')).Filter(conf.default.staticFiles); + staticFileScanner = new (require('jsdoc/src/scanner')).Scanner(); + + staticFilePaths.forEach(function(filePath) { + var extraStaticFiles = staticFileScanner.scan([filePath], 10, staticFileFilter); + + extraStaticFiles.forEach(function(fileName) { + var sourcePath = fs.toDir(filePath); + var toDir = fs.toDir( fileName.replace(sourcePath, outdir) ); + fs.mkPath(toDir); + copyFile(fileName, path.join(toDir, path.basename(fileName)), function(err){if(err) console.err(err);}); + }); + }); + } + + if (sourceFilePaths.length) { + sourceFiles = shortenPaths( sourceFiles, path.commonPrefix(sourceFilePaths) ); + } + data().each(function(doclet) { + var url = helper.createLink(doclet); + if (docdash.noURLEncode) { + url = decodeURI(url); + } + helper.registerLink(doclet.longname, url); + + // add a shortened version of the full path + var docletPath; + if (doclet.meta) { + docletPath = getPathFromDoclet(doclet); + docletPath = sourceFiles[docletPath].shortened; + if (docletPath) { + doclet.meta.shortpath = docletPath; + } + } + }); + + data().each(function(doclet) { + var url = helper.longnameToUrl[doclet.longname]; + + if (url.indexOf('#') > -1) { + doclet.id = helper.longnameToUrl[doclet.longname].split(/#/).pop(); + } + else { + doclet.id = doclet.name; + } + + if ( needsSignature(doclet) ) { + addSignatureParams(doclet); + addSignatureReturns(doclet); + addAttribs(doclet); + } + }); + + // do this after the urls have all been generated + data().each(function(doclet) { + doclet.ancestors = getAncestorLinks(doclet); + + if (doclet.kind === 'member') { + addSignatureTypes(doclet); + addAttribs(doclet); + } + + if (doclet.kind === 'constant') { + addSignatureTypes(doclet); + addAttribs(doclet); + doclet.kind = 'member'; + } + }); + + var members = helper.getMembers(data); + members.tutorials = tutorials.children; + + // output pretty-printed source files by default + var outputSourceFiles = conf.default && conf.default.outputSourceFiles !== false + ? true + : false; + + // add template helpers + view.find = find; + view.linkto = linkto; + view.resolveAuthorLinks = resolveAuthorLinks; + view.tutoriallink = tutoriallink; + view.htmlsafe = htmlsafe; + view.outputSourceFiles = outputSourceFiles; + + // once for all + view.nav = buildNav(members); + attachModuleSymbols( find({ longname: {left: 'module:'} }), members.modules ); + + // generate the pretty-printed source files first so other pages can link to them + if (outputSourceFiles) { + generateSourceFiles(sourceFiles, opts.encoding); + } + + if (members.globals.length) { + generate('', 'Global', [{kind: 'globalobj'}], globalUrl); + } + + // index page displays information from package.json and lists files + var files = find({kind: 'file'}); + var packages = find({kind: 'package'}); + + generate('', 'Home', + packages.concat( + [{kind: 'mainpage', readme: opts.readme, longname: (opts.mainpagetitle) ? opts.mainpagetitle : 'Main Page'}] + ).concat(files), + indexUrl); + + // common nav generation, no need for templating here, we already have full html + if (docdash.commonNav) { + fs.writeFileSync(path.join(outdir, 'nav.inc.html'), view.nav, 'utf8'); + } + + // set up the lists that we'll use to generate pages + var classes = taffy(members.classes); + var modules = taffy(members.modules); + var namespaces = taffy(members.namespaces); + var mixins = taffy(members.mixins); + var externals = taffy(members.externals); + var interfaces = taffy(members.interfaces); + + Object.keys(helper.longnameToUrl).forEach(function(longname) { + var myModules = helper.find(modules, {longname: longname}); + if (myModules.length) { + generate('Module', myModules[0].name, myModules, helper.longnameToUrl[longname]); + } + + var myClasses = helper.find(classes, {longname: longname}); + if (myClasses.length) { + generate('Class', myClasses[0].name, myClasses, helper.longnameToUrl[longname]); + } + + var myNamespaces = helper.find(namespaces, {longname: longname}); + if (myNamespaces.length) { + generate('Namespace', myNamespaces[0].name, myNamespaces, helper.longnameToUrl[longname]); + } + + var myMixins = helper.find(mixins, {longname: longname}); + if (myMixins.length) { + generate('Mixin', myMixins[0].name, myMixins, helper.longnameToUrl[longname]); + } + + var myExternals = helper.find(externals, {longname: longname}); + if (myExternals.length) { + generate('External', myExternals[0].name, myExternals, helper.longnameToUrl[longname]); + } + + var myInterfaces = helper.find(interfaces, {longname: longname}); + if (myInterfaces.length) { + generate('Interface', myInterfaces[0].name, myInterfaces, helper.longnameToUrl[longname]); + } + }); + + // TODO: move the tutorial functions to templateHelper.js + function generateTutorial(title, tutorial, filename) { + var tutorialData = { + title: title, + header: tutorial.title, + content: tutorial.parse(), + children: tutorial.children + }; + + var tutorialPath = path.join(outdir, filename); + var html = view.render('tutorial.tmpl', tutorialData); + + // yes, you can use {@link} in tutorials too! + html = helper.resolveLinks(html); // turn {@link foo} into
    foo + fs.writeFileSync(tutorialPath, html, 'utf8'); + } + + // tutorials can have only one parent so there is no risk for loops + function saveChildren(node) { + node.children.forEach(function(child) { + generateTutorial('Tutorial: ' + child.title, child, helper.tutorialToUrl(child.name)); + saveChildren(child); + }); + } + + saveChildren(tutorials); +}; diff --git a/internal/documentation/jsdoc/docdash/tmpl/augments.tmpl b/internal/documentation/jsdoc/docdash/tmpl/augments.tmpl new file mode 100644 index 00000000000..446d28aa537 --- /dev/null +++ b/internal/documentation/jsdoc/docdash/tmpl/augments.tmpl @@ -0,0 +1,10 @@ + + + +
      +
    • +
    + diff --git a/internal/documentation/jsdoc/docdash/tmpl/container.tmpl b/internal/documentation/jsdoc/docdash/tmpl/container.tmpl new file mode 100644 index 00000000000..0621f94c095 --- /dev/null +++ b/internal/documentation/jsdoc/docdash/tmpl/container.tmpl @@ -0,0 +1,203 @@ + + + + + + + + + +
    + +
    + +

    + + + + Namespace + + + + + +

    + +
    + + + + +
    + + + +
    + +
    + +
    + + +
    + + + + + + + + + + + + + +
    + + + +

    Example 1? 's':'' ?>

    + + + +
    + + + +

    Extends

    + + + + + +

    Requires

    + +
      +
    • +
    + + + +

    Classes

    + +
    +
    +
    +
    + + + +

    Interfaces

    + +
    +
    +
    +
    + + + +

    Mixins

    + +
    +
    +
    +
    + + + +

    Namespaces

    + +
    +
    +
    +
    + + + +

    Members

    + + + + + + + +

    Methods

    + + + + + + + +

    Type Definitions

    + + + + + + + + + +

    Events

    + + + + + +
    + +
    + + + diff --git a/internal/documentation/jsdoc/docdash/tmpl/details.tmpl b/internal/documentation/jsdoc/docdash/tmpl/details.tmpl new file mode 100644 index 00000000000..32686cf31ac --- /dev/null +++ b/internal/documentation/jsdoc/docdash/tmpl/details.tmpl @@ -0,0 +1,152 @@ +" + data.defaultvalue + ""; + defaultObjectClass = ' class="object-value"'; +} +?> + +
    + +
    Description:
    +
    + + + +
    Source:
    +
    • + , +
    + + + +
    Version:
    +
    + + + +
    Since:
    +
    + + + +
    Inherited From:
    +
    • + +
    + + + +
    Overrides:
    +
    • + +
    + + + +
    Implementations:
    +
      + +
    • + +
    + + + +
    Implements:
    +
      + +
    • + +
    + + + +
    Mixes In:
    + +
      + +
    • + +
    + + + +
    Deprecated:
    • Yes
    + + + +
    Author:
    +
    +
      +
    • +
    +
    + + + + + + + + +
    License:
    +
    + + + +
    Default Value:
    +
      + > +
    + + + +
    Tutorials:
    +
    +
      +
    • +
    +
    + + + +
    See:
    +
    +
      +
    • +
    +
    + + + +
    To Do:
    +
    +
      +
    • +
    +
    + +
    + + + +
    Properties:
    + + + + diff --git a/internal/documentation/jsdoc/docdash/tmpl/example.tmpl b/internal/documentation/jsdoc/docdash/tmpl/example.tmpl new file mode 100644 index 00000000000..e87caa5b72c --- /dev/null +++ b/internal/documentation/jsdoc/docdash/tmpl/example.tmpl @@ -0,0 +1,2 @@ + +
    diff --git a/internal/documentation/jsdoc/docdash/tmpl/examples.tmpl b/internal/documentation/jsdoc/docdash/tmpl/examples.tmpl new file mode 100644 index 00000000000..04d975e96dc --- /dev/null +++ b/internal/documentation/jsdoc/docdash/tmpl/examples.tmpl @@ -0,0 +1,13 @@ + +

    + +
    + \ No newline at end of file diff --git a/internal/documentation/jsdoc/docdash/tmpl/exceptions.tmpl b/internal/documentation/jsdoc/docdash/tmpl/exceptions.tmpl new file mode 100644 index 00000000000..8d0f9d5e1eb --- /dev/null +++ b/internal/documentation/jsdoc/docdash/tmpl/exceptions.tmpl @@ -0,0 +1,32 @@ + + +
    +
    +
    + +
    +
    +
    +
    +
    +
    + Type +
    +
    + +
    +
    +
    +
    +
    + +
    + + + + + +
    + diff --git a/internal/documentation/jsdoc/docdash/tmpl/layout.tmpl b/internal/documentation/jsdoc/docdash/tmpl/layout.tmpl new file mode 100644 index 00000000000..8fb37f4ca6d --- /dev/null +++ b/internal/documentation/jsdoc/docdash/tmpl/layout.tmpl @@ -0,0 +1,102 @@ + + + + + + <?js= title ?> - <?js= ((env.conf.docdash.meta && env.conf.docdash.meta.title) || "Documentation") ?> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +

    + + + + + +
    + + + +
    + +
    + +
    + Documentation generated by JSDoc on using the docdash theme. +
    + + + + + + + + + + + + + + + + + diff --git a/internal/documentation/jsdoc/docdash/tmpl/mainpage.tmpl b/internal/documentation/jsdoc/docdash/tmpl/mainpage.tmpl new file mode 100644 index 00000000000..461bf013aa6 --- /dev/null +++ b/internal/documentation/jsdoc/docdash/tmpl/mainpage.tmpl @@ -0,0 +1,15 @@ + + + +
    +

    +
    + + +
    +
    +
    + diff --git a/internal/documentation/jsdoc/docdash/tmpl/members.tmpl b/internal/documentation/jsdoc/docdash/tmpl/members.tmpl new file mode 100644 index 00000000000..3cc698ca965 --- /dev/null +++ b/internal/documentation/jsdoc/docdash/tmpl/members.tmpl @@ -0,0 +1,38 @@ + +

    + + +

    + + + + + +
    + +
    + + + +
    Type:
    +
      +
    • + +
    • +
    + + + +
    Fires:
    +
      +
    • +
    + + + +
    Example 1? 's':'' ?>
    + + diff --git a/internal/documentation/jsdoc/docdash/tmpl/method.tmpl b/internal/documentation/jsdoc/docdash/tmpl/method.tmpl new file mode 100644 index 00000000000..a51d519a013 --- /dev/null +++ b/internal/documentation/jsdoc/docdash/tmpl/method.tmpl @@ -0,0 +1,132 @@ + + + +

    Constructor

    + + +

    + + +

    + + + + + + +
    Extends:
    + + + + +
    Type:
    +
      +
    • + +
    • +
    + + + +
    This:
    +
    + + + +
    Example 1? 's':'' ?>
    + + + + +
    Parameters:
    + + + + +
    Requires:
    +
      +
    • +
    + + + +
    Fires:
    +
      +
    • +
    + + + +
    Listens to Events:
    +
      +
    • +
    + + + +
    Listeners of This Event:
    +
      +
    • +
    + + + +
    Modifies:
    + 1) { ?>
      +
    • +
    + + + + +
    Throws:
    + 1) { ?>
      +
    • +
    + + + + +
    Returns:
    + 1) { ?>
      +
    • +
    + + + + +
    Yields:
    + 1) { ?>
      +
    • +
    + + diff --git a/internal/documentation/jsdoc/docdash/tmpl/modifies.tmpl b/internal/documentation/jsdoc/docdash/tmpl/modifies.tmpl new file mode 100644 index 00000000000..14b1922c190 --- /dev/null +++ b/internal/documentation/jsdoc/docdash/tmpl/modifies.tmpl @@ -0,0 +1,14 @@ + + + +
    +
    + Type +
    +
    + +
    +
    + \ No newline at end of file diff --git a/internal/documentation/jsdoc/docdash/tmpl/namespace.tmpl b/internal/documentation/jsdoc/docdash/tmpl/namespace.tmpl new file mode 100644 index 00000000000..bc156fe9d71 --- /dev/null +++ b/internal/documentation/jsdoc/docdash/tmpl/namespace.tmpl @@ -0,0 +1,133 @@ + + + + +

    Constructor

    + + +

    + + +

    + + + + + + +
    Extends:
    + + + + +
    Type:
    +
      +
    • + +
    • +
    + + + +
    This:
    +
    + + + +
    Example 1? 's':'' ?>
    + + + + +
    Parameters:
    + + + + +
    Requires:
    +
      +
    • +
    + + + +
    Fires:
    +
      +
    • +
    + + + +
    Listens to Events:
    +
      +
    • +
    + + + +
    Listeners of This Event:
    +
      +
    • +
    + + + +
    Modifies:
    + 1) { ?>
      +
    • +
    + + + + +
    Throws:
    + 1) { ?>
      +
    • +
    + + + + +
    Returns:
    + 1) { ?>
      +
    • +
    + + + + +
    Yields:
    + 1) { ?>
      +
    • +
    + + diff --git a/internal/documentation/jsdoc/docdash/tmpl/params.tmpl b/internal/documentation/jsdoc/docdash/tmpl/params.tmpl new file mode 100644 index 00000000000..679b3245739 --- /dev/null +++ b/internal/documentation/jsdoc/docdash/tmpl/params.tmpl @@ -0,0 +1,132 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    + + + + + + <optional>
    + + + + <nullable>
    + + + + <repeatable>
    + +
    + + + + +
    Properties
    + +
    diff --git a/internal/documentation/jsdoc/docdash/tmpl/properties.tmpl b/internal/documentation/jsdoc/docdash/tmpl/properties.tmpl new file mode 100644 index 00000000000..bc382631756 --- /dev/null +++ b/internal/documentation/jsdoc/docdash/tmpl/properties.tmpl @@ -0,0 +1,107 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeAttributesDefaultDescription
    + + + + + + <optional>
    + + + + <nullable>
    + +
    + + + + +
    Properties
    +
    diff --git a/internal/documentation/jsdoc/docdash/tmpl/returns.tmpl b/internal/documentation/jsdoc/docdash/tmpl/returns.tmpl new file mode 100644 index 00000000000..23fefa49246 --- /dev/null +++ b/internal/documentation/jsdoc/docdash/tmpl/returns.tmpl @@ -0,0 +1,19 @@ + +
    + +
    + + + +
    +
    + Type +
    +
    + +
    +
    + \ No newline at end of file diff --git a/internal/documentation/jsdoc/docdash/tmpl/source.tmpl b/internal/documentation/jsdoc/docdash/tmpl/source.tmpl new file mode 100644 index 00000000000..e559b5d1038 --- /dev/null +++ b/internal/documentation/jsdoc/docdash/tmpl/source.tmpl @@ -0,0 +1,8 @@ + +
    +
    +
    +
    +
    \ No newline at end of file diff --git a/internal/documentation/jsdoc/docdash/tmpl/tutorial.tmpl b/internal/documentation/jsdoc/docdash/tmpl/tutorial.tmpl new file mode 100644 index 00000000000..88a0ad52aa2 --- /dev/null +++ b/internal/documentation/jsdoc/docdash/tmpl/tutorial.tmpl @@ -0,0 +1,19 @@ +
    + +
    + 0) { ?> +
      +
    • +
    + + +

    +
    + +
    + +
    + +
    diff --git a/internal/documentation/jsdoc/docdash/tmpl/type.tmpl b/internal/documentation/jsdoc/docdash/tmpl/type.tmpl new file mode 100644 index 00000000000..415cafb9330 --- /dev/null +++ b/internal/documentation/jsdoc/docdash/tmpl/type.tmpl @@ -0,0 +1,26 @@ +", ""); + var longname = name.replace("Array.<","").replace(">", ""); + } + } + } else { + var resolvedName = name; + } + var link = self.linkto(name, self.htmlsafe(resolvedName)); + if (longname) { + link = link.replace(longname, resolvedName); + } + ?> + +| + From 8e53c143a4a3eee12f0d79a9c4399842f9b0abe3 Mon Sep 17 00:00:00 2001 From: Gianluca Beil Date: Thu, 27 Nov 2025 14:44:36 +0200 Subject: [PATCH 02/15] docs: Migrate API Docs to Vitepress --- .gitignore | 2 +- internal/documentation/.vitepress/config.ts | 452 ++++++++--------- .../.vitepress/theme/apidocs.css | 19 + .../documentation/.vitepress/theme/custom.css | 33 +- .../documentation/.vitepress/theme/index.ts | 3 +- .../documentation/jsdoc/docdash/README.md | 9 + .../documentation/jsdoc/docdash/package.json | 3 + .../documentation/jsdoc/docdash/publish.js | 456 ++++++++---------- .../jsdoc/docdash/tmpl/augments.tmpl | 6 +- .../jsdoc/docdash/tmpl/container.tmpl | 345 +++++++------ .../jsdoc/docdash/tmpl/details.tmpl | 239 +++++---- .../jsdoc/docdash/tmpl/example.tmpl | 4 +- .../jsdoc/docdash/tmpl/examples.tmpl | 10 +- .../jsdoc/docdash/tmpl/exceptions.tmpl | 34 +- .../jsdoc/docdash/tmpl/layout.tmpl | 108 +---- .../jsdoc/docdash/tmpl/mainpage.tmpl | 9 +- .../jsdoc/docdash/tmpl/members.tmpl | 31 +- .../jsdoc/docdash/tmpl/method.tmpl | 132 +++-- .../jsdoc/docdash/tmpl/modifies.tmpl | 11 +- .../jsdoc/docdash/tmpl/namespace.tmpl | 132 +++-- .../jsdoc/docdash/tmpl/params.tmpl | 91 +--- .../jsdoc/docdash/tmpl/properties.tmpl | 72 +-- .../jsdoc/docdash/tmpl/returns.tmpl | 16 +- .../jsdoc/docdash/tmpl/source.tmpl | 8 +- .../jsdoc/docdash/tmpl/tutorial.tmpl | 26 +- .../jsdoc/docdash/tmpl/type.tmpl | 5 +- .../documentation/jsdoc/jsdoc-workspace.json | 11 +- internal/documentation/jsdoc/jsdoc.json | 11 +- .../documentation/jsdoc/templates/custom.css | 13 - .../documentation/jsdoc/templates/layout.tmpl | 135 ------ internal/documentation/package.json | 6 +- package-lock.json | 107 +++- 32 files changed, 1060 insertions(+), 1479 deletions(-) create mode 100644 internal/documentation/.vitepress/theme/apidocs.css create mode 100644 internal/documentation/jsdoc/docdash/package.json delete mode 100644 internal/documentation/jsdoc/templates/custom.css delete mode 100644 internal/documentation/jsdoc/templates/layout.tmpl diff --git a/.gitignore b/.gitignore index ac208343158..afee8b743ce 100644 --- a/.gitignore +++ b/.gitignore @@ -68,4 +68,4 @@ internal/documentation/.vitepress/dist internal/documentation/.vitepress/cache internal/documentation/dist internal/documentation/schema/* -internal/documentation/tmp +internal/documentation/docs/api \ No newline at end of file diff --git a/internal/documentation/.vitepress/config.ts b/internal/documentation/.vitepress/config.ts index ab2c702f67f..8102dc40517 100644 --- a/internal/documentation/.vitepress/config.ts +++ b/internal/documentation/.vitepress/config.ts @@ -5,6 +5,18 @@ import { defineConfig } from "vitepress"; // markdown import MarkdownItImplicitFigures from "markdown-it-implicit-figures"; +import fs from "node:fs"; +import path from "node:path"; + +// Create items for all api pages +const apiPages: { text: string; link: string; }[] = []; +for (const file of fs.readdirSync(path.join("docs", "api"))) { + apiPages.push({ + text: file.replaceAll("_", "/").replace(".md", ""), + link: "api/" + file + }); +} + export default defineConfig({ // Would be set in CI job via CLI arguments. For local development, it's just root. @@ -36,30 +48,30 @@ export default defineConfig({ ] ], - themeConfig: { + themeConfig: { - logo: { - light: "/images/Logo_B_RGB.png", - dark: "/images/Logo_O_RGB.png" - }, - externalLinkIcon: false, - outline: [1, 3], + logo: { + light: "/images/Logo_B_RGB.png", + dark: "/images/Logo_O_RGB.png" + }, + externalLinkIcon: false, + outline: [1, 3], - nav: nav(), + nav: nav(), - sidebar: { - "/": guide(), - }, + sidebar: { + "/": guide(), + }, - socialLinks: [ + socialLinks: [ - { icon: "github", link: "https://github.com/UI5/cli" }, - ], + { icon: "github", link: "https://github.com/UI5/cli" }, + ], - footer: { + footer: { - message: ` + message: ` © Copyright ${new Date().getFullYear()}, SAP SE and UI5 CLI Contributors
    Legal Disclosure Terms of Use @@ -68,222 +80,218 @@ export default defineConfig({ `, - }, - - search: { - provider: "local", - //hotKeys: [], // disable hotkeys to avoid search while using UI5 web components input - }, - - + }, - }, + search: { + provider: "local", + //hotKeys: [], // disable hotkeys to avoid search while using UI5 web components input + }, + }, - markdown: { - // Configure the Markdown-it instance - config: (md) => { - // https://www.npmjs.com/package/markdown-it-implicit-figures - md.use(MarkdownItImplicitFigures, { - figcaption: true, - }); - }, - }, + markdown: { + // Configure the Markdown-it instance + config: (md) => { + // https://www.npmjs.com/package/markdown-it-implicit-figures + md.use(MarkdownItImplicitFigures, { + figcaption: true, + }); + }, + }, - vite: { - build: { - chunkSizeWarningLimit: 4000, // chunk for local search index dominates - } - } + vite: { + build: { + chunkSizeWarningLimit: 4000, // chunk for local search index dominates + }, + } }); function nav() { - return [ - { - - text: 'V5', - items: [ - { - text: 'V4', - link: `/../v4/`, - target: "_self" - }, - { - text: 'V3', - link: `/../v3/`, - target: "_self" - }, - { - text: 'V2', - link: `/../v2/`, - target: "_self" - } - ] - }, - ]; + return [ + { + + text: 'V5', + items: [ + { + text: 'V4', + link: `/../v4/`, + target: "_self" + }, + { + text: 'V3', + link: `/../v3/`, + target: "_self" + }, + { + text: 'V2', + link: `/../v2/`, + target: "_self" + } + ] + }, + ]; } function guide() { - return [ - - { - text: "Introduction", - collapsed: false, - - items: [ - - { - text: "Home", - link: "/", - }, - { - text: "Getting Started", - link: "/pages/GettingStarted", - }, - - ], - - }, - { - text: "UI5 CLI", - collapsed: true, - link: "/pages/CLI", - - - }, - { - text: "Configuration", - collapsed: true, - - link: "/pages/Configuration", - - }, - { - text: "Development", - collapsed: false, - items: [ - { - text: "Overview", - link: "/pages/Overview", - }, - { - text: "OpenUI5", - link: "/pages/OpenUI5", - }, - { - text: "SAPUI5", - link: "/pages/SAPUI5", - }, - { - text: "Workspace", - link: "/pages/Workspace", - }, - - ], - }, - - { - text: "Extensibility", - collapsed: false, - items: [ - { - text: "Custom Tasks", - link: "/pages/extensibility/CustomTasks", - }, - { - text: "Custom Server Middleware", - link: "/pages/extensibility/CustomServerMiddleware", - }, - { - text: "Project Shims", - link: "/pages/extensibility/ProjectShims", - }, - ], - }, - { - text: "Modules", - collapsed: false, - items: [ - { - text: "Server", - link: "/pages/Server", - }, - { - text: "Builder", - link: "/pages/Builder", - }, - { - text: "Project", - link: "/pages/Project", - }, - { - text: "File System", - link: "/pages/FileSystem", - }, - ], - }, - { - text: "FAQ", - collapsed: false, - link: "/pages/FAQ", - - }, - { - text: "Upgrade Guides", - collapsed: false, - items: [ - { - text: "Migrate to v5", - link: "/updates/migrate-v5", - }, - { - text: "Migrate to v4", - link: "/updates/migrate-v4", - }, - { - text: "Migrate to v3", - link: "/updates/migrate-v3", - }, - { - text: "Migrate to v2", - link: "/updates/migrate-v2", - }, - { - text: "Migrate to v1", - link: "/updates/migrate-v1", - }, - ], - }, - { - text: "Miscellaneous", - collapsed: false, - items: [ - { - text: "Troubleshooting", - link: "/pages/Troubleshooting", - }, - { - text: "Benchmarking", - link: "/pages/Benchmarking", - }, - { - text: "ECMAScript Support", - link: "/pages/ESSupport", - }, - { - text: "Code Analysis", - link: "/pages/CodeAnalysis", - }, - ], - }, - { - text: "API Reference", - link: "/api/index.html", - target: "_blank" - - }, - - ]; + return [ + + { + text: "Introduction", + collapsed: false, + + items: [ + + { + text: "Home", + link: "/", + }, + { + text: "Getting Started", + link: "/pages/GettingStarted", + }, + + ], + + }, + { + text: "UI5 CLI", + collapsed: true, + link: "/pages/CLI", + + + }, + { + text: "Configuration", + collapsed: true, + + link: "/pages/Configuration", + + }, + { + text: "Development", + collapsed: false, + items: [ + { + text: "Overview", + link: "/pages/Overview", + }, + { + text: "OpenUI5", + link: "/pages/OpenUI5", + }, + { + text: "SAPUI5", + link: "/pages/SAPUI5", + }, + { + text: "Workspace", + link: "/pages/Workspace", + }, + + ], + }, + + { + text: "Extensibility", + collapsed: false, + items: [ + { + text: "Custom Tasks", + link: "/pages/extensibility/CustomTasks", + }, + { + text: "Custom Server Middleware", + link: "/pages/extensibility/CustomServerMiddleware", + }, + { + text: "Project Shims", + link: "/pages/extensibility/ProjectShims", + }, + ], + }, + { + text: "Modules", + collapsed: false, + items: [ + { + text: "Server", + link: "/pages/Server", + }, + { + text: "Builder", + link: "/pages/Builder", + }, + { + text: "Project", + link: "/pages/Project", + }, + { + text: "File System", + link: "/pages/FileSystem", + }, + ], + }, + { + text: "FAQ", + collapsed: false, + link: "/pages/FAQ", + + }, + { + text: "Upgrade Guides", + collapsed: false, + items: [ + { + text: "Migrate to v5", + link: "/updates/migrate-v5", + }, + { + text: "Migrate to v4", + link: "/updates/migrate-v4", + }, + { + text: "Migrate to v3", + link: "/updates/migrate-v3", + }, + { + text: "Migrate to v2", + link: "/updates/migrate-v2", + }, + { + text: "Migrate to v1", + link: "/updates/migrate-v1", + }, + ], + }, + { + text: "Miscellaneous", + collapsed: false, + items: [ + { + text: "Troubleshooting", + link: "/pages/Troubleshooting", + }, + { + text: "Benchmarking", + link: "/pages/Benchmarking", + }, + { + text: "ECMAScript Support", + link: "/pages/ESSupport", + }, + { + text: "Code Analysis", + link: "/pages/CodeAnalysis", + }, + ], + }, + { + text: "API", + collapsed: true, + items: apiPages + }, + + ]; } diff --git a/internal/documentation/.vitepress/theme/apidocs.css b/internal/documentation/.vitepress/theme/apidocs.css new file mode 100644 index 00000000000..ea5211085d9 --- /dev/null +++ b/internal/documentation/.vitepress/theme/apidocs.css @@ -0,0 +1,19 @@ +/** + JSDoc Function Highlighting + */ +.jsdoc-object { + background-color: var(--vp-c-bg-soft); + padding: 15px; +} + +.jsdoc-object .type-signature { + color: var(--vp-c-brand-2); +} + +.jsdoc-object .signature { + color: var(--vp-c-brand-1); +} + +.jsdoc-object .signature .signature-attributes { + font-size: 11px; +} diff --git a/internal/documentation/.vitepress/theme/custom.css b/internal/documentation/.vitepress/theme/custom.css index e666cba1d66..882508cee9c 100644 --- a/internal/documentation/.vitepress/theme/custom.css +++ b/internal/documentation/.vitepress/theme/custom.css @@ -14,14 +14,14 @@ /* Water */ --ui5-water-main: #1873b4; /* denim-600 */ - --ui5-water-secondary: #53b8de; /* viking-400 */ + --ui5-water-secondary: #53b8de; /* viking-400 */ --ui5-water-background: #ffffff; /* white */ /* Tailwind CSS Color Generator ** https://uicolors.app/create */ - /* VP */ + /* VP */ /* The most solid color used mainly for colored text. */ --vp-c-brand-1: #1873b4; /* lochmara-600 */ /* The color used mainly for hover state of the button. */ @@ -32,7 +32,14 @@ /* The soft color must be semi transparent alpha channel. */ /*--vp-c-brand-soft: rgba(12, 72, 120, 0.14);*/ /* --brand-dark-blue */ --vp-c-brand-soft: rgba(3, 103, 161, 0.14); /* lochmara-700 */ + + /* Makes the sidebar ever so slighty wider, fixes claustrophobic width with api docs */ + --vp-sidebar-width: 345px; + + /* Makes the page wider for the api docs */ + --vp-layout-max-width: 80%; } + .dark { /* The most solid color used mainly for colored text. */ --vp-c-brand-1: #ff5a37; /* lochmara-600 */ @@ -44,15 +51,15 @@ /* The bg color used for main screen */ --vp-c-bg: #000000; /* The alternative bg color used in places such as "sidebar" or "code block". */ - + /* The elevated bg color. This is used at parts where it "floats", such as "dialog". */ /*--vp-c-bg-elv: red;*/ /* #1d2025;*/ - /* The bg color to slightly ditinguish some components from the page. - ** Used for things like "carbon ads" or "table". + /* The bg color to slightly ditinguish some components from the page. + ** Used for things like "carbon ads" or "table". */ --vp-c-bg-soft: #2b313a; /* 202127 */ - /* This is used for separators. This is used to divide sections within the same components, - ** such as having separator on "h2" heading. + /* This is used for separators. This is used to divide sections within the same components, + ** such as having separator on "h2" heading. */ --vp-c-divider: rgba(82,82,89,0.32); /*#2e2e32;*/ /* This is designed for borders on interactive components. @@ -86,7 +93,7 @@ } /* Default KBD - ** https://flowbite.com/docs/components/kbd/ + ** https://flowbite.com/docs/components/kbd/ */ .u-kbd { @apply px-2 py-1 text-xs font-semibold text-gray-800 bg-gray-50 border border-gray-300 rounded-lg dark:bg-gray-600 dark:text-gray-100 dark:border-gray-500; @@ -142,7 +149,7 @@ * Custom Block * -------------------------------------------------------------------------- */ .vp-doc .custom-block { - padding: 16px; + padding: 16px; } .vp-doc .custom-block.tip { @@ -301,3 +308,11 @@ li .learn-more { margin-right: 5px; vertical-align: middle; } + +/** +* Vitepress content width fix +* This expands the content to fit more stuff +* -------------------------------------------------------------------------- */ +.VPDoc.has-aside .content-container { + max-width: 100% !important; +} diff --git a/internal/documentation/.vitepress/theme/index.ts b/internal/documentation/.vitepress/theme/index.ts index 82c2a17338e..5bb7828edd7 100644 --- a/internal/documentation/.vitepress/theme/index.ts +++ b/internal/documentation/.vitepress/theme/index.ts @@ -5,6 +5,7 @@ import { onMounted, watch, nextTick } from "vue"; // custom css import "./custom.css"; +import "./apidocs.css"; // global components import Badgen from "@theme/components/Badgen.vue"; @@ -25,7 +26,7 @@ export default { }, setup() { - // this function will be executed inside VitePressApp's setup hook. + // this function will be executed inside VitePressApp's setup hook. // all composition APIs are available here. const route = useRoute(); let initZoom: () => void; diff --git a/internal/documentation/jsdoc/docdash/README.md b/internal/documentation/jsdoc/docdash/README.md index b91027191cf..cdcc93bf990 100644 --- a/internal/documentation/jsdoc/docdash/README.md +++ b/internal/documentation/jsdoc/docdash/README.md @@ -2,6 +2,15 @@ This folder includes files from [docdash 2.0.2](https://github.com/clenemt/docdash/tree/bee5d0789068be6a1e01ce02968b23dd021b4fb6), which is a documentation template for JSDoc. +These files have been modified to change the output from HTML to Markdown, and to remove unnecessary features for our use case. + +## Modifications by SAP +* Output Markdown instead of HTML (Modified all templates) +* Removed navbar functionality from publish.js +* Removed static files functionality from publish.js +* Modified js links to link to GitHub +* Modified internal linking + ## Contributors Thanks to [lodash](https://lodash.com) and [minami](https://github.com/nijikokun/minami). diff --git a/internal/documentation/jsdoc/docdash/package.json b/internal/documentation/jsdoc/docdash/package.json new file mode 100644 index 00000000000..a0df0c86778 --- /dev/null +++ b/internal/documentation/jsdoc/docdash/package.json @@ -0,0 +1,3 @@ +{ + "type": "commonjs" +} diff --git a/internal/documentation/jsdoc/docdash/publish.js b/internal/documentation/jsdoc/docdash/publish.js index 5894d832722..1aef74c1552 100644 --- a/internal/documentation/jsdoc/docdash/publish.js +++ b/internal/documentation/jsdoc/docdash/publish.js @@ -8,43 +8,17 @@ var logger = require('jsdoc/util/logger'); var path = require('jsdoc/path'); var taffy = require('@jsdoc/salty').taffy; var template = require('jsdoc/template'); -var util = require('util'); +var util = require('node:util'); var htmlsafe = helper.htmlsafe; -var linkto = helper.linkto; var resolveAuthorLinks = helper.resolveAuthorLinks; -var scopeToPunc = helper.scopeToPunc; -var hasOwnProp = Object.prototype.hasOwnProperty; var data; var view; +var githubSourceBaseUrl; var outdir = path.normalize(env.opts.destination); -function copyFile(source, target, cb) { - var cbCalled = false; - - var rd = fs.createReadStream(source); - rd.on("error", function(err) { - done(err); - }); - var wr = fs.createWriteStream(target); - wr.on("error", function(err) { - done(err); - }); - wr.on("close", function(ex) { - done(); - }); - rd.pipe(wr); - - function done(err) { - if (!cbCalled) { - cb(err); - cbCalled = true; - } - } -} - function find(spec) { return helper.find(data, spec); } @@ -63,7 +37,7 @@ function hashToLink(doclet, hash) { var url = helper.createLink(doclet); url = url.replace(/(#.+|$)/, hash); - return '' + hash + ''; + return '[' + hash + '](' + url + ')'; } function needsSignature(doclet) { @@ -83,11 +57,11 @@ function needsSignature(doclet) { } } } - // and namespaces that are functions get a signature (but finding them is a - // bit messy) - else if (doclet.kind === 'namespace' && doclet.meta && doclet.meta.code && - doclet.meta.code.type && doclet.meta.code.type.match(/[Ff]unction/)) { - needsSig = true; + // and namespaces that are functions get a signature (but finding them is a + // bit messy) + else if (doclet.kind === 'namespace' && doclet.meta && doclet.meta.code && + doclet.meta.code.type && doclet.meta.code.type.match(/[Ff]unction/)) { + needsSig = true; } return needsSig; @@ -137,7 +111,7 @@ function buildItemTypeStrings(item) { if (item && item.type && item.type.names) { item.type.names.forEach(function(name) { - types.push( linkto(name, htmlsafe(name)) ); + types.push( linkTo(name, htmlsafe(name)) ); }); } @@ -199,24 +173,24 @@ function addSignatureReturns(f) { } f.signature = '' + (f.signature || '') + '' + - '' + returnTypesString + ''; + '' + returnTypesString + ''; } function addSignatureTypes(f) { var types = f.type ? buildItemTypeStrings(f) : []; f.signature = (f.signature || '') + '' + - (types.length ? ' :' + types.join('|') : '') + ''; + (types.length ? ' :' + types.join('|') : '') + ''; } function addAttribs(f) { var attribs = helper.getAttribs(f); var attribsString = buildAttribsString(attribs); if (attribsString && attribsString.length) { - f.attribs = util.format('%s', attribsString); + f.attribs = "
    " + util.format('%s', attribsString); } else { - f.attribs = util.format('%s', attribsString); + f.attribs = "
    " + util.format('%s', attribsString); } } @@ -322,192 +296,14 @@ function attachModuleSymbols(doclets, modules) { }); } -function buildMemberNav(items, itemHeading, itemsSeen, linktoFn) { - var nav = ''; - - if (items && items.length) { - var itemsNav = ''; - var docdash = env && env.conf && env.conf.docdash || {}; - var level = typeof docdash.navLevel === 'number' && docdash.navLevel >= 0 ? - docdash.navLevel : - Infinity; - - items.forEach(function(item) { - var displayName; - var methods = find({kind:'function', memberof: item.longname}); - var members = find({kind:'member', memberof: item.longname}); - var conf = env && env.conf || {}; - var classes = ''; - - // show private class? - if (docdash.private === false && item.access === 'private') return; - - // depth to show? - if (item.ancestors && item.ancestors.length > level) { - classes += 'level-hide'; - } - - classes = classes ? ' class="'+ classes + '"' : ''; - itemsNav += ''; - if ( !hasOwnProp.call(item, 'longname') ) { - itemsNav += linktoFn('', item.name); - } else if ( !hasOwnProp.call(itemsSeen, item.longname) ) { - if (conf.templates.default.useLongnameInNav) { - displayName = item.longname; - } else { - displayName = item.name; - } - itemsNav += linktoFn(item.longname, displayName.replace(/\b(module|event):/g, '')); - - if (docdash.static && members.find(function (m) { return m.scope === 'static'; } )) { - itemsNav += "
      "; - - members.forEach(function (member) { - if (!member.scope === 'static') return; - itemsNav += "
      • ' + itemsNav + '
      '; - } - else { - nav += '

      ' + itemHeading + '

        ' + itemsNav + '
      '; - } - } - } - - return nav; -} - -function linktoTutorial(longName, name) { - return tutoriallink(name); -} - -function linktoExternal(longName, name) { - return linkto(longName, name.replace(/(^"|"$)/g, '')); -} - -/** - * Create the navigation sidebar. - * @param {object} members The members that will be used to create the sidebar. - * @param {array} members.classes - * @param {array} members.externals - * @param {array} members.globals - * @param {array} members.mixins - * @param {array} members.modules - * @param {array} members.namespaces - * @param {array} members.tutorials - * @param {array} members.events - * @param {array} members.interfaces - * @return {string} The HTML for the navigation sidebar. - */ - -function buildNav(members) { - var nav = '

      Home

      '; - var seen = {}; - var seenTutorials = {}; - var docdash = env && env.conf && env.conf.docdash || {}; - if(docdash.menu){ - for(var menu in docdash.menu){ - nav += '

      '; - } - } - - function buildMemberNavGlobal() { - var ret = ""; - if (members.globals.length) { - var globalNav = ''; - - members.globals.forEach(function(g) { - if ( (docdash.typedefs || g.kind !== 'typedef') && !hasOwnProp.call(seen, g.longname) ) { - globalNav += '
    • ' + linkto(g.longname, g.name) + '
    • '; - } - seen[g.longname] = true; - }); - - if (!globalNav) { - // turn the heading into a link so you can actually get to the global page - ret += '

      ' + linkto('global', 'Global') + '

      '; - } - else { - if(docdash.collapse === "top") { - ret += '

      Global

        ' + globalNav + '
      '; - } - else { - ret += '

      Global

        ' + globalNav + '
      '; - } - } - } - return ret; - } - var defaultOrder = [ - 'Classes', 'Modules', 'Externals', 'Events', 'Namespaces', 'Mixins', 'Tutorials', 'Interfaces', 'Global' - ]; - var order = docdash.sectionOrder || defaultOrder; - var sections = { - Classes: buildMemberNav(members.classes, 'Classes', seen, linkto), - Modules: buildMemberNav(members.modules, 'Modules', {}, linkto), - Externals: buildMemberNav(members.externals, 'Externals', seen, linktoExternal), - Events: buildMemberNav(members.events, 'Events', seen, linkto), - Namespaces: buildMemberNav(members.namespaces, 'Namespaces', seen, linkto), - Mixins: buildMemberNav(members.mixins, 'Mixins', seen, linkto), - Tutorials: buildMemberNav(members.tutorials, 'Tutorials', seenTutorials, linktoTutorial), - Interfaces: buildMemberNav(members.interfaces, 'Interfaces', seen, linkto), - Global: buildMemberNavGlobal() - }; - order.forEach(member => nav += sections[member]); - - return nav; -} - /** @param {TAFFY} taffyData See . @param {object} opts @param {Tutorial} tutorials */ exports.publish = function(taffyData, opts, tutorials) { + helper.fileExtension = ".md"; + var docdash = env && env.conf && env.conf.docdash || {}; data = taffyData; @@ -517,6 +313,9 @@ exports.publish = function(taffyData, opts, tutorials) { var templatePath = path.normalize(opts.template); view = new template.Template( path.join(templatePath, 'tmpl') ); + // Store GitHub base URL for source file links + githubSourceBaseUrl = docdash.githubSourceBaseUrl || null; + // claim some special filenames in advance, so the All-Powerful Overseer of Filename Uniqueness // doesn't try to hand them out later var indexUrl = helper.getUniqueFilename('index'); @@ -626,43 +425,8 @@ exports.publish = function(taffyData, opts, tutorials) { outdir = path.join.apply(null, subdirs); } } - - fs.mkPath(outdir); - - // copy the template's static files to outdir - var fromDir = path.join(templatePath, 'static'); - var staticFiles = fs.ls(fromDir, 3); - staticFiles.forEach(function(fileName) { - var toDir = fs.toDir( fileName.replace(fromDir, outdir) ); - fs.mkPath(toDir); - copyFile(fileName, path.join(toDir, path.basename(fileName)), function(err){if(err) console.err(err);}); - }); - - // copy user-specified static files to outdir - var staticFilePaths; - var staticFileFilter; - var staticFileScanner; - if (conf.default.staticFiles) { - // The canonical property name is `include`. We accept `paths` for backwards compatibility - // with a bug in JSDoc 3.2.x. - staticFilePaths = conf.default.staticFiles.include || - conf.default.staticFiles.paths || - []; - staticFileFilter = new (require('jsdoc/src/filter')).Filter(conf.default.staticFiles); - staticFileScanner = new (require('jsdoc/src/scanner')).Scanner(); - - staticFilePaths.forEach(function(filePath) { - var extraStaticFiles = staticFileScanner.scan([filePath], 10, staticFileFilter); - - extraStaticFiles.forEach(function(fileName) { - var sourcePath = fs.toDir(filePath); - var toDir = fs.toDir( fileName.replace(sourcePath, outdir) ); - fs.mkPath(toDir); - copyFile(fileName, path.join(toDir, path.basename(fileName)), function(err){if(err) console.err(err);}); - }); - }); - } + fs.mkPath(outdir); if (sourceFilePaths.length) { sourceFiles = shortenPaths( sourceFiles, path.commonPrefix(sourceFilePaths) ); @@ -728,14 +492,13 @@ exports.publish = function(taffyData, opts, tutorials) { // add template helpers view.find = find; - view.linkto = linkto; + view.linkto = linkTo; view.resolveAuthorLinks = resolveAuthorLinks; view.tutoriallink = tutoriallink; view.htmlsafe = htmlsafe; view.outputSourceFiles = outputSourceFiles; // once for all - view.nav = buildNav(members); attachModuleSymbols( find({ longname: {left: 'module:'} }), members.modules ); // generate the pretty-printed source files first so other pages can link to them @@ -757,11 +520,6 @@ exports.publish = function(taffyData, opts, tutorials) { ).concat(files), indexUrl); - // common nav generation, no need for templating here, we already have full html - if (docdash.commonNav) { - fs.writeFileSync(path.join(outdir, 'nav.inc.html'), view.nav, 'utf8'); - } - // set up the lists that we'll use to generate pages var classes = taffy(members.classes); var modules = taffy(members.modules); @@ -829,3 +587,177 @@ exports.publish = function(taffyData, opts, tutorials) { saveChildren(tutorials); }; + +function linkTo(longname, linkText, cssClass, fragmentId) { + const classString = cssClass ? util.format(' class="%s"', cssClass) : ''; + let fileUrl; + let fragmentString = fragmentId ? `#${fragmentId}` : ''; + let stripped; + let text; + + // handle cases like: + // @see + // @see http://example.org + stripped = longname ? longname.replace(/^<|>$/g, '') : ''; + const hasUrlPrefix = /^(http|ftp)s?:\/\//.test(stripped); + + if (hasUrlPrefix) { + fileUrl = stripped; + text = linkText || stripped; + // Add target and rel attributes for external links + return util.format('
      %s', + encodeURI(fileUrl + fragmentString), classString, text); + } + // handle complex type expressions that may require multiple links + // (but skip anything that looks like an inline tag or HTML tag) + else if (longname && isComplexTypeExpression(longname) && + !/\{@.+\}/.test(longname) && !/^<[\s\S]+>/.test(longname)) { + // Parse complex types and create links for nested types + return linkComplexType(longname, linkText, cssClass); + } + else { + fileUrl = helper.longnameToUrl[longname] || ''; + text = linkText || longname; + + // If the URL contains a fragment (hash), extract it + if (fileUrl && fileUrl.indexOf('#') > -1) { + const parts = fileUrl.split('#'); + fileUrl = parts[0]; + // Only use the URL's fragment if no explicit fragmentId was provided + if (!fragmentId) { + fragmentString = '#' + parts[1]; + } + } + + // Convert source file links to GitHub URLs if configured + if (fileUrl && githubSourceBaseUrl && (fileUrl.endsWith('.js.md') || longname.endsWith('.js'))) { + fileUrl = convertSourceLinkToGitHub(fileUrl, longname); + // GitHub links should open in new tab + return util.format('%s', + encodeURI(fileUrl + fragmentString), classString, text); + } + // Remove .md extension from internal links for VitePress compatibility + // Handle both cases: with and without fragment identifiers + else if (fileUrl) { + fileUrl = fileUrl.replace(/\.md$/, ''); + } + } + + text = text || longname; + + if (!fileUrl) { + return text; + } + else { + return util.format('%s', encodeURI(fileUrl + fragmentString), + classString, text); + } +} + +function convertSourceLinkToGitHub(fileUrl, longname) { + if (!githubSourceBaseUrl) { + return fileUrl; + } + + // Look up the original source path from the reverse mapping + let sourcePath = helper.longnameToUrl.urlToLongname || {}; + + // Try to find the original path from the URL + for (let originalPath in helper.longnameToUrl) { + if (helper.longnameToUrl[originalPath] === fileUrl) { + sourcePath = originalPath; + break; + } + } + + // If we found a valid source path, convert it to GitHub URL + if (typeof sourcePath === 'string' && sourcePath.endsWith('.js')) { + // Clean up the path - remove any leading slashes or backslashes + sourcePath = sourcePath.replace(/^[\/\\]+/, ''); + + // Return the GitHub URL + return `${githubSourceBaseUrl}/${sourcePath}`; + } + + // Fallback: if no mapping found, return original fileUrl + return fileUrl; +} + +function isComplexTypeExpression(expr) { + // record types, type unions, and type applications all count as "complex" + return /^{.+}$/.test(expr) || /^.+\|.+$/.test(expr) || /^.+<.+>$/.test(expr); +} + +function linkComplexType(longname, linkText, cssClass) { + const classString = cssClass ? util.format(' class="%s"', cssClass) : ''; + + // Handle type unions (e.g., "string | number") + if (/^.+\|.+$/.test(longname) && !/<.+>/.test(longname)) { + const types = longname.split('|').map(t => t.trim()); + return types.map(type => { + let url = helper.longnameToUrl[type]; + if (url) { + // Remove .md extension for VitePress compatibility + url = url.replace(/\.md$/, ''); + return util.format('%s', encodeURI(url), classString, htmlsafe(type)); + } + return htmlsafe(type); + }).join(' | '); + } + + // Handle generic types (e.g., "Array.", "Promise.<@ui5/fs/Resource>") + if (/<.+>/.test(longname)) { + return linkGenericType(longname, classString); + } + + // Handle record types (e.g., "{a: string, b: number}") + if (/^{.+}$/.test(longname)) { + return htmlsafe(longname); + } + + // Fallback + return linkText || htmlsafe(longname); +} + +function linkGenericType(type, classString) { + // Match patterns like "Promise." or "Array." or "Promise.>" + const match = type.match(/^([^<]+)<(.+)>$/); + + if (!match) { + return htmlsafe(type); + } + + const baseType = match[1]; + const innerType = match[2]; + + // Link the base type if it has a URL + let result = ''; + let baseUrl = helper.longnameToUrl[baseType]; + if (baseUrl) { + // Remove .md extension for VitePress compatibility + baseUrl = baseUrl.replace(/\.md$/, ''); + result = util.format('%s', encodeURI(baseUrl), classString, htmlsafe(baseType)); + } else { + result = htmlsafe(baseType); + } + + result += '.<'; + + // Recursively handle the inner type + if (isComplexTypeExpression(innerType)) { + result += linkComplexType(innerType, null, classString.replace(' class="', '').replace('"', '')); + } else { + let innerUrl = helper.longnameToUrl[innerType]; + if (innerUrl) { + // Remove .md extension for VitePress compatibility + innerUrl = innerUrl.replace(/\.md$/, ''); + result += util.format('%s', encodeURI(innerUrl), classString, htmlsafe(innerType)); + } else { + result += htmlsafe(innerType); + } + } + + result += '>'; + + return result; +} diff --git a/internal/documentation/jsdoc/docdash/tmpl/augments.tmpl b/internal/documentation/jsdoc/docdash/tmpl/augments.tmpl index 446d28aa537..053f971a107 100644 --- a/internal/documentation/jsdoc/docdash/tmpl/augments.tmpl +++ b/internal/documentation/jsdoc/docdash/tmpl/augments.tmpl @@ -4,7 +4,7 @@ ?> -
        -
      • -
      + +- + diff --git a/internal/documentation/jsdoc/docdash/tmpl/container.tmpl b/internal/documentation/jsdoc/docdash/tmpl/container.tmpl index 0621f94c095..fc13a900a0d 100644 --- a/internal/documentation/jsdoc/docdash/tmpl/container.tmpl +++ b/internal/documentation/jsdoc/docdash/tmpl/container.tmpl @@ -17,187 +17,170 @@ -
      - -
      - -

      - - - - Namespace - - - - - -

      - -
      - - - - -
      - - - -
      - -
      - -
      - - -
      - - - - - - - - - - - - - -
      - - - -

      Example 1? 's':'' ?>

      - - - -
      - - - -

      Extends

      - - - - - -

      Requires

      - -
        -
      • -
      - - - -

      Classes

      - -
      -
      -
      -
      - - - -

      Interfaces

      - -
      -
      -
      -
      - - - -

      Mixins

      - -
      -
      -
      -
      - - - -

      Namespaces

      - -
      -
      -
      -
      - - - -

      Members

      - - - - - - - -

      Methods

      - - - - - - - -

      Type Definitions

      - - - - - - - - - -

      Events

      - - - - - -
      - -
      + +## Namespace + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +### Example 1? 's':'' ?> + + + + + + + +### Extends + + + + + +### Requires + + +- + + + + +### Classes + + +- + + + + + +### Interfaces + + +- + + + + + +### Mixins + + +- + + + + + +### Namespaces + + +- + + + + + +### Members + + + + + + + +### Methods + + + + + + + +### Type Definitions + + + + + + + + + +### Events + + + + + + diff --git a/internal/documentation/jsdoc/docdash/tmpl/details.tmpl b/internal/documentation/jsdoc/docdash/tmpl/details.tmpl index 32686cf31ac..276c0fbb310 100644 --- a/internal/documentation/jsdoc/docdash/tmpl/details.tmpl +++ b/internal/documentation/jsdoc/docdash/tmpl/details.tmpl @@ -9,144 +9,125 @@ if (data.defaultvalue && (data.defaultvaluetype === 'object' || data.defaultvalu var indentedValues = JSON.stringify(JSON.parse(data.defaultvalue), null, ' '); data.defaultvalue = indentedValues; } catch (e) {} - data.defaultvalue = "
      " + data.defaultvalue + "
      "; - defaultObjectClass = ' class="object-value"'; + data.defaultvalue = "```\n" + data.defaultvalue + "\n```"; + defaultObjectClass = ' object-value'; } ?> -
      - -
      Description:
      -
      - - - -
      Source:
      -
      • - , -
      - - - -
      Version:
      -
      - - - -
      Since:
      -
      - - - -
      Inherited From:
      -
      • - -
      - - - -
      Overrides:
      -
      • - -
      - - - -
      Implementations:
      -
        - -
      • - -
      - - - -
      Implements:
      -
        - -
      • - -
      - - - -
      Mixes In:
      - -
        - -
      • - -
      - - - -
      Deprecated:
      • Yes
      - - - -
      Author:
      -
      -
        -
      • -
      -
      - - - - - - - - -
      License:
      -
      - - - -
      Default Value:
      -
        - > -
      - - - -
      Tutorials:
      -
      -
        -
      • -
      -
      - - - -
      See:
      -
      -
        -
      • -
      -
      - - - -
      To Do:
      -
      -
        -
      • -
      -
      - -
      + +**Description:** + + + + +**Source:** , + + + + +**Version:** + + + + +**Since:** + + + + +**Inherited From:** + + + + +**Overrides:** + + + + +**Implementations:** + +- + + + + + +**Implements:** + +- + + + + + +**Mixes In:** + +- + + + + + +**Deprecated:** + + + + +**Author:** + +- + + + + + +**Copyright:** + + + + +**License:** + + + + +**Default Value:** + + + + + +**Tutorials:** + +- + + + + + +**See:** + +- + + + + + +**To Do:** + +- + + + -
      Properties:
      +##### Properties: - + diff --git a/internal/documentation/jsdoc/docdash/tmpl/example.tmpl b/internal/documentation/jsdoc/docdash/tmpl/example.tmpl index e87caa5b72c..031a1ced242 100644 --- a/internal/documentation/jsdoc/docdash/tmpl/example.tmpl +++ b/internal/documentation/jsdoc/docdash/tmpl/example.tmpl @@ -1,2 +1,4 @@ -
      +``` + +``` diff --git a/internal/documentation/jsdoc/docdash/tmpl/examples.tmpl b/internal/documentation/jsdoc/docdash/tmpl/examples.tmpl index 04d975e96dc..a4fdceea1f7 100644 --- a/internal/documentation/jsdoc/docdash/tmpl/examples.tmpl +++ b/internal/documentation/jsdoc/docdash/tmpl/examples.tmpl @@ -5,9 +5,13 @@ data.forEach(function(example) { if (example.caption) { ?> -

      +** + -
      +``` + +``` + \ No newline at end of file +?> diff --git a/internal/documentation/jsdoc/docdash/tmpl/exceptions.tmpl b/internal/documentation/jsdoc/docdash/tmpl/exceptions.tmpl index 8d0f9d5e1eb..cc1dc7af193 100644 --- a/internal/documentation/jsdoc/docdash/tmpl/exceptions.tmpl +++ b/internal/documentation/jsdoc/docdash/tmpl/exceptions.tmpl @@ -2,31 +2,13 @@ var data = obj; ?> -
      -
      -
      - -
      -
      -
      -
      -
      -
      - Type -
      -
      - -
      -
      -
      -
      -
      + + +**Type:** -
      - - - - - -
      + + + + + diff --git a/internal/documentation/jsdoc/docdash/tmpl/layout.tmpl b/internal/documentation/jsdoc/docdash/tmpl/layout.tmpl index 8fb37f4ca6d..7ae96f8c0b2 100644 --- a/internal/documentation/jsdoc/docdash/tmpl/layout.tmpl +++ b/internal/documentation/jsdoc/docdash/tmpl/layout.tmpl @@ -1,102 +1,10 @@ - - - - - - <?js= title ?> - <?js= ((env.conf.docdash.meta && env.conf.docdash.meta.title) || "Documentation") ?> - - - - - - - - - - - - - - - - - - - - - - - - - - +--- +prev: false +next: false +--- - - - - - - - -
      - -

      - - - - - -
      - - - -
      - -
      - -
      - Documentation generated by JSDoc on using the docdash theme. -
      - - - - - - + +# - - - - - - - - - - + + diff --git a/internal/documentation/jsdoc/docdash/tmpl/mainpage.tmpl b/internal/documentation/jsdoc/docdash/tmpl/mainpage.tmpl index 461bf013aa6..ac73961fa2a 100644 --- a/internal/documentation/jsdoc/docdash/tmpl/mainpage.tmpl +++ b/internal/documentation/jsdoc/docdash/tmpl/mainpage.tmpl @@ -4,12 +4,9 @@ var self = this; ?> -
      -

      -
      +# + -
      -
      -
      + diff --git a/internal/documentation/jsdoc/docdash/tmpl/members.tmpl b/internal/documentation/jsdoc/docdash/tmpl/members.tmpl index 3cc698ca965..fc3169c2b2d 100644 --- a/internal/documentation/jsdoc/docdash/tmpl/members.tmpl +++ b/internal/documentation/jsdoc/docdash/tmpl/members.tmpl @@ -1,38 +1,35 @@ , if so don't add another one +var hasClosingDiv = signature.includes(''); ?> -

      +####
      /, '').replace(/<\/div>$/, '') + name + signature + (hasClosingDiv ? '' : '
      ') ?> -

      + -
      - -
      + -
      Type:
      -
        -
      • - -
      • -
      +##### Type: +- -
      Fires:
      -
        -
      • -
      +##### Fires: + +- + -
      Example 1? 's':'' ?>
      - +##### Example 1? 's':'' ?>: + diff --git a/internal/documentation/jsdoc/docdash/tmpl/method.tmpl b/internal/documentation/jsdoc/docdash/tmpl/method.tmpl index a51d519a013..7434ca22e89 100644 --- a/internal/documentation/jsdoc/docdash/tmpl/method.tmpl +++ b/internal/documentation/jsdoc/docdash/tmpl/method.tmpl @@ -4,10 +4,10 @@ var self = this; ?> -

      Constructor

      +## Constructor -

      '>/, '') ?>

      + ?> -

      + -
      Extends:
      - +##### Extends: + -
      Type:
      -
        -
      • - -
      • -
      +##### Type: +- -
      This:
      -
      +##### This: +- -
      Example 1? 's':'' ?>
      - +##### Example 1? 's':'' ?>: + -
      Parameters:
      - +##### Parameters: + -
      Requires:
      -
        -
      • -
      +##### Requires: + +- + -
      Fires:
      -
        -
      • -
      +##### Fires: + +- + -
      Listens to Events:
      -
        -
      • -
      +##### Listens to Events: + +- + -
      Listeners of This Event:
      -
        -
      • -
      +##### Listeners of This Event: + +- + - -
      Modifies:
      - 1) { ?>
        -
      • -
      - - +##### Modifies: + 1) { ?> + +- + + + + -
      Throws:
      - 1) { ?>
        -
      • -
      1) { ?> + +- + + - + -
      Returns:
      - 1) { ?>
        1) { ?> + +- + + -
      • -
      - + - -
      Yields:
      - 1) { ?>
        -
      • -
      - - +##### Yields: + 1) { ?> + +- + + + + diff --git a/internal/documentation/jsdoc/docdash/tmpl/modifies.tmpl b/internal/documentation/jsdoc/docdash/tmpl/modifies.tmpl index 14b1922c190..4bba92bb498 100644 --- a/internal/documentation/jsdoc/docdash/tmpl/modifies.tmpl +++ b/internal/documentation/jsdoc/docdash/tmpl/modifies.tmpl @@ -3,12 +3,5 @@ var data = obj || {}; ?> -
      -
      - Type -
      -
      - -
      -
      - \ No newline at end of file +**Type:** + diff --git a/internal/documentation/jsdoc/docdash/tmpl/namespace.tmpl b/internal/documentation/jsdoc/docdash/tmpl/namespace.tmpl index bc156fe9d71..348a59d0a53 100644 --- a/internal/documentation/jsdoc/docdash/tmpl/namespace.tmpl +++ b/internal/documentation/jsdoc/docdash/tmpl/namespace.tmpl @@ -5,10 +5,10 @@ var self = this; -

      Constructor

      +## Constructor -

      + ?> -

      + -
      Extends:
      - +##### Extends: + -
      Type:
      -
        -
      • - -
      • -
      +##### Type: +- -
      This:
      -
      +##### This: +- -
      Example 1? 's':'' ?>
      - +##### Example 1? 's':'' ?>: + -
      Parameters:
      - +##### Parameters: + -
      Requires:
      -
        -
      • -
      +##### Requires: + +- + -
      Fires:
      -
        -
      • -
      +##### Fires: + +- + -
      Listens to Events:
      -
        -
      • -
      +##### Listens to Events: + +- + -
      Listeners of This Event:
      -
        -
      • -
      +##### Listeners of This Event: + +- + -
      Modifies:
      - 1) { ?>
        -
      • -
      - - 1) { ?> + +- + + + + -
      Throws:
      - 1) { ?>
        1) { ?> + +- + + -
      • -
      - - + -
      Returns:
      - 1) { ?>
        -
      • -
      1) { ?> + +- + + - - + -
      Yields:
      - 1) { ?>
        -
      • -
      - - 1) { ?> + +- + + + + diff --git a/internal/documentation/jsdoc/docdash/tmpl/params.tmpl b/internal/documentation/jsdoc/docdash/tmpl/params.tmpl index 679b3245739..d9b5561b580 100644 --- a/internal/documentation/jsdoc/docdash/tmpl/params.tmpl +++ b/internal/documentation/jsdoc/docdash/tmpl/params.tmpl @@ -13,12 +13,12 @@ if (parentParam && parentParam.name && param.name) { try { paramRegExp = new RegExp('^(?:' + parentParam.name + '(?:\\[\\])*)\\.(.+)$'); - } - catch (e) { + } + catch (e) { console.error("Problem parsing comment", e); - // there's probably a typo in the JSDoc comment that resulted in a weird - // parameter name - return; + // there's probably a typo in the JSDoc comment that resulted in a weird + // parameter name + return; } if ( paramRegExp.test(param.name) ) { @@ -57,76 +57,11 @@ } }); ?> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      NameTypeAttributesDefaultDescription
      - - - - - - <optional>
      - - - - <nullable>
      - - - - <repeatable>
      - -
      - - - - -
      Properties
      - -
      +| Name | Type | Attributes | Default | Description | +| --- | --- | --- | --- | --- | +| `` | | optional, nullable, repeatable | `` | ') ?>
      *Properties:* + diff --git a/internal/documentation/jsdoc/docdash/tmpl/properties.tmpl b/internal/documentation/jsdoc/docdash/tmpl/properties.tmpl index bc382631756..00e177f1934 100644 --- a/internal/documentation/jsdoc/docdash/tmpl/properties.tmpl +++ b/internal/documentation/jsdoc/docdash/tmpl/properties.tmpl @@ -1,6 +1,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      NameTypeAttributesDefaultDescription
      - - - - - - <optional>
      - - - - <nullable>
      - -
      - - - - -
      Properties
      -
      +?>
      NameTypeAttributesDefaultDescription
      optionalnullable") ?>
      Properties
      diff --git a/internal/documentation/jsdoc/docdash/tmpl/returns.tmpl b/internal/documentation/jsdoc/docdash/tmpl/returns.tmpl index 23fefa49246..bf3e18fc412 100644 --- a/internal/documentation/jsdoc/docdash/tmpl/returns.tmpl +++ b/internal/documentation/jsdoc/docdash/tmpl/returns.tmpl @@ -2,18 +2,10 @@ var data = obj || {}; if (data.description) { ?> -
      - -
      + + -
      -
      - Type -
      -
      - -
      -
      - \ No newline at end of file +**Type:** + diff --git a/internal/documentation/jsdoc/docdash/tmpl/source.tmpl b/internal/documentation/jsdoc/docdash/tmpl/source.tmpl index e559b5d1038..38334dc60e2 100644 --- a/internal/documentation/jsdoc/docdash/tmpl/source.tmpl +++ b/internal/documentation/jsdoc/docdash/tmpl/source.tmpl @@ -1,8 +1,6 @@ -
      -
      -
      -
      -
      \ No newline at end of file +``` + +``` diff --git a/internal/documentation/jsdoc/docdash/tmpl/tutorial.tmpl b/internal/documentation/jsdoc/docdash/tmpl/tutorial.tmpl index 88a0ad52aa2..40a160efc8f 100644 --- a/internal/documentation/jsdoc/docdash/tmpl/tutorial.tmpl +++ b/internal/documentation/jsdoc/docdash/tmpl/tutorial.tmpl @@ -1,19 +1,13 @@ -
      + 0) { ?> +## Child Tutorials -
      - 0) { ?> -
        -
      • -
      - + +- + + -

      -
      +## -
      - -
      - -
      + diff --git a/internal/documentation/jsdoc/docdash/tmpl/type.tmpl b/internal/documentation/jsdoc/docdash/tmpl/type.tmpl index 415cafb9330..33434d33aa4 100644 --- a/internal/documentation/jsdoc/docdash/tmpl/type.tmpl +++ b/internal/documentation/jsdoc/docdash/tmpl/type.tmpl @@ -20,7 +20,4 @@ if (longname) { link = link.replace(longname, resolvedName); } - ?> - -| - + ?> \| \ No newline at end of file diff --git a/internal/documentation/jsdoc/jsdoc-workspace.json b/internal/documentation/jsdoc/jsdoc-workspace.json index 92340614658..f0eccb0489d 100644 --- a/internal/documentation/jsdoc/jsdoc-workspace.json +++ b/internal/documentation/jsdoc/jsdoc-workspace.json @@ -18,7 +18,7 @@ ], "opts": { "encoding": "utf8", - "destination": "dist/api/", + "destination": "docs/api/", "recurse": true, "verbose": true, "access": "public" @@ -27,11 +27,7 @@ "cleverLinks": false, "monospaceLinks": false, "default": { - "useLongnameInNav": true, - "layoutFile": "jsdoc/templates/layout.tmpl", - "staticFiles": { - "include": ["jsdoc/templates/custom.css"] - } + "useLongnameInNav": true } }, "openGraph": { @@ -66,6 +62,7 @@ "class": "menu-item", "id": "github_link" } - } + }, + "githubSourceBaseUrl": "https://github.com/UI5/cli/blob/main/packages" } } diff --git a/internal/documentation/jsdoc/jsdoc.json b/internal/documentation/jsdoc/jsdoc.json index ab5fdc7dfee..3a8af30993d 100644 --- a/internal/documentation/jsdoc/jsdoc.json +++ b/internal/documentation/jsdoc/jsdoc.json @@ -17,7 +17,7 @@ ], "opts": { "encoding": "utf8", - "destination": "dist/api/", + "destination": "docs/api/", "recurse": true, "verbose": true, "access": "public" @@ -26,11 +26,7 @@ "cleverLinks": false, "monospaceLinks": false, "default": { - "useLongnameInNav": true, - "layoutFile": "jsdoc/templates/layout.tmpl", - "staticFiles": { - "include": ["jsdoc/templates/custom.css"] - } + "useLongnameInNav": true } }, "openGraph": { @@ -65,6 +61,7 @@ "class": "menu-item", "id": "github_link" } - } + }, + "githubSourceBaseUrl": "https://github.com/UI5/cli/blob/main/packages/" } } diff --git a/internal/documentation/jsdoc/templates/custom.css b/internal/documentation/jsdoc/templates/custom.css deleted file mode 100644 index c38efbd9f2f..00000000000 --- a/internal/documentation/jsdoc/templates/custom.css +++ /dev/null @@ -1,13 +0,0 @@ -footer { - display: flex; -} -.ui5-footer-item { - flex-grow: 0; - margin: auto 0.6rem; - padding: 0.4rem 0; - white-space: nowrap; -} -.ui5-footer-item-growing { - flex-grow: 1; - white-space: normal; -} diff --git a/internal/documentation/jsdoc/templates/layout.tmpl b/internal/documentation/jsdoc/templates/layout.tmpl deleted file mode 100644 index 63bad429df7..00000000000 --- a/internal/documentation/jsdoc/templates/layout.tmpl +++ /dev/null @@ -1,135 +0,0 @@ - - - - - - - <?js= title ?> - <?js= ((env.conf.docdash.meta && env.conf.docdash.meta.title) || "Documentation") ?> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      - -

      - - - - - -
      - - - -
      - -
      - - - - - - - - - - - - - - - - - - - - - diff --git a/internal/documentation/package.json b/internal/documentation/package.json index dd1b6c824f4..6362a295459 100644 --- a/internal/documentation/package.json +++ b/internal/documentation/package.json @@ -30,8 +30,8 @@ "build:assets": "sh -c 'DEST_DIR=${1:-./dist}; cp -r ./docs/images \"$DEST_DIR/images\"' --", "preview": "vitepress preview --port 8080", "jsdoc": "npm run jsdoc-generate && open-cli dist/api/index.html", - "jsdoc-generate": "jsdoc -c jsdoc/jsdoc-workspace.json -t $(node -p 'path.dirname(require.resolve(\"docdash\"))') ./ || (echo 'Error during JSDoc generation! Check log.' && exit 1)", - "jsdoc-generate-gh-pages": "jsdoc -c jsdoc/jsdoc.json -t $(node -p 'path.dirname(require.resolve(\"docdash\"))') ./ || (echo 'Error during JSDoc generation! Check log.' && exit 1)", + "jsdoc-generate": "jsdoc -c jsdoc/jsdoc-workspace.json -t jsdoc/docdash ./ || (echo 'Error during JSDoc generation! Check log.' && exit 1) && rm -r docs/api/*.js.md", + "jsdoc-generate-gh-pages": "jsdoc -c jsdoc/jsdoc.json -t jsdoc/docdash ./ || (echo 'Error during JSDoc generation! Check log.' && exit 1) && rm -r docs/api/*.js.md", "generate-cli-doc": "node ./scripts/generateCliDoc.js", "schema-generate": "node ./scripts/buildSchema.js", "schema-generate-gh-pages": "node ./scripts/buildSchema.js gh-pages", @@ -50,7 +50,7 @@ }, "devDependencies": { "@apidevtools/json-schema-ref-parser": "^14.2.1", - "docdash": "^2.0.2", + "@jsdoc/salty": "^0.2.9", "handlebars": "^4.7.8", "jsdoc": "^4.0.4", "open-cli": "^8.0.0", diff --git a/package-lock.json b/package-lock.json index 923f496d358..d13e3a6499f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -66,7 +66,7 @@ }, "devDependencies": { "@apidevtools/json-schema-ref-parser": "^14.2.1", - "docdash": "^2.0.2", + "@jsdoc/salty": "^0.2.9", "handlebars": "^4.7.8", "jsdoc": "^4.0.4", "open-cli": "^8.0.0", @@ -6510,6 +6510,30 @@ "url": "https://github.com/sponsors/fb55" } }, + "node_modules/cheerio/node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/cheerio/node_modules/parse5": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", + "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", + "license": "MIT", + "dependencies": { + "entities": "^6.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, "node_modules/chokidar": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", @@ -8256,16 +8280,6 @@ "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", "license": "MIT" }, - "node_modules/docdash": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/docdash/-/docdash-2.0.2.tgz", - "integrity": "sha512-3SDDheh9ddrwjzf6dPFe1a16M6ftstqTNjik2+1fx46l24H9dD2osT2q9y+nBEC1wWz4GIqA48JmicOLQ0R8xA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@jsdoc/salty": "^0.2.1" - } - }, "node_modules/docopt": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/docopt/-/docopt-0.6.2.tgz", @@ -8454,6 +8468,16 @@ "node": ">= 0.8" } }, + "node_modules/encoding": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", + "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", + "license": "MIT", + "optional": true, + "dependencies": { + "iconv-lite": "^0.6.2" + } + }, "node_modules/encoding-sniffer": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/encoding-sniffer/-/encoding-sniffer-0.2.1.tgz", @@ -8479,6 +8503,19 @@ "node": ">=0.10.0" } }, + "node_modules/encoding/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "license": "MIT", + "optional": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/enhance-visitors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/enhance-visitors/-/enhance-visitors-1.0.0.tgz", @@ -15510,26 +15547,38 @@ "dev": true, "license": "MIT" }, - "node_modules/parse5": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", - "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", + "node_modules/parse5-htmlparser2-tree-adapter": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.1.0.tgz", + "integrity": "sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==", "license": "MIT", "dependencies": { - "entities": "^6.0.0" + "domhandler": "^5.0.3", + "parse5": "^7.0.0" }, "funding": { "url": "https://github.com/inikulin/parse5?sponsor=1" } }, - "node_modules/parse5-htmlparser2-tree-adapter": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.1.0.tgz", - "integrity": "sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==", + "node_modules/parse5-htmlparser2-tree-adapter/node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/parse5-htmlparser2-tree-adapter/node_modules/parse5": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", + "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", "license": "MIT", "dependencies": { - "domhandler": "^5.0.3", - "parse5": "^7.0.0" + "entities": "^6.0.0" }, "funding": { "url": "https://github.com/inikulin/parse5?sponsor=1" @@ -15547,7 +15596,7 @@ "url": "https://github.com/inikulin/parse5?sponsor=1" } }, - "node_modules/parse5/node_modules/entities": { + "node_modules/parse5-parser-stream/node_modules/entities": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", @@ -15559,6 +15608,18 @@ "url": "https://github.com/fb55/entities?sponsor=1" } }, + "node_modules/parse5-parser-stream/node_modules/parse5": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", + "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", + "license": "MIT", + "dependencies": { + "entities": "^6.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, "node_modules/parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", From 51a6a362aaa3f4d44688c727f5a1b4d4add64ba6 Mon Sep 17 00:00:00 2001 From: Gianluca Beil Date: Wed, 21 Jan 2026 09:51:07 +0100 Subject: [PATCH 03/15] docs: Don't replace all occurrences of .md# This had the risk of potentially destroying an external link that pointed to an .md file with a line number --- internal/documentation/jsdoc/docdash/tmpl/container.tmpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/documentation/jsdoc/docdash/tmpl/container.tmpl b/internal/documentation/jsdoc/docdash/tmpl/container.tmpl index fc13a900a0d..420832f2d07 100644 --- a/internal/documentation/jsdoc/docdash/tmpl/container.tmpl +++ b/internal/documentation/jsdoc/docdash/tmpl/container.tmpl @@ -146,7 +146,7 @@ ### Methods - + From d2110c3dd24dae9f649d3d8beb8bc2594b0b9e6a Mon Sep 17 00:00:00 2001 From: Gianluca Beil Date: Wed, 21 Jan 2026 09:52:01 +0100 Subject: [PATCH 04/15] docs: Highlight modified code in publish.js and don't write source files .js.md --- .../documentation/jsdoc/docdash/publish.js | 1349 +++++++++-------- internal/documentation/package.json | 4 +- 2 files changed, 723 insertions(+), 630 deletions(-) diff --git a/internal/documentation/jsdoc/docdash/publish.js b/internal/documentation/jsdoc/docdash/publish.js index 1aef74c1552..74635ea6eaf 100644 --- a/internal/documentation/jsdoc/docdash/publish.js +++ b/internal/documentation/jsdoc/docdash/publish.js @@ -12,247 +12,244 @@ var util = require('node:util'); var htmlsafe = helper.htmlsafe; var resolveAuthorLinks = helper.resolveAuthorLinks; +// Not needed anymore +//var linkto = helper.linkto; +//var scopeToPunc = helper.scopeToPunc; +//var hasOwnProp = Object.prototype.hasOwnProperty; var data; var view; + +// Modified to be able to link source files to GitHub var githubSourceBaseUrl; var outdir = path.normalize(env.opts.destination); function find(spec) { - return helper.find(data, spec); + return helper.find(data, spec); } function tutoriallink(tutorial) { - return helper.toTutorial(tutorial, null, { tag: 'em', classname: 'disabled', prefix: 'Tutorial: ' }); + return helper.toTutorial(tutorial, null, { tag: 'em', classname: 'disabled', prefix: '' }); } function getAncestorLinks(doclet) { - return helper.getAncestorLinks(data, doclet); + return helper.getAncestorLinks(data, doclet); } +// Modified to output Markdown style links function hashToLink(doclet, hash) { - if ( !/^(#.+)/.test(hash) ) { return hash; } + if ( !/^(#.+)/.test(hash) ) { return hash; } - var url = helper.createLink(doclet); + var url = helper.createLink(doclet); - url = url.replace(/(#.+|$)/, hash); - return '[' + hash + '](' + url + ')'; + url = url.replace(/(#.+|$)/, hash); + return '[' + hash + '](' + url + ')'; } function needsSignature(doclet) { - var needsSig = false; - - // function and class definitions always get a signature - if (doclet.kind === 'function' || doclet.kind === 'class' && !doclet.hideconstructor) { - needsSig = true; - } - // typedefs that contain functions get a signature, too - else if (doclet.kind === 'typedef' && doclet.type && doclet.type.names && - doclet.type.names.length) { - for (var i = 0, l = doclet.type.names.length; i < l; i++) { - if (doclet.type.names[i].toLowerCase() === 'function') { - needsSig = true; - break; - } - } - } - // and namespaces that are functions get a signature (but finding them is a - // bit messy) - else if (doclet.kind === 'namespace' && doclet.meta && doclet.meta.code && - doclet.meta.code.type && doclet.meta.code.type.match(/[Ff]unction/)) { - needsSig = true; - } - - return needsSig; + var needsSig = false; + + // function and class definitions always get a signature + if (doclet.kind === 'function' || doclet.kind === 'class') { + needsSig = true; + } + // typedefs that contain functions get a signature, too + else if (doclet.kind === 'typedef' && doclet.type && doclet.type.names && + doclet.type.names.length) { + for (var i = 0, l = doclet.type.names.length; i < l; i++) { + if (doclet.type.names[i].toLowerCase() === 'function') { + needsSig = true; + break; + } + } + } + + return needsSig; } function getSignatureAttributes(item) { - var attributes = []; + var attributes = []; - if (item.optional) { - attributes.push('opt'); - } + if (item.optional) { + attributes.push('opt'); + } - if (item.nullable === true) { - attributes.push('nullable'); - } - else if (item.nullable === false) { - attributes.push('non-null'); - } + if (item.nullable === true) { + attributes.push('nullable'); + } + else if (item.nullable === false) { + attributes.push('non-null'); + } - return attributes; + return attributes; } function updateItemName(item) { - var attributes = getSignatureAttributes(item); - var itemName = item.name || ''; + var attributes = getSignatureAttributes(item); + var itemName = item.name || ''; - if (item.variable) { - itemName = '…' + itemName; - } + if (item.variable) { + itemName = '…' + itemName; + } - if (attributes && attributes.length) { - itemName = util.format( '%s%s', itemName, - attributes.join(', ') ); - } + if (attributes && attributes.length) { + itemName = util.format( '%s%s', itemName, + attributes.join(', ') ); + } - return itemName; + return itemName; } function addParamAttributes(params) { - return params.filter(function(param) { - return param.name && param.name.indexOf('.') === -1; - }).map(updateItemName); + return params.filter(function(param) { + return param.name && param.name.indexOf('.') === -1; + }).map(updateItemName); } function buildItemTypeStrings(item) { - var types = []; + var types = []; - if (item && item.type && item.type.names) { - item.type.names.forEach(function(name) { - types.push( linkTo(name, htmlsafe(name)) ); - }); - } + if (item && item.type && item.type.names) { + item.type.names.forEach(function(name) { + types.push( linkTo(name, htmlsafe(name)) ); + }); + } - return types; + return types; } function buildAttribsString(attribs) { - var attribsString = ''; + var attribsString = ''; - if (attribs && attribs.length) { - attribsString = htmlsafe( util.format('(%s) ', attribs.join(', ')) ); - } + if (attribs && attribs.length) { + attribsString = htmlsafe( util.format('(%s) ', attribs.join(', ')) ); + } - return attribsString; + return attribsString; } function addNonParamAttributes(items) { - var types = []; + var types = []; - items.forEach(function(item) { - types = types.concat( buildItemTypeStrings(item) ); - }); + items.forEach(function(item) { + types = types.concat( buildItemTypeStrings(item) ); + }); - return types; + return types; } function addSignatureParams(f) { - var params = f.params ? addParamAttributes(f.params) : []; - f.signature = util.format( '%s(%s)', (f.signature || ''), params.join(', ') ); + var params = f.params ? addParamAttributes(f.params) : []; + f.signature = util.format( '%s(%s)', (f.signature || ''), params.join(', ') ); } function addSignatureReturns(f) { - var attribs = []; - var attribsString = ''; - var returnTypes = []; - var returnTypesString = ''; - var source = f.yields || f.returns; - - // jam all the return-type attributes into an array. this could create odd results (for example, - // if there are both nullable and non-nullable return types), but let's assume that most people - // who use multiple @return tags aren't using Closure Compiler type annotations, and vice-versa. - if (source) { - source.forEach(function(item) { - helper.getAttribs(item).forEach(function(attrib) { - if (attribs.indexOf(attrib) === -1) { - attribs.push(attrib); - } - }); - }); - - attribsString = buildAttribsString(attribs); - } - - if (source) { - returnTypes = addNonParamAttributes(source); - } - if (returnTypes.length) { - returnTypesString = util.format( ' → %s{%s}', attribsString, returnTypes.join('|') ); - } - - f.signature = '' + (f.signature || '') + '' + - '' + returnTypesString + ''; + var attribs = []; + var attribsString = ''; + var returnTypes = []; + var returnTypesString = ''; + + // jam all the return-type attributes into an array. this could create odd results (for example, + // if there are both nullable and non-nullable return types), but let's assume that most people + // who use multiple @return tags aren't using Closure Compiler type annotations, and vice-versa. + if (f.returns) { + f.returns.forEach(function(item) { + helper.getAttribs(item).forEach(function(attrib) { + if (attribs.indexOf(attrib) === -1) { + attribs.push(attrib); + } + }); + }); + + attribsString = buildAttribsString(attribs); + } + + if (f.returns) { + returnTypes = addNonParamAttributes(f.returns); + } + if (returnTypes.length) { + returnTypesString = util.format( ' → %s{%s}', attribsString, returnTypes.join('|') ); + } + + // Modified to support coloring in Vitepress + f.signature = '' + (f.signature || '') + '' + + '' + returnTypesString + ''; } +// Modified to support coloring in Vitepress function addSignatureTypes(f) { - var types = f.type ? buildItemTypeStrings(f) : []; + var types = f.type ? buildItemTypeStrings(f) : []; - f.signature = (f.signature || '') + '' + - (types.length ? ' :' + types.join('|') : '') + ''; + f.signature = (f.signature || '') + '' + + (types.length ? ' :' + types.join('|') : '') + ''; } function addAttribs(f) { - var attribs = helper.getAttribs(f); - var attribsString = buildAttribsString(attribs); - if (attribsString && attribsString.length) { - f.attribs = "
      " + util.format('%s', attribsString); - } - else { - f.attribs = "
      " + util.format('%s', attribsString); - } + var attribs = helper.getAttribs(f); + var attribsString = buildAttribsString(attribs); + + f.attribs = util.format('%s', attribsString); } function shortenPaths(files, commonPrefix) { - Object.keys(files).forEach(function(file) { - files[file].shortened = files[file].resolved.replace(commonPrefix, '') - // always use forward slashes - .replace(/\\/g, '/'); - }); + Object.keys(files).forEach(function(file) { + files[file].shortened = files[file].resolved.replace(commonPrefix, '') + // always use forward slashes + .replace(/\\/g, '/'); + }); - return files; + return files; } function getPathFromDoclet(doclet) { - if (!doclet.meta) { - return null; - } + if (!doclet.meta) { + return null; + } - return doclet.meta.path && doclet.meta.path !== 'null' ? - path.join(doclet.meta.path, doclet.meta.filename) : - doclet.meta.filename; + return doclet.meta.path && doclet.meta.path !== 'null' ? + path.join(doclet.meta.path, doclet.meta.filename) : + doclet.meta.filename; } function generate(type, title, docs, filename, resolveLinks) { - resolveLinks = resolveLinks === false ? false : true; + resolveLinks = resolveLinks === false ? false : true; - var docData = { - type: type, - title: title, - docs: docs - }; + var docData = { + type: type, + title: title, + docs: docs + }; - var outpath = path.join(outdir, filename), - html = view.render('container.tmpl', docData); + var outpath = path.join(outdir, filename), + html = view.render('container.tmpl', docData); - if (resolveLinks) { - html = helper.resolveLinks(html); // turn {@link foo} into foo - } + if (resolveLinks) { + html = helper.resolveLinks(html); // turn {@link foo} into foo + } - fs.writeFileSync(outpath, html, 'utf8'); + fs.writeFileSync(outpath, html, 'utf8'); } +// Modified: Don't write source files function generateSourceFiles(sourceFiles, encoding) { - encoding = encoding || 'utf8'; - Object.keys(sourceFiles).forEach(function(file) { - var source; - // links are keyed to the shortened path in each doclet's `meta.shortpath` property - var sourceOutfile = helper.getUniqueFilename(sourceFiles[file].shortened); - helper.registerLink(sourceFiles[file].shortened, sourceOutfile); - - try { - source = { - kind: 'source', - code: helper.htmlsafe( fs.readFileSync(sourceFiles[file].resolved, encoding) ) - }; - } - catch(e) { - logger.error('Error while generating source file %s: %s', file, e.message); - } - - generate('Source', sourceFiles[file].shortened, [source], sourceOutfile, false); - }); + encoding = encoding || 'utf8'; + Object.keys(sourceFiles).forEach(function(file) { + var source; + // links are keyed to the shortened path in each doclet's `meta.shortpath` property + var sourceOutfile = helper.getUniqueFilename(sourceFiles[file].shortened); + helper.registerLink(sourceFiles[file].shortened, sourceOutfile); + + try { + source = { + kind: 'source', + code: helper.htmlsafe( fs.readFileSync(sourceFiles[file].resolved, encoding) ) + }; + } + catch(e) { + logger.error('Error while generating source file %s: %s', file, e.message); + } + }); } /** @@ -267,34 +264,136 @@ function generateSourceFiles(sourceFiles, encoding) { * @param {Array.} modules - The array of module doclets to search. */ function attachModuleSymbols(doclets, modules) { - var symbols = {}; - - // build a lookup table - doclets.forEach(function(symbol) { - symbols[symbol.longname] = symbols[symbol.longname] || []; - symbols[symbol.longname].push(symbol); - }); - - return modules.map(function(module) { - if (symbols[module.longname]) { - module.modules = symbols[module.longname] - // Only show symbols that have a description. Make an exception for classes, because - // we want to show the constructor-signature heading no matter what. - .filter(function(symbol) { - return symbol.description || symbol.kind === 'class'; - }) - .map(function(symbol) { - symbol = doop(symbol); - - if (symbol.kind === 'class' || symbol.kind === 'function' && !symbol.hideconstructor) { - symbol.name = symbol.name.replace('module:', '(require("') + '"))'; - } - - return symbol; - }); - } - }); + var symbols = {}; + + // build a lookup table + doclets.forEach(function(symbol) { + symbols[symbol.longname] = symbols[symbol.longname] || []; + symbols[symbol.longname].push(symbol); + }); + + return modules.map(function(module) { + if (symbols[module.longname]) { + module.modules = symbols[module.longname] + // Only show symbols that have a description. Make an exception for classes, because + // we want to show the constructor-signature heading no matter what. + .filter(function(symbol) { + return symbol.description || symbol.kind === 'class'; + }) + .map(function(symbol) { + symbol = doop(symbol); + + if (symbol.kind === 'class' || symbol.kind === 'function') { + symbol.name = symbol.name.replace('module:', '(require("') + '"))'; + } + + return symbol; + }); + } + }); +} + +// Removed navbar functionality +/* +function buildMemberNav(items, itemHeading, itemsSeen, linktoFn) { + var nav = ''; + + if (items && items.length) { + var itemsNav = ''; + + items.forEach(function(item) { + var methods = find({kind:'function', memberof: item.longname}); + var members = find({kind:'member', memberof: item.longname}); + var docdash = env && env.conf && env.conf.docdash || {}; + + if ( !hasOwnProp.call(item, 'longname') ) { + itemsNav += '
    • ' + linktoFn('', item.name); + itemsNav += '
    • '; + } else if ( !hasOwnProp.call(itemsSeen, item.longname) ) { + itemsNav += '
    • ' + linktoFn(item.longname, item.name.replace(/^module:/, '')); + + if (docdash.static && members.find(function (m) { return m.scope === 'static'; } )) { + itemsNav += "
        "; + + members.forEach(function (member) { + if (!member.scope === 'static') return; + itemsNav += "
      • "; + itemsNav += linkto(member.longname, member.name); + itemsNav += "
      • "; + }); + + itemsNav += "
      "; + } + + if (methods.length) { + itemsNav += "
        "; + + methods.forEach(function (method) { + itemsNav += "
      • "; + itemsNav += linkto(method.longname, method.name); + itemsNav += "
      • "; + }); + + itemsNav += "
      "; + } + + itemsNav += '
    • '; + itemsSeen[item.longname] = true; + } + }); + + if (itemsNav !== '') { + nav += '

      ' + itemHeading + '

        ' + itemsNav + '
      '; + } + } + + return nav; +} + +function linktoTutorial(longName, name) { + return tutoriallink(name); +} + +function linktoExternal(longName, name) { + return linkto(longName, name.replace(/(^"|"$)/g, '')); +} + +function buildNav(members) { + var nav = '

      Home

      '; + var seen = {}; + var seenTutorials = {}; + + nav += buildMemberNav(members.tutorials, 'Tutorials', seenTutorials, linktoTutorial); + nav += buildMemberNav(members.classes, 'Classes', seen, linkto); + nav += buildMemberNav(members.modules, 'Modules', {}, linkto); + nav += buildMemberNav(members.externals, 'Externals', seen, linktoExternal); + nav += buildMemberNav(members.events, 'Events', seen, linkto); + nav += buildMemberNav(members.namespaces, 'Namespaces', seen, linkto); + nav += buildMemberNav(members.mixins, 'Mixins', seen, linkto); + nav += buildMemberNav(members.interfaces, 'Interfaces', seen, linkto); + + if (members.globals.length) { + var globalNav = ''; + + members.globals.forEach(function(g) { + if ( g.kind !== 'typedef' && !hasOwnProp.call(seen, g.longname) ) { + globalNav += '
    • ' + linkto(g.longname, g.name) + '
    • '; + } + seen[g.longname] = true; + }); + + if (!globalNav) { + // turn the heading into a link so you can actually get to the global page + nav += '

      ' + linkto('global', 'Global') + '

      '; + } + else { + nav += '

      Global

        ' + globalNav + '
      '; + } + } + + return nav; } +*/ /** @param {TAFFY} taffyData See . @@ -302,462 +401,456 @@ function attachModuleSymbols(doclets, modules) { @param {Tutorial} tutorials */ exports.publish = function(taffyData, opts, tutorials) { - helper.fileExtension = ".md"; - - var docdash = env && env.conf && env.conf.docdash || {}; - data = taffyData; - - var conf = env.conf.templates || {}; - conf.default = conf.default || {}; - - var templatePath = path.normalize(opts.template); - view = new template.Template( path.join(templatePath, 'tmpl') ); - - // Store GitHub base URL for source file links - githubSourceBaseUrl = docdash.githubSourceBaseUrl || null; - - // claim some special filenames in advance, so the All-Powerful Overseer of Filename Uniqueness - // doesn't try to hand them out later - var indexUrl = helper.getUniqueFilename('index'); - // don't call registerLink() on this one! 'index' is also a valid longname - - var globalUrl = helper.getUniqueFilename('global'); - helper.registerLink('global', globalUrl); - - // set up templating - view.layout = conf.default.layoutFile ? - path.getResourcePath(path.dirname(conf.default.layoutFile), - path.basename(conf.default.layoutFile) ) : - 'layout.tmpl'; - - // set up tutorials for helper - helper.setTutorials(tutorials); - - data = helper.prune(data); - - docdash.sort !== false && data.sort('longname, version, since'); - helper.addEventListeners(data); - - var sourceFiles = {}; - var sourceFilePaths = []; - data().each(function(doclet) { - if(docdash.removeQuotes){ - if(docdash.removeQuotes === "all"){ - if(doclet.name){ - doclet.name = doclet.name.replace(/"/g, ''); - doclet.name = doclet.name.replace(/'/g, ''); - } - if(doclet.longname){ - doclet.longname = doclet.longname.replace(/"/g, ''); - doclet.longname = doclet.longname.replace(/'/g, ''); - } - } - else if(docdash.removeQuotes === "trim"){ - if(doclet.name){ - doclet.name = doclet.name.replace(/^"(.*)"$/, '$1'); - doclet.name = doclet.name.replace(/^'(.*)'$/, '$1'); - } - if(doclet.longname){ - doclet.longname = doclet.longname.replace(/^"(.*)"$/, '$1'); - doclet.longname = doclet.longname.replace(/^'(.*)'$/, '$1'); - } - } - } - doclet.attribs = ''; - - if (doclet.examples) { - doclet.examples = doclet.examples.map(function(example) { - var caption, code; - - if (example && example.match(/^\s*([\s\S]+?)<\/caption>(\s*[\n\r])([\s\S]+)$/i)) { - caption = RegExp.$1; - code = RegExp.$3; - } - - return { - caption: caption || '', - code: code || example || '' - }; - }); - } - if (doclet.see) { - doclet.see.forEach(function(seeItem, i) { - doclet.see[i] = hashToLink(doclet, seeItem); - }); - } - - // build a list of source files - var sourcePath; - if (doclet.meta) { - sourcePath = getPathFromDoclet(doclet); - sourceFiles[sourcePath] = { - resolved: sourcePath, - shortened: null - }; - if (sourceFilePaths.indexOf(sourcePath) === -1) { - sourceFilePaths.push(sourcePath); - } - } - }); - - // update outdir if necessary, then create outdir - var packageInfo = ( find({kind: 'package'}) || [] ) [0]; - if (packageInfo) { - var subdirs = [outdir]; - - if (packageInfo.name) { - var packageName = packageInfo.name.split('/'); - - if (packageName.length > 1 && docdash.scopeInOutputPath !== false) { - subdirs.push(packageName[0]); - } - - if (docdash.nameInOutputPath !== false) { - subdirs.push((packageName.length > 1 ? packageName[1] : packageName[0])); - } - } - - if (packageInfo.version && docdash.versionInOutputPath !== false) { - subdirs.push(packageInfo.version); - } - - if (subdirs.length > 1) { - outdir = path.join.apply(null, subdirs); - } - } - - fs.mkPath(outdir); - - if (sourceFilePaths.length) { - sourceFiles = shortenPaths( sourceFiles, path.commonPrefix(sourceFilePaths) ); - } - data().each(function(doclet) { - var url = helper.createLink(doclet); - if (docdash.noURLEncode) { - url = decodeURI(url); - } - helper.registerLink(doclet.longname, url); - - // add a shortened version of the full path - var docletPath; - if (doclet.meta) { - docletPath = getPathFromDoclet(doclet); - docletPath = sourceFiles[docletPath].shortened; - if (docletPath) { - doclet.meta.shortpath = docletPath; - } - } - }); - - data().each(function(doclet) { - var url = helper.longnameToUrl[doclet.longname]; - - if (url.indexOf('#') > -1) { - doclet.id = helper.longnameToUrl[doclet.longname].split(/#/).pop(); - } - else { - doclet.id = doclet.name; - } - - if ( needsSignature(doclet) ) { - addSignatureParams(doclet); - addSignatureReturns(doclet); - addAttribs(doclet); - } - }); - - // do this after the urls have all been generated - data().each(function(doclet) { - doclet.ancestors = getAncestorLinks(doclet); - - if (doclet.kind === 'member') { - addSignatureTypes(doclet); - addAttribs(doclet); - } - - if (doclet.kind === 'constant') { - addSignatureTypes(doclet); - addAttribs(doclet); - doclet.kind = 'member'; - } - }); - - var members = helper.getMembers(data); - members.tutorials = tutorials.children; - - // output pretty-printed source files by default - var outputSourceFiles = conf.default && conf.default.outputSourceFiles !== false - ? true - : false; - - // add template helpers - view.find = find; - view.linkto = linkTo; - view.resolveAuthorLinks = resolveAuthorLinks; - view.tutoriallink = tutoriallink; - view.htmlsafe = htmlsafe; - view.outputSourceFiles = outputSourceFiles; - - // once for all - attachModuleSymbols( find({ longname: {left: 'module:'} }), members.modules ); - - // generate the pretty-printed source files first so other pages can link to them - if (outputSourceFiles) { - generateSourceFiles(sourceFiles, opts.encoding); - } - - if (members.globals.length) { - generate('', 'Global', [{kind: 'globalobj'}], globalUrl); - } - - // index page displays information from package.json and lists files - var files = find({kind: 'file'}); - var packages = find({kind: 'package'}); - - generate('', 'Home', - packages.concat( - [{kind: 'mainpage', readme: opts.readme, longname: (opts.mainpagetitle) ? opts.mainpagetitle : 'Main Page'}] - ).concat(files), - indexUrl); - - // set up the lists that we'll use to generate pages - var classes = taffy(members.classes); - var modules = taffy(members.modules); - var namespaces = taffy(members.namespaces); - var mixins = taffy(members.mixins); - var externals = taffy(members.externals); - var interfaces = taffy(members.interfaces); - - Object.keys(helper.longnameToUrl).forEach(function(longname) { - var myModules = helper.find(modules, {longname: longname}); - if (myModules.length) { - generate('Module', myModules[0].name, myModules, helper.longnameToUrl[longname]); - } - - var myClasses = helper.find(classes, {longname: longname}); - if (myClasses.length) { - generate('Class', myClasses[0].name, myClasses, helper.longnameToUrl[longname]); - } - - var myNamespaces = helper.find(namespaces, {longname: longname}); - if (myNamespaces.length) { - generate('Namespace', myNamespaces[0].name, myNamespaces, helper.longnameToUrl[longname]); - } - - var myMixins = helper.find(mixins, {longname: longname}); - if (myMixins.length) { - generate('Mixin', myMixins[0].name, myMixins, helper.longnameToUrl[longname]); - } - - var myExternals = helper.find(externals, {longname: longname}); - if (myExternals.length) { - generate('External', myExternals[0].name, myExternals, helper.longnameToUrl[longname]); - } - - var myInterfaces = helper.find(interfaces, {longname: longname}); - if (myInterfaces.length) { - generate('Interface', myInterfaces[0].name, myInterfaces, helper.longnameToUrl[longname]); - } - }); - - // TODO: move the tutorial functions to templateHelper.js - function generateTutorial(title, tutorial, filename) { - var tutorialData = { - title: title, - header: tutorial.title, - content: tutorial.parse(), - children: tutorial.children - }; - - var tutorialPath = path.join(outdir, filename); - var html = view.render('tutorial.tmpl', tutorialData); - - // yes, you can use {@link} in tutorials too! - html = helper.resolveLinks(html); // turn {@link foo} into foo - fs.writeFileSync(tutorialPath, html, 'utf8'); - } - - // tutorials can have only one parent so there is no risk for loops - function saveChildren(node) { - node.children.forEach(function(child) { - generateTutorial('Tutorial: ' + child.title, child, helper.tutorialToUrl(child.name)); - saveChildren(child); - }); - } - - saveChildren(tutorials); + // Modified from .html to .md to output Markdown files + helper.fileExtension = ".md"; + + var docdash = env && env.conf && env.conf.docdash || {}; + data = taffyData; + + var conf = env.conf.templates || {}; + conf.default = conf.default || {}; + + var templatePath = path.normalize(opts.template); + view = new template.Template( path.join(templatePath, 'tmpl') ); + + // Modified + // Store GitHub base URL for source file links + githubSourceBaseUrl = docdash.githubSourceBaseUrl || null; + + // claim some special filenames in advance, so the All-Powerful Overseer of Filename Uniqueness + // doesn't try to hand them out later + var indexUrl = helper.getUniqueFilename('index'); + // don't call registerLink() on this one! 'index' is also a valid longname + + var globalUrl = helper.getUniqueFilename('global'); + helper.registerLink('global', globalUrl); + + // set up templating + view.layout = conf.default.layoutFile ? + path.getResourcePath(path.dirname(conf.default.layoutFile), + path.basename(conf.default.layoutFile) ) : + 'layout.tmpl'; + + // set up tutorials for helper + helper.setTutorials(tutorials); + + data = helper.prune(data); + + docdash.sort !== false && data.sort('longname, version, since'); + helper.addEventListeners(data); + + var sourceFiles = {}; + var sourceFilePaths = []; + data().each(function(doclet) { + doclet.attribs = ''; + + if (doclet.examples) { + doclet.examples = doclet.examples.map(function(example) { + var caption, code; + + if (example.match(/^\s*([\s\S]+?)<\/caption>(\s*[\n\r])([\s\S]+)$/i)) { + caption = RegExp.$1; + code = RegExp.$3; + } + + return { + caption: caption || '', + code: code || example + }; + }); + } + if (doclet.see) { + doclet.see.forEach(function(seeItem, i) { + doclet.see[i] = hashToLink(doclet, seeItem); + }); + } + + // build a list of source files + var sourcePath; + if (doclet.meta) { + sourcePath = getPathFromDoclet(doclet); + sourceFiles[sourcePath] = { + resolved: sourcePath, + shortened: null + }; + if (sourceFilePaths.indexOf(sourcePath) === -1) { + sourceFilePaths.push(sourcePath); + } + } + }); + + /// update outdir if necessary, then create outdir + var packageInfo = ( find({kind: 'package'}) || [] ) [0]; + if (packageInfo && packageInfo.name) { + outdir = path.join( outdir, packageInfo.name, (packageInfo.version || '') ); + } + fs.mkPath(outdir); + + // Modified: Static files are not needed anymore + // copy the template's static files to outdir + /*var fromDir = path.join(templatePath, 'static'); + var staticFiles = fs.ls(fromDir, 3); + + staticFiles.forEach(function(fileName) { + var toDir = fs.toDir( fileName.replace(fromDir, outdir) ); + fs.mkPath(toDir); + fs.copyFileSync(fileName, toDir); + }); + + // copy user-specified static files to outdir + var staticFilePaths; + var staticFileFilter; + var staticFileScanner; + if (conf.default.staticFiles) { + // The canonical property name is `include`. We accept `paths` for backwards compatibility + // with a bug in JSDoc 3.2.x. + staticFilePaths = conf.default.staticFiles.include || + conf.default.staticFiles.paths || + []; + staticFileFilter = new (require('jsdoc/src/filter')).Filter(conf.default.staticFiles); + staticFileScanner = new (require('jsdoc/src/scanner')).Scanner(); + + staticFilePaths.forEach(function(filePath) { + var extraStaticFiles = staticFileScanner.scan([filePath], 10, staticFileFilter); + + extraStaticFiles.forEach(function(fileName) { + var sourcePath = fs.toDir(filePath); + var toDir = fs.toDir( fileName.replace(sourcePath, outdir) ); + fs.mkPath(toDir); + fs.copyFileSync(fileName, toDir); + }); + }); + }*/ + + if (sourceFilePaths.length) { + sourceFiles = shortenPaths( sourceFiles, path.commonPrefix(sourceFilePaths) ); + } + data().each(function(doclet) { + var url = helper.createLink(doclet); + helper.registerLink(doclet.longname, url); + + // add a shortened version of the full path + var docletPath; + if (doclet.meta) { + docletPath = getPathFromDoclet(doclet); + docletPath = sourceFiles[docletPath].shortened; + if (docletPath) { + doclet.meta.shortpath = docletPath; + } + } + }); + + data().each(function(doclet) { + var url = helper.longnameToUrl[doclet.longname]; + + if (url.indexOf('#') > -1) { + doclet.id = helper.longnameToUrl[doclet.longname].split(/#/).pop(); + } + else { + doclet.id = doclet.name; + } + + if ( needsSignature(doclet) ) { + addSignatureParams(doclet); + addSignatureReturns(doclet); + addAttribs(doclet); + } + }); + + // do this after the urls have all been generated + data().each(function(doclet) { + doclet.ancestors = getAncestorLinks(doclet); + + if (doclet.kind === 'member') { + addSignatureTypes(doclet); + addAttribs(doclet); + } + + if (doclet.kind === 'constant') { + addSignatureTypes(doclet); + addAttribs(doclet); + doclet.kind = 'member'; + } + }); + + var members = helper.getMembers(data); + members.tutorials = tutorials.children; + + // output pretty-printed source files by default + var outputSourceFiles = conf.default && conf.default.outputSourceFiles !== false + ? true + : false; + + // add template helpers + view.find = find; + view.linkto = linkTo; + view.resolveAuthorLinks = resolveAuthorLinks; + view.tutoriallink = tutoriallink; + view.htmlsafe = htmlsafe; + view.outputSourceFiles = outputSourceFiles; + + // once for all + // Modified: Not needed anymore + //view.nav = buildNav(members); + attachModuleSymbols( find({ longname: {left: 'module:'} }), members.modules ); + + // generate the pretty-printed source files first so other pages can link to them + if (outputSourceFiles) { + generateSourceFiles(sourceFiles, opts.encoding); + } + + if (members.globals.length) { + generate('', 'Global', [{kind: 'globalobj'}], globalUrl); + } + + // index page displays information from package.json and lists files + var files = find({kind: 'file'}); + var packages = find({kind: 'package'}); + + generate('', 'Home', + packages.concat( + [{kind: 'mainpage', readme: opts.readme, longname: (opts.mainpagetitle) ? opts.mainpagetitle : 'Main Page'}] + ).concat(files), + indexUrl); + + // set up the lists that we'll use to generate pages + var classes = taffy(members.classes); + var modules = taffy(members.modules); + var namespaces = taffy(members.namespaces); + var mixins = taffy(members.mixins); + var externals = taffy(members.externals); + var interfaces = taffy(members.interfaces); + + Object.keys(helper.longnameToUrl).forEach(function(longname) { + var myModules = helper.find(modules, {longname: longname}); + if (myModules.length) { + generate('Module', myModules[0].name, myModules, helper.longnameToUrl[longname]); + } + + var myClasses = helper.find(classes, {longname: longname}); + if (myClasses.length) { + generate('Class', myClasses[0].name, myClasses, helper.longnameToUrl[longname]); + } + + var myNamespaces = helper.find(namespaces, {longname: longname}); + if (myNamespaces.length) { + generate('Namespace', myNamespaces[0].name, myNamespaces, helper.longnameToUrl[longname]); + } + + var myMixins = helper.find(mixins, {longname: longname}); + if (myMixins.length) { + generate('Mixin', myMixins[0].name, myMixins, helper.longnameToUrl[longname]); + } + + var myExternals = helper.find(externals, {longname: longname}); + if (myExternals.length) { + generate('External', myExternals[0].name, myExternals, helper.longnameToUrl[longname]); + } + + var myInterfaces = helper.find(interfaces, {longname: longname}); + if (myInterfaces.length) { + generate('Interface', myInterfaces[0].name, myInterfaces, helper.longnameToUrl[longname]); + } + }); + + // TODO: move the tutorial functions to templateHelper.js + function generateTutorial(title, tutorial, filename) { + var tutorialData = { + title: title, + header: tutorial.title, + content: tutorial.parse(), + children: tutorial.children + }; + + var tutorialPath = path.join(outdir, filename); + var html = view.render('tutorial.tmpl', tutorialData); + + // yes, you can use {@link} in tutorials too! + html = helper.resolveLinks(html); // turn {@link foo} into foo + fs.writeFileSync(tutorialPath, html, 'utf8'); + } + + // tutorials can have only one parent so there is no risk for loops + function saveChildren(node) { + node.children.forEach(function(child) { + generateTutorial(child.title, child, helper.tutorialToUrl(child.name)); + saveChildren(child); + }); + } + + saveChildren(tutorials); }; function linkTo(longname, linkText, cssClass, fragmentId) { - const classString = cssClass ? util.format(' class="%s"', cssClass) : ''; - let fileUrl; - let fragmentString = fragmentId ? `#${fragmentId}` : ''; - let stripped; - let text; - - // handle cases like: - // @see - // @see http://example.org - stripped = longname ? longname.replace(/^<|>$/g, '') : ''; - const hasUrlPrefix = /^(http|ftp)s?:\/\//.test(stripped); - - if (hasUrlPrefix) { - fileUrl = stripped; - text = linkText || stripped; - // Add target and rel attributes for external links - return util.format('%s', - encodeURI(fileUrl + fragmentString), classString, text); - } - // handle complex type expressions that may require multiple links - // (but skip anything that looks like an inline tag or HTML tag) - else if (longname && isComplexTypeExpression(longname) && - !/\{@.+\}/.test(longname) && !/^<[\s\S]+>/.test(longname)) { - // Parse complex types and create links for nested types - return linkComplexType(longname, linkText, cssClass); - } - else { - fileUrl = helper.longnameToUrl[longname] || ''; - text = linkText || longname; - - // If the URL contains a fragment (hash), extract it - if (fileUrl && fileUrl.indexOf('#') > -1) { - const parts = fileUrl.split('#'); - fileUrl = parts[0]; - // Only use the URL's fragment if no explicit fragmentId was provided - if (!fragmentId) { - fragmentString = '#' + parts[1]; - } - } - - // Convert source file links to GitHub URLs if configured - if (fileUrl && githubSourceBaseUrl && (fileUrl.endsWith('.js.md') || longname.endsWith('.js'))) { - fileUrl = convertSourceLinkToGitHub(fileUrl, longname); - // GitHub links should open in new tab - return util.format('%s', - encodeURI(fileUrl + fragmentString), classString, text); - } - // Remove .md extension from internal links for VitePress compatibility - // Handle both cases: with and without fragment identifiers - else if (fileUrl) { - fileUrl = fileUrl.replace(/\.md$/, ''); - } - } - - text = text || longname; - - if (!fileUrl) { - return text; - } - else { - return util.format('%s', encodeURI(fileUrl + fragmentString), - classString, text); - } + const classString = cssClass ? util.format(' class="%s"', cssClass) : ''; + let fileUrl; + let fragmentString = fragmentId ? `#${fragmentId}` : ''; + let stripped; + let text; + + // handle cases like: + // @see + // @see http://example.org + stripped = longname ? longname.replace(/^<|>$/g, '') : ''; + const hasUrlPrefix = /^(http|ftp)s?:\/\//.test(stripped); + + if (hasUrlPrefix) { + fileUrl = stripped; + text = linkText || stripped; + // Add target and rel attributes for external links + return util.format('%s', + encodeURI(fileUrl + fragmentString), classString, text); + } + // handle complex type expressions that may require multiple links + // (but skip anything that looks like an inline tag or HTML tag) + else if (longname && isComplexTypeExpression(longname) && + !/\{@.+\}/.test(longname) && !/^<[\s\S]+>/.test(longname)) { + // Parse complex types and create links for nested types + return linkComplexType(longname, linkText, cssClass); + } + else { + fileUrl = helper.longnameToUrl[longname] || ''; + text = linkText || longname; + + // If the URL contains a fragment (hash), extract it + if (fileUrl && fileUrl.indexOf('#') > -1) { + const parts = fileUrl.split('#'); + fileUrl = parts[0]; + // Only use the URL's fragment if no explicit fragmentId was provided + if (!fragmentId) { + fragmentString = '#' + parts[1]; + } + } + + // Convert source file links to GitHub URLs if configured + if (fileUrl && githubSourceBaseUrl && (fileUrl.endsWith('.js.md') || longname.endsWith('.js'))) { + fileUrl = convertSourceLinkToGitHub(fileUrl, longname); + // GitHub links should open in new tab + return util.format('%s', + encodeURI(fileUrl + fragmentString), classString, text); + } + // Remove .md extension from internal links for VitePress compatibility + // Handle both cases: with and without fragment identifiers + else if (fileUrl) { + fileUrl = fileUrl.replace(/\.md$/, ''); + } + } + + text = text || longname; + + if (!fileUrl) { + return text; + } + else { + return util.format('%s', encodeURI(fileUrl + fragmentString), + classString, text); + } } function convertSourceLinkToGitHub(fileUrl, longname) { - if (!githubSourceBaseUrl) { - return fileUrl; - } - - // Look up the original source path from the reverse mapping - let sourcePath = helper.longnameToUrl.urlToLongname || {}; - - // Try to find the original path from the URL - for (let originalPath in helper.longnameToUrl) { - if (helper.longnameToUrl[originalPath] === fileUrl) { - sourcePath = originalPath; - break; - } - } - - // If we found a valid source path, convert it to GitHub URL - if (typeof sourcePath === 'string' && sourcePath.endsWith('.js')) { - // Clean up the path - remove any leading slashes or backslashes - sourcePath = sourcePath.replace(/^[\/\\]+/, ''); - - // Return the GitHub URL - return `${githubSourceBaseUrl}/${sourcePath}`; - } - - // Fallback: if no mapping found, return original fileUrl - return fileUrl; + if (!githubSourceBaseUrl) { + return fileUrl; + } + + // Look up the original source path from the reverse mapping + let sourcePath = helper.longnameToUrl.urlToLongname || {}; + + // Try to find the original path from the URL + for (let originalPath in helper.longnameToUrl) { + if (helper.longnameToUrl[originalPath] === fileUrl) { + sourcePath = originalPath; + break; + } + } + + // If we found a valid source path, convert it to GitHub URL + if (typeof sourcePath === 'string' && sourcePath.endsWith('.js')) { + // Clean up the path - remove any leading slashes or backslashes + sourcePath = sourcePath.replace(/^[\/\\]+/, ''); + + // Return the GitHub URL + return `${githubSourceBaseUrl}/${sourcePath}`; + } + + // Fallback: if no mapping found, return original fileUrl + return fileUrl; } function isComplexTypeExpression(expr) { - // record types, type unions, and type applications all count as "complex" - return /^{.+}$/.test(expr) || /^.+\|.+$/.test(expr) || /^.+<.+>$/.test(expr); + // record types, type unions, and type applications all count as "complex" + return /^{.+}$/.test(expr) || /^.+\|.+$/.test(expr) || /^.+<.+>$/.test(expr); } function linkComplexType(longname, linkText, cssClass) { - const classString = cssClass ? util.format(' class="%s"', cssClass) : ''; - - // Handle type unions (e.g., "string | number") - if (/^.+\|.+$/.test(longname) && !/<.+>/.test(longname)) { - const types = longname.split('|').map(t => t.trim()); - return types.map(type => { - let url = helper.longnameToUrl[type]; - if (url) { - // Remove .md extension for VitePress compatibility - url = url.replace(/\.md$/, ''); - return util.format('%s', encodeURI(url), classString, htmlsafe(type)); - } - return htmlsafe(type); - }).join(' | '); - } - - // Handle generic types (e.g., "Array.", "Promise.<@ui5/fs/Resource>") - if (/<.+>/.test(longname)) { - return linkGenericType(longname, classString); - } - - // Handle record types (e.g., "{a: string, b: number}") - if (/^{.+}$/.test(longname)) { - return htmlsafe(longname); - } - - // Fallback - return linkText || htmlsafe(longname); + const classString = cssClass ? util.format(' class="%s"', cssClass) : ''; + + // Handle type unions (e.g., "string | number") + if (/^.+\|.+$/.test(longname) && !/<.+>/.test(longname)) { + const types = longname.split('|').map(t => t.trim()); + return types.map(type => { + let url = helper.longnameToUrl[type]; + if (url) { + // Remove .md extension for VitePress compatibility + url = url.replace(/\.md$/, ''); + return util.format('%s', encodeURI(url), classString, htmlsafe(type)); + } + return htmlsafe(type); + }).join(' | '); + } + + // Handle generic types (e.g., "Array.", "Promise.<@ui5/fs/Resource>") + if (/<.+>/.test(longname)) { + return linkGenericType(longname, classString); + } + + // Handle record types (e.g., "{a: string, b: number}") + if (/^{.+}$/.test(longname)) { + return htmlsafe(longname); + } + + // Fallback + return linkText || htmlsafe(longname); } function linkGenericType(type, classString) { - // Match patterns like "Promise." or "Array." or "Promise.>" - const match = type.match(/^([^<]+)<(.+)>$/); - - if (!match) { - return htmlsafe(type); - } - - const baseType = match[1]; - const innerType = match[2]; - - // Link the base type if it has a URL - let result = ''; - let baseUrl = helper.longnameToUrl[baseType]; - if (baseUrl) { - // Remove .md extension for VitePress compatibility - baseUrl = baseUrl.replace(/\.md$/, ''); - result = util.format('%s', encodeURI(baseUrl), classString, htmlsafe(baseType)); - } else { - result = htmlsafe(baseType); - } - - result += '.<'; - - // Recursively handle the inner type - if (isComplexTypeExpression(innerType)) { - result += linkComplexType(innerType, null, classString.replace(' class="', '').replace('"', '')); - } else { - let innerUrl = helper.longnameToUrl[innerType]; - if (innerUrl) { - // Remove .md extension for VitePress compatibility - innerUrl = innerUrl.replace(/\.md$/, ''); - result += util.format('%s', encodeURI(innerUrl), classString, htmlsafe(innerType)); - } else { - result += htmlsafe(innerType); - } - } - - result += '>'; - - return result; + // Match patterns like "Promise." or "Array." or "Promise.>" + const match = type.match(/^([^<]+)<(.+)>$/); + + if (!match) { + return htmlsafe(type); + } + + const baseType = match[1]; + const innerType = match[2]; + + // Link the base type if it has a URL + let result = ''; + let baseUrl = helper.longnameToUrl[baseType]; + if (baseUrl) { + // Remove .md extension for VitePress compatibility + baseUrl = baseUrl.replace(/\.md$/, ''); + result = util.format('%s', encodeURI(baseUrl), classString, htmlsafe(baseType)); + } else { + result = htmlsafe(baseType); + } + + result += '.<'; + + // Recursively handle the inner type + if (isComplexTypeExpression(innerType)) { + result += linkComplexType(innerType, null, classString.replace(' class="', '').replace('"', '')); + } else { + let innerUrl = helper.longnameToUrl[innerType]; + if (innerUrl) { + // Remove .md extension for VitePress compatibility + innerUrl = innerUrl.replace(/\.md$/, ''); + result += util.format('%s', encodeURI(innerUrl), classString, htmlsafe(innerType)); + } else { + result += htmlsafe(innerType); + } + } + + result += '>'; + + return result; } diff --git a/internal/documentation/package.json b/internal/documentation/package.json index 6362a295459..94f350fab3c 100644 --- a/internal/documentation/package.json +++ b/internal/documentation/package.json @@ -30,8 +30,8 @@ "build:assets": "sh -c 'DEST_DIR=${1:-./dist}; cp -r ./docs/images \"$DEST_DIR/images\"' --", "preview": "vitepress preview --port 8080", "jsdoc": "npm run jsdoc-generate && open-cli dist/api/index.html", - "jsdoc-generate": "jsdoc -c jsdoc/jsdoc-workspace.json -t jsdoc/docdash ./ || (echo 'Error during JSDoc generation! Check log.' && exit 1) && rm -r docs/api/*.js.md", - "jsdoc-generate-gh-pages": "jsdoc -c jsdoc/jsdoc.json -t jsdoc/docdash ./ || (echo 'Error during JSDoc generation! Check log.' && exit 1) && rm -r docs/api/*.js.md", + "jsdoc-generate": "jsdoc -c jsdoc/jsdoc-workspace.json -t jsdoc/docdash ./ || (echo 'Error during JSDoc generation! Check log.' && exit 1)", + "jsdoc-generate-gh-pages": "jsdoc -c jsdoc/jsdoc.json -t jsdoc/docdash ./ || (echo 'Error during JSDoc generation! Check log.' && exit 1)", "generate-cli-doc": "node ./scripts/generateCliDoc.js", "schema-generate": "node ./scripts/buildSchema.js", "schema-generate-gh-pages": "node ./scripts/buildSchema.js gh-pages", From f0377ef3b73c8daeba68473ceb5b0d0961176236 Mon Sep 17 00:00:00 2001 From: Gianluca Beil Date: Wed, 21 Jan 2026 12:56:05 +0100 Subject: [PATCH 05/15] docs: Fix .md ending in internal links --- internal/documentation/jsdoc/docdash/publish.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/internal/documentation/jsdoc/docdash/publish.js b/internal/documentation/jsdoc/docdash/publish.js index 74635ea6eaf..d7f0a3be62a 100644 --- a/internal/documentation/jsdoc/docdash/publish.js +++ b/internal/documentation/jsdoc/docdash/publish.js @@ -221,7 +221,7 @@ function generate(type, title, docs, filename, resolveLinks) { docs: docs }; - var outpath = path.join(outdir, filename), + var outpath = path.join(outdir, filename + ".md"), html = view.render('container.tmpl', docData); if (resolveLinks) { @@ -402,7 +402,7 @@ function buildNav(members) { */ exports.publish = function(taffyData, opts, tutorials) { // Modified from .html to .md to output Markdown files - helper.fileExtension = ".md"; + helper.fileExtension = ""; var docdash = env && env.conf && env.conf.docdash || {}; data = taffyData; @@ -594,6 +594,7 @@ exports.publish = function(taffyData, opts, tutorials) { //view.nav = buildNav(members); attachModuleSymbols( find({ longname: {left: 'module:'} }), members.modules ); + // generate the pretty-printed source files first so other pages can link to them if (outputSourceFiles) { generateSourceFiles(sourceFiles, opts.encoding); @@ -681,6 +682,8 @@ exports.publish = function(taffyData, opts, tutorials) { saveChildren(tutorials); }; +// Taken from templateHelper.js in jsdoc/util + function linkTo(longname, linkText, cssClass, fragmentId) { const classString = cssClass ? util.format(' class="%s"', cssClass) : ''; let fileUrl; From 38a9278540550b714ef76a67b3862af4fabb325c Mon Sep 17 00:00:00 2001 From: Gianluca Beil Date: Wed, 21 Jan 2026 13:54:49 +0100 Subject: [PATCH 06/15] docs: Fix pipe encoding in Markdown files --- internal/documentation/jsdoc/docdash/publish.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/internal/documentation/jsdoc/docdash/publish.js b/internal/documentation/jsdoc/docdash/publish.js index d7f0a3be62a..916ca8704c9 100644 --- a/internal/documentation/jsdoc/docdash/publish.js +++ b/internal/documentation/jsdoc/docdash/publish.js @@ -228,7 +228,8 @@ function generate(type, title, docs, filename, resolveLinks) { html = helper.resolveLinks(html); // turn {@link foo} into foo } - fs.writeFileSync(outpath, html, 'utf8'); + // Modified: replaceAll fixes pipe escaping + fs.writeFileSync(outpath, html.replaceAll("\\|", "|"), 'utf8'); } // Modified: Don't write source files From c4c44d8af023678865ae63093af2e4cea3bdba11 Mon Sep 17 00:00:00 2001 From: Matthias Osswald Date: Wed, 21 Jan 2026 14:11:46 +0100 Subject: [PATCH 07/15] refactor: Remove unused code from jsdoc publish.js --- .../documentation/jsdoc/docdash/publish.js | 147 ------------------ 1 file changed, 147 deletions(-) diff --git a/internal/documentation/jsdoc/docdash/publish.js b/internal/documentation/jsdoc/docdash/publish.js index 916ca8704c9..a1bdea8f61a 100644 --- a/internal/documentation/jsdoc/docdash/publish.js +++ b/internal/documentation/jsdoc/docdash/publish.js @@ -236,20 +236,9 @@ function generate(type, title, docs, filename, resolveLinks) { function generateSourceFiles(sourceFiles, encoding) { encoding = encoding || 'utf8'; Object.keys(sourceFiles).forEach(function(file) { - var source; // links are keyed to the shortened path in each doclet's `meta.shortpath` property var sourceOutfile = helper.getUniqueFilename(sourceFiles[file].shortened); helper.registerLink(sourceFiles[file].shortened, sourceOutfile); - - try { - source = { - kind: 'source', - code: helper.htmlsafe( fs.readFileSync(sourceFiles[file].resolved, encoding) ) - }; - } - catch(e) { - logger.error('Error while generating source file %s: %s', file, e.message); - } }); } @@ -294,108 +283,6 @@ function attachModuleSymbols(doclets, modules) { }); } -// Removed navbar functionality -/* -function buildMemberNav(items, itemHeading, itemsSeen, linktoFn) { - var nav = ''; - - if (items && items.length) { - var itemsNav = ''; - - items.forEach(function(item) { - var methods = find({kind:'function', memberof: item.longname}); - var members = find({kind:'member', memberof: item.longname}); - var docdash = env && env.conf && env.conf.docdash || {}; - - if ( !hasOwnProp.call(item, 'longname') ) { - itemsNav += '
    • ' + linktoFn('', item.name); - itemsNav += '
    • '; - } else if ( !hasOwnProp.call(itemsSeen, item.longname) ) { - itemsNav += '
    • ' + linktoFn(item.longname, item.name.replace(/^module:/, '')); - - if (docdash.static && members.find(function (m) { return m.scope === 'static'; } )) { - itemsNav += "
        "; - - members.forEach(function (member) { - if (!member.scope === 'static') return; - itemsNav += "
      • "; - itemsNav += linkto(member.longname, member.name); - itemsNav += "
      • "; - }); - - itemsNav += "
      "; - } - - if (methods.length) { - itemsNav += "
        "; - - methods.forEach(function (method) { - itemsNav += "
      • "; - itemsNav += linkto(method.longname, method.name); - itemsNav += "
      • "; - }); - - itemsNav += "
      "; - } - - itemsNav += '
    • '; - itemsSeen[item.longname] = true; - } - }); - - if (itemsNav !== '') { - nav += '

      ' + itemHeading + '

        ' + itemsNav + '
      '; - } - } - - return nav; -} - -function linktoTutorial(longName, name) { - return tutoriallink(name); -} - -function linktoExternal(longName, name) { - return linkto(longName, name.replace(/(^"|"$)/g, '')); -} - -function buildNav(members) { - var nav = '

      Home

      '; - var seen = {}; - var seenTutorials = {}; - - nav += buildMemberNav(members.tutorials, 'Tutorials', seenTutorials, linktoTutorial); - nav += buildMemberNav(members.classes, 'Classes', seen, linkto); - nav += buildMemberNav(members.modules, 'Modules', {}, linkto); - nav += buildMemberNav(members.externals, 'Externals', seen, linktoExternal); - nav += buildMemberNav(members.events, 'Events', seen, linkto); - nav += buildMemberNav(members.namespaces, 'Namespaces', seen, linkto); - nav += buildMemberNav(members.mixins, 'Mixins', seen, linkto); - nav += buildMemberNav(members.interfaces, 'Interfaces', seen, linkto); - - if (members.globals.length) { - var globalNav = ''; - - members.globals.forEach(function(g) { - if ( g.kind !== 'typedef' && !hasOwnProp.call(seen, g.longname) ) { - globalNav += '
    • ' + linkto(g.longname, g.name) + '
    • '; - } - seen[g.longname] = true; - }); - - if (!globalNav) { - // turn the heading into a link so you can actually get to the global page - nav += '

      ' + linkto('global', 'Global') + '

      '; - } - else { - nav += '

      Global

        ' + globalNav + '
      '; - } - } - - return nav; -} -*/ - /** @param {TAFFY} taffyData See . @param {object} opts @@ -488,40 +375,6 @@ exports.publish = function(taffyData, opts, tutorials) { fs.mkPath(outdir); // Modified: Static files are not needed anymore - // copy the template's static files to outdir - /*var fromDir = path.join(templatePath, 'static'); - var staticFiles = fs.ls(fromDir, 3); - - staticFiles.forEach(function(fileName) { - var toDir = fs.toDir( fileName.replace(fromDir, outdir) ); - fs.mkPath(toDir); - fs.copyFileSync(fileName, toDir); - }); - - // copy user-specified static files to outdir - var staticFilePaths; - var staticFileFilter; - var staticFileScanner; - if (conf.default.staticFiles) { - // The canonical property name is `include`. We accept `paths` for backwards compatibility - // with a bug in JSDoc 3.2.x. - staticFilePaths = conf.default.staticFiles.include || - conf.default.staticFiles.paths || - []; - staticFileFilter = new (require('jsdoc/src/filter')).Filter(conf.default.staticFiles); - staticFileScanner = new (require('jsdoc/src/scanner')).Scanner(); - - staticFilePaths.forEach(function(filePath) { - var extraStaticFiles = staticFileScanner.scan([filePath], 10, staticFileFilter); - - extraStaticFiles.forEach(function(fileName) { - var sourcePath = fs.toDir(filePath); - var toDir = fs.toDir( fileName.replace(sourcePath, outdir) ); - fs.mkPath(toDir); - fs.copyFileSync(fileName, toDir); - }); - }); - }*/ if (sourceFilePaths.length) { sourceFiles = shortenPaths( sourceFiles, path.commonPrefix(sourceFilePaths) ); From 50cd1aacaab50a238e8bf0893ef412ef95b95b4b Mon Sep 17 00:00:00 2001 From: Matthias Osswald Date: Wed, 21 Jan 2026 14:24:22 +0100 Subject: [PATCH 08/15] refactor: Integrate docdash into documentation project --- internal/documentation/jsdoc/docdash/publish.js | 1 - 1 file changed, 1 deletion(-) diff --git a/internal/documentation/jsdoc/docdash/publish.js b/internal/documentation/jsdoc/docdash/publish.js index a1bdea8f61a..ef84676e2a2 100644 --- a/internal/documentation/jsdoc/docdash/publish.js +++ b/internal/documentation/jsdoc/docdash/publish.js @@ -4,7 +4,6 @@ var doop = require('jsdoc/util/doop'); var fs = require('jsdoc/fs'); var helper = require('jsdoc/util/templateHelper'); -var logger = require('jsdoc/util/logger'); var path = require('jsdoc/path'); var taffy = require('@jsdoc/salty').taffy; var template = require('jsdoc/template'); From dcef222576cce640267057fea1c27ec9900e6116 Mon Sep 17 00:00:00 2001 From: Gianluca Beil Date: Wed, 21 Jan 2026 14:53:24 +0100 Subject: [PATCH 09/15] docs: Cleanup old comments and unused code --- internal/documentation/jsdoc/docdash/publish.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/internal/documentation/jsdoc/docdash/publish.js b/internal/documentation/jsdoc/docdash/publish.js index ef84676e2a2..913422e911e 100644 --- a/internal/documentation/jsdoc/docdash/publish.js +++ b/internal/documentation/jsdoc/docdash/publish.js @@ -11,10 +11,6 @@ var util = require('node:util'); var htmlsafe = helper.htmlsafe; var resolveAuthorLinks = helper.resolveAuthorLinks; -// Not needed anymore -//var linkto = helper.linkto; -//var scopeToPunc = helper.scopeToPunc; -//var hasOwnProp = Object.prototype.hasOwnProperty; var data; var view; @@ -288,7 +284,7 @@ function attachModuleSymbols(doclets, modules) { @param {Tutorial} tutorials */ exports.publish = function(taffyData, opts, tutorials) { - // Modified from .html to .md to output Markdown files + // Modified to fix .md file extensions in internal links helper.fileExtension = ""; var docdash = env && env.conf && env.conf.docdash || {}; From c29381a56df6cb25189c8621af41667f912824ed Mon Sep 17 00:00:00 2001 From: Gianluca Beil Date: Wed, 21 Jan 2026 16:27:48 +0100 Subject: [PATCH 10/15] fix: Change two dots in Promise to one --- internal/documentation/jsdoc/docdash/publish.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/internal/documentation/jsdoc/docdash/publish.js b/internal/documentation/jsdoc/docdash/publish.js index 913422e911e..91fe1d4b95c 100644 --- a/internal/documentation/jsdoc/docdash/publish.js +++ b/internal/documentation/jsdoc/docdash/publish.js @@ -686,7 +686,9 @@ function linkGenericType(type, classString) { result = htmlsafe(baseType); } - result += '.<'; + // Only add a separating dot if the baseType doesn't already end with one + const needsDot = !String(baseType).trim().endsWith('.'); + result += (needsDot ? '.<' : '<'); // Recursively handle the inner type if (isComplexTypeExpression(innerType)) { From de3ccd4bd872ba224d7fd84f01c6e71b4927ed81 Mon Sep 17 00:00:00 2001 From: Gianluca Beil Date: Thu, 22 Jan 2026 10:10:43 +0100 Subject: [PATCH 11/15] docs: Stop index.md and global.md from generating --- .../documentation/jsdoc/docdash/publish.js | 22 ------------------- 1 file changed, 22 deletions(-) diff --git a/internal/documentation/jsdoc/docdash/publish.js b/internal/documentation/jsdoc/docdash/publish.js index 91fe1d4b95c..ef1d4d52ec9 100644 --- a/internal/documentation/jsdoc/docdash/publish.js +++ b/internal/documentation/jsdoc/docdash/publish.js @@ -300,14 +300,6 @@ exports.publish = function(taffyData, opts, tutorials) { // Store GitHub base URL for source file links githubSourceBaseUrl = docdash.githubSourceBaseUrl || null; - // claim some special filenames in advance, so the All-Powerful Overseer of Filename Uniqueness - // doesn't try to hand them out later - var indexUrl = helper.getUniqueFilename('index'); - // don't call registerLink() on this one! 'index' is also a valid longname - - var globalUrl = helper.getUniqueFilename('global'); - helper.registerLink('global', globalUrl); - // set up templating view.layout = conf.default.layoutFile ? path.getResourcePath(path.dirname(conf.default.layoutFile), @@ -449,20 +441,6 @@ exports.publish = function(taffyData, opts, tutorials) { generateSourceFiles(sourceFiles, opts.encoding); } - if (members.globals.length) { - generate('', 'Global', [{kind: 'globalobj'}], globalUrl); - } - - // index page displays information from package.json and lists files - var files = find({kind: 'file'}); - var packages = find({kind: 'package'}); - - generate('', 'Home', - packages.concat( - [{kind: 'mainpage', readme: opts.readme, longname: (opts.mainpagetitle) ? opts.mainpagetitle : 'Main Page'}] - ).concat(files), - indexUrl); - // set up the lists that we'll use to generate pages var classes = taffy(members.classes); var modules = taffy(members.modules); From 4a5a868e21b0afd056bf50cd514c1d01f6f2ca1a Mon Sep 17 00:00:00 2001 From: Gianluca Beil Date: Thu, 22 Jan 2026 15:14:44 +0100 Subject: [PATCH 12/15] docs: Replace caption with i --- packages/builder/lib/processors/bundlers/moduleBundler.js | 2 +- packages/builder/lib/tasks/generateResourcesJson.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/builder/lib/processors/bundlers/moduleBundler.js b/packages/builder/lib/processors/bundlers/moduleBundler.js index 843904d6cbd..e4f026e63cc 100644 --- a/packages/builder/lib/processors/bundlers/moduleBundler.js +++ b/packages/builder/lib/processors/bundlers/moduleBundler.js @@ -58,7 +58,7 @@ const log = getLogger("builder:processors:bundlers:moduleBundler"); * Excludes should be marked with a leading exclamation mark '!'. The order of filters is relevant; a later * exclusion overrides an earlier inclusion, and vice versa. * - * List of modules as glob patterns that should be in- or excluded + * Example: * * ```js * // Includes everything from "some/path/to/module/", diff --git a/packages/builder/lib/tasks/generateResourcesJson.js b/packages/builder/lib/tasks/generateResourcesJson.js index acd0f747530..aa612455252 100644 --- a/packages/builder/lib/tasks/generateResourcesJson.js +++ b/packages/builder/lib/tasks/generateResourcesJson.js @@ -55,7 +55,7 @@ function getCreatorOptions(projectName) { * Therefore it is also not supported in combination with self-contained build. *

      * - * sample resources.json + * sample resources.json * * ```js * const resourcesJson = { From 4455a95b98815a9f542ceaa468c3e69ab3b5be91 Mon Sep 17 00:00:00 2001 From: Gianluca Beil Date: Thu, 22 Jan 2026 15:25:48 +0100 Subject: [PATCH 13/15] docs: Fix examples being rendered as text --- internal/documentation/jsdoc/docdash/tmpl/params.tmpl | 2 +- internal/documentation/jsdoc/docdash/tmpl/properties.tmpl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/documentation/jsdoc/docdash/tmpl/params.tmpl b/internal/documentation/jsdoc/docdash/tmpl/params.tmpl index d9b5561b580..97f0e3415ec 100644 --- a/internal/documentation/jsdoc/docdash/tmpl/params.tmpl +++ b/internal/documentation/jsdoc/docdash/tmpl/params.tmpl @@ -63,5 +63,5 @@ var self = this; params.forEach(function(param) { if (!param) { return; } -?>| `` | | optional, nullable, repeatable | `` | ') ?>
      *Properties:* +?>| `` | | optional, nullable, repeatable | `` | ').replaceAll("```js", "
      ").replaceAll("```", "
      ") ?>
      *Properties:* diff --git a/internal/documentation/jsdoc/docdash/tmpl/properties.tmpl b/internal/documentation/jsdoc/docdash/tmpl/properties.tmpl index 00e177f1934..3830cbdad29 100644 --- a/internal/documentation/jsdoc/docdash/tmpl/properties.tmpl +++ b/internal/documentation/jsdoc/docdash/tmpl/properties.tmpl @@ -36,4 +36,4 @@ props.hasDefault = true; } }); -?>
      NameTypeAttributesDefaultDescription
      optionalnullable") ?>
      Properties
      +?>
      NameTypeAttributesDefaultDescription
      optionalnullable").replaceAll("```js", "
      ").replaceAll("```", "
      ") ?>
      Properties
      From 2367b0d0919cd94ad4fdd0b1f8cc7e1c94d07e81 Mon Sep 17 00:00:00 2001 From: Gianluca Beil Date: Thu, 22 Jan 2026 15:27:00 +0100 Subject: [PATCH 14/15] docs: Fix header anchor positioning --- internal/documentation/.vitepress/theme/custom.css | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/internal/documentation/.vitepress/theme/custom.css b/internal/documentation/.vitepress/theme/custom.css index 882508cee9c..702336af3ac 100644 --- a/internal/documentation/.vitepress/theme/custom.css +++ b/internal/documentation/.vitepress/theme/custom.css @@ -316,3 +316,11 @@ li .learn-more { .VPDoc.has-aside .content-container { max-width: 100% !important; } + +/** +* This makes the header anchor appear left next to the method names +* again + */ +.jsdoc-object + .header-anchor { + padding: 15px; +} From d1fc5b150e795fea285de47280ac331a58683457 Mon Sep 17 00:00:00 2001 From: Gianluca Beil Date: Thu, 22 Jan 2026 15:27:24 +0100 Subject: [PATCH 15/15] docs: Remove dots in Promise's completely --- internal/documentation/jsdoc/docdash/publish.js | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/internal/documentation/jsdoc/docdash/publish.js b/internal/documentation/jsdoc/docdash/publish.js index ef1d4d52ec9..22e75d348c8 100644 --- a/internal/documentation/jsdoc/docdash/publish.js +++ b/internal/documentation/jsdoc/docdash/publish.js @@ -653,20 +653,22 @@ function linkGenericType(type, classString) { const baseType = match[1]; const innerType = match[2]; - // Link the base type if it has a URL + // Strip any trailing dots from baseType for display (handles "Promise." cases) + const displayBase = String(baseType).replace(/\.+$/g, '').trim(); + + // Link the base type if it has a URL. Try both the original baseType and the stripped variant. let result = ''; - let baseUrl = helper.longnameToUrl[baseType]; + let baseUrl = helper.longnameToUrl[baseType] || helper.longnameToUrl[displayBase]; if (baseUrl) { // Remove .md extension for VitePress compatibility baseUrl = baseUrl.replace(/\.md$/, ''); - result = util.format('%s', encodeURI(baseUrl), classString, htmlsafe(baseType)); + result = util.format('%s', encodeURI(baseUrl), classString, htmlsafe(displayBase)); } else { - result = htmlsafe(baseType); + result = htmlsafe(displayBase); } - // Only add a separating dot if the baseType doesn't already end with one - const needsDot = !String(baseType).trim().endsWith('.'); - result += (needsDot ? '.<' : '<'); + // Always use angle brackets without an extra dot: Base + result += '<'; // Recursively handle the inner type if (isComplexTypeExpression(innerType)) {