My JavaScript book is out! Don't miss the opportunity to upgrade your beginner or average dev skills.

Sunday, February 10, 2013

JavaScript Modules, Maybe

So, you might know already, but ES guys are talking these days about modules and things, as usual, went out of control since everyone wants its own best module version, ever!

Current Status

Synchronous, asynchronous, AMD, require() ... apparently these are all right for some use case, but wrong for some other.
It looks like JS cannot do synchronous modules ... wait what?
why browsers can't since browser have synchronous require since the very beginning? <script> tag anybody?
This was me after reading few times JS cannot do sync. Turned out, sync script is in HTML specs, not JS one, but wasn't this about JS indeed, where JS on browser never had this real problem and is simply envious of node.js module loader simplicity?

How About Facing Reality

... where in every language, requiring dependencies has always been synchronous because nobody ever cared about that latency, right? And did anyone even bothered using asynchronous file reading to include modules?
Not even node.js does that, the most async-centric env I personally know!
As summary, since this is a browser only problem, what I would expect is the ability from browsers engine to pause in a non blocking way the client until the file has been loaded. You know what I mean? F# does that so that's not an impossible reality ...
How cool would that be and how "free from browsers limits" the specification of the next module loader in a programming language would be?
The answer seems to be that JavaScript is not an HTML/W3C matter but is limited because of HTML/W3C implementors, those browsers ...

It Doesn't Matter, Had Module!

Developers are concerned that TC39 might not have real use cases and the funny part is that an evangelist of everything you know about the web as @paul_irish is had his self some concern about TC39 choices in term of real-world cases.
Meanwhile, AMD does not seem to be an answer, neither the preferred choice, but regardless we have these scenarios:
  • those who write for the web and go AMD
  • those who write for node.js and go require
  • those who will probably add all this crap regardless, and with all due respect for the dev who wrote that with best intentions, even if testing only on web or only node.js (and again, the point is not about the snippet but the fact it should be everywhere in the JS world, you got what I mean, I am sure!)
In few words, Domenic's suspects are already a reality: nobody is even caring/following about what's going on in this discussion!
Lazy developers will simply realize at some point nothing works anymore and will go strike blaming the corrupted system, the conspiracy agains the World Wide Web, the fact nobody told them it was going to disappear or change even after 5 years of warnings about deprecations in console, etc etc ... right?
Wrong, they'll just use what worked for them 'till that day without problems and they will still think that you should not break what's in already, which is one of my favorite parts about ES5, the best update ever, if only every browser was there already, it would be a better JS world for everyone, isn't it?

Decoupling Import From Loading

How insane would that be? A semantic syntax that works in any platform, no matter how the platform loads stuff, the build process behind, or the fact you might wrap this way and do what you think is best, even improving upfront your static analysis ... right?
So here a beta repository called remodel, something you might want to git clone git://github.com/WebReflection/remodule.git to run eventually node node_modules/wru/node/program.js test/remodule.js and see that all tests are passing already.

Wait, What

So that project is about having import like syntax available even in ES3
// ES.next modules syntax
import {a} from "something"

// remodule
imports("a").from("something");
You are following, right?
// ES.next modules export
module.exports.a = "whatever";

// remodule
modules("something", {
  a: "whatever"
});
So, kinda yes, the missing part of all this mess is that a module should be able to register itself if we would like to static analyze it and make the logic work everywhere in both sync and async environments, right?
// ES.next modules export
module.exports = {
  what: "ever"
};

// remodule
modules("exports", {
  what: "ever"
});

// remodule backward compatible
modules("exports", module.exports = {
  what: "ever"
});
Latter example is about loading that file with current require or without it ... unfortunately modules function should be there but that's easy to fix, right?

What Else

modules("test", {
  a: "this is a",
  b: "this is b",
  c: "this is c"
});
With above code, we might be able to export the test module, regardless the position in the filesystem or the package manager, and do funny things such:
var test = imports('*').from('test');
JSON.stringify(test);
// {"a":"this is a","b":"this is b","c":"this is c"}
Cool? we just imported whatever the module exported itself ... there's really nothing to worry about, as @benvie might spot out, the this is safe too, is the exported object.
var a = imports('a').from('test');
a; // "this is a"
Well, it's straight forward to get that we can import just one thing from a module, right? Behind the scene, the module must be imported once, and never again, but we can grab a property as needed instead of the whole thing, right? ... and more!
var arr = imports('a', 'c').from('test');
// same as
var arr = imports(['a', 'c']).from('test');

// same as, once Array comprehension is in ...
var [a, b] = imports('a', 'c').from('test');
We can specify multiple properties out of a single module ... cool, uh? And more!
var aliased = imports({
  a: 'A',
  c: 'b'
}).from('test');

// in a real world scenario ...
var $ = imports({
  jQuery: '$'
}).from('jquery').$;
Yep, aliases are there too, so that you can actually test everything for real in this file.

So ...

what I've done there, is not even in charge of loading synchronously or asynchronously anything ... I mean, that should be your build process in charge of making things work and instantly available once needed, right? I mean, the Web/DOM part, I get it, modal spinners all over instead of a frozen tab so annoying for the user, but why nobody simply came out with a library in charge of this? Why ES6 modules will break browsers, using all those reserved words unusable in older browsers, will break node.js logic, being incompatible with the exported module, and will probably be never adopted in fact by anybody?
I am still dreaming about web improvements where stuff that gets out is what's really needed and most likely already used out there. Improved? Better? Sure! Pointless? No, thank you!

1 comment:

Adrien Risser said...

I too like how Node.js does not introduce yet another keyword and keeps it simple with a plain function, "require".
Your way is the way to go, like "let's improve JavaScript by using, well, JavaScript!"
Likewise, I do not fancy new syntaxic diabetic operators like arrows or whatnot.
Cheers!