From e41cac788287e9e020665c7df8eb8715ff6d4e51 Mon Sep 17 00:00:00 2001 From: Joseph Savona Date: Thu, 27 Aug 2015 19:09:42 -0700 Subject: [PATCH] babel-relay-plugin: generalize warning about `nodes` fields --- scripts/babel-relay-plugin/package.json | 2 +- .../babel-relay-plugin/src/GraphQLPrinter.js | 49 ++++++++++++------- .../connectionWithNodesField.fixture | 19 +++++++ .../src/__tests__/testschema.rfc.graphql | 1 + .../src/__tests__/testschema.rfc.json | 16 ++++++ 5 files changed, 69 insertions(+), 18 deletions(-) create mode 100644 scripts/babel-relay-plugin/src/__fixtures__/connectionWithNodesField.fixture diff --git a/scripts/babel-relay-plugin/package.json b/scripts/babel-relay-plugin/package.json index 509bfd4b6a72a..27ec36fd54275 100644 --- a/scripts/babel-relay-plugin/package.json +++ b/scripts/babel-relay-plugin/package.json @@ -1,6 +1,6 @@ { "name": "babel-relay-plugin", - "version": "0.1.3", + "version": "0.2.0", "description": "Babel Relay Plugin for transpiling GraphQL queries for use with Relay.", "license": "BSD-3-Clause", "repository": "facebook/relay", diff --git a/scripts/babel-relay-plugin/src/GraphQLPrinter.js b/scripts/babel-relay-plugin/src/GraphQLPrinter.js index 58dbbf0963017..7ca97e8be6e70 100644 --- a/scripts/babel-relay-plugin/src/GraphQLPrinter.js +++ b/scripts/babel-relay-plugin/src/GraphQLPrinter.js @@ -376,7 +376,8 @@ function printField( metadata.pk = 'id'; } - if (isConnection(options.schema, fieldDecl)) { + var connectionMetadata = getConnectionMetadata(options.schema, fieldDecl); + if (connectionMetadata) { metadata.connection = true; if (!getArgNamed(fieldDecl, 'find')) { @@ -387,16 +388,23 @@ function printField( var selections = getSelections(field); selections.forEach(function(subfield) { var subfieldName = getName(subfield); - if (subfieldName === 'nodes') { + var subfieldDecl = + types.getNamedType(fieldDecl.type).getFields()[subfieldName]; + var subfieldType = types.getNamedType(subfieldDecl.type); + if (subfieldName === 'edges') { + hasEdgesSelection = true; + } else if ( + isList(subfieldDecl.type) && + subfieldType.name === connectionMetadata.nodeType.name + ) { + // Detect eg `nodes{...}` instead of `edges{node{...}}` throw new Error(util.format( - 'Unsupported "nodes" field on connection, `%s`. Instead, use ' + - '"edges{node{...}}".', + 'Unsupported `%s{...}` field on connection `%s`. Use ' + + '`edges{node{...}}` instead.', + subfieldName, fieldName )); } - if (subfieldName === 'edges') { - hasEdgesSelection = true; - } }); if (hasEdgesSelection && !!fieldDecl.type.getFields()['pageInfo']) { subRequisiteFields.pageInfo = true; @@ -647,46 +655,53 @@ function getArgNamed(field, name) { return remaining.length === 1 ? remaining[0] : null; } -function isConnection(schema, fieldDecl) { +function getConnectionMetadata(schema, fieldDecl) { if (!isConnectionType(fieldDecl.type)) { - return false; + return null; } // Connections must be limitable. if (!getArgNamed(fieldDecl, 'first') && !getArgNamed(fieldDecl, 'last')) { - return false; + return null; } var fieldType = types.getNamedType(fieldDecl.type); // Connections must have a non-scalar `edges` field. var edgesField = fieldType.getFields()['edges']; if (!edgesField) { - return false; + return null; } var edgesType = types.getNamedType(edgesField.type); if (edgesType instanceof types.GraphQLScalarType) { - return false; + return null; } // Connections' `edges` field must have a non-scalar `node` field. var edgesType = types.getNamedType(edgesField.type); var nodeField = edgesType.getFields()['node']; if (!nodeField) { - return false; + return null; } var nodeType = types.getNamedType(nodeField.type); if (nodeType instanceof types.GraphQLScalarType) { - return false; + return null; } // Connections' `edges` field must have a scalar `cursor` field. var cursorField = edgesType.getFields()['cursor']; if (!cursorField) { - return false; + return null; } var cursorType = types.getNamedType(cursorField.type); if (!(cursorType instanceof types.GraphQLScalarType)) { - return false; + return null; } - return true; + return { + cursorType, + cursorField, + edgesType, + edgesField, + nodeType, + nodeField, + }; } function trimArray(arr) { diff --git a/scripts/babel-relay-plugin/src/__fixtures__/connectionWithNodesField.fixture b/scripts/babel-relay-plugin/src/__fixtures__/connectionWithNodesField.fixture new file mode 100644 index 0000000000000..36f2a236fc785 --- /dev/null +++ b/scripts/babel-relay-plugin/src/__fixtures__/connectionWithNodesField.fixture @@ -0,0 +1,19 @@ +Input: +var Relay = require('react-relay'); +var x = Relay.QL` + query { + node(id: 123) { + friends(first: 3) { + nodes { + id + } + } + } + } +`; + +Output: +var Relay = require('react-relay'); +var x = (function () { + throw new Error('Encountered a GraphQL validation error processing file `connectionWithNodesField.fixture`. Check your terminal for details.'); +})(); \ No newline at end of file diff --git a/scripts/babel-relay-plugin/src/__tests__/testschema.rfc.graphql b/scripts/babel-relay-plugin/src/__tests__/testschema.rfc.graphql index f022ce55a38b9..58aebba93c106 100644 --- a/scripts/babel-relay-plugin/src/__tests__/testschema.rfc.graphql +++ b/scripts/babel-relay-plugin/src/__tests__/testschema.rfc.graphql @@ -89,6 +89,7 @@ type PendingPost { type UserConnection { count: Int edges: [UserConnectionEdge] + nodes: [User] pageInfo: PageInfo } diff --git a/scripts/babel-relay-plugin/src/__tests__/testschema.rfc.json b/scripts/babel-relay-plugin/src/__tests__/testschema.rfc.json index a35ba3615ee76..419e5cc6b78fd 100644 --- a/scripts/babel-relay-plugin/src/__tests__/testschema.rfc.json +++ b/scripts/babel-relay-plugin/src/__tests__/testschema.rfc.json @@ -508,6 +508,22 @@ "isDeprecated": false, "deprecationReason": null }, + { + "name": "nodes", + "description": null, + "args": [], + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "User", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, { "name": "pageInfo", "description": null,