Skip to content

Commit bbcd61d

Browse files
committed
Switch Markdown rendering from Text::MultiMarkdown to CommonMark
Custom rendering function to add `id` attribute to headings. Fixes #2312
1 parent fbad753 commit bbcd61d

File tree

7 files changed

+121
-37
lines changed

7 files changed

+121
-37
lines changed

.github/workflows/test.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ jobs:
4141
with:
4242
node-version: "10"
4343
- run: npm install -g yarn && yarn install
44+
- run: apt-get update && apt-get -y install libcmark-dev
4445
- name: Install Carton
4546
run: >
4647
curl -sL https://git.io/cpm | perl -

cpanfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ requires 'Catalyst::TraitFor::Request::REST::ForBrowsers';
1414
requires 'Catalyst::View::JSON';
1515
requires 'Catalyst::View::Xslate';
1616
requires 'CatalystX::Fastly::Role::Response', '0.06';
17+
requires 'CommonMark';
1718
requires 'Config::General';
1819
requires 'Config::ZOMG', '1.000000';
1920
requires 'Cpanel::JSON::XS';
@@ -69,7 +70,6 @@ requires 'Plack::Test';
6970
requires 'Ref::Util', '>= 0.008';
7071
requires 'Router::Simple';
7172
requires 'Term::Size::Any';
72-
requires 'Text::MultiMarkdown';
7373
requires 'Text::Pluralize';
7474
requires 'Text::Xslate::Bridge';
7575
requires 'Text::Xslate::Bridge::Star';

cpanfile.snapshot

Lines changed: 19 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -809,6 +809,15 @@ DISTRIBUTIONS
809809
perl 5.006
810810
strict 0
811811
warnings 0
812+
CommonMark-0.290000
813+
pathname: N/NW/NWELLNHOF/CommonMark-0.290000.tar.gz
814+
provides:
815+
CommonMark 0.290000
816+
CommonMark::Node 0.290000
817+
requirements:
818+
Devel::CheckLib 0
819+
ExtUtils::MakeMaker 0
820+
perl 5.008
812821
Config-Any-0.32
813822
pathname: H/HA/HAARG/Config-Any-0.32.tar.gz
814823
provides:
@@ -1482,6 +1491,16 @@ DISTRIBUTIONS
14821491
Test::More 0.98
14831492
parent 0
14841493
perl 5.008001
1494+
Devel-CheckLib-1.16
1495+
pathname: M/MA/MATTN/Devel-CheckLib-1.16.tar.gz
1496+
provides:
1497+
Devel::CheckLib 1.16
1498+
requirements:
1499+
Exporter 0
1500+
ExtUtils::MakeMaker 0
1501+
File::Spec 0
1502+
File::Temp 0.16
1503+
perl 5.004050
14851504
Devel-Confess-0.009004
14861505
pathname: H/HA/HAARG/Devel-Confess-0.009004.tar.gz
14871506
provides:
@@ -5231,35 +5250,6 @@ DISTRIBUTIONS
52315250
Exporter 0
52325251
ExtUtils::MakeMaker 0
52335252
perl 5.006
5234-
Text-Markdown-1.000031
5235-
pathname: B/BO/BOBTFISH/Text-Markdown-1.000031.tar.gz
5236-
provides:
5237-
Text::Markdown 1.000031
5238-
requirements:
5239-
Digest::MD5 0
5240-
Encode 0
5241-
ExtUtils::MakeMaker 6.42
5242-
FindBin 0
5243-
List::MoreUtils 0
5244-
Test::Differences 0
5245-
Test::Exception 0
5246-
Test::More 0.42
5247-
Text::Balanced 0
5248-
Text-MultiMarkdown-1.000035
5249-
pathname: B/BO/BOBTFISH/Text-MultiMarkdown-1.000035.tar.gz
5250-
provides:
5251-
Text::MultiMarkdown 1.000035
5252-
requirements:
5253-
Digest::MD5 0
5254-
Encode 0
5255-
ExtUtils::MakeMaker 6.59
5256-
FindBin 0
5257-
HTML::Entities 0
5258-
List::MoreUtils 0
5259-
Test::Exception 0
5260-
Test::More 0.42
5261-
Text::Markdown v1.0.26
5262-
perl 5.008
52635253
Text-Pluralize-1.1
52645254
pathname: K/KV/KVAIL/Text-Pluralize-1.1.tar.gz
52655255
provides:

lib/MetaCPAN/Web/Controller/Feed.pm

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use HTML::Escape qw( escape_html );
99
use MetaCPAN::Web::Types qw( ArrayRef Enum HashRef Str Undef Uri );
1010
use Params::ValidationCompiler qw( validation_for );
1111
use Path::Tiny qw( path );
12-
use Text::MultiMarkdown qw( markdown );
12+
use MetaCPAN::Web::RenderUtil qw( render_markdown );
1313
use XML::FeedPP ();
1414

