vite-plugin-vue
vite-plugin-vue copied to clipboard
Dev server does not transpile script in .vue files (but it works in .ts ones)
Describe the bug
I set my build/esbuild target to something below ES2020 in order to support by target browser (~Safari 13, supports around ES2019, pre-ES2020). Then I can use newer syntax in .ts files just fine, but as soon as I use any new syntax in .vue SFC files (inside of <script lang="ts">, no matter if setup or not), I get errors, because it's not properly transpiled.
One example of such syntax is optional chaining operator, and as such adding a line like
test()?.value || 'bb'
will yield error like following:

Again, it works as expected if I use this inside of any .ts file, but fails inside of .vue.
Reproduction
https://github.com/p0358/vite-vue-transpilation-repro
Steps to reproduce
- Clone my repro repo (see commit to see what changes I had to made to default project, most importantly target)
- pnpm run dev
- Open in your latest browser, see all works
- Open in some older pre-ES2020 browser, see it fails and throws SyntaxError
- Change code in HelloWorld.vue to not include optional chaining anymore (change
?.to.) - See that it now works in old browser too, and code from main.ts will print something into the console now, confirming the issue lies in improper transpilation of .vue files
Example old browser to test in: https://github.com/niutech/qt-ultralight-browser (note this one sadly has no dev tools, I can package a build based on the same browser with dev-tools if you want to test)
System Info
System:
OS: Windows 10 10.0.19045
CPU: (4) x64 Intel(R) Core(TM) i5-6400 CPU @ 2.70GHz
Memory: 530.82 MB / 15.88 GB
Binaries:
Node: 19.1.0 - C:\Program Files\nodejs\node.EXE
Yarn: 1.22.19 - C:\Program Files\nodejs\yarn.CMD
npm: 8.19.3 - C:\Program Files\nodejs\npm.CMD
Browsers:
Edge: Spartan (44.19041.1266.0)
Internet Explorer: 11.0.19041.1566
npmPackages:
@vitejs/plugin-vue: ^3.2.0 => 3.2.0
vite: ^3.2.3 => 3.2.4
Used Package Manager
pnpm
Logs
SyntaxError: Unexpected token '.' — HelloWorld.vue
Validations
- [X] Follow our Code of Conduct
- [X] Read the Contributing Guidelines.
- [X] Read the docs.
- [X] Check that there isn't already an issue that reports the same bug to avoid creating a duplicate.
- [X] Make sure this is a Vite issue and not a framework-specific issue. For example, if it's a Vue SFC related bug, it should likely be reported to vuejs/core instead.
- [X] Check that this is a concrete bug. For Q&A open a GitHub Discussion or join our Discord Chat Server.
- [X] The provided reproduction is a minimal reproducible example of the bug.
Can also be reproduced with this chromium version (has devtools; grab chrome-win.zip): https://commondatastorage.googleapis.com/chromium-browser-snapshots/index.html?prefix=Win_x64/683419/

