UNPKG

@microsoft/api-extractor

Version:

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

118 lines (116 loc) 5.91 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 }); /* tslint:disable:no-bitwise */ const ts = require("typescript"); const AstModuleVariable_1 = require("./AstModuleVariable"); const AstItem_1 = require("./AstItem"); const AstModule_1 = require("./AstModule"); const allowedTypes = ['string', 'number', 'boolean']; /** * This class is part of the AstItem abstract syntax tree. It represents exports of * a namespace, the exports can be module variable constants of type "string", "boolean" or "number". * An AstNamespace is defined using TypeScript's "namespace" keyword. * * @remarks A note about terminology: * - EcmaScript "namespace modules" are not conventional namespaces; their semantics are * more like static classes in C# or Java. * - API Extractor's support for namespaces is currently limited to representing tables of * constants, and has a benefit of enabling WebPack to avoid bundling unused values. * - We currently still recommend to use static classes for utility libraries, since this * provides getters/setters, public/private, and some other structure missing from namespaces. */ class AstNamespace extends AstModule_1.default { constructor(options) { super(options); this._exportedNormalizedSymbols = []; this.kind = AstItem_1.AstItemKind.Namespace; this.name = options.declarationSymbol.name; const exportSymbols = this.typeChecker.getExportsOfModule(this.declarationSymbol); if (exportSymbols) { if (this.context.policies.namespaceSupport === 'conservative') { this._processConservativeMembers(exportSymbols); } else { this._processPermissiveMembers(exportSymbols); } } } // Used when policies.namespaceSupport=conservative _processConservativeMembers(exportSymbols) { for (const exportSymbol of exportSymbols) { const followedSymbol = this.followAliases(exportSymbol); if (!followedSymbol.declarations) { // This is an API Extractor bug, but it could happen e.g. if we upgrade to a new // version of the TypeScript compiler that introduces new AST variations that we // haven't tested before. this.reportWarning(`The definition "${exportSymbol.name}" has no declarations`); continue; } if (!(followedSymbol.flags === ts.SymbolFlags.BlockScopedVariable)) { this.reportWarning(`Unsupported export "${exportSymbol.name}" ` + 'Currently the "namespace" block only supports constant variables.'); continue; } // Since we are imposing that the items within a namespace be // const properties we are only taking the first declaration. // If we decide to add support for other types within a namespace // we will have for evaluate each declaration. const declarations = followedSymbol.getDeclarations(); if (!declarations) { throw new Error('Missing declaration'); } const declaration = declarations[0]; if (declaration.parent && declaration.parent.flags !== ts.NodeFlags.Const) { this.reportWarning(`Export "${exportSymbol.name}" is missing the "const" ` + 'modifier. Currently the "namespace" block only supports constant variables.'); continue; } const propertySignature = declaration; if (!propertySignature.type || allowedTypes.indexOf(propertySignature.type.getText()) < 0) { this.reportWarning(`Export "${exportSymbol.name}" must specify and be of type` + '"string", "number" or "boolean"'); continue; } if (!propertySignature.initializer) { this.reportWarning(`Export "${exportSymbol.name}" must have an initialized value`); continue; } // Typescript's VariableDeclaration AST nodes have an VariableDeclarationList parent, // and the VariableDeclarationList exists within a VariableStatement, which is where // the JSDoc comment Node can be found. // If there is no parent or grandparent of this VariableDeclaration then // we do not know how to obtain the JSDoc comment. let jsdocNode = undefined; if (!declaration.parent || !declaration.parent.parent || declaration.parent.parent.kind !== ts.SyntaxKind.VariableStatement) { this.reportWarning(`Unable to locate the documentation node for "${exportSymbol.name}"; ` + `this may be an API Extractor bug`); } else { jsdocNode = declaration.parent.parent; } const exportMemberOptions = { context: this.context, declaration, declarationSymbol: followedSymbol, jsdocNode: jsdocNode, exportSymbol }; this.addMemberItem(new AstModuleVariable_1.default(exportMemberOptions)); this._exportedNormalizedSymbols.push({ exportedName: exportSymbol.name, followedSymbol: followedSymbol }); } } // Used when policies.namespaceSupport=permissive _processPermissiveMembers(exportSymbols) { for (const exportSymbol of exportSymbols) { this.processModuleExport(exportSymbol); } } } exports.default = AstNamespace; //# sourceMappingURL=AstNamespace.js.map