How to include Node modules with Webpack

Webpack is a really convenient tool to bundle your application. However, its configuration can be problematic. Here is a solution to a common problem on how to correctly include Node.js modules, such as fs and path in your application.

The problem

Maybe you came here because you encountered one of the following or similar error messages:

Uncaught TypeError: __webpack_require__(...) is not a function
Uncaught ReferenceError: require is not defined
at Object.128 (external "require('fs')":1)
Module not found: Error: Cannot resolve module 'fs'
TypeError: fs.readFileSync is not a function

I had a similar problem in a project using Webpack, Electron, and Angular. However, thes exceptions are not limited to the fs module of Node.js, but will occur with any Node module (such as path, child_process, etc.) if your project is not properly configured.

During my research, I came across several threads on Stackoverflow. As it seems, I am not the only one with this problem.

The tricky thing is that this error does not show up during compile time, but only when you run your application in the browser. For example, it might show up in the Chrome developer console. That is because Webpack’s compiler converts the require() call to its own __webpack_require__(). At runtime it looks into its internal module registry.

In conclusion, the above error can show up due to two different issues:

  • Node.js modules are not globally exposed by Webpack.
  • You tried to require a Node module instead of importing it. Maybe like so:
import { existsSync } from 'fs';
let fs = require("fs");

The solution

How can we expose the Node modules globally in our project? We have to define them as externals in our webpack.config.js:

module.exports = {
   "externals": {
      "electron": "require('electron')",
      "child_process": "require('child_process')",
      "fs": "require('fs')",
      "path": "require('path')"
      [remainder omitted]

If you have no webpack.config.js you probably haven’t ecjected your projected yet.

Run ng eject  in a console to eject webpack.config.js from angular-cli. This will generate webpack.config.js in the root folder of your project. You’re free to configure it the way you want. However, once created, there is not yet an externals option in module.exports. Therefore, you have to add it manually.

Finally, just add the modules you need following the template above.

The official Webpack documentation for the externals option states:

The externals configuration option provides a way of excluding dependencies from the output bundles. Instead, the created bundle relies on that dependency to be present in the consumer’s environment. This feature is typically most useful to library developers, however there are a variety of applications for it.

Since all Node functions are already provided through the Webpack externals, one does not have to require them.

Finally, you import any module you need at the top of the respective class like so:

import * as fs from 'fs';

Solution for Electron users

If you are using Electron and it still does not work, try the following things in addition.

Add target:’electron-renderer’ to module.exports in the Webpack configuration. Others also recommend to use this NPM package: webpack-target-electron-renderer

Find more information on Webpack and Node.js in the Webpack documentation.

Webpack has a difficult learning curve. But once you have set up your projects, its a really fast and convenient way to build your application. I hope that this article has solved one of the problems you might face on your way.