It's worth noting that I was having issues with transpilation working in dev mode at all, for which I had to explicitly add esbuild.target option like so, in addition to existing build.target:
export default defineConfig({
// [...]
plugins: [vue()],
build: {
target: ["chrome102", "safari13"],
},
esbuild: {
target: ["chrome102", "safari13"]
},
// [...]
});
Because of that I was not sure if issue belongs to vite or this plugin here. These issues might even potentially be unrelated, I mention the above thing just now, because I forgot at that time I even did this, assuming it just made things work.
vite-plugin-vue always transpiles Vue code into esnext, not follows dev server config.
https://github.com/vitejs/vite-plugin-vue/blob/d32cf69c9bef5533cbe61dae538677fcbc8ae637/packages/plugin-vue/src/main.ts#L223-L232
I think you should use the latest browser for developing, But if there are special cases, this is also possible to be a bug.
I think you should use the latest browser for developing, But if there are special cases, this is also possible to be a bug.
My case is a little bit special, because I develop for an embedded browser for a video game, which is a product based on a fixed version of WebKit which they rebase on top of latest version with some considerate delay (current version is based on mid-2019 WebKit, they plan to update to newer one within a couple of months).
With that + the fact this kind of browser has custom JavaScript bindings for communication with the game engine, it'd be hard to test with an outside browser, as even developing mocks would not give the exact same experience behavior. And the thing is, it basically fully works, until it encounters some ES2020+ syntax like here.
vite-plugin-vuealways transpiles Vue code intoesnext, not follows dev server config.
I see, would it be possible to consider changing it? I'm not exactly sure what's the relationship here between the Vue plugin and Vite, but I know setting esbuild.target in Vite config causes it to transpile .ts files in dev server mode, and that seems to work 100% fine. After all ESBuild is pretty fast, and theoretically matching the target option as long as it's post-ES6 shouldn't really have any side effects, right? Maybe the plugin could try to inherit that target option or at least make it manually configurable for the user?
Or perhaps it's a Vite bug that it transpiles .ts files directly there, but not output of plugins?
Vite transpiles .ts code to esbuild.target, but Vue Plugin does not.
Assume esbuild target is es6, when dev for IE11 (only ES6).
Then the code ({ n: 1 })?.n.
-
runs at
main.ts, it is transpiled toes6by Vite. -
error occurs at
HelloWorld.vue, it is also transpiled, but toesnextby Vue Plugin.
If this is seem like a bug. Vue Plugin should follow Vite config.
Vue Plugin use import.meta for HMR. That feature is available >= ES2020.
If retrieved esbuild target from Vite is < ES2020. Plugin should show warning even error message.
I can't provide any approach for your case excepting take patch. It is hard-coded.
@sapphi-red Sorry for pinging. Would you see it as a bug?
When esbuild.target is ES2020+, I think this is a "p3-minor-bug" and "regression". I think we should use esbuild.target if it's defined.
But when esbuild.target is ES2020<, it's not a "regression" because it didn't work in past. IMO it's a "won't fix". Vite heavily relies on import.meta and won't work without that. Maybe you could workaround with a custom plugin.
FYI this part was set by https://github.com/vitejs/vite/pull/10207 to avoid transpiling import.meta into import_meta that breaks HMR.
In my case it seems the browser turns out to not be actually ES2020< (Safari 13), as import.meta is supported (since Safari 11.1), thankfully.
I can confirm that manually editing vue plugin's .js file under node_modules to change esnext to safari13 fixes my issue and everything works 100% properly then.
In such case I think it would be reasonable to just use the esbuild.target setting directly if it's defined. If too old of a target there would break Vite's dev server itself due to lack of import.meta support, then I think it's not a concern of this plugin itself, as the whole HMR system would already be broken in the first place, so nothing would work. So perhaps Vite should have some warning for too old setting there, but the possibility to transpile between that minimum HMR target and latest proved very useful to me, so it would be cool to keep it there, given it works just fine and is optional (as in my use-case I can't really use any other browser than my exact frozen target version).
@p0358 same issue and modify target inside node_modules/vite-plugin-vue fix my problem too. I also agree that specifying a too low target which breaks the bundle is not the concern of this lib. It is user's concern specifying target in the config.esbuild. vite-plugin-vue should respect the target in esbuild regradless of the risk that may break the bundling.

As seen above, browser starting from 2018 already support import.meta, which is much earlier than the release of es2020.
When I used the decorator function of ts, I also encountered a problem
When I used the decorator in the dev environment of vite, an exception occurred, but it was normal in the build environment
So I checked the differences between the dev and build environments
I found that in the build environment, the target of esbuild can be replaced by esbuild:{target}, esbuildTransformOptions in vite.config.ts, while in the dev environment, it is hard-coded, target:esnext, and esbuildTransformOptions does not replace target:esnext
Logic executed when vite:esbuild builds
https://github.com/vitejs/vite/blob/main/packages/vite/src/node/plugins/esbuild.ts (238)
vite-plugin-vue Logic executed in dev environment
https://github.com/vitejs/vite-plugin-vue/blob/main/packages/plugin-vue/src/main.ts (253)
This leads to inconsistency between dev environment and build environment, which I find strange
vite is the basic component of vite-plugin-vue.
In the vite project, when vite-plugin-vue calls the transformWithEsbuild function provided by vite:esbuild, the parameters passed in should be consistent with the parameters of transformWithEsbuild called by vite:esbuild itself when building.
That is, esbuildTransformOptions should be used to replace and merge target:esnext
And this code will only be triggered in the development environment, so options.devServer?.config.esbuild can be used
This way, the results of dev environment and build environment can be consistent
So I submitted a PR https://github.com/vitejs/vite-plugin-vue/pull/444
#444 This fix allows overwrite esbuild config, so it fixes https://github.com/vitejs/vite-plugin-vue/issues/278 , and https://github.com/vitejs/vite-plugin-vue/issues/31 is repeated with 278. Can https://github.com/vitejs/vite-plugin-vue/issues/31 be closed?
https://github.com/vitejs/vite-plugin-vue/issues/278 , in dev mode, whether the configuration when calling esbuild build can be read synchronously from vite.config.js or support parameter passing duplication,
#444此修复允许覆盖 esbuild 配置,因此它修复了#278,并且#31与 278 重复。#31可以关闭吗?
#278,dev 模式下,调用 esbuild build 时的配置是否可以从 vite.config.js 中同步读取或者支持参数传递重复,
The problem is not completely solved.
#444 only fixed lang="ts", the problem still exists with lang="js".
I think #222 is a more thorough fix.