From 8dd72a10ac7df4ebea211827ec93e4c6ed138142 Mon Sep 17 00:00:00 2001 From: Domantas Petrauskas Date: Thu, 5 May 2022 19:59:53 +0300 Subject: [PATCH] Improve package manager abstraction --- .../scalajsbundler/ExternalCommand.scala | 130 ++++++++++-------- .../scala/scalajsbundler/PackageJson.scala | 5 +- .../sbtplugin/NpmUpdateTasks.scala | 15 +- .../sbtplugin/PackageJsonTasks.scala | 8 +- .../sbtplugin/ScalaJSBundlerPlugin.scala | 15 +- 5 files changed, 94 insertions(+), 79 deletions(-) diff --git a/sbt-scalajs-bundler/src/main/scala/scalajsbundler/ExternalCommand.scala b/sbt-scalajs-bundler/src/main/scala/scalajsbundler/ExternalCommand.scala index e794e84b..6abb86ce 100644 --- a/sbt-scalajs-bundler/src/main/scala/scalajsbundler/ExternalCommand.scala +++ b/sbt-scalajs-bundler/src/main/scala/scalajsbundler/ExternalCommand.scala @@ -4,13 +4,14 @@ import java.io.File import sbt._ import scalajsbundler.util.Commands +import scalajsbundler.util.JSON /** * Attempts to smoothen platform-specific differences when invoking commands. * * @param name Name of the command to run */ -abstract class PackageManager(name: String, args: Seq[String]) { +abstract class PackageManager(val name: String, val args: Seq[String], val installCommand: String) { /** * Runs the command `cmd` @@ -26,6 +27,61 @@ abstract class PackageManager(name: String, args: Seq[String]) { case _ => Seq(name) } + def install(baseDir: File, + installDir: File, + logger: Logger): Unit = { + this match { + case lfs: LockFileSupport => + lfs.lockFileRead(baseDir, installDir, logger) + case _ => + () + } + + run(installCommand +: args: _*)(installDir, logger) + + this match { + case lfs: LockFileSupport => + lfs.lockFileWrite(baseDir, installDir, logger) + case _ => + () + } + } + + val packageJsonContents: Map[String, JSON] +} + +trait AddPackagesSupport { this: PackageManager => + + val addPackagesCommand: String + + /** + * Locally install NPM packages + * + * @param baseDir The (sub-)project directory which contains yarn.lock + * @param installDir The directory in which to install the packages + * @param logger sbt logger + * @param npmPackages Packages to install (e.g. "webpack", "webpack@2.2.1") + */ + def addPackages(baseDir: File, + installDir: File, + logger: Logger, + )(npmPackages: String*): Unit = { + this match { + case lfs: LockFileSupport => + lfs.lockFileRead(baseDir, installDir, logger) + case _ => + () + } + + run(addPackagesCommand +: (args ++ npmPackages): _*)(installDir, logger) + + this match { + case lfs: LockFileSupport => + lfs.lockFileWrite(baseDir, installDir, logger) + case _ => + () + } + } } trait LockFileSupport { @@ -61,67 +117,23 @@ trait LockFileSupport { } case class Npm( - args: Seq[String] = Seq.empty, - lockFileName: String = "package-lock.json" -) extends PackageManager("npm", args) + lockFileName: String = "package-lock.json", + override val args: Seq[String] = Seq.empty, + addPackagesCommand: String = "install" +) extends PackageManager("npm", args, "install") with LockFileSupport + with AddPackagesSupport { + override val packageJsonContents: Map[String, JSON] = Map.empty +} case class Yarn( - args: Seq[String] = Yarn.DefaultArgs, - lockFileName: String = "yarn.lock" -) extends PackageManager("yarn", args) - with LockFileSupport + version: String, + lockFileName: String = "yarn.lock", + override val args: Seq[String] = Yarn.DefaultArgs, + addPackagesCommand: String = "add") extends PackageManager("yarn", args, "install") + with LockFileSupport with AddPackagesSupport { + override val packageJsonContents: Map[String, JSON] = Map("packageManager" -> JSON.str(s"yarn@$version")) +} object Yarn { val DefaultArgs: Seq[String] = Seq("--non-interactive", "--mutex", "network") } - -object PackageManager { - - /** - * Locally install NPM packages - * - * @param baseDir The (sub-)project directory which contains yarn.lock - * @param installDir The directory in which to install the packages - * @param useYarn Whether to use yarn or npm - * @param logger sbt logger - * @param npmExtraArgs Additional arguments to pass to npm - * @param npmPackages Packages to install (e.g. "webpack", "webpack@2.2.1") - */ - def addPackages(baseDir: File, - installDir: File, - logger: Logger, - packageManager: PackageManager - )(npmPackages: String*): Unit = { - val maybeLockFileWrapper = packageManager match { - case lfs: LockFileSupport => () => - case _ => - } - if (useYarn) { - syncYarnLockfile(baseDir, installDir, logger) { - Yarn.run("add" +: (yarnOptions ++ yarnExtraArgs ++ npmPackages): _*)( - installDir, - logger) - } - } else { - syncNpmLockfile(baseDir, installDir, logger) { - Npm.run("install" +: (npmPackages ++ npmExtraArgs): _*)(installDir, logger) - } - } - } - - def install(baseDir: File, - installDir: File, - logger: Logger, - npmExtraArgs: Seq[String], - yarnExtraArgs: Seq[String]): Unit = - if (useYarn) { - syncYarnLockfile(baseDir, installDir, logger) { - Yarn.run("install" +: (yarnOptions ++ yarnExtraArgs): _*)(installDir, - logger) - } - } else { - syncNpmLockfile(baseDir, installDir, logger) { - Npm.run("install" +: npmExtraArgs: _*)(installDir, logger) - } - } -} diff --git a/sbt-scalajs-bundler/src/main/scala/scalajsbundler/PackageJson.scala b/sbt-scalajs-bundler/src/main/scala/scalajsbundler/PackageJson.scala index d8b34ab9..4f9fa07f 100644 --- a/sbt-scalajs-bundler/src/main/scala/scalajsbundler/PackageJson.scala +++ b/sbt-scalajs-bundler/src/main/scala/scalajsbundler/PackageJson.scala @@ -31,7 +31,8 @@ object PackageJson { currentConfiguration: Configuration, webpackVersion: String, webpackDevServerVersion: String, - webpackCliVersion: String + webpackCliVersion: String, + packageManager: PackageManager ): Unit = { val npmManifestDependencies = NpmDependencies.collectFromClasspath(fullClasspath) val dependencies = @@ -62,7 +63,7 @@ object PackageJson { val packageJson = JSON.obj( ( - additionalNpmConfig.toSeq :+ + (additionalNpmConfig.toSeq ++ packageManager.packageJsonContents.toSeq) :+ "dependencies" -> JSON.objStr(resolveDependencies(dependencies, npmResolutions, log)) :+ "devDependencies" -> JSON.objStr(resolveDependencies(devDependencies, npmResolutions, log)) ): _* diff --git a/sbt-scalajs-bundler/src/main/scala/scalajsbundler/sbtplugin/NpmUpdateTasks.scala b/sbt-scalajs-bundler/src/main/scala/scalajsbundler/sbtplugin/NpmUpdateTasks.scala index cb4d5612..87eba104 100644 --- a/sbt-scalajs-bundler/src/main/scala/scalajsbundler/sbtplugin/NpmUpdateTasks.scala +++ b/sbt-scalajs-bundler/src/main/scala/scalajsbundler/sbtplugin/NpmUpdateTasks.scala @@ -1,8 +1,9 @@ package scalajsbundler.sbtplugin import java.nio.file.Path -import scalajsbundler.ExternalCommand + import sbt._ +import scalajsbundler.PackageManager object NpmUpdateTasks { @@ -21,12 +22,10 @@ object NpmUpdateTasks { def npmUpdate(baseDir: File, targetDir: File, packageJsonFile: File, - useYarn: Boolean, jsResources: Seq[(String, Path)], streams: Keys.TaskStreams, - npmExtraArgs: Seq[String], - yarnExtraArgs: Seq[String]): File = { - val dir = npmInstallDependencies(baseDir, targetDir, packageJsonFile, useYarn, streams, npmExtraArgs, yarnExtraArgs) + packageManager: PackageManager): File = { + val dir = npmInstallDependencies(baseDir, targetDir, packageJsonFile, streams, packageManager) npmInstallJSResources(targetDir, jsResources, Seq.empty, streams) dir } @@ -45,10 +44,8 @@ object NpmUpdateTasks { def npmInstallDependencies(baseDir: File, targetDir: File, packageJsonFile: File, - useYarn: Boolean, streams: Keys.TaskStreams, - npmExtraArgs: Seq[String], - yarnExtraArgs: Seq[String]): File = { + packageManager: PackageManager): File = { val log = streams.log val cachedActionFunction = FileFunction.cached( @@ -56,7 +53,7 @@ object NpmUpdateTasks { inStyle = FilesInfo.hash ) { _ => log.info("Updating NPM dependencies") - ExternalCommand.install(baseDir, targetDir, useYarn, log, npmExtraArgs, yarnExtraArgs) + packageManager.install(baseDir, targetDir, log) Set.empty } cachedActionFunction(Set(packageJsonFile)) diff --git a/sbt-scalajs-bundler/src/main/scala/scalajsbundler/sbtplugin/PackageJsonTasks.scala b/sbt-scalajs-bundler/src/main/scala/scalajsbundler/sbtplugin/PackageJsonTasks.scala index e9754efd..d613833a 100644 --- a/sbt-scalajs-bundler/src/main/scala/scalajsbundler/sbtplugin/PackageJsonTasks.scala +++ b/sbt-scalajs-bundler/src/main/scala/scalajsbundler/sbtplugin/PackageJsonTasks.scala @@ -1,7 +1,7 @@ package scalajsbundler.sbtplugin import sbt._ - +import scalajsbundler.PackageManager import scalajsbundler.{BundlerFile, PackageJson} import scalajsbundler.util.{Caching, JSON} @@ -31,7 +31,8 @@ object PackageJsonTasks { webpackVersion: String, webpackDevServerVersion: String, webpackCliVersion: String, - streams: Keys.TaskStreams + streams: Keys.TaskStreams, + packageManager: PackageManager ): BundlerFile.PackageJson = { val hash = Seq( @@ -63,7 +64,8 @@ object PackageJsonTasks { configuration, webpackVersion, webpackDevServerVersion, - webpackCliVersion + webpackCliVersion, + packageManager ) () } diff --git a/sbt-scalajs-bundler/src/main/scala/scalajsbundler/sbtplugin/ScalaJSBundlerPlugin.scala b/sbt-scalajs-bundler/src/main/scala/scalajsbundler/sbtplugin/ScalaJSBundlerPlugin.scala index ea3c5761..de8c306f 100644 --- a/sbt-scalajs-bundler/src/main/scala/scalajsbundler/sbtplugin/ScalaJSBundlerPlugin.scala +++ b/sbt-scalajs-bundler/src/main/scala/scalajsbundler/sbtplugin/ScalaJSBundlerPlugin.scala @@ -4,10 +4,12 @@ import org.scalajs.sbtplugin.ScalaJSPlugin.autoImport._ import org.scalajs.sbtplugin.{ScalaJSPlugin, Stage} import sbt.Keys._ import sbt.{Def, _} +import scalajsbundler.AddPackagesSupport import scalajsbundler.{BundlerFile, NpmDependencies, Webpack, WebpackDevServer} import scalajsbundler.ExternalCommand.addPackages import scalajsbundler.Npm import scalajsbundler.PackageManager +import scalajsbundler.Yarn import scalajsbundler.util.{JSON, ScalaJSNativeLibraries} @@ -578,7 +580,10 @@ object ScalaJSBundlerPlugin extends AutoPlugin { if (!jsdomDir.exists()) { log.info(s"Installing jsdom in ${installDir.absolutePath}") IO.createDirectory(installDir) - addPackages(baseDir, installDir, useYarn.value, log, npmExtraArgs.value, yarnExtraArgs.value)(s"jsdom@$jsdomVersion") + packageManager.value match { + case aps: AddPackagesSupport => + aps.addPackages(baseDir, installDir, log)(s"jsdom@$jsdomVersion") + } } installDir } @@ -610,10 +615,7 @@ object ScalaJSBundlerPlugin extends AutoPlugin { baseDirectory.value, (crossTarget in npmUpdate).value, scalaJSBundlerPackageJson.value.file, - useYarn.value, - streams.value, - npmExtraArgs.value, - yarnExtraArgs.value), + streams.value, packageManager.value), npmInstallJSResources := NpmUpdateTasks.npmInstallJSResources( (crossTarget in npmUpdate).value, @@ -633,7 +635,8 @@ object ScalaJSBundlerPlugin extends AutoPlugin { (version in webpack).value, (version in startWebpackDevServer).value, webpackCliVersion.value, - streams.value + streams.value, + packageManager.value ),