Skip to content

Commit b41ed92

Browse files
zkoppertCopilot
andcommitted
Upgrade commonmarker to ~> 2.8.2
Commonmarker 2.x is a from-scratch rewrite that swaps the upstream parser from cmark-gfm (C) to comrak (Rust). It also redesigns the Ruby API: - Module: CommonMarker -> Commonmarker (lowercase m) - Render entry point: CommonMarker.render_html(content, opts, exts) -> Commonmarker.to_html(content, options: {parse:, render:, extension:}) - Symbol arrays of opts/exts -> nested option hashes with snake_case keys - :FOOTNOTES moved from a parse opt to an extension - A bundled syntax highlighter plugin is enabled by default The migration preserves the legacy public contract: callers can still pass `commonmarker_opts:` and `commonmarker_exts:` as symbol arrays. The wrapper translates each legacy symbol into the new nested hash structure. Several behaviors had to be pinned explicitly to preserve cmark-gfm 0.x output: - hardbreaks defaults to true in 2.x; set false to match cmark. - tagfilter, autolink, table, strikethrough, tasklist, and shortcodes are extensions that default to on in 2.x but were strictly opt-in in 0.x; explicitly disable any the caller did not request. - header_ids is on by default in 2.x and injects an empty anchor inside every heading; explicitly disable it unless the caller requested it. - The syntax_highlighter plugin is disabled (plugins: {syntax_highlighter: nil}) to keep `<pre lang>` blocks clean. Closes #2059 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Signed-off-by: Zack Koppert <zkoppert@github.com>
1 parent be33798 commit b41ed92

3 files changed

Lines changed: 96 additions & 17 deletions

File tree

Gemfile

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,7 @@ gemspec
44
gem "redcarpet", :platforms => :ruby
55
gem "kramdown", :platforms => :jruby
66
gem "RedCloth"
7-
# using a tag version here because 0.18.3 was not published by the author to encourage users to upgrade.
8-
# however we want to bump up to this version since this has a security patch
9-
gem "commonmarker", git: "https://github.com/gjtorikian/commonmarker.git", tag: "v0.18.3"
7+
gem "commonmarker", "~> 2.8.2"
108
gem "rdoc", "~> 7.2.0"
119
gem "org-ruby", "0.9.12"
1210
gem "creole", "~>0.5.0"

Gemfile.lock

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,3 @@
1-
GIT
2-
remote: https://github.com/gjtorikian/commonmarker.git
3-
revision: 2838ebaa83ee0081d481c21f3bc0e4cb3e8de9da
4-
tag: v0.18.3
5-
specs:
6-
commonmarker (0.18.3)
7-
ruby-enum (~> 0.5)
8-
91
PATH
102
remote: .
113
specs:
@@ -34,6 +26,13 @@ GEM
3426
builder (3.3.0)
3527
cgi (0.5.1)
3628
charlock_holmes (0.7.9)
29+
commonmarker (2.8.2)
30+
rb_sys (~> 0.9)
31+
commonmarker (2.8.2-aarch64-linux)
32+
commonmarker (2.8.2-arm-linux)
33+
commonmarker (2.8.2-arm64-darwin)
34+
commonmarker (2.8.2-x86_64-darwin)
35+
commonmarker (2.8.2-x86_64-linux)
3736
concurrent-ruby (1.3.6)
3837
connection_pool (3.0.2)
3938
crass (1.0.6)
@@ -85,14 +84,15 @@ GEM
8584
stringio
8685
racc (1.8.1)
8786
rake (13.4.2)
87+
rake-compiler-dock (1.12.0)
88+
rb_sys (0.9.128)
89+
rake-compiler-dock (= 1.12.0)
8890
rdoc (7.2.0)
8991
erb
9092
psych (>= 4.0.0)
9193
tsort
9294
redcarpet (3.6.1)
9395
rexml (3.4.4)
94-
ruby-enum (0.9.0)
95-
i18n
9696
rubypants (0.7.1)
9797
rugged (1.9.0)
9898
sanitize (6.1.3)
@@ -135,7 +135,7 @@ DEPENDENCIES
135135
RedCloth
136136
activesupport (~> 8.1.3)
137137
asciidoctor (~> 2.0.26)
138-
commonmarker!
138+
commonmarker (~> 2.8.2)
139139
creole (~> 0.5.0)
140140
github-linguist (>= 7.1.3)
141141
github-markup!

lib/github/markup/markdown.rb

