I'm building an modular app and have a configuration file to include different modules depending on user preferences.
Normally I'd do something like
var fs = require('fs');
but I'm taking the required modules from an array, so for each require I have a script object that looks like this
{
name:'fs',
file:'fs',
isGlobal:true
}
then I'm dynamically requesting the module with
window[script.name] = require(script.file);
this works fine if I check for window.fs. However, other modules that rely on fs will call just fs.
I know window.fs and fs should both resolve, but in node-webkit, they aren't.
I'm trying to figure out a way to include the var name as a global directly.
Of course, I can't use var script.name = require(script.file); as that would set the script.name value, not a global variable.
Any suggestions on this?
You can refer to global in cross env way:
var global = new Function("return this")();
global.fs = require('fs');
Then at any point you can refer to fs module via simple global fs variable, and in browser environment global would be window.
Still, you should reconsider your approach. As above is very poor way to work with CJS style. One of the beauties of CJS is that it allows you not to rely on global scope, and you go against that, which raise issues.
Other thing, it's a bad practice to resolve paths passed to require dynamically. In modules that we'll have soon with ES6 it won't be possible, you should always use plain strings, and it's good practice to follow with CJS as well. It would be more future bulletproof if you generate script that injects plain strings to requires.
Note: it's been a few versions since I've last used node-webkit, but I think the below is still accurate. I tend to abbreviate node-webkit to nw, if that's okay. :)
I know window.fs and fs should both resolve, but in node-webkit, they aren't.
In nw, global is the global object, while each nw window has it's own window object (unless you fork nw). This may be a lil bit confusing, since using the nw devtools to create a global will actually create a property on window as expected, so it's not unreasonable to assume you can create globals the same way as in a browser. However, that's really just a side-effect of the devtools running in nw's window context.
However, code running in nw's module context does not even have access to window but can, of course, access global normally.
This is documented here. The first three paragraphs specifically deal with your issue. In short, you'll want to be sure which context your code is running in.
Node's globals meanwhile have a (brief) description here.
Related
I'm using a query on both server and client (pub/sub). So I have something like this at a few different locations.
const FOO = 'bar';
Collection.find({property:FOO})
Foo may potentially change and rather than have to update my code at separate locations, I was thinking it may be worth it to abstract this away to a global variable that is visible by both client and server.
I created a new file 'lib/constants.js' and simply did FOO = 'bar; (note no keyword). This seems to work just fine. I found this solution as the accepted answer How can I access constants in the lib/constants.js file in Meteor?
My question is if this a desired pattern in Meteor and even general JS.
I understand I can abstract this away into a module, but that may be overkill in this case. I also think using session/reactive vars is unsafe as it can kinda lead to action at a distance. I'm not even gonna consider using settings.json as that should only be for environment variables.
Any insights?
yes, If you are using older version of meteor then you can use setting.json but for updated version we have import option.
I don't think the pattern is that bad. I would put that file in /imports though and explicitly import it.
Alternatively, you can write into Meteor.settings.public from the server, e.g., on start-up, and those values will be available on the client in the same location. You can do this without having a settings file, which is nice because it doesn't require you to make any changes between development and production.
Server:
Meteor.startup(() => {
// code to run on server at startup
Meteor.settings.public.FOO = 'bar';
});
Client:
> console.log(Meteor.settings.public.FOO);
bar
This is actually a b̶a̶d̶ unfavoured pattern because with global variables you cannot track what things changed and in general constructing a modular and replaceable components is much better. This pattern was only made possible due to Meteor early days where imports directory/pattern was not supported yet and you'd have your entire code split up between both,server and client.
https://docs.meteor.com/changelog.html#v13220160415
You can find many write ups about it online and event stackoverflow answers so I don't want to restate the obvious.
Using a settings.json variable is not an option since we may dynamically change so what are our options? For me I'd say:
Store it the database and either publish it or retrieve it using methods with proper access scoping of course. Also you can dynamically modify it using methods that author DB changes.
Or, you may try using Meteor.EnvironmentVariable. I'd be lying if I said I know how to use it properly but I've seen it being used in couple Meteor projects to tackle a similar situation.
https://www.eventedmind.com/items/meteor-dynamic-scoping-with-environment-variables
Why are global variables considered bad practice?
I am trying to understand this keyword. but the problem is with the node environment. I am getting the expected behavior in Chrome Developer tool but the same code isn't working fine in the node environment.
When we create a var in the global context, it is supposed to be inside the global (node) or window (browser) but in node environment, it doesn't get attached to document.
I am just testing a simple 3 lines of code which works totally fine in chrome.
This is for Node environment
var color = 'red';
console.log(this.color);
console.log(global.color)
and this is for Browser which works fine
var color = 'red';
console.log(this.color);
console.log(window.color)
For the node environment, I am receiving undefined which is not expected.
Here's a software development rule: don't rely on variables sticking on this, global, or module-related objects. Scope can vary and lead to unexpected behavior and bugs. Use explicit (this|global|module.exports).varName bindings.
But if you just want to understand how things work in Node:
Code is wrapped into an IIFE when executed, setting the this value to module.exports (not global).
Access to global is persistent across modules, so if you write global.foo='foo' in a module then require it in bar.js, global.foo will be set to 'foo' in bar.js. It is discouraged to directly use global for most use cases - stick to exports and require.
According to the specs, var is not supposed to make things stick to global: there are some exceptions (see below), but you should not rely on that when writing code
Related questions:
Do let statements create properties on the global object?
Meaning of “this” in node.js modules and functions
This is probably an odd question because it's more typical for people to ask how to avoid using globals.
Coming from the Ruby world, I've become very comfortable using globals in two specific examples:
Constants. When a file is imported in Ruby, all of its constants are automatically made available to the other files in the program.
(and this ties in with the first) Packages. When I load a Ruby Gem in a required file, it also becomes available in my other files.
I've been starting to use module.exports, but I'm finding that I'm importing same modules in lots of different files.
I'd really like to have these features in Javascript. The way I'm writing my code at the moment, I'm using a functional approach and passing all my constants as parameters. The problem is my code is getting too verbose for my liking.
I'm really not looking for a "short answer: no" type of response, here. Even if it is too difficult, I'd appreciate being pointed in a direction for how to avoid passing constants as parameters to functions.
One method of using globals could be to use HTML5 Local Storage.
My thinking is, have an object with your globals, and on page load save each global variable into its own local storage location.
So you have an object with your globals stored:
var globals = {
GLOBAL1: "SomeString",
GLOBAL2: 400
}
Then onload / or if you want to do it sooner have it called before the page loads, you can have a function run through your globals and save the values into local storage
for(var key in globals) {
localStorage.setItem(key, globals[key]);
}
Then, later on, when a function needs, for example GLOBAL2 you can call:
localStorage.getItem("GLOBAL2");
This may be just me lacking a 'bigger picture' so to speak, but I'm having trouble understanding why exporting modules is needed to just split up files.
I tried doing something like this:
//server.js
var app = require('koa')();
var othermodule1 = require('othermodule1')();
var othermodule2 = require('othermodule2')();
var router = require('./config/routes')();
app.use(router.routes());
//routes.js
module.exports = require('koa-router')()
.get('*', function*(next){
othermodule1.something;
})
realizing that routes.js does not have access to 'othermodule1' after calling it from serverjs. I know that there's a way to pass needed variables during the require call, but I have a lot more than just 2 modules that I would need to pass. So from my probably naive perspective, this seems somewhat unnecessarily cumbersome. Someone care to enlighten me or is there actually a way to do this that I missed?
Each node.js module is meant to be a stand-alone sharable unit. It includes everything that it needs to do its job. That's the principle behind modules.
This principle makes for a little more overhead at the start of each module to require() in everything you need in that module, but it's only done once at the server startup and all modules are cached anyway so it isn't generally a meaningful performance issue.
You can make global things by assigning to the global object, but they that often breaks modularity and definitely goes against the design spirit of independently shareable modules.
In your specific code, if routes needs access to othermodule1, then it should just require() it in as needed. That's how modules work. routes should just include the things it needs. Modules are cached so requiring it many times just gives every require() the same module handle from a cache.
This is an adjustment in thinking from other systems, but once you get use to it, you just do it and it's no big deal. Either require() in what you need (the plain shareable module method) or pass something into a module on its constructor (the push method) or create init() methods so someone can initialize you properly or call some other module to get the things you need (the pull method).
app.js:
global.fs = require('fs');
require('./process.js').init();
process.js
// Do stuff with `fs`
fs.realpathSync(...); // etc.
Is it acceptable to save fs under global so that the code within process.js can access fs without having to re-require it?
Or is there some other way I should be doing this?
I believe it is generally not acceptable for a library intended for general distribution to depend on using global variable(s) for anything. The issue is that unless the user knows exactly what globals are used by the library they might get overwritten (intentionally or inadvertently) and cause unexpected behavior of the application or library.
In your specific example, "process.js" should be fine to require the module again. Due to the caching behavior of modules in node.js the overhead of calling "require" again is trivial and it is guaranteed to get exactly the same object that you would have passed through the global (without the risk inherent to using global variables).
This is similar question to node.js require inheritance? I think you can skip global and just do:
fs = require('fs'); // in app.js
fs will be attached to global automatically and will be visible in process.js. Some people prefer to use global for visibility.