diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 14551759..5f8de2e2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -58,7 +58,7 @@ jobs: - run: sbt ++${{ matrix.scala }} ci - name: Compress target directories - run: tar cf targets.tar target dom/target tests/target project/target + run: tar cf targets.tar target mdocs/target tests/target dom/target jsdocs/target project/target - name: Upload target directories uses: actions/upload-artifact@v2 @@ -133,3 +133,46 @@ jobs: run: echo $PGP_SECRET | base64 -d | gpg --import - run: sbt ++${{ matrix.scala }} release + + site: + name: Publish Site + needs: [build] + if: github.event_name != 'pull_request' + strategy: + matrix: + os: [ubuntu-latest] + scala: [2.13.6] + java: [adopt@1.8] + runs-on: ${{ matrix.os }} + steps: + - name: Checkout current branch (full) + uses: actions/checkout@v2 + with: + fetch-depth: 0 + + - name: Setup Java and Scala + uses: olafurpg/setup-scala@v13 + with: + java-version: ${{ matrix.java }} + + - name: Cache sbt + uses: actions/cache@v2 + with: + path: | + ~/.sbt + ~/.ivy2/cache + ~/.coursier/cache/v1 + ~/.cache/coursier/v1 + ~/AppData/Local/Coursier/Cache/v1 + ~/Library/Caches/Coursier/v1 + key: ${{ runner.os }}-sbt-cache-v2-${{ hashFiles('**/*.sbt') }}-${{ hashFiles('project/build.properties') }} + + - name: Generate + run: sbt ++${{ matrix.scala }} docs/mdoc docs/laikaSite + + - name: Publish + uses: peaceiris/actions-gh-pages@v3 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: ./mdocs/target/docs/site + publish_branch: gh-pages diff --git a/README.md b/README.md index 64920cc7..47096bce 100644 --- a/README.md +++ b/README.md @@ -3,17 +3,20 @@ Use http4s in your browser with Scala.js! Features: -* A `Client` implementation backed by [`fetch`](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) -* A [`Service Worker`](https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API) integration to install your `HttpRoutes` as a [`FetchEvent` handler](https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerGlobalScope/onfetch) +* A [`Client` implementation](https://www.javadoc.io/doc/org.http4s/http4s-dom_sjs1_2.13/latest/org/http4s/dom/FetchClientBuilder.html) backed by [`fetch`](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) +* A [`Service Worker` integration](https://www.javadoc.io/doc/org.http4s/http4s-dom_sjs1_2.13/latest/org/http4s/dom/ServiceWorker$.html) to install your `HttpRoutes` as a [`FetchEvent` handler](https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerGlobalScope/onfetch) * Encoders for [`File`](https://developer.mozilla.org/en-US/docs/Web/API/File), [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob) and [`ReadableStream`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream) +Notably, http4s-dom can also be used to create _serverless_ apps with [Cloudflare Workers](https://workers.cloudflare.com) which have adopted the same APIs used in the browser! ### Usage -```sbt -// supports http4s 0.23.x and scala-js-dom 2.x +[![http4s-dom Scala version support](https://index.scala-lang.org/http4s/http4s-dom/http4s-dom/latest-by-scala-version.svg?targetType=Js)](https://index.scala-lang.org/http4s/http4s-dom/http4s-dom) + +```scala +// Supports http4s 0.23.x and scala-js-dom 2.x libraryDependencies += "org.http4s" %%% "http4s-dom" % "0.2.0" -// supports http4s 0.23.x and scala-js-dom 1.x +// Or, for compatibility with scala-js-dom 1.x libraryDependencies += "org.http4s" %%% "http4s-dom" % "0.1.0" ``` diff --git a/build.sbt b/build.sbt index df0c1fa1..b6a0257e 100644 --- a/build.sbt +++ b/build.sbt @@ -143,3 +143,112 @@ lazy val tests = project ) .dependsOn(dom) .enablePlugins(ScalaJSPlugin, BuildInfoPlugin, NoPublishPlugin) + +lazy val jsdocs = + project + .dependsOn(dom) + .settings( + fatalWarningsInCI := false, // Remove once mdocjs bumps to sjs-dom v2 + libraryDependencies ++= Seq( + "org.http4s" %%% "http4s-circe" % http4sVersion, + "io.circe" %%% "circe-generic" % "0.15.0-M1" + ) + ) + .enablePlugins(ScalaJSPlugin) + +import laika.ast.Path.Root +import laika.ast._ +import laika.ast.LengthUnit._ +import laika.helium.Helium +import laika.helium.config.{HeliumIcon, IconLink, ImageLink, ReleaseInfo, Teaser, TextLink} +import laika.theme.config.Color + +Global / excludeLintKeys += laikaDescribe + +lazy val docs = + project + .in(file("mdocs")) + .settings( + fatalWarningsInCI := false, // Remove once mdocjs bumps to sjs-dom v2 + mdocJS := Some(jsdocs), + Laika / sourceDirectories := Seq(mdocOut.value), + laikaDescribe := "", + laikaConfig ~= { _.withRawContent }, + laikaExtensions ++= Seq( + laika.markdown.github.GitHubFlavor, + laika.parse.code.SyntaxHighlighting + ), + laikaTheme := Helium + .defaults + .all + .metadata( + language = Some("en"), + title = Some("http4s-dom") + ) + .site + .autoLinkJS() // Actually, this *disables* auto-linking, to avoid duplicates with mdoc + .site + .layout( + contentWidth = px(860), + navigationWidth = px(275), + topBarHeight = px(35), + defaultBlockSpacing = px(10), + defaultLineHeight = 1.5, + anchorPlacement = laika.helium.config.AnchorPlacement.Right + ) + .site + .themeColors( + primary = Color.hex("5B7980"), + secondary = Color.hex("cc6600"), + primaryMedium = Color.hex("a7d4de"), + primaryLight = Color.hex("e9f1f2"), + text = Color.hex("5f5f5f"), + background = Color.hex("ffffff"), + bgGradient = + (Color.hex("334044"), Color.hex("5B7980")) // only used for landing page background + ) + .site + .darkMode + .disabled + .site + .topNavigationBar( + homeLink = ImageLink.external( + "https://http4s.org", + Image.external("https://http4s.org/v1.0/images/http4s-logo-text-dark-2.svg")), + navLinks = Seq( + IconLink.external( + "https://www.javadoc.io/doc/org.http4s/http4s-dom_sjs1_2.13/latest/org/http4s/dom/index.html", + HeliumIcon.api, + options = Styles("svg-link")), + IconLink.external( + "https://github.com/http4s/http4s-dom", + HeliumIcon.github, + options = Styles("svg-link")), + IconLink.external("https://discord.gg/XF3CXcMzqD", HeliumIcon.chat), + IconLink.external("https://twitter.com/http4s", HeliumIcon.twitter) + ) + ) + .build + ) + .enablePlugins(MdocPlugin, LaikaPlugin) + +ThisBuild / githubWorkflowAddedJobs += + WorkflowJob( + "site", + "Publish Site", + scalas = List(crossScalaVersions.value.last), + cond = Some("github.event_name != 'pull_request'"), + needs = List("build"), + steps = githubWorkflowJobSetup.value.toList ::: List( + WorkflowStep.Sbt(List("docs/mdoc", "docs/laikaSite"), name = Some("Generate")), + WorkflowStep.Use( + UseRef.Public("peaceiris", "actions-gh-pages", "v3"), + Map( + "github_token" -> "${{ secrets.GITHUB_TOKEN }}", + "publish_dir" -> "./mdocs/target/docs/site", + "publish_branch" -> "gh-pages" + ), + name = Some("Publish") + ) + ) + ) diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 00000000..38c5ce21 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,69 @@ +# http4s-dom + +Use http4s in your browser with Scala.js! +Features: + +* A [`Client` implementation](https://www.javadoc.io/doc/org.http4s/http4s-dom_sjs1_2.13/latest/org/http4s/dom/FetchClientBuilder.html) backed by [`fetch`](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) +* A [`Service Worker` integration](https://www.javadoc.io/doc/org.http4s/http4s-dom_sjs1_2.13/latest/org/http4s/dom/ServiceWorker$.html) to install your `HttpRoutes` as a [`FetchEvent` handler](https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerGlobalScope/onfetch) +* Encoders for [`File`](https://developer.mozilla.org/en-US/docs/Web/API/File), [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob) and [`ReadableStream`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream) + +Notably, http4s-dom can also be used to create _serverless_ apps with [Cloudflare Workers](https://workers.cloudflare.com) which have adopted the same APIs used in the browser! + +## Installation + +[![http4s-dom Scala version support](https://index.scala-lang.org/http4s/http4s-dom/http4s-dom/latest-by-scala-version.svg?targetType=Js)](https://index.scala-lang.org/http4s/http4s-dom/http4s-dom) + +```scala +// Supports http4s 0.23.x and scala-js-dom 2.x +libraryDependencies += "org.http4s" %%% "http4s-dom" % "0.2.0" + +// Or, for compatibility with scala-js-dom 1.x +// libraryDependencies += "org.http4s" %%% "http4s-dom" % "0.1.0" + +// recommended, brings in the latest client module +libraryDependencies += "org.http4s" %%% "http4s-client" % "0.23.6" + +// optional, for JSON support +libraryDependencies += "org.http4s" %%% "http4s-circe" % "0.23.6" +libraryDependencies += "io.circe" %%% "circe-generic" % "0.15.0-M1" +``` + +## Example + +```scala mdoc:js +
+

+ I'm bored. +

+ +

+
+--- +import cats.effect._ +import cats.effect.unsafe.implicits._ +import io.circe.generic.auto._ +import org.http4s.circe.CirceEntityCodec._ +import org.http4s.dom._ +import org.scalajs.dom._ + +val client = FetchClientBuilder[IO].create + +val activityElement = document.getElementById("activity") + +case class Activity(activity: String) + +val fetchActivity: IO[Unit] = for { + _ <- IO(activityElement.innerHTML = "fetching...") + activity <- client.expect[Activity]("https://www.boredapi.com/api/activity") + _ <- IO(activityElement.innerHTML = activity.activity) +} yield () + +val button = document.getElementById("button").asInstanceOf[html.Button] + +button.onclick = _ => fetchActivity.unsafeRunAndForget() +``` + +## Learn more + +The [`FetchClientBuilder`](https://www.javadoc.io/doc/org.http4s/http4s-dom_sjs1_2.13/latest/org/http4s/dom/FetchClientBuilder.html) creates a standard http4s [`Client`](https://http4s.org/v0.23/api/org/http4s/client/client) that is described in the [http4s documentation](https://http4s.org/v0.23/client/). +Similarly, the [`ServiceWorker`](https://www.javadoc.io/doc/org.http4s/http4s-dom_sjs1_2.13/latest/org/http4s/dom/ServiceWorker$.html) `FetchEvent` listener integrates directly with [http4s services](https://http4s.org/v0.23/service/). diff --git a/docs/default.template.html b/docs/default.template.html new file mode 100644 index 00000000..d03ff058 --- /dev/null +++ b/docs/default.template.html @@ -0,0 +1,105 @@ + + + + + + + + ${cursor.currentDocument.title} + @:for(laika.site.metadata.authors) + + @:@ + @:for(laika.site.metadata.description) + + @:@ + @:for(helium.favIcons) + + @:@ + @:for(helium.webFonts) + + @:@ + @:linkCSS { paths = ${helium.site.includeCSS} } + @:linkJS { paths = ${helium.site.includeJS} } + @:heliumInitVersions + @:heliumInitPreview(container) + + + + + +
+ + + + + ${?helium.topBar.home} + + ${?helium.topBar.links} + +
+ + + +
+ + + +
+ + ${cursor.currentDocument.content} + + +
+ + + +
+ +
+ + + diff --git a/docs/styles/site.css b/docs/styles/site.css new file mode 100644 index 00000000..cf1a921d --- /dev/null +++ b/docs/styles/site.css @@ -0,0 +1,9 @@ +header img { + height: 28px; + width: auto; + margin-top: 6px; +} + +#sidebar li.nav-header { + margin-top: -20px; +} diff --git a/project/CI.scala b/project/CI.scala index 14a701ce..c2cdad8f 100644 --- a/project/CI.scala +++ b/project/CI.scala @@ -52,8 +52,8 @@ object CI { extends CI( rootProject = "/", jsEnv = Some(JSEnv.Chrome), - testCommands = List("test"), - mimaReport = false, + testCommands = List("test", "docs/mdoc"), + mimaReport = true, suffixCommands = List()) val AllCIs: List[CI] = List(Firefox, Chrome) diff --git a/project/plugins.sbt b/project/plugins.sbt index 4ffd04d4..cd1e3726 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -5,3 +5,5 @@ addSbtPlugin("com.codecommit" % "sbt-spiewak-sonatype" % "0.22.1") addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.4.3") addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.7.1") addSbtPlugin("com.eed3si9n" % "sbt-buildinfo" % "0.10.0") +addSbtPlugin("org.scalameta" % "sbt-mdoc" % "2.2.24") +addSbtPlugin("org.planet42" % "laika-sbt" % "0.18.0")