mirror of
https://github.com/swc-project/swc.git
synced 2024-12-24 14:16:12 +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"
|
||||
name = "swc_bundler"
|
||||
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
|
||||
[features]
|
||||
|
@ -284,6 +284,7 @@ where
|
||||
let mut injector = Es6ModuleInjector {
|
||||
imported: take(&mut dep.body),
|
||||
ctxt: dep_info.ctxt(),
|
||||
is_direct,
|
||||
};
|
||||
entry.body.visit_mut_with(&mut injector);
|
||||
|
||||
@ -495,6 +496,7 @@ impl Fold for Unexporter {
|
||||
struct Es6ModuleInjector {
|
||||
imported: Vec<ModuleItem>,
|
||||
ctxt: SyntaxContext,
|
||||
is_direct: bool,
|
||||
}
|
||||
|
||||
impl VisitMut for Es6ModuleInjector {
|
||||
@ -507,10 +509,43 @@ impl VisitMut for Es6ModuleInjector {
|
||||
for item in items {
|
||||
//
|
||||
match item {
|
||||
ModuleItem::ModuleDecl(ModuleDecl::Import(ImportDecl { span, .. }))
|
||||
if span.ctxt == self.ctxt =>
|
||||
{
|
||||
ModuleItem::ModuleDecl(ModuleDecl::Import(ImportDecl {
|
||||
span, specifiers, ..
|
||||
})) if span.ctxt == self.ctxt => {
|
||||
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),
|
||||
|
@ -1,9 +1,9 @@
|
||||
use super::ModuleGraph;
|
||||
use super::PlanBuilder;
|
||||
use crate::ModuleId;
|
||||
use petgraph::EdgeDirection::Incoming;
|
||||
|
||||
// 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!(
|
||||
module_ids,
|
||||
&[],
|
||||
@ -12,6 +12,7 @@ pub(super) fn least_common_ancestor(g: &ModuleGraph, module_ids: &[ModuleId]) ->
|
||||
if module_ids.len() == 1 {
|
||||
return module_ids[0];
|
||||
}
|
||||
let g = &b.direct_deps;
|
||||
|
||||
// Check for roots
|
||||
for &mid in module_ids {
|
||||
@ -28,7 +29,7 @@ pub(super) fn least_common_ancestor(g: &ModuleGraph, module_ids: &[ModuleId]) ->
|
||||
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);
|
||||
return id;
|
||||
}
|
||||
@ -43,15 +44,16 @@ pub(super) fn least_common_ancestor(g: &ModuleGraph, module_ids: &[ModuleId]) ->
|
||||
.iter()
|
||||
.skip(2)
|
||||
.cloned()
|
||||
.fold(least_common_ancestor(g, &[first, second]), |prev, item| {
|
||||
least_common_ancestor(g, &[prev, item])
|
||||
.fold(least_common_ancestor(b, &[first, second]), |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
|
||||
I: IntoIterator<Item = ModuleId>,
|
||||
{
|
||||
let g = &b.direct_deps;
|
||||
for l in li {
|
||||
// Root
|
||||
if g.neighbors_directed(l, Incoming).count() == 0 {
|
||||
@ -73,27 +75,46 @@ where
|
||||
None
|
||||
}
|
||||
|
||||
fn check_itself_and_parent(g: &ModuleGraph, li: &[ModuleId], ri: &[ModuleId]) -> Option<ModuleId> {
|
||||
if let Some(id) = check_itself(g, li.iter().copied(), ri) {
|
||||
fn check_itself_and_parent(b: &PlanBuilder, li: &[ModuleId], ri: &[ModuleId]) -> Option<ModuleId> {
|
||||
let g = &b.direct_deps;
|
||||
|
||||
if let Some(id) = check_itself(b, li.iter().copied(), ri) {
|
||||
return Some(id);
|
||||
}
|
||||
|
||||
for &l in li {
|
||||
if let Some(id) = check_itself_and_parent(
|
||||
g,
|
||||
&g.neighbors_directed(l, Incoming).collect::<Vec<_>>(),
|
||||
ri,
|
||||
) {
|
||||
let mut l_dependants = g.neighbors_directed(l, Incoming).collect::<Vec<_>>();
|
||||
for &l_dependant in &l_dependants {
|
||||
if g.neighbors_directed(l_dependant, Incoming).count() == 0 {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
for &r in ri {
|
||||
if let Some(id) = check_itself_and_parent(
|
||||
g,
|
||||
&g.neighbors_directed(r, Incoming).collect::<Vec<_>>(),
|
||||
li,
|
||||
) {
|
||||
let mut r_dependants = g.neighbors_directed(r, Incoming).collect::<Vec<_>>();
|
||||
for &r_dependant in &r_dependants {
|
||||
if g.neighbors_directed(r_dependant, Incoming).count() == 0 {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -279,7 +279,7 @@ where
|
||||
//
|
||||
// a <- b
|
||||
// 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();
|
||||
|
||||
@ -310,7 +310,7 @@ where
|
||||
{
|
||||
dependants[1]
|
||||
} else {
|
||||
least_common_ancestor(&builder.direct_deps, &dependants)
|
||||
least_common_ancestor(&builder, &dependants)
|
||||
};
|
||||
|
||||
if dependants.len() == 2 && dependants.contains(&higher_module) {
|
||||
|
@ -451,10 +451,7 @@ impl Fold for ExportRenamer<'_> {
|
||||
| Decl::TsModule(_) => ModuleItem::ModuleDecl(ModuleDecl::ExportDecl(decl)),
|
||||
|
||||
Decl::Class(mut c) => {
|
||||
c.ident = match actual.rename(c.ident, true) {
|
||||
Ok(v) => v,
|
||||
Err(v) => v,
|
||||
};
|
||||
c.ident = c.ident.fold_with(&mut actual);
|
||||
|
||||
if self.unexport {
|
||||
Stmt::Decl(Decl::Class(c)).into()
|
||||
@ -466,10 +463,7 @@ impl Fold for ExportRenamer<'_> {
|
||||
}
|
||||
}
|
||||
Decl::Fn(mut f) => {
|
||||
f.ident = match actual.rename(f.ident, true) {
|
||||
Ok(v) => v,
|
||||
Err(v) => v,
|
||||
};
|
||||
f.ident = f.ident.fold_with(&mut actual);
|
||||
if self.unexport {
|
||||
Stmt::Decl(Decl::Fn(f)).into()
|
||||
} else {
|
||||
|
@ -16,7 +16,7 @@ use url::Url;
|
||||
#[test]
|
||||
#[ignore = "Too slow"]
|
||||
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 {
|
||||
|
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