docs: support interfaces in doclint (#420)
This commit is contained in:
parent
73b148a0c6
commit
57c3916b0c
|
|
@ -15,14 +15,14 @@
|
|||
*/
|
||||
|
||||
import { BrowserContext, BrowserContextOptions } from './browserContext';
|
||||
import { EventEmitter } from './platform';
|
||||
import * as platform from './platform';
|
||||
|
||||
export class Browser extends EventEmitter {
|
||||
newContext(options?: BrowserContextOptions): Promise<BrowserContext> { throw new Error('Not implemented'); }
|
||||
browserContexts(): BrowserContext[] { throw new Error('Not implemented'); }
|
||||
defaultContext(): BrowserContext { throw new Error('Not implemented'); }
|
||||
export interface Browser extends platform.EventEmitterType {
|
||||
newContext(options?: BrowserContextOptions): Promise<BrowserContext>;
|
||||
browserContexts(): BrowserContext[];
|
||||
defaultContext(): BrowserContext;
|
||||
|
||||
disconnect(): void { throw new Error('Not implemented'); }
|
||||
isConnected(): boolean { throw new Error('Not implemented'); }
|
||||
close(): Promise<void> { throw new Error('Not implemented'); }
|
||||
disconnect(): void;
|
||||
isConnected(): boolean;
|
||||
close(): Promise<void>;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ import { Page, Worker } from '../page';
|
|||
import { CRTarget } from './crTarget';
|
||||
import { Protocol } from './protocol';
|
||||
import { CRPage } from './crPage';
|
||||
import * as browser from '../browser';
|
||||
import { Browser } from '../browser';
|
||||
import * as network from '../network';
|
||||
import * as types from '../types';
|
||||
import * as platform from '../platform';
|
||||
|
|
@ -38,7 +38,7 @@ export type CRConnectOptions = {
|
|||
transport?: ConnectionTransport;
|
||||
};
|
||||
|
||||
export class CRBrowser extends browser.Browser {
|
||||
export class CRBrowser extends platform.EventEmitter implements Browser {
|
||||
_connection: CRConnection;
|
||||
_client: CRSession;
|
||||
private _defaultContext: BrowserContext;
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import * as browser from '../browser';
|
||||
import { Browser } from '../browser';
|
||||
import { BrowserContext, BrowserContextOptions } from '../browserContext';
|
||||
import { Events } from '../events';
|
||||
import { assert, helper, RegisteredListener } from '../helper';
|
||||
|
|
@ -33,7 +33,7 @@ export type FFConnectOptions = {
|
|||
transport?: ConnectionTransport;
|
||||
};
|
||||
|
||||
export class FFBrowser extends browser.Browser {
|
||||
export class FFBrowser extends platform.EventEmitter implements Browser {
|
||||
_connection: FFConnection;
|
||||
_targets: Map<string, Target>;
|
||||
private _defaultContext: BrowserContext;
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import * as browser from '../browser';
|
||||
import { Browser } from '../browser';
|
||||
import { BrowserContext, BrowserContextOptions } from '../browserContext';
|
||||
import { assert, helper, RegisteredListener } from '../helper';
|
||||
import * as network from '../network';
|
||||
|
|
@ -26,13 +26,14 @@ import { Events } from '../events';
|
|||
import { Protocol } from './protocol';
|
||||
import { WKConnection, WKConnectionEvents, WKPageProxySession } from './wkConnection';
|
||||
import { WKPageProxy } from './wkPageProxy';
|
||||
import * as platform from '../platform';
|
||||
|
||||
export type WKConnectOptions = {
|
||||
slowMo?: number,
|
||||
transport: ConnectionTransport;
|
||||
};
|
||||
|
||||
export class WKBrowser extends browser.Browser {
|
||||
export class WKBrowser extends platform.EventEmitter implements Browser {
|
||||
readonly _connection: WKConnection;
|
||||
private readonly _defaultContext: BrowserContext;
|
||||
private readonly _contexts = new Map<string, BrowserContext>();
|
||||
|
|
|
|||
|
|
@ -126,6 +126,7 @@ Documentation.Member = class {
|
|||
* @param {!Array<!Documentation.Member>} argsArray
|
||||
*/
|
||||
constructor(kind, name, type, argsArray, comment = '', returnComment = '', required = true) {
|
||||
if (name === 'code') debugger;
|
||||
this.kind = kind;
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
|
|
|
|||
|
|
@ -45,33 +45,40 @@ function checkSources(sources) {
|
|||
const checker = program.getTypeChecker();
|
||||
const sourceFiles = program.getSourceFiles();
|
||||
const errors = [];
|
||||
const apiClassNames = new Set();
|
||||
/** @type {!Array<!Documentation.Class>} */
|
||||
const classes = [];
|
||||
/** @type {!Map<string, string>} */
|
||||
/** @type {!Map<string, string[]>} */
|
||||
const inheritance = new Map();
|
||||
sourceFiles.filter(x => !x.fileName.includes('node_modules')).map(x => visit(x));
|
||||
const documentation = new Documentation(recreateClassesWithInheritance(classes, inheritance));
|
||||
const documentation = new Documentation(recreateClassesWithInheritance(classes, inheritance).filter(cls => apiClassNames.has(cls.name)));
|
||||
|
||||
return {errors, documentation};
|
||||
|
||||
/**
|
||||
* @param {!Array<!Documentation.Class>} classes
|
||||
* @param {!Map<string, string>} inheritance
|
||||
* @param {!Map<string, string[]>} inheritance
|
||||
* @return {!Array<!Documentation.Class>}
|
||||
*/
|
||||
function recreateClassesWithInheritance(classes, inheritance) {
|
||||
const classesByName = new Map(classes.map(cls => [cls.name, cls]));
|
||||
return classes.map(cls => {
|
||||
const membersMap = new Map();
|
||||
for (let wp = cls; wp; wp = classesByName.get(inheritance.get(wp.name))) {
|
||||
for (const member of wp.membersArray) {
|
||||
const visit = cls => {
|
||||
if (!cls)
|
||||
return;
|
||||
for (const member of cls.membersArray) {
|
||||
// Member was overridden.
|
||||
const memberId = member.kind + ':' + member.name;
|
||||
if (membersMap.has(memberId))
|
||||
continue;
|
||||
membersMap.set(memberId, member);
|
||||
}
|
||||
}
|
||||
const parents = inheritance.get(cls.name) || [];
|
||||
for (const parent of parents)
|
||||
visit(classesByName.get(parent));
|
||||
};
|
||||
visit(cls);
|
||||
return new Documentation.Class(expandPrefix(cls.name), Array.from(membersMap.values()));
|
||||
});
|
||||
}
|
||||
|
|
@ -80,7 +87,8 @@ function checkSources(sources) {
|
|||
* @param {!ts.Node} node
|
||||
*/
|
||||
function visit(node) {
|
||||
if (ts.isClassDeclaration(node) || ts.isClassExpression(node)) {
|
||||
const fileName = node.getSourceFile().fileName;
|
||||
if (ts.isClassDeclaration(node) || ts.isClassExpression(node) || ts.isInterfaceDeclaration(node)) {
|
||||
const symbol = node.name ? checker.getSymbolAtLocation(node.name) : node.symbol;
|
||||
let className = symbol.getName();
|
||||
|
||||
|
|
@ -92,13 +100,12 @@ function checkSources(sources) {
|
|||
}
|
||||
if (className && !excludeClasses.has(className)) {
|
||||
classes.push(serializeClass(className, symbol, node));
|
||||
const parentClassName = parentClass(node);
|
||||
if (parentClassName)
|
||||
inheritance.set(className, parentClassName);
|
||||
inheritance.set(className, parentClasses(node));
|
||||
excludeClasses.add(className);
|
||||
}
|
||||
}
|
||||
const fileName = node.getSourceFile().fileName;
|
||||
if (fileName.endsWith('/api.ts') && ts.isExportSpecifier(node))
|
||||
apiClassNames.add(expandPrefix((node.propertyName || node.name).text));
|
||||
if (!fileName.endsWith('platform.ts') && !fileName.includes('src/server/')) {
|
||||
// Only relative imports.
|
||||
if (ts.isImportDeclaration(node) && ts.isStringLiteral(node.moduleSpecifier)) {
|
||||
|
|
@ -126,17 +133,18 @@ function checkSources(sources) {
|
|||
ts.forEachChild(node, visit);
|
||||
}
|
||||
|
||||
function parentClass(classNode) {
|
||||
function parentClasses(classNode) {
|
||||
const parents = [];
|
||||
for (const herigateClause of classNode.heritageClauses || []) {
|
||||
for (const heritageType of herigateClause.types) {
|
||||
let expression = heritageType.expression;
|
||||
if (expression.kind === ts.SyntaxKind.PropertyAccessExpression)
|
||||
expression = expression.name;
|
||||
if (classNode.name.escapedText !== expression.escapedText)
|
||||
return expression.escapedText;
|
||||
parents.push(expression.escapedText);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
return parents;
|
||||
}
|
||||
|
||||
function serializeSymbol(symbol, circular = []) {
|
||||
|
|
@ -225,6 +233,8 @@ function checkSources(sources) {
|
|||
/** @type {!Array<!Documentation.Member>} */
|
||||
const members = classEvents.get(className) || [];
|
||||
for (const [name, member] of symbol.members || []) {
|
||||
if (className === 'Error')
|
||||
continue;
|
||||
if (name.startsWith('_'))
|
||||
continue;
|
||||
if (EventEmitter.prototype.hasOwnProperty(name))
|
||||
|
|
|
|||
|
|
@ -58,17 +58,9 @@ module.exports = async function lint(page, mdSources, jsSources) {
|
|||
* @return {!Documentation}
|
||||
*/
|
||||
function filterJSDocumentation(jsSources, jsDocumentation) {
|
||||
const apijs = jsSources.find(source => source.name() === 'api.ts');
|
||||
let includedClasses = null;
|
||||
if (apijs) {
|
||||
const api = require(path.join(apijs.filePath(), '..', '..', 'lib', 'api.js'));
|
||||
includedClasses = new Set(Object.keys(api).map(c => jsBuilder.expandPrefix(c)));
|
||||
}
|
||||
// Filter private classes and methods.
|
||||
const classes = [];
|
||||
for (const cls of jsDocumentation.classesArray) {
|
||||
if (includedClasses && !includedClasses.has(cls.name))
|
||||
continue;
|
||||
const members = cls.membersArray.filter(member => !EXCLUDE_PROPERTIES.has(`${cls.name}.${member.name}`));
|
||||
classes.push(new Documentation.Class(cls.name, members));
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue