1
0
mirror of https://github.com/Jermolene/TiddlyWiki5 synced 2024-06-14 17:39:56 +00:00

Introduce JSON parse utility function with error handling (#6401)

* Introduce JSON parse utility function with error handling

Fixes #6400

* Fix typo
This commit is contained in:
Jeremy Ruston 2022-02-21 15:29:25 +00:00 committed by GitHub
parent 5378b45c40
commit 82c8fe7fa8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 48 additions and 58 deletions

View File

@ -409,6 +409,19 @@ $tw.utils.parseFields = function(text,fields) {
return fields; return fields;
}; };
// Safely parse a string as JSON
$tw.utils.parseJSONSafe = function(text,defaultJSON) {
try {
return JSON.parse(text);
} catch(e) {
if(typeof defaultJSON === "function") {
return defaultJSON(e);
} else {
return defaultJSON || {};
}
}
};
/* /*
Resolves a source filepath delimited with `/` relative to a specified absolute root filepath. Resolves a source filepath delimited with `/` relative to a specified absolute root filepath.
In relative paths, the special folder name `..` refers to immediate parent directory, and the In relative paths, the special folder name `..` refers to immediate parent directory, and the
@ -1322,7 +1335,7 @@ $tw.Wiki = function(options) {
var tiddler = tiddlers[title]; var tiddler = tiddlers[title];
if(tiddler) { if(tiddler) {
if(tiddler.fields.type === "application/json" && tiddler.hasField("plugin-type") && tiddler.fields.text) { if(tiddler.fields.type === "application/json" && tiddler.hasField("plugin-type") && tiddler.fields.text) {
pluginInfo[tiddler.fields.title] = JSON.parse(tiddler.fields.text); pluginInfo[tiddler.fields.title] = $tw.utils.parseJSONSafe(tiddler.fields.text);
results.modifiedPlugins.push(tiddler.fields.title); results.modifiedPlugins.push(tiddler.fields.title);
} }
} else { } else {
@ -1455,7 +1468,7 @@ $tw.Wiki.prototype.defineTiddlerModules = function() {
} }
break; break;
case "application/json": case "application/json":
$tw.modules.define(tiddler.fields.title,tiddler.fields["module-type"],JSON.parse(tiddler.fields.text)); $tw.modules.define(tiddler.fields.title,tiddler.fields["module-type"],$tw.utils.parseJSONSafe(tiddler.fields.text));
break; break;
case "application/x-tiddler-dictionary": case "application/x-tiddler-dictionary":
$tw.modules.define(tiddler.fields.title,tiddler.fields["module-type"],$tw.utils.parseFields(tiddler.fields.text)); $tw.modules.define(tiddler.fields.title,tiddler.fields["module-type"],$tw.utils.parseFields(tiddler.fields.text));
@ -1644,12 +1657,7 @@ $tw.modules.define("$:/boot/tiddlerdeserializer/json","tiddlerdeserializer",{
} }
return true; return true;
}, },
data = {}; data = $tw.utils.parseJSONSafe(text);
try {
data = JSON.parse(text);
} catch(e) {
// Ignore JSON parse errors
}
if($tw.utils.isArray(data) && isTiddlerArrayValid(data)) { if($tw.utils.isArray(data) && isTiddlerArrayValid(data)) {
return data; return data;
} else if(isTiddlerValid(data)) { } else if(isTiddlerValid(data)) {
@ -1689,7 +1697,7 @@ $tw.boot.decryptEncryptedTiddlers = function(callback) {
$tw.crypto.setPassword(data.password); $tw.crypto.setPassword(data.password);
var decryptedText = $tw.crypto.decrypt(encryptedText); var decryptedText = $tw.crypto.decrypt(encryptedText);
if(decryptedText) { if(decryptedText) {
var json = JSON.parse(decryptedText); var json = $tw.utils.parseJSONSafe(decryptedText);
for(var title in json) { for(var title in json) {
$tw.preloadTiddler(json[title]); $tw.preloadTiddler(json[title]);
} }
@ -1889,7 +1897,7 @@ filepath: pathname of the directory containing the specification file
$tw.loadTiddlersFromSpecification = function(filepath,excludeRegExp) { $tw.loadTiddlersFromSpecification = function(filepath,excludeRegExp) {
var tiddlers = []; var tiddlers = [];
// Read the specification // Read the specification
var filesInfo = JSON.parse(fs.readFileSync(filepath + path.sep + "tiddlywiki.files","utf8")); var filesInfo = $tw.utils.parseJSONSafe(fs.readFileSync(filepath + path.sep + "tiddlywiki.files","utf8"));
// Helper to process a file // Helper to process a file
var processFile = function(filename,isTiddlerFile,fields,isEditableFile) { var processFile = function(filename,isTiddlerFile,fields,isEditableFile) {
var extInfo = $tw.config.fileExtensionInfo[path.extname(filename)], var extInfo = $tw.config.fileExtensionInfo[path.extname(filename)],
@ -2019,7 +2027,7 @@ $tw.loadPluginFolder = function(filepath,excludeRegExp) {
console.log("Warning: missing plugin.info file in " + filepath); console.log("Warning: missing plugin.info file in " + filepath);
return null; return null;
} }
var pluginInfo = JSON.parse(fs.readFileSync(infoPath,"utf8")); var pluginInfo = $tw.utils.parseJSONSafe(fs.readFileSync(infoPath,"utf8"));
// Read the plugin files // Read the plugin files
var pluginFiles = $tw.loadTiddlersFromPath(filepath,excludeRegExp); var pluginFiles = $tw.loadTiddlersFromPath(filepath,excludeRegExp);
// Save the plugin tiddlers into the plugin info // Save the plugin tiddlers into the plugin info
@ -2136,7 +2144,7 @@ $tw.loadWikiTiddlers = function(wikiPath,options) {
pluginFields; pluginFields;
// Bail if we don't have a wiki info file // Bail if we don't have a wiki info file
if(fs.existsSync(wikiInfoPath)) { if(fs.existsSync(wikiInfoPath)) {
wikiInfo = JSON.parse(fs.readFileSync(wikiInfoPath,"utf8")); wikiInfo = $tw.utils.parseJSONSafe(fs.readFileSync(wikiInfoPath,"utf8"));
} else { } else {
return null; return null;
} }

View File

@ -48,7 +48,7 @@ Command.prototype.execute = function() {
} }
// Tweak the tiddlywiki.info to remove any included wikis // Tweak the tiddlywiki.info to remove any included wikis
var packagePath = $tw.boot.wikiPath + "/tiddlywiki.info", var packagePath = $tw.boot.wikiPath + "/tiddlywiki.info",
packageJson = JSON.parse(fs.readFileSync(packagePath)); packageJson = $tw.utils.parseJSONSafe(fs.readFileSync(packagePath));
delete packageJson.includeWikis; delete packageJson.includeWikis;
fs.writeFileSync(packagePath,JSON.stringify(packageJson,null,$tw.config.preferences.jsonSpaces)); fs.writeFileSync(packagePath,JSON.stringify(packageJson,null,$tw.config.preferences.jsonSpaces));
return null; return null;

View File

@ -69,7 +69,7 @@ Command.prototype.execute = function() {
$tw.utils.createFileDirectories(pathname); $tw.utils.createFileDirectories(pathname);
fs.writeFileSync(pathname,JSON.stringify(tiddler),"utf8"); fs.writeFileSync(pathname,JSON.stringify(tiddler),"utf8");
// Collect the skinny list data // Collect the skinny list data
var pluginTiddlers = JSON.parse(tiddler.text), var pluginTiddlers = $tw.utils.parseJSONSafe(tiddler.text),
readmeContent = (pluginTiddlers.tiddlers[title + "/readme"] || {}).text, readmeContent = (pluginTiddlers.tiddlers[title + "/readme"] || {}).text,
doesRequireReload = !!self.commander.wiki.doesPluginInfoRequireReload(pluginTiddlers), doesRequireReload = !!self.commander.wiki.doesPluginInfoRequireReload(pluginTiddlers),
iconTiddler = pluginTiddlers.tiddlers[title + "/icon"] || {}, iconTiddler = pluginTiddlers.tiddlers[title + "/icon"] || {},

View File

@ -151,7 +151,7 @@ WikiFolderMaker.prototype.saveCustomPlugin = function(pluginTiddler) {
pluginInfo = pluginTiddler.getFieldStrings({exclude: ["text","type"]}); pluginInfo = pluginTiddler.getFieldStrings({exclude: ["text","type"]});
this.saveJSONFile(directory + path.sep + "plugin.info",pluginInfo); this.saveJSONFile(directory + path.sep + "plugin.info",pluginInfo);
self.log("Writing " + directory + path.sep + "plugin.info: " + JSON.stringify(pluginInfo,null,$tw.config.preferences.jsonSpaces)); self.log("Writing " + directory + path.sep + "plugin.info: " + JSON.stringify(pluginInfo,null,$tw.config.preferences.jsonSpaces));
var pluginTiddlers = JSON.parse(pluginTiddler.fields.text).tiddlers; // A hashmap of tiddlers in the plugin var pluginTiddlers = $tw.utils.parseJSONSafe(pluginTiddler.fields.text).tiddlers; // A hashmap of tiddlers in the plugin
$tw.utils.each(pluginTiddlers,function(tiddler) { $tw.utils.each(pluginTiddlers,function(tiddler) {
self.saveTiddler(directory,new $tw.Tiddler(tiddler)); self.saveTiddler(directory,new $tw.Tiddler(tiddler));
}); });

View File

@ -17,16 +17,13 @@ exports["application/x-tiddler-html-div"] = function(text,fields) {
}; };
exports["application/json"] = function(text,fields) { exports["application/json"] = function(text,fields) {
var incoming, var results = [],
results = []; incoming = $tw.utils.parseJSONSafe(text,function(err) {
try { return [{
incoming = JSON.parse(text); title: "JSON error: " + err,
} catch(e) { text: ""
incoming = [{ }];
title: "JSON error: " + e, });
text: ""
}]
}
if(!$tw.utils.isArray(incoming)) { if(!$tw.utils.isArray(incoming)) {
incoming = [incoming]; incoming = [incoming];
} }

View File

@ -52,7 +52,7 @@ PluginSwitcher.prototype.switchPlugins = function() {
var tiddler = self.wiki.getTiddler(title); var tiddler = self.wiki.getTiddler(title);
if(tiddler && tiddler.isPlugin() && plugins.indexOf(title) === -1) { if(tiddler && tiddler.isPlugin() && plugins.indexOf(title) === -1) {
plugins.push(title); plugins.push(title);
var pluginInfo = JSON.parse(self.wiki.getTiddlerText(title)), var pluginInfo = $tw.utils.parseJSONSafe(self.wiki.getTiddlerText(title)),
dependents = $tw.utils.parseStringArray(tiddler.fields.dependents || ""); dependents = $tw.utils.parseStringArray(tiddler.fields.dependents || "");
$tw.utils.each(dependents,function(title) { $tw.utils.each(dependents,function(title) {
accumulatePlugin(title); accumulatePlugin(title);

View File

@ -61,7 +61,7 @@ GiteaSaver.prototype.save = function(text,method,callback) {
} }
var use_put = true; var use_put = true;
if(xhr.status !== 404) { if(xhr.status !== 404) {
getResponseData = JSON.parse(getResponseDataJson); getResponseData = $tw.utils.parseJSONSafe(getResponseDataJson);
$tw.utils.each(getResponseData,function(details) { $tw.utils.each(getResponseData,function(details) {
if(details.name === filename) { if(details.name === filename) {
sha = details.sha; sha = details.sha;
@ -104,7 +104,7 @@ GiteaSaver.prototype.upload = function(uri,method,headers,data,callback) {
if(err) { if(err) {
return callback(err); return callback(err);
} }
var putResponseData = JSON.parse(putResponseDataJson); var putResponseData = $tw.utils.parseJSONSafe(putResponseDataJson);
callback(null); callback(null);
} }
}); });

View File

@ -61,7 +61,7 @@ GitHubSaver.prototype.save = function(text,method,callback) {
return callback(err); return callback(err);
} }
if(xhr.status !== 404) { if(xhr.status !== 404) {
getResponseData = JSON.parse(getResponseDataJson); getResponseData = $tw.utils.parseJSONSafe(getResponseDataJson);
$tw.utils.each(getResponseData,function(details) { $tw.utils.each(getResponseData,function(details) {
if(details.name === filename) { if(details.name === filename) {
sha = details.sha; sha = details.sha;
@ -84,7 +84,7 @@ GitHubSaver.prototype.save = function(text,method,callback) {
if(err) { if(err) {
return callback(err); return callback(err);
} }
var putResponseData = JSON.parse(putResponseDataJson); var putResponseData = $tw.utils.parseJSONSafe(putResponseDataJson);
callback(null); callback(null);
} }
}); });

View File

@ -58,7 +58,7 @@ GitLabSaver.prototype.save = function(text,method,callback) {
} }
var requestType = "POST"; var requestType = "POST";
if(xhr.status !== 404) { if(xhr.status !== 404) {
getResponseData = JSON.parse(getResponseDataJson); getResponseData = $tw.utils.parseJSONSafe(getResponseDataJson);
$tw.utils.each(getResponseData,function(details) { $tw.utils.each(getResponseData,function(details) {
if(details.name === filename) { if(details.name === filename) {
requestType = "PUT"; requestType = "PUT";
@ -82,7 +82,7 @@ GitLabSaver.prototype.save = function(text,method,callback) {
if(err) { if(err) {
return callback(err); return callback(err);
} }
var putResponseData = JSON.parse(putResponseDataJson); var putResponseData = $tw.utils.parseJSONSafe(putResponseDataJson);
callback(null); callback(null);
} }
}); });

View File

@ -18,7 +18,7 @@ exports.path = /^\/recipes\/default\/tiddlers\/(.+)$/;
exports.handler = function(request,response,state) { exports.handler = function(request,response,state) {
var title = $tw.utils.decodeURIComponentSafe(state.params[0]), var title = $tw.utils.decodeURIComponentSafe(state.params[0]),
fields = JSON.parse(state.data); fields = $tw.utils.parseJSONSafe(state.data);
// Pull up any subfields in the `fields` object // Pull up any subfields in the `fields` object
if(fields.fields) { if(fields.fields) {
$tw.utils.each(fields.fields,function(field,name) { $tw.utils.each(fields.fields,function(field,name) {

View File

@ -152,7 +152,7 @@ exports.startup = function() {
if(event.data.status.charAt(0) === "2") { if(event.data.status.charAt(0) === "2") {
if(event.data.cookies) { if(event.data.cookies) {
if(event.data.cookies.type === "save-info") { if(event.data.cookies.type === "save-info") {
var tiddlers = JSON.parse(event.data.body); var tiddlers = $tw.utils.parseJSONSafe(event.data.body);
$tw.utils.each(tiddlers,function(tiddler) { $tw.utils.each(tiddlers,function(tiddler) {
$tw.wiki.addTiddler(new $tw.Tiddler($tw.wiki.getCreationFields(),tiddler,{ $tw.wiki.addTiddler(new $tw.Tiddler($tw.wiki.getCreationFields(),tiddler,{
title: event.data.cookies.infoTitlePrefix + event.data.cookies.url + "/" + tiddler.title, title: event.data.cookies.infoTitlePrefix + event.data.cookies.url + "/" + tiddler.title,
@ -170,7 +170,7 @@ exports.startup = function() {
},$tw.wiki.getModificationFields())); },$tw.wiki.getModificationFields()));
}); });
} else if(event.data.cookies.type === "save-tiddler") { } else if(event.data.cookies.type === "save-tiddler") {
var tiddler = JSON.parse(event.data.body); var tiddler = $tw.utils.parseJSONSafe(event.data.body);
$tw.wiki.addTiddler(new $tw.Tiddler(tiddler)); $tw.wiki.addTiddler(new $tw.Tiddler(tiddler));
} }
} }

View File

@ -41,7 +41,7 @@ exports.upgrade = function(wiki,titles,tiddlers) {
// Check if we're dealing with a plugin // Check if we're dealing with a plugin
if(incomingTiddler && incomingTiddler["plugin-type"]) { if(incomingTiddler && incomingTiddler["plugin-type"]) {
// Check whether the plugin contains JS modules // Check whether the plugin contains JS modules
var requiresReload = wiki.doesPluginInfoRequireReload(JSON.parse(incomingTiddler.text)) ? (wiki.getTiddlerText("$:/language/ControlPanel/Plugins/PluginWillRequireReload") + " ") : ""; var requiresReload = wiki.doesPluginInfoRequireReload($tw.utils.parseJSONSafe(incomingTiddler.text)) ? (wiki.getTiddlerText("$:/language/ControlPanel/Plugins/PluginWillRequireReload") + " ") : "";
messages[title] = requiresReload; messages[title] = requiresReload;
if(incomingTiddler.version) { if(incomingTiddler.version) {
// Upgrade the incoming plugin if it is in the upgrade library // Upgrade the incoming plugin if it is in the upgrade library

View File

@ -33,7 +33,7 @@ Attempt to extract the tiddlers from an encrypted store area using the current p
exports.decryptStoreArea = function(encryptedStoreArea,password) { exports.decryptStoreArea = function(encryptedStoreArea,password) {
var decryptedText = $tw.crypto.decrypt(encryptedStoreArea,password); var decryptedText = $tw.crypto.decrypt(encryptedStoreArea,password);
if(decryptedText) { if(decryptedText) {
var json = JSON.parse(decryptedText), var json = $tw.utils.parseJSONSafe(decryptedText),
tiddlers = []; tiddlers = [];
for(var title in json) { for(var title in json) {
if(title !== "$:/isEncrypted") { if(title !== "$:/isEncrypted") {

View File

@ -198,7 +198,7 @@ var importDataTypes = [
]; ];
function parseJSONTiddlers(json,fallbackTitle) { function parseJSONTiddlers(json,fallbackTitle) {
var data = JSON.parse(json); var data = $tw.utils.parseJSONSafe(json);
if(!$tw.utils.isArray(data)) { if(!$tw.utils.isArray(data)) {
data = [data]; data = [data];
} }

View File

@ -30,11 +30,7 @@ exports.getEditionInfo = function() {
var entry = entries[entryIndex]; var entry = entries[entryIndex];
// Check if directories have a valid tiddlywiki.info // Check if directories have a valid tiddlywiki.info
if(!editionInfo[entry] && $tw.utils.isDirectory(path.resolve(editionPath,entry))) { if(!editionInfo[entry] && $tw.utils.isDirectory(path.resolve(editionPath,entry))) {
var info; var info = $tw.utils.parseJSONSafe(fs.readFileSync(path.resolve(editionPath,entry,"tiddlywiki.info"),"utf8"),null);
try {
info = JSON.parse(fs.readFileSync(path.resolve(editionPath,entry,"tiddlywiki.info"),"utf8"));
} catch(ex) {
}
if(info) { if(info) {
editionInfo[entry] = info; editionInfo[entry] = info;
} }

View File

@ -24,10 +24,8 @@ exports.repackPlugin = function(title,additionalTiddlers,excludeTiddlers) {
throw "No such tiddler as " + title; throw "No such tiddler as " + title;
} }
// Extract the JSON // Extract the JSON
var jsonPluginTiddler; var jsonPluginTiddler = $tw.utils.parseJSONSafe(pluginTiddler.fields.text,null);
try { if(!jsonPluginTiddler) {
jsonPluginTiddler = JSON.parse(pluginTiddler.fields.text);
} catch(e) {
throw "Cannot parse plugin tiddler " + title + "\n" + $tw.language.getString("Error/Caption") + ": " + e; throw "Cannot parse plugin tiddler " + title + "\n" + $tw.language.getString("Error/Caption") + ": " + e;
} }
// Get the list of tiddlers // Get the list of tiddlers

View File

@ -498,11 +498,7 @@ NavigatorWidget.prototype.handleNewTiddlerEvent = function(event) {
// Import JSON tiddlers into a pending import tiddler // Import JSON tiddlers into a pending import tiddler
NavigatorWidget.prototype.handleImportTiddlersEvent = function(event) { NavigatorWidget.prototype.handleImportTiddlersEvent = function(event) {
// Get the tiddlers // Get the tiddlers
var tiddlers = []; var tiddlers = $tw.utils.parseJSONSafe(event.param,[]);
try {
tiddlers = JSON.parse(event.param);
} catch(e) {
}
// Get the current $:/Import tiddler // Get the current $:/Import tiddler
var importTitle = event.importTitle ? event.importTitle : IMPORT_TITLE, var importTitle = event.importTitle ? event.importTitle : IMPORT_TITLE,
importTiddler = this.wiki.getTiddler(importTitle), importTiddler = this.wiki.getTiddler(importTitle),

View File

@ -833,12 +833,7 @@ exports.getTiddlerData = function(titleOrTiddler,defaultData) {
switch(tiddler.fields.type) { switch(tiddler.fields.type) {
case "application/json": case "application/json":
// JSON tiddler // JSON tiddler
try { return $tw.utils.parseJSONSafe(tiddler.fields.text,defaultData);
data = JSON.parse(tiddler.fields.text);
} catch(ex) {
return defaultData;
}
return data;
case "application/x-tiddler-dictionary": case "application/x-tiddler-dictionary":
return $tw.utils.parseFields(tiddler.fields.text); return $tw.utils.parseFields(tiddler.fields.text);
} }