tools: overhaul tools/doc/json.js
Modernize: * Replace `var` with `const` / `let`. * Wrap `switch` cases with `const`/`let` in blocks. * Replace common functions with arrow functions. * Replace string concatenation with template literals. * Shorthand object literals. * Use destructuring and spread. Optimize: * Move RegExp declaration out of loops. * Replace `.match()` with `.test()` in boolean context. * Replace RegExp with string when string suffices. * Make RegExp more strict to reject unrelated cases. * Make RegExp do the trimming to eliminate many `.trim()` calls. * Cache retrieved object properties. * Remove conditions that cannot be false. * Remove code that seems obsolete (it means a state that cannot happen or is not typical). Clarify: * Sync code examples in comments with the actual source state. * Expand some one-letter variable names. * Rename confusingly similar variables. * Move variable declarations closer to their context. * Remove non-actual commented out code. * Unify blank lines between top-level blocks. Fix: * Fix conditions that cannot be true. Guard: * Throw on unexpected state more often. PR-URL: https://github.com/nodejs/node/pull/19832 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Trivikram Kamat <trivikr.dev@gmail.com> Reviewed-By: Gus Caplan <me@gus.host>
This commit is contained in:
parent
0a679327be
commit
9a6dd07e8d
@ -31,49 +31,47 @@ const marked = require('marked');
|
|||||||
|
|
||||||
// Customized heading without id attribute.
|
// Customized heading without id attribute.
|
||||||
const renderer = new marked.Renderer();
|
const renderer = new marked.Renderer();
|
||||||
renderer.heading = function(text, level) {
|
renderer.heading = (text, level) => `<h${level}>${text}</h${level}>\n`;
|
||||||
return `<h${level}>${text}</h${level}>\n`;
|
marked.setOptions({ renderer });
|
||||||
};
|
|
||||||
marked.setOptions({
|
|
||||||
renderer: renderer
|
|
||||||
});
|
|
||||||
|
|
||||||
function doJSON(input, filename, cb) {
|
function doJSON(input, filename, cb) {
|
||||||
const root = { source: filename };
|
const root = { source: filename };
|
||||||
const stack = [root];
|
const stack = [root];
|
||||||
var depth = 0;
|
let depth = 0;
|
||||||
var current = root;
|
let current = root;
|
||||||
var state = null;
|
let state = null;
|
||||||
const lexed = marked.lexer(input);
|
|
||||||
lexed.forEach(function(tok) {
|
|
||||||
const type = tok.type;
|
|
||||||
var text = tok.text;
|
|
||||||
|
|
||||||
// <!-- type = module -->
|
const exampleHeading = /^example/i;
|
||||||
|
const metaExpr = /<!--([^=]+)=([^-]+)-->\n*/g;
|
||||||
|
const stabilityExpr = /^Stability: ([0-5])(?:\s*-\s*)?(.*)$/;
|
||||||
|
|
||||||
|
const lexed = marked.lexer(input);
|
||||||
|
lexed.forEach((tok) => {
|
||||||
|
const { type } = tok;
|
||||||
|
let { text } = tok;
|
||||||
|
|
||||||
|
// <!-- name=module -->
|
||||||
// This is for cases where the markdown semantic structure is lacking.
|
// This is for cases where the markdown semantic structure is lacking.
|
||||||
if (type === 'paragraph' || type === 'html') {
|
if (type === 'paragraph' || type === 'html') {
|
||||||
var metaExpr = /<!--([^=]+)=([^-]+)-->\n*/g;
|
text = text.replace(metaExpr, (_0, key, value) => {
|
||||||
text = text.replace(metaExpr, function(_0, k, v) {
|
current[key.trim()] = value.trim();
|
||||||
current[k.trim()] = v.trim();
|
|
||||||
return '';
|
return '';
|
||||||
});
|
});
|
||||||
text = text.trim();
|
text = text.trim();
|
||||||
if (!text) return;
|
if (!text) return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type === 'heading' &&
|
if (type === 'heading' && !exampleHeading.test(text.trim())) {
|
||||||
!text.trim().match(/^example/i)) {
|
|
||||||
if (tok.depth - depth > 1) {
|
if (tok.depth - depth > 1) {
|
||||||
return cb(new Error('Inappropriate heading level\n' +
|
return cb(
|
||||||
JSON.stringify(tok)));
|
new Error(`Inappropriate heading level\n${JSON.stringify(tok)}`));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sometimes we have two headings with a single blob of description.
|
// Sometimes we have two headings with a single blob of description.
|
||||||
// Treat as a clone.
|
// Treat as a clone.
|
||||||
if (current &&
|
if (state === 'AFTERHEADING' && depth === tok.depth) {
|
||||||
state === 'AFTERHEADING' &&
|
const clone = current;
|
||||||
depth === tok.depth) {
|
|
||||||
var clone = current;
|
|
||||||
current = newSection(tok);
|
current = newSection(tok);
|
||||||
current.clone = clone;
|
current.clone = clone;
|
||||||
// Don't keep it around on the stack.
|
// Don't keep it around on the stack.
|
||||||
@ -86,15 +84,15 @@ function doJSON(input, filename, cb) {
|
|||||||
// root is always considered the level=0 section,
|
// root is always considered the level=0 section,
|
||||||
// and the lowest heading is 1, so this should always
|
// and the lowest heading is 1, so this should always
|
||||||
// result in having a valid parent node.
|
// result in having a valid parent node.
|
||||||
var d = tok.depth;
|
let closingDepth = tok.depth;
|
||||||
while (d <= depth) {
|
while (closingDepth <= depth) {
|
||||||
finishSection(stack.pop(), stack[stack.length - 1]);
|
finishSection(stack.pop(), stack[stack.length - 1]);
|
||||||
d++;
|
closingDepth++;
|
||||||
}
|
}
|
||||||
current = newSection(tok);
|
current = newSection(tok);
|
||||||
}
|
}
|
||||||
|
|
||||||
depth = tok.depth;
|
({ depth } = tok);
|
||||||
stack.push(current);
|
stack.push(current);
|
||||||
state = 'AFTERHEADING';
|
state = 'AFTERHEADING';
|
||||||
return;
|
return;
|
||||||
@ -109,9 +107,13 @@ function doJSON(input, filename, cb) {
|
|||||||
// A list: starting with list_start, ending with list_end,
|
// A list: starting with list_start, ending with list_end,
|
||||||
// maybe containing other nested lists in each item.
|
// maybe containing other nested lists in each item.
|
||||||
//
|
//
|
||||||
|
// A metadata:
|
||||||
|
// <!-- YAML
|
||||||
|
// added: v1.0.0
|
||||||
|
// -->
|
||||||
|
//
|
||||||
// If one of these isn't found, then anything that comes
|
// If one of these isn't found, then anything that comes
|
||||||
// between here and the next heading should be parsed as the desc.
|
// between here and the next heading should be parsed as the desc.
|
||||||
var stability;
|
|
||||||
if (state === 'AFTERHEADING') {
|
if (state === 'AFTERHEADING') {
|
||||||
if (type === 'blockquote_start') {
|
if (type === 'blockquote_start') {
|
||||||
state = 'AFTERHEADING_BLOCKQUOTE';
|
state = 'AFTERHEADING_BLOCKQUOTE';
|
||||||
@ -156,8 +158,8 @@ function doJSON(input, filename, cb) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type === 'paragraph' &&
|
let stability;
|
||||||
(stability = text.match(/^Stability: ([0-5])(?:\s*-\s*)?(.*)$/))) {
|
if (type === 'paragraph' && (stability = text.match(stabilityExpr))) {
|
||||||
current.stability = parseInt(stability[1], 10);
|
current.stability = parseInt(stability[1], 10);
|
||||||
current.stabilityText = stability[2].trim();
|
current.stabilityText = stability[2].trim();
|
||||||
return;
|
return;
|
||||||
@ -167,7 +169,6 @@ function doJSON(input, filename, cb) {
|
|||||||
current.desc = current.desc || [];
|
current.desc = current.desc || [];
|
||||||
current.desc.links = lexed.links;
|
current.desc.links = lexed.links;
|
||||||
current.desc.push(tok);
|
current.desc.push(tok);
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Finish any sections left open.
|
// Finish any sections left open.
|
||||||
@ -181,69 +182,67 @@ function doJSON(input, filename, cb) {
|
|||||||
|
|
||||||
// Go from something like this:
|
// Go from something like this:
|
||||||
//
|
//
|
||||||
// [ { type: 'list_item_start' },
|
// [ { type: "list_item_start" },
|
||||||
// { type: 'text',
|
// { type: "text",
|
||||||
// text: '`settings` Object, Optional' },
|
// text: "`options` {Object|string}" },
|
||||||
// { type: 'list_start', ordered: false },
|
// { type: "list_start",
|
||||||
// { type: 'list_item_start' },
|
// ordered: false },
|
||||||
// { type: 'text',
|
// { type: "list_item_start" },
|
||||||
// text: 'exec: String, file path to worker file. Default: `__filename`' },
|
// { type: "text",
|
||||||
// { type: 'list_item_end' },
|
// text: "`encoding` {string|null} **Default:** `'utf8'`" },
|
||||||
// { type: 'list_item_start' },
|
// { type: "list_item_end" },
|
||||||
// { type: 'text',
|
// { type: "list_item_start" },
|
||||||
// text: 'args: Array, string arguments passed to worker.' },
|
// { type: "text",
|
||||||
// { type: 'text',
|
// text: "`mode` {integer} **Default:** `0o666`" },
|
||||||
// text: 'Default: `process.argv.slice(2)`' },
|
// { type: "list_item_end" },
|
||||||
// { type: 'list_item_end' },
|
// { type: "list_item_start" },
|
||||||
// { type: 'list_item_start' },
|
// { type: "text",
|
||||||
// { type: 'text',
|
// text: "`flag` {string} **Default:** `'a'`" },
|
||||||
// text: 'silent: Boolean, whether to send output to parent\'s stdio.' },
|
// { type: "space" },
|
||||||
// { type: 'text', text: 'Default: `false`' },
|
// { type: "list_item_end" },
|
||||||
// { type: 'space' },
|
// { type: "list_end" },
|
||||||
// { type: 'list_item_end' },
|
// { type: "list_item_end" } ]
|
||||||
// { type: 'list_end' },
|
|
||||||
// { type: 'list_item_end' },
|
|
||||||
// { type: 'list_end' } ]
|
|
||||||
//
|
//
|
||||||
// to something like:
|
// to something like:
|
||||||
//
|
//
|
||||||
// [ { name: 'settings',
|
// [ { textRaw: "`options` {Object|string} ",
|
||||||
// type: 'object',
|
// options: [
|
||||||
// optional: true,
|
// { textRaw: "`encoding` {string|null} **Default:** `'utf8'` ",
|
||||||
// settings:
|
// name: "encoding",
|
||||||
// [ { name: 'exec',
|
// type: "string|null",
|
||||||
// type: 'string',
|
// default: "`'utf8'`" },
|
||||||
// desc: 'file path to worker file',
|
// { textRaw: "`mode` {integer} **Default:** `0o666` ",
|
||||||
// default: '__filename' },
|
// name: "mode",
|
||||||
// { name: 'args',
|
// type: "integer",
|
||||||
// type: 'array',
|
// default: "`0o666`" },
|
||||||
// default: 'process.argv.slice(2)',
|
// { textRaw: "`flag` {string} **Default:** `'a'` ",
|
||||||
// desc: 'string arguments passed to worker.' },
|
// name: "flag",
|
||||||
// { name: 'silent',
|
// type: "string",
|
||||||
// type: 'boolean',
|
// default: "`'a'`" } ],
|
||||||
// desc: 'whether to send output to parent\'s stdio.',
|
// name: "options",
|
||||||
// default: 'false' } ] } ]
|
// type: "Object|string",
|
||||||
|
// optional: true } ]
|
||||||
|
|
||||||
function processList(section) {
|
function processList(section) {
|
||||||
const list = section.list;
|
const { list } = section;
|
||||||
const values = [];
|
const values = [];
|
||||||
var current;
|
|
||||||
const stack = [];
|
const stack = [];
|
||||||
|
let current;
|
||||||
|
|
||||||
// For now, *just* build the hierarchical list.
|
// For now, *just* build the hierarchical list.
|
||||||
list.forEach(function(tok) {
|
list.forEach((tok) => {
|
||||||
const type = tok.type;
|
const { type } = tok;
|
||||||
if (type === 'space') return;
|
if (type === 'space') return;
|
||||||
if (type === 'list_item_start' || type === 'loose_item_start') {
|
if (type === 'list_item_start' || type === 'loose_item_start') {
|
||||||
var n = {};
|
const item = {};
|
||||||
if (!current) {
|
if (!current) {
|
||||||
values.push(n);
|
values.push(item);
|
||||||
current = n;
|
current = item;
|
||||||
} else {
|
} else {
|
||||||
current.options = current.options || [];
|
current.options = current.options || [];
|
||||||
stack.push(current);
|
stack.push(current);
|
||||||
current.options.push(n);
|
current.options.push(item);
|
||||||
current = n;
|
current = item;
|
||||||
}
|
}
|
||||||
} else if (type === 'list_item_end') {
|
} else if (type === 'list_item_end') {
|
||||||
if (!current) {
|
if (!current) {
|
||||||
@ -277,33 +276,35 @@ function processList(section) {
|
|||||||
switch (section.type) {
|
switch (section.type) {
|
||||||
case 'ctor':
|
case 'ctor':
|
||||||
case 'classMethod':
|
case 'classMethod':
|
||||||
case 'method':
|
case 'method': {
|
||||||
// Each item is an argument, unless the name is 'return',
|
// Each item is an argument, unless the name is 'return',
|
||||||
// in which case it's the return value.
|
// in which case it's the return value.
|
||||||
section.signatures = section.signatures || [];
|
section.signatures = section.signatures || [];
|
||||||
var sig = {};
|
const sig = {};
|
||||||
section.signatures.push(sig);
|
section.signatures.push(sig);
|
||||||
sig.params = values.filter(function(v) {
|
sig.params = values.filter((value) => {
|
||||||
if (v.name === 'return') {
|
if (value.name === 'return') {
|
||||||
sig.return = v;
|
sig.return = value;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
parseSignature(section.textRaw, sig);
|
parseSignature(section.textRaw, sig);
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case 'property':
|
case 'property': {
|
||||||
// There should be only one item, which is the value.
|
// There should be only one item, which is the value.
|
||||||
// Copy the data up to the section.
|
// Copy the data up to the section.
|
||||||
var value = values[0] || {};
|
const value = values[0] || {};
|
||||||
delete value.name;
|
delete value.name;
|
||||||
section.typeof = value.type || section.typeof;
|
section.typeof = value.type || section.typeof;
|
||||||
delete value.type;
|
delete value.type;
|
||||||
Object.keys(value).forEach(function(k) {
|
Object.keys(value).forEach((key) => {
|
||||||
section[k] = value[k];
|
section[key] = value[key];
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case 'event':
|
case 'event':
|
||||||
// Event: each item is an argument.
|
// Event: each item is an argument.
|
||||||
@ -313,117 +314,111 @@ function processList(section) {
|
|||||||
default:
|
default:
|
||||||
if (section.list.length > 0) {
|
if (section.list.length > 0) {
|
||||||
section.desc = section.desc || [];
|
section.desc = section.desc || [];
|
||||||
for (var i = 0; i < section.list.length; i++) {
|
section.desc.push(...section.list);
|
||||||
section.desc.push(section.list[i]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// section.listParsed = values;
|
|
||||||
delete section.list;
|
delete section.list;
|
||||||
}
|
}
|
||||||
|
|
||||||
const paramExpr = /\((.*)\);?$/;
|
|
||||||
|
|
||||||
// textRaw = "someobject.someMethod(a[, b=100][, c])"
|
const paramExpr = /\((.+)\);?$/;
|
||||||
|
|
||||||
|
// text: "someobject.someMethod(a[, b=100][, c])"
|
||||||
function parseSignature(text, sig) {
|
function parseSignature(text, sig) {
|
||||||
var params = text.match(paramExpr);
|
let [, sigParams] = text.match(paramExpr) || [];
|
||||||
if (!params) return;
|
if (!sigParams) return;
|
||||||
params = params[1];
|
sigParams = sigParams.split(',');
|
||||||
params = params.split(/,/);
|
let optionalLevel = 0;
|
||||||
var optionalLevel = 0;
|
|
||||||
const optionalCharDict = { '[': 1, ' ': 0, ']': -1 };
|
const optionalCharDict = { '[': 1, ' ': 0, ']': -1 };
|
||||||
params.forEach(function(p, i) {
|
sigParams.forEach((sigParam, i) => {
|
||||||
p = p.trim();
|
sigParam = sigParam.trim();
|
||||||
if (!p) return;
|
if (!sigParam) {
|
||||||
var param = sig.params[i];
|
throw new Error(`Empty parameter slot: ${text}`);
|
||||||
var optional = false;
|
}
|
||||||
var def;
|
let listParam = sig.params[i];
|
||||||
|
let optional = false;
|
||||||
|
let defaultValue;
|
||||||
|
|
||||||
// For grouped optional params such as someMethod(a[, b[, c]]).
|
// For grouped optional params such as someMethod(a[, b[, c]]).
|
||||||
var pos;
|
let pos;
|
||||||
for (pos = 0; pos < p.length; pos++) {
|
for (pos = 0; pos < sigParam.length; pos++) {
|
||||||
if (optionalCharDict[p[pos]] === undefined) { break; }
|
const levelChange = optionalCharDict[sigParam[pos]];
|
||||||
optionalLevel += optionalCharDict[p[pos]];
|
if (levelChange === undefined) break;
|
||||||
|
optionalLevel += levelChange;
|
||||||
}
|
}
|
||||||
p = p.substring(pos);
|
sigParam = sigParam.substring(pos);
|
||||||
optional = (optionalLevel > 0);
|
optional = (optionalLevel > 0);
|
||||||
for (pos = p.length - 1; pos >= 0; pos--) {
|
for (pos = sigParam.length - 1; pos >= 0; pos--) {
|
||||||
if (optionalCharDict[p[pos]] === undefined) { break; }
|
const levelChange = optionalCharDict[sigParam[pos]];
|
||||||
optionalLevel += optionalCharDict[p[pos]];
|
if (levelChange === undefined) break;
|
||||||
|
optionalLevel += levelChange;
|
||||||
}
|
}
|
||||||
p = p.substring(0, pos + 1);
|
sigParam = sigParam.substring(0, pos + 1);
|
||||||
|
|
||||||
const eq = p.indexOf('=');
|
const eq = sigParam.indexOf('=');
|
||||||
if (eq !== -1) {
|
if (eq !== -1) {
|
||||||
def = p.substr(eq + 1);
|
defaultValue = sigParam.substr(eq + 1);
|
||||||
p = p.substr(0, eq);
|
sigParam = sigParam.substr(0, eq);
|
||||||
}
|
}
|
||||||
if (!param) {
|
if (!listParam) {
|
||||||
param = sig.params[i] = { name: p };
|
listParam = sig.params[i] = { name: sigParam };
|
||||||
}
|
}
|
||||||
// At this point, the name should match.
|
// At this point, the name should match.
|
||||||
if (p !== param.name) {
|
if (sigParam !== listParam.name) {
|
||||||
console.error('Warning: invalid param "%s"', p);
|
throw new Error(
|
||||||
console.error(` > ${JSON.stringify(param)}`);
|
`Warning: invalid param "${sigParam}"\n` +
|
||||||
console.error(` > ${text}`);
|
` > ${JSON.stringify(listParam)}\n` +
|
||||||
|
` > ${text}`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if (optional) param.optional = true;
|
if (optional) listParam.optional = true;
|
||||||
if (def !== undefined) param.default = def;
|
if (defaultValue !== undefined) listParam.default = defaultValue.trim();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const returnExpr = /^returns?\s*:?\s*/i;
|
||||||
|
const nameExpr = /^['`"]?([^'`": {]+)['`"]?\s*:?\s*/;
|
||||||
|
const typeExpr = /^\{([^}]+)\}\s*/;
|
||||||
|
const leadingHyphen = /^-\s*/;
|
||||||
|
const defaultExpr = /\s*\*\*Default:\*\*\s*([^]+)$/i;
|
||||||
|
|
||||||
function parseListItem(item) {
|
function parseListItem(item) {
|
||||||
if (item.options) item.options.forEach(parseListItem);
|
if (item.options) item.options.forEach(parseListItem);
|
||||||
if (!item.textRaw) return;
|
if (!item.textRaw) {
|
||||||
|
throw new Error(`Empty list item: ${JSON.stringify(item)}`);
|
||||||
|
}
|
||||||
|
|
||||||
// The goal here is to find the name, type, default, and optional.
|
// The goal here is to find the name, type, default, and optional.
|
||||||
// Anything left over is 'desc'.
|
// Anything left over is 'desc'.
|
||||||
var text = item.textRaw.trim();
|
let text = item.textRaw.trim();
|
||||||
// text = text.replace(/^(Argument|Param)s?\s*:?\s*/i, '');
|
|
||||||
|
|
||||||
text = text.replace(/^, /, '').trim();
|
if (returnExpr.test(text)) {
|
||||||
const retExpr = /^returns?\s*:?\s*/i;
|
|
||||||
const ret = text.match(retExpr);
|
|
||||||
if (ret) {
|
|
||||||
item.name = 'return';
|
item.name = 'return';
|
||||||
text = text.replace(retExpr, '');
|
text = text.replace(returnExpr, '');
|
||||||
} else {
|
} else {
|
||||||
var nameExpr = /^['`"]?([^'`": {]+)['`"]?\s*:?\s*/;
|
const [, name] = text.match(nameExpr) || [];
|
||||||
var name = text.match(nameExpr);
|
|
||||||
if (name) {
|
if (name) {
|
||||||
item.name = name[1];
|
item.name = name;
|
||||||
text = text.replace(nameExpr, '');
|
text = text.replace(nameExpr, '');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
text = text.trim();
|
const [, type] = text.match(typeExpr) || [];
|
||||||
const defaultExpr = /\s*\*\*Default:\*\*\s*([^]+)$/i;
|
|
||||||
const def = text.match(defaultExpr);
|
|
||||||
if (def) {
|
|
||||||
item.default = def[1].replace(/\.$/, '');
|
|
||||||
text = text.replace(defaultExpr, '');
|
|
||||||
}
|
|
||||||
|
|
||||||
text = text.trim();
|
|
||||||
const typeExpr = /^\{([^}]+)\}/;
|
|
||||||
const type = text.match(typeExpr);
|
|
||||||
if (type) {
|
if (type) {
|
||||||
item.type = type[1];
|
item.type = type;
|
||||||
text = text.replace(typeExpr, '');
|
text = text.replace(typeExpr, '');
|
||||||
}
|
}
|
||||||
|
|
||||||
text = text.trim();
|
text = text.replace(leadingHyphen, '');
|
||||||
const optExpr = /^Optional\.|(?:, )?Optional$/;
|
|
||||||
const optional = text.match(optExpr);
|
const [, defaultValue] = text.match(defaultExpr) || [];
|
||||||
if (optional) {
|
if (defaultValue) {
|
||||||
item.optional = true;
|
item.default = defaultValue.replace(/\.$/, '');
|
||||||
text = text.replace(optExpr, '');
|
text = text.replace(defaultExpr, '');
|
||||||
}
|
}
|
||||||
|
|
||||||
text = text.replace(/^\s*-\s*/, '');
|
|
||||||
text = text.trim();
|
|
||||||
if (text) item.desc = text;
|
if (text) item.desc = text;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -437,7 +432,7 @@ function finishSection(section, parent) {
|
|||||||
|
|
||||||
if (!section.type) {
|
if (!section.type) {
|
||||||
section.type = 'module';
|
section.type = 'module';
|
||||||
if (parent && (parent.type === 'misc')) {
|
if (parent.type === 'misc') {
|
||||||
section.type = 'misc';
|
section.type = 'misc';
|
||||||
}
|
}
|
||||||
section.displayName = section.name;
|
section.displayName = section.name;
|
||||||
@ -458,13 +453,13 @@ function finishSection(section, parent) {
|
|||||||
// Merge them into the parent.
|
// Merge them into the parent.
|
||||||
if (section.type === 'class' && section.ctors) {
|
if (section.type === 'class' && section.ctors) {
|
||||||
section.signatures = section.signatures || [];
|
section.signatures = section.signatures || [];
|
||||||
var sigs = section.signatures;
|
const sigs = section.signatures;
|
||||||
section.ctors.forEach(function(ctor) {
|
section.ctors.forEach((ctor) => {
|
||||||
ctor.signatures = ctor.signatures || [{}];
|
ctor.signatures = ctor.signatures || [{}];
|
||||||
ctor.signatures.forEach(function(sig) {
|
ctor.signatures.forEach((sig) => {
|
||||||
sig.desc = ctor.desc;
|
sig.desc = ctor.desc;
|
||||||
});
|
});
|
||||||
sigs.push.apply(sigs, ctor.signatures);
|
sigs.push(...ctor.signatures);
|
||||||
});
|
});
|
||||||
delete section.ctors;
|
delete section.ctors;
|
||||||
}
|
}
|
||||||
@ -472,23 +467,26 @@ function finishSection(section, parent) {
|
|||||||
// Properties are a bit special.
|
// Properties are a bit special.
|
||||||
// Their "type" is the type of object, not "property".
|
// Their "type" is the type of object, not "property".
|
||||||
if (section.properties) {
|
if (section.properties) {
|
||||||
section.properties.forEach(function(p) {
|
section.properties.forEach((prop) => {
|
||||||
if (p.typeof) p.type = p.typeof;
|
if (prop.typeof) {
|
||||||
else delete p.type;
|
prop.type = prop.typeof;
|
||||||
delete p.typeof;
|
delete prop.typeof;
|
||||||
|
} else {
|
||||||
|
delete prop.type;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle clones.
|
// Handle clones.
|
||||||
if (section.clone) {
|
if (section.clone) {
|
||||||
var clone = section.clone;
|
const { clone } = section;
|
||||||
delete section.clone;
|
delete section.clone;
|
||||||
delete clone.clone;
|
delete clone.clone;
|
||||||
deepCopy(section, clone);
|
deepCopy(section, clone);
|
||||||
finishSection(clone, parent);
|
finishSection(clone, parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
var plur;
|
let plur;
|
||||||
if (section.type.slice(-1) === 's') {
|
if (section.type.slice(-1) === 's') {
|
||||||
plur = `${section.type}es`;
|
plur = `${section.type}es`;
|
||||||
} else if (section.type.slice(-1) === 'y') {
|
} else if (section.type.slice(-1) === 'y') {
|
||||||
@ -501,8 +499,8 @@ function finishSection(section, parent) {
|
|||||||
// collection of stuff, like the "globals" section.
|
// collection of stuff, like the "globals" section.
|
||||||
// Make the children top-level items.
|
// Make the children top-level items.
|
||||||
if (section.type === 'misc') {
|
if (section.type === 'misc') {
|
||||||
Object.keys(section).forEach(function(k) {
|
Object.keys(section).forEach((key) => {
|
||||||
switch (k) {
|
switch (key) {
|
||||||
case 'textRaw':
|
case 'textRaw':
|
||||||
case 'name':
|
case 'name':
|
||||||
case 'type':
|
case 'type':
|
||||||
@ -513,10 +511,10 @@ function finishSection(section, parent) {
|
|||||||
if (parent.type === 'misc') {
|
if (parent.type === 'misc') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (Array.isArray(k) && parent[k]) {
|
if (parent[key] && Array.isArray(parent[key])) {
|
||||||
parent[k] = parent[k].concat(section[k]);
|
parent[key] = parent[key].concat(section[key]);
|
||||||
} else if (!parent[k]) {
|
} else if (!parent[key]) {
|
||||||
parent[k] = section[k];
|
parent[key] = section[key];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -530,28 +528,26 @@ function finishSection(section, parent) {
|
|||||||
// Not a general purpose deep copy.
|
// Not a general purpose deep copy.
|
||||||
// But sufficient for these basic things.
|
// But sufficient for these basic things.
|
||||||
function deepCopy(src, dest) {
|
function deepCopy(src, dest) {
|
||||||
Object.keys(src).filter(function(k) {
|
Object.keys(src)
|
||||||
return !dest.hasOwnProperty(k);
|
.filter((key) => !dest.hasOwnProperty(key))
|
||||||
}).forEach(function(k) {
|
.forEach((key) => { dest[key] = cloneValue(src[key]); });
|
||||||
dest[k] = deepCopy_(src[k]);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function deepCopy_(src) {
|
function cloneValue(src) {
|
||||||
if (!src) return src;
|
if (!src) return src;
|
||||||
if (Array.isArray(src)) {
|
if (Array.isArray(src)) {
|
||||||
const c = new Array(src.length);
|
const clone = new Array(src.length);
|
||||||
src.forEach(function(v, i) {
|
src.forEach((value, i) => {
|
||||||
c[i] = deepCopy_(v);
|
clone[i] = cloneValue(value);
|
||||||
});
|
});
|
||||||
return c;
|
return clone;
|
||||||
}
|
}
|
||||||
if (typeof src === 'object') {
|
if (typeof src === 'object') {
|
||||||
const c = {};
|
const clone = {};
|
||||||
Object.keys(src).forEach(function(k) {
|
Object.keys(src).forEach((key) => {
|
||||||
c[k] = deepCopy_(src[k]);
|
clone[key] = cloneValue(src[key]);
|
||||||
});
|
});
|
||||||
return c;
|
return clone;
|
||||||
}
|
}
|
||||||
return src;
|
return src;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user