web-dev-qa-db-ja.com

Angular 6 KotlinJsとのTypeScript統合

Kotlinでコンパイルされたjavascriptモジュールをangular 6 TypeScriptファイルにインポートできました。簡単ではなく、結果が混乱しました。もっとエレガントな方法が存在するかどうか知りたいのですが。

もともと私はKotlinファイルを取ります:

_package com.example.test

data class SomeInterface(
    var id: String? = null,
    var value: String? = null
) {
}
_

次のJavaScriptにうまくコンパイルされます

_(function (root, factory) {
  if (typeof define === 'function' && define.AMD)
    define(['exports', 'kotlin'], factory);
  else if (typeof exports === 'object')
    factory(module.exports, require('kotlin'));
  else {
    if (typeof kotlin === 'undefined') {
      throw new Error("Error loading module 'TestKotlinCompiled'. Its dependency 'kotlin' was not found. Please, check whether 'kotlin' is loaded prior to 'TestKotlinCompiled'.");
    }
    root.TestKotlinCompiled = factory(typeof TestKotlinCompiled === 'undefined' ? {} : TestKotlinCompiled, kotlin);
  }
}(this, function (_, Kotlin) {
  'use strict';
  var Kind_CLASS = Kotlin.Kind.CLASS;
  function SomeInterface(id, value) {
    if (id === void 0)
      id = null;
    if (value === void 0)
      value = null;
    this.id = id;
    this.value = value;
  }
  SomeInterface.$metadata$ = {
    kind: Kind_CLASS,
    simpleName: 'SomeInterface',
    interfaces: []
  };
  SomeInterface.prototype.component1 = function () {
    return this.id;
  };
  SomeInterface.prototype.component2 = function () {
    return this.value;
  };
  SomeInterface.prototype.copy_rkkr90$ = function (id, value) {
    return new SomeInterface(id === void 0 ? this.id : id, value === void 0 ? this.value : value);
  };
  SomeInterface.prototype.toString = function () {
    return 'SomeInterface(id=' + Kotlin.toString(this.id) + (', value=' + Kotlin.toString(this.value)) + ')';
  };
  SomeInterface.prototype.hashCode = function () {
    var result = 0;
    result = result * 31 + Kotlin.hashCode(this.id) | 0;
    result = result * 31 + Kotlin.hashCode(this.value) | 0;
    return result;
  };
  SomeInterface.prototype.equals = function (other) {
    return this === other || (other !== null && (typeof other === 'object' && (Object.getPrototypeOf(this) === Object.getPrototypeOf(other) && (Kotlin.equals(this.id, other.id) && Kotlin.equals(this.value, other.value)))));
  };
  var package$com = _.com || (_.com = {});
  var package$example = package$com.example || (package$com.example =     {});
  var package$test = package$example.test || (package$example.test = {});
  package$test.SomeInterface = SomeInterface;
  Kotlin.defineModule('TestKotlinCompiled', _);
  return _;
}));
_

Package.jsonで、依存関係セクションに_"kotlin": "^1.2.70",_を追加します。 angular componentでは、インポートにこのようなコードを使用する必要があります。

_import * as TestKotlinCompiled from "../../generated/TestKotlinCompiled";

// @ts-ignore
const SomeInterface = TestKotlinCompiled.com.example.test.SomeInterface;
// @ts-ignore
type SomeInterface = TestKotlinCompiled.com.example.test.SomeInterface;
_

これは、モジュールSomeInterfacに対して生成されたパッケージ_com.example.test_でクラスTestKotlinCompiledを使用するための最小限の必須コードです。

ここでの問題は次のとおりです。

_// @ts-ignore_が必要なのは、コンパイル時にts-comilerがインポートされるモジュールのコンテンツを認識しないためです。

new SomeInterface()にはconstが必要です

_let x: SomeInterface;_にはtypeが必要です

これらすべてはひどくハックに見えます。 consttypeを使わずに、_import {SomeInterface} from '../../generated/TestKotlinCompiled' using namespace com.example.test_のようなもっと簡単なものを選びました。では、上記のコードを簡略化する方法はありますか?

10
Sergey Okatov

私もこの統合を試して解決しました。克服すべき多くの問題があります。

  • typeScript宣言ファイルの生成
  • kotlin JSモジュールをnode_modulesに解凍する
  • サードパーティライブラリ

