Skip to content

Commit eae0e33

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

File tree

7 files changed

+121
-33
lines changed

7 files changed

+121
-33
lines changed

.github/workflows/test.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ jobs:
3333
with:
3434
node-version: "18"
3535
- run: npm install -g yarn && yarn install
36+
- run: apt-get update && apt-get -y install libcmark-dev
3637
- name: Install Carton
3738
uses: perl-actions/install-with-cpm@stable
3839
with:

cpanfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ requires 'Catalyst::TraitFor::Request::REST';
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 & 26 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.33
813822
pathname: H/HA/HAARG/Config-Any-0.33.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:
@@ -5440,32 +5459,6 @@ DISTRIBUTIONS
54405459
Exporter 0
54415460
ExtUtils::MakeMaker 0
54425461
perl 5.006
5443-
Text-Markdown-1.000031
5444-
pathname: B/BO/BOBTFISH/Text-Markdown-1.000031.tar.gz
5445-
provides:
5446-
Text::Markdown 1.000031
5447-
requirements:
5448-
Digest::MD5 0
5449-
Encode 0
5450-
ExtUtils::MakeMaker 6.42
5451-
FindBin 0
5452-
List::MoreUtils 0
5453-
Test::Differences 0
5454-
Test::Exception 0
5455-
Test::More 0.42
5456-
Text::Balanced 0
5457-
Text-MultiMarkdown-1.002
5458-
pathname: B/BD/BDFOY/Text-MultiMarkdown-1.002.tar.gz
5459-
provides:
5460-
Text::MultiMarkdown 1.002
5461-
requirements:
5462-
Digest::MD5 0
5463-
Encode 0
5464-
ExtUtils::MakeMaker 6.64
5465-
File::Spec::Functions 0
5466-
HTML::Entities 0
5467-
Text::Markdown v1.0.26
5468-
perl 5.008
54695462
Text-Pluralize-1.1
54705463
pathname: K/KV/KVAIL/Text-Pluralize-1.1.tar.gz
54715464
provides:

lib/MetaCPAN/Web/Controller/Feed.pm

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

@@ -100,7 +100,7 @@ sub news : Private {
100100
101101
#$str =~ s{\[([^]]+)\]\(([^)]+)\)}{<a href="$2">$1</a>}g;
102102
$e{abstract} = $str;
103-
$e{abstract} = markdown($str);
103+
$e{abstract} = render_markdown($str);
104104
105105
push @entries, \%e;
106106
}

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
split_index
1618
);
1719

@@ -161,4 +163,66 @@ sub split_index {
161163
return ( $pod_index, $html );
162164
}
163165

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

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

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,12 @@ 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 ();
1413
use MetaCPAN::Web::RenderUtil qw( gravatar_image ); ## no perlimports
14+
use MetaCPAN::Web::RenderUtil qw( render_markdown );
1515
use overload ();
1616

1717
my $num_formatter = Number::Format->new;
@@ -26,11 +26,9 @@ sub format_bytes {
2626
$num_formatter->format_bytes($number);
2727
}
2828

29-
my $md = Text::MultiMarkdown->new( heading_ids => 1 );
30-
3129
sub markdown {
3230
my ($text) = @_;
33-
mark_raw( $md->markdown($text) );
31+
mark_raw( render_markdown($text) );
3432
}
3533

3634
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)