How to use Express to serve a frontend compiled by Webpack? - javascript

I have the following Express app which serves static files, specifically public/index.html.
server.js
const express = require('express');
const app = express();
const port = 8000;
app.use(express.static('public'));
app.listen(port, () => {
console.log(`Example app listening # http://localhost:${port}`);
});
public/index.html
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
</head>
<body>
<script src="./dist/bundle.js"></script>
<script>
// possibly make API calls to http://localhost:8000
</script>
</body>
</html>
This HTML file requires a script located at ./dist/bundle.js.
The bundle is compiled using Webpack, which has this configuration:
webpack.config.js
const path = require("path");
const webpack = require("webpack");
const TerserPlugin = require("terser-webpack-plugin");
module.exports = {
entry: "./src/index.js",
mode: "development",
module: {
rules: [
{
test: /\.(js)$/,
exclude: /(node_modules)/,
loader: "babel-loader",
options: { presets: ["#babel/env"] }
},
{
test: /\.css$/,
use: ["style-loader", "css-loader"]
}
]
},
resolve: { extensions: ["*", ".js"] },
output: {
path: path.resolve(__dirname, "public/dist/"),
publicPath: "/dist/",
filename: "bundle.js"
},
devServer: {
contentBase: __dirname,
port: 3000,
publicPath: "http://localhost:3000/dist/",
hot: true
},
plugins: [new webpack.HotModuleReplacementPlugin()],
optimization: {
minimize: true,
minimizer: [new TerserPlugin()]
}
};
As you can see in output.path, the bundle's compiled output should go into public/dist which is inside the static file directory served by Express. That way, index.html can access it.
Now the problem is, my development workflow is incredibly inefficient.
webpack --mode development to write the latest bundle.js into public/dist
node server.js to re-run the server (in case there were any server-side changes)
If I were using webpack-dev-server alone without my own backend, I would get all the nice benefits of hot reloading, HMR, etc, and I don't have to worry about recompiling.
I know I can run webpack-dev-server and server.js concurrently on two different ports, but index.html would be served from server.js and wouldn't be able to access the in memory dist/bundle.js served by the webpack dev server.
Is there a way to serve a Webpack-compiled frontend from an Express server and still get all the nice benefits of using Webpack-dev-server, specifically hot reloading?
Thanks!

Related

webpack-dev-server not serving index.html generated by HtmlWebPackPlugin

I have the following webpack.config.js
const path = require("path");
const webpack = require("webpack");
const HtmlWebPackPlugin = require("html-webpack-plugin");
const TerserPlugin = require("terser-webpack-plugin");
module.exports = {
entry: "./src/index.js",
mode: "development",
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /(node_modules)/,
use: {
loader: "babel-loader"
}
},
{
test: /\.css$/,
use: ["style-loader", "css-loader"]
}
]
},
resolve: { extensions: ["*", ".js"] },
output: {
path: path.resolve(__dirname, "public/dist/"),
publicPath: "/dist/",
filename: "bundle.js"
},
devServer: {
contentBase: __dirname,
port: 3000,
publicPath: "http://localhost:3000/dist/",
proxy: {
'/api' : {
target: 'http://localhost:8000',
secure: false
}
},
hot: true
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
new HtmlWebPackPlugin({
hash: true,
template: "./src/index.html",
filename: "index.html"
})
],
optimization: {
minimize: true,
minimizer: [new TerserPlugin()]
}
};
When I run webpack-dev-server, everything compiles fine, but when I navigate to localhost:3000/public/dist/index.html, I get a "file not found" error.
However, when I run webpack --mode development, the index.html file is correctly written to public/dist and I'm able to open it on my web browser.
I'm not sure why this file is correctly written when webpack is run normally, but not when the dev server is ran. I know the webpack dev server serves files from memory so I don't expect it to show up on my filesystem but I at least expect to be able to open it from the browser.
Thanks!
Webpack dev server doesn't actually build the files to your project directory. From their docs:
webpack-dev-server doesn't write any output files after compiling. Instead, it keeps bundle files in memory and serves them as if they were real files mounted at the server's root path.
So running webpack-dev-server, you shouldn't expect to see any built files (like your resultant index.html) in your dist directory. It will be serving the project from memory directly to your localhost:3000, or whatever pathname you choose. However, running webpack actually builds the files to your destination directory, and you'll find them there. Hope that clears it up for you.

How to make Webpack build on Express server started

