Open fabio-ivona opened 2 years ago
After PR https://github.com/vitejs/vite/pull/3929 has ben released in V3,
This was introduced in v2.4.0. So I guess this was happening before v3 or there is a different reason.
Both using and not using the vite livewire plugin, it is generally detrimental to have a full refresh when a .blade file changes
I suppose filtering the module in handleHotUpdate
will prevent the refresh.
https://github.com/def-studio/vite-livewire-plugin/blob/2023843edfe6ee1c2fc049407359ef916c93a4cd/src/index.ts#L239-L245
@sapphi-red missed that feature! thanks!
@sapphi-red this one is also impacting the official Laravel plugin.
It seems that Vite is triggering a complete browser refresh whenever files that Tailwind is configured to watch change, but I would have thought that Vite would be able to just push the new changes to the browser in a HMR way, just like it does when I edit an entry point.
Is this expected behaviour with Tailwind JIT? I can provide an example repo of the issue.
Feels like a workaround to have to introduce the handleHotUpdate
function to the plugin just to filter this out.
@timacdonald I think this is expected for now.
When tailwind has foo.html
in content
option, it registers that as a dependency of bar.css
(the content is @tailwind base;
and others).
For this example, when foo.html
is changed, Vite needs to trigger a full reload. Because Vite does not know whether only updating css by HMR is enough when foo.html
is changed. (When a html is changed, the full reload is required because there is a dependency-relation which does not appear in the module graph. I think this should be enhanced but it's not a easy.)
That said, I agree the default behavior could be improved.
Thank you for the detailed explanation @sapphi-red I can see how that all comes together now.
I changed the import method of my component from the relative import to the absolute import, and the problem was solved
@sapphi-red with your help I managed to stop the page full reload trigger bu returning []
after having handled the module myself:
handleHotUpdate(ctx) {
for (const pattern of pluginConfig.watch) {
if ((0, minimatch_1.default)(ctx.file, pattern)) {
rivewireRefresh(ctx, pluginConfig);
return [];
}
}
}
there is a problem with this solution, though: returning []
seems to prevent taiwlindcss from recompiling its css file
is there any way to trigger it in order to obtain a .css hot reload?
@fabio-ivona I think you'll need to do this one
Filter and narrow down the affected module list so that the HMR is more accurate.
instead of returning []
(not tried though).
@sapphi-red the original Module list contains only the tailwind .css
file
if I leave that one, the full reload occours, if I remove it, tailwind JIT doesn't recompile
so I cannot narrow it furthermore :disappointed:
a (really) dirty workaround is to add a dummy node ( in that module importers Set in order to make it fail the check in https://github.com/vitejs/vite/commit/d97b33a8cb9a72ed64244f239900a9a862b6ba68
if (ctx.modules[0]?.importers && ctx.modules[0].importers.size === 1) {
const dummyModule = {...ctx.modules[0]};
dummyModule.importers = new Set;
dummyModule.isSelfAccepting = true;
ctx.modules[0].importers.add(dummyModule);
}
It seems to work, but I really don't like my solution. Any idea?
@fabio-ivona How about stopping at this condition? https://github.com/vitejs/vite/blob/b2c0ee04d4db4a0ef5a084c50f49782c5f88587c/packages/vite/src/node/server/hmr.ts#L246-L252
@fabio-ivona How about stopping at this condition?
@sapphi-red how can I obtain that?
@fabio-ivona I think setting ctx.modules[0].importers = new Set()
will make this for-loop skipped.
@sapphi-red wouldn't this skip tailwind jit compiling too?
@fabio-ivona I think setting ctx.modules[0].importers = new Set() will make this for-loop skipped.
@sapphi-red I confirm, it solves the full refresh issue, but it stops Tailwind jit compilation too (I guess because app.css is removed from node importers)
@fabio-ivona Maybe it should be unwrapping instead.
handleHotUpdate(ctx) {
for (const pattern of pluginConfig.watch) {
if ((0, minimatch_1.default)(ctx.file, pattern)) {
rivewireRefresh(ctx, pluginConfig);
return [...ctx.modules[0].importers, ...ctx.modules.slice(1)]
}
}
}
@sapphi-red it works like a charm!
thanks for your hints and the awesome work you do here! :heart:
@fabio-ivona could you help me out to put the workaround in my laravel 9 + tailwind + livewire project?
I tried to understand the vite Plugin API, but I'm unable to put the handleHotUpdate
code in the right place.
Hi @anhofmann , are you using my Vite Livewire plugin? Or is it a custom code?
Thank you @fabio-ivona for the fast reply! To be honest, I have no idea what I'm using. I've created a Laravel 9 project two months ago with Jetstream and I'm using Livewire. As far as I understand, my setup is the default that comes with Laravel. And I have the full page refresh effect, that is described in this issue.
My vite.config.js
looks like this:
import { defineConfig } from 'vite';
import laravel, { refreshPaths } from 'laravel-vite-plugin';
export default defineConfig({
plugins: [
laravel({
input: [
'resources/css/app.css',
'resources/js/app.js',
],
refresh: [
...refreshPaths,
'app/Http/Livewire/**',
'app/Forms/Components/**',
],
}),
],
});
BTW: I tried to set refresh
to false
, but when I run sail npm run dev
, I still have the full page reloads on file changes. No idea why my setting gets ignored.
I was hoping to be able to disable the reload mechanism, or have a reload mechanism, that doesn't reload the full page, because this destroys the state in my Livewire components.
This is my postcss.config.js
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
};
and this my package.json
{
"private": true,
"scripts": {
"dev": "vite",
"build": "vite build"
},
"devDependencies": {
"@alpinejs/focus": "^3.10.5",
"@tailwindcss/forms": "^0.5.2",
"@tailwindcss/typography": "^0.5.4",
"alpinejs": "^3.10.3",
"autoprefixer": "^10.4.7",
"axios": "^1.1.2",
"laravel-vite-plugin": "^0.7.2",
"postcss": "^8.1.14",
"tailwindcss": "^3.1",
"vite": "^4.0.0"
}
}
@anhofmann you can use this to solve the issue, both by directly using the plugin or by implementing your custom solution and taking my code as an example:
Describe the bug
After PR #3929 has ben released in V3,
In Laravel+tailwind+livewire stacks we are experiencing unwanted full refreshes triggered by changes on files registered as dependencies by tailwind
background
during Livewire components development, often with nested interactions, it is generally unwanted to full refresh the page until all template+css changes are completed, as it compels the developer to recreate again the same state of the component in order to show the part under development
Additionally a vite plugin has been released in order to trigger livewire components hot reloads when template files are changed (see here the plugin). The plugin uses
to trigger a client-side script that starts the update routine for all livewire components in the page.
the problem
Both using and not using the vite livewire plugin, it is generally detrimental to have a full refresh when a .blade file changes
also, full page refreshes for .blade file changes is a feature already covered by laravel vite plugin (see here the plugin) with an opt-in feature in its config
the cause
PR #3929 fixes other issues by adding this code https://github.com/vitejs/vite/commit/d97b33a8cb9a72ed64244f239900a9a862b6ba68 to trigger a full refresh when a change happens in a .blade file registered as dependency by tailwind preprocessing
reproduction
the issue can be reproduced in any plain laravel+tailwind installation, seeing that the page is full refreshed at every .blade file change.
a ready to use repository is available at the reproduction link, it works with docker+docker-compose in order to setup an nginx+php+mysql environment, but can also be set up manually
see the repository readme for detailed steps
desired behaviour
in V2 the behaviour was the desired one:
when a .blade file was changes, tailwind recompiled app.css file, then Vite:
the solution
is there the possibility to make this part of code conditional? maybe with a vite config setting to disable it where needed?
I've set up a small Laravel/livewire project to reproduce the issue
Reproduction
https://github.com/fabio-ivona/livewire-refresh
System Info
Used Package Manager
npm
Logs
Click to expand!
```shell vite:config bundled config file loaded in 55.85ms +0ms vite:config using resolved config: { vite:config plugins: [ vite:config 'vite:pre-alias', vite:config 'alias', vite:config 'vite:modulepreload-polyfill', vite:config 'vite:optimized-deps', vite:config 'vite:resolve', vite:config 'vite:html-inline-proxy', vite:config 'vite:css', vite:config 'vite:esbuild', vite:config 'vite:json', vite:config 'vite:wasm-helper', vite:config 'vite:worker', vite:config 'vite:asset', vite:config 'vite:wasm-fallback', vite:config 'vite:define', vite:config 'vite:css-post', vite:config 'vite:worker-import-meta-url', vite:config 'vite:dynamic-import-vars', vite:config 'vite:import-glob', vite:config 'laravel', vite:config 'vite:client-inject', vite:config 'vite:import-analysis' vite:config ], vite:config optimizeDeps: { vite:config disabled: 'build', vite:config force: undefined, vite:config esbuildOptions: { preserveSymlinks: undefined } vite:config }, vite:config server: { vite:config preTransformRequests: true, vite:config origin: '__laravel_vite_placeholder__', vite:config host: '0.0.0.0', vite:config port: 5173, vite:config strictPort: true, vite:config middlewareMode: false, vite:config fs: { strict: true, allow: [Array], deny: [Array] } vite:config }, vite:config base: '/', vite:config publicDir: '', vite:config build: { vite:config target: [ 'es2020', 'edge88', 'firefox78', 'chrome87', 'safari13' ], vite:config polyfillModulePreload: true, vite:config outDir: 'public/build', vite:config assetsDir: 'assets', vite:config assetsInlineLimit: 4096, vite:config cssCodeSplit: true, vite:config cssTarget: [ 'es2020', 'edge88', 'firefox78', 'chrome87', 'safari13' ], vite:config sourcemap: false, vite:config rollupOptions: { input: [Array] }, vite:config minify: 'esbuild', vite:config terserOptions: {}, vite:config write: true, vite:config emptyOutDir: null, vite:config manifest: true, vite:config lib: false, vite:config ssr: false, vite:config ssrManifest: false, vite:config reportCompressedSize: true, vite:config chunkSizeWarningLimit: 500, vite:config watch: null, vite:config commonjsOptions: { include: [Array], extensions: [Array] }, vite:config dynamicImportVarsOptions: { warnOnError: true, exclude: [Array] } vite:config }, vite:config resolve: { alias: [ [Object], [Object], [Object] ] }, vite:config ssr: { vite:config format: 'esm', vite:config target: 'node', vite:config noExternal: [ 'laravel-vite-plugin' ], vite:config optimizeDeps: { disabled: true, esbuildOptions: [Object] } vite:config }, vite:config configFile: '/var/www/html/vite.config.js', vite:config configFileDependencies: [ '/var/www/html/vite.config.js' ], vite:config inlineConfig: { vite:config root: undefined, vite:config base: undefined, vite:config mode: undefined, vite:config configFile: undefined, vite:config logLevel: undefined, vite:config clearScreen: undefined, vite:config optimizeDeps: { force: undefined }, vite:config server: {} vite:config }, vite:config root: '/var/www/html', vite:config cacheDir: '/var/www/html/node_modules/.vite', vite:config command: 'serve', vite:config mode: 'development', vite:config isWorker: false, vite:config mainConfig: null, vite:config isProduction: false, vite:config preview: { vite:config port: undefined, vite:config strictPort: true, vite:config host: '0.0.0.0', vite:config https: undefined, vite:config open: undefined, vite:config proxy: undefined, vite:config cors: undefined, vite:config headers: undefined vite:config }, vite:config env: { vite:config VITE_PUSHER_APP_KEY: '', vite:config VITE_PUSHER_HOST: '', vite:config VITE_PUSHER_PORT: '443', vite:config VITE_PUSHER_SCHEME: 'https', vite:config VITE_PUSHER_APP_CLUSTER: 'mt1', vite:config BASE_URL: '/', vite:config MODE: 'development', vite:config DEV: true, vite:config PROD: false vite:config }, vite:config assetsInclude: [Function: assetsInclude], vite:config logger: { vite:config hasWarned: false, vite:config info: [Function: info], vite:config warn: [Function: warn], vite:config warnOnce: [Function: warnOnce], vite:config error: [Function: error], vite:config clearScreen: [Function: clearScreen], vite:config hasErrorLogged: [Function: hasErrorLogged] vite:config }, vite:config packageCache: Map(0) {}, vite:config createResolver: [Function: createResolver], vite:config worker: { vite:config format: 'iife', vite:config plugins: [ vite:config [Object], [Object], [Object], vite:config [Object], [Object], [Object], vite:config [Object], [Object], [Object], vite:config [Object], [Object], [Object], vite:config [Object], [Object], [Object], vite:config [Object], [Object], [Object], vite:config [Object], [Object] vite:config ], vite:config rollupOptions: {} vite:config }, vite:config appType: 'spa', vite:config experimental: { importGlobRestoreExtension: false, hmrPartialAccept: false } vite:config } +57ms vite:deps Hash is consistent. Skipping. Use --force to override. +0ms VITE v3.0.4 ready in 255 ms ➜ Local: http://localhost:5173/ ➜ Network: http://172.27.0.7:5173/ LARAVEL v9.23.0 plugin v0.5.2 ➜ APP_URL: http://livewire-refresh.test vite:spa-fallback Rewriting GET / to /index.html +0ms vite:time 2.70ms /index.html +0ms vite:time 5.04ms /index.html +3ms vite:spa-fallback Rewriting GET / to /index.html +6ms vite:time 0.52ms /index.html +2ms vite:time 0.91ms /index.html +0ms vite:hmr [file change] storage/framework/sessions/5bVKUXpEXuUOZ0bLZZRyXLZ5WHgm4uDKplNJHS34 +0ms vite:hmr [no modules matched] storage/framework/sessions/5bVKUXpEXuUOZ0bLZZRyXLZ5WHgm4uDKplNJHS34 +1ms vite:resolve 1.90ms /@vite/client -> /var/www/html/node_modules/vite/dist/client/client.mjs +0ms vite:resolve 0.39ms /resources/css/app.css?direct -> /var/www/html/resources/css/app.css?direct +4ms vite:load 6.02ms [fs] /@vite/client +0ms vite:resolve 0.33ms @vite/env -> /var/www/html/node_modules/vite/dist/client/env.mjs +12ms vite:resolve 0.11ms /node_modules/vite/dist/client/env.mjs -> /var/www/html/node_modules/vite/dist/client/env.mjs +1ms vite:import-analysis 5.20ms [1 imports rewritten] node_modules/vite/dist/client/client.mjs +0ms vite:transform 9.23ms /@vite/client +0ms vite:time 21.74ms /@vite/client +248ms vite:load 14.64ms [fs] /resources/css/app.css?direct +13ms vite:cache [304] /@vite/client +0ms vite:time 0.37ms /@vite/client +4ms vite:load 4.74ms [fs] /node_modules/vite/dist/client/env.mjs +4ms vite:import-analysis 0.07ms [no imports] node_modules/vite/dist/client/env.mjs +7ms vite:transform 0.39ms /node_modules/vite/dist/client/env.mjs +7ms vite:time 1.37ms /node_modules/vite/dist/client/env.mjs +2ms vite:cache [304] /node_modules/vite/dist/client/env.mjs +332ms vite:time 0.44ms /node_modules/vite/dist/client/env.mjs +331ms vite:resolve 0.17ms /resources/views/welcome.blade.php -> /var/www/html/resources/views/welcome.blade.php +516ms vite:resolve 0.21ms /resources/views/livewire/hello-world.blade.php -> /var/www/html/resources/views/livewire/hello-world.blade.php +1ms vite:resolve 0.18ms /resources/js/app.js -> /var/www/html/resources/js/app.js +0ms vite:resolve 0.18ms /resources/js/bootstrap.js -> /var/www/html/resources/js/bootstrap.js +1ms vite:resolve 0.16ms /tailwind.config.js -> /var/www/html/tailwind.config.js +0ms vite:import-analysis [skipped] resources/css/app.css?direct +511ms vite:transform 515.90ms /resources/css/app.css?direct +511ms vite:time 532.72ms /resources/css/app.css +180ms vite:cache [304] /resources/css/app.css?direct +182ms vite:time 0.65ms /resources/css/app.css +2ms vite:deps ✨ static imports crawl ended +2s vite:hmr [file change] storage/framework/sessions/5bVKUXpEXuUOZ0bLZZRyXLZ5WHgm4uDKplNJHS34 +5s vite:hmr [no modules matched] storage/framework/sessions/5bVKUXpEXuUOZ0bLZZRyXLZ5WHgm4uDKplNJHS34 +0ms vite:hmr [file change] storage/framework/sessions/5bVKUXpEXuUOZ0bLZZRyXLZ5WHgm4uDKplNJHS34 +279ms vite:hmr [no modules matched] storage/framework/sessions/5bVKUXpEXuUOZ0bLZZRyXLZ5WHgm4uDKplNJHS34 +0ms vite:hmr [file change] storage/framework/sessions/5bVKUXpEXuUOZ0bLZZRyXLZ5WHgm4uDKplNJHS34 +339ms vite:hmr [no modules matched] storage/framework/sessions/5bVKUXpEXuUOZ0bLZZRyXLZ5WHgm4uDKplNJHS34 +0ms vite:hmr [file change] resources/views/livewire/hello-world.blade.php +11s 7:37:41 AM [vite] page reload resources/views/livewire/hello-world.blade.php vite:hmr [file change] storage/framework/views/4e0125ea13b45623cb2cef72879f41ca2753763e.php +50ms vite:hmr [no modules matched] storage/framework/views/4e0125ea13b45623cb2cef72879f41ca2753763e.php +0ms vite:hmr [file change] storage/framework/sessions/5bVKUXpEXuUOZ0bLZZRyXLZ5WHgm4uDKplNJHS34 +2ms vite:hmr [no modules matched] storage/framework/sessions/5bVKUXpEXuUOZ0bLZZRyXLZ5WHgm4uDKplNJHS34 +1ms vite:cache [304] /@vite/client +16s vite:time 1.45ms /@vite/client +16s vite:load 3.14ms [fs] /resources/css/app.css?direct +17s vite:import-analysis [skipped] resources/css/app.css?direct +16s vite:transform 28.68ms /resources/css/app.css?direct +16s vite:time 35.04ms /resources/css/app.css +36ms vite:cache [304] /@vite/client +38ms vite:time 1.25ms /@vite/client +2ms vite:cache [304] /resources/css/app.css?direct +1ms vite:time 0.53ms /resources/css/app.css +1ms vite:hmr [file change] .idea/workspace.xml +80ms vite:hmr [no modules matched] .idea/workspace.xml +0ms vite:cache [304] /node_modules/vite/dist/client/env.mjs +29ms vite:time 0.44ms /node_modules/vite/dist/client/env.mjs +29ms vite:cache [304] /node_modules/vite/dist/client/env.mjs +19ms vite:time 0.59ms /node_modules/vite/dist/client/env.mjs +19ms ```Validations