diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/ModuleFragmentToExternalName.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/ModuleFragmentToExternalName.kt index a3c58afa14b..575add95c32 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/ModuleFragmentToExternalName.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/ModuleFragmentToExternalName.kt @@ -15,9 +15,24 @@ import org.jetbrains.kotlin.ir.declarations.path private const val EXPORTER_FILE_POSTFIX = ".export" class ModuleFragmentToExternalName(private val jsOutputNamesMapping: Map) { + private val externalNameToItsFile = hashMapOf() + fun getExternalNameFor(file: IrFile, granularity: JsGenerationGranularity): String { assert(granularity == JsGenerationGranularity.PER_FILE) { "This method should be used only for PER_FILE granularity" } - return file.module.getJsOutputName().getExternalModuleNameForPerFile(file) + return file.module.getJsOutputName().getExternalModuleNameForPerFile(file).also { + val alreadyReservedBy = externalNameToItsFile.putIfAbsent(it.lowercase(), file) + + if (alreadyReservedBy != null && alreadyReservedBy != file) { + error( + """ + |There are two files in module '${file.module.name}' that have the similar package and file names. + | - Package "${file.packageFqName.asString()}" and path "${file.path}" + | - Package "${alreadyReservedBy.packageFqName.asString()}" and path "${alreadyReservedBy.path}" + |Note, that if the difference is only in letter cases, it also could lead to a clash of the compiled artifacts + """.trimMargin() + ) + } + } } fun getExternalNameForExporterFile(file: IrFile, granularity: JsGenerationGranularity): String { diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/Kotlin2JsGradlePluginIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/Kotlin2JsGradlePluginIT.kt index 9e5252999c6..f9dd9cc23c6 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/Kotlin2JsGradlePluginIT.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/Kotlin2JsGradlePluginIT.kt @@ -359,6 +359,27 @@ class Kotlin2JsIrGradlePluginIT : AbstractKotlin2JsGradlePluginIT(true) { } } + @DisplayName("per-file with the declarations validation") + @GradleTest + fun testPerFileProjectWithResultFilesClash(gradleVersion: GradleVersion) { + project("kotlin-js-invalid-per-file-project", gradleVersion) { + buildAndFail("compileDevelopmentExecutableKotlinJs") { + assertTasksFailed(":compileDevelopmentExecutableKotlinJs") + assertOutputContains(""" + |There are two files in module '' that have the similar package and file names. + | - Package "com.example" and + """.trimMargin()) + } + buildAndFail("compileProductionExecutableKotlinJs") { + assertTasksFailed(":compileProductionExecutableKotlinJs") + assertOutputContains(""" + |There are two files in module '' that have the similar package and file names. + | - Package "com.example" and path + """.trimMargin()) + } + } + } + @DisplayName("fully qualified names can be used in the sourcemap") @GradleTest fun testKotlinJsSourceMapGenerateFqNames(gradleVersion: GradleVersion) { diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-invalid-per-file-project/build.gradle.kts b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-invalid-per-file-project/build.gradle.kts new file mode 100644 index 00000000000..26eb11c7cee --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-invalid-per-file-project/build.gradle.kts @@ -0,0 +1,16 @@ +plugins { + kotlin("js") +} + +repositories { + mavenLocal() + mavenCentral() +} + +kotlin { + js(IR) { + useEsModules() + binaries.executable() + nodejs() + } +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-invalid-per-file-project/custom/main.txt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-invalid-per-file-project/custom/main.txt new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-invalid-per-file-project/gradle.properties b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-invalid-per-file-project/gradle.properties new file mode 100644 index 00000000000..abb12f59146 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-invalid-per-file-project/gradle.properties @@ -0,0 +1,8 @@ +kotlin.tests.individualTaskReports=true +kotlin.js.ir.output.granularity=per-file +kotlin.incremental=false +kotlin.incremental.js=false +kotlin.incremental.js.ir=false +kotlin.incremental.js.klib=false +kotlin.incremental.multiplatform=false +kotlin.incremental.useClasspathSnapshot=false \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-invalid-per-file-project/src/main/kotlin/base1/Base.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-invalid-per-file-project/src/main/kotlin/base1/Base.kt new file mode 100644 index 00000000000..2d3a770d00f --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-invalid-per-file-project/src/main/kotlin/base1/Base.kt @@ -0,0 +1,16 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package com.example + +@JsExport +fun best(): Int { + return 42 +} + +@JsExport +fun simpleBest(): Int { + return 73 +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-invalid-per-file-project/src/main/kotlin/base2/base.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-invalid-per-file-project/src/main/kotlin/base2/base.kt new file mode 100644 index 00000000000..d6904f24f79 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-invalid-per-file-project/src/main/kotlin/base2/base.kt @@ -0,0 +1,9 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package com.example + +@JsExport +public val foo = 44 diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/CompilerFlags.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/CompilerFlags.kt index cea307e856c..2c6172db31a 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/CompilerFlags.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/CompilerFlags.kt @@ -24,6 +24,7 @@ internal const val MINIMIZED_MEMBER_NAMES = "-Xir-minimized-member-names" internal const val KLIB_MODULE_NAME = "-Xir-module-name" +internal const val PER_FILE = "-Xir-per-file" internal const val PER_MODULE = "-Xir-per-module" internal const val PER_MODULE_OUTPUT_NAME = "-Xir-per-module-output-name" diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/OutputGranularity.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/OutputGranularity.kt index a26d054ee1d..937daab7b7f 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/OutputGranularity.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/OutputGranularity.kt @@ -7,7 +7,8 @@ package org.jetbrains.kotlin.gradle.targets.js.ir enum class KotlinJsIrOutputGranularity { WHOLE_PROGRAM, - PER_MODULE; + PER_MODULE, + PER_FILE; companion object { fun byArgument(argument: String): KotlinJsIrOutputGranularity? = @@ -17,6 +18,9 @@ enum class KotlinJsIrOutputGranularity { } fun KotlinJsIrOutputGranularity.toCompilerArgument(): String { - val perModule = this == KotlinJsIrOutputGranularity.PER_MODULE - return "$PER_MODULE=$perModule" + return when (this) { + KotlinJsIrOutputGranularity.PER_FILE -> PER_FILE + KotlinJsIrOutputGranularity.PER_MODULE -> PER_MODULE + KotlinJsIrOutputGranularity.WHOLE_PROGRAM -> "" + } } \ No newline at end of file