Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to watch and reload index.html by using webpack-dev-server together with html-webpack-plugin #100

Closed
elaijuh opened this issue Oct 17, 2015 · 29 comments

Comments

@elaijuh
Copy link

elaijuh commented Oct 17, 2015

Every time I change the index.html template, I need to re-run the build task now.

@jantimon
Copy link
Owner

Hmm this should work - could you please try the 2.0 version or provide a demo?

@viko16
Copy link

viko16 commented Dec 29, 2015

Same problem i met.

When i run webpack-dev-server --inline --hot --colors --content-base=./www, it prints webpack: bundle is now INVALID.

plugin version:

"html-webpack-plugin": "^2.1.0",
"webpack-dev-server": "^1.14.0"

@uinz
Copy link

uinz commented Feb 25, 2016

Same

var webpack            = require('webpack')
  , path               = require('path')
  , HtmlWebpackPlugin  = require('html-webpack-plugin')
  , CleanWebpackPlugin = require('clean-webpack-plugin')
  , ExtractTextPlugin  = require("extract-text-webpack-plugin")
  , autoprefixer       = require('autoprefixer')

module.exports = {
  devServer: {
    historyApiFallback: true,
    hot: true,
    inline: true,
    progress: true,
    contentBase: './app',
    port: 8080,
  },

  entry: [
    path.resolve(__dirname, 'app/src/'),
  ],

  output: {
    path: path.resolve(__dirname, 'app/build/'),
    filename: './js/bundle?[hash].js'
  },

  module: {
    loaders: [
      {
        test: /\.jade$/,
        loader: 'jade'
      },

      {
        test: /\.js$/,
        include: path.resolve(__dirname, 'app/src'),
        exclude: /node_modules/,
        loader: 'babel'
      },

      {
        test: /\.styl$/,
        include: path.resolve(__dirname, 'app/src'),
        loader: ExtractTextPlugin.extract("style-loader", "css-loader!postcss-loader!stylus-loader")
      },

      {
        test: /\.css$/,
        loader: ExtractTextPlugin.extract("style-loader", "css-loader!postcss-loader")
      },
    ]
  },

  postcss: [ autoprefixer ],

  plugins: [
    new ExtractTextPlugin("./css/style?[hash].css"), // separate css

    new CleanWebpackPlugin(['app/build'], {
      verbose: true, 
      dry: false
    }),

    new webpack.optimize.UglifyJsPlugin({
      compress: {
        warnings: false
      }
    }),

    new HtmlWebpackPlugin({
      filename: 'index.html',
      template: './app/src/jade/index.jade',
      inject: 'body',
    }),

    new HtmlWebpackPlugin({
      filename: 'homepage.html',
      template: './app/src/jade/homepage.jade',
      inject: 'body',
    }),
  ]
}

webpack-dev-server --devtool eval --progress --hot --colors --host 0.0.0.0 --content-base app

webpack: bundle is now INVALID.
Hash: 020f485713bee7553bdf  
Version: webpack 1.12.14
Time: 154ms
                               Asset       Size  Chunks             Chunk Names
./css/style?020f485713bee7553bdf.css    1.93 kB       0  [emitted]  main
                          index.html  297 bytes          [emitted]  
chunk    {0} ./js/bundle?020f485713bee7553bdf.js, ./css/style?020f485713bee7553bdf.css (main) 213 kB
     + 79 hidden modules
Child html-webpack-plugin for "homepage.html":
    chunk    {0} homepage.html 6.11 kB
         + 3 hidden modules
Child html-webpack-plugin for "index.html":
    chunk    {0} index.html 6.1 kB [rendered]
        [0] ./~/html-webpack-plugin/lib/loader.js!./app/src/jade/index.jade 394 bytes {0} [built]
         + 2 hidden modules
