experiment: cache NPM dependencies via Gradle
diff --git a/build.gradle.kts b/build.gradle.kts
index 0870e26..293e9ad 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -640,6 +640,22 @@
}
}
+ // must be first, otherwise Gradle tries to fetch POM files from the cache-redirector repos,
+ // which fails because cache-director doesn't support HEAD requests.
+ ivy("https://registry.npmjs.org/") {
+ name = "npm repo for gradle-node-plugin"
+ patternLayout {
+ artifact("[orgPath]/-/[module]-[revision].[ext]")
+// artifact("[orgPath]/-/[module]-[revision].[ext]")
+ }
+ metadataSources {
+ artifact()
+ }
+ content {
+ onlyForAttribute(Usage.USAGE_ATTRIBUTE, objects.named("npm-dependencies-cache"))
+ }
+ }
+
mirrorRepo?.let(::maven)
maven(intellijRepo) {
@@ -688,6 +704,29 @@
}
}
+// exclusiveContent {
+// forRepository {
+//// ivy("https://packages.jetbrains.team/") { // packages.jetbrains.team doesn't support HEAD?
+// ivy("https://registry.npmjs.org/") {
+// name = "npm repo for gradle-node-plugin"
+// patternLayout {
+// // https://packages.jetbrains.team/npm/p/kt/kotlin-dependencies/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.40.0.tgz
+// artifact("[orgPath]/-/@[module]-[revision].[ext]")
+// artifact("[orgPath]/-/[module]-[revision].[ext]")
+// }
+// metadataSources {
+// artifact()
+// }
+//// content {
+//// onlyForAttribute(Usage.USAGE_ATTRIBUTE, objects.named("npm-dependencies-cache"))
+//// }
+// }
+// }
+// filter {
+// includeGroupAndSubgroups("npm.p.kt.kotlin-dependencies")
+// }
+// }
+
mavenCentral()
}
}
diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml
index cdf0373..75dffb4 100644
--- a/gradle/verification-metadata.xml
+++ b/gradle/verification-metadata.xml
@@ -16,6 +16,151 @@
</trusted-artifacts>
</configuration>
<components>
+ <component group="@jridgewell.gen-mapping" name="gen-mapping" version="0.3.8">
+ <artifact name="gen-mapping-0.3.8.tgz">
+ <sha256 value="eea6fe4688f23b4f006fddac83e5d5cfbf9235795f1e0027776ca1cb6f5b2248" origin="Generated by Gradle"/>
+ </artifact>
+ </component>
+ <component group="@jridgewell.resolve-uri" name="resolve-uri" version="3.1.2">
+ <artifact name="resolve-uri-3.1.2.tgz">
+ <sha256 value="db52f9f62558baab13353dd39e3200750fc33e6a129a575b46226f493776e230" origin="Generated by Gradle"/>
+ </artifact>
+ </component>
+ <component group="@jridgewell.set-array" name="set-array" version="1.2.1">
+ <artifact name="set-array-1.2.1.tgz">
+ <sha256 value="6fa2237de1ac707fb3e93152272ca5478319db7f97ed301821963a3780bf0648" origin="Generated by Gradle"/>
+ </artifact>
+ </component>
+ <component group="@jridgewell.source-map" name="source-map" version="0.3.6">
+ <artifact name="source-map-0.3.6.tgz">
+ <sha256 value="359aa9f6efb7c78e9bc06ec2ee2c0e60f35130c0265a24631c2fd2e9ff40d61c" origin="Generated by Gradle"/>
+ </artifact>
+ </component>
+ <component group="@jridgewell.sourcemap-codec" name="sourcemap-codec" version="1.5.0">
+ <artifact name="sourcemap-codec-1.5.0.tgz">
+ <sha256 value="bb27e266358b0ab7ab8a776d875d195c57ae9f24224045716dce2b51205f1b5d" origin="Generated by Gradle"/>
+ </artifact>
+ </component>
+ <component group="@jridgewell.trace-mapping" name="trace-mapping" version="0.3.25">
+ <artifact name="trace-mapping-0.3.25.tgz">
+ <sha256 value="fff09d294a0cffa226f658d6b7c2ca897b57b80f54cc6eeac545ab11574241ed" origin="Generated by Gradle"/>
+ </artifact>
+ </component>
+ <component group="@rollup.plugin-terser" name="plugin-terser" version="0.4.4">
+ <artifact name="plugin-terser-0.4.4.tgz">
+ <sha256 value="087fc82de5e7c904508ecf5f2c8c24f3df2af501859ceaf1b789f3c7c26b32c9" origin="Generated by Gradle"/>
+ </artifact>
+ </component>
+ <component group="@rollup.rollup-android-arm-eabi" name="rollup-android-arm-eabi" version="4.40.0">
+ <artifact name="rollup-android-arm-eabi-4.40.0.tgz">
+ <sha256 value="f4be4affb12c1a76b735055d6cd0b8bae056fefe0baa9396a0fae7508a50b2c6" origin="Generated by Gradle"/>
+ </artifact>
+ </component>
+ <component group="@rollup.rollup-android-arm64" name="rollup-android-arm64" version="4.40.0">
+ <artifact name="rollup-android-arm64-4.40.0.tgz">
+ <sha256 value="b026b8e1e53611f4de4bc0d94ae4d01ae48e0abf5af40170831e2cb6605a5f88" origin="Generated by Gradle"/>
+ </artifact>
+ </component>
+ <component group="@rollup.rollup-darwin-arm64" name="rollup-darwin-arm64" version="4.40.0">
+ <artifact name="rollup-darwin-arm64-4.40.0.tgz">
+ <sha256 value="5cbdfd1c6596c0e8e7d9272bbeabd6f4588e77ab96f72d92d45ca16ebb03cfd3" origin="Generated by Gradle"/>
+ </artifact>
+ </component>
+ <component group="@rollup.rollup-darwin-x64" name="rollup-darwin-x64" version="4.40.0">
+ <artifact name="rollup-darwin-x64-4.40.0.tgz">
+ <sha256 value="efdf02ffe85356a0ca3bb05b141a6d63d92146554fd8539322bae288487558bb" origin="Generated by Gradle"/>
+ </artifact>
+ </component>
+ <component group="@rollup.rollup-freebsd-arm64" name="rollup-freebsd-arm64" version="4.40.0">
+ <artifact name="rollup-freebsd-arm64-4.40.0.tgz">
+ <sha256 value="06f33247a20c3f85eb9f45f8f36031f527196d3d87f3ca7b68aa4e406ecdf7fb" origin="Generated by Gradle"/>
+ </artifact>
+ </component>
+ <component group="@rollup.rollup-freebsd-x64" name="rollup-freebsd-x64" version="4.40.0">
+ <artifact name="rollup-freebsd-x64-4.40.0.tgz">
+ <sha256 value="8e9c6a7e0b2dbd7f90f2bfa17dcb8e2fd2f97784ba1010dcc45f8687febf9c16" origin="Generated by Gradle"/>
+ </artifact>
+ </component>
+ <component group="@rollup.rollup-linux-arm-gnueabihf" name="rollup-linux-arm-gnueabihf" version="4.40.0">
+ <artifact name="rollup-linux-arm-gnueabihf-4.40.0.tgz">
+ <sha256 value="710f04a8c8b68d16758b1a21e0fd282eb064f9be2aea1b03f33a20666b286c11" origin="Generated by Gradle"/>
+ </artifact>
+ </component>
+ <component group="@rollup.rollup-linux-arm-musleabihf" name="rollup-linux-arm-musleabihf" version="4.40.0">
+ <artifact name="rollup-linux-arm-musleabihf-4.40.0.tgz">
+ <sha256 value="df31fd3b6bb227dc88f079cc3fdca737cb66dab8410e07ad58446b220766c3d3" origin="Generated by Gradle"/>
+ </artifact>
+ </component>
+ <component group="@rollup.rollup-linux-arm64-gnu" name="rollup-linux-arm64-gnu" version="4.40.0">
+ <artifact name="rollup-linux-arm64-gnu-4.40.0.tgz">
+ <sha256 value="504f689935d9778fad27e900930623ec7739b5e8c1d6cf71e3c6e8cfdb30bfa4" origin="Generated by Gradle"/>
+ </artifact>
+ </component>
+ <component group="@rollup.rollup-linux-arm64-musl" name="rollup-linux-arm64-musl" version="4.40.0">
+ <artifact name="rollup-linux-arm64-musl-4.40.0.tgz">
+ <sha256 value="1bd1153b2abf5beefba34284163313688c967472ef7129e588c7da4376cfcd9f" origin="Generated by Gradle"/>
+ </artifact>
+ </component>
+ <component group="@rollup.rollup-linux-loongarch64-gnu" name="rollup-linux-loongarch64-gnu" version="4.40.0">
+ <artifact name="rollup-linux-loongarch64-gnu-4.40.0.tgz">
+ <sha256 value="f39fd6397de933a66fbeea811a2f1c1313dad52197bba0c322768091d0ea68be" origin="Generated by Gradle"/>
+ </artifact>
+ </component>
+ <component group="@rollup.rollup-linux-powerpc64le-gnu" name="rollup-linux-powerpc64le-gnu" version="4.40.0">
+ <artifact name="rollup-linux-powerpc64le-gnu-4.40.0.tgz">
+ <sha256 value="5dacd37c50adcd5a84562980a5f1d13b7413f6817880803357dc93ef6f220421" origin="Generated by Gradle"/>
+ </artifact>
+ </component>
+ <component group="@rollup.rollup-linux-riscv64-gnu" name="rollup-linux-riscv64-gnu" version="4.40.0">
+ <artifact name="rollup-linux-riscv64-gnu-4.40.0.tgz">
+ <sha256 value="fe5c584fb75e6bda39bceca0c8616dc0f4237fa760572a4c5f23920d1942d5e7" origin="Generated by Gradle"/>
+ </artifact>
+ </component>
+ <component group="@rollup.rollup-linux-riscv64-musl" name="rollup-linux-riscv64-musl" version="4.40.0">
+ <artifact name="rollup-linux-riscv64-musl-4.40.0.tgz">
+ <sha256 value="00f4edb0182a5f2c98c24e705cbae6def3bb2d6169a058b2dd5988d18edbdd64" origin="Generated by Gradle"/>
+ </artifact>
+ </component>
+ <component group="@rollup.rollup-linux-s390x-gnu" name="rollup-linux-s390x-gnu" version="4.40.0">
+ <artifact name="rollup-linux-s390x-gnu-4.40.0.tgz">
+ <sha256 value="0374203733f33e1175d9d700f1d70f701836e08b13e66295c14fb106ad2b3a69" origin="Generated by Gradle"/>
+ </artifact>
+ </component>
+ <component group="@rollup.rollup-linux-x64-gnu" name="rollup-linux-x64-gnu" version="4.40.0">
+ <artifact name="rollup-linux-x64-gnu-4.40.0.tgz">
+ <sha256 value="648d4bc67bf5dc888a0ddefc3f6dc7dbd2734a2143db38b66e75630b91cca8de" origin="Generated by Gradle"/>
+ </artifact>
+ </component>
+ <component group="@rollup.rollup-linux-x64-musl" name="rollup-linux-x64-musl" version="4.40.0">
+ <artifact name="rollup-linux-x64-musl-4.40.0.tgz">
+ <sha256 value="22fba885b11d18cb4dd978f86a30d9b4c47962b3fbcf80934c141917e3a606f2" origin="Generated by Gradle"/>
+ </artifact>
+ </component>
+ <component group="@rollup.rollup-win32-arm64-msvc" name="rollup-win32-arm64-msvc" version="4.40.0">
+ <artifact name="rollup-win32-arm64-msvc-4.40.0.tgz">
+ <sha256 value="bc608baa6db4b39dac3d7ed55c93d31219ad05435a2e8c493ce71dc243797625" origin="Generated by Gradle"/>
+ </artifact>
+ </component>
+ <component group="@rollup.rollup-win32-ia32-msvc" name="rollup-win32-ia32-msvc" version="4.40.0">
+ <artifact name="rollup-win32-ia32-msvc-4.40.0.tgz">
+ <sha256 value="102f181ef5436266e93f7df9ae517186fca0d530b294533b457a01634cb08f63" origin="Generated by Gradle"/>
+ </artifact>
+ </component>
+ <component group="@rollup.rollup-win32-x64-msvc" name="rollup-win32-x64-msvc" version="4.40.0">
+ <artifact name="rollup-win32-x64-msvc-4.40.0.tgz">
+ <sha256 value="17363f6f7477d140956e12b7ab6d7dff91f051eb9ed38e959b0b389750762d56" origin="Generated by Gradle"/>
+ </artifact>
+ </component>
+ <component group="@types.estree" name="estree" version="1.0.7">
+ <artifact name="estree-1.0.7.tgz">
+ <sha256 value="e6a77ad40eed5771cc43b2181cbeb9fd49e80a1bed6db0396778d587978e4d70" origin="Generated by Gradle"/>
+ </artifact>
+ </component>
+ <component group="acorn" name="acorn" version="8.14.1">
+ <artifact name="acorn-8.14.1.tgz">
+ <sha256 value="e9ca7f9939683a07d74565636ab8cc29f5fbc886a3e7a54af22c2f9e0f59930f" origin="Generated by Gradle"/>
+ </artifact>
+ </component>
<component group="androidx.annotation" name="annotation" version="1.0.0">
<artifact name="annotation-1.0.0.jar">
<md5 value="558104d32e109ad96655ecbe9fe4e39f" origin="Generated by Gradle"/>
@@ -160,6 +305,11 @@
<sha256 value="f5759b7fcdfc83a525a036deedcbd32e5b536b625ebc282426f16ca137eb5902" origin="Generated by Gradle"/>
</artifact>
</component>
+ <component group="buffer-from" name="buffer-from" version="1.1.2">
+ <artifact name="buffer-from-1.1.2.tgz">
+ <sha256 value="9c2b03d59eca8f463a1927e07273ddaa87785fe3f61626c42b005540e962e343" origin="Generated by Gradle"/>
+ </artifact>
+ </component>
<component group="ch.qos.logback" name="logback-classic" version="1.3.15">
<artifact name="logback-classic-1.3.15.jar">
<md5 value="6fae5bf09da5961706c45ac35d72e995" origin="Generated by Gradle"/>
@@ -1678,6 +1828,11 @@
<sha256 value="f76b85adb0c00721ae267b7cfde4da7f71d3121cc2160c9fc00c0c89f8c53c8a" origin="Generated by Gradle"/>
</artifact>
</component>
+ <component group="commander" name="commander" version="2.20.3">
+ <artifact name="commander-2.20.3.tgz">
+ <sha256 value="3c02903de017a98d4dc3fe2bed4900beb0d04cf7b679dca0915fd8c759652b55" origin="Generated by Gradle"/>
+ </artifact>
+ </component>
<component group="commons-beanutils" name="commons-beanutils" version="1.10.1">
<artifact name="commons-beanutils-1.10.1.jar">
<md5 value="27ac839d60e2bff6b222827756fde6cb" origin="Generated by Gradle"/>
@@ -1786,6 +1941,11 @@
<sha256 value="d2720b0feb36272c7d311003a14d77ec108603c38c301329791f6ceedb6396f5" origin="Generated by Gradle"/>
</artifact>
</component>
+ <component group="fsevents" name="fsevents" version="2.3.3">
+ <artifact name="fsevents-2.3.3.tgz">
+ <sha256 value="c77e7a5d5ff31dd7acea7c44d4a0455e0528cdacbd24a8cb6c82b66d239b587e" origin="Generated by Gradle"/>
+ </artifact>
+ </component>
<component group="google" name="91936d4ee3ccc839f0addd53c9ebf087b1e39251.build-tools" version="r30.0.3">
<artifact name="91936d4ee3ccc839f0addd53c9ebf087b1e39251.build-tools-r30.0.3-windows.zip">
<md5 value="f0b89c50696fe827a462080af164f1ef" origin="Generated by Gradle"/>
@@ -5816,6 +5976,46 @@
<sha256 value="1be1aac16d424ce940d5407bef86656dc4ed5803c93e563cb1682ae07b591ecb" origin="Generated by Gradle"/>
</artifact>
</component>
+ <component group="randombytes" name="randombytes" version="2.1.0">
+ <artifact name="randombytes-2.1.0.tgz">
+ <sha256 value="b8a6d3e6532817912ad3dacbb0e64be75026941a5167549aa1d7fecbafd1bcaa" origin="Generated by Gradle"/>
+ </artifact>
+ </component>
+ <component group="rollup" name="rollup" version="4.40.0">
+ <artifact name="rollup-4.40.0.tgz">
+ <sha256 value="c25de66498beddeb3dec3fb9511b88c79b2b9e53c37763a12f46ce2b46387580" origin="Generated by Gradle"/>
+ </artifact>
+ </component>
+ <component group="safe-buffer" name="safe-buffer" version="5.2.1">
+ <artifact name="safe-buffer-5.2.1.tgz">
+ <sha256 value="5d181804516c4a693a384272a7bd0e42d17e0d4b301ccfbe408669ccafdcb3e8" origin="Generated by Gradle"/>
+ </artifact>
+ </component>
+ <component group="serialize-javascript" name="serialize-javascript" version="6.0.2">
+ <artifact name="serialize-javascript-6.0.2.tgz">
+ <sha256 value="2f292f35c62f85997a1e1e21473406b512b440609cfd359d7a26995c1b431ae2" origin="Generated by Gradle"/>
+ </artifact>
+ </component>
+ <component group="smob" name="smob" version="1.5.0">
+ <artifact name="smob-1.5.0.tgz">
+ <sha256 value="85b6c338a99092849907520ee9e027c3710ee3dbbb94e240e5d3bec0a6f8d4aa" origin="Generated by Gradle"/>
+ </artifact>
+ </component>
+ <component group="source-map" name="source-map" version="0.6.1">
+ <artifact name="source-map-0.6.1.tgz">
+ <sha256 value="bdbca10d17ff5a5802d5acfc7b2f22f9f9bf587632a95650d3c5f513c7092b86" origin="Generated by Gradle"/>
+ </artifact>
+ </component>
+ <component group="source-map-support" name="source-map-support" version="0.5.21">
+ <artifact name="source-map-support-0.5.21.tgz">
+ <sha256 value="5d9b04ef3e6824fdcf91cfcc03ab427fae486bc6859735805593f51b3554f636" origin="Generated by Gradle"/>
+ </artifact>
+ </component>
+ <component group="terser" name="terser" version="5.39.0">
+ <artifact name="terser-5.39.0.tgz">
+ <sha256 value="ef669abcd38fc0a31dede049f1186ae4999783c34cfa6652a5e12b4e57cccf58" origin="Generated by Gradle"/>
+ </artifact>
+ </component>
<component group="webassembly" name="testsuite" version="18f8340">
<artifact name="testsuite-18f8340.zip">
<md5 value="d6ea2de35c54be9e8cfd74eebe475b62" origin="Generated by Gradle"/>
diff --git a/repo/gradle-settings-conventions/cache-redirector/src/main/kotlin/cache-redirector.settings.gradle.kts b/repo/gradle-settings-conventions/cache-redirector/src/main/kotlin/cache-redirector.settings.gradle.kts
index 448dbca2..138e59b 100644
--- a/repo/gradle-settings-conventions/cache-redirector/src/main/kotlin/cache-redirector.settings.gradle.kts
+++ b/repo/gradle-settings-conventions/cache-redirector/src/main/kotlin/cache-redirector.settings.gradle.kts
@@ -153,7 +153,8 @@
"https://jcenter.bintray.com" to "https://cache-redirector.jetbrains.com/jcenter.bintray.com",
"https://jcenter.bintray.com" to "https://cache-redirector.jetbrains.com/jcenter",
"https://www.python.org/ftp" to "https://cache-redirector.jetbrains.com/www.python.org/ftp",
- "https://registry.npmjs.org" to "https://cache-redirector.jetbrains.com/registry.npmjs.org",
+ // cache-redirector.jetbrains doesn't support HEAD requests :(
+ //"https://registry.npmjs.org" to "https://cache-redirector.jetbrains.com/registry.npmjs.org",
"https://maven.google.com" to "https://cache-redirector.jetbrains.com/maven.google.com",
"https://dl.google.com/go" to "https://cache-redirector.jetbrains.com/dl.google.com/go",
"https://dl.google.com/go" to "https://cache-redirector.jetbrains.com/dl.google.com.go",
diff --git a/wasm/wasm.debug.browsers/build.gradle.kts b/wasm/wasm.debug.browsers/build.gradle.kts
index fe00a52..00d8e68 100644
--- a/wasm/wasm.debug.browsers/build.gradle.kts
+++ b/wasm/wasm.debug.browsers/build.gradle.kts
@@ -1,10 +1,19 @@
+import com.github.gradle.node.NodeExtension
+import com.github.gradle.node.exec.NodeExecConfiguration
+import com.github.gradle.node.npm.exec.NpmExecRunner
import com.github.gradle.node.npm.task.NpxTask
+import com.github.gradle.node.util.DefaultProjectApiHelper
+import com.github.gradle.node.variant.VariantComputer
+import groovy.json.JsonSlurper
import org.gradle.api.tasks.PathSensitivity.RELATIVE
+import org.gradle.kotlin.dsl.support.serviceOf
+import java.io.ByteArrayOutputStream
+import java.io.Serializable
description = "Simple Kotlin/Wasm devtools formatters"
plugins {
- id("base")
+ base
id("share-kotlin-wasm-custom-formatters")
alias(libs.plugins.gradle.node)
}
@@ -39,6 +48,7 @@
group = "build"
dependsOn(tasks.npmInstall)
+ dependsOn(addNodeModulesToNpmCache)
val rollupConfigMjsFile = file("rollup.config.mjs")
inputs.file(rollupConfigMjsFile)
@@ -61,10 +71,10 @@
tasks {
npmInstall {
- val nodeModulesDir = projectDir.resolve("node_modules")
- outputs.upToDateWhen {
- nodeModulesDir.isDirectory
- }
+// val nodeModulesDir = projectDir.resolve("node_modules")
+// outputs.upToDateWhen {
+// nodeModulesDir.isDirectory
+// }
if (gradle.startParameter.isOffline) {
args.add("--offline")
@@ -83,3 +93,283 @@
artifact(npmBuild)
}
}
+
+
+/**
+ * Extract node_modules dependencies from the `package-lock.json`.
+ *
+ * Convert each to GAV dependency coordinates.
+ */
+val nodeModuleDependencies = try {
+ val packageLockFile = file("package-lock.json")
+ JsonSlurper().parse(packageLockFile)
+ .let { it as Map<*, *> }
+ .let { it["packages"] as Map<*, *> }
+ .let { packages ->
+ packages.keys
+ .map { dep -> dep as String }
+ .filter { dep -> dep.startsWith("node_modules/") }
+ .map { dep ->
+ val depData = packages[dep] as Map<*, *>
+ val version = depData["version"] as String
+
+ val module = dep.substringAfter("node_modules/").substringAfter("/")
+ val group = dep.substringAfter("node_modules/")
+ .replace("/", ".")
+ //.removePrefix("@")
+
+ //"npm.p.kt.kotlin-dependencies.$group:$module:$version@tgz"
+ "$group:$module:$version@tgz"
+ }
+ }
+} catch (e: Exception) {
+ System.err.println("Error parsing package-lock.json")
+ throw e
+}
+
+val nodeModulesDependencies by configurations.registering {
+ isCanBeDeclared = true
+ isCanBeResolved = false
+ isCanBeConsumed = false
+ isVisible = false
+ defaultDependencies {
+ nodeModuleDependencies.forEach {
+ add(project.dependencies.create(it))
+ }
+ }
+}
+
+val nodeModulesDependenciesResolver by configurations.registering {
+ isCanBeDeclared = false
+ isCanBeResolved = true
+ isCanBeConsumed = false
+ isVisible = false
+ extendsFrom(nodeModulesDependencies.get())
+ attributes {
+ attribute(Usage.USAGE_ATTRIBUTE, project.objects.named("npm-dependencies-cache"))
+ }
+}
+
+// use an object to avoid 'project script references' CC errors
+object NpmUtil {
+
+ /**
+ * Util for executing npm.
+ */
+ fun npmExecProvider(
+ nodeExtension: NodeExtension,
+ objects: ObjectFactory,
+ configure: NpmExecProviderSpec.() -> Unit,
+ ): Provider<NpmExecResult> {
+
+ val spec = objects.newInstance<NpmExecProviderSpec>()
+ .apply(configure)
+
+ return spec.providers.provider {
+ val variantComputer = VariantComputer()
+ val projectHelper = objects.newInstance<DefaultProjectApiHelper>()
+ val npmExecRunner = objects.newInstance<NpmExecRunner>()
+
+ val stdOut = ByteArrayOutputStream()
+ val stdErr = ByteArrayOutputStream()
+
+ val result = try {
+ val nodeExecConfiguration =
+ NodeExecConfiguration(
+ command = spec.command.get(),
+ environment = spec.environment.orNull.orEmpty(),
+ workingDir = nodeExtension.nodeProjectDir.get().asFile,
+ ignoreExitValue = spec.ignoreExitValue.orNull ?: false,
+ ) {
+ standardOutput = stdOut
+ errorOutput = stdErr
+ }
+
+ npmExecRunner.executeNpmCommand(
+ project = projectHelper,
+ extension = nodeExtension,
+ nodeExecConfiguration = nodeExecConfiguration,
+ variants = variantComputer,
+ )
+ } finally {
+ stdOut.close()
+ stdErr.close()
+ }
+
+ NpmExecResult(
+ exitValue = result.exitValue,
+ standardOutput = stdOut.toString(),
+ errorOutput = stdErr.toString(),
+ )
+ }
+ }
+
+ abstract class NpmExecProviderSpec @Inject constructor(
+ val providers: ProviderFactory,
+ ) {
+ abstract val command: ListProperty<String>
+ abstract val environment: MapProperty<String, String>
+ abstract val ignoreExitValue: Property<Boolean>
+
+ /** Set the value of the [command] property. */
+ fun command(vararg command: String) {
+ this.command.set(listOf(*command))
+ }
+ }
+
+ data class NpmExecResult(
+ val exitValue: Int,
+ val standardOutput: String,
+ val errorOutput: String,
+ ) : Serializable
+
+}
+
+val addNodeModulesToNpmCache by tasks.registering {
+ val objects = serviceOf<ObjectFactory>()
+
+ val nodeModulesDependenciesFiles = nodeModulesDependenciesResolver.map { it.incoming.files }
+ inputs.files(nodeModulesDependenciesFiles)
+
+ dependsOn(tasks.nodeSetup)
+
+ val nodeExtension = project.node
+
+ doLast {
+
+ /**
+ * Get all content of npm's cache, so we can avoid re-adding items to the cache (which is a little slow).
+ */
+ val cacheLsLines = run {
+ val npmCacheLsResult = NpmUtil.npmExecProvider(nodeExtension, objects) {
+ command("cache", "ls")
+ }.get()
+
+ logger.info("npm cache ls result :\n${npmCacheLsResult.toString().lines().joinToString("\\n ")}")
+
+ npmCacheLsResult.standardOutput.lineSequence()
+ }
+
+ nodeModulesDependenciesFiles.get()
+ .filter { dep ->
+ // filter out dependencies that are already present in npm's cache
+ cacheLsLines.none { it.endsWith(dep.invariantSeparatorsPath) }
+ }
+ .forEach { dep ->
+ logger.lifecycle("Adding ${dep.name} to npm cache...")
+
+ val result = NpmUtil.npmExecProvider(nodeExtension, objects) {
+ command("cache", "add", dep.invariantSeparatorsPath)
+ }.get()
+
+ logger.info("npm cache add result: ${result.toString().lines().joinToString("\\n ")}")
+ }
+ }
+}
+
+// region EXPERIMENT - run `npm install --dry-run --offline` as an up-to-date check for npmInstall
+// https://github.com/node-gradle/gradle-node-plugin/issues/81
+
+
+// abstract class NpmExecSource @Inject internal constructor(
+// private val execOps: ExecOperations,
+// ) : ValueSource<NpmExecResult, NpmExecSource.Parameters> {
+//
+// interface Parameters : ValueSourceParameters {
+// // val nodeExtension: Property<NodeExtension>
+// val environment: MapProperty<String, String>
+// val ignoreExitValue: Property<Boolean>
+// val command: ListProperty<String>
+// val
+//// val workingDir: DirectoryProperty
+// }
+//
+// override fun obtain(): NpmExecResult {
+// val variantComputer = VariantComputer()
+// val projectHelper = objects.newInstance(DefaultProjectApiHelper::class)
+// val npmExecRunner = objects.newInstance(NpmExecRunner::class)
+//
+// val stdOut = ByteArrayOutputStream()
+// val stdErr = ByteArrayOutputStream()
+//
+// val nodeExtension = NodeExtension(ProjectBuilder.builder().build())
+//
+// val result = try {
+// val nodeExecConfiguration =
+// NodeExecConfiguration(
+// command = parameters.command.get(),
+// environment = parameters.environment.orNull.orEmpty(),
+//// workingDir = parameters.nodeExtension.get().nodeProjectDir.get().asFile,
+// workingDir = nodeExtension.nodeProjectDir.get().asFile,
+// ignoreExitValue = parameters.ignoreExitValue.orNull ?: false,
+// ) {
+// standardOutput = stdOut
+// errorOutput = stdErr
+// }
+//
+// npmExecRunner.executeNpmCommand(
+// project = projectHelper,
+//// extension = parameters.nodeExtension.get(),
+// extension = nodeExtension ,
+// nodeExecConfiguration = nodeExecConfiguration,
+// variants = variantComputer,
+// )
+// } finally {
+// stdOut.close()
+// stdErr.close()
+// }
+//
+// return NpmExecResult(
+// result.exitValue,
+// standardOutput = stdOut.toString(),
+// errorOutput = stdErr.toString(),
+// )
+// }
+// }
+
+//val npmInstallUpToDateCheck by tasks.registering {
+// val nodeExtension = project.node
+// val objects = serviceOf<ObjectFactory>()
+// val npmInstallOfflineExecResult = NpmUtil.npmExecProvider(nodeExtension, objects) {
+// command = listOf("install", "--dry-run", "--offline")
+// }
+//
+// val result = temporaryDir.resolve("result.txt")
+// outputs.file(result)
+//
+// doLast {
+// println("[$path] checking if npm install is up to date...")
+// val isUpToDate: Boolean =
+// npmInstallOfflineExecResult.get().run {
+// println(npmInstallOfflineExecResult)
+// errorOutput.isEmpty()
+// && standardOutput.lines().none { it.startsWith("add") }
+// }
+// println("[$path] result : $isUpToDate")
+// result.apply {
+// parentFile.mkdirs()
+// createNewFile()
+// writeText(isUpToDate.toString())
+// }
+// }
+//}
+////
+//tasks.npmInstall {
+//// val nodeExtension = project.node
+//// val objects = serviceOf<ObjectFactory>()
+// val npmInstallOfflineExecResult = nodeExtension.npmExec {
+// command = listOf("install", "--dry-run", "--offline")
+// }
+//
+//// val upToDateCheckResult = npmInstallUpToDateCheck.map { it.outputs.files.singleOrNull()?.readText()?.toBoolean() ?: false }
+//
+// outputs.upToDateWhen {
+// "missing dependencies" in npmInstallOfflineExecResult.get().standardOutput
+//// npmInstallOfflineExecResult.get().run {
+//// println(npmInstallOfflineExecResult)
+//// errorOutput.isEmpty()
+//// && standardOutput.lines().none { it.startsWith("add") }
+//// }
+// }
+//}
+//endregion