[JS IR] .d.ts generation for module systems
Support .d.ts generation for UMD, AMD, CommonJS module kinds in addition to existing "plain" module kind.
This commit is contained in:
+25
-9
@@ -6,23 +6,39 @@
|
|||||||
package org.jetbrains.kotlin.ir.backend.js.export
|
package org.jetbrains.kotlin.ir.backend.js.export
|
||||||
|
|
||||||
import org.jetbrains.kotlin.ir.backend.js.utils.sanitizeName
|
import org.jetbrains.kotlin.ir.backend.js.utils.sanitizeName
|
||||||
|
import org.jetbrains.kotlin.serialization.js.ModuleKind
|
||||||
|
|
||||||
// TODO: Support module kinds other than plain
|
// TODO: Support module kinds other than plain
|
||||||
|
|
||||||
fun ExportedModule.toTypeScript(): String {
|
fun ExportedModule.toTypeScript(): String {
|
||||||
val prefix = " type Nullable<T> = T | null | undefined\n"
|
val indent = if (moduleKind == ModuleKind.PLAIN) " " else ""
|
||||||
val body = declarations.joinToString("\n") { it.toTypeScript(" ") }
|
val types = "${indent}type Nullable<T> = T | null | undefined\n"
|
||||||
return "declare namespace ${sanitizeName(name)} {\n$prefix$body\n}\n"
|
|
||||||
|
val declarationsDts =
|
||||||
|
types + declarations.joinToString("\n") {
|
||||||
|
it.toTypeScript(
|
||||||
|
indent = indent,
|
||||||
|
prefix = if (moduleKind == ModuleKind.PLAIN) "" else "export "
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val namespaceName = sanitizeName(name)
|
||||||
|
|
||||||
|
return when (moduleKind) {
|
||||||
|
ModuleKind.PLAIN -> "declare namespace $namespaceName {\n$declarationsDts\n}\n"
|
||||||
|
ModuleKind.AMD, ModuleKind.COMMON_JS -> declarationsDts
|
||||||
|
ModuleKind.UMD -> "$declarationsDts\nexport as namespace $namespaceName;"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun List<ExportedDeclaration>.toTypeScript(indent: String): String =
|
fun List<ExportedDeclaration>.toTypeScript(indent: String): String =
|
||||||
joinToString("") { it.toTypeScript(indent) + "\n" }
|
joinToString("") { it.toTypeScript(indent) + "\n" }
|
||||||
|
|
||||||
fun ExportedDeclaration.toTypeScript(indent: String): String = indent + when (this) {
|
fun ExportedDeclaration.toTypeScript(indent: String, prefix: String = ""): String = indent + when (this) {
|
||||||
is ErrorDeclaration -> "/* ErrorDeclaration: $message */"
|
is ErrorDeclaration -> "/* ErrorDeclaration: $message */"
|
||||||
|
|
||||||
is ExportedNamespace ->
|
is ExportedNamespace ->
|
||||||
"namespace $name {\n" + declarations.toTypeScript("$indent ") + "$indent}"
|
"${prefix}namespace $name {\n" + declarations.toTypeScript("$indent ") + "$indent}"
|
||||||
|
|
||||||
is ExportedFunction -> {
|
is ExportedFunction -> {
|
||||||
val keyword: String = when {
|
val keyword: String = when {
|
||||||
@@ -44,7 +60,7 @@ fun ExportedDeclaration.toTypeScript(indent: String): String = indent + when (th
|
|||||||
|
|
||||||
val renderedReturnType = returnType.toTypeScript()
|
val renderedReturnType = returnType.toTypeScript()
|
||||||
|
|
||||||
"$keyword$name$renderedTypeParameters($renderedParameters): $renderedReturnType;"
|
"${prefix}$keyword$name$renderedTypeParameters($renderedParameters): $renderedReturnType;"
|
||||||
}
|
}
|
||||||
is ExportedConstructor ->
|
is ExportedConstructor ->
|
||||||
"constructor(${parameters.joinToString(", ") { it.toTypeScript() }});"
|
"constructor(${parameters.joinToString(", ") { it.toTypeScript() }});"
|
||||||
@@ -54,7 +70,7 @@ fun ExportedDeclaration.toTypeScript(indent: String): String = indent + when (th
|
|||||||
isMember -> (if (isAbstract) "abstract " else "") + (if (!mutable) "readonly " else "")
|
isMember -> (if (isAbstract) "abstract " else "") + (if (!mutable) "readonly " else "")
|
||||||
else -> if (mutable) "let " else "const "
|
else -> if (mutable) "let " else "const "
|
||||||
}
|
}
|
||||||
keyword + name + ": " + type.toTypeScript() + ";"
|
prefix + keyword + name + ": " + type.toTypeScript() + ";"
|
||||||
}
|
}
|
||||||
|
|
||||||
is ExportedClass -> {
|
is ExportedClass -> {
|
||||||
@@ -85,8 +101,8 @@ fun ExportedDeclaration.toTypeScript(indent: String): String = indent + when (th
|
|||||||
|
|
||||||
val bodyString = privateCtorString + membersString + indent
|
val bodyString = privateCtorString + membersString + indent
|
||||||
|
|
||||||
val klassExport = "$modifiers$keyword $name$renderedTypeParameters$superClassClause$superInterfacesClause {\n$bodyString}"
|
val klassExport = "$prefix$modifiers$keyword $name$renderedTypeParameters$superClassClause$superInterfacesClause {\n$bodyString}"
|
||||||
val staticsExport = if (nestedClasses.isNotEmpty()) "\n" + ExportedNamespace(name, nestedClasses).toTypeScript(indent) else ""
|
val staticsExport = if (nestedClasses.isNotEmpty()) "\n" + ExportedNamespace(name, nestedClasses).toTypeScript(indent, prefix) else ""
|
||||||
klassExport + staticsExport
|
klassExport + staticsExport
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+28
@@ -83,6 +83,34 @@ public class IrJsTypeScriptExportES6TestGenerated extends AbstractIrJsTypeScript
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("js/js.translator/testData/typescript-export/moduleSystems")
|
||||||
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
public static class ModuleSystems extends AbstractIrJsTypeScriptExportES6Test {
|
||||||
|
private void runTest(String testDataFilePath) throws Exception {
|
||||||
|
KotlinTestUtils.runTest0(this::doTest, TargetBackend.JS_IR_ES6, testDataFilePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testAllFilesPresentInModuleSystems() throws Exception {
|
||||||
|
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("js/js.translator/testData/typescript-export/moduleSystems"), Pattern.compile("^([^_](.+))\\.kt$"), null, TargetBackend.JS_IR_ES6, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("commonjs.kt")
|
||||||
|
public void testCommonjs() throws Exception {
|
||||||
|
runTest("js/js.translator/testData/typescript-export/moduleSystems/commonjs.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("plain.kt")
|
||||||
|
public void testPlain() throws Exception {
|
||||||
|
runTest("js/js.translator/testData/typescript-export/moduleSystems/plain.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("umd.kt")
|
||||||
|
public void testUmd() throws Exception {
|
||||||
|
runTest("js/js.translator/testData/typescript-export/moduleSystems/umd.kt");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("js/js.translator/testData/typescript-export/namespaces")
|
@TestMetadata("js/js.translator/testData/typescript-export/namespaces")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
|||||||
Generated
+28
@@ -83,6 +83,34 @@ public class IrJsTypeScriptExportTestGenerated extends AbstractIrJsTypeScriptExp
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("js/js.translator/testData/typescript-export/moduleSystems")
|
||||||
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
public static class ModuleSystems extends AbstractIrJsTypeScriptExportTest {
|
||||||
|
private void runTest(String testDataFilePath) throws Exception {
|
||||||
|
KotlinTestUtils.runTest0(this::doTest, TargetBackend.JS_IR, testDataFilePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testAllFilesPresentInModuleSystems() throws Exception {
|
||||||
|
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("js/js.translator/testData/typescript-export/moduleSystems"), Pattern.compile("^([^_](.+))\\.kt$"), null, TargetBackend.JS_IR, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("commonjs.kt")
|
||||||
|
public void testCommonjs() throws Exception {
|
||||||
|
runTest("js/js.translator/testData/typescript-export/moduleSystems/commonjs.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("plain.kt")
|
||||||
|
public void testPlain() throws Exception {
|
||||||
|
runTest("js/js.translator/testData/typescript-export/moduleSystems/plain.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("umd.kt")
|
||||||
|
public void testUmd() throws Exception {
|
||||||
|
runTest("js/js.translator/testData/typescript-export/moduleSystems/umd.kt");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("js/js.translator/testData/typescript-export/namespaces")
|
@TestMetadata("js/js.translator/testData/typescript-export/namespaces")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
|||||||
Generated
+28
@@ -78,6 +78,34 @@ public class LegacyJsTypeScriptExportTestGenerated extends AbstractLegacyJsTypeS
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("js/js.translator/testData/typescript-export/moduleSystems")
|
||||||
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
public static class ModuleSystems extends AbstractLegacyJsTypeScriptExportTest {
|
||||||
|
private void runTest(String testDataFilePath) throws Exception {
|
||||||
|
KotlinTestUtils.runTest0(this::doTest, TargetBackend.JS, testDataFilePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testAllFilesPresentInModuleSystems() throws Exception {
|
||||||
|
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("js/js.translator/testData/typescript-export/moduleSystems"), Pattern.compile("^([^_](.+))\\.kt$"), null, TargetBackend.JS, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("commonjs.kt")
|
||||||
|
public void testCommonjs() throws Exception {
|
||||||
|
runTest("js/js.translator/testData/typescript-export/moduleSystems/commonjs.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("plain.kt")
|
||||||
|
public void testPlain() throws Exception {
|
||||||
|
runTest("js/js.translator/testData/typescript-export/moduleSystems/plain.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("umd.kt")
|
||||||
|
public void testUmd() throws Exception {
|
||||||
|
runTest("js/js.translator/testData/typescript-export/moduleSystems/umd.kt");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("js/js.translator/testData/typescript-export/namespaces")
|
@TestMetadata("js/js.translator/testData/typescript-export/namespaces")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
type Nullable<T> = T | null | undefined
|
||||||
|
export namespace foo {
|
||||||
|
const prop: number;
|
||||||
|
class C {
|
||||||
|
constructor(x: number);
|
||||||
|
readonly x: number;
|
||||||
|
doubleX(): number;
|
||||||
|
}
|
||||||
|
function box(): string;
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
// CHECK_TYPESCRIPT_DECLARATIONS
|
||||||
|
// SKIP_MINIFICATION
|
||||||
|
// SKIP_NODE_JS
|
||||||
|
// MODULE_KIND: COMMON_JS
|
||||||
|
|
||||||
|
@file:JsExport
|
||||||
|
|
||||||
|
package foo
|
||||||
|
|
||||||
|
val prop = 10
|
||||||
|
|
||||||
|
class C(val x: Int) {
|
||||||
|
fun doubleX() = x * 2
|
||||||
|
}
|
||||||
|
|
||||||
|
fun box(): String = "OK"
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
declare namespace JS_TESTS {
|
||||||
|
type Nullable<T> = T | null | undefined
|
||||||
|
namespace foo {
|
||||||
|
const prop: number;
|
||||||
|
class C {
|
||||||
|
constructor(x: number);
|
||||||
|
readonly x: number;
|
||||||
|
doubleX(): number;
|
||||||
|
}
|
||||||
|
function box(): string;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
// CHECK_TYPESCRIPT_DECLARATIONS
|
||||||
|
// SKIP_MINIFICATION
|
||||||
|
// SKIP_NODE_JS
|
||||||
|
|
||||||
|
@file:JsExport
|
||||||
|
|
||||||
|
package foo
|
||||||
|
|
||||||
|
val prop = 10
|
||||||
|
|
||||||
|
class C(val x: Int) {
|
||||||
|
fun doubleX() = x * 2
|
||||||
|
}
|
||||||
|
|
||||||
|
fun box(): String = "OK"
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
type Nullable<T> = T | null | undefined
|
||||||
|
export namespace foo {
|
||||||
|
const prop: number;
|
||||||
|
class C {
|
||||||
|
constructor(x: number);
|
||||||
|
readonly x: number;
|
||||||
|
doubleX(): number;
|
||||||
|
}
|
||||||
|
function box(): string;
|
||||||
|
}
|
||||||
|
export as namespace JS_TESTS;
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
// CHECK_TYPESCRIPT_DECLARATIONS
|
||||||
|
// SKIP_MINIFICATION
|
||||||
|
// SKIP_NODE_JS
|
||||||
|
// MODULE_KIND: UMD
|
||||||
|
|
||||||
|
@file:JsExport
|
||||||
|
|
||||||
|
package foo
|
||||||
|
|
||||||
|
val prop = 10
|
||||||
|
|
||||||
|
class C(val x: Int) {
|
||||||
|
fun doubleX() = x * 2
|
||||||
|
}
|
||||||
|
|
||||||
|
fun box(): String = "OK"
|
||||||
Reference in New Issue
Block a user