問題を説明するブログ投稿はこちら https://medium.com/@dr.david.h.akehurst/building-applications-with-kotlin-and-TypeScript-8a165e76252c

また、すべてをより簡単にするGradleプラグインも作成しました https://github.com/dhakehurst/net.akehurst.kotlin.kt2ts

1
user2228895

AngularでのKotlinJsの使いやすさを少し改善することに成功しました。私は https://github.com/svok/kotlin-multiplatform-sample に実験を破棄します

まず、Gradleでマルチプラットフォームサブモジュールを作成する必要があります。その中で、(他の可能なプラットフォームの中で)jsファイルを生成します。

次に、package.jsonに追加します...

{
  "dependencies": {
    "kotlin": "^1.3.21",
    "proj-common": "file:../build/javascript-compiled"
  }
}

proj-commonは、コンパイルされたKotlinモジュールです。 kotlin-jsファイルのビルド先のパス。

したがって、TypeScriptでは、もう1つのnpmモジュールを使用するだけです。

import {sample} from 'proj-common/proj-common';

// For class Sample
sample = new sample.Sample();

// For object Platform
platform = sample.Platform;

コンパイルは// @ts-ignoreを使用する必要なしにうまくいきます

更新

上記の説明では、従属関係に問題がありました。それらはエクスポートされませんでしたが、すべてのサブ依存関係がnpmリポジトリに同等のものを持っているわけではありません。以下のコードはこの問題を解決します。

tasks {
    task<Sync>("assembleWeb") {
        val dependencies = configurations.get("jsMainImplementation").map {
            val file = it
            val (tDir, tVer) = "^(.*)-([\\d.]+-\\w+|[\\d.]+)\\.jar$"
                .toRegex()
                .find(file.name)
                ?.groupValues
                ?.drop(1)
                ?: listOf("", "")
            var jsFile: File? = null
            copy {
                from(zipTree(file.absolutePath), {
                    includeEmptyDirs = false
                    include { fileTreeElement ->
                        val path = fileTreeElement.path
                        val res = (path.endsWith(".js") || path.endsWith(".map"))
                                && (path.startsWith("META-INF/resources/") || !path.startsWith("META-INF/"))
                        if (res && path.endsWith(".js") && ! path.endsWith(".meta.js")) jsFile = fileTreeElement.file
                        res
                    }
                })
                into("$npmTarget/$tDir")
            }
            jsFile?.also { packageJson(tDir, it, tVer) }
            tDir to jsFile
        }
            .filter { it.second != null }
            .map { it.first to it.second!! }
            .toMap()

        packageJson(npmDir, File(jsOutputFile), project.version.toString(), dependencies)
        dependsOn("jsMainClasses")
    }

    assemble.get().dependsOn("assembleWeb")
}

fun packageJson(dir: String, jsFile: File, version: String, dependencies: Map<String, File> = emptyMap()) {
    val deps = dependencies.map {
        """"${js2Name(it.value)}": "file:../${it.key}""""
    }.joinToString(",\n            ")
    val text = """
        {
          "name": "${js2Name(jsFile)}",
          "version": "${version}",
          "main": "./${jsFile.name}",
          "dependencies": {
            ${deps}
          }
        }
    """.trimIndent()
    File("$npmTarget/$dir/package.json").apply {
        if (parentFile.exists()) {
            parentFile.delete()
        }
        parentFile.mkdirs()
        writeText(text)
    }
}

fun js2Name(jsFile: File) = jsFile.name.replace("""\.js$""".toRegex(), "")

次に、フロントサブモジュールからインポートします。

{
  "dependencies": {
    "proj-common": "file:../build/npm"
  }
}

そしてTypeScriptファイルで:

import {sample} from 'proj-common';

// For class Sample
sample = new sample.Sample();

// For object Platform
platform = sample.Platform;

サンプルプロジェクトは https://github.com/svok/kotlin-multiplatform-sample を参照してください

アップデート2

これで、gradleにプラグインを接続するのと同じくらい簡単に、kotlin共通サブプロジェクトでフルスタックプロジェクトを作成できます

plugins {
    id("com.crowdproj.plugins.jar2npm")
}

このプラグインは、コンパイル時にすべてのkotlin-js jarパッケージをnode_modulesに自動的に挿入します。

https://github.com/svok/kotlin-multiplatform-sample プロジェクトがこのプラグインで書き直されました。 proj-angularfront サブモジュールを参照してください。

1
Sergey Okatov