How to make Webpack and Node work together? - node.js

I was planning to use ES6 Modules on front-end so I did experiment on Webpack. However, I am having a hard time making the ff: work
Hot reload when there are changes in client-side
Hot reload when there are changes in server-side (before webpack, I'm using nodemon . and not have issues with it)
Losing debug/console.log info in terminal since it's printing the webpack status and nothing on the server e.g my custom 'Server running....' log.
The setup below I was trying to run via npm start. And everytime I make any change, I have to run npm start again
package.json
{
"name": "socket-io-chat",
"version": "1.0.0",
"description": "",
"main": "server.js",
"scripts": {
"start": "npm run build",
"build": "webpack -d && webpack-dev-server --hot --inline --watch && node --watch",
"build:prod": "webpack -p && webpack-dev-server"
},
"author": "",
"license": "ISC",
"dependencies": {
"cors": "^2.7.1",
"express": "*",
"socket.io": "*"
},
"devDependencies": {
"babel-core": "^6.14.0",
"babel-loader": "^6.2.5",
"babel-preset-es2015": "^6.14.0",
"babel-preset-stage-2": "^6.13.0",
"nodemon": "^1.10.2",
"webpack": "^1.13.2",
"webpack-dev-server": "^1.15.1"
}
}
webpack.config.js
var path = require("path");
var debug = process.env.NODE_ENV !== "production";
var webpack = require('webpack');
var SRC_DIR = path.join(__dirname, "public");
var DIST_DIR = path.join(__dirname, "public/js/dist");
var config = {
context: __dirname,
devtool: debug ? "inline-sourcemap" : null,
entry: {
guest : path.join(SRC_DIR, "entry-guest.js"),
authenticated : path.join(SRC_DIR, "entry-authenticated.js")
},
output: {
path: DIST_DIR,
filename: "[name].js"
},
modules: {
loaders: [
{
test: /\.js?/,
include: SRC_DIR,
loader: "babel-loader",
query: {
presets: ["es2015", "stage-2"]
}
}
]
},
plugins: debug ? [] : [
new webpack.optimize.DedupePlugin(),
new webpack.optimize.OccurenceOrderPlugin(),
new webpack.optimize.UglifyJsPlugin({ mangle: false, sourcemap: false }),
],
devServer: {
https: false,
contentBase: SRC_DIR,
stats: 'errors-only',
port: 3000
}
};
module.exports = config;
server.js
//create server
var express = require('express');
var cors = require('cors');
var app = express();
app.use(cors());
console.log('here');
var server = require('http').createServer(app);
//prepare socket io, make it listen to server
var io = require('socket.io').listen(server);
users = [];
connections = [];
var port = process.env.PORT || 3000;
server.listen(port);
console.log(`Server running *:${port}`);
//routing
app.get('/', function(req, res) {
res.sendFile(__dirname + '/public/index.html');
});
//open a connection
io.sockets.on('connection', function(socket) {
connections.push(socket);
console.log('Connected: %s sockets connected:', connections.length);
//...more codes here
});

In your webpack config file you can try using the plugin "Hot Module Replacement"
Along side with your already implemented Nodemon you should have both the client and server reloading on changes.
plugins: [
new webpack.HotModuleReplacementPlugin(),
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: '"development"'
}
})
],
Here is an example webpack config utilizing HotModuleReplacement.
var path = require('path');
var webpack = require('webpack');
module.exports = {
devtool: 'eval',
entry: [
'react-hot-loader/patch',
'webpack-dev-server/client?http://localhost:3001',
'webpack/hot/only-dev-server',
path.resolve(__dirname, 'client', 'index.jsx')
],
output: {
path: path.resolve(__dirname, 'public'),
publicPath: '/',
filename: 'bundle.js'
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: '"development"'
}
})
],
resolve: {
modules: [path.resolve(__dirname, 'node_modules')],
extensions: ['*', '.js', '.jsx']
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: ['babel-loader']
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader'],
},
{
test: /\.(png|svg|jpg|gif)$/,
use: [
'file-loader'
]
},
]
},
devServer: {
port: 3001,
contentBase: path.resolve(__dirname, 'public'),
hot: true,
historyApiFallback: true
},
devtool: 'source-map'
};

