Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .scalafmt.conf
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
version = "3.5.9"
version = "3.8.3"
runner.dialect = scala3
fileOverride {
"glob:**/scaladex/src/**" {
Expand Down
6 changes: 3 additions & 3 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@ enablePlugins(NpmPackagePlugin)
name := "dexsearch"
version := "0.1.5"

scalaVersion := "3.2.1" // or any other Scala version >= 2.11.12
scalaVersion := "3.3.4" // or any other Scala version >= 2.11.12

libraryDependencies ++= Seq(
"com.monovore" %%% "decline-effect" % "2.4.1"
"com.monovore" %%% "decline-effect" % "2.4.1",
"tech.neander" %%% "cue4s-cats-effect" % "0.0.6"
)

// This is an application with a main method
Expand All @@ -27,7 +28,6 @@ npmPackageBinaryEnable := true
npmPackageDependencies ++= {
Seq(
"node-fetch" -> "^2.6.1",
"prompts" -> "^2.4.2",
"clipboardy" -> "^3.0.0"
)
}
2 changes: 1 addition & 1 deletion project/build.properties
Original file line number Diff line number Diff line change
@@ -1 +1 @@
sbt.version=1.8.2
sbt.version=1.10.5
2 changes: 1 addition & 1 deletion project/plugins.sbt
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
// addSbtPlugin("org.scalablytyped.converter" % "sbt-converter" % "1.0.0-beta40")
addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.12.0")
addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.17.0")
addSbtPlugin("io.chrisdavenport" % "sbt-npm-package" % "0.1.2")
60 changes: 36 additions & 24 deletions src/main/scala/dex/main.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,6 @@ package dex
import scala.scalajs.js
import scala.scalajs.js.annotation.JSImport

// @main
def main() = println(util.inspect(prompts.default))

import scala.scalajs.js
import scala.scalajs.js.JSConverters._
import scala.util.Success
Expand All @@ -16,6 +13,8 @@ import cats.syntax.all._
import com.monovore.decline.effect.CommandIOApp
import cats.effect.IO
import cats.effect.ExitCode
import cue4s.catseffect.PromptsIO
import cue4s.CompletionError

object Main
extends CommandIOApp("dex", "searches scaladex for Scala libraries"):
Expand All @@ -24,18 +23,31 @@ object Main

def main = (BuildTool.opt, LibName.argument).mapN {
(maybeBuildTool, maybeLibName) =>
for
libName <- maybeLibName.map(IO.pure(_)).getOrElse(promptLibName)
projects <- scaladex.search(libName.value)
project <- promptProject(projects)
modules <- promptModules(project)
details <- scaladex.project(project.organization, project.repository)
version <- promptVersion(details)
buildTool <- maybeBuildTool.map(IO.pure(_)).getOrElse(promptBuildTool)
content = format(buildTool, details.groupId, modules, version)
_ <- copyToClipboard(content)
_ <- IO.println(doneMessage)
yield ExitCode(0)
PromptsIO.make
.use: prompts =>
for
libName <- maybeLibName
.map(IO.pure(_))
.getOrElse(promptLibName(prompts))
projects <- scaladex.search(libName.value)
project <- promptProject(prompts, projects)
modules <- promptModules(prompts, project)
details <- scaladex.project(
project.organization,
project.repository
)
version <- promptVersion(prompts, details)
buildTool <- maybeBuildTool
.map(IO.pure(_))
.getOrElse(promptBuildTool(prompts))
content = format(buildTool, details.groupId, modules, version)
_ <- copyToClipboard(content)
_ <- IO.println(doneMessage)
yield ExitCode(0)
.recoverWith:
case CompletionError.Interrupted =>
IO.println("cancelled").as(ExitCode.Error)
case CompletionError.Error(msg) => IO.println(msg).as(ExitCode.Error)
}
end main

Expand All @@ -45,14 +57,14 @@ object Main
modules: js.Array[String],
version: String
): List[String] = modules.toList.map { case module =>
buildTool match
case BuildTool.SBT => s""""$groupId" %% "$module" % "$version""""
case BuildTool.Mill => s"""ivy"$groupId::$module:$version""""
case BuildTool.Bleep => s"""$groupId::$module:$version"""
case BuildTool.ScalaCLI =>
s"""//> using lib "$groupId::$module:$version""""
case BuildTool.Ammonite =>
s"""import $$ivy.`$groupId::$module:$version`"""
}
buildTool match
case BuildTool.SBT => s""""$groupId" %% "$module" % "$version""""
case BuildTool.Mill => s"""ivy"$groupId::$module:$version""""
case BuildTool.Bleep => s"""$groupId::$module:$version"""
case BuildTool.ScalaCLI =>
s"""//> using lib "$groupId::$module:$version""""
case BuildTool.Ammonite =>
s"""import $$ivy.`$groupId::$module:$version`"""
}

