Adding in support for require/require.def syntax as used by RequireJS. Very basic support, nothing fancy like plugins or require.modify
authorJames Burke <jrburke@mozillamessaging.com>
Wed, 19 May 2010 12:43:57 -0700
changeset 594 7c4c4060858a
parent 593 21537f905bf6
child 679 c1ce1908df8d
push id1
push userjrburke@gmail.com
push date2010-05-19 19:44 +0000
Adding in support for require/require.def syntax as used by RequireJS. Very basic support, nothing fancy like plugins or require.modify
packages/jetpack-core/lib/securable-module.js
packages/me/README.md
packages/me/lib/main.js
packages/me/lib/my-module-cjs.js
packages/me/lib/my-module.js
packages/me/package.json
packages/me/tests/test-my-module.js
--- a/packages/jetpack-core/lib/securable-module.js
+++ b/packages/jetpack-core/lib/securable-module.js
@@ -185,53 +185,166 @@
 
      this.fs = options.fs;
      this.sandboxFactory = options.sandboxFactory;
      this.sandboxes = {};
      this.modules = options.modules;
      this.globals = options.globals;
    };
 
+    //START Patch to support RequireJS-style
+    var ostring = Object.prototype.toString;
+    function isFunction(it) {
+        return ostring.call(it) === "[object Function]";
+    }
+    function isArray(it) {
+        return ostring.call(it) === "[object Array]";
+    }
+    //END Patch to support RequireJS-style
+
    exports.Loader.prototype = {
      _makeRequire: function _makeRequire(rootDir) {
        var self = this;
 
-       return function require(module) {
+       //Patch to support RequireJS-style removed the return from next line
+       function require(module) {
          var path = self.fs.resolveModule(rootDir, module);
          if (!path)
            throw new Error('Module "' + module + '" not found');
          if (!(path in self.modules)) {
            var options = self.fs.getFile(path);
            if (options.filename === undefined)
              options.filename = path;
 
            var exports = {};
            var sandbox = self.sandboxFactory.createSandbox(options);
            self.sandboxes[path] = sandbox;
            for (name in self.globals)
              sandbox.defineProperty(name, self.globals[name]);
            sandbox.defineProperty('require', self._makeRequire(path));
            sandbox.evaluate("var exports = {};");
-           self.modules[path] = sandbox.getProperty("exports");
+           var exported = sandbox.getProperty("exports");
            sandbox.evaluate(options);
+
+           //If a require.def call did not define the module, assume
+           //exports is in play.
+           if (!self.modules[path]) {
+             self.modules[path] = exported;
+           }
          }
          return self.modules[path];
        };
-     },
 
-     findSandboxForModule: function findSandboxForModule(module) {
-       var path = this.fs.resolveModule(null, module);
-       if (!path)
-         throw new Error('Module "' + module + '" not found');
-       if (!(path in this.sandboxes))
-         this.require(module);
-       if (!(path in this.sandboxes))
-         throw new Error('Internal error: path not in sandboxes: ' +
-                         path);
-       return this.sandboxes[path];
+       //START Patch to support RequireJS-style require and require.def calls.
+       //It basically just allows the callback style used in RequireJS,
+       //it DOES NOT support the following from RequireJS:
+       //contexts, config, plugins, require.modify, page load support.
+
+       /**
+        * Main entry point.
+        *
+        * If the only argument to require is a string, then the module that
+        * is represented by that string is fetched for the appropriate context.
+        *
+        * If the first argument is an array, then it will be treated as an array
+        * of dependency string names to fetch. An optional function callback can
+        * be specified to execute when all of those dependencies are available.
+        */
+       function requirejs(deps, callback) {
+         if (typeof deps === "string" && !isFunction(callback)) {
+           //Just return the module wanted. In this scenario, the
+           //second arg (if passed) is just the contextName.
+           return require(deps);
+         }
+
+         //Do more work, let the def function handle it.
+         return requirejs.def.apply(require, arguments);
+       }
+
+        /**
+         * The function that handles definitions of modules. Differs from
+         * require() in that a string for the module should be the first argument,
+         * and the function to execute after dependencies are loaded should
+         * return a value to define the module corresponding to the first argument's
+         * name.
+         */
+        requirejs.def = function (name, deps, callback) {
+
+            //Normalize the arguments.
+            if (typeof name === "string") {
+                //Check if there are no dependencies, and adjust args.
+                if (!isArray(deps)) {
+                    callback = deps;
+                    deps = [];
+                }
+            } else if (isArray(name)) {
+                //Just some code that has dependencies. Adjust args accordingly.
+                callback = deps;
+                deps = name;
+                name = null;
+            } else if (isFunction(name)) {
+                //Just a function that does not define a module and
+                //does not have dependencies. Useful if just want to wait
+                //for whatever modules are in flight and execute some code after
+                //those modules load.
+                callback = name;
+                name = null;
+                deps = [];
+            }
+
+            //Set up the path if we have a name
+            if (name) {
+                var namePath = self.fs.resolveModule(rootDir, name);
+            }
+
+            //If the callback is not an actual function, it means it already
+            //has the definition of the module as a literal value.
+            if (name && callback && !isFunction(callback) && !self.modules[namePath]) {
+                self.modules[namePath] = callback;
+                return requirejs;
+            }
+
+            //Load all the dependencies.
+            var depModules = [], exports = {}, usesExports = false, exported;
+            deps.forEach(function (dep) {
+                if (dep === "require") {
+                    depModules.push(requirejs);
+                } else if (dep === "module") {
+                    depModules.push({
+                        id: name
+                    });
+                } else if (dep === "exports") {
+                    usesExports = true;
+                    depModules.push(exports);
+                } else {
+                    var depPath = self.fs.resolveModule(rootDir, dep);
+
+                    if (!self.modules[depPath]) {
+                        require(dep);
+                    }
+                    depModules.push(self.modules[depPath]);
+                }
+            });
+
+            //Execute the function.
+            if (callback) {
+                exported = callback.apply(null, depModules);
+            }
+
+            //Assign output of function to name, if exports is not in play.
+            if (name) {
+                self.modules[namePath] = usesExports ? exports : exported;
+            }
+
+            return requirejs;
+        };
+
+
+       return requirejs;
+       //END Patch to support RequireJS-style
      },
 
      require: function require(module) {
        return (this._makeRequire(null))(module);
      },
 
      runScript: function runScript(options, extraOutput) {
        if (typeof(options) == 'string')
new file mode 100644
--- /dev/null
+++ b/packages/me/README.md
@@ -0,0 +1,6 @@
+This is my *first* package. It contains:
+
+* A tiny module.
+* A tiny test suite.
+* Some meager documentation.
+
new file mode 100644
--- /dev/null
+++ b/packages/me/lib/main.js
@@ -0,0 +1,35 @@
+// The `main` function is called by the host application that enables
+// this module. In this example, `options` and `callbacks` are not
+// used.
+require.def("main", ["context-menu", "my-module", "my-module-cjs"], function(contextMenu, mod, modcjs) {
+
+    return {
+        main: function (options, callbacks) {
+            console.log('test one: ' + mod.add(5, 3));
+            console.log('test two: ' + modcjs.add(3, 5));
+
+            // Create a new context menu item.
+            var menuItem = contextMenu.Item({
+
+                label: "Search with Google",
+
+                // A CSS selector. Matching on this selector triggers the
+                // display of our context menu.
+                context: "a[href]",
+
+                // When the context menu item is clicked, perform a Google
+                // search for the link text.
+                onClick: function (contextObj, item) {
+                    var anchor = contextObj.node;
+                    console.log("searching for " + anchor.textContent);
+                    var searchUrl = "http://www.google.com/search?q=" +
+                                    anchor.textContent;
+                    contextObj.window.location.href = searchUrl;
+                }
+            });
+
+            // Add the new menu item to the application's context menu.
+            contextMenu.add(menuItem);
+        }
+    };    
+});
new file mode 100644
--- /dev/null
+++ b/packages/me/lib/my-module-cjs.js
@@ -0,0 +1,3 @@
+exports.add = function (a, b) {
+    return a + b;
+};
new file mode 100644
--- /dev/null
+++ b/packages/me/lib/my-module.js
@@ -0,0 +1,7 @@
+require.def("my-module", function () {
+    return {
+        add: function (a, b) {
+            return a + b;
+        }
+    };
+});
new file mode 100644
--- /dev/null
+++ b/packages/me/package.json
@@ -0,0 +1,5 @@
+{
+  "description": "This is my first package, it's tiny.",
+  "author": "Me (http://me.org)"
+}
+
new file mode 100644
--- /dev/null
+++ b/packages/me/tests/test-my-module.js
@@ -0,0 +1,6 @@
+var myModule = require("my-module");
+
+exports.ensureAdditionWorks = function(test) {
+  test.assertEqual(myModule.add(1, 1), 2, "1 + 1 = 2");
+};
+