Data-Dump-JavaScript/lib/Data/Dump/JavaScript.pm
package Data::Dump::JavaScript;
$Data::Dump::JavaScript::VERSION = '0.002';
use strict;
use warnings;
use Exporter 'import';
use Scalar::Util 'blessed';
use Encode ();
use B;
# ABSTRACT: Pretty printing of data structures as JavaScript
our @EXPORT_OK = qw( dump_javascript false true );
# Literal names
# Users may override Booleans with literal 0 or 1 if desired.
our($FALSE, $TRUE) = map { bless \(my $dummy = $_), 'Data::Dump::JavaScript::_Bool' } 0, 1;
# Escaped special character map with u2028 and u2029
my %ESCAPE = (
'"' => '"',
'\\' => '\\',
'/' => '/',
'b' => "\x08",
'f' => "\x0c",
'n' => "\x0a",
'r' => "\x0d",
't' => "\x09",
'u2028' => "\x{2028}",
'u2029' => "\x{2029}"
);
my %REVERSE = map { $ESCAPE{$_} => "\\$_" } keys %ESCAPE;
for(0x00 .. 0x1f) {
my $packed = pack 'C', $_;
$REVERSE{$packed} = sprintf '\u%.4X', $_ unless defined $REVERSE{$packed};
}
my $indent_level;
# standard and semi-standard JS default
my $indent_count = 2;
sub false () {$FALSE} ## no critic (prototypes)
sub true () {$TRUE} ## no critic (prototypes)
sub dump_javascript {
$indent_level = 0;
Encode::encode 'UTF-8', _encode_value(shift);
}
sub _encode_key {
my $str = shift;
$str =~ s!([\x00-\x1f\x{2028}\x{2029}\\"/])!$REVERSE{$1}!gs;
return "$str";
}
sub _encode_array {
my $str = '[';
$str .= "\n"
if scalar @{$_[0]} > 1;
$indent_level++;
$str .= join(",\n", map { scalar @{$_[0]} > 1 ? _get_indented(_encode_value($_)) : _encode_value($_) } @{$_[0]});
$indent_level--;
$str .= scalar @{$_[0]} > 1
? "\n" . _get_indented("]")
: ']';
}
sub _encode_object {
my $object = shift;
my $str = '{';
$str .= "\n"
if keys %$object > 0;
$indent_level++;
my @pairs = map { _get_indented(_encode_key($_)) . ': ' . _encode_value($object->{$_}) }
sort keys %$object;
#$str .= join(",\n", @pairs) . "\n";
$str .= join(",\n", @pairs);
$indent_level--;
$str .= keys %$object > 0
? "\n" . _get_indented("}")
: '}';
return $str;
}
sub _encode_string {
my $str = shift;
$str =~ s!([\x00-\x1f\x{2028}\x{2029}\\"/])!$REVERSE{$1}!gs;
return "'$str'";
}
sub _encode_value {
my $value = shift;
# Reference
if (my $ref = ref $value) {
# Object
return _encode_object($value) if $ref eq 'HASH';
# Array
return _encode_array($value) if $ref eq 'ARRAY';
# True or false
return $$value ? 'true' : 'false' if $ref eq 'SCALAR';
return $value ? 'true' : 'false' if $ref eq 'Data::Dump::JavaScript::_Bool';
# Blessed reference with TO_JSON method
if (blessed $value && (my $sub = $value->can('TO_JSON'))) {
return _encode_value($value->$sub);
}
}
# Null
return 'null' unless defined $value;
# Number (bitwise operators change behavior based on the internal value type)
return $value
if B::svref_2object(\$value)->FLAGS & (B::SVp_IOK | B::SVp_NOK)
# filter out "upgraded" strings whose numeric form doesn't strictly match
&& 0 + $value eq $value
# filter out inf and nan
&& $value * 0 == 0;
# String
return _encode_string($value);
}
sub _get_indented {
return ' ' x ( $indent_level * $indent_count ) . shift;
}
# Emulate boolean type
package Data::Dump::JavaScript::_Bool;
$Data::Dump::JavaScript::_Bool::VERSION = '0.002';
use overload '""' => sub { ${$_[0]} }, fallback => 1;
1;
__END__
=pod
=encoding UTF-8
=head1 NAME
Data::Dump::JavaScript - Pretty printing of data structures as JavaScript
=head1 VERSION
version 0.002
=head1 SYNOPSIS
use Data::Dump::JavaScript qw( dump_javascript true false );
my $javascript = dump_javascript({ foo => [1, 2], bar => 'hello!', baz => true });
=head1 DESCRIPTION
Data::Dump::JavaScript is a fork of L<JSON::Tiny> version 0.55 which outputs
pretty-printed JavaScript.
The indention is according to L<JavaScript Standard Style|http://standardjs.com>
and L<JavaScript Semi-Standard Style|https://github.com/Flet/semistandard>.
Hash keys are sorted in standard string comparison order.
=head1 FUNCTIONS
Data::Dump::JavaScript implements the following functions, which can be imported
individually.
=head2 false
my $false = false;
False value, used because Perl has no equivalent.
=head2 true
my $true = true;
True value, used because Perl has no native equivalent.
=head2 dump_javascript
my $bytes = dump_javascript({ foo => 'bar' });
Encode Perl value to UTF-8 encoded JavaScript.
=head1 SEE ALSO
=over 4
=item L<Data::JavaScript>
Can only dump Perl structures as JavaScript variable assignment.
No boolean handling, no formatting.
=item L<Data::JavaScript::Anon>
No boolean handling, no formatting.
=back
=head1 AUTHOR
Alexander Hartmaier <abraxxa@cpan.org>
=head1 COPYRIGHT AND LICENSE
This software is copyright (c) 2020 by Alexander Hartmaier.
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.
=cut