I'm refactoring a large javascript codebase to use RequireJS. Unfortunately, many of the files I'm dealing with are not object-oriented, and cannot return an object without significant modification. Is there a more efficient way to give 'dependent' modules access to the functions and variables contained in a module (without returning an object) ?
I have read about using the exports syntax for defining modules, but it is very unclear whether that would be a valid solution for this situation.
In a defined module, the exports object is what gets exported from the module and passed to whatever module requires it.
Consider this:
define(["exports"], function(exports){
exports.myCustomFunction = function(){};
exports.myCustomObject = {};
exports.myCustomVariable = true;
})
This module will place all the disparate functions and/or objects that you want made public onto the exports object.
At this point RequireJS will use that exports object to pass to a module that requires it:
require(["nameOfCustomModule|filename"], function(myCustomModule){
//evaluates to true
console.log(myCustomModule.myCustomVariable);
})
Here's a simple fiddle. Just bring up your console and you will see the value logged there. http://jsfiddle.net/xeucv/
Hope this clears it up a bit!
Related
The question is simple, how do we make es6 modules act like the ImportScript function used on the web browser.
Explanation
The main reason is to soften the blow for developers as they change their code from es5 syntax to es6 so that the transition does not blow up your code the moment you make the change and find out there are a thousand errors due to missing inclusions. It also give's people the option to stay as is indefinitely if you don't want to make the full change at all.
Desired output
ImportScript(A file path/'s); can be applied globally(implicitly) across subsequently required code and vise-verse inside a main file to avoid explicit inclusion in all files.
ES6 Inclusion
This still does not ignore the fact that all your libraries will depend on modules format as well. So it is inevitable that we will still have to include the export statement in every file we need to require. However, this should not limit us to the ability to have a main file that interconnects them all without having to explicitly add includes to every file whenever you need a certain functionality.
DISCLAIMER'S
(Numbered):
(Security) I understand there are many reasons that modules exist and going around them is not advisable for security reasons/load times. However I am not sure about the risk (if any) of even using a method like "eval()" to include such scripts if you are only doing it once at the start of an applications life and to a constant value that does not accept external input. The theory is that if an external entity is able to change the initial state of your program as is launched then your system has already been compromised. So as it is I think the whole argument around Globalization vs modules boils down to the project being done(security/speed needed) and preference/risk.
(Not for everyone) This is a utility I am not implying that everyone uses this
(Already published works) I have searched a lot for this functionality but I am not infallible to err. So If a simple usage of this has already been done that follows this specification(or simpler), I'd love to know how/where I can attain such code. Then I will promptly mark that as the answer or just remove this thread entirely
Example Code
ES5 Way
const fs = require('fs');
let path = require('path');
/* only accepts the scripts with global variables and functions and
does not work with classes unless declared as a var.
*/
function include(f) {
eval.apply(global, [fs.readFileSync(f).toString()])
}
Main file Concept example:
ImportScript("filePath1");loaded first
ImportScript("filePath2");loaded second
ImportScript("filePath3");loaded third
ImportScript("filePath4");loaded fourth
ImportScript("filePath5");loaded fifth
ImportScript("someExternalDependency");sixth
/* where "functionNameFromFile4" is a function defined in
file4 , and "variableFromFile2" is a global dynamic
variable that may change over the lifetime of the
application.
*/
functionNameFromFile4(variableFromFile2);
/* current context has access to previous scripts contexts
and those scripts recognize the current global context as
well in short: All scripts should be able to access
variables and functions from other scripts implicitly
through this , even if they are added after the fact
*/
Typical exported file example (Covers all methods of export via modules):
/*where "varFromFile1" is a dynamic variable created in file1
that may change over the lifetime of the application and "var" is a
variable of type(varFromFile4) being concatenated/added together
with "varFromFile4".
*/
functionNameFromFile4(var){
return var+varFromFile1;
}
//Typical export statement
exportAllHere;
/*
This is just an example and does not cover all usage cases , just
an example of the possible functionality
*/
CONCLUSION
So you still need to export the files as required by the es6 standard , however you only need to import them once in a main file to globalize their functionality across all scripts.
I'm not personally a fan of globalizing all the exports from a module, but here's a little snippet that shows you how one ESM module's exports can be all assigned to the global object:
Suppose you had a simple module called operators.js:
export function add(a, b) {
return a + b;
}
export function subtract(a, b) {
return a - b;
}
You can import that module and then assign all of its exported properties to the global object with this:
import * as m from "./operators.js"
for (const [prop, value] of Object.entries(m)) {
global[prop] = value;
}
// can now access the exports globally
add(1, 2);
FYI, I think the syntax:
include("filePath1")
is going to be tough in ESM modules because dynamic imports in an ESM module using import (which is presumably what you would have to use to implement the include() function you show) are asynchronous (they return a promise), not synchronous like require().
I wonder if a bundler or a transpiler would be an option?
There is experimental work in nodejs related to custom loaders here: https://nodejs.org/api/esm.html#hooks.
If you can handle your include() function returning a promise, here's how you put the above code into that function:
async function include(moduleName) {
const m = await import(moduleName);
for (const [prop, value] of Object.entries(m)) {
global[prop] = value;
}
return m;
}
How do you require a file within itself in node.js? E.g. api.js:
var api = require(./api.js);
What is the best practice for doing this?
You can totally do it. Try this, for instance (in a file named a.js):
exports.foo = 'foo';
var a = require('./a');
console.log(a);
exports.bar = 'bar';
console.log(a);
At the point where require executes, it will return the module a as it exists at the point where require runs so the field foo will be defined but not bar.
There's no point to doing this though. You use require to bring into your current scope an object which would otherwise not be available (namely, a module). But you don't need to do this to access the module you are currently in: it is already fully available.
The code above works because Node has rules to handle cyclic dependencies. And here you have a module which is cyclicly dependent on itself. Rather than go into an infinite loop of requires, Node has require return the module as built up to that point: a partial module. And the modules in a cyclic dependency have to be designed to handle the fact that they may get partial modules.
Cyclic dependencies are to be avoided as much as possible. Most of the time this means refactoring the modules to avoid the mutual dependency by moving functionality into one or more new modules.
So, again, the best practice is to not do this in the first place.
I'm starting out a long term project, based on Node.js, and so I'm looking to build upon a solid dependency injection (DI) system.
Although Node.js at its core implies using simple module require()s for wiring components, I find this approach not best suited for a large project (e.g. requiring modules in each file is not that maintainable, testable or dynamic).
Now, I'd done my bits of research before posting this question and I've found out some interesting DI libraries for Node.js (see wire.js and dependable.js).
However, for maximal simplicity and minimal repetition I've come up with my own proposition of implementing DI:
You have a module, di.js, which acts as the container and is initialized by pointing to a JSON file storing a map of dependency names and their respective .js files.
This already provides a dynamic nature to the DI, as you may easily swap test/development dependencies.
The container can return dependencies by using an inject() function, which finds the dependency mapping and calls require() with it.
For simplicity, the module is assigned to a global variable, i.e. global.$di, so that any file in the project may use the container/injector by calling $di.inject().
Here's the gist of the implementation:
File di.js
module.exports = function(path) {
this.deps = require(path);
return {
inject: function(name) {
if (!deps[name])
throw new Error('dependency "' + name + '" isn\'t registered');
return require(deps[name]);
}
};
};
Dependency map JSON file
{
"vehicle": "lib/jetpack",
"fuel": "lib/benzine",
"octane": "lib/octane98"
}
Initialize the $di in the main JavaScript file, according to development/test mode:
var path = 'dep-map-' + process.env.NODE_ENV + '.json;
$di = require('di')(path);
Use it in some file:
var vehicle = $di.inject('vehicle');
vehicle.go();
So far, the only problem I could think of using this approach is the global variable $di.
Supposedly, global variables are a bad practice, but it seems to me like I'm saving a lot of repetition for the cost of a single global variable.
What can be suggested against my proposal?
Overall this approach sounds fine to me.
The way global variables work in Node.js is that when you declare a variable without the var keyword, and it gets added to the global object which is shared between all modules. You can also explicitly use global.varname. Example:
vehicle = "jetpack"
fuel = "benzine"
console.log(vehicle) // "jetpack"
console.log(global.fuel) // "benzine"
Variables declared with var will only be local to the module.
var vehicle = "car"
console.log(vehicle) // "car"
console.log(global.vehicle) // "jetpack"
So in your code if you are doing $di = require('di')(path) (without var), then you should be able to use it in other modules without any issues. Using global.$di might make the code more readable.
Your approach is a clear and simple one which is good. Whether you have a global variable or require your module every time is not important.
Regarding testability it allows you to replace your modules with mocks. For unit testing you should add a function that makes it easy for you to apply different mocks for each test. Something that extends your dependency map temporarily.
For further reading I can recommend a great blog article on dependency injection in Node.js as well as a talk on the future dependency injector of angular.js which is designed by some serious masterminds.
BTW, you might be interested in Fire Up! which is a dependency injection container I implemented.
I'm reading the cluster.js file of the cluster package and this part confuses me:
fs.readdirSync(__dirname + '/plugins').forEach(function(plugin){
plugin = plugin.replace('.js', '');
exports.__defineGetter__(plugin, function(){
return require('./plugins/' + plugin);
});
});
I know that you can bind objects or functions to the exports object to expose them to different files, but it seems that it is calling a function already bound to the object. However, I always thought you needed to require the file and access functions that way. What is going on here?
This is realization of lazy loading for plugins. Plugin will be loaded only after first access to module property with his name. __defineGetter__ is the 'syntax sugar' not presented in ECMAScript standard. It binds an object's property to a function to be called when that property is looked up.
If a module sets exports to a single function rather than an arbitrary object, then the result of require will be a function reference which can be called directly (note that a function is actually a type of object and as such can have properties, which can also be functions).
That's not what's going on here, though. At the time the code you've shown is executed, a function called __defineGetter__ has already been defined and attached to exports. Here it's simply being called as a method of exports (presumably because the author didn't feel the need to create a redundant local name for it).
i.e. somewhere along the line there's something like
exports.__defineGetter__ = function(propname, getter) {
...
}
Since it doesn't have a local name, the only way to call it is through exports.
Obviously the purpose of the code here is to allow you to call cluster.nameOfPlugin.method(...) without having to manually require each plugin, while not requiring all the possible plugins to be preloaded; instead only the ones you actually use get loaded.
I have being playing around with requirejs for the last few days. I am trying to understand the differences between define and require.
Define seems to allow for module separation and allow for dependency ordering to be adhere. But it downloads all the files it needs to begin with. Whilst require only loads what you need when you need it.
Can these two be used together and for what purposes should each of them be used?
With define you register a module in require.js that you can then depend on in other module definitions or require statements.
With require you "just" load/use a module or javascript file that can be loaded by require.js.
For examples have a look at the documentation
My rule of thumb:
Define: If you want to declare a module other parts of your application will depend on.
Require: If you just want to load and use stuff.
From the require.js source code (line 1902):
/**
* The function that handles definitions of modules. Differs from
* require() in that a string for the module should be the first argument,
* and the function to execute after dependencies are loaded should
* return a value to define the module corresponding to the first argument's
* name.
*/
The define() function accepts two optional parameters (a string that represent a module ID and an array of required modules) and one required parameter (a factory method).
The return of the factory method MUST return the implementation for your module (in the same way that the Module Pattern does).
The require() function doesn't have to return the implementation of a new module.
Using define() you are asking something like "run the function that I am passing as a parameter and assign whatever returns to the ID that I am passing but, before, check that these dependencies are loaded".
Using require() you are saying something like "the function that I pass has the following dependencies, check that these dependencies are loaded before running it".
The require() function is where you use your defined modules, in order to be sure that the modules are defined, but you are not defining new modules there.
General rules:
You use define when you want to define a module that will be reused
You use require to simply load a dependency
//sample1.js file : module definition
define(function() {
var sample1 = {};
//do your stuff
return sample1;
});
//sample2.js file : module definition and also has a dependency on jQuery and sample1.js
define(['jquery', 'sample1'], function($,sample1) {
var sample2 = {
getSample1:sample1.getSomeData();
};
var selectSomeElement = $('#someElementId');
//do your stuff....
return sample2;
});
//calling in any file (mainly in entry file)
require(['sample2'], function(sample2) {
// sample1 will be loaded also
});
Hope this helps you.
"define" method for facilitating module definition
and
"require" method for handling dependency loading
define is used to define named or unnamed modules based on the proposal using the following signature:
define(
module_id /*optional*/,
[dependencies] /*optional*/,
definition function /*function for instantiating the module or object*/
);
require on the other hand is typically used to load code in a top-level JavaScript file or within a module should you wish to dynamically fetch dependencies
Refer to https://addyosmani.com/writing-modular-js/ for more information.
require() and define() both used to load dependencies.There is a major difference between these two method.
Its very Simple Guys
Require() : Method is used to run immediate functionalities.
define() : Method is used to define modules for use in multiple locations(reuse).