1515
sub recent_rss : Path('/recent.rss') Args(0) {
@@ -91,7 +91,7 @@ sub news : Private {
9191
9292
#$str =~ s{\[([^]]+)\]\(([^)]+)\)}{<a href="$2">$1</a>}g;
9393
$e{abstract} = $str;
94-
$e{abstract} = markdown($str);
94+
$e{abstract} = render_markdown($str);
9595
9696
push @entries, \%e;
9797
}

lib/MetaCPAN/Web/RenderUtil.pm

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,12 @@ use HTML::Escape qw( escape_html );
88
use HTML::Restrict ();
99
use URI ();
1010
use Digest::MD5 ();
11+
use CommonMark qw( :node :event );
1112

1213
our @EXPORT_OK = qw(
1314
filter_html
1415
gravatar_image
16+
render_markdown
1517
);
1618

1719
sub filter_html {
@@ -146,4 +148,66 @@ sub gravatar_image {
146148
$grav_id, $size // 80;
147149
}
148150

151+
my @is_leaf;
152+
$is_leaf[$_] = 1
153+
for (
154+
NODE_HTML, NODE_HRULE, NODE_CODE_BLOCK, NODE_TEXT,
155+
NODE_SOFTBREAK, NODE_LINEBREAK, NODE_CODE, NODE_INLINE_HTML,
156+
);
157+
158+
sub render_markdown {
159+
my ($markdown) = @_;
160+
161+
my $doc = CommonMark->parse_document($markdown);
162+
163+
my ( $html, $header_content, %seen_header );
164+
165+
my $iter = $doc->iterator;
166+
while ( my ( $ev_type, $node ) = $iter->next ) {
167+
my $node_type = $node->get_type;
168+
169+
if ( $node_type == NODE_DOCUMENT ) {
170+
next;
171+
}
172+
173+
if ( $node_type == NODE_HEADER ) {
174+
if ( $ev_type == EVENT_ENTER ) {
175+
$header_content = '';
176+
}
177+
if ( $ev_type == EVENT_EXIT ) {
178+
$header_content =~ s{(?:-(\d+))?$}{'-' . (($1 // 1) + 1)}e
179+
while $seen_header{$header_content}++;
180+
181+
my $header_html = $node->render_html;
182+
$header_html
183+
=~ s/^<h[0-9]+\b\K/' id="'.escape_html($header_content).'"'/e;
184+
$html .= $header_html;
185+
186+
undef $header_content;
187+
}
188+
}
189+
elsif ($ev_type == EVENT_ENTER
190+
&& $node->parent->get_type == NODE_DOCUMENT )
191+
{
192+
$html .= $node->render_html;
193+
}
194+
195+
if ( defined $header_content ) {
196+
if ( $is_leaf[$node_type] ) {
197+
my $content = lc( $node->get_literal );
198+
$content =~ s/\A\s+//;
199+
$content =~ s/\s+\z//;
200+
$content =~ s/\s+/-/g;
201+
202+
if ( length $content ) {
203+
$header_content .= '-' if length $header_content;
204+
$header_content .= $content;
205+
}
206+
}
207+
}
208+
}
209+
210+
return $html;
211+
}
212+
149213
1;

lib/MetaCPAN/Web/View/Xslate/Bridge.pm

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,11 @@ use parent qw(Text::Xslate::Bridge);
66
use Text::Xslate::Util qw( mark_raw );
77
use Number::Format ();
88
use Ref::Util qw( is_coderef is_regexpref );
9-
use Text::MultiMarkdown ();
109
use List::Util ();
1110
use DateTime ();
1211
use With::Roles ();
1312
use Text::Pluralize ();
14-
use MetaCPAN::Web::RenderUtil qw( gravatar_image );
13+
use MetaCPAN::Web::RenderUtil qw( gravatar_image render_markdown );
1514
use overload ();
1615

1716
my $num_formatter = Number::Format->new;
@@ -26,11 +25,9 @@ sub format_bytes {
2625
$num_formatter->format_bytes($number);
2726
}
2827

29-
my $md = Text::MultiMarkdown->new( heading_ids => 1 );
30-
3128
sub markdown {
3229
my ($text) = @_;
33-
mark_raw( $md->markdown($text) );
30+
mark_raw( render_markdown($text) );
3431
}
3532

3633
sub filter_html {

t/markdown.t

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
use strict;
2+
use warnings;
3+
use Test::More;
4+
5+
use MetaCPAN::Web::RenderUtil qw(render_markdown);
6+
7+
my $html = render_markdown(<<'EOM');
8+
# Heading
9+
10+
Some body text
11+
12+
## Heading
13+
14+
More stuff
15+
16+
## Heading
17+
18+
more stuff
19+
20+
## Heading **with** _markup_
21+
22+
Content
23+
24+
EOM
25+
26+
like $html, qr{<h1 id="heading">Heading</h1>}, 'first heading';
27+
like $html, qr{<h2 id="heading-2">Heading</h2>}, 'second heading';
28+
like $html, qr{<h2 id="heading-3">Heading</h2>}, 'third heading';
29+
30+
like $html, qr{<h2 id="heading-with-markup">Heading <}, 'heading with markup';
31+
32+
done_testing();

0 commit comments

Comments
 (0)