0

[Node.js / CommonJS]

I'm creating a single GitHub repo for my tool which has a Core, Control, Server, and Client library. It's unlikely that all of them will be used at the same time.

I'd like my fellow coworkers to require one entry point for all four libraries. So I made this questionable implementation: exporting the Core class with static properties pointing to the other classes.

Tool.js

class Core
{
    static get Control(){return require("./control")};
    static get Server(){return require("./server")};
    static get Client(){return require("./client")};
    constructor(opts)
    {
    }
}
module.exports = Core;
// module.exports = {Core, Control, Server, Client};

index.js

//Create core instance
const Tool = require("Tool");
const tool = new Tool();

//Create server instance
const ToolServer = require("Tool").Server;

//etc
const ToolClient = require("Tool").Client;

Is this a bad pattern? I'd RTFM in regards to importing classes from static properties, but I'm not sure where to find resources that dives deep into the inner workings of JavaScript/Node.js/CommonJS.

  • I think I'm missing some of the magic in this code. How does require() learn to map "Tool" to Core? – candied_orange May 25 '18 at 9:47
  • 1
    @candied_orange Hi, "Tool" resolves to "Tool.js" or something like that. Core is the name of the class definition, not necessarily the filename. – Vic May 25 '18 at 9:53
  • are you telling me that the Core class is defined in Tool.js, and require("Tool") somehow knows Core is the only class to care about in there? – candied_orange May 25 '18 at 10:02
  • 1
    @candied_orange Neither. You get undefined. {Core, Core2} is shorthand for {Core: Core, Core2: Core2} which means you would need to do something like require("Tool").Core2.Server to get the Server class. – Vic May 25 '18 at 10:22
  • 1
    @candied_orange Yup, you're directly assigning the Core class to module.exports, unlike the previous example which was assigning an object {Core: Core} instead. I suggest you read up on ES6/ESNext and CommonJS if you want to learn more about this. – Vic May 25 '18 at 10:58
1

As it seems to me this works as a namespace, currently ES6 modules allow for something similar with the export and import keywords:

Core.js

import * as Control from './control';
import * as Server from './server';

export {Control, Server}

I've used that pattern in many projects and it always helps in keeping code modular.

The only thing I would change in your require implementation you could use an object instead of a class, that way is more readable and there are no functions involved, something like:

Core.js

const Control = require('./control');
const Server = require('./server');

module.exports = {Controle, Server};

// Another alternative would be

module.exports.Control = require('./control');
module.exports.Server = require('./server');

If you still want to do a lazy require as I assume for having functions, this could be an alternative:

module.exports = {
  Control = () => require('./control'),
  Server = () => require('./server')
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.