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.

after-compile.js 8.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. const path = require("path");
  4. const constants = require("./constants");
  5. const instances_1 = require("./instances");
  6. const utils_1 = require("./utils");
  7. function makeAfterCompile(instance, configFilePath) {
  8. let getCompilerOptionDiagnostics = true;
  9. let checkAllFilesForErrors = true;
  10. return (compilation, callback) => {
  11. // Don't add errors for child compilations
  12. if (compilation.compiler.isChild()) {
  13. callback();
  14. return;
  15. }
  16. removeTSLoaderErrors(compilation.errors);
  17. provideCompilerOptionDiagnosticErrorsToWebpack(getCompilerOptionDiagnostics, compilation, instance, configFilePath);
  18. getCompilerOptionDiagnostics = false;
  19. const modules = determineModules(compilation);
  20. const filesToCheckForErrors = determineFilesToCheckForErrors(checkAllFilesForErrors, instance);
  21. checkAllFilesForErrors = false;
  22. const filesWithErrors = new Map();
  23. provideErrorsToWebpack(filesToCheckForErrors, filesWithErrors, compilation, modules, instance);
  24. provideDeclarationFilesToWebpack(filesToCheckForErrors, instance, compilation);
  25. instance.filesWithErrors = filesWithErrors;
  26. instance.modifiedFiles = null;
  27. instance.projectsMissingSourceMaps = new Set();
  28. callback();
  29. };
  30. }
  31. exports.makeAfterCompile = makeAfterCompile;
  32. /**
  33. * handle compiler option errors after the first compile
  34. */
  35. function provideCompilerOptionDiagnosticErrorsToWebpack(getCompilerOptionDiagnostics, compilation, instance, configFilePath) {
  36. if (getCompilerOptionDiagnostics) {
  37. const { languageService, loaderOptions, compiler, program } = instance;
  38. const errorsToAdd = utils_1.formatErrors(program === undefined
  39. ? languageService.getCompilerOptionsDiagnostics()
  40. : program.getOptionsDiagnostics(), loaderOptions, instance.colors, compiler, { file: configFilePath || 'tsconfig.json' }, compilation.compiler.context);
  41. compilation.errors.push(...errorsToAdd);
  42. }
  43. }
  44. /**
  45. * build map of all modules based on normalized filename
  46. * this is used for quick-lookup when trying to find modules
  47. * based on filepath
  48. */
  49. function determineModules(compilation) {
  50. return compilation.modules.reduce((modules, module) => {
  51. if (module.resource) {
  52. const modulePath = path.normalize(module.resource);
  53. const existingModules = modules.get(modulePath);
  54. if (existingModules !== undefined) {
  55. if (existingModules.indexOf(module) === -1) {
  56. existingModules.push(module);
  57. }
  58. }
  59. else {
  60. modules.set(modulePath, [module]);
  61. }
  62. }
  63. return modules;
  64. }, new Map());
  65. }
  66. function determineFilesToCheckForErrors(checkAllFilesForErrors, instance) {
  67. const { files, modifiedFiles, filesWithErrors, otherFiles } = instance;
  68. // calculate array of files to check
  69. const filesToCheckForErrors = new Map();
  70. if (checkAllFilesForErrors) {
  71. // check all files on initial run
  72. for (const [filePath, file] of files) {
  73. filesToCheckForErrors.set(filePath, file);
  74. }
  75. for (const [filePath, file] of otherFiles) {
  76. filesToCheckForErrors.set(filePath, file);
  77. }
  78. }
  79. else if (modifiedFiles !== null && modifiedFiles !== undefined) {
  80. // check all modified files, and all dependants
  81. for (const modifiedFileName of modifiedFiles.keys()) {
  82. utils_1.collectAllDependants(instance.reverseDependencyGraph, modifiedFileName).forEach(fileName => {
  83. const fileToCheckForErrors = files.get(fileName) || otherFiles.get(fileName);
  84. filesToCheckForErrors.set(fileName, fileToCheckForErrors);
  85. });
  86. }
  87. }
  88. // re-check files with errors from previous build
  89. if (filesWithErrors !== undefined) {
  90. for (const [fileWithErrorName, fileWithErrors] of filesWithErrors) {
  91. filesToCheckForErrors.set(fileWithErrorName, fileWithErrors);
  92. }
  93. }
  94. return filesToCheckForErrors;
  95. }
  96. function provideErrorsToWebpack(filesToCheckForErrors, filesWithErrors, compilation, modules, instance) {
  97. const { compiler, program, languageService, files, loaderOptions, compilerOptions, otherFiles } = instance;
  98. const filePathRegex = compilerOptions.checkJs === true
  99. ? constants.dtsTsTsxJsJsxRegex
  100. : constants.dtsTsTsxRegex;
  101. for (const filePath of filesToCheckForErrors.keys()) {
  102. if (filePath.match(filePathRegex) === null) {
  103. continue;
  104. }
  105. const sourceFile = program === undefined ? undefined : program.getSourceFile(filePath);
  106. // If the source file is undefined, that probably means it’s actually part of an unbuilt project reference,
  107. // which will have already produced a more useful error than the one we would get by proceeding here.
  108. // If it’s undefined and we’re not using project references at all, I guess carry on so the user will
  109. // get a useful error about which file was unexpectedly missing.
  110. if (utils_1.isUsingProjectReferences(instance) && sourceFile === undefined) {
  111. continue;
  112. }
  113. const errors = program === undefined
  114. ? [
  115. ...languageService.getSyntacticDiagnostics(filePath),
  116. ...languageService.getSemanticDiagnostics(filePath)
  117. ]
  118. : [
  119. ...program.getSyntacticDiagnostics(sourceFile),
  120. ...program.getSemanticDiagnostics(sourceFile)
  121. ];
  122. if (errors.length > 0) {
  123. const fileWithError = files.get(filePath) || otherFiles.get(filePath);
  124. filesWithErrors.set(filePath, fileWithError);
  125. }
  126. // if we have access to a webpack module, use that
  127. const associatedModules = modules.get(filePath);
  128. if (associatedModules !== undefined) {
  129. associatedModules.forEach(module => {
  130. // remove any existing errors
  131. removeTSLoaderErrors(module.errors);
  132. // append errors
  133. const formattedErrors = utils_1.formatErrors(errors, loaderOptions, instance.colors, compiler, { module }, compilation.compiler.context);
  134. module.errors.push(...formattedErrors);
  135. compilation.errors.push(...formattedErrors);
  136. });
  137. }
  138. else {
  139. // otherwise it's a more generic error
  140. const formattedErrors = utils_1.formatErrors(errors, loaderOptions, instance.colors, compiler, { file: filePath }, compilation.compiler.context);
  141. compilation.errors.push(...formattedErrors);
  142. }
  143. }
  144. }
  145. /**
  146. * gather all declaration files from TypeScript and output them to webpack
  147. */
  148. function provideDeclarationFilesToWebpack(filesToCheckForErrors, instance, compilation) {
  149. for (const filePath of filesToCheckForErrors.keys()) {
  150. if (filePath.match(constants.tsTsxRegex) === null) {
  151. continue;
  152. }
  153. const outputFiles = instances_1.getEmitOutput(instance, filePath);
  154. const declarationFiles = outputFiles.filter(outputFile => outputFile.name.match(constants.dtsDtsxOrDtsDtsxMapRegex));
  155. declarationFiles.forEach(declarationFile => {
  156. const assetPath = path.relative(compilation.compiler.outputPath, declarationFile.name);
  157. compilation.assets[assetPath] = {
  158. source: () => declarationFile.text,
  159. size: () => declarationFile.text.length
  160. };
  161. });
  162. }
  163. }
  164. /**
  165. * handle all other errors. The basic approach here to get accurate error
  166. * reporting is to start with a "blank slate" each compilation and gather
  167. * all errors from all files. Since webpack tracks errors in a module from
  168. * compilation-to-compilation, and since not every module always runs through
  169. * the loader, we need to detect and remove any pre-existing errors.
  170. */
  171. function removeTSLoaderErrors(errors) {
  172. let index = -1;
  173. let length = errors.length;
  174. while (++index < length) {
  175. if (errors[index].loaderSource === 'ts-loader') {
  176. errors.splice(index--, 1);
  177. length--;
  178. }
  179. }
  180. }