Skip to content

Commit

Permalink
Setup a propoer pipline?
Browse files Browse the repository at this point in the history
  • Loading branch information
Quafadas committed Apr 3, 2024
1 parent 8176d2a commit 3b52696
Show file tree
Hide file tree
Showing 8 changed files with 217 additions and 78 deletions.
14 changes: 14 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,21 @@ on:
pull_request:

jobs:

test:
if: github.event_name == 'push' || github.event_name == 'pull_request'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- uses: coursier/cache-action@v6.4
- uses: VirtusLab/scala-cli-setup@main
- uses: taiki-e/install-action@just
- run: just gha

publish:
needs: test
if: github.event_name == 'push'
runs-on: ubuntu-latest
steps:
Expand Down
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@
.metals
.bsp
.vscode
native
native
testDir
26 changes: 0 additions & 26 deletions htmlGen.test.scala

This file was deleted.

22 changes: 9 additions & 13 deletions justfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,22 @@ default:
just --list

setupIde:
scala-cli setup-ide .
scala-cli setup-ide . --exclude testDir --exclude native

package:
scala-cli package .


test:
scala-cli test project.scala htmlGen.scala htmlGen.test.scala

packageWatcher:
scala-cli package file.nativewatcher.scala project.native.scala -f

serveNative:
scala-cli run file.nativewatcher.scala project.native.scala

procT:
scala-cli run process.scala project.scala
scala-cli test . --exclude testDir --exclude native

jvmWatch:
scala-cli run project.scala file.watcher.scala

jvmServe:
scala-cli run project.scala live.serverJvm.scala htmlGen.scala file.hasher.scala -- "/Users/simon/Code/helloScalaJs/out"
scala-cli run project.scala live.serverJvm.scala htmlGen.scala file.hasher.scala -- "/Users/simon/Code/helloScalaJs/out"

setupPlaywright:
cs launch com.microsoft.playwright:playwright:1.41.1 -M "com.microsoft.playwright.CLI" -- install --with-deps

gha:
setupPlaywright test
86 changes: 49 additions & 37 deletions live.serverJvm.scala → live.server.scala
Original file line number Diff line number Diff line change
Expand Up @@ -33,43 +33,49 @@ import fs2.io.file.Files
import fs2.io.Watcher.Event
import org.http4s.ServerSentEvent
import _root_.io.circe.Encoder
import Main2.seedMapOnStart

import cats.syntax.strong
import fs2.concurrent.Topic

sealed trait FrontendEvent(val typ: String) derives Encoder.AsObject
case class KeepAlive(override val typ: String = "keepAlive") extends FrontendEvent(typ) derives Encoder.AsObject
case class PageRefresh(override val typ: String = "pageRefresh") extends FrontendEvent(typ) derives Encoder.AsObject

object Main2 extends IOApp:

import cats.effect.Concurrent

val refreshTopic = Topic[IO, String].toResource

def buildRunner(refreshTopic: Topic[IO, String]) = ProcessBuilder(
"just",
"packageW"
).withWorkingDirectory(fs2.io.file.Path("/Users/simon/Code/helloScalaJs"))
.spawn[IO]
.use { p =>
// p.stderr.through(fs2.io.stdout).compile.drain >>
p.stderr
.through(text.utf8.decode)
.debug()
.chunks
.evalMap(aChunk =>
if aChunk.toString.contains("node ./") then
// IO.println("emit") >>
refreshTopic.publish1("hi")
else IO.unit
)
.compile
.drain
}
.background
object LiveServer extends IOApp:

private val refreshTopic = Topic[IO, String].toResource

private def buildRunner(refreshTopic: Topic[IO, String], workDir: fs2.io.file.Path, outDir: fs2.io.file.Path) =
ProcessBuilder(
"scala-cli",
"--power",
"package",
"--js",
".",
"-o",
outDir.toString(),
"-f",
"-w"
).withWorkingDirectory(workDir)
.spawn[IO]
.use { p =>
// p.stderr.through(fs2.io.stdout).compile.drain >>
p.stderr
.through(text.utf8.decode)
.debug()
.chunks
.evalMap(aChunk =>
if aChunk.toString.contains("node ./") then
// IO.println("emit") >>
refreshTopic.publish1("hi")
else IO.unit
)
.compile
.drain
}
.background

def seedMapOnStart(stringPath: String, mr: MapRef[IO, String, Option[String]]) =
private def seedMapOnStart(stringPath: String, mr: MapRef[IO, String, Option[String]]) =
val asFs2 = fs2.io.file.Path(stringPath)
fs2.io.file
.Files[IO]
Expand All @@ -92,7 +98,7 @@ object Main2 extends IOApp:

end seedMapOnStart

