1
0
mirror of https://github.com/Jermolene/TiddlyWiki5 synced 2024-11-30 05:19:57 +00:00

Further refactoring of the boot process and module mechanism

This commit is contained in:
Jeremy Ruston 2012-11-15 10:40:03 +00:00
parent 539b64b626
commit 9281fa8786
2 changed files with 153 additions and 136 deletions

View File

@ -29,11 +29,16 @@ In practice, each module is wrapped in a separate script block.
/////////////////////////// Setting up $tw /////////////////////////// Setting up $tw
// Set up $tw global for the server // Set up $tw global for the server
if(typeof(window) === "undefined" && !global.$tw) { if(typeof(window) === "undefined") {
global.$tw = {}; // No `browser` member for the server global.$tw = global.$tw || {}; // No `browser` member for the server
exports.$tw = $tw; // Export $tw for when boot.js is required directly in node.js exports.$tw = $tw; // Export $tw for when boot.js is required directly in node.js
} }
// Include bootprefix if we're on the server
if(!$tw.browser) {
require("./bootprefix.js");
}
// Crypto helper object // Crypto helper object
// Setup crypto // Setup crypto
@ -57,7 +62,6 @@ var Crypto = function() {
password = window.prompt("Enter password to decrypt TiddlyWiki"); password = window.prompt("Enter password to decrypt TiddlyWiki");
} }
}; };
this.setPassword = function(newPassword) { this.setPassword = function(newPassword) {
password = newPassword; password = newPassword;
}; };
@ -293,19 +297,6 @@ if(!$tw.browser) {
/////////////////////////// Module mechanism /////////////////////////// Module mechanism
/*
Register the exports of a single module in the $tw.modules.types hashmap
*/
$tw.modules.registerModuleExports = function(name,moduleType,moduleExports) {
if(!$tw.utils.hop($tw.modules.types,moduleType)) {
$tw.modules.types[moduleType] = {};
}
if($tw.utils.hop($tw.modules.types[moduleType],name)) {
console.log("Warning: Reregistering module - " + name);
}
$tw.modules.types[moduleType][name] = moduleExports;
};
/* /*
Apply a callback to each module of a particular type Apply a callback to each module of a particular type
moduleType: type of modules to enumerate moduleType: type of modules to enumerate
@ -316,7 +307,7 @@ $tw.modules.forEachModuleOfType = function(moduleType,callback) {
if(modules) { if(modules) {
for(var title in modules) { for(var title in modules) {
if($tw.utils.hop(modules,title)) { if($tw.utils.hop(modules,title)) {
callback(title,modules[title]); callback(title,$tw.modules.execute(title));
} }
} }
} }
@ -379,6 +370,7 @@ $tw.Tiddler = function(/* [fields,] fields */) {
$tw.Tiddler.prototype.hasField = function(field) { $tw.Tiddler.prototype.hasField = function(field) {
return $tw.utils.hop(this.fields,field); return $tw.utils.hop(this.fields,field);
}; };
/* /*
Hashmap of field modules by field name Hashmap of field modules by field name
*/ */
@ -387,17 +379,17 @@ $tw.Tiddler.fieldModules = {};
/* /*
Register and install the built in tiddler field modules Register and install the built in tiddler field modules
*/ */
$tw.modules.registerModuleExports("$:/boot/tiddlerfields/modified","tiddlerfield",{ $tw.modules.define("$:/boot/tiddlerfields/modified","tiddlerfield",{
name: "modified", name: "modified",
parse: $tw.utils.parseDate, parse: $tw.utils.parseDate,
stringify: $tw.utils.stringifyDate stringify: $tw.utils.stringifyDate
}); });
$tw.modules.registerModuleExports("$:/boot/tiddlerfields/created","tiddlerfield",{ $tw.modules.define("$:/boot/tiddlerfields/created","tiddlerfield",{
name: "created", name: "created",
parse: $tw.utils.parseDate, parse: $tw.utils.parseDate,
stringify: $tw.utils.stringifyDate stringify: $tw.utils.stringifyDate
}); });
$tw.modules.registerModuleExports("$:/boot/tiddlerfields/tags","tiddlerfield",{ $tw.modules.define("$:/boot/tiddlerfields/tags","tiddlerfield",{
name: "tags", name: "tags",
parse: $tw.utils.parseStringArray, parse: $tw.utils.parseStringArray,
stringify: function(value) { stringify: function(value) {
@ -412,8 +404,6 @@ $tw.modules.registerModuleExports("$:/boot/tiddlerfields/tags","tiddlerfield",{
return result.join(" "); return result.join(" ");
} }
}); });
// Install built in tiddler fields module so that they are available immediately
$tw.Tiddler.fieldModules = $tw.modules.getModulesByTypeAsHashmap("tiddlerfield");
/////////////////////////// Barebones wiki store /////////////////////////// Barebones wiki store
@ -422,6 +412,8 @@ Construct a wiki store object
*/ */
$tw.Wiki = function() { $tw.Wiki = function() {
this.tiddlers = {}; this.tiddlers = {};
this.bundles = {}; // Hashmap of plugin information by title
this.bundledTiddlers = {}; // Hashmap of constituent tiddlers from plugins by title
}; };
$tw.Wiki.prototype.addTiddler = function(tiddler) { $tw.Wiki.prototype.addTiddler = function(tiddler) {
@ -441,8 +433,6 @@ $tw.Wiki.prototype.addTiddlers = function(tiddlers) {
Extract constituent tiddlers from bundle tiddlers so that we can easily access them in getTiddler() Extract constituent tiddlers from bundle tiddlers so that we can easily access them in getTiddler()
*/ */
$tw.Wiki.prototype.unpackBundleTiddlers = function() { $tw.Wiki.prototype.unpackBundleTiddlers = function() {
this.bundles = {}; // Hashmap of plugin information by title
this.bundledTiddlers = {}; // Hashmap of constituent tiddlers from plugins by title
// Collect up all the plugin tiddlers // Collect up all the plugin tiddlers
for(var title in this.tiddlers) { for(var title in this.tiddlers) {
var tiddler = this.tiddlers[title]; var tiddler = this.tiddlers[title];
@ -463,52 +453,36 @@ $tw.Wiki.prototype.unpackBundleTiddlers = function() {
} }
}; };
/*
Define all modules stored in ordinary tiddlers
*/
$tw.Wiki.prototype.defineTiddlerModules = function() {
for(var title in this.tiddlers) {
if($tw.utils.hop(this.tiddlers,title)) {
var tiddler = this.getTiddler(title);
if(tiddler.fields.type === "application/javascript" && tiddler.hasField("module-type")) {
// Define the module
$tw.modules.define(tiddler.fields.title,tiddler.fields["module-type"],tiddler.fields.text);
}
}
}
};
/* /*
Register all the module tiddlers that have a module type Register all the module tiddlers that have a module type
*/ */
$tw.Wiki.prototype.registerModuleTiddlers = function() { $tw.Wiki.prototype.defineBundledModules = function() {
/*jslint evil: true */ for(var title in this.bundledTiddlers) {
var title, tiddler; if($tw.utils.hop(this.bundledTiddlers,title)) {
// If in the browser, define any modules from bundles var tiddler = this.getTiddler(title);
if($tw.browser) { if(!$tw.utils.hop(this.tiddlers,title)) {
for(title in $tw.wiki.bundledTiddlers) {
if($tw.utils.hop($tw.wiki.bundledTiddlers,title)) {
tiddler = $tw.wiki.getTiddler(title);
if(!$tw.utils.hop($tw.wiki.tiddlers,title)) {
if(tiddler.fields.type === "application/javascript" && tiddler.hasField("module-type")) {
// Define the module
var source = [
"(function(module,exports,require) {",
tiddler.fields.text,
"})"
];
$tw.modules.define(tiddler.fields.title,tiddler.fields["module-type"],window["eval"](source.join("")));
}
}
}
}
}
// Register and execute any modules from bundles
for(title in $tw.wiki.bundledTiddlers) {
if($tw.utils.hop($tw.wiki.bundledTiddlers,title)) {
tiddler = $tw.wiki.getTiddler(title);
if(!$tw.utils.hop($tw.wiki.tiddlers,title)) {
if(tiddler.fields.type === "application/javascript" && tiddler.hasField("module-type")) { if(tiddler.fields.type === "application/javascript" && tiddler.hasField("module-type")) {
// Execute and register the module // Define the module
$tw.modules.registerModuleExports(title,tiddler.fields["module-type"],$tw.modules.execute(title)); $tw.modules.define(tiddler.fields.title,tiddler.fields["module-type"],tiddler.fields.text);
} }
} }
} }
} }
// Register and execute any modules in ordinary tiddlers
for(title in $tw.wiki.tiddlers) {
if($tw.utils.hop($tw.wiki.tiddlers,title)) {
tiddler = $tw.wiki.getTiddler(title);
if(tiddler.fields.type === "application/javascript" && tiddler.hasField("module-type")) {
$tw.modules.registerModuleExports(title,tiddler.fields["module-type"],$tw.modules.execute(title));
}
}
}
}; };
$tw.Wiki.prototype.getTiddler = function(title) { $tw.Wiki.prototype.getTiddler = function(title) {
@ -558,7 +532,7 @@ $tw.Wiki.prototype.deserializeTiddlers = function(type,text,srcFields) {
/* /*
Register the built in tiddler deserializer modules Register the built in tiddler deserializer modules
*/ */
$tw.modules.registerModuleExports("$:/boot/tiddlerdeserializer/js","tiddlerdeserializer",{ $tw.modules.define("$:/boot/tiddlerdeserializer/js","tiddlerdeserializer",{
"application/javascript": function(text,fields) { "application/javascript": function(text,fields) {
var headerCommentRegExp = new RegExp($tw.config.jsModuleHeaderRegExpString,"mg"), var headerCommentRegExp = new RegExp($tw.config.jsModuleHeaderRegExpString,"mg"),
match = headerCommentRegExp.exec(text); match = headerCommentRegExp.exec(text);
@ -569,7 +543,7 @@ $tw.modules.registerModuleExports("$:/boot/tiddlerdeserializer/js","tiddlerdeser
return [fields]; return [fields];
} }
}); });
$tw.modules.registerModuleExports("$:/boot/tiddlerdeserializer/tid","tiddlerdeserializer",{ $tw.modules.define("$:/boot/tiddlerdeserializer/tid","tiddlerdeserializer",{
"application/x-tiddler": function(text,fields) { "application/x-tiddler": function(text,fields) {
var split = text.split(/\r?\n\r?\n/mg); var split = text.split(/\r?\n\r?\n/mg);
if(split.length > 1) { if(split.length > 1) {
@ -581,30 +555,27 @@ $tw.modules.registerModuleExports("$:/boot/tiddlerdeserializer/tid","tiddlerdese
return [fields]; return [fields];
} }
}); });
$tw.modules.registerModuleExports("$:/boot/tiddlerdeserializer/txt","tiddlerdeserializer",{ $tw.modules.define("$:/boot/tiddlerdeserializer/txt","tiddlerdeserializer",{
"text/plain": function(text,fields) { "text/plain": function(text,fields) {
fields.text = text; fields.text = text;
fields.type = "text/plain"; fields.type = "text/plain";
return [fields]; return [fields];
} }
}); });
$tw.modules.registerModuleExports("$:/boot/tiddlerdeserializer/html","tiddlerdeserializer",{ $tw.modules.define("$:/boot/tiddlerdeserializer/html","tiddlerdeserializer",{
"text/html": function(text,fields) { "text/html": function(text,fields) {
fields.text = text; fields.text = text;
fields.type = "text/html"; fields.type = "text/html";
return [fields]; return [fields];
} }
}); });
$tw.modules.registerModuleExports("$:/boot/tiddlerdeserializer/json","tiddlerdeserializer",{ $tw.modules.define("$:/boot/tiddlerdeserializer/json","tiddlerdeserializer",{
"application/json": function(text,fields) { "application/json": function(text,fields) {
var tiddlers = JSON.parse(text); var tiddlers = JSON.parse(text);
return tiddlers; return tiddlers;
} }
}); });
// Install the tiddler deserializer modules so they are immediately available
$tw.modules.applyMethods("tiddlerdeserializer",$tw.Wiki.tiddlerDeserializerModules);
/////////////////////////// Intermediate initialisation /////////////////////////// Intermediate initialisation
/* /*
@ -625,23 +596,29 @@ $tw.modules.execute = function(moduleName,moduleRoot) {
return $tw.modules.execute(modRequire,name); return $tw.modules.execute(modRequire,name);
}, },
exports = {}, exports = {},
module = $tw.modules.titles[name]; moduleInfo = $tw.modules.titles[name];
if(!module) { if(!moduleInfo) {
throw new Error("Cannot find module named '" + moduleName + "' required by module '" + moduleRoot + "', resolved to " + name); throw new Error("Cannot find module named '" + moduleName + "' required by module '" + moduleRoot + "', resolved to " + name);
} }
if(module.exports) { if(!moduleInfo.exports) {
return module.exports; if(typeof moduleInfo.definition === "string") { // String
} else { moduleInfo.definition = window["eval"]("(function(module,exports,require) {" + moduleInfo.definition + "})");
module.exports = {}; moduleInfo.exports = {};
module.fn(module,module.exports,require); moduleInfo.definition(moduleInfo,moduleInfo.exports,require);
return module.exports; } else if(typeof moduleInfo.definition === "function") { // Function
moduleInfo.exports = {};
moduleInfo.definition(moduleInfo,moduleInfo.exports,require);
} else { // Object
moduleInfo.exports = moduleInfo.definition;
}
} }
return moduleInfo.exports;
}; };
/* /*
Register a deserializer that can extract tiddlers from the DOM Register a deserializer that can extract tiddlers from the DOM
*/ */
$tw.modules.registerModuleExports("$:/boot/tiddlerdeserializer/dom","tiddlerdeserializer",{ $tw.modules.define("$:/boot/tiddlerdeserializer/dom","tiddlerdeserializer",{
"(DOM)": function(node) { "(DOM)": function(node) {
var extractTextTiddlers = function(node) { var extractTextTiddlers = function(node) {
var e = node.firstChild; var e = node.firstChild;
@ -714,8 +691,26 @@ $tw.modules.registerModuleExports("$:/boot/tiddlerdeserializer/dom","tiddlerdese
return result; return result;
} }
}); });
// Install the tiddler deserializer modules
$tw.modules.applyMethods("tiddlerdeserializer",$tw.Wiki.tiddlerDeserializerModules); $tw.loadTiddlers = function() {
// In the browser, we load tiddlers from certain elements
var containerIds = [
"libraryModules",
"modules",
"bootKernelPrefix",
"bootKernel",
"styleArea",
"storeArea",
"shadowArea"
];
for(var t=0; t<containerIds.length; t++) {
$tw.wiki.addTiddlers($tw.wiki.deserializeTiddlers("(DOM)",document.getElementById(containerIds[t])));
}
// Load any preloaded tiddlers
if($tw.preloadTiddlers) {
$tw.wiki.addTiddlers($tw.preloadTiddlers);
}
};
// End of if($tw.browser) // End of if($tw.browser)
} }
@ -827,10 +822,10 @@ Execute the module named 'moduleName'. The name can optionally be relative to th
*/ */
$tw.modules.execute = function(moduleName,moduleRoot) { $tw.modules.execute = function(moduleName,moduleRoot) {
var name = moduleRoot ? $tw.utils.resolvePath(moduleName,moduleRoot) : moduleName, var name = moduleRoot ? $tw.utils.resolvePath(moduleName,moduleRoot) : moduleName,
module = $tw.modules.titles[name], moduleInfo = $tw.modules.titles[name],
tiddler = $tw.wiki.getTiddler(name), tiddler = $tw.wiki.getTiddler(name),
sandbox = { sandbox = {
module: module, module: moduleInfo,
exports: {}, exports: {},
console: console, console: console,
process: process, process: process,
@ -839,61 +834,38 @@ $tw.modules.execute = function(moduleName,moduleRoot) {
return $tw.modules.execute(title,name); return $tw.modules.execute(title,name);
} }
}; };
if(!tiddler || tiddler.fields.type !== "application/javascript") { if(!moduleInfo) {
// If there is no tiddler with that name, let node try to find it // If we don't have a module with that name, let node.js try to find it
return require(moduleName); return require(moduleName);
} }
// Define the module if it is not defined // Execute the module if we haven't already done so
module = module || { if(!moduleInfo.exports) {
moduleType: tiddler.fields["module-type"]
};
$tw.modules.titles[name] = module;
// Execute it to get its exports if we haven't already done so
if(!module.exports) {
try { try {
vm.runInNewContext(tiddler.fields.text,sandbox,tiddler.fields.title); // Check the type of the definition
if(typeof moduleInfo.definition === "string") { // String
vm.runInNewContext(moduleInfo.definition,sandbox,tiddler.fields.title);
moduleInfo.exports = sandbox.exports;
} else if(typeof moduleInfo.definition === "function") { // Function
moduleInfo.exports = moduleInfo.definition(moduleInfo,sandbox.require,moduleInfo.exports);
} else { // Object
moduleInfo.exports = moduleInfo.definition;
}
} catch(e) { } catch(e) {
throw "Error executing boot module " + tiddler.fields.title + ":\n" + e; throw "Error executing boot module " + name + ":\n" + e;
} }
module.exports = sandbox.exports;
} }
// Return the exports of the module // Return the exports of the module
return module.exports; return moduleInfo.exports;
}; };
// End of if(!$tw.browser) $tw.loadTiddlers = function() {
}
/////////////////////////// Final initialisation
// Load tiddlers
var t;
if($tw.browser) {
// In the browser, we load tiddlers from certain elements
var containerIds = [
"libraryModules",
"modules",
"bootKernelPrefix",
"bootKernel",
"styleArea",
"storeArea",
"shadowArea"
];
for(t=0; t<containerIds.length; t++) {
$tw.wiki.addTiddlers($tw.wiki.deserializeTiddlers("(DOM)",document.getElementById(containerIds[t])));
}
// Load any preloaded tiddlers
if($tw.preloadTiddlers) {
$tw.wiki.addTiddlers($tw.preloadTiddlers);
}
} else {
// On the server, we load tiddlers from specified folders // On the server, we load tiddlers from specified folders
var folders = [ var folders = [
$tw.boot.bootPath, $tw.boot.bootPath,
path.resolve($tw.boot.wikiPath,$tw.config.wikiShadowsSubDir), path.resolve($tw.boot.wikiPath,$tw.config.wikiShadowsSubDir),
path.resolve($tw.boot.wikiPath,$tw.config.wikiTiddlersSubDir) path.resolve($tw.boot.wikiPath,$tw.config.wikiTiddlersSubDir)
]; ];
for(t=0; t<folders.length; t++) { for(var t=0; t<folders.length; t++) {
$tw.wiki.addTiddlers($tw.loadTiddlersFromPath(folders[t])); $tw.wiki.addTiddlers($tw.loadTiddlersFromPath(folders[t]));
} }
// Load any plugins listed in the wiki info file // Load any plugins listed in the wiki info file
@ -919,14 +891,27 @@ if($tw.browser) {
} }
} }
} }
};
// End of if(!$tw.browser)
} }
// Install plugins /////////////////////////// Final initialisation
// Install built in tiddler fields modules
$tw.Tiddler.fieldModules = $tw.modules.getModulesByTypeAsHashmap("tiddlerfield");
// Install the tiddler deserializer modules
$tw.modules.applyMethods("tiddlerdeserializer",$tw.Wiki.tiddlerDeserializerModules);
// Load tiddlers
$tw.loadTiddlers();
// Unpack bundle tiddlers
$tw.wiki.unpackBundleTiddlers(); $tw.wiki.unpackBundleTiddlers();
// Register typed modules from the tiddlers we've just loaded // Register typed modules from the tiddlers we've just loaded
$tw.wiki.registerModuleTiddlers(); if(!$tw.browser) {
$tw.wiki.defineTiddlerModules();
}
$tw.wiki.defineBundledModules();
// Run any startup modules // Run any startup modules
$tw.modules.forEachModuleOfType("startup",function(title,module) { $tw.modules.forEachModuleOfType("startup",function(title,module) {
if(module.startup) { if(module.startup) {

View File

@ -13,26 +13,58 @@ See Boot.js for further details of the boot process.
\*/ \*/
// Set up $tw global for the browser // Set up $tw global for the browser
if(window && !window.$tw) { if(typeof(window) === "undefined") {
window.$tw = {browser: {}}; global.$tw = global.$tw || {}; // No `browser` member for the server
} else {
window.$tw = window.$tw || {browser: {}};
} }
$tw.modules = {titles: {}}; // hashmap by module name of {fn:, exports:, moduleType:} /*
Information about each module is kept in an object with these members:
moduleType: type of module
definition: object, function or string defining the module; see below
exports: exports of the module, filled in after execution
The `definition` can be of several types:
* An object can be used to directly specify the exports of the module
* A function with the arguments `module,require,exports` that returns `exports`
* A string function body with the same arguments
Each moduleInfo object is stored in two hashmaps: $tw.modules.titles and $tw.modules.types. The first is indexed by title and the second is indexed by type and then title
*/
$tw.modules = {
titles: {}, // hashmap by module name of moduleInfo
types: {} // hashmap by module type and then name of moduleInfo
};
/* /*
Define a JavaScript tiddler module for later execution Define a JavaScript tiddler module for later execution
moduleName: name of module being defined moduleName: name of module being defined
moduleType: type of module moduleType: type of module
fn: function defining the module, called with the arguments (module,require,exports) definition: module definition; see discussion above
*/ */
$tw.modules.define = function(moduleName,moduleType,fn) { $tw.modules.define = function(moduleName,moduleType,definition) {
/*jslint evil: true */
// Create the moduleInfo
var moduleInfo = {
moduleType: moduleType,
definition: definition,
exports: undefined
};
// Store the module in the titles hashmap
if(Object.prototype.hasOwnProperty.call($tw.modules.titles,moduleName)) { if(Object.prototype.hasOwnProperty.call($tw.modules.titles,moduleName)) {
console.log("Warning: Redefined module - " + moduleName); console.log("Warning: Redefined module - " + moduleName);
} }
$tw.modules.titles[moduleName] = { $tw.modules.titles[moduleName] = moduleInfo;
moduleType: moduleType, // Store the module in the types hashmap
fn: fn if(!Object.prototype.hasOwnProperty.call($tw.modules.types,moduleType)) {
}; $tw.modules.types[moduleType] = {};
}
if(Object.prototype.hasOwnProperty.call($tw.modules.types[moduleType],moduleName)) {
console.log("Warning: Redefined module - " + moduleName);
}
$tw.modules.types[moduleType][moduleName] = moduleInfo;
}; };
/* /*