diff options
author | David Sherret <dsherret@users.noreply.github.com> | 2022-01-13 11:58:00 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-01-13 11:58:00 -0500 |
commit | f12164646ba8327e9e6275b0ab97602f85e9854f (patch) | |
tree | 1668ac008ac455d38d9eacd70dec4ed7f1d542c2 /cli/emit.rs | |
parent | 5e2d7737f5542c02572aa6585c8a6a78c84ae5ab (diff) |
refactor: move transpiling to deno_ast (#13332)
Diffstat (limited to 'cli/emit.rs')
-rw-r--r-- | cli/emit.rs | 180 |
1 files changed, 170 insertions, 10 deletions
diff --git a/cli/emit.rs b/cli/emit.rs index 35d6f1110..e21013b61 100644 --- a/cli/emit.rs +++ b/cli/emit.rs @@ -4,10 +4,10 @@ //! populate a cache, emit files, and transform a graph into the structures for //! loading into an isolate. -use crate::ast; use crate::cache::CacheType; use crate::cache::Cacher; use crate::colors; +use crate::config_file; use crate::config_file::ConfigFile; use crate::config_file::IgnoredCompilerOptions; use crate::config_file::TsConfig; @@ -15,10 +15,25 @@ 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_core::anyhow::anyhow; use deno_core::anyhow::Context; use deno_core::error::AnyError; @@ -27,6 +42,7 @@ use deno_core::serde::Deserialize; use deno_core::serde::Deserializer; use deno_core::serde::Serialize; use deno_core::serde::Serializer; +use deno_core::serde_json; use deno_core::serde_json::json; use deno_core::serde_json::Value; use deno_core::ModuleSpecifier; @@ -517,7 +533,7 @@ pub(crate) struct BundleOptions { /// of modules from the graph. struct BundleLoader<'a> { cm: Rc<swc::common::SourceMap>, - emit_options: &'a ast::EmitOptions, + emit_options: &'a deno_ast::EmitOptions, graph: &'a ModuleGraph, } @@ -529,7 +545,7 @@ impl swc::bundler::Load for BundleLoader<'_> { match file_name { swc::common::FileName::Url(specifier) => { if let Some(m) = self.graph.get(specifier) { - let (fm, module) = ast::transpile_module( + let (fm, module) = transpile_module( specifier, m.maybe_source().unwrap_or(""), *m.media_type(), @@ -556,6 +572,77 @@ impl swc::bundler::Load for BundleLoader<'_> { } } +/// 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(), + span: err.span(), + 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); @@ -597,8 +684,8 @@ pub(crate) fn bundle( ) -> Result<(String, Option<String>), AnyError> { let globals = swc::common::Globals::new(); deno_ast::swc::common::GLOBALS.set(&globals, || { - let emit_options: ast::EmitOptions = options.ts_config.into(); - let source_map_config = ast::SourceMapConfig { + let emit_options: deno_ast::EmitOptions = options.ts_config.into(); + let source_map_config = deno_ast::SourceMapConfig { inline_sources: emit_options.inline_sources, }; @@ -617,7 +704,7 @@ pub(crate) fn bundle( }; // This hook will rewrite the `import.meta` when bundling to give a consistent // behavior between bundled and unbundled code. - let hook = Box::new(ast::BundleHook); + let hook = Box::new(BundleHook); let mut bundler = swc::bundler::Bundler::new( &globals, cm.clone(), @@ -722,11 +809,10 @@ pub(crate) fn emit( if is_valid && !needs_reload { continue; } - let (emit, maybe_map) = - ast::transpile(&module.parsed_source, &emit_options)?; + let transpiled_source = module.parsed_source.transpile(&emit_options)?; emit_count += 1; - cache.set(CacheType::Emit, &module.specifier, emit)?; - if let Some(map) = maybe_map { + cache.set(CacheType::Emit, &module.specifier, transpiled_source.text)?; + if let Some(map) = transpiled_source.source_map { cache.set(CacheType::SourceMap, &module.specifier, map)?; } if !is_valid { @@ -861,6 +947,80 @@ pub(crate) fn to_file_map( files } +/// This contains the logic for Deno to rewrite the `import.meta` when bundling. +pub struct BundleHook; + +impl Hook for BundleHook { + fn get_import_meta_props( + &self, + span: Span, + module_record: &ModuleRecord, + ) -> Result<Vec<deno_ast::swc::ast::KeyValueProp>, AnyError> { + use deno_ast::swc::ast; + + Ok(vec![ + ast::KeyValueProp { + key: ast::PropName::Ident(ast::Ident::new("url".into(), span)), + value: Box::new(ast::Expr::Lit(ast::Lit::Str(ast::Str { + span, + value: module_record.file_name.to_string().into(), + kind: ast::StrKind::Synthesized, + has_escape: false, + }))), + }, + ast::KeyValueProp { + key: ast::PropName::Ident(ast::Ident::new("main".into(), span)), + value: Box::new(if module_record.is_entry { + ast::Expr::Member(ast::MemberExpr { + span, + obj: Box::new(ast::Expr::MetaProp(ast::MetaPropExpr { + span, + kind: ast::MetaPropKind::ImportMeta, + })), + prop: ast::MemberProp::Ident(ast::Ident::new("main".into(), span)), + }) + } else { + ast::Expr::Lit(ast::Lit::Bool(ast::Bool { span, value: false })) + }), + }, + ]) + } +} + +impl From<config_file::TsConfig> for deno_ast::EmitOptions { + fn from(config: config_file::TsConfig) -> Self { + let options: config_file::EmitConfigOptions = + serde_json::from_value(config.0).unwrap(); + let imports_not_used_as_values = + match options.imports_not_used_as_values.as_str() { + "preserve" => deno_ast::ImportsNotUsedAsValues::Preserve, + "error" => deno_ast::ImportsNotUsedAsValues::Error, + _ => deno_ast::ImportsNotUsedAsValues::Remove, + }; + let (transform_jsx, jsx_automatic, jsx_development) = + match options.jsx.as_str() { + "react" => (true, false, false), + "react-jsx" => (true, true, false), + "react-jsxdev" => (true, true, true), + _ => (false, false, false), + }; + deno_ast::EmitOptions { + emit_metadata: options.emit_decorator_metadata, + imports_not_used_as_values, + inline_source_map: options.inline_source_map, + inline_sources: options.inline_sources, + source_map: options.source_map, + jsx_automatic, + jsx_development, + jsx_factory: options.jsx_factory, + jsx_fragment_factory: options.jsx_fragment_factory, + jsx_import_source: options.jsx_import_source, + transform_jsx, + var_decl_imports: false, + } + } +} + #[cfg(test)] mod tests { use super::*; |