My JavaScript book is out! Don't miss the opportunity to upgrade your beginner or average dev skills.
Showing posts with label CommonJS. Show all posts
Showing posts with label CommonJS. Show all posts

Sunday, January 15, 2012

Y U NO use libraries and add stuff

This is an early introduction to a project I have been thinking about for a while.
The project is already usable in github but the documentation is lacking all over the place so please be patient and I'll add everything necessary to understand and use yuno.

Zero Stress Namespace And Dependencies Resolver

Let's face the reality: today there is still no standard way to include dependencies in a script.
If we are using a generic JS loader, the aim is to simply download files and eventually wait for one or more dependency in order to be able to use everything we need.
The require logic introduced via node.js does not scale in the browser due synchronous nature of the method itself plus the sandbox not that easy to emulate in a browser environment.
The AMD concept is kinda OKish but once we load after dependencies, there is no way to implement a new one within the callback unless we are not exporting.
I find AMD approach surely the most convenient but still not the best one:
  1. we cannot implement a provide like procedure, whenever this could be handy or not
  2. it's not clear within the module code itself, what we are exporting exactly

Specially the last point means that AMD does not scale properly with already combined code because AMD relies in the module/folder structure itself ... so, cannot we do anything better than what we have so far?

The yuno Concept


Directly out of a well known meme, yuno logic is quite straightforward:
  • automagically resolved path, you point once to yuno.js file in your page and you are ready to go
  • compatible with already combined files (smart builder coming soon)
  • yuno.use() semantic method to define dependencies, if necessary
  • yuno.use().and() resolved callback to receive modules AMD style once everything has been loaded
  • yuno.add() standard ES5 way to define new namespaces, objects, properties, or constructors ( so no extra note in the documentation is needed )
  • cross referenced dependencies automagically resolved: if two different scripts needs same library, this will be loaded once for both
  • external url compatible, because you may want to include a file from some known CDN rather than put all scripts in your own host ( speed up common libraries download across different libraries that depend on same core, e.g/ jQuery )
  • modules, namespaces, or global objects, cannot be reassigned twice, which means if we are adding twice same thing we are doing it wrong, but if we are not aware of other script that added same thing before we have a notification
  • something else I may decide to add after this post
Here some example:

// define a jQuery plugin
yuno.use(
"jQuery",
"extraStuff"
).and(function (jQuery, extraStuff) {
yuno.add(jQuery.fn, "myPlugin", {value:function () {
// your amazing code here
}});
// we may opt for just this line
jQuery.fn.myPlugin = function () {};
// in order to export our plugin
// however, the purpose of yuno is to have
// a common recognizable way to understand
// what the module is about
// plus the "add" method is safer
});

Let's imagine that extraStuff contains similar code:

// define extraStuff
yuno.use(
"jQuery"
).and(function (jQuery) {
yuno.add(jQuery.fn, "extraStuff", {value:function () {
// your amazing code here
}});
});

Both plugin and extraStuff needs jQuery to be executed ... will jQuery be loaded twice? Nope, it's simply part of a queue of modules that needs to be resolved.
As soon as it's loaded/added once, every module that depends on jQuery will be notified so that if the list of dependencies is fully loaded, the callback passed to and will be executed.

Y U NO Add

Modules are only one part of the proposal since we may define a script where no external dependency is needed.

// note: no external dependency, just add
yuno.add(this, "MyFreakingCoolConstructor", {value:
function MyFreakingCoolConstructor() {
// freaking cool stuff here
}
});
// this points to the global object so that ...
MyFreakingCoolConstructor.prototype.doStuff = function () {
// freaking cool method
};

The yuno.add method reflects Object.defineProperty which means for ES5 compatible browsers getters, setters, and values, are all accepted and flagged as not enumerable, not writable, and not configurable by default.
Of course we can re-define this behavior but most likely this is what we need/want as default in any case ... isn't it?
For those browsers not there yet, the Object.defineProperty method is partially shimmed where __defineGetter/Setter__ or simply the value property will be used instead.
Bear in mind this shim may change accordingly with real needs but so far all mobile browsers should work as expected plus all Desktop browsers except IE less than 9 ... not so common targets for modern web sites.
Last, but not least, yuno logic does not necessarily need the add call so feel free to simply define your global object or your namespace the way you want.
However, as I have said before, the add method is a simple call able to make things more robust, to speedup and ensure notifications, and to use a standard, recognizable pattern, to define our own objects/functions being sure nobody did before thanks to defaults descriptor behavior which is not writable and not configurable indeed.

To DOs

This is just an initial idea of what the yuno object is able to do but few things are in my mind. On top of the list we have the possibility to shortener CDN calls via prefixes such "cdn:jQuery", as example, in order to use most common CDNs to load widely shared libraries.
Last, but not least, the reason I am writing this is because I am personally not that happy with any solution we have out there so if you are willing to contribute, please just leave a comment, thanks.

Monday, March 08, 2010

CommonJS - A YAGNI Based "require"

I am very busy these days with my last (hopefully) moving into my new and completely empty rented flat ... and while I am building home utilities and preparing my last post about A Better JS Class, with some extra case where common libraries fail with their parent implementations, I would like to quickly share this require function I wrote days ago but just recently came back in front of my eyes.

Why require

In CommonJS we have different techniques and agreement about how developers should structure/organize their namespaces and libraries.
The first common function adopted from CommonJS "followers" is the require one. This function aim is to load once, and runtime, a namespace, allowing scripts loaded via this function to define exported properties/variables or methods/functions.

// file main.js
var $ = require("jquery").$;

// file jquery.js
// let's imagine jQuery library has this piece of code inside
if ( typeof exports != "undefined" ) {
// export the library as dollar function
exports.$ = jQuery;
}


The require Function


