summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock15
-rw-r--r--cli/Cargo.toml1
-rw-r--r--cli/emit.rs295
-rw-r--r--cli/main.rs19
4 files changed, 25 insertions, 305 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 299749c21..e104af655 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -748,6 +748,7 @@ dependencies = [
"deno_core",
"deno_crypto",
"deno_doc",
+ "deno_emit",
"deno_fetch",
"deno_graph",
"deno_lint",
@@ -922,6 +923,20 @@ dependencies = [
]
[[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]]
name = "deno_fetch"
version = "0.77.0"
dependencies = [
diff --git a/cli/Cargo.toml b/cli/Cargo.toml
index a0540a425..fc09642e9 100644
--- a/cli/Cargo.toml
+++ b/cli/Cargo.toml
@@ -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_core = { version = "0.136.0", path = "../core" }
deno_doc = "0.35.0"
+deno_emit = "0.2.0"
deno_graph = "0.27.0"
deno_lint = { version = "0.30.0", features = ["docs"] }
deno_runtime = { version = "0.62.0", path = "../runtime" }
diff --git a/cli/emit.rs b/cli/emit.rs
index 57ed556dc..a4fdedab7 100644
--- a/cli/emit.rs
+++ b/cli/emit.rs
@@ -15,28 +15,12 @@ use crate::diagnostics::Diagnostics;
use crate::flags;
use crate::graph_util::GraphData;
use crate::graph_util::ModuleEntry;
-use crate::text_encoding::strip_bom;
use crate::tsc;
use crate::version;
-use deno_ast::get_syntax;
-use deno_ast::swc;
use deno_ast::swc::bundler::Hook;
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::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::parking_lot::RwLock;
use deno_core::serde::Deserialize;
@@ -55,18 +39,10 @@ use deno_graph::ResolutionError;
use std::collections::HashMap;
use std::collections::HashSet;
use std::fmt;
-use std::rc::Rc;
use std::result;
use std::sync::Arc;
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
/// checking the code in the module graph. Note that a user provided config
/// 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 ts_config: TsConfig,
pub reload: bool,
diff --git a/cli/main.rs b/cli/main.rs
index 5fb0c1a90..b6c7e97ff 100644
--- a/cli/main.rs
+++ b/cli/main.rs
@@ -758,7 +758,7 @@ fn bundle_module_graph(
graph: &deno_graph::ModuleGraph,
ps: &ProcState,
flags: &Flags,
-) -> Result<(String, Option<String>), AnyError> {
+) -> Result<deno_emit::BundleEmit, AnyError> {
info!("{} {}", colors::green("Bundle"), graph.roots[0].0);
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,
- emit::BundleOptions {
- bundle_type: emit::BundleType::Module,
- ts_config,
+ deno_emit::BundleOptions {
+ bundle_type: deno_emit::BundleType::Module,
+ emit_options: ts_config.into(),
emit_ignore_directives: true,
},
)
@@ -836,12 +836,11 @@ async fn bundle_command(
let operation = |(ps, graph): (ProcState, Arc<deno_graph::ModuleGraph>)| {
let out_file = bundle_flags.out_file.clone();
async move {
- let (bundle_emit, maybe_bundle_map) =
- bundle_module_graph(graph.as_ref(), &ps, &ps.flags)?;
+ let bundle_output = bundle_module_graph(graph.as_ref(), &ps, &ps.flags)?;
debug!(">>>>> bundle END");
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();
fs_util::write_file(out_file, output_bytes, 0o644)?;
info!(
@@ -850,7 +849,7 @@ async fn bundle_command(
out_file,
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_len = map_bytes.len();
let ext = if let Some(curr_ext) = out_file.extension() {
@@ -868,7 +867,7 @@ async fn bundle_command(
);
}
} else {
- println!("{}", bundle_emit);
+ println!("{}", bundle_output.code);
}
Ok(())