Simple JS in Node.js

J2V8 support to run Node.js and the JVM in a single process.

import com.eclipsesource.v8.NodeJS;

public class JavaClass {
    final NodeJS nodeJS = NodeJS.createNodeJS();
}

But I got exception when start Node.js.

E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.hsy.wrapper, PID: 32371
    java.lang.RuntimeException: Unable to start activity ComponentInfo{com.hsy.wrapper/com.hsy.wrapper.MainActivity}: java.lang.UnsupportedOperationException: StartNodeJS Not Supported.
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2416)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2476)
        at android.app.ActivityThread.-wrap11(ActivityThread.java)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1344)
        at android.os.Handler.dispatchMessage(Handler.java:102)
        at android.os.Looper.loop(Looper.java:148)
        at android.app.ActivityThread.main(ActivityThread.java:5417)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
    Caused by: java.lang.UnsupportedOperationException: StartNodeJS Not Supported.    
        at com.eclipsesource.v8.V8._startNodeJS(Native Method)
        at com.eclipsesource.v8.V8.createNodeRuntime(V8.java:1013)
        at com.eclipsesource.v8.NodeJS.createNodeJS(NodeJS.java:101)
        at com.eclipsesource.v8.NodeJS.createNodeJS(NodeJS.java:48)
        at com.hsy.wrapper.JavaClass.NodeJS.createNodeJS(JavaClass.java:4)

The author of J2V8 said that "The node wrappers are not available on Android" at GitHub issue #216. Someone said that "You need to recompile the JNI source code with-D NODE_COMPATIBLE=1option." According to Arnold Schrijver's try, I do not want to rebuild the J2V8 source code by myself. I would like to package the JavaScript programs and NPM (Node Package Manager) modules to single JavaScript file.

J2V8 issue #216

How to use NodeJS in Android using J2V8

Viable options for running NodeJS on Android

J2V8 issue #14783

create Node.js project

I have to prepare the Node.js project and install NPM modules.

$ mkdir nodejsProject
$ cd nodejsProject/
$ npm init
$ npm install lodash webpack --save
$ mkdir dist
$ mkdir src

I got package.json which is looks like below:

{
  "name": "nodejsproject",
  "scripts": {
    "build": "./node_modules/.bin/webpack --config webpack.config.js",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  ......
  "dependencies": {
    "lodash": "^4.17.4",
    "webpack": "^3.6.0"
  }
}

Write JavaScript program with lodash library.

/src/simple.js

import _ from 'lodash';

export function getString() {
    return _.join(['Hello, ', 'World'], ' ');
};

package the program and module

Write config of webpack module in /webpack.config.js.

const path = require('path');
const webpack = require('webpack');

module.exports = {
  entry: './src/simple.js',
  output: {
    library: 'libraryName',
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist')
  }
};

Use webpack to package JavaScript program and Node.js module.

$ npm run build

> [email protected] build /Users/yen/Desktop/nodejsProject

> webpack --config webpack.config.js

Hash: 5f0bb84951ae289f2552
Version: webpack 3.6.0
Time: 358ms
    Asset    Size  Chunks                    Chunk Names
bundle.js  544 kB       0  [emitted]  [big]  main
   [0] ./src/simple.js 100 bytes {0} [built]
   [2] (webpack)/buildin/global.js 509 bytes {0} [built]
   [3] (webpack)/buildin/module.js 517 bytes {0} [built]
    + 1 hidden module

webpack configuration document

The file structure of Node.js project should look like below:

$ tree
.
├── dist
│   └── bundle.js
├── package.json
├── src
│   └── simple.js
└── webpack.config.js

2 directories, 4 files

first try

Put /dist/bundle.js to Android Studio project in/src/main/assets/simple.js

var libraryName =
/******/ (function(modules) { // webpackBootstrap
/******/     // The module cache
/******/     var installedModules = {};

......

"use strict";
Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
/* harmony export (immutable) */ __webpack_exports__["getString"] = getString;
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_lodash__ = __webpack_require__(1);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_lodash___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0_lodash__);


function getString() {
    return __WEBPACK_IMPORTED_MODULE_0_lodash___default.a.join(['Hello, ', 'World'], ' ');
};

......

/***/ })
/******/ ]);

