toolkit/components/workerloader/require.js

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/toolkit/components/workerloader/require.js	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,243 @@
     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 +
     1.9 +/**
    1.10 + * Implementation of a CommonJS module loader for workers.
    1.11 + *
    1.12 + * Use:
    1.13 + * // in the .js file loaded by the constructor of the worker
    1.14 + * importScripts("resource://gre/modules/workers/require.js");
    1.15 + * let module = require("resource://gre/modules/worker/myModule.js");
    1.16 + *
    1.17 + * // in myModule.js
    1.18 + * // Load dependencies
    1.19 + * let SimpleTest = require("resource://gre/modules/workers/SimpleTest.js");
    1.20 + * let Logger = require("resource://gre/modules/workers/Logger.js");
    1.21 + *
    1.22 + * // Define things that will not be exported
    1.23 + * let someValue = // ...
    1.24 + *
    1.25 + * // Export symbols
    1.26 + * exports.foo = // ...
    1.27 + * exports.bar = // ...
    1.28 + *
    1.29 + *
    1.30 + * Note #1:
    1.31 + * Properties |fileName| and |stack| of errors triggered from a module
    1.32 + * contain file names that do not correspond to human-readable module paths.
    1.33 + * Human readers should rather use properties |moduleName| and |moduleStack|.
    1.34 + *
    1.35 + * Note #2:
    1.36 + * The current version of |require()| only accepts absolute URIs.
    1.37 + *
    1.38 + * Note #3:
    1.39 + * By opposition to some other module loader implementations, this module
    1.40 + * loader does not enforce separation of global objects. Consequently, if
    1.41 + * a module modifies a global object (e.g. |String.prototype|), all other
    1.42 + * modules in the same worker may be affected.
    1.43 + */
    1.44 +
    1.45 +
    1.46 +(function(exports) {
    1.47 +  "use strict";
    1.48 +
    1.49 +  if (exports.require) {
    1.50 +    // Avoid double-imports
    1.51 +    return;
    1.52 +  }
    1.53 +
    1.54 +  // Simple implementation of |require|
    1.55 +  let require = (function() {
    1.56 +
    1.57 +    /**
    1.58 +     * Mapping from module paths to module exports.
    1.59 +     *
    1.60 +     * @keys {string} The absolute path to a module.
    1.61 +     * @values {object} The |exports| objects for that module.
    1.62 +     */
    1.63 +    let modules = new Map();
    1.64 +
    1.65 +    /**
    1.66 +     * Mapping from object urls to module paths.
    1.67 +     */
    1.68 +    let paths = {
    1.69 +      /**
    1.70 +       * @keys {string} The object url holding a module.
    1.71 +       * @values {string} The absolute path to that module.
    1.72 +       */
    1.73 +      _map: new Map(),
    1.74 +      /**
    1.75 +       * A regexp that may be used to search for all mapped paths.
    1.76 +       */
    1.77 +      get regexp() {
    1.78 +        if (this._regexp) {
    1.79 +          return this._regexp;
    1.80 +        }
    1.81 +        let objectURLs = [];
    1.82 +        for (let [objectURL, _] of this._map) {
    1.83 +          objectURLs.push(objectURL);
    1.84 +        }
    1.85 +        return this._regexp = new RegExp(objectURLs.join("|"), "g");
    1.86 +      },
    1.87 +      _regexp: null,
    1.88 +      /**
    1.89 +       * Add a mapping from an object url to a path.
    1.90 +       */
    1.91 +      set: function(url, path) {
    1.92 +        this._regexp = null; // invalidate regexp
    1.93 +        this._map.set(url, path);
    1.94 +      },
    1.95 +      /**
    1.96 +       * Get a mapping from an object url to a path.
    1.97 +       */
    1.98 +      get: function(url) {
    1.99 +        return this._map.get(url);
   1.100 +      },
   1.101 +      /**
   1.102 +       * Transform a string by replacing all the instances of objectURLs
   1.103 +       * appearing in that string with the corresponding module path.
   1.104 +       *
   1.105 +       * This is used typically to translate exception stacks.
   1.106 +       *
   1.107 +       * @param {string} source A source string.
   1.108 +       * @return {string} The same string as |source|, in which every occurrence
   1.109 +       * of an objectURL registered in this object has been replaced with the
   1.110 +       * corresponding module path.
   1.111 +       */
   1.112 +      substitute: function(source) {
   1.113 +        let map = this._map;
   1.114 +        return source.replace(this.regexp, function(url) {
   1.115 +          return map.get(url) || url;
   1.116 +        }, "g");
   1.117 +      }
   1.118 +    };
   1.119 +
   1.120 +    /**
   1.121 +     * A human-readable version of |stack|.
   1.122 +     *
   1.123 +     * @type {string}
   1.124 +     */
   1.125 +    Object.defineProperty(Error.prototype, "moduleStack",
   1.126 +    {
   1.127 +      get: function() {
   1.128 +        return paths.substitute(this.stack);
   1.129 +      }
   1.130 +    });
   1.131 +    /**
   1.132 +     * A human-readable version of |fileName|.
   1.133 +     *
   1.134 +     * @type {string}
   1.135 +     */
   1.136 +    Object.defineProperty(Error.prototype, "moduleName",
   1.137 +    {
   1.138 +      get: function() {
   1.139 +        return paths.substitute(this.fileName);
   1.140 +      }
   1.141 +    });
   1.142 +
   1.143 +    /**
   1.144 +     * Import a module
   1.145 +     *
   1.146 +     * @param {string} path The path to the module.
   1.147 +     * @return {*} An object containing the properties exported by the module.
   1.148 +     */
   1.149 +    return function require(path) {
   1.150 +      if (typeof path != "string" || path.indexOf("://") == -1) {
   1.151 +        throw new TypeError("The argument to require() must be a string uri, got " + path);
   1.152 +      }
   1.153 +      // Automatically add ".js" if there is no extension
   1.154 +      let uri;
   1.155 +      if (path.lastIndexOf(".") <= path.lastIndexOf("/")) {
   1.156 +        uri = path + ".js";
   1.157 +      } else {
   1.158 +        uri = path;
   1.159 +      }
   1.160 +
   1.161 +      // Exports provided by the module
   1.162 +      let exports = Object.create(null);
   1.163 +
   1.164 +      // Identification of the module
   1.165 +      let module = {
   1.166 +        id: path,
   1.167 +        uri: uri,
   1.168 +        exports: exports
   1.169 +      };
   1.170 +
   1.171 +      // Make module available immediately
   1.172 +      // (necessary in case of circular dependencies)
   1.173 +      if (modules.has(path)) {
   1.174 +        return modules.get(path).exports;
   1.175 +      }
   1.176 +      modules.set(path, module);
   1.177 +
   1.178 +      let name = ":" + path;
   1.179 +      let objectURL;
   1.180 +      try {
   1.181 +        // Load source of module, synchronously
   1.182 +        let xhr = new XMLHttpRequest();
   1.183 +        xhr.open("GET", uri, false);
   1.184 +        xhr.responseType = "text";
   1.185 +        xhr.send();
   1.186 +
   1.187 +
   1.188 +        let source = xhr.responseText;
   1.189 +        if (source == "") {
   1.190 +          // There doesn't seem to be a better way to detect that the file couldn't be found
   1.191 +          throw new Error("Could not find module " + path);
   1.192 +        }
   1.193 +        // From the source, build a function and an object URL. We
   1.194 +        // avoid any newline at the start of the file to ensure that
   1.195 +        // we do not mess up with line numbers. However, using object URLs
   1.196 +        // messes up with stack traces in instances of Error().
   1.197 +        source = "require._tmpModules[\"" + name + "\"] = " +
   1.198 +          "function(exports, require, module) {" +
   1.199 +          source +
   1.200 +        "\n}\n";
   1.201 +        let blob = new Blob(
   1.202 +          [
   1.203 +            (new TextEncoder()).encode(source)
   1.204 +          ], {
   1.205 +            type: "application/javascript"
   1.206 +          });
   1.207 +        objectURL = URL.createObjectURL(blob);
   1.208 +        paths.set(objectURL, path);
   1.209 +        importScripts(objectURL);
   1.210 +        require._tmpModules[name].call(null, exports, require, module);
   1.211 +
   1.212 +      } catch (ex) {
   1.213 +        // Module loading has failed, exports should not be made available
   1.214 +        // after all.
   1.215 +        modules.delete(path);
   1.216 +        throw ex;
   1.217 +      } finally {
   1.218 +        if (objectURL) {
   1.219 +          // Clean up the object url as soon as possible. It will not be needed.
   1.220 +          URL.revokeObjectURL(objectURL);
   1.221 +        }
   1.222 +        delete require._tmpModules[name];
   1.223 +      }
   1.224 +
   1.225 +      Object.freeze(module.exports);
   1.226 +      Object.freeze(module);
   1.227 +      return module.exports;
   1.228 +    };
   1.229 +  })();
   1.230 +
   1.231 +  /**
   1.232 +   * An object used to hold temporarily the module constructors
   1.233 +   * while they are being loaded.
   1.234 +   *
   1.235 +   * @keys {string} The path to the module, prefixed with ":".
   1.236 +   * @values {function} A function wrapping the module.
   1.237 +   */
   1.238 +  require._tmpModules = Object.create(null);
   1.239 +  Object.freeze(require);
   1.240 +
   1.241 +  Object.defineProperty(exports, "require", {
   1.242 +    value: require,
   1.243 +    enumerable: true,
   1.244 +    configurable: false
   1.245 +  });
   1.246 +})(this);

mercurial