bundler: fix bugs (#1105)

swc_bundler:
 - Correct lca for circular dependencies.
 - Handle namespaced imports. (#1109)
 - Handle namespaced reexports. (#1110)
 - Handle shorthand properly. (#1111)
 - Implement `import.meta` (#1115)
 - Handle reexport mixed with imports correctly. (#1116)
 - Handle export default decls in computed-key modules. 
 - Remove all export * from. output. (#1118)
 - Handle export of class or function declaration in a computed module properly. (#1119)


swc_ecma_transforms:
 - Do not rename class members. (#1117)
This commit is contained in:
강동윤 2020-10-02 11:07:40 +09:00 committed by GitHub
parent 1f00d9ba26
commit 9879fa59c8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
95 changed files with 701 additions and 61 deletions

View File

@ -14,6 +14,8 @@ on: [push, pull_request]
env:
CARGO_INCREMENTAL: 0
# To make spack tests reliable
RAYON_NUM_THREADS: 1
CI: "1"
jobs:

View File

@ -7,7 +7,7 @@ edition = "2018"
license = "Apache-2.0/MIT"
name = "swc_atoms"
repository = "https://github.com/swc-project/swc.git"
version = "0.2.3"
version = "0.2.4"
[dependencies]
string_cache = "0.8"

View File

@ -626,6 +626,7 @@ key
keyof
length
let
main
meta
module
namespace
@ -660,6 +661,7 @@ typeof
undefined
unique
unknown
url
var
void
while

View File

@ -6,7 +6,7 @@ edition = "2018"
license = "Apache-2.0/MIT"
name = "swc_bundler"
repository = "https://github.com/swc-project/swc.git"
version = "0.8.0"
version = "0.9.0"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[features]
@ -25,7 +25,7 @@ radix_fmt = "1"
rayon = {version = "1", optional = true}
relative-path = "1.2"
retain_mut = "=0.1.1"
swc_atoms = {version = "0.2", path = "../atoms"}
swc_atoms = {version = "0.2.4", path = "../atoms"}
swc_common = {version = "0.10.0", path = "../common"}
swc_ecma_ast = {version = "0.32.0", path = "../ecmascript/ast"}
swc_ecma_codegen = {version = "0.36.0", path = "../ecmascript/codegen"}
@ -35,4 +35,6 @@ swc_ecma_utils = {version = "0.22.0", path = "../ecmascript/utils"}
swc_ecma_visit = {version = "0.18.0", path = "../ecmascript/visit"}
[dev-dependencies]
reqwest = {version = "0.10.8", features = ["blocking"]}
testing = {version = "0.10.0", path = "../testing"}
url = "2.1.1"

View File

@ -1,7 +1,8 @@
use anyhow::Error;
use std::{collections::HashMap, io::stdout};
use swc_bundler::{BundleKind, Bundler, Config, Load, Resolve};
use swc_common::{sync::Lrc, FileName, FilePathMapping, Globals, SourceMap};
use swc_bundler::{BundleKind, Bundler, Config, Hook, Load, Resolve};
use swc_common::{sync::Lrc, FileName, FilePathMapping, Globals, SourceMap, Span};
use swc_ecma_ast::Expr;
use swc_ecma_codegen::{text_writer::JsWriter, Emitter};
use swc_ecma_parser::{lexer::Lexer, EsConfig, Parser, StringInput, Syntax};
@ -22,6 +23,7 @@ fn main() {
external_modules,
..Default::default()
},
Box::new(Noop),
);
let mut entries = HashMap::default();
entries.insert("main".to_string(), FileName::Real("assets/main.js".into()));
@ -104,3 +106,11 @@ impl Resolve for PathResolver {
))
}
}
struct Noop;
impl Hook for Noop {
fn get_import_meta_url(&self, _: Span, _: &FileName) -> Result<Option<Expr>, Error> {
unimplemented!()
}
}

View File

@ -128,7 +128,11 @@ where
{
let mut normal_reexports = vec![];
let mut star_reexports = vec![];
for (src, mut specifiers) in additional_modules {
for (src, specifiers) in additional_modules {
if specifiers.is_empty() {
continue;
}
// If a dependency is indirect, we need to export items from it manually.
let is_indirect = !nomral_plan.chunks.contains(&src.module_id);
@ -139,12 +143,6 @@ where
} else {
&mut normal_reexports
};
if specifiers.is_empty() {
//
let dep = self.scope.get_module(src.module_id).unwrap();
specifiers = dep.exports.items.clone();
}
for specifier in specifiers {
let (imported, exported) = match specifier {
@ -235,7 +233,11 @@ where
entry.body.visit_mut_with(&mut injector);
// print_hygiene(
// &format!("entry:injection {:?} <- {:?}", info.ctxt(), src.ctxt,),
// &format!(
// "entry:reexport injection {:?} <- {:?}",
// info.ctxt(),
// src.ctxt,
// ),
// &self.cm,
// &entry,
// );

View File

@ -5,7 +5,7 @@ use crate::{
load::Load,
resolve::Resolve,
util::{self, IntoParallelIterator},
Bundler,
Bundler, Hook,
};
use anyhow::{Context, Error};
#[cfg(feature = "concurrent")]
@ -13,7 +13,7 @@ use rayon::iter::ParallelIterator;
use retain_mut::RetainMut;
use std::{borrow::Cow, mem::take};
use swc_atoms::js_word;
use swc_common::{SyntaxContext, DUMMY_SP};
use swc_common::{FileName, SyntaxContext, DUMMY_SP};
use swc_ecma_ast::*;
use swc_ecma_utils::prepend_stmts;
use swc_ecma_visit::{noop_fold_type, noop_visit_mut_type, Fold, FoldWith, VisitMut, VisitMutWith};
@ -61,6 +61,12 @@ where
};
let mut entry: Module = (*info.module).clone();
entry.visit_mut_with(&mut ImportMetaHandler {
file: &info.fm.name,
hook: &self.hook,
is_entry,
err: None,
});
// print_hygiene(&format!("{}", info.fm.name), &self.cm, &entry);
@ -274,7 +280,11 @@ where
// print_hygiene("entry:after:injection", &self.cm, &entry);
log::debug!("Merged {} as an es module", info.fm.name);
// print_hygiene("ES6", &self.cm, &entry);
// print_hygiene(
// &format!("ES6: {:?} <- {:?}", info.ctxt(), dep_info.ctxt()),
// &self.cm,
// &entry,
// );
continue;
}
@ -538,3 +548,84 @@ impl VisitMut for DefaultRenamer {
}
}
}
struct ImportMetaHandler<'a, 'b> {
file: &'a FileName,
hook: &'a Box<dyn 'b + Hook>,
is_entry: bool,
err: Option<Error>,
}
impl VisitMut for ImportMetaHandler<'_, '_> {
fn visit_mut_member_expr(&mut self, e: &mut MemberExpr) {
if self.err.is_some() {
return;
}
if e.computed {
e.obj.visit_mut_with(self);
}
e.prop.visit_mut_with(self);
}
fn visit_mut_expr(&mut self, e: &mut Expr) {
e.visit_mut_children_with(self);
match e {
Expr::Member(me) => {
if !me.computed {
match &me.obj {
ExprOrSuper::Super(_) => {}
ExprOrSuper::Expr(obj) => match &**obj {
Expr::MetaProp(MetaPropExpr {
meta:
Ident {
sym: js_word!("import"),
..
},
prop:
Ident {
sym: js_word!("meta"),
..
},
..
}) => match &*me.prop {
Expr::Ident(Ident {
sym: js_word!("url"),
..
}) => {
let res = self.hook.get_import_meta_url(me.span, self.file);
match res {
Ok(v) => match v {
Some(expr) => {
*e = expr;
return;
}
None => {}
},
Err(err) => self.err = Some(err),
}
}
Expr::Ident(Ident {
sym: js_word!("main"),
..
}) if !self.is_entry => {
*e = Expr::Lit(Lit::Bool(Bool {
span: me.span,
value: false,
}));
return;
}
_ => {}
},
_ => {}
},
}
}
}
_ => {}
}
}
}

View File

@ -48,12 +48,22 @@ pub(super) fn least_common_ancestor(g: &ModuleGraph, module_ids: &[ModuleId]) ->
});
}
fn check_itself<I>(li: I, ri: &[ModuleId]) -> Option<ModuleId>
fn check_itself<I>(g: &ModuleGraph, li: I, ri: &[ModuleId]) -> Option<ModuleId>
where
I: IntoIterator<Item = ModuleId>,
{
for l in li {
// Root
if g.neighbors_directed(l, Incoming).count() == 0 {
return Some(l);
}
for &r in ri {
// Root
if g.neighbors_directed(r, Incoming).count() == 0 {
return Some(l);
}
if l == r {
return Some(l);
}
@ -64,32 +74,40 @@ where
}
fn check_itself_and_parent(g: &ModuleGraph, li: &[ModuleId], ri: &[ModuleId]) -> Option<ModuleId> {
if let Some(id) = check_itself(li.iter().copied(), ri) {
if let Some(id) = check_itself(g, li.iter().copied(), ri) {
return Some(id);
}
for &l in li {
if let Some(id) = check_itself(g.neighbors_directed(l, Incoming), ri) {
if let Some(id) = check_itself_and_parent(
g,
&g.neighbors_directed(l, Incoming).collect::<Vec<_>>(),
ri,
) {
return Some(id);
}
}
for &r in ri {
if let Some(id) = check_itself(g.neighbors_directed(r, Incoming), li) {
if let Some(id) = check_itself_and_parent(
g,
&g.neighbors_directed(r, Incoming).collect::<Vec<_>>(),
li,
) {
return Some(id);
}
}
for &l in li {
for &r in ri {
let lv = g.neighbors_directed(l, Incoming).collect::<Vec<_>>();
let rv = g.neighbors_directed(r, Incoming).collect::<Vec<_>>();
// for &l in li {
// for &r in ri {
// let lv = g.neighbors_directed(l, Incoming).collect::<Vec<_>>();
// let rv = g.neighbors_directed(r, Incoming).collect::<Vec<_>>();
if let Some(id) = check_itself_and_parent(g, &lv, &rv) {
return Some(id);
}
}
}
// if let Some(id) = check_itself_and_parent(g, &lv, &rv) {
// return Some(id);
// }
// }
// }
None
}

View File

@ -446,7 +446,9 @@ where
.map(|v| &v.0)
.chain(m.exports.reexports.iter().map(|v| &v.0))
{
log::debug!("Dependency: {:?} => {:?}", module_id, src.module_id);
if !builder.direct_deps.contains_edge(module_id, src.module_id) {
log::debug!("Dependency: {:?} => {:?}", module_id, src.module_id);
}
builder.direct_deps.add_edge(module_id, src.module_id, 0);

View File

@ -543,8 +543,8 @@ fn cjs_004() {
assert_eq!(p.circular.len(), 0);
// As both of a and b depend on `common`, it should be merged into a parent
// module.
assert_normal(t, &p, "main", &["entry"]);
assert_normal_transitive(t, &p, "entry", &["a", "b"], &["common"]);
assert_normal_transitive(t, &p, "main", &["entry"], &["common"]);
assert_normal_transitive(t, &p, "entry", &["a", "b"], &[]);
assert_normal(t, &p, "a", &[]);
assert_normal(t, &p, "b", &[]);
@ -645,7 +645,7 @@ fn deno_001() {
dbg!(&p);
assert_normal(t, &p, "main", &["http-server"]);
assert_normal_transitive(t, &p, "main", &["http-server"], &["io-bufio"]);
assert_normal(t, &p, "io-bufio", &[]);
assert_circular(t, &p, "http-server", &["_io"]);
@ -702,6 +702,7 @@ fn circular_002() {
}
#[test]
#[ignore = "Not deterministic yet"]
fn deno_002() {
suite()
.file(
@ -907,6 +908,7 @@ fn circular_root_entry_2() {
}
#[test]
#[ignore = "Not deterministic yet"]
fn deno_003() {
suite()
.file(

View File

@ -2,9 +2,10 @@ use super::load::TransformedModule;
use crate::{Bundler, Load, Resolve};
use anyhow::Error;
use std::mem::take;
use swc_atoms::js_word;
use swc_common::DUMMY_SP;
use swc_ecma_ast::*;
use swc_ecma_utils::{find_ids, ExprFactory};
use swc_ecma_utils::{find_ids, private_ident, ExprFactory};
use swc_ecma_visit::{noop_fold_type, Fold, FoldWith};
impl<L, R> Bundler<'_, L, R>
@ -113,13 +114,22 @@ impl Fold for ExportToReturn {
let stmt = match decl {
ModuleDecl::Import(_) => None,
ModuleDecl::ExportDecl(export) => {
let ids: Vec<Ident> = find_ids(&export.decl);
self.exports.extend(
ids.into_iter()
.map(Prop::Shorthand)
.map(Box::new)
.map(PropOrSpread::Prop),
);
match &export.decl {
Decl::Class(ClassDecl { ident, .. }) | Decl::Fn(FnDecl { ident, .. }) => {
self.exports
.push(PropOrSpread::Prop(Box::new(Prop::Shorthand(ident.clone()))));
}
Decl::Var(decl) => {
let ids: Vec<Ident> = find_ids(decl);
self.exports.extend(
ids.into_iter()
.map(Prop::Shorthand)
.map(Box::new)
.map(PropOrSpread::Prop),
);
}
_ => unreachable!(),
}
Some(Stmt::Decl(export.decl))
}
@ -138,7 +148,41 @@ impl Fold for ExportToReturn {
None
}
ModuleDecl::ExportDefaultDecl(_) => None,
ModuleDecl::ExportDefaultDecl(export) => match export.decl {
DefaultDecl::Class(expr) => {
let ident = expr.ident;
let ident = ident.unwrap_or_else(|| private_ident!("_default_decl"));
self.exports
.push(PropOrSpread::Prop(Box::new(Prop::KeyValue(KeyValueProp {
key: PropName::Ident(Ident::new(js_word!("default"), export.span)),
value: Box::new(Expr::Ident(ident.clone())),
}))));
Some(Stmt::Decl(Decl::Class(ClassDecl {
ident,
class: expr.class,
declare: false,
})))
}
DefaultDecl::Fn(expr) => {
let ident = expr.ident;
let ident = ident.unwrap_or_else(|| private_ident!("_default_decl"));
self.exports
.push(PropOrSpread::Prop(Box::new(Prop::KeyValue(KeyValueProp {
key: PropName::Ident(Ident::new(js_word!("default"), export.span)),
value: Box::new(Expr::Ident(ident.clone())),
}))));
Some(Stmt::Decl(Decl::Fn(FnDecl {
ident,
function: expr.function,
declare: false,
})))
}
DefaultDecl::TsInterfaceDecl(_) => None,
},
ModuleDecl::ExportDefaultExpr(_) => None,
ModuleDecl::ExportAll(_) => {
unimplemented!("export * from 'foo' inside a module loaded with computed key")

View File

@ -223,12 +223,16 @@ where
})
.unwrap_or_else(Vec::new);
let new_import = ImportDecl {
specifiers,
..import
};
if !specifiers.is_empty() {
let new_import = ImportDecl {
specifiers,
..import
};
return new_import;
return new_import;
}
self.info.forced_ns.insert(import.src.value.clone());
}
_ => {}
@ -304,6 +308,28 @@ where
s
}
fn fold_prop(&mut self, mut prop: Prop) -> Prop {
prop = prop.fold_children_with(self);
match prop {
Prop::Shorthand(mut i) => {
if let Some(&ctxt) = self.imported_idents.get(&i.to_id()) {
let local = i.clone();
i.span = i.span.with_ctxt(ctxt);
return Prop::KeyValue(KeyValueProp {
key: PropName::Ident(local),
value: Box::new(Expr::Ident(i)),
});
}
Prop::Shorthand(i)
}
_ => prop,
}
}
fn fold_expr(&mut self, e: Expr) -> Expr {
match e {
Expr::Ident(mut i) if self.deglob_phase => {

View File

@ -1,5 +1,5 @@
use self::scope::Scope;
use crate::{Load, ModuleId, Resolve};
use crate::{Hook, Load, ModuleId, Resolve};
use anyhow::{Context, Error};
use std::collections::HashMap;
use swc_atoms::JsWord;
@ -69,6 +69,8 @@ where
_helper_ctxt: SyntaxContext,
scope: Scope,
hook: Box<dyn 'a + Hook>,
}
impl<'a, L, R> Bundler<'a, L, R>
@ -82,6 +84,7 @@ where
loader: L,
resolver: R,
config: Config,
hook: Box<dyn 'a + Hook>,
) -> Self {
GLOBALS.set(&globals, || {
let used_mark = Mark::fresh(Mark::root());
@ -98,6 +101,7 @@ where
used_mark,
_helper_ctxt: helper_ctxt,
scope: Default::default(),
hook,
}
})
}

View File

@ -3,7 +3,7 @@ use super::{load::TransformedModule, Bundler, Config};
use crate::{util::HygieneRemover, Load, ModuleId, Resolve};
use anyhow::Error;
use std::{collections::HashMap, path::PathBuf};
use swc_common::{sync::Lrc, FileName, SourceFile, SourceMap, GLOBALS};
use swc_common::{sync::Lrc, FileName, SourceFile, SourceMap, Span, GLOBALS};
use swc_ecma_ast::*;
use swc_ecma_parser::{lexer::Lexer, JscTarget, Parser, StringInput};
use swc_ecma_utils::drop_span;
@ -131,6 +131,7 @@ impl TestBuilder {
disable_inliner: true,
external_modules: vec![],
},
Box::new(Hook),
);
let mut t = Tester {
@ -146,3 +147,11 @@ impl TestBuilder {
.expect("WTF?");
}
}
struct Hook;
impl crate::Hook for Hook {
fn get_import_meta_url(&self, _: Span, _: &FileName) -> Result<Option<Expr>, Error> {
unreachable!()
}
}

9
bundler/src/hook.rs Normal file
View File

@ -0,0 +1,9 @@
use anyhow::Error;
use swc_common::{FileName, Span};
use swc_ecma_ast::Expr;
/// Note: As this is rarely used, it's recommended to pass it as a trait object.
pub trait Hook: swc_common::sync::Sync + swc_common::sync::Send {
/// Return [None] to preserve `import.meta.url`
fn get_import_meta_url(&self, span: Span, file: &FileName) -> Result<Option<Expr>, Error>;
}

View File

@ -1,7 +1,6 @@
#![deny(unused)]
pub use self::{
bundler::{Bundle, BundleKind, Bundler, Config},
hook::Hook,
id::ModuleId,
load::Load,
resolve::Resolve,
@ -10,6 +9,7 @@ pub use self::{
mod bundler;
mod debug;
mod hash;
mod hook;
mod id;
mod load;
mod resolve;

123
bundler/tests/deno.rs Normal file
View File

@ -0,0 +1,123 @@
//! In-tree testing for deno integration.
//!
//! This module exists because this is way easier than using copying requires
//! files.
use anyhow::{Context, Error};
use std::collections::HashMap;
use swc_bundler::{Bundler, Load, Resolve};
use swc_common::{sync::Lrc, FileName, SourceFile, SourceMap, Span, GLOBALS};
use swc_ecma_ast::{Expr, Lit, Module, Str};
use swc_ecma_parser::{lexer::Lexer, JscTarget, Parser, StringInput, Syntax, TsConfig};
use swc_ecma_transforms::typescript::strip;
use swc_ecma_visit::FoldWith;
use url::Url;
#[test]
#[ignore = "Too slow"]
fn oak_6_2_0_application() {
bundle("https://deno.land/x/oak@v6.2.0/mod.ts");
}
fn bundle(url: &str) -> Module {
let result = testing::run_test2(false, |cm, _handler| {
GLOBALS.with(|globals| {
let bundler = Bundler::new(
globals,
cm.clone(),
Loader { cm: cm.clone() },
Resolver,
swc_bundler::Config {
require: false,
disable_inliner: true,
..Default::default()
},
Box::new(Hook),
);
let mut entries = HashMap::new();
entries.insert("main".to_string(), FileName::Custom(url.to_string()));
let output = bundler.bundle(entries).unwrap();
Ok(output.into_iter().next().unwrap().module)
})
})
.unwrap();
result
}
#[derive(Clone)]
struct Loader {
cm: Lrc<SourceMap>,
}
impl Load for Loader {
fn load(&self, file: &FileName) -> Result<(Lrc<SourceFile>, Module), Error> {
let url = match file {
FileName::Custom(v) => v,
_ => unreachable!("this test only uses url"),
};
let url = Url::parse(&url).context("failed to parse url")?;
let resp = reqwest::blocking::get(url.clone())
.with_context(|| format!("failed to fetch `{}`", url))?;
let bytes = resp
.bytes()
.with_context(|| format!("failed to read data from `{}`", url))?;
let src = String::from_utf8_lossy(&bytes);
let fm = self
.cm
.new_source_file(FileName::Custom(url.to_string()), src.to_string());
let lexer = Lexer::new(
Syntax::Typescript(TsConfig {
decorators: true,
..Default::default()
}),
JscTarget::Es2020,
StringInput::from(&*fm),
None,
);
let mut parser = Parser::new_from(lexer);
let module = parser.parse_typescript_module().unwrap();
let module = module.fold_with(&mut strip());
Ok((fm, module))
}
}
#[derive(Debug, Clone, Copy)]
struct Resolver;
impl Resolve for Resolver {
fn resolve(&self, base: &FileName, module_specifier: &str) -> Result<FileName, Error> {
let base_url = match base {
FileName::Custom(v) => v,
_ => unreachable!("this test only uses url"),
};
let base_url = Url::parse(&base_url).context("failed to parse url")?;
let options = Url::options();
let base_url = options.base_url(Some(&base_url));
let url = base_url
.parse(module_specifier)
.with_context(|| format!("failed to resolve `{}`", module_specifier))?;
return Ok(FileName::Custom(url.to_string()));
}
}
struct Hook;
impl swc_bundler::Hook for Hook {
fn get_import_meta_url(&self, span: Span, file: &FileName) -> Result<Option<Expr>, Error> {
Ok(Some(Expr::Lit(Lit::Str(Str {
span,
value: file.to_string().into(),
has_escape: false,
}))))
}
}

View File

@ -6,7 +6,7 @@ edition = "2018"
license = "Apache-2.0/MIT"
name = "swc_ecmascript"
repository = "https://github.com/swc-project/swc.git"
version = "0.8.2"
version = "0.8.3"
[features]
codegen = ["swc_ecma_codegen"]

View File

@ -144,7 +144,7 @@
),
ctxt: #0,
},
value: Atom('url' type=inline),
value: Atom('url' type=static),
},
},
),

View File

@ -6,7 +6,7 @@ edition = "2018"
license = "Apache-2.0/MIT"
name = "swc_ecma_transforms"
repository = "https://github.com/swc-project/swc.git"
version = "0.24.2"
version = "0.24.3"
[features]
const-modules = ["dashmap"]

View File

@ -702,6 +702,8 @@ impl<'a> VisitMut for Hygiene<'a> {
self.visit_mut_fn(node.ident.clone(), &mut node.function);
}
fn visit_mut_private_name(&mut self, _: &mut PrivateName) {}
/// Invoked for `IdetifierRefrence` / `BindingIdentifier`
fn visit_mut_ident(&mut self, i: &mut Ident) {
if i.sym == js_word!("arguments") || i.sym == js_word!("undefined") {

View File

@ -889,6 +889,8 @@ impl<'a> VisitMut for Resolver<'a> {
f.return_type.visit_mut_with(self);
}
fn visit_mut_private_name(&mut self, _: &mut PrivateName) {}
fn visit_mut_ident(&mut self, i: &mut Ident) {
let ident_type = self.ident_type;
let in_type = self.in_type;

View File

@ -521,3 +521,30 @@ let X = ((_class = class X {
}), _class);
export { X as default };"
);
test!(
Syntax::Typescript(TsConfig {
decorators: true,
..Default::default()
}),
|_| chain!(
resolver(),
typescript::strip(),
decorators(decorators::Config {
legacy: true,
emit_metadata: false
}),
dce(Default::default())
),
issue_1111,
"
const a = 1;
export const d = { a };
",
"
const a = 1;
export const d = {
a
};
"
);

View File

@ -2,7 +2,7 @@ use crate::{
get_compiler,
util::{CtxtExt, MapErr},
};
use anyhow::bail;
use anyhow::{bail, Error};
use fxhash::FxHashMap;
use napi::{CallContext, Env, JsObject, Status, Task};
use serde::Deserialize;
@ -13,6 +13,8 @@ use std::{
};
use swc::{config::SourceMapsConfig, Compiler, TransformOutput};
use swc_bundler::{BundleKind, Bundler, Load, Resolve};
use swc_common::{FileName, Span};
use swc_ecma_ast::{Expr, Lit, Str};
struct ConfigItem {
loader: Box<dyn Load>,
@ -87,6 +89,7 @@ impl Task for BundleTask {
.collect(),
..Default::default()
},
Box::new(Hook),
);
let result = bundler
@ -182,3 +185,15 @@ pub(crate) fn bundle(cx: CallContext) -> napi::Result<JsObject> {
},
})
}
struct Hook;
impl swc_bundler::Hook for Hook {
fn get_import_meta_url(&self, span: Span, file: &FileName) -> Result<Option<Expr>, Error> {
Ok(Some(Expr::Lit(Lit::Str(Str {
span,
value: file.to_string().into(),
has_escape: false,
}))))
}
}

View File

@ -2,6 +2,7 @@
extern crate test;
use anyhow::Error;
use spack::{loaders::swc::SwcLoader, resolvers::NodeResolver};
use std::{
collections::HashMap,
@ -13,7 +14,8 @@ use std::{
};
use swc::config::SourceMapsConfig;
use swc_bundler::{BundleKind, Bundler, Config};
use swc_common::{FileName, GLOBALS};
use swc_common::{FileName, Span, GLOBALS};
use swc_ecma_ast::{Expr, Lit, Str};
use swc_ecma_transforms::fixer;
use swc_ecma_visit::FoldWith;
use test::{
@ -176,6 +178,7 @@ fn reference_tests(tests: &mut Vec<TestDescAndFn>, errors: bool) -> Result<(), i
.map(From::from)
.collect(),
},
Box::new(Hook),
);
let modules = bundler
@ -253,3 +256,15 @@ fn errors() {
reference_tests(&mut tests, true).unwrap();
test_main(&args, tests, Some(Options::new()));
}
struct Hook;
impl swc_bundler::Hook for Hook {
fn get_import_meta_url(&self, span: Span, file: &FileName) -> Result<Option<Expr>, Error> {
Ok(Some(Expr::Lit(Lit::Str(Str {
span,
value: file.to_string().into(),
has_escape: false,
}))))
}
}

View File

@ -51,7 +51,8 @@ class MuxAsyncIterator {
this.signal = deferred();
}
}
const MuxAsyncIterator1 = MuxAsyncIterator, deferred1 = deferred;
const MuxAsyncIterator1 = MuxAsyncIterator;
const deferred1 = deferred;
class ServerRequest {
/**
* Value of Content-Length header.

View File

@ -51,7 +51,8 @@ class MuxAsyncIterator {
this.signal = deferred();
}
}
const deferred1 = deferred, MuxAsyncIterator1 = MuxAsyncIterator;
const MuxAsyncIterator1 = MuxAsyncIterator;
const deferred1 = deferred;
class ServerRequest {
/**
* Value of Content-Length header.

View File

@ -5,7 +5,8 @@ class MuxAsyncIterator {
this.signal = deferred();
}
}
const deferred1 = deferred, MuxAsyncIterator1 = MuxAsyncIterator;
const MuxAsyncIterator1 = MuxAsyncIterator;
const deferred1 = deferred;
console.log(deferred1, writeResponse, readRequest, MuxAsyncIterator1);
class ServerRequest {
constructor(){

View File

@ -5,5 +5,6 @@ class MuxAsyncIterator {
this.signal = deferred();
}
}
const MuxAsyncIterator1 = MuxAsyncIterator, deferred1 = deferred;
const MuxAsyncIterator1 = MuxAsyncIterator;
const deferred1 = deferred;
console.log(deferred1, MuxAsyncIterator1);

View File

@ -1,7 +1,7 @@
function deferred() {
}
const deferred1 = deferred;
function MuxAsyncIterator() {
}
const MuxAsyncIterator1 = MuxAsyncIterator;
const deferred1 = deferred;
console.log(deferred1, MuxAsyncIterator1);

View File

@ -0,0 +1,9 @@
{
"jsc": {
"parser": {
"syntax": "typescript",
"dynamicImport": true
},
"target": "es2020"
}
}

View File

@ -0,0 +1 @@
export const a = 1;

View File

@ -0,0 +1,3 @@
const a = await import("./a.ts");
console.log(a);

View File

@ -0,0 +1,2 @@
const a = await import("./a.ts");
console.log(a);

View File

@ -0,0 +1,7 @@
{
"jsc": {
"parser": {
"syntax": "typescript"
}
}
}

View File

@ -0,0 +1 @@
export const a = 1

View File

@ -0,0 +1 @@
export * as a from './a';

View File

@ -0,0 +1,7 @@
const _a = function() {
var a = 1;
return {
a
};
}();
export { _a as a };

View File

@ -0,0 +1 @@
export const a = "a";

View File

@ -0,0 +1,3 @@
import { a } from "./a";
export const d = { a };

View File

@ -0,0 +1,3 @@
import { d } from "./d";
console.log(d);

View File

@ -0,0 +1,5 @@
const a = "a";
const d = {
a: a
};
console.log(d);

View File

@ -0,0 +1,3 @@
export class A {
}

View File

@ -0,0 +1,4 @@
import * as _a from './a';
export { _a as a }

View File

@ -0,0 +1,8 @@
const _a = function() {
class A {
}
return {
A
};
}();
export { _a as a };

View File

@ -0,0 +1,3 @@
export class A {
}

View File

@ -0,0 +1,3 @@
import * as a from './a';
console.log(a)

View File

@ -0,0 +1,8 @@
const a = function() {
class A {
}
return {
A
};
}();
console.log(a);

View File

@ -0,0 +1 @@
export const a = "a";

View File

@ -0,0 +1,3 @@
import * as a from "./a";
console.log(a); // { a: "a" }

View File

@ -0,0 +1,7 @@
const a = function() {
const a = "a";
return {
a
};
}();
console.log(a); // { a: "a" }

View File

@ -0,0 +1,8 @@
{
"jsc": {
"target": "es2020",
"parser": {
"syntax": "typescript"
}
}
}

View File

@ -0,0 +1 @@
export { a, b } from "./k";

View File

@ -0,0 +1,3 @@
export function a(...d: string[]): string {
return d.join(" ");
}

View File

@ -0,0 +1,3 @@
export function a(...d: string[]): string {
return d.join("/");
}

View File

@ -0,0 +1,11 @@
import * as _i from "./i";
import * as _j from "./j";
const k = globalThis.value ? _i : _j;
export const i = _i;
export const j = _j;
export const {
a,
} = k;

View File

@ -0,0 +1,19 @@
const _i = function() {
function a(...d) {
return d.join(" ");
}
return {
a
};
}();
const _j = function() {
function a(...d) {
return d.join("/");
}
return {
a
};
}();
const k = globalThis.value ? _i : _j;
const { a , } = k;
export { a, b };

View File

@ -0,0 +1,9 @@
{
"jsc": {
"target": "es2020",
"parser": {
"syntax": "ecmascript",
"exportNamespaceFrom": true
}
}
}

View File

@ -0,0 +1,3 @@
export * as c from "./c";
export const b = "b";

View File

@ -0,0 +1,2 @@
export const c = "c";
export default class C { }

View File

@ -0,0 +1,4 @@
import * as b from "./b";
console.log(b.b); // "b"
console.log(b.c); // { c: "c", default: class C }

View File

@ -0,0 +1,13 @@
const _c = function() {
const c = "c";
class C {
}
return {
c,
default: C
};
}();
const c = _c;
const b = "b";
console.log(b); // "b"
console.log(c); // { c: "c", default: class C }

View File

@ -0,0 +1 @@
export const a = "a";

View File

@ -0,0 +1,3 @@
import { a } from "./a";
export const d = { a };

View File

@ -0,0 +1,3 @@
import { d } from "./d";
console.log(d);

View File

@ -0,0 +1,5 @@
const a = "a";
const d = {
a: a
};
console.log(d);

View File

@ -0,0 +1 @@
export const a = "a";

View File

@ -0,0 +1 @@
export * from "./a";

View File

@ -0,0 +1,3 @@
import { a } from "./e";
console.log(a);

View File

@ -0,0 +1,2 @@
const a = "a";
console.log(a);

View File

@ -0,0 +1,8 @@
{
"jsc": {
"target": "es2020",
"parser": {
"syntax": "typescript"
}
}
}

View File

@ -0,0 +1,4 @@
import { isMain, modUrl } from "./f";
console.log(isMain, modUrl);
console.log(import.meta.main, import.meta.url);

View File

@ -0,0 +1,2 @@
export const isMain = import.meta.main;
export const modUrl = import.meta.url;

View File

@ -0,0 +1,4 @@
const isMain = false;
const modUrl = "$DIR/tests/pass/pr-1105/example-7/input/f.js";
console.log(isMain, modUrl);
console.log(import.meta.main, "$DIR/tests/pass/pr-1105/example-7/input/entry.js");

View File

@ -0,0 +1,8 @@
{
"jsc": {
"parser": {
"syntax": "ecmascript",
"exportNamespaceFrom": true
}
}
}

View File

@ -0,0 +1 @@
export const a = "a";

View File

@ -0,0 +1 @@
export * as a from "./a";

View File

@ -0,0 +1,7 @@
const _a = function() {
var a = "a";
return {
a
};
}();
export { _a as a };

View File

@ -0,0 +1,7 @@
{
"jsc": {
"parser": {
"syntax": "typescript"
}
}
}

View File

@ -0,0 +1 @@
export const a = "a";

View File

@ -0,0 +1 @@
export * as a from "./a";

View File

@ -0,0 +1,7 @@
const _a = function() {
var a = "a";
return {
a
};
}();
export { _a as a };