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