Search code examples
javascriptnode.jsmodule.exports

How to use module exports correctly in my local app


Currently I am working on a personal project and am running into a problem understanding the functionality of module.exports within my app...

My app structure looks like this:

app
├── common
│   ├── cmd
│   │   ├── run-cmd.js
├── routes
│   ├── system
│   │   ├── temp.js

I am hoping to achieve a way to store reusable functions in a separate location in my app (in this example run-cmd.js) and have them easily accessible throughout the rest of my app (temp.js) without having to specifically reference the file path (example: require(../../folder/folder/file)

Here is run-cmd.js:

const { spawn } = require("child_process");

module.exports.runCmd = (cmd, callback) => {
    var command = spawn(cmd, [], {shell:true});
    const REGEX_LINE_BREAK = /(\r\n|\n|\r)/gm;
    var result = '';

    command.stdout.on('data', function(data) {
        console.log(data.toString())
        result += data.toString().replace(REGEX_LINE_BREAK, "");
    });

    command.on('close', (code) => {
        console.log("code: " + code)
        return callback(result);
    });
} 

Here is temp.js:

var express = require('express'),
    router = express.Router(),
    runCmd = require('../../common/cmd/run-cmd');

// Add a binding to handle '/temp'
router
    .get('/temp', (req, res) => {
        runCmd('vcgencmd measure_temp', function(result) {
            console.log("Result we are prepping to send: " + result);
            res.json({result: result});
        });
    });

module.exports = router;

I feel like the require() example above defeats the purpose of the flexibility of using module.exports... where doing something like require(./run-cmd) or even require(from-root/path/run-cmd) would be much better to reference in other locations throughout my app. Additionally, in this current example I get an error saying TypeError: runCmd is not a function.

Can anyone help clear some of my confusion with using module.exports and tell me where I am going wrong?


Solution

  • There are several ways to achieve what you wish. One simple but effective solution is to add an attribute to the global object. This has to be done in your main (entry) js file, before any require statements:

    (Paths below are built assuming your entry js file is in the app folder)

    global.__runCmdPath = __dirname + '/common/cmd/run-cmd';
    

    Then, in any other module from which you would like to require runCmd you could simply:

    const runCmd = require(__runCmdPath);
    

    You could even set, as attribute of the global object, the base directory of the scripts containing the functions you'd like to export, and then just append the single script-name, like:

    const runCmd = require(__scriptsDir + '/run-cmd');
    const runCmd2 = require(__scriptsDir + '/run-cmd2');
    

    Another strategy would be to add the scripts folder to the NODE_PATH environmental variable, with the following command for UNIX:

    export NODE_PATH=./common/cmd
    

    and for Windows:

    set NODE_PATH=./common/cmd
    

    Then, you may command node app.js and this way, you will simply be able to require('run-cmd') everywhere. This will work as long as you remember to re-set the variable everytime you start a new shell session.

    For what regards TypeError: runCmd is not a function., this will continue to occur if you try to call runCmd directly, from where you have required it. This is because require() returns an exports object, and your exported methods all belong to this object.

    If you would like to call the runCmd method you exported, in this case, you'll have to do it this way:

    runCmd.runCmd('vcgencmd measure_temp', function(result) {
                console.log("Result we are prepping to send: " + result);
                res.json({result: result});
            });
    

    Thus, you might want to re-name the constant containing your exports object, or the exported name of the runCmd function, to whatever suits you best.