From 609f0ca9bc700e3ce2cfe37e54c50e392808cb0d Mon Sep 17 00:00:00 2001 From: Svyatoslav Kuzmich Date: Tue, 14 Jul 2020 14:16:41 +0300 Subject: [PATCH] [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. --- .../js/export/ExportModelToTsDeclarations.kt | 34 ++++++++++++++----- .../IrJsTypeScriptExportES6TestGenerated.java | 28 +++++++++++++++ .../IrJsTypeScriptExportTestGenerated.java | 28 +++++++++++++++ ...LegacyJsTypeScriptExportTestGenerated.java | 28 +++++++++++++++ .../moduleSystems/commonjs.d.ts | 10 ++++++ .../moduleSystems/commonjs.kt | 16 +++++++++ .../moduleSystems/plain.d.ts | 12 +++++++ .../typescript-export/moduleSystems/plain.kt | 15 ++++++++ .../moduleSystems/tsconfig.json | 0 .../typescript-export/moduleSystems/umd.d.ts | 11 ++++++ .../typescript-export/moduleSystems/umd.kt | 16 +++++++++ 11 files changed, 189 insertions(+), 9 deletions(-) create mode 100644 js/js.translator/testData/typescript-export/moduleSystems/commonjs.d.ts create mode 100644 js/js.translator/testData/typescript-export/moduleSystems/commonjs.kt create mode 100644 js/js.translator/testData/typescript-export/moduleSystems/plain.d.ts create mode 100644 js/js.translator/testData/typescript-export/moduleSystems/plain.kt create mode 100644 js/js.translator/testData/typescript-export/moduleSystems/tsconfig.json create mode 100644 js/js.translator/testData/typescript-export/moduleSystems/umd.d.ts create mode 100644 js/js.translator/testData/typescript-export/moduleSystems/umd.kt diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/export/ExportModelToTsDeclarations.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/export/ExportModelToTsDeclarations.kt index 00e14949eec..505e0c7928f 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/export/ExportModelToTsDeclarations.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/export/ExportModelToTsDeclarations.kt @@ -6,23 +6,39 @@ package org.jetbrains.kotlin.ir.backend.js.export import org.jetbrains.kotlin.ir.backend.js.utils.sanitizeName +import org.jetbrains.kotlin.serialization.js.ModuleKind // TODO: Support module kinds other than plain fun ExportedModule.toTypeScript(): String { - val prefix = " type Nullable = T | null | undefined\n" - val body = declarations.joinToString("\n") { it.toTypeScript(" ") } - return "declare namespace ${sanitizeName(name)} {\n$prefix$body\n}\n" + val indent = if (moduleKind == ModuleKind.PLAIN) " " else "" + val types = "${indent}type Nullable = T | null | undefined\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.toTypeScript(indent: String): String = 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 ExportedNamespace -> - "namespace $name {\n" + declarations.toTypeScript("$indent ") + "$indent}" + "${prefix}namespace $name {\n" + declarations.toTypeScript("$indent ") + "$indent}" is ExportedFunction -> { val keyword: String = when { @@ -44,7 +60,7 @@ fun ExportedDeclaration.toTypeScript(indent: String): String = indent + when (th val renderedReturnType = returnType.toTypeScript() - "$keyword$name$renderedTypeParameters($renderedParameters): $renderedReturnType;" + "${prefix}$keyword$name$renderedTypeParameters($renderedParameters): $renderedReturnType;" } is ExportedConstructor -> "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 "") else -> if (mutable) "let " else "const " } - keyword + name + ": " + type.toTypeScript() + ";" + prefix + keyword + name + ": " + type.toTypeScript() + ";" } is ExportedClass -> { @@ -85,8 +101,8 @@ fun ExportedDeclaration.toTypeScript(indent: String): String = indent + when (th val bodyString = privateCtorString + membersString + indent - val klassExport = "$modifiers$keyword $name$renderedTypeParameters$superClassClause$superInterfacesClause {\n$bodyString}" - val staticsExport = if (nestedClasses.isNotEmpty()) "\n" + ExportedNamespace(name, nestedClasses).toTypeScript(indent) else "" + val klassExport = "$prefix$modifiers$keyword $name$renderedTypeParameters$superClassClause$superInterfacesClause {\n$bodyString}" + val staticsExport = if (nestedClasses.isNotEmpty()) "\n" + ExportedNamespace(name, nestedClasses).toTypeScript(indent, prefix) else "" klassExport + staticsExport } } diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/es6/semantics/IrJsTypeScriptExportES6TestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/es6/semantics/IrJsTypeScriptExportES6TestGenerated.java index 1778f562586..b6e7eab5ef7 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/es6/semantics/IrJsTypeScriptExportES6TestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/es6/semantics/IrJsTypeScriptExportES6TestGenerated.java @@ -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") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrJsTypeScriptExportTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrJsTypeScriptExportTestGenerated.java index 4d72e23d0bd..cc029d5de4e 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrJsTypeScriptExportTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrJsTypeScriptExportTestGenerated.java @@ -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") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/LegacyJsTypeScriptExportTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/LegacyJsTypeScriptExportTestGenerated.java index ad9510071ad..368ed7b8bd4 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/LegacyJsTypeScriptExportTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/LegacyJsTypeScriptExportTestGenerated.java @@ -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") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/js/js.translator/testData/typescript-export/moduleSystems/commonjs.d.ts b/js/js.translator/testData/typescript-export/moduleSystems/commonjs.d.ts new file mode 100644 index 00000000000..c25552a5bbd --- /dev/null +++ b/js/js.translator/testData/typescript-export/moduleSystems/commonjs.d.ts @@ -0,0 +1,10 @@ +type Nullable = T | null | undefined +export namespace foo { + const prop: number; + class C { + constructor(x: number); + readonly x: number; + doubleX(): number; + } + function box(): string; +} \ No newline at end of file diff --git a/js/js.translator/testData/typescript-export/moduleSystems/commonjs.kt b/js/js.translator/testData/typescript-export/moduleSystems/commonjs.kt new file mode 100644 index 00000000000..133cb4a7898 --- /dev/null +++ b/js/js.translator/testData/typescript-export/moduleSystems/commonjs.kt @@ -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" \ No newline at end of file diff --git a/js/js.translator/testData/typescript-export/moduleSystems/plain.d.ts b/js/js.translator/testData/typescript-export/moduleSystems/plain.d.ts new file mode 100644 index 00000000000..a4ae18f2ad8 --- /dev/null +++ b/js/js.translator/testData/typescript-export/moduleSystems/plain.d.ts @@ -0,0 +1,12 @@ +declare namespace JS_TESTS { + type Nullable = T | null | undefined + namespace foo { + const prop: number; + class C { + constructor(x: number); + readonly x: number; + doubleX(): number; + } + function box(): string; + } +} diff --git a/js/js.translator/testData/typescript-export/moduleSystems/plain.kt b/js/js.translator/testData/typescript-export/moduleSystems/plain.kt new file mode 100644 index 00000000000..92a9c994875 --- /dev/null +++ b/js/js.translator/testData/typescript-export/moduleSystems/plain.kt @@ -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" \ No newline at end of file diff --git a/js/js.translator/testData/typescript-export/moduleSystems/tsconfig.json b/js/js.translator/testData/typescript-export/moduleSystems/tsconfig.json new file mode 100644 index 00000000000..e69de29bb2d diff --git a/js/js.translator/testData/typescript-export/moduleSystems/umd.d.ts b/js/js.translator/testData/typescript-export/moduleSystems/umd.d.ts new file mode 100644 index 00000000000..001b152254b --- /dev/null +++ b/js/js.translator/testData/typescript-export/moduleSystems/umd.d.ts @@ -0,0 +1,11 @@ +type Nullable = 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; \ No newline at end of file diff --git a/js/js.translator/testData/typescript-export/moduleSystems/umd.kt b/js/js.translator/testData/typescript-export/moduleSystems/umd.kt new file mode 100644 index 00000000000..1e11dd8246d --- /dev/null +++ b/js/js.translator/testData/typescript-export/moduleSystems/umd.kt @@ -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" \ No newline at end of file