From d6daa7565d44b4973ae378064b65c50245a65242 Mon Sep 17 00:00:00 2001 From: Dunqing <29533304+Dunqing@users.noreply.github.com> Date: Mon, 10 Feb 2025 11:57:39 +0000 Subject: [PATCH] feat(napi/transform): support for enabling legacy decorator (#8927) --- napi/transform/index.d.ts | 15 +++++++++++++ napi/transform/src/transformer.rs | 32 +++++++++++++++++++++++---- napi/transform/test/transform.test.ts | 30 +++++++++++++++++++++++++ 3 files changed, 73 insertions(+), 4 deletions(-) diff --git a/napi/transform/index.d.ts b/napi/transform/index.d.ts index e9a56e95124d8..5af85dc024f5c 100644 --- a/napi/transform/index.d.ts +++ b/napi/transform/index.d.ts @@ -20,6 +20,19 @@ export interface CompilerAssumptions { setPublicClassFields?: boolean } +export interface DecoratorOptions { + /** + * Enables experimental support for decorators, which is a version of decorators that predates the TC39 standardization process. + * + * Decorators are a language feature which hasn’t yet been fully ratified into the JavaScript specification. + * This means that the implementation version in TypeScript may differ from the implementation in JavaScript when it it decided by TC39. + * + * @see https://www.typescriptlang.org/tsconfig/#experimentalDecorators + * @default false + */ + legacy?: boolean +} + export interface ErrorLabel { message?: string start: number @@ -270,6 +283,8 @@ export interface TransformOptions { define?: Record /** Inject Plugin */ inject?: Record + /** Decorator plugin */ + decorator?: DecoratorOptions } export interface TransformResult { diff --git a/napi/transform/src/transformer.rs b/napi/transform/src/transformer.rs index 7cf38c39edc1e..4e996fe15df88 100644 --- a/napi/transform/src/transformer.rs +++ b/napi/transform/src/transformer.rs @@ -15,9 +15,8 @@ use oxc::{ diagnostics::OxcDiagnostic, span::SourceType, transformer::{ - DecoratorOptions, EnvOptions, HelperLoaderMode, HelperLoaderOptions, - InjectGlobalVariablesConfig, InjectImport, JsxRuntime, ReplaceGlobalDefinesConfig, - RewriteExtensionsMode, + EnvOptions, HelperLoaderMode, HelperLoaderOptions, InjectGlobalVariablesConfig, + InjectImport, JsxRuntime, ReplaceGlobalDefinesConfig, RewriteExtensionsMode, }, CompilerInterface, }; @@ -134,6 +133,9 @@ pub struct TransformOptions { /// Inject Plugin #[napi(ts_type = "Record")] pub inject: Option>>>, + + /// Decorator plugin + pub decorator: Option, } impl TryFrom for oxc::transformer::TransformOptions { @@ -152,7 +154,10 @@ impl TryFrom for oxc::transformer::TransformOptions { .typescript .map(oxc::transformer::TypeScriptOptions::from) .unwrap_or_default(), - decorator: DecoratorOptions::default(), + decorator: options + .decorator + .map(oxc::transformer::DecoratorOptions::from) + .unwrap_or_default(), jsx: match options.jsx { Some(Either::A(s)) => { if s == "preserve" { @@ -262,6 +267,25 @@ impl From for oxc::transformer::TypeScriptOptions { } } +#[napi(object)] +#[derive(Default)] +pub struct DecoratorOptions { + /// Enables experimental support for decorators, which is a version of decorators that predates the TC39 standardization process. + /// + /// Decorators are a language feature which hasn’t yet been fully ratified into the JavaScript specification. + /// This means that the implementation version in TypeScript may differ from the implementation in JavaScript when it it decided by TC39. + /// + /// @see https://www.typescriptlang.org/tsconfig/#experimentalDecorators + /// @default false + pub legacy: Option, +} + +impl From for oxc::transformer::DecoratorOptions { + fn from(options: DecoratorOptions) -> Self { + oxc::transformer::DecoratorOptions { legacy: options.legacy.unwrap_or_default() } + } +} + /// Configure how TSX and JSX are transformed. /// /// @see {@link https://babeljs.io/docs/babel-plugin-transform-react-jsx#options} diff --git a/napi/transform/test/transform.test.ts b/napi/transform/test/transform.test.ts index 90bf9bc052cd4..132a2b67e6eb1 100644 --- a/napi/transform/test/transform.test.ts +++ b/napi/transform/test/transform.test.ts @@ -244,3 +244,33 @@ describe('inject plugin', () => { expect(ret.code).toEqual('import $inject_Object_assign from "foo";\nlet _ = $inject_Object_assign;\n'); }); }); + +describe('legacy decorator', () => { + it('matches output', () => { + const code = ` + export default @dce class C { + @dce + prop = 0; + method(@dce param) {} + } + `; + const ret = transform('test.tsx', code, { + decorator: { + legacy: true, + }, + }); + expect(ret.code).toMatchInlineSnapshot(` + "import _decorate from "@babel/runtime/helpers/decorate"; + import _decorateParam from "@babel/runtime/helpers/decorateParam"; + let C = class C { + prop = 0; + method(param) {} + }; + _decorate([dce], C.prototype, "prop", void 0); + _decorate([_decorateParam(0, dce)], C.prototype, "method", null); + C = _decorate([dce], C); + export default C; + " + `); + }); +});