How to make Webpack build on Express server started - javascript

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.

Related

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

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!

Vue.js / Webpack build "index.html" using a "static" subdomain for CSS and JS

I have a Vue.js project that is working fine in localhost.
But when I build and deploy to production, I need the static files (.css and .js) to be served on a "static" subdomain.
For example, my main URL:
https://www.example.com/index.html
The static assets will be:
https://static.example.com/css/app.50f83e17.css
https://static.example.com/js/chunk-vendors.6f495bf3.js
When I run "npm run build" Webpack build the "index.html" file loading like this:
<link href=/css/app.50f83e17.css rel=stylesheet>
But I need the href to be like this:
<link href=https://static.example.com/css/app.50f83e17.css rel=stylesheet>
How do I configure Vue.js or Webpack to build the "index.html" using a different subdomain for the CSS and JS?
To achieve this you would need to use webpack publicPath
You can configure webpack to build index.html using a different subdomain,
webpack.config.js
import webpack from 'webpack';
export default {
output: {
..
publicPath: 'https://static.example.com/js/',
},
};
npm run build Webpack build the "index.html" would have
..
..
<script type="text/javascript" src="https://static.example.com/js/chunk-vendors.6f495bf3.js"></script>
For css,
webpack.config.js
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
plugins: [
new MiniCssExtractPlugin({
filename: '[name].css',
chunkFilename: '[id].css',
}),
],
module: {
rules: [
{
test: /\.css$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
options: {
publicPath: 'https://static.example.com/css/',
},
},
'css-loader',
],
},
],
},
};
You can also specify publicPath at runtime,
entry.js
__webpack_public_path__ = myRuntimePublicPath;
// rest of your application entry
Note: please consider adding a environment variable instead of hardcoding the asset CDN path, and pass the environment variable via npm scripts, alternately you can also define a global publicPath
var myRuntimePublicPath = 'https://static.example.com/js/'
and use it in the entry file (say entry.js) as displayed above.
refer: webpack publicPath and mini-css-extract-plugin publicPath
In case of using Vue Router
The base URL your application bundle will be deployed at publicPath (known as baseUrl before Vue CLI 3.3).
vue.config.js
module.exports = {
publicPath: process.env.NODE_ENV === 'production'
? 'https://static.example.com/js/'
: '/'
}
Vue Router
// override the base to hardcoded default value as it gets the value from publicPath
base: '/'
This will allow subdomain for javaScript bundle of vuejs build.

Content Hashes, ExtractTextPlugin and HtmlWebpackPlugin

