require(“patience.js”)

I’ve updated Naked JavaScript with a require method to make dragging in it (and all the jQuery dependencies) easier. I also gave NJS a dressing-down so it’s better able to inspect itself, and factored out the test code from the main library. Most of the work actually happened the week of the 16th. I got most of this written nearing on the next weekend, and then crashed.

The Javascript Dependency Mess

One of the growing pains of a Javascript project is that there is no good way to express dependencies. Earlier versions of NJS required a half dozen Javascript and CSS files in every HTML document that wanted to use it. A couple times while working on the Javascript parser I would have liked to just open a window and browse things, but I never got past the pain of sorting out all the extra files.

Even CSS has it’s @import directive, but Javascript still hasn’t grown up. Many JS libraries I’ve seen ship their production code as a single concatenated file, which has evidence of a parallel life as separate files for development.

The Escape Hatch

Fortunately, as documented in several places throughout the web, there does appear to be a hack in the wild that is reasonably effective. By the time the browser is executing Javascript, it has enough of a document object model going that you can add new script elements to the page (or link elements for CSS), and the browser will execute them just as if they had been there from the beginning.

A Fatal Flaw

Unfortunately, you can’t turn this into the kind of require/include mechanism familiar in other languages. The problem is that the new script won’t be executed until the current one finishes (at least – there might also be network delays). So, each file can’t name it’s own dependencies. You still have to have one file that lists everything, in order. The only improvement is that you can take that one file and re-use it in multiple pages, with a single script statement in each.

A possible work-around would be put the entire rest of the contents of the file in a function to be executed after the dependencies have loaded. Often times it’s in a function anyway to establish a private scope. However, this was too large an impact on the source files (and on every source file) for me to stomach, so I had to pass.

Creeping Featurism

The example routines for doing this were a mere half-dozen lines or so, but you needed one for Javascript and one for CSS. So the first thing I did was set it up to create the appropriate element type based on the mime type. From there it was a short extension to automatic detection based on extension.

Relatively Easy

I have a nasty set of opposing desires: I like small directories with few entries, so that I can actually find things, and I hate specifying long paths – especially over and over again. So next up was giving my require a directory context so that I didn’t have to repeat myself. In isolation, this worked fairly well.

Relatively Hard

Recall that my use case was to easily pull NakedJS into the parser project, hopefully without having to copy it’s great mess of files (addition to not having to list them all) Here’s the problem: script element paths are all relative to the base document. All those requires in the NJS manifest file would reference paths not in the parser file tree. I thought that the path scoping would solve this easily enough.

I was wrong. The manifest file (njs.js) is Javascript code. It executes when it’s loaded. In order to set up a path context, it has to be executed by Javascript code, using the require mechanism. Recall the part about new scripts not be executed until the current code stops. By the time the NJS code started running, the path stack had been unwound and evaporated. File not found.

Essentially, you can’t pass information to the script. Well, I suppose you use some kind of rendezvous on an object property, but not only does that sound messy, I just thought of it now and that didn’t do me any good a few days ago ;^) So your script is starting over nearly from scratch. Fortunately, since you can add script elements, you can also list the existing ones. Conveniently, these have their full path rather than the relative one they probably started with. So, for the price of having the manifest script search for itself, you can figure out the difference between it’s expected relative path and it’s actual full path, and then specify a full path for the new scripts.

Whats in a Name? -or- My Own Private DLL Hell

One of the purposes of implementing ‘require’ as opposed to ‘include’ was to catch multiple dependencies. That got mostly deflated when I had to use the single big manifest for each package, but there are still cases where it comes – such as when the parser imported Naked Javascript.

The current parser-test runs it’s test code immediately from the script file. Yes, this by itself probably needs to be fixed some time. Since the DOM isn’t fully up and running yet, the results of the tests are stored in an array until everything is ready, and then de-spooled into a div to make them visible. When I added NJS, this all disappeared. NJS was re-loading the debug module and re-initializing the stored messages to an empty array. Why? because “…/naked/script/cgd/debug.js” != “…/parse/script/cgd/debug.js”

Identity Crisis

So, what is the identity of a file? I’m not handling the files myself (just their paths) so content hashes are out. You could say it’s the filename (debug.js) but then why bother having directories at all? You also can’t have similar files in different subdirectories. If you use the full path, the names still don’t match and you load the file twice. Having two similar files still raises the version incompatibility problem, but at least doing it once prevents you from wiping out data. I’ve settled on the ‘canonical relative path’, in this case “script/cgd/debug.js” In short, I’ve come to have a certain appreciation for why Java adopted the “com.” convention.

Who’s Surprised?

As usual IE, breaks. It’s creating the script elements, but when one loads it’s missing a dependency. Perhaps they’re out of order? I’m going to have to spend some off-time at work to figure it out.

Edit: I just tried it in IE again, things worked fine (well, as fine as they used to…) perhaps the old and new versions were getting mixed up due to caching?

Naked Javascript Gets the Dressing Down

After getting require working (in Firefox, Safari, and Opera) I cleaned up Naked Javascript a bit. One of the things I realized after developing NJS is that a lot of my code wouldn’t show very much in. The obvious place to start is NJS itself. Most of it was already in one one ‘class’, so fortunately there were no drastic changes required.

There were a few other little improvements, such as printing something when the toString method returns a blank string.

Leaving Home

One of the larger transformations was splitting out the test code. Formally loading the Naked Javascript module opened it’s test dialogs. This code had to be refactored and put into it’s own file. The test data also got ‘stripped’ so that the test data and even jQuery extensions could be inspected. In some cases I had to make some exceptions for items that can’t be immediately initialized because they reference the DOM.

No Clothes Required

Of course, I had to go back and expose require itself for browsing.

Posted Friday, November 28th, 2008 under Devlog.

Comments are closed.