ts-loader
Version:
TypeScript loader for webpack
256 lines • 11.8 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
const path = require("path");
const constants = require("./constants");
const instances_1 = require("./instances");
const utils_1 = require("./utils");
function makeAfterCompile(instance, configFilePath) {
let getCompilerOptionDiagnostics = true;
let checkAllFilesForErrors = true;
return (compilation, callback) => {
// Don't add errors for child compilations
if (compilation.compiler.isChild()) {
callback();
return;
}
if (instance.loaderOptions.transpileOnly) {
provideAssetsFromSolutionBuilderHost(instance, compilation);
callback();
return;
}
removeTSLoaderErrors(compilation.errors);
provideCompilerOptionDiagnosticErrorsToWebpack(getCompilerOptionDiagnostics, compilation, instance, configFilePath);
getCompilerOptionDiagnostics = false;
const modules = determineModules(compilation);
const filesToCheckForErrors = determineFilesToCheckForErrors(checkAllFilesForErrors, instance);
checkAllFilesForErrors = false;
const filesWithErrors = new Map();
provideErrorsToWebpack(filesToCheckForErrors, filesWithErrors, compilation, modules, instance);
provideDeclarationFilesToWebpack(filesToCheckForErrors, instance, compilation);
provideTsBuildInfoFilesToWebpack(instance, compilation);
provideSolutionErrorsToWebpack(compilation, modules, instance);
provideAssetsFromSolutionBuilderHost(instance, compilation);
instance.filesWithErrors = filesWithErrors;
instance.modifiedFiles = undefined;
instance.projectsMissingSourceMaps = new Set();
callback();
};
}
exports.makeAfterCompile = makeAfterCompile;
/**
* handle compiler option errors after the first compile
*/
function provideCompilerOptionDiagnosticErrorsToWebpack(getCompilerOptionDiagnostics, compilation, instance, configFilePath) {
if (getCompilerOptionDiagnostics) {
const { languageService, loaderOptions, compiler, program } = instance;
const errorsToAdd = utils_1.formatErrors(program === undefined
? languageService.getCompilerOptionsDiagnostics()
: program.getOptionsDiagnostics(), loaderOptions, instance.colors, compiler, { file: configFilePath || 'tsconfig.json' }, compilation.compiler.context);
compilation.errors.push(...errorsToAdd);
}
}
/**
* build map of all modules based on normalized filename
* this is used for quick-lookup when trying to find modules
* based on filepath
*/
function determineModules(compilation) {
return compilation.modules.reduce((modules, module) => {
if (module.resource) {
const modulePath = path.normalize(module.resource);
const existingModules = modules.get(modulePath);
if (existingModules !== undefined) {
if (existingModules.indexOf(module) === -1) {
existingModules.push(module);
}
}
else {
modules.set(modulePath, [module]);
}
}
return modules;
}, new Map());
}
function determineFilesToCheckForErrors(checkAllFilesForErrors, instance) {
const { files, modifiedFiles, filesWithErrors, otherFiles } = instance;
// calculate array of files to check
const filesToCheckForErrors = new Map();
if (checkAllFilesForErrors) {
// check all files on initial run
for (const [filePath, file] of files) {
filesToCheckForErrors.set(filePath, file);
}
for (const [filePath, file] of otherFiles) {
filesToCheckForErrors.set(filePath, file);
}
}
else if (modifiedFiles !== null && modifiedFiles !== undefined) {
// check all modified files, and all dependants
for (const modifiedFileName of modifiedFiles.keys()) {
utils_1.collectAllDependants(instance.reverseDependencyGraph, modifiedFileName).forEach(fileName => {
const fileToCheckForErrors = files.get(fileName) || otherFiles.get(fileName);
filesToCheckForErrors.set(fileName, fileToCheckForErrors);
});
}
}
// re-check files with errors from previous build
if (filesWithErrors !== undefined) {
for (const [fileWithErrorName, fileWithErrors] of filesWithErrors) {
filesToCheckForErrors.set(fileWithErrorName, fileWithErrors);
}
}
return filesToCheckForErrors;
}
function provideErrorsToWebpack(filesToCheckForErrors, filesWithErrors, compilation, modules, instance) {
const { compiler, files, loaderOptions, compilerOptions, otherFiles } = instance;
const filePathRegex = compilerOptions.allowJs === true
? constants.dtsTsTsxJsJsxRegex
: constants.dtsTsTsxRegex;
// I’m pretty sure this will never be undefined here
const program = utils_1.ensureProgram(instance);
for (const filePath of filesToCheckForErrors.keys()) {
if (filePath.match(filePathRegex) === null) {
continue;
}
const sourceFile = program && program.getSourceFile(filePath);
// If the source file is undefined, that probably means it’s actually part of an unbuilt project reference,
// which will have already produced a more useful error than the one we would get by proceeding here.
// If it’s undefined and we’re not using project references at all, I guess carry on so the user will
// get a useful error about which file was unexpectedly missing.
if (utils_1.isUsingProjectReferences(instance) && sourceFile === undefined) {
continue;
}
const errors = [];
if (program && sourceFile) {
errors.push(...program.getSyntacticDiagnostics(sourceFile), ...program
.getSemanticDiagnostics(sourceFile)
// Output file has not been built from source file - this message is redundant with
// program.getOptionsDiagnostics() separately added in instances.ts
.filter(({ code }) => code !== 6305));
}
if (errors.length > 0) {
const fileWithError = files.get(filePath) || otherFiles.get(filePath);
filesWithErrors.set(filePath, fileWithError);
}
// if we have access to a webpack module, use that
const associatedModules = modules.get(filePath);
if (associatedModules !== undefined) {
associatedModules.forEach(module => {
// remove any existing errors
removeTSLoaderErrors(module.errors);
// append errors
const formattedErrors = utils_1.formatErrors(errors, loaderOptions, instance.colors, compiler, { module }, compilation.compiler.context);
module.errors.push(...formattedErrors);
compilation.errors.push(...formattedErrors);
});
}
else {
// otherwise it's a more generic error
const formattedErrors = utils_1.formatErrors(errors, loaderOptions, instance.colors, compiler, { file: filePath }, compilation.compiler.context);
compilation.errors.push(...formattedErrors);
}
}
}
function provideSolutionErrorsToWebpack(compilation, modules, instance) {
if (!instance.solutionBuilderHost ||
!(instance.solutionBuilderHost.diagnostics.global.length ||
instance.solutionBuilderHost.diagnostics.perFile.size)) {
return;
}
const { compiler, loaderOptions, solutionBuilderHost: { diagnostics } } = instance;
for (const [filePath, perFileDiagnostics] of diagnostics.perFile) {
// if we have access to a webpack module, use that
const associatedModules = modules.get(filePath);
if (associatedModules !== undefined) {
associatedModules.forEach(module => {
// remove any existing errors
removeTSLoaderErrors(module.errors);
// append errors
const formattedErrors = utils_1.formatErrors(perFileDiagnostics, loaderOptions, instance.colors, compiler, { module }, compilation.compiler.context);
module.errors.push(...formattedErrors);
compilation.errors.push(...formattedErrors);
});
}
else {
// otherwise it's a more generic error
const formattedErrors = utils_1.formatErrors(perFileDiagnostics, loaderOptions, instance.colors, compiler, { file: filePath }, compilation.compiler.context);
compilation.errors.push(...formattedErrors);
}
}
// Add global solution errors
compilation.errors.push(...utils_1.formatErrors(diagnostics.global, instance.loaderOptions, instance.colors, instance.compiler, { file: 'tsconfig.json' }, compilation.compiler.context));
}
/**
* gather all declaration files from TypeScript and output them to webpack
*/
function provideDeclarationFilesToWebpack(filesToCheckForErrors, instance, compilation) {
for (const filePath of filesToCheckForErrors.keys()) {
if (filePath.match(constants.tsTsxRegex) === null) {
continue;
}
if (!instances_1.isReferencedFile(instance, filePath)) {
addDeclarationFilesAsAsset(instances_1.getEmitOutput(instance, filePath), compilation);
}
}
}
function addDeclarationFilesAsAsset(outputFiles, compilation, skipOutputFile) {
outputFilesToAsset(outputFiles, compilation, outputFile => skipOutputFile && skipOutputFile(outputFile)
? true
: !outputFile.name.match(constants.dtsDtsxOrDtsDtsxMapRegex));
}
function outputFileToAsset(outputFile, compilation) {
const assetPath = path.relative(compilation.compiler.outputPath, outputFile.name);
compilation.assets[assetPath] = {
source: () => outputFile.text,
size: () => outputFile.text.length
};
}
function outputFilesToAsset(outputFiles, compilation, skipOutputFile) {
for (const outputFile of outputFiles) {
if (!skipOutputFile || !skipOutputFile(outputFile)) {
outputFileToAsset(outputFile, compilation);
}
}
}
/**
* gather all .tsbuildinfo for the project
*/
function provideTsBuildInfoFilesToWebpack(instance, compilation) {
if (instance.watchHost) {
// Ensure emit is complete
instances_1.getEmitFromWatchHost(instance);
if (instance.watchHost.tsbuildinfo) {
outputFileToAsset(instance.watchHost.tsbuildinfo, compilation);
}
instance.watchHost.outputFiles.clear();
instance.watchHost.tsbuildinfo = undefined;
}
}
/**
* gather all solution builder assets
*/
function provideAssetsFromSolutionBuilderHost(instance, compilation) {
if (instance.solutionBuilderHost) {
// written files
outputFilesToAsset(instance.solutionBuilderHost.writtenFiles, compilation);
instance.solutionBuilderHost.writtenFiles.length = 0;
}
}
/**
* handle all other errors. The basic approach here to get accurate error
* reporting is to start with a "blank slate" each compilation and gather
* all errors from all files. Since webpack tracks errors in a module from
* compilation-to-compilation, and since not every module always runs through
* the loader, we need to detect and remove any pre-existing errors.
*/
function removeTSLoaderErrors(errors) {
let index = -1;
let length = errors.length;
while (++index < length) {
if (errors[index].loaderSource === 'ts-loader') {
errors.splice(index--, 1);
length--;
}
}
}
//# sourceMappingURL=after-compile.js.map