UNPKG

12 kBJavaScriptView Raw
1"use strict";
2// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
3// See LICENSE in the project root for license information.
4var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
5 if (k2 === undefined) k2 = k;
6 var desc = Object.getOwnPropertyDescriptor(m, k);
7 if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
8 desc = { enumerable: true, get: function() { return m[k]; } };
9 }
10 Object.defineProperty(o, k2, desc);
11}) : (function(o, m, k, k2) {
12 if (k2 === undefined) k2 = k;
13 o[k2] = m[k];
14}));
15var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
16 Object.defineProperty(o, "default", { enumerable: true, value: v });
17}) : function(o, v) {
18 o["default"] = v;
19});
20var __importStar = (this && this.__importStar) || function (mod) {
21 if (mod && mod.__esModule) return mod;
22 var result = {};
23 if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
24 __setModuleDefault(result, mod);
25 return result;
26};
27Object.defineProperty(exports, "__esModule", { value: true });
28exports.TypeScriptHelpers = void 0;
29/* eslint-disable no-bitwise */
30const ts = __importStar(require("typescript"));
31const SourceFileLocationFormatter_1 = require("./SourceFileLocationFormatter");
32const TypeScriptInternals_1 = require("./TypeScriptInternals");
33const node_core_library_1 = require("@rushstack/node-core-library");
34class TypeScriptHelpers {
35 /**
36 * This traverses any symbol aliases to find the original place where an item was defined.
37 * For example, suppose a class is defined as "export default class MyClass { }"
38 * but exported from the package's index.ts like this:
39 *
40 * export { default as _MyClass } from './MyClass';
41 *
42 * In this example, calling followAliases() on the _MyClass symbol will return the
43 * original definition of MyClass, traversing any intermediary places where the
44 * symbol was imported and re-exported.
45 */
46 static followAliases(symbol, typeChecker) {
47 let current = symbol;
48 for (;;) {
49 if (!(current.flags & ts.SymbolFlags.Alias)) {
50 break;
51 }
52 const currentAlias = typeChecker.getAliasedSymbol(current);
53 if (!currentAlias || currentAlias === current) {
54 break;
55 }
56 current = currentAlias;
57 }
58 return current;
59 }
60 /**
61 * Returns true if TypeScriptHelpers.followAliases() would return something different
62 * from the input `symbol`.
63 */
64 static isFollowableAlias(symbol, typeChecker) {
65 if (!(symbol.flags & ts.SymbolFlags.Alias)) {
66 return false;
67 }
68 const alias = typeChecker.getAliasedSymbol(symbol);
69 if (!alias || alias === symbol) {
70 return false;
71 }
72 return true;
73 }
74 /**
75 * Certain virtual symbols do not have any declarations. For example, `ts.TypeChecker.getExportsOfModule()` can
76 * sometimes return a "prototype" symbol for an object, even though there is no corresponding declaration in the
77 * source code. API Extractor generally ignores such symbols.
78 */
79 static tryGetADeclaration(symbol) {
80 if (symbol.declarations && symbol.declarations.length > 0) {
81 return symbol.declarations[0];
82 }
83 return undefined;
84 }
85 /**
86 * Returns true if the specified symbol is an ambient declaration.
87 */
88 static isAmbient(symbol, typeChecker) {
89 const followedSymbol = TypeScriptHelpers.followAliases(symbol, typeChecker);
90 if (followedSymbol.declarations && followedSymbol.declarations.length > 0) {
91 const firstDeclaration = followedSymbol.declarations[0];
92 // Test 1: Are we inside the sinister "declare global {" construct?
93 const highestModuleDeclaration = TypeScriptHelpers.findHighestParent(firstDeclaration, ts.SyntaxKind.ModuleDeclaration);
94 if (highestModuleDeclaration) {
95 if (highestModuleDeclaration.name.getText().trim() === 'global') {
96 return true;
97 }
98 }
99 // Test 2: Otherwise, the main heuristic for ambient declarations is by looking at the
100 // ts.SyntaxKind.SourceFile node to see whether it has a symbol or not (i.e. whether it
101 // is acting as a module or not).
102 const sourceFile = firstDeclaration.getSourceFile();
103 if (typeChecker.getSymbolAtLocation(sourceFile)) {
104 return false;
105 }
106 }
107 return true;
108 }
109 /**
110 * Same semantics as tryGetSymbolForDeclaration(), but throws an exception if the symbol
111 * cannot be found.
112 */
113 static getSymbolForDeclaration(declaration, checker) {
114 const symbol = TypeScriptInternals_1.TypeScriptInternals.tryGetSymbolForDeclaration(declaration, checker);
115 if (!symbol) {
116 throw new node_core_library_1.InternalError('Unable to determine semantic information for declaration:\n' +
117 SourceFileLocationFormatter_1.SourceFileLocationFormatter.formatDeclaration(declaration));
118 }
119 return symbol;
120 }
121 // Return name of the module, which could be like "./SomeLocalFile' or like 'external-package/entry/point'
122 static getModuleSpecifier(nodeWithModuleSpecifier) {
123 if (nodeWithModuleSpecifier.kind === ts.SyntaxKind.ImportType) {
124 // As specified internally in typescript:/src/compiler/types.ts#ValidImportTypeNode
125 if (nodeWithModuleSpecifier.argument.kind !== ts.SyntaxKind.LiteralType ||
126 nodeWithModuleSpecifier.argument.literal.kind !== ts.SyntaxKind.StringLiteral) {
127 throw new node_core_library_1.InternalError(`Invalid ImportTypeNode: ${nodeWithModuleSpecifier.getText()}\n` +
128 SourceFileLocationFormatter_1.SourceFileLocationFormatter.formatDeclaration(nodeWithModuleSpecifier));
129 }
130 const literalTypeNode = nodeWithModuleSpecifier.argument;
131 const stringLiteral = literalTypeNode.literal;
132 return stringLiteral.text.trim();
133 }
134 // Node is a declaration
135 if (nodeWithModuleSpecifier.moduleSpecifier &&
136 ts.isStringLiteralLike(nodeWithModuleSpecifier.moduleSpecifier)) {
137 return TypeScriptInternals_1.TypeScriptInternals.getTextOfIdentifierOrLiteral(nodeWithModuleSpecifier.moduleSpecifier);
138 }
139 return undefined;
140 }
141 /**
142 * Returns an ancestor of "node", such that the ancestor, any intermediary nodes,
143 * and the starting node match a list of expected kinds. Undefined is returned
144 * if there aren't enough ancestors, or if the kinds are incorrect.
145 *
146 * For example, suppose child "C" has parents A --> B --> C.
147 *
148 * Calling _matchAncestor(C, [ExportSpecifier, NamedExports, ExportDeclaration])
149 * would return A only if A is of kind ExportSpecifier, B is of kind NamedExports,
150 * and C is of kind ExportDeclaration.
151 *
152 * Calling _matchAncestor(C, [ExportDeclaration]) would return C.
153 */
154 static matchAncestor(node, kindsToMatch) {
155 // (slice(0) clones an array)
156 const reversedParentKinds = kindsToMatch.slice(0).reverse();
157 let current = undefined;
158 for (const parentKind of reversedParentKinds) {
159 if (!current) {
160 // The first time through, start with node
161 current = node;
162 }
163 else {
164 // Then walk the parents
165 current = current.parent;
166 }
167 // If we ran out of items, or if the kind doesn't match, then fail
168 if (!current || current.kind !== parentKind) {
169 return undefined;
170 }
171 }
172 // If we matched everything, then return the node that matched the last parentKinds item
173 return current;
174 }
175 /**
176 * Does a depth-first search of the children of the specified node. Returns the first child
177 * with the specified kind, or undefined if there is no match.
178 */
179 static findFirstChildNode(node, kindToMatch) {
180 for (const child of node.getChildren()) {
181 if (child.kind === kindToMatch) {
182 return child;
183 }
184 const recursiveMatch = TypeScriptHelpers.findFirstChildNode(child, kindToMatch);
185 if (recursiveMatch) {
186 return recursiveMatch;
187 }
188 }
189 return undefined;
190 }
191 /**
192 * Returns the first parent node with the specified SyntaxKind, or undefined if there is no match.
193 */
194 static findFirstParent(node, kindToMatch) {
195 let current = node.parent;
196 while (current) {
197 if (current.kind === kindToMatch) {
198 return current;
199 }
200 current = current.parent;
201 }
202 return undefined;
203 }
204 /**
205 * Returns the highest parent node with the specified SyntaxKind, or undefined if there is no match.
206 * @remarks
207 * Whereas findFirstParent() returns the first match, findHighestParent() returns the last match.
208 */
209 static findHighestParent(node, kindToMatch) {
210 let current = node;
211 let highest = undefined;
212 for (;;) {
213 current = TypeScriptHelpers.findFirstParent(current, kindToMatch);
214 if (!current) {
215 break;
216 }
217 highest = current;
218 }
219 return highest;
220 }
221 /**
222 * Decodes the names that the compiler generates for a built-in ECMAScript symbol.
223 *
224 * @remarks
225 * TypeScript binds well-known ECMAScript symbols like `[Symbol.iterator]` as `__@iterator`.
226 * If `name` is of this form, then `tryGetWellKnownSymbolName()` converts it back into e.g. `[Symbol.iterator]`.
227 * If the string does not start with `__@` then `undefined` is returned.
228 */
229 static tryDecodeWellKnownSymbolName(name) {
230 const match = TypeScriptHelpers._wellKnownSymbolNameRegExp.exec(name);
231 if (match) {
232 const identifier = match[1];
233 return `[Symbol.${identifier}]`;
234 }
235 return undefined;
236 }
237 /**
238 * Returns whether the provided name was generated for a TypeScript `unique symbol`.
239 */
240 static isUniqueSymbolName(name) {
241 return TypeScriptHelpers._uniqueSymbolNameRegExp.test(name);
242 }
243 /**
244 * Derives the string representation of a TypeScript late-bound symbol.
245 */
246 static tryGetLateBoundName(declarationName) {
247 // Create a node printer that ignores comments and indentation that we can use to convert
248 // declarationName to a string.
249 const printer = ts.createPrinter({ removeComments: true }, {
250 onEmitNode(hint, node, emitCallback) {
251 ts.setEmitFlags(declarationName, ts.EmitFlags.NoIndentation | ts.EmitFlags.SingleLine);
252 emitCallback(hint, node);
253 }
254 });
255 const sourceFile = declarationName.getSourceFile();
256 const text = printer.printNode(ts.EmitHint.Unspecified, declarationName, sourceFile);
257 // clean up any emit flags we've set on any nodes in the tree.
258 ts.disposeEmitNodes(sourceFile);
259 return text;
260 }
261}
262exports.TypeScriptHelpers = TypeScriptHelpers;
263// Matches TypeScript's encoded names for well-known ECMAScript symbols like
264// "__@iterator" or "__@toStringTag".
265TypeScriptHelpers._wellKnownSymbolNameRegExp = /^__@(\w+)$/;
266// Matches TypeScript's encoded names for late-bound symbols derived from `unique symbol` declarations
267// which have the form of "__@<variableName>@<symbolId>", i.e. "__@someSymbol@12345".
268TypeScriptHelpers._uniqueSymbolNameRegExp = /^__@.*@\d+$/;
269//# sourceMappingURL=TypeScriptHelpers.js.map
\No newline at end of file