Skip to content

Commit 25a4021

Browse files
authored
feat(rstest): add preserveNewUrl option to keep new URL untouched (#12632)
* feat(rstest): add preserveNewUrl option to keep new URL untouched * fix: format Rust code with rustfmt
1 parent 44a7564 commit 25a4021

File tree

11 files changed

+138
-0
lines changed

11 files changed

+138
-0
lines changed

crates/node_binding/napi-binding.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2790,6 +2790,7 @@ export interface RawRstestPluginOptions {
27902790
importMetaPathName: boolean
27912791
hoistMockModule: boolean
27922792
manualMockRoot: string
2793+
preserveNewUrl?: Array<string>
27932794
}
27942795

27952796
export interface RawRuleSetCondition {

crates/rspack_binding_api/src/rstest.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@ pub struct RawRstestPluginOptions {
1212
pub hoist_mock_module: bool,
1313
// Root of the manual mock directory.
1414
pub manual_mock_root: String,
15+
// Preserve `new URL("*.<ext>", import.meta.url)` expressions for specified extensions
16+
// instead of transforming them to asset imports.
17+
// This allows rstest to dynamically load modules (e.g., wasm) at runtime.
18+
// Example: [".wasm"] to preserve wasm URL expressions.
19+
pub preserve_new_url: Option<Vec<String>>,
1520
}
1621

1722
impl From<RawRstestPluginOptions> for RstestPluginOptions {
@@ -21,6 +26,7 @@ impl From<RawRstestPluginOptions> for RstestPluginOptions {
2126
hoist_mock_module: value.hoist_mock_module,
2227
import_meta_path_name: value.import_meta_path_name,
2328
manual_mock_root: value.manual_mock_root,
29+
preserve_new_url: value.preserve_new_url.unwrap_or_default(),
2430
}
2531
}
2632
}

crates/rspack_plugin_rstest/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,6 @@ mod mock_module_id_dependency;
44
mod module_path_name_dependency;
55
mod parser_plugin;
66
mod plugin;
7+
mod url_dependency;
78

89
pub use plugin::*;

crates/rspack_plugin_rstest/src/plugin.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ use crate::{
1919
mock_method_dependency::MockMethodDependencyTemplate,
2020
mock_module_id_dependency::MockModuleIdDependencyTemplate,
2121
module_path_name_dependency::ModulePathNameDependencyTemplate, parser_plugin::RstestParserPlugin,
22+
url_dependency::RstestUrlDependencyTemplate,
2223
};
2324

2425
#[derive(Debug)]
@@ -27,6 +28,7 @@ pub struct RstestPluginOptions {
2728
pub hoist_mock_module: bool,
2829
pub import_meta_path_name: bool,
2930
pub manual_mock_root: String,
31+
pub preserve_new_url: Vec<String>,
3032
}
3133

3234
#[derive(Debug)]
@@ -148,6 +150,15 @@ async fn compilation_stage_9999(
148150
Arc::new(ImportDependencyTemplate::default()),
149151
);
150152

153+
if !self.options.preserve_new_url.is_empty() {
154+
compilation.set_dependency_template(
155+
RstestUrlDependencyTemplate::template_type(),
156+
Arc::new(RstestUrlDependencyTemplate::new(
157+
self.options.preserve_new_url.clone(),
158+
)),
159+
);
160+
}
161+
151162
Ok(())
152163
}
153164

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
use rspack_cacheable::cacheable;
2+
use rspack_core::{
3+
DependencyCodeGeneration, DependencyTemplate, DependencyTemplateType, DependencyType,
4+
ModuleDependency, TemplateContext, TemplateReplaceSource,
5+
};
6+
use rspack_plugin_javascript::dependency::{URLDependency, URLDependencyTemplate};
7+
8+
#[cacheable]
9+
#[derive(Debug, Default)]
10+
pub struct RstestUrlDependencyTemplate {
11+
/// List of extensions to preserve (e.g., [".wasm", ".node"])
12+
preserve_extensions: Vec<String>,
13+
}
14+
15+
impl RstestUrlDependencyTemplate {
16+
pub fn new(preserve_extensions: Vec<String>) -> Self {
17+
Self {
18+
preserve_extensions,
19+
}
20+
}
21+
22+
pub fn template_type() -> DependencyTemplateType {
23+
DependencyTemplateType::Dependency(DependencyType::NewUrl)
24+
}
25+
}
26+
27+
impl DependencyTemplate for RstestUrlDependencyTemplate {
28+
fn render(
29+
&self,
30+
dep: &dyn DependencyCodeGeneration,
31+
source: &mut TemplateReplaceSource,
32+
code_generatable_context: &mut TemplateContext,
33+
) {
34+
let dep = dep
35+
.as_any()
36+
.downcast_ref::<URLDependency>()
37+
.expect("RstestUrlDependencyTemplate should be used for URLDependency");
38+
39+
// Strip query string and fragment from request path before checking extension
40+
let request = dep.request();
41+
let request_path = request.split(&['?', '#'][..]).next().unwrap_or(request);
42+
43+
let should_preserve = request_path.rsplit('.').next().is_some_and(|ext| {
44+
self.preserve_extensions.iter().any(|preserve_ext| {
45+
// Support both ".ext" and "ext" formats
46+
let preserve_ext = preserve_ext.trim_start_matches('.');
47+
ext.eq_ignore_ascii_case(preserve_ext)
48+
})
49+
});
50+
51+
if should_preserve {
52+
return;
53+
}
54+
55+
URLDependencyTemplate::default().render(dep, source, code_generatable_context);
56+
}
57+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
const wasmUrl = new URL("./test.wasm", import.meta.url);
2+
const pngUrl = new URL("./test.png", import.meta.url);
3+
4+
console.log(wasmUrl, pngUrl);
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
const {
2+
experiments: { RstestPlugin }
3+
} = require("@rspack/core");
4+
5+
/** @type {import("@rspack/core").Configuration} */
6+
module.exports = [
7+
{
8+
entry: "./index.js",
9+
target: "node",
10+
node: {
11+
__filename: false,
12+
__dirname: false
13+
},
14+
output: {
15+
filename: "bundle.js"
16+
},
17+
plugins: [
18+
new RstestPlugin({
19+
injectModulePathName: true,
20+
hoistMockModule: true,
21+
importMetaPathName: true,
22+
manualMockRoot: __dirname,
23+
preserveNewUrl: [".wasm"]
24+
})
25+
]
26+
},
27+
{
28+
entry: {
29+
main: "./test.js"
30+
},
31+
output: {
32+
filename: "[name].js"
33+
},
34+
externalsPresets: {
35+
node: true
36+
}
37+
}
38+
];
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
/** @type {import("../../../..").TConfigCaseConfig} */
2+
module.exports = {
3+
findBundle: function (i) {
4+
if (i === 0) return ["main.js"];
5+
return ["main.js"];
6+
}
7+
};
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
const fs = require("fs");
2+
const path = require("path");
3+
4+
const content = fs.readFileSync(path.resolve(__dirname, "bundle.js"), "utf-8");
5+
6+
it("should keep wasm new URL untouched in rstest", () => {
7+
expect(content).toContain('new URL("./test.wasm", import.meta.url)');
8+
});
9+
10+
it("should keep non-wasm new URL behavior", () => {
11+
expect(content).toContain("/* asset import */");
12+
});
Lines changed: 1 addition & 0 deletions
Loading

0 commit comments

Comments
 (0)