webpack: bundle is now VALID.
  "devDependencies": {
    "autoprefixer": "^6.3.3",
    "babel-loader": "^6.2.3",
    "babel-preset-es2015": "^6.5.0",
    "babel-preset-stage-0": "^6.5.0",
    "clean-webpack-plugin": "^0.1.8",
    "css-loader": "^0.23.1",
    "extract-text-webpack-plugin": "^1.0.1",
    "file-loader": "^0.8.5",
    "html-webpack-plugin": "^2.9.0",
    "jade": "^1.11.0",
    "jade-loader": "^0.8.0",
    "jquery": "^2.2.1",
    "normalize.css": "^3.0.3",
    "postcss-loader": "^0.8.1",
    "precss": "^1.4.0",
    "style-loader": "^0.13.0",
    "stylus-loader": "^1.5.1",
    "webpack-dev-server": "^1.14.1"
  }

@epoch
Copy link

epoch commented May 12, 2016

"html-webpack-plugin": "^2.8.1",
"webpack-dev-server": "^1.14.1"

getting the same error

@usergit
Copy link

usergit commented Oct 17, 2016

any updates to this issue?

@jantimon
Copy link
Owner

@usergit I am sorry but I really don't get the problem - every thing works as expected.

You change the html file, webpack sets the state to invalid and recompiles everything.

Only hot-module replacement is not possible.

@viko16
Copy link

viko16 commented Nov 2, 2016

😨 unfortunately and this is the things i met.

When i change the html file, webpack does recompiled but page still have no refresh action.

  "devDependencies": {
    "html-webpack-plugin": "^2.24.1",
    "webpack": "^1.13.2",
    "webpack-dev-server": "^1.16.2"
  }
  plugins: [
    new HtmlWebpackPlugin({
      template: path.resolve(__dirname, 'index.html')
    })
  ]

webpack-dev-server --inline --hot

@jantimon
Copy link
Owner

jantimon commented Nov 2, 2016

@viko16 there is no auto refresh by now - would be open for pull requests :)

@MichalLytek
Copy link

MichalLytek commented Nov 6, 2016

Temporary solutions are always the best 😆
Just require template in your entry file (app.js or sth):

import "./index.pug";

And you will have fast bundling time + reload but don't do this on production 😉

@pspeter3
Copy link

It seems like Vue has solved this with https://github.com/vuejs-templates/webpack/blob/master/template/build/dev-server.js#L32-L37

@jantimon
Copy link
Owner

@pspeter3 wow that's nice - is there any way we could move this into this plugin?

@pspeter3
Copy link

@jantimon I can try. I'll fork and submit a PR

@pspeter3
Copy link

So I think the issue is that the plugin doesn't have access to the hot middleware plugin. It seems like the Vue dev server is doing the best thing.

@Silentz0r
Copy link

@19majkel94 Can you elaborate how you did it? i did import in my entry and did not reload the html.

@milewski
Copy link

milewski commented Jan 7, 2017

i came a long way defeating one by one.. .ts, .babel, .scss.... and then i got stuck at .html, no hot reload for this on vanilla html.... nor even auto reload.. any way to get this working some other how ?

@jantimon
Copy link
Owner

jantimon commented Jan 7, 2017

It looks like that this is really hard to accomplish on compiler level but could be accomplished on the level of the dev-server. I guess it might also be possible to write a custom html-webpack-hmr-plugin which would introduce a new entry point e.g. using https://github.com/jantimon/extra-entry-webpack-plugin which does the hmr - maybe you want to start working on that?

@windmaomao
Copy link

Running it doesn't generate the /dist folder files. Instead, if i just run webpack --watch and lite-server, it works for me. For some reason webpack-dev-server does kick off html plugin, but for some reason the plugin doesn't write to /dist afterwards.

@jantimon
Copy link
Owner

jantimon commented Feb 1, 2017

@windmaomao that's completely unrelated to this thread - it's a performance boost by webpack-dev-server and can be solved by https://github.com/jantimon/html-webpack-harddisk-plugin or by setting the output filesystem - for further information please open a stackoverflow question

@jacekk015
Copy link

jacekk015 commented Feb 9, 2017

In case if needed, here's working setup with hot reload of .js and index.html
For hot reload development:
npm run dev

For production:
npm run build

webpack.config.js

