There are three parts to this:
-
sbt-scala-relay
- Adds sbt tasks to run the relay-compiler and generate Scala.js facades (relayCompiler
andrelayConvert
).addSbtPlugin("com.goodcover.relay" % "sbt-scala-relay" % "<version>")
-
scala-relay-core
- Contains the Scala.js equivalent to relay types as well as the@graphql
annotation which allows GraphQL definitions to be defined inline.libraryDependencies += "com.goodcover.relay" %%% "scala-relay-core" % "<version>"
ℹ️ This is added automatically by
sbt-scala-relay
. -
scala-relay-macros
- Contains thegraphqlGen
macro which is similar to the@graphql
annotation but is able to return the type of the GraphQL definition. This also includes an IntelliJ plugin that makes the types available to the IDE.libraryDependencies += "com.goodcover.relay" %%% "scala-relay-macros" % "<version>"
ℹ️ This has to be added manually.
Add the sbt-scala-relay
sbt plugin to project/plugins.sbt
:
addSbtPlugin("com.goodcover.relay" % "sbt-scala-relay" % "<version>")
Enable the ScalaRelayPlugin
on your frontend project:
enablePlugins(ScalaRelayPlugin)
Add the path to the schema:
relaySchema := resourceDirectory.value / "graphql" / "schema.graphqls"
Optionally specify the command to run relay-compiler
and include a dependency on the task that installs it. For
example, if you are using scalajs-bundler add:
relayCompilerCommand := s"node ${(Compile / npmInstallDependencies).value}/node_modules/.bin/relay-compiler"
Add the relay dependencies to your node package manager. For scalajs-bundler
add:
Compile / npmDevDependencies ++= (Compile / relayDependencies).value
You will need to configure your bundler so that it is able to resolve @JSImport("__generated__/...")
to the
relayCompileDirectory
. For example, for webpack you might add:
"resolve": {
"modules": [
"node_modules",
"../../resource_managed/main"
]
}
The first task is relayExtract
which processes all the @graphql
annotations and graphqlGen
macros within your
Scala sources (unmanagedSources
) and extracts the GraphQL definitions into their own .graphql
file.
The second task is relayWrap
which takes all the extracted .graphql
files as well as any others from
unmanagedResources
and wraps them with the graphql
tagged template and outputs them as
.js
files (or .ts
if relayTypeScript := true
). We do this because relay-compiler
is rather fussy about where the
executable definitions come from. The only reliable way to get it to work is to have all your executable definitions
come from the same type of file as the target language.
The final two tasks, relayConvert
and relayCompile
, are the main tasks you will interact with and they each depend
on the output of tasks above, relayExtract
and relayWrap
, respectively.
relayConvert
takes all the GraphQL definitions and generates Scala.js facades for them. These are equivalent to the
TypeScript or Flow types that relay-compiler
produces.
relayCompile
takes all the GraphQL definitions wrapped with the graphql
tagged template and passes those to
relay-compiler
. The resulting sources are imported via @JSImport("__generated__/...")
annotations from the Scala.js
facades generated by relayConvert
.
For an example on multi-project setup, with GraphQL definitions from one project used by another, take a look at the multi-project sbt scripted test.
You need to make relayConvert
aware of the GraphQL definition from the dependency by setting:
Compile / relayGraphQLDependencies ++= (dependencyProject / Compile / relayGraphQLFiles).value
ℹ️ This will add a task dependency on
dependencyProject / Compile / relayExtract
.
Then make relayCompile
aware of the unmanaged GraphQL definitions:
Compile / relayInclude ++= {
val base = relayBaseDirectory.value.toPath
(dependencyProject / Compile / unmanagedResourceDirectories).value.map { dir =>
base.relativize(dir.toPath).toString + "/**"
}
}
As well as the extracted ones:
Compile / relayInclude +=
relayBaseDirectory.value.toPath.relativize((dependencyProject / Compile / relayExtractDirectory).value.toPath).toString + "/**"
This does not add a task dependency on dependencyProject / Compile / relayExtract
. Although technically only
relayExtra
is required, it is more convenient to depend on relayCompile
since you will need to run it at some point
before bundling the dependent project:
Compile / relayCompile := ((Compile / relayCompile) dependsOn (dependencyProject / Compile / relayCompile)).value
Do not forget to update your bundler configuration to also include the relayCompileDirectory
of the dependency:
"resolve": {
"modules": [
"node_modules",
"../../resource_managed/main",
"../../../../../../dependencyProject/target/scala-2.13/resource_managed/main",
]
}
ℹ️ You should probably use path.resolve
against the base directory but that is an exercise for you to figure out.