UNPKG

tsickle

Version:

Transpile TypeScript code to JavaScript with Closure annotations.

250 lines (248 loc) 14.4 kB
"use strict"; var path = require("path"); var source_map_1 = require("source-map"); var ts = require("typescript"); var decorator_annotator_1 = require("./decorator-annotator"); var es5processor_1 = require("./es5processor"); var modules_manifest_1 = require("./modules_manifest"); var sourceMapUtils = require("./source_map_utils"); var tsickle_1 = require("./tsickle"); /** * Tsickle can perform 2 different precompilation transforms - decorator downleveling * and closurization. Both require tsc to have already type checked their * input, so they can't both be run in one call to tsc. If you only want one of * the transforms, you can specify it in the constructor, if you want both, you'll * have to specify it by calling reconfigureForRun() with the appropriate Pass. */ var Pass; (function (Pass) { Pass[Pass["NONE"] = 0] = "NONE"; Pass[Pass["DECORATOR_DOWNLEVEL"] = 1] = "DECORATOR_DOWNLEVEL"; Pass[Pass["CLOSURIZE"] = 2] = "CLOSURIZE"; })(Pass = exports.Pass || (exports.Pass = {})); var ANNOTATION_SUPPORT = "\ninterface DecoratorInvocation {\n type: Function;\n args?: any[];\n}\n"; /** * TsickleCompilerHost does tsickle processing of input files, including * closure type annotation processing, decorator downleveling and * require -> googmodule rewriting. */ var TsickleCompilerHost = (function () { function TsickleCompilerHost(delegate, tscOptions, options, environment) { this.delegate = delegate; this.tscOptions = tscOptions; this.options = options; this.environment = environment; // The manifest of JS modules output by the compiler. this.modulesManifest = new modules_manifest_1.ModulesManifest(); /** Error messages produced by tsickle, if any. */ this.diagnostics = []; /** externs.js files produced by tsickle, if any. */ this.externs = {}; this.sourceFileToPreexistingSourceMap = new Map(); this.preexistingSourceMaps = new Map(); this.decoratorDownlevelSourceMaps = new Map(); this.tsickleSourceMaps = new Map(); } /** * Tsickle can perform 2 kinds of precompilation source transforms - decorator * downleveling and closurization. They can't be run in the same run of the * typescript compiler, because they both depend on type information that comes * from running the compiler. We need to use the same compiler host to run both * so we have all the source map data when finally write out. Thus if we want * to run both transforms, we call reconfigureForRun() between the calls to * ts.createProgram(). */ TsickleCompilerHost.prototype.reconfigureForRun = function (oldProgram, pass) { this.runConfiguration = { oldProgram: oldProgram, pass: pass }; }; TsickleCompilerHost.prototype.getSourceFile = function (fileName, languageVersion, onError) { if (this.runConfiguration === undefined || this.runConfiguration.pass === Pass.NONE) { var sourceFile_1 = this.delegate.getSourceFile(fileName, languageVersion, onError); return this.stripAndStoreExistingSourceMap(sourceFile_1); } var sourceFile = this.runConfiguration.oldProgram.getSourceFile(fileName); switch (this.runConfiguration.pass) { case Pass.DECORATOR_DOWNLEVEL: return this.downlevelDecorators(sourceFile, this.runConfiguration.oldProgram, fileName, languageVersion); case Pass.CLOSURIZE: return this.closurize(sourceFile, this.runConfiguration.oldProgram, fileName, languageVersion); default: throw new Error('tried to use TsickleCompilerHost with unknown pass enum'); } }; TsickleCompilerHost.prototype.writeFile = function (fileName, content, writeByteOrderMark, onError, sourceFiles) { if (path.extname(fileName) !== '.map') { if (!tsickle_1.isDtsFileName(fileName) && this.tscOptions.inlineSourceMap) { content = this.combineInlineSourceMaps(fileName, content); } if (this.options.googmodule && !tsickle_1.isDtsFileName(fileName)) { content = this.convertCommonJsToGoogModule(fileName, content); } } else { content = this.combineSourceMaps(fileName, content); } this.delegate.writeFile(fileName, content, writeByteOrderMark, onError, sourceFiles); }; TsickleCompilerHost.prototype.getSourceMapKeyForPathAndName = function (outputFilePath, sourceFileName) { var fileDir = path.dirname(outputFilePath); return this.getCanonicalFileName(path.resolve(fileDir, sourceFileName)); }; TsickleCompilerHost.prototype.getSourceMapKeyForSourceFile = function (sourceFile) { return this.getCanonicalFileName(path.resolve(sourceFile.path)); }; TsickleCompilerHost.prototype.stripAndStoreExistingSourceMap = function (sourceFile) { if (sourceMapUtils.containsInlineSourceMap(sourceFile.text)) { var sourceMapJson = sourceMapUtils.extractInlineSourceMap(sourceFile.text); var sourceMap_1 = sourceMapUtils.sourceMapTextToGenerator(sourceMapJson); var stripedSourceText = sourceMapUtils.removeInlineSourceMap(sourceFile.text); var stripedSourceFile = ts.createSourceFile(sourceFile.fileName, stripedSourceText, sourceFile.languageVersion); this.sourceFileToPreexistingSourceMap.set(stripedSourceFile, sourceMap_1); return stripedSourceFile; } return sourceFile; }; TsickleCompilerHost.prototype.combineSourceMaps = function (filePath, tscSourceMapText) { var _this = this; // We stripe inline source maps off source files before they've been parsed // which is before they have path properties, so we need to construct the // map of sourceMapKey to preexistingSourceMap after the whole program has been // loaded. if (this.sourceFileToPreexistingSourceMap.size > 0 && this.preexistingSourceMaps.size === 0) { this.sourceFileToPreexistingSourceMap.forEach(function (sourceMap, sourceFile) { var sourceMapKey = _this.getSourceMapKeyForSourceFile(sourceFile); _this.preexistingSourceMaps.set(sourceMapKey, sourceMap); }); } var tscSourceMapConsumer = sourceMapUtils.sourceMapTextToConsumer(tscSourceMapText); var tscSourceMapGenerator = sourceMapUtils.sourceMapConsumerToGenerator(tscSourceMapConsumer); if (this.tsickleSourceMaps.size > 0) { // TODO(lucassloan): remove when the .d.ts has the correct types for (var _i = 0, _a = tscSourceMapConsumer.sources; _i < _a.length; _i++) { var sourceFileName = _a[_i]; var sourceMapKey = this.getSourceMapKeyForPathAndName(filePath, sourceFileName); var tsickleSourceMapGenerator = this.tsickleSourceMaps.get(sourceMapKey); var tsickleSourceMapConsumer = sourceMapUtils.sourceMapGeneratorToConsumerWithFileName(tsickleSourceMapGenerator, sourceFileName); tscSourceMapGenerator.applySourceMap(tsickleSourceMapConsumer); } } if (this.decoratorDownlevelSourceMaps.size > 0) { // TODO(lucassloan): remove when the .d.ts has the correct types for (var _b = 0, _c = tscSourceMapConsumer.sources; _b < _c.length; _b++) { var sourceFileName = _c[_b]; var sourceMapKey = this.getSourceMapKeyForPathAndName(filePath, sourceFileName); var decoratorDownlevelSourceMapGenerator = this.decoratorDownlevelSourceMaps.get(sourceMapKey); var decoratorDownlevelSourceMapConsumer = sourceMapUtils.sourceMapGeneratorToConsumerWithFileName(decoratorDownlevelSourceMapGenerator, sourceFileName); tscSourceMapGenerator.applySourceMap(decoratorDownlevelSourceMapConsumer); } } if (this.preexistingSourceMaps.size > 0) { // TODO(lucassloan): remove when the .d.ts has the correct types for (var _d = 0, _e = tscSourceMapConsumer.sources; _d < _e.length; _d++) { var sourceFileName = _e[_d]; var sourceMapKey = this.getSourceMapKeyForPathAndName(filePath, sourceFileName); var preexistingSourceMapGenerator = this.preexistingSourceMaps.get(sourceMapKey); if (preexistingSourceMapGenerator) { var preexistingSourceMapConsumer = new source_map_1.SourceMapConsumer(preexistingSourceMapGenerator.toJSON()); tscSourceMapGenerator.applySourceMap(preexistingSourceMapConsumer); } } } return tscSourceMapGenerator.toString(); }; TsickleCompilerHost.prototype.combineInlineSourceMaps = function (filePath, compiledJsWithInlineSourceMap) { var sourceMapJson = sourceMapUtils.extractInlineSourceMap(compiledJsWithInlineSourceMap); var composedSourceMap = this.combineSourceMaps(filePath, sourceMapJson); return sourceMapUtils.setInlineSourceMap(compiledJsWithInlineSourceMap, composedSourceMap); }; TsickleCompilerHost.prototype.convertCommonJsToGoogModule = function (fileName, content) { var moduleId = this.environment.fileNameToModuleId(fileName); var _a = es5processor_1.processES5(fileName, moduleId, content, this.environment.pathToModuleName.bind(this.environment), this.options.es5Mode, this.options.prelude), output = _a.output, referencedModules = _a.referencedModules; var moduleName = this.environment.pathToModuleName('', fileName); this.modulesManifest.addModule(fileName, moduleName); for (var _i = 0, referencedModules_1 = referencedModules; _i < referencedModules_1.length; _i++) { var referenced = referencedModules_1[_i]; this.modulesManifest.addReferencedModule(fileName, referenced); } return output; }; TsickleCompilerHost.prototype.downlevelDecorators = function (sourceFile, program, fileName, languageVersion) { this.decoratorDownlevelSourceMaps.set(this.getSourceMapKeyForSourceFile(sourceFile), new source_map_1.SourceMapGenerator()); if (this.environment.shouldSkipTsickleProcessing(fileName)) return sourceFile; var fileContent = sourceFile.text; var converted = decorator_annotator_1.convertDecorators(program.getTypeChecker(), sourceFile); if (converted.diagnostics) { (_a = this.diagnostics).push.apply(_a, converted.diagnostics); } if (converted.output === fileContent) { // No changes; reuse the existing parse. return sourceFile; } fileContent = converted.output + ANNOTATION_SUPPORT; this.decoratorDownlevelSourceMaps.set(this.getSourceMapKeyForSourceFile(sourceFile), converted.sourceMap); return ts.createSourceFile(fileName, fileContent, languageVersion, true); var _a; }; TsickleCompilerHost.prototype.closurize = function (sourceFile, program, fileName, languageVersion) { this.tsickleSourceMaps.set(this.getSourceMapKeyForSourceFile(sourceFile), new source_map_1.SourceMapGenerator()); var isDefinitions = tsickle_1.isDtsFileName(fileName); // Don't tsickle-process any d.ts that isn't a compilation target; // this means we don't process e.g. lib.d.ts. if (isDefinitions && this.environment.shouldSkipTsickleProcessing(fileName)) return sourceFile; var _a = tsickle_1.annotate(program, sourceFile, this.options, this.delegate, this.tscOptions), output = _a.output, externs = _a.externs, diagnostics = _a.diagnostics, sourceMap = _a.sourceMap; if (externs) { this.externs[fileName] = externs; } if (this.environment.shouldIgnoreWarningsForPath(sourceFile.path)) { // All diagnostics (including warnings) are treated as errors. // If we've decided to ignore them, just discard them. // Warnings include stuff like "don't use @type in your jsdoc"; tsickle // warns and then fixes up the code to be Closure-compatible anyway. diagnostics = diagnostics.filter(function (d) { return d.category === ts.DiagnosticCategory.Error; }); } this.diagnostics = diagnostics; this.tsickleSourceMaps.set(this.getSourceMapKeyForSourceFile(sourceFile), sourceMap); return ts.createSourceFile(fileName, output, languageVersion, true); }; /** Concatenate all generated externs definitions together into a string. */ TsickleCompilerHost.prototype.getGeneratedExterns = function () { var allExterns = ''; for (var _i = 0, _a = Object.keys(this.externs); _i < _a.length; _i++) { var fileName = _a[_i]; allExterns += "// externs from " + fileName + ":\n"; allExterns += this.externs[fileName]; } return allExterns; }; // Delegate everything else to the original compiler host. TsickleCompilerHost.prototype.fileExists = function (fileName) { return this.delegate.fileExists(fileName); }; TsickleCompilerHost.prototype.getCurrentDirectory = function () { return this.delegate.getCurrentDirectory(); }; ; TsickleCompilerHost.prototype.useCaseSensitiveFileNames = function () { return this.delegate.useCaseSensitiveFileNames(); }; TsickleCompilerHost.prototype.getNewLine = function () { return this.delegate.getNewLine(); }; TsickleCompilerHost.prototype.getDirectories = function (path) { return this.delegate.getDirectories(path); }; TsickleCompilerHost.prototype.readFile = function (fileName) { return this.delegate.readFile(fileName); }; TsickleCompilerHost.prototype.getDefaultLibFileName = function (options) { return this.delegate.getDefaultLibFileName(options); }; TsickleCompilerHost.prototype.getCanonicalFileName = function (fileName) { return this.delegate.getCanonicalFileName(fileName); }; return TsickleCompilerHost; }()); exports.TsickleCompilerHost = TsickleCompilerHost; //# sourceMappingURL=tsickle_compiler_host.js.map