Webpack-dev-middleware and webpack-hot-middleware configuration - node.js

I have some conceptual troubles with webpack-dev-middleware and webpack-hot-middleware
I have SSR app (react + express), webpack (with clientside and serverside configs). Webpack configs:
client.config
const config: Configuration = {
name: 'client',
target: 'web',
mode: IS_DEV ? "development" : "production",
output: {
path: path.join(DIST_DIR, 'client'),
filename: IS_DEV ? 'core/js/[name].js' : 'core/js/[contenthash].js',
publicPath: '/',
},
entry: [
path.join(SRC_DIR, 'client.tsx'),
IS_DEV && 'webpack-hot-middleware/client',
].filter(Boolean) as Entry,
module: {
rules: [
*client rules here*
],
} as ModuleOptions,
optimization: *optimization here*,
resolve: *resolve*,
plugins: [
IS_DEV && new webpack.HotModuleReplacementPlugin(),
IS_DEV && new ReactRefreshWebpackPlugin({
overlay: {
sockIntegration: 'whm',
},
}),
new MiniCssExtractPlugin({
filename: IS_DEV ? "core/css/[name].css" : "core/css/[contenthash].css",
chunkFilename: IS_DEV ? "core/css/[id].css" : "core/css/[contenthash].css",
}),
new ESLintPlugin({
extensions: ["js", "jsx", "ts", "tsx"],
}),
new LoadablePlugin(),
new CopyWebpackPlugin({
patterns: [{ from: './src/assets', to: 'assets' }]
}),
].filter(Boolean) as WebpackPluginInstance[],
devtool: IS_DEV ? "source-map" : false,
}
and
server.config
const config: Configuration = {
name: 'server',
mode: IS_DEV ? "development" : "production",
target: 'node',
output: {
path: path.join(DIST_DIR, 'server'),
filename: 'server.js',
libraryTarget: 'commonjs',
publicPath: '/',
},
entry: path.join(SRC_DIR, 'server.ts'),
module: {
rules: [
*server rules here*
],
},
optimization: common.optimization,
resolve: common.resolve,
plugins: [
new webpack.HotModuleReplacementPlugin(),
],
devtool: IS_DEV ? "source-map" : false,
externals: [
nodeExternals(),
'#loadable/component'
],
node: {
__dirname: false,
__filename: false,
},
};
Server file:
import { IS_DEV } from '../webpack/env';
import devMiddleware from 'webpack-dev-middleware';
import hotMiddleware from 'webpack-hot-middleware';
import config from '../webpack/client.config';
const app = express();
const compiler = webpack(config);
if (IS_DEV) {
const devServer = devMiddleware(compiler, {
serverSideRender: true,
publicPath: '/'
});
app.use(hotMiddleware(compiler, {
path: '/__webpack_hmr'
}));
app.use(devServer);
}
app.get('*', *server render here*);
const port = process.env.PORT || 8080;
app.listen(port, () => {
console.log(`Application is started on ${port}`);
});
Result of compile here (build folder):
/build
/client
/assets
/core
/css
/js
/static
/server
Production version works well, client's files served by nginx, express runs on port with nginx proxy also. I want to make dev server with hot reload, make same as it written in instruction,but i'm stuck here. I cant understand how it must work and what command in package.json must be.
For example: what webpack config i must use in devMiddleware compiler - client or server or merged?
Be glad if someone explain (best with pointing out where i went wrong). Thanks a lot.

Related

Electron application cannot resolve any node module that is added to webpack externals