Related

How can I use fs with webpack 4.46.0?

I, am trying to use the fs module but I get this error
Module not found: Error: Can't resolve 'fs' in '/home/pau/Escritorio/Master/Blockchain/Prac2/PRAC2_Template/Ejercicio_2/app/src'
# ./src/index.js 5:11-24
I have fs installed and for what I've seen, theres a problem with webpack that makes this happen.
Mi packeage.json is this
{
"name": "app",
"version": "1.0.0",
"description": "",
"private": true,
"scripts": {
"build": "webpack",
"dev": "webpack-dev-server"
},
"devDependencies": {
"copy-webpack-plugin": "^5.0.5",
"webpack": "^4.41.2",
"webpack-cli": "^3.3.10",
"webpack-dev-server": "^3.9.0"
},
"dependencies": {
"fs": "0.0.1-security",
"ipfs-http-client": "^47.0.0",
"web3": "^1.8.1"
}
}
and my webpack.config.js this
const path = require("path");
const CopyWebpackPlugin = require("copy-webpack-plugin");
module.exports = {
entry: "./src/index.js",
output: {
filename: "index.js",
path: path.resolve(__dirname, "dist"),
},
plugins: [
new CopyWebpackPlugin([{ from: "./src/index.html", to: "index.html" }]),
],
devServer: { contentBase: path.join(__dirname, "dist"), compress: true },
};
I found this link https://github.com/webpack/webpack/issues/13498, where they say that adding
module: {
rules: [
{
test: /#aws-sdk\/lib-storage\//,
resolve: {
alias: {
'./runtimeConfig': './runtimeConfig.browser',
},
},
},
],
},
The problem disappear, but it doesnt work for me. My webpack.config.js with that code looks like this:
const path = require("path");
const CopyWebpackPlugin = require("copy-webpack-plugin");
module.exports = {
entry: "./src/index.js",
output: {
filename: "index.js",
path: path.resolve(__dirname, "dist"),
},
plugins: [
new CopyWebpackPlugin([{ from: "./src/index.html", to: "index.html" }]),
],
devServer: { contentBase: path.join(__dirname, "dist"), compress: true },
module: {
rules: [
{
test: /#aws-sdk\/lib-storage\//,
resolve: {
alias: {
'./runtimeConfig': './runtimeConfig.browser',
},
},
},
],
},
};
But I get the same error.
How can I solve this?
fs is a built-in module in Node.js. You just have to require it, like this:
const fs = require('fs');
and then you can use it.
The fact, that you have fs in your package.json might interfere with the built-in module. You should remove fs from your package.json and run npm install to remove any packages, which might have been unnecessarily installed.
After that it should work.

webpack-hot-middleware webpack express typescript

