mirror of
https://github.com/swc-project/swc.git
synced 2024-12-25 06:36:08 +03:00
bundler: Fix bugs (#1154)
swc_bundler: - Fix importing from transitive dependencies with aliases. - Fix stack overflow while computing plan for modules with cyclic dependencies. - Handle `export *` for function / class properly. (#1155)
This commit is contained in:
parent
f0ea70cb25
commit
6f006208ac
@ -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.10.2"
|
version = "0.10.3"
|
||||||
|
|
||||||
# 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]
|
||||||
|
@ -284,6 +284,7 @@ where
|
|||||||
let mut injector = Es6ModuleInjector {
|
let mut injector = Es6ModuleInjector {
|
||||||
imported: take(&mut dep.body),
|
imported: take(&mut dep.body),
|
||||||
ctxt: dep_info.ctxt(),
|
ctxt: dep_info.ctxt(),
|
||||||
|
is_direct,
|
||||||
};
|
};
|
||||||
entry.body.visit_mut_with(&mut injector);
|
entry.body.visit_mut_with(&mut injector);
|
||||||
|
|
||||||
@ -495,6 +496,7 @@ impl Fold for Unexporter {
|
|||||||
struct Es6ModuleInjector {
|
struct Es6ModuleInjector {
|
||||||
imported: Vec<ModuleItem>,
|
imported: Vec<ModuleItem>,
|
||||||
ctxt: SyntaxContext,
|
ctxt: SyntaxContext,
|
||||||
|
is_direct: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VisitMut for Es6ModuleInjector {
|
impl VisitMut for Es6ModuleInjector {
|
||||||
@ -507,10 +509,43 @@ impl VisitMut for Es6ModuleInjector {
|
|||||||
for item in items {
|
for item in items {
|
||||||
//
|
//
|
||||||
match item {
|
match item {
|
||||||
ModuleItem::ModuleDecl(ModuleDecl::Import(ImportDecl { span, .. }))
|
ModuleItem::ModuleDecl(ModuleDecl::Import(ImportDecl {
|
||||||
if span.ctxt == self.ctxt =>
|
span, specifiers, ..
|
||||||
{
|
})) if span.ctxt == self.ctxt => {
|
||||||
buf.extend(take(&mut self.imported));
|
buf.extend(take(&mut self.imported));
|
||||||
|
|
||||||
|
if !self.is_direct {
|
||||||
|
let decls = specifiers
|
||||||
|
.iter()
|
||||||
|
.filter_map(|specifier| match specifier {
|
||||||
|
ImportSpecifier::Named(ImportNamedSpecifier {
|
||||||
|
local,
|
||||||
|
imported: Some(imported),
|
||||||
|
..
|
||||||
|
}) => {
|
||||||
|
let mut imported = imported.clone();
|
||||||
|
imported.span = imported.span.with_ctxt(self.ctxt);
|
||||||
|
|
||||||
|
Some(VarDeclarator {
|
||||||
|
span: DUMMY_SP,
|
||||||
|
name: Pat::Ident(local.clone()),
|
||||||
|
init: Some(Box::new(Expr::Ident(imported))),
|
||||||
|
definite: false,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
if !decls.is_empty() {
|
||||||
|
buf.push(ModuleItem::Stmt(Stmt::Decl(Decl::Var(VarDecl {
|
||||||
|
span: DUMMY_SP,
|
||||||
|
kind: VarDeclKind::Const,
|
||||||
|
declare: false,
|
||||||
|
decls,
|
||||||
|
}))));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => buf.push(item),
|
_ => buf.push(item),
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
use super::ModuleGraph;
|
use super::PlanBuilder;
|
||||||
use crate::ModuleId;
|
use crate::ModuleId;
|
||||||
use petgraph::EdgeDirection::Incoming;
|
use petgraph::EdgeDirection::Incoming;
|
||||||
|
|
||||||
// TODO: Optimize with cache.
|
// TODO: Optimize with cache.
|
||||||
pub(super) fn least_common_ancestor(g: &ModuleGraph, module_ids: &[ModuleId]) -> ModuleId {
|
pub(super) fn least_common_ancestor(b: &PlanBuilder, module_ids: &[ModuleId]) -> ModuleId {
|
||||||
assert_ne!(
|
assert_ne!(
|
||||||
module_ids,
|
module_ids,
|
||||||
&[],
|
&[],
|
||||||
@ -12,6 +12,7 @@ pub(super) fn least_common_ancestor(g: &ModuleGraph, module_ids: &[ModuleId]) ->
|
|||||||
if module_ids.len() == 1 {
|
if module_ids.len() == 1 {
|
||||||
return module_ids[0];
|
return module_ids[0];
|
||||||
}
|
}
|
||||||
|
let g = &b.direct_deps;
|
||||||
|
|
||||||
// Check for roots
|
// Check for roots
|
||||||
for &mid in module_ids {
|
for &mid in module_ids {
|
||||||
@ -28,7 +29,7 @@ pub(super) fn least_common_ancestor(g: &ModuleGraph, module_ids: &[ModuleId]) ->
|
|||||||
return first;
|
return first;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(id) = check_itself_and_parent(g, &[first], &[second]) {
|
if let Some(id) = check_itself_and_parent(b, &[first], &[second]) {
|
||||||
log::debug!("Found lca: {:?}", id);
|
log::debug!("Found lca: {:?}", id);
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
@ -43,15 +44,16 @@ pub(super) fn least_common_ancestor(g: &ModuleGraph, module_ids: &[ModuleId]) ->
|
|||||||
.iter()
|
.iter()
|
||||||
.skip(2)
|
.skip(2)
|
||||||
.cloned()
|
.cloned()
|
||||||
.fold(least_common_ancestor(g, &[first, second]), |prev, item| {
|
.fold(least_common_ancestor(b, &[first, second]), |prev, item| {
|
||||||
least_common_ancestor(g, &[prev, item])
|
least_common_ancestor(b, &[prev, item])
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_itself<I>(g: &ModuleGraph, li: I, ri: &[ModuleId]) -> Option<ModuleId>
|
fn check_itself<I>(b: &PlanBuilder, li: I, ri: &[ModuleId]) -> Option<ModuleId>
|
||||||
where
|
where
|
||||||
I: IntoIterator<Item = ModuleId>,
|
I: IntoIterator<Item = ModuleId>,
|
||||||
{
|
{
|
||||||
|
let g = &b.direct_deps;
|
||||||
for l in li {
|
for l in li {
|
||||||
// Root
|
// Root
|
||||||
if g.neighbors_directed(l, Incoming).count() == 0 {
|
if g.neighbors_directed(l, Incoming).count() == 0 {
|
||||||
@ -73,27 +75,46 @@ where
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_itself_and_parent(g: &ModuleGraph, li: &[ModuleId], ri: &[ModuleId]) -> Option<ModuleId> {
|
fn check_itself_and_parent(b: &PlanBuilder, li: &[ModuleId], ri: &[ModuleId]) -> Option<ModuleId> {
|
||||||
if let Some(id) = check_itself(g, li.iter().copied(), ri) {
|
let g = &b.direct_deps;
|
||||||
|
|
||||||
|
if let Some(id) = check_itself(b, li.iter().copied(), ri) {
|
||||||
return Some(id);
|
return Some(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
for &l in li {
|
for &l in li {
|
||||||
if let Some(id) = check_itself_and_parent(
|
let mut l_dependants = g.neighbors_directed(l, Incoming).collect::<Vec<_>>();
|
||||||
g,
|
for &l_dependant in &l_dependants {
|
||||||
&g.neighbors_directed(l, Incoming).collect::<Vec<_>>(),
|
if g.neighbors_directed(l_dependant, Incoming).count() == 0 {
|
||||||
ri,
|
return Some(l_dependant);
|
||||||
) {
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
l_dependants.retain(|&id| !b.is_circular(id));
|
||||||
|
if l_dependants.is_empty() {
|
||||||
|
return Some(l);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(id) = check_itself_and_parent(b, &l_dependants, ri) {
|
||||||
return Some(id);
|
return Some(id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for &r in ri {
|
for &r in ri {
|
||||||
if let Some(id) = check_itself_and_parent(
|
let mut r_dependants = g.neighbors_directed(r, Incoming).collect::<Vec<_>>();
|
||||||
g,
|
for &r_dependant in &r_dependants {
|
||||||
&g.neighbors_directed(r, Incoming).collect::<Vec<_>>(),
|
if g.neighbors_directed(r_dependant, Incoming).count() == 0 {
|
||||||
li,
|
return Some(r_dependant);
|
||||||
) {
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
r_dependants.retain(|&id| !b.is_circular(id));
|
||||||
|
|
||||||
|
if r_dependants.is_empty() {
|
||||||
|
return Some(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(id) = check_itself_and_parent(b, &r_dependants, li) {
|
||||||
return Some(id);
|
return Some(id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -279,7 +279,7 @@ where
|
|||||||
//
|
//
|
||||||
// a <- b
|
// a <- b
|
||||||
// a <- c
|
// a <- c
|
||||||
let module = least_common_ancestor(&builder.direct_deps, &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();
|
||||||
|
|
||||||
@ -310,7 +310,7 @@ where
|
|||||||
{
|
{
|
||||||
dependants[1]
|
dependants[1]
|
||||||
} else {
|
} else {
|
||||||
least_common_ancestor(&builder.direct_deps, &dependants)
|
least_common_ancestor(&builder, &dependants)
|
||||||
};
|
};
|
||||||
|
|
||||||
if dependants.len() == 2 && dependants.contains(&higher_module) {
|
if dependants.len() == 2 && dependants.contains(&higher_module) {
|
||||||
|
@ -451,10 +451,7 @@ 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 = match actual.rename(c.ident, true) {
|
c.ident = c.ident.fold_with(&mut actual);
|
||||||
Ok(v) => v,
|
|
||||||
Err(v) => v,
|
|
||||||
};
|
|
||||||
|
|
||||||
if self.unexport {
|
if self.unexport {
|
||||||
Stmt::Decl(Decl::Class(c)).into()
|
Stmt::Decl(Decl::Class(c)).into()
|
||||||
@ -466,10 +463,7 @@ impl Fold for ExportRenamer<'_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Decl::Fn(mut f) => {
|
Decl::Fn(mut f) => {
|
||||||
f.ident = match actual.rename(f.ident, true) {
|
f.ident = f.ident.fold_with(&mut actual);
|
||||||
Ok(v) => v,
|
|
||||||
Err(v) => v,
|
|
||||||
};
|
|
||||||
if self.unexport {
|
if self.unexport {
|
||||||
Stmt::Decl(Decl::Fn(f)).into()
|
Stmt::Decl(Decl::Fn(f)).into()
|
||||||
} else {
|
} else {
|
||||||
|
@ -16,7 +16,7 @@ use url::Url;
|
|||||||
#[test]
|
#[test]
|
||||||
#[ignore = "Too slow"]
|
#[ignore = "Too slow"]
|
||||||
fn oak_6_2_0_application() {
|
fn oak_6_2_0_application() {
|
||||||
bundle("https://deno.land/x/oak@v6.2.0/mod.ts");
|
bundle("https://deno.land/x/oak@v6.2.0/application.ts");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bundle(url: &str) -> Module {
|
fn bundle(url: &str) -> Module {
|
||||||
|
33
bundler/tests/fixture/issue-1150-2/input/entry.ts
Normal file
33
bundler/tests/fixture/issue-1150-2/input/entry.ts
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import { a as defaultA, O } from "./m.ts";
|
||||||
|
|
||||||
|
export { O } from "./m.ts";
|
||||||
|
|
||||||
|
interface AOptions {
|
||||||
|
a?(): void;
|
||||||
|
c?: O;
|
||||||
|
}
|
||||||
|
|
||||||
|
class A {
|
||||||
|
#a: () => void;
|
||||||
|
#c?: O;
|
||||||
|
constructor(o: AOptions = {}) {
|
||||||
|
const {
|
||||||
|
a = defaultA,
|
||||||
|
c,
|
||||||
|
} = o;
|
||||||
|
this.#a = a;
|
||||||
|
this.#c = c;
|
||||||
|
}
|
||||||
|
|
||||||
|
a() {
|
||||||
|
this.#a();
|
||||||
|
}
|
||||||
|
|
||||||
|
c() {
|
||||||
|
console.log(this.#c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let a = new A();
|
||||||
|
a.a();
|
||||||
|
a.c();
|
2
bundler/tests/fixture/issue-1150-2/input/m.ts
Normal file
2
bundler/tests/fixture/issue-1150-2/input/m.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
export { a } from "./n.ts";
|
||||||
|
export { O } from "./o.ts";
|
3
bundler/tests/fixture/issue-1150-2/input/n.ts
Normal file
3
bundler/tests/fixture/issue-1150-2/input/n.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export function a() {
|
||||||
|
console.log("a");
|
||||||
|
}
|
5
bundler/tests/fixture/issue-1150-2/input/o.ts
Normal file
5
bundler/tests/fixture/issue-1150-2/input/o.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
export enum O {
|
||||||
|
A,
|
||||||
|
B,
|
||||||
|
C,
|
||||||
|
}
|
33
bundler/tests/fixture/issue-1150-2/output/entry.ts
Normal file
33
bundler/tests/fixture/issue-1150-2/output/entry.ts
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
function a() {
|
||||||
|
console.log("a");
|
||||||
|
}
|
||||||
|
const a1 = a;
|
||||||
|
var O;
|
||||||
|
(function(O1) {
|
||||||
|
O1[O1["A"] = 0] = "A";
|
||||||
|
O1[O1["B"] = 1] = "B";
|
||||||
|
O1[O1["C"] = 2] = "C";
|
||||||
|
})(O || (O = {
|
||||||
|
}));
|
||||||
|
const O1 = O;
|
||||||
|
const defaultA = a1;
|
||||||
|
export { O1 as O };
|
||||||
|
class A {
|
||||||
|
#a;
|
||||||
|
#c;
|
||||||
|
constructor(o = {
|
||||||
|
}){
|
||||||
|
const { a: a2 = defaultA , c , } = o;
|
||||||
|
this.#a = a2;
|
||||||
|
this.#c = c;
|
||||||
|
}
|
||||||
|
a() {
|
||||||
|
this.#a();
|
||||||
|
}
|
||||||
|
c() {
|
||||||
|
console.log(this.#c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let a3 = new A();
|
||||||
|
a3.a();
|
||||||
|
a3.c();
|
7
bundler/tests/fixture/issue-1155-1-fn/input/entry.ts
Normal file
7
bundler/tests/fixture/issue-1155-1-fn/input/entry.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import { a } from "./p.ts";
|
||||||
|
|
||||||
|
function b() {
|
||||||
|
a();
|
||||||
|
}
|
||||||
|
|
||||||
|
b();
|
3
bundler/tests/fixture/issue-1155-1-fn/input/i.ts
Normal file
3
bundler/tests/fixture/issue-1155-1-fn/input/i.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export function a() {
|
||||||
|
console.log("a");
|
||||||
|
}
|
1
bundler/tests/fixture/issue-1155-1-fn/input/p.ts
Normal file
1
bundler/tests/fixture/issue-1155-1-fn/input/p.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from "./i.ts";
|
7
bundler/tests/fixture/issue-1155-1-fn/output/entry.ts
Normal file
7
bundler/tests/fixture/issue-1155-1-fn/output/entry.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
function a() {
|
||||||
|
console.log("a");
|
||||||
|
}
|
||||||
|
function b() {
|
||||||
|
a();
|
||||||
|
}
|
||||||
|
b();
|
7
bundler/tests/fixture/issue-1155-2-var/input/entry.ts
Normal file
7
bundler/tests/fixture/issue-1155-2-var/input/entry.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import { a } from "./p.ts";
|
||||||
|
|
||||||
|
function b() {
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
b();
|
1
bundler/tests/fixture/issue-1155-2-var/input/i.ts
Normal file
1
bundler/tests/fixture/issue-1155-2-var/input/i.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export const a = 'a';
|
1
bundler/tests/fixture/issue-1155-2-var/input/p.ts
Normal file
1
bundler/tests/fixture/issue-1155-2-var/input/p.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from "./i.ts";
|
5
bundler/tests/fixture/issue-1155-2-var/output/entry.ts
Normal file
5
bundler/tests/fixture/issue-1155-2-var/output/entry.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
const a = 'a';
|
||||||
|
function b() {
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
b();
|
7
bundler/tests/fixture/issue-1155-3-class/input/entry.ts
Normal file
7
bundler/tests/fixture/issue-1155-3-class/input/entry.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import { a } from "./p.ts";
|
||||||
|
|
||||||
|
function b() {
|
||||||
|
return new a();
|
||||||
|
}
|
||||||
|
|
||||||
|
b();
|
1
bundler/tests/fixture/issue-1155-3-class/input/i.ts
Normal file
1
bundler/tests/fixture/issue-1155-3-class/input/i.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export class a { }
|
1
bundler/tests/fixture/issue-1155-3-class/input/p.ts
Normal file
1
bundler/tests/fixture/issue-1155-3-class/input/p.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from "./i.ts";
|
6
bundler/tests/fixture/issue-1155-3-class/output/entry.ts
Normal file
6
bundler/tests/fixture/issue-1155-3-class/output/entry.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
class a {
|
||||||
|
}
|
||||||
|
function b() {
|
||||||
|
return new a();
|
||||||
|
}
|
||||||
|
b();
|
Loading…
Reference in New Issue
Block a user