From bb67a90673511209e8fc133b29056009026da28a Mon Sep 17 00:00:00 2001 From: Ferdinand Thiessen Date: Wed, 22 Jun 2022 20:48:51 +0200 Subject: [PATCH 1/2] Serialization: Do not escape Autolinks As stated in the Commonmark specs backslash escapes do not work in Autolinks, so this prevents escaping Autolinks and should resolve #65. --- src/to_markdown.ts | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/to_markdown.ts b/src/to_markdown.ts index 52b88e0..46eff2c 100644 --- a/src/to_markdown.ts +++ b/src/to_markdown.ts @@ -112,18 +112,21 @@ export const defaultMarkdownSerializer = new MarkdownSerializer({ } }, text(state, node) { - state.text(node.text!) + state.text(node.text!, !state.isAutolink) } }, { em: {open: "*", close: "*", mixable: true, expelEnclosingWhitespace: true}, strong: {open: "**", close: "**", mixable: true, expelEnclosingWhitespace: true}, link: { open(_state, mark, parent, index) { - return isPlainURL(mark, parent, index, 1) ? "<" : "[" + _state.isAutolink = isPlainURL(mark, parent, index, 1) + return _state.isAutolink ? "<" : "[" }, close(_state, mark, parent, index) { - return isPlainURL(mark, parent, index, -1) ? ">" + const cont = _state.isAutolink ? ">" : "](" + mark.attrs.href + (mark.attrs.title ? ' "' + mark.attrs.title.replace(/"/g, '\\"') + '"' : "") + ")" + _state.isAutolink = undefined + return cont } }, code: {open(_state, _mark, parent, index) { return backticksFor(parent.child(index), -1) }, @@ -159,6 +162,8 @@ export class MarkdownSerializerState { out: string = "" /// @internal closed: Node | null = null + /// @intermal + isAutolink?: boolean = undefined /// @internal inTightList: boolean = false From f7fa5f145ab0baf183908743c94bdc35b621cec1 Mon Sep 17 00:00:00 2001 From: Ferdinand Thiessen Date: Wed, 22 Jun 2022 21:10:20 +0200 Subject: [PATCH 2/2] Add tests to validate URLs are not backslash escaped --- test/test-parse.ts | 70 ++++++++++++++++++++++++++++------------------ 1 file changed, 43 insertions(+), 27 deletions(-) diff --git a/test/test-parse.ts b/test/test-parse.ts index 5431be7..9c419bb 100644 --- a/test/test-parse.ts +++ b/test/test-parse.ts @@ -150,39 +150,55 @@ describe("markdown", () => { }) it("doesn't put a code block after a list item inside the list item", () => - same("* list item\n\n```\ncode\n```", - doc(ul({tight: true}, li(p("list item"))), pre("code")))) + same("* list item\n\n```\ncode\n```", + doc(ul({tight: true}, li(p("list item"))), pre("code"))) + ) it("doesn't escape characters in code", () => - same("foo`*`", doc(p("foo", code("*"))))) + same("foo`*`", doc(p("foo", code("*")))) + ) it("doesn't escape underscores between word characters", () => - same( - "abc_def", - doc(p("abc_def")) - ) + same( + "abc_def", + doc(p("abc_def")) + ) ) - it("doesn't escape strips of underscores between word characters", () => - same( - "abc___def", - doc(p("abc___def")) - ) - ) - - it("escapes underscores at word boundaries", () => - same( - "\\_abc\\_", - doc(p("_abc_")) - ) - ) - - it("escapes underscores surrounded by non-word characters", () => - same( - "/\\_abc\\_)", - doc(p("/_abc_)")) - ) - ) + it("doesn't escape strips of underscores between word characters", () => + same( + "abc___def", + doc(p("abc___def")) + ) + ) + + it("escapes underscores at word boundaries", () => + same( + "\\_abc\\_", + doc(p("_abc_")) + ) + ) + + it("escapes underscores surrounded by non-word characters", () => + same( + "/\\_abc\\_)", + doc(p("/_abc_)")) + ) + ) + + it("ensure no escapes in url", () => + parse( + "[text](https://example.com/_file/#~anchor)", + doc(p(a({href: "https://example.com/_file/#~anchor"}, "text"))) + ) + ) + + it("ensure no escapes in autolinks", () => + same( + "", + doc(p(a({href: "https://example.com/_file/#~anchor"}, "https://example.com/_file/#~anchor"))) + ) + ) it("escapes extra characters from options", () => { let markdownSerializer = new MarkdownSerializer(defaultMarkdownSerializer.nodes,