webpack / webpack-dev-server

Serves a webpack app. Updates the browser on changes. Documentation https://webpack.js.org/configuration/dev-server/.
MIT License
7.78k stars 1.43k forks source link

webpack-dev-server goes into infinite compile loop after making the smallest change to the source code #4362

Closed OleksiL closed 7 months ago

OleksiL commented 2 years ago

Hi Guys.

I have an issue when webpack-dev-server goes into infinite compile loop after any change to the source code. The same code works without this issue for my colleague. And it doesn't go into infinite loop every time: sometimes it will compile only once and stop (so works as it should), but sometimes it goes into frenzy mode. In the the verbose log attached on the first change it compiled only once and stopped, but after I did another change it went into infinite loop. webpack-dev-server.log

I've added my package.json, package-lock.json, and webpackfolder to the zip file webpack-dev-server-infinite-loop.zip

    "webpack-cli": "4.9.2",
    "webpack-dev-server": "3.11.2",
    "webpack-merge": "5.8.0",
    "webpack-notifier": "1.15.0",
    "workbox-webpack-plugin": "6.5.1"

Symptoms: When I make a change to the component - webpack-dev-server recompiles it, but doesn't stop after the first cycle and keeps doing it: code-change-example-causing-it browser-console intellij-console

I've tried to play with webpack related libraries versions, npm and node versions, clearing all caches for gradle, maven, intellij. Modified webpack configuration files to exclude things from watched. Removed and cloned the repository. Generated a new appliation with JHipster and compared generated webpack configs with mine, tried to make different changes. Nothing helps.

alexander-akait commented 2 years ago

Please firstly update webpack-dev-server to v4

OleksiL commented 2 years ago

Please firstly update webpack-dev-server to v4

This was, actually, how I noticed the issue: I was in the process of updating front-end libraries and migrated webpack-dev-server from v3 to v4. Later when testing it I found that it goes into infinite loop. At first I thought that something was wrong with my webpack configs and I modified them many times. Couldn't resolve it and after few days switched back to the previous version. But then I noticed that the issue still exists. After that I checked out few months old code from master branch, cleared all caches, removed all libraries, let npm and gradle to re-download all dependencies and even downgraded intellij version. It didn't help. The same source code works without issue for my colleague :( I start thinking that it is not webpack related, but something on my pc intervenes and modifies the files and in this way triggers recompiling. Like antivirus, but I don't have any... Any ides what it can be?

alexander-akait commented 2 years ago

What is os? Please use the issue template in future, there are minimum questions which we need

OleksiL commented 2 years ago

Windows 10. I scanned issue template, but maybe not thoroughly enough.

alexander-akait commented 2 years ago

Can you remove webpack.HotModuleReplacementPlugin, when you set hot: true, webpack-dev-server automatically apply this plugin, anyway can you provide example with code (no need to copy all code, only for showing the problem)

OleksiL commented 2 years ago

I've solved it by changing ending of one word: ignore -> ignored :

      watchOptions: {  
        ignored: ['**/node_modules', utils.root('src/test')]
      },

I don't know how could it work correctly for everyone else with just ignore because docs tell to use ignored. I also had to change regexp values in the array because it started complaining about them after I'd made a change. Maybe there are two different ways to ignore files and only one of them worked in my case.

alexander-akait commented 2 years ago

Do you change this options in dev server options? Because we have validation, so you should get an error...

OleksiL commented 2 years ago

Yes, in devServer options. I am using these versions of libraries:

    "webpack": "5.39.0",
    "webpack-cli": "4.7.2",
    "webpack-dev-server": "3.11.2",

Now it looks like this:

devServer: {
      stats: options.stats,
      hot: true,
      contentBase: './build/resources/main/static/',
      port: 9060,
      proxy: [
        {
          context: ['/api', '/services', '/management', '/swagger-resources', '/v2/api-docs', '/v3/api-docs'],
          target: `http${options.tls ? 's' : ''}://localhost:8080`,
          secure: false,
          changeOrigin: options.tls
        }
      ],
      watchOptions: {
        ignored: ['**/node_modules', utils.root('src/test')]
      },
      https: options.tls,
      historyApiFallback: true
    }

I didn't have any validation errors neither now nor then. But I had to change regexps after switching from ignore to ignored. Should I have validation errors before the change or after it?

alexander-akait commented 2 years ago

Yes, it should be validation error, @snitin315 can you look at this?

snitin315 commented 2 years ago

Yes. I will look into this.

snitin315 commented 2 years ago

So, this config is for v3, in which we had watchOptions property with type: object only, hence there was no error.

https://github.com/webpack/webpack-dev-server/blob/aa3cddcf6eb2347704870f0e0cf33bc211e2a378/lib/options.json#L417-L420

In v4 we moved this to static.watch which also has type: object only, we have not defined specific property because options are from chokidar which can change anytime and we will have to change them again in our schema.

https://github.com/webpack/webpack-dev-server/blob/7c1d680008c986ed4721095c4babfa802ff31445/lib/options.json#L996-L1014

snitin315 commented 2 years ago

Similar is the case with other options as well which uses external API like historyAPIFallback, bonjour etc

alexander-akait commented 2 years ago

We need to add additionalProperties: false, let's do it

marekdedic commented 1 year ago

Hi, I'm getting this problem without having the devServer key in my config at all...

The repo is skaut/shared-drive-mover

bogdan-panteleev commented 1 year ago

Same for me. Just configured webpack as follows. And if I run webpack --watch, then I have webpack recompiling the project endlessly.

Eventually I just switched to ESBuild.

package.json

{
  "name": "grokking_algorithms",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "binary_search": "cd src && tsc binary-search.ts && node binary-search.js",
    "start": "webpack --watch"
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@types/node": "^20.1.1",
    "prettier": "^2.8.8",
    "ts-loader": "^9.4.2",
    "ts-node": "^10.9.1",
    "tsconfig-paths": "^4.2.0",
    "typescript": "^5.0.4",
    "webpack": "^5.82.0",
    "webpack-cli": "^5.1.0"
  }
}

