From 57d50a8bbc5c79feac935200fef01a4e14a224eb Mon Sep 17 00:00:00 2001 From: Simon Siefke Date: Wed, 29 Jul 2026 22:21:38 +0000 Subject: [PATCH] fix(core): release Rspack runtime module references (AI-assisted) --- .../src/webpack/plugins/ChunkAssetPlugin.ts | 21 +++++- .../__tests__/ChunkAssetPlugin.test.ts | 68 +++++++++++++++++++ 2 files changed, 88 insertions(+), 1 deletion(-) create mode 100644 packages/docusaurus/src/webpack/plugins/__tests__/ChunkAssetPlugin.test.ts diff --git a/packages/docusaurus/src/webpack/plugins/ChunkAssetPlugin.ts b/packages/docusaurus/src/webpack/plugins/ChunkAssetPlugin.ts index 86c004913dfb..835f3a6636b1 100644 --- a/packages/docusaurus/src/webpack/plugins/ChunkAssetPlugin.ts +++ b/packages/docusaurus/src/webpack/plugins/ChunkAssetPlugin.ts @@ -56,14 +56,27 @@ ${DocusaurusGetChunkAssetFn} = function(chunkId) { chunkId = ${JSON.stringify( */ export default class ChunkAssetPlugin { apply(compiler: Compiler): void { + const runtimeModules = new Set(); + compiler.hooks.thisCompilation.tap(PluginName, (compilation) => { + runtimeModules.clear(); compilation.hooks.additionalTreeRuntimeRequirements.tap( PluginName, (chunk) => { - compilation.addRuntimeModule(chunk, new ChunkAssetRuntimeModule()); + const runtimeModule = new ChunkAssetRuntimeModule(); + runtimeModules.add(runtimeModule); + compilation.addRuntimeModule(chunk, runtimeModule); }, ); }); + + compiler.hooks.shutdown.tap(PluginName, () => { + // Rspack keeps runtime module generator callbacks alive after shutdown. + runtimeModules.forEach((runtimeModule) => + runtimeModule.clearCompilationReferences(), + ); + runtimeModules.clear(); + }); } } @@ -78,4 +91,10 @@ class ChunkAssetRuntimeModule extends webpack.RuntimeModule { override generate() { return generateGetChunkAssetRuntimeCode(this.chunk!); } + + clearCompilationReferences() { + this.compilation = undefined; + this.chunk = undefined; + this.chunkGraph = undefined; + } } diff --git a/packages/docusaurus/src/webpack/plugins/__tests__/ChunkAssetPlugin.test.ts b/packages/docusaurus/src/webpack/plugins/__tests__/ChunkAssetPlugin.test.ts new file mode 100644 index 000000000000..be6bbfde2a2b --- /dev/null +++ b/packages/docusaurus/src/webpack/plugins/__tests__/ChunkAssetPlugin.test.ts @@ -0,0 +1,68 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import {describe, expect, it, vi} from 'vitest'; +import {fromPartial} from '@total-typescript/shoehorn'; +import ChunkAssetPlugin from '../ChunkAssetPlugin'; +import type webpack from 'webpack'; + +function createHook() { + const handlers: ((...args: Args) => void)[] = []; + return { + call: (...args: Args) => handlers.forEach((handler) => handler(...args)), + hook: { + tap: (_name: string, handler: (...args: Args) => void) => { + handlers.push(handler); + }, + }, + }; +} + +describe('ChunkAssetPlugin', () => { + it('detaches runtime modules when the compiler shuts down', () => { + const thisCompilation = createHook<[webpack.Compilation]>(); + const shutdown = createHook<[]>(); + const additionalTreeRuntimeRequirements = createHook<[webpack.Chunk]>(); + const chunk = fromPartial({}); + const chunkGraph = fromPartial({}); + const addRuntimeModule = vi.fn(); + const compilation = fromPartial({ + addRuntimeModule, + hooks: { + additionalTreeRuntimeRequirements: + additionalTreeRuntimeRequirements.hook, + }, + }); + addRuntimeModule.mockImplementation( + (runtimeChunk: webpack.Chunk, runtimeModule: webpack.RuntimeModule) => { + runtimeModule.attach(compilation, runtimeChunk, chunkGraph); + }, + ); + const compiler = fromPartial({ + hooks: { + shutdown: shutdown.hook, + thisCompilation: thisCompilation.hook, + }, + }); + + new ChunkAssetPlugin().apply(compiler); + thisCompilation.call(compilation); + additionalTreeRuntimeRequirements.call(chunk); + + const runtimeModule = addRuntimeModule.mock + .calls[0]![1] as webpack.RuntimeModule; + expect(runtimeModule.compilation).toBe(compilation); + expect(runtimeModule.chunk).toBe(chunk); + expect(runtimeModule.chunkGraph).toBe(chunkGraph); + + shutdown.call(); + + expect(runtimeModule.compilation).toBeUndefined(); + expect(runtimeModule.chunk).toBeUndefined(); + expect(runtimeModule.chunkGraph).toBeUndefined(); + }); +});