var webpack = require('webpack')
var HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
    entry: './src/main.js',
    output: {
        path: path.resolve(__dirname, './dist'),
        publicPath: '/dist/',
        filename: 'build.js'
    },
    module: {
        rules: [{
                test: /\.js$/,
                loader: 'babel-loader',
                exclude: /node_modules/
            },
            {
                test: /\.(png|jpg|gif|svg)$/,
                loader: 'file-loader',
                options: {
                    name: '[name].[ext]?[hash]'
                }
            }
        ]
    },
    resolve: {},
    devServer: {
        historyApiFallback: true,
        noInfo: true
    },
    performance: {
        hints: false
    },
    devtool: '#eval-source-map'
}

if (process.env.NODE_ENV !== 'production') {
    module.exports.plugins = (module.exports.plugins || []).concat([
        new HtmlWebpackPlugin({
            template: './index.html'
        })
    ])
}

if (process.env.NODE_ENV === 'production') {
    module.exports.devtool = '#source-map'
    module.exports.plugins = (module.exports.plugins || []).concat([
        new webpack.DefinePlugin({
            'process.env': {
                NODE_ENV: '"production"'
            }
        }),
        new webpack.optimize.UglifyJsPlugin({
            sourceMap: true,
            compress: {
                warnings: false
            }
        }),
        new webpack.LoaderOptionsPlugin({
            minimize: true,
            debug: false
        })
    ])
}

package.json

  "name": "test1",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "dev": "cross-env NODE_ENV=development webpack-dev-server --open --inline --hot",
    "build": "cross-env NODE_ENV=production webpack --progress --hide-modules"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "mithril": "^1.0.0"
  },
  "devDependencies": {
    "babel-core": "^6.22.1",
    "babel-loader": "^6.2.10",
    "babel-plugin-transform-react-jsx": "^6.22.0",
    "babel-preset-es2015": "^6.22.0",
    "cross-env": "^3.1.4",
    "file-loader": "^0.10.0",
    "html-webpack-plugin": "^2.28.0",
    "webpack": "^2.2.1",
    "webpack-dev-server": "^2.3.0"
  }
}

/src/main.js

if (process.env.NODE_ENV !== 'production') {
    require('file-loader!../index.html')
}

test1_complete_package2.zip

@milewski
Copy link

i think yet a better solution was doing something like this

devServer: {
    contentBase: [ PathToTheFolderWhereYourHTMLsLives ],
    watchContentBase: true
}

this approach doesnt require any "requiring" inside js files...

@jacekk015
Copy link

My approach is a hot reload and not full page reload.
If you change little piece of .js code you don't need to reload whole page.

@milewski
Copy link

Again, I just hate the fact that I need to add 'unnecessary' stuff in my js entries.. And somehow remember to remove that.. Or just leave there... Yet the one I showed still has HMR with js but with the html files will do a full page reload yes. Both ways not ideal.. Would rather study a possibility to create a plugin to inject html webpack plug-in outputs as an webpack entry and somehow get it done automatically...

@youwenda
Copy link

youwenda commented Sep 6, 2017

Using reload-html-webpack-plugin is done !!

@cncuckoo
Copy link

cncuckoo commented Sep 12, 2017

When you change the templates, the html-webpack-plugin does recompiling them, but it just not to re-write the new generated html files to the disk. Given this, even if the devServer reloading the pages, the result will not change. If you are in this case, try this plugin:

https://github.com/jantimon/html-webpack-harddisk-plugin

@avdd
Copy link

avdd commented Feb 25, 2018

No need to write to disk or open more sockets: just use the html-webpack-plugin-after-emit event to trigger a content-changed message from the devserver::

let devServer; // set below
module.exports = {
  plugins: [reloadHtml],
  devServer: {
    before(app, server) {
      devServer = server;
    }
  }
}
function reloadHtml() {
  this.plugin('compilation',
    thing => thing.plugin('html-webpack-plugin-after-emit', trigger));
  const cache = {};
  function trigger(data, callback) {
    const orig = cache[data.outputName];
    const html = data.html.source();
    // plugin seems to emit on any unrelated change?
    if (orig && orig !== html)
      devServer.sockWrite(devServer.sockets, 'content-changed');
    cache[data.outputName] = html;
    callback();
  }
}

