From acb2d2eef19d97b93fcb38aa3bb37760864818bd Mon Sep 17 00:00:00 2001 From: Matthias Osswald Date: Wed, 21 Jan 2026 14:35:58 +0100 Subject: [PATCH 01/36] 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 d24ecb5ecce..eb688a68754 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -10,3 +10,8 @@ precedence = "aggregate" SPDX-FileCopyrightText = "2026 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 aab6c3f70d3bc30202b77f237945db30bebe0b7b Mon Sep 17 00:00:00 2001 From: Gianluca Beil Date: Thu, 27 Nov 2025 14:44:36 +0200 Subject: [PATCH 02/36] 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 - internal/documentation/package.json | 6 +- package-lock.json | 96 +++- 31 files changed, 1049 insertions(+), 1344 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 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/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 6f8b070454c..85fb24ea618 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", @@ -7139,6 +7139,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", @@ -8927,16 +8951,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", @@ -9161,6 +9175,18 @@ "url": "https://github.com/fb55/encoding-sniffer?sponsor=1" } }, + "node_modules/encoding-sniffer/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", + "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", @@ -15916,26 +15942,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" @@ -15953,7 +15991,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==", @@ -15965,6 +16003,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 e850e46e76e6ed119eba19162bfbede348064015 Mon Sep 17 00:00:00 2001 From: Gianluca Beil Date: Wed, 21 Jan 2026 09:51:07 +0100 Subject: [PATCH 03/36] 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 2b9824611ae153f7328e29ca0ffd1a6011b5c9e0 Mon Sep 17 00:00:00 2001 From: Gianluca Beil Date: Wed, 21 Jan 2026 09:52:01 +0100 Subject: [PATCH 04/36] 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 da32fa5c917cef3b3132a58eef0dd6e39d419f7e Mon Sep 17 00:00:00 2001 From: Gianluca Beil Date: Wed, 21 Jan 2026 12:56:05 +0100 Subject: [PATCH 05/36] 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 c0fb20463ada35491bff1f5c053e810abb2c8068 Mon Sep 17 00:00:00 2001 From: Gianluca Beil Date: Wed, 21 Jan 2026 13:54:49 +0100 Subject: [PATCH 06/36] 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 21b992ebdb17673dfe8cd3f397ea946443142eaf Mon Sep 17 00:00:00 2001 From: Matthias Osswald Date: Wed, 21 Jan 2026 14:11:46 +0100 Subject: [PATCH 07/36] 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 e73b7654ab98807ae9cdd5c034349364710e1452 Mon Sep 17 00:00:00 2001 From: Matthias Osswald Date: Wed, 21 Jan 2026 14:24:22 +0100 Subject: [PATCH 08/36] 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 541112fc61c6dbfbf8e1aed019c88b4cb346bf38 Mon Sep 17 00:00:00 2001 From: Gianluca Beil Date: Wed, 21 Jan 2026 14:53:24 +0100 Subject: [PATCH 09/36] 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 020a2553428b106eaae84cacb7f773029a4e96ac Mon Sep 17 00:00:00 2001 From: Gianluca Beil Date: Wed, 21 Jan 2026 16:27:48 +0100 Subject: [PATCH 10/36] 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 cf4348425190db99b31cec73f24117c1ed474faa Mon Sep 17 00:00:00 2001 From: Gianluca Beil Date: Thu, 22 Jan 2026 10:10:43 +0100 Subject: [PATCH 11/36] 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 41ed216df9cc0241e2b87c07cb8aa89f0ed877ba Mon Sep 17 00:00:00 2001 From: Gianluca Beil Date: Thu, 22 Jan 2026 15:14:44 +0100 Subject: [PATCH 12/36] 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 42b3b02b51fb129207f5994a6fbbf750f657ccba Mon Sep 17 00:00:00 2001 From: Gianluca Beil Date: Thu, 22 Jan 2026 15:25:48 +0100 Subject: [PATCH 13/36] 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 f0a1d1a45579bad93b10c4b82b19692f27c344e0 Mon Sep 17 00:00:00 2001 From: Gianluca Beil Date: Thu, 22 Jan 2026 15:27:00 +0100 Subject: [PATCH 14/36] 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 b477565592900aab7f235a49c56d8e37f7ec6fc3 Mon Sep 17 00:00:00 2001 From: Gianluca Beil Date: Thu, 22 Jan 2026 15:27:24 +0100 Subject: [PATCH 15/36] 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)) { From aa6606e3c9eb8be73a2c0b397b33ec2fade52001 Mon Sep 17 00:00:00 2001 From: Gianluca Beil Date: Fri, 23 Jan 2026 09:29:08 +0100 Subject: [PATCH 16/36] docs: Initial support for tree --- internal/documentation/.vitepress/config.ts | 46 ++++++++++++++++++--- 1 file changed, 40 insertions(+), 6 deletions(-) diff --git a/internal/documentation/.vitepress/config.ts b/internal/documentation/.vitepress/config.ts index 8102dc40517..dde3e461a1d 100644 --- a/internal/documentation/.vitepress/config.ts +++ b/internal/documentation/.vitepress/config.ts @@ -17,6 +17,45 @@ for (const file of fs.readdirSync(path.join("docs", "api"))) { }); } +const tree = { + text: "API", + collapsed: false, + items: [] +} + +for (let file of fs.readdirSync(path.join("docs", "api"))) { + file = file.replace(".md", "").replace("module-", "") + const treePath = file.split("_"); + appendToTree(tree, file, treePath, 0); +} + +function appendToTree(tree, file, treePath, index) { + if (treePath.length === index) { + tree.items.push({ + text: treePath[index], + link: file + }); + return; + } + let found = false; + for (const treeItem of tree.items) { + if (treeItem.text === treePath[index]) { + appendToTree(treeItem, file, treePath, index+1); + found = true; + break; + } + } + if (!found) { + let newItem = { + text: treePath[index], + collapsed: false, + items: [] + } + appendToTree(newItem, file, treePath, index+1); + tree.items.push(newItem); + } +} + export default defineConfig({ // Would be set in CI job via CLI arguments. For local development, it's just root. @@ -286,12 +325,7 @@ function guide() { }, ], }, - { - text: "API", - collapsed: true, - items: apiPages - }, - + tree ]; } From b4f6aef1414abd1c7f5c93cd8360aef0b20bf14f Mon Sep 17 00:00:00 2001 From: Gianluca Beil Date: Fri, 23 Jan 2026 11:07:19 +0100 Subject: [PATCH 17/36] docs: Fully working tree in docs --- internal/documentation/.vitepress/config.ts | 21 ++++++++++++------- .../documentation/.vitepress/theme/custom.css | 3 --- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/internal/documentation/.vitepress/config.ts b/internal/documentation/.vitepress/config.ts index dde3e461a1d..b28525ec23f 100644 --- a/internal/documentation/.vitepress/config.ts +++ b/internal/documentation/.vitepress/config.ts @@ -20,20 +20,23 @@ for (const file of fs.readdirSync(path.join("docs", "api"))) { const tree = { text: "API", collapsed: false, - items: [] + items: [{ + text: "@ui5", + items: [] + }] } for (let file of fs.readdirSync(path.join("docs", "api"))) { - file = file.replace(".md", "").replace("module-", "") - const treePath = file.split("_"); + file = file.replace(".md", ""); + const treePath = file.replace("module-", "").split("_"); appendToTree(tree, file, treePath, 0); } function appendToTree(tree, file, treePath, index) { - if (treePath.length === index) { + if (treePath.length - 1 === index) { tree.items.push({ - text: treePath[index], - link: file + text: treePath[index].replace("module-", ""), + link: "/api/" + file }); return; } @@ -47,8 +50,8 @@ function appendToTree(tree, file, treePath, index) { } if (!found) { let newItem = { - text: treePath[index], - collapsed: false, + text: treePath[index].replace("module-", ""), + collapsed: treePath[index] !== "@ui5", items: [] } appendToTree(newItem, file, treePath, index+1); @@ -56,6 +59,8 @@ function appendToTree(tree, file, treePath, index) { } } +tree.items = tree.items[0].items; // Display items inside @ui5 as root + export default defineConfig({ // Would be set in CI job via CLI arguments. For local development, it's just root. diff --git a/internal/documentation/.vitepress/theme/custom.css b/internal/documentation/.vitepress/theme/custom.css index 702336af3ac..34838f25f2a 100644 --- a/internal/documentation/.vitepress/theme/custom.css +++ b/internal/documentation/.vitepress/theme/custom.css @@ -33,9 +33,6 @@ /*--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%; } From 68f2704bfb936d338ff089d15300f3ff27c0decb Mon Sep 17 00:00:00 2001 From: Gianluca Beil Date: Fri, 23 Jan 2026 13:41:38 +0100 Subject: [PATCH 18/36] docs: Cleanup after api docs implementation --- internal/documentation/.vitepress/config.ts | 106 +++++++++----------- 1 file changed, 50 insertions(+), 56 deletions(-) diff --git a/internal/documentation/.vitepress/config.ts b/internal/documentation/.vitepress/config.ts index b28525ec23f..ecbfec3de45 100644 --- a/internal/documentation/.vitepress/config.ts +++ b/internal/documentation/.vitepress/config.ts @@ -5,61 +5,9 @@ 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 - }); -} - -const tree = { - text: "API", - collapsed: false, - items: [{ - text: "@ui5", - items: [] - }] -} - -for (let file of fs.readdirSync(path.join("docs", "api"))) { - file = file.replace(".md", ""); - const treePath = file.replace("module-", "").split("_"); - appendToTree(tree, file, treePath, 0); -} - -function appendToTree(tree, file, treePath, index) { - if (treePath.length - 1 === index) { - tree.items.push({ - text: treePath[index].replace("module-", ""), - link: "/api/" + file - }); - return; - } - let found = false; - for (const treeItem of tree.items) { - if (treeItem.text === treePath[index]) { - appendToTree(treeItem, file, treePath, index+1); - found = true; - break; - } - } - if (!found) { - let newItem = { - text: treePath[index].replace("module-", ""), - collapsed: treePath[index] !== "@ui5", - items: [] - } - appendToTree(newItem, file, treePath, index+1); - tree.items.push(newItem); - } -} - -tree.items = tree.items[0].items; // Display items inside @ui5 as root +// api docs +import * as path from "node:path"; +import * as fs from "node:fs"; export default defineConfig({ @@ -330,7 +278,53 @@ function guide() { }, ], }, - tree + (() => { + // This function builds the tree for the api docs + const tree = { + text: "API", + collapsed: false, + items: [{ + text: "@ui5", + items: [] + }] + } + + for (let file of fs.readdirSync(path.join("docs", "api"))) { + file = file.replace(".md", ""); + const treePath = file.replace("module-", "").split("_"); + appendToTree(tree, file, treePath, 0); + } + + function appendToTree(tree, file, treePath, index) { + if (treePath.length - 1 === index) { + tree.items.push({ + text: treePath[index].replace("module-", ""), + link: "/api/" + file + }); + return; + } + let found = false; + for (const treeItem of tree.items) { + if (treeItem.text === treePath[index]) { + appendToTree(treeItem, file, treePath, index+1); + found = true; + break; + } + } + if (!found) { + let newItem = { + text: treePath[index].replace("module-", ""), + collapsed: treePath[index] !== "@ui5", + items: [] + } + appendToTree(newItem, file, treePath, index+1); + tree.items.push(newItem); + } + } + + tree.items = tree.items[0].items; // Display items inside @ui5 as root + return tree; + })() ]; } From 4fb295d57402968c552a0ca508774f2b18e5b362 Mon Sep 17 00:00:00 2001 From: Gianluca Beil Date: Fri, 23 Jan 2026 15:26:29 +0100 Subject: [PATCH 19/36] docs: Improved tree --- internal/documentation/.vitepress/config.ts | 32 +++++++++++++++++++ .../documentation/.vitepress/theme/custom.css | 7 ++++ 2 files changed, 39 insertions(+) diff --git a/internal/documentation/.vitepress/config.ts b/internal/documentation/.vitepress/config.ts index ecbfec3de45..ce17634a0e6 100644 --- a/internal/documentation/.vitepress/config.ts +++ b/internal/documentation/.vitepress/config.ts @@ -323,6 +323,38 @@ function guide() { } tree.items = tree.items[0].items; // Display items inside @ui5 as root + + const moveIndex = []; + + for (let index = 0; index < tree.items.length; index++) { + const treeItem = tree.items[index]; + if (!treeItem.link) treeItem.text = "@ui5/" + treeItem.text; + + if (treeItem.link) { + let to; + for (let index2 = 0; index2 < tree.items.length; index2++) { + const item = tree.items[index2]; + console.log(item, !item.link, item.text, treeItem.text.replace("@ui5/", "")) + if (!item.link && item.text.replace("@ui5/", "") === treeItem.text.replace("@ui5/", "")) { + to = index2; + break; + } + } + + moveIndex.push({ + from: index, + to: to + }); + } + } + + for (const moveItem of moveIndex) { + const item = tree.items[moveItem.from]; + item.text = "main"; + tree.items[moveItem.to].items.push(item); + delete tree.items[moveItem.from]; + } + return tree; })() ]; diff --git a/internal/documentation/.vitepress/theme/custom.css b/internal/documentation/.vitepress/theme/custom.css index 34838f25f2a..d7cac3bfd3b 100644 --- a/internal/documentation/.vitepress/theme/custom.css +++ b/internal/documentation/.vitepress/theme/custom.css @@ -35,6 +35,9 @@ /* Makes the page wider for the api docs */ --vp-layout-max-width: 80%; + + /* Makes the sidebar wider */ + --vp-sidebar-width: 300px; } .dark { @@ -321,3 +324,7 @@ li .learn-more { .jsdoc-object + .header-anchor { padding: 15px; } + +.VPSidebarItem p { + overflow-wrap: anywhere !important; +} From 2aa28103f47dda3c58dd664f6be7bd8322f58270 Mon Sep 17 00:00:00 2001 From: Gianluca Beil Date: Mon, 2 Feb 2026 10:41:26 +0100 Subject: [PATCH 20/36] docs: Remove console.log and push main entries to the top --- internal/documentation/.vitepress/config.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/internal/documentation/.vitepress/config.ts b/internal/documentation/.vitepress/config.ts index ce17634a0e6..347272fca27 100644 --- a/internal/documentation/.vitepress/config.ts +++ b/internal/documentation/.vitepress/config.ts @@ -334,7 +334,6 @@ function guide() { let to; for (let index2 = 0; index2 < tree.items.length; index2++) { const item = tree.items[index2]; - console.log(item, !item.link, item.text, treeItem.text.replace("@ui5/", "")) if (!item.link && item.text.replace("@ui5/", "") === treeItem.text.replace("@ui5/", "")) { to = index2; break; @@ -351,7 +350,9 @@ function guide() { for (const moveItem of moveIndex) { const item = tree.items[moveItem.from]; item.text = "main"; + tree.items[moveItem.to].items = tree.items[moveItem.to].items.reverse(); tree.items[moveItem.to].items.push(item); + tree.items[moveItem.to].items = tree.items[moveItem.to].items.reverse(); delete tree.items[moveItem.from]; } From 979838529d639335c8c8638669a2b0cf90c96f35 Mon Sep 17 00:00:00 2001 From: Gianluca Beil Date: Mon, 2 Feb 2026 12:45:21 +0100 Subject: [PATCH 21/36] docs: Remove unused dev dependency --- internal/documentation/package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/internal/documentation/package.json b/internal/documentation/package.json index 94f350fab3c..2ab2b0e2ba3 100644 --- a/internal/documentation/package.json +++ b/internal/documentation/package.json @@ -50,7 +50,6 @@ }, "devDependencies": { "@apidevtools/json-schema-ref-parser": "^14.2.1", - "@jsdoc/salty": "^0.2.9", "handlebars": "^4.7.8", "jsdoc": "^4.0.4", "open-cli": "^8.0.0", From dd74e4a1c389ae48fccf3e14c815e77e00159faf Mon Sep 17 00:00:00 2001 From: d3xter666 Date: Tue, 3 Feb 2026 14:27:51 +0200 Subject: [PATCH 22/36] refactor: Deliver docdash over NPM and patch it --- .../jsdoc/patches/tmpl/augments.tmpl | 10 + .../jsdoc/patches/tmpl/container.tmpl | 186 ++++++++++++++++++ .../jsdoc/patches/tmpl/details.tmpl | 133 +++++++++++++ .../jsdoc/patches/tmpl/example.tmpl | 4 + .../jsdoc/patches/tmpl/examples.tmpl | 17 ++ .../jsdoc/patches/tmpl/exceptions.tmpl | 14 ++ .../jsdoc/patches/tmpl/layout.tmpl | 10 + .../jsdoc/patches/tmpl/mainpage.tmpl | 12 ++ .../jsdoc/patches/tmpl/members.tmpl | 35 ++++ .../jsdoc/patches/tmpl/method.tmpl | 128 ++++++++++++ .../jsdoc/patches/tmpl/modifies.tmpl | 7 + .../jsdoc/patches/tmpl/namespace.tmpl | 129 ++++++++++++ .../jsdoc/patches/tmpl/params.tmpl | 67 +++++++ .../jsdoc/patches/tmpl/properties.tmpl | 39 ++++ .../jsdoc/patches/tmpl/returns.tmpl | 11 ++ .../jsdoc/patches/tmpl/source.tmpl | 6 + .../jsdoc/patches/tmpl/tutorial.tmpl | 13 ++ .../jsdoc/patches/tmpl/type.tmpl | 23 +++ internal/documentation/package.json | 8 +- .../documentation/scripts/prepareDocdash.sh | 56 ++++++ package-lock.json | 12 +- 21 files changed, 916 insertions(+), 4 deletions(-) create mode 100644 internal/documentation/jsdoc/patches/tmpl/augments.tmpl create mode 100644 internal/documentation/jsdoc/patches/tmpl/container.tmpl create mode 100644 internal/documentation/jsdoc/patches/tmpl/details.tmpl create mode 100644 internal/documentation/jsdoc/patches/tmpl/example.tmpl create mode 100644 internal/documentation/jsdoc/patches/tmpl/examples.tmpl create mode 100644 internal/documentation/jsdoc/patches/tmpl/exceptions.tmpl create mode 100644 internal/documentation/jsdoc/patches/tmpl/layout.tmpl create mode 100644 internal/documentation/jsdoc/patches/tmpl/mainpage.tmpl create mode 100644 internal/documentation/jsdoc/patches/tmpl/members.tmpl create mode 100644 internal/documentation/jsdoc/patches/tmpl/method.tmpl create mode 100644 internal/documentation/jsdoc/patches/tmpl/modifies.tmpl create mode 100644 internal/documentation/jsdoc/patches/tmpl/namespace.tmpl create mode 100644 internal/documentation/jsdoc/patches/tmpl/params.tmpl create mode 100644 internal/documentation/jsdoc/patches/tmpl/properties.tmpl create mode 100644 internal/documentation/jsdoc/patches/tmpl/returns.tmpl create mode 100644 internal/documentation/jsdoc/patches/tmpl/source.tmpl create mode 100644 internal/documentation/jsdoc/patches/tmpl/tutorial.tmpl create mode 100644 internal/documentation/jsdoc/patches/tmpl/type.tmpl create mode 100755 internal/documentation/scripts/prepareDocdash.sh diff --git a/internal/documentation/jsdoc/patches/tmpl/augments.tmpl b/internal/documentation/jsdoc/patches/tmpl/augments.tmpl new file mode 100644 index 00000000000..053f971a107 --- /dev/null +++ b/internal/documentation/jsdoc/patches/tmpl/augments.tmpl @@ -0,0 +1,10 @@ + + + + +- + + diff --git a/internal/documentation/jsdoc/patches/tmpl/container.tmpl b/internal/documentation/jsdoc/patches/tmpl/container.tmpl new file mode 100644 index 00000000000..420832f2d07 --- /dev/null +++ b/internal/documentation/jsdoc/patches/tmpl/container.tmpl @@ -0,0 +1,186 @@ + + + + + + + + + + +## Namespace + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +### Example 1? 's':'' ?> + + + + + + + +### Extends + + + + + +### Requires + + +- + + + + +### Classes + + +- + + + + + +### Interfaces + + +- + + + + + +### Mixins + + +- + + + + + +### Namespaces + + +- + + + + + +### Members + + + + + + + +### Methods + + + + + + + +### Type Definitions + + + + + + + + + +### Events + + + + + + + + + diff --git a/internal/documentation/jsdoc/patches/tmpl/details.tmpl b/internal/documentation/jsdoc/patches/tmpl/details.tmpl new file mode 100644 index 00000000000..276c0fbb310 --- /dev/null +++ b/internal/documentation/jsdoc/patches/tmpl/details.tmpl @@ -0,0 +1,133 @@ + + + +**Description:** + + + + +**Source:** , + + + + +**Version:** + + + + +**Since:** + + + + +**Inherited From:** + + + + +**Overrides:** + + + + +**Implementations:** + +- + + + + + +**Implements:** + +- + + + + + +**Mixes In:** + +- + + + + + +**Deprecated:** + + + + +**Author:** + +- + + + + + +**Copyright:** + + + + +**License:** + + + + +**Default Value:** + + + + + +**Tutorials:** + +- + + + + + +**See:** + +- + + + + + +**To Do:** + +- + + + + + + +##### Properties: + + + + diff --git a/internal/documentation/jsdoc/patches/tmpl/example.tmpl b/internal/documentation/jsdoc/patches/tmpl/example.tmpl new file mode 100644 index 00000000000..031a1ced242 --- /dev/null +++ b/internal/documentation/jsdoc/patches/tmpl/example.tmpl @@ -0,0 +1,4 @@ + +``` + +``` diff --git a/internal/documentation/jsdoc/patches/tmpl/examples.tmpl b/internal/documentation/jsdoc/patches/tmpl/examples.tmpl new file mode 100644 index 00000000000..a4fdceea1f7 --- /dev/null +++ b/internal/documentation/jsdoc/patches/tmpl/examples.tmpl @@ -0,0 +1,17 @@ + +** + + +``` + +``` + + diff --git a/internal/documentation/jsdoc/patches/tmpl/exceptions.tmpl b/internal/documentation/jsdoc/patches/tmpl/exceptions.tmpl new file mode 100644 index 00000000000..cc1dc7af193 --- /dev/null +++ b/internal/documentation/jsdoc/patches/tmpl/exceptions.tmpl @@ -0,0 +1,14 @@ + + + + +**Type:** + + + + + + + diff --git a/internal/documentation/jsdoc/patches/tmpl/layout.tmpl b/internal/documentation/jsdoc/patches/tmpl/layout.tmpl new file mode 100644 index 00000000000..7ae96f8c0b2 --- /dev/null +++ b/internal/documentation/jsdoc/patches/tmpl/layout.tmpl @@ -0,0 +1,10 @@ +--- +prev: false +next: false +--- + + +# + + + diff --git a/internal/documentation/jsdoc/patches/tmpl/mainpage.tmpl b/internal/documentation/jsdoc/patches/tmpl/mainpage.tmpl new file mode 100644 index 00000000000..ac73961fa2a --- /dev/null +++ b/internal/documentation/jsdoc/patches/tmpl/mainpage.tmpl @@ -0,0 +1,12 @@ + + + +# + + + + + diff --git a/internal/documentation/jsdoc/patches/tmpl/members.tmpl b/internal/documentation/jsdoc/patches/tmpl/members.tmpl new file mode 100644 index 00000000000..fc3169c2b2d --- /dev/null +++ b/internal/documentation/jsdoc/patches/tmpl/members.tmpl @@ -0,0 +1,35 @@ +, if so don't add another one +var hasClosingDiv = signature.includes('
      '); +?> +####
      /, '').replace(/<\/div>$/, '') + name + signature + (hasClosingDiv ? '' : '
      ') ?> + + + + + + + + + + + + +##### Type: +- + + + +##### Fires: + +- + + + + +##### Example 1? 's':'' ?>: + + diff --git a/internal/documentation/jsdoc/patches/tmpl/method.tmpl b/internal/documentation/jsdoc/patches/tmpl/method.tmpl new file mode 100644 index 00000000000..7434ca22e89 --- /dev/null +++ b/internal/documentation/jsdoc/patches/tmpl/method.tmpl @@ -0,0 +1,128 @@ + + + +## 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/patches/tmpl/modifies.tmpl b/internal/documentation/jsdoc/patches/tmpl/modifies.tmpl new file mode 100644 index 00000000000..4bba92bb498 --- /dev/null +++ b/internal/documentation/jsdoc/patches/tmpl/modifies.tmpl @@ -0,0 +1,7 @@ + + + +**Type:** + diff --git a/internal/documentation/jsdoc/patches/tmpl/namespace.tmpl b/internal/documentation/jsdoc/patches/tmpl/namespace.tmpl new file mode 100644 index 00000000000..348a59d0a53 --- /dev/null +++ b/internal/documentation/jsdoc/patches/tmpl/namespace.tmpl @@ -0,0 +1,129 @@ + + + + +## 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/patches/tmpl/params.tmpl b/internal/documentation/jsdoc/patches/tmpl/params.tmpl new file mode 100644 index 00000000000..97f0e3415ec --- /dev/null +++ b/internal/documentation/jsdoc/patches/tmpl/params.tmpl @@ -0,0 +1,67 @@ + +| Name | Type | Attributes | Default | Description | +| --- | --- | --- | --- | --- | +| `` | | optional, nullable, repeatable | `` | ').replaceAll("```js", "
      ").replaceAll("```", "
      ") ?>
      *Properties:* + diff --git a/internal/documentation/jsdoc/patches/tmpl/properties.tmpl b/internal/documentation/jsdoc/patches/tmpl/properties.tmpl new file mode 100644 index 00000000000..3830cbdad29 --- /dev/null +++ b/internal/documentation/jsdoc/patches/tmpl/properties.tmpl @@ -0,0 +1,39 @@ +
      NameTypeAttributesDefaultDescription
      optionalnullable").replaceAll("```js", "
      ").replaceAll("```", "
      ") ?>
      Properties
      diff --git a/internal/documentation/jsdoc/patches/tmpl/returns.tmpl b/internal/documentation/jsdoc/patches/tmpl/returns.tmpl new file mode 100644 index 00000000000..bf3e18fc412 --- /dev/null +++ b/internal/documentation/jsdoc/patches/tmpl/returns.tmpl @@ -0,0 +1,11 @@ + + + + + + +**Type:** + diff --git a/internal/documentation/jsdoc/patches/tmpl/source.tmpl b/internal/documentation/jsdoc/patches/tmpl/source.tmpl new file mode 100644 index 00000000000..38334dc60e2 --- /dev/null +++ b/internal/documentation/jsdoc/patches/tmpl/source.tmpl @@ -0,0 +1,6 @@ + +``` + +``` diff --git a/internal/documentation/jsdoc/patches/tmpl/tutorial.tmpl b/internal/documentation/jsdoc/patches/tmpl/tutorial.tmpl new file mode 100644 index 00000000000..40a160efc8f --- /dev/null +++ b/internal/documentation/jsdoc/patches/tmpl/tutorial.tmpl @@ -0,0 +1,13 @@ + 0) { ?> +## Child Tutorials + + +- + + + +## + + diff --git a/internal/documentation/jsdoc/patches/tmpl/type.tmpl b/internal/documentation/jsdoc/patches/tmpl/type.tmpl new file mode 100644 index 00000000000..33434d33aa4 --- /dev/null +++ b/internal/documentation/jsdoc/patches/tmpl/type.tmpl @@ -0,0 +1,23 @@ +", ""); + 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); + } + ?> \| \ No newline at end of file diff --git a/internal/documentation/package.json b/internal/documentation/package.json index 2ab2b0e2ba3..fe631588429 100644 --- a/internal/documentation/package.json +++ b/internal/documentation/package.json @@ -24,14 +24,15 @@ "scripts": { "start": "vitepress dev --open", "lint": "eslint .", - "depcheck": "depcheck --ignores @ui5/documentation,vitepress,docdash,jsdoc,open-cli,handlebars,@types/node,@ui5/webcomponents,autoprefixer,cssnano,@theme/components", + "depcheck": "depcheck --ignores @ui5/documentation,vitepress,jsdoc,open-cli,handlebars,@types/node,@ui5/webcomponents,autoprefixer,cssnano,@theme/components", "dev": "vitepress dev", "build:vitepress": "vitepress build", "build:assets": "sh -c 'DEST_DIR=${1:-./dist}; cp -r ./docs/images \"$DEST_DIR/images\"' --", "preview": "vitepress preview --port 8080", + "prepare-docdash": "./scripts/prepareDocdash.sh", "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)", - "jsdoc-generate-gh-pages": "jsdoc -c jsdoc/jsdoc.json -t jsdoc/docdash ./ || (echo 'Error during JSDoc generation! Check log.' && exit 1)", + "jsdoc-generate": "npm run prepare-docdash && jsdoc -c jsdoc/jsdoc-workspace.json -t jsdoc/docdash ./ || (echo 'Error during JSDoc generation! Check log.' && exit 1)", + "jsdoc-generate-gh-pages": "npm run prepare-docdash && 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", @@ -50,6 +51,7 @@ }, "devDependencies": { "@apidevtools/json-schema-ref-parser": "^14.2.1", + "docdash": "2.0.2", "handlebars": "^4.7.8", "jsdoc": "^4.0.4", "open-cli": "^8.0.0", diff --git a/internal/documentation/scripts/prepareDocdash.sh b/internal/documentation/scripts/prepareDocdash.sh new file mode 100755 index 00000000000..479e6e78b3a --- /dev/null +++ b/internal/documentation/scripts/prepareDocdash.sh @@ -0,0 +1,56 @@ +#!/usr/bin/env bash +# +# Prepares docdash for jsdoc generation by applying patches for VitePress-compatible Markdown output. +# +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +ROOT_DIR="$(dirname "$SCRIPT_DIR")" +PATCHES_DIR="$ROOT_DIR/jsdoc/patches" +TARGET_DIR="$ROOT_DIR/jsdoc/docdash" + +log() { echo "[prepareDocdash] $1"; } +error() { echo "[prepareDocdash] ERROR: $1" >&2; exit 1; } + +# Find docdash in node_modules (handles monorepo hoisting) +find_docdash() { + local dir="$ROOT_DIR" + while [[ "$dir" != "/" ]]; do + [[ -d "$dir/node_modules/docdash" ]] && echo "$dir/node_modules/docdash" && return + dir="$(dirname "$dir")" + done + return 1 +} + +DOCDASH_SOURCE=$(find_docdash) || error "docdash not found in node_modules. Run 'npm install' first." + +log "Source: $DOCDASH_SOURCE" +log "Target: $TARGET_DIR" + +# Clean and copy docdash +rm -rf "$TARGET_DIR" +mkdir -p "$TARGET_DIR" +cp "$DOCDASH_SOURCE/publish.js" "$TARGET_DIR/" +cp -r "$DOCDASH_SOURCE/tmpl" "$TARGET_DIR/" +echo '{"type": "commonjs"}' > "$TARGET_DIR/package.json" +log "docdash copied successfully" + +# Apply patch (git apply preferred, pre-patched file as fallback) +if git apply --directory=jsdoc/docdash "$PATCHES_DIR/publish.js.patch" 2>/dev/null; then + log "Patch applied successfully using 'git apply'" +else + error "Could not apply patch: git apply failed and no pre-patched file exists" +fi + +# Replace templates with custom VitePress-compatible versions +rm -rf "$TARGET_DIR/tmpl" +cp -r "$PATCHES_DIR/tmpl" "$TARGET_DIR/" +log "Templates replaced successfully" + +# Verify +[[ -f "$TARGET_DIR/publish.js" ]] || error "Verification failed: publish.js not found" +grep -q "githubSourceBaseUrl" "$TARGET_DIR/publish.js" || error "Verification failed: publish.js missing modifications" +grep -q "## " "$TARGET_DIR/tmpl/container.tmpl" || error "Verification failed: templates not VitePress-compatible" +log "Verification passed - docdash is ready for use" + +log "docdash preparation completed successfully!" diff --git a/package-lock.json b/package-lock.json index 85fb24ea618..874df1d4611 100644 --- a/package-lock.json +++ b/package-lock.json @@ -66,7 +66,7 @@ }, "devDependencies": { "@apidevtools/json-schema-ref-parser": "^14.2.1", - "@jsdoc/salty": "^0.2.9", + "docdash": "^2.0.2", "handlebars": "^4.7.8", "jsdoc": "^4.0.4", "open-cli": "^8.0.0", @@ -8951,6 +8951,16 @@ "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", From adee4a6bc2bbbe1f5e560bf540fc5908f134f03e Mon Sep 17 00:00:00 2001 From: Gianluca Beil Date: Tue, 3 Feb 2026 13:25:13 +0100 Subject: [PATCH 23/36] docs: Add code comments in config.ts --- internal/documentation/.vitepress/config.ts | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/internal/documentation/.vitepress/config.ts b/internal/documentation/.vitepress/config.ts index 347272fca27..6a6c7e62aa8 100644 --- a/internal/documentation/.vitepress/config.ts +++ b/internal/documentation/.vitepress/config.ts @@ -296,6 +296,7 @@ function guide() { } function appendToTree(tree, file, treePath, index) { + // If it's the last leaf if (treePath.length - 1 === index) { tree.items.push({ text: treePath[index].replace("module-", ""), @@ -303,7 +304,10 @@ function guide() { }); return; } + + // All functions below call appendToTree recursively to create the tree structure let found = false; + // Checks if the leaf does already exist and adds new leafs to it for (const treeItem of tree.items) { if (treeItem.text === treePath[index]) { appendToTree(treeItem, file, treePath, index+1); @@ -311,6 +315,7 @@ function guide() { break; } } + // Creates a new leaf for the entry and adds leafs inside of it if (!found) { let newItem = { text: treePath[index].replace("module-", ""), @@ -322,14 +327,19 @@ function guide() { } } - tree.items = tree.items[0].items; // Display items inside @ui5 as root + // Display items inside @ui5 as root + tree.items = tree.items[0].items; const moveIndex = []; for (let index = 0; index < tree.items.length; index++) { const treeItem = tree.items[index]; + + // Adds @ui5 prefix to all leafs in the first level if (!treeItem.link) treeItem.text = "@ui5/" + treeItem.text; + // Finds leafs that have a similar name in first level e.g. @ui5/sever and server + // It adds all found items into moveIndex so that they can later be moved into the corresponding package leaf as main if (treeItem.link) { let to; for (let index2 = 0; index2 < tree.items.length; index2++) { @@ -347,6 +357,8 @@ function guide() { } } + // Iterates through the moveIndex and moves the items + // Reverse is that it adds main to the beginning instead of the end for (const moveItem of moveIndex) { const item = tree.items[moveItem.from]; item.text = "main"; From 2733097c3941484ba67ba65d2a0703fd18f95ec6 Mon Sep 17 00:00:00 2001 From: Gianluca Beil Date: Tue, 3 Feb 2026 13:35:41 +0100 Subject: [PATCH 24/36] docs: Revert changes in publish.js --- .../documentation/jsdoc/docdash/publish.js | 1285 +++++++++-------- 1 file changed, 713 insertions(+), 572 deletions(-) diff --git a/internal/documentation/jsdoc/docdash/publish.js b/internal/documentation/jsdoc/docdash/publish.js index 22e75d348c8..5894d832722 100644 --- a/internal/documentation/jsdoc/docdash/publish.js +++ b/internal/documentation/jsdoc/docdash/publish.js @@ -4,237 +4,281 @@ 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('node:util'); +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; -// Modified to be able to link source files to GitHub -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); + return helper.find(data, spec); } function tutoriallink(tutorial) { - return helper.toTutorial(tutorial, null, { tag: 'em', classname: 'disabled', prefix: '' }); + return helper.toTutorial(tutorial, null, { tag: 'em', classname: 'disabled', prefix: 'Tutorial: ' }); } 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 + ''; } function needsSignature(doclet) { - 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; + 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 = []; + 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 = ''; - - // 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 + '
      '; + 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 + ''; } -// 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); - - f.attribs = util.format('%s', attribsString); + 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, '/'); - }); + 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 + ".md"), - 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 + } - // Modified: replaceAll fixes pipe escaping - fs.writeFileSync(outpath, html.replaceAll("\\|", "|"), '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) { - // 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); - }); + 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); + }); } /** @@ -249,442 +293,539 @@ 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.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.hideconstructor) { + symbol.name = symbol.name.replace('module:', '(require("') + '"))'; + } + + return symbol; + }); + } + }); } -/** - @param {TAFFY} taffyData See . - @param {object} opts - @param {Tutorial} tutorials - */ -exports.publish = function(taffyData, opts, tutorials) { - // Modified to fix .md file extensions in internal links - helper.fileExtension = ""; - - 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; - - // 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 - - 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); - } - - // 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); -}; - -// Taken from templateHelper.js in jsdoc/util - -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 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 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 linktoTutorial(longName, name) { + return tutoriallink(name); } -function isComplexTypeExpression(expr) { - // record types, type unions, and type applications all count as "complex" - return /^{.+}$/.test(expr) || /^.+\|.+$/.test(expr) || /^.+<.+>$/.test(expr); +function linktoExternal(longName, name) { + return linkto(longName, name.replace(/(^"|"$)/g, '')); } -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); -} +/** + * 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 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]; - - // 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] || helper.longnameToUrl[displayBase]; - if (baseUrl) { - // Remove .md extension for VitePress compatibility - baseUrl = baseUrl.replace(/\.md$/, ''); - result = util.format('%s', encodeURI(baseUrl), classString, htmlsafe(displayBase)); - } else { - result = htmlsafe(displayBase); - } - - // Always use angle brackets without an extra dot: Base - 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; +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); +}; From 78e0f3e8e7ced45ddaffddbcea20863c6f3e752e Mon Sep 17 00:00:00 2001 From: Gianluca Beil Date: Tue, 3 Feb 2026 13:55:18 +0100 Subject: [PATCH 25/36] docs: Update .gitignore --- .gitignore | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index afee8b743ce..c00cfe15acf 100644 --- a/.gitignore +++ b/.gitignore @@ -68,4 +68,6 @@ internal/documentation/.vitepress/dist internal/documentation/.vitepress/cache internal/documentation/dist internal/documentation/schema/* -internal/documentation/docs/api \ No newline at end of file +internal/documentation/docs/api +internal/documentation/jsdoc/docdash/tmpl +internal/documentation/jsdoc/docdash/publish.js \ No newline at end of file From 139618d54bc151253149f783d5395b55adf8232c Mon Sep 17 00:00:00 2001 From: Gianluca Beil Date: Tue, 3 Feb 2026 13:56:39 +0100 Subject: [PATCH 26/36] docs: Move docdash out of cli project --- .../documentation/jsdoc/docdash/publish.js | 831 ---------- .../jsdoc/docdash/tmpl/augments.tmpl | 10 - .../jsdoc/docdash/tmpl/container.tmpl | 186 --- .../jsdoc/docdash/tmpl/details.tmpl | 133 -- .../jsdoc/docdash/tmpl/example.tmpl | 4 - .../jsdoc/docdash/tmpl/examples.tmpl | 17 - .../jsdoc/docdash/tmpl/exceptions.tmpl | 14 - .../jsdoc/docdash/tmpl/layout.tmpl | 10 - .../jsdoc/docdash/tmpl/mainpage.tmpl | 12 - .../jsdoc/docdash/tmpl/members.tmpl | 35 - .../jsdoc/docdash/tmpl/method.tmpl | 128 -- .../jsdoc/docdash/tmpl/modifies.tmpl | 7 - .../jsdoc/docdash/tmpl/namespace.tmpl | 129 -- .../jsdoc/docdash/tmpl/params.tmpl | 67 - .../jsdoc/docdash/tmpl/properties.tmpl | 39 - .../jsdoc/docdash/tmpl/returns.tmpl | 11 - .../jsdoc/docdash/tmpl/source.tmpl | 6 - .../jsdoc/docdash/tmpl/tutorial.tmpl | 13 - .../jsdoc/docdash/tmpl/type.tmpl | 23 - .../jsdoc/patches/publish.js.patch | 1398 +++++++++++++++++ .../documentation/scripts/prepareDocdash.sh | 53 +- 21 files changed, 1403 insertions(+), 1723 deletions(-) delete mode 100644 internal/documentation/jsdoc/docdash/publish.js delete mode 100644 internal/documentation/jsdoc/docdash/tmpl/augments.tmpl delete mode 100644 internal/documentation/jsdoc/docdash/tmpl/container.tmpl delete mode 100644 internal/documentation/jsdoc/docdash/tmpl/details.tmpl delete mode 100644 internal/documentation/jsdoc/docdash/tmpl/example.tmpl delete mode 100644 internal/documentation/jsdoc/docdash/tmpl/examples.tmpl delete mode 100644 internal/documentation/jsdoc/docdash/tmpl/exceptions.tmpl delete mode 100644 internal/documentation/jsdoc/docdash/tmpl/layout.tmpl delete mode 100644 internal/documentation/jsdoc/docdash/tmpl/mainpage.tmpl delete mode 100644 internal/documentation/jsdoc/docdash/tmpl/members.tmpl delete mode 100644 internal/documentation/jsdoc/docdash/tmpl/method.tmpl delete mode 100644 internal/documentation/jsdoc/docdash/tmpl/modifies.tmpl delete mode 100644 internal/documentation/jsdoc/docdash/tmpl/namespace.tmpl delete mode 100644 internal/documentation/jsdoc/docdash/tmpl/params.tmpl delete mode 100644 internal/documentation/jsdoc/docdash/tmpl/properties.tmpl delete mode 100644 internal/documentation/jsdoc/docdash/tmpl/returns.tmpl delete mode 100644 internal/documentation/jsdoc/docdash/tmpl/source.tmpl delete mode 100644 internal/documentation/jsdoc/docdash/tmpl/tutorial.tmpl delete mode 100644 internal/documentation/jsdoc/docdash/tmpl/type.tmpl create mode 100644 internal/documentation/jsdoc/patches/publish.js.patch diff --git a/internal/documentation/jsdoc/docdash/publish.js b/internal/documentation/jsdoc/docdash/publish.js deleted file mode 100644 index 5894d832722..00000000000 --- a/internal/documentation/jsdoc/docdash/publish.js +++ /dev/null @@ -1,831 +0,0 @@ -/*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 deleted file mode 100644 index 053f971a107..00000000000 --- a/internal/documentation/jsdoc/docdash/tmpl/augments.tmpl +++ /dev/null @@ -1,10 +0,0 @@ - - - - -- - - diff --git a/internal/documentation/jsdoc/docdash/tmpl/container.tmpl b/internal/documentation/jsdoc/docdash/tmpl/container.tmpl deleted file mode 100644 index 420832f2d07..00000000000 --- a/internal/documentation/jsdoc/docdash/tmpl/container.tmpl +++ /dev/null @@ -1,186 +0,0 @@ - - - - - - - - - - -## 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 deleted file mode 100644 index 276c0fbb310..00000000000 --- a/internal/documentation/jsdoc/docdash/tmpl/details.tmpl +++ /dev/null @@ -1,133 +0,0 @@ - - - -**Description:** - - - - -**Source:** , - - - - -**Version:** - - - - -**Since:** - - - - -**Inherited From:** - - - - -**Overrides:** - - - - -**Implementations:** - -- - - - - - -**Implements:** - -- - - - - - -**Mixes In:** - -- - - - - - -**Deprecated:** - - - - -**Author:** - -- - - - - - -**Copyright:** - - - - -**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 deleted file mode 100644 index 031a1ced242..00000000000 --- a/internal/documentation/jsdoc/docdash/tmpl/example.tmpl +++ /dev/null @@ -1,4 +0,0 @@ - -``` - -``` diff --git a/internal/documentation/jsdoc/docdash/tmpl/examples.tmpl b/internal/documentation/jsdoc/docdash/tmpl/examples.tmpl deleted file mode 100644 index a4fdceea1f7..00000000000 --- a/internal/documentation/jsdoc/docdash/tmpl/examples.tmpl +++ /dev/null @@ -1,17 +0,0 @@ - -** - - -``` - -``` - - diff --git a/internal/documentation/jsdoc/docdash/tmpl/exceptions.tmpl b/internal/documentation/jsdoc/docdash/tmpl/exceptions.tmpl deleted file mode 100644 index cc1dc7af193..00000000000 --- a/internal/documentation/jsdoc/docdash/tmpl/exceptions.tmpl +++ /dev/null @@ -1,14 +0,0 @@ - - - - -**Type:** - - - - - - - diff --git a/internal/documentation/jsdoc/docdash/tmpl/layout.tmpl b/internal/documentation/jsdoc/docdash/tmpl/layout.tmpl deleted file mode 100644 index 7ae96f8c0b2..00000000000 --- a/internal/documentation/jsdoc/docdash/tmpl/layout.tmpl +++ /dev/null @@ -1,10 +0,0 @@ ---- -prev: false -next: false ---- - - -# - - - diff --git a/internal/documentation/jsdoc/docdash/tmpl/mainpage.tmpl b/internal/documentation/jsdoc/docdash/tmpl/mainpage.tmpl deleted file mode 100644 index ac73961fa2a..00000000000 --- a/internal/documentation/jsdoc/docdash/tmpl/mainpage.tmpl +++ /dev/null @@ -1,12 +0,0 @@ - - - -# - - - - - diff --git a/internal/documentation/jsdoc/docdash/tmpl/members.tmpl b/internal/documentation/jsdoc/docdash/tmpl/members.tmpl deleted file mode 100644 index fc3169c2b2d..00000000000 --- a/internal/documentation/jsdoc/docdash/tmpl/members.tmpl +++ /dev/null @@ -1,35 +0,0 @@ -, if so don't add another one -var hasClosingDiv = signature.includes(''); -?> -####
          /, '').replace(/<\/div>$/, '') + name + signature + (hasClosingDiv ? '' : '
          ') ?> - - - - - - - - - - - - -##### Type: -- - - - -##### Fires: - -- - - - - -##### Example 1? 's':'' ?>: - - diff --git a/internal/documentation/jsdoc/docdash/tmpl/method.tmpl b/internal/documentation/jsdoc/docdash/tmpl/method.tmpl deleted file mode 100644 index 7434ca22e89..00000000000 --- a/internal/documentation/jsdoc/docdash/tmpl/method.tmpl +++ /dev/null @@ -1,128 +0,0 @@ - - - -## 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 deleted file mode 100644 index 4bba92bb498..00000000000 --- a/internal/documentation/jsdoc/docdash/tmpl/modifies.tmpl +++ /dev/null @@ -1,7 +0,0 @@ - - - -**Type:** - diff --git a/internal/documentation/jsdoc/docdash/tmpl/namespace.tmpl b/internal/documentation/jsdoc/docdash/tmpl/namespace.tmpl deleted file mode 100644 index 348a59d0a53..00000000000 --- a/internal/documentation/jsdoc/docdash/tmpl/namespace.tmpl +++ /dev/null @@ -1,129 +0,0 @@ - - - - -## 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 deleted file mode 100644 index 97f0e3415ec..00000000000 --- a/internal/documentation/jsdoc/docdash/tmpl/params.tmpl +++ /dev/null @@ -1,67 +0,0 @@ - -| Name | Type | Attributes | Default | Description | -| --- | --- | --- | --- | --- | -| `` | | 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 deleted file mode 100644 index 3830cbdad29..00000000000 --- a/internal/documentation/jsdoc/docdash/tmpl/properties.tmpl +++ /dev/null @@ -1,39 +0,0 @@ -
          NameTypeAttributesDefaultDescription
          optionalnullable").replaceAll("```js", "
          ").replaceAll("```", "
          ") ?>
          Properties
          diff --git a/internal/documentation/jsdoc/docdash/tmpl/returns.tmpl b/internal/documentation/jsdoc/docdash/tmpl/returns.tmpl deleted file mode 100644 index bf3e18fc412..00000000000 --- a/internal/documentation/jsdoc/docdash/tmpl/returns.tmpl +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - -**Type:** - diff --git a/internal/documentation/jsdoc/docdash/tmpl/source.tmpl b/internal/documentation/jsdoc/docdash/tmpl/source.tmpl deleted file mode 100644 index 38334dc60e2..00000000000 --- a/internal/documentation/jsdoc/docdash/tmpl/source.tmpl +++ /dev/null @@ -1,6 +0,0 @@ - -``` - -``` diff --git a/internal/documentation/jsdoc/docdash/tmpl/tutorial.tmpl b/internal/documentation/jsdoc/docdash/tmpl/tutorial.tmpl deleted file mode 100644 index 40a160efc8f..00000000000 --- a/internal/documentation/jsdoc/docdash/tmpl/tutorial.tmpl +++ /dev/null @@ -1,13 +0,0 @@ - 0) { ?> -## Child Tutorials - - -- - - - -## - - diff --git a/internal/documentation/jsdoc/docdash/tmpl/type.tmpl b/internal/documentation/jsdoc/docdash/tmpl/type.tmpl deleted file mode 100644 index 33434d33aa4..00000000000 --- a/internal/documentation/jsdoc/docdash/tmpl/type.tmpl +++ /dev/null @@ -1,23 +0,0 @@ -", ""); - 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); - } - ?> \| \ No newline at end of file diff --git a/internal/documentation/jsdoc/patches/publish.js.patch b/internal/documentation/jsdoc/patches/publish.js.patch new file mode 100644 index 00000000000..49dc45aa69c --- /dev/null +++ b/internal/documentation/jsdoc/patches/publish.js.patch @@ -0,0 +1,1398 @@ +diff --git a/internal/documentation/jsdoc/docdash/publish.js b/internal/documentation/jsdoc/docdash/publish.js +index 5894d8327..22e75d348 100644 +--- a/internal/documentation/jsdoc/docdash/publish.js ++++ b/internal/documentation/jsdoc/docdash/publish.js +@@ -4,281 +4,237 @@ + 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 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 outdir = path.normalize(env.opts.destination); ++// Modified to be able to link source files to GitHub ++var githubSourceBaseUrl; + +-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; +- } +- } +-} ++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.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 + ".md"), ++ 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'); ++ // Modified: replaceAll fixes pipe escaping ++ fs.writeFileSync(outpath, html.replaceAll("\\|", "|"), '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) { ++ // 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); ++ }); + } + + /** +@@ -293,539 +249,442 @@ 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; ++ }); ++ } ++ }); + } + +-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; +-} ++/** ++ @param {TAFFY} taffyData See . ++ @param {object} opts ++ @param {Tutorial} tutorials ++ */ ++exports.publish = function(taffyData, opts, tutorials) { ++ // Modified to fix .md file extensions in internal links ++ helper.fileExtension = ""; ++ ++ 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; ++ ++ // 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 ++ ++ 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); ++ } ++ ++ // 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 linktoTutorial(longName, name) { +- return tutoriallink(name); ++// Taken from templateHelper.js in jsdoc/util ++ ++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 linktoExternal(longName, name) { +- return linkto(longName, name.replace(/(^"|"$)/g, '')); ++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; + } + +-/** +- * 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 isComplexTypeExpression(expr) { ++ // record types, type unions, and type applications all count as "complex" ++ return /^{.+}$/.test(expr) || /^.+\|.+$/.test(expr) || /^.+<.+>$/.test(expr); ++} + +-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; ++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); + } + +-/** +- @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); +-}; ++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]; ++ ++ // 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] || helper.longnameToUrl[displayBase]; ++ if (baseUrl) { ++ // Remove .md extension for VitePress compatibility ++ baseUrl = baseUrl.replace(/\.md$/, ''); ++ result = util.format('%s', encodeURI(baseUrl), classString, htmlsafe(displayBase)); ++ } else { ++ result = htmlsafe(displayBase); ++ } ++ ++ // Always use angle brackets without an extra dot: Base ++ 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/scripts/prepareDocdash.sh b/internal/documentation/scripts/prepareDocdash.sh index 479e6e78b3a..85d542777b9 100755 --- a/internal/documentation/scripts/prepareDocdash.sh +++ b/internal/documentation/scripts/prepareDocdash.sh @@ -4,53 +4,10 @@ # set -euo pipefail -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -ROOT_DIR="$(dirname "$SCRIPT_DIR")" -PATCHES_DIR="$ROOT_DIR/jsdoc/patches" -TARGET_DIR="$ROOT_DIR/jsdoc/docdash" +DOCDASH_PATH="$(npm list docdash --parseable)" +DOCDASH_DESTINATION="jsdoc/docdash" -log() { echo "[prepareDocdash] $1"; } -error() { echo "[prepareDocdash] ERROR: $1" >&2; exit 1; } +cp -R "jsdoc/patches/tmpl" "$DOCDASH_DESTINATION/tmpl" +cp "$DOCDASH_PATH/publish.js" "$DOCDASH_DESTINATION/" -# Find docdash in node_modules (handles monorepo hoisting) -find_docdash() { - local dir="$ROOT_DIR" - while [[ "$dir" != "/" ]]; do - [[ -d "$dir/node_modules/docdash" ]] && echo "$dir/node_modules/docdash" && return - dir="$(dirname "$dir")" - done - return 1 -} - -DOCDASH_SOURCE=$(find_docdash) || error "docdash not found in node_modules. Run 'npm install' first." - -log "Source: $DOCDASH_SOURCE" -log "Target: $TARGET_DIR" - -# Clean and copy docdash -rm -rf "$TARGET_DIR" -mkdir -p "$TARGET_DIR" -cp "$DOCDASH_SOURCE/publish.js" "$TARGET_DIR/" -cp -r "$DOCDASH_SOURCE/tmpl" "$TARGET_DIR/" -echo '{"type": "commonjs"}' > "$TARGET_DIR/package.json" -log "docdash copied successfully" - -# Apply patch (git apply preferred, pre-patched file as fallback) -if git apply --directory=jsdoc/docdash "$PATCHES_DIR/publish.js.patch" 2>/dev/null; then - log "Patch applied successfully using 'git apply'" -else - error "Could not apply patch: git apply failed and no pre-patched file exists" -fi - -# Replace templates with custom VitePress-compatible versions -rm -rf "$TARGET_DIR/tmpl" -cp -r "$PATCHES_DIR/tmpl" "$TARGET_DIR/" -log "Templates replaced successfully" - -# Verify -[[ -f "$TARGET_DIR/publish.js" ]] || error "Verification failed: publish.js not found" -grep -q "githubSourceBaseUrl" "$TARGET_DIR/publish.js" || error "Verification failed: publish.js missing modifications" -grep -q "## " "$TARGET_DIR/tmpl/container.tmpl" || error "Verification failed: templates not VitePress-compatible" -log "Verification passed - docdash is ready for use" - -log "docdash preparation completed successfully!" +git apply jsdoc/patches/publish.js.patch \ No newline at end of file From ec5568b8cde7b15f6d5568269013964b87fc7f8b Mon Sep 17 00:00:00 2001 From: Gianluca Beil Date: Tue, 3 Feb 2026 14:19:59 +0100 Subject: [PATCH 27/36] docs: Revert README.md changes --- .../documentation/jsdoc/docdash/README.md | 141 ++++++++++++++++-- 1 file changed, 132 insertions(+), 9 deletions(-) diff --git a/internal/documentation/jsdoc/docdash/README.md b/internal/documentation/jsdoc/docdash/README.md index cdcc93bf990..09328d32062 100644 --- a/internal/documentation/jsdoc/docdash/README.md +++ b/internal/documentation/jsdoc/docdash/README.md @@ -1,19 +1,142 @@ # Docdash +[![Build Status](https://api.travis-ci.org/clenemt/docdash.png?branch=master)](https://travis-ci.org/clenemt/docdash) [![npm version](https://badge.fury.io/js/docdash.svg)](https://badge.fury.io/js/docdash) [![license](https://img.shields.io/npm/l/docdash.svg)](../../../../../docdash/LICENSE.md) -This folder includes files from [docdash 2.0.2](https://github.com/clenemt/docdash/tree/bee5d0789068be6a1e01ce02968b23dd021b4fb6), which is a documentation template for JSDoc. +A clean, responsive documentation template theme for JSDoc 4. -These files have been modified to change the output from HTML to Markdown, and to remove unnecessary features for our use case. +![docdash-screenshot](https://cloud.githubusercontent.com/assets/447956/13398144/4dde7f36-defd-11e5-8909-1a9013302cb9.png) -## 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 +![docdash-screenshot-2](https://cloud.githubusercontent.com/assets/447956/13401057/e30effd8-df0a-11e5-9f51-66257ac38e94.jpg) + +## Example +See http://clenemt.github.io/docdash/ for a sample demo. :rocket: + +## Install + +```bash +$ npm install docdash +``` + +## Usage +Clone repository to your designated `jsdoc` template directory, then: + +```bash +$ jsdoc entry-file.js -t path/to/docdash +``` + +## Usage (npm) +In your projects `package.json` file add a new script: + +```json +"script": { + "generate-docs": "node_modules/.bin/jsdoc -c jsdoc.json" +} +``` + +In your `jsdoc.json` file, add a template option. + +```json +"opts": { + "template": "node_modules/docdash" +} +``` + +## Sample `jsdoc.json` +See the config file for the [fixtures](../../../../../docdash/fixtures/fixtures.conf.json) or the sample below. + +```json +{ + "tags": { + "allowUnknownTags": false + }, + "source": { + "include": "../js", + "includePattern": "\\.js$", + "excludePattern": "(node_modules/|docs)" + }, + "plugins": [ + "plugins/markdown" + ], + "opts": { + "template": "assets/template/docdash/", + "encoding": "utf8", + "destination": "docs/", + "recurse": true, + "verbose": true + }, + "templates": { + "cleverLinks": false, + "monospaceLinks": false + } +} +``` + +## Options +Docdash supports the following options: + +```json5 +{ + "docdash": { + "static": [false|true], // Display the static members inside the navbar + "sort": [false|true], // Sort the methods in the navbar + "sectionOrder": [ // Order the main section in the navbar (default order shown here) + "Classes", + "Modules", + "Externals", + "Events", + "Namespaces", + "Mixins", + "Tutorials", + "Interfaces" + ], + "disqus": "", // Shortname for your disqus (subdomain during site creation) + "openGraph": { // Open Graph options (mostly for Facebook and other sites to easily extract meta information) + "title": "", // Title of the website + "type": "website", // Type of the website + "image": "", // Main image/logo + "site_name": "", // Site name + "url": "" // Main canonical URL for the main page of the site + }, + "meta": { // Meta information options (mostly for search engines that have not indexed your site yet) + "title": "", // Also will be used as postfix to actualy page title, prefixed with object/document name + "description": "", // Description of overal contents of your website + "keyword": "" // Keywords for search engines + }, + "search": [false|true], // Display seach box above navigation which allows to search/filter navigation items + "commonNav": [false|true], // Group all html code for