diff --git a/crates/rspack_core/src/compiler/compilation.rs b/crates/rspack_core/src/compiler/compilation.rs index 99972dd0780..5b36477ccd8 100644 --- a/crates/rspack_core/src/compiler/compilation.rs +++ b/crates/rspack_core/src/compiler/compilation.rs @@ -1408,6 +1408,9 @@ impl Compilation { let mut runtime_requirements; loop { + // runtime_requirements: rt_requirements of last time + // runtime_requirements_mut: changed rt_requirements + // requirements: all rt_requirements runtime_requirements = runtime_requirements_mut; runtime_requirements_mut = RuntimeGlobals::default(); call_hook( @@ -1415,6 +1418,8 @@ impl Compilation { &runtime_requirements, &mut runtime_requirements_mut, )?; + + // check if we have changes to runtime_requirements runtime_requirements_mut = runtime_requirements_mut.difference(requirements.intersection(runtime_requirements_mut)); if runtime_requirements_mut.is_empty() { @@ -1891,6 +1896,7 @@ impl Compilation { let runtime_module_identifier = ModuleIdentifier::from(format!("{}/{}", &chunk.runtime, module.identifier())); module.attach(*chunk_ukey); + self.chunk_graph.add_module(runtime_module_identifier); self .chunk_graph diff --git a/crates/rspack_plugin_extract_css/src/plugin.rs b/crates/rspack_plugin_extract_css/src/plugin.rs index f406273954d..eb46d46fa85 100644 --- a/crates/rspack_plugin_extract_css/src/plugin.rs +++ b/crates/rspack_plugin_extract_css/src/plugin.rs @@ -432,7 +432,7 @@ async fn compilation( } #[plugin_hook(CompilationRuntimeRequirementInTree for PluginCssExtract)] -fn runtime_requirements_in_tree( +fn runtime_requirement_in_tree( &self, compilation: &mut Compilation, chunk_ukey: &ChunkUkey, @@ -440,27 +440,21 @@ fn runtime_requirements_in_tree( runtime_requirements: &RuntimeGlobals, runtime_requirements_mut: &mut RuntimeGlobals, ) -> Result> { + // different from webpack, Rspack can invoke this multiple times, + // each time with current runtime_globals, and records every mutation + // by `runtime_requirements_mut`, but this RuntimeModule depends on + // 2 runtimeGlobals, if check current runtime_requirements, we might + // insert CssLoadingRuntimeModule with with_loading: true but with_hmr: false + // for the first time, and with_loading: false but with_hmr: true for the + // second time + // For plugin that depends on 2 runtime_globals, should check all_runtime_requirements if !self.options.runtime { return Ok(None); } - let with_loading = runtime_requirements.contains(RuntimeGlobals::ENSURE_CHUNK_HANDLERS) && { - let chunk = compilation.chunk_by_ukey.expect_get(chunk_ukey); - - chunk - .get_all_async_chunks(&compilation.chunk_group_by_ukey) - .iter() - .any(|chunk| { - !compilation - .chunk_graph - .get_chunk_modules_by_source_type(chunk, SOURCE_TYPE[0], &compilation.get_module_graph()) - .is_empty() - }) - }; - - let with_hmr = runtime_requirements.contains(RuntimeGlobals::HMR_DOWNLOAD_UPDATE_HANDLERS); - - if with_loading || with_hmr { + if runtime_requirements.contains(RuntimeGlobals::HMR_DOWNLOAD_UPDATE_HANDLERS) + || runtime_requirements.contains(RuntimeGlobals::ENSURE_CHUNK_HANDLERS) + { if let Some(chunk_filename) = self.options.chunk_filename.template() && chunk_filename.contains("hash") { @@ -499,8 +493,6 @@ fn runtime_requirements_in_tree( self.options.attributes.clone(), self.options.link_type.clone(), self.options.insert.clone(), - with_loading, - with_hmr, )), )?; } @@ -668,7 +660,7 @@ impl Plugin for PluginCssExtract { .context .compilation_hooks .runtime_requirement_in_tree - .tap(runtime_requirements_in_tree::new(self)); + .tap(runtime_requirement_in_tree::new(self)); ctx .context .compilation_hooks diff --git a/crates/rspack_plugin_extract_css/src/runtime.rs b/crates/rspack_plugin_extract_css/src/runtime.rs index d41e7f30fb7..59478f3d0b0 100644 --- a/crates/rspack_plugin_extract_css/src/runtime.rs +++ b/crates/rspack_plugin_extract_css/src/runtime.rs @@ -3,10 +3,12 @@ use std::sync::Arc; use cow_utils::CowUtils; use rspack_collections::UkeySet; use rspack_core::{ - impl_runtime_module, rspack_sources::RawSource, ChunkUkey, Compilation, CrossOriginLoading, - RuntimeGlobals, RuntimeModule, RuntimeModuleStage, + impl_runtime_module, + rspack_sources::{RawSource, SourceExt}, + ChunkUkey, Compilation, CrossOriginLoading, RuntimeGlobals, RuntimeModule, RuntimeModuleStage, }; use rspack_error::Result; +use rspack_plugin_runtime::get_chunk_runtime_requirements; use rustc_hash::FxHashMap; use crate::plugin::{InsertType, SOURCE_TYPE}; @@ -22,9 +24,6 @@ pub(crate) struct CssLoadingRuntimeModule { attributes: FxHashMap, link_type: Option, insert: InsertType, - - loading: bool, - hmr: bool, } impl CssLoadingRuntimeModule { @@ -33,10 +32,8 @@ impl CssLoadingRuntimeModule { attributes: FxHashMap, link_type: Option, insert: InsertType, - loading: bool, - hmr: bool, ) -> Self { - Self::with_default(chunk, attributes, link_type, insert, loading, hmr) + Self::with_default(chunk, attributes, link_type, insert) } fn get_css_chunks(&self, compilation: &Compilation) -> UkeySet { @@ -73,9 +70,34 @@ impl RuntimeModule for CssLoadingRuntimeModule { compilation: &rspack_core::Compilation, ) -> Result { let runtime = RUNTIME_CODE; + let runtime_requirements = get_chunk_runtime_requirements(compilation, &self.chunk); + + let with_loading = runtime_requirements.contains(RuntimeGlobals::ENSURE_CHUNK_HANDLERS) && { + let chunk = compilation.chunk_by_ukey.expect_get(&self.chunk); + + chunk + .get_all_async_chunks(&compilation.chunk_group_by_ukey) + .iter() + .any(|chunk| { + !compilation + .chunk_graph + .get_chunk_modules_by_source_type( + chunk, + SOURCE_TYPE[0], + &compilation.get_module_graph(), + ) + .is_empty() + }) + }; + + let with_hmr = runtime_requirements.contains(RuntimeGlobals::HMR_DOWNLOAD_UPDATE_HANDLERS); + + if !with_hmr && !with_loading { + return Ok(RawSource::from("").boxed()); + } let mut attr = String::default(); - let mut attributes = self.attributes.iter().collect::>(); + let mut attributes: Vec<(&String, &String)> = self.attributes.iter().collect::>(); attributes.sort_unstable_by(|(k1, _), (k2, _)| k1.cmp(k2)); for (attr_key, attr_value) in attributes { @@ -121,7 +143,7 @@ impl RuntimeModule for CssLoadingRuntimeModule { ), }; - let runtime = if self.loading { + let runtime = if with_loading { let chunks = self.get_css_chunks(compilation); if chunks.is_empty() { runtime.cow_replace("__WITH_LOADING__", "// no chunk loading") @@ -171,7 +193,7 @@ impl RuntimeModule for CssLoadingRuntimeModule { runtime.cow_replace("__WITH_LOADING__", "// no chunk loading") }; - let runtime = if self.hmr { + let runtime = if with_hmr { runtime.cow_replace( "__WITH_HMT__", &WITH_HMR.cow_replace( diff --git a/packages/playground/cases/lazy-compilation/css-update/index.test.ts b/packages/playground/cases/lazy-compilation/css-update/index.test.ts new file mode 100644 index 00000000000..b2256daf0d1 --- /dev/null +++ b/packages/playground/cases/lazy-compilation/css-update/index.test.ts @@ -0,0 +1,21 @@ +import { expect, test } from "@/fixtures"; + +test("should update style", async ({ page }) => { + const body = await page.$("body"); + const backgroundColor = await body!.evaluate( + el => window.getComputedStyle(el).backgroundColor + ); + // first time enter the page, style is red + expect(backgroundColor, "red"); + + // second time enter the page, this time brings query, + // trigger lazy-compile + const url = await body!.evaluate(() => window.location.href); + await page.goto(`${url}?1`); + const updatedBody = await page.$("body"); + const updatedBackgroundColor = await updatedBody!.evaluate( + el => window.getComputedStyle(el).backgroundColor + ); + // first time enter the page, style is red + expect(updatedBackgroundColor, "blue"); +}); diff --git a/packages/playground/cases/lazy-compilation/css-update/rspack.config.js b/packages/playground/cases/lazy-compilation/css-update/rspack.config.js new file mode 100644 index 00000000000..adcf542d557 --- /dev/null +++ b/packages/playground/cases/lazy-compilation/css-update/rspack.config.js @@ -0,0 +1,37 @@ +const rspack = require("@rspack/core"); + +/** @type { import('@rspack/core').RspackOptions } */ +module.exports = { + context: __dirname, + entry: "./src/index.js", + mode: "development", + module: { + rules: [ + { + test: /\.css$/, + use: [rspack.CssExtractRspackPlugin.loader, "css-loader"] + } + ] + }, + plugins: [new rspack.HtmlRspackPlugin(), new rspack.CssExtractRspackPlugin()], + experiments: { + css: false, + lazyCompilation: true + }, + optimization: { + splitChunks: { + minSize: 0, + chunks: "all", + cacheGroups: { + styles: { + test: /\.css$/, + name: "style.css" + } + } + } + }, + devServer: { + hot: true, + port: 5678 + } +}; diff --git a/packages/playground/cases/lazy-compilation/css-update/src/blue.css b/packages/playground/cases/lazy-compilation/css-update/src/blue.css new file mode 100644 index 00000000000..d7cf32a7d7f --- /dev/null +++ b/packages/playground/cases/lazy-compilation/css-update/src/blue.css @@ -0,0 +1,3 @@ +body { + background-color: blue; +} diff --git a/packages/playground/cases/lazy-compilation/css-update/src/change.js b/packages/playground/cases/lazy-compilation/css-update/src/change.js new file mode 100644 index 00000000000..d9c5131d383 --- /dev/null +++ b/packages/playground/cases/lazy-compilation/css-update/src/change.js @@ -0,0 +1 @@ +import "./blue.css"; diff --git a/packages/playground/cases/lazy-compilation/css-update/src/index.js b/packages/playground/cases/lazy-compilation/css-update/src/index.js new file mode 100644 index 00000000000..46b4db678c8 --- /dev/null +++ b/packages/playground/cases/lazy-compilation/css-update/src/index.js @@ -0,0 +1,6 @@ +import "./red.css"; + +if (new URL(window.location.href).search) { + // @ts-expect-error change.js no dts + import("./change.js"); +} diff --git a/packages/playground/cases/lazy-compilation/css-update/src/red.css b/packages/playground/cases/lazy-compilation/css-update/src/red.css new file mode 100644 index 00000000000..e8c8b61f5d6 --- /dev/null +++ b/packages/playground/cases/lazy-compilation/css-update/src/red.css @@ -0,0 +1,3 @@ +body { + background-color: red; +} diff --git a/tests/plugin-test/css-extract/cases/runtime/expected/runtime~main.js b/tests/plugin-test/css-extract/cases/runtime/expected/runtime~main.js index c650da8f4f7..6a90b5965a4 100644 --- a/tests/plugin-test/css-extract/cases/runtime/expected/runtime~main.js +++ b/tests/plugin-test/css-extract/cases/runtime/expected/runtime~main.js @@ -93,6 +93,17 @@ __webpack_require__.e = function (chunkId) { return "" + chunkId + ".js"; }; +})(); +// webpack/runtime/get mini-css chunk filename +(() => { +// This function allow to reference chunks + __webpack_require__.miniCssF = function (chunkId) { + // return url for filenames not based on template + + // return url for filenames based on template + return "" + chunkId + ".css"; + }; + })(); // webpack/runtime/global (() => {