UNPKG

15 kBJavaScriptView Raw
1"use strict";
2var __assign = (this && this.__assign) || function () {
3 __assign = Object.assign || function(t) {
4 for (var s, i = 1, n = arguments.length; i < n; i++) {
5 s = arguments[i];
6 for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
7 t[p] = s[p];
8 }
9 return t;
10 };
11 return __assign.apply(this, arguments);
12};
13Object.defineProperty(exports, "__esModule", { value: true });
14var ts = require("typescript");
15var path = require("path");
16var compile_dts_1 = require("./compile-dts");
17var types_usage_evaluator_1 = require("./types-usage-evaluator");
18var typescript_helpers_1 = require("./typescript-helpers");
19var node_modules_helpers_1 = require("./node-modules-helpers");
20var module_info_1 = require("./module-info");
21var generate_output_1 = require("./generate-output");
22var logger_1 = require("./logger");
23var skippedNodes = [
24 ts.SyntaxKind.ExportDeclaration,
25 ts.SyntaxKind.ImportDeclaration,
26 ts.SyntaxKind.ImportEqualsDeclaration,
27];
28function generateDtsBundle(filePath, options) {
29 if (options === void 0) { options = {}; }
30 if (!ts.sys.fileExists(filePath)) {
31 throw new Error("File \"" + filePath + "\" does not exist");
32 }
33 var program = compile_dts_1.compileDts(filePath, options.preferredConfigPath, options.followSymlinks);
34 var typeChecker = program.getTypeChecker();
35 var criteria = {
36 allowedTypesLibraries: options.allowedTypesLibraries,
37 importedLibraries: options.importedLibraries,
38 inlinedLibraries: options.inlinedLibraries || [],
39 typeRoots: ts.getEffectiveTypeRoots(program.getCompilerOptions(), {}),
40 };
41 var sourceFiles = program.getSourceFiles().filter(function (file) {
42 // tslint:disable-next-line:no-unnecessary-type-assertion
43 return !program.isSourceFileDefaultLibrary(file);
44 });
45 logger_1.verboseLog("Input source files:\n " + sourceFiles.map(function (file) { return file.fileName; }).join('\n '));
46 var typesUsageEvaluator = new types_usage_evaluator_1.TypesUsageEvaluator(sourceFiles, typeChecker);
47 var rootSourceFile = getRootSourceFile(program);
48 var rootSourceFileSymbol = typeChecker.getSymbolAtLocation(rootSourceFile);
49 if (rootSourceFileSymbol === undefined) {
50 throw new Error('Symbol for root source file not found');
51 }
52 var rootFileExports = typeChecker.getExportsOfModule(rootSourceFileSymbol).map(function (symbol) { return typescript_helpers_1.getActualSymbol(symbol, typeChecker); });
53 var collectionResult = {
54 typesReferences: new Set(),
55 imports: new Map(),
56 statements: [],
57 };
58 var updateResultCommonParams = {
59 isStatementUsed: function (statement) { return isNodeUsed(statement, rootFileExports, typesUsageEvaluator, typeChecker); },
60 shouldStatementBeImported: function (statement) { return shouldNodeBeImported(statement, rootFileExports, typesUsageEvaluator); },
61 shouldDeclareGlobalBeInlined: function (currentModule) { return Boolean(options.inlineDeclareGlobals) && currentModule.type === 0 /* ShouldBeInlined */; },
62 getModuleInfo: function (fileName) { return module_info_1.getModuleInfo(fileName, criteria); },
63 getDeclarationsForExportedAssignment: function (exportAssignment) {
64 var symbolForExpression = typeChecker.getSymbolAtLocation(exportAssignment.expression);
65 if (symbolForExpression === undefined) {
66 return [];
67 }
68 var symbol = typescript_helpers_1.getActualSymbol(symbolForExpression, typeChecker);
69 return getDeclarationsForSymbol(symbol);
70 },
71 };
72 for (var _i = 0, sourceFiles_1 = sourceFiles; _i < sourceFiles_1.length; _i++) {
73 var sourceFile = sourceFiles_1[_i];
74 logger_1.verboseLog("\n\n======= Preparing file: " + sourceFile.fileName + " =======");
75 var prevStatementsCount = collectionResult.statements.length;
76 var updateFn = sourceFile === rootSourceFile ? updateResultForRootSourceFile : updateResult;
77 updateFn(__assign({}, updateResultCommonParams, { currentModule: module_info_1.getModuleInfo(sourceFile.fileName, criteria), statements: sourceFile.statements }), collectionResult);
78 if (collectionResult.statements.length === prevStatementsCount) {
79 logger_1.verboseLog("No output for file: " + sourceFile.fileName);
80 }
81 }
82 if (options.failOnClass) {
83 var isClassStatementFound = collectionResult.statements.some(function (statement) { return ts.isClassDeclaration(statement); });
84 if (isClassStatementFound) {
85 throw new Error('At least 1 class statement is found in generated dts.');
86 }
87 }
88 return generate_output_1.generateOutput(__assign({}, collectionResult, { needStripDefaultKeywordForStatement: function (statement) { return statement.getSourceFile() !== rootSourceFile; }, shouldStatementHasExportKeyword: function (statement) {
89 var result = true;
90 if (ts.isClassDeclaration(statement) || ts.isEnumDeclaration(statement) || ts.isFunctionDeclaration(statement)) {
91 // not every class, enum and function can be exported (only exported from root file can)
92 result = isDeclarationExported(rootFileExports, typeChecker, statement);
93 if (ts.isEnumDeclaration(statement)) {
94 // const enum always can be exported
95 result = result || typescript_helpers_1.hasNodeModifier(statement, ts.SyntaxKind.ConstKeyword);
96 }
97 }
98 else if (typescript_helpers_1.isDeclareGlobalStatement(statement) || ts.isExportDeclaration(statement)) {
99 result = false;
100 }
101 return result;
102 } }), {
103 sortStatements: options.sortNodes,
104 umdModuleName: options.umdModuleName,
105 });
106}
107exports.generateDtsBundle = generateDtsBundle;
108// tslint:disable-next-line:cyclomatic-complexity
109function updateResult(params, result) {
110 for (var _i = 0, _a = params.statements; _i < _a.length; _i++) {
111 var statement = _a[_i];
112 // we should skip import and exports statements
113 if (skippedNodes.indexOf(statement.kind) !== -1) {
114 continue;
115 }
116 if (typescript_helpers_1.isDeclareModuleStatement(statement)) {
117 updateResultForModuleDeclaration(statement, params, result);
118 continue;
119 }
120 if (params.currentModule.type === 3 /* ShouldBeUsedForModulesOnly */) {
121 continue;
122 }
123 if (typescript_helpers_1.isDeclareGlobalStatement(statement) && params.shouldDeclareGlobalBeInlined(params.currentModule, statement)) {
124 result.statements.push(statement);
125 continue;
126 }
127 if (ts.isExportAssignment(statement) && statement.isExportEquals && params.currentModule.type === 1 /* ShouldBeImported */) {
128 updateResultForImportedEqExportAssignment(statement, params, result);
129 continue;
130 }
131 if (!params.isStatementUsed(statement)) {
132 logger_1.verboseLog("Skip file member: " + statement.getText().replace(/(\n|\r)/g, '').slice(0, 50) + "...");
133 continue;
134 }
135 switch (params.currentModule.type) {
136 case 2 /* ShouldBeReferencedAsTypes */:
137 addTypesReference(params.currentModule.typesLibraryName, result.typesReferences);
138 break;
139 case 1 /* ShouldBeImported */:
140 updateImportsForStatement(statement, params, result);
141 break;
142 case 0 /* ShouldBeInlined */:
143 result.statements.push(statement);
144 break;
145 }
146 }
147}
148function updateResultForRootSourceFile(params, result) {
149 function isReExportFromImportableModule(statement) {
150 if (!ts.isExportDeclaration(statement) || statement.moduleSpecifier === undefined || !ts.isStringLiteral(statement.moduleSpecifier)) {
151 return false;
152 }
153 var moduleFileName = resolveModuleFileName(statement.getSourceFile().fileName, statement.moduleSpecifier.text);
154 return params.getModuleInfo(moduleFileName).type === 1 /* ShouldBeImported */;
155 }
156 updateResult(params, result);
157 // add skipped by `updateResult` exports
158 for (var _i = 0, _a = params.statements; _i < _a.length; _i++) {
159 var statement = _a[_i];
160 var isExportDefault = ts.isExportAssignment(statement) && !statement.isExportEquals;
161 var isReExportFromImportable = isReExportFromImportableModule(statement);
162 if (isExportDefault || isReExportFromImportable) {
163 result.statements.push(statement);
164 }
165 }
166}
167function updateResultForImportedEqExportAssignment(exportAssignment, params, result) {
168 var moduleDeclarations = params.getDeclarationsForExportedAssignment(exportAssignment)
169 .filter(typescript_helpers_1.isNamespaceStatement)
170 .filter(function (s) { return s.getSourceFile() === exportAssignment.getSourceFile(); });
171 // if we have `export =` somewhere so we can decide that every declaration of exported symbol in this way
172 // is "part of the exported module" and we need to update result according every member of each declaration
173 // but treat they as current module (we do not need to update module info)
174 for (var _i = 0, moduleDeclarations_1 = moduleDeclarations; _i < moduleDeclarations_1.length; _i++) {
175 var moduleDeclaration = moduleDeclarations_1[_i];
176 if (moduleDeclaration.body === undefined || !ts.isModuleBlock(moduleDeclaration.body)) {
177 continue;
178 }
179 updateResult(__assign({}, params, { statements: moduleDeclaration.body.statements }), result);
180 }
181}
182function updateResultForModuleDeclaration(moduleDecl, params, result) {
183 if (moduleDecl.body === undefined || !ts.isModuleBlock(moduleDecl.body)) {
184 return;
185 }
186 var moduleName = moduleDecl.name.text;
187 var moduleFileName = resolveModuleFileName(params.currentModule.fileName, moduleName);
188 var moduleInfo = params.getModuleInfo(moduleFileName);
189 // if we have declaration of external module inside internal one
190 // we need to just add it to result without any processing
191 if (!params.currentModule.isExternal && moduleInfo.isExternal) {
192 result.statements.push(moduleDecl);
193 return;
194 }
195 updateResult(__assign({}, params, { currentModule: moduleInfo, statements: moduleDecl.body.statements }), result);
196}
197function resolveModuleFileName(currentFileName, moduleName) {
198 return moduleName.startsWith('.') ? path.join(currentFileName, '..', moduleName) : "node_modules/" + moduleName + "/";
199}
200function addTypesReference(library, typesReferences) {
201 if (!typesReferences.has(library)) {
202 logger_1.normalLog("Library \"" + library + "\" will be added via reference directive");
203 typesReferences.add(library);
204 }
205}
206function updateImportsForStatement(statement, params, result) {
207 if (params.currentModule.type !== 1 /* ShouldBeImported */) {
208 return;
209 }
210 var statementsToImport = ts.isVariableStatement(statement) ? statement.declarationList.declarations : [statement];
211 for (var _i = 0, statementsToImport_1 = statementsToImport; _i < statementsToImport_1.length; _i++) {
212 var statementToImport = statementsToImport_1[_i];
213 if (params.shouldStatementBeImported(statementToImport)) {
214 addImport(statementToImport, params.currentModule.libraryName, result.imports);
215 }
216 }
217}
218function addImport(statement, library, imports) {
219 if (statement.name === undefined) {
220 throw new Error("Import/usage unnamed declaration: " + statement.getText());
221 }
222 var libraryImports = imports.get(library);
223 if (libraryImports === undefined) {
224 libraryImports = new Set();
225 imports.set(library, libraryImports);
226 }
227 var importName = statement.name.getText();
228 if (!libraryImports.has(importName)) {
229 logger_1.normalLog("Adding import with name \"" + importName + "\" for library \"" + library + "\"");
230 libraryImports.add(importName);
231 }
232}
233function getRootSourceFile(program) {
234 var rootFiles = program.getRootFileNames();
235 if (rootFiles.length !== 1) {
236 logger_1.verboseLog("Root files:\n " + rootFiles.join('\n '));
237 throw new Error("There is not one root file - " + rootFiles.length);
238 }
239 var sourceFileName = rootFiles[0];
240 var sourceFile = program.getSourceFile(sourceFileName);
241 if (sourceFile === undefined) {
242 throw new Error("Cannot get source file for root file " + sourceFileName);
243 }
244 return sourceFile;
245}
246function isDeclarationExported(exportedSymbols, typeChecker, declaration) {
247 if (declaration.name === undefined) {
248 return false;
249 }
250 var declarationSymbol = typeChecker.getSymbolAtLocation(declaration.name);
251 return exportedSymbols.some(function (rootExport) { return rootExport === declarationSymbol; });
252}
253function isNodeUsed(node, rootFileExports, typesUsageEvaluator, typeChecker) {
254 if (typescript_helpers_1.isNodeNamedDeclaration(node)) {
255 return rootFileExports.some(function (rootExport) { return typesUsageEvaluator.isTypeUsedBySymbol(node, rootExport); });
256 }
257 else if (ts.isVariableStatement(node)) {
258 return node.declarationList.declarations.some(function (declaration) {
259 return isNodeUsed(declaration, rootFileExports, typesUsageEvaluator, typeChecker);
260 });
261 }
262 return false;
263}
264function shouldNodeBeImported(node, rootFileExports, typesUsageEvaluator) {
265 var symbolsUsingNode = typesUsageEvaluator.getSymbolsUsingNode(node);
266 if (symbolsUsingNode === null) {
267 throw new Error('Something went wrong - value cannot be null');
268 }
269 // we should import only symbols which are used in types directly
270 return Array.from(symbolsUsingNode).some(function (symbol) {
271 var symbolsDeclarations = getDeclarationsForSymbol(symbol);
272 if (symbolsDeclarations.length === 0 || symbolsDeclarations.every(isDeclarationFromExternalModule)) {
273 return false;
274 }
275 return rootFileExports.some(function (rootSymbol) { return typesUsageEvaluator.isSymbolUsedBySymbol(symbol, rootSymbol); });
276 });
277}
278function getDeclarationsForSymbol(symbol) {
279 var result = [];
280 // Disabling tslint is for backward compat with TypeScript < 3
281 // tslint:disable-next-line:strict-type-predicates
282 if (symbol.valueDeclaration !== undefined) {
283 result.push(symbol.valueDeclaration);
284 }
285 // Disabling tslint is for backward compat with TypeScript < 3
286 // tslint:disable-next-line:strict-type-predicates
287 if (symbol.declarations !== undefined) {
288 result.push.apply(result, symbol.declarations);
289 }
290 return result;
291}
292function isDeclarationFromExternalModule(node) {
293 return node_modules_helpers_1.getLibraryName(node.getSourceFile().fileName) !== null;
294}