File Coverage

blib/lib/JavaScript/Const/Exporter.pm
Criterion Covered Total %
statement 83 86 96.5
branch 20 26 76.9
condition n/a
subroutine 18 18 100.0
pod 1 1 100.0
total 122 131 93.1


line stmt bran cond sub pod time code
1             package JavaScript::Const::Exporter;
2              
3             # ABSTRACT: Convert exported Perl constants to JavaScript
4              
5 5     5   926661 use v5.10;
  5         125  
6              
7 5     5   2964 use Moo 1.002000;
  5         62149  
  5         32  
8             use MooX::Options
9 5         44 protect_argv => 0,
10 5     5   10701 usage_string => '%c %o [output-filename]';
  5         20474  
11              
12 5     5   650507 use Carp;
  5         13  
  5         339  
13 5     5   3214 use JSON::MaybeXS ();
  5         31567  
  5         161  
14 5     5   2770 use Module::Load qw/ load /;
  5         6244  
  5         39  
15 5     5   2880 use Package::Stash;
  5         9642  
  5         211  
16 5     5   3038 use Ref::Util qw/ is_scalarref /;
  5         10010  
  5         459  
17 5     5   2468 use Sub::Identify 0.06 qw/ is_sub_constant /;
  5         5342  
  5         300  
18 5     5   104 use Try::Tiny;
  5         12  
  5         288  
19 5     5   2672 use Types::Common::String qw/ NonEmptySimpleStr /;
  5         637693  
  5         81  
20 5     5   3038 use Types::Standard qw/ ArrayRef Bool HashRef InstanceOf /;
  5         15  
  5         36  
21              
22             # RECOMMEND PREREQ: Cpanel::JSON::XS
23             # RECOMMEND PREREQ: Package::Stash::XS
24             # RECOMMEND PREREQ: Ref::Util::XS
25             # RECOMMEND PREREQ: Type::Tiny::XS
26              
27 5     5   8152 use namespace::autoclean;
  5         41554  
  5         24  
28              
29             our $VERSION = 'v0.1.6';
30              
31              
32             option use_var => (
33             is => 'ro',
34             isa => Bool,
35             default => 0,
36             negatable => 0,
37             short => 'u',
38             doc => 'use var instead of const',
39             );
40              
41              
42             option module => (
43             is => 'ro',
44             isa => NonEmptySimpleStr,
45             required => 1,
46             format => 's',
47             short => 'm',
48             doc => 'module name to extract constants from',
49             );
50              
51              
52             option constants => (
53             is => 'ro',
54             isa => ArrayRef [NonEmptySimpleStr],
55             predicate => 1,
56             format => 's',
57             repeatable => 1,
58             short => 'c',
59             doc => 'constants or export tags to extract',
60             );
61              
62              
63             option include => (
64             is => 'ro',
65             isa => ArrayRef [NonEmptySimpleStr],
66             predicate => 1,
67             short => 'I',
68             format => 's',
69             repeatable => 1,
70             doc => 'paths to include',
71             );
72              
73              
74             option pretty => (
75             is => 'ro',
76             isa => Bool,
77             default => 0,
78             short => 'p',
79             doc => 'enable pretty printed JSON',
80             );
81              
82              
83             has stash => (
84             is => 'lazy',
85             isa => InstanceOf ['Package::Stash'],
86             builder => sub {
87 7     7   216 my ($self) = @_;
88 7 100       40 if ($self->has_include) {
89 3         6 push @INC, @{$self->include};
  3         11  
90             }
91 7         27 my $namespace = $self->module;
92 7         39 load($namespace);
93 7         3254 return Package::Stash->new($namespace);
94             },
95             handles => [qw/ has_symbol get_symbol /],
96             );
97              
98              
99             has tags => (
100             is => 'lazy',
101             isa => HashRef,
102             builder => sub {
103 2     2   29 my ($self) = @_;
104 2 50       83 if ( $self->has_symbol('%EXPORT_TAGS') ) {
105 2         119 return $self->get_symbol('%EXPORT_TAGS');
106             }
107             else {
108 0         0 my $namespace = $self->module;
109 0         0 croak "No \%EXPORT_TAGS were found in ${namespace}";
110             }
111             }
112             );
113              
114              
115             has json => (
116             is => 'lazy',
117             builder => sub {
118 7     7   196 my ($self) = @_;
119 7         58 return JSON::MaybeXS->new(
120             utf8 => 1,
121             allow_nonref => 1,
122             pretty => $self->pretty,
123             );
124             },
125             handles => [qw/ encode /],
126             );
127              
128              
129             sub process {
130 7     7 1 62441 my ($self) = @_;
131              
132 7         18 my @imports;
133              
134 7 100       80 if ( $self->has_constants ) {
    50          
135 5         11 @imports = @{ $self->constants };
  5         29  
136             }
137             elsif ( $self->has_symbol('@EXPORT_OK') ) {
138 2         102 @imports = @{ $self->get_symbol('@EXPORT_OK') };
  2         41  
139             }
140             else {
141 0         0 croak "No \@EXPORT_OK in " . $self->module;
142             }
143              
144 7         81 my %symbols = map { $self->_import_to_symbol($_) } @imports;
  16         48  
145              
146 7 100       82 my $decl = $self->use_var ? "var" : "const";
147              
148 7         18 my $buffer = "";
149 7         71 for my $name ( sort keys %symbols ) {
150 86         138 my $val = $symbols{$name};
151 86         1335 my $json = $self->encode($val);
152 86 50       2304 $json =~ s/\n$// if $self->pretty;
153 86         228 $buffer .= "${decl} ${name} = ${json};\n";
154             }
155 7         70 return $buffer;
156             }
157              
158             sub _import_to_symbol {
159 89     89   174 my ( $self, $import ) = @_;
160              
161             state $reserved = {
162 89         159 map { $_ => 1 }
  320         698  
163             qw/
164             abstract arguments await boolean break byte case catch char class
165             const continue debugger default delete do double else enum eval
166             export extends false final finally float for function goto if
167             implements import in instanceof int interface let long native new
168             null package private protected public return short static super
169             switch synchronized this throw throws transient true try typeof
170             var void volatile while with yield
171             /
172             };
173              
174 89 100       227 return ( ) if $reserved->{$import};
175              
176 88 100       321 if ( my ($name) = $import =~ /^[\$\@\%](\w.*)$/ ) {
    100          
177 12         218 my $ref = $self->get_symbol($import);
178 12 100       395 my $val = is_scalarref($ref) ? $$ref : $ref;
179 12         53 return ( $name => $val );
180             }
181             elsif ( my ($tag) = $import =~ /^[:\-](\w.*)$/ ) {
182 2 50       49 my $imports = $self->tags->{$tag}
183             or croak "No tag '${tag}' found in " . $self->module;
184 2         141 return ( map { $self->_import_to_symbol($_) } @{$imports} );
  73         172  
  2         6  
185             }
186             else {
187 74 50       1264 my $fn = $self->get_symbol( '&' . $import )
188             or croak "Cannot find symbol '${import}' in " . $self->module;
189 74 50       2227 is_sub_constant($fn) or carp "Symbol '${import}' is not a constant in " . $self->module;
190 74         137 my $val = $fn->();
191 74         217 return ( $import => $val );
192             }
193              
194             }
195              
196              
197             1;
198              
199             __END__