mirror of
https://github.com/swc-project/swc.git
synced 2024-12-29 16:42:28 +03:00
bundler: Handle computed accesses correctly (#1159)
swc_bundler: - Handle computed accesses to imports correctly. - Make planning deterministic. - Prefer exports over imports while planning. - Fix https://deno.land/x/oak@v6.3.1/examples/server.ts
This commit is contained in:
parent
a5e6242601
commit
ad7cb6544d
@ -6,7 +6,7 @@ edition = "2018"
|
|||||||
license = "Apache-2.0/MIT"
|
license = "Apache-2.0/MIT"
|
||||||
name = "swc_bundler"
|
name = "swc_bundler"
|
||||||
repository = "https://github.com/swc-project/swc.git"
|
repository = "https://github.com/swc-project/swc.git"
|
||||||
version = "0.11.0"
|
version = "0.11.1"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
[features]
|
[features]
|
||||||
@ -36,6 +36,7 @@ swc_ecma_visit = {version = "0.19.0", path = "../ecmascript/visit"}
|
|||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
reqwest = {version = "0.10.8", features = ["blocking"]}
|
reqwest = {version = "0.10.8", features = ["blocking"]}
|
||||||
|
tempfile = "3.1.0"
|
||||||
testing = {version = "0.10.0", path = "../testing"}
|
testing = {version = "0.10.0", path = "../testing"}
|
||||||
url = "2.1.1"
|
url = "2.1.1"
|
||||||
walkdir = "2"
|
walkdir = "2"
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use super::load::TransformedModule;
|
use super::plan::Plan;
|
||||||
use crate::{Bundler, Load, Resolve};
|
use crate::{bundler::load::TransformedModule, util::CHashSet, Bundler, Load, ModuleId, Resolve};
|
||||||
use anyhow::Error;
|
use anyhow::Error;
|
||||||
use std::mem::take;
|
use std::mem::take;
|
||||||
use swc_atoms::js_word;
|
use swc_atoms::js_word;
|
||||||
@ -31,20 +31,27 @@ where
|
|||||||
/// ```
|
/// ```
|
||||||
pub(super) fn wrap_esm_as_a_var(
|
pub(super) fn wrap_esm_as_a_var(
|
||||||
&self,
|
&self,
|
||||||
_info: &TransformedModule,
|
plan: &Plan,
|
||||||
module: Module,
|
module: Module,
|
||||||
|
info: &TransformedModule,
|
||||||
|
merged: &CHashSet<ModuleId>,
|
||||||
id: Ident,
|
id: Ident,
|
||||||
) -> Result<Module, Error> {
|
) -> Result<Module, Error> {
|
||||||
let span = module.span;
|
let span = module.span;
|
||||||
|
|
||||||
|
let mut module_items = vec![];
|
||||||
|
|
||||||
let stmts = {
|
let stmts = {
|
||||||
let mut module = module.fold_with(&mut ExportToReturn::default());
|
let mut module = module.fold_with(&mut ExportToReturn::default());
|
||||||
|
|
||||||
take(&mut module.body)
|
take(&mut module.body)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|v| match v {
|
.filter_map(|v| match v {
|
||||||
ModuleItem::ModuleDecl(_) => unreachable!(),
|
ModuleItem::Stmt(s) => Some(s),
|
||||||
ModuleItem::Stmt(s) => s,
|
_ => {
|
||||||
|
module_items.push(v);
|
||||||
|
None
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
};
|
};
|
||||||
@ -85,11 +92,26 @@ where
|
|||||||
}],
|
}],
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(Module {
|
module_items.push(ModuleItem::Stmt(Stmt::Decl(Decl::Var(var_decl))));
|
||||||
|
|
||||||
|
let module = Module {
|
||||||
span: DUMMY_SP,
|
span: DUMMY_SP,
|
||||||
shebang: None,
|
shebang: None,
|
||||||
body: vec![ModuleItem::Stmt(Stmt::Decl(Decl::Var(var_decl)))],
|
body: module_items,
|
||||||
})
|
};
|
||||||
|
|
||||||
|
let module_plan;
|
||||||
|
let module_plan = match plan.normal.get(&info.id) {
|
||||||
|
Some(v) => v,
|
||||||
|
None => {
|
||||||
|
module_plan = Default::default();
|
||||||
|
&module_plan
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let module = self.merge_imports(&plan, module_plan, module, info, merged, false)?;
|
||||||
|
// print_hygiene("Imports", &self.cm, &module);
|
||||||
|
|
||||||
|
Ok(module)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -112,7 +134,7 @@ impl Fold for ExportToReturn {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let stmt = match decl {
|
let stmt = match decl {
|
||||||
ModuleDecl::Import(_) => None,
|
ModuleDecl::Import(_) => return ModuleItem::ModuleDecl(decl),
|
||||||
ModuleDecl::ExportDecl(export) => {
|
ModuleDecl::ExportDecl(export) => {
|
||||||
match &export.decl {
|
match &export.decl {
|
||||||
Decl::Class(ClassDecl { ident, .. }) | Decl::Fn(FnDecl { ident, .. }) => {
|
Decl::Class(ClassDecl { ident, .. }) | Decl::Fn(FnDecl { ident, .. }) => {
|
@ -62,6 +62,7 @@ where
|
|||||||
// Transitive dependencies
|
// Transitive dependencies
|
||||||
let mut additional_modules = vec![];
|
let mut additional_modules = vec![];
|
||||||
let mut reexports = vec![];
|
let mut reexports = vec![];
|
||||||
|
let mut decls_for_reexport = vec![];
|
||||||
|
|
||||||
// Remove transitive dependencies which is merged by parent moudle.
|
// Remove transitive dependencies which is merged by parent moudle.
|
||||||
for v in info.exports.reexports.clone() {
|
for v in info.exports.reexports.clone() {
|
||||||
@ -76,6 +77,41 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (src, specifiers) in info.exports.reexports.iter() {
|
||||||
|
let imported = self.scope.get_module(src.module_id).unwrap();
|
||||||
|
|
||||||
|
// export * from './foo';
|
||||||
|
if specifiers.is_empty() {
|
||||||
|
decls_for_reexport.extend(
|
||||||
|
imported
|
||||||
|
.exports
|
||||||
|
.items
|
||||||
|
.iter()
|
||||||
|
.chain(imported.exports.reexports.iter().map(|v| &*v.1).flatten())
|
||||||
|
.map(|specifier| match specifier {
|
||||||
|
Specifier::Specific { local, alias } => VarDeclarator {
|
||||||
|
span: DUMMY_SP,
|
||||||
|
name: Pat::Ident(
|
||||||
|
local.clone().replace_mark(info.mark()).into_ident(),
|
||||||
|
),
|
||||||
|
init: Some(Box::new(Expr::Ident(
|
||||||
|
alias.clone().unwrap_or_else(|| local.clone()).into_ident(),
|
||||||
|
))),
|
||||||
|
definite: false,
|
||||||
|
},
|
||||||
|
Specifier::Namespace { local, .. } => VarDeclarator {
|
||||||
|
span: DUMMY_SP,
|
||||||
|
name: Pat::Ident(
|
||||||
|
local.clone().replace_mark(info.mark()).into_ident(),
|
||||||
|
),
|
||||||
|
init: Some(Box::new(Expr::Ident(local.clone().into_ident()))),
|
||||||
|
definite: false,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
let deps = reexports
|
let deps = reexports
|
||||||
.into_par_iter()
|
.into_par_iter()
|
||||||
.map(|(src, specifiers)| -> Result<_, Error> {
|
.map(|(src, specifiers)| -> Result<_, Error> {
|
||||||
@ -110,7 +146,20 @@ where
|
|||||||
});
|
});
|
||||||
|
|
||||||
if let Some(id) = id_of_export_namespace_from {
|
if let Some(id) = id_of_export_namespace_from {
|
||||||
dep = self.wrap_esm_as_a_var(info, dep, id)?;
|
dep = self.wrap_esm_as_a_var(plan, dep, &imported, merged, id)?;
|
||||||
|
|
||||||
|
let module_plan;
|
||||||
|
let module_plan = match plan.normal.get(&info.id) {
|
||||||
|
Some(v) => v,
|
||||||
|
None => {
|
||||||
|
module_plan = Default::default();
|
||||||
|
&module_plan
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
dep = self
|
||||||
|
.merge_imports(plan, &module_plan, dep, &info, merged, false)
|
||||||
|
.context("failed to merge imports")?;
|
||||||
} else {
|
} else {
|
||||||
dep = self.remark_exports(dep, src.ctxt, None, false);
|
dep = self.remark_exports(dep, src.ctxt, None, false);
|
||||||
}
|
}
|
||||||
@ -256,6 +305,16 @@ where
|
|||||||
assert_eq!(injector.imported, vec![]);
|
assert_eq!(injector.imported, vec![]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !decls_for_reexport.is_empty() {
|
||||||
|
entry
|
||||||
|
.body
|
||||||
|
.push(ModuleItem::Stmt(Stmt::Decl(Decl::Var(VarDecl {
|
||||||
|
span: DUMMY_SP,
|
||||||
|
kind: VarDeclKind::Const,
|
||||||
|
declare: false,
|
||||||
|
decls: decls_for_reexport,
|
||||||
|
}))));
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use super::plan::Plan;
|
use super::plan::{NormalPlan, Plan};
|
||||||
use crate::{
|
use crate::{
|
||||||
bundler::load::{Imports, Specifier},
|
bundler::load::{Imports, Specifier, TransformedModule},
|
||||||
id::ModuleId,
|
id::ModuleId,
|
||||||
load::Load,
|
load::Load,
|
||||||
resolve::Resolve,
|
resolve::Resolve,
|
||||||
@ -84,278 +84,295 @@ where
|
|||||||
self.merge_reexports(plan, module_plan, &mut entry, &info, merged)
|
self.merge_reexports(plan, module_plan, &mut entry, &info, merged)
|
||||||
.context("failed to merge reepxorts")?;
|
.context("failed to merge reepxorts")?;
|
||||||
|
|
||||||
|
// We handle this kind of modules specially.
|
||||||
|
if self.scope.should_be_wrapped_with_a_fn(info.id) {
|
||||||
|
return Ok(entry);
|
||||||
|
}
|
||||||
|
|
||||||
// print_hygiene("after: merge_reexports", &self.cm, &entry);
|
// print_hygiene("after: merge_reexports", &self.cm, &entry);
|
||||||
|
|
||||||
let to_merge: Vec<_> = info
|
entry = self
|
||||||
.imports
|
.merge_imports(plan, module_plan, entry, &info, merged, is_entry)
|
||||||
.specifiers
|
.context("failed to merge imports")?;
|
||||||
.iter()
|
|
||||||
.filter(|(src, _)| {
|
|
||||||
log::trace!("Checking: {} <= {}", info.fm.name, src.src.value);
|
|
||||||
|
|
||||||
// Import and export from same file. We use export to merge it.
|
|
||||||
if info
|
|
||||||
.exports
|
|
||||||
.reexports
|
|
||||||
.iter()
|
|
||||||
.any(|(es, _)| es.module_id == src.module_id)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Skip if a dependency is going to be merged by other dependency
|
|
||||||
module_plan.chunks.contains(&src.module_id)
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let (deps, transitive_deps) = util::join(
|
|
||||||
|| {
|
|
||||||
to_merge
|
|
||||||
.into_par_iter()
|
|
||||||
.map(|(src, specifiers)| -> Result<Option<_>, Error> {
|
|
||||||
self.run(|| {
|
|
||||||
if !merged.insert(src.module_id) {
|
|
||||||
log::debug!("Skipping: {} <= {}", info.fm.name, src.src.value);
|
|
||||||
return Ok(None);
|
|
||||||
}
|
|
||||||
|
|
||||||
log::debug!("Merging: {} <= {}", info.fm.name, src.src.value);
|
|
||||||
|
|
||||||
let dep_info = self.scope.get_module(src.module_id).unwrap();
|
|
||||||
info.helpers.extend(&dep_info.helpers);
|
|
||||||
// In the case of
|
|
||||||
//
|
|
||||||
// a <- b
|
|
||||||
// b <- c
|
|
||||||
//
|
|
||||||
// we change it to
|
|
||||||
//
|
|
||||||
// a <- b + chunk(c)
|
|
||||||
//
|
|
||||||
let mut dep = self
|
|
||||||
.merge_modules(plan, src.module_id, false, false, merged)
|
|
||||||
.with_context(|| {
|
|
||||||
format!(
|
|
||||||
"failed to merge: ({}):{} <= ({}):{}",
|
|
||||||
info.id, info.fm.name, src.module_id, src.src.value
|
|
||||||
)
|
|
||||||
})?;
|
|
||||||
|
|
||||||
if dep_info.is_es6 {
|
|
||||||
// print_hygiene("dep:before:tree-shaking", &self.cm, &dep);
|
|
||||||
|
|
||||||
let is_acccessed_with_computed_key =
|
|
||||||
specifiers.iter().any(|s| match s {
|
|
||||||
Specifier::Namespace { all: true, .. } => true,
|
|
||||||
_ => false,
|
|
||||||
});
|
|
||||||
|
|
||||||
// If an import with a computed key exists, we can't shake tree
|
|
||||||
if is_acccessed_with_computed_key {
|
|
||||||
let id = specifiers
|
|
||||||
.iter()
|
|
||||||
.find_map(|s| match s {
|
|
||||||
Specifier::Namespace { local, all: true } => {
|
|
||||||
Some(local)
|
|
||||||
}
|
|
||||||
_ => None,
|
|
||||||
})
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
dep = self.wrap_esm_as_a_var(
|
|
||||||
&dep_info,
|
|
||||||
dep,
|
|
||||||
id.clone().replace_mark(dep_info.mark()).into_ident(),
|
|
||||||
)?;
|
|
||||||
} else {
|
|
||||||
if let Some(imports) = info
|
|
||||||
.imports
|
|
||||||
.specifiers
|
|
||||||
.iter()
|
|
||||||
.find(|(s, _)| s.module_id == dep_info.id)
|
|
||||||
.map(|v| &v.1)
|
|
||||||
{
|
|
||||||
// print_hygiene(
|
|
||||||
// "dep: before remarking exports",
|
|
||||||
// &self.cm,
|
|
||||||
// &dep,
|
|
||||||
// );
|
|
||||||
|
|
||||||
dep = self.remark_exports(
|
|
||||||
dep,
|
|
||||||
dep_info.ctxt(),
|
|
||||||
Some(&imports),
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// print_hygiene(
|
|
||||||
// "dep: after remarking exports",
|
|
||||||
// &self.cm,
|
|
||||||
// &dep,
|
|
||||||
// );
|
|
||||||
}
|
|
||||||
// print_hygiene("dep:after:tree-shaking", &self.cm, &dep);
|
|
||||||
|
|
||||||
// if let Some(imports) = info
|
|
||||||
// .imports
|
|
||||||
// .specifiers
|
|
||||||
// .iter()
|
|
||||||
// .find(|(s, _)| s.module_id == dep_info.id)
|
|
||||||
// .map(|v| &v.1)
|
|
||||||
// {
|
|
||||||
// dep = dep.fold_with(&mut ExportRenamer {
|
|
||||||
// mark: dep_info.mark(),
|
|
||||||
// _exports: &dep_info.exports,
|
|
||||||
// imports: &imports,
|
|
||||||
// extras: vec![],
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
// print_hygiene("dep:after:export-renamer", &self.cm, &dep);
|
|
||||||
|
|
||||||
dep = dep.fold_with(&mut Unexporter);
|
|
||||||
}
|
|
||||||
// print_hygiene("dep:before-injection", &self.cm, &dep);
|
|
||||||
|
|
||||||
Ok(Some((dep, dep_info)))
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>()
|
|
||||||
},
|
|
||||||
|| {
|
|
||||||
let deps = module_plan
|
|
||||||
.transitive_chunks
|
|
||||||
.clone()
|
|
||||||
.into_par_iter()
|
|
||||||
.map(|id| -> Result<_, Error> {
|
|
||||||
if !merged.insert(id) {
|
|
||||||
return Ok(None);
|
|
||||||
}
|
|
||||||
|
|
||||||
let dep_info = self.scope.get_module(id).unwrap();
|
|
||||||
let mut dep = self.merge_modules(plan, id, false, true, merged)?;
|
|
||||||
|
|
||||||
// print_hygiene("transitive dep", &self.cm, &dep);
|
|
||||||
|
|
||||||
dep = self.remark_exports(dep, dep_info.ctxt(), None, true);
|
|
||||||
dep = dep.fold_with(&mut Unexporter);
|
|
||||||
|
|
||||||
// As transitive deps can have no direct relation with entry,
|
|
||||||
// remark_exports is not enough.
|
|
||||||
Ok(Some((dep, dep_info)))
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
deps
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut targets = module_plan.chunks.clone();
|
|
||||||
|
|
||||||
for (dep, is_direct) in deps
|
|
||||||
.into_iter()
|
|
||||||
.map(|v| (v, true))
|
|
||||||
.chain(transitive_deps.into_iter().map(|v| (v, false)))
|
|
||||||
{
|
|
||||||
let dep = dep?;
|
|
||||||
let dep = match dep {
|
|
||||||
Some(v) => v,
|
|
||||||
None => continue,
|
|
||||||
};
|
|
||||||
|
|
||||||
let (mut dep, dep_info) = dep;
|
|
||||||
|
|
||||||
if let Some(idx) = targets.iter().position(|v| *v == dep_info.id) {
|
|
||||||
targets.remove(idx);
|
|
||||||
if let Some(v) = plan.normal.get(&dep_info.id) {
|
|
||||||
targets.retain(|&id| !v.chunks.contains(&id));
|
|
||||||
}
|
|
||||||
if let Some(v) = plan.circular.get(&dep_info.id) {
|
|
||||||
targets.retain(|&id| !v.chunks.contains(&id));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// print_hygiene("dep: before injection", &self.cm, &dep);
|
|
||||||
|
|
||||||
if dep_info.is_es6 {
|
|
||||||
// print_hygiene("entry: before injection", &self.cm, &entry);
|
|
||||||
|
|
||||||
// Replace import statement / require with module body
|
|
||||||
let mut injector = Es6ModuleInjector {
|
|
||||||
imported: take(&mut dep.body),
|
|
||||||
ctxt: dep_info.ctxt(),
|
|
||||||
is_direct,
|
|
||||||
};
|
|
||||||
entry.body.visit_mut_with(&mut injector);
|
|
||||||
|
|
||||||
if injector.imported.is_empty() {
|
|
||||||
// print_hygiene("entry:after:injection", &self.cm, &entry);
|
|
||||||
|
|
||||||
log::debug!("Merged {} as an es module", info.fm.name);
|
|
||||||
// print_hygiene(
|
|
||||||
// &format!("ES6: {:?} <- {:?}", info.ctxt(), dep_info.ctxt()),
|
|
||||||
// &self.cm,
|
|
||||||
// &entry,
|
|
||||||
// );
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if !is_direct {
|
|
||||||
prepend_stmts(&mut entry.body, injector.imported.into_iter());
|
|
||||||
|
|
||||||
// print_hygiene("ES6", &self.cm, &entry);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// print_hygiene("entry: failed to inject", &self.cm, &entry);
|
|
||||||
|
|
||||||
dep.body = take(&mut injector.imported);
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.config.require {
|
|
||||||
self.merge_cjs(
|
|
||||||
plan,
|
|
||||||
is_entry,
|
|
||||||
&mut entry,
|
|
||||||
&info,
|
|
||||||
Cow::Owned(dep),
|
|
||||||
&dep_info,
|
|
||||||
&mut targets,
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// if is_entry && self.config.require && !targets.is_empty() {
|
|
||||||
// log::info!("Injectng remaining: {:?}", targets);
|
|
||||||
|
|
||||||
// // Handle transitive dependencies
|
|
||||||
// for target in targets.drain(..) {
|
|
||||||
// log::trace!(
|
|
||||||
// "Remaining: {}",
|
|
||||||
// self.scope.get_module(target).unwrap().fm.name
|
|
||||||
// );
|
|
||||||
|
|
||||||
// let dep_info = self.scope.get_module(target).unwrap();
|
|
||||||
// self.merge_cjs(
|
|
||||||
// plan,
|
|
||||||
// &mut entry,
|
|
||||||
// &info,
|
|
||||||
// Cow::Borrowed(&dep_info.module),
|
|
||||||
// &dep_info,
|
|
||||||
// &mut targets,
|
|
||||||
// )?;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
if is_entry {
|
|
||||||
// print_hygiene("done", &self.cm, &entry);
|
|
||||||
self.finalize_merging_of_entry(plan, &mut entry);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(entry)
|
Ok(entry)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(super) fn merge_imports(
|
||||||
|
&self,
|
||||||
|
plan: &Plan,
|
||||||
|
module_plan: &NormalPlan,
|
||||||
|
mut entry: Module,
|
||||||
|
info: &TransformedModule,
|
||||||
|
merged: &CHashSet<ModuleId>,
|
||||||
|
is_entry: bool,
|
||||||
|
) -> Result<Module, Error> {
|
||||||
|
let to_merge: Vec<_> = info
|
||||||
|
.imports
|
||||||
|
.specifiers
|
||||||
|
.iter()
|
||||||
|
.filter(|(src, _)| {
|
||||||
|
log::trace!("Checking: {} <= {}", info.fm.name, src.src.value);
|
||||||
|
|
||||||
|
// Import and export from same file. We use export to merge it.
|
||||||
|
if info
|
||||||
|
.exports
|
||||||
|
.reexports
|
||||||
|
.iter()
|
||||||
|
.any(|(es, _)| es.module_id == src.module_id)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip if a dependency is going to be merged by other dependency
|
||||||
|
module_plan.chunks.contains(&src.module_id)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let (deps, transitive_deps) = util::join(
|
||||||
|
|| {
|
||||||
|
to_merge
|
||||||
|
.into_par_iter()
|
||||||
|
.map(|(src, specifiers)| -> Result<Option<_>, Error> {
|
||||||
|
self.run(|| {
|
||||||
|
if !merged.insert(src.module_id) {
|
||||||
|
log::debug!("Skipping: {} <= {}", info.fm.name, src.src.value);
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
|
||||||
|
log::debug!("Merging: {} <= {}", info.fm.name, src.src.value);
|
||||||
|
|
||||||
|
let dep_info = self.scope.get_module(src.module_id).unwrap();
|
||||||
|
info.helpers.extend(&dep_info.helpers);
|
||||||
|
// In the case of
|
||||||
|
//
|
||||||
|
// a <- b
|
||||||
|
// b <- c
|
||||||
|
//
|
||||||
|
// we change it to
|
||||||
|
//
|
||||||
|
// a <- b + chunk(c)
|
||||||
|
//
|
||||||
|
let mut dep = self
|
||||||
|
.merge_modules(plan, src.module_id, false, false, merged)
|
||||||
|
.with_context(|| {
|
||||||
|
format!(
|
||||||
|
"failed to merge: ({}):{} <= ({}):{}",
|
||||||
|
info.id, info.fm.name, src.module_id, src.src.value
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
if dep_info.is_es6 {
|
||||||
|
// print_hygiene("dep:before:tree-shaking", &self.cm, &dep);
|
||||||
|
|
||||||
|
let is_acccessed_with_computed_key =
|
||||||
|
self.scope.should_be_wrapped_with_a_fn(dep_info.id);
|
||||||
|
|
||||||
|
// If an import with a computed key exists, we can't shake tree
|
||||||
|
if is_acccessed_with_computed_key {
|
||||||
|
let id = specifiers
|
||||||
|
.iter()
|
||||||
|
.find_map(|s| match s {
|
||||||
|
Specifier::Namespace { local, all: true } => {
|
||||||
|
Some(local)
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
dep = self.wrap_esm_as_a_var(
|
||||||
|
plan,
|
||||||
|
dep,
|
||||||
|
&dep_info,
|
||||||
|
merged,
|
||||||
|
id.clone().replace_mark(dep_info.mark()).into_ident(),
|
||||||
|
)?;
|
||||||
|
} else {
|
||||||
|
if let Some(imports) = info
|
||||||
|
.imports
|
||||||
|
.specifiers
|
||||||
|
.iter()
|
||||||
|
.find(|(s, _)| s.module_id == dep_info.id)
|
||||||
|
.map(|v| &v.1)
|
||||||
|
{
|
||||||
|
// print_hygiene(
|
||||||
|
// "dep: before remarking exports",
|
||||||
|
// &self.cm,
|
||||||
|
// &dep,
|
||||||
|
// );
|
||||||
|
|
||||||
|
dep = self.remark_exports(
|
||||||
|
dep,
|
||||||
|
dep_info.ctxt(),
|
||||||
|
Some(&imports),
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// print_hygiene("dep:after:tree-shaking", &self.cm, &dep);
|
||||||
|
|
||||||
|
// if let Some(imports) = info
|
||||||
|
// .imports
|
||||||
|
// .specifiers
|
||||||
|
// .iter()
|
||||||
|
// .find(|(s, _)| s.module_id == dep_info.id)
|
||||||
|
// .map(|v| &v.1)
|
||||||
|
// {
|
||||||
|
// dep = dep.fold_with(&mut ExportRenamer {
|
||||||
|
// mark: dep_info.mark(),
|
||||||
|
// _exports: &dep_info.exports,
|
||||||
|
// imports: &imports,
|
||||||
|
// extras: vec![],
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
// print_hygiene("dep:after:export-renamer", &self.cm, &dep);
|
||||||
|
|
||||||
|
dep = dep.fold_with(&mut Unexporter);
|
||||||
|
}
|
||||||
|
// print_hygiene("dep:before-injection", &self.cm, &dep);
|
||||||
|
|
||||||
|
Ok(Some((dep, dep_info)))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
},
|
||||||
|
|| {
|
||||||
|
let deps = module_plan
|
||||||
|
.transitive_chunks
|
||||||
|
.clone()
|
||||||
|
.into_par_iter()
|
||||||
|
.map(|id| -> Result<_, Error> {
|
||||||
|
if !merged.insert(id) {
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
|
||||||
|
let dep_info = self.scope.get_module(id).unwrap();
|
||||||
|
let mut dep = self.merge_modules(plan, id, false, true, merged)?;
|
||||||
|
|
||||||
|
// print_hygiene("transitive dep", &self.cm, &dep);
|
||||||
|
|
||||||
|
dep = self.remark_exports(dep, dep_info.ctxt(), None, true);
|
||||||
|
dep = dep.fold_with(&mut Unexporter);
|
||||||
|
|
||||||
|
// As transitive deps can have no direct relation with entry,
|
||||||
|
// remark_exports is not enough.
|
||||||
|
Ok(Some((dep, dep_info)))
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
deps
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut targets = module_plan.chunks.clone();
|
||||||
|
|
||||||
|
for (dep, is_direct) in deps
|
||||||
|
.into_iter()
|
||||||
|
.map(|v| (v, true))
|
||||||
|
.chain(transitive_deps.into_iter().map(|v| (v, false)))
|
||||||
|
{
|
||||||
|
let dep = dep?;
|
||||||
|
let dep = match dep {
|
||||||
|
Some(v) => v,
|
||||||
|
None => continue,
|
||||||
|
};
|
||||||
|
|
||||||
|
let (mut dep, dep_info) = dep;
|
||||||
|
|
||||||
|
if let Some(idx) = targets.iter().position(|v| *v == dep_info.id) {
|
||||||
|
targets.remove(idx);
|
||||||
|
if let Some(v) = plan.normal.get(&dep_info.id) {
|
||||||
|
targets.retain(|&id| !v.chunks.contains(&id));
|
||||||
|
}
|
||||||
|
if let Some(v) = plan.circular.get(&dep_info.id) {
|
||||||
|
targets.retain(|&id| !v.chunks.contains(&id));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// print_hygiene("dep: before injection", &self.cm, &dep);
|
||||||
|
|
||||||
|
if dep_info.is_es6 {
|
||||||
|
// print_hygiene("entry: before injection", &self.cm, &entry);
|
||||||
|
|
||||||
|
// Replace import statement / require with module body
|
||||||
|
let mut injector = Es6ModuleInjector {
|
||||||
|
imported: take(&mut dep.body),
|
||||||
|
ctxt: dep_info.ctxt(),
|
||||||
|
is_direct,
|
||||||
|
};
|
||||||
|
entry.body.visit_mut_with(&mut injector);
|
||||||
|
|
||||||
|
if injector.imported.is_empty() {
|
||||||
|
// print_hygiene("entry:after:injection", &self.cm, &entry);
|
||||||
|
|
||||||
|
log::debug!(
|
||||||
|
"Merged {} into {} as an es module",
|
||||||
|
dep_info.fm.name,
|
||||||
|
info.fm.name,
|
||||||
|
);
|
||||||
|
// print_hygiene(
|
||||||
|
// &format!("ES6: {:?} <- {:?}", info.ctxt(), dep_info.ctxt()),
|
||||||
|
// &self.cm,
|
||||||
|
// &entry,
|
||||||
|
// );
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if !is_direct {
|
||||||
|
prepend_stmts(&mut entry.body, injector.imported.into_iter());
|
||||||
|
|
||||||
|
// print_hygiene("ES6", &self.cm, &entry);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// print_hygiene("entry: failed to inject", &self.cm, &entry);
|
||||||
|
|
||||||
|
dep.body = take(&mut injector.imported);
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.config.require {
|
||||||
|
self.merge_cjs(
|
||||||
|
plan,
|
||||||
|
is_entry,
|
||||||
|
&mut entry,
|
||||||
|
&info,
|
||||||
|
Cow::Owned(dep),
|
||||||
|
&dep_info,
|
||||||
|
&mut targets,
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if is_entry && self.config.require && !targets.is_empty() {
|
||||||
|
// log::info!("Injectng remaining: {:?}", targets);
|
||||||
|
|
||||||
|
// // Handle transitive dependencies
|
||||||
|
// for target in targets.drain(..) {
|
||||||
|
// log::trace!(
|
||||||
|
// "Remaining: {}",
|
||||||
|
// self.scope.get_module(target).unwrap().fm.name
|
||||||
|
// );
|
||||||
|
|
||||||
|
// let dep_info = self.scope.get_module(target).unwrap();
|
||||||
|
// self.merge_cjs(
|
||||||
|
// plan,
|
||||||
|
// &mut entry,
|
||||||
|
// &info,
|
||||||
|
// Cow::Borrowed(&dep_info.module),
|
||||||
|
// &dep_info,
|
||||||
|
// &mut targets,
|
||||||
|
// )?;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
if is_entry {
|
||||||
|
self.finalize_merging_of_entry(plan, &mut entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(entry)
|
||||||
|
}
|
||||||
|
|
||||||
fn finalize_merging_of_entry(&self, plan: &Plan, entry: &mut Module) {
|
fn finalize_merging_of_entry(&self, plan: &Plan, entry: &mut Module) {
|
||||||
// print_hygiene("done", &self.cm, &entry);
|
// print_hygiene("done", &self.cm, &entry);
|
||||||
|
|
||||||
@ -412,6 +429,15 @@ where
|
|||||||
});
|
});
|
||||||
|
|
||||||
entry.visit_mut_with(&mut DefaultRenamer);
|
entry.visit_mut_with(&mut DefaultRenamer);
|
||||||
|
|
||||||
|
// print_hygiene(
|
||||||
|
// "done-clean",
|
||||||
|
// &self.cm,
|
||||||
|
// &entry
|
||||||
|
// .clone()
|
||||||
|
// .fold_with(&mut hygiene())
|
||||||
|
// .fold_with(&mut fixer(None)),
|
||||||
|
// );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@ use swc_ecma_visit::FoldWith;
|
|||||||
|
|
||||||
mod circular;
|
mod circular;
|
||||||
mod cjs;
|
mod cjs;
|
||||||
|
mod computed_key;
|
||||||
mod export;
|
mod export;
|
||||||
mod merge;
|
mod merge;
|
||||||
mod plan;
|
mod plan;
|
||||||
|
@ -26,7 +26,9 @@ struct PlanBuilder {
|
|||||||
/// This contains all dependencies, including transitive ones. For example,
|
/// This contains all dependencies, including transitive ones. For example,
|
||||||
/// if `a` dependes on `b` and `b` depdends on `c`, all of
|
/// if `a` dependes on `b` and `b` depdends on `c`, all of
|
||||||
/// `(a, b)`, `(a, c)`,`(b, c)` will be inserted.
|
/// `(a, b)`, `(a, c)`,`(b, c)` will be inserted.
|
||||||
all_deps: HashSet<(ModuleId, ModuleId)>,
|
///
|
||||||
|
/// `bool` is `true` if it's connected with exports.
|
||||||
|
all_deps: HashMap<(ModuleId, ModuleId), bool>,
|
||||||
|
|
||||||
/// Graph to compute direct dependencies (direct means it will be merged
|
/// Graph to compute direct dependencies (direct means it will be merged
|
||||||
/// directly)
|
/// directly)
|
||||||
@ -168,7 +170,7 @@ where
|
|||||||
None => {}
|
None => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.add_to_graph(&mut builder, module.id, &mut vec![]);
|
self.add_to_graph(&mut builder, module.id, &mut vec![], true);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut metadata = HashMap::<ModuleId, Metadata>::default();
|
let mut metadata = HashMap::<ModuleId, Metadata>::default();
|
||||||
@ -222,11 +224,14 @@ where
|
|||||||
let root_entry = *root_entry;
|
let root_entry = *root_entry;
|
||||||
let mut bfs = Bfs::new(&builder.direct_deps, root_entry);
|
let mut bfs = Bfs::new(&builder.direct_deps, root_entry);
|
||||||
|
|
||||||
|
let mut done = HashSet::new();
|
||||||
|
|
||||||
while let Some(entry) = bfs.next(&builder.direct_deps) {
|
while let Some(entry) = bfs.next(&builder.direct_deps) {
|
||||||
let deps: Vec<_> = builder
|
let mut deps: Vec<_> = builder
|
||||||
.direct_deps
|
.direct_deps
|
||||||
.neighbors_directed(entry, Outgoing)
|
.neighbors_directed(entry, Outgoing)
|
||||||
.collect();
|
.collect();
|
||||||
|
deps.sort();
|
||||||
|
|
||||||
for &dep in &deps {
|
for &dep in &deps {
|
||||||
if let Some(circular_members) = builder.circular.get(entry) {
|
if let Some(circular_members) = builder.circular.get(entry) {
|
||||||
@ -237,21 +242,33 @@ where
|
|||||||
entry
|
entry
|
||||||
);
|
);
|
||||||
if entry != root_entry && dep != root_entry {
|
if entry != root_entry && dep != root_entry {
|
||||||
|
done.insert(dep);
|
||||||
plans.normal.entry(entry).or_default().chunks.push(dep);
|
plans.normal.entry(entry).or_default().chunks.push(dep);
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if done.contains(&dep) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
let is_es6 = self.scope.get_module(entry).unwrap().is_es6;
|
let is_es6 = self.scope.get_module(entry).unwrap().is_es6;
|
||||||
let dependants = builder
|
let mut dependants = builder
|
||||||
.direct_deps
|
.direct_deps
|
||||||
.neighbors_directed(dep, Incoming)
|
.neighbors_directed(dep, Incoming)
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
dependants.sort();
|
||||||
|
|
||||||
if metadata.get(&dep).map(|md| md.bundle_cnt).unwrap_or(0) == 1 {
|
if metadata.get(&dep).map(|md| md.bundle_cnt).unwrap_or(0) == 1 {
|
||||||
log::debug!("{:?} depends on {:?}", entry, dep);
|
log::debug!("{:?} depends on {:?}", entry, dep);
|
||||||
|
|
||||||
|
let is_reexport = builder
|
||||||
|
.all_deps
|
||||||
|
.get(&(entry, dep))
|
||||||
|
.copied()
|
||||||
|
.unwrap_or(false);
|
||||||
|
|
||||||
// Common js support.
|
// Common js support.
|
||||||
if !is_es6 {
|
if !is_es6 {
|
||||||
// Dependancy of
|
// Dependancy of
|
||||||
@ -280,7 +297,6 @@ where
|
|||||||
// a <- b
|
// a <- b
|
||||||
// a <- c
|
// a <- c
|
||||||
let module = least_common_ancestor(&builder, &dependants);
|
let module = least_common_ancestor(&builder, &dependants);
|
||||||
|
|
||||||
let normal_plan = plans.normal.entry(module).or_default();
|
let normal_plan = plans.normal.entry(module).or_default();
|
||||||
|
|
||||||
for &dep in &deps {
|
for &dep in &deps {
|
||||||
@ -301,6 +317,17 @@ where
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if is_reexport {
|
||||||
|
let normal_plan = plans.normal.entry(entry).or_default();
|
||||||
|
if !normal_plan.chunks.contains(&dep) {
|
||||||
|
done.insert(dep);
|
||||||
|
|
||||||
|
log::trace!("Normal: {:?} => {:?}", entry, dep);
|
||||||
|
normal_plan.chunks.push(dep);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if 2 <= dependants.len() {
|
if 2 <= dependants.len() {
|
||||||
// Should be merged as a transitive dependency.
|
// Should be merged as a transitive dependency.
|
||||||
let higher_module = if plans.entries.contains(&dependants[0]) {
|
let higher_module = if plans.entries.contains(&dependants[0]) {
|
||||||
@ -314,8 +341,11 @@ where
|
|||||||
};
|
};
|
||||||
|
|
||||||
if dependants.len() == 2 && dependants.contains(&higher_module) {
|
if dependants.len() == 2 && dependants.contains(&higher_module) {
|
||||||
let mut entry =
|
let mut entry = if is_reexport {
|
||||||
*dependants.iter().find(|&&v| v != higher_module).unwrap();
|
higher_module
|
||||||
|
} else {
|
||||||
|
*dependants.iter().find(|&&v| v != higher_module).unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
// We choose higher node if import is circular
|
// We choose higher node if import is circular
|
||||||
if builder.is_circular(entry) {
|
if builder.is_circular(entry) {
|
||||||
@ -325,6 +355,7 @@ where
|
|||||||
let normal_plan = plans.normal.entry(entry).or_default();
|
let normal_plan = plans.normal.entry(entry).or_default();
|
||||||
if !normal_plan.chunks.contains(&dep) {
|
if !normal_plan.chunks.contains(&dep) {
|
||||||
log::trace!("Normal: {:?} => {:?}", entry, dep);
|
log::trace!("Normal: {:?} => {:?}", entry, dep);
|
||||||
|
done.insert(dep);
|
||||||
normal_plan.chunks.push(dep);
|
normal_plan.chunks.push(dep);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -335,12 +366,14 @@ where
|
|||||||
.transitive_chunks;
|
.transitive_chunks;
|
||||||
if !t.contains(&dep) {
|
if !t.contains(&dep) {
|
||||||
log::trace!("Transitive: {:?} => {:?}", entry, dep);
|
log::trace!("Transitive: {:?} => {:?}", entry, dep);
|
||||||
|
done.insert(dep);
|
||||||
t.push(dep)
|
t.push(dep)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Direct dependency.
|
// Direct dependency.
|
||||||
log::trace!("Normal: {:?} => {:?}", entry, dep);
|
log::trace!("Normal: {:?} => {:?}", entry, dep);
|
||||||
|
done.insert(dep);
|
||||||
plans.normal.entry(entry).or_default().chunks.push(dep);
|
plans.normal.entry(entry).or_default().chunks.push(dep);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -436,6 +469,7 @@ where
|
|||||||
builder: &mut PlanBuilder,
|
builder: &mut PlanBuilder,
|
||||||
module_id: ModuleId,
|
module_id: ModuleId,
|
||||||
path: &mut Vec<ModuleId>,
|
path: &mut Vec<ModuleId>,
|
||||||
|
is_in_reexports: bool,
|
||||||
) {
|
) {
|
||||||
builder.direct_deps.add_node(module_id);
|
builder.direct_deps.add_node(module_id);
|
||||||
|
|
||||||
@ -444,34 +478,44 @@ where
|
|||||||
.get_module(module_id)
|
.get_module(module_id)
|
||||||
.expect("failed to get module");
|
.expect("failed to get module");
|
||||||
|
|
||||||
for src in m
|
for (src, is_export) in m
|
||||||
.imports
|
.imports
|
||||||
.specifiers
|
.specifiers
|
||||||
.iter()
|
.iter()
|
||||||
.map(|v| &v.0)
|
.map(|v| (&v.0, false))
|
||||||
.chain(m.exports.reexports.iter().map(|v| &v.0))
|
.chain(m.exports.reexports.iter().map(|v| (&v.0, true)))
|
||||||
{
|
{
|
||||||
if !builder.direct_deps.contains_edge(module_id, src.module_id) {
|
if !builder.direct_deps.contains_edge(module_id, src.module_id) {
|
||||||
log::debug!("Dependency: {:?} => {:?}", module_id, src.module_id);
|
log::debug!(
|
||||||
|
"Dependency: {:?} => {:?}; in export = {:?}; export = {:?}",
|
||||||
|
module_id,
|
||||||
|
src.module_id,
|
||||||
|
is_in_reexports,
|
||||||
|
is_export
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
builder.direct_deps.add_edge(module_id, src.module_id, 0);
|
builder.direct_deps.add_edge(module_id, src.module_id, 0);
|
||||||
|
|
||||||
for &id in &*path {
|
for &id in &*path {
|
||||||
builder.all_deps.insert((id, src.module_id));
|
builder
|
||||||
|
.all_deps
|
||||||
|
.insert((id, src.module_id), is_in_reexports);
|
||||||
}
|
}
|
||||||
builder.all_deps.insert((module_id, src.module_id));
|
builder
|
||||||
|
.all_deps
|
||||||
|
.insert((module_id, src.module_id), is_export);
|
||||||
|
|
||||||
if !builder.all_deps.contains(&(src.module_id, module_id)) {
|
if !builder.all_deps.contains_key(&(src.module_id, module_id)) {
|
||||||
path.push(module_id);
|
path.push(module_id);
|
||||||
self.add_to_graph(builder, src.module_id, path);
|
self.add_to_graph(builder, src.module_id, path, is_export);
|
||||||
assert_eq!(path.pop(), Some(module_id));
|
assert_eq!(path.pop(), Some(module_id));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prevent dejavu
|
// Prevent dejavu
|
||||||
for (src, _) in &m.imports.specifiers {
|
for (src, _) in &m.imports.specifiers {
|
||||||
if builder.all_deps.contains(&(src.module_id, module_id)) {
|
if builder.all_deps.contains_key(&(src.module_id, module_id)) {
|
||||||
log::debug!("Circular dep: {:?} => {:?}", module_id, src.module_id);
|
log::debug!("Circular dep: {:?} => {:?}", module_id, src.module_id);
|
||||||
|
|
||||||
builder.mark_as_circular(module_id, src.module_id);
|
builder.mark_as_circular(module_id, src.module_id);
|
||||||
|
@ -109,8 +109,8 @@ fn concurrency_001() {
|
|||||||
|
|
||||||
assert_eq!(p.circular.len(), 0);
|
assert_eq!(p.circular.len(), 0);
|
||||||
|
|
||||||
assert_normal(t, &p, "main", &["a"]);
|
assert_normal(t, &p, "main", &["a", "b"]);
|
||||||
assert_normal(t, &p, "a", &["b"]);
|
assert_normal(t, &p, "a", &[]);
|
||||||
assert_normal(t, &p, "b", &[]);
|
assert_normal(t, &p, "b", &[]);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -153,8 +153,8 @@ fn concurrency_002() {
|
|||||||
|
|
||||||
assert_eq!(p.circular.len(), 0);
|
assert_eq!(p.circular.len(), 0);
|
||||||
|
|
||||||
assert_normal(t, &p, "main", &["a"]);
|
assert_normal(t, &p, "main", &["a", "b"]);
|
||||||
assert_normal(t, &p, "a", &["b"]);
|
assert_normal(t, &p, "a", &[]);
|
||||||
assert_normal(t, &p, "b", &[]);
|
assert_normal(t, &p, "b", &[]);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -702,7 +702,6 @@ fn circular_002() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[ignore = "Not deterministic yet"]
|
|
||||||
fn deno_002() {
|
fn deno_002() {
|
||||||
suite()
|
suite()
|
||||||
.file(
|
.file(
|
||||||
@ -799,7 +798,13 @@ fn deno_002() {
|
|||||||
|
|
||||||
dbg!(&p);
|
dbg!(&p);
|
||||||
|
|
||||||
assert_normal(t, &p, "main", &["http-server"]);
|
assert_normal_transitive(
|
||||||
|
t,
|
||||||
|
&p,
|
||||||
|
"main",
|
||||||
|
&["http-server"],
|
||||||
|
&["encoding-utf8", "io-bufio", "_util-assert"],
|
||||||
|
);
|
||||||
|
|
||||||
assert_normal_transitive(t, &p, "http-server", &["async-mod"], &[]);
|
assert_normal_transitive(t, &p, "http-server", &["async-mod"], &[]);
|
||||||
assert_circular(t, &p, "http-server", &["http-_io"]);
|
assert_circular(t, &p, "http-server", &["http-_io"]);
|
||||||
@ -810,25 +815,22 @@ fn deno_002() {
|
|||||||
|
|
||||||
assert_normal(t, &p, "_util-assert", &[]);
|
assert_normal(t, &p, "_util-assert", &[]);
|
||||||
|
|
||||||
assert_normal_transitive(
|
assert_normal(t, &p, "http-_io", &["textproto-mod", "http-http_status"]);
|
||||||
t,
|
|
||||||
&p,
|
|
||||||
"http-_io",
|
|
||||||
&["textproto-mod", "http-http_status"],
|
|
||||||
&["encoding-utf8", "io-bufio", "_util-assert"],
|
|
||||||
);
|
|
||||||
assert_circular(t, &p, "http-_io", &["http-server"]);
|
assert_circular(t, &p, "http-_io", &["http-server"]);
|
||||||
|
|
||||||
assert_normal(t, &p, "textproto-mod", &[]);
|
assert_normal(t, &p, "textproto-mod", &[]);
|
||||||
|
|
||||||
assert_normal(t, &p, "_util-assert", &[]);
|
assert_normal(t, &p, "_util-assert", &[]);
|
||||||
|
|
||||||
assert_normal(t, &p, "async-mod", &["async-mux_async_iterator"]);
|
assert_normal(
|
||||||
|
t,
|
||||||
|
&p,
|
||||||
|
"async-mod",
|
||||||
|
&["async-mux_async_iterator", "async-deferred"],
|
||||||
|
);
|
||||||
|
|
||||||
assert_normal(t, &p, "bytes-mod", &[]);
|
assert_normal(t, &p, "bytes-mod", &[]);
|
||||||
|
|
||||||
assert_normal(t, &p, "async-mux_async_iterator", &["async-deferred"]);
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -908,7 +910,6 @@ fn circular_root_entry_2() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[ignore = "Not deterministic yet"]
|
|
||||||
fn deno_003() {
|
fn deno_003() {
|
||||||
suite()
|
suite()
|
||||||
.file(
|
.file(
|
||||||
@ -945,9 +946,14 @@ fn deno_003() {
|
|||||||
|
|
||||||
assert_normal(t, &p, "main", &["async-mod"]);
|
assert_normal(t, &p, "main", &["async-mod"]);
|
||||||
|
|
||||||
assert_normal(t, &p, "async-mod", &["async-mux_async_iterator"]);
|
assert_normal(
|
||||||
|
t,
|
||||||
|
&p,
|
||||||
|
"async-mod",
|
||||||
|
&["async-deferred", "async-mux_async_iterator"],
|
||||||
|
);
|
||||||
|
|
||||||
assert_normal(t, &p, "async-mux_async_iterator", &["async-deferred"]);
|
assert_normal(t, &p, "async-mux_async_iterator", &[]);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
});
|
});
|
||||||
|
@ -121,20 +121,17 @@ impl ExportRenamer<'_> {
|
|||||||
impl Fold for ExportRenamer<'_> {
|
impl Fold for ExportRenamer<'_> {
|
||||||
noop_fold_type!();
|
noop_fold_type!();
|
||||||
|
|
||||||
|
/// no-op, as imports or exports are only related to top level nodes.
|
||||||
fn fold_class(&mut self, node: Class) -> Class {
|
fn fold_class(&mut self, node: Class) -> Class {
|
||||||
node
|
node
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// no-op, as imports or exports are only related to top level nodes.
|
||||||
fn fold_function(&mut self, node: Function) -> Function {
|
fn fold_function(&mut self, node: Function) -> Function {
|
||||||
node
|
node
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fold_module_item(&mut self, item: ModuleItem) -> ModuleItem {
|
fn fold_module_item(&mut self, item: ModuleItem) -> ModuleItem {
|
||||||
let mut actual = ActualMarker {
|
|
||||||
dep_ctxt: self.dep_ctxt,
|
|
||||||
imports: self.imports,
|
|
||||||
};
|
|
||||||
|
|
||||||
let span = item.span();
|
let span = item.span();
|
||||||
let item: ModuleItem = item.fold_children_with(self);
|
let item: ModuleItem = item.fold_children_with(self);
|
||||||
|
|
||||||
@ -442,7 +439,7 @@ impl Fold for ExportRenamer<'_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ModuleItem::ModuleDecl(ModuleDecl::ExportDecl(decl)) => {
|
ModuleItem::ModuleDecl(ModuleDecl::ExportDecl(mut decl)) => {
|
||||||
//
|
//
|
||||||
return match decl.decl {
|
return match decl.decl {
|
||||||
Decl::TsInterface(_)
|
Decl::TsInterface(_)
|
||||||
@ -451,7 +448,11 @@ impl Fold for ExportRenamer<'_> {
|
|||||||
| Decl::TsModule(_) => ModuleItem::ModuleDecl(ModuleDecl::ExportDecl(decl)),
|
| Decl::TsModule(_) => ModuleItem::ModuleDecl(ModuleDecl::ExportDecl(decl)),
|
||||||
|
|
||||||
Decl::Class(mut c) => {
|
Decl::Class(mut c) => {
|
||||||
c.ident = c.ident.fold_with(&mut actual);
|
c.ident.visit_mut_with(&mut ActualMarker {
|
||||||
|
dep_ctxt: self.dep_ctxt,
|
||||||
|
imports: self.imports,
|
||||||
|
remarks: &mut self.remark_map,
|
||||||
|
});
|
||||||
|
|
||||||
if self.unexport {
|
if self.unexport {
|
||||||
Stmt::Decl(Decl::Class(c)).into()
|
Stmt::Decl(Decl::Class(c)).into()
|
||||||
@ -463,7 +464,11 @@ impl Fold for ExportRenamer<'_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Decl::Fn(mut f) => {
|
Decl::Fn(mut f) => {
|
||||||
f.ident = f.ident.fold_with(&mut actual);
|
f.ident.visit_mut_with(&mut ActualMarker {
|
||||||
|
dep_ctxt: self.dep_ctxt,
|
||||||
|
imports: self.imports,
|
||||||
|
remarks: &mut self.remark_map,
|
||||||
|
});
|
||||||
if self.unexport {
|
if self.unexport {
|
||||||
Stmt::Decl(Decl::Fn(f)).into()
|
Stmt::Decl(Decl::Fn(f)).into()
|
||||||
} else {
|
} else {
|
||||||
@ -473,8 +478,14 @@ impl Fold for ExportRenamer<'_> {
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Decl::Var(..) => {
|
Decl::Var(ref mut v) => {
|
||||||
ModuleItem::ModuleDecl(ModuleDecl::ExportDecl(decl.fold_with(&mut actual)))
|
v.decls.visit_mut_with(&mut ActualMarker {
|
||||||
|
dep_ctxt: self.dep_ctxt,
|
||||||
|
imports: self.imports,
|
||||||
|
remarks: &mut self.remark_map,
|
||||||
|
});
|
||||||
|
|
||||||
|
ModuleItem::ModuleDecl(ModuleDecl::ExportDecl(decl))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -494,90 +505,51 @@ impl Fold for ExportRenamer<'_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ActualMarker<'a> {
|
struct ActualMarker<'a, 'b> {
|
||||||
dep_ctxt: SyntaxContext,
|
dep_ctxt: SyntaxContext,
|
||||||
|
|
||||||
/// Dependant module's import
|
/// Dependant module's import
|
||||||
imports: Option<&'a [Specifier]>,
|
imports: Option<&'a [Specifier]>,
|
||||||
|
|
||||||
|
remarks: &'b mut RemarkMap,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ActualMarker<'_> {
|
impl ActualMarker<'_, '_> {
|
||||||
fn rename(&self, ident: Ident, only_if_aliased: bool) -> Result<Ident, Ident> {
|
fn rename(&mut self, ident: &mut Ident) {
|
||||||
if self.imports.is_none() {
|
if self.imports.is_none() {
|
||||||
return Err(ident);
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(mut ident) = self.imports.as_ref().unwrap().iter().find_map(|s| match s {
|
if let Some(id) = self.imports.as_ref().unwrap().iter().find_map(|s| match s {
|
||||||
Specifier::Specific {
|
Specifier::Specific {
|
||||||
alias: Some(alias),
|
alias: Some(alias),
|
||||||
local,
|
local,
|
||||||
} if *alias == ident.sym => Some(Ident::new(local.sym().clone(), ident.span)),
|
} if *alias == ident.sym => Some((local.sym().clone(), ident.span.ctxt)),
|
||||||
Specifier::Specific { alias: None, local }
|
Specifier::Specific { alias: None, local } if *local == ident.sym => {
|
||||||
if !only_if_aliased && *local == ident.sym =>
|
Some(local.to_id())
|
||||||
{
|
|
||||||
Some(local.clone().into_ident())
|
|
||||||
}
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
}) {
|
}) {
|
||||||
ident.span = ident.span.with_ctxt(self.dep_ctxt);
|
ident.sym = id.0.clone();
|
||||||
|
self.remarks.insert(id, self.dep_ctxt);
|
||||||
return Ok(ident);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Err(ident)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Fold for ActualMarker<'_> {
|
impl VisitMut for ActualMarker<'_, '_> {
|
||||||
noop_fold_type!();
|
noop_visit_mut_type!();
|
||||||
|
|
||||||
fn fold_expr(&mut self, node: Expr) -> Expr {
|
fn visit_mut_expr(&mut self, _: &mut Expr) {}
|
||||||
node
|
|
||||||
|
fn visit_mut_ident(&mut self, ident: &mut Ident) {
|
||||||
|
self.rename(ident)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fold_ident(&mut self, ident: Ident) -> Ident {
|
fn visit_mut_private_name(&mut self, _: &mut PrivateName) {}
|
||||||
match self.rename(ident, false) {
|
|
||||||
Ok(v) => v,
|
|
||||||
Err(v) => v,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn fold_private_name(&mut self, i: PrivateName) -> PrivateName {
|
fn visit_mut_export_named_specifier(&mut self, s: &mut ExportNamedSpecifier) {
|
||||||
i
|
s.orig.visit_mut_with(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fold_export_named_specifier(&mut self, s: ExportNamedSpecifier) -> ExportNamedSpecifier {
|
|
||||||
if let Some(..) = s.exported {
|
|
||||||
ExportNamedSpecifier {
|
|
||||||
orig: self.fold_ident(s.orig),
|
|
||||||
..s
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
match self.rename(s.orig.clone(), false) {
|
|
||||||
Ok(exported) => ExportNamedSpecifier {
|
|
||||||
orig: s.orig,
|
|
||||||
exported: Some(exported),
|
|
||||||
..s
|
|
||||||
},
|
|
||||||
Err(orig) => ExportNamedSpecifier { orig, ..s },
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn fold_prop(&mut self, p: Prop) -> Prop {
|
|
||||||
match p {
|
|
||||||
Prop::Shorthand(i) => match self.rename(i.clone(), false) {
|
|
||||||
Ok(renamed) => Prop::KeyValue(KeyValueProp {
|
|
||||||
key: i.into(),
|
|
||||||
value: Box::new(renamed.into()),
|
|
||||||
}),
|
|
||||||
Err(orig) => Prop::Shorthand(orig),
|
|
||||||
},
|
|
||||||
_ => p.fold_with(self),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: shorthand, etc
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct RemarkIdents<'a> {
|
struct RemarkIdents<'a> {
|
||||||
|
@ -4,7 +4,7 @@ use super::{
|
|||||||
};
|
};
|
||||||
use crate::{id::Id, load::Load, resolve::Resolve};
|
use crate::{id::Id, load::Load, resolve::Resolve};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use swc_atoms::js_word;
|
use swc_atoms::{js_word, JsWord};
|
||||||
use swc_common::{FileName, SyntaxContext};
|
use swc_common::{FileName, SyntaxContext};
|
||||||
use swc_ecma_ast::*;
|
use swc_ecma_ast::*;
|
||||||
use swc_ecma_utils::find_ids;
|
use swc_ecma_utils::find_ids;
|
||||||
@ -63,7 +63,7 @@ where
|
|||||||
L: Load,
|
L: Load,
|
||||||
R: Resolve,
|
R: Resolve,
|
||||||
{
|
{
|
||||||
fn ctxt_for(&self, src: &str) -> Option<SyntaxContext> {
|
fn ctxt_for(&self, src: &JsWord) -> Option<SyntaxContext> {
|
||||||
// Don't apply mark if it's a core module.
|
// Don't apply mark if it's a core module.
|
||||||
if self
|
if self
|
||||||
.bundler
|
.bundler
|
||||||
@ -80,6 +80,27 @@ where
|
|||||||
|
|
||||||
Some(ctxt.apply_mark(mark))
|
Some(ctxt.apply_mark(mark))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn mark_as_wrapping_required(&self, src: &JsWord) {
|
||||||
|
// Don't apply mark if it's a core module.
|
||||||
|
if self
|
||||||
|
.bundler
|
||||||
|
.config
|
||||||
|
.external_modules
|
||||||
|
.iter()
|
||||||
|
.any(|v| v == src)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let path = self.bundler.resolve(self.file_name, src);
|
||||||
|
let path = match path {
|
||||||
|
Ok(v) => v,
|
||||||
|
_ => return,
|
||||||
|
};
|
||||||
|
let (id, _) = self.bundler.scope.module_id_gen.gen(&path);
|
||||||
|
|
||||||
|
self.bundler.scope.mark_as_wrapping_required(id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<L, R> VisitMut for ExportFinder<'_, '_, L, R>
|
impl<L, R> VisitMut for ExportFinder<'_, '_, L, R>
|
||||||
@ -178,16 +199,20 @@ where
|
|||||||
let ctxt = named
|
let ctxt = named
|
||||||
.src
|
.src
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|s| &*s.value)
|
.map(|s| &s.value)
|
||||||
.and_then(|src| self.ctxt_for(src));
|
.and_then(|src| self.ctxt_for(src));
|
||||||
|
let mut need_wrapping = false;
|
||||||
|
|
||||||
let v = self.info.items.entry(named.src.clone()).or_default();
|
let v = self.info.items.entry(named.src.clone()).or_default();
|
||||||
for s in &mut named.specifiers {
|
for s in &mut named.specifiers {
|
||||||
match s {
|
match s {
|
||||||
ExportSpecifier::Namespace(n) => v.push(Specifier::Namespace {
|
ExportSpecifier::Namespace(n) => {
|
||||||
local: n.name.clone().into(),
|
need_wrapping = true;
|
||||||
all: true,
|
v.push(Specifier::Namespace {
|
||||||
}),
|
local: n.name.clone().into(),
|
||||||
|
all: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
ExportSpecifier::Default(d) => {
|
ExportSpecifier::Default(d) => {
|
||||||
v.push(Specifier::Specific {
|
v.push(Specifier::Specific {
|
||||||
local: d.exported.clone().into(),
|
local: d.exported.clone().into(),
|
||||||
@ -213,6 +238,11 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if need_wrapping {
|
||||||
|
self.mark_as_wrapping_required(&named.src.as_ref().unwrap().value);
|
||||||
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -127,7 +127,7 @@ where
|
|||||||
L: Load,
|
L: Load,
|
||||||
R: Resolve,
|
R: Resolve,
|
||||||
{
|
{
|
||||||
fn ctxt_for(&self, src: &str) -> Option<SyntaxContext> {
|
fn ctxt_for(&self, src: &JsWord) -> Option<SyntaxContext> {
|
||||||
// Don't apply mark if it's a core module.
|
// Don't apply mark if it's a core module.
|
||||||
if self
|
if self
|
||||||
.bundler
|
.bundler
|
||||||
@ -144,6 +144,27 @@ where
|
|||||||
|
|
||||||
Some(ctxt.apply_mark(mark))
|
Some(ctxt.apply_mark(mark))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn mark_as_wrapping_required(&self, src: &JsWord) {
|
||||||
|
// Don't apply mark if it's a core module.
|
||||||
|
if self
|
||||||
|
.bundler
|
||||||
|
.config
|
||||||
|
.external_modules
|
||||||
|
.iter()
|
||||||
|
.any(|v| v == src)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let path = self.bundler.resolve(self.path, src);
|
||||||
|
let path = match path {
|
||||||
|
Ok(v) => v,
|
||||||
|
Err(_) => return,
|
||||||
|
};
|
||||||
|
let (id, _) = self.bundler.scope.module_id_gen.gen(&path);
|
||||||
|
|
||||||
|
self.bundler.scope.mark_as_wrapping_required(id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<L, R> Fold for ImportHandler<'_, '_, L, R>
|
impl<L, R> Fold for ImportHandler<'_, '_, L, R>
|
||||||
@ -268,10 +289,13 @@ where
|
|||||||
});
|
});
|
||||||
|
|
||||||
if self.deglob_phase {
|
if self.deglob_phase {
|
||||||
|
let mut wrapping_required = vec![];
|
||||||
for import in self.info.imports.iter_mut() {
|
for import in self.info.imports.iter_mut() {
|
||||||
let use_ns = self.info.forced_ns.contains(&import.src.value);
|
let use_ns = self.info.forced_ns.contains(&import.src.value);
|
||||||
|
|
||||||
if use_ns {
|
if use_ns {
|
||||||
|
wrapping_required.push(import.src.value.clone());
|
||||||
|
|
||||||
import.specifiers.retain(|s| match s {
|
import.specifiers.retain(|s| match s {
|
||||||
ImportSpecifier::Namespace(_) => true,
|
ImportSpecifier::Namespace(_) => true,
|
||||||
_ => false,
|
_ => false,
|
||||||
@ -290,6 +314,10 @@ where
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for id in wrapping_required {
|
||||||
|
self.mark_as_wrapping_required(&id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
items
|
items
|
||||||
|
@ -1,74 +1,75 @@
|
|||||||
use super::ImportHandler;
|
// use super::ImportHandler;
|
||||||
use crate::bundler::tests::suite;
|
// use crate::bundler::tests::suite;
|
||||||
use std::path::Path;
|
// use std::path::Path;
|
||||||
use swc_common::{FileName, Mark, SyntaxContext};
|
// use swc_common::{FileName, Mark, SyntaxContext};
|
||||||
use swc_ecma_visit::FoldWith;
|
// use swc_ecma_visit::FoldWith;
|
||||||
|
|
||||||
#[test]
|
// #[test]
|
||||||
#[ignore]
|
// #[ignore]
|
||||||
fn ns_import_deglob_simple() {
|
// fn ns_import_deglob_simple() {
|
||||||
suite().run(|t| {
|
// suite().run(|t| {
|
||||||
let m = t.parse(
|
// let m = t.parse(
|
||||||
"
|
// "
|
||||||
import * as ns from 'foo';
|
// import * as ns from 'foo';
|
||||||
ns.foo();
|
// ns.foo();
|
||||||
",
|
// ",
|
||||||
);
|
// );
|
||||||
let mut v = ImportHandler {
|
// let mut v = ImportHandler {
|
||||||
path: &FileName::Real(Path::new("index.js").to_path_buf()),
|
// path: &FileName::Real(Path::new("index.js").to_path_buf()),
|
||||||
bundler: &t.bundler,
|
// bundler: &t.bundler,
|
||||||
top_level: false,
|
// top_level: false,
|
||||||
info: Default::default(),
|
// info: Default::default(),
|
||||||
ns_usage: Default::default(),
|
// ns_usage: Default::default(),
|
||||||
deglob_phase: false,
|
// deglob_phase: false,
|
||||||
imported_idents: Default::default(),
|
// imported_idents: Default::default(),
|
||||||
module_ctxt: SyntaxContext::empty().apply_mark(Mark::fresh(Mark::root())),
|
// module_ctxt:
|
||||||
idents_to_deglob: Default::default(),
|
// SyntaxContext::empty().apply_mark(Mark::fresh(Mark::root())),
|
||||||
};
|
// idents_to_deglob: Default::default(), };
|
||||||
let m = m.fold_with(&mut v);
|
// let m = m.fold_with(&mut v);
|
||||||
assert!(v.info.forced_ns.is_empty());
|
// assert!(v.info.forced_ns.is_empty());
|
||||||
assert_eq!(v.info.imports.len(), 1);
|
// assert_eq!(v.info.imports.len(), 1);
|
||||||
|
|
||||||
t.assert_eq(&m, "foo();");
|
// t.assert_eq(&m, "foo();");
|
||||||
|
|
||||||
Ok(())
|
// Ok(())
|
||||||
})
|
// })
|
||||||
}
|
// }
|
||||||
|
|
||||||
#[test]
|
// #[test]
|
||||||
#[ignore]
|
// #[ignore]
|
||||||
fn ns_import_deglob_multi() {
|
// fn ns_import_deglob_multi() {
|
||||||
suite().run(|t| {
|
// suite().run(|t| {
|
||||||
let m = t.parse(
|
// let m = t.parse(
|
||||||
"
|
// "
|
||||||
import * as ns from 'foo';
|
// import * as ns from 'foo';
|
||||||
ns.foo();
|
// ns.foo();
|
||||||
ns.bar();
|
// ns.bar();
|
||||||
",
|
// ",
|
||||||
);
|
// );
|
||||||
let mut v = ImportHandler {
|
// let mut v = ImportHandler {
|
||||||
path: &FileName::Real(Path::new("index.js").to_path_buf()),
|
// path: &FileName::Real(Path::new("index.js").to_path_buf()),
|
||||||
bundler: &t.bundler,
|
// bundler: &t.bundler,
|
||||||
top_level: false,
|
// top_level: false,
|
||||||
info: Default::default(),
|
// info: Default::default(),
|
||||||
ns_usage: Default::default(),
|
// ns_usage: Default::default(),
|
||||||
deglob_phase: false,
|
// deglob_phase: false,
|
||||||
imported_idents: Default::default(),
|
// imported_idents: Default::default(),
|
||||||
module_ctxt: SyntaxContext::empty().apply_mark(Mark::fresh(Mark::root())),
|
// module_ctxt:
|
||||||
idents_to_deglob: Default::default(),
|
// SyntaxContext::empty().apply_mark(Mark::fresh(Mark::root())),
|
||||||
};
|
// idents_to_deglob: Default::default(), };
|
||||||
let m = m.fold_with(&mut v);
|
// let m = m.fold_with(&mut v);
|
||||||
assert!(v.info.forced_ns.is_empty());
|
// assert!(v.info.forced_ns.is_empty());
|
||||||
assert_eq!(v.info.imports.len(), 1);
|
// assert_eq!(v.info.imports.len(), 1);
|
||||||
assert_eq!(v.info.imports[0].specifiers.len(), 2);
|
// assert_eq!(v.info.imports[0].specifiers.len(), 2);
|
||||||
assert!(!format!("{:?}", v.info.imports[0].specifiers).contains("ns"));
|
// assert!(!format!("{:?}",
|
||||||
|
// v.info.imports[0].specifiers).contains("ns"));
|
||||||
|
|
||||||
t.assert_eq(
|
// t.assert_eq(
|
||||||
&m,
|
// &m,
|
||||||
"foo();
|
// "foo();
|
||||||
bar();",
|
// bar();",
|
||||||
);
|
// );
|
||||||
|
|
||||||
Ok(())
|
// Ok(())
|
||||||
})
|
// })
|
||||||
}
|
// }
|
||||||
|
@ -75,7 +75,7 @@ where
|
|||||||
.context("failed to analyze module")?;
|
.context("failed to analyze module")?;
|
||||||
files.dedup_by_key(|v| v.1.clone());
|
files.dedup_by_key(|v| v.1.clone());
|
||||||
|
|
||||||
log::debug!("({}) Storing module: {}", v.id, file_name);
|
log::debug!("({}, {:?}) Storing module: {}", v.id, v.ctxt(), file_name);
|
||||||
self.scope.store_module(v.clone());
|
self.scope.store_module(v.clone());
|
||||||
|
|
||||||
// Load dependencies and store them in the `Scope`
|
// Load dependencies and store them in the `Scope`
|
||||||
|
@ -7,7 +7,6 @@ use swc_common::{sync::Lrc, FileName, Globals, Mark, SourceMap, SyntaxContext, D
|
|||||||
use swc_ecma_ast::Module;
|
use swc_ecma_ast::Module;
|
||||||
|
|
||||||
mod chunk;
|
mod chunk;
|
||||||
mod computed_key;
|
|
||||||
mod export;
|
mod export;
|
||||||
mod finalize;
|
mod finalize;
|
||||||
mod helpers;
|
mod helpers;
|
||||||
|
@ -3,7 +3,8 @@ use crate::{
|
|||||||
id::{ModuleId, ModuleIdGenerator},
|
id::{ModuleId, ModuleIdGenerator},
|
||||||
util::CloneMap,
|
util::CloneMap,
|
||||||
};
|
};
|
||||||
use swc_common::FileName;
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
|
use swc_common::{sync::Lrc, FileName};
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub(super) struct Metadata {
|
pub(super) struct Metadata {
|
||||||
@ -18,6 +19,8 @@ pub(super) struct Scope {
|
|||||||
|
|
||||||
/// Cached after applying basical transformations.
|
/// Cached after applying basical transformations.
|
||||||
transformed_modules: CloneMap<ModuleId, TransformedModule>,
|
transformed_modules: CloneMap<ModuleId, TransformedModule>,
|
||||||
|
|
||||||
|
accessed_with_computed_key: CloneMap<ModuleId, Lrc<AtomicBool>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Scope {
|
impl Scope {
|
||||||
@ -40,4 +43,23 @@ impl Scope {
|
|||||||
pub fn get_module(&self, id: ModuleId) -> Option<TransformedModule> {
|
pub fn get_module(&self, id: ModuleId) -> Option<TransformedModule> {
|
||||||
Some(self.transformed_modules.get(&id)?.clone())
|
Some(self.transformed_modules.get(&id)?.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set the module as
|
||||||
|
pub fn mark_as_wrapping_required(&self, id: ModuleId) {
|
||||||
|
if let Some(v) = self.accessed_with_computed_key.get(&id) {
|
||||||
|
v.store(true, Ordering::SeqCst);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.accessed_with_computed_key
|
||||||
|
.insert(id, Lrc::new(AtomicBool::from(true)));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn should_be_wrapped_with_a_fn(&self, id: ModuleId) -> bool {
|
||||||
|
if let Some(v) = self.accessed_with_computed_key.get(&id) {
|
||||||
|
v.load(Ordering::SeqCst)
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -70,6 +70,7 @@ impl<'a> Tester<'a> {
|
|||||||
.unwrap_or_else(|| panic!("failed to find module named {}", name))
|
.unwrap_or_else(|| panic!("failed to find module named {}", name))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
pub fn parse(&self, s: &str) -> Module {
|
pub fn parse(&self, s: &str) -> Module {
|
||||||
let fm = self
|
let fm = self
|
||||||
.cm
|
.cm
|
||||||
@ -85,6 +86,7 @@ impl<'a> Tester<'a> {
|
|||||||
parser.parse_module().unwrap()
|
parser.parse_module().unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
pub fn assert_eq(&self, m: &Module, expected: &str) {
|
pub fn assert_eq(&self, m: &Module, expected: &str) {
|
||||||
let expected = self.parse(expected);
|
let expected = self.parse(expected);
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
|
|
||||||
use std::io::{stderr, Write};
|
use std::io::{stderr, Write};
|
||||||
use swc_common::{sync::Lrc, SourceMap};
|
use swc_common::{sync::Lrc, SourceMap, SyntaxContext};
|
||||||
use swc_ecma_ast::{Ident, Module};
|
use swc_ecma_ast::{Ident, Module};
|
||||||
use swc_ecma_codegen::{text_writer::JsWriter, Emitter};
|
use swc_ecma_codegen::{text_writer::JsWriter, Emitter};
|
||||||
use swc_ecma_visit::{noop_fold_type, Fold, FoldWith};
|
use swc_ecma_visit::{noop_fold_type, Fold, FoldWith};
|
||||||
@ -30,6 +30,9 @@ impl Fold for HygieneVisualizer {
|
|||||||
noop_fold_type!();
|
noop_fold_type!();
|
||||||
|
|
||||||
fn fold_ident(&mut self, node: Ident) -> Ident {
|
fn fold_ident(&mut self, node: Ident) -> Ident {
|
||||||
|
if node.span.ctxt == SyntaxContext::empty() {
|
||||||
|
return node;
|
||||||
|
}
|
||||||
Ident {
|
Ident {
|
||||||
sym: format!("{}{:?}", node.sym, node.span.ctxt()).into(),
|
sym: format!("{}{:?}", node.sym, node.span.ctxt()).into(),
|
||||||
..node
|
..node
|
||||||
|
@ -1,7 +1,103 @@
|
|||||||
use std::hash::Hash;
|
use std::{hash::Hash, mem::replace};
|
||||||
use swc_common::{Span, SyntaxContext};
|
use swc_atoms::js_word;
|
||||||
|
use swc_common::{Span, SyntaxContext, DUMMY_SP};
|
||||||
|
use swc_ecma_ast::*;
|
||||||
use swc_ecma_visit::{noop_visit_mut_type, VisitMut};
|
use swc_ecma_visit::{noop_visit_mut_type, VisitMut};
|
||||||
|
|
||||||
|
/// Helper for migration from [Fold] to [VisitMut]
|
||||||
|
pub(crate) trait MapWithMut: Sized {
|
||||||
|
fn dummy() -> Self;
|
||||||
|
|
||||||
|
fn take(&mut self) -> Self {
|
||||||
|
replace(self, Self::dummy())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn map_with_mut<F>(&mut self, op: F)
|
||||||
|
where
|
||||||
|
F: FnOnce(Self) -> Self,
|
||||||
|
{
|
||||||
|
let dummy = Self::dummy();
|
||||||
|
let v = replace(self, dummy);
|
||||||
|
let v = op(v);
|
||||||
|
let _dummy = replace(self, v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MapWithMut for ModuleItem {
|
||||||
|
#[inline(always)]
|
||||||
|
fn dummy() -> Self {
|
||||||
|
ModuleItem::Stmt(Stmt::Empty(EmptyStmt { span: DUMMY_SP }))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MapWithMut for Stmt {
|
||||||
|
#[inline(always)]
|
||||||
|
fn dummy() -> Self {
|
||||||
|
Stmt::Empty(EmptyStmt { span: DUMMY_SP })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MapWithMut for Expr {
|
||||||
|
#[inline(always)]
|
||||||
|
fn dummy() -> Self {
|
||||||
|
Expr::Invalid(Invalid { span: DUMMY_SP })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MapWithMut for Pat {
|
||||||
|
#[inline(always)]
|
||||||
|
fn dummy() -> Self {
|
||||||
|
Pat::Invalid(Invalid { span: DUMMY_SP })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> MapWithMut for Option<T> {
|
||||||
|
#[inline(always)]
|
||||||
|
fn dummy() -> Self {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> MapWithMut for Vec<T> {
|
||||||
|
#[inline(always)]
|
||||||
|
fn dummy() -> Self {
|
||||||
|
Vec::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> MapWithMut for Box<T>
|
||||||
|
where
|
||||||
|
T: MapWithMut,
|
||||||
|
{
|
||||||
|
#[inline(always)]
|
||||||
|
fn dummy() -> Self {
|
||||||
|
Box::new(T::dummy())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MapWithMut for Ident {
|
||||||
|
fn dummy() -> Self {
|
||||||
|
Ident::new(js_word!(""), DUMMY_SP)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MapWithMut for ObjectPatProp {
|
||||||
|
fn dummy() -> Self {
|
||||||
|
ObjectPatProp::Assign(AssignPatProp {
|
||||||
|
span: DUMMY_SP,
|
||||||
|
key: Ident::dummy(),
|
||||||
|
value: None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MapWithMut for PatOrExpr {
|
||||||
|
fn dummy() -> Self {
|
||||||
|
PatOrExpr::Pat(Box::new(Pat::Ident(Ident::dummy())))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) struct CHashSet<V>
|
pub(crate) struct CHashSet<V>
|
||||||
where
|
where
|
||||||
|
@ -2,12 +2,16 @@
|
|||||||
//!
|
//!
|
||||||
//! This module exists because this is way easier than using copying requires
|
//! This module exists because this is way easier than using copying requires
|
||||||
//! files.
|
//! files.
|
||||||
|
|
||||||
use anyhow::{Context, Error};
|
use anyhow::{Context, Error};
|
||||||
use std::collections::HashMap;
|
use std::{
|
||||||
|
collections::HashMap,
|
||||||
|
fs::write,
|
||||||
|
process::{Command, Stdio},
|
||||||
|
};
|
||||||
use swc_bundler::{Bundler, Load, Resolve};
|
use swc_bundler::{Bundler, Load, Resolve};
|
||||||
use swc_common::{sync::Lrc, FileName, SourceFile, SourceMap, Span, GLOBALS};
|
use swc_common::{sync::Lrc, FileName, SourceFile, SourceMap, Span, GLOBALS};
|
||||||
use swc_ecma_ast::{Expr, Lit, Module, Str};
|
use swc_ecma_ast::{Expr, Lit, Module, Str};
|
||||||
|
use swc_ecma_codegen::{text_writer::JsWriter, Emitter};
|
||||||
use swc_ecma_parser::{lexer::Lexer, JscTarget, Parser, StringInput, Syntax, TsConfig};
|
use swc_ecma_parser::{lexer::Lexer, JscTarget, Parser, StringInput, Syntax, TsConfig};
|
||||||
use swc_ecma_transforms::typescript::strip;
|
use swc_ecma_transforms::typescript::strip;
|
||||||
use swc_ecma_visit::FoldWith;
|
use swc_ecma_visit::FoldWith;
|
||||||
@ -15,11 +19,53 @@ use url::Url;
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[ignore = "Too slow"]
|
#[ignore = "Too slow"]
|
||||||
fn oak_6_2_0_application() {
|
fn oak_6_3_1_application() {
|
||||||
bundle("https://deno.land/x/oak@v6.2.0/application.ts");
|
run("https://deno.land/x/oak@v6.3.1/application.ts");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bundle(url: &str) -> Module {
|
#[test]
|
||||||
|
#[ignore = "Too slow"]
|
||||||
|
fn oak_6_3_1_mod() {
|
||||||
|
run("https://deno.land/x/oak@v6.3.1/mod.ts");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[ignore = "Too slow"]
|
||||||
|
fn std_0_74_9_http_server() {
|
||||||
|
run("https://deno.land/std@0.74.0/http/server.ts");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[ignore = "Too slow"]
|
||||||
|
fn oak_6_3_1_example() {
|
||||||
|
run("https://deno.land/x/oak@v6.3.1/examples/server.ts");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(url: &str) {
|
||||||
|
let dir = tempfile::tempdir().expect("failed to crate temp file");
|
||||||
|
let path = dir.path().join("main.js");
|
||||||
|
println!("{}", path.display());
|
||||||
|
|
||||||
|
let src = bundle(url);
|
||||||
|
write(&path, &src).unwrap();
|
||||||
|
|
||||||
|
let output = Command::new("deno")
|
||||||
|
.arg("run")
|
||||||
|
.arg("--allow-all")
|
||||||
|
.arg("--no-check")
|
||||||
|
.arg(&path)
|
||||||
|
.stdout(Stdio::piped())
|
||||||
|
.stderr(Stdio::piped())
|
||||||
|
.status()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
std::mem::forget(dir);
|
||||||
|
|
||||||
|
dbg!(output);
|
||||||
|
assert!(output.success());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bundle(url: &str) -> String {
|
||||||
let result = testing::run_test2(false, |cm, _handler| {
|
let result = testing::run_test2(false, |cm, _handler| {
|
||||||
GLOBALS.with(|globals| {
|
GLOBALS.with(|globals| {
|
||||||
let bundler = Bundler::new(
|
let bundler = Bundler::new(
|
||||||
@ -29,7 +75,7 @@ fn bundle(url: &str) -> Module {
|
|||||||
Resolver,
|
Resolver,
|
||||||
swc_bundler::Config {
|
swc_bundler::Config {
|
||||||
require: false,
|
require: false,
|
||||||
disable_inliner: true,
|
disable_inliner: false,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
Box::new(Hook),
|
Box::new(Hook),
|
||||||
@ -37,8 +83,21 @@ fn bundle(url: &str) -> Module {
|
|||||||
let mut entries = HashMap::new();
|
let mut entries = HashMap::new();
|
||||||
entries.insert("main".to_string(), FileName::Custom(url.to_string()));
|
entries.insert("main".to_string(), FileName::Custom(url.to_string()));
|
||||||
let output = bundler.bundle(entries).unwrap();
|
let output = bundler.bundle(entries).unwrap();
|
||||||
|
let module = output.into_iter().next().unwrap().module;
|
||||||
|
|
||||||
Ok(output.into_iter().next().unwrap().module)
|
let mut buf = vec![];
|
||||||
|
{
|
||||||
|
Emitter {
|
||||||
|
cfg: swc_ecma_codegen::Config { minify: false },
|
||||||
|
cm: cm.clone(),
|
||||||
|
comments: None,
|
||||||
|
wr: Box::new(JsWriter::new(cm.clone(), "\n", &mut buf, None)),
|
||||||
|
}
|
||||||
|
.emit_module(&module)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(String::from_utf8_lossy(&buf).to_string())
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
function a() {
|
function a() {
|
||||||
console.log("a");
|
console.log("a");
|
||||||
}
|
}
|
||||||
|
const a1 = a;
|
||||||
function b() {
|
function b() {
|
||||||
a();
|
a1();
|
||||||
}
|
}
|
||||||
b();
|
b();
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
const a = 'a';
|
const a = 'a';
|
||||||
|
const a1 = a;
|
||||||
function b() {
|
function b() {
|
||||||
return a;
|
return a1;
|
||||||
}
|
}
|
||||||
b();
|
b();
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
class a {
|
class a {
|
||||||
}
|
}
|
||||||
|
const a1 = a;
|
||||||
function b() {
|
function b() {
|
||||||
return new a();
|
return new a1();
|
||||||
}
|
}
|
||||||
b();
|
b();
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
function deferred() {
|
function deferred() {
|
||||||
}
|
}
|
||||||
|
const deferred1 = deferred;
|
||||||
class MuxAsyncIterator {
|
class MuxAsyncIterator {
|
||||||
constructor(){
|
constructor(){
|
||||||
this.signal = deferred();
|
this.signal = deferred();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const MuxAsyncIterator1 = MuxAsyncIterator;
|
const MuxAsyncIterator1 = MuxAsyncIterator;
|
||||||
const deferred1 = deferred;
|
|
||||||
console.log(deferred1, MuxAsyncIterator1);
|
console.log(deferred1, MuxAsyncIterator1);
|
||||||
|
@ -1,2 +1,3 @@
|
|||||||
const a = "a";
|
const a = "a";
|
||||||
console.log(a);
|
const a1 = a;
|
||||||
|
console.log(a1);
|
||||||
|
Loading…
Reference in New Issue
Block a user