I am trying to build an Electron application using Vue.js.
I am using webpack-dev-server to run the electron app in development mode.
In the webpack config I am adding all my node_modules to the externals array since I do not want them to be bundled.
The webpack development server gets started successfully without any error and the application is also launched as expected but I get the following error in the console.
Uncaught Error: Cannot find module 'frappejs'.
Note: This is not the only module that cannot be resolved. All the modules that I have added to the webpack externals arrays could not be resolved.
If I do not add them to the externals array, the node_modules are detected and the above error disappears.
Another thing that I have noticed is that if I replace
const frappe = require('frappejs'); with
const frappe = require('../../node_modules/frappejs');
The error disappers in this case as well when I am explicitly pointing to the node_modules directory.
What maybe the reason for this behaviour?
config.js
const webpack = require('webpack');
// plugins
const VueLoaderPlugin = require('vue-loader/lib/plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CaseSensitivePathsWebpackPlugin = require('case-sensitive-paths-webpack-plugin');
const FriendlyErrorsWebpackPlugin = require('friendly-errors-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const { getAppConfig, resolveAppDir } = require('./utils');
const appDependencies = require(resolveAppDir('./package.json')).dependencies;
const frappeDependencies = require(resolveAppDir('./node_modules/frappejs/package.json')).dependencies;
// const frappeDependencies = require('../package.json').dependencies;
let getConfig, getElectronMainConfig;
function makeConfig() {
const isProduction = process.env.NODE_ENV === 'production';
process.env.ELECTRON = 'true';
const isElectron = process.env.ELECTRON === 'true';
const isMonoRepo = process.env.MONO_REPO === 'true';
const whiteListedModules = ['vue'];
const allDependencies = Object.assign(frappeDependencies, appDependencies);
const externals = Object.keys(allDependencies).filter(d => !whiteListedModules.includes(d));
getConfig = function getConfig() {
const appConfig = getAppConfig();
const config = {
mode: isProduction ? 'production' : 'development',
context: resolveAppDir(),
entry: isElectron ? appConfig.electron.entry : appConfig.dev.entry,
externals: isElectron ? externals : undefined,
target: isElectron ? 'electron-renderer' : 'web',
output: {
path: isElectron ? resolveAppDir('./dist/electron') : resolveAppDir('./dist'),
filename: '[name].js',
// publicPath: appConfig.dev.assetsPublicPath,
libraryTarget: isElectron ? 'commonjs2' : undefined
},
devtool: !isProduction ? 'cheap-module-eval-source-map' : '',
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader'
},
{
test: /\.js$/,
loader: 'babel-loader',
exclude: file => (
/node_modules/.test(file) &&
!/\.vue\.js/.test(file)
)
},
{
test: /\.node$/,
use: 'node-loader'
},
{
test: /\.css$/,
use: [
'vue-style-loader',
'css-loader'
]
},
{
test: /\.scss$/,
use: [
'vue-style-loader',
'css-loader',
'sass-loader'
]
},
{
test: /\.(png|svg|jpg|gif)$/,
use: [
'file-loader'
]
}
]
},
resolve: {
extensions: ['.js', '.vue', '.json', '.css', '.node'],
alias: {
'vue$': 'vue/dist/vue.esm.js',
'deepmerge$': 'deepmerge/dist/umd.js',
'#': appConfig.dev.srcDir ? resolveAppDir(appConfig.dev.srcDir) : null
}
},
plugins: [
new webpack.DefinePlugin(Object.assign({
'process.env': appConfig.dev.env,
'process.env.NODE_ENV': isProduction ? '"production"' : '"development"',
'process.env.ELECTRON': JSON.stringify(process.env.ELECTRON)
}, !isProduction ? {
'__static': `"${resolveAppDir(appConfig.staticPath).replace(/\\/g, '\\\\')}"`
} : {})),
new VueLoaderPlugin(),
new HtmlWebpackPlugin({
template: resolveAppDir(appConfig.dev.entryHtml),
nodeModules: !isProduction
? isMonoRepo ? resolveAppDir('../../node_modules') : resolveAppDir('./node_modules')
: false
}),
new CaseSensitivePathsWebpackPlugin(),
new webpack.NamedModulesPlugin(),
new webpack.HotModuleReplacementPlugin(),
new FriendlyErrorsWebpackPlugin({
compilationSuccessInfo: {
messages: [`FrappeJS server started at http://${appConfig.dev.devServerHost}:${appConfig.dev.devServerPort}`],
},
}),
new webpack.ProgressPlugin(),
isProduction ? new CopyWebpackPlugin([
{
from: resolveAppDir(appConfig.staticPath),
to: resolveAppDir('./dist/electron/static'),
ignore: ['.*']
}
]) : null,
// isProduction ? new BabiliWebpackPlugin() : null,
// isProduction ? new webpack.LoaderOptionsPlugin({ minimize: true }) : null,
].filter(Boolean),
optimization: {
noEmitOnErrors: false
},
devServer: {
// contentBase: './dist', // dist path is directly configured in express
hot: true,
quiet: true
},
node: {
// prevent webpack from injecting useless setImmediate polyfill because Vue
// source contains it (although only uses it if it's native).
setImmediate: false,
// process is injected via DefinePlugin, although some 3rd party
// libraries may require a mock to work properly (#934)
process: 'mock',
// prevent webpack from injecting mocks to Node native modules
// that does not make sense for the client
dgram: 'empty',
fs: 'empty',
net: 'empty',
tls: 'empty',
child_process: 'empty'
}
}
return config;
}
getElectronMainConfig = function getElectronMainConfig() {
const appConfig = getAppConfig();
return {
entry: {
main: resolveAppDir(appConfig.electron.paths.main)
},
externals: externals,
module: {
rules: [
{
test: /\.js$/,
use: 'babel-loader',
exclude: /node_modules/
},
{
test: /\.node$/,
use: 'node-loader'
}
]
},
node: {
__dirname: !isProduction,
__filename: !isProduction
},
output: {
filename: '[name].js',
libraryTarget: 'commonjs2',
path: resolveAppDir('./dist/electron')
},
plugins: [
new webpack.NoEmitOnErrorsPlugin(),
// isProduction && new BabiliWebpackPlugin(),
isProduction && new webpack.DefinePlugin({
'process.env.NODE_ENV': '"production"'
})
].filter(Boolean),
resolve: {
extensions: ['.js', '.json', '.node']
},
target: 'electron-main'
}
}
}
makeConfig();
module.exports = {
getConfig,
getElectronMainConfig
};
Note: the resolveAppDir function returns the cwd path concatenated with the parameter passed.

