UNPKG

13.2 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3exports.makeAfterCompile = void 0;
4const path = require("path");
5const constants = require("./constants");
6const instances_1 = require("./instances");
7const utils_1 = require("./utils");
8/**
9 * This returns a function that has options to add assets and also to provide errors to webpack
10 * In webpack 4 we can do both during the afterCompile hook
11 * In webpack 5 only errors should be provided during aftercompile. Assets should be
12 * emitted during the afterProcessAssets hook
13 */
14function makeAfterCompile(instance, addAssets, provideErrors, configFilePath) {
15 let getCompilerOptionDiagnostics = true;
16 let checkAllFilesForErrors = true;
17 return (compilation, callback) => {
18 // Don't add errors for child compilations
19 if (compilation.compiler.isChild()) {
20 callback();
21 return;
22 }
23 if (instance.loaderOptions.transpileOnly) {
24 if (addAssets) {
25 provideAssetsFromSolutionBuilderHost(instance, compilation);
26 }
27 callback();
28 return;
29 }
30 removeCompilationTSLoaderErrors(compilation, instance.loaderOptions);
31 if (provideErrors) {
32 provideCompilerOptionDiagnosticErrorsToWebpack(getCompilerOptionDiagnostics, compilation, instance, configFilePath);
33 }
34 getCompilerOptionDiagnostics = false;
35 const modules = determineModules(compilation, instance);
36 const filesToCheckForErrors = determineFilesToCheckForErrors(checkAllFilesForErrors, instance);
37 checkAllFilesForErrors = false;
38 const filesWithErrors = new Map();
39 if (provideErrors) {
40 provideErrorsToWebpack(filesToCheckForErrors, filesWithErrors, compilation, modules, instance);
41 provideSolutionErrorsToWebpack(compilation, modules, instance);
42 }
43 if (addAssets) {
44 provideDeclarationFilesToWebpack(filesToCheckForErrors, instance, compilation);
45 provideTsBuildInfoFilesToWebpack(instance, compilation);
46 provideAssetsFromSolutionBuilderHost(instance, compilation);
47 }
48 instance.filesWithErrors = filesWithErrors;
49 instance.modifiedFiles = undefined;
50 instance.projectsMissingSourceMaps = new Set();
51 callback();
52 };
53}
54exports.makeAfterCompile = makeAfterCompile;
55/**
56 * handle compiler option errors after the first compile
57 */
58function provideCompilerOptionDiagnosticErrorsToWebpack(getCompilerOptionDiagnostics, compilation, instance, configFilePath) {
59 if (getCompilerOptionDiagnostics) {
60 const { languageService, loaderOptions, compiler, program } = instance;
61 const errors = utils_1.formatErrors(program === undefined
62 ? languageService.getCompilerOptionsDiagnostics()
63 : program.getOptionsDiagnostics(), loaderOptions, instance.colors, compiler, { file: configFilePath || 'tsconfig.json' }, compilation.compiler.context);
64 compilation.errors.push(...errors);
65 }
66}
67/**
68 * build map of all modules based on normalized filename
69 * this is used for quick-lookup when trying to find modules
70 * based on filepath
71 */
72function determineModules(compilation, { filePathKeyMapper }) {
73 const modules = new Map();
74 compilation.modules.forEach(module => {
75 if (module.resource) {
76 const modulePath = filePathKeyMapper(module.resource);
77 const existingModules = modules.get(modulePath);
78 if (existingModules !== undefined) {
79 if (!existingModules.includes(module)) {
80 existingModules.push(module);
81 }
82 }
83 else {
84 modules.set(modulePath, [module]);
85 }
86 }
87 });
88 return modules;
89}
90function determineFilesToCheckForErrors(checkAllFilesForErrors, instance) {
91 const { files, modifiedFiles, filesWithErrors, otherFiles } = instance;
92 // calculate array of files to check
93 const filesToCheckForErrors = new Map();
94 if (checkAllFilesForErrors) {
95 // check all files on initial run
96 for (const [filePath, file] of files) {
97 addFileToCheckForErrors(filePath, file);
98 }
99 for (const [filePath, file] of otherFiles) {
100 addFileToCheckForErrors(filePath, file);
101 }
102 }
103 else if (modifiedFiles !== null &&
104 modifiedFiles !== undefined &&
105 modifiedFiles.size) {
106 const reverseDependencyGraph = utils_1.populateReverseDependencyGraph(instance);
107 // check all modified files, and all dependants
108 for (const modifiedFileName of modifiedFiles.keys()) {
109 for (const fileName of utils_1.collectAllDependants(reverseDependencyGraph, modifiedFileName).keys()) {
110 const fileToCheckForErrors = files.get(fileName) || otherFiles.get(fileName);
111 addFileToCheckForErrors(fileName, fileToCheckForErrors);
112 }
113 }
114 }
115 // re-check files with errors from previous build
116 if (filesWithErrors !== undefined) {
117 for (const [fileWithErrorName, fileWithErrors] of filesWithErrors) {
118 addFileToCheckForErrors(fileWithErrorName, fileWithErrors);
119 }
120 }
121 return filesToCheckForErrors;
122 function addFileToCheckForErrors(filePath, file) {
123 if (!utils_1.isReferencedFile(instance, filePath)) {
124 filesToCheckForErrors.set(filePath, file);
125 }
126 }
127}
128function provideErrorsToWebpack(filesToCheckForErrors, filesWithErrors, compilation, modules, instance) {
129 const { compiler, files, loaderOptions, compilerOptions, otherFiles, } = instance;
130 const filePathRegex = compilerOptions.allowJs === true
131 ? constants.dtsTsTsxJsJsxRegex
132 : constants.dtsTsTsxRegex;
133 // I’m pretty sure this will never be undefined here
134 const program = utils_1.ensureProgram(instance);
135 for (const [filePath, { fileName }] of filesToCheckForErrors.entries()) {
136 if (fileName.match(filePathRegex) === null) {
137 continue;
138 }
139 const sourceFile = program && program.getSourceFile(fileName);
140 const errors = [];
141 if (program && sourceFile) {
142 errors.push(...program.getSyntacticDiagnostics(sourceFile), ...program
143 .getSemanticDiagnostics(sourceFile)
144 // Output file has not been built from source file - this message is redundant with
145 // program.getOptionsDiagnostics() separately added in instances.ts
146 .filter(({ code }) => code !== 6305));
147 }
148 if (errors.length > 0) {
149 const fileWithError = files.get(filePath) || otherFiles.get(filePath);
150 filesWithErrors.set(filePath, fileWithError);
151 }
152 // if we have access to a webpack module, use that
153 const associatedModules = modules.get(instance.filePathKeyMapper(fileName));
154 if (associatedModules !== undefined) {
155 associatedModules.forEach(module => {
156 removeModuleTSLoaderError(module, loaderOptions);
157 // append errors
158 const formattedErrors = utils_1.formatErrors(errors, loaderOptions, instance.colors, compiler, { module }, compilation.compiler.context);
159 formattedErrors.forEach(error => {
160 if (module.addError) {
161 module.addError(error);
162 }
163 else {
164 module.errors.push(error);
165 }
166 });
167 compilation.errors.push(...formattedErrors);
168 });
169 }
170 else {
171 // otherwise it's a more generic error
172 const formattedErrors = utils_1.formatErrors(errors, loaderOptions, instance.colors, compiler, { file: fileName }, compilation.compiler.context);
173 compilation.errors.push(...formattedErrors);
174 }
175 }
176}
177function provideSolutionErrorsToWebpack(compilation, modules, instance) {
178 if (!instance.solutionBuilderHost ||
179 !(instance.solutionBuilderHost.diagnostics.global.length ||
180 instance.solutionBuilderHost.diagnostics.perFile.size)) {
181 return;
182 }
183 const { compiler, loaderOptions, solutionBuilderHost: { diagnostics }, } = instance;
184 for (const [filePath, perFileDiagnostics] of diagnostics.perFile) {
185 // if we have access to a webpack module, use that
186 const associatedModules = modules.get(filePath);
187 if (associatedModules !== undefined) {
188 associatedModules.forEach(module => {
189 removeModuleTSLoaderError(module, loaderOptions);
190 // append errors
191 const formattedErrors = utils_1.formatErrors(perFileDiagnostics, loaderOptions, instance.colors, compiler, { module }, compilation.compiler.context);
192 formattedErrors.forEach(error => {
193 if (module.addError) {
194 module.addError(error);
195 }
196 else {
197 module.errors.push(error);
198 }
199 });
200 compilation.errors.push(...formattedErrors);
201 });
202 }
203 else {
204 // otherwise it's a more generic error
205 const formattedErrors = utils_1.formatErrors(perFileDiagnostics, loaderOptions, instance.colors, compiler, { file: path.resolve(perFileDiagnostics[0].file.fileName) }, compilation.compiler.context);
206 compilation.errors.push(...formattedErrors);
207 }
208 }
209 // Add global solution errors
210 compilation.errors.push(...utils_1.formatErrors(diagnostics.global, instance.loaderOptions, instance.colors, instance.compiler, { file: 'tsconfig.json' }, compilation.compiler.context));
211}
212/**
213 * gather all declaration files from TypeScript and output them to webpack
214 */
215function provideDeclarationFilesToWebpack(filesToCheckForErrors, instance, compilation) {
216 for (const { fileName } of filesToCheckForErrors.values()) {
217 if (fileName.match(constants.tsTsxRegex) === null) {
218 continue;
219 }
220 addDeclarationFilesAsAsset(instances_1.getEmitOutput(instance, fileName), compilation);
221 }
222}
223function addDeclarationFilesAsAsset(outputFiles, compilation, skipOutputFile) {
224 outputFilesToAsset(outputFiles, compilation, outputFile => skipOutputFile && skipOutputFile(outputFile)
225 ? true
226 : !outputFile.name.match(constants.dtsDtsxOrDtsDtsxMapRegex));
227}
228function outputFileToAsset(outputFile, compilation) {
229 const assetPath = path.relative(compilation.compiler.outputPath, outputFile.name);
230 compilation.assets[assetPath] = {
231 source: () => outputFile.text,
232 size: () => outputFile.text.length,
233 };
234}
235function outputFilesToAsset(outputFiles, compilation, skipOutputFile) {
236 for (const outputFile of outputFiles) {
237 if (!skipOutputFile || !skipOutputFile(outputFile)) {
238 outputFileToAsset(outputFile, compilation);
239 }
240 }
241}
242/**
243 * gather all .tsbuildinfo for the project
244 */
245function provideTsBuildInfoFilesToWebpack(instance, compilation) {
246 if (instance.watchHost) {
247 // Ensure emit is complete
248 instances_1.getEmitFromWatchHost(instance);
249 if (instance.watchHost.tsbuildinfo) {
250 outputFileToAsset(instance.watchHost.tsbuildinfo, compilation);
251 }
252 instance.watchHost.outputFiles.clear();
253 instance.watchHost.tsbuildinfo = undefined;
254 }
255}
256/**
257 * gather all solution builder assets
258 */
259function provideAssetsFromSolutionBuilderHost(instance, compilation) {
260 if (instance.solutionBuilderHost) {
261 // written files
262 outputFilesToAsset(instance.solutionBuilderHost.writtenFiles, compilation);
263 instance.solutionBuilderHost.writtenFiles.length = 0;
264 }
265}
266/**
267 * handle all other errors. The basic approach here to get accurate error
268 * reporting is to start with a "blank slate" each compilation and gather
269 * all errors from all files. Since webpack tracks errors in a module from
270 * compilation-to-compilation, and since not every module always runs through
271 * the loader, we need to detect and remove any pre-existing errors.
272 */
273function removeCompilationTSLoaderErrors(compilation, loaderOptions) {
274 compilation.errors = compilation.errors.filter(error => error.loaderSource !== utils_1.tsLoaderSource(loaderOptions));
275}
276function removeModuleTSLoaderError(module, loaderOptions) {
277 /**
278 * Since webpack 5, the `errors` property is deprecated,
279 * so we can check if some methods for reporting errors exist.
280 */
281 if (!!module.addError) {
282 const warnings = module.getWarnings();
283 const errors = module.getErrors();
284 module.clearWarningsAndErrors();
285 Array.from(warnings || []).forEach(warning => module.addWarning(warning));
286 Array.from(errors || [])
287 .filter((error) => error.loaderSource !== utils_1.tsLoaderSource(loaderOptions))
288 .forEach(error => module.addError(error));
289 }
290 else {
291 module.errors = module.errors.filter(error => error.loaderSource !== utils_1.tsLoaderSource(loaderOptions));
292 }
293}
294//# sourceMappingURL=after-compile.js.map
\No newline at end of file