File Coverage

blib/lib/JavaScript/Const/Exporter.pm
Criterion Covered Total %
statement 80 83 96.3
branch 20 26 76.9
condition n/a
subroutine 17 17 100.0
pod 1 1 100.0
total 118 127 92.9


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