From b2f416dbbef3291b2b64acc08dc4ae4ad1f64d04 Mon Sep 17 00:00:00 2001 From: Ilya Goncharov Date: Tue, 4 Oct 2022 14:41:24 +0200 Subject: [PATCH] [Gradle, JS] Remove requireNodeModules [Gradle, JS] Remove 4 major webpack [Gradle, JS] Remove webpack 4 related dependencies [Gradle, JS] How want to see npm dependency [JS IR] Object factory and remove forceResolve [Gradle, JS] Object factory to package json producer [Gradle, JS] Only npm dependencies as required [Gradle, JS] NodeJsRootPlugin applied once [Gradle, JS] NodeJsRootPlugin applied once #2 [Gradle, JS] Divide task providers from NodeJsRootExtension [Gradle, JS] Remove redundant [Gradle, JS] Fix #1 [Gradle, JS] Fix #2 [Gradle, JS] Fix #3 [Gradle, JS] Fix #4 [Gradle, JS] Fix #5 [Gradle, JS] Fix tests [Gradle, JS] No objectFactory [Gradle, JS] Remove archive compat and fs compat [Gradle, JS] Build service kotlin npm resolution manager [JS] Build servicify Build servicify Build servicify Fix internal Tasks requirements as build service ~ try to move from build service [JS] no nested [JS] Configuration resolver inside input files Revert "[JS] Configuration resolver inside input files" This reverts commit 48536e011a8a341e9cb62fbf8e03a09f2ba115ba. [JS] nested but only configuration [JS] packageJsonHandlers serializable [JS] packageJsonProducer as part of task inputs [JS] packageJsonProducer ~1 ~2 ~3 ~4 ~5 ~6 ~7 ~8 ~9 ~10 Remove comments [Gradle, JS] Remove comments [Gradle, JS] Fix internals and test [Gradle, JS] Input package json files for root package json [Gradle, JS] Kotlin npm install always tracked externally [Gradle, JS] Rename resolution items [Gradle, JS] Build service with new scheme --- .../kotlin/gradle/JsConfigurationCacheIT.kt | 4 +- .../kotlin/gradle/Kotlin2JsGradlePluginIT.kt | 3 +- .../gradle/targets/js/NpmPackages.kt | 5 - .../internal/ConfigurationPhaseAware.kt | 1 + .../gradle/plugin/PropertiesProvider.kt | 13 - .../mpp/DefaultKotlinDependencyHandler.kt | 10 +- .../targets/js/KotlinGradleNpmPackage.kt | 6 +- .../gradle/targets/js/KotlinJsTarget.kt | 13 +- .../gradle/targets/js/NpmPackageVersion.kt | 6 +- .../kotlin/gradle/targets/js/NpmVersions.kt | 15 +- .../targets/js/RequiredKotlinJsDependency.kt | 3 +- .../gradle/targets/js/ir/KotlinBrowserJsIr.kt | 53 +-- .../targets/js/ir/KotlinJsIrSubTarget.kt | 7 +- .../gradle/targets/js/ir/KotlinJsIrTarget.kt | 2 + .../gradle/targets/js/ir/KotlinNodeJsIr.kt | 11 +- .../gradle/targets/js/nodejs/NodeJsEnv.kt | 6 +- .../gradle/targets/js/nodejs/NodeJsExec.kt | 13 +- .../targets/js/nodejs/NodeJsRootExtension.kt | 131 +++----- .../targets/js/nodejs/NodeJsRootPlugin.kt | 119 ++++++- .../targets/js/nodejs/NodeJsSetupTask.kt | 11 +- .../targets/js/nodejs/TasksRequirements.kt | 2 +- .../js/npm/AbstractNodeModulesCache.kt | 4 +- .../js/npm/CompositeNodeModulesCache.kt | 54 ++- .../targets/js/npm/GradleNodeModuleBuilder.kt | 8 +- .../targets/js/npm/GradleNodeModulesCache.kt | 72 +++- .../js/npm/KotlinNpmResolutionManager.kt | 312 ++++++----------- .../kotlin/gradle/targets/js/npm/NpmApi.kt | 47 +-- .../gradle/targets/js/npm/NpmDependency.kt | 88 ++--- .../targets/js/npm/NpmDependencyExtension.kt | 6 +- .../gradle/targets/js/npm/NpmProject.kt | 3 +- .../targets/js/npm/NpmResolverPlugin.kt | 27 +- .../targets/js/npm/PublicPackageJsonTask.kt | 82 ++--- .../targets/js/npm/RequiresNpmDependencies.kt | 1 - .../KotlinCompilationNpmResolution.kt | 37 -- .../resolved/KotlinProjectNpmResolution.kt | 29 +- .../npm/resolved/KotlinRootNpmResolution.kt | 70 +++- .../PreparedKotlinCompilationNpmResolution.kt | 20 ++ .../js/npm/resolver/GradleDependencies.kt | 42 +++ .../KotlinCompilationNpmResolution.kt | 201 +++++++++++ .../resolver/KotlinCompilationNpmResolver.kt | 316 +++--------------- .../npm/resolver/KotlinProjectNpmResolver.kt | 72 ++-- .../js/npm/resolver/KotlinRootNpmResolver.kt | 307 ++--------------- .../KotlinRootNpmResolverStateHolder.kt | 38 --- .../MayBeUpToDatePackageJsonTasksRegistry.kt | 31 -- .../npm/resolver/PackageJsonProducerInputs.kt | 33 ++ .../js/npm/tasks/KotlinNpmCachesSetup.kt | 17 +- .../js/npm/tasks/KotlinNpmInstallTask.kt | 76 ++--- .../js/npm/tasks/KotlinPackageJsonTask.kt | 135 ++++---- .../js/npm/tasks/RootPackageJsonTask.kt | 45 ++- .../targets/js/subtargets/KotlinBrowserJs.kt | 44 +-- .../js/subtargets/KotlinJsSubTarget.kt | 14 +- .../gradle/targets/js/testing/KotlinJsTest.kt | 16 +- .../js/testing/KotlinJsTestFramework.kt | 3 - .../targets/js/testing/karma/KotlinKarma.kt | 38 +-- .../targets/js/testing/mocha/KotlinMocha.kt | 4 +- .../js/typescript/TypeScriptValidationTask.kt | 18 +- .../targets/js/webpack/KotlinWebpack.kt | 13 +- .../targets/js/webpack/KotlinWebpackConfig.kt | 40 +-- .../kotlin/gradle/targets/js/yarn/Yarn.kt | 33 +- .../gradle/targets/js/yarn/YarnBasics.kt | 117 +------ .../targets/js/yarn/YarnEntryRegistry.kt | 50 --- .../kotlin/gradle/targets/js/yarn/YarnEnv.kt | 4 +- .../YarnImportedPackagesVersionResolver.kt | 4 +- .../kotlin/gradle/targets/js/yarn/YarnLock.kt | 154 --------- .../gradle/targets/js/yarn/YarnPlugin.kt | 22 +- .../targets/js/yarn/YarnRootExtension.kt | 31 +- .../gradle/targets/js/yarn/YarnSetupTask.kt | 10 +- .../gradle/targets/js/yarn/YarnWorkspaces.kt | 27 +- .../kotlin/gradle/utils/compatibility.kt | 39 --- 69 files changed, 1291 insertions(+), 1997 deletions(-) delete mode 100644 libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/resolved/KotlinCompilationNpmResolution.kt create mode 100644 libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/resolved/PreparedKotlinCompilationNpmResolution.kt create mode 100644 libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/resolver/GradleDependencies.kt create mode 100644 libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/resolver/KotlinCompilationNpmResolution.kt delete mode 100644 libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/resolver/KotlinRootNpmResolverStateHolder.kt delete mode 100644 libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/resolver/MayBeUpToDatePackageJsonTasksRegistry.kt create mode 100644 libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/resolver/PackageJsonProducerInputs.kt delete mode 100644 libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/yarn/YarnLock.kt diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/JsConfigurationCacheIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/JsConfigurationCacheIT.kt index 1f909db8397..e5a200d8f25 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/JsConfigurationCacheIT.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/JsConfigurationCacheIT.kt @@ -86,7 +86,6 @@ abstract class AbstractJsConfigurationCacheIT(protected val irBackend: Boolean) ":packageJson", ":publicPackageJson", ":rootPackageJson", - ":kotlinNpmInstall", ":compileKotlinJs", ":nodeTest", ) + if (irBackend) listOf(":compileProductionExecutableKotlinJs") else emptyList() @@ -109,7 +108,6 @@ abstract class AbstractJsConfigurationCacheIT(protected val irBackend: Boolean) ":packageJson", ":publicPackageJson", ":rootPackageJson", - ":kotlinNpmInstall", ":compileKotlinJs", ":nodeTest", ) + if (irBackend) listOf(":compileProductionExecutableKotlinJs") else emptyList() @@ -125,7 +123,7 @@ abstract class AbstractJsConfigurationCacheIT(protected val irBackend: Boolean) assertSimpleConfigurationCacheScenarioWorks( "assemble", "kotlinStoreYarnLock", buildOptions = defaultBuildOptions, - executedTaskNames = listOf(":kotlinNpmInstall") + executedTaskNames = listOf(":rootPackageJson") ) } } 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 35f565f7afb..7e0855d412b 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 @@ -1575,6 +1575,7 @@ abstract class AbstractKotlin2JsGradlePluginIT(protected val irBackend: Boolean) project("kotlin-js-browser-project", gradleVersion) { buildGradleKts.modify { originalScript -> buildString { + append("import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootPlugin.Companion.kotlinNpmResolutionManager") append(originalScript) append( """ @@ -1583,7 +1584,7 @@ abstract class AbstractKotlin2JsGradlePluginIT(protected val irBackend: Boolean) | val nodejs = the() | tasks.named("kotlinNpmInstall") { | doFirst { - | nodejs.npmResolutionManager.state = + | project.rootProject.kotlinNpmResolutionManager.get().state = | org.jetbrains.kotlin.gradle.targets.js.npm.KotlinNpmResolutionManager.ResolutionState.Error(GradleException("someSpecialException")) | } | } diff --git a/libraries/tools/kotlin-gradle-plugin-npm-versions-codegen/src/main/kotlin/org/jetbrains/kotlin/generators/gradle/targets/js/NpmPackages.kt b/libraries/tools/kotlin-gradle-plugin-npm-versions-codegen/src/main/kotlin/org/jetbrains/kotlin/generators/gradle/targets/js/NpmPackages.kt index f06e016b759..0c859a38381 100644 --- a/libraries/tools/kotlin-gradle-plugin-npm-versions-codegen/src/main/kotlin/org/jetbrains/kotlin/generators/gradle/targets/js/NpmPackages.kt +++ b/libraries/tools/kotlin-gradle-plugin-npm-versions-codegen/src/main/kotlin/org/jetbrains/kotlin/generators/gradle/targets/js/NpmPackages.kt @@ -7,14 +7,9 @@ package org.jetbrains.kotlin.generators.gradle.targets.js val npmPackages = listOf( NpmPackage("webpack"), - NpmPackage("webpack", "4.46.0", "webpack4"), NpmPackage("webpack-cli"), - NpmPackage("webpack-cli", "3.3.12", "webpackCli3"), - NpmPackage("webpack-bundle-analyzer"), NpmPackage("webpack-dev-server"), - NpmPackage("webpack-dev-server", "3.11.2", "webpackDevServer3"), NpmPackage("source-map-loader"), - NpmPackage("source-map-loader", "1.1.0", "sourceMapLoader1"), NpmPackage("source-map-support"), NpmPackage("css-loader"), NpmPackage("style-loader"), diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/internal/ConfigurationPhaseAware.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/internal/ConfigurationPhaseAware.kt index 95b78234f0a..9cfb214d26d 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/internal/ConfigurationPhaseAware.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/internal/ConfigurationPhaseAware.kt @@ -5,6 +5,7 @@ package org.jetbrains.kotlin.gradle.internal +import java.io.Serializable import kotlin.reflect.KProperty abstract class ConfigurationPhaseAware { diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/PropertiesProvider.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/PropertiesProvider.kt index 79e13decec5..69f7801578a 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/PropertiesProvider.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/PropertiesProvider.kt @@ -456,19 +456,6 @@ internal class PropertiesProvider private constructor(private val project: Proje val publishJsCompilerAttribute: Boolean get() = this.booleanProperty("$jsCompilerProperty.publish.attribute") ?: true - /** - * Use Webpack 4 for compatibility - */ - val webpackMajorVersion: WebpackMajorVersion - get() = this.property(WebpackMajorVersion.webpackMajorVersion)?.let { WebpackMajorVersion.byArgument(it) } - ?.also { version -> - if (!WebpackMajorVersion.webpackVersionWarning && version != WebpackMajorVersion.DEFAULT) { - WebpackMajorVersion.webpackVersionWarning = true - project.logger.warn(WebpackMajorVersion.warningMessage) - } - } - ?: WebpackMajorVersion.DEFAULT - /** * Use Kotlin/JS backend compiler type */ diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/DefaultKotlinDependencyHandler.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/DefaultKotlinDependencyHandler.kt index 0b45ccd7b89..fdce51652d6 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/DefaultKotlinDependencyHandler.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/DefaultKotlinDependencyHandler.kt @@ -106,7 +106,7 @@ class DefaultKotlinDependencyHandler( version: String, ): NpmDependency = NpmDependency( - project = project, + objectFactory = project.objects, name = name, version = version, ) @@ -134,7 +134,7 @@ class DefaultKotlinDependencyHandler( version: String ): NpmDependency = NpmDependency( - project = project, + objectFactory = project.objects, name = name, version = version, scope = NpmDependency.Scope.DEV @@ -161,7 +161,7 @@ class DefaultKotlinDependencyHandler( version: String, ): NpmDependency = NpmDependency( - project = project, + objectFactory = project.objects, name = name, version = version, scope = NpmDependency.Scope.OPTIONAL, @@ -190,7 +190,7 @@ class DefaultKotlinDependencyHandler( version: String ): NpmDependency = NpmDependency( - project = project, + objectFactory = project.objects, name = name, version = version, scope = NpmDependency.Scope.PEER @@ -202,7 +202,7 @@ class DefaultKotlinDependencyHandler( scope: NpmDependency.Scope, ): NpmDependency = directoryNpmDependency( - project = project, + objectFactory = project.objects, name = name, directory = directory, scope = scope, diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/KotlinGradleNpmPackage.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/KotlinGradleNpmPackage.kt index fdca551328a..2ac79ca8330 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/KotlinGradleNpmPackage.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/KotlinGradleNpmPackage.kt @@ -7,9 +7,9 @@ package org.jetbrains.kotlin.gradle.targets.js import org.gradle.api.Project import org.gradle.api.artifacts.Dependency -import org.jetbrains.kotlin.gradle.targets.js.npm.NpmDependency +import java.io.Serializable -data class KotlinGradleNpmPackage(val simpleModuleName: String) : RequiredKotlinJsDependency { - override fun createDependency(project: Project, scope: NpmDependency.Scope): Dependency = +data class KotlinGradleNpmPackage(val simpleModuleName: String) : Serializable { + fun createDependency(project: Project): Dependency = project.dependencies.create("org.jetbrains.kotlin:kotlin-$simpleModuleName") } \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/KotlinJsTarget.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/KotlinJsTarget.kt index 4b75416c679..8b2738914ed 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/KotlinJsTarget.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/KotlinJsTarget.kt @@ -16,9 +16,14 @@ import org.jetbrains.kotlin.gradle.plugin.AbstractKotlinTargetConfigurator.Compa import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation.Companion.MAIN_COMPILATION_NAME import org.jetbrains.kotlin.gradle.plugin.KotlinJsCompilerType.LEGACY import org.jetbrains.kotlin.gradle.plugin.mpp.* -import org.jetbrains.kotlin.gradle.targets.js.dsl.* +import org.jetbrains.kotlin.gradle.plugin.mpp.PRIMARY_SINGLE_COMPONENT_NAME +import org.jetbrains.kotlin.gradle.targets.js.dsl.KotlinJsBrowserDsl +import org.jetbrains.kotlin.gradle.targets.js.dsl.KotlinJsNodeDsl +import org.jetbrains.kotlin.gradle.targets.js.dsl.KotlinJsSubTargetContainerDsl +import org.jetbrains.kotlin.gradle.targets.js.dsl.KotlinJsTargetDsl import org.jetbrains.kotlin.gradle.targets.js.ir.KotlinJsBinaryContainer import org.jetbrains.kotlin.gradle.targets.js.ir.KotlinJsIrTarget +import org.jetbrains.kotlin.gradle.targets.js.npm.NpmResolverPlugin import org.jetbrains.kotlin.gradle.targets.js.subtargets.KotlinBrowserJs import org.jetbrains.kotlin.gradle.targets.js.subtargets.KotlinNodeJs import org.jetbrains.kotlin.gradle.tasks.locateOrRegisterTask @@ -147,8 +152,13 @@ constructor( private val propertiesProvider = PropertiesProvider(project) + private val commonLazy by lazy { + NpmResolverPlugin.apply(project) + } + //Browser private val browserLazyDelegate = lazy { + commonLazy project.objects.newInstance(KotlinBrowserJs::class.java, this).also { it.configure() @@ -177,6 +187,7 @@ constructor( //node.js private val nodejsLazyDelegate = lazy { + commonLazy project.objects.newInstance(KotlinNodeJs::class.java, this).also { it.configure() diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/NpmPackageVersion.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/NpmPackageVersion.kt index 7d1968fa44f..7dd3d1928b3 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/NpmPackageVersion.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/NpmPackageVersion.kt @@ -5,11 +5,11 @@ package org.jetbrains.kotlin.gradle.targets.js -import org.gradle.api.Project import org.gradle.api.artifacts.Dependency +import org.gradle.api.model.ObjectFactory import org.jetbrains.kotlin.gradle.targets.js.npm.NpmDependency data class NpmPackageVersion(val name: String, var version: String) : RequiredKotlinJsDependency { - override fun createDependency(project: Project, scope: NpmDependency.Scope): Dependency = - NpmDependency(project, name, version, scope) + override fun createDependency(objectFactory: ObjectFactory, scope: NpmDependency.Scope): Dependency = + NpmDependency(objectFactory, scope, name, version) } \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/NpmVersions.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/NpmVersions.kt index 2aa68d5b9a6..39c377ddd24 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/NpmVersions.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/NpmVersions.kt @@ -12,24 +12,19 @@ import java.io.Serializable */ // DO NOT MODIFY DIRECTLY! Use org.jetbrains.kotlin.generators.gradle.targets.js.MainKt class NpmVersions : Serializable { - val webpack4 = NpmPackageVersion("webpack", "4.46.0") - val webpackCli3 = NpmPackageVersion("webpack-cli", "3.3.12") - val webpackDevServer3 = NpmPackageVersion("webpack-dev-server", "3.11.2") - val sourceMapLoader1 = NpmPackageVersion("source-map-loader", "1.1.0") val webpack = NpmPackageVersion("webpack", "5.74.0") val webpackCli = NpmPackageVersion("webpack-cli", "4.10.0") - val webpackBundleAnalyzer = NpmPackageVersion("webpack-bundle-analyzer", "4.5.0") - val webpackDevServer = NpmPackageVersion("webpack-dev-server", "4.9.3") + val webpackDevServer = NpmPackageVersion("webpack-dev-server", "4.11.1") val sourceMapLoader = NpmPackageVersion("source-map-loader", "4.0.0") val sourceMapSupport = NpmPackageVersion("source-map-support", "0.5.21") val cssLoader = NpmPackageVersion("css-loader", "6.7.1") val styleLoader = NpmPackageVersion("style-loader", "3.3.1") - val sassLoader = NpmPackageVersion("sass-loader", "13.0.2") - val sass = NpmPackageVersion("sass", "1.54.3") + val sassLoader = NpmPackageVersion("sass-loader", "13.1.0") + val sass = NpmPackageVersion("sass", "1.55.0") val toStringLoader = NpmPackageVersion("to-string-loader", "1.2.0") val miniCssExtractPlugin = NpmPackageVersion("mini-css-extract-plugin", "2.6.1") val mocha = NpmPackageVersion("mocha", "10.0.0") - val karma = NpmPackageVersion("karma", "6.4.0") + val karma = NpmPackageVersion("karma", "6.4.1") val karmaChromeLauncher = NpmPackageVersion("karma-chrome-launcher", "3.1.1") val karmaPhantomjsLauncher = NpmPackageVersion("karma-phantomjs-launcher", "1.0.4") val karmaFirefoxLauncher = NpmPackageVersion("karma-firefox-launcher", "2.1.2") @@ -41,7 +36,7 @@ class NpmVersions : Serializable { val karmaCoverage = NpmPackageVersion("karma-coverage", "2.2.0") val karmaSourcemapLoader = NpmPackageVersion("karma-sourcemap-loader", "0.3.8") val formatUtil = NpmPackageVersion("format-util", "1.0.5") - val typescript = NpmPackageVersion("typescript", "4.7.4") + val typescript = NpmPackageVersion("typescript", "4.8.4") val kotlinJsTestRunner = KotlinGradleNpmPackage("test-js-runner") } \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/RequiredKotlinJsDependency.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/RequiredKotlinJsDependency.kt index c2306a7336f..2c54c85d532 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/RequiredKotlinJsDependency.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/RequiredKotlinJsDependency.kt @@ -7,9 +7,10 @@ package org.jetbrains.kotlin.gradle.targets.js import org.gradle.api.Project import org.gradle.api.artifacts.Dependency +import org.gradle.api.model.ObjectFactory import org.jetbrains.kotlin.gradle.targets.js.npm.NpmDependency import java.io.Serializable interface RequiredKotlinJsDependency : Serializable { - fun createDependency(project: Project, scope: NpmDependency.Scope = NpmDependency.Scope.DEV): Dependency + fun createDependency(objectFactory: ObjectFactory, scope: NpmDependency.Scope = NpmDependency.Scope.DEV): Dependency } \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/KotlinBrowserJsIr.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/KotlinBrowserJsIr.kt index e6f1c797c17..664e02446e2 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/KotlinBrowserJsIr.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/KotlinBrowserJsIr.kt @@ -13,7 +13,6 @@ import org.gradle.api.tasks.Copy import org.gradle.language.base.plugins.LifecycleBasePlugin import org.jetbrains.kotlin.gradle.dsl.KotlinJsDce import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType -import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJsCompilation import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.util.archivesName import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.util.distsDirectory @@ -23,14 +22,13 @@ import org.jetbrains.kotlin.gradle.targets.js.dsl.ExperimentalDceDsl import org.jetbrains.kotlin.gradle.targets.js.dsl.KotlinJsBinaryMode import org.jetbrains.kotlin.gradle.targets.js.dsl.KotlinJsBrowserDsl import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootExtension -import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootPlugin +import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootPlugin.Companion.kotlinNodeJsExtension import org.jetbrains.kotlin.gradle.targets.js.testing.KotlinJsTest import org.jetbrains.kotlin.gradle.targets.js.testing.karma.KotlinKarma import org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpack import org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpackConfig import org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpackConfig.Mode import org.jetbrains.kotlin.gradle.targets.js.webpack.WebpackDevtool -import org.jetbrains.kotlin.gradle.targets.js.webpack.WebpackMajorVersion.Companion.choose import org.jetbrains.kotlin.gradle.tasks.dependsOn import org.jetbrains.kotlin.gradle.utils.doNotTrackStateCompat import org.jetbrains.kotlin.gradle.utils.newFileProperty @@ -42,15 +40,11 @@ abstract class KotlinBrowserJsIr @Inject constructor(target: KotlinJsIrTarget) : KotlinJsIrSubTarget(target, "browser"), KotlinJsBrowserDsl { - private val nodeJs = NodeJsRootPlugin.apply(project.rootProject) + private val nodeJs = project.rootProject.kotlinNodeJsExtension private val webpackTaskConfigurations: MutableList> = mutableListOf() private val runTaskConfigurations: MutableList> = mutableListOf() - private val propertiesProvider = PropertiesProvider(project) - private val webpackMajorVersion - get() = propertiesProvider.webpackMajorVersion - override val testTaskDescription: String get() = "Run all ${target.name} tests inside browser using karma and webpack" @@ -110,9 +104,6 @@ abstract class KotlinBrowserJsIr @Inject constructor(target: KotlinJsIrTarget) : override fun configureRun( compilation: KotlinJsIrCompilation ) { - val project = compilation.target.project - val nodeJs = NodeJsRootPlugin.apply(project.rootProject) - val commonRunTask = registerSubTargetTask(disambiguateCamelCased(RUN_TASK_NAME)) {} compilation.binaries @@ -134,10 +125,7 @@ abstract class KotlinBrowserJsIr @Inject constructor(target: KotlinJsIrTarget) : task.dependsOn(binary.linkSyncTask) task.outputDirectory.convention(distsDirectory).finalizeValueOnRead() - webpackMajorVersion.choose( - { task.args.add(0, "serve") }, - { task.bin = "webpack-dev-server/bin/webpack-dev-server.js" } - )() + task.args.add(0, "serve") task.description = "start ${mode.name.toLowerCaseAsciiOnly()} webpack dev server" val openValue = if (compilation.platformType == KotlinPlatformType.wasm) { @@ -156,29 +144,19 @@ abstract class KotlinBrowserJsIr @Inject constructor(target: KotlinJsIrTarget) : true } - webpackMajorVersion.choose( - { - task.devServer = KotlinWebpackConfig.DevServer( - open = openValue, - static = mutableListOf( - "./kotlin", - compilation.output.resourcesDir.canonicalPath, - ), - client = KotlinWebpackConfig.DevServer.Client( - KotlinWebpackConfig.DevServer.Client.Overlay( - errors = true, - warnings = false - ) - ) + task.devServer = KotlinWebpackConfig.DevServer( + open = openValue, + static = mutableListOf( + "./kotlin", + compilation.output.resourcesDir.canonicalPath + ), + client = KotlinWebpackConfig.DevServer.Client( + KotlinWebpackConfig.DevServer.Client.Overlay( + errors = true, + warnings = false ) - }, - { - task.devServer = KotlinWebpackConfig.DevServer( - open = true, - contentBase = mutableListOf(compilation.output.resourcesDir.canonicalPath) - ) - } - )() + ) + ) task.doNotTrackStateCompat("Tracked by external webpack tool") @@ -205,7 +183,6 @@ abstract class KotlinBrowserJsIr @Inject constructor(target: KotlinJsIrTarget) : compilation: KotlinJsIrCompilation ) { val project = compilation.target.project - val nodeJs = NodeJsRootPlugin.apply(project.rootProject) val processResourcesTask = target.project.tasks.named(compilation.processResourcesTaskName) diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/KotlinJsIrSubTarget.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/KotlinJsIrSubTarget.kt index d411fa452e5..65d348b0464 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/KotlinJsIrSubTarget.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/KotlinJsIrSubTarget.kt @@ -12,7 +12,9 @@ import org.gradle.api.plugins.ExtensionAware import org.gradle.api.tasks.Copy import org.gradle.api.tasks.TaskProvider import org.gradle.language.base.plugins.LifecycleBasePlugin -import org.jetbrains.kotlin.gradle.plugin.* +import org.jetbrains.kotlin.gradle.plugin.AbstractKotlinTargetConfigurator +import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation +import org.jetbrains.kotlin.gradle.plugin.KotlinTargetWithTests import org.jetbrains.kotlin.gradle.plugin.mpp.isMain import org.jetbrains.kotlin.gradle.plugin.whenEvaluated import org.jetbrains.kotlin.gradle.targets.js.KotlinJsPlatformTestRun @@ -20,7 +22,6 @@ import org.jetbrains.kotlin.gradle.targets.js.dsl.Distribution import org.jetbrains.kotlin.gradle.targets.js.dsl.ExperimentalDistributionDsl import org.jetbrains.kotlin.gradle.targets.js.dsl.KotlinJsBinaryMode import org.jetbrains.kotlin.gradle.targets.js.dsl.KotlinJsSubTargetDsl -import org.jetbrains.kotlin.gradle.targets.js.npm.NpmResolverPlugin import org.jetbrains.kotlin.gradle.targets.js.npm.npmProject import org.jetbrains.kotlin.gradle.targets.js.testing.KotlinJsTest import org.jetbrains.kotlin.gradle.tasks.dependsOn @@ -52,8 +53,6 @@ abstract class KotlinJsIrSubTarget( } internal fun configure() { - NpmResolverPlugin.apply(project) - target.compilations.all { val npmProject = it.npmProject it.kotlinOptions { diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/KotlinJsIrTarget.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/KotlinJsIrTarget.kt index a8340456098..435baff6ad0 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/KotlinJsIrTarget.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/KotlinJsIrTarget.kt @@ -21,6 +21,7 @@ import org.jetbrains.kotlin.gradle.targets.js.KotlinJsReportAggregatingTestRun import org.jetbrains.kotlin.gradle.targets.js.KotlinJsTarget import org.jetbrains.kotlin.gradle.targets.js.binaryen.BinaryenExec import org.jetbrains.kotlin.gradle.targets.js.dsl.* +import org.jetbrains.kotlin.gradle.targets.js.npm.NpmResolverPlugin import org.jetbrains.kotlin.gradle.targets.js.npm.npmProject import org.jetbrains.kotlin.gradle.targets.js.typescript.TypeScriptValidationTask import org.jetbrains.kotlin.gradle.tasks.locateOrRegisterTask @@ -110,6 +111,7 @@ constructor( } private val commonLazy by lazy { + NpmResolverPlugin.apply(project) compilations.all { compilation -> compilation.binaries .withType(JsIrBinary::class.java) diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/KotlinNodeJsIr.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/KotlinNodeJsIr.kt index 51c63d1e86b..cef08c5035b 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/KotlinNodeJsIr.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/KotlinNodeJsIr.kt @@ -9,7 +9,7 @@ import org.gradle.api.Action import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType import org.jetbrains.kotlin.gradle.targets.js.dsl.KotlinJsNodeDsl import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsExec -import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootPlugin +import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootPlugin.Companion.kotlinNodeJsExtension import org.jetbrains.kotlin.gradle.targets.js.testing.KotlinJsTest import org.jetbrains.kotlin.gradle.targets.js.testing.KotlinWasmNode import org.jetbrains.kotlin.gradle.tasks.dependsOn @@ -21,7 +21,8 @@ abstract class KotlinNodeJsIr @Inject constructor(target: KotlinJsIrTarget) : KotlinJsIrSubTargetBase(target, "node"), KotlinJsNodeDsl { - private val nodeJs = NodeJsRootPlugin.apply(project.rootProject) + private val nodeJs = project.rootProject.kotlinNodeJsExtension + private val nodeJsTaskProviders = project.rootProject.kotlinNodeJsExtension override val testTaskDescription: String get() = "Run all ${target.name} tests inside nodejs using the builtin test framework" @@ -51,9 +52,9 @@ abstract class KotlinNodeJsIr @Inject constructor(target: KotlinJsIrTarget) : override fun configureTestDependencies(test: KotlinJsTest) { test.dependsOn( - nodeJs.npmInstallTaskProvider, - nodeJs.storeYarnLockTaskProvider, - nodeJs.nodeJsSetupTaskProvider + nodeJsTaskProviders.npmInstallTaskProvider, + nodeJsTaskProviders.storeYarnLockTaskProvider, + nodeJsTaskProviders.nodeJsSetupTaskProvider ) } diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/nodejs/NodeJsEnv.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/nodejs/NodeJsEnv.kt index da2887dd32d..dde42b7ec3f 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/nodejs/NodeJsEnv.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/nodejs/NodeJsEnv.kt @@ -1,10 +1,12 @@ package org.jetbrains.kotlin.gradle.targets.js.nodejs +import org.jetbrains.kotlin.gradle.targets.js.npm.NpmApi import org.jetbrains.kotlin.gradle.tasks.internal.CleanableStore import java.io.File data class NodeJsEnv( val cleanableStore: CleanableStore, + val rootPackageDir: File, val nodeDir: File, val nodeBinDir: File, val nodeExecutable: String, @@ -12,7 +14,9 @@ data class NodeJsEnv( val platformName: String, val architectureName: String, val ivyDependency: String, - val downloadBaseUrl: String + val downloadBaseUrl: String, + + val packageManager: NpmApi, ) { val isWindows: Boolean get() = platformName == "win" diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/nodejs/NodeJsExec.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/nodejs/NodeJsExec.kt index 5c596bdcbe3..bfba48e5c27 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/nodejs/NodeJsExec.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/nodejs/NodeJsExec.kt @@ -12,6 +12,7 @@ import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJsCompilation import org.jetbrains.kotlin.gradle.targets.js.RequiredKotlinJsDependency import org.jetbrains.kotlin.gradle.targets.js.addWasmExperimentalArguments +import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootPlugin.Companion.kotlinNodeJsExtension import org.jetbrains.kotlin.gradle.targets.js.npm.RequiresNpmDependencies import org.jetbrains.kotlin.gradle.targets.js.npm.npmProject import org.jetbrains.kotlin.gradle.tasks.registerTask @@ -48,10 +49,6 @@ constructor( @NormalizeLineEndings val inputFileProperty: RegularFileProperty = project.newFileProperty() - @get:Internal - override val nodeModulesRequired: Boolean - get() = true - @get:Internal override val requiredNpmDependencies: Set by lazy { mutableSetOf().also { @@ -92,7 +89,9 @@ constructor( ): TaskProvider { val target = compilation.target val project = target.project - val nodeJs = NodeJsRootPlugin.apply(project.rootProject) + NodeJsRootPlugin.apply(project.rootProject) + val nodeJs = project.rootProject.kotlinNodeJsExtension + val nodeJsTaskProviders = project.rootProject.kotlinNodeJsExtension val npmProject = compilation.npmProject return project.registerTask( @@ -103,8 +102,8 @@ constructor( it.executable = nodeJs.requireConfigured().nodeExecutable it.workingDir = npmProject.dir it.dependsOn( - nodeJs.npmInstallTaskProvider, - nodeJs.storeYarnLockTaskProvider, + nodeJsTaskProviders.npmInstallTaskProvider, + nodeJsTaskProviders.storeYarnLockTaskProvider, ) it.dependsOn(compilation.compileKotlinTaskProvider) if (compilation.platformType == KotlinPlatformType.wasm) { diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/nodejs/NodeJsRootExtension.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/nodejs/NodeJsRootExtension.kt index 3bcc22d9f0f..92fd3e3552a 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/nodejs/NodeJsRootExtension.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/nodejs/NodeJsRootExtension.kt @@ -12,29 +12,45 @@ import org.jetbrains.kotlin.gradle.internal.ConfigurationPhaseAware import org.jetbrains.kotlin.gradle.logging.kotlinInfo import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider import org.jetbrains.kotlin.gradle.targets.js.NpmVersions -import org.jetbrains.kotlin.gradle.targets.js.npm.KotlinNpmResolutionManager import org.jetbrains.kotlin.gradle.targets.js.npm.NpmApi +import org.jetbrains.kotlin.gradle.targets.js.npm.resolver.KotlinRootNpmResolver import org.jetbrains.kotlin.gradle.targets.js.npm.resolver.PACKAGE_JSON_UMBRELLA_TASK_NAME import org.jetbrains.kotlin.gradle.targets.js.npm.tasks.KotlinNpmCachesSetup import org.jetbrains.kotlin.gradle.targets.js.npm.tasks.KotlinNpmInstallTask import org.jetbrains.kotlin.gradle.targets.js.npm.tasks.RootPackageJsonTask import org.jetbrains.kotlin.gradle.targets.js.yarn.Yarn import org.jetbrains.kotlin.gradle.targets.js.yarn.YarnLockCopyTask -import org.jetbrains.kotlin.gradle.targets.js.yarn.YarnLockCopyTask.Companion.RESTORE_YARN_LOCK_NAME -import org.jetbrains.kotlin.gradle.targets.js.yarn.YarnLockCopyTask.Companion.STORE_YARN_LOCK_NAME import org.jetbrains.kotlin.gradle.tasks.internal.CleanableStore import java.io.File -import java.io.Serializable -open class NodeJsRootExtension(@Transient val rootProject: Project) : ConfigurationPhaseAware(), Serializable { +open class NodeJsRootExtension( + val project: Project +) : ConfigurationPhaseAware() { + init { - check(rootProject.rootProject == rootProject) + check(project.rootProject == project) + + val projectProperties = PropertiesProvider(project) + + if (projectProperties.errorJsGenerateExternals != null) { + project.logger.warn( + """ + | + |========== + |Please note, Dukat integration in Gradle plugin does not work now, it was removed. + |We rethink how we can integrate properly. + |========== + | + """.trimMargin() + ) + } } - private val logger = rootProject.logger + val rootProjectDir + get() = project.projectDir - private val gradleHome = rootProject.gradle.gradleUserHomeDir.also { - logger.kotlinInfo("Storing cached files in $it") + private val gradleHome = project.gradle.gradleUserHomeDir.also { + project.logger.kotlinInfo("Storing cached files in $it") } var installationDir by Property(gradleHome.resolve("nodejs")) @@ -52,70 +68,12 @@ open class NodeJsRootExtension(@Transient val rootProject: Project) : Configurat var packageManager: NpmApi by Property(Yarn()) - @Transient - private val projectProperties = PropertiesProvider(rootProject) + val taskRequirements: TasksRequirements + get() = resolver.tasksRequirements - private val errorGenerateExternals = run { - if (projectProperties.errorJsGenerateExternals != null) { - logger.warn( - """ - | - |========== - |Please note, Dukat integration in Gradle plugin does not work now, it was removed. - |We rethink how we can integrate properly. - |========== - | - """.trimMargin() - ) - } - } + lateinit var resolver: KotlinRootNpmResolver - val taskRequirements: TasksRequirements = TasksRequirements() - - val nodeJsSetupTaskProvider: TaskProvider - get() = rootProject.tasks.withType(NodeJsSetupTask::class.java).named(NodeJsSetupTask.NAME) - - @Suppress( - "UNNECESSARY_SAFE_CALL", - "SAFE_CALL_WILL_CHANGE_NULLABILITY" - ) // TODO: investigate this warning; fixing it breaks integration tests. - val npmInstallTaskProvider: TaskProvider? - get() = rootProject?.tasks?.withType(KotlinNpmInstallTask::class.java)?.named(KotlinNpmInstallTask.NAME) - - val packageJsonUmbrellaTaskProvider: TaskProvider - get() = rootProject.tasks.named(PACKAGE_JSON_UMBRELLA_TASK_NAME) - - @Suppress( - "UNNECESSARY_SAFE_CALL", - "SAFE_CALL_WILL_CHANGE_NULLABILITY" - ) // TODO: investigate this warning; fixing it breaks integration tests. - val rootPackageJsonTaskProvider: TaskProvider? - get() = rootProject?.tasks?.withType(RootPackageJsonTask::class.java)?.named(RootPackageJsonTask.NAME) - - @Suppress( - "UNNECESSARY_SAFE_CALL", - "SAFE_CALL_WILL_CHANGE_NULLABILITY" - ) // TODO: investigate this warning; fixing it breaks integration tests. - val npmCachesSetupTaskProvider: TaskProvider? - get() = rootProject?.tasks?.withType(KotlinNpmCachesSetup::class.java)?.named(KotlinNpmCachesSetup.NAME) - - @Suppress( - "UNNECESSARY_SAFE_CALL", - "SAFE_CALL_WILL_CHANGE_NULLABILITY" - ) // TODO: investigate this warning; fixing it breaks integration tests. - val storeYarnLockTaskProvider: TaskProvider? - get() = rootProject?.tasks?.withType(YarnLockCopyTask::class.java)?.named(STORE_YARN_LOCK_NAME) - - @Suppress( - "UNNECESSARY_SAFE_CALL", - "SAFE_CALL_WILL_CHANGE_NULLABILITY" - ) // TODO: investigate this warning; fixing it breaks integration tests. - val restoreYarnLockTaskProvider: TaskProvider? - get() = rootProject?.tasks?.withType(YarnLockCopyTask::class.java)?.named(RESTORE_YARN_LOCK_NAME) - - val rootPackageDir: File by lazy { - rootProject.buildDir.resolve("js") - } + val rootPackageDir: File = project.buildDir.resolve("js") val projectPackagesDir: File get() = rootPackageDir.resolve("packages") @@ -123,6 +81,8 @@ open class NodeJsRootExtension(@Transient val rootProject: Project) : Configurat val nodeModulesGradleCacheDir: File get() = rootPackageDir.resolve("packages_imported") + val versions = NpmVersions() + override fun finalizeConfiguration(): NodeJsEnv { val platformHelper = PlatformHelper val platform = platformHelper.osName @@ -146,28 +106,35 @@ open class NodeJsRootExtension(@Transient val rootProject: Project) : Configurat return NodeJsEnv( cleanableStore = cleanableStore, + rootPackageDir = rootPackageDir, nodeDir = nodeDir, nodeBinDir = nodeBinDir, nodeExecutable = getExecutable("node", nodeCommand, "exe"), platformName = platform, architectureName = architecture, ivyDependency = getIvyDependency(), - downloadBaseUrl = nodeDownloadBaseUrl + downloadBaseUrl = nodeDownloadBaseUrl, + packageManager = packageManager ) } - internal fun executeSetup() { - if (download) { - val nodeJsSetupTask = nodeJsSetupTaskProvider.get() - nodeJsSetupTask.actions.forEach { - it.execute(nodeJsSetupTask) - } - } - } + val nodeJsSetupTaskProvider: TaskProvider + get() = project.tasks.withType(NodeJsSetupTask::class.java).named(NodeJsSetupTask.NAME) - val versions = NpmVersions() + val npmInstallTaskProvider: TaskProvider + get() = project.tasks.withType(KotlinNpmInstallTask::class.java).named(KotlinNpmInstallTask.NAME) - val npmResolutionManager = KotlinNpmResolutionManager(this) + val rootPackageJsonTaskProvider: TaskProvider + get() = project.tasks.withType(RootPackageJsonTask::class.java).named(RootPackageJsonTask.NAME) + + val packageJsonUmbrellaTaskProvider: TaskProvider + get() = project.tasks.named(PACKAGE_JSON_UMBRELLA_TASK_NAME) + + val npmCachesSetupTaskProvider: TaskProvider + get() = project.tasks.withType(KotlinNpmCachesSetup::class.java).named(KotlinNpmCachesSetup.NAME) + + val storeYarnLockTaskProvider: TaskProvider + get() = project.tasks.withType(YarnLockCopyTask::class.java).named(YarnLockCopyTask.STORE_YARN_LOCK_NAME) companion object { const val EXTENSION_NAME: String = "kotlinNodeJs" diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/nodejs/NodeJsRootPlugin.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/nodejs/NodeJsRootPlugin.kt index 28690a836cd..90c916dbe5d 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/nodejs/NodeJsRootPlugin.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/nodejs/NodeJsRootPlugin.kt @@ -9,18 +9,25 @@ import org.gradle.api.Plugin import org.gradle.api.Project import org.gradle.api.Task import org.gradle.api.plugins.BasePlugin -import org.jetbrains.kotlin.gradle.utils.markResolvable +import org.gradle.api.provider.Provider import org.jetbrains.kotlin.gradle.targets.js.MultiplePluginDeclarationDetector -import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootExtension.Companion.EXTENSION_NAME +import org.jetbrains.kotlin.gradle.targets.js.npm.CompositeNodeModulesCache +import org.jetbrains.kotlin.gradle.targets.js.npm.GradleNodeModulesCache +import org.jetbrains.kotlin.gradle.targets.js.npm.KotlinNpmResolutionManager +import org.jetbrains.kotlin.gradle.targets.js.npm.resolver.KotlinRootNpmResolver import org.jetbrains.kotlin.gradle.targets.js.npm.resolver.PACKAGE_JSON_UMBRELLA_TASK_NAME -import org.jetbrains.kotlin.gradle.targets.js.npm.tasks.KotlinNpmInstallTask import org.jetbrains.kotlin.gradle.targets.js.npm.tasks.KotlinNpmCachesSetup +import org.jetbrains.kotlin.gradle.targets.js.npm.tasks.KotlinNpmInstallTask import org.jetbrains.kotlin.gradle.targets.js.yarn.YarnPlugin import org.jetbrains.kotlin.gradle.tasks.CleanDataTask import org.jetbrains.kotlin.gradle.tasks.registerTask +import org.jetbrains.kotlin.gradle.utils.castIsolatedKotlinPluginClassLoaderAware +import org.jetbrains.kotlin.gradle.utils.doNotTrackStateCompat +import org.jetbrains.kotlin.gradle.utils.markResolvable +import org.jetbrains.kotlin.gradle.utils.providerWithLazyConvention open class NodeJsRootPlugin : Plugin { - override fun apply(project: Project): Unit = project.run { + override fun apply(project: Project) { MultiplePluginDeclarationDetector.detect(project) project.plugins.apply(BasePlugin::class.java) @@ -29,35 +36,93 @@ open class NodeJsRootPlugin : Plugin { "NodeJsRootPlugin can be applied only to root project" } - val settings = this.extensions.create(EXTENSION_NAME, NodeJsRootExtension::class.java, this) + val nodeJs = project.extensions.create( + NodeJsRootExtension.EXTENSION_NAME, + NodeJsRootExtension::class.java, + project + ) - val setupTask = registerTask(NodeJsSetupTask.NAME) { + val setupTask = project.registerTask(NodeJsSetupTask.NAME) { it.group = TASKS_GROUP_NAME it.description = "Download and install a local node/npm version" - it.configuration = provider { - this.project.configurations.detachedConfiguration(this.project.dependencies.create(it.ivyDependency)) + it.configuration = project.provider { + project.configurations.detachedConfiguration(project.dependencies.create(it.ivyDependency)) .markResolvable() .also { conf -> conf.isTransitive = false } } } - val setupFileHasherTask = registerTask(KotlinNpmCachesSetup.NAME) { + val gradleNodeModulesProvider: Provider = GradleNodeModulesCache.registerIfAbsent( + project, + project.projectDir, + nodeJs.nodeModulesGradleCacheDir + ) + + val compositeNodeModulesProvider: Provider = CompositeNodeModulesCache.registerIfAbsent( + project, + project.projectDir, + nodeJs.nodeModulesGradleCacheDir + ) + + val setupFileHasherTask = project.registerTask(KotlinNpmCachesSetup.NAME) { it.description = "Setup file hasher for caches" + + it.gradleNodeModules.set(gradleNodeModulesProvider) + it.compositeNodeModules.set(compositeNodeModulesProvider) } - registerTask(KotlinNpmInstallTask.NAME) { + val npmInstall = project.registerTask(KotlinNpmInstallTask.NAME) { it.dependsOn(setupTask) it.dependsOn(setupFileHasherTask) it.group = TASKS_GROUP_NAME it.description = "Find, download and link NPM dependencies and projects" + + it.onlyIf { task -> + task as KotlinNpmInstallTask + task.preparedFiles.all { file -> + file.exists() + } + } + + it.doNotTrackStateCompat("NPM package manager track by its own") } - registerTask(PACKAGE_JSON_UMBRELLA_TASK_NAME) + project.registerTask(PACKAGE_JSON_UMBRELLA_TASK_NAME) YarnPlugin.apply(project) - tasks.register("node" + CleanDataTask.NAME_SUFFIX, CleanDataTask::class.java) { - it.cleanableStoreProvider = provider { settings.requireConfigured().cleanableStore } + nodeJs.resolver = KotlinRootNpmResolver( + project.name, + project.version.toString(), + TasksRequirements(), + nodeJs.versions, + nodeJs.projectPackagesDir, + nodeJs.rootProjectDir, + ) + + val objectFactory = project.objects + + val npmResolutionManager = KotlinNpmResolutionManager.registerIfAbsent( + project, + objectFactory.providerWithLazyConvention { + nodeJs.resolver.close() + }, + objectFactory.providerWithLazyConvention { + nodeJs.resolver.compilations.associate { compilation -> + "${compilation.project.path}:${compilation.disambiguatedName}" to compilation.packageJsonHandlers + } + }, + gradleNodeModulesProvider, + compositeNodeModulesProvider + ) + + npmInstall.configure { + it.npmResolutionManager.set(npmResolutionManager) + it.npmResolutionManager.disallowChanges() + } + + project.tasks.register("node" + CleanDataTask.NAME_SUFFIX, CleanDataTask::class.java) { + it.cleanableStoreProvider = project.provider { nodeJs.requireConfigured().cleanableStore } it.group = TASKS_GROUP_NAME it.description = "Clean unused local node version" } @@ -69,7 +134,33 @@ open class NodeJsRootPlugin : Plugin { fun apply(rootProject: Project): NodeJsRootExtension { check(rootProject == rootProject.rootProject) rootProject.plugins.apply(NodeJsRootPlugin::class.java) - return rootProject.extensions.getByName(EXTENSION_NAME) as NodeJsRootExtension + return rootProject.extensions.getByName(NodeJsRootExtension.EXTENSION_NAME) as NodeJsRootExtension } + + val Project.kotlinNodeJsExtension: NodeJsRootExtension + get() = extensions.getByName(NodeJsRootExtension.EXTENSION_NAME).castIsolatedKotlinPluginClassLoaderAware() + + private val Project.gradleNodeModules + get() = GradleNodeModulesCache.registerIfAbsent( + this, + null, + null + ) + + private val Project.compositeNodeModules + get() = CompositeNodeModulesCache.registerIfAbsent( + this, + null, + null + ) + + val Project.kotlinNpmResolutionManager + get() = KotlinNpmResolutionManager.registerIfAbsent( + this, + null, + null, + gradleNodeModules, + compositeNodeModules + ) } } diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/nodejs/NodeJsSetupTask.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/nodejs/NodeJsSetupTask.kt index c55fcccbafe..933ca6180bb 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/nodejs/NodeJsSetupTask.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/nodejs/NodeJsSetupTask.kt @@ -2,6 +2,7 @@ package org.jetbrains.kotlin.gradle.targets.js.nodejs import org.gradle.api.DefaultTask import org.gradle.api.artifacts.Configuration +import org.gradle.api.file.ArchiveOperations import org.gradle.api.file.FileSystemOperations import org.gradle.api.model.ObjectFactory import org.gradle.api.provider.Provider @@ -9,8 +10,8 @@ import org.gradle.api.tasks.* import org.gradle.internal.hash.FileHasher import org.jetbrains.kotlin.gradle.logging.kotlinInfo import org.jetbrains.kotlin.gradle.plugin.statistics.KotlinBuildStatsService +import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootPlugin.Companion.kotlinNodeJsExtension import org.jetbrains.kotlin.gradle.targets.js.extractWithUpToDate -import org.jetbrains.kotlin.gradle.utils.ArchiveOperationsCompat import org.jetbrains.kotlin.statistics.metrics.NumericalMetrics import java.io.File import java.net.URI @@ -21,12 +22,13 @@ import javax.inject.Inject abstract class NodeJsSetupTask : DefaultTask() { @Transient - private val settings = NodeJsRootPlugin.apply(project.rootProject) + private val settings = project.rootProject.kotlinNodeJsExtension private val env by lazy { settings.requireConfigured() } private val shouldDownload = settings.download - private val archiveOperations = ArchiveOperationsCompat(project) + @get:Inject + abstract internal val archiveOperations: ArchiveOperations @get:Inject internal open val fileHasher: FileHasher @@ -36,8 +38,7 @@ abstract class NodeJsSetupTask : DefaultTask() { internal abstract val objects: ObjectFactory @get:Inject - internal open val fs: FileSystemOperations - get() = error("Should be injected") + abstract internal val fs: FileSystemOperations val ivyDependency: String @Input get() = env.ivyDependency diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/nodejs/TasksRequirements.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/nodejs/TasksRequirements.kt index 531a2a5bdee..f189ba221d7 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/nodejs/TasksRequirements.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/nodejs/TasksRequirements.kt @@ -30,7 +30,7 @@ class TasksRequirements : Serializable { val requiredNpmDependencies = requirements .asSequence() - .map { it.createDependency(task.compilation.target.project) } + .map { it.createDependency(task.compilation.target.project.objects) } .filterIsInstance() .toMutableSet() diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/AbstractNodeModulesCache.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/AbstractNodeModulesCache.kt index 8858af66ffb..0826bf86c34 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/AbstractNodeModulesCache.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/AbstractNodeModulesCache.kt @@ -16,8 +16,8 @@ import java.io.File /** * Cache for storing already created [GradleNodeModule]s */ -internal abstract class AbstractNodeModulesCache : AutoCloseable, BuildService { - internal interface Parameters : BuildServiceParameters { +abstract class AbstractNodeModulesCache : AutoCloseable, BuildService { + interface Parameters : BuildServiceParameters { val rootProjectDir: DirectoryProperty val cacheDir: DirectoryProperty } diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/CompositeNodeModulesCache.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/CompositeNodeModulesCache.kt index 11cf03bfb0a..44e94752bef 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/CompositeNodeModulesCache.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/CompositeNodeModulesCache.kt @@ -5,14 +5,24 @@ package org.jetbrains.kotlin.gradle.targets.js.npm +import org.gradle.api.Project +import org.gradle.api.Task +import org.gradle.api.provider.Property import org.gradle.api.provider.Provider -import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootExtension +import org.gradle.api.tasks.Internal +import org.jetbrains.kotlin.gradle.tasks.withType +import org.jetbrains.kotlin.gradle.utils.SingleActionPerProject import java.io.File +internal interface UsesCompositeNodeModulesCache : Task { + @get:Internal + val compositeNodeModules: Property +} + /** * Cache for storing already created [GradleNodeModule]s */ -internal abstract class CompositeNodeModulesCache : AbstractNodeModulesCache() { +abstract class CompositeNodeModulesCache : AbstractNodeModulesCache() { override val type: String get() = "composite" @@ -24,4 +34,44 @@ internal abstract class CompositeNodeModulesCache : AbstractNodeModulesCache() { val module = CompositeNodeModuleBuilder(file, parameters.cacheDir.get().asFile) return module.rebuild() } + + companion object { + private val serviceClass = CompositeNodeModulesCache::class.java + private val serviceName = serviceClass.name + + private fun registerIfAbsentImpl( + project: Project, + rootProjectDir: File?, + cacheDir: File? + ): Provider { + project.gradle.sharedServices.registrations.findByName(serviceName)?.let { + @Suppress("UNCHECKED_CAST") + return it.service as Provider + } + + val message = { + "Build service Composite Node Modules should be already registered" + } + + requireNotNull(rootProjectDir, message) + requireNotNull(cacheDir, message) + + return project.gradle.sharedServices.registerIfAbsent(serviceName, serviceClass) { + it.parameters.rootProjectDir.set(rootProjectDir) + it.parameters.cacheDir.set(cacheDir) + } + } + + fun registerIfAbsent( + project: Project, + rootProjectDir: File?, + cacheDir: File? + ) = registerIfAbsentImpl(project, rootProjectDir, cacheDir).also { serviceProvider -> + SingleActionPerProject.run(project, UsesCompositeNodeModulesCache::class.java.name) { + project.tasks.withType().configureEach { task -> + task.usesService(serviceProvider) + } + } + } + } } \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/GradleNodeModuleBuilder.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/GradleNodeModuleBuilder.kt index e947357eb0a..e6de376c540 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/GradleNodeModuleBuilder.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/GradleNodeModuleBuilder.kt @@ -5,22 +5,22 @@ package org.jetbrains.kotlin.gradle.targets.js.npm +import org.gradle.api.file.ArchiveOperations +import org.gradle.api.file.FileSystemOperations import org.gradle.api.file.FileTree import org.jetbrains.kotlin.gradle.targets.js.HTML import org.jetbrains.kotlin.gradle.targets.js.JS import org.jetbrains.kotlin.gradle.targets.js.JS_MAP import org.jetbrains.kotlin.gradle.targets.js.META_JS import org.jetbrains.kotlin.gradle.targets.js.ir.KLIB_TYPE -import org.jetbrains.kotlin.gradle.utils.ArchiveOperationsCompat -import org.jetbrains.kotlin.gradle.utils.FileSystemOperationsCompat import java.io.File /** * Creates fake NodeJS module directory from given gradle [dependency]. */ internal class GradleNodeModuleBuilder( - val fs: FileSystemOperationsCompat, - val archiveOperations: ArchiveOperationsCompat, + val fs: FileSystemOperations, + val archiveOperations: ArchiveOperations, val moduleName: String, val moduleVersion: String, val srcFiles: Collection, diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/GradleNodeModulesCache.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/GradleNodeModulesCache.kt index 69db10669a8..8f6aafd9fa9 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/GradleNodeModulesCache.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/GradleNodeModulesCache.kt @@ -5,28 +5,40 @@ package org.jetbrains.kotlin.gradle.targets.js.npm +import org.gradle.api.Project +import org.gradle.api.Task +import org.gradle.api.file.ArchiveOperations +import org.gradle.api.file.DirectoryProperty import org.gradle.api.file.FileSystemOperations -import org.jetbrains.kotlin.gradle.utils.ArchiveOperationsCompat -import org.jetbrains.kotlin.gradle.utils.FileSystemOperationsCompat +import org.gradle.api.provider.Property +import org.gradle.api.provider.Provider +import org.gradle.api.tasks.Internal +import org.jetbrains.kotlin.gradle.report.BuildMetricsService +import org.jetbrains.kotlin.gradle.report.UsesBuildMetricsService +import org.jetbrains.kotlin.gradle.report.reportingSettings +import org.jetbrains.kotlin.gradle.tasks.withType +import org.jetbrains.kotlin.gradle.utils.SingleActionPerProject import java.io.File import javax.inject.Inject +internal interface UsesGradleNodeModulesCache : Task { + @get:Internal + val gradleNodeModules: Property +} + /** * Cache for storing already created [GradleNodeModule]s */ -internal abstract class GradleNodeModulesCache : AbstractNodeModulesCache() { +abstract class GradleNodeModulesCache : AbstractNodeModulesCache() { - // TODO: replace by injected service org.gradle.api.file.FileSystemOperations once min support Gradle is 6.2 - // https://github.com/gradle/gradle/commit/d02b9d84c08dba64775fb9581e3280f88d319a21 - @Transient - lateinit var fs: FileSystemOperationsCompat + @get:Inject + abstract val fs: FileSystemOperations override val type: String get() = "gradle" - // TODO: replace by injected service org.gradle.api.file.ArchiveOperations once min supported Gradle is 6.6 - @Transient - lateinit var archiveOperations: ArchiveOperationsCompat + @get:Inject + abstract val archiveOperations: ArchiveOperations override fun buildImportedPackage( name: String, @@ -37,4 +49,44 @@ internal abstract class GradleNodeModulesCache : AbstractNodeModulesCache() { module.visitArtifacts() return module.rebuild() } + + companion object { + private val serviceClass = GradleNodeModulesCache::class.java + private val serviceName = serviceClass.name + + private fun registerIfAbsentImpl( + project: Project, + rootProjectDir: File?, + cacheDir: File? + ): Provider { + project.gradle.sharedServices.registrations.findByName(serviceName)?.let { + @Suppress("UNCHECKED_CAST") + return it.service as Provider + } + + val message = { + "Build service Gradle Node Modules should be already registered" + } + + requireNotNull(rootProjectDir, message) + requireNotNull(cacheDir, message) + + return project.gradle.sharedServices.registerIfAbsent(serviceName, serviceClass) { + it.parameters.rootProjectDir.set(rootProjectDir) + it.parameters.cacheDir.set(cacheDir) + } + } + + fun registerIfAbsent( + project: Project, + rootProjectDir: File?, + cacheDir: File? + ) = registerIfAbsentImpl(project, rootProjectDir, cacheDir).also { serviceProvider -> + SingleActionPerProject.run(project, UsesGradleNodeModulesCache::class.java.name) { + project.tasks.withType().configureEach { task -> + task.usesService(serviceProvider) + } + } + } + } } \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/KotlinNpmResolutionManager.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/KotlinNpmResolutionManager.kt index 050a70d4a51..4fa34c92a64 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/KotlinNpmResolutionManager.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/KotlinNpmResolutionManager.kt @@ -5,169 +5,74 @@ package org.jetbrains.kotlin.gradle.targets.js.npm -import org.gradle.api.Incubating +import org.gradle.api.Project import org.gradle.api.Task -import org.gradle.api.internal.project.ProjectInternal import org.gradle.api.logging.Logger +import org.gradle.api.provider.MapProperty +import org.gradle.api.provider.Property +import org.gradle.api.provider.Provider import org.gradle.api.services.BuildService import org.gradle.api.services.BuildServiceParameters +import org.gradle.api.tasks.Internal import org.gradle.internal.service.ServiceRegistry -import org.jetbrains.kotlin.gradle.internal.isInIdeaSync -import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootExtension -import org.jetbrains.kotlin.gradle.targets.js.npm.resolved.KotlinCompilationNpmResolution -import org.jetbrains.kotlin.gradle.targets.js.npm.resolved.KotlinProjectNpmResolution +import org.jetbrains.kotlin.gradle.targets.js.npm.resolved.Installation import org.jetbrains.kotlin.gradle.targets.js.npm.resolved.KotlinRootNpmResolution -import org.jetbrains.kotlin.gradle.targets.js.npm.resolver.KotlinCompilationNpmResolver -import org.jetbrains.kotlin.gradle.targets.js.npm.resolver.KotlinProjectNpmResolver -import org.jetbrains.kotlin.gradle.targets.js.npm.resolver.KotlinRootNpmResolver -import org.jetbrains.kotlin.gradle.targets.js.npm.tasks.KotlinPackageJsonTask -import org.jetbrains.kotlin.gradle.utils.unavailableValueError -import java.io.File +import org.jetbrains.kotlin.gradle.tasks.withType +import org.jetbrains.kotlin.gradle.utils.SingleActionPerProject -/** - * # NPM resolution state manager - * - * ## Resolving process from Gradle - * - * **configuring**. Global initial state. [NpmResolverPlugin] should be applied for each project - * that requires NPM resolution. When applied, [KotlinProjectNpmResolver] will be created for the - * corresponding project and will subscribe to all js compilations. [NpmResolverPlugin] requires - * kotlin mulitplatform or plaform plugin applied first. - * - * **up-to-date-checked**. This state is compilation local: one compilation may be in up-to-date-checked - * state, while another may be steel in configuring state. New compilations may be added in this - * state, but compilations that are already up-to-date-checked cannot be changed. - * Initiated by calling [KotlinPackageJsonTask.producerInputs] getter (will be called by Gradle). - * [KotlinCompilationNpmResolver] will create and **resolve** aggregated compilation configuration, - * which contains all related compilation configuration and NPM tools configuration. - * NPM tools configuration contains all dependencies that is required for all enabled - * tasks related to this compilation. It is important to resolve this configuration inside particular - * project and not globally. Aggregated configuration will be analyzed for gradle internal dependencies - * (project dependencies), gradle external dependencies and npm dependencies. This collections will - * be treated as `packageJson` task inputs. - * - * **package-json-created**. This state also compilation local. Initiated by executing `packageJson` - * task for particular compilation. If `packageJson` task is up-to-date, this state is reached by - * first calling [KotlinCompilationNpmResolver.getResolutionOrResolveIfForced] which may be called - * by compilation that depends on this compilation. Note that package.json will be executed only for - * required compilations, while other may be missed. - * - * **Prepared**. - * Global final state. Initiated by executing global `rootPackageJson` task. - * - * **Installed**. - * All created package.json files will be gathered and package manager will be executed. - * Package manager will create lock file, that will be parsed for transitive npm dependencies - * that will be added to the root [NpmDependency] objects. `kotlinNpmInstall` task may be up-to-date. - * In this case, installed state will be reached by first call of [installIfNeeded] without executing - * package manager. - * - * Resolution will be used from [NpmDependency] by calling [getNpmDependencyResolvedCompilation]. - * Also resolution will be checked before calling [NpmProject.require] and executing any task - * that requires npm dependencies or node_modules. - * - * User can call [requireInstalled] to get resolution info. - * - * ## Resolving process during Idea import - * - * In this case [forceFullResolve] will be set, and all compilations will be resolved - * even without `packageJson` task execution. - * - * During building gradle project model, all [NpmDependency] will be asked for there files, - * and [getNpmDependencyResolvedCompilation] will be called. In the [forceFullResolve] mode - * project will be fully resolved: all `package.json` files will be created, and package manager - * will be called. We are manually tracking package.json files contents to avoid calling package manager - * if nothing was changes. - * - * Note that in this case resolution process will be performed outside of task execution. - */ -class KotlinNpmResolutionManager(@Transient private val nodeJsSettings: NodeJsRootExtension?) { - private val forceFullResolve: Boolean by lazy { - (nodeJsSettings ?: unavailableValueError("nodeJsSettings")).rootProject.isInIdeaSync +internal interface UsesKotlinNpmResolutionManager : Task { + @get:Internal + val npmResolutionManager: Property +} + +abstract class KotlinNpmResolutionManager : BuildService { + + interface Parameters : BuildServiceParameters { + val resolution: Property + + // pulled up from compilation resolver since it was failing with ClassNotFoundException on deserialization, see KT-49061 + val packageJsonHandlers: MapProperty Unit>> + + val gradleNodeModulesProvider: Property + val compositeNodeModulesProvider: Property } - val resolver = KotlinRootNpmResolver(nodeJsSettings, forceFullResolve) + val resolution + get() = parameters.resolution - internal abstract class KotlinNpmResolutionManagerStateHolder : BuildService { - @Volatile - internal var state: ResolutionState? = null - } - - private val stateHolderProvider = (nodeJsSettings ?: unavailableValueError("nodeJsSettings")) - .rootProject.gradle.sharedServices.registerIfAbsent( - "npm-resolution-manager-state-holder", KotlinNpmResolutionManagerStateHolder::class.java - ) { - }.also { - resolver.declareBuildServiceForRootProjectJsTasks(it) - resolver.declareBuildServiceForAllProjectsJsTasks(it) - } - - private val stateHolder get() = stateHolderProvider.get() - - var state: ResolutionState - get() = stateHolder.state ?: ResolutionState.Configuring(resolver) - set(value) { - stateHolder.state = value - } + @Volatile + var state: ResolutionState = ResolutionState.Configuring(resolution.get()) sealed class ResolutionState { - abstract val npmProjects: List - class Configuring(val resolver: KotlinRootNpmResolver) : ResolutionState() { - override val npmProjects: List - get() = resolver.compilations.map { it.npmProject } - } + class Configuring(val resolution: KotlinRootNpmResolution) : ResolutionState() - open class Prepared(val preparedInstallation: KotlinRootNpmResolver.Installation) : ResolutionState() { - override val npmProjects: List - get() = npmProjectsByProjectResolutions(preparedInstallation.projectResolutions) - } + open class Prepared(val preparedInstallation: Installation) : ResolutionState() - class Installed internal constructor(internal val resolved: KotlinRootNpmResolution) : ResolutionState() { - override val npmProjects: List - get() = npmProjectsByProjectResolutions(resolved.projects) - } + class Installed() : ResolutionState() - class Error(val wrappedException: Throwable) : ResolutionState() { - override val npmProjects: List - get() = emptyList() - } - - companion object { - fun npmProjectsByProjectResolutions( - resolutions: Map - ): List { - return resolutions - .values - .flatMap { it.npmProjects.map { it.npmProject } } - } - } + class Error(val wrappedException: Throwable) : ResolutionState() } - @Incubating - internal fun requireInstalled( - services: ServiceRegistry, - logger: Logger, - reason: String = "" - ) = installIfNeeded(reason = reason, services = services, logger = logger) - - internal fun requireConfiguringState(): KotlinRootNpmResolver = - (this.state as? ResolutionState.Configuring ?: error("NPM Dependencies already resolved and installed")).resolver - internal fun isConfiguringState(): Boolean = this.state is ResolutionState.Configuring - internal fun prepare(logger: Logger) = prepareIfNeeded(requireNotPrepared = true, logger = logger) + internal fun prepare( + logger: Logger, + npmEnvironment: NpmEnvironment, + yarnEnvironment: YarnEnvironment, + ) = prepareIfNeeded(logger = logger, npmEnvironment, yarnEnvironment) internal fun installIfNeeded( - reason: String? = "", args: List = emptyList(), services: ServiceRegistry, - logger: Logger - ): KotlinRootNpmResolution? { - synchronized(stateHolder) { + logger: Logger, + npmEnvironment: NpmEnvironment, + yarnEnvironment: YarnEnvironment, + ): Unit? { + synchronized(this) { if (state is ResolutionState.Installed) { - return (state as ResolutionState.Installed).resolved + return Unit } if (state is ResolutionState.Error) { @@ -175,14 +80,9 @@ class KotlinNpmResolutionManager(@Transient private val nodeJsSettings: NodeJsRo } return try { - val installUpToDate = nodeJsSettings?.npmInstallTaskProvider?.get()?.state?.upToDate ?: false - val forceUpToDate = installUpToDate && !forceFullResolve - - val installation = prepareIfNeeded(requireUpToDateReason = reason, logger = logger) - val resolution = installation - .install(forceUpToDate, args, services, logger) - state = ResolutionState.Installed(resolution) - resolution + val installation: Installation = prepareIfNeeded(logger = logger, npmEnvironment, yarnEnvironment) + installation.install(args, services, logger, npmEnvironment, yarnEnvironment) + state = ResolutionState.Installed() } catch (e: Exception) { state = ResolutionState.Error(e) throw e @@ -190,93 +90,97 @@ class KotlinNpmResolutionManager(@Transient private val nodeJsSettings: NodeJsRo } } - internal val packageJsonFiles: Collection - get() = state.npmProjects.map { it.packageJsonFile } +// internal val packageJsonFiles: Collection +// get() = state.npmProjects.map { it.packageJsonFile } - /** - * @param requireUpToDateReason Check that project already resolved, - * or it is up-to-date but just not closed. Show given message if it is not. - * @param requireNotPrepared Check that project is not prepared - */ private fun prepareIfNeeded( - requireUpToDateReason: String? = null, - requireNotPrepared: Boolean = false, - logger: Logger - ): KotlinRootNpmResolver.Installation { - fun alreadyResolved(installation: KotlinRootNpmResolver.Installation): KotlinRootNpmResolver.Installation { - if (requireNotPrepared) error("Project already prepared") - return installation - } - + logger: Logger, + npmEnvironment: NpmEnvironment, + yarnEnvironment: YarnEnvironment, + ): Installation { val state0 = this.state return when (state0) { is ResolutionState.Prepared -> { - alreadyResolved(state0.preparedInstallation) + state0.preparedInstallation } + is ResolutionState.Configuring -> { - synchronized(stateHolder) { + synchronized(this) { val state1 = this.state when (state1) { - is ResolutionState.Prepared -> alreadyResolved(state1.preparedInstallation) + is ResolutionState.Prepared -> state1.preparedInstallation is ResolutionState.Configuring -> { - val upToDate = nodeJsSettings?.rootPackageJsonTaskProvider?.get()?.state?.upToDate ?: true - if (requireUpToDateReason != null && !upToDate) { - error("NPM dependencies should be resolved $requireUpToDateReason") - } - - state1.resolver.prepareInstallation(logger).also { + state1.resolution.prepareInstallation( + logger, + npmEnvironment, + yarnEnvironment, + this + ).also { this.state = ResolutionState.Prepared(it) } } + is ResolutionState.Installed -> error("Project already installed") is ResolutionState.Error -> throw state1.wrappedException } } } + is ResolutionState.Installed -> error("Project already installed") is ResolutionState.Error -> throw state0.wrappedException } } - internal fun getNpmDependencyResolvedCompilation(npmDependency: NpmDependency): KotlinCompilationNpmResolution? { - val project = npmDependency.project!! - val projectPath = project.path - val services = (project as ProjectInternal).services - val logger = project.logger + companion object { + private val serviceClass = KotlinNpmResolutionManager::class.java + private val serviceName = serviceClass.name - val resolvedProject = - if (forceFullResolve) { - installIfNeeded(reason = null, services = services, logger = logger)?.get(projectPath) ?: return null - } else { - // may return null only during npm resolution - // (it can be called since NpmDependency added to configuration that - // requires resolve to build package.json, in this case we should just skip this call) - val state0 = state - when (state0) { - is ResolutionState.Prepared -> state0.preparedInstallation[projectPath] - is ResolutionState.Configuring -> { - return null - //error("Cannot use NpmDependency before :kotlinNpmInstall task execution") - } - is ResolutionState.Installed -> state0.resolved[projectPath] - is ResolutionState.Error -> { - return null - } - } + private fun registerIfAbsentImpl( + project: Project, + resolution: Provider?, + packageJsonHandlers: Provider Unit>>>?, + gradleNodeModulesProvider: Provider, + compositeNodeModulesProvider: Provider + ): Provider { + project.gradle.sharedServices.registrations.findByName(serviceName)?.let { + @Suppress("UNCHECKED_CAST") + return it.service as Provider } - return resolvedProject.npmProjectsByNpmDependency[npmDependency] ?: error("NPM project resolved without $this") - } + val message = { + "Build service KotlinNpmResolutionManager should be already registered" + } - internal fun checkRequiredDependencies(task: T) - where T : RequiresNpmDependencies, - T : Task { - val targetRequired = resolver.taskRequirements.byTask[task.path]?.toSet() ?: setOf() + requireNotNull(resolution, message) + requireNotNull(packageJsonHandlers, message) - task.requiredNpmDependencies.forEach { - check(it in targetRequired) { - "${it.createDependency(task.project)} required by $task was not found resolved at the time of nodejs package manager call. " + - "This may be caused by changing $task configuration after npm dependencies resolution." + return project.gradle.sharedServices.registerIfAbsent(serviceName, serviceClass) { + it.parameters.resolution.set(resolution) + it.parameters.packageJsonHandlers.set(packageJsonHandlers) + it.parameters.gradleNodeModulesProvider.set(gradleNodeModulesProvider) + it.parameters.compositeNodeModulesProvider.set(compositeNodeModulesProvider) + } + } + + fun registerIfAbsent( + project: Project, + resolution: Provider?, + packageJsonHandlers: Provider Unit>>>?, + gradleNodeModulesProvider: Provider, + compositeNodeModulesProvider: Provider + ) = registerIfAbsentImpl( + project, + resolution, + packageJsonHandlers, + gradleNodeModulesProvider, + compositeNodeModulesProvider + ).also { serviceProvider -> + SingleActionPerProject.run(project, UsesKotlinNpmResolutionManager::class.java.name) { + project.tasks.withType().configureEach { task -> + task.usesService(serviceProvider) + task.usesService(gradleNodeModulesProvider) + task.usesService(compositeNodeModulesProvider) + } } } } diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/NpmApi.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/NpmApi.kt index 143cb0b15b4..30362c1cd09 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/NpmApi.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/NpmApi.kt @@ -5,12 +5,12 @@ package org.jetbrains.kotlin.gradle.targets.js.npm -import org.gradle.api.Project import org.gradle.api.logging.Logger import org.gradle.internal.service.ServiceRegistry -import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootExtension -import org.jetbrains.kotlin.gradle.targets.js.npm.resolved.KotlinCompilationNpmResolution +import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsEnv +import org.jetbrains.kotlin.gradle.targets.js.npm.resolved.PreparedKotlinCompilationNpmResolution import org.jetbrains.kotlin.gradle.targets.js.yarn.YarnEnv +import org.jetbrains.kotlin.gradle.targets.js.yarn.YarnResolution import java.io.File import java.io.Serializable @@ -18,36 +18,26 @@ import java.io.Serializable * NodeJS package manager API */ interface NpmApi : Serializable { - fun setup(project: Project) - fun preparedFiles(nodeJs: NpmEnvironment): Collection fun prepareRootProject( - rootProject: Project?, nodeJs: NpmEnvironment, rootProjectName: String, rootProjectVersion: String, logger: Logger, - subProjects: Collection, + subProjects: Collection, resolutions: Map, - forceFullResolve: Boolean ) fun resolveRootProject( services: ServiceRegistry, logger: Logger, nodeJs: NpmEnvironment, - yarn: YarnEnv, - npmProjects: Collection, + yarn: YarnEnvironment, + npmProjects: Collection, cliArgs: List ) - fun resolveDependency( - npmResolution: KotlinCompilationNpmResolution, - dependency: NpmDependency, - transitive: Boolean - ): Set - companion object { fun resolveOperationDescription(packageManagerTitle: String): String = "Resolving NPM dependencies using $packageManagerTitle" @@ -57,12 +47,29 @@ interface NpmApi : Serializable { data class NpmEnvironment( val rootPackageDir: File, val nodeExecutable: String, - val isWindows: Boolean + val isWindows: Boolean, + val packageManager: NpmApi ) : Serializable -internal val NodeJsRootExtension.asNpmEnvironment +internal val NodeJsEnv.asNpmEnvironment get() = NpmEnvironment( rootPackageDir, - requireConfigured().nodeExecutable, - requireConfigured().isWindows + nodeExecutable, + isWindows, + packageManager ) + +data class YarnEnvironment( + val executable: String, + val standalone: Boolean, + val ignoreScripts: Boolean, + val yarnResolutions: List +) : Serializable + +internal val YarnEnv.asYarnEnvironment + get() = YarnEnvironment( + executable, + standalone, + ignoreScripts, + yarnResolutions + ) \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/NpmDependency.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/NpmDependency.kt index 1ca6e4f5253..dcdad1ff8df 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/NpmDependency.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/NpmDependency.kt @@ -5,31 +5,23 @@ package org.jetbrains.kotlin.gradle.targets.js.npm -import org.gradle.api.Project import org.gradle.api.artifacts.Dependency import org.gradle.api.artifacts.FileCollectionDependency -import org.gradle.api.artifacts.SelfResolvingDependency +import org.gradle.api.artifacts.component.ComponentIdentifier import org.gradle.api.file.FileCollection -import org.gradle.api.internal.artifacts.DependencyResolveContext -import org.gradle.api.internal.artifacts.ResolvableDependency import org.gradle.api.internal.artifacts.dependencies.SelfResolvingDependencyInternal +import org.gradle.api.model.ObjectFactory import org.gradle.api.tasks.TaskDependency -import org.gradle.internal.component.local.model.DefaultLibraryBinaryIdentifier -import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootPlugin import org.jetbrains.kotlin.gradle.targets.js.npm.NpmProject.Companion.PACKAGE_JSON -import org.jetbrains.kotlin.gradle.targets.js.npm.resolved.KotlinCompilationNpmResolution import java.io.File data class NpmDependency( - @Transient - internal val project: Project?, + val objectFactory: ObjectFactory, + val scope: Scope = Scope.NORMAL, private val name: String, private val version: String, - val scope: Scope = Scope.NORMAL, -) : SelfResolvingDependency, - SelfResolvingDependencyInternal, - ResolvableDependency, - FileCollectionDependency { +) : FileCollectionDependency, + SelfResolvingDependencyInternal { enum class Scope { NORMAL, @@ -38,73 +30,33 @@ data class NpmDependency( PEER } + private var reason: String? = null + override fun getGroup(): String? = null + override fun getName() = name + + override fun getVersion() = version + internal var parent: NpmDependency? = null internal val dependencies = mutableSetOf() internal var resolvedVersion: String? = null internal var integrity: String? = null override fun resolve(transitive: Boolean): Set = - resolveProject() - ?.let { - it - .npmProject - .nodeJs - .packageManager - .resolveDependency( - it, - this, - transitive - ) - } - ?: mutableSetOf() + resolve() - override fun resolve(): MutableSet { - val npmPackage = parent?.resolveProject() - ?: resolveProject() - ?: return mutableSetOf() - return mutableSetOf(npmPackage.npmProject.resolve(key)!!) - } + override fun getTargetComponentId(): ComponentIdentifier? = null + override fun resolve(): MutableSet = mutableSetOf() - override fun resolve(context: DependencyResolveContext) { - val npmPackage = resolveProject() - if (npmPackage != null) { - npmPackage.project.files(npmPackage.npmProject.resolve(key)) - dependencies.forEach { - context.add(it) - } - } - } - - // may return null only during npm resolution - // (it can be called since NpmDependency added to configuration that - // requires resolve to build package.json, in this case we should just skip this call) - private fun resolveProject(): KotlinCompilationNpmResolution? { - val nodeJs = NodeJsRootPlugin.apply(project!!.rootProject) - return nodeJs.npmResolutionManager.getNpmDependencyResolvedCompilation(this) - } - - val key: String = name - - override fun toString() = "$key: $version" - - override fun getFiles(): FileCollection = project!!.files(resolve(true)) - - override fun getName() = name - - override fun getVersion() = version + override fun getFiles(): FileCollection = objectFactory.fileCollection() override fun getBuildDependencies(): TaskDependency = TaskDependency { mutableSetOf() } override fun contentEquals(dependency: Dependency) = this == dependency - override fun getTargetComponentId() = DefaultLibraryBinaryIdentifier(project!!.path, key, "npm") - override fun copy(): Dependency = this.copy(name = name) - private var reason: String? = null - override fun because(reason: String?) { this.reason = reason } @@ -113,20 +65,20 @@ data class NpmDependency( } internal fun directoryNpmDependency( - project: Project, + objectFactory: ObjectFactory, + scope: NpmDependency.Scope, name: String, directory: File, - scope: NpmDependency.Scope, ): NpmDependency { check(directory.isDirectory) { "Dependency on local path should point on directory but $directory found" } return NpmDependency( - project = project, + objectFactory = objectFactory, + scope = scope, name = name, version = fileVersion(directory), - scope = scope, ) } diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/NpmDependencyExtension.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/NpmDependencyExtension.kt index 6b8954c4bc2..69941dbf714 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/NpmDependencyExtension.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/NpmDependencyExtension.kt @@ -118,7 +118,7 @@ private abstract class NpmDependencyExtensionDelegate( version: String, ): NpmDependency = NpmDependency( - project = project, + objectFactory = project.objects, name = name, version = version, scope = scope, @@ -289,10 +289,10 @@ private fun defaultNpmDependencyDelegate( directory: File, ): NpmDependency = directoryNpmDependency( - project = project, + objectFactory = project.objects, + scope = scope, name = name, directory = directory, - scope = scope, ) override fun processNonStringFirstArgument(arg: Any?, vararg args: Any?): NpmDependency { diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/NpmProject.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/NpmProject.kt index 1cb7cd7b705..79fa42de9e2 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/NpmProject.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/NpmProject.kt @@ -13,6 +13,7 @@ import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJsCompilation import org.jetbrains.kotlin.gradle.plugin.mpp.disambiguateName import org.jetbrains.kotlin.gradle.targets.js.dsl.KotlinJsTargetDsl import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootPlugin +import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootPlugin.Companion.kotlinNodeJsExtension import org.jetbrains.kotlin.gradle.targets.js.npm.tasks.KotlinPackageJsonTask import org.jetbrains.kotlin.util.capitalizeDecapitalize.toLowerCaseAsciiOnly import java.io.File @@ -37,7 +38,7 @@ open class NpmProject(@Transient val compilation: KotlinJsCompilation) : Seriali } @Transient - val nodeJs = NodeJsRootPlugin.apply(project.rootProject) + val nodeJs = project.rootProject.kotlinNodeJsExtension val dir: File by lazy { nodeJs.projectPackagesDir.resolve(name) diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/NpmResolverPlugin.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/NpmResolverPlugin.kt index 5ab313b77cd..f505a34bb80 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/NpmResolverPlugin.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/NpmResolverPlugin.kt @@ -7,13 +7,34 @@ package org.jetbrains.kotlin.gradle.targets.js.npm import org.gradle.api.Plugin import org.gradle.api.Project +import org.jetbrains.kotlin.gradle.plugin.whenEvaluated import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootPlugin +import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootPlugin.Companion.kotlinNodeJsExtension +import org.jetbrains.kotlin.gradle.targets.js.npm.resolver.implementing +import org.jetbrains.kotlin.gradle.targets.js.testing.KotlinJsTest class NpmResolverPlugin : Plugin { - override fun apply(target: Project) { - val project = target + override fun apply(project: Project) { val nodeJs = NodeJsRootPlugin.apply(project.rootProject) - nodeJs.npmResolutionManager.requireConfiguringState().addProject(project) + project.rootProject.kotlinNodeJsExtension.resolver.addProject(project) + val kotlinNodeJsTaskProvidersExtension = project.rootProject.kotlinNodeJsExtension + project.whenEvaluated { + project.tasks.implementing(RequiresNpmDependencies::class) + .configureEach { task -> + if (task.enabled) { + task as RequiresNpmDependencies + // KotlinJsTest delegates npm dependencies to testFramework, + // which can be defined after this configure action + if (task !is KotlinJsTest) { + nodeJs.taskRequirements.addTaskRequirements(task) + } + task.dependsOn( + kotlinNodeJsTaskProvidersExtension.npmInstallTaskProvider, + kotlinNodeJsTaskProvidersExtension.storeYarnLockTaskProvider, + ) + } + } + } } companion object { diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/PublicPackageJsonTask.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/PublicPackageJsonTask.kt index 13a39bb9f49..3a947c43dd5 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/PublicPackageJsonTask.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/PublicPackageJsonTask.kt @@ -6,33 +6,40 @@ package org.jetbrains.kotlin.gradle.targets.js.npm import org.gradle.api.DefaultTask +import org.gradle.api.provider.Property import org.gradle.api.tasks.Input -import org.gradle.api.tasks.Nested +import org.gradle.api.tasks.Internal import org.gradle.api.tasks.OutputFile import org.gradle.api.tasks.TaskAction -import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJsCompilation -import org.jetbrains.kotlin.gradle.targets.js.ir.KotlinJsIrCompilation import org.jetbrains.kotlin.gradle.targets.js.npm.NpmProject.Companion.PACKAGE_JSON import org.jetbrains.kotlin.gradle.utils.property import java.io.File -import javax.inject.Inject -open class PublicPackageJsonTask -@Inject -constructor( - @Transient - private val compilation: KotlinJsCompilation -) : DefaultTask() { - private val npmProject = compilation.npmProject +abstract class PublicPackageJsonTask : + DefaultTask(), + UsesKotlinNpmResolutionManager { - @Transient - private val nodeJs = npmProject.nodeJs - private val resolutionManager = nodeJs.npmResolutionManager + @get:Internal + abstract val compilationDisambiguatedName: Property - private val compilationName = compilation.disambiguatedName private val projectPath = project.path - private val packageJsonHandlers = compilation.packageJsonHandlers + @get:Input + val projectVersion = project.version.toString() + + @get:Input + abstract val jsIrCompilation: Property + + @get:Input + abstract val npmProjectName: Property + + @get:Internal + abstract val npmProjectMain: Property + + private val packageJsonHandlers: List Unit> + get() = npmResolutionManager.get().parameters.packageJsonHandlers.get() + .getValue("$projectPath:${compilationDisambiguatedName.get()}") + @get:Input val packageJsonCustomFields: Map @@ -41,47 +48,40 @@ constructor( packageJsonHandlers.forEach { it() } }.customFields - private val compilationResolver - get() = resolutionManager.resolver[projectPath][compilationName] - private val compilationResolution - get() = compilationResolver.getResolutionOrResolveIfForced() ?: error("Compilation resolution isn't available") + get() = npmResolutionManager.get().resolution.get()[projectPath][compilationDisambiguatedName.get()] + .getResolutionOrResolve( + npmResolutionManager.get(), + logger + ) - @get:Nested - internal val externalDependencies: Collection + @get:Input + val externalDependencies: Collection get() = compilationResolution.externalNpmDependencies - .map { - NpmDependencyDeclaration( - scope = it.scope, - name = it.name, - version = it.version, - ) - } - - private val publicPackageJsonTaskName by lazy { - npmProject.publicPackageJsonTaskName - } private val defaultPackageJsonFile by lazy { project.buildDir .resolve("tmp") - .resolve(publicPackageJsonTaskName) + .resolve(name) .resolve(PACKAGE_JSON) } @get:OutputFile var packageJsonFile: File by property { defaultPackageJsonFile } - private val isJrIrCompilation = compilation is KotlinJsIrCompilation - private val projectVersion = project.version.toString() - @TaskAction fun resolve() { - packageJson(npmProject.name, projectVersion, npmProject.main, externalDependencies, packageJsonHandlers).let { packageJson -> - packageJson.main = "${npmProject.name}.js" + packageJson( + npmProjectName.get(), + projectVersion, + npmProjectMain.get(), + externalDependencies, + packageJsonHandlers + ).let { packageJson -> + packageJson.main = "${npmProjectName}.js" - if (isJrIrCompilation) { - packageJson.types = "${npmProject.name}.d.ts" + if (jsIrCompilation.get()) { + packageJson.types = "${npmProjectName}.d.ts" } packageJson.apply { diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/RequiresNpmDependencies.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/RequiresNpmDependencies.kt index 031b1b56da2..45d0a100302 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/RequiresNpmDependencies.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/RequiresNpmDependencies.kt @@ -11,7 +11,6 @@ import org.jetbrains.kotlin.gradle.targets.js.RequiredKotlinJsDependency interface RequiresNpmDependencies { val compilation: KotlinJsCompilation - val nodeModulesRequired: Boolean val requiredNpmDependencies: Set @Internal diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/resolved/KotlinCompilationNpmResolution.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/resolved/KotlinCompilationNpmResolution.kt deleted file mode 100644 index b834d6a97e7..00000000000 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/resolved/KotlinCompilationNpmResolution.kt +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2010-2020 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 org.jetbrains.kotlin.gradle.targets.js.npm.resolved - -import org.gradle.api.Project -import org.jetbrains.kotlin.gradle.targets.js.npm.* -import java.io.Serializable - -/** - * Resolved [NpmProject] - */ -class KotlinCompilationNpmResolution( - @Transient - private val _project: Project?, - val npmProject: NpmProject, - val internalCompositeDependencies: Collection, - val externalGradleDependencies: Collection, - private val _externalNpmDependencies: Collection, - val packageJson: PackageJson -) : Serializable { - val project - get() = _project!! - - val externalNpmDependencies - get() = _externalNpmDependencies - .map { - NpmDependency( - project = _project, - name = it.name, - version = it.version, - scope = it.scope, - ) - } -} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/resolved/KotlinProjectNpmResolution.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/resolved/KotlinProjectNpmResolution.kt index 6b4856d4292..51a5d165fc6 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/resolved/KotlinProjectNpmResolution.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/resolved/KotlinProjectNpmResolution.kt @@ -5,35 +5,20 @@ package org.jetbrains.kotlin.gradle.targets.js.npm.resolved -import org.jetbrains.kotlin.gradle.targets.js.RequiredKotlinJsDependency -import org.jetbrains.kotlin.gradle.targets.js.npm.NpmDependency +import org.jetbrains.kotlin.gradle.targets.js.npm.resolver.KotlinCompilationNpmResolution +import java.io.Serializable -/** - * Info about NPM projects inside particular gradle [project]. - */ class KotlinProjectNpmResolution( - val project: String, - val npmProjects: List, - val taskRequirements: Map> -) { - val npmProjectsByNpmDependency: Map by lazy { - mutableMapOf().also { result -> - npmProjects.forEach { npmPackage -> - npmPackage.externalNpmDependencies.forEach { npmDependency -> - result[npmDependency] = npmPackage - } - } - } - } - - val byCompilation by lazy { npmProjects.associateBy { it.npmProject.compilationName } } + val byCompilation: Map, +) : Serializable { + val npmProjects: Collection + get() = byCompilation.values operator fun get(compilationName: String): KotlinCompilationNpmResolution { -// check(compilation.target.project.path == project) return byCompilation.getValue(compilationName) } companion object { - fun empty(project: String) = KotlinProjectNpmResolution(project, listOf(), mapOf()) + fun empty() = KotlinProjectNpmResolution(emptyMap()) } } \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/resolved/KotlinRootNpmResolution.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/resolved/KotlinRootNpmResolution.kt index 970d7c0f456..ce8e51b4403 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/resolved/KotlinRootNpmResolution.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/resolved/KotlinRootNpmResolution.kt @@ -5,11 +5,69 @@ package org.jetbrains.kotlin.gradle.targets.js.npm.resolved -import org.gradle.api.Project +import org.gradle.api.logging.Logger +import org.gradle.internal.service.ServiceRegistry +import org.jetbrains.kotlin.gradle.targets.js.npm.KotlinNpmResolutionManager +import org.jetbrains.kotlin.gradle.targets.js.npm.NpmEnvironment +import org.jetbrains.kotlin.gradle.targets.js.npm.YarnEnvironment +import org.jetbrains.kotlin.gradle.targets.js.yarn.toVersionString +import java.io.Serializable -internal class KotlinRootNpmResolution( - val rootProject: Project?, - internal val projects: Map -) { - operator fun get(project: String) = projects[project] ?: KotlinProjectNpmResolution.empty(project) +class KotlinRootNpmResolution( + val projects: Map, + val rootProjectName: String, + val rootProjectVersion: String +) : Serializable { + operator fun get(project: String) = projects[project] ?: KotlinProjectNpmResolution.empty() + + /** + * Don't use directly, use [KotlinNpmResolutionManager.installIfNeeded] instead. + */ + internal fun prepareInstallation( + logger: Logger, + npmEnvironment: NpmEnvironment, + yarnEnvironment: YarnEnvironment, + npmResolutionManager: KotlinNpmResolutionManager + ): Installation { + synchronized(projects) { + npmResolutionManager.parameters.gradleNodeModulesProvider.get().close() + npmResolutionManager.parameters.compositeNodeModulesProvider.get().close() + + val projectResolutions: List = projects.values.flatMap { it.npmProjects }.map { it.close(npmResolutionManager, logger) } + npmEnvironment.packageManager.prepareRootProject( + npmEnvironment, + rootProjectName, + rootProjectVersion, + logger, + projectResolutions, + yarnEnvironment.yarnResolutions + .associate { it.path to it.toVersionString() }, + ) + + return Installation( + projectResolutions + ) + } + } +} + +class Installation(val compilationResolutions: Collection) { + internal fun install( + args: List, + services: ServiceRegistry, + logger: Logger, + npmEnvironment: NpmEnvironment, + yarnEnvironment: YarnEnvironment, + ) { + synchronized(compilationResolutions) { + npmEnvironment.packageManager.resolveRootProject( + services, + logger, + npmEnvironment, + yarnEnvironment, + compilationResolutions, + args + ) + } + } } \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/resolved/PreparedKotlinCompilationNpmResolution.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/resolved/PreparedKotlinCompilationNpmResolution.kt new file mode 100644 index 00000000000..e22548c773c --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/resolved/PreparedKotlinCompilationNpmResolution.kt @@ -0,0 +1,20 @@ +/* + * Copyright 2010-2020 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 org.jetbrains.kotlin.gradle.targets.js.npm.resolved + +import org.jetbrains.kotlin.gradle.targets.js.npm.* +import java.io.File +import java.io.Serializable + +/** + * Resolved [NpmProject] + */ +class PreparedKotlinCompilationNpmResolution( + val npmProjectDir: File, + val internalCompositeDependencies: Collection, + val externalGradleDependencies: Collection, + val externalNpmDependencies: Collection, +) : Serializable \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/resolver/GradleDependencies.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/resolver/GradleDependencies.kt new file mode 100644 index 00000000000..04f597f998e --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/resolver/GradleDependencies.kt @@ -0,0 +1,42 @@ +/* + * Copyright 2010-2022 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 org.jetbrains.kotlin.gradle.targets.js.npm.resolver + +import org.gradle.api.artifacts.ResolvedArtifact +import org.gradle.api.artifacts.ResolvedDependency +import org.gradle.api.initialization.IncludedBuild +import java.io.File +import java.io.Serializable + +data class ExternalGradleDependency( + val dependency: ResolvedDependency, + val artifact: ResolvedArtifact +) : Serializable + +data class FileCollectionExternalGradleDependency( + val files: Collection, + val dependencyVersion: String? +) : Serializable + +data class FileExternalGradleDependency( + val dependencyName: String, + val dependencyVersion: String, + val file: File +) : Serializable + +data class CompositeDependency( + val dependencyName: String, + val dependencyVersion: String, + val includedBuildDir: File, + @Transient + val includedBuild: IncludedBuild? +) : Serializable + +data class InternalDependency( + val projectPath: String, + val compilationName: String, + val projectName: String +) : Serializable \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/resolver/KotlinCompilationNpmResolution.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/resolver/KotlinCompilationNpmResolution.kt new file mode 100644 index 00000000000..8038ebc745b --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/resolver/KotlinCompilationNpmResolution.kt @@ -0,0 +1,201 @@ +/* + * Copyright 2010-2022 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 org.jetbrains.kotlin.gradle.targets.js.npm.resolver + +import org.gradle.api.logging.Logger +import org.jetbrains.kotlin.gradle.targets.js.nodejs.TasksRequirements +import org.jetbrains.kotlin.gradle.targets.js.npm.* +import org.jetbrains.kotlin.gradle.targets.js.npm.NpmProject.Companion.PACKAGE_JSON +import org.jetbrains.kotlin.gradle.targets.js.npm.resolved.PreparedKotlinCompilationNpmResolution +import java.io.Serializable +import java.io.File + +class KotlinCompilationNpmResolution( + var internalDependencies: Collection, + var internalCompositeDependencies: Collection, + var externalGradleDependencies: Collection, + var externalNpmDependencies: Collection, + var fileCollectionDependencies: Collection, + val projectPath: String, + val projectPackagesDir: File, + val rootDir: File, + val compilationDisambiguatedName: String, + val npmProjectName: String, + val npmProjectVersion: String, + val npmProjectMain: String, + val npmProjectPackageJsonFile: File, + val npmProjectDir: File, + val tasksRequirements: TasksRequirements +) : Serializable { + + val inputs: PackageJsonProducerInputs + get() = PackageJsonProducerInputs( + internalDependencies.map { it.projectName }, + internalCompositeDependencies.flatMap { it.getPackages() }, + externalGradleDependencies.map { it.file }, + externalNpmDependencies.map { it.uniqueRepresentation() }, + fileCollectionDependencies.flatMap { it.files } + ) + + private var closed = false + private var resolution: PreparedKotlinCompilationNpmResolution? = null + + @Synchronized + fun resolve( + skipWriting: Boolean = false, + npmResolutionManager: KotlinNpmResolutionManager, + logger: Logger + ): PreparedKotlinCompilationNpmResolution { + check(resolution == null) { "$this already resolved" } + + return createPackageJson( + skipWriting, + npmResolutionManager, + logger + ).also { + resolution = it + } + } + + @Synchronized + fun getResolutionOrResolve( + npmResolutionManager: KotlinNpmResolutionManager, + logger: Logger, + ): PreparedKotlinCompilationNpmResolution { + + return resolution ?: resolve( + skipWriting = true, + npmResolutionManager, + logger + ) + } + + @Synchronized + fun close( + npmResolutionManager: KotlinNpmResolutionManager, + logger: Logger, + ): PreparedKotlinCompilationNpmResolution { + check(!closed) { "$this already closed" } + closed = true + return getResolutionOrResolve(npmResolutionManager, logger) + } + + fun createPackageJson( + skipWriting: Boolean, + npmResolutionManager: KotlinNpmResolutionManager, + logger: Logger + ): PreparedKotlinCompilationNpmResolution { + val rootResolver = npmResolutionManager.parameters.resolution.get() + + internalDependencies.map { + val compilationNpmResolution: KotlinCompilationNpmResolution = rootResolver[it.projectPath][it.compilationName] + compilationNpmResolution.getResolutionOrResolve( + npmResolutionManager, + logger + ) + } + val importedExternalGradleDependencies = externalGradleDependencies.mapNotNull { + npmResolutionManager.parameters.gradleNodeModulesProvider.get().get(it.dependencyName, it.dependencyVersion, it.file) + } + fileCollectionDependencies.flatMap { dependency -> + dependency.files + // Gradle can hash with FileHasher only files and only existed files + .filter { it.isFile } + .map { file -> + npmResolutionManager.parameters.gradleNodeModulesProvider.get().get( + file.name, + dependency.dependencyVersion ?: "0.0.1", + file + ) + } + }.filterNotNull() + val transitiveNpmDependencies = importedExternalGradleDependencies.flatMap { + it.dependencies + }.filter { it.scope != NpmDependency.Scope.DEV } + + val compositeDependencies = internalCompositeDependencies.flatMap { dependency -> + dependency.getPackages() + .map { file -> + npmResolutionManager.parameters.compositeNodeModulesProvider.get().get( + dependency.dependencyName, + dependency.dependencyVersion, + file + ) + } + }.filterNotNull() + + val toolsNpmDependencies = tasksRequirements + .getCompilationNpmRequirements(projectPath, compilationDisambiguatedName) + + val otherNpmDependencies = toolsNpmDependencies + transitiveNpmDependencies + val allNpmDependencies = disambiguateDependencies(externalNpmDependencies, otherNpmDependencies, logger) + val packageJsonHandlers = + npmResolutionManager.parameters.packageJsonHandlers.get()["$projectPath:${compilationDisambiguatedName}"] + ?: emptyList() + + val packageJson = packageJson( + npmProjectName, + npmProjectVersion, + npmProjectMain, + allNpmDependencies, + packageJsonHandlers + ) + + compositeDependencies.forEach { + packageJson.dependencies[it.name] = it.version + } + + packageJsonHandlers.forEach { + it(packageJson) + } + + if (!skipWriting) { + packageJson.saveTo(npmProjectPackageJsonFile) + } + + return PreparedKotlinCompilationNpmResolution( + npmProjectDir, + compositeDependencies, + importedExternalGradleDependencies, + allNpmDependencies, + ) + } + + private fun disambiguateDependencies( + direct: Collection, + others: Collection, + logger: Logger, + ): Collection { + val unique = others.groupBy(NpmDependencyDeclaration::name) + .filterKeys { k -> direct.none { it.name == k } } + .mapNotNull { (name, dependencies) -> + dependencies.maxByOrNull { dep -> + SemVer.from(dep.version, true) + }?.also { selected -> + if (dependencies.size > 1) { + logger.warn( + """ + Transitive npm dependency version clash for compilation "${compilationDisambiguatedName}" + Candidates: + ${dependencies.joinToString("\n") { "\t\t" + it.name + "@" + it.version }} + Selected: + ${selected.name}@${selected.version} + """.trimIndent() + ) + } + } + } + return direct + unique + } + + internal fun CompositeDependency.getPackages(): List { + val packages = includedBuildDir.resolve(projectPackagesDir.relativeTo(rootDir)) + return packages + .list() + ?.map { packages.resolve(it) } + ?.map { it.resolve(PACKAGE_JSON) } + ?: emptyList() + } +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/resolver/KotlinCompilationNpmResolver.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/resolver/KotlinCompilationNpmResolver.kt index 7008799ed6c..a545eebdd0c 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/resolver/KotlinCompilationNpmResolver.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/resolver/KotlinCompilationNpmResolver.kt @@ -12,12 +12,9 @@ import org.gradle.api.artifacts.ResolvedDependency import org.gradle.api.artifacts.component.ProjectComponentIdentifier import org.gradle.api.attributes.Category import org.gradle.api.attributes.Usage -import org.gradle.api.initialization.IncludedBuild import org.gradle.api.internal.artifacts.DefaultProjectComponentIdentifier -import org.gradle.api.tasks.* +import org.gradle.api.tasks.TaskProvider import org.gradle.api.tasks.bundling.Zip -import org.gradle.configurationcache.extensions.serviceOf -import org.gradle.work.NormalizeLineEndings import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation import org.jetbrains.kotlin.gradle.plugin.categoryByName import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJsCompilation @@ -28,29 +25,24 @@ import org.jetbrains.kotlin.gradle.plugin.sources.KotlinDependencyScope import org.jetbrains.kotlin.gradle.plugin.sources.compilationDependencyConfigurationByScope import org.jetbrains.kotlin.gradle.plugin.sources.sourceSetDependencyConfigurationByScope import org.jetbrains.kotlin.gradle.plugin.usesPlatformOf +import org.jetbrains.kotlin.gradle.targets.js.ir.KotlinJsIrCompilation import org.jetbrains.kotlin.gradle.targets.js.ir.KotlinJsIrTarget +import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootPlugin.Companion.kotlinNpmResolutionManager import org.jetbrains.kotlin.gradle.targets.js.npm.* -import org.jetbrains.kotlin.gradle.targets.js.npm.NpmProject.Companion.PACKAGE_JSON -import org.jetbrains.kotlin.gradle.targets.js.npm.resolved.KotlinCompilationNpmResolution import org.jetbrains.kotlin.gradle.targets.js.npm.tasks.KotlinPackageJsonTask import org.jetbrains.kotlin.gradle.tasks.registerTask import org.jetbrains.kotlin.gradle.utils.CompositeProjectComponentArtifactMetadata import org.jetbrains.kotlin.gradle.utils.`is` import org.jetbrains.kotlin.gradle.utils.topRealPath -import org.jetbrains.kotlin.gradle.utils.unavailableValueError -import java.io.File import java.io.Serializable /** * See [KotlinNpmResolutionManager] for details about resolution process. */ -internal class KotlinCompilationNpmResolver( - @Transient +class KotlinCompilationNpmResolver( val projectResolver: KotlinProjectNpmResolver, - @Transient val compilation: KotlinJsCompilation ) : Serializable { - @Transient var rootResolver = projectResolver.resolver val npmProject = compilation.npmProject @@ -61,26 +53,32 @@ internal class KotlinCompilationNpmResolver( project.version.toString() } - val nodeJs get() = rootResolver.nodeJs - private val nodeJs_ get() = nodeJs ?: unavailableValueError("nodeJs") - val target get() = compilation.target val project get() = target.project - val projectPath = project.path + val projectPath: String = project.path - @Transient - val packageJsonTaskHolder: TaskProvider? = + val packageJsonTaskHolder: TaskProvider = KotlinPackageJsonTask.create(compilation) - @Transient - val publicPackageJsonTaskHolder: TaskProvider = + val publicPackageJsonTaskHolder: TaskProvider = run { + val npmResolutionManager = project.kotlinNpmResolutionManager project.registerTask( - npmProject.publicPackageJsonTaskName, - listOf(compilation) + npmProject.publicPackageJsonTaskName ) { it.dependsOn(packageJsonTaskHolder) + + it.compilationDisambiguatedName.set(compilation.disambiguatedName) + + it.npmResolutionManager.apply { + set(npmResolutionManager) + disallowChanges() + } + + it.jsIrCompilation.set(compilation is KotlinJsIrCompilation) + it.npmProjectName.set(npmProject.name) + it.npmProjectMain.set(npmProject.main) }.also { packageJsonTask -> if (compilation.isMain()) { project.tasks @@ -91,66 +89,29 @@ internal class KotlinCompilationNpmResolver( } } } + } override fun toString(): String = "KotlinCompilationNpmResolver(${npmProject.name})" - @delegate:Transient - private val aggregatedConfiguration: Configuration by lazy { - createAggregatedConfiguration() - } + private var _compilationNpmResolution: KotlinCompilationNpmResolution? = null - private val packageJsonProducer_: PackageJsonProducer by lazy { - val visitor = ConfigurationVisitor() - visitor.visit(aggregatedConfiguration) - visitor.toPackageJsonProducer().also { it.compilationResolver = this } - } - - val packageJsonProducer: PackageJsonProducer + val compilationNpmResolution: KotlinCompilationNpmResolution get() { - val packageJsonProducer = packageJsonProducer_ - packageJsonProducer.compilationResolver = this - return packageJsonProducer + return _compilationNpmResolution ?: run { + val visitor = ConfigurationVisitor() + visitor.visit(createAggregatedConfiguration()) + visitor.toPackageJsonProducer() + }.also { + _compilationNpmResolution = it + } } - private var closed = false - private var resolution: KotlinCompilationNpmResolution? = null - - @Synchronized - fun resolve(skipWriting: Boolean = false): KotlinCompilationNpmResolution { - check(!closed) { "$this already closed" } - check(resolution == null) { "$this already resolved" } - - return packageJsonProducer.createPackageJson(skipWriting).also { - resolution = it - } - } - - @Synchronized - fun getResolutionOrResolveIfForced(): KotlinCompilationNpmResolution? { - if (resolution != null) return resolution - if (rootResolver.mayBeUpToDateTasksRegistry.get().shouldResolveNpmDependenciesFor(npmProject.packageJsonTaskPath)) { - // when we need to resolve the compilation but the task is UP-TO-DATE, so we don't have ready resolution yet - return resolve(skipWriting = true) - } - if (rootResolver.forceFullResolve) { - // need to force all NPM tasks to be configured in IDEA import - rootResolver.gradleNodeModules.fileHasher = project.serviceOf() - rootResolver.compositeNodeModules.fileHasher = project.serviceOf() - project.tasks.implementing(RequiresNpmDependencies::class).all {} - return resolve() - } - return null // we don't need to resolve NPM dependencies for the compilation - } - @Synchronized fun close(): KotlinCompilationNpmResolution? { - check(!closed) { "$this already closed" } - val resolution = getResolutionOrResolveIfForced() - closed = true - return resolution + return _compilationNpmResolution } - private fun createAggregatedConfiguration(): Configuration { + fun createAggregatedConfiguration(): Configuration { val all = project.configurations.create(compilation.disambiguateName("npm")) all.usesPlatformOf(target) @@ -174,46 +135,16 @@ internal class KotlinCompilationNpmResolver( } // We don't have `kotlin-js-test-runner` in NPM yet - all.dependencies.add(nodeJs_.versions.kotlinJsTestRunner.createDependency(project)) + all.dependencies.add(rootResolver.versions.kotlinJsTestRunner.createDependency(project)) return all } - data class ExternalGradleDependency( - val dependency: ResolvedDependency, - val artifact: ResolvedArtifact - ) : Serializable - - data class FileCollectionExternalGradleDependency( - val files: Collection, - val dependencyVersion: String? - ) : Serializable - - data class FileExternalGradleDependency( - val dependencyName: String, - val dependencyVersion: String, - val file: File - ) : Serializable - - data class CompositeDependency( - val dependencyName: String, - val dependencyVersion: String, - val includedBuildDir: File, - @Transient - val includedBuild: IncludedBuild? - ) : Serializable - - data class InternalDependency( - val projectPath: String, - val compilationName: String, - val projectName: String - ) : Serializable - inner class ConfigurationVisitor { private val internalDependencies = mutableSetOf() private val internalCompositeDependencies = mutableSetOf() private val externalGradleDependencies = mutableSetOf() - private val externalNpmDependencies = mutableSetOf() + private val externalNpmDependencies = mutableSetOf() private val fileCollectionDependencies = mutableSetOf() private val visitedDependencies = mutableSetOf() @@ -225,7 +156,7 @@ internal class KotlinCompilationNpmResolver( configuration.allDependencies.forEach { dependency -> when (dependency) { - is NpmDependency -> externalNpmDependencies.add(dependency) + is NpmDependency -> externalNpmDependencies.add(dependency.toDeclaration()) is FileCollectionDependency -> fileCollectionDependencies.add( FileCollectionExternalGradleDependency( dependency.files.files, @@ -240,7 +171,7 @@ internal class KotlinCompilationNpmResolver( val main = compilation.target.compilations.findByName(KotlinCompilation.MAIN_COMPILATION_NAME) as KotlinJsCompilation internalDependencies.add( InternalDependency( - projectResolver.project.path, + projectResolver.projectPath, main.disambiguatedName, projectResolver[main].npmProject.name ) @@ -336,7 +267,7 @@ internal class KotlinCompilationNpmResolver( } } - fun toPackageJsonProducer() = PackageJsonProducer( + fun toPackageJsonProducer() = KotlinCompilationNpmResolution( internalDependencies, internalCompositeDependencies, externalGradleDependencies.map { @@ -346,169 +277,18 @@ internal class KotlinCompilationNpmResolver( it.artifact.file ) }, - externalNpmDependencies.map { it.toDeclaration() }, + externalNpmDependencies, fileCollectionDependencies, - projectPath + projectPath, + rootResolver.projectPackagesDir, + rootResolver.rootProjectDir, + compilationDisambiguatedName, + npmProject.name, + npmVersion, + npmProject.main, + npmProject.packageJsonFile, + npmProject.dir, + rootResolver.tasksRequirements ) } - - @Suppress("unused") - class PackageJsonProducerInputs( - @get:Input - val internalDependencies: Collection, - - @get:PathSensitive(PathSensitivity.ABSOLUTE) - @get:IgnoreEmptyDirectories - @get:NormalizeLineEndings - @get:InputFiles - val internalCompositeDependencies: Collection, - - @get:PathSensitive(PathSensitivity.ABSOLUTE) - @get:IgnoreEmptyDirectories - @get:NormalizeLineEndings - @get:InputFiles - val externalGradleDependencies: Collection, - - @get:Input - val externalDependencies: Collection, - - @get:Input - val fileCollectionDependencies: Collection - ) - - @Suppress("MemberVisibilityCanBePrivate") - class PackageJsonProducer( - var internalDependencies: Collection, - var internalCompositeDependencies: Collection, - var externalGradleDependencies: Collection, - var externalNpmDependencies: Collection, - var fileCollectionDependencies: Collection, - val projectPath: String - ) : Serializable { - private val projectPackagesDir by lazy { compilationResolver.nodeJs_.projectPackagesDir } - private val rootDir by lazy { compilationResolver.nodeJs_.rootProject.rootDir } - - @Transient - internal lateinit var compilationResolver: KotlinCompilationNpmResolver - - val inputs: PackageJsonProducerInputs - get() = PackageJsonProducerInputs( - internalDependencies.map { it.projectName }, - internalCompositeDependencies.flatMap { it.getPackages() }, - externalGradleDependencies.map { it.file }, - externalNpmDependencies.map { it.uniqueRepresentation() }, - fileCollectionDependencies.flatMap { it.files } - ) - - fun createPackageJson(skipWriting: Boolean): KotlinCompilationNpmResolution { - internalDependencies.map { - compilationResolver.rootResolver[it.projectPath][it.compilationName].getResolutionOrResolveIfForced() - ?: error("Unresolved dependent npm package: ${compilationResolver} -> $it") - } - val importedExternalGradleDependencies = externalGradleDependencies.mapNotNull { - compilationResolver.rootResolver.gradleNodeModules.get(it.dependencyName, it.dependencyVersion, it.file) - } + fileCollectionDependencies.flatMap { dependency -> - dependency.files - // Gradle can hash with FileHasher only files and only existed files - .filter { it.isFile } - .map { file -> - compilationResolver.rootResolver.gradleNodeModules.get( - file.name, - dependency.dependencyVersion ?: "0.0.1", - file - ) - } - }.filterNotNull() - val transitiveNpmDependencies = importedExternalGradleDependencies.flatMap { - it.dependencies - }.filter { it.scope != NpmDependency.Scope.DEV } - - val compositeDependencies = internalCompositeDependencies.flatMap { dependency -> - dependency.getPackages() - .map { file -> - compilationResolver.rootResolver.compositeNodeModules.get( - dependency.dependencyName, - dependency.dependencyVersion, - file - ) - } - }.filterNotNull() - - val toolsNpmDependencies = compilationResolver.rootResolver.taskRequirements - .getCompilationNpmRequirements(projectPath, compilationResolver.compilationDisambiguatedName) - - val otherNpmDependencies = toolsNpmDependencies + transitiveNpmDependencies - val allNpmDependencies = disambiguateDependencies(externalNpmDependencies, otherNpmDependencies) - val packageJsonHandlers = if (compilationResolver.compilation != null) { - compilationResolver.compilation.packageJsonHandlers - } else { - compilationResolver.rootResolver.getPackageJsonHandlers(projectPath, compilationResolver.compilationDisambiguatedName) - } - - val packageJson = packageJson( - compilationResolver.npmProject.name, - compilationResolver.npmVersion, - compilationResolver.npmProject.main, - allNpmDependencies, - packageJsonHandlers - ) - - compositeDependencies.forEach { - packageJson.dependencies[it.name] = it.version - } - - packageJsonHandlers.forEach { - it(packageJson) - } - - if (!skipWriting) { - packageJson.saveTo(compilationResolver.npmProject.packageJsonFile) - } - - return KotlinCompilationNpmResolution( - @Suppress("SENSELESS_COMPARISON") - if (compilationResolver.compilation != null) compilationResolver.project else null, - compilationResolver.npmProject, - compositeDependencies, - importedExternalGradleDependencies, - allNpmDependencies, - packageJson - ) - } - - private fun disambiguateDependencies( - direct: Collection, - others: Collection, - ): Collection { - val unique = others.groupBy(NpmDependencyDeclaration::name) - .filterKeys { k -> direct.none { it.name == k } } - .mapNotNull { (name, dependencies) -> - dependencies.maxByOrNull { dep -> - SemVer.from(dep.version, true) - }?.also { selected -> - if (dependencies.size > 1) { - compilationResolver.project.logger.warn( - """ - Transitive npm dependency version clash for compilation "${compilationResolver.compilation.name}" - Candidates: - ${dependencies.joinToString("\n") { "\t\t" + it.name + "@" + it.version }} - Selected: - ${selected.name}@${selected.version} - """.trimIndent() - ) - } - } - } - return direct + unique - } - - private fun CompositeDependency.getPackages(): List { - val packages = includedBuildDir.resolve(projectPackagesDir.relativeTo(rootDir)) - return packages - .list() - ?.map { packages.resolve(it) } - ?.map { it.resolve(PACKAGE_JSON) } - ?: emptyList() - } - } } diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/resolver/KotlinProjectNpmResolver.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/resolver/KotlinProjectNpmResolver.kt index ed080536da6..c3d980e76fc 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/resolver/KotlinProjectNpmResolver.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/resolver/KotlinProjectNpmResolver.kt @@ -14,32 +14,24 @@ import org.jetbrains.kotlin.gradle.dsl.kotlinExtensionOrNull import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType import org.jetbrains.kotlin.gradle.plugin.KotlinTarget import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJsCompilation -import org.jetbrains.kotlin.gradle.plugin.whenEvaluated import org.jetbrains.kotlin.gradle.targets.js.KotlinJsTarget -import org.jetbrains.kotlin.gradle.targets.js.npm.RequiresNpmDependencies +import org.jetbrains.kotlin.gradle.targets.js.npm.KotlinNpmResolutionManager import org.jetbrains.kotlin.gradle.targets.js.npm.resolved.KotlinProjectNpmResolution -import org.jetbrains.kotlin.gradle.targets.js.testing.KotlinJsTest -import org.jetbrains.kotlin.gradle.utils.unavailableValueError import java.io.Serializable import kotlin.reflect.KClass /** * See [KotlinNpmResolutionManager] for details about resolution process. */ -internal class KotlinProjectNpmResolver( - @Transient - val project: Project, - @Transient +class KotlinProjectNpmResolver( + project: Project, var resolver: KotlinRootNpmResolver ) : Serializable { - override fun toString(): String = "ProjectNpmResolver($project)" - - private val projectPath by lazy { project.path } + val projectPath by lazy { project.path } private val byCompilation = mutableMapOf() operator fun get(compilation: KotlinJsCompilation): KotlinCompilationNpmResolver { - check(compilation.target.project == project) return byCompilation[compilation.disambiguatedName] ?: error("$compilation was not registered in $this") } @@ -47,38 +39,17 @@ internal class KotlinProjectNpmResolver( return byCompilation[compilationName] ?: error("$compilationName was not registered in $this") } - private var closed = false + private var resolution: KotlinProjectNpmResolution? = null val compilationResolvers: Collection get() = byCompilation.values init { - addContainerListeners() - - project.whenEvaluated { - val nodeJs = resolver.nodeJs ?: unavailableValueError("resolver.nodeJs") - project.tasks.implementing(RequiresNpmDependencies::class) - .configureEach { task -> - if (task.enabled) { - task as RequiresNpmDependencies - // KotlinJsTest delegates npm dependencies to testFramework, - // which can be defined after this configure action - val packageJsonTaskHolder = get(task.compilation).packageJsonTaskHolder - if (task !is KotlinJsTest) { - nodeJs.taskRequirements.addTaskRequirements(task) - } - task.dependsOn(packageJsonTaskHolder) - task.dependsOn( - nodeJs.npmInstallTaskProvider, - nodeJs.storeYarnLockTaskProvider, - ) - } - } - } + project.addContainerListeners() } - private fun addContainerListeners() { - val kotlin = project.kotlinExtensionOrNull + private fun Project.addContainerListeners() { + val kotlin = kotlinExtensionOrNull ?: error("NpmResolverPlugin should be applied after kotlin plugin") when (kotlin) { @@ -91,10 +62,11 @@ internal class KotlinProjectNpmResolver( } private fun addTargetListeners(target: KotlinTarget) { - check(!closed) { resolver.alreadyResolvedMessage("add target $target") } + check(resolution == null) { resolver.alreadyResolvedMessage("add target $target") } if (target.platformType == KotlinPlatformType.js || - target.platformType == KotlinPlatformType.wasm) { + target.platformType == KotlinPlatformType.wasm + ) { target.compilations.all { compilation -> if (compilation is KotlinJsCompilation) { // compilation may be KotlinWithJavaTarget for old Kotlin2JsPlugin @@ -115,19 +87,23 @@ internal class KotlinProjectNpmResolver( @Synchronized private fun addCompilation(compilation: KotlinJsCompilation) { - check(!closed) { resolver.alreadyResolvedMessage("add compilation $compilation") } + check(resolution == null) { resolver.alreadyResolvedMessage("add compilation $compilation") } - byCompilation[compilation.disambiguatedName] = KotlinCompilationNpmResolver(this, compilation) + byCompilation[compilation.disambiguatedName] = + KotlinCompilationNpmResolver( + this, + compilation + ) } fun close(): KotlinProjectNpmResolution { - check(!closed) - closed = true - - return KotlinProjectNpmResolution( - projectPath, - byCompilation.values.mapNotNull { it.close() }, - resolver.taskRequirements.byTask + return resolution ?: KotlinProjectNpmResolution( + byCompilation + .map { (key, value) -> + value.close()?.let { key to it } + } + .filterNotNull() + .toMap(), ) } } diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/resolver/KotlinRootNpmResolver.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/resolver/KotlinRootNpmResolver.kt index a7529e0a93f..5881fddb422 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/resolver/KotlinRootNpmResolver.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/resolver/KotlinRootNpmResolver.kt @@ -6,204 +6,45 @@ package org.jetbrains.kotlin.gradle.targets.js.npm.resolver import org.gradle.api.Project -import org.gradle.api.Task -import org.gradle.api.logging.Logger -import org.gradle.api.provider.Provider -import org.gradle.api.services.BuildService -import org.gradle.internal.service.ServiceRegistry import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJsCompilation import org.jetbrains.kotlin.gradle.plugin.mpp.isMain +import org.jetbrains.kotlin.gradle.targets.js.NpmVersions import org.jetbrains.kotlin.gradle.targets.js.ir.KotlinJsIrCompilation -import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootExtension import org.jetbrains.kotlin.gradle.targets.js.nodejs.TasksRequirements -import org.jetbrains.kotlin.gradle.targets.js.npm.* -import org.jetbrains.kotlin.gradle.targets.js.npm.resolved.KotlinCompilationNpmResolution -import org.jetbrains.kotlin.gradle.targets.js.npm.resolved.KotlinProjectNpmResolution import org.jetbrains.kotlin.gradle.targets.js.npm.resolved.KotlinRootNpmResolution -import org.jetbrains.kotlin.gradle.targets.js.npm.tasks.KotlinNpmCachesSetup -import org.jetbrains.kotlin.gradle.targets.js.npm.tasks.KotlinNpmInstallTask -import org.jetbrains.kotlin.gradle.targets.js.npm.tasks.KotlinPackageJsonTask -import org.jetbrains.kotlin.gradle.targets.js.npm.tasks.RootPackageJsonTask -import org.jetbrains.kotlin.gradle.targets.js.yarn.YarnEnv -import org.jetbrains.kotlin.gradle.targets.js.yarn.YarnPlugin -import org.jetbrains.kotlin.gradle.targets.js.yarn.YarnResolution -import org.jetbrains.kotlin.gradle.targets.js.yarn.toVersionString -import org.jetbrains.kotlin.gradle.tasks.Kotlin2JsCompile -import org.jetbrains.kotlin.gradle.tasks.withType -import org.jetbrains.kotlin.gradle.utils.ArchiveOperationsCompat -import org.jetbrains.kotlin.gradle.utils.FileSystemOperationsCompat -import org.jetbrains.kotlin.gradle.utils.unavailableValueError +import java.io.File +import java.io.Serializable -/** - * See [KotlinNpmResolutionManager] for details about resolution process. - * - * This class contains many transient properties to deduplicate data when configuration cache is used. - * Regularly tasks share the same instance of this class, but with configuration cache each task that holds a reference to the instance will - * create an own copy. We use build services as a single storage for the heavy state of this class. - */ class KotlinRootNpmResolver internal constructor( - @Transient - val nodeJs: NodeJsRootExtension?, - val forceFullResolve: Boolean -) { - private val nodeJs_ - get() = nodeJs ?: unavailableValueError("nodeJs") + val rootProjectName: String, + val rootProjectVersion: String, + val tasksRequirements: TasksRequirements, + val versions: NpmVersions, + val projectPackagesDir: File, + val rootProjectDir: File, +) : Serializable { - private val rootProject: Project? - get() = nodeJs?.rootProject + internal var resolution: KotlinRootNpmResolution? = null - private val rootProject_: Project - get() = rootProject ?: unavailableValueError("rootProject") - - private val rootProjectName by lazy { - rootProject_.name - } - - private val rootProjectVersion by lazy { - rootProject_.version.toString() - } - - @Transient - @Volatile - private var state_: RootResolverState? = RootResolverState.CONFIGURING - - var state - get() = state_ ?: resolverStateHolder.get().state - set(value) { - if (state_ != null) { - state_ = value - } else { - resolverStateHolder.get().state = value - } - } - - private val archiveOperations by lazy { ArchiveOperationsCompat(rootProject_) } - private val fs by lazy { FileSystemOperationsCompat(rootProject_) } - - internal val gradleNodeModulesProvider: Provider = - rootProject_ - .gradle.sharedServices.registerIfAbsent("gradle-node-modules", GradleNodeModulesCache::class.java) { - it.parameters.cacheDir.set(nodeJs_.nodeModulesGradleCacheDir) - it.parameters.rootProjectDir.set(rootProject_.projectDir) - }.also { - declareBuildServiceForRootProjectJsTasks(it) - declareBuildServiceForAllProjectsJsTasks(it) - } - - internal val gradleNodeModules: GradleNodeModulesCache - get() = gradleNodeModulesProvider.get().also { - it.archiveOperations = archiveOperations - it.fs = fs - } - - internal val compositeNodeModulesProvider: Provider = - rootProject_ - .gradle.sharedServices.registerIfAbsent("composite-node-modules", CompositeNodeModulesCache::class.java) { - it.parameters.cacheDir.set(nodeJs_.nodeModulesGradleCacheDir) - it.parameters.rootProjectDir.set(rootProject_.projectDir) - }.also { - declareBuildServiceForRootProjectJsTasks(it) - declareBuildServiceForAllProjectsJsTasks(it) - } - - internal val compositeNodeModules: CompositeNodeModulesCache - get() = compositeNodeModulesProvider.get() - - @Transient - private val projectResolvers_: MutableMap? = mutableMapOf() - - @Transient - private val yarnEnvironment_: Provider? = rootProject_.provider { - YarnPlugin.apply(rootProject_).requireConfigured() - } - - @Transient - private val npmEnvironment_: Provider? = rootProject_.provider { - nodeJs_.asNpmEnvironment - } - - @Transient - private val yarnResolutions_: Provider>? = rootProject_.provider { - YarnPlugin.apply(rootProject_).resolutions - } - - @Transient - private val taskRequirements_: TasksRequirements? = nodeJs_.taskRequirements - - private val resolverStateHolder by lazy { - rootProject_.gradle.sharedServices.registerIfAbsent( - KotlinRootNpmResolverStateHolder::class.qualifiedName, - KotlinRootNpmResolverStateHolder::class.java - ) { service -> - service.parameters.projectResolvers.set(projectResolvers_) - service.parameters.packageManager.set(nodeJs_.packageManager) - service.parameters.yarnEnvironment.set(yarnEnvironment_?.get()) - service.parameters.npmEnvironment.set(npmEnvironment_?.get()) - service.parameters.yarnResolutions.set(yarnResolutions_?.get()) - service.parameters.taskRequirements.set(taskRequirements_) - service.parameters.packageJsonHandlers.set(compilations.associate { compilation -> - "${compilation.project.path}:${compilation.disambiguatedName}" to compilation.packageJsonHandlers - }.filter { it.value.isNotEmpty() }) - }.also { - declareBuildServiceForRootProjectJsTasks(it) - declareBuildServiceForAllProjectsJsTasks(it) - } - } - - private val configurationCacheProjectResolvers: MutableMap - get() { - val stateHolder = resolverStateHolder.get() - val projResolvers = stateHolder.parameters.projectResolvers.get() - if (stateHolder.initialized) return projResolvers - projResolvers.forEach { (_, value) -> - value.resolver = this - value.compilationResolvers.forEach { compResolver -> - compResolver.rootResolver = this - } - } - stateHolder.initialized = true - return projResolvers - } - - private val projectResolvers - get() = projectResolvers_ ?: configurationCacheProjectResolvers - - private val packageManager - get() = nodeJs?.packageManager ?: resolverStateHolder.get().parameters.packageManager.get() - - private val yarnEnvironment - get() = yarnEnvironment_?.get() ?: resolverStateHolder.get().parameters.yarnEnvironment.get() - - private val npmEnvironment - get() = npmEnvironment_?.get() ?: resolverStateHolder.get().parameters.npmEnvironment.get() - - private val yarnResolutions - get() = yarnResolutions_?.get() ?: resolverStateHolder.get().parameters.yarnResolutions.get() - - internal val taskRequirements - get() = taskRequirements_ ?: resolverStateHolder.get().parameters.taskRequirements.get() - - internal val mayBeUpToDateTasksRegistry = - MayBeUpToDatePackageJsonTasksRegistry.registerIfAbsent(rootProject_) + val projectResolvers: MutableMap = mutableMapOf() fun alreadyResolvedMessage(action: String) = "Cannot $action. NodeJS projects already resolved." fun addProject(target: Project) { synchronized(projectResolvers) { - check(state == RootResolverState.CONFIGURING) { alreadyResolvedMessage("add new project: $target") } - projectResolvers[target.path] = KotlinProjectNpmResolver(target, this) + check(resolution == null) { alreadyResolvedMessage("add new project: $target") } + val kotlinProjectNpmResolver = KotlinProjectNpmResolver(target, this) + projectResolvers[target.path] = kotlinProjectNpmResolver } } - internal operator fun get(projectPath: String) = projectResolvers[projectPath] ?: error("$projectPath is not configured for JS usage") + internal operator fun get(projectPath: String) = + projectResolvers[projectPath] ?: error("$projectPath is not configured for JS usage") val compilations: Collection get() = projectResolvers.values.flatMap { it.compilationResolvers.map { it.compilation } } - internal fun getPackageJsonHandlers(projectPath: String, compilationDisambiguatedName: String): List Unit> = - resolverStateHolder.get().parameters.packageJsonHandlers.get()["$projectPath:$compilationDisambiguatedName"] ?: emptyList() - internal fun findDependentResolver(src: Project, target: Project): List? { // todo: proper finding using KotlinTargetComponent.findUsageContext val targetResolver = this[target.path] @@ -242,113 +83,15 @@ class KotlinRootNpmResolver internal constructor( return mainCompilations } - /** - * Don't use directly, use [KotlinNpmResolutionManager.installIfNeeded] instead. - */ - internal fun prepareInstallation(logger: Logger): Installation { - synchronized(projectResolvers) { - state = RootResolverState.PROJECTS_CLOSED - - val projectResolutions = projectResolvers.values - .map { it.close() } - .associateBy { it.project } - val allNpmPackages = projectResolutions.values.flatMap { it.npmProjects } - - gradleNodeModules.close() - compositeNodeModules.close() - - packageManager.prepareRootProject( - rootProject, - npmEnvironment, - rootProjectName, - rootProjectVersion, - logger, - allNpmPackages, - yarnResolutions - .associate { it.path to it.toVersionString() }, - forceFullResolve - ) - - return Installation( - projectResolutions - ) - } + internal fun close(): KotlinRootNpmResolution { + return resolution ?: KotlinRootNpmResolution( + projectResolvers + .map { (key, value) -> key to value.close() } + .toMap(), + rootProjectName, + rootProjectVersion + ) } - - open inner class Installation(val projectResolutions: Map) { - operator fun get(project: String) = - projectResolutions[project] ?: KotlinProjectNpmResolution.empty(project) - - internal fun install( - forceUpToDate: Boolean, - args: List, - services: ServiceRegistry, - logger: Logger - ): KotlinRootNpmResolution { - synchronized(projectResolvers) { - if (state == RootResolverState.INSTALLED) { - return KotlinRootNpmResolution(rootProject, projectResolutions) - } - check(state == RootResolverState.PROJECTS_CLOSED) { - "Projects must be closed" - } - state = RootResolverState.INSTALLED - - val allNpmPackages = projectResolutions - .values - .flatMap { it.npmProjects } - - packageManager.resolveRootProject( - services, - logger, - npmEnvironment, - yarnEnvironment, - allNpmPackages, - args - ) - - return KotlinRootNpmResolution(rootProject, projectResolutions) - } - } - } - - private fun removeOutdatedPackages(nodeJs: NodeJsRootExtension, allNpmPackages: List) { - val packages = allNpmPackages.mapTo(mutableSetOf()) { it.npmProject.name } - nodeJs.projectPackagesDir.listFiles()?.forEach { - if (it.name !in packages) { - it.deleteRecursively() - } - } - } - - internal fun declareBuildServiceForRootProjectJsTasks(serviceProvider: Provider>) { - rootProject_.tasks.withType().configureEach { - it.usesService(serviceProvider) - } - rootProject_.tasks.withType().configureEach { - it.usesService(serviceProvider) - } - rootProject_.tasks.withType().configureEach { - it.usesService(serviceProvider) - } - } - - internal fun declareBuildServiceForAllProjectsJsTasks(serviceProvider: Provider>) { - rootProject_.allprojects { project -> - project.tasks.withType().configureEach { - it.usesService(serviceProvider) - } - project.tasks.withType().configureEach { - it.usesService(serviceProvider) - } - } - } -} - -enum class RootResolverState { - CONFIGURING, - PROJECTS_CLOSED, - INSTALLED } const val PACKAGE_JSON_UMBRELLA_TASK_NAME = "packageJsonUmbrella" \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/resolver/KotlinRootNpmResolverStateHolder.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/resolver/KotlinRootNpmResolverStateHolder.kt deleted file mode 100644 index acdc2dc4a2b..00000000000 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/resolver/KotlinRootNpmResolverStateHolder.kt +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2010-2021 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 org.jetbrains.kotlin.gradle.targets.js.npm.resolver - -import org.gradle.api.provider.ListProperty -import org.gradle.api.provider.MapProperty -import org.gradle.api.provider.Property -import org.gradle.api.services.BuildService -import org.gradle.api.services.BuildServiceParameters -import org.jetbrains.kotlin.gradle.targets.js.nodejs.TasksRequirements -import org.jetbrains.kotlin.gradle.targets.js.npm.NpmApi -import org.jetbrains.kotlin.gradle.targets.js.npm.NpmEnvironment -import org.jetbrains.kotlin.gradle.targets.js.npm.PackageJson -import org.jetbrains.kotlin.gradle.targets.js.yarn.YarnEnv -import org.jetbrains.kotlin.gradle.targets.js.yarn.YarnResolution - -// We use this service as a single storage for duplicated data in configuration cache -abstract class KotlinRootNpmResolverStateHolder : BuildService { - internal interface Parameters : BuildServiceParameters { - val projectResolvers: MapProperty - val packageManager: Property - val yarnEnvironment: Property - val npmEnvironment: Property - val yarnResolutions: ListProperty - val taskRequirements: Property - - // pulled up from compilation resolver since it was failing with ClassNotFoundException on deserialization, see KT-49061 - val packageJsonHandlers: MapProperty Unit>> - } - - var initialized = false - - @Volatile - var state = RootResolverState.CONFIGURING -} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/resolver/MayBeUpToDatePackageJsonTasksRegistry.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/resolver/MayBeUpToDatePackageJsonTasksRegistry.kt deleted file mode 100644 index b3b138dc313..00000000000 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/resolver/MayBeUpToDatePackageJsonTasksRegistry.kt +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2010-2021 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 org.jetbrains.kotlin.gradle.targets.js.npm.resolver - -import org.gradle.api.Project -import org.gradle.api.provider.Provider -import org.gradle.api.services.BuildService -import org.gradle.api.services.BuildServiceParameters -import org.jetbrains.kotlin.gradle.targets.js.npm.tasks.KotlinPackageJsonTask -import java.util.concurrent.ConcurrentHashMap - -internal abstract class MayBeUpToDatePackageJsonTasksRegistry : BuildService { - private val mayBeUpToDateTasks = ConcurrentHashMap.newKeySet() - - fun markForNpmDependenciesResolve(task: KotlinPackageJsonTask) { - mayBeUpToDateTasks.add(task.path) - } - - fun shouldResolveNpmDependenciesFor(taskPath: String) = taskPath in mayBeUpToDateTasks - - companion object { - fun registerIfAbsent(project: Project): Provider = - project.rootProject.gradle.sharedServices.registerIfAbsent( - MayBeUpToDatePackageJsonTasksRegistry::class.qualifiedName, - MayBeUpToDatePackageJsonTasksRegistry::class.java - ) {} - } -} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/resolver/PackageJsonProducerInputs.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/resolver/PackageJsonProducerInputs.kt new file mode 100644 index 00000000000..64b18c5207d --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/resolver/PackageJsonProducerInputs.kt @@ -0,0 +1,33 @@ +/* + * Copyright 2010-2022 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 org.jetbrains.kotlin.gradle.targets.js.npm.resolver + +import org.gradle.api.tasks.* +import org.gradle.work.NormalizeLineEndings +import java.io.File + +class PackageJsonProducerInputs( + @get:Input + val internalDependencies: Collection, + + @get:PathSensitive(PathSensitivity.ABSOLUTE) + @get:IgnoreEmptyDirectories + @get:NormalizeLineEndings + @get:InputFiles + val internalCompositeDependencies: Collection, + + @get:PathSensitive(PathSensitivity.ABSOLUTE) + @get:IgnoreEmptyDirectories + @get:NormalizeLineEndings + @get:InputFiles + val externalGradleDependencies: Collection, + + @get:Input + val externalDependencies: Collection, + + @get:Input + val fileCollectionDependencies: Collection +) \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/tasks/KotlinNpmCachesSetup.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/tasks/KotlinNpmCachesSetup.kt index 30b29e02c7f..bc67a277b0f 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/tasks/KotlinNpmCachesSetup.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/tasks/KotlinNpmCachesSetup.kt @@ -6,21 +6,24 @@ package org.jetbrains.kotlin.gradle.targets.js.npm.tasks import org.gradle.api.DefaultTask +import org.gradle.api.provider.Property +import org.gradle.api.tasks.Internal import org.gradle.api.tasks.TaskAction import org.gradle.internal.hash.FileHasher -import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootPlugin +import org.jetbrains.kotlin.gradle.targets.js.npm.CompositeNodeModulesCache +import org.jetbrains.kotlin.gradle.targets.js.npm.GradleNodeModulesCache +import org.jetbrains.kotlin.gradle.targets.js.npm.UsesCompositeNodeModulesCache +import org.jetbrains.kotlin.gradle.targets.js.npm.UsesGradleNodeModulesCache import javax.inject.Inject -abstract class KotlinNpmCachesSetup : DefaultTask() { +abstract class KotlinNpmCachesSetup : + DefaultTask(), + UsesGradleNodeModulesCache, + UsesCompositeNodeModulesCache { @get:Inject open val fileHasher: FileHasher get() = throw UnsupportedOperationException() - @Transient - private val nodeJs = NodeJsRootPlugin.apply(project.rootProject) - private val gradleNodeModules = nodeJs.npmResolutionManager.resolver.gradleNodeModulesProvider - private val compositeNodeModules = nodeJs.npmResolutionManager.resolver.compositeNodeModulesProvider - @TaskAction fun setup() { gradleNodeModules.get().fileHasher = fileHasher diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/tasks/KotlinNpmInstallTask.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/tasks/KotlinNpmInstallTask.kt index e6492c41ec9..01e1ff29402 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/tasks/KotlinNpmInstallTask.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/tasks/KotlinNpmInstallTask.kt @@ -9,72 +9,66 @@ import org.gradle.api.DefaultTask import org.gradle.api.tasks.* import org.gradle.work.NormalizeLineEndings import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootExtension -import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootPlugin +import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootPlugin.Companion.kotlinNodeJsExtension import org.jetbrains.kotlin.gradle.targets.js.npm.KotlinNpmResolutionManager +import org.jetbrains.kotlin.gradle.targets.js.npm.UsesKotlinNpmResolutionManager import org.jetbrains.kotlin.gradle.targets.js.npm.asNpmEnvironment -import org.jetbrains.kotlin.gradle.utils.unavailableValueError +import org.jetbrains.kotlin.gradle.targets.js.npm.asYarnEnvironment +import org.jetbrains.kotlin.gradle.targets.js.yarn.yarn import java.io.File -open class KotlinNpmInstallTask : DefaultTask() { +abstract class KotlinNpmInstallTask : + DefaultTask(), + UsesKotlinNpmResolutionManager { init { check(project == project.rootProject) - - onlyIf { - preparedFiles.all { - it.exists() - } - } } - @Transient - private val nodeJs: NodeJsRootExtension? = NodeJsRootPlugin.apply(project.rootProject) - private val resolutionManager = (nodeJs ?: unavailableValueError("nodeJs")).npmResolutionManager + // Only in configuration phase + // Not part of configuration caching + + private val nodeJs: NodeJsRootExtension + get() = project.rootProject.kotlinNodeJsExtension + + private val yarn + get() = project.rootProject.yarn + + // ----- + + private val npmEnvironment by lazy { + nodeJs.requireConfigured().asNpmEnvironment + } + + private val yarnEnv by lazy { + yarn.requireConfigured().asYarnEnvironment + } @Input val args: MutableList = mutableListOf() - @get:Internal - val nodeModulesDir: File by lazy { - (nodeJs ?: unavailableValueError("nodeJs")) - .rootPackageDir - .resolve("node_modules") - } - - init { - outputs.upToDateWhen { - nodeModulesDir.isDirectory - } - } - - @Suppress("unused") - @get:PathSensitive(PathSensitivity.ABSOLUTE) - @get:IgnoreEmptyDirectories - @get:NormalizeLineEndings - @get:InputFiles - val packageJsonFiles: Collection by lazy { - resolutionManager.packageJsonFiles - } - @get:PathSensitive(PathSensitivity.ABSOLUTE) @get:IgnoreEmptyDirectories @get:NormalizeLineEndings @get:InputFiles val preparedFiles: Collection by lazy { - (nodeJs ?: unavailableValueError("nodeJs")).packageManager.preparedFiles(nodeJs.asNpmEnvironment) + nodeJs.packageManager.preparedFiles(npmEnvironment) } @get:OutputFile val yarnLock: File by lazy { - (nodeJs ?: unavailableValueError("nodeJs")).rootPackageDir.resolve("yarn.lock") + nodeJs.rootPackageDir.resolve("yarn.lock") } @TaskAction fun resolve() { - resolutionManager.installIfNeeded( - args = args, - services = services, - logger = logger - ) ?: throw (resolutionManager.state as KotlinNpmResolutionManager.ResolutionState.Error).wrappedException + npmResolutionManager.get() + .installIfNeeded( + args = args, + services = services, + logger = logger, + npmEnvironment, + yarnEnv + ) ?: throw (npmResolutionManager.get().state as KotlinNpmResolutionManager.ResolutionState.Error).wrappedException } companion object { diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/tasks/KotlinPackageJsonTask.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/tasks/KotlinPackageJsonTask.kt index 8480e0699bb..40c67a035e9 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/tasks/KotlinPackageJsonTask.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/tasks/KotlinPackageJsonTask.kt @@ -11,92 +11,86 @@ import org.gradle.api.tasks.* import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJsCompilation import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootExtension import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootPlugin -import org.jetbrains.kotlin.gradle.targets.js.npm.PackageJson -import org.jetbrains.kotlin.gradle.targets.js.npm.fakePackageJsonValue -import org.jetbrains.kotlin.gradle.targets.js.npm.npmProject -import org.jetbrains.kotlin.gradle.targets.js.npm.resolver.MayBeUpToDatePackageJsonTasksRegistry +import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootPlugin.Companion.kotlinNodeJsExtension +import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootPlugin.Companion.kotlinNpmResolutionManager +import org.jetbrains.kotlin.gradle.targets.js.npm.* import org.jetbrains.kotlin.gradle.targets.js.npm.resolver.KotlinCompilationNpmResolver +import org.jetbrains.kotlin.gradle.targets.js.npm.resolver.KotlinRootNpmResolver import org.jetbrains.kotlin.gradle.targets.js.npm.resolver.PACKAGE_JSON_UMBRELLA_TASK_NAME +import org.jetbrains.kotlin.gradle.targets.js.npm.resolver.PackageJsonProducerInputs import org.jetbrains.kotlin.gradle.tasks.registerTask -import org.jetbrains.kotlin.gradle.utils.chainedDisallowChanges -import org.jetbrains.kotlin.gradle.utils.getValue import java.io.File -abstract class KotlinPackageJsonTask : DefaultTask() { +abstract class KotlinPackageJsonTask : + DefaultTask(), + UsesKotlinNpmResolutionManager, + UsesGradleNodeModulesCache, + UsesCompositeNodeModulesCache { + // Only in configuration phase + // Not part of configuration caching - init { - onlyIf { - npmResolutionManager.isConfiguringState() + private val nodeJs: NodeJsRootExtension + get() = project.rootProject.kotlinNodeJsExtension + + private val rootResolver: KotlinRootNpmResolver + get() = nodeJs.resolver + + private val compilationResolver: KotlinCompilationNpmResolver + get() = rootResolver[projectPath][compilationDisambiguatedName.get()] + + private fun findDependentTasks(): Collection = + compilationResolver.compilationNpmResolution.internalDependencies.map { dependency -> + nodeJs.resolver[dependency.projectPath][dependency.compilationName].npmProject.packageJsonTaskPath + } + compilationResolver.compilationNpmResolution.internalCompositeDependencies.map { dependency -> + dependency.includedBuild?.task(":$PACKAGE_JSON_UMBRELLA_TASK_NAME") ?: error("includedBuild instance is not available") + dependency.includedBuild.task(":${RootPackageJsonTask.NAME}") } - outputs.upToDateWhen { - // this way we will ensure that we need to resolve dependencies for the compilation in case of UP-TO-DATE task - // used in KotlinCompilationNpmResolver#getResolutionOrResolveIfForced - mayBeUpToDateTasksRegistry.get().markForNpmDependenciesResolve(it as KotlinPackageJsonTask) - true - } - } - @Transient - private lateinit var nodeJs: NodeJsRootExtension + // ----- - private val npmResolutionManager by project.provider { nodeJs.npmResolutionManager } - - @Transient - private lateinit var compilation: KotlinJsCompilation + private val projectPath = project.path @get:Internal - internal abstract val mayBeUpToDateTasksRegistry: Property + abstract val compilationDisambiguatedName: Property - private val compilationDisambiguatedName by lazy { - compilation.disambiguatedName - } - - @Input - val projectPath = project.path - - private val compilationResolver - get() = npmResolutionManager.resolver[projectPath][compilationDisambiguatedName] - - private val producer: KotlinCompilationNpmResolver.PackageJsonProducer - get() = compilationResolver.packageJsonProducer + private val packageJsonHandlers: List Unit> + get() = npmResolutionManager.get().parameters.packageJsonHandlers.get() + .getValue("$projectPath:${compilationDisambiguatedName.get()}") @get:Input val packageJsonCustomFields: Map by lazy { PackageJson(fakePackageJsonValue, fakePackageJsonValue) .apply { - compilation.packageJsonHandlers.forEach { it() } + packageJsonHandlers.forEach { it() } }.customFields } - private fun findDependentTasks(): Collection = - producer.internalDependencies.map { dependency -> - npmResolutionManager.resolver[dependency.projectPath][dependency.compilationName].npmProject.packageJsonTaskPath - } + producer.internalCompositeDependencies.map { dependency -> - dependency.includedBuild?.task(":$PACKAGE_JSON_UMBRELLA_TASK_NAME") ?: error("includedBuild instance is not available") - dependency.includedBuild.task(":${RootPackageJsonTask.NAME}") - } @get:Input internal val toolsNpmDependencies: List by lazy { nodeJs.taskRequirements - .getCompilationNpmRequirements(projectPath, compilationDisambiguatedName) + .getCompilationNpmRequirements(projectPath, compilationDisambiguatedName.get()) .map { it.toString() } .sorted() } + // nested inputs are processed in configuration phase + // so npmResolutionManager must not be used @get:Nested - internal val producerInputs: KotlinCompilationNpmResolver.PackageJsonProducerInputs by lazy { - producer.inputs + internal val producerInputs: PackageJsonProducerInputs by lazy { + compilationResolver.compilationNpmResolution.inputs } @get:OutputFile - val packageJson: File by lazy { - compilationResolver.npmProject.packageJsonFile - } + abstract val packageJson: Property @TaskAction fun resolve() { - compilationResolver.resolve() + npmResolutionManager.get().resolution.get()[projectPath][compilationDisambiguatedName.get()] + .resolve( + npmResolutionManager = npmResolutionManager.get(), + logger = logger + ) } companion object { @@ -104,29 +98,50 @@ abstract class KotlinPackageJsonTask : DefaultTask() { val target = compilation.target val project = target.project val npmProject = compilation.npmProject - val nodeJs = NodeJsRootPlugin.apply(project.rootProject) + val nodeJsTaskProviders = project.rootProject.kotlinNodeJsExtension - val npmCachesSetupTask = nodeJs.npmCachesSetupTaskProvider + val npmCachesSetupTask = nodeJsTaskProviders.npmCachesSetupTaskProvider val packageJsonTaskName = npmProject.packageJsonTaskName - val packageJsonUmbrella = nodeJs.packageJsonUmbrellaTaskProvider + val packageJsonUmbrella = nodeJsTaskProviders.packageJsonUmbrellaTaskProvider + val npmResolutionManager = project.kotlinNpmResolutionManager + val gradleNodeModules = GradleNodeModulesCache.registerIfAbsent(project, null, null) + val compositeNodeModules = CompositeNodeModulesCache.registerIfAbsent(project, null, null) val packageJsonTask = project.registerTask(packageJsonTaskName) { task -> - task.nodeJs = nodeJs - task.compilation = compilation + task.compilationDisambiguatedName.set(compilation.disambiguatedName) task.description = "Create package.json file for $compilation" task.group = NodeJsRootPlugin.TASKS_GROUP_NAME - val packageJsonStateService = MayBeUpToDatePackageJsonTasksRegistry.registerIfAbsent(project) - task.mayBeUpToDateTasksRegistry.value(packageJsonStateService).chainedDisallowChanges().also { - task.usesService(packageJsonStateService) + + task.npmResolutionManager.apply { + set(npmResolutionManager) + disallowChanges() + } + + task.gradleNodeModules.apply { + set(gradleNodeModules) + disallowChanges() + } + + task.compositeNodeModules.apply { + set(compositeNodeModules) + disallowChanges() + } + + task.packageJson.set(compilation.npmProject.packageJsonFile) + + task.onlyIf { + it as KotlinPackageJsonTask + it.npmResolutionManager.get().isConfiguringState() } task.dependsOn(target.project.provider { task.findDependentTasks() }) task.dependsOn(npmCachesSetupTask) } + packageJsonUmbrella.configure { task -> task.inputs.file(packageJsonTask.map { it.packageJson }) } - nodeJs.rootPackageJsonTaskProvider!!.configure { it.mustRunAfter(packageJsonTask) } + nodeJsTaskProviders.rootPackageJsonTaskProvider.configure { it.mustRunAfter(packageJsonTask) } return packageJsonTask } diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/tasks/RootPackageJsonTask.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/tasks/RootPackageJsonTask.kt index ebd1ac6d6f3..040be63c0c3 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/tasks/RootPackageJsonTask.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/tasks/RootPackageJsonTask.kt @@ -8,22 +8,40 @@ package org.jetbrains.kotlin.gradle.targets.js.npm.tasks import org.gradle.api.DefaultTask import org.gradle.api.tasks.* import org.gradle.work.NormalizeLineEndings -import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootPlugin -import org.jetbrains.kotlin.gradle.targets.js.npm.NpmProject +import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootPlugin.Companion.kotlinNodeJsExtension +import org.jetbrains.kotlin.gradle.targets.js.npm.* +import org.jetbrains.kotlin.gradle.targets.js.npm.resolver.KotlinRootNpmResolver +import org.jetbrains.kotlin.gradle.targets.js.yarn.yarn import java.io.File -open class RootPackageJsonTask : DefaultTask() { +abstract class RootPackageJsonTask : + DefaultTask(), + UsesKotlinNpmResolutionManager { init { check(project == project.rootProject) - - onlyIf { - resolutionManager.isConfiguringState() - } } - @Transient - private val nodeJs = NodeJsRootPlugin.apply(project.rootProject) - private val resolutionManager = nodeJs.npmResolutionManager + // Only in configuration phase + // Not part of configuration caching + + private val nodeJs + get() = project.rootProject.kotlinNodeJsExtension + + private val yarn + get() = project.rootProject.yarn + + private val rootResolver: KotlinRootNpmResolver + get() = nodeJs.resolver + + // ----- + + private val npmEnvironment by lazy { + nodeJs.requireConfigured().asNpmEnvironment + } + + private val yarnEnv by lazy { + yarn.requireConfigured().asYarnEnvironment + } @get:OutputFile val rootPackageJson: File by lazy { @@ -35,12 +53,15 @@ open class RootPackageJsonTask : DefaultTask() { @get:NormalizeLineEndings @get:InputFiles val packageJsonFiles: Collection by lazy { - resolutionManager.packageJsonFiles + rootResolver.projectResolvers.values + .flatMap { it.compilationResolvers } + .map { it.compilationNpmResolution } + .map { it.npmProjectPackageJsonFile } } @TaskAction fun resolve() { - resolutionManager.prepare(logger) + npmResolutionManager.get().prepare(logger, npmEnvironment, yarnEnv) } companion object { diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/subtargets/KotlinBrowserJs.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/subtargets/KotlinBrowserJs.kt index 5ed78f2bdb9..b8910e82e7f 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/subtargets/KotlinBrowserJs.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/subtargets/KotlinBrowserJs.kt @@ -14,7 +14,6 @@ import org.gradle.language.base.plugins.LifecycleBasePlugin import org.jetbrains.kotlin.gradle.dsl.KotlinJsDce import org.jetbrains.kotlin.gradle.plugin.COMPILER_CLASSPATH_CONFIGURATION_NAME import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation -import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJsCompilation import org.jetbrains.kotlin.gradle.plugin.mpp.isMain import org.jetbrains.kotlin.gradle.plugin.mpp.isTest @@ -25,7 +24,7 @@ import org.jetbrains.kotlin.gradle.targets.js.KotlinJsTarget import org.jetbrains.kotlin.gradle.targets.js.dsl.* import org.jetbrains.kotlin.gradle.targets.js.ir.executeTaskBaseName import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootExtension -import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootPlugin +import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootPlugin.Companion.kotlinNodeJsExtension import org.jetbrains.kotlin.gradle.targets.js.npm.npmProject import org.jetbrains.kotlin.gradle.targets.js.testing.KotlinJsTest import org.jetbrains.kotlin.gradle.targets.js.testing.karma.KotlinKarma @@ -33,7 +32,6 @@ import org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpack import org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpackConfig import org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpackConfig.Mode import org.jetbrains.kotlin.gradle.targets.js.webpack.WebpackDevtool -import org.jetbrains.kotlin.gradle.targets.js.webpack.WebpackMajorVersion.Companion.choose import org.jetbrains.kotlin.gradle.tasks.dependsOn import org.jetbrains.kotlin.gradle.tasks.registerTask import org.jetbrains.kotlin.gradle.utils.doNotTrackStateCompat @@ -51,9 +49,6 @@ abstract class KotlinBrowserJs @Inject constructor(target: KotlinJsTarget) : private val runTaskConfigurations: MutableList> = mutableListOf() private val dceConfigurations: MutableList> = mutableListOf() private val distribution: Distribution = createDefaultDistribution(project) - private val propertiesProvider = PropertiesProvider(project) - private val webpackMajorVersion - get() = propertiesProvider.webpackMajorVersion override val testTaskDescription: String get() = "Run all ${target.name} tests inside browser using karma and webpack" @@ -138,7 +133,7 @@ abstract class KotlinBrowserJs @Inject constructor(target: KotlinJsTarget) : devDceTaskProvider: TaskProvider ) { val project = compilation.target.project - val nodeJs = NodeJsRootPlugin.apply(project.rootProject) + val nodeJs = project.rootProject.kotlinNodeJsExtension val commonRunTask = registerSubTargetTask(disambiguateCamelCased(RUN_TASK_NAME)) {} @@ -156,33 +151,20 @@ abstract class KotlinBrowserJs @Inject constructor(target: KotlinJsTarget) : listOf(compilation) ) { task -> task.outputDirectory.convention(distsDirectory).finalizeValueOnRead() - webpackMajorVersion.choose( - { task.args.add(0, "serve") }, - { task.bin = "webpack-dev-server/bin/webpack-dev-server.js" } - )() + task.args.add(0, "serve") task.description = "start ${type.name.toLowerCaseAsciiOnly()} webpack dev server" - webpackMajorVersion.choose( - { - task.devServer = KotlinWebpackConfig.DevServer( - open = true, - static = mutableListOf(compilation.output.resourcesDir.canonicalPath), - client = KotlinWebpackConfig.DevServer.Client( - KotlinWebpackConfig.DevServer.Client.Overlay( - errors = true, - warnings = false - ) - ) + task.devServer = KotlinWebpackConfig.DevServer( + open = true, + static = mutableListOf(compilation.output.resourcesDir.canonicalPath), + client = KotlinWebpackConfig.DevServer.Client( + KotlinWebpackConfig.DevServer.Client.Overlay( + errors = true, + warnings = false ) - }, - { - task.devServer = KotlinWebpackConfig.DevServer( - open = true, - contentBase = mutableListOf(compilation.output.resourcesDir.canonicalPath) - ) - } - )() + ) + ) task.doNotTrackStateCompat("Tracked by external webpack tool") @@ -212,7 +194,7 @@ abstract class KotlinBrowserJs @Inject constructor(target: KotlinJsTarget) : devDceTaskProvider: TaskProvider ) { val project = compilation.target.project - val nodeJs = NodeJsRootPlugin.apply(project.rootProject) + val nodeJs = project.rootProject.kotlinNodeJsExtension val processResourcesTask = target.project.tasks.named(compilation.processResourcesTaskName) diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/subtargets/KotlinJsSubTarget.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/subtargets/KotlinJsSubTarget.kt index a51afe0c763..dd0e2441e4b 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/subtargets/KotlinJsSubTarget.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/subtargets/KotlinJsSubTarget.kt @@ -20,8 +20,7 @@ import org.jetbrains.kotlin.gradle.plugin.whenEvaluated import org.jetbrains.kotlin.gradle.targets.js.KotlinJsPlatformTestRun import org.jetbrains.kotlin.gradle.targets.js.KotlinJsTarget import org.jetbrains.kotlin.gradle.targets.js.dsl.KotlinJsSubTargetDsl -import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootPlugin -import org.jetbrains.kotlin.gradle.targets.js.npm.NpmResolverPlugin +import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootPlugin.Companion.kotlinNodeJsExtension import org.jetbrains.kotlin.gradle.targets.js.npm.npmProject import org.jetbrains.kotlin.gradle.targets.js.testing.KotlinJsTest import org.jetbrains.kotlin.gradle.tasks.registerTask @@ -35,7 +34,8 @@ abstract class KotlinJsSubTarget( ) : KotlinJsSubTargetDsl { val project get() = target.project - private val nodeJs = NodeJsRootPlugin.apply(project.rootProject) + private val nodeJs = project.rootProject.kotlinNodeJsExtension + private val nodeJsTaskProviders = project.rootProject.kotlinNodeJsExtension abstract val testTaskDescription: String @@ -53,8 +53,6 @@ abstract class KotlinJsSubTarget( } internal fun configure() { - NpmResolverPlugin.apply(project) - configureTests() target.compilations.all { @@ -108,10 +106,10 @@ abstract class KotlinJsSubTarget( testJs.inputFileProperty.fileProvider(compileOutputFile) testJs.dependsOn( - nodeJs.npmInstallTaskProvider, - nodeJs.storeYarnLockTaskProvider, + nodeJsTaskProviders.npmInstallTaskProvider, + nodeJsTaskProviders.storeYarnLockTaskProvider, compileTask, - nodeJs.nodeJsSetupTaskProvider + nodeJsTaskProviders.nodeJsSetupTaskProvider ) testJs.onlyIf { diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/testing/KotlinJsTest.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/testing/KotlinJsTest.kt index a006f7cf585..17faa104771 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/testing/KotlinJsTest.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/testing/KotlinJsTest.kt @@ -14,7 +14,7 @@ import org.gradle.work.NormalizeLineEndings import org.jetbrains.kotlin.gradle.internal.testing.TCServiceMessagesTestExecutionSpec import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJsCompilation import org.jetbrains.kotlin.gradle.targets.js.RequiredKotlinJsDependency -import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootPlugin +import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootPlugin.Companion.kotlinNodeJsExtension import org.jetbrains.kotlin.gradle.targets.js.npm.RequiresNpmDependencies import org.jetbrains.kotlin.gradle.targets.js.npm.npmProject import org.jetbrains.kotlin.gradle.targets.js.testing.karma.KotlinKarma @@ -33,16 +33,12 @@ constructor( ) : KotlinTest(), RequiresNpmDependencies { @Transient - private val nodeJs = NodeJsRootPlugin.apply(project.rootProject) - - private val npmResolutionManager by project.provider { nodeJs.npmResolutionManager } + private val nodeJs = project.rootProject.kotlinNodeJsExtension private val nodeExecutable by project.provider { nodeJs.requireConfigured().nodeExecutable } private val npmProjectDir by project.provider { compilation.npmProject.dir } - private val projectPath = project.path - @Input var environment = mutableMapOf() @@ -110,9 +106,6 @@ constructor( val nodeJsArgs: MutableList = mutableListOf() - override val nodeModulesRequired: Boolean - @Internal get() = testFramework!!.nodeModulesRequired - override val requiredNpmDependencies: Set @Internal get() = testFramework!!.requiredNpmDependencies @@ -164,11 +157,6 @@ constructor( return testFramework } - override fun executeTests() { - npmResolutionManager.checkRequiredDependencies(task = this) - super.executeTests() - } - override fun createTestExecutionSpec(): TCServiceMessagesTestExecutionSpec { val forkOptions = DefaultProcessForkOptions(fileResolver) forkOptions.workingDir = npmProjectDir diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/testing/KotlinJsTestFramework.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/testing/KotlinJsTestFramework.kt index 06cf0d2c433..02e1a5de495 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/testing/KotlinJsTestFramework.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/testing/KotlinJsTestFramework.kt @@ -18,7 +18,4 @@ interface KotlinJsTestFramework : RequiresNpmDependencies { nodeJsArgs: MutableList, debug: Boolean ): TCServiceMessagesTestExecutionSpec - - override val nodeModulesRequired: Boolean - get() = true } \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/testing/karma/KotlinKarma.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/testing/karma/KotlinKarma.kt index 17346a72907..e2338332253 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/testing/karma/KotlinKarma.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/testing/karma/KotlinKarma.kt @@ -32,11 +32,10 @@ import org.jetbrains.kotlin.gradle.targets.js.dsl.WebpackRulesDsl.Companion.webp import org.jetbrains.kotlin.gradle.targets.js.internal.parseNodeJsStackTraceAsJvm import org.jetbrains.kotlin.gradle.targets.js.jsQuoted import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootPlugin +import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootPlugin.Companion.kotlinNodeJsExtension import org.jetbrains.kotlin.gradle.targets.js.npm.npmProject import org.jetbrains.kotlin.gradle.targets.js.testing.* import org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpackConfig -import org.jetbrains.kotlin.gradle.targets.js.webpack.WebpackMajorVersion -import org.jetbrains.kotlin.gradle.targets.js.webpack.WebpackMajorVersion.Companion.choose import org.jetbrains.kotlin.gradle.tasks.KotlinTest import org.jetbrains.kotlin.gradle.testing.internal.reportsDir import org.jetbrains.kotlin.gradle.utils.appendLine @@ -56,7 +55,7 @@ class KotlinKarma( private val npmProject = compilation.npmProject @Transient - private val nodeJs = NodeJsRootPlugin.apply(project.rootProject) + private val nodeJs = project.rootProject.kotlinNodeJsExtension private val nodeRootPackageDir by lazy { nodeJs.rootPackageDir } private val versions = nodeJs.versions @@ -81,8 +80,6 @@ class KotlinKarma( override val settingsState: String get() = "KotlinKarma($config)" - private val webpackMajorVersion = PropertiesProvider(project).webpackMajorVersion - val webpackConfig = KotlinWebpackConfig( configDirectory = project.projectDir.resolve("webpack.config.d"), optimization = KotlinWebpackConfig.Optimization( @@ -94,13 +91,11 @@ class KotlinKarma( export = false, progressReporter = true, progressReporterPathFilter = nodeRootPackageDir, - webpackMajorVersion = webpackMajorVersion, rules = project.objects.webpackRulesContainer(), experiments = mutableSetOf("topLevelAwait") ) init { - requiredDependencies.add(versions.kotlinJsTestRunner) requiredDependencies.add(versions.karma) useKotlinReporter() @@ -266,23 +261,14 @@ class KotlinKarma( config.frameworks.add("webpack") requiredDependencies.add(versions.karmaWebpack) requiredDependencies.add( - webpackMajorVersion.choose( - versions.webpack, - versions.webpack4 - ) + versions.webpack ) requiredDependencies.add( - webpackMajorVersion.choose( - versions.webpackCli, - versions.webpackCli3 - ) + versions.webpackCli ) requiredDependencies.add(versions.formatUtil) requiredDependencies.add( - webpackMajorVersion.choose( - versions.sourceMapLoader, - versions.sourceMapLoader1 - ) + versions.sourceMapLoader ) addPreprocessor("webpack") @@ -298,14 +284,12 @@ class KotlinKarma( // noinspection JSUnnecessarySemicolon ;(function(config) { const webpack = require('webpack'); - ${ - if (webpackMajorVersion != WebpackMajorVersion.V4) { - """ - // https://github.com/webpack/webpack/issues/12951 - const PatchSourceMapSource = require('kotlin-test-js-runner/webpack-5-debug'); - config.plugins.push(new PatchSourceMapSource()) - """ - } else "" + ${ + """ + // https://github.com/webpack/webpack/issues/12951 + const PatchSourceMapSource = require('kotlin-test-js-runner/webpack-5-debug'); + config.plugins.push(new PatchSourceMapSource()) + """ } config.plugins.push(new webpack.SourceMapDevToolPlugin({ moduleFilenameTemplate: "[absolute-resource-path]" diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/testing/mocha/KotlinMocha.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/testing/mocha/KotlinMocha.kt index 84ae86de304..437c8a2b459 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/testing/mocha/KotlinMocha.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/testing/mocha/KotlinMocha.kt @@ -15,6 +15,7 @@ import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJsCompilation import org.jetbrains.kotlin.gradle.targets.js.RequiredKotlinJsDependency import org.jetbrains.kotlin.gradle.targets.js.internal.parseNodeJsStackTraceAsJvm import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootPlugin +import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootPlugin.Companion.kotlinNodeJsExtension import org.jetbrains.kotlin.gradle.targets.js.npm.npmProject import org.jetbrains.kotlin.gradle.targets.js.testing.KotlinJsTest import org.jetbrains.kotlin.gradle.targets.js.testing.KotlinJsTestFramework @@ -25,7 +26,7 @@ class KotlinMocha(@Transient override val compilation: KotlinJsCompilation, priv @Transient private val project: Project = compilation.target.project private val npmProject = compilation.npmProject - private val versions = NodeJsRootPlugin.apply(project.rootProject).versions + private val versions = project.rootProject.kotlinNodeJsExtension.versions private val isTeamCity = project.providers.gradleProperty(TCServiceMessagesTestExecutor.TC_PROJECT_PROPERTY) override val settingsState: String @@ -33,7 +34,6 @@ class KotlinMocha(@Transient override val compilation: KotlinJsCompilation, priv override val requiredNpmDependencies: Set get() = setOf( - versions.kotlinJsTestRunner, versions.mocha, versions.sourceMapSupport, versions.formatUtil diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/typescript/TypeScriptValidationTask.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/typescript/TypeScriptValidationTask.kt index 6400149cc26..502e88ef10f 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/typescript/TypeScriptValidationTask.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/typescript/TypeScriptValidationTask.kt @@ -6,15 +6,15 @@ package org.jetbrains.kotlin.gradle.targets.js.typescript import org.gradle.api.DefaultTask -import org.gradle.api.tasks.* -import org.gradle.api.provider.Property import org.gradle.api.file.DirectoryProperty +import org.gradle.api.provider.Property +import org.gradle.api.tasks.* import org.gradle.work.NormalizeLineEndings import org.jetbrains.kotlin.gradle.internal.execWithProgress import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJsCompilation import org.jetbrains.kotlin.gradle.targets.js.RequiredKotlinJsDependency import org.jetbrains.kotlin.gradle.targets.js.ir.KotlinIrJsGeneratedTSValidationStrategy -import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootPlugin +import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootPlugin.Companion.kotlinNodeJsExtension import org.jetbrains.kotlin.gradle.targets.js.npm.RequiresNpmDependencies import org.jetbrains.kotlin.gradle.targets.js.npm.npmProject import javax.inject.Inject @@ -30,14 +30,13 @@ constructor( @get:Internal @Transient - protected val nodeJs = NodeJsRootPlugin.apply(project.rootProject) + protected val nodeJs = project.rootProject.kotlinNodeJsExtension - @get:Internal - override val nodeModulesRequired: Boolean get() = false + private val versions = nodeJs.versions @get:Internal override val requiredNpmDependencies: Set - get() = setOf(nodeJs.versions.typescript) + get() = setOf(versions.typescript) @get:SkipWhenEmpty @get:NormalizeLineEndings @@ -69,9 +68,8 @@ constructor( val message = "Oops, Kotlin/JS compiler generated invalid d.ts files." - when (validationStrategy) { - KotlinIrJsGeneratedTSValidationStrategy.ERROR -> error(message) - KotlinIrJsGeneratedTSValidationStrategy.IGNORE -> {} + if (validationStrategy == KotlinIrJsGeneratedTSValidationStrategy.ERROR) { + error(message) } } diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/webpack/KotlinWebpack.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/webpack/KotlinWebpack.kt index 01b5c2b9e87..850720a1ff4 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/webpack/KotlinWebpack.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/webpack/KotlinWebpack.kt @@ -35,6 +35,7 @@ import org.jetbrains.kotlin.gradle.targets.js.dsl.KotlinWebpackRulesContainer import org.jetbrains.kotlin.gradle.targets.js.dsl.WebpackRulesDsl import org.jetbrains.kotlin.gradle.targets.js.dsl.WebpackRulesDsl.Companion.webpackRulesContainer import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootPlugin +import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootPlugin.Companion.kotlinNodeJsExtension import org.jetbrains.kotlin.gradle.targets.js.npm.RequiresNpmDependencies import org.jetbrains.kotlin.gradle.targets.js.npm.npmProject import org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpackConfig.Mode @@ -55,9 +56,8 @@ constructor( private val objects: ObjectFactory ) : DefaultTask(), RequiresNpmDependencies, WebpackRulesDsl, UsesBuildMetricsService { @Transient - private val nodeJs = NodeJsRootPlugin.apply(project.rootProject) + private val nodeJs = project.rootProject.kotlinNodeJsExtension private val versions = nodeJs.versions - private val resolutionManager = nodeJs.npmResolutionManager private val rootPackageDir by lazy { nodeJs.rootPackageDir } private val npmProject = compilation.npmProject @@ -202,9 +202,6 @@ constructor( @Internal var generateConfigOnly: Boolean = false - @Input - val webpackMajorVersion = PropertiesProvider(project).webpackMajorVersion - fun webpackConfigApplier(body: Action) { webpackConfigAppliers.add(body) } @@ -234,7 +231,6 @@ constructor( devtool = devtool, sourceMaps = sourceMaps, resolveFromModulesFirst = resolveFromModulesFirst, - webpackMajorVersion = webpackMajorVersion ) private fun createRunner(): KotlinWebpackRunner { @@ -262,9 +258,6 @@ constructor( ) } - override val nodeModulesRequired: Boolean - @Internal get() = true - override val requiredNpmDependencies: Set @Internal get() = createWebpackConfig(true).getRequiredDependencies(versions) @@ -272,8 +265,6 @@ constructor( @TaskAction fun doExecute() { - resolutionManager.checkRequiredDependencies(task = this) - val runner = createRunner() if (generateConfigOnly) { diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/webpack/KotlinWebpackConfig.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/webpack/KotlinWebpackConfig.kt index b6a2965f463..27139a632f8 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/webpack/KotlinWebpackConfig.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/webpack/KotlinWebpackConfig.kt @@ -20,7 +20,6 @@ import org.jetbrains.kotlin.gradle.targets.js.appendConfigsFromDir import org.jetbrains.kotlin.gradle.targets.js.dsl.KotlinWebpackRulesContainer import org.jetbrains.kotlin.gradle.targets.js.dsl.WebpackRulesDsl import org.jetbrains.kotlin.gradle.targets.js.jsQuoted -import org.jetbrains.kotlin.gradle.targets.js.webpack.WebpackMajorVersion.Companion.choose import org.jetbrains.kotlin.gradle.utils.appendLine import org.jetbrains.kotlin.gradle.utils.relativeOrAbsolute import java.io.File @@ -49,6 +48,8 @@ data class KotlinWebpackConfig( @get:NormalizeLineEndings @get:InputDirectory var configDirectory: File? = null, + @Internal + var reportEvaluatedConfigFile: File? = null, @Input @Optional var devServer: DevServer? = null, @@ -72,9 +73,7 @@ data class KotlinWebpackConfig( @Optional var progressReporterPathFilter: File? = null, @Input - var resolveFromModulesFirst: Boolean = false, - @Input - val webpackMajorVersion: WebpackMajorVersion = WebpackMajorVersion.V5 + var resolveFromModulesFirst: Boolean = false ) : WebpackRulesDsl { @get:Input @@ -94,36 +93,23 @@ data class KotlinWebpackConfig( fun getRequiredDependencies(versions: NpmVersions) = mutableSetOf().also { - it.add(versions.kotlinJsTestRunner) it.add( - webpackMajorVersion.choose( - versions.webpack, - versions.webpack4 - ) + versions.webpack ) it.add( - webpackMajorVersion.choose( - versions.webpackCli, - versions.webpackCli3 - ) + versions.webpackCli ) it.add(versions.formatUtil) if (sourceMaps) { it.add( - webpackMajorVersion.choose( - versions.sourceMapLoader, - versions.sourceMapLoader1 - ) + versions.sourceMapLoader ) } if (devServer != null) { it.add( - webpackMajorVersion.choose( - versions.webpackDevServer, - versions.webpackDevServer3 - ) + versions.webpackDevServer ) } @@ -294,16 +280,8 @@ data class KotlinWebpackConfig( enforce: "pre" }); config.devtool = ${devtool?.let { "'$it'" } ?: false}; - ${ - webpackMajorVersion.choose( - "config.ignoreWarnings = [/Failed to parse source map/]", - """ - config.stats = config.stats || {} - Object.assign(config.stats, config.stats, { - warningsFilter: [/Failed to parse source map/] - }) - """ - ) + ${ + "config.ignoreWarnings = [/Failed to parse source map/]" } """.trimIndent() diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/yarn/Yarn.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/yarn/Yarn.kt index 7bde6fa946d..8d2470bf090 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/yarn/Yarn.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/yarn/Yarn.kt @@ -5,55 +5,43 @@ package org.jetbrains.kotlin.gradle.targets.js.yarn -import org.gradle.api.Project import org.gradle.api.logging.Logger import org.gradle.internal.service.ServiceRegistry -import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootExtension import org.jetbrains.kotlin.gradle.targets.js.npm.NpmApi -import org.jetbrains.kotlin.gradle.targets.js.npm.NpmDependency import org.jetbrains.kotlin.gradle.targets.js.npm.NpmEnvironment -import org.jetbrains.kotlin.gradle.targets.js.npm.resolved.KotlinCompilationNpmResolution +import org.jetbrains.kotlin.gradle.targets.js.npm.YarnEnvironment +import org.jetbrains.kotlin.gradle.targets.js.npm.resolved.PreparedKotlinCompilationNpmResolution import java.io.File class Yarn : NpmApi { private val yarnWorkspaces = YarnWorkspaces() - private fun getDelegate(project: Project): NpmApi = - yarnWorkspaces - - override fun setup(project: Project) = - getDelegate(project.rootProject).setup(project) - override fun preparedFiles(nodeJs: NpmEnvironment): Collection = yarnWorkspaces.preparedFiles(nodeJs) override fun prepareRootProject( - rootProject: Project?, nodeJs: NpmEnvironment, rootProjectName: String, rootProjectVersion: String, logger: Logger, - subProjects: Collection, + subProjects: Collection, resolutions: Map, - forceFullResolve: Boolean ) = yarnWorkspaces .prepareRootProject( - rootProject, nodeJs, rootProjectName, rootProjectVersion, logger, subProjects, resolutions, - forceFullResolve ) override fun resolveRootProject( services: ServiceRegistry, logger: Logger, nodeJs: NpmEnvironment, - yarn: YarnEnv, - npmProjects: Collection, + yarn: YarnEnvironment, + npmProjects: Collection, cliArgs: List ) { yarnWorkspaces @@ -66,15 +54,4 @@ class Yarn : NpmApi { cliArgs ) } - - override fun resolveDependency( - npmResolution: KotlinCompilationNpmResolution, - dependency: NpmDependency, - transitive: Boolean - ) = getDelegate(npmResolution.project) - .resolveDependency( - npmResolution, - dependency, - transitive - ) } \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/yarn/YarnBasics.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/yarn/YarnBasics.kt index 89ad4129818..1a75abeceaa 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/yarn/YarnBasics.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/yarn/YarnBasics.kt @@ -5,31 +5,21 @@ package org.jetbrains.kotlin.gradle.targets.js.yarn -import org.gradle.api.Project import org.gradle.api.logging.Logger import org.gradle.internal.service.ServiceRegistry import org.jetbrains.kotlin.gradle.internal.execWithProgress import org.jetbrains.kotlin.gradle.targets.js.npm.NpmApi -import org.jetbrains.kotlin.gradle.targets.js.npm.NpmDependency -import org.jetbrains.kotlin.gradle.targets.js.npm.NpmDependency.Scope.PEER import org.jetbrains.kotlin.gradle.targets.js.npm.NpmEnvironment -import org.jetbrains.kotlin.gradle.targets.js.npm.resolved.KotlinCompilationNpmResolution +import org.jetbrains.kotlin.gradle.targets.js.npm.YarnEnvironment import java.io.File abstract class YarnBasics : NpmApi { - private val nonTransitiveResolvedDependencies = mutableMapOf>() - private val transitiveResolvedDependencies = mutableMapOf>() - - override fun setup(project: Project) { - YarnPlugin.apply(project).executeSetup() - } - fun yarnExec( services: ServiceRegistry, logger: Logger, nodeJs: NpmEnvironment, - yarn: YarnEnv, + yarn: YarnEnvironment, dir: File, description: String, args: List @@ -69,107 +59,4 @@ abstract class YarnBasics : NpmApi { } } - - override fun resolveDependency( - npmResolution: KotlinCompilationNpmResolution, - dependency: NpmDependency, - transitive: Boolean - ): Set { - val files = (if (transitive) { - transitiveResolvedDependencies - } else { - nonTransitiveResolvedDependencies - })[dependency] - - if (files != null) { - return files - } - - val npmProject = npmResolution.npmProject - - val all = mutableSetOf() - - npmProject.resolve(dependency.key)?.let { - if (it.isFile) all.add(it) - if (it.path.endsWith(".js")) { - val baseName = it.path.removeSuffix(".js") - val metaJs = File(baseName + ".meta.js") - if (metaJs.isFile) all.add(metaJs) - val kjsmDir = File(baseName) - if (kjsmDir.isDirectory) { - kjsmDir.walkTopDown() - .filter { it.extension == "kjsm" } - .forEach { all.add(it) } - } - } - } - - nonTransitiveResolvedDependencies[dependency] = all - - if (transitive) { - dependency.dependencies.forEach { - resolveDependency( - npmResolution, - it, - transitive - ).also { files -> - all.addAll(files) - } - } - transitiveResolvedDependencies[dependency] = all - } - - return all - } - - protected fun yarnLockReadTransitiveDependencies( - nodeWorkDir: File, - srcDependenciesList: Collection - ) { - val yarnLock = nodeWorkDir - .resolve("yarn.lock") - .takeIf { it.isFile } - ?: return - - val entryRegistry = YarnEntryRegistry(yarnLock) - val visited = mutableMapOf() - - fun resolveRecursively(src: NpmDependency) { - if (src.scope == PEER) { - return - } - - val copy = visited[src] - if (copy != null) { - src.resolvedVersion = copy.resolvedVersion - src.integrity = copy.integrity - src.dependencies.addAll(copy.dependencies) - return - } - visited[src] = src - - val deps = entryRegistry.find(src.key, src.version) - - src.resolvedVersion = deps.version - src.integrity = deps.integrity - - deps.dependencies.mapTo(src.dependencies) { dep -> - val scopedName = dep.scopedName - val child = NpmDependency( - project = src.project, - name = scopedName.toString(), - version = dep.version ?: "*" - ) - child.parent = src - - resolveRecursively(child) - - child - } - } - - srcDependenciesList.forEach { src -> - resolveRecursively(src) - } - } } diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/yarn/YarnEntryRegistry.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/yarn/YarnEntryRegistry.kt index b2d016c04bf..16fab9ebd21 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/yarn/YarnEntryRegistry.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/yarn/YarnEntryRegistry.kt @@ -11,56 +11,6 @@ import org.jetbrains.kotlin.gradle.targets.js.npm.includedRange import org.jetbrains.kotlin.gradle.targets.js.npm.intersect import java.io.File -internal class YarnEntryRegistry(private val lockFile: File) { - private val entryMap = YarnLock.parse(lockFile) - .entries - .associateBy { it.dependencyKey } - - fun find(packageKey: String, version: String): YarnLock.Entry { - val key = dependencyKey(packageKey, version) - val entry = entryMap[key] - - if (entry != null) { - return entry - } - - entryMap.entries - .firstOrNull { (_, entry) -> - if (entry.version == null) { - false - } else { - (includedRange(version) intersect includedRange(entry.version)) != null - } - } - ?.let { return it.value } - - throw GradleException( - "Cannot find $key in yarn.lock" - ) - } - - private val YarnLock.Entry.dependencyKey: String - get() = key.correctDependencyKey() - - private fun dependencyKey(packageKey: String, version: String) = - YarnLock.dependencyKey(packageKey, version).correctDependencyKey() - - private fun String.correctDependencyKey(): String = - when { - GITHUB_MARKER in this -> replace(GITHUB_MARKER, SEPARATOR) - FILE_MARKER in this -> { - val location = substringAfter(FILE_MARKER) - val path = lockFile - .parentFile - .resolve(location) - .canonicalPath - - replaceAfter(FILE_MARKER, path) - } - else -> this - } -} - private const val SEPARATOR = "@" private const val FILE_MARKER = "${SEPARATOR}$FILE_VERSION_PREFIX" diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/yarn/YarnEnv.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/yarn/YarnEnv.kt index f386036b24c..de51e43f45e 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/yarn/YarnEnv.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/yarn/YarnEnv.kt @@ -7,7 +7,6 @@ package org.jetbrains.kotlin.gradle.targets.js.yarn import org.jetbrains.kotlin.gradle.tasks.internal.CleanableStore import java.io.File -import java.io.Serializable data class YarnEnv( val downloadUrl: String, @@ -20,4 +19,5 @@ data class YarnEnv( val yarnLockMismatchReport: YarnLockMismatchReport, val reportNewYarnLock: Boolean, val yarnLockAutoReplace: Boolean, -) : Serializable + val yarnResolutions: List +) diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/yarn/YarnImportedPackagesVersionResolver.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/yarn/YarnImportedPackagesVersionResolver.kt index 13ae3a7ab75..edbec75f928 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/yarn/YarnImportedPackagesVersionResolver.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/yarn/YarnImportedPackagesVersionResolver.kt @@ -11,12 +11,12 @@ import org.jetbrains.kotlin.gradle.targets.js.npm.GradleNodeModule import org.jetbrains.kotlin.gradle.targets.js.npm.NpmProject import org.jetbrains.kotlin.gradle.targets.js.npm.PackageJson import org.jetbrains.kotlin.gradle.targets.js.npm.fileVersion -import org.jetbrains.kotlin.gradle.targets.js.npm.resolved.KotlinCompilationNpmResolution +import org.jetbrains.kotlin.gradle.targets.js.npm.resolved.PreparedKotlinCompilationNpmResolution import java.io.File class YarnImportedPackagesVersionResolver( private val logger: Logger, - private val npmProjects: Collection, + npmProjects: Collection, private val nodeJsWorldDir: File ) { private val resolvedVersion = mutableMapOf() diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/yarn/YarnLock.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/yarn/YarnLock.kt deleted file mode 100644 index 29423a5904e..00000000000 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/yarn/YarnLock.kt +++ /dev/null @@ -1,154 +0,0 @@ -/* - * Copyright 2010-2020 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 org.jetbrains.kotlin.gradle.targets.js.yarn - -import com.google.gson.stream.JsonReader -import org.jetbrains.kotlin.gradle.targets.js.npm.PackageJson -import java.io.File -import java.io.StringReader - -data class YarnLock(val entries: List) { - data class Entry( - val key: String, - val version: String?, - val resolved: String?, - val integrity: String?, - val dependencies: List - ) - - data class Dependency(val key: String, val version: String?) { - val scopedName: PackageJson.ScopedName - get() = PackageJson.scopedName(key) - } - - companion object { - fun dependencyKey(packageKey: String, version: String) = "$packageKey@$version" - - private class Node(val parent: Node?, val value: String? = null) { - var indent: String? = null - val children = mutableMapOf() - } - - fun parse(yarnLock: File): YarnLock { - val root = parseTree(yarnLock) - - return YarnLock(root.children.map { - val values = it.value.children - Entry( - it.key, - values["version"]?.value, - values["resolved"]?.value, - values["integrity"]?.value, - values["dependencies"]?.children?.map { dep -> - Dependency(dep.key, dep.value.value) - } ?: listOf() - ) - }) - } - - private fun parseTree(yarnLock: File): Node { - val root = Node(null) - var parent = root - var onNewLevel = true - yarnLock.useLines { - it.forEach { line -> - if (!line.startsWith("#")) { - var i = 0 - while (i < line.length && line[i].isWhitespace()) i++ - val indentPos = i - val indent = line.substring(0, indentPos) - - val key = line.endsWith(":") - - if (line.isNotEmpty()) { - val values = parseLine(line, indentPos) - - if (onNewLevel) { - parent.indent = indent - onNewLevel = false - } else { - while (indent != parent.indent!!) { - parent = parent.parent!! - } - } - - val child = Node(parent, if (values.size > 1) values[1] else null) - if (key) { - values.forEach { - parent.children[it] = child - } - parent = child - onNewLevel = true - } else { - parent.children[values[0]] = child - } - } - } - } - } - return root - } - - private fun parseLine(line: String, indentPos: Int): List { - val quotedLineResult = replaceQuoted(line.removeSuffix(":")) - - return quotedLineResult - .parse(indentPos) - } - - private fun replaceQuoted(line: String): QuotedLineResult { - var result: String = line - val quotedValues = QUOTED_VALUE.findAll(line) - .flatMap { it.groupValues.drop(1).asSequence() } - .toList() - - quotedValues.forEachIndexed { index, item -> - result = result.replace( - """ - "$item" - """.trimIndent(), - """ - "$$index" - """.trimIndent() - ) - } - - return QuotedLineResult( - result, - quotedValues - ) - } - } -} - -private data class QuotedLineResult( - val value: String, - val quotedValues: List -) - -private fun QuotedLineResult.parse(indentPos: Int): List { - return value - .substring(indentPos) - .split(" ") - .map { value -> - val withoutComma = value.removeSuffix(",") - if (withoutComma.startsWith("\"")) { - val (index) = QUOTED_PLACEHOLDER.find(withoutComma)!!.destructured - val restoredValue = """ - "${quotedValues[index.toInt()]}" - """.trimIndent() - - try { - JsonReader(StringReader(restoredValue)).nextString() - } catch (e: Throwable) { - restoredValue.removePrefix("\"").removeSuffix("\"") - } - } else withoutComma - } -} - -private val QUOTED_VALUE = """"(.+?)"""".toRegex() -private val QUOTED_PLACEHOLDER = """\$(\d+)""".toRegex() \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/yarn/YarnPlugin.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/yarn/YarnPlugin.kt index 86247ba89a1..c0d3a338341 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/yarn/YarnPlugin.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/yarn/YarnPlugin.kt @@ -11,6 +11,8 @@ import org.gradle.api.tasks.TaskProvider import org.jetbrains.kotlin.gradle.utils.markResolvable import org.jetbrains.kotlin.gradle.targets.js.MultiplePluginDeclarationDetector import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootPlugin +import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootPlugin.Companion.kotlinNodeJsExtension +import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootPlugin.Companion.kotlinNpmResolutionManager import org.jetbrains.kotlin.gradle.targets.js.npm.RequiresNpmDependencies import org.jetbrains.kotlin.gradle.targets.js.npm.resolver.implementing import org.jetbrains.kotlin.gradle.targets.js.npm.tasks.KotlinNpmInstallTask @@ -30,10 +32,12 @@ open class YarnPlugin : Plugin { } val yarnRootExtension = this.extensions.create(YarnRootExtension.YARN, YarnRootExtension::class.java, this) - val nodeJs = NodeJsRootPlugin.apply(this) + NodeJsRootPlugin.apply(project) + val nodeJs = this.kotlinNodeJsExtension + val nodeJsTaskProviders = this.kotlinNodeJsExtension val setupTask = registerTask(YarnSetupTask.NAME) { - it.dependsOn(nodeJs.nodeJsSetupTaskProvider) + it.dependsOn(nodeJsTaskProviders.nodeJsSetupTaskProvider) it.configuration = provider { this.project.configurations.detachedConfiguration(this.project.dependencies.create(it.ivyDependency)) @@ -43,9 +47,19 @@ open class YarnPlugin : Plugin { } val rootPackageJson = tasks.register(RootPackageJsonTask.NAME, RootPackageJsonTask::class.java) { task -> - task.dependsOn(nodeJs.npmCachesSetupTaskProvider) + task.dependsOn(nodeJsTaskProviders.npmCachesSetupTaskProvider) task.group = NodeJsRootPlugin.TASKS_GROUP_NAME task.description = "Create root package.json" + + task.npmResolutionManager.apply { + set(project.kotlinNpmResolutionManager) + disallowChanges() + } + + task.onlyIf { + it as RootPackageJsonTask + it.npmResolutionManager.get().isConfiguringState() + } } configureRequiresNpmDependencies(project, rootPackageJson) @@ -62,7 +76,7 @@ open class YarnPlugin : Plugin { it.description = "Clean unused local yarn version" } - val packageJsonUmbrella = nodeJs + val packageJsonUmbrella = nodeJsTaskProviders .packageJsonUmbrellaTaskProvider yarnRootExtension.rootPackageJsonTaskProvider.configure { diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/yarn/YarnRootExtension.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/yarn/YarnRootExtension.kt index 87de4fe1a1f..bb4691e1eb2 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/yarn/YarnRootExtension.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/yarn/YarnRootExtension.kt @@ -7,7 +7,6 @@ package org.jetbrains.kotlin.gradle.targets.js.yarn import org.jetbrains.kotlin.gradle.targets.js.nodejs.PlatformHelper import org.gradle.api.Action -import org.gradle.api.Incubating import org.gradle.api.Project import org.gradle.api.tasks.TaskProvider import org.jetbrains.kotlin.gradle.internal.ConfigurationPhaseAware @@ -20,7 +19,6 @@ import org.jetbrains.kotlin.gradle.tasks.internal.CleanableStore import java.io.File open class YarnRootExtension( - @Transient val project: Project ) : ConfigurationPhaseAware() { init { @@ -60,7 +58,7 @@ open class YarnRootExtension( .withType(RootPackageJsonTask::class.java) .named(RootPackageJsonTask.NAME) - var resolutions: MutableList = mutableListOf() + var resolutions: MutableList by Property(mutableListOf()) fun resolution(path: String, configure: Action) { resolutions.add( @@ -75,21 +73,6 @@ open class YarnRootExtension( }) } - @Incubating - fun disableGranularWorkspaces() { - val packageJsonUmbrella = NodeJsRootPlugin.apply(project) - .packageJsonUmbrellaTaskProvider - - rootPackageJsonTaskProvider.configure { - it.dependsOn(packageJsonUmbrella) - } - - project.allprojects - .forEach { - it.tasks.implementing(RequiresNpmDependencies::class).all {} - } - } - override fun finalizeConfiguration(): YarnEnv { val cleanableStore = CleanableStore[installationDir.path] @@ -116,20 +99,10 @@ open class YarnRootExtension( yarnLockMismatchReport = yarnLockMismatchReport, reportNewYarnLock = reportNewYarnLock, yarnLockAutoReplace = yarnLockAutoReplace, + yarnResolutions = resolutions ) } - internal fun executeSetup() { - NodeJsRootPlugin.apply(project).executeSetup() - - if (!download) return - - val yarnSetupTask = yarnSetupTaskProvider.get() - yarnSetupTask.actions.forEach { - it.execute(yarnSetupTask) - } - } - companion object { const val YARN: String = "kotlinYarn" diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/yarn/YarnSetupTask.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/yarn/YarnSetupTask.kt index 55a505d67d2..c801ed92592 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/yarn/YarnSetupTask.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/yarn/YarnSetupTask.kt @@ -7,6 +7,7 @@ package org.jetbrains.kotlin.gradle.targets.js.yarn import org.gradle.api.DefaultTask import org.gradle.api.artifacts.Configuration +import org.gradle.api.file.ArchiveOperations import org.gradle.api.file.FileSystemOperations import org.gradle.api.provider.Provider import org.gradle.api.tasks.* @@ -15,24 +16,23 @@ import org.jetbrains.kotlin.gradle.logging.kotlinInfo import org.jetbrains.kotlin.gradle.plugin.statistics.KotlinBuildStatsService import org.jetbrains.kotlin.gradle.targets.js.extractWithUpToDate import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootPlugin -import org.jetbrains.kotlin.gradle.utils.ArchiveOperationsCompat import org.jetbrains.kotlin.statistics.metrics.NumericalMetrics import java.io.File import java.net.URI import javax.inject.Inject -open class YarnSetupTask : DefaultTask() { +abstract class YarnSetupTask : DefaultTask() { @Transient private val settings = project.yarn private val env by lazy { settings.requireConfigured() } private val shouldDownload = settings.download - private val archiveOperations = ArchiveOperationsCompat(project) + @get:Inject + abstract internal val archiveOperations: ArchiveOperations @get:Inject - internal open val fileHasher: FileHasher - get() = error("Should be injected") + abstract internal val fileHasher: FileHasher @get:Inject internal open val fs: FileSystemOperations diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/yarn/YarnWorkspaces.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/yarn/YarnWorkspaces.kt index 588bf3bf524..39cfbbe1653 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/yarn/YarnWorkspaces.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/yarn/YarnWorkspaces.kt @@ -5,14 +5,10 @@ package org.jetbrains.kotlin.gradle.targets.js.yarn -import org.gradle.api.Project import org.gradle.api.logging.Logger import org.gradle.internal.service.ServiceRegistry -import org.jetbrains.kotlin.gradle.targets.js.npm.NpmApi -import org.jetbrains.kotlin.gradle.targets.js.npm.NpmEnvironment -import org.jetbrains.kotlin.gradle.targets.js.npm.NpmProject -import org.jetbrains.kotlin.gradle.targets.js.npm.PackageJson -import org.jetbrains.kotlin.gradle.targets.js.npm.resolved.KotlinCompilationNpmResolution +import org.jetbrains.kotlin.gradle.targets.js.npm.* +import org.jetbrains.kotlin.gradle.targets.js.npm.resolved.PreparedKotlinCompilationNpmResolution import java.io.File class YarnWorkspaces : YarnBasics() { @@ -25,18 +21,13 @@ class YarnWorkspaces : YarnBasics() { } override fun prepareRootProject( - rootProject: Project?, nodeJs: NpmEnvironment, rootProjectName: String, rootProjectVersion: String, logger: Logger, - subProjects: Collection, + subProjects: Collection, resolutions: Map, - forceFullResolve: Boolean ) { - if (forceFullResolve) { - rootProject?.let { setup(it) } - } return prepareRootPackageJson( nodeJs, rootProjectName, @@ -52,7 +43,7 @@ class YarnWorkspaces : YarnBasics() { rootProjectName: String, rootProjectVersion: String, logger: Logger, - npmProjects: Collection, + npmProjects: Collection, resolutions: Map ) { val rootPackageJsonFile = preparedFiles(nodeJs).single() @@ -71,8 +62,8 @@ class YarnWorkspaces : YarnBasics() { services: ServiceRegistry, logger: Logger, nodeJs: NpmEnvironment, - yarn: YarnEnv, - npmProjects: Collection, + yarn: YarnEnvironment, + npmProjects: Collection, cliArgs: List ) { val nodeJsWorldDir = nodeJs.rootPackageDir @@ -86,15 +77,13 @@ class YarnWorkspaces : YarnBasics() { NpmApi.resolveOperationDescription("yarn"), cliArgs ) - - yarnLockReadTransitiveDependencies(nodeJsWorldDir, npmProjects.flatMap { it.externalNpmDependencies }) } private fun saveRootProjectWorkspacesPackageJson( rootProjectName: String, rootProjectVersion: String, logger: Logger, - npmProjects: Collection, + npmProjects: Collection, resolutions: Map, rootPackageJsonFile: File ) { @@ -102,7 +91,7 @@ class YarnWorkspaces : YarnBasics() { val rootPackageJson = PackageJson(rootProjectName, rootProjectVersion) rootPackageJson.private = true - val npmProjectWorkspaces = npmProjects.map { it.npmProject.dir.relativeTo(nodeJsWorldDir).path } + val npmProjectWorkspaces = npmProjects.map { it.npmProjectDir.relativeTo(nodeJsWorldDir).path } val importedProjectWorkspaces = YarnImportedPackagesVersionResolver(logger, npmProjects, nodeJsWorldDir).resolveAndUpdatePackages() diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/utils/compatibility.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/utils/compatibility.kt index a05e0b16f59..b4641335b74 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/utils/compatibility.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/utils/compatibility.kt @@ -53,45 +53,6 @@ internal fun checkGradleCompatibility( internal val AbstractArchiveTask.archivePathCompatible: File get() = archiveFile.get().asFile -internal class ArchiveOperationsCompat(@Transient private val project: Project) : Serializable { - private val archiveOperations: Any? = try { - (project as ProjectInternal).services.get(ArchiveOperations::class.java) - } catch (e: NoClassDefFoundError) { - // Gradle version < 6.6 - null - } - - fun zipTree(obj: Any): FileTree { - return when (archiveOperations) { - is ArchiveOperations -> archiveOperations.zipTree(obj) - else -> project.zipTree(obj) - } - } - - fun tarTree(obj: Any): FileTree { - return when (archiveOperations) { - is ArchiveOperations -> archiveOperations.tarTree(obj) - else -> project.tarTree(obj) - } - } -} - -internal class FileSystemOperationsCompat(@Transient private val project: Project) : Serializable { - private val fileSystemOperations: Any? = try { - (project as ProjectInternal).services.get(FileSystemOperations::class.java) - } catch (e: NoClassDefFoundError) { - // Gradle version < 6.0 - null - } - - fun copy(action: (CopySpec) -> Unit): WorkResult? { - return when (fileSystemOperations) { - is FileSystemOperations -> fileSystemOperations.copy(action) - else -> project.copy(action) - } - } -} - // Gradle dropped out getOwnerBuildOperationId. Workaround to build correct plugin for Gradle < 6.8 // See https://github.com/gradle/gradle/commit/0296f4441ae69ad608cfef6a90fef3fdf314fa2c internal interface LegacyTestDescriptorInternal : TestDescriptor {