Skip to content

Commit 946ba86

Browse files
committed
Add fastNpmInstall which output only tracks lock-files
1 parent 642f304 commit 946ba86

File tree

3 files changed

+117
-10
lines changed

3 files changed

+117
-10
lines changed

src/main/kotlin/com/github/gradle/node/NodeExtension.kt

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,31 @@ open class NodeExtension(project: Project) {
108108
*/
109109
val nodeProxySettings = project.objects.property<ProxySettings>().convention(ProxySettings.SMART)
110110

111+
/**
112+
* Use fast NpmInstall logic, excluding node_modules for output tracking resulting in a significantly faster
113+
* npm install/ci configuration at the cost of slightly decreased correctness in certain circumstances.
114+
*
115+
* In practice this means that if you change node_modules through other means than npm install/ci
116+
* NpmInstall tasks will continue being up-to-date, but if you're modifying node_modules through
117+
* other tools you may have other correctness problems and surfacing them here may be preferred.
118+
*
119+
* https://docs.npmjs.com/cli/v8/configuring-npm/package-lock-json#hidden-lockfiles
120+
*
121+
* Requires npm 7 or later
122+
* This will become the default in 4.x
123+
*/
124+
val fastNpmInstall = project.objects.property<Boolean>().convention(false)
125+
126+
/**
127+
* Disable functionality that requires newer versions of npm
128+
*
129+
* If you're not downloading Node.js and using old version of Node or npm
130+
* set this to true to disable functionality that makes use of newer functionality.
131+
*
132+
* This will be removed in 4.x
133+
*/
134+
val oldNpm = project.objects.property<Boolean>().convention(false)
135+
111136
@Suppress("unused")
112137
@Deprecated("Deprecated in version 3.0, please use nodeProjectDir now")
113138
val nodeModulesDir = nodeProjectDir

src/main/kotlin/com/github/gradle/node/npm/task/NpmInstallTask.kt

Lines changed: 47 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,15 @@ abstract class NpmInstallTask : NpmTask() {
2020
val nodeModulesOutputFilter =
2121
objects.property<Action<ConfigurableFileTree>>()
2222

23+
@get:Internal
24+
val fastInstall = objects.property<Boolean>()
25+
2326
init {
2427
group = NodePlugin.NPM_GROUP
2528
description = "Install node packages from package.json."
2629
dependsOn(NpmSetupTask.NAME)
2730
npmCommand.set(nodeExtension.npmInstallCommand.map { listOf(it) })
31+
fastInstall.set(nodeExtension.fastNpmInstall)
2832
}
2933

3034
@PathSensitive(RELATIVE)
@@ -75,23 +79,56 @@ abstract class NpmInstallTask : NpmTask() {
7579
@Suppress("unused")
7680
protected fun getNodeModulesDirectory(): Provider<Directory> {
7781
val filter = nodeModulesOutputFilter.orNull
78-
return if (filter == null) nodeExtension.nodeProjectDir.dir("node_modules")
82+
return if (filter == null && !fastInstall.get()) nodeExtension.nodeProjectDir.dir("node_modules")
7983
else providers.provider { null }
8084
}
8185

8286
@Optional
8387
@OutputFiles
8488
@Suppress("unused")
8589
protected fun getNodeModulesFiles(): Provider<FileTree> {
86-
val nodeModulesDirectoryProvider = nodeExtension.nodeProjectDir.dir("node_modules")
87-
return zip(nodeModulesDirectoryProvider, nodeModulesOutputFilter)
88-
.flatMap { (nodeModulesDirectory, nodeModulesOutputFilter) ->
89-
if (nodeModulesOutputFilter != null) {
90-
val fileTree = projectHelper.fileTree(nodeModulesDirectory)
91-
nodeModulesOutputFilter.execute(fileTree)
92-
providers.provider { fileTree }
93-
} else providers.provider { null }
94-
}
90+
return if (fastInstall.get()) {
91+
providers.provider { null }
92+
} else {
93+
val nodeModulesDirectoryProvider = nodeExtension.nodeProjectDir.dir("node_modules")
94+
zip(nodeModulesDirectoryProvider, nodeModulesOutputFilter)
95+
.flatMap { (nodeModulesDirectory, nodeModulesOutputFilter) ->
96+
if (nodeModulesOutputFilter != null) {
97+
val fileTree = projectHelper.fileTree(nodeModulesDirectory)
98+
nodeModulesOutputFilter.execute(fileTree)
99+
providers.provider { fileTree }
100+
} else providers.provider { null }
101+
}
102+
}
103+
}
104+
105+
@Optional
106+
@OutputFile
107+
protected fun getNodeModulesPackageLock(): Provider<File> {
108+
if (isLegacyNpm()) {
109+
return providers.provider { null }
110+
}
111+
112+
return projectFileIfExists("node_modules/.package-lock.json")
113+
}
114+
115+
/**
116+
* Is our npm likely to be lower than 7?
117+
*/
118+
private fun isLegacyNpm(): Boolean {
119+
if (nodeExtension.oldNpm.get()) {
120+
return true
121+
}
122+
123+
val npmVersion = nodeExtension.npmVersion.get()
124+
if (npmVersion.isBlank()) {
125+
if (nodeExtension.version.get().split('.').first().toInt() <= 14)
126+
return true
127+
} else if (npmVersion.split('.').first().toInt() < 7 ) {
128+
return true
129+
}
130+
131+
return false
95132
}
96133

97134
// For DSL

src/test/groovy/com/github/gradle/node/npm/task/NpmInstall_integTest.groovy

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,51 @@ class NpmInstall_integTest extends AbstractIntegTest {
4949
gv << GRADLE_VERSIONS_UNDER_TEST
5050
}
5151
52+
def 'install packages with fast npm install (#gv.version)'() {
53+
given:
54+
gradleVersion = gv
55+
56+
writeBuild('''
57+
plugins {
58+
id 'com.github.node-gradle.node'
59+
}
60+
61+
node {
62+
fastNpmInstall = true
63+
}
64+
''')
65+
writePackageJson '''
66+
{
67+
"name": "fastinstall",
68+
"dependencies": {
69+
"@isaacs/string-locale-compare": "1.1.0"
70+
}
71+
}
72+
'''
73+
74+
when:
75+
def result = build('npmInstall')
76+
77+
then:
78+
result.task(":npmInstall").outcome == TaskOutcome.SUCCESS
79+
80+
when:
81+
result = build('npmInstall')
82+
83+
then:
84+
// because of package-lock.json that was generated during the previous npm install execution
85+
result.task(":npmInstall").outcome == TaskOutcome.SUCCESS
86+
87+
when:
88+
result = build('npmInstall')
89+
90+
then:
91+
result.task(":npmInstall").outcome == TaskOutcome.UP_TO_DATE
92+
93+
where:
94+
gv << GRADLE_VERSIONS_UNDER_TEST
95+
}
96+
5297
def 'install packages with npm >= 7 (#gv.version)'() {
5398
given:
5499
gradleVersion = gv

0 commit comments

Comments
 (0)