Skip to content
Merged
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
3 changes: 3 additions & 0 deletions .scalafix.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
rules = [OrganizeImports]

OrganizeImports.removeUnused = false
65 changes: 63 additions & 2 deletions .scalafmt.conf
Original file line number Diff line number Diff line change
@@ -1,2 +1,63 @@
version = "3.7.15"
runner.dialect = scala3
version = 3.7.17

runner.dialect = scala3

maxColumn = 96

includeCurlyBraceInSelectChains = true
includeNoParensInSelectChains = true

optIn {
breakChainOnFirstMethodDot = false
forceBlankLineBeforeDocstring = true
}

binPack {
literalArgumentLists = true
parentConstructors = Never
}

danglingParentheses {
defnSite = false
callSite = false
ctrlSite = false

exclude = []
}

newlines {
beforeCurlyLambdaParams = multilineWithCaseOnly
afterCurlyLambda = squash
implicitParamListModifierPrefer = before
sometimesBeforeColonInMethodReturnType = true
}

align.preset = none
align.stripMargin = true

assumeStandardLibraryStripMargin = true

docstrings {
style = Asterisk
oneline = unfold
}

project.git = true

trailingCommas = never

rewrite {
// RedundantBraces honestly just doesn't work, otherwise I'd love to use it
rules = [PreferCurlyFors, RedundantParens, SortImports]

redundantBraces {
maxLines = 1
stringInterpolation = true
}
}

rewriteTokens {
"⇒": "=>"
"→": "->"
"←": "<-"
}
4 changes: 2 additions & 2 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ lazy val root = (project in file("."))
"com.google.jimfs" % "jimfs" % "1.2",
"com.outr" %% "scribe" % "3.13.0",
"org.typelevel" %% "cats-effect" % "3.5.2",
"org.scala-js" %% "scalajs-js-envs-test-kit" % "1.1.1" % Test,
"org.scala-js" %% "scalajs-js-envs-test-kit" % "1.4.0" % Test,
"com.novocode" % "junit-interface" % "0.11" % Test
),
releaseProcess := Seq[ReleaseStep](
Expand All @@ -65,7 +65,7 @@ lazy val root = (project in file("."))
},
// For all Sonatype accounts created on or after February 2021
sonatypeCredentialHost := "s01.oss.sonatype.org",
Test / parallelExecution := false,
Test / parallelExecution := true,
Test / publishArtifact := false,
usePgpKeyHex("F7E440260BAE93EB4AD2723D6613CA76E011F638")
)
4 changes: 4 additions & 0 deletions project/plugins.sbt
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
val sbtTypelevelVersion = "0.6.4"
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "2.1.1")
addSbtPlugin("com.github.sbt" % "sbt-release" % "1.3.0")
addSbtPlugin("com.github.sbt" % "sbt-pgp" % "2.2.1")
addSbtPlugin("org.xerial.sbt" % "sbt-sonatype" % "3.10.0")
addSbtPlugin("org.scoverage" % "sbt-scoverage" % "2.0.9")
addSbtPlugin("org.typelevel" % "sbt-typelevel" % sbtTypelevelVersion)
addSbtPlugin("org.typelevel" % "sbt-typelevel-scalafix" % sbtTypelevelVersion)
addSbtPlugin("org.typelevel" % "sbt-typelevel-site" % sbtTypelevelVersion)
7 changes: 5 additions & 2 deletions src/main/java/jsenv/DriverJar.java
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ void extractDriverToTempDir() throws URISyntaxException, IOException {
URI uri = maybeExtractNestedJar(originalUri);

// Create zip filesystem if loading from jar.
try (FileSystem fileSystem = "jar".equals(uri.getScheme()) ? initFileSystem(uri) : null) {
FileSystem fileSystem = "jar".equals(uri.getScheme()) ? initFileSystem(uri) : null;
Path srcRoot = Paths.get(uri);
// jar file system's .relativize gives wrong results when used with
// spring-boot-maven-plugin, convert to the default filesystem to
Expand Down Expand Up @@ -159,6 +159,8 @@ void extractDriverToTempDir() throws URISyntaxException, IOException {
throw new RuntimeException("Failed to extract driver from " + uri + ", full uri: " + originalUri, e);
}
});
if (fileSystem != null) {
fileSystem.close();
}
}

