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