[bug] WebSocket HMR not working as expected with Nuxt
Describe the bug
The methods described in this document does not mark HMR work on mobile devices.
Because Nuxt does not use vite's HMR config. Nuxt rewrite vite config, and uses ws://$host:$port/_nuxt/ to provide HMR:
09-27 18:01:57.473 9019 9019 E Tauri/Console: File: http://tauri.localhost/_nuxt/@vite/client - Line 535 - Msg: WebSocket connection to 'ws://tauri.localhost/_nuxt/' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED
09-27 18:01:57.473 9019 9019 E Tauri/Console: File: http://tauri.localhost/_nuxt/@vite/client - Line 535 - Msg: Uncaught (in promise) SyntaxError: Failed to construct 'WebSocket': The URL 'ws://localhost:undefined/_nuxt/' is invalid.
It tries to connect to localhost:undefined and tauri.localhost, but this is incorrect.
All endpoints have been tried and there is no WebSocket support. This may require a direct connection to the host.
Reproduction
From official document: https://v2.tauri.app/start/frontend/nuxt/
adb devices # connect a Android devices ...
pnpm tauri android dev
If this is not detailed enough, please call me for reproduction.
Expected behavior
No error.
Full tauri info output
[✔] Environment
- OS: Windows 10.0.22631 x86_64 (X64)
✔ WebView2: 129.0.2792.52
✔ MSVC: Visual Studio Community 2022
✔ rustc: 1.81.0 (eeb90cda1 2024-09-04)
✔ cargo: 1.81.0 (2dbb1af80 2024-08-20)
✔ rustup: 1.27.1 (54dd3d00f 2024-04-24)
✔ Rust toolchain: stable-x86_64-pc-windows-msvc (default)
- node: 20.14.0
- pnpm: 9.11.0
- npm: 10.7.0
- bun: 1.1.28
[-] Packages
- tauri 🦀: 2.0.0-rc.15
- tauri-build 🦀: 2.0.0-rc.12
- wry 🦀: 0.43.1
- tao 🦀: 0.30.1
- @tauri-apps/api : 2.0.0-rc.5
- @tauri-apps/cli : 2.0.0-rc.16
[-] Plugins
- tauri-plugin-log 🦀: 2.0.0-rc.2
- @tauri-apps/plugin-log : not installed!
[-] App
- build-type: bundle
- CSP: connect-src ws://*
- frontendDist: ../dist
- devUrl: http://localhost:3000/
- framework: Vue.js (Nuxt)
- bundler: Webpack
Stack trace
09-27 18:02:41.207 9019 9019 E Tauri/Console: File: http://tauri.localhost/_nuxt/@vite/client - Line 535 - Msg: WebSocket connection to 'ws://tauri.localhost/_nuxt/' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED
09-27 18:02:41.209 9019 9019 E Tauri/Console: File: http://tauri.localhost/_nuxt/@vite/client - Line 535 - Msg: Uncaught (in promise) SyntaxError: Failed to construct 'WebSocket': The URL 'ws://localhost:undefined/_nuxt/' is invalid.
09-27 18:04:34.822 9019 9019 I HwViewRootImpl: removeInvalidNode jank list is null
09-27 18:04:37.867 9019 9019 I HwViewRootImpl: removeInvalidNode jank list is null
09-27 18:04:40.996 9019 9019 E Tauri/Console: File: http://tauri.localhost/__nuxt_devtools__/client/_nuxt/l4ouzdbv.js - Line 8 - Msg: Uncaught (in promise) Error: [birpc] timeout on calling "getOptions"
09-27 18:04:41.307 9019 9019 E Tauri/Console: File: http://tauri.localhost/__nuxt_devtools__/client/_nuxt/l4ouzdbv.js - Line 8 - Msg: Uncaught (in promise) Error: [birpc] timeout on calling "getModuleOptions"
09-27 18:04:41.309 9019 9019 E Tauri/Console: File: http://tauri.localhost/__nuxt_devtools__/client/_nuxt/l4ouzdbv.js - Line 8 - Msg: Uncaught (in promise) Error: [birpc] timeout on calling "getOptions"
09-27 18:04:41.312 9019 9019 E Tauri/Console: File: http://tauri.localhost/__nuxt_devtools__/client/_nuxt/l4ouzdbv.js - Line 8 - Msg: Uncaught (in promise) Error: [birpc] timeout on calling "telemetryEvent"
09-27 18:04:41.314 9019 9019 E Tauri/Console: File: http://tauri.localhost/__nuxt_devtools__/client/_nuxt/l4ouzdbv.js - Line 8 - Msg: Uncaught (in promise) Error: [birpc] timeout on calling "getOptions"
Additional context
No response
I found a workaround:
{
devServer: {
host: isMobile ? '0.0.0.0' : undefined,
},
hooks: {
'vite:extend': function ({ config }) {
if (config.server && config.server.hmr && config.server.hmr !== true) {
config.server.hmr.protocol = 'ws'
config.server.hmr.host = '192.168.XXX.XXX'
config.server.hmr.port = 3000
}
},
},
vite: {
clearScreen: false,
envPrefix: ['VITE_', 'TAURI_'],
server: {
strictPort: true,
watch: {
ignored: ['**/src-tauri/**'],
},
},
},
}
But I think undefined appearing in URLs is a bug.
Hey @Sun-ZhenXing can you share a working example, as I am getting a white screen creating a nuxt based app for mobile
I dig a little bit further and saw those errors as the culprit of the white screen
11-18 01:04:14.193 15907 15907 E Tauri/Console: File: - Line 138 - Msg: Uncaught TypeError: Cannot redefine property: postMessage 11-18 01:04:14.194 15907 15907 E Tauri/Console: File: - Line 2 - Msg: Uncaught TypeError: Cannot redefine property: metadata 11-18 01:04:14.194 15907 15907 E Tauri/Console: File: - Line 25 - Msg: Uncaught TypeError: Cannot redefine property: TAURI_PATTERN 11-18 01:04:14.194 15907 15907 E Tauri/Console: File: - Line 5 - Msg: Uncaught TypeError: Cannot redefine property: path
@gkkirilov Im suspecting about the blank screen is caused (correct me if Im wrong, those folders dont need to be generated for dev but I suspect Tauri needs it for app dev) because dist and .output folders are empty. When running npx tauri android dev after nuxi generate both folders content wipe with no reason apparently i don't know if Tauri is gonna use those folders because they ask to run nuxi generate... Ive taken some snapshots while npx tauri android dev --verbose was running and I got this 5 different both folders state:
FIRST STATE:
SECOND STATE: (Notice that nitro.json is removed)
THIRD STATE:
FOURTH STATE:
FIFTH STATE:
The wipe of both folders occurs exactly at this part of the logging:
If I remove ssr: false the blank screen dissapears and content appears but looks messy obviously because ssr has no place here... anyways sometimes I get the app correctly rendered... those are the 3 possible cases but HMR still not working with @Sun-ZhenXing solution. In both cases the initial loading page of Nuxt appears as usual.
FIRST CASE:
SECOND CASE:
THIRD CASE:
Any clues?
Here's my minimal working config. (Some of) the lengthy fs ignores are necessary to keep the server startup time low. It was taking well over 1 minute to startup without putting these ignores in.
The entire setup took me quite a few hours to figure out.
The main issue with HMR is the undefined value for the Vite WS port.
The vite:hook example above helped, but I needed to leave the original devServer values alone (better to take in what Tauri gives you anyway).
export default defineNuxtConfig({
// (optional) Enable the Nuxt devtools
devtools: { enabled: true },
// Enable SSG
ssr: false,
ignore: ['**/src-tauri/**', '**/node_modules/**', '**/dist/**', '**/.git/**', '**/.nuxt/**', '**/.output/**'],
// Enables the development server to be discoverable by other devices when running on iOS physical devices
devServer: { host: process.env.TAURI_DEV_HOST || 'localhost' },
vite: {
// Better support for Tauri CLI output
clearScreen: false,
// Enable environment variables
// Additional environment variables can be found at
// https://v2.tauri.app/reference/environment-variables/
envPrefix: ['VITE_', 'TAURI_'],
server: {
// Tauri requires a consistent port
strictPort: true,
watch: {
ignored: [
'**/src-tauri/**',
'**/node_modules/**',
'**/dist/**',
'**/.git/**',
'**/.nuxt/**',
'**/public/**',
'**/.output/**'
]
}
},
},
hooks: {
'vite:extend': function ({ config }) {
if (config.server && config.server.hmr && config.server.hmr !== true) {
config.server.hmr.port = 3000
}
},
},
compatibilityDate: '2024-11-29'
});
This got my HMR working. Thanks
hooks: {
'vite:extend': function ({ config }) {
if (config.server && config.server.hmr && config.server.hmr !== true) {
config.server.hmr.port = 3000
}
},
},
vite: {
clearScreen: false,
envPrefix: ['VITE_', 'TAURI_'],
server: {
watch: {
ignored: ["**/src-tauri/**"],
usePolling: true
},
strictPort: true,
hmr: {
host: '192.168.0.15',
port: 5137,
protocol: "ws"
},
},
css: {
preprocessorOptions: {
scss: {
additionalData: '@use "@/assets/scss/variables.scss" as *;',
},
},
},
},
````
When reproducing on my Android phone, I encountered another new issue tauri-apps/wry#1420.
I am attaching the Cargo lock file that works so that it can be debugged properly. Until the above issue is fixed, I can't confirm that the latest Tauri will work on my Android phone.
# Cargo.toml
[package]
name = "app"
version = "0.1.0"
description = "A Tauri App"
authors = ["you"]
license = ""
repository = ""
edition = "2021"
rust-version = "1.77.2"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
# Do not update tauri, till the issue is resolved
[lib]
name = "app_lib"
crate-type = ["staticlib", "cdylib", "lib"]
[build-dependencies]
tauri-build = { version = "2.0.0", features = [] }
[dependencies]
serde_json = "1.0"
serde = { version = "1.0", features = ["derive"] }
log = { version = "0.4" }
tauri = { version = "2.0.0", features = [] }
tauri-plugin-log = "2.0.0"
// nuxt.config.ts
// ...
import process from 'node:process'
const isMobile = !!/android|ios/.exec(process.env.TAURI_ENV_PLATFORM || '')
const host = process.env.NUXT_HMR_HOST || process.env.TAURI_DEV_HOST || undefined
// https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({
devServer: {
host: isMobile ? '0.0.0.0' : undefined,
},
ignore: [
'**/src-tauri/**',
'**/node_modules/**',
'**/dist/**',
'**/.git/**',
'**/.nuxt/**',
'**/.output/**',
],
hooks: {
// [BUG] https://github.com/tauri-apps/tauri/issues/11165
'vite:extend': host && isMobile
? ({ config }) => {
if (config.server && config.server.hmr && config.server.hmr !== true) {
config.server.hmr.protocol = 'ws'
config.server.hmr.host = host
config.server.hmr.port = 3000
}
}
: undefined,
},
vite: {
clearScreen: false,
envPrefix: ['VITE_', 'TAURI_'],
server: {
watch: {
ignored: ['**/src-tauri/**'],
usePolling: true,
},
strictPort: true,
},
css: {
preprocessorOptions: {
scss: { api: 'modern-compiler' },
},
},
},
})
I'm pretty sure that this problem can't be solved at the moment, because WebSocket doesn't seem to be able to be proxied yet.
So this workaround will work for a long time yet.
I have similar problem.
This is my config
compatibilityDate: '2024-11-01',
devtools: { enabled: true },
ssr: false,
devServer: { host: process.env.TAURI_DEV_HOST || 'localhost' },
hooks: {
'vite:extend': function ({ config }) {
if (config.server && config.server.hmr && config.server.hmr !== true) {
config.server.hmr.port = 3000
}
},
},
vite: {
clearScreen: false,
envPrefix: ['VITE_', 'TAURI_'],
server: {
watch: {
ignored: ["**/src-tauri/**"],
usePolling: true
},
strictPort: true,
hmr: {
host: '192.168.1.2',
port: 3000,
protocol: "ws"
},
},
},
})
I see in page data. I cant see in android emulator any data. When i remove ssr: false or set up ssr: true, i can see data in android emulator. However hmr never works for me. Any suggestions ?
in tauri conf i have
"beforeDevCommand": "yarn dev",
"beforeBuildCommand": "yarn generate",
"devUrl": "http://localhost:3000",
"frontendDist": "../dist"
},