How to Bundle all the files inside “src” folder and replace the existing bundled files inside “Public” folder in any Node.js web app?
My project structure is similar to this: https://github.com/googlearchive/friendlypix-web
For javascript files, you should have at least one entry-point for your application in order to bundle it.
Example of webpack.config.json with one entrypoint src/index.js
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'public'),
},
};
More information about bundling one or many entry-points : https://webpack.js.org/concepts/entry-points/
Furthermore, if you have imported static assets (images, css, saas, fonts...) in your javascript files, you need to add some configuration and eventually install webpack loaders to bundle them.
For example, for CSS assets :
Install webpack loaders via npm install --save-dev style-loader css-loader
Update webpack configuration
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'public'),
},
module: {
rules: [
{
test: /\.css$/i,
use: ['style-loader', 'css-loader'],
},
],
},
};
More information about asset management : https://webpack.js.org/guides/asset-management/
Finally, you may need to bundle static files or directories that are not imported in your javascript files and you want to copy them as they are.
In this case, you could use the copy-webpack-plugin :
Install plugin via npm install copy-webpack-plugin --save-dev
Update webpack configuration
const CopyPlugin = require("copy-webpack-plugin");
module.exports = {
...
plugins: [
new CopyPlugin({
patterns: [
{ from: "source", to: "dest" },
{ from: "other", to: "public" },
],
}),
],
...
}
More info about copy-webpack-plugin : https://webpack.js.org/plugins/copy-webpack-plugin/
I really recommend you to take a look at the official webpack documentation which covers topics including creating production bundles, code splitting, caching and other tips and hints.
Related
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*'
]
}
}
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']
})],
...
}
We are trying to create a microfront ends app, we would like to take each micro app (created with CRA) run
npm run build
over this app, take the /build folder created, and make a npm package out of it and publish it to our npm repo.
We don't want to ejact the app projects to be able to edit the webpack.config.
We prefer to take the /build and pass it over webpack again (even in a different project) in order to get an output that can be used as npm package and can be published and imported correctly to a new project.
Im trying doing it by taking the /build folder and running it over webpack with this configuration:
const path = require("path");
const UglifyJsPlugin = require("uglifyjs-webpack-plugin");
const glob = require("glob");
module.exports = {
entry: {
"bundle.js": glob
.sync("build/static/?(js|css)/main.*.?(js|css)")
.map(f => path.resolve(__dirname, f))
},
output: {
filename: "build/static/js/bundle.min.js",
libraryTarget: "commonjs2"
},
module: {
rules: [
{
test: /\.css$/,
use: ["style-loader", "css-loader"]
}
]
},
plugins: [new UglifyJsPlugin()],
resolve: {
alias: {
src: path.join(__dirname, "./src")
}
},
externals: {
react: "commonjs react"
}
};
The result is a single js. after publishing it to npm, im trying to import it and getting many errors like:
I think there is something missing in my webpack.config or maybe there is a different way to take the all /build folder and combine it to something that can be published as npm package and imported correctly?
Any help will be very much appreciated!
Thank you!
I'm using yarn workspaces where the root directory has a package directory with all my repos. Each repo has its own node_modules directory containing its dependencies. The root node_modules directory contains all the dev dependencies for the whole project as well as all other dev related things such as webpack.config files. Webpack uses hot module reload for the express server package.
The problem I have is, how to configure webpack externals to exclude all node_modules directories through the whole project, not just in the root?
webpack-node-externals doesn't seem to work given this scenario.
Error message:
WARNING in ./packages/servers/express/node_modules/colors/lib/colors.js
127:29-43 Critical dependency: the request of a dependency is an expression
WARNING in ./packages/servers/express/node_modules/express/lib/view.js
79:29-41 Critical dependency: the request of a dependency is an expression
Webpack config:
const webpack = require('webpack');
const path = require('path');
const nodeExternals = require('webpack-node-externals');
const StartServerPlugin = require('start-server-webpack-plugin');
module.exports = {
entry: [
'babel-polyfill',
'webpack/hot/poll?1000',
path.join(__dirname, '../packages/servers/express/server/index.js')
],
watch: true,
target: 'node',
externals: [
nodeExternals({
whitelist: ['webpack/hot/poll?1000']
})
],
resolve: {
alias: {
handlebars: 'handlebars/dist/handlebars.js'
}
},
module: {
rules: [
{
test: /\.js?$/,
use: 'babel-loader',
exclude: /node_modules/
}
]
},
plugins: [
new StartServerPlugin('server.js'),
new webpack.NamedModulesPlugin(),
new webpack.HotModuleReplacementPlugin(),
new webpack.NoEmitOnErrorsPlugin(),
new webpack.DefinePlugin({
'process.env': { BUILD_TARGET: JSON.stringify('server') }
})
],
output: {
path: path.join(__dirname, '../packages/servers/express/.build'),
filename: 'server.js'
}
};
If using yarn workspaces with webpack-node-externals a better solution than setting modulesFromFile: true is to use the following externals setting in your webpack config:
externals: [
nodeExternals(),
nodeExternals({
modulesDir: path.resolve(__dirname, 'path/to/root/node_modules'),
}),
],
Essentially using two instances of nodeExternals. 1 for the package node_modules and one for the root node_modules.
Thanks to #blackxored I was able to fix it on my project.
In your webpack config file do the following:
import nodeExternals from 'webpack-node-externals'
Then add
externals: [
nodeExternals({
modulesFromFile: true,
}),
],
Yarn workspaces hoist compatible modules to the root node_modules directory leaving any incompatible (different semver, etc.) modules with the dependent workspace's node_modules directory. If a package is requested without using a relative path it is either native, from node_module's, or possibly a symlinked package from one of your workspaces. You probably want all of those packages to be external.
how to configure webpack externals to exclude all node_modules directories through the whole project, not just in the root?
I would try using a function with webpack's external option. You are passed the context of the require, the name of the module requested, and a callback to indicate whether this particular import (require) should be considered external.
externals: [
(ctx, req, cb) => {
if (!/node_modules/.test(ctx) && req[0] !== '.') {
// Assumes you have defined an "entries" variable
let notAnEntry = (path) => {
return Object.keys(entries).every((entry) => {
return entries[entry] !== path
});
};
if (notAnEntry(require.resolve(req))) {
// This module is external in a commonjs context
return cb(null, `commonjs ${req}`);
}
}
cb();
}
]
I'm using yarn workspaces where the root directory has a package directory with all my repos. Each repo has its own node_modules directory containing its dependencies. The root node_modules directory contains all the dev dependencies for the whole project as well as all other dev related things such as webpack.config files. Webpack uses hot module reload for the express server package.
The problem I have is, how to configure webpack externals to exclude all node_modules directories through the whole project, not just in the root?
webpack-node-externals doesn't seem to work given this scenario.
Error message:
WARNING in ./packages/servers/express/node_modules/colors/lib/colors.js
127:29-43 Critical dependency: the request of a dependency is an expression
WARNING in ./packages/servers/express/node_modules/express/lib/view.js
79:29-41 Critical dependency: the request of a dependency is an expression
Webpack config:
const webpack = require('webpack');
const path = require('path');
const nodeExternals = require('webpack-node-externals');
const StartServerPlugin = require('start-server-webpack-plugin');
module.exports = {
entry: [
'babel-polyfill',
'webpack/hot/poll?1000',
path.join(__dirname, '../packages/servers/express/server/index.js')
],
watch: true,
target: 'node',
externals: [
nodeExternals({
whitelist: ['webpack/hot/poll?1000']
})
],
resolve: {
alias: {
handlebars: 'handlebars/dist/handlebars.js'
}
},
module: {
rules: [
{
test: /\.js?$/,
use: 'babel-loader',
exclude: /node_modules/
}
]
},
plugins: [
new StartServerPlugin('server.js'),
new webpack.NamedModulesPlugin(),
new webpack.HotModuleReplacementPlugin(),
new webpack.NoEmitOnErrorsPlugin(),
new webpack.DefinePlugin({
'process.env': { BUILD_TARGET: JSON.stringify('server') }
})
],
output: {
path: path.join(__dirname, '../packages/servers/express/.build'),
filename: 'server.js'
}
};
If using yarn workspaces with webpack-node-externals a better solution than setting modulesFromFile: true is to use the following externals setting in your webpack config:
externals: [
nodeExternals(),
nodeExternals({
modulesDir: path.resolve(__dirname, 'path/to/root/node_modules'),
}),
],
Essentially using two instances of nodeExternals. 1 for the package node_modules and one for the root node_modules.
Thanks to #blackxored I was able to fix it on my project.
In your webpack config file do the following:
import nodeExternals from 'webpack-node-externals'
Then add
externals: [
nodeExternals({
modulesFromFile: true,
}),
],
Yarn workspaces hoist compatible modules to the root node_modules directory leaving any incompatible (different semver, etc.) modules with the dependent workspace's node_modules directory. If a package is requested without using a relative path it is either native, from node_module's, or possibly a symlinked package from one of your workspaces. You probably want all of those packages to be external.
how to configure webpack externals to exclude all node_modules directories through the whole project, not just in the root?
I would try using a function with webpack's external option. You are passed the context of the require, the name of the module requested, and a callback to indicate whether this particular import (require) should be considered external.
externals: [
(ctx, req, cb) => {
if (!/node_modules/.test(ctx) && req[0] !== '.') {
// Assumes you have defined an "entries" variable
let notAnEntry = (path) => {
return Object.keys(entries).every((entry) => {
return entries[entry] !== path
});
};
if (notAnEntry(require.resolve(req))) {
// This module is external in a commonjs context
return cb(null, `commonjs ${req}`);
}
}
cb();
}
]