Lines changed: 84 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,90 @@ module Markup
55
class Markdown < Implementation
66
MARKDOWN_GEMS = {
77
"commonmarker" => proc { |content, options: {}|
8-
commonmarker_opts = [:GITHUB_PRE_LANG].concat(options.fetch(:commonmarker_opts, []))
9-
commonmarker_exts = options.fetch(:commonmarker_exts, [:tagfilter, :autolink, :table, :strikethrough])
10-
CommonMarker.render_html(content, commonmarker_opts, commonmarker_exts)
8+
legacy_opts = options.fetch(:commonmarker_opts, [])
9+
legacy_exts = options.fetch(
10+
:commonmarker_exts,
11+
[:tagfilter, :autolink, :table, :strikethrough],
12+
)
13+
14+
parse_options = {}
15+
# commonmarker 2.x changes several render defaults that diverge from cmark-gfm 0.x:
16+
# - hardbreaks defaults to true in 2.x but was false in 0.x.
17+
# - escaped_char_spans defaults to true in 2.x and wraps backslash-escaped chars in
18+
# <span data-escaped-char>; 0.x emitted bare characters.
19+
# - gfm_quirks defaults to false in 2.x; 0.x (cmark-gfm) always had the quirk on,
20+
# which collapses ****foo**** to <strong>foo</strong> instead of nesting.
21+
# - github_pre_lang defaults to true in 2.x; set explicitly to match the legacy contract.
22+
render_options = {
23+
github_pre_lang: true,
24+
hardbreaks: false,
25+
escaped_char_spans: false,
26+
gfm_quirks: true,
27+
}
28+
extension_options = {}
29+
30+
legacy_opts.each do |opt|
31+
case opt
32+
when :DEFAULT then nil
33+
when :SOURCEPOS then render_options[:sourcepos] = true
34+
when :HARDBREAKS then render_options[:hardbreaks] = true
35+
when :NOBREAKS then render_options[:hardbreaks] = false
36+
when :SMART then parse_options[:smart] = true
37+
when :GITHUB_PRE_LANG then render_options[:github_pre_lang] = true
38+
when :UNSAFE then render_options[:unsafe] = true
39+
when :FOOTNOTES then extension_options[:footnotes] = true
40+
when :FULL_INFO_STRING then render_options[:full_info_string] = true
41+
# The legacy options below existed in cmark-gfm 0.x but have no direct commonmarker
42+
# 2.x equivalent. Accept them so existing callers don't break, but they have no effect:
43+
# :VALIDATE_UTF8 / :LIBERAL_HTML_TAG - enforced at the Rust type layer in 2.x.
44+
# :TABLE_PREFER_STYLE_ATTRIBUTES - no 2.x render knob for inline table styles.
45+
# :STRIKETHROUGH_DOUBLE_TILDE - 2.x always accepts both single and double tilde.
46+
when :VALIDATE_UTF8, :LIBERAL_HTML_TAG,
47+
:TABLE_PREFER_STYLE_ATTRIBUTES, :STRIKETHROUGH_DOUBLE_TILDE
48+
nil
49+
else
50+
raise ArgumentError, "unknown commonmarker option: #{opt.inspect}"
51+
end
52+
end
53+
54+
legacy_exts.each do |ext|
55+
case ext
56+
when :strikethrough, :tagfilter, :autolink, :table, :tasklist,
57+
:shortcodes, :footnotes, :multiline_block_quotes,
58+
:math_dollars, :math_code, :wikilinks_title_after_pipe,
59+
:wikilinks_title_before_pipe, :underline, :subscript, :spoiler,
60+
:greentext, :alerts, :description_lists
61+
extension_options[ext] = true
62+
when :header_ids
63+
# header_ids takes a string prefix in 2.x rather than a boolean. The legacy contract
64+
# only passed it as a symbol, so use an empty prefix to enable anchor generation.
65+
extension_options[:header_ids] = ""
66+
else
67+
raise ArgumentError, "unknown commonmarker extension: #{ext.inspect}"
68+
end
69+
end
70+
71+
# Several extensions (tagfilter, autolink, table, strikethrough, tasklist, shortcodes)
72+
# are enabled by default in commonmarker 2.x but were strictly opt-in in 0.x. Explicitly
73+
# disable any extension the caller did not request so behavior matches the legacy contract.
74+
[:strikethrough, :tagfilter, :autolink, :table, :tasklist, :shortcodes].each do |ext|
75+
extension_options[ext] = false unless extension_options[ext]
76+
end
77+
78+
# header_ids is enabled by default in commonmarker 2.x (it injects anchor tags inside
79+
# every heading). The legacy 0.x wrapper never enabled it implicitly, so disable it
80+
# unless the caller explicitly requested it.
81+
extension_options[:header_ids] = nil unless extension_options.key?(:header_ids)
82+
83+
Commonmarker.to_html(
84+
content,
85+
options: {
86+
parse: parse_options,
87+
render: render_options,
88+
extension: extension_options,
89+
},
90+
plugins: {syntax_highlighter: nil},
91+
)
1192
},
1293
"github/markdown" => proc { |content, options: {}|
1394
GitHub::Markdown.render(content)

0 commit comments

Comments
 (0)