From 3f04a420737219a4e920e8f721ad4a9f4eadafac Mon Sep 17 00:00:00 2001 From: Kitson Kelly Date: Fri, 21 Aug 2020 17:45:27 +1000 Subject: [PATCH] WIP --- compiler/ast.rs | 114 ++++++++++++++++++++++----------------- compiler/compiler.rs | 43 +-------------- compiler/lib.rs | 43 ++++++++++++++- compiler/module_graph.rs | 39 ++++++++++++-- 4 files changed, 143 insertions(+), 96 deletions(-) diff --git a/compiler/ast.rs b/compiler/ast.rs index 762249c0175fec..2d9557173d6a86 100644 --- a/compiler/ast.rs +++ b/compiler/ast.rs @@ -75,6 +75,7 @@ struct EmptyHandler; impl swc_ecmascript::codegen::Handlers for EmptyHandler {} +/// A buffer for collecting diagnostic messages from the AST parser. #[derive(Debug)] pub struct DiagnosticBuffer(Vec); @@ -170,6 +171,39 @@ fn get_syntax(media_type: MediaType) -> Syntax { } } +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct DependencyDescriptor { + /// A flag indicating if the import is dynamic or not. + pub is_dynamic: bool, + /// A flag indicating that the import is a `type` only type of import. + pub is_type: bool, + /// Any leading comments associated with the dependency. This is used for + /// further processing of supported pragma that impact the dependency. + pub leading_comments: Vec, + /// The location of the import/export statement. + pub location: Location, + /// The text specifier associated with the import/export statement. + pub specifier: String, +} + +impl Default for DependencyDescriptor { + fn default() -> Self { + DependencyDescriptor { + is_dynamic: false, + is_type: false, + leading_comments: Vec::new(), + location: Location::default(), + specifier: String::default(), + } + } +} + +struct DependencyCollector<'a> { + comments: &'a SingleThreadedComments, + pub items: Vec, + source_map: &'a SourceMap, +} + #[derive(Debug, Clone)] pub struct EmitTranspileOptions { /// When emitting a legacy decorator, also emit experimental decorator meta @@ -221,7 +255,7 @@ impl fmt::Debug for ParsedModule { impl ParsedModule { /// Identifies all syntactical dependencies of a module (e.g. `import` and - /// `export` statements). + /// `export` statements) and returns them as a vector of descriptors. pub fn get_dependencies(&self) -> Vec { let mut collector = DependencyCollector { comments: &self.comments, @@ -315,6 +349,13 @@ impl ParsedModule { /// For a given specifier, source, and media type, parse the source of the /// module and return a representation which can be further processed. +/// +/// # Arguments +/// +/// - `specifier` - The module specifier for the module. +/// - `source` - The source code for the module. +/// - `media_type` - The media type for the module. +/// pub fn parse( specifier: &ModuleSpecifier, source: &str, @@ -362,33 +403,6 @@ pub fn parse( }) } -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct DependencyDescriptor { - pub is_dynamic: bool, - pub is_type: bool, - pub leading_comments: Vec, - pub location: Location, - pub specifier: String, -} - -impl Default for DependencyDescriptor { - fn default() -> Self { - DependencyDescriptor { - is_dynamic: false, - is_type: false, - leading_comments: Vec::new(), - location: Location::default(), - specifier: String::default(), - } - } -} - -struct DependencyCollector<'a> { - comments: &'a SingleThreadedComments, - pub items: Vec, - source_map: &'a SourceMap, -} - impl<'a> Visit for DependencyCollector<'a> { fn visit_import_decl( &mut self, @@ -540,7 +554,7 @@ mod tests { location: Location { filename: "https://deno.land/x/mod.js".to_owned(), col: 0, - line: 0, + line: 1, }, specifier: "./test.ts".to_owned() }, @@ -550,8 +564,8 @@ mod tests { leading_comments: Vec::new(), location: Location { filename: "https://deno.land/x/mod.js".to_owned(), - col: 18, - line: 1, + col: 22, + line: 2, }, specifier: "./foo.ts".to_owned() } @@ -559,23 +573,27 @@ mod tests { ); } - // #[test] - // fn test_parsed_module_get_leading_comments() { - // let specifier = - // ModuleSpecifier::resolve_url_or_path("https://deno.land/x/mod.ts") - // .unwrap(); - // let source = r#"// this is the first comment - // // this is the second comment - // import * as bar from "./test.ts";"#; - // let parsed_module = parse(&specifier, source, MediaType::TypeScript) - // .expect("could not parse module"); - // let dependencies = parsed_module.get_dependencies(); - // let first = dependencies.first().unwrap(); - // let actual = parsed_module.get_leading_comments(&first.span); - // assert_eq!(actual[0].text, " this is the first comment".to_string()); - // assert_eq!(actual[1].text, " this is the second comment".to_string()); - // assert_eq!(actual.len(), 2); - // } + #[test] + fn test_parsed_module_get_leading_comments() { + let specifier = + ModuleSpecifier::resolve_url_or_path("https://deno.land/x/mod.ts") + .unwrap(); + let source = r#"// this is the first comment + // this is the second comment + import * as bar from "./test.ts";"#; + let parsed_module = parse(&specifier, source, MediaType::TypeScript) + .expect("could not parse module"); + let dependencies = parsed_module.get_dependencies(); + let leading_comments: Vec<&str> = dependencies[0] + .leading_comments + .iter() + .map(|c| c.text.as_str()) + .collect(); + assert_eq!( + leading_comments, + vec![" this is the first comment", " this is the second comment"] + ); + } #[test] fn test_transpile() { diff --git a/compiler/compiler.rs b/compiler/compiler.rs index e3b9ee5df80e80..a434cb2d724cc8 100644 --- a/compiler/compiler.rs +++ b/compiler/compiler.rs @@ -10,7 +10,9 @@ use crate::msg::IgnoredCompilerOptions; use crate::ops; use crate::ops::compiler_op; use crate::ops::json_op; +use crate::CompileOptions; use crate::Result; +use crate::TranspileOptions; use deno_core::js_check; use deno_core::CoreIsolate; @@ -48,47 +50,6 @@ pub struct CompilerState { pub written_files: Vec, } -/// A structure which provides options to `CompilerIsolate::compile()` -#[derive(Default)] -pub struct CompileOptions<'a> { - /// If `true` then files will be transpiled in a way that they can be combined - /// together in a bundle. This will not actually perform the bundling, but - /// just provide individual files back that could be. - pub bundle: bool, - /// If `true` then debug logging will be output from the isolate. - pub debug: bool, - /// A flag to indicate that the compilation should be incremental, and only - /// changed sources will be emitted based on information supplied in the - /// `maybe_build_info` argument. - pub incremental: bool, - /// A vector of libs to be used when type checking the code. For example: - /// - /// ```rust - /// let libs = vec!["deno.ns"]; - /// ``` - /// - pub lib: Vec<&'a str>, - /// A string of configuration data that augments the the default configuration - /// passed to the TypeScript compiler. This is typically the contents of a - /// user supplied `tsconfig.json`. - pub maybe_config: Option, -} - -/// A structure which provides options to `CompilerIsolate::compile()` -#[derive(Default)] -pub struct TranspileOptions { - /// If `true` then files will be transpiled in a way that they can be combined - /// together in a bundle. This will not actually perform the bundling, but - /// just provide individual files back that could be. - pub bundle: bool, - /// If `true` then debug logging will be output from the isolate. - pub debug: bool, - /// A string of configuration data that augments the the default configuration - /// passed to the TypeScript compiler. This is typically the contents of a - /// user supplied `tsconfig.json`. - pub maybe_config: Option, -} - #[derive(Debug, Clone, PartialEq)] /// The result of a compilation. pub struct CompilerEmit { diff --git a/compiler/lib.rs b/compiler/lib.rs index cbac50a71242fa..6fcd81781365e8 100644 --- a/compiler/lib.rs +++ b/compiler/lib.rs @@ -35,10 +35,8 @@ use std::result; pub use crate::bundler::bundle; pub use crate::compiler::create_compiler_snapshot; -pub use crate::compiler::CompileOptions; pub use crate::compiler::CompilerIsolate; pub use crate::compiler::Sources; -pub use crate::compiler::TranspileOptions; pub use crate::import_map::ImportMap; pub use crate::import_map::ImportMapError; pub use crate::module_graph::CacheType; @@ -56,6 +54,47 @@ pub use crate::msg::MediaType; type Result = result::Result; +/// A structure which provides options when compiling modules. +#[derive(Default)] +pub struct CompileOptions<'a> { + /// If `true` then files will be transpiled in a way that they can be combined + /// together in a bundle. This will not actually perform the bundling, but + /// just provide individual files back that could be. + pub bundle: bool, + /// If `true` then debug logging will be output from the isolate. + pub debug: bool, + /// A flag to indicate that the compilation should be incremental, and only + /// changed sources will be emitted based on information supplied in the + /// `maybe_build_info` argument. + pub incremental: bool, + /// A vector of libs to be used when type checking the code. For example: + /// + /// ```rust + /// let libs = vec!["deno.ns"]; + /// ``` + /// + pub lib: Vec<&'a str>, + /// A string of configuration data that augments the the default configuration + /// passed to the TypeScript compiler. This is typically the contents of a + /// user supplied `tsconfig.json`. + pub maybe_config: Option, +} + +/// A structure which provides options when transpiling modules. +#[derive(Default)] +pub struct TranspileOptions { + /// If `true` then files will be transpiled in a way that they can be combined + /// together in a bundle. This will not actually perform the bundling, but + /// just provide individual files back that could be. + pub bundle: bool, + /// If `true` then debug logging will be output from the isolate. + pub debug: bool, + /// A string of configuration data that augments the the default configuration + /// passed to the TypeScript compiler. This is typically the contents of a + /// user supplied `tsconfig.json`. + pub maybe_config: Option, +} + #[cfg(test)] pub mod tests { use super::*; diff --git a/compiler/module_graph.rs b/compiler/module_graph.rs index ed1ed05d7bdeab..eef1f5433469f1 100644 --- a/compiler/module_graph.rs +++ b/compiler/module_graph.rs @@ -4,9 +4,7 @@ use crate::ast::parse; use crate::ast::EmitTranspileOptions; use crate::ast::Location; use crate::ast::ParsedModule; -use crate::compiler::CompileOptions; use crate::compiler::CompilerIsolate; -use crate::compiler::TranspileOptions; use crate::config::json_merge; use crate::config::parse_config; use crate::import_map::ImportMap; @@ -14,7 +12,9 @@ use crate::msg::CompilerStats; use crate::msg::EmittedFile; use crate::msg::IgnoredCompilerOptions; use crate::msg::MediaType; +use crate::CompileOptions; use crate::Result; +use crate::TranspileOptions; use deno_core::ModuleSpecifier; use deno_core::Snapshot; @@ -546,9 +546,7 @@ struct TranspileTsOptions { pub struct Graph { build_info: HashMap, handler: Rc>, - /// A hash map of a representation of all the modules within the graph, with - /// a key of the module specifier of that module. - pub modules: HashMap, + modules: HashMap, roots: Vec, } @@ -562,6 +560,20 @@ impl Graph { } } + /// Compile (type check and transform) the graph, updating any emitted modules + /// and build info with the specifier handler. The result contains any + /// performance stats from the compiler and optionally any user provided + /// configuration compiler options that were ignored. + /// + /// # Arguments + /// + /// - `options` - A structure of compiler options which effect how the modules + /// in the graph are compiled. + /// - `maybe_snapshot_data` - A snapshot of a TypeScript compiler isolate, + /// which will be used to compile the modules. Currently, this is required + /// but maybe optional in the future. If not present, the result will + /// error. + /// pub fn compile( self, options: CompileOptions, @@ -587,6 +599,7 @@ impl Graph { maybe_build_info, options, )?; + drop(p); let mut p = provider.borrow_mut(); p.update(emit.written_files, &cache_type)?; p.flush(&cache_type)?; @@ -667,6 +680,20 @@ impl Graph { sources } + /// Transpile (only transform) the graph, updating any emitted modules + /// with the specifier handler. The result contains any performance stats + /// from the compiler and optionally any user provided configuration compiler + /// options that were ignored. + /// + /// # Arguments + /// + /// - `options` - A structure of options which impact how the code is + /// transpiled. + /// - `maybe_shanpshot_data` - If provided, it is expected that the data is + /// a snapshot of the TypeScript compiler, which will in turn be used to + /// perform the transpilation. If none, then the transpilation will be + /// performed by swc. + /// pub fn transpile( mut self, options: TranspileOptions, @@ -753,6 +780,8 @@ impl Graph { } self.flush(&cache_type)?; + // TODO @kitsonk - provide some useful stats + Ok((CompilerStats { items: vec![] }, maybe_ignored_options)) } }