Permalink
Browse files

jsx module system

This patch introduces a module system that works within the framework of
Conkeror's 'require' and 'load', makes no sacrifices in the way of
reloading modular code, and preserves our ability to write modules in
ordinary, idiomatic javascript.  It does this by introducing two
conventions: first, that Conkeror module files have the extension '.jsx';
and second, that the rest of the filename can be normalized to obtain the
module name.

By existing conventions, module filenames that have more than one word in
them use the hyphen as word separator.  The jsx module system translates
the hyphens to underscores to obtain the module name (since hyphen is not
allowed in js identifiers).  The 'feature-name' however, (the argument to
the 'provide' command), matches the filename, using hyphens, because
feature names are strings, not javascript identifiers.

When 'load' is called on a module spec (that is, a string that names a
module, or the relative path to a module) that does not have a file
extension, the search first tries the bare name as given with no
extension, then it tries it with the ".js" extension, and last it tries it
with the ".jsx" extension.
  • Loading branch information...
1 parent 37da0ae commit bd90fcc15d504640a878667ecf2638d17628b44e @retroj committed Jan 31, 2012
Showing with 62 additions and 16 deletions.
  1. +28 −14 components/application.js
  2. +34 −2 tests/simple/modules.js
View
@@ -1,5 +1,5 @@
/**
- * (C) Copyright 2007,2010 John J. Foerch
+ * (C) Copyright 2007,2010,2012 John J. Foerch
* (C) Copyright 2007-2008 Jeremy Maitin-Shepard
*
* Use, modification, and distribution are subject to the terms specified in the
@@ -96,7 +96,18 @@ application.prototype = {
if (this.loading_urls.indexOf(url, 1) != -1)
throw new Error("Circular module dependency detected: "+
this.loading_urls.join(",\n"));
- this.load_url(url, this);
+ if (url.substr(-4) == ".jsx") {
+ var scopename = url.substr(url.lastIndexOf('/')+1)
+ .replace('-', '_', 'g');
+ var dot = scopename.indexOf(".");
+ if (dot > -1)
+ scopename = scopename.substr(0, dot);
+ var scope = { __proto__: this };
+ } else
+ scope = this;
+ this.load_url(url, scope);
+ if (scopename)
+ this[scopename] = scope;
var success = true;
// call-after-load callbacks
for (let f in this.loading_features[0]) {
@@ -123,27 +134,30 @@ application.prototype = {
path = module.parent.path;
if (path !== undefined) {
var url = this.make_uri(module).spec;
- load1.call(this, url, path);
+ load1.call(this.conkeror, url, path);
} else {
// module name or relative path
- var autoext = module.substr(-3) != '.js';
- var suffix = false;
+ var si = module.lastIndexOf('/');
+ var module_leaf = module.substr(si+1);
+ var autoext = module_leaf.lastIndexOf(".") <= 0;
+ var exts = { 0:"", 1:".js", 2:".jsx", len:3 };
+ var exti = 0;
var i = -1;
var tried = {};
path = this.loading_paths[0];
if (path === undefined)
path = this.load_paths[++i];
while (path !== undefined) {
- let truepath = path;
+ var truepath = path;
+ var sep = path.substr(-1) == '/' ? '' : '/';
+ var ext = exts[exti];
try {
- let sep = path.substr(-1) == '/' ? '' : '/';
- url = path + sep + module + (suffix ? '.js' : '');
- let si = module.lastIndexOf('/');
+ url = path + sep + module + ext;
if (si > -1)
- truepath += module.substr(0, si);
+ truepath += sep + module.substr(0, si);
if (! tried[url]) {
tried[url] = true;
- load1.call(this, url, truepath);
+ load1.call(this.conkeror, url, truepath);
return;
}
} catch (e if (typeof e == 'string' &&
@@ -154,8 +168,8 @@ application.prototype = {
// null op. (suppress error, try next path)
}
if (autoext)
- suffix = !suffix;
- if (! suffix)
+ exti = (exti + 1) % exts.len;
+ if (exti == 0)
path = this.load_paths[++i];
}
throw new Error("Module not found ("+module+")");
@@ -191,7 +205,7 @@ application.prototype = {
try {
funcs[i]();
} catch (e) {
- dump_error(e);
+ this.dump_error(e);
}
}
}
View
@@ -57,10 +57,13 @@ walnut_run({
assert_objects_equal(
["chrome://conkeror/content/foo",
"chrome://conkeror/content/foo.js",
+ "chrome://conkeror/content/foo.jsx",
"chrome://conkeror/content/extensions/foo",
"chrome://conkeror/content/extensions/foo.js",
+ "chrome://conkeror/content/extensions/foo.jsx",
"chrome://conkeror/content/page-modes/foo",
- "chrome://conkeror/content/page-modes/foo.js"],
+ "chrome://conkeror/content/page-modes/foo.js",
+ "chrome://conkeror/content/page-modes/foo.jsx"],
this.ob);
},
test_load_search_2__with_extension: function () {
@@ -115,10 +118,13 @@ walnut_run({
this.ob,
["chrome://conkeror/content/page-modes/foo",
"chrome://conkeror/content/page-modes/foo.js",
+ "chrome://conkeror/content/page-modes/foo.jsx",
"chrome://conkeror/content/extensions/page-modes/foo",
"chrome://conkeror/content/extensions/page-modes/foo.js",
+ "chrome://conkeror/content/extensions/page-modes/foo.jsx",
"chrome://conkeror/content/page-modes/page-modes/foo",
- "chrome://conkeror/content/page-modes/page-modes/foo.js"]);
+ "chrome://conkeror/content/page-modes/page-modes/foo.js",
+ "chrome://conkeror/content/page-modes/page-modes/foo.jsx"]);
}
});
@@ -260,5 +266,31 @@ walnut_run({
};
load(make_uri("chrome://conkeror/content/foo.js"));
assert_equals(tried, "chrome://conkeror/content/bar.js");
+ },
+ test_load_relative_path_nested: function () {
+ loading_paths = [];
+ loading_urls = [];
+ loading_features = [];
+ pending_loads = [];
+ after_load_functions = [];
+ features = {};
+ var ob = [];
+ var mock_modules = {
+ foo: function () {
+ load("deeper/bar");
+ provide("foo");
+ },
+ bar: function () {
+ assert(loading_paths[0].indexOf("some-path/deeper") > 0);
+ provide("bar");
+ }
+ };
+ load_url = function (url) {
+ var module = url.substr(url.lastIndexOf('/')+1);
+ mock_modules[module]();
+ };
+ load("some-path/foo");
+ assert(featurep("foo"));
+ assert(featurep("bar"));
}
});

0 comments on commit bd90fcc

Please sign in to comment.