KMK
KMK

Reputation: 1509

How to re-use Node.js module when required with different paths

I have a custom module called counter, which is used by multiple other modules. These other modules are located in different directories, which leads to requiring the counter module with different paths.

Below is simplified setup.

- dir1
-- counter.js
-- module1.js
- dir2
-- module2.js

counter.js

var count = 0;

counter.increment = function(){
  count++;
  console.log(count);
};

module1.js

var counter = require('./counter');
counter.increment(); // Prints 1

module2.js

var counter = require('../dir1/counter')
counter.increment(); // Prints 1, but should print 2

If the same include path was used, then counter would only be created once and the same counter module would be used by both module1 and module2. But because the require paths are different, two separate counter modules are created.

Does anyone know how to prevent this? I would like to have only one counter module.

Upvotes: 1

Views: 354

Answers (4)

Parth Acharya
Parth Acharya

Reputation: 545

This happens because when you import a file, it creates a new instance there, so in every module of yours it will start with 0.

Solution would be to import counter as global in main file and use the same everywhere

In main file

global.counter = require('./counter');

in module files

global.counter.increment();

counter file can be loaded from any of the files, modules and it won't matter. Just declare it once and make sure it is loaded before using the increment method.

Upvotes: 0

explorer
explorer

Reputation: 952

I use NODE_PATH environment variable in my projects. It's value is, root of the project. Then requiring local modules becomes like requiring npm modules.

For your same setup, I change the require statements:

Below is simplified setup.

- dir1
-- counter.js
-- module1.js
- dir2
-- module2.js

counter.js

var count = 0;

exports.increment = function(){
  count++;
  console.log(count);
};

module1.js

var counter = require('dir1/counter');
counter.increment(); // Prints 1

module2.js

var counter = require('dir1/counter')
counter.increment(); // Prints 2

I run the program with NODE_ENV environment variable.

NODE_ENV=. node
> require('dir1/module1');
1
{}
> require('dir2/module2');
2
{}
>

. indicates, current folder.

Upvotes: 0

Samay Jain
Samay Jain

Reputation: 265

A good solution would be to make only the count variable global instead of making the complete module global. For Example-

In Main File-

global.count=0;

In counter.js

counter.increment = function(){
     count++;
     console.log(count);
};

This way you don't need to change your module1.js and module2.js and even for further modules you can use it as you would have earlier.

Upvotes: 1

jfriend00
jfriend00

Reputation: 707198

Since you want a per-app singleton and require() has no way of knowing that a module with two separate paths is actually the same module, the only way I know of to do this (other than removing the separate paths to the module) is to have your module register a singleton as a global and then have it's own initialization check for that singleton before creating another singleton. Then, each time you load it, it would just return an export that points to that one singleton.

 // counter.js
 // if no singleton yet, create the one singleton
 if (!global._counter) {
    global._counter = new Counter();
 }
 // export the singleton
 module.exports = global._counter;

The first time this is loaded with require() (no matter what the path), it will create the one singleton and store it in the global._counter variable. The second time it is loaded (even with a different path), it will find the previous singleton and just export that same one. So, there will only ever be one counter.

This lets all the code that is using it treat it like a real module and avoid using global references anywhere but here. Using globals is generally to be avoided, but this is one thing they are good for when you want a singleton reference to something and cannot use a module-level variable to hold it (which is your exact case).

Then, you can just use it like a normal module:

var counter = require('./counter');
counter.increment(); // Prints 1

// in another file
var counter = require('../dir1/counter');
counter.increment(); // Prints 2

Upvotes: 2

Related Questions