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(html): support function filename and [name] in filename #7753

Merged
merged 6 commits into from
Oct 28, 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
2 changes: 1 addition & 1 deletion crates/node_binding/binding.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1372,7 +1372,7 @@ export interface RawHtmlRspackPluginBaseOptions {

export interface RawHtmlRspackPluginOptions {
/** emitted file name in output path */
filename?: string
filename?: string[]
/** template html file */
template?: string
templateFn?: (data: string) => Promise<string>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use rspack_plugin_html::sri::HtmlSriHashFunction;
pub type RawHtmlScriptLoading = String;
pub type RawHtmlInject = String;
pub type RawHtmlSriHashFunction = String;
pub type RawHtmlFilename = String;
pub type RawHtmlFilename = Vec<String>;

type RawTemplateRenderFn = ThreadsafeFunction<String, String>;

Expand All @@ -27,7 +27,7 @@ type RawTemplateParameter =
#[napi(object, object_to_js = false)]
pub struct RawHtmlRspackPluginOptions {
/// emitted file name in output path
#[napi(ts_type = "string")]
#[napi(ts_type = "string[]")]
pub filename: Option<RawHtmlFilename>,
/// template html file
pub template: Option<String>,
Expand Down Expand Up @@ -70,7 +70,9 @@ impl From<RawHtmlRspackPluginOptions> for HtmlRspackPluginOptions {
});

HtmlRspackPluginOptions {
filename: value.filename.unwrap_or_else(|| String::from("index.html")),
filename: value
.filename
.unwrap_or_else(|| vec![String::from("index.html")]),
template: value.template,
template_fn: value.template_fn.map(|func| TemplateRenderFn {
inner: Box::new(move |data| {
Expand Down
6 changes: 3 additions & 3 deletions crates/rspack_plugin_html/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ impl std::fmt::Debug for TemplateRenderFn {
pub struct HtmlRspackPluginOptions {
/// emitted file name in output path
#[serde(default = "default_filename")]
pub filename: String,
pub filename: Vec<String>,
/// template html file
pub template: Option<String>,
#[serde(skip)]
Expand Down Expand Up @@ -152,8 +152,8 @@ pub struct HtmlRspackPluginOptions {
pub base: Option<HtmlRspackPluginBaseOptions>,
}

fn default_filename() -> String {
String::from("index.html")
fn default_filename() -> Vec<String> {
vec![String::from("index.html")]
}

fn default_script_loading() -> HtmlScriptLoading {
Expand Down
106 changes: 55 additions & 51 deletions crates/rspack_plugin_html/src/plugin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,13 @@ impl HtmlRspackPlugin {
}

async fn generate_html(
filename: &str,
html_file_name: &Filename<NoFilenameFn>,
config: &HtmlRspackPluginOptions,
compilation: &mut Compilation,
hooks: &HtmlPluginHooks,
) -> Result<(String, String, Vec<PathBuf>), miette::Error> {
let public_path = config.get_public_path(compilation, &config.filename);
let public_path = config.get_public_path(compilation, filename);

let mut template = HtmlTemplate::new(config, compilation)?;

Expand Down Expand Up @@ -111,6 +112,7 @@ async fn generate_html(

template
.create_parameters(
filename,
config,
&alter_asset_tag_groups_data.head_tags,
&alter_asset_tag_groups_data.body_tags,
Expand Down Expand Up @@ -177,60 +179,62 @@ async fn process_assets(&self, compilation: &mut Compilation) -> Result<()> {
let config: &HtmlRspackPluginOptions = &self.config;
let hooks = HtmlRspackPlugin::get_compilation_hooks(compilation.id());

let output_file_name = FilenameTemplate::from(
config
.filename
.cow_replace("[templatehash]", "[contenthash]")
.into_owned(),
);

let (template_file_name, html) =
match generate_html(&output_file_name, config, compilation, &hooks).await {
Ok(content) => {
compilation.file_dependencies.extend(content.2);
(content.0, content.1)
}
Err(err) => {
let error_msg = err.to_string();
compilation.push_diagnostic(Diagnostic::from(err));
("error.html".to_string(), create_error_html(&error_msg))
}
};

let mut before_emit_data = hooks
.before_emit
.call(BeforeEmitData {
html,
output_name: output_file_name.as_str().to_string(),
})
.await?;
// TODO: parallel generate html
for filename in &config.filename {
let output_file_name = FilenameTemplate::from(
filename
.cow_replace("[templatehash]", "[contenthash]")
.into_owned(),
);

if let Some(favicon) = &config.favicon {
match create_favicon_asset(favicon, config, compilation) {
Ok(favicon) => compilation.emit_asset(favicon.0, favicon.1),
Err(err) => {
let error_msg = err.to_string();
compilation.push_diagnostic(Diagnostic::from(err));
before_emit_data.html = create_error_html(&error_msg);
}
};
}
let (template_file_name, html) =
match generate_html(filename, &output_file_name, config, compilation, &hooks).await {
Ok(content) => {
compilation.file_dependencies.extend(content.2);
(content.0, content.1)
}
Err(err) => {
let error_msg = err.to_string();
compilation.push_diagnostic(Diagnostic::from(err));
("error.html".to_string(), create_error_html(&error_msg))
}
};

let mut before_emit_data = hooks
.before_emit
.call(BeforeEmitData {
html,
output_name: output_file_name.as_str().to_string(),
})
.await?;

if let Some(favicon) = &config.favicon {
match create_favicon_asset(favicon, config, compilation) {
Ok(favicon) => compilation.emit_asset(favicon.0, favicon.1),
Err(err) => {
let error_msg = err.to_string();
compilation.push_diagnostic(Diagnostic::from(err));
before_emit_data.html = create_error_html(&error_msg);
}
};
}

let html_asset = create_html_asset(
&output_file_name,
&before_emit_data.html,
&template_file_name,
compilation,
);
let html_asset = create_html_asset(
&output_file_name,
&before_emit_data.html,
&template_file_name,
compilation,
);

compilation.emit_asset(html_asset.0.clone(), html_asset.1);
compilation.emit_asset(html_asset.0.clone(), html_asset.1);

let _ = hooks
.after_emit
.call(AfterEmitData {
output_name: html_asset.0.to_string(),
})
.await?;
let _ = hooks
.after_emit
.call(AfterEmitData {
output_name: html_asset.0.to_string(),
})
.await?;
}

Ok(())
}
Expand Down
3 changes: 2 additions & 1 deletion crates/rspack_plugin_html/src/template.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ impl HtmlTemplate {

pub async fn create_parameters(
&mut self,
filename: &str,
config: &HtmlRspackPluginOptions,
head_tags: &Vec<HtmlPluginTag>,
body_tags: &Vec<HtmlPluginTag>,
Expand Down Expand Up @@ -145,7 +146,7 @@ impl HtmlTemplate {
Mode::None => "none",
},
"output": {
"publicPath": config.get_public_path(compilation, &config.filename),
"publicPath": config.get_public_path(compilation, filename),
"crossOriginLoading": match &compilation.options.output.cross_origin_loading {
CrossOriginLoading::Disable => "false",
CrossOriginLoading::Enable(value) => value,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
```html title=default.611dc973e15950e8.html
```html title=main.0c2527cc4ffe363a.html
<!doctype html><html><head><meta charset="utf-8"><title>rspack</title><script defer src="/runtime.js"></script><script defer src="/main.js"></script><link href="/main.css" rel="stylesheet"></head><body></body></html>
```

```html title=index.611dc973e15950e8.html
```html title=main.951842af77c9d957.html
<!doctype html><html><head><meta charset="UTF-8"><title>Rspack App</title><script defer src="/runtime.js"></script><script defer src="/main.js"></script><link href="/main.css" rel="stylesheet"></head><body></body></html>
```
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ module.exports = {
builtins: {
html: [
{
filename: "[name].[hash].html"
filename: "[name].[contenthash].html"
},
{
template: "./index.html",
filename: "[name].[hash].html"
filename: "[name].[contenthash].html"
}
]
},
Expand Down
2 changes: 1 addition & 1 deletion packages/rspack/etc/core.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -2124,7 +2124,7 @@ const HtmlRspackPluginImpl: {
// @public (undocumented)
export type HtmlRspackPluginOptions = {
title?: string;
filename?: string;
filename?: string | ((entry: string) => string);
template?: string;
templateContent?: string | TemplateRenderFunction;
templateParameters?: Record<string, string> | boolean | TemplateParamFunction;
Expand Down
40 changes: 37 additions & 3 deletions packages/rspack/src/builtin-plugin/HtmlRspackPlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ export type HtmlRspackPluginOptions = {
* The file to write the HTML to. You can specify a subdirectory here too (eg: pages/index.html).
* @default 'index.html'
*/
filename?: string;
filename?: string | ((entry: string) => string);

/** The template file path. */
template?: string;
Expand Down Expand Up @@ -125,8 +125,13 @@ export type HtmlRspackPluginOptions = {
hash?: boolean;
};

const templateFilenameFunction = z
.function()
.args(z.string())
.returns(z.string());

const htmlRspackPluginOptions = z.strictObject({
filename: z.string().optional(),
filename: z.string().or(templateFilenameFunction).optional(),
template: z
.string()
.refine(
Expand Down Expand Up @@ -303,8 +308,37 @@ const HtmlRspackPluginImpl = create(
templateParameters = rawTemplateParameters;
}

let filenames: Set<string> | undefined = undefined;
if (typeof c.filename === "string") {
filenames = new Set();
if (c.filename.includes("[name]")) {
if (typeof this.options.entry === "object") {
for (const entryName of Object.keys(this.options.entry)) {
filenames.add(c.filename.replace(/\[name\]/g, entryName));
}
} else {
throw new Error(
"HtmlRspackPlugin: filename with `[name]` does not support function entry"
);
}
} else {
filenames.add(c.filename);
}
} else if (typeof c.filename === "function") {
filenames = new Set();
if (typeof this.options.entry === "object") {
for (const entryName of Object.keys(this.options.entry)) {
filenames.add(c.filename(entryName));
}
} else {
throw new Error(
"HtmlRspackPlugin: function filename does not support function entry"
);
}
}

return {
filename: c.filename,
filename: filenames ? Array.from(filenames) : undefined,
template: c.template,
hash: c.hash,
title: c.title,
Expand Down
2 changes: 1 addition & 1 deletion tests/plugin-test/copy-plugin/build/main.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading