When upgrading to Node 14, I ran into this issue where running the test suite would throw a set of warnings like so:

(node:79706) Warning: Accessing non-existent property 'cat' of module exports inside circular dependency
(Use `node --trace-warnings ...` to show where the warning was created)
(node:79706) Warning: Accessing non-existent property 'cd' of module exports inside circular dependency
(node:79706) Warning: Accessing non-existent property 'chmod' of module exports inside circular dependency
(node:79706) Warning: Accessing non-existent property 'cp' of module exports inside circular dependency

One way of finding out what packages were throwing these warnings was by running the same command but with the --trace-warnings option:

npx cross-env NODE_OPTIONS="--trace-warnings" yarn test

This helped in pinpointing which packages needed to be updated!

Bonus

Here is a contrived example of two classes depending on each other:

// a.js
const B = require('./b');

class A {
    constructor(name) {
        this.name = name;
        this.b = new B();
        this.b.log(name);
    }
}

const a = new A('cat')

module.exports = A;

// b.js
const A = require('./a');

class B {
    constructor() {
        this.a = A;
        console.log(this.a)
    }

    log(stuff) {
        console.log(stuff)
    }
}

module.exports = B;

// package.json
  "scripts": {
    "build": "node a.js"
  },

Now building our files gives us this warning:

$ yarn build

(node:18952) Warning: Accessing non-existent property 'Symbol(nodejs.util.inspect.custom)' of module exports inside circular dependency
(Use `node --trace-warnings ...` to show where the warning was created)

Now using --trace-warnings we can see where this issue is happening:

$ npx cross-env NODE_OPTIONS="--trace-warnings" yarn build

(node:18978) Warning: Accessing non-existent property 'Symbol(nodejs.util.inspect.custom)' of module exports inside circular dependency
    at emitCircularRequireWarning (node:internal/modules/cjs/loader:698:11)
    at Object.get (node:internal/modules/cjs/loader:712:5)
    at formatValue (node:internal/util/inspect:746:30)
    at inspect (node:internal/util/inspect:330:10)
    at formatWithOptionsInternal (node:internal/util/inspect:1998:40)
    at formatWithOptions (node:internal/util/inspect:1880:10)
    at console.value (node:internal/console/constructor:327:14)
    at console.log (node:internal/console/constructor:363:61)
    at new B (/Users/ygaberman/programs/test/b.js:6:17)
    at new A (/Users/ygaberman/programs/test/a.js:7:18)

The last two lines show us where the dependency is. This is quite useful for chasing down any circular dependencies!


Can we fix it? Yes.