vite-plugin-react icon indicating copy to clipboard operation
vite-plugin-react copied to clipboard

Support TC39 decorators (TS 5, esbuild 0.21, Vite 5.3)

Open ArnaudBarre opened this issue 2 years ago • 20 comments

Update: Support for TC39 decorators has been shipped in esbuild 0.21, included in Vite 5.3. SWC support is still not fully there, see https://github.com/swc-project/swc/pull/9488

As the goal of this plugin was since the beginning to enforce good practice and be transpiler agnostic (OXC is coming 👀), I'm still waiting before adding support to the plugin.

In the meantime, you can easily patch the library if you want to opt-in into the use of an unstable feature

ArnaudBarre avatar Apr 12 '23 17:04 ArnaudBarre

I'm not sure the referenced issue is going to be closed out however Evan posted this update stating parsing decorators was working: https://github.com/evanw/esbuild/issues/104#issuecomment-1597922979

I have also patched this package with pnpm to supply the decorator version to SWC. Along with using SWC for build, this appears to be working in dev, build, and Vitest.

ProTip avatar Aug 30 '23 22:08 ProTip

I've been using this without issue since posting the prior comment. Any interest in a PR to get this into the plugin?

ProTip avatar Apr 29 '24 21:04 ProTip

Looking into this further, the latest versions of SWC do not require specifying the decorator support version. They have moved to Stage 3 as the default decorator version and have introduced new options to switch back to legacy TS decorators.

So, updating to the latest SWC and adjusting the documentation would be a way forward.

ProTip avatar Apr 29 '24 21:04 ProTip

I was waiting for a stable lowering of the new spec to ship in esbuild to change the behaviour of the plugin, so that people can use them in dev and don't need to bump to much they prod target. That's good news that SWC changed that, I'll take a look and at least add some documentation

Edit: https://github.com/evanw/decorator-tests was updated 2 days ago, I hope Evan will find a implementation that he is ok to ship to esbuild!

ArnaudBarre avatar Apr 29 '24 21:04 ArnaudBarre

update: it's shipped in esbuild

morajabi avatar May 08 '24 10:05 morajabi

Yep I saw that, I'll look at a new API for the plugin once 0.21 is shipped by Vite

ArnaudBarre avatar May 10 '24 16:05 ArnaudBarre

update: it's shipped in esbuild

Hello, I'm not very clear about what swc is responsible for when esbuild is. swc is similar to babel, so syntax downgrade is swc's responsibility. Why does esbuild also have downgrade operations?

vaynevayne avatar Jun 19 '24 09:06 vaynevayne

@vaynevayne I personally use SWC for dev and build, but the default at least a while back was for SWC to only be used for dev and esbuild used for build.

ProTip avatar Jun 20 '24 03:06 ProTip

@vaynevayne esbuild is both a bundler (aggregating multiple JS files into bigger JS outputs) and a transformer (transforming one TS(X) file into JS, while potentially downgrading if enabled). It can do most of what SWC and Babel do, and this is why this is currently the default transformer used by Vite. But because esbuild doesn't support fast refresh, we need to use either babel or SWC for React plugins.

For me it's better to keep SWC usage only in development and let the default vite build use esbuild. This is why syntax downgrading need to be sync between both. While this can be seen as limiting, I actually find that esbuild do a better job at enforcing spec compliance, which avoid creating technical debt int he future.

ArnaudBarre avatar Jun 21 '24 20:06 ArnaudBarre

thanks You made it very clear

vaynevayne avatar Jun 22 '24 14:06 vaynevayne

How to use decorator syntax in JSX? In Vite 5.3

remloyal avatar Aug 29 '24 01:08 remloyal

I have been patching this for over a year via pnpm:

diff --git a/index.mjs b/index.mjs
index 41e1f9f9230aaa2ba6c9eca7f5256e719848a72b..53c20bc6e4b2fa6a2c31bb37ec4627a7b34ae0e9 100644
--- a/index.mjs
+++ b/index.mjs
@@ -172,7 +172,8 @@ var transformWithOptions = async (id, code, target, options, reactConfig) => {
         experimental: { plugins: options.plugins },
         transform: {
           useDefineForClassFields: true,
-          react: reactConfig
+          react: reactConfig,
+          decoratorVersion: "2022-03"
         }
       }
     });

ProTip avatar Aug 29 '24 02:08 ProTip

I have been patching this for over a year via pnpm:

