esbuild: module2.require is not a function when bundling for node

Given:

test.js:

require('./test2')

test2.js:

console.log(typeof module.require('crypto'))

And running:

$ node test.js 
object
$ esbuild --version
0.7.15

$ node <(npx esbuild --bundle --platform=node test.js)
/dev/fd/11:11
  console.log(typeof module2.require("crypto"));
                             ^

TypeError: module2.require is not a function

When I look at the bundled output, it seems that no arguments are passed in to require_test2:

var __commonJS = (callback, module2) => () => {
  if (!module2) {
    module2 = {exports: {}};
    callback(module2.exports, module2);
  }
  return module2.exports;
};

// test2.js
var require_test2 = __commonJS((exports, module2) => {
  console.log(typeof module2.require("crypto"));
});

// test.js
require_test2();

This pattern is used in the real world, for example here:

https://github.com/apollographql/apollo-server/blob/570f548b88750a06fbf5f67a4abe78fb0f870ccd/packages/apollo-server-core/src/utils/createSHA.ts#L5-L7

I’ve also tried passing --define:module.require=require and --define:module2.require=require but neither of them seem to have any effect (should I open a separate bug for that?)

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Reactions: 1
  • Comments: 26 (16 by maintainers)

Most upvoted comments

There was an earlier version of moment that used this pattern that I’ve had to fix it for in the past. This was used to dynamically require locales, so it was doing:

module.require('./locale/' + name);

and the tool I was working on at the time was designed to handle these cases via globbing analysis.

So I guess in theory it was an attempt to use it as the opt-out actually. But it’s not really a reliable opt-out unless we want to explictly decide to make it one and actively drive that.

If it’s only explicit CallExpression cases for the unbound scope I think it’s fine - it would still be undefined in other cases.

In short, if esbuild aims to build for Node.js equally then it probably should detect it. Otherwise it could be ok to leave out.