Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SWC plugin #1224

Closed
renchap opened this issue Apr 5, 2022 · 34 comments
Closed

SWC plugin #1224

renchap opened this issue Apr 5, 2022 · 34 comments

Comments

@renchap
Copy link
Contributor

renchap commented Apr 5, 2022

Is your feature request related to a problem? Please describe.
I want to switch our projects from Babel to SWC, a Rust-based JS transpiler.
SWC is used by Next.js unless you opt-out from it because you rely on Babel.

Describe proposed solution
SWC has a brand new plugin system, which should be able to handle the transformations needed by Lingui's macros.

This is not yet stable / well documented, but some plugins are available here: https://github.com/swc-project/plugins (for Styled Components for example).

Those plugins are compiled to WASM, so they can be written in several languages (including AssemblyScript, a Typescript derivative compiling to WASM)

A first step would probably be to continue using Babel for extracting and use this plugin only to transform macros, but in the long term being able to use SWC for extracting could bring a much faster extract and heavily reduce dependencies needed for extraction (swc + swc-lingui and thats pretty much all).

Describe alternatives you've considered

  • Porting Babel macros to a SWC plugin: not sure this can easily be done, this has been discussed a bit in Add SWC Plugin kentcdodds/babel-plugin-macros#144 but this would probably be a new project. Also babel-macros is frozen as the author no longer uses it
  • Using another transpiling tool, like esbuild: there is no support for esbuild + Lingui either (and esbuild does not have a plugin support)
  • Switching away from Lingui: I really like Lingui, I would prefer not :)
  • Keep using Babel: swc (and other modern compilers) are much faster, it would really be better to be able to switch away!

Additional context
Add any other context or screenshots about the feature request here.

@stale
Copy link

stale bot commented Jun 10, 2022

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

@stale stale bot added the wontfix label Jun 10, 2022
@renchap
Copy link
Contributor Author

renchap commented Jun 10, 2022

This issue is getting a lot of 👍, dont close it!

@kopax-polyconseil
Copy link

Best post here, swc, ts-jest, both aren't supported by lingui, and our code base growing, our unit test gets poor performances, and the question to remove lingui become more and more obvious.

This is a must for big projects, otherwise lingui will never stay long in any on them.

@bojanbass
Copy link

Any update on this one?

@semoal
Copy link
Contributor

semoal commented Sep 24, 2022

Nothing yet we can't do right now, there's no way we can replicate the macro's thing on swc (at least as far as I know, not a rust expert here) But of course, any help or info will be highly appreciated :)

@kopax-polyconseil
Copy link

Our project which is used by millions of users now remove lingui translation for nothing, twice time lost. My advice, don't use macro and even more experimental lingui for your app.

@sitole
Copy link

sitole commented Nov 14, 2022

Hi! Is there any progress or any plans for the future?
We have codebase build on Lingui but right now we are upgrading to React 18 + Next 13 and we don't want to stay on Babel.
For now we are okay with @lingui/react syntax but we would like to use lingui extract but its also not possible without Babel.

Is there any chance? 🚀

@semoal
Copy link
Contributor

semoal commented Nov 15, 2022

Yes I'm already moving pieces together thanks to a similar project that implemented Macros on SWC, I'm already working on something suitable for Lingui... I'll open-source it soon.

@shouze
Copy link

shouze commented Nov 16, 2022

@semoal do you mean that you’ve started implementing a plugin in rust for swc?

I can help at least testing it and maybe writing it. I guess that you need writing a visitor, right?

@sitole
Copy link

sitole commented Nov 16, 2022

Iam also ready and happy to test in on our codebase 🔥

@sitole
Copy link

sitole commented Nov 16, 2022

Hey! I found some solution that enables Babel for Lingui export but Next.js app can be still SWC-only.
You can just add custom Babel configuration into .linguirc file and remove your Babel configuration file in app.