I'm sure someone can make a nice plugin out of this with more style, but this works for me.

@sebastianseilund
Copy link

reload-html-webpack-plugin doesn't seem to work with Webpack 4.

Thanks @avdd for the quick work-around, which worked for me (even though I wish this somehow worked out of the box between html-webpack-plugin and webpack-dev-server).

If anyone else runs into Webpack 4's deprecation warning:

DeprecationWarning: Tapable.plugin is deprecated. Use new API on `.hooks` instead

You can change reloadHtml to this:

function reloadHtml() {
  const cache = {}
  const plugin = {name: 'CustomHtmlReloadPlugin'}
  this.hooks.compilation.tap(plugin, compilation => {
    compilation.hooks.htmlWebpackPluginAfterEmit.tap(plugin, data => {
      const orig = cache[data.outputName]
      const html = data.html.source()
      // plugin seems to emit on any unrelated change?
      if (orig && orig !== html) {
        devServer.sockWrite(devServer.sockets, 'content-changed')
      }
      cache[data.outputName] = html
    })
  })
}

@entr
Copy link

entr commented May 9, 2018

@sebastianseilund not able to get this working compilation.hooks.htmlWebpackPluginAfterEmit.tap(..) produces:

TypeError: Cannot read property 'tap' of undefined

and indeed Object.keys( compilation.hooks ) is:

[ 'buildModule',
  'rebuildModule',
  'failedModule',
  'succeedModule',
  'finishModules',
  'finishRebuildingModule',
  'unseal',
  'seal',
  'optimizeDependenciesBasic',
  'optimizeDependencies',
  'optimizeDependenciesAdvanced',
  'afterOptimizeDependencies',
  'optimize',
  'optimizeModulesBasic',
  'optimizeModules',
  'optimizeModulesAdvanced',
  'afterOptimizeModules',
  'optimizeChunksBasic',
  'optimizeChunks',
  'optimizeChunksAdvanced',
  'afterOptimizeChunks',
  'optimizeTree',
  'afterOptimizeTree',
  'optimizeChunkModulesBasic',
  'optimizeChunkModules',
  'optimizeChunkModulesAdvanced',
  'afterOptimizeChunkModules',
  'shouldRecord',
  'reviveModules',
  'optimizeModuleOrder',
  'advancedOptimizeModuleOrder',
  'beforeModuleIds',
  'moduleIds',
  'optimizeModuleIds',
  'afterOptimizeModuleIds',
  'reviveChunks',
  'optimizeChunkOrder',
  'beforeChunkIds',
  'optimizeChunkIds',
  'afterOptimizeChunkIds',
  'recordModules',
  'recordChunks',
  'beforeHash',
  'contentHash',
  'afterHash',
  'recordHash',
  'record',
  'beforeModuleAssets',
  'shouldGenerateChunkAssets',
  'beforeChunkAssets',
  'additionalChunkAssets',
  'additionalAssets',
  'optimizeChunkAssets',
  'afterOptimizeChunkAssets',
  'optimizeAssets',
  'afterOptimizeAssets',
  'needAdditionalSeal',
  'afterSeal',
  'chunkHash',
  'moduleAsset',
  'chunkAsset',
  'assetPath',
  'needAdditionalPass',
  'childCompiler',
  'normalModuleLoader',
  'optimizeExtractedChunksBasic',
  'optimizeExtractedChunks',
  'optimizeExtractedChunksAdvanced',
  'afterOptimizeExtractedChunks' ]

webpack 4.8
html-webpack-plugin 3.20

@entr
Copy link

entr commented May 9, 2018

NVM, it was a result of bad ordering in webpack-merge, where our custom reloadHtml plugin was coming before HtmlWebpackPlugin

@lock
Copy link

lock bot commented Jun 8, 2018

This issue has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

@lock lock bot locked as resolved and limited conversation to collaborators Jun 8, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests