KotlinJS x ReactNativeにトライしてみた

前回でKotlinJSの使い方がチョットだけわかったので、ReactNativeで動くようにしてみたいと思います。

プロジェクト作成。

Intellij ideaで進めていきます。

KotlinJSプロジェクトを作ります。

次のそのプロジェクトのrootでReactNativeのプロジェクトを作ります。

react-native init rn

とりあえず図のような構成にします。

f:id:anect:20171207110148p:plain

コンパイル結果のディレクトリを調整する

Project Structure -> Project Settings -> Projectを開きます。

f:id:anect:20171206182309p:plain

Project compiler outputをReactNativeプロジェクトのnode_modulesにします

変更後

f:id:anect:20171207124119p:plain

次にProject Structure -> Project Settings -> Modulesを開きます。

f:id:anect:20171206180923p:plain

図のCompiler outputを作成したReactNativeプロジェクトのnode_modulesに変更します。

変更後

f:id:anect:20171207124203p:plain

次に、Preferences -> Build, Execution ... -> Compiler -> Kotlin Compilerを開きます。

f:id:anect:20171206181338p:plain

前回もありましたが、Destination directoryModule kindを以下のように変更します。

f:id:anect:20171207124244p:plain

これでプロジェクトの設定は完了です。

ReactNativeでHelloWorld

では、ハロワしましょう。

index.jsはReact Nativeが直接参照しているので変更してはいけません。

とりあえずはrnディレクトリにあるApp.jsを変更して今回目標とするハロワのコードを書いてみましょう。

App.js

import React from 'react';
import {
  Text,
  View,
} from 'react-native';

module.exports = () =>
    <View style={{flex: 1, alignItems: 'center', justifyContent: 'center'}}>
        <Text>Hello World</Text>
    </View>;

これをiPhoneSimulatorで確認します。

react-native run-ios

f:id:anect:20171207104708p:plain

目標はこんな感じです。

App.ktを作成

src以下にApp.ktファイルを作成します

コンパイルが通る程度に適当にApp.jsっぽい感じに書いてみましょう

import kotlin.js.json

@JsModule("react")
external object React {
    class Component
    fun createElement(element:Component, props: dynamic, children: dynamic)
}

@JsModule("react-native")
external object ReactNative {
    val View: React.Component
    val Text: React.Component
}

fun App():dynamic = React.createElement(
        ReactNative.View,
        json(Pair("style", json(
                Pair("flex", 1),
                Pair("alignItems", "center"),
                Pair("justifyContent", "center")
        ))),
        React.createElement(ReactNative.Text, null, "Hello World"));

JSXでは書けないのでReact.createElementで書きます。

json

これはKotlinJSの機能でJavaScriptJSONに変換できる機能です。

json - Kotlin Programming Language

以下がコンパイル結果です

(function (_, Kotlin, $module$react, $module$react_native) {
  'use strict';
  var Pair = Kotlin.kotlin.Pair;
  var json = Kotlin.kotlin.js.json_pyyo18$;
  function App() {
    return $module$react.createElement($module$react_native.View, json([new Pair('style', json([new Pair('flex', 1), new Pair('alignItems', 'center'), new Pair('justifyContent', 'center')]))]), $module$react.createElement($module$react_native.Text, null, 'Hello World'));
  }
  _.App = App;
  Kotlin.defineModule('ReactNativeKotlin', _);
  return _;
}(module.exports, require('kotlin'), require('react'), require('react-native')));

コンパイル結果みるとイケそうな感じ

index.jsを少し変更します。

import { AppRegistry } from 'react-native';
import { App } from 'ReactNativeKotlin';

AppRegistry.registerComponent('rn', () => App);

実行

f:id:anect:20171207113751p:plain

...

...

...

(あれ、読み込みが遅い…)

...

...

f:id:anect:20171207113847p:plain

駄目だったか…

原因を探す

とりあえずbundleだけしてみましょう。

% react-native bundle --entry-file index.js --bundle-output ./ --dev false --platform ios
Scanning folders for symlinks in /Users/Ryohlan/dev/kotlin/ReactNativeKotlin/rn/node_modules (15ms)
Scanning folders for symlinks in /Users/Ryohlan/dev/kotlin/ReactNativeKotlin/rn/node_modules (14ms)
Loading dependency graph, done.

...

...

bundleも終わらない。

kotlin.jsがbundle出来ない

コンパイル結果を色々いじって見た結果、kotlin.jsをrequireするとbundleが上手く行きません。

ログにタイムアウトが出てたのでmetro-bundlerのタイムアウト時間を伸ばしてみたけど駄目でした。

とりあえず、kotlin.jsからコンパイル結果で使っているものを抜粋して動かしてみます。

function Pair(first, second) {
  this.first = first;
  this.second = second;
}
Pair.prototype.toString = function () {
  return '(' + this.first + ', ' + this.second + ')';
};
Pair.prototype.component1 = function () {
  return this.first;
};
Pair.prototype.component2 = function () {
  return this.second;
};

function json(pairs) {
  var tmp$;
  var res = {};
  for (tmp$ = 0; tmp$ !== pairs.length; ++tmp$) {
    var tmp$_0 = pairs[tmp$];
    var name = tmp$_0.component1(), value = tmp$_0.component2();
    res[name] = value;
  }
  return res;
}

var Kotlin = {
  defineModule: function(a, b) {},
  kotlin: {
    Pair: Pair,
    js: {
      json_pyyo18$: json,
    }
  }
};

(function (_, Kotlin, $module$react, $module$react_native) {
  'use strict';
  var Pair = Kotlin.kotlin.Pair;
  var json = Kotlin.kotlin.js.json_pyyo18$;
  function App() {
    return $module$react.createElement($module$react_native.View, json([new Pair('style', json([new Pair('flex', 1), new Pair('alignItems', 'center'), new Pair('justifyContent', 'center')]))]), $module$react.createElement($module$react_native.Text, null, 'Hello World'));
  }
  _.App = App;
  Kotlin.defineModule('ReactNativeKotlin', _);
  return _;
}(module.exports, Kotlin, require('react'), require('react-native')));

f:id:anect:20171207170945p:plain

ちゃんと表示されました。

やはりkotlin.jsの何かが問題なようです。(37000行あるので全部見れてません…)

うーむ、いいところまで行った気がしたんですがね。

まとめ

現状だと普通に動かすのは無理のようです。

kotlin.jsはKotlinの機能をjsに置き換える処理が書かれているので無くすことは出来ないので、修正されるかReactNative用の何かが出るまで待つしかないですかね。

まぁReactNativeでKotlinJS使いたいって要望無さそうですが…

と思ったらいるっぽい。

Use Kotlin with npm, webpack and react | Kotlin Blog

f:id:anect:20171207172121p:plain