File Coverage

blib/lib/Mojo/Alien/npm.pm
Criterion Covered Total %
statement 23 70 32.8
branch 4 32 12.5
condition 2 12 16.6
subroutine 8 12 66.6
pod 3 3 100.0
total 40 129 31.0


line stmt bran cond sub pod time code
1             package Mojo::Alien::npm;
2 11     11   1327664 use Mojo::Base -base;
  11         66  
  11         78  
3              
4 11     11   2141 use Carp qw(croak);
  11         25  
  11         612  
5 11     11   2241 use File::chdir;
  11         10205  
  11         1255  
6 11     11   4257 use Mojo::File qw(path);
  11         252672  
  11         790  
7 11     11   5579 use Mojo::JSON qw(decode_json false);
  11         218127  
  11         1273  
8              
9 11   50 11   119 use constant DEBUG => ($ENV{MOJO_NPM_DEBUG} || $ENV{MOJO_WEBPACK_DEBUG}) && 1;
  11         26  
  11         14548  
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 0 my $self = shift;
21 0 0       0 croak "Can't get dependency info without package.json" unless -r $self->config;
22              
23 0 0       0 my @args = $self->binary eq 'pnpm' ? qw(ls --json --silent) : qw(ls --json --parseable --silent);
24 0         0 my $dependencies;
25              
26             eval {
27 0         0 my $NPM = $self->_run(@args);
28              
29             # "WARN" might come from pnpm, and it also returns an array-ref
30 0         0 $dependencies = decode_json(join '', grep { !/WARN/ } <$NPM>);
  0         0  
31 0 0       0 $dependencies = $dependencies->[0] if ref $dependencies eq 'ARRAY';
32 0 0       0 $dependencies = {map { %{$dependencies->{$_} || {}} } qw(devDependencies dependencies)};
  0         0  
  0         0  
33 0 0       0 } or do {
34 0         0 croak sprintf '%s failed: %s', join(' ', $self->binary, @args), $@;
35             };
36              
37 0         0 my $package = decode_json $self->config->slurp;
38 0         0 my %types = (devDependencies => 'dev', dependencies => 'prod', optionalDependencies => 'optional');
39 0         0 for my $type (qw(optionalDependencies devDependencies dependencies)) {
40 0         0 for my $name (keys %{$package->{$type}}) {
  0         0  
41 0         0 $dependencies->{$name}{required} = $package->{$type}{$name};
42 0         0 $dependencies->{$name}{type} = $types{$type};
43 0   0     0 $dependencies->{$name}{version} //= '';
44             }
45             }
46              
47 0         0 return $dependencies;
48             }
49              
50             sub init {
51 0     0 1 0 my $self = shift;
52 0 0       0 return $self if -r $self->config;
53 0         0 $self->_run(qw(init -y));
54 0 0       0 croak "$self->{basename} init failed: @{[$self->config]} was not generated." unless -r $self->config;
  0         0  
55 0         0 return $self;
56             }
57              
58             sub install {
59 0     0 1 0 my ($self, $name, $info) = @_;
60 0 0       0 croak "Can't install packages without package.json" unless -w $self->config;
61              
62             # Install everything
63 0 0       0 do { $self->_run('install'); return $self } unless $name;
  0         0  
  0         0  
64              
65             # Install specific package
66 0 0       0 $name = sprintf '%s@%s', $name, $info->{version} if $info->{version};
67 0   0     0 my $type = sprintf '--save-%s', $info->{type} || 'dev';
68 0         0 $self->_run('install', $name, $type);
69 0         0 return $self;
70             }
71              
72             sub _run {
73 0     0   0 my $self = shift;
74 0         0 my @cmd = ($self->binary, @_);
75 0   0     0 $self->{basename} ||= path($cmd[0])->basename;
76 0         0 local $CWD = $self->config->dirname->to_string;
77 0         0 local $ENV{NODE_ENV} = $self->mode;
78 0         0 warn "[NPM] cd $CWD && @cmd\n" if DEBUG;
79 0 0       0 open my $NPM, '-|', @cmd or die "Can't fork @cmd: $!";
80 0 0       0 return $NPM if defined wantarray;
81 0         0 map { DEBUG && print } <$NPM>;
  0         0  
82             }
83              
84             # This is a utility function for the unit tests
85             sub _setup_working_directory {
86 1     1   9 my ($class, $dir) = @_;
87 1 50   1   5 my $remove_tree = $ENV{MOJO_NPM_CLEAN} ? 'remove_tree' : sub { };
88 1 50       9 chdir(my $work_dir = path($dir ? $dir : ('local', path($0)->basename))->to_abs->tap($remove_tree)->make_path)
    50          
89             or die "Couldn't set up working directory: $!";
90 1 50 33     497 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 1         301 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