File: YAMLFrontMatter.pm

package info (click to toggle)
libcode-tidyall-plugin-yamlfrontmatter-perl 1.000003-2
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, forky, sid, trixie
  • size: 264 kB
  • sloc: perl: 357; makefile: 2
file content (226 lines) | stat: -rw-r--r-- 5,564 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
package Code::TidyAll::Plugin::YAMLFrontMatter;

use strict;
use warnings;
use namespace::autoclean;

our $VERSION = '1.000003';

use Moo;

use Encode qw( decode encode FB_CROAK );
use Path::Tiny qw( path );
use Try::Tiny qw( catch try );
use YAML::PP 0.006 ();

extends 'Code::TidyAll::Plugin';

# This regular expression is based on the regex
#     \A(---\s*\n.*?\n?)^((---|\.\.\.)\s*$\n?)    (with the m flag)
# from the Jekyll source code here:
# https://github.com/jekyll/jekyll/blob/c7d98cae2652b2df7ebd3c60b4f8c87950760e47/lib/jekyll/document.rb#L13
# note - The 'm' modifier in ruby is essentially the same as 's' in Perl
#        so we need to enable the 's' modifier not 'm'
#      - Ruby essentially always treats '^' and '$' the way Perl does when the
#        'm' modifier is enabled, so we need to turn that on too
#      - We need to enable the 'x' modifier and space things out so that
#        Perl treats '$\n' as '$' and '\n' and not the variable '$\' and 'n'
my $YAML_REGEX = qr{
   \A
      # the starting ---, and anything up until...
      (---\s*\n.*?\n?)

      # ...the first --- or ... on their own line
      ^ (?:---|\.\.\.) \s* $ \n?
}msx;

has encoding => (
    is => 'ro',

    # By default Jekyll 2.0 and later defaults to utf-8, so this seems
    # like a sensible default for us
    default => 'UTF-8',
);

has required_top_level_keys => (
    is      => 'ro',
    default => q{},
);

has _req_keys_hash => ( is => 'lazy' );

sub _build__req_keys_hash {
    my $self = shift;
    return +{

        # note use of magical split on space to do automatic trimming
        map { $_ => 1 } split q{ }, $self->required_top_level_keys
    };
}

sub validate_file {
    my ( $self, $filename ) = @_;

    my $src = path($filename)->slurp_raw;

    # YAML::PP always expects things to be in UTF-8 bytes
    my $encoding = $self->encoding;
    try {
        $src = decode( $encoding, $src, FB_CROAK );
        $src = encode( 'UTF-8', $src, FB_CROAK );
    }
    catch {
        die "File does not match encoding '$encoding': $_";
    };

    # is there a BOM?  There's not meant to be a BOM!
    if ( $src =~ /\A\x{EF}\x{BB}\x{BF}/ ) {
        die "Starting document with UTF-8 BOM is not allowed\n";
    }

    # match the YAML front matter.
    my $yaml;
    unless ( ($yaml) = $src =~ $YAML_REGEX ) {
        die "'$filename' does not start with valid YAML Front Matter\n";
    }

    # parse the YAML front matter.
    my $ds = try {
        my $yp = YAML::PP->new(

            # Insist on YAML 1.1.  Jekyl uses SafeYAML to parse YAML
            # which will either use Syck (via "YAML") or LibYAML
            # (via "Psych") to parse the YAML, so what YAML it can
            # use is a matter of debate.  However, since this module
            # is a linter, we can make up our own rules, and they are
            # that you have to write your frontmatter only in the
            # YAML 1.1 spec.  Todo: Make this insist in the most
            # compatible YAML (i.e. freak out if someone uses "y"
            # instead of "true") if YAML::PP ever supports such
            # an option.
            schema => ['YAML1_1'],

            # we do not want to create circular refs
            cyclic_refs => 'fatal',

        );
        return $yp->load_string($yaml);
    }
    catch {
        die "Problem parsing YAML: $_";
    };

    # check for required keys
    my $errors = q{};
    for ( sort keys %{ $self->_req_keys_hash } ) {
        next if $ds->{$_};
        $errors .= "Missing required YAML Front Matter key: '$_'\n";
    }
    die $errors if $errors;

    return;
}

1;

# ABSTRACT: TidyAll plugin for validating YAML Front Matter

__END__

=pod

=encoding UTF-8

=head1 NAME

Code::TidyAll::Plugin::YAMLFrontMatter - TidyAll plugin for validating YAML Front Matter

=head1 VERSION

version 1.000003

=head1 SYNOPSIS

In your .tidyallrc file:

    [YAMLFrontMatter]
    select = **/*.md
    required_top_level_keys = title layout

=head1 DESCRIPTION

This is a validator plugin for L<Code::TidyAll> that can be used to check
that files have valid YAML Front Matter, like Jekyll et al use.

It will complain if:

=over

=item There's no YAML Front Matter

=item The YAML Front Matter isn't valid YAML

=item There's a UTF-8 BOM at the start of the file

=item The file isn't encoded in the configured encoding (UTF-8 by default)

=item The YAML Front Matter is missing one or more configured top level keys

=item The YAML Front Matter contains circular references

=back

=head2 Options

=over

=item C<required_top_level_keys>

Keys that must be present at the top level of the YAML Front Matter.

=item C<encoding>

The encoding the file is in.  Defaults to UTF-8 (just like Jekyll 2.0 and
later.)

=back

=head1 SEE ALSO

L<Jekyll's Front Matter Documentation|https://jekyllrb.com/docs/frontmatter/>

=head1 SUPPORT

Please report all issues with this code using the GitHub issue tracker at
L<https://github.com/maxmind/Code-TidyAll-Plugin-YAMLFrontMatter/issues>.

Bugs may be submitted through L<https://github.com/maxmind/Code-Tidyall-Plugin-YAMLFrontMatter/issues>.

=head1 AUTHOR

Mark Fowler <mfowler@maxmind.com>

=head1 CONTRIBUTORS

=for stopwords Dave Rolsky Greg Oschwald

=over 4

=item *

Dave Rolsky <autarch@urth.org>

=item *

Greg Oschwald <goschwald@maxmind.com>

=back

=head1 COPYRIGHT AND LICENSE

This software is copyright (c) 2019 by MaxMind, Inc.

This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.

=cut