Esm: module involving circular require evaluated twice with 'vars' option turned on

Created on 8 Jan 2018  路  3Comments  路  Source: standard-things/esm

Hi, @jdalton, it's me again馃槀

I just found that a module involving circular require would be evaluated twice as in this repro repo: Mensu/std-esm-repro-issue-217

File structure

index.js, top.js and sub.js in the same directory.

index.js

const topModule = require('./top');
// topModule.subModule is expected to contain methodOfSub
console.log(topModule.subModule);

top.js

console.log('evaluating top module');

// this 'exports = module.exports = ...' pattern might be confusing to your compiler
// but it does exist in our project...
exports = module.exports = {
  methodOfTop,
};

// requires sub module
exports.subModule = require('./sub');

function methodOfTop() {

}

sub.js

console.log('evaluating sub module');
// requires top module back
const topModule = require('./top');

module.exports = {
  methodOfSub,
};

function methodOfSub() {
  // using topModule.methodOfTop
}

How to Reproduce

running node -r @std/esm index.js

  • @std/esm version: 0.19.1
  • node version: 9.2 on macOS and 9.3 on Ubuntu 16.04
  • with vars option turned on: "vars": true

Expected Output

which is the same as raw node

evaluating top module
evaluating sub module
{ methodOfSub: [Function: methodOfSub] }

Actual Output

evaluating top module
evaluating sub module
evaluating top module
{}

Workarounds

  • use exports.methodOfTop = methodOfTop instead of exports = module.exports = { methodOfTop }

    • the name methodOfTop would be repeated twice, so when there are many names to export, export.name = name would be annoying

  • turn off "vars" option: removing "vars": true from package.json
  • install @std/[email protected] instead: npm i @std/[email protected]
  • use repl: node -e 'console.log(require("./index").subModule)' -r @std/esm or cat index.js | node -r @std/esm
  • just refactor to es6 module

In light of the fact that @std/[email protected] would not have this problem, I assume this might be a bug introduced in v0.19.0 and v0.19.1, which might be somehow related to #209

Considering the first workaround, I could understand it if this will become a won't fix due to the confusing exports = module.exports = ..., but I would still be glad if you could check it out with the repro repo when available. Thanks in advance!

bug

All 3 comments

If we add some console.log like this (with // ADDED), the output may be more helpful for debugging

top.js

console.log('evaluating top module');

// this 'exports = module.exports = ...' pattern might be confusing to your compiler
// but it does exist in our project...
exports = module.exports = {
  methodOfTop,
};

// requires sub module
console.log('[top]', 'before requiring sub module');  // ADDED
exports.subModule = require('./sub');
console.log('[top]', 'after requiring sub module');  // ADDED

function methodOfTop() {

}

sub.js

console.log('evaluating sub module');
// requires top module back
const topModule = require('./top');
console.log('[sub]', 'top module is', topModule);  // ADDED

module.exports = {
  methodOfSub,
};

function methodOfSub() {
  // using topModule.methodOfTop
}

Expected Output

evaluating top module
[top] before requiring sub module
evaluating sub module
[sub] top module is { methodOfTop: [Function: methodOfTop] }
[top] after requiring sub module
{ methodOfSub: [Function: methodOfSub] }

Actual Output

evaluating top module
[top] before requiring sub module
evaluating sub module
evaluating top module
[top] before requiring sub module
[top] after requiring sub module
[sub] top module is { methodOfTop: [Function: methodOfTop], subModule: {} }
[top] after requiring sub module
{}

Update:
make expected output and actual output in the correct section

Thank you for filing the issues and creating the repro repos @Mensu!
You're a big help here. I can repro the issue on master branch too.

Update:

I found the issue. It's how we track state associated with module entries. Sometimes a miss can happen (if the module.export is replaced) causing the loader to think it's a new module.

Was this page helpful?
0 / 5 - 0 ratings