PiotrDabkowski / Js2Py

JavaScript to Python Translator & JavaScript interpreter written in 100% pure Python🚀 Try it online:
http://piter.io/projects/js2py
MIT License
2.48k stars 260 forks source link

js2py.require('paper') => AssertionError: Could not link required node_modules #125

Open doom-fr opened 6 years ago

doom-fr commented 6 years ago

when I do (in the parent folder of node_modules) :

import js2py
js2py.require('paper')

Here is the error I get :

js2py.require('paper')
v6.11.2
La syntaxe du nom de fichier, de répertoire ou de volume est incorrecte.
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "C:\Users\doom\AppData\Local\Programs\Python\Python35\lib\site-packages\js2py\node_import.py", line 46, in require
    _init()
  File "C:\Users\doom\AppData\Local\Programs\Python\Python35\lib\site-packages\js2py\node_import.py", line 13, in _init
    assert subprocess.call('cd %s;npm install babel-core babel-cli babel-preset-es2015 babel-polyfill babelify browserify' % repr(DIRNAME), shell=True, cwd=DIRNAME)==0, 'Could not link required node_modules'
AssertionError: Could not link required node_modules

I have tried to install manually npm install babel-core babel-cli babel-preset-es2015 babel-polyfill babelify browserify but I get the same error.

Have tried with another js lib (mustache and get same error) Both js libs were installed with npm install paper mustache

OS : windows

alex-pancho commented 6 years ago

now i have some problem, i try install: npm -g install babel-core babel-cli babel-preset-es2015 babel-polyfill babelify browserify may be -g key helps you, but not work in my case

alex-pancho commented 6 years ago

replaced into %Y:\or\python\path%\Lib\site-packages\js2py node_import.py to file into arch - work in windows https://1drv.ms/u/s!AgThWBJfRj69gvN_zJ7m_ta2KZQ6EA or insert inside Node_imprt this code:

__all__ = ['require']
import subprocess, os, codecs, glob
from .evaljs import translate_js
import six
DID_INIT = False
DIRNAME = os.path.dirname(os.path.abspath(__file__))
#PY_NODE_MODULES_PATH = os.path.join(DIRNAME, 'py_node_modules')
PY_NODE_MODULES_PATH = os.path.join(DIRNAME, 'node_modules')
def _init():
    global DID_INIT
    if DID_INIT:
        return
    assert subprocess.call('node -v', shell=True, cwd=DIRNAME)==0, 'You must have node installed! run: brew install node'
    #assert subprocess.call('cd %s;npm install babel-core babel-cli babel-preset-es2015 babel-polyfill babelify browserify' % repr(DIRNAME), shell=True, cwd=DIRNAME)==0, 'Could not link required node_modules'
    assert subprocess.call([
        'npm',
        'install',
        'babel-core',
        'babel-cli',
        'babel-preset-es2015',
        'babel-polyfill',
        'babelify',
        'browserify'
    ], shell=True, cwd=DIRNAME)==0, 'Could not link required node_modules'
    DID_INIT = True

ADD_TO_GLOBALS_FUNC = '''
;function addToGlobals(name, obj) {
    if (!Object.prototype.hasOwnProperty('_fake_exports')) {
        Object.prototype._fake_exports = {};
    }
    Object.prototype._fake_exports[name] = obj;
};

'''
# subprocess.call("""node -e 'require("browserify")'""", shell=True)
GET_FROM_GLOBALS_FUNC = '''
;function getFromGlobals(name) {
    if (!Object.prototype.hasOwnProperty('_fake_exports')) {
        throw Error("Could not find any value named "+name);
    }
    if (Object.prototype._fake_exports.hasOwnProperty(name)) {
        return Object.prototype._fake_exports[name];
    } else {
        throw Error("Could not find any value named "+name);
    }
};

'''

