@@ -89,6 +89,39 @@ private enum class YarnDependencyType(val type: String) {
89
89
DEV_DEPENDENCIES (" devDependencies" )
90
90
}
91
91
92
+ internal class Yarn2Command (private val enableCorepack : Boolean? ) : CommandLineTool {
93
+ @Suppress(" Unused" ) // The no-arg constructor is required by the requirements command.
94
+ constructor () : this (null )
95
+
96
+ /* *
97
+ * The Yarn 2+ executable is not installed globally: The program shipped by the project in `.yarn/releases` is used
98
+ * instead. The value of the 'yarnPath' property in the resource file `.yarnrc.yml` defines the path to the
99
+ * executable for the current project e.g. `yarnPath: .yarn/releases/yarn-3.2.1.cjs`.
100
+ * This map holds the mapping between the directory and their Yarn 2+ executables. It is only used if Yarn has not
101
+ * been installed via Corepack; then it is accessed under a default name.
102
+ */
103
+ private val yarn2ExecutablesByPath: MutableMap <File , File > = mutableMapOf ()
104
+
105
+ override fun command (workingDir : File ? ): String {
106
+ if (workingDir == null ) return " "
107
+ if (isCorepackEnabled(workingDir)) return " yarn"
108
+
109
+ val executablePath = yarn2ExecutablesByPath.getOrPut(workingDir) { getYarnExecutable(workingDir) }.absolutePath
110
+ return executablePath.takeUnless { Os .isWindows } ? : " node $executablePath "
111
+ }
112
+
113
+ override fun getVersion (workingDir : File ? ): String =
114
+ // `getVersion` with a `null` parameter is called once by the Analyzer to get the version of the global tool.
115
+ // For Yarn2+, the version is specific to each definition file being scanned therefore a global version doesn't
116
+ // apply.
117
+ // TODO: An alternative would be to collate the versions of all tools in `yarn2CommandsByPath`.
118
+ if (workingDir == null ) " " else super .getVersion(workingDir)
119
+
120
+ override fun getVersionRequirement (): RangesList = RangesListFactory .create(" >=2.0.0" )
121
+
122
+ private fun isCorepackEnabled (workingDir : File ): Boolean = enableCorepack ? : isCorepackEnabledInManifest(workingDir)
123
+ }
124
+
92
125
/* *
93
126
* The [Yarn 2+](https://v2.yarnpkg.com/) package manager for JavaScript.
94
127
*
@@ -102,7 +135,7 @@ private enum class YarnDependencyType(val type: String) {
102
135
* force a specific behavior in some environments.
103
136
*/
104
137
class Yarn2 (name : String , analyzerConfig : AnalyzerConfiguration ) :
105
- NodePackageManager (name, NodePackageManagerType .YARN2 , analyzerConfig), CommandLineTool {
138
+ NodePackageManager (name, NodePackageManagerType .YARN2 , analyzerConfig) {
106
139
companion object {
107
140
/* *
108
141
* The name of the option to disable HTTPS server certificate verification.
@@ -121,15 +154,6 @@ class Yarn2(name: String, analyzerConfig: AnalyzerConfiguration) :
121
154
122
155
override val globsForDefinitionFiles = listOf (NodePackageManagerType .DEFINITION_FILE )
123
156
124
- /* *
125
- * The Yarn 2+ executable is not installed globally: The program shipped by the project in `.yarn/releases` is used
126
- * instead. The value of the 'yarnPath' property in the resource file `.yarnrc.yml` defines the path to the
127
- * executable for the current project e.g. `yarnPath: .yarn/releases/yarn-3.2.1.cjs`.
128
- * This map holds the mapping between the directory and their Yarn 2+ executables. It is only used if Yarn has not
129
- * been installed via Corepack; then it is accessed under a default name.
130
- */
131
- private val yarn2ExecutablesByPath: MutableMap <File , File > = mutableMapOf ()
132
-
133
157
private val disableRegistryCertificateVerification =
134
158
options[OPTION_DISABLE_REGISTRY_CERTIFICATE_VERIFICATION ].toBoolean()
135
159
@@ -139,38 +163,16 @@ class Yarn2(name: String, analyzerConfig: AnalyzerConfiguration) :
139
163
// All the projects parsed by this package manager, mapped by their ids.
140
164
private val allProjects = mutableMapOf<Identifier , Project >()
141
165
166
+ internal val yarn2Command = Yarn2Command (options[OPTION_COREPACK_OVERRIDE ].toBoolean())
167
+
142
168
// The issues that have been found when resolving the dependencies.
143
169
private val issues = mutableListOf<Issue >()
144
170
145
171
// A builder to build the dependency graph of the project.
146
172
override val graphBuilder = DependencyGraphBuilder (Yarn2DependencyHandler ())
147
173
148
- override fun command (workingDir : File ? ): String {
149
- if (workingDir == null ) return " "
150
- if (isCorepackEnabled(workingDir)) return " yarn"
151
-
152
- val executablePath = yarn2ExecutablesByPath.getOrPut(workingDir) { getYarnExecutable(workingDir) }.absolutePath
153
- return executablePath.takeUnless { Os .isWindows } ? : " node $executablePath "
154
- }
155
-
156
- override fun getVersion (workingDir : File ? ): String =
157
- // `getVersion` with a `null` parameter is called once by the Analyzer to get the version of the global tool.
158
- // For Yarn2+, the version is specific to each definition file being scanned therefore a global version doesn't
159
- // apply.
160
- // TODO: An alternative would be to collate the versions of all tools in `yarn2CommandsByPath`.
161
- if (workingDir == null ) " " else super .getVersion(workingDir)
162
-
163
- override fun getVersionRequirement (): RangesList = RangesListFactory .create(" >=2.0.0" )
164
-
165
- private fun isCorepackEnabled (workingDir : File ): Boolean =
166
- if (OPTION_COREPACK_OVERRIDE in options) {
167
- options[OPTION_COREPACK_OVERRIDE ].toBoolean()
168
- } else {
169
- isCorepackEnabledInManifest(workingDir)
170
- }
171
-
172
174
override fun beforeResolution (analysisRoot : File , definitionFiles : List <File >) =
173
- definitionFiles.forEach { checkVersion(it.parentFile) }
175
+ definitionFiles.forEach { yarn2Command. checkVersion(it.parentFile) }
174
176
175
177
override fun resolveDependencies (
176
178
analysisRoot : File ,
@@ -197,11 +199,11 @@ class Yarn2(name: String, analyzerConfig: AnalyzerConfiguration) :
197
199
}
198
200
199
201
private fun installDependencies (workingDir : File ) {
200
- run (" install" , workingDir = workingDir).requireSuccess()
202
+ yarn2Command. run (" install" , workingDir = workingDir).requireSuccess()
201
203
}
202
204
203
205
private fun getPackageInfos (workingDir : File ): List <PackageInfo > {
204
- val process = run (
206
+ val process = yarn2Command. run (
205
207
" info" ,
206
208
" -A" ,
207
209
" -R" ,
@@ -256,7 +258,7 @@ class Yarn2(name: String, analyzerConfig: AnalyzerConfiguration) :
256
258
async {
257
259
logger.info { " Fetching packages details chunk #$index ." }
258
260
259
- val process = run (
261
+ val process = yarn2Command. run (
260
262
" npm" ,
261
263
" info" ,
262
264
" --json" ,
0 commit comments