From 24a07653d541021e962348ed4aac5051692a849a Mon Sep 17 00:00:00 2001 From: inottn Date: Sun, 1 Dec 2024 11:53:17 +0800 Subject: [PATCH] feat: support chunksSortMode option to HtmlRspackPlugin --- crates/node_binding/binding.d.ts | 1 + .../src/options/raw_builtins/raw_html.rs | 9 + crates/rspack_plugin_html/src/asset.rs | 22 ++- crates/rspack_plugin_html/src/config.rs | 31 ++++ packages/rspack/etc/core.api.md | 1 + .../src/builtin-plugin/HtmlRspackPlugin.ts | 6 + tests/plugin-test/html-plugin/basic.test.js | 158 +++++++++--------- .../en/plugins/rspack/html-rspack-plugin.mdx | 8 + .../zh/plugins/rspack/html-rspack-plugin.mdx | 7 + 9 files changed, 161 insertions(+), 82 deletions(-) diff --git a/crates/node_binding/binding.d.ts b/crates/node_binding/binding.d.ts index c05214e8a33..5e9759b80be 100644 --- a/crates/node_binding/binding.d.ts +++ b/crates/node_binding/binding.d.ts @@ -1469,6 +1469,7 @@ export interface RawHtmlRspackPluginOptions { /** entry_chunk_name (only entry chunks are supported) */ chunks?: Array excludeChunks?: Array + chunksSortMode: "auto" | "manual" sri?: "sha256" | "sha384" | "sha512" minify?: boolean title?: string diff --git a/crates/rspack_binding_options/src/options/raw_builtins/raw_html.rs b/crates/rspack_binding_options/src/options/raw_builtins/raw_html.rs index a3377bf263f..4990f5132cd 100644 --- a/crates/rspack_binding_options/src/options/raw_builtins/raw_html.rs +++ b/crates/rspack_binding_options/src/options/raw_builtins/raw_html.rs @@ -4,6 +4,7 @@ use std::str::FromStr; use napi::bindgen_prelude::Either3; use napi_derive::napi; use rspack_napi::threadsafe_function::ThreadsafeFunction; +use rspack_plugin_html::config::HtmlChunkSortMode; use rspack_plugin_html::config::HtmlInject; use rspack_plugin_html::config::HtmlRspackPluginBaseOptions; use rspack_plugin_html::config::HtmlRspackPluginOptions; @@ -17,6 +18,7 @@ pub type RawHtmlScriptLoading = String; pub type RawHtmlInject = String; pub type RawHtmlSriHashFunction = String; pub type RawHtmlFilename = Vec; +type RawChunkSortMode = String; type RawTemplateRenderFn = ThreadsafeFunction; @@ -48,6 +50,9 @@ pub struct RawHtmlRspackPluginOptions { /// entry_chunk_name (only entry chunks are supported) pub chunks: Option>, pub exclude_chunks: Option>, + #[napi(ts_type = "\"auto\" | \"manual\"")] + pub chunks_sort_mode: RawChunkSortMode, + #[napi(ts_type = "\"sha256\" | \"sha384\" | \"sha512\"")] pub sri: Option, pub minify: Option, @@ -65,6 +70,9 @@ impl From for HtmlRspackPluginOptions { let script_loading = HtmlScriptLoading::from_str(&value.script_loading).expect("Invalid script_loading value"); + let chunks_sort_mode = + HtmlChunkSortMode::from_str(&value.chunks_sort_mode).expect("Invalid chunks_sort_mode value"); + let sri = value.sri.as_ref().map(|s| { HtmlSriHashFunction::from_str(s).unwrap_or_else(|_| panic!("Invalid sri value: {s}")) }); @@ -105,6 +113,7 @@ impl From for HtmlRspackPluginOptions { script_loading, chunks: value.chunks, exclude_chunks: value.exclude_chunks, + chunks_sort_mode, sri, minify: value.minify, title: value.title, diff --git a/crates/rspack_plugin_html/src/asset.rs b/crates/rspack_plugin_html/src/asset.rs index 525e01c2315..536cb021e6a 100644 --- a/crates/rspack_plugin_html/src/asset.rs +++ b/crates/rspack_plugin_html/src/asset.rs @@ -22,7 +22,7 @@ use serde::{Deserialize, Serialize}; use sugar_path::SugarPath; use crate::{ - config::{HtmlInject, HtmlRspackPluginOptions, HtmlScriptLoading}, + config::{HtmlChunkSortMode, HtmlInject, HtmlRspackPluginOptions, HtmlScriptLoading}, sri::{add_sri, create_digest_from_asset}, tag::HtmlPluginTag, }; @@ -49,7 +49,7 @@ impl HtmlPluginAssets { let mut asset_map = HashMap::new(); assets.public_path = public_path.to_string(); - let included_assets = compilation + let filtered_entry_names = compilation .entrypoints .keys() .filter(|&entry_name| { @@ -62,6 +62,24 @@ impl HtmlPluginAssets { } included }) + .collect::>(); + + let sorted_entry_names = match config.chunks_sort_mode { + HtmlChunkSortMode::Auto => filtered_entry_names, + HtmlChunkSortMode::Manual => config + .chunks + .as_ref() + .map(|chunks| { + chunks + .iter() + .filter(|&name| compilation.entrypoints.contains_key(name)) + .collect() + }) + .unwrap_or(filtered_entry_names), + }; + + let included_assets = sorted_entry_names + .iter() .map(|entry_name| compilation.entrypoint_by_name(entry_name)) .flat_map(|entry| entry.get_files(&compilation.chunk_by_ukey)) .filter_map(|asset_name| { diff --git a/crates/rspack_plugin_html/src/config.rs b/crates/rspack_plugin_html/src/config.rs index d1c10d2e6dc..867f56feb1b 100644 --- a/crates/rspack_plugin_html/src/config.rs +++ b/crates/rspack_plugin_html/src/config.rs @@ -114,6 +114,31 @@ impl std::fmt::Debug for TemplateRenderFn { } } +#[derive(Serialize, Debug, Clone, Copy, Default)] +#[serde(rename_all = "snake_case")] +pub enum HtmlChunkSortMode { + #[default] + Auto, + Manual, + // TODO: support function +} + +impl FromStr for HtmlChunkSortMode { + type Err = anyhow::Error; + + fn from_str(s: &str) -> Result { + if s.eq("auto") { + Ok(HtmlChunkSortMode::Auto) + } else if s.eq("manual") { + Ok(HtmlChunkSortMode::Manual) + } else { + Err(anyhow::Error::msg( + "chunksSortMode in html config only support 'auto' or 'manual'", + )) + } + } +} + #[derive(Serialize, Debug)] #[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct HtmlRspackPluginOptions { @@ -139,6 +164,7 @@ pub struct HtmlRspackPluginOptions { /// entry_chunk_name (only entry chunks are supported) pub chunks: Option>, pub exclude_chunks: Option>, + pub chunks_sort_mode: HtmlChunkSortMode, /// hash func that used in subsource integrity /// sha384, sha256 or sha512 @@ -164,6 +190,10 @@ fn default_inject() -> HtmlInject { HtmlInject::Head } +fn default_chunks_sort_mode() -> HtmlChunkSortMode { + HtmlChunkSortMode::Auto +} + impl Default for HtmlRspackPluginOptions { fn default() -> HtmlRspackPluginOptions { HtmlRspackPluginOptions { @@ -177,6 +207,7 @@ impl Default for HtmlRspackPluginOptions { script_loading: default_script_loading(), chunks: None, exclude_chunks: None, + chunks_sort_mode: default_chunks_sort_mode(), sri: None, minify: None, title: None, diff --git a/packages/rspack/etc/core.api.md b/packages/rspack/etc/core.api.md index 514bbce5d66..5336752a1c5 100644 --- a/packages/rspack/etc/core.api.md +++ b/packages/rspack/etc/core.api.md @@ -2296,6 +2296,7 @@ export type HtmlRspackPluginOptions = { scriptLoading?: "blocking" | "defer" | "module" | "systemjs-module"; chunks?: string[]; excludeChunks?: string[]; + chunksSortMode?: "auto" | "manual"; sri?: "sha256" | "sha384" | "sha512"; minify?: boolean; favicon?: string; diff --git a/packages/rspack/src/builtin-plugin/HtmlRspackPlugin.ts b/packages/rspack/src/builtin-plugin/HtmlRspackPlugin.ts index 80a64d79574..0f1dad3e8d8 100644 --- a/packages/rspack/src/builtin-plugin/HtmlRspackPlugin.ts +++ b/packages/rspack/src/builtin-plugin/HtmlRspackPlugin.ts @@ -102,6 +102,9 @@ export type HtmlRspackPluginOptions = { /** Allows you to skip some chunks. */ excludeChunks?: string[]; + /** Allows to control how chunks should be sorted before they are included to the HTML. */ + chunksSortMode?: "auto" | "manual"; + /** The sri hash algorithm, disabled by default. */ sri?: "sha256" | "sha384" | "sha512"; @@ -164,6 +167,7 @@ const htmlRspackPluginOptions = z.strictObject({ .optional(), chunks: z.string().array().optional(), excludeChunks: z.string().array().optional(), + chunksSortMode: z.enum(["auto", "manual"]).optional(), sri: z.enum(["sha256", "sha384", "sha512"]).optional(), minify: z.boolean().optional(), title: z.string().optional(), @@ -205,6 +209,7 @@ const HtmlRspackPluginImpl = create( ? "false" : configInject; const base = typeof c.base === "string" ? { href: c.base } : c.base; + const chunksSortMode = c.chunksSortMode ?? "auto"; let compilation: Compilation | null = null; this.hooks.compilation.tap("HtmlRspackPlugin", compilationInstance => { @@ -346,6 +351,7 @@ const HtmlRspackPluginImpl = create( publicPath: c.publicPath, chunks: c.chunks, excludeChunks: c.excludeChunks, + chunksSortMode, sri: c.sri, minify: c.minify, meta, diff --git a/tests/plugin-test/html-plugin/basic.test.js b/tests/plugin-test/html-plugin/basic.test.js index 016e81fbe2f..ba3d9d9e2b5 100644 --- a/tests/plugin-test/html-plugin/basic.test.js +++ b/tests/plugin-test/html-plugin/basic.test.js @@ -2934,43 +2934,42 @@ describe("HtmlWebpackPlugin", () => { ); }); - // TODO: support `chunksSortMode` - // it("should sort the chunks in auto mode", (done) => { - // testHtmlPlugin( - // { - // mode: "production", - // entry: { - // util: path.join(__dirname, "fixtures/util.js"), - // index: path.join(__dirname, "fixtures/index.js"), - // }, - // output: { - // path: OUTPUT_DIR, - // filename: "[name]_bundle.js", - // }, - // optimization: { - // splitChunks: { - // cacheGroups: { - // commons: { - // chunks: "initial", - // name: "common", - // enforce: true, - // }, - // }, - // }, - // }, - // plugins: [ - // new HtmlWebpackPlugin({ - // chunksSortMode: "auto", - // }), - // ], - // }, - // [ - // /(