var require = (function (
global, // global scope object (not necessary window)
cache, // object with loaded namespace to avoid reloads
exports, // variable name (better compression)
ActiveXObject // property name (better compression)
) {
/*!WebReflection:MitStyle*/
function request(namespace) {
// create the xhr object, no need to optimize
// this check is nothing compared with the time
// required to load and evaluate the resource
var xhr = new global[global[ActiveXObject] ? ActiveXObject : "XMLHttpRequest"]("Microsoft.XMLHTTP");
xhr.open(
// method
"GET",
// path replaced
(require.root || "") + "/" + namespace.replace(/\./g, "/") + ".js",
// synchronous
false
);
// send request
xhr.send(null);
// assign the runtime created export object
// with the required namespace
return (cache[namespace] = Function(
exports,
// the text will be evaluated in a global function
// it can register exported variables/methods
// simply writing:
// export.$ = {};
// so that require("mylib").$; will always
// point to the correct property
xhr.responseText + ";return " + exports
// be sure the function is executed
// with a global context (the this reference)
).call(global, {}));
}
// if namespace has been loaded already
// the associated export object will be returned
// otherwise the precedent function will be
// executed
function require(namespace) {
return cache[namespace] || request(namespace);
}
// the exposed function
return require;
}(this, {}, "exports", "ActiveXObject"));

Above piece of code fit into about 350 bytes once minified, less than 260 bytes gzipped ... sweet, isn't it?
Bear in mind if we need a root, we can simply add it via require.root = "./my/js/path"; without last slash after the final folder (e.g. require.root="." to load from the current one).

YAGNI In Details

For those unable to understand the acronym, YAGNI simply means "Ya ain't gonna need it".
The YAGNI behind my proposal could be summarized in this way:
  • server side frameworks have their own native require, no need to fully replicate it, it's already there
  • simply Ajax, if the browser does not load via other protocols and you are testing, enable file: via about:confing/strict or the local support for XHR
  • the most used case is the one we all know, require function will be there, as global one, and module object is not yet important (CommonJS is constantly updated as well)
  • A well organized namespace will improbably affect object properties (e.g. Object.prototype["my.name.space"] does not make much sense). No need to use hasOwnProperty, specially not the object itself one, since Object.prototype.hasOwnProperty could be redefined without problems and most probably this is an edge case more dangerous than the prototype["my.long.namespace"]

All these points are better considered in another version I did not know, the one from David Flanagan.
In any case, we should consider this valid point of view about require and JavaScript client, from Lucas Smith.
At least now we have more alternatives in the field, with this one that should simply bite others at least for size, and for common case reliability.
Enjoy!

Monday, February 01, 2010

CommonJS - Why Server Side Only?

There is one single thing I don't like about CommonJS Idea, the fact nobody is thinking about the client side!!!

CommonJS Why

Since everybody would like to use JavaScript as Server Side Programing Language, where right now I can count there about 50 implementations, somebody decided that at least basic stuff such IO operations, streams or sockets, and much more, should have a common behavior, namespace, API, across all different implementations.
In few words, these guys are trying to create their own WSW Consortium, and this is absolutely OK.
So what am I complaining about?

Client CommonJS

If we, as developers and libraries authors, would have adopted a similar strategy ages ago, rather than fight with each other about natives prototypes pollutions, web development would be probably even easier for everybody.
We failed, while those server side guys started correctly.
The problem, for both language usage and its environment, is that JavaScript on server has different problems than a bloody "attachEvent VS addEventListener" but while common practices are in any case appreciated and widely adopted world wide, CommonJS is not friendly at all with Client Side JavaScript.
The basic example?

require

This function aim is to retrieve from a namespace, where it is basically represented via dot notation, translated into folders paths, a generic variable, object, function, whatever.
The power a dedicated build could have over ouw miserable secured/sandboxed version of browser JavaScript engines is endless.
As example, the only way I could think about to implement a client side require, is this one:

if (typeof require === "undefined") {
// (C) WebReflection - Mit Style License
var require = (function (context, root, Function) {
function require(namespace) {
if (!hasOwnProperty.call(cache, namespace)) {
var xhr = new XMLHttpRequest;
xhr.open("GET", (root + "." + namespace.replace(/(^.*)(?:\.[0-9A-Za-z$_]+)$/, "$1")).replace(/\./g, "/") + ".js", false);
xhr.send(null);
cache[namespace] = Function(xhr.responseText + ";return function(){return eval(arguments[0])};").call(context);
}
return /(?:^.*\.|^)([0-9A-Za-z$_]+)$/.test(namespace) && cache[namespace](RegExp.$1);
};
var XMLHttpRequest = this.XMLHttpRequest || function () {
return new ActiveXObject("Microsoft.XMLHTTP");
};
var cache = {};
var hasOwnProperty = cache.hasOwnProperty;
return require;
})(this, "", this.Function);
}


How It Works

Let's say we have a file called mylib.js and let's say this file contains our libraries variables.
To obtain the base object, we could simply do something like this:

<script src="require.js"></script>
<script>
var base = require("mylib.base");
base.alert("hello");

var $alert = require("mylib.base").alert;
$alert("world");
</script>

Where mylib.js file is nothing different from:

var base = (function () {
var self = this;
return {
alert:function (msg) {
self.alert(msg);
}
};
}).call(this);

Easy? In few words if a file contains proper variable declarations, rather than global myvar = {} without var prefix, we can imagine we could have all our libraries automatically "sandboxed", at least the global namespace won't be polluted that much and via my implementation of require, each file will be loaded simply once and never again, and we can retrieve step after step just what we need.

P.S. please note that my implementation is just an imperfect proof of concept since require in CommonJS accepts syntax like require("mylib").base; but untile we won't have runtime cross browser resolved get/set, this is not possible to implement synchronously :(