
# scala-relay-compiler

The purpose of this project is to generate Scala.js bindings for the
`relay-compiler`.  Typically the `relay-compiler` generates `flow` bindings
along with the compiled queries.  This project replaces that generation and
outputs `js.native` traits instead.  It is **experimental** and the code needs work,
but it can generated traits for deep hierarchical graphs.

It uses flow because it's what relay uses, and its better than raw javascript.

It uses `outputDir` to generate all the source files and whatnot in the same package.
So its a big flat package repository in the same directory.
Typically `(resourceManaged in Compile).value / "relay-compiler-out"` is where it outputs
in sbt parlance.

### Versions
- `0.11.0` - Relay `1.6.2`
- `0.9.0` - Relay `1.5.0`
- `0.8.2` - Relay `1.4.0`

## Example

```sh
$ ./bin/scala-relay-compiler.js --src example/src/ --schema example/schema.graphql --out example/out/
```

## Features
 - Handles names elegantly by scoping them to the companion object.
 - Handles first layer of spreading, right now we spit out `js.|` to
   join disjoint fields, even though in fact they are not disjoint,
   they are a union, however, this requires a fix later down the line.
 - `@scalajs(extends: String)` This can give you a parent class to mixin.  It's
   your job to verify it.
 - `@scalajs(useNulls: Boolean)` this can give you finer control on using `A | Null`
   on a Fragment, field or inline fragment.


## Example

Ill walk you through a simple example.  I'll assume you know roughly how [Relay Modern](https://github.com/facebook/relay/) works.

We'll obviously need a schema, for which we'll use the example provided.

```gql
schema {
  query: Root
}

type Root {
  dictionary: [Word]
}

type Word {
  id: String!
  definition: WordDefinition
}

type WordDefinition {
  id: String
  text: String
  image: String
}
```

From this we'll want to generate some queries in which Ill list one top level query including
two fragments.  Now this is where we start to diverge from the stock `relay-compiler`.  Our method
of collecting queries/fragments/mutations is a regex (I know, ugly) through `*.scala` files.  We look for the `@gql("""` indicator and its corresponding `""")` to isolate the queries.

Here is an example taken straight from the repository.

```scala
object Foo {

  val otherGql = @gql("""
  fragment DictionaryComponent_definition on WordDefinition {
    text
    image
  }
  """)

  val gql = @gql("""
  fragment DictionaryComponent_word on Word {
    id
    definition {
      ...DictionaryComponent_definition
      text
      id
    }
  }
  """)

  val query = @gql("""
  query DictionaryQuery {
    dictionary {
      ...DictionaryComponent_word
    }
  }
  """)
}
```

So now we have the schema, and a query + two fragments.  Time to generate!  So we run the following command.

```sh
$ ./bin/scala-relay-compiler.js --src example/src/ --schema example/schema.graphql --out example/out/
```

And get three files. Queries and Mutations or top level components all have the same
structure.  The object represents the top level query, and the trait represents the data coming over the wire.

This basically replaces the js files that typically get generated by the compiler.  Specifically the
part the runtime needs is in `DictionaryQuery.query`

So the generation below handles `DictionaryQuery`.  I cut some text for brevity

```scala
trait DictionaryQuery extends js.Object {
  /** Combined the fields on a spread: DictionaryComponent_word */
   val dictionary : js.Array[DictionaryComponent_word]
}


object DictionaryQuery extends _root_.relay.graphql.GenericGraphQLTaggedNode {
  val query: _root_.relay.graphql.ConcreteBatch = _root_.scala.scalajs.js.JSON.parse("""{
  "fragment": {
    ....
    "text": "query DictionaryQuery {\n  dictionary {\n    ...DictionaryComponent_word\n  }\n}\n\nfragment DictionaryComponent_word on Word {\n  id\n  definition {\n    ...DictionaryComponent_definition\n    text\n    id\n  }\n}\n\nfragment DictionaryComponent_definition on WordDefinition {\n  text\n  image\n}\n"
}""").asInstanceOf[_root_.relay.graphql.ConcreteBatch]

```

What does fragments look like you ask?  You'll notice some comments, its basically a way to look
up what happened in the code to make sure the generator didn't do something wonky.

As you can see the top level definition lives at the root package `relay.generated` And all the sub
traits get generated underneath the trait's companion object.

```scala
package relay.generated

trait DictionaryComponent_word extends js.Object {
  /** New fields added, conflicts detected. */
   val definition : DictionaryComponent_word.Definition
   val id : String
}

object DictionaryComponent_word extends _root_.relay.graphql.GenericGraphQLTaggedNode {

  trait Definition extends js.Object {
    val id : String
    /** getDirectMembersForFrag child of DictionaryComponent_definition Combining fields, with or? "true"  */
    val text : String
    /** getDirectMembersForFrag child of DictionaryComponent_definition */
    val image : String
  }

  val query: _root_.relay.graphql.ConcreteFragment = _root_.scala.scalajs.js.JSON.parse("""{
    ....
    """)
}
```

Ill skip the other definition because there's nothing additionally interesting.

**So that's it.**

You can use from sbt by using, and it will include the tiny shim of javascript traits to use.

```sbt
addSbtPlugin("com.dispalt.relay" % "sbt-relay-compiler" % "<version>")
```

## TODO

A list of tasks/ideas the community could help with. `High | Med | Low` refers to the complexity/difficulty.

 - [x] Med: Fix `InlineFragments` so they work properly.  Right now we just really
 don't handle them.
 - [ ] Big: Fix spreading so it goes recursively?  Right now spreading is
 difficult because the types that are mixed in couple potentially conflict
   - [ ] Med: Make it work recursively
   - [ ] Med: Generate the necessary class depth.
   - [ ] Big: How to handle traversing since the order of the frags are unable to
    to be changed.
 - [ ] Med: Handle connections and edges with a superclass.
 - [ ] High: Handle exotic features of graphql.
   - [ ] Low: Handle conditionals.
   - [ ] Low: Figure out what is not working.
 - [ ] High: Does recursion work?
 - [ ] Low: handle indents in generated code better
 - [ ]
