App-PFT

 view release on metacpan or  search on metacpan

bin/pft-make  view on Meta::CPAN

    </head>

The result of a build is a collection of HTML pages.  Since C<a href> links
are relative, the generated site will work fine even if moved or copied
remotely on another system (see L<pft-pub(1)>).

=head2 Injected data

The B<pft make> command will populate the C<ROOT/build> directory.

Additional static data to inject in the resulting website can be placed in
the C<ROOT/inject> directory.  This meets the common requirement of placing
additional files in the root directory of online websites (typical case
being the C<.htaccess> file of Apache).

The L<pft-make(1)> command will first attempt to hard-link the injected files,
from C<ROOT/inject> to C<ROOT/build>.  If this fails (e.g. because
hard-links are not supported by the filesystem) soft-links are attempted.
If nothing else succeeds, B<pft make> will make a copy of each injected
file.

=head1 OPTIONS

=over

=item B<--help>

Show this guide.

=back

=head1 EXIT STATUS

=over

=item

1 in case of option parsing failure.

=item

2 if it was impossible to construct the filesystem tree.

=item

3 in case of corrupt configuration.

=back

=head1 SEE ALSO

L<pft(1)>, L<pft-gen-rss(1)>, L<pft-init(1)>

=cut

use strict;
use warnings;
use utf8;
use v5.16;

use feature qw/say state/;

use Carp;
use Digest::MD5;
use Encode::Locale;
use Encode;
use Template::Alloy;

use File::Spec;
use File::Basename qw/dirname basename/;
use File::Path qw/make_path/;

use PFT::Text;
use PFT::Tree;
use PFT::Util;

use App::PFT;
use App::PFT::Util qw/ln/;

use Pod::Usage;
use Getopt::Long;
Getopt::Long::Configure qw/bundling/;

GetOptions(
    'help|h!'       => sub {
        pod2usage
            -exitval => 1,
            -verbose => 2,
            -input => App::PFT::help_of 'make',
    },
) or exit 1;

my $tree = eval{ PFT::Tree->new } || do {
    say STDERR $@ =~ s/ at.*$//rs;
    exit 2
};

my $conf = eval{ $tree->conf } || do {
    say STDERR 'Configuration error: ', $@ =~ s/ at.*$//rs;
    exit 3;
};

my $template = Template::Alloy->new(
    INCLUDE_PATH => $tree->dir_templates,
    ENCODING => $conf->{site}{encoding},
);
my $dir_build = $tree->dir_build;
my $map = $tree->content_map;

sub node_to_rel {
    my $node = shift;
    confess unless $node;
    my $hdr = $node->header;
    my $k = $node->content_type;

    if ($k =~ /::Blog$/) {(
        'blog',
        sprintf('%04d-%02d', $hdr->date->y, $hdr->date->m),
        sprintf('%02d-%s.html', $hdr->date->d, $hdr->slug),
    )} elsif ($k =~ /::Month$/) {(
        'blog',
        sprintf('%04d-%02d.html', $hdr->date->y, $hdr->date->m),
    )} elsif ($k =~ /::Page$/) {(
        'pages',
        $hdr->slug . '.html',
    )} elsif ($k =~ /::Tag$/) {(
        'tags',
        $hdr->slug . '.html',
    )} elsif ($k =~ /::Picture$/) {(
        'pics',
        $node->content->relpath
    )} elsif ($k =~ /::Attachment$/) {(
        'attachments',
        $node->content->relpath
    )} else { die $k };
}

sub node_to_root {
    # NOTE: you actually wantarray!
    my $k = shift->content_type;
    if ($k =~ /::Blog$/) {(
        '..', '..'
    )} elsif ($k =~ /::(?:Month|Page|Tag)$/) {(
        '..',
    )} else {
         die "Why going back from $k?"
    };
}

sub node_to_href {
    my($cur_node, $other_node) = @_;
    join('/', node_to_root($cur_node), node_to_rel($other_node));
}

sub node_to_date {
    my $d = shift->date;
    return undef unless defined $d;
    return $d->to_hash unless @_;
    return $d->repr(shift)

bin/pft-make  view on Meta::CPAN


my $home_node_slug = PFT::Header::slugify($conf->{site}{home});
my $home_node;

for my $node ($map->nodes) {
    my $content = $node->content;

    if ($content->isa('PFT::Content::Entry')) {
        compile_entry($node, $content)
    }
    elsif ($content->isa('PFT::Content::Blob')) {
        install_blob($node, $content)
    }
}

sub write_file {
    my($data, $path) = @_;

    my $dirname = dirname $path;
    my $enc = $conf->{site}{encoding};
    my $fh;

    make_path $dirname;

    my $temp = File::Spec->catfile($dirname, "." . basename $path);

    open($fh, ">:encoding($enc)", $temp) or croak "Opening $temp: $!";
    print $fh $data;
    close $fh;

    if (-e "$path") {
        # This branch enables an upload-time optimization: if the file
        # created file is exactly the same as the previous compilation (same
        # checksum) we keep the old one.  Rsync will not upload it again.
        my $md5 = Digest::MD5->new;
        $md5->add(encode($enc, $data));
        my $digest_new = $md5->hexdigest;

        open($fh, "<:raw", $path)
            or croak "Opening $path: $!";
        $md5->addfile($fh);
        close $fh;

        if ($digest_new eq $md5->hexdigest) {
            # Unchanged! Leave the old one.
            unlink $temp;
            return;
        }
    }

    rename $temp => $path;
}

sub compile_entry {
    my($node, $content) = @_;

    my $hdr = $node->header;
    my $first = 1;
    foreach ($node->symbols_unres) {
        if ($first) {
            say STDERR "Unresolved links in $node:"
        }

        my($symbol, $reason) = @$_;
        say STDERR "- link: $symbol";
        say STDERR "  reason: $reason";

        undef $first;
    }

    my $is_home;
    if (!$node->virtual && $hdr->slug eq $home_node_slug) {
        die "There should be no doubles" if defined $home_node;
        $home_node = $node;
        $is_home ++;
    }

    if ($hdr->opts->{hide}) {
        print "Node $node will be hidden\n";
        return;
    }

    my %entry_info = (
        site => {
            root => join('/', node_to_root($node)),
            %{$conf->{site}},
        },
        content => {
            title       => $node->title,
            html        => $node->html(sub { node_to_href($node, shift) }),
            tags        => nodes_to_anchors($node, $node->tags),
            date        => sub { node_to_date($node, @_) },
            is_home     => $is_home,
            author      => $node->author,
            is_virtual  => $node->virtual,
        },
        links => {
            site_links($node),
            prev        => node_to_anchor($node, $node->prev),
            next        => node_to_anchor($node, $node->next),
            parent      => node_to_anchor($node, $node->month),
            children    => nodes_to_anchors($node, $node->children),
        }
    );

    my $out_data;
    $template->process(
        # Encoding alert!
        ($hdr->opts->{template} || $conf->{site}{template}),
        \%entry_info,
        \$out_data,
    ) || croak 'Template expansion issue: ', $template->error;

    my $out_path = File::Spec->catfile(
        $dir_build,
        map encode($conf->{site}{encoding}, $_) => node_to_rel($node)
    );

    write_file $out_data => $out_path;
}

sub install_blob {
    my($node, $content) = @_;

    my $out_path = File::Spec->catfile($dir_build, node_to_rel($node));



( run in 0.315 second using v1.01-cache-2.11-cpan-5511b514fd6 )