webpack-hot-middleware webpack express typescript - node.js

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"
}
}

Related

How can I connect to my backend server from my frontend dev server react

I am running my node backend on port 8000 and my dev server front end on port 9002. Ive tried a few ways to proxy to my backend when running dev, but all seem to fail (maybe because i also have
"type": "module"
)? I have tried http-proxy-middleware, but with no success.
In my server.cjs file, I have an app.get('/properties'....). It retrieves an array of objects with address, city and zip. On postman, running on port 8000, I get the correct response. Additionally, when i run start on port 8000 on my frontend, I also get the correct response. However, when I run on port 9002, my error is http://localhost:9002/properties 404, not found.
For development purposes, I need to be able to work on development, that way I wint have to run build for every UI change I make. Here is some snippets of my code below:
package.json
{
"name": "properties-three",
"version": "1.0.0",
"description": "",
"main": "index.js",
"type": "module",
"proxy": "http://localhost:8000",
"scripts":
{
"test": "echo \"Error: no test specified\" && exit 1",
"dev": "webpack-dev-server --open --config webpack.config.cjs",
"start": "nodemon --use_strict server.mjs",
"build": "webpack --mode production"
}, "
author": "",
"license": "ISC",
"devDependencies": {
"#babel/core": "^7.19.6",
"#babel/preset-env": "^7.19.4",
"#babel/preset-react": "^7.18.6",
"babel-loader": "^8.2.5",
"copy-webpack-plugin": "^11.0.0",
"html-webpack-plugin": "^5.5.0",
"webpack": "^5.74.0",
"webpack-cli": "^4.10.0"
},
"dependencies": {
"axios": "^1.1.3",
"cors": "^2.8.5",
"express": "^4.18.2",
"http-proxy-middleware": "^2.0.6",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.4.2",
"webpack-dev-server": "^4.11.1"
}
}
setupProxy.js
import {createProxyMiddleware} from "http-proxy-middleware";
module.exports = function(app) {
app.use(
'/api/properties',
createProxyMiddleware({
target: 'http://localhost:8000',
changeOrigin: true,
secure: false,
ws: true
})
);
};
webpack.config.cjs:
const HtmlWebPackPlugin = require("html-webpack-plugin");
const path = require('path');
const htmlPlugin = new HtmlWebPackPlugin({
template: "./src/index.html",
filename: "./index.html"
});
module.exports = {
entry: "./src/index.js",
output: { // NEW
path: path.resolve(__dirname, 'dist'),
filename: "bundle.js",
publicPath: "/"
}, // NEW Ends
devServer: {
port: 9002,
static: path.join(__dirname, 'dist'),
hot: true,
historyApiFallback: true
},
resolve: {
extensions: ['.ts', ".js", ".jsx"],
},
mode: "development",
plugins: [htmlPlugin],
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: {
loader: "babel-loader"
}
}
]
}
};
server.mjs
import express from 'express';
import cors from 'cors';
import path from "path";
import properties from './routes/properties.routes.js'
import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const app = express();
const PORT = 8000;
app.use(express.static(`${process.cwd()}/build`));
app.use(express.static(`${process.cwd()}/public`));
app.use(cors());
app.use(express.json());
app.use("/properties", properties);
app.use(express.static(path.join(__dirname, '/dist')));
app.get('/*', function (req, res) {
res.sendFile(path.join(__dirname, 'dist', 'index.html'));
});
app.listen(PORT, () => console.log(`server is running on http://locahost:${PORT}`));
App.js
import React, {useState, useEffect} from "react";
import axios from "axios";
function App() {
const [properties, setProperties] = useState([]);
useEffect(() =>{
axios.get('/properties').then((response) =>{
setProperties(response.data)
})
}, []);
return (
<div className="App">Test</div>
);
}
export default App;
I have tried changing my setupProxy.js to use
import {createProxyMiddleware} from "http-proxy-middleware";
instead of
const { createProxyMiddleware } = require('http-proxy-middleware');
I have also tried changing in the component
axios.get('/properties')
to
axios.get('/api/properties')
I figured out the fix. As #NirAlfasi pointed out, I was running the BE and the FE on the same machine. Additionally, my db is currently using mock data stored within my project (for the meantime).
Within my App.js, all I needed to do was create a const baseUrl pointing to my BE port. Then in the get request on the FE, point to that baseUrl, plus the endpoint.
const baseURL = 'http://localhost:8000';
axios.get(`${baseURL}/properties`).then((response)...

Angular 6 ssr page load is too big

For the first time I am very disappointed of angular or myself because I don't know if it's angular problem or mine. I have angular 6.0.0 project which is not very big, but not small too. I implemented angular universal ssr in my project.
Results without ssr : *hint I have lazy modules. Main is my first page only.
Results with ssr on heroku free server (but same main.js size on localhost, just load time on localhost is 40ms) :
I think those numbers are way too big ? SSR works fine, but the load time is terrible - from 184ms to 2.50s.
Detailed project information :
Package.json ( hint: i am using heroku to deploy my web to nodejs server) :
{
....
"engines": {
"node": "8.11.1",
"npm": "6.5.0"
},
"scripts": {
"ng": "ng",
"start:heroku": "node dist/server",
"start": "ng serve --aot",
"build": "ng build",
"lint": "ng lint Deals",
"build:client-and-server-bundles": "ng build --prod --aot && ng run Deals:server:production",
"build:prerender": "npm run build:client-and-server-bundles && npm run compile:server && npm run generate:prerender",
"build:ssr": "npm run build:client-and-server-bundles && npm run compile:server",
"compile:server": "tsc -p server.tsconfig.json",
"generate:prerender": "cd dist && node prerender",
"serve:prerender": "cd dist/browser && http-server",
"serve:ssr": "node dist/server ",
"postinstall": "npm run build:ssr"
},
"pre-commit": [],
"private": true,
"dependencies": {
"#angular-devkit/build-angular": "0.6.0",
"#angular/animations": "^6.1.10",
"#angular/cdk": "^7.1.0",
"#angular/cli": "6.0.0",
"#angular/common": "^6.0.0",
"#angular/compiler": "^6.0.0",
"#angular/compiler-cli": "^6.0.0",
"#angular/core": "^6.0.0",
"#angular/fire": "^5.1.1",
"#angular/forms": "^6.0.0",
"#angular/http": "^6.0.0",
"#angular/language-service": "^6.0.0",
"#angular/material": "^7.1.0",
"#angular/platform-browser": "^6.0.0",
"#angular/platform-browser-dynamic": "^6.0.0",
"#angular/platform-server": "^6.0.0",
"#angular/pwa": "^0.11.4",
"#angular/router": "^6.0.0",
"#angular/service-worker": "^6.0.0",
"#ngrx/core": "^1.2.0",
"#ngrx/store-devtools": "^6.1.2",
"#nguniversal/common": "^6.0.0",
"#nguniversal/express-engine": "^6.0.0",
"#nguniversal/module-map-ngfactory-loader": "^6.0.0",
"#ngx-translate/core": "^10.0.2",
"#ngx-translate/http-loader": "^4.0.0",
"#types/node": "^8.0.30",
"angular2-cookie": "^1.2.6",
"angularfire2": "^5.1.1",
"angulartics-clicky": "^1.0.0",
"angulartics2": "^6.3.1",
"codelyzer": "^4.0.2",
"core-js": "^2.4.1",
"express": "^4.15.2",
"firebase": "^5.6.0",
"hammerjs": "^2.0.8",
"http-server": "^0.10.0",
"ngx-page-scroll": "^5.0.1",
"nodemailer": "^4.7.0",
"npm": "^6.4.1",
"pre-commit": "^1.2.2",
"reflect-metadata": "^0.1.10",
"rxjs": "^6.2.2",
"ts-loader": "^4.2.0",
"tslint": "^5.7.0",
"typescript": "~2.7.2",
"zone.js": "^0.8.26"
},
Server.ts which renders ssr :
import 'zone.js/dist/zone-node';
import 'reflect-metadata';
import { enableProdMode } from '#angular/core';
// Express Engine
import { ngExpressEngine } from '#nguniversal/express-engine';
// Import module map for lazy loading
import { provideModuleMap } from '#nguniversal/module-map-ngfactory-loader';
import * as express from 'express';
import { join } from 'path';
// Faster server renders w/ Prod mode (dev mode never needed)
enableProdMode();
// Express server
const app = express();
const PORT = process.env.PORT || 4000;
const DIST_FOLDER = join(process.cwd(), 'dist');
// * NOTE :: leave this as require() since this file is built Dynamically from webpack
const { AppServerModuleNgFactory, LAZY_MODULE_MAP } = require('./server/main');
// Our Universal express-engine (found # https://github.com/angular/universal/tree/master/modules/express-engine)
app.engine('html', ngExpressEngine({
bootstrap: AppServerModuleNgFactory,
providers: [
provideModuleMap(LAZY_MODULE_MAP),
// In case you want to use an AppShell with SSR and Lazy loading
// you'd need to uncomment the below. (see: https://github.com/angular/angular-cli/issues/9202)
// {
// provide: NgModuleFactoryLoader,
// useClass: ModuleMapNgFactoryLoader,
// deps: [
// Compiler,
// MODULE_MAP
// ],
// },
]
}));
app.set('view engine', 'html');
app.set('views', join(DIST_FOLDER, 'browser'));
// Example Express Rest API endpoints
// app.get('/api/**', (req, res) => { });
// Server static files from /browser
app.get('*.*', express.static(join(DIST_FOLDER, 'browser'), {
maxAge: '1y'
}));
// All regular routes use the Universal engine
app.get('*', (req, res) => {
res.render('index', { req });
});
// Start up the Node server
app.listen(PORT, () => {});
server.tsconfig.json :
{
"compileOnSave": false,
"compilerOptions": {
"outDir": "./dist",
"sourceMap": true,
"declaration": false,
"moduleResolution": "node",
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"target": "es5",
"typeRoots": [
"node_modules/#types"
],
"lib": [
"es2017",
"dom"
]
},
"include": ["server.ts", "prerender.ts"]
}
Maybe you need to use compression?
https://github.com/Angular-RU/angular-universal-starter/blob/6d1e980655a55b2e1651ac5d040aaff80bc8fe8c/server.ts#L45
server.ts
import 'zone.js/dist/zone-node';
import 'reflect-metadata';
const test = process.env['TEST'] === 'true';
const domino = require('domino');
const fs = require('fs');
const path = require('path');
const template = fs.readFileSync(path.join(__dirname, '.', 'dist', 'index.html')).toString();
const win = domino.createWindow(template);
const files = fs.readdirSync(`${process.cwd()}/dist-server`);
global['window'] = win;
Object.defineProperty(win.document.body.style, 'transform', {
value: () => {
return {
enumerable: true,
configurable: true,
};
},
});
global['document'] = win.document;
global['CSS'] = null;
// global['XMLHttpRequest'] = require('xmlhttprequest').XMLHttpRequest;
global['Prism'] = null;
import { enableProdMode } from '#angular/core';
import * as express from 'express';
import * as compression from 'compression';
import * as cookieparser from 'cookie-parser';
const { provideModuleMap } = require('#nguniversal/module-map-ngfactory-loader');
const mainFiles = files.filter((file) => file.startsWith('main'));
const hash = mainFiles[0].split('.')[1];
const { AppServerModuleNgFactory, LAZY_MODULE_MAP } = require(`./dist-server/main.${hash}`);
import { ngExpressEngine } from '#nguniversal/express-engine';
import { REQUEST, RESPONSE } from '#nguniversal/express-engine/tokens';
const PORT = process.env.PORT || 4000;
import { ROUTES } from './static.paths';
import { exit } from 'process';
enableProdMode();
const app = express();
app.use(compression());
app.use(cookieparser());
const redirectowww = false;
const redirectohttps = true;
const wwwredirecto = true;
app.use((req, res, next) => {
// for domain/index.html
if (req.url === '/index.html') {
res.redirect(301, 'https://' + req.hostname);
}
// check if it is a secure (https) request
// if not redirect to the equivalent https url
if (
redirectohttps &&
req.headers['x-forwarded-proto'] !== 'https' &&
req.hostname !== 'localhost'
) {
// special for robots.txt
if (req.url === '/robots.txt') {
next();
return;
}
res.redirect(301, 'https://' + req.hostname + req.url);
}
// www or not
if (redirectowww && !req.hostname.startsWith('www.')) {
res.redirect(301, 'https://www.' + req.hostname + req.url);
}
// www or not
if (wwwredirecto && req.hostname.startsWith('www.')) {
const host = req.hostname.slice(4, req.hostname.length);
res.redirect(301, 'https://' + host + req.url);
}
// for test
if (test && req.url === '/test/exit') {
res.send('exit');
exit(0);
return;
}
next();
});
app.engine(
'html',
ngExpressEngine({
bootstrap: AppServerModuleNgFactory,
providers: [provideModuleMap(LAZY_MODULE_MAP)],
}),
);
app.set('view engine', 'html');
app.set('views', 'src');
app.get('*.*', express.static(path.join(__dirname, '.', 'dist')));
app.get(ROUTES, express.static(path.join(__dirname, '.', 'static')));
app.get('*', (req, res) => {
global['navigator'] = req['headers']['user-agent'];
const http =
req.headers['x-forwarded-proto'] === undefined ? 'http' : req.headers['x-forwarded-proto'];
const url = req.originalUrl;
// tslint:disable-next-line:no-console
console.time(`GET: ${url}`);
res.render(
'../dist/index',
{
req: req,
res: res,
providers: [
{
provide: REQUEST,
useValue: req,
},
{
provide: RESPONSE,
useValue: res,
},
{
provide: 'ORIGIN_URL',
useValue: `${http}://${req.headers.host}`,
},
],
},
(err, html) => {
if (!!err) {
throw err;
}
// tslint:disable-next-line:no-console
console.timeEnd(`GET: ${url}`);
res.send(html);
},
);
});
app.listen(PORT, () => {
console.log(`listening on http://localhost:${PORT}!`);
});

React - can't access environment variables in the server side

I have created an app using Create-React-App on the client side and using express in the server side. I have created an .env file for my environment variables where the DB_USER and DB_PASSWORD will be stored.
In the server side, I would like to use a mongoose connection and the .env variables will be the credentials when connecting to the mongodb server.
I'm getting undefined variables in the terminal console when I do process.env.DB_USER. Instead I'm getting my OS environment variables, and NodeJS variables. However, I do see the variables when I do console.log(process.env.DB_USER) in chrome console/client side.
I tried to install dotenv, dotenv-webpack and configure my webpack.config but nothing seem to be working. I have also added REACT_APP_* as prefix to my variables, but still undefined values.
Also when I use dotenv and set this require('dotenv').config() in my index.js file, it complains about an fs dependency??
I just can't get the environment variables to be read in the server folder, ideally it will be nice to have these variables read in the client and server folder. Has anyone encountered this issue before? I'm just learning how to use Create-React-App and webpack. Your help will be appreciated!!!
server.js
//server.js
const express = require('express');
const path = require('path');
const router = require('./routes/routes.js')
const app = express();
const mongoose = require('mongoose');
const bodyParser = require('body-parser');
const user = process.env.REACT_APP_DBUSER;
const password = process.env.REACT_APP_DBPASSWORD;
//tells express frontend will reside in client folder
app.use(express.static(path.join(__dirname, '../build')));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
console.log('node', process.env);
console.log(user);//this is undefined
//connect to mongoDB
mongoose.connect(`mongodb://${user}:${password}#ds141812.mlab.com:41812/note_app_db`, { useNewUrlParser: true });
let conn = mongoose.connection;
conn.on('error', console.error.bind(console, 'connection:'));
conn.once('open', () => {
console.log('connected to database');
});
//pass in routes from router const
app.use('/',router)
module.exports=app;
webpack.config
const webpack = require('webpack');
const path = require('path');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const Dotenv = require('dotenv-webpack');
const config = {
entry: __dirname + '/client/js/index.jsx',
output: {
path: __dirname + '/build',
filename: 'bundle.js'
},
resolve: {
extensions: ['.js', '.jsx', '.css']
},
module: {
rules: [
{
test: /\.jsx?/,
exclude: /node_modules/,
loader: 'babel-loader',
query: {
presets: ['#babel/preset-env', '#babel/react']
}
},
{
test: /\.(png|jpg|svg|gif|mp4|mov)$/,
use: [{
loader: 'file-loader',
options: {
name: '/assets/[name]-[hash:8].[ext]'
}
}]
},
{
test: /\.scss$/,
loader: 'style-loader!css-loader!sass-loader'
}
]
},
devServer: {
publicPath: '/',
contentBase: __dirname + '/build',
port: 5000,
historyApiFallback: {
index: 'index.html'
}
},
plugins: [
new CopyWebpackPlugin([
{ from: './client/index.html', to: './index.html' }
]),
new Dotenv({
path: './.env',
}),
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: JSON.stringify("development"),
REACT_APP_DBUSER: JSON.stringify(process.env.REACT_APP_DBUSER),
REACT_APP_DBPASSWORD: JSON.stringify(process.env.REACT_APP_DBPASSWORD)
}
})
]
}
module.exports = config
index.jsx
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
require('dotenv').config({path: '../../.env'});//is this how it supposed to look like???
ReactDOM.render(<App />, document.getElementById('root'));
package.json
{
"name": "note_app",
"version": "0.1.0",
"private": true,
"dependencies": {
"#babel/core": "^7.1.5",
"#babel/preset-env": "^7.1.5",
"#babel/preset-react": "^7.0.0",
"axios": "^0.18.0",
"babel-loader": "^8.0.4",
"body-parser": "^1.18.3",
"copy-webpack-plugin": "^4.6.0",
"css-loader": "^1.0.1",
"dotenv": "^6.1.0",
"dotenv-webpack": "^1.5.7",
"env-cmd": "^8.0.2",
"express": "^4.16.4",
"file-loader": "^2.0.0",
"mongoose": "^5.3.11",
"node-sass": "^4.10.0",
"nodemon": "^1.18.6",
"react": "^16.6.1",
"react-dom": "^16.6.1",
"react-modal": "^3.6.1",
"react-router-dom": "^4.3.1",
"react-scripts": "2.1.1",
"sass-loader": "^7.1.0",
"style-loader": "^0.23.1",
"webpack-cli": "^3.1.2"
},
"scripts": {
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject",
"webpack": "webpack --mode development",
"dev": "npm run webpack && nodemon bin/www"
},
"eslintConfig": {
"extends": "react-app"
},
"browserslist": [
">0.2%",
"not dead",
"not ie <= 11",
"not op_mini all"
]
}
.env file
REACT_APP_DBUSER="username"
REACT_APP_DBPASSWORD="password"
file structure
Found the solution, so dotenv is fine and I had uninstall dotenv-webpack. In order for the server.js file to read the environment variables, I just needed to add require('dotenv').config(); in this file. It should be good to go!
Replacing my old incorrect answer.
Rather than using that dotenv-webpack package, could you use this package:
https://www.npmjs.com/package/dotenv
In theory, you can remove that new DotEnv from your plugins and then when you call require('dotenv').config(), your webpack.config.js should have access to those env variables, then your webpack define plugin will pick them up and inject them into your code

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'}`);
});

How to make Webpack and Node work together?

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'
};

Resources