I'd like to add an important functionality that I can use at work without using require() every time. So, I thought modifying built-in objects can make this happen but I could not locate the locations of those objects to do the modification.
Are those objects in NodeJS binary? Would that mean I have to fork the whole NodeJS repo to make this happen?
Please, I don't want to use prototype editing etc which are same as require. I need to have native feeling.
First off, I agree with an earlier comment that this sounds like a bit of an XY problem where we could better help you if you describe what problem you're really trying to solve. Portable node.js programs that work anywhere or work with any future versions of node.js don't rely on some sort of custom configured environment in order to run. They use the built-in capabilities of node.js and they require/import in external things they want to add to the environment.
Are those objects in NodeJS binary?
Yes, they are in the executable.
Would that mean I have to fork the whole NodeJS repo to make this happen?
Yes.
Please, I don't want to use prototype editing etc which are same as require. I need to have native feeling.
"Native feeling"? This sounds like you haven't really bought into the node.js module architecture. It is different than many other environments. It's easy to get used to over time. IMO, it would really be better to go with the flow and architecture of the platform rather than make some custom version of node.js just to save one line of typing in your startup code.
And, the whole concept of adding a number of globals you can use anywhere pretty much shows that you haven't fully understood the design, architectural, code reuse and testability advantages of the module design baked into node.js. If you had, you wouldn't be trying to write a lot of code that can't be reused in other ways that you don't anticipate now.
That said, in searching through the node.js source code on Github, I found this source file node.js which is where lots of things are added to the node.js global object such as setTimeout(), clearTimeout(), setImmediate(), clearImmediate() and so on. So, that source file seems to be where node.js is setting up the global object. If you wanted to add your own things there, that's one place where it would be done.
To provide a sample of that code (you can see the link above for the complete code):
if (!config.noBrowserGlobals) {
// Override global console from the one provided by the VM
// to the one implemented by Node.js
// https://console.spec.whatwg.org/#console-namespace
exposeNamespace(global, 'console', createGlobalConsole(global.console));
const { URL, URLSearchParams } = require('internal/url');
// https://url.spec.whatwg.org/#url
exposeInterface(global, 'URL', URL);
// https://url.spec.whatwg.org/#urlsearchparams
exposeInterface(global, 'URLSearchParams', URLSearchParams);
const {
TextEncoder, TextDecoder
} = require('internal/encoding');
// https://encoding.spec.whatwg.org/#textencoder
exposeInterface(global, 'TextEncoder', TextEncoder);
// https://encoding.spec.whatwg.org/#textdecoder
exposeInterface(global, 'TextDecoder', TextDecoder);
// https://html.spec.whatwg.org/multipage/webappapis.html#windoworworkerglobalscope
const timers = require('timers');
defineOperation(global, 'clearInterval', timers.clearInterval);
defineOperation(global, 'clearTimeout', timers.clearTimeout);
defineOperation(global, 'setInterval', timers.setInterval);
defineOperation(global, 'setTimeout', timers.setTimeout);
defineOperation(global, 'queueMicrotask', queueMicrotask);
// Non-standard extensions:
defineOperation(global, 'clearImmediate', timers.clearImmediate);
defineOperation(global, 'setImmediate', timers.setImmediate);
}
This code is built into the node.js executable so the only way I know of to directly modify it (without hackish patching of the executable itself) would be to modify the file and then rebuild node.js for your platform into a custom build.
On a little more practical note, you can also use the -r module command line argument to tell node.js to run require(module) before starting your main script. So, you could make a different way of starting node.js from a shell file that always passes the -r fullPathToYourModule argument to node.js so it will always run your startup module that adds things to the global object.
Again, you'd be doing this just to save one line of typing in your startup file. It is really worth doing that?
Related
I have to deal with a node.js server project that uses global variables for common APIs. For instance in the entry point server.js there is a Firebase variable for the real-time database that is stored like this:
fireDB = admin.database();
I wasn't aware that this is possible and I would consider this a bad approach, but now I have to deal with it.
I'm not really interested to re-write any of the many calls to this variable in all those files, rather I would find a way to make fireDB show me suggestions only by changing this variable or installing an extension.
I tried to define it on top of the file as var fireDB, but then suggestions only work in the same file, not in others.
When I set a dot behind admin.database() the suggestions work, when I write fireDB. I get no suggestions, yet the call seems to be possible. Suggestions need to work in other files, too. How can I get this to work?
WARNING: MAKE SURE YOU UNDERSTAND THE PROBLEMS WITH GLOBALS BEFORE USING THEM IN A PROJECT
The above warning/disclaimer is mostly for anyone starting a new project that might happen across this answer.
With that out of the way, create a new .d.ts file and put it somewhere with a descriptive name. For example, globals.d.ts at the top level of the directory. Then just populate it with the following (I don't have any experience with firebase, so I had to make some assumptions about which module you're using, etc.):
globals.d.ts
import { database } from "firebase-admin";
declare global {
var fireDB: database.Database;
}
IntelliSense should then recognize fireDB as a global of the appropriate type in the rest of your JavaScript project.
Why does this work? IntelliSense uses TypeScript even if you're working with a JS project. Many popular JS packages includes a .d.ts file where typings are declared, which allows IntelliSense to suggest something useful when you type require('firebase-admin').database(), for example.
IntelliSense will also automatically create typings internally when you do something "obvious", e.g. with literals:
const MY_OBJ = { a: 1, b: "hello"};
MY_OBJ. // IntelliSense can already autocomplete properties "a" and "b" here
Global autocompletion isn't one of those "obvious" things, however, probably because of all the problems with global variables. I'd also guess it'd be difficult to efficiently know what order your files will run in (and hence when a global might be declared). Thus, you need to explicitly declare your global typings.
If you're interested in further augmenting the capabilities of IntelliSense within your JS project, you can also use comments to explicitly create typings:
/**
* #param {String[]} arrayOfStrings
*/
function asAnExample(arrayOfStrings) {
arrayOfStrings. // IntelliSense recognizes this as an array and will provide suggestions for it
}
See this TypeScript JSDoc reference for more on that.
I got to the point that I'd like to have a factory to manage all my dependencies for the modules in a single place instead of having a lot of statements using require all over the place in my code.
I've looked at some approaches that rely on AMD, but I'd like to know how to do it by using node.js / express combination with the OOB module loader which I think it uses common.js.
I've been thinking of doing something like this:
module.exports = {
lib:[],
load:function(name){
if(this.lib[name]!==undefined && this.lib[name]!==null){
return this.lib[name];
}
switch(name)
{
case 'express':
this.lib[name] = require('express');
break;
case 'morgan':
this.lib[name] = require('morgan');
break;
case 'body-parser':
this.lib[name] = require('body-parser');
break;
}
console.log(this.lib);
return this.lib[name];
}
};
Some people say that's more than a factory its a mediator pattern, so either way I just wanted to illustrate my point.
my basic requirement is to handle all the dependencies from a single place in the system if I need to change a dependency I just change it on this file and automatically updates through the whole system.
so is there a better way to handle this? any Implementation that already have done this approach?
thanks!
Technically this is what require() does internally.
require('foo'); require('foo')
guarantees that it will load and run foo only once. The second call will return a cached copy from its internal array.
You can achieve the same naming indirection (and an API adapter if you'll ever decide to change the implementation without changing callers) by requiring JS files or your node modules that re-export modules you actually use (e.g. require('./my-express-wrapper') instead of require('express')).
if I need to change a dependency I just change it on this file and automatically updates through the whole system.
I'd be concerned that it will cause code to be surprising:
require('factory').load('body-parser'); // loads Formidable!?
I see little benefit in having such layer of indirection:
Even in the best case of drop-in-replacement it saves very little work, because project-global find'n'replace of require('foo') with require('bar') is an easy task in most text editors.
The hard part of replacing module (which is unlikely to be 100% compatible) is getting existing code to correctly work with it. This is not avoided by use of the factory pattern. You'll need to write an adapter either way, and sometimes it may even be better to actually change uses of the module everywhere than to write an emulation layer for an API that probably wasn't good anyway.
I'm trying to just load the event_broker module in the chaplinjs . I am able to to do by doing something like
require(["underscore", "chaplin"], function(_, chaplin)
{
var eventBroker = _({}).extend(chaplin.EventBroker);
});
But, this is not good enough in my case. I need to be able to load the event_broker module synchronously. I know that is what require designed to do. Is there a way to do that?
I know that is what require designed to do.
No, that's not what RequireJS is designed to do. (Did you forget to put "not" in there?) RequireJS is designed to load modules asynchronously.
I would normally suggest loading Chaplin through a script element because that would be synchronous but, after looking at the code of Chaplin, I see that it fails with throw new Error('Chaplin requires Common.js or AMD modules'); if it does not detect a CommonJS or AMD environment.
Almond can be used to load bundles of AMD modules synchronously so this may be an option for you.
I read in another question that i can not find now that it was a bad idea to make modules in node.js global, then he changed his answer because of changes in node.js
The examples for express.js on github does now show an example.
https://github.com/visionmedia/express/tree/master/examples
So what if i need the same module in multiple files, like sequelize or async?
app.js:
var document = require('./routes/document')
async = require('async');
/routes/document.js:
async.series([...]);
OR
app.js:
var document = require('./routes/document')
var async = require('async');
/routes/document.js:
var async = require('async');
async.series([...]);
Should i require the async module again in document.js, or just make async global so that i can use it without require it in new files?
Do not use globals to access modules. Always use require and only require() the modules you need in that .js file. Require will only load your module once and hold a reference to it.
Only requiring what you need, even if it's several modules, is good because it makes it clear what the dependencies of your code are and might help you restructure your code later.
Only requiring() what you need can also help at the point where you want to replace one module with another. For example, you might want to replace knox(S3) with the new aws-sdk module. When you remove the knox npm module, require will immediately blow up in the files that use/require knox. If it was a global variable, you would need to find all references to that global reference and rely on your IDE or text editor to find it.
In short, include it in each file. The file is cached after the first require, so the content is really only run once (you can test this by putting a log statement in the require; it'll only show once).
What I do is make one file which requires all the others I need, and exports them all. Then you only need to require the one file. This can be really handy when you have many modules that you're including.
There is nothing wrong with requiring a module more than once. Internally require only executes the required file once and then caches the result. See the Node.js Modules documentation for more information.
I want to set up include paths (and other paths, like view script paths) based on the module being accessed. Is this safe? If not, how could I safely set up include paths dynamically? I'm doing something like the code below (this is from a controller plugin.)
public function dispatchLoopStartup(Zend_Controller_Request_Abstract $request) {
$modName = $request->getModuleName();
$modulePath = APP_PATH.'/modules/'.$modName.'/classes';
set_include_path(get_include_path().PATH_SEPARATOR.$modulePath);
}
I'm not sure whether it is safe or not, but it doesn't sound like the best practice. What if someone entered a module name like ../admin/? You should sanitize the module name before using it.
It's fine as long as you sanitize your variables before using them, but it won't be very performant. Fiddling with include paths at runtime causes a serious impact performance.
You're trying to load models/helpers per module? You should look at Zend_Application:
Zend_Application provides a bootstrapping facility for applications which provides reusable resources, common- and module-based bootstrap classes and dependency checking. It also takes care of setting up the PHP environment and introduces autoloading by default.
Emphasis by me