diff --git a/.changeset/rude-beds-whisper.md b/.changeset/rude-beds-whisper.md new file mode 100644 index 000000000000..4e156479f2a5 --- /dev/null +++ b/.changeset/rude-beds-whisper.md @@ -0,0 +1,7 @@ +--- +"@rspack/binding": patch +"@rspack/core": patch +"@rspack/cli": patch +--- + +feat: resolve.byDependency diff --git a/crates/node_binding/binding.d.ts b/crates/node_binding/binding.d.ts index 879a171c12bb..9fe81f84602c 100644 --- a/crates/node_binding/binding.d.ts +++ b/crates/node_binding/binding.d.ts @@ -330,6 +330,7 @@ export interface RawResolveOptions { symlinks?: boolean tsConfigPath?: string modules?: Array + byDependency?: Record } export interface RawSnapshotStrategy { hash: boolean diff --git a/crates/rspack_binding_options/src/options/raw_resolve.rs b/crates/rspack_binding_options/src/options/raw_resolve.rs index 6f59b10d2c67..425964d16c75 100644 --- a/crates/rspack_binding_options/src/options/raw_resolve.rs +++ b/crates/rspack_binding_options/src/options/raw_resolve.rs @@ -1,7 +1,7 @@ use std::collections::HashMap; use napi_derive::napi; -use rspack_core::{Alias, AliasMap, Resolve}; +use rspack_core::{Alias, AliasMap, ByDependency, DependencyCategory, Resolve}; use serde::Deserialize; pub type AliasValue = serde_json::Value; @@ -26,6 +26,7 @@ pub struct RawResolveOptions { pub symlinks: Option, pub ts_config_path: Option, pub modules: Option>, + pub by_dependency: Option>, } fn normalize_alias(alias: Option) -> anyhow::Result> { @@ -76,6 +77,18 @@ impl TryFrom for Resolve { let fallback = normalize_alias(value.fallback)?; let modules = value.modules; let tsconfig = value.ts_config_path.map(std::path::PathBuf::from); + let by_dependency = value + .by_dependency + .map(|i| { + i.into_iter() + .map(|(k, v)| { + let v = v.try_into()?; + Ok((DependencyCategory::from(k.as_str()), v)) + }) + .collect::>() + }) + .transpose()?; + Ok(Resolve { modules, prefer_relative, @@ -88,6 +101,7 @@ impl TryFrom for Resolve { symlinks, tsconfig, fallback, + by_dependency, }) } } diff --git a/crates/rspack_core/src/compiler/resolver.rs b/crates/rspack_core/src/compiler/resolver.rs index 19765c8340ea..9430979a2708 100644 --- a/crates/rspack_core/src/compiler/resolver.rs +++ b/crates/rspack_core/src/compiler/resolver.rs @@ -7,7 +7,7 @@ use std::{ use dashmap::DashMap; use rustc_hash::FxHasher; -use crate::{AliasMap, DependencyType}; +use crate::DependencyType; use crate::{DependencyCategory, Resolve}; pub type ResolveResult = nodejs_resolver::ResolveResult; @@ -42,7 +42,9 @@ impl ResolverFactory { pub fn new(base_options: Resolve) -> Self { let cache = Arc::new(nodejs_resolver::Cache::default()); let resolver = Resolver(nodejs_resolver::Resolver::new( - base_options.clone().to_inner_options(cache.clone(), false), + base_options + .clone() + .to_inner_options(cache.clone(), false, DependencyCategory::Unknown), )); Self { cache, @@ -58,38 +60,15 @@ impl ResolverFactory { } else { let base_options = self.base_options.clone(); let merged_options = match &options.resolve_options { - Some(o) => merge_resolver_options(base_options, o.clone()), - None => match &self.base_options.condition_names { - None => { - let is_esm = matches!(options.dependency_category, DependencyCategory::Esm); - let condition_names = if is_esm { - vec![ - String::from("import"), - String::from("module"), - String::from("webpack"), - String::from("development"), - String::from("browser"), - ] - } else { - vec![ - String::from("require"), - String::from("module"), - String::from("webpack"), - String::from("development"), - String::from("browser"), - ] - }; - let options = Resolve { - condition_names: Some(condition_names), - ..self.base_options.clone() - }; - merge_resolver_options(base_options, options) - } - _ => base_options, - }, + Some(o) => base_options.merge(o.clone()), + None => base_options, }; let resolver = Arc::new(Resolver(nodejs_resolver::Resolver::new( - merged_options.to_inner_options(self.cache.clone(), options.resolve_to_context), + merged_options.to_inner_options( + self.cache.clone(), + options.resolve_to_context, + options.dependency_category, + ), ))); self.resolvers.insert(options, resolver.clone()); resolver @@ -97,153 +76,6 @@ impl ResolverFactory { } } -fn merge_resolver_options(base: Resolve, other: Resolve) -> Resolve { - fn overwrite(a: Option, b: Option, f: F) -> Option - where - T: Clone, - F: FnOnce(T, T) -> T, - { - match (a, b) { - (Some(a), Some(b)) => Some(f(a, b)), - (Some(a), None) => Some(a), - (None, Some(b)) => Some(b), - (None, None) => None, - } - } - - let alias = overwrite(base.alias, other.alias, |pre, mut now| { - now.extend(pre.into_iter()); - let now: indexmap::IndexSet<(String, Vec)> = now.into_iter().collect(); - now.into_iter().collect() - }); - let fallback = overwrite(base.fallback, other.fallback, |pre, mut now| { - now.extend(pre.into_iter()); - let now: indexmap::IndexSet<(String, Vec)> = now.into_iter().collect(); - now.into_iter().collect() - }); - let prefer_relative = overwrite(base.prefer_relative, other.prefer_relative, |_, value| { - value - }); - let symlinks = overwrite(base.symlinks, other.symlinks, |_, value| value); - let browser_field = overwrite(base.browser_field, other.browser_field, |_, value| value); - let extensions = overwrite(base.extensions, other.extensions, |base, value| { - normalize_string_array(&base, value) - }); - let main_files = overwrite(base.main_files, other.main_files, |base, value| { - normalize_string_array(&base, value) - }); - let main_fields = overwrite(base.main_fields, other.main_fields, |base, value| { - normalize_string_array(&base, value) - }); - let modules = overwrite(base.modules, other.modules, |base, value| { - normalize_string_array(&base, value) - }); - let condition_names = overwrite( - base.condition_names, - other.condition_names, - |base, value| normalize_string_array(&base, value), - ); - let tsconfig = other.tsconfig; - - Resolve { - fallback, - modules, - alias, - prefer_relative, - symlinks, - browser_field, - extensions, - main_files, - main_fields, - condition_names, - tsconfig, - } -} - -fn normalize_string_array(a: &[String], b: Vec) -> Vec { - b.into_iter().fold(vec![], |mut acc, item| { - if item.eq("...") { - acc.append(&mut a.to_vec()); - } else { - acc.push(item); - } - acc - }) -} - -#[cfg(test)] -mod test { - use super::*; - - fn to_string(a: Vec<&str>) -> Vec { - a.into_iter().map(String::from).collect() - } - - #[test] - fn test_merge_resolver_options() { - use crate::AliasMap; - let base = Resolve { - extensions: Some(to_string(vec!["a", "b"])), - alias: Some(vec![("c".to_string(), vec![AliasMap::Ignored])]), - symlinks: Some(false), - main_files: Some(to_string(vec!["d", "e", "f"])), - main_fields: Some(to_string(vec!["g", "h", "i"])), - browser_field: Some(true), - condition_names: Some(to_string(vec!["j", "k"])), - ..Default::default() - }; - let another = Resolve { - extensions: Some(to_string(vec!["a1", "b1"])), - alias: Some(vec![("c2".to_string(), vec![AliasMap::Ignored])]), - prefer_relative: Some(true), - browser_field: Some(true), - main_files: Some(to_string(vec!["d1", "e", "..."])), - main_fields: Some(to_string(vec!["...", "h", "..."])), - condition_names: Some(to_string(vec!["f", "..."])), - ..Default::default() - }; - let options = merge_resolver_options(base, another); - assert_eq!( - options.extensions.expect("should be Ok"), - to_string(vec!["a1", "b1"]) - ); - assert!(options.prefer_relative.expect("should be Ok")); - assert!(!options.symlinks.expect("should be Ok")); - assert_eq!( - options.main_files.expect("should be Ok"), - vec!["d1", "e", "d", "e", "f"] - ); - assert_eq!( - options.main_fields.expect("should be Ok"), - vec!["g", "h", "i", "h", "g", "h", "i"] - ); - assert_eq!( - options.alias.expect("should be Ok"), - vec![ - ("c2".to_string(), vec![AliasMap::Ignored]), - ("c".to_string(), vec![AliasMap::Ignored]) - ] - ); - assert_eq!(options.condition_names.expect("should be Ok").len(), 3); - } - - #[test] - fn test_normalize_string_array() { - let base = to_string(vec!["base0", "base1"]); - assert!(normalize_string_array(&base, vec![]).is_empty()); - assert_eq!( - normalize_string_array(&base, to_string(vec!["a", "b"])), - to_string(vec!["a", "b"]) - ); - assert_eq!( - normalize_string_array(&base, to_string(vec!["...", "a", "...", "b", "..."])), - to_string(vec![ - "base0", "base1", "a", "base0", "base1", "b", "base0", "base1" - ]) - ); - } -} - #[derive(Debug)] pub struct Resolver(pub(crate) nodejs_resolver::Resolver); diff --git a/crates/rspack_core/src/dependency/mod.rs b/crates/rspack_core/src/dependency/mod.rs index 6670fb1e29de..36ae351568b8 100644 --- a/crates/rspack_core/src/dependency/mod.rs +++ b/crates/rspack_core/src/dependency/mod.rs @@ -81,6 +81,20 @@ pub enum DependencyCategory { Wasm, } +impl From<&str> for DependencyCategory { + fn from(value: &str) -> Self { + match value { + "esm" => Self::Esm, + "commonjs" => Self::CommonJS, + "url" => Self::Url, + "wasm" => Self::Wasm, + "css-import" => Self::CssImport, + "css-compose" => Self::CssCompose, + _ => Self::Unknown, + } + } +} + pub trait Dependency: CodeGeneratable + AsAny + DynHash + DynClone + DynEq + Send + Sync + Debug { diff --git a/crates/rspack_core/src/options/resolve.rs b/crates/rspack_core/src/options/resolve.rs index 767c16a23ea0..5d0093b1e20d 100644 --- a/crates/rspack_core/src/options/resolve.rs +++ b/crates/rspack_core/src/options/resolve.rs @@ -1,5 +1,9 @@ use std::{path::PathBuf, sync::Arc}; +use hashlink::LinkedHashMap; + +use crate::DependencyCategory; + pub type AliasMap = nodejs_resolver::AliasMap; pub type Alias = Vec<(String, Vec)>; @@ -37,6 +41,8 @@ pub struct Resolve { // Same as `alias`, but only used if default resolving fails // Default is `[]` pub fallback: Option, + + pub by_dependency: Option, } impl Resolve { @@ -44,36 +50,39 @@ impl Resolve { self, cache: Arc, resolve_to_context: bool, + dependency_type: DependencyCategory, ) -> nodejs_resolver::Options { - let tsconfig = self.tsconfig; + let options = self.merge_by_dependency(dependency_type); + let tsconfig = options.tsconfig; let enforce_extension = nodejs_resolver::EnforceExtension::Auto; let external_cache = Some(cache); let description_file = String::from("package.json"); - let extensions = self.extensions.unwrap_or_else(|| { - vec![".tsx", ".jsx", ".ts", ".js", ".mjs", ".json"] + let extensions = options.extensions.unwrap_or_else(|| { + vec![".tsx", ".ts", ".jsx", ".js", ".mjs", ".json"] .into_iter() .map(|s| s.to_string()) .collect() }); - let alias = self.alias.unwrap_or_default(); - let prefer_relative = self.prefer_relative.unwrap_or(false); - let symlinks = self.symlinks.unwrap_or(true); - let main_files = self + let alias = options.alias.unwrap_or_default(); + let prefer_relative = options.prefer_relative.unwrap_or(false); + let symlinks = options.symlinks.unwrap_or(true); + let main_files = options .main_files .unwrap_or_else(|| vec![String::from("index")]); - let main_fields = self + let main_fields = options .main_fields .unwrap_or_else(|| vec![String::from("module"), String::from("main")]); - let browser_field = self.browser_field.unwrap_or(true); + let browser_field = options.browser_field.unwrap_or(true); let condition_names = std::collections::HashSet::from_iter( - self + options .condition_names .unwrap_or_else(|| vec!["module".to_string(), "import".to_string()]), ); - let modules = self + let modules = options .modules .unwrap_or_else(|| vec!["node_modules".to_string()]); - let fallback = self.fallback.unwrap_or_default(); + let fallback = options.fallback.unwrap_or_default(); + nodejs_resolver::Options { fallback, modules, @@ -92,4 +101,194 @@ impl Resolve { resolve_to_context, } } + + fn merge_by_dependency(mut self, dependency_type: DependencyCategory) -> Self { + let by_dependency = self + .by_dependency + .as_ref() + .and_then(|i| i.get(&dependency_type).cloned()); + self.by_dependency = None; + if let Some(by_dependency) = by_dependency { + self = self.merge(by_dependency); + } + self + } + + pub fn merge(self, value: Self) -> Self { + merge_resolver_options(self, value) + } +} + +#[derive(Debug, Clone, Default, Hash, PartialEq, Eq)] +pub struct ByDependency(LinkedHashMap); + +impl FromIterator<(DependencyCategory, Resolve)> for ByDependency { + fn from_iter>(i: I) -> Self { + Self(LinkedHashMap::from_iter(i.into_iter())) + } +} + +impl ByDependency { + // TODO: maybe a Merge trait for implementing cleverMerge in rust side? + pub fn merge(mut self, value: Self) -> Self { + for (k, v) in value.0 { + self.0.insert(k, v); + } + self + } + + pub fn get(&self, k: &DependencyCategory) -> Option<&Resolve> { + self.0.get(k) + } +} + +fn merge_resolver_options(base: Resolve, other: Resolve) -> Resolve { + fn overwrite(a: Option, b: Option, f: F) -> Option + where + T: Clone, + F: FnOnce(T, T) -> T, + { + match (a, b) { + (Some(a), Some(b)) => Some(f(a, b)), + (Some(a), None) => Some(a), + (None, Some(b)) => Some(b), + (None, None) => None, + } + } + + let alias = overwrite(base.alias, other.alias, |pre, mut now| { + now.extend(pre.into_iter()); + let now: indexmap::IndexSet<(String, Vec)> = now.into_iter().collect(); + now.into_iter().collect() + }); + let fallback = overwrite(base.fallback, other.fallback, |pre, mut now| { + now.extend(pre.into_iter()); + let now: indexmap::IndexSet<(String, Vec)> = now.into_iter().collect(); + now.into_iter().collect() + }); + let prefer_relative = overwrite(base.prefer_relative, other.prefer_relative, |_, value| { + value + }); + let symlinks = overwrite(base.symlinks, other.symlinks, |_, value| value); + let browser_field = overwrite(base.browser_field, other.browser_field, |_, value| value); + let extensions = overwrite(base.extensions, other.extensions, |base, value| { + normalize_string_array(&base, value) + }); + let main_files = overwrite(base.main_files, other.main_files, |base, value| { + normalize_string_array(&base, value) + }); + let main_fields = overwrite(base.main_fields, other.main_fields, |base, value| { + normalize_string_array(&base, value) + }); + let modules = overwrite(base.modules, other.modules, |base, value| { + normalize_string_array(&base, value) + }); + let condition_names = overwrite( + base.condition_names, + other.condition_names, + |base, value| normalize_string_array(&base, value), + ); + let by_dependency = overwrite(base.by_dependency, other.by_dependency, |pre, now| { + pre.merge(now) + }); + let tsconfig = overwrite(base.tsconfig, other.tsconfig, |_, value| value); + + Resolve { + fallback, + modules, + alias, + prefer_relative, + symlinks, + browser_field, + extensions, + main_files, + main_fields, + condition_names, + tsconfig, + by_dependency, + } +} + +fn normalize_string_array(a: &[String], b: Vec) -> Vec { + b.into_iter().fold(vec![], |mut acc, item| { + if item.eq("...") { + acc.append(&mut a.to_vec()); + } else { + acc.push(item); + } + acc + }) +} + +#[cfg(test)] +mod test { + use super::*; + + fn to_string(a: Vec<&str>) -> Vec { + a.into_iter().map(String::from).collect() + } + + #[test] + fn test_merge_resolver_options() { + use crate::AliasMap; + let base = Resolve { + extensions: Some(to_string(vec!["a", "b"])), + alias: Some(vec![("c".to_string(), vec![AliasMap::Ignored])]), + symlinks: Some(false), + main_files: Some(to_string(vec!["d", "e", "f"])), + main_fields: Some(to_string(vec!["g", "h", "i"])), + browser_field: Some(true), + condition_names: Some(to_string(vec!["j", "k"])), + ..Default::default() + }; + let another = Resolve { + extensions: Some(to_string(vec!["a1", "b1"])), + alias: Some(vec![("c2".to_string(), vec![AliasMap::Ignored])]), + prefer_relative: Some(true), + browser_field: Some(true), + main_files: Some(to_string(vec!["d1", "e", "..."])), + main_fields: Some(to_string(vec!["...", "h", "..."])), + condition_names: Some(to_string(vec!["f", "..."])), + ..Default::default() + }; + let options = merge_resolver_options(base, another); + assert_eq!( + options.extensions.expect("should be Ok"), + to_string(vec!["a1", "b1"]) + ); + assert!(options.prefer_relative.expect("should be Ok")); + assert!(!options.symlinks.expect("should be Ok")); + assert_eq!( + options.main_files.expect("should be Ok"), + vec!["d1", "e", "d", "e", "f"] + ); + assert_eq!( + options.main_fields.expect("should be Ok"), + vec!["g", "h", "i", "h", "g", "h", "i"] + ); + assert_eq!( + options.alias.expect("should be Ok"), + vec![ + ("c2".to_string(), vec![AliasMap::Ignored]), + ("c".to_string(), vec![AliasMap::Ignored]) + ] + ); + assert_eq!(options.condition_names.expect("should be Ok").len(), 3); + } + + #[test] + fn test_normalize_string_array() { + let base = to_string(vec!["base0", "base1"]); + assert!(normalize_string_array(&base, vec![]).is_empty()); + assert_eq!( + normalize_string_array(&base, to_string(vec!["a", "b"])), + to_string(vec!["a", "b"]) + ); + assert_eq!( + normalize_string_array(&base, to_string(vec!["...", "a", "...", "b", "..."])), + to_string(vec![ + "base0", "base1", "a", "base0", "base1", "b", "base0", "base1" + ]) + ); + } } diff --git a/crates/rspack_plugin_wasm/tests/fixtures/imports-multiple/expected/main.js b/crates/rspack_plugin_wasm/tests/fixtures/imports-multiple/expected/main.js index d02d26416368..d18e47e1a439 100644 --- a/crates/rspack_plugin_wasm/tests/fixtures/imports-multiple/expected/main.js +++ b/crates/rspack_plugin_wasm/tests/fixtures/imports-multiple/expected/main.js @@ -1,10 +1,10 @@ (self['webpackChunkwebpack'] = self['webpackChunkwebpack'] || []).push([["main"], { "./index.js": function (module, exports, __webpack_require__) { -it("should allow to run a WebAssembly module importing from multiple modules", function() { +(async function() { return __webpack_require__.el("./module.js").then(__webpack_require__.bind(__webpack_require__, "./module.js")).then(__webpack_require__.ir).then(function(mod) { - expect(mod.result).toBe(42); + if (mod.result !== 42) throw new Error('panic'); }); -}); +})(); }, },function(__webpack_require__) { diff --git a/crates/rspack_plugin_wasm/tests/fixtures/imports-multiple/index.js b/crates/rspack_plugin_wasm/tests/fixtures/imports-multiple/index.js index 5667add55b04..bccb00a3b04c 100644 --- a/crates/rspack_plugin_wasm/tests/fixtures/imports-multiple/index.js +++ b/crates/rspack_plugin_wasm/tests/fixtures/imports-multiple/index.js @@ -1,5 +1,5 @@ -it("should allow to run a WebAssembly module importing from multiple modules", function () { +(async function () { return import("./module").then(function (mod) { - expect(mod.result).toBe(42); + if (mod.result !== 42) throw new Error('panic') }); -}); +})(); diff --git a/packages/rspack-cli/src/commands/build.ts b/packages/rspack-cli/src/commands/build.ts index c51772b8a918..1160d5077298 100644 --- a/packages/rspack-cli/src/commands/build.ts +++ b/packages/rspack-cli/src/commands/build.ts @@ -88,7 +88,6 @@ export class BuildCommand implements RspackCommand { const errorHandler = (err, Stats) => { callback(err, Stats); - if (!cli.isWatch(compiler)) console.timeEnd("build"); }; const compiler = await cli.createCompiler(rspackOptions, errorHandler); diff --git a/packages/rspack/src/config/adapter.ts b/packages/rspack/src/config/adapter.ts index 4ea4e862624a..e45e2efbc896 100644 --- a/packages/rspack/src/config/adapter.ts +++ b/packages/rspack/src/config/adapter.ts @@ -126,11 +126,21 @@ function getRawAlias( return Object.fromEntries(entires); } +function getRawResolveByDependency( + byDependency: Resolve["byDependency"] +): RawOptions["resolve"]["byDependency"] { + if (byDependency === undefined) return byDependency; + return Object.fromEntries( + Object.entries(byDependency).map(([k, v]) => [k, getRawResolve(v)]) + ); +} + function getRawResolve(resolve: Resolve): RawOptions["resolve"] { return { ...resolve, alias: getRawAlias(resolve.alias), - fallback: getRawAlias(resolve.fallback) + fallback: getRawAlias(resolve.fallback), + byDependency: getRawResolveByDependency(resolve.byDependency) }; } diff --git a/packages/rspack/src/config/defaults.ts b/packages/rspack/src/config/defaults.ts index 0f9a01cd4701..2deeac06c734 100644 --- a/packages/rspack/src/config/defaults.ts +++ b/packages/rspack/src/config/defaults.ts @@ -453,28 +453,55 @@ const getResolveDefaults = ({ const jsExtensions = [ ".tsx", - ".jsx", ".ts", + ".jsx", ".js", ".json", - ".d.ts", - ".wasm" + ".wasm", + ".d.ts" ]; const tp = targetProperties; const browserField = tp && tp.web && (!tp.node || (tp.electron && tp.electronRenderer)); + const cjsDeps = () => ({ + browserField, + mainFields: browserField ? ["browser", "module", "..."] : ["module", "..."], + conditionNames: ["require", "module", "..."], + extensions: [...jsExtensions] + }); + const esmDeps = () => ({ + browserField, + mainFields: browserField ? ["browser", "module", "..."] : ["module", "..."], + conditionNames: ["import", "module", "..."], + extensions: [...jsExtensions] + }); + const resolveOptions: ResolveOptions = { modules: ["node_modules"], - // TODO: align with webpack, we need resolve.byDependency! - // conditionNames: undefined, + conditionNames: conditions, mainFiles: ["index"], - // TODO: align with webpack - extensions: [...jsExtensions], + extensions: [], browserField, - // TODO: align with webpack, we need resolve.byDependency! - mainFields: [browserField && "browser", "module", "main"].filter(Boolean) + mainFields: ["main"].filter(Boolean), + byDependency: { + wasm: esmDeps(), + esm: esmDeps(), + url: { + preferRelative: true + }, + // worker: { + // ...esmDeps(), + // preferRelative: true + // }, + commonjs: cjsDeps(), + // amd: cjsDeps(), + // for backward-compat: loadModule + // loader: cjsDeps(), + // for backward-compat: Custom Dependency and getResolve without dependencyType + unknown: cjsDeps() + } }; return resolveOptions; diff --git a/packages/rspack/src/config/schema.js b/packages/rspack/src/config/schema.js index fa58231ff549..1ecdaace0aec 100644 --- a/packages/rspack/src/config/schema.js +++ b/packages/rspack/src/config/schema.js @@ -1240,6 +1240,19 @@ module.exports = { "Prefer to resolve module requests as relative request and fallback to resolving as module.", type: "boolean" }, + byDependency: { + description: + 'Extra resolve options per dependency category. Typical categories are "commonjs", "amd", "esm".', + type: "object", + additionalProperties: { + description: "Options object for resolving requests.", + oneOf: [ + { + $ref: "#/definitions/ResolveOptions" + } + ] + } + }, tsConfigPath: { description: "Path to tsconfig.json", type: "string" diff --git a/packages/rspack/src/config/types.ts b/packages/rspack/src/config/types.ts index d5eb110fae53..5167c4054ffa 100644 --- a/packages/rspack/src/config/types.ts +++ b/packages/rspack/src/config/types.ts @@ -240,6 +240,9 @@ export interface ResolveOptions { modules?: string[]; preferRelative?: boolean; tsConfigPath?: string; + byDependency?: { + [k: string]: ResolveOptions; + }; } export type ResolveAlias = { [k: string]: false | string | Array; diff --git a/packages/rspack/tests/Defaults.unittest.ts b/packages/rspack/tests/Defaults.unittest.ts index 0b706275fc10..f7a751345135 100644 --- a/packages/rspack/tests/Defaults.unittest.ts +++ b/packages/rspack/tests/Defaults.unittest.ts @@ -236,18 +236,106 @@ describe("snapshots", () => { "plugins": [], "resolve": { "browserField": true, - "extensions": [ - ".tsx", - ".jsx", - ".ts", - ".js", - ".json", - ".d.ts", - ".wasm", + "byDependency": { + "commonjs": { + "browserField": true, + "conditionNames": [ + "require", + "module", + "...", + ], + "extensions": [ + ".tsx", + ".ts", + ".jsx", + ".js", + ".json", + ".wasm", + ".d.ts", + ], + "mainFields": [ + "browser", + "module", + "...", + ], + }, + "esm": { + "browserField": true, + "conditionNames": [ + "import", + "module", + "...", + ], + "extensions": [ + ".tsx", + ".ts", + ".jsx", + ".js", + ".json", + ".wasm", + ".d.ts", + ], + "mainFields": [ + "browser", + "module", + "...", + ], + }, + "unknown": { + "browserField": true, + "conditionNames": [ + "require", + "module", + "...", + ], + "extensions": [ + ".tsx", + ".ts", + ".jsx", + ".js", + ".json", + ".wasm", + ".d.ts", + ], + "mainFields": [ + "browser", + "module", + "...", + ], + }, + "url": { + "preferRelative": true, + }, + "wasm": { + "browserField": true, + "conditionNames": [ + "import", + "module", + "...", + ], + "extensions": [ + ".tsx", + ".ts", + ".jsx", + ".js", + ".json", + ".wasm", + ".d.ts", + ], + "mainFields": [ + "browser", + "module", + "...", + ], + }, + }, + "conditionNames": [ + "webpack", + "production", + "browser", ], + "extensions": [], "mainFields": [ - "browser", - "module", "main", ], "mainFiles": [ @@ -409,6 +497,9 @@ describe("snapshots", () => { @@ ... @@ - "minRemainingSize": undefined, + "minRemainingSize": 0, + @@ ... @@ + - "production", + + "development", `) ); /** @@ -718,7 +809,28 @@ describe("snapshots", () => { - "browserField": true, + "browserField": false, @@ ... @@ + - "browserField": true, + + "browserField": false, + @@ ... @@ + - "browser", + @@ ... @@ + - "browserField": true, + + "browserField": false, + @@ ... @@ + - "browser", + @@ ... @@ + - "browserField": true, + + "browserField": false, + @@ ... @@ + - "browser", + @@ ... @@ + - "browserField": true, + + "browserField": false, + @@ ... @@ + - "browser", + @@ ... @@ - "browser", + + "node", @@ ... @@ - "target": "web", + "target": "node", @@ -729,6 +841,8 @@ describe("snapshots", () => { - Expected + Received + @@ ... @@ + + "worker", @@ ... @@ - "target": "web", + "target": "webworker", @@ -759,7 +873,29 @@ describe("snapshots", () => { - "browserField": true, + "browserField": false, @@ ... @@ + - "browserField": true, + + "browserField": false, + @@ ... @@ + - "browser", + @@ ... @@ + - "browserField": true, + + "browserField": false, + @@ ... @@ + - "browser", + @@ ... @@ + - "browserField": true, + + "browserField": false, + @@ ... @@ + - "browser", + @@ ... @@ + - "browserField": true, + + "browserField": false, + @@ ... @@ + - "browser", + @@ ... @@ - "browser", + + "node", + + "electron", @@ ... @@ - "target": "web", + "target": "electron-main", @@ -790,7 +926,29 @@ describe("snapshots", () => { - "browserField": true, + "browserField": false, @@ ... @@ - - "browser", + - "browserField": true, + + "browserField": false, + @@ ... @@ + - "browser", + @@ ... @@ + - "browserField": true, + + "browserField": false, + @@ ... @@ + - "browser", + @@ ... @@ + - "browserField": true, + + "browserField": false, + @@ ... @@ + - "browser", + @@ ... @@ + - "browserField": true, + + "browserField": false, + @@ ... @@ + - "browser", + @@ ... @@ + + "node", + @@ ... @@ + + "electron", @@ ... @@ - "target": "web", + "target": "electron-preload", @@ -882,6 +1040,9 @@ describe("snapshots", () => { @@ ... @@ - "minRemainingSize": undefined, + "minRemainingSize": 0, + @@ ... @@ + - "production", + + "development", `) ); diff --git a/packages/rspack/tests/cases/resolve/dep-conditions/.gitignore b/packages/rspack/tests/cases/resolve/dep-conditions/.gitignore deleted file mode 100644 index 9bfb325f1097..000000000000 --- a/packages/rspack/tests/cases/resolve/dep-conditions/.gitignore +++ /dev/null @@ -1 +0,0 @@ -!node_modules/ \ No newline at end of file diff --git a/packages/rspack/tests/cases/resolve/dep-conditions/index.js b/packages/rspack/tests/cases/resolve/dep-conditions/index.js deleted file mode 100644 index 63d0b4b80057..000000000000 --- a/packages/rspack/tests/cases/resolve/dep-conditions/index.js +++ /dev/null @@ -1,7 +0,0 @@ -import v1 from "dep-condition"; -const v2 = require("dep-condition"); - -it("should make different modules for resolve", function () { - expect(v1).toBe("import"); - expect(v2).toBe("require"); -}); diff --git a/packages/rspack/tests/cases/resolve/dep-conditions/node_modules/dep-condition/default.js b/packages/rspack/tests/cases/resolve/dep-conditions/node_modules/dep-condition/default.js deleted file mode 100644 index 859701f7db1a..000000000000 --- a/packages/rspack/tests/cases/resolve/dep-conditions/node_modules/dep-condition/default.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = "default" \ No newline at end of file diff --git a/packages/rspack/tests/cases/resolve/dep-conditions/node_modules/dep-condition/import.js b/packages/rspack/tests/cases/resolve/dep-conditions/node_modules/dep-condition/import.js deleted file mode 100644 index 8adcdcd9129b..000000000000 --- a/packages/rspack/tests/cases/resolve/dep-conditions/node_modules/dep-condition/import.js +++ /dev/null @@ -1 +0,0 @@ -export default "import"; \ No newline at end of file diff --git a/packages/rspack/tests/cases/resolve/dep-conditions/node_modules/dep-condition/package.json b/packages/rspack/tests/cases/resolve/dep-conditions/node_modules/dep-condition/package.json deleted file mode 100644 index 6f8d06c6a645..000000000000 --- a/packages/rspack/tests/cases/resolve/dep-conditions/node_modules/dep-condition/package.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "name": "dep-condition", - "exports": { - ".": { - "import": "./import.js", - "require": "./require", - "default": "./default" - } - } -} \ No newline at end of file diff --git a/packages/rspack/tests/cases/resolve/dep-conditions/node_modules/dep-condition/require.js b/packages/rspack/tests/cases/resolve/dep-conditions/node_modules/dep-condition/require.js deleted file mode 100644 index 02ab4713c3b1..000000000000 --- a/packages/rspack/tests/cases/resolve/dep-conditions/node_modules/dep-condition/require.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = "require" \ No newline at end of file diff --git a/packages/rspack/tests/configCases/resolve/by-dependency/baz.js b/packages/rspack/tests/configCases/resolve/by-dependency/baz.js new file mode 100644 index 000000000000..c55720d996d4 --- /dev/null +++ b/packages/rspack/tests/configCases/resolve/by-dependency/baz.js @@ -0,0 +1 @@ +export default "baz"; diff --git a/packages/rspack/tests/configCases/resolve/by-dependency/foo.bar b/packages/rspack/tests/configCases/resolve/by-dependency/foo.bar new file mode 100644 index 000000000000..d02ba545bd3b --- /dev/null +++ b/packages/rspack/tests/configCases/resolve/by-dependency/foo.bar @@ -0,0 +1 @@ +export default 'foo'; diff --git a/packages/rspack/tests/configCases/resolve/by-dependency/index.js b/packages/rspack/tests/configCases/resolve/by-dependency/index.js new file mode 100644 index 000000000000..ce785c88dc13 --- /dev/null +++ b/packages/rspack/tests/configCases/resolve/by-dependency/index.js @@ -0,0 +1,10 @@ +import foo from "./foo"; +import baz from "./baz"; + +it("should resolve 'foo.bar', byDependency '.bar' extension works", function () { + expect(foo).toBe("foo"); +}); + +it("should resolve 'baz.js', byDependency '...' extensions works", function () { + expect(baz).toBe("baz"); +}); diff --git a/packages/rspack/tests/configCases/resolve/by-dependency/webpack.config.js b/packages/rspack/tests/configCases/resolve/by-dependency/webpack.config.js new file mode 100644 index 000000000000..c2fe1e5bafb4 --- /dev/null +++ b/packages/rspack/tests/configCases/resolve/by-dependency/webpack.config.js @@ -0,0 +1,10 @@ +module.exports = { + mode: "development", + resolve: { + byDependency: { + esm: { + extensions: [".bar", "..."] + } + } + } +}; diff --git a/packages/rspack/tests/hotCases/disposing/remove-chunk-with-shared-in-other-runtime/module.js b/packages/rspack/tests/hotCases/disposing/remove-chunk-with-shared-in-other-runtime/module.js index 4c6a7f35a5b3..65dfded5eee7 100644 --- a/packages/rspack/tests/hotCases/disposing/remove-chunk-with-shared-in-other-runtime/module.js +++ b/packages/rspack/tests/hotCases/disposing/remove-chunk-with-shared-in-other-runtime/module.js @@ -1,3 +1,3 @@ -export default () => new Worker(new URL("./chunk2", import.meta.url)); +export default () => new Worker(new URL("./chunk2.js", import.meta.url)); // TODO(ahabhgk): should be "./chunk2" WorkerDependency instead of URLDependency --- export default 42;