webpack doesn't bundle node modules - node.js

I want to require a module from node_modules and I want to bundle it (for test purposes), but Webpack behaves as if it is added to externals.
// no externals or any plugin used
let config = {
mode: 'none',
target: 'node',
entry: {
output: `/example.js`,
},
resolve: {
extensions: ['.js'],
},
output: {
path: './dist',
},
};
// exampl.js
require('path')
// dist/output.js
require('path');
Expected behavior
the node module path to be bundled
actual behavior
Webpack keep require('path');

This is by design. When you set target: 'node' in webpack config, webpack will not bundle the built-ins module of Node.js. path is a built-in module of Node.js, it doesn't come from the node_modules directory.
using node webpack will compile for usage in a Node.js-like environment (uses Node.js require to load chunks and not touch any built in modules like fs or path).
See targets

Related

How to combine multiple node js file into a single bundle using webpack

I am trying to create a single bundle from multiple javascript file in a nodejs application.
The configuration I am using looks somewhat like this:
const path = require('path')
const nodeExternals = require('webpack-node-externals')
'use strict';
module.exports = {
externals: [nodeExternals({})],
entry: './lib/index.js',
output: {
iife: false,
path: path.resolve(__dirname, 'lib'),
filename: 'bundle.js', // <-- Important
},
target: 'node', // <-- Important
};
The problem is when I run bundle.js command instead for it to do what the command says, i get the full source of the file streamed into the terminal.
It seems the file contains some sort of IIFE that gets executed immediately. I set iife: false to false in the webpack configuration but that also did not make any difference.
Any ideas what could be wrong?
Edit:
I am calling webpack by adding:
bundle: webpack --config webpack.config.js to script section in package.json and then I run npm run bundle
As an alternative, you could use https://www.npmjs.com/package/#vercel/ncc package to bundle all your Node.js code into one file.

Webpack bundle imports with an #

I am trying to bundle a Node Express server built with TypeScript using Webpack.
Compiling/Transpiling into one JavaScript file server.js works well, but the file does not seem to have all necessary imports included. If the file is in dist/server.js and there are still Node modules in node_modules/..., then starting the server using node dist/server.js works well. But if I copy the server.js to any other location without also copying the Node modules, and then start it, it does not find classes imported with an #.
Possible error:
mysystem:Desktop myuser$ node server.js
internal/modules/cjs/loader.js:582
throw err;
^
Error: Cannot find module '#overnightjs/core'
at Function.Module._resolveFilename (internal/modules/cjs/loader.js:580:15)
at Function.Module._load (internal/modules/cjs/loader.js:506:25)
at Module.require (internal/modules/cjs/loader.js:636:17)
at require (internal/modules/cjs/helpers.js:20:18)
at Object.<anonymous> (/Users/myuser/Desktop/server.js:1:1422)
at a (/Users/myuser/Desktop/server.js:1:172)
at Object.<anonymous> (/Users/myuser/Desktop/server.js:1:6473)
at a (/Users/myuser/Desktop/server.js:1:172)
at Object.<anonymous> (/Users/myuser/Desktop/server.js:1:6233)
at a (/Users/myuser/Desktop/server.js:1:172)
I think that I might have something missing in my Webpack configuration file, which is:
const path = require('path');
const nodeExternals = require('webpack-node-externals');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = {
entry: "./src/start.ts",
output: {
filename: "server.js",
path: path.resolve(__dirname, "dist")
},
node: {
// Need this when working with express, otherwise the build fails
__dirname: false, // if you don't put this is, __dirname
__filename: false, // and __filename return blank or /
fs: 'empty',
},
resolve: {
alias: {},
// Add '.ts', and '.tsx' as resolvable exteensions.
extensions: [".ts", ".tsx", ".js", ".json"]
},
externals: [nodeExternals()], // Need this to avoid error when working with Express
module: {
rules: [
// All files with a '.ts' or '.tsx' extension will be handled by 'awesome-typescript-loader'.
{ test: /\.tsx?$/, loader: "awesome-typescript-loader" },
// All output '.js' files will have any sourcemaps re-processed by 'source-map-loader'.
{ enforce: "pre", test: /\.js$/, loader: "source-map-loader" }
]
},
plugins: [
new CleanWebpackPlugin({
cleanAfterEveryBuildPatterns: ['dist']
})
]
};
The versions I use:
Node (v10.13.0)
yarn (1.7.0)
Express (4.17.1)
TypeScript (3.7.4)
Webpack (4.41.4)
Webpack-CLI (3.3.10)
How can I also add imports starting with an # into the bundle in Webpack?
It is make sense, Your externals config tells webpack not to bundle node_modules into the bundle but require them at run time.
This means that if you are not copying node_modules to the new location, it will try to require it, and will fail.
Usually it is best practice not to bundle node_modules whenever you are creating a node bundle.
You have 2 choices:
Remove externals config - will increase bundle size cause it will contain all the node_modules that your app is using.
Installing a fresh node_modules on you new location, if this location is a production env, you can use npm install --production which will install only the dependencies without devDependencies.
Don't bundle modules when transpiling TypeScript. Standard practice is two download modules wherever you've deployed you server. Make sure to use the production flag if you're all done developing. npm i --production

