Fix bundler (#1234)

swc_bundler:
 - Handle dependencies of circular modules in wrapped modules. (#1214)
 - Handle `export * from './file.ts'` properly. (denoland/deno#8481)
 - Fix deglobbing. (denoland/deno#8486)
This commit is contained in:
강동윤 2020-11-27 19:37:22 +09:00 committed by GitHub
parent f8a1fb878d
commit 8ca3d1160d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 173 additions and 59 deletions

View File

@ -8,7 +8,7 @@ edition = "2018"
license = "Apache-2.0/MIT"
name = "swc_bundler"
repository = "https://github.com/swc-project/swc.git"
version = "0.17.1"
version = "0.17.2"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[features]

View File

@ -1,9 +1,6 @@
use super::plan::CircularPlan;
use crate::{
bundler::{
chunk::{merge::Ctx, sort::sort},
load::TransformedModule,
},
bundler::chunk::{merge::Ctx, sort::sort},
id::Id,
Bundler, Load, ModuleId, Resolve,
};
@ -34,16 +31,6 @@ where
"# of circular modules should be 2 or greater than 2 including entry. Got {:?}",
plan
);
let entry_module = self.scope.get_module(entry_id).unwrap();
let modules = plan
.chunks
.iter()
.map(|&id| self.scope.get_module(id).unwrap())
.collect::<Vec<_>>();
let mut entry = self
.merge_modules(ctx, entry_id, false, false)
.context("failed to merge dependency of a cyclic module")?;
if !ctx.merged.insert(entry_id) {
log::debug!("[circular] skip: {:?}", entry_id);
@ -54,6 +41,14 @@ where
});
}
log::debug!("[circular] Stsrting with: {:?}", entry_id);
let entry_module = self.scope.get_module(entry_id).unwrap();
let mut entry = self
.merge_modules(ctx, entry_id, false, false)
.context("failed to merge dependency of a cyclic module")?;
let mut exports = vec![];
for item in entry.body.iter_mut() {
match item {
@ -124,24 +119,8 @@ where
// imports: &entry_module.imports,
// });
// print_hygiene("entry:drop_imports", &self.cm, &entry);
let mut deps = plan.chunks.clone();
deps.retain(|&dep| {
if dep == entry_id {
return false;
}
if !ctx.merged.insert(dep) {
log::debug!("[circular] skip: {:?}", dep);
return false;
}
log::debug!("Circular merge: {:?}", dep);
true
});
deps.sort();
let new_module = self.merge_circular_modules(ctx, &modules, entry, deps)?;
let new_module = self.merge_circular_modules(ctx, entry, entry_id, plan.chunks.clone())?;
entry = new_module;
@ -164,13 +143,29 @@ where
}
/// Merges `a` and `b` into one module.
fn merge_circular_modules(
pub(super) fn merge_circular_modules(
&self,
ctx: &Ctx,
_circular_modules: &[TransformedModule],
mut entry: Module,
deps: Vec<ModuleId>,
entry_id: ModuleId,
mut deps: Vec<ModuleId>,
) -> Result<Module, Error> {
deps.retain(|&dep| {
if dep == entry_id {
return false;
}
if !ctx.merged.insert(dep) {
log::debug!("[circular] skip: {:?}", dep);
return false;
}
log::debug!("Circular merge: {:?}", dep);
true
});
deps.sort();
self.run(|| {
let mut dep_body = vec![];

View File

@ -238,12 +238,15 @@ where
}
}
pub(super) struct ExportInjector {
pub(super) struct ExportInjector<'a> {
pub ctx: &'a Ctx,
pub export_ctxt: SyntaxContext,
pub wrapped: bool,
pub imported: Vec<ModuleItem>,
pub source: Source,
}
impl VisitMut for ExportInjector {
impl VisitMut for ExportInjector<'_> {
noop_visit_mut_type!();
fn visit_mut_module_items(&mut self, orig: &mut Vec<ModuleItem>) {
@ -298,6 +301,13 @@ impl VisitMut for ExportInjector {
ModuleItem::ModuleDecl(ModuleDecl::ExportAll(ref export))
if export.src.value == self.source.src.value =>
{
if !self.wrapped {
let export_ctxt = export.span.ctxt;
self.ctx
.transitive_remap
.insert(self.export_ctxt, export_ctxt);
}
buf.extend(take(&mut self.imported));
}

View File

@ -80,7 +80,7 @@ where
// &module,
// );
module = self.merge_deps(ctx, is_entry, module, plan, &info)?;
module = self.merge_deps(ctx, is_entry, module, plan, &info, !allow_circular)?;
// print_hygiene(
// &format!("after merging deps: {}", info.fm.name),
@ -150,18 +150,16 @@ where
if let Some(plan) = ctx.plan.circular.get(&dep_id) {
module = self
.merge_circular(ctx, plan, dep_id)
.merge_circular_modules(ctx, module, dep_id, plan.chunks.clone())
.with_context(|| format!("failed to merge {:?} (circular import)", dep_id))?;
}
module = self
.merge_deps(ctx, false, module, plan, &dep_info)
.merge_deps(ctx, false, module, plan, &dep_info, false)
.context("failed to merge dependencies")?;
self.handle_import_deps(ctx, &dep_info, &mut module, false);
// print_hygiene("wrapped: after deps", &self.cm, &module);
module
} else {
let mut module = self.merge_modules(ctx, dep_id, false, true)?;
@ -278,6 +276,7 @@ where
mut module: Module,
plan: &NormalPlan,
info: &TransformedModule,
from_circular: bool,
) -> Result<Module, Error> {
self.run(|| -> Result<_, Error> {
log::debug!(
@ -287,15 +286,18 @@ where
plan
);
let deps = info
.exports
.reexports
.iter()
.map(|v| &v.0)
.cloned()
.filter(|source| plan.chunks.iter().all(|chunk| chunk.id != source.module_id))
.collect();
self.transform_indirect_reexports(ctx, &mut module, deps)?;
if !from_circular {
let deps = info
.exports
.reexports
.iter()
.map(|v| &v.0)
.cloned()
.filter(|source| plan.chunks.iter().all(|chunk| chunk.id != source.module_id))
.collect();
self.transform_indirect_reexports(ctx, &mut module, deps)?;
}
if plan.chunks.is_empty() {
return Ok(module);
}
@ -373,6 +375,10 @@ where
let mut injector = ExportInjector {
imported: take(&mut dep_module.body),
source: source.unwrap().clone(),
ctx,
export_ctxt: info.export_ctxt(),
// We use id of the entry
wrapped: self.scope.should_be_wrapped_with_a_fn(info.id),
};
module.body.visit_mut_with(&mut injector);
@ -503,7 +509,7 @@ where
/// This should only be called after everything is merged.
///
/// This method does not care about orders of statement, and it's expected
/// to be collaed before `sort`.
/// to be called before `sort`.
fn handle_export_stars(&self, ctx: &Ctx, entry: &mut Module) {
{
// Handle `export *` for non-wrapped modules.
@ -525,6 +531,7 @@ where
}
}
}
_ => {}
}
}

View File

@ -448,6 +448,17 @@ where
}
// Handle circular imports
for (root_entry, _) in builder.kinds.iter() {
if let Some(members) = builder.circular.get(*root_entry) {
plans
.normal
.entry(*root_entry)
.or_default()
.chunks
.retain(|dep| !members.contains(&dep.id));
}
}
for (root_entry, _) in builder.kinds.iter() {
let mut bfs = Bfs::new(&builder.direct_deps, *root_entry);

View File

@ -364,11 +364,7 @@ where
}
Expr::Member(mut e) => {
e.obj = e.obj.fold_with(self);
if e.computed {
e.prop = e.prop.fold_with(self);
}
e = e.fold_with(self);
match &e.obj {
ExprOrSuper::Expr(obj) => {
@ -377,11 +373,11 @@ where
// Deglob identifier usages.
if self.deglob_phase && self.idents_to_deglob.contains(&i.to_id()) {
match *e.prop {
Expr::Ident(prop) => {
Expr::Ident(prop) if prop.sym == i.sym => {
return Expr::Ident(Ident::new(
prop.sym,
prop.span.with_ctxt(i.span.ctxt),
))
));
}
_ => {}
}

View File

@ -299,6 +299,75 @@ fn deno_8399_2() {
run("tests/deno/issue-8399-2/input.ts", &[]);
}
#[test]
fn deno_8486_1() {
run("tests/deno/issue-8486-1/input.ts", &["myCLI"]);
}
#[test]
fn deno_7288_1() {
run("tests/deno/deno-7288-1/input.ts", &[]);
}
#[test]
fn deno_8481_1() {
run(
"https://raw.githubusercontent.com/nats-io/nats.ws/master/src/mod.ts",
&[
"Bench",
"Connect",
"DataBuffer",
"DebugEvents",
"DenoBuffer",
"Empty",
"ErrorCode",
"Events",
"Heartbeat",
"INFO",
"JSONCodec",
"Kind",
"MAX_SIZE",
"Metric",
"MsgHdrsImpl",
"MsgImpl",
"MuxSubscription",
"NatsConnectionImpl",
"NatsError",
"Nuid",
"Parser",
"ProtocolHandler",
"QueuedIterator",
"Request",
"State",
"StringCodec",
"SubscriptionImpl",
"Subscriptions",
"TD",
"TE",
"checkOptions",
"connect",
"createInbox",
"credsAuthenticator",
"deferred",
"delay",
"extractProtocolMessage",
"headers",
"isIP",
"jwtAuthenticator",
"nkeyAuthenticator",
"nkeys",
"nuid",
"parseIP",
"readAll",
"render",
"setTransportFactory",
"setUrlParseFn",
"timeout",
"writeAll",
],
)
}
#[test]
fn merging_order_01() {
run(

View File

@ -0,0 +1,2 @@
import * as path from "https://deno.land/std@0.67.0/path/mod.ts";
console.log(path);

View File

@ -0,0 +1,24 @@
import * as log from "https://deno.land/std/log/mod.ts";
export async function myCLI(): Promise<void> {
await log.setup({
handlers: {
file: new log.handlers.FileHandler("DEBUG", {
filename: 'my.log'
}),
console: new log.handlers.ConsoleHandler("INFO")
},
loggers: {
default: {
level: "DEBUG",
handlers: ["console", "file"]
}
}
});
log.info("Ok!");
}
if (import.meta.main) {
myCLI();
}