Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support chunksSortMode option to HtmlRspackPlugin #8585

Merged
merged 2 commits into from
Dec 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions crates/node_binding/binding.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1469,6 +1469,7 @@ export interface RawHtmlRspackPluginOptions {
/** entry_chunk_name (only entry chunks are supported) */
chunks?: Array<string>
excludeChunks?: Array<string>
chunksSortMode: "auto" | "manual"
sri?: "sha256" | "sha384" | "sha512"
minify?: boolean
title?: string
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -17,6 +18,7 @@ pub type RawHtmlScriptLoading = String;
pub type RawHtmlInject = String;
pub type RawHtmlSriHashFunction = String;
pub type RawHtmlFilename = Vec<String>;
type RawChunkSortMode = String;

type RawTemplateRenderFn = ThreadsafeFunction<String, String>;

Expand Down Expand Up @@ -48,6 +50,9 @@ pub struct RawHtmlRspackPluginOptions {
/// entry_chunk_name (only entry chunks are supported)
pub chunks: Option<Vec<String>>,
pub exclude_chunks: Option<Vec<String>>,
#[napi(ts_type = "\"auto\" | \"manual\"")]
pub chunks_sort_mode: RawChunkSortMode,

#[napi(ts_type = "\"sha256\" | \"sha384\" | \"sha512\"")]
pub sri: Option<RawHtmlSriHashFunction>,
pub minify: Option<bool>,
Expand All @@ -65,6 +70,9 @@ impl From<RawHtmlRspackPluginOptions> 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}"))
});
Expand Down Expand Up @@ -105,6 +113,7 @@ impl From<RawHtmlRspackPluginOptions> for HtmlRspackPluginOptions {
script_loading,
chunks: value.chunks,
exclude_chunks: value.exclude_chunks,
chunks_sort_mode,
sri,
minify: value.minify,
title: value.title,
Expand Down
42 changes: 28 additions & 14 deletions crates/rspack_plugin_html/src/asset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};
Expand All @@ -49,19 +49,33 @@ impl HtmlPluginAssets {
let mut asset_map = HashMap::new();
assets.public_path = public_path.to_string();

let included_assets = compilation
.entrypoints
.keys()
.filter(|&entry_name| {
let mut included = true;
if let Some(included_chunks) = &config.chunks {
included = included_chunks.iter().any(|c| c.eq(entry_name));
}
if let Some(exclude_chunks) = &config.exclude_chunks {
included = included && !exclude_chunks.iter().any(|c| c.eq(entry_name));
}
included
})
let sorted_entry_names: Vec<&String> =
if matches!(config.chunks_sort_mode, HtmlChunkSortMode::Manual)
&& let Some(chunks) = &config.chunks
{
chunks
.iter()
.filter(|&name| compilation.entrypoints.contains_key(name))
.collect()
} else {
compilation
.entrypoints
.keys()
.filter(|&entry_name| {
let mut included = true;
if let Some(included_chunks) = &config.chunks {
included = included_chunks.iter().any(|c| c.eq(entry_name));
}
if let Some(exclude_chunks) = &config.exclude_chunks {
included = included && !exclude_chunks.iter().any(|c| c.eq(entry_name));
}
included
})
.collect()
};

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| {
Expand Down
31 changes: 31 additions & 0 deletions crates/rspack_plugin_html/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Self, Self::Err> {
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 {
Expand All @@ -139,6 +164,7 @@ pub struct HtmlRspackPluginOptions {
/// entry_chunk_name (only entry chunks are supported)
pub chunks: Option<Vec<String>>,
pub exclude_chunks: Option<Vec<String>>,
pub chunks_sort_mode: HtmlChunkSortMode,

/// hash func that used in subsource integrity
/// sha384, sha256 or sha512
Expand All @@ -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 {
Expand All @@ -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,
Expand Down
1 change: 1 addition & 0 deletions crates/rspack_plugin_html/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#![feature(box_patterns)]
#![feature(let_chains)]

pub mod asset;
pub mod config;
Expand Down
1 change: 1 addition & 0 deletions packages/rspack/etc/core.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
6 changes: 6 additions & 0 deletions packages/rspack/src/builtin-plugin/HtmlRspackPlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";

Expand Down Expand Up @@ -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(),
Expand Down Expand Up @@ -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 => {
Expand Down Expand Up @@ -346,6 +351,7 @@ const HtmlRspackPluginImpl = create(
publicPath: c.publicPath,
chunks: c.chunks,
excludeChunks: c.excludeChunks,
chunksSortMode,
sri: c.sri,
minify: c.minify,
meta,
Expand Down
158 changes: 78 additions & 80 deletions tests/plugin-test/html-plugin/basic.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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",
// }),
// ],
// },
// [
// /(<script defer src="common_bundle.js">.+<script defer src="util_bundle.js">.+<script defer src="index_bundle.js">)|(<script defer src="common_bundle.js">.+<script defer src="index_bundle.js">.+<script defer src="util_bundle.js">)/,
// ],
// null,
// done,
// );
// });
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",
}),
],
},
[
/(<script defer src="common_bundle.js">.+<script defer src="util_bundle.js">.+<script defer src="index_bundle.js">)|(<script defer src="common_bundle.js">.+<script defer src="index_bundle.js">.+<script defer src="util_bundle.js">)/,
],
null,
done,
);
});

// TODO: support `chunksSortMode`
// it("should sort the chunks in custom (reverse alphabetical) order", (done) => {
Expand Down Expand Up @@ -3008,49 +3007,48 @@ describe("HtmlWebpackPlugin", () => {
// );
// });

// TODO: support `chunksSortMode`
// it("should sort manually by the chunks", (done) => {
// testHtmlPlugin(
// {
// mode: "production",
// entry: {
// b: path.join(__dirname, "fixtures/util.js"),
// a: path.join(__dirname, "fixtures/theme.js"),
// d: path.join(__dirname, "fixtures/util.js"),
// c: path.join(__dirname, "fixtures/theme.js"),
// },
// output: {
// path: OUTPUT_DIR,
// filename: "[name]_bundle.js",
// },
// module: {
// rules: [{ test: /\.css$/, loader: "css-loader" }],
// },
// optimization: {
// splitChunks: {
// cacheGroups: {
// commons: {
// chunks: "initial",
// name: "common",
// enforce: true,
// },
// },
// },
// },
// plugins: [
// new HtmlWebpackPlugin({
// chunksSortMode: "manual",
// chunks: ["common", "a", "b", "c"],
// }),
// ],
// },
// [
// /<script defer src="common_bundle.js">.+<script defer src="a_bundle.js">.+<script defer src="b_bundle.js">.+<script defer src="c_bundle.js">/,
// ],
// null,
// done,
// );
// });
it("should sort manually by the chunks", (done) => {
testHtmlPlugin(
{
mode: "production",
entry: {
b: path.join(__dirname, "fixtures/util.js"),
a: path.join(__dirname, "fixtures/theme.js"),
d: path.join(__dirname, "fixtures/util.js"),
c: path.join(__dirname, "fixtures/theme.js"),
},
output: {
path: OUTPUT_DIR,
filename: "[name]_bundle.js",
},
module: {
rules: [{ test: /\.css$/, loader: "css-loader" }],
},
optimization: {
splitChunks: {
cacheGroups: {
commons: {
chunks: "initial",
name: "common",
enforce: true,
},
},
},
},
plugins: [
new HtmlWebpackPlugin({
chunksSortMode: "manual",
chunks: ["common", "a", "b", "c"],
}),
],
},
[
/<script defer src="common_bundle.js">.+<script defer src="a_bundle.js">.+<script defer src="b_bundle.js">.+<script defer src="c_bundle.js">/,
],
null,
done,
);
});

it("should add the webpack compilation object as a property of the templateParam object", (done) => {
testHtmlPlugin(
Expand Down
Loading
Loading