webpack.config.js

const webpack = require('webpack');
const path = require('path');

module.exports = {
  entry: './src/main.ts',
  mode: 'development',
  target: 'node',
  module: {
    rules: [
      {
        test: /\.ts$/,
        loader: 'ts-loader',
        exclude: [/test/, /\.spec\.ts$/, /\.e2e-spec\.ts$/],
      },
    ],
  },
  resolve: {
    extensions: ['.ts', '.json', '.js'],
  },
  plugins: [],
  output: {
    path: path.join(__dirname, 'dist'),
    filename: 'main.js',
    library: {
      name: 'handler',
      type: 'umd',
    },
  },
  externals: [],
  optimization: {
    minimize: false,
  },
};
samir-kamble commented 1 year ago

Same for me. Just configured webpack as follows. And if I run webpack --watch, then I have webpack recompiling the project endlessly.

Eventually I just switched to ESBuild.

package.json

{
  "name": "grokking_algorithms",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "binary_search": "cd src && tsc binary-search.ts && node binary-search.js",
    "start": "webpack --watch"
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@types/node": "^20.1.1",
    "prettier": "^2.8.8",
    "ts-loader": "^9.4.2",
    "ts-node": "^10.9.1",
    "tsconfig-paths": "^4.2.0",
    "typescript": "^5.0.4",
    "webpack": "^5.82.0",
    "webpack-cli": "^5.1.0"
  }
}

webpack.config.js

const webpack = require('webpack');
const path = require('path');

module.exports = {
  entry: './src/main.ts',
  mode: 'development',
  target: 'node',
  module: {
    rules: [
      {
        test: /\.ts$/,
        loader: 'ts-loader',
        exclude: [/test/, /\.spec\.ts$/, /\.e2e-spec\.ts$/],
      },
    ],
  },
  resolve: {
    extensions: ['.ts', '.json', '.js'],
  },
  plugins: [],
  output: {
    path: path.join(__dirname, 'dist'),
    filename: 'main.js',
    library: {
      name: 'handler',
      type: 'umd',
    },
  },
  externals: [],
  optimization: {
    minimize: false,
  },
};

Did this stopped infinite loop problem ? Also, I did not see webpack.config.js file. I have webpack.custom.js file.

Do I need to create a file with that name?

samir-kamble commented 1 year ago

We need to add additionalProperties: false, let's do it

Where do we need to add it?

vbansal2 commented 10 months ago

is there any update on this issue?

OZZlE commented 8 months ago

watchpack

I tried even setting watch to false and it still does it

alexander-akait commented 8 months ago

There are a lot of different problems here, please provide reproducible test repo before ask a help, without it we can't help you

OZZlE commented 8 months ago

package.json

