39

Hitting this exact problem currently:

FileA:
var b = require file B
var c = require file C

FileB:
var a = require file A

FileC:
var a = require file A

When I run the code, I get an error in File C:

A.doSomething is not a function

Threw a debugger in there and saw that A is an empty object. What's really weird is that I'm only getting an error in File C, but not File B. Super confused here.

Eliran Malka
  • 15,821
  • 6
  • 77
  • 100
Salar
  • 861
  • 9
  • 13
  • 1
    I wrote a tool to check your Webpack project for circular dependencies: https://github.com/DelvarWorld/webpack-cyclic-dependency-checker – Andy Ray Jul 21 '16 at 22:12

1 Answers1

67

This is not a webpack issue but a property of CommonJS modules.

When a CommonJS module is first required, its exports property is initialized to an empty object behind the scenes.

module.exports = {};

The module can then decide to extend this exports property, or override it.

exports.namedExport = function() { /* ... */ }; // extends

module.exports = { namedExport: function() { /* ... */ } }; // overrides

So when A requires B and B requires A right after, A is not executed again (which would produce an infinite loop), but its current exports property is returned. Since A required B at the very top of the file, before exporting anything, the require('A') call in the B module will yield an empty object.

A common fix for circular dependencies is to put your imports at the end of the file, after you've exported the variables needed by other modules.

A:

module.exports = { foo: 'bar' };
require('B'); // at this point A.exports is not empty anymore

B:

var A = require('A');
A.foo === 'bar';
CodeTower
  • 6,293
  • 5
  • 30
  • 54
Alexandre Kirszenberg
  • 35,938
  • 10
  • 88
  • 72
  • hey you :) thanks it helped me understand the problem. But what if in A, you need access to some attribute exported by B? In my codebase I just replaced `module.exports` by `exports.attribute` and it now works but it feels not very natural – Sebastien Lorber Mar 23 '16 at 20:39
  • You can always do the same thing in B. `module.exports = { bar: 'foo' }; var A = require('a');`, then `module.exports = { foo: 'bar' }; var B = require('B');`. If your exports depend on each other, then you should build them progressively by extending `exports` instead of overriding it. – Alexandre Kirszenberg Mar 23 '16 at 20:53
  • 4
    This is the most important answer ever given on Stack Overflow. And you are a hero. – netpoetica Jan 31 '17 at 22:12
  • 1
    Saved my bacon. Thx! – John Hildenbiddle Feb 10 '17 at 22:57
  • We have this problem and this answer solves it but does not work when we do our mocha build. :( – Pablo Jomer Aug 08 '17 at 11:33