mirror of
https://github.com/microsoft/playwright.git
synced 2025-01-05 19:04:43 +03:00
chore: move doc-specific code into documentation (#18933)
This commit is contained in:
parent
941090f0c4
commit
03d2b2ecbf
@ -19,7 +19,7 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const md = require('../markdown');
|
||||
const Documentation = require('./documentation');
|
||||
const docs = require('./documentation');
|
||||
|
||||
/** @typedef {import('../markdown').MarkdownNode} MarkdownNode */
|
||||
/** @typedef {import('../markdown').MarkdownHeaderNode} MarkdownHeaderNode */
|
||||
@ -45,7 +45,7 @@ class ApiParser {
|
||||
const params = paramsPath ? md.parse(fs.readFileSync(paramsPath).toString()) : undefined;
|
||||
checkNoDuplicateParamEntries(params);
|
||||
const api = params ? applyTemplates(body, params) : body;
|
||||
/** @type {Map<string, Documentation.Class>} */
|
||||
/** @type {Map<string, documentation.Class>} */
|
||||
this.classes = new Map();
|
||||
md.visitAll(api, node => {
|
||||
if (node.type === 'h1')
|
||||
@ -59,7 +59,7 @@ class ApiParser {
|
||||
if (node.type === 'h3')
|
||||
this.parseArgument(node);
|
||||
});
|
||||
this.documentation = new Documentation([...this.classes.values()]);
|
||||
this.documentation = new docs.Documentation([...this.classes.values()]);
|
||||
this.documentation.index();
|
||||
}
|
||||
|
||||
@ -77,7 +77,7 @@ class ApiParser {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
const clazz = new Documentation.Class(extractMetainfo(node), name, [], extendsName, extractComments(node));
|
||||
const clazz = new docs.Class(extractMetainfo(node), name, [], extendsName, extractComments(node));
|
||||
this.classes.set(clazz.name, clazz);
|
||||
}
|
||||
|
||||
@ -100,16 +100,16 @@ class ApiParser {
|
||||
}
|
||||
}
|
||||
if (!returnType)
|
||||
returnType = new Documentation.Type('void');
|
||||
returnType = new docs.Type('void');
|
||||
|
||||
const comments = extractComments(spec);
|
||||
let member;
|
||||
if (match[1] === 'event')
|
||||
member = Documentation.Member.createEvent(extractMetainfo(spec), name, returnType, comments);
|
||||
member = docs.Member.createEvent(extractMetainfo(spec), name, returnType, comments);
|
||||
if (match[1] === 'property')
|
||||
member = Documentation.Member.createProperty(extractMetainfo(spec), name, returnType, comments, !optional);
|
||||
member = docs.Member.createProperty(extractMetainfo(spec), name, returnType, comments, !optional);
|
||||
if (['method', 'async method', 'optional method', 'optional async method'].includes(match[1])) {
|
||||
member = Documentation.Member.createMethod(extractMetainfo(spec), name, [], returnType, comments);
|
||||
member = docs.Member.createMethod(extractMetainfo(spec), name, [], returnType, comments);
|
||||
if (match[1].includes('async'))
|
||||
member.async = true;
|
||||
if (match[1].includes('optional'))
|
||||
@ -118,7 +118,7 @@ class ApiParser {
|
||||
if (!member)
|
||||
throw new Error('Unknown member: ' + spec.text);
|
||||
|
||||
const clazz = /** @type {Documentation.Class} */(this.classes.get(match[2]));
|
||||
const clazz = /** @type {documentation.Class} */(this.classes.get(match[2]));
|
||||
const existingMember = clazz.membersArray.find(m => m.name === name && m.kind === member.kind);
|
||||
if (existingMember && isTypeOverride(existingMember, member)) {
|
||||
for (const lang of member?.langs?.only || []) {
|
||||
@ -173,8 +173,8 @@ class ApiParser {
|
||||
// match[1] === 'option'
|
||||
let options = method.argsArray.find(o => o.name === 'options');
|
||||
if (!options) {
|
||||
const type = new Documentation.Type('Object', []);
|
||||
options = Documentation.Member.createProperty({ langs: {}, experimental: false, since: 'v1.0' }, 'options', type, undefined, false);
|
||||
const type = new docs.Type('Object', []);
|
||||
options = docs.Member.createProperty({ langs: {}, experimental: false, since: 'v1.0' }, 'options', type, undefined, false);
|
||||
method.argsArray.push(options);
|
||||
}
|
||||
const p = this.parseProperty(spec);
|
||||
@ -196,12 +196,12 @@ class ApiParser {
|
||||
const name = text.substring(0, typeStart).replace(/\`/g, '').trim();
|
||||
const comments = extractComments(spec);
|
||||
const { type, optional } = this.parseType(/** @type {MarkdownLiNode} */(param));
|
||||
return Documentation.Member.createProperty(extractMetainfo(spec), name, type, comments, !optional);
|
||||
return docs.Member.createProperty(extractMetainfo(spec), name, type, comments, !optional);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {MarkdownLiNode} spec
|
||||
* @return {{ type: Documentation.Type, optional: boolean, experimental: boolean }}
|
||||
* @return {{ type: documentation.Type, optional: boolean, experimental: boolean }}
|
||||
*/
|
||||
parseType(spec) {
|
||||
const arg = parseVariable(spec.text);
|
||||
@ -210,9 +210,9 @@ class ApiParser {
|
||||
const { name, text } = parseVariable(/** @type {string} */(child.text));
|
||||
const comments = /** @type {MarkdownNode[]} */ ([{ type: 'text', text }]);
|
||||
const childType = this.parseType(child);
|
||||
properties.push(Documentation.Member.createProperty({ langs: {}, experimental: childType.experimental, since: 'v1.0' }, name, childType.type, comments, !childType.optional));
|
||||
properties.push(docs.Member.createProperty({ langs: {}, experimental: childType.experimental, since: 'v1.0' }, name, childType.type, comments, !childType.optional));
|
||||
}
|
||||
const type = Documentation.Type.parse(arg.type, properties);
|
||||
const type = docs.Type.parse(arg.type, properties);
|
||||
return { type, optional: arg.optional, experimental: arg.experimental };
|
||||
}
|
||||
}
|
||||
@ -415,8 +415,8 @@ function childrenWithoutProperties(spec) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Documentation.Member} existingMember
|
||||
* @param {Documentation.Member} member
|
||||
* @param {documentation.Member} existingMember
|
||||
* @param {documentation.Member} member
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function isTypeOverride(existingMember, member) {
|
||||
|
@ -23,6 +23,7 @@ const path = require('path');
|
||||
const { parseApi } = require('./api_parser');
|
||||
const missingDocs = require('./missingDocs');
|
||||
const md = require('../markdown');
|
||||
const docs = require('./documentation');
|
||||
|
||||
/** @typedef {import('./documentation').Type} Type */
|
||||
/** @typedef {import('../markdown').MarkdownNode} MarkdownNode */
|
||||
@ -189,7 +190,7 @@ async function run() {
|
||||
const data = fs.readFileSync(filePath, 'utf-8');
|
||||
let rootNode = md.filterNodesForLanguage(md.parse(data), lang);
|
||||
// Validates code snippet groups.
|
||||
rootNode = md.processCodeGroups(rootNode, lang, tabs => tabs.map(tab => tab.spec));
|
||||
rootNode = docs.processCodeGroups(rootNode, lang, tabs => tabs.map(tab => tab.spec));
|
||||
// Renders links.
|
||||
documentation.renderLinksInText(rootNode);
|
||||
// Validate links.
|
||||
|
@ -36,15 +36,15 @@ const md = require('../markdown');
|
||||
* @typedef {{
|
||||
* only?: string[],
|
||||
* aliases?: Object<string, string>,
|
||||
* types?: Object<string, Documentation.Type>,
|
||||
* overrides?: Object<string, Documentation.Member>,
|
||||
* types?: Object<string, Type>,
|
||||
* overrides?: Object<string, Member>,
|
||||
* }} Langs
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {function({
|
||||
* clazz?: Documentation.Class,
|
||||
* member?: Documentation.Member,
|
||||
* clazz?: Class,
|
||||
* member?: Member,
|
||||
* param?: string,
|
||||
* option?: string,
|
||||
* href?: string,
|
||||
@ -65,13 +65,19 @@ const md = require('../markdown');
|
||||
* }} LanguageOptions
|
||||
*/
|
||||
|
||||
/** @typedef {{
|
||||
* value: string, groupId: string, spec: MarkdownNode
|
||||
* }} CodeGroup */
|
||||
|
||||
/** @typedef {function(CodeGroup[]): MarkdownNode[]} CodeGroupTransformer */
|
||||
|
||||
class Documentation {
|
||||
/**
|
||||
* @param {!Array<!Documentation.Class>} classesArray
|
||||
* @param {!Array<!Class>} classesArray
|
||||
*/
|
||||
constructor(classesArray) {
|
||||
this.classesArray = classesArray;
|
||||
/** @type {!Map<string, !Documentation.Class>} */
|
||||
/** @type {!Map<string, !Class>} */
|
||||
this.classes = new Map();
|
||||
this.index();
|
||||
}
|
||||
@ -147,7 +153,7 @@ class Documentation {
|
||||
* @param {Renderer} linkRenderer
|
||||
*/
|
||||
setLinkRenderer(linkRenderer) {
|
||||
// @type {Map<string, Documentation.Class>}
|
||||
// @type {Map<string, Class>}
|
||||
const classesMap = new Map();
|
||||
const membersMap = new Map();
|
||||
for (const clazz of this.classesArray) {
|
||||
@ -156,7 +162,7 @@ class Documentation {
|
||||
membersMap.set(`${member.kind}: ${clazz.name}.${member.name}`, member);
|
||||
}
|
||||
/**
|
||||
* @param {Documentation.Class|Documentation.Member|null} classOrMember
|
||||
* @param {Class|Member|null} classOrMember
|
||||
* @param {MarkdownNode[] | undefined} nodes
|
||||
*/
|
||||
this._patchLinks = (classOrMember, nodes) => patchLinks(classOrMember, nodes, classesMap, membersMap, linkRenderer);
|
||||
@ -174,7 +180,7 @@ class Documentation {
|
||||
|
||||
/**
|
||||
* @param {string} lang
|
||||
* @param {import('../markdown').CodeGroupTransformer} transformer
|
||||
* @param {CodeGroupTransformer} transformer
|
||||
*/
|
||||
setCodeGroupsTransformer(lang, transformer) {
|
||||
this._codeGroupsTransformer = { lang, transformer };
|
||||
@ -185,7 +191,7 @@ class Documentation {
|
||||
clazz.visit(item => {
|
||||
let spec = item.spec;
|
||||
if (spec && this._codeGroupsTransformer)
|
||||
spec = md.processCodeGroups(spec, this._codeGroupsTransformer.lang, this._codeGroupsTransformer.transformer);
|
||||
spec = processCodeGroups(spec, this._codeGroupsTransformer.lang, this._codeGroupsTransformer.transformer);
|
||||
item.comment = generateSourceCodeComment(spec);
|
||||
});
|
||||
}
|
||||
@ -196,11 +202,11 @@ class Documentation {
|
||||
}
|
||||
}
|
||||
|
||||
Documentation.Class = class {
|
||||
class Class {
|
||||
/**
|
||||
* @param {Metainfo} metainfo
|
||||
* @param {string} name
|
||||
* @param {!Array<!Documentation.Member>} membersArray
|
||||
* @param {!Array<!Member>} membersArray
|
||||
* @param {?string=} extendsName
|
||||
* @param {MarkdownNode[]=} spec
|
||||
*/
|
||||
@ -216,19 +222,19 @@ Documentation.Class = class {
|
||||
this.index();
|
||||
const match = /** @type {string[]} */(name.match(/(API|JS|CDP|[A-Z])(.*)/));
|
||||
this.varName = match[1].toLowerCase() + match[2];
|
||||
/** @type {!Map<string, !Documentation.Member>} */
|
||||
/** @type {!Map<string, !Member>} */
|
||||
this.members = new Map();
|
||||
/** @type {!Map<string, !Documentation.Member>} */
|
||||
/** @type {!Map<string, !Member>} */
|
||||
this.properties = new Map();
|
||||
/** @type {!Array<!Documentation.Member>} */
|
||||
/** @type {!Array<!Member>} */
|
||||
this.propertiesArray = [];
|
||||
/** @type {!Map<string, !Documentation.Member>} */
|
||||
/** @type {!Map<string, !Member>} */
|
||||
this.methods = new Map();
|
||||
/** @type {!Array<!Documentation.Member>} */
|
||||
/** @type {!Array<!Member>} */
|
||||
this.methodsArray = [];
|
||||
/** @type {!Map<string, !Documentation.Member>} */
|
||||
/** @type {!Map<string, !Member>} */
|
||||
this.events = new Map();
|
||||
/** @type {!Array<!Documentation.Member>} */
|
||||
/** @type {!Array<!Member>} */
|
||||
this.eventsArray = [];
|
||||
}
|
||||
|
||||
@ -259,7 +265,7 @@ Documentation.Class = class {
|
||||
}
|
||||
|
||||
clone() {
|
||||
const cls = new Documentation.Class({ langs: this.langs, experimental: this.experimental, since: this.since }, this.name, this.membersArray.map(m => m.clone()), this.extends, this.spec);
|
||||
const cls = new Class({ langs: this.langs, experimental: this.experimental, since: this.since }, this.name, this.membersArray.map(m => m.clone()), this.extends, this.spec);
|
||||
cls.comment = this.comment;
|
||||
return cls;
|
||||
}
|
||||
@ -335,7 +341,7 @@ Documentation.Class = class {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {function(Documentation.Member|Documentation.Class): void} visitor
|
||||
* @param {function(Member|Class): void} visitor
|
||||
*/
|
||||
visit(visitor) {
|
||||
visitor(this);
|
||||
@ -348,13 +354,13 @@ Documentation.Class = class {
|
||||
}
|
||||
};
|
||||
|
||||
Documentation.Member = class {
|
||||
class Member {
|
||||
/**
|
||||
* @param {string} kind
|
||||
* @param {Metainfo} metainfo
|
||||
* @param {string} name
|
||||
* @param {?Documentation.Type} type
|
||||
* @param {!Array<!Documentation.Member>} argsArray
|
||||
* @param {?Type} type
|
||||
* @param {!Array<!Member>} argsArray
|
||||
* @param {MarkdownNode[]=} spec
|
||||
* @param {boolean=} required
|
||||
*/
|
||||
@ -369,12 +375,12 @@ Documentation.Member = class {
|
||||
this.argsArray = argsArray;
|
||||
this.required = required;
|
||||
this.comment = '';
|
||||
/** @type {!Map<string, !Documentation.Member>} */
|
||||
/** @type {!Map<string, !Member>} */
|
||||
this.args = new Map();
|
||||
this.index();
|
||||
/** @type {!Documentation.Class | null} */
|
||||
/** @type {!Class | null} */
|
||||
this.clazz = null;
|
||||
/** @type {Documentation.Member=} */
|
||||
/** @type {Member=} */
|
||||
this.enclosingMethod = undefined;
|
||||
this.deprecated = false;
|
||||
if (spec) {
|
||||
@ -466,7 +472,7 @@ Documentation.Member = class {
|
||||
}
|
||||
|
||||
clone() {
|
||||
const result = new Documentation.Member(this.kind, { langs: this.langs, experimental: this.experimental, since: this.since }, this.name, this.type?.clone(), this.argsArray.map(arg => arg.clone()), this.spec, this.required);
|
||||
const result = new Member(this.kind, { langs: this.langs, experimental: this.experimental, since: this.since }, this.name, this.type?.clone(), this.argsArray.map(arg => arg.clone()), this.spec, this.required);
|
||||
result.alias = this.alias;
|
||||
result.async = this.async;
|
||||
result.paramOrOption = this.paramOrOption;
|
||||
@ -476,40 +482,40 @@ Documentation.Member = class {
|
||||
/**
|
||||
* @param {Metainfo} metainfo
|
||||
* @param {string} name
|
||||
* @param {!Array<!Documentation.Member>} argsArray
|
||||
* @param {?Documentation.Type} returnType
|
||||
* @param {!Array<!Member>} argsArray
|
||||
* @param {?Type} returnType
|
||||
* @param {MarkdownNode[]=} spec
|
||||
* @return {!Documentation.Member}
|
||||
* @return {!Member}
|
||||
*/
|
||||
static createMethod(metainfo, name, argsArray, returnType, spec) {
|
||||
return new Documentation.Member('method', metainfo, name, returnType, argsArray, spec);
|
||||
return new Member('method', metainfo, name, returnType, argsArray, spec);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Metainfo} metainfo
|
||||
* @param {!string} name
|
||||
* @param {!Documentation.Type} type
|
||||
* @param {!Type} type
|
||||
* @param {!MarkdownNode[]=} spec
|
||||
* @param {boolean=} required
|
||||
* @return {!Documentation.Member}
|
||||
* @return {!Member}
|
||||
*/
|
||||
static createProperty(metainfo, name, type, spec, required) {
|
||||
return new Documentation.Member('property', metainfo, name, type, [], spec, required);
|
||||
return new Member('property', metainfo, name, type, [], spec, required);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Metainfo} metainfo
|
||||
* @param {string} name
|
||||
* @param {?Documentation.Type=} type
|
||||
* @param {?Type=} type
|
||||
* @param {MarkdownNode[]=} spec
|
||||
* @return {!Documentation.Member}
|
||||
* @return {!Member}
|
||||
*/
|
||||
static createEvent(metainfo, name, type = null, spec) {
|
||||
return new Documentation.Member('event', metainfo, name, type, [], spec);
|
||||
return new Member('event', metainfo, name, type, [], spec);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {function(Documentation.Member|Documentation.Class): void} visitor
|
||||
* @param {function(Member|Class): void} visitor
|
||||
*/
|
||||
visit(visitor) {
|
||||
visitor(this);
|
||||
@ -520,15 +526,15 @@ Documentation.Member = class {
|
||||
}
|
||||
};
|
||||
|
||||
Documentation.Type = class {
|
||||
class Type {
|
||||
/**
|
||||
* @param {string} expression
|
||||
* @param {!Array<!Documentation.Member>=} properties
|
||||
* @return {Documentation.Type}
|
||||
* @param {!Array<!Member>=} properties
|
||||
* @return {Type}
|
||||
*/
|
||||
static parse(expression, properties = []) {
|
||||
expression = expression.replace(/\\\(/g, '(').replace(/\\\)/g, ')');
|
||||
const type = Documentation.Type.fromParsedType(parseTypeExpression(expression));
|
||||
const type = Type.fromParsedType(parseTypeExpression(expression));
|
||||
type.expression = expression;
|
||||
if (type.name === 'number')
|
||||
throw new Error('Number types should be either int or float, not number in: ' + expression);
|
||||
@ -550,7 +556,7 @@ Documentation.Type = class {
|
||||
|
||||
/**
|
||||
* @param {ParsedType} parsedType
|
||||
* @return {Documentation.Type}
|
||||
* @return {Type}
|
||||
*/
|
||||
static fromParsedType(parsedType, inUnion = false) {
|
||||
if (!inUnion && !parsedType.unionName && isStringUnion(parsedType) ) {
|
||||
@ -558,12 +564,12 @@ Documentation.Type = class {
|
||||
}
|
||||
|
||||
if (!inUnion && (parsedType.union || parsedType.unionName)) {
|
||||
const type = new Documentation.Type(parsedType.unionName || '');
|
||||
const type = new Type(parsedType.unionName || '');
|
||||
type.union = [];
|
||||
// @ts-ignore
|
||||
for (let t = parsedType; t; t = t.union) {
|
||||
const nestedUnion = !!t.unionName && t !== parsedType;
|
||||
type.union.push(Documentation.Type.fromParsedType(t, !nestedUnion));
|
||||
type.union.push(Type.fromParsedType(t, !nestedUnion));
|
||||
if (nestedUnion)
|
||||
break;
|
||||
}
|
||||
@ -571,41 +577,41 @@ Documentation.Type = class {
|
||||
}
|
||||
|
||||
if (parsedType.args) {
|
||||
const type = new Documentation.Type('function');
|
||||
const type = new Type('function');
|
||||
type.args = [];
|
||||
// @ts-ignore
|
||||
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) : undefined;
|
||||
type.args.push(Type.fromParsedType(t));
|
||||
type.returnType = parsedType.retType ? Type.fromParsedType(parsedType.retType) : undefined;
|
||||
return type;
|
||||
}
|
||||
|
||||
if (parsedType.template) {
|
||||
const type = new Documentation.Type(parsedType.name);
|
||||
const type = new Type(parsedType.name);
|
||||
type.templates = [];
|
||||
// @ts-ignore
|
||||
for (let t = parsedType.template; t; t = t.next)
|
||||
type.templates.push(Documentation.Type.fromParsedType(t));
|
||||
type.templates.push(Type.fromParsedType(t));
|
||||
return type;
|
||||
}
|
||||
return new Documentation.Type(parsedType.name);
|
||||
return new Type(parsedType.name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} name
|
||||
* @param {!Array<!Documentation.Member>=} properties
|
||||
* @param {!Array<!Member>=} properties
|
||||
*/
|
||||
constructor(name, properties) {
|
||||
this.name = name.replace(/^\[/, '').replace(/\]$/, '');
|
||||
/** @type {Documentation.Member[] | undefined} */
|
||||
/** @type {Member[] | undefined} */
|
||||
this.properties = this.name === 'Object' ? properties : undefined;
|
||||
/** @type {Documentation.Type[] | undefined} */
|
||||
/** @type {Type[] | undefined} */
|
||||
this.union;
|
||||
/** @type {Documentation.Type[] | undefined} */
|
||||
/** @type {Type[] | undefined} */
|
||||
this.args;
|
||||
/** @type {Documentation.Type | undefined} */
|
||||
/** @type {Type | undefined} */
|
||||
this.returnType;
|
||||
/** @type {Documentation.Type[] | undefined} */
|
||||
/** @type {Type[] | undefined} */
|
||||
this.templates;
|
||||
/** @type {string | undefined} */
|
||||
this.expression;
|
||||
@ -621,7 +627,7 @@ Documentation.Type = class {
|
||||
}
|
||||
|
||||
clone() {
|
||||
const type = new Documentation.Type(this.name, this.properties ? this.properties.map(prop => prop.clone()) : undefined);
|
||||
const type = new Type(this.name, this.properties ? this.properties.map(prop => prop.clone()) : undefined);
|
||||
if (this.union)
|
||||
type.union = this.union.map(type => type.clone());
|
||||
if (this.args)
|
||||
@ -635,7 +641,7 @@ Documentation.Type = class {
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {Documentation.Member[]}
|
||||
* @returns {Member[]}
|
||||
*/
|
||||
deepProperties() {
|
||||
const types = [];
|
||||
@ -648,7 +654,7 @@ Documentation.Type = class {
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {Documentation.Member[] | undefined}
|
||||
* @returns {Member[] | undefined}
|
||||
*/
|
||||
sortedProperties() {
|
||||
if (!this.properties)
|
||||
@ -689,7 +695,7 @@ Documentation.Type = class {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Documentation.Type[]} result
|
||||
* @param {Type[]} result
|
||||
*/
|
||||
_collectAllTypes(result) {
|
||||
result.push(this);
|
||||
@ -795,10 +801,10 @@ function matchingBracket(str, open, close) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Documentation.Class|Documentation.Member|null} classOrMember
|
||||
* @param {Class|Member|null} classOrMember
|
||||
* @param {MarkdownNode[]|undefined} spec
|
||||
* @param {Map<string, Documentation.Class>} classesMap
|
||||
* @param {Map<string, Documentation.Member>} membersMap
|
||||
* @param {Map<string, Class>} classesMap
|
||||
* @param {Map<string, Member>} membersMap
|
||||
* @param {Renderer} linkRenderer
|
||||
*/
|
||||
function patchLinks(classOrMember, spec, classesMap, membersMap, linkRenderer) {
|
||||
@ -849,6 +855,8 @@ function generateSourceCodeComment(spec) {
|
||||
md.visitAll(comments, node => {
|
||||
if (node.type === 'li' && node.liType === 'bullet')
|
||||
node.liType = 'default';
|
||||
if (node.type === 'code' && node.codeLang)
|
||||
node.codeLang = parseCodeLang(node.codeLang).highlighter;
|
||||
if (node.type === 'note') {
|
||||
// @ts-ignore
|
||||
node.type = 'text';
|
||||
@ -859,7 +867,7 @@ function generateSourceCodeComment(spec) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Documentation.Member} optionsArg
|
||||
* @param {Member} optionsArg
|
||||
* @param {LanguageOptions=} options
|
||||
*/
|
||||
function patchCSharpOptionOverloads(optionsArg, options = {}) {
|
||||
@ -927,4 +935,88 @@ function csharpOptionOverloadSuffix(option, type) {
|
||||
throw new Error(`CSharp option "${option}" has unsupported type overload "${type}"`);
|
||||
}
|
||||
|
||||
module.exports = Documentation;
|
||||
/**
|
||||
* @param {MarkdownNode[]} spec
|
||||
* @param {string} language
|
||||
* @param {CodeGroupTransformer} transformer
|
||||
* @returns {MarkdownNode[]}
|
||||
*/
|
||||
function processCodeGroups(spec, language, transformer) {
|
||||
/** @type {MarkdownNode[]} */
|
||||
const newSpec = [];
|
||||
for (let i = 0; i < spec.length; ++i) {
|
||||
/** @type {{value: string, groupId: string, spec: MarkdownNode}[]} */
|
||||
const tabs = [];
|
||||
for (;i < spec.length; i++) {
|
||||
const codeLang = spec[i].codeLang;
|
||||
if (!codeLang)
|
||||
break;
|
||||
let parsed;
|
||||
try {
|
||||
parsed = parseCodeLang(codeLang);
|
||||
} catch (e) {
|
||||
throw new Error(e.message + '\n while processing:\n' + md.render([spec[i]]));
|
||||
}
|
||||
if (!parsed.codeGroup)
|
||||
break;
|
||||
if (parsed.language && parsed.language !== language)
|
||||
continue;
|
||||
const [groupId, value] = parsed.codeGroup.split('-');
|
||||
const clone = md.clone(spec[i]);
|
||||
clone.codeLang = parsed.highlighter;
|
||||
tabs.push({ groupId, value, spec: clone });
|
||||
}
|
||||
if (tabs.length) {
|
||||
if (tabs.length === 1)
|
||||
throw new Error(`Lonely tab "${tabs[0].spec.codeLang}". Make sure there are at least two tabs in the group.\n` + md.render([tabs[0].spec]));
|
||||
|
||||
// Validate group consistency.
|
||||
const groupId = tabs[0].groupId;
|
||||
const values = new Set();
|
||||
for (const tab of tabs) {
|
||||
if (tab.groupId !== groupId)
|
||||
throw new Error('Mixed group ids: ' + md.render(spec));
|
||||
if (values.has(tab.value))
|
||||
throw new Error(`Duplicated tab "${tab.value}"\n` + md.render(tabs.map(tab => tab.spec)));
|
||||
values.add(tab.value);
|
||||
}
|
||||
|
||||
// Append transformed nodes.
|
||||
newSpec.push(...transformer(tabs));
|
||||
}
|
||||
if (i < spec.length)
|
||||
newSpec.push(spec[i]);
|
||||
}
|
||||
return newSpec;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} codeLang
|
||||
* @return {{ highlighter: string, language: string|undefined, codeGroup: string|undefined}}
|
||||
*/
|
||||
function parseCodeLang(codeLang) {
|
||||
if (codeLang === 'python async')
|
||||
return { highlighter: 'py', codeGroup: 'python-async', language: 'python' };
|
||||
if (codeLang === 'python sync')
|
||||
return { highlighter: 'py', codeGroup: 'python-sync', language: 'python' };
|
||||
|
||||
const [highlighter] = codeLang.split(' ');
|
||||
if (!highlighter)
|
||||
throw new Error(`Cannot parse code block lang: "${codeLang}"`);
|
||||
|
||||
const languageMatch = codeLang.match(/ lang=([\w\d]+)/);
|
||||
let language = languageMatch ? languageMatch[1] : undefined;
|
||||
if (!language) {
|
||||
if (highlighter === 'ts')
|
||||
language = 'js';
|
||||
else if (highlighter === 'py')
|
||||
language = 'python';
|
||||
else if (['js', 'python', 'csharp', 'java'].includes(highlighter))
|
||||
language = highlighter;
|
||||
}
|
||||
|
||||
const tabMatch = codeLang.match(/ tab=([\w\d-]+)/);
|
||||
return { highlighter, language, codeGroup: tabMatch ? tabMatch[1] : '' };
|
||||
}
|
||||
|
||||
module.exports = { Documentation, Class, Member, Type, processCodeGroups, parseCodeLang };
|
||||
|
@ -18,7 +18,7 @@
|
||||
const path = require('path');
|
||||
const toKebabCase = require('lodash/kebabCase')
|
||||
const devices = require('../../packages/playwright-core/lib/server/deviceDescriptors');
|
||||
const Documentation = require('../doclint/documentation');
|
||||
const docs = require('../doclint/documentation');
|
||||
const PROJECT_DIR = path.join(__dirname, '..', '..');
|
||||
const fs = require('fs');
|
||||
const { parseOverrides } = require('./parseOverrides');
|
||||
@ -30,7 +30,7 @@ Error.stackTraceLimit = 50;
|
||||
class TypesGenerator {
|
||||
/**
|
||||
* @param {{
|
||||
* documentation: Documentation,
|
||||
* documentation: docs.Documentation,
|
||||
* overridesToDocsClassMapping?: Map<string, string>,
|
||||
* ignoreMissing?: Set<string>,
|
||||
* doNotExportClassNames?: Set<string>,
|
||||
@ -39,7 +39,7 @@ class TypesGenerator {
|
||||
* }} options
|
||||
*/
|
||||
constructor(options) {
|
||||
/** @type {Array<{name: string, properties: Documentation.Member[]}>} */
|
||||
/** @type {Array<{name: string, properties: docs.Member[]}>} */
|
||||
this.objectDefinitions = [];
|
||||
/** @type {Set<string>} */
|
||||
this.handledMethods = new Set();
|
||||
@ -77,6 +77,8 @@ class TypesGenerator {
|
||||
return `\`${option}\``;
|
||||
if (clazz)
|
||||
return `[${clazz.name}]`;
|
||||
if (!member || !member.clazz)
|
||||
throw new Error('Internal error');
|
||||
const className = member.clazz.varName === 'playwrightAssertions' ? '' : member.clazz.varName + '.';
|
||||
if (member.kind === 'method')
|
||||
return createMarkdownLink(member, `${className}${member.alias}(${this.renderJSSignature(member.argsArray)})`);
|
||||
@ -203,7 +205,7 @@ class TypesGenerator {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Documentation.Class} classDesc
|
||||
* @param {docs.Class} classDesc
|
||||
*/
|
||||
classToString(classDesc) {
|
||||
const parts = [];
|
||||
@ -229,7 +231,7 @@ class TypesGenerator {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Documentation.Class} classDesc
|
||||
* @param {docs.Class} classDesc
|
||||
*/
|
||||
hasUniqueEvents(classDesc) {
|
||||
if (!classDesc.events.size)
|
||||
@ -241,7 +243,7 @@ class TypesGenerator {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Documentation.Class} classDesc
|
||||
* @param {docs.Class} classDesc
|
||||
*/
|
||||
createEventDescriptions(classDesc) {
|
||||
if (!this.hasUniqueEvents(classDesc))
|
||||
@ -263,7 +265,7 @@ class TypesGenerator {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Documentation.Class} classDesc
|
||||
* @param {docs.Class} classDesc
|
||||
* @param {boolean=} exportMembersAsGlobals
|
||||
*/
|
||||
classBody(classDesc, exportMembersAsGlobals) {
|
||||
@ -319,8 +321,8 @@ class TypesGenerator {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Documentation.Class} classDesc
|
||||
* @param {Documentation.Member} member
|
||||
* @param {docs.Class} classDesc
|
||||
* @param {docs.Member} member
|
||||
*/
|
||||
hasOwnMethod(classDesc, member) {
|
||||
if (this.handledMethods.has(`${classDesc.name}.${member.alias}#${member.overloadIndex}`))
|
||||
@ -333,7 +335,7 @@ class TypesGenerator {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Documentation.Class} classDesc
|
||||
* @param {docs.Class} classDesc
|
||||
*/
|
||||
parentClass(classDesc) {
|
||||
if (!classDesc.extends)
|
||||
@ -378,7 +380,7 @@ class TypesGenerator {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Documentation.Type} type
|
||||
* @param {docs.Type} type
|
||||
*/
|
||||
stringifyComplexType(type, indent, ...namespace) {
|
||||
if (!type)
|
||||
@ -387,7 +389,7 @@ class TypesGenerator {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Documentation.Member[]} properties
|
||||
* @param {docs.Member[]} properties
|
||||
* @param {string} name
|
||||
* @param {string=} indent
|
||||
* @returns {string}
|
||||
@ -406,7 +408,7 @@ class TypesGenerator {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Documentation.Type=} type
|
||||
* @param {docs.Type=} type
|
||||
* @returns{string}
|
||||
*/
|
||||
stringifySimpleType(type, indent = '', ...namespace) {
|
||||
@ -455,7 +457,7 @@ class TypesGenerator {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Documentation.Member} member
|
||||
* @param {docs.Member} member
|
||||
*/
|
||||
argsFromMember(member, indent, ...namespace) {
|
||||
if (member.kind === 'property')
|
||||
@ -464,7 +466,7 @@ class TypesGenerator {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Documentation.Member} member
|
||||
* @param {docs.Member} member
|
||||
* @param {string} indent
|
||||
*/
|
||||
memberJSDOC(member, indent) {
|
||||
@ -480,7 +482,7 @@ class TypesGenerator {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Documentation.Member[]} args
|
||||
* @param {docs.Member[]} args
|
||||
*/
|
||||
renderJSSignature(args) {
|
||||
const tokens = [];
|
||||
|
@ -62,12 +62,6 @@
|
||||
* lines: string[],
|
||||
* }} MarkdownPropsNode */
|
||||
|
||||
/** @typedef {{
|
||||
* value: string, groupId: string, spec: MarkdownNode
|
||||
* }} CodeGroup */
|
||||
|
||||
/** @typedef {function(CodeGroup[]): MarkdownNode[]} CodeGroupTransformer */
|
||||
|
||||
/** @typedef {MarkdownTextNode | MarkdownLiNode | MarkdownCodeNode | MarkdownNoteNode | MarkdownHeaderNode | MarkdownNullNode | MarkdownPropsNode } MarkdownNode */
|
||||
|
||||
function flattenWrappedLines(content) {
|
||||
@ -310,10 +304,7 @@ function innerRenderMdNode(indent, node, lastNode, result, maxColumns) {
|
||||
|
||||
if (node.type === 'code') {
|
||||
newLine();
|
||||
if (process.env.API_JSON_MODE)
|
||||
result.push(`${indent}\`\`\`${node.codeLang}`);
|
||||
else
|
||||
result.push(`${indent}\`\`\`${node.codeLang ? parseCodeLang(node.codeLang).highlighter : ''}`);
|
||||
result.push(`${indent}\`\`\`${node.codeLang}`);
|
||||
for (const line of node.lines)
|
||||
result.push(indent + line);
|
||||
result.push(`${indent}\`\`\``);
|
||||
@ -473,86 +464,4 @@ function filterNodesForLanguage(nodes, language) {
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} codeLang
|
||||
* @return {{ highlighter: string, language: string|undefined, codeGroup: string|undefined}}
|
||||
*/
|
||||
function parseCodeLang(codeLang) {
|
||||
if (codeLang === 'python async')
|
||||
return { highlighter: 'py', codeGroup: 'python-async', language: 'python' };
|
||||
if (codeLang === 'python sync')
|
||||
return { highlighter: 'py', codeGroup: 'python-sync', language: 'python' };
|
||||
|
||||
const [highlighter] = codeLang.split(' ');
|
||||
if (!highlighter)
|
||||
throw new Error(`Cannot parse code block lang: "${codeLang}"`);
|
||||
|
||||
const languageMatch = codeLang.match(/ lang=([\w\d]+)/);
|
||||
let language = languageMatch ? languageMatch[1] : undefined;
|
||||
if (!language) {
|
||||
if (highlighter === 'ts')
|
||||
language = 'js';
|
||||
else if (highlighter === 'py')
|
||||
language = 'python';
|
||||
else if (['js', 'python', 'csharp', 'java'].includes(highlighter))
|
||||
language = highlighter;
|
||||
}
|
||||
|
||||
const tabMatch = codeLang.match(/ tab=([\w\d-]+)/);
|
||||
return { highlighter, language, codeGroup: tabMatch ? tabMatch[1] : '' };
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {MarkdownNode[]} spec
|
||||
* @param {string} language
|
||||
* @param {CodeGroupTransformer} transformer
|
||||
* @returns {MarkdownNode[]}
|
||||
*/
|
||||
function processCodeGroups(spec, language, transformer) {
|
||||
/** @type {MarkdownNode[]} */
|
||||
const newSpec = [];
|
||||
for (let i = 0; i < spec.length; ++i) {
|
||||
/** @type {{value: string, groupId: string, spec: MarkdownNode}[]} */
|
||||
const tabs = [];
|
||||
for (;i < spec.length; i++) {
|
||||
const codeLang = spec[i].codeLang;
|
||||
if (!codeLang)
|
||||
break;
|
||||
let parsed;
|
||||
try {
|
||||
parsed = parseCodeLang(codeLang);
|
||||
} catch (e) {
|
||||
throw new Error(e.message + '\n while processing:\n' + render([spec[i]]));
|
||||
}
|
||||
if (!parsed.codeGroup)
|
||||
break;
|
||||
if (parsed.language && parsed.language !== language)
|
||||
continue;
|
||||
const [groupId, value] = parsed.codeGroup.split('-');
|
||||
tabs.push({ groupId, value, spec: spec[i] });
|
||||
}
|
||||
if (tabs.length) {
|
||||
if (tabs.length === 1)
|
||||
throw new Error(`Lonely tab "${tabs[0].spec.codeLang}". Make sure there are at least two tabs in the group.\n` + render([tabs[0].spec]));
|
||||
|
||||
// Validate group consistency.
|
||||
const groupId = tabs[0].groupId;
|
||||
const values = new Set();
|
||||
for (const tab of tabs) {
|
||||
if (tab.groupId !== groupId)
|
||||
throw new Error('Mixed group ids: ' + render(spec));
|
||||
if (values.has(tab.value))
|
||||
throw new Error(`Duplicated tab "${tab.value}"\n` + render(tabs.map(tab => tab.spec)));
|
||||
values.add(tab.value);
|
||||
}
|
||||
|
||||
// Append transformed nodes.
|
||||
newSpec.push(...transformer(tabs));
|
||||
}
|
||||
if (i < spec.length)
|
||||
newSpec.push(spec[i]);
|
||||
}
|
||||
return newSpec;
|
||||
}
|
||||
|
||||
module.exports = { parse, render, clone, visitAll, visit, generateToc, filterNodesForLanguage, parseCodeLang, processCodeGroups };
|
||||
module.exports = { parse, render, clone, visitAll, visit, generateToc, filterNodesForLanguage };
|
||||
|
Loading…
Reference in New Issue
Block a user