def fileWatcher(
private def fileWatcher(
stringPath: fs2.io.file.Path,
mr: MapRef[IO, String, Option[String]]
): ResourceIO[IO[OutcomeIO[Unit]]] =
Expand Down Expand Up @@ -135,7 +141,7 @@ object Main2 extends IOApp:
.background
end fileWatcher

def routes(
private def routes(
stringPath: String,
refreshTopic: Topic[IO, String]
): Resource[IO, (HttpApp[IO], MapRef[IO, String, Option[String]], Ref[IO, Map[String, String]])] =
Expand Down Expand Up @@ -169,7 +175,7 @@ object Main2 extends IOApp:
)
end routes

def buildServer(httpApp: HttpApp[IO]) = EmberServerBuilder
private def buildServer(httpApp: HttpApp[IO]) = EmberServerBuilder
.default[IO]
.withHttp2
.withHost(host"localhost")
Expand All @@ -178,20 +184,26 @@ object Main2 extends IOApp:
.withShutdownTimeout(10.milli)
.build

/*
args.head is the base directory
args.tail.head is the output directory
*/
override def run(args: List[String]): IO[ExitCode] =
val serveDir = args.head
println("args || " + args.mkString(","))
val baseDir = args.head
val outDir = args.tail.head
val server = for
_ <- IO.println("Start dev server ").toResource
refreshPub <- refreshTopic
_ <- buildRunner(refreshPub)
routes <- routes(serveDir, refreshPub)
_ <- buildRunner(refreshPub, fs2.io.file.Path(baseDir), fs2.io.file.Path(outDir))
routes <- routes(outDir.toString(), refreshPub)
(app, mr, ref) = routes
_ <- seedMapOnStart(args.head, mr)
_ <- fileWatcher(fs2.io.file.Path(serveDir), mr)
_ <- fileWatcher(fs2.io.file.Path(baseDir), mr)
server <- buildServer(app)
yield server

server.use(_ => IO.never).as(ExitCode.Success)

end run
end Main2
end LiveServer
116 changes: 116 additions & 0 deletions live.server.test.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import scala.compiletime.uninitialized
import com.microsoft.playwright.*
import com.microsoft.playwright.assertions.PlaywrightAssertions.assertThat
import com.sun.net.httpserver.*;
import java.net.InetSocketAddress;
import com.microsoft.playwright.Page.InputValueOptions
import com.sun.net.httpserver.SimpleFileServer
import java.nio.file.Paths
import com.microsoft.playwright.impl.driver.Driver
import scala.concurrent.Future

import cats.effect.unsafe.implicits.global

/*
Run
cs launch com.microsoft.playwright:playwright:1.41.1 -M "com.microsoft.playwright.CLI" -- install --with-deps
before this test, to make sure that the driver bundles are downloaded.
*/
class PlaywrightTest extends munit.FunSuite:

val port = 8080
var pw: Playwright = uninitialized
var browser: Browser = uninitialized
var page: Page = uninitialized

val testDir = os.pwd / "testDir"
val outDir = testDir / ".out"

override def beforeAll(): Unit =

pw = Playwright.create()
browser = pw.chromium().launch();
page = browser.newPage();
end beforeAll

test("incremental") {

if os.exists(testDir) then os.remove.all(os.pwd / "testDir")
os.makeDir.all(outDir)

os.write.over(testDir / "hello.scala", helloWorldCode("Hello"))
os.write.over(testDir / ".out" / "styles.less", "")

LiveServer
.run(
List(
testDir.toString,
outDir.toString
)
)
.unsafeToFuture()

Thread.sleep(500) // give the thing time to start.

page.navigate(s"http://localhost:8085/index.html")
assertThat(page.locator("h1")).containsText("HelloWorld");

os.write.over(testDir / "hello.scala", helloWorldCode("Bye"))
assertThat(page.locator("h1")).containsText("ByeWorld");

os.write.append(testDir / ".out" / "styles.less", "h1 { color: red; }")
assertThat(page.locator("h1")).hasCSS("color", "rgb(255, 0, 0)")

}

override def afterAll(): Unit =
super.afterAll()
pw.close()
// os.remove.all(testDir)

end afterAll

end PlaywrightTest

def helloWorldCode(greet: String) = s"""
//> using scala 3.3.3
//> using platform js

//> using dep org.scala-js::scalajs-dom::2.8.0
//> using dep com.raquo::laminar::17.0.0-M6

//> using jsModuleKind es
//> using jsModuleSplitStyleStr smallmodulesfor
//> using jsSmallModuleForPackage webapp

package webapp

import org.scalajs.dom
import org.scalajs.dom.document
import com.raquo.laminar.api.L.{*, given}

@main
def main: Unit =
renderOnDomContentLoaded(
dom.document.getElementById("app"),
interactiveApp
)

def interactiveApp =
val hiVar = Var("World")
div(
h1(
"$greet",
child.text <-- hiVar.signal
),
p("This is a simple example of a Laminar app."),
// https://demo.laminar.dev/app/form/controlled-inputs
input(
typ := "text",
controlled(
value <-- hiVar.signal,
onInput.mapToValue --> hiVar.writer
)
)
)
"""
7 changes: 6 additions & 1 deletion project.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
//> using scala 3.4.1
//> using platform jvm

//> using exclude testDir

//> using dep org.http4s::http4s-ember-server::0.23.26
//> using dep org.http4s::http4s-dsl::0.23.26
//> using dep org.http4s::http4s-scalatags::0.25.2
Expand All @@ -10,7 +12,10 @@
//> using dep co.fs2::fs2-io::3.10.2
//> using dep com.lihaoyi::scalatags::0.12.0

//> using dep org.scalameta::munit::1.0.0-M11
//> using test.dep org.scalameta::munit::1.0.0-M11
//> using test.dep com.microsoft.playwright:playwright:1.41.1
//> using test.dep com.microsoft.playwright:driver-bundle:1.41.1
//> using test.dep com.lihaoyi::os-lib:0.9.3

//> using publish.repository central-s01
//> using publish.organization io.github.quafadas
Expand Down
21 changes: 21 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# An experiment in a dev server for scala JS

Try and break the dependance on node / npm completely whilst retaining a sane developer experience for browser based scala-js development.

## Goals
- Live reload / link on change
- Hot application of style (no page reload)
- Proxy server (TODO)

## Contraints

- Scala cli to build frontend
- ESModule output (only)
- Third party ESModules via import map rather than npm
- Styles through LESS

## Quickstart

```sh

```

0 comments on commit 3b52696

Please sign in to comment.