diff options
author | David Sherret <dsherret@users.noreply.github.com> | 2021-09-07 10:39:32 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-09-07 10:39:32 -0400 |
commit | 2c2e3ec1ca47803f791ea72ea6247d8eedf87ec8 (patch) | |
tree | 9ba3ddfde58f4a4feaf98fc230ec18861891c9be /cli/ast | |
parent | a5bcf7033e66a828dc88a313f4cca11f116c3f83 (diff) |
refactor(lsp): use deno_ast and cache swc ASTs (#11780)
Diffstat (limited to 'cli/ast')
-rw-r--r-- | cli/ast/bundle_hook.rs | 13 | ||||
-rw-r--r-- | cli/ast/comments.rs | 106 | ||||
-rw-r--r-- | cli/ast/mod.rs | 565 | ||||
-rw-r--r-- | cli/ast/source_file_info.rs | 130 | ||||
-rw-r--r-- | cli/ast/transforms.rs | 38 |
5 files changed, 202 insertions, 650 deletions
diff --git a/cli/ast/bundle_hook.rs b/cli/ast/bundle_hook.rs index ab7eb545f..8e5b56c32 100644 --- a/cli/ast/bundle_hook.rs +++ b/cli/ast/bundle_hook.rs @@ -1,15 +1,18 @@ // Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. +use deno_ast::swc::bundler::Hook; +use deno_ast::swc::bundler::ModuleRecord; +use deno_ast::swc::common::Span; use deno_core::error::AnyError; pub struct BundleHook; -impl swc_bundler::Hook for BundleHook { +impl Hook for BundleHook { fn get_import_meta_props( &self, - span: swc_common::Span, - module_record: &swc_bundler::ModuleRecord, - ) -> Result<Vec<swc_ecmascript::ast::KeyValueProp>, AnyError> { - use swc_ecmascript::ast; + span: Span, + module_record: &ModuleRecord, + ) -> Result<Vec<deno_ast::swc::ast::KeyValueProp>, AnyError> { + use deno_ast::swc::ast; // we use custom file names, and swc "wraps" these in `<` and `>` so, we // want to strip those back out. diff --git a/cli/ast/comments.rs b/cli/ast/comments.rs deleted file mode 100644 index dfe7f99a9..000000000 --- a/cli/ast/comments.rs +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. - -use std::cell::RefCell; -use std::rc::Rc; -use std::sync::Arc; -use swc_common::comments::Comment; -use swc_common::comments::Comments; -use swc_common::comments::SingleThreadedComments; -use swc_common::comments::SingleThreadedCommentsMapInner; -use swc_common::BytePos; - -/// An implementation of swc's `Comments` that implements `Sync` -/// to support being used in multi-threaded code. This implementation -/// is immutable and should you need mutability you may create a copy -/// by converting it to an swc `SingleThreadedComments`. -#[derive(Clone, Debug)] -pub struct MultiThreadedComments { - leading: Arc<SingleThreadedCommentsMapInner>, - trailing: Arc<SingleThreadedCommentsMapInner>, -} - -impl MultiThreadedComments { - pub fn from_single_threaded(comments: SingleThreadedComments) -> Self { - let (leading, trailing) = comments.take_all(); - let leading = Arc::new(Rc::try_unwrap(leading).unwrap().into_inner()); - let trailing = Arc::new(Rc::try_unwrap(trailing).unwrap().into_inner()); - MultiThreadedComments { leading, trailing } - } - - pub fn as_single_threaded(&self) -> SingleThreadedComments { - let leading = Rc::new(RefCell::new((*self.leading).to_owned())); - let trailing = Rc::new(RefCell::new((*self.trailing).to_owned())); - SingleThreadedComments::from_leading_and_trailing(leading, trailing) - } - - /// Gets a vector of all the comments sorted by position. - pub fn get_vec(&self) -> Vec<Comment> { - let mut comments = self - .leading - .values() - .chain(self.trailing.values()) - .flatten() - .cloned() - .collect::<Vec<_>>(); - comments.sort_by_key(|comment| comment.span.lo); - comments - } -} - -impl Comments for MultiThreadedComments { - fn has_leading(&self, pos: BytePos) -> bool { - self.leading.contains_key(&pos) - } - - fn get_leading(&self, pos: BytePos) -> Option<Vec<Comment>> { - self.leading.get(&pos).cloned() - } - - fn has_trailing(&self, pos: BytePos) -> bool { - self.trailing.contains_key(&pos) - } - - fn get_trailing(&self, pos: BytePos) -> Option<Vec<Comment>> { - self.trailing.get(&pos).cloned() - } - - fn add_leading(&self, _pos: BytePos, _cmt: Comment) { - panic_readonly(); - } - - fn add_leading_comments(&self, _pos: BytePos, _comments: Vec<Comment>) { - panic_readonly(); - } - - fn move_leading(&self, _from: BytePos, _to: BytePos) { - panic_readonly(); - } - - fn take_leading(&self, _pos: BytePos) -> Option<Vec<Comment>> { - panic_readonly(); - } - - fn add_trailing(&self, _pos: BytePos, _cmt: Comment) { - panic_readonly(); - } - - fn add_trailing_comments(&self, _pos: BytePos, _comments: Vec<Comment>) { - panic_readonly(); - } - - fn move_trailing(&self, _from: BytePos, _to: BytePos) { - panic_readonly(); - } - - fn take_trailing(&self, _pos: BytePos) -> Option<Vec<Comment>> { - panic_readonly(); - } - - fn add_pure_comment(&self, _pos: BytePos) { - panic_readonly(); - } -} - -fn panic_readonly() -> ! { - panic!("MultiThreadedComments do not support write operations") -} diff --git a/cli/ast/mod.rs b/cli/ast/mod.rs index 232db1305..57117bf7b 100644 --- a/cli/ast/mod.rs +++ b/cli/ast/mod.rs @@ -1,62 +1,44 @@ // Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. use crate::config_file; -use crate::media_type::MediaType; use crate::text_encoding::strip_bom; +use deno_ast::get_syntax; +use deno_ast::swc::ast::Module; +use deno_ast::swc::ast::Program; +use deno_ast::swc::codegen::text_writer::JsWriter; +use deno_ast::swc::codegen::Node; +use deno_ast::swc::common::chain; +use deno_ast::swc::common::comments::SingleThreadedComments; +use deno_ast::swc::common::BytePos; +use deno_ast::swc::common::FileName; +use deno_ast::swc::common::Globals; +use deno_ast::swc::common::SourceMap; +use deno_ast::swc::common::Spanned; +use deno_ast::swc::parser::lexer::Lexer; +use deno_ast::swc::parser::StringInput; +use deno_ast::swc::transforms::fixer; +use deno_ast::swc::transforms::helpers; +use deno_ast::swc::transforms::hygiene; +use deno_ast::swc::transforms::pass::Optional; +use deno_ast::swc::transforms::proposals; +use deno_ast::swc::transforms::react; +use deno_ast::swc::transforms::typescript; +use deno_ast::swc::visit::FoldWith; +use deno_ast::Diagnostic; +use deno_ast::LineAndColumnDisplay; +use deno_ast::MediaType; +use deno_ast::ParsedSource; use deno_core::error::AnyError; use deno_core::resolve_url_or_path; use deno_core::serde_json; use deno_core::ModuleSpecifier; -use std::error::Error; -use std::fmt; -use std::ops::Range; use std::rc::Rc; -use std::sync::Arc; -use swc_common::chain; -use swc_common::comments::Comment; -use swc_common::comments::CommentKind; -use swc_common::comments::Comments; -use swc_common::comments::SingleThreadedComments; -use swc_common::BytePos; -use swc_common::FileName; -use swc_common::Globals; -use swc_common::SourceFile; -use swc_common::SourceMap; -use swc_common::Span; -use swc_common::Spanned; -use swc_ecmascript::ast::Module; -use swc_ecmascript::ast::Program; -use swc_ecmascript::codegen::text_writer::JsWriter; -use swc_ecmascript::codegen::Node; -use swc_ecmascript::dep_graph::analyze_dependencies; -use swc_ecmascript::dep_graph::DependencyDescriptor; -use swc_ecmascript::parser::lexer::Lexer; -use swc_ecmascript::parser::token::Token; -use swc_ecmascript::parser::EsConfig; -use swc_ecmascript::parser::JscTarget; -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::hygiene; -use swc_ecmascript::transforms::pass::Optional; -use swc_ecmascript::transforms::proposals; -use swc_ecmascript::transforms::react; -use swc_ecmascript::transforms::typescript; -use swc_ecmascript::visit::FoldWith; mod bundle_hook; -mod comments; -mod source_file_info; mod transforms; pub use bundle_hook::BundleHook; -use comments::MultiThreadedComments; -use source_file_info::SourceFileInfo; - -static TARGET: JscTarget = JscTarget::Es2020; #[derive(Debug, Clone, Eq, PartialEq)] pub struct Location { @@ -65,9 +47,29 @@ pub struct Location { pub col: usize, } -impl From<swc_common::Loc> for Location { - fn from(swc_loc: swc_common::Loc) -> Self { - use swc_common::FileName::*; +impl Location { + pub fn from_pos(parsed_source: &ParsedSource, pos: BytePos) -> Self { + Location::from_line_and_column( + parsed_source.specifier().to_string(), + parsed_source.source().line_and_column_index(pos), + ) + } + + pub fn from_line_and_column( + specifier: String, + line_and_column: deno_ast::LineAndColumnIndex, + ) -> Self { + Location { + specifier, + line: line_and_column.line_index + 1, + col: line_and_column.column_index, + } + } +} + +impl From<deno_ast::swc::common::Loc> for Location { + fn from(swc_loc: deno_ast::swc::common::Loc) -> Self { + use deno_ast::swc::common::FileName::*; let filename = match &swc_loc.file.name { Real(path_buf) => path_buf.to_string_lossy().to_string(), @@ -78,7 +80,7 @@ impl From<swc_common::Loc> for Location { Location { specifier: filename, line: swc_loc.line, - col: swc_loc.col_display, + col: swc_loc.col.0, } } } @@ -95,60 +97,6 @@ impl std::fmt::Display for Location { } } -/// A diagnostic from the AST parser. -#[derive(Debug, Clone, Eq, PartialEq)] -pub struct Diagnostic { - pub location: Location, - pub message: String, -} - -impl Error for Diagnostic {} - -impl fmt::Display for Diagnostic { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{} at {}", self.message, self.location) - } -} - -fn get_es_config(jsx: bool) -> EsConfig { - EsConfig { - class_private_methods: true, - class_private_props: true, - class_props: true, - dynamic_import: true, - export_default_from: true, - export_namespace_from: true, - import_meta: true, - jsx, - nullish_coalescing: true, - num_sep: true, - optional_chaining: true, - top_level_await: true, - ..EsConfig::default() - } -} - -fn get_ts_config(tsx: bool, dts: bool) -> TsConfig { - TsConfig { - decorators: true, - dts, - dynamic_import: true, - tsx, - ..TsConfig::default() - } -} - -pub fn get_syntax(media_type: &MediaType) -> Syntax { - match media_type { - MediaType::JavaScript => Syntax::Es(get_es_config(false)), - MediaType::Jsx => Syntax::Es(get_es_config(true)), - MediaType::TypeScript => Syntax::Typescript(get_ts_config(false, false)), - MediaType::Dts => Syntax::Typescript(get_ts_config(false, true)), - MediaType::Tsx => Syntax::Typescript(get_ts_config(true, false)), - _ => Syntax::Es(get_es_config(false)), - } -} - #[derive(Debug, Clone)] pub enum ImportsNotUsedAsValues { Remove, @@ -246,222 +194,91 @@ fn strip_config_from_emit_options( } } -/// A logical structure to hold the value of a parsed module for further -/// processing. -#[derive(Clone)] -pub struct ParsedModule { - info: Arc<SourceFileInfo>, - comments: MultiThreadedComments, - pub module: Module, -} - -impl fmt::Debug for ParsedModule { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("ParsedModule") - .field("comments", &self.comments) - .field("module", &self.module) - .finish() - } -} - -impl ParsedModule { - /// Return a vector of dependencies for the module. - pub fn analyze_dependencies(&self) -> Vec<DependencyDescriptor> { - analyze_dependencies(&self.module, &self.comments) - } - - /// Get the module's leading comments, where triple slash directives might - /// be located. - pub fn get_leading_comments(&self) -> Vec<Comment> { - self - .comments - .get_leading(self.module.span.lo) - .unwrap_or_else(Vec::new) - } - - /// Get the module's comments sorted by position. - pub fn get_comments(&self) -> Vec<Comment> { - self.comments.get_vec() - } - - /// Get a location for a given position within the module. - pub fn get_location(&self, pos: BytePos) -> Location { - self.info.get_location(pos) - } - - /// Transform a TypeScript file into a JavaScript file, based on the supplied - /// options. - /// - /// The result is a tuple of the code and optional source map as strings. - pub fn transpile( - self, - options: &EmitOptions, - ) -> Result<(String, Option<String>), AnyError> { - let program = Program::Module(self.module); - let source_map = Rc::new(SourceMap::default()); - let file_name = FileName::Custom(self.info.specifier.clone()); - source_map.new_source_file(file_name, self.info.text.clone()); - let comments = self.comments.as_single_threaded(); // needs to be mutable - - let jsx_pass = react::react( - source_map.clone(), - Some(&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), - Optional::new(transforms::DownlevelImportsFolder, options.repl_imports), - Optional::new(transforms::StripExportsFolder, options.repl_imports), - proposals::decorators::decorators(proposals::decorators::Config { - legacy: true, - emit_metadata: options.emit_metadata - }), - // DownlevelImportsFolder::new(), // todo: make this conditional - helpers::inject_helpers(), - typescript::strip::strip_with_config(strip_config_from_emit_options( - options - )), - fixer(Some(&comments)), - hygiene(), - ); - - 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( - 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(&comments), - cm: 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(); - 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)) - } -} - -/// For a given specifier, source, and media type, parse the text of the -/// module and return a representation which can be further processed. +/// Transform a TypeScript file into a JavaScript file, based on the supplied +/// options. /// -/// # Arguments -/// -/// - `specifier` - The module specifier for the module. -/// - `source` - The source code for the module. -/// - `media_type` - The media type for the module. -/// -// NOTE(bartlomieju): `specifier` has `&str` type instead of -// `&ModuleSpecifier` because runtime compiler APIs don't -// require valid module specifiers -pub fn parse( - specifier: &str, - source: &str, - media_type: &MediaType, -) -> Result<ParsedModule, AnyError> { - let source = strip_bom(source); - let info = SourceFileInfo::new(specifier, source); - let input = - StringInput::new(source, BytePos(0), BytePos(source.len() as u32)); - let (comments, module) = - parse_string_input(input, media_type).map_err(|err| Diagnostic { - location: info.get_location(err.span().lo), - message: err.into_kind().msg().to_string(), - })?; - - Ok(ParsedModule { - info: Arc::new(info), - comments: MultiThreadedComments::from_single_threaded(comments), - module, - }) -} - -pub enum TokenOrComment { - Token(Token), - Comment { kind: CommentKind, text: String }, -} - -pub struct LexedItem { - pub span: Span, - pub inner: TokenOrComment, -} - -impl LexedItem { - pub fn span_as_range(&self) -> Range<usize> { - self.span.lo.0 as usize..self.span.hi.0 as usize - } -} - -fn flatten_comments( - comments: SingleThreadedComments, -) -> impl Iterator<Item = Comment> { - let (leading, trailing) = comments.take_all(); - let mut comments = (*leading).clone().into_inner(); - comments.extend((*trailing).clone().into_inner()); - comments.into_iter().flat_map(|el| el.1) -} +/// The result is a tuple of the code and optional source map as strings. +pub fn transpile( + parsed_source: &ParsedSource, + options: &EmitOptions, +) -> Result<(String, Option<String>), AnyError> { + let program: Program = (*parsed_source.program()).clone(); + let source_map = Rc::new(SourceMap::default()); + let file_name = FileName::Custom(parsed_source.specifier().to_string()); + source_map + .new_source_file(file_name, parsed_source.source().text().to_string()); + let comments = parsed_source.comments().as_single_threaded(); // needs to be mutable -pub fn lex(source: &str, media_type: &MediaType) -> Vec<LexedItem> { - let comments = SingleThreadedComments::default(); - let lexer = Lexer::new( - get_syntax(media_type), - TARGET, - StringInput::new(source, BytePos(0), BytePos(source.len() as u32)), + let jsx_pass = react::react( + source_map.clone(), Some(&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), + Optional::new(transforms::DownlevelImportsFolder, options.repl_imports), + Optional::new(transforms::StripExportsFolder, options.repl_imports), + proposals::decorators::decorators(proposals::decorators::Config { + legacy: true, + emit_metadata: options.emit_metadata + }), + // DownlevelImportsFolder::new(), // todo: make this conditional + helpers::inject_helpers(), + typescript::strip::strip_with_config(strip_config_from_emit_options( + options + )), + fixer(Some(&comments)), + hygiene(), ); - let mut tokens: Vec<LexedItem> = lexer - .map(|token| LexedItem { - span: token.span, - inner: TokenOrComment::Token(token.token), + let program = deno_ast::swc::common::GLOBALS.set(&Globals::new(), || { + helpers::HELPERS.set(&helpers::Helpers::new(false), || { + program.fold_with(&mut passes) }) - .collect(); - - tokens.extend(flatten_comments(comments).map(|comment| LexedItem { - span: comment.span, - inner: TokenOrComment::Comment { - kind: comment.kind, - text: comment.text, - }, - })); - - tokens.sort_by_key(|item| item.span.lo.0); + }); - tokens + let mut src_map_buf = vec![]; + let mut buf = vec![]; + { + let writer = Box::new(JsWriter::new( + source_map.clone(), + "\n", + &mut buf, + Some(&mut src_map_buf), + )); + let config = deno_ast::swc::codegen::Config { minify: false }; + let mut emitter = deno_ast::swc::codegen::Emitter { + cfg: config, + comments: Some(&comments), + cm: 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(); + 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)) } /// A low level function which transpiles a source module into an swc @@ -469,22 +286,32 @@ pub fn lex(source: &str, media_type: &MediaType) -> Vec<LexedItem> { pub fn transpile_module( specifier: &str, source: &str, - media_type: &MediaType, + media_type: MediaType, emit_options: &EmitOptions, globals: &Globals, cm: Rc<SourceMap>, -) -> Result<(Rc<SourceFile>, Module), AnyError> { +) -> Result<(Rc<deno_ast::swc::common::SourceFile>, Module), AnyError> { let source = strip_bom(source); let source_file = cm.new_source_file( FileName::Custom(specifier.to_string()), source.to_string(), ); let input = StringInput::from(&*source_file); - let (comments, module) = - parse_string_input(input, media_type).map_err(|err| Diagnostic { - location: cm.lookup_char_pos(err.span().lo).into(), + let comments = SingleThreadedComments::default(); + let syntax = get_syntax(media_type); + let lexer = Lexer::new(syntax, deno_ast::TARGET, input, Some(&comments)); + let mut parser = deno_ast::swc::parser::Parser::new_from(lexer); + let module = parser.parse_module().map_err(|err| { + let location = cm.lookup_char_pos(err.span().lo); + Diagnostic { + display_position: LineAndColumnDisplay { + line_number: location.line, + column_number: location.col_display + 1, + }, + specifier: specifier.to_string(), message: err.into_kind().msg().to_string(), - })?; + } + })?; let jsx_pass = react::react( cm, @@ -511,7 +338,7 @@ pub fn transpile_module( fixer(Some(&comments)), ); - let module = swc_common::GLOBALS.set(globals, || { + let module = deno_ast::swc::common::GLOBALS.set(globals, || { helpers::HELPERS.set(&helpers::Helpers::new(false), || { module.fold_with(&mut passes) }) @@ -520,69 +347,12 @@ pub fn transpile_module( Ok((source_file, module)) } -fn parse_string_input( - input: StringInput, - media_type: &MediaType, -) -> Result< - (SingleThreadedComments, Module), - swc_ecmascript::parser::error::Error, -> { - let syntax = get_syntax(media_type); - let comments = SingleThreadedComments::default(); - let lexer = Lexer::new(syntax, TARGET, input, Some(&comments)); - let mut parser = swc_ecmascript::parser::Parser::new_from(lexer); - let module = parser.parse_module()?; - - Ok((comments, module)) -} - #[cfg(test)] mod tests { use super::*; - use std::collections::HashMap; - use swc_common::BytePos; - use swc_ecmascript::dep_graph::DependencyKind; - - #[test] - fn test_parsed_module_analyze_dependencies() { - let specifier = resolve_url_or_path("https://deno.land/x/mod.js").unwrap(); - let source = "import * as bar from './test.ts';\nconst foo = await import('./foo.ts');"; - let parsed_module = - parse(specifier.as_str(), source, &MediaType::JavaScript) - .expect("could not parse module"); - let actual = parsed_module.analyze_dependencies(); - assert_eq!( - actual, - vec![ - DependencyDescriptor { - kind: DependencyKind::Import, - is_dynamic: false, - leading_comments: Vec::new(), - span: Span::new(BytePos(0), BytePos(33), Default::default()), - specifier: "./test.ts".into(), - specifier_span: Span::new( - BytePos(21), - BytePos(32), - Default::default() - ), - import_assertions: HashMap::default(), - }, - DependencyDescriptor { - kind: DependencyKind::Import, - is_dynamic: true, - leading_comments: Vec::new(), - span: Span::new(BytePos(52), BytePos(70), Default::default()), - specifier: "./foo.ts".into(), - specifier_span: Span::new( - BytePos(59), - BytePos(69), - Default::default() - ), - import_assertions: HashMap::default(), - } - ] - ); - } + use deno_ast::parse_module; + use deno_ast::ParseParams; + use deno_ast::SourceTextInfo; #[test] fn test_transpile() { @@ -605,10 +375,15 @@ mod tests { } } "#; - let module = parse(specifier.as_str(), source, &MediaType::TypeScript) - .expect("could not parse module"); - let (code, maybe_map) = module - .transpile(&EmitOptions::default()) + let module = parse_module(ParseParams { + specifier: specifier.as_str().to_string(), + source: SourceTextInfo::from_string(source.to_string()), + media_type: deno_ast::MediaType::TypeScript, + capture_tokens: false, + maybe_syntax: None, + }) + .expect("could not parse module"); + let (code, maybe_map) = transpile(&module, &EmitOptions::default()) .expect("could not strip types"); assert!(code.starts_with("var D;\n(function(D) {\n")); assert!( @@ -628,10 +403,15 @@ mod tests { } } "#; - let module = parse(specifier.as_str(), source, &MediaType::Tsx) - .expect("could not parse module"); - let (code, _) = module - .transpile(&EmitOptions::default()) + let module = parse_module(ParseParams { + specifier: specifier.as_str().to_string(), + source: SourceTextInfo::from_string(source.to_string()), + media_type: deno_ast::MediaType::Tsx, + capture_tokens: false, + maybe_syntax: None, + }) + .expect("could not parse module"); + let (code, _) = transpile(&module, &EmitOptions::default()) .expect("could not strip types"); assert!(code.contains("React.createElement(\"div\", null")); } @@ -658,10 +438,15 @@ mod tests { } } "#; - let module = parse(specifier.as_str(), source, &MediaType::TypeScript) - .expect("could not parse module"); - let (code, _) = module - .transpile(&EmitOptions::default()) + let module = parse_module(ParseParams { + specifier: specifier.as_str().to_string(), + source: SourceTextInfo::from_string(source.to_string()), + media_type: deno_ast::MediaType::TypeScript, + capture_tokens: false, + maybe_syntax: None, + }) + .expect("could not parse module"); + let (code, _) = transpile(&module, &EmitOptions::default()) .expect("could not strip types"); assert!(code.contains("_applyDecoratedDescriptor(")); } diff --git a/cli/ast/source_file_info.rs b/cli/ast/source_file_info.rs deleted file mode 100644 index 5792fb419..000000000 --- a/cli/ast/source_file_info.rs +++ /dev/null @@ -1,130 +0,0 @@ -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. - -use super::Location; - -use swc_common::BytePos; - -pub struct SourceFileInfo { - pub specifier: String, - pub text: String, - line_start_byte_positions: Vec<BytePos>, -} - -impl SourceFileInfo { - pub fn new(specifier: &str, text: &str) -> SourceFileInfo { - SourceFileInfo { - line_start_byte_positions: get_line_start_positions(text), - specifier: specifier.to_string(), - text: text.to_string(), - } - } - - pub fn get_location(&self, pos: BytePos) -> Location { - let line_index = self.get_line_index_at_pos(pos); - let col = self.get_column_on_line_index_at_pos(line_index, pos); - - Location { - specifier: self.specifier.clone(), - // todo(dsherret): this is temporarily 1-indexed in order to have - // the same behaviour as swc, but we should change this to be 0-indexed - // in order to be the same as the LSP. - line: line_index + 1, - col, - } - } - - fn get_line_index_at_pos(&self, pos: BytePos) -> usize { - match self.line_start_byte_positions.binary_search(&pos) { - Ok(index) => index, - Err(insert_index) => insert_index - 1, - } - } - - fn get_column_on_line_index_at_pos( - &self, - line_index: usize, - pos: BytePos, - ) -> usize { - assert!(line_index < self.line_start_byte_positions.len()); - let pos = pos.0 as usize; - let line_start_pos = self.line_start_byte_positions[line_index].0 as usize; - let line_end_pos = self - .line_start_byte_positions - .get(line_index + 1) - // may include line feed chars at the end, but in that case the pos should be less - .map(|p| p.0 as usize) - .unwrap_or_else(|| self.text.len()); - let line_text = &self.text[line_start_pos..line_end_pos]; - - if pos < line_start_pos { - panic!( - "byte position {} was less than the start line position of {}", - pos, line_start_pos - ); - } else if pos > line_end_pos { - panic!( - "byte position {} exceeded the end line position of {}", - pos, line_end_pos - ); - } else if pos == line_end_pos { - line_text.chars().count() - } else { - line_text - .char_indices() - .position(|(c_pos, _)| line_start_pos + c_pos >= pos) - .unwrap() - } - } -} - -fn get_line_start_positions(text: &str) -> Vec<BytePos> { - let mut result = vec![BytePos(0)]; - for (pos, c) in text.char_indices() { - if c == '\n' { - let line_start_pos = BytePos((pos + 1) as u32); - result.push(line_start_pos); - } - } - result -} - -#[cfg(test)] -mod test { - use super::SourceFileInfo; - use crate::ast::Location; - - use swc_common::BytePos; - - #[test] - fn should_provide_locations() { - let text = "12\n3\r\n4\n5"; - let specifier = "file:///file.ts"; - let info = SourceFileInfo::new(specifier, text); - assert_pos_line_and_col(&info, 0, 1, 0); // 1 - assert_pos_line_and_col(&info, 1, 1, 1); // 2 - assert_pos_line_and_col(&info, 2, 1, 2); // \n - assert_pos_line_and_col(&info, 3, 2, 0); // 3 - assert_pos_line_and_col(&info, 4, 2, 1); // \r - assert_pos_line_and_col(&info, 5, 2, 2); // \n - assert_pos_line_and_col(&info, 6, 3, 0); // 4 - assert_pos_line_and_col(&info, 7, 3, 1); // \n - assert_pos_line_and_col(&info, 8, 4, 0); // 5 - assert_pos_line_and_col(&info, 9, 4, 1); // <EOF> - } - - fn assert_pos_line_and_col( - info: &SourceFileInfo, - pos: u32, - line: usize, - col: usize, - ) { - assert_eq!( - info.get_location(BytePos(pos)), - Location { - specifier: info.specifier.clone(), - line, - col, - } - ); - } -} diff --git a/cli/ast/transforms.rs b/cli/ast/transforms.rs index 461a12ff4..142f27093 100644 --- a/cli/ast/transforms.rs +++ b/cli/ast/transforms.rs @@ -1,7 +1,7 @@ -use swc_common::DUMMY_SP; -use swc_ecmascript::ast as swc_ast; -use swc_ecmascript::visit::noop_fold_type; -use swc_ecmascript::visit::Fold; +use deno_ast::swc::ast as swc_ast; +use deno_ast::swc::common::DUMMY_SP; +use deno_ast::swc::visit::noop_fold_type; +use deno_ast::swc::visit::Fold; /// Transforms import declarations to variable declarations /// with a dynamic import. This is used to provide import @@ -15,7 +15,7 @@ impl Fold for DownlevelImportsFolder { &mut self, module_item: swc_ast::ModuleItem, ) -> swc_ast::ModuleItem { - use swc_ecmascript::ast::*; + use deno_ast::swc::ast::*; match &module_item { ModuleItem::ModuleDecl(ModuleDecl::Import(import_decl)) => { @@ -117,7 +117,7 @@ impl Fold for StripExportsFolder { &mut self, module_item: swc_ast::ModuleItem, ) -> swc_ast::ModuleItem { - use swc_ecmascript::ast::*; + use deno_ast::swc::ast::*; match module_item { ModuleItem::ModuleDecl(ModuleDecl::ExportAll(export_all)) => { @@ -249,18 +249,18 @@ fn create_assignment(key: String) -> swc_ast::ObjectPatProp { #[cfg(test)] mod test { + use deno_ast::swc::ast::Module; + use deno_ast::swc::codegen::text_writer::JsWriter; + use deno_ast::swc::codegen::Node; + use deno_ast::swc::common::FileName; + use deno_ast::swc::common::SourceMap; + use deno_ast::swc::parser::Parser; + use deno_ast::swc::parser::StringInput; + use deno_ast::swc::parser::Syntax; + use deno_ast::swc::parser::TsConfig; + use deno_ast::swc::visit::Fold; + use deno_ast::swc::visit::FoldWith; use std::rc::Rc; - use swc_common::FileName; - use swc_common::SourceMap; - use swc_ecmascript::ast::Module; - use swc_ecmascript::codegen::text_writer::JsWriter; - use swc_ecmascript::codegen::Node; - use swc_ecmascript::parser::Parser; - use swc_ecmascript::parser::StringInput; - use swc_ecmascript::parser::Syntax; - use swc_ecmascript::parser::TsConfig; - use swc_ecmascript::visit::Fold; - use swc_ecmascript::visit::FoldWith; use super::*; @@ -450,8 +450,8 @@ mod test { { let writer = Box::new(JsWriter::new(source_map.clone(), "\n", &mut buf, None)); - let config = swc_ecmascript::codegen::Config { minify: false }; - let mut emitter = swc_ecmascript::codegen::Emitter { + let config = deno_ast::swc::codegen::Config { minify: false }; + let mut emitter = deno_ast::swc::codegen::Emitter { cfg: config, comments: None, cm: source_map, |