11cb0ef41Sopenharmony_ci// Copyright Joyent, Inc. and other Node contributors. 21cb0ef41Sopenharmony_ci// 31cb0ef41Sopenharmony_ci// Permission is hereby granted, free of charge, to any person obtaining a 41cb0ef41Sopenharmony_ci// copy of this software and associated documentation files (the 51cb0ef41Sopenharmony_ci// "Software"), to deal in the Software without restriction, including 61cb0ef41Sopenharmony_ci// without limitation the rights to use, copy, modify, merge, publish, 71cb0ef41Sopenharmony_ci// distribute, sublicense, and/or sell copies of the Software, and to permit 81cb0ef41Sopenharmony_ci// persons to whom the Software is furnished to do so, subject to the 91cb0ef41Sopenharmony_ci// following conditions: 101cb0ef41Sopenharmony_ci// 111cb0ef41Sopenharmony_ci// The above copyright notice and this permission notice shall be included 121cb0ef41Sopenharmony_ci// in all copies or substantial portions of the Software. 131cb0ef41Sopenharmony_ci// 141cb0ef41Sopenharmony_ci// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 151cb0ef41Sopenharmony_ci// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 161cb0ef41Sopenharmony_ci// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 171cb0ef41Sopenharmony_ci// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 181cb0ef41Sopenharmony_ci// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 191cb0ef41Sopenharmony_ci// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 201cb0ef41Sopenharmony_ci// USE OR OTHER DEALINGS IN THE SOFTWARE. 211cb0ef41Sopenharmony_ci 221cb0ef41Sopenharmony_ciimport html from 'remark-html'; 231cb0ef41Sopenharmony_ciimport { unified } from 'unified'; 241cb0ef41Sopenharmony_ciimport { selectAll } from 'unist-util-select'; 251cb0ef41Sopenharmony_ci 261cb0ef41Sopenharmony_ciimport * as common from './common.mjs'; 271cb0ef41Sopenharmony_ci 281cb0ef41Sopenharmony_ci// Unified processor: input is https://github.com/syntax-tree/mdast, 291cb0ef41Sopenharmony_ci// output is: https://gist.github.com/1777387. 301cb0ef41Sopenharmony_ciexport function jsonAPI({ filename }) { 311cb0ef41Sopenharmony_ci return (tree, file) => { 321cb0ef41Sopenharmony_ci 331cb0ef41Sopenharmony_ci const exampleHeading = /^example/i; 341cb0ef41Sopenharmony_ci const metaExpr = /<!--([^=]+)=([^-]+)-->\n*/g; 351cb0ef41Sopenharmony_ci const stabilityExpr = /^Stability: ([0-5])(?:\s*-\s*)?(.*)$/s; 361cb0ef41Sopenharmony_ci 371cb0ef41Sopenharmony_ci // Extract definitions. 381cb0ef41Sopenharmony_ci const definitions = selectAll('definition', tree); 391cb0ef41Sopenharmony_ci 401cb0ef41Sopenharmony_ci // Determine the start, stop, and depth of each section. 411cb0ef41Sopenharmony_ci const sections = []; 421cb0ef41Sopenharmony_ci let section = null; 431cb0ef41Sopenharmony_ci tree.children.forEach((node, i) => { 441cb0ef41Sopenharmony_ci if (node.type === 'heading' && 451cb0ef41Sopenharmony_ci !exampleHeading.test(textJoin(node.children, file))) { 461cb0ef41Sopenharmony_ci if (section) section.stop = i - 1; 471cb0ef41Sopenharmony_ci section = { start: i, stop: tree.children.length, depth: node.depth }; 481cb0ef41Sopenharmony_ci sections.push(section); 491cb0ef41Sopenharmony_ci } 501cb0ef41Sopenharmony_ci }); 511cb0ef41Sopenharmony_ci 521cb0ef41Sopenharmony_ci // Collect and capture results. 531cb0ef41Sopenharmony_ci const result = { type: 'module', source: filename }; 541cb0ef41Sopenharmony_ci while (sections.length > 0) { 551cb0ef41Sopenharmony_ci doSection(sections.shift(), result); 561cb0ef41Sopenharmony_ci } 571cb0ef41Sopenharmony_ci file.json = result; 581cb0ef41Sopenharmony_ci 591cb0ef41Sopenharmony_ci // Process a single section (recursively, including subsections). 601cb0ef41Sopenharmony_ci function doSection(section, parent) { 611cb0ef41Sopenharmony_ci if (section.depth - parent.depth > 1) { 621cb0ef41Sopenharmony_ci throw new Error('Inappropriate heading level\n' + 631cb0ef41Sopenharmony_ci JSON.stringify(section)); 641cb0ef41Sopenharmony_ci } 651cb0ef41Sopenharmony_ci 661cb0ef41Sopenharmony_ci const current = newSection(tree.children[section.start], file); 671cb0ef41Sopenharmony_ci let nodes = tree.children.slice(section.start + 1, section.stop + 1); 681cb0ef41Sopenharmony_ci 691cb0ef41Sopenharmony_ci // Sometimes we have two headings with a single blob of description. 701cb0ef41Sopenharmony_ci // Treat as a clone. 711cb0ef41Sopenharmony_ci if ( 721cb0ef41Sopenharmony_ci nodes.length === 0 && sections.length > 0 && 731cb0ef41Sopenharmony_ci section.depth === sections[0].depth 741cb0ef41Sopenharmony_ci ) { 751cb0ef41Sopenharmony_ci nodes = tree.children.slice(sections[0].start + 1, 761cb0ef41Sopenharmony_ci sections[0].stop + 1); 771cb0ef41Sopenharmony_ci } 781cb0ef41Sopenharmony_ci 791cb0ef41Sopenharmony_ci // Extract (and remove) metadata that is not directly inferable 801cb0ef41Sopenharmony_ci // from the markdown itself. 811cb0ef41Sopenharmony_ci nodes.forEach((node, i) => { 821cb0ef41Sopenharmony_ci // Input: <!-- name=module -->; output: {name: module}. 831cb0ef41Sopenharmony_ci if (node.type === 'html') { 841cb0ef41Sopenharmony_ci node.value = node.value.replace(metaExpr, (_0, key, value) => { 851cb0ef41Sopenharmony_ci current[key.trim()] = value.trim(); 861cb0ef41Sopenharmony_ci return ''; 871cb0ef41Sopenharmony_ci }); 881cb0ef41Sopenharmony_ci if (!node.value.trim()) delete nodes[i]; 891cb0ef41Sopenharmony_ci } 901cb0ef41Sopenharmony_ci 911cb0ef41Sopenharmony_ci // Process metadata: 921cb0ef41Sopenharmony_ci // <!-- YAML 931cb0ef41Sopenharmony_ci // added: v1.0.0 941cb0ef41Sopenharmony_ci // --> 951cb0ef41Sopenharmony_ci if (node.type === 'html' && common.isYAMLBlock(node.value)) { 961cb0ef41Sopenharmony_ci current.meta = common.extractAndParseYAML(node.value); 971cb0ef41Sopenharmony_ci delete nodes[i]; 981cb0ef41Sopenharmony_ci } 991cb0ef41Sopenharmony_ci 1001cb0ef41Sopenharmony_ci // Stability marker: > Stability: ... 1011cb0ef41Sopenharmony_ci if ( 1021cb0ef41Sopenharmony_ci node.type === 'blockquote' && node.children.length === 1 && 1031cb0ef41Sopenharmony_ci node.children[0].type === 'paragraph' && 1041cb0ef41Sopenharmony_ci nodes.slice(0, i).every((node) => node.type === 'list') 1051cb0ef41Sopenharmony_ci ) { 1061cb0ef41Sopenharmony_ci const text = textJoin(node.children[0].children, file); 1071cb0ef41Sopenharmony_ci const stability = stabilityExpr.exec(text); 1081cb0ef41Sopenharmony_ci if (stability) { 1091cb0ef41Sopenharmony_ci current.stability = parseInt(stability[1], 10); 1101cb0ef41Sopenharmony_ci current.stabilityText = stability[2].replaceAll('\n', ' ').trim(); 1111cb0ef41Sopenharmony_ci delete nodes[i]; 1121cb0ef41Sopenharmony_ci } 1131cb0ef41Sopenharmony_ci } 1141cb0ef41Sopenharmony_ci }); 1151cb0ef41Sopenharmony_ci 1161cb0ef41Sopenharmony_ci // Compress the node array. 1171cb0ef41Sopenharmony_ci nodes = nodes.filter(() => true); 1181cb0ef41Sopenharmony_ci 1191cb0ef41Sopenharmony_ci // If the first node is a list, extract it. 1201cb0ef41Sopenharmony_ci const list = nodes[0] && nodes[0].type === 'list' ? 1211cb0ef41Sopenharmony_ci nodes.shift() : null; 1221cb0ef41Sopenharmony_ci 1231cb0ef41Sopenharmony_ci // Now figure out what this list actually means. 1241cb0ef41Sopenharmony_ci // Depending on the section type, the list could be different things. 1251cb0ef41Sopenharmony_ci const values = list ? 1261cb0ef41Sopenharmony_ci list.children.map((child) => parseListItem(child, file)) : []; 1271cb0ef41Sopenharmony_ci 1281cb0ef41Sopenharmony_ci switch (current.type) { 1291cb0ef41Sopenharmony_ci case 'ctor': 1301cb0ef41Sopenharmony_ci case 'classMethod': 1311cb0ef41Sopenharmony_ci case 'method': { 1321cb0ef41Sopenharmony_ci // Each item is an argument, unless the name is 'return', 1331cb0ef41Sopenharmony_ci // in which case it's the return value. 1341cb0ef41Sopenharmony_ci const sig = {}; 1351cb0ef41Sopenharmony_ci sig.params = values.filter((value) => { 1361cb0ef41Sopenharmony_ci if (value.name === 'return') { 1371cb0ef41Sopenharmony_ci sig.return = value; 1381cb0ef41Sopenharmony_ci return false; 1391cb0ef41Sopenharmony_ci } 1401cb0ef41Sopenharmony_ci return true; 1411cb0ef41Sopenharmony_ci }); 1421cb0ef41Sopenharmony_ci parseSignature(current.textRaw, sig); 1431cb0ef41Sopenharmony_ci current.signatures = [sig]; 1441cb0ef41Sopenharmony_ci break; 1451cb0ef41Sopenharmony_ci } 1461cb0ef41Sopenharmony_ci case 'property': 1471cb0ef41Sopenharmony_ci // There should be only one item, which is the value. 1481cb0ef41Sopenharmony_ci // Copy the data up to the section. 1491cb0ef41Sopenharmony_ci if (values.length) { 1501cb0ef41Sopenharmony_ci const signature = values[0]; 1511cb0ef41Sopenharmony_ci 1521cb0ef41Sopenharmony_ci // Shove the name in there for properties, 1531cb0ef41Sopenharmony_ci // since they are always just going to be the value etc. 1541cb0ef41Sopenharmony_ci signature.textRaw = `\`${current.name}\` ${signature.textRaw}`; 1551cb0ef41Sopenharmony_ci 1561cb0ef41Sopenharmony_ci for (const key in signature) { 1571cb0ef41Sopenharmony_ci if (signature[key]) { 1581cb0ef41Sopenharmony_ci if (key === 'type') { 1591cb0ef41Sopenharmony_ci current.typeof = signature.type; 1601cb0ef41Sopenharmony_ci } else { 1611cb0ef41Sopenharmony_ci current[key] = signature[key]; 1621cb0ef41Sopenharmony_ci } 1631cb0ef41Sopenharmony_ci } 1641cb0ef41Sopenharmony_ci } 1651cb0ef41Sopenharmony_ci } 1661cb0ef41Sopenharmony_ci break; 1671cb0ef41Sopenharmony_ci 1681cb0ef41Sopenharmony_ci case 'event': 1691cb0ef41Sopenharmony_ci // Event: each item is an argument. 1701cb0ef41Sopenharmony_ci current.params = values; 1711cb0ef41Sopenharmony_ci break; 1721cb0ef41Sopenharmony_ci 1731cb0ef41Sopenharmony_ci default: 1741cb0ef41Sopenharmony_ci // If list wasn't consumed, put it back in the nodes list. 1751cb0ef41Sopenharmony_ci if (list) nodes.unshift(list); 1761cb0ef41Sopenharmony_ci } 1771cb0ef41Sopenharmony_ci 1781cb0ef41Sopenharmony_ci // Convert remaining nodes to a 'desc'. 1791cb0ef41Sopenharmony_ci // Unified expects to process a string; but we ignore that as we 1801cb0ef41Sopenharmony_ci // already have pre-parsed input that we can inject. 1811cb0ef41Sopenharmony_ci if (nodes.length) { 1821cb0ef41Sopenharmony_ci if (current.desc) current.shortDesc = current.desc; 1831cb0ef41Sopenharmony_ci 1841cb0ef41Sopenharmony_ci current.desc = unified() 1851cb0ef41Sopenharmony_ci .use(function() { 1861cb0ef41Sopenharmony_ci this.Parser = () => ( 1871cb0ef41Sopenharmony_ci { type: 'root', children: nodes.concat(definitions) } 1881cb0ef41Sopenharmony_ci ); 1891cb0ef41Sopenharmony_ci }) 1901cb0ef41Sopenharmony_ci .use(html, { sanitize: false }) 1911cb0ef41Sopenharmony_ci .processSync('').toString().trim(); 1921cb0ef41Sopenharmony_ci if (!current.desc) delete current.desc; 1931cb0ef41Sopenharmony_ci } 1941cb0ef41Sopenharmony_ci 1951cb0ef41Sopenharmony_ci // Process subsections. 1961cb0ef41Sopenharmony_ci while (sections.length > 0 && sections[0].depth > section.depth) { 1971cb0ef41Sopenharmony_ci doSection(sections.shift(), current); 1981cb0ef41Sopenharmony_ci } 1991cb0ef41Sopenharmony_ci 2001cb0ef41Sopenharmony_ci // If type is not set, default type based on parent type, and 2011cb0ef41Sopenharmony_ci // set displayName and name properties. 2021cb0ef41Sopenharmony_ci if (!current.type) { 2031cb0ef41Sopenharmony_ci current.type = (parent.type === 'misc' ? 'misc' : 'module'); 2041cb0ef41Sopenharmony_ci current.displayName = current.name; 2051cb0ef41Sopenharmony_ci current.name = current.name.toLowerCase() 2061cb0ef41Sopenharmony_ci .trim().replace(/\s+/g, '_'); 2071cb0ef41Sopenharmony_ci } 2081cb0ef41Sopenharmony_ci 2091cb0ef41Sopenharmony_ci // Pluralize type to determine which 'bucket' to put this section in. 2101cb0ef41Sopenharmony_ci let plur; 2111cb0ef41Sopenharmony_ci if (current.type.slice(-1) === 's') { 2121cb0ef41Sopenharmony_ci plur = `${current.type}es`; 2131cb0ef41Sopenharmony_ci } else if (current.type.slice(-1) === 'y') { 2141cb0ef41Sopenharmony_ci plur = current.type.replace(/y$/, 'ies'); 2151cb0ef41Sopenharmony_ci } else { 2161cb0ef41Sopenharmony_ci plur = `${current.type}s`; 2171cb0ef41Sopenharmony_ci } 2181cb0ef41Sopenharmony_ci 2191cb0ef41Sopenharmony_ci // Classes sometimes have various 'ctor' children 2201cb0ef41Sopenharmony_ci // which are actually just descriptions of a constructor class signature. 2211cb0ef41Sopenharmony_ci // Merge them into the parent. 2221cb0ef41Sopenharmony_ci if (current.type === 'class' && current.ctors) { 2231cb0ef41Sopenharmony_ci current.signatures = current.signatures || []; 2241cb0ef41Sopenharmony_ci const sigs = current.signatures; 2251cb0ef41Sopenharmony_ci current.ctors.forEach((ctor) => { 2261cb0ef41Sopenharmony_ci ctor.signatures = ctor.signatures || [{}]; 2271cb0ef41Sopenharmony_ci ctor.signatures.forEach((sig) => { 2281cb0ef41Sopenharmony_ci sig.desc = ctor.desc; 2291cb0ef41Sopenharmony_ci }); 2301cb0ef41Sopenharmony_ci sigs.push(...ctor.signatures); 2311cb0ef41Sopenharmony_ci }); 2321cb0ef41Sopenharmony_ci delete current.ctors; 2331cb0ef41Sopenharmony_ci } 2341cb0ef41Sopenharmony_ci 2351cb0ef41Sopenharmony_ci // Properties are a bit special. 2361cb0ef41Sopenharmony_ci // Their "type" is the type of object, not "property". 2371cb0ef41Sopenharmony_ci if (current.type === 'property') { 2381cb0ef41Sopenharmony_ci if (current.typeof) { 2391cb0ef41Sopenharmony_ci current.type = current.typeof; 2401cb0ef41Sopenharmony_ci delete current.typeof; 2411cb0ef41Sopenharmony_ci } else { 2421cb0ef41Sopenharmony_ci delete current.type; 2431cb0ef41Sopenharmony_ci } 2441cb0ef41Sopenharmony_ci } 2451cb0ef41Sopenharmony_ci 2461cb0ef41Sopenharmony_ci // If the parent's type is 'misc', then it's just a random 2471cb0ef41Sopenharmony_ci // collection of stuff, like the "globals" section. 2481cb0ef41Sopenharmony_ci // Make the children top-level items. 2491cb0ef41Sopenharmony_ci if (current.type === 'misc') { 2501cb0ef41Sopenharmony_ci Object.keys(current).forEach((key) => { 2511cb0ef41Sopenharmony_ci switch (key) { 2521cb0ef41Sopenharmony_ci case 'textRaw': 2531cb0ef41Sopenharmony_ci case 'name': 2541cb0ef41Sopenharmony_ci case 'type': 2551cb0ef41Sopenharmony_ci case 'desc': 2561cb0ef41Sopenharmony_ci case 'miscs': 2571cb0ef41Sopenharmony_ci return; 2581cb0ef41Sopenharmony_ci default: 2591cb0ef41Sopenharmony_ci if (parent.type === 'misc') { 2601cb0ef41Sopenharmony_ci return; 2611cb0ef41Sopenharmony_ci } 2621cb0ef41Sopenharmony_ci if (parent[key] && Array.isArray(parent[key])) { 2631cb0ef41Sopenharmony_ci parent[key] = parent[key].concat(current[key]); 2641cb0ef41Sopenharmony_ci } else if (!parent[key]) { 2651cb0ef41Sopenharmony_ci parent[key] = current[key]; 2661cb0ef41Sopenharmony_ci } 2671cb0ef41Sopenharmony_ci } 2681cb0ef41Sopenharmony_ci }); 2691cb0ef41Sopenharmony_ci } 2701cb0ef41Sopenharmony_ci 2711cb0ef41Sopenharmony_ci // Add this section to the parent. Sometimes we have two headings with a 2721cb0ef41Sopenharmony_ci // single blob of description. If the preceding entry at this level 2731cb0ef41Sopenharmony_ci // shares a name and is lacking a description, copy it backwards. 2741cb0ef41Sopenharmony_ci if (!parent[plur]) parent[plur] = []; 2751cb0ef41Sopenharmony_ci const prev = parent[plur].slice(-1)[0]; 2761cb0ef41Sopenharmony_ci if (prev && prev.name === current.name && !prev.desc) { 2771cb0ef41Sopenharmony_ci prev.desc = current.desc; 2781cb0ef41Sopenharmony_ci } 2791cb0ef41Sopenharmony_ci parent[plur].push(current); 2801cb0ef41Sopenharmony_ci } 2811cb0ef41Sopenharmony_ci }; 2821cb0ef41Sopenharmony_ci} 2831cb0ef41Sopenharmony_ci 2841cb0ef41Sopenharmony_ci 2851cb0ef41Sopenharmony_ciconst paramExpr = /\((.+)\);?$/; 2861cb0ef41Sopenharmony_ci 2871cb0ef41Sopenharmony_ci// text: "someobject.someMethod(a[, b=100][, c])" 2881cb0ef41Sopenharmony_cifunction parseSignature(text, sig) { 2891cb0ef41Sopenharmony_ci const list = []; 2901cb0ef41Sopenharmony_ci 2911cb0ef41Sopenharmony_ci let [, sigParams] = text.match(paramExpr) || []; 2921cb0ef41Sopenharmony_ci if (!sigParams) return; 2931cb0ef41Sopenharmony_ci sigParams = sigParams.split(','); 2941cb0ef41Sopenharmony_ci let optionalLevel = 0; 2951cb0ef41Sopenharmony_ci const optionalCharDict = { '[': 1, ' ': 0, ']': -1 }; 2961cb0ef41Sopenharmony_ci sigParams.forEach((sigParam, i) => { 2971cb0ef41Sopenharmony_ci sigParam = sigParam.trim(); 2981cb0ef41Sopenharmony_ci if (!sigParam) { 2991cb0ef41Sopenharmony_ci throw new Error(`Empty parameter slot: ${text}`); 3001cb0ef41Sopenharmony_ci } 3011cb0ef41Sopenharmony_ci let listParam = sig.params[i]; 3021cb0ef41Sopenharmony_ci let optional = false; 3031cb0ef41Sopenharmony_ci let defaultValue; 3041cb0ef41Sopenharmony_ci 3051cb0ef41Sopenharmony_ci // For grouped optional params such as someMethod(a[, b[, c]]). 3061cb0ef41Sopenharmony_ci let pos; 3071cb0ef41Sopenharmony_ci for (pos = 0; pos < sigParam.length; pos++) { 3081cb0ef41Sopenharmony_ci const levelChange = optionalCharDict[sigParam[pos]]; 3091cb0ef41Sopenharmony_ci if (levelChange === undefined) break; 3101cb0ef41Sopenharmony_ci optionalLevel += levelChange; 3111cb0ef41Sopenharmony_ci } 3121cb0ef41Sopenharmony_ci sigParam = sigParam.substring(pos); 3131cb0ef41Sopenharmony_ci optional = (optionalLevel > 0); 3141cb0ef41Sopenharmony_ci for (pos = sigParam.length - 1; pos >= 0; pos--) { 3151cb0ef41Sopenharmony_ci const levelChange = optionalCharDict[sigParam[pos]]; 3161cb0ef41Sopenharmony_ci if (levelChange === undefined) break; 3171cb0ef41Sopenharmony_ci optionalLevel += levelChange; 3181cb0ef41Sopenharmony_ci } 3191cb0ef41Sopenharmony_ci sigParam = sigParam.substring(0, pos + 1); 3201cb0ef41Sopenharmony_ci 3211cb0ef41Sopenharmony_ci const eq = sigParam.indexOf('='); 3221cb0ef41Sopenharmony_ci if (eq !== -1) { 3231cb0ef41Sopenharmony_ci defaultValue = sigParam.substr(eq + 1); 3241cb0ef41Sopenharmony_ci sigParam = sigParam.substr(0, eq); 3251cb0ef41Sopenharmony_ci } 3261cb0ef41Sopenharmony_ci 3271cb0ef41Sopenharmony_ci // At this point, the name should match. If it doesn't find one that does. 3281cb0ef41Sopenharmony_ci // Example: shared signatures for: 3291cb0ef41Sopenharmony_ci // ### new Console(stdout[, stderr][, ignoreErrors]) 3301cb0ef41Sopenharmony_ci // ### new Console(options) 3311cb0ef41Sopenharmony_ci if (!listParam || sigParam !== listParam.name) { 3321cb0ef41Sopenharmony_ci listParam = null; 3331cb0ef41Sopenharmony_ci for (const param of sig.params) { 3341cb0ef41Sopenharmony_ci if (param.name === sigParam) { 3351cb0ef41Sopenharmony_ci listParam = param; 3361cb0ef41Sopenharmony_ci } else if (param.options) { 3371cb0ef41Sopenharmony_ci for (const option of param.options) { 3381cb0ef41Sopenharmony_ci if (option.name === sigParam) { 3391cb0ef41Sopenharmony_ci listParam = Object.assign({}, option); 3401cb0ef41Sopenharmony_ci } 3411cb0ef41Sopenharmony_ci } 3421cb0ef41Sopenharmony_ci } 3431cb0ef41Sopenharmony_ci } 3441cb0ef41Sopenharmony_ci 3451cb0ef41Sopenharmony_ci if (!listParam) { 3461cb0ef41Sopenharmony_ci if (sigParam.startsWith('...')) { 3471cb0ef41Sopenharmony_ci listParam = { name: sigParam }; 3481cb0ef41Sopenharmony_ci } else { 3491cb0ef41Sopenharmony_ci throw new Error( 3501cb0ef41Sopenharmony_ci `Invalid param "${sigParam}"\n` + 3511cb0ef41Sopenharmony_ci ` > ${JSON.stringify(listParam)}\n` + 3521cb0ef41Sopenharmony_ci ` > ${text}`, 3531cb0ef41Sopenharmony_ci ); 3541cb0ef41Sopenharmony_ci } 3551cb0ef41Sopenharmony_ci } 3561cb0ef41Sopenharmony_ci } 3571cb0ef41Sopenharmony_ci 3581cb0ef41Sopenharmony_ci if (optional) listParam.optional = true; 3591cb0ef41Sopenharmony_ci if (defaultValue !== undefined) listParam.default = defaultValue.trim(); 3601cb0ef41Sopenharmony_ci 3611cb0ef41Sopenharmony_ci list.push(listParam); 3621cb0ef41Sopenharmony_ci }); 3631cb0ef41Sopenharmony_ci 3641cb0ef41Sopenharmony_ci sig.params = list; 3651cb0ef41Sopenharmony_ci} 3661cb0ef41Sopenharmony_ci 3671cb0ef41Sopenharmony_ci 3681cb0ef41Sopenharmony_ciconst returnExpr = /^returns?\s*:?\s*/i; 3691cb0ef41Sopenharmony_ciconst nameExpr = /^['`"]?([^'`": {]+)['`"]?\s*:?\s*/; 3701cb0ef41Sopenharmony_ciconst typeExpr = /^\{([^}]+)\}\s*/; 3711cb0ef41Sopenharmony_ciconst leadingHyphen = /^-\s*/; 3721cb0ef41Sopenharmony_ciconst defaultExpr = /\s*\*\*Default:\*\*\s*([^]+)$/i; 3731cb0ef41Sopenharmony_ci 3741cb0ef41Sopenharmony_cifunction parseListItem(item, file) { 3751cb0ef41Sopenharmony_ci const current = {}; 3761cb0ef41Sopenharmony_ci 3771cb0ef41Sopenharmony_ci current.textRaw = item.children.filter((node) => node.type !== 'list') 3781cb0ef41Sopenharmony_ci .map((node) => ( 3791cb0ef41Sopenharmony_ci file.value.slice(node.position.start.offset, node.position.end.offset)), 3801cb0ef41Sopenharmony_ci ) 3811cb0ef41Sopenharmony_ci .join('').replace(/\s+/g, ' ').replace(/<!--.*?-->/sg, ''); 3821cb0ef41Sopenharmony_ci let text = current.textRaw; 3831cb0ef41Sopenharmony_ci 3841cb0ef41Sopenharmony_ci if (!text) { 3851cb0ef41Sopenharmony_ci throw new Error(`Empty list item: ${JSON.stringify(item)}`); 3861cb0ef41Sopenharmony_ci } 3871cb0ef41Sopenharmony_ci 3881cb0ef41Sopenharmony_ci // The goal here is to find the name, type, default. 3891cb0ef41Sopenharmony_ci // Anything left over is 'desc'. 3901cb0ef41Sopenharmony_ci 3911cb0ef41Sopenharmony_ci if (returnExpr.test(text)) { 3921cb0ef41Sopenharmony_ci current.name = 'return'; 3931cb0ef41Sopenharmony_ci text = text.replace(returnExpr, ''); 3941cb0ef41Sopenharmony_ci } else { 3951cb0ef41Sopenharmony_ci const [, name] = text.match(nameExpr) || []; 3961cb0ef41Sopenharmony_ci if (name) { 3971cb0ef41Sopenharmony_ci current.name = name; 3981cb0ef41Sopenharmony_ci text = text.replace(nameExpr, ''); 3991cb0ef41Sopenharmony_ci } 4001cb0ef41Sopenharmony_ci } 4011cb0ef41Sopenharmony_ci 4021cb0ef41Sopenharmony_ci const [, type] = text.match(typeExpr) || []; 4031cb0ef41Sopenharmony_ci if (type) { 4041cb0ef41Sopenharmony_ci current.type = type; 4051cb0ef41Sopenharmony_ci text = text.replace(typeExpr, ''); 4061cb0ef41Sopenharmony_ci } 4071cb0ef41Sopenharmony_ci 4081cb0ef41Sopenharmony_ci text = text.replace(leadingHyphen, ''); 4091cb0ef41Sopenharmony_ci 4101cb0ef41Sopenharmony_ci const [, defaultValue] = text.match(defaultExpr) || []; 4111cb0ef41Sopenharmony_ci if (defaultValue) { 4121cb0ef41Sopenharmony_ci current.default = defaultValue.replace(/\.$/, ''); 4131cb0ef41Sopenharmony_ci text = text.replace(defaultExpr, ''); 4141cb0ef41Sopenharmony_ci } 4151cb0ef41Sopenharmony_ci 4161cb0ef41Sopenharmony_ci if (text) current.desc = text; 4171cb0ef41Sopenharmony_ci 4181cb0ef41Sopenharmony_ci const options = item.children.find((child) => child.type === 'list'); 4191cb0ef41Sopenharmony_ci if (options) { 4201cb0ef41Sopenharmony_ci current.options = options.children.map((child) => ( 4211cb0ef41Sopenharmony_ci parseListItem(child, file) 4221cb0ef41Sopenharmony_ci )); 4231cb0ef41Sopenharmony_ci } 4241cb0ef41Sopenharmony_ci 4251cb0ef41Sopenharmony_ci return current; 4261cb0ef41Sopenharmony_ci} 4271cb0ef41Sopenharmony_ci 4281cb0ef41Sopenharmony_ci// This section parses out the contents of an H# tag. 4291cb0ef41Sopenharmony_ci 4301cb0ef41Sopenharmony_ci// To reduce escape slashes in RegExp string components. 4311cb0ef41Sopenharmony_ciconst r = String.raw; 4321cb0ef41Sopenharmony_ci 4331cb0ef41Sopenharmony_ciconst eventPrefix = '^Event: +'; 4341cb0ef41Sopenharmony_ciconst classPrefix = '^[Cc]lass: +'; 4351cb0ef41Sopenharmony_ciconst ctorPrefix = '^(?:[Cc]onstructor: +)?`?new +'; 4361cb0ef41Sopenharmony_ciconst classMethodPrefix = '^Static method: +'; 4371cb0ef41Sopenharmony_ciconst maybeClassPropertyPrefix = '(?:Class property: +)?'; 4381cb0ef41Sopenharmony_ci 4391cb0ef41Sopenharmony_ciconst maybeQuote = '[\'"]?'; 4401cb0ef41Sopenharmony_ciconst notQuotes = '[^\'"]+'; 4411cb0ef41Sopenharmony_ci 4421cb0ef41Sopenharmony_ciconst maybeBacktick = '`?'; 4431cb0ef41Sopenharmony_ci 4441cb0ef41Sopenharmony_ci// To include constructs like `readable\[Symbol.asyncIterator\]()` 4451cb0ef41Sopenharmony_ci// or `readable.\_read(size)` (with Markdown escapes). 4461cb0ef41Sopenharmony_ciconst simpleId = r`(?:(?:\\?_)+|\b)\w+\b`; 4471cb0ef41Sopenharmony_ciconst computedId = r`\\?\[[\w\.]+\\?\]`; 4481cb0ef41Sopenharmony_ciconst id = `(?:${simpleId}|${computedId})`; 4491cb0ef41Sopenharmony_ciconst classId = r`[A-Z]\w+`; 4501cb0ef41Sopenharmony_ci 4511cb0ef41Sopenharmony_ciconst ancestors = r`(?:${id}\.?)+`; 4521cb0ef41Sopenharmony_ciconst maybeAncestors = r`(?:${id}\.?)*`; 4531cb0ef41Sopenharmony_ci 4541cb0ef41Sopenharmony_ciconst callWithParams = r`\([^)]*\)`; 4551cb0ef41Sopenharmony_ci 4561cb0ef41Sopenharmony_ciconst maybeExtends = `(?: +extends +${maybeAncestors}${classId})?`; 4571cb0ef41Sopenharmony_ci 4581cb0ef41Sopenharmony_ciconst headingExpressions = [ 4591cb0ef41Sopenharmony_ci { type: 'event', re: RegExp( 4601cb0ef41Sopenharmony_ci `${eventPrefix}${maybeBacktick}${maybeQuote}(${notQuotes})${maybeQuote}${maybeBacktick}$`, 'i') }, 4611cb0ef41Sopenharmony_ci 4621cb0ef41Sopenharmony_ci { type: 'class', re: RegExp( 4631cb0ef41Sopenharmony_ci `${classPrefix}${maybeBacktick}(${maybeAncestors}${classId})${maybeExtends}${maybeBacktick}$`, '') }, 4641cb0ef41Sopenharmony_ci 4651cb0ef41Sopenharmony_ci { type: 'ctor', re: RegExp( 4661cb0ef41Sopenharmony_ci `${ctorPrefix}(${maybeAncestors}${classId})${callWithParams}${maybeBacktick}$`, '') }, 4671cb0ef41Sopenharmony_ci 4681cb0ef41Sopenharmony_ci { type: 'classMethod', re: RegExp( 4691cb0ef41Sopenharmony_ci `${classMethodPrefix}${maybeBacktick}${maybeAncestors}(${id})${callWithParams}${maybeBacktick}$`, 'i') }, 4701cb0ef41Sopenharmony_ci 4711cb0ef41Sopenharmony_ci { type: 'method', re: RegExp( 4721cb0ef41Sopenharmony_ci `^${maybeBacktick}${maybeAncestors}(${id})${callWithParams}${maybeBacktick}$`, 'i') }, 4731cb0ef41Sopenharmony_ci 4741cb0ef41Sopenharmony_ci { type: 'property', re: RegExp( 4751cb0ef41Sopenharmony_ci `^${maybeClassPropertyPrefix}${maybeBacktick}${ancestors}(${id})${maybeBacktick}$`, 'i') }, 4761cb0ef41Sopenharmony_ci]; 4771cb0ef41Sopenharmony_ci 4781cb0ef41Sopenharmony_cifunction newSection(header, file) { 4791cb0ef41Sopenharmony_ci const text = textJoin(header.children, file); 4801cb0ef41Sopenharmony_ci 4811cb0ef41Sopenharmony_ci // Infer the type from the text. 4821cb0ef41Sopenharmony_ci for (const { type, re } of headingExpressions) { 4831cb0ef41Sopenharmony_ci const [, name] = text.match(re) || []; 4841cb0ef41Sopenharmony_ci if (name) { 4851cb0ef41Sopenharmony_ci return { textRaw: text, type, name }; 4861cb0ef41Sopenharmony_ci } 4871cb0ef41Sopenharmony_ci } 4881cb0ef41Sopenharmony_ci return { textRaw: text, name: text }; 4891cb0ef41Sopenharmony_ci} 4901cb0ef41Sopenharmony_ci 4911cb0ef41Sopenharmony_cifunction textJoin(nodes, file) { 4921cb0ef41Sopenharmony_ci return nodes.map((node) => { 4931cb0ef41Sopenharmony_ci if (node.type === 'linkReference') { 4941cb0ef41Sopenharmony_ci return file.value.slice(node.position.start.offset, 4951cb0ef41Sopenharmony_ci node.position.end.offset); 4961cb0ef41Sopenharmony_ci } else if (node.type === 'inlineCode') { 4971cb0ef41Sopenharmony_ci return `\`${node.value}\``; 4981cb0ef41Sopenharmony_ci } else if (node.type === 'strong') { 4991cb0ef41Sopenharmony_ci return `**${textJoin(node.children, file)}**`; 5001cb0ef41Sopenharmony_ci } else if (node.type === 'emphasis') { 5011cb0ef41Sopenharmony_ci return `_${textJoin(node.children, file)}_`; 5021cb0ef41Sopenharmony_ci } else if (node.children) { 5031cb0ef41Sopenharmony_ci return textJoin(node.children, file); 5041cb0ef41Sopenharmony_ci } 5051cb0ef41Sopenharmony_ci return node.value; 5061cb0ef41Sopenharmony_ci }).join(''); 5071cb0ef41Sopenharmony_ci} 508