Commit d302d07 for mammothjs
commit d302d0755e075811a37268f4cef00e7c54cb409a
Author: Michael Williamson <mike@zwobble.org>
Date: Sat Sep 26 22:28:05 2026 +0100
Escape HTML IDs in markdown writer
diff --git a/NEWS b/NEWS
index 3d9324a..a820136 100644
--- a/NEWS
+++ b/NEWS
@@ -17,6 +17,8 @@
* Handle paragraphs and runs that have been moved and tracked as a revision.
+* Escape HTML IDs in Markdown writer.
+
# 1.12.3
* Avoid excessive backtracking when parsing an unterminated string with many
diff --git a/lib/writers/markdown-writer.js b/lib/writers/markdown-writer.js
index a89ad9f..a5b572e 100644
--- a/lib/writers/markdown-writer.js
+++ b/lib/writers/markdown-writer.js
@@ -1,5 +1,6 @@
var _ = require("underscore");
+var htmlWriter = require("./html-writer");
function symmetricMarkdownElement(end) {
return markdownElement(end, end);
@@ -52,10 +53,10 @@ function markdownListItem(attributes, list, listItem) {
list = list || {indent: 0, isOrdered: false, count: 0};
list.count++;
listItem.hasClosed = false;
-
+
var bullet = list.isOrdered ? list.count + "." : "-";
var start = repeatString("\t", list.indent) + bullet + " ";
-
+
return {
start: start,
end: function() {
@@ -94,20 +95,20 @@ function markdownWriter() {
var elementStack = [];
var list = null;
var listItem = {};
-
+
function open(tagName, attributes) {
attributes = attributes || {};
-
+
var createElement = htmlToMarkdown[tagName] || function() {
return {};
};
var element = createElement(attributes, list, listItem);
elementStack.push({end: element.end, list: list});
-
+
if (element.list) {
list = element.list;
}
-
+
var anchorBeforeStart = element.anchorPosition === "before";
if (anchorBeforeStart) {
writeAnchor(attributes);
@@ -118,29 +119,32 @@ function markdownWriter() {
writeAnchor(attributes);
}
}
-
+
function writeAnchor(attributes) {
if (attributes.id) {
- fragments.push('<a id="' + attributes.id + '"></a>');
+ var writer = htmlWriter.writer();
+ writer.open("a", {id: attributes.id});
+ writer.close("a");
+ fragments.push(writer.asString());
}
}
-
+
function close(tagName) {
var element = elementStack.pop();
list = element.list;
var end = _.isFunction(element.end) ? element.end() : element.end;
fragments.push(end || "");
}
-
+
function selfClosing(tagName, attributes) {
open(tagName, attributes);
close(tagName);
}
-
+
function text(value) {
fragments.push(escapeMarkdown(value));
}
-
+
function asString() {
return fragments.join("");
}
diff --git a/test/writers/markdown-writer.tests.js b/test/writers/markdown-writer.tests.js
index 6d078c5..e7ab9e1 100644
--- a/test/writers/markdown-writer.tests.js
+++ b/test/writers/markdown-writer.tests.js
@@ -90,6 +90,14 @@ test('elements with IDs have anchor tags with IDs appended to start of markdown
return assert.equal(writer.asString(), '# <a id="start"></a>Hello\n\n');
});
+test('anchor tag IDs are escaped', function() {
+ var writer = mdWriter.writer();
+ writer.open("h1", {id: "\"start\""});
+ writer.text("Hello");
+ writer.close("h1");
+ return assert.equal(writer.asString(), '# <a id=""start""></a>Hello\n\n');
+});
+
test('links have anchors before opening square bracket', function() {
var writer = mdWriter.writer();
writer.open("a", {href: "http://example.com", id: "start"});