fin*_*usl 6 javascript kotlin kotlin-js-interop kotlin-js
我是 JS 和 Kotlin/JS 的新手。我有以下来自示例的黑曜石插件的最小工作 Javascript 代码。它按预期工作:
var obsidian = require('obsidian');
class SomePlugin extends obsidian.Plugin {
onload() {
new obsidian.Notice('This is a notice!');
}
}
module.exports = Plugin;
Run Code Online (Sandbox Code Playgroud)
我希望使用 Kotlin 来扩展这个插件,因为我了解这种语言,但是将其转换为 Kotlin/JS 时遇到一些问题。到目前为止我的方法:
可运行的项目可以在 Github 上找到。运行gradle build生成构建文件夹。它会在浏览器步骤中失败,但该步骤不是必需的。构建完成后,可以在 .js 文件中找到生成的 js 文件build\js\packages\main\kotlin\main.js。
主程序.kt
@JsExport
class SomePlugin: Plugin() {
override fun onload() {
Notice("This is a notice!")
}
}
@JsModule("obsidian")
@JsNonModule // required by the umd moduletype
external open class Component {
open fun onload()
}
@JsModule("obsidian")
@JsNonModule
external open class Plugin : Component {
}
@JsModule("obsidian")
@JsNonModule
external open class Notice(message: String, timeout: Number = definedExternally) {
open fun hide()
}
Run Code Online (Sandbox Code Playgroud)
编辑:感谢@S.Janssen 的评论,我将模块类型切换为 umd
构建.gradle.kts
plugins {
kotlin("js") version "1.5.20"
}
group = "de.example"
version = "1.0-SNAPSHOT"
repositories {
mavenCentral()
}
dependencies {
implementation(npm("obsidian", "0.12.5", false))
}
kotlin {
js(IR) {
binaries.executable()
browser {
webpackTask {
output.libraryTarget = "umd"
}
}
}
}
tasks.withType<KotlinJsCompile>().configureEach {
kotlinOptions.moduleKind = "umd"
}
Run Code Online (Sandbox Code Playgroud)
我实际上不需要一个可以在 中运行的结果browser,但是如果没有browser定义,它甚至不会生成 js 文件。对于该browser部分,会抛出异常,并显示Can't resolve 'obsidian' in 'path\kotlin'。但至少在 .js 下创建了一个 .js 文件build/js/packages/test/kotlin/test.js。然而,该代码与我预期的代码完全不同,并且黑曜石也不接受它作为有效的插件代码。我还尝试了其他一些 gradle 选项。像“umd”,“amd”,“plain”,遗留编译器而不是IR,nodejs而不是浏览器。但没有任何东西创建可运行的 js 文件。错误消息有所不同。对于旧版编译器,它需要 kotlin.js 文件,即使我将其放在文件夹中的旁边或将内容复制到脚本中,它也找不到该文件。
如何获得功能与上面发布的 Javascript 代码类似的代码?我知道它会产生开销,但根据我的理解,当前生成的代码甚至没有定义或导出我的类。
我从 obisidan 调试器收到的错误消息:
Plugin failure: obsidian-sample-plugin TypeError: Object prototype may only be an Object or null: undefined
Run Code Online (Sandbox Code Playgroud)
生成的代码:
(function (root, factory) {
if (typeof define === 'function' && define.amd)
define(['exports', 'obsidian', 'obsidian', 'obsidian'], factory);
else if (typeof exports === 'object')
factory(module.exports, require('obsidian'), require('obsidian'), require('obsidian'));
else {
if (typeof Component === 'undefined') {
throw new Error("Error loading module 'main'. Its dependency 'obsidian' was not found. Please, check whether 'obsidian' is loaded prior to 'main'.");
}if (typeof Plugin === 'undefined') {
throw new Error("Error loading module 'main'. Its dependency 'obsidian' was not found. Please, check whether 'obsidian' is loaded prior to 'main'.");
}if (typeof Notice === 'undefined') {
throw new Error("Error loading module 'main'. Its dependency 'obsidian' was not found. Please, check whether 'obsidian' is loaded prior to 'main'.");
}root.main = factory(typeof main === 'undefined' ? {} : main, Component, Plugin, Notice);
}
}(this, function (_, Component, Plugin, Notice) {
'use strict';
SomePlugin.prototype = Object.create(Plugin.prototype);
SomePlugin.prototype.constructor = SomePlugin;
function Unit() {
Unit_instance = this;
}
Unit.$metadata$ = {
simpleName: 'Unit',
kind: 'object',
interfaces: []
};
var Unit_instance;
function Unit_getInstance() {
if (Unit_instance == null)
new Unit();
return Unit_instance;
}
function SomePlugin() {
Plugin.call(this);
}
SomePlugin.prototype.onload_sv8swh_k$ = function () {
new Notice('This is a notice!');
Unit_getInstance();
};
SomePlugin.prototype.onload = function () {
return this.onload_sv8swh_k$();
};
SomePlugin.$metadata$ = {
simpleName: 'SomePlugin',
kind: 'class',
interfaces: []
};
_.SomePlugin = SomePlugin;
return _;
}));
Run Code Online (Sandbox Code Playgroud)
小智 4
You can find a working example of what you\'re going for here. I\'ll go through some of the changes that needed to be made to your code one-by-one in this reply.
\nobsidianCan\'t resolve \'obsidian\' in \'path\\kotlin\' occurs because the obsidian-api package is not a standalone library. Instead, it only consist of a obsidian.d.ts file, which is a TypeScript declaration file. Similar to a header file in other languages, this header file does not provide any implementations, but only the signatures and types for the library \xe2\x80\x93 meaning Kotlin/JS\' webpack (or any JavaScript tooling, for that matter) won\'t be able to resolve the actual implementations. This is expected, and can be addressed by declaring the module as external. To do so in Kotlin/JS, create a directory called webpack.config.d, and add a file 01.externals.js with the following content:
config.externals = {\n obsidian: \'obsidian\',\n};\nRun Code Online (Sandbox Code Playgroud)\n(You can actually find an equivalent snippet in the offical sample-plugin configuration, as well, since this isn\'t a Kotlin/JS specific problem)
\n@JsModule declarationsBecause you\'re importing multiple declarations from the same package, instead of annotating multiple signatures with @JsModule / @JsNonModule, you\'ll have to create a separate file, and annotate it with @file:@JsModule("...") / @file:JsNonModule:
@file:JsModule("obsidian")\n@file:JsNonModule\n\nopen external class Component {\n open fun onload()\n open fun onunload()\n}\n\nopen external class Plugin(\n app: Any,\n manifest: Any\n) : Component\n\nopen external class Notice(message: String, timeout: Number = definedExternally) {\n open fun hide()\n}\nRun Code Online (Sandbox Code Playgroud)\nAdditionally, some of your problems stem from the fact that Obsidian\'s examples implicitly make the assumption that you are targeting ES6 (while Kotlin\'s current target is ES5). Specifically, this makes a difference in regards to how your plugin exports its members, as well as how classes are instantiated.
\n关于继承(因为YourPlugin继承自Plugin),ES6 类自动使用所有参数初始化父类。ES5 的原型继承不支持这一点。这就是为什么在上面的代码片段中,我们需要显式传递Plugin类构造函数app和manifest参数,并在特定插件的实现中传递它们:
class SomePlugin(\n app: Any,\n manifest: Any\n) : Plugin(\n app,\n manifest\n)\nRun Code Online (Sandbox Code Playgroud)\n关于导出您的插件,Obsidian 希望module.exports或直接exports.default成为您的Plugin类。为了实现这种精确的导出行为,需要满足一些条件,不幸的是,这有点麻烦:\n- 库目标需要是 CommonJS:(output.libraryTarget = "commonjs"而不是CommonJS2)\n- 为了防止创建间接级别,如下所示通常情况下,导出的库需要设置为null:output.library = null\n- 要将插件导出为 as default,其类声明需要标记为@JsName("default")。