From c02eb25873e62bb9dae7bb782664b5caf1bc3ec9 Mon Sep 17 00:00:00 2001 From: John Doty Date: Sat, 16 Sep 2023 22:48:17 -0700 Subject: [PATCH] [oden] Use deno_ast instead of raw swc for typescript OK, look, I hate SWC and I really don't want to use it if I can help it, but the other options are worse. --- Cargo.lock | 273 +++++++++++++++++++++++---------------- Cargo.toml | 7 +- src/script/typescript.rs | 196 ++++------------------------ 3 files changed, 192 insertions(+), 284 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b0f0a3ff..b62386bc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -61,7 +61,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" dependencies = [ "cfg-if", - "getrandom", "once_cell", "version_check", ] @@ -499,6 +498,59 @@ version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308" +[[package]] +name = "data-url" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41b319d1b62ffbd002e057f36bebd1f42b9f97927c9577461d855f3513c4289f" + +[[package]] +name = "deno_ast" +version = "0.29.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "577ec3850834c2578eb44afa9250f9a807f8497664e6e2aaae19cea0aac2fe3b" +dependencies = [ + "anyhow", + "base64", + "deno_media_type", + "dprint-swc-ext", + "serde", + "swc_atoms", + "swc_common", + "swc_config", + "swc_config_macro", + "swc_ecma_ast", + "swc_ecma_codegen", + "swc_ecma_codegen_macros", + "swc_ecma_loader", + "swc_ecma_parser", + "swc_ecma_transforms_base", + "swc_ecma_transforms_classes", + "swc_ecma_transforms_macros", + "swc_ecma_transforms_proposal", + "swc_ecma_transforms_react", + "swc_ecma_transforms_typescript", + "swc_ecma_utils", + "swc_ecma_visit", + "swc_eq_ignore_macros", + "swc_macros_common", + "swc_visit", + "swc_visit_macros", + "text_lines", + "url", +] + +[[package]] +name = "deno_media_type" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a798670c20308e5770cc0775de821424ff9e85665b602928509c8c70430b3ee0" +dependencies = [ + "data-url", + "serde", + "url", +] + [[package]] name = "digest" version = "0.10.7" @@ -530,6 +582,22 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" +[[package]] +name = "dprint-swc-ext" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a0a2492465344a58a37ae119de59e81fe5a2885f2711c7b5048ef0dfa14ce42" +dependencies = [ + "bumpalo", + "num-bigint", + "rustc-hash", + "swc_atoms", + "swc_common", + "swc_ecma_ast", + "swc_ecma_parser", + "text_lines", +] + [[package]] name = "either" version = "1.8.1" @@ -1044,79 +1112,6 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" -[[package]] -name = "lexical" -version = "6.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7aefb36fd43fef7003334742cbf77b243fcd36418a1d1bdd480d613a67968f6" -dependencies = [ - "lexical-core", -] - -[[package]] -name = "lexical-core" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cde5de06e8d4c2faabc400238f9ae1c74d5412d03a7bd067645ccbc47070e46" -dependencies = [ - "lexical-parse-float", - "lexical-parse-integer", - "lexical-util", - "lexical-write-float", - "lexical-write-integer", -] - -[[package]] -name = "lexical-parse-float" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "683b3a5ebd0130b8fb52ba0bdc718cc56815b6a097e28ae5a6997d0ad17dc05f" -dependencies = [ - "lexical-parse-integer", - "lexical-util", - "static_assertions", -] - -[[package]] -name = "lexical-parse-integer" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d0994485ed0c312f6d965766754ea177d07f9c00c9b82a5ee62ed5b47945ee9" -dependencies = [ - "lexical-util", - "static_assertions", -] - -[[package]] -name = "lexical-util" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5255b9ff16ff898710eb9eb63cb39248ea8a5bb036bea8085b1a767ff6c4e3fc" -dependencies = [ - "static_assertions", -] - -[[package]] -name = "lexical-write-float" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accabaa1c4581f05a3923d1b4cfd124c329352288b7b9da09e766b0668116862" -dependencies = [ - "lexical-util", - "lexical-write-integer", - "static_assertions", -] - -[[package]] -name = "lexical-write-integer" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1b6f3d1f4422866b68192d62f77bc5c700bee84f3069f2469d7bc8c77852446" -dependencies = [ - "lexical-util", - "static_assertions", -] - [[package]] name = "libc" version = "0.2.144" @@ -1207,9 +1202,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.5.0" +version = "2.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +checksum = "8f232d6ef707e1956a43342693d2a31e72989554d58299d7a88738cc95b0d35c" [[package]] name = "memmap2" @@ -1532,6 +1527,7 @@ version = "0.1.0" dependencies = [ "anyhow", "bytemuck", + "deno_ast", "env_logger", "fontdue", "image", @@ -1540,12 +1536,6 @@ dependencies = [ "notify", "oden-js", "pollster", - "swc_common", - "swc_ecma_codegen", - "swc_ecma_parser", - "swc_ecma_transforms_base", - "swc_ecma_transforms_typescript", - "swc_ecma_visit", "tracy-client", "wgpu", "winit", @@ -1629,6 +1619,12 @@ version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" +[[package]] +name = "pathdiff" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" + [[package]] name = "peeking_take_while" version = "0.1.2" @@ -2193,9 +2189,9 @@ dependencies = [ [[package]] name = "swc_atoms" -version = "0.5.6" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93d0307dc4bfd107d49c7528350c372758cfca94fb503629b9a056e6a1572860" +checksum = "9f54563d7dcba626d4acfe14ed12def7ecc28e004debe3ecd2c3ee07cc47e449" dependencies = [ "once_cell", "rustc-hash", @@ -2207,11 +2203,10 @@ dependencies = [ [[package]] name = "swc_common" -version = "0.31.16" +version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6414bd4e553f5638961d39b07075ffd37a3d63176829592f4a5900260d94ca1" +checksum = "39cb7fcd56655c8ae7dcf2344f0be6cbff4d9c7cb401fe3ec8e56e1de8dfe582" dependencies = [ - "ahash 0.8.3", "ast_node", "better_scoped_tls", "cfg-if", @@ -2223,6 +2218,7 @@ dependencies = [ "rustc-hash", "serde", "siphasher", + "sourcemap", "string_cache", "swc_atoms", "swc_eq_ignore_macros", @@ -2259,14 +2255,15 @@ dependencies = [ [[package]] name = "swc_ecma_ast" -version = "0.106.6" +version = "0.109.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebf4d6804b1da4146c4c0359d129e3dd43568d321f69d7953d9abbca4ded76ba" +checksum = "7bc2286cedd688a68f214faa1c19bb5cceab7c9c54d0cbe3273e4c1704e38f69" dependencies = [ "bitflags 2.3.2", "is-macro", "num-bigint", "scoped-tls", + "serde", "string_enum", "swc_atoms", "swc_common", @@ -2275,9 +2272,9 @@ dependencies = [ [[package]] name = "swc_ecma_codegen" -version = "0.141.10" +version = "0.144.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac23ce426fca1f664579a8c4b9b42e47e2cb387bdce55c7fe1121c00dad009ea" +checksum = "8e62ba2c0ed1f119fc1a76542d007f1b2c12854d54dea15f5491363227debe11" dependencies = [ "memchr", "num-bigint", @@ -2306,14 +2303,27 @@ dependencies = [ ] [[package]] -name = "swc_ecma_parser" -version = "0.136.7" +name = "swc_ecma_loader" +version = "0.44.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "411341a6433540222c2a16043934f4398bba7e816059fd814f602e44731ad8c1" +checksum = "e7d7c322462657ae27ac090a2c89f7e456c94416284a2f5ecf66c43a6a3c19d1" +dependencies = [ + "anyhow", + "pathdiff", + "serde", + "swc_common", + "tracing", +] + +[[package]] +name = "swc_ecma_parser" +version = "0.139.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3eab46cb863bc5cd61535464e07e5b74d5f792fa26a27b9f6fd4c8daca9903b7" dependencies = [ "either", - "lexical", "num-bigint", + "num-traits", "serde", "smallvec", "smartstring", @@ -2327,9 +2337,9 @@ dependencies = [ [[package]] name = "swc_ecma_transforms_base" -version = "0.129.13" +version = "0.132.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589da1447e30a46498a99b25d52602fb367fff7e6774fa83cae71829ac935415" +checksum = "01ffd4a8149052bfc1ec1832fcbe04f317846ce635a49ec438df33b06db27d26" dependencies = [ "better_scoped_tls", "bitflags 2.3.2", @@ -2349,10 +2359,24 @@ dependencies = [ ] [[package]] -name = "swc_ecma_transforms_macros" -version = "0.5.2" +name = "swc_ecma_transforms_classes" +version = "0.121.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f59c4b6ed5d78d3ad9fc7c6f8ab4f85bba99573d31d9a2c0a712077a6b45efd2" +checksum = "f4b7fee0e2c6f12456d2aefb2418f2f26529b995945d493e1dce35a5a22584fc" +dependencies = [ + "swc_atoms", + "swc_common", + "swc_ecma_ast", + "swc_ecma_transforms_base", + "swc_ecma_utils", + "swc_ecma_visit", +] + +[[package]] +name = "swc_ecma_transforms_macros" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8188eab297da773836ef5cf2af03ee5cca7a563e1be4b146f8141452c28cc690" dependencies = [ "pmutil", "proc-macro2", @@ -2362,12 +2386,31 @@ dependencies = [ ] [[package]] -name = "swc_ecma_transforms_react" -version = "0.175.17" +name = "swc_ecma_transforms_proposal" +version = "0.166.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b58f795939b4412d3ba94a5299dc6d64834ef0e272098872399637e8ec795833" +checksum = "122fd9a69f464694edefbf9c59106b3c15e5cc8cb8575a97836e4fb79018e98f" +dependencies = [ + "either", + "rustc-hash", + "serde", + "smallvec", + "swc_atoms", + "swc_common", + "swc_ecma_ast", + "swc_ecma_transforms_base", + "swc_ecma_transforms_classes", + "swc_ecma_transforms_macros", + "swc_ecma_utils", + "swc_ecma_visit", +] + +[[package]] +name = "swc_ecma_transforms_react" +version = "0.178.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "675b5c755b0448268830e85e59429095d3423c0ce4a850b209c6f0eeab069f63" dependencies = [ - "ahash 0.8.3", "base64", "dashmap", "indexmap", @@ -2388,9 +2431,9 @@ dependencies = [ [[package]] name = "swc_ecma_transforms_typescript" -version = "0.179.18" +version = "0.182.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "491ac322e85eee53c33b972cb73e5b86327f293ecb5ec6a1bc8b2f5f052bd7a3" +checksum = "4eba97b1ea71739fcf278aedad4677a3cacb52288a3f3566191b70d16a889de6" dependencies = [ "serde", "swc_atoms", @@ -2404,9 +2447,9 @@ dependencies = [ [[package]] name = "swc_ecma_utils" -version = "0.119.9" +version = "0.122.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22e881888b8d5f2c869dd026bb367e0c789385f4d2b39bd0321fead742b1f995" +checksum = "11006a3398ffd4693c4d3b0a1b1a5030edbdc04228159f5301120a6178144708" dependencies = [ "indexmap", "num_cpus", @@ -2422,9 +2465,9 @@ dependencies = [ [[package]] name = "swc_ecma_visit" -version = "0.92.5" +version = "0.95.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f61da6cac0ec3b7e62d367cfbd9e38e078a4601271891ad94f0dac5ff69f839" +checksum = "0f628ec196e76e67892441e14eef2e423a738543d32bffdabfeec20c29582117" dependencies = [ "num-bigint", "swc_atoms", @@ -2513,6 +2556,15 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "text_lines" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fd5828de7deaa782e1dd713006ae96b3bee32d3279b79eb67ecf8072c059bcf" +dependencies = [ + "serde", +] + [[package]] name = "thiserror" version = "1.0.40" @@ -2764,6 +2816,7 @@ dependencies = [ "form_urlencoded", "idna", "percent-encoding", + "serde", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 9c3d0ff3..7d8b2f08 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,6 +11,7 @@ tracing = ["tracy-client/enable"] [dependencies] anyhow = "1.0" bytemuck = { version = "1.13", features = ["derive"] } +deno_ast = { version = "0.29.3", features = ["transpiling", "typescript"] } env_logger = "0.10" fontdue = "0.7.3" image = { version = "0.24", default-features = false, features = ["png"] } @@ -19,12 +20,6 @@ lru = "0.11.0" notify = "6" oden-js = { path = "oden-js" } pollster = "0.3" -swc_common = "0.31.16" -swc_ecma_codegen = "0.141.10" -swc_ecma_parser = "0.136.7" -swc_ecma_transforms_base = "0.129.13" -swc_ecma_transforms_typescript = "0.179.18" -swc_ecma_visit = "0.92.5" tracy-client = { version = "0.15.2", default-features = false } wgpu = "0.17" winit = "0.28" diff --git a/src/script/typescript.rs b/src/script/typescript.rs index 9e94bb35..229da5d5 100644 --- a/src/script/typescript.rs +++ b/src/script/typescript.rs @@ -1,173 +1,33 @@ +use deno_ast::{parse_module, MediaType, ParseParams, SourceTextInfo}; use oden_js::{Error, Result}; -use std::cell::RefCell; -use std::rc::Rc; - -use swc_common::{ - self, comments::SingleThreadedComments, errors::Diagnostic, sync::Lrc, FileName, Globals, Mark, - SourceMap, GLOBALS, -}; -use swc_ecma_codegen::{text_writer::JsWriter, Emitter}; -use swc_ecma_parser::{lexer::Lexer, Parser, StringInput, Syntax}; -use swc_ecma_transforms_base::{fixer::fixer, hygiene::hygiene, resolver}; -use swc_ecma_transforms_typescript::strip; -use swc_ecma_visit::{swc_ecma_ast::EsVersion, FoldWith}; - -struct DiagnosticCollector { - cell: Rc>>, -} - -impl DiagnosticCollector { - pub fn into_handler(self) -> swc_common::errors::Handler { - swc_common::errors::Handler::with_emitter(true, false, Box::new(self)) - } -} - -impl swc_common::errors::Emitter for DiagnosticCollector { - fn emit(&mut self, db: &swc_common::errors::DiagnosticBuilder<'_>) { - use std::ops::Deref; - self.cell.borrow_mut().push(db.deref().clone()); - } -} - -fn is_fatal_swc_diagnostic(diagnostic: &Diagnostic) -> bool { - use swc_common::errors::Level; - match diagnostic.level { - Level::Bug - | Level::Cancelled - | Level::FailureNote - | Level::Fatal - | Level::PhaseFatal - | Level::Error => true, - Level::Help | Level::Note | Level::Warning => false, - } -} - -fn format_swc_diagnostic(source_map: &SourceMap, diagnostic: &Diagnostic) -> String { - if let Some(span) = &diagnostic.span.primary_span() { - let file_name = source_map.span_to_filename(*span); - let loc = source_map.lookup_char_pos(span.lo); - format!( - "{} at {}:{}:{}", - diagnostic.message(), - file_name, - loc.line, - loc.col_display + 1, - ) - } else { - diagnostic.message() - } -} - -fn diagnostics_to_parse_error<'a>( - name: &str, - source_map: &SourceMap, - diagnostics: impl Iterator, -) -> Error { - Error::ParseError( - name.into(), - diagnostics - .map(|d| format_swc_diagnostic(source_map, d)) - .collect::>() - .join("\n\n"), - ) -} - -fn ensure_no_fatal_swc_diagnostics<'a>( - name: &str, - source_map: &SourceMap, - diagnostics: impl Iterator, -) -> Result<()> { - let fatal_diagnostics = diagnostics - .filter(|d| is_fatal_swc_diagnostic(d)) - .collect::>(); - if !fatal_diagnostics.is_empty() { - Err(diagnostics_to_parse_error( - name, - source_map, - fatal_diagnostics.into_iter(), - )) - } else { - Ok(()) - } -} pub fn transpile_to_javascript(path: &str, input: String) -> Result { - // NOTE: This was taken almost verbatim from - // https://github.com/swc-project/swc/blob/main/crates/swc_ecma_transforms_typescript/examples/ts_to_js.rs - // This appears to be similar to what deno_ast does, but this has - // the advantage of actually compiling. :P - // - // NOTE: Really need to figure out how to get this to generate a source - // map for the transpilation or something. - let cm: Lrc = Default::default(); - let diagnostics_cell: Rc>> = Default::default(); - let handler = DiagnosticCollector { - cell: diagnostics_cell.clone(), - } - .into_handler(); - - let fm = cm.new_source_file(FileName::Custom(path.into()), input.into()); - let comments = SingleThreadedComments::default(); - - let lexer = Lexer::new( - Syntax::Typescript(Default::default()), - EsVersion::Es2020, - StringInput::from(&*fm), - Some(&comments), - ); - - let mut parser = Parser::new_from(lexer); - - for e in parser.take_errors() { - e.into_diagnostic(&handler).emit(); - } - - let module = parser - .parse_module() - .map_err(|e| e.into_diagnostic(&handler)) - .map_err(|mut e| { - e.emit(); - let diagnostics = diagnostics_cell.borrow(); - diagnostics_to_parse_error(path, &cm, diagnostics.iter()) - })?; - - let globals = Globals::default(); - GLOBALS.set(&globals, || { - let unresolved_mark = Mark::new(); - let top_level_mark = Mark::new(); - - // Optionally transforms decorators here before the resolver pass - // as it might produce runtime declarations. - - // Conduct identifier scope analysis - let module = module.fold_with(&mut resolver(unresolved_mark, top_level_mark, true)); - - // Remove typescript types - let module = module.fold_with(&mut strip(top_level_mark)); - - // Fix up any identifiers with the same name, but different contexts - let module = module.fold_with(&mut hygiene()); - - // Ensure that we have enough parenthesis. - let module = module.fold_with(&mut fixer(Some(&comments))); - - let mut buf = vec![]; - { - let mut emitter = Emitter { - cfg: swc_ecma_codegen::Config { - minify: false, - ..Default::default() - }, - cm: cm.clone(), - comments: Some(&comments), - wr: JsWriter::new(cm.clone(), "\n", &mut buf, None), - }; - - emitter.emit_module(&module).unwrap(); - } - - let diagnostics = diagnostics_cell.borrow(); - ensure_no_fatal_swc_diagnostics(path, &cm, diagnostics.iter())?; - Ok(String::from_utf8(buf).expect("non-utf8?")) + let text_info = SourceTextInfo::new(input.into()); + let parsed_source = parse_module(ParseParams { + specifier: path.to_string(), + media_type: MediaType::TypeScript, + text_info, + capture_tokens: true, + maybe_syntax: None, + scope_analysis: false, }) + .map_err(|e| { + let position = e.display_position(); + Error::ParseError( + path.into(), + format!( + "{} at {}:{}:{}", + e.message(), + path, + position.line_number, + position.column_number + ), + ) + })?; + + let options: deno_ast::EmitOptions = Default::default(); + let transpiled = parsed_source + .transpile(&options) + .map_err(|e| Error::ParseError(path.into(), e.to_string()))?; + return Ok(transpiled.text); }