Why does webpack + pug generate an Entrypoint undefined = ./index.html error?

357 Views Asked by At

The essence of the question is this: there is a build on webpack, everything works fine with one exception: when changing PUG files. the project is rebuilt, but the content is not updated in the browser. When building it produces an error: Entrypoint undefined = ./index.html

    const path = require('path');
const fs = require('fs');
const HTMLWebpackPlugin = require('html-webpack-plugin');
const {CleanWebpackPlugin} = require('clean-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const OptimizeCssAssetWebpackPlugin = require('optimize-css-assets-webpack-plugin');
const TerserWebpackPlugin = require('terser-webpack-plugin');


const PATHS = {
  src: path.join(__dirname, './src'),
  dist: path.join(__dirname, './dist'),
  assets: 'assets/',
  pages: function () { return `${this.src}/pug/` }
}

// const PAGES_DIR = PATHS.src
const PAGES = fs.readdirSync(PATHS.pages()).filter(fileName => fileName.endsWith('.pug'));

const isDev = process.env.NODE_ENV === 'development'
const isProd = !isDev

const optimization = () => {
  const config = {
    splitChunks: {
      chunks: 'all'
    }
  }

  if (isProd) {
    config.minimizer = [
      new OptimizeCssAssetWebpackPlugin(),
      new TerserWebpackPlugin()
    ]
  }

  return config
}

const filename = ext => isDev ? `[name].${ext}` : `[name].[hash].${ext}`

const cssLoaders = extra => {
  const loaders = [
    {
      loader: MiniCssExtractPlugin.loader,
      options: {
        hmr: isDev,
        reloadAll: true
      },
    },
    'css-loader'
  ]

  if (extra) {
    loaders.push(extra)
  }

  return loaders
}

const babelOptions = preset => {
  const opts = {
    presets: [
      '@babel/preset-env'
    ],
    plugins: [
      '@babel/plugin-proposal-class-properties'
    ]
  }

  if (preset) {
    opts.presets.push(preset)
  }

  return opts
}

const jsLoaders = () => {
  const loaders = [{
    loader: 'babel-loader',
    options: babelOptions()
  }]

  if (isDev) {
    loaders.push('eslint-loader')
  }

  return loaders
}

const plugins = () => {
  const base = [
    new CleanWebpackPlugin(),
    new CopyWebpackPlugin([
      { from: `${PATHS.src}/${PATHS.assets}img`, to: `${PATHS.assets}img` },
      { from: `${PATHS.src}/${PATHS.assets}fonts`, to: `${PATHS.assets}fonts` },
      { from: `${PATHS.src}/static`, to: '' },
    ]),
    new MiniCssExtractPlugin({
      filename: filename('css')
    }),
    ...PAGES.map(page => new HTMLWebpackPlugin({
      template: `${PATHS.pages()}/${page}`,
      filename: `./${page.replace(/\.pug/,'.html')}`,
    }))
  ]


  return base
}

module.exports = {
  context: PATHS.src,
  mode: process.env.NODE_ENV,
  entry: {
    app: PATHS.src,
  },
  output: {
    filename: filename('js'),
    path: PATHS.dist
  },
  resolve: {
    extensions: ['.js', '.json', '.png'],
    alias: {
      '@': PATHS.src,
    }
  },
  optimization: optimization(),
  devServer: {
    hot: isDev
  },
  devtool: isDev ? 'source-map' : '',
  plugins: plugins(),
  module: {
    rules: [
      {
        test: /\.pug$/,
        loader: 'pug-loader',
        options: {
          pretty: isProd
        }
      },
      {
        test: /\.css$/,
        use: cssLoaders()
      },
      {
        test: /\.s[ac]ss$/,
        use: cssLoaders('sass-loader')
      },
      {
        test: /\.(png|jpg|svg|gif)$/,
        use: ['file-loader']
      },
      {
        test: /\.(ttf|woff|woff2|eot)$/,
        use: ['file-loader']
      },
      {
        test: /\.xml$/,
        use: ['xml-loader']
      },
      {
        test: /\.csv$/,
        use: ['csv-loader']
      },
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: jsLoaders()
      }
    ]
  }
}

Project structure: enter image description here

2

There are 2 best solutions below

0
On BEST ANSWER

I have solved this problem with additional package chokidar.

i use next code:

    const chokidar = require('chokidar');
   ...
  devServer: {
    hot: true,
    overlay: {
      warnings: false,
      errors: true
    },
    before(app, server) {
      chokidar.watch([
        `${PATHS.src}/**/*.pug`
      ]).on('all', function() {
        server.sockWrite(server.sockets, 'content-changed');
      })
    }
  }
   ...
0
On

Have you tried html-webpack-pug-plugin?

npm i html-webpack-pug-plugin

Replace this:

const plugins = () => {
    const base = [
        //..
        ...PAGES.map(page => new HTMLWebpackPlugin({
            template: `${PATHS.pages()}/${page}`,
            filename: `./${page.replace(/\.pug/, '.html')}`,
        }))
    ]
//...

With this:

const plugins = () => {
    const base = [
        //..
        new HtmlWebpackPugPlugin(),
        new HtmlWebpackPlugin({
            template: './src/pug/index.pug',
            filename: 'index.pug',
        })
    ]
//...

The plugin will copy the main index.pug file into the dist directory with reference to js and css files.

The pug files in static directory should then load the main template file like this:

extends ../../dist/index.pug