angularwebpackwebpack-dev-serverlivereloadhot-module-replacement

Webpack Live Reload is always one step behind


I've just integrated "Webpack" into my newly created Angular project.

Question:

1) After running the command "npm run start", the web can be displayed properly. However, upon modifying my files, the web does refreshes itself, but the changes are not reflected. ONLY when I make one more changes, my initial changes gets shown. And the second changes is still left behind.

TLDR; webpack-dev-server seems to be one step slower to reflect my changes. Why is that?

So far the modified files are as below:

Below is my "webpack.config.js" file.

const webpack = require('webpack');
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const {AngularCompilerPlugin} = require('@ngtools/webpack');

module.exports = {
  entry: {
    main: ['./src/main.ts'],
    polyfills: ['./src/polyfills.ts']
  },
  output: {
    path: path.join(process.cwd(), 'dist'),
    filename: '[name].bundle.js'
  },
  resolve: {
    extensions: [' ', '.ts', '.js']
  },
  module: {
    rules: [
      {
        test: /\.html$/,
        loader: "raw-loader"
      },
      {
        test: /\.ts$/,
        loader: "@ngtools/webpack"
      },
      {
        test: /\.css$/,
        exclude: /node_modules/,
        use: [
          'css-to-string-loader',
          {loader: 'css-loader', options: {sourceMap: true}},
          {loader: 'sass-loader', options: {sourceMap: true}}
        ]
      }
    ]
  },
  devServer: {
    contentBase: './dist',
    hot: true
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: __dirname + '/src/index.html',
      output: __dirname + '/dist',
      hash: false,
      inject: true,
      compile: true,
      favicon: false,
      minify: false,
      cache: true,
      showErrors: true
    }),
    new AngularCompilerPlugin({
      tsConfigPath: 'tsconfig.json',
      mainPath: __dirname + '/src/main.ts',
      sourceMap: true
    })
  ]
};

Below is my "package.json" file.

{
  "name": "testwebpack",
  "version": "0.0.0",
  "scripts": {
    "ng": "ng",
    "start": "webpack-dev-server --content-base src --hot --inline --port=4200",
    "build": "webpack",
    "test": "ng test",
    "lint": "ng lint",
    "e2e": "ng e2e"
  },
  "private": true,
  "dependencies": {
    "@angular/animations": "~8.2.3",
    "@angular/common": "~8.2.3",
    "@angular/compiler": "~8.2.3",
    "@angular/core": "~8.2.3",
    "@angular/forms": "~8.2.3",
    "@angular/platform-browser": "~8.2.3",
    "@angular/platform-browser-dynamic": "~8.2.3",
    "@angular/router": "~8.2.3",
    "rxjs": "~6.4.0",
    "tslib": "^1.10.0",
    "zone.js": "~0.9.1"
  },
  "devDependencies": {
    "@angular-devkit/build-angular": "~0.803.0",
    "@angular/cli": "^8.3.1",
    "@angular/compiler-cli": "~8.2.3",
    "@angular/language-service": "~8.2.3",
    "@angularclass/hmr": "^2.1.3",
    "@ngtools/webpack": "^8.3.1",
    "@types/core-js": "^2.5.2",
    "@types/jasmine": "~3.3.8",
    "@types/jasminewd2": "~2.0.3",
    "@types/node": "~8.9.4",
    "angular2-template-loader": "^0.6.2",
    "codelyzer": "^5.0.0",
    "core-js": "^3.2.1",
    "css-loader": "^3.2.0",
    "css-to-string-loader": "^0.1.3",
    "hot-module-replacement": "^3.0.3",
    "html-loader": "^0.5.5",
    "html-webpack-plugin": "^3.2.0",
    "jasmine-core": "~3.4.0",
    "jasmine-spec-reporter": "~4.2.1",
    "karma": "~4.1.0",
    "karma-chrome-launcher": "~2.2.0",
    "karma-coverage-istanbul-reporter": "~2.0.1",
    "karma-jasmine": "~2.0.1",
    "karma-jasmine-html-reporter": "^1.4.0",
    "protractor": "~5.4.0",
    "raw-loader": "^3.1.0",
    "sass-loader": "^7.3.1",
    "to-string-loader": "^1.1.5",
    "ts-loader": "^6.0.4",
    "ts-node": "~7.0.0",
    "tslint": "~5.15.0",
    "typescript": "~3.5.3",
    "webpack": "^4.39.3",
    "webpack-cli": "^3.3.7",
    "webpack-dev-server": "^3.8.0"
  }
}

Below is my "tsconfig.json" file.

{
  "compileOnSave": false,
  "compilerOptions": {
    "baseUrl": "./",
    "outDir": "./dist/out-tsc",
    "sourceMap": true,
    "declaration": false,
    "downlevelIteration": true,
    "experimentalDecorators": true,
    "module": "esnext",
    "moduleResolution": "node",
    "importHelpers": true,
    "emitDecoratorMetadata": true,
    "target": "es2015",
    "typeRoots": [
      "node_modules/@types"
    ],
    "lib": [
      "es2018",
      "dom"
    ]
  },
  "angularCompilerOptions": {
    "fullTemplateTypeCheck": true,
    "strictInjectionParameters": true
  }
}

Solution

  • For some reason, the below code works:

    webpack.config.js

    const webpack = require('webpack');
    const path = require('path');
    const HtmlWebpackPlugin = require('html-webpack-plugin');
    const {AngularCompilerPlugin} = require('@ngtools/webpack');
    const CopyWebpackPlugin = require('copy-webpack-plugin');
    
    module.exports = {
      entry: {
        main: ['./src/main.ts'],
        polyfills: ['./src/polyfills.ts']
      },
      output: {
        path: path.join(process.cwd(), 'dist'),
        filename: '[name].bundle.js'
      },
      resolve: {
        extensions: [' ', '.ts', '.js']
      },
      module: {
        rules: [
          {test: /\.html$/, loader: 'raw-loader'},
          {test: /\.ts$/, loader: '@ngtools/webpack'},
          {test: /\.css$/, exclude: /node_modules/,
          use: [
            'css-to-string-loader',
            {loader: 'css-loader', options: {sourceMap: true}},
            {loader: 'sass-loader', options: {sourceMap: true}}
          ]}
        ]
      },
      plugins: [
        new HtmlWebpackPlugin({
          template: './src/index.html',
          output: '/dist',
          inject: true
        }),
        new AngularCompilerPlugin({
          tsConfigPath: 'tsconfig.json',
          mainPath: './src/main.ts',
          sourceMap: true
        }),
        new CopyWebpackPlugin([
          {
            "context": "src",
            "to": "",
            "from": {
              "glob": "assets/**/*",
              "dot": true
            }
          },
          {
            "context": "src",
            "to": "",
            "from": {
              "glob": "favicon.ico",
              "dot": true
            }
          }
        ])
      ]
    };
    


    polyfills.ts

    import 'core-js/features/reflect';
    import 'zone.js/dist/zone';
    


    main.ts

    import { enableProdMode } from '@angular/core';
    import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
    import { AppModule } from './app/app.module';
    import { environment } from './environments/environment';
    import './polyfills';
    
    if (environment.production) {
      enableProdMode();
    }
    platformBrowserDynamic().bootstrapModule(AppModule)
      .catch(err => console.error(err));