How to use webpack to create a front end and a admin?

I am actually new on node.js. I am trying to create an app with admin and frontend using webpack and express. I have created an example app but now the problem is how I can create the app with the admin. Below I have shared my webpack config file
var webpack = require('webpack');
var path = require('path');
module.exports = {
entry: {
frontend: './src/index',
backend: './admin/index'
},
module: {
loaders: [
{
test: /\.js?$/, loader: 'babel', exclude: /node_modules/
},
{
test: /\.css$/,
loaders: [
'style?sourceMap',
'css?modules&importLoaders=1&localIdentName=[path]___[name]__[local]___[hash:base64:5]'
]
}
]
},
resolve: {
extensions: ['', '.js']
},
output: {
path: path.join(__dirname, '/public'),
publicPath: '/',
filename: 'bundle.js'
},
devServer: {
contentBase: './public',
hot: true
},
plugins: [
new webpack.optimize.OccurenceOrderPlugin(),
new webpack.HotModuleReplacementPlugin(),
new webpack.NoErrorsPlugin()
]
};
Webpack production configuration file
var config = require('./webpack.config.js');
var webpack = require('webpack');
config.plugins.push(
new webpack.DefinePlugin({
"process.env": {
"NODE_ENV": JSON.stringify("production")
}
})
);
config.plugins.push(
new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: false
}
})
);
Is there any easy solution or what I can do now?

Separate client and server in Webpack

Goal. Configuring app, which has: React, Webpack and MongoDB.
So, I've already setup Webpack for React and tried import Mongoose. The problem: React client-side and Mongoose - server-side, and because of that Webpack must have configurations for both. Using this answer: https://stackoverflow.com/a/37391247/7479176 I tried to configure Webpack. After that, I tried import Mongoose in my server.jsx file, but it didn't work.
Question. How to configure Webpack, so I can work with MongoDB?
Edited. I figured out how to rid of warnings (see Warnings):
output: {
filename: 'bundle.node.js',
libraryTarget: 'commonjs',
path: path.resolve(__dirname, 'dist')
},
externals: [
/^(?!\.|\/).+/i
],
But, when I added code into server.jsx (see server.jsx), it didn't log message in console.
Webpack configurations:
const path = require('path')
const webpack = require('webpack')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const CleanWebpackPlugin = require('clean-webpack-plugin')
module.exports = [
{
entry: {
index: './src/app/app.jsx'
},
devtool: 'inline-source-map',
devServer: {
port: 3000,
host: 'localhost',
historyApiFallback: true,
noInfo: false,
stats: 'minimal',
hot: true, // Tell the dev-server we're using HMR
contentBase: path.resolve(__dirname, './dist'),
publicPath: '/'
},
plugins: [
new webpack.NamedModulesPlugin(),
new webpack.HotModuleReplacementPlugin(), // Enable HMR
new CleanWebpackPlugin(['dist']),
new HtmlWebpackPlugin({
template: './src/index.html',
filename: 'index.html',
inject: 'body'
})
],
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: ['babel-loader']
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
}
]
}
},
{
entry: {
index: './src/server/server.jsx'
},
target: 'node',
output: {
filename: 'bundle.node.js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: ['babel-loader']
}
]
}
}
]
server.jsx:
import mongoose from 'mongoose'
import '../config/database.js'
mongoose.Promise = global.Promise
mongoose.connect(config.database)
// check connection
mongoose.connection.once('open', () => console.log('Connected to MongoDB'))
// check for db errors
mongoose.connection.on('error', err => console.log(err))
Warnings:
WARNING in ./node_modules/mongoose/lib/drivers/index.js
10:13-49 Critical dependency: the request of a dependency is an expression
WARNING in ./node_modules/require_optional/index.js
82:18-42 Critical dependency: the request of a dependency is an expression
WARNING in ./node_modules/require_optional/index.js
90:20-44 Critical dependency: the request of a dependency is an expression
WARNING in ./node_modules/require_optional/index.js
97:35-67 Critical dependency: the request of a dependency is an expression
WARNING in ./node_modules/es6-promise/dist/es6-promise.js
Module not found: Error: Can't resolve 'vertx' in 'D:\Projects\JavaScriptProjects\pizzaday\node_modules\es6-promise\dist'
# ./node_modules/es6-promise/dist/es6-promise.js 131:20-30
# ./node_modules/mongodb/lib/mongo_client.js
# ./node_modules/mongodb/index.js
# ./node_modules/mongoose/lib/index.js
# ./node_modules/mongoose/index.js
# ./src/server/server.jsx
Solution. I used webpack-dev-middleware and webpack-hot-middleware with basic Express server. I tried launch MongoDB with React on webpack-dev-server and thats was main problem.
I made new server.js within separate folder following advice from Neil Lunn and setup basic Express server with middleware and split Webpack config into 3 separate files common, dev and prod.
This fragment of code in server.js helped me to run server and client together with Webpack which bonded everything together:
app.use(webpackDevMiddleware(compiler, {
publicPath: config.output.publicPath
}))
app.use(webpackHotMiddleware(compiler))
New Webpack config (webpack.common.js):
const webpack = require('webpack')
const path = require('path')
const CleanWebpackPlugin = require('clean-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
entry: {
app: [
'webpack-hot-middleware/client?path=/__webpack_hmr&timeout=2000&reload=true',
'./src/index.jsx'
]
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
new CleanWebpackPlugin(['dist']),
new HtmlWebpackPlugin({
template: './index.html',
filename: 'index.html',
inject: 'body'
})
],
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist'),
publicPath: '/'
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: ['babel-loader']
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
}
]
}
}

