diff --git a/bundler/Cargo.toml b/bundler/Cargo.toml index 501d96dcb0e..0f2610bd3cf 100644 --- a/bundler/Cargo.toml +++ b/bundler/Cargo.toml @@ -8,7 +8,7 @@ edition = "2018" license = "Apache-2.0/MIT" name = "swc_bundler" repository = "https://github.com/swc-project/swc.git" -version = "0.14.1" +version = "0.15.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [features] diff --git a/bundler/src/bundler/chunk/merge.rs b/bundler/src/bundler/chunk/merge.rs index c490b313bc1..4634aca66d2 100644 --- a/bundler/src/bundler/chunk/merge.rs +++ b/bundler/src/bundler/chunk/merge.rs @@ -322,6 +322,12 @@ where if !is_direct { prepend_stmts(&mut entry.body, injector.imported.into_iter()); + log::debug!( + "Merged {} into {} as a transitive es module", + dep_info.fm.name, + info.fm.name, + ); + // print_hygiene("ES6", &self.cm, &entry); continue; } diff --git a/bundler/src/bundler/chunk/plan/lca.rs b/bundler/src/bundler/chunk/plan/lca.rs index 802a8f784ab..a410790ae76 100644 --- a/bundler/src/bundler/chunk/plan/lca.rs +++ b/bundler/src/bundler/chunk/plan/lca.rs @@ -29,6 +29,13 @@ pub(super) fn least_common_ancestor(b: &PlanBuilder, module_ids: &[ModuleId]) -> return first; } + if b.direct_deps.contains_edge(first, second) { + return second; + } + if b.direct_deps.contains_edge(second, first) { + return first; + } + if let Some(id) = check_itself_and_parent(b, &[first], &[second]) { log::debug!("Found lca: {:?}", id); return id; diff --git a/bundler/src/bundler/chunk/plan/mod.rs b/bundler/src/bundler/chunk/plan/mod.rs index 4d2c78ac64f..9c1ce0a94a6 100644 --- a/bundler/src/bundler/chunk/plan/mod.rs +++ b/bundler/src/bundler/chunk/plan/mod.rs @@ -11,6 +11,7 @@ use petgraph::{ EdgeDirection::{Incoming, Outgoing}, }; use std::{ + cmp::Ordering, collections::{hash_map::Entry, HashMap, HashSet}, ops::{Deref, DerefMut}, }; @@ -383,6 +384,13 @@ where } } + // Sort transitive chunks topologically. + for (_, normal_plan) in &mut plans.normal { + normal_plan + .transitive_chunks + .sort_by(|a, b| toposort(&builder, *a, *b)); + } + // Handle circular imports for (root_entry, _) in builder.kinds.iter() { let mut bfs = Bfs::new(&builder.direct_deps, *root_entry); @@ -461,6 +469,7 @@ where } // dbg!(&plans); + plans } @@ -538,3 +547,17 @@ where } } } + +/// Compare topology of `i` and `k`. +fn toposort(b: &PlanBuilder, i: ModuleId, j: ModuleId) -> Ordering { + // + let higher = least_common_ancestor(b, &[i, j]); + + if higher == i { + Ordering::Greater + } else if higher == j { + Ordering::Less + } else { + Ordering::Equal + } +} diff --git a/bundler/src/bundler/chunk/plan/tests.rs b/bundler/src/bundler/chunk/plan/tests.rs index 4e5ee67b26c..dbc9ae2781f 100644 --- a/bundler/src/bundler/chunk/plan/tests.rs +++ b/bundler/src/bundler/chunk/plan/tests.rs @@ -645,7 +645,7 @@ fn deno_001() { dbg!(&p); - assert_normal_transitive(t, &p, "main", &["http-server"], &["io-bufio"]); + assert_normal_transitive(t, &p, "main", &["http-server"], &[]); assert_normal(t, &p, "io-bufio", &[]); assert_circular(t, &p, "http-server", &["_io"]); @@ -798,27 +798,27 @@ fn deno_002() { dbg!(&p); - assert_normal_transitive( - t, - &p, - "main", - &["http-server"], - &["encoding-utf8", "io-bufio", "_util-assert"], - ); + assert_normal_transitive(t, &p, "main", &["http-server"], &[]); assert_normal_transitive(t, &p, "http-server", &["async-mod"], &[]); assert_circular(t, &p, "http-server", &["http-_io"]); assert_normal(t, &p, "encoding-utf8", &[]); - assert_normal(t, &p, "io-bufio", &["bytes-mod"]); + assert_normal(t, &p, "io-bufio", &[]); assert_normal(t, &p, "_util-assert", &[]); assert_normal(t, &p, "http-_io", &["textproto-mod", "http-http_status"]); assert_circular(t, &p, "http-_io", &["http-server"]); - assert_normal(t, &p, "textproto-mod", &[]); + assert_normal_transitive( + t, + &p, + "textproto-mod", + &["bytes-mod"], + &["encoding-utf8", "io-bufio"], + ); assert_normal(t, &p, "_util-assert", &[]); diff --git a/bundler/src/id.rs b/bundler/src/id.rs index 6c590378758..38b7b72f4ae 100644 --- a/bundler/src/id.rs +++ b/bundler/src/id.rs @@ -8,7 +8,7 @@ use swc_common::{sync::Lock, FileName, Mark, SyntaxContext, DUMMY_SP}; use swc_ecma_ast::Ident; use swc_ecma_utils::ident::IdentLike; -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct ModuleId(u32); impl fmt::Display for ModuleId { @@ -17,6 +17,13 @@ impl fmt::Display for ModuleId { } } +impl fmt::Debug for ModuleId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "ModuleId({})", self.0)?; + Ok(()) + } +} + #[derive(Debug, Default)] pub(crate) struct ModuleIdGenerator { v: AtomicU32, diff --git a/bundler/tests/deno.rs b/bundler/tests/deno.rs index ff61dd95488..55d389916b9 100644 --- a/bundler/tests/deno.rs +++ b/bundler/tests/deno.rs @@ -6,6 +6,7 @@ use anyhow::{Context, Error}; use sha1::{Digest, Sha1}; use std::{ collections::HashMap, + env, fs::{create_dir_all, read_to_string, write}, path::PathBuf, process::{Command, Stdio}, @@ -20,43 +21,38 @@ use swc_ecma_visit::FoldWith; use url::Url; #[test] -#[ignore = "deno is not installed"] fn oak_6_3_1_application() { run("https://deno.land/x/oak@v6.3.1/application.ts", None); } #[test] -#[ignore = "deno is not installed"] fn oak_6_3_1_mod() { run("https://deno.land/x/oak@v6.3.1/mod.ts", None); } #[test] -#[ignore = "deno is not installed"] fn std_0_74_9_http_server() { run("https://deno.land/std@0.74.0/http/server.ts", None); } #[test] -#[ignore] +#[ignore = "Does not finish by default"] fn oak_6_3_1_example_server() { run("https://deno.land/x/oak@v6.3.1/examples/server.ts", None); } #[test] -#[ignore] +#[ignore = "Does not finish by default"] fn oak_6_3_1_example_sse_server() { run("https://deno.land/x/oak@v6.3.1/examples/sseServer.ts", None); } #[test] -#[ignore = "deno is not installed"] fn std_0_75_0_http_server() { run("https://deno.land/std@0.75.0/http/server.ts", None); } #[test] -#[ignore = "deno is not installed"] fn deno_8188() { run( "https://raw.githubusercontent.com/nats-io/nats.ws/master/src/mod.ts", @@ -65,11 +61,15 @@ fn deno_8188() { } #[test] -#[ignore = "deno is not installed"] fn deno_8189() { run("https://deno.land/x/lz4/mod.ts", None); } +#[test] +fn deno_8211() { + run("https://unpkg.com/luxon@1.25.0/src/luxon.js", None); +} + fn run(url: &str, expeceted_bytes: Option) { let dir = tempfile::tempdir().expect("failed to crate temp file"); let path = dir.path().join("main.js"); @@ -81,6 +81,10 @@ fn run(url: &str, expeceted_bytes: Option) { assert_eq!(src.len(), expected); } + if env::var("CI").is_ok() { + return; + } + let output = Command::new("deno") .arg("run") .arg("--allow-all") diff --git a/bundler/tests/fixture/deno-8211-1/input/entry.ts b/bundler/tests/fixture/deno-8211-1/input/entry.ts new file mode 100644 index 00000000000..f44f45f1b27 --- /dev/null +++ b/bundler/tests/fixture/deno-8211-1/input/entry.ts @@ -0,0 +1,7 @@ +import FixedOffsetZone from "./fixedOffsetZone"; +import Zone from "./zone"; + +export { + Zone, + FixedOffsetZone, +}; \ No newline at end of file diff --git a/bundler/tests/fixture/deno-8211-1/input/fixedOffsetZone.ts b/bundler/tests/fixture/deno-8211-1/input/fixedOffsetZone.ts new file mode 100644 index 00000000000..657de08acfa --- /dev/null +++ b/bundler/tests/fixture/deno-8211-1/input/fixedOffsetZone.ts @@ -0,0 +1,3 @@ +import Zone from "./zone"; + +export default class FixedOffsetZone extends Zone { } \ No newline at end of file diff --git a/bundler/tests/fixture/deno-8211-1/input/zone.ts b/bundler/tests/fixture/deno-8211-1/input/zone.ts new file mode 100644 index 00000000000..b5bdb997a19 --- /dev/null +++ b/bundler/tests/fixture/deno-8211-1/input/zone.ts @@ -0,0 +1 @@ +export default class Zone { } \ No newline at end of file diff --git a/bundler/tests/fixture/deno-8211-1/output/entry.ts b/bundler/tests/fixture/deno-8211-1/output/entry.ts new file mode 100644 index 00000000000..170d1f862f5 --- /dev/null +++ b/bundler/tests/fixture/deno-8211-1/output/entry.ts @@ -0,0 +1,5 @@ +class Zone { +} +class FixedOffsetZone extends Zone { +} +export { Zone, FixedOffsetZone }; diff --git a/bundler/tests/fixture/deno-8211-2/input/entry.ts b/bundler/tests/fixture/deno-8211-2/input/entry.ts new file mode 100644 index 00000000000..9233d164d1d --- /dev/null +++ b/bundler/tests/fixture/deno-8211-2/input/entry.ts @@ -0,0 +1,9 @@ +import Info from "./info"; +import Zone from "./zone"; +import FixedOffsetZone from "./fixedOffsetZone"; + +export { + Zone, + Info, + FixedOffsetZone, +}; \ No newline at end of file diff --git a/bundler/tests/fixture/deno-8211-2/input/fixedOffsetZone.ts b/bundler/tests/fixture/deno-8211-2/input/fixedOffsetZone.ts new file mode 100644 index 00000000000..657de08acfa --- /dev/null +++ b/bundler/tests/fixture/deno-8211-2/input/fixedOffsetZone.ts @@ -0,0 +1,3 @@ +import Zone from "./zone"; + +export default class FixedOffsetZone extends Zone { } \ No newline at end of file diff --git a/bundler/tests/fixture/deno-8211-2/input/info.ts b/bundler/tests/fixture/deno-8211-2/input/info.ts new file mode 100644 index 00000000000..f0574a00467 --- /dev/null +++ b/bundler/tests/fixture/deno-8211-2/input/info.ts @@ -0,0 +1,8 @@ +import FixedOffsetZone from "./fixedOffsetZone"; + + +export default class Info { + use() { + console.log(FixedOffsetZone) + } +} \ No newline at end of file diff --git a/bundler/tests/fixture/deno-8211-2/input/zone.ts b/bundler/tests/fixture/deno-8211-2/input/zone.ts new file mode 100644 index 00000000000..b5bdb997a19 --- /dev/null +++ b/bundler/tests/fixture/deno-8211-2/input/zone.ts @@ -0,0 +1 @@ +export default class Zone { } \ No newline at end of file diff --git a/bundler/tests/fixture/deno-8211-2/output/entry.ts b/bundler/tests/fixture/deno-8211-2/output/entry.ts new file mode 100644 index 00000000000..62110dcf8b2 --- /dev/null +++ b/bundler/tests/fixture/deno-8211-2/output/entry.ts @@ -0,0 +1,10 @@ +class Zone { +} +class FixedOffsetZone extends Zone { +} +class Info { + use() { + console.log(FixedOffsetZone); + } +} +export { Zone, Info, FixedOffsetZone };