I'm trying to set up webpack-hot-middleware on my node api
but when I made a change on welcome.controller.ts, the console shows the build process but I can't see the changes, what I'm missing?, I'm using typescript and webpack, here is my setup:
Webpack.config.js
var fs = require('fs');
var webpack = require('webpack');
var path = require("path");
var nodeModules = {};
fs.readdirSync('node_modules')
.filter(function (x) {
return ['.bin'].indexOf(x) === -1;
})
.forEach(function (mod) {
nodeModules[mod] = 'commonjs ' + mod;
});
module.exports = {
entry: [
'webpack-hot-middleware/client?
path=http://localhost:3000/__webpack_hmr&reload=true',
'./src/server'
],
output: {
path: __dirname + '/dist',
filename: 'server.js',
publicPath: '/'
},
devServer: {
contentBase: './src'
},
node: {
__dirname: false,
__filename: false
},
resolve: {
// Add '.ts' and '.tsx' as a resolvable extension.
extensions: ['.webpack.js', '.web.js', '.ts', '.tsx', '.js'],
},
devtool: '#source-map',
plugins: [
new webpack.HotModuleReplacementPlugin(),
new webpack.NoEmitOnErrorsPlugin()
],
module: {
loaders: [
// All files with a '.ts' or '.tsx'
// extension will be handled by 'ts-loader'
{
test: /\.tsx?$/,
loader: 'ts-loader',
},
],
},
target: 'node',
externals: nodeModules
};
server.ts
import * as express from 'express';
import * as webpack from 'webpack';
import * as webpackDevMiddleware from 'webpack-dev-middleware';
import * as webpackHotMiddleware from 'webpack-hot-middleware';
import * as dotenv from 'dotenv';
import * as bodyParser from 'body-parser';
import * as config from '../webpack.config';
import * as welcomeController from './controllers/welcome.controller';
import * as path from 'path'
dotenv.config();
const compiler = webpack(config);
const app = express();
app.use(webpackDevMiddleware(compiler, {
publicPath: config.output.path,
stats: {colors: true},
noInfo: true
}));
app.use(webpackHotMiddleware(compiler, {
log: console.log, path: '/__webpack_hmr', heartbeat: 10 * 1000
}));
app.set('port', process.env.PORT || 3000);
app.use(bodyParser.json());
app.use('/', express.static(path.join(__dirname, "/dist")));
app.use(bodyParser.urlencoded({ extended: true }));
app.get('/', welcomeController.index);
app.listen(app.get('port'), () => {
console.log(('App is running at http://localhost:%d in %s mode'),
app.get('port'), app.get('env'));
console.log('Press CTRL-C to stop\n');
});
if (module.hot) {
module.hot.accept('./server', () => {
console.log('si')
});
}
module.exports = app;
welcome.controller.ts
import { Request, Response } from 'express';
let pkg = require(__dirname + '/../../package.json');
export let index = (req: Request, res: Response) => {
res.json({
message: 'Welcome to API sekeleton.',
version: pkg.version,
});
}
package.json
{
"name": "projectq",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"build": "webpack --config webpack.config.js",
"start": "npm run build && node ./dist/server.js"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"dotenv": "^5.0.0",
"express": "^4.16.2",
"ts-loader": "^3.5.0",
"typescript": "^2.7.1",
"typings": "^2.1.1",
"webpack": "^3.11.0",
"webpack-dev-middleware": "^2.0.5",
"webpack-dev-server": "^2.11.1",
"webpack-hot-middleware": "^2.21.0"
}
}

Webpack builds on production environment

In React JS starter kits like the one at https://github.com/wallacyyy/reactly-starter-kit, I see package.json files that have content like this:
"scripts": {
"build": "cross-env NODE_ENV=production webpack --config ./webpack.prod.config.js --progress --colors",
...
},
"dependencies": {
"express": "^4.15.2",
"react": "^15.5.4",
"react-dom": "^15.5.4"
},
"devDependencies": {
"webpack": "^2.2.1",
"webpack-dev-server": "^2.4.2"
}
The build script uses webpack to process the production build. How is it able to run on production when webpack is only a devDependency?
Webpack doesn't run on your production environment. Your build script just sets the NODE_ENV variable to equals production and thus letting Webpack and his plugins know they should prepare the bundle for production use. What exactly happens when you run this command depends on your webpack configuration, but among most common things would be code minification. You can also specify different kind of source maps and many other things. See https://webpack.js.org/guides/production/ for more information.
You keep both prod and dev webpack config. And in prod webpack config use definePlugin to set process.env.NODE_ENV as production ( you can also use other env variables ).
Now during transpiling and minification, NODE_ENV will be used in your vendor lib or in you app js.
'use strict';
var webpack = require('webpack');
var uglifyPlugin = new webpack.optimize.UglifyJsPlugin({
minimize: true,
comments: false
});
var definePlugin = new webpack.DefinePlugin({
'process.env': {
'NODE_ENV': JSON.stringify('production')
}
});
var commonChunkPlugin = new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
chunks: ['vendor', 'source'],
filename: 'vendor.bundle.js'
});
module.exports = {
context: __dirname + '/jsFolder',
entry: {
source: ['./app.jsx'],
vendor: [
'react',
'react-dom',
'redux',
'axios'
]
},
output: {
filename: "[name].bundle.js"
},
resolve: {
extensions: ['.js', '.jsx']
},
module: {
loaders: [
{ test: /\.json$/, loader: 'json-loader' },
{
loader: 'babel-loader',
exclude: /node_modules/,
query: {
presets: ['react', 'es2015', 'stage-0']
}
},
{ test: /\.css$/, loader: "css-loader" },
]
},
plugins: [commonChunkPlugin, definePlugin, uglifyPlugin],
node: {
console: true,
fs: 'empty',
net: 'empty',
tls: 'empty'
},
target: 'web'
};