Expand All @@ -173,7 +175,8 @@ private URI maybeExtractNestedJar(final URI uri) throws URISyntaxException {
}
String innerJar = String.join(JAR_URL_SEPARATOR, parts[0], parts[1]);
URI jarUri = new URI(innerJar);
try (FileSystem fs = FileSystems.newFileSystem(jarUri, Collections.emptyMap())) {
try {
FileSystem fs = FileSystems.newFileSystem(jarUri, Collections.emptyMap());
Path fromPath = Paths.get(jarUri);
Path toPath = driverTempDir.resolve(fromPath.getFileName().toString());
Files.copy(fromPath, toPath);
Expand Down
36 changes: 36 additions & 0 deletions src/main/scala/jsenv/playwright/CEComRun.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package jsenv.playwright

import cats.effect.IO
import cats.effect.unsafe.implicits.global
import jsenv.playwright.PWEnv.Config
import org.scalajs.jsenv.Input
import org.scalajs.jsenv.JSComRun
import org.scalajs.jsenv.RunConfig

import scala.concurrent._

// browserName, headless, pwConfig, runConfig, input, onMessage
class CEComRun(
override val browserName: String,
override val headless: Boolean,
override val pwConfig: Config,
override val runConfig: RunConfig,
override val input: Seq[Input],
onMessage: String => Unit
) extends JSComRun
with Runner {
scribe.debug(s"Creating CEComRun for $browserName")
// enableCom is false for CERun and true for CEComRun
// send is called only from JSComRun
override def send(msg: String): Unit = sendQueue.offer(msg)
// receivedMessage is called only from JSComRun. Hence its implementation is empty in CERun
override protected def receivedMessage(msg: String): Unit = onMessage(msg)

lazy val future: Future[Unit] =
jsRunPrg(browserName, headless, isComEnabled = true, None)
.use(_ => IO.unit)
.unsafeToFuture()

}

private class WindowOnErrorException(errs: List[String]) extends Exception(s"JS error: $errs")
153 changes: 14 additions & 139 deletions src/main/scala/jsenv/playwright/CERun.scala
Original file line number Diff line number Diff line change
@@ -1,152 +1,27 @@
package jsenv.playwright

import cats.effect.IO
import cats.effect.unsafe.implicits.global
import cats.effect.{IO, Resource}
import com.microsoft.playwright.BrowserType.LaunchOptions
import jsenv.playwright.PWEnv.Config
import jsenv.playwright.PageFactory._
import jsenv.playwright.ResourcesFactory._
import org.scalajs.jsenv.{Input, JSComRun, JSRun, RunConfig}
import org.scalajs.jsenv.Input
import org.scalajs.jsenv.JSRun
import org.scalajs.jsenv.RunConfig

import java.util.concurrent.ConcurrentLinkedQueue
import java.util.concurrent.atomic.AtomicBoolean
import scala.concurrent._
import scala.concurrent.duration.DurationInt

class CERun(
browserName: String,
headless: Boolean,
pwConfig: Config,
runConfig: RunConfig,
input: Seq[Input]
) extends JSRun {

implicit val ec: scala.concurrent.ExecutionContext =
scala.concurrent.ExecutionContext.global

// enableCom is false for CERun and true for CEComRun
protected val enableCom = false
protected val intf = "this.scalajsPlayWrightInternalInterface"
protected val sendQueue = new ConcurrentLinkedQueue[String]
// receivedMessage is called only from JSComRun. Hence its implementation is empty in CERun
protected def receivedMessage(msg: String): Unit = ()

/** A Future that completes if the run completes.
*
* The future is failed if the run fails.
*
* Note that a JSRun is not required to ever terminate on it's own. That
* means even if all code is executed and the event loop is empty, the run
* may continue to run. As a consequence, it is *not* correct to rely on
* termination of a JSRun without any external means of stopping it (i.e.
* calling [[close]]).
*/
var wantToClose = new AtomicBoolean(false)
// List of programs
// 1. isInterfaceUp()
// Create PW resource if not created. Create browser,context and page
// 2. Sleep
// 3. wantClose
// 4. sendAll()
// 5. fetchAndProcess()
// 6. Close diver
// 7. Close streams
// 8. Close materializer
// Flow
// if interface is down and dont want to close wait for 100 milliseconds
// interface is up and dont want to close sendAll(), fetchAndProcess() Sleep for 100 milliseconds
// If want to close then close driver, streams, materializer
// After future is completed close driver, streams, materializer

def jsRunPrg(
browserName: String,
headless: Boolean,
isComEnabled: Boolean,
launchOptions: Option[LaunchOptions]
): Resource[IO, Unit] = for {
_ <- Resource.pure(
scribe.info(
s"Begin Main with isComEnabled $isComEnabled " +
s"and browserName $browserName " +
s"and headless is $headless "
)
)
pageInstance <- createPage(
browserName,
headless,
launchOptions
)
_ <- preparePageForJsRun(
pageInstance,
materializer(pwConfig),
input,
isComEnabled
)
connectionReady <- isConnectionUp(pageInstance, intf)
_ <-
if (!connectionReady) Resource.pure[IO, Unit](IO.sleep(100.milliseconds))
else Resource.pure[IO, Unit](IO.unit)
_ <- isConnectionUp(pageInstance, intf)
out <- outputStream(runConfig)
_ <- processUntilStop(
wantToClose,
pageInstance,
intf,
sendQueue,
out,
receivedMessage,
isComEnabled
)
} yield ()

override val browserName: String,
override val headless: Boolean,
override val pwConfig: Config,
override val runConfig: RunConfig,
override val input: Seq[Input]
) extends JSRun
with Runner {
scribe.debug(s"Creating CERun for $browserName")
lazy val future: Future[Unit] =
jsRunPrg(browserName, headless, enableCom, None)
jsRunPrg(browserName, headless, isComEnabled = false, None)
.use(_ => IO.unit)
.unsafeToFuture()

/** Stops the run and releases all the resources.
*
* This <strong>must</strong> be called to ensure the run's resources are
* released.
*
* Whether or not this makes the run fail or not is up to the implementation.
* However, in the following cases, calling [[close]] may not fail the run:
* <ul> <li>[[future]] is already completed when [[close]] is called.
* <li>This is a [[CERun]] and the event loop inside the VM is empty.
* </ul>
*
* Idempotent, async, nothrow.
*/

override def close(): Unit = {
wantToClose.set(true)
scribe.info(s"StopSignal is ${wantToClose.get()}")
}

override protected def receivedMessage(msg: String): Unit = ()
}
// browserName, headless, pwConfig, runConfig, input, onMessage
class CEComRun(
browserName: String,
headless: Boolean,
pwConfig: Config,
runConfig: RunConfig,
input: Seq[Input],
onMessage: String => Unit
) extends CERun(
browserName,
headless,
pwConfig,
runConfig,
input
)
with JSComRun {
// enableCom is false for CERun and true for CEComRun
override protected val enableCom = true
// send is called only from JSComRun
override def send(msg: String): Unit = sendQueue.offer(msg)
// receivedMessage is called only from JSComRun. Hence its implementation is empty in CERun
override protected def receivedMessage(msg: String): Unit = onMessage(msg)
}

private class WindowOnErrorException(errs: List[String])
extends Exception(s"JS error: $errs")
20 changes: 16 additions & 4 deletions src/main/scala/jsenv/playwright/CEUtils.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
package jsenv.playwright

import org.scalajs.jsenv.{Input, UnsupportedInputException}
import scribe.format.{FormatterInterpolator, dateFull, level, mdc, messages, methodName, threadName}
import org.scalajs.jsenv.Input
import org.scalajs.jsenv.UnsupportedInputException
import scribe.format.FormatterInterpolator
import scribe.format.classNameSimple
import scribe.format.dateFull
import scribe.format.level
import scribe.format.mdc
import scribe.format.messages
import scribe.format.methodName
import scribe.format.threadName

import java.nio.file.Path

Expand All @@ -12,6 +20,8 @@ object CEUtils {
): String = {
val tags = fullInput.map {
case Input.Script(path) => makeTag(path, "text/javascript", materializer)
case Input.CommonJSModule(path) =>
makeTag(path, "text/javascript", materializer)
case Input.ESModule(path) => makeTag(path, "module", materializer)
case _ => throw new UnsupportedInputException(fullInput)
}
Expand All @@ -36,8 +46,10 @@ object CEUtils {

def setupLogger(showLogs: Boolean, debug: Boolean): Unit = {
val formatter =
formatter"$dateFull [$threadName] $level $methodName - $messages$mdc"
scribe.Logger.root
formatter"$dateFull [$threadName] $classNameSimple $level $methodName - $messages$mdc"
scribe
.Logger
.root
.clearHandlers()
.withHandler(
formatter = formatter
Expand Down
Loading