From 33c866f02d8b7a00f96fca3baab0c630fa3b1fbc Mon Sep 17 00:00:00 2001 From: Luciano Martorella Date: Thu, 12 Mar 2026 15:39:32 +0100 Subject: [PATCH 1/3] - Collected .d.ts content --- packages/ngtools/webpack/src/ivy/plugin.ts | 5 ++++- packages/ngtools/webpack/src/ivy/symbol.ts | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/ngtools/webpack/src/ivy/plugin.ts b/packages/ngtools/webpack/src/ivy/plugin.ts index f46b1360f26a..dfb989b865fa 100644 --- a/packages/ngtools/webpack/src/ivy/plugin.ts +++ b/packages/ngtools/webpack/src/ivy/plugin.ts @@ -681,6 +681,7 @@ export class AngularWebpackPlugin { let content: string | undefined; let map: string | undefined; + let declaration: string | undefined; program.emit( sourceFile, (filename, data) => { @@ -688,6 +689,8 @@ export class AngularWebpackPlugin { map = data; } else if (filename.endsWith('.js')) { content = data; + } else if (filename.endsWith('.d.ts')) { + declaration = data; } }, undefined, @@ -705,7 +708,7 @@ export class AngularWebpackPlugin { ...getExtraDependencies(sourceFile), ].map(externalizePath); - return { content, map, dependencies, hash }; + return { content, map, declaration, dependencies, hash }; }; } diff --git a/packages/ngtools/webpack/src/ivy/symbol.ts b/packages/ngtools/webpack/src/ivy/symbol.ts index 6d884a256144..bfccd91c4c29 100644 --- a/packages/ngtools/webpack/src/ivy/symbol.ts +++ b/packages/ngtools/webpack/src/ivy/symbol.ts @@ -11,6 +11,7 @@ export const AngularPluginSymbol: unique symbol = Symbol.for('@ngtools/webpack[a export interface EmitFileResult { content?: string; map?: string; + declaration?: string; dependencies: readonly string[]; hash?: Uint8Array; } From e885819d728ff04ce7948c4efb46d000c5a26160 Mon Sep 17 00:00:00 2001 From: Luciano Martorella Date: Thu, 12 Mar 2026 15:43:57 +0100 Subject: [PATCH 2/3] - Possible fix to write .d.ts near the original source file --- packages/ngtools/webpack/src/ivy/loader.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/ngtools/webpack/src/ivy/loader.ts b/packages/ngtools/webpack/src/ivy/loader.ts index 79a511fcee06..959b8b248d3c 100644 --- a/packages/ngtools/webpack/src/ivy/loader.ts +++ b/packages/ngtools/webpack/src/ivy/loader.ts @@ -6,6 +6,7 @@ * found in the LICENSE file at https://angular.dev/license */ +import * as fs from 'node:fs'; import * as path from 'node:path'; import type { LoaderContext } from 'webpack'; import { AngularPluginSymbol, FileEmitterCollection } from './symbol'; @@ -72,6 +73,10 @@ export function angularWebpackLoader( ); } + // Write the declaration file in the target dir + if (result.declaration) { + fs.writeFileSync(this.resourcePath.replace('.ts', '.d.ts'), result.declaration); + } callback(undefined, resultContent, resultMap); }) .catch((err) => { From ce5732ba0c564c7a368bb7465916f0e4054af150 Mon Sep 17 00:00:00 2001 From: Luciano Martorella Date: Thu, 12 Mar 2026 17:20:11 +0100 Subject: [PATCH 3/3] - Passing tsconfig info to honor declaration Dir --- packages/ngtools/webpack/src/ivy/loader.ts | 12 ++++++++++-- packages/ngtools/webpack/src/ivy/plugin.ts | 4 ++++ packages/ngtools/webpack/src/ivy/symbol.ts | 4 ++++ 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/packages/ngtools/webpack/src/ivy/loader.ts b/packages/ngtools/webpack/src/ivy/loader.ts index 959b8b248d3c..cb4222e2c5ff 100644 --- a/packages/ngtools/webpack/src/ivy/loader.ts +++ b/packages/ngtools/webpack/src/ivy/loader.ts @@ -74,8 +74,16 @@ export function angularWebpackLoader( } // Write the declaration file in the target dir - if (result.declaration) { - fs.writeFileSync(this.resourcePath.replace('.ts', '.d.ts'), result.declaration); + if (result.declaration && fileEmitter.compilerOptions.declaration) { + let target = this.resourcePath.replace('.ts', '.d.ts'); + if (fileEmitter.compilerOptions.declarationDir) { + if (!fileEmitter.compilerOptions.baseUrl) { + throw new Error('When declarationDir is specified, baseUrl is required as well'); + } + const relDir = path.relative(fileEmitter.compilerOptions.baseUrl, target); + target = path.join(fileEmitter.compilerOptions.declarationDir, relDir); + } + fs.writeFileSync(target, result.declaration); } callback(undefined, resultContent, resultMap); }) diff --git a/packages/ngtools/webpack/src/ivy/plugin.ts b/packages/ngtools/webpack/src/ivy/plugin.ts index dfb989b865fa..603acdbb6c53 100644 --- a/packages/ngtools/webpack/src/ivy/plugin.ts +++ b/packages/ngtools/webpack/src/ivy/plugin.ts @@ -85,6 +85,7 @@ export class AngularWebpackPlugin { private readonly requiredFilesToEmit = new Set(); private readonly requiredFilesToEmitCache = new Map(); private readonly fileEmitHistory = new Map(); + private compilerOptions!: CompilerOptions; constructor(options: Partial = {}) { this.pluginOptions = { @@ -186,6 +187,7 @@ export class AngularWebpackPlugin { // Setup and read TypeScript and Angular compiler configuration const { compilerOptions, rootNames, errors } = this.loadConfiguration(); + this.compilerOptions = compilerOptions; // Create diagnostics reporter and report configuration file errors const diagnosticsReporter = createDiagnosticsReporter(compilation, (diagnostic) => @@ -325,6 +327,8 @@ export class AngularWebpackPlugin { compilation.compiler.webpack.NormalModule.getCompilationHooks(compilation).loader.tap( PLUGIN_NAME, (context) => { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + fileEmitters!.compilerOptions = this.compilerOptions; const loaderContext = context as typeof context & { [AngularPluginSymbol]?: FileEmitterCollection; }; diff --git a/packages/ngtools/webpack/src/ivy/symbol.ts b/packages/ngtools/webpack/src/ivy/symbol.ts index bfccd91c4c29..af784055d1ee 100644 --- a/packages/ngtools/webpack/src/ivy/symbol.ts +++ b/packages/ngtools/webpack/src/ivy/symbol.ts @@ -6,6 +6,8 @@ * found in the LICENSE file at https://angular.dev/license */ +import { CompilerOptions } from 'typescript'; + export const AngularPluginSymbol: unique symbol = Symbol.for('@ngtools/webpack[angular-compiler]'); export interface EmitFileResult { @@ -37,6 +39,8 @@ export class FileEmitterRegistration { export class FileEmitterCollection { #registrations: FileEmitterRegistration[] = []; + public compilerOptions!: CompilerOptions; + register(): FileEmitterRegistration { const registration = new FileEmitterRegistration(); this.#registrations.push(registration);