def require(module_name, include_polyfill=False, update=False):
    assert isinstance(module_name, str), 'module_name must be a string!'
    py_name = module_name.replace('-', '_')
    module_filename = '%s.py'%py_name
    var_name = py_name.rpartition('/')[-1]
    #print('NODE_MODULES_PATH path : ', os.path.join(NODE_MODULES_PATH, module_filename))
    if not os.path.exists(os.path.join(PY_NODE_MODULES_PATH, module_filename)) or update:
    #if not os.path.exists(os.path.join(NODE_MODULES_PATH, module_filename)) or update:
        _init()
        in_file_name = 'tmp0in439341018923js2py.js'
        out_file_name = 'tmp0out439341018923js2py.js'
        code = ADD_TO_GLOBALS_FUNC
        if include_polyfill:
            code += "\n;require('babel-polyfill');\n"
        code += """
        var module_temp_love_python = require(%s);
        addToGlobals(%s, module_temp_love_python);
        """ % (repr(module_name), repr(module_name))
        with open(os.path.join(DIRNAME, in_file_name), 'wb') as f:
            f.write(code.encode('utf-8') if six.PY3 else code)

        pkg_name = module_name.partition('/')[0]
        print('pkg_name : ', pkg_name)
        print('module_name : ', module_name)
        # make sure the module is installed
        #assert subprocess.call('cd %s;npm install %s' %(repr(DIRNAME), pkg_name), shell=True, cwd=DIRNAME)==0, 'Could not install the required module: ' + pkg_name
        assert subprocess.call([
            'npm',
            'install',
            pkg_name
        ], shell=True, cwd=DIRNAME)==0, 'Could not install the required module: ' + pkg_name

        # convert the module
        assert subprocess.call(
            '''node -e "(require('browserify')('./%s').bundle(function (err,data) {fs.writeFile('%s', require('babel-core').transform(data, {'presets': require('babel-preset-es2015')}).code, ()=>{});}))"''' % (in_file_name, out_file_name),
            shell=True,
            cwd=DIRNAME,
        )==0, 'Error when converting module to the js bundle'

        os.remove(os.path.join(DIRNAME, in_file_name))
        with codecs.open(os.path.join(DIRNAME, out_file_name), "r", "utf-8") as f:
            js_code = f.read()
        os.remove(os.path.join(DIRNAME, out_file_name))

        js_code += GET_FROM_GLOBALS_FUNC
        js_code += ';var %s = getFromGlobals(%s);%s' % (var_name, repr(module_name), var_name)
        print('Please wait, translating...')
        py_code = translate_js(js_code)

        dirname = os.path.dirname(os.path.join(PY_NODE_MODULES_PATH, module_filename))
        if not os.path.isdir(dirname):
            os.makedirs(dirname)
        with open(os.path.join(PY_NODE_MODULES_PATH, module_filename), 'wb') as f:
            f.write(py_code.encode('utf-8') if six.PY3 else py_code)
    else:
        with codecs.open(os.path.join(PY_NODE_MODULES_PATH, module_filename), "r", "utf-8") as f:
            py_code = f.read()

    context = {}
    exec(py_code, context)
    return context['var'][var_name].to_py()
chaseSpace commented 5 years ago

Solution On windows

You just need to do that follow:
find out file: site-packages/js2py/node_import.py, change line 17 and 79 old:

 assert subprocess.call(
        'cd %s;npm install babel-core babel-cli babel-preset-es2015 babel-polyfill babelify browserify browserify-shim'
        % repr(DIRNAME),
        shell=True,
        cwd=DIRNAME) == 0, 'Could not link required node_modules'

new:

assert subprocess.call(
        'npm install babel-core babel-cli babel-preset-es2015 babel-polyfill babelify browserify browserify-shim',
        shell=True,
        cwd=DIRNAME) == 0, 'Could not link required node_modules'

Remove "cd %s;", meanwhile don't forget to remove "% repr(DIRNAME)" at the end. so is line 79.

The dir of execution has been specified with cwd, so do not need "cd xxx;".
Sure, the addition of "cd xxx;" is not a error, that may be a bug for subprocess. I did a test follow:

c = 'dir'  # succ
c = 'cd c:\\;dir'  # err
encoding = 'gbk' # do need if your OS LANGUAGE is 中文.
child = subprocess.Popen(c, shell=True, stdout=subprocess.PIPE, encoding=encoding, cwd='c:\py36\Lib\site-packages\js2py')
print(child.stdout.read())

The output is unintelligible text. And I get the plain text for that by:

c = 'cd c:\\;dir'
out = subprocess.getstatusoutput()
print(out)  # (returncode=1, output='系统找不到指定的路径(The system could not find the specified path)。')

