diff --git a/crates/swc/tests/fixture/issues-1xxx/issue-1682/case1/output/index.ts b/crates/swc/tests/fixture/issues-1xxx/issue-1682/case1/output/index.ts index be30d320ce5..d28b9cd6fc5 100644 --- a/crates/swc/tests/fixture/issues-1xxx/issue-1682/case1/output/index.ts +++ b/crates/swc/tests/fixture/issues-1xxx/issue-1682/case1/output/index.ts @@ -6,8 +6,8 @@ exports.getPackage = getPackage; var swcHelpers = require("@swc/helpers"); var _path = require("path"); async function getPackage() { - const pkg = await Promise.resolve().then(function() { - return swcHelpers.interopRequireWildcard(require((0, _path).join(process.cwd(), 'package.json'))); + const pkg = await Promise.resolve(`${(0, _path).join(process.cwd(), 'package.json')}`).then(function(s) { + return swcHelpers.interopRequireWildcard(require(s)); }); return pkg.default || pkg; } diff --git a/crates/swc/tests/fixture/issues-1xxx/issue-1682/case2/output/index.ts b/crates/swc/tests/fixture/issues-1xxx/issue-1682/case2/output/index.ts index be30d320ce5..d28b9cd6fc5 100644 --- a/crates/swc/tests/fixture/issues-1xxx/issue-1682/case2/output/index.ts +++ b/crates/swc/tests/fixture/issues-1xxx/issue-1682/case2/output/index.ts @@ -6,8 +6,8 @@ exports.getPackage = getPackage; var swcHelpers = require("@swc/helpers"); var _path = require("path"); async function getPackage() { - const pkg = await Promise.resolve().then(function() { - return swcHelpers.interopRequireWildcard(require((0, _path).join(process.cwd(), 'package.json'))); + const pkg = await Promise.resolve(`${(0, _path).join(process.cwd(), 'package.json')}`).then(function(s) { + return swcHelpers.interopRequireWildcard(require(s)); }); return pkg.default || pkg; } diff --git a/crates/swc_bundler/src/bundler/chunk/computed_key.rs b/crates/swc_bundler/src/bundler/chunk/computed_key.rs index c1cb25e35c7..8858aa4f137 100644 --- a/crates/swc_bundler/src/bundler/chunk/computed_key.rs +++ b/crates/swc_bundler/src/bundler/chunk/computed_key.rs @@ -4,8 +4,8 @@ use anyhow::{bail, Error}; use swc_atoms::js_word; use swc_common::{SyntaxContext, DUMMY_SP}; use swc_ecma_ast::*; -use swc_ecma_utils::{find_ids, private_ident, ExprFactory}; -use swc_ecma_visit::{noop_fold_type, noop_visit_type, Fold, Visit}; +use swc_ecma_utils::{contains_top_level_await, find_ids, private_ident, ExprFactory}; +use swc_ecma_visit::{noop_fold_type, Fold}; use crate::{bundler::chunk::merge::Ctx, modules::Modules, Bundler, Load, ModuleId, Resolve}; @@ -43,11 +43,7 @@ where }; let injected_ctxt = self.injected_ctxt; - let is_async = { - let mut v = TopLevelAwaitFinder { found: false }; - module.visit_with(&mut v); - v.found - }; + let is_async = module.iter().any(|m| contains_top_level_await(m.1)); let mut additional_items = vec![]; @@ -182,24 +178,6 @@ where } } -struct TopLevelAwaitFinder { - found: bool, -} - -impl Visit for TopLevelAwaitFinder { - noop_visit_type!(); - - fn visit_function(&mut self, _: &Function) {} - - fn visit_arrow_expr(&mut self, _: &ArrowExpr) {} - - fn visit_class_member(&mut self, _: &ClassMember) {} - - fn visit_await_expr(&mut self, _: &AwaitExpr) { - self.found = true; - } -} - struct ExportToReturn { return_props: Vec, synthesized_ctxt: SyntaxContext, diff --git a/crates/swc_bundler/src/bundler/finalize.rs b/crates/swc_bundler/src/bundler/finalize.rs index b32c132d764..778356056da 100644 --- a/crates/swc_bundler/src/bundler/finalize.rs +++ b/crates/swc_bundler/src/bundler/finalize.rs @@ -11,8 +11,8 @@ use swc_ecma_transforms_base::{ helpers::{inject_helpers, HELPERS}, hygiene::hygiene, }; -use swc_ecma_utils::{find_ids, private_ident, ExprFactory}; -use swc_ecma_visit::{noop_fold_type, noop_visit_type, Fold, FoldWith, Visit, VisitWith}; +use swc_ecma_utils::{contains_top_level_await, find_ids, private_ident, ExprFactory}; +use swc_ecma_visit::{noop_fold_type, Fold, FoldWith}; use crate::{hash::calc_hash, Bundle, BundleKind, Bundler, Load, ModuleType, Resolve}; @@ -148,10 +148,7 @@ where return module; } - let mut top_level_await_finder = TopLevelAwaitFinder::default(); - module.visit_with(&mut top_level_await_finder); - - let is_async = top_level_await_finder.found; + let is_async = contains_top_level_await(&module); // Properties of returned object let mut props = vec![]; @@ -365,21 +362,6 @@ where } } -#[derive(Default)] -struct TopLevelAwaitFinder { - found: bool, -} - -impl Visit for TopLevelAwaitFinder { - noop_visit_type!(); - - fn visit_stmts(&mut self, _: &[Stmt]) {} - - fn visit_await_expr(&mut self, _: &AwaitExpr) { - self.found = true; - } -} - /// Import renamer. This pass changes import path. struct Renamer<'a, R> where diff --git a/crates/swc_ecma_transforms_module/src/common_js.rs b/crates/swc_ecma_transforms_module/src/common_js.rs index 686d7871d39..9797455d99d 100644 --- a/crates/swc_ecma_transforms_module/src/common_js.rs +++ b/crates/swc_ecma_transforms_module/src/common_js.rs @@ -1062,13 +1062,55 @@ impl ModulePass for CommonJs { /// ``` pub(super) fn handle_dynamic_import( span: Span, - args: Vec, + mut args: Vec, es_module_interop: bool, ) -> Expr { + // there's a evalution order problem here + let (resolve_arg, then_arg, require_arg) = if let Expr::Lit(Lit::Str(_)) = &*args[0].expr { + (Vec::new(), Vec::new(), args) + } else { + let arg = private_ident!("s"); + let rest = args.split_off(1); + let import = args.into_iter().next().unwrap(); + ( + vec![ExprOrSpread { + spread: None, + expr: if import.expr.is_tpl() { + import.expr + } else { + Box::new(Expr::Tpl(Tpl { + span: DUMMY_SP, + exprs: vec![import.expr], + quasis: vec![ + TplElement { + span: DUMMY_SP, + tail: true, + cooked: None, + raw: "".into(), + }, + TplElement { + span: DUMMY_SP, + tail: true, + cooked: None, + raw: "".into(), + }, + ], + })) + }, + }], + vec![arg.clone().into()], + { + let mut require_arg = vec![arg.as_arg()]; + require_arg.extend(rest); + require_arg + }, + ) + }; + let resolve_call = CallExpr { span: DUMMY_SP, callee: member_expr!(DUMMY_SP, Promise.resolve).as_callee(), - args: Default::default(), + args: resolve_arg, type_args: Default::default(), }; // Promise.resolve().then @@ -1083,7 +1125,7 @@ pub(super) fn handle_dynamic_import( ident: None, function: Function { span: DUMMY_SP, - params: vec![], + params: then_arg, is_generator: false, is_async: false, type_params: Default::default(), @@ -1097,7 +1139,7 @@ pub(super) fn handle_dynamic_import( let mut expr = Box::new(Expr::Call(CallExpr { span: DUMMY_SP, callee: quote_ident!("require").as_callee(), - args, + args: require_arg, type_args: Default::default(), })); diff --git a/crates/swc_ecma_transforms_module/src/util.rs b/crates/swc_ecma_transforms_module/src/util.rs index 766296ec3dd..c4eb7e2001a 100644 --- a/crates/swc_ecma_transforms_module/src/util.rs +++ b/crates/swc_ecma_transforms_module/src/util.rs @@ -571,8 +571,8 @@ impl Scope { // TODO: import assertion && args.len() == 1 => { - let expr = args.pop().unwrap().expr.fold_with(folder); - let expr = match &*expr { + let mut expr = args.pop().unwrap().expr.fold_with(folder); + let expr = match &mut *expr { Expr::Lit(Lit::Str(s)) => { let src = folder.resolver().resolve(s.value.clone()); @@ -582,15 +582,11 @@ impl Scope { ..s.clone() }))) } - _ => expr, - }; - - let expr = match *expr { - Expr::Ident(ident) => match Self::fold_ident(folder, ident) { + Expr::Ident(ident) => Box::new(match Self::fold_ident(folder, ident.take()) { Ok(expr) => expr, Err(ident) => Expr::Ident(ident), - }, - expr => expr, + }), + _ => expr, }; folder.make_dynamic_import(span, vec![expr.as_arg()]) diff --git a/crates/swc_ecma_transforms_module/tests/common_js.rs b/crates/swc_ecma_transforms_module/tests/common_js.rs index 2c6372f0ddf..a3939b77312 100644 --- a/crates/swc_ecma_transforms_module/tests/common_js.rs +++ b/crates/swc_ecma_transforms_module/tests/common_js.rs @@ -4411,8 +4411,8 @@ test!( ", r#""use strict"; var _bar = require("bar"); - Promise.resolve().then(function() { - return _interopRequireWildcard(require(_bar.foo)); + Promise.resolve(`${_bar.foo}`).then(function(s) { + return _interopRequireWildcard(require(s)); }); "# ); @@ -4429,8 +4429,8 @@ test!( ", r#""use strict"; var _bar = _interopRequireDefault(require("bar")); - Promise.resolve().then(function() { - return _interopRequireWildcard(require(_bar.default)); + Promise.resolve(`${_bar.default}`).then(function(s) { + return _interopRequireWildcard(require(s)); }); "# ); @@ -4447,8 +4447,8 @@ test!( ", r#""use strict"; var _bar = require("bar"); - Promise.resolve().then(function() { - return _interopRequireWildcard(require(`world/${(0, _bar).foo(baz)}.js`)); + Promise.resolve(`world/${(0, _bar).foo(baz)}.js`).then(function(s) { + return _interopRequireWildcard(require(s)); }); "# ); @@ -5393,6 +5393,21 @@ test!( " ); +test!( + syntax(), + |_| tr(Config { + ..Default::default() + }), + issue_4253, + "let pipeline = await import(await resolve(file))", + " +\"use strict\"; +let pipeline = await Promise.resolve(`${await resolve(file)}`).then(function(s) { + return _interopRequireWildcard(require(s)); +}); +" +); + #[testing::fixture("tests/fixture/commonjs/**/input.js")] fn fixture(input: PathBuf) { let dir = input.parent().unwrap().to_path_buf(); diff --git a/crates/swc_ecma_utils/src/lib.rs b/crates/swc_ecma_utils/src/lib.rs index 13138420cf9..16c06fc7557 100644 --- a/crates/swc_ecma_utils/src/lib.rs +++ b/crates/swc_ecma_utils/src/lib.rs @@ -2375,6 +2375,48 @@ where v.bindings } +pub struct TopLevelAwait { + found: bool, +} + +impl Visit for TopLevelAwait { + noop_visit_type!(); + + fn visit_stmts(&mut self, _: &[Stmt]) {} + + fn visit_param(&mut self, _: &Param) {} + + fn visit_function(&mut self, _: &Function) {} + + fn visit_arrow_expr(&mut self, _: &ArrowExpr) {} + + fn visit_class_member(&mut self, prop: &ClassMember) { + match prop { + ClassMember::ClassProp(ClassProp { + key: PropName::Computed(computed), + .. + }) + | ClassMember::Method(ClassMethod { + key: PropName::Computed(computed), + .. + }) => computed.visit_children_with(self), + _ => (), + }; + } + + fn visit_await_expr(&mut self, _: &AwaitExpr) { + self.found = true; + } +} + +pub fn contains_top_level_await>(t: &V) -> bool { + let mut finder = TopLevelAwait { found: false }; + + t.visit_with(&mut finder); + + finder.found +} + #[cfg(test)] mod test { use swc_common::{input::StringInput, BytePos};