Skip to content

Commit

Permalink
feat: support generator.dataUrl as function (#5841)
Browse files Browse the repository at this point in the history
  • Loading branch information
luhc228 authored Mar 13, 2024
1 parent 822698f commit 079e993
Show file tree
Hide file tree
Showing 20 changed files with 234 additions and 111 deletions.
8 changes: 7 additions & 1 deletion crates/node_binding/binding.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -603,8 +603,14 @@ export interface RawAliasOptionItem {
}

export interface RawAssetGeneratorDataUrl {
type: "options"
type: "options"| "function"
options?: RawAssetGeneratorDataUrlOptions
function?: (options: { content: string, filename: string }) => string
}

export interface RawAssetGeneratorDataUrlFnArgs {
filename: string
content: string
}

export interface RawAssetGeneratorDataUrlOptions {
Expand Down
73 changes: 63 additions & 10 deletions crates/rspack_binding_options/src/options/raw_module/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@ use napi::bindgen_prelude::Either3;
use napi::Either;
use napi_derive::napi;
use rspack_core::{
AssetGeneratorDataUrl, AssetGeneratorDataUrlOptions, AssetGeneratorOptions,
AssetInlineGeneratorOptions, AssetParserDataUrl, AssetParserDataUrlOptions, AssetParserOptions,
AssetResourceGeneratorOptions, BoxLoader, DescriptionData, DynamicImportMode, FuncUseCtx,
GeneratorOptions, GeneratorOptionsByModuleType, JavascriptParserOptions, JavascriptParserOrder,
JavascriptParserUrl, ModuleNoParseRule, ModuleNoParseRules, ModuleNoParseTestFn, ModuleOptions,
ModuleRule, ModuleRuleEnforce, ModuleRuleUse, ModuleRuleUseLoader, ModuleType, ParserOptions,
ParserOptionsByModuleType,
AssetGeneratorDataUrl, AssetGeneratorDataUrlFnArgs, AssetGeneratorDataUrlOptions,
AssetGeneratorOptions, AssetInlineGeneratorOptions, AssetParserDataUrl,
AssetParserDataUrlOptions, AssetParserOptions, AssetResourceGeneratorOptions, BoxLoader,
DescriptionData, DynamicImportMode, FuncUseCtx, GeneratorOptions, GeneratorOptionsByModuleType,
JavascriptParserOptions, JavascriptParserOrder, JavascriptParserUrl, ModuleNoParseRule,
ModuleNoParseRules, ModuleNoParseTestFn, ModuleOptions, ModuleRule, ModuleRuleEnforce,
ModuleRuleUse, ModuleRuleUseLoader, ModuleType, ParserOptions, ParserOptionsByModuleType,
};
use rspack_error::error;
use rspack_loader_react_refresh::REACT_REFRESH_LOADER_IDENTIFIER;
Expand Down Expand Up @@ -473,14 +473,42 @@ impl From<RawAssetResourceGeneratorOptions> for AssetResourceGeneratorOptions {
}
}

#[derive(Debug, Deserialize, Default)]
#[derive(Deserialize, Default)]
#[serde(rename_all = "camelCase")]
#[napi(object)]
pub struct RawAssetGeneratorDataUrl {
#[napi(ts_type = r#""options""#)]
#[napi(ts_type = r#""options"| "function""#)]
pub r#type: String,
pub options: Option<RawAssetGeneratorDataUrlOptions>,
// TODO: pub function
#[serde(skip_deserializing)]
#[napi(ts_type = r#"(options: { content: string, filename: string }) => string"#)]
pub function: Option<JsFunction>,
}

#[derive(Debug, Deserialize, Clone)]
#[serde(rename_all = "camelCase")]
#[napi(object)]
pub struct RawAssetGeneratorDataUrlFnArgs {
pub filename: String,
pub content: String,
}

impl From<AssetGeneratorDataUrlFnArgs> for RawAssetGeneratorDataUrlFnArgs {
fn from(value: AssetGeneratorDataUrlFnArgs) -> Self {
Self {
filename: value.filename,
content: value.content,
}
}
}
impl Debug for RawAssetGeneratorDataUrl {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("RawAssetGeneratorDataUrl")
.field("r#type", &self.r#type)
.field("options", &self.options)
.field("function", &"...")
.finish()
}
}

impl From<RawAssetGeneratorDataUrl> for AssetGeneratorDataUrl {
Expand All @@ -492,6 +520,31 @@ impl From<RawAssetGeneratorDataUrl> for AssetGeneratorDataUrl {
.expect("should have an \"options\" when RawAssetGeneratorDataUrl.type is \"options\"")
.into(),
),
"function" => {
let func = value
.function
.expect("should have a function when RawAssetGeneratorDataUrl.type is \"function\"");

let env = get_napi_env();

let func: napi::Result<ThreadsafeFunction<RawAssetGeneratorDataUrlFnArgs, String>> =
try { rspack_binding_macros::js_fn_into_threadsafe_fn!(func, &Env::from(env)) };

Self::Func(Arc::new(move |value: AssetGeneratorDataUrlFnArgs| {
let func = func
.clone()
.expect("Can't clone RawAssetGeneratorDataUrl.type as function");
Ok(
func
.call(value.into(), ThreadsafeFunctionCallMode::NonBlocking)
.into_rspack_result()
.expect("The result of dataUrl function into rspack result failed")
.blocking_recv()
.unwrap_or_else(|err| panic!("Failed to call external function: {err}"))
.expect("call dataUrl function failed"),
)
}))
}
_ => panic!(
"Failed to resolve the RawAssetGeneratorDataUrl.type {}. Expected type is `options`.",
value.r#type
Expand Down
34 changes: 31 additions & 3 deletions crates/rspack_core/src/options/module.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
use std::fmt::{self, Debug};
use std::{
fmt::{self, Debug},
sync::Arc,
};

use async_recursion::async_recursion;
use derivative::Derivative;
Expand Down Expand Up @@ -246,10 +249,35 @@ pub struct AssetGeneratorOptions {
pub data_url: Option<AssetGeneratorDataUrl>,
}

#[derive(Debug, Clone)]
pub struct AssetGeneratorDataUrlFnArgs {
pub filename: String,
pub content: String,
}

pub type AssetGeneratorDataUrlFn =
Arc<dyn Fn(AssetGeneratorDataUrlFnArgs) -> Result<String> + Sync + Send>;

pub enum AssetGeneratorDataUrl {
Options(AssetGeneratorDataUrlOptions),
// TODO: Function
Func(AssetGeneratorDataUrlFn),
}

impl fmt::Debug for AssetGeneratorDataUrl {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Options(i) => i.fmt(f),
Self::Func(_) => "Func(...)".fmt(f),
}
}
}

impl Clone for AssetGeneratorDataUrl {
fn clone(&self) -> Self {
match self {
Self::Options(i) => Self::Options(i.clone()),
Self::Func(i) => Self::Func(i.clone()),
}
}
}

#[derive(Debug, Clone)]
Expand Down
60 changes: 42 additions & 18 deletions crates/rspack_plugin_asset/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@ use rspack_core::{
tree_shaking::{
analyzer::OptimizeAnalyzer, asset_module::AssetModule, visitor::OptimizeAnalyzeResult,
},
AssetGeneratorDataUrl, AssetParserDataUrl, BuildExtraDataType, BuildMetaDefaultObject,
BuildMetaExportsType, CodeGenerationDataAssetInfo, CodeGenerationDataFilename,
CodeGenerationDataUrl, Compilation, CompilerOptions, GenerateContext, Module, ModuleType,
NormalModule, ParseContext, ParserAndGenerator, PathData, Plugin, PluginContext,
PluginRenderManifestHookOutput, RenderManifestArgs, RenderManifestEntry, ResourceData,
RuntimeGlobals, SourceType, NAMESPACE_OBJECT_EXPORT,
AssetGeneratorDataUrl, AssetGeneratorDataUrlFnArgs, AssetParserDataUrl, BuildExtraDataType,
BuildMetaDefaultObject, BuildMetaExportsType, CodeGenerationDataAssetInfo,
CodeGenerationDataFilename, CodeGenerationDataUrl, Compilation, CompilerOptions, GenerateContext,
Module, ModuleType, NormalModule, ParseContext, ParserAndGenerator, PathData, Plugin,
PluginContext, PluginRenderManifestHookOutput, RenderManifestArgs, RenderManifestEntry,
ResourceData, RuntimeGlobals, SourceType, NAMESPACE_OBJECT_EXPORT,
};
use rspack_error::{error, IntoTWithDiagnosticArray, Result};
use rspack_hash::{RspackHash, RspackHashDigest};
Expand Down Expand Up @@ -109,6 +109,23 @@ impl AssetParserAndGenerator {
hasher.digest(&compiler_options.output.hash_digest)
}

fn get_data_url(
&self,
resource_data: &ResourceData,
data_url: Option<&AssetGeneratorDataUrl>,
source: &BoxSource,
) -> Option<String> {
let func_args = AssetGeneratorDataUrlFnArgs {
filename: resource_data.resource_path.to_string_lossy().to_string(),
content: source.source().into_owned().to_string(),
};

if let Some(AssetGeneratorDataUrl::Func(data_url)) = data_url {
return Some(data_url(func_args).expect("call data_url function failed"));
}
None
}

fn get_mimetype(
&self,
resource_data: &ResourceData,
Expand Down Expand Up @@ -325,21 +342,28 @@ impl ParserAndGenerator for AssetParserAndGenerator {
let result = match generate_context.requested_source_type {
SourceType::JavaScript => {
let exported_content = if parsed_asset_config.is_inline() {
let resource_data = normal_module.resource_resolved_data();
let resource_data: &ResourceData = normal_module.resource_resolved_data();
let data_url = generate_context
.module_generator_options
.and_then(|x| x.asset_data_url(module_type));
let mimetype = self.get_mimetype(resource_data, data_url)?;
let encoding = self.get_encoding(resource_data, data_url);
let encoded_content = self.get_encoded_content(resource_data, &encoding, source)?;
let encoded_source = format!(
r#"data:{mimetype}{},{encoded_content}"#,
if encoding.is_empty() {
String::new()
} else {
format!(";{encoding}")
}
);

let encoded_source: String;

if let Some(custom_data_url) = self.get_data_url(resource_data, data_url, source) {
encoded_source = custom_data_url;
} else {
let mimetype = self.get_mimetype(resource_data, data_url)?;
let encoding = self.get_encoding(resource_data, data_url);
let encoded_content = self.get_encoded_content(resource_data, &encoding, source)?;
encoded_source = format!(
r#"data:{mimetype}{},{encoded_content}"#,
if encoding.is_empty() {
String::new()
} else {
format!(";{encoding}")
}
);
}

generate_context
.data
Expand Down
2 changes: 1 addition & 1 deletion packages/rspack-plugin-minify/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ module.exports = class RspackMinifyPlugin {
sourceAsString,
map,
true
)
)
: new RawSource(result.code),
{
...asset.info,
Expand Down
4 changes: 2 additions & 2 deletions packages/rspack-test-tools/src/case/watch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,13 +57,13 @@ const creator = new WatchCaseCreator({
stepName: run.name,
tempDir: temp!,
runable: true
})
})
: new RspackWatchStepProcessor({
name,
stepName: run.name,
tempDir: temp!,
runable: true
})
})
);
}
});
Expand Down
4 changes: 2 additions & 2 deletions packages/rspack-test-tools/src/processor/normal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ export class RspackNormalProcessor extends BasicTaskProcessor<ECompilerType.Rspa
// emitOnErrors: true,
minimizer: [terserForTesting],
...testConfig.optimization
}
}
: {
removeAvailableModules: true,
removeEmptyChunks: true,
Expand All @@ -87,7 +87,7 @@ export class RspackNormalProcessor extends BasicTaskProcessor<ECompilerType.Rspa
chunkIds: "named",
minimizer: [terserForTesting],
...compilerOptions?.optimization
},
},
// CHANGE: rspack does not support `performance` yet.
// performance: {
// hints: false
Expand Down
8 changes: 3 additions & 5 deletions packages/rspack-test-tools/src/runner/runner/basic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -155,11 +155,9 @@ export class BasicRunner<T extends ECompilerType = ECompilerType.Rspack>
if (modules && modulePathStr in modules) {
return modules[modulePathStr];
} else {
return require(
modulePathStr.startsWith("node:")
? modulePathStr.slice(5)
: modulePathStr
);
return require(modulePathStr.startsWith("node:")
? modulePathStr.slice(5)
: modulePathStr);
}
};
}
Expand Down
6 changes: 3 additions & 3 deletions packages/rspack/src/builtin-plugin/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,8 @@ function resolveTreeShaking(
return treeShaking !== undefined
? treeShaking.toString()
: production
? "true"
: "false";
? "true"
: "false";
}

export interface Builtins {
Expand All @@ -97,7 +97,7 @@ export function deprecated_resolveBuiltins(
...builtins.css?.modules
},
namedExports: builtins.css?.namedExports
}
}
: undefined,
treeShaking: resolveTreeShaking(builtins.treeShaking, production)
};
Expand Down
Loading

2 comments on commit 079e993

@rspack-bot
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

πŸ“ Benchmark detail: Open

task failure

@rspack-bot
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

πŸ“ Ran ecosystem CI: Open

suite result
modernjs ❌ failure
nx ❌ failure
rspress ❌ failure
rsbuild ❌ failure
compat ❌ failure
examples ❌ failure

Please sign in to comment.