aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/src/main/kotlin/pages/ContentNodes.kt2
-rw-r--r--plugins/base/src/main/kotlin/transformers/documentables/ModuleAndPackageDocumentationTransformer.kt34
-rw-r--r--plugins/base/src/main/kotlin/transformers/pages/samples/SamplesTransformer.kt74
-rw-r--r--plugins/base/src/main/kotlin/translators/descriptors/DefaultDescriptorToDocumentableTranslator.kt15
-rw-r--r--plugins/base/src/main/kotlin/translators/documentables/DefaultPageCreator.kt18
-rw-r--r--plugins/base/src/test/kotlin/linkableContent/LinkableContentTest.kt161
-rw-r--r--plugins/base/src/test/resources/linkable/includes/include1.md7
-rw-r--r--plugins/base/src/test/resources/linkable/includes/include2.md7
-rw-r--r--plugins/base/src/test/resources/linkable/samples/jsMain/kotlin/JsClass.kt9
-rw-r--r--plugins/base/src/test/resources/linkable/samples/jsMain/resources/Samples.kt10
-rw-r--r--plugins/base/src/test/resources/linkable/samples/jvmMain/kotlin/JvmClass.kt9
-rw-r--r--plugins/base/src/test/resources/linkable/samples/jvmMain/resources/Samples.kt10
-rw-r--r--plugins/base/src/test/resources/linkable/sources/jsMain/kotlin/JsClass.kt3
-rw-r--r--plugins/base/src/test/resources/linkable/sources/jvmMain/kotlin/JvmClass.kt3
14 files changed, 313 insertions, 49 deletions
diff --git a/core/src/main/kotlin/pages/ContentNodes.kt b/core/src/main/kotlin/pages/ContentNodes.kt
index c0576006..4127796c 100644
--- a/core/src/main/kotlin/pages/ContentNodes.kt
+++ b/core/src/main/kotlin/pages/ContentNodes.kt
@@ -91,6 +91,7 @@ data class ContentResolvedLink(
/** All links that do not need to be resolved */
data class ContentEmbeddedResource(
+ override val children: List<ContentNode> = emptyList(),
val address: String,
val altText: String?,
override val dci: DCI,
@@ -98,7 +99,6 @@ data class ContentEmbeddedResource(
override val style: Set<Style> = emptySet(),
override val extra: PropertyContainer<ContentNode> = PropertyContainer.empty()
) : ContentLink {
- override val children = emptyList<ContentNode>()
override fun withNewExtras(newExtras: PropertyContainer<ContentNode>): ContentEmbeddedResource =
copy(extra = newExtras)
}
diff --git a/plugins/base/src/main/kotlin/transformers/documentables/ModuleAndPackageDocumentationTransformer.kt b/plugins/base/src/main/kotlin/transformers/documentables/ModuleAndPackageDocumentationTransformer.kt
index a5d6540e..b69c43d3 100644
--- a/plugins/base/src/main/kotlin/transformers/documentables/ModuleAndPackageDocumentationTransformer.kt
+++ b/plugins/base/src/main/kotlin/transformers/documentables/ModuleAndPackageDocumentationTransformer.kt
@@ -2,6 +2,8 @@ package org.jetbrains.dokka.base.transformers.documentables
import org.jetbrains.dokka.model.DModule
import org.jetbrains.dokka.model.PlatformDependent
+import org.jetbrains.dokka.model.doc.DocumentationNode
+import org.jetbrains.dokka.pages.PlatformData
import org.jetbrains.dokka.parsers.MarkdownParser
import org.jetbrains.dokka.plugability.DokkaContext
import org.jetbrains.dokka.transformers.documentation.PreMergeDocumentableTransformer
@@ -66,15 +68,15 @@ internal object ModuleAndPackageDocumentationTransformer : PreMergeDocumentableT
}
}.toMap()
- val packagesDocumentation = module.packages.map {
- it.name to it.platformData.mapNotNull { pd ->
- val doc = modulesAndPackagesDocumentation[Pair(module.name, pd)]
- val facade = context.platforms[pd]?.facade
- ?: return@mapNotNull null.also { context.logger.warn("Could not find platform data for ${pd.name}") }
- val descriptor = facade.resolveSession.getPackageFragment(FqName(it.name))
- ?: return@mapNotNull null.also { context.logger.warn("Could not find descriptor for $") }
- doc?.get("Package")?.get(it.name)?.run {
- pd to MarkdownParser(
+ val packagesDocumentation = module.packages.map { dPackage ->
+ dPackage.name to dPackage.platformData.mapNotNull { platformData ->
+ val doc = modulesAndPackagesDocumentation[Pair(module.name, platformData)]
+ val facade = context.platforms[platformData]?.facade
+ ?: return@mapNotNull null.also { context.logger.warn("Could not find platform data for ${platformData.name}") }
+ val descriptor = facade.resolveSession.getPackageFragment(FqName(dPackage.name))
+ ?: return@mapNotNull null.also { context.logger.warn("Could not find descriptor for ${dPackage.name}") }
+ doc?.get("Package")?.get(dPackage.name)?.run {
+ platformData to MarkdownParser(
facade,
descriptor,
context.logger
@@ -84,11 +86,12 @@ internal object ModuleAndPackageDocumentationTransformer : PreMergeDocumentableT
}.toMap()
module.copy(
- documentation = module.documentation.let { PlatformDependent(it.map + moduleDocumentation) },
+ documentation = module.documentation.let { mergeDocumentation(it.map, moduleDocumentation) },
packages = module.packages.map {
- if (packagesDocumentation[it.name] != null)
+ val packageDocumentation = packagesDocumentation[it.name]
+ if (packageDocumentation != null && packageDocumentation.isNotEmpty())
it.copy(documentation = it.documentation.let { value ->
- PlatformDependent(value.map + packagesDocumentation[it.name]!!)
+ mergeDocumentation(value.map, packageDocumentation)
})
else
it
@@ -96,4 +99,11 @@ internal object ModuleAndPackageDocumentationTransformer : PreMergeDocumentableT
)
}
}
+
+ private fun mergeDocumentation(origin: Map<PlatformData, DocumentationNode>, new: Map<PlatformData, DocumentationNode>) = PlatformDependent(
+ (origin.asSequence() + new.asSequence())
+ .distinct()
+ .groupBy({ it.key }, { it.value })
+ .mapValues { (_, values) -> DocumentationNode(values.flatMap { it.children }) }
+ )
}
diff --git a/plugins/base/src/main/kotlin/transformers/pages/samples/SamplesTransformer.kt b/plugins/base/src/main/kotlin/transformers/pages/samples/SamplesTransformer.kt
index bd10a923..b41b07f5 100644
--- a/plugins/base/src/main/kotlin/transformers/pages/samples/SamplesTransformer.kt
+++ b/plugins/base/src/main/kotlin/transformers/pages/samples/SamplesTransformer.kt
@@ -6,6 +6,7 @@ import org.jetbrains.dokka.EnvironmentAndFacade
import org.jetbrains.dokka.Platform
import org.jetbrains.dokka.analysis.AnalysisEnvironment
import org.jetbrains.dokka.analysis.DokkaResolutionFacade
+import org.jetbrains.dokka.base.renderers.platforms
import org.jetbrains.dokka.links.DRI
import org.jetbrains.dokka.model.doc.Sample
import org.jetbrains.dokka.model.properties.PropertyContainer
@@ -30,9 +31,9 @@ abstract class SamplesTransformer(val context: DokkaContext) : PageTransformer {
val analysis = setUpAnalysis(context)
return input.transformContentPagesTree { page ->
- page.documentable?.documentation?.map?.entries?.fold(page) { acc, entry ->
- entry.value.children.filterIsInstance<Sample>().fold(acc) { acc, sample ->
- acc.modified(content = acc.content.addSample(page, entry.key, sample.name, analysis))
+ page.documentable?.documentation?.allEntries?.fold(page) { acc, entry ->
+ entry.second.children.filterIsInstance<Sample>().fold(acc) { acc, sample ->
+ acc.modified(content = acc.content.addSample(page, entry.first, sample.name, analysis))
}
} ?: page
}
@@ -55,19 +56,35 @@ abstract class SamplesTransformer(val context: DokkaContext) : PageTransformer {
}
}.toMap()
- private fun ContentNode.addSample(contentPage: ContentPage, platform: PlatformData, fqName: String, analysis: Map<PlatformData, EnvironmentAndFacade>): ContentNode {
- val facade = analysis[platform]?.facade ?:
- return this.also { context.logger.warn("Cannot resolve facade for platform ${platform.name}")}
+ private fun ContentNode.addSample(contentPage: ContentPage, platform: PlatformData?, fqName: String, analysis: Map<PlatformData, EnvironmentAndFacade>): ContentNode {
+ val facade = if(platform == null) {
+ analysis.entries.find { it.key.platformType.name == "common" }?.value
+ } else {
+ analysis[platform]
+ }?.facade ?: return this.also { context.logger.warn("Cannot resolve facade for platform ${platform?.name ?: "expect"}") }
val psiElement = fqNameToPsiElement(facade, fqName) ?:
return this.also { context.logger.warn("Cannot find PsiElement corresponding to $fqName") }
val imports = processImports(psiElement) // TODO: Process somehow imports. Maybe just attach them at the top of each body
val body = processBody(psiElement)
- val node = platformHintedContentCode(platform, contentPage.dri, body, "kotlin")
- return this.safeAs<ContentGroup>()?.run { copy(
- children = children.indexOfFirst { contentNode ->
- contentNode.safeAs<ContentHeader>()?.children?.firstOrNull()?.safeAs<ContentText>()?.text == "Sample"
- }.takeIf { it != -1 }?.let { children.apply { this.safeAs<MutableList<ContentNode>>()?.add(it+1, node) } } ?: children.also { context.logger.warn("Not found Sample block in ${contentPage.dri}")}
- ) } ?: this.also { context.logger.warn("ContentPage ${contentPage.dri} cannot be cast to ContentGroup") }
+ val node = contentCode(contentPage.platforms(), contentPage.dri, body, "kotlin")
+
+ return bfs(fqName, node)
+ }
+
+ private fun ContentNode.bfs(fqName: String, node: ContentCode): ContentNode {
+ return when(this) {
+ is ContentHeader -> copy(children.map { it.bfs(fqName, node) })
+ is ContentCode -> copy(children.map { it.bfs(fqName, node) })
+ is ContentDRILink -> copy(children.map { it.bfs(fqName, node) })
+ is ContentResolvedLink -> copy(children.map { it.bfs(fqName, node) })
+ is ContentEmbeddedResource -> copy(children.map { it.bfs(fqName, node) })
+ is ContentTable -> copy(children.map { it.bfs(fqName, node) as ContentGroup })
+ is ContentList -> copy(children.map { it.bfs(fqName, node) })
+ is ContentGroup -> copy(children.map { it.bfs(fqName, node) })
+ is PlatformHintedContent -> copy(inner.bfs(fqName, node))
+ is ContentText -> if (text == fqName) node else this
+ else -> this
+ }
}
private fun fqNameToPsiElement(resolutionFacade: DokkaResolutionFacade, functionName: String): PsiElement? {
@@ -79,24 +96,21 @@ abstract class SamplesTransformer(val context: DokkaContext) : PageTransformer {
return DescriptorToSourceUtils.descriptorToDeclaration(symbol)
}
- private fun platformHintedContentCode(platformData: PlatformData, dri: Set<DRI>, content: String, language: String) =
- PlatformHintedContent(
- inner = ContentCode(
- children = listOf(
- ContentText(
- text = content,
- dci = DCI(dri, ContentKind.BriefComment),
- platforms = setOf(platformData),
- style = emptySet(),
- extra = PropertyContainer.empty()
- )
- ),
- language = language,
- extra = PropertyContainer.empty(),
- dci = DCI(dri, ContentKind.Source),
- platforms = setOf(platformData),
- style = emptySet()
+ private fun contentCode(platforms: List<PlatformData>, dri: Set<DRI>, content: String, language: String) =
+ ContentCode(
+ children = listOf(
+ ContentText(
+ text = content,
+ dci = DCI(dri, ContentKind.BriefComment),
+ platforms = platforms.toSet(),
+ style = emptySet(),
+ extra = PropertyContainer.empty()
+ )
),
- platforms = setOf(platformData)
+ language = language,
+ extra = PropertyContainer.empty(),
+ dci = DCI(dri, ContentKind.Source),
+ platforms = platforms.toSet(),
+ style = emptySet()
)
} \ No newline at end of file
diff --git a/plugins/base/src/main/kotlin/translators/descriptors/DefaultDescriptorToDocumentableTranslator.kt b/plugins/base/src/main/kotlin/translators/descriptors/DefaultDescriptorToDocumentableTranslator.kt
index be18c592..00e3239c 100644
--- a/plugins/base/src/main/kotlin/translators/descriptors/DefaultDescriptorToDocumentableTranslator.kt
+++ b/plugins/base/src/main/kotlin/translators/descriptors/DefaultDescriptorToDocumentableTranslator.kt
@@ -500,12 +500,15 @@ private class DokkaDescriptorVisitor(
.filter { it.kind == ClassKind.ENUM_ENTRY }
.map { enumEntryDescriptor(it, parent) }
-
- private fun DeclarationDescriptor.resolveDescriptorData(platformData: PlatformData?): PlatformDependent<DocumentationNode> =
- if (platformData != null) PlatformDependent.from(
- platformData,
- getDocumentation()
- ) else PlatformDependent.expectFrom(getDocumentation())
+ private fun DeclarationDescriptor.resolveDescriptorData(platformData: PlatformData?): PlatformDependent<DocumentationNode> {
+ val documentation = getDocumentation()
+ return if (documentation.children.isEmpty())
+ PlatformDependent.empty()
+ else if (platformData != null)
+ PlatformDependent.from(platformData, documentation)
+ else
+ PlatformDependent.expectFrom(documentation)
+ }
private fun ClassDescriptor.resolveClassDescriptionData(platformData: PlatformData?): ClassInfo {
return ClassInfo(
diff --git a/plugins/base/src/main/kotlin/translators/documentables/DefaultPageCreator.kt b/plugins/base/src/main/kotlin/translators/documentables/DefaultPageCreator.kt
index 424e6aa4..dd28b533 100644
--- a/plugins/base/src/main/kotlin/translators/documentables/DefaultPageCreator.kt
+++ b/plugins/base/src/main/kotlin/translators/documentables/DefaultPageCreator.kt
@@ -284,6 +284,23 @@ open class DefaultPageCreator(
}
}
+ fun DocumentableContentBuilder.contentForSamples() {
+ val samples = tags.withTypeNamed<Sample>()
+ if (samples.isNotEmpty()) {
+ platforms.forEach { platformData ->
+ val content = samples.filter { it.value.isEmpty() || platformData in it.value }
+ if (content.isNotEmpty()) {
+ group(platformData = setOf(platformData)) {
+ header(4, kind = ContentKind.Comment) { text("Samples") }
+ content.forEach {
+ comment(Text(it.key))
+ }
+ }
+ }
+ }
+ }
+ }
+
fun DocumentableContentBuilder.contentForUnnamedTags() {
val unnamedTags: List<PlatformDependent<TagWrapper>> =
tags.filterNot { (k, _) -> k.isSubclassOf(NamedTagWrapper::class) || k in specialTags }
@@ -305,6 +322,7 @@ open class DefaultPageCreator(
header(3) { text("Description") }
platformDependentHint(platformData = platforms.toSet()) {
contentForDescription()
+ contentForSamples()
contentForParams()
contentForUnnamedTags()
contentForSeeAlso()
diff --git a/plugins/base/src/test/kotlin/linkableContent/LinkableContentTest.kt b/plugins/base/src/test/kotlin/linkableContent/LinkableContentTest.kt
new file mode 100644
index 00000000..71854035
--- /dev/null
+++ b/plugins/base/src/test/kotlin/linkableContent/LinkableContentTest.kt
@@ -0,0 +1,161 @@
+package linkableContent
+
+import org.jetbrains.dokka.SourceLinkDefinitionImpl
+import org.jetbrains.dokka.base.transformers.pages.samples.DefaultSamplesTransformer
+import org.jetbrains.dokka.base.transformers.pages.sourcelinks.SourceLinksTransformer
+import org.jetbrains.dokka.pages.*
+import org.jetbrains.dokka.testApi.testRunner.AbstractCoreTest
+import org.jetbrains.kotlin.utils.addToStdlib.safeAs
+import org.junit.jupiter.api.Assertions
+import org.junit.jupiter.api.Test
+
+class LinkableContentTest : AbstractCoreTest() {
+
+ @Test
+ fun `Include module and package documentation`() {
+
+ val testDataDir = getTestDataDir("multiplatform/basicMultiplatformTest").toAbsolutePath()
+ val includesDir = getTestDataDir("linkable/includes").toAbsolutePath()
+
+ val configuration = dokkaConfiguration {
+ passes {
+ pass {
+ moduleName = "example"
+ analysisPlatform = "js"
+ targets = listOf("js")
+ sourceRoots = listOf("jsMain", "commonMain", "jvmAndJsSecondCommonMain").map {
+ "$testDataDir/$it/kotlin"
+ }
+ includes = listOf("$includesDir/include2.md")
+ }
+ pass {
+ moduleName = "example"
+ analysisPlatform = "jvm"
+ targets = listOf("jvm")
+ sourceRoots = listOf("jvmMain", "commonMain", "jvmAndJsSecondCommonMain").map {
+ "$testDataDir/$it/kotlin"
+ }
+ includes = listOf("$includesDir/include1.md")
+ }
+ }
+ }
+
+ testFromData(configuration) {
+ documentablesMergingStage = {
+ Assertions.assertEquals(2, it.documentation.size)
+ Assertions.assertEquals(2, it.packages.size)
+ Assertions.assertEquals(1, it.packages.first().documentation.size)
+ Assertions.assertEquals(1, it.packages.last().documentation.size)
+ }
+ }
+
+ }
+
+ @Test
+ fun `Sources multiplatform class documentation`() {
+
+ val testDataDir = getTestDataDir("linkable/sources").toAbsolutePath()
+
+ val configuration = dokkaConfiguration {
+ passes {
+ pass {
+ moduleName = "example"
+ analysisPlatform = "js"
+ targets = listOf("js")
+ sourceRoots = listOf("$testDataDir/jsMain/kotlin")
+ sourceLinks = listOf(
+ SourceLinkDefinitionImpl(
+ path = "jsMain/kotlin",
+ url = "https://github.com/user/repo/tree/master/src/jsMain/kotlin",
+ lineSuffix = "#L"
+ )
+ )
+ }
+ pass {
+ moduleName = "example"
+ analysisPlatform = "jvm"
+ targets = listOf("jvm")
+ sourceRoots = listOf("$testDataDir/jvmMain/kotlin")
+ sourceLinks = listOf(
+ SourceLinkDefinitionImpl(
+ path = "jvmMain/kotlin",
+ url = "https://github.com/user/repo/tree/master/src/jvmMain/kotlin",
+ lineSuffix = "#L"
+ )
+ )
+ }
+ }
+ }
+
+ testFromData(configuration) {
+ renderingStage = { rootPageNode, dokkaContext ->
+ val newRoot = SourceLinksTransformer(dokkaContext).invoke(rootPageNode)
+
+ val moduleChildren = newRoot.children
+ Assertions.assertEquals(1, moduleChildren.size)
+ val packageChildren = moduleChildren.first().children
+ Assertions.assertEquals(2, packageChildren.size)
+ packageChildren.forEach {
+ val name = it.name.substringBefore("Class")
+ val crl = it.safeAs<ClasslikePageNode>()?.content?.safeAs<ContentGroup>()?.children?.last()
+ ?.safeAs<PlatformHintedContent>()?.children?.singleOrNull().safeAs<ContentResolvedLink>()
+ Assertions.assertEquals(
+ "https://github.com/user/repo/tree/master/src/${name.toLowerCase()}Main/kotlin/${name}Class.kt#L3",
+ crl?.address
+ )
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `Samples multiplatform documentation`() {
+
+ val testDataDir = getTestDataDir("linkable/samples").toAbsolutePath()
+
+ val configuration = dokkaConfiguration {
+ passes {
+ pass {
+ moduleName = "example"
+ analysisPlatform = "js"
+ targets = listOf("js")
+ sourceRoots = listOf("$testDataDir/jsMain/kotlin")
+ samples = listOf("$testDataDir/jsMain/resources/Samples.kt")
+ }
+ pass {
+ moduleName = "example"
+ analysisPlatform = "jvm"
+ targets = listOf("jvm")
+ sourceRoots = listOf("$testDataDir/jvmMain/kotlin")
+ samples = listOf("$testDataDir/jvmMain/resources/Samples.kt")
+ }
+ }
+ }
+
+ testFromData(configuration) {
+ renderingStage = { rootPageNode, dokkaContext ->
+ val newRoot = DefaultSamplesTransformer(dokkaContext).invoke(rootPageNode)
+
+ val moduleChildren = newRoot.children
+ Assertions.assertEquals(1, moduleChildren.size)
+ val packageChildren = moduleChildren.first().children
+ Assertions.assertEquals(2, packageChildren.size)
+ packageChildren.forEach {
+ val name = it.name.substringBefore("Class")
+ val classChildren = it.children
+ Assertions.assertEquals(2, classChildren.size)
+ val function = classChildren.find { it.name == "printWithExclamation" }
+ val text = function.safeAs<MemberPageNode>()?.content?.safeAs<ContentGroup>()?.children?.last()
+ ?.safeAs<PlatformHintedContent>()?.children?.singleOrNull()
+ ?.safeAs<ContentGroup>()?.children?.singleOrNull()?.safeAs<ContentGroup>()?.children?.last()
+ ?.safeAs<ContentGroup>()?.children?.singleOrNull()
+ ?.safeAs<ContentCode>()?.children?.singleOrNull()?.safeAs<ContentText>()?.text
+ Assertions.assertEquals(
+ "${name}Class().printWithExclamation(\"Hi, $name\")",
+ text
+ )
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/plugins/base/src/test/resources/linkable/includes/include1.md b/plugins/base/src/test/resources/linkable/includes/include1.md
new file mode 100644
index 00000000..03d9037d
--- /dev/null
+++ b/plugins/base/src/test/resources/linkable/includes/include1.md
@@ -0,0 +1,7 @@
+# Module example
+
+This is JVM documentation for module example
+
+# Package example
+
+This is JVM documentation for package example \ No newline at end of file
diff --git a/plugins/base/src/test/resources/linkable/includes/include2.md b/plugins/base/src/test/resources/linkable/includes/include2.md
new file mode 100644
index 00000000..1574003d
--- /dev/null
+++ b/plugins/base/src/test/resources/linkable/includes/include2.md
@@ -0,0 +1,7 @@
+# Module example
+
+This is JS documentation for module example
+
+# Package greeteer
+
+This is JS documentation for package greeteer \ No newline at end of file
diff --git a/plugins/base/src/test/resources/linkable/samples/jsMain/kotlin/JsClass.kt b/plugins/base/src/test/resources/linkable/samples/jsMain/kotlin/JsClass.kt
new file mode 100644
index 00000000..b61ce704
--- /dev/null
+++ b/plugins/base/src/test/resources/linkable/samples/jsMain/kotlin/JsClass.kt
@@ -0,0 +1,9 @@
+package p2
+
+class JsClass {
+
+ /**
+ * @sample samples.SamplesJs.exampleUsage
+ */
+ fun printWithExclamation(msg: String) = println(msg + "!")
+} \ No newline at end of file
diff --git a/plugins/base/src/test/resources/linkable/samples/jsMain/resources/Samples.kt b/plugins/base/src/test/resources/linkable/samples/jsMain/resources/Samples.kt
new file mode 100644
index 00000000..55be0ad8
--- /dev/null
+++ b/plugins/base/src/test/resources/linkable/samples/jsMain/resources/Samples.kt
@@ -0,0 +1,10 @@
+package samples
+
+import p2.JsClass
+
+class SamplesJs {
+
+ fun exampleUsage() {
+ JsClass().printWithExclamation("Hi, Js")
+ }
+} \ No newline at end of file
diff --git a/plugins/base/src/test/resources/linkable/samples/jvmMain/kotlin/JvmClass.kt b/plugins/base/src/test/resources/linkable/samples/jvmMain/kotlin/JvmClass.kt
new file mode 100644
index 00000000..960184e6
--- /dev/null
+++ b/plugins/base/src/test/resources/linkable/samples/jvmMain/kotlin/JvmClass.kt
@@ -0,0 +1,9 @@
+package p2
+
+class JvmClass {
+
+ /**
+ * @sample samples.SamplesJvm.exampleUsage
+ */
+ fun printWithExclamation(msg: String) = println(msg + "!")
+} \ No newline at end of file
diff --git a/plugins/base/src/test/resources/linkable/samples/jvmMain/resources/Samples.kt b/plugins/base/src/test/resources/linkable/samples/jvmMain/resources/Samples.kt
new file mode 100644
index 00000000..69418fa9
--- /dev/null
+++ b/plugins/base/src/test/resources/linkable/samples/jvmMain/resources/Samples.kt
@@ -0,0 +1,10 @@
+package samples
+
+import p2.JvmClass
+
+class SamplesJvm {
+
+ fun exampleUsage() {
+ JvmClass().printWithExclamation("Hi, Jvm")
+ }
+} \ No newline at end of file
diff --git a/plugins/base/src/test/resources/linkable/sources/jsMain/kotlin/JsClass.kt b/plugins/base/src/test/resources/linkable/sources/jsMain/kotlin/JsClass.kt
new file mode 100644
index 00000000..00dd009b
--- /dev/null
+++ b/plugins/base/src/test/resources/linkable/sources/jsMain/kotlin/JsClass.kt
@@ -0,0 +1,3 @@
+package p1
+
+class JsClass \ No newline at end of file
diff --git a/plugins/base/src/test/resources/linkable/sources/jvmMain/kotlin/JvmClass.kt b/plugins/base/src/test/resources/linkable/sources/jvmMain/kotlin/JvmClass.kt
new file mode 100644
index 00000000..2113c589
--- /dev/null
+++ b/plugins/base/src/test/resources/linkable/sources/jvmMain/kotlin/JvmClass.kt
@@ -0,0 +1,3 @@
+package p1
+
+class JvmClass \ No newline at end of file