diff options
author | Kitson Kelly <me@kitsonkelly.com> | 2020-10-20 14:10:42 +1100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-10-20 14:10:42 +1100 |
commit | 57e95032c898cfdfe795e6924d402fdbe2ae59a8 (patch) | |
tree | e548b283d0129b5a1c00828f5a456cc094276bd1 /cli/ast.rs | |
parent | 034ab48086557af00216ffe311c71ad4eb0ec4d5 (diff) |
feat(cli): add support for bundle --no-check (#8023)
Fixes #6686
Diffstat (limited to 'cli/ast.rs')
-rw-r--r-- | cli/ast.rs | 136 |
1 files changed, 122 insertions, 14 deletions
diff --git a/cli/ast.rs b/cli/ast.rs index e3b45a6fd..95f243717 100644 --- a/cli/ast.rs +++ b/cli/ast.rs @@ -1,13 +1,14 @@ // Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. use crate::media_type::MediaType; +use crate::tsc_config; use deno_core::error::AnyError; +use deno_core::serde_json; use deno_core::ModuleSpecifier; use std::error::Error; use std::fmt; use std::rc::Rc; -use std::result; use std::sync::Arc; use std::sync::RwLock; use swc_common::chain; @@ -21,6 +22,7 @@ use swc_common::errors::HandlerFlags; use swc_common::FileName; use swc_common::Globals; use swc_common::Loc; +use swc_common::SourceFile; use swc_common::SourceMap; use swc_common::Span; use swc_ecmascript::ast::Module; @@ -38,13 +40,11 @@ use swc_ecmascript::parser::TsConfig; use swc_ecmascript::transforms::fixer; use swc_ecmascript::transforms::helpers; use swc_ecmascript::transforms::pass::Optional; -use swc_ecmascript::transforms::proposals::decorators; +use swc_ecmascript::transforms::proposals; use swc_ecmascript::transforms::react; use swc_ecmascript::transforms::typescript; use swc_ecmascript::visit::FoldWith; -type Result<V> = result::Result<V, AnyError>; - static TARGET: JscTarget = JscTarget::Es2020; #[derive(Debug, Clone, Eq, PartialEq)] @@ -173,7 +173,10 @@ pub fn get_syntax(media_type: &MediaType) -> Syntax { /// Options which can be adjusted when transpiling a module. #[derive(Debug, Clone)] -pub struct TranspileOptions { +pub struct EmitOptions { + /// Indicate if JavaScript is being checked/transformed as well, or if it is + /// only TypeScript. + pub check_js: bool, /// When emitting a legacy decorator, also emit experimental decorator meta /// data. Defaults to `false`. pub emit_metadata: bool, @@ -190,9 +193,10 @@ pub struct TranspileOptions { pub transform_jsx: bool, } -impl Default for TranspileOptions { +impl Default for EmitOptions { fn default() -> Self { - TranspileOptions { + EmitOptions { + check_js: false, emit_metadata: false, inline_source_map: true, jsx_factory: "React.createElement".into(), @@ -202,6 +206,21 @@ impl Default for TranspileOptions { } } +impl From<tsc_config::TsConfig> for EmitOptions { + fn from(config: tsc_config::TsConfig) -> Self { + let options: tsc_config::EmitConfigOptions = + serde_json::from_value(config.0).unwrap(); + EmitOptions { + check_js: options.check_js, + emit_metadata: options.emit_decorator_metadata, + inline_source_map: true, + jsx_factory: options.jsx_factory, + jsx_fragment_factory: options.jsx_fragment_factory, + transform_jsx: options.jsx == "react", + } + } +} + /// A logical structure to hold the value of a parsed module for further /// processing. #[derive(Clone)] @@ -245,8 +264,8 @@ impl ParsedModule { /// The result is a tuple of the code and optional source map as strings. pub fn transpile( self, - options: &TranspileOptions, - ) -> Result<(String, Option<String>)> { + options: &EmitOptions, + ) -> Result<(String, Option<String>), AnyError> { let program = Program::Module(self.module); let jsx_pass = react::react( @@ -263,7 +282,7 @@ impl ParsedModule { ); let mut passes = chain!( Optional::new(jsx_pass, options.transform_jsx), - decorators::decorators(decorators::Config { + proposals::decorators::decorators(proposals::decorators::Config { legacy: true, emit_metadata: options.emit_metadata }), @@ -329,7 +348,7 @@ pub fn parse( specifier: &ModuleSpecifier, source: &str, media_type: &MediaType, -) -> Result<ParsedModule> { +) -> Result<ParsedModule, AnyError> { let source_map = SourceMap::default(); let source_file = source_map.new_source_file( FileName::Custom(specifier.to_string()), @@ -372,6 +391,95 @@ pub fn parse( }) } +/// A low level function which transpiles a source module into an swc +/// SourceFile. +pub fn transpile_module( + filename: &str, + src: &str, + media_type: &MediaType, + emit_options: &EmitOptions, + cm: Rc<SourceMap>, +) -> Result<(Rc<SourceFile>, Module), AnyError> { + // TODO(@kitsonk) DRY-up with ::parse() + let error_buffer = ErrorBuffer::new(); + let handler = Handler::with_emitter_and_flags( + Box::new(error_buffer.clone()), + HandlerFlags { + can_emit_warnings: true, + dont_buffer_diagnostics: true, + ..HandlerFlags::default() + }, + ); + let comments = SingleThreadedComments::default(); + let syntax = get_syntax(media_type); + let source_file = + cm.new_source_file(FileName::Custom(filename.to_string()), src.to_string()); + let lexer = Lexer::new( + syntax, + TARGET, + StringInput::from(&*source_file), + Some(&comments), + ); + let mut parser = swc_ecmascript::parser::Parser::new_from(lexer); + let sm = cm.clone(); + let module = parser.parse_module().map_err(move |err| { + let mut diagnostic = err.into_diagnostic(&handler); + diagnostic.emit(); + + DiagnosticBuffer::from_error_buffer(error_buffer, |span| { + sm.lookup_char_pos(span.lo) + }) + })?; + // TODO(@kitsonk) DRY-up with ::transpile() + let jsx_pass = react::react( + cm, + Some(&comments), + react::Options { + pragma: emit_options.jsx_factory.clone(), + pragma_frag: emit_options.jsx_fragment_factory.clone(), + // this will use `Object.assign()` instead of the `_extends` helper + // when spreading props. + use_builtins: true, + ..Default::default() + }, + ); + let mut passes = chain!( + Optional::new(jsx_pass, emit_options.transform_jsx), + proposals::decorators::decorators(proposals::decorators::Config { + legacy: true, + emit_metadata: emit_options.emit_metadata + }), + typescript::strip(), + fixer(Some(&comments)), + ); + let module = module.fold_with(&mut passes); + + Ok((source_file, module)) +} + +pub struct BundleHook; + +impl swc_bundler::Hook for BundleHook { + fn get_import_meta_url( + &self, + span: swc_common::Span, + file: &swc_common::FileName, + ) -> Result<Option<swc_ecmascript::ast::Expr>, AnyError> { + // we use custom file names, and swc "wraps" these in `<` and `>` so, we + // want to strip those back out. + let mut value = file.to_string(); + value.pop(); + value.remove(0); + Ok(Some(swc_ecmascript::ast::Expr::Lit( + swc_ecmascript::ast::Lit::Str(swc_ecmascript::ast::Str { + span, + value: value.into(), + has_escape: false, + }), + ))) + } +} + #[cfg(test)] mod tests { use super::*; @@ -436,7 +544,7 @@ mod tests { let module = parse(&specifier, source, &MediaType::TypeScript) .expect("could not parse module"); let (code, maybe_map) = module - .transpile(&TranspileOptions::default()) + .transpile(&EmitOptions::default()) .expect("could not strip types"); assert!(code.starts_with("var D;\n(function(D) {\n")); assert!( @@ -460,7 +568,7 @@ mod tests { let module = parse(&specifier, source, &MediaType::TSX) .expect("could not parse module"); let (code, _) = module - .transpile(&TranspileOptions::default()) + .transpile(&EmitOptions::default()) .expect("could not strip types"); assert!(code.contains("React.createElement(\"div\", null")); } @@ -491,7 +599,7 @@ mod tests { let module = parse(&specifier, source, &MediaType::TypeScript) .expect("could not parse module"); let (code, _) = module - .transpile(&TranspileOptions::default()) + .transpile(&EmitOptions::default()) .expect("could not strip types"); assert!(code.contains("_applyDecoratedDescriptor(")); } |