summaryrefslogtreecommitdiff
path: root/cli/swc_util.rs
diff options
context:
space:
mode:
authorKitson Kelly <me@kitsonkelly.com>2020-09-14 22:27:44 +1000
committerGitHub <noreply@github.com>2020-09-14 14:27:44 +0200
commita6f34d47222ad7cc40519bd95a58ae773d1fe656 (patch)
tree462618f8013501abcc231ea1316783e173db7ba0 /cli/swc_util.rs
parente688a701e20cbed8908b4fb3629a554dc75b4e5b (diff)
refactor: use ParsedModule and improve MediaTypes enum (#7456)
Diffstat (limited to 'cli/swc_util.rs')
-rw-r--r--cli/swc_util.rs445
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("));
- }
-}