"scripts": {
    "webpack-dev-server": "webpack-dev-server --env=epidev --config webpack.config.dev.js",
},
"devDependencies": {
  "@babel/core": "^7.20.5",
  "@babel/plugin-proposal-class-properties": "^7.18.6",
  "@babel/plugin-transform-runtime": "^7.19.6",
  "@babel/preset-env": "^7.20.2",
  "@babel/preset-react": "^7.18.6",
  "@frctl/fractal": "^1.5.14",
  "@frctl/nunjucks": "^1.0.3",
  "@testing-library/jest-dom": "^5.16.5",
  "@testing-library/react": "^13.4.0",
  "autoprefixer": "^10.4.13",
  "babel-eslint": "^10.1.0",
  "babel-loader": "^9.1.0",
  "cpx": "^1.5.0",
  "css-loader": "^6.7.2",
  "css-mediaquery": "^0.1.2",
  "cssnano": "^5.1.14",
  "eslint": "^7.32.0",
  "eslint-config-airbnb-base": "^15.0.0",
  "eslint-loader": "^4.0.2",
  "eslint-plugin-import": "^2.26.0",
  "eslint-plugin-react": "^7.31.11",
  "file-loader": "^6.2.0",
  "husky": "^8.0.2",
  "jest": "^29.3.1",
  "jest-environment-jsdom": "^29.3.1",
  "lint-staged": "^13.1.0",
  "mini-css-extract-plugin": "^2.7.2",
  "npm-run-all": "^4.1.5",
  "postcss": "^8.4.20",
  "postcss-cli": "^10.1.0",
  "postcss-flexbugs-fixes": "^5.0.2",
  "postcss-focus": "^5.0.1",
  "postcss-loader": "^7.0.2",
  "prettier": "^2.8.1",
  "script-loader": "^0.7.2",
  "style-loader": "^3.3.1",
  "stylelint": "^14.16.0",
  "stylelint-config-recommended-scss": "^8.0.0",
  "stylelint-scss": "^4.3.0",
  "stylelint-webpack-plugin": "^3.3.0",
  "svg-sprite-loader": "^6.0.11",
  "svgo": "^3.0.2",
  "svgo-loader": "^3.0.3",
  "webpack": "^5.75.0",
  "webpack-bundle-analyzer": "^4.7.0",
  "webpack-cli": "^5.0.1",
  "webpack-dev-server": "^4.11.1",
  "webpack-merge": "^5.8.0"
},
"engines": {
  "node": ">=16"
},
"dependencies": {
  "@babel/plugin-proposal-decorators": "^7.20.5",
  "animate.css": "^4.1.1",
  "babel-polyfill": "^6.26.0",
  "body-scroll-lock": "^4.0.0-beta.0",
  "focus-trap": "^7.2.0",
  "foundation-sites": "^6.7.5",
  "iframe-resizer": "^4.3.2",
  "ion-rangeslider": "^2.3.1",
  "jquery": "^3.6.2",
  "jquery-modal": "^0.9.2",
  "jquery.scrollto": "^2.1.3",
  "js-cookie": "^3.0.1",
  "mobx": "^6.7.0",
  "mobx-react": "^7.6.0",
  "picturefill": "^3.0.3",
  "rangeslider.js": "^2.3.3",
  "react": "^18.2.0",
  "react-dom": "^18.2.0",
  "react-modal": "^3.16.1",
  "react-responsive": "^9.0.2",
  "sanitize.css": "^13.0.0",
  "sass": "^1.69.5",
  "sass-loader": "^13.3.2",
  "scrollprogress": "^3.0.2",
  "slick-carousel": "^1.8.1",
  "stickyfilljs": "^2.1.0",
  "svgxuse": "^1.2.6",
  "universal-cookie": "^4.0.4",
  "whatwg-fetch": "^3.6.2"
}

webpack.config.dev.js

const path = require('path');
const webpack = require('webpack');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const SpriteLoaderPlugin = require('svg-sprite-loader/plugin');
const StyleLintPlugin = require('stylelint-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');

/*eslint-disable */
var dirname = __dirname;
/*eslint-enable */

const resolve = {
  modules: [
    // path.resolve(dirname, 'components/atoms/'),
    // path.resolve(dirname, 'components/molecules/'),
    // path.resolve(dirname, 'components/organisms/'),
    // path.resolve(dirname, 'static/scripts'),
    // path.resolve(dirname, 'static/styles'),
    // path.resolve(dirname, 'static'),
    // path.resolve(dirname, 'node_modules/foundation-sites/scss'),
    'node_modules',
  ],
  extensions: ['.js', '.jsx'],
};
const eslintRule = {
  enforce: 'pre',
  test: /\.js$/,
  exclude: /node_modules/,
  loader: 'eslint-loader',
  options: {
    configFile: '.eslintrc.js',
  },
};
const getScssRule = (env) => {
  return {
    test: /\.scss$/,
    use: [
      {
        loader: 'style-loader',
      },
      'css-loader',
      {
        loader: 'postcss-loader',
        options: {
          postcssOptions: {
            plugins: [
              /* eslint-disable global-require */
              require('postcss-focus')(),
              require('autoprefixer')({ grid: true }),
              require('cssnano')(),
              /* eslint-enable global-require */
            ],
          },
        },
      },
      {
        loader: 'sass-loader',
        options: {
          sassOptions: {
            includePaths: ['node_modules'],
          }
        },
      },
    ],
  }
};
const fileRule = {
  test: /\.(svg|png|jpe?g|gif|woff|woff2|eot|ttf|otf)$/,
  // exclude: [
  //   path.resolve('./static/icons/**/*'),
  //   path.resolve('./static/fonts/icons.svg'),
  // ],
  exclude: path.resolve('./static/icons'),
  use: [
    {
      loader: 'file-loader',
      options: {
        name: '[name].[ext]',
        outputPath: 'static/fonts/',
      },
    },
  ],
};
const svgSpriteRule = {
  test: /\.svg$/,
  include: path.resolve('./static/icons'),
  use: [
    {
      loader: 'svg-sprite-loader',
      options: {
        extract: true,
        spriteFilename: 'static/images/icons.svg',
        esModule: false,
      },
    },
    'svgo-loader',
  ],
};

