compiler.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. "use strict";
  2. var __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. };
  13. var __values = (this && this.__values) || function(o) {
  14. var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
  15. if (m) return m.call(o);
  16. if (o && typeof o.length === "number") return {
  17. next: function () {
  18. if (o && i >= o.length) o = void 0;
  19. return { value: o && o[i++], done: !o };
  20. }
  21. };
  22. throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
  23. };
  24. var __read = (this && this.__read) || function (o, n) {
  25. var m = typeof Symbol === "function" && o[Symbol.iterator];
  26. if (!m) return o;
  27. var i = m.call(o), r, ar = [], e;
  28. try {
  29. while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
  30. }
  31. catch (error) { e = { error: error }; }
  32. finally {
  33. try {
  34. if (r && !r.done && (m = i["return"])) m.call(i);
  35. }
  36. finally { if (e) throw e.error; }
  37. }
  38. return ar;
  39. };
  40. Object.defineProperty(exports, "__esModule", { value: true });
  41. var bs_logger_1 = require("bs-logger");
  42. var bufferFrom = require("buffer-from");
  43. var stableStringify = require("fast-json-stable-stringify");
  44. var fs_1 = require("fs");
  45. var memoize = require("lodash.memoize");
  46. var mkdirp = require("mkdirp");
  47. var path_1 = require("path");
  48. var messages_1 = require("./util/messages");
  49. var sha1_1 = require("./util/sha1");
  50. var hasOwn = Object.prototype.hasOwnProperty;
  51. function createCompiler(configs) {
  52. var e_1, _a, _b, _c;
  53. var logger = configs.logger.child({ namespace: 'ts-compiler' });
  54. logger.debug('creating typescript compiler', configs.tsJest.isolatedModules ? '(isolated modules)' : '(language service)');
  55. var cachedir = configs.tsCacheDir;
  56. var memoryCache = {
  57. contents: Object.create(null),
  58. versions: Object.create(null),
  59. outputs: Object.create(null),
  60. };
  61. var ts = configs.compilerModule;
  62. var cwd = configs.cwd;
  63. var extensions = ['.ts', '.tsx'];
  64. var _d = configs.typescript, compilerOptions = _d.options, fileNames = _d.fileNames;
  65. if (compilerOptions.allowJs) {
  66. extensions.push('.js');
  67. extensions.push('.jsx');
  68. }
  69. try {
  70. for (var fileNames_1 = __values(fileNames), fileNames_1_1 = fileNames_1.next(); !fileNames_1_1.done; fileNames_1_1 = fileNames_1.next()) {
  71. var path = fileNames_1_1.value;
  72. memoryCache.versions[path] = 1;
  73. }
  74. }
  75. catch (e_1_1) { e_1 = { error: e_1_1 }; }
  76. finally {
  77. try {
  78. if (fileNames_1_1 && !fileNames_1_1.done && (_a = fileNames_1.return)) _a.call(fileNames_1);
  79. }
  80. finally { if (e_1) throw e_1.error; }
  81. }
  82. var getExtension = compilerOptions.jsx === ts.JsxEmit.Preserve
  83. ? function (path) { return (/\.[tj]sx$/.test(path) ? '.jsx' : '.js'); }
  84. : function (_) { return '.js'; };
  85. var transformers = configs.tsCustomTransformers;
  86. var getOutput = function (code, fileName) {
  87. logger.debug({ fileName: fileName }, 'getOutput(): compiling as isolated module');
  88. var result = ts.transpileModule(code, {
  89. fileName: fileName,
  90. transformers: transformers,
  91. compilerOptions: compilerOptions,
  92. reportDiagnostics: configs.shouldReportDiagnostic(fileName),
  93. });
  94. if (result.diagnostics)
  95. configs.raiseDiagnostics(result.diagnostics, fileName, logger);
  96. return [result.outputText, result.sourceMapText];
  97. };
  98. var getTypeInfo = function (_code, _fileName, _position) {
  99. throw new TypeError(messages_1.Errors.TypesUnavailableWithoutTypeCheck);
  100. };
  101. if (!configs.tsJest.isolatedModules) {
  102. var updateMemoryCache_1 = function (code, fileName) {
  103. logger.debug({ fileName: fileName }, "updateMemoryCache()");
  104. if (memoryCache.contents[fileName] !== code) {
  105. memoryCache.contents[fileName] = code;
  106. memoryCache.versions[fileName] = (memoryCache.versions[fileName] || 0) + 1;
  107. }
  108. };
  109. var serviceHostDebugCtx = (_b = {},
  110. _b[bs_logger_1.LogContexts.logLevel] = bs_logger_1.LogLevels.debug,
  111. _b.namespace = 'ts:serviceHost',
  112. _b.call = null,
  113. _b);
  114. var serviceHostTraceCtx = __assign(__assign({}, serviceHostDebugCtx), (_c = {}, _c[bs_logger_1.LogContexts.logLevel] = bs_logger_1.LogLevels.trace, _c));
  115. var serviceHost = {
  116. getScriptFileNames: function () { return Object.keys(memoryCache.versions); },
  117. getScriptVersion: function (fileName) {
  118. var normalizedFileName = path_1.normalize(fileName);
  119. var version = memoryCache.versions[normalizedFileName];
  120. return version === undefined ? undefined : String(version);
  121. },
  122. getScriptSnapshot: function (fileName) {
  123. var normalizedFileName = path_1.normalize(fileName);
  124. var hit = hasOwn.call(memoryCache.contents, normalizedFileName);
  125. logger.trace({ normalizedFileName: normalizedFileName, cacheHit: hit }, "getScriptSnapshot():", 'cache', hit ? 'hit' : 'miss');
  126. if (!hit) {
  127. memoryCache.contents[normalizedFileName] = ts.sys.readFile(normalizedFileName);
  128. }
  129. var contents = memoryCache.contents[normalizedFileName];
  130. if (contents === undefined) {
  131. return;
  132. }
  133. return ts.ScriptSnapshot.fromString(contents);
  134. },
  135. fileExists: memoize(ts.sys.fileExists),
  136. readFile: logger.wrap(serviceHostTraceCtx, 'readFile', memoize(ts.sys.readFile)),
  137. readDirectory: memoize(ts.sys.readDirectory),
  138. getDirectories: memoize(ts.sys.getDirectories),
  139. directoryExists: memoize(ts.sys.directoryExists),
  140. realpath: memoize(ts.sys.realpath),
  141. getNewLine: function () { return '\n'; },
  142. getCurrentDirectory: function () { return cwd; },
  143. getCompilationSettings: function () { return compilerOptions; },
  144. getDefaultLibFileName: function () { return ts.getDefaultLibFilePath(compilerOptions); },
  145. getCustomTransformers: function () { return transformers; },
  146. };
  147. logger.debug('creating language service');
  148. var service_1 = ts.createLanguageService(serviceHost);
  149. getOutput = function (code, fileName) {
  150. logger.debug({ fileName: fileName }, 'getOutput(): compiling using language service');
  151. updateMemoryCache_1(code, fileName);
  152. var output = service_1.getEmitOutput(fileName);
  153. if (configs.shouldReportDiagnostic(fileName)) {
  154. logger.debug({ fileName: fileName }, 'getOutput(): computing diagnostics');
  155. var diagnostics = service_1
  156. .getCompilerOptionsDiagnostics()
  157. .concat(service_1.getSyntacticDiagnostics(fileName))
  158. .concat(service_1.getSemanticDiagnostics(fileName));
  159. configs.raiseDiagnostics(diagnostics, fileName, logger);
  160. }
  161. if (output.emitSkipped) {
  162. throw new TypeError(path_1.relative(cwd, fileName) + ": Emit skipped");
  163. }
  164. if (output.outputFiles.length === 0) {
  165. throw new TypeError(messages_1.interpolate(messages_1.Errors.UnableToRequireDefinitionFile, {
  166. file: path_1.basename(fileName),
  167. }));
  168. }
  169. return [output.outputFiles[1].text, output.outputFiles[0].text];
  170. };
  171. getTypeInfo = function (code, fileName, position) {
  172. updateMemoryCache_1(code, fileName);
  173. var info = service_1.getQuickInfoAtPosition(fileName, position);
  174. var name = ts.displayPartsToString(info ? info.displayParts : []);
  175. var comment = ts.displayPartsToString(info ? info.documentation : []);
  176. return { name: name, comment: comment };
  177. };
  178. }
  179. var compile = readThrough(cachedir, memoryCache, getOutput, getExtension, cwd, logger);
  180. return { cwd: cwd, compile: compile, getTypeInfo: getTypeInfo, extensions: extensions, cachedir: cachedir, ts: ts };
  181. }
  182. exports.createCompiler = createCompiler;
  183. function readThrough(cachedir, memoryCache, compile, getExtension, cwd, logger) {
  184. if (!cachedir) {
  185. return function (code, fileName, lineOffset) {
  186. logger.debug({ fileName: fileName }, 'readThrough(): no cache');
  187. var _a = __read(compile(code, fileName, lineOffset), 2), value = _a[0], sourceMap = _a[1];
  188. var output = updateOutput(value, fileName, sourceMap, getExtension, cwd);
  189. memoryCache.outputs[fileName] = output;
  190. return output;
  191. };
  192. }
  193. mkdirp.sync(cachedir);
  194. return function (code, fileName, lineOffset) {
  195. var cachePath = path_1.join(cachedir, getCacheName(code, fileName));
  196. var extension = getExtension(fileName);
  197. var outputPath = "" + cachePath + extension;
  198. try {
  199. var output_1 = fs_1.readFileSync(outputPath, 'utf8');
  200. if (isValidCacheContent(output_1)) {
  201. logger.debug({ fileName: fileName }, 'readThrough(): cache hit');
  202. memoryCache.outputs[fileName] = output_1;
  203. return output_1;
  204. }
  205. }
  206. catch (err) { }
  207. logger.debug({ fileName: fileName }, 'readThrough(): cache miss');
  208. var _a = __read(compile(code, fileName, lineOffset), 2), value = _a[0], sourceMap = _a[1];
  209. var output = updateOutput(value, fileName, sourceMap, getExtension, cwd);
  210. logger.debug({ fileName: fileName, outputPath: outputPath }, 'readThrough(): writing caches');
  211. memoryCache.outputs[fileName] = output;
  212. fs_1.writeFileSync(outputPath, output);
  213. return output;
  214. };
  215. }
  216. function updateOutput(outputText, fileName, sourceMap, getExtension, sourceRoot) {
  217. var base = path_1.basename(fileName);
  218. var base64Map = bufferFrom(updateSourceMap(sourceMap, fileName, sourceRoot), 'utf8').toString('base64');
  219. var sourceMapContent = "data:application/json;charset=utf-8;base64," + base64Map;
  220. var sourceMapLength = (base + ".map").length + (getExtension(fileName).length - path_1.extname(fileName).length);
  221. return outputText.slice(0, -sourceMapLength) + sourceMapContent;
  222. }
  223. function updateSourceMap(sourceMapText, fileName, _sourceRoot) {
  224. var sourceMap = JSON.parse(sourceMapText);
  225. sourceMap.file = fileName;
  226. sourceMap.sources = [fileName];
  227. delete sourceMap.sourceRoot;
  228. return stableStringify(sourceMap);
  229. }
  230. function getCacheName(sourceCode, fileName) {
  231. return sha1_1.sha1(fileName, '\x00', sourceCode);
  232. }
  233. function isValidCacheContent(contents) {
  234. return /(?:9|0=|Q==)$/.test(contents.slice(-3));
  235. }