webpackbabeljswebpack-4webpack-dev-middlewarewebpack-hot-middleware

WebpackOptionsValidationError Invalid Configuration webpack(config)


I'm building a base webserver that I plan on using for future projects. I'm setting up a full javascript stack. I'm using Express, Webpack, and Babel.

The issue is that in setting up hmr through Express the configuration file has been running into validation errors.

The config file worked well under webpack-dev-server and in a basic set up of Express. Yet, when called through webpack(config) the config errors.

webpack.config.js

require('@babel/register');
module.exports = require('./webpack.config.babel.js');

webpack.config.babel.js

import path from 'path'
import webpack from 'webpack'
import WebpackBar from 'webpackbar'
import merge from 'webpack-merge'
import HtmlWebpackPlugin from 'html-webpack-plugin'
import MiniCssExtractPlugin from 'mini-css-extract-plugin'

import common from './webpack.common.babel'

export default merge(common, {
    mode: 'production',
    target: 'web',
    output: {
        path: path.resolve(__dirname, './dist'),
        filename: `[name].js`,
        publicPath: '/',
        library: 'kerillian',
        libraryTarget: 'umd'
    },
    module: {
        rules: [
            {
                test: /\.(sc|c)ss$/,
                use: [
                  {
                    loader: MiniCssExtractPlugin.loader,
                    options: {
                      includePaths: [
                        path.resolve(__dirname, 'node_modules/')
                      ]
                    }
                  },
                  'css-loader',
                  'sass-loader'
                ]
              }
        ]
    },
    plugins: [
        new MiniCssExtractPlugin({
            filename: '[name].css'
        }),
        new webpack.HotModuleReplacementPlugin(),
        new HtmlWebpackPlugin({
            filename:'./templates/template.html',
            template: './src/html/template.html',
            inject: 'body'
        }), 
        new WebpackBar()
    ]
})

webpack.common.babel.js

import path from 'path'
import webpack from 'webpack'

const config = {
    entry: {
        index: './src/js/index.js'
    },
    performance: {
        hints: false
    },
    externals: {
        jquery: 'jquery'
    },
    optimization: {
        splitChunks: {
            name: true,
            cacheGroups: {
                index: {
                    test: /static\/js/
                },
                vendors: {
                    test: /([\\/]node_modules[\\/])/,
                    name: 'vendor',
                    chunks: 'all'
                }
            }
        }
    },
    module: {
        rules: [ 
            {
                test: /\.(ttf|eot|woff|woff2)$/,
                loader: 'file-loader',
                options: {
                    name: path.resolve(__dirname,`./dist/public/fonts/[name].[ext]`),
                }
            },
            {
                test: /\.(png|svg|gif|jpg)$/,
                loader: 'file-loader',
                options: {
                    name: path.resolve(__dirname,`./dist/public/images/[name].[ext]`),
                },
            },
            {
                test: /\.(html)$/,
                use: {
                    loader: 'html-loader',
                    options: { minimize: true }
                }
            },
            {
                test: /\.(js|jsx)$/,
                exclude: /node_modules/,
                loader: 'babel-loader',
                query: {
                    presets: ['@babel/preset-env']
                }
            } 
        ]
    },
    plugins: [
        new webpack.NamedModulesPlugin(),
        new webpack.ProvidePlugin({
            $: 'jquery',
            jQuery: 'jquery',
            'window.jQuery': 'jquery'
        }),
    ],
    resolve: {
        modules: ['src', 'node_modules'],
        extensions: ['.js', '.css'],
        alias: {
            milligram: path.join(__dirname, '/node_modules/milligram/dist/milligram.css')
        }
    }

}

export default config

server.js

require('dotenv').config()
const path = require('path')
const express = require('express')
const graphqlHTTP = require('express-graphql')
const { GraphQLSchema } = require('graphql')
const connect = require('../database/database.js')

const isDeveloping = process.env.DEV

connect.connectDB()

const schema = new GraphQLSchema({})

const app = express(),
            DIST_DIR = './dist',
            HTML_FILE = path.join(DIST_DIR, '/templates/template.html')

if(isDeveloping){
    const webpack =require('webpack')
    let webpackMiddleware = require('webpack-dev-middleware')
    let webpackHotMiddleware = require('webpack-hot-middleware')
    let config = require('../../webpack.config.js')
    let compiler = webpack(config);
    app.use(webpackMiddleware)
    app.use(webpackHotMiddleware(compiler))
} else {
    app.use(express.static(`${DIST_DIR}`))
    app.use(express.static(`${DIST_DIR}/public`))
}

app.use('/graphql', graphqlHTTP({
    schema: schema,
    graphql: true
}))

app.get('*', (req, res) => {
    res.sendFile(HTML_FILE, { root: './'})
})

const PORT = process.env.PORT || 3000
app.listen(PORT, () => {
    console.log(`App listening to ${PORT}...`)
    console.log("Press Ctrl-C to quit")
})

Error

