5
bundle.js      2.83 kB   0  [emitted]  main
bundle.js.map  3.36 kB   0  [emitted]  main

When I add the code below with the custom externals, I can remove the node_modules from being directly included in the bundle.js output.

bundle.js      743 kB       0  [emitted]  main
bundle.js.map  864 kB       0  [emitted]  main

This significantly reduces the bundle size. But I get an error in the browser saying: Uncaught ReferenceError: require is not defined in the browser. Does anyone know how to fix this issue?

var path = require("path"),
  fs = require("fs");

// return what's in the node modules folder apart from ".bin"
const nodeModules = fs
  .readdirSync("./node_modules")
  .filter(d => d != ".bin");

/**
 * Remove non-direct references to modules from bundles
 *
 * @param {any} context
 * @param {any} request
 * @param {any} callback
 * @returns
 */
function ignoreNodeModules(context, request, callback) {
  // IF someone is importing a module e.g. "import {chatModule} from
  // "./components/chat" using a relative path, then we're okay with them bringing
  // in into the bundle
  if (request[0] == ".") 
    return callback();

  // IF someone is doing "import {Subject} from "rxjs/Subject" only want to know
  // if first part is a node_module
  const module = request.split("/")[0];
  if (nodeModules.indexOf(module) !== -1) {
    // append "commonjs " - tells webpack to go and grab from node_modules instead
    // of including in bundle
    return callback(null, "commonjs " + request);
  }

  return callback();
}

module.exports = {
  entry: "./src/index.tsx",
  output: {
    filename: "bundle.js"
  },
  devtool: "source-map",
  resolve: {
    extensions: ["", ".ts", ".tsx", ".js"]
  },
  module: {
    loaders: [
      {
        test: /\.ts(x?)$/,
        loader: "ts-loader"
      }
    ]
  },
  // Runs our custom function everytime webpack sees a module
  externals: [ignoreNodeModules]
}
Clement
  • 4,491
  • 4
  • 39
  • 69

1 Answers1

10

Your bundle is smaller because you're not including your node_modules, but this leads to a fundamental problem: you no longer send your dependencies to the browser, so your code can't run at all. As you probably know, browsers don't natively support require(), so your current ignoreNodeModules function tells Webpack to skip bundling them and leave in the require(), but the browser doesn't know how to handle it.

If you want to reduce bundle size, consider using Webpack's code splitting so that you only bundle dependencies that are needed for each page/section. Alternatively, you could consider using a browser-side require() loader such as RequireJS.

Using externals is only really useful for distributing server-side Node libraries, where you would expect consumers of your library to provide your dependencies rather than bundling them with your library.

The comments on the documentation about externals are also worth reading for some further context on the issue.

Aurora0001
  • 13,139
  • 5
  • 50
  • 53
  • When it comes to best practice, would it be better to do code-splitting or using a browser-side require() loader such as RequireJS? – Clement Dec 04 '16 at 13:37
  • It depends on your server and app. If you're using HTTP2, the answer is *don't bundle at all*, because it can handle multiple concurrent requests easily. Otherwise, I would use code splitting because Webpack supports it a bit better than RequireJS (but there's nothing *technically* wrong with it). – Aurora0001 Dec 04 '16 at 13:57
  • Awesome! thanks a lot for that advice. I haven't heard a lot about HTTP2 before, but I'll look into it :) otherwise I'll go with code-splitting and see how it goes. – Clement Dec 04 '16 at 14:06
  • @ClementOh glad it helped - don't forget to accept if you consider this question solved now. If you have any further questions related to this, feel free to reply here with the link so I get a notification and I'll try to take a look! – Aurora0001 Dec 04 '16 at 15:05
  • 1
    Done. Thanks for the offer! Really appreciate your help! I actually do have a question on webpack and express in the link below. Would be great to see what you think: http://stackoverflow.com/questions/40959835/webpack-express-cannot-resolve-module-fs-request-dependency-is-expression – Clement Dec 04 '16 at 15:11