Theory:
One of the things that appeals to me about node.js is using it as a command line tool.
In theory, I can write libraries in Javascript and place them in my ~/.node_libraries directory, and then then I can reuse those libraries.
So for instance, I have a text.js in ~/.node_libraries, and it has a bunch of text-related functions I use over and over (depunctuate(), tokenize_text(), things like that).
The beauty of this is that I could use the same text.js file with my command-line scripts and server side. Right now I'm doing all that text processing stuff with Python, but I'd like to just stick to one language.
Practice:
AFAICT, in order to create a node.js module, I have to attach everything that I want to be available to exports or this. I.e., in text.js, I have to do:
exports.depunctuate = depunctuate
or
this.depunctuate = depunctuate
If I use exports, I have problems with using the library server-side à la:
<script src=text.js></script>
because then I get exports is not defined errors.
If I use this, I avoid the error, but everything I export ends up getting attached to the window object.
Is there some way I can set up these libraries that avoid both of these problems? For instance, is there some way I can wrap the exporting of exports so that the var will be apparent to node, but not when it's used in a plain Javascript file on a server?
How about testing for the existence of the exports object before sticking stuff in it?
This has worked well for me so far, but maybe there are better ideas out there:
if(typeof(exports) !== 'undefined' && exports !== null) {
exports.foo = foo;
exports.bar = bar;
}
In CoffeeScript, this can be done a little more tersely:
[exports.foo, exports.bar] = [foo, bar] if exports?
so this comes into a namespacing issue. Unless a function is called with the new operator you will get a this context === to window (global). A way to dodge this is:
(function( exports ) {
/* put your depuncuate definition here to keep it from leaking to the global */
exports.depunctuate = depunctuate;
})( (typeof exports === 'undefined') ? myAppNamespace : exports );
Related
This is an ES6-specific duplicate of this SO thread, which details how to create a javascript module that can be exported for use in both a browser and node context.
I would be just as glad for an answer to appear there, if that's more appropriate.
For background, here's a quote of the ES5 solution, as well as the problems that arise in trying to translate it to ES6. (Credit to users #caolan and #broesch.)
(function(exports){
// Your code goes here. For example:
exports.test = function(){
return 'hello world'
};
})(typeof exports === 'undefined'? this.mymodule = {} : exports);
Thus, if exports is undefined, you must be in the browser, in which case mymodule is declared on the window (i.e., this). Or, if exports is defined, it's in a node context, in which case you can just var mymodule = require('mymodule'). And in either environment, you can then use it as mymodule.test(). Great.
One problem with this is that exports.whatever doesn't expose whatever in the current scope, so if you want to use whatever within the module, you end up needing to do
var whatever = 3;
exports.whatever = whatever;
which can get cumbersome, and is easy to forget.
In ES6, on the other hand, you can do export const whatever = 3, which would both export and expose whatever, which is DRYer and thus easier to maintain.
The new problems are that
export must be at the top level of the file, which means you can't use the syntax from the ES5 answer
export isn't a function (is it?), so you can't use type of to achieve the conditional browser/node context.
So, my question is: what is the ES6 version of creating a .js file that can exported to both the browser and node?
I have my HTML setup like this:
<script type="module" src="main.js"></script>
and all the ES6 modules work fine. The only problem is I now can't refer to anything from within DevTools (like using the Console and typing in a variable to see it's value or using a function manually).
How do I import modules whilst being able to use the DevTools? Thanks!
One way to make a variable accessable within DevTools is to create it on the window object:
// Variable in your module
var importantNumber = 1;
window.importantNumber = importantNumber;
This method works fine if you just have a couple of variables, but if you need to have access to a lot more variables within DevTools, I would recommend you go to the sources-tab in DevTools, search for your module and adding a breakpoint. When the execution pauses, you have access to all the variables within that module on the DevTools console.
If you want to be able to refer to variables created within the module from the console's global scope, you'll have to deliberately expose each such variable that you want to be visible from the console. Either assign each variable to window (probably not a good idea - the whole point of modules is to make things more modular, without global pollution), or perhaps assign a single object to window, to which you assign module variables. For example:
// in the console:
setTimeout(() => {
window.myModule.foo();
console.log(window.myModule.bar);
});
<script type="module">
function foo() {
console.log('doing foo');
}
const bar = 'a string bar';
const thisModule = { foo, bar };
window.myModule = thisModule;
// If you ever reassign variables, you'll have to reassign them on thisModule too
// or, only reference and reassign properties of thisModule, rather than create independent variables
</script>
For anyone else interested, if you're comfortable with it, use a bundler like Webpack. I don't believe (at least at this point) that the browser will by itself be able to use the DevTools on modules (the other solutions are quite janky, and aren't fantastic to work with).
Hopefully in the future, when all major browsers will be able to support ES6 modules without a bundler, we'll be able to use DevTools.
Using a Helper
I personally use a little helper function in development that allows me to expose a bunch a variables in a single expression. For example, it makes the following two blocks equivalent:
window.playerOne = playerOne;
window.someClass = someClass;
window.localData = localData;
globalize({playerOne, someClass, localData});
The helper looks like this:
const globalize = function(variables) {
Object.entries(variables).forEach(([name, value]) => window[name] = value);
};
I'm working on small JS library that I would like to be able to use in different projects.
My problem is the following: I need some data which is defined in a couple files from another JS project of which I do not control the source code. These files are structured as follows:
(function() {
var exportObj;
exportObj = typeof exports !== "undefined" && exports !== null ? exports : this;
exportObj.??? = ...;
}).call(this);
Now, I can prevent global namespace pollution by defining an exports variable in the global scope. I can then rename it to whatever I want once the external files have loaded.
However, this won't work well if I ever need to work in parallel with another script that uses the exportObj = typeof exports ... pattern, as I would catch all the definitions from that script as well.
Is there anyway I can define the value of this that is used for the execution of the external JS files I need to include? That way I could redirect the definitions to a variable of my choosing, without affecting any other script which really on a global exports variable.
It might be possible to wrap the content of those files in exactly the same pattern leading to something like
(function () {
(function() {
var exportObj;
exportObj = typeof exports !== "undefined" && exports !== null ? exports : this;
exportObj.??? = ...;
}).call(this);
}).call(yourNamespace);
If you don't control the source, you could try to proxy it through your server and wrap it for you. You could also try to fetch the scripts as text, wrap it in the client and then insert it. Both sound kinda hacky, but are probably the easiest ways to fix the problem without being too intrusive and clever.
This is my second weekend playing with Node, so this is a bit newbie.
I have a js file full of common utilities that provide stuff that JavaScript doesn't. Severely clipped, it looks like this:
module.exports = {
Round: function(num, dec) {
return Math.round(num * Math.pow(10,dec)) / Math.pow(10,dec);
}
};
Many other custom code modules - also included via require() statements - need to call the utility functions. They make calls like this:
module.exports = {
Init: function(pie) {
// does lots of other stuff, but now needs to round a number
// using the custom rounding fn provided in the common util code
console.log(util.Round(pie, 2)); // ReferenceError: util is not defined
}
};
The node.js file that is actually run is very simple (well, for this example). It just require()'s in the code and kicks off the custom code's Init() fn, like this:
var util = require("./utilities.js");
var customCode = require("./programCode.js");
customCode.Init(Math.PI);
Well, this doesn't work, I get a "ReferenceError: util is not defined" coming from the customCode. I know everything in each required file is "private" and this is why the error is occuring, but I also know that the variable holding the utility code object has GOT to be stored somewhere, perhaps hanging off of global?
I searched through global but didn't see any reference to utils in there. I was thinking of using something like global.utils.Round in the custom code.
So the question is, given that the utility code could be referred to as anything really (var u, util, or utility), how in heck can I organize this so that other code modules can see these utilities?
There are at least two ways to solve this:
If you need something from another module in a file, just require it. That's the easy one.
Provide something which actually builds the module for you. I will explain this in a second.
However, your current approach won't work as the node.js module system doesn't provide globals as you might expect them from other languages. Except for the things exported with module.exports you get nothing from the required module, and the required module doesn't know anything of the requiree's environment.
Just require it
To avoid the gap mentioned above, you need to require the other module beforehand:
// -- file: ./programCode.js
var util = require(...);
module.exports = {
Init: function(pie) {
console.log(util.Round(pie, 2));
}
};
requires are cached, so don't think too much about performance at this point.
Keep it flexible
In this case you don't directly export the contents of your module. Instead, you provide a constructor that will create the actual content. This enables you to give some additional arguments, for example another version of your utility library:
// -- file: ./programCode.js
module.exports = {
create: function(util){
return {
Init: function(pie) {
console.log(util.Round(pie, 2));
}
}
}
};
// --- other file
var util = require(...);
var myModule = require('./module').create(util);
As you can see this will create a new object when you call create. As such it will consume more memory as the first approach. Thus I recommend you to just require() things.
I just took a shot at wrapping one of our modules that is meant to be included via a <script> tag in some boilerplate to allow optional AMD loading with requirejs.
It was quite painful and the best I could come up with is:
(function(){
var exports, jQuery;
if (typeof window.define === 'function' && typeof window.requirejs === 'function') {
exports = {};
define(['jquery'], function (jq) {
jQuery = jq;
return thisModule();
});
} else {
exports = window;
jQuery = window.jQuery;
thisModule();
}
function thisModule() {
}
})();
Notice that this is
A LOT of boilerplate
Requires you to declare dependencies in variables (only jQuery in this case thankfully) AND amd
Needs yet more code if I want to have CommonJs support.
I am primarily concerned about the second point as that one is going to be a doozy when I get beyond wrapping our core files. I'm sure there's some neat(er) wrapper implementations out there but I can't find any.
Anyone have any tips?
What you are trying to re-create something that already exists, I did exactly the same thing, coming up with a slightly different solution in my StackOverflow question.
To cut a long story short the name you need to know is "Universal Module Definition" and there's a GitHub located at https://github.com/umdjs/umd with a variety of different implementations.
Are you trying to do this for an internal module, or an external module?
If you're not require-ing in additional modules, would it be possible to build your modules assuming AMD, and then just shim out the define() function somewhere else in your code, if it's not there? Of course, you'll have to use named modules, but you'd have to basically do that anyway...
If your modules all return their exports from the define() function, it would be relatively simple, and your shimmed define function could look something like this:
//Whatever additional guards you want could be added, of course...
if (typeof(window.define) === undefined){
window.define = function(name, deps, callback){
window.myNamespace[name] = callback();
};
}
At least that way you wouldn't have to add the boilerplate to every module...
If you've got a larger library with lots of interdependent sub-modules, you're probably just going to have to commit to using Require all the way, or not, and then use your wrapper code around your whole library to handle AMD support, the way Jquery and Knockout JS do it.
After hacking on this I managed to come up with the following which seems significantly better and could even be included as a shim in a regular script tag:
A couple notes and drawbacks.
You have to replace any explicit setting on the window object with the exports object
It assumes that any dependency exists as a similarly named property on the window object (though it also makes sure to place that property there). This is generally safe enough in my case but you could easily hack in something like the requirejs paths configuration.
Actually I'm not convinced that the entire exports concept is particularly necessary, or at least not necessary in all cases.
(function () {
var define, exports = {};
if (window.define && window.define.amd) {
define = window.define;
} else {
exports = window;
define = function (name, dependencies, fn) {
var deps = [];
for (var i = 0; i < dependencies.length; i++)
deps.push(window[dependencies[i]]);
var module = fn.apply(undefined, deps);
if (!window[name]) window[name] = module;
};
}
define('mylib.interaction', ['jQuery', 'mylib.core', 'jQuery.UI'], function($, mylib) {
return /*....blah...*/;
})
})()