diff options
author | Jakob Odersky <jakob@odersky.com> | 2016-11-07 18:06:30 -0800 |
---|---|---|
committer | Jakob Odersky <jakob@odersky.com> | 2016-11-08 16:52:27 -0800 |
commit | 71e5666ceeab0db8bb69c3bfcd2ddef5ab982029 (patch) | |
tree | d3420020e70e88a1532d8d0960c2544bf14eb015 | |
parent | a11c7826f2ba4ae20a0ab4e004e53ba910157bc3 (diff) | |
download | workbench-71e5666ceeab0db8bb69c3bfcd2ddef5ab982029.tar.gz workbench-71e5666ceeab0db8bb69c3bfcd2ddef5ab982029.tar.bz2 workbench-71e5666ceeab0db8bb69c3bfcd2ddef5ab982029.zip |
Update scalajs versions, related dependencies and remove obsolete features
-rw-r--r-- | build.sbt | 48 | ||||
-rw-r--r-- | client/src/main/scala/workbench/WorkbenchClient.scala | 36 | ||||
-rw-r--r-- | example/build.sbt | 18 | ||||
-rw-r--r-- | example/project/build.properties | 2 | ||||
-rw-r--r-- | example/project/build.sbt | 3 | ||||
-rw-r--r-- | example/src/main/resources/index-dev.html | 2 | ||||
-rw-r--r-- | example/src/main/resources/index-opt.html | 2 | ||||
-rw-r--r-- | example/src/main/scala/example/ScalaJSExample.scala | 7 | ||||
-rw-r--r-- | project/Dependencies.scala | 16 | ||||
-rw-r--r-- | project/build.sbt | 2 | ||||
-rw-r--r-- | shared/main/scala/workbench/Shared.scala | 15 | ||||
-rw-r--r-- | src/main/scala/workbench/Server.scala | 17 | ||||
-rw-r--r-- | src/main/scala/workbench/WorkbenchPlugin.scala (renamed from src/main/scala/workbench/Plugin.scala) | 105 |
13 files changed, 124 insertions, 149 deletions
@@ -1,8 +1,7 @@ import sbt.Keys._ -val scalaJsVersion = "0.6.2" - val defaultSettings = Seq( + scalacOptions ++= Seq("-feature", "-deprecation"), unmanagedSourceDirectories in Compile += baseDirectory.value / "shared" / "main" / "scala", unmanagedSourceDirectories in Test += baseDirectory.value / "shared" / "test" / "scala" ) @@ -11,7 +10,7 @@ lazy val root = project.in(file(".")).settings(defaultSettings:_*).settings( name := "workbench", version := "0.3.0-SNAPSHOT", organization := "com.lihaoyi", - scalaVersion := "2.10.5", + scalaVersion := "2.10.6", sbtPlugin := true, publishArtifact in Test := false, publishTo := Some("releases" at "https://oss.sonatype.org/service/local/staging/deploy/maven2"), @@ -39,30 +38,25 @@ lazy val root = project.in(file(".")).settings(defaultSettings:_*).settings( (fullOptJS in (client, Compile)).value (artifactPath in (client, Compile, fullOptJS)).value }, - resolvers += Resolver.url("scala-js-releases", - url("http://dl.bintray.com/content/scala-js/scala-js-releases"))( - Resolver.ivyStylePatterns), - addSbtPlugin("org.scala-js" % "sbt-scalajs" % "0.6.1"), + addSbtPlugin("org.scala-js" % "sbt-scalajs" % "0.6.13"), libraryDependencies ++= Seq( - "org.scala-lang" % "scala-compiler" % scalaVersion.value, - "io.spray" % "spray-can" % "1.3.1", - "io.spray" % "spray-routing" % "1.3.1", - "com.typesafe.akka" %% "akka-actor" % "2.3.9", - "org.scala-lang.modules" %% "scala-async" % "0.9.3" % "provided", - "com.lihaoyi" %% "autowire" % "0.2.5", - "com.lihaoyi" %% "upickle" % "0.2.8" - ), - resolvers += "bintray/non" at "http://dl.bintray.com/non/maven" + Dependencies.sprayCan, + Dependencies.sprayRouting, + Dependencies.akka, + Dependencies.autowire.value, + Dependencies.upickle.value + ) ) -lazy val client = project.in(file("client")).enablePlugins(ScalaJSPlugin) - .settings(defaultSettings: _*) - .settings( - unmanagedSourceDirectories in Compile += baseDirectory.value / ".." / "shared" / "main" / "scala", - libraryDependencies ++= Seq( - "org.scala-js" %%% "scalajs-dom" % "0.8.0", - "com.lihaoyi" %%% "autowire" % "0.2.4", - "com.lihaoyi" %%% "upickle" % "0.2.6" - ), - emitSourceMaps := false -) +lazy val client = project.in(file("client")) + .enablePlugins(ScalaJSPlugin) + .settings(defaultSettings: _*) + .settings( + unmanagedSourceDirectories in Compile += baseDirectory.value / ".." / "shared" / "main" / "scala", + libraryDependencies ++= Seq( + Dependencies.autowire.value, + Dependencies.dom.value, + Dependencies.upickle.value + ), + emitSourceMaps := false + ) diff --git a/client/src/main/scala/workbench/WorkbenchClient.scala b/client/src/main/scala/workbench/WorkbenchClient.scala index dc99bbc..e98b16b 100644 --- a/client/src/main/scala/workbench/WorkbenchClient.scala +++ b/client/src/main/scala/workbench/WorkbenchClient.scala @@ -1,20 +1,22 @@ package com.lihaoyi.workbench -import upickle._ +import upickle.Js +import upickle.default +import upickle.default.{Reader, Writer} +import upickle.json import org.scalajs.dom import org.scalajs.dom.ext._ -import upickle.{Reader, Writer, Js} import scala.scalajs.js import scala.scalajs.js.annotation.JSExport -import scalajs.concurrent.JSExecutionContext.Implicits.runNow +import scalajs.concurrent.JSExecutionContext.Implicits.queue import org.scalajs.dom.raw._ /** * The connection from workbench server to the client */ -object Wire extends autowire.Server[Js.Value, upickle.Reader, upickle.Writer] with ReadWrite{ +object Wire extends autowire.Server[Js.Value, Reader, Writer] with ReadWrite{ def wire(parsed: Js.Arr): Unit = { val Js.Arr(path, args: Js.Obj) = parsed - val req = new Request(upickle.readJs[Seq[String]](path), args.value.toMap) + val req = new Request(default.readJs[Seq[String]](path), args.value.toMap) Wire.route[Api](WorkbenchClient).apply(req) } } @@ -28,7 +30,7 @@ object WorkbenchClient extends Api{ @JSExport var success = false @JSExport - def main(bootSnippet: String, host: String, port: Int): Unit = { + def main(host: String, port: Int): Unit = { def rec(): Unit = { Ajax.post(s"http://$host:$port/notifications").onComplete { case util.Success(data) => @@ -44,7 +46,7 @@ object WorkbenchClient extends Api{ if (success) println("Workbench disconnected " + e) success = false interval = math.min(interval * 2, 30000) - dom.setTimeout(() => rec(), interval) + dom.window.setTimeout(() => rec(), interval) } } @@ -59,31 +61,19 @@ object WorkbenchClient extends Api{ override def clear(): Unit = { dom.document.asInstanceOf[js.Dynamic].body = shadowBody.cloneNode(true) for(i <- 0 until 100000){ - dom.clearTimeout(i) - dom.clearInterval(i) + dom.window.clearTimeout(i) + dom.window.clearInterval(i) } } @JSExport override def reload(): Unit = { dom.console.log("Reloading page...") - dom.location.reload() + dom.window.location.reload() } @JSExport - override def run(path: String, bootSnippet: Option[String]): Unit = { + override def run(path: String): Unit = { val tag = dom.document.createElement("script").asInstanceOf[HTMLElement] - var loaded = false - tag.setAttribute("src", path) - bootSnippet.foreach{ bootSnippet => - tag.onreadystatechange = (e: dom.Event) => { - if (!loaded) { - dom.console.log("Workbench reboot") - js.eval(bootSnippet) - } - loaded = true - } - tag.asInstanceOf[js.Dynamic].onload = tag.onreadystatechange - } dom.document.head.appendChild(tag) } @JSExport diff --git a/example/build.sbt b/example/build.sbt index fed07bd..65f7ed2 100644 --- a/example/build.sbt +++ b/example/build.sbt @@ -1,25 +1,15 @@ -import com.lihaoyi.workbench.Plugin._ - -// Turn this project into a Scala.js project by importing these settings enablePlugins(ScalaJSPlugin) - -workbenchSettings +enablePlugins(WorkbenchPlugin) name := "Example" -scalaVersion := "2.11.2" +scalaVersion := "2.12.0" version := "0.1-SNAPSHOT" -resolvers += "Typesafe repository" at "http://repo.typesafe.com/typesafe/releases/" - libraryDependencies ++= Seq( - "org.scala-js" %%% "scalajs-dom" % "0.8.0" + "org.scala-js" %%% "scalajs-dom" % "0.9.1" ) -bootSnippet := "ScalaJSExample().main();" - -disableOptimizer := true - +// (experimental feature) spliceBrowsers <<= spliceBrowsers.triggeredBy(fastOptJS in Compile) - diff --git a/example/project/build.properties b/example/project/build.properties index 748703f..27e88aa 100644 --- a/example/project/build.properties +++ b/example/project/build.properties @@ -1 +1 @@ -sbt.version=0.13.7 +sbt.version=0.13.13 diff --git a/example/project/build.sbt b/example/project/build.sbt index f623fb6..2094cdd 100644 --- a/example/project/build.sbt +++ b/example/project/build.sbt @@ -1,4 +1,3 @@ - -addSbtPlugin("org.scala-js" % "sbt-scalajs" % "0.6.1") +addSbtPlugin("org.scala-js" % "sbt-scalajs" % "0.6.13") lazy val root = project.in(file(".")).dependsOn(file("../..")) diff --git a/example/src/main/resources/index-dev.html b/example/src/main/resources/index-dev.html index 364241b..6f27812 100644 --- a/example/src/main/resources/index-dev.html +++ b/example/src/main/resources/index-dev.html @@ -14,7 +14,7 @@ <script type="text/javascript" src="../example-fastopt.js"></script> <script> - ScalaJSExample().main(); + example.ScalaJSExample().main(); </script> </body> </html> diff --git a/example/src/main/resources/index-opt.html b/example/src/main/resources/index-opt.html index 5abb478..d8ad3ab 100644 --- a/example/src/main/resources/index-opt.html +++ b/example/src/main/resources/index-opt.html @@ -12,7 +12,7 @@ <script type="text/javascript" src="../example-opt.js"></script> <script> - ScalaJSExample().main(); + example.ScalaJSExample().main(); </script> </body> </html> diff --git a/example/src/main/scala/example/ScalaJSExample.scala b/example/src/main/scala/example/ScalaJSExample.scala index 0255262..3298e4f 100644 --- a/example/src/main/scala/example/ScalaJSExample.scala +++ b/example/src/main/scala/example/ScalaJSExample.scala @@ -1,6 +1,7 @@ package example import scala.scalajs.js.annotation.JSExport import org.scalajs.dom +import org.scalajs.dom.html import scala.util.Random case class Point(x: Int, y: Int){ @@ -8,14 +9,12 @@ case class Point(x: Int, y: Int){ def /(d: Int) = Point(x / d, y / d) } -// Seems like you need this for sbt ~fastOptJS to work -// mkdir ~/.sbt/0.13/plugins/target/scala-2.10/sbt-0.13/classes @JSExport object ScalaJSExample { val ctx = dom.document .getElementById("canvas") - .asInstanceOf[dom.HTMLCanvasElement] + .asInstanceOf[html.Canvas] .getContext("2d") .asInstanceOf[dom.CanvasRenderingContext2D] @@ -41,6 +40,6 @@ object ScalaJSExample { } @JSExport def main(): Unit = { - dom.setInterval(() => run, 10) + dom.window.setInterval(() => run, 10) } } diff --git a/project/Dependencies.scala b/project/Dependencies.scala new file mode 100644 index 0000000..c7e4c9e --- /dev/null +++ b/project/Dependencies.scala @@ -0,0 +1,16 @@ +import sbt._ +import org.scalajs.sbtplugin.ScalaJSPlugin.autoImport._ + +object Dependencies { + + // jvm dependencies + val sprayCan = "io.spray" % "spray-can" % "1.3.1" + val sprayRouting = "io.spray" % "spray-routing" % "1.3.1" + val akka = "com.typesafe.akka" %% "akka-actor" % "2.3.15" + + // js and shared dependencies + val autowire = Def.setting("com.lihaoyi" %%% "autowire" % "0.2.6") + val dom = Def.setting("org.scala-js" %%% "scalajs-dom" % "0.9.1") + val upickle = Def.setting("com.lihaoyi" %%% "upickle" % "0.4.3") + +} diff --git a/project/build.sbt b/project/build.sbt index 0dd657b..bfdab78 100644 --- a/project/build.sbt +++ b/project/build.sbt @@ -1 +1 @@ -addSbtPlugin("org.scala-js" % "sbt-scalajs" % "0.6.1") +addSbtPlugin("org.scala-js" % "sbt-scalajs" % "0.6.13") diff --git a/shared/main/scala/workbench/Shared.scala b/shared/main/scala/workbench/Shared.scala index f9a1434..c4c8a11 100644 --- a/shared/main/scala/workbench/Shared.scala +++ b/shared/main/scala/workbench/Shared.scala @@ -1,13 +1,15 @@ package com.lihaoyi.workbench -import upickle.{Js, Reader, Writer} +import upickle.default.{Reader, Writer} +import upickle.Js + /** * A standard way to read and write `Js.Value`s with autowire/upickle */ trait ReadWrite{ - def write[Result: Writer](r: Result) = upickle.writeJs(r) - def read[Result: Reader](p: Js.Value) = upickle.readJs[Result](p) + def write[Result: Writer](r: Result) = upickle.default.writeJs(r) + def read[Result: Reader](p: Js.Value) = upickle.default.readJs[Result](p) } /** @@ -31,8 +33,7 @@ trait Api{ def print(level: String, msg: String): Unit /** - * Execute the javascript file available at the given `path`. Optionally, - * run a `bootSnippet` after the file has been executed. + * Execute the javascript file available at the given `path`. */ - def run(path: String, bootSnippet: Option[String]): Unit -}
\ No newline at end of file + def run(path: String): Unit +} diff --git a/src/main/scala/workbench/Server.scala b/src/main/scala/workbench/Server.scala index 1d6d1ba..5953fcb 100644 --- a/src/main/scala/workbench/Server.scala +++ b/src/main/scala/workbench/Server.scala @@ -8,13 +8,13 @@ import spray.httpx.encoding.Gzip import spray.routing.SimpleRoutingApp import akka.actor.ActorDSL._ -import upickle.{Reader, Writer, Js} +import upickle.Js +import upickle.default.{Reader, Writer} import spray.http.{HttpEntity, AllOrigins, HttpResponse} import spray.http.HttpHeaders.`Access-Control-Allow-Origin` import concurrent.duration._ import scala.concurrent.Future import scala.io.Source -import org.scalajs.core.tools.optimizer.{ScalaJSClosureOptimizer, ScalaJSOptimizer} import org.scalajs.core.tools.io._ import org.scalajs.core.tools.logging.Level import scala.tools.nsc @@ -24,12 +24,11 @@ import scala.tools.nsc.backend.JavaPlatform import scala.tools.nsc.util.ClassPath.JavaContext import scala.collection.mutable import scala.tools.nsc.typechecker.Analyzer -import org.scalajs.core.tools.classpath.{CompleteClasspath, PartialClasspath} import scala.tools.nsc.util.{JavaClassPath, DirectoryClassPath} import spray.http.HttpHeaders._ import spray.http.HttpMethods._ -class Server(url: String, port: Int, bootSnippet: String) extends SimpleRoutingApp{ +class Server(url: String, port: Int) extends SimpleRoutingApp{ val corsHeaders: List[ModeledHeader] = List( `Access-Control-Allow-Methods`(OPTIONS, GET, POST), @@ -47,9 +46,9 @@ class Server(url: String, port: Int, bootSnippet: String) extends SimpleRoutingA /** * The connection from workbench server to the client */ - object Wire extends autowire.Client[Js.Value, upickle.Reader, upickle.Writer] with ReadWrite{ + object Wire extends autowire.Client[Js.Value, Reader, Writer] with ReadWrite{ def doCall(req: Request): Future[Js.Value] = { - longPoll ! Js.Arr(upickle.writeJs(req.path), Js.Obj(req.args.toSeq:_*)) + longPoll ! Js.Arr(upickle.default.writeJs(req.path), Js.Obj(req.args.toSeq:_*)) Future.successful(Js.Null) } } @@ -68,7 +67,7 @@ class Server(url: String, port: Int, bootSnippet: String) extends SimpleRoutingA case object Clear import system.dispatcher - system.scheduler.schedule(0 seconds, 10 seconds, self, Clear) + system.scheduler.schedule(0.seconds, 10.seconds, self, Clear) def respond(a: ActorRef, s: String) = { a ! HttpResponse( entity = s, @@ -116,7 +115,7 @@ class Server(url: String, port: Int, bootSnippet: String) extends SimpleRoutingA (function(){ $body - com.lihaoyi.workbench.WorkbenchClient().main(${upickle.write(bootSnippet)}, ${upickle.write(url)}, ${upickle.write(port)}) + com.lihaoyi.workbench.WorkbenchClient().main(${upickle.default.write(url)}, ${upickle.default.write(port)}) }).call(this) """ } @@ -132,4 +131,4 @@ class Server(url: String, port: Int, bootSnippet: String) extends SimpleRoutingA } def kill() = system.shutdown() -}
\ No newline at end of file +} diff --git a/src/main/scala/workbench/Plugin.scala b/src/main/scala/workbench/WorkbenchPlugin.scala index a120d0f..31fd659 100644 --- a/src/main/scala/workbench/Plugin.scala +++ b/src/main/scala/workbench/WorkbenchPlugin.scala @@ -5,24 +5,19 @@ import sbt.Keys._ import autowire._ import org.scalajs.sbtplugin.ScalaJSPlugin import org.scalajs.core.tools.io._ -import org.scalajs.core.tools.optimizer.ScalaJSOptimizer import org.scalajs.sbtplugin.ScalaJSPluginInternal._ import org.scalajs.sbtplugin.Implicits._ -object Plugin extends AutoPlugin { +object WorkbenchPlugin extends AutoPlugin { override def requires = ScalaJSPlugin object autoImport { val refreshBrowsers = taskKey[Unit]("Sends a message to all connected web pages asking them to refresh the page") - val updateBrowsers = taskKey[Unit]("Partially resets some of the stuff in the browser") + val updatedJS = taskKey[List[String]]("Provides the addresses of the JS files that have changed") val spliceBrowsers = taskKey[Unit]("Attempts to do a live update of the code running in the browser while maintaining state") val localUrl = settingKey[(String, Int)]("localUrl") - private[Plugin] val server = settingKey[Server]("local websocket server") - - val bootSnippet = settingKey[String]("piece of javascript to make things happen") - val updatedJS = taskKey[List[String]]("Provides the addresses of the JS files that have changed") val sjs = inputKey[Unit]("Run a command via the sjs REPL, which compiles it to Javascript and runs it in the browser") val replFile = taskKey[File]("The temporary file which holds the source code for the currently executing sjs REPL") val sjsReset = taskKey[Unit]("Reset the currently executing sjs REPL") @@ -30,30 +25,12 @@ object Plugin extends AutoPlugin { import autoImport._ import ScalaJSPlugin.AutoImport._ + val server = settingKey[Server]("local websocket server") + lazy val replHistory = collection.mutable.Buffer.empty[String] val workbenchSettings = Seq( localUrl := ("localhost", 12345), - updatedJS := { - var files: List[String] = Nil - ((crossTarget in Compile).value * "*.js").get.foreach { - (x: File) => - streams.value.log.info("workbench: Checking " + x.getName) - FileFunction.cached(streams.value.cacheDirectory / x.getName, FilesInfo.lastModified, FilesInfo.lastModified) { - (f: Set[File]) => - val fsPath = f.head.getAbsolutePath.drop(new File("").getAbsolutePath.length) - files = fsPath :: files - f - }(Set(x)) - } - files - }, - updatedJS := { - updatedJS.value.map{ path => - val url = localUrl.value - s"http://${url._1}:${url._2}$path" - } - }, (extraLoggers in ThisBuild) := { val clientLogger = FullLogger{ new Logger { @@ -71,16 +48,28 @@ object Plugin extends AutoPlugin { streams.value.log.info("workbench: Reloading Pages...") server.value.Wire[Api].reload().call() }, - updateBrowsers := { - val changed = updatedJS.value - // There is no point in clearing the browser if no js files have changed. - if (changed.length > 0) { - server.value.Wire[Api].clear().call() - - changed.foreach { path => - streams.value.log.info("workbench: Refreshing " + path) - server.value.Wire[Api].run(path, Some(bootSnippet.value)).call() - } + // this currently requires the old <<= syntax + // see https://github.com/sbt/sbt/issues/1444 + refreshBrowsers <<= refreshBrowsers.triggeredBy(fastOptJS in Compile), + updatedJS := { + var files: List[String] = Nil + ((crossTarget in Compile).value * "*.js").get.foreach { + (x: File) => + streams.value.log.info("workbench: Checking " + x.getName) + FileFunction.cached(streams.value.cacheDirectory / x.getName, FilesInfo.lastModified, FilesInfo.lastModified) { + (f: Set[File]) => + val fsPath = f.head.getAbsolutePath.drop(new File("").getAbsolutePath.length) + files = fsPath :: files + f + }(Set(x)) + } + files + }, + updatedJS := { + val paths = updatedJS.value + val url = localUrl.value + paths.map { path => + s"http://${url._1}:${url._2}$path" } }, spliceBrowsers := { @@ -91,17 +80,17 @@ object Plugin extends AutoPlugin { path <- changed if !path.endsWith(".js.js") }{ - streams.value.log.info("workbench: Splicing " + path) - val prefix = "http://localhost:12345/" + val url = localUrl.value + val prefix = s"http://${url._1}:${url._2}/" val s = munge(sbt.IO.read(new sbt.File(path.drop(prefix.length)))) sbt.IO.write(new sbt.File(path.drop(prefix.length) + ".js"), s.getBytes) - server.value.Wire[Api].run(path + ".js", None).call() + server.value.Wire[Api].run(path + ".js").call() } } }, - server := new Server(localUrl.value._1, localUrl.value._2, bootSnippet.value), + server := new Server(localUrl.value._1, localUrl.value._2), (onUnload in Global) := { (onUnload in Global).value.compose{ state => server.value.kill() state @@ -110,7 +99,6 @@ object Plugin extends AutoPlugin { artifactPath in sjs := crossTarget.value / "repl.js", replFile := { val f = sourceManaged.value / "repl.scala" - println("Creating replFile\n" + replHistory.mkString("\n")) sbt.IO.write(f, replHistory.mkString("\n")) f }, @@ -140,21 +128,21 @@ object Plugin extends AutoPlugin { Some(output.getParentFile.toURI()) else None - import ScalaJSOptimizer._ - - (scalaJSOptimizer in fastOptJS).value.optimizeCP( - (scalaJSPreLinkClasspath in fastOptJS).value, - Config( - output = WritableFileVirtualJSFile(output), - cache = None, - wantSourceMap = (emitSourceMaps in fastOptJS).value, - relativizeSourceMapBase = relSourceMapBase, - checkIR = (scalaJSOptimizerOptions in fastOptJS).value.checkScalaJSIR, - disableOptimizer = (scalaJSOptimizerOptions in fastOptJS).value.disableOptimizer, - batchMode = (scalaJSOptimizerOptions in fastOptJS).value.batchMode - ), - s.log - ) + // TODO: re-enable this feature for latest scalajs + // NOTE: maybe use 'scalaJSOptimizerOptions in fullOptJS' + // (scalaJSOptimizer in fastOptJS).value.optimizeCP( + // (scalaJSPreLinkClasspath in fastOptJS).value, + // Config( + // output = WritableFileVirtualJSFile(output), + // cache = None, + // wantSourceMap = (emitSourceMaps in fastOptJS).value, + // relativizeSourceMapBase = relSourceMapBase, + // checkIR = (scalaJSOptimizerOptions in fastOptJS).value.checkScalaJSIR, + // disableOptimizer = (scalaJSOptimizerOptions in fastOptJS).value.disableOptimizer, + // batchMode = (scalaJSOptimizerOptions in fastOptJS).value.batchMode + // ), + // s.log + // ) // end of C&P val outPath = sbt.IO.relativize( baseDirectory.value, @@ -167,8 +155,7 @@ object Plugin extends AutoPlugin { ) Def.task { server.value.Wire[Api].run( - s"http://localhost:12345/$outPath", - None + s"http://localhost:12345/$outPath" ).call() () } |