diff --git a/packages/builder/lib/processors/jsdoc/lib/createIndexFiles.js b/packages/builder/lib/processors/jsdoc/lib/createIndexFiles.js index 8dd7bf42e78..b11afedb140 100644 --- a/packages/builder/lib/processors/jsdoc/lib/createIndexFiles.js +++ b/packages/builder/lib/processors/jsdoc/lib/createIndexFiles.js @@ -67,7 +67,11 @@ function createIndexFiles(versionInfoFile, unpackedTestresourcesRoot, targetFile // Handle empty files scenario resolve({}); } else { - resolve(JSON.parse(String(data))); + try { + resolve(JSON.parse(String(data))); + } catch (oParseError) { + reject(new SyntaxError(`Failed to parse "${file}" as JSON: ${oParseError.message}`)); + } } }); }); diff --git a/packages/builder/lib/processors/jsdoc/lib/transformApiJson.js b/packages/builder/lib/processors/jsdoc/lib/transformApiJson.js index 6b9b9286797..c80be271665 100644 --- a/packages/builder/lib/processors/jsdoc/lib/transformApiJson.js +++ b/packages/builder/lib/processors/jsdoc/lib/transformApiJson.js @@ -9,6 +9,7 @@ const cheerio = require("cheerio"); const path = require('path'); const {TypeParser} = require("./ui5/template/utils/typeParser"); +const {extractSince} = require("./ui5/template/utils/versionUtil"); const log = (function() { try { return require("@ui5/logger").getLogger("builder:processors:jsdoc:transformApiJson"); @@ -455,8 +456,20 @@ function transformer(sInputFile, sOutputFile, sLibraryFile, vDependencyAPIFiles, oParameter.defaultValue = formatters.formatDefaultValue(oParameter.defaultValue); // Description - if (oParameter.description) { - oParameter.description = formatters.formatDescription(oParameter.description); + oParameter.description = formatters.formatDescriptionSince(oParameter.description, oParameter.since); + oParameter.description = formatters.formatDescription(oParameter.description); + + // Experimental + if (oParameter.experimental) { + oParameter.experimentalText = formatters.formatExperimental(oParameter.experimental.since ?? oParameter.experimental); + } + + // Deprecated + if (oParameter.deprecated) { + const dep = typeof oParameter.deprecated === "string" + ? extractSince(oParameter.deprecated) + : { since: oParameter.deprecated.since, value: oParameter.deprecated.text }; + oParameter.deprecatedText = formatters.formatDeprecated(dep?.since, dep?.value); } }); @@ -506,11 +519,22 @@ function transformer(sInputFile, sOutputFile, sLibraryFile, vDependencyAPIFiles, oProperty.name = formatters.formatEntityName(oProperty.name, oSymbol.name, oProperty.static); // Description + // Note: The "@since" information is not appended to the description here because it is + // rendered separately as a dedicated "Since" status in the SubApiDetail view. Appending it + // would duplicate the version information for typedef/enum properties. + oProperty.description = formatters.formatDescription(oProperty.description); + + // Experimental + if (oProperty.experimental) { + oProperty.experimentalText = formatters.formatExperimental(oProperty.experimental.since ?? oProperty.experimental); + } + + // Deprecated if (oProperty.deprecated) { - oProperty.description = formatters.formatDescription(oProperty.description, - oProperty.deprecated.text, oProperty.deprecated.since); - } else { - oProperty.description = formatters.formatDescription(oProperty.description); + const dep = typeof oProperty.deprecated === "string" + ? extractSince(oProperty.deprecated) + : { since: oProperty.deprecated.since, value: oProperty.deprecated.text }; + oProperty.deprecatedText = formatters.formatDeprecated(dep?.since, dep?.value); } // Examples @@ -582,6 +606,11 @@ function transformer(sInputFile, sOutputFile, sLibraryFile, vDependencyAPIFiles, // Default value oProperty.defaultValue = formatters.formatDefaultValue(oProperty.defaultValue); + // Experimental + if (oProperty.experimental) { + oProperty.experimentalText = formatters.formatExperimental(oProperty.experimental.since ?? oProperty.experimental); + } + // Deprecated if (oProperty.deprecated) { oProperty.deprecatedText = formatters.formatDeprecated(oProperty.deprecated.since, @@ -622,6 +651,11 @@ function transformer(sInputFile, sOutputFile, sLibraryFile, vDependencyAPIFiles, oAggregation.description = formatters.formatDescriptionSince(oAggregation.description, oAggregation.since); } + // Experimental + if (oAggregation.experimental) { + oAggregation.experimentalText = formatters.formatExperimental(oAggregation.experimental.since ?? oAggregation.experimental); + } + // Link enabled oAggregation.linkEnabled = !isBuiltInType(oAggregation.type); }); @@ -649,11 +683,18 @@ function transformer(sInputFile, sOutputFile, sLibraryFile, vDependencyAPIFiles, } // Description + oAssociation.description = formatters.formatDescriptionSince(oAssociation.description, oAssociation.since); + oAssociation.description = formatters.formatDescription(oAssociation.description); + + // Experimental + if (oAssociation.experimental) { + oAssociation.experimentalText = formatters.formatExperimental(oAssociation.experimental.since ?? oAssociation.experimental); + } + + // Deprecated if (oAssociation.deprecated) { - oAssociation.description = formatters.formatDescription(oAssociation.description, - oAssociation.deprecated.text, oAssociation.deprecated.since); - } else { - oAssociation.description = formatters.formatDescriptionSince(oAssociation.description, oAssociation.since); + oAssociation.deprecatedText = formatters.formatDeprecated(oAssociation.deprecated.since, + oAssociation.deprecated.text); } }); } @@ -784,10 +825,17 @@ function transformer(sInputFile, sOutputFile, sLibraryFile, vDependencyAPIFiles, oParameter.description = formatters.formatDescription(oParameter.description); } + // Experimental + if (oParameter.experimental) { + oParameter.experimentalText = formatters.formatExperimental(oParameter.experimental.since ?? oParameter.experimental); + } + // Deprecated if (oParameter.deprecated) { - oParameter.deprecatedText = formatters.formatDeprecated(oParameter.deprecated.since, - oParameter.deprecated.text); + const dep = typeof oParameter.deprecated === "string" + ? extractSince(oParameter.deprecated) + : { since: oParameter.deprecated.since, value: oParameter.deprecated.text }; + oParameter.deprecatedText = formatters.formatDeprecated(dep?.since, dep?.value); } }); } @@ -827,7 +875,7 @@ function transformer(sInputFile, sOutputFile, sLibraryFile, vDependencyAPIFiles, // Deprecated if (oMethod.deprecated) { - oMethod.deprecatedText = formatters.formatEventDeprecated(oMethod.deprecated.since, + oMethod.deprecatedText = formatters.formatMethodDeprecated(oMethod.deprecated.since, oMethod.deprecated.text); } @@ -842,11 +890,20 @@ function transformer(sInputFile, sOutputFile, sLibraryFile, vDependencyAPIFiles, oParameter.defaultValue = formatters.formatDefaultValue(oParameter.defaultValue); // Description + oParameter.description = formatters.formatDescriptionSince(oParameter.description, oParameter.since); + oParameter.description = formatters.formatDescription(oParameter.description); + + // Experimental + if (oParameter.experimental) { + oParameter.experimentalText = formatters.formatExperimental(oParameter.experimental.since ?? oParameter.experimental); + } + + // Deprecated if (oParameter.deprecated) { - oParameter.description = formatters.formatDescription(oParameter.description, - oParameter.deprecated.text, oParameter.deprecated.since); - } else { - oParameter.description = formatters.formatDescription(oParameter.description); + const dep = typeof oParameter.deprecated === "string" + ? extractSince(oParameter.deprecated) + : { since: oParameter.deprecated.since, value: oParameter.deprecated.text }; + oParameter.deprecatedText = formatters.formatDeprecated(dep?.since, dep?.value); } }); @@ -976,21 +1033,29 @@ function transformer(sInputFile, sOutputFile, sLibraryFile, vDependencyAPIFiles, oChainObject.aDependentLibraryFiles.forEach((sFile) => { aPromises.push(new Promise((oResolve) => { fs.readFile(sFile, 'utf8', (oError, oData) => { - oResolve(oError ? false : oData); + oResolve(oError ? false : {sFile, sData: oData}); }); })); }); return Promise.all(aPromises).then((aValues) => { const oDependentAPIs = {}; - aValues.forEach((sData) => { + aValues.forEach((oValue) => { + if (!oValue) { + return; + } + const {sFile, sData} = oValue; let oData; try { oData = JSON.parse(sData); } catch (e) { - // Silence possible dependency library invalid json errors as they are not critical - // and we should not fail BCP: 1870560058 + // Dependency library files are not critical; log and continue instead of failing. BCP: 1870560058 + if (sData.trim() === "") { + log.verbose(`The file "${sFile}" is empty.`); + } else { + log.verbose(`Failed to parse "${sFile}" as JSON: ${e.message}`); + } } // OpenUI5 build specific - own library can be listed as dependency library also. @@ -1248,12 +1313,16 @@ function transformer(sInputFile, sOutputFile, sLibraryFile, vDependencyAPIFiles, fs.readFile(sPath, 'utf8', (oError, oFileData) => { if (!oError) { - oFileData = JSON.parse(oFileData); - if (oFileData.explored && oFileData.explored.entities && oFileData.explored.entities.length > 0) { - oChainObject.entitiesWithSamples = []; - oFileData.explored.entities.forEach((oEntity) => { - oChainObject.entitiesWithSamples.push(oEntity.id); - }); + try { + oFileData = JSON.parse(oFileData); + if (oFileData.explored && oFileData.explored.entities && oFileData.explored.entities.length > 0) { + oChainObject.entitiesWithSamples = []; + oFileData.explored.entities.forEach((oEntity) => { + oChainObject.entitiesWithSamples.push(oEntity.id); + }); + } + } catch (oParseError) { + log.error(`Failed to parse file "${sPath}": ${oParseError.message}`); } } // We always resolve as this data is not mandatory @@ -1273,9 +1342,17 @@ function transformer(sInputFile, sOutputFile, sLibraryFile, vDependencyAPIFiles, fs.readFile(sInputFile, 'utf8', (oError, sFileData) => { if (oError) { oReject(oError); - } else { + return; + } + try { oChainObject.fileData = JSON.parse(sFileData); oResolve(oChainObject); + } catch (oParseError) { + if (sFileData.trim() === "") { + oReject(new SyntaxError(`The file "${sInputFile}" is empty.`)); + } else { + oReject(new SyntaxError(`Failed to parse "${sInputFile}" as JSON: ${oParseError.message}`)); + } } }); }); @@ -1490,6 +1567,10 @@ function transformer(sInputFile, sOutputFile, sLibraryFile, vDependencyAPIFiles, return result; }, + formatExperimental: function (sExperimentalSince) { + return `Experimental since ${sExperimentalSince}`; + }, + /** * Formats the default value of the property as a string. * @param defaultValue - the default value of the property @@ -2414,9 +2495,23 @@ function transformer(sInputFile, sOutputFile, sLibraryFile, vDependencyAPIFiles, // Phone name - available only for parameters oProperty.phoneName = [aPhoneName.join("."), oProperty.name].join("."); - // Description (no deprecation etc. possible in JSDoc) + // Description + // Note: The "@since" information is not appended to the description here because it is + // rendered separately as a dedicated "Since" status in the SubApiDetail view. Appending it + // would duplicate the version information for typedef/enum properties. oProperty.description = formatters.formatDescription(oProperty.description); + // Experimental + if (oProperty.experimental) { + oProperty.experimentalText = formatters.formatExperimental(oProperty.experimental.since ?? oProperty.experimental); + } + + // Deprecated + if (oProperty.deprecated) { + oProperty.deprecatedText = formatters.formatDeprecated(oProperty.deprecated.since, + oProperty.deprecated.text); + } + // Add property to parameter array as we need a simple structure aProperties.push(oProperty); diff --git a/packages/builder/lib/processors/jsdoc/lib/ui5/plugin.js b/packages/builder/lib/processors/jsdoc/lib/ui5/plugin.js index e7e5f07901b..b2279dfd8fb 100644 --- a/packages/builder/lib/processors/jsdoc/lib/ui5/plugin.js +++ b/packages/builder/lib/processors/jsdoc/lib/ui5/plugin.js @@ -74,7 +74,9 @@ const logger = require('jsdoc/util/logger'); const pluginConfig = (env.conf && env.conf.templates && env.conf.templates.ui5) || env.opts.sapui5 || {}; const escope = require("escope"); const templatePath = path.getResourcePath(env.opts.template) || "./template"; -const {isSemVer} = require(`${templatePath}/utils/versionUtil`); +const {isSemVer, extractSince} = require(`${templatePath}/utils/versionUtil`); +const inline = require("jsdoc/tag/inline"); +const getASTNodeInfo = require("jsdoc/src/astnode").getInfo; /* ---- logging ---- */ @@ -1295,7 +1297,7 @@ function collectClassInfo(extendCall, classDoclet) { if (classInfoComment == null) { // If the class info object doesn't have a @lends tag, add it const raw = `/** @lends ${jsdocClassName}.prototype */`; - info(`lends comment injected for class '${jsdocClassName}'`); + debug(`lends comment injected for class '${jsdocClassName}'`); addLeadingCommentNode(classInfoNode, { type: "CommentBlock", raw, @@ -1480,13 +1482,18 @@ function collectClassInfo(extendCall, classDoclet) { } }; each(settings.parameters, "type", (pName, pSettings, pDoclet) => { + const optional = pName.endsWith("?"); + if (optional) { + pName = pName.slice(0, -1); + } info.parameters[pName] = { name : pName, doc : pDoclet && pDoclet.description, deprecation : pDoclet && pDoclet.deprecated, since : pDoclet && pDoclet.since, experimental : pDoclet && pDoclet.experimental, - type : pSettings && pSettings.type ? pSettings.type.value.value : "" + type : pSettings && pSettings.type ? pSettings.type.value.value : "", + optional }; }); }); @@ -1788,7 +1795,10 @@ function createAutoDoc(oClassInfo, classComment, doclet, node, parser, filename, lines = Array.prototype.concat.apply([], lines); // flatten lines = removeDuplicateEmptyLines(lines); lines.push("@synthetic"); - + if (doclet?.__ui5?.moduleOverride) { + const [moduleName, exportName] = doclet.__ui5.moduleOverride; + lines.push(`@ui5-module-override ${moduleName} ${exportName}`.trim()); + } const comment = " * " + lines.join("\r\n * "); jsdocCommentFound("/**\r\n" + comment + "\r\n */"); @@ -2353,8 +2363,11 @@ function createAutoDoc(oClassInfo, classComment, doclet, node, parser, filename, "@param {object} oControlEvent.getParameters" ]; for (const pName in info.parameters ) { + const optional = info.parameters[pName].optional; lines.push( - "@param {" + (info.parameters[pName].type || "") + "} oControlEvent.getParameters." + pName + " " + (info.parameters[pName].doc || "") + "@param {" + (info.parameters[pName].type || "") + "} " + + (optional ? "[" : "") + "oControlEvent.getParameters." + pName + (optional ? "]" : "") + + " " + (info.parameters[pName].doc || "") ); } lines.push(visibilityTags); @@ -2691,7 +2704,7 @@ exports.defineTags = function(dictionary) { mustHaveValue: true, onTagged: function(doclet, tag) { if (isSemVer(doclet.experimental)) { - error(`@since and @ui5-experimental-since must not be used both for the same API (${doclet.meta?.lineno})`); + warning(`@since and @ui5-experimental-since must not be used both for the same API (${doclet.meta?.lineno})`); } doclet.since = tag.value; } @@ -2717,7 +2730,6 @@ exports.defineTags = function(dictionary) { dictionary.defineTag('experimental', { // value is optional onTagged: function(doclet, tag) { - future(`As of version 1.139, the experimental tag is deprecated and should no longer be used. Use the @ui5-experimental-since tag instead (${doclet.meta?.lineno})`); if (isSemVer(doclet.experimental)) { error(`@experimental and @ui5-experimental-since must not be used both for the same API (${doclet.meta?.lineno})`); } @@ -2743,7 +2755,7 @@ exports.defineTags = function(dictionary) { mustHaveValue: true, onTagged: function(doclet, tag) { if (doclet.since || doclet.experimental) { - error(`@experimental/@since and @ui5-experimental-since must not be used together for the same API (${doclet.meta?.lineno})`); + warning(`@experimental/@since and @ui5-experimental-since must not be used together for the same API (${doclet.meta?.lineno})`); } const version = tag.value?.trim() ?? ""; if (!isSemVer(version)) { @@ -2932,6 +2944,77 @@ exports.defineTags = function(dictionary) { }); }; +const inlineTags = { + "ui5-experimental-since": { + onTagged(doclet, symbol, tag) { + if (symbol.since || symbol.experimental) { + error(`@experimental/@since and @ui5-experimental-since must not be used together for the same API entity (line:${doclet.meta?.lineno})`); + } + const version = tag.value?.trim() ?? ""; + if (!isSemVer(version)) { + error(`@ui5-experimental-since must have a version and only a version, not '${version}' (line:${doclet.meta?.lineno})`); + } + symbol.experimental = version; + } + }, + "since": { + onTagged(doclet, symbol, tag) { + if (isSemVer(symbol.experimental)) { + error(`@since and @ui5-experimental-since must not be used both for the same API (line:${doclet.meta?.lineno})`); + } + if (!isSemVer(tag.value)) { + error(`@since must have a version and only a version, not '${tag.value}' (line:${doclet.meta?.lineno})`); + } + symbol.since = tag.value; + } + }, + "deprecated": { + onTagged(doclet, symbol, tag) { + const info = extractSince(tag.value); + if (!info?.since) { + error(`@deprecated inline tag must include a version (line:${doclet.meta?.lineno})`); + } + if (!info?.value) { + error(`@deprecated inline tag must include a description (line:${doclet.meta?.lineno})`); + } + symbol.deprecated = tag.value; // raw string, parsed again in transformApiJson + } + } +}; + +function fixAndRemoveTagInfo(str, tagInfo) { + if (tagInfo.text.includes("{@")) { + const startTag = str.indexOf(tagInfo.completeTag); + if (startTag === -1) { + return str.replace(tagInfo.completeTag, ""); + } + const startText = str.indexOf(tagInfo.text, startTag); + let openBraces = 1; + let endTag = startText; + while (endTag < str.length) { + const c = str.charAt(endTag++); + if (c === "{") { + openBraces++; + } else if (c === "}") { + openBraces--; + if (openBraces === 0) { + break; + } + } + } + const endText = openBraces === 0 ? endTag - 1 : endTag; + tagInfo.completeTag = str.slice(startTag, endTag); + tagInfo.text = str.slice(startText, endText).trim(); + } + return str.replace(tagInfo.completeTag, ""); +} + +const inlineTagReplacers = Object.keys(inlineTags).reduce( + (result, key) => { + result[key] = fixAndRemoveTagInfo; + return result; + }, {}); + exports.handlers = { /** @@ -3053,6 +3136,46 @@ exports.handlers = { warning(`fixing return type 'this' with ${e.doclet.memberof}`); e.doclet.returns[0].type.names[0] = e.doclet.memberof; }*/ + + if (e.doclet.experimental + && !e.doclet.deprecated + && !isSemVer(e.doclet.experimental)) { + future(`As of version 1.139, the experimental tag is deprecated and should no longer be used. Use the @ui5-experimental-since tag instead (${e.doclet.meta?.lineno})`); + } + + // process UI5 inline tags in param tags + e.doclet.params?.forEach((param) => { + if (param.description) { + const result = inline.replaceInlineTags(param.description, inlineTagReplacers); + if (result.tags.length > 0) { + param.description = result.newString; + result.tags.forEach((tagInfo) => { + const tag = { + title: tagInfo.tag, + value: tagInfo.text + }; + inlineTags[tagInfo.tag].onTagged(e.doclet, param, tag); + }); + } + } + }); + + // process UI5 inline tags in @property tags (typedef) + e.doclet.properties?.forEach((prop) => { + if (prop.description) { + const result = inline.replaceInlineTags(prop.description, inlineTagReplacers); + if (result.tags.length > 0) { + prop.description = result.newString; + result.tags.forEach((tagInfo) => { + const tag = { + title: tagInfo.tag, + value: tagInfo.text + }; + inlineTags[tagInfo.tag].onTagged(e.doclet, prop, tag); + }); + } + } + }); }, beforeParse : function(e) { @@ -3285,7 +3408,11 @@ exports.astNodeVisitor = { function processPotentialEnum(literal, comment) { const values = literal.properties.reduce((map, prop) => { - map[getPropertyKey(prop)] = convertValue(prop.value); + // use the JSDoc API to determine a name from the property's key + // this is necessary to get the same enumID as in the later step where + // name info from a JSDoc doclet is used to build the enumID + const key = getASTNodeInfo(prop).name; + map[key] = convertValue(prop.value); return map; }, Object.create(null)); // determine unique enum ID from key set @@ -3551,6 +3678,23 @@ exports.astNodeVisitor = { tagInfo.type = getTypeStrings(tagInfo.parsedType); // console.info("new parse result", tagInfo.type); } + // #### BEGIN: MODIFIED BY SAP + if (tagInfo && tagInfo.name && /^\[|=/.test(tagInfo.name)) { + // debug("tag info before post-processing the name-portion", tagInfo); + let name = tagInfo.name; + if (name.startsWith("[") && name.endsWith("]")) { + name = name.slice(1,-1).trim(); + tagInfo.optional = true; + } + const p = name.indexOf("="); + if (p > 0) { // intentionaly checks for != 0 (no empty name allowed) + tagInfo.defaultvalue = name.slice(p + 1).trim(); + name = name.slice(0, p).trim(); + } + tagInfo.name = name; + // debug("tag info after post-processing the name-portion", tagInfo); + } + // #### END: MODIFIED BY SAP return tagInfo; }; }()); diff --git a/packages/builder/lib/processors/jsdoc/lib/ui5/template/publish.js b/packages/builder/lib/processors/jsdoc/lib/ui5/template/publish.js index 98ad45f43ee..e1054e84846 100644 --- a/packages/builder/lib/processors/jsdoc/lib/ui5/template/publish.js +++ b/packages/builder/lib/processors/jsdoc/lib/ui5/template/publish.js @@ -120,6 +120,9 @@ function loadExternalSymbols(apiJsonFolder) { const file = path.join(templateConf.apiJsonFolder, localFileName); try { const sJSON = fs.readFileSync(file, 'UTF-8'); + if (sJSON.trim() === "") { + throw new SyntaxError(`The file is empty.`); + } const data = JSON.parse(sJSON); if ( !Array.isArray(data.symbols) ) { throw new TypeError("api.json does not contain a 'symbols' array"); @@ -1084,7 +1087,7 @@ function createAPIJSON4Symbol(symbol, omitDefaults) { future(`**** Failed to parse version in string '${value}'. ` + `Version might be missing or has an unexpected format.`); } - if ( info.value ) { + if ( info.value || (info.value === "" && name === "experimental")) { curr["text"] = normalizeWS(info.value); } closeTag(name, true); @@ -1394,6 +1397,9 @@ function createAPIJSON4Symbol(symbol, omitDefaults) { tag(pn); attrib("name", pn); attrib("type", param.type); + if (param.optional) { + attrib("optional", !!param.optional, false, /* raw = */true); + } attribSince(param.since); tag("description", normalizeWS(param.doc), true); tagWithSince("experimental", param.experimental); @@ -1531,9 +1537,9 @@ function createAPIJSON4Symbol(symbol, omitDefaults) { stakeholderList("allowedFor", stakeholders(symbol)); } - // as JSDoc does not allow tags for other tags, no @deprecated etc. can be supported - // tagWithSince("experimental", properties[i].experimental); - // tagWithSince("deprecated", properties[i].deprecated); + attribSince(properties[i].since); + tagWithSince("experimental", properties[i].experimental); + tagWithSince("deprecated", properties[i].deprecated); closeTag(name); } @@ -1838,6 +1844,9 @@ function createAPIJSON4Symbol(symbol, omitDefaults) { stakeholderList("allowedFor", stakeholders(symbol)); } tag("description", normalizeWS(prop.description), true); + attribSince(prop.since); + tagWithSince("experimental", prop.experimental); + tagWithSince("deprecated", prop.deprecated); closeTag("property"); }); endCollection("properties"); @@ -1918,6 +1927,9 @@ function createAPIJSON4Symbol(symbol, omitDefaults) { tag("parameter"); attrib("name", param.name); attrib("type", listTypes(param.type)); + if (param.optional) { + attrib("optional", param.optional, false, /* raw = */ true); + } if ( param.since ) { attrib("since", extractVersion(param.since)); } @@ -2257,6 +2269,8 @@ const builtinTypes = { Boolean:true, Date:true, Error:true, + GeneratorFunction: true, + Iterator: true, Map:true, Number:true, String:true, @@ -2288,11 +2302,14 @@ const builtinTypes = { HTMLElement:true, Node:true, PerformanceResourceTiming:true, + ReadableStream: true, Request:true, Response:true, Storage:true, Touch:true, TouchList:true, + URL: true, + URLSearchParams: true, Window:true, XMLDocument:true diff --git a/packages/builder/lib/processors/jsdoc/lib/ui5/template/utils/versionUtil.js b/packages/builder/lib/processors/jsdoc/lib/ui5/template/utils/versionUtil.js index 8623a230b69..5a6644928c2 100644 --- a/packages/builder/lib/processors/jsdoc/lib/ui5/template/utils/versionUtil.js +++ b/packages/builder/lib/processors/jsdoc/lib/ui5/template/utils/versionUtil.js @@ -78,10 +78,11 @@ const rSinceIndicator = /^(?:as\s+of|since)(?:\s+version)?\s*/i; * * * @param {string|boolean} value + * @param {boolean} [keepEmptyText=false] * @returns {{pos: number, value: string, since: string|null|undefined}} * undefined: value is falsy */ -function extractSince(value) { +function extractSince(value, keepEmptyText = false) { if ( !value ) { return undefined; @@ -97,7 +98,7 @@ function extractSince(value) { return { since: value, pos: value.length, - value: "" + value: keepEmptyText ? "" : null }; }