mirror of
https://github.com/fluencelabs/aqua.git
synced 2024-12-04 22:50:18 +00:00
parent
4696e95129
commit
6522deccb4
@ -0,0 +1,32 @@
|
||||
package aqua.backend.js
|
||||
|
||||
import aqua.model.AquaContext
|
||||
import aqua.model.transform.BodyConfig
|
||||
import cats.data.Chain
|
||||
|
||||
case class JavaScriptFile(context: AquaContext) {
|
||||
|
||||
def funcs: Chain[JavaScriptFunc] =
|
||||
Chain.fromSeq(context.funcs.values.toSeq).map(JavaScriptFunc(_))
|
||||
|
||||
def generateJS(conf: BodyConfig = BodyConfig()): String =
|
||||
JavaScriptFile.Header + "\n\n" + funcs.map(_.generateTypescript(conf)).toList.mkString("\n\n")
|
||||
}
|
||||
|
||||
object JavaScriptFile {
|
||||
|
||||
val Header: String =
|
||||
s"""/**
|
||||
| *
|
||||
| * This file is auto-generated. Do not edit manually: changes may be erased.
|
||||
| * Generated by Aqua compiler: https://github.com/fluencelabs/aqua/.
|
||||
| * If you find any bugs, please write an issue on GitHub: https://github.com/fluencelabs/aqua/issues
|
||||
| * Aqua version: ${Option(getClass.getPackage.getImplementationVersion)
|
||||
.filter(_.nonEmpty)
|
||||
.getOrElse("Unknown")}
|
||||
| *
|
||||
| */
|
||||
|import { RequestFlowBuilder } from '@fluencelabs/fluence/dist/api.unstable';
|
||||
|""".stripMargin
|
||||
|
||||
}
|
@ -0,0 +1,98 @@
|
||||
package aqua.backend.js
|
||||
|
||||
import aqua.backend.air.FuncAirGen
|
||||
import aqua.model.func.{ArgDef, FuncCallable}
|
||||
import aqua.model.transform.BodyConfig
|
||||
import aqua.types._
|
||||
import cats.syntax.functor._
|
||||
import cats.syntax.show._
|
||||
|
||||
case class JavaScriptFunc(func: FuncCallable) {
|
||||
|
||||
import JavaScriptFunc._
|
||||
|
||||
def argsJavaScript: String =
|
||||
func.args.args.map(ad => s"${ad.name}").mkString(", ")
|
||||
|
||||
def generateTypescript(conf: BodyConfig = BodyConfig()): String = {
|
||||
|
||||
val tsAir = FuncAirGen(func).generateClientAir(conf)
|
||||
|
||||
val returnCallback = func.ret.as {
|
||||
s"""h.onEvent('${conf.callbackService}', '${conf.respFuncName}', (args) => {
|
||||
| const [res] = args;
|
||||
| resolve(res);
|
||||
|});
|
||||
|""".stripMargin
|
||||
|
||||
}
|
||||
|
||||
val setCallbacks = func.args.args.map {
|
||||
case ArgDef.Data(argName, OptionType(_)) =>
|
||||
s"""h.on('${conf.getDataService}', '$argName', () => {return $argName === null ? [] : [$argName];});"""
|
||||
case ArgDef.Data(argName, _) =>
|
||||
s"""h.on('${conf.getDataService}', '$argName', () => {return $argName;});"""
|
||||
case ArgDef.Arrow(argName, at) =>
|
||||
val value = s"$argName(${argsCallToJs(
|
||||
at
|
||||
)})"
|
||||
val expr = at.res.fold(s"$value; return {}")(_ => s"return $value")
|
||||
s"""h.on('${conf.callbackService}', '$argName', (args) => {$expr;});"""
|
||||
}.mkString("\n")
|
||||
|
||||
val returnVal =
|
||||
func.ret.fold("Promise.race([promise, Promise.resolve()])")(_ => "promise")
|
||||
|
||||
s"""
|
||||
|export async function ${func.funcName}(client${if (func.args.isEmpty) ""
|
||||
else ", "}${argsJavaScript}) {
|
||||
| let request;
|
||||
| const promise = new Promise((resolve, reject) => {
|
||||
| request = new RequestFlowBuilder()
|
||||
| .disableInjections()
|
||||
| .withRawScript(
|
||||
| `
|
||||
|${tsAir.show}
|
||||
| `,
|
||||
| )
|
||||
| .configHandler((h) => {
|
||||
| ${conf.relayVarName.fold("") { r =>
|
||||
s"""h.on('${conf.getDataService}', '$r', () => {
|
||||
| return client.relayPeerId;
|
||||
| });""".stripMargin
|
||||
}}
|
||||
| $setCallbacks
|
||||
| ${returnCallback.getOrElse("")}
|
||||
| h.onEvent('${conf.errorHandlingService}', '${conf.errorFuncName}', (args) => {
|
||||
| // assuming error is the single argument
|
||||
| const [err] = args;
|
||||
| reject(err);
|
||||
| });
|
||||
| })
|
||||
| .handleScriptError(reject)
|
||||
| .handleTimeout(() => {
|
||||
| reject('Request timed out for ${func.funcName}');
|
||||
| })
|
||||
| .build();
|
||||
| });
|
||||
| await client.initiateFlow(request);
|
||||
| return ${returnVal};
|
||||
|}
|
||||
""".stripMargin
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
object JavaScriptFunc {
|
||||
|
||||
def argsToTs(at: ArrowType): String =
|
||||
at.args
|
||||
.zipWithIndex
|
||||
.map(_.swap)
|
||||
.map(kv => "arg" + kv._1)
|
||||
.mkString(", ")
|
||||
|
||||
def argsCallToJs(at: ArrowType): String =
|
||||
at.args.zipWithIndex.map(_._2).map(idx => s"args[$idx]").mkString(", ")
|
||||
|
||||
}
|
@ -55,7 +55,7 @@ lazy val cli = project
|
||||
"com.monovore" %% "decline-enumeratum" % declineEnumV
|
||||
)
|
||||
)
|
||||
.dependsOn(semantics, `backend-air`, `backend-ts`, linker)
|
||||
.dependsOn(semantics, `backend-air`, `backend-ts`, `backend-js`, linker)
|
||||
|
||||
lazy val types = project
|
||||
.settings(commons)
|
||||
@ -112,3 +112,8 @@ lazy val `backend-ts` = project
|
||||
.in(file("backend/ts"))
|
||||
.settings(commons: _*)
|
||||
.dependsOn(`backend-air`)
|
||||
|
||||
lazy val `backend-js` = project
|
||||
.in(file("backend/js"))
|
||||
.settings(commons: _*)
|
||||
.dependsOn(`backend-air`)
|
||||
|
@ -97,6 +97,12 @@ object AppOps {
|
||||
.map(_ => true)
|
||||
.withDefault(false)
|
||||
|
||||
val compileToJs: Opts[Boolean] =
|
||||
Opts
|
||||
.flag("js", "Generate .js file instead of typescript")
|
||||
.map(_ => true)
|
||||
.withDefault(false)
|
||||
|
||||
val noRelay: Opts[Boolean] =
|
||||
Opts
|
||||
.flag("no-relay", "Do not generate a pass through the relay node")
|
||||
|
@ -37,19 +37,20 @@ object AquaCli extends IOApp with LogSupport {
|
||||
importOpts,
|
||||
outputOpts,
|
||||
compileToAir,
|
||||
compileToJs,
|
||||
noRelay,
|
||||
noXorWrapper,
|
||||
wrapWithOption(helpOpt),
|
||||
wrapWithOption(versionOpt),
|
||||
logLevelOpt
|
||||
).mapN { case (input, imports, output, toAir, noRelay, noXor, h, v, logLevel) =>
|
||||
).mapN { case (input, imports, output, toAir, toJs, noRelay, noXor, h, v, logLevel) =>
|
||||
WLogger.setDefaultLogLevel(LogLevel.toLogLevel(logLevel))
|
||||
WLogger.setDefaultFormatter(CustomLogFormatter)
|
||||
|
||||
// if there is `--help` or `--version` flag - show help and version
|
||||
// otherwise continue program execution
|
||||
h.map(_ => helpAndExit) orElse v.map(_ => versionAndExit) getOrElse {
|
||||
val target = if (toAir) AquaCompiler.AirTarget else AquaCompiler.TypescriptTarget
|
||||
val target = if (toAir) AquaCompiler.AirTarget else if (toJs) AquaCompiler.JavaScriptTarget else AquaCompiler.TypescriptTarget
|
||||
val bc = {
|
||||
val bc = BodyConfig(wrapWithXor = !noXor)
|
||||
bc.copy(relayVarName = bc.relayVarName.filterNot(_ => noRelay))
|
||||
|
@ -2,6 +2,7 @@ package aqua
|
||||
|
||||
import aqua.backend.air.FuncAirGen
|
||||
import aqua.backend.ts.TypescriptFile
|
||||
import aqua.backend.js.JavaScriptFile
|
||||
import aqua.io.{AquaFileError, AquaFiles, FileModuleId, Unresolvable}
|
||||
import aqua.linker.Linker
|
||||
import aqua.model.AquaContext
|
||||
@ -24,6 +25,7 @@ import java.nio.file.Path
|
||||
object AquaCompiler extends LogSupport {
|
||||
sealed trait CompileTarget
|
||||
case object TypescriptTarget extends CompileTarget
|
||||
case object JavaScriptTarget extends CompileTarget
|
||||
case object AirTarget extends CompileTarget
|
||||
|
||||
case class Prepared(modFile: Path, srcPath: Path, targetPath: Path, context: AquaContext) {
|
||||
@ -152,6 +154,25 @@ object AquaCompiler extends LogSupport {
|
||||
|
||||
}
|
||||
|
||||
case JavaScriptTarget =>
|
||||
preps.map { p =>
|
||||
p.targetPath("js") match {
|
||||
case Invalid(t) =>
|
||||
EitherT.pure(t.getMessage)
|
||||
case Valid(tp) =>
|
||||
writeFile(tp, JavaScriptFile(p.context).generateJS(bodyConfig)).flatTap { _ =>
|
||||
EitherT.pure(
|
||||
Validated.catchNonFatal(
|
||||
info(
|
||||
s"Result ${tp.toAbsolutePath}: compilation OK (${p.context.funcs.size} functions)"
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// TODO add function name to AirTarget class
|
||||
case AirTarget =>
|
||||
preps
|
||||
|
Loading…
Reference in New Issue
Block a user