Skip to content
Draft
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
4 changes: 2 additions & 2 deletions crates/node_binding/napi-binding.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1732,7 +1732,7 @@ export interface RawAssetGeneratorOptions {
outputPath?: JsFilename
publicPath?: "auto" | JsFilename
dataUrl?: RawAssetGeneratorDataUrlOptions | ((source: Buffer, context: RawAssetGeneratorDataUrlFnCtx) => string)
importMode?: "url" | "preserve"
importMode?: "url" | "preserve" | "newURL"
binary?: boolean
}

Expand All @@ -1759,7 +1759,7 @@ export interface RawAssetResourceGeneratorOptions {
filename?: JsFilename
outputPath?: JsFilename
publicPath?: "auto" | JsFilename
importMode?: "url" | "preserve"
importMode?: "url" | "preserve" | "newURL"
binary?: boolean
}

Expand Down
4 changes: 2 additions & 2 deletions crates/rspack_binding_api/src/raw_options/raw_module/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -575,7 +575,7 @@ pub struct RawAssetGeneratorOptions {
)]
pub data_url: Option<RawAssetGeneratorDataUrl>,

#[napi(ts_type = r#""url" | "preserve""#)]
#[napi(ts_type = r#""url" | "preserve" | "newURL""#)]
pub import_mode: Option<String>,
pub binary: Option<bool>,
}
Expand Down Expand Up @@ -626,7 +626,7 @@ pub struct RawAssetResourceGeneratorOptions {
pub output_path: Option<JsFilename>,
#[napi(ts_type = "\"auto\" | JsFilename")]
pub public_path: Option<JsFilename>,
#[napi(ts_type = r#""url" | "preserve""#)]
#[napi(ts_type = r#""url" | "preserve" | "newURL""#)]
pub import_mode: Option<String>,
pub binary: Option<bool>,
}
Expand Down
7 changes: 6 additions & 1 deletion crates/rspack_core/src/options/module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -504,6 +504,7 @@ bitflags! {
impl AssetGeneratorImportModeFlags: u8 {
const URL = 1 << 0;
const PRESERVE = 1 << 1;
const NEW_URL = 1 << 2;
}
}

Expand All @@ -518,14 +519,18 @@ impl AssetGeneratorImportMode {
pub fn is_preserve(&self) -> bool {
self.0.contains(AssetGeneratorImportModeFlags::PRESERVE)
}
pub fn is_new_url(&self) -> bool {
self.0.contains(AssetGeneratorImportModeFlags::NEW_URL)
}
}

impl From<String> for AssetGeneratorImportMode {
fn from(s: String) -> Self {
match s.as_str() {
"url" => Self(AssetGeneratorImportModeFlags::URL),
"preserve" => Self(AssetGeneratorImportModeFlags::PRESERVE),
_ => unreachable!("AssetGeneratorImportMode error"),
"newURL" => Self(AssetGeneratorImportModeFlags::NEW_URL),
_ => unreachable!("asset generator import mode should be url, preserve or newURL"),
}
}
}
Expand Down
72 changes: 52 additions & 20 deletions crates/rspack_plugin_asset/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -562,7 +562,7 @@ impl ParserAndGenerator for AssetParserAndGenerator {
)
.await?;

let asset_path = if import_mode.is_preserve() {
let asset_path = if import_mode.is_preserve() || import_mode.is_new_url() {
generate_context
.data
.insert(CodeGenerationPublicPathAutoReplace(true));
Expand Down Expand Up @@ -631,37 +631,69 @@ impl ParserAndGenerator for AssetParserAndGenerator {
return Ok(RawStringSource::from("").boxed());
}

if import_mode.is_preserve() && parsed_asset_config.is_resource() {
let is_module = compilation.options.output.module;
if let Some(ref mut scope) = generate_context.concatenation_scope {
scope.register_namespace_export(NAMESPACE_OBJECT_EXPORT);
if is_module {
if parsed_asset_config.is_resource() {
if import_mode.is_preserve() {
let is_module = compilation.options.output.module;
if let Some(ref mut scope) = generate_context.concatenation_scope {
scope.register_namespace_export(NAMESPACE_OBJECT_EXPORT);
if is_module {
return Ok(
RawStringSource::from(format!(
r#"import {NAMESPACE_OBJECT_EXPORT} from {exported_content};"#
))
.boxed(),
);
} else {
let supports_const = compilation.options.output.environment.supports_const();
let declaration_kind = if supports_const { "const" } else { "var" };
return Ok(
RawStringSource::from(format!(
r#"{declaration_kind} {NAMESPACE_OBJECT_EXPORT} = require({exported_content});"#
))
.boxed(),
);
}
} else {
generate_context
.runtime_requirements
.insert(RuntimeGlobals::MODULE);
return Ok(
RawStringSource::from(format!(
r#"import {NAMESPACE_OBJECT_EXPORT} from {exported_content};"#
))
.boxed(),
RawStringSource::from(format!(r#"module.exports = require({exported_content});"#))
.boxed(),
);
}
};

if import_mode.is_new_url() {
let supports_arrow_function = compilation
.options
.output
.environment
.supports_arrow_function();
let url_function_call = if supports_arrow_function {
format!(r#"/*#__PURE__*/(()=>new URL({exported_content}, import.meta.url).href)()"#)
} else {
format!(
r#"/*#__PURE__*/(function(){{return new URL({exported_content}, import.meta.url).href}})()"#
)
};
if let Some(ref mut scope) = generate_context.concatenation_scope {
let supports_const = compilation.options.output.environment.supports_const();
let declaration_kind = if supports_const { "const" } else { "var" };
scope.register_namespace_export(NAMESPACE_OBJECT_EXPORT);
return Ok(
RawStringSource::from(format!(
r#"{declaration_kind} {NAMESPACE_OBJECT_EXPORT} = require({exported_content});"#
r#"{declaration_kind} {NAMESPACE_OBJECT_EXPORT} = {url_function_call};"#
))
.boxed(),
);
} else {
return Ok(
RawStringSource::from(format!(r#"module.exports = {url_function_call};"#)).boxed(),
);
}
} else {
generate_context
.runtime_requirements
.insert(RuntimeGlobals::MODULE);
return Ok(
RawStringSource::from(format!(r#"module.exports = require({exported_content});"#))
.boxed(),
);
}
};
}

if let Some(ref mut scope) = generate_context.concatenation_scope {
scope.register_namespace_export(NAMESPACE_OBJECT_EXPORT);
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 @@ -237,7 +237,7 @@ export type AssetInlineGeneratorOptions = {
export type AssetModuleFilename = Filename;

// @public
export type AssetModuleImportMode = "url" | "preserve";
export type AssetModuleImportMode = "url" | "preserve" | "newURL";

// @public
export type AssetModuleOutputPath = Filename;
Expand Down
3 changes: 2 additions & 1 deletion packages/rspack/src/config/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1202,7 +1202,7 @@ export type AssetModuleOutputPath = Filename;
* Only for modules with module type 'asset' or 'asset/resource'.
* @default "url"
*/
export type AssetModuleImportMode = "url" | "preserve";
export type AssetModuleImportMode = "url" | "preserve" | "newURL";

/** Options for asset modules. */
export type AssetResourceGeneratorOptions = {
Expand All @@ -1224,6 +1224,7 @@ export type AssetResourceGeneratorOptions = {
/**
* If "url", a URL pointing to the asset will be generated based on publicPath.
* If "preserve", preserve import/require statement from generated asset.
* If "newURL", a new URL object will be created for the asset.
* Only for modules with module type 'asset' or 'asset/resource'.
* @default "url"
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
const { rspack } = require("@rspack/core");
const assert = require("assert");

/** @type {import("@rspack/core").Configuration} */
module.exports = {
context: __dirname,
entry: {
index: "./img.png"
},
module: {
rules: [
{
test: /\.png$/,
type: "asset/resource",
generator: {
importMode: "newURL"
}
}
],
},
plugins: [
new rspack.BannerPlugin({
banner: 'var __import__meta__url__ = {};',
stage: 0
}),
new rspack.DefinePlugin({
'import.meta.url': '__import__meta__url__'
}),
new (class {
apply(compiler) {
compiler.hooks.compilation.tap("MyPlugin", compilation => {
compilation.hooks.processAssets.tap("MyPlugin", assets => {
let list = Object.keys(assets);
const js = list.find(item => item.endsWith("js"));
const jsContent = assets[js].source().toString();
console.log(jsContent, 22222222)

assert(/new URL\(['"]\.\/(\w*)\.png['"], import\.meta\.url\)/.test(jsContent));
});
});
}
})()
]
};
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
exports.noTests = true;
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
const assert = require("assert");

/** @type {import("@rspack/core").Configuration} */
module.exports = {
context: __dirname,
entry: {
index: "./img.png"
},
output: {
filename: `[name].js`,
chunkFilename: `async.js`,
module: true,
library: {
type: "modern-module"
},
iife: false,
chunkFormat: "module",
chunkLoading: "import"
},
module: {
rules: [
{
test: /\.png$/,
type: "asset/resource",
generator: {
filename: "static/img/[name].png",
importMode: "newURL"
}
}
]
},
experiments: {
outputModule: true
},
optimization: {
concatenateModules: true,
avoidEntryIife: true,
minimize: false
},
plugins: [
new (class {
apply(compiler) {
compiler.hooks.compilation.tap("MyPlugin", compilation => {
compilation.hooks.processAssets.tap("MyPlugin", assets => {
let list = Object.keys(assets);
const js = list.find(item => item.endsWith("js"));
const jsContent = assets[js].source().toString();

const referenceWithNewURL =
/const\simg_namespaceObject\s=.*new\sURL\(['"]\.\/static\/img\/img\.png['"]/.test(
jsContent
);

assert(referenceWithNewURL);
const hasExports =
/export\s{\simg_namespaceObject\sas\sdefault\s}/.test(jsContent);
assert(hasExports);
});
});
}
})()
]
};
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
exports.noTests = true;
Loading