index.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462
  1. "use strict";
  2. var __extends = (this && this.__extends) || (function () {
  3. var extendStatics = Object.setPrototypeOf ||
  4. ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
  5. function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
  6. return function (d, b) {
  7. extendStatics(d, b);
  8. function __() { this.constructor = d; }
  9. d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
  10. };
  11. })();
  12. Object.defineProperty(exports, "__esModule", { value: true });
  13. var path_1 = require("path");
  14. var fs_1 = require("fs");
  15. var os_1 = require("os");
  16. var sourceMapSupport = require("source-map-support");
  17. var mkdirp = require("mkdirp");
  18. var crypto = require("crypto");
  19. var yn = require("yn");
  20. var arrify = require("arrify");
  21. var bufferFrom = require("buffer-from");
  22. var make_error_1 = require("make-error");
  23. var util = require("util");
  24. /**
  25. * @internal
  26. */
  27. exports.INSPECT_CUSTOM = util.inspect.custom || 'inspect';
  28. /**
  29. * Debugging `ts-node`.
  30. */
  31. var shouldDebug = yn(process.env.TS_NODE_DEBUG);
  32. var debug = shouldDebug ? console.log.bind(console, 'ts-node') : function () { return undefined; };
  33. var debugFn = shouldDebug ?
  34. function (key, fn) {
  35. return function (x) {
  36. debug(key, x);
  37. return fn(x);
  38. };
  39. } :
  40. function (_, fn) { return fn; };
  41. /**
  42. * Export the current version.
  43. */
  44. exports.VERSION = require('../package.json').version;
  45. /**
  46. * Default register options.
  47. */
  48. exports.DEFAULTS = {
  49. files: yn(process.env['TS_NODE_FILES']),
  50. cache: yn(process.env['TS_NODE_CACHE'], { default: true }),
  51. pretty: yn(process.env['TS_NODE_PRETTY']),
  52. cacheDirectory: process.env['TS_NODE_CACHE_DIRECTORY'],
  53. compiler: process.env['TS_NODE_COMPILER'],
  54. compilerOptions: parse(process.env['TS_NODE_COMPILER_OPTIONS']),
  55. ignore: split(process.env['TS_NODE_IGNORE']),
  56. project: process.env['TS_NODE_PROJECT'],
  57. skipIgnore: yn(process.env['TS_NODE_SKIP_IGNORE']),
  58. skipProject: yn(process.env['TS_NODE_SKIP_PROJECT']),
  59. ignoreDiagnostics: split(process.env['TS_NODE_IGNORE_DIAGNOSTICS']),
  60. typeCheck: yn(process.env['TS_NODE_TYPE_CHECK']),
  61. transpileOnly: yn(process.env['TS_NODE_TRANSPILE_ONLY'])
  62. };
  63. /**
  64. * Default TypeScript compiler options required by `ts-node`.
  65. */
  66. var DEFAULT_COMPILER_OPTIONS = {
  67. sourceMap: true,
  68. inlineSourceMap: false,
  69. inlineSources: true,
  70. declaration: false,
  71. noEmit: false,
  72. outDir: '$$ts-node$$'
  73. };
  74. /**
  75. * Split a string array of values.
  76. */
  77. function split(value) {
  78. return typeof value === 'string' ? value.split(/ *, */g) : undefined;
  79. }
  80. exports.split = split;
  81. /**
  82. * Parse a string as JSON.
  83. */
  84. function parse(value) {
  85. return typeof value === 'string' ? JSON.parse(value) : undefined;
  86. }
  87. exports.parse = parse;
  88. /**
  89. * Replace backslashes with forward slashes.
  90. */
  91. function normalizeSlashes(value) {
  92. return value.replace(/\\/g, '/');
  93. }
  94. exports.normalizeSlashes = normalizeSlashes;
  95. /**
  96. * TypeScript diagnostics error.
  97. */
  98. var TSError = /** @class */ (function (_super) {
  99. __extends(TSError, _super);
  100. function TSError(diagnosticText, diagnosticCodes) {
  101. var _this = _super.call(this, "\u2A2F Unable to compile TypeScript:\n" + diagnosticText) || this;
  102. _this.diagnosticText = diagnosticText;
  103. _this.diagnosticCodes = diagnosticCodes;
  104. _this.name = 'TSError';
  105. return _this;
  106. }
  107. /**
  108. * @internal
  109. */
  110. TSError.prototype[exports.INSPECT_CUSTOM] = function () {
  111. return this.diagnosticText;
  112. };
  113. return TSError;
  114. }(make_error_1.BaseError));
  115. exports.TSError = TSError;
  116. /**
  117. * Return a default temp directory based on home directory of user.
  118. */
  119. function getTmpDir() {
  120. var hash = crypto.createHash('sha256').update(os_1.homedir(), 'utf8').digest('hex');
  121. return path_1.join(os_1.tmpdir(), "ts-node-" + hash);
  122. }
  123. /**
  124. * Register TypeScript compiler.
  125. */
  126. function register(opts) {
  127. if (opts === void 0) { opts = {}; }
  128. var options = Object.assign({}, exports.DEFAULTS, opts);
  129. var cacheDirectory = options.cacheDirectory || getTmpDir();
  130. var originalJsHandler = require.extensions['.js'];
  131. var ignoreDiagnostics = arrify(options.ignoreDiagnostics).concat([
  132. 6059,
  133. 18002,
  134. 18003 // "No inputs were found in config file."
  135. ]).map(Number);
  136. var memoryCache = {
  137. contents: Object.create(null),
  138. versions: Object.create(null),
  139. outputs: Object.create(null)
  140. };
  141. var ignore = options.skipIgnore ? [] : arrify(options.ignore || '/node_modules/').map(function (str) { return new RegExp(str); });
  142. // Install source map support and read from memory cache.
  143. sourceMapSupport.install({
  144. environment: 'node',
  145. retrieveFile: function (path) {
  146. return memoryCache.outputs[path];
  147. }
  148. });
  149. // Require the TypeScript compiler and configuration.
  150. var cwd = process.cwd();
  151. var compilerOptions = options.compilerOptions, project = options.project, skipProject = options.skipProject;
  152. var compiler = options.compiler || 'typescript';
  153. var typeCheck = options.typeCheck === true || options.transpileOnly !== true;
  154. var ts = require(compiler);
  155. var transformers = options.transformers || undefined;
  156. var readFile = options.readFile || ts.sys.readFile;
  157. var fileExists = options.fileExists || ts.sys.fileExists;
  158. var config = readConfig(cwd, ts, fileExists, readFile, compilerOptions, project, skipProject);
  159. var configDiagnosticList = filterDiagnostics(config.errors, ignoreDiagnostics);
  160. var extensions = ['.ts', '.tsx'];
  161. var fileNames = options.files ? config.fileNames : [];
  162. var cachedir = path_1.join(path_1.resolve(cwd, cacheDirectory), getCompilerDigest({
  163. version: ts.version,
  164. options: config.options,
  165. fileNames: fileNames,
  166. typeCheck: typeCheck,
  167. ignoreDiagnostics: ignoreDiagnostics,
  168. compiler: compiler
  169. }));
  170. var diagnosticHost = {
  171. getNewLine: function () { return os_1.EOL; },
  172. getCurrentDirectory: function () { return cwd; },
  173. getCanonicalFileName: function (path) { return path; }
  174. };
  175. var formatDiagnostics = options.pretty
  176. ? ts.formatDiagnosticsWithColorAndContext
  177. : ts.formatDiagnostics;
  178. function createTSError(diagnostics) {
  179. var diagnosticText = formatDiagnostics(diagnostics, diagnosticHost);
  180. var diagnosticCodes = diagnostics.map(function (x) { return x.code; });
  181. return new TSError(diagnosticText, diagnosticCodes);
  182. }
  183. // Render the configuration errors and exit the script.
  184. if (configDiagnosticList.length)
  185. throw createTSError(configDiagnosticList);
  186. // Enable `allowJs` when flag is set.
  187. if (config.options.allowJs) {
  188. extensions.push('.js');
  189. extensions.push('.jsx');
  190. }
  191. // Initialize files from TypeScript into project.
  192. for (var _i = 0, fileNames_1 = fileNames; _i < fileNames_1.length; _i++) {
  193. var path = fileNames_1[_i];
  194. memoryCache.versions[path] = 1;
  195. }
  196. /**
  197. * Get the extension for a transpiled file.
  198. */
  199. var getExtension = config.options.jsx === ts.JsxEmit.Preserve ?
  200. (function (path) { return /\.[tj]sx$/.test(path) ? '.jsx' : '.js'; }) :
  201. (function (_) { return '.js'; });
  202. /**
  203. * Create the basic required function using transpile mode.
  204. */
  205. var getOutput = function (code, fileName, lineOffset) {
  206. if (lineOffset === void 0) { lineOffset = 0; }
  207. var result = ts.transpileModule(code, {
  208. fileName: fileName,
  209. transformers: transformers,
  210. compilerOptions: config.options,
  211. reportDiagnostics: true
  212. });
  213. var diagnosticList = result.diagnostics ?
  214. filterDiagnostics(result.diagnostics, ignoreDiagnostics) :
  215. [];
  216. if (diagnosticList.length)
  217. throw createTSError(diagnosticList);
  218. return [result.outputText, result.sourceMapText];
  219. };
  220. var getTypeInfo = function (_code, _fileName, _position) {
  221. throw new TypeError("Type information is unavailable without \"--type-check\"");
  222. };
  223. // Use full language services when the fast option is disabled.
  224. if (typeCheck) {
  225. // Set the file contents into cache.
  226. var updateMemoryCache_1 = function (code, fileName) {
  227. if (memoryCache.contents[fileName] !== code) {
  228. memoryCache.contents[fileName] = code;
  229. memoryCache.versions[fileName] = (memoryCache.versions[fileName] || 0) + 1;
  230. }
  231. };
  232. // Create the compiler host for type checking.
  233. var serviceHost = {
  234. getScriptFileNames: function () { return Object.keys(memoryCache.versions); },
  235. getScriptVersion: function (fileName) {
  236. var version = memoryCache.versions[fileName];
  237. // We need to return `undefined` and not a string here because TypeScript will use
  238. // `getScriptVersion` and compare against their own version - which can be `undefined`.
  239. // If we don't return `undefined` it results in `undefined === "undefined"` and run
  240. // `createProgram` again (which is very slow). Using a `string` assertion here to avoid
  241. // TypeScript errors from the function signature (expects `(x: string) => string`).
  242. return version === undefined ? undefined : String(version);
  243. },
  244. getScriptSnapshot: function (fileName) {
  245. // Read contents into TypeScript memory cache.
  246. if (!Object.prototype.hasOwnProperty.call(memoryCache.contents, fileName)) {
  247. memoryCache.contents[fileName] = readFile(fileName);
  248. }
  249. var contents = memoryCache.contents[fileName];
  250. if (contents === undefined)
  251. return;
  252. return ts.ScriptSnapshot.fromString(contents);
  253. },
  254. fileExists: debugFn('fileExists', fileExists),
  255. readFile: debugFn('readFile', readFile),
  256. readDirectory: debugFn('readDirectory', ts.sys.readDirectory),
  257. getDirectories: debugFn('getDirectories', ts.sys.getDirectories),
  258. directoryExists: debugFn('directoryExists', ts.sys.directoryExists),
  259. getNewLine: function () { return os_1.EOL; },
  260. getCurrentDirectory: function () { return cwd; },
  261. getCompilationSettings: function () { return config.options; },
  262. getDefaultLibFileName: function () { return ts.getDefaultLibFilePath(config.options); },
  263. getCustomTransformers: function () { return transformers; }
  264. };
  265. var service_1 = ts.createLanguageService(serviceHost);
  266. getOutput = function (code, fileName, lineOffset) {
  267. if (lineOffset === void 0) { lineOffset = 0; }
  268. // Must set memory cache before attempting to read file.
  269. updateMemoryCache_1(code, fileName);
  270. var output = service_1.getEmitOutput(fileName);
  271. // Get the relevant diagnostics - this is 3x faster than `getPreEmitDiagnostics`.
  272. var diagnostics = service_1.getCompilerOptionsDiagnostics()
  273. .concat(service_1.getSyntacticDiagnostics(fileName))
  274. .concat(service_1.getSemanticDiagnostics(fileName));
  275. var diagnosticList = filterDiagnostics(diagnostics, ignoreDiagnostics);
  276. if (diagnosticList.length)
  277. throw createTSError(diagnosticList);
  278. if (output.emitSkipped) {
  279. throw new TypeError(path_1.relative(cwd, fileName) + ": Emit skipped");
  280. }
  281. // Throw an error when requiring `.d.ts` files.
  282. if (output.outputFiles.length === 0) {
  283. throw new TypeError('Unable to require `.d.ts` file.\n' +
  284. 'This is usually the result of a faulty configuration or import. ' +
  285. 'Make sure there is a `.js`, `.json` or another executable extension and ' +
  286. 'loader (attached before `ts-node`) available alongside ' +
  287. ("`" + path_1.basename(fileName) + "`."));
  288. }
  289. return [output.outputFiles[1].text, output.outputFiles[0].text];
  290. };
  291. getTypeInfo = function (code, fileName, position) {
  292. updateMemoryCache_1(code, fileName);
  293. var info = service_1.getQuickInfoAtPosition(fileName, position);
  294. var name = ts.displayPartsToString(info ? info.displayParts : []);
  295. var comment = ts.displayPartsToString(info ? info.documentation : []);
  296. return { name: name, comment: comment };
  297. };
  298. }
  299. var compile = readThrough(cachedir, options.cache === true, memoryCache, getOutput, getExtension);
  300. var register = { cwd: cwd, compile: compile, getTypeInfo: getTypeInfo, extensions: extensions, cachedir: cachedir, ts: ts };
  301. // Register the extensions.
  302. extensions.forEach(function (extension) {
  303. registerExtension(extension, ignore, register, originalJsHandler);
  304. });
  305. return register;
  306. }
  307. exports.register = register;
  308. /**
  309. * Check if the filename should be ignored.
  310. */
  311. function shouldIgnore(filename, ignore) {
  312. var relname = normalizeSlashes(filename);
  313. return ignore.some(function (x) { return x.test(relname); });
  314. }
  315. /**
  316. * Register the extension for node.
  317. */
  318. function registerExtension(ext, ignore, register, originalHandler) {
  319. var old = require.extensions[ext] || originalHandler;
  320. require.extensions[ext] = function (m, filename) {
  321. if (shouldIgnore(filename, ignore)) {
  322. return old(m, filename);
  323. }
  324. var _compile = m._compile;
  325. m._compile = function (code, fileName) {
  326. debug('module._compile', fileName);
  327. return _compile.call(this, register.compile(code, fileName), fileName);
  328. };
  329. return old(m, filename);
  330. };
  331. }
  332. /**
  333. * Do post-processing on config options to support `ts-node`.
  334. */
  335. function fixConfig(ts, config) {
  336. // Delete options that *should not* be passed through.
  337. delete config.options.out;
  338. delete config.options.outFile;
  339. delete config.options.composite;
  340. delete config.options.declarationDir;
  341. delete config.options.declarationMap;
  342. delete config.options.emitDeclarationOnly;
  343. // Target ES5 output by default (instead of ES3).
  344. if (config.options.target === undefined) {
  345. config.options.target = ts.ScriptTarget.ES5;
  346. }
  347. // Target CommonJS modules by default (instead of magically switching to ES6 when the target is ES6).
  348. if (config.options.module === undefined) {
  349. config.options.module = ts.ModuleKind.CommonJS;
  350. }
  351. return config;
  352. }
  353. /**
  354. * Load TypeScript configuration.
  355. */
  356. function readConfig(cwd, ts, fileExists, readFile, compilerOptions, project, noProject) {
  357. var config = { compilerOptions: {} };
  358. var basePath = normalizeSlashes(cwd);
  359. var configFileName = undefined;
  360. // Read project configuration when available.
  361. if (!noProject) {
  362. configFileName = project
  363. ? normalizeSlashes(path_1.resolve(cwd, project))
  364. : ts.findConfigFile(normalizeSlashes(cwd), fileExists);
  365. if (configFileName) {
  366. var result = ts.readConfigFile(configFileName, readFile);
  367. // Return diagnostics.
  368. if (result.error) {
  369. return { errors: [result.error], fileNames: [], options: {} };
  370. }
  371. config = result.config;
  372. basePath = normalizeSlashes(path_1.dirname(configFileName));
  373. }
  374. }
  375. // Override default configuration options `ts-node` requires.
  376. config.compilerOptions = Object.assign({}, config.compilerOptions, compilerOptions, DEFAULT_COMPILER_OPTIONS);
  377. return fixConfig(ts, ts.parseJsonConfigFileContent(config, ts.sys, basePath, undefined, configFileName));
  378. }
  379. /**
  380. * Wrap the function with caching.
  381. */
  382. function readThrough(cachedir, shouldCache, memoryCache, compile, getExtension) {
  383. if (shouldCache === false) {
  384. return function (code, fileName, lineOffset) {
  385. debug('readThrough', fileName);
  386. var _a = compile(code, fileName, lineOffset), value = _a[0], sourceMap = _a[1];
  387. var output = updateOutput(value, fileName, sourceMap, getExtension);
  388. memoryCache.outputs[fileName] = output;
  389. return output;
  390. };
  391. }
  392. // Make sure the cache directory exists before continuing.
  393. mkdirp.sync(cachedir);
  394. return function (code, fileName, lineOffset) {
  395. debug('readThrough', fileName);
  396. var cachePath = path_1.join(cachedir, getCacheName(code, fileName));
  397. var extension = getExtension(fileName);
  398. var outputPath = "" + cachePath + extension;
  399. try {
  400. var output_1 = fs_1.readFileSync(outputPath, 'utf8');
  401. if (isValidCacheContent(output_1)) {
  402. memoryCache.outputs[fileName] = output_1;
  403. return output_1;
  404. }
  405. }
  406. catch (err) { /* Ignore. */ }
  407. var _a = compile(code, fileName, lineOffset), value = _a[0], sourceMap = _a[1];
  408. var output = updateOutput(value, fileName, sourceMap, getExtension);
  409. memoryCache.outputs[fileName] = output;
  410. fs_1.writeFileSync(outputPath, output);
  411. return output;
  412. };
  413. }
  414. /**
  415. * Update the output remapping the source map.
  416. */
  417. function updateOutput(outputText, fileName, sourceMap, getExtension) {
  418. var base64Map = bufferFrom(updateSourceMap(sourceMap, fileName), 'utf8').toString('base64');
  419. var sourceMapContent = "data:application/json;charset=utf-8;base64," + base64Map;
  420. var sourceMapLength = (path_1.basename(fileName) + ".map").length + (getExtension(fileName).length - path_1.extname(fileName).length);
  421. return outputText.slice(0, -sourceMapLength) + sourceMapContent;
  422. }
  423. /**
  424. * Update the source map contents for improved output.
  425. */
  426. function updateSourceMap(sourceMapText, fileName) {
  427. var sourceMap = JSON.parse(sourceMapText);
  428. sourceMap.file = fileName;
  429. sourceMap.sources = [fileName];
  430. delete sourceMap.sourceRoot;
  431. return JSON.stringify(sourceMap);
  432. }
  433. /**
  434. * Get the file name for the cache entry.
  435. */
  436. function getCacheName(sourceCode, fileName) {
  437. return crypto.createHash('sha256')
  438. .update(path_1.extname(fileName), 'utf8')
  439. .update('\x00', 'utf8')
  440. .update(sourceCode, 'utf8')
  441. .digest('hex');
  442. }
  443. /**
  444. * Ensure the given cached content is valid by sniffing for a base64 encoded '}'
  445. * at the end of the content, which should exist if there is a valid sourceMap present.
  446. */
  447. function isValidCacheContent(contents) {
  448. return /(?:9|0=|Q==)$/.test(contents.slice(-3));
  449. }
  450. /**
  451. * Create a hash of the current configuration.
  452. */
  453. function getCompilerDigest(obj) {
  454. return crypto.createHash('sha256').update(JSON.stringify(obj), 'utf8').digest('hex');
  455. }
  456. /**
  457. * Filter diagnostics.
  458. */
  459. function filterDiagnostics(diagnostics, ignore) {
  460. return diagnostics.filter(function (x) { return ignore.indexOf(x.code) === -1; });
  461. }
  462. //# sourceMappingURL=index.js.map