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

Leverage SWC parser to transpile JSX and TypeScript #5210

Open
curran opened this issue Oct 17, 2023 · 6 comments
Open

Leverage SWC parser to transpile JSX and TypeScript #5210

curran opened this issue Oct 17, 2023 · 6 comments

Comments

@curran
Copy link
Contributor

curran commented Oct 17, 2023

Feature Use Case

As a developer building an in-browser IDE using @rollup/browser, I want to enable my users to author .jsx, .ts, and .tsx files while also minimizing bundle size (of the in-browser IDE app itself, not the user-generated content).

Feature Proposal

There exists at present several Rollup plugins that transpile JSX and strip out TypeScript, such as @rollup/plugin-babel and @rollup/plugin-sucrase, but these are not designed to run in the browser and, and if they could run in the browser would introduce substantial overhead.

The feature proposal is to leverage the new WASM SWC build introduced in #5073 to transpile JSX and strip out TypeScript. This should, in theory, be possible because SWC provides options to perform these transformations as part of its API. See https://swc.rs/docs/configuration/compilation

It may be possible to implement this feature by allowing Rollup JavaScript API users to specify custom overrides for the SWC parser options, specifically for TypeScript (jsc.parser.syntax = "typescript") and JSX (jsc.parser.tsx = true).

This would allow Rollup users to transpile TypeScript and JSX without relying on additional third party Rollup plugins.

Might this be feasible?

@lukastaegert
Copy link
Member

That is indeed something I see on the roadmap. There could even be two different modes of operation for each: "transpile" and "preserve". The problem is only that a lot of work is involved to make this possible. "preserve" is probably a little easier, as we would "only" need to define the corresponding AST nodes in Rollup, write the encoder/decoders for those nodes and think how scoping etc. works. For "transpile", we would need to do the same, but we would also need to handle rendering. For TypeScript, this mostly means removing types, but when we would also need to start thinking about output compatibility targets, and then it gets really hairy, e.g. do we want to start replacing classes with other constructs like tsc would?
So there is a lot to think about.

@lukastaegert
Copy link
Member

Of course, "preserve" could also become a killer feature for TypeScript, as we could basically bundle TypeScript to TypeScript, not sure if there are many other tools that support that...

@curran
Copy link
Contributor Author

curran commented Oct 17, 2023

Amazing! I'm glad to hear that that I haven't totally lost my mind and the idea makes sense at a high level.

Related: ESBuild can also run in the browser thanks to WASM, since May evanw/esbuild#797 (comment)

I think in the mean time I may try to craft a custom Rollup plugin that invokes the ESBuild WASM build and uses that for transpilation of TS and JSX. Then the app will unfortunately be pulling in two relatively large WASM assets, one for Rollup's SWC parser and another the ESBuild. At least this is the path forward that I see now - there may be better options.

a lot of work is involved to make this possible

I'd be curious to map out this space of work and contribute where I can. Here's a rough first pass at structuring the work:

  • Modes of Operation:

    • Transpile
    • Preserve
  • Work Items for Preserve Mode:

    • Define the corresponding AST nodes in Rollup (and identify what specific kinds of new nodes are required)
    • Write the encoder/decoders for those nodes.
    • Determine how scoping and other related aspects will work.
  • Work Items for Transpile Mode:

    • Define the corresponding AST nodes in Rollup (similar to "preserve").
    • Write the encoder/decoders for those nodes (similar to "preserve").
    • Handle rendering.
      • For TypeScript, this primarily involves removing types.
      • Consider output compatibility targets.
        • Address challenges such as deciding if we should replace classes with other constructs similar to how tsc would.
  • Additional Considerations:

    • The "preserve" mode could become a standout feature for TypeScript.
      • Potential to bundle TypeScript to TypeScript.
      • Analyze the uniqueness of this feature compared to other tools in the market.

@lukastaegert
Copy link
Member

I guess the first thing to do is a theoretical question: What should a TypeScript/JSX AST look like? We currently go to great lengths to ensure the AST in Rollup (that is exposed via this.parse and in the moduleParsed hook to plugins) conforms to the ESTree spec. Adding TypeScript or JSX would need to extend this, but there is no "official spec". Looking at this discussion for Babel, it might be that typescript-eslint are close to what I would imagine here and we could follow their spec.

Then there is one thing that we could do as a first step for both the "preserve" and "transpile" efforts which would be to support TypeScript and JSX only in this.parse:

  • add additional typescript and jsx boolean options to this.parse that are forwarded to swc to enable the corresponding syntax
  • extend the binary encoders to support the new nodes, making sure we do any transformations necessary to conform to the final AST structure. From previous experience, SWC does not really follow e.g. the ESTree spec, which meant we needed to merge or split some nodes in the converters or derive additional attributes.
  • extend the JavaScript decoders in the same way to construct the JavaScript AST nodes

Once that is done, we have a usable new feature that we can release while based on that, we can then progress into one or the other direction.

@curran
Copy link
Contributor Author

curran commented Oct 30, 2023

Here's another idea, which would be lighter weight than augmenting the AST while still fulfilling the requirements I'm looking for:

  • What if [@rollup/plugin-swc](https://github.com/rollup/plugins/tree/master/packages/swc#rollupplugin-swc) is modified such that it can work in the browser?
  • Ideally the existing WASM asset from @rollup/browser could expose enough stuff from SWC to support a browser-compatible variant of @rollup/plugin-swc.
  • That way the Rollup AST remains ESTree-compliant, and also my app can just serve a single large WASM asset to support both module bundling (via @rollup/browser) AND transpilation (via @rollup/plugin-swc or maybe a new thing @rollup/plugin-swc-browser.

Would that make sense?

@lukastaegert
Copy link
Member

That plugin just uses the opaque @swc/core JavaScript package. There are no synergies with how Rollup uses swc at the moment. One could theoretically re-implement @swc/core to merge the binaries with the Rollup binaries, but that would also massively blow up Rollup by maybe a factor of five. Currently, Rollup only leverages a hand-picked subset of SWC. That being said, I think it should be possible to create a browser version of that plugin by just replacing @swc/core with @swc/wasm, maybe that is what you are looking for?

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

2 participants