diff --git a/.mergify.yml b/.mergify.yml index af3ab3df..e46d7373 100644 --- a/.mergify.yml +++ b/.mergify.yml @@ -17,3 +17,27 @@ pull_request_rules: - status-success=Build and Test (ubuntu-latest, 3, temurin@17, rootJVM) actions: merge: {} +- name: Label core PRs + conditions: + - files~=^modules/core/ + actions: + label: + add: + - core + remove: [] +- name: Label testkit PRs + conditions: + - files~=^modules/testkit/ + actions: + label: + add: + - testkit + remove: [] +- name: Label tests PRs + conditions: + - files~=^modules/tests/ + actions: + label: + add: + - tests + remove: [] diff --git a/build.sbt b/build.sbt index 5b2308c4..ad856d05 100644 --- a/build.sbt +++ b/build.sbt @@ -1,35 +1,51 @@ Global / onChangedBuildSource := ReloadOnSourceChanges ThisBuild / crossScalaVersions := List("3.3.1") -ThisBuild / tlBaseVersion := "0.35" +ThisBuild / tlBaseVersion := "0.36" ThisBuild / tlCiReleaseBranches := Seq("master") -lazy val root = tlCrossRootProject.aggregate(crystal) +lazy val root = tlCrossRootProject.aggregate(core, testkit, tests) -lazy val crystal = crossProject(JVMPlatform, JSPlatform) - .in(file(".")) +lazy val core = crossProject(JVMPlatform, JSPlatform) + .in(file("modules/core")) .settings( + name := "crystal", scalacOptions += "-language:implicitConversions", libraryDependencies ++= Settings.Libraries.CatsJS.value ++ Settings.Libraries.CatsEffectJS.value ++ Settings.Libraries.Fs2JS.value ++ Settings.Libraries.Monocle.value ++ - Settings.Libraries.Log4Cats.value ++ - ( - Settings.Libraries.MUnit.value ++ - Settings.Libraries.Discipline.value ++ - Settings.Libraries.DisciplineMUnit.value ++ - Settings.Libraries.CatsLaws.value ++ - Settings.Libraries.MonocleMacro.value ++ - Settings.Libraries.MonocleLaw.value - ).map(_ % Test) + Settings.Libraries.Log4Cats.value ) .jsSettings( libraryDependencies ++= { - Settings.Libraries.ScalaJSReact.value ++ (if (scalaBinaryVersion.value == "3") - Settings.Libraries.LucumaReact.value - else Settings.Libraries.ReactCommon.value) + Settings.Libraries.ScalaJSReact.value ++ + Settings.Libraries.LucumaReact.value } ) + +lazy val testkit = crossProject(JVMPlatform, JSPlatform) + .in(file("modules/testkit")) + .settings( + name := "crystal-testkit", + libraryDependencies ++= + Settings.Libraries.ScalaCheck.value + ) + .dependsOn(core) + +lazy val tests = crossProject(JVMPlatform, JSPlatform) + .in(file("modules/tests")) + .enablePlugins(NoPublishPlugin) + .settings( + name := "crystal-tests", + libraryDependencies ++= + (Settings.Libraries.MUnit.value ++ + Settings.Libraries.Discipline.value ++ + Settings.Libraries.DisciplineMUnit.value ++ + Settings.Libraries.CatsLaws.value ++ + Settings.Libraries.MonocleMacro.value ++ + Settings.Libraries.MonocleLaw.value).map(_ % Test) + ) + .dependsOn(testkit) diff --git a/js/src/main/scala/crystal/react/hooks/SerialState.scala b/modules/core/js/src/main/scala/crystal/react/hooks/SerialState.scala similarity index 100% rename from js/src/main/scala/crystal/react/hooks/SerialState.scala rename to modules/core/js/src/main/scala/crystal/react/hooks/SerialState.scala diff --git a/js/src/main/scala/crystal/react/hooks/UseAsyncEffect.scala b/modules/core/js/src/main/scala/crystal/react/hooks/UseAsyncEffect.scala similarity index 100% rename from js/src/main/scala/crystal/react/hooks/UseAsyncEffect.scala rename to modules/core/js/src/main/scala/crystal/react/hooks/UseAsyncEffect.scala diff --git a/js/src/main/scala/crystal/react/hooks/UseEffectResult.scala b/modules/core/js/src/main/scala/crystal/react/hooks/UseEffectResult.scala similarity index 100% rename from js/src/main/scala/crystal/react/hooks/UseEffectResult.scala rename to modules/core/js/src/main/scala/crystal/react/hooks/UseEffectResult.scala diff --git a/js/src/main/scala/crystal/react/hooks/UseEffectWhenDepsReady.scala b/modules/core/js/src/main/scala/crystal/react/hooks/UseEffectWhenDepsReady.scala similarity index 100% rename from js/src/main/scala/crystal/react/hooks/UseEffectWhenDepsReady.scala rename to modules/core/js/src/main/scala/crystal/react/hooks/UseEffectWhenDepsReady.scala diff --git a/js/src/main/scala/crystal/react/hooks/UseResource.scala b/modules/core/js/src/main/scala/crystal/react/hooks/UseResource.scala similarity index 100% rename from js/src/main/scala/crystal/react/hooks/UseResource.scala rename to modules/core/js/src/main/scala/crystal/react/hooks/UseResource.scala diff --git a/js/src/main/scala/crystal/react/hooks/UseSerialState.scala b/modules/core/js/src/main/scala/crystal/react/hooks/UseSerialState.scala similarity index 100% rename from js/src/main/scala/crystal/react/hooks/UseSerialState.scala rename to modules/core/js/src/main/scala/crystal/react/hooks/UseSerialState.scala diff --git a/js/src/main/scala/crystal/react/hooks/UseSerialStateView.scala b/modules/core/js/src/main/scala/crystal/react/hooks/UseSerialStateView.scala similarity index 100% rename from js/src/main/scala/crystal/react/hooks/UseSerialStateView.scala rename to modules/core/js/src/main/scala/crystal/react/hooks/UseSerialStateView.scala diff --git a/js/src/main/scala/crystal/react/hooks/UseSingleEffect.scala b/modules/core/js/src/main/scala/crystal/react/hooks/UseSingleEffect.scala similarity index 100% rename from js/src/main/scala/crystal/react/hooks/UseSingleEffect.scala rename to modules/core/js/src/main/scala/crystal/react/hooks/UseSingleEffect.scala diff --git a/js/src/main/scala/crystal/react/hooks/UseStateCallback.scala b/modules/core/js/src/main/scala/crystal/react/hooks/UseStateCallback.scala similarity index 100% rename from js/src/main/scala/crystal/react/hooks/UseStateCallback.scala rename to modules/core/js/src/main/scala/crystal/react/hooks/UseStateCallback.scala diff --git a/js/src/main/scala/crystal/react/hooks/UseStateView.scala b/modules/core/js/src/main/scala/crystal/react/hooks/UseStateView.scala similarity index 100% rename from js/src/main/scala/crystal/react/hooks/UseStateView.scala rename to modules/core/js/src/main/scala/crystal/react/hooks/UseStateView.scala diff --git a/js/src/main/scala/crystal/react/hooks/UseStateViewWithReuse.scala b/modules/core/js/src/main/scala/crystal/react/hooks/UseStateViewWithReuse.scala similarity index 100% rename from js/src/main/scala/crystal/react/hooks/UseStateViewWithReuse.scala rename to modules/core/js/src/main/scala/crystal/react/hooks/UseStateViewWithReuse.scala diff --git a/js/src/main/scala/crystal/react/hooks/UseStreamResource.scala b/modules/core/js/src/main/scala/crystal/react/hooks/UseStreamResource.scala similarity index 100% rename from js/src/main/scala/crystal/react/hooks/UseStreamResource.scala rename to modules/core/js/src/main/scala/crystal/react/hooks/UseStreamResource.scala diff --git a/js/src/main/scala/crystal/react/hooks/package.scala b/modules/core/js/src/main/scala/crystal/react/hooks/package.scala similarity index 100% rename from js/src/main/scala/crystal/react/hooks/package.scala rename to modules/core/js/src/main/scala/crystal/react/hooks/package.scala diff --git a/js/src/main/scala/crystal/react/package.scala b/modules/core/js/src/main/scala/crystal/react/package.scala similarity index 100% rename from js/src/main/scala/crystal/react/package.scala rename to modules/core/js/src/main/scala/crystal/react/package.scala diff --git a/js/src/main/scala/crystal/react/reuse/AppliedSyntax.scala b/modules/core/js/src/main/scala/crystal/react/reuse/AppliedSyntax.scala similarity index 100% rename from js/src/main/scala/crystal/react/reuse/AppliedSyntax.scala rename to modules/core/js/src/main/scala/crystal/react/reuse/AppliedSyntax.scala diff --git a/js/src/main/scala/crystal/react/reuse/CurrySyntax.scala b/modules/core/js/src/main/scala/crystal/react/reuse/CurrySyntax.scala similarity index 100% rename from js/src/main/scala/crystal/react/reuse/CurrySyntax.scala rename to modules/core/js/src/main/scala/crystal/react/reuse/CurrySyntax.scala diff --git a/js/src/main/scala/crystal/react/reuse/CurryingSyntax.scala b/modules/core/js/src/main/scala/crystal/react/reuse/CurryingSyntax.scala similarity index 100% rename from js/src/main/scala/crystal/react/reuse/CurryingSyntax.scala rename to modules/core/js/src/main/scala/crystal/react/reuse/CurryingSyntax.scala diff --git a/js/src/main/scala/crystal/react/reuse/ReusableInterop.scala b/modules/core/js/src/main/scala/crystal/react/reuse/ReusableInterop.scala similarity index 100% rename from js/src/main/scala/crystal/react/reuse/ReusableInterop.scala rename to modules/core/js/src/main/scala/crystal/react/reuse/ReusableInterop.scala diff --git a/js/src/main/scala/crystal/react/reuse/Reuse.scala b/modules/core/js/src/main/scala/crystal/react/reuse/Reuse.scala similarity index 100% rename from js/src/main/scala/crystal/react/reuse/Reuse.scala rename to modules/core/js/src/main/scala/crystal/react/reuse/Reuse.scala diff --git a/js/src/main/scala/crystal/react/reuse/package.scala b/modules/core/js/src/main/scala/crystal/react/reuse/package.scala similarity index 100% rename from js/src/main/scala/crystal/react/reuse/package.scala rename to modules/core/js/src/main/scala/crystal/react/reuse/package.scala diff --git a/js/src/main/scala/crystal/react/syntax/effect.scala b/modules/core/js/src/main/scala/crystal/react/syntax/effect.scala similarity index 100% rename from js/src/main/scala/crystal/react/syntax/effect.scala rename to modules/core/js/src/main/scala/crystal/react/syntax/effect.scala diff --git a/js/src/main/scala/crystal/react/syntax/package.scala b/modules/core/js/src/main/scala/crystal/react/syntax/package.scala similarity index 100% rename from js/src/main/scala/crystal/react/syntax/package.scala rename to modules/core/js/src/main/scala/crystal/react/syntax/package.scala diff --git a/js/src/main/scala/crystal/react/syntax/pot.scala b/modules/core/js/src/main/scala/crystal/react/syntax/pot.scala similarity index 100% rename from js/src/main/scala/crystal/react/syntax/pot.scala rename to modules/core/js/src/main/scala/crystal/react/syntax/pot.scala diff --git a/js/src/main/scala/crystal/react/syntax/view.scala b/modules/core/js/src/main/scala/crystal/react/syntax/view.scala similarity index 100% rename from js/src/main/scala/crystal/react/syntax/view.scala rename to modules/core/js/src/main/scala/crystal/react/syntax/view.scala diff --git a/shared/src/main/scala/crystal/Pot.scala b/modules/core/shared/src/main/scala/crystal/Pot.scala similarity index 100% rename from shared/src/main/scala/crystal/Pot.scala rename to modules/core/shared/src/main/scala/crystal/Pot.scala diff --git a/shared/src/main/scala/crystal/PotOption.scala b/modules/core/shared/src/main/scala/crystal/PotOption.scala similarity index 100% rename from shared/src/main/scala/crystal/PotOption.scala rename to modules/core/shared/src/main/scala/crystal/PotOption.scala diff --git a/shared/src/main/scala/crystal/package.scala b/modules/core/shared/src/main/scala/crystal/package.scala similarity index 100% rename from shared/src/main/scala/crystal/package.scala rename to modules/core/shared/src/main/scala/crystal/package.scala diff --git a/shared/src/main/scala/crystal/syntax.scala b/modules/core/shared/src/main/scala/crystal/syntax.scala similarity index 100% rename from shared/src/main/scala/crystal/syntax.scala rename to modules/core/shared/src/main/scala/crystal/syntax.scala diff --git a/shared/src/main/scala/crystal/viewF.scala b/modules/core/shared/src/main/scala/crystal/viewF.scala similarity index 100% rename from shared/src/main/scala/crystal/viewF.scala rename to modules/core/shared/src/main/scala/crystal/viewF.scala diff --git a/shared/src/test/scala/crystal/EqInstances.scala b/modules/testkit/shared/src/main/scala/crystal/EqInstances.scala similarity index 92% rename from shared/src/test/scala/crystal/EqInstances.scala rename to modules/testkit/shared/src/main/scala/crystal/EqInstances.scala index da741ae5..cdf96b67 100644 --- a/shared/src/test/scala/crystal/EqInstances.scala +++ b/modules/testkit/shared/src/main/scala/crystal/EqInstances.scala @@ -19,7 +19,7 @@ trait EqInstances { private[this] def arbitraryValues[A](using A: Arbitrary[A]): LazyList[A] = LazyList.continually(A.arbitrary.sample).flatten - given viewEq[A: Eq: Arbitrary: Cogen]: Eq[ViewF[Id, A]] = Eq.instance { (v1, v2) => + given [A: Eq: Arbitrary: Cogen]: Eq[ViewF[Id, A]] = Eq.instance { (v1, v2) => Eq[A].eqv(v1.get, v2.get) && arbitraryValues[(A => A)].take(functionEqualityCheckCount).forall { f => var newA1: A | Null = null diff --git a/modules/testkit/shared/src/main/scala/crystal/arb/arbitraries.scala b/modules/testkit/shared/src/main/scala/crystal/arb/arbitraries.scala new file mode 100644 index 00000000..fd2acad8 --- /dev/null +++ b/modules/testkit/shared/src/main/scala/crystal/arb/arbitraries.scala @@ -0,0 +1,45 @@ +// Copyright (c) 2016-2023 Association of Universities for Research in Astronomy, Inc. (AURA) +// For license information see LICENSE or https://opensource.org/licenses/BSD-3-Clause + +package crystal.arb + +import cats.Id +import crystal.Pot +import crystal.PotOption +import crystal.ViewF +import org.scalacheck.* + +import scala.annotation.targetName + +import Arbitrary.* +import Gen.* + +given [A: Arbitrary]: Arbitrary[Pot[A]] = + Arbitrary( + oneOf( + Gen.const(Pot.Pending), + arbitrary[Throwable].map(Pot.Error.apply), + arbitrary[A].map(Pot.Ready.apply) + ) + ) + +given [A](using Arbitrary[A => A]): Arbitrary[Pot[A] => Pot[A]] = + Arbitrary(arbitrary[A => A].map(f => _.map(f))) + +given [A: Arbitrary]: Arbitrary[PotOption[A]] = + Arbitrary( + oneOf( + Gen.const(PotOption.Pending), + arbitrary[Throwable].map(PotOption.Error.apply), + Gen.const(PotOption.ReadyNone), + arbitrary[A].map(PotOption.ReadySome.apply) + ) + ) + +given viewFArb[A: Arbitrary]: Arbitrary[ViewF[Id, A]] = Arbitrary( + arbitrary[A].map(a => ViewF.apply[Id, A](a, (f, cb) => cb(f(a)))) +) + +@targetName("potOptionFnArbitrary") +given [A](using fArb: Arbitrary[A => A]): Arbitrary[PotOption[A] => PotOption[A]] = + Arbitrary(arbitrary[A => A].map(f => _.map(f))) diff --git a/js/src/test/scala/crystal/react/reuse/ReuseSpec.scala b/modules/tests/js/src/test/scala/crystal/react/reuse/ReuseSpec.scala similarity index 99% rename from js/src/test/scala/crystal/react/reuse/ReuseSpec.scala rename to modules/tests/js/src/test/scala/crystal/react/reuse/ReuseSpec.scala index e1ca7d8e..fd4f7bea 100644 --- a/js/src/test/scala/crystal/react/reuse/ReuseSpec.scala +++ b/modules/tests/js/src/test/scala/crystal/react/reuse/ReuseSpec.scala @@ -7,6 +7,8 @@ import japgolly.scalajs.react.Reusability import munit.ScalaCheckSuite import org.scalacheck.Prop.* +import scala.language.implicitConversions + final class ReuseSpec extends ScalaCheckSuite { test("Reuse(function).by(value) syntax") { diff --git a/shared/src/test/scala/crystal/PotOptionSpec.scala b/modules/tests/shared/src/test/scala/crystal/PotOptionSpec.scala similarity index 99% rename from shared/src/test/scala/crystal/PotOptionSpec.scala rename to modules/tests/shared/src/test/scala/crystal/PotOptionSpec.scala index ab8e6db0..935ad8ef 100644 --- a/shared/src/test/scala/crystal/PotOptionSpec.scala +++ b/modules/tests/shared/src/test/scala/crystal/PotOptionSpec.scala @@ -8,6 +8,7 @@ import cats.laws.discipline.SemigroupalTests.Isomorphisms import cats.laws.discipline.* import cats.laws.discipline.arbitrary.* import cats.syntax.all.* +import crystal.arb.given import crystal.throwable.given import monocle.law.discipline.PrismTests import munit.DisciplineSuite @@ -18,8 +19,6 @@ import scala.util.Failure import scala.util.Success import scala.util.Try -import arbitraries.given - class PotOptionSpec extends DisciplineSuite { given Isomorphisms[PotOption] = Isomorphisms.invariant[PotOption] diff --git a/shared/src/test/scala/crystal/PotSpec.scala b/modules/tests/shared/src/test/scala/crystal/PotSpec.scala similarity index 99% rename from shared/src/test/scala/crystal/PotSpec.scala rename to modules/tests/shared/src/test/scala/crystal/PotSpec.scala index ed49d915..eadf8201 100644 --- a/shared/src/test/scala/crystal/PotSpec.scala +++ b/modules/tests/shared/src/test/scala/crystal/PotSpec.scala @@ -8,6 +8,7 @@ import cats.laws.discipline.SemigroupalTests.Isomorphisms import cats.laws.discipline.* import cats.laws.discipline.arbitrary.* import cats.syntax.all.* +import crystal.arb.given import crystal.throwable.given import monocle.law.discipline.PrismTests import munit.DisciplineSuite @@ -18,8 +19,6 @@ import scala.util.Failure import scala.util.Success import scala.util.Try -import arbitraries.given - class PotSpec extends DisciplineSuite { given Isomorphisms[Pot] = Isomorphisms.invariant[Pot] diff --git a/shared/src/test/scala/crystal/ViewFLawsSpec.scala b/modules/tests/shared/src/test/scala/crystal/ViewFLawsSpec.scala similarity index 95% rename from shared/src/test/scala/crystal/ViewFLawsSpec.scala rename to modules/tests/shared/src/test/scala/crystal/ViewFLawsSpec.scala index a58c36f0..d369af97 100644 --- a/shared/src/test/scala/crystal/ViewFLawsSpec.scala +++ b/modules/tests/shared/src/test/scala/crystal/ViewFLawsSpec.scala @@ -7,10 +7,9 @@ import cats.Eq import cats.Id import cats.Invariant import cats.laws.discipline.InvariantSemigroupalTests +import crystal.arb.given import munit.DisciplineSuite -import arbitraries.given - class ViewFLawsSpec extends DisciplineSuite with EqInstances { checkAll( diff --git a/shared/src/test/scala/crystal/ViewFSpec.scala b/modules/tests/shared/src/test/scala/crystal/ViewFSpec.scala similarity index 100% rename from shared/src/test/scala/crystal/ViewFSpec.scala rename to modules/tests/shared/src/test/scala/crystal/ViewFSpec.scala diff --git a/shared/src/test/scala/crystal/ViewListFSpec.scala b/modules/tests/shared/src/test/scala/crystal/ViewListFSpec.scala similarity index 100% rename from shared/src/test/scala/crystal/ViewListFSpec.scala rename to modules/tests/shared/src/test/scala/crystal/ViewListFSpec.scala diff --git a/shared/src/test/scala/crystal/ViewOptFSpec.scala b/modules/tests/shared/src/test/scala/crystal/ViewOptFSpec.scala similarity index 100% rename from shared/src/test/scala/crystal/ViewOptFSpec.scala rename to modules/tests/shared/src/test/scala/crystal/ViewOptFSpec.scala diff --git a/shared/src/test/scala/crystal/package.scala b/modules/tests/shared/src/test/scala/crystal/package.scala similarity index 100% rename from shared/src/test/scala/crystal/package.scala rename to modules/tests/shared/src/test/scala/crystal/package.scala diff --git a/project/Settings.scala b/project/Settings.scala index 2610b43e..c2537b5c 100644 --- a/project/Settings.scala +++ b/project/Settings.scala @@ -11,11 +11,11 @@ object Settings { val disciplineMUnit = "1.0.9" val fs2 = "3.9.3" val log4Cats = "2.6.0" + val lucumaReact = "0.47.0" val monocle = "3.2.0" val mUnit = "0.7.29" val mUnitCatsEffect = "1.0.7" - val reactCommon = "0.24.0" - val lucumaReact = "0.47.0" + val scalaCheck = "1.17.0" val scalajsReact = "2.1.1" } @@ -90,16 +90,15 @@ object Settings { ) ) - val ReactCommon = Def.setting( + val LucumaReact = Def.setting( Seq[ModuleID]( - "io.github.cquiroz.react" %%% "common" % reactCommon, - "io.github.cquiroz.react" %%% "cats" % reactCommon + "edu.gemini" %%% "lucuma-react-common" % lucumaReact ) ) - val LucumaReact = Def.setting( + val ScalaCheck = Def.setting( Seq[ModuleID]( - "edu.gemini" %%% "lucuma-react-common" % lucumaReact + "org.scalacheck" %%% "scalacheck" % scalaCheck ) ) diff --git a/shared/src/test/scala/crystal/arbitraries.scala b/shared/src/test/scala/crystal/arbitraries.scala deleted file mode 100644 index a056abde..00000000 --- a/shared/src/test/scala/crystal/arbitraries.scala +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright (c) 2016-2023 Association of Universities for Research in Astronomy, Inc. (AURA) -// For license information see LICENSE or https://opensource.org/licenses/BSD-3-Clause - -package crystal - -import cats.Id -import org.scalacheck.* - -import scala.annotation.targetName - -import Arbitrary.* -import Gen.* - -object arbitraries { - given [A: Arbitrary]: Arbitrary[Pot[A]] = - Arbitrary( - oneOf( - Gen.const(Pot.Pending), - arbitrary[Throwable].map(Pot.Error.apply), - arbitrary[A].map(Pot.Ready.apply) - ) - ) - - given [A](using Arbitrary[A => A]): Arbitrary[Pot[A] => Pot[A]] = - Arbitrary(arbitrary[A => A].map(f => _.map(f))) - - given [A: Arbitrary]: Arbitrary[PotOption[A]] = - Arbitrary( - oneOf( - Gen.const(PotOption.Pending), - arbitrary[Throwable].map(PotOption.Error.apply), - Gen.const(PotOption.ReadyNone), - arbitrary[A].map(PotOption.ReadySome.apply) - ) - ) - - given viewFArb[A: Arbitrary]: Arbitrary[ViewF[Id, A]] = Arbitrary( - arbitrary[A].map(a => ViewF.apply[Id, A](a, (f, cb) => cb(f(a)))) - ) - - @targetName("potOptionFnArbitrary") - given [A](using fArb: Arbitrary[A => A]): Arbitrary[PotOption[A] => PotOption[A]] = - Arbitrary(arbitrary[A => A].map(f => _.map(f))) -}