File Coverage

blib/lib/Mojo/Alien/npm.pm
Criterion Covered Total %
statement 18 71 25.3
branch 0 34 0.0
condition 1 12 8.3
subroutine 6 12 50.0
pod 3 3 100.0
total 28 132 21.2


line stmt bran cond sub pod time code
1             package Mojo::Alien::npm;
2 10     10   767162 use Mojo::Base -base;
  10         68  
  10         55  
3              
4 10     10   1376 use Carp qw(croak);
  10         17  
  10         378  
5 10     10   1139 use File::chdir;
  10         5914  
  10         708  
6 10     10   2893 use Mojo::File qw(path);
  10         192498  
  10         541  
7 10     10   3176 use Mojo::JSON qw(decode_json false);
  10         148250  
  10         779  
8              
9 10   50 10   67 use constant DEBUG => ($ENV{MOJO_NPM_DEBUG} || $ENV{MOJO_WEBPACK_DEBUG}) && 1;
  10         26  
  10         10368  
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 0         $self->_run($self->binary eq 'pnpm' ? qw(init) : 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             # Make sure npm can install devDependencies and dependency
63 0           local $self->{mode} = '';
64              
65             # Install everything
66 0 0         do { $self->_run('install'); return $self } unless $name;
  0            
  0            
67              
68             # Install specific package
69 0 0         $name = sprintf '%s@%s', $name, $info->{version} if $info->{version};
70 0   0       my $type = sprintf '--save-%s', $info->{type} || 'dev';
71 0           $self->_run('install', $name, $type);
72 0           return $self;
73             }
74              
75             sub _run {
76 0     0     my $self = shift;
77 0           my @cmd = ($self->binary, @_);
78 0   0       $self->{basename} ||= path($cmd[0])->basename;
79 0           local $CWD = $self->config->dirname->to_string;
80 0           local $ENV{NODE_ENV} = $self->mode;
81 0           warn "[NPM] cd $CWD && @cmd\n" if DEBUG;
82 0 0         open my $NPM, '-|', @cmd or die "Can't fork @cmd: $!";
83 0 0         return $NPM if defined wantarray;
84 0           map { DEBUG && print } <$NPM>;
  0            
85             }
86              
87             # This is a utility function for the unit tests
88             sub _setup_working_directory {
89 0     0     my ($class, $dir) = @_;
90 0 0   0     my $remove_tree = $ENV{MOJO_NPM_CLEAN} ? 'remove_tree' : sub { };
91 0 0         chdir(my $work_dir = path($dir ? $dir : ('local', path($0)->basename))->to_abs->tap($remove_tree)->make_path)
    0          
92             or die "Couldn't set up working directory: $!";
93 0 0 0       symlink $work_dir->dirname->child('node_modules')->make_path, 'node_modules'
94             or warn "Couldn't set up shared node_modules: $!"
95             unless -e 'node_modules';
96 0           return $work_dir;
97             }
98              
99             1;
100              
101             =encoding utf8
102              
103             =head1 NAME
104              
105             Mojo::Alien::npm - Runs the external nodejs program npm
106              
107             =head1 SYNOPSIS
108              
109             use Mojo::Alien::npm;
110             my $npm = Mojo::Alien::npm->new;
111              
112             $npm->init;
113             $npm->install;
114              
115             =head1 DESCRIPTION
116              
117             L is a class for runnig the external nodejs program
118             L.
119              
120             =head1 ATTRIBUTES
121              
122             =head2 binary
123              
124             $array_ref = $npm->binary;
125             $npm = $npm->binary(['npm']);
126              
127             The path to the npm executable. Default is "npm" unless the C
128             environment variable has been set. This can also be set to "pnpm" in case you
129             prefer L.
130              
131             =head2 config
132              
133             $path = $npm->config;
134             $npm = $npm->config(path->to_abs->child('package.json'));
135              
136             Holds an I path to "package.json".
137              
138             =head2 mode
139              
140             $str = $npm->mode;
141             $npm = $npm->mode('development');
142              
143             Should be either "development" or "production". Will be used as "NODE_ENV"
144             environment variable when calling "npm".
145              
146             =head1 METHODS
147              
148             =head2 dependencies
149              
150             $dependencies = $npm->dependencies;
151              
152             Used to get dependencies from L combined with information from
153             C. The returned hash-ref looks like this:
154              
155             {
156             "package-name" => {
157             required => $str, # version from package.json
158             type => $str, # dev, optional or prod
159             version => $str, # installed version
160             ...
161             },
162             ...
163             }
164              
165             =head2 init
166              
167             $npm->init;
168              
169             Used to create a default L file.
170              
171             =head2 install
172              
173             $npm->install;
174             $npm->install('package-name');
175             $npm->install('package-name', {type => 'prod', version => '0.1.2'});
176              
177             Installs either all modules from L or a given package by name. An
178             additional C<$info> hash can also be provided.
179              
180             =head1 SEE ALSO
181              
182             L.
183              
184             =cut