I'm new to React and Webpack. I have webpack.config.js like this:
const webpack = require('webpack');
const path = require('path');
module.exports = {
cache: true,
entry: {
'user': ['./client/User/index']
},
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist'),
publicPath: '/static'
},
module: {
rules: [
{
test: /\.js$/,
loader: 'babel-loader',
exclude: /node_modules/,
include: path.join(__dirname, 'client'),
query: { cacheDirectory: true }
}
]
},
node: { fs: 'empty' }
};
And this is my app.js entry of Express server:
import express from 'express';
import bodyParser from 'body-parser';
import morgan from 'morgan';
import webpack from 'webpack';
import session from 'express-session';
const app = express();
import config from './webpack.config';
const compiler = webpack(config);
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use('/assets', express.static(`${__dirname}/static`));
app.use('/', require('./server/route/index'));
app.listen(3000, () => console.log('Our Blog is listening on port 3000...'));
Webpack does not build and yields any bundle in the dist folder as configured. It can only build when I use webpack-cli command. Besides, in my html document, I include the bundle as <script src='/static/user.bundle.js'></script>. I thought this must be the correct path, because it will map /static to dist folder on local machine as configured where the bundle is exactly located after build. But it keeps sending me 404 error due to resource not found. May you help me solve this?
webpack creates the file in memory. so you can't see it as per documentation.
webpack-dev-server doesn't write any output files after compiling. Instead, it keeps bundle files in memory and serves them as if they were real files mounted at the server's root path. If your page expects to find the bundle files on a different path, you can change this with the publicPath option in the dev server's configuration.
I suspect because you did not specify it as production mode that's why it's not writing to dist folder. Also you need to use webpack-dev-middleware. Here's an example for you how to use it
I hope this helps.

Static resources not showing up in Chrome Dev Tools

Messing round with a simple react + webpack app, however I'm having issues debugging because the static resources don't seem to be loading in dev tools (and I can't work out why breakpoints aren't working!)
Got the following in my webpack.config.js file:
var ExtractTextPlugin = require('extract-text-webpack-plugin');
const webpack = require('webpack');
const path = require('path');
module.exports = {
devtool: 'eval-source-map',
entry: path.join(__dirname, 'src', 'app.js'),
output: {
path: path.join(__dirname, 'dist'),
filename: 'bundle.js'
},
module: {
loaders: [
{
test: path.join(__dirname, 'src'),
loader: ['babel-loader'],
query: {
cacheDirectory: 'babel_cache',
presets: ['react', 'es2015']
}
},
{
test: /\.css/,
loader: ExtractTextPlugin.extract("style-loader", "css-loader")
},
{
test: /\.(eot|svg|ttf|woff|woff2)$/,
loader: 'file-loader'
}
]
},
plugins: [
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV)
}),
new webpack.optimize.DedupePlugin(),
new webpack.optimize.OccurenceOrderPlugin(),
new webpack.optimize.UglifyJsPlugin({
compress: { warnings: false },
mangle: true,
sourcemap: false,
beautify: false,
dead_code: true
}),
new ExtractTextPlugin("styles.css")
]
};
And my express app serves up the /dist folder:
app.use('/static', Express.static(path.join(__dirname, '..', 'dist')));
However, when I try to view the source code (using a sourcemap), my dev tools is only showing the following:
When I navigate to localhost:3000/static/bundle.js and localhost:3000/static/bundle.js.map it shows the files in plaintext. My app also 'works' (ie. the JS is being used) but it doesn't seem to show in Chrome. Am I missing something very obvious here?
Thanks for any help!
So found a way to do this, just incase you're reading this in the future.
The issue I had was that I was using index.ejs to display the markup:
index.ejs
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Title</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<div id="main"><%- markup -%></div>
</body>
</html>
server.js
return res.render('index', { markup });
I wasn't including dist/bundle.js into my .ejs file, and this meant that Chrome Tools didn't pick it up. I added <script src="static/bundle.js"></script> to my index.ejs (even though not strictly necessary for the code to work!) and it began to appear in chrome dev tools at last.
I can now finally set breakpoints in my code and debug, as well as opening my .js files in the Source tab, so all appears to be working fine.
I'd be interested to hear if there's another way of doing this, one that doesn't involve needlessly getting the bundle.js script in the html.

React WebPack - Send My Work to online Server

