1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/toolkit/devtools/Require.jsm Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,195 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +"use strict"; 1.9 + 1.10 +/** 1.11 + * Require.jsm is a small module loader that loads JavaScript modules as 1.12 + * defined by AMD/RequireJS and CommonJS, or specifically as used by: 1.13 + * GCLI, Orion, Firebug, CCDump, NetPanel/HTTPMonitor and others. 1.14 + * 1.15 + * To date, no attempt has been made to ensure that Require.jsm closely follows 1.16 + * either the AMD or CommonJS specs. It is hoped that a more formal JavaScript 1.17 + * module standard will arrive before this is necessary. In the mean time it 1.18 + * serves the projects it loads. 1.19 + */ 1.20 + 1.21 +this.EXPORTED_SYMBOLS = [ "define", "require" ]; 1.22 + 1.23 +const console = (function() { 1.24 + const tempScope = {}; 1.25 + Components.utils.import("resource://gre/modules/devtools/Console.jsm", tempScope); 1.26 + return tempScope.console; 1.27 +})(); 1.28 + 1.29 +/** 1.30 + * Define a module along with a payload. 1.31 + * @param moduleName Name for the payload 1.32 + * @param deps Ignored. For compatibility with CommonJS AMD Spec 1.33 + * @param payload Function with (require, exports, module) params 1.34 + */ 1.35 +this.define = function define(moduleName, deps, payload) { 1.36 + if (typeof moduleName != "string") { 1.37 + throw new Error("Error: Module name is not a string"); 1.38 + } 1.39 + 1.40 + if (arguments.length == 2) { 1.41 + payload = deps; 1.42 + } 1.43 + else { 1.44 + payload.deps = deps; 1.45 + } 1.46 + 1.47 + if (define.debugDependencies) { 1.48 + console.log("define: " + moduleName + " -> " + payload.toString() 1.49 + .slice(0, 40).replace(/\n/, '\\n').replace(/\r/, '\\r') + "..."); 1.50 + } 1.51 + 1.52 + if (moduleName in define.modules) { 1.53 + throw new Error("Error: Redefining module: " + moduleName); 1.54 + } 1.55 + 1.56 + // Mark the payload so we know we need to call it to get the real module 1.57 + payload.__uncompiled = true; 1.58 + define.modules[moduleName] = payload; 1.59 +} 1.60 + 1.61 +/** 1.62 + * The global store of un-instantiated modules 1.63 + */ 1.64 +define.modules = {}; 1.65 + 1.66 +/** 1.67 + * Should we console.log on module definition/instantiation/requirement? 1.68 + */ 1.69 +define.debugDependencies = false; 1.70 + 1.71 + 1.72 +/** 1.73 + * We invoke require() in the context of a Domain so we can have multiple 1.74 + * sets of modules running separate from each other. 1.75 + * This contrasts with JSMs which are singletons, Domains allows us to 1.76 + * optionally load a CommonJS module twice with separate data each time. 1.77 + * Perhaps you want 2 command lines with a different set of commands in each, 1.78 + * for example. 1.79 + */ 1.80 +function Domain() { 1.81 + this.modules = {}; 1.82 + 1.83 + if (define.debugDependencies) { 1.84 + this.depth = ""; 1.85 + } 1.86 +} 1.87 + 1.88 +/** 1.89 + * Lookup module names and resolve them by calling the definition function if 1.90 + * needed. 1.91 + * There are 2 ways to call this, either with an array of dependencies and a 1.92 + * callback to call when the dependencies are found (which can happen 1.93 + * asynchronously in an in-page context) or with a single string an no 1.94 + * callback where the dependency is resolved synchronously and returned. 1.95 + * The API is designed to be compatible with the CommonJS AMD spec and 1.96 + * RequireJS. 1.97 + * @param deps A name, or array of names for the payload 1.98 + * @param callback Function to call when the dependencies are resolved 1.99 + * @return The module required or undefined for array/callback method 1.100 + */ 1.101 +Domain.prototype.require = function(config, deps, callback) { 1.102 + if (arguments.length <= 2) { 1.103 + callback = deps; 1.104 + deps = config; 1.105 + config = undefined; 1.106 + } 1.107 + 1.108 + if (Array.isArray(deps)) { 1.109 + var params = deps.map(function(dep) { 1.110 + return this.lookup(dep); 1.111 + }, this); 1.112 + if (callback) { 1.113 + callback.apply(null, params); 1.114 + } 1.115 + return undefined; 1.116 + } 1.117 + else { 1.118 + return this.lookup(deps); 1.119 + } 1.120 +}; 1.121 + 1.122 +/** 1.123 + * Lookup module names and resolve them by calling the definition function if 1.124 + * needed. 1.125 + * @param moduleName A name for the payload to lookup 1.126 + * @return The module specified by aModuleName or null if not found 1.127 + */ 1.128 +Domain.prototype.lookup = function(moduleName) { 1.129 + if (moduleName in this.modules) { 1.130 + var module = this.modules[moduleName]; 1.131 + if (define.debugDependencies) { 1.132 + console.log(this.depth + " Using module: " + moduleName); 1.133 + } 1.134 + return module; 1.135 + } 1.136 + 1.137 + if (!(moduleName in define.modules)) { 1.138 + throw new Error("Missing module: " + moduleName); 1.139 + } 1.140 + 1.141 + var module = define.modules[moduleName]; 1.142 + 1.143 + if (define.debugDependencies) { 1.144 + console.log(this.depth + " Compiling module: " + moduleName); 1.145 + } 1.146 + 1.147 + if (module.__uncompiled) { 1.148 + if (define.debugDependencies) { 1.149 + this.depth += "."; 1.150 + } 1.151 + 1.152 + var exports = {}; 1.153 + try { 1.154 + var params = module.deps.map(function(dep) { 1.155 + if (dep === "require") { 1.156 + return this.require.bind(this); 1.157 + } 1.158 + if (dep === "exports") { 1.159 + return exports; 1.160 + } 1.161 + if (dep === "module") { 1.162 + return { id: moduleName, uri: "" }; 1.163 + } 1.164 + return this.lookup(dep); 1.165 + }.bind(this)); 1.166 + 1.167 + var reply = module.apply(null, params); 1.168 + module = (reply !== undefined) ? reply : exports; 1.169 + } 1.170 + catch (ex) { 1.171 + dump("Error using module '" + moduleName + "' - " + ex + "\n"); 1.172 + throw ex; 1.173 + } 1.174 + 1.175 + if (define.debugDependencies) { 1.176 + this.depth = this.depth.slice(0, -1); 1.177 + } 1.178 + } 1.179 + 1.180 + // cache the resulting module object for next time 1.181 + this.modules[moduleName] = module; 1.182 + 1.183 + return module; 1.184 +}; 1.185 + 1.186 +/** 1.187 + * Expose the Domain constructor and a global domain (on the define function 1.188 + * to avoid exporting more than we need. This is a common pattern with 1.189 + * require systems) 1.190 + */ 1.191 +define.Domain = Domain; 1.192 +define.globalDomain = new Domain(); 1.193 + 1.194 +/** 1.195 + * Expose a default require function which is the require of the global 1.196 + * sandbox to make it easy to use. 1.197 + */ 1.198 +this.require = define.globalDomain.require.bind(define.globalDomain);