diff --git a/packages/ngtools/webpack/src/ivy/loader.ts b/packages/ngtools/webpack/src/ivy/loader.ts index 79a511fcee06..cb4222e2c5ff 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,18 @@ export function angularWebpackLoader( ); } + // Write the declaration file in the target dir + 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); }) .catch((err) => { diff --git a/packages/ngtools/webpack/src/ivy/plugin.ts b/packages/ngtools/webpack/src/ivy/plugin.ts index f46b1360f26a..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; }; @@ -681,6 +685,7 @@ export class AngularWebpackPlugin { let content: string | undefined; let map: string | undefined; + let declaration: string | undefined; program.emit( sourceFile, (filename, data) => { @@ -688,6 +693,8 @@ export class AngularWebpackPlugin { map = data; } else if (filename.endsWith('.js')) { content = data; + } else if (filename.endsWith('.d.ts')) { + declaration = data; } }, undefined, @@ -705,7 +712,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..af784055d1ee 100644 --- a/packages/ngtools/webpack/src/ivy/symbol.ts +++ b/packages/ngtools/webpack/src/ivy/symbol.ts @@ -6,11 +6,14 @@ * 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 { content?: string; map?: string; + declaration?: string; dependencies: readonly string[]; hash?: Uint8Array; } @@ -36,6 +39,8 @@ export class FileEmitterRegistration { export class FileEmitterCollection { #registrations: FileEmitterRegistration[] = []; + public compilerOptions!: CompilerOptions; + register(): FileEmitterRegistration { const registration = new FileEmitterRegistration(); this.#registrations.push(registration);