mirror of
https://github.com/denoland/deno.git
synced 2025-01-20 20:42:19 -05:00
WIP
This commit is contained in:
parent
0871741e98
commit
ae831b111f
5 changed files with 454 additions and 363 deletions
|
@ -1,6 +1,6 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
use deno_ast::MediaType;
|
||||
use deno_ast::{MediaType, ParsedSource};
|
||||
use deno_core::url::Url;
|
||||
use deno_graph::{ExternalModule, JsonModule, WasmModule};
|
||||
|
||||
|
@ -16,11 +16,12 @@ pub struct BundleJsModule {
|
|||
pub specifier: Url,
|
||||
pub media_type: MediaType,
|
||||
pub source: String,
|
||||
pub ast: Option<ParsedSource>,
|
||||
pub dependencies: Vec<BundleDep>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum BundleMod {
|
||||
pub enum BundleModule {
|
||||
Js(BundleJsModule),
|
||||
Json(JsonModule),
|
||||
Wasm(WasmModule),
|
||||
|
@ -32,7 +33,7 @@ pub enum BundleMod {
|
|||
pub struct BundleGraph {
|
||||
id: usize,
|
||||
url_to_id: HashMap<Url, usize>,
|
||||
modules: HashMap<usize, BundleMod>,
|
||||
modules: HashMap<usize, BundleModule>,
|
||||
}
|
||||
|
||||
/// The bundle graph only contains fully resolved modules.
|
||||
|
@ -45,7 +46,29 @@ impl BundleGraph {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn insert(&mut self, specifier: Url, module: BundleMod) -> usize {
|
||||
pub fn get_specifier(&self, id: usize) -> Option<Url> {
|
||||
if let Some(module) = self.modules.get(&id) {
|
||||
match module {
|
||||
BundleModule::Js(m) => Some(m.specifier.clone()),
|
||||
BundleModule::Json(m) => Some(m.specifier.clone()),
|
||||
BundleModule::Wasm(m) => Some(m.specifier.clone()),
|
||||
BundleModule::Node(_) => None, // FIXME
|
||||
BundleModule::External(external_module) => todo!(),
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get(&self, url: &Url) -> Option<&BundleModule> {
|
||||
if let Some(id) = self.url_to_id.get(&url) {
|
||||
return self.modules.get(&id);
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
pub fn insert(&mut self, specifier: Url, module: BundleModule) -> usize {
|
||||
let id = self.register(specifier);
|
||||
self.modules.insert(id, module);
|
||||
id
|
||||
|
@ -63,7 +86,7 @@ impl BundleGraph {
|
|||
}
|
||||
|
||||
pub fn add_dependency(&mut self, id: usize, dep: BundleDep) {
|
||||
if let Some(BundleMod::Js(module)) = self.modules.get_mut(&id) {
|
||||
if let Some(BundleModule::Js(module)) = self.modules.get_mut(&id) {
|
||||
module.dependencies.push(dep)
|
||||
}
|
||||
}
|
||||
|
|
173
cli/tools/bundle/bundle_resolver.rs
Normal file
173
cli/tools/bundle/bundle_resolver.rs
Normal file
|
@ -0,0 +1,173 @@
|
|||
use std::{
|
||||
collections::{HashMap, HashSet},
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
use deno_core::{anyhow::Context, error::AnyError, url::Url};
|
||||
use deno_graph::{GraphKind, Module, ModuleGraph, NpmModule};
|
||||
use deno_runtime::deno_node::NodeResolver;
|
||||
use indexmap::IndexSet;
|
||||
use node_resolver::{NodeModuleKind, NodeResolutionMode};
|
||||
|
||||
use crate::{
|
||||
graph_util::{CreateGraphOptions, ModuleGraphCreator},
|
||||
npm::CliNpmResolver,
|
||||
tools::bundle::bundle_graph::BundleDep,
|
||||
};
|
||||
|
||||
use super::bundle_graph::{BundleGraph, BundleJsModule, BundleModule};
|
||||
|
||||
pub async fn build_resolved_graph(
|
||||
module_graph_creator: &Arc<ModuleGraphCreator>,
|
||||
npm_resolver: &Arc<dyn CliNpmResolver>,
|
||||
node_resolver: &Arc<NodeResolver>,
|
||||
files: Vec<Url>,
|
||||
) -> Result<BundleGraph, AnyError> {
|
||||
let graph = module_graph_creator
|
||||
.create_graph_with_options(CreateGraphOptions {
|
||||
graph_kind: GraphKind::CodeOnly,
|
||||
roots: files.clone(),
|
||||
is_dynamic: false,
|
||||
loader: None,
|
||||
})
|
||||
.await?;
|
||||
|
||||
graph.valid()?;
|
||||
|
||||
let mut bundle_graph = BundleGraph::new();
|
||||
|
||||
let mut npm_modules: IndexSet<Url> = IndexSet::new();
|
||||
let mut seen: HashSet<Url> = HashSet::new();
|
||||
let mut pending_npm_dep_links: HashMap<usize, Url> = HashMap::new();
|
||||
|
||||
walk_graph(
|
||||
&graph,
|
||||
&mut seen,
|
||||
npm_resolver,
|
||||
node_resolver,
|
||||
&mut bundle_graph,
|
||||
&mut npm_modules,
|
||||
&mut pending_npm_dep_links,
|
||||
);
|
||||
|
||||
// Resolve npm modules
|
||||
// Hack: Create sub graphs for every npm module we encounter and
|
||||
// expand npm specifiers to the actual file
|
||||
while let Some(url) = npm_modules.pop() {
|
||||
let npm_graph = module_graph_creator
|
||||
.create_graph_with_options(CreateGraphOptions {
|
||||
graph_kind: GraphKind::CodeOnly,
|
||||
roots: vec![url.clone()],
|
||||
is_dynamic: false,
|
||||
loader: None,
|
||||
})
|
||||
.await?;
|
||||
|
||||
walk_graph(
|
||||
&npm_graph,
|
||||
&mut seen,
|
||||
npm_resolver,
|
||||
node_resolver,
|
||||
&mut bundle_graph,
|
||||
&mut npm_modules,
|
||||
&mut pending_npm_dep_links,
|
||||
);
|
||||
}
|
||||
|
||||
Ok(bundle_graph)
|
||||
}
|
||||
|
||||
fn walk_graph(
|
||||
graph: &ModuleGraph,
|
||||
seen: &mut HashSet<Url>,
|
||||
npm_resolver: &Arc<dyn CliNpmResolver>,
|
||||
node_resolver: &Arc<NodeResolver>,
|
||||
bundle_graph: &mut BundleGraph,
|
||||
npm_modules: &mut IndexSet<Url>,
|
||||
pending_npm_dep_links: &mut HashMap<usize, Url>,
|
||||
) {
|
||||
for module in graph.modules() {
|
||||
let url = module.specifier();
|
||||
|
||||
if seen.contains(&url) {
|
||||
continue;
|
||||
}
|
||||
|
||||
seen.insert(url.clone());
|
||||
|
||||
match module {
|
||||
Module::Npm(module) => {
|
||||
let resolved = resolve_npm_module(&module, npm_resolver, node_resolver);
|
||||
npm_modules.insert(resolved.clone());
|
||||
}
|
||||
Module::Js(js_module) => {
|
||||
let id = bundle_graph.insert(
|
||||
url.clone(),
|
||||
BundleModule::Js(BundleJsModule {
|
||||
specifier: url.clone(),
|
||||
media_type: js_module.media_type,
|
||||
source: js_module.source.to_string(),
|
||||
ast: None,
|
||||
dependencies: vec![],
|
||||
}),
|
||||
);
|
||||
|
||||
for (raw, dep) in &js_module.dependencies {
|
||||
if let Some(code) = dep.get_code() {
|
||||
if code.scheme() == "npm" {
|
||||
pending_npm_dep_links.insert(id, code.clone());
|
||||
} else {
|
||||
let dep_id = bundle_graph.register(code.clone());
|
||||
bundle_graph.add_dependency(
|
||||
id,
|
||||
BundleDep {
|
||||
id: dep_id,
|
||||
raw: raw.to_string(),
|
||||
is_dyanmic: dep.is_dynamic,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Module::Json(json_module) => {
|
||||
bundle_graph
|
||||
.insert(url.clone(), BundleModule::Json(json_module.clone()));
|
||||
}
|
||||
Module::Wasm(wasm_module) => {
|
||||
bundle_graph
|
||||
.insert(url.clone(), BundleModule::Wasm(wasm_module.clone()));
|
||||
}
|
||||
Module::Node(built_in_node_module) => {
|
||||
bundle_graph.insert(
|
||||
url.clone(),
|
||||
BundleModule::Node(built_in_node_module.module_name.to_string()),
|
||||
);
|
||||
}
|
||||
Module::External(external_module) => todo!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn resolve_npm_module(
|
||||
module: &NpmModule,
|
||||
npm_resolver: &Arc<dyn CliNpmResolver>,
|
||||
node_resolver: &Arc<NodeResolver>,
|
||||
) -> Url {
|
||||
let nv = module.nv_reference.nv();
|
||||
let managed = npm_resolver.as_managed().unwrap();
|
||||
let package_folder = managed.resolve_pkg_folder_from_deno_module(nv).unwrap();
|
||||
|
||||
let resolved = node_resolver
|
||||
.resolve_package_subpath_from_deno_module(
|
||||
&package_folder,
|
||||
module.nv_reference.sub_path(),
|
||||
None, // FIXME
|
||||
NodeModuleKind::Esm, // FIXME
|
||||
NodeResolutionMode::Execution,
|
||||
)
|
||||
.with_context(|| format!("Could not resolve '{}'.", module.nv_reference))
|
||||
.unwrap();
|
||||
|
||||
resolved
|
||||
}
|
191
cli/tools/bundle/chunk_graph.rs
Normal file
191
cli/tools/bundle/chunk_graph.rs
Normal file
|
@ -0,0 +1,191 @@
|
|||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
use deno_core::url::Url;
|
||||
use indexmap::IndexSet;
|
||||
|
||||
use crate::args::{BundleFlags, BundlePlatform};
|
||||
|
||||
use super::bundle_graph::{BundleGraph, BundleModule};
|
||||
|
||||
pub fn assign_chunks(
|
||||
bundle_flags: &BundleFlags,
|
||||
chunk_graph: &mut ChunkGraph,
|
||||
graph: &BundleGraph,
|
||||
url: &Url,
|
||||
parent_chunk_id: Option<usize>,
|
||||
is_dynamic: bool,
|
||||
) {
|
||||
let module = graph.get(url).unwrap();
|
||||
|
||||
match &module {
|
||||
&BundleModule::Js(js_module) => {
|
||||
let chunk_id = chunk_graph.assign_specifier_to_chunk(
|
||||
url,
|
||||
parent_chunk_id,
|
||||
ChunkKind::Js,
|
||||
is_dynamic,
|
||||
);
|
||||
|
||||
for dep in &js_module.dependencies {
|
||||
if let Some(specifier) = graph.get_specifier(dep.id) {
|
||||
assign_chunks(
|
||||
bundle_flags,
|
||||
chunk_graph,
|
||||
graph,
|
||||
&specifier,
|
||||
Some(chunk_id),
|
||||
dep.is_dyanmic,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
BundleModule::Json(json_module) => {
|
||||
chunk_graph.assign_specifier_to_chunk(
|
||||
url,
|
||||
parent_chunk_id,
|
||||
ChunkKind::Js,
|
||||
is_dynamic,
|
||||
);
|
||||
}
|
||||
BundleModule::Wasm(wasm_module) => {
|
||||
chunk_graph.assign_specifier_to_chunk(
|
||||
url,
|
||||
parent_chunk_id,
|
||||
ChunkKind::Asset("wasm".to_string()),
|
||||
true,
|
||||
);
|
||||
}
|
||||
BundleModule::Node(built_in_node_module) => {
|
||||
if let BundlePlatform::Browser = bundle_flags.platform {
|
||||
// TODO: Show where it was imported from
|
||||
log::log!(
|
||||
log::Level::Error,
|
||||
"Imported Node internal module '{}' which will fail in browsers.",
|
||||
built_in_node_module
|
||||
);
|
||||
}
|
||||
}
|
||||
BundleModule::External(external_module) => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
pub enum ChunkKind {
|
||||
Asset(String),
|
||||
Js,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Chunk {
|
||||
pub id: usize,
|
||||
pub name: String,
|
||||
pub kind: ChunkKind,
|
||||
pub parent_ids: HashSet<usize>,
|
||||
pub children: Vec<usize>, // TODO: IndexSet?
|
||||
pub specifiers: IndexSet<Url>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ChunkGraph {
|
||||
pub id: usize,
|
||||
pub chunks: HashMap<usize, Chunk>,
|
||||
pub root_chunks: HashSet<usize>,
|
||||
pub specifier_to_chunks: HashMap<Url, Vec<usize>>,
|
||||
}
|
||||
|
||||
impl ChunkGraph {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
id: 0,
|
||||
chunks: HashMap::new(),
|
||||
root_chunks: HashSet::new(),
|
||||
specifier_to_chunks: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_or_create_chunk(
|
||||
&mut self,
|
||||
url: &Url,
|
||||
parent_chunk_id: Option<usize>,
|
||||
kind: ChunkKind,
|
||||
is_dynamic: bool,
|
||||
) -> usize {
|
||||
if let Some(parent_chunk_id) = parent_chunk_id {
|
||||
if !is_dynamic {
|
||||
return parent_chunk_id;
|
||||
}
|
||||
}
|
||||
|
||||
let name = if let Ok(f) = url.to_file_path() {
|
||||
if let Some(name) = f.file_stem() {
|
||||
name.to_string_lossy().to_string()
|
||||
} else {
|
||||
format!("chunk_{}", self.id)
|
||||
}
|
||||
} else {
|
||||
format!("chunk_{}", self.id)
|
||||
};
|
||||
|
||||
let ext = match &kind {
|
||||
ChunkKind::Asset(ext) => ext,
|
||||
ChunkKind::Js => "js",
|
||||
};
|
||||
|
||||
let full_name = format!("{}.{}", name, ext);
|
||||
|
||||
self.new_chunk(full_name, parent_chunk_id, kind)
|
||||
}
|
||||
|
||||
pub fn assign_specifier_to_chunk(
|
||||
&mut self,
|
||||
url: &Url,
|
||||
parent_chunk_id: Option<usize>,
|
||||
kind: ChunkKind,
|
||||
is_dynamic: bool,
|
||||
) -> usize {
|
||||
let chunk_id =
|
||||
self.get_or_create_chunk(&url, parent_chunk_id, kind, is_dynamic);
|
||||
|
||||
if let Some(value) = self.specifier_to_chunks.get_mut(&url) {
|
||||
value.push(chunk_id)
|
||||
} else {
|
||||
let value = vec![chunk_id];
|
||||
self.specifier_to_chunks.insert(url.clone(), value);
|
||||
}
|
||||
|
||||
if let Some(chunk) = self.chunks.get_mut(&chunk_id) {
|
||||
chunk.specifiers.insert(url.clone());
|
||||
}
|
||||
|
||||
chunk_id
|
||||
}
|
||||
|
||||
fn new_chunk(
|
||||
&mut self,
|
||||
name: String,
|
||||
parent_id: Option<usize>,
|
||||
kind: ChunkKind,
|
||||
) -> usize {
|
||||
let id = self.id;
|
||||
self.id += 1;
|
||||
|
||||
let mut parent_ids = HashSet::new();
|
||||
if let Some(parent_id) = parent_id {
|
||||
parent_ids.insert(parent_id);
|
||||
} else {
|
||||
self.root_chunks.insert(id);
|
||||
}
|
||||
|
||||
let chunk = Chunk {
|
||||
id,
|
||||
name,
|
||||
kind,
|
||||
parent_ids,
|
||||
children: vec![],
|
||||
specifiers: IndexSet::new(),
|
||||
};
|
||||
|
||||
self.chunks.insert(id, chunk);
|
||||
id
|
||||
}
|
||||
}
|
|
@ -1,31 +1,28 @@
|
|||
use std::{
|
||||
cmp::Ordering,
|
||||
collections::{HashMap, HashSet},
|
||||
fs,
|
||||
io::Write,
|
||||
path::{Path, PathBuf},
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
use bundle_graph::{BundleDep, BundleGraph, BundleJsModule, BundleMod};
|
||||
use deno_core::{anyhow::Context, error::AnyError, url::Url};
|
||||
use deno_graph::{GraphKind, Module, ModuleGraph, NpmModule, Resolution};
|
||||
use deno_runtime::{colors, deno_node::NodeResolver};
|
||||
use deno_semver::package;
|
||||
use bundle_graph::BundleModule;
|
||||
use bundle_resolver::build_resolved_graph;
|
||||
use chunk_graph::{assign_chunks, ChunkGraph};
|
||||
use deno_core::error::AnyError;
|
||||
use deno_runtime::colors;
|
||||
use flate2::{write::ZlibEncoder, Compression};
|
||||
use indexmap::IndexSet;
|
||||
use node_resolver::{NodeModuleKind, NodeResolutionMode};
|
||||
|
||||
use crate::{
|
||||
args::{BundleFlags, BundlePlatform, Flags},
|
||||
args::{BundleFlags, Flags},
|
||||
factory::CliFactory,
|
||||
graph_util::CreateGraphOptions,
|
||||
npm::CliNpmResolver,
|
||||
resolver::CjsTracker,
|
||||
util::{fs::collect_specifiers, path::matches_pattern_or_exact_path},
|
||||
};
|
||||
|
||||
mod bundle_graph;
|
||||
mod bundle_resolver;
|
||||
mod chunk_graph;
|
||||
mod transform;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct BundleChunkStat {
|
||||
|
@ -62,159 +59,20 @@ pub async fn bundle(
|
|||
|
||||
let module_graph_creator = factory.module_graph_creator().await?;
|
||||
|
||||
let graph = module_graph_creator
|
||||
.create_graph_with_options(CreateGraphOptions {
|
||||
graph_kind: GraphKind::CodeOnly,
|
||||
roots: files.clone(),
|
||||
is_dynamic: false,
|
||||
loader: None,
|
||||
})
|
||||
.await?;
|
||||
|
||||
graph.valid()?;
|
||||
|
||||
let mut bundle_graph = BundleGraph::new();
|
||||
|
||||
let mut id = 0;
|
||||
let mut all_modules: HashMap<Url, Module> = HashMap::new();
|
||||
let mut module_to_id: HashMap<Url, usize> = HashMap::new();
|
||||
let mut id_to_module: HashMap<usize, Url> = HashMap::new();
|
||||
|
||||
let mut npm_modules: IndexSet<Url> = IndexSet::new();
|
||||
let mut seen: IndexSet<Url> = IndexSet::new();
|
||||
|
||||
fn resolve_npm_module(
|
||||
module: &NpmModule,
|
||||
npm_resolver: &Arc<dyn CliNpmResolver>,
|
||||
node_resolver: &Arc<NodeResolver>,
|
||||
) -> Url {
|
||||
let nv = module.nv_reference.nv();
|
||||
let managed = npm_resolver.as_managed().unwrap();
|
||||
let package_folder =
|
||||
managed.resolve_pkg_folder_from_deno_module(nv).unwrap();
|
||||
|
||||
let resolved = node_resolver
|
||||
.resolve_package_subpath_from_deno_module(
|
||||
&package_folder,
|
||||
module.nv_reference.sub_path(),
|
||||
None, // FIXME
|
||||
NodeModuleKind::Esm, // FIXME
|
||||
NodeResolutionMode::Execution,
|
||||
)
|
||||
.with_context(|| format!("Could not resolve '{}'.", module.nv_reference))
|
||||
.unwrap();
|
||||
|
||||
resolved
|
||||
}
|
||||
|
||||
let mut pending_npm_dep_links: HashMap<usize, Url> = HashMap::new();
|
||||
|
||||
// Hack: Create sub graphs for every npm module we encounter and
|
||||
// expand npm specifiers to the actual file
|
||||
for module in graph.modules() {
|
||||
let url = module.specifier();
|
||||
seen.insert(url.clone());
|
||||
|
||||
let current_id = id;
|
||||
module_to_id.insert(url.clone(), current_id);
|
||||
id_to_module.insert(current_id, url.clone());
|
||||
|
||||
id += 1;
|
||||
|
||||
match module {
|
||||
Module::Npm(module) => {
|
||||
let resolved = resolve_npm_module(&module, npm_resolver, node_resolver);
|
||||
npm_modules.insert(resolved.clone());
|
||||
}
|
||||
Module::Js(js_module) => {
|
||||
let id = bundle_graph.insert(
|
||||
url.clone(),
|
||||
BundleMod::Js(BundleJsModule {
|
||||
specifier: url.clone(),
|
||||
media_type: js_module.media_type,
|
||||
source: js_module.source.to_string(),
|
||||
dependencies: vec![],
|
||||
}),
|
||||
);
|
||||
|
||||
for (raw, dep) in &js_module.dependencies {
|
||||
if let Some(code) = dep.get_code() {
|
||||
if code.scheme() == "npm" {
|
||||
pending_npm_dep_links.insert(id, code.clone());
|
||||
} else {
|
||||
let dep_id = bundle_graph.register(code.clone());
|
||||
bundle_graph.add_dependency(
|
||||
id,
|
||||
BundleDep {
|
||||
id: dep_id,
|
||||
raw: raw.to_string(),
|
||||
is_dyanmic: dep.is_dynamic,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
eprintln!("JS dep {} {:#?}", raw, dep);
|
||||
}
|
||||
}
|
||||
Module::Json(json_module) => {
|
||||
bundle_graph.insert(url.clone(), BundleMod::Json(json_module.clone()));
|
||||
}
|
||||
Module::Wasm(wasm_module) => {
|
||||
bundle_graph.insert(url.clone(), BundleMod::Wasm(wasm_module.clone()));
|
||||
}
|
||||
Module::Node(built_in_node_module) => {
|
||||
bundle_graph.insert(
|
||||
url.clone(),
|
||||
BundleMod::Node(built_in_node_module.module_name.to_string()),
|
||||
);
|
||||
}
|
||||
Module::External(external_module) => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
let npm_modules_vec =
|
||||
npm_modules.iter().map(|u| u.clone()).collect::<Vec<_>>();
|
||||
|
||||
eprintln!("npm vec: {:#?}", npm_modules_vec);
|
||||
while let Some(url) = npm_modules.pop() {
|
||||
let npm_graph = module_graph_creator
|
||||
.create_graph_with_options(CreateGraphOptions {
|
||||
graph_kind: GraphKind::CodeOnly,
|
||||
roots: vec![url.clone()],
|
||||
is_dynamic: false,
|
||||
loader: None,
|
||||
})
|
||||
.await?;
|
||||
|
||||
for module in npm_graph.modules() {
|
||||
if seen.contains(module.specifier()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
match module {
|
||||
Module::Npm(module) => {
|
||||
let resolved =
|
||||
resolve_npm_module(&module, npm_resolver, node_resolver);
|
||||
npm_modules.insert(resolved.clone());
|
||||
}
|
||||
_ => {
|
||||
all_modules.insert(url.clone(), module.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
eprintln!("RES {:#?}", all_modules);
|
||||
}
|
||||
let bundle_graph = build_resolved_graph(
|
||||
module_graph_creator,
|
||||
npm_resolver,
|
||||
node_resolver,
|
||||
files.clone(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
let mut chunk_graph = ChunkGraph::new();
|
||||
for file in files {
|
||||
assign_chunks(
|
||||
&bundle_flags,
|
||||
&mut chunk_graph,
|
||||
&all_modules,
|
||||
npm_resolver,
|
||||
node_resolver,
|
||||
cjs_tracker,
|
||||
&bundle_graph,
|
||||
&file,
|
||||
None,
|
||||
true,
|
||||
|
@ -238,11 +96,17 @@ pub async fn bundle(
|
|||
let mut source = String::new();
|
||||
|
||||
for spec in chunk.specifiers.iter().rev() {
|
||||
if let Some(module) = graph.get(&spec) {
|
||||
if let Some(module) = bundle_graph.get(&spec) {
|
||||
// FIXME: don't print module urls by default
|
||||
source.push_str(&format!("// {}\n", spec.to_string()));
|
||||
if let Some(contents) = &module.source() {
|
||||
source.push_str(contents);
|
||||
match module {
|
||||
BundleModule::Js(bundle_js_module) => {
|
||||
source.push_str(&bundle_js_module.source);
|
||||
}
|
||||
BundleModule::Json(json_module) => todo!(),
|
||||
BundleModule::Wasm(wasm_module) => todo!(),
|
||||
BundleModule::Node(_) => todo!(),
|
||||
BundleModule::External(external_module) => todo!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -302,199 +166,3 @@ pub async fn bundle(
|
|||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn assign_chunks(
|
||||
bundle_flags: &BundleFlags,
|
||||
chunk_graph: &mut ChunkGraph,
|
||||
graph: &HashMap<Url, Module>,
|
||||
npm_resolver: &Arc<dyn CliNpmResolver>,
|
||||
node_resolver: &Arc<NodeResolver>,
|
||||
cjs_tracker: &Arc<CjsTracker>,
|
||||
url: &Url,
|
||||
parent_chunk_id: Option<usize>,
|
||||
is_dynamic: bool,
|
||||
) {
|
||||
let module = graph.get(url).unwrap();
|
||||
|
||||
match module {
|
||||
Module::Js(js_module) => {
|
||||
let chunk_id = chunk_graph.assign_specifier_to_chunk(
|
||||
url,
|
||||
parent_chunk_id,
|
||||
ChunkKind::Js,
|
||||
is_dynamic,
|
||||
);
|
||||
|
||||
for (_, dep) in &js_module.dependencies {
|
||||
match &dep.maybe_code {
|
||||
Resolution::None => todo!(),
|
||||
Resolution::Ok(resolution_resolved) => {
|
||||
assign_chunks(
|
||||
bundle_flags,
|
||||
chunk_graph,
|
||||
graph,
|
||||
npm_resolver,
|
||||
node_resolver,
|
||||
cjs_tracker,
|
||||
&resolution_resolved.specifier,
|
||||
Some(chunk_id),
|
||||
dep.is_dynamic,
|
||||
);
|
||||
}
|
||||
Resolution::Err(resolution_error) => todo!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
Module::Json(json_module) => {
|
||||
chunk_graph.assign_specifier_to_chunk(
|
||||
url,
|
||||
parent_chunk_id,
|
||||
ChunkKind::Js,
|
||||
is_dynamic,
|
||||
);
|
||||
}
|
||||
Module::Wasm(wasm_module) => {
|
||||
chunk_graph.assign_specifier_to_chunk(
|
||||
url,
|
||||
parent_chunk_id,
|
||||
ChunkKind::Asset("wasm".to_string()),
|
||||
true,
|
||||
);
|
||||
}
|
||||
Module::Npm(_) => {
|
||||
unreachable!()
|
||||
}
|
||||
Module::Node(built_in_node_module) => {
|
||||
if let BundlePlatform::Browser = bundle_flags.platform {
|
||||
// TODO: Show where it was imported from
|
||||
log::log!(
|
||||
log::Level::Error,
|
||||
"Imported Node internal module '{}' which will fail in browsers.",
|
||||
built_in_node_module.specifier.to_string()
|
||||
);
|
||||
}
|
||||
}
|
||||
Module::External(external_module) => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
enum ChunkKind {
|
||||
Asset(String),
|
||||
Js,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Chunk {
|
||||
id: usize,
|
||||
name: String,
|
||||
pub kind: ChunkKind,
|
||||
parent_ids: HashSet<usize>,
|
||||
children: Vec<usize>, // TODO: IndexSet?
|
||||
specifiers: IndexSet<Url>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct ChunkGraph {
|
||||
pub id: usize,
|
||||
pub chunks: HashMap<usize, Chunk>,
|
||||
pub root_chunks: HashSet<usize>,
|
||||
pub specifier_to_chunks: HashMap<Url, Vec<usize>>,
|
||||
}
|
||||
|
||||
impl ChunkGraph {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
id: 0,
|
||||
chunks: HashMap::new(),
|
||||
root_chunks: HashSet::new(),
|
||||
specifier_to_chunks: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_or_create_chunk(
|
||||
&mut self,
|
||||
url: &Url,
|
||||
parent_chunk_id: Option<usize>,
|
||||
kind: ChunkKind,
|
||||
is_dynamic: bool,
|
||||
) -> usize {
|
||||
if let Some(parent_chunk_id) = parent_chunk_id {
|
||||
if !is_dynamic {
|
||||
return parent_chunk_id;
|
||||
}
|
||||
}
|
||||
|
||||
let name = if let Ok(f) = url.to_file_path() {
|
||||
if let Some(name) = f.file_stem() {
|
||||
name.to_string_lossy().to_string()
|
||||
} else {
|
||||
format!("chunk_{}", self.id)
|
||||
}
|
||||
} else {
|
||||
format!("chunk_{}", self.id)
|
||||
};
|
||||
|
||||
let ext = match &kind {
|
||||
ChunkKind::Asset(ext) => ext,
|
||||
ChunkKind::Js => "js",
|
||||
};
|
||||
|
||||
let full_name = format!("{}.{}", name, ext);
|
||||
|
||||
self.new_chunk(full_name, parent_chunk_id, kind)
|
||||
}
|
||||
|
||||
fn assign_specifier_to_chunk(
|
||||
&mut self,
|
||||
url: &Url,
|
||||
parent_chunk_id: Option<usize>,
|
||||
kind: ChunkKind,
|
||||
is_dynamic: bool,
|
||||
) -> usize {
|
||||
let chunk_id =
|
||||
self.get_or_create_chunk(&url, parent_chunk_id, kind, is_dynamic);
|
||||
|
||||
if let Some(value) = self.specifier_to_chunks.get_mut(&url) {
|
||||
value.push(chunk_id)
|
||||
} else {
|
||||
let value = vec![chunk_id];
|
||||
self.specifier_to_chunks.insert(url.clone(), value);
|
||||
}
|
||||
|
||||
if let Some(chunk) = self.chunks.get_mut(&chunk_id) {
|
||||
chunk.specifiers.insert(url.clone());
|
||||
}
|
||||
|
||||
chunk_id
|
||||
}
|
||||
|
||||
fn new_chunk(
|
||||
&mut self,
|
||||
name: String,
|
||||
parent_id: Option<usize>,
|
||||
kind: ChunkKind,
|
||||
) -> usize {
|
||||
let id = self.id;
|
||||
self.id += 1;
|
||||
|
||||
let mut parent_ids = HashSet::new();
|
||||
if let Some(parent_id) = parent_id {
|
||||
parent_ids.insert(parent_id);
|
||||
} else {
|
||||
self.root_chunks.insert(id);
|
||||
}
|
||||
|
||||
let chunk = Chunk {
|
||||
id,
|
||||
name,
|
||||
kind,
|
||||
parent_ids,
|
||||
children: vec![],
|
||||
specifiers: IndexSet::new(),
|
||||
};
|
||||
|
||||
self.chunks.insert(id, chunk);
|
||||
id
|
||||
}
|
||||
}
|
||||
|
|
36
cli/tools/bundle/transform/mod.rs
Normal file
36
cli/tools/bundle/transform/mod.rs
Normal file
|
@ -0,0 +1,36 @@
|
|||
use deno_ast::{
|
||||
parse_module, swc::visit::FoldWith, ParseParams, ParsedSource, SourceMap,
|
||||
};
|
||||
|
||||
use super::bundle_graph::{BundleJsModule, BundleModule};
|
||||
|
||||
pub fn transform_bundle(module: &BundleModule) {
|
||||
//
|
||||
|
||||
match module {
|
||||
BundleModule::Js(module) => {
|
||||
let parsed_source = parse_module(ParseParams {
|
||||
specifier: module.specifier.clone(),
|
||||
media_type: module.media_type,
|
||||
capture_tokens: false,
|
||||
maybe_syntax: None,
|
||||
scope_analysis: false,
|
||||
text: module.source.clone().into(),
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
// TODO: optional
|
||||
let source_map =
|
||||
SourceMap::single(module.specifier.clone(), module.source.clone());
|
||||
|
||||
let program = parsed_source.program();
|
||||
|
||||
// Transpile
|
||||
}
|
||||
|
||||
// FIXME
|
||||
BundleModule::Json(_json_module) => todo!(),
|
||||
BundleModule::Wasm(_wasm_module) => {}
|
||||
BundleModule::Node(_) | BundleModule::External(_) => {}
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue