Skip to content

Commit

Permalink
refactor(core): rewrite Webpack ChunkAssetPlugin with RuntimeModule (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
slorber authored Sep 10, 2024
1 parent 1a70734 commit 3725dc3
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 42 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build-perf.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ jobs:
repo-token: ${{ secrets.GITHUB_TOKEN }}
build-script: build:website:fast
clean-script: clear:website # see https://github.com/facebook/docusaurus/pull/6838
pattern: '{website/build/assets/js/main*js,website/build/assets/css/styles*css,website/.docusaurus/globalData.json,website/.docusaurus/registry.js,website/.docusaurus/routes.js,website/.docusaurus/routesChunkNames.json,website/.docusaurus/site-metadata.json,website/.docusaurus/codeTranslations.json,website/.docusaurus/i18n.json,website/.docusaurus/docusaurus.config.mjs,website/build/index.html,website/build/docs.html,website/build/docs/**/*.html,website/build/blog.html,website/build/blog/**/*.html}'
pattern: '{website/build/assets/js/main*js,website/build/assets/js/runtime~main*js,website/build/assets/css/styles*css,website/.docusaurus/globalData.json,website/.docusaurus/registry.js,website/.docusaurus/routes.js,website/.docusaurus/routesChunkNames.json,website/.docusaurus/site-metadata.json,website/.docusaurus/codeTranslations.json,website/.docusaurus/i18n.json,website/.docusaurus/docusaurus.config.mjs,website/build/index.html,website/build/docs.html,website/build/docs/**/*.html,website/build/blog.html,website/build/blog/**/*.html}'
# HTML files: exclude versioned docs pages, tags pages, html redirect files
exclude: '{website/build/docs/?.?.?/**/*.html,website/build/docs/next/**/*.html,website/build/blog/tags/**/*.html,**/*.html.html}'
strip-hash: '\.([^;]\w{7})\.'
Expand Down
102 changes: 63 additions & 39 deletions packages/docusaurus/src/webpack/plugins/ChunkAssetPlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,49 +7,73 @@

import webpack, {type Compiler} from 'webpack';

const pluginName = 'chunk-asset-plugin';
// Adds a custom Docusaurus Webpack runtime function `__webpack_require__.gca`
// gca = Get Chunk Asset, it converts a chunkName to a JS asset URL
// It is called in Core client/docusaurus.ts for chunk preloading/prefetching
// Example: gca("814f3328") = "/baseUrl/assets/js/814f3328.03fcc178.js"
// See also: https://github.com/facebook/docusaurus/pull/10485

/**
* We modify webpack runtime to add an extra function called
* "__webpack_require__.gca" that will allow us to get the corresponding chunk
* asset for a webpack chunk. Pass it the chunkName or chunkId you want to load.
* For example: if you have a chunk named "my-chunk-name" that will map to
* "/publicPath/0a84b5e7.c8e35c7a.js" as its corresponding output path
* __webpack_require__.gca("my-chunk-name") will return
* "/publicPath/0a84b5e7.c8e35c7a.js"
*
* "gca" stands for "get chunk asset"
// The name of the custom Docusaurus Webpack runtime function
const DocusaurusGetChunkAssetFn = '__webpack_require__.gca';

const PluginName = 'Docusaurus-ChunkAssetPlugin';

function generateGetChunkAssetRuntimeCode(chunk: webpack.Chunk): string {
const chunkIdToName = chunk.getChunkMaps(false).name;
const chunkNameToId = Object.fromEntries(
Object.entries(chunkIdToName).map(([chunkId, chunkName]) => [
chunkName,
chunkId,
]),
);

const {
// publicPath = __webpack_require__.p
// Example: "/" or "/baseUrl/"
// https://github.com/webpack/webpack/blob/v5.94.0/lib/runtime/PublicPathRuntimeModule.js
publicPath,

// getChunkScriptFilename = __webpack_require__.u
// Example: getChunkScriptFilename("814f3328") = "814f3328.03fcc178.js"
// https://github.com/webpack/webpack/blob/v5.94.0/lib/runtime/GetChunkFilenameRuntimeModule.js
getChunkScriptFilename,
} = webpack.RuntimeGlobals;

const code = `// Docusaurus function to get chunk asset
${DocusaurusGetChunkAssetFn} = function(chunkId) { chunkId = ${JSON.stringify(
chunkNameToId,
)}[chunkId]||chunkId; return ${publicPath} + ${getChunkScriptFilename}(chunkId); };`;

return webpack.Template.asString(code);
}

/*
Note: we previously used `MainTemplate.hooks.requireExtensions.tap()`
But it will be removed in Webpack 6 and is not supported by Rspack
So instead we use equivalent code inspired by:
- https://github.com/webpack/webpack/blob/v5.94.0/lib/RuntimePlugin.js#L462
- https://github.com/webpack/webpack/blob/v5.94.0/lib/runtime/CompatRuntimeModule.js
*/
export default class ChunkAssetPlugin {
apply(compiler: Compiler): void {
compiler.hooks.thisCompilation.tap(pluginName, ({mainTemplate}) => {
mainTemplate.hooks.requireExtensions.tap(pluginName, (source, chunk) => {
const chunkIdToName = chunk.getChunkMaps(false).name;
const chunkNameToId = Object.fromEntries(
Object.entries(chunkIdToName).map(([chunkId, chunkName]) => [
chunkName,
chunkId,
]),
);
const buf = [source];
buf.push('// function to get chunk asset');
buf.push(
// If chunkName is passed, we convert it to chunk asset url
// .p => public path url ("/" or "/baseUrl/")
// .u(chunkId) =>
// chunk asset url ("assets/js/x63b64xd.contentHash.js")
// not sure where this is documented, but this link was helpful:
// https://programmer.help/blogs/5d68849083e1a.html
//
// Note: __webpack_require__.gca() is called in docusaurus.ts for
// prefetching
// Note: we previously used jsonpScriptSrc (Webpack 4)
`__webpack_require__.gca = function(chunkId) { chunkId = ${JSON.stringify(
chunkNameToId,
)}[chunkId]||chunkId; return __webpack_require__.p + __webpack_require__.u(chunkId); };`,
);
return webpack.Template.asString(buf);
});
compiler.hooks.thisCompilation.tap(PluginName, (compilation) => {
compilation.hooks.additionalTreeRuntimeRequirements.tap(
PluginName,
(chunk) => {
compilation.addRuntimeModule(chunk, new ChunkAssetRuntimeModule());
},
);
});
}
}

// Inspired by https://github.com/webpack/webpack/blob/v5.94.0/lib/runtime/CompatRuntimeModule.js
class ChunkAssetRuntimeModule extends webpack.RuntimeModule {
constructor() {
super('ChunkAssetRuntimeModule', webpack.RuntimeModule.STAGE_ATTACH);
this.fullHash = true;
}
override generate() {
return generateGetChunkAssetRuntimeCode(this.chunk!);
}
}
2 changes: 0 additions & 2 deletions packages/docusaurus/src/webpack/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,6 @@ export default async function createServerConfig(params: {
path: outputDir,
filename: outputFilename,
libraryTarget: 'commonjs2',
// Workaround for Webpack 4 Bug (https://github.com/webpack/webpack/issues/6522)
globalObject: 'this',
},
plugins: [
// Show compilation progress bar.
Expand Down
1 change: 1 addition & 0 deletions project-words.txt
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,7 @@ rmiz
rsdoctor
Rsdoctor
RSDOCTOR
Rspack
rtcts
rtlcss
saurus
Expand Down

0 comments on commit 3725dc3

Please sign in to comment.