[Gradle, JS] Add npm support but default Yarn package manager left
Co-authored-by: Alexander Likhachev <Alexander.Likhachev@jetbrains.com> ^KT-64119 fixed
This commit is contained in:
committed by
Space Team
parent
f9348c69ba
commit
d18660c8f1
+21
-1
@@ -112,10 +112,30 @@ class JsIrConfigurationCacheIT : KGPBaseTest() {
|
||||
@DisplayName("KT-48241: configuration cache works with test dependencies")
|
||||
@GradleTest
|
||||
fun testTestDependencies(gradleVersion: GradleVersion) {
|
||||
project("kotlin-js-project-with-test-dependencies", gradleVersion) {
|
||||
assertSimpleConfigurationCacheScenarioWorks(
|
||||
"assemble", "kotlinStorePackageLock",
|
||||
buildOptions = defaultBuildOptions.copy(
|
||||
jsOptions = defaultBuildOptions.jsOptions?.copy(
|
||||
yarn = false
|
||||
)
|
||||
),
|
||||
executedTaskNames = listOf(":rootPackageJson")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@DisplayName("KT-48241: configuration cache works with test dependencies for yarn.lock")
|
||||
@GradleTest
|
||||
fun testTestDependenciesYarnLock(gradleVersion: GradleVersion) {
|
||||
project("kotlin-js-project-with-test-dependencies", gradleVersion) {
|
||||
assertSimpleConfigurationCacheScenarioWorks(
|
||||
"assemble", "kotlinStoreYarnLock",
|
||||
buildOptions = defaultBuildOptions,
|
||||
buildOptions = defaultBuildOptions.copy(
|
||||
jsOptions = defaultBuildOptions.jsOptions?.copy(
|
||||
yarn = true
|
||||
)
|
||||
),
|
||||
executedTaskNames = listOf(":rootPackageJson")
|
||||
)
|
||||
}
|
||||
|
||||
+40
-9
@@ -28,16 +28,47 @@ class JsSetupConfigurationCacheIT : KGPBaseTest() {
|
||||
@DisplayName("Check Node.JS setup on different platforms")
|
||||
@GradleTest
|
||||
fun checkNodeJsSetup(gradleVersion: GradleVersion) {
|
||||
project("kotlin-js-browser-project", gradleVersion) {
|
||||
build("kotlinUpgradeYarnLock") {
|
||||
assertTasksExecuted(":kotlinUpgradeYarnLock")
|
||||
assertConfigurationCacheStored()
|
||||
}
|
||||
project(
|
||||
"kotlin-js-browser-project",
|
||||
gradleVersion,
|
||||
buildOptions = defaultBuildOptions.copy(
|
||||
jsOptions = defaultBuildOptions.jsOptions?.copy(
|
||||
yarn = false
|
||||
)
|
||||
)
|
||||
) {
|
||||
checkNodeJsSetup("kotlinUpgradePackageLock")
|
||||
}
|
||||
}
|
||||
|
||||
build("kotlinUpgradeYarnLock") {
|
||||
assertTasksUpToDate(":kotlinUpgradeYarnLock")
|
||||
assertConfigurationCacheReused()
|
||||
}
|
||||
// hack to be run on Mac m*
|
||||
@DisplayName("Check Node.JS setup on different platforms with Yarn")
|
||||
@GradleTest
|
||||
fun checkNodeJsSetupYarn(gradleVersion: GradleVersion) {
|
||||
project(
|
||||
"kotlin-js-browser-project",
|
||||
gradleVersion,
|
||||
buildOptions = defaultBuildOptions.copy(
|
||||
jsOptions = defaultBuildOptions.jsOptions?.copy(
|
||||
yarn = true
|
||||
)
|
||||
)
|
||||
) {
|
||||
checkNodeJsSetup("kotlinUpgradeYarnLock")
|
||||
}
|
||||
}
|
||||
|
||||
private fun TestProject.checkNodeJsSetup(
|
||||
upgradeTask: String
|
||||
) {
|
||||
build(upgradeTask) {
|
||||
assertTasksExecuted(":$upgradeTask")
|
||||
assertConfigurationCacheStored()
|
||||
}
|
||||
|
||||
build(upgradeTask) {
|
||||
assertTasksUpToDate(":$upgradeTask")
|
||||
assertConfigurationCacheReused()
|
||||
}
|
||||
}
|
||||
}
|
||||
+43
-281
@@ -12,12 +12,15 @@ import org.gradle.testkit.runner.BuildResult
|
||||
import org.gradle.util.GradleVersion
|
||||
import org.jetbrains.kotlin.gradle.targets.js.dsl.Distribution
|
||||
import org.jetbrains.kotlin.gradle.targets.js.ir.KLIB_TYPE
|
||||
import org.jetbrains.kotlin.gradle.targets.js.npm.LockCopyTask
|
||||
import org.jetbrains.kotlin.gradle.targets.js.npm.LockCopyTask.Companion.PACKAGE_LOCK_MISMATCH_MESSAGE
|
||||
import org.jetbrains.kotlin.gradle.targets.js.npm.LockCopyTask.Companion.RESTORE_PACKAGE_LOCK_NAME
|
||||
import org.jetbrains.kotlin.gradle.targets.js.npm.LockCopyTask.Companion.STORE_PACKAGE_LOCK_NAME
|
||||
import org.jetbrains.kotlin.gradle.targets.js.npm.LockCopyTask.Companion.UPGRADE_PACKAGE_LOCK
|
||||
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.fromSrcPackageJson
|
||||
import org.jetbrains.kotlin.gradle.targets.js.yarn.YarnLockCopyTask.Companion.STORE_YARN_LOCK_NAME
|
||||
import org.jetbrains.kotlin.gradle.targets.js.yarn.YarnLockCopyTask.Companion.UPGRADE_YARN_LOCK
|
||||
import org.jetbrains.kotlin.gradle.targets.js.yarn.YarnLockCopyTask.Companion.YARN_LOCK_MISMATCH_MESSAGE
|
||||
import org.jetbrains.kotlin.gradle.targets.js.yarn.YarnPlugin
|
||||
import org.jetbrains.kotlin.gradle.testbase.*
|
||||
import org.jetbrains.kotlin.gradle.testbase.TestVersions.Gradle.G_7_6
|
||||
import org.jetbrains.kotlin.gradle.util.replaceText
|
||||
@@ -34,6 +37,7 @@ import kotlin.test.assertTrue
|
||||
@JsGradlePluginTests
|
||||
class Kotlin2JsIrGradlePluginIT : KGPBaseTest() {
|
||||
|
||||
|
||||
@DisplayName("TS type declarations are generated")
|
||||
@GradleTest
|
||||
fun generateDts(gradleVersion: GradleVersion) {
|
||||
@@ -171,37 +175,6 @@ class Kotlin2JsIrGradlePluginIT : KGPBaseTest() {
|
||||
}
|
||||
}
|
||||
|
||||
@DisplayName("js composite build works with yarn.lock persistence")
|
||||
@GradleTest
|
||||
@GradleTestVersions(minVersion = G_7_6)
|
||||
fun testJsCompositeBuildWithUpgradeYarnLock(gradleVersion: GradleVersion) {
|
||||
project("js-composite-build", gradleVersion) {
|
||||
buildGradleKts.modify(::transformBuildScriptWithPluginsDsl)
|
||||
|
||||
subProject("lib").apply {
|
||||
buildGradleKts.modify(::transformBuildScriptWithPluginsDsl)
|
||||
}
|
||||
|
||||
subProject("base").apply {
|
||||
buildGradleKts.modify(::transformBuildScriptWithPluginsDsl)
|
||||
}
|
||||
|
||||
build("kotlinUpgradeYarnLock") {
|
||||
assertTasksExecuted(":base:publicPackageJson")
|
||||
assertTasksExecuted(":lib:lib-2:publicPackageJson")
|
||||
assertTasksExecuted(":kotlinNpmInstall")
|
||||
assertTasksExecuted(":kotlinUpgradeYarnLock")
|
||||
}
|
||||
|
||||
build(":nodeTest") {
|
||||
assertTasksUpToDate(":base:publicPackageJson")
|
||||
assertTasksUpToDate(":lib:lib-2:publicPackageJson")
|
||||
assertTasksUpToDate(":kotlinNpmInstall")
|
||||
assertTasksExecuted(":kotlinStoreYarnLock")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@GradleTest
|
||||
fun testJsIrIncrementalInParallel(gradleVersion: GradleVersion) {
|
||||
project("kotlin-js-browser-project", gradleVersion) {
|
||||
@@ -1029,7 +1002,15 @@ class Kotlin2JsIrGradlePluginIT : KGPBaseTest() {
|
||||
@DisplayName("public NPM dependencies are included into package.json")
|
||||
@GradleTest
|
||||
fun testPackageJsonWithPublicNpmDependencies(gradleVersion: GradleVersion) {
|
||||
project("npm-dependencies", gradleVersion) {
|
||||
project(
|
||||
"npm-dependencies",
|
||||
gradleVersion,
|
||||
buildOptions = defaultBuildOptions.copy(
|
||||
jsOptions = defaultBuildOptions.jsOptions?.copy(
|
||||
yarn = false
|
||||
)
|
||||
)
|
||||
) {
|
||||
build("jsJar") {
|
||||
val archive = projectPath
|
||||
.resolve("build/libs")
|
||||
@@ -1097,8 +1078,8 @@ class Kotlin2JsIrGradlePluginIT : KGPBaseTest() {
|
||||
}
|
||||
}
|
||||
|
||||
build(UPGRADE_YARN_LOCK) {
|
||||
assertTasksExecuted(":$UPGRADE_YARN_LOCK")
|
||||
build(UPGRADE_PACKAGE_LOCK) {
|
||||
assertTasksExecuted(":$UPGRADE_PACKAGE_LOCK")
|
||||
}
|
||||
|
||||
build("jsJar") {
|
||||
@@ -1216,10 +1197,18 @@ class Kotlin2JsIrGradlePluginIT : KGPBaseTest() {
|
||||
}
|
||||
}
|
||||
|
||||
@DisplayName("yarn resolutions works")
|
||||
@DisplayName("npm overrides works")
|
||||
@GradleTest
|
||||
fun testYarnResolution(gradleVersion: GradleVersion) {
|
||||
project("kotlin-js-yarn-resolutions", gradleVersion) {
|
||||
fun testNpmOverrides(gradleVersion: GradleVersion) {
|
||||
project(
|
||||
"kotlin-js-npm-overrides",
|
||||
gradleVersion,
|
||||
buildOptions = defaultBuildOptions.copy(
|
||||
jsOptions = defaultBuildOptions.jsOptions?.copy(
|
||||
yarn = false
|
||||
)
|
||||
)
|
||||
) {
|
||||
build("packageJson", "rootPackageJson", "kotlinNpmInstall") {
|
||||
fun getPackageJson() =
|
||||
projectPath.resolve("build/js")
|
||||
@@ -1229,16 +1218,16 @@ class Kotlin2JsIrGradlePluginIT : KGPBaseTest() {
|
||||
}
|
||||
|
||||
val name = "lodash"
|
||||
val version = getPackageJson().resolutions?.get(name)
|
||||
val version = getPackageJson().overrides?.get(name)
|
||||
val requiredVersion = ">=1.0.0 <1.2.1 || >1.4.0 <2.0.0"
|
||||
assertTrue("Root package.json must have resolution $name with version $requiredVersion, but $version found") {
|
||||
assertTrue("Root package.json must override $name with version $requiredVersion, but $version found") {
|
||||
version == requiredVersion
|
||||
}
|
||||
|
||||
val react = "react"
|
||||
val reactVersion = getPackageJson().resolutions?.get(react)
|
||||
val reactVersion = getPackageJson().overrides?.get(react)
|
||||
val requiredReactVersion = "16.0.0"
|
||||
assertTrue("Root package.json must have resolution $react with version $requiredReactVersion, but $reactVersion found") {
|
||||
assertTrue("Root package.json must override $react with version $requiredReactVersion, but $reactVersion found") {
|
||||
reactVersion == requiredReactVersion
|
||||
}
|
||||
}
|
||||
@@ -1374,176 +1363,6 @@ class Kotlin2JsIrGradlePluginIT : KGPBaseTest() {
|
||||
}
|
||||
}
|
||||
|
||||
@DisplayName("Failing with yarn.lock update")
|
||||
@GradleTest
|
||||
fun testFailingWithYarnLockUpdate(gradleVersion: GradleVersion) {
|
||||
project("kotlin-js-yarn-lock-project", gradleVersion) {
|
||||
build(STORE_YARN_LOCK_NAME) {
|
||||
assertTasksExecuted(":$STORE_YARN_LOCK_NAME")
|
||||
}
|
||||
|
||||
projectPath.resolve("kotlin-js-store").deleteRecursively()
|
||||
|
||||
buildGradleKts.modify {
|
||||
it + "\n" +
|
||||
"""
|
||||
rootProject.plugins.withType(org.jetbrains.kotlin.gradle.targets.js.yarn.YarnPlugin::class.java) {
|
||||
rootProject.the<org.jetbrains.kotlin.gradle.targets.js.yarn.YarnRootExtension>().reportNewYarnLock = true
|
||||
}
|
||||
""".trimIndent()
|
||||
}
|
||||
|
||||
buildAndFail(STORE_YARN_LOCK_NAME) {
|
||||
assertTasksFailed(":$STORE_YARN_LOCK_NAME")
|
||||
}
|
||||
|
||||
buildGradleKts.modify {
|
||||
it + "\n" +
|
||||
"""
|
||||
dependencies {
|
||||
implementation(npm("decamelize", "6.0.0"))
|
||||
}
|
||||
|
||||
rootProject.plugins.withType(org.jetbrains.kotlin.gradle.targets.js.yarn.YarnPlugin::class.java) {
|
||||
rootProject.the<org.jetbrains.kotlin.gradle.targets.js.yarn.YarnRootExtension>().yarnLockMismatchReport =
|
||||
org.jetbrains.kotlin.gradle.targets.js.yarn.YarnLockMismatchReport.FAIL
|
||||
}
|
||||
""".trimIndent()
|
||||
}
|
||||
|
||||
buildAndFail(STORE_YARN_LOCK_NAME) {
|
||||
assertTasksFailed(":$STORE_YARN_LOCK_NAME")
|
||||
}
|
||||
|
||||
// yarn.lock was not updated
|
||||
buildAndFail(STORE_YARN_LOCK_NAME) {
|
||||
assertTasksFailed(":$STORE_YARN_LOCK_NAME")
|
||||
}
|
||||
|
||||
build(UPGRADE_YARN_LOCK) {
|
||||
assertTasksExecuted(":$UPGRADE_YARN_LOCK")
|
||||
}
|
||||
|
||||
buildGradleKts.modify {
|
||||
val replaced = it.replace("implementation(npm(\"decamelize\", \"6.0.0\"))", "")
|
||||
replaced + "\n" +
|
||||
"""
|
||||
rootProject.plugins.withType(org.jetbrains.kotlin.gradle.targets.js.yarn.YarnPlugin::class.java) {
|
||||
rootProject.the<org.jetbrains.kotlin.gradle.targets.js.yarn.YarnRootExtension>().yarnLockMismatchReport =
|
||||
org.jetbrains.kotlin.gradle.targets.js.yarn.YarnLockMismatchReport.WARNING
|
||||
}
|
||||
""".trimIndent()
|
||||
}
|
||||
|
||||
build(STORE_YARN_LOCK_NAME) {
|
||||
assertTasksExecuted(":$STORE_YARN_LOCK_NAME")
|
||||
|
||||
assertOutputContains(YARN_LOCK_MISMATCH_MESSAGE)
|
||||
}
|
||||
|
||||
build(UPGRADE_YARN_LOCK) {
|
||||
assertTasksExecuted(":$UPGRADE_YARN_LOCK")
|
||||
}
|
||||
|
||||
buildGradleKts.modify {
|
||||
it + "\n" +
|
||||
"""
|
||||
dependencies {
|
||||
implementation(npm("decamelize", "6.0.0"))
|
||||
}
|
||||
|
||||
rootProject.plugins.withType(org.jetbrains.kotlin.gradle.targets.js.yarn.YarnPlugin::class.java) {
|
||||
rootProject.the<org.jetbrains.kotlin.gradle.targets.js.yarn.YarnRootExtension>().yarnLockMismatchReport =
|
||||
org.jetbrains.kotlin.gradle.targets.js.yarn.YarnLockMismatchReport.NONE
|
||||
}
|
||||
""".trimIndent()
|
||||
}
|
||||
|
||||
build(STORE_YARN_LOCK_NAME) {
|
||||
assertTasksExecuted(":$STORE_YARN_LOCK_NAME")
|
||||
|
||||
assertOutputDoesNotContain(YARN_LOCK_MISMATCH_MESSAGE)
|
||||
}
|
||||
|
||||
build(UPGRADE_YARN_LOCK) {
|
||||
assertTasksExecuted(":$UPGRADE_YARN_LOCK")
|
||||
}
|
||||
|
||||
buildGradleKts.modify {
|
||||
it.replace("implementation(npm(\"decamelize\", \"6.0.0\"))", "")
|
||||
}
|
||||
|
||||
projectPath.resolve("kotlin-js-store").deleteRecursively()
|
||||
|
||||
buildGradleKts.modify {
|
||||
it + "\n" +
|
||||
"""
|
||||
rootProject.plugins.withType(org.jetbrains.kotlin.gradle.targets.js.yarn.YarnPlugin::class.java) {
|
||||
rootProject.the<org.jetbrains.kotlin.gradle.targets.js.yarn.YarnRootExtension>().yarnLockMismatchReport =
|
||||
org.jetbrains.kotlin.gradle.targets.js.yarn.YarnLockMismatchReport.NONE
|
||||
rootProject.the<org.jetbrains.kotlin.gradle.targets.js.yarn.YarnRootExtension>().reportNewYarnLock =
|
||||
true
|
||||
}
|
||||
""".trimIndent()
|
||||
}
|
||||
|
||||
build(STORE_YARN_LOCK_NAME) {
|
||||
assertTasksExecuted(":$STORE_YARN_LOCK_NAME")
|
||||
|
||||
assertOutputDoesNotContain(YARN_LOCK_MISMATCH_MESSAGE)
|
||||
}
|
||||
|
||||
buildGradleKts.modify {
|
||||
it + "\n" +
|
||||
"""
|
||||
dependencies {
|
||||
implementation(npm("decamelize", "6.0.0"))
|
||||
}
|
||||
|
||||
rootProject.plugins.withType(org.jetbrains.kotlin.gradle.targets.js.yarn.YarnPlugin::class.java) {
|
||||
rootProject.the<org.jetbrains.kotlin.gradle.targets.js.yarn.YarnRootExtension>().yarnLockMismatchReport =
|
||||
org.jetbrains.kotlin.gradle.targets.js.yarn.YarnLockMismatchReport.FAIL
|
||||
rootProject.the<org.jetbrains.kotlin.gradle.targets.js.yarn.YarnRootExtension>().yarnLockAutoReplace =
|
||||
true
|
||||
}
|
||||
""".trimIndent()
|
||||
}
|
||||
|
||||
buildAndFail(STORE_YARN_LOCK_NAME) {
|
||||
assertTasksFailed(":$STORE_YARN_LOCK_NAME")
|
||||
}
|
||||
|
||||
//yarn.lock was updated
|
||||
build(STORE_YARN_LOCK_NAME) {
|
||||
assertTasksExecuted(":$STORE_YARN_LOCK_NAME")
|
||||
}
|
||||
|
||||
buildGradleKts.modify {
|
||||
it.replace("implementation(npm(\"decamelize\", \"6.0.0\"))", "")
|
||||
}
|
||||
|
||||
// check if everything ok without build/js/yarn.lock
|
||||
build("clean") {
|
||||
assertTasksExecuted(":clean")
|
||||
}
|
||||
|
||||
build("clean") {
|
||||
assertTasksUpToDate(":clean")
|
||||
}
|
||||
|
||||
buildAndFail(STORE_YARN_LOCK_NAME) {
|
||||
assertTasksFailed(":$STORE_YARN_LOCK_NAME")
|
||||
}
|
||||
|
||||
projectPath.resolve("kotlin-js-store").deleteRecursively()
|
||||
|
||||
//check if independent tasks can be executed
|
||||
build("help") {
|
||||
assertTasksExecuted(":help")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@DisplayName("webpack configuration is valid")
|
||||
@GradleTest
|
||||
fun testWebpackConfig(gradleVersion: GradleVersion) {
|
||||
@@ -1622,7 +1441,15 @@ class Kotlin2JsIrGradlePluginIT : KGPBaseTest() {
|
||||
@DisplayName("Disable download should not download Node.JS and Yarn")
|
||||
@GradleTest
|
||||
fun testNodeJsAndYarnNotDownloaded(gradleVersion: GradleVersion) {
|
||||
project("nodeJsDownload", gradleVersion) {
|
||||
project(
|
||||
"nodeJsDownload",
|
||||
gradleVersion,
|
||||
buildOptions = defaultBuildOptions.copy(
|
||||
jsOptions = defaultBuildOptions.jsOptions?.copy(
|
||||
yarn = true
|
||||
)
|
||||
)
|
||||
) {
|
||||
buildGradleKts.modify {
|
||||
it + "\n" +
|
||||
"""
|
||||
@@ -1640,71 +1467,6 @@ class Kotlin2JsIrGradlePluginIT : KGPBaseTest() {
|
||||
}
|
||||
}
|
||||
|
||||
@DisplayName("Yarn.lock persistence")
|
||||
@GradleTest
|
||||
fun testYarnLockStore(gradleVersion: GradleVersion) {
|
||||
project("nodeJsDownload", gradleVersion) {
|
||||
build("assemble", "kotlinStoreYarnLock") {
|
||||
assertFileExists(projectPath.resolve("kotlin-js-store").resolve("yarn.lock"))
|
||||
assert(
|
||||
projectPath
|
||||
.resolve("kotlin-js-store")
|
||||
.resolve("yarn.lock")
|
||||
.readText() == projectPath.resolve("build/js/yarn.lock").readText()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@DisplayName("Yarn ignore scripts")
|
||||
@GradleTest
|
||||
fun testYarnIgnoreScripts(gradleVersion: GradleVersion) {
|
||||
project("nodeJsDownload", gradleVersion) {
|
||||
buildGradleKts.modify {
|
||||
it + "\n" +
|
||||
"""
|
||||
dependencies {
|
||||
implementation(npm("puppeteer", "11.0.0"))
|
||||
}
|
||||
""".trimIndent()
|
||||
}
|
||||
build("assemble", "kotlinNpmInstall") {
|
||||
assert(
|
||||
projectPath
|
||||
.resolve("build")
|
||||
.resolve("js")
|
||||
.resolve("node_modules")
|
||||
.resolve("puppeteer")
|
||||
.resolve(".local-chromium")
|
||||
.notExists()
|
||||
) {
|
||||
"Chromium should not be installed with --ignore-scripts"
|
||||
}
|
||||
}
|
||||
buildGradleKts.modify {
|
||||
it + "\n" +
|
||||
"""
|
||||
rootProject.plugins.withType<org.jetbrains.kotlin.gradle.targets.js.yarn.YarnPlugin> {
|
||||
rootProject.the<org.jetbrains.kotlin.gradle.targets.js.yarn.YarnRootExtension>().ignoreScripts = false
|
||||
}
|
||||
""".trimIndent()
|
||||
}
|
||||
|
||||
build("clean")
|
||||
|
||||
build("assemble", "kotlinNpmInstall") {
|
||||
assertDirectoryExists(
|
||||
projectPath
|
||||
.resolve("build")
|
||||
.resolve("js")
|
||||
.resolve("node_modules")
|
||||
.resolve("puppeteer")
|
||||
.resolve(".local-chromium")
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@DisplayName("js files from dependency are installed")
|
||||
@GradleTest
|
||||
fun testKotlinJsDependencyWithJsFiles(gradleVersion: GradleVersion) {
|
||||
@@ -1754,7 +1516,7 @@ class Kotlin2JsIrGradlePluginIT : KGPBaseTest() {
|
||||
}
|
||||
}
|
||||
|
||||
@DisplayName("test package.jsom skipped with main package.json was up-to-date")
|
||||
@DisplayName("test package.json skipped with main package.json was up-to-date")
|
||||
@GradleTest
|
||||
fun testPackageJsonSkippedWithUpToDatePackageJsons(gradleVersion: GradleVersion) {
|
||||
project("kotlin-js-browser-library-project", gradleVersion) {
|
||||
|
||||
+464
@@ -0,0 +1,464 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
import org.gradle.util.GradleVersion
|
||||
import org.jetbrains.kotlin.gradle.targets.js.npm.LockCopyTask
|
||||
import org.jetbrains.kotlin.gradle.targets.js.npm.LockCopyTask.Companion.PACKAGE_LOCK_MISMATCH_MESSAGE
|
||||
import org.jetbrains.kotlin.gradle.targets.js.npm.LockCopyTask.Companion.RESTORE_PACKAGE_LOCK_NAME
|
||||
import org.jetbrains.kotlin.gradle.targets.js.npm.LockCopyTask.Companion.STORE_PACKAGE_LOCK_NAME
|
||||
import org.jetbrains.kotlin.gradle.targets.js.npm.LockCopyTask.Companion.UPGRADE_PACKAGE_LOCK
|
||||
import org.jetbrains.kotlin.gradle.targets.js.yarn.YarnPlugin
|
||||
import org.jetbrains.kotlin.gradle.testbase.*
|
||||
import org.jetbrains.kotlin.gradle.testbase.TestVersions.Gradle.G_7_6
|
||||
import org.junit.jupiter.api.DisplayName
|
||||
import kotlin.io.path.deleteRecursively
|
||||
import kotlin.io.path.notExists
|
||||
import kotlin.io.path.readText
|
||||
|
||||
class NpmGradlePluginIT : PackageManagerGradlePluginIT() {
|
||||
override val yarn: Boolean = false
|
||||
|
||||
override val plugin = "nodejs.NodeJsRootPlugin"
|
||||
|
||||
override val extension = "npm.NpmExtension"
|
||||
|
||||
override val upgradeTaskName: String = UPGRADE_PACKAGE_LOCK
|
||||
|
||||
override val storeTaskName = STORE_PACKAGE_LOCK_NAME
|
||||
|
||||
override val restoreTaskName = RESTORE_PACKAGE_LOCK_NAME
|
||||
|
||||
override val reportNewLockFile = "reportNewPackageLock"
|
||||
|
||||
override val lockMismatchReport = "packageLockMismatchReport"
|
||||
|
||||
override val lockFileAutoReplace = "packageLockAutoReplace"
|
||||
|
||||
override val mismatchReportType = "npm.LockFileMismatchReport"
|
||||
|
||||
override val lockFileName: String = LockCopyTask.PACKAGE_LOCK
|
||||
|
||||
override val setProperty: (String) -> String = { ".set($it)" }
|
||||
}
|
||||
|
||||
class YarnGradlePluginIT : PackageManagerGradlePluginIT() {
|
||||
override val yarn: Boolean = true
|
||||
|
||||
override val plugin: String = "yarn.YarnPlugin"
|
||||
|
||||
override val extension: String = "yarn.YarnRootExtension"
|
||||
|
||||
override val upgradeTaskName: String = YarnPlugin.UPGRADE_YARN_LOCK
|
||||
|
||||
override val storeTaskName = YarnPlugin.STORE_YARN_LOCK_NAME
|
||||
|
||||
override val restoreTaskName: String = YarnPlugin.RESTORE_YARN_LOCK_NAME
|
||||
|
||||
override val reportNewLockFile: String = "reportNewYarnLock"
|
||||
|
||||
override val lockMismatchReport: String = "yarnLockMismatchReport"
|
||||
|
||||
override val lockFileAutoReplace: String = "yarnLockAutoReplace"
|
||||
|
||||
override val mismatchReportType: String = "yarn.YarnLockMismatchReport"
|
||||
|
||||
override val lockFileName: String = LockCopyTask.YARN_LOCK
|
||||
|
||||
override val setProperty: (String) -> String = { " = $it" }
|
||||
}
|
||||
|
||||
@JsGradlePluginTests
|
||||
abstract class PackageManagerGradlePluginIT : KGPBaseTest() {
|
||||
|
||||
abstract val yarn: Boolean
|
||||
|
||||
abstract val plugin: String
|
||||
|
||||
abstract val extension: String
|
||||
|
||||
abstract val upgradeTaskName: String
|
||||
|
||||
abstract val storeTaskName: String
|
||||
|
||||
abstract val restoreTaskName: String
|
||||
|
||||
abstract val reportNewLockFile: String
|
||||
|
||||
abstract val lockMismatchReport: String
|
||||
|
||||
abstract val lockFileAutoReplace: String
|
||||
|
||||
abstract val mismatchReportType: String
|
||||
|
||||
abstract val lockFileName: String
|
||||
|
||||
abstract val setProperty: (String) -> String
|
||||
|
||||
override val defaultBuildOptions: BuildOptions
|
||||
get() = super.defaultBuildOptions.copy(
|
||||
jsOptions = super.defaultBuildOptions.jsOptions?.copy(
|
||||
yarn = yarn
|
||||
)
|
||||
)
|
||||
|
||||
@DisplayName("js composite build works with lock file persistence")
|
||||
@GradleTest
|
||||
@GradleTestVersions(minVersion = G_7_6)
|
||||
fun testJsCompositeBuildWithUpgradeLockFile(gradleVersion: GradleVersion) {
|
||||
project("js-composite-build", gradleVersion) {
|
||||
testJsCompositeBuildWithUpgradeLockFile(
|
||||
upgradeTaskName,
|
||||
storeTaskName
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun TestProject.testJsCompositeBuildWithUpgradeLockFile(
|
||||
upgradeTask: String,
|
||||
storeTask: String,
|
||||
) {
|
||||
buildGradleKts.modify(::transformBuildScriptWithPluginsDsl)
|
||||
|
||||
subProject("lib").apply {
|
||||
buildGradleKts.modify(::transformBuildScriptWithPluginsDsl)
|
||||
}
|
||||
|
||||
subProject("base").apply {
|
||||
buildGradleKts.modify(::transformBuildScriptWithPluginsDsl)
|
||||
}
|
||||
|
||||
build(upgradeTask) {
|
||||
assertTasksExecuted(":base:publicPackageJson")
|
||||
assertTasksExecuted(":lib:lib-2:publicPackageJson")
|
||||
assertTasksExecuted(":kotlinNpmInstall")
|
||||
assertTasksExecuted(":$upgradeTask")
|
||||
}
|
||||
|
||||
build(":nodeTest") {
|
||||
assertTasksUpToDate(":base:publicPackageJson")
|
||||
assertTasksUpToDate(":lib:lib-2:publicPackageJson")
|
||||
assertTasksUpToDate(":kotlinNpmInstall")
|
||||
assertTasksExecuted(":$storeTask")
|
||||
}
|
||||
}
|
||||
|
||||
@DisplayName("Failing with lock file update")
|
||||
@GradleTest
|
||||
fun testFailingWithLockFileUpdate(gradleVersion: GradleVersion) {
|
||||
project("kotlin-js-package-lock-project", gradleVersion) {
|
||||
testFailingWithLockFileUpdate(
|
||||
storeTaskName = storeTaskName,
|
||||
restoreTaskName = restoreTaskName,
|
||||
upgradeTaskName = upgradeTaskName,
|
||||
extension = extension,
|
||||
reportNewLockFile = reportNewLockFile,
|
||||
lockMismatchReport = lockMismatchReport,
|
||||
lockFileAutoReplace = lockFileAutoReplace,
|
||||
mismatchReport = mismatchReportType,
|
||||
set = setProperty
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun TestProject.testFailingWithLockFileUpdate(
|
||||
storeTaskName: String,
|
||||
restoreTaskName: String,
|
||||
upgradeTaskName: String,
|
||||
extension: String,
|
||||
reportNewLockFile: String,
|
||||
lockMismatchReport: String,
|
||||
lockFileAutoReplace: String,
|
||||
mismatchReport: String,
|
||||
set: (String) -> String,
|
||||
) {
|
||||
build(storeTaskName) {
|
||||
assertTasksSkipped(":$restoreTaskName")
|
||||
assertTasksExecuted(":$storeTaskName")
|
||||
}
|
||||
|
||||
projectPath.resolve(LockCopyTask.KOTLIN_JS_STORE).deleteRecursively()
|
||||
|
||||
buildGradleKts.modify {
|
||||
it + "\n" +
|
||||
"""
|
||||
rootProject.plugins.withType(org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootPlugin::class.java) {
|
||||
rootProject.the<org.jetbrains.kotlin.gradle.targets.js.$extension>().$reportNewLockFile${set("true")}
|
||||
}
|
||||
""".trimIndent()
|
||||
}
|
||||
|
||||
buildAndFail(storeTaskName) {
|
||||
assertTasksSkipped(":$restoreTaskName")
|
||||
assertTasksFailed(":$storeTaskName")
|
||||
}
|
||||
|
||||
buildGradleKts.modify {
|
||||
it + "\n" +
|
||||
"""
|
||||
dependencies {
|
||||
implementation(npm("decamelize", "6.0.0"))
|
||||
}
|
||||
|
||||
rootProject.plugins.withType(org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootPlugin::class.java) {
|
||||
rootProject.the<org.jetbrains.kotlin.gradle.targets.js.$extension>().$lockMismatchReport${
|
||||
set(
|
||||
"org.jetbrains.kotlin.gradle.targets.js.$mismatchReport.FAIL"
|
||||
)
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
}
|
||||
|
||||
buildAndFail(storeTaskName) {
|
||||
assertTasksExecuted(":$restoreTaskName")
|
||||
assertTasksFailed(":$storeTaskName")
|
||||
}
|
||||
|
||||
// yarn.lock was not updated
|
||||
buildAndFail(storeTaskName) {
|
||||
assertTasksExecuted(":$restoreTaskName")
|
||||
assertTasksFailed(":$storeTaskName")
|
||||
}
|
||||
|
||||
build(upgradeTaskName) {
|
||||
assertTasksExecuted(":$restoreTaskName")
|
||||
assertTasksExecuted(":$upgradeTaskName")
|
||||
}
|
||||
|
||||
buildGradleKts.modify {
|
||||
val replaced = it.replace("implementation(npm(\"decamelize\", \"6.0.0\"))", "")
|
||||
replaced + "\n" +
|
||||
"""
|
||||
rootProject.plugins.withType(org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootPlugin::class.java) {
|
||||
rootProject.the<org.jetbrains.kotlin.gradle.targets.js.$extension>().$lockMismatchReport${
|
||||
set(
|
||||
"org.jetbrains.kotlin.gradle.targets.js.$mismatchReport.WARNING"
|
||||
)
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
}
|
||||
|
||||
build(storeTaskName) {
|
||||
assertTasksExecuted(":$restoreTaskName")
|
||||
assertTasksExecuted(":$storeTaskName")
|
||||
|
||||
assertOutputContains(PACKAGE_LOCK_MISMATCH_MESSAGE)
|
||||
}
|
||||
|
||||
build(upgradeTaskName) {
|
||||
assertTasksExecuted(":$restoreTaskName")
|
||||
assertTasksExecuted(":$upgradeTaskName")
|
||||
}
|
||||
|
||||
buildGradleKts.modify {
|
||||
it + "\n" +
|
||||
"""
|
||||
dependencies {
|
||||
implementation(npm("decamelize", "6.0.0"))
|
||||
}
|
||||
|
||||
rootProject.plugins.withType(org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootPlugin::class.java) {
|
||||
rootProject.the<org.jetbrains.kotlin.gradle.targets.js.$extension>().$lockMismatchReport${
|
||||
set(
|
||||
"org.jetbrains.kotlin.gradle.targets.js.$mismatchReport.NONE"
|
||||
)
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
}
|
||||
|
||||
build(storeTaskName) {
|
||||
assertTasksExecuted(":$restoreTaskName")
|
||||
assertTasksExecuted(":$storeTaskName")
|
||||
|
||||
assertOutputDoesNotContain(PACKAGE_LOCK_MISMATCH_MESSAGE)
|
||||
}
|
||||
|
||||
build(upgradeTaskName) {
|
||||
assertTasksExecuted(":$restoreTaskName")
|
||||
assertTasksExecuted(":$upgradeTaskName")
|
||||
}
|
||||
|
||||
buildGradleKts.modify {
|
||||
it.replace("implementation(npm(\"decamelize\", \"6.0.0\"))", "")
|
||||
}
|
||||
|
||||
projectPath.resolve(LockCopyTask.KOTLIN_JS_STORE).deleteRecursively()
|
||||
|
||||
buildGradleKts.modify {
|
||||
it + "\n" +
|
||||
"""
|
||||
rootProject.plugins.withType(org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootPlugin::class.java) {
|
||||
rootProject.the<org.jetbrains.kotlin.gradle.targets.js.$extension>().$lockMismatchReport${
|
||||
set(
|
||||
"org.jetbrains.kotlin.gradle.targets.js.$mismatchReport.NONE"
|
||||
)
|
||||
}
|
||||
rootProject.the<org.jetbrains.kotlin.gradle.targets.js.$extension>().$reportNewLockFile${
|
||||
set(
|
||||
"true"
|
||||
)
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
}
|
||||
|
||||
build(storeTaskName) {
|
||||
assertTasksSkipped(":$restoreTaskName")
|
||||
assertTasksExecuted(":$storeTaskName")
|
||||
|
||||
assertOutputDoesNotContain(PACKAGE_LOCK_MISMATCH_MESSAGE)
|
||||
}
|
||||
|
||||
buildGradleKts.modify {
|
||||
it + "\n" +
|
||||
"""
|
||||
dependencies {
|
||||
implementation(npm("decamelize", "6.0.0"))
|
||||
}
|
||||
|
||||
rootProject.plugins.withType(org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootPlugin::class.java) {
|
||||
rootProject.the<org.jetbrains.kotlin.gradle.targets.js.$extension>().$lockMismatchReport${
|
||||
set(
|
||||
"org.jetbrains.kotlin.gradle.targets.js.$mismatchReport.FAIL"
|
||||
)
|
||||
}
|
||||
rootProject.the<org.jetbrains.kotlin.gradle.targets.js.$extension>().$lockFileAutoReplace${
|
||||
set(
|
||||
"true"
|
||||
)
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
}
|
||||
|
||||
buildAndFail(storeTaskName) {
|
||||
assertTasksUpToDate(":$restoreTaskName")
|
||||
assertTasksFailed(":$storeTaskName")
|
||||
}
|
||||
|
||||
//yarn.lock was updated
|
||||
build(storeTaskName) {
|
||||
assertTasksExecuted(":$restoreTaskName")
|
||||
assertTasksExecuted(":$storeTaskName")
|
||||
}
|
||||
|
||||
buildGradleKts.modify {
|
||||
it.replace("implementation(npm(\"decamelize\", \"6.0.0\"))", "")
|
||||
}
|
||||
|
||||
// check if everything ok without build/js/yarn.lock
|
||||
build("clean") {
|
||||
assertTasksExecuted(":clean")
|
||||
}
|
||||
|
||||
build("clean") {
|
||||
assertTasksUpToDate(":clean")
|
||||
}
|
||||
|
||||
buildAndFail(storeTaskName) {
|
||||
assertTasksExecuted(":$restoreTaskName")
|
||||
assertTasksFailed(":$storeTaskName")
|
||||
}
|
||||
|
||||
projectPath.resolve(LockCopyTask.KOTLIN_JS_STORE).deleteRecursively()
|
||||
|
||||
//check if independent tasks can be executed
|
||||
build("help") {
|
||||
assertTasksExecuted(":help")
|
||||
}
|
||||
}
|
||||
|
||||
@DisplayName("Lock file persistence")
|
||||
@GradleTest
|
||||
fun testLockStore(gradleVersion: GradleVersion) {
|
||||
project("nodeJsDownload", gradleVersion) {
|
||||
testLockStore(
|
||||
storeTaskName,
|
||||
lockFileName
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun TestProject.testLockStore(
|
||||
taskName: String,
|
||||
lockFile: String,
|
||||
) {
|
||||
build("assemble", taskName) {
|
||||
assertFileExists(projectPath.resolve(LockCopyTask.KOTLIN_JS_STORE).resolve(lockFile))
|
||||
assert(
|
||||
projectPath
|
||||
.resolve(LockCopyTask.KOTLIN_JS_STORE)
|
||||
.resolve(lockFile)
|
||||
.readText() == projectPath.resolve("build/js/${lockFile}").readText()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@DisplayName("Package manager ignore scripts")
|
||||
@GradleTest
|
||||
fun testIgnoreScripts(gradleVersion: GradleVersion) {
|
||||
project("nodeJsDownload", gradleVersion) {
|
||||
testIgnoreScripts(
|
||||
plugin,
|
||||
extension,
|
||||
setProperty
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun TestProject.testIgnoreScripts(
|
||||
plugin: String,
|
||||
extension: String,
|
||||
set: (String) -> String,
|
||||
) {
|
||||
buildGradleKts.modify {
|
||||
it + "\n" +
|
||||
"""
|
||||
dependencies {
|
||||
implementation(npm("puppeteer", "11.0.0"))
|
||||
}
|
||||
""".trimIndent()
|
||||
}
|
||||
build("assemble", "kotlinNpmInstall") {
|
||||
assert(
|
||||
projectPath
|
||||
.resolve("build")
|
||||
.resolve("js")
|
||||
.resolve("node_modules")
|
||||
.resolve("puppeteer")
|
||||
.resolve(".local-chromium")
|
||||
.notExists()
|
||||
) {
|
||||
"Chromium should not be installed with --ignore-scripts"
|
||||
}
|
||||
}
|
||||
|
||||
buildGradleKts.modify {
|
||||
it + "\n" +
|
||||
"""
|
||||
rootProject.plugins.withType<org.jetbrains.kotlin.gradle.targets.js.$plugin> {
|
||||
rootProject.the<org.jetbrains.kotlin.gradle.targets.js.$extension>().ignoreScripts${set("false")}
|
||||
}
|
||||
""".trimIndent()
|
||||
}
|
||||
|
||||
build("clean")
|
||||
|
||||
build("assemble", "kotlinNpmInstall") {
|
||||
assertDirectoryExists(
|
||||
projectPath
|
||||
.resolve("build")
|
||||
.resolve("js")
|
||||
.resolve("node_modules")
|
||||
.resolve("puppeteer")
|
||||
.resolve(".local-chromium")
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
+4
-2
@@ -37,7 +37,7 @@ data class BuildOptions(
|
||||
val buildCacheEnabled: Boolean = false,
|
||||
val kaptOptions: KaptOptions? = null,
|
||||
val androidVersion: String? = null,
|
||||
val jsOptions: JsOptions? = null,
|
||||
val jsOptions: JsOptions? = JsOptions(),
|
||||
val buildReport: List<BuildReportType> = emptyList(),
|
||||
val usePreciseJavaTracking: Boolean? = null,
|
||||
val languageVersion: String? = null,
|
||||
@@ -79,7 +79,8 @@ data class BuildOptions(
|
||||
data class JsOptions(
|
||||
val incrementalJs: Boolean? = null,
|
||||
val incrementalJsKlib: Boolean? = null,
|
||||
val incrementalJsIr: Boolean? = null
|
||||
val incrementalJsIr: Boolean? = null,
|
||||
val yarn: Boolean? = null,
|
||||
)
|
||||
|
||||
data class NativeOptions(
|
||||
@@ -163,6 +164,7 @@ data class BuildOptions(
|
||||
jsOptions.incrementalJs?.let { arguments.add("-Pkotlin.incremental.js=$it") }
|
||||
jsOptions.incrementalJsKlib?.let { arguments.add("-Pkotlin.incremental.js.klib=$it") }
|
||||
jsOptions.incrementalJsIr?.let { arguments.add("-Pkotlin.incremental.js.ir=$it") }
|
||||
jsOptions.yarn?.let { arguments.add("-Pkotlin.js.yarn=$it") }
|
||||
}
|
||||
|
||||
if (androidVersion != null) {
|
||||
|
||||
+4
@@ -1,3 +1,5 @@
|
||||
import org.jetbrains.kotlin.gradle.targets.js.yarn.yarn
|
||||
|
||||
plugins {
|
||||
kotlin("js")
|
||||
}
|
||||
@@ -10,6 +12,8 @@ kotlin {
|
||||
}
|
||||
}
|
||||
|
||||
yarn
|
||||
|
||||
rootProject.plugins.withType<org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootPlugin> {
|
||||
rootProject.the<org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootExtension>().downloadBaseUrl = null
|
||||
}
|
||||
|
||||
+4
-4
@@ -10,13 +10,13 @@ repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
rootProject.plugins.withType(org.jetbrains.kotlin.gradle.targets.js.yarn.YarnPlugin::class.java) {
|
||||
rootProject.the<org.jetbrains.kotlin.gradle.targets.js.yarn.YarnRootExtension>().apply {
|
||||
resolution("lodash") {
|
||||
rootProject.plugins.withType(org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootPlugin::class.java) {
|
||||
rootProject.the<org.jetbrains.kotlin.gradle.targets.js.npm.NpmExtension>().apply {
|
||||
override("lodash") {
|
||||
include("^1.0.0")
|
||||
exclude("~1.2.1", "1.3.0 - 1.4.0")
|
||||
}
|
||||
resolution("react", "16.0.0")
|
||||
override("react", "16.0.0")
|
||||
}
|
||||
}
|
||||
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
rootProject.name = "kotlin-js-npm-overrides"
|
||||
|
||||
include("base")
|
||||
include("lib")
|
||||
-4
@@ -1,4 +0,0 @@
|
||||
rootProject.name = "kotlin-js-yarn-resolutions"
|
||||
|
||||
include("base")
|
||||
include("lib")
|
||||
+2
-2
@@ -25,9 +25,9 @@ allprojects.forEach {
|
||||
}
|
||||
}
|
||||
|
||||
tasks.named<org.jetbrains.kotlin.gradle.targets.js.yarn.YarnLockCopyTask>("kotlinStoreYarnLock") {
|
||||
tasks.named<org.jetbrains.kotlin.gradle.targets.js.npm.LockCopyTask>("kotlinStorePackageLock") {
|
||||
//A little hacky way to make yarn results
|
||||
inputFile.fileValue(projectDir.resolve("yarnLockStub"))
|
||||
inputFile.fileValue(projectDir.resolve("packageLockStub"))
|
||||
}
|
||||
|
||||
kotlin {
|
||||
|
||||
+2
-2
@@ -5,9 +5,9 @@ plugins {
|
||||
rootProject.plugins.withType<org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootPlugin> {
|
||||
rootProject.the<org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootExtension>().nodeVersion = "20.2.0"
|
||||
|
||||
tasks.named<org.jetbrains.kotlin.gradle.targets.js.yarn.YarnLockCopyTask>("kotlinStoreYarnLock") {
|
||||
tasks.named<org.jetbrains.kotlin.gradle.targets.js.npm.LockCopyTask>("kotlinStorePackageLock") {
|
||||
//A little hacky way to make yarn results
|
||||
inputFile.fileValue(projectDir.resolve("yarnLockStub"))
|
||||
inputFile.fileValue(projectDir.resolve("packageLockStub"))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -20,9 +20,9 @@ allprojects.forEach {
|
||||
}
|
||||
}
|
||||
|
||||
tasks.named<org.jetbrains.kotlin.gradle.targets.js.yarn.YarnLockCopyTask>("kotlinStoreYarnLock") {
|
||||
tasks.named<org.jetbrains.kotlin.gradle.targets.js.npm.LockCopyTask>("kotlinStorePackageLock") {
|
||||
//A little hacky way to make yarn results
|
||||
inputFile.fileValue(projectDir.resolve("yarnLockStub"))
|
||||
inputFile.fileValue(projectDir.resolve("packageLockStub"))
|
||||
}
|
||||
|
||||
repositories {
|
||||
|
||||
-4
@@ -5,15 +5,11 @@
|
||||
|
||||
package org.jetbrains.kotlin.gradle.internal
|
||||
|
||||
import jetbrains.buildServer.messages.serviceMessages.ServiceMessageParserCallback
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.internal.project.ProjectInternal
|
||||
import org.gradle.internal.logging.progress.ProgressLogger
|
||||
import org.gradle.internal.service.ServiceRegistry
|
||||
import org.gradle.process.ExecResult
|
||||
import org.gradle.process.internal.ExecAction
|
||||
import org.gradle.process.internal.ExecActionFactory
|
||||
import org.jetbrains.kotlin.gradle.internal.testing.TCServiceMessageOutputStreamHandler
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.io.PipedInputStream
|
||||
import java.io.PipedOutputStream
|
||||
|
||||
+5
@@ -26,6 +26,7 @@ import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLI
|
||||
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLIN_INTERNAL_DIAGNOSTICS_USE_PARSABLE_FORMATTING
|
||||
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLIN_JS_KARMA_BROWSERS
|
||||
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLIN_JS_STDLIB_DOM_API_INCLUDED
|
||||
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLIN_JS_YARN
|
||||
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLIN_MPP_ALLOW_LEGACY_DEPENDENCIES
|
||||
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLIN_MPP_ANDROID_GRADLE_PLUGIN_COMPATIBILITY_NO_WARN
|
||||
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLIN_MPP_ANDROID_SOURCE_SET_LAYOUT_ANDROID_STYLE_NO_WARN
|
||||
@@ -418,6 +419,9 @@ internal class PropertiesProvider private constructor(private val project: Proje
|
||||
val stdlibDomApiIncluded: Boolean
|
||||
get() = booleanProperty(KOTLIN_JS_STDLIB_DOM_API_INCLUDED) ?: true
|
||||
|
||||
val yarn: Boolean
|
||||
get() = booleanProperty(KOTLIN_JS_YARN) ?: true
|
||||
|
||||
val kotlinTestInferJvmVariant: Boolean
|
||||
get() = booleanProperty("kotlin.test.infer.jvm.variant") ?: true
|
||||
|
||||
@@ -613,6 +617,7 @@ internal class PropertiesProvider private constructor(private val project: Proje
|
||||
val KOTLIN_STDLIB_DEFAULT_DEPENDENCY = property("kotlin.stdlib.default.dependency")
|
||||
val KOTLIN_STDLIB_JDK_VARIANTS_VERSION_ALIGNMENT = property("kotlin.stdlib.jdk.variants.version.alignment")
|
||||
val KOTLIN_JS_STDLIB_DOM_API_INCLUDED = property("kotlin.js.stdlib.dom.api.included")
|
||||
val KOTLIN_JS_YARN = property("kotlin.js.yarn")
|
||||
val KOTLIN_MPP_ENABLE_GRANULAR_SOURCE_SETS_METADATA = property("kotlin.mpp.enableGranularSourceSetsMetadata")
|
||||
val KOTLIN_MPP_ENABLE_COMPATIBILITY_METADATA_VARIANT = property("kotlin.mpp.enableCompatibilityMetadataVariant")
|
||||
val KOTLIN_MPP_ENABLE_CINTEROP_COMMONIZATION = property("kotlin.mpp.enableCInteropCommonization")
|
||||
|
||||
+11
-4
@@ -138,9 +138,10 @@ abstract class AbstractSetupTask<Env : AbstractEnv, Settings : AbstractSettings<
|
||||
if (file.exists()) {
|
||||
file.useLines { seq ->
|
||||
val list = seq.first().split(" ")
|
||||
list.size == 2 &&
|
||||
list[0] == fileHasher.calculateDirHash(destination) &&
|
||||
list[1] == fileHasher.hash(dist).toByteArray().toHex().also { distHash = it }
|
||||
list.size == 3 &&
|
||||
list[0] == CACHE_VERSION &&
|
||||
list[1] == fileHasher.calculateDirHash(destination) &&
|
||||
list[2] == fileHasher.hash(dist).toByteArray().toHex().also { distHash = it }
|
||||
}
|
||||
} else false
|
||||
}
|
||||
@@ -156,11 +157,17 @@ abstract class AbstractSetupTask<Env : AbstractEnv, Settings : AbstractSettings<
|
||||
extract(dist, destination.parentFile)
|
||||
|
||||
destinationHashFile.writeText(
|
||||
fileHasher.calculateDirHash(destination)!! +
|
||||
CACHE_VERSION +
|
||||
" " +
|
||||
fileHasher.calculateDirHash(destination)!! +
|
||||
" " +
|
||||
(distHash ?: fileHasher.hash(dist).toByteArray().toHex())
|
||||
)
|
||||
}
|
||||
|
||||
abstract fun extract(archive: File, destination: File)
|
||||
|
||||
companion object {
|
||||
const val CACHE_VERSION = "2"
|
||||
}
|
||||
}
|
||||
|
||||
+3
-2
@@ -52,9 +52,9 @@ abstract class KotlinBrowserJsIr @Inject constructor(target: KotlinJsIrTarget) :
|
||||
override fun configureTestDependencies(test: KotlinJsTest) {
|
||||
test.dependsOn(
|
||||
nodeJs.npmInstallTaskProvider,
|
||||
nodeJs.storeYarnLockTaskProvider,
|
||||
nodeJs.nodeJsSetupTaskProvider
|
||||
)
|
||||
test.dependsOn(nodeJs.packageManagerExtension.map { it.postInstallTasks })
|
||||
}
|
||||
|
||||
override fun configureDefaultTestFramework(test: KotlinJsTest) {
|
||||
@@ -267,10 +267,11 @@ abstract class KotlinBrowserJsIr @Inject constructor(target: KotlinJsIrTarget) :
|
||||
) {
|
||||
dependsOn(
|
||||
nodeJs.npmInstallTaskProvider,
|
||||
nodeJs.storeYarnLockTaskProvider,
|
||||
target.project.tasks.named(compilation.processResourcesTaskName)
|
||||
)
|
||||
|
||||
dependsOn(nodeJs.packageManagerExtension.map { it.postInstallTasks })
|
||||
|
||||
configureOptimization(mode)
|
||||
|
||||
this.inputFilesDirectory.set(inputFilesDirectory)
|
||||
|
||||
+1
-1
@@ -62,8 +62,8 @@ abstract class KotlinNodeJsIr @Inject constructor(target: KotlinJsIrTarget) :
|
||||
if (target.wasmTargetType != KotlinWasmTargetType.WASI) {
|
||||
test.dependsOn(
|
||||
nodeJsTaskProviders.npmInstallTaskProvider,
|
||||
nodeJsTaskProviders.storeYarnLockTaskProvider,
|
||||
)
|
||||
test.dependsOn(nodeJs.packageManagerExtension.map { it.postInstallTasks })
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
package org.jetbrains.kotlin.gradle.targets.js.nodejs
|
||||
|
||||
import org.jetbrains.kotlin.gradle.targets.js.AbstractEnv
|
||||
import org.jetbrains.kotlin.gradle.targets.js.npm.NpmApi
|
||||
import org.jetbrains.kotlin.gradle.targets.js.npm.NpmApiExecution
|
||||
import org.jetbrains.kotlin.gradle.tasks.internal.CleanableStore
|
||||
import java.io.File
|
||||
|
||||
@@ -17,7 +17,7 @@ data class NodeJsEnv(
|
||||
override val ivyDependency: String,
|
||||
override val downloadBaseUrl: String?,
|
||||
|
||||
val packageManager: NpmApi,
|
||||
val packageManager: NpmApiExecution<PackageManagerEnvironment>,
|
||||
) : AbstractEnv {
|
||||
val isWindows: Boolean
|
||||
get() = platformName == "win"
|
||||
|
||||
+1
-1
@@ -113,8 +113,8 @@ constructor(
|
||||
it.workingDir(npmProject.dir)
|
||||
it.dependsOn(
|
||||
nodeJsTaskProviders.npmInstallTaskProvider,
|
||||
nodeJsTaskProviders.storeYarnLockTaskProvider,
|
||||
)
|
||||
it.dependsOn(nodeJs.packageManagerExtension.map { it.postInstallTasks })
|
||||
}
|
||||
it.dependsOn(nodeJsTaskProviders.nodeJsSetupTaskProvider)
|
||||
it.dependsOn(compilation.compileKotlinTaskProvider)
|
||||
|
||||
+14
-11
@@ -14,14 +14,14 @@ import org.jetbrains.kotlin.gradle.logging.kotlinInfo
|
||||
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider
|
||||
import org.jetbrains.kotlin.gradle.targets.js.AbstractSettings
|
||||
import org.jetbrains.kotlin.gradle.targets.js.NpmVersions
|
||||
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.YarnLockStoreTask
|
||||
import org.jetbrains.kotlin.gradle.targets.js.yarn.yarn
|
||||
import org.jetbrains.kotlin.gradle.tasks.internal.CleanableStore
|
||||
import org.jetbrains.kotlin.gradle.utils.getFile
|
||||
import org.jetbrains.kotlin.gradle.utils.property
|
||||
@@ -80,7 +80,7 @@ open class NodeJsRootExtension(
|
||||
// Release schedule: https://github.com/nodejs/Release
|
||||
// Actual LTS and Current versions: https://nodejs.org/en/download/
|
||||
// Older versions and more information, e.g. V8 version inside: https://nodejs.org/en/download/releases/
|
||||
override var version by Property("18.12.1")
|
||||
override var version by Property("20.10.0")
|
||||
|
||||
override var command by Property("node")
|
||||
|
||||
@@ -91,7 +91,7 @@ open class NodeJsRootExtension(
|
||||
command = value
|
||||
}
|
||||
|
||||
var packageManager: NpmApi by Property(Yarn())
|
||||
val packageManagerExtension: org.gradle.api.provider.Property<NpmApiExt> = project.objects.property()
|
||||
|
||||
val taskRequirements: TasksRequirements
|
||||
get() = resolver.tasksRequirements
|
||||
@@ -135,7 +135,7 @@ open class NodeJsRootExtension(
|
||||
val name = platform.get().name
|
||||
val architecture = platform.get().arch
|
||||
|
||||
val nodeDirName = "node-v$nodeVersion-$name-$architecture"
|
||||
val nodeDirName = "node-v$version-$name-$architecture"
|
||||
val cleanableStore = CleanableStore[installationDir.absolutePath]
|
||||
val nodeDir = cleanableStore[nodeDirName].use()
|
||||
val isWindows = platform.get().isWindows()
|
||||
@@ -148,21 +148,23 @@ open class NodeJsRootExtension(
|
||||
|
||||
fun getIvyDependency(): String {
|
||||
val type = if (isWindows) "zip" else "tar.gz"
|
||||
return "org.nodejs:node:$nodeVersion:$name-$architecture@$type"
|
||||
return "org.nodejs:node:$version:$name-$architecture@$type"
|
||||
}
|
||||
|
||||
packageManagerExtension.disallowChanges()
|
||||
|
||||
return NodeJsEnv(
|
||||
download = download,
|
||||
cleanableStore = cleanableStore,
|
||||
rootPackageDir = rootPackageDirectory.getFile(),
|
||||
dir = nodeDir,
|
||||
nodeBinDir = nodeBinDir,
|
||||
nodeExecutable = getExecutable("node", nodeCommand, "exe"),
|
||||
nodeExecutable = getExecutable("node", command, "exe"),
|
||||
platformName = name,
|
||||
architectureName = architecture,
|
||||
ivyDependency = getIvyDependency(),
|
||||
downloadBaseUrl = nodeDownloadBaseUrl,
|
||||
packageManager = packageManager
|
||||
downloadBaseUrl = downloadBaseUrl,
|
||||
packageManager = packageManagerExtension.get().packageManager
|
||||
)
|
||||
}
|
||||
|
||||
@@ -181,8 +183,9 @@ open class NodeJsRootExtension(
|
||||
val npmCachesSetupTaskProvider: TaskProvider<out KotlinNpmCachesSetup>
|
||||
get() = project.tasks.withType(KotlinNpmCachesSetup::class.java).named(KotlinNpmCachesSetup.NAME)
|
||||
|
||||
val storeYarnLockTaskProvider: TaskProvider<out YarnLockCopyTask>
|
||||
get() = project.tasks.withType(YarnLockCopyTask::class.java).named(YarnLockCopyTask.STORE_YARN_LOCK_NAME)
|
||||
@Deprecated("This is deprecated and will be removed. Use corresponding property from YarnRootExtension")
|
||||
val storeYarnLockTaskProvider: TaskProvider<YarnLockStoreTask>
|
||||
get() = project.yarn.storeYarnLockTaskProvider
|
||||
|
||||
companion object {
|
||||
const val EXTENSION_NAME: String = "kotlinNodeJs"
|
||||
|
||||
+186
-35
@@ -8,24 +8,29 @@ package org.jetbrains.kotlin.gradle.targets.js.nodejs
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.Task
|
||||
import org.gradle.api.file.RegularFile
|
||||
import org.gradle.api.plugins.BasePlugin
|
||||
import org.gradle.api.provider.Provider
|
||||
import org.gradle.api.tasks.TaskProvider
|
||||
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider
|
||||
import org.jetbrains.kotlin.gradle.plugin.internal.configurationTimePropertiesAccessor
|
||||
import org.jetbrains.kotlin.gradle.plugin.internal.usedAtConfigurationTime
|
||||
import org.jetbrains.kotlin.gradle.plugin.variantImplementationFactory
|
||||
import org.jetbrains.kotlin.gradle.targets.js.MultiplePluginDeclarationDetector
|
||||
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.UsesKotlinNpmResolutionManager
|
||||
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.npm.resolver.PACKAGE_JSON_UMBRELLA_TASK_NAME
|
||||
import org.jetbrains.kotlin.gradle.targets.js.npm.resolver.implementing
|
||||
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.YarnPlugin
|
||||
import org.jetbrains.kotlin.gradle.tasks.CleanDataTask
|
||||
import org.jetbrains.kotlin.gradle.tasks.registerTask
|
||||
import org.jetbrains.kotlin.gradle.tasks.withType
|
||||
import org.jetbrains.kotlin.gradle.utils.*
|
||||
import org.jetbrains.kotlin.gradle.utils.castIsolatedKotlinPluginClassLoaderAware
|
||||
import org.jetbrains.kotlin.gradle.utils.getFile
|
||||
import org.jetbrains.kotlin.gradle.utils.onlyIfCompat
|
||||
import org.jetbrains.kotlin.gradle.utils.providerWithLazyConvention
|
||||
|
||||
open class NodeJsRootPlugin : Plugin<Project> {
|
||||
override fun apply(project: Project) {
|
||||
@@ -43,8 +48,24 @@ open class NodeJsRootPlugin : Plugin<Project> {
|
||||
project
|
||||
)
|
||||
|
||||
val npm = project.extensions.create(
|
||||
NpmExtension.EXTENSION_NAME,
|
||||
NpmExtension::class.java,
|
||||
project
|
||||
)
|
||||
|
||||
addPlatform(project, nodeJs)
|
||||
|
||||
npm.nodeJsEnvironment.value(
|
||||
project.provider {
|
||||
nodeJs.requireConfigured()
|
||||
}
|
||||
).disallowChanges()
|
||||
|
||||
nodeJs.packageManagerExtension.convention(
|
||||
npm
|
||||
)
|
||||
|
||||
val setupTask = project.registerTask<NodeJsSetupTask>(NodeJsSetupTask.NAME) {
|
||||
it.group = TASKS_GROUP_NAME
|
||||
it.description = "Download and install a local node/npm version"
|
||||
@@ -97,21 +118,125 @@ open class NodeJsRootPlugin : Plugin<Project> {
|
||||
|
||||
val objectFactory = project.objects
|
||||
|
||||
// TODO: Could we use common approach with build services to KotlinNpmResolutionManager?
|
||||
val npmResolutionManager = project.gradle.sharedServices.registerIfAbsent(
|
||||
KotlinNpmResolutionManager::class.java.name,
|
||||
KotlinNpmResolutionManager::class.java
|
||||
) {
|
||||
it.parameters.resolution.set(
|
||||
objectFactory.providerWithLazyConvention {
|
||||
nodeJs.resolver.close()
|
||||
}
|
||||
)
|
||||
it.parameters.gradleNodeModulesProvider.set(gradleNodeModulesProvider)
|
||||
it.parameters.packagesDir.set(nodeJs.projectPackagesDirectory)
|
||||
val npmResolutionManager: Provider<KotlinNpmResolutionManager> = KotlinNpmResolutionManager.registerIfAbsent(
|
||||
project,
|
||||
objectFactory.providerWithLazyConvention {
|
||||
nodeJs.resolver.close()
|
||||
},
|
||||
gradleNodeModulesProvider,
|
||||
nodeJs.projectPackagesDirectory
|
||||
)
|
||||
|
||||
val rootPackageJson = project.tasks.register(RootPackageJsonTask.NAME, RootPackageJsonTask::class.java) { task ->
|
||||
task.dependsOn(nodeJs.npmCachesSetupTaskProvider)
|
||||
task.group = TASKS_GROUP_NAME
|
||||
task.description = "Create root package.json"
|
||||
|
||||
task.npmResolutionManager.value(npmResolutionManager)
|
||||
.disallowChanges()
|
||||
|
||||
task.onlyIfCompat("Prepare NPM project only in configuring state") {
|
||||
it as RootPackageJsonTask
|
||||
it.npmResolutionManager.get().isConfiguringState()
|
||||
}
|
||||
}
|
||||
|
||||
YarnPlugin.apply(project)
|
||||
configureRequiresNpmDependencies(project, rootPackageJson)
|
||||
|
||||
val packageJsonUmbrella = nodeJs
|
||||
.packageJsonUmbrellaTaskProvider
|
||||
|
||||
nodeJs.rootPackageJsonTaskProvider.configure {
|
||||
it.dependsOn(packageJsonUmbrella)
|
||||
}
|
||||
|
||||
npmInstall.configure {
|
||||
it.dependsOn(rootPackageJson)
|
||||
it.inputs.property("npmIgnoreScripts", { npm.ignoreScripts })
|
||||
}
|
||||
|
||||
project.tasks.register(LockCopyTask.STORE_PACKAGE_LOCK_NAME, LockStoreTask::class.java) { task ->
|
||||
task.dependsOn(npmInstall)
|
||||
task.inputFile.set(nodeJs.rootPackageDirectory.map { it.file(LockCopyTask.PACKAGE_LOCK) })
|
||||
|
||||
task.additionalInputFiles.from(
|
||||
nodeJs.rootPackageDirectory.map { it.file(LockCopyTask.YARN_LOCK) }
|
||||
)
|
||||
task.additionalInputFiles.from(
|
||||
task.outputDirectory.map { it.file(LockCopyTask.YARN_LOCK) }
|
||||
)
|
||||
|
||||
task.outputDirectory.set(npm.lockFileDirectory)
|
||||
task.fileName.set(npm.lockFileName)
|
||||
|
||||
task.lockFileMismatchReport.value(
|
||||
project.provider { npm.requireConfigured().packageLockMismatchReport }
|
||||
).disallowChanges()
|
||||
task.reportNewLockFile.value(
|
||||
project.provider { npm.requireConfigured().reportNewPackageLock }
|
||||
).disallowChanges()
|
||||
task.lockFileAutoReplace.value(
|
||||
project.provider { npm.requireConfigured().packageLockAutoReplace }
|
||||
).disallowChanges()
|
||||
}
|
||||
|
||||
project.tasks.register(LockCopyTask.UPGRADE_PACKAGE_LOCK, LockStoreTask::class.java) { task ->
|
||||
task.dependsOn(npmInstall)
|
||||
task.inputFile.set(nodeJs.rootPackageDirectory.map { it.file(LockCopyTask.PACKAGE_LOCK) })
|
||||
task.outputDirectory.set(npm.lockFileDirectory)
|
||||
task.fileName.set(npm.lockFileName)
|
||||
|
||||
task.additionalInputFiles.from(
|
||||
nodeJs.rootPackageDirectory.map { it.file(LockCopyTask.YARN_LOCK) }
|
||||
)
|
||||
task.additionalInputFiles.from(
|
||||
task.outputDirectory.map { it.file(LockCopyTask.YARN_LOCK) }
|
||||
)
|
||||
|
||||
task.lockFileMismatchReport.value(
|
||||
LockFileMismatchReport.NONE
|
||||
).disallowChanges()
|
||||
task.reportNewLockFile.value(
|
||||
false
|
||||
).disallowChanges()
|
||||
task.lockFileAutoReplace.value(
|
||||
true
|
||||
).disallowChanges()
|
||||
}
|
||||
|
||||
project.tasks.register(LockCopyTask.RESTORE_PACKAGE_LOCK_NAME, LockCopyTask::class.java) { task ->
|
||||
task.inputFile.set(
|
||||
npm.lockFileDirectory.flatMap { dir ->
|
||||
dir.file(npm.lockFileName)
|
||||
}
|
||||
)
|
||||
task.additionalInputFiles.from(
|
||||
npm.lockFileDirectory.map { it.file(LockCopyTask.YARN_LOCK) }
|
||||
)
|
||||
task.outputDirectory.set(nodeJs.rootPackageDirectory)
|
||||
task.fileName.set(LockCopyTask.PACKAGE_LOCK)
|
||||
task.onlyIf {
|
||||
val inputFileExists = task.inputFile.getOrNull()?.asFile?.exists() == true
|
||||
// Workaround for "skip if not exists"
|
||||
// https://github.com/gradle/gradle/issues/2919
|
||||
if (!inputFileExists) {
|
||||
task.inputFile.set(null as RegularFile?)
|
||||
}
|
||||
inputFileExists || task.additionalInputFiles.files.any { it.exists() }
|
||||
}
|
||||
}
|
||||
|
||||
npm.preInstallTasks.value(
|
||||
listOf(npm.restorePackageLockTaskProvider)
|
||||
).disallowChanges()
|
||||
|
||||
npm.postInstallTasks.value(
|
||||
listOf(npm.storePackageLockTaskProvider)
|
||||
).disallowChanges()
|
||||
|
||||
npmInstall.configure {
|
||||
it.dependsOn(nodeJs.packageManagerExtension.map { it.preInstallTasks })
|
||||
}
|
||||
|
||||
npmInstall.configure {
|
||||
it.npmResolutionManager.value(npmResolutionManager).disallowChanges()
|
||||
@@ -122,6 +247,12 @@ open class NodeJsRootPlugin : Plugin<Project> {
|
||||
it.group = TASKS_GROUP_NAME
|
||||
it.description = "Clean unused local node version"
|
||||
}
|
||||
|
||||
val propertiesProvider = PropertiesProvider(project)
|
||||
|
||||
if (propertiesProvider.yarn) {
|
||||
YarnPlugin.apply(project)
|
||||
}
|
||||
}
|
||||
|
||||
// from https://github.com/node-gradle/gradle-node-plugin
|
||||
@@ -142,6 +273,42 @@ open class NodeJsRootPlugin : Plugin<Project> {
|
||||
).disallowChanges()
|
||||
}
|
||||
|
||||
// Yes, we need to break Task Configuration Avoidance here
|
||||
// In case when we need to create package.json's files and execute kotlinNpmInstall,
|
||||
// We need to configure all RequiresNpmDependencies tasks to install them,
|
||||
// Because we need to persist lock file
|
||||
// We execute this block in configure phase of rootPackageJson to be sure,
|
||||
// That Task Configuration Avoidance will not be broken for tasks not related with NPM installing
|
||||
// https://youtrack.jetbrains.com/issue/KT-48241
|
||||
private fun configureRequiresNpmDependencies(
|
||||
project: Project,
|
||||
rootPackageJson: TaskProvider<RootPackageJsonTask>,
|
||||
) {
|
||||
val fn: (Project) -> Unit = {
|
||||
it.tasks.implementing(RequiresNpmDependencies::class)
|
||||
.forEach {}
|
||||
}
|
||||
rootPackageJson.configure {
|
||||
project.allprojects
|
||||
.forEach { project ->
|
||||
if (it.state.executed) {
|
||||
fn(project)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
project.allprojects
|
||||
.forEach {
|
||||
if (!it.state.executed) {
|
||||
it.afterEvaluate { project ->
|
||||
rootPackageJson.configure {
|
||||
fn(project)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val TASKS_GROUP_NAME: String = "nodeJs"
|
||||
|
||||
@@ -154,30 +321,14 @@ open class NodeJsRootPlugin : Plugin<Project> {
|
||||
val Project.kotlinNodeJsExtension: NodeJsRootExtension
|
||||
get() = extensions.getByName(NodeJsRootExtension.EXTENSION_NAME).castIsolatedKotlinPluginClassLoaderAware()
|
||||
|
||||
private val Project.gradleNodeModules
|
||||
get() = GradleNodeModulesCache.registerIfAbsent(
|
||||
this,
|
||||
null,
|
||||
null
|
||||
)
|
||||
|
||||
val Project.kotlinNpmResolutionManager: Provider<KotlinNpmResolutionManager>
|
||||
get() {
|
||||
val npmResolutionManager = project.gradle.sharedServices.registerIfAbsent(
|
||||
return project.gradle.sharedServices.registerIfAbsent(
|
||||
KotlinNpmResolutionManager::class.java.name,
|
||||
KotlinNpmResolutionManager::class.java
|
||||
) {
|
||||
error("Must be already registered")
|
||||
}
|
||||
|
||||
SingleActionPerProject.run(project, UsesKotlinNpmResolutionManager::class.java.name) {
|
||||
project.tasks.withType<UsesKotlinNpmResolutionManager>().configureEach { task ->
|
||||
task.usesService(npmResolutionManager)
|
||||
task.usesService(gradleNodeModules)
|
||||
}
|
||||
}
|
||||
|
||||
return npmResolutionManager
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -43,7 +43,7 @@ abstract class NodeJsSetupTask : AbstractSetupTask<NodeJsEnv, NodeJsRootExtensio
|
||||
it.into(destination)
|
||||
}
|
||||
|
||||
fixBrokenSymlinks(destination, env.isWindows, fixBrokenSymLinks)
|
||||
fixBrokenSymlinks(this.destination, env.isWindows, fixBrokenSymLinks)
|
||||
|
||||
if (!env.isWindows) {
|
||||
File(env.nodeExecutable).setExecutable(true)
|
||||
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright 2010-2023 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.nodejs
|
||||
|
||||
import org.gradle.api.file.FileCollection
|
||||
import org.gradle.api.provider.ListProperty
|
||||
import org.gradle.api.tasks.TaskProvider
|
||||
import org.jetbrains.kotlin.gradle.targets.js.npm.NpmApiExecution
|
||||
|
||||
interface NpmApiExtension<out Env : PackageManagerEnvironment, out NpmApi : NpmApiExecution<Env>> {
|
||||
val packageManager: NpmApi
|
||||
|
||||
val environment: Env
|
||||
|
||||
val additionalInstallOutput: FileCollection
|
||||
|
||||
val preInstallTasks: ListProperty<TaskProvider<*>>
|
||||
|
||||
val postInstallTasks: ListProperty<TaskProvider<*>>
|
||||
}
|
||||
|
||||
typealias NpmApiExt = NpmApiExtension<PackageManagerEnvironment, NpmApiExecution<PackageManagerEnvironment>>
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
/*
|
||||
* Copyright 2010-2023 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.nodejs
|
||||
|
||||
import java.io.Serializable
|
||||
|
||||
interface PackageManagerEnvironment : Serializable
|
||||
+1
-1
@@ -77,7 +77,7 @@ internal class GradleNodeModuleBuilder(
|
||||
|
||||
packageJson.devDependencies.clear()
|
||||
|
||||
// yarn requires semver
|
||||
// npm requires semver
|
||||
packageJson.version = fixSemver(packageJson.version)
|
||||
|
||||
return makeNodeModule(cacheDir, packageJson) { nodeModule ->
|
||||
|
||||
+66
-11
@@ -5,20 +5,27 @@
|
||||
|
||||
package org.jetbrains.kotlin.gradle.targets.js.npm
|
||||
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.Task
|
||||
import org.gradle.api.file.Directory
|
||||
import org.gradle.api.file.DirectoryProperty
|
||||
import org.gradle.api.logging.Logger
|
||||
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.targets.js.nodejs.PackageManagerEnvironment
|
||||
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.tasks.withType
|
||||
import org.jetbrains.kotlin.gradle.utils.SingleActionPerProject
|
||||
import kotlin.contracts.ExperimentalContracts
|
||||
import kotlin.contracts.contract
|
||||
|
||||
internal interface UsesKotlinNpmResolutionManager : Task {
|
||||
@get:Internal
|
||||
@@ -78,16 +85,16 @@ abstract class KotlinNpmResolutionManager : BuildService<KotlinNpmResolutionMana
|
||||
|
||||
internal fun prepare(
|
||||
logger: Logger,
|
||||
npmEnvironment: NpmEnvironment,
|
||||
yarnEnvironment: YarnEnvironment,
|
||||
) = prepareIfNeeded(logger = logger, npmEnvironment, yarnEnvironment)
|
||||
nodeJsEnvironment: NodeJsEnvironment,
|
||||
environment: PackageManagerEnvironment,
|
||||
) = prepareIfNeeded(logger = logger, nodeJsEnvironment, environment)
|
||||
|
||||
internal fun installIfNeeded(
|
||||
args: List<String> = emptyList(),
|
||||
services: ServiceRegistry,
|
||||
logger: Logger,
|
||||
npmEnvironment: NpmEnvironment,
|
||||
yarnEnvironment: YarnEnvironment,
|
||||
nodeJsEnvironment: NodeJsEnvironment,
|
||||
packageManagerEnvironment: PackageManagerEnvironment,
|
||||
): Unit? {
|
||||
synchronized(this) {
|
||||
if (state is ResolutionState.Installed) {
|
||||
@@ -99,8 +106,8 @@ abstract class KotlinNpmResolutionManager : BuildService<KotlinNpmResolutionMana
|
||||
}
|
||||
|
||||
return try {
|
||||
val installation: Installation = prepareIfNeeded(logger = logger, npmEnvironment, yarnEnvironment)
|
||||
installation.install(args, services, logger, npmEnvironment, yarnEnvironment)
|
||||
val installation: Installation = prepareIfNeeded(logger = logger, nodeJsEnvironment, packageManagerEnvironment)
|
||||
installation.install(args, services, logger, nodeJsEnvironment, packageManagerEnvironment)
|
||||
state = ResolutionState.Installed()
|
||||
} catch (e: Exception) {
|
||||
state = ResolutionState.Error(e)
|
||||
@@ -111,8 +118,8 @@ abstract class KotlinNpmResolutionManager : BuildService<KotlinNpmResolutionMana
|
||||
|
||||
private fun prepareIfNeeded(
|
||||
logger: Logger,
|
||||
npmEnvironment: NpmEnvironment,
|
||||
yarnEnvironment: YarnEnvironment,
|
||||
nodeJsEnvironment: NodeJsEnvironment,
|
||||
packageManagerEnvironment: PackageManagerEnvironment,
|
||||
): Installation {
|
||||
val state0 = this.state
|
||||
return when (state0) {
|
||||
@@ -128,8 +135,8 @@ abstract class KotlinNpmResolutionManager : BuildService<KotlinNpmResolutionMana
|
||||
is ResolutionState.Configuring -> {
|
||||
state1.resolution.prepareInstallation(
|
||||
logger,
|
||||
npmEnvironment,
|
||||
yarnEnvironment,
|
||||
nodeJsEnvironment,
|
||||
packageManagerEnvironment,
|
||||
this
|
||||
).also {
|
||||
this.state = ResolutionState.Prepared(it)
|
||||
@@ -146,4 +153,52 @@ abstract class KotlinNpmResolutionManager : BuildService<KotlinNpmResolutionMana
|
||||
is ResolutionState.Error -> throw state0.wrappedException
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val serviceClass = KotlinNpmResolutionManager::class.java
|
||||
private val serviceName = serviceClass.name
|
||||
|
||||
private fun registerIfAbsentImpl(
|
||||
project: Project,
|
||||
resolution: Provider<KotlinRootNpmResolution>?,
|
||||
gradleNodeModulesProvider: Provider<GradleNodeModulesCache>?,
|
||||
packagesDir: Provider<Directory>
|
||||
): Provider<KotlinNpmResolutionManager> {
|
||||
project.gradle.sharedServices.registrations.findByName(serviceName)?.let {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return it.service as Provider<KotlinNpmResolutionManager>
|
||||
}
|
||||
|
||||
val message = {
|
||||
"Build service KotlinNpmResolutionManager should be already registered"
|
||||
}
|
||||
|
||||
requireNotNull(resolution, message)
|
||||
requireNotNull(gradleNodeModulesProvider, message)
|
||||
|
||||
return project.gradle.sharedServices.registerIfAbsent(serviceName, serviceClass) {
|
||||
it.parameters.resolution.set(
|
||||
resolution
|
||||
)
|
||||
it.parameters.gradleNodeModulesProvider.set(gradleNodeModulesProvider)
|
||||
it.parameters.packagesDir.set(packagesDir)
|
||||
}
|
||||
}
|
||||
|
||||
fun registerIfAbsent(
|
||||
project: Project,
|
||||
resolution: Provider<KotlinRootNpmResolution>?,
|
||||
gradleNodeModulesProvider: Provider<GradleNodeModulesCache>?,
|
||||
packagesDir: Provider<Directory>
|
||||
) = registerIfAbsentImpl(project, resolution, gradleNodeModulesProvider, packagesDir).also { serviceProvider ->
|
||||
SingleActionPerProject.run(project, UsesKotlinNpmResolutionManager::class.java.name) {
|
||||
project.tasks.withType<UsesKotlinNpmResolutionManager>().configureEach { task ->
|
||||
task.usesService(serviceProvider)
|
||||
if (gradleNodeModulesProvider != null) {
|
||||
task.usesService(gradleNodeModulesProvider)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright 2010-2023 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
|
||||
|
||||
import org.gradle.api.DefaultTask
|
||||
import org.gradle.api.file.*
|
||||
import org.gradle.api.provider.Property
|
||||
import org.gradle.api.provider.Provider
|
||||
import org.gradle.api.tasks.*
|
||||
import org.gradle.work.DisableCachingByDefault
|
||||
import org.gradle.work.NormalizeLineEndings
|
||||
import java.io.File
|
||||
import javax.inject.Inject
|
||||
|
||||
@DisableCachingByDefault
|
||||
abstract class LockCopyTask : DefaultTask() {
|
||||
|
||||
@get:NormalizeLineEndings
|
||||
@get:InputFile
|
||||
@get:Optional
|
||||
@get:PathSensitive(PathSensitivity.RELATIVE)
|
||||
abstract val inputFile: RegularFileProperty
|
||||
|
||||
@get:NormalizeLineEndings
|
||||
@get:InputFiles
|
||||
@get:PathSensitive(PathSensitivity.RELATIVE)
|
||||
val additionalInputFiles: ConfigurableFileCollection = project.objects.fileCollection()
|
||||
|
||||
@get:Internal
|
||||
abstract val outputDirectory: DirectoryProperty
|
||||
|
||||
@get:Internal
|
||||
abstract val fileName: Property<String>
|
||||
|
||||
@get:OutputFile
|
||||
val outputFile: Provider<File>
|
||||
get() = outputDirectory.map { dir ->
|
||||
dir.asFile.resolve(fileName.get())
|
||||
}
|
||||
|
||||
@get:Inject
|
||||
abstract val fs: FileSystemOperations
|
||||
|
||||
@TaskAction
|
||||
open fun copy() {
|
||||
fs.copy { copy ->
|
||||
inputFile.getOrNull()?.let { inputFile ->
|
||||
copy.from(inputFile) {
|
||||
it.rename { fileName.get() }
|
||||
}
|
||||
}
|
||||
|
||||
copy.from(additionalInputFiles)
|
||||
copy.into(outputDirectory)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val STORE_PACKAGE_LOCK_NAME = "kotlinStorePackageLock"
|
||||
const val RESTORE_PACKAGE_LOCK_NAME = "kotlinRestorePackageLock"
|
||||
const val UPGRADE_PACKAGE_LOCK = "kotlinUpgradePackageLock"
|
||||
const val PACKAGE_LOCK_MISMATCH_MESSAGE = "Lock file was changed. Run the `$UPGRADE_PACKAGE_LOCK` task to actualize lock file"
|
||||
const val KOTLIN_JS_STORE = "kotlin-js-store"
|
||||
const val PACKAGE_LOCK = "package-lock.json"
|
||||
const val YARN_LOCK = "yarn.lock"
|
||||
}
|
||||
}
|
||||
|
||||
enum class LockFileMismatchReport {
|
||||
NONE,
|
||||
WARNING,
|
||||
FAIL,
|
||||
}
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright 2010-2023 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
|
||||
|
||||
import org.gradle.api.GradleException
|
||||
import org.gradle.api.provider.Property
|
||||
import org.gradle.api.tasks.Input
|
||||
import org.gradle.work.DisableCachingByDefault
|
||||
import org.jetbrains.kotlin.gradle.utils.contentEquals
|
||||
|
||||
@DisableCachingByDefault
|
||||
abstract class LockStoreTask : LockCopyTask() {
|
||||
@get:Input
|
||||
abstract val lockFileMismatchReport: Property<LockFileMismatchReport>
|
||||
|
||||
@get:Input
|
||||
abstract val reportNewLockFile: Property<Boolean>
|
||||
|
||||
@get:Input
|
||||
abstract val lockFileAutoReplace: Property<Boolean>
|
||||
|
||||
override fun copy() {
|
||||
val outputFile = outputDirectory.get().asFile.resolve(fileName.get())
|
||||
|
||||
val value = inputFile.orNull
|
||||
requireNotNull(value) {
|
||||
"Input file $fileName should exist"
|
||||
}
|
||||
|
||||
val shouldReportMismatch = if (!outputFile.exists()) {
|
||||
reportNewLockFile.get()
|
||||
} else {
|
||||
lockFileMismatchReport.get() != LockFileMismatchReport.NONE && !contentEquals(value.asFile, outputFile)
|
||||
}
|
||||
|
||||
// outputFile is updated only with auto replace or not existed, but we need delete all other files initially
|
||||
fs.delete {
|
||||
it.delete(additionalInputFiles)
|
||||
}
|
||||
|
||||
if (!outputFile.exists() || lockFileAutoReplace.get()) {
|
||||
super.copy()
|
||||
}
|
||||
|
||||
if (shouldReportMismatch) {
|
||||
when (lockFileMismatchReport.get()) {
|
||||
LockFileMismatchReport.NONE -> {}
|
||||
LockFileMismatchReport.WARNING -> {
|
||||
logger.warn(PACKAGE_LOCK_MISMATCH_MESSAGE)
|
||||
}
|
||||
LockFileMismatchReport.FAIL -> throw GradleException(PACKAGE_LOCK_MISMATCH_MESSAGE)
|
||||
else -> error("Unknown mismatch report kind")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+139
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
* Copyright 2010-2023 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
|
||||
|
||||
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.resolved.PreparedKotlinCompilationNpmResolution
|
||||
import org.jetbrains.kotlin.gradle.utils.getFile
|
||||
import java.io.File
|
||||
|
||||
class Npm : NpmApiExecution<NpmEnvironment> {
|
||||
|
||||
override fun preparedFiles(nodeJs: NodeJsEnvironment): Collection<File> {
|
||||
return listOf(
|
||||
nodeJs
|
||||
.rootPackageDir
|
||||
.resolve(NpmProject.PACKAGE_JSON)
|
||||
)
|
||||
}
|
||||
|
||||
override fun prepareRootProject(
|
||||
nodeJs: NodeJsEnvironment,
|
||||
packageManagerEnvironment: NpmEnvironment,
|
||||
rootProjectName: String,
|
||||
rootProjectVersion: String,
|
||||
subProjects: Collection<PreparedKotlinCompilationNpmResolution>,
|
||||
) {
|
||||
return prepareRootPackageJson(
|
||||
nodeJs,
|
||||
rootProjectName,
|
||||
rootProjectVersion,
|
||||
subProjects,
|
||||
packageManagerEnvironment.overrides
|
||||
.associate { it.path to it.toVersionString() },
|
||||
)
|
||||
}
|
||||
|
||||
private fun prepareRootPackageJson(
|
||||
nodeJs: NodeJsEnvironment,
|
||||
rootProjectName: String,
|
||||
rootProjectVersion: String,
|
||||
npmProjects: Collection<PreparedKotlinCompilationNpmResolution>,
|
||||
overrides: Map<String, String>,
|
||||
) {
|
||||
val rootPackageJsonFile = preparedFiles(nodeJs).single()
|
||||
|
||||
saveRootProjectWorkspacesPackageJson(
|
||||
rootProjectName,
|
||||
rootProjectVersion,
|
||||
npmProjects,
|
||||
overrides,
|
||||
rootPackageJsonFile
|
||||
)
|
||||
}
|
||||
|
||||
override fun resolveRootProject(
|
||||
services: ServiceRegistry,
|
||||
logger: Logger,
|
||||
nodeJs: NodeJsEnvironment,
|
||||
packageManagerEnvironment: NpmEnvironment,
|
||||
npmProjects: Collection<PreparedKotlinCompilationNpmResolution>,
|
||||
cliArgs: List<String>,
|
||||
) {
|
||||
val nodeJsWorldDir = nodeJs.rootPackageDir
|
||||
|
||||
npmExec(
|
||||
services,
|
||||
logger,
|
||||
nodeJs,
|
||||
packageManagerEnvironment,
|
||||
nodeJsWorldDir,
|
||||
NpmApiExecution.resolveOperationDescription("npm"),
|
||||
cliArgs
|
||||
)
|
||||
}
|
||||
|
||||
fun npmExec(
|
||||
services: ServiceRegistry,
|
||||
logger: Logger,
|
||||
nodeJs: NodeJsEnvironment,
|
||||
environment: NpmEnvironment,
|
||||
dir: File,
|
||||
description: String,
|
||||
args: List<String>,
|
||||
) {
|
||||
services.execWithProgress(description) { exec ->
|
||||
val arguments = listOf("install") + args
|
||||
.plus(
|
||||
if (logger.isDebugEnabled) "--verbose" else ""
|
||||
)
|
||||
.plus(
|
||||
if (environment.ignoreScripts) "--ignore-scripts" else ""
|
||||
).filter { it.isNotEmpty() }
|
||||
|
||||
if (!environment.standalone) {
|
||||
val nodeExecutable = nodeJs.nodeExecutable
|
||||
val nodePath = File(nodeExecutable).parent
|
||||
exec.environment(
|
||||
"PATH",
|
||||
"$nodePath${File.pathSeparator}${System.getenv("PATH")}"
|
||||
)
|
||||
}
|
||||
|
||||
val command = environment.executable
|
||||
|
||||
exec.executable = command
|
||||
exec.args = arguments
|
||||
|
||||
exec.workingDir = dir
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun saveRootProjectWorkspacesPackageJson(
|
||||
rootProjectName: String,
|
||||
rootProjectVersion: String,
|
||||
npmProjects: Collection<PreparedKotlinCompilationNpmResolution>,
|
||||
overrides: Map<String, String>,
|
||||
rootPackageJsonFile: File,
|
||||
) {
|
||||
val nodeJsWorldDir = rootPackageJsonFile.parentFile
|
||||
val rootPackageJson = PackageJson(rootProjectName, rootProjectVersion)
|
||||
rootPackageJson.private = true
|
||||
|
||||
val npmProjectWorkspaces = npmProjects.map { it.npmProjectDir.getFile().relativeTo(nodeJsWorldDir).path }
|
||||
val importedProjectWorkspaces =
|
||||
NpmImportedPackagesVersionResolver(npmProjects, nodeJsWorldDir).resolveAndUpdatePackages()
|
||||
|
||||
rootPackageJson.workspaces = npmProjectWorkspaces + importedProjectWorkspaces
|
||||
rootPackageJson.overrides = overrides
|
||||
rootPackageJson.saveTo(
|
||||
rootPackageJsonFile
|
||||
)
|
||||
}
|
||||
}
|
||||
+11
-30
@@ -8,32 +8,30 @@ package org.jetbrains.kotlin.gradle.targets.js.npm
|
||||
import org.gradle.api.logging.Logger
|
||||
import org.gradle.internal.service.ServiceRegistry
|
||||
import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsEnv
|
||||
import org.jetbrains.kotlin.gradle.targets.js.nodejs.PackageManagerEnvironment
|
||||
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
|
||||
|
||||
/**
|
||||
* NodeJS package manager API
|
||||
*/
|
||||
interface NpmApi : Serializable {
|
||||
fun preparedFiles(nodeJs: NpmEnvironment): Collection<File>
|
||||
interface NpmApiExecution<out T : PackageManagerEnvironment> : Serializable {
|
||||
fun preparedFiles(nodeJs: NodeJsEnvironment): Collection<File>
|
||||
|
||||
fun prepareRootProject(
|
||||
nodeJs: NpmEnvironment,
|
||||
nodeJs: NodeJsEnvironment,
|
||||
packageManagerEnvironment: @UnsafeVariance T,
|
||||
rootProjectName: String,
|
||||
rootProjectVersion: String,
|
||||
logger: Logger,
|
||||
subProjects: Collection<PreparedKotlinCompilationNpmResolution>,
|
||||
resolutions: Map<String, String>,
|
||||
)
|
||||
|
||||
fun resolveRootProject(
|
||||
services: ServiceRegistry,
|
||||
logger: Logger,
|
||||
nodeJs: NpmEnvironment,
|
||||
yarn: YarnEnvironment,
|
||||
nodeJs: NodeJsEnvironment,
|
||||
packageManagerEnvironment: @UnsafeVariance T,
|
||||
npmProjects: Collection<PreparedKotlinCompilationNpmResolution>,
|
||||
cliArgs: List<String>
|
||||
)
|
||||
@@ -44,32 +42,15 @@ interface NpmApi : Serializable {
|
||||
}
|
||||
}
|
||||
|
||||
data class NpmEnvironment(
|
||||
data class NodeJsEnvironment(
|
||||
val rootPackageDir: File,
|
||||
val nodeExecutable: String,
|
||||
val isWindows: Boolean,
|
||||
val packageManager: NpmApi
|
||||
val packageManager: NpmApiExecution<PackageManagerEnvironment>
|
||||
) : Serializable
|
||||
|
||||
internal val NodeJsEnv.asNpmEnvironment
|
||||
get() = NpmEnvironment(
|
||||
internal val NodeJsEnv.asNodeJsEnvironment
|
||||
get() = NodeJsEnvironment(
|
||||
rootPackageDir,
|
||||
nodeExecutable,
|
||||
isWindows,
|
||||
packageManager
|
||||
)
|
||||
|
||||
data class YarnEnvironment(
|
||||
val executable: String,
|
||||
val standalone: Boolean,
|
||||
val ignoreScripts: Boolean,
|
||||
val yarnResolutions: List<YarnResolution>
|
||||
) : Serializable
|
||||
|
||||
internal val YarnEnv.asYarnEnvironment
|
||||
get() = YarnEnvironment(
|
||||
executable,
|
||||
standalone,
|
||||
ignoreScripts,
|
||||
yarnResolutions
|
||||
)
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* Copyright 2010-2023 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
|
||||
|
||||
data class NpmEnv(
|
||||
val executable: String,
|
||||
val ignoreScripts: Boolean,
|
||||
val standalone: Boolean,
|
||||
val packageLockMismatchReport: LockFileMismatchReport,
|
||||
val reportNewPackageLock: Boolean,
|
||||
val packageLockAutoReplace: Boolean,
|
||||
val overrides: List<NpmOverride>
|
||||
)
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright 2010-2023 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
|
||||
|
||||
import org.jetbrains.kotlin.gradle.targets.js.nodejs.PackageManagerEnvironment
|
||||
|
||||
data class NpmEnvironment(
|
||||
val executable: String,
|
||||
val ignoreScripts: Boolean,
|
||||
val standalone: Boolean,
|
||||
val overrides: List<NpmOverride>
|
||||
) : PackageManagerEnvironment
|
||||
|
||||
internal val NpmEnv.asNpmEnvironment
|
||||
get() = NpmEnvironment(
|
||||
executable,
|
||||
ignoreScripts,
|
||||
standalone,
|
||||
overrides
|
||||
)
|
||||
+126
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
import org.gradle.api.Action
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.Task
|
||||
import org.gradle.api.file.DirectoryProperty
|
||||
import org.gradle.api.file.FileCollection
|
||||
import org.gradle.api.provider.ListProperty
|
||||
import org.gradle.api.tasks.TaskProvider
|
||||
import org.jetbrains.kotlin.gradle.internal.ConfigurationPhaseAware
|
||||
import org.jetbrains.kotlin.gradle.logging.kotlinInfo
|
||||
import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsEnv
|
||||
import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootPlugin
|
||||
import org.jetbrains.kotlin.gradle.targets.js.nodejs.NpmApiExtension
|
||||
|
||||
open class NpmExtension(
|
||||
val project: Project,
|
||||
) : ConfigurationPhaseAware<NpmEnv>(), NpmApiExtension<NpmEnvironment, Npm> {
|
||||
init {
|
||||
check(project == project.rootProject)
|
||||
}
|
||||
|
||||
private val gradleHome = project.gradle.gradleUserHomeDir.also {
|
||||
project.logger.kotlinInfo("Storing cached files in $it")
|
||||
}
|
||||
|
||||
override val packageManager: Npm by lazy {
|
||||
Npm()
|
||||
}
|
||||
|
||||
override val environment: NpmEnvironment by lazy {
|
||||
requireConfigured().asNpmEnvironment
|
||||
}
|
||||
|
||||
override val additionalInstallOutput: FileCollection = project.objects.fileCollection().from(
|
||||
{
|
||||
nodeJsEnvironment.get().rootPackageDir.resolve("package-lock.json")
|
||||
}
|
||||
)
|
||||
|
||||
override val preInstallTasks: ListProperty<TaskProvider<*>> = project.objects.listProperty(TaskProvider::class.java)
|
||||
|
||||
override val postInstallTasks: ListProperty<TaskProvider<*>> = project.objects.listProperty(TaskProvider::class.java)
|
||||
|
||||
val command: org.gradle.api.provider.Property<String> = project.objects.property(String::class.java)
|
||||
.convention("npm")
|
||||
|
||||
val lockFileName: org.gradle.api.provider.Property<String> = project.objects.property(String::class.java)
|
||||
.convention(LockCopyTask.PACKAGE_LOCK)
|
||||
val lockFileDirectory: DirectoryProperty = project.objects.directoryProperty()
|
||||
.convention(project.layout.projectDirectory.dir(LockCopyTask.KOTLIN_JS_STORE))
|
||||
|
||||
val ignoreScripts: org.gradle.api.provider.Property<Boolean> = project.objects.property(Boolean::class.java)
|
||||
.convention(true)
|
||||
|
||||
val packageLockMismatchReport: org.gradle.api.provider.Property<LockFileMismatchReport> =
|
||||
project.objects.property(LockFileMismatchReport::class.java)
|
||||
.convention(LockFileMismatchReport.FAIL)
|
||||
|
||||
val reportNewPackageLock: org.gradle.api.provider.Property<Boolean> = project.objects.property(Boolean::class.java)
|
||||
.convention(false)
|
||||
|
||||
val packageLockAutoReplace: org.gradle.api.provider.Property<Boolean> = project.objects.property(Boolean::class.java)
|
||||
.convention(false)
|
||||
|
||||
val overrides: ListProperty<NpmOverride> = project.objects.listProperty(NpmOverride::class.java)
|
||||
|
||||
fun override(path: String, configure: Action<NpmOverride>) {
|
||||
overrides.add(
|
||||
NpmOverride(path)
|
||||
.apply { configure.execute(this) }
|
||||
)
|
||||
}
|
||||
|
||||
fun override(path: String, version: String) {
|
||||
override(path, Action {
|
||||
it.include(version)
|
||||
})
|
||||
}
|
||||
|
||||
internal val nodeJsEnvironment: org.gradle.api.provider.Property<NodeJsEnv> = project.objects.property(NodeJsEnv::class.java)
|
||||
|
||||
override fun finalizeConfiguration(): NpmEnv {
|
||||
val nodeJsEnvironmentValue = nodeJsEnvironment.get()
|
||||
val isWindows = nodeJsEnvironmentValue.isWindows
|
||||
|
||||
fun getExecutable(command: String, customCommand: String, windowsExtension: String): String {
|
||||
val finalCommand = if (isWindows && customCommand == command) "$command.$windowsExtension" else customCommand
|
||||
return if (nodeJsEnvironmentValue.download)
|
||||
nodeJsEnvironmentValue.nodeBinDir
|
||||
.resolve(finalCommand).absolutePath
|
||||
else
|
||||
finalCommand
|
||||
}
|
||||
return NpmEnv(
|
||||
executable = getExecutable("npm", command.get(), "cmd"),
|
||||
ignoreScripts = ignoreScripts.get(),
|
||||
standalone = !nodeJsEnvironmentValue.download,
|
||||
packageLockMismatchReport = packageLockMismatchReport.get(),
|
||||
reportNewPackageLock = reportNewPackageLock.get(),
|
||||
packageLockAutoReplace = packageLockAutoReplace.get(),
|
||||
overrides = overrides.get()
|
||||
)
|
||||
}
|
||||
|
||||
val restorePackageLockTaskProvider: TaskProvider<out Task>
|
||||
get() = project.tasks.named(LockCopyTask.RESTORE_PACKAGE_LOCK_NAME)
|
||||
|
||||
val storePackageLockTaskProvider: TaskProvider<out Task>
|
||||
get() = project.tasks.named(LockCopyTask.STORE_PACKAGE_LOCK_NAME)
|
||||
|
||||
companion object {
|
||||
const val EXTENSION_NAME: String = "kotlinNpm"
|
||||
|
||||
operator fun get(project: Project): NpmExtension {
|
||||
val rootProject = project.rootProject
|
||||
rootProject.plugins.apply(NodeJsRootPlugin::class.java)
|
||||
return rootProject.extensions.getByName(EXTENSION_NAME) as NpmExtension
|
||||
}
|
||||
}
|
||||
}
|
||||
+3
-6
@@ -1,17 +1,14 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Copyright 2010-2023 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
|
||||
package org.jetbrains.kotlin.gradle.targets.js.npm
|
||||
|
||||
import org.gradle.api.logging.Logger
|
||||
import org.jetbrains.kotlin.gradle.targets.js.npm.GradleNodeModule
|
||||
import org.jetbrains.kotlin.gradle.targets.js.npm.resolved.PreparedKotlinCompilationNpmResolution
|
||||
import java.io.File
|
||||
|
||||
class YarnImportedPackagesVersionResolver(
|
||||
private val logger: Logger,
|
||||
class NpmImportedPackagesVersionResolver(
|
||||
npmProjects: Collection<PreparedKotlinCompilationNpmResolution>,
|
||||
private val nodeJsWorldDir: File
|
||||
) {
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright 2010-2023 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
|
||||
|
||||
import java.io.Serializable
|
||||
|
||||
class NpmOverride(
|
||||
val path: String
|
||||
) : Serializable {
|
||||
var includedVersions = mutableListOf<String>()
|
||||
var excludedVersions = mutableListOf<String>()
|
||||
|
||||
fun include(vararg include: String) {
|
||||
includedVersions.addAll(include)
|
||||
}
|
||||
|
||||
fun exclude(vararg exclude: String) {
|
||||
excludedVersions.addAll(exclude)
|
||||
}
|
||||
}
|
||||
|
||||
fun NpmOverride.toVersionString(): String {
|
||||
return buildNpmVersion(includedVersions, excludedVersions)
|
||||
}
|
||||
+2
-1
@@ -30,8 +30,9 @@ class NpmResolverPlugin : Plugin<Project> {
|
||||
}
|
||||
task.dependsOn(
|
||||
kotlinNodeJsTaskProvidersExtension.npmInstallTaskProvider,
|
||||
kotlinNodeJsTaskProvidersExtension.storeYarnLockTaskProvider,
|
||||
)
|
||||
|
||||
task.dependsOn(nodeJs.packageManagerExtension.map { it.postInstallTasks })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -35,7 +35,7 @@ class PackageJson(
|
||||
|
||||
var workspaces: Collection<String>? = null
|
||||
|
||||
var resolutions: Map<String, String>? = null
|
||||
var overrides: Map<String, String>? = null
|
||||
|
||||
var types: String? = null
|
||||
|
||||
|
||||
+12
-15
@@ -7,10 +7,9 @@ package org.jetbrains.kotlin.gradle.targets.js.npm.resolved
|
||||
|
||||
import org.gradle.api.logging.Logger
|
||||
import org.gradle.internal.service.ServiceRegistry
|
||||
import org.jetbrains.kotlin.gradle.targets.js.nodejs.PackageManagerEnvironment
|
||||
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 org.jetbrains.kotlin.gradle.targets.js.npm.NodeJsEnvironment
|
||||
import java.io.Serializable
|
||||
|
||||
class KotlinRootNpmResolution(
|
||||
@@ -25,8 +24,8 @@ class KotlinRootNpmResolution(
|
||||
*/
|
||||
internal fun prepareInstallation(
|
||||
logger: Logger,
|
||||
npmEnvironment: NpmEnvironment,
|
||||
yarnEnvironment: YarnEnvironment,
|
||||
nodeJsEnvironment: NodeJsEnvironment,
|
||||
packageManagerEnvironment: PackageManagerEnvironment,
|
||||
npmResolutionManager: KotlinNpmResolutionManager,
|
||||
): Installation {
|
||||
synchronized(projects) {
|
||||
@@ -41,14 +40,12 @@ class KotlinRootNpmResolution(
|
||||
)
|
||||
}
|
||||
|
||||
npmEnvironment.packageManager.prepareRootProject(
|
||||
npmEnvironment,
|
||||
nodeJsEnvironment.packageManager.prepareRootProject(
|
||||
nodeJsEnvironment,
|
||||
packageManagerEnvironment,
|
||||
rootProjectName,
|
||||
rootProjectVersion,
|
||||
logger,
|
||||
projectResolutions,
|
||||
yarnEnvironment.yarnResolutions
|
||||
.associate { it.path to it.toVersionString() },
|
||||
)
|
||||
|
||||
return Installation(
|
||||
@@ -63,15 +60,15 @@ class Installation(val compilationResolutions: Collection<PreparedKotlinCompilat
|
||||
args: List<String>,
|
||||
services: ServiceRegistry,
|
||||
logger: Logger,
|
||||
npmEnvironment: NpmEnvironment,
|
||||
yarnEnvironment: YarnEnvironment,
|
||||
nodeJsEnvironment: NodeJsEnvironment,
|
||||
packageManagerEnvironment: PackageManagerEnvironment,
|
||||
) {
|
||||
synchronized(compilationResolutions) {
|
||||
npmEnvironment.packageManager.resolveRootProject(
|
||||
nodeJsEnvironment.packageManager.resolveRootProject(
|
||||
services,
|
||||
logger,
|
||||
npmEnvironment,
|
||||
yarnEnvironment,
|
||||
nodeJsEnvironment,
|
||||
packageManagerEnvironment,
|
||||
compilationResolutions,
|
||||
args
|
||||
)
|
||||
|
||||
+21
-24
@@ -10,14 +10,7 @@ import org.gradle.api.file.Directory
|
||||
import org.gradle.api.file.FileCollection
|
||||
import org.gradle.api.file.RegularFile
|
||||
import org.gradle.api.provider.Provider
|
||||
import org.gradle.api.tasks.IgnoreEmptyDirectories
|
||||
import org.gradle.api.tasks.Input
|
||||
import org.gradle.api.tasks.InputFiles
|
||||
import org.gradle.api.tasks.Internal
|
||||
import org.gradle.api.tasks.OutputFile
|
||||
import org.gradle.api.tasks.PathSensitive
|
||||
import org.gradle.api.tasks.PathSensitivity
|
||||
import org.gradle.api.tasks.TaskAction
|
||||
import org.gradle.api.tasks.*
|
||||
import org.gradle.work.DisableCachingByDefault
|
||||
import org.gradle.work.NormalizeLineEndings
|
||||
import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootExtension
|
||||
@@ -25,10 +18,8 @@ import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootPlugin.Companion.
|
||||
import org.jetbrains.kotlin.gradle.targets.js.npm.KotlinNpmResolutionManager
|
||||
import org.jetbrains.kotlin.gradle.targets.js.npm.NpmProject
|
||||
import org.jetbrains.kotlin.gradle.targets.js.npm.UsesKotlinNpmResolutionManager
|
||||
import org.jetbrains.kotlin.gradle.targets.js.npm.asNpmEnvironment
|
||||
import org.jetbrains.kotlin.gradle.targets.js.npm.asYarnEnvironment
|
||||
import org.jetbrains.kotlin.gradle.targets.js.npm.asNodeJsEnvironment
|
||||
import org.jetbrains.kotlin.gradle.targets.js.npm.resolver.KotlinRootNpmResolver
|
||||
import org.jetbrains.kotlin.gradle.targets.js.yarn.yarn
|
||||
import org.jetbrains.kotlin.gradle.utils.getFile
|
||||
import java.io.File
|
||||
|
||||
@@ -46,9 +37,6 @@ abstract class KotlinNpmInstallTask :
|
||||
private val nodeJs: NodeJsRootExtension
|
||||
get() = project.rootProject.kotlinNodeJsExtension
|
||||
|
||||
private val yarn
|
||||
get() = project.rootProject.yarn
|
||||
|
||||
private val rootResolver: KotlinRootNpmResolver
|
||||
get() = nodeJs.resolver
|
||||
|
||||
@@ -57,12 +45,12 @@ abstract class KotlinNpmInstallTask :
|
||||
|
||||
// -----
|
||||
|
||||
private val npmEnvironment by lazy {
|
||||
nodeJs.requireConfigured().asNpmEnvironment
|
||||
private val nodsJsEnvironment by lazy {
|
||||
nodeJs.requireConfigured().asNodeJsEnvironment
|
||||
}
|
||||
|
||||
private val yarnEnv by lazy {
|
||||
yarn.requireConfigured().asYarnEnvironment
|
||||
private val packageManagerEnv by lazy {
|
||||
nodeJs.packageManagerExtension.get().environment
|
||||
}
|
||||
|
||||
@Input
|
||||
@@ -73,7 +61,7 @@ abstract class KotlinNpmInstallTask :
|
||||
@get:NormalizeLineEndings
|
||||
@get:InputFiles
|
||||
val preparedFiles: Collection<File> by lazy {
|
||||
nodeJs.packageManager.preparedFiles(npmEnvironment)
|
||||
nodeJs.packageManagerExtension.get().packageManager.preparedFiles(nodsJsEnvironment)
|
||||
}
|
||||
|
||||
@get:PathSensitive(PathSensitivity.RELATIVE)
|
||||
@@ -92,12 +80,21 @@ abstract class KotlinNpmInstallTask :
|
||||
}
|
||||
)
|
||||
|
||||
@get:OutputFile
|
||||
@get:OutputFiles
|
||||
val additionalFiles: FileCollection by lazy {
|
||||
nodeJs.packageManagerExtension.get().additionalInstallOutput
|
||||
}
|
||||
|
||||
@Deprecated(
|
||||
"This property is deprecated and will be removed in future. Use additionalFiles instead",
|
||||
replaceWith = ReplaceWith("additionalFiles")
|
||||
)
|
||||
@get:Internal
|
||||
val yarnLockFile: Provider<RegularFile> = nodeJs.rootPackageDirectory.map { it.file("yarn.lock") }
|
||||
|
||||
@Deprecated(
|
||||
"This property is deprecated and will be removed in future. Use yarnLockFile instead",
|
||||
replaceWith = ReplaceWith("yarnLockFile")
|
||||
"This property is deprecated and will be removed in future. Use additionalFiles instead",
|
||||
replaceWith = ReplaceWith("additionalFiles")
|
||||
)
|
||||
@get:Internal
|
||||
val yarnLock: File
|
||||
@@ -115,8 +112,8 @@ abstract class KotlinNpmInstallTask :
|
||||
args = args,
|
||||
services = services,
|
||||
logger = logger,
|
||||
npmEnvironment,
|
||||
yarnEnv
|
||||
nodsJsEnvironment,
|
||||
packageManagerEnv
|
||||
) ?: throw (npmResolutionManager.get().state as KotlinNpmResolutionManager.ResolutionState.Error).wrappedException
|
||||
}
|
||||
|
||||
|
||||
+6
-11
@@ -16,10 +16,8 @@ import org.gradle.work.NormalizeLineEndings
|
||||
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.npm.UsesKotlinNpmResolutionManager
|
||||
import org.jetbrains.kotlin.gradle.targets.js.npm.asNpmEnvironment
|
||||
import org.jetbrains.kotlin.gradle.targets.js.npm.asYarnEnvironment
|
||||
import org.jetbrains.kotlin.gradle.targets.js.npm.asNodeJsEnvironment
|
||||
import org.jetbrains.kotlin.gradle.targets.js.npm.resolver.KotlinRootNpmResolver
|
||||
import org.jetbrains.kotlin.gradle.targets.js.yarn.yarn
|
||||
import org.jetbrains.kotlin.gradle.utils.getFile
|
||||
import java.io.File
|
||||
|
||||
@@ -37,9 +35,6 @@ abstract class RootPackageJsonTask :
|
||||
private val nodeJs
|
||||
get() = project.rootProject.kotlinNodeJsExtension
|
||||
|
||||
private val yarn
|
||||
get() = project.rootProject.yarn
|
||||
|
||||
private val rootResolver: KotlinRootNpmResolver
|
||||
get() = nodeJs.resolver
|
||||
|
||||
@@ -48,12 +43,12 @@ abstract class RootPackageJsonTask :
|
||||
|
||||
// -----
|
||||
|
||||
private val npmEnvironment by lazy {
|
||||
nodeJs.requireConfigured().asNpmEnvironment
|
||||
private val nodeJsEnvironment by lazy {
|
||||
nodeJs.requireConfigured().asNodeJsEnvironment
|
||||
}
|
||||
|
||||
private val yarnEnv by lazy {
|
||||
yarn.requireConfigured().asYarnEnvironment
|
||||
private val packageManagerEnv by lazy {
|
||||
nodeJs.packageManagerExtension.get().environment
|
||||
}
|
||||
|
||||
@get:OutputFile
|
||||
@@ -87,7 +82,7 @@ abstract class RootPackageJsonTask :
|
||||
|
||||
@TaskAction
|
||||
fun resolve() {
|
||||
npmResolutionManager.get().prepare(logger, npmEnvironment, yarnEnv)
|
||||
npmResolutionManager.get().prepare(logger, nodeJsEnvironment, packageManagerEnv)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
+1
-1
@@ -46,7 +46,7 @@ fun FileHasher.calculateDirHash(
|
||||
dir.walk()
|
||||
.forEach { file ->
|
||||
hasher.putString(file.toRelativeString(dir))
|
||||
if (file.isFile && !Files.isSymbolicLink(file.toPath())) {
|
||||
if (file.isFile) {
|
||||
if (!Files.isSymbolicLink(file.toPath())) {
|
||||
hasher.putHash(hash(file))
|
||||
} else {
|
||||
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* 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.yarn
|
||||
|
||||
import org.gradle.api.provider.Provider
|
||||
import org.gradle.api.tasks.Internal
|
||||
import org.gradle.work.DisableCachingByDefault
|
||||
import org.jetbrains.kotlin.gradle.targets.js.npm.LockCopyTask
|
||||
import org.jetbrains.kotlin.gradle.targets.js.npm.LockFileMismatchReport
|
||||
import org.jetbrains.kotlin.gradle.targets.js.npm.LockStoreTask
|
||||
|
||||
@DisableCachingByDefault
|
||||
abstract class YarnLockCopyTask : LockCopyTask()
|
||||
|
||||
@DisableCachingByDefault
|
||||
abstract class YarnLockStoreTask : LockStoreTask() {
|
||||
@Internal
|
||||
val yarnLockMismatchReport: Provider<YarnLockMismatchReport> = lockFileMismatchReport.map { it.fromLockFileMismatchReport() }
|
||||
|
||||
@Internal
|
||||
val reportNewYarnLock: Provider<Boolean> = reportNewLockFile
|
||||
|
||||
@Internal
|
||||
val yarnLockAutoReplace: Provider<Boolean> = lockFileAutoReplace
|
||||
}
|
||||
|
||||
enum class YarnLockMismatchReport {
|
||||
NONE,
|
||||
WARNING,
|
||||
FAIL;
|
||||
|
||||
fun toLockFileMismatchReport(): LockFileMismatchReport =
|
||||
when(this) {
|
||||
NONE -> LockFileMismatchReport.NONE
|
||||
WARNING -> LockFileMismatchReport.WARNING
|
||||
FAIL -> LockFileMismatchReport.FAIL
|
||||
}
|
||||
}
|
||||
|
||||
fun LockFileMismatchReport.fromLockFileMismatchReport(): YarnLockMismatchReport =
|
||||
when(this) {
|
||||
LockFileMismatchReport.NONE -> YarnLockMismatchReport.NONE
|
||||
LockFileMismatchReport.WARNING -> YarnLockMismatchReport.WARNING
|
||||
LockFileMismatchReport.FAIL -> YarnLockMismatchReport.FAIL
|
||||
}
|
||||
+8
-11
@@ -7,39 +7,36 @@ package org.jetbrains.kotlin.gradle.targets.js.yarn
|
||||
|
||||
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.YarnEnvironment
|
||||
import org.jetbrains.kotlin.gradle.targets.js.npm.NpmApiExecution
|
||||
import org.jetbrains.kotlin.gradle.targets.js.npm.NodeJsEnvironment
|
||||
import org.jetbrains.kotlin.gradle.targets.js.npm.resolved.PreparedKotlinCompilationNpmResolution
|
||||
import java.io.File
|
||||
|
||||
class Yarn : NpmApi {
|
||||
class Yarn : NpmApiExecution<YarnEnvironment> {
|
||||
private val yarnWorkspaces = YarnWorkspaces()
|
||||
|
||||
override fun preparedFiles(nodeJs: NpmEnvironment): Collection<File> =
|
||||
override fun preparedFiles(nodeJs: NodeJsEnvironment): Collection<File> =
|
||||
yarnWorkspaces.preparedFiles(nodeJs)
|
||||
|
||||
override fun prepareRootProject(
|
||||
nodeJs: NpmEnvironment,
|
||||
nodeJs: NodeJsEnvironment,
|
||||
yarn: YarnEnvironment,
|
||||
rootProjectName: String,
|
||||
rootProjectVersion: String,
|
||||
logger: Logger,
|
||||
subProjects: Collection<PreparedKotlinCompilationNpmResolution>,
|
||||
resolutions: Map<String, String>,
|
||||
) = yarnWorkspaces
|
||||
.prepareRootProject(
|
||||
nodeJs,
|
||||
yarn,
|
||||
rootProjectName,
|
||||
rootProjectVersion,
|
||||
logger,
|
||||
subProjects,
|
||||
resolutions,
|
||||
)
|
||||
|
||||
override fun resolveRootProject(
|
||||
services: ServiceRegistry,
|
||||
logger: Logger,
|
||||
nodeJs: NpmEnvironment,
|
||||
nodeJs: NodeJsEnvironment,
|
||||
yarn: YarnEnvironment,
|
||||
npmProjects: Collection<PreparedKotlinCompilationNpmResolution>,
|
||||
cliArgs: List<String>
|
||||
|
||||
+5
-10
@@ -8,17 +8,16 @@ package org.jetbrains.kotlin.gradle.targets.js.yarn
|
||||
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.NpmEnvironment
|
||||
import org.jetbrains.kotlin.gradle.targets.js.npm.YarnEnvironment
|
||||
import org.jetbrains.kotlin.gradle.targets.js.npm.NpmApiExecution
|
||||
import org.jetbrains.kotlin.gradle.targets.js.npm.NodeJsEnvironment
|
||||
import java.io.File
|
||||
|
||||
abstract class YarnBasics : NpmApi {
|
||||
abstract class YarnBasics : NpmApiExecution<YarnEnvironment> {
|
||||
|
||||
fun yarnExec(
|
||||
services: ServiceRegistry,
|
||||
logger: Logger,
|
||||
nodeJs: NpmEnvironment,
|
||||
nodeJs: NodeJsEnvironment,
|
||||
yarn: YarnEnvironment,
|
||||
dir: File,
|
||||
description: String,
|
||||
@@ -35,11 +34,7 @@ abstract class YarnBasics : NpmApi {
|
||||
|
||||
val nodeExecutable = nodeJs.nodeExecutable
|
||||
if (!yarn.ignoreScripts) {
|
||||
val nodePath = if (nodeJs.isWindows) {
|
||||
File(nodeExecutable).parent
|
||||
} else {
|
||||
nodeExecutable
|
||||
}
|
||||
val nodePath = File(nodeExecutable).parent
|
||||
exec.environment(
|
||||
"PATH",
|
||||
"$nodePath${File.pathSeparator}${System.getenv("PATH")}"
|
||||
|
||||
-17
@@ -1,17 +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 org.gradle.api.GradleException
|
||||
import org.jetbrains.kotlin.gradle.targets.js.npm.FILE_VERSION_PREFIX
|
||||
import org.jetbrains.kotlin.gradle.targets.js.npm.includedRange
|
||||
import org.jetbrains.kotlin.gradle.targets.js.npm.intersect
|
||||
import java.io.File
|
||||
|
||||
private const val SEPARATOR = "@"
|
||||
|
||||
private const val FILE_MARKER = "${SEPARATOR}$FILE_VERSION_PREFIX"
|
||||
private const val GITHUB_MARKER = "${SEPARATOR}github:"
|
||||
+4
-2
@@ -16,10 +16,12 @@ data class YarnEnv(
|
||||
override val dir: File,
|
||||
val executable: String,
|
||||
override val ivyDependency: String,
|
||||
val standalone: Boolean,
|
||||
val ignoreScripts: Boolean,
|
||||
val yarnLockMismatchReport: YarnLockMismatchReport,
|
||||
val reportNewYarnLock: Boolean,
|
||||
val yarnLockAutoReplace: Boolean,
|
||||
val yarnResolutions: List<YarnResolution>
|
||||
) : AbstractEnv
|
||||
) : AbstractEnv {
|
||||
val standalone: Boolean
|
||||
get() = !download
|
||||
}
|
||||
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright 2010-2023 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 org.jetbrains.kotlin.gradle.targets.js.nodejs.PackageManagerEnvironment
|
||||
|
||||
data class YarnEnvironment(
|
||||
val executable: String,
|
||||
val standalone: Boolean,
|
||||
val ignoreScripts: Boolean,
|
||||
val yarnResolutions: List<YarnResolution>
|
||||
) : PackageManagerEnvironment
|
||||
|
||||
internal val YarnEnv.asYarnEnvironment
|
||||
get() = YarnEnvironment(
|
||||
executable,
|
||||
standalone,
|
||||
ignoreScripts,
|
||||
yarnResolutions
|
||||
)
|
||||
-103
@@ -1,103 +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.yarn
|
||||
|
||||
import org.gradle.api.DefaultTask
|
||||
import org.gradle.api.GradleException
|
||||
import org.gradle.api.file.DirectoryProperty
|
||||
import org.gradle.api.file.FileSystemOperations
|
||||
import org.gradle.api.file.RegularFileProperty
|
||||
import org.gradle.api.provider.Property
|
||||
import org.gradle.api.provider.Provider
|
||||
import org.gradle.api.tasks.*
|
||||
import org.gradle.work.DisableCachingByDefault
|
||||
import org.gradle.work.NormalizeLineEndings
|
||||
import org.jetbrains.kotlin.gradle.utils.contentEquals
|
||||
import java.io.File
|
||||
import javax.inject.Inject
|
||||
|
||||
@DisableCachingByDefault
|
||||
abstract class YarnLockCopyTask : DefaultTask() {
|
||||
|
||||
@get:NormalizeLineEndings
|
||||
@get:InputFile
|
||||
@get:PathSensitive(PathSensitivity.RELATIVE)
|
||||
abstract val inputFile: RegularFileProperty
|
||||
|
||||
@get:Internal
|
||||
abstract val outputDirectory: DirectoryProperty
|
||||
|
||||
@get:Internal
|
||||
abstract val fileName: Property<String>
|
||||
|
||||
@get:OutputFile
|
||||
val outputFile: Provider<File>
|
||||
get() = outputDirectory.map { regularFile ->
|
||||
regularFile.asFile.resolve(fileName.get())
|
||||
}
|
||||
|
||||
@get:Inject
|
||||
abstract val fs: FileSystemOperations
|
||||
|
||||
@TaskAction
|
||||
open fun copy() {
|
||||
fs.copy { copy ->
|
||||
copy.from(inputFile)
|
||||
copy.rename { fileName.get() }
|
||||
copy.into(outputDirectory)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
val STORE_YARN_LOCK_NAME = "kotlinStoreYarnLock"
|
||||
val RESTORE_YARN_LOCK_NAME = "kotlinRestoreYarnLock"
|
||||
val UPGRADE_YARN_LOCK = "kotlinUpgradeYarnLock"
|
||||
val YARN_LOCK_MISMATCH_MESSAGE = "yarn.lock was changed. Run the `${YarnLockCopyTask.UPGRADE_YARN_LOCK}` task to actualize yarn.lock file"
|
||||
}
|
||||
}
|
||||
|
||||
@DisableCachingByDefault
|
||||
abstract class YarnLockStoreTask : YarnLockCopyTask() {
|
||||
@Input
|
||||
lateinit var yarnLockMismatchReport: Provider<YarnLockMismatchReport>
|
||||
|
||||
@Input
|
||||
lateinit var reportNewYarnLock: Provider<Boolean>
|
||||
|
||||
@Input
|
||||
lateinit var yarnLockAutoReplace: Provider<Boolean>
|
||||
|
||||
override fun copy() {
|
||||
val outputFile = outputDirectory.get().asFile.resolve(fileName.get())
|
||||
|
||||
val shouldReportMismatch = if (!outputFile.exists()) {
|
||||
reportNewYarnLock.get()
|
||||
} else {
|
||||
yarnLockMismatchReport.get() != YarnLockMismatchReport.NONE && !contentEquals(inputFile.get().asFile, outputFile)
|
||||
}
|
||||
|
||||
if (!outputFile.exists() || yarnLockAutoReplace.get()) {
|
||||
super.copy()
|
||||
}
|
||||
|
||||
if (shouldReportMismatch) {
|
||||
when (yarnLockMismatchReport.get()) {
|
||||
YarnLockMismatchReport.NONE -> {}
|
||||
YarnLockMismatchReport.WARNING -> {
|
||||
logger.warn(YARN_LOCK_MISMATCH_MESSAGE)
|
||||
}
|
||||
YarnLockMismatchReport.FAIL -> throw GradleException(YARN_LOCK_MISMATCH_MESSAGE)
|
||||
else -> error("Unknown yarn.lock mismatch report kind")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum class YarnLockMismatchReport {
|
||||
NONE,
|
||||
WARNING,
|
||||
FAIL,
|
||||
}
|
||||
+35
-81
@@ -7,22 +7,14 @@ package org.jetbrains.kotlin.gradle.targets.js.yarn
|
||||
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.tasks.TaskProvider
|
||||
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.LockCopyTask
|
||||
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.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.targets.js.yarn.YarnLockCopyTask.Companion.UPGRADE_YARN_LOCK
|
||||
import org.jetbrains.kotlin.gradle.tasks.CleanDataTask
|
||||
import org.jetbrains.kotlin.gradle.tasks.registerTask
|
||||
import org.jetbrains.kotlin.gradle.utils.detachedResolvable
|
||||
import org.jetbrains.kotlin.gradle.utils.onlyIfCompat
|
||||
|
||||
open class YarnPlugin : Plugin<Project> {
|
||||
override fun apply(project: Project): Unit = project.run {
|
||||
@@ -40,6 +32,10 @@ open class YarnPlugin : Plugin<Project> {
|
||||
yarnRootExtension.platform.value(nodeJs.platform)
|
||||
.disallowChanges()
|
||||
|
||||
nodeJs.packageManagerExtension.set(
|
||||
yarnRootExtension
|
||||
)
|
||||
|
||||
val setupTask = registerTask<YarnSetupTask>(YarnSetupTask.NAME) {
|
||||
it.dependsOn(nodeJsTaskProviders.nodeJsSetupTaskProvider)
|
||||
|
||||
@@ -52,110 +48,64 @@ open class YarnPlugin : Plugin<Project> {
|
||||
}
|
||||
}
|
||||
|
||||
val kotlinNpmResolutionManager = project.kotlinNpmResolutionManager
|
||||
|
||||
val rootPackageJson = tasks.register(RootPackageJsonTask.NAME, RootPackageJsonTask::class.java) { task ->
|
||||
task.dependsOn(nodeJsTaskProviders.npmCachesSetupTaskProvider)
|
||||
task.group = NodeJsRootPlugin.TASKS_GROUP_NAME
|
||||
task.description = "Create root package.json"
|
||||
|
||||
task.npmResolutionManager.value(kotlinNpmResolutionManager)
|
||||
.disallowChanges()
|
||||
|
||||
task.onlyIfCompat("Prepare NPM project only in configuring state") {
|
||||
it as RootPackageJsonTask
|
||||
it.npmResolutionManager.get().isConfiguringState()
|
||||
}
|
||||
}
|
||||
|
||||
configureRequiresNpmDependencies(project, rootPackageJson)
|
||||
|
||||
val kotlinNpmInstall = tasks.named(KotlinNpmInstallTask.NAME)
|
||||
kotlinNpmInstall.configure {
|
||||
it.dependsOn(rootPackageJson)
|
||||
it.dependsOn(setupTask)
|
||||
it.inputs.property("ignoreScripts", { yarnRootExtension.ignoreScripts })
|
||||
it.inputs.property("yarnIgnoreScripts", { yarnRootExtension.ignoreScripts })
|
||||
}
|
||||
|
||||
yarnRootExtension.nodeJsEnvironment.value(
|
||||
project.provider {
|
||||
nodeJs.requireConfigured()
|
||||
}
|
||||
).disallowChanges()
|
||||
|
||||
tasks.register("yarn" + CleanDataTask.NAME_SUFFIX, CleanDataTask::class.java) {
|
||||
it.cleanableStoreProvider = provider { yarnRootExtension.requireConfigured().cleanableStore }
|
||||
it.description = "Clean unused local yarn version"
|
||||
}
|
||||
|
||||
val packageJsonUmbrella = nodeJsTaskProviders
|
||||
.packageJsonUmbrellaTaskProvider
|
||||
|
||||
yarnRootExtension.rootPackageJsonTaskProvider.configure {
|
||||
it.dependsOn(packageJsonUmbrella)
|
||||
}
|
||||
|
||||
tasks.register(STORE_YARN_LOCK_NAME, YarnLockStoreTask::class.java) { task ->
|
||||
task.dependsOn(kotlinNpmInstall)
|
||||
task.inputFile.set(nodeJs.rootPackageDirectory.map { it.file("yarn.lock") })
|
||||
task.inputFile.set(nodeJs.rootPackageDirectory.map { it.file(LockCopyTask.YARN_LOCK) })
|
||||
task.outputDirectory.set(yarnRootExtension.lockFileDirectory)
|
||||
task.fileName.set(yarnRootExtension.lockFileName)
|
||||
|
||||
task.yarnLockMismatchReport = provider { yarnRootExtension.requireConfigured().yarnLockMismatchReport }
|
||||
task.reportNewYarnLock = provider { yarnRootExtension.requireConfigured().reportNewYarnLock }
|
||||
task.yarnLockAutoReplace = provider { yarnRootExtension.requireConfigured().yarnLockAutoReplace }
|
||||
task.lockFileMismatchReport.value(
|
||||
provider { yarnRootExtension.requireConfigured().yarnLockMismatchReport.toLockFileMismatchReport() }
|
||||
).disallowChanges()
|
||||
task.reportNewLockFile.value(
|
||||
provider { yarnRootExtension.requireConfigured().reportNewYarnLock }
|
||||
).disallowChanges()
|
||||
task.lockFileAutoReplace.value(
|
||||
provider { yarnRootExtension.requireConfigured().yarnLockAutoReplace }
|
||||
).disallowChanges()
|
||||
}
|
||||
|
||||
tasks.register(UPGRADE_YARN_LOCK, YarnLockCopyTask::class.java) { task ->
|
||||
task.dependsOn(kotlinNpmInstall)
|
||||
task.inputFile.set(nodeJs.rootPackageDirectory.map { it.file("yarn.lock") })
|
||||
task.inputFile.set(nodeJs.rootPackageDirectory.map { it.file(LockCopyTask.YARN_LOCK) })
|
||||
task.outputDirectory.set(yarnRootExtension.lockFileDirectory)
|
||||
task.fileName.set(yarnRootExtension.lockFileName)
|
||||
}
|
||||
|
||||
val restoreYarnLock = tasks.register(RESTORE_YARN_LOCK_NAME, YarnLockCopyTask::class.java) {
|
||||
tasks.register(RESTORE_YARN_LOCK_NAME, YarnLockCopyTask::class.java) {
|
||||
val lockFile = yarnRootExtension.lockFileDirectory.resolve(yarnRootExtension.lockFileName)
|
||||
it.inputFile.set(yarnRootExtension.lockFileDirectory.resolve(yarnRootExtension.lockFileName))
|
||||
it.outputDirectory.set(nodeJs.rootPackageDirectory)
|
||||
it.fileName.set("yarn.lock")
|
||||
it.fileName.set(LockCopyTask.YARN_LOCK)
|
||||
it.onlyIf {
|
||||
lockFile.exists()
|
||||
}
|
||||
}
|
||||
|
||||
kotlinNpmInstall.configure {
|
||||
it.dependsOn(restoreYarnLock)
|
||||
}
|
||||
}
|
||||
yarnRootExtension.preInstallTasks.value(
|
||||
listOf(yarnRootExtension.restoreYarnLockTaskProvider)
|
||||
).disallowChanges()
|
||||
|
||||
// Yes, we need to break Task Configuration Avoidance here
|
||||
// In case when we need to create package.json's files and execute kotlinNpmInstall,
|
||||
// We need to configure all RequiresNpmDependencies tasks to install them,
|
||||
// Because we need to persist yarn.lock
|
||||
// We execute this block in configure phase of rootPackageJson to be sure,
|
||||
// That Task Configuration Avoidance will not be broken for tasks not related with NPM installing
|
||||
// https://youtrack.jetbrains.com/issue/KT-48241
|
||||
private fun configureRequiresNpmDependencies(
|
||||
project: Project,
|
||||
rootPackageJson: TaskProvider<RootPackageJsonTask>,
|
||||
) {
|
||||
val fn: (Project) -> Unit = {
|
||||
it.tasks.implementing(RequiresNpmDependencies::class)
|
||||
.forEach {}
|
||||
}
|
||||
rootPackageJson.configure {
|
||||
project.allprojects
|
||||
.forEach { project ->
|
||||
if (it.state.executed) {
|
||||
fn(project)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
project.allprojects
|
||||
.forEach {
|
||||
if (!it.state.executed) {
|
||||
it.afterEvaluate { project ->
|
||||
rootPackageJson.configure {
|
||||
fn(project)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
yarnRootExtension.postInstallTasks.value(
|
||||
listOf(yarnRootExtension.storeYarnLockTaskProvider)
|
||||
).disallowChanges()
|
||||
}
|
||||
|
||||
companion object {
|
||||
@@ -164,5 +114,9 @@ open class YarnPlugin : Plugin<Project> {
|
||||
rootProject.plugins.apply(YarnPlugin::class.java)
|
||||
return rootProject.extensions.getByName(YarnRootExtension.YARN) as YarnRootExtension
|
||||
}
|
||||
|
||||
const val STORE_YARN_LOCK_NAME = "kotlinStoreYarnLock"
|
||||
const val RESTORE_YARN_LOCK_NAME = "kotlinRestoreYarnLock"
|
||||
const val UPGRADE_YARN_LOCK = "kotlinUpgradeYarnLock"
|
||||
}
|
||||
}
|
||||
|
||||
+37
-10
@@ -7,17 +7,24 @@ package org.jetbrains.kotlin.gradle.targets.js.yarn
|
||||
|
||||
import org.gradle.api.Action
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.Task
|
||||
import org.gradle.api.file.FileCollection
|
||||
import org.gradle.api.provider.ListProperty
|
||||
import org.gradle.api.tasks.TaskProvider
|
||||
import org.jetbrains.kotlin.gradle.logging.kotlinInfo
|
||||
import org.jetbrains.kotlin.gradle.targets.js.AbstractSettings
|
||||
import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsEnv
|
||||
import org.jetbrains.kotlin.gradle.targets.js.nodejs.NpmApiExtension
|
||||
import org.jetbrains.kotlin.gradle.targets.js.nodejs.Platform
|
||||
import org.jetbrains.kotlin.gradle.targets.js.npm.tasks.RootPackageJsonTask
|
||||
import org.jetbrains.kotlin.gradle.targets.js.npm.LockCopyTask
|
||||
import org.jetbrains.kotlin.gradle.targets.js.yarn.YarnPlugin.Companion.RESTORE_YARN_LOCK_NAME
|
||||
import org.jetbrains.kotlin.gradle.targets.js.yarn.YarnPlugin.Companion.STORE_YARN_LOCK_NAME
|
||||
import org.jetbrains.kotlin.gradle.tasks.internal.CleanableStore
|
||||
import java.io.File
|
||||
|
||||
open class YarnRootExtension(
|
||||
val project: Project
|
||||
) : AbstractSettings<YarnEnv>() {
|
||||
) : AbstractSettings<YarnEnv>(), NpmApiExtension<YarnEnvironment, Yarn> {
|
||||
init {
|
||||
check(project == project.rootProject)
|
||||
}
|
||||
@@ -26,6 +33,24 @@ open class YarnRootExtension(
|
||||
project.logger.kotlinInfo("Storing cached files in $it")
|
||||
}
|
||||
|
||||
override val packageManager: Yarn by lazy {
|
||||
Yarn()
|
||||
}
|
||||
|
||||
override val environment: YarnEnvironment by lazy {
|
||||
requireConfigured().asYarnEnvironment
|
||||
}
|
||||
|
||||
override val additionalInstallOutput: FileCollection = project.objects.fileCollection().from(
|
||||
{
|
||||
nodeJsEnvironment.get().rootPackageDir.resolve(LockCopyTask.YARN_LOCK)
|
||||
}
|
||||
)
|
||||
|
||||
override val preInstallTasks: ListProperty<TaskProvider<*>> = project.objects.listProperty(TaskProvider::class.java)
|
||||
|
||||
override val postInstallTasks: ListProperty<TaskProvider<*>> = project.objects.listProperty(TaskProvider::class.java)
|
||||
|
||||
override var installationDir by Property(gradleHome.resolve("yarn"))
|
||||
|
||||
override var downloadBaseUrl: String? by Property("https://github.com/yarnpkg/yarn/releases/download")
|
||||
@@ -34,8 +59,8 @@ open class YarnRootExtension(
|
||||
override var command by Property("yarn")
|
||||
|
||||
override var download by Property(true)
|
||||
var lockFileName by Property("yarn.lock")
|
||||
var lockFileDirectory: File by Property(project.rootDir.resolve("kotlin-js-store"))
|
||||
var lockFileName by Property(LockCopyTask.YARN_LOCK)
|
||||
var lockFileDirectory: File by Property(project.rootDir.resolve(LockCopyTask.KOTLIN_JS_STORE))
|
||||
|
||||
var ignoreScripts by Property(true)
|
||||
|
||||
@@ -50,11 +75,6 @@ open class YarnRootExtension(
|
||||
.withType(YarnSetupTask::class.java)
|
||||
.named(YarnSetupTask.NAME)
|
||||
|
||||
val rootPackageJsonTaskProvider: TaskProvider<RootPackageJsonTask>
|
||||
get() = project.tasks
|
||||
.withType(RootPackageJsonTask::class.java)
|
||||
.named(RootPackageJsonTask.NAME)
|
||||
|
||||
internal val platform: org.gradle.api.provider.Property<Platform> = project.objects.property(Platform::class.java)
|
||||
|
||||
var resolutions: MutableList<YarnResolution> by Property(mutableListOf())
|
||||
@@ -72,6 +92,8 @@ open class YarnRootExtension(
|
||||
})
|
||||
}
|
||||
|
||||
internal val nodeJsEnvironment: org.gradle.api.provider.Property<NodeJsEnv> = project.objects.property(NodeJsEnv::class.java)
|
||||
|
||||
override fun finalizeConfiguration(): YarnEnv {
|
||||
val cleanableStore = CleanableStore[installationDir.path]
|
||||
|
||||
@@ -93,7 +115,6 @@ open class YarnRootExtension(
|
||||
cleanableStore = cleanableStore,
|
||||
dir = home,
|
||||
executable = getExecutable("yarn", command, "cmd"),
|
||||
standalone = !download,
|
||||
ivyDependency = "com.yarnpkg:yarn:$version@tar.gz",
|
||||
ignoreScripts = ignoreScripts,
|
||||
yarnLockMismatchReport = yarnLockMismatchReport,
|
||||
@@ -103,6 +124,12 @@ open class YarnRootExtension(
|
||||
)
|
||||
}
|
||||
|
||||
val restoreYarnLockTaskProvider: TaskProvider<YarnLockCopyTask>
|
||||
get() = project.tasks.withType(YarnLockCopyTask::class.java).named(RESTORE_YARN_LOCK_NAME)
|
||||
|
||||
val storeYarnLockTaskProvider: TaskProvider<YarnLockStoreTask>
|
||||
get() = project.tasks.withType(YarnLockStoreTask::class.java).named(STORE_YARN_LOCK_NAME)
|
||||
|
||||
companion object {
|
||||
const val YARN: String = "kotlinYarn"
|
||||
|
||||
|
||||
+10
-14
@@ -13,7 +13,7 @@ import org.jetbrains.kotlin.gradle.utils.getFile
|
||||
import java.io.File
|
||||
|
||||
class YarnWorkspaces : YarnBasics() {
|
||||
override fun preparedFiles(nodeJs: NpmEnvironment): Collection<File> {
|
||||
override fun preparedFiles(nodeJs: NodeJsEnvironment): Collection<File> {
|
||||
return listOf(
|
||||
nodeJs
|
||||
.rootPackageDir
|
||||
@@ -22,28 +22,26 @@ class YarnWorkspaces : YarnBasics() {
|
||||
}
|
||||
|
||||
override fun prepareRootProject(
|
||||
nodeJs: NpmEnvironment,
|
||||
nodeJs: NodeJsEnvironment,
|
||||
yarn: YarnEnvironment,
|
||||
rootProjectName: String,
|
||||
rootProjectVersion: String,
|
||||
logger: Logger,
|
||||
subProjects: Collection<PreparedKotlinCompilationNpmResolution>,
|
||||
resolutions: Map<String, String>,
|
||||
) {
|
||||
return prepareRootPackageJson(
|
||||
nodeJs,
|
||||
rootProjectName,
|
||||
rootProjectVersion,
|
||||
logger,
|
||||
subProjects,
|
||||
resolutions
|
||||
yarn.yarnResolutions
|
||||
.associate { it.path to it.toVersionString() },
|
||||
)
|
||||
}
|
||||
|
||||
private fun prepareRootPackageJson(
|
||||
nodeJs: NpmEnvironment,
|
||||
nodeJs: NodeJsEnvironment,
|
||||
rootProjectName: String,
|
||||
rootProjectVersion: String,
|
||||
logger: Logger,
|
||||
npmProjects: Collection<PreparedKotlinCompilationNpmResolution>,
|
||||
resolutions: Map<String, String>
|
||||
) {
|
||||
@@ -52,7 +50,6 @@ class YarnWorkspaces : YarnBasics() {
|
||||
saveRootProjectWorkspacesPackageJson(
|
||||
rootProjectName,
|
||||
rootProjectVersion,
|
||||
logger,
|
||||
npmProjects,
|
||||
resolutions,
|
||||
rootPackageJsonFile
|
||||
@@ -62,7 +59,7 @@ class YarnWorkspaces : YarnBasics() {
|
||||
override fun resolveRootProject(
|
||||
services: ServiceRegistry,
|
||||
logger: Logger,
|
||||
nodeJs: NpmEnvironment,
|
||||
nodeJs: NodeJsEnvironment,
|
||||
yarn: YarnEnvironment,
|
||||
npmProjects: Collection<PreparedKotlinCompilationNpmResolution>,
|
||||
cliArgs: List<String>
|
||||
@@ -75,7 +72,7 @@ class YarnWorkspaces : YarnBasics() {
|
||||
nodeJs,
|
||||
yarn,
|
||||
nodeJsWorldDir,
|
||||
NpmApi.resolveOperationDescription("yarn"),
|
||||
NpmApiExecution.resolveOperationDescription("yarn"),
|
||||
cliArgs
|
||||
)
|
||||
}
|
||||
@@ -83,7 +80,6 @@ class YarnWorkspaces : YarnBasics() {
|
||||
private fun saveRootProjectWorkspacesPackageJson(
|
||||
rootProjectName: String,
|
||||
rootProjectVersion: String,
|
||||
logger: Logger,
|
||||
npmProjects: Collection<PreparedKotlinCompilationNpmResolution>,
|
||||
resolutions: Map<String, String>,
|
||||
rootPackageJsonFile: File
|
||||
@@ -94,10 +90,10 @@ class YarnWorkspaces : YarnBasics() {
|
||||
|
||||
val npmProjectWorkspaces = npmProjects.map { it.npmProjectDir.getFile().relativeTo(nodeJsWorldDir).path }
|
||||
val importedProjectWorkspaces =
|
||||
YarnImportedPackagesVersionResolver(logger, npmProjects, nodeJsWorldDir).resolveAndUpdatePackages()
|
||||
NpmImportedPackagesVersionResolver(npmProjects, nodeJsWorldDir).resolveAndUpdatePackages()
|
||||
|
||||
rootPackageJson.workspaces = npmProjectWorkspaces + importedProjectWorkspaces
|
||||
rootPackageJson.resolutions = resolutions
|
||||
rootPackageJson.customField("resolutions", resolutions)
|
||||
rootPackageJson.saveTo(
|
||||
rootPackageJsonFile
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user