File Coverage

blib/lib/Mojo/Alien/npm.pm
Criterion Covered Total %
statement 18 70 25.7
branch 0 32 0.0
condition 1 12 8.3
subroutine 6 12 50.0
pod 3 3 100.0
total 28 129 21.7


line stmt bran cond sub pod time code
1             package Mojo::Alien::npm;
2 10     10   992790 use Mojo::Base -base;
  10         54  
  10         82  
3              
4 10     10   1984 use Carp qw(croak);
  10         23  
  10         527  
5 10     10   1680 use File::chdir;
  10         7824  
  10         938  
6 10     10   4253 use Mojo::File qw(path);
  10         255584  
  10         763  
7 10     10   4840 use Mojo::JSON qw(decode_json false);
  10         192856  
  10         1167  
8              
9 10   50 10   111 use constant DEBUG => ($ENV{MOJO_NPM_DEBUG} || $ENV{MOJO_WEBPACK_DEBUG}) && 1;
  10         23  
  10         13333  
10              
11             has binary => sub {
12             my $self = shift;
13             return $ENV{MOJO_NPM_BINARY} ? $ENV{MOJO_NPM_BINARY} : 'npm';
14             };
15              
16             has config => sub { path->to_abs->child('package.json') };
17             has mode => sub { $ENV{NODE_ENV} || 'development' };
18              
19             sub dependencies {
20 0     0 1   my $self = shift;
21 0 0         croak "Can't get dependency info without package.json" unless -r $self->config;
22              
23 0 0         my @args = $self->binary eq 'pnpm' ? qw(ls --json --silent) : qw(ls --json --parseable --silent);
24 0           my $dependencies;
25              
26             eval {
27 0           my $NPM = $self->_run(@args);
28              
29             # "WARN" might come from pnpm, and it also returns an array-ref
30 0           $dependencies = decode_json(join '', grep { !/WARN/ } <$NPM>);
  0            
31 0 0         $dependencies = $dependencies->[0] if ref $dependencies eq 'ARRAY';
32 0 0         $dependencies = {map { %{$dependencies->{$_} || {}} } qw(devDependencies dependencies)};
  0            
  0            
33 0 0         } or do {
34 0           croak sprintf '%s failed: %s', join(' ', $self->binary, @args), $@;
35             };
36              
37 0           my $package = decode_json $self->config->slurp;
38 0           my %types = (devDependencies => 'dev', dependencies => 'prod', optionalDependencies => 'optional');
39 0           for my $type (qw(optionalDependencies devDependencies dependencies)) {
40 0           for my $name (keys %{$package->{$type}}) {
  0            
41 0           $dependencies->{$name}{required} = $package->{$type}{$name};
42 0           $dependencies->{$name}{type} = $types{$type};
43 0   0       $dependencies->{$name}{version} //= '';
44             }
45             }
46              
47 0           return $dependencies;
48             }
49              
50             sub init {
51 0     0 1   my $self = shift;
52 0 0         return $self if -r $self->config;
53 0           $self->_run(qw(init -y));
54 0 0         croak "$self->{basename} init failed: @{[$self->config]} was not generated." unless -r $self->config;
  0            
55 0           return $self;
56             }
57              
58             sub install {
59 0     0 1   my ($self, $name, $info) = @_;
60 0 0         croak "Can't install packages without package.json" unless -w $self->config;
61              
62             # Install everything
63 0 0         do { $self->_run('install'); return $self } unless $name;
  0            
  0            
64              
65             # Install specific package
66 0 0         $name = sprintf '%s@%s', $name, $info->{version} if $info->{version};
67 0   0       my $type = sprintf '--save-%s', $info->{type} || 'dev';
68 0           $self->_run('install', $name, $type);
69 0           return $self;
70             }
71              
72             sub _run {
73 0     0     my $self = shift;
74 0           my @cmd = ($self->binary, @_);
75 0   0       $self->{basename} ||= path($cmd[0])->basename;
76 0           local $CWD = $self->config->dirname->to_string;
77 0           local $ENV{NODE_ENV} = $self->mode;
78 0           warn "[NPM] cd $CWD && @cmd\n" if DEBUG;
79 0 0         open my $NPM, '-|', @cmd or die "Can't fork @cmd: $!";
80 0 0         return $NPM if defined wantarray;
81 0           map { DEBUG && print } <$NPM>;
  0            
82             }
83              
84             # This is a utility function for the unit tests
85             sub _setup_working_directory {
86 0     0     my ($class, $dir) = @_;
87 0 0   0     my $remove_tree = $ENV{MOJO_NPM_CLEAN} ? 'remove_tree' : sub { };
88 0 0         chdir(my $work_dir = path($dir ? $dir : ('local', path($0)->basename))->to_abs->tap($remove_tree)->make_path)
    0          
89             or die "Couldn't set up working directory: $!";
90 0 0 0       symlink $work_dir->dirname->child('node_modules')->make_path, 'node_modules'
91             or warn "Couldn't set up shared node_modules: $!"
92             unless -e 'node_modules';
93 0           return $work_dir;
94             }
95              
96             1;
97              
98             =encoding utf8
99              
100             =head1 NAME
101              
102             Mojo::Alien::npm - Runs the external nodejs program npm
103              
104             =head1 SYNOPSIS
105              
106             use Mojo::Alien::npm;
107             my $npm = Mojo::Alien::npm->new;
108              
109             $npm->init;
110             $npm->install;
111              
112             =head1 DESCRIPTION
113              
114             L is a class for runnig the external nodejs program
115             L.
116              
117             =head1 ATTRIBUTES
118              
119             =head2 binary
120              
121             $array_ref = $npm->binary;
122             $npm = $npm->binary(['npm']);
123              
124             The path to the npm executable. Default is "npm" unless the C
125             environment variable has been set. This can also be set to "pnpm" in case you
126             prefer L.
127              
128             =head2 config
129              
130             $path = $npm->config;
131             $npm = $npm->config(path->to_abs->child('package.json'));
132              
133             Holds an I path to "package.json".
134              
135             =head2 mode
136              
137             $str = $npm->mode;
138             $npm = $npm->mode('development');
139              
140             Should be either "development" or "production". Will be used as "NODE_ENV"
141             environment variable when calling "npm".
142              
143             =head1 METHODS
144              
145             =head2 dependencies
146              
147             $dependencies = $npm->dependencies;
148              
149             Used to get dependencies from L combined with information from
150             C. The returned hash-ref looks like this:
151              
152             {
153             "package-name" => {
154             required => $str, # version from package.json
155             type => $str, # dev, optional or prod
156             version => $str, # installed version
157             ...
158             },
159             ...
160             }
161              
162             =head2 init
163              
164             $npm->init;
165              
166             Used to create a default L file.
167              
168             =head2 install
169              
170             $npm->install;
171             $npm->install('package-name');
172             $npm->install('package-name', {type => 'prod', version => '0.1.2'});
173              
174             Installs either all modules from L or a given package by name. An
175             additional C<$info> hash can also be provided.
176              
177             =head1 SEE ALSO
178              
179             L.
180              
181             =cut