const moduleConfig = (origEnv) => {
  var apiEnvTarget = 'production';
  var env = 'development';

  return {
    mode: 'development',
    // devtool: origEnv.epi ? 'source-map' : '',
    resolve,
    entry: {
      main: [
        path.resolve(dirname, 'static/styles/main.scss')
      ],
      react: [

        // was main
        path.resolve(dirname, 'static/scripts/main.js'),
        // path.resolve(dirname, 'static/styles/main.scss'),

        path.resolve(dirname, 'static/scripts/react-bundle.js')
      ],
    },
    output: {
      path: path.resolve(dirname, 'dist'),
      publicPath: '/',
      filename: './static/scripts/[name].module.js',
      clean: false,
    },
    module: {
      rules: [
        eslintRule,
        {
          test: /\.(js|jsx)$/,
          exclude: /node_modules\/(?![foundation-sites])/,
          loader: 'babel-loader',
          options: { 
            "presets": [
              [
                "@babel/preset-env",
                {
                  "targets": { "esmodules": true },
                }
              ],
              "@babel/preset-react"
            ],
          }
        },
        getScssRule(env),
        fileRule,
        svgSpriteRule,
      ],
    },
    plugins: [
      new StyleLintPlugin({
        configFile: './.stylelintrc',
      }),
      new MiniCssExtractPlugin({
        filename: './static/styles/[name].css',
      }),
      new SpriteLoaderPlugin(),
      new webpack.ProvidePlugin({
        $: 'jquery',
        jQuery: 'jquery',
        foundation: 'Foundation'
      }),
      // this is used to import environment specific modules like api request etc, ie api will be mocked in development
      new webpack.NormalModuleReplacementPlugin(/(.*)-ENVIRONMENT_TARGET(\.*)/, function(resource) {
        resource.request = resource.request.replace(/-ENVIRONMENT_TARGET/, `-${apiEnvTarget}`);
      }),
      new HtmlWebpackPlugin({
        title: 'Development',
      })      
    ],
    devServer: {
      watchFiles: [
        /components\//,
        /static\/[^/]*\/*.js/,
        /static\/[^/]*\/*.scss/,
      ],
      static: {
        watch: {
          usePolling: false,
          ignored: [
            /dist\//, 
            /docs\//, 
            /node_modules\//,
            /public\//, 
            /static\/icons\//, 
            /static\/fonts\//, 
          ],
        },
      },      
      devMiddleware: {
        index: true,
        mimeTypes: { phtml: 'text/html' },
        publicPath: path.join(dirname, 'public'),
        // publicPath: '/',
        serverSideRender: false,
        writeToDisk: true,
      },
    },
    optimization: {
      splitChunks: {
        cacheGroups: {
          styles: {
            name: 'styles',
            test: /\.css$/,
            chunks: 'all',
            enforce: true,
          },
        },
      },
    },
    watch: false,
    watchOptions: {
      ignored: [
        'dist/**/*', 
        'docs/**/*', 
        'node_modules/**/*',
        'public/**/*', 
        'static/icons/**/*', 
      ],
    },    
    stats: {warnings:false},
  };
}
module.exports = function config(env, argv = {}) {
  return moduleConfig({
    ...env,
    epi: true,
    epidev: true,
  }, argv);
};
alexander-akait commented 7 months ago

@OZZlE What are steps to reproduce the problem? I see you have writeToDisk: true and

watchFiles: [
        /components\//,
        /static\/[^/]*\/*.js/,
        /static\/[^/]*\/*.scss/,
      ],

please make sure you ignore generated webpack file, otherwise you can have a loop of reloading

alexander-akait commented 7 months ago

The original problem was resolved. Anyway feel free to feedback