Relay: how to use new babel-plugin-relay to transform `graphql` queries ?

Created on 18 Apr 2017  路  19Comments  路  Source: facebook/relay

I am using relay in my scala.js project , in old version of babel-relay-plugin

      // `babel-relay-plugin` returns a function for creating plugin instances
      var getBabelRelayPlugin = require('babel-plugin-relay');
      var babel = require('babel-core')

      // load previously saved schema data (see "Schema JSON" below)
      var schemaData = require(process.argv[2]);

      // create a plugin instance
      var plugin = getBabelRelayPlugin(schemaData.data);

      process.stdin.resume();
      process.stdin.setEncoding('utf8');
      var data = "";

      process.stdin.on('data', function(chunk) {
          data += chunk;
      });

      process.stdin.on('end', function() {
          var result = babel.transform(data, {
            plugins: [plugin],
            ast: false,
          });
          process.stdout.write(result.code);
      });

but with new plugin i am getting following error :

[error] --------Babel Output: /private/var/folders/n0/c0fyqlqx0gg15mv4t5mchgj80000gn/T/relayscratch/node_modules/babel-core/lib/transformation/file/index.js:614
[error]       throw err;
[error]       ^
[error] 
[error] TypeError: unknown: Cannot read property 'functionExpression' of undefined
[error]     at createModernNode (/private/var/folders/n0/c0fyqlqx0gg15mv4t5mchgj80000gn/T/relayscratch/node_modules/babel-plugin-relay/lib/createModernNode.js:23:11)
[error]     at createAST (/private/var/folders/n0/c0fyqlqx0gg15mv4t5mchgj80000gn/T/relayscratch/node_modules/babel-plugin-relay/lib/compileGraphQLTag.js:84:51)
[error]     at compileGraphQLTag (/private/var/folders/n0/c0fyqlqx0gg15mv4t5mchgj80000gn/T/relayscratch/node_modules/babel-plugin-relay/lib/compileGraphQLTag.js:74:37)
[error]     at PluginPass.TaggedTemplateExpression (/private/var/folders/n0/c0fyqlqx0gg15mv4t5mchgj80000gn/T/relayscratch/node_modules/babel-plugin-relay/lib/BabelPluginRelay.js:41:43)
[error]     at newFn (/private/var/folders/n0/c0fyqlqx0gg15mv4t5mchgj80000gn/T/relayscratch/node_modules/babel-traverse/lib/visitors.js:276:21)
[error]     at NodePath._call (/private/var/folders/n0/c0fyqlqx0gg15mv4t5mchgj80000gn/T/relayscratch/node_modules/babel-traverse/lib/path/context.js:76:18)
[error]     at NodePath.call (/private/var/folders/n0/c0fyqlqx0gg15mv4t5mchgj80000gn/T/relayscratch/node_modules/babel-traverse/lib/path/context.js:48:17)
[error]     at NodePath.visit (/private/var/folders/n0/c0fyqlqx0gg15mv4t5mchgj80000gn/T/relayscratch/node_modules/babel-traverse/lib/path/context.js:105:12)
[error]     at TraversalContext.visitQueue (/private/var/folders/n0/c0fyqlqx0gg15mv4t5mchgj80000gn/T/relayscratch/node_modules/babel-traverse/lib/context.js:150:16)
[error]     at TraversalContext.visitSingle (/private/var/folders/n0/c0fyqlqx0gg15mv4t5mchgj80000gn/T/relayscratch/node_modules/babel-traverse/lib/context.js:108:19)

in this particular case

data = graphql`query hello {
             viewer {
               allEmployees {
                 edges {
                   node {
                     id
                     name
                     age
                   }
                 }
               }
             }
           }`

All 19 comments

If you have to use babel.transform and not .babelrc, you can use this test to help you out:

https://github.com/facebook/relay/blob/6abc70a258d98e1653f9d26671e540b68cbb9359/packages/babel-plugin-relay/__tests__/BabelPluginRelay-test.js#L46

SCHEMA_PATH corresponds with a schema.graphql file that you generate. You can generate schema.graphql like so

import { printSchema } from 'graphql/utilities';
import schema from '../graphql/schema'; // schema.js file that exports a GraphQLSchema class from 'graphql' package

const graphQLFile = path.join(__dirname, 'schema.graphql')
fs.writeFileSync(graphQLFile, printSchema(schema));

You'll also need to use relay-compiler to generate the .graphql files for you for each tagged graphql string literal in your app.

npm install -g relay-compiler@dev
relay-compiler --src ${SOURCE_PATH} --schema ${SCHEMA_PATH}

Regarding your second point, does the relay-compiler only need the schema plus the independent fragment to do its job? I would assume it needs a view of all fragments (aka dependent fragments), correct?

Is there an example of writing it inline, we don't have a lot of flexibility in scalajs, so any help would be excellent.

So the relay-compiler needs to know the schema, and the --src needs to be a directory containing all of the related graphql tags: it will parse each tag independently, but when transforming queries or mutations, it needs to have access to the fragment definitions. If you're only running it on an individual fragment file, it'll work, for now (though you'll end up deleting any other files that were already put into your colocated __generated__ directory, because none of the existing fragment transforms depend on knowledge about other fragments. This behavior (where you can run the compiler on individual fragment definitions) isn't guaranteed to continue to work, though.

So Ill explain briefly how we did this for previous relay-classic. We basically created a scala macro that would run the snippet @chandu0101 included above and replaced it inline with a string then evaled that would then work. So we're trying to figure out how to do that in relay modern.

I know this isn't core to getting this adopted for you guys, but the old integration was smooth and I'd love for it to continue to be a smooth experience.

Some thoughts, feel free to correct them:

  • It seems the "organization" of the files is assumed by the compiler.
  • Babel now does very little, in the modern case, it looks like it just drops a require in.
  • Since the compiler does so much now and it operates on files, is there a better way to make it able to work on stdin or is too much baked into the on disk structure that it'd be too difficult

Please check out http://facebook.github.io/relay/docs/babel-plugin-relay.html which explains how to configure the plugin

@dispalt - you could probably configure loading files from a different source than the file system with a small amount of effort. The file reading and writing is pretty well separated from the rest of the compiler. If you need to make changes to make this possible or export the right things, send a PR

Right now the compiler assumes that generated files end up in ./__generated__/ relative to the source file to create the right require() statement, however we would be happy to consider PRs that made this configurable.

Actually I'd just prefer to use it more like a library, so, for instance, calling RelayFlowParser directly perhaps? Maybe calling this, https://github.com/facebook/relay/blob/38c5cb954c36af01614c93b250bb761f858bd70b/packages/relay-compiler/codegen/flowtype/RelayFlowParser.js#L121

Obviously this isn't exposed but would something like that give me snippets that I could use to feed the new core?

The object you import at runtime (for us, written as .graphql.js files in __generated__ directories) needs to conform to the GeneratedNode type: https://github.com/facebook/relay/blob/master/packages/relay-compiler/codegen/writeRelayGeneratedFile.js#L24. You could even hand-write these if you wanted to skip the compiler process entirely (I do not recommend this, even our tests mostly generate these automatically).

The Root | Fragment type the RelayFlowParser produces is an intermediate type: it's used in the compilation process, but would be nonsense if fed to the runtime (at least, as it stands). You could probably modify the plugin to take an IR instead of a GeneratedNode, but again, I really don't recommend that strategy.

Makes sense, that's what I am starting to figure out, so it looks like this the complete runtime result then? https://github.com/facebook/relay/blob/cee1f364a22d394f04548fa1634b2147a2787a1d/packages/relay-compiler/codegen/RelayFileWriter.js#L165

@dispalt The RelayFileWriter is where most of the heavy lifting (in terms of converting a parsed AST into the generated nodes) actually happens. It's also (for now) where we decide to write files into __generated__ directories. We probably could do with pulling it apart a little, to allow better configuration from RelayBin of where files actually go. Until recently, we generated all files into a single "library" directory, but we've found keeping generated files colocated to the file that causes them to be generated reduces the mental overhead of working with them.

If you want to re-enable the library output, feel free to make a pull request to add it as an option.

We actually already have the outputDir as an option in the RelayFileWriter: https://github.com/facebook/relay/blob/cee1f364a22d394f04548fa1634b2147a2787a1d/packages/relay-compiler/codegen/RelayFileWriter.js#L47

Pass that argument in, and you'll output your generated files into a single library (with no sub-directories). I'm not yet sure what our medium-term plans are with this (there's a lot of iteration we'd like to do on the compiler), so if you rely on this internal API there's a good chance a new release will break your compiler's compatibility. But allowing library generation instead of colocated generation seems, on the surface, to be a reasonable configuration option to continue to support. But there's a lot of hidden complexity in how we support this, so the argument you pass in to make this work is likely going to change.

I think it should, yes. There's no reason why you couldn't use relay-compiler alone without the rest of relay.

Here's some really rough (emphasis on rough) on what I am trying to do so I can effectively use it from scala.

https://github.com/dispalt/relay-modern-helper

Basically you can feed runner.js a graphql fragment and it will spit out what runtime needs.

echo fragment.gql|node runner.js

Would this get me in trouble? I copied and altered the relay-compiler script.

I would also like to know if the above approach is reasonable for a non-JS project.

I want to use Relay Modern with ClojureScript, which, being JVM-based, likely has similar challenges as Scala.js. Ideally, I'd like to use RelayCompiler as a library, completely divorced from the filesystem and Node. With a UMD package that provides a function like RelayCompiler.compile(schema, listOfGraphQLStrings) => {graphQLString: compiledOutput}, would we JVM users be able to compile our projects in Nashorn instead of Node and, if so, what changes would be necessary to accomplish this?

@leebyron I am having similar issue: https://github.com/facebook/relay/issues/1753#issuecomment-308069649

solved it by changing this line:

plugins: ['transform-class-properties', __dirname + '/src/babelRelayPlugin']

to this:

plugins: ['relay', 'transform-class-properties', __dirname + '/src/babelRelayPlugin']

will see if that will cause me any problems in the future

Yeah what @DomKM proposed would be nicer than the current way I am doing it, but I did solve my problem here, basically providing a shim around the input since it's not parsing javascript, and using the output similar to how it works today. It would be nice to not depend on watchman though, since we aren't using the watching semantics and its a tougher cross-compiled dependency.

Thanks for the input here. We're currently going through old issues that appear to have gone stale (ie. not updated in about the last 3 months) because the volume of material in the issue tracker is becoming hard to manage. If this is still important to you please comment and we'll re-open this.

Thanks once again!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

janicduplessis picture janicduplessis  路  3Comments

rayronvictor picture rayronvictor  路  3Comments

johntran picture johntran  路  3Comments

leebyron picture leebyron  路  3Comments

bondanherumurti picture bondanherumurti  路  3Comments