doc: further align docs w/ playwright.dev (3) (#4884)
This commit is contained in:
parent
5215add60d
commit
80f8a0fdf5
|
|
@ -36,8 +36,8 @@ Most of the accessibility tree gets filtered out when converting from Blink AX T
|
|||
- `readonly` <[boolean]> Whether the node is read only, if applicable.
|
||||
- `required` <[boolean]> Whether the node is required, if applicable.
|
||||
- `selected` <[boolean]> Whether the node is selected in its parent node, if applicable.
|
||||
- `checked` <boolean|"mixed"> Whether the checkbox is checked, or "mixed", if applicable.
|
||||
- `pressed` <boolean|"mixed"> Whether the toggle button is checked, or "mixed", if applicable.
|
||||
- `checked` <[boolean]|"mixed"> Whether the checkbox is checked, or "mixed", if applicable.
|
||||
- `pressed` <[boolean]|"mixed"> Whether the toggle button is checked, or "mixed", if applicable.
|
||||
- `level` <[number]> The level of a heading, if applicable.
|
||||
- `valuemin` <[number]> The minimum value in a node, if applicable.
|
||||
- `valuemax` <[number]> The maximum value in a node, if applicable.
|
||||
|
|
|
|||
|
|
@ -18,6 +18,17 @@
|
|||
|
||||
/** @typedef {import('../markdown').MarkdownNode} MarkdownNode */
|
||||
|
||||
/**
|
||||
* @typedef {{
|
||||
* name: string,
|
||||
* args: ParsedType | null,
|
||||
* retType: ParsedType | null,
|
||||
* template: ParsedType | null,
|
||||
* union: ParsedType | null,
|
||||
* next: ParsedType | null,
|
||||
* }} ParsedType
|
||||
*/
|
||||
|
||||
class Documentation {
|
||||
/**
|
||||
* @param {!Array<!Documentation.Class>} classesArray
|
||||
|
|
@ -242,19 +253,186 @@ Documentation.Member = class {
|
|||
};
|
||||
|
||||
Documentation.Type = class {
|
||||
/**
|
||||
* @param {string} expression
|
||||
* @param {!Array<!Documentation.Member>=} properties
|
||||
* @return {Documentation.Type}
|
||||
*/
|
||||
static parse(expression, properties = []) {
|
||||
expression = expression.replace(/\\\(/g, '(').replace(/\\\)/g, ')');
|
||||
const type = Documentation.Type.fromParsedType(parseTypeExpression(expression));
|
||||
if (!properties.length)
|
||||
return type;
|
||||
const types = [];
|
||||
type._collectAllTypes(types);
|
||||
let success = false;
|
||||
for (const t of types) {
|
||||
if (t.name === 'Object') {
|
||||
t.properties = properties;
|
||||
success = true;
|
||||
}
|
||||
}
|
||||
if (!success)
|
||||
throw new Error('Nested properties given, but there are no objects in type expression: ' + expression);
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {ParsedType} parsedType
|
||||
* @return {Documentation.Type}
|
||||
*/
|
||||
static fromParsedType(parsedType, inUnion = false) {
|
||||
if (!inUnion && parsedType.union) {
|
||||
const type = new Documentation.Type('union');
|
||||
type.union = [];
|
||||
for (let t = parsedType; t; t = t.union)
|
||||
type.union.push(Documentation.Type.fromParsedType(t, true));
|
||||
return type;
|
||||
}
|
||||
|
||||
if (parsedType.args) {
|
||||
const type = new Documentation.Type('function');
|
||||
type.args = [];
|
||||
for (let t = parsedType.args; t; t = t.next)
|
||||
type.args.push(Documentation.Type.fromParsedType(t));
|
||||
type.returnType = parsedType.retType ? Documentation.Type.fromParsedType(parsedType.retType) : null;
|
||||
return type;
|
||||
}
|
||||
|
||||
if (parsedType.template) {
|
||||
const type = new Documentation.Type(parsedType.name);
|
||||
type.templates = [];
|
||||
for (let t = parsedType.template; t; t = t.next)
|
||||
type.templates.push(Documentation.Type.fromParsedType(t));
|
||||
return type;
|
||||
}
|
||||
return new Documentation.Type(parsedType.name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} name
|
||||
* @param {!Array<!Documentation.Member>=} properties
|
||||
*/
|
||||
constructor(name, properties = []) {
|
||||
this.name = name;
|
||||
this.properties = properties;
|
||||
constructor(name, properties) {
|
||||
this.name = name.replace(/^\[/, '').replace(/\]$/, '');
|
||||
this.properties = this.name === 'Object' ? properties : undefined;
|
||||
/** @type {Documentation.Type[]} | undefined */
|
||||
this.union;
|
||||
/** @type {Documentation.Type[]} | undefined */
|
||||
this.args;
|
||||
/** @type {Documentation.Type} | undefined */
|
||||
this.returnType;
|
||||
/** @type {Documentation.Type[]} | undefined */
|
||||
this.templates;
|
||||
}
|
||||
|
||||
visit(visitor) {
|
||||
for (const p of this.properties || [])
|
||||
p.visit(visitor);
|
||||
const types = [];
|
||||
this._collectAllTypes(types);
|
||||
for (const type of types) {
|
||||
for (const p of type.properties || [])
|
||||
p.visit(visitor);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {Documentation.Member[]}
|
||||
*/
|
||||
deepProperties() {
|
||||
const types = [];
|
||||
this._collectAllTypes(types);
|
||||
for (const type of types) {
|
||||
if (type.properties && type.properties.length)
|
||||
return type.properties;
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Documentation.Type[]} result
|
||||
*/
|
||||
_collectAllTypes(result) {
|
||||
result.push(this);
|
||||
for (const t of this.union || [])
|
||||
t._collectAllTypes(result);
|
||||
for (const t of this.args || [])
|
||||
t._collectAllTypes(result);
|
||||
for (const t of this.templates || [])
|
||||
t._collectAllTypes(result);
|
||||
if (this.returnType)
|
||||
this.returnType._collectAllTypes(result);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} type
|
||||
* @returns {ParsedType}
|
||||
*/
|
||||
function parseTypeExpression(type) {
|
||||
type = type.trim();
|
||||
let name = type;
|
||||
let next = null;
|
||||
let template = null;
|
||||
let args = null;
|
||||
let retType = null;
|
||||
let firstTypeLength = type.length;
|
||||
|
||||
for (let i = 0; i < type.length; i++) {
|
||||
if (type[i] === '<') {
|
||||
name = type.substring(0, i);
|
||||
const matching = matchingBracket(type.substring(i), '<', '>');
|
||||
template = parseTypeExpression(type.substring(i + 1, i + matching - 1));
|
||||
firstTypeLength = i + matching;
|
||||
break;
|
||||
}
|
||||
if (type[i] === '(') {
|
||||
name = type.substring(0, i);
|
||||
const matching = matchingBracket(type.substring(i), '(', ')');
|
||||
args = parseTypeExpression(type.substring(i + 1, i + matching - 1));
|
||||
i = i + matching;
|
||||
if (type[i] === ':') {
|
||||
retType = parseTypeExpression(type.substring(i + 1));
|
||||
next = retType.next;
|
||||
retType.next = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (type[i] === '|' || type[i] === ',') {
|
||||
name = type.substring(0, i);
|
||||
firstTypeLength = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
let union = null;
|
||||
if (type[firstTypeLength] === '|')
|
||||
union = parseTypeExpression(type.substring(firstTypeLength + 1));
|
||||
else if (type[firstTypeLength] === ',')
|
||||
next = parseTypeExpression(type.substring(firstTypeLength + 1));
|
||||
return {
|
||||
name,
|
||||
args,
|
||||
retType,
|
||||
template,
|
||||
union,
|
||||
next
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} str
|
||||
* @param {any} open
|
||||
* @param {any} close
|
||||
*/
|
||||
function matchingBracket(str, open, close) {
|
||||
let count = 1;
|
||||
let i = 1;
|
||||
for (; i < str.length && count; i++) {
|
||||
if (str[i] === open)
|
||||
count++;
|
||||
else if (str[i] === close)
|
||||
count--;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
module.exports = Documentation;
|
||||
|
|
|
|||
|
|
@ -22,12 +22,14 @@ const Documentation = require('./Documentation');
|
|||
|
||||
/** @typedef {import('../markdown').MarkdownNode} MarkdownNode */
|
||||
|
||||
/** @typedef {function({
|
||||
* clazz?: Documentation.Class,
|
||||
* member?: Documentation.Member,
|
||||
* param?: string,
|
||||
* option?: string
|
||||
* }): string} Renderer */
|
||||
/**
|
||||
* @typedef {function({
|
||||
* clazz?: Documentation.Class,
|
||||
* member?: Documentation.Member,
|
||||
* param?: string,
|
||||
* option?: string
|
||||
* }): string} Renderer
|
||||
*/
|
||||
|
||||
class MDOutline {
|
||||
/**
|
||||
|
|
@ -192,8 +194,12 @@ function parseMember(member) {
|
|||
}
|
||||
if (!returnType)
|
||||
returnType = new Documentation.Type('void');
|
||||
if (match[1] === 'async method')
|
||||
returnType.name = `Promise<${returnType.name}>`;
|
||||
|
||||
if (match[1] === 'async method') {
|
||||
const templates = [ returnType ];
|
||||
returnType = new Documentation.Type('Promise');
|
||||
returnType.templates = templates;
|
||||
}
|
||||
|
||||
if (match[1] === 'event')
|
||||
return Documentation.Member.createEvent(name, returnType, extractComments(member));
|
||||
|
|
@ -234,27 +240,14 @@ function parseProperty(spec) {
|
|||
* @return {Documentation.Type}
|
||||
*/
|
||||
function parseType(spec) {
|
||||
const { type } = parseArgument(spec.text);
|
||||
let typeName = type.replace(/[\[\]\\]/g, '');
|
||||
const literals = typeName.match(/("[^"]+"(\|"[^"]+")*)/);
|
||||
if (literals) {
|
||||
const assorted = literals[1];
|
||||
typeName = typeName.substring(0, literals.index) + assorted + typeName.substring(literals.index + literals[0].length);
|
||||
}
|
||||
const arg = parseArgument(spec.text);
|
||||
const properties = [];
|
||||
const hasNonEnumProperties = typeName.split('|').some(part => {
|
||||
const basicTypes = new Set(['string', 'number', 'boolean']);
|
||||
const arrayTypes = new Set([...basicTypes].map(type => `Array<${type}>`));
|
||||
return !basicTypes.has(part) && !arrayTypes.has(part) && !(part.startsWith('"') && part.endsWith('"'));
|
||||
});
|
||||
if (hasNonEnumProperties && spec) {
|
||||
for (const child of spec.children || []) {
|
||||
const { name, text } = parseArgument(child.text);
|
||||
const comments = /** @type {MarkdownNode[]} */ ([{ type: 'text', text }]);
|
||||
properties.push(Documentation.Member.createProperty(name, parseType(child), comments, guessRequired(text)));
|
||||
}
|
||||
for (const child of spec.children || []) {
|
||||
const { name, text } = parseArgument(child.text);
|
||||
const comments = /** @type {MarkdownNode[]} */ ([{ type: 'text', text }]);
|
||||
properties.push(Documentation.Member.createProperty(name, parseType(child), comments, guessRequired(text)));
|
||||
}
|
||||
return new Documentation.Type(typeName, properties);
|
||||
return Documentation.Type.parse(arg.type, properties);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -268,29 +268,37 @@ function renderProperty(name, type, spec) {
|
|||
if (spec && spec.length)
|
||||
comment = spec[0].text;
|
||||
let children;
|
||||
if (type.properties && type.properties.length)
|
||||
children = type.properties.map(p => renderProperty(`\`${p.name}\``, p.type, p.spec))
|
||||
const properties = type.deepProperties();
|
||||
if (properties && properties.length)
|
||||
children = properties.map(p => renderProperty(`\`${p.name}\``, p.type, p.spec))
|
||||
else if (spec && spec.length > 1)
|
||||
children = spec.slice(1).map(s => md.clone(s));
|
||||
|
||||
let typeText = renderType(type);
|
||||
if (typeText === '[Promise]<[void]>')
|
||||
typeText = '[Promise]';
|
||||
|
||||
/** @type {MarkdownNode} */
|
||||
const result = {
|
||||
type: 'li',
|
||||
liType: 'default',
|
||||
text: `${name} <${renderType(type.name)}>${comment ? ' ' + comment : ''}`,
|
||||
text: `${name} <${typeText}>${comment ? ' ' + comment : ''}`,
|
||||
children
|
||||
};
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} type
|
||||
* @param {Documentation.Type} type
|
||||
*/
|
||||
function renderType(type) {
|
||||
if (type.includes('"'))
|
||||
return type.replace(/,/g, '|').replace(/Array/, "[Array]").replace(/null/, "[null]").replace(/number/, "[number]");
|
||||
const result = type.replace(/([\w]+)/g, '[$1]');
|
||||
if (result === '[Promise]<[void]>')
|
||||
return '[Promise]';
|
||||
return result.replace(/[(]/g, '\\(').replace(/[)]/g, '\\)');
|
||||
if (type.union)
|
||||
return type.union.map(l => renderType(l)).join('|');
|
||||
if (type.templates)
|
||||
return `[${type.name}]<${type.templates.map(l => renderType(l)).join(', ')}>`;
|
||||
if (type.args)
|
||||
return `[function]\\(${type.args.map(l => renderType(l)).join(', ')}\\)${type.returnType ? ':' + renderType(type.returnType) : ''}`;
|
||||
if (type.name.startsWith('"'))
|
||||
return type.name;
|
||||
return `[${type.name}]`;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -57,22 +57,7 @@ function serializeClass(clazz) {
|
|||
result.extends = clazz.extends;
|
||||
if (clazz.comment)
|
||||
result.comment = clazz.comment;
|
||||
result.methods = {};
|
||||
result.events = {};
|
||||
result.properties = {};
|
||||
for (const member of clazz.membersArray) {
|
||||
let map;
|
||||
if (member.kind === 'event') {
|
||||
map = result.events;
|
||||
} else if (member.kind === 'method') {
|
||||
map = result.methods;
|
||||
} else if (member.kind === 'property') {
|
||||
map = result.properties;
|
||||
} else {
|
||||
throw new Error('Unexpected member kind: ' + member.kind + ' ' + member.name + ' ' + member.type);
|
||||
}
|
||||
map[member.name] = serializeMember(member);
|
||||
}
|
||||
result.members = clazz.membersArray.map(serializeMember);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
@ -82,9 +67,7 @@ function serializeClass(clazz) {
|
|||
function serializeMember(member) {
|
||||
const result = /** @type {any} */ ({ ...member });
|
||||
sanitize(result);
|
||||
result.args = {};
|
||||
for (const arg of member.argsArray)
|
||||
result.args[arg.name] = serializeProperty(arg);
|
||||
result.args = member.argsArray.map(serializeProperty);
|
||||
if (member.type)
|
||||
result.type = serializeType(member.type)
|
||||
return result;
|
||||
|
|
@ -99,27 +82,27 @@ function serializeProperty(arg) {
|
|||
}
|
||||
|
||||
function sanitize(result) {
|
||||
delete result.kind;
|
||||
delete result.args;
|
||||
delete result.argsArray;
|
||||
delete result.templates;
|
||||
delete result.clazz;
|
||||
if (result.properties && !Object.keys(result.properties).length)
|
||||
delete result.properties;
|
||||
if (result.comment === '')
|
||||
delete result.comment;
|
||||
if (result.returnComment === '')
|
||||
delete result.returnComment;
|
||||
delete result.spec;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Documentation.Type} type
|
||||
*/
|
||||
function serializeType(type) {
|
||||
/** @type {any} */
|
||||
const result = { ...type };
|
||||
if (type.properties && type.properties.length) {
|
||||
result.properties = {};
|
||||
for (const prop of type.properties)
|
||||
result.properties[prop.name] = serializeProperty(prop);
|
||||
} else {
|
||||
delete result.properties;
|
||||
}
|
||||
if (type.properties)
|
||||
result.properties = type.properties.map(serializeProperty);
|
||||
if (type.union)
|
||||
result.union = type.union.map(serializeType);
|
||||
if (type.templates)
|
||||
result.templates = type.templates.map(serializeType);
|
||||
if (type.args)
|
||||
result.args = type.args.map(serializeType);
|
||||
if (type.returnType)
|
||||
result.returnType = serializeType(type.returnType);
|
||||
return result;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -278,19 +278,7 @@ function writeComment(comment, indent = '') {
|
|||
function stringifyComplexType(type, indent, ...namespace) {
|
||||
if (!type)
|
||||
return 'void';
|
||||
let typeString = stringifySimpleType(parseType(type.name));
|
||||
if (type.properties.length && typeString.indexOf('Object') !== -1) {
|
||||
const name = namespace.map(n => n[0].toUpperCase() + n.substring(1)).join('');
|
||||
const shouldExport = exported[name];
|
||||
objectDefinitions.push({name, properties: type.properties});
|
||||
if (shouldExport) {
|
||||
typeString = typeString.replace(/Object/g, name);
|
||||
} else {
|
||||
const objType = stringifyObjectType(type.properties, name, indent);
|
||||
typeString = typeString.replace(/Object/g, objType);
|
||||
}
|
||||
}
|
||||
return typeString;
|
||||
return stringifySimpleType(type, indent, ...namespace);
|
||||
}
|
||||
|
||||
function stringifyObjectType(properties, name, indent = '') {
|
||||
|
|
@ -302,122 +290,47 @@ function stringifyObjectType(properties, name, indent = '') {
|
|||
}
|
||||
|
||||
/**
|
||||
* @param {string} type
|
||||
* @param {Documentation.Type=} type
|
||||
* @returns{string}
|
||||
*/
|
||||
function parseType(type) {
|
||||
type = type.trim();
|
||||
if (type.startsWith('?')) {
|
||||
const parsed = parseType(type.substring(1));
|
||||
parsed.nullable = true;
|
||||
return parsed;
|
||||
}
|
||||
if (type.startsWith('...'))
|
||||
return parseType('Array<' + type.substring(3) + '>');
|
||||
let name = type;
|
||||
let next = null;
|
||||
let template = null;
|
||||
let args = null;
|
||||
let retType = null;
|
||||
let firstTypeLength = type.length;
|
||||
for (let i = 0; i < type.length; i++) {
|
||||
if (type[i] === '<') {
|
||||
name = type.substring(0, i);
|
||||
const matching = matchingBracket(type.substring(i), '<', '>');
|
||||
template = parseType(type.substring(i + 1, i + matching - 1));
|
||||
firstTypeLength = i + matching;
|
||||
break;
|
||||
}
|
||||
if (type[i] === '(') {
|
||||
name = type.substring(0, i);
|
||||
const matching = matchingBracket(type.substring(i), '(', ')');
|
||||
args = parseType(type.substring(i + 1, i + matching - 1));
|
||||
i = i + matching;
|
||||
if (type[i] === ':') {
|
||||
retType = parseType(type.substring(i + 1));
|
||||
next = retType.next;
|
||||
retType.next = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (type[i] === '|' || type[i] === ',') {
|
||||
name = type.substring(0, i);
|
||||
firstTypeLength = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
let pipe = null;
|
||||
if (type[firstTypeLength] === '|')
|
||||
pipe = parseType(type.substring(firstTypeLength + 1));
|
||||
else if (type[firstTypeLength] === ',')
|
||||
next = parseType(type.substring(firstTypeLength + 1));
|
||||
if (name === 'Promise' && !template)
|
||||
template = parseType('void');
|
||||
return {
|
||||
name,
|
||||
args,
|
||||
retType,
|
||||
template,
|
||||
pipe,
|
||||
next
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {string}
|
||||
*/
|
||||
function stringifySimpleType(parsedType) {
|
||||
if (!parsedType)
|
||||
function stringifySimpleType(type, indent = '', ...namespace) {
|
||||
if (!type)
|
||||
return 'void';
|
||||
if (parsedType.name === 'Object' && parsedType.template) {
|
||||
const keyType = stringifySimpleType({
|
||||
...parsedType.template,
|
||||
next: null
|
||||
});
|
||||
const valueType = stringifySimpleType(parsedType.template.next);
|
||||
if (type.name === 'Object' && type.templates) {
|
||||
const keyType = stringifySimpleType(type.templates[0], indent, ...namespace);
|
||||
const valueType = stringifySimpleType(type.templates[1], indent, ...namespace);
|
||||
return `{ [key: ${keyType}]: ${valueType}; }`;
|
||||
}
|
||||
let out = parsedType.name;
|
||||
if (parsedType.args) {
|
||||
let args = parsedType.args;
|
||||
const stringArgs = [];
|
||||
while (args) {
|
||||
const arg = args;
|
||||
args = args.next;
|
||||
arg.next = null;
|
||||
stringArgs.push({
|
||||
type: stringifySimpleType(arg),
|
||||
name: arg.name.toLowerCase()
|
||||
});
|
||||
let out = type.name;
|
||||
if (type.name === 'Object' && type.properties && type.properties.length) {
|
||||
const name = namespace.map(n => n[0].toUpperCase() + n.substring(1)).join('');
|
||||
const shouldExport = exported[name];
|
||||
objectDefinitions.push({name, properties: type.properties});
|
||||
if (shouldExport) {
|
||||
out = name;
|
||||
} else {
|
||||
out = stringifyObjectType(type.properties, name, indent);
|
||||
}
|
||||
out = `((${stringArgs.map(({name, type}) => `${name}: ${type}`).join(', ')}) => ${stringifySimpleType(parsedType.retType)})`;
|
||||
} else if (parsedType.name === 'function') {
|
||||
}
|
||||
|
||||
if (type.args) {
|
||||
const stringArgs = type.args.map(a => ({
|
||||
type: stringifySimpleType(a, indent, ...namespace),
|
||||
name: a.name.toLowerCase()
|
||||
}));
|
||||
out = `((${stringArgs.map(({name, type}) => `${name}: ${type}`).join(', ')}) => ${stringifySimpleType(type.returnType, indent, ...namespace)})`;
|
||||
} else if (type.name === 'function') {
|
||||
out = 'Function';
|
||||
}
|
||||
if (out === 'path')
|
||||
return 'string';
|
||||
if (parsedType.nullable)
|
||||
out = 'null|' + out;
|
||||
if (parsedType.template)
|
||||
out += '<' + stringifySimpleType(parsedType.template) + '>';
|
||||
if (parsedType.pipe)
|
||||
out += '|' + stringifySimpleType(parsedType.pipe);
|
||||
if (parsedType.next)
|
||||
out += ', ' + stringifySimpleType(parsedType.next);
|
||||
if (type.templates)
|
||||
out += '<' + type.templates.map(t => stringifySimpleType(t, indent, ...namespace)).join(', ') + '>';
|
||||
if (type.union)
|
||||
out = type.union.map(t => stringifySimpleType(t, indent, ...namespace)).join('|');
|
||||
return out.trim();
|
||||
}
|
||||
|
||||
function matchingBracket(str, open, close) {
|
||||
let count = 1;
|
||||
let i = 1;
|
||||
for (; i < str.length && count; i++) {
|
||||
if (str[i] === open)
|
||||
count++;
|
||||
else if (str[i] === close)
|
||||
count--;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Documentation.Member} member
|
||||
*/
|
||||
|
|
|
|||
Loading…
Reference in a new issue