diff --git a/crates/turbopack-core/src/resolve/mod.rs b/crates/turbopack-core/src/resolve/mod.rs index edf4f2472074a0..fac3aed34c00b3 100644 --- a/crates/turbopack-core/src/resolve/mod.rs +++ b/crates/turbopack-core/src/resolve/mod.rs @@ -1008,6 +1008,7 @@ pub async fn resolve_raw( #[turbo_tasks::function] pub async fn resolve( lookup_path: Vc, + reference_type: Value, request: Vc, options: Vc, ) -> Result> { @@ -1015,12 +1016,14 @@ pub async fn resolve( .await? .resolve() .await?; - let result = handle_resolve_plugins(lookup_path, request, options, raw_result).await?; + let result = + handle_resolve_plugins(lookup_path, reference_type, request, options, raw_result).await?; Ok(result) } async fn handle_resolve_plugins( lookup_path: Vc, + reference_type: Value, request: Vc, options: Vc, result: Vc, @@ -1028,13 +1031,17 @@ async fn handle_resolve_plugins( async fn apply_plugins_to_path( path: Vc, lookup_path: Vc, + reference_type: Value, request: Vc, options: Vc, ) -> Result>> { for plugin in &options.await?.plugins { let after_resolve_condition = plugin.after_resolve_condition().resolve().await?; if *after_resolve_condition.matches(path).await? { - if let Some(result) = *plugin.after_resolve(path, lookup_path, request).await? { + if let Some(result) = *plugin + .after_resolve(path, lookup_path, reference_type.clone(), request) + .await? + { return Ok(Some(result)); } } @@ -1052,7 +1059,8 @@ async fn handle_resolve_plugins( if let &ResolveResultItem::Source(source) = primary { let path = source.ident().path().resolve().await?; if let Some(new_result) = - apply_plugins_to_path(path, lookup_path, request, options).await? + apply_plugins_to_path(path, lookup_path, reference_type.clone(), request, options) + .await? { let new_result = new_result.await?; changed = true; @@ -1333,12 +1341,21 @@ async fn resolve_into_folder( return resolve_internal_inline(package_path, request.resolve().await?, options) .await; } - ResolveIntoPackage::MainField(name) => { + ResolveIntoPackage::MainField { + field: name, + extensions, + } => { if let Some(package_json) = &*read_package_json(package_json_path).await? { if let Some(field_value) = package_json[name].as_str() { let request = Request::parse(Value::new(normalize_request(field_value).into())); + let options = if let Some(extensions) = extensions { + options.with_extensions(extensions.clone()) + } else { + options + }; + let result = &*resolve_internal_inline(package_path, request, options) .await? .await?; @@ -1542,7 +1559,7 @@ async fn resolve_into_package( if could_match_others { for resolve_into_package in options_value.into_package.iter() { match resolve_into_package { - ResolveIntoPackage::Default(_) | ResolveIntoPackage::MainField(_) => { + ResolveIntoPackage::Default(_) | ResolveIntoPackage::MainField { .. } => { // doesn't affect packages with subpath if path.is_match("/") { results.push(resolve_into_folder(package_path, options, query)); diff --git a/crates/turbopack-core/src/resolve/node.rs b/crates/turbopack-core/src/resolve/node.rs index f456767dfc80fb..f41a842a419dcf 100644 --- a/crates/turbopack-core/src/resolve/node.rs +++ b/crates/turbopack-core/src/resolve/node.rs @@ -13,8 +13,9 @@ pub fn node_cjs_resolve_options(root: Vc) -> Vc ("require".to_string(), ConditionValue::Set), ] .into(); + let extensions = vec![".js".to_string(), ".json".to_string(), ".node".to_string()]; ResolveOptions { - extensions: vec![".js".to_string(), ".json".to_string(), ".node".to_string()], + extensions, modules: vec![ResolveModules::Nested( root, vec!["node_modules".to_string()], @@ -24,7 +25,10 @@ pub fn node_cjs_resolve_options(root: Vc) -> Vc conditions: conditions.clone(), unspecified_conditions: ConditionValue::Unset, }, - ResolveIntoPackage::MainField("main".to_string()), + ResolveIntoPackage::MainField { + field: "main".to_string(), + extensions: None, + }, ResolveIntoPackage::Default("index".to_string()), ], in_package: vec![ResolveInPackage::ImportsField { @@ -43,6 +47,7 @@ pub fn node_esm_resolve_options(root: Vc) -> Vc ("import".to_string(), ConditionValue::Set), ] .into(); + let extensions = vec![".js".to_string(), ".json".to_string(), ".node".to_string()]; ResolveOptions { extensions: vec![], modules: vec![ResolveModules::Nested( @@ -54,10 +59,11 @@ pub fn node_esm_resolve_options(root: Vc) -> Vc conditions: conditions.clone(), unspecified_conditions: ConditionValue::Unset, }, - ResolveIntoPackage::MainField("main".to_string()), - ResolveIntoPackage::Default("index.js".to_string()), - ResolveIntoPackage::Default("index.json".to_string()), - ResolveIntoPackage::Default("index.node".to_string()), + ResolveIntoPackage::MainField { + field: "main".to_string(), + extensions: Some(extensions), + }, + ResolveIntoPackage::Default("index".to_string()), ], in_package: vec![ResolveInPackage::ImportsField { conditions, diff --git a/crates/turbopack-core/src/resolve/options.rs b/crates/turbopack-core/src/resolve/options.rs index 1185a1bd531fa7..0447ba84cc6f98 100644 --- a/crates/turbopack-core/src/resolve/options.rs +++ b/crates/turbopack-core/src/resolve/options.rs @@ -67,7 +67,10 @@ pub enum ResolveIntoPackage { /// [main]: https://nodejs.org/api/packages.html#main /// [module]: https://esbuild.github.io/api/#main-fields /// [browser]: https://esbuild.github.io/api/#main-fields - MainField(String), + MainField { + field: String, + extensions: Option>, + }, /// Default behavior of using the index.js file at the root of the package. Default(String), } @@ -455,6 +458,14 @@ impl ResolveOptions { ); Ok(resolve_options.into()) } + + /// Overrides the extensions used for resolving + #[turbo_tasks::function] + pub async fn with_extensions(self: Vc, extensions: Vec) -> Result> { + let mut resolve_options = self.await?.clone_value(); + resolve_options.extensions = extensions; + Ok(resolve_options.into()) + } } #[turbo_tasks::value(shared)] diff --git a/crates/turbopack-core/src/resolve/plugin.rs b/crates/turbopack-core/src/resolve/plugin.rs index eaea46053db91e..3cbca4592ed30e 100644 --- a/crates/turbopack-core/src/resolve/plugin.rs +++ b/crates/turbopack-core/src/resolve/plugin.rs @@ -1,8 +1,11 @@ use anyhow::Result; -use turbo_tasks::Vc; +use turbo_tasks::{Value, Vc}; use turbo_tasks_fs::{glob::Glob, FileSystemPath}; -use crate::resolve::{parse::Request, ResolveResultOption}; +use crate::{ + reference_type::ReferenceType, + resolve::{parse::Request, ResolveResultOption}, +}; /// A condition which determines if the hooks of a resolve plugin gets called. #[turbo_tasks::value] @@ -48,6 +51,7 @@ pub trait ResolvePlugin { self: Vc, fs_path: Vc, lookup_path: Vc, + reference_type: Value, request: Vc, ) -> Vc; } diff --git a/crates/turbopack-dev/src/react_refresh.rs b/crates/turbopack-dev/src/react_refresh.rs index da235accd5200a..ef3c97c35044d8 100644 --- a/crates/turbopack-dev/src/react_refresh.rs +++ b/crates/turbopack-dev/src/react_refresh.rs @@ -1,9 +1,10 @@ use anyhow::Result; -use turbo_tasks::Vc; +use turbo_tasks::{Value, Vc}; use turbo_tasks_fs::FileSystemPath; use turbopack::resolve_options_context::ResolveOptionsContext; use turbopack_core::{ issue::{Issue, IssueExt, IssueSeverity, StyledString}, + reference_type::{CommonJsReferenceSubType, ReferenceType}, resolve::parse::Request, }; use turbopack_ecmascript::resolve::apply_cjs_specific_options; @@ -49,8 +50,13 @@ pub async fn assert_can_resolve_react_refresh( let resolve_options = apply_cjs_specific_options(turbopack::resolve_options(path, resolve_options_context)); for request in [react_refresh_request_in_next(), react_refresh_request()] { - let result = - turbopack_core::resolve::resolve(path, request, resolve_options).first_source(); + let result = turbopack_core::resolve::resolve( + path, + Value::new(ReferenceType::CommonJs(CommonJsReferenceSubType::Undefined)), + request, + resolve_options, + ) + .first_source(); if result.await?.is_some() { return Ok(ResolveReactRefreshResult::Found(request).cell()); diff --git a/crates/turbopack-ecmascript-runtime/js/src/dev/runtime/nodejs/runtime-backend-nodejs.ts b/crates/turbopack-ecmascript-runtime/js/src/dev/runtime/nodejs/runtime-backend-nodejs.ts index 7993dcc733eb4b..1d94c4801a4635 100644 --- a/crates/turbopack-ecmascript-runtime/js/src/dev/runtime/nodejs/runtime-backend-nodejs.ts +++ b/crates/turbopack-ecmascript-runtime/js/src/dev/runtime/nodejs/runtime-backend-nodejs.ts @@ -13,7 +13,10 @@ interface RequireContextEntry { external: boolean; } -type ExternalRequire = (id: ModuleId) => Exports | EsmNamespaceObject; +type ExternalRequire = ( + id: ModuleId, + esm?: boolean +) => Exports | EsmNamespaceObject; type ExternalImport = (id: ModuleId) => Promise; interface TurbopackDevContext extends TurbopackDevBaseContext { diff --git a/crates/turbopack-ecmascript-runtime/js/src/shared-node/node-utils.ts b/crates/turbopack-ecmascript-runtime/js/src/shared-node/node-utils.ts index d3554bcd2e9919..93ac77d171337c 100644 --- a/crates/turbopack-ecmascript-runtime/js/src/shared-node/node-utils.ts +++ b/crates/turbopack-ecmascript-runtime/js/src/shared-node/node-utils.ts @@ -13,8 +13,23 @@ function commonJsRequireContext( : commonJsRequire(sourceModule, entry.id()); } -function externalImport(id: ModuleId) { - return import(id); +async function externalImport(id: ModuleId) { + let raw; + try { + raw = await import(id); + } catch (err) { + // TODO(alexkirsz) This can happen when a client-side module tries to load + // an external module we don't provide a shim for (e.g. querystring, url). + // For now, we fail semi-silently, but in the future this should be a + // compilation error. + throw new Error(`Failed to load external module ${id}: ${err}`); + } + + if (raw && raw.__esModule && raw.default && "default" in raw.default) { + return interopEsm(raw.default, {}, true); + } + + return raw; } function externalRequire( diff --git a/crates/turbopack-ecmascript/src/lib.rs b/crates/turbopack-ecmascript/src/lib.rs index f29416f19fda3f..7edee3b6304e0d 100644 --- a/crates/turbopack-ecmascript/src/lib.rs +++ b/crates/turbopack-ecmascript/src/lib.rs @@ -98,6 +98,9 @@ pub struct EcmascriptOptions { /// This allows to construct url depends on the different building context, /// e.g. SSR, CSR, or Node.js. pub url_rewrite_behavior: Option, + /// External imports should used `__turbopack_import__` instead of + /// `__turbopack_require__` and become async module references. + pub import_externals: bool, } #[turbo_tasks::value(serialization = "auto_for_input")] @@ -175,7 +178,11 @@ impl EcmascriptModuleAssetBuilder { ) }; if let Some(part) = self.part { - Vc::upcast(EcmascriptModulePartAsset::new(base, part)) + Vc::upcast(EcmascriptModulePartAsset::new( + base, + part, + self.options.import_externals, + )) } else { Vc::upcast(base) } diff --git a/crates/turbopack-ecmascript/src/references/async_module.rs b/crates/turbopack-ecmascript/src/references/async_module.rs index 283e718ea65ed8..ddcf3867981bf7 100644 --- a/crates/turbopack-ecmascript/src/references/async_module.rs +++ b/crates/turbopack-ecmascript/src/references/async_module.rs @@ -6,7 +6,7 @@ use swc_core::{ ecma::ast::{ArrayLit, ArrayPat, Expr, Ident, Pat, Program}, quote, }; -use turbo_tasks::{trace::TraceRawVcs, TryFlatJoinIterExt, Vc}; +use turbo_tasks::{trace::TraceRawVcs, TryFlatJoinIterExt, TryJoinIterExt, Vc}; use turbopack_core::chunk::{AsyncModuleInfo, ChunkableModule}; use super::esm::base::ReferencedAsset; @@ -46,6 +46,7 @@ pub struct AsyncModule { pub placeable: Vc>, pub references: IndexSet>, pub has_top_level_await: bool, + pub import_externals: bool, } /// Option<[AsyncModule]>. @@ -80,22 +81,24 @@ struct AsyncModuleIdents(IndexSet); impl AsyncModule { #[turbo_tasks::function] async fn get_async_idents( - self: Vc, + &self, chunking_context: Vc>, async_module_info: Vc, ) -> Result> { - let this = self.await?; let async_module_info = async_module_info.await?; - let reference_idents = this + let reference_idents = self .references .iter() .map(|r| async { let referenced_asset = r.get_referenced_asset().await?; Ok(match &*referenced_asset { ReferencedAsset::OriginalReferenceTypeExternal(_) => { - // TODO(WEB-1259): we need to detect if external modules are esm - None + if self.import_externals { + referenced_asset.get_ident().await? + } else { + None + } } ReferencedAsset::Some(placeable) => { let chunk_item = placeable @@ -121,8 +124,28 @@ impl AsyncModule { } #[turbo_tasks::function] - pub(crate) fn is_self_async(&self) -> Vc { - Vc::cell(self.has_top_level_await) + pub(crate) async fn is_self_async(&self) -> Result> { + if self.has_top_level_await { + return Ok(Vc::cell(true)); + } + + Ok(Vc::cell( + self.import_externals + && self + .references + .iter() + .map(|r| async { + let referenced_asset = r.get_referenced_asset().await?; + Ok(matches!( + &*referenced_asset, + ReferencedAsset::OriginalReferenceTypeExternal(_) + )) + }) + .try_join() + .await? + .iter() + .any(|&b| b), + )) } /// Returns diff --git a/crates/turbopack-ecmascript/src/references/esm/base.rs b/crates/turbopack-ecmascript/src/references/esm/base.rs index 7d8aa29d1af7ec..c063eb74b696b3 100644 --- a/crates/turbopack-ecmascript/src/references/esm/base.rs +++ b/crates/turbopack-ecmascript/src/references/esm/base.rs @@ -105,6 +105,7 @@ pub struct EsmAssetReference { pub annotations: ImportAnnotations, pub issue_source: Option>, pub export_name: Option>, + pub import_externals: bool, } /// A list of [EsmAssetReference]s @@ -130,6 +131,7 @@ impl EsmAssetReference { issue_source: Option>, annotations: Value, export_name: Option>, + import_externals: bool, ) -> Vc { Self::cell(EsmAssetReference { origin, @@ -137,6 +139,7 @@ impl EsmAssetReference { issue_source, annotations: annotations.into_value(), export_name, + import_externals, }) } @@ -209,13 +212,13 @@ impl CodeGenerateable for EsmAssetReference { ) -> Result> { let mut visitors = Vec::new(); + let this = &*self.await?; let chunking_type = self.chunking_type().await?; let resolved = self.resolve_reference().await?; // Insert code that throws immediately at time of import if a request is // unresolvable if resolved.is_unresolveable_ref() { - let this = &*self.await?; let request = request_to_string(this.request).await?.to_string(); visitors.push(create_visitor!(visit_mut_program(program: &mut Program) { insert_hoisted_stmt(program, Stmt::Expr(ExprStmt { @@ -232,6 +235,7 @@ impl CodeGenerateable for EsmAssetReference { // only chunked references can be imported if chunking_type.is_some() { let referenced_asset = self.get_referenced_asset().await?; + let import_externals = this.import_externals; if let Some(ident) = referenced_asset.get_ident().await? { match &*referenced_asset { ReferencedAsset::Some(asset) => { @@ -262,11 +266,19 @@ impl CodeGenerateable for EsmAssetReference { let request = request.clone(); visitors.push(create_visitor!(visit_mut_program(program: &mut Program) { // TODO Technically this should insert a ESM external, but we don't support that yet - let stmt = quote!( - "var $name = __turbopack_external_require__($id, true);" as Stmt, - name = Ident::new(ident.clone().into(), DUMMY_SP), - id: Expr = Expr::Lit(request.clone().into()) - ); + let stmt = if import_externals { + quote!( + "var $name = __turbopack_external_import__($id);" as Stmt, + name = Ident::new(ident.clone().into(), DUMMY_SP), + id: Expr = Expr::Lit(request.clone().into()) + ) + } else { + quote!( + "var $name = __turbopack_external_require__($id, true);" as Stmt, + name = Ident::new(ident.clone().into(), DUMMY_SP), + id: Expr = Expr::Lit(request.clone().into()) + ) + }; insert_hoisted_stmt(program, stmt); })); } diff --git a/crates/turbopack-ecmascript/src/references/esm/dynamic.rs b/crates/turbopack-ecmascript/src/references/esm/dynamic.rs index 5e1c04e6980632..d539ec97388f63 100644 --- a/crates/turbopack-ecmascript/src/references/esm/dynamic.rs +++ b/crates/turbopack-ecmascript/src/references/esm/dynamic.rs @@ -30,6 +30,7 @@ pub struct EsmAsyncAssetReference { pub path: Vc, pub issue_source: Vc, pub in_try: bool, + pub import_externals: bool, } #[turbo_tasks::value_impl] @@ -41,6 +42,7 @@ impl EsmAsyncAssetReference { path: Vc, issue_source: Vc, in_try: bool, + import_externals: bool, ) -> Vc { Self::cell(EsmAsyncAssetReference { origin, @@ -48,6 +50,7 @@ impl EsmAsyncAssetReference { path, issue_source, in_try, + import_externals, }) } } @@ -115,6 +118,7 @@ impl CodeGenerateable for EsmAsyncAssetReference { .await?; let path = &self.path.await?; + let import_externals = self.import_externals; let visitor = match &*pm { PatternMapping::Invalid => { @@ -161,6 +165,30 @@ impl CodeGenerateable for EsmAsyncAssetReference { ]; }) } + PatternMapping::OriginalReferenceTypeExternal(_) + | PatternMapping::OriginalReferenceExternal => { + create_visitor!(exact path, visit_mut_call_expr(call_expr: &mut CallExpr) { + let old_args = std::mem::take(&mut call_expr.args); + let expr = match old_args.into_iter().next() { + Some(ExprOrSpread { expr, spread: None }) => pm.apply(*expr), + _ => pm.create(), + }; + if import_externals { + call_expr.callee = Callee::Expr(quote_expr!("__turbopack_external_import__")); + call_expr.args = vec![ + ExprOrSpread { spread: None, expr: Box::new(expr) }, + ]; + } else { + call_expr.callee = Callee::Expr(quote_expr!("Promise.resolve().then")); + call_expr.args = vec![ + ExprOrSpread { spread: None, expr: quote_expr!( + "() => __turbopack_external_require__($arg, true)", + arg: Expr = expr + ) }, + ]; + } + }) + } _ => { create_visitor!(exact path, visit_mut_call_expr(call_expr: &mut CallExpr) { let old_args = std::mem::take(&mut call_expr.args); diff --git a/crates/turbopack-ecmascript/src/references/mod.rs b/crates/turbopack-ecmascript/src/references/mod.rs index 845bbe4d0803a3..8740587f72d795 100644 --- a/crates/turbopack-ecmascript/src/references/mod.rs +++ b/crates/turbopack-ecmascript/src/references/mod.rs @@ -266,6 +266,7 @@ struct AnalysisState<'a> { // the object allocation. first_import_meta: bool, import_parts: bool, + import_externals: bool, } impl<'a> AnalysisState<'a> { @@ -301,6 +302,7 @@ pub(crate) async fn analyze_ecmascript_module( let transforms = raw_module.transforms; let options = raw_module.options; let compile_time_info = raw_module.compile_time_info; + let import_externals = options.import_externals; let origin = Vc::upcast::>(module); @@ -446,6 +448,7 @@ pub(crate) async fn analyze_ecmascript_module( } else { None }, + import_externals, ); import_references.push(r); } @@ -560,6 +563,7 @@ pub(crate) async fn analyze_ecmascript_module( placeable: Vc::upcast(module), references: import_references.iter().copied().collect(), has_top_level_await, + import_externals, } .cell(); analysis.set_async_module(async_module); @@ -579,6 +583,7 @@ pub(crate) async fn analyze_ecmascript_module( placeable: Vc::upcast(module), references: import_references.iter().copied().collect(), has_top_level_await, + import_externals, } .cell(); analysis.set_async_module(async_module); @@ -622,6 +627,7 @@ pub(crate) async fn analyze_ecmascript_module( placeable: Vc::upcast(module), references: import_references.iter().copied().collect(), has_top_level_await, + import_externals, } .cell(); analysis.set_async_module(async_module); @@ -671,6 +677,7 @@ pub(crate) async fn analyze_ecmascript_module( fun_args_values: Mutex::new(HashMap::>::new()), first_import_meta: true, import_parts: options.import_parts, + import_externals: options.import_externals, }; enum Action { @@ -1083,6 +1090,7 @@ async fn handle_call) + Send + Sync>( Vc::cell(ast_path.to_vec()), issue_source(source, span), in_try, + state.import_externals, )); return Ok(()); } @@ -1777,6 +1785,7 @@ async fn handle_free_var_reference( .map(|export| ModulePart::export(export.to_string())) }) .flatten(), + state.import_externals, ) .resolve() .await?; @@ -2585,6 +2594,7 @@ async fn resolve_as_webpack_runtime( let resolved = resolve( origin.origin_path().parent().resolve().await?, + Value::new(ReferenceType::CommonJs(CommonJsReferenceSubType::Undefined)), request, options, ); diff --git a/crates/turbopack-ecmascript/src/resolve/mod.rs b/crates/turbopack-ecmascript/src/resolve/mod.rs index 9433d2a28446bd..f3910377cd0b57 100644 --- a/crates/turbopack-ecmascript/src/resolve/mod.rs +++ b/crates/turbopack-ecmascript/src/resolve/mod.rs @@ -100,11 +100,22 @@ pub async fn url_resolve( let ty = Value::new(ReferenceType::Url(ty.into_value())); let resolve_options = origin.resolve_options(ty.clone()); let rel_request = request.as_relative(); - let rel_result = resolve(origin.origin_path().parent(), rel_request, resolve_options); + let reference_type = Value::new(ReferenceType::Url(UrlReferenceSubType::EcmaScriptNewUrl)); + let rel_result = resolve( + origin.origin_path().parent(), + reference_type.clone(), + rel_request, + resolve_options, + ); let result = if *rel_result.is_unresolveable().await? && rel_request.resolve().await? != request { - resolve(origin.origin_path().parent(), request, resolve_options) - .with_affecting_sources(rel_result.await?.get_affecting_sources().clone()) + resolve( + origin.origin_path().parent(), + reference_type, + request, + resolve_options, + ) + .with_affecting_sources(rel_result.await?.get_affecting_sources().clone()) } else { rel_result }; diff --git a/crates/turbopack-ecmascript/src/tree_shake/asset.rs b/crates/turbopack-ecmascript/src/tree_shake/asset.rs index aca45d5a033a6a..f4a07b9bcd75b4 100644 --- a/crates/turbopack-ecmascript/src/tree_shake/asset.rs +++ b/crates/turbopack-ecmascript/src/tree_shake/asset.rs @@ -23,6 +23,7 @@ use crate::{ pub struct EcmascriptModulePartAsset { pub(crate) full_module: Vc, pub(crate) part: Vc, + pub(crate) import_externals: bool, } #[turbo_tasks::value_impl] @@ -31,10 +32,15 @@ impl EcmascriptModulePartAsset { /// of a pointer to the full module and the [ModulePart] pointing the part /// of the module. #[turbo_tasks::function] - pub fn new(module: Vc, part: Vc) -> Vc { + pub fn new( + module: Vc, + part: Vc, + import_externals: bool, + ) -> Vc { EcmascriptModulePartAsset { full_module: module, part, + import_externals, } .cell() } @@ -76,6 +82,7 @@ impl Module for EcmascriptModulePartAsset { Vc::upcast(EcmascriptModulePartAsset::new( self.full_module, ModulePart::internal(part_id), + self.import_externals, )), Vc::cell("ecmascript module part".to_string()), ))) diff --git a/crates/turbopack-ecmascript/src/typescript/resolve.rs b/crates/turbopack-ecmascript/src/typescript/resolve.rs index de645580aa50ea..5b41f8b346e5ab 100644 --- a/crates/turbopack-ecmascript/src/typescript/resolve.rs +++ b/crates/turbopack-ecmascript/src/typescript/resolve.rs @@ -135,16 +135,17 @@ async fn resolve_extends( // An empty extends is treated as "./tsconfig" Request::Empty => { let request = Request::parse_string("./tsconfig".to_string()); - Ok(resolve(parent_dir, request, resolve_options).first_source()) + Ok(resolve(parent_dir, + Value::new(ReferenceType::TypeScript(TypeScriptReferenceSubType::Undefined)), request, resolve_options).first_source()) } // All other types are treated as module imports, and potentially joined with // "tsconfig.json". This includes "relative" imports like '.' and '..'. _ => { - let mut result = resolve(parent_dir, request, resolve_options).first_source(); + let mut result = resolve(parent_dir, Value::new(ReferenceType::TypeScript(TypeScriptReferenceSubType::Undefined)), request, resolve_options).first_source(); if result.await?.is_none() { let request = Request::parse_string(format!("{extends}/tsconfig")); - result = resolve(parent_dir, request, resolve_options).first_source(); + result = resolve(parent_dir, Value::new(ReferenceType::TypeScript(TypeScriptReferenceSubType::Undefined)), request, resolve_options).first_source(); } Ok(result) } @@ -157,14 +158,30 @@ async fn resolve_extends_rooted_or_relative( resolve_options: Vc, path: &str, ) -> Result> { - let mut result = resolve(lookup_path, request, resolve_options).first_source(); + let mut result = resolve( + lookup_path, + Value::new(ReferenceType::TypeScript( + TypeScriptReferenceSubType::Undefined, + )), + request, + resolve_options, + ) + .first_source(); // If the file doesn't end with ".json" and we can't find the file, then we have // to try again with it. // https://github.com/microsoft/TypeScript/blob/611a912d/src/compiler/commandLineParser.ts#L3305 if !path.ends_with(".json") && result.await?.is_none() { let request = Request::parse_string(format!("{path}.json")); - result = resolve(lookup_path, request, resolve_options).first_source(); + result = resolve( + lookup_path, + Value::new(ReferenceType::TypeScript( + TypeScriptReferenceSubType::Undefined, + )), + request, + resolve_options, + ) + .first_source(); } Ok(result) } @@ -354,14 +371,35 @@ pub async fn type_resolve( }; let context_path = context_path.resolve().await?; let result = if let Some(types_request) = types_request { - let result1 = resolve(context_path, request, options); + let result1 = resolve( + context_path, + Value::new(ReferenceType::TypeScript( + TypeScriptReferenceSubType::Undefined, + )), + request, + options, + ); if !*result1.is_unresolveable().await? { result1 } else { - resolve(context_path, types_request, options) + resolve( + context_path, + Value::new(ReferenceType::TypeScript( + TypeScriptReferenceSubType::Undefined, + )), + types_request, + options, + ) } } else { - resolve(context_path, request, options) + resolve( + context_path, + Value::new(ReferenceType::TypeScript( + TypeScriptReferenceSubType::Undefined, + )), + request, + options, + ) }; let result = origin .asset_context() @@ -436,7 +474,10 @@ async fn apply_typescript_types_options( .collect(); resolve_options .into_package - .push(ResolveIntoPackage::MainField("types".to_string())); + .push(ResolveIntoPackage::MainField { + field: "types".to_string(), + extensions: Some(vec![".d.ts".to_string(), ".ts".to_string()]), + }); resolve_options .into_package .push(ResolveIntoPackage::Default("index".to_string())); diff --git a/crates/turbopack-ecmascript/src/webpack/mod.rs b/crates/turbopack-ecmascript/src/webpack/mod.rs index eb261540fe31d7..4ba6ecbd359f61 100644 --- a/crates/turbopack-ecmascript/src/webpack/mod.rs +++ b/crates/turbopack-ecmascript/src/webpack/mod.rs @@ -171,6 +171,7 @@ impl ModuleReference for WebpackRuntimeAssetReference { let resolved = resolve( self.origin.origin_path().parent().resolve().await?, + Value::new(ReferenceType::CommonJs(CommonJsReferenceSubType::Undefined)), self.request, options, ); diff --git a/crates/turbopack-node/src/evaluate.rs b/crates/turbopack-node/src/evaluate.rs index 3b62d648b59d47..e8383b4a6b5ec4 100644 --- a/crates/turbopack-node/src/evaluate.rs +++ b/crates/turbopack-node/src/evaluate.rs @@ -126,8 +126,8 @@ pub async fn get_evaluate_pool( runtime_asset.ident().path().join("evaluate.js".to_string()), AssetContent::file( File::from( - "import { run } from 'RUNTIME'; run((...args) => \ - (require('INNER').default(...args)))", + "import { run } from 'RUNTIME'; run(async (...args) => ((await \ + import('INNER')).default(...args)))", ) .into(), ), diff --git a/crates/turbopack/src/lib.rs b/crates/turbopack/src/lib.rs index 2f484e5c8bea70..68452f854d9f32 100644 --- a/crates/turbopack/src/lib.rs +++ b/crates/turbopack/src/lib.rs @@ -459,7 +459,12 @@ impl AssetContext for ModuleAssetContext { ) -> Result> { let context_path = origin_path.parent().resolve().await?; - let result = resolve(context_path, request, resolve_options); + let result = resolve( + context_path, + reference_type.clone(), + request, + resolve_options, + ); let mut result = self.process_resolve_result(result.resolve().await?, reference_type); if *self.is_types_resolving_enabled().await? { diff --git a/crates/turbopack/src/module_options/mod.rs b/crates/turbopack/src/module_options/mod.rs index 4507828115e4cd..b9d4db83b9a371 100644 --- a/crates/turbopack/src/module_options/mod.rs +++ b/crates/turbopack/src/module_options/mod.rs @@ -77,6 +77,7 @@ impl ModuleOptions { execution_context, ref rules, esm_url_rewrite_behavior, + import_externals, use_lightningcss, .. } = *module_options_context.await?; @@ -132,6 +133,7 @@ impl ModuleOptions { split_into_parts: enable_tree_shaking, import_parts: enable_tree_shaking, url_rewrite_behavior: esm_url_rewrite_behavior, + import_externals, ..Default::default() }; diff --git a/crates/turbopack/src/module_options/module_options_context.rs b/crates/turbopack/src/module_options/module_options_context.rs index 74855dbe888546..2f31e0f277ab18 100644 --- a/crates/turbopack/src/module_options/module_options_context.rs +++ b/crates/turbopack/src/module_options/module_options_context.rs @@ -165,6 +165,9 @@ pub struct ModuleOptionsContext { pub placeholder_for_future_extensions: (), pub enable_tree_shaking: bool, pub esm_url_rewrite_behavior: Option, + /// References to externals from ESM imports should use `import()` and make + /// async modules. + pub import_externals: bool, pub use_lightningcss: bool, } diff --git a/crates/turbopack/src/resolve.rs b/crates/turbopack/src/resolve.rs index b8e9883f0a1495..f59e06fc30405c 100644 --- a/crates/turbopack/src/resolve.rs +++ b/crates/turbopack/src/resolve.rs @@ -157,27 +157,28 @@ async fn base_resolve_options( conditions }; + let extensions = if let Some(environment) = emulating { + environment.resolve_extensions().await?.clone_value() + } else { + let mut ext = Vec::new(); + if opt.enable_typescript && opt.enable_react { + ext.push(".tsx".to_string()); + } + if opt.enable_typescript { + ext.push(".ts".to_string()); + } + if opt.enable_react { + ext.push(".jsx".to_string()); + } + ext.push(".js".to_string()); + if opt.enable_node_native_modules { + ext.push(".node".to_string()); + } + ext.push(".json".to_string()); + ext + }; Ok(ResolveOptions { - extensions: if let Some(environment) = emulating { - environment.resolve_extensions().await?.clone_value() - } else { - let mut ext = Vec::new(); - if opt.enable_typescript && opt.enable_react { - ext.push(".tsx".to_string()); - } - if opt.enable_typescript { - ext.push(".ts".to_string()); - } - if opt.enable_react { - ext.push(".jsx".to_string()); - } - ext.push(".js".to_string()); - if opt.enable_node_native_modules { - ext.push(".node".to_string()); - } - ext.push(".json".to_string()); - ext - }, + extensions, modules: if let Some(environment) = emulating { if *environment.resolve_node_modules().await? { vec![ResolveModules::Nested( @@ -203,12 +204,21 @@ async fn base_resolve_options( unspecified_conditions: ConditionValue::Unset, }]; if opt.browser { - resolve_into.push(ResolveIntoPackage::MainField("browser".to_string())); + resolve_into.push(ResolveIntoPackage::MainField { + field: "browser".to_string(), + extensions: None, + }); } if opt.module { - resolve_into.push(ResolveIntoPackage::MainField("module".to_string())); + resolve_into.push(ResolveIntoPackage::MainField { + field: "module".to_string(), + extensions: None, + }); } - resolve_into.push(ResolveIntoPackage::MainField("main".to_string())); + resolve_into.push(ResolveIntoPackage::MainField { + field: "main".to_string(), + extensions: None, + }); resolve_into.push(ResolveIntoPackage::Default("index".to_string())); resolve_into }, diff --git a/crates/turbopack/src/unsupported_sass.rs b/crates/turbopack/src/unsupported_sass.rs index 4a9183202d7abb..14e2d6952e9425 100644 --- a/crates/turbopack/src/unsupported_sass.rs +++ b/crates/turbopack/src/unsupported_sass.rs @@ -1,10 +1,11 @@ //! TODO(WEB-741) Remove this file once Sass is supported. use anyhow::Result; -use turbo_tasks::Vc; +use turbo_tasks::{Value, Vc}; use turbo_tasks_fs::{glob::Glob, FileSystemPath}; use turbopack_core::{ issue::{Issue, IssueExt, IssueSeverity, StyledString}, + reference_type::ReferenceType, resolve::{ parse::Request, plugin::{ResolvePlugin, ResolvePluginCondition}, @@ -38,6 +39,7 @@ impl ResolvePlugin for UnsupportedSassResolvePlugin { &self, fs_path: Vc, lookup_path: Vc, + _reference_type: Value, request: Vc, ) -> Result> { let extension = fs_path.extension().await?;