Nodejs webpack and express server together

I am developing a browser game using nodejs and phaser framework and I really like this sandbox for the start: https://github.com/nkholski/phaser3-es6-webpack
The thing is, I need to use socket.io, but since I am kind of new to nodejs and these stuff, I jus can't manage to create a server and run it with this webpack at the same time.
I have made a server.js and pointed package.json for it as a start action.
const express = require('express');
const app = express();
const server = require('http').Server(app);
const io = require('socket.io').listen(server);
server.listen(8000, function () {
console.log("listening");
});
io.on('connection', (socket) => {...});
webpack.config.js:
module.exports = {
entry: {
app: [
'babel-polyfill',
path.resolve(__dirname, 'src/main.js')
],
vendor: ['phaser']
},
devtool: 'cheap-source-map',
output: {
pathinfo: true,
path: path.resolve(__dirname, 'dist'),
publicPath: './dist/',
filename: 'bundle.js'
},
watch: true,
plugins: [
definePlugin,
new webpack.optimize.CommonsChunkPlugin({ name: 'vendor'/* chunkName= */, filename: 'vendor.bundle.js'/* filename= */}),
new BrowserSyncPlugin({
host: process.env.IP || 'localhost',
port: process.env.PORT || 3000,
server: {
baseDir: ['./', './build']
}
})
],
module: {
rules: [
{ test: /\.js$/, use: ['babel-loader'], include: path.join(__dirname, 'src') },
{ test: /phaser-split\.js$/, use: ['expose-loader?Phaser'] }
]
},
node: {
fs: 'empty',
net: 'empty',
tls: 'empty'
},
resolve: {
alias: {
'phaser': phaser
}
}
}
So what happened was, that I just created empty listening server and on the other port there was running the webpack project.
Is there any way to make this work together?
Thanks

Webpack-dev-server showing directory list instead of the app page

I am building a progressive web app using react and i am using a webpack dev server.
My Web app config :
var webpack = require('webpack');
var path = require('path');
module.exports = {
entry: [
'webpack-dev-server/client?http://127.0.0.1:8080',
'webpack/hot/only-dev-server',
'./src/index.jsx'
],
output: {
path: path.join(__dirname,'public'),
filename: 'bundle.js',
publicPath: 'http://localhost:8080'
},
devServer: {
contentBase: "./public",
hot: true
},
resolve: {
modulesDirectories: ['node_modules','src'],
extensions: ['','.js']
},
module: {
loaders: [
{
test: /\.jsx?$/,
exclude: /node_modules/,
loaders: ['react-hot','babel?presets[]=react,presets[]=react,presets[]=es2015']
}
]
},
plugins:[
new webpack.HotModuleReplacementPlugin(),
new webpack.NoErrorsPlugin()
]
};
This is my directory structure :
Please help me with this as i am not able to see my index.html.
Is there something wrong with my config file
Your index.html is in the folder "src"
so your webpack.config.js should like this:
devServer: {
contentBase: "./src",
hot: true
},

Resources