You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

instances.js 9.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. const chalk_1 = require("chalk");
  4. const fs = require("fs");
  5. const path = require("path");
  6. const after_compile_1 = require("./after-compile");
  7. const compilerSetup_1 = require("./compilerSetup");
  8. const config_1 = require("./config");
  9. const constants_1 = require("./constants");
  10. const logger = require("./logger");
  11. const servicesHost_1 = require("./servicesHost");
  12. const utils_1 = require("./utils");
  13. const watch_run_1 = require("./watch-run");
  14. const instances = {};
  15. /**
  16. * The loader is executed once for each file seen by webpack. However, we need to keep
  17. * a persistent instance of TypeScript that contains all of the files in the program
  18. * along with definition files and options. This function either creates an instance
  19. * or returns the existing one. Multiple instances are possible by using the
  20. * `instance` property.
  21. */
  22. function getTypeScriptInstance(loaderOptions, loader) {
  23. if (instances.hasOwnProperty(loaderOptions.instance)) {
  24. const instance = instances[loaderOptions.instance];
  25. utils_1.ensureProgram(instance);
  26. return { instance: instances[loaderOptions.instance] };
  27. }
  28. const colors = new chalk_1.default.constructor({ enabled: loaderOptions.colors });
  29. const log = logger.makeLogger(loaderOptions, colors);
  30. const compiler = compilerSetup_1.getCompiler(loaderOptions, log);
  31. if (compiler.errorMessage !== undefined) {
  32. return { error: utils_1.makeError(colors.red(compiler.errorMessage), undefined) };
  33. }
  34. return successfulTypeScriptInstance(loaderOptions, loader, log, colors, compiler.compiler, compiler.compilerCompatible, compiler.compilerDetailsLogMessage);
  35. }
  36. exports.getTypeScriptInstance = getTypeScriptInstance;
  37. function successfulTypeScriptInstance(loaderOptions, loader, log, colors, compiler, compilerCompatible, compilerDetailsLogMessage) {
  38. const configFileAndPath = config_1.getConfigFile(compiler, colors, loader, loaderOptions, compilerCompatible, log, compilerDetailsLogMessage);
  39. if (configFileAndPath.configFileError !== undefined) {
  40. const { message, file } = configFileAndPath.configFileError;
  41. return {
  42. error: utils_1.makeError(colors.red('error while reading tsconfig.json:' + constants_1.EOL + message), file)
  43. };
  44. }
  45. const { configFilePath, configFile } = configFileAndPath;
  46. const basePath = loaderOptions.context || path.dirname(configFilePath || '');
  47. const configParseResult = config_1.getConfigParseResult(compiler, configFile, basePath);
  48. if (configParseResult.errors.length > 0 && !loaderOptions.happyPackMode) {
  49. const errors = utils_1.formatErrors(configParseResult.errors, loaderOptions, colors, compiler, { file: configFilePath }, loader.context);
  50. loader._module.errors.push(...errors);
  51. return {
  52. error: utils_1.makeError(colors.red('error while parsing tsconfig.json'), configFilePath)
  53. };
  54. }
  55. const compilerOptions = compilerSetup_1.getCompilerOptions(configParseResult);
  56. const files = new Map();
  57. const otherFiles = new Map();
  58. const appendTsTsxSuffixesIfRequired = loaderOptions.appendTsSuffixTo.length > 0 ||
  59. loaderOptions.appendTsxSuffixTo.length > 0
  60. ? (filePath) => utils_1.appendSuffixesIfMatch({
  61. '.ts': loaderOptions.appendTsSuffixTo,
  62. '.tsx': loaderOptions.appendTsxSuffixTo
  63. }, filePath)
  64. : (filePath) => filePath;
  65. // same strategy as https://github.com/s-panferov/awesome-typescript-loader/pull/531/files
  66. let { getCustomTransformers: customerTransformers } = loaderOptions;
  67. let getCustomTransformers = Function.prototype;
  68. if (typeof customerTransformers === 'function') {
  69. getCustomTransformers = customerTransformers;
  70. }
  71. else if (typeof customerTransformers === 'string') {
  72. try {
  73. customerTransformers = require(customerTransformers);
  74. }
  75. catch (err) {
  76. throw new Error(`Failed to load customTransformers from "${loaderOptions.getCustomTransformers}": ${err.message}`);
  77. }
  78. if (typeof customerTransformers !== 'function') {
  79. throw new Error(`Custom transformers in "${loaderOptions.getCustomTransformers}" should export a function, got ${typeof getCustomTransformers}`);
  80. }
  81. getCustomTransformers = customerTransformers;
  82. }
  83. if (loaderOptions.transpileOnly) {
  84. // quick return for transpiling
  85. // we do need to check for any issues with TS options though
  86. const program = configParseResult.projectReferences !== undefined
  87. ? compiler.createProgram({
  88. rootNames: configParseResult.fileNames,
  89. options: configParseResult.options,
  90. projectReferences: configParseResult.projectReferences
  91. })
  92. : compiler.createProgram([], compilerOptions);
  93. // happypack does not have _module.errors - see https://github.com/TypeStrong/ts-loader/issues/336
  94. if (!loaderOptions.happyPackMode) {
  95. const diagnostics = program.getOptionsDiagnostics();
  96. const errors = utils_1.formatErrors(diagnostics, loaderOptions, colors, compiler, { file: configFilePath || 'tsconfig.json' }, loader.context);
  97. loader._module.errors.push(...errors);
  98. }
  99. instances[loaderOptions.instance] = {
  100. compiler,
  101. compilerOptions,
  102. appendTsTsxSuffixesIfRequired,
  103. loaderOptions,
  104. files,
  105. otherFiles,
  106. program,
  107. dependencyGraph: {},
  108. reverseDependencyGraph: {},
  109. transformers: getCustomTransformers(program),
  110. colors
  111. };
  112. return { instance: instances[loaderOptions.instance] };
  113. }
  114. // Load initial files (core lib files, any files specified in tsconfig.json)
  115. let normalizedFilePath;
  116. try {
  117. const filesToLoad = loaderOptions.onlyCompileBundledFiles
  118. ? configParseResult.fileNames.filter(fileName => constants_1.dtsDtsxOrDtsDtsxMapRegex.test(fileName))
  119. : configParseResult.fileNames;
  120. filesToLoad.forEach(filePath => {
  121. normalizedFilePath = path.normalize(filePath);
  122. files.set(normalizedFilePath, {
  123. text: fs.readFileSync(normalizedFilePath, 'utf-8'),
  124. version: 0
  125. });
  126. });
  127. }
  128. catch (exc) {
  129. return {
  130. error: utils_1.makeError(colors.red(`A file specified in tsconfig.json could not be found: ${normalizedFilePath}`), normalizedFilePath)
  131. };
  132. }
  133. // if allowJs is set then we should accept js(x) files
  134. const scriptRegex = configParseResult.options.allowJs === true
  135. ? /\.tsx?$|\.jsx?$/i
  136. : /\.tsx?$/i;
  137. const instance = (instances[loaderOptions.instance] = {
  138. compiler,
  139. compilerOptions,
  140. appendTsTsxSuffixesIfRequired,
  141. loaderOptions,
  142. files,
  143. otherFiles,
  144. languageService: null,
  145. version: 0,
  146. transformers: {},
  147. dependencyGraph: {},
  148. reverseDependencyGraph: {},
  149. modifiedFiles: null,
  150. colors
  151. });
  152. if (!loader._compiler.hooks) {
  153. throw new Error("You may be using an old version of webpack; please check you're using at least version 4");
  154. }
  155. if (loaderOptions.experimentalWatchApi && compiler.createWatchProgram) {
  156. log.logInfo('Using watch api');
  157. // If there is api available for watch, use it instead of language service
  158. instance.watchHost = servicesHost_1.makeWatchHost(scriptRegex, log, loader, instance, configParseResult.projectReferences);
  159. instance.watchOfFilesAndCompilerOptions = compiler.createWatchProgram(instance.watchHost);
  160. instance.program = instance.watchOfFilesAndCompilerOptions
  161. .getProgram()
  162. .getProgram();
  163. instance.transformers = getCustomTransformers(instance.program);
  164. }
  165. else {
  166. const servicesHost = servicesHost_1.makeServicesHost(scriptRegex, log, loader, instance, loaderOptions.experimentalFileCaching, configParseResult.projectReferences);
  167. instance.languageService = compiler.createLanguageService(servicesHost.servicesHost, compiler.createDocumentRegistry());
  168. if (servicesHost.clearCache !== null) {
  169. loader._compiler.hooks.watchRun.tap('ts-loader', servicesHost.clearCache);
  170. }
  171. instance.transformers = getCustomTransformers(instance.languageService.getProgram());
  172. }
  173. loader._compiler.hooks.afterCompile.tapAsync('ts-loader', after_compile_1.makeAfterCompile(instance, configFilePath));
  174. loader._compiler.hooks.watchRun.tapAsync('ts-loader', watch_run_1.makeWatchRun(instance));
  175. return { instance };
  176. }
  177. function getEmitOutput(instance, filePath) {
  178. const program = utils_1.ensureProgram(instance);
  179. if (program !== undefined) {
  180. const outputFiles = [];
  181. const writeFile = (fileName, text, writeByteOrderMark) => outputFiles.push({ name: fileName, writeByteOrderMark, text });
  182. const sourceFile = program.getSourceFile(filePath);
  183. // The source file will be undefined if it’s part of an unbuilt project reference
  184. if (sourceFile !== undefined || !utils_1.isUsingProjectReferences(instance)) {
  185. program.emit(sourceFile, writeFile,
  186. /*cancellationToken*/ undefined,
  187. /*emitOnlyDtsFiles*/ false, instance.transformers);
  188. }
  189. return outputFiles;
  190. }
  191. else {
  192. // Emit Javascript
  193. return instance.languageService.getProgram().getSourceFile(filePath) ===
  194. undefined
  195. ? []
  196. : instance.languageService.getEmitOutput(filePath).outputFiles;
  197. }
  198. }
  199. exports.getEmitOutput = getEmitOutput;