fix(es/module): Support top-level await in dynamic imports (#4277)

This commit is contained in:
Austaras 2022-04-08 14:28:10 +08:00 committed by GitHub
parent 9feabcd145
commit 720244fff9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 124 additions and 69 deletions

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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<PropOrSpread>,
synthesized_ctxt: SyntaxContext,

View File

@ -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

View File

@ -1062,13 +1062,55 @@ impl ModulePass for CommonJs {
/// ```
pub(super) fn handle_dynamic_import(
span: Span,
args: Vec<ExprOrSpread>,
mut args: Vec<ExprOrSpread>,
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(),
}));

View File

@ -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()])

View File

@ -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();

View File

@ -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<V: VisitWith<TopLevelAwait>>(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};