From cd39f50ab6915e8c5113d6471f4399e10c269129 Mon Sep 17 00:00:00 2001 From: "gaoyuan.1226" Date: Fri, 26 Apr 2024 15:52:42 +0800 Subject: [PATCH] feat(copyRspackPlugin): support transform configuration --- Cargo.lock | 2 + crates/node_binding/binding.d.ts | 1 + .../src/options/raw_builtins/raw_copy.rs | 37 ++++++++++-- crates/rspack_plugin_copy/Cargo.toml | 2 + crates/rspack_plugin_copy/src/lib.rs | 60 ++++++++++++++----- plugin-test/copy-plugin/CopyPlugin.test.js | 33 ++++++++++ .../__snapshots__/CopyPlugin.test.js.snap | 10 ++++ .../en/plugins/rspack/copy-rspack-plugin.mdx | 10 ++++ .../zh/plugins/rspack/copy-rspack-plugin.mdx | 10 ++++ 9 files changed, 146 insertions(+), 19 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d85374cf1c9..4091e1c52c5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3146,6 +3146,8 @@ name = "rspack_plugin_copy" version = "0.1.0" dependencies = [ "dashmap", + "derivative", + "futures", "glob", "lazy_static", "pathdiff", diff --git a/crates/node_binding/binding.d.ts b/crates/node_binding/binding.d.ts index b9e30fef3dc..9dae34d013d 100644 --- a/crates/node_binding/binding.d.ts +++ b/crates/node_binding/binding.d.ts @@ -742,6 +742,7 @@ export interface RawCopyPattern { priority: number globOptions: RawCopyGlobOptions info?: RawInfo + transform?: (input: string, absoluteFilename: string) => string | Buffer } export interface RawCopyRspackPluginOptions { diff --git a/crates/rspack_binding_options/src/options/raw_builtins/raw_copy.rs b/crates/rspack_binding_options/src/options/raw_builtins/raw_copy.rs index f4521a7b121..f4a783e2339 100644 --- a/crates/rspack_binding_options/src/options/raw_builtins/raw_copy.rs +++ b/crates/rspack_binding_options/src/options/raw_builtins/raw_copy.rs @@ -1,12 +1,19 @@ use std::path::PathBuf; +use derivative::Derivative; +use napi::{bindgen_prelude::Buffer, Either}; use napi_derive::napi; +use rspack_core::rspack_sources::RawSource; +use rspack_napi::threadsafe_function::ThreadsafeFunction; use rspack_plugin_copy::{ - CopyGlobOptions, CopyPattern, CopyRspackPluginOptions, Info, Related, ToType, + CopyGlobOptions, CopyPattern, CopyRspackPluginOptions, Info, Related, ToType, Transformer, }; -#[derive(Debug, Clone)] -#[napi(object)] +type RawTransformer = ThreadsafeFunction<(String, String), Either>; + +#[derive(Derivative)] +#[derivative(Debug, Clone)] +#[napi(object, object_to_js = false)] pub struct RawCopyPattern { pub from: String, pub to: Option, @@ -17,6 +24,9 @@ pub struct RawCopyPattern { pub priority: i32, pub glob_options: RawCopyGlobOptions, pub info: Option, + #[derivative(Debug = "ignore")] + #[napi(ts_type = "(input: string, absoluteFilename: string) => string | Buffer")] + pub transform: Option, } #[derive(Debug, Clone)] @@ -47,7 +57,7 @@ pub struct RawCopyGlobOptions { } #[derive(Debug)] -#[napi(object)] +#[napi(object, object_to_js = false)] pub struct RawCopyRspackPluginOptions { pub patterns: Vec, } @@ -64,6 +74,7 @@ impl From for CopyPattern { priority, glob_options, info, + transform, } = value; Self { @@ -97,6 +108,24 @@ impl From for CopyPattern { .collect() }), }, + transform: transform.map(|transformer| { + Transformer::Fn(Box::new(move |input, absolute_filename| { + let f = transformer.clone(); + + fn convert_to_enum(input: Either) -> RawSource { + match input { + Either::A(s) => RawSource::Source(s), + Either::B(b) => RawSource::Buffer(b.to_vec()), + } + } + + Box::pin(async move { + f.call((input.to_owned(), absolute_filename.to_owned())) + .await + .map(|i| convert_to_enum(i)) + }) + })) + }), } } } diff --git a/crates/rspack_plugin_copy/Cargo.toml b/crates/rspack_plugin_copy/Cargo.toml index a3df34673d9..92cd68732b8 100644 --- a/crates/rspack_plugin_copy/Cargo.toml +++ b/crates/rspack_plugin_copy/Cargo.toml @@ -7,6 +7,8 @@ version = "0.1.0" [dependencies] dashmap = { workspace = true } +derivative = { workspace = true } +futures = { workspace = true } glob = { workspace = true } lazy_static = "1.4.0" pathdiff = { workspace = true } diff --git a/crates/rspack_plugin_copy/src/lib.rs b/crates/rspack_plugin_copy/src/lib.rs index fd6bd643f43..bbf850343de 100644 --- a/crates/rspack_plugin_copy/src/lib.rs +++ b/crates/rspack_plugin_copy/src/lib.rs @@ -8,6 +8,8 @@ use std::{ }; use dashmap::DashSet; +use derivative::Derivative; +use futures::future::BoxFuture; use glob::{MatchOptions, Pattern as GlobPattern}; use regex::Regex; use rspack_core::{ @@ -20,7 +22,7 @@ use rspack_hook::{plugin, plugin_hook}; use rspack_util::infallible::ResultInfallibleExt as _; use sugar_path::SugarPath; -#[derive(Debug, Clone)] +#[derive(Debug)] pub struct CopyRspackPluginOptions { pub patterns: Vec, } @@ -66,7 +68,15 @@ impl Display for ToType { } } -#[derive(Debug, Clone)] +pub type TransformerFn = + Box Fn(String, &'a str) -> BoxFuture<'a, Result> + Sync + Send>; + +pub enum Transformer { + Fn(TransformerFn), +} + +#[derive(Derivative)] +#[derivative(Debug)] pub struct CopyPattern { pub from: String, pub to: Option, @@ -77,6 +87,8 @@ pub struct CopyPattern { pub force: bool, pub priority: i32, pub glob_options: CopyGlobOptions, + #[derivative(Debug = "ignore")] + pub transform: Option, } #[derive(Debug, Clone)] @@ -227,7 +239,7 @@ impl CopyRspackPlugin { logger.debug(format!("reading '{}'...", absolute_filename.display())); // TODO inputFileSystem - let source = match tokio::fs::read(absolute_filename.clone()).await { + let mut source = match tokio::fs::read(absolute_filename.clone()).await { Ok(data) => { logger.debug(format!("read '{}'...", absolute_filename.display())); @@ -244,6 +256,23 @@ impl CopyRspackPlugin { } }; + if let Some(transform) = &pattern.transform { + if let Some(absolute_filename) = absolute_filename.to_str() { + let content = match source { + RawSource::Source(code) => code, + RawSource::Buffer(buffer) => String::from_utf8(buffer).unwrap(), + }; + + match transform { + Transformer::Fn(transformer) => { + source = transformer(content, absolute_filename) + .await + .expect("run copy transformer error"); + } + } + } + } + let filename = if matches!(&to_type, ToType::Template) { logger.log(format!( "interpolating template '{}' for '${}'...`", @@ -300,15 +329,25 @@ impl CopyRspackPlugin { ) -> Option>> { let orig_from = &pattern.from; let normalized_orig_from = PathBuf::from(orig_from); - let mut context = pattern - .context + + let pattern_context = if pattern.context.is_none() { + Some(compilation.options.context.as_path().into()) + } else if let Some(ctx) = pattern.context.clone() + && !ctx.is_absolute() + { + Some(compilation.options.context.as_path().join(ctx)) + } else { + pattern.context.clone() + }; + + let mut context = pattern_context .clone() .unwrap_or(compilation.options.context.as_path().to_path_buf()); logger.log(format!( "starting to process a pattern from '{}' using '{:?}' context", normalized_orig_from.display(), - pattern.context.as_ref().map(|p| p.display()) + pattern_context.as_ref().map(|p| p.display()) )); let abs_from = if normalized_orig_from.is_absolute() { @@ -520,15 +559,6 @@ async fn process_assets(&self, compilation: &mut Compilation) -> Result<()> { .iter() .enumerate() .map(|(index, pattern)| { - let mut pattern = pattern.clone(); - if pattern.context.is_none() { - pattern.context = Some(compilation.options.context.as_path().into()); - } else if let Some(ctx) = pattern.context.clone() - && !ctx.is_absolute() - { - pattern.context = Some(compilation.options.context.as_path().join(ctx)) - }; - CopyRspackPlugin::run_patter( compilation, &pattern, diff --git a/plugin-test/copy-plugin/CopyPlugin.test.js b/plugin-test/copy-plugin/CopyPlugin.test.js index 3f7771d32e6..c152c506ce3 100644 --- a/plugin-test/copy-plugin/CopyPlugin.test.js +++ b/plugin-test/copy-plugin/CopyPlugin.test.js @@ -429,6 +429,39 @@ describe("CopyPlugin", () => { ); }); }); + + it("should work with transform fn", async () => { + const compiler = rspack([ + { + mode: "development", + context: path.resolve(__dirname, "./fixtures"), + plugins: [ + new rspack.CopyRspackPlugin({ + patterns: [ + { + from: path.resolve(__dirname, "./fixtures/directory"), + transform: source => { + return source + "transform aaaa"; + } + } + ] + }) + ], + entry: path.resolve(__dirname, "./helpers/enter.js"), + output: { + path: path.resolve(__dirname, "./outputs/dist/b") + } + } + ]); + + const { stats } = await compile(compiler); + + stats.stats.forEach((item, index) => { + expect(readAssets(compiler.compilers[index], item)).toMatchSnapshot( + "assets" + ); + }); + }); }); describe("watch mode", () => { diff --git a/plugin-test/copy-plugin/__snapshots__/CopyPlugin.test.js.snap b/plugin-test/copy-plugin/__snapshots__/CopyPlugin.test.js.snap index ef321ad3552..6961037e66b 100644 --- a/plugin-test/copy-plugin/__snapshots__/CopyPlugin.test.js.snap +++ b/plugin-test/copy-plugin/__snapshots__/CopyPlugin.test.js.snap @@ -28,6 +28,16 @@ exports[`CopyPlugin basic should work with multi compiler mode: warnings 1`] = ` exports[`CopyPlugin basic should work with multi compiler mode: warnings 2`] = `Array []`; +exports[`CopyPlugin basic should work with transform fn: assets 1`] = ` +Object { + ".dottedfile": "dottedfile contents +transform aaaa", + "directoryfile.txt": "newtransform aaaa", + "nested/deep-nested/deepnested.txt": "transform aaaa", + "nested/nestedfile.txt": "transform aaaa", +} +`; + exports[`CopyPlugin stats should minify: assets 1`] = ` Object { "asset-modules/deepnested.txt": "", diff --git a/website/docs/en/plugins/rspack/copy-rspack-plugin.mdx b/website/docs/en/plugins/rspack/copy-rspack-plugin.mdx index 60e027329ca..fa1292174ca 100644 --- a/website/docs/en/plugins/rspack/copy-rspack-plugin.mdx +++ b/website/docs/en/plugins/rspack/copy-rspack-plugin.mdx @@ -35,6 +35,10 @@ new rspack.CopyRspackPlugin(options); dot?: boolean; // Default to true ignore?: string[]; // ignore specific path }; + transform?: ( + input: string, + absoluteFilename: string, + ) => string | Buffer; } )[]; }; @@ -116,6 +120,12 @@ new rspack.CopyRspackPlugin(options); description: 'The configuration for glob queries: `caseSensitiveMatch` determines whether the matching is case sensitive, and `dot` determines whether files starting with . are matched. `ignore` is an array of strings in glob format that can be used to ignore specific paths.', }, + { + name: '`transform`', + type: '`function`', + default: 'undefined', + description: 'Allows to modify the file contents.', + }, ]} /> diff --git a/website/docs/zh/plugins/rspack/copy-rspack-plugin.mdx b/website/docs/zh/plugins/rspack/copy-rspack-plugin.mdx index 6c8563a2e3f..4358d2cac7d 100644 --- a/website/docs/zh/plugins/rspack/copy-rspack-plugin.mdx +++ b/website/docs/zh/plugins/rspack/copy-rspack-plugin.mdx @@ -35,6 +35,10 @@ new rspack.CopyRspackPlugin(options); dot?: boolean; // 默认 true ignore?: string[]; // 忽略特定路径 }; + transform?: ( + input: string, + absoluteFilename: string, + ) => string | Buffer; } )[]; }; @@ -115,6 +119,12 @@ new rspack.CopyRspackPlugin(options); description: 'glob 查询选项,`caseSensitiveMatch` 决定是否大小写敏感,`dot` 决定是否匹配以 `.` 开头的文件,`ignore` 是 glob 形式的字符串数组,可以使用该配置忽略部分特定路径。', }, + { + name: '`transform`', + type: '`function`', + default: 'undefined', + description: '允许修改文件内容。', + }, ]} />