1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-12-21 23:04:45 -05:00

refactor: use deno_emit (#14737)

This commit is contained in:
David Sherret 2022-05-30 09:39:14 -04:00 committed by GitHub
parent 01e5bbadac
commit 0cfdf5cede
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 25 additions and 305 deletions

15
Cargo.lock generated
View file

@ -748,6 +748,7 @@ dependencies = [
"deno_core",
"deno_crypto",
"deno_doc",
"deno_emit",
"deno_fetch",
"deno_graph",
"deno_lint",
@ -921,6 +922,20 @@ dependencies = [
"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]]
name = "deno_fetch"
version = "0.77.0"

View file

@ -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" }

View file

@ -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,

View file

@ -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(())