Files
TiddlyWiki5/plugins/tiddlywiki/wikitext-serialize/utils/parsetree.js
Saq Imtiaz 785086e0a5 Fixes ESLint errors (#9668)
* fix: apply automatic eslint fixes

* lint: allow hashbang comment for tiddlywiki.js

* lint: first back of manual lint fixes for unused vars

* lint: added more fixes for unused vars

* lint: missed files

* lint: updated eslint config with selected rules from #9669
2026-02-20 08:38:42 +00:00

133 lines
4.2 KiB
JavaScript

/*\
title: $:/plugins/tiddlywiki/wikitext-serialize/utils/parsetree.js
type: application/javascript
module-type: utils
Parse tree utility functions.
\*/
"use strict";
function initSerializers(Parser) {
if(Parser && !Parser.prototype.serializers) {
Parser.prototype.serializers = {};
$tw.modules.forEachModuleOfType("wikiruleserializer",function(title,module) {
var rule = module.name;
var serialize = module.serialize;
Parser.prototype.serializers[rule] = serialize;
});
}
};
/*
Utility to get the (similarly but not 1:1 equal) original wikitext of a parse tree node or array of nodes.
Based on `node.rule` metadata added in `wikiparser.js`.
*/
exports.serializeWikitextParseTree = function serializeWikitextParseTree(tree,options) {
options = options || {};
var output = [];
if($tw.utils.isArray(tree)) {
$tw.utils.each(tree,function(node) {
output.push(serializeWikitextParseTree(node,options));
});
} else if(tree) {
if(tree.type === "text" && !tree.rule) {
output.push(tree.text);
} else {
var Parser = $tw.utils.getParser("text/vnd.tiddlywiki");
// initialize the serializers only once on first use
initSerializers(Parser);
var serializeOneRule = Parser.prototype.serializers[tree.rule];
if(serializeOneRule) {
output.push(serializeOneRule(tree,serializeWikitextParseTree));
} else if(tree.rule === "parseblock") {
output.push(serializeWikitextParseTree(tree.children,options),"\n\n");
} else {
// when no rule is found, just serialize the children, for example the void nodes
output.push(serializeWikitextParseTree(tree.children,options));
}
}
}
return output.join("");
};
/*
Serialize a parsed attribute node
*/
exports.serializeAttribute = function(node,options) {
options = options || {};
if(!node || typeof node !== "object" || !node.name || !node.type) {
return null;
}
// If name is number, means it is a positional attribute and name is omitted
var positional = parseInt(node.name) >= 0,
// Use the original assignment operator if available, otherwise default to '='
assign = positional ? "" : (node.assignmentOperator || "="),
attributeString = positional ? "" : node.name;
if(node.type === "string") {
if(node.value === "true") {
return attributeString;
}
// For macro parameters (using ':' separator), preserve unquoted values
// For widget attributes (using '=' separator), always use quotes
if(assign === ":" && !node.quoted) {
attributeString += assign + node.value;
} else if(assign === "") {
// Positional parameter
if(!node.quoted) {
attributeString += node.value;
} else {
attributeString += '"' + node.value + '"';
}
} else {
attributeString += assign + '"' + node.value + '"';
}
} else if(node.type === "filtered") {
attributeString += assign + "{{{" + node.filter + "}}}";
} else if(node.type === "indirect") {
attributeString += assign + "{{" + node.textReference + "}}";
} else if(node.type === "substituted") {
attributeString += assign + "`" + node.rawValue + "`";
} else if(node.type === "macro") {
if(node.value && typeof node.value === "object") {
if(node.value.type === "transclude") {
// Handle the transclude-based macro call structure
var macroName = node.value.attributes && node.value.attributes["$variable"] ?
node.value.attributes["$variable"].value : "";
if(!macroName) {
return null;
}
var params = [];
if(node.value.orderedAttributes) {
node.value.orderedAttributes.forEach(function(attr) {
if(attr.name !== "$variable") {
var paramStr = exports.serializeAttribute(attr);
if(paramStr) {
params.push(paramStr);
}
}
});
}
attributeString += assign + "<<" + macroName + (params.length > 0 ? " " + params.join(" ") : "") + ">>";
} else if(node.value.type === "macrocall") {
// Handle the classical macrocall structure for backwards compatibility
var params = node.value.params.map(function(param) {
return param.value;
}).join(" ");
attributeString += assign + "<<" + node.value.name + " " + params + ">>";
} else {
// Unsupported macro structure
return null;
}
} else {
// Unsupported macro structure
return null;
}
} else {
// Unsupported type
return null;
}
return attributeString;
};