mirror of
https://github.com/swc-project/swc.git
synced 2024-12-25 06:36:08 +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"]
|
||||
|
||||
[features]
|
||||
default = ["swc_v1"]
|
||||
swc_v1 = []
|
||||
swc_v2 = []
|
||||
|
||||
[dependencies]
|
||||
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();
|
||||
});
|
||||
|
||||
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();
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
});
|
||||
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"devDependencies": {
|
||||
"jest": "^25.1.0"
|
||||
"jest": "^29.7.0"
|
||||
}
|
||||
}
|
||||
|
@ -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<String>,
|
||||
|
||||
#[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<swc_core::ecma::transforms::typescript::Config>,
|
||||
|
||||
#[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<TransformOutput, Error> {
|
||||
|
||||
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<TransformOutput, Error> {
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user