diff --git a/bindings/binding_typescript_wasm/Cargo.toml b/bindings/binding_typescript_wasm/Cargo.toml index f32c52feb06e..af04a79923d4 100644 --- a/bindings/binding_typescript_wasm/Cargo.toml +++ b/bindings/binding_typescript_wasm/Cargo.toml @@ -13,9 +13,6 @@ bench = false crate-type = ["cdylib"] [features] -default = ["swc_v1"] -swc_v1 = [] -swc_v2 = [] [dependencies] anyhow = "1.0.66" diff --git a/bindings/binding_typescript_wasm/__tests__/__snapshots__/transform.js.snap b/bindings/binding_typescript_wasm/__tests__/__snapshots__/transform.js.snap new file mode 100644 index 000000000000..532010e65168 --- /dev/null +++ b/bindings/binding_typescript_wasm/__tests__/__snapshots__/transform.js.snap @@ -0,0 +1,32 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`transform in strip-only mode should throw an error when it encounters an enum 1`] = ` +" + x TypeScript enum is not supported in strip-only mode + ,---- + 1 | enum Foo {} + : ^^^^^^^^^^^ + \`---- +" +`; + +exports[`transform in strip-only mode should throw an error with a descriptive message when it encounters a decorator 1`] = ` +" + x Decorators are not supported + ,---- + 1 | class Foo { @decorator foo() {} } + : ^^^^^^^^^^ + \`---- +" +`; + +exports[`transform in transform mode should transpile enum 1`] = ` +"var Foo; +(function(Foo) {})(Foo || (Foo = {})); +" +`; + +exports[`transform should strip types 1`] = ` +"export const foo = 1; +" +`; diff --git a/bindings/binding_typescript_wasm/__tests__/transform.js b/bindings/binding_typescript_wasm/__tests__/transform.js index a20de8582026..7cf8e6675ce8 100644 --- a/bindings/binding_typescript_wasm/__tests__/transform.js +++ b/bindings/binding_typescript_wasm/__tests__/transform.js @@ -6,7 +6,7 @@ it("properly reports error", function () { }).toThrow(); }); -describe("trannsform", () => { +describe("transform", () => { it("should strip types", async () => { const { code } = await swc.transform( ` @@ -15,26 +15,36 @@ describe("trannsform", () => { `, {} ); - expect(code).toMatchInlineSnapshot(` - "export const foo = 1; - " - `); + expect(code).toMatchSnapshot(); }); - it("should preserve enum", async () => { - const { code } = await swc.transform( - ` - enum Foo { - Bar - } - `, - {} - ); - await expect(code).toMatchInlineSnapshot(` - "enum Foo { - Bar - } - " - `); + describe("in strip-only mode", () => { + it("should throw an error when it encounters an enum", async () => { + await expect( + swc.transform("enum Foo {}", { + mode: "strip-only", + }) + ).rejects.toMatchSnapshot(); + }); + + it('should throw an error with a descriptive message when it encounters a decorator', async () => { + await expect( + swc.transform("class Foo { @decorator foo() {} }", { + mode: "strip-only", + }) + ).rejects.toMatchSnapshot(); + }) + }); + + describe("in transform mode", () => { + it("should transpile enum", async () => { + const { code } = await swc.transform("enum Foo {}", { + mode: "transform", + }); + + expect(code).toMatchSnapshot(); + }); + + }); }); diff --git a/bindings/binding_typescript_wasm/package.json b/bindings/binding_typescript_wasm/package.json index 62046512ab73..c6fc18a2397e 100644 --- a/bindings/binding_typescript_wasm/package.json +++ b/bindings/binding_typescript_wasm/package.json @@ -1,5 +1,5 @@ { "devDependencies": { - "jest": "^25.1.0" + "jest": "^29.7.0" } } diff --git a/bindings/binding_typescript_wasm/src/lib.rs b/bindings/binding_typescript_wasm/src/lib.rs index f580a9e44451..e84f69d86fa2 100644 --- a/bindings/binding_typescript_wasm/src/lib.rs +++ b/bindings/binding_typescript_wasm/src/lib.rs @@ -2,11 +2,14 @@ use anyhow::{Context, Error}; use serde::{Deserialize, Serialize}; use swc_core::{ common::{ - comments::SingleThreadedComments, errors::ColorConfig, source_map::SourceMapGenConfig, - sync::Lrc, FileName, Mark, SourceMap, GLOBALS, + comments::SingleThreadedComments, + errors::{ColorConfig, HANDLER}, + source_map::SourceMapGenConfig, + sync::Lrc, + FileName, Mark, SourceMap, Spanned, GLOBALS, }, ecma::{ - ast::{EsVersion, Program}, + ast::{Decorator, EsVersion, Program, TsEnumDecl, TsParamPropParam}, codegen::text_writer::JsWriter, parser::{ parse_file_as_module, parse_file_as_program, parse_file_as_script, Syntax, TsSyntax, @@ -18,9 +21,9 @@ use swc_core::{ hygiene::hygiene, resolver, }, - typescript::strip_type, + typescript::{strip_type, typescript}, }, - visit::VisitMutWith, + visit::{Visit, VisitMutWith, VisitWith}, }, }; use swc_error_reporters::handler::{try_with_handler, HandlerOpts}; @@ -46,7 +49,7 @@ pub struct Options { #[serde(default)] pub filename: Option, - #[serde(default)] + #[serde(default = "default_ts_syntax")] pub parser: TsSyntax, #[serde(default)] @@ -55,12 +58,31 @@ pub struct Options { #[serde(default)] pub source_maps: bool, - // #[serde(default)] - // pub transform: swc_core::ecma::transforms::typescript::Config, + #[serde(default)] + pub mode: Mode, + + #[serde(default)] + pub transform: Option, + #[serde(default)] pub codegen: swc_core::ecma::codegen::Config, } +fn default_ts_syntax() -> TsSyntax { + TsSyntax { + decorators: true, + ..Default::default() + } +} + +#[derive(Clone, Copy, Default, Deserialize)] +#[serde(rename_all = "kebab-case")] +pub enum Mode { + #[default] + StripOnly, + Transform, +} + #[derive(Serialize)] pub struct TransformOutput { code: String, @@ -142,7 +164,6 @@ fn operate(input: String, options: Options) -> Result { let unresolved_mark = Mark::new(); let top_level_mark = Mark::new(); - HELPERS.set(&Helpers::new(options.external_helpers), || { // Apply resolver @@ -150,7 +171,19 @@ fn operate(input: String, options: Options) -> Result { // Strip typescript types - program.visit_mut_with(&mut strip_type()); + program.visit_with(&mut Validator { mode: options.mode }); + + match options.mode { + Mode::StripOnly => { + program.visit_mut_with(&mut strip_type()); + } + Mode::Transform => { + program.visit_mut_with(&mut typescript( + options.transform.unwrap_or_default(), + top_level_mark, + )); + } + } // Apply external helpers @@ -217,3 +250,43 @@ impl SourceMapGenConfig for TsSourceMapGenConfig { f.to_string() } } + +struct Validator { + mode: Mode, +} + +impl Visit for Validator { + fn visit_decorator(&mut self, n: &Decorator) { + HANDLER.with(|handler| { + handler.span_err(n.span, "Decorators are not supported"); + }); + } + + fn visit_ts_enum_decl(&mut self, e: &TsEnumDecl) { + if matches!(self.mode, Mode::StripOnly) { + HANDLER.with(|handler| { + handler.span_err( + e.span, + "TypeScript enum is not supported in strip-only mode", + ); + }); + return; + } + + e.visit_children_with(self); + } + + fn visit_ts_param_prop_param(&mut self, n: &TsParamPropParam) { + if matches!(self.mode, Mode::StripOnly) { + HANDLER.with(|handler| { + handler.span_err( + n.span(), + "TypeScript parameter property is not supported in strip-only mode", + ); + }); + return; + } + + n.visit_children_with(self); + } +} diff --git a/yarn.lock b/yarn.lock index b577206f2f1d..5aca9fc529a7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6327,7 +6327,7 @@ __metadata: version: 0.0.0-use.local resolution: "binding_typescript_wasm-2142ce@workspace:bindings/binding_typescript_wasm" dependencies: - jest: "npm:^25.1.0" + jest: "npm:^29.7.0" languageName: unknown linkType: soft