From ebb3472d2472ee9479c37da16610ce8ff989fecc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Goetz?= Date: Thu, 21 May 2026 13:57:16 +0200 Subject: [PATCH] Draft of Rspack compatiblity --- src/after-compile.ts | 14 ++++++++++---- src/config.ts | 4 +++- src/index.ts | 17 ++++++++++++----- src/instances.ts | 29 ++++++++++++++++++++--------- src/servicesHost.ts | 3 ++- src/utils.ts | 11 +++++++---- 6 files changed, 54 insertions(+), 24 deletions(-) diff --git a/src/after-compile.ts b/src/after-compile.ts index 62b58d36a..c1504ceaf 100644 --- a/src/after-compile.ts +++ b/src/after-compile.ts @@ -1,6 +1,6 @@ import * as path from 'path'; import type * as ts from 'typescript'; -import * as webpack from 'webpack'; +import type * as webpack from 'webpack'; import * as constants from './constants'; import { getEmitFromWatchHost, getEmitOutput } from './instances'; @@ -105,6 +105,7 @@ function provideCompilerOptionDiagnosticErrorsToWebpack( loaderOptions, instance.colors, compiler, + compilation.compiler.webpack.WebpackError, { file: configFilePath || 'tsconfig.json' }, compilation.compiler.context ); @@ -125,7 +126,7 @@ function determineModules( const modules: Map = new Map(); compilation.modules.forEach(module => { - if (module instanceof webpack.NormalModule && module.resource) { + if (module instanceof compilation.compiler.webpack.NormalModule && module.resource) { const modulePath = filePathKeyMapper(module.resource); const existingModules = modules.get(modulePath); if (existingModules !== undefined) { @@ -248,6 +249,7 @@ function provideErrorsToWebpack( loaderOptions, instance.colors, compiler, + compilation.compiler.webpack.WebpackError, { module }, compilation.compiler.context ); @@ -269,6 +271,7 @@ function provideErrorsToWebpack( loaderOptions, instance.colors, compiler, + compilation.compiler.webpack.WebpackError, { file: fileName }, compilation.compiler.context ); @@ -312,6 +315,7 @@ function provideSolutionErrorsToWebpack( loaderOptions, instance.colors, compiler, + compilation.compiler.webpack.WebpackError, { module }, compilation.compiler.context ); @@ -333,6 +337,7 @@ function provideSolutionErrorsToWebpack( loaderOptions, instance.colors, compiler, + compilation.compiler.webpack.WebpackError, { file: path.resolve(perFileDiagnostics[0].file!.fileName) }, compilation.compiler.context ); @@ -348,6 +353,7 @@ function provideSolutionErrorsToWebpack( instance.loaderOptions, instance.colors, instance.compiler, + compilation.compiler.webpack.WebpackError, { file: 'tsconfig.json' }, compilation.compiler.context ) @@ -401,7 +407,7 @@ function outputFileToAsset( // As suggested by @JonWallsten here: https://github.com/TypeStrong/ts-loader/pull/1251#issuecomment-800032753 compilation.emitAsset( assetPath, - new webpack.sources.RawSource(outputFile.text) + new compilation.compiler.webpack.sources.RawSource(outputFile.text) ); } @@ -462,7 +468,7 @@ function removeCompilationTSLoaderErrors( loaderOptions: LoaderOptions ) { compilation.errors = compilation.errors.filter( - error => error instanceof webpack.WebpackError && error.details !== tsLoaderSource(loaderOptions) + error => error instanceof compilation.compiler.webpack.WebpackError && error.details !== tsLoaderSource(loaderOptions) ); } diff --git a/src/config.ts b/src/config.ts index 32d5e3b3c..02e5de688 100644 --- a/src/config.ts +++ b/src/config.ts @@ -20,7 +20,8 @@ export function getConfigFile( loaderOptions: LoaderOptions, compilerCompatible: boolean, log: logger.Logger, - compilerDetailsLogMessage: string + compilerDetailsLogMessage: string, + WebpackError: typeof webpack.WebpackError ) { const configFilePath = findConfigFile( compiler, @@ -45,6 +46,7 @@ export function getConfigFile( loaderOptions, colors, compiler, + WebpackError, { file: configFilePath }, loader.context )[0]; diff --git a/src/index.ts b/src/index.ts index 31ee632ec..f929855a3 100644 --- a/src/index.ts +++ b/src/index.ts @@ -41,7 +41,11 @@ function loader( this.cacheable && this.cacheable(); const callback = this.async(); const options = getLoaderOptions(this); - const instanceOrError = getTypeScriptInstance(options, this); + + // TODO :: improve this construct + const WebpackError = this._compiler?.webpack.WebpackError!; + + const instanceOrError = getTypeScriptInstance(options, this, WebpackError); if (instanceOrError.error !== undefined) { callback(new Error(instanceOrError.error.message)); @@ -49,7 +53,7 @@ function loader( } const instance = instanceOrError.instance!; buildSolutionReferences(instance, this); - successLoader(this, contents, callback, instance, inputSourceMap); + successLoader(this, contents, callback, instance, WebpackError, inputSourceMap); } function successLoader( @@ -57,10 +61,11 @@ function successLoader( contents: string, callback: ReturnType['async']>, instance: TSInstance, + WebpackError: typeof webpack.WebpackError, inputSourceMap?: Record ) { initializeInstance(loaderContext, instance); - reportTranspileErrors(instance, loaderContext); + reportTranspileErrors(instance, loaderContext, WebpackError); const rawFilePath = path.normalize(loaderContext.resourcePath); const filePath = @@ -82,7 +87,7 @@ function successLoader( instance ); const { outputText, sourceMapText } = instance.loaderOptions.transpileOnly - ? getTranspilationEmit(filePath, contents, instance, loaderContext) + ? getTranspilationEmit(filePath, contents, instance, loaderContext, WebpackError) : getEmit(rawFilePath, filePath, instance, loaderContext); // the following function is async, which means it will immediately return and run in the "background" @@ -629,7 +634,8 @@ function getTranspilationEmit( fileName: string, contents: string, instance: TSInstance, - loaderContext: webpack.LoaderContext + loaderContext: webpack.LoaderContext, + WebpackError: typeof webpack.WebpackError, ) { if (isReferencedFile(instance, fileName)) { const outputFiles = @@ -662,6 +668,7 @@ function getTranspilationEmit( instance.loaderOptions, instance.colors, instance.compiler, + WebpackError, { module }, loaderContext.context ); diff --git a/src/instances.ts b/src/instances.ts index 7e9202f3b..e9e2355f1 100644 --- a/src/instances.ts +++ b/src/instances.ts @@ -2,7 +2,7 @@ import * as chalk from 'chalk'; import * as fs from 'fs'; import * as path from 'path'; import type * as typescript from 'typescript'; -import * as webpack from 'webpack'; +import type * as webpack from 'webpack'; import { makeAfterCompile } from './after-compile'; import { getCompiler, getCompilerOptions } from './compilerSetup'; @@ -44,7 +44,8 @@ const instancesBySolutionBuilderConfigs = new Map(); */ export function getTypeScriptInstance( loaderOptions: LoaderOptions, - loader: webpack.LoaderContext + loader: webpack.LoaderContext, + WebpackError: typeof webpack.WebpackError ): { instance?: TSInstance; error?: webpack.WebpackError } { const existing = getTSInstanceFromCache( loader._compiler!, @@ -65,7 +66,7 @@ export function getTypeScriptInstance( if (compiler.errorMessage !== undefined) { return { - error: makeError(loaderOptions, colors.red(compiler.errorMessage), ''), + error: makeError(WebpackError, loaderOptions, colors.red(compiler.errorMessage), ''), }; } @@ -76,7 +77,8 @@ export function getTypeScriptInstance( colors, compiler.compiler!, compiler.compilerCompatible!, - compiler.compilerDetailsLogMessage! + compiler.compilerDetailsLogMessage!, + WebpackError ); } @@ -123,7 +125,8 @@ function successfulTypeScriptInstance( colors: chalk.Chalk, compiler: typeof typescript, compilerCompatible: boolean, - compilerDetailsLogMessage: string + compilerDetailsLogMessage: string, + WebpackError: typeof webpack.WebpackError, ) { const configFileAndPath = getConfigFile( compiler, @@ -132,13 +135,15 @@ function successfulTypeScriptInstance( loaderOptions, compilerCompatible, log, - compilerDetailsLogMessage! + compilerDetailsLogMessage!, + WebpackError ); if (configFileAndPath.configFileError !== undefined) { const { message, file } = configFileAndPath.configFileError; return { error: makeError( + WebpackError, loaderOptions, colors.red('error while reading tsconfig.json:' + EOL + message), file ?? '' @@ -179,6 +184,7 @@ function successfulTypeScriptInstance( loaderOptions, colors, compiler, + WebpackError, { file: configFilePath }, loader.context ); @@ -187,6 +193,7 @@ function successfulTypeScriptInstance( return { error: makeError( + WebpackError, loaderOptions, colors.red('error while parsing tsconfig.json'), configFilePath || '' @@ -264,6 +271,7 @@ function successfulTypeScriptInstance( } catch (exc) { return { error: makeError( + WebpackError, loaderOptions, colors.red( `A file specified in tsconfig.json could not be found: ${normalizedFilePath!}` @@ -324,7 +332,7 @@ function addAssetHooks( compilation.hooks.processAssets.tap( { name: 'ts-loader', - stage: webpack.Compilation.PROCESS_ASSETS_STAGE_ADDITIONAL, + stage: compilation.compiler.webpack.Compilation.PROCESS_ASSETS_STAGE_ADDITIONAL, }, () => { cachedMakeAfterCompile(compilation, () => { @@ -478,7 +486,8 @@ function getScriptRegexp(instance: TSInstance) { export function reportTranspileErrors( instance: TSInstance, - loader: webpack.LoaderContext + loader: webpack.LoaderContext, + WebpackError: typeof webpack.WebpackError, ) { if (!instance.reportTranspileErrors) { return; @@ -489,7 +498,8 @@ export function reportTranspileErrors( if (!instance.loaderOptions.happyPackMode) { const solutionErrors: webpack.WebpackError[] = getSolutionErrors( instance, - loader.context + loader.context, + WebpackError ); const diagnostics = instance.program!.getOptionsDiagnostics(); const errors = formatErrors( @@ -497,6 +507,7 @@ export function reportTranspileErrors( instance.loaderOptions, instance.colors, instance.compiler, + WebpackError, { file: instance.configFilePath || 'tsconfig.json' }, loader.context ); diff --git a/src/servicesHost.ts b/src/servicesHost.ts index ff1997c1f..eef2f319c 100644 --- a/src/servicesHost.ts +++ b/src/servicesHost.ts @@ -1163,7 +1163,7 @@ export function makeSolutionBuilderHost( } } -export function getSolutionErrors(instance: TSInstance, context: string) { +export function getSolutionErrors(instance: TSInstance, context: string, WebpackError: typeof webpack.WebpackError) { const solutionErrors: webpack.WebpackError[] = []; if ( instance.solutionBuilderHost && @@ -1177,6 +1177,7 @@ export function getSolutionErrors(instance: TSInstance, context: string) { instance.loaderOptions, instance.colors, instance.compiler, + WebpackError, { file: filePath ? undefined : 'tsconfig.json' }, context ) diff --git a/src/utils.ts b/src/utils.ts index 37de60bff..fe82d0966 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -2,7 +2,7 @@ import type { Chalk } from 'chalk'; import * as fs from 'fs'; import micromatch from 'micromatch'; import * as path from 'path'; -import * as webpack from 'webpack'; +import type * as webpack from 'webpack'; import type * as typescript from 'typescript'; import constants = require('./constants'); @@ -45,6 +45,7 @@ export function formatErrors( loaderOptions: LoaderOptions, colors: Chalk, compiler: typeof typescript, + WebpackError: typeof webpack.WebpackError, merge: { file?: string; module?: webpack.Module }, context: string ): webpack.WebpackError[] { @@ -100,7 +101,8 @@ export function formatErrors( : loaderOptions.errorFormatter(errorInfo, colors); const error = makeError( - loaderOptions, + WebpackError, + loaderOptions, message, merge.file === undefined ? errorInfo.file : merge.file, start, @@ -145,13 +147,14 @@ export function fsReadFile( } export function makeError( + WebpackError: typeof webpack.WebpackError, loaderOptions: LoaderOptions, message: string, file: string, location?: FileLocation, - endLocation?: FileLocation + endLocation?: FileLocation, ): webpack.WebpackError { - const error = new webpack.WebpackError(message); + const error = new WebpackError(message); error.file = file; error.loc = location === undefined