diff --git a/index.mjs b/index.mjs
index 41e1f9f9230aaa2ba6c9eca7f5256e719848a72b..53c20bc6e4b2fa6a2c31bb37ec4627a7b34ae0e9 100644
--- a/index.mjs
+++ b/index.mjs
@@ -172,7 +172,8 @@ var transformWithOptions = async (id, code, target, options, reactConfig) => {
         experimental: { plugins: options.plugins },
         transform: {
           useDefineForClassFields: true,
-          react: reactConfig
+          react: reactConfig,
+          decoratorVersion: "2022-03"
         }
       }
     });

thanks, had to ask chatgpt how to do that in my npm project and found out that npx patch-package is a thing :)

stephen-dahl avatar Aug 29 '24 15:08 stephen-dahl

I just found this -- would be great to have support, as decorators are very nice.

NullVoxPopuli avatar Sep 09 '24 21:09 NullVoxPopuli

While waiting for vite support, I'd like to point out the following answer to another thread as a reference on how to use stage 3 decorators in vite. It is easy to get lost in decorators' issues, I hope this will help somebody.

https://github.com/vitejs/vite-plugin-react-swc/issues/85#issuecomment-2003922124

andeeplus avatar Oct 07 '24 19:10 andeeplus

@andeeplus if you do just that you will loose HMR. Maybe running both with this one first will works but not guarantee. The best way to do this for now is to patch the plugin.

Reminder: The future of Vite is OXC and opting into a still not fully standard decorator transform that is not fully complete is not something we plan to support long term and that's why I don't want to make it one flag away or the default for now.

ArnaudBarre avatar Oct 07 '24 20:10 ArnaudBarre

@ArnaudBarre Thanks for the tips, I've read in several places about the patch but I'm not really sure on what to patch really. I can see an example above but it's not very clear about what to patch. For instance in my current project I'm not using react and if I'm not wrong the patch above is referred to the swc-react plugin and it basically adds decoratorVersion: "2022-03", that is already included in the config I was using.

In any case I was having weird behaviours with the solution I posted above and checking the test suite in the issue that you post in your latest comment I've realized why: the swc transformation unfortunately is not very accurate.

So I took a more naive approach and for my use case the following is more than enough:

import { defineConfig } from 'vite';

export default defineConfig({
  esbuild: {
    target: 'es2022',
  },
});

Thanks for your time.

andeeplus avatar Oct 09 '24 21:10 andeeplus

@andeeplus Thanks! I tried this with latest version and It needs to add parserConfig in addition to https://github.com/vitejs/vite-plugin-react-swc/issues/85#issuecomment-2003922124 .

vite.config.ts

import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react-swc'
import swc from "unplugin-swc";

// https://vite.dev/config/
export default defineConfig({
  server: { open: true },
  plugins: [
    react({
      parserConfig(id: string) {
        if (id.endsWith(".ts")) return { syntax: "typescript", tsx: false, decorators: true };    
      }
    }),
    swc.vite({
      jsc: {
        parser: {
          syntax: "typescript",
          decorators: true,
        },
        transform: {
          decoratorMetadata: true,
          decoratorVersion: "2022-03",
          react: {
            runtime: "automatic",
          },
        },
      },
    }),
  ],
})

package.json

  "devDependencies": {
    "@vitejs/plugin-react-swc": "^3.7.2",
    "typescript": "^5.7.2",
    "unplugin-swc": "^1.5.1",
    "vite": "^6.0.6"
  }

no experimentalDecorators in tsconfig.

takawitter avatar Jan 02 '25 02:01 takawitter

Full support for downgrading TC39 decorators is still ongoing but in the meantime, to allow more people to enable various SWC specific things, I added in v3.8 an option called useAtYourOwnRisk_mutateSwcOptions. See https://github.com/vitejs/vite-plugin-react-swc/releases/tag/v3.8.0

ArnaudBarre avatar Feb 08 '25 23:02 ArnaudBarre

Our project has two packages: one exports a library built with lit-element, the other is a React website. We're using npm link for faster development.

The library disables useDefineForClassFields in tsconfig and relies on experimental decorators.

The problem is, @vitejs/plugin-react-swc forces useDefineForClassFields to true to align with the current spec, which breaks the link setup.

If you're running into the same problem using lit-element, @ArnaudBarre’s useAtYourOwnRisk_mutateSwcOptions provides a workaround:

plugins: [
  react({
    tsDecorators: true,
    useAtYourOwnRisk_mutateSwcOptions: (options) => {
      options.jsc.transform.useDefineForClassFields = false;
    },
  })
]

zero41120 avatar Jun 27 '25 00:06 zero41120