The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
package Module::Build::JSAN;

use strict;
use vars qw($VERSION @ISA);

$VERSION = '0.01';
use Module::Build;
@ISA = qw(Module::Build);
use File::Spec::Functions qw(catdir);
use File::Basename qw(dirname);

sub new {
    my $pkg = shift;
    my %p = @_;
    $p{metafile} ||= 'META.json';
    if (my $keywords = delete $p{keywords} || delete $p{tags}) {
        if ($p{meta_merge}) {
            $p{meta_merge}->{keywords} = $keywords
        } else {
            $p{meta_merge} = { keywords => $keywords };
        }
    }
    return $pkg->SUPER::new(%p);
}


sub ACTION_dist {
    my $self = shift;

    require Pod::Simple::HTML;
    require Pod::Simple::Text;
    require Pod::Select;

    for (qw(html text pod)) {
        my $dir = catdir 'doc', $_;
        unless (-e $dir) {
            File::Path::mkpath($dir, 0, 0755)
                or die "Couldn't mkdir $dir: $!";
            $self->add_to_cleanup($dir);
        }
    }

    my $lib_dir  = catdir 'lib';
    my $pod_dir  = catdir 'doc', 'pod';
    my $html_dir = catdir 'doc', 'html';
    my $txt_dir  = catdir 'doc', 'text';

    my $js_files = $self->find_dist_packages;
    foreach my $file (map { $_->{file} } values %$js_files) {
        (my $pod = $file) =~ s|^$lib_dir|$pod_dir|;
        $pod =~ s/\.js$/.pod/;
        my $dir = dirname $pod;
		unless (-e $dir) {
            File::Path::mkpath($dir, 0, 0755)
                or die "Couldn't mkdir $dir: $!";
		}
        # Ignore existing documentation files.
        next if -e $pod;
        open my $fh, ">", $pod or die "Cannot open $pod: $!\n";

        Pod::Select::podselect( { -output => $fh }, $file );

        print $fh "\n=cut\n";

        close $fh;
    }

    for my $pod (@{Module::Build->rscan_dir($pod_dir, qr/\.pod$/)}) {
        # Generate HTML docs.
        (my $html = $pod) =~ s|^$pod_dir|$html_dir|;
        $html =~ s/\.pod$/.html/;
        my $dir = dirname $html;
		unless (-e $dir) {
            File::Path::mkpath($dir, 0, 0755)
                or die "Couldn't mkdir $dir: $!";
		}
        open my $fh, ">", $html or die "Cannot open $html: $!\n";
        my $parser = Pod::Simple::HTML->new;
        $parser->output_fh($fh);
        $parser->parse_file($pod);
        close $fh;

        # Generate text docs.
        (my $txt = $pod) =~ s|^$pod_dir|$txt_dir|;
        $txt =~ s/\.pod$/.txt/;
        $dir = dirname $txt;
		unless (-e $dir) {
            File::Path::mkpath($dir, 0, 0755)
                or die "Couldn't mkdir $dir: $!";
		}
        open $fh, ">", $txt or die "Cannot open $txt: $!\n";
        $parser = Pod::Simple::Text->new;
        $parser->output_fh($fh);
        $parser->parse_file($pod);
        close $fh;
    }
    $self->depends_on('manifest');

    $self->depends_on('distdir');
   
    my $dist_dir = $self->dist_dir;

    $self->_strip_pod($dist_dir);

    $self->make_tarball($dist_dir);
    $self->delete_filetree($dist_dir);


    $self->add_to_cleanup('META.json');
    $self->add_to_cleanup('*.gz');
}

sub ACTION_manifest {
    my $self = shift;
    $self->SUPER::ACTION_manifest(@_);
    $self->add_to_cleanup('MANIFEST.bak');
}

sub ACTION_deps {
    my $self = shift;

    my $prefix = './tests/lib';

    require JSAN::Shell;
    my $jsan = JSAN::Shell->new;
    $jsan->index;

    my @deps = (
        keys( %{$self->{properties}{build_requires}} ),
        keys( %{$self->{properties}{requires}} ),
    );

    eval { $jsan->install( $_, $prefix ) }, ($@ && print$@)
        for @deps;

    $self->add_to_cleanup( $prefix );
}

sub dist_version {
    my $self = shift;
    my $p = $self->{properties};
    return $p->{dist_version} if defined $p->{dist_version};

    if ($self->module_name) {
        $p->{dist_version_from} ||=
          join( '/', 'lib', split /\./, $self->module_name ) . '.js';
        print $p->{dist_version_from}, $/;
    }

    die "Can't determine distribution version, must supply either "
      . "'dist_version',\n'dist_version_from', or 'module_name' parameter"
      unless $p->{dist_version_from};

    # Search for the version number.
    return $p->{dist_version} = $self->_parse_version(
        $self->module_name,
        $p->{dist_version_from}
    );
}

sub find_js_files  { shift->_find_file_by_type('js',  'lib') }

