From bd90fcc15d504640a878667ecf2638d17628b44e Mon Sep 17 00:00:00 2001 From: John Foerch Date: Mon, 30 Jan 2012 19:18:30 -0500 Subject: [PATCH] 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. --- components/application.js | 42 ++++++++++++++++++++++++++++-------------- tests/simple/modules.js | 36 ++++++++++++++++++++++++++++++++++-- 2 files changed, 62 insertions(+), 16 deletions(-) diff --git a/components/application.js b/components/application.js index 7e88914..962ca73 100644 --- a/components/application.js +++ b/components/application.js @@ -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); } } } diff --git a/tests/simple/modules.js b/tests/simple/modules.js index a88dc11..9e39040 100644 --- a/tests/simple/modules.js +++ b/tests/simple/modules.js @@ -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")); } }); -- 2.11.4.GIT