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

Vue.js support #75

Closed
loeffel-io opened this issue Apr 21, 2020 · 15 comments
Closed

Vue.js support #75

loeffel-io opened this issue Apr 21, 2020 · 15 comments

Comments

@loeffel-io
Copy link

Hey @evanw,

thank you for your package! ❤️

Is the Vue.js support still not planed #27? (in case of the react support)

@tooolbox
Copy link
Contributor

If you use Vue with JSX you could probably use custom jsx-factory and jsx-fragment settings.

@loeffel-io
Copy link
Author

Sadly i do not use Vue with JSX :/

@jacobdeichert
Copy link

Evan mentioned in #27:

Since Vue has a custom syntax and compiler specific to their project, I consider replicating it inside esbuild to be out of scope for the goals of this project. This is the same reason that esbuild won't support Svelte

Even though I use svelte and want to use esbuild, it makes sense to me that Evan wants to create a bundler focused on a smaller scope - it's exactly what I would be doing too. I've been thinking a lot about bundlers lately and we really do need a fresh take like this. Rewriting the vue or svelte compiler really just doesn't belong in this project.

Though, if esbuild has plugin support one day (or at least file pipelining hooks), I can see someone being able to call the vue cli or a svelte cli compiler on each file before esbuild starts the bundling process. And it's possible there's members of the community that will try to recreate the vue/svelte compilers in golang, rust, whatever to improve performance even further :)

I created svelvet and once esbuild supports generating esm bundle outputs (#48) I might even pivot svelvet to use esbuild instead of snowpack.

@deanishe
Copy link

if esbuild has plugin support one day (or at least file pipelining hooks)

Couldn't it be used as a library by a Vue/Svelte compiler?

@jacobdeichert
Copy link

Exactly! That's my point here:

I might even pivot svelvet to use esbuild instead of snowpack.

Though, file pipelining hooks in esbuild could be a really cool way to do plugin support without requiring plugins to be written in a certain language. Just calling other cli tools at certain steps in the bundling process would allow precompilation of js and other assets possibly, before esbuild finishes the bundle.

@yyx990803
Copy link
Contributor

Vue SFC/Template to JavaScript compilation is a completely different process from TS/ES syntax conversions or minifications. Vue's template syntax and SFC formats are framework proprietary and subject to version changes - unlike ES syntax which is a standard and a much slower moving target (TS changes happen mostly at the type checker level and syntax is also relatively stable).

More importantly, the way Vue templates are compiled need coordination with Vue's runtime to ensure correct runtime behavior, so it really doesn't make sense for esbuild to cover that.

What I do think esbuild can benefit Vue, and other JS projects in general, is exposing a way to consume esbuild from JavaScript without the overhead of starting a child process and reading/writing to disk for every file transform. An API that starts a long-living child process that can receive/send data from the Node.js main process. This allows other bundlers or dev systems (e.g. vite) to leverage esbuild as the underlying sub bundler / TS/JSX transformer / minifier much more efficiently.

The reason for this is because while esbuild is amazing, it's a black box that cannot interop with the JS ecosystem, and the bundling process doesn't allow pluggable transforms which is indispensable for building actual applications. My ideal system would be using Rollup as the bundler but esbuild for actual TS transforms and final minification.

@evanw
Copy link
Owner

evanw commented May 5, 2020

Well said!

without the overhead of starting a child process and reading/writing to disk for every file transform

I'm intending for esbuild to be a batch process of sorts. That's why it allows you to pass more than one file on the command line, so that many files can all be processed in one go. Is this not something that works for other bundlers or dev systems such as vite?

An API that starts a long-living child process that can receive/send data from the Node.js main process.

I've been thinking a lot about a streaming protocol over stdin/stdout that would let you feed esbuild commands, and then esbuild would send the results back to you without touching disk. Would that be sufficient for this?

@yyx990803
Copy link
Contributor

yyx990803 commented May 5, 2020

I'm intending for esbuild to be a batch process of sorts. That's why it allows you to pass more than one file on the command line, so that many files can all be processed in one go. Is this not something that works for other bundlers or dev systems such as vite?

vite is a dev server, and it compiles source files on-demand when the browser sends in HTTP request for native ES module imports. So by nature it compiles every file in isolation without the possibility for batching.

I've been thinking a lot about a streaming protocol over stdin/stdout that would let you feed esbuild commands, and then esbuild would send the results back to you without touching disk. Would that be sufficient for this?

The limitation of that is you have to feed the esbuild process everything in one go - and as mentioned above, vite's model is by nature at odds with that. Even in the context of a bundler like Rollup, the transform for each source file is also isolated (every transform call can be async). So I imagine it would still incur the cost of a new child process on every transform.

This is the API I imagine would be useful:

const { startCompilerProcess } = require('esbuild')

;(async () => {
  const compiler = await startCompilerProcess()
  
  // under the hood, sends the buffer to esbuild for processing and sends back processed buffer
  // this can be called as many times as necessary without starting new child processes
  const compiled = await compiler.compile(srcBuffer, options).toString()

  compiler.dispose() // explicitly stop the child process (or auto exit on main process exit)
})()

I'm not very familiar with Go, but I imagine it should be a thin extra layer that doesn't affect how esbuild core works.

@CyberAP
Copy link

CyberAP commented May 5, 2020

Can child_process.spawn and stdin\stdout support resolve that issue without an additional API surface on esbuild side? Would it create a new esbuild process on every call in that case?

@yyx990803
Copy link
Contributor

@CyberAP I think the current binary just terminates itself when the processing is complete.

@praneetloke
Copy link

What I do think esbuild can benefit Vue, and other JS projects in general, is exposing a way to consume esbuild from JavaScript without the overhead of starting a child process and reading/writing to disk for every file transform. An API that starts a long-living child process that can receive/send data from the Node.js main process. This allows other bundlers or dev systems (e.g. vite) to leverage esbuild as the underlying sub bundler / TS/JSX transformer / minifier much more efficiently.

This, to me, sounds like having an RPC server on the esbuild side and having js clients open a persistent connection to it. To do that, esbuild should first implement an API-based approach which I guess is not how it is designed right now.

@evanw
Copy link
Owner

evanw commented May 5, 2020

@yyx990803 I just added an API similar to what you proposed:

(async () => {
  const jsx = `
    import * as React from 'react'
    import * as ReactDOM from 'react-dom'

    ReactDOM.render(
      <h1>Hello, world!</h1>,
      document.getElementById('root')
    );
  `

  // Start the esbuild child process once
  const esbuild = require('esbuild')
  const service = await esbuild.startService()

  // This can be called many times without the overhead of starting a service
  const { js } = await service.transform(jsx, { loader: 'jsx' })
  console.log(js)

  // The child process can be explicitly killed when it's no longer needed
  service.stop()
})()

It should now be possible to make use of esbuild's single-file transform ability from JavaScript as a library without the overhead of process creation. The complete API is documented in the TypeScript type definitions.

@yyx990803
Copy link
Contributor

@evanw fantastic! Thank you so much for the fast implementation.

@evanw
Copy link
Owner

evanw commented May 7, 2020

Was this API sufficient to address this issue on the esbuild side? I assume there's still more work to fully integrate this API into the Vue ecosystem, but I don't think this issue is the appropriate place to track that work.

@yyx990803
Copy link
Contributor

yyx990803 commented May 7, 2020 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

8 participants