Separating functionality out into separate index.js files - javascript

I'm collaborating on a new NodeJS module with a colleague and the file structure is as follows:
index.js
let MyModule = require('./src/index.js);
MyModule = new MyModule();
module.exports = MyModule;
src/index.js
const depA = require('depA');
const MyModule = function MyModule() {
this.doStuff = function doStuff() {
console.log('doing stuff...)
}
}
module.exports = MyModule;
I'm wondering if there is any need/value to split into these two index.js files and whether it would be better to have just one top level file. Is this considered an anti-pattern? I did wonder if this approach works better when you have a complex library with functionality split out across many feature files.

Related

Add functions in other folder, to an object in this folder

I want to create an object that would import functions from another folder and it would look something like this:
class = {
functions: {
//All functions here
}
}
The functions would be inside of a different folder, however, I want to make some sort of importer in which it would make new classes for each new function/file it finds inside of the folder.
someFunction.js Function File:
function someFunction() {
console.log("this is some function");
}
So I would like for something to look like this:
class.functions.someFunction()
No, I do not want to have it hard coded into the object, I want to import all functions from a folder and create functions like that.
Well, first I wan't to answer your question as I think you want, even if I also think it is not the correct way to proceed.
I'll also assume that with class you are not referring to an actual ES6 Class, but we are talking about a plain object.
So this is the code:
const fs = require('fs');
const path = require('path');
function importer(dirPath) {
const absoluteDirPath = path.normalize(
path.isAbsolute(dirPath)
? dirPath
: path.resolve(process.cwd(), dirPath)
);
const output = {
functions: {}
};
const content = fs.readdirSync(path.normalize(absoluteDirPath));
content.forEach((basename) => {
const absoluteItemPath = path.join(absoluteDirPath, basename);
if (fs.statSync(absoluteItemPath).isFile() && /\.js$/i.test(basename)) {
output.functions[basename.slice(-3)] = require(path.relative(
__dirname,
absoluteItemPath
));
}
});
return output;
}
module.exports = importer;
For this to work, all your functions in your files should be exported like:
module.exports = function myFunction() {};
To use the 'importer', you just do:
const artemis = importer('/path/to/directory'); // PATH MUST BE ABSOLUTE OR RELATIVE TO CWD.
/*
SUPPOSING THAT YOUR DIRECTORY CONTAINS THE FOLLOWING FILES:
function1.js
function2.js
Then you can do:
artemis.function1();
artemis.function2();
Please note that your files must be named in a JS friendly way (a valid string for an object key).
*/
A final important note about this odd method: This will only ever work in a NodeJS environment. Even if functions could have worked in other environments (like a browser). The next method, will work for any ECMAScript environment after proper building process: transpilation (EX: Babel) and bundling (EX: Webpack).
Suggested Solution
Use ES6 Static import / export like modern JS libraries do. This comes with huge benefits, from static code analysis to tree shaking and more.
Let's suppose the following hierarchy:
// - index.js
// - internals/
// - index.js
// - module-1.js
// - module-2.js
internals/module-1.js
function module1() {}
export {module1};
internals/module-2.js
import {module1} from 'module-1.js';
function module2() {
// YOU CAN USE module1 IF YOU NEED. (AVOID CIRCULAR REFERENCES)
module1();
}
export {module2};
internals/index.js
import {module1} from './module-1.js';
import {module2} from './module-2.js';
export {module1, module2};
index.js
import * as moduleGroup from './internals/index.js';
export {moduleGroup};
Finally, where you import your moduleGroup, you can do:
moduleGroup.module1();
moduleGroup.module2();
Obviously this is a basic scenario, but this is, IMHO, the correct way to deliver a group of functions and other stuff. Please let me know if you have any doubt.

JavaScript unit testing with external module

I am working on an existing node project where the code structure for most of the js files looks something like following.
var mod1 = require("mod1");
var mod2 = require("mod2");
var modn = require("moden");
function func1(data, callback) {
// Do some validation with data etc..
// Use one or more imported modules here
}
function func2(data, callback) {
// Do some validation with data etc..
// Use one or more imported modules here
}
exports.func1 = func1
exports.func2 = func2
How can unit test func1, while not being dependent on the imported modules? How can I mock/stub them?
I come from the Java world so I am familiar with the mocking concepts, but here I am not sure how can I mock the globally declared module imports using require.
Currently we are using nodeunit for the unit testing purpose, but it tests very small part of the code.
I am reading about simon.js and testdouble but not sure how to use them to mock the global variables.
Any help/direction is appreciated.
For overriding dependencies during testing I recommend to use in your situation the https://github.com/thlorenz/proxyquire module
For quick exmple I paste example from project github
foo.js:
var path = require('path');
module.exports.extnameAllCaps = function (file) {
return path.extname(file).toUpperCase();
};
module.exports.basenameAllCaps = function (file) {
return path.basename(file).toUpperCase();
}
foo.test.js:
var proxyquire = require('proxyquire')
, assert = require('assert')
, pathStub = { };
// when no overrides are specified, path.extname behaves normally
var foo = proxyquire('./foo', { 'path': pathStub });
assert.equal(foo.extnameAllCaps('file.txt'), '.TXT');

Javascript, multi file modules and Require.Js

I am designing a not-trivial application in Javascript.
From what i read so far a common practice is to avoid cluttering the global namespace by defining everything into modules.
And for convenience and code clarity a module can be divided into separate files using the module Augmentation pattern
var MODULE = (function (my) {
// functions, objects, etc ...
return my;
}(MODULE || {}));
Now when having many modules and module dependencies, require.Js seems like a promising tool to add order, decoupling and cleaner namespace. having all modules loaded asynchronously and make sure they run only after their dependencies are ready.
define(["depenencyModule1", "depenencyModule2"],
function(depenencyModule1, depenencyModule2) {
// define MyModule
return MyModule;
}
);
This usage however interferes with the module augmentation pattern from before, at first it seems like i am using it wrong but then i went through require.js documentation and found this:
"Only one module should be defined per JavaScript file, given the nature of the module name-to-file-path lookup algorithm."
So now i am confused, If i write my module to a single file it will be huge and maintainable, doesn't that make require.js useless?
Or perhaps Javascript concept of a module is a tiny bit of code compare to modules in other languages ?
RequireJS allows you to have a facade module which is implemented as a group of RequireJS modules. For instance, you could have:
define(function (require, exports, module) {
'use strict';
var foo = require("./foo");
var bar = require("./bar");
for(var prop in foo) {
exports[prop] = foo[prop];
}
for(var prop in bar) {
exports[prop] = bar[prop];
}
});
This module exports everything from foo and bar. From the point of view of someone importing it, it looks like a single module, even though three RequireJS modules are involved (the facade, and the two modules it imports).
Another thing I've done is declare a single class across multiple modules. I might have a foo module that exports the class Foo:
define(function (require, exports, module) {
'use strict';
var core = require("./foo_core");
require("./foo_init");
require("./foo_gui");
...
exports.Foo = core.Foo;
});
The foo_core module actually defines the class:
define(function (require, exports, module) {
'use strict';
function Foo () {
// ...
}
Foo.prototype.bar = function () { ... };
exports.Foo = Foo
});
Other modules add to it. foo_init:
define(function (require, exports, module) {
'use strict';
var Foo = require("./foo_core").Foo;
Foo.prototype.init = function () { ... };
});
foo_gui:
define(function (require, exports, module) {
'use strict';
var Foo = require("./foo_core").Foo;
Foo.prototype.render = function () { ... };
Foo.prototype.erase = function () { ... };
});
I've used both methods above to split code which from the standpoint of the API should appear as a single module but is implemented across multiple files.

Use ES6 library in Ember-cli project

I have a small EmberJS application that uses Ember-Cli. My application has a private ES6 library that is a bower dependency. Basically, what I want is to import the library and use it wherever I want.
If I'm not wrong, I should transpile the library in my brocfile.js and use it afterwards. Unfortunately, I cannot provide too much concrete information but I'll try my best to be the clearer possible.
My external library is named main-lib and is structured the following way (it is working in another project):
bower_components
main-lib
api.js
main.js
message.js
In the main.js file, I have the following:
import Api from 'main/api';
import Message from 'main/message';
var main = {};
main.api = Api;
main.message = Message;
export default main;
So, what I want to do, is, in my application, to import main and use the different functions it contains.
Example, in a certain emberjs controller:
import Main from 'main';
//use Main here
To do so, I thought of doing the following in my brocfile.js
var tree = 'bower_components/main-lib';
var ES6Modules = require('broccoli-es6modules');
var amdFiles = new ES6Modules(tree, {
format: 'amd',
bundleOptions: {
entry: 'main.js',
name: 'mainLib'
}
});
However, this does nothing. Basically, I want the transpiled files to be included in vendor.js or somewhere where I would be able to use the library by importing it.
There's something I'm missing here but I can't pinpoint it.
Edit1: After adding these lines at the end of my brocfile.js:
mergeTrees = require('broccoli-merge-trees')
module.exports = mergeTrees([app.toTree(), amdFiles]);
I can get an ES5 that looks like this:
define(['main/api', 'main/message'], function(api, message) {
var main = {};
main.api = Api;
main.message = Message;
var _main = main;
return _main;
});
The problem is that it does not import main/api and main/message as well. Do I have to repeat the code for each file that I want ?
Also, the file is not concatenated in vendor.js but simply but at the root of /dist
You have the following: import Api from 'main/api' - but I don't see a folder called main in what you've explained - only a folder called main-lib ...
Could it be that main/api and main/message are not included because they actually don't exist? You might need to use main-lib/api and main-lib/message in your main.js file
The integration of Broccoli with ember-cli already includes a transpiler, so I think something like this should be enough:
app.import('bower_components/main-lib/main.js', {
type: 'vendor',
exports: { 'main': ['default'] }
);
And then you can:
import Main from 'main';
With what you currently have in your Brocfile you still need to merge your amdFiles (app.import would do that for you).
Something like:
mergeTrees = require('broccoli-merge-trees')
module.exports = mergeTrees([app.toTree(), amdFiles]);
None of this is tested, but you get the idea.
Ember seem to be advocating using: https://www.npmjs.com/package/broccoli-es6modules
This would mean importing your module would look something like:
var mergeTrees = require('broccoli-merge-trees');
var tree = './bower_components/main-lib';
var ES6Modules = require('broccoli-es6modules');
var amdFiles = new ES6Modules(tree, {
format: 'amd',
bundleOptions: {
entry: 'main.js',
name: 'main-lib'
}
});
module.exports = mergeTrees([app.toTree(), amdFiles])

How to make object references available in Node modules?

I have an Express.js web app. In the main app.js file, I require() a bunch of third party dependencies. Recently I've started extracting parts of my app.js code into separate modules, e.g.
// in app.js
var users = require('./modules/users');
// and then e.g.
// users.add(doc);
Inside my modules, I sometimes need to use objects that are available in my main app.js file and I'm wondering what's the best way to pass references to those object to my modules.
My current approach:
// in app.js
// pass needed object references in initialization step
users.init([db, logger]);
and
// in modules/users.js
var db, logger;
exports.init = function (deps) {
db = deps[0];
logger = deps[1];
};
Does this approach make sense? Is there a better way to perform this?
Sure, just use modules! :)
// db.js
// create db instance here rather than in app.js
module.exports = db;
And
// logger.js
// create logger instance here rather than in app.js
module.exports = logger;
Then
// app.js
var db = require('./db');
And
// lib/somewhere.js
var db = require('../db');
This way you're able to rely on the CommonJS dependency injection system rather than on doing the dependency injection all by yourself (passing references around instead of requireing a module into yours).
The reason why this works as expected is that modules are only interpreted once, so if you instantiate everything once as opposed to using a factory function, it just works.
You should just be able to require modules as normal:
// users.js
var db = require('./db');
exports.init = function() {
// use db in here
};
However, sometimes this isn't possible and you will need to explicitly pass in the module.
One way to do it is to pass in dependencies when you require the module:
// users.js
module.exports = function(db, logger) {
return {
init: function() { /* use db and logger in here */}
};
}
// app.js
var db = ...;
var logger = ...;
var users = require('./users')(db, logger);
users.init();
This is the pattern that I personally prefer, I think it's cleaner to pass dependencies into the require than into some init method like you have in your example.
You'll see this done in ExpressJS code quite a lot, for example when we have all our routes in another file and need to pass our app instance around:
require('./routes')(app);
If you need something to be initialized specifically in app.js rather than their own module you can export them from app.js and then require app.js:
// app.js
var db = require("db"),
logger = require("logger");
// do your initialization with db and logger
module.exports = { db: db, logger: logger };
and then:
// users.js
var db = require("./app").db,
logger = require("./app").logger;
// use db and logger
This works because as #Nico mentioned, modules are interpreted once and app.js will not be interpreted each time it is required from elsewhere.

Categories

Resources