How to point webpack to a specific node_modules folder

I am trying to build a grpc web client and I need to pack the code to resolve the require statements.
I have compiled the protos to js and it works if I have them in the current folder where I have installed the node modules.
The problem is if I have the compiled proto in some other place and I require them from there, webpack looks for the node modules in that path.
From my client.js
working version:
const {StopRequest, StopReply} = require('./work_pb.js');
Problematic version:
const {StopRequest, StopReply} = require('../../../messages/proto/output/work_pb.js');
In this last case it looks for the node modules in ../../../messages/proto/output/.
The node modules are installed in the current path where my client.js is and from where I ran npx webpack client.js.
ERROR in /home/xxx/yyy/zzz/messages/proto/output/work_pb.js
Module not found: Error: Can't resolve 'google-protobuf' in '/home/xxx/yyy/zzz/messages/proto/output'
# /home/xxx/yyy/zzz/messages/proto/output/work_pb.js 11:11-37
# ./client.js
How do I tell webpack to look in the current path for the node modules and not in the path of the compiled proto?
You can specify resolve.modules to customize the directory, where Webpack searches for modules with absolute import paths:
// inside webpack.config.js
const path = require('path');
module.exports = {
//...
resolve: {
modules: [path.resolve(__dirname, 'node_modules'), 'node_modules']
}
};
This will let node_modules inside your project root (where webpack config resides) take precedence over the default setting "node_modules", which resolves paths via node module resolution algorithm.
More infos: Webpack docs: Module Resolution

Express, Pug and Webpack

I have a Node js server app which uses Express and Pug. I would like to bundle it to single script which can be deployed by pm2. There seem to be several problems with this.
In runtime I get Cannot find module "." and during compilation few messages like
WARNING in ./node_modules/express/lib/view.js 80:29-41 Critical
dependency: the request of a dependency is an expression
appear which come from dynamic imports like require(mod).__express. I assume Webpack can't statically resolve those and does not know which dependency to include.
How can this be solved ?
How do I make Pug compile and be part of the output js ?
It is because webpack rebundle node_modules (already bundled) dependencies and in the case of pug, it doesn't work.
You need to use webpack-node-externals within the webpack externals option in order to specifically ask not to re-bundle depedencies.
Install webpack-node-externals: npm i -D webpack-node-externals
Integrate it your webpack config file:
Example
// ...
const nodeExternals = require('webpack-node-externals')
module.exports = {
target: 'node',
entry: {
// ...
},
module: {
// ...
},
externals: [nodeExternals()],
output: {
// ...
},
}

Resolve the module loaded by plugin in Webpack config

The following example will work only if some-module module is Node module, and won't work for modules loaded by Webpack plugin.
How can Webpack's own logic (enhanced-resolve) be used to resolve module paths in config?
In my case it was bower-webpack-plugin, but I guess this should work in the same way with any ResolverPlugin
var BowerWebpackPlugin = require("bower-webpack-plugin");
module.exports = {
...
module: {
plugins: [new BowerWebpackPlugin()],
loaders: [
{
// this won't work
test: require.resolve("some-bower-module")
loader: "imports?this=>window"
}
]
};
require.resolve inside webpack.config.js is resolved by Node and not Webpack's resolver. You can use require("path").resolve("path/to/bower/module") to get the full path to your Bower module.

Resources