So is clear now, that's a bug here for subprocess. Because semicolon ; is valid split charactor of winOS shell .
Welcome to discuss with me.
Remark of Chinese: 如果是国内的同学,务必先确保你的电脑能够正确执行npm install命令,当然你需要先安装nodejs环境,然后可以通过 npm config set registry https://registry.npm.taobao.org 设置淘宝的源,然后就OK了。

chenxs1427 commented 4 years ago

@alex-pancho thanks bro, it helps a lot

jomaldxbox commented 3 years ago

Hello! Wondering if this fix got checked in? Looks like I am running into the same problem, and I would need to go into the code to fix it

rodolfogf-dev commented 3 years ago

Im facing this same issue as @jomaldxbox . I already did all the fix steps commented previously but no success with all of them. Anyone still having this same problem? I could provide more details if necessary, but i tried with the same 'paper' lib, on the require method.

rodolfogf-dev commented 3 years ago

Hello! Wondering if this fix got checked in? Looks like I am running into the same problem, and I would need to go into the code to fix it

@jomaldxbox After some time figuring out how to resolve this, I compared and mixed both @alex-pancho and @chaseSpace solutions. I`m no python expert (to be honest I recently started to code and study, so my apologies if I made some mistake about it), but I changed my site-packages/js2py/node_import.py file resulting on this code bellow. After that it worked fine :)

__all__ = ['require']

import subprocess, os, codecs, glob
from .evaljs import translate_js, DEFAULT_HEADER
from .translators.friendly_nodes import is_valid_py_name
import six
import tempfile
import hashlib
import random

DID_INIT = False
DIRNAME = os.path.dirname(os.path.abspath(__file__))
PY_NODE_MODULES_PATH = os.path.join(DIRNAME, 'node_modules')

def _init():
    global DID_INIT
    if DID_INIT:
        return
    assert subprocess.call(
        'node -v', shell=True, cwd=DIRNAME
    ) == 0, 'You must have node installed! run: brew install node'
    #assert subprocess.call(        'cd %s;npm install babel-core babel-cli babel-preset-es2015 babel-polyfill babelify browserify browserify-shim'% repr(DIRNAME),shell=True,cwd=DIRNAME) == 0, 'Could not link required node_modules'
    assert subprocess.call([
        'npm',
        'install',
        'babel-core',
        'babel-cli',
        'babel-preset-es2015',
        'babel-polyfill',
        'babelify',
        'browserify'
    ], shell=True, cwd=DIRNAME)==0, 'Could not link required node_modules'
    DID_INIT = True

ADD_TO_GLOBALS_FUNC = '''
;function addToGlobals(name, obj) {
    if (!Object.prototype.hasOwnProperty('_fake_exports')) {
        Object.prototype._fake_exports = {};
    }
    Object.prototype._fake_exports[name] = obj;
};

'''
# subprocess.call("""node -e 'require("browserify")'""", shell=True)
GET_FROM_GLOBALS_FUNC = '''
;function getFromGlobals(name) {
    if (!Object.prototype.hasOwnProperty('_fake_exports')) {
        throw Error("Could not find any value named "+name);
    }
    if (Object.prototype._fake_exports.hasOwnProperty(name)) {
        return Object.prototype._fake_exports[name];
    } else {
        throw Error("Could not find any value named "+name);
    }
};

'''

def _get_module_py_name(module_name):
    return module_name.replace('-', '_')

def _get_module_var_name(module_name):
    cand =  _get_module_py_name(module_name).rpartition('/')[-1]
    if not is_valid_py_name(cand):
        raise ValueError(
            "Invalid Python module name %s (generated from %s). Unsupported/invalid npm module specification?" % (
                repr(cand), repr(module_name)))
    return cand

def _get_and_translate_npm_module(module_name, include_polyfill=False, update=False, maybe_version_str=""):
    assert isinstance(module_name, str), 'module_name must be a string!'

    py_name = _get_module_py_name(module_name)
    module_filename = '%s.py' % py_name
    var_name = _get_module_var_name(module_name)
    if not os.path.exists(os.path.join(PY_NODE_MODULES_PATH,
                                       module_filename)) or update:
        _init()
        module_hash = hashlib.sha1(module_name.encode("utf-8")).hexdigest()[:15]
        version = random.randrange(10000000000000)
        in_file_name = 'in_%s_%d.js' % (module_hash, version)
        out_file_name = 'out_%s_%d.js' % (module_hash, version)
        code = ADD_TO_GLOBALS_FUNC
        if include_polyfill:
            code += "\n;require('babel-polyfill');\n"
        code += """
        var module_temp_love_python = require(%s);
        addToGlobals(%s, module_temp_love_python);
        """ % (repr(module_name), repr(module_name))
        with open(os.path.join(DIRNAME, in_file_name), 'wb') as f:
            f.write(code.encode('utf-8') if six.PY3 else code)

        pkg_name = module_name.partition('/')[0]
        if maybe_version_str:
            pkg_name += '@' + maybe_version_str
        # make sure the module is installed
        assert subprocess.call(
            'cd %s;npm install %s' % (repr(DIRNAME), pkg_name),
            shell=True,
            cwd=DIRNAME
        ) == 0, 'Could not install the required module: ' + pkg_name

        # convert the module
        assert subprocess.call(
            '''node -e "(require('browserify')('./%s').bundle(function (err,data) {if (err) {console.log(err);throw new Error(err);};fs.writeFile('%s', require('babel-core').transform(data, {'presets': require('babel-preset-es2015')}).code, ()=>{});}))"'''
            % (in_file_name, out_file_name),
            shell=True,
            cwd=DIRNAME,
        ) == 0, 'Error when converting module to the js bundle'

        os.remove(os.path.join(DIRNAME, in_file_name))
        with codecs.open(os.path.join(DIRNAME, out_file_name), "r",
                         "utf-8") as f:
            js_code = f.read()
        print("Bundled JS library dumped at: %s" % os.path.join(DIRNAME, out_file_name))
        if len(js_code) < 50:
            raise RuntimeError("Candidate JS bundle too short - likely browserify issue.")
        js_code += GET_FROM_GLOBALS_FUNC
        js_code += ';var %s = getFromGlobals(%s);%s' % (
            var_name, repr(module_name), var_name)
        print('Please wait, translating...')
        py_code = translate_js(js_code)

        dirname = os.path.dirname(
            os.path.join(PY_NODE_MODULES_PATH, module_filename))
        if not os.path.isdir(dirname):
            os.makedirs(dirname)
        with open(os.path.join(PY_NODE_MODULES_PATH, module_filename),
                  'wb') as f:
            f.write(py_code.encode('utf-8') if six.PY3 else py_code)
    else:
        with codecs.open(
                os.path.join(PY_NODE_MODULES_PATH, module_filename), "r",
                "utf-8") as f:
            py_code = f.read()
    return py_code

def require(module_name, include_polyfill=False, update=False):
    assert isinstance(module_name, str), 'module_name must be a string!'
    py_name = module_name.replace('-', '_')
    module_filename = '%s.py'%py_name
    var_name = py_name.rpartition('/')[-1]
    #print('NODE_MODULES_PATH path : ', os.path.join(NODE_MODULES_PATH, module_filename))
    if not os.path.exists(os.path.join(PY_NODE_MODULES_PATH, module_filename)) or update:
    #if not os.path.exists(os.path.join(NODE_MODULES_PATH, module_filename)) or update:
        _init()
        in_file_name = 'tmp0in439341018923js2py.js'
        out_file_name = 'tmp0out439341018923js2py.js'
        code = ADD_TO_GLOBALS_FUNC
        if include_polyfill:
            code += "\n;require('babel-polyfill');\n"
        code += """
        var module_temp_love_python = require(%s);
        addToGlobals(%s, module_temp_love_python);
        """ % (repr(module_name), repr(module_name))
        with open(os.path.join(DIRNAME, in_file_name), 'wb') as f:
            f.write(code.encode('utf-8') if six.PY3 else code)

        pkg_name = module_name.partition('/')[0]
        print('pkg_name : ', pkg_name)
        print('module_name : ', module_name)
        # make sure the module is installed
        #assert subprocess.call('cd %s;npm install %s' %(repr(DIRNAME), pkg_name), shell=True, cwd=DIRNAME)==0, 'Could not install the required module: ' + pkg_name
        assert subprocess.call([
            'npm',
            'install',
            pkg_name
        ], shell=True, cwd=DIRNAME)==0, 'Could not install the required module: ' + pkg_name

        # convert the module
        assert subprocess.call(
            '''node -e "(require('browserify')('./%s').bundle(function (err,data) {fs.writeFile('%s', require('babel-core').transform(data, {'presets': require('babel-preset-es2015')}).code, ()=>{});}))"''' % (in_file_name, out_file_name),
            shell=True,
            cwd=DIRNAME,
        )==0, 'Error when converting module to the js bundle'

        os.remove(os.path.join(DIRNAME, in_file_name))
        with codecs.open(os.path.join(DIRNAME, out_file_name), "r", "utf-8") as f:
            js_code = f.read()
        os.remove(os.path.join(DIRNAME, out_file_name))

        js_code += GET_FROM_GLOBALS_FUNC
        js_code += ';var %s = getFromGlobals(%s);%s' % (var_name, repr(module_name), var_name)
        print('Please wait, translating...')
        py_code = translate_js(js_code)

        dirname = os.path.dirname(os.path.join(PY_NODE_MODULES_PATH, module_filename))
        if not os.path.isdir(dirname):
            os.makedirs(dirname)
        with open(os.path.join(PY_NODE_MODULES_PATH, module_filename), 'wb') as f:
            f.write(py_code.encode('utf-8') if six.PY3 else py_code)
    else:
        with codecs.open(os.path.join(PY_NODE_MODULES_PATH, module_filename), "r", "utf-8") as f:
            py_code = f.read()

    context = {}
    exec(py_code, context)
    return context['var'][var_name].to_py()
birdflyi commented 10 months ago

A site-packages/js2py/node_import.py file to require my custom module.

node_import.py:

__all__ = ['require']

import subprocess, os, codecs, glob
from .evaljs import translate_js, DEFAULT_HEADER
from .translators.friendly_nodes import is_valid_py_name
import six
import tempfile
import hashlib
import random

DID_INIT = False
DIRNAME = tempfile.mkdtemp()
PY_NODE_MODULES_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'py_node_modules')

def _init():
    global DID_INIT
    if DID_INIT:
        return
    assert subprocess.call(
        'node -v', shell=True, cwd=DIRNAME
    ) == 0, 'You must have node installed! run: brew install node'
    assert subprocess.call(
        'npm install babel-core babel-cli babel-preset-es2015 babel-polyfill babelify browserify browserify-shim'
        ,
        shell=True,
        cwd=DIRNAME) == 0, 'Could not link required node_modules'
    DID_INIT = True

ADD_TO_GLOBALS_FUNC = '''
;function addToGlobals(name, obj) {
    if (!Object.prototype.hasOwnProperty('_fake_exports')) {
        Object.prototype._fake_exports = {};
    }
    Object.prototype._fake_exports[name] = obj;
};

'''
# subprocess.call("""node -e 'require("browserify")'""", shell=True)
GET_FROM_GLOBALS_FUNC = '''
;function getFromGlobals(name) {
    if (!Object.prototype.hasOwnProperty('_fake_exports')) {
        throw Error("Could not find any value named "+name);
    }
    if (Object.prototype._fake_exports.hasOwnProperty(name)) {
        return Object.prototype._fake_exports[name];
    } else {
        throw Error("Could not find any value named "+name);
    }
};

'''

def _get_module_py_name(module_name):
    return module_name.replace('-', '_')

def _get_module_var_name(module_name):
    cand =  _get_module_py_name(module_name).rpartition('/')[-1]
    if not is_valid_py_name(cand):
        raise ValueError(
            "Invalid Python module name %s (generated from %s). Unsupported/invalid npm module specification?" % (
                repr(cand), repr(module_name)))
    return cand

def _get_and_translate_npm_module(module_name, include_polyfill=False, update=False, maybe_version_str="", **kwargs):
    assert isinstance(module_name, str), 'module_name must be a string!'

    py_name = _get_module_py_name(module_name)
    module_filename = '%s.py' % py_name
    var_name = _get_module_var_name(module_name)
    if not os.path.exists(os.path.join(PY_NODE_MODULES_PATH,
                                       module_filename)) or update:
        _init()
        module_hash = hashlib.sha1(module_name.encode("utf-8")).hexdigest()[:15]
        version = random.randrange(10000000000000)
        in_file_name = 'in_%s_%d.js' % (module_hash, version)
        out_file_name = 'out_%s_%d.js' % (module_hash, version)
        code = ADD_TO_GLOBALS_FUNC

        local_root = ""
        module_lib_settings = ""
        local_lib_dir_untracked_list = []
        abs_module_name = ""
        if module_name.startswith('.'):
            if module_name.startswith('./'):
                local_root = os.getcwd()
                abs_module_name = os.path.join(local_root, module_name.removeprefix('./'))
            elif module_name.startswith('../'):
                local_root = os.path.dirname(os.getcwd())
                abs_module_name = os.path.join(local_root, module_name.removeprefix('../'))
            else:
                pass
        else:
            abs_module_name = module_name
            module_path_strs = module_name.replace("\\", '/').split('/')
            if len(module_path_strs) > 1:
                local_root = '/'.join(module_path_strs[:-1])
                # module_name = module_path_strs[-1]
            else:
                pass
        abs_module_name = abs_module_name.replace('\\', '/')

        if kwargs.get("local_lib"):
            local_lib_dir_str = kwargs.get("local_lib", "")
            local_lib_dir_list = [local_lib_dir_str]
        else:
            local_lib_dir_list = []

        if local_root:
            local_lib_dir_list = [local_root] + local_lib_dir_list
        module_paths = eval(subprocess.getoutput('''node -e "console.log(require.resolve.paths('somemodule'));"'''))
        module_paths = [s.replace('\\', '/') for s in module_paths]
        for local_lib_dir in local_lib_dir_list:
            local_lib_dir = local_lib_dir.replace('\\', '/')
            if local_lib_dir not in module_paths:
                module_paths.append(local_lib_dir)
                local_lib_dir_untracked_list.append(local_lib_dir)
        # module_lib_settings = """const { exec } = require("child_process");
        # const command = "export PATH=$PATH:'%s'";
        # exec(command, (error, stdout, stderr) => {
        #   if (error) {
        #     console.error(`Failed to execute command: ${error}`);
        #     return;
        #   }
        #   console.log(stdout);
        # });
        # """ % "': '".join(local_lib_dir_untracked_list)
        # code += '\n' + module_lib_settings + '\n'

        module_name = abs_module_name

        if include_polyfill:
            code += "\n;require('babel-polyfill');\n"
        code += """
        var module_temp_love_python = require(%s);
        addToGlobals(%s, module_temp_love_python);
        """ % (repr(module_name), repr(module_name))
        with open(os.path.join(DIRNAME, in_file_name), 'wb') as f:
            f.write(code.encode('utf-8') if six.PY3 else code)

        pkg_name = module_name.partition('/')[0]
        if not local_lib_dir_untracked_list and not module_name.__contains__('.') and not module_name.__contains__('/'):
            if maybe_version_str:
                pkg_name += '@' + maybe_version_str

            # make sure the module is installed
            assert subprocess.call(
                'npm install %s' % (pkg_name),
                shell=True,
                cwd=DIRNAME
            ) == 0, 'Could not install the required module: ' + pkg_name

        # convert the module
        assert subprocess.call(
            '''node -e "%s (require('browserify')('./%s').bundle(function (err,data) {if (err) {console.log(err);throw new Error(err);};fs.writeFile('%s', require('babel-core').transform(data, {'presets': require('babel-preset-es2015')}).code, ()=>{});}))"'''
            % (module_lib_settings, in_file_name, out_file_name),
            shell=True,
            cwd=DIRNAME,
        ) == 0, 'Error when converting module to the js bundle'

        os.remove(os.path.join(DIRNAME, in_file_name))
        with codecs.open(os.path.join(DIRNAME, out_file_name), "r",
                         "utf-8") as f:
            js_code = f.read()
        print("Bundled JS library dumped at: %s" % os.path.join(DIRNAME, out_file_name))
        if len(js_code) < 50:
            raise RuntimeError("Candidate JS bundle too short - likely browserify issue.")
        js_code += GET_FROM_GLOBALS_FUNC
        js_code += ';var %s = getFromGlobals(%s);%s' % (
            var_name, repr(module_name), var_name)
        print('Please wait, translating...')
        py_code = translate_js(js_code)

        dirname = os.path.dirname(
            os.path.join(PY_NODE_MODULES_PATH, module_filename))
        if not os.path.isdir(dirname):
            os.makedirs(dirname)
        with open(os.path.join(PY_NODE_MODULES_PATH, module_filename),
                  'wb') as f:
            f.write(py_code.encode('utf-8') if six.PY3 else py_code)
    else:
        with codecs.open(
                os.path.join(PY_NODE_MODULES_PATH, module_filename), "r",
                "utf-8") as f:
            py_code = f.read()
    return py_code

def require(module_name, include_polyfill=True, update=False, context=None, **kwargs):
    """
    Installs the provided npm module, exports a js bundle via browserify, converts to ECMA 5.1 via babel and
    finally translates the generated JS bundle to Python via Js2Py.
    Returns a pure python object that behaves like the installed module. Nice!

    :param module_name: Name of the npm module to require. For example 'esprima'. Supports specific versions via @
        specification. Eg: 'crypto-js@3.3'.
    :param include_polyfill: Whether the babel-polyfill should be included as part of the translation. May be needed
    for some modules that use unsupported features of JS6 such as Map or typed arrays.
    :param update: Whether to force update the translation. Otherwise uses a cached version if exists.
    :param context: Optional context in which the translated module should be executed in. If provided, the
        header (js2py imports) will be skipped as it is assumed that the context already has all the necessary imports.
    :return: The JsObjectWrapper containing the translated module object. Can be used like a standard python object.
    """
    module_name, maybe_version = (module_name+"@@@").split('@')[:2]

    py_code = _get_and_translate_npm_module(module_name, include_polyfill=include_polyfill, update=update,
                                            maybe_version_str=maybe_version, **kwargs)
    # this is a bit hacky but we need to strip the default header from the generated code...
    if context is not None:
        if not py_code.startswith(DEFAULT_HEADER):
            # new header version? retranslate...
            assert not update, "Unexpected header."
            py_code = _get_and_translate_npm_module(module_name, include_polyfill=include_polyfill, update=True, **kwargs)
            assert py_code.startswith(DEFAULT_HEADER), "Unexpected header."
        py_code = py_code[len(DEFAULT_HEADER):]
    context = {} if context is None else context
    exec(py_code, context)
    return context['var'][_get_module_var_name(module_name)].to_py()

The ./tst.py file:

tst.py:

import js2py
import os
import re

def path_format(matched):
    return ''.join(list(map(lambda s: str(s).replace('\\', '/'), str(matched.group()))))

cur_dir = os.path.abspath('.').replace('\\', '/')
local_lib = cur_dir
a = js2py.require('./node_modules/add_numbers1', update=True)
a = js2py.require('./node_modules/add_numbers1', update=True, local_lib=local_lib)
a = js2py.require(os.path.join(local_lib, 'node_modules/add_numbers1').replace('\\', '/'), update=True)
print(a(2, 3))

# js2py.require('./node_modules/index')
js_path = './node_modules/index.js'
js_abs_path = os.path.join(cur_dir, js_path).replace('\\', '/')
local_lib = os.path.dirname(js_abs_path)
with open(js_path, 'r', encoding='utf-8') as f:
   js_index = f.read()
js_index = re.sub("require\(.+\)", path_format, js_index)
js_index = js_index.replace('../', os.path.dirname(local_lib) + '/').replace('./', local_lib + '/')
context = js2py.EvalJs(enable_require=True)
context.execute(js_index)
f_add_index = context.eval("addNumbers")
print('addNumber: %s, var s: %s' % (f_add_index(2, 3), context.s))

Project files ./node_modules/index.js and ./node_modules/add_numbers1.js:

index.js :

const addNumbers = require("./add_numbers1");
var s = "this is a var";
console.log(addNumbers(2, 2));

add_numbers1.js:

module.exports = function addNumbers(num1, num2) {
  return num1 + num2;
};

However, when I migrate to a production project, an ArrowFunction support error comes out: js2py.internals.simplex.JsException: SyntaxError: Line 63: ArrowFunction is not supported by ECMA 5.1. I am eagerly looking forward to its support for ES6.❤️‍🔥