end Main
139 changes: 46 additions & 93 deletions src/main/scala/dex/prompts.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,104 +6,57 @@ import scala.scalajs.js.Promise
import scala.scalajs.js.Thenable
import cats.effect.IO
import scala.scalajs.js.JSConverters._

@js.native
@JSImport("prompts", JSImport.Namespace)
object prompts extends js.Any:
def default[T](x: js.Object): Promise[T] = js.native

def ioPrompt[T](x: js.Object): IO[T] =
IO.fromPromise(IO(prompts.default(x)))

def promptLibName: IO[LibName] =
val libNameQuestion = new js.Object {
val `type` = "text"
val name = "libName"
val message = "Which library ?"
}

trait LibNameAnswer extends js.Object {
val libName: String
}

ioPrompt[LibNameAnswer](libNameQuestion).map(_.libName).map(LibName(_))
end promptLibName

def promptProject(project: js.Array[Project]): IO[Project] =
val projectQuestion = new js.Object {
val `type` = "autocomplete"
val name = "project"
val message = "From which repo ?"
val choices = project.map { p =>
new js.Object {
val title = s"${p.repository} from ${p.organization}"
val value = p
}
}
}

trait ProjectAnswer extends js.Object {
val project: Project
}

ioPrompt[ProjectAnswer](projectQuestion).map(_.project)
import cue4s.*, catseffect.PromptsIO

def promptLibName(prompts: PromptsIO): IO[LibName] =
prompts
.text("Which library?")
.map(_.toEither)
.flatMap(IO.fromEither)
.map(LibName(_))

def promptProject(prompts: PromptsIO, project: js.Array[Project]): IO[Project] =
val mapping = project.toList
.map: p =>
val title = s"${p.repository} from ${p.organization}"
val value = p

title -> p
.toMap

if mapping.isEmpty then IO.raiseError(CompletionError.Error("no repos found"))
else
prompts
.singleChoice("From which repo?", mapping.keySet.toList.sorted)
.map(_.toEither)
.flatMap(IO.fromEither)
.map(mapping(_))
end promptProject

def promptModules(project: Project): IO[js.Array[String]] =
val moduleQuestion = new js.Object {
val `type` = "autocompleteMultiselect"
val name = "modules"
val message = "Which modules ?"
val choices = project.artifacts.map { artifact =>
new js.Object {
val title = artifact
val value = artifact
}
}
}

trait ProjectAnswer extends js.Object {
val modules: js.Array[String]
}
def promptModules(prompts: PromptsIO, project: Project): IO[js.Array[String]] =
val choices = project.artifacts.toList

ioPrompt[ProjectAnswer](moduleQuestion).map(_.modules)
prompts
.multiChoiceNoneSelected("Which modules?", choices)
.map(_.toEither)
.flatMap(IO.fromEither)
.map(_.toJSArray)
end promptModules

def promptVersion(project: ProjectDetails): IO[String] =
val versionQuestion = new js.Object {
val `type` = "autocomplete"
val name = "version"
val message = "Which version ?"
val choices = project.versions.map { version =>
new js.Object {
val title = version
val value = version
}
}
}

trait VersionAnswer extends js.Object:
val version: String

ioPrompt[VersionAnswer](versionQuestion).map(_.version)
def promptVersion(prompts: PromptsIO, project: ProjectDetails): IO[String] =
prompts
.singleChoice("Which version?", project.versions.toList)
.map(_.toEither)
.flatMap(IO.fromEither)
end promptVersion

def promptBuildTool: IO[BuildTool] =
val versionQuestion = new js.Object {
val `type` = "select"
val name = "buildTool"
val message = "Which build tool ?"
val choices = BuildTool.values.map { bt =>
new js.Object {
val title = bt.toString()
val value = bt
}
}.toJSArray
}

trait VersionAnswer extends js.Object {
val buildTool: BuildTool
}

ioPrompt[VersionAnswer](versionQuestion).map(_.buildTool)
def promptBuildTool(prompts: PromptsIO): IO[BuildTool] =
prompts
.singleChoice(
"Which build tool?",
BuildTool.values.map(_.toString).toList
)
.map(_.toEither)
.flatMap(IO.fromEither)
.map(BuildTool.valueOf)
end promptBuildTool