Skip to content

Commit ca6e9fe

Browse files
clydinalan-agius4
authored andcommitted
refactor(@ngtools/webpack): use Webpack loader context hook to access file emitters
The Angular Webpack loader will now access the Angular Webpack Plugin's file emitters via the loader context directly. This is instead of using the not fully public Webpack compilation property on the loader context. This change also removes the need to directly alter the Webpack compilation object which previously added an additional property.
1 parent f2f15c0 commit ca6e9fe

File tree

2 files changed

+29
-22
lines changed

2 files changed

+29
-22
lines changed

packages/ngtools/webpack/src/ivy/loader.ts

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,20 +10,19 @@ import * as path from 'path';
1010
import { AngularPluginSymbol, FileEmitterCollection } from './symbol';
1111

1212
export function angularWebpackLoader(
13-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
14-
this: any,
13+
this: import('webpack').LoaderContext<unknown> & {
14+
[AngularPluginSymbol]?: FileEmitterCollection;
15+
},
1516
content: string,
16-
// Source map types are broken in the webpack type definitions
17-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
18-
map: any,
17+
map: string,
1918
) {
2019
const callback = this.async();
2120
if (!callback) {
2221
throw new Error('Invalid webpack version');
2322
}
2423

25-
const fileEmitter = this._compilation[AngularPluginSymbol] as FileEmitterCollection;
26-
if (typeof fileEmitter !== 'object') {
24+
const fileEmitter = this[AngularPluginSymbol];
25+
if (!fileEmitter || typeof fileEmitter !== 'object') {
2726
if (this.resourcePath.endsWith('.js')) {
2827
// Passthrough for JS files when no plugin is used
2928
this.callback(undefined, content, map);

packages/ngtools/webpack/src/ivy/plugin.ts

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -51,11 +51,6 @@ export interface AngularWebpackPluginOptions {
5151
inlineStyleFileExtension?: string;
5252
}
5353

54-
// Add support for missing properties in Webpack types as well as the loader's file emitter
55-
interface WebpackCompilation extends Compilation {
56-
[AngularPluginSymbol]: FileEmitterCollection;
57-
}
58-
5954
function initializeNgccProcessor(
6055
compiler: Compiler,
6156
tsconfig: string,
@@ -89,6 +84,7 @@ function hashContent(content: string): Uint8Array {
8984
}
9085

9186
const PLUGIN_NAME = 'angular-compiler';
87+
const compilationFileEmitters = new WeakMap<Compilation, FileEmitterCollection>();
9288

9389
export class AngularWebpackPlugin {
9490
private readonly pluginOptions: AngularWebpackPluginOptions;
@@ -153,14 +149,9 @@ export class AngularWebpackPlugin {
153149
let ngccProcessor: NgccProcessor | undefined;
154150
let resourceLoader: WebpackResourceLoader | undefined;
155151
let previousUnused: Set<string> | undefined;
156-
compiler.hooks.thisCompilation.tap(PLUGIN_NAME, (thisCompilation) => {
157-
const compilation = thisCompilation as WebpackCompilation;
158-
152+
compiler.hooks.thisCompilation.tap(PLUGIN_NAME, (compilation) => {
159153
// Register plugin to ensure deterministic emit order in multi-plugin usage
160-
if (!compilation[AngularPluginSymbol]) {
161-
compilation[AngularPluginSymbol] = new FileEmitterCollection();
162-
}
163-
const emitRegistration = compilation[AngularPluginSymbol].register();
154+
const emitRegistration = this.registerWithCompilation(compilation);
164155

165156
this.watchMode = compiler.watchMode;
166157

@@ -184,7 +175,7 @@ export class AngularWebpackPlugin {
184175
}
185176

186177
// Setup and read TypeScript and Angular compiler configuration
187-
const { compilerOptions, rootNames, errors } = this.loadConfiguration(compilation);
178+
const { compilerOptions, rootNames, errors } = this.loadConfiguration();
188179

189180
// Create diagnostics reporter and report configuration file errors
190181
const diagnosticsReporter = createDiagnosticsReporter(compilation);
@@ -316,6 +307,23 @@ export class AngularWebpackPlugin {
316307
});
317308
}
318309

310+
private registerWithCompilation(compilation: Compilation) {
311+
let fileEmitters = compilationFileEmitters.get(compilation);
312+
if (!fileEmitters) {
313+
fileEmitters = new FileEmitterCollection();
314+
compilationFileEmitters.set(compilation, fileEmitters);
315+
compilation.compiler.webpack.NormalModule.getCompilationHooks(compilation).loader.tap(
316+
PLUGIN_NAME,
317+
(loaderContext: { [AngularPluginSymbol]?: FileEmitterCollection }) => {
318+
loaderContext[AngularPluginSymbol] = fileEmitters;
319+
},
320+
);
321+
}
322+
const emitRegistration = fileEmitters.register();
323+
324+
return emitRegistration;
325+
}
326+
319327
private markResourceUsed(normalizedResourcePath: string, currentUnused: Set<string>): void {
320328
if (!currentUnused.has(normalizedResourcePath)) {
321329
return;
@@ -333,7 +341,7 @@ export class AngularWebpackPlugin {
333341

334342
private async rebuildRequiredFiles(
335343
modules: Iterable<Module>,
336-
compilation: WebpackCompilation,
344+
compilation: Compilation,
337345
fileEmitter: FileEmitter,
338346
): Promise<void> {
339347
if (this.requiredFilesToEmit.size === 0) {
@@ -379,7 +387,7 @@ export class AngularWebpackPlugin {
379387
this.requiredFilesToEmitCache.clear();
380388
}
381389

382-
private loadConfiguration(compilation: WebpackCompilation) {
390+
private loadConfiguration() {
383391
const {
384392
options: compilerOptions,
385393
rootNames,

0 commit comments

Comments
 (0)