Open SystemParadox opened 2 years ago
Vite's document seems to be wrong (or there was a behavior change on chokidar side?). See #7850.
I also wonder if the order matters:
say if we move the user-specified ones at the top before **/node_modules/**
🤔
@bluwy good thought but alas no, swapping the order doesn't help:
ignored: [
'!**/node_modules/foo/**',
'**/node_modules/**',
],
Chokidar still seems to ignore the whole of node_modules
and doesn't bother looking inside it.
@SystemParadox
If it's any help, creating a custom plugin to override the vite-enforced watch options seems to have worked for me
{
name: 'watch-node-modules',
configureServer: (server: ViteDevServer) : void => {
server.watcher.options = {
...server.watcher.options,
ignored: [
/node_modules\/(?!my-package-name).*/,
'**/.git/**',
]
}
}
}
Thanks, that's very helpful as a temporary workaround until chokidar provides an official recommendation of how to fix this properly.
To preempt anyone who tries to close this:
Similar to https://github.com/vitejs/vite/issues/6718, it would be nice to exclude locally linked packages from being ignored by default. When I make a change in a sub-dependency, I want the bundle to rebuild.
I currently can't use vite because of this issue. I have a monorepo, where I build dependencies separately. but vite doesn't detect changes to them and there seems to be no way of making it detect them. The trick with the plugin by @bluwy didn't work for me either.
The workaround above using a custom plugin doesn't work for a plain vite.build({ watch: true })
because the configureServer
hook never gets called when you're not using the dev server.
I tried using the options
universal hook to replace inputOptions.watch.chokidar.ignored
as desired but doesn't seem to have any effect on what is actually watched/ignored.
Custom Vite plugin (doesn't work):
{
name: 'watch-node-modules',
options(inputOptions) {
inputOptions.watch.chokidar.ignored = [
/node_modules\/(?!hydrogen-view-sdk).*/,
'**/.git/**',
];
return inputOptions;
}
}
When using Vite, inputOptions.watch.chokidar.ignored
is normally:
[
'**/.git/**',
'**/node_modules/**',
'**/test-results/**',
'/home/eric/Documents/github/element/matrix-public-archive/node_modules/.vite/**'
]
Just packaged up @ryzr's awesome little snippet into something reusable where you can also list multiple modules if you want 🚀
import { ViteDevServer } from 'vite';
export function pluginWatchNodeModules(modules) {
// Merge module into pipe separated string for RegExp() below.
let pattern = `/node_modules\\/(?!${modules.join('|')}).*/`;
return {
name: 'watch-node-modules',
configureServer: (server: ViteDevServer) : void => {
server.watcher.options = {
...server.watcher.options,
ignored: [
new RegExp(pattern),
'**/.git/**',
]
}
}
}
}
Then to use it, pass into your plugins
array like so:
// Import from the separate file you might store this in, e.g. 'utils'
import { pluginWatchNodeModules } from './utils';
plugins: [
// ... other plugins...
pluginWatchNodeModules(['your-plugin', 'another-example']),
],
Edit: p.s. Don't forget to ensure that you exclude these packages from optimizeDeps
like so:
optimizeDeps: {
exclude: [
'your-plugin',
'another-example',
],
},
I don't know if something changed in the last month but @patricknelson snippet did not work for me. Been banging my head against this for hours but finally saw this in docs and so tried this:
import type { PluginOption } from 'vite';
export function watchNodeModules(modules: string[]): PluginOption {
return {
name: 'watch-node-modules',
config() {
return {
server: {
watch: {
ignored: modules.map((m) => `!**/node_modules/${m}/**`),
},
},
};
},
};
}
And it worked! The other "gotcha" I wanted to call out is that if you need to include a dep of your excluded dep, you need to include EXACTLY what is in your import line. For me it was react-icons
and I wasted a few hours until I realized I had to include react-icons/fi/index.js
because that is what was in the import line in my esm package.
I don't know if something changed in the last month but @patricknelson snippet did not work for me. Been banging my head against this for hours but finally saw this in docs and so tried this:
import type { PluginOption } from 'vite'; export function watchNodeModules(modules: string[]): PluginOption { return { name: 'watch-node-modules', config() { return { server: { watch: { ignored: modules.map((m) => `!**/node_modules/${m}/**`), }, }, }; }, }; }
And it worked! The other "gotcha" I wanted to call out is that if you need to include a dep of your excluded dep, you need to include EXACTLY what is in your import line. For me it was
react-icons
and I wasted a few hours until I realized I had to includereact-icons/fi/index.js
because that is what was in the import line in my esm package.
Many thanks. It works, but I need to Ctrl + S
my vite.config.js
(I am using react in my laravel codebase) to make my app load again to see changes in the excluded package.
Is there any way that make vite automatically reload without manually saving vite.config.js
again?
@quyle92 you may be running into cache stuff. Are you also listing your package in optimizeDeps.exclude
? This is the final version of plugin I've been using:
import type { PluginOption } from 'vite';
export function watchNodeModules(modules: string[]): PluginOption {
return {
name: 'watch-node-modules',
config() {
return {
server: {
watch: {
ignored: modules.map((m) => `!**/node_modules/${m}/**`),
},
},
optimizeDeps: {
exclude: modules,
},
};
},
};
}
Hi @TheTedAdams ,
Thanks for your reply.
If I add optimizeDeps: { exclude: modules, },
vite failed to build my app and throw error at node_modules/uncontrollable/lib/esm/utils.js
with message being Uncaught SyntaxError: ambiguous indirect export: default
.
The issue with using optimizeDeps.exclude
for this is that this also excludes deps of that package from vite's esm/cjs interop magic. So you then have to run through and include a bunch of your deps-of-deps in optimizeDeps.include
to re-opt them in to esm/cjs interop. (@quyle92 that's probably the issue you're running into.)
I added the plugin in https://github.com/vitejs/vite/issues/8619#issuecomment-1707700396 and vite updates my linked package when I save the vite config but it still doesn't update when I change the source code. I have resolve.preserveSymlinks: true
enabled. Any suggestions for how I can watch a linked package?
English is not my native language, and there may be grammar errors in the following content. Please understand.
I tried this method, but not work.(https://github.com/antfu/vite-plugin-restart/issues/10) This is my method. Create a new .env file for the root path.
# .env
VITE_CHANGE_KEY=anything
and use node to change this file
// generateId.js
import { readFile, writeFile } from 'fs';
readFile('./.env', 'utf-8', (err, contents) => {
if (err) {
console.error(err);
process.exit(1);
return;
}
contents = `VITE_CHANGE_KEY=${Math.random()}`;
writeFile('./.env', contents, 'utf-8', (err) => {
if (err) {
console.log(err);
} else {
console.log(contents);
}
});
});
Because Vite will observe changes in the. env file and then re-run.
(https://vitejs.dev/guide/dep-pre-bundling.html#caching)
So, when your specific dependencies have changed, you can run node generateId.js
.
As for how to know if the dependency has changed, you can use nodemon
// nodemon.json
{
"exec": "npm run *** && npm run generate-id", // generate-id just scripts command => 'node generateId.js'
}
It's stupid, but it works.
@quyle92 you can read package's dependencies and put them to vite optimizeDeps. include
:
// vite.config.js
import path from 'path'
import {readFileSync} from 'fs'
export function watchNodeModules(modules) {
return {
name: 'watch-node-modules',
config() {
return {
server: {
watch: {
ignored: modules.map((m) => `!**/node_modules/${m}/**`),
},
},
optimizeDeps: {
exclude: modules,
include:modules.reduce((totalIncludes,m)=>{
const url=path.join(process.cwd(), `node_modules/${m}/package.json`)
const source = readFileSync(url, 'utf-8');
const pkg = JSON.parse(source);
const dependencies=pkg.dependencies
// https://vitejs.dev/config/dep-optimization-options.html#optimizedeps-exclude
const include=Object.keys(dependencies).reduce((includes,d)=>{
// remove types package
if(d.includes('@types')){
return includes
}
includes.push(`${m} > ${d}`)
return includes
},[])
totalIncludes=[...new Set([...totalIncludes,...include])]
return totalIncludes
},[]),
},
};
},
};
}
@quyle92 you can read package's dependencies and put them to vite
optimizeDeps. include
:// vite.config.js import path from 'path' import {readFileSync} from 'fs' export function watchNodeModules(modules) { return { name: 'watch-node-modules', config() { return { server: { watch: { ignored: modules.map((m) => `!**/node_modules/${m}/**`), }, }, optimizeDeps: { exclude: modules, include:modules.reduce((totalIncludes,m)=>{ const url=path.join(process.cwd(), `node_modules/${m}/package.json`) const source = readFileSync(url, 'utf-8'); const pkg = JSON.parse(source); const dependencies=pkg.dependencies // https://vitejs.dev/config/dep-optimization-options.html#optimizedeps-exclude const include=Object.keys(dependencies).reduce((includes,d)=>{ // remove types package if(d.includes('@types')){ return includes } includes.push(`${m} > ${d}`) return includes },[]) totalIncludes=[...new Set([...totalIncludes,...include])] return totalIncludes },[]), }, }; }, }; }
This throws error does not provide an export named 'default'
Just packaged up @ryzr's awesome little snippet into something reusable where you can also list multiple modules if you want 🚀
import { ViteDevServer } from 'vite'; export function pluginWatchNodeModules(modules) { // Merge module into pipe separated string for RegExp() below. let pattern = `/node_modules\\/(?!${modules.join('|')}).*/`; return { name: 'watch-node-modules', configureServer: (server: ViteDevServer) : void => { server.watcher.options = { ...server.watcher.options, ignored: [ new RegExp(pattern), '**/.git/**', ] } } } }
Then to use it, pass into your
plugins
array like so:// Import from the separate file you might store this in, e.g. 'utils' import { pluginWatchNodeModules } from './utils'; plugins: [ // ... other plugins... pluginWatchNodeModules(['your-plugin', 'another-example']), ],
Edit: p.s. Don't forget to ensure that you exclude these packages from
optimizeDeps
like so:optimizeDeps: { exclude: [ 'your-plugin', 'another-example', ], },
Was unable to get it working until I added server.watcher._userIgnored = undefined
picked from chokidar source (when configureServer
is called, server.watcher
is already instanciated).
export function pluginWatchNodeModules (modules) {
return {
name: 'watch-node-modules',
configureServer: (server) => {
const regexp = `/node_modules\\/(?!${modules.join('|')}).*/`
server.watcher.options = {
...server.watcher.options,
ignored: [
'**/.git/**',
'**/test-results/**',
new RegExp(regexp)
]
}
server.watcher._userIgnored = undefined
},
config () {
return {
optimizeDeps: {
exclude: modules
}
}
}
}
}
Mb anyone worked with nuxt 3
Fix of @AttackXiaoJinJin work with small changes, but SSR updated only one time, to fix this we add to nuxt.config.ts
{
...
nitro: {
devServer: {
watch: [...absoluteResolvedPackagePaths]
}
}
...
}
Did work for me:
function watchPackages(packageNames) {
let isWatching = false;
return {
name: 'vite-plugin-watch-packages',
buildStart() {
if (!isWatching) {
packageNames.forEach((packageName) => {
const absPackagePath = path.resolve('node_modules', packageName);
const realPackagePath = fs.realpathSync(absPackagePath);
this.addWatchFile(realPackagePath);
});
isWatching = true;
}
},
};
}
// in vite.config.js
{
plugins: [watchPackages(['dayjs', 'foo/bar', '@some-scoped-package/utils'])]
}
But it works only for build --watch
mode. Linked packages (npm link
/npm i <../../your/fs/path>
) will work too.
Thanks for sharing @acupofspirt! This still doesn't seem to work for me in https://github.com/vega/editor if I link https://github.com/vega/vega-lite. I run yarn watch
in Vega-Lite and make a change in the code but the editor does not refresh. Any idea what might be wrong?
@domoritz It will work only for build --watch
. I've updated my comment. Sorry for the confusion 😔
Ah bummer, I really need it with vite serve
.
If it helps, it works with npx vite
without any custom scripts when re-saving the vite config file while force
is true
in the config:
optimizeDeps: { force: true },
or if the command is started with: --force
6718
Hi @acupofspirt,
I tried using this plugin. It's great and worked well for the files inside the src or the root but not in the module graph.
But it did not work for the files inside the node_modules.
I did use the build --watch
mode. I also tried to print the path the files it added, and the path to the files were valid.
May I know your vite version?
Thanks!
6718
Hi @acupofspirt,
I tried using this plugin. It's great and worked well for the files inside the src or the root but not in the module graph.
But it did not work for the files inside the node_modules.
I did use the
build --watch
mode. I also tried to print the path the files it added, and the path to the files were valid.May I know your vite version?
Thanks!
Any file of other projects inside the node_modules can't be watched expectedly either. It's weird.
Hi, is there anyone still care about it? ALL workarounds above not work for me, that's quite irritating... I am spending the whole night for this thing that shouldn't happen at all.
Hi, is there anyone still care about it?
✋ me for sure. It's been bugging my team for a while.
I dont use command "vite", which does not reflect changes in node_modules correctly. Instead I use these scripts:
"preview": "vite preview",
"build-watch": "vite build --watch",
"dev": "concurrently \"npm:build-watch\" \"npm:preview\""
This checks for all changes (even in node_modules) and restarts the dev server. It is triggered by changes in my linked package without problem.
Oh nice. How fast is that?
I switched to parcel which seems to work with watching deps if I set --watch-dir ..
.
Hmm, I tried your suggestion @maylow22 but vite doesn't seem to rebuild when I change the sources in my linked dependency. Do you have an example repo I can try or a specific config/plugin?
It does not listen for changes in your source code, but rather in the built package. Therefore, I have a watcher script in a linked package that rebuilds the linked package (shared library) whenever a change in the source code occurs:
"watch": "vite build --watch"
After this watcher finishes its build, another watcher in the main app triggers a build in the main app. I don't claim it's a perfect solution, but it works. It is slower than restarting a development server, and since it performs a production build, it might not be ideal.
Waiting for a best practice.🤖
Chokidar v4 is going to drop support for globs altogether (see https://github.com/paulmillr/chokidar/issues/1225). So this is now firmly in vite's arena. Vite now has the opportunity to show how this can be done right and hopefully other tools will follow vite's example.
As far as I can work out, for any kind of include/exclude system to avoid this problem you need 3 options:
This works pretty much as it does now, exclude
would default to node_modules/**
, and include
defaults to **
. But the user can also specify forceInclude
to override things that are excluded.
Different naming or other combinations of 3 options may be possible (I considered defaultExclude
, include
and exclude
but that doesn't work so well), but you need minimum 3 or you just end up with this problem where you can't include things within node_modules
sanely.
Obviously there are more complicated options which would also work, such as an ordered list of entries with type: include/exclude
against each of them.
The workaround with patching watcher options in configureServer can be improved a little by replacing the existing node_modules ignore, so that other custom ignores are preserved:
function pluginWatchNodeModules(modules) {
let pattern = `/node_modules\\/(?!${modules.join('|')}).*/`;
return {
name: 'watch-node-modules',
configureServer: (server: ViteDevServer) : void => {
const node_modules_i = server.watcher.options.ignored.indexOf('**/node_modules/**');
server.watcher.options.ignored.splice(node_modules_i,1,new RegExp(pattern));
server.watcher._userIgnored=undefined;
},
}
}
This does give you file change events for files in the modules listed and triggers vite hmr, however plugins handleHotUpdate hook can get called with an incomplete context if you are using pnpm due to a mismatch between the symlink and real path of the module inside node_modules.
For that to work, vite needs to update https://github.com/vitejs/vite/blob/b55c32f7e36ee7cc3754a5d667785d066dece10a/packages/vite/src/node/server/hmr.ts#L120 to use the real path of file
before finding affected modules.
If you are creating vite build -w --mode lib
to the lib directory and debug from vite
CLI
Then you will frustrated to find that the page doesn't find the latest lib! But you can try it as follows:
--force
flag for scripts-dev in package.jsonoptimizeDeps.exclude = [<your-package>]
in vite.config.tsserver.watch.ignored= ['!**/dist/**']
in vite.config.tsIt's work on my case
@SystemParadox
If it's any help, creating a custom plugin to override the vite-enforced watch options seems to have worked for me
{ name: 'watch-node-modules', configureServer: (server: ViteDevServer) : void => { server.watcher.options = { ...server.watcher.options, ignored: [ /node_modules\/(?!my-package-name).*/, '**/.git/**', ] } } }
MAC is successful, Windows is invalid Does vite accept pull requests to fix this issue first?
Tried everything on this page and could not get a dependency (workspace: or portal: in Yarn) to correctly HMR, or even refresh the cache to take changes in the dependency. Going to have to move away from the dev server to a much worse dummy Express server and do a vite build --watch
. Hopefully we can get a feature added for this soon.
Has anyone got this working with a scoped package (@myscope/package
)?
Describe the bug
The documentation for server.watch contains the following example:
This example does not work. It appears that the builtin
**/node_modules/**
exclude causes chokidar to not even look innode_modules
despite the negation in the subdirectory.It appears this was originally tested (see #5023 and #5239) with
ignored: ['!**/node_modules/**']
. This does work, but in a real project will almost immediately result inError: ENOSPC: System limit for number of file watchers reached
.See https://github.com/paulmillr/chokidar/issues/1225. I played with various chokidar options but I couldn't see a way to achieve this.
Reproduction
See chokidar issue.
System Info
Used Package Manager
npm
Logs
No response
Validations