From d403218ce0103029cfa7a5469a3f189e473ca34b Mon Sep 17 00:00:00 2001 From: Shu Ding Date: Wed, 13 Jul 2022 07:00:56 +0200 Subject: [PATCH] Fix independent CSSModule missing from flight manifest (#38575) In certain cases, mini-css-extractor will create independent `CSSModule` to the module graph, which doesn't have a user request or resource. Hence we can't match them in the flight manifest plugin like before. This PR adds a work around to use `mod.type` and `mod._identifier` to get such CSS assets. ## Bug - [ ] Related issues linked using `fixes #number` - [ ] Integration tests added - [ ] Errors have helpful link attached, see `contributing.md` ## Feature - [ ] Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR. - [ ] Related issues linked using `fixes #number` - [ ] Integration tests added - [ ] Documentation added - [ ] Telemetry added. In case of a feature if it's used or not. - [ ] Errors have helpful link attached, see `contributing.md` ## Documentation / Examples - [ ] Make sure the linting passes by running `pnpm lint` - [ ] The examples guidelines are followed from [our contributing doc](https://github.com/vercel/next.js/blob/canary/contributing.md#adding-examples) --- .../webpack/plugins/flight-manifest-plugin.ts | 38 ++++++++++++------- packages/next/client/app-index.tsx | 1 + 2 files changed, 25 insertions(+), 14 deletions(-) diff --git a/packages/next/build/webpack/plugins/flight-manifest-plugin.ts b/packages/next/build/webpack/plugins/flight-manifest-plugin.ts index d6c8fc250984..50f0fda78e67 100644 --- a/packages/next/build/webpack/plugins/flight-manifest-plugin.ts +++ b/packages/next/build/webpack/plugins/flight-manifest-plugin.ts @@ -58,8 +58,10 @@ export class FlightManifestPlugin { compilation.hooks.processAssets.tap( { name: PLUGIN_NAME, + // Have to be in the optimize stage to run after updating the CSS + // asset hash via extract mini css plugin. // @ts-ignore TODO: Remove ignore when webpack 5 is stable - stage: webpack.Compilation.PROCESS_ASSETS_STAGE_ADDITIONS, + stage: webpack.Compilation.PROCESS_ASSETS_STAGE_OPTIMIZE_HASH, }, (assets: any) => this.createAsset(assets, compilation, compiler.context) ) @@ -77,7 +79,22 @@ export class FlightManifestPlugin { id: string | number, mod: any ) { - const resource: string = mod.resource + const isCSSModule = + mod.type === 'css/mini-extract' || + (mod.loaders && + (dev + ? mod.loaders.some((item: any) => + item.loader.includes('next-style-loader/index.js') + ) + : mod.loaders.some((item: any) => + item.loader.includes('mini-css-extract-plugin/loader.js') + ))) + + const resource = + mod.type === 'css/mini-extract' + ? mod._identifier.slice(mod._identifier.lastIndexOf('!') + 1) + : mod.resource + if (!resource) return const moduleExports: any = manifest[resource] || {} @@ -87,21 +104,14 @@ export class FlightManifestPlugin { // Note that this isn't that reliable as webpack is still possible to assign // additional queries to make sure there's no conflict even using the `named` // module ID strategy. - let ssrNamedModuleId = relative(context, mod.resourceResolveData.path) + let ssrNamedModuleId = relative( + context, + mod.resourceResolveData?.path || resource + ) if (!ssrNamedModuleId.startsWith('.')) ssrNamedModuleId = `./${ssrNamedModuleId}` - if ( - mod.request && - /\.css$/.test(mod.request) && - (dev - ? mod.loaders.some((item: any) => - item.loader.includes('next-style-loader/index.js') - ) - : mod.loaders.some((item: any) => - item.loader.includes('mini-css-extract-plugin/loader.js') - )) - ) { + if (isCSSModule) { if (!manifest[resource]) { if (dev) { const chunkIdNameMapping = (chunk.ids || []).map((chunkId) => { diff --git a/packages/next/client/app-index.tsx b/packages/next/client/app-index.tsx index b7ca2b3d227a..95525aced666 100644 --- a/packages/next/client/app-index.tsx +++ b/packages/next/client/app-index.tsx @@ -31,6 +31,7 @@ self.__next_require__ = __webpack_require__ // eslint-disable-next-line no-undef ;(self as any).__next_chunk_load__ = (chunk: string) => { + if (!chunk) return Promise.resolve() if (chunk.endsWith('.css')) { const existingTag = document.querySelector(`link[href="${chunk}"]`) if (!existingTag) {