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   876668 use v5.10;
  5         110  
6              
7 5     5   2753 use Moo 1.002000;
  5         58790  
  5         33  
8             use MooX::Options
9 5         33 protect_argv => 0,
10 5     5   10217 usage_string => '%c %o [output-filename]';
  5         19391  
11              
12 5     5   614051 use Carp;
  5         12  
  5         270  
13 5     5   2630 use JSON::MaybeXS ();
  5         29543  
  5         140  
14 5     5   2647 use Module::Load qw/ load /;
  5         5836  
  5         36  
15 5     5   2747 use Package::Stash;
  5         8663  
  5         192  
16 5     5   2557 use Ref::Util qw/ is_scalarref /;
  5         9498  
  5         387  
17 5     5   2331 use Sub::Identify 0.06 qw/ is_sub_constant /;
  5         5105  
  5         289  
18 5     5   40 use Try::Tiny;
  5         12  
  5         259  
19 5     5   2531 use Types::Common::String qw/ NonEmptySimpleStr /;
  5         614439  
  5         81  
20 5     5   2981 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   8039 use namespace::autoclean;
  5         40914  
  5         46  
28              
29             our $VERSION = 'v0.1.4';
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   222 my ($self) = @_;
88 7 100       38 if ($self->has_include) {
89 3         6 push @INC, @{$self->include};
  3         11  
90             }
91 7         30 my $namespace = $self->module;
92 7         36 load($namespace);
93 7         3438 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   38 my ($self) = @_;
104 2 50       39 if ( $self->has_symbol('%EXPORT_TAGS') ) {
105 2         123 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   222 my ($self) = @_;
119 7         62 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 66606 my ($self) = @_;
131              
132 7         18 my @imports;
133              
134 7 100       82 if ( $self->has_constants ) {
    50          
135 5         14 @imports = @{ $self->constants };
  5         27  
136             }
137             elsif ( $self->has_symbol('@EXPORT_OK') ) {
138 2         88 @imports = @{ $self->get_symbol('@EXPORT_OK') };
  2         45  
139             }
140             else {
141 0         0 croak "No \@EXPORT_OK in " . $self->module;
142             }
143              
144 7         80 my %symbols = map { $self->_import_to_symbol($_) } @imports;
  16         45  
145              
146 7 100       44 my $decl = $self->use_var ? "var" : "const";
147              
148 7         18 my $buffer = "";
149 7         76 for my $name ( sort keys %symbols ) {
150 86         145 my $val = $symbols{$name};
151 86         1312 my $json = $self->encode($val);
152 86 50       2292 $json =~ s/\n$// if $self->pretty;
153 86         244 $buffer .= "${decl} ${name} = ${json};\n";
154             }
155 7         65 return $buffer;
156             }
157              
158             sub _import_to_symbol {
159 89     89   186 my ( $self, $import ) = @_;
160              
161             state $reserved = {
162 89         149 map { $_ => 1 }
  320         709  
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       238 return ( ) if $reserved->{$import};
175              
176 88 100       336 if ( my ($name) = $import =~ /^[\$\@\%](\w.*)$/ ) {
    100          
177 12         237 my $ref = $self->get_symbol($import);
178 12 100       389 my $val = is_scalarref($ref) ? $$ref : $ref;
179 12         53 return ( $name => $val );
180             }
181             elsif ( my ($tag) = $import =~ /^[:\-](\w.*)$/ ) {
182 2 50       48 my $imports = $self->tags->{$tag}
183             or croak "No tag '${tag}' found in " . $self->module;
184 2         144 return ( map { $self->_import_to_symbol($_) } @{$imports} );
  73         216  
  2         7  
185             }
186             else {
187 74 50       1260 my $fn = $self->get_symbol( '&' . $import )
188             or croak "Cannot find symbol '${import}' in " . $self->module;
189 74 50       2111 is_sub_constant($fn) or carp "Symbol '${import}' is not a constant in " . $self->module;
190 74         136 my $val = $fn->();
191 74         213 return ( $import => $val );
192             }
193              
194             }
195              
196              
197             1;
198              
199             __END__