How to implement "JS require" function in Perl JE?

JE is a good JavaScript engine written in pure pearl. Executes generic JS code nicely, and its ability to bind perl routines to JS functions is remarkable.

What is missing is a feature require

that is implemented in "node.js" and performs a similar task as perl requires.

It would be nice to know how to implement such a function in perl and bind it to JE for example. in a simple skeleton:

use 5.016;
use warnings;
use Path::Tiny;

use JE::Destroyer;
use JE;

my $jslib = path("./jslib");

my $j = new JE;
$j->new_function("say", sub { say @_ });  # the "say" in the JS
$j->new_function("require", sub {         # the "require"

    my $source = $jslib->child($_[0])->slurp_utf8; #read the source from some file
    #how to implement the require ?
    #e.g. what JE object should be created and what to bind to it?

});

      

Any idea how the function is node.js

require

implemented in C and how to implement it in perl?

EDIT

For a more precise example, adding a more extended skeleton:

  • file ./reqtest.js

    (main program)
console.log("start");
p = require('adder');
console.log(p.add(2,3));

      

  • file ./node_modules/adder.js

    (in node_modules "- for test node.js

    )
exports.add = function() {
    var sum = 0, i = 0, args = arguments, l = args.length;
    while (i < l) {
        sum += args[i++];
    }
    return sum;
};

      

do the above as

node reqtest.js

      

prints

start
5

      

so it correctly requested adder.js

and returned the object, but running it with the following perl / JE script:

use 5.016;
use warnings;
use JE::Destroyer;
use JE;
use Scalar::Util qw( weaken );
use Path::Tiny;
use PIR;

my $script = path("reqtest.js")->slurp_utf8;

my $j = new JE;
$j->{console} = {};
$j->{console}->new_function("log", sub { say @_ });
{
    weaken(my $j = $j);
    $j->new_function(require => sub {
        my $source = get_source($_[0]);
        $j->eval($source) if $source;
    });
}

$j->eval($script);#eval the main script

JE::Destroyer::destroy($j);
undef $j;

#extremelly simplyfied
sub get_source {
    my $name = shift;
    return path("node_modules")->child("$name.js")->slurp_utf8;
}

      

prints only:

start

      

therefore "a simple implementation $j->eval($source)

require

does not follow the module specification CommonJS

- it needs something else.

+3


source to share


2 answers


After a bit of research, I created the following (extremely simplified) solution:

use 5.016;
use warnings;
use JE::Destroyer;
use JE;
use Scalar::Util qw( weaken );
use Path::Tiny;

my $script = path("reqtest.js")->slurp_utf8;

my $j = new JE;
$j->{console} = {};
$j->{console}->new_function("log", sub { say @_ });
{
    weaken(my $j = $j);   #based on ikegami previous answer
    $j->new_function(require => sub {
        my $source = get_source($_[0]);
        my $code = new JE::Object::Function $j, qw(exports), $source;
        my $exports = $j->{Object};
        $code->($exports);
        return $exports;
    });
}

$j->eval($script);#eval the main script

JE::Destroyer::destroy($j);
undef $j;

# extremelly simplyfied code search
# the real code-search for the requide("some") should try the following
# some.js some/index.js some.json some/index.json
# for the defined search-paths.
sub get_source {
    my $name = shift;
    return path("node_modules")->child("$name.js")->slurp_utf8;
}

      

The above shortest possible CommonJS implementation requires and prints the same as the node.js

example in the question



start
5

      

Of course a lot of extra work is required (like caching code), but the minimal skeleton works.

+1


source


To accomplish require.js

you can use



my $require_js = get('http://requirejs.org/docs/release/2.1.18/comments/require.js');

$j->eval($require_js);

      

0


source







All Articles