I guess I'll start with my webpack config.
const webpack = require('webpack');
const path = require('path');
/**
* Environment
*/
const nodeEnv = process.env.NODE_ENV || 'development';
const isProduction = nodeEnv === 'production';
const isDevelopment = !isProduction;
const HtmlWebpackPlugin = require('html-webpack-plugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const sourcePath = path.join(__dirname, 'assets');
const buildPath = path.join(__dirname, 'dist');
const extractSass = new ExtractTextPlugin({
filename: '[name].[contenthash].css',
disable: isDevelopment
});
/**
* Plugins
*/
const plugins = [
new HtmlWebpackPlugin({
template: path.join(sourcePath, 'index.html'),
}),
extractSass
];
if (isProduction) {
} else {
plugins.concat([
new webpack.HotModuleReplacementPlugin(),
]);
}
module.exports = {
entry: ['./assets/app.js', './assets/app.scss'],
devtool: isProduction ? 'eval' : 'source-map',
plugins: plugins,
module: {
rules: [{
test: /\.scss$/,
use: extractSass.extract({
use: [{
loader: "css-loader"
}, {
loader: "sass-loader"
}],
// use style-loader in development
fallback: "style-loader"
})
}]
},
output: {
filename: 'bundle.js',
path: buildPath
},
devServer: {
contentBase: buildPath,
port: 9000
}
};
This all works fine when running on the webpack dev server but I'm trying to figure out how this fits together on a production environment.
As you can see, as per the sass-loader documentation, I'm creating a file called [name].[contenthash].css if NODE_ENV is set to production. I love the idea of serving files based on the content hash because I love integrity.
The difficulty I'm having is understanding how I can pass that file name, that content hash into the index.html template I'm creating so that I can <link> the stylesheet.
Is it a server side thing?
Is there any way to pass that file name into the HTML template on
production?
Is it intentional that I do it manually or script it out?
I just don't understand how these two components come together to produce a publishable build. HtmlWebpackPlugin produced a .html in the output directory but obviously it has no innate understanding of where to find it's styles.
Your config seems correct.
Is there any way to pass that file name into the HTML template on production?
What should be happening is that the HtmlWebpackPlugin should be creating a new index.html file in your buildPath directory, which has the generated bundles automatically injected in it (for example the generated CSS bundle will be injected in the head tag and the generated script bundles at the bottom of the body tag)
Beyond that it is just a matter of serving that dist/index.html to whoever visits your site/app. So the answer to
Is it a server side thing?
is yes.
Try doing a build without the dev server, by simply running webpack, so you can see the output of your configuration (the dev server builds things in memory, so you do not actually get to see them)

webpack did not output file to correct directory

I try to implement hot reloading in a laravel app. But I have problem doing output using webpack. When I do http://localhost/bundle.js it's 404 not found. I wonder why. In my terminal I did see bundle.js is bundled by webpack, but where has it gone to?
gulpfile.js
var elixir = require('laravel-elixir');
var webpackDevMiddleware = require('webpack-dev-middleware');
var webpackHotMiddleware = require('webpack-hot-middleware');
var webpackConfig = require('./webpack.dev.config');
var bundler = webpack(webpackConfig);
gulp.task('hot', function(){
elixir(function(mix) {
browserSync({
proxy: 'somewhere.local',
middleware: [
webpackDevMiddleware(bundler, {
publicPath: '/'
}),
webpackHotMiddleware(bundler)
]
});
});
});
webpack.dev.config.js The problem is here
module.exports = {
debug: true,
context: path.join(__dirname, 'resources/assets/bundle/entries'),
entry: ['./feed.js'],
output: {
path : path.join(__dirname, 'public/bundle'),
publicPath : '/',
filename : 'bundle.js'
}
resolve: {
extensions: ['', '.js', '.jsx']
}
}
my laravel view
I also included
<script src="http://localhost:3000/bundle.js"></script>
Something is wrong in my webpack config file. Here's my site directory :
webpack dev server doesn't write the bundle to disk.
This modified bundle is served from memory at the relative path specified in publicPath (see API). It will not be written to your configured output directory. Where a bundle already exists at the same URL path, the bundle in memory takes precedence (by default).
you can find out more here: https://webpack.github.io/docs/webpack-dev-server.html
depending on your exact needs, you might consider using a different webpack configuration

Using relay in react native app

How to expose a graphql endpoint to react native app? Has anyone used relay in react native application? The examples in technical overview of relay https://facebook.github.io/relay/docs/getting-started.html use webpack to serve relay app and expose it to a graphql server:
import express from 'express';
import graphQLHTTP from 'express-graphql';
import path from 'path';
import webpack from 'webpack';
import WebpackDevServer from 'webpack-dev-server';
import {StarWarsSchema} from './data/starWarsSchema';
const APP_PORT = 3000;
const GRAPHQL_PORT = 8080;
// Expose a GraphQL endpoint
var graphQLServer = express();
graphQLServer.use('/', graphQLHTTP({schema: StarWarsSchema, pretty: true}));
graphQLServer.listen(GRAPHQL_PORT, () => console.log(
`GraphQL Server is now running on http://localhost:${GRAPHQL_PORT}`
));
// Serve the Relay app
var compiler = webpack({
entry: path.resolve(__dirname, 'js', 'app.js'),
eslint: {
configFile: '.eslintrc'
},
module: {
loaders: [
{
test: /\.js$/,
loader: 'babel',
query: {
stage: 0,
plugins: ['./build/babelRelayPlugin']
}
},
{
test: /\.js$/,
loader: 'eslint'
}
]
},
output: {filename: 'app.js', path: '/'}
});
var app = new WebpackDevServer(compiler, {
contentBase: '/public/',
proxy: {'/graphql': `http://localhost:${GRAPHQL_PORT}`},
publicPath: '/js/',
stats: {colors: true}
});
// Serve static resources
app.use('/', express.static('public'));
app.use('/node_modules', express.static('node_modules'));
app.listen(APP_PORT, () => {
console.log(`Relay Star Wars is now running on http://localhost:${APP_PORT}`);
});
but react native uses react-native packager to bundle its modules; has anyone tried using relay in the react native app?
It is now possible to use react native and relay together.
Github announcement: https://github.com/facebook/relay/issues/26
Instructions for existing RN apps: http://pulse.mixrt.com/discussion/26/technical-making-relay-work-with-existing-react-native-apps-a-step-by-step-tutorial .
Copy of the instructions:
Back up your project.
Make sure you have your GraphQL server ready and your schema.json at hand too. For more details about the latter two visit the React-Relay project page.
Ensure that you’re using `npm` version 3 or greater.
If React Native's packager (`react-native start`) is running somewhere in the background, you should stop it now.
Run: watchman watch-del-all and also: rm -rf $TMPDIR/react-* to avoid running into a known packager issue ( https://github.com/facebook/react-native/issues/4968 ).
Delete the `./node_modules` directory from your project.
Edit your `package.json` file, make sure it has the following:
"dependencies": {
"react": "^0.14.7",
"react-native": "facebook/react-native",
"react-relay": "^0.7.3"
},
"devDependencies": {
"babel-core": "^6.6.4",
"babel-preset-react-native": "^1.4.0",
"babel-relay-plugin": "^0.7.3"
}
Babel version is especially important. Make sure that your project uses babel 6.5 or later, we need it for the passPerPreset feature in .babelrc file.
Create a new file `.babelrc` and place it in your project's directory:
{
"presets": [
"./scripts/babelRelayPlugin",
"react-native"
],
"passPerPreset": true
}
Create a new file in your project's directory called `babelRelayPlugin.js` with the following content:
const getBabelRelayPlugin = require('babel-relay-plugin');
const schema = require('./schema.json');
module.exports = { plugins: [getBabelRelayPlugin(schema.data)] };
Copy your `schema.json` file to the project's directory too.
Run: npm install
You can find a working version in here, until this will be solved.
you need to install react-relay package for react-native app
Before the entry point of your react-native app, inject network layer with the url you exposed
Relay.injectNetworkLayer(
new Relay.DefaultNetworkLayer('http://localhost:8000/graphql')
);

Categories

Resources