express.static() only works when run locally

For some reason when I run my express server on my machine it it serves the static files in my build folder without any problem, but when I deploy on Heroku I'm getting the following 404 error:
HTTP404: NOT FOUND - The server has not found anything matching the requested URI (Uniform Resource Identifier).
GET - http://playcheckerswithme.herokuapp.com/
Here's my directory structure:
.
├──index.js
├──webpack.config.js
├──package.json
├──.gitignore
├──node_modules
| ├── ...
|
├──src
| ├──assets
| | ├──index.html
| | ├──images
| | | ├──...
| |
| ├──components
| | ├──...
| |
| ├──reducers
| ├──...
|
├──build
├──index.html
├──bundle.js
├──images
├──...
And here's index.js:
var express = require('express');
var app = express();
var http = require('http').Server(app);
app.use(express.static(__dirname + '/build'))
http.listen(process.env.PORT || 3000, function(){
console.log(`listening on port: ${process.env.PORT || '3000'}`);
});
app.use(express.static(__dirname + '/build')) should be serving all static files in my build folder, but it seems that in production it isn't working correctlly. Any ideas?
Edit -
For extra context here are my package.json and webpack.config.js files
package.json
{
"name": "Checkers",
"version": "1.0.0",
"description": "react + redux + express environment for a checkers app",
"private": true,
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"webpack-server": "webpack-dev-server --hot --progress --colors",
"start": "node build/index.js"
},
"keywords": [],
"author": "Nathan Jones",
"devDependencies": {
"autoprefixer-loader": "^3.1.0",
"babel-core": "^6.3.21",
"babel-loader": "^6.2.0",
"babel-preset-es2015": "^6.3.13",
"babel-preset-react": "^6.3.13",
"babel-preset-stage-0": "^6.3.13",
"copy-webpack-plugin": "^4.0.1",
"file-loader": "^0.10.0",
"react-hot-loader": "^1.2.8",
"source-map-loader": "^0.1.5",
"url-loader": "^0.5.7",
"webpack": "^1.11.0",
"webpack-dev-server": "^1.10.1"
},
"dependencies": {
"express": "^4.15.2",
"react": "^15.4.2",
"react-dom": "^15.4.2",
"react-redux-utilities": "^1.0.7"
}
}
webpack.config.js
var { resolve } = require('path');
var webpack = require('webpack');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
var CopyWebpackPlugin = require('copy-webpack-plugin');
function getEntrySources(sources) {
if (process.env.NODE_ENV !== 'production') {
sources.push('webpack-dev-server/client?http://localhost:8080');
sources.push('webpack/hot/only-dev-server');
}
return sources;
}
module.exports = {
entry: getEntrySources(['./src/app.js']),
output: {
filename: 'bundle.js',
path: resolve(__dirname, 'build'),
publicPath: '/'
},
devtool: 'source-map',
devServer: {
inline: true,
hot: true,
contentBase: resolve(__dirname, 'build'),
publicPath: '/'
},
module: {
preLoaders: [
{
test: /\.jsx?$/,
exclude: /node_modules/,
loader: 'source-map'
}
],
loaders: [
{
test: /\.jsx?$/,
exclude: /node_modules/,
loaders: ['react-hot', 'babel-loader?presets[]=es2015,presets[]=react,presets[]=stage-0'],
},
{
test: /\.scss$/,
loader: ExtractTextPlugin.extract(
'style-loader',
'css-loader?sourceMap!autoprefixer?browsers=last 3 versions!sass-loader?sourceMap'
)
},
{
test: /\.(ttf|otf|eot|svg|woff(2)?)(\?[a-z0-9]+)?$/,
loader: 'file-loader?name=fonts/[name].[ext]'
},
{
test: /\.(png|jpg|jpeg)$/,
loader: 'url-loader'
}
]
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
new CopyWebpackPlugin([{from: 'src/assets/', force: true}], {copyUnmodified: true})
]
};
Your npm "start" script is "node build/index.js" which means that __dirname would be pointing to <project-root>/build. You need to change your build/index.js file to use this instead since it's already in the build folder:
if (__dirname.slice(-6) === '/build') { // For production
app.use(express.static(__dirname))
} else { // For development
app.use(express.static(__dirname + '/build'))
}
Try remove slash:
app.use(express.static(__dirname + 'build'));
or use path module:
var express = require('express');
var path = require('path');
var app = express();
var http = require('http').Server(app);
app.use(express.static(path.join(__dirname, 'build')));
http.listen(process.env.PORT || 3000, function(){
console.log(`listening on port: ${process.env.PORT || '3000'}`);
});

