如何将 Javascript 导出的 class 转换为 Kotlin/JS?
How to convert Javascript exported class to Kotlin/JS?
我是 JS 和 Kotlin/JS 的新手。我从一个示例中为 Obsidian 的插件提供了以下最小工作 Javascript 代码。它按预期工作:
var obsidian = require('obsidian');
class SomePlugin extends obsidian.Plugin {
onload() {
new obsidian.Notice('This is a notice!');
}
}
module.exports = Plugin;
我希望使用 Kotlin 来扩展这个插件,因为我知道这种语言,但是我在将它转换为 Kotlin/JS 时遇到了一些问题。到目前为止我的方法:
可以找到运行可用的项目here on Github。 运行 gradle build
生成构建文件夹。它会在浏览器步骤中失败,但该步骤不是必需的。构建后生成的 js 文件可以在 build\js\packages\main\kotlin\main.js
.
中找到
main.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()
}
编辑:感谢@S.Janssen的评论,我将模块类型切换为umd
build.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"
}
我其实不需要browser
中可以运行的结果,但是如果没有browser
的定义,它甚至不会生成js文件。对于 browser
部分,抛出一个异常 Can't resolve 'obsidian' in 'path\kotlin'
。但至少在 build/js/packages/test/kotlin/test.js
下创建了一个 .js 文件。然而,代码与我预期的代码完全不同,黑曜石也不接受它作为有效的插件代码。我还尝试了其他一些 gradle 选项。像“umd”,“amd”,“plain”,旧版编译器而不是 IR,nodejs 而不是浏览器。但没有创建 运行nable js 文件。错误消息不同。对于旧版编译器,它需要 kotlin.js 文件,即使我将它放在文件夹中的旁边或将内容复制到脚本中,它也找不到。
如何获得与上面发布的 Javascript 代码功能相似的代码?我知道它会有开销,但根据我的理解,当前生成的代码甚至没有定义或导出我的 class。
我从 obisidan 调试器得到的错误消息:
Plugin failure: obsidian-sample-plugin TypeError: Object prototype may only be an Object or null: undefined
生成的代码:
(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 _;
}));
你可以找到一个你想要做什么的工作示例 here。 我将介绍一些需要对你的在此回复中一一编码。
无法解析obsidian
Can't resolve 'obsidian' in 'path\kotlin'
出现是因为 obsidian-api
包不是独立库。相反,它只包含一个 obsidian.d.ts
文件,这是一个 TypeScript 声明文件。类似于其他语言的头文件,这个头文件不提供任何实现,而只提供库的签名和类型——意思是 Kotlin/JS' webpack(或任何 JavaScript 工具,就此而言)将无法解决实际的实施。这是预期的,可以通过将模块声明为 external
来解决。为此,请在 Kotlin/JS 中创建一个名为 webpack.config.d
的目录,然后添加一个包含以下内容的文件 01.externals.js
:
config.externals = {
obsidian: 'obsidian',
};
(您实际上也可以在官方 sample-plugin configuration 中找到等效的片段,因为这不是 Kotlin/JS 特定的问题)
分组多个 @JsModule
声明
因为您要从同一个包中导入多个声明,而不是使用 @JsModule
/ @JsNonModule
注释多个签名,您必须创建一个单独的文件,并使用 @file:@JsModule("...")
/ @file:JsNonModule
:
@file:JsModule("obsidian")
@file:JsNonModule
open external class Component {
open fun onload()
open fun onunload()
}
open external class Plugin(
app: Any,
manifest: Any
) : Component
open external class Notice(message: String, timeout: Number = definedExternally) {
open fun hide()
}
Kotlin 的 ES5 与 Obsidian 的 ES6
此外,您的一些问题源于以下事实:Obsidian 的示例隐含地假设您的目标是 ES6(而 Kotlin 当前的目标是 ES5)。具体来说,这会影响您的插件如何导出其成员,以及 classes 的实例化方式。
继承
关于继承(因为 YourPlugin
继承自 Plugin
),ES6 class 会自动使用所有参数初始化父 class。这是 ES5 的原型继承中不支持的东西。这就是为什么在上面的代码片段中,我们需要明确地向 Plugin
class 构造函数传递 app
和 manifest
参数,并在您的特定插件的实现中传递它们:
class SomePlugin(
app: Any,
manifest: Any
) : Plugin(
app,
manifest
)
导出/模块系统
关于导出您的插件,Obsidian 希望 module.exports
或 exports.default
直接成为您的 Plugin
class。要实现这种精确的导出行为,需要满足一些条件,不幸的是这有点麻烦:
- 库目标需要是 CommonJS:output.libraryTarget = "commonjs"
(不是 CommonJS2)
- 为了防止创建间接级别,通常情况下,导出的库需要设置为 null
:output.library = null
- 要将插件导出为 default
,其 class 声明需要标记为 @JsName("default")
.
我是 JS 和 Kotlin/JS 的新手。我从一个示例中为 Obsidian 的插件提供了以下最小工作 Javascript 代码。它按预期工作:
var obsidian = require('obsidian');
class SomePlugin extends obsidian.Plugin {
onload() {
new obsidian.Notice('This is a notice!');
}
}
module.exports = Plugin;
我希望使用 Kotlin 来扩展这个插件,因为我知道这种语言,但是我在将它转换为 Kotlin/JS 时遇到了一些问题。到目前为止我的方法:
可以找到运行可用的项目here on Github。 运行 gradle build
生成构建文件夹。它会在浏览器步骤中失败,但该步骤不是必需的。构建后生成的 js 文件可以在 build\js\packages\main\kotlin\main.js
.
main.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()
}
编辑:感谢@S.Janssen的评论,我将模块类型切换为umd
build.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"
}
我其实不需要browser
中可以运行的结果,但是如果没有browser
的定义,它甚至不会生成js文件。对于 browser
部分,抛出一个异常 Can't resolve 'obsidian' in 'path\kotlin'
。但至少在 build/js/packages/test/kotlin/test.js
下创建了一个 .js 文件。然而,代码与我预期的代码完全不同,黑曜石也不接受它作为有效的插件代码。我还尝试了其他一些 gradle 选项。像“umd”,“amd”,“plain”,旧版编译器而不是 IR,nodejs 而不是浏览器。但没有创建 运行nable js 文件。错误消息不同。对于旧版编译器,它需要 kotlin.js 文件,即使我将它放在文件夹中的旁边或将内容复制到脚本中,它也找不到。
如何获得与上面发布的 Javascript 代码功能相似的代码?我知道它会有开销,但根据我的理解,当前生成的代码甚至没有定义或导出我的 class。
我从 obisidan 调试器得到的错误消息:
Plugin failure: obsidian-sample-plugin TypeError: Object prototype may only be an Object or null: undefined
生成的代码:
(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 _;
}));
你可以找到一个你想要做什么的工作示例 here。 我将介绍一些需要对你的在此回复中一一编码。
无法解析obsidian
Can't resolve 'obsidian' in 'path\kotlin'
出现是因为 obsidian-api
包不是独立库。相反,它只包含一个 obsidian.d.ts
文件,这是一个 TypeScript 声明文件。类似于其他语言的头文件,这个头文件不提供任何实现,而只提供库的签名和类型——意思是 Kotlin/JS' webpack(或任何 JavaScript 工具,就此而言)将无法解决实际的实施。这是预期的,可以通过将模块声明为 external
来解决。为此,请在 Kotlin/JS 中创建一个名为 webpack.config.d
的目录,然后添加一个包含以下内容的文件 01.externals.js
:
config.externals = {
obsidian: 'obsidian',
};
(您实际上也可以在官方 sample-plugin configuration 中找到等效的片段,因为这不是 Kotlin/JS 特定的问题)
分组多个 @JsModule
声明
因为您要从同一个包中导入多个声明,而不是使用 @JsModule
/ @JsNonModule
注释多个签名,您必须创建一个单独的文件,并使用 @file:@JsModule("...")
/ @file:JsNonModule
:
@file:JsModule("obsidian")
@file:JsNonModule
open external class Component {
open fun onload()
open fun onunload()
}
open external class Plugin(
app: Any,
manifest: Any
) : Component
open external class Notice(message: String, timeout: Number = definedExternally) {
open fun hide()
}
Kotlin 的 ES5 与 Obsidian 的 ES6
此外,您的一些问题源于以下事实:Obsidian 的示例隐含地假设您的目标是 ES6(而 Kotlin 当前的目标是 ES5)。具体来说,这会影响您的插件如何导出其成员,以及 classes 的实例化方式。
继承
关于继承(因为 YourPlugin
继承自 Plugin
),ES6 class 会自动使用所有参数初始化父 class。这是 ES5 的原型继承中不支持的东西。这就是为什么在上面的代码片段中,我们需要明确地向 Plugin
class 构造函数传递 app
和 manifest
参数,并在您的特定插件的实现中传递它们:
class SomePlugin(
app: Any,
manifest: Any
) : Plugin(
app,
manifest
)
导出/模块系统
关于导出您的插件,Obsidian 希望 module.exports
或 exports.default
直接成为您的 Plugin
class。要实现这种精确的导出行为,需要满足一些条件,不幸的是这有点麻烦:
- 库目标需要是 CommonJS:output.libraryTarget = "commonjs"
(不是 CommonJS2)
- 为了防止创建间接级别,通常情况下,导出的库需要设置为 null
:output.library = null
- 要将插件导出为 default
,其 class 声明需要标记为 @JsName("default")
.