@microsoft/api-extractor
Version:
Validate, document, and review the exported API for a TypeScript library
274 lines (272 loc) • 12.8 kB
JavaScript
;
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
// See LICENSE in the project root for license information.
Object.defineProperty(exports, "__esModule", { value: true });
const os = require("os");
const path = require("path");
const ts = require("typescript");
const node_core_library_1 = require("@microsoft/node-core-library");
const AstItem_1 = require("../ast/AstItem");
const AstItemVisitor_1 = require("./AstItemVisitor");
const AstMember_1 = require("../ast/AstMember");
const ReleaseTag_1 = require("../aedoc/ReleaseTag");
const ApiJsonConverter_1 = require("../api/ApiJsonConverter");
const ApiJsonFile_1 = require("../api/ApiJsonFile");
/**
* For a library such as "example-package", ApiFileGenerator generates the "example-package.api.json"
* file which represents the API surface for that package. This file should be published as part
* of the library's NPM package. API Extractor will read this file later when it is analyzing
* another project that consumes the library. (Otherwise, API Extractor would have to re-analyze all
* the *.d.ts files, which would be bad because the compiler definitions might not be available for
* a published package, or the results of the analysis might be different somehow.) Documentation
* tools such as api-documenter can also use the *.api.json files.
*
* @public
*/
class ApiJsonGenerator extends AstItemVisitor_1.default {
constructor() {
super(...arguments);
this.jsonOutput = {};
}
writeJsonFile(reportFilename, context) {
this.visit(context.package, this.jsonOutput);
// Write the output before validating the schema, so we can debug it
node_core_library_1.JsonFile.save(this.jsonOutput, reportFilename);
// Validate that the output conforms to our JSON schema
ApiJsonFile_1.ApiJsonFile.jsonSchema.validateObjectWithCallback(this.jsonOutput, (errorInfo) => {
const errorMessage = path.basename(reportFilename)
+ ` does not conform to the expected schema -- please report this API Extractor bug:`
+ os.EOL + errorInfo.details;
console.log(os.EOL + 'ERROR: ' + errorMessage + os.EOL + os.EOL);
throw new Error(errorMessage);
});
}
// @override
visit(astItem, refObject) {
switch (astItem.inheritedReleaseTag) {
case ReleaseTag_1.ReleaseTag.None:
case ReleaseTag_1.ReleaseTag.Beta:
case ReleaseTag_1.ReleaseTag.Public:
break;
default:
return; // skip @alpha and @internal definitions
}
super.visit(astItem, refObject);
}
visitAstStructuredType(astStructuredType, refObject) {
if (!astStructuredType.supportedName) {
return;
}
const kind = astStructuredType.kind === AstItem_1.AstItemKind.Class ? ApiJsonConverter_1.ApiJsonConverter.convertKindToJson(AstItem_1.AstItemKind.Class) :
astStructuredType.kind === AstItem_1.AstItemKind.Interface ?
ApiJsonConverter_1.ApiJsonConverter.convertKindToJson(AstItem_1.AstItemKind.Interface) : '';
const structureNode = {
kind: kind,
extends: astStructuredType.extends || '',
implements: astStructuredType.implements || '',
typeParameters: astStructuredType.typeParameters || [],
deprecatedMessage: astStructuredType.inheritedDeprecatedMessage || [],
summary: astStructuredType.documentation.summary || [],
remarks: astStructuredType.documentation.remarks || [],
isBeta: astStructuredType.inheritedReleaseTag === ReleaseTag_1.ReleaseTag.Beta
};
refObject[astStructuredType.name] = structureNode;
ApiJsonGenerator._methodCounter = 0;
const members = astStructuredType.getSortedMemberItems();
if (members && members.length) {
const membersNode = {};
structureNode[ApiJsonGenerator._MEMBERS_KEY] = membersNode;
for (const astItem of members) {
this.visit(astItem, membersNode);
}
}
}
visitAstEnum(astEnum, refObject) {
if (!astEnum.supportedName) {
return;
}
const valuesNode = {};
const enumNode = {
kind: ApiJsonConverter_1.ApiJsonConverter.convertKindToJson(astEnum.kind),
values: valuesNode,
deprecatedMessage: astEnum.inheritedDeprecatedMessage || [],
summary: astEnum.documentation.summary || [],
remarks: astEnum.documentation.remarks || [],
isBeta: astEnum.inheritedReleaseTag === ReleaseTag_1.ReleaseTag.Beta
};
refObject[astEnum.name] = enumNode;
for (const astItem of astEnum.getSortedMemberItems()) {
this.visit(astItem, valuesNode);
}
}
visitAstEnumValue(astEnumValue, refObject) {
if (!astEnumValue.supportedName) {
return;
}
const declaration = astEnumValue.getDeclaration();
const firstToken = declaration ? declaration.getFirstToken() : undefined;
const lastToken = declaration ? declaration.getLastToken() : undefined;
const value = lastToken && lastToken !== firstToken ? lastToken.getText() : '';
refObject[astEnumValue.name] = {
kind: ApiJsonConverter_1.ApiJsonConverter.convertKindToJson(astEnumValue.kind),
value: value,
deprecatedMessage: astEnumValue.inheritedDeprecatedMessage || [],
summary: astEnumValue.documentation.summary || [],
remarks: astEnumValue.documentation.remarks || [],
isBeta: astEnumValue.inheritedReleaseTag === ReleaseTag_1.ReleaseTag.Beta
};
}
visitAstFunction(astFunction, refObject) {
if (!astFunction.supportedName) {
return;
}
const returnValueNode = {
type: astFunction.returnType,
description: astFunction.documentation.returnsMessage
};
const newNode = {
kind: ApiJsonConverter_1.ApiJsonConverter.convertKindToJson(astFunction.kind),
signature: astFunction.getDeclarationLine(),
returnValue: returnValueNode,
parameters: this._createParameters(astFunction),
deprecatedMessage: astFunction.inheritedDeprecatedMessage || [],
summary: astFunction.documentation.summary || [],
remarks: astFunction.documentation.remarks || [],
isBeta: astFunction.inheritedReleaseTag === ReleaseTag_1.ReleaseTag.Beta
};
refObject[astFunction.name] = newNode;
}
visitAstPackage(astPackage, refObject) {
/* tslint:disable:no-string-literal */
refObject['kind'] = ApiJsonConverter_1.ApiJsonConverter.convertKindToJson(astPackage.kind);
refObject['name'] = astPackage.name;
refObject['summary'] = astPackage.documentation.summary;
refObject['remarks'] = astPackage.documentation.remarks;
/* tslint:enable:no-string-literal */
const membersNode = {};
refObject[ApiJsonGenerator._EXPORTS_KEY] = membersNode;
for (const astItem of astPackage.getSortedMemberItems()) {
this.visit(astItem, membersNode);
}
}
visitAstNamespace(astNamespace, refObject) {
if (!astNamespace.supportedName) {
return;
}
const membersNode = {};
for (const astItem of astNamespace.getSortedMemberItems()) {
this.visit(astItem, membersNode);
}
const newNode = {
kind: ApiJsonConverter_1.ApiJsonConverter.convertKindToJson(astNamespace.kind),
deprecatedMessage: astNamespace.inheritedDeprecatedMessage || [],
summary: astNamespace.documentation.summary || [],
remarks: astNamespace.documentation.remarks || [],
isBeta: astNamespace.inheritedReleaseTag === ReleaseTag_1.ReleaseTag.Beta,
exports: membersNode
};
refObject[astNamespace.name] = newNode;
}
visitAstMember(astMember, refObject) {
if (!astMember.supportedName) {
return;
}
refObject[astMember.name] = 'astMember-' + astMember.getDeclaration().kind;
}
visitAstProperty(astProperty, refObject) {
if (!astProperty.supportedName) {
return;
}
if (astProperty.getDeclaration().kind === ts.SyntaxKind.SetAccessor) {
return;
}
const newNode = {
kind: ApiJsonConverter_1.ApiJsonConverter.convertKindToJson(astProperty.kind),
signature: astProperty.getDeclarationLine(),
isOptional: !!astProperty.isOptional,
isReadOnly: !!astProperty.isReadOnly,
isStatic: !!astProperty.isStatic,
type: astProperty.type,
deprecatedMessage: astProperty.inheritedDeprecatedMessage || [],
summary: astProperty.documentation.summary || [],
remarks: astProperty.documentation.remarks || [],
isBeta: astProperty.inheritedReleaseTag === ReleaseTag_1.ReleaseTag.Beta
};
refObject[astProperty.name] = newNode;
}
visitAstModuleVariable(astModuleVariable, refObject) {
const newNode = {
kind: ApiJsonConverter_1.ApiJsonConverter.convertKindToJson(astModuleVariable.kind),
signature: astModuleVariable.getDeclarationLine(),
type: astModuleVariable.type,
value: astModuleVariable.value,
deprecatedMessage: astModuleVariable.inheritedDeprecatedMessage || [],
summary: astModuleVariable.documentation.summary || [],
remarks: astModuleVariable.documentation.remarks || [],
isBeta: astModuleVariable.inheritedReleaseTag === ReleaseTag_1.ReleaseTag.Beta
};
refObject[astModuleVariable.name] = newNode;
}
visitAstMethod(astMethod, refObject) {
if (!astMethod.supportedName) {
return;
}
let newNode;
if (astMethod.name === '__constructor') {
newNode = {
kind: ApiJsonConverter_1.ApiJsonConverter.convertKindToJson(AstItem_1.AstItemKind.Constructor),
signature: astMethod.getDeclarationLine(),
parameters: this._createParameters(astMethod),
deprecatedMessage: astMethod.inheritedDeprecatedMessage || [],
summary: astMethod.documentation.summary || [],
remarks: astMethod.documentation.remarks || []
};
}
else {
const returnValueNode = {
type: astMethod.returnType,
description: astMethod.documentation.returnsMessage
};
newNode = {
kind: ApiJsonConverter_1.ApiJsonConverter.convertKindToJson(astMethod.kind),
signature: astMethod.getDeclarationLine(),
accessModifier: astMethod.accessModifier ? AstMember_1.ApiAccessModifier[astMethod.accessModifier].toLowerCase() : '',
isOptional: !!astMethod.isOptional,
isStatic: !!astMethod.isStatic,
returnValue: returnValueNode,
parameters: this._createParameters(astMethod),
deprecatedMessage: astMethod.inheritedDeprecatedMessage || [],
summary: astMethod.documentation.summary || [],
remarks: astMethod.documentation.remarks || [],
isBeta: astMethod.inheritedReleaseTag === ReleaseTag_1.ReleaseTag.Beta
};
}
refObject[astMethod.name] = newNode;
}
_createParameters(astFunction) {
const result = {};
for (const astParameter of astFunction.params) {
if (!astParameter.supportedName) {
continue; // skip parameter names with unusual characters
}
const apiParameter = {
name: astParameter.name,
description: [],
isOptional: astParameter.isOptional,
isSpread: astParameter.isSpread,
type: astParameter.type || ''
};
const aedocParameter = astFunction.documentation.parameters[astParameter.name];
if (aedocParameter) {
apiParameter.description = aedocParameter.description;
}
result[astParameter.name] = apiParameter;
}
return result;
}
}
ApiJsonGenerator._methodCounter = 0;
ApiJsonGenerator._MEMBERS_KEY = 'members';
ApiJsonGenerator._EXPORTS_KEY = 'exports';
exports.default = ApiJsonGenerator;
//# sourceMappingURL=ApiJsonGenerator.js.map