Skip to content

Watcher doesn't see a new file until restart #6262

Description

@gaearon

Do you want to request a feature or report a bug?

Bug.

What is the current behavior?

Consider this project:

package.json

{
  "scripts": {
    "start": "webpack --watch src/index.js dist/bundle.js"
  },
  "devDependencies": {
    "webpack": "^3.10.0"
  }
}

src/index.js

import x from './x';
alert(x);

src/x.js

export default 42;

Then do this series of steps:

  1. Run npm install.
  2. Run npm start.
  3. Delete src/x.js.
  4. Create src/x folder.
  5. Create src/x/index.js with same content as src/x.js used to have.

What is the expected behavior?

Webpack recovers from a "not found" error. At the very least after re-saving either src/index.js or src/x/index.js.

Actual behavior is that Webpack gets stuck insisting ./x is not found, and the only way to fix it is by restarting the watcher.

Please mention other relevant information such as the browser version, Node.js version, webpack version and Operating System.

OS X Sierra, Node 8.9.1 (but it's an old issue, existed at least since 2016 react/create-react-app#1164).

I couldn't figure out how to correctly set up Webpack beta so I didn't test that.

Activity

  1. sokra commented on Jan 8, 2018

    @sokra
    Member

    This is caused by the "unsafe" resolver cache. You can disable the cache with resolve.unsafeCache: false but this has very negative impact on performance.

  2. gaearon commented on Jan 8, 2018

    @gaearon
    ContributorAuthor

    Is there any middle ground or a heuristic we could use? For example, if a file is not found, then invalidate any cache entries around it on any FS event.

  3. bpartridge commented on Jan 13, 2018

    @bpartridge

    This seems to be the same root issue as #5523, and we're running into it as well when gradually refactoring a codebase (in our case, the watcher starts reporting ENOENT when you rename x.js to x.ts or similar).

    It seems that this is where the cache entry is saved (the resolve.unsafeCache setting is what determines the cachePredicate):

    if(module && this.cachePredicate(module)) {
    dependencies.forEach(d => d.__NormalModuleFactoryCache = module);

    And the cache entry is read here:

    const dependencies = data.dependencies;
    const cacheEntry = dependencies[0].__NormalModuleFactoryCache;
    if(cacheEntry) return callback(null, cacheEntry);

    Perhaps one way to achieve this with a minimal performance overhead would be to have a synchronous plugin tap immediately after a cache entry is read or created? Then a new plugin, only enabled during dev (and perhaps enabled by default?), could then:

    • run an asynchronous NodeFileWatcher
    • maintain a weak trie of the cache entries
    • mark parts of that trie as invalidated when a filesystem change is detected
    • quickly check the trie synchronously before returning a module from cache (i.e. "check-cache-entry-validity" as a plugin tap point)

    This way you wouldn't need to completely turn off the caching, and overhead would be minimal - no need to go out to every resolver out there. And once the API hooks are inserted, the proposed plugin could be developed in a separate repository to meet all these edge cases, so that ongoing webpack core development isn't affected.

    Am I missing anything here? Any reason not to try this approach?

  4. rhys-vdw commented on Feb 14, 2018

    @rhys-vdw

    Also suffering from this issue at present while slowly migrating from JS to TS. Branch changes are particularly annoying as they nearly always involve renames. I tried to unsafeCache: false and it didn't appear to work (although I'm not sure if this might be a symptom of other plugins or configuration).

  5. ZuBB commented on Feb 15, 2018

    @ZuBB

    I may be wrong but looks like next PR should fix this #6451

    would be nice to get some confirmation of my guess from core team/major contributors..

  6. wujashek commented on Feb 28, 2018

    @wujashek

    Actually after spending few hours on this, I'm quite sure unsafeCache option is actually in both resolve and in module section of configuration. Second place is undocumented.

    At least for v3.x,

    class NormalModuleFactory extends Tapable {
    	constructor(context, resolvers, options) {
    		super();
    		this.resolvers = resolvers;
    		this.ruleSet = new RuleSet(options.rules || options.loaders);
    		this.cachePredicate = typeof options.unsafeCache === "function" ? options.unsafeCache : Boolean.bind(null, options.unsafeCache);`

    And it can be actually a function and then it is a predicate.
    Maybe I'm not aware of sth, otherwise it's quite confusing in the docs, I can create a PR if it's the case ?

  7. soda-x commented on Jul 31, 2018

    @soda-x

    same issue here

  8. Lukortech commented on Aug 27, 2019

    @Lukortech

    I am going to hop on the train and mention it really slows down my developement since no changes detected causes the need to restart and open new server for my react-app each time I run into this.

  9. sokra commented on Aug 27, 2019

    @sokra
    Member

    webpack 5 has this problem fixed

  10. minimit commented on Nov 21, 2019

    @minimit

    In webpack 5 it's not fixed when using multiple aliases #10015

  11. raminrez commented on Dec 22, 2019

    @raminrez

    @gaearon @minimit we have the same issue when using multiple aliases

  12. minimit commented on Dec 22, 2019

    @minimit

    @gaearon @minimit we have the same issue when using multiple aliases

    it's getting worked on webpack 5 #9802 (comment)

  13. vankop commented on Aug 9, 2020

    @vankop
    Contributor

    Should work with webpack@next. Feel free to report new issue with reproducible repo.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    Priority

    None yet

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions