UNPKG

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