[gjs/wip/require: 7/7] bootstrap: Add a CommonJS-style imports system



commit b0bb7500e3ebfe80cb30d505dad109ac3598e218
Author: Jasper St. Pierre <jstpierre mecheye net>
Date:   Thu Jan 2 15:59:16 2014 -0500

    bootstrap: Add a CommonJS-style imports system
    
    Use a native JS hook to add an implementation of the CommonJS Modules
    1.1.1 specification, using require(). This is independent from the
    existing imports system.
    
    This implementation has one minor deviation from the spec to be more
    compatible with the old imports system, and that is that modules are
    not run in their own private namespace, but instead directly in their
    "exports" namespace. That is, "this" and "exports" are the same object
    within the module. The "module" variable is also on "exports".
    This means that we can port the majority of modules over without
    requiring that they be rewritten to use exports, which doesn't exist
    in old versions of gjs.

 doc/Style_Guide.txt          |   12 +++---
 doc/cairo.txt                |    2 +-
 examples/clutter.js          |    3 +-
 examples/gettext.js          |    4 +-
 examples/gio-cat.js          |    4 +-
 examples/gtk.js              |    3 +-
 examples/http-server.js      |    2 +-
 examples/webkit.js           |    5 +-
 gi/foreign.cpp               |    9 +---
 gi/util.cpp                  |    2 +-
 gjs/byteArray.cpp            |    2 +-
 modules/bootstrap.js         |   90 ++++++++++++++++++++++++++++++++++++++++-
 modules/cairo.js             |    5 +-
 modules/format.js            |    2 +-
 modules/gettext.js           |    6 +-
 modules/lang.js              |    2 +-
 modules/mainloop.js          |    4 +-
 modules/overrides/GLib.js    |    6 +-
 modules/overrides/GObject.js |    6 +-
 modules/overrides/Gio.js     |   10 ++--
 modules/overrides/Gtk.js     |    6 +-
 modules/tweener/tweener.js   |   11 +++--
 22 files changed, 140 insertions(+), 56 deletions(-)
---
diff --git a/doc/Style_Guide.txt b/doc/Style_Guide.txt
index e93a601..eb7e700 100644
--- a/doc/Style_Guide.txt
+++ b/doc/Style_Guide.txt
@@ -19,8 +19,8 @@ and the like.
 Use CamelCase when importing modules to distinguish them from ordinary variables, e.g.
 
 <pre>
-const Big = imports.big;
-const GLib = imports.gi.GLib;
+const Big = require('big');
+const GLib = require('gi/GLib');
 </pre>
 
 
@@ -62,7 +62,7 @@ in at function invocation time, it is not a variable that can be captured in clo
 To solve this, use Lang.bind, eg:
 
 <pre>
-const Lang = imports.lang;
+const Lang = require('lang');
 
 let closure = Lang.bind(this, function() { this._fnorbate() });
 </pre>
@@ -71,7 +71,7 @@ A more realistic example would be connecting to a signal on a
 method of a prototype:
 
 <pre>
