mirror of
https://github.com/Jermolene/TiddlyWiki5
synced 2025-07-04 11:02:51 +00:00
Merge branch 'master' into multi-wiki-support
This commit is contained in:
commit
b4564e31bd
@ -2188,6 +2188,8 @@ Returns an array of search paths
|
|||||||
*/
|
*/
|
||||||
$tw.getLibraryItemSearchPaths = function(libraryPath,envVar) {
|
$tw.getLibraryItemSearchPaths = function(libraryPath,envVar) {
|
||||||
var pluginPaths = [path.resolve($tw.boot.corePath,libraryPath)],
|
var pluginPaths = [path.resolve($tw.boot.corePath,libraryPath)],
|
||||||
|
env;
|
||||||
|
if(envVar) {
|
||||||
env = process.env[envVar];
|
env = process.env[envVar];
|
||||||
if(env) {
|
if(env) {
|
||||||
env.split(path.delimiter).map(function(item) {
|
env.split(path.delimiter).map(function(item) {
|
||||||
@ -2196,6 +2198,7 @@ $tw.getLibraryItemSearchPaths = function(libraryPath,envVar) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return pluginPaths;
|
return pluginPaths;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
23
core/modules/utils/errors.js
Normal file
23
core/modules/utils/errors.js
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
/*\
|
||||||
|
title: $:/core/modules/utils/errors.js
|
||||||
|
type: application/javascript
|
||||||
|
module-type: utils
|
||||||
|
|
||||||
|
Custom errors for TiddlyWiki.
|
||||||
|
|
||||||
|
\*/
|
||||||
|
(function(){
|
||||||
|
|
||||||
|
function TranscludeRecursionError() {
|
||||||
|
Error.apply(this,arguments);
|
||||||
|
this.signatures = Object.create(null);
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Maximum permitted depth of the widget tree for recursion detection */
|
||||||
|
TranscludeRecursionError.MAX_WIDGET_TREE_DEPTH = 1000;
|
||||||
|
|
||||||
|
TranscludeRecursionError.prototype = Object.create(Error);
|
||||||
|
|
||||||
|
exports.TranscludeRecursionError = TranscludeRecursionError;
|
||||||
|
|
||||||
|
})();
|
@ -14,8 +14,12 @@ Utilities for working with the TiddlyWiki repository file structure
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
Get an object containing all the plugins as a hashmap by title of the JSON representation of the plugin
|
Get an object containing all the plugins as a hashmap by title of the JSON representation of the plugin
|
||||||
|
Options:
|
||||||
|
|
||||||
|
ignoreEnvironmentVariables: defaults to false
|
||||||
*/
|
*/
|
||||||
exports.getAllPlugins = function() {
|
exports.getAllPlugins = function(options) {
|
||||||
|
options = options || {};
|
||||||
var fs = require("fs"),
|
var fs = require("fs"),
|
||||||
path = require("path"),
|
path = require("path"),
|
||||||
tiddlers = {};
|
tiddlers = {};
|
||||||
@ -39,9 +43,9 @@ exports.getAllPlugins = function() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
$tw.utils.each($tw.getLibraryItemSearchPaths($tw.config.pluginsPath,$tw.config.pluginsEnvVar),collectPublisherPlugins);
|
$tw.utils.each($tw.getLibraryItemSearchPaths($tw.config.pluginsPath,options.ignoreEnvironmentVariables ? undefined : $tw.config.pluginsEnvVar),collectPublisherPlugins);
|
||||||
$tw.utils.each($tw.getLibraryItemSearchPaths($tw.config.themesPath,$tw.config.themesEnvVar),collectPublisherPlugins);
|
$tw.utils.each($tw.getLibraryItemSearchPaths($tw.config.themesPath,options.ignoreEnvironmentVariables ? undefined : $tw.config.themesEnvVar),collectPublisherPlugins);
|
||||||
$tw.utils.each($tw.getLibraryItemSearchPaths($tw.config.languagesPath,$tw.config.languagesEnvVar),collectPlugins);
|
$tw.utils.each($tw.getLibraryItemSearchPaths($tw.config.languagesPath,options.ignoreEnvironmentVariables ? undefined : $tw.config.languagesEnvVar),collectPlugins);
|
||||||
return tiddlers;
|
return tiddlers;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -30,7 +30,30 @@ TranscludeWidget.prototype.render = function(parent,nextSibling) {
|
|||||||
this.parentDomNode = parent;
|
this.parentDomNode = parent;
|
||||||
this.computeAttributes();
|
this.computeAttributes();
|
||||||
this.execute();
|
this.execute();
|
||||||
|
try {
|
||||||
this.renderChildren(parent,nextSibling);
|
this.renderChildren(parent,nextSibling);
|
||||||
|
} catch(error) {
|
||||||
|
if(error instanceof $tw.utils.TranscludeRecursionError) {
|
||||||
|
// We were infinite looping.
|
||||||
|
// We need to try and abort as much of the loop as we can, so we will keep "throwing" upward until we find a transclusion that has a different signature.
|
||||||
|
// Hopefully that will land us just outside where the loop began. That's where we want to issue an error.
|
||||||
|
// Rendering widgets beneath this point may result in a freezing browser if they explode exponentially.
|
||||||
|
var transcludeSignature = this.getVariable("transclusion");
|
||||||
|
if(this.getAncestorCount() > $tw.utils.TranscludeRecursionError.MAX_WIDGET_TREE_DEPTH - 50) {
|
||||||
|
// For the first fifty transcludes we climb up, we simply collect signatures.
|
||||||
|
// We're assuming that those first 50 will likely include all transcludes involved in the loop.
|
||||||
|
error.signatures[transcludeSignature] = true;
|
||||||
|
} else if(!error.signatures[transcludeSignature]) {
|
||||||
|
// Now that we're past the first 50, let's look for the first signature that wasn't in the loop. That'll be where we print the error and resume rendering.
|
||||||
|
this.children = [this.makeChildWidget({type: "error", attributes: {
|
||||||
|
"$message": {type: "string", value: $tw.language.getString("Error/RecursiveTransclusion")}
|
||||||
|
}})];
|
||||||
|
this.renderChildren(parent,nextSibling);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -12,9 +12,6 @@ Widget base class
|
|||||||
/*global $tw: false */
|
/*global $tw: false */
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
/* Maximum permitted depth of the widget tree for recursion detection */
|
|
||||||
var MAX_WIDGET_TREE_DEPTH = 1000;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Create a widget object for a parse tree node
|
Create a widget object for a parse tree node
|
||||||
parseTreeNode: reference to the parse tree node to be rendered
|
parseTreeNode: reference to the parse tree node to be rendered
|
||||||
@ -494,10 +491,8 @@ Widget.prototype.makeChildWidgets = function(parseTreeNodes,options) {
|
|||||||
this.children = [];
|
this.children = [];
|
||||||
var self = this;
|
var self = this;
|
||||||
// Check for too much recursion
|
// Check for too much recursion
|
||||||
if(this.getAncestorCount() > MAX_WIDGET_TREE_DEPTH) {
|
if(this.getAncestorCount() > $tw.utils.TranscludeRecursionError.MAX_WIDGET_TREE_DEPTH) {
|
||||||
this.children.push(this.makeChildWidget({type: "error", attributes: {
|
throw new $tw.utils.TranscludeRecursionError();
|
||||||
"$message": {type: "string", value: $tw.language.getString("Error/RecursiveTransclusion")}
|
|
||||||
}}));
|
|
||||||
} else {
|
} else {
|
||||||
// Create set variable widgets for each variable
|
// Create set variable widgets for each variable
|
||||||
$tw.utils.each(options.variables,function(value,name) {
|
$tw.utils.each(options.variables,function(value,name) {
|
||||||
|
@ -7,7 +7,8 @@ title: Output
|
|||||||
|
|
||||||
\whitespace trim
|
\whitespace trim
|
||||||
<$transclude $tiddler="Output"/>
|
<$transclude $tiddler="Output"/>
|
||||||
|
|
||||||
+
|
+
|
||||||
title: ExpectedResult
|
title: ExpectedResult
|
||||||
|
|
||||||
<p><span class="tc-error">Recursive transclusion error in transclude widget</span></p>
|
<span class="tc-error">Recursive transclusion error in transclude widget</span>
|
@ -17,7 +17,7 @@ if($tw.node) {
|
|||||||
describe("Plugin tests", function() {
|
describe("Plugin tests", function() {
|
||||||
|
|
||||||
// Get all the plugins as a hashmap by title of a JSON string with the plugin content
|
// Get all the plugins as a hashmap by title of a JSON string with the plugin content
|
||||||
var tiddlers = $tw.utils.getAllPlugins();
|
var tiddlers = $tw.utils.getAllPlugins({ignoreEnvironmentVariables: true});
|
||||||
// console.log(JSON.stringify(Object.keys(tiddlers),null,4));
|
// console.log(JSON.stringify(Object.keys(tiddlers),null,4));
|
||||||
describe("every plugin should have the required standard fields", function() {
|
describe("every plugin should have the required standard fields", function() {
|
||||||
var titles = Object.keys(tiddlers);
|
var titles = Object.keys(tiddlers);
|
||||||
|
@ -160,6 +160,47 @@ describe("Widget module", function() {
|
|||||||
expect(wrapper.innerHTML).toBe("<span class=\"tc-error\">Recursive transclusion error in transclude widget</span>");
|
expect(wrapper.innerHTML).toBe("<span class=\"tc-error\">Recursive transclusion error in transclude widget</span>");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should handle single-tiddler recursion with branching nodes", function() {
|
||||||
|
var wiki = new $tw.Wiki();
|
||||||
|
// Add a tiddler
|
||||||
|
wiki.addTiddlers([
|
||||||
|
{title: "TiddlerOne", text: "<$tiddler tiddler='TiddlerOne'><$transclude /> <$transclude /></$tiddler>"},
|
||||||
|
]);
|
||||||
|
// Test parse tree
|
||||||
|
var parseTreeNode = {type: "widget", children: [
|
||||||
|
{type: "transclude", attributes: {
|
||||||
|
"tiddler": {type: "string", value: "TiddlerOne"}
|
||||||
|
}}
|
||||||
|
]};
|
||||||
|
// Construct the widget node
|
||||||
|
var widgetNode = createWidgetNode(parseTreeNode,wiki);
|
||||||
|
// Render the widget node to the DOM
|
||||||
|
var wrapper = renderWidgetNode(widgetNode);
|
||||||
|
// Test the rendering
|
||||||
|
expect(wrapper.innerHTML).toBe("<span class=\"tc-error\">Recursive transclusion error in transclude widget</span> <span class=\"tc-error\">Recursive transclusion error in transclude widget</span>");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should handle many-tiddler recursion with branching nodes", function() {
|
||||||
|
var wiki = new $tw.Wiki();
|
||||||
|
// Add a tiddler
|
||||||
|
wiki.addTiddlers([
|
||||||
|
{title: "TiddlerOne", text: "<$transclude tiddler='TiddlerTwo'/> <$transclude tiddler='TiddlerTwo'/>"},
|
||||||
|
{title: "TiddlerTwo", text: "<$transclude tiddler='TiddlerOne'/>"}
|
||||||
|
]);
|
||||||
|
// Test parse tree
|
||||||
|
var parseTreeNode = {type: "widget", children: [
|
||||||
|
{type: "transclude", attributes: {
|
||||||
|
"tiddler": {type: "string", value: "TiddlerOne"}
|
||||||
|
}}
|
||||||
|
]};
|
||||||
|
// Construct the widget node
|
||||||
|
var widgetNode = createWidgetNode(parseTreeNode,wiki);
|
||||||
|
// Render the widget node to the DOM
|
||||||
|
var wrapper = renderWidgetNode(widgetNode);
|
||||||
|
// Test the rendering
|
||||||
|
expect(wrapper.innerHTML).toBe("<span class=\"tc-error\">Recursive transclusion error in transclude widget</span>");
|
||||||
|
});
|
||||||
|
|
||||||
it("should deal with SVG elements", function() {
|
it("should deal with SVG elements", function() {
|
||||||
var wiki = new $tw.Wiki();
|
var wiki = new $tw.Wiki();
|
||||||
// Construct the widget node
|
// Construct the widget node
|
||||||
|
@ -30,6 +30,7 @@ name: 具可读性的插件条目的名称
|
|||||||
parent-plugin: 对于一个插件,指定其为哪个插件的子插件
|
parent-plugin: 对于一个插件,指定其为哪个插件的子插件
|
||||||
plugin-priority: 插件条目的优先级数值
|
plugin-priority: 插件条目的优先级数值
|
||||||
plugin-type: 插件条目的类型
|
plugin-type: 插件条目的类型
|
||||||
|
stability: 插件的开发状态:已弃用、实验性、稳定或旧版
|
||||||
released: TiddlyWiki 的发布日期
|
released: TiddlyWiki 的发布日期
|
||||||
revision: 条目存放于服务器中的修订版本
|
revision: 条目存放于服务器中的修订版本
|
||||||
source: 条目的网址
|
source: 条目的网址
|
||||||
|
@ -30,6 +30,7 @@ name: 具可讀性的套件條目的名稱
|
|||||||
parent-plugin: 對於一個插件,指定其為哪個插件的子插件
|
parent-plugin: 對於一個插件,指定其為哪個插件的子插件
|
||||||
plugin-priority: 套件條目的優先級數值
|
plugin-priority: 套件條目的優先級數值
|
||||||
plugin-type: 套件條目的類型
|
plugin-type: 套件條目的類型
|
||||||
|
stability: 插件的開發狀態:已棄用、實驗性、穩定或舊版
|
||||||
released: TiddlyWiki 的釋出日期
|
released: TiddlyWiki 的釋出日期
|
||||||
revision: 條目存放於伺服器中的修訂版本
|
revision: 條目存放於伺服器中的修訂版本
|
||||||
source: 條目的網址
|
source: 條目的網址
|
||||||
|
Loading…
x
Reference in New Issue
Block a user