@microsoft/api-extractor
Version:
Validatation, documentation, and auditing for the exported API of a TypeScript package
184 lines (182 loc) • 8.68 kB
JavaScript
/* tslint:disable:no-bitwise */
;
var __extends = (this && this.__extends) || (function () {
var extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
var ts = require("typescript");
var ApiMethod_1 = require("./ApiMethod");
var ApiProperty_1 = require("./ApiProperty");
var ApiItem_1 = require("./ApiItem");
var ApiItemContainer_1 = require("./ApiItemContainer");
var TypeScriptHelpers_1 = require("../TypeScriptHelpers");
var PrettyPrinter_1 = require("../PrettyPrinter");
/**
* This class is part of the ApiItem abstract syntax tree. It represents a class,
* interface, or type literal expression.
*/
var ApiStructuredType = (function (_super) {
__extends(ApiStructuredType, _super);
function ApiStructuredType(options) {
var _this = _super.call(this, options) || this;
_this._processedMemberNames = new Set();
_this._setterNames = new Set();
_this._classLikeDeclaration = options.declaration;
_this.type = _this.typeChecker.getDeclaredTypeOfSymbol(_this.declarationSymbol);
if (_this.declarationSymbol.flags & ts.SymbolFlags.Interface) {
_this.kind = ApiItem_1.ApiItemKind.Interface;
}
else if (_this.declarationSymbol.flags & ts.SymbolFlags.TypeLiteral) {
_this.kind = ApiItem_1.ApiItemKind.TypeLiteral;
}
else {
_this.kind = ApiItem_1.ApiItemKind.Class;
}
for (var _i = 0, _a = _this._classLikeDeclaration.members; _i < _a.length; _i++) {
var memberDeclaration = _a[_i];
var memberSymbol = TypeScriptHelpers_1.default.tryGetSymbolForDeclaration(memberDeclaration);
if (memberSymbol) {
_this._processMember(memberSymbol, memberDeclaration);
}
else {
// If someone put an extra semicolon after their function, we don't care about that
if (memberDeclaration.kind !== ts.SyntaxKind.SemicolonClassElement) {
// If there is some other non-semantic junk, add a warning so we can investigate it
_this.reportWarning(PrettyPrinter_1.default.formatFileAndLineNumber(memberDeclaration)
+ (": No semantic information for \"" + memberDeclaration.getText() + "\""));
}
}
}
// If there is a getter and no setter, mark it as readonly.
for (var _b = 0, _c = _this.getSortedMemberItems(); _b < _c.length; _b++) {
var member = _c[_b];
var memberSymbol = TypeScriptHelpers_1.default.tryGetSymbolForDeclaration(member.getDeclaration());
if (memberSymbol && (memberSymbol.flags === ts.SymbolFlags.GetAccessor)) {
if (!_this._setterNames.has(member.name)) {
member.isReadOnly = true;
}
}
}
// Check for heritage clauses (implements and extends)
if (_this._classLikeDeclaration.heritageClauses) {
for (var _d = 0, _e = _this._classLikeDeclaration.heritageClauses; _d < _e.length; _d++) {
var heritage = _e[_d];
var typeText = heritage.types && heritage.types.length && heritage.types[0].expression ?
heritage.types[0].expression.getText() : undefined;
if (heritage.token === ts.SyntaxKind.ExtendsKeyword) {
_this.extends = typeText;
}
else if (heritage.token === ts.SyntaxKind.ImplementsKeyword) {
_this.implements = typeText;
}
}
}
// Check for type parameters
if (_this._classLikeDeclaration.typeParameters && _this._classLikeDeclaration.typeParameters.length) {
if (!_this.typeParameters) {
_this.typeParameters = [];
}
for (var _f = 0, _g = _this._classLikeDeclaration.typeParameters; _f < _g.length; _f++) {
var param = _g[_f];
_this.typeParameters.push(param.getText());
}
}
// Throw errors for setters that don't have a corresponding getter
_this._setterNames.forEach(function (setterName) {
if (!_this.memberItems.has(setterName)) {
_this.reportError("Found setter named " + setterName + " with no corresponding getter. WriteOnly properties are prohibited.");
}
});
return _this;
}
/**
* @virtual
*/
ApiStructuredType.prototype.visitTypeReferencesForApiItem = function () {
_super.prototype.visitTypeReferencesForApiItem.call(this);
// Collect type references from the base classes
if (this._classLikeDeclaration && this._classLikeDeclaration.heritageClauses) {
for (var _i = 0, _a = this._classLikeDeclaration.heritageClauses; _i < _a.length; _i++) {
var clause = _a[_i];
this.visitTypeReferencesForNode(clause);
}
}
};
/**
* Returns a line of text such as "class MyClass extends MyBaseClass", excluding the
* curly braces and body. The name "MyClass" will be the public name seend by external
* callers, not the declared name of the class; @see ApiItem.name documentation for details.
*/
ApiStructuredType.prototype.getDeclarationLine = function () {
var result = '';
if (this.kind !== ApiItem_1.ApiItemKind.TypeLiteral) {
result += (this.declarationSymbol.flags & ts.SymbolFlags.Interface)
? 'interface ' : 'class ';
result += this.name;
if (this._classLikeDeclaration.typeParameters) {
result += '<';
result += this._classLikeDeclaration.typeParameters
.map(function (param) { return param.getText(); })
.join(', ');
result += '>';
}
if (this._classLikeDeclaration.heritageClauses) {
result += ' ';
result += this._classLikeDeclaration.heritageClauses
.map(function (clause) { return clause.getText(); })
.join(', ');
}
}
return result;
};
ApiStructuredType.prototype._processMember = function (memberSymbol, memberDeclaration) {
if (memberDeclaration.modifiers) {
for (var i = 0; i < memberDeclaration.modifiers.length; i++) {
var modifier = memberDeclaration.modifiers[i];
if (modifier.kind === ts.SyntaxKind.PrivateKeyword) {
return;
}
}
}
if (this._processedMemberNames.has(memberSymbol.name)) {
if (memberSymbol.flags === ts.SymbolFlags.SetAccessor) {
// In case of setters, just add them to a list to check later if they have a getter
this._setterNames.add(memberSymbol.name);
}
// Throw an error for duplicate names, because we use names as identifiers
// @todo #261549 Define a JsDoc tag to allow defining an identifier for overloaded methods eg. @overload method2
return;
}
// Proceed to add the member
this._processedMemberNames.add(memberSymbol.name);
var memberOptions = {
extractor: this.extractor,
declaration: memberDeclaration,
declarationSymbol: memberSymbol,
jsdocNode: memberDeclaration
};
if (memberSymbol.flags & (ts.SymbolFlags.Method |
ts.SymbolFlags.Constructor |
ts.SymbolFlags.Signature |
ts.SymbolFlags.Function)) {
this.addMemberItem(new ApiMethod_1.default(memberOptions));
}
else if (memberSymbol.flags & (ts.SymbolFlags.Property |
ts.SymbolFlags.GetAccessor)) {
this.addMemberItem(new ApiProperty_1.default(memberOptions));
}
else {
this.reportWarning("Unsupported member: " + memberSymbol.name);
}
};
return ApiStructuredType;
}(ApiItemContainer_1.default));
exports.default = ApiStructuredType;
//# sourceMappingURL=ApiStructuredType.js.map