Mimosa 2.1.4, require.js support improvements, thx r.js + esprima
Mimosa v2.1.4
brings with it something I've procrastinaed on for awhile thinking it would be much harder than it was. v2.1.4
includes mimosa-require v2.0.0
which vastly improves how that module determines your JavaScript code's define
and require
dependencies/config.
How does it improve it?
Backstory
The functionality that is mimosa-require's require.js support was one of a few key reasons I built Mimosa in the first place (starting almost 2 years ago!). Brunch didn't support AMD/require. Grunt was young, hard to configure (still is) and...just no. And there really wasn't anything else.
I wanted something that would:
- validate dependency paths
- validate requirejs.config.paths
- map paths, shim and shim dependency paths, etc
- let me know a path is bad the second it is bad
This validation alone is a huge time saver. And it really comes in handy when reorganizing your codebase. Move folderX to folderY then work your way through all the pathing errors mimosa-require informs you of and you are set.
I also wanted something that could determine the "main" require.js files, build out a r.js config, run it and output the results. Ideally it would do this without a single line of config, just by being smart about how require.js works and by programmitically learning things about the code as it was processed.
But the way mimosa-require was determining your dependencies and requirejs config was, well, it was evil.
The Hack
mimosa-require used eval
.
And I feel shame.
Rather than try and tackle any sort of complicated parsing or regex, I decided, maybe correctly at the time, that a better, quicker way to get the information mimosa-require needed was to eval
your JavaScript code.
mimosa-require defined in-scope versions of define
, require
and requirejs
, then eval
ed your JavaScript. The local version of, for instance, define
would capture the dependency array and off it would go. It would even treat the callback function as a string and search for embedded require
calls in the event you were using require.js' commonjs wrapper.
The fact that eval
was there never caused problems in and of itself, but treating the code that way would.
Everything would work great if you all your code was wrapped in define
and require
, and it would work great if any code outside those function calls didn't attempt to access some scope that mimosa-require's server-side eval
wouldn't know anything about.
But, if your code looked like this? Trouble.
var uA = window.navigator.userAgent;
define(["a","b"], function(a, b) {
...
});
mimosa-require would define a window
, just to cover a lot of global variable cases, but window.navigator
would not exist in-scope. So window.navigator.userAgent
would throw an error and mimosa-require would be unable to do anything with this file.
Bummer.
It would be an even bigger bummer if this file was one of your "main" files. mimosa-require would now not know it was a main file and it would not build the combined file.
Use Esprima
I've had "require + esprima" on my ToDo list for a very long time, but I knew it would be a huge effort. So much to parse, so many edge cases. Ugh.
Then an issue was logged against mimosa-require that had this problem at its root and I finally decided to look into it.
While I love writing esprima AST parsing code -- for reals, its legit awesome to write -- I was not going to love writing this.
r.js to the Rescue
I remember shortly after I released Mimosa and mimosa-require that James Burke the creator of require.js switched how require.js itself parses code to use Esprima. Certainly the tool mimosa-require is trying to help you use -- and that has to do all the things that mimosa-require does, just not interactively -- can provide some guidance.
An API for this maybe? No such luck. And why would there be.
But there is a parse.js sitting in the r.js repo. And it does have a parse
function. And it does have a findConfig
function.
Well, that's awesome.
It's not exposed, and it doesn't quite do what I need it to, but some copy paste, some minor hacking and day after I started I had ripped out the eval
BS and replaced it with r.js' legit esprima parsing.
Ugh
Seriously. That's all it took. Copy, paste, 75 lines of code changed/removed. I let the evil fester for an exceptionally long time because I knew it would be 1000+ lines of code to do it properly and I had a lot of other higher priorities. For whatever reason it didn't occur to me that those 1000+ lines of code already existed.
Win
So now there are far fewer caveats to the require.js support. mimosa-require won't run your code and it won't trip over anything. It's using the same parsing mechanism r.js uses, so all should be right.
So try it out.