-const Lang = imports.lang;
+const Lang = require('lang');
 
 MyPrototype = {
     _init : function() {
@@ -106,7 +106,7 @@ If your usage of an object is like a hash table (and thus conceptually the keys
 # We use javaStyle variable names, with CamelCase for type names and lowerCamelCase for variable and method 
names. However, when calling a C method with underscore-based names via introspection, we just keep them 
looking as they do in C for simplicity.
 # Private variables, whether object member variables or module-scoped variables, should begin with "_".
 # True global variables (in the global or 'window' object) should be avoided whenever possible. If you do 
create them, the variable name should have a namespace in it, like "BigFoo"
-# When you assign a module to an alias to avoid typing "imports.foo.bar" all the time, the alias should be 
"const TitleCase" so "const Bar = imports.foo.bar;"
+# When you assign a module to an alias to avoid typing "require('foo/bar')" all the time, the alias should 
be "const TitleCase" so "const Bar = require('foo/bar');"
 # If you need to name a variable something weird to avoid a namespace collision, add a trailing "_" (not 
leading, leading "_" means private).
 # For GObject constructors, always use the lowerCamelCase style for property names instead of dashes or 
underscores.
 
@@ -170,7 +170,7 @@ There are lots of ways to simulate "inheritance" in JavaScript. In general, it's
 
 Our preferred approach is to use a Spidermonkey-specific extension and directly set the __proto__ member of 
the subclass's prototype to point to the prototype of the base class. Looking up a property in the subclass 
starts with the properties of the instance. If the property isn't there, then the prototype chain is followed 
first to the subclass's prototype and then to the base class's prototype.
 <pre>
-const Lang = imports.lang;
+const Lang = require('lang');
 
 function Base(foo) {
   this._init(foo);
diff --git a/doc/cairo.txt b/doc/cairo.txt
index 17b1152..e4071a5 100644
--- a/doc/cairo.txt
+++ b/doc/cairo.txt
@@ -6,7 +6,7 @@ Naming
 The module name is called 'cairo' and usually imported into
 the namespace as 'Cairo'.
 
-gjs> const Cairo = imports.cairo;
+gjs> const Cairo = require('cairo');
 
 Methods are studlyCaps, similar to other JavaScript apis, eg
 
diff --git a/examples/clutter.js b/examples/clutter.js
index a6b50ed..f0872f9 100644
--- a/examples/clutter.js
+++ b/examples/clutter.js
@@ -1,4 +1,5 @@
-const Clutter = imports.gi.Clutter;
+
+const Clutter = require('gi/Clutter');
 
 Clutter.init(null);
 
diff --git a/examples/gettext.js b/examples/gettext.js
index 44377fc..78f3364 100644
--- a/examples/gettext.js
+++ b/examples/gettext.js
@@ -1,6 +1,6 @@
 
-const Gettext = imports.gettext;
-const Gtk = imports.gi.Gtk;
+const Gettext = require('gettext');
+const Gtk = require('gi/Gtk');
 
 Gettext.bindtextdomain("gnome-panel-3.0", "/usr/share/locale");
 Gettext.textdomain("gnome-panel-3.0");
diff --git a/examples/gio-cat.js b/examples/gio-cat.js
index f26e710..842769b 100644
--- a/examples/gio-cat.js
+++ b/examples/gio-cat.js
@@ -1,6 +1,6 @@
 
-const GLib = imports.gi.GLib;
-const Gio = imports.gi.Gio;
+const GLib = require('gi/GLib');
+const Gio = require('gi/Gio');
 
 let loop = GLib.MainLoop.new(null, false);
 
diff --git a/examples/gtk.js b/examples/gtk.js
index 638a147..c44f460 100644
--- a/examples/gtk.js
+++ b/examples/gtk.js
@@ -1,4 +1,5 @@
-const Gtk = imports.gi.Gtk;
+
+const Gtk = require('gi/Gtk');
 
 // This is a callback function. The data arguments are ignored
 // in this example. More on callbacks below.
diff --git a/examples/http-server.js b/examples/http-server.js
index 36c73f0..d7910fa 100644
--- a/examples/http-server.js
+++ b/examples/http-server.js
@@ -1,6 +1,6 @@
 // This is a simple example of a HTTP server in Gjs using libsoup
 
-const Soup = imports.gi.Soup;
+const Soup = require('gi/Soup');
 
 function main() {
     let handler = function(server, msg, path, query, client) {
diff --git a/examples/webkit.js b/examples/webkit.js
index 064cef6..8427370 100644
--- a/examples/webkit.js
+++ b/examples/webkit.js
@@ -1,5 +1,6 @@
-const Gtk = imports.gi.Gtk;
-const WebKit = imports.gi.WebKit;
+
+const Gtk = require('gi/Gtk');
+const WebKit = require('gi/WebKit');
 
 Gtk.init(null);
 
diff --git a/gi/foreign.cpp b/gi/foreign.cpp
index 7e2710a..bb47ff1 100644
--- a/gi/foreign.cpp
+++ b/gi/foreign.cpp
@@ -74,15 +74,12 @@ gjs_foreign_load_foreign_module(JSContext *context,
 
         // FIXME: Find a way to check if a module is imported
         //        and only execute this statement if isn't
-        script = g_strdup_printf("imports.%s;", gi_namespace);
-        if (!gjs_context_eval((GjsContext*) JS_GetContextPrivate(context), script, strlen(script),
-                              "<internal>", &code,
-                              &error)) {
-            g_printerr("ERROR: %s\n", error->message);
+        script = g_strdup_printf("require('%s');", gi_namespace);
+        if (!gjs_eval_with_scope(context, NULL, script, -1, "<internal>", NULL)) {
             g_free(script);
-            g_error_free(error);
             return JS_FALSE;
         }
+
         g_free(script);
         foreign_modules[i].loaded = TRUE;
         return JS_TRUE;
diff --git a/gi/util.cpp b/gi/util.cpp
index 055d6a6..a0e2ee8 100644
--- a/gi/util.cpp
+++ b/gi/util.cpp
@@ -281,7 +281,7 @@ gjs_lookup_namespace_object_by_name(JSContext      *context,
     if (!gjs_get_string_id(context, ns_name, &name))
         goto out;
 
-    script = g_strdup_printf("imports.gi.%s;", name);
+    script = g_strdup_printf("require('gi/%s');", name);
     if (!gjs_eval_with_scope(context, NULL, script, -1, "<internal>", &ns_val))
         goto out;
 
diff --git a/gjs/byteArray.cpp b/gjs/byteArray.cpp
index b543fa5..579fc56 100644
--- a/gjs/byteArray.cpp
+++ b/gjs/byteArray.cpp
@@ -547,7 +547,7 @@ byte_array_get_prototype(JSContext *context)
 
     if (!JSVAL_IS_OBJECT (retval)) {
         if (!gjs_eval_with_scope(context, NULL,
-                                 "imports.byteArray.ByteArray.prototype;", -1,
+                                 "require('byteArray').ByteArray.prototype;", -1,
                                  "<internal>", &retval))
             g_error ("Could not import byte array prototype\n");
     }
diff --git a/modules/bootstrap.js b/modules/bootstrap.js
index 2292ef5..87aadff 100644
--- a/modules/bootstrap.js
+++ b/modules/bootstrap.js
@@ -62,9 +62,93 @@
         importGIModuleWithOverrides(giModuleID, moduleVersion);
     }
 
+    function createModuleScope(id, uri) {
+        let module = {};
+
+        Object.defineProperty(module, "id", { value: id,
+                                              configurable: false,
+                                              writable: false });
+        Object.defineProperty(module, "uri", { value: uri,
+                                               configurable: false,
+                                               writable: false });
+
+        let scope = {};
+        scope.module = module;
+
+        // XXX -- for compatibility with the old module system, we don't
+        // give modules their own private namespace, but simply export
+        // the module scope directly.
+        //
+        // This should eventually go away, when we fully adopt CommonJS.
+        scope.exports = scope;
+
+        return scope;
+    }
+
+    function loadJSModule(moduleID) {
+        function getModuleContents(modulePath) {
+            let file = Gio.File.new_for_commandline_arg(modulePath);
+            if (!file.query_exists(null))
+                return null;
+
+            let success, script;
+            try {
+                [success, script] = file.load_contents(null);
+            } catch(e) {
+                return null;
+            }
+
+            return script;
+        }
+
+        function evalModule(modulePath, script) {
+            let scope = createModuleScope(moduleID, modulePath);
+            _exports[moduleID] = scope;
+
+            let evalSuccess = false;
+            try {
+                // Don't catch errors for the eval, as those should propagate
+                // back up to the user...
+                Importer.evalWithScope(scope, script, modulePath);
+                evalSuccess = true;
+            } finally {
+                if (!evalSuccess)
+                    delete _exports[moduleID];
+            }
+        }
+
+        for (let path of require.paths) {
+            let modulePath = path + '/' + moduleID + '.js';
+            let script = getModuleContents(modulePath);
+            if (!script)
+                continue;
+
+            evalModule(modulePath, script);
+        }
+    }
+
+    let require = exports.require = function require(moduleID) {
+        if (_exports[moduleID])
+            return _exports[moduleID];
+
+        const FINDERS = [
+            loadNativeModule,
+            loadGIModule,
+            loadJSModule,
+        ];
+
+        for (let finder of FINDERS) {
+            finder(moduleID);
+            if (_exports[moduleID])
+                return _exports[moduleID];
+        }
+
+        throw new Error("Could not load module '" + moduleID + "'");
+    }
+    require.paths = Importer.getBuiltinSearchPath();
 
-    function installImports() {
-        // Implement the global "imports" object.
+    function installCompatImports() {
+        // Implement the old global "imports" object.
 
         // imports.gi
         let gi = new Proxy({
@@ -182,6 +266,6 @@
 
         exports.imports = rootImporter;
     }
-    installImports();
+    installCompatImports();
 
 })(window, importNativeModule);
diff --git a/modules/cairo.js b/modules/cairo.js
index 7555c3c..8de7ab9 100644
--- a/modules/cairo.js
+++ b/modules/cairo.js
@@ -18,7 +18,7 @@
 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 // IN THE SOFTWARE.
 
-const Lang = imports.lang;
+const Lang = require('lang');
 
 const Antialias = {
     DEFAULT: 0,
@@ -143,5 +143,4 @@ const SurfaceType = {
 };
 
 // Merge stuff defined in native code
-Lang.copyProperties(imports.cairoNative, this);
-
+Lang.copyProperties(require('cairoNative'), this);
diff --git a/modules/format.js b/modules/format.js
index bf3d0a7..d480c83 100644
--- a/modules/format.js
+++ b/modules/format.js
@@ -1,6 +1,6 @@
 // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
 
-const GjsPrivate = imports.gi.GjsPrivate;
+const GjsPrivate = require('gi/GjsPrivate');
 
 function vprintf(str, args) {
     let i = 0;
diff --git a/modules/gettext.js b/modules/gettext.js
index cf056a8..548bec7 100644
--- a/modules/gettext.js
+++ b/modules/gettext.js
@@ -24,7 +24,7 @@
  *
  * Usage:
  *
- * const Gettext = imports.gettext;
+ * const Gettext = require('gettext');
  *
  * Gettext.textdomain("myapp");
  * Gettext.bindtextdomain("myapp", "/usr/share/locale");
@@ -32,8 +32,8 @@
  * let translated = Gettext.gettext("Hello world!");
  */
 
-const GLib = imports.gi.GLib;
-const GjsPrivate = imports.gi.GjsPrivate;
+const GLib = require('gi/GLib');
+const GjsPrivate = require('gi/GjsPrivate');
 
 function textdomain(domain) {
     return GjsPrivate.textdomain(domain);
diff --git a/modules/lang.js b/modules/lang.js
index 73de257..b6cc1c8 100644
--- a/modules/lang.js
+++ b/modules/lang.js
@@ -21,7 +21,7 @@
 
 // Utilities that are "meta-language" things like manipulating object props
 
-const Gi = imports._gi;
+const Gi = require('_gi');
 
 function countProperties(obj) {
     let count = 0;
diff --git a/modules/mainloop.js b/modules/mainloop.js
index 89c37fe..679058f 100644
--- a/modules/mainloop.js
+++ b/modules/mainloop.js
@@ -21,8 +21,8 @@
 
 // A layer of convenience and backwards-compatibility over GLib MainLoop facilities
 
-const GLib = imports.gi.GLib;
-const GObject = imports.gi.GObject;
+const GLib = require('gi/GLib');
+const GObject = require('gi/GObject');
 
 var _mainLoops = {};
 
diff --git a/modules/overrides/GLib.js b/modules/overrides/GLib.js
index f4161a2..6a056dd 100644
--- a/modules/overrides/GLib.js
+++ b/modules/overrides/GLib.js
@@ -18,7 +18,7 @@
 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 // IN THE SOFTWARE.
 
-const ByteArray = imports.byteArray;
+const ByteArray = require('byteArray');
 
 let GLib;
 let originalVariantClass;
@@ -246,7 +246,7 @@ function _unpack_variant(variant, deep) {
 }
 
 function _init() {
-    // this is imports.gi.GLib
+    // this is require('gi/GLib');
 
     GLib = this;
 
@@ -280,6 +280,6 @@ function _init() {
     };
 
     this.Bytes.prototype.toArray = function() {
-       return imports.byteArray.fromGBytes(this);
+       return require('byteArray').fromGBytes(this);
     };
 }
diff --git a/modules/overrides/GObject.js b/modules/overrides/GObject.js
index f66a0c1..a013324 100644
--- a/modules/overrides/GObject.js
+++ b/modules/overrides/GObject.js
@@ -18,9 +18,9 @@
 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 // IN THE SOFTWARE.
 
-const Lang = imports.lang;
-const Gi = imports._gi;
-const GjsPrivate = imports.gi.GjsPrivate;
+const Lang = require('lang');
+const Gi = require('_gi');
+const GjsPrivate = require('gi/GjsPrivate');
 
 let GObject;
 
diff --git a/modules/overrides/Gio.js b/modules/overrides/Gio.js
index 950641b..a4fdd92 100644
--- a/modules/overrides/Gio.js
+++ b/modules/overrides/Gio.js
@@ -18,11 +18,11 @@
 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 // IN THE SOFTWARE.
 
-var GLib = imports.gi.GLib;
-var GObject = imports.gi.GObject;
-var GjsPrivate = imports.gi.GjsPrivate;
-var Lang = imports.lang;
-var Signals = imports.signals;
+const GLib = require('gi/GLib');
+const GObject = require('gi/GObject');
+const GjsPrivate = require('gi/GjsPrivate');
+const Lang = require('lang');
+const Signals = require('signals');
 var Gio;
 
 function _signatureLength(sig) {
diff --git a/modules/overrides/Gtk.js b/modules/overrides/Gtk.js
index 8a23977..f5d7f14 100644
--- a/modules/overrides/Gtk.js
+++ b/modules/overrides/Gtk.js
@@ -19,10 +19,10 @@
 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 // IN THE SOFTWARE.
 
-const Lang = imports.lang;
-const GObject = imports.gi.GObject;
+const Lang = require('lang');
+const GObject = require('gi/GObject');
 
-var GjsPrivate = imports.gi.GjsPrivate;
+var GjsPrivate = require('gi/GjsPrivate');
 
 let Gtk;
 
diff --git a/modules/tweener/tweener.js b/modules/tweener/tweener.js
index 503dbf4..cb0b2c1 100644
--- a/modules/tweener/tweener.js
+++ b/modules/tweener/tweener.js
@@ -34,10 +34,11 @@
  http://code.google.com/p/tweener/wiki/License
  */
 
-const GLib = imports.gi.GLib;
+const GLib = require('gi/GLib');
 
-const TweenList = imports.tweener.tweenList;
-const Signals = imports.signals;
+const Equations = require('tweener/equations');
+const TweenList = require('tweener/tweenList');
+const Signals = require('signals');
 
 var _inited = false;
 var _engineExists = false;
@@ -517,13 +518,13 @@ function _addTweenOrCaller(target, tweeningParameters, isCaller) {
 
     // FIXME: Tweener allows you to use functions with an all lower-case name
     if (typeof obj.transition == "string") {
-        transition = imports.tweener.equations[obj.transition];
+        transition = Equations[obj.transition];
     } else {
         transition = obj.transition;
     }
 
     if (!transition)
-        transition = imports.tweener.equations["easeOutExpo"];
+        transition = Equations.easeOutExpo;
 
     var tween;
 


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]