Deploying an app developed with babel-node

I have been using webpack for the first time, starting for a tutorial, but I'm stuck trying to deploy this to digital ocean.
I have been running the server during development by typing
npm start
Which calls:
babel-node devServer.js
This works fine for me locally, but when I try to run it on digital ocean it first works for a few minutes, then dies. I read somewhere that running babel-node on a live server isn't recommended, so I guess this is something to do with that.
I can see from this line in package.json :
"build:webpack": "NODE_ENV=production node_modules/webpack/bin/webpack.js --config webpack.config.prod.js",
that I should be doing some sort of deploy step, which I do, but I can still only get it to run using npm start, which uses babel-node devServer.js
How do I actually run this after doing the build? What am I doing wrong?
From package.json:
"scripts": {
"build:webpack": "NODE_ENV=production node_modules/webpack/bin/webpack.js --config webpack.config.prod.js",
"build": "npm run clean && npm run build:webpack",
"test": "mocha --compilers js:babel-core/register --require ./test/test_helper.js \"test/**/*#(.js|.jsx)\"",
"clean": "rimraf dist",
"start": "babel-node devServer.js",
"tunnel": "browser-sync start --proxy localhost:7770 --tunnel wesbos",
"test:watch": "npm run test -- --watch"
},
My dev config:
var path = require('path');
var webpack = require('webpack');
module.exports = {
devtool: 'source-map',
entry: [
'webpack-hot-middleware/client',
'./client/index'
],
output: {
path: path.join(__dirname, 'dist'),
filename: 'bundle.js',
publicPath: '/static/'
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
new webpack.NoErrorsPlugin()
],
resolve: {
root: [
path.resolve('./client')
],
alias: {
},
},
module: {
loaders: [
// js
{
test: /\.js$/,
loaders: ['babel'],
include: path.join(__dirname, 'client')
},
// CSS
{
test: /\.styl$/,
include: path.join(__dirname, 'client'),
loader: 'style-loader!css-loader!stylus-loader'
}
]
}
};
Prod config:
var path = require('path');
var webpack = require('webpack');
module.exports = {
devtool: 'source-map',
entry: [
'./client/index'
],
output: {
path: path.join(__dirname, 'dist'),
filename: 'bundle.js',
publicPath: '/static/'
},
plugins: [
new webpack.optimize.OccurenceOrderPlugin(),
new webpack.DefinePlugin({
'process.env': {
'NODE_ENV': "'production'"
}
}),
new webpack.optimize.UglifyJsPlugin({
compressor: {
warnings: false
}
})
],
resolve: {
root: [
path.resolve('./client')
],
alias: {
},
},
module: {
loaders: [
// js
{
test: /\.js$/,
loaders: ['babel'],
include: path.join(__dirname, 'client')
},
// CSS
{
test: /\.styl$/,
include: path.join(__dirname, 'client'),
loader: 'style-loader!css-loader!stylus-loader'
}
]
}
};
You could try using babel-loader and running the build script in npm start
In your package.json:
"start": "npm run build && babel-node --presets es2015 devServer.js"
Also include the following dependencies in your package.json:
"babel-loader": "^6.2.0",
"babel-preset-es2015": "^6.3.13",
In your webpack.config:
loaders: [
{ test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader',
query: {
presets: ['react', 'es2015']
}
}
]

Resources