LoginSignup
4
2

More than 5 years have passed since last update.

Java でも ES2015 を使いたい! → (´・ω・`)

Last updated at Posted at 2018-03-06

MOTIVATION

Babel は最新の ECMAScript 構文 (クラスやラムダや const, let など) を古い仕様の JS 環境でも実行できるように変換するトランスコンパイラ。例えば React は ECMA2015 + JSX のソースを Babel で一般的なブラウザでも解釈できる JS に変換しています。

React を使う用事があって、使い慣れた Finagle か Akka で API サーバを用意しようと思い、Node/React と JavaVM はうまく共存させたかったのがそもそもの始まり。Babel でブラウザ実行可能な JS が生成できるなら Java Scripting API の Nashorn で ES2015 が使えたり Node.js のライブラリが利用できるのでは? と。

CONCLUSION

結論から先に。

ES2015 で書かれた JS を Babel でトランスコンパイルして Nashorn で実行することは可能。ただし:

厳しい。理由は以下の通り。

  • babel-standalone の準備 eval()30 秒かかる。うなる CPU ファン。まぁサーバサイドなどは準備の済んだ ScriptEngine をキャッシュしておけば良いかもしれない。
  • 準備時間を妥協できるならトランスコンパイルは可能。ただ Nashorn には require() がなく、従って外部ライブラリが使えない。webpack も併せて使う必要がありそう。あるいは Java で同等機能を実装して bindings で渡せば回避可能かもしれない。
  • そもそも環境設定に npm を前提とするので Nashorn を使わずとも babel を直接起動する手段がある (そしてそっちの方が速い)。
  • babel-standalone は presets に env が使えないなど制約が厳しい。

従って Nashorn を使うより実行環境の node なり npm なりを Runtime.exec() で使用するほうが良い。

PLAN

こんなプランで実行しましたという手順。

npm で es2015 の presets をインストールしています (が、本当に必要だったかは不明)。

$ npm init
$ npm install babel-cli babel-preset-es2015 --save

1. babel.js の準備

babel-standaloneInstallation にあるようにリリースページや npm で babel.js を入手します。

final ScriptEngineManager manager = new ScriptEngineManager();

final ScriptEngine babel = manager.getEngineByName("JavaScript");
final String babelJS = "babel.js";
babel.put(ScriptEngine.FILENAME, babelJS);
try(Reader in = new FileReader(babelJS)){
  babel.eval(in);
}

この babel.eval(in) で 30 秒程度かかります。また babel.min.js を使用すると例外が発生しますので babel.js を使用します。

javax.script.ScriptException: SyntaxError: empty range in char class in babel.min.js at line number 4
        at jdk.scripting.nashorn/jdk.nashorn.api.scripting.NashornScriptEngine.throwAsScriptException(Unknown Source)
...
Caused by: babel.min.js:4 SyntaxError: empty range in char class
        at jdk.scripting.nashorn/jdk.nashorn.internal.runtime.ECMAErrors.error(Unknown Source)
...
Caused by: jdk.nashorn.internal.runtime.ParserException: empty range in char class
        at jdk.scripting.nashorn/jdk.nashorn.internal.runtime.regexp.RegExp.throwParserException(Unknown Source)
...

2. トランスコンパイルの実行

final String es2015JS = "es2015.js";
final String es2015 = new String(Files.readAllBytes(Paths.get(es2015JS)), StandardCharsets.UTF_8);
babel.put(ScriptEngine.FILENAME, "<transcompile>");
babel.put("src", es2015);
babel.put("a", new Object[3]);
final Object[] result = (Object[])babel.eval(
  "var r = Babel.transform(src, {presets:['es2015']});\n" +
  "a[0] = r.code;\n" +
  "a[1] = r.map;\n" +
  "a[2] = r.ast;\n" +
  "a"
);
System.out.println(result[0]);

Babel.transformFileSync は何故か undefined でした。関数内で arguments とか使っているから?

とりあえず一度 ES2015 のソースを読み込んで transform し結果をリターンバッファに格納します。es2015.js の中は以下の通り:

// run `npm install kuromoji` before
import kuromoji from "kuromoji"

kuromoji.builder({ dicPath: "./node_modules/kuromoji/dict" }).build((err, tokenizer) => {
  var path = tokenizer.tokenize("すもももももももものうち")
  console.log(path)
})

Nashorn 上でのトランスコンパイルにより以下のようなソースが生成されました。

"use strict";

var _kuromoji = require("kuromoji");

var _kuromoji2 = _interopRequireDefault(_kuromoji);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

_kuromoji2.default.builder({ dicPath: "./node_modules/kuromoji/dict" }).build(function (err, tokenizer) {
  var path = tokenizer.tokenize("すもももももももものうち");
  console.log(path);
}); // run `npm install kuromoji` before

Babel でトランスコンパイルした結果も全く同じ。

$ node_modules/.bin/babel --presets=es2015 es2015.js

3. トランスコンパイルされたコードの実行

スクリプトエンジンを Babel とは別にしてトランスコンパイルされたコードを実行します。

final ScriptEngine engine = manager.getEngineByName("JavaScript");
engine.put(ScriptEngine.FILENAME, es2015JS);
engine.eval(result[0].toString());

しかし require() を使っているので実行できませんね :frowning2:

javax.script.ScriptException: ReferenceError: "require" is not defined in es2015.js at line number 3
        at jdk.scripting.nashorn/jdk.nashorn.api.scripting.NashornScriptEngine.throwAsScriptException(Unknown Source)
...

おしまし

4
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
2