I’m a long time user of Browserslist and Vite and I was pretty convinced they paired very-well. It seems to be the case for both CSS and JavaScript output, but it actually is not for JavaScript.
The issue
Weirdly, it took me ages to notice the JavaScript transformations done by Vite (using ESBuild under the hood) always produce the same result, no matter your Browserslist configuration. Whoops!
In reality, the issue is that ESBuild doesn’t support Browserslist and uses its own fine-grained target
system with a different syntax.
Knowing this, we are left with two options to honor our browsers target: maintaining a second list with a compliant ESBuild syntax (passed through Vite build.target
), or use the following fix.
The fix in two steps
Fortunately, there’s the browserslist-to-esbuild
package (by Marco Fugaro) that is able to properly pass the content of a browserslist
file to ESBuild.
The steps described here can also be visualized in a commit of mine.
- First, add
browserslist-to-esbuild
to your project (tested version: 1.2.0):
npm i -D browserslist-to-esbuild
- Then, in your
vite.config.js
file:
import { defineConfig } from 'vite'
import browserslistToEsbuild from 'browserslist-to-esbuild'
export default defineConfig({
build: {
target: browserslistToEsbuild(),
},
})
That’s it! But wait, there’s more…
(Note: another package can do this, but I have not tested it.)
Unsupported JavaScript features
Now that ESBuild properly receives a browserslist
config, maybe it will throw an error on compilation if you are trying to use a JavaScript feature unsupported by some of the browsers you target. In my case, I was trying to use top-level await
, which is available starting Safari 15 while I want to support Safari starting 12.2 (iOS) or 13.1 (macOS).
ERROR [vite:esbuild-transpile] Transform failed with 1 error: 18:07:46
js/helpers/TopLevelAwait.js:1:0: ERROR: Top-level await is not available in the configured target environment ("chrome100", "edge102", "firefox91", "ios12.2", "safari13.1" + 2 overrides)
I was well aware that some of my target browsers don’t support this feature: I’m actually using top-level await
to detect another browser feature, so what I want is top-level await
to remain in my code and crash in some browsers. This way, my app knows which workaround to use when the feature is not detected.
Well, we are lucky again, because ESBuild allows to receive a list of “supported” features that will be prioritized over the compilation target coming from browserslist
. All we have to do in our Vite configuration is to use the esbuild
config entry to set supported['top-level-await']
to true
:
export default defineConfig({
esbuild: {
/**
* Prevents ESBuild to throw when using a feature not supported by the
* list of supported browsers coming from the `browserslist` file.
*/
supported: {
'top-level-await': true,
},
},
}
Wit this, we can compile without error, and the JavaScript now preserves await
at top-level.
With this knowledge in mind, I think it’s safe to say that Vite is only one step away to claim full Browserslist support, and that step is probably that Vite could use browserslist-to-esbuild
to make everyone’s life easier.
More on Vite and Browserslist
Actually, Vite doesn’t support Browserslist at all: the only reason it “seems” to work well with CSS is because Vite uses PostCSS, which itself natively uses Browserslist. We could say that Vite supports Browserslist by proxy, for CSS. I’m not the only one who have been confused by this situation.
On the other hand, if your project uses the @vitejs/plugin-legacy
, an official Vite plugin helping you supporting browsers outside of Vite browser compatibility list, then Vite will make use of your Browserslist configuration because this plugin uses Babel @babel/preset-env
instead of ESBuild, and this Babel plugin uses your Browserslist configuration out of the box in order to define the list of JavaScript transformations that Babel will apply.
As @vitejs/plugin-legacy
has explicit browser target documentation and even a ignorebrowserslistconfig
option, I’m gonna end by quoting myself with a sentence from the previous section:
Vite is only one step away to claim full Browserslist support, and that step is probably that Vite could use of
browserslist-to-esbuild
to make everyone’s life easier.
Or, for now, two steps away: as mentioned by @kingyue, there’s currently a bug in (Fixed in @vitejs/plugin-legacy
(and a fix in review !), causing the Browserslist configuration to be ignored.@vitejs/plugin-legacy
4.0.1.)
Top comments (4)
@vitejs/plugin-legacy
does not use browserslistrc currently: github.com/vitejs/vite/issues/2476This has been fixed in
@vitejs/plugin-legacy
4.0.1!I just tested in a project using
@vitejs/plugin-legacy
, and indeed the output remains the same no matter the Browserslist configuration. I’ve added a note at the end of the article. Thanks a lot!@iskin already opened a follow-up issue in Vite repository. ❤️