ts-loader
Version:
TypeScript loader for webpack
349 lines • 15.9 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.makeAfterCompile = makeAfterCompile;
const path = __importStar(require("path"));
const webpack = __importStar(require("webpack"));
const constants = __importStar(require("./constants"));
const instances_1 = require("./instances");
const utils_1 = require("./utils");
const loaderUtils_1 = require("./loaderUtils");
/**
* This returns a function that has options to add assets and also to provide errors to webpack
* In webpack 4 we can do both during the afterCompile hook
* In webpack 5 only errors should be provided during aftercompile. Assets should be
* emitted during the afterProcessAssets hook
*/
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;
}
removeCompilationTSLoaderErrors(compilation, instance.loaderOptions);
provideCompilerOptionDiagnosticErrorsToWebpack(getCompilerOptionDiagnostics, compilation, instance, configFilePath);
getCompilerOptionDiagnostics = false;
const modules = determineModules(compilation, instance);
const filesToCheckForErrors = determineFilesToCheckForErrors(checkAllFilesForErrors, instance);
checkAllFilesForErrors = false;
const filesWithErrors = new Map();
provideErrorsToWebpack(filesToCheckForErrors, filesWithErrors, compilation, modules, instance);
provideSolutionErrorsToWebpack(compilation, modules, instance);
provideDeclarationFilesToWebpack(filesToCheckForErrors, instance, compilation);
provideTsBuildInfoFilesToWebpack(instance, compilation);
provideAssetsFromSolutionBuilderHost(instance, compilation);
instance.filesWithErrors = filesWithErrors;
instance.modifiedFiles = undefined;
instance.projectsMissingSourceMaps = new Set();
callback();
};
}
/**
* handle compiler option errors after the first compile
*/
function provideCompilerOptionDiagnosticErrorsToWebpack(getCompilerOptionDiagnostics, compilation, instance, configFilePath) {
if (getCompilerOptionDiagnostics) {
const { languageService, loaderOptions, compiler, program } = instance;
const errors = (0, utils_1.formatErrors)(program === undefined
? languageService.getCompilerOptionsDiagnostics()
: program.getOptionsDiagnostics(), loaderOptions, instance.colors, compiler, { file: configFilePath || 'tsconfig.json' }, compilation.compiler.context);
compilation.errors.push(...errors);
}
}
/**
* 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, { filePathKeyMapper }) {
const modules = new Map();
compilation.modules.forEach(module => {
const resource = module.resource;
// || (module as webpack.NormalModule).resourceResolveData?.path; // not sure this is needed
if (resource) {
const modulePath = filePathKeyMapper(resource);
const existingModules = modules.get(modulePath);
if (existingModules !== undefined) {
if (!existingModules.includes(module)) {
existingModules.push(module);
}
}
else {
modules.set(modulePath, [module]);
}
}
});
return modules;
}
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) {
addFileToCheckForErrors(filePath, file);
}
for (const [filePath, file] of otherFiles) {
addFileToCheckForErrors(filePath, file);
}
}
else if (modifiedFiles !== null &&
modifiedFiles !== undefined &&
modifiedFiles.size) {
const reverseDependencyGraph = (0, utils_1.populateReverseDependencyGraph)(instance);
// check all modified files, and all dependants
for (const modifiedFileName of modifiedFiles.keys()) {
for (const fileName of (0, utils_1.collectAllDependants)(reverseDependencyGraph, modifiedFileName).keys()) {
const fileToCheckForErrors = files.get(fileName) || otherFiles.get(fileName);
if (fileToCheckForErrors) { //file may have been removed
addFileToCheckForErrors(fileName, fileToCheckForErrors);
}
}
}
}
// re-check files with errors from previous build
if (filesWithErrors !== undefined) {
for (const [fileWithErrorName, fileWithErrors] of filesWithErrors) {
addFileToCheckForErrors(fileWithErrorName, fileWithErrors);
}
}
return filesToCheckForErrors;
function addFileToCheckForErrors(filePath, file) {
if (file && !(0, utils_1.isReferencedFile)(instance, filePath)) {
filesToCheckForErrors.set(filePath, file);
}
}
}
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 = (0, utils_1.ensureProgram)(instance);
for (const [filePath, { fileName }] of filesToCheckForErrors.entries()) {
if (fileName.match(filePathRegex) === null) {
continue;
}
const sourceFile = program && program.getSourceFile(fileName);
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(instance.filePathKeyMapper(fileName));
if (associatedModules !== undefined) {
associatedModules.forEach(module => {
removeModuleTSLoaderError(module, loaderOptions);
// append errors
const formattedErrors = (0, utils_1.formatErrors)(errors, loaderOptions, instance.colors, compiler, { module }, compilation.compiler.context);
if (!moduleHasWebpackErrors(module)) {
formattedErrors.forEach(error => (0, loaderUtils_1.addErrorToModule)(module, error));
}
compilation.errors.push(...formattedErrors);
});
}
else {
// otherwise it's a more generic error
const formattedErrors = (0, utils_1.formatErrors)(errors, loaderOptions, instance.colors, compiler, { file: fileName }, 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 => {
removeModuleTSLoaderError(module, loaderOptions);
// append errors
const formattedErrors = (0, utils_1.formatErrors)(perFileDiagnostics, loaderOptions, instance.colors, compiler, { module }, compilation.compiler.context);
if (!moduleHasWebpackErrors(module)) {
formattedErrors.forEach(error => (0, loaderUtils_1.addErrorToModule)(module, error));
}
compilation.errors.push(...formattedErrors);
});
}
else {
// otherwise it's a more generic error
const formattedErrors = (0, utils_1.formatErrors)(perFileDiagnostics, loaderOptions, instance.colors, compiler, { file: path.resolve(perFileDiagnostics[0].file.fileName) }, compilation.compiler.context);
compilation.errors.push(...formattedErrors);
}
}
// Add global solution errors
compilation.errors.push(...(0, 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.
* JavaScript declaration files are included if `allowJs` is set.
*/
function provideDeclarationFilesToWebpack(filesToCheckForErrors, instance, compilation) {
const filePathRegex = instance.compilerOptions.allowJs === true
? constants.dtsTsTsxJsJsxRegex
: constants.dtsTsTsxRegex;
for (const { fileName } of filesToCheckForErrors.values()) {
if (fileName.match(filePathRegex) === null) {
continue;
}
addDeclarationFilesAsAsset((0, instances_1.getEmitOutput)(instance, fileName), 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)
// According to @alexander-akait (and @sokra) we should always '/' https://github.com/TypeStrong/ts-loader/pull/1251#issuecomment-799606985
.replace(/\\/g, '/');
if (loaderUtils_1.isWebpack5) {
compilation.emitAsset(assetPath, new webpack.sources.RawSource(outputFile.text));
}
else {
compilation.assets[assetPath] = {
source: () => outputFile.text,
size: () => Buffer.byteLength(outputFile.text, 'utf8'),
};
}
}
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
(0, 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 removeCompilationTSLoaderErrors(compilation, loaderOptions) {
// Filter out ts-loader's own errors so they can be re-added fresh each
// compilation.
compilation.errors = compilation.errors.filter(error => !isTSLoaderModuleError(error, loaderOptions));
}
function removeModuleTSLoaderError(module, loaderOptions) {
if (loaderUtils_1.isWebpack5) {
const warnings = Array.from(module.getWarnings() || []);
const errors = Array.from(module.getErrors() || []);
module.clearWarningsAndErrors();
warnings.forEach(warning => {
module.addWarning(warning);
});
errors
.filter((error) => !isTSLoaderModuleError(error, loaderOptions))
.forEach((error) => {
module.addError(error);
});
}
else {
const webpackModule = module;
const warnings = (webpackModule.warnings || []).slice();
const errors = (webpackModule.errors || []).slice();
webpackModule.warnings = [];
webpackModule.errors = [];
warnings.forEach((warning) => {
webpackModule.warnings.push(warning);
});
errors
.filter((error) => !isTSLoaderModuleError(error, loaderOptions))
.forEach((error) => {
webpackModule.errors.push(error);
});
}
}
function isTSLoaderModuleError(error, loaderOptions) {
return (error === null || error === void 0 ? void 0 : error.details) === (0, utils_1.tsLoaderSource)(loaderOptions);
}
function moduleHasWebpackErrors(module) {
return module.getNumberOfErrors
? module.getNumberOfErrors() > 0
: ((module.errors || [])
.length > 0);
}
//# sourceMappingURL=after-compile.js.map