sub find_dist_packages {
    my $self = shift;
    # Only packages in .js files are candidates for inclusion here.
    # Only include things in the MANIFEST, not things in developer's
    # private stock.

    my $manifest = $self->_read_manifest('MANIFEST')
      or die "Can't find dist packages without a MANIFEST file "
        . "- run 'manifest' action first";

    # Localize
    my %dist_files = map { $self->localize_file_path($_) => $_ }
      keys %$manifest;

    my @js_files = grep {exists $dist_files{$_}} keys %{ $self->find_js_files };

    my %out;
    for my $file (@js_files) {
        next if $file =~ m{^t/};  # Skip things in t/

        # Assume that the file name corresponds to the library. This may need
        # to be more sophisticated in the future, but will do for now.
        (my $lib = $file) =~ s|^[^/]+/||;
        $lib = join '.', split m{/}, $lib;
        $lib =~ s/\.js$//;
        $out{$lib} = {
            file => $dist_files{$file},
            version => $self->_parse_version($lib, $file),
        };
    }
    return \%out;
}

sub _parse_version {
    my ($self, $lib, $file) = @_;
    my $version_from = File::Spec->catfile( split m{/}, $file );
    open VF, "<$version_from" or die "Cannot open '$version_from': $!\n";
    my $version = '';
    my $find    = qr/VERSION\s*(?:=|:)\s*[^\d._]*([\d._]+)/;
    while (<VF>) {
        last if ($version) = /$find/;
    }
    close VF;
    return $version;
}

sub write_metafile {
    my $self = shift;
    my $metafile = $self->metafile;

    require Module::Build::JSAN::ConfigData;  # Only works after the 'build'
    if (Module::Build::JSAN::ConfigData->feature('JSON_support')) {
        require JSON;
        $self->prepare_metadata( my $node = {} );
        open my $meta, '>', $metafile or die "Cannot open '$metafile': $!\n";
        print $meta JSON->new->objToJson($node, {
            pretty    => 1,
            indent    => 4,
            delimiter => 1
        });
        close $meta;
    } else {
        $self->log_warn(
            "\nCouldn't load JSON.pm, generating a minimal META.json without ",
            "it.\nPlease check and edit the generated metadata, or consider ",
            "installing\nJSON.pm.\n\n"
        );
        $self->_write_minimal_metadata;
    }
    $self->_add_to_manifest('MANIFEST', $metafile);
}

sub _write_minimal_metadata {
    my $self = shift;
    my $p = $self->{properties};

    my $file = $self->metafile;
    my $fh = IO::File->new("> $file") or die "Can't open $file: $!";

  # XXX Add the meta_add & meta_merge stuff
  print $fh <<"END_OF_META";
{
    "name": "$p->{dist_name}",
    "version": "$p->{dist_version}",
    "author":
    @{[ join "\n", map qq{  "$_"}, @{$self->dist_author} ]},
    "abstract": "@{[ $self->dist_abstract ]}",
    "license": "$p->{license}",
    "generated_by": "Module::Build::JSAN version $Module::Build::JSAN::VERSION, without JSON.pm"
}
END_OF_META
}

sub _write_default_maniskip {
    my $self = shift;
    my $file = shift || 'MANIFEST.SKIP';

    $self->SUPER::_write_default_maniskip($file);

    my $fh = IO::File->new(">> $file")
      or die "Can't open $file: $!";
    print $fh <<'EOF';
^Build.PL$
.tar.gz$
^tests/lib/
EOF
    print $fh $self->dist_dir, "\n";
    $fh->close();
}

sub _strip_pod {
    my ($self, $dist_dir) = @_;

    require Pod::Stripper;
    
    my $files    = $self->find_js_files;
    my $stripper = Pod::Stripper->new;
    foreach my $file ( keys %{$files} ) {
        # This will leave empty comment blocks in tact.
        # That looks odd. Pod::Stripper::JSAN should be made.
        $stripper->parse_from_file($file, "$dist_dir/$file");
    }
}

sub check_prereq {  }
sub ignore_prereqs { 1 }

1;
__END__

=head1 NAME

Module::Build::JSAN - Build JavaScript modules for JSAN

=head1 SYNOPSIS

  use Module::Build::JSAN;

  my $build = Module::Build::JSAN->new(
      module_name    => 'Foo-Bar',
      license        => 'perl',
      dist_author    => 'Joe Developer <joe@foobar.com>',
      dist_abstract  => 'Say something pithy here',
      dist_version   => '0.02',
      keywords       => [qw(Foo Bar pithyness)],
      build_requires => {
          'Test.Simple' => 0.20,
      },
      requires     => {
          'JSAN'     => 0.10,
          'Baz-Quux' => 0.02,
      },
  );

  $build->create_build_script;

=head1 DESCRIPTION

This is a developer aid for creating JSAN distributions. Please use the example
given in the SYNOPSIS to create distributions.

This works nearly identically to L<Module::Build>, so please refer to its
documentation.

=head1 DIFFERENCES

=over 4

=item 1 META.json

JSAN uses the JSON format instead of the YAML format, as it's legal Javascript
and just plain easier to work with. This means that Module::Build::JSAN will
generate META.json files instead of META.yml files. Do not be alarmed.

=item 2 ./Build deps

This is a new action added in Module::Build::JSAN. You run this in order to
install your dependencies when testing while developing. This will allow you to
develop against the latest versions of the distributions you are building upon.

=back

=head1 BUGS

Please send bug reports to <bug-module-build-jsan@rt.cpan.org>.

=head1 AUTHORS

David Wheeler <david@kineticode.com>
Casey West <casey@geeknest.com>
Rob Kinyon <rob.kinyon@iinteractive.com>

=head1 COPYRIGHT AND LICENSE

Copyright 2005 by David Wheeler, Casey West, Rob Kinyon

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

=cut