mirror of
https://github.com/swc-project/swc.git
synced 2024-12-23 13:51:19 +03:00
refactor(swc_bundler): Extract logic for analyzing cycles (#2733)
This commit is contained in:
parent
3294a35417
commit
b869c81888
4
.github/workflows/compilation.yml
vendored
4
.github/workflows/compilation.yml
vendored
@ -48,6 +48,10 @@ jobs:
|
||||
run: |
|
||||
(cd crates/swc_ecma_ast && cargo hack check --feature-powerset --no-dev-deps)
|
||||
|
||||
- name: Check swc_ecma_loader
|
||||
run: |
|
||||
(cd crates/swc_ecma_loader && cargo hack check --feature-powerset --no-dev-deps)
|
||||
|
||||
- name: Check swc_ecma_transforms
|
||||
run: |
|
||||
(cd crates/swc_ecma_transforms && cargo hack check --feature-powerset --no-dev-deps)
|
||||
|
39
Cargo.lock
generated
39
Cargo.lock
generated
@ -181,6 +181,18 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "auto_impl"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7862e21c893d65a1650125d157eaeec691439379a1cee17ee49031b79236ada4"
|
||||
dependencies = [
|
||||
"proc-macro-error",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "0.1.7"
|
||||
@ -2477,7 +2489,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "swc_bundler"
|
||||
version = "0.81.0"
|
||||
version = "0.81.1"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"anyhow",
|
||||
@ -2510,6 +2522,8 @@ dependencies = [
|
||||
"swc_ecma_transforms_typescript",
|
||||
"swc_ecma_utils",
|
||||
"swc_ecma_visit",
|
||||
"swc_fast_graph",
|
||||
"swc_graph_analyzer",
|
||||
"swc_node_base",
|
||||
"tempfile",
|
||||
"testing",
|
||||
@ -2577,7 +2591,7 @@ dependencies = [
|
||||
name = "swc_css_codegen"
|
||||
version = "0.32.0"
|
||||
dependencies = [
|
||||
"auto_impl",
|
||||
"auto_impl 0.4.1",
|
||||
"bitflags",
|
||||
"swc_atoms 0.2.9",
|
||||
"swc_common",
|
||||
@ -3120,6 +3134,27 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "swc_fast_graph"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"indexmap",
|
||||
"petgraph",
|
||||
"swc_common",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "swc_graph_analyzer"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"auto_impl 0.5.0",
|
||||
"petgraph",
|
||||
"swc_fast_graph",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "swc_macros_common"
|
||||
version = "0.3.3"
|
||||
|
@ -9,7 +9,7 @@ include = ["Cargo.toml", "build.rs", "src/**/*.rs", "src/**/*.js"]
|
||||
license = "Apache-2.0/MIT"
|
||||
name = "swc_bundler"
|
||||
repository = "https://github.com/swc-project/swc.git"
|
||||
version = "0.81.0"
|
||||
version = "0.81.1"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
[features]
|
||||
@ -46,6 +46,8 @@ swc_ecma_transforms_base = {version = "0.44.0", path = "../swc_ecma_transforms_b
|
||||
swc_ecma_transforms_optimization = {version = "0.64.0", path = "../swc_ecma_transforms_optimization"}
|
||||
swc_ecma_utils = {version = "0.52.0", path = "../swc_ecma_utils"}
|
||||
swc_ecma_visit = {version = "0.44.0", path = "../swc_ecma_visit"}
|
||||
swc_fast_graph = {version = "0.1", path = "../swc_fast_graph/"}
|
||||
swc_graph_analyzer = {version = "0.1", path = "../swc_graph_analyzer/"}
|
||||
tracing = "0.1.28"
|
||||
|
||||
[dev-dependencies]
|
||||
|
@ -1,22 +1,17 @@
|
||||
use crate::{
|
||||
bundler::load::TransformedModule, dep_graph::ModuleGraph, BundleKind, Bundler, Load, ModuleId,
|
||||
Resolve,
|
||||
bundler::{load::TransformedModule, scope::Scope},
|
||||
dep_graph::ModuleGraph,
|
||||
BundleKind, Bundler, Load, ModuleId, Resolve,
|
||||
};
|
||||
use anyhow::{bail, Error};
|
||||
use swc_common::collections::{AHashMap, AHashSet};
|
||||
use swc_common::collections::AHashMap;
|
||||
use swc_graph_analyzer::{DepGraph, GraphAnalyzer};
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
struct PlanBuilder {
|
||||
/// `(src, dst)`
|
||||
tracked: AHashSet<(ModuleId, ModuleId)>,
|
||||
|
||||
graph: ModuleGraph,
|
||||
cycles: Vec<Vec<ModuleId>>,
|
||||
all: Vec<ModuleId>,
|
||||
|
||||
kinds: AHashMap<ModuleId, BundleKind>,
|
||||
}
|
||||
|
||||
@ -28,6 +23,21 @@ pub(super) struct Plan {
|
||||
pub all: Vec<ModuleId>,
|
||||
}
|
||||
|
||||
impl DepGraph for Scope {
|
||||
type ModuleId = ModuleId;
|
||||
|
||||
fn deps_of(&self, module_id: Self::ModuleId) -> Vec<Self::ModuleId> {
|
||||
let m = self.get_module(module_id).expect("failed to get module");
|
||||
|
||||
m.imports
|
||||
.specifiers
|
||||
.iter()
|
||||
.chain(m.exports.reexports.iter())
|
||||
.map(|v| v.0.module_id)
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
impl<L, R> Bundler<'_, L, R>
|
||||
where
|
||||
L: Load,
|
||||
@ -38,6 +48,7 @@ where
|
||||
entries: AHashMap<String, TransformedModule>,
|
||||
) -> Result<(Plan, ModuleGraph, Vec<Vec<ModuleId>>), Error> {
|
||||
let mut builder = PlanBuilder::default();
|
||||
let mut analyzer = GraphAnalyzer::new(&self.scope);
|
||||
|
||||
for (name, module) in entries {
|
||||
match builder.kinds.insert(module.id, BundleKind::Named { name }) {
|
||||
@ -45,77 +56,19 @@ where
|
||||
None => {}
|
||||
}
|
||||
|
||||
self.add_to_graph(&mut builder, module.id, &mut vec![module.id]);
|
||||
analyzer.load(module.id);
|
||||
}
|
||||
let res = analyzer.into_result();
|
||||
|
||||
// dbg!(&builder.cycles);
|
||||
|
||||
Ok((
|
||||
Plan {
|
||||
entries: builder.kinds,
|
||||
all: builder.all,
|
||||
all: res.all,
|
||||
},
|
||||
builder.graph,
|
||||
builder.cycles,
|
||||
res.graph,
|
||||
res.cycles,
|
||||
))
|
||||
}
|
||||
|
||||
fn add_to_graph(
|
||||
&self,
|
||||
builder: &mut PlanBuilder,
|
||||
module_id: ModuleId,
|
||||
path: &mut Vec<ModuleId>,
|
||||
) {
|
||||
if cfg!(test) {
|
||||
tracing::debug!("Adding {:?} to the graph (path = {:?})", module_id, path);
|
||||
}
|
||||
let visited = builder.all.contains(&module_id);
|
||||
// dbg!(visited);
|
||||
// dbg!(&path);
|
||||
let cycle_rpos = if visited {
|
||||
path.iter().rposition(|v| *v == module_id)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
if let Some(rpos) = cycle_rpos {
|
||||
let cycle = path[rpos..].to_vec();
|
||||
tracing::debug!("Found cycle: {:?}", cycle);
|
||||
builder.cycles.push(cycle);
|
||||
}
|
||||
|
||||
let prev_last = *path.last().unwrap();
|
||||
// Prevent infinite recursion.
|
||||
if !builder.tracked.insert((prev_last, module_id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
path.push(module_id);
|
||||
|
||||
if !visited {
|
||||
builder.all.push(module_id);
|
||||
}
|
||||
builder.graph.add_node(module_id);
|
||||
|
||||
let m = self
|
||||
.scope
|
||||
.get_module(module_id)
|
||||
.expect("failed to get module");
|
||||
|
||||
for (src, _) in m
|
||||
.imports
|
||||
.specifiers
|
||||
.iter()
|
||||
.chain(m.exports.reexports.iter())
|
||||
{
|
||||
tracing::debug!("Dep: {} -> {}", module_id, src.module_id);
|
||||
|
||||
builder.graph.add_edge(module_id, src.module_id, ());
|
||||
|
||||
self.add_to_graph(builder, src.module_id, path);
|
||||
}
|
||||
|
||||
let res = path.pop();
|
||||
debug_assert_eq!(res, Some(module_id));
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
use crate::{util::fast_graph::FastDiGraphMap, ModuleId};
|
||||
use crate::ModuleId;
|
||||
use swc_fast_graph::digraph::FastDiGraphMap;
|
||||
|
||||
pub(crate) type ModuleGraph = FastDiGraphMap<ModuleId, ()>;
|
||||
|
@ -1,10 +1,10 @@
|
||||
use crate::util::fast_graph::FastDiGraphMap;
|
||||
use petgraph::{
|
||||
EdgeDirection,
|
||||
EdgeDirection::{Incoming, Outgoing},
|
||||
};
|
||||
use std::{collections::VecDeque, iter::repeat};
|
||||
use swc_common::collections::AHashSet;
|
||||
use swc_fast_graph::digraph::FastDiGraphMap;
|
||||
|
||||
/// Is dependency between nodes hard?
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
|
@ -6,8 +6,6 @@ use swc_ecma_ast::*;
|
||||
use swc_ecma_utils::ident::IdentLike;
|
||||
use swc_ecma_visit::{noop_visit_mut_type, VisitMut};
|
||||
|
||||
pub(crate) mod fast_graph;
|
||||
|
||||
#[cfg(feature = "concurrent")]
|
||||
pub(crate) type Readonly<T> = std::sync::Arc<T>;
|
||||
|
||||
|
17
crates/swc_fast_graph/Cargo.toml
Normal file
17
crates/swc_fast_graph/Cargo.toml
Normal file
@ -0,0 +1,17 @@
|
||||
[package]
|
||||
authors = ["강동윤 <kdy1997.dev@gmail.com>"]
|
||||
description = "Faster version of petgraph"
|
||||
edition = "2018"
|
||||
include = ["Cargo.toml", "src/**/*.rs"]
|
||||
license = "Apache-2.0"
|
||||
name = "swc_fast_graph"
|
||||
repository = "https://github.com/swc-project/swc.git"
|
||||
version = "0.1.0"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
ahash = "0.7.6"
|
||||
indexmap = "1.7.0"
|
||||
petgraph = "0.5"
|
||||
swc_common = {version = "0.14.6", path = "../swc_common"}
|
1
crates/swc_fast_graph/src/lib.rs
Normal file
1
crates/swc_fast_graph/src/lib.rs
Normal file
@ -0,0 +1 @@
|
||||
pub mod digraph;
|
17
crates/swc_graph_analyzer/Cargo.toml
Normal file
17
crates/swc_graph_analyzer/Cargo.toml
Normal file
@ -0,0 +1,17 @@
|
||||
[package]
|
||||
authors = ["강동윤 <kdy1997.dev@gmail.com>"]
|
||||
description = "Graph analyzer"
|
||||
edition = "2018"
|
||||
include = ["Cargo.toml", "src/**/*.rs"]
|
||||
license = "Apache-2.0"
|
||||
name = "swc_graph_analyzer"
|
||||
repository = "https://github.com/swc-project/swc.git"
|
||||
version = "0.1.0"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
ahash = "0.7.6"
|
||||
auto_impl = "0.5.0"
|
||||
petgraph = "0.5.0"
|
||||
swc_fast_graph = {version = "0.1", path = "../swc_fast_graph/"}
|
||||
tracing = "0.1.29"
|
99
crates/swc_graph_analyzer/src/lib.rs
Normal file
99
crates/swc_graph_analyzer/src/lib.rs
Normal file
@ -0,0 +1,99 @@
|
||||
use ahash::AHashSet;
|
||||
use auto_impl::auto_impl;
|
||||
use std::{fmt::Debug, hash::Hash, marker::PhantomData};
|
||||
use swc_fast_graph::digraph::FastDiGraphMap;
|
||||
|
||||
#[auto_impl(&, Box, Rc, Arc)]
|
||||
pub trait DepGraph {
|
||||
type ModuleId: Debug + Copy + Eq + Hash + Ord;
|
||||
|
||||
fn deps_of(&self, module_id: Self::ModuleId) -> Vec<Self::ModuleId>;
|
||||
}
|
||||
|
||||
pub struct GraphAnalyzer<G>
|
||||
where
|
||||
G: DepGraph,
|
||||
{
|
||||
/// `(src, dst)`
|
||||
tracked: AHashSet<(G::ModuleId, G::ModuleId)>,
|
||||
dep_graph: G,
|
||||
data: GraphResult<G>,
|
||||
}
|
||||
|
||||
impl<G> GraphAnalyzer<G>
|
||||
where
|
||||
G: DepGraph,
|
||||
{
|
||||
pub fn new(dep_graph: G) -> Self {
|
||||
Self {
|
||||
dep_graph,
|
||||
data: GraphResult {
|
||||
all: Default::default(),
|
||||
graph: Default::default(),
|
||||
cycles: Default::default(),
|
||||
_marker: Default::default(),
|
||||
},
|
||||
tracked: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn load(&mut self, entry: G::ModuleId) {
|
||||
self.add_to_graph(entry, &mut vec![entry])
|
||||
}
|
||||
|
||||
fn add_to_graph(&mut self, module_id: G::ModuleId, path: &mut Vec<G::ModuleId>) {
|
||||
let visited = self.data.all.contains(&module_id);
|
||||
// dbg!(visited);
|
||||
// dbg!(&path);
|
||||
let cycle_rpos = if visited {
|
||||
path.iter().rposition(|v| *v == module_id)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
if let Some(rpos) = cycle_rpos {
|
||||
let cycle = path[rpos..].to_vec();
|
||||
tracing::debug!("Found cycle: {:?}", cycle);
|
||||
self.data.cycles.push(cycle);
|
||||
}
|
||||
|
||||
let prev_last = *path.last().unwrap();
|
||||
// Prevent infinite recursion.
|
||||
if !self.tracked.insert((prev_last, module_id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
path.push(module_id);
|
||||
|
||||
if !visited {
|
||||
self.data.all.push(module_id);
|
||||
}
|
||||
self.data.graph.add_node(module_id);
|
||||
|
||||
for dep_module_id in self.dep_graph.deps_of(module_id) {
|
||||
tracing::debug!("Dep: {:?} -> {:?}", module_id, dep_module_id);
|
||||
|
||||
self.data.graph.add_edge(module_id, dep_module_id, ());
|
||||
|
||||
self.add_to_graph(dep_module_id, path);
|
||||
}
|
||||
|
||||
let res = path.pop();
|
||||
debug_assert_eq!(res, Some(module_id));
|
||||
}
|
||||
|
||||
pub fn into_result(self) -> GraphResult<G> {
|
||||
self.data
|
||||
}
|
||||
}
|
||||
|
||||
pub struct GraphResult<G>
|
||||
where
|
||||
G: DepGraph,
|
||||
{
|
||||
pub all: Vec<G::ModuleId>,
|
||||
pub graph: FastDiGraphMap<G::ModuleId, ()>,
|
||||
pub cycles: Vec<Vec<G::ModuleId>>,
|
||||
|
||||
_marker: PhantomData<G>,
|
||||
}
|
Loading…
Reference in New Issue
Block a user