kerillian  | ✔ Webpack
kerillian  |   Compiled successfully in 4.34s
kerillian  |
kerillian  | Hash: b216aa3c06ff5b3496ac
kerillian  | Version: webpack 4.39.1
kerillian  | Time: 4346ms
kerillian  | Built at: 08/08/2019 6:39:38 PM
kerillian  |                     Asset       Size  Chunks             Chunk Names
kerillian  | ./templates/template.html  308 bytes          [emitted]
kerillian  |                  index.js   8.88 KiB       0  [emitted]  index
kerillian  |                vendor.css   9.85 KiB       1  [emitted]  vendor
kerillian  |                 vendor.js    117 KiB       1  [emitted]  vendor
kerillian  | Entrypoint index = vendor.css vendor.js index.js
kerillian  | [./node_modules/webpack/buildin/global.js] (webpack)/buildin/global.js 472 bytes {1} [built]
kerillian  | [./src/js/index.js] ./src/js/index.js + 1 modules 423 bytes {0} [built]
kerillian  |     | ./src/js/index.js 108 bytes [built]
kerillian  |     | ./src/views/App.js 300 bytes [built]
kerillian  |     + 9 hidden modules
kerillian  | Child html-webpack-plugin for "templates/template.html":
kerillian  |      1 asset
kerillian  |     Entrypoint undefined = ./templates/template.html
kerillian  |     [./node_modules/html-webpack-plugin/lib/loader.js!./src/html/template.html] 175 bytes {0} [built]
kerillian  | Child mini-css-extract-plugin node_modules/css-loader/dist/cjs.js!node_modules/sass-loader/lib/loader.js!node_modules/milligram/dist/milligram.css:
kerillian  |     Entrypoint mini-css-extract-plugin = *
kerillian  |        2 modules
kerillian  | {
kerillian  |   default: {
kerillian  |     entry: { index: './src/js/index.js' },
kerillian  |     performance: { hints: false },
kerillian  |     externals: { jquery: 'jquery' },
kerillian  |     optimization: { splitChunks: [Object] },
kerillian  |     module: { rules: [Array] },
kerillian  |     plugins: [
kerillian  |       [NamedModulesPlugin],
kerillian  |       [ProvidePlugin],
kerillian  |       [MiniCssExtractPlugin],
kerillian  |       [HotModuleReplacementPlugin],
kerillian  |       [HtmlWebpackPlugin],
kerillian  |       [WebpackBarPlugin]
kerillian  |     ],
kerillian  |     resolve: { modules: [Array], extensions: [Array], alias: [Object] },
kerillian  |     mode: 'production',
kerillian  |     target: 'web',
kerillian  |     output: {
kerillian  |       path: '/usr/src/app/dist',
kerillian  |       filename: '[name].js',
kerillian  |       publicPath: '/',
kerillian  |       library: 'kerillian'
kerillian  |     }
kerillian  |   }
kerillian  | }
kerillian  | /usr/src/app/node_modules/webpack/lib/webpack.js:31
kerillian  |        throw new WebpackOptionsValidationError(webpackOptionsValidationErrors);
kerillian  |        ^
kerillian  |
kerillian  | WebpackOptionsValidationError: Invalid configuration object. Webpack has been initialised using a configuration object that does not match the API schema.
kerillian  |  - configuration has an unknown property 'default'. These properties are valid:
kerillian  |    object { amd?, bail?, cache?, context?, dependencies?, devServer?, devtool?, entry?, externals?, infrastructureLogging?, loader?, mode?, module?, name?, node?, optimization?, output?, parallelism?, performance?, plugins?, profile?, recordsInputPath?, recordsOutputPath?, recordsPath?, resolve?, resolveLoader?, serve?, stats?, target?, watch?, watchOptions? }
kerillian  |    For typos: please correct them.
kerillian  |    For loader options: webpack >= v2.0.0 no longer allows custom properties in configuration.
kerillian  |      Loaders should be updated to allow passing options via loader options in module.rules.
kerillian  |      Until loaders are updated one can use the LoaderOptionsPlugin to pass these options to the loader:
kerillian  |      plugins: [
kerillian  |        new webpack.LoaderOptionsPlugin({
kerillian  |          // test: /\.xxx$/, // may apply this only for some modules
kerillian  |          options: {
kerillian  |            default: …
kerillian  |          }
kerillian  |        })
kerillian  |      ]
kerillian  |     at webpack (/usr/src/app/node_modules/webpack/lib/webpack.js:31:9)
kerillian  |     at Object.<anonymous> (/usr/src/app/config/server/server.js:23:20)
kerillian  |     at Module._compile (internal/modules/cjs/loader.js:777:30)
kerillian  |     at Object.Module._extensions..js (internal/modules/cjs/loader.js:788:10)
kerillian  |     at Module.load (internal/modules/cjs/loader.js:643:32)
kerillian  |     at Function.Module._load (internal/modules/cjs/loader.js:556:12)
kerillian  |     at Function.Module.runMain (internal/modules/cjs/loader.js:840:10)
kerillian  |     at internal/main/run_main_module.js:17:11

Solution

  • The simple answer to this issue is an incompatibility between webpack-middleware and the format babel complies into. These systems just don't work together. I have moved away from this approach and instead use nodedemon and webpack --watch.

    While this is not the most accepted approach for setting up a node hot reload feature it had been working well in the dockerized development environment I have setup for this project.