UNPKG

@microsoft/api-extractor

Version:

Validatation, documentation, and auditing for the exported API of a TypeScript package

201 lines (199 loc) 9.14 kB
"use strict"; 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 fs = require("fs"); var ApiItem_1 = require("../definitions/ApiItem"); var ApiItemVisitor_1 = require("../ApiItemVisitor"); var ApiPackage_1 = require("../definitions/ApiPackage"); var IndentedWriter_1 = require("../IndentedWriter"); var ApiDocumentation_1 = require("../definitions/ApiDocumentation"); /** * For a library such as "example-package", ApiFileGenerator generates the "example-package.api.ts" * report which is used to detect API changes. The output is pseudocode whose syntax is similar * but not identical to a "*.d.ts" typings file. The output file is designed to be committed to * Git with a branch policy that will trigger an API review workflow whenever the file contents * have changed. For example, the API file indicates *whether* a class has been documented, * but it does not include the documentation text (since minor text changes should not require * an API review). */ var ApiFileGenerator = (function (_super) { __extends(ApiFileGenerator, _super); function ApiFileGenerator() { var _this = _super !== null && _super.apply(this, arguments) || this; _this._indentedWriter = new IndentedWriter_1.default(); return _this; } /** * Compares the contents of two API files that were created using ApiFileGenerator, * and returns true if they are equivalent. Note that these files are not normally edited * by a human; the "equivalence" comparison here is intended to ignore spurious changes that * might be introduced by a tool, e.g. Git newline normalization or an editor that strips * whitespace when saving. */ ApiFileGenerator.areEquivalentApiFileContents = function (actualFileContent, expectedFileContent) { // NOTE: "\s" also matches "\r" and "\n" var normalizedActual = actualFileContent.replace(/[\s]+/g, ' '); var normalizedExpected = expectedFileContent.replace(/[\s]+/g, ' '); return normalizedActual === normalizedExpected; }; /** * Generates the report and writes it to disk. * * @param reportFilename - The output filename * @param analyzer - An Analyzer object representing the input project. */ ApiFileGenerator.prototype.writeApiFile = function (reportFilename, extractor) { var fileContent = this.generateApiFileContent(extractor); fs.writeFileSync(reportFilename, fileContent); }; ApiFileGenerator.prototype.generateApiFileContent = function (extractor) { this._insideTypeLiteral = 0; // Normalize to CRLF this.visit(extractor.package); var fileContent = this._indentedWriter.toString().replace(/\r?\n/g, '\r\n'); return fileContent; }; ApiFileGenerator.prototype.visitApiStructuredType = function (apiStructuredType) { var _this = this; var declarationLine = apiStructuredType.getDeclarationLine(); if (apiStructuredType.documentation.preapproved) { this._indentedWriter.writeLine('// @internal (preapproved)'); this._indentedWriter.writeLine(declarationLine + ' {'); this._indentedWriter.writeLine('}'); return; } if (apiStructuredType.kind !== ApiItem_1.ApiItemKind.TypeLiteral) { this._writeJsdocSynopsis(apiStructuredType); } this._indentedWriter.writeLine(declarationLine + ' {'); this._indentedWriter.indentScope(function () { if (apiStructuredType.kind === ApiItem_1.ApiItemKind.TypeLiteral) { // Type literals don't have normal JSDoc. Write only the warnings, // and put them after the '{' since the declaration is nested. _this._writeWarnings(apiStructuredType); } for (var _i = 0, _a = apiStructuredType.getSortedMemberItems(); _i < _a.length; _i++) { var member = _a[_i]; _this.visit(member); _this._indentedWriter.writeLine(); } }); this._indentedWriter.write('}'); }; ApiFileGenerator.prototype.visitApiEnum = function (apiEnum) { var _this = this; this._writeJsdocSynopsis(apiEnum); this._indentedWriter.writeLine("enum " + apiEnum.name + " {"); this._indentedWriter.indentScope(function () { var members = apiEnum.getSortedMemberItems(); for (var i = 0; i < members.length; ++i) { _this.visit(members[i]); _this._indentedWriter.writeLine(i < members.length - 1 ? ',' : ''); } }); this._indentedWriter.write('}'); }; ApiFileGenerator.prototype.visitApiEnumValue = function (apiEnumValue) { this._writeJsdocSynopsis(apiEnumValue); this._indentedWriter.write(apiEnumValue.getDeclarationLine()); }; ApiFileGenerator.prototype.visitApiPackage = function (apiPackage) { for (var _i = 0, _a = apiPackage.getSortedMemberItems(); _i < _a.length; _i++) { var apiItem = _a[_i]; this.visit(apiItem); this._indentedWriter.writeLine(); this._indentedWriter.writeLine(); } this._writeJsdocSynopsis(apiPackage); }; ApiFileGenerator.prototype.visitApiMember = function (apiMember) { if (apiMember.documentation) { this._writeJsdocSynopsis(apiMember); } this._indentedWriter.write(apiMember.getDeclarationLine()); if (apiMember.typeLiteral) { this._insideTypeLiteral += 1; this.visit(apiMember.typeLiteral); this._insideTypeLiteral -= 1; } }; ApiFileGenerator.prototype.visitApiFunction = function (apiFunction) { this._writeJsdocSynopsis(apiFunction); this._indentedWriter.write(apiFunction.getDeclarationLine()); }; ApiFileGenerator.prototype.visitApiParam = function (apiParam) { throw Error('Not Implemented'); }; /** * Writes a synopsis of the JSDoc comments, which indicates the API tag, * whether the item has been documented, and any warnings that were detected * by the Analzer. */ ApiFileGenerator.prototype._writeJsdocSynopsis = function (apiItem) { this._writeWarnings(apiItem); var lines = []; if (apiItem instanceof ApiPackage_1.default && !apiItem.documentation.summary.length) { lines.push('(No packageDescription for this package)'); } else { var footer = ''; switch (apiItem.documentation.apiTag) { case ApiDocumentation_1.ApiTag.Internal: footer += '@internal'; break; case ApiDocumentation_1.ApiTag.Alpha: footer += '@alpha'; break; case ApiDocumentation_1.ApiTag.Beta: footer += '@beta'; break; case ApiDocumentation_1.ApiTag.Public: footer += '@public'; break; } // deprecatedMessage is initialized by default, // this ensures it has contents before adding '@deprecated' if (apiItem.documentation.deprecatedMessage.length > 0) { if (footer) { footer += ' '; } footer += '@deprecated'; } // If we are anywhere inside a TypeLiteral, _insideTypeLiteral is greater than 0 if (this._insideTypeLiteral === 0 && apiItem.needsDocumentation) { if (footer) { footer += ' '; } footer += '(undocumented)'; } if (footer) { lines.push(footer); } } this._writeLinesAsComments(lines); }; ApiFileGenerator.prototype._writeWarnings = function (apiItem) { var lines = apiItem.warnings.map(function (x) { return 'WARNING: ' + x; }); this._writeLinesAsComments(lines); }; ApiFileGenerator.prototype._writeLinesAsComments = function (lines) { if (lines.length) { // Write the lines prefixed by slashes. If there are multiple lines, add "//" to each line this._indentedWriter.write('// '); this._indentedWriter.write(lines.join('\n// ')); this._indentedWriter.writeLine(); } }; return ApiFileGenerator; }(ApiItemVisitor_1.default)); exports.default = ApiFileGenerator; //# sourceMappingURL=ApiFileGenerator.js.map