mirror of
https://github.com/swc-project/swc.git
synced 2024-12-25 22:56:11 +03:00
feat(bindings/ts): Add transform/strip-only mode (#9138)
**Description:** This PR adds `strip-only`/`transform` mode to `@swc/wasm-typescript`. - Both mode errors on decorator usages. - In `strip-only` mode, `enum` and TypeScript parameter properties are treated as an invalid syntax. - In `transform` mode, those are transpiled.
This commit is contained in:
parent
db5c25309b
commit
a08bb46ebd
@ -13,9 +13,6 @@ bench = false
|
|||||||
crate-type = ["cdylib"]
|
crate-type = ["cdylib"]
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["swc_v1"]
|
|
||||||
swc_v1 = []
|
|
||||||
swc_v2 = []
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0.66"
|
anyhow = "1.0.66"
|
||||||
|
@ -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;
|
||||||
|
"
|
||||||
|
`;
|
@ -6,7 +6,7 @@ it("properly reports error", function () {
|
|||||||
}).toThrow();
|
}).toThrow();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("trannsform", () => {
|
describe("transform", () => {
|
||||||
it("should strip types", async () => {
|
it("should strip types", async () => {
|
||||||
const { code } = await swc.transform(
|
const { code } = await swc.transform(
|
||||||
`
|
`
|
||||||
@ -15,26 +15,36 @@ describe("trannsform", () => {
|
|||||||
`,
|
`,
|
||||||
{}
|
{}
|
||||||
);
|
);
|
||||||
expect(code).toMatchInlineSnapshot(`
|
expect(code).toMatchSnapshot();
|
||||||
"export const foo = 1;
|
|
||||||
"
|
|
||||||
`);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should preserve enum", async () => {
|
describe("in strip-only mode", () => {
|
||||||
const { code } = await swc.transform(
|
it("should throw an error when it encounters an enum", async () => {
|
||||||
`
|
await expect(
|
||||||
enum Foo {
|
swc.transform("enum Foo {}", {
|
||||||
Bar
|
mode: "strip-only",
|
||||||
}
|
})
|
||||||
`,
|
).rejects.toMatchSnapshot();
|
||||||
{}
|
});
|
||||||
);
|
|
||||||
await expect(code).toMatchInlineSnapshot(`
|
it('should throw an error with a descriptive message when it encounters a decorator', async () => {
|
||||||
"enum Foo {
|
await expect(
|
||||||
Bar
|
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();
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"jest": "^25.1.0"
|
"jest": "^29.7.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,11 +2,14 @@ use anyhow::{Context, Error};
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use swc_core::{
|
use swc_core::{
|
||||||
common::{
|
common::{
|
||||||
comments::SingleThreadedComments, errors::ColorConfig, source_map::SourceMapGenConfig,
|
comments::SingleThreadedComments,
|
||||||
sync::Lrc, FileName, Mark, SourceMap, GLOBALS,
|
errors::{ColorConfig, HANDLER},
|
||||||
|
source_map::SourceMapGenConfig,
|
||||||
|
sync::Lrc,
|
||||||
|
FileName, Mark, SourceMap, Spanned, GLOBALS,
|
||||||
},
|
},
|
||||||
ecma::{
|
ecma::{
|
||||||
ast::{EsVersion, Program},
|
ast::{Decorator, EsVersion, Program, TsEnumDecl, TsParamPropParam},
|
||||||
codegen::text_writer::JsWriter,
|
codegen::text_writer::JsWriter,
|
||||||
parser::{
|
parser::{
|
||||||
parse_file_as_module, parse_file_as_program, parse_file_as_script, Syntax, TsSyntax,
|
parse_file_as_module, parse_file_as_program, parse_file_as_script, Syntax, TsSyntax,
|
||||||
@ -18,9 +21,9 @@ use swc_core::{
|
|||||||
hygiene::hygiene,
|
hygiene::hygiene,
|
||||||
resolver,
|
resolver,
|
||||||
},
|
},
|
||||||
typescript::strip_type,
|
typescript::{strip_type, typescript},
|
||||||
},
|
},
|
||||||
visit::VisitMutWith,
|
visit::{Visit, VisitMutWith, VisitWith},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use swc_error_reporters::handler::{try_with_handler, HandlerOpts};
|
use swc_error_reporters::handler::{try_with_handler, HandlerOpts};
|
||||||
@ -46,7 +49,7 @@ pub struct Options {
|
|||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub filename: Option<String>,
|
pub filename: Option<String>,
|
||||||
|
|
||||||
#[serde(default)]
|
#[serde(default = "default_ts_syntax")]
|
||||||
pub parser: TsSyntax,
|
pub parser: TsSyntax,
|
||||||
|
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
@ -55,12 +58,31 @@ pub struct Options {
|
|||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub source_maps: bool,
|
pub source_maps: bool,
|
||||||
|
|
||||||
// #[serde(default)]
|
#[serde(default)]
|
||||||
// pub transform: swc_core::ecma::transforms::typescript::Config,
|
pub mode: Mode,
|
||||||
|
|
||||||
|
#[serde(default)]
|
||||||
|
pub transform: Option<swc_core::ecma::transforms::typescript::Config>,
|
||||||
|
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub codegen: swc_core::ecma::codegen::Config,
|
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)]
|
#[derive(Serialize)]
|
||||||
pub struct TransformOutput {
|
pub struct TransformOutput {
|
||||||
code: String,
|
code: String,
|
||||||
@ -142,7 +164,6 @@ fn operate(input: String, options: Options) -> Result<TransformOutput, Error> {
|
|||||||
|
|
||||||
let unresolved_mark = Mark::new();
|
let unresolved_mark = Mark::new();
|
||||||
let top_level_mark = Mark::new();
|
let top_level_mark = Mark::new();
|
||||||
|
|
||||||
HELPERS.set(&Helpers::new(options.external_helpers), || {
|
HELPERS.set(&Helpers::new(options.external_helpers), || {
|
||||||
// Apply resolver
|
// Apply resolver
|
||||||
|
|
||||||
@ -150,7 +171,19 @@ fn operate(input: String, options: Options) -> Result<TransformOutput, Error> {
|
|||||||
|
|
||||||
// Strip typescript types
|
// 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
|
// Apply external helpers
|
||||||
|
|
||||||
@ -217,3 +250,43 @@ impl SourceMapGenConfig for TsSourceMapGenConfig {
|
|||||||
f.to_string()
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -6327,7 +6327,7 @@ __metadata:
|
|||||||
version: 0.0.0-use.local
|
version: 0.0.0-use.local
|
||||||
resolution: "binding_typescript_wasm-2142ce@workspace:bindings/binding_typescript_wasm"
|
resolution: "binding_typescript_wasm-2142ce@workspace:bindings/binding_typescript_wasm"
|
||||||
dependencies:
|
dependencies:
|
||||||
jest: "npm:^25.1.0"
|
jest: "npm:^29.7.0"
|
||||||
languageName: unknown
|
languageName: unknown
|
||||||
linkType: soft
|
linkType: soft
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user