fix(Kotlin/JS IR): nested comments in implicit export.
This commit is contained in:
+117
-112
@@ -55,138 +55,142 @@ fun List<ExportedDeclaration>.toTypeScript(moduleKind: ModuleKind): String {
|
||||
fun List<ExportedDeclaration>.toTypeScript(indent: String): String =
|
||||
joinToString("") { it.toTypeScript(indent) + "\n" }
|
||||
|
||||
fun ExportedDeclaration.toTypeScript(indent: String, prefix: String = ""): String = indent + when (this) {
|
||||
is ErrorDeclaration -> "/* ErrorDeclaration: $message */"
|
||||
fun ExportedDeclaration.toTypeScript(indent: String, prefix: String = ""): String =
|
||||
indent + when (this) {
|
||||
is ErrorDeclaration -> "/* ErrorDeclaration: $message */"
|
||||
|
||||
is ExportedNamespace ->
|
||||
"${prefix}namespace $name {\n" + declarations.toTypeScript("$indent ") + "$indent}"
|
||||
is ExportedNamespace ->
|
||||
"${prefix}namespace $name {\n" + declarations.toTypeScript("$indent ") + "$indent}"
|
||||
|
||||
is ExportedFunction -> {
|
||||
val visibility = if (isProtected) "protected " else ""
|
||||
is ExportedFunction -> {
|
||||
val visibility = if (isProtected) "protected " else ""
|
||||
|
||||
val keyword: String = when {
|
||||
isMember -> when {
|
||||
isStatic -> "static "
|
||||
isAbstract -> "abstract "
|
||||
else -> ""
|
||||
val keyword: String = when {
|
||||
isMember -> when {
|
||||
isStatic -> "static "
|
||||
isAbstract -> "abstract "
|
||||
else -> ""
|
||||
}
|
||||
else -> "function "
|
||||
}
|
||||
else -> "function "
|
||||
}
|
||||
|
||||
val renderedParameters = parameters.joinToString(", ") { it.toTypeScript(indent) }
|
||||
val renderedParameters = parameters.joinToString(", ") { it.toTypeScript(indent) }
|
||||
|
||||
val renderedTypeParameters =
|
||||
if (typeParameters.isNotEmpty())
|
||||
"<" + typeParameters.joinToString(", ") { it.toTypeScript(indent) } + ">"
|
||||
else
|
||||
""
|
||||
val renderedTypeParameters =
|
||||
if (typeParameters.isNotEmpty())
|
||||
"<" + typeParameters.joinToString(", ") { it.toTypeScript(indent) } + ">"
|
||||
else
|
||||
""
|
||||
|
||||
val renderedReturnType = returnType.toTypeScript(indent)
|
||||
val containsUnresolvedChar = !name.isValidES5Identifier()
|
||||
val renderedReturnType = returnType.toTypeScript(indent)
|
||||
val containsUnresolvedChar = !name.isValidES5Identifier()
|
||||
|
||||
val escapedName = when {
|
||||
isMember && containsUnresolvedChar -> "\"$name\""
|
||||
else -> name
|
||||
}
|
||||
|
||||
if (!isMember && containsUnresolvedChar) "" else "${prefix}$visibility$keyword$escapedName$renderedTypeParameters($renderedParameters): $renderedReturnType;"
|
||||
}
|
||||
|
||||
is ExportedConstructor -> {
|
||||
val renderedParameters = parameters.joinToString(", ") { it.toTypeScript(indent) }
|
||||
"${visibility.keyword}constructor($renderedParameters);"
|
||||
}
|
||||
|
||||
is ExportedConstructSignature -> {
|
||||
val renderedParameters = parameters.joinToString(", ") { it.toTypeScript(indent) }
|
||||
"new($renderedParameters): ${returnType.toTypeScript(indent)};"
|
||||
}
|
||||
|
||||
is ExportedProperty -> {
|
||||
val visibility = if (isProtected) "protected " else ""
|
||||
val keyword = when {
|
||||
isMember -> (if (isAbstract) "abstract " else "")
|
||||
else -> if (mutable) "let " else "const "
|
||||
}
|
||||
val possibleStatic = if (isMember && isStatic) "static " else ""
|
||||
val containsUnresolvedChar = !name.isValidES5Identifier()
|
||||
val memberName = when {
|
||||
isMember && containsUnresolvedChar -> "\"$name\""
|
||||
else -> name
|
||||
}
|
||||
val typeToTypeScript = type.toTypeScript(indent)
|
||||
if (isMember && !isField) {
|
||||
val getter = "$prefix$visibility$possibleStatic${keyword}get $memberName(): $typeToTypeScript;"
|
||||
if (!mutable) getter
|
||||
else getter + "\n" + "$indent$prefix$visibility$possibleStatic${keyword}set $memberName(value: $typeToTypeScript);"
|
||||
} else {
|
||||
if (!isMember && containsUnresolvedChar) ""
|
||||
else {
|
||||
val readonly = if (isMember && !mutable) "readonly " else ""
|
||||
"$prefix$visibility$possibleStatic$keyword$readonly$memberName: $typeToTypeScript;"
|
||||
val escapedName = when {
|
||||
isMember && containsUnresolvedChar -> "\"$name\""
|
||||
else -> name
|
||||
}
|
||||
|
||||
if (!isMember && containsUnresolvedChar) "" else "${prefix}$visibility$keyword$escapedName$renderedTypeParameters($renderedParameters): $renderedReturnType;"
|
||||
}
|
||||
}
|
||||
|
||||
is ExportedClass -> {
|
||||
val keyword = if (isInterface) "interface" else "class"
|
||||
val superInterfacesKeyword = if (isInterface) "extends" else "implements"
|
||||
is ExportedConstructor -> {
|
||||
val renderedParameters = parameters.joinToString(", ") { it.toTypeScript(indent) }
|
||||
"${visibility.keyword}constructor($renderedParameters);"
|
||||
}
|
||||
|
||||
val superClassClause = superClass?.let { it.toExtendsClause(indent) } ?: ""
|
||||
val superInterfacesClause = superInterfaces.toImplementsClause(superInterfacesKeyword, indent)
|
||||
is ExportedConstructSignature -> {
|
||||
val renderedParameters = parameters.joinToString(", ") { it.toTypeScript(indent) }
|
||||
"new($renderedParameters): ${returnType.toTypeScript(indent)};"
|
||||
}
|
||||
|
||||
val members = members
|
||||
.let { if (shouldNotBeImplemented()) it.withMagicProperty() else it }
|
||||
.map {
|
||||
if (!ir.isInner || it !is ExportedFunction || !it.isStatic) {
|
||||
it
|
||||
} else {
|
||||
// Remove $outer argument from secondary constructors of inner classes
|
||||
it.copy(parameters = it.parameters.drop(1))
|
||||
is ExportedProperty -> {
|
||||
val visibility = if (isProtected) "protected " else ""
|
||||
val keyword = when {
|
||||
isMember -> (if (isAbstract) "abstract " else "")
|
||||
else -> if (mutable) "let " else "const "
|
||||
}
|
||||
val possibleStatic = if (isMember && isStatic) "static " else ""
|
||||
val containsUnresolvedChar = !name.isValidES5Identifier()
|
||||
val memberName = when {
|
||||
isMember && containsUnresolvedChar -> "\"$name\""
|
||||
else -> name
|
||||
}
|
||||
val typeToTypeScript = type.toTypeScript(indent)
|
||||
if (isMember && !isField) {
|
||||
val getter = "$prefix$visibility$possibleStatic${keyword}get $memberName(): $typeToTypeScript;"
|
||||
if (!mutable) getter
|
||||
else getter + "\n" + "$indent$prefix$visibility$possibleStatic${keyword}set $memberName(value: $typeToTypeScript);"
|
||||
} else {
|
||||
if (!isMember && containsUnresolvedChar) ""
|
||||
else {
|
||||
val readonly = if (isMember && !mutable) "readonly " else ""
|
||||
"$prefix$visibility$possibleStatic$keyword$readonly$memberName: $typeToTypeScript;"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val (innerClasses, nonInnerClasses) = nestedClasses.partition { it.ir.isInner }
|
||||
val innerClassesProperties = innerClasses.map { it.toReadonlyProperty() }
|
||||
val membersString = (members + innerClassesProperties).joinToString("") { it.toTypeScript("$indent ") + "\n" }
|
||||
is ExportedClass -> {
|
||||
val keyword = if (isInterface) "interface" else "class"
|
||||
val superInterfacesKeyword = if (isInterface) "extends" else "implements"
|
||||
|
||||
// If there are no exported constructors, add a private constructor to disable default one
|
||||
val privateCtorString =
|
||||
if (!isInterface && !isAbstract && members.none { it is ExportedConstructor })
|
||||
"$indent private constructor();\n"
|
||||
else
|
||||
""
|
||||
val superClassClause = superClass?.let { it.toExtendsClause(indent) } ?: ""
|
||||
val superInterfacesClause = superInterfaces.toImplementsClause(superInterfacesKeyword, indent)
|
||||
|
||||
val renderedTypeParameters =
|
||||
if (typeParameters.isNotEmpty())
|
||||
"<" + typeParameters.joinToString(", ") + ">"
|
||||
else
|
||||
""
|
||||
val members = members
|
||||
.let { if (shouldNotBeImplemented()) it.withMagicProperty() else it }
|
||||
.map {
|
||||
if (!ir.isInner || it !is ExportedFunction || !it.isStatic) {
|
||||
it
|
||||
} else {
|
||||
// Remove $outer argument from secondary constructors of inner classes
|
||||
it.copy(parameters = it.parameters.drop(1))
|
||||
}
|
||||
}
|
||||
|
||||
val modifiers = if (isAbstract && !isInterface) "abstract " else ""
|
||||
val (innerClasses, nonInnerClasses) = nestedClasses.partition { it.ir.isInner }
|
||||
val innerClassesProperties = innerClasses.map { it.toReadonlyProperty() }
|
||||
val membersString = (members + innerClassesProperties).joinToString("") { it.toTypeScript("$indent ") + "\n" }
|
||||
|
||||
val bodyString = privateCtorString + membersString + indent
|
||||
// If there are no exported constructors, add a private constructor to disable default one
|
||||
val privateCtorString =
|
||||
if (!isInterface && !isAbstract && members.none { it is ExportedConstructor })
|
||||
"$indent private constructor();\n"
|
||||
else
|
||||
""
|
||||
|
||||
val nestedClasses = nonInnerClasses + innerClasses.map { it.withProtectedConstructors() }
|
||||
val klassExport = "$prefix$modifiers$keyword $name$renderedTypeParameters$superClassClause$superInterfacesClause {\n$bodyString}"
|
||||
val staticsExport =
|
||||
if (nestedClasses.isNotEmpty()) "\n" + ExportedNamespace(name, nestedClasses).toTypeScript(indent, prefix) else ""
|
||||
val renderedTypeParameters =
|
||||
if (typeParameters.isNotEmpty())
|
||||
"<" + typeParameters.joinToString(", ") + ">"
|
||||
else
|
||||
""
|
||||
|
||||
if (name.isValidES5Identifier()) klassExport + staticsExport else ""
|
||||
val modifiers = if (isAbstract && !isInterface) "abstract " else ""
|
||||
|
||||
val bodyString = privateCtorString + membersString + indent
|
||||
|
||||
val nestedClasses = nonInnerClasses + innerClasses.map { it.withProtectedConstructors() }
|
||||
val klassExport =
|
||||
"$prefix$modifiers$keyword $name$renderedTypeParameters$superClassClause$superInterfacesClause {\n$bodyString}"
|
||||
val staticsExport =
|
||||
if (nestedClasses.isNotEmpty()) "\n" + ExportedNamespace(name, nestedClasses).toTypeScript(indent, prefix) else ""
|
||||
|
||||
if (name.isValidES5Identifier()) klassExport + staticsExport else ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun ExportedType.toExtendsClause(indent: String): String {
|
||||
return when (this) {
|
||||
is ExportedType.ImplicitlyExportedType -> " /*${type.toExtendsClause(indent)} */"
|
||||
else -> " extends ${toTypeScript(indent)}"
|
||||
val isImplicitlyExportedType = this is ExportedType.ImplicitlyExportedType
|
||||
val extendsClause = " extends ${toTypeScript(indent, isImplicitlyExportedType)}"
|
||||
return when {
|
||||
isImplicitlyExportedType -> " /*$extendsClause */"
|
||||
else -> extendsClause
|
||||
}
|
||||
}
|
||||
|
||||
fun List<ExportedType>.toImplementsClause(superInterfacesKeyword: String, indent: String): String {
|
||||
val (exportedInterfaces, nonExportedInterfaces) = partition { it !is ExportedType.ImplicitlyExportedType }
|
||||
val listOfNonExportedInterfaces = nonExportedInterfaces.joinToString(", ") {
|
||||
(it as ExportedType.ImplicitlyExportedType).type.toTypeScript(indent)
|
||||
(it as ExportedType.ImplicitlyExportedType).type.toTypeScript(indent, true)
|
||||
}
|
||||
return when {
|
||||
exportedInterfaces.isEmpty() && nonExportedInterfaces.isNotEmpty() ->
|
||||
@@ -275,39 +279,40 @@ fun ExportedParameter.toTypeScript(indent: String): String {
|
||||
return "$name$questionMark: $type"
|
||||
}
|
||||
|
||||
fun ExportedType.toTypeScript(indent: String): String = when (this) {
|
||||
fun ExportedType.toTypeScript(indent: String, isInCommentContext: Boolean = false): String = when (this) {
|
||||
is ExportedType.Primitive -> typescript
|
||||
is ExportedType.Array -> "Array<${elementType.toTypeScript(indent)}>"
|
||||
is ExportedType.Array -> "Array<${elementType.toTypeScript(indent, isInCommentContext)}>"
|
||||
is ExportedType.Function -> "(" + parameterTypes
|
||||
.withIndex()
|
||||
.joinToString(", ") { (index, type) ->
|
||||
"p$index: ${type.toTypeScript(indent)}"
|
||||
} + ") => " + returnType.toTypeScript(indent)
|
||||
"p$index: ${type.toTypeScript(indent, isInCommentContext)}"
|
||||
} + ") => " + returnType.toTypeScript(indent, isInCommentContext)
|
||||
|
||||
is ExportedType.ClassType ->
|
||||
name + if (arguments.isNotEmpty()) "<${arguments.joinToString(", ") { it.toTypeScript(indent) }}>" else ""
|
||||
name + if (arguments.isNotEmpty()) "<${arguments.joinToString(", ") { it.toTypeScript(indent, isInCommentContext) }}>" else ""
|
||||
is ExportedType.TypeOf ->
|
||||
"typeof $name"
|
||||
|
||||
is ExportedType.ErrorType -> "any /*$comment*/"
|
||||
is ExportedType.Nullable -> "Nullable<" + baseType.toTypeScript(indent) + ">"
|
||||
is ExportedType.ErrorType -> if (isInCommentContext) comment else "any /*$comment*/"
|
||||
is ExportedType.Nullable -> "Nullable<" + baseType.toTypeScript(indent, isInCommentContext) + ">"
|
||||
is ExportedType.InlineInterfaceType -> {
|
||||
members.joinToString(prefix = "{\n", postfix = "$indent}", separator = "") { it.toTypeScript("$indent ") + "\n" }
|
||||
}
|
||||
is ExportedType.IntersectionType -> {
|
||||
lhs.toTypeScript(indent) + " & " + rhs.toTypeScript(indent)
|
||||
lhs.toTypeScript(indent) + " & " + rhs.toTypeScript(indent, isInCommentContext)
|
||||
}
|
||||
is ExportedType.UnionType -> {
|
||||
lhs.toTypeScript(indent) + " | " + rhs.toTypeScript(indent)
|
||||
lhs.toTypeScript(indent) + " | " + rhs.toTypeScript(indent, isInCommentContext)
|
||||
}
|
||||
is ExportedType.LiteralType.StringLiteralType -> "\"$value\""
|
||||
is ExportedType.LiteralType.NumberLiteralType -> value.toString()
|
||||
is ExportedType.ImplicitlyExportedType -> {
|
||||
ExportedType.Primitive.Any.toTypeScript(indent) + "/* ${type.toTypeScript("")} */"
|
||||
val typeString = type.toTypeScript("", true)
|
||||
if (isInCommentContext) typeString else ExportedType.Primitive.Any.toTypeScript(indent) + "/* $typeString */"
|
||||
}
|
||||
is ExportedType.TypeParameter -> if (constraint == null) {
|
||||
name
|
||||
} else {
|
||||
"$name extends ${constraint.toTypeScript(indent)}"
|
||||
"$name extends ${constraint.toTypeScript(indent, isInCommentContext)}"
|
||||
}
|
||||
}
|
||||
@@ -31,5 +31,11 @@ declare namespace JS_TESTS {
|
||||
class F extends foo.A /* implements foo.NonExportedInterface */ {
|
||||
constructor();
|
||||
}
|
||||
class G /* implements foo.NonExportedGenericInterface<foo.NonExportedType> */ {
|
||||
constructor();
|
||||
}
|
||||
class H /* extends foo.NonExportedGenericType<foo.NonExportedType> */ {
|
||||
constructor();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,9 @@
|
||||
package foo
|
||||
|
||||
interface NonExportedInterface
|
||||
interface NonExportedGenericInterface<T>
|
||||
open class NonExportedType(val value: Int)
|
||||
open class NonExportedGenericType<T>(val value: T)
|
||||
|
||||
@JsExport
|
||||
interface ExportedInterface
|
||||
@@ -44,4 +46,10 @@ class D : NonExportedInterface, ExportedInterface
|
||||
class E : NonExportedType(42), ExportedInterface
|
||||
|
||||
@JsExport
|
||||
class F : A(NonExportedType(42)), NonExportedInterface
|
||||
class F : A(NonExportedType(42)), NonExportedInterface
|
||||
|
||||
@JsExport
|
||||
class G : NonExportedGenericInterface<NonExportedType>
|
||||
|
||||
@JsExport
|
||||
class H : NonExportedGenericType<NonExportedType>(NonExportedType(42))
|
||||
Reference in New Issue
Block a user