Define execution context of external JS file? - javascript

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.

Related

how to share javascript code between front- and back-end (ES6)

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?

How to write a module that works with Node.js, RequireJS as well as without them

I am working on a JavaScript library for JSON/XML processing. My library works in browser as well as Node.js (with xmldom and xmlhttprequest modules).
One of the users recently asked for RequireJS support. I have taken a look at the RequireJS/AMD thing and think it is a good approach so I'd like to provide this.
However I'd like to retain the portability: my library must work in browsers (with and without RequireJS) as well as Node.js. And in the browser environment I don't depend on xmldom or xmlhttprequest since these things are provided by the browser itself.
My question is: how can I implement my library so that it works in browsers as well as in Node.js with an without RequireJS?
A bit of historyand my current solution
I initially wrote my library for browsers. So it just created a global-scope object and put everything inside it:
var Jsonix = { ... };
Later on users asked for Node.js support. So I added:
if(typeof require === 'function'){
module.exports.Jsonix = Jsonix;
}
I also had to import few modules mentioned above. I did it conditionally, depending on whether the require function is available or not:
if (typeof require === 'function')
{
var XMLHttpRequest = require('xmlhttprequest').XMLHttpRequest;
return new XMLHttpRequest();
}
Now there's this story with RequireJS. If RequireJS is present then the require function is present as well. But module loading works differently, I have to use the define function etc. I also can't just require things since require has an async API in RequireJS. Moreover, if my library is loaded via RequireJS, it seems to process the source code and detects require('something') even if I do it conditionally like
if (typeof require === 'function' && typeof require.specified !== 'function) ...
RequireJS still detects require('xmlhttprequest') an tries to load the corresponding JS file.
Currently I'm coming to the following solution.
// Module factory function, AMD style
var _jsonix = function(_jsonix_xmldom, _jsonix_xmlhttprequest, _jsonix_fs)
{
// Complete Jsonix script is included below
var Jsonix = { ... };
// Complete Jsonix script is included above
return { Jsonix: Jsonix };
};
// If require function exists ...
if (typeof require === 'function') {
// ... but define function does not exists, assume we're in the Node.js environment
// In this case, load the define function via amdefine
if (typeof define !== 'function') {
var define = require('amdefine')(module);
define(["xmldom", "xmlhttprequest", "fs"], _jsonix);
}
else {
// Otherwise assume we're in the RequireJS environment
define([], _jsonix);
}
}
// Since require function does not exists,
// assume we're neither in Node.js nor in RequireJS environment
// This is probably a browser environment
else
{
// Call the module factory directly
var Jsonix = _jsonix();
}
And this is how I check for dependencies now:
if (typeof _jsonix_xmlhttprequest !== 'undefined')
{
var XMLHttpRequest = _jsonix_xmlhttprequest.XMLHttpRequest;
return new XMLHttpRequest();
}
If I have require but not define then I assume this is a Node.js environment. I use amdefine to define the module and pass the required dependencies.
If I have require and define thet I assume this is a RequireJS environment, so I just use the define function. Currently I also assume this is a browser environment so dependencies like xmldom and xmlhttprequest are not available and don't require them. (This is probably nor correct.)
If I don't have the require function then I assume this is a browser environment without RequireJS/AMD support so I invoke the module factory _jsonix directly and export the result as a global object.
So, this is my approach so far. Seems a little bit awkward to me, and as a newbie to RequireJS/AMD I'm seeking advise. Is it the right approach? Are there better ways to address the problem? I'd be grateful for your help.
Take a look at how underscore.js handles it.
// Export the Underscore object for **Node.js**, with
// backwards-compatibility for the old `require()` API. If we're in
// the browser, add `_` as a global object.
if (typeof exports !== 'undefined') {
if (typeof module !== 'undefined' && module.exports) {
exports = module.exports = _;
}
exports._ = _;
} else {
root._ = _;
}
...
// AMD registration happens at the end for compatibility with AMD loaders
// that may not enforce next-turn semantics on modules. Even though general
// practice for AMD registration is to be anonymous, underscore registers
// as a named module because, like jQuery, it is a base library that is
// popular enough to be bundled in a third party lib, but not be part of
// an AMD load request. Those cases could generate an error when an
// anonymous define() is called outside of a loader request.
if (typeof define === 'function' && define.amd) {
define('underscore', [], function() {
return _;
});
}
This is what I ended up with:
// If the require function exists ...
if (typeof require === 'function') {
// ... but the define function does not exists
if (typeof define !== 'function') {
// Assume we're in the Node.js environment
// In this case, load the define function via amdefine
var define = require('amdefine')(module);
// Use xmldom and xmlhttprequests as dependencies
define(["xmldom", "xmlhttprequest", "fs"], _jsonix_factory);
}
else {
// Otherwise assume we're in the browser/RequireJS environment
// Load the module without xmldom and xmlhttprequests dependencies
define([], _jsonix_factory);
}
}
// If the require function does not exists, we're not in Node.js and therefore in browser environment
else
{
// Just call the factory and set Jsonix as global.
var Jsonix = _jsonix_factory().Jsonix;
}
Here is a template I'm currently using, it's both AMD and node compatible though not directly loadable stand-alone in the browser...
The main advantage to this approach is that the domain-specific code does not need to care about what imported it, for the general case.
/**********************************************************************
*
*
*
**********************************************************************/
((typeof define)[0]=='u'?function(f){module.exports=f(require)}:define)(
function(require){ var module={} // makes module AMD/node compatible...
/*********************************************************************/
/*********************************************************************/
/**********************************************************************
* vim:set ts=4 sw=4 : */ return module })

Wrapper to allow a module to work with AMD/CommonJs or script tags?

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...*/;
})
})()

Read file with fs.readFileSync and eval contents...which scope have the functions? How to access?

I recently tried to import a file into my existing node.js project. I know this should be written with a module but i include my external javascript file like this:
eval(fs.readFileSync('public/templates/simple.js')+'')
The contents of simple.js looks like this:
if (typeof examples == 'undefined') { var examples = {}; }
if (typeof examples.simple == 'undefined') { examples.simple = {}; }
examples.simple.helloWorld = function(opt_data, opt_sb) {
var output = opt_sb || new soy.StringBuilder();
output.append('Hello world!');
return opt_sb ? '' : output.toString();
};
(Yes, google closure templates).
I can now call the template file using:
examples.simple.helloWorld();
Everything is working like expected. However I'm not able to figure out what the scope of these functions is and where I could possibly access the examples object.
Everything is running in a node.js 0.8 server and like I said its working...I just dont quite know why?
Thanks for clarification.
eval() puts variables into the local scope of the place where you called it.
It's as if the eval() was replaced by the code in the string argument.
I suggest to change the content of the files to:
(function() {
...
return examples;
})();
That way, you can say:
var result = eval(file);
and it will be obvious where everything is/ends up.
Note: eval() is a huge security risk; make sure you read only from trusted sources.

Suggestions for dealing with `exports` in node.js

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 );

Categories