Bear with me as I lead you through the process that elicited my question.
I'm working on a CLI app in node and I'm using objects to encapsulate my business logic using this pattern:
// my-project/lib/widget/myobject.js
var MyObject = function(x) {
this.x = x;
};
MyObject.prototype.getX = function() {
return this.x;
};
module.exports = MyObject;
I'm also testing these objects:
// my-project/test/lib/widget/myobject.spec.js
var MyObject = require('../../../lib/widget/myobject.js');
describe('MyObject', function() {
...
});
At one point I was unhappy with the naming and directory structure I had chosen. I found myself tediously counting those parent directory references (..) in several spec files when rewriting the relative paths. I figured there must be an easier way to reference a root directory containing these object definitions.
One of the recommendations I found here suggested "putting application-specific modules into node_modules".
Now, as I understand modules, they are the packages I download from npm and use in my project. They contain libraries of useful things with a single API exported to me when I call require. This is not how I view the simple single-purpose classes built specifically for the internal use of my application.
If you've stuck with me this far, thank you! Here is my question:
How do I make the internals of my application more "modular" so it properly follows the intent of the Node module system while remaining object oriented?
I'm not sure how suitable this is for production or for modules you plan on distributing but in your main file you could add this:
process.env.NODE_PATH = __dirname;
require('module').Module._initPaths();
which would let you always require modules relative to the folder containing your main file. I.e. if you had a file in:
library/some_file.js
then in tests/some_other_file.js you could just do:
require('library/some_file');
Or as an alternative you could add this in your main file:
global.__base = __dirname + '/';
and then in your other modules require using:
var MyObject = require(__base + 'my-project/lib/widget/myobject');
Related
I'm trying to figure out how to perform dynamic import of classes in ES6 one the server side (node.js with Babel).
I would like to have some functionalities similar to what reflection offers in Java. The idea is to import all the classes in a specific folder and instanciate them dynamically.
So for example I could have multiple classes declared in a folder like the one below :
export default class MyClass {
constructor(somevar) {
this._somevar = somevar
}
//...
//some more instance level functions here
}
and then somewhere else in my app's code I could have a function that finds out all the classes in a specific folder and tries to instanciate them :
//somewhere else in my app
instanciationFunction(){
//find all the classes in a specific folder
var classFiles = glob.sync(p + '/path_to_classes/**/*.js', {
nodir: true
});
_.each(classFiles, async function (file) {
console.log(file);
var TheClass = import(file);
var instance = new TheClass();
//and then do whatever I want with that new instance
});
}
I've tried doing it with require but I get errors. Apparently the constructor cant be found.
Any idea would be greatly appreciated.
Thanks
ES module definitions are declarative, and the current direction tools are taking is the path where dependencies are determined during parse (via static analysis), waaay before any of the code is executed. This means dynamic and conditional imports go against the said path. It's not like in Node where imports are determined on execution, upon executing require.
If you want dynamic, runtime imports, consider taking a look at SystemJS. If you're familiar with RequireJS, it takes the same concept, but expands it to multiple module formats, including ES6. It has SystemJS.import which appears to do what you want, plus handles the path resolution that you're currently doing.
Alternatively, if your intention is to shed off excess code, consider using Rollup. It will analyze code for you and only include code that's actually used. That way, you don't need to manually do conditional loading.
You need to preprocess with babel, because they are not yet a part of node (for that matter, neither are static imports - node uses require).
https://github.com/airbnb/babel-plugin-dynamic-import-node
steps:
pre
npm i -D babel-cli or npm i -D babel
1
npm i -D babel-plugin-dynamic-import-node
2
.babelrc
{
"plugins": ["dynamic-import-node"]
}
ready, go!
babel-node test_import.js for babel-cli, or for raw babel:
a
(edit) package.json
"scripts": {
"pretest": "babel test_imports.js -o dist/test_imports.js",
"test": "node dist/test_imports.js"
//...
b
node test
I had the same usecase and i managed to dynamically load and instantiate default exported classes using:
const c = import("theClass.js")
const i = new c.default();
using node v16.4.0
I write different files under JavaScript Module Patern like this:
file 1.js
var Module = (function (module) {
module.function = function () {
console.log(this.thirdFunction) // This is my issue. I cannot access the function from the third file because it is concatenated in a way that the function still not exist.
};
return module;
}(Module || {}));
some-folder/file 2.js
var Module = (function (module) {
module.somethingElse = function () {
};
return module;
}(Module || {}));
whatever/file 3.js
var Module = (function (module) {
module.thirdFunction = function () {
};
}(Module || {}));
I put all these files in different directories, names in a different time.
Then I am using concatenating tool to have one file and then I use it in my html file. But I am facing trouble that I cannot resolve.
To have all these working, I have to include them in a specific way and order to call functions from whatever I need and to re-order files when something is not yet defined/created in the returned final object. If I have 100 files and folders it will be a trouble for me again.
Do I understand this right: http://gruntjs.com/configuring-tasks#globbing-patterns
that I have manually to order files, folders and everything in my grunt tasks?
I do not want to use AMD tools, just plain JavaScript files and some approach to hack the order requirements. If there is no any easy idea for me to you, I would try the AMD tools like require.js but I am not sure if these kind of tools can help with this lame problem.
I would appreciate some grunt tool, some files/folders names conventions, anything that would not force me to install more and more tools.
Thank you in advance!
Another thing that bothers me is the following:
If I want to isolate code but I do not have to return object property in the final object, is it alright to do something like this:
file 4.js
var Module = (function (module) {
var someThing = Module.somethingElse() // from file 2.js
and then using someThing here for clicking, DOM rendering, etc, etc
}(Module || {}));
Is it stupid to stick to the same var Module conventions for files where I actually do not return anything? I just think of way how to avoid the object name and using this again
Indeed, AMD tools were created just for this kind of problem. However you can work around this to some extent with grunt. You could simply organize the files that need to go first into a folder and list them out in the order you want, and then have another folder containing all files who's order doesn't matter, which will include everything.
'js/main/First.js',
'js/main/Second.js',
'js/rest/*.js'
No matter what you choose for this project, you might want to look into Require.js or Browserify for future work.
While working on many differente Node.js projects, I began having common code that I want to move out in a new Node.js package in order to not rewrite the same code multiple times. I've a new Node.js module and I'm using it in my projects using npm link.
Now, I'm a bit confused as to how to structure this common library in order to properly modularize it. This is what I have right now in my common library:
// "my-common-lib"'s app.js
module.exports = {
math: require("./math/mathLib"),
network: require("./network/networkLib")
};
--
//mathLib.js
exports.pi = 3.14;
This works, I can do the following in another node.js project:
var commonLibrary = require("my-common-lib");
var commonMath = commonLibrary.Math;
console.log("Pi: " + commonMath.pi);
While this solves the issue, I would prefer something similar to how lodash does it:
var commonMath = require("my-common-lib/math");
console.log("Awesome pi: " + commonMath.pi);
I can't quite figure out how lodash does it, and I would definitely like to avoid having a humongous main js file.
TL;DR I want to modularize a node.js module so I can require submodules (require("my-common-lib\myCommonMathLib")), how can I do this?
lodash does it with a dedicated modular build. Look at the ES6 build for example. Every "sub-project" has a dedicated module, in a dedicated '.js' file. The aggregating file (lodash.js) simply imports all other modules.
If you want the nice lib/module convention, simply have a your lib.js file (aggregator) at the top level, next to a directory by the same name where all internal modules are kept.
Another option for the require("lib") part is to have a "main": "lib.js" configuration in your package.json
If you want to use lodash/array for example, LoDash has an array.js file, with the following:
module.exports = {
'chunk': require('./array/chunk'),
'compact': require('./array/compact'),
So you can easily have math.js inside your main folder, which has something like:
module.exports = {
pi: 3.14
// OR
pi: require('./math/pi'); // and have file pi.js inside math folder
}
This way you can use it as a short:
var math = require('my-common-lib/math');
math.pi; // 3.14
[EDIT]
Thanks to Stafano that formalized my question in a better way:
You have a module
-) There are several files in this module
-) All these files depend on a configuration whose path is unknown to the module itself
-) This module does not do much on its own, and is meant to be used by other applications
-) These applications should inject a configuration path into the module before it can be used
So i have this module, used from another application. It's composed of other submodules and i want to configure it using a configuration object.
I already tried to inject the configuration in my submodels but i had the same problem exposed in the original question.
For example my module use mongoDB (with mongoose) as a store.
// app.js
// in the config object i have the URI to the mongo instance (in order to create a connection).
var myModule = require('myModule')(config);
// myModule.js
// files
// myModule/index.js expose the module's functionalities
// is the entry point so I create the mongoose connection
var mongoose = require('mongoose');
module.exports = function(config){
var connection = mongoose.createConnection(config.store.URL);
// I need to expose this connection to the others submodules.
}
// myModule/storeController.js contains the business logic that use the store (createItem, deleteItem, get...) and requrie mongoose and my Models (store in the models folder)
var mongoose = require('mongoose');
var Item = require('./models/item.js');
exports.createItem = function(item){
Item.save(item, function(err, item){
if (err) throw
...
});
}
// myModule/models/item.js
// In this module i need to use the connection application in the configuration.
var mongoose = require('mongoose');
var connection = // i don't know how to get it
var ItemSchema = new mongoose.Schema({
name: String
});
module.exports = mongoose.model('item', ItemSchema);
If I inject the configuration obj to the item.js i can't do the module.exports of my model.
I hope that this example can clarify my question, but the problem is the simple, expose an object after get it as a parameter.
[PREVIOUS]
I have a node.js application that require a module. This module accept the coniguration file path (a JSON file).
I need to load that configuration on require and expose it to the module.
How can I achieve this behavior?
Something like:
// app.js
var myModule = require('myModule')(__dirname + '/config/myModuleCnfig.json');
// myModule.js
module.exports = function(configPath){
var config = require(configPath);
module.exports = config; // This is wrong
}
Is there another way to get the configuration path, configure the module and share the configuration??
With "share the configuration" i mean that i want to give the possibility to other files of my module to use that configuration.
Thanks for any suggestions!
FINAL EDIT:
After many misunderstandings, your problem is finally clear to me. To summarise what's in the comments, here is the situation:
You have a module
There are several files in this module
All these files depend on a configuration whose path is unknown to the module
itself
This module does not do much on its own, and is meant to be
used by other applications
These applications should inject a
configuration path into the module before it can be used
Since you cannot modify dynamically what a module exports, you should use another approach. As with most situations that you encounter in programming, there is not one way which is always right, as much pedends on your requirements and limitations.
The easiest way to do this (which I don't recommend) is to use a global variable, which you set in your myModule.js file and will be used by the other files in your module. The biggest drawback of this approach is that you wouldn't be able to use multiple instances of the module at the same time with different configurations. Also, any other module could easily modify (deliberately or not) you configuration at any time, by simply changing the value of the global variable, so it's also a security risk.
A much better way, which will probably require more work on your part - depending on how many files you have - is to implement some kind of Inversion of Control (IoC). In your case, you could turn all your exports into functions that accept a config, and then initialise them by passing the active configuration after you require the module. I don't know the specifics of your implementation, so here is some sample code:
// submodule1.js
module.exports = function(config) {
// return something that uses the configuration
}
// myModule.js
var fs = require('fs');
var submodule1 = require('./submodule1');
var submodule2 = require('./submodule2');
// ...
module.exports = function(configPath){
var config = JSON.parse(fs.readFileSync(configPath));
var sm1 = submodule1(config);
var sm2 = submodule2(config);
return /* an object that uses sm1 and sm2 */;
}
If your module is quite complex, you can use some IoC library that does the binding for you. An good one could be Electrolite.
Hope this helps.
PREVIOUS ANSWER:
You can use a library called jsop:
var jsop = require('jsop');
var config = jsop('./config/myModuleCnfig.json');
If you don't want to add a dependency to this module, the linked GitHub page also has a snippet that you can use to load the json config using only native methods.
EDIT: I just realised that this module is only for node 0.11, which you are probably not using. Since you don't probably need the writing functionality, you can use the following snippet instead:
var fs = require('fs')
var config = JSON.parse(fs.readFileSync('./config/myModuleCnfig.json'))
EDIT 2:
Now I think I understand your problem better. To pass the path to the required configuration, you can do something like this:
// myModule.js
var fs = require('fs')
module.exports = function(configPath){
var config = JSON.parse(fs.readFileSync(configPath))
return config;
}
I am currently using requirejs to manage module js/css dependencies.
I'd like to discover the possibilities of having node do this via a centralized config file.
So instead of manually doing something like
define([
'jquery'
'lib/somelib'
'views/someview']
within each module.
I'd have node inject the dependencies ie
require('moduleA').setDeps('jquery','lib/somelib','views/someview')
Anyway, I'm interested in any projects looking at dependency injection for node.
thanks
I've come up with a solution for dependency injection. It's called injectr, and it uses node's vm library and replaces the default functionality of require when including a file.
So in your tests, instead of require('libToTest'), use injectr('libToTest' { 'libToMock' : myMock });. I wanted to make the interface as straightforward as possible, with no need to alter the code being tested. I think it works quite well.
It's just worth noting that injectr files are relative to the working directory, unlike require which is relative to the current file, but that shouldn't matter because it's only used in tests.
I've previously toyed with the idea of providing an alternate require to make a form of dependency injection available in Node.js.
Module code
For example, suppose you have following statements in code.js:
fs = require('fs');
console.log(fs.readFileSync('text.txt', 'utf-8'));
If you run this code with node code.js, then it will print out the contents of text.txt.
Injector code
However, suppose you have a test module that wants to abstract away the file system.
Your test file test.js could then look like this:
var origRequire = global.require;
global.require = dependencyLookup;
require('./code.js');
function dependencyLookup (file) {
switch (file) {
case 'fs': return { readFileSync: function () { return "test contents"; } };
default: return origRequire(file);
}
}
If you now run node test.js, it will print out "test contents", even though it includes code.js.
I've also written a module to accomplish this, it's called rewire. Just use npm install rewire and then:
var rewire = require("rewire"),
myModule = rewire("./path/to/myModule.js"); // exactly like require()
// Your module will now export a special setter and getter for private variables.
myModule.__set__("myPrivateVar", 123);
myModule.__get__("myPrivateVar"); // = 123
// This allows you to mock almost everything within the module e.g. the fs-module.
// Just pass the variable name as first parameter and your mock as second.
myModule.__set__("fs", {
readFile: function (path, encoding, cb) {
cb(null, "Success!");
}
});
myModule.readSomethingFromFileSystem(function (err, data) {
console.log(data); // = Success!
});
I've been inspired by Nathan MacInnes's injectr but used a different approach. I don't use vm to eval the test-module, in fact I use node's own require. This way your module behaves exactly like using require() (except your modifications). Also debugging is fully supported.