UNPKG

24.3 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 });
14exports.generateDtsBundle = void 0;
15var ts = require("typescript");
16var path = require("path");
17var compile_dts_1 = require("./compile-dts");
18var types_usage_evaluator_1 = require("./types-usage-evaluator");
19var typescript_1 = require("./helpers/typescript");
20var fix_path_1 = require("./helpers/fix-path");
21var module_info_1 = require("./module-info");
22var generate_output_1 = require("./generate-output");
23var logger_1 = require("./logger");
24function generateDtsBundle(entries, options) {
25 if (options === void 0) { options = {}; }
26 logger_1.normalLog('Compiling input files...');
27 var _a = compile_dts_1.compileDts(entries.map(function (entry) { return entry.filePath; }), options.preferredConfigPath, options.followSymlinks), program = _a.program, rootFilesRemapping = _a.rootFilesRemapping;
28 var typeChecker = program.getTypeChecker();
29 var typeRoots = ts.getEffectiveTypeRoots(program.getCompilerOptions(), {});
30 var sourceFiles = program.getSourceFiles().filter(function (file) {
31 return !isSourceFileDefaultLibrary(program, file);
32 });
33 logger_1.verboseLog("Input source files:\n " + sourceFiles.map(function (file) { return file.fileName; }).join('\n '));
34 var typesUsageEvaluator = new types_usage_evaluator_1.TypesUsageEvaluator(sourceFiles, typeChecker);
35 // tslint:disable-next-line:cyclomatic-complexity
36 return entries.map(function (entry) {
37 logger_1.normalLog("Processing " + entry.filePath);
38 var newRootFilePath = rootFilesRemapping.get(entry.filePath);
39 if (newRootFilePath === undefined) {
40 throw new Error("Cannot remap root source file " + entry.filePath);
41 }
42 var rootSourceFile = getRootSourceFile(program, newRootFilePath);
43 var rootSourceFileSymbol = typeChecker.getSymbolAtLocation(rootSourceFile);
44 if (rootSourceFileSymbol === undefined) {
45 throw new Error("Symbol for root source file " + newRootFilePath + " not found");
46 }
47 var librariesOptions = entry.libraries || {};
48 var criteria = {
49 allowedTypesLibraries: librariesOptions.allowedTypesLibraries,
50 importedLibraries: librariesOptions.importedLibraries,
51 inlinedLibraries: librariesOptions.inlinedLibraries || [],
52 typeRoots: typeRoots,
53 };
54 var rootFileExports = typescript_1.getExportsForSourceFile(typeChecker, rootSourceFileSymbol);
55 var rootFileExportSymbols = rootFileExports.map(function (exp) { return exp.symbol; });
56 var collectionResult = {
57 typesReferences: new Set(),
58 imports: new Map(),
59 statements: [],
60 renamedExports: [],
61 };
62 var outputOptions = entry.output || {};
63 var updateResultCommonParams = {
64 isStatementUsed: function (statement) { return isNodeUsed(statement, rootFileExportSymbols, typesUsageEvaluator, typeChecker); },
65 shouldStatementBeImported: function (statement) {
66 return shouldNodeBeImported(statement, rootFileExportSymbols, typesUsageEvaluator, typeChecker, isSourceFileDefaultLibrary.bind(null, program));
67 },
68 shouldDeclareGlobalBeInlined: function (currentModule) { return Boolean(outputOptions.inlineDeclareGlobals) && currentModule.type === 0 /* ShouldBeInlined */; },
69 shouldDeclareExternalModuleBeInlined: function () { return Boolean(outputOptions.inlineDeclareExternals); },
70 getModuleInfo: function (fileName) { return module_info_1.getModuleInfo(fileName, criteria); },
71 resolveIdentifier: function (identifier) {
72 var symbol = getDeclarationNameSymbol(identifier, typeChecker);
73 if (symbol === null) {
74 return undefined;
75 }
76 var declarations = typescript_1.getDeclarationsForSymbol(symbol);
77 if (declarations.length === 0) {
78 return undefined;
79 }
80 var decl = declarations[0];
81 if (!typescript_1.isNodeNamedDeclaration(decl)) {
82 return undefined;
83 }
84 return decl.name;
85 },
86 getDeclarationsForExportedAssignment: function (exportAssignment) {
87 var symbolForExpression = typeChecker.getSymbolAtLocation(exportAssignment.expression);
88 if (symbolForExpression === undefined) {
89 return [];
90 }
91 var symbol = typescript_1.getActualSymbol(symbolForExpression, typeChecker);
92 return typescript_1.getDeclarationsForSymbol(symbol);
93 },
94 getDeclarationUsagesSourceFiles: function (declaration) {
95 return getDeclarationUsagesSourceFiles(declaration, rootFileExportSymbols, typesUsageEvaluator, typeChecker);
96 },
97 areDeclarationSame: function (left, right) {
98 var leftSymbols = typescript_1.splitTransientSymbol(getNodeSymbol(left, typeChecker), typeChecker);
99 var rightSymbols = typescript_1.splitTransientSymbol(getNodeSymbol(right, typeChecker), typeChecker);
100 return leftSymbols.some(function (leftSymbol) { return rightSymbols.includes(leftSymbol); });
101 },
102 };
103 for (var _i = 0, sourceFiles_1 = sourceFiles; _i < sourceFiles_1.length; _i++) {
104 var sourceFile = sourceFiles_1[_i];
105 logger_1.verboseLog("\n\n======= Preparing file: " + sourceFile.fileName + " =======");
106 var prevStatementsCount = collectionResult.statements.length;
107 var updateFn = sourceFile === rootSourceFile ? updateResultForRootSourceFile : updateResult;
108 var currentModule = module_info_1.getModuleInfo(sourceFile.fileName, criteria);
109 var params = __assign(__assign({}, updateResultCommonParams), { currentModule: currentModule, statements: sourceFile.statements });
110 updateFn(params, collectionResult);
111 // handle `import * as module` usage if it's used as whole module
112 if (currentModule.type === 1 /* ShouldBeImported */ && updateResultCommonParams.isStatementUsed(sourceFile)) {
113 updateImportsForStatement(sourceFile, params, collectionResult);
114 }
115 if (collectionResult.statements.length === prevStatementsCount) {
116 logger_1.verboseLog("No output for file: " + sourceFile.fileName);
117 }
118 }
119 if (entry.failOnClass) {
120 var classes = collectionResult.statements.filter(ts.isClassDeclaration);
121 if (classes.length !== 0) {
122 var classesNames = classes.map(function (c) { return c.name === undefined ? 'anonymous class' : c.name.text; });
123 throw new Error(classes.length + " class statement(s) are found in generated dts: " + classesNames.join(', '));
124 }
125 }
126 return generate_output_1.generateOutput(__assign(__assign({}, collectionResult), { needStripDefaultKeywordForStatement: function (statement) {
127 var statementExports = typescript_1.getExportsForStatement(rootFileExports, typeChecker, statement);
128 // if true - no direct export was found
129 // that means that node might have an export keyword (like interface, type, etc)
130 // otherwise, if there are only re-exports with renaming (like export { foo as bar })
131 // we don't need to put export keyword for this statement
132 // because we'll re-export it in the way
133 return statementExports.find(function (exp) { return exp.exportedName === 'default'; }) === undefined;
134 }, shouldStatementHasExportKeyword: function (statement) {
135 var statementExports = typescript_1.getExportsForStatement(rootFileExports, typeChecker, statement);
136 // if true - no direct export was found
137 // that means that node might have an export keyword (like interface, type, etc)
138 // otherwise, if there are only re-exports with renaming (like export { foo as bar })
139 // we don't need to put export keyword for this statement
140 // because we'll re-export it in the way
141 var result = statementExports.length === 0 || statementExports.find(function (exp) { return exp.exportedName === exp.symbol.escapedName; }) !== undefined;
142 if (ts.isClassDeclaration(statement)
143 || ts.isEnumDeclaration(statement)
144 || ts.isFunctionDeclaration(statement)
145 || ts.isVariableStatement(statement)) {
146 // "valuable" statements must be re-exported from root source file
147 // to having export keyword in declaration fle
148 result = result && statementExports.length !== 0;
149 if (ts.isEnumDeclaration(statement)) {
150 // const enum always can be exported
151 result = result || typescript_1.hasNodeModifier(statement, ts.SyntaxKind.ConstKeyword);
152 }
153 }
154 else if (typescript_1.isAmbientModule(statement) || ts.isExportDeclaration(statement)) {
155 result = false;
156 }
157 return result;
158 }, needStripConstFromConstEnum: function (constEnum) {
159 if (!program.getCompilerOptions().preserveConstEnums || !outputOptions.respectPreserveConstEnum) {
160 return false;
161 }
162 var enumSymbol = getNodeSymbol(constEnum, typeChecker);
163 if (enumSymbol === null) {
164 return false;
165 }
166 return rootFileExportSymbols.includes(enumSymbol);
167 } }), {
168 sortStatements: outputOptions.sortNodes,
169 umdModuleName: outputOptions.umdModuleName,
170 noBanner: outputOptions.noBanner,
171 });
172 });
173}
174exports.generateDtsBundle = generateDtsBundle;
175var skippedNodes = [
176 ts.SyntaxKind.ExportDeclaration,
177 ts.SyntaxKind.ImportDeclaration,
178 ts.SyntaxKind.ImportEqualsDeclaration,
179];
180// tslint:disable-next-line:cyclomatic-complexity
181function updateResult(params, result) {
182 for (var _i = 0, _a = params.statements; _i < _a.length; _i++) {
183 var statement = _a[_i];
184 // we should skip import and exports statements
185 if (skippedNodes.indexOf(statement.kind) !== -1) {
186 continue;
187 }
188 if (typescript_1.isDeclareModuleStatement(statement)) {
189 updateResultForModuleDeclaration(statement, params, result);
190 continue;
191 }
192 if (params.currentModule.type === 3 /* ShouldBeUsedForModulesOnly */) {
193 continue;
194 }
195 if (typescript_1.isDeclareGlobalStatement(statement) && params.shouldDeclareGlobalBeInlined(params.currentModule, statement)) {
196 result.statements.push(statement);
197 continue;
198 }
199 if (ts.isExportAssignment(statement) && statement.isExportEquals && params.currentModule.type === 1 /* ShouldBeImported */) {
200 updateResultForImportedEqExportAssignment(statement, params, result);
201 continue;
202 }
203 if (!params.isStatementUsed(statement)) {
204 logger_1.verboseLog("Skip file member: " + statement.getText().replace(/(\n|\r)/g, '').slice(0, 50) + "...");
205 continue;
206 }
207 switch (params.currentModule.type) {
208 case 2 /* ShouldBeReferencedAsTypes */:
209 addTypesReference(params.currentModule.typesLibraryName, result.typesReferences);
210 break;
211 case 1 /* ShouldBeImported */:
212 updateImportsForStatement(statement, params, result);
213 break;
214 case 0 /* ShouldBeInlined */:
215 result.statements.push(statement);
216 break;
217 }
218 }
219}
220// tslint:disable-next-line:cyclomatic-complexity
221function updateResultForRootSourceFile(params, result) {
222 function isReExportFromImportableModule(statement) {
223 if (!ts.isExportDeclaration(statement) || statement.moduleSpecifier === undefined || !ts.isStringLiteral(statement.moduleSpecifier)) {
224 return false;
225 }
226 var moduleFileName = resolveModuleFileName(statement.getSourceFile().fileName, statement.moduleSpecifier.text);
227 return params.getModuleInfo(moduleFileName).type === 1 /* ShouldBeImported */;
228 }
229 updateResult(params, result);
230 // add skipped by `updateResult` exports
231 for (var _i = 0, _a = params.statements; _i < _a.length; _i++) {
232 var statement = _a[_i];
233 // "export default" or "export ="
234 var isExportAssignment = ts.isExportAssignment(statement);
235 var isReExportFromImportable = isReExportFromImportableModule(statement);
236 if (isExportAssignment || isReExportFromImportable) {
237 result.statements.push(statement);
238 }
239 // export { foo, bar, baz as fooBar }
240 if (ts.isExportDeclaration(statement) && statement.exportClause !== undefined && ts.isNamedExports(statement.exportClause)) {
241 for (var _b = 0, _c = statement.exportClause.elements; _b < _c.length; _b++) {
242 var exportItem = _c[_b];
243 if (exportItem.name.getText() === 'default' && exportItem.propertyName === undefined) {
244 // export { default }
245 // return export { /get name of exportItem.name's symbol node/ as default };
246 // it seems unnecessary?
247 continue;
248 }
249 if (exportItem.propertyName !== undefined && exportItem.propertyName.getText() === 'default') {
250 var resolvedIdentifier = params.resolveIdentifier(exportItem.propertyName);
251 // export { default as name }
252 result.renamedExports.push(((resolvedIdentifier === null || resolvedIdentifier === void 0 ? void 0 : resolvedIdentifier.getText()) || '') + " as " + exportItem.name.getText());
253 continue;
254 }
255 // export { baz as propertyName }
256 if (exportItem.propertyName !== undefined) {
257 result.renamedExports.push(exportItem.getText());
258 }
259 }
260 }
261 }
262}
263function updateResultForImportedEqExportAssignment(exportAssignment, params, result) {
264 var moduleDeclarations = params.getDeclarationsForExportedAssignment(exportAssignment)
265 .filter(typescript_1.isNamespaceStatement)
266 .filter(function (s) { return s.getSourceFile() === exportAssignment.getSourceFile(); });
267 // if we have `export =` somewhere so we can decide that every declaration of exported symbol in this way
268 // is "part of the exported module" and we need to update result according every member of each declaration
269 // but treat they as current module (we do not need to update module info)
270 for (var _i = 0, moduleDeclarations_1 = moduleDeclarations; _i < moduleDeclarations_1.length; _i++) {
271 var moduleDeclaration = moduleDeclarations_1[_i];
272 if (moduleDeclaration.body === undefined || !ts.isModuleBlock(moduleDeclaration.body)) {
273 continue;
274 }
275 updateResult(__assign(__assign({}, params), { statements: moduleDeclaration.body.statements }), result);
276 }
277}
278function updateResultForModuleDeclaration(moduleDecl, params, result) {
279 if (moduleDecl.body === undefined || !ts.isModuleBlock(moduleDecl.body)) {
280 return;
281 }
282 var moduleName = moduleDecl.name.text;
283 var moduleFileName = resolveModuleFileName(params.currentModule.fileName, moduleName);
284 var moduleInfo = params.getModuleInfo(moduleFileName);
285 // if we have declaration of external module inside internal one
286 if (!params.currentModule.isExternal && moduleInfo.isExternal) {
287 // if it's allowed - we need to just add it to result without any processing
288 if (params.shouldDeclareExternalModuleBeInlined()) {
289 result.statements.push(moduleDecl);
290 }
291 return;
292 }
293 updateResult(__assign(__assign({}, params), { currentModule: moduleInfo, statements: moduleDecl.body.statements }), result);
294}
295function resolveModuleFileName(currentFileName, moduleName) {
296 return moduleName.startsWith('.') ? fix_path_1.fixPath(path.join(currentFileName, '..', moduleName)) : "node_modules/" + moduleName + "/";
297}
298function addTypesReference(library, typesReferences) {
299 if (!typesReferences.has(library)) {
300 logger_1.normalLog("Library \"" + library + "\" will be added via reference directive");
301 typesReferences.add(library);
302 }
303}
304function updateImportsForStatement(statement, params, result) {
305 if (params.currentModule.type !== 1 /* ShouldBeImported */) {
306 return;
307 }
308 var statementsToImport = ts.isVariableStatement(statement) ? statement.declarationList.declarations : [statement];
309 for (var _i = 0, statementsToImport_1 = statementsToImport; _i < statementsToImport_1.length; _i++) {
310 var statementToImport = statementsToImport_1[_i];
311 if (params.shouldStatementBeImported(statementToImport)) {
312 addImport(statementToImport, params, result.imports);
313 }
314 }
315}
316function getDeclarationUsagesSourceFiles(declaration, rootFileExports, typesUsageEvaluator, typeChecker) {
317 return new Set(getExportedSymbolsUsingStatement(declaration, rootFileExports, typesUsageEvaluator, typeChecker)
318 .map(function (symbol) { return typescript_1.getDeclarationsForSymbol(symbol); })
319 .reduce(function (acc, val) { return acc.concat(val); }, [])
320 .map(function (declaration) { return declaration.getSourceFile(); }));
321}
322function addImport(statement, params, imports) {
323 if (statement.name === undefined) {
324 throw new Error("Import/usage unnamed declaration: " + statement.getText());
325 }
326 params.getDeclarationUsagesSourceFiles(statement).forEach(function (sourceFile) {
327 sourceFile.statements
328 .filter(ts.isImportDeclaration)
329 .forEach(function (importDeclaration) {
330 var importClause = importDeclaration.importClause;
331 if (importClause === undefined) {
332 return;
333 }
334 var importModuleSpecifier = importDeclaration.moduleSpecifier.text;
335 var importItem = imports.get(importModuleSpecifier);
336 if (importItem === undefined) {
337 importItem = {
338 defaultImports: new Set(),
339 namedImports: new Set(),
340 starImports: new Set(),
341 };
342 imports.set(importModuleSpecifier, importItem);
343 }
344 if (importClause.name !== undefined && params.areDeclarationSame(statement, importClause)) {
345 // import name from 'module';
346 importItem.defaultImports.add(importClause.name.text);
347 }
348 if (importClause.namedBindings !== undefined) {
349 if (ts.isNamedImports(importClause.namedBindings)) {
350 // import { El1, El2 } from 'module';
351 importClause.namedBindings.elements
352 .filter(params.areDeclarationSame.bind(params, statement))
353 .forEach(function (specifier) { return importItem.namedImports.add(specifier.getText()); });
354 }
355 else {
356 // import * as name from 'module';
357 importItem.starImports.add(importClause.namedBindings.name.getText());
358 }
359 }
360 });
361 });
362}
363function getRootSourceFile(program, rootFileName) {
364 if (program.getRootFileNames().indexOf(rootFileName) === -1) {
365 throw new Error("There is no such root file " + rootFileName);
366 }
367 var sourceFile = program.getSourceFile(rootFileName);
368 if (sourceFile === undefined) {
369 throw new Error("Cannot get source file for root file " + rootFileName);
370 }
371 return sourceFile;
372}
373function isNodeUsed(node, rootFileExports, typesUsageEvaluator, typeChecker) {
374 if (typescript_1.isNodeNamedDeclaration(node)) {
375 var nodeSymbol_1 = getNodeSymbol(node, typeChecker);
376 if (nodeSymbol_1 === null) {
377 return false;
378 }
379 return rootFileExports.some(function (rootExport) { return typesUsageEvaluator.isSymbolUsedBySymbol(nodeSymbol_1, rootExport); });
380 }
381 else if (ts.isVariableStatement(node)) {
382 return node.declarationList.declarations.some(function (declaration) {
383 return isNodeUsed(declaration, rootFileExports, typesUsageEvaluator, typeChecker);
384 });
385 }
386 return false;
387}
388function shouldNodeBeImported(node, rootFileExports, typesUsageEvaluator, typeChecker, isDefaultLibrary) {
389 var nodeSymbol = getNodeSymbol(node, typeChecker);
390 if (nodeSymbol === null) {
391 return false;
392 }
393 var symbolDeclarations = typescript_1.getDeclarationsForSymbol(nodeSymbol);
394 var isSymbolDeclaredInDefaultLibrary = symbolDeclarations.some(function (declaration) { return isDefaultLibrary(declaration.getSourceFile()); });
395 if (isSymbolDeclaredInDefaultLibrary) {
396 // we shouldn't import a node declared in the default library (such dom, es2015)
397 // yeah, actually we should check that node is declared only in the default lib
398 // but it seems we can check that at least one declaration is from default lib
399 // to treat the node as un-importable
400 // because we can't re-export declared somewhere else node with declaration merging
401 // also, if some lib file will not be added to the project
402 // for example like it is described in the react declaration file (e.g. React Native)
403 // then here we still have a bug with "importing global declaration from a package"
404 // (see https://github.com/timocov/dts-bundle-generator/issues/71)
405 // but I don't think it is a big problem for now
406 // and it's possible that it will be fixed in https://github.com/timocov/dts-bundle-generator/issues/59
407 return false;
408 }
409 return getExportedSymbolsUsingStatement(node, rootFileExports, typesUsageEvaluator, typeChecker).length !== 0;
410}
411function getExportedSymbolsUsingStatement(node, rootFileExports, typesUsageEvaluator, typeChecker) {
412 var nodeSymbol = getNodeSymbol(node, typeChecker);
413 if (nodeSymbol === null) {
414 return [];
415 }
416 var symbolsUsingNode = typesUsageEvaluator.getSymbolsUsingSymbol(nodeSymbol);
417 if (symbolsUsingNode === null) {
418 throw new Error('Something went wrong - value cannot be null');
419 }
420 // we should import only symbols which are used in types directly
421 return Array.from(symbolsUsingNode).filter(function (symbol) {
422 var symbolsDeclarations = typescript_1.getDeclarationsForSymbol(symbol);
423 if (symbolsDeclarations.length === 0 || symbolsDeclarations.every(typescript_1.isDeclarationFromExternalModule)) {
424 return false;
425 }
426 return rootFileExports.some(function (rootSymbol) { return typesUsageEvaluator.isSymbolUsedBySymbol(symbol, rootSymbol); });
427 });
428}
429function isSourceFileDefaultLibrary(program, file) {
430 // tslint:disable-next-line:no-unnecessary-type-assertion
431 return program.isSourceFileDefaultLibrary(file);
432}
433function getNodeSymbol(node, typeChecker) {
434 var nodeName = node.name;
435 if (nodeName === undefined) {
436 return null;
437 }
438 return getDeclarationNameSymbol(nodeName, typeChecker);
439}
440function getDeclarationNameSymbol(name, typeChecker) {
441 var symbol = typeChecker.getSymbolAtLocation(name);
442 if (symbol === undefined) {
443 return null;
444 }
445 return typescript_1.getActualSymbol(symbol, typeChecker);
446}