Call the JavaScript function in Java. com.eclipsesource.v8.V8Object;

public class JavaClass {

    ......

    public void exeJavaScript() {
        String jsContent = readFromAsset("simple.js");
        V8 runtime = V8.createV8Runtime();
        runtime.executeVoidScript(jsContent);

        V8Object library = runtime.getObject("libraryName");
        Log.d("Test", library.executeStringFunction("getString", null));

        myClass.release();
        runtime.release();
    }
}

It is not work. I got SyntaxError.

D/AndroidRuntime: Shutting down VM
E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.hsy.wrapper, PID: 20119
    java.lang.RuntimeException: Unable to start activity ComponentInfo{com.hsy.wrapper/com.hsy.wrapper.MainActivity}: undefined:1: SyntaxError: Unexpected end of input    
    var libraryName =/******/ (function(modules) ......
D/Error: ERR: exClass=com.eclipsesource.v8.V8ScriptCompilationException
D/Error: ERR: exMsg=undefined:1: SyntaxError: Unexpected end of input
D/Error: ERR: file=V8.java
D/Error: ERR: class=com.eclipsesource.v8.V8
D/Error: ERR: method=_executeVoidScript line=-2
D/Error: ERR: TOTAL BYTES WRITTEN: 2111296

results

I think the newline code is suspect. I found the online minify tool to deal with this problem.

Minify - css and JavaScript minifier

The minified JavaScript is looks like below:

var libraryName=(function(modules){var installedModules={};function __webpack_require__(moduleId){if(installedModules[moduleId]){return installedModules[moduleId].exports;/******/}
var module=installedModules[moduleId]={i:moduleId,l:!1,exports:{}/******/};modules[moduleId].call(module.exports,module,module.exports,__webpack_require__);module.l=!0;return module.exports;/******/}
__webpack_require__.m=modules;__webpack_require__.c=installedModules;__webpack_require__.d=function(exports,name,getter){if(!__webpack_require__.o(exports,name)){Object.defineProperty(exports,name,{configurable:!1,enumerable:!0,get:getter});/******/}/******/};__webpack_require__.n=function(module){var getter=module&&module.__esModule?function getDefault(){return module['default']}:function getModuleExports(){return module};__webpack_require__.d(getter,'a',getter);return getter;/******/};__webpack_require__.o=function(object,property){return Object.prototype.hasOwnProperty.call(object,property)};__webpack_require__.p="";return __webpack_require__(__webpack_require__.s=0);/******/})([(function(module,__webpack_exports__,__webpack_require__){"use strict";Object.defineProperty(__webpack_exports__,"__esModule",{value:!0});__webpack_exports__.getString=getString;var __WEBPACK_IMPORTED_MODULE_0_lodash__=__webpack_require__(1);var __WEBPACK_IMPORTED_MODULE_0_lodash___default=__webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0_lodash__);function getString(){return __WEBPACK_IMPORTED_MODULE_0_lodash___default.a.join(['Hello, ','World'],' ')};

......

module.exports=g;/***/}),(function(module,exports){module.exports=function(module){if(!module.webpackPolyfill){module.deprecate=function(){};module.paths=[];if(!module.children)module.children=[];Object.defineProperty(module,"loaded",{enumerable:!0,get:function(){return module.l}});Object.defineProperty(module,"id",{enumerable:!0,get:function(){return module.i}});module.webpackPolyfill=1}
return module};/***/})])

Result in LogCat is:

09-22 02:45:19.096 20310-20310/? D/Test: Hello,  World

results matching ""

    No results matching ""