UNPKG

@microsoft/api-extractor

Version:

Validate, document, and review the exported API for a TypeScript library

274 lines (272 loc) 12.8 kB
"use strict"; // 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