I have a online server where i put my works and portfolio and stuff , i have made a simple app in react and i want to run it in my server, but im getting the error
http://hseleiro.pt/bundle.js 404 (Not Found)
What im i doing wrong ? i dont know if im making the correct steps to show my react app in a online server, could some one help me ?
I have tried to change my pat on my Webpack.config.js but with no sucess.
Index.HTML
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="http://www.hseleiro.pt/ReduxSimpleStarter/style/style.css">
<link rel="stylesheet" href="https://cdn.rawgit.com/twbs/bootstrap/v4-dev/dist/css/bootstrap.css">
</head>
<body>
<div class="container"></div>
</body>
<script src="/bundle.js"></script>
</html>
WebPack.Config.Js
module.exports = {
entry: [
'./src/index.js'
],
output: {
path: __dirname,
publicPath: '/',
filename: 'bundle.js'
},
module: {
loaders: [{
exclude: /node_modules/,
loader: 'babel',
query: {
presets: ['react', 'es2015', 'stage-1']
}
}]
},
resolve: {
extensions: ['', '.js', '.jsx']
},
devServer: {
historyApiFallback: true,
contentBase: './'
}
};
Thank you !!
I believe you want to expose the main index.html file you are using for your site and the bundle file on your server. The index file should contain a script tag, like this <script src="/bundle.js"></script> that references the source bundle you created with Webpack. When the site loads the bundle, your react app should start running.

html-webpack-plugin not inject js file into index.html when using webpack-dev-server

Here is my webpack config :
var path = require('path');
var webpack = require('webpack')
var HtmlWebpackPlugin = require('html-webpack-plugin')
var fs = require('fs'),buildPath='./dist/';
var folder_exists = fs.existsSync(buildPath);
if(folder_exists == true)
{
require('shelljs/global')
rm('-rf', 'dist')
};
module.exports = {
entry: './src/main',
output: {
path: path.join(__dirname, './dist'),
filename: '[name].js',
publicPath: '/dist/'
},
devServer: {
historyApiFallback: true,
hot: false,
inline: true,
grogress: true,
},
// "vue-hot-reload-api": "^1.2.2",
module: {
loaders: [
{ test: /\.vue$/, loader: 'vue' },
{ test: /\.js$/, loader: 'babel', exclude: /node_modules/ },
{ test: /\.css$/, loader: 'style-loader!css-loader'},
//install css-loader style-loader sass-loader node-sass --save-dev
{ test: /\.scss$/, loader: 'style!css!sass?sourceMap'},
{ test: /\.(png|jpg|gif)$/, loader: 'url-loader?limit=8192&name=images/[name].[ext]'},
{ test: /\.(html|tpl)$/, loader: 'html-loader' },
]
},
vue: {
loaders: {
js:'babel',
css: 'style-loader!css-loader',
sass:'style!css!sass?sourceMap'
}
},
babel: {
presets: ['es2015'],
plugins: ['transform-runtime']
},
plugins:[
new HtmlWebpackPlugin({
template: 'index.html',
filename: './index.html',
inject:true
}),
],
resolve: {
extensions: ['', '.js', '.vue'],
alias: {
filter: path.join(__dirname, './src/filters'),
components: path.join(__dirname, './src/components')
}
},
devtool: 'eval-source-map'
};
And in package.json:
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "webpack-dev-server --inline",
"build": "webpack --config webpack.config.prod.js"
},
When I run npm start, in localhost the js file is not injected in index.html.
If I run webpack or npm run build, the js file is injected successfully.
Can html-webpack-plugin also inject js file into index.html when I'm in localhost?
This issue is related specifically to the fact that the webpack-development-server does not have the ability to write files to your local file system and instead writes its files to MEMORY Only.
This is why you were able to properly generate files wth Html-Webpack-Plugin when you run the default webpack build command (NOT the WDS / Webpack-Development-Server) with:
webpack
Alternately since you were using vue.js Webpack-simple project (https://github.com/vuejs-templates/webpack-simple/tree/master/template) you were also able to use the npm scripts that come with the Vue.js sample (located inside of your package.json) via:
npm run build
In either case the files ARE written to the directory as you would have thought they should be since Building with webpack CAN write the file system, where as no files were written when using Webpack-Development-Server (again this does not work because WDS writes to memory and not the local file system).
I stumbled onto this answer when working on the same problem thanks to your comment:
"If I run webpack or npm run build, the js file is injected successfully. Can html-webpack-plugin also inject js file into index.html when I'm in localhost?"
To sum up:
Html-Webpack-Plugin WILL NOT write files to the local file system when it is used as a part of the Webpack-Development-Server (WDS) even though Html-Webpack-Plugin WILL write files (with an identical webpack configuration) when using the normal webpack build process (no WDS).
I have same problem and below config work for me.
I used to have absolute path and when change it to relative, it work.
new HtmlWebpackPlugin({
inject: true,
template:'public/index.html', // relative to project root
filename:'index.html' // relative to build folder
})
You can try config like this..
new HtmlWebpackPlugin({
filename: 'index.html',
template: 'index.html',
inject: true
})
and Index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>My App</title>
</head>
<body>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>
You will need install npm i -D html-webpack-plugin vue-loader vue-html-loader
but I recommend you create a project from template, vue init webpack
This template use the html-webpack-plugin

Categories

Resources