diff --git a/lib/Plerd/Post.pm b/lib/Plerd/Post.pm index 00657a4..a142e36 100644 --- a/lib/Plerd/Post.pm +++ b/lib/Plerd/Post.pm @@ -398,6 +398,15 @@ sub _build_social_meta_tags { } +# Normalize CRLF ("Windows") and bare-CR ("classic Mac") newlines to LF, so +# source files publish cleanly and get rewritten with consistent line endings +# regardless of the editor that produced them. +sub _normalize_newlines { + my ( $text ) = @_; + $text =~ s/\r\n?/\n/g; + return $text; +} + # This next internal method does a bunch of stuff. # It's called via Moose-trigger when the object's source_file attribute is set. # * Read and store the file's data (body) and metadata @@ -413,6 +422,7 @@ sub _process_source_file { my %attributes; my @ordered_attribute_names = qw( title time published_filename guid tags); while ( my $line = <$fh> ) { + $line = _normalize_newlines( $line ); chomp $line; last unless $line =~ /\S/; my ($key, $value) = $line =~ /^\s*(\w+?)\s*:\s*(.*?)\s*$/; @@ -428,8 +438,8 @@ sub _process_source_file { $self->attributes( \%attributes ); my $body; - while ( <$fh> ) { - $body .= $_; + while ( my $line = <$fh> ) { + $body .= _normalize_newlines( $line ); } close $fh; diff --git a/t/crlf.t b/t/crlf.t new file mode 100644 index 0000000..3b249c0 --- /dev/null +++ b/t/crlf.t @@ -0,0 +1,65 @@ +use warnings; +use strict; +use Test::More; +use Path::Class::Dir; +use Path::Class::File; +use URI; + +use FindBin; +use lib "$FindBin::Bin/../lib"; + +use_ok( 'Plerd' ); +use Plerd::Init; + +# Regression test for GitHub issue #25: source files that use CRLF ("Windows") +# newlines used to break publication with a misleading "not in W3C format" +# error. Plerd should accept CRLF source files, publish them cleanly, and +# normalize the rewritten source file to LF newlines. + +my $blog_dir = Path::Class::Dir->new( "$FindBin::Bin/crlf_testblog" ); +$blog_dir->rmtree; + +Plerd::Init::initialize( $blog_dir->stringify, 0 ); + +my $source_file = Path::Class::File->new( $blog_dir, 'source', 'crlf-post.md' ); +{ + open my $fh, '>:raw', $source_file->stringify + or die "Can't write $source_file: $!"; + print $fh "title: CRLF Post\r\n"; + print $fh "time: 2018-03-04T12:00:00\r\n"; + print $fh "tags: foo, bar\r\n"; + print $fh "\r\n"; + print $fh "This is the **body** of the post.\r\n"; + print $fh "\r\n"; + print $fh "Second paragraph here.\r\n"; + close $fh; +} + +my $plerd = Plerd->new( + path => $blog_dir->stringify, + title => 'Test Blog', + author_name => 'Nobody', + author_email => 'nobody@example.com', + base_uri => URI->new( 'http://blog.example.com/' ), +); + +eval { $plerd->publish_all; }; +is( $@, '', 'A CRLF source file publishes without error.' ); + +my $published = Path::Class::File->new( + $blog_dir, 'docroot', '2018-03-04-crlf-post.html' +); +ok( -e $published, 'The CRLF post produced its HTML file.' ); + +# The source file is rewritten in place during publication; it should no +# longer contain any carriage returns. +my $rewritten = do { + open my $fh, '<:raw', $source_file->stringify or die $!; + local $/; + <$fh>; +}; +unlike( $rewritten, qr/\r/, 'Rewritten source file is free of carriage returns.' ); + +$blog_dir->rmtree; + +done_testing();