3

I'd like to generate html contents using javascript template literals, and output static html files on Webpack compile time. I also need a development server with hot module replacement capabilities that works with html, css, and js files.

I require page1.js inside HtmlWebpackPlugin options:

new HtmlWebpackPlugin({
  template: './html-template.ejs',
  appMount: require('./src/views/page1')
})

Then I use a custom ejs template html-template.ejs to import the generated html contents:

<% if (htmlWebpackPlugin.options.appMount) { %>
<%= htmlWebpackPlugin.options.appMount %>
<% } %>

page1.js looks like this:

const h1 = (text) => `<h1>${text}</h1>`;

module.exports = h1('Hello World');

Note that page1.js is not set as entry point. The only entry point is src/index.js (which only have a console.log statement inside).

When I run webpack everything works fine, dist/index.html is generated along side with dist/main.js.

If I run webpack-dev-server and try to edit page1.js, the browser reloads but html contents doesn't update.

Any ideas on how to make webpack-dev-server and Hot Module Replacement works on views files? Or do you have any other better solution to create a simple static site generator?

pldg
  • 2,427
  • 3
  • 22
  • 37
  • Maybe just use Jekyll. Or you want exactly to implement? – Dmitry Surin Aug 05 '18 at 18:03
  • And also it sounds like your browser refreshes the page just before webpack has updated the view. Seems like you need to call some callback. – Dmitry Surin Aug 05 '18 at 18:06
  • @DmitrySurin I don't want to use Jekyll, I want to create a static site generator with Webpack. The only problem right now is with `devServer`, and it looks like a bug to me: *page1.js* doesn't get connected to HMR correctly. I've also tried with *mini-html-webpack-plugin* and with *webpack-serve*, but the bug persist. – pldg Aug 05 '18 at 19:03

2 Answers2

1

You can do this in html:

<%=require('./views/page1.js')%>
Petr Averyanov
  • 9,327
  • 3
  • 20
  • 38
  • Thanks, this help solve the problem. I'd like to know why require *page1.js* inside *HtmlWebpackPlugin* options doesn't connect it to HMR, seems like a bug. – pldg Aug 06 '18 at 16:27
1

Following @petr answer, I confirm that require page1.js directly inside html-template.ejs works fine:

  • All require calls inside webpack.config.js file are executed by node - page1.js will not be connected to devServer
  • All require calls inside HtmlWebpackPlugin templates are executed by webpack - page1.js will be connected to devServer correctly

See this github issue reference.

To go even further you can create a loop to automatically load all views and generate html files:

webpack.config.js

  • Loop through each views filename and load the specific HtmlWebpackPlugin configuration for that view
const viewsFiles = require('./readViewsFiles');
const viewsConfig = require('./webpack.views');

const pages = viewsFiles.map(pageName => {
  return viewsConfig({
    title: pageName,
    path: pageName == 'page1' ? '' : pageName,
    pageName
  });
});

module.exports = pages;

readViewsFiles.js

webpack.views.js

  • Return a new instance of HtmlWebpackPlugin
  • If path == '' then filename: 'index.html' otherwise filename: 'page2/index.html' (and so on for every views name)
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = ({
  path = '',
  template = './html-template.ejs',
  title,
  pageName
} = {}) => ({
  plugins: [
    new HtmlWebpackPlugin({
      filename: `${path && path + '/'}index.html`,
      template,
      title,
      pageName
    })
  ]
});

html-template.ejs

  • Dynamically loads the correct views file
<%= require(`./src/views/${htmlWebpackPlugin.options.pageName}.js`) %>

page1.js and other similar views files

const h1 = (text) => `<h1>${text}</h1>`;

module.exports = h1('Hello World');

When you run webpack the structure in the dist folder for html files will looks like this:

dist
│    index.html
│
│--- page2
│         index.html
│
│--- page3
          index.html

The dist folder is considered the root of your server.

Now you can implement a router in your app to navigate between pages:

  • Set a link to / for page1: http://www.mywebsite.com/
  • Set a link to /page2 for page2: http://www.mywebsite.com/page2

Note: you don't have to add index.html to your links to navigate between pages. With this method you'll not see .html extension at the end of the url.

Note: you can't view your app via file:/// protocol because your router links are set as absolute path, you must use a local or a remote server.

pldg
  • 2,427
  • 3
  • 22
  • 37