diff options
Diffstat (limited to 'cli/swc_util.rs')
-rw-r--r-- | cli/swc_util.rs | 445 |
1 files changed, 0 insertions, 445 deletions
diff --git a/cli/swc_util.rs b/cli/swc_util.rs deleted file mode 100644 index f54f187e3..000000000 --- a/cli/swc_util.rs +++ /dev/null @@ -1,445 +0,0 @@ -// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. - -use crate::msg::MediaType; -use deno_core::ErrBox; -use serde::Serialize; -use std::error::Error; -use std::fmt; -use std::rc::Rc; -use std::sync::Arc; -use std::sync::RwLock; -use swc_common::chain; -use swc_common::comments::SingleThreadedComments; -use swc_common::errors::Diagnostic; -use swc_common::errors::DiagnosticBuilder; -use swc_common::errors::Emitter; -use swc_common::errors::Handler; -use swc_common::errors::HandlerFlags; -use swc_common::FileName; -use swc_common::Globals; -use swc_common::SourceMap; -use swc_common::Span; -use swc_ecmascript::ast::Program; -use swc_ecmascript::codegen::text_writer::JsWriter; -use swc_ecmascript::codegen::Node; -use swc_ecmascript::parser::lexer::Lexer; -use swc_ecmascript::parser::EsConfig; -use swc_ecmascript::parser::JscTarget; -use swc_ecmascript::parser::Parser; -use swc_ecmascript::parser::StringInput; -use swc_ecmascript::parser::Syntax; -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::react; -use swc_ecmascript::transforms::typescript; -use swc_ecmascript::visit::FoldWith; - -#[derive(Debug, Serialize, Clone, PartialEq)] -pub struct Location { - pub filename: String, - pub line: usize, - pub col: usize, -} - -impl Into<Location> for swc_common::Loc { - fn into(self) -> Location { - use swc_common::FileName::*; - - let filename = match &self.file.name { - Real(path_buf) => path_buf.to_string_lossy().to_string(), - Custom(str_) => str_.to_string(), - _ => panic!("invalid filename"), - }; - - Location { - filename, - line: self.line, - col: self.col_display, - } - } -} - -fn get_default_es_config() -> EsConfig { - let mut config = EsConfig::default(); - config.num_sep = true; - config.class_private_props = true; - config.class_private_methods = true; - config.class_props = true; - config.export_default_from = true; - config.export_namespace_from = true; - config.dynamic_import = true; - config.nullish_coalescing = true; - config.optional_chaining = true; - config.import_meta = true; - config.top_level_await = true; - config -} - -fn get_default_ts_config() -> TsConfig { - let mut ts_config = TsConfig::default(); - ts_config.dynamic_import = true; - ts_config.decorators = true; - ts_config -} - -pub fn get_syntax_for_dts() -> Syntax { - let mut ts_config = TsConfig::default(); - ts_config.dts = true; - Syntax::Typescript(ts_config) -} - -pub fn get_syntax_for_media_type(media_type: MediaType) -> Syntax { - match media_type { - MediaType::JavaScript => Syntax::Es(get_default_es_config()), - MediaType::JSX => { - let mut config = get_default_es_config(); - config.jsx = true; - Syntax::Es(config) - } - MediaType::TypeScript => Syntax::Typescript(get_default_ts_config()), - MediaType::TSX => { - let mut config = get_default_ts_config(); - config.tsx = true; - Syntax::Typescript(config) - } - _ => Syntax::Es(get_default_es_config()), - } -} - -#[derive(Clone, Debug)] -pub struct SwcDiagnosticBuffer { - pub diagnostics: Vec<String>, -} - -impl Error for SwcDiagnosticBuffer {} - -impl fmt::Display for SwcDiagnosticBuffer { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let msg = self.diagnostics.join(","); - - f.pad(&msg) - } -} - -impl SwcDiagnosticBuffer { - pub fn from_swc_error( - error_buffer: SwcErrorBuffer, - parser: &AstParser, - ) -> Self { - let s = error_buffer.0.read().unwrap().clone(); - - let diagnostics = s - .iter() - .map(|d| { - let mut msg = d.message(); - - if let Some(span) = d.span.primary_span() { - let location = parser.get_span_location(span); - let filename = match &location.file.name { - FileName::Custom(n) => n, - _ => unreachable!(), - }; - msg = format!( - "{} at {}:{}:{}", - msg, filename, location.line, location.col_display - ); - } - - msg - }) - .collect::<Vec<String>>(); - - Self { diagnostics } - } -} - -#[derive(Clone)] -pub struct SwcErrorBuffer(Arc<RwLock<Vec<Diagnostic>>>); - -impl SwcErrorBuffer { - pub fn default() -> Self { - Self(Arc::new(RwLock::new(vec![]))) - } -} - -impl Emitter for SwcErrorBuffer { - fn emit(&mut self, db: &DiagnosticBuilder) { - self.0.write().unwrap().push((**db).clone()); - } -} - -/// Low-level utility structure with common AST parsing functions. -/// -/// Allows to build more complicated parser by providing a callback -/// to `parse_module`. -pub struct AstParser { - pub buffered_error: SwcErrorBuffer, - pub source_map: Rc<SourceMap>, - pub handler: Handler, - pub comments: SingleThreadedComments, - pub globals: Globals, -} - -impl AstParser { - pub fn default() -> Self { - let buffered_error = SwcErrorBuffer::default(); - - let handler = Handler::with_emitter_and_flags( - Box::new(buffered_error.clone()), - HandlerFlags { - dont_buffer_diagnostics: true, - can_emit_warnings: true, - ..Default::default() - }, - ); - - AstParser { - buffered_error, - source_map: Rc::new(SourceMap::default()), - handler, - comments: SingleThreadedComments::default(), - globals: Globals::new(), - } - } - - pub fn parse_module( - &self, - file_name: &str, - media_type: MediaType, - source_code: &str, - ) -> Result<swc_ecmascript::ast::Module, SwcDiagnosticBuffer> { - let swc_source_file = self.source_map.new_source_file( - FileName::Custom(file_name.to_string()), - source_code.to_string(), - ); - - let buffered_err = self.buffered_error.clone(); - let syntax = get_syntax_for_media_type(media_type); - - let lexer = Lexer::new( - syntax, - JscTarget::Es2019, - StringInput::from(&*swc_source_file), - Some(&self.comments), - ); - - let mut parser = Parser::new_from(lexer); - - parser.parse_module().map_err(move |err| { - let mut diagnostic = err.into_diagnostic(&self.handler); - diagnostic.emit(); - SwcDiagnosticBuffer::from_swc_error(buffered_err, self) - }) - } - - pub fn get_span_location(&self, span: Span) -> swc_common::Loc { - self.source_map.lookup_char_pos(span.lo()) - } - - pub fn get_span_comments( - &self, - span: Span, - ) -> Vec<swc_common::comments::Comment> { - self - .comments - .with_leading(span.lo(), |comments| comments.to_vec()) - } -} - -#[derive(Debug, Clone)] -pub struct EmitTranspileOptions { - /// When emitting a legacy decorator, also emit experimental decorator meta - /// data. Defaults to `false`. - pub emit_metadata: bool, - /// Should the source map be inlined in the emitted code file, or provided - /// as a separate file. Defaults to `true`. - pub inline_source_map: bool, - /// When transforming JSX, what value should be used for the JSX factory. - /// Defaults to `React.createElement`. - pub jsx_factory: String, - /// When transforming JSX, what value should be used for the JSX fragment - /// factory. Defaults to `React.Fragment`. - pub jsx_fragment_factory: String, - /// Should JSX be transformed or preserved. Defaults to `true`. - pub transform_jsx: bool, -} - -impl Default for EmitTranspileOptions { - fn default() -> Self { - EmitTranspileOptions { - emit_metadata: false, - inline_source_map: true, - jsx_factory: "React.createElement".into(), - jsx_fragment_factory: "React.Fragment".into(), - transform_jsx: true, - } - } -} - -pub fn transpile( - file_name: &str, - media_type: MediaType, - source_code: &str, - options: &EmitTranspileOptions, -) -> Result<(String, Option<String>), ErrBox> { - let ast_parser = AstParser::default(); - let module = ast_parser.parse_module(file_name, media_type, source_code)?; - let program = Program::Module(module); - - let jsx_pass = react::react( - ast_parser.source_map.clone(), - Some(&ast_parser.comments), - react::Options { - pragma: options.jsx_factory.clone(), - pragma_frag: 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, options.transform_jsx), - decorators::decorators(decorators::Config { - legacy: true, - emit_metadata: options.emit_metadata - }), - typescript::strip(), - fixer(Some(&ast_parser.comments)), - ); - - let program = swc_common::GLOBALS.set(&Globals::new(), || { - helpers::HELPERS.set(&helpers::Helpers::new(false), || { - program.fold_with(&mut passes) - }) - }); - - let mut src_map_buf = vec![]; - let mut buf = vec![]; - { - let writer = Box::new(JsWriter::new( - ast_parser.source_map.clone(), - "\n", - &mut buf, - Some(&mut src_map_buf), - )); - let config = swc_ecmascript::codegen::Config { minify: false }; - let mut emitter = swc_ecmascript::codegen::Emitter { - cfg: config, - comments: Some(&ast_parser.comments), - cm: ast_parser.source_map.clone(), - wr: writer, - }; - program.emit_with(&mut emitter)?; - } - let mut src = String::from_utf8(buf)?; - let mut map: Option<String> = None; - { - let mut buf = Vec::new(); - ast_parser - .source_map - .build_source_map_from(&mut src_map_buf, None) - .to_writer(&mut buf)?; - - if options.inline_source_map { - src.push_str("//# sourceMappingURL=data:application/json;base64,"); - let encoded_map = base64::encode(buf); - src.push_str(&encoded_map); - } else { - map = Some(String::from_utf8(buf)?); - } - } - Ok((src, map)) -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_transpile() { - let source = r#" - enum D { - A, - B, - C, - } - export class A { - private b: string; - protected c: number = 1; - e: "foo"; - constructor (public d = D.A) { - const e = "foo" as const; - this.e = e; - } - } - "#; - let result = transpile( - "test.ts", - MediaType::TypeScript, - source, - &EmitTranspileOptions::default(), - ) - .unwrap(); - let (code, maybe_map) = result; - assert!(code.starts_with("var D;\n(function(D) {\n")); - assert!( - code.contains("\n//# sourceMappingURL=data:application/json;base64,") - ); - assert!(maybe_map.is_none()); - } - - #[test] - fn test_transpile_tsx() { - let source = r#" - export class A { - render() { - return <div><span></span></div> - } - } - "#; - let result = transpile( - "test.ts", - MediaType::TSX, - source, - &EmitTranspileOptions::default(), - ) - .unwrap(); - let (code, _maybe_source_map) = result; - assert!(code.contains("React.createElement(\"div\", null")); - } - - #[test] - fn test_transpile_decorators() { - let source = r#" - function enumerable(value: boolean) { - return function ( - _target: any, - _propertyKey: string, - descriptor: PropertyDescriptor, - ) { - descriptor.enumerable = value; - }; - } - - export class A { - @enumerable(false) - a() { - Test.value; - } - } - "#; - let result = transpile( - "test.ts", - MediaType::TypeScript, - source, - &EmitTranspileOptions::default(), - ) - .unwrap(); - let (code, _maybe_source_map) = result; - assert!(code.contains("_applyDecoratedDescriptor(")); - } -} |