mirror of
https://github.com/denoland/deno.git
synced 2025-01-21 04:52:26 -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 std::collections::HashMap;
|
||||||
|
|
||||||
use deno_ast::MediaType;
|
use deno_ast::{MediaType, ParsedSource};
|
||||||
use deno_core::url::Url;
|
use deno_core::url::Url;
|
||||||
use deno_graph::{ExternalModule, JsonModule, WasmModule};
|
use deno_graph::{ExternalModule, JsonModule, WasmModule};
|
||||||
|
|
||||||
|
@ -16,11 +16,12 @@ pub struct BundleJsModule {
|
||||||
pub specifier: Url,
|
pub specifier: Url,
|
||||||
pub media_type: MediaType,
|
pub media_type: MediaType,
|
||||||
pub source: String,
|
pub source: String,
|
||||||
|
pub ast: Option<ParsedSource>,
|
||||||
pub dependencies: Vec<BundleDep>,
|
pub dependencies: Vec<BundleDep>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum BundleMod {
|
pub enum BundleModule {
|
||||||
Js(BundleJsModule),
|
Js(BundleJsModule),
|
||||||
Json(JsonModule),
|
Json(JsonModule),
|
||||||
Wasm(WasmModule),
|
Wasm(WasmModule),
|
||||||
|
@ -32,7 +33,7 @@ pub enum BundleMod {
|
||||||
pub struct BundleGraph {
|
pub struct BundleGraph {
|
||||||
id: usize,
|
id: usize,
|
||||||
url_to_id: HashMap<Url, usize>,
|
url_to_id: HashMap<Url, usize>,
|
||||||
modules: HashMap<usize, BundleMod>,
|
modules: HashMap<usize, BundleModule>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The bundle graph only contains fully resolved modules.
|
/// 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);
|
let id = self.register(specifier);
|
||||||
self.modules.insert(id, module);
|
self.modules.insert(id, module);
|
||||||
id
|
id
|
||||||
|
@ -63,7 +86,7 @@ impl BundleGraph {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_dependency(&mut self, id: usize, dep: BundleDep) {
|
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)
|
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::{
|
use std::{
|
||||||
cmp::Ordering,
|
cmp::Ordering,
|
||||||
collections::{HashMap, HashSet},
|
|
||||||
fs,
|
fs,
|
||||||
io::Write,
|
io::Write,
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
};
|
};
|
||||||
|
|
||||||
use bundle_graph::{BundleDep, BundleGraph, BundleJsModule, BundleMod};
|
use bundle_graph::BundleModule;
|
||||||
use deno_core::{anyhow::Context, error::AnyError, url::Url};
|
use bundle_resolver::build_resolved_graph;
|
||||||
use deno_graph::{GraphKind, Module, ModuleGraph, NpmModule, Resolution};
|
use chunk_graph::{assign_chunks, ChunkGraph};
|
||||||
use deno_runtime::{colors, deno_node::NodeResolver};
|
use deno_core::error::AnyError;
|
||||||
use deno_semver::package;
|
use deno_runtime::colors;
|
||||||
use flate2::{write::ZlibEncoder, Compression};
|
use flate2::{write::ZlibEncoder, Compression};
|
||||||
use indexmap::IndexSet;
|
|
||||||
use node_resolver::{NodeModuleKind, NodeResolutionMode};
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
args::{BundleFlags, BundlePlatform, Flags},
|
args::{BundleFlags, Flags},
|
||||||
factory::CliFactory,
|
factory::CliFactory,
|
||||||
graph_util::CreateGraphOptions,
|
|
||||||
npm::CliNpmResolver,
|
|
||||||
resolver::CjsTracker,
|
|
||||||
util::{fs::collect_specifiers, path::matches_pattern_or_exact_path},
|
util::{fs::collect_specifiers, path::matches_pattern_or_exact_path},
|
||||||
};
|
};
|
||||||
|
|
||||||
mod bundle_graph;
|
mod bundle_graph;
|
||||||
|
mod bundle_resolver;
|
||||||
|
mod chunk_graph;
|
||||||
|
mod transform;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct BundleChunkStat {
|
struct BundleChunkStat {
|
||||||
|
@ -62,159 +59,20 @@ pub async fn bundle(
|
||||||
|
|
||||||
let module_graph_creator = factory.module_graph_creator().await?;
|
let module_graph_creator = factory.module_graph_creator().await?;
|
||||||
|
|
||||||
let graph = module_graph_creator
|
let bundle_graph = build_resolved_graph(
|
||||||
.create_graph_with_options(CreateGraphOptions {
|
module_graph_creator,
|
||||||
graph_kind: GraphKind::CodeOnly,
|
npm_resolver,
|
||||||
roots: files.clone(),
|
node_resolver,
|
||||||
is_dynamic: false,
|
files.clone(),
|
||||||
loader: None,
|
)
|
||||||
})
|
.await?;
|
||||||
.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 mut chunk_graph = ChunkGraph::new();
|
let mut chunk_graph = ChunkGraph::new();
|
||||||
for file in files {
|
for file in files {
|
||||||
assign_chunks(
|
assign_chunks(
|
||||||
&bundle_flags,
|
&bundle_flags,
|
||||||
&mut chunk_graph,
|
&mut chunk_graph,
|
||||||
&all_modules,
|
&bundle_graph,
|
||||||
npm_resolver,
|
|
||||||
node_resolver,
|
|
||||||
cjs_tracker,
|
|
||||||
&file,
|
&file,
|
||||||
None,
|
None,
|
||||||
true,
|
true,
|
||||||
|
@ -238,11 +96,17 @@ pub async fn bundle(
|
||||||
let mut source = String::new();
|
let mut source = String::new();
|
||||||
|
|
||||||
for spec in chunk.specifiers.iter().rev() {
|
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
|
// FIXME: don't print module urls by default
|
||||||
source.push_str(&format!("// {}\n", spec.to_string()));
|
source.push_str(&format!("// {}\n", spec.to_string()));
|
||||||
if let Some(contents) = &module.source() {
|
match module {
|
||||||
source.push_str(contents);
|
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(())
|
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