Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
Reconcile OpenAjax Metadata for use with AMD/Module specs

The Problem

The current (Nov 2011) Dojo OAM files in Maqetta are written such that the require attribute conflates the main library file with other dependencies, while using direct calls to dojo.require for the remainder of the dependencies. This is an issue now that we are trying to make use of AMD loading in Dojo. We need to OAM files to better support Dojo's AMD loader; but I'd like to also ensure that the metadata stays as generic as possible and doesn't bring in implementation details.

Library Element

The OpenAjax Metadata spec mentions the following, under the <library> section:

The 'src' attribute specifies the absolute or relative URI for the main JavaScript file or the base directory for the given Ajax library.

Whereas we currently point <library> to the library base directory and include the main JS file as a require, we can instead point <library> to the main JS file, thereby dividing it from the rest of the dependencies.

Then, any JS <require> elements can be used in the module loader dependencies array, when the OAM file is parsed.

Module JS vs. Plain JS

There's still an issue with how OAM can differentiate between JS files that are coded as modules (AMD or CommonJS/Modules) or are just "plain" JS files. Each type is loaded differently, so the distinction is significant.

One approach would be to add a module attribute to <require>, whose value is a simple module spec identifier (i.e "amd", "commonjs"):

		<require type="javascript" src="../dijit/form/Button.js" module="amd"/>

The idea is that an OAM parser that understands the module attribute could take the src and transform it to a module id as necessitated by the module loader (i.e. "dijit/form/Button" from the example above). An OAM parser that did not check for module could still load the resource at the given src the regular way (adding a <script> tag or using XHR).

The problem with that is that module loaders don't necessarily work that way. A user can configure the module loader with "module paths", where the first segment of a module id can point to an arbitrary path on the system. So while a JS file may live at the provided src, the module id might be something different and there might not be a clean transform from src to the module id.

Also, we don't really know if AMD module files can be loaded in <script> elements one after the other. There may be issues with ordering and timing in this case. After all, the point of an AMD loader is to circumvent issues with loading in <script> tags.

New Require Type

Therefore, in order to alleviate the issues mentioned above, the best option may be to create an entirely new type for the <require> elements:

		<require type="javascript-module" format="amd" src="dijit/form/Button"/>

For backwards compatibility, an OAM parser that didn't know about the javascript-module type would ignore it. Unfortunately, that would basically render any such OAM file unusable, since the dependencies for the widget would not get loaded.

In order to distinguish between different module loader specs, we also added a format attribute, whose values may be "amd" or "commonjs", ...

The AMD spec only specifies a define global function, but reserves the global require for use by AMD loaders. Although, they are free to ignore the latter.

So a correct AMD implementation of Heading_oam.json would be:

<script src="../dojo/dojo.js"></script>
<script>
    define(function (require) {
        require([
            "dojox/mobile",
            "dojox/mobile/parser",
            "dojox/mobile/deviceTheme",
            "dojox/mobile/compat"
        ], function(mobile, parser, deviceTheme, compat) {
            // ...
        });
    });
</script>

The Dojo loader does support the global require, so the above could be written thusly:

<script src="../dojo/dojo.js"></script>
<script>
    require([
        "dojox/mobile",
        "dojox/mobile/parser",
        "dojox/mobile/deviceTheme",
        "dojox/mobile/compat"
    ], function(mobile, parser, deviceTheme, compat) {
        // ...
    });
</script>

CommonJS/Modules implementation isn't too different from AMD, just more straightforward.

var mobile = require("dojox/mobile");
var parser = require("dojox/mobile/parser");
var deviceTheme = require("dojox/mobile/deviceTheme");
var compat = require("dojox/mobile/compat");

// ... code ...
{
"id": "http://dojotoolkit.org/dojox/mobile/Heading",
"name": "dojox.mobile.Heading",
"spec": "1.0",
"version": "1.7",
"require": [
{
"type": "javascript-module",
"format": "amd",
"src": "dojox/mobile",
"$library": "dojo"
},
{
"type": "javascript-module",
"format": "amd",
"src": "dojox/mobile/parser",
"$library": "dojo"
},
{
"type": "javascript-module",
"format": "amd",
"src": "dojox/mobile/deviceTheme",
"$library": "dojo"
},
{
"type": "javascript-module",
"format": "amd",
"src": "dojox/mobile/compat",
"$library": "dojo"
}
],
"library": {
"dojo": {
"src": "../../../dojo/dojo.js"
}
},
"property": {
"back": {
"datatype": "string"
},
"href": {
"datatype": "string"
},
"moveTo": {
"datatype": "string"
},
"transition": {
"datatype": "string",
"defaultValue": "slide"
},
"label": {
"datatype": "string",
"defaultValue": "Heading"
},
"iconBase": {
"datatype": "string"
},
"fixed": {
"datatype": "string",
"defaultValue": "",
"option": [
{
"value": ""
},
{
"value": "top"
},
{
"value": "bottom"
}
]
}
},
"content": "<h1 label='Heading'></h1>"
}
<widget id="http://dojotoolkit.org/dojox/mobile/Heading"
name="dojox.mobile.Heading"
spec="1.0"
version="1.7">
<library name="dojo" src="../../../dojo/dojo.js">
<require type="javascript-module" format="amd" src="dojox/mobile"/>
<require type="javascript-module" format="amd" src="dojox/mobile/parser"/>
<require type="javascript-module" format="amd" src="dojox/mobile/deviceTheme"/>
<require type="javascript-module" format="amd" src="dojox/mobile/compat"/>
</library>
<properties>
<property name="back" datatype="string"/>
<property name="href" datatype="string"/>
<property name="moveTo" datatype="string"/>
<property name="transition" datatype="string" defaultValue="slide"/>
<property name="label" datatype="string" defaultValue="Heading"/>
<property name="iconBase" datatype="string"/>
<property name="fixed" datatype="string" defaultValue="">
<option value=""/>
<option value="top"/>
<option value="bottom"/>
</property>
</properties>
<content>
<![CDATA[
<h1 label='Heading'></h1>
]]>
</content>
</widget>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment