How to bundle "express" node.js application? (with Webpack) - node.js

I'm trying to bundle a node.js express application into single file for distribution to remove servers. I am trying to use webpack for such purposes. The bundling process works fine, but when I try to run the app from the bundle I am getting the error:
Error: secure random number generation not supported by this browser
use chrome, FireFox or Internet Explorer 11"
Below is my webpack config. I am running the code with node bundle.js
var webpack = require('webpack');
module.exports = {
context: __dirname,
devtool: 'eval',
entry: [
'./index.js'
],
output: {
filename: 'bundle.js',
publicPath: '/public'
},
resolve: {
extensions: ['', '.js', '.json'],
},
module: {
loaders: [
{
test: /\.css$/,
loader: 'null-loader'
},
{
test: /\.json$/,
loader: 'null-loader'
}
]
},
plugins: [
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify('production')
}),
new webpack.NoErrorsPlugin(),
new webpack.optimize.DedupePlugin(),
new webpack.optimize.UglifyJsPlugin({
compress: { warnings: false }
}),
new webpack.optimize.AggressiveMergingPlugin(),
new webpack.optimize.OccurenceOrderPlugin(true)
]
}
Here is a full error stack:
Error: secure random number generation not supported by this browser
use chrome, FireFox or Internet Explorer 11
at module.exports (webpack:///./~/crypto-browserify/rng.js?:21:13)
at exports.randomBytes (webpack:///./~/crypto-browserify/index.js?:22:23)
at _rng (webpack:///./~/node-uuid/uuid.js?:60:53)
at eval (webpack:///./~/node-uuid/uuid.js?:121:20)
at Object.eval (webpack:///./~/node-uuid/uuid.js?:272:3)
at eval (webpack:///./~/node-uuid/uuid.js?:274:30)
at Object.<anonymous> (/Users/tomi/Github/apps/react-app-test/server/bundle.js:545:2)
at __webpack_require__ (/Users/tomi/Github/apps/react-app-test/server/bundle.js:20:30)
at eval (webpack:///./~/graphql-tools/dist/mock.js?:18:17)
at Object.<anonymous> (/Users/tomi/Github/apps/react-app-test/server/bundle.js:341:2)

I'm sure #tomitrescak has already found the answer to this question from 4 years ago... but, for anybody else stumbling here - there is one option missing in the webpack config:
...
target: 'node',
...
Hope, this will help somebody...

Late in the game but the fact that crypto is replaced by the browser substitute is the reason why the build is failing. I solved this issue with browserify by simply excluding that module from the build since the intent is to run it with NodeJS, the crypto module will just be required as expected:
browserify index.js --exclude crypto -o build.js
For Webpack, excluding crypto should also work then.

Related

How to include migration schema files in webpack build

Issue
When building the application with Webpack into a single bundle, the library migrate-mongo is unable to find the migration schema module eg migrations/[some-number]-[some-name]-migration.js. This is probably due to the dynamic import of the migration module by the library itself and because webpack replaces how require loads modules.
How can I configure Webpack that it registers the migration js files in it's build, so when the library calls the require it will be able to retrieve the modules.
Library tries requires the module in the following absolute path strategy:
// Roughly
path.join(pwd(), migrationsDir, fileName)
Error received
Could not migrate up [file-name]-migration.js. Cannot find module [absolute-path]/migrations/[file-name]-migration.js
Even though the file exists, tried multiple relative locations just to be sure.
State
I'm using Webpack for building Nest.js application and migrate-mongo for migrations in an NX project. Migration files are located in migrations/ directory and migration up is triggered by application on startup. Migration works normally when app is started in development mode.
migrate.ts
// This function is executed on app bootstrap
export const migrate = async (): Promise<void> => {
Logger.log('[Migration]: Started');
config.set(createConfig());
const {db, client} = await database.connect();
const migrated = await up(db, client);
migrated.forEach((fileName) => {
Logger.log(`[Migration]: migrated ${fileName}`);
});
Logger.log('[Migration]: End');
};
webpack.config.js
module.exports = {
mode: 'development',
target: 'node',
entry: './src/main.ts',
module: {
rules: [
{
test: /\.ts?$/,
use: 'ts-loader',
exclude: [/node_modules/, /other-app/],
}
],
},
resolve: {
extensions: ['.ts', '.js'],
plugins: [
new TsconfigPathsPlugin({
configFile: path.resolve(__dirname, '../../tsconfig.json'),
}),
],
},
output: {
path: path.resolve(__dirname, '../../dist/apps/my-backend'),
filename: 'src/main.js',
},
externals: [
nodeExternals(),
],
plugins: [/* ... */],
}
Tried solutions
Tried using CopyPlugin to copy the migrations, but as found out all modules are placed within the built bundle.
Tried updating externals to include the directory with migrations
Tried requiring or importing the file and directory in app to register the modules

How do I integrate NewRelic into a Node Typescript Express server bundled with Webpack?

Frankly, I've tried it all. I'm not a total whiz with Webpack, however I seem to be getting along pretty well over the years with configuring new projects.
What I cannot seem to do now is set up the NewRelic service into an existing Node/Typescript/Express/Webpack application.
As it stands, my app gets nicely bundled to a single file in my /dist folder and runs quick and nimble. Seems like this 'node agent' put out by New Relic doesn't play well with Typescript imports.
Webpack Config
const path = require('path');
const webpack = require('webpack');
const nodeExternals = require('webpack-node-externals');
const NodemonPlugin = require ('nodemon-webpack-plugin');
module.exports = (env = {}) => {
const config = {
entry: ['./src/app.ts'],
mode: env.development ? 'development' : 'production',
target: 'node',
devtool: env.development ? 'inline-source-map' : false,
resolve: {
extensions: ['.ts', '.js'],
modules: ['node_modules', 'src', 'package.json'],
},
module: {
rules: [
{
test: /\.ts$/,
use: ['ts-loader', 'eslint-loader'],
// exclude: /node_modules/,
},
],
},
plugins: [],
externals: [ 'newrelic', nodeExternals() ]
};
if (env.nodemon) {
config.watch = true;
config.plugins.push(new NodemonPlugin())
}
return config;
};
there exists a standard /project_root/.newrelic file
CircleCi picks up this project up and runs "build:ci" script from package.json ==> "webpack"
output is /dist/main.js
references
https://docs.newrelic.com/docs/agents/nodejs-agent/installation-configuration/install-nodejs-agent
https://docs.newrelic.com/docs/agents/nodejs-agent/installation-configuration/nodejs-agent-configuration
https://discuss.newrelic.com/t/node-agent-fails-with-webpack/24874
Your first line of the starting point of the app should be
import newrelic from 'newrelic';
Of course, run npm install newrelic --save first
Then, create a newrelic.js file on the root of the repo (outside of src).
Then you put in the details like:
'use strict'
exports.config = {
app_name: ['appName'],
license_key: '1234567890',
allow_all_headers: true,
attributes: {
exclude: [
'request.headers.cookie',
'request.headers.authorization',
'request.headers.proxyAuthorization',
'request.headers.setCookie*',
'request.headers.x*',
'response.headers.cookie',
'response.headers.authorization',
'response.headers.proxyAuthorization',
'response.headers.setCookie*',
'response.headers.x*'
]
}
}

Webpack - How do you require an optional dependency in bundle (saslprep)

I am using webpack to bundle a number of back-end scripts into a single file during my deployment process.
When connecting to the MongoDB database there is an optional dependency, which throws a warning if it is not included.
Warning: no saslprep library specified. Passwords will not be sanitized
In my development environment this error is easily resolved by installing the optional dependency.
npm install saslprep --save
However when bundling with webpack the optional dependency is not included and the warning persists in the production deployment. I can trace the cause of this easily enough, the mongodb library is requiring this as an optional dependency:
let saslprep;
try {
saslprep = require('saslprep');
} catch (e) {
// don't do anything;
}
I have tried following the webpack documentation using shimming, externals, plugins and frankly am quite lost as to the correct approach to resolve this issue. Here is my current webpack.config file (attempting to require this as a plugin).
const path = require('path');
const webpack = require('webpack');
module.exports = {
entry: './src/api/index.ts',
target: 'node',
mode: 'production',
module: {
rules: [
{
test: /\.tsx?$/,
loader: 'ts-loader',
exclude: /node_modules/
}
]
},
resolve: {
extensions: ['.js', '.tsx', '.ts', '.json']
},
output: {
filename: 'api.js',
path: path.resolve(__dirname, 'dist'),
},
plugins: [
new webpack.IgnorePlugin(/fsevents/),
new webpack.IgnorePlugin(/blessed/),
new webpack.ProvidePlugin({
saslprep: path.resolve(__dirname, "node_modules/saslprep/index.js")
})
],
};
Thanks in advance.
Thanks to Brendan for steering me in the right direction. Ultimately the answer was found here: http://www.matthiassommer.it/software-architecture/webpack-node-modules/
The key piece of information being:
Webpack’s compiler converts the require() call to its own webpack_require(). At runtime it looks into its internal module registry.
Following the steps outlined therein the resolution becomes:
const path = require('path');
const webpack = require('webpack');
module.exports = {
entry: './src/api/index.ts',
target: 'node',
mode: 'production',
module: {
rules: [
{
test: /\.tsx?$/,
loader: 'ts-loader',
exclude: /node_modules/
}
]
},
resolve: {
extensions: ['.js', '.tsx', '.ts', '.json'],
},
output: {
filename: 'api.js',
path: path.resolve(__dirname, 'dist'),
},
plugins: [
new webpack.IgnorePlugin(/fsevents/),
new webpack.IgnorePlugin(/blessed/),
],
externals: {
"saslprep": "require('saslprep')"
}
};
Please note that in my testing the quotes around "saslprep" do appear to berequired when importing externals.
The module saslprep is included into the bundle but it will fail to load because it's trying to read the file code-points.mem that doesn't exist.
You have to copy this file from the saslprep package into the root of your project (the relative path must be ../code-points.mem).
It doesn't matter if you copy it by hand or with the copy-webpack-plugin plugin.
module.exports = {
plugins: [
new CopyPlugin({
patterns: [
{
from: require.resolve("saslprep/code-points.mem"),
to: "../code-points.mem"
}
]
})
],
}
webpack should be 'seeing' that require('saslprep') code and bundling saslprep. You can confirm this by looking at the file(s) created by webpack and searching for something distinctive from the library's source code. I would look at the MongoDB code that is checking for that dependency -- it may be that the way they're checking that it exists doesn't work with the way that webpack bundles and defines the dependency. (Most authors of node libraries are probably not thinking about how the code will operate when bundled -- webpack can be used for Node, but it rarely is.) If that's the case, you might be able to reverse-engineer what the code is checking for and put it in place via an alias, etc.
I you use the webpack-node-externals package, you can also do this:
const nodeExternals = require("webpack-node-externals")
...
const webpackConfig = {
...
externals: [nodeExternals({
allowlist: ['saslprep']
})],
...
}

Webpack not creating the HTML file it used to create

I was building a nodejs app (typescript, react, webpack) and the build was working fine. I.e. I got in the output what I expected. A HTML file, with the bundle file I expected.
Suddenly, without any change in my code, webpack is only generating a the bundle javascript file (as usual) but the HTML file is gone, i.e it does not generate any more.
Here my webpack config file:
const path = require('path');
const config = {
mode: "production",
entry: path.resolve(__dirname, "../src/index.tsx"),
resolve: {
extensions: ['.tsx', '.js']
},
output: {
// options related to how webpack emits results
path: path.resolve(__dirname, "../dist"),
filename: "bundle.js"
},
module: {
rules: [
{
test: /.tsx$|.js$/,
loader: "ts-loader" ,
include: path.resolve(__dirname, "../src")
},
]
}
}
module.exports = config;
Any clue why my HTML file is not generated anymore and how I can recover it? Thank you!
As I know Webpack by default outputs main.js if you do not specify output filename configuration like this code. Try this code:
const path = require('path');
const config = {
entry: path.resolve(__dirname, "../src/index.tsx"),
resolve: {
extensions: ['.tsx', '.js']
},
output: {
// options related to how webpack emits results
path: path.resolve(__dirname, "../dist"),
filename: 'my-first-webpack.bundle.js'
},
module: {
rules: [
{
test: /\.tsx$/,
loader: "ts-loader" ,
include: path.resolve(__dirname, "../src")
},
]
}
}
module.exports = config;
So the HTML was actually never created. So the question is obsolete.
I was confused because before there used to be an HTML in the output. But it was not generated by webpack (it came from Typescript script)

Module not found: Error: Can't resolve 'fs' Using Webpack

Hi im using socket io in my application. Which is requiring fs. when i try to bundle my javascript using below webpack config. im getting error can not resolve 'fs'.
Module not found: Error: Can't resolve 'fs' in 'my application path/node_modules/socket.io/lib'
i found by adding target:'node' and node:{fs:'empty'}. this issue got resolved.
But there is an issue in sass-loader. Getting below error.
ERROR in javascript/bundle.js from UglifyJs
Unexpected token: name (zlibLimiter) [javascript/bundle.js:60019,4]
Child extract-text-webpack-plugin ../../../node_modules/extract-text-webpack-plugin/dist ../../../node_modules/css-loader/index.js??ref--2-2!../../../node_modules/sass-loader/lib/loader.js!s
running the application ignoring above error. getting below error.
external "crypto":1 Uncaught ReferenceError: require is not defined
at Object.__decorate (external "crypto":1)
at __webpack_require__ (bootstrap 93620a17882f7a2aa1d3:19)
at Object.byteToHex (rng.js:4)
at __webpack_require__ (bootstrap 93620a17882f7a2aa1d3:19)
Below is my webpack config and versions. Can some one please help me to resolve this issue.
"webpack": "~3.6.0",
npm -v 5.8.0
node -v v8.4.0
const webpack = require('webpack');
const env = process.env.NODE_ENV;
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const path = require('path');
const extractSass = new ExtractTextPlugin({
filename: 'css/[name].css',
allChunks: false
});
let output = {
path: __dirname + '/src/main/resources/static/',
filename: 'javascript/[name].js'
};
if (env === 'debug' || env === 'nondev') {
output = {
path: __dirname + '/target/classes/static/',
filename: 'javascript/[name].js'
};
}
let config = {
context: __dirname + '/app/js/src',
entry: {
bundle: './index.jsx',
application: './static/scss/application.scss',
'application-pdap': './static/scss/application-pdap.scss'
},
output: output,
devtool: 'cheap-module-source-map',
module: {
rules: [
{
test: /.jsx?$/,
loader: 'babel-loader',
exclude: /node_modules/,
query: {presets: ['es2015', 'react']}
},
{
test: /\.(woff|woff2|eot|ttf|svg|png|jpg|gif)$/,
loader: 'file-loader?limit=1024&name=images/[name].[ext]'
},
{
test: /\.(scss|css)$/,
include: [path.resolve(__dirname, 'app/js/src/static/scss')],
use: ExtractTextPlugin.extract({
publicPath: '../',
use: [
{
loader: 'css-loader',
options: {
minimize: true,
sourceMap: false
}
},
{loader: 'sass-loader'}
],
fallback: 'style-loader'
})
}
]
},
plugins: [extractSass],
};
if (env === 'production' || env === 'nondev') {
config.devtool = 'nosources-source-map';
config.plugins.push(
new webpack.DefinePlugin({
'process.env': {NODE_ENV: '"production"'}
})
);
config.plugins.push(new webpack.optimize.UglifyJsPlugin({
compress: {warnings: false},
comments: false,
sourceMap: false,
minimize: false
}));
}
module.exports = config;
The solution depends on the type of application you want to build. Usually front-end and back-end JavaScript code are bundled separately, effectively creating two output bundles.
Front-end
For a frontend/web project, add the socket.io client libary to your app bundle. There is no need to include any node dependencies (fs) or mock entries like node: { fs:'empty' }. You can choose target:'web' or leave it out, as it is the default.
Back-end
Pick target:'node' and install socket.io server library. You do not need to specify externals: ["fs"] as shown in the other answer, because target: 'node' will take care of not bundling path, fs and other built-in modules.
Better avoid npm i fs - this is a needless escape hatch and a security risk. There have already happened cases of malicious npm packages with common package names.
You could even deliberate about, wether a node backend bundle is needed at all. An alternative is to install webpack-node-externals, which treats either all or specific npm packages as "externals" and excludes them from the bundle:
var nodeExternals = require('webpack-node-externals');
module.exports = {
target: 'node', // ignore built-in modules like path, fs, etc.
externals: [nodeExternals()], // ignore all modules in node_modules folder
// ...
};
This makes sense for the back-end, as all dependencies are installed in node_modules at server start time and don't need to be included in the bundle.
Exclude files from loaders
To exclude files from certain loaders and their transformations, you can use exclude module rule. An example is to omit node_modules from babel-loader transformations:
{ test: /\.(jsx|js)$/, exclude: /node_modules/, loader: "babel-loader" }
Further reading
Just install fs (npm i fs), and add at your webpack config
externals: ["fs"],
If someone is still facing this issue, you can try the following workaround. Update your webpack file to including the following configuration:
node: {
fs: 'empty',
}
We had a similar issue within our project and adding this specific configuration resolved the 'fs' related error in webpack.
It's also worth checking the version of Webpack version. we had to revert back the webpack and webpack cli versions to 4.0.0 and 4.2.0 respectively.

Resources