diff --git a/Changes b/Changes index c5dd6a0..65eab37 100644 --- a/Changes +++ b/Changes @@ -1,5 +1,9 @@ Revision history for pmarkdown and the Markdown::Perl module. +1.08 - ? + + - Add a new yaml_metadata hook to receive the parsed YAML. + 1.07 - 2024-05-25 - Allow to specify a link content through the resolve_link_ref hook. diff --git a/lib/Markdown/Perl.pm b/lib/Markdown/Perl.pm index cb8d777..1722666 100644 --- a/lib/Markdown/Perl.pm +++ b/lib/Markdown/Perl.pm @@ -50,7 +50,7 @@ sub set_mode { return; } -Readonly::Array my @VALID_HOOKS => qw(resolve_link_ref); +Readonly::Array my @VALID_HOOKS => qw(resolve_link_ref yaml_metadata); sub set_hooks { my ($this, %hooks) = &_get_this_and_args; ## no critic (ProhibitAmpersandSigils) @@ -324,6 +324,13 @@ optionally a C key containing the title of the key. The hash-ref can also contain a C<content> key, in which case its value should be a span of HTML which will replace whatever would have been used for the link content. +=item * + +C<yaml_metadata>: this hook will trigger if there is valid (!) YAML metadata in +the file and will give you a hashref as an argument. If the hook throws a die(), +the Markdown parsing will stop as the die() needs to be handled by your code. +This allows for conditional parsing based on values in the metadata section. + =back =head1 AUTHOR diff --git a/lib/Markdown/Perl/BlockParser.pm b/lib/Markdown/Perl/BlockParser.pm index f72de83..7374ad5 100644 --- a/lib/Markdown/Perl/BlockParser.pm +++ b/lib/Markdown/Perl/BlockParser.pm @@ -137,7 +137,6 @@ sub process { # Done at a later stage, as escaped characters don’t have their Markdown # meaning, we need a way to represent that. - # Note: for now, nothing is done with the extracted metadata. $this->_parse_yaml_metadata() if $this->get_parse_file_metadata eq 'yaml'; while (defined (my $l = $this->next_line())) { @@ -372,11 +371,14 @@ sub _parse_yaml_metadata { my $metadata = eval { YAML::Tiny->read_string($+{YAML}) }; if ($EVAL_ERROR) { pos($this->{md}) = 0; - return; + carp 'YAML Metadata (Markdown frontmatter) is invalid.'; + return 1; + } + if(exists($this->{pmarkdown}{hooks}{yaml_metadata})) { + $this->{pmarkdown}{hooks}{yaml_metadata}->($metadata->[0]); } } - - return; + return 1; } # https://spec.commonmark.org/0.30/#atx-headings diff --git a/lib/Markdown/Perl/Options.pm b/lib/Markdown/Perl/Options.pm index 127c771..bde11c4 100644 --- a/lib/Markdown/Perl/Options.pm +++ b/lib/Markdown/Perl/Options.pm @@ -181,9 +181,8 @@ sub _word_list { =head3 B<parse_file_metadata> I<(enum, default: yaml)> This option controls whether the parser accepts optional metadata at the -beginning of the file. Currently, it does nothing with these metadata, even when -they are accepted. In the future they might be used to build complete HTML file -instead of just fragment. +beginning of the file. The module does nothing with the metadata itself but you +can configure a hook to intercept the YAML. The possible values are: diff --git a/t/501-hooks-yaml-metadata.t b/t/501-hooks-yaml-metadata.t new file mode 100644 index 0000000..17c6955 --- /dev/null +++ b/t/501-hooks-yaml-metadata.t @@ -0,0 +1,71 @@ +use strict; +use warnings; +use utf8; + +use Markdown::Perl 'convert', 'set_hooks'; +use Test::More; +use Test2::Tools::Warnings; +use Test2::Tools::Exception; + +my $p = Markdown::Perl->new(); +my $page = <<EOF; +--- +name: Mark is down +draft: false +number: 42 +--- +# Mark is down! + +I repeat: "Mark is down!" +EOF + +my $invalid_page = <<EOF; +--- +name: Mark is down + draft: false + number: 42 +--- +# Mark is down! + +I repeat: "Mark is down!" +EOF + +# Test 1: Check if we can get a string value +{ + sub hook_is_name_mark { + my $x = shift; + ok(exists($x->{name}) && $x->{name} eq 'Mark is down', "key 'name' was retrieved and validated as being 'Mark is down'"); + } + $p->set_hooks(yaml_metadata => \&hook_is_name_mark); + $p->convert($page); +} + +# Test 2: Validate that hook is not called if yaml is invalid +{ + my $hook_called = 0; + sub hook_called { + $hook_called = 1; + } + $p->set_hooks(yaml_metadata => \&hook_called); + ok(!$hook_called, "Hook was not called because metadata was invalid."); + $p->convert($invalid_page); +} + +# Test 3: Validate that invalid yaml causes a carp() +{ + sub hook { + } + $p->set_hooks(yaml_metadata => \&hook); + like(warning { $p->convert($invalid_page) }, qr/invalid/, "Got expected warning"); +} + +# Test 4: What happens if inside the hook we die() +{ + sub hook_die { + die "last words"; + } + $p->set_hooks(yaml_metadata => \&hook_die); + like( dies { $p->convert($page) }, qr/last words/, "The hook correctly died."); +} + +done_testing;