More info in documentation (https://lingui.js.org/ref/conf.html#id3)
For me works just Next.js babel configuration:

"extractBabelOptions": {
    "presets": ["next/babel"]
}

@armandabric
Copy link

@sitole Are you using @lingui/macro?

I've tried but I'm getting some error in the @lingui/macro stack

image

@sitole
Copy link

sitole commented Nov 16, 2022

@sitole Are you using @lingui/macro?

I've tried but I'm getting some error in the @lingui/macro stack

image

Yea, this is just for non-macro users. But you can still use <Trans> from @lingui/react also with i18n from @lingui/core. But Sam just debugging possible issues. It's not solution ready for production. 😁

For example with this and Babel configuration i'm not able to export TypeScript translations for example

const propertyName = i18n._(`super-code-in-typescript-code`)

@landsman
Copy link
Contributor

This would help us a lot. Anyway how can I help?

@timofei-iatsenko
Copy link
Collaborator

Well, don't know how it's going by @semoal. I've tried to contact him but no luck. So I started my own development of SWC plugin. It's now on very early stages and it's moving quite slow because i'm not a professional rust developer (but i do have a lot of expertise in writing transforms and working with AST so it helps)

Currently, i have only this tests passing:

test!(
    Default::default(),
    |_| TransformVisitor,
    substitution_in_tpl_literal,
    // input
     r#"
     t`Refresh inbox`
     t`Refresh ${foo} inbox ${bar}`
     t`Refresh ${foo.bar} inbox ${bar}`
     t`Refresh ${expr()}`
     "#,
    // output after transform
    r#"
    i18n._("Refresh inbox", {})
    i18n._("Refresh {foo} inbox {bar}", {foo, bar})
    i18n._("Refresh {0} inbox {bar}", {0: foo.bar, bar})
    i18n._("Refresh {0}", {0: expr()})
    "#
);

test!(
    Default::default(),
    |_| TransformVisitor,
    custom_i18n_passed,
    // input
     r#"
     t(custom_i18n)`Refresh inbox`
     t(custom_i18n)`Refresh ${foo} inbox ${bar}`
     t(custom_i18n)`Refresh ${foo.bar} inbox ${bar}`
     t(custom_i18n)`Refresh ${expr()}`
     "#,
    // output after transform
    r#"
    custom_i18n._("Refresh inbox", {})
    custom_i18n._("Refresh {foo} inbox {bar}", {foo, bar})
    custom_i18n._("Refresh {0} inbox {bar}", {0: foo.bar, bar})
    custom_i18n._("Refresh {0}", {0: expr()})
    "#
);

Which is personally for me is a big WIN because showing potential and that rest is just a matter of time.

If someone has more experience in rust, and would like to help, i'm looking for a rust mentorship or at least code review of what i've written.

@shouze
Copy link

shouze commented Dec 15, 2022

Very nice work @thekip! Do you have open a pull request somewhere, or any versioned source code that you can share? I'm willing to help if I can.

@renchap
Copy link
Contributor Author

renchap commented Dec 15, 2022

This is amazing!

Could you share a repository? I think SWC author / Vercel employees might want to help you on this as this is one of the most wanted plugins for Next.js users

@timofei-iatsenko
Copy link
Collaborator

https://github.com/thekip/swc-lingui-plugin

As mentioned this is not production ready and very far even from 0.0.1 stage.

Future plans:

I'm going to implement a bare minimum of a macro (JSX transformation is a must). Probably not all edge cases or event functionalities would work in that, so only the most useful and important (JSX transformation, plural) and then will ask help from community to implement the rest.

@renchap
Copy link
Contributor Author

renchap commented Dec 15, 2022

I would recommend you to keep a list of things to do in the README, so people can help implementing some of them. Also split in into what you want for a first version, and what is next.

You should also open some issues on topics you are not sure you handled correctly, questions to be answered…

I dont give any promises as I am very busy atm but I will watch the repo and have a look asap!

@timofei-iatsenko
Copy link
Collaborator

@renchap good suggestions. I've added a list of tasks to the repo, and will create couple issues with questions which i don't know how to solve properly.

Meanwhile, i've implemented basic transformation for ICU calls (plurals, select, etc), it's still far from first launch but implementation went much quicker than before.

@timofei-iatsenko
Copy link
Collaborator

Started implementing JSX transformation and extended list of tasks in readme.

Also checked original macro's test suite, well... there so much cases...

The current macro implementation allows the user to use too much different syntax.
From my point of view it's better to keep implementation simple and provide users with opinionated way how to write code instead of implementing all possible ways how user can write the code (the js with jsx is so flexible)

For example, user can write:

<Trans>{'My Text'}</Trans>

Instead of simple:

<Trans>My Text</Trans>

This is supported in current babel macro. From AST point of view this children prop is an expression, not a JSXString, so to support it in Rust implement should have a complicated heuristic to parse what inside expression and should we use it as message or not.

@timofei-iatsenko
Copy link
Collaborator

Latest updates. I've implemented jsx transformations with interpolations and imports replacement, so now this test passes:

  
import { Trans } from "@lingui/macro";
const t = <Trans>
  Hello <strong>World!</strong><br />
<p>
  My name is <a href="/about">{" "}
  <em>{name}</em></a>
</p>
</Trans>

// // ↓ ↓ ↓ ↓ ↓ ↓
import { Trans } from "@lingui/core";
const t = <Trans id={"Hello <0>World!</0><1/><2>My name is <3> <4>{name}</4></3></2>"} values={{
  name: name,
}} components={{
  0: <strong />,
    1: <br />,
    2: <p />,
    3: <a href="/about" />,
    4: <em />
}} />;

It was the hardest part, so rest is should be much simpler.

@timofei-iatsenko
Copy link
Collaborator

For those how are tracking progress on the plugin. I've completely stuck trying to implementing recursive parsing of ICU / Trans expressions in the JSX. I'm stuck implementing usual simple data-structures in Rust's needed to implement this recursive feature.
Rust is very different when it comes to data structures and some popular approaches just don't work, and i could not come how to make it in Rust-way.

By recursive JSX i mean something like this:

<Trans>
  Hello <strong>World!</strong><br />
  You have <Plural value={5} one={"# message"} few={"# messages"}>
</Trans>

@shouze
Copy link

shouze commented Jan 3, 2023

ping @Hywan I know that you master Rust like nobody, maybe can you point @thekip to some solution? 🤔

@timofei-iatsenko
Copy link
Collaborator

I moved on from the dead point. Reorganized code to do stuff in two phases - collecting, then emitting.
This way, rust allowed me to do so. Have no idea how fast implementation would be, i'm more concentrated on greening as many tests from original test suite as possible.

So this

<Trans>
  Hello <strong>World!</strong><br />
  You have <Plural value={5} one={"# message"} few={"# messages"}>
</Trans>

And even this

<Trans>
  Hello <strong>World!</strong><br />
  You have <Plural value={5} one={<Trans># message</Trans>} few={<Trans># messages</Trans>}>
</Trans>

With any level of depth is supported.
Now i'm focused on refactoring t macro to make it work the same way as JSX

@Hywan
Copy link

Hywan commented Jan 5, 2023

Hey :-),

How can I help?

@timofei-iatsenko
Copy link
Collaborator

@Hywan It would be great if you could do a code review and give some advices on https://github.com/thekip/swc-lingui-plugin

I'm new to rust so all suggestions would be appreciated.

@timofei-iatsenko
Copy link
Collaborator

Meanwhile, you guys can review this ticket related to macro

#1324
#1323
#1320

You might have more experience with the lib and prove me wrong.

@timofei-iatsenko
Copy link
Collaborator

Created a first pre-release. Tested a plugin on my mid-size project and it works.
https://github.com/thekip/swc-lingui-plugin/releases/tag/0.0.1

When we agreed on details with @andrii-bodnar i will move the repo under lingui organization and make a proper npm release within @lingui npm scope. As for now, don't want to mess up things and create another trashy package on npm.

@timofei-iatsenko
Copy link
Collaborator

@Martin005 why a "breaking change" label appeared here? This plugin doesn't introduce any breaking changes.

@Martin005
Copy link
Contributor

@thekip Oops, sorry, my mistake 😯

@timofei-iatsenko
Copy link
Collaborator

Second "Pre-release"
https://github.com/thekip/swc-lingui-plugin/releases/tag/0.0.2

Now all features from original macro are implemented. Need to test on real projects. NPM release hope would be soon.

@timofei-iatsenko
Copy link
Collaborator

Released on NPM. Let's test it. If you find an issue please fill an issue on github of that repo with snippet of code which is not transpiled properly.

https://github.com/lingui/swc-plugin/releases/tag/0.1.0

@timofei-iatsenko
Copy link
Collaborator

@Martin005 we can close this issue and continue discussion in the new repo.

@andrii-bodnar andrii-bodnar unpinned this issue Aug 18, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests