mirror of
https://github.com/denoland/deno.git
synced 2024-12-22 15:24:46 -05:00
refactor: use deno_emit (#14737)
This commit is contained in:
parent
01e5bbadac
commit
0cfdf5cede
4 changed files with 25 additions and 305 deletions
15
Cargo.lock
generated
15
Cargo.lock
generated
|
@ -748,6 +748,7 @@ dependencies = [
|
||||||
"deno_core",
|
"deno_core",
|
||||||
"deno_crypto",
|
"deno_crypto",
|
||||||
"deno_doc",
|
"deno_doc",
|
||||||
|
"deno_emit",
|
||||||
"deno_fetch",
|
"deno_fetch",
|
||||||
"deno_graph",
|
"deno_graph",
|
||||||
"deno_lint",
|
"deno_lint",
|
||||||
|
@ -921,6 +922,20 @@ dependencies = [
|
||||||
"termcolor",
|
"termcolor",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "deno_emit"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7d43a724dec6898f53984acc966d4ccf24d4d4c0a568db8e4429055166e3c86d"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"base64 0.13.0",
|
||||||
|
"deno_ast",
|
||||||
|
"deno_graph",
|
||||||
|
"futures",
|
||||||
|
"parking_lot 0.11.2",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "deno_fetch"
|
name = "deno_fetch"
|
||||||
version = "0.77.0"
|
version = "0.77.0"
|
||||||
|
|
|
@ -48,6 +48,7 @@ winres = "=0.1.12"
|
||||||
deno_ast = { version = "0.15.0", features = ["bundler", "cjs", "codegen", "dep_graph", "module_specifier", "proposal", "react", "sourcemap", "transforms", "transpiling", "typescript", "view", "visit"] }
|
deno_ast = { version = "0.15.0", features = ["bundler", "cjs", "codegen", "dep_graph", "module_specifier", "proposal", "react", "sourcemap", "transforms", "transpiling", "typescript", "view", "visit"] }
|
||||||
deno_core = { version = "0.136.0", path = "../core" }
|
deno_core = { version = "0.136.0", path = "../core" }
|
||||||
deno_doc = "0.35.0"
|
deno_doc = "0.35.0"
|
||||||
|
deno_emit = "0.2.0"
|
||||||
deno_graph = "0.27.0"
|
deno_graph = "0.27.0"
|
||||||
deno_lint = { version = "0.30.0", features = ["docs"] }
|
deno_lint = { version = "0.30.0", features = ["docs"] }
|
||||||
deno_runtime = { version = "0.62.0", path = "../runtime" }
|
deno_runtime = { version = "0.62.0", path = "../runtime" }
|
||||||
|
|
295
cli/emit.rs
295
cli/emit.rs
|
@ -15,28 +15,12 @@ use crate::diagnostics::Diagnostics;
|
||||||
use crate::flags;
|
use crate::flags;
|
||||||
use crate::graph_util::GraphData;
|
use crate::graph_util::GraphData;
|
||||||
use crate::graph_util::ModuleEntry;
|
use crate::graph_util::ModuleEntry;
|
||||||
use crate::text_encoding::strip_bom;
|
|
||||||
use crate::tsc;
|
use crate::tsc;
|
||||||
use crate::version;
|
use crate::version;
|
||||||
|
|
||||||
use deno_ast::get_syntax;
|
|
||||||
use deno_ast::swc;
|
|
||||||
use deno_ast::swc::bundler::Hook;
|
use deno_ast::swc::bundler::Hook;
|
||||||
use deno_ast::swc::bundler::ModuleRecord;
|
use deno_ast::swc::bundler::ModuleRecord;
|
||||||
use deno_ast::swc::common::comments::SingleThreadedComments;
|
|
||||||
use deno_ast::swc::common::FileName;
|
|
||||||
use deno_ast::swc::common::Mark;
|
|
||||||
use deno_ast::swc::common::SourceMap;
|
|
||||||
use deno_ast::swc::common::Span;
|
use deno_ast::swc::common::Span;
|
||||||
use deno_ast::swc::common::Spanned;
|
|
||||||
use deno_ast::swc::parser::error::Error as SwcError;
|
|
||||||
use deno_ast::swc::parser::lexer::Lexer;
|
|
||||||
use deno_ast::swc::parser::StringInput;
|
|
||||||
use deno_ast::Diagnostic;
|
|
||||||
use deno_ast::LineAndColumnDisplay;
|
|
||||||
use deno_ast::SourceRangedForSpanned;
|
|
||||||
use deno_core::anyhow::anyhow;
|
|
||||||
use deno_core::anyhow::Context;
|
|
||||||
use deno_core::error::AnyError;
|
use deno_core::error::AnyError;
|
||||||
use deno_core::parking_lot::RwLock;
|
use deno_core::parking_lot::RwLock;
|
||||||
use deno_core::serde::Deserialize;
|
use deno_core::serde::Deserialize;
|
||||||
|
@ -55,18 +39,10 @@ use deno_graph::ResolutionError;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::rc::Rc;
|
|
||||||
use std::result;
|
use std::result;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
|
|
||||||
const IGNORE_DIRECTIVES: &[&str] = &[
|
|
||||||
"// deno-fmt-ignore-file",
|
|
||||||
"// deno-lint-ignore-file",
|
|
||||||
"// This code was bundled using `deno bundle` and it's not recommended to edit it manually",
|
|
||||||
""
|
|
||||||
];
|
|
||||||
|
|
||||||
/// Represents the "default" type library that should be used when type
|
/// Represents the "default" type library that should be used when type
|
||||||
/// checking the code in the module graph. Note that a user provided config
|
/// checking the code in the module graph. Note that a user provided config
|
||||||
/// of `"lib"` would override this value.
|
/// of `"lib"` would override this value.
|
||||||
|
@ -487,277 +463,6 @@ pub fn check_and_maybe_emit(
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum BundleType {
|
|
||||||
/// Return the emitted contents of the program as a single "flattened" ES
|
|
||||||
/// module.
|
|
||||||
Module,
|
|
||||||
/// Return the emitted contents of the program as a single script that
|
|
||||||
/// executes the program using an immediately invoked function execution
|
|
||||||
/// (IIFE).
|
|
||||||
Classic,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<BundleType> for swc::bundler::ModuleType {
|
|
||||||
fn from(bundle_type: BundleType) -> Self {
|
|
||||||
match bundle_type {
|
|
||||||
BundleType::Classic => Self::Iife,
|
|
||||||
BundleType::Module => Self::Es,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct BundleOptions {
|
|
||||||
pub bundle_type: BundleType,
|
|
||||||
pub ts_config: TsConfig,
|
|
||||||
pub emit_ignore_directives: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A module loader for swc which does the appropriate retrieval and transpiling
|
|
||||||
/// of modules from the graph.
|
|
||||||
struct BundleLoader<'a> {
|
|
||||||
cm: Rc<swc::common::SourceMap>,
|
|
||||||
emit_options: &'a deno_ast::EmitOptions,
|
|
||||||
graph: &'a ModuleGraph,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl swc::bundler::Load for BundleLoader<'_> {
|
|
||||||
fn load(
|
|
||||||
&self,
|
|
||||||
file_name: &swc::common::FileName,
|
|
||||||
) -> Result<swc::bundler::ModuleData, AnyError> {
|
|
||||||
match file_name {
|
|
||||||
swc::common::FileName::Url(specifier) => {
|
|
||||||
if let Some(m) = self.graph.get(specifier) {
|
|
||||||
let (fm, module) = transpile_module(
|
|
||||||
specifier,
|
|
||||||
m.maybe_source.as_ref().map(|s| s as &str).unwrap_or(""),
|
|
||||||
m.media_type,
|
|
||||||
self.emit_options,
|
|
||||||
self.cm.clone(),
|
|
||||||
)?;
|
|
||||||
Ok(swc::bundler::ModuleData {
|
|
||||||
fm,
|
|
||||||
module,
|
|
||||||
helpers: Default::default(),
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
Err(anyhow!(
|
|
||||||
"Module \"{}\" unexpectedly missing when bundling.",
|
|
||||||
specifier
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => unreachable!(
|
|
||||||
"Received a request for unsupported filename {:?}",
|
|
||||||
file_name
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Transpiles a source module into an swc SourceFile.
|
|
||||||
fn transpile_module(
|
|
||||||
specifier: &ModuleSpecifier,
|
|
||||||
source: &str,
|
|
||||||
media_type: MediaType,
|
|
||||||
options: &deno_ast::EmitOptions,
|
|
||||||
cm: Rc<swc::common::SourceMap>,
|
|
||||||
) -> Result<(Rc<swc::common::SourceFile>, swc::ast::Module), AnyError> {
|
|
||||||
let source = strip_bom(source);
|
|
||||||
let source = if media_type == MediaType::Json {
|
|
||||||
format!(
|
|
||||||
"export default JSON.parse(`{}`);",
|
|
||||||
source.replace("${", "\\${").replace('`', "\\`")
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
source.to_string()
|
|
||||||
};
|
|
||||||
let source_file =
|
|
||||||
cm.new_source_file(FileName::Url(specifier.clone()), source);
|
|
||||||
let input = StringInput::from(&*source_file);
|
|
||||||
let comments = SingleThreadedComments::default();
|
|
||||||
let syntax = if media_type == MediaType::Json {
|
|
||||||
get_syntax(MediaType::JavaScript)
|
|
||||||
} else {
|
|
||||||
get_syntax(media_type)
|
|
||||||
};
|
|
||||||
let lexer = Lexer::new(syntax, deno_ast::ES_VERSION, input, Some(&comments));
|
|
||||||
let mut parser = swc::parser::Parser::new_from(lexer);
|
|
||||||
let module = parser
|
|
||||||
.parse_module()
|
|
||||||
.map_err(|e| swc_err_to_diagnostic(&cm, specifier, e))?;
|
|
||||||
let diagnostics = parser
|
|
||||||
.take_errors()
|
|
||||||
.into_iter()
|
|
||||||
.map(|e| swc_err_to_diagnostic(&cm, specifier, e))
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
let top_level_mark = Mark::fresh(Mark::root());
|
|
||||||
let program = deno_ast::fold_program(
|
|
||||||
swc::ast::Program::Module(module),
|
|
||||||
options,
|
|
||||||
cm,
|
|
||||||
&comments,
|
|
||||||
top_level_mark,
|
|
||||||
&diagnostics,
|
|
||||||
)?;
|
|
||||||
let module = match program {
|
|
||||||
swc::ast::Program::Module(module) => module,
|
|
||||||
_ => unreachable!(),
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok((source_file, module))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn swc_err_to_diagnostic(
|
|
||||||
source_map: &SourceMap,
|
|
||||||
specifier: &ModuleSpecifier,
|
|
||||||
err: SwcError,
|
|
||||||
) -> Diagnostic {
|
|
||||||
let location = source_map.lookup_char_pos(err.span().lo);
|
|
||||||
Diagnostic {
|
|
||||||
specifier: specifier.to_string(),
|
|
||||||
range: err.range(),
|
|
||||||
display_position: LineAndColumnDisplay {
|
|
||||||
line_number: location.line,
|
|
||||||
column_number: location.col_display + 1,
|
|
||||||
},
|
|
||||||
kind: err.into_kind(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A resolver implementation for swc that resolves specifiers from the graph.
|
|
||||||
struct BundleResolver<'a>(&'a ModuleGraph);
|
|
||||||
|
|
||||||
impl swc::bundler::Resolve for BundleResolver<'_> {
|
|
||||||
fn resolve(
|
|
||||||
&self,
|
|
||||||
referrer: &swc::common::FileName,
|
|
||||||
specifier: &str,
|
|
||||||
) -> Result<swc::common::FileName, AnyError> {
|
|
||||||
let referrer = if let swc::common::FileName::Url(referrer) = referrer {
|
|
||||||
referrer
|
|
||||||
} else {
|
|
||||||
unreachable!(
|
|
||||||
"An unexpected referrer was passed when bundling: {:?}",
|
|
||||||
referrer
|
|
||||||
);
|
|
||||||
};
|
|
||||||
if let Some(specifier) =
|
|
||||||
self.0.resolve_dependency(specifier, referrer, false)
|
|
||||||
{
|
|
||||||
Ok(deno_ast::swc::common::FileName::Url(specifier.clone()))
|
|
||||||
} else {
|
|
||||||
Err(anyhow!(
|
|
||||||
"Cannot resolve \"{}\" from \"{}\".",
|
|
||||||
specifier,
|
|
||||||
referrer
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Given a module graph, generate and return a bundle of the graph and
|
|
||||||
/// optionally its source map. Unlike emitting with `check_and_maybe_emit` and
|
|
||||||
/// `emit`, which store the emitted modules in the cache, this function simply
|
|
||||||
/// returns the output.
|
|
||||||
pub fn bundle(
|
|
||||||
graph: &ModuleGraph,
|
|
||||||
options: BundleOptions,
|
|
||||||
) -> Result<(String, Option<String>), AnyError> {
|
|
||||||
let globals = swc::common::Globals::new();
|
|
||||||
deno_ast::swc::common::GLOBALS.set(&globals, || {
|
|
||||||
let emit_options: deno_ast::EmitOptions = options.ts_config.into();
|
|
||||||
let source_map_config = deno_ast::SourceMapConfig {
|
|
||||||
inline_sources: emit_options.inline_sources,
|
|
||||||
};
|
|
||||||
|
|
||||||
let cm = Rc::new(swc::common::SourceMap::new(
|
|
||||||
swc::common::FilePathMapping::empty(),
|
|
||||||
));
|
|
||||||
let loader = BundleLoader {
|
|
||||||
graph,
|
|
||||||
emit_options: &emit_options,
|
|
||||||
cm: cm.clone(),
|
|
||||||
};
|
|
||||||
let resolver = BundleResolver(graph);
|
|
||||||
let config = swc::bundler::Config {
|
|
||||||
module: options.bundle_type.into(),
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
// This hook will rewrite the `import.meta` when bundling to give a consistent
|
|
||||||
// behavior between bundled and unbundled code.
|
|
||||||
let hook = Box::new(BundleHook);
|
|
||||||
let mut bundler = swc::bundler::Bundler::new(
|
|
||||||
&globals,
|
|
||||||
cm.clone(),
|
|
||||||
loader,
|
|
||||||
resolver,
|
|
||||||
config,
|
|
||||||
hook,
|
|
||||||
);
|
|
||||||
let mut entries = HashMap::new();
|
|
||||||
entries.insert(
|
|
||||||
"bundle".to_string(),
|
|
||||||
swc::common::FileName::Url(graph.roots[0].0.clone()),
|
|
||||||
);
|
|
||||||
let output = bundler
|
|
||||||
.bundle(entries)
|
|
||||||
.context("Unable to output during bundling.")?;
|
|
||||||
let mut buf = Vec::new();
|
|
||||||
let mut srcmap = Vec::new();
|
|
||||||
{
|
|
||||||
let cfg = swc::codegen::Config {
|
|
||||||
minify: false,
|
|
||||||
ascii_only: false,
|
|
||||||
target: deno_ast::ES_VERSION,
|
|
||||||
};
|
|
||||||
let mut wr = Box::new(swc::codegen::text_writer::JsWriter::new(
|
|
||||||
cm.clone(),
|
|
||||||
"\n",
|
|
||||||
&mut buf,
|
|
||||||
Some(&mut srcmap),
|
|
||||||
));
|
|
||||||
|
|
||||||
if options.emit_ignore_directives {
|
|
||||||
// write leading comments in bundled file
|
|
||||||
use swc::codegen::text_writer::WriteJs;
|
|
||||||
let cmt = IGNORE_DIRECTIVES.join("\n") + "\n";
|
|
||||||
wr.write_comment(&cmt)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut emitter = swc::codegen::Emitter {
|
|
||||||
cfg,
|
|
||||||
cm: cm.clone(),
|
|
||||||
comments: None,
|
|
||||||
wr,
|
|
||||||
};
|
|
||||||
emitter
|
|
||||||
.emit_module(&output[0].module)
|
|
||||||
.context("Unable to emit during bundling.")?;
|
|
||||||
}
|
|
||||||
let mut code =
|
|
||||||
String::from_utf8(buf).context("Emitted code is an invalid string.")?;
|
|
||||||
let mut maybe_map: Option<String> = None;
|
|
||||||
{
|
|
||||||
let mut buf = Vec::new();
|
|
||||||
cm.build_source_map_with_config(&mut srcmap, None, source_map_config)
|
|
||||||
.to_writer(&mut buf)?;
|
|
||||||
if emit_options.inline_source_map {
|
|
||||||
let encoded_map = format!(
|
|
||||||
"//# sourceMappingURL=data:application/json;base64,{}\n",
|
|
||||||
base64::encode(buf)
|
|
||||||
);
|
|
||||||
code.push_str(&encoded_map);
|
|
||||||
} else if emit_options.source_map {
|
|
||||||
maybe_map = Some(String::from_utf8(buf)?);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok((code, maybe_map))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct EmitOptions {
|
pub struct EmitOptions {
|
||||||
pub ts_config: TsConfig,
|
pub ts_config: TsConfig,
|
||||||
pub reload: bool,
|
pub reload: bool,
|
||||||
|
|
19
cli/main.rs
19
cli/main.rs
|
@ -758,7 +758,7 @@ fn bundle_module_graph(
|
||||||
graph: &deno_graph::ModuleGraph,
|
graph: &deno_graph::ModuleGraph,
|
||||||
ps: &ProcState,
|
ps: &ProcState,
|
||||||
flags: &Flags,
|
flags: &Flags,
|
||||||
) -> Result<(String, Option<String>), AnyError> {
|
) -> Result<deno_emit::BundleEmit, AnyError> {
|
||||||
info!("{} {}", colors::green("Bundle"), graph.roots[0].0);
|
info!("{} {}", colors::green("Bundle"), graph.roots[0].0);
|
||||||
|
|
||||||
let (ts_config, maybe_ignored_options) = emit::get_ts_config(
|
let (ts_config, maybe_ignored_options) = emit::get_ts_config(
|
||||||
|
@ -772,11 +772,11 @@ fn bundle_module_graph(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
emit::bundle(
|
deno_emit::bundle_graph(
|
||||||
graph,
|
graph,
|
||||||
emit::BundleOptions {
|
deno_emit::BundleOptions {
|
||||||
bundle_type: emit::BundleType::Module,
|
bundle_type: deno_emit::BundleType::Module,
|
||||||
ts_config,
|
emit_options: ts_config.into(),
|
||||||
emit_ignore_directives: true,
|
emit_ignore_directives: true,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
@ -836,12 +836,11 @@ async fn bundle_command(
|
||||||
let operation = |(ps, graph): (ProcState, Arc<deno_graph::ModuleGraph>)| {
|
let operation = |(ps, graph): (ProcState, Arc<deno_graph::ModuleGraph>)| {
|
||||||
let out_file = bundle_flags.out_file.clone();
|
let out_file = bundle_flags.out_file.clone();
|
||||||
async move {
|
async move {
|
||||||
let (bundle_emit, maybe_bundle_map) =
|
let bundle_output = bundle_module_graph(graph.as_ref(), &ps, &ps.flags)?;
|
||||||
bundle_module_graph(graph.as_ref(), &ps, &ps.flags)?;
|
|
||||||
debug!(">>>>> bundle END");
|
debug!(">>>>> bundle END");
|
||||||
|
|
||||||
if let Some(out_file) = out_file.as_ref() {
|
if let Some(out_file) = out_file.as_ref() {
|
||||||
let output_bytes = bundle_emit.as_bytes();
|
let output_bytes = bundle_output.code.as_bytes();
|
||||||
let output_len = output_bytes.len();
|
let output_len = output_bytes.len();
|
||||||
fs_util::write_file(out_file, output_bytes, 0o644)?;
|
fs_util::write_file(out_file, output_bytes, 0o644)?;
|
||||||
info!(
|
info!(
|
||||||
|
@ -850,7 +849,7 @@ async fn bundle_command(
|
||||||
out_file,
|
out_file,
|
||||||
colors::gray(display::human_size(output_len as f64))
|
colors::gray(display::human_size(output_len as f64))
|
||||||
);
|
);
|
||||||
if let Some(bundle_map) = maybe_bundle_map {
|
if let Some(bundle_map) = bundle_output.maybe_map {
|
||||||
let map_bytes = bundle_map.as_bytes();
|
let map_bytes = bundle_map.as_bytes();
|
||||||
let map_len = map_bytes.len();
|
let map_len = map_bytes.len();
|
||||||
let ext = if let Some(curr_ext) = out_file.extension() {
|
let ext = if let Some(curr_ext) = out_file.extension() {
|
||||||
|
@ -868,7 +867,7 @@ async fn bundle_command(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
println!("{}", bundle_emit);
|
println!("{}", bundle_output.code);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
Loading…
Reference in a new issue