├── .babelrc.js ├── .eslintrc.js ├── .github ├── FUNDING.yml └── workflows │ └── build.yaml ├── .gitignore ├── .npmignore ├── .prettierrc ├── LICENSE ├── README.md ├── __tests__ ├── ReactRelayFragmentContainer-test.tsx ├── ReactRelayPaginationContainer-test.tsx ├── ReactRelayQueryRenderer-test.tsx ├── ReactRelayRefetchContainer-test.tsx ├── RelayHooks-test.tsx ├── __generated__ │ └── .gitignore ├── enqueue.ts ├── internalAct.ts ├── testschema.graphql ├── useFragment-test.tsx ├── useFragmentNode-test.tsx ├── useFragmentNodeRequired-test.tsx ├── useLazyLoadQueryNode-test.tsx ├── useMutation-test.tsx ├── usePaginationFragment-test.tsx ├── usePreloadQuery-test.tsx ├── useRefetchable-test.tsx ├── useRefetchableFragment-test.tsx └── useSubscription-test.tsx ├── docs ├── RelayHooks-Introduction.md ├── useFragment.md ├── useMutation.md ├── usePagination.md ├── usePreloadedQuery.md ├── useRefetchable.md └── useSubscription.md ├── examples ├── relay-hook-example │ ├── cra │ │ ├── .gitignore │ │ ├── README.md │ │ ├── __generated__ │ │ │ └── relay │ │ │ │ ├── Entries_entries.graphql.js │ │ │ │ ├── QueryAppQuery.graphql.js │ │ │ │ └── createEntryMutation.graphql.js │ │ ├── package.json │ │ ├── public │ │ │ ├── favicon.ico │ │ │ ├── index.html │ │ │ ├── logo192.png │ │ │ ├── logo512.png │ │ │ ├── manifest.json │ │ │ └── robots.txt │ │ ├── schema.gql │ │ ├── server │ │ │ └── server.js │ │ └── src │ │ │ ├── app.js │ │ │ ├── components │ │ │ ├── Entries.js │ │ │ └── __generated__ │ │ │ │ └── Entries_entries.graphql.js │ │ │ ├── index.css │ │ │ ├── index.js │ │ │ ├── logo.svg │ │ │ ├── mutations │ │ │ ├── __generated__ │ │ │ │ └── createEntryMutation.graphql.js │ │ │ └── create.js │ │ │ ├── query │ │ │ ├── QueryApp.js │ │ │ └── __generated__ │ │ │ │ └── QueryAppQuery.graphql.js │ │ │ ├── serviceWorker.js │ │ │ └── setupTests.js │ ├── nextjs-ssr-preload │ │ ├── .babelrc │ │ ├── .eslintignore │ │ ├── .eslintrc.js │ │ ├── .gitignore │ │ ├── .watchmanconfig │ │ ├── README.md │ │ ├── components │ │ │ ├── Header.tsx │ │ │ ├── Home.tsx │ │ │ ├── Todo.tsx │ │ │ ├── TodoApp.tsx │ │ │ ├── TodoList.tsx │ │ │ ├── TodoListFooter.tsx │ │ │ └── TodoTextInput.tsx │ │ ├── data │ │ │ ├── database.js │ │ │ ├── schema.graphql │ │ │ └── schema │ │ │ │ ├── index.js │ │ │ │ ├── mutations │ │ │ │ ├── AddTodoMutation.js │ │ │ │ ├── ChangeTodoStatusMutation.js │ │ │ │ ├── MarkAllTodosMutation.js │ │ │ │ ├── RemoveCompletedTodosMutation.js │ │ │ │ ├── RemoveTodoMutation.js │ │ │ │ └── RenameTodoMutation.js │ │ │ │ ├── nodes.js │ │ │ │ └── queries │ │ │ │ └── UserQuery.js │ │ ├── html.js │ │ ├── mutations │ │ │ ├── AddTodoMutation.ts │ │ │ ├── ChangeTodoStatusMutation.ts │ │ │ ├── MarkAllTodosMutation.ts │ │ │ ├── RemoveCompletedTodosMutation.ts │ │ │ ├── RemoveTodoMutation.ts │ │ │ └── RenameTodoMutation.ts │ │ ├── next-env.d.ts │ │ ├── next.config.js │ │ ├── now.json │ │ ├── package.json │ │ ├── pages │ │ │ ├── _app.tsx │ │ │ ├── _document.tsx │ │ │ ├── index.tsx │ │ │ └── you.tsx │ │ ├── relay │ │ │ └── createRelayEnvironment.ts │ │ ├── scripts │ │ │ └── updateSchema.js │ │ ├── server.js │ │ ├── tsconfig.json │ │ └── types │ │ │ ├── react-relay_types.js │ │ │ └── relay-runtime_types.js │ ├── pagination-nextjs-ssr │ │ ├── .babelrc │ │ ├── .eslintrc.js │ │ ├── .gitignore │ │ ├── .prettierrc │ │ ├── .watchmanconfig │ │ ├── README.md │ │ ├── data │ │ │ ├── database.js │ │ │ ├── schema.graphql │ │ │ └── schema │ │ │ │ ├── index.js │ │ │ │ ├── mutations │ │ │ │ ├── AddTodoMutation.js │ │ │ │ ├── ChangeTodoStatusMutation.js │ │ │ │ ├── MarkAllTodosMutation.js │ │ │ │ ├── RemoveCompletedTodosMutation.js │ │ │ │ ├── RemoveTodoMutation.js │ │ │ │ └── RenameTodoMutation.js │ │ │ │ ├── nodes.js │ │ │ │ └── queries │ │ │ │ └── UserQuery.js │ │ ├── html.js │ │ ├── next-env.d.ts │ │ ├── next.config.js │ │ ├── now.json │ │ ├── package.json │ │ ├── scripts │ │ │ └── updateSchema.js │ │ ├── server.js │ │ ├── src │ │ │ ├── components │ │ │ │ ├── Header.tsx │ │ │ │ ├── LinearProgress.ts │ │ │ │ ├── RootPage.tsx │ │ │ │ ├── Select.tsx │ │ │ │ ├── TablePagination.tsx │ │ │ │ ├── Todo.tsx │ │ │ │ ├── TodoApp.tsx │ │ │ │ ├── TodoList.tsx │ │ │ │ └── TodoTextInput.tsx │ │ │ ├── mutations │ │ │ │ ├── AddTodoMutation.ts │ │ │ │ ├── ChangeTodoStatusMutation.ts │ │ │ │ ├── MarkAllTodosMutation.ts │ │ │ │ ├── ReadInlineUser.ts │ │ │ │ ├── RemoveTodoMutation.ts │ │ │ │ └── RenameTodoMutation.ts │ │ │ ├── pages │ │ │ │ ├── _app.tsx │ │ │ │ ├── _document.tsx │ │ │ │ ├── index.tsx │ │ │ │ ├── paginated.tsx │ │ │ │ └── scroll.tsx │ │ │ └── relay │ │ │ │ ├── connection.ts │ │ │ │ ├── createRelayEnvironment.ts │ │ │ │ ├── index.ts │ │ │ │ └── withData.tsx │ │ ├── tsconfig.json │ │ └── types │ │ │ ├── react-relay_types.js │ │ │ └── relay-runtime_types.js │ └── todo │ │ ├── .babelrc │ │ ├── .eslintignore │ │ ├── .eslintrc.js │ │ ├── .flowconfig │ │ ├── .gitignore │ │ ├── .watchmanconfig │ │ ├── README.md │ │ ├── data │ │ ├── database.js │ │ ├── schema.graphql │ │ └── schema │ │ │ ├── index.js │ │ │ ├── mutations │ │ │ ├── AddTodoMutation.js │ │ │ ├── ChangeTodoStatusMutation.js │ │ │ ├── MarkAllTodosMutation.js │ │ │ ├── RemoveCompletedTodosMutation.js │ │ │ ├── RemoveTodoMutation.js │ │ │ └── RenameTodoMutation.js │ │ │ ├── nodes.js │ │ │ └── queries │ │ │ └── UserQuery.js │ │ ├── flow-typed │ │ └── npm │ │ │ ├── @babel │ │ │ ├── cli_vx.x.x.js │ │ │ ├── core_vx.x.x.js │ │ │ ├── node_vx.x.x.js │ │ │ ├── plugin-proposal-class-properties_vx.x.x.js │ │ │ ├── plugin-transform-runtime_vx.x.x.js │ │ │ ├── preset-env_vx.x.x.js │ │ │ ├── preset-flow_vx.x.x.js │ │ │ ├── preset-react_vx.x.x.js │ │ │ └── runtime_vx.x.x.js │ │ │ ├── babel-eslint_vx.x.x.js │ │ │ ├── babel-loader_vx.x.x.js │ │ │ ├── babel-plugin-relay_vx.x.x.js │ │ │ ├── classnames_v2.x.x.js │ │ │ ├── eslint-config-fbjs_vx.x.x.js │ │ │ ├── eslint-config-prettier_vx.x.x.js │ │ │ ├── eslint-plugin-babel_vx.x.x.js │ │ │ ├── eslint-plugin-flowtype_vx.x.x.js │ │ │ ├── eslint-plugin-jsx-a11y_vx.x.x.js │ │ │ ├── eslint-plugin-prettier_vx.x.x.js │ │ │ ├── eslint-plugin-react_vx.x.x.js │ │ │ ├── eslint-plugin-relay_vx.x.x.js │ │ │ ├── eslint_vx.x.x.js │ │ │ ├── express_v4.16.x.js │ │ │ ├── flow-bin_v0.x.x.js │ │ │ ├── flow-typed_vx.x.x.js │ │ │ ├── prettier_v1.x.x.js │ │ │ ├── prop-types_v15.x.x.js │ │ │ ├── react-relay_vx.x.x.js │ │ │ ├── relay-compiler_vx.x.x.js │ │ │ ├── todomvc-app-css_vx.x.x.js │ │ │ ├── todomvc-common_vx.x.x.js │ │ │ ├── webpack-dev-server_vx.x.x.js │ │ │ └── webpack_v4.x.x.js │ │ ├── html.js │ │ ├── js │ │ ├── app.js │ │ ├── components │ │ │ ├── Todo.js │ │ │ ├── TodoApp.js │ │ │ ├── TodoList.js │ │ │ ├── TodoListFooter.js │ │ │ └── TodoTextInput.js │ │ ├── index.js │ │ ├── mutations │ │ │ ├── AddTodoMutation.js │ │ │ ├── ChangeTodoStatusMutation.js │ │ │ ├── MarkAllTodosMutation.js │ │ │ ├── RemoveCompletedTodosMutation.js │ │ │ ├── RemoveTodoMutation.js │ │ │ └── RenameTodoMutation.js │ │ └── query │ │ │ └── QueryApp.js │ │ ├── package.json │ │ ├── public │ │ ├── base.css │ │ ├── index.css │ │ ├── index.html │ │ └── learn.json │ │ ├── scripts │ │ └── updateSchema.js │ │ ├── server.js │ │ └── types │ │ ├── react-relay_types.js │ │ └── relay-runtime_types.js └── suspense │ ├── nextjs-ssr-preload │ ├── .babelrc │ ├── .eslintignore │ ├── .eslintrc.js │ ├── .gitignore │ ├── .watchmanconfig │ ├── README.md │ ├── components │ │ ├── Header.tsx │ │ ├── Home.tsx │ │ ├── Todo.tsx │ │ ├── TodoApp.tsx │ │ ├── TodoList.tsx │ │ ├── TodoListFooter.tsx │ │ └── TodoTextInput.tsx │ ├── data │ │ ├── database.js │ │ ├── schema.graphql │ │ └── schema │ │ │ ├── index.js │ │ │ ├── mutations │ │ │ ├── AddTodoMutation.js │ │ │ ├── ChangeTodoStatusMutation.js │ │ │ ├── MarkAllTodosMutation.js │ │ │ ├── RemoveCompletedTodosMutation.js │ │ │ ├── RemoveTodoMutation.js │ │ │ └── RenameTodoMutation.js │ │ │ ├── nodes.js │ │ │ └── queries │ │ │ └── UserQuery.js │ ├── html.js │ ├── mutations │ │ ├── AddTodoMutation.ts │ │ ├── ChangeTodoStatusMutation.ts │ │ ├── MarkAllTodosMutation.ts │ │ ├── RemoveCompletedTodosMutation.ts │ │ ├── RemoveTodoMutation.ts │ │ └── RenameTodoMutation.ts │ ├── next-env.d.ts │ ├── next.config.js │ ├── now.json │ ├── package.json │ ├── pages │ │ ├── _app.tsx │ │ ├── _document.tsx │ │ ├── index.tsx │ │ └── you.tsx │ ├── relay │ │ └── createRelayEnvironment.ts │ ├── scripts │ │ └── updateSchema.js │ ├── server.js │ ├── tsconfig.json │ └── types │ │ ├── react-relay_types.js │ │ └── relay-runtime_types.js │ └── nextjs-ssr │ ├── .babelrc │ ├── .eslintignore │ ├── .eslintrc.js │ ├── .gitignore │ ├── .watchmanconfig │ ├── README.md │ ├── components │ ├── Header.tsx │ ├── Todo.tsx │ ├── TodoApp.tsx │ ├── TodoList.tsx │ ├── TodoListFooter.tsx │ └── TodoTextInput.tsx │ ├── data │ ├── database.js │ ├── schema.graphql │ └── schema │ │ ├── index.js │ │ ├── mutations │ │ ├── AddTodoMutation.js │ │ ├── ChangeTodoStatusMutation.js │ │ ├── MarkAllTodosMutation.js │ │ ├── RemoveCompletedTodosMutation.js │ │ ├── RemoveTodoMutation.js │ │ └── RenameTodoMutation.js │ │ ├── nodes.js │ │ └── queries │ │ └── UserQuery.js │ ├── html.js │ ├── mutations │ ├── AddTodoMutation.ts │ ├── ChangeTodoStatusMutation.ts │ ├── MarkAllTodosMutation.ts │ ├── RemoveCompletedTodosMutation.ts │ ├── RemoveTodoMutation.ts │ └── RenameTodoMutation.ts │ ├── next-env.d.ts │ ├── next.config.js │ ├── now.json │ ├── package.json │ ├── pages │ ├── _app.tsx │ ├── _document.tsx │ ├── index.tsx │ └── you.tsx │ ├── public │ └── favicon.ico │ ├── relay │ ├── createRelayEnvironment.ts │ ├── index.js │ └── withData.tsx │ ├── scripts │ └── updateSchema.js │ ├── server.js │ ├── tsconfig.json │ └── types │ ├── react-relay_types.js │ └── relay-runtime_types.js ├── jest.config.js ├── package-lock.json ├── package.json ├── replace.config.js ├── rollup.config.js ├── scripts └── setup.ts ├── src ├── FetchResolver.ts ├── FragmentResolver.ts ├── QueryFetcher.ts ├── ReactRelayContext.ts ├── RelayEnvironmentProvider.tsx ├── RelayHooksTypes.ts ├── Utils.ts ├── getConnectionState.ts ├── index.ts ├── loadQuery.ts ├── useForceUpdate.ts ├── useFragment.tsx ├── useMutation.ts ├── useOssFragment.tsx ├── usePagination.ts ├── usePreloadedQuery.ts ├── useQuery.ts ├── useRefetchable.ts ├── useRelayEnvironment.ts └── useSubscription.ts ├── tsconfig.esm.json ├── tsconfig.json └── website ├── .gitignore ├── core └── Footer.js ├── i18n └── en.json ├── package.json ├── sidebars.json ├── siteConfig.js ├── static ├── css │ └── custom.css ├── img │ ├── favicon.ico │ ├── oss_logo.png │ ├── undraw_code_review.svg │ ├── undraw_monitor.svg │ ├── undraw_note_list.svg │ ├── undraw_online.svg │ ├── undraw_open_source.svg │ ├── undraw_operating_system.svg │ ├── undraw_react.svg │ ├── undraw_tweetstorm.svg │ └── undraw_youtube_tutorial.svg └── index.html └── yarn.lock /.babelrc.js: -------------------------------------------------------------------------------- 1 | const plugin = require('babel-preset-fbjs/plugins/dev-expression'); 2 | 3 | module.exports = { 4 | plugins: [plugin] 5 | } -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [morrys] 2 | custom: https://www.paypal.me/m0rrys 3 | -------------------------------------------------------------------------------- /.github/workflows/build.yaml: -------------------------------------------------------------------------------- 1 | name: Build 2 | on: [push, pull_request] 3 | 4 | jobs: 5 | build: 6 | runs-on: ubuntu-latest 7 | 8 | strategy: 9 | matrix: 10 | node-version: [12.x] 11 | 12 | steps: 13 | - uses: actions/checkout@v1 14 | - name: Use Node.js ${{ matrix.node-version }} 15 | uses: actions/setup-node@v1 16 | with: 17 | node-version: ${{ matrix.node-version }} 18 | - name: install dependencies 19 | run: npm ci 20 | - name: compile 21 | run: npm run compile 22 | - name: format 23 | run: npm run format:ci 24 | - name: lint 25 | run: npm run eslint 26 | - name: test 27 | run: npm run test 28 | env: 29 | CI: true 30 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | lib 3 | *.tgz 4 | coverage 5 | .DS_Store 6 | yarn.lock 7 | package-lock.json 8 | !/package-lock.json 9 | .vscode -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | src/ 2 | *.tgz 3 | tsconfig.json 4 | examples/ 5 | website/ 6 | docs/ 7 | scripts 8 | coverage 9 | __tests__ 10 | __mocks__ 11 | .github 12 | .eslintrc.js 13 | .prettierrc 14 | jest.config.js 15 | rollup.config.js 16 | .vscode 17 | .babelrc.js 18 | tsconfig.esm.json -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "tabWidth": 4, 3 | "singleQuote": true, 4 | "trailingComma": "all", 5 | "arrowParens": "always", 6 | "printWidth": 120, 7 | "parser": "typescript", 8 | "endOfLine": "auto" 9 | } 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Lorenzo Di Giacomo 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /__tests__/__generated__/.gitignore: -------------------------------------------------------------------------------- 1 | *.ts -------------------------------------------------------------------------------- /__tests__/enqueue.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | * 7 | * @emails oncall+relay 8 | * @format 9 | */ 10 | 11 | // This file is sync'd from https://github.com/facebook/react/tree/main/packages/jest-react 12 | 13 | 'use strict'; 14 | 15 | let didWarnAboutMessageChannel = false; 16 | let enqueueTaskImpl = null; 17 | 18 | export function enqueueTask(task: () => void) { 19 | if (enqueueTaskImpl === null) { 20 | try { 21 | // read require off the module object to get around the bundlers. 22 | // we don't want them to detect a require and bundle a Node polyfill. 23 | const requireString = ('require' + Math.random()).slice(0, 7); 24 | const nodeRequire = module && module[requireString]; 25 | // assuming we're in node, let's try to get node's 26 | // version of setImmediate, bypassing fake timers if any. 27 | enqueueTaskImpl = nodeRequire.call(module, 'timers').setImmediate; 28 | } catch { 29 | // we're in a browser 30 | // we can't use regular timers because they may still be faked 31 | // so we try MessageChannel+postMessage instead 32 | enqueueTaskImpl = function (callback: () => void) { 33 | if (__DEV__) { 34 | if (didWarnAboutMessageChannel === false) { 35 | didWarnAboutMessageChannel = true; 36 | if (typeof MessageChannel === 'undefined') { 37 | console.error( 38 | 'This browser does not have a MessageChannel implementation, ' + 39 | 'so enqueuing tasks via await act(async () => ...) will fail. ' + 40 | 'Please file an issue at https://github.com/facebook/react/issues ' + 41 | 'if you encounter this warning.', 42 | ); 43 | } 44 | } 45 | } 46 | /*global MessageChannel*/ 47 | const channel = new MessageChannel(); 48 | channel.port1.onmessage = callback; 49 | channel.port2.postMessage(undefined); 50 | }; 51 | } 52 | } 53 | return enqueueTaskImpl(task); 54 | } 55 | -------------------------------------------------------------------------------- /docs/usePreloadedQuery.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: use-preloaded-query 3 | title: usePreloadedQuery 4 | --- 5 | 6 | # [sample project with nextjs and SSR](https://github.com/relay-tools/relay-hooks/tree/master/examples/relay-hook-example/nextjs-ssr-preload) 7 | 8 | # loadQuery 9 | 10 | * input parameters 11 | 12 | same as useQuery + environment 13 | 14 | * output parameters 15 | * `next: ( 16 | environment: IEnvironment, 17 | gqlQuery: GraphQLTaggedNode, 18 | variables?: TOperationType['variables'], 19 | options?: QueryOptions, 20 | ) => Promise`: fetches data. A promise returns to allow the await in case of SSR 21 | * `dispose: () => void`: cancel the subscription and dispose of the fetch 22 | * `subscribe: (callback: (value: any) => any) => () => void`: used by the usePreloadedQuery 23 | * `getValue: (environment?: IEnvironment) => RenderProps | Promise`: used by the usePreloadedQuery 24 | 25 | ```ts 26 | import {graphql, loadQuery} from 'relay-hooks'; 27 | import {environment} from ''./environment'; 28 | 29 | const query = graphql` 30 | query AppQuery($id: ID!) { 31 | user(id: $id) { 32 | name 33 | } 34 | } 35 | `; 36 | 37 | const prefetch = loadQuery(); 38 | prefetch.next( 39 | environment, 40 | query, 41 | {id: '4'}, 42 | {fetchPolicy: 'store-or-network'}, 43 | ); 44 | // pass prefetch to usePreloadedQuery() 45 | ``` 46 | 47 | # loadLazyQuery 48 | 49 | **is the same as loadQuery but must be used with suspense** 50 | 51 | # usePreloadedQuery 52 | 53 | * input parameters 54 | * loadQuery | loadLazyQuery 55 | 56 | * output parameters 57 | * same as useQuery 58 | 59 | ```ts 60 | function Component(props) { 61 | data = usePreloadedQuery(props.prefetched); 62 | return data; 63 | } 64 | ``` -------------------------------------------------------------------------------- /docs/useSubscription.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: use-subscription 3 | title: useSubscription 4 | --- 5 | 6 | ```ts 7 | import { useMemo } from 'react'; 8 | import { useSubscription, graphql } from 'relay-hooks'; 9 | 10 | const subscriptionSpec = graphql` 11 | subscription TodoSubscription { 12 | todos { 13 | node { 14 | id 15 | text 16 | complete 17 | } 18 | } 19 | } 20 | `; 21 | 22 | const TodoList = (props) => { 23 | // NOTE: This will re-subscribe every render if config is not memoized. Please 24 | // do not pass an object defined inline. 25 | useSubscription( 26 | useMemo(() => ({ 27 | subscription: subscriptionSpec, 28 | variables: {} 29 | }), []) 30 | ); 31 | 32 | // ??? 33 | }; 34 | -------------------------------------------------------------------------------- /examples/relay-hook-example/cra/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /examples/relay-hook-example/cra/README.md: -------------------------------------------------------------------------------- 1 | # Relay Hooks TodoMVC 2 | 3 | ## Installation 4 | 5 | ``` 6 | yarn 7 | ``` 8 | 9 | ## Running 10 | 11 | ``` 12 | yarn compile 13 | yarn server 14 | yarn start 15 | ``` 16 | -------------------------------------------------------------------------------- /examples/relay-hook-example/cra/__generated__/relay/Entries_entries.graphql.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @flow 3 | */ 4 | 5 | /* eslint-disable */ 6 | 7 | 'use strict'; 8 | 9 | /*:: 10 | import type { ReaderFragment } from 'relay-runtime'; 11 | import type { FragmentReference } from "relay-runtime"; 12 | declare export opaque type Entries_entries$ref: FragmentReference; 13 | declare export opaque type Entries_entries$fragmentType: Entries_entries$ref; 14 | export type Entries_entries = $ReadOnlyArray<{| 15 | +id: string, 16 | +text: ?string, 17 | +$refType: Entries_entries$ref, 18 | |}>; 19 | export type Entries_entries$data = Entries_entries; 20 | export type Entries_entries$key = $ReadOnlyArray<{ 21 | +$data?: Entries_entries$data, 22 | +$fragmentRefs: Entries_entries$ref, 23 | ... 24 | }>; 25 | */ 26 | 27 | 28 | const node/*: ReaderFragment*/ = { 29 | "kind": "Fragment", 30 | "name": "Entries_entries", 31 | "type": "Entry", 32 | "metadata": { 33 | "plural": true 34 | }, 35 | "argumentDefinitions": [], 36 | "selections": [ 37 | { 38 | "kind": "ScalarField", 39 | "alias": null, 40 | "name": "id", 41 | "args": null, 42 | "storageKey": null 43 | }, 44 | { 45 | "kind": "ScalarField", 46 | "alias": null, 47 | "name": "text", 48 | "args": null, 49 | "storageKey": null 50 | } 51 | ] 52 | }; 53 | // prettier-ignore 54 | (node/*: any*/).hash = 'ccd643d090e21ec668dc0b2947383e70'; 55 | 56 | module.exports = node; 57 | -------------------------------------------------------------------------------- /examples/relay-hook-example/cra/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cra", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^4.2.4", 7 | "@testing-library/react": "^9.3.2", 8 | "@testing-library/user-event": "^7.1.2", 9 | "babel-plugin-relay": "13.1.1", 10 | "graphql": "^14.6.0", 11 | "react": "^16.13.1", 12 | "react-dom": "^16.13.1", 13 | "react-scripts": "3.4.1", 14 | "relay-compiler": "^13.1.1", 15 | "relay-hooks": "7.0.0", 16 | "relay-runtime": "13.1.1" 17 | }, 18 | "devDependencies": { 19 | "apollo-server-express": "2.11.0" 20 | }, 21 | "scripts": { 22 | "start": "react-scripts start", 23 | "build": "react-scripts build", 24 | "test": "react-scripts test", 25 | "eject": "react-scripts eject", 26 | "compile": "relay-compiler --src ./src/ --schema ./schema.gql", 27 | "server": "node ./server/server.js" 28 | }, 29 | "eslintConfig": { 30 | "extends": "react-app" 31 | }, 32 | "browserslist": { 33 | "production": [ 34 | ">0.2%", 35 | "not dead", 36 | "not op_mini all" 37 | ], 38 | "development": [ 39 | "last 1 chrome version", 40 | "last 1 firefox version", 41 | "last 1 safari version" 42 | ] 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /examples/relay-hook-example/cra/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/relay-tools/relay-hooks/31534668ef8275e3f4fc288406319a3c5dc8b636/examples/relay-hook-example/cra/public/favicon.ico -------------------------------------------------------------------------------- /examples/relay-hook-example/cra/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | React App 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /examples/relay-hook-example/cra/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/relay-tools/relay-hooks/31534668ef8275e3f4fc288406319a3c5dc8b636/examples/relay-hook-example/cra/public/logo192.png -------------------------------------------------------------------------------- /examples/relay-hook-example/cra/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/relay-tools/relay-hooks/31534668ef8275e3f4fc288406319a3c5dc8b636/examples/relay-hook-example/cra/public/logo512.png -------------------------------------------------------------------------------- /examples/relay-hook-example/cra/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /examples/relay-hook-example/cra/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /examples/relay-hook-example/cra/schema.gql: -------------------------------------------------------------------------------- 1 | type Entry { 2 | id: ID! 3 | text: String 4 | } 5 | 6 | input CreateEntryInput { 7 | clientMutationId: String 8 | id: ID! 9 | text: String! 10 | } 11 | 12 | type CreateEntryPayload { 13 | clientMutationId: String 14 | entry: Entry 15 | } 16 | 17 | type Query { 18 | entries: [Entry] 19 | } 20 | 21 | type Mutation { 22 | createEntry(input: CreateEntryInput): CreateEntryPayload 23 | } 24 | -------------------------------------------------------------------------------- /examples/relay-hook-example/cra/server/server.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | const {ApolloServer, gql} = require('apollo-server-express'); 3 | 4 | // Construct a schema, using GraphQL schema language 5 | var typeDefs = gql` 6 | type Entry { 7 | id: ID! 8 | text: String 9 | } 10 | 11 | input CreateEntryInput { 12 | clientMutationId: String 13 | id: ID! 14 | text: String! 15 | } 16 | 17 | type CreateEntryPayload { 18 | clientMutationId: String 19 | entry: Entry 20 | } 21 | 22 | type Query { 23 | entries: [Entry] 24 | } 25 | 26 | type Mutation { 27 | createEntry(input: CreateEntryInput): CreateEntryPayload 28 | } 29 | `; 30 | 31 | // The root provides a resolver function for each API endpoint 32 | var resolvers = { 33 | Query: { 34 | entries: getEntries, 35 | }, 36 | Mutation: { 37 | createEntry: createEntry, 38 | }, 39 | }; 40 | 41 | const server = new ApolloServer({ 42 | typeDefs, 43 | resolvers, 44 | }); 45 | 46 | var app = express(); 47 | 48 | server.applyMiddleware({app}); 49 | 50 | app.listen(3003); 51 | console.log('Running a GraphQL API server at localhost:3003'); 52 | 53 | let entries = []; 54 | 55 | function getEntries() { 56 | return entries; 57 | } 58 | 59 | function createEntry(parent, args, context) { 60 | const {id, text} = args.input; 61 | const entry = {id, text}; 62 | 63 | console.log('creating the entry: ', entry); 64 | entries.push(entry); 65 | return { 66 | clientMutationId: args.input.clientMutationId, 67 | entry, 68 | }; 69 | } 70 | -------------------------------------------------------------------------------- /examples/relay-hook-example/cra/src/app.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | import { useQuery, RelayEnvironmentProvider } from 'relay-hooks'; 4 | import { Environment, Network, RecordSource, Store } from 'relay-runtime'; 5 | 6 | import { create } from './mutations/create'; 7 | 8 | import QueryApp from './query/QueryApp'; 9 | import Entries from './components/Entries'; 10 | 11 | async function fetchQuery(operation, variables) { 12 | const response = await fetch('http://localhost:3003/graphql', { 13 | method: 'POST', 14 | headers: { 15 | 'Content-Type': 'application/json', 16 | }, 17 | body: JSON.stringify({ 18 | query: operation.text, 19 | variables, 20 | }), 21 | }); 22 | 23 | return response.json(); 24 | } 25 | 26 | const modernEnvironment = new Environment({ 27 | network: Network.create(fetchQuery), 28 | store: new Store(new RecordSource()), 29 | }); 30 | 31 | const AppTodo = (propsApp) => { 32 | const { data, error } = useQuery( 33 | QueryApp, 34 | {}, 35 | { 36 | fetchPolicy: 'store-or-network', 37 | }, 38 | ); /*propsApp; */ 39 | async function submitEntry() { 40 | await create('try', modernEnvironment).catch(console.error); 41 | } 42 | 43 | console.log('renderer', data, propsApp); 44 | if (data && data.entries) { 45 | return ( 46 | 47 | 50 | 51 | 52 | ); 53 | } else if (error) { 54 | console.log('error', error); 55 | return
; 56 | } 57 | return
loading
; 58 | }; 59 | 60 | const App = ( 61 | 62 | 63 | 64 | ); 65 | 66 | export default App; 67 | -------------------------------------------------------------------------------- /examples/relay-hook-example/cra/src/components/Entries.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { useFragment } from 'relay-hooks'; 3 | import graphql from 'babel-plugin-relay/macro'; 4 | 5 | const fragmentSpec = graphql` 6 | fragment Entries_entries on Entry @relay(plural: true) { 7 | id 8 | text 9 | } 10 | `; 11 | 12 | const Entries = (props) => { 13 | const entries = useFragment(fragmentSpec, props.entries); 14 | //const {entries} = props; 15 | return entries.map((entry) => ( 16 |
17 | 18 | {entry.id} : {entry.text} 19 | 20 |
21 | )); 22 | }; 23 | /*export default createFragmentContainer(Entries, { 24 | // This `list` fragment corresponds to the prop named `list` that is 25 | // expected to be populated with server data by the `` component. 26 | entries: fragmentSpec, 27 | });*/ 28 | export default Entries; 29 | -------------------------------------------------------------------------------- /examples/relay-hook-example/cra/src/components/__generated__/Entries_entries.graphql.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @flow 3 | */ 4 | 5 | /* eslint-disable */ 6 | 7 | 'use strict'; 8 | 9 | /*:: 10 | import type { ReaderFragment } from 'relay-runtime'; 11 | import type { FragmentReference } from "relay-runtime"; 12 | declare export opaque type Entries_entries$ref: FragmentReference; 13 | declare export opaque type Entries_entries$fragmentType: Entries_entries$ref; 14 | export type Entries_entries = $ReadOnlyArray<{| 15 | +id: string, 16 | +text: ?string, 17 | +$refType: Entries_entries$ref, 18 | |}>; 19 | export type Entries_entries$data = Entries_entries; 20 | export type Entries_entries$key = $ReadOnlyArray<{ 21 | +$data?: Entries_entries$data, 22 | +$fragmentRefs: Entries_entries$ref, 23 | ... 24 | }>; 25 | */ 26 | 27 | 28 | const node/*: ReaderFragment*/ = { 29 | "argumentDefinitions": [], 30 | "kind": "Fragment", 31 | "metadata": { 32 | "plural": true 33 | }, 34 | "name": "Entries_entries", 35 | "selections": [ 36 | { 37 | "alias": null, 38 | "args": null, 39 | "kind": "ScalarField", 40 | "name": "id", 41 | "storageKey": null 42 | }, 43 | { 44 | "alias": null, 45 | "args": null, 46 | "kind": "ScalarField", 47 | "name": "text", 48 | "storageKey": null 49 | } 50 | ], 51 | "type": "Entry", 52 | "abstractKey": null 53 | }; 54 | // prettier-ignore 55 | (node/*: any*/).hash = 'ccd643d090e21ec668dc0b2947383e70'; 56 | 57 | module.exports = node; 58 | -------------------------------------------------------------------------------- /examples/relay-hook-example/cra/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 12 | monospace; 13 | } 14 | -------------------------------------------------------------------------------- /examples/relay-hook-example/cra/src/index.js: -------------------------------------------------------------------------------- 1 | import ReactDOM from 'react-dom'; 2 | import './index.css'; 3 | import App from './app'; 4 | import * as serviceWorker from './serviceWorker'; 5 | 6 | ReactDOM.render(App, document.getElementById('root')); 7 | 8 | // If you want your app to work offline and load faster, you can change 9 | // unregister() to register() below. Note this comes with some pitfalls. 10 | // Learn more about service workers: https://bit.ly/CRA-PWA 11 | serviceWorker.unregister(); 12 | -------------------------------------------------------------------------------- /examples/relay-hook-example/cra/src/mutations/create.js: -------------------------------------------------------------------------------- 1 | import { commitMutation } from 'relay-hooks'; 2 | import graphql from 'babel-plugin-relay/macro'; 3 | const mutation = graphql` 4 | mutation createEntryMutation($input: CreateEntryInput) { 5 | createEntry(input: $input) { 6 | clientMutationId 7 | entry { 8 | id 9 | text 10 | } 11 | } 12 | } 13 | `; 14 | 15 | function updater(store, edge) { 16 | const records = store.getRoot().getLinkedRecords('entries'); 17 | 18 | if (!records) return; 19 | 20 | store.getRoot().setLinkedRecords([...records, edge], 'entries'); 21 | } 22 | 23 | export function create(text, environment) { 24 | const id = '' + Math.random() * 1000; 25 | 26 | return new Promise((res, rej) => 27 | commitMutation(environment, { 28 | mutation, 29 | variables: { 30 | input: { 31 | id, 32 | text, 33 | }, 34 | }, 35 | updater: (store) => { 36 | const payload = store.getRootField('createEntry'); 37 | if (!payload) return; 38 | 39 | const entry = payload.getLinkedRecord('entry'); 40 | entry && updater(store, entry); 41 | }, 42 | optimisticUpdater: (store) => { 43 | const entry = store.create(id, 'Entry'); 44 | 45 | entry.setValue(id, 'id'); 46 | entry.setValue(text, 'text'); 47 | 48 | updater(store, entry); 49 | }, 50 | onError: rej, 51 | onCompleted: res, 52 | }), 53 | ); 54 | } 55 | -------------------------------------------------------------------------------- /examples/relay-hook-example/cra/src/query/QueryApp.js: -------------------------------------------------------------------------------- 1 | import graphql from 'babel-plugin-relay/macro'; 2 | 3 | const QueryApp = graphql` 4 | query QueryAppQuery { 5 | entries { 6 | ...Entries_entries 7 | } 8 | } 9 | `; 10 | 11 | export default QueryApp; 12 | -------------------------------------------------------------------------------- /examples/relay-hook-example/cra/src/setupTests.js: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom/extend-expect'; 6 | -------------------------------------------------------------------------------- /examples/relay-hook-example/nextjs-ssr-preload/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["next/babel"], 3 | "plugins": [ 4 | ["relay", {"artifactDirectory": "./__generated__/relay/"}], 5 | "@babel/plugin-transform-runtime", 6 | "@babel/plugin-proposal-class-properties", 7 | ["styled-components", {"ssr": true}] 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /examples/relay-hook-example/nextjs-ssr-preload/.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | lib 3 | flow-typed 4 | types 5 | __generated__ 6 | -------------------------------------------------------------------------------- /examples/relay-hook-example/nextjs-ssr-preload/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: [ 3 | 'fbjs/strict', 4 | 'plugin:flowtype/recommended', 5 | 'plugin:prettier/recommended', 6 | 'plugin:react/recommended', 7 | 'plugin:relay/recommended', 8 | 'prettier/flowtype', 9 | 'prettier/react', 10 | ], 11 | parser: 'babel-eslint', 12 | plugins: ['babel', 'flowtype', 'prettier', 'react', 'relay'], 13 | rules: { 14 | 'no-console': 'off', 15 | 'one-var': 'off', 16 | 'react/prop-types': 'off', // Replaced by flow types 17 | 18 | // Mutations aren't located in the same file as Components 19 | 'relay/unused-fields': 'off', 20 | 21 | // Strict Flow linting 22 | 'flowtype/array-style-complex-type': 'error', 23 | 'flowtype/array-style-simple-type': 'error', 24 | 'flowtype/newline-after-flow-annotation': ['error', 'never'], 25 | 'flowtype/no-dupe-keys': 'error', 26 | 'flowtype/no-existential-type': 'error', 27 | 'flowtype/no-mutable-array': 'error', 28 | 'flowtype/no-primitive-constructor-types': 'error', 29 | 'flowtype/no-unused-expressions': 'error', 30 | 'flowtype/no-weak-types': 'error', 31 | 'flowtype/require-parameter-type': 'error', 32 | 'flowtype/require-return-type': 'off', // Too strict 33 | 'flowtype/require-types-at-top': 'error', 34 | 'flowtype/require-valid-file-annotation': 'error', 35 | 'flowtype/require-variable-type': 'off', // Too strict 36 | 'flowtype/sort-keys': 'error', 37 | }, 38 | settings: { 39 | react: { 40 | version: '16.8.1', 41 | flowVersion: '0.94.0', 42 | } 43 | }, 44 | }; 45 | -------------------------------------------------------------------------------- /examples/relay-hook-example/nextjs-ssr-preload/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .eslintcache 3 | .next 4 | node_modules 5 | __generated__ 6 | -------------------------------------------------------------------------------- /examples/relay-hook-example/nextjs-ssr-preload/.watchmanconfig: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/relay-tools/relay-hooks/31534668ef8275e3f4fc288406319a3c5dc8b636/examples/relay-hook-example/nextjs-ssr-preload/.watchmanconfig -------------------------------------------------------------------------------- /examples/relay-hook-example/nextjs-ssr-preload/README.md: -------------------------------------------------------------------------------- 1 | # Relay Hooks NextJS SSR TodoMVC 2 | 3 | ## Installation 4 | 5 | ``` 6 | yarn 7 | ``` 8 | 9 | ## Running 10 | 11 | Set up generated files: 12 | 13 | ``` 14 | yarn 15 | yarn compile 16 | ``` 17 | 18 | Start a local server: 19 | 20 | ``` 21 | yarn dev 22 | ``` 23 | -------------------------------------------------------------------------------- /examples/relay-hook-example/nextjs-ssr-preload/components/Header.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Link from 'next/link'; 3 | import styled, { css } from 'styled-components'; 4 | import { withRouter } from 'next/router'; 5 | 6 | const StyledButton = styled.button` 7 | margin: auto; 8 | padding: 10px; 9 | cursor: pointer; 10 | display: -webkit-box; 11 | flex: 1; 12 | ${(props) => 13 | props.selected && 14 | css` 15 | border: 1px solid #999; 16 | box-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2); 17 | box-sizing: border-box; 18 | `} 19 | `; 20 | 21 | const MyButton = React.forwardRef(({ onClick, href, children, selected }: any, ref) => ( 22 | 23 | {children} 24 | 25 | )); 26 | 27 | const StyledDiv = styled.div` 28 | display: flex; 29 | background: #fff; 30 | box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), 0 25px 50px 0 rgba(0, 0, 0, 0.1); 31 | `; 32 | 33 | const Header = ({ userId }) => { 34 | const selectedYou = userId === 'you'; 35 | return ( 36 | 37 | 38 | ME 39 | 40 | 41 | YOU 42 | 43 | 44 | ); 45 | }; 46 | 47 | export default Header; 48 | -------------------------------------------------------------------------------- /examples/relay-hook-example/nextjs-ssr-preload/components/Home.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import TodoApp, { QUERY_APP } from './TodoApp'; 3 | import { usePreloadedQuery, useQuery } from 'relay-hooks'; 4 | import { TodoAppQuery } from '../__generated__/relay/TodoAppQuery.graphql'; 5 | 6 | const Home = ({ prefetch }) => { 7 | const { error, data, retry } = usePreloadedQuery(prefetch); 8 | if (data) { 9 | return ; 10 | } else if (error) { 11 | return
{error.message}
; 12 | } 13 | return
loading
; 14 | }; 15 | 16 | export default Home; 17 | -------------------------------------------------------------------------------- /examples/relay-hook-example/nextjs-ssr-preload/data/schema/index.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | /** 3 | * This file provided by Facebook is for non-commercial testing and evaluation 4 | * purposes only. Facebook reserves all rights not expressly granted. 5 | * 6 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 7 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 8 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 9 | * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 10 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 11 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 12 | */ 13 | 14 | import {GraphQLObjectType, GraphQLSchema} from 'graphql'; 15 | 16 | import {nodeField} from './nodes.js'; 17 | import {UserQuery} from './queries/UserQuery'; 18 | import {AddTodoMutation} from './mutations/AddTodoMutation'; 19 | import {ChangeTodoStatusMutation} from './mutations/ChangeTodoStatusMutation'; 20 | import {MarkAllTodosMutation} from './mutations/MarkAllTodosMutation'; 21 | import {RemoveCompletedTodosMutation} from './mutations/RemoveCompletedTodosMutation'; 22 | import {RemoveTodoMutation} from './mutations/RemoveTodoMutation'; 23 | import {RenameTodoMutation} from './mutations/RenameTodoMutation'; 24 | 25 | const Query = new GraphQLObjectType({ 26 | name: 'Query', 27 | fields: { 28 | user: UserQuery, 29 | node: nodeField, 30 | }, 31 | }); 32 | 33 | const Mutation = new GraphQLObjectType({ 34 | name: 'Mutation', 35 | fields: { 36 | addTodo: AddTodoMutation, 37 | changeTodoStatus: ChangeTodoStatusMutation, 38 | markAllTodos: MarkAllTodosMutation, 39 | removeCompletedTodos: RemoveCompletedTodosMutation, 40 | removeTodo: RemoveTodoMutation, 41 | renameTodo: RenameTodoMutation, 42 | }, 43 | }); 44 | 45 | export const schema = new GraphQLSchema({ 46 | query: Query, 47 | mutation: Mutation, 48 | }); 49 | -------------------------------------------------------------------------------- /examples/relay-hook-example/nextjs-ssr-preload/data/schema/mutations/ChangeTodoStatusMutation.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | /* graphql-relay doesn't export types, and isn't in flow-typed. This gets too messy */ 3 | /* eslint flowtype/require-return-type: 'off' */ 4 | /** 5 | * This file provided by Facebook is for non-commercial testing and evaluation 6 | * purposes only. Facebook reserves all rights not expressly granted. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 9 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 10 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 11 | * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 12 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 13 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 14 | */ 15 | 16 | import {fromGlobalId, mutationWithClientMutationId} from 'graphql-relay'; 17 | import {GraphQLBoolean, GraphQLID, GraphQLNonNull} from 'graphql'; 18 | import {GraphQLTodo, GraphQLUser} from '../nodes'; 19 | import { 20 | changeTodoStatus, 21 | getTodoOrThrow, 22 | getUserOrThrow, 23 | Todo, 24 | User, 25 | } from '../../database'; 26 | 27 | const ChangeTodoStatusMutation = mutationWithClientMutationId({ 28 | name: 'ChangeTodoStatus', 29 | inputFields: { 30 | complete: {type: new GraphQLNonNull(GraphQLBoolean)}, 31 | id: {type: new GraphQLNonNull(GraphQLID)}, 32 | userId: {type: new GraphQLNonNull(GraphQLID)}, 33 | }, 34 | outputFields: { 35 | todo: { 36 | type: new GraphQLNonNull(GraphQLTodo), 37 | resolve: ({id}) => getTodoOrThrow(id), 38 | }, 39 | user: { 40 | type: new GraphQLNonNull(GraphQLUser), 41 | resolve: ({userId}) => getUserOrThrow(userId), 42 | }, 43 | }, 44 | mutateAndGetPayload: ({id, complete, userId}) => { 45 | changeTodoStatus(id, complete); 46 | 47 | return {id, userId}; 48 | }, 49 | }); 50 | 51 | export {ChangeTodoStatusMutation}; 52 | -------------------------------------------------------------------------------- /examples/relay-hook-example/nextjs-ssr-preload/data/schema/mutations/MarkAllTodosMutation.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | /* graphql-relay doesn't export types, and isn't in flow-typed. This gets too messy */ 3 | /* eslint flowtype/require-return-type: 'off' */ 4 | /** 5 | * This file provided by Facebook is for non-commercial testing and evaluation 6 | * purposes only. Facebook reserves all rights not expressly granted. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 9 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 10 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 11 | * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 12 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 13 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 14 | */ 15 | 16 | import {mutationWithClientMutationId} from 'graphql-relay'; 17 | import {GraphQLBoolean, GraphQLID, GraphQLList, GraphQLNonNull} from 'graphql'; 18 | import {GraphQLTodo, GraphQLUser} from '../nodes'; 19 | 20 | import { 21 | getTodoOrThrow, 22 | getUserOrThrow, 23 | markAllTodos, 24 | Todo, 25 | User, 26 | } from '../../database'; 27 | 28 | const MarkAllTodosMutation = mutationWithClientMutationId({ 29 | name: 'MarkAllTodos', 30 | inputFields: { 31 | complete: {type: new GraphQLNonNull(GraphQLBoolean)}, 32 | userId: {type: new GraphQLNonNull(GraphQLID)}, 33 | }, 34 | outputFields: { 35 | changedTodos: { 36 | type: new GraphQLList(new GraphQLNonNull(GraphQLTodo)), 37 | resolve: ({changedTodoIds}) => 38 | changedTodoIds.map(todoId => getTodoOrThrow(todoId)), 39 | }, 40 | user: { 41 | type: new GraphQLNonNull(GraphQLUser), 42 | resolve: ({userId}) => getUserOrThrow(userId), 43 | }, 44 | }, 45 | mutateAndGetPayload: ({complete, userId}) => { 46 | const changedTodoIds = markAllTodos(userId, complete); 47 | 48 | return {changedTodoIds, userId}; 49 | }, 50 | }); 51 | 52 | export {MarkAllTodosMutation}; 53 | -------------------------------------------------------------------------------- /examples/relay-hook-example/nextjs-ssr-preload/data/schema/mutations/RemoveCompletedTodosMutation.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | /* graphql-relay doesn't export types, and isn't in flow-typed. This gets too messy */ 3 | /* eslint flowtype/require-return-type: 'off' */ 4 | /** 5 | * This file provided by Facebook is for non-commercial testing and evaluation 6 | * purposes only. Facebook reserves all rights not expressly granted. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 9 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 10 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 11 | * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 12 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 13 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 14 | */ 15 | 16 | import {mutationWithClientMutationId, toGlobalId} from 'graphql-relay'; 17 | import {GraphQLID, GraphQLList, GraphQLNonNull, GraphQLString} from 'graphql'; 18 | import {GraphQLUser} from '../nodes'; 19 | import {getUserOrThrow, removeCompletedTodos, User} from '../../database'; 20 | 21 | const RemoveCompletedTodosMutation = mutationWithClientMutationId({ 22 | name: 'RemoveCompletedTodos', 23 | inputFields: { 24 | userId: {type: new GraphQLNonNull(GraphQLID)}, 25 | }, 26 | outputFields: { 27 | deletedTodoIds: { 28 | type: new GraphQLList(new GraphQLNonNull(GraphQLString)), 29 | resolve: ({deletedTodoIds}) => deletedTodoIds, 30 | }, 31 | user: { 32 | type: new GraphQLNonNull(GraphQLUser), 33 | resolve: ({userId}) => getUserOrThrow(userId), 34 | }, 35 | }, 36 | mutateAndGetPayload: ({userId}) => { 37 | const deletedTodoIds = removeCompletedTodos(userId); 38 | 39 | return {deletedTodoIds, userId}; 40 | }, 41 | }); 42 | 43 | export {RemoveCompletedTodosMutation}; 44 | -------------------------------------------------------------------------------- /examples/relay-hook-example/nextjs-ssr-preload/data/schema/mutations/RemoveTodoMutation.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | /* graphql-relay doesn't export types, and isn't in flow-typed. This gets too messy */ 3 | /* eslint flowtype/require-return-type: 'off' */ 4 | /** 5 | * This file provided by Facebook is for non-commercial testing and evaluation 6 | * purposes only. Facebook reserves all rights not expressly granted. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 9 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 10 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 11 | * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 12 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 13 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 14 | */ 15 | 16 | import {mutationWithClientMutationId, fromGlobalId} from 'graphql-relay'; 17 | import {GraphQLID, GraphQLNonNull} from 'graphql'; 18 | import {GraphQLUser} from '../nodes'; 19 | import {getUserOrThrow, removeTodo, User} from '../../database'; 20 | 21 | const RemoveTodoMutation = mutationWithClientMutationId({ 22 | name: 'RemoveTodo', 23 | inputFields: { 24 | id: {type: new GraphQLNonNull(GraphQLID)}, 25 | userId: {type: new GraphQLNonNull(GraphQLID)}, 26 | }, 27 | outputFields: { 28 | deletedTodoId: { 29 | type: new GraphQLNonNull(GraphQLID), 30 | resolve: ({id}) => id, 31 | }, 32 | user: { 33 | type: new GraphQLNonNull(GraphQLUser), 34 | resolve: ({userId}) => getUserOrThrow(userId), 35 | }, 36 | }, 37 | mutateAndGetPayload: ({id, userId}) => { 38 | removeTodo(id, userId); 39 | 40 | return {id, userId}; 41 | }, 42 | }); 43 | 44 | export {RemoveTodoMutation}; 45 | -------------------------------------------------------------------------------- /examples/relay-hook-example/nextjs-ssr-preload/data/schema/mutations/RenameTodoMutation.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | /* graphql-relay doesn't export types, and isn't in flow-typed. This gets too messy */ 3 | /* eslint flowtype/require-return-type: 'off' */ 4 | /** 5 | * This file provided by Facebook is for non-commercial testing and evaluation 6 | * purposes only. Facebook reserves all rights not expressly granted. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 9 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 10 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 11 | * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 12 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 13 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 14 | */ 15 | 16 | import {mutationWithClientMutationId, fromGlobalId} from 'graphql-relay'; 17 | import {GraphQLID, GraphQLNonNull, GraphQLString} from 'graphql'; 18 | import {GraphQLTodo} from '../nodes'; 19 | import {getTodoOrThrow, renameTodo, Todo} from '../../database'; 20 | 21 | const RenameTodoMutation = mutationWithClientMutationId({ 22 | name: 'RenameTodo', 23 | inputFields: { 24 | id: {type: new GraphQLNonNull(GraphQLID)}, 25 | text: {type: new GraphQLNonNull(GraphQLString)}, 26 | }, 27 | outputFields: { 28 | todo: { 29 | type: new GraphQLNonNull(GraphQLTodo), 30 | resolve: ({id}) => getTodoOrThrow(id), 31 | }, 32 | }, 33 | mutateAndGetPayload: ({id, text}) => { 34 | renameTodo(id, text); 35 | 36 | return {id}; 37 | }, 38 | }); 39 | 40 | export {RenameTodoMutation}; 41 | -------------------------------------------------------------------------------- /examples/relay-hook-example/nextjs-ssr-preload/data/schema/queries/UserQuery.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | /* graphql-relay doesn't export types, and isn't in flow-typed. This gets too messy */ 3 | /* eslint flowtype/require-return-type: 'off' */ 4 | /** 5 | * This file provided by Facebook is for non-commercial testing and evaluation 6 | * purposes only. Facebook reserves all rights not expressly granted. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 9 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 10 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 11 | * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 12 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 13 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 14 | */ 15 | 16 | import {GraphQLString} from 'graphql'; 17 | import {GraphQLUser} from '../nodes'; 18 | import {User, getUserOrThrow} from '../../database'; 19 | 20 | const UserQuery = { 21 | type: GraphQLUser, 22 | args: { 23 | id: {type: GraphQLString}, 24 | }, 25 | resolve: (root, {id}) => getUserOrThrow(id), 26 | }; 27 | 28 | export {UserQuery}; 29 | -------------------------------------------------------------------------------- /examples/relay-hook-example/nextjs-ssr-preload/html.js: -------------------------------------------------------------------------------- 1 | export default ({ body, title }) => { 2 | return ` 3 | 4 | 5 | 6 | 7 | 8 | ${title} 9 | 10 | 11 | 12 | 13 |
${body}
14 | 15 | 16 | 17 | 18 | `; 19 | }; -------------------------------------------------------------------------------- /examples/relay-hook-example/nextjs-ssr-preload/mutations/ChangeTodoStatusMutation.ts: -------------------------------------------------------------------------------- 1 | // @flow 2 | /** 3 | * This file provided by Facebook is for non-commercial testing and evaluation 4 | * purposes only. Facebook reserves all rights not expressly granted. 5 | * 6 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 7 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 8 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 9 | * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 10 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 11 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 12 | */ 13 | 14 | import {commitMutation, graphql} from 'relay-hooks'; 15 | 16 | const mutation = graphql` 17 | mutation ChangeTodoStatusMutation($input: ChangeTodoStatusInput!) { 18 | changeTodoStatus(input: $input) { 19 | todo { 20 | id 21 | complete 22 | } 23 | user { 24 | id 25 | completedCount 26 | } 27 | } 28 | } 29 | `; 30 | 31 | function getOptimisticResponse(complete: boolean, todo: any, user: any): any { 32 | return { 33 | changeTodoStatus: { 34 | todo: { 35 | complete: complete, 36 | id: todo.id, 37 | }, 38 | user: { 39 | id: user.id, 40 | completedCount: complete 41 | ? user.completedCount + 1 42 | : user.completedCount - 1, 43 | }, 44 | }, 45 | }; 46 | } 47 | 48 | function commit(environment: any, complete: boolean, todo: any, user: any) { 49 | const input: any = { 50 | complete, 51 | userId: user.userId, 52 | id: todo.id, 53 | }; 54 | commitMutation(environment, { 55 | mutation, 56 | variables: { 57 | input, 58 | }, 59 | optimisticResponse: getOptimisticResponse(complete, todo, user), 60 | }); 61 | } 62 | 63 | export default {commit}; 64 | -------------------------------------------------------------------------------- /examples/relay-hook-example/nextjs-ssr-preload/mutations/RemoveTodoMutation.ts: -------------------------------------------------------------------------------- 1 | // @flow 2 | /** 3 | * This file provided by Facebook is for non-commercial testing and evaluation 4 | * purposes only. Facebook reserves all rights not expressly granted. 5 | * 6 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 7 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 8 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 9 | * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 10 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 11 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 12 | */ 13 | 14 | import {commitMutation, graphql} from 'relay-hooks'; 15 | 16 | const mutation = graphql` 17 | mutation RemoveTodoMutation($input: RemoveTodoInput!) { 18 | removeTodo(input: $input) { 19 | deletedTodoId 20 | user { 21 | completedCount 22 | totalCount 23 | } 24 | } 25 | } 26 | `; 27 | 28 | function commit(environment: any, todo: any, user: any): any { 29 | const input: any = { 30 | id: todo.id, 31 | userId: user.userId, 32 | }; 33 | return commitMutation(environment, { 34 | mutation, 35 | variables: { 36 | input, 37 | }, 38 | configs: [ 39 | { 40 | type: 'NODE_DELETE', 41 | deletedIDFieldName: 'deletedTodoId', 42 | }, 43 | ], 44 | optimisticResponse: { 45 | removeTodo: { 46 | deletedTodoId: todo.id, 47 | user: { 48 | id: user.id, 49 | completedCount: user.completedCount - (todo.complete ? 1 : 0), 50 | totalCount: user.totalCount - 1, 51 | }, 52 | }, 53 | }, 54 | }); 55 | } 56 | 57 | export default {commit}; 58 | -------------------------------------------------------------------------------- /examples/relay-hook-example/nextjs-ssr-preload/mutations/RenameTodoMutation.ts: -------------------------------------------------------------------------------- 1 | // @flow 2 | /** 3 | * This file provided by Facebook is for non-commercial testing and evaluation 4 | * purposes only. Facebook reserves all rights not expressly granted. 5 | * 6 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 7 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 8 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 9 | * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 10 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 11 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 12 | */ 13 | 14 | import {commitMutation, graphql} from 'relay-hooks'; 15 | import {Disposable} from 'relay-runtime'; 16 | 17 | const mutation = graphql` 18 | mutation RenameTodoMutation($input: RenameTodoInput!) { 19 | renameTodo(input: $input) { 20 | todo { 21 | id 22 | text 23 | } 24 | } 25 | } 26 | `; 27 | 28 | function getOptimisticResponse(text: string, todo: any): any { 29 | return { 30 | renameTodo: { 31 | todo: { 32 | id: todo.id, 33 | text: text, 34 | }, 35 | }, 36 | }; 37 | } 38 | 39 | function commit(environment: any, text: string, todo: any): Disposable { 40 | const input: any = { 41 | text, 42 | id: todo.id, 43 | }; 44 | 45 | return commitMutation(environment, { 46 | mutation, 47 | variables: { 48 | input, 49 | }, 50 | optimisticResponse: getOptimisticResponse(text, todo), 51 | }); 52 | } 53 | 54 | export default {commit}; 55 | -------------------------------------------------------------------------------- /examples/relay-hook-example/nextjs-ssr-preload/next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | -------------------------------------------------------------------------------- /examples/relay-hook-example/nextjs-ssr-preload/next.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | webpack(config, options) { 5 | config.resolve.alias['relay'] = path.join( 6 | __dirname, 7 | '__generated__', 8 | 'relay', 9 | ); 10 | return config; 11 | }, 12 | }; 13 | -------------------------------------------------------------------------------- /examples/relay-hook-example/nextjs-ssr-preload/now.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 2, 3 | "builds": [ 4 | { 5 | "src": "next.config.js", 6 | "use": "@now/next" 7 | } 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /examples/relay-hook-example/nextjs-ssr-preload/pages/_document.tsx: -------------------------------------------------------------------------------- 1 | import Document, {Head, Main, NextScript, DocumentContext} from 'next/document'; 2 | import {createGlobalStyle, ServerStyleSheet} from 'styled-components'; 3 | import * as React from 'react'; 4 | 5 | const GlobalStyle = createGlobalStyle` 6 | 7 | body { 8 | font: 14px 'Helvetica Neue', Helvetica, Arial, sans-serif; 9 | line-height: 1.4em; 10 | background: #f5f5f5; 11 | color: #4d4d4d; 12 | min-width: 230px; 13 | max-width: 550px; 14 | flex-grow: 1; 15 | margin: 0 auto; 16 | display: flex; 17 | flex-direction: column; 18 | -webkit-font-smoothing: antialiased; 19 | -moz-osx-font-smoothing: grayscale; 20 | font-weight: 300; 21 | * { 22 | -webkit-tap-highlight-color: rgba(255, 255, 255, 0); 23 | } 24 | } 25 | 26 | #__next { 27 | display: flex; 28 | flex-direction: column; 29 | } 30 | 31 | 32 | &:focus { 33 | outline: 0; 34 | } 35 | 36 | button { 37 | margin: 0; 38 | padding: 0; 39 | border: 0; 40 | background: none; 41 | font-size: 100%; 42 | vertical-align: baseline; 43 | font-family: inherit; 44 | font-weight: inherit; 45 | color: inherit; 46 | -webkit-appearance: none; 47 | appearance: none; 48 | -webkit-font-smoothing: antialiased; 49 | -moz-osx-font-smoothing: grayscale; 50 | } 51 | 52 | `; 53 | 54 | type Props = { 55 | styleTags: string; 56 | }; 57 | 58 | export default class MyDocument extends Document { 59 | static async getInitialProps(ctx) { 60 | const sheet = new ServerStyleSheet(); 61 | 62 | const page = ctx.renderPage(App => props => { 63 | return sheet.collectStyles(); 64 | }); 65 | const styleTags = sheet.getStyleElement(); 66 | return { 67 | ...page, 68 | styleTags, 69 | }; 70 | } 71 | 72 | render() { 73 | console.log('render document'); 74 | return ( 75 | 76 | 77 | 78 | {this.props.styleTags} 79 | 80 | 81 | 82 |
83 | 84 | 85 | 86 | ); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /examples/relay-hook-example/nextjs-ssr-preload/pages/index.tsx: -------------------------------------------------------------------------------- 1 | import Home from '../components/Home'; 2 | 3 | export default Home; 4 | -------------------------------------------------------------------------------- /examples/relay-hook-example/nextjs-ssr-preload/pages/you.tsx: -------------------------------------------------------------------------------- 1 | import Home from '../components/Home'; 2 | 3 | export default Home; 4 | -------------------------------------------------------------------------------- /examples/relay-hook-example/nextjs-ssr-preload/relay/createRelayEnvironment.ts: -------------------------------------------------------------------------------- 1 | import {Network} from 'relay-runtime'; 2 | import {Store, Environment, RecordSource} from 'relay-runtime'; 3 | 4 | import fetch from 'isomorphic-unfetch'; 5 | import {loadQuery} from 'relay-hooks'; 6 | 7 | let relayEnvironment: Environment; 8 | 9 | function fetchQuery(operation, variables, cacheConfig, uploadables) { 10 | const endpoint = 'http://localhost:3000/graphql'; 11 | return fetch(endpoint, { 12 | method: 'POST', 13 | headers: { 14 | Accept: 'application/json', 15 | 'Content-Type': 'application/json', 16 | }, // Add authentication and other headers here 17 | body: JSON.stringify({ 18 | query: operation.text, // GraphQL text from input 19 | variables, 20 | }), 21 | }).then(response => response.json()); 22 | } 23 | 24 | type InitProps = { 25 | records?: any; 26 | }; 27 | 28 | const network = Network.create(fetchQuery); 29 | 30 | function createEnvironment(records) { 31 | const recordSource = new RecordSource(records); 32 | const store = new Store(recordSource); 33 | const environment = new Environment({ 34 | network, 35 | store, 36 | }); 37 | return environment; 38 | } 39 | 40 | const prefetch = loadQuery(); 41 | 42 | export default function initEnvironment(options: InitProps = {}) { 43 | const {records = {}} = options; 44 | 45 | if (typeof window === 'undefined') { 46 | prefetch.dispose(); 47 | return {environment: createEnvironment(records), prefetch}; 48 | } 49 | 50 | // reuse Relay environment on client-side 51 | if (!relayEnvironment) { 52 | relayEnvironment = createEnvironment(records); 53 | } 54 | return {environment: relayEnvironment, prefetch}; 55 | } 56 | -------------------------------------------------------------------------------- /examples/relay-hook-example/nextjs-ssr-preload/scripts/updateSchema.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env babel-node 2 | // @flow 3 | /** 4 | * This file provided by Facebook is for non-commercial testing and evaluation 5 | * purposes only. Facebook reserves all rights not expressly granted. 6 | * 7 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 8 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 9 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 10 | * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 11 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 12 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 13 | */ 14 | 15 | import fs from 'fs'; 16 | import path from 'path'; 17 | import {schema} from '../data/schema'; 18 | import {printSchema} from 'graphql'; 19 | 20 | const schemaPath = path.resolve(__dirname, '../data/schema.graphql'); 21 | 22 | fs.writeFileSync(schemaPath, printSchema(schema)); 23 | 24 | console.log('Wrote ' + schemaPath); 25 | -------------------------------------------------------------------------------- /examples/relay-hook-example/nextjs-ssr-preload/server.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | /** 3 | * This file provided by Facebook is for non-commercial testing and evaluation 4 | * purposes only. Facebook reserves all rights not expressly granted. 5 | * 6 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 7 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 8 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 9 | * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 10 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 11 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 12 | */ 13 | require('es6-promise').polyfill(); 14 | require('isomorphic-fetch'); 15 | import express from 'express'; 16 | import graphQLHTTP from 'express-graphql'; 17 | import {schema} from './data/schema'; 18 | import next from 'next'; 19 | 20 | const app = next({dev: process.env.NODE_ENV !== 'production'}); 21 | const handle = app.getRequestHandler(); 22 | 23 | const port = 3000; 24 | 25 | app.prepare().then(() => { 26 | const server = express(); 27 | 28 | server.use( 29 | '/graphql', 30 | graphQLHTTP({ 31 | schema, 32 | graphiql: false, 33 | pretty: true, 34 | }), 35 | ); 36 | 37 | server.get('*', (req, res) => { 38 | return handle(req, res); 39 | }); 40 | 41 | server.listen(port, err => { 42 | if (err) throw err; 43 | console.log(`> Ready on http://localhost:${port}`); 44 | }); 45 | }); 46 | -------------------------------------------------------------------------------- /examples/relay-hook-example/nextjs-ssr-preload/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowJs": true, 4 | "allowSyntheticDefaultImports": true, 5 | "esModuleInterop": true, 6 | "isolatedModules": true, 7 | "noImplicitAny": false, 8 | "jsx": "preserve", 9 | "lib": [ 10 | "dom", 11 | "es2016", 12 | "es2017.object" 13 | ], 14 | "moduleResolution": "node", 15 | "noEmit": true, 16 | "strict": true, 17 | "target": "esnext", 18 | "skipLibCheck": true, 19 | "forceConsistentCasingInFileNames": true, 20 | "module": "esnext", 21 | "resolveJsonModule": true 22 | }, 23 | "exclude": [ 24 | "node_modules", 25 | "babel.config.js", 26 | "metro.config.js", 27 | "jest.config.js" 28 | ], 29 | "include": [ 30 | "next-env.d.ts", 31 | "**/*.ts", 32 | "**/*.tsx" 33 | ] 34 | } 35 | -------------------------------------------------------------------------------- /examples/relay-hook-example/nextjs-ssr-preload/types/relay-runtime_types.js: -------------------------------------------------------------------------------- 1 | declare module 'relay-runtime' { 2 | declare export var ConnectionHandler: any; 3 | declare export var RecordSource: any; 4 | declare export var Store: any; 5 | declare export var Environment: any; 6 | declare export var Network: any; 7 | 8 | declare export type RequestNode = any; 9 | declare export type Variables = any; 10 | declare export type ReaderFragment = any; 11 | declare export type ConcreteRequest = any; 12 | declare export opaque type FragmentReference; 13 | } 14 | -------------------------------------------------------------------------------- /examples/relay-hook-example/pagination-nextjs-ssr/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["next/babel"], 3 | "plugins": [ 4 | ["relay", {"artifactDirectory": "./src/__generated__/relay/"}], 5 | "@babel/plugin-transform-runtime", 6 | "@babel/plugin-proposal-class-properties", 7 | ["styled-components", {"ssr": true}] 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /examples/relay-hook-example/pagination-nextjs-ssr/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .eslintcache 3 | .next 4 | node_modules 5 | __generated__ 6 | -------------------------------------------------------------------------------- /examples/relay-hook-example/pagination-nextjs-ssr/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "tabWidth": 4, 3 | "singleQuote": true, 4 | "trailingComma": "all", 5 | "arrowParens": "always", 6 | "printWidth": 100, 7 | "parser": "typescript" 8 | } 9 | -------------------------------------------------------------------------------- /examples/relay-hook-example/pagination-nextjs-ssr/.watchmanconfig: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/relay-tools/relay-hooks/31534668ef8275e3f4fc288406319a3c5dc8b636/examples/relay-hook-example/pagination-nextjs-ssr/.watchmanconfig -------------------------------------------------------------------------------- /examples/relay-hook-example/pagination-nextjs-ssr/README.md: -------------------------------------------------------------------------------- 1 | # Relay Hooks NextJS SSR TodoMVC 2 | 3 | ## Installation 4 | 5 | ``` 6 | yarn 7 | ``` 8 | 9 | ## Running 10 | 11 | Set up generated files: 12 | 13 | ``` 14 | yarn 15 | yarn compile 16 | ``` 17 | 18 | Start a local server: 19 | 20 | ``` 21 | yarn dev 22 | ``` 23 | -------------------------------------------------------------------------------- /examples/relay-hook-example/pagination-nextjs-ssr/data/schema/index.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | /** 3 | * This file provided by Facebook is for non-commercial testing and evaluation 4 | * purposes only. Facebook reserves all rights not expressly granted. 5 | * 6 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 7 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 8 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 9 | * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 10 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 11 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 12 | */ 13 | 14 | import { GraphQLObjectType, GraphQLSchema } from 'graphql'; 15 | 16 | import { AddTodoMutation } from './mutations/AddTodoMutation'; 17 | import { ChangeTodoStatusMutation } from './mutations/ChangeTodoStatusMutation'; 18 | import { MarkAllTodosMutation } from './mutations/MarkAllTodosMutation'; 19 | import { RemoveCompletedTodosMutation } from './mutations/RemoveCompletedTodosMutation'; 20 | import { RemoveTodoMutation } from './mutations/RemoveTodoMutation'; 21 | import { RenameTodoMutation } from './mutations/RenameTodoMutation'; 22 | import { nodeField } from './nodes.js'; 23 | import { UserQuery } from './queries/UserQuery'; 24 | 25 | const Query = new GraphQLObjectType({ 26 | name: 'Query', 27 | fields: { 28 | user: UserQuery, 29 | node: nodeField, 30 | }, 31 | }); 32 | 33 | const Mutation = new GraphQLObjectType({ 34 | name: 'Mutation', 35 | fields: { 36 | addTodo: AddTodoMutation, 37 | changeTodoStatus: ChangeTodoStatusMutation, 38 | markAllTodos: MarkAllTodosMutation, 39 | removeCompletedTodos: RemoveCompletedTodosMutation, 40 | removeTodo: RemoveTodoMutation, 41 | renameTodo: RenameTodoMutation, 42 | }, 43 | }); 44 | 45 | export const schema = new GraphQLSchema({ 46 | query: Query, 47 | mutation: Mutation, 48 | }); 49 | -------------------------------------------------------------------------------- /examples/relay-hook-example/pagination-nextjs-ssr/data/schema/mutations/ChangeTodoStatusMutation.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/explicit-function-return-type */ 2 | // @flow 3 | /* graphql-relay doesn't export types, and isn't in flow-typed. This gets too messy */ 4 | /** 5 | * This file provided by Facebook is for non-commercial testing and evaluation 6 | * purposes only. Facebook reserves all rights not expressly granted. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 9 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 10 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 11 | * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 12 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 13 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 14 | */ 15 | 16 | import { GraphQLBoolean, GraphQLID, GraphQLNonNull } from 'graphql'; 17 | import { mutationWithClientMutationId } from 'graphql-relay'; 18 | import { changeTodoStatus, getTodoOrThrow, getUserOrThrow } from '../../database'; 19 | import { GraphQLTodo, GraphQLUser } from '../nodes'; 20 | 21 | const ChangeTodoStatusMutation = mutationWithClientMutationId({ 22 | name: 'ChangeTodoStatus', 23 | inputFields: { 24 | complete: { type: new GraphQLNonNull(GraphQLBoolean) }, 25 | id: { type: new GraphQLNonNull(GraphQLID) }, 26 | userId: { type: new GraphQLNonNull(GraphQLID) }, 27 | }, 28 | outputFields: { 29 | todo: { 30 | type: new GraphQLNonNull(GraphQLTodo), 31 | resolve: ({ id }) => getTodoOrThrow(id), 32 | }, 33 | user: { 34 | type: new GraphQLNonNull(GraphQLUser), 35 | resolve: ({ userId }) => getUserOrThrow(userId), 36 | }, 37 | }, 38 | mutateAndGetPayload: ({ id, complete, userId }) => { 39 | changeTodoStatus(id, complete); 40 | 41 | return { id, userId }; 42 | }, 43 | }); 44 | 45 | export { ChangeTodoStatusMutation }; 46 | -------------------------------------------------------------------------------- /examples/relay-hook-example/pagination-nextjs-ssr/data/schema/mutations/MarkAllTodosMutation.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/explicit-function-return-type */ 2 | // @flow 3 | /* graphql-relay doesn't export types, and isn't in flow-typed. This gets too messy */ 4 | /** 5 | * This file provided by Facebook is for non-commercial testing and evaluation 6 | * purposes only. Facebook reserves all rights not expressly granted. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 9 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 10 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 11 | * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 12 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 13 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 14 | */ 15 | 16 | import { GraphQLBoolean, GraphQLID, GraphQLList, GraphQLNonNull } from 'graphql'; 17 | import { mutationWithClientMutationId } from 'graphql-relay'; 18 | import { getTodoOrThrow, getUserOrThrow, markAllTodos } from '../../database'; 19 | import { GraphQLTodo, GraphQLUser } from '../nodes'; 20 | 21 | const MarkAllTodosMutation = mutationWithClientMutationId({ 22 | name: 'MarkAllTodos', 23 | inputFields: { 24 | complete: { type: new GraphQLNonNull(GraphQLBoolean) }, 25 | userId: { type: new GraphQLNonNull(GraphQLID) }, 26 | }, 27 | outputFields: { 28 | changedTodos: { 29 | type: new GraphQLList(new GraphQLNonNull(GraphQLTodo)), 30 | resolve: ({ changedTodoIds }) => changedTodoIds.map((todoId) => getTodoOrThrow(todoId)), 31 | }, 32 | user: { 33 | type: new GraphQLNonNull(GraphQLUser), 34 | resolve: ({ userId }) => getUserOrThrow(userId), 35 | }, 36 | }, 37 | mutateAndGetPayload: ({ complete, userId }) => { 38 | const changedTodoIds = markAllTodos(userId, complete); 39 | 40 | return { changedTodoIds, userId }; 41 | }, 42 | }); 43 | 44 | export { MarkAllTodosMutation }; 45 | -------------------------------------------------------------------------------- /examples/relay-hook-example/pagination-nextjs-ssr/data/schema/mutations/RemoveCompletedTodosMutation.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/explicit-function-return-type */ 2 | // @flow 3 | /* graphql-relay doesn't export types, and isn't in flow-typed. This gets too messy */ 4 | /** 5 | * This file provided by Facebook is for non-commercial testing and evaluation 6 | * purposes only. Facebook reserves all rights not expressly granted. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 9 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 10 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 11 | * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 12 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 13 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 14 | */ 15 | 16 | import { GraphQLID, GraphQLList, GraphQLNonNull, GraphQLString } from 'graphql'; 17 | import { mutationWithClientMutationId } from 'graphql-relay'; 18 | import { getUserOrThrow, removeCompletedTodos } from '../../database'; 19 | import { GraphQLUser } from '../nodes'; 20 | 21 | const RemoveCompletedTodosMutation = mutationWithClientMutationId({ 22 | name: 'RemoveCompletedTodos', 23 | inputFields: { 24 | userId: { type: new GraphQLNonNull(GraphQLID) }, 25 | }, 26 | outputFields: { 27 | deletedTodoIds: { 28 | type: new GraphQLList(new GraphQLNonNull(GraphQLString)), 29 | resolve: ({ deletedTodoIds }) => deletedTodoIds, 30 | }, 31 | user: { 32 | type: new GraphQLNonNull(GraphQLUser), 33 | resolve: ({ userId }) => getUserOrThrow(userId), 34 | }, 35 | }, 36 | mutateAndGetPayload: ({ userId }) => { 37 | const deletedTodoIds = removeCompletedTodos(userId); 38 | 39 | return { deletedTodoIds, userId }; 40 | }, 41 | }); 42 | 43 | export { RemoveCompletedTodosMutation }; 44 | -------------------------------------------------------------------------------- /examples/relay-hook-example/pagination-nextjs-ssr/data/schema/mutations/RemoveTodoMutation.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/explicit-function-return-type */ 2 | // @flow 3 | /* graphql-relay doesn't export types, and isn't in flow-typed. This gets too messy */ 4 | /** 5 | * This file provided by Facebook is for non-commercial testing and evaluation 6 | * purposes only. Facebook reserves all rights not expressly granted. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 9 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 10 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 11 | * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 12 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 13 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 14 | */ 15 | 16 | import { GraphQLID, GraphQLNonNull } from 'graphql'; 17 | import { mutationWithClientMutationId } from 'graphql-relay'; 18 | import { getUserOrThrow, removeTodo } from '../../database'; 19 | import { GraphQLUser } from '../nodes'; 20 | 21 | const RemoveTodoMutation = mutationWithClientMutationId({ 22 | name: 'RemoveTodo', 23 | inputFields: { 24 | id: { type: new GraphQLNonNull(GraphQLID) }, 25 | userId: { type: new GraphQLNonNull(GraphQLID) }, 26 | }, 27 | outputFields: { 28 | deletedTodoId: { 29 | type: new GraphQLNonNull(GraphQLID), 30 | resolve: ({ id }) => id, 31 | }, 32 | user: { 33 | type: new GraphQLNonNull(GraphQLUser), 34 | resolve: ({ userId }) => getUserOrThrow(userId), 35 | }, 36 | }, 37 | mutateAndGetPayload: ({ id, userId }) => { 38 | removeTodo(id, userId); 39 | 40 | return { id, userId }; 41 | }, 42 | }); 43 | 44 | export { RemoveTodoMutation }; 45 | -------------------------------------------------------------------------------- /examples/relay-hook-example/pagination-nextjs-ssr/data/schema/mutations/RenameTodoMutation.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/explicit-function-return-type */ 2 | // @flow 3 | /* graphql-relay doesn't export types, and isn't in flow-typed. This gets too messy */ 4 | /** 5 | * This file provided by Facebook is for non-commercial testing and evaluation 6 | * purposes only. Facebook reserves all rights not expressly granted. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 9 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 10 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 11 | * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 12 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 13 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 14 | */ 15 | 16 | import { GraphQLID, GraphQLNonNull, GraphQLString } from 'graphql'; 17 | import { mutationWithClientMutationId } from 'graphql-relay'; 18 | import { getTodoOrThrow, renameTodo } from '../../database'; 19 | import { GraphQLTodo } from '../nodes'; 20 | 21 | const RenameTodoMutation = mutationWithClientMutationId({ 22 | name: 'RenameTodo', 23 | inputFields: { 24 | id: { type: new GraphQLNonNull(GraphQLID) }, 25 | text: { type: new GraphQLNonNull(GraphQLString) }, 26 | }, 27 | outputFields: { 28 | todo: { 29 | type: new GraphQLNonNull(GraphQLTodo), 30 | resolve: ({ id }) => getTodoOrThrow(id), 31 | }, 32 | }, 33 | mutateAndGetPayload: ({ id, text }) => { 34 | renameTodo(id, text); 35 | 36 | return { id }; 37 | }, 38 | }); 39 | 40 | export { RenameTodoMutation }; 41 | -------------------------------------------------------------------------------- /examples/relay-hook-example/pagination-nextjs-ssr/data/schema/queries/UserQuery.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/explicit-function-return-type */ 2 | // @flow 3 | /* graphql-relay doesn't export types, and isn't in flow-typed. This gets too messy */ 4 | /** 5 | * This file provided by Facebook is for non-commercial testing and evaluation 6 | * purposes only. Facebook reserves all rights not expressly granted. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 9 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 10 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 11 | * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 12 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 13 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 14 | */ 15 | 16 | import { GraphQLString } from 'graphql'; 17 | import { connectionArgs } from 'graphql-relay'; 18 | import { getUserOrThrow } from '../../database'; 19 | import { GraphQLUser } from '../nodes'; 20 | 21 | const UserQuery = { 22 | type: GraphQLUser, 23 | args: { 24 | id: { type: GraphQLString }, 25 | ...connectionArgs, 26 | }, 27 | resolve: (root, { id }) => getUserOrThrow(id), 28 | }; 29 | 30 | export { UserQuery }; 31 | -------------------------------------------------------------------------------- /examples/relay-hook-example/pagination-nextjs-ssr/html.js: -------------------------------------------------------------------------------- 1 | export default ({ body, title }) => { 2 | return ` 3 | 4 | 5 | 6 | 7 | 8 | ${title} 9 | 10 | 11 | 12 | 13 |
${body}
14 | 15 | 16 | 17 | 18 | `; 19 | }; -------------------------------------------------------------------------------- /examples/relay-hook-example/pagination-nextjs-ssr/next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | // NOTE: This file should not be edited 5 | // see https://nextjs.org/docs/basic-features/typescript for more information. 6 | -------------------------------------------------------------------------------- /examples/relay-hook-example/pagination-nextjs-ssr/next.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | webpack(config, options) { 5 | config.resolve.alias['relay'] = path.join( 6 | __dirname, 7 | 'src', 8 | '__generated__', 9 | 'relay', 10 | ); 11 | config.resolve.preferRelative = true; 12 | return config; 13 | }, 14 | reactStrictMode: true, 15 | compiler: { 16 | styledComponents: true, 17 | relay: { 18 | src: './src', 19 | language: 'typescript', // or 'javascript` 20 | artifactDirectory: './__generated__/relay', // you can leave this undefined if you did not specify one in the `relay.json` 21 | }, 22 | } 23 | }; -------------------------------------------------------------------------------- /examples/relay-hook-example/pagination-nextjs-ssr/now.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 2, 3 | "builds": [ 4 | { 5 | "src": "next.config.js", 6 | "use": "@now/next" 7 | } 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /examples/relay-hook-example/pagination-nextjs-ssr/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "dev": "cross-env NODE_ENV=development babel-node server.js", 5 | "build": "next build", 6 | "start": "cross-env NODE_ENV=production babel-node server.js", 7 | "compile": "relay-compiler", 8 | "update-schema": "babel-node ./scripts/updateSchema.js", 9 | "lint": "eslint ./ --cache" 10 | }, 11 | "relay": { 12 | "src": "./", 13 | "schema": "./data/schema.graphql", 14 | "language": "typescript", 15 | "artifactDirectory": "./src/__generated__/relay", 16 | "excludes": ["__generated__/**", "node_modules/**", ".next/**"] 17 | }, 18 | "dependencies": { 19 | "styled-components": "6.1.1", 20 | "styled-system": "5.1.5", 21 | "classnames": "2.2.6", 22 | "es6-promise": "4.2.8", 23 | "express": "^4.16.4", 24 | "express-graphql": "^0.7.1", 25 | "graphql": "^15.5.0", 26 | "graphql-relay": "^0.6.0", 27 | "isomorphic-unfetch": "3.0.0", 28 | "isomorphic-fetch": "^2.1.1", 29 | "next": "14.0.3", 30 | "prop-types": "^15.7.2", 31 | "react": "^18.0.0", 32 | "react-dom": "^18.0.0", 33 | "relay-hooks": "9.3.0", 34 | "relay-runtime": "18.0.0", 35 | "whatwg-fetch": "3.0.0", 36 | "react-infinite-scroller": "1.2.6" 37 | }, 38 | "resolutions": { "**/@types/react": "17.0.2", "**/@types/react-transition-group": "4.2.0" }, 39 | "devDependencies": { 40 | "@babel/core": "^7.3.4", 41 | "@babel/node": "^7.2.2", 42 | "@babel/plugin-proposal-class-properties": "^7.3.4", 43 | "@babel/plugin-transform-runtime": "^7.3.4", 44 | "babel-plugin-styled-components": "^2.1.4", 45 | "@babel/preset-env": "^7.3.4", 46 | "@babel/preset-react": "^7.0.0", 47 | "@babel/runtime": "^7.15.4", 48 | "@types/node": "^12.7.12", 49 | "@types/react": "^18.0.0", 50 | "@types/react-dom": "^18.0.0", 51 | "@types/relay-runtime": "^14.0.0", 52 | "@zeit/next-typescript": "1.1.1", 53 | "babel-loader": "^8.0.5", 54 | "babel-plugin-relay": "13.0.1", 55 | "cross-env": "6.0.3", 56 | "relay-compiler": "18.0.0", 57 | "relay-compiler-language-typescript": "15.0.1", 58 | "typescript": "^4.2.4" 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /examples/relay-hook-example/pagination-nextjs-ssr/scripts/updateSchema.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env babel-node 2 | // @flow 3 | /** 4 | * This file provided by Facebook is for non-commercial testing and evaluation 5 | * purposes only. Facebook reserves all rights not expressly granted. 6 | * 7 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 8 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 9 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 10 | * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 11 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 12 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 13 | */ 14 | 15 | import fs from 'fs'; 16 | import path from 'path'; 17 | import { printSchema } from 'graphql'; 18 | import { schema } from '../data/schema'; 19 | 20 | const schemaPath = path.resolve(__dirname, '../data/schema.graphql'); 21 | 22 | fs.writeFileSync(schemaPath, printSchema(schema)); 23 | -------------------------------------------------------------------------------- /examples/relay-hook-example/pagination-nextjs-ssr/server.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | /** 3 | * This file provided by Facebook is for non-commercial testing and evaluation 4 | * purposes only. Facebook reserves all rights not expressly granted. 5 | * 6 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 7 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 8 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 9 | * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 10 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 11 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 12 | */ 13 | require('es6-promise').polyfill(); 14 | require('isomorphic-fetch'); 15 | import express from 'express'; 16 | import graphQLHTTP from 'express-graphql'; 17 | import next from 'next'; 18 | import { schema } from './data/schema'; 19 | 20 | const app = next({ dev: process.env.NODE_ENV !== 'production' }); 21 | const handle = app.getRequestHandler(); 22 | 23 | const port = 3000; 24 | 25 | app.prepare().then(() => { 26 | const server = express(); 27 | 28 | server.use( 29 | '/graphql', 30 | graphQLHTTP({ 31 | schema, 32 | graphiql: false, 33 | pretty: true, 34 | }), 35 | ); 36 | 37 | server.get('*', (req, res) => { 38 | return handle(req, res); 39 | }); 40 | 41 | server.listen(port, (err) => { 42 | if (err) throw err; 43 | console.log(`> Ready on http://localhost:${port}`); 44 | }); 45 | }); 46 | -------------------------------------------------------------------------------- /examples/relay-hook-example/pagination-nextjs-ssr/src/components/LinearProgress.ts: -------------------------------------------------------------------------------- 1 | import styled, { keyframes } from "styled-components"; 2 | 3 | const progressKeyframe = keyframes` 4 | 0% { 5 | background-color: hsl(200, 100%, 50%); 6 | } 7 | 50% { 8 | background-color: hsl(200, 50%, 50%); 9 | } 10 | 100% { 11 | background-color: hsl(200, 0%, 50%); 12 | } 13 | `; 14 | 15 | 16 | const Skeleton = styled.div` 17 | animation: ${progressKeyframe} 1s linear infinite alternate; 18 | `; 19 | // 2.5s ease-in-out 0s infinite normal none running 1s linear infinite alternate 20 | export const LinearProgress = styled(Skeleton)` 21 | width: 100%; 22 | height: 0.3rem; 23 | border-radius: 0.25rem; 24 | `; 25 | -------------------------------------------------------------------------------- /examples/relay-hook-example/pagination-nextjs-ssr/src/components/Select.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react' 2 | import styled from 'styled-components' 3 | 4 | const Main = styled("div")` 5 | font-family: sans-serif; 6 | width: 70px; 7 | `; 8 | 9 | const DropDownContainer = styled("div")` 10 | margin: 0 auto; 11 | position: absolute; 12 | width: 70px; 13 | `; 14 | 15 | const DropDownHeader = styled("div")` 16 | padding: 10px 10px; 17 | box-shadow: 0 2px 3px rgba(0, 0, 0, 0.15); 18 | font-weight: 500; 19 | font-size: 1.3rem; 20 | color: #3faffa; 21 | background: #ffffff; 22 | cursor: pointer; 23 | `; 24 | 25 | const DropDownListContainer = styled("div")``; 26 | 27 | const DropDownList = styled("ul")` 28 | padding: 0; 29 | margin: 0; 30 | padding-left: 1em; 31 | background: #ffffff; 32 | border: 2px solid #e5e5e5; 33 | box-sizing: border-box; 34 | color: #3faffa; 35 | font-size: 1.3rem; 36 | font-weight: 500; 37 | &:first-child { 38 | padding-top: 0.8em; 39 | } 40 | cursor: pointer; 41 | `; 42 | 43 | const ListItem = styled("li")` 44 | list-style: none; 45 | margin-bottom: 0.8em; 46 | `; 47 | 48 | export const Select = (props) => { 49 | const { items, onItemSelected, defaultValue } = props; 50 | const [menuVisible, setMenuVisible] = useState(false) 51 | const [selectedOption, setSelectedOption] = useState(defaultValue); 52 | 53 | 54 | const onOptionClicked = value => { 55 | setSelectedOption(value); 56 | setMenuVisible(false); 57 | onItemSelected(value) 58 | }; 59 | 60 | const toggleMenu = () => { 61 | setMenuVisible(!menuVisible) 62 | } 63 | 64 | const list = items.map((item: any) => ( 65 | onOptionClicked(item)} key={item}>{item} 66 | )) 67 | 68 | return ( 69 |
70 | 71 | {selectedOption} 72 | 73 | {menuVisible && ( 74 | {list} 75 | )} 76 | 77 | 78 |
79 | 80 | ) 81 | } -------------------------------------------------------------------------------- /examples/relay-hook-example/pagination-nextjs-ssr/src/mutations/ReadInlineUser.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/camelcase */ 2 | import { graphql, readInlineData } from 'relay-runtime'; 3 | 4 | import { 5 | ReadInlineUser_user$data, 6 | ReadInlineUser_user$key, 7 | } from '../__generated__/relay/ReadInlineUser_user.graphql'; 8 | const fragmentNode = graphql` 9 | fragment ReadInlineUser_user on User @inline { 10 | id 11 | userId 12 | totalCount 13 | completedCount 14 | } 15 | `; 16 | 17 | // non-React function called from React 18 | export function getUser(userRef: ReadInlineUser_user$key): ReadInlineUser_user$data { 19 | return readInlineData(fragmentNode, userRef); 20 | } 21 | -------------------------------------------------------------------------------- /examples/relay-hook-example/pagination-nextjs-ssr/src/mutations/RenameTodoMutation.ts: -------------------------------------------------------------------------------- 1 | // @flow 2 | /** 3 | * This file provided by Facebook is for non-commercial testing and evaluation 4 | * purposes only. Facebook reserves all rights not expressly granted. 5 | * 6 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 7 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 8 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 9 | * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 10 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 11 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 12 | */ 13 | 14 | import { commitMutation, graphql } from 'relay-hooks'; 15 | import { Disposable } from 'relay-runtime'; 16 | import { RenameTodoMutation as RenameTodoMutationType } from '../__generated__/relay/RenameTodoMutation.graphql'; 17 | 18 | const mutation = graphql` 19 | mutation RenameTodoMutation($input: RenameTodoInput!) @raw_response_type { 20 | renameTodo(input: $input) { 21 | todo { 22 | id 23 | text 24 | } 25 | } 26 | } 27 | `; 28 | 29 | function getOptimisticResponse(text: string, todo: any): any { 30 | return { 31 | renameTodo: { 32 | todo: { 33 | id: todo.id, 34 | text: text, 35 | }, 36 | }, 37 | }; 38 | } 39 | 40 | export function commit(environment: any, text: string, todo: any): Disposable { 41 | const input: any = { 42 | text, 43 | id: todo.id, 44 | }; 45 | 46 | return commitMutation(environment, { 47 | mutation, 48 | variables: { 49 | input, 50 | }, 51 | optimisticResponse: getOptimisticResponse(text, todo), 52 | }); 53 | } 54 | 55 | export const RenameTodoMutation = { 56 | commit, 57 | }; 58 | -------------------------------------------------------------------------------- /examples/relay-hook-example/pagination-nextjs-ssr/src/pages/_document.tsx: -------------------------------------------------------------------------------- 1 | import Document, { Html, Head, Main, NextScript } from "next/document"; 2 | import { ServerStyleSheet } from "styled-components"; 3 | 4 | export default class MyDocument extends Document { 5 | static async getInitialProps(ctx) { 6 | const sheet = new ServerStyleSheet(); 7 | const originalRenderPage = ctx.renderPage; 8 | 9 | try { 10 | ctx.renderPage = () => 11 | originalRenderPage({ 12 | enhanceApp: (App) => (props) => 13 | sheet.collectStyles(), 14 | }); 15 | 16 | const initialProps = await Document.getInitialProps(ctx); 17 | return { 18 | ...initialProps, 19 | styles: ( 20 | <> 21 | {initialProps.styles} 22 | {sheet.getStyleElement()} 23 | 24 | ), 25 | }; 26 | } finally { 27 | sheet.seal(); 28 | } 29 | } 30 | 31 | render() { 32 | return ( 33 | 34 | 35 | 36 |
37 | 38 | 39 | 40 | ); 41 | } 42 | } -------------------------------------------------------------------------------- /examples/relay-hook-example/pagination-nextjs-ssr/src/pages/index.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/no-default-export */ 2 | import { createRootPage } from '../components/RootPage'; 3 | const first = 2147483647; 4 | export default createRootPage(first); 5 | -------------------------------------------------------------------------------- /examples/relay-hook-example/pagination-nextjs-ssr/src/pages/paginated.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/no-default-export */ 2 | import { createRootPage } from '../components/RootPage'; 3 | const first = 2; 4 | export default createRootPage(first); 5 | -------------------------------------------------------------------------------- /examples/relay-hook-example/pagination-nextjs-ssr/src/pages/scroll.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/no-default-export */ 2 | import { createRootPage } from '../components/RootPage'; 3 | const first = 1; 4 | export default createRootPage(first); 5 | -------------------------------------------------------------------------------- /examples/relay-hook-example/pagination-nextjs-ssr/src/relay/index.ts: -------------------------------------------------------------------------------- 1 | export { initEnvironment } from './createRelayEnvironment'; 2 | export { withData } from './withData'; 3 | -------------------------------------------------------------------------------- /examples/relay-hook-example/pagination-nextjs-ssr/src/relay/withData.tsx: -------------------------------------------------------------------------------- 1 | import { DocumentContext } from 'next/document'; 2 | import React from 'react'; 3 | import { fetchQuery } from 'relay-hooks'; 4 | import { GraphQLTaggedNode } from 'relay-runtime'; 5 | import { initEnvironment } from './createRelayEnvironment'; 6 | 7 | type OptionsWithData = { 8 | query: GraphQLTaggedNode; 9 | first: number; 10 | }; 11 | 12 | export const withData = (ComposedComponent: any, options: OptionsWithData): any => { 13 | function WithData(): JSX.Element { 14 | return ; 15 | } 16 | 17 | WithData.getInitialProps = async (ctx: DocumentContext): Promise => { 18 | const isServer = !!ctx.req; 19 | let composedInitialProps = {}; 20 | if (ComposedComponent.getInitialProps) { 21 | composedInitialProps = await ComposedComponent.getInitialProps(ctx); 22 | } 23 | if (!isServer) { 24 | return { 25 | ...composedInitialProps, 26 | environment: null, 27 | }; 28 | } 29 | 30 | let queryRecords = {}; 31 | const environment = initEnvironment(); 32 | const userId = ctx.query && ctx.query.userId ? ctx.query.userId : 'me'; 33 | 34 | const { query, first } = options; 35 | const variables = { 36 | first, 37 | userId, 38 | }; 39 | if (query) { 40 | await fetchQuery(environment, query, variables); 41 | queryRecords = environment 42 | .getStore() 43 | .getSource() 44 | .toJSON(); 45 | } 46 | return { 47 | ...composedInitialProps, 48 | queryRecords, 49 | environment, 50 | }; 51 | }; 52 | 53 | return WithData; 54 | }; 55 | -------------------------------------------------------------------------------- /examples/relay-hook-example/pagination-nextjs-ssr/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowJs": true, 4 | "allowSyntheticDefaultImports": true, 5 | "esModuleInterop": true, 6 | "isolatedModules": true, 7 | "noImplicitAny": false, 8 | "jsx": "preserve", 9 | "lib": [ 10 | "dom", 11 | "es2016", 12 | "es2017.object" 13 | ], 14 | "moduleResolution": "node", 15 | "noEmit": true, 16 | "strict": true, 17 | "target": "esnext", 18 | "skipLibCheck": true, 19 | "forceConsistentCasingInFileNames": true, 20 | "module": "esnext", 21 | "resolveJsonModule": true, 22 | "incremental": true 23 | }, 24 | "exclude": [ 25 | "node_modules", 26 | "babel.config.js", 27 | "metro.config.js", 28 | "jest.config.js" 29 | ], 30 | "include": [ 31 | "next-env.d.ts", 32 | "**/*.ts", 33 | "**/*.tsx" 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /examples/relay-hook-example/pagination-nextjs-ssr/types/relay-runtime_types.js: -------------------------------------------------------------------------------- 1 | declare module 'relay-runtime' { 2 | declare export var ConnectionHandler: any; 3 | declare export var RecordSource: any; 4 | declare export var Store: any; 5 | declare export var Environment: any; 6 | declare export var Network: any; 7 | 8 | declare export type RequestNode = any; 9 | declare export type Variables = any; 10 | declare export type ReaderFragment = any; 11 | declare export type ConcreteRequest = any; 12 | declare export opaque type FragmentReference; 13 | } 14 | -------------------------------------------------------------------------------- /examples/relay-hook-example/todo/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | ["relay", { "artifactDirectory": "./__generated__/relay/" }], 4 | "@babel/plugin-transform-runtime", 5 | "@babel/plugin-proposal-class-properties" 6 | ], 7 | "presets": ["@babel/preset-react", "@babel/preset-env", "@babel/preset-flow"] 8 | } 9 | -------------------------------------------------------------------------------- /examples/relay-hook-example/todo/.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | lib 3 | flow-typed 4 | types 5 | __generated__ 6 | -------------------------------------------------------------------------------- /examples/relay-hook-example/todo/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: [ 3 | 'fbjs/strict', 4 | 'plugin:flowtype/recommended', 5 | 'plugin:prettier/recommended', 6 | 'plugin:react/recommended', 7 | 'plugin:relay/recommended', 8 | 'prettier/flowtype', 9 | 'prettier/react', 10 | ], 11 | parser: 'babel-eslint', 12 | plugins: ['babel', 'flowtype', 'prettier', 'react', 'relay'], 13 | rules: { 14 | 'no-console': 'off', 15 | 'one-var': 'off', 16 | 'react/prop-types': 'off', // Replaced by flow types 17 | 18 | // Mutations aren't located in the same file as Components 19 | 'relay/unused-fields': 'off', 20 | 21 | // Strict Flow linting 22 | 'flowtype/array-style-complex-type': 'error', 23 | 'flowtype/array-style-simple-type': 'error', 24 | 'flowtype/newline-after-flow-annotation': ['error', 'never'], 25 | 'flowtype/no-dupe-keys': 'error', 26 | 'flowtype/no-existential-type': 'error', 27 | 'flowtype/no-mutable-array': 'error', 28 | 'flowtype/no-primitive-constructor-types': 'error', 29 | 'flowtype/no-unused-expressions': 'error', 30 | 'flowtype/no-weak-types': 'error', 31 | 'flowtype/require-parameter-type': 'error', 32 | 'flowtype/require-return-type': 'off', // Too strict 33 | 'flowtype/require-types-at-top': 'error', 34 | 'flowtype/require-valid-file-annotation': 'error', 35 | 'flowtype/require-variable-type': 'off', // Too strict 36 | 'flowtype/sort-keys': 'error', 37 | }, 38 | settings: { 39 | react: { 40 | version: '16.8.1', 41 | flowVersion: '0.94.0', 42 | } 43 | }, 44 | }; 45 | -------------------------------------------------------------------------------- /examples/relay-hook-example/todo/.flowconfig: -------------------------------------------------------------------------------- 1 | [ignore] 2 | 3 | [include] 4 | 5 | [libs] 6 | flow-typed/ 7 | types/ 8 | 9 | [lints] 10 | 11 | [options] 12 | module.system.node.resolve_dirname=node_modules 13 | module.system.node.resolve_dirname=__generated__ 14 | 15 | [strict] 16 | 17 | [version] 18 | ^0.94.0 19 | -------------------------------------------------------------------------------- /examples/relay-hook-example/todo/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .eslintcache 3 | __generated__ 4 | yarn-error.log -------------------------------------------------------------------------------- /examples/relay-hook-example/todo/.watchmanconfig: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/relay-tools/relay-hooks/31534668ef8275e3f4fc288406319a3c5dc8b636/examples/relay-hook-example/todo/.watchmanconfig -------------------------------------------------------------------------------- /examples/relay-hook-example/todo/README.md: -------------------------------------------------------------------------------- 1 | # Relay Hooks TodoMVC 2 | 3 | ## Installation 4 | 5 | ``` 6 | yarn 7 | ``` 8 | 9 | ## Running 10 | 11 | Set up generated files: 12 | 13 | ``` 14 | yarn run update-schema 15 | yarn run build 16 | ``` 17 | 18 | Start a local server: 19 | 20 | ``` 21 | yarn run start 22 | ``` 23 | 24 | ## Developing 25 | 26 | Any changes you make to files in the `js/` directory will cause the server to 27 | automatically rebuild the app and refresh your browser. 28 | 29 | If at any time you make changes to `data/schema.js`, stop the server, 30 | regenerate `data/schema.graphql`, and restart the server: 31 | 32 | ``` 33 | yarn run update-schema 34 | yarn run build 35 | yarn run start 36 | ``` 37 | 38 | ## License 39 | 40 | This file provided by Facebook is for non-commercial testing and evaluation 41 | purposes only. Facebook reserves all rights not expressly granted. 42 | 43 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 44 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 45 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 46 | FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 47 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 48 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 49 | -------------------------------------------------------------------------------- /examples/relay-hook-example/todo/data/schema/index.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | /** 3 | * This file provided by Facebook is for non-commercial testing and evaluation 4 | * purposes only. Facebook reserves all rights not expressly granted. 5 | * 6 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 7 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 8 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 9 | * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 10 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 11 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 12 | */ 13 | 14 | import {GraphQLObjectType, GraphQLSchema} from 'graphql'; 15 | 16 | import {nodeField} from './nodes.js'; 17 | import {UserQuery} from './queries/UserQuery'; 18 | import {AddTodoMutation} from './mutations/AddTodoMutation'; 19 | import {ChangeTodoStatusMutation} from './mutations/ChangeTodoStatusMutation'; 20 | import {MarkAllTodosMutation} from './mutations/MarkAllTodosMutation'; 21 | import {RemoveCompletedTodosMutation} from './mutations/RemoveCompletedTodosMutation'; 22 | import {RemoveTodoMutation} from './mutations/RemoveTodoMutation'; 23 | import {RenameTodoMutation} from './mutations/RenameTodoMutation'; 24 | 25 | const Query = new GraphQLObjectType({ 26 | name: 'Query', 27 | fields: { 28 | user: UserQuery, 29 | node: nodeField, 30 | }, 31 | }); 32 | 33 | const Mutation = new GraphQLObjectType({ 34 | name: 'Mutation', 35 | fields: { 36 | addTodo: AddTodoMutation, 37 | changeTodoStatus: ChangeTodoStatusMutation, 38 | markAllTodos: MarkAllTodosMutation, 39 | removeCompletedTodos: RemoveCompletedTodosMutation, 40 | removeTodo: RemoveTodoMutation, 41 | renameTodo: RenameTodoMutation, 42 | }, 43 | }); 44 | 45 | export const schema = new GraphQLSchema({ 46 | query: Query, 47 | mutation: Mutation, 48 | }); 49 | -------------------------------------------------------------------------------- /examples/relay-hook-example/todo/data/schema/mutations/RemoveCompletedTodosMutation.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | /* graphql-relay doesn't export types, and isn't in flow-typed. This gets too messy */ 3 | /* eslint flowtype/require-return-type: 'off' */ 4 | /** 5 | * This file provided by Facebook is for non-commercial testing and evaluation 6 | * purposes only. Facebook reserves all rights not expressly granted. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 9 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 10 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 11 | * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 12 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 13 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 14 | */ 15 | 16 | import {mutationWithClientMutationId, toGlobalId} from 'graphql-relay'; 17 | import {GraphQLID, GraphQLList, GraphQLNonNull, GraphQLString} from 'graphql'; 18 | import {GraphQLUser} from '../nodes'; 19 | import {getUserOrThrow, removeCompletedTodos, User} from '../../database'; 20 | 21 | type Input = {| 22 | +userId: string, 23 | |}; 24 | 25 | type Payload = {| 26 | +deletedTodoIds: $ReadOnlyArray, 27 | +userId: string, 28 | |}; 29 | 30 | const RemoveCompletedTodosMutation = mutationWithClientMutationId({ 31 | name: 'RemoveCompletedTodos', 32 | inputFields: { 33 | userId: {type: new GraphQLNonNull(GraphQLID)}, 34 | }, 35 | outputFields: { 36 | deletedTodoIds: { 37 | type: new GraphQLList(new GraphQLNonNull(GraphQLString)), 38 | resolve: ({deletedTodoIds}: Payload): $ReadOnlyArray => 39 | deletedTodoIds, 40 | }, 41 | user: { 42 | type: new GraphQLNonNull(GraphQLUser), 43 | resolve: ({userId}: Payload): User => getUserOrThrow(userId), 44 | }, 45 | }, 46 | mutateAndGetPayload: ({userId}: Input): Payload => { 47 | const deletedTodoLocalIds = removeCompletedTodos(userId); 48 | 49 | const deletedTodoIds = deletedTodoLocalIds.map( 50 | toGlobalId.bind(null, 'Todo'), 51 | ); 52 | 53 | return {deletedTodoIds, userId}; 54 | }, 55 | }); 56 | 57 | export {RemoveCompletedTodosMutation}; 58 | -------------------------------------------------------------------------------- /examples/relay-hook-example/todo/data/schema/mutations/RemoveTodoMutation.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | /* graphql-relay doesn't export types, and isn't in flow-typed. This gets too messy */ 3 | /* eslint flowtype/require-return-type: 'off' */ 4 | /** 5 | * This file provided by Facebook is for non-commercial testing and evaluation 6 | * purposes only. Facebook reserves all rights not expressly granted. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 9 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 10 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 11 | * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 12 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 13 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 14 | */ 15 | 16 | import {mutationWithClientMutationId, fromGlobalId} from 'graphql-relay'; 17 | import {GraphQLID, GraphQLNonNull} from 'graphql'; 18 | import {GraphQLUser} from '../nodes'; 19 | import {getUserOrThrow, removeTodo, User} from '../../database'; 20 | 21 | type Input = {| 22 | +id: string, 23 | +userId: string, 24 | |}; 25 | 26 | type Payload = {| 27 | +id: string, 28 | +userId: string, 29 | |}; 30 | 31 | const RemoveTodoMutation = mutationWithClientMutationId({ 32 | name: 'RemoveTodo', 33 | inputFields: { 34 | id: {type: new GraphQLNonNull(GraphQLID)}, 35 | userId: {type: new GraphQLNonNull(GraphQLID)}, 36 | }, 37 | outputFields: { 38 | deletedTodoId: { 39 | type: new GraphQLNonNull(GraphQLID), 40 | resolve: ({id}: Payload): string => id, 41 | }, 42 | user: { 43 | type: new GraphQLNonNull(GraphQLUser), 44 | resolve: ({userId}: Payload): User => getUserOrThrow(userId), 45 | }, 46 | }, 47 | mutateAndGetPayload: ({id, userId}: Input): Payload => { 48 | const localTodoId = fromGlobalId(id).id; 49 | removeTodo(localTodoId, userId); 50 | 51 | return {id, userId}; 52 | }, 53 | }); 54 | 55 | export {RemoveTodoMutation}; 56 | -------------------------------------------------------------------------------- /examples/relay-hook-example/todo/data/schema/mutations/RenameTodoMutation.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | /* graphql-relay doesn't export types, and isn't in flow-typed. This gets too messy */ 3 | /* eslint flowtype/require-return-type: 'off' */ 4 | /** 5 | * This file provided by Facebook is for non-commercial testing and evaluation 6 | * purposes only. Facebook reserves all rights not expressly granted. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 9 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 10 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 11 | * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 12 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 13 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 14 | */ 15 | 16 | import {mutationWithClientMutationId, fromGlobalId} from 'graphql-relay'; 17 | import {GraphQLID, GraphQLNonNull, GraphQLString} from 'graphql'; 18 | import {GraphQLTodo} from '../nodes'; 19 | import {getTodoOrThrow, renameTodo, Todo} from '../../database'; 20 | 21 | type Input = {| 22 | +id: string, 23 | +text: string, 24 | |}; 25 | 26 | type Payload = {| 27 | +localTodoId: string, 28 | |}; 29 | 30 | const RenameTodoMutation = mutationWithClientMutationId({ 31 | name: 'RenameTodo', 32 | inputFields: { 33 | id: {type: new GraphQLNonNull(GraphQLID)}, 34 | text: {type: new GraphQLNonNull(GraphQLString)}, 35 | }, 36 | outputFields: { 37 | todo: { 38 | type: new GraphQLNonNull(GraphQLTodo), 39 | resolve: ({localTodoId}: Payload): Todo => getTodoOrThrow(localTodoId), 40 | }, 41 | }, 42 | mutateAndGetPayload: ({id, text}: Input): Payload => { 43 | const localTodoId = fromGlobalId(id).id; 44 | renameTodo(localTodoId, text); 45 | 46 | return {localTodoId}; 47 | }, 48 | }); 49 | 50 | export {RenameTodoMutation}; 51 | -------------------------------------------------------------------------------- /examples/relay-hook-example/todo/data/schema/queries/UserQuery.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | /* graphql-relay doesn't export types, and isn't in flow-typed. This gets too messy */ 3 | /* eslint flowtype/require-return-type: 'off' */ 4 | /** 5 | * This file provided by Facebook is for non-commercial testing and evaluation 6 | * purposes only. Facebook reserves all rights not expressly granted. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 9 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 10 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 11 | * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 12 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 13 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 14 | */ 15 | 16 | import {GraphQLString} from 'graphql'; 17 | import {GraphQLUser} from '../nodes'; 18 | import {User, getUserOrThrow} from '../../database'; 19 | 20 | type Input = { 21 | +id: string, 22 | }; 23 | 24 | const UserQuery = { 25 | type: GraphQLUser, 26 | args: { 27 | id: {type: GraphQLString}, 28 | }, 29 | resolve: (root: {}, {id}: Input): User => getUserOrThrow(id), 30 | }; 31 | 32 | export {UserQuery}; 33 | -------------------------------------------------------------------------------- /examples/relay-hook-example/todo/flow-typed/npm/@babel/node_vx.x.x.js: -------------------------------------------------------------------------------- 1 | // flow-typed signature: 9634098c001c2efa44e659b57f7d4b14 2 | // flow-typed version: <>/@babel/node_v^7.2.2/flow_v0.94.0 3 | 4 | /** 5 | * This is an autogenerated libdef stub for: 6 | * 7 | * '@babel/node' 8 | * 9 | * Fill this stub out by replacing all the `any` types. 10 | * 11 | * Once filled out, we encourage you to share your work with the 12 | * community by sending a pull request to: 13 | * https://github.com/flowtype/flow-typed 14 | */ 15 | 16 | declare module '@babel/node' { 17 | declare module.exports: any; 18 | } 19 | 20 | /** 21 | * We include stubs for each file inside this npm package in case you need to 22 | * require those files directly. Feel free to delete any files that aren't 23 | * needed. 24 | */ 25 | declare module '@babel/node/bin/babel-node' { 26 | declare module.exports: any; 27 | } 28 | 29 | declare module '@babel/node/lib/_babel-node' { 30 | declare module.exports: any; 31 | } 32 | 33 | declare module '@babel/node/lib/babel-node' { 34 | declare module.exports: any; 35 | } 36 | 37 | // Filename aliases 38 | declare module '@babel/node/bin/babel-node.js' { 39 | declare module.exports: $Exports<'@babel/node/bin/babel-node'>; 40 | } 41 | declare module '@babel/node/lib/_babel-node.js' { 42 | declare module.exports: $Exports<'@babel/node/lib/_babel-node'>; 43 | } 44 | declare module '@babel/node/lib/babel-node.js' { 45 | declare module.exports: $Exports<'@babel/node/lib/babel-node'>; 46 | } 47 | -------------------------------------------------------------------------------- /examples/relay-hook-example/todo/flow-typed/npm/@babel/plugin-proposal-class-properties_vx.x.x.js: -------------------------------------------------------------------------------- 1 | // flow-typed signature: 61486cb068ce21681d4e2d2640db10f1 2 | // flow-typed version: <>/@babel/plugin-proposal-class-properties_v^7.3.4/flow_v0.94.0 3 | 4 | /** 5 | * This is an autogenerated libdef stub for: 6 | * 7 | * '@babel/plugin-proposal-class-properties' 8 | * 9 | * Fill this stub out by replacing all the `any` types. 10 | * 11 | * Once filled out, we encourage you to share your work with the 12 | * community by sending a pull request to: 13 | * https://github.com/flowtype/flow-typed 14 | */ 15 | 16 | declare module '@babel/plugin-proposal-class-properties' { 17 | declare module.exports: any; 18 | } 19 | 20 | /** 21 | * We include stubs for each file inside this npm package in case you need to 22 | * require those files directly. Feel free to delete any files that aren't 23 | * needed. 24 | */ 25 | declare module '@babel/plugin-proposal-class-properties/lib/index' { 26 | declare module.exports: any; 27 | } 28 | 29 | // Filename aliases 30 | declare module '@babel/plugin-proposal-class-properties/lib/index.js' { 31 | declare module.exports: $Exports<'@babel/plugin-proposal-class-properties/lib/index'>; 32 | } 33 | -------------------------------------------------------------------------------- /examples/relay-hook-example/todo/flow-typed/npm/@babel/plugin-transform-runtime_vx.x.x.js: -------------------------------------------------------------------------------- 1 | // flow-typed signature: 3bbc69696c5abbcf0994313535fff166 2 | // flow-typed version: <>/@babel/plugin-transform-runtime_v^7.3.4/flow_v0.94.0 3 | 4 | /** 5 | * This is an autogenerated libdef stub for: 6 | * 7 | * '@babel/plugin-transform-runtime' 8 | * 9 | * Fill this stub out by replacing all the `any` types. 10 | * 11 | * Once filled out, we encourage you to share your work with the 12 | * community by sending a pull request to: 13 | * https://github.com/flowtype/flow-typed 14 | */ 15 | 16 | declare module '@babel/plugin-transform-runtime' { 17 | declare module.exports: any; 18 | } 19 | 20 | /** 21 | * We include stubs for each file inside this npm package in case you need to 22 | * require those files directly. Feel free to delete any files that aren't 23 | * needed. 24 | */ 25 | declare module '@babel/plugin-transform-runtime/lib/definitions' { 26 | declare module.exports: any; 27 | } 28 | 29 | declare module '@babel/plugin-transform-runtime/lib/index' { 30 | declare module.exports: any; 31 | } 32 | 33 | // Filename aliases 34 | declare module '@babel/plugin-transform-runtime/lib/definitions.js' { 35 | declare module.exports: $Exports<'@babel/plugin-transform-runtime/lib/definitions'>; 36 | } 37 | declare module '@babel/plugin-transform-runtime/lib/index.js' { 38 | declare module.exports: $Exports<'@babel/plugin-transform-runtime/lib/index'>; 39 | } 40 | -------------------------------------------------------------------------------- /examples/relay-hook-example/todo/flow-typed/npm/@babel/preset-flow_vx.x.x.js: -------------------------------------------------------------------------------- 1 | // flow-typed signature: ad96fb37691315cf15ed6c2a7d890024 2 | // flow-typed version: <>/@babel/preset-flow_v^7.0.0/flow_v0.94.0 3 | 4 | /** 5 | * This is an autogenerated libdef stub for: 6 | * 7 | * '@babel/preset-flow' 8 | * 9 | * Fill this stub out by replacing all the `any` types. 10 | * 11 | * Once filled out, we encourage you to share your work with the 12 | * community by sending a pull request to: 13 | * https://github.com/flowtype/flow-typed 14 | */ 15 | 16 | declare module '@babel/preset-flow' { 17 | declare module.exports: any; 18 | } 19 | 20 | /** 21 | * We include stubs for each file inside this npm package in case you need to 22 | * require those files directly. Feel free to delete any files that aren't 23 | * needed. 24 | */ 25 | declare module '@babel/preset-flow/lib/index' { 26 | declare module.exports: any; 27 | } 28 | 29 | // Filename aliases 30 | declare module '@babel/preset-flow/lib/index.js' { 31 | declare module.exports: $Exports<'@babel/preset-flow/lib/index'>; 32 | } 33 | -------------------------------------------------------------------------------- /examples/relay-hook-example/todo/flow-typed/npm/@babel/preset-react_vx.x.x.js: -------------------------------------------------------------------------------- 1 | // flow-typed signature: e7347ed70897733b4d136cb0cb8ae5c4 2 | // flow-typed version: <>/@babel/preset-react_v^7.0.0/flow_v0.94.0 3 | 4 | /** 5 | * This is an autogenerated libdef stub for: 6 | * 7 | * '@babel/preset-react' 8 | * 9 | * Fill this stub out by replacing all the `any` types. 10 | * 11 | * Once filled out, we encourage you to share your work with the 12 | * community by sending a pull request to: 13 | * https://github.com/flowtype/flow-typed 14 | */ 15 | 16 | declare module '@babel/preset-react' { 17 | declare module.exports: any; 18 | } 19 | 20 | /** 21 | * We include stubs for each file inside this npm package in case you need to 22 | * require those files directly. Feel free to delete any files that aren't 23 | * needed. 24 | */ 25 | declare module '@babel/preset-react/lib/index' { 26 | declare module.exports: any; 27 | } 28 | 29 | // Filename aliases 30 | declare module '@babel/preset-react/lib/index.js' { 31 | declare module.exports: $Exports<'@babel/preset-react/lib/index'>; 32 | } 33 | -------------------------------------------------------------------------------- /examples/relay-hook-example/todo/flow-typed/npm/babel-loader_vx.x.x.js: -------------------------------------------------------------------------------- 1 | // flow-typed signature: 67633ab172e0306588506ec8c9dfb0c5 2 | // flow-typed version: <>/babel-loader_v^8.0.5/flow_v0.94.0 3 | 4 | /** 5 | * This is an autogenerated libdef stub for: 6 | * 7 | * 'babel-loader' 8 | * 9 | * Fill this stub out by replacing all the `any` types. 10 | * 11 | * Once filled out, we encourage you to share your work with the 12 | * community by sending a pull request to: 13 | * https://github.com/flowtype/flow-typed 14 | */ 15 | 16 | declare module 'babel-loader' { 17 | declare module.exports: any; 18 | } 19 | 20 | /** 21 | * We include stubs for each file inside this npm package in case you need to 22 | * require those files directly. Feel free to delete any files that aren't 23 | * needed. 24 | */ 25 | declare module 'babel-loader/lib/cache' { 26 | declare module.exports: any; 27 | } 28 | 29 | declare module 'babel-loader/lib/Error' { 30 | declare module.exports: any; 31 | } 32 | 33 | declare module 'babel-loader/lib/index' { 34 | declare module.exports: any; 35 | } 36 | 37 | declare module 'babel-loader/lib/injectCaller' { 38 | declare module.exports: any; 39 | } 40 | 41 | declare module 'babel-loader/lib/transform' { 42 | declare module.exports: any; 43 | } 44 | 45 | // Filename aliases 46 | declare module 'babel-loader/lib/cache.js' { 47 | declare module.exports: $Exports<'babel-loader/lib/cache'>; 48 | } 49 | declare module 'babel-loader/lib/Error.js' { 50 | declare module.exports: $Exports<'babel-loader/lib/Error'>; 51 | } 52 | declare module 'babel-loader/lib/index.js' { 53 | declare module.exports: $Exports<'babel-loader/lib/index'>; 54 | } 55 | declare module 'babel-loader/lib/injectCaller.js' { 56 | declare module.exports: $Exports<'babel-loader/lib/injectCaller'>; 57 | } 58 | declare module 'babel-loader/lib/transform.js' { 59 | declare module.exports: $Exports<'babel-loader/lib/transform'>; 60 | } 61 | -------------------------------------------------------------------------------- /examples/relay-hook-example/todo/flow-typed/npm/classnames_v2.x.x.js: -------------------------------------------------------------------------------- 1 | // flow-typed signature: cf86673cc32d185bdab1d2ea90578d37 2 | // flow-typed version: 614bf49aa8/classnames_v2.x.x/flow_>=v0.25.x 3 | 4 | type $npm$classnames$Classes = 5 | | string 6 | | { [className: string]: * } 7 | | false 8 | | void 9 | | null; 10 | 11 | declare module "classnames" { 12 | declare module.exports: ( 13 | ...classes: Array<$npm$classnames$Classes | $npm$classnames$Classes[]> 14 | ) => string; 15 | } 16 | 17 | declare module "classnames/bind" { 18 | declare module.exports: $Exports<"classnames">; 19 | } 20 | 21 | declare module "classnames/dedupe" { 22 | declare module.exports: $Exports<"classnames">; 23 | } 24 | -------------------------------------------------------------------------------- /examples/relay-hook-example/todo/flow-typed/npm/eslint-config-fbjs_vx.x.x.js: -------------------------------------------------------------------------------- 1 | // flow-typed signature: a0b153f1bc4939065c8880ccac1a347b 2 | // flow-typed version: <>/eslint-config-fbjs_v^2.1.0/flow_v0.94.0 3 | 4 | /** 5 | * This is an autogenerated libdef stub for: 6 | * 7 | * 'eslint-config-fbjs' 8 | * 9 | * Fill this stub out by replacing all the `any` types. 10 | * 11 | * Once filled out, we encourage you to share your work with the 12 | * community by sending a pull request to: 13 | * https://github.com/flowtype/flow-typed 14 | */ 15 | 16 | declare module 'eslint-config-fbjs' { 17 | declare module.exports: any; 18 | } 19 | 20 | /** 21 | * We include stubs for each file inside this npm package in case you need to 22 | * require those files directly. Feel free to delete any files that aren't 23 | * needed. 24 | */ 25 | declare module 'eslint-config-fbjs/shared' { 26 | declare module.exports: any; 27 | } 28 | 29 | declare module 'eslint-config-fbjs/strict' { 30 | declare module.exports: any; 31 | } 32 | 33 | declare module 'eslint-config-fbjs/utils/change-error-level' { 34 | declare module.exports: any; 35 | } 36 | 37 | // Filename aliases 38 | declare module 'eslint-config-fbjs/index' { 39 | declare module.exports: $Exports<'eslint-config-fbjs'>; 40 | } 41 | declare module 'eslint-config-fbjs/index.js' { 42 | declare module.exports: $Exports<'eslint-config-fbjs'>; 43 | } 44 | declare module 'eslint-config-fbjs/shared.js' { 45 | declare module.exports: $Exports<'eslint-config-fbjs/shared'>; 46 | } 47 | declare module 'eslint-config-fbjs/strict.js' { 48 | declare module.exports: $Exports<'eslint-config-fbjs/strict'>; 49 | } 50 | declare module 'eslint-config-fbjs/utils/change-error-level.js' { 51 | declare module.exports: $Exports<'eslint-config-fbjs/utils/change-error-level'>; 52 | } 53 | -------------------------------------------------------------------------------- /examples/relay-hook-example/todo/flow-typed/npm/eslint-plugin-prettier_vx.x.x.js: -------------------------------------------------------------------------------- 1 | // flow-typed signature: e32cedd2724ba2746e9c84638bafafcf 2 | // flow-typed version: <>/eslint-plugin-prettier_v^3.0.1/flow_v0.94.0 3 | 4 | /** 5 | * This is an autogenerated libdef stub for: 6 | * 7 | * 'eslint-plugin-prettier' 8 | * 9 | * Fill this stub out by replacing all the `any` types. 10 | * 11 | * Once filled out, we encourage you to share your work with the 12 | * community by sending a pull request to: 13 | * https://github.com/flowtype/flow-typed 14 | */ 15 | 16 | declare module 'eslint-plugin-prettier' { 17 | declare module.exports: any; 18 | } 19 | 20 | /** 21 | * We include stubs for each file inside this npm package in case you need to 22 | * require those files directly. Feel free to delete any files that aren't 23 | * needed. 24 | */ 25 | declare module 'eslint-plugin-prettier/eslint-plugin-prettier' { 26 | declare module.exports: any; 27 | } 28 | 29 | // Filename aliases 30 | declare module 'eslint-plugin-prettier/eslint-plugin-prettier.js' { 31 | declare module.exports: $Exports<'eslint-plugin-prettier/eslint-plugin-prettier'>; 32 | } 33 | -------------------------------------------------------------------------------- /examples/relay-hook-example/todo/flow-typed/npm/flow-bin_v0.x.x.js: -------------------------------------------------------------------------------- 1 | // flow-typed signature: 6a5610678d4b01e13bbfbbc62bdaf583 2 | // flow-typed version: 3817bc6980/flow-bin_v0.x.x/flow_>=v0.25.x 3 | 4 | declare module "flow-bin" { 5 | declare module.exports: string; 6 | } 7 | -------------------------------------------------------------------------------- /examples/relay-hook-example/todo/flow-typed/npm/prop-types_v15.x.x.js: -------------------------------------------------------------------------------- 1 | // flow-typed signature: d9a983bb1ac458a256c31c139047bdbb 2 | // flow-typed version: 927687984d/prop-types_v15.x.x/flow_>=v0.41.x 3 | 4 | type $npm$propTypes$ReactPropsCheckType = ( 5 | props: any, 6 | propName: string, 7 | componentName: string, 8 | href?: string) => ?Error; 9 | 10 | declare module 'prop-types' { 11 | declare var array: React$PropType$Primitive>; 12 | declare var bool: React$PropType$Primitive; 13 | declare var func: React$PropType$Primitive; 14 | declare var number: React$PropType$Primitive; 15 | declare var object: React$PropType$Primitive; 16 | declare var string: React$PropType$Primitive; 17 | declare var symbol: React$PropType$Primitive; 18 | declare var any: React$PropType$Primitive; 19 | declare var arrayOf: React$PropType$ArrayOf; 20 | declare var element: React$PropType$Primitive; /* TODO */ 21 | declare var instanceOf: React$PropType$InstanceOf; 22 | declare var node: React$PropType$Primitive; /* TODO */ 23 | declare var objectOf: React$PropType$ObjectOf; 24 | declare var oneOf: React$PropType$OneOf; 25 | declare var oneOfType: React$PropType$OneOfType; 26 | declare var shape: React$PropType$Shape; 27 | 28 | declare function checkPropTypes( 29 | propTypes: $Subtype<{[_: $Keys]: $npm$propTypes$ReactPropsCheckType}>, 30 | values: V, 31 | location: string, 32 | componentName: string, 33 | getStack: ?(() => ?string) 34 | ) : void; 35 | } 36 | -------------------------------------------------------------------------------- /examples/relay-hook-example/todo/flow-typed/npm/todomvc-app-css_vx.x.x.js: -------------------------------------------------------------------------------- 1 | // flow-typed signature: f08666ad0b00d16b281e58074c2c3ab5 2 | // flow-typed version: <>/todomvc-app-css_v^2.2.0/flow_v0.94.0 3 | 4 | /** 5 | * This is an autogenerated libdef stub for: 6 | * 7 | * 'todomvc-app-css' 8 | * 9 | * Fill this stub out by replacing all the `any` types. 10 | * 11 | * Once filled out, we encourage you to share your work with the 12 | * community by sending a pull request to: 13 | * https://github.com/flowtype/flow-typed 14 | */ 15 | 16 | declare module 'todomvc-app-css' { 17 | declare module.exports: any; 18 | } 19 | -------------------------------------------------------------------------------- /examples/relay-hook-example/todo/flow-typed/npm/todomvc-common_vx.x.x.js: -------------------------------------------------------------------------------- 1 | // flow-typed signature: d016a5da0105356bd7cb548b577f720c 2 | // flow-typed version: <>/todomvc-common_v^1.0.3/flow_v0.94.0 3 | 4 | /** 5 | * This is an autogenerated libdef stub for: 6 | * 7 | * 'todomvc-common' 8 | * 9 | * Fill this stub out by replacing all the `any` types. 10 | * 11 | * Once filled out, we encourage you to share your work with the 12 | * community by sending a pull request to: 13 | * https://github.com/flowtype/flow-typed 14 | */ 15 | 16 | declare module 'todomvc-common' { 17 | declare module.exports: any; 18 | } 19 | 20 | /** 21 | * We include stubs for each file inside this npm package in case you need to 22 | * require those files directly. Feel free to delete any files that aren't 23 | * needed. 24 | */ 25 | declare module 'todomvc-common/base' { 26 | declare module.exports: any; 27 | } 28 | 29 | // Filename aliases 30 | declare module 'todomvc-common/base.js' { 31 | declare module.exports: $Exports<'todomvc-common/base'>; 32 | } 33 | -------------------------------------------------------------------------------- /examples/relay-hook-example/todo/html.js: -------------------------------------------------------------------------------- 1 | export default ({ body, title }) => { 2 | return ` 3 | 4 | 5 | 6 | 7 | 8 | ${title} 9 | 10 | 11 | 12 | 13 |
${body}
14 | 15 | 16 | 17 | 18 | `; 19 | }; -------------------------------------------------------------------------------- /examples/relay-hook-example/todo/js/index.js: -------------------------------------------------------------------------------- 1 | import App from './app' 2 | import * as React from 'react'; 3 | import ReactDOM from 'react-dom'; 4 | 5 | const rootElement = document.getElementById('root'); 6 | 7 | if (rootElement) { 8 | ReactDOM.render( 9 | App, 10 | rootElement, 11 | ); 12 | } -------------------------------------------------------------------------------- /examples/relay-hook-example/todo/js/mutations/RenameTodoMutation.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | /** 3 | * This file provided by Facebook is for non-commercial testing and evaluation 4 | * purposes only. Facebook reserves all rights not expressly granted. 5 | * 6 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 7 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 8 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 9 | * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 10 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 11 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 12 | */ 13 | 14 | import { 15 | graphql, 16 | type Disposable, 17 | type Environment, 18 | } from 'react-relay'; 19 | import { useMutation } from 'relay-hooks'; 20 | 21 | import type {Todo_todo} from 'relay/Todo_todo.graphql'; 22 | 23 | import type { 24 | RenameTodoInput, 25 | RenameTodoMutationResponse, 26 | } from 'relay/RenameTodoMutation.graphql'; 27 | 28 | export const mutation = graphql` 29 | mutation RenameTodoMutation($input: RenameTodoInput!) { 30 | renameTodo(input: $input) { 31 | todo { 32 | id 33 | text 34 | } 35 | } 36 | } 37 | `; 38 | 39 | function getOptimisticResponse( 40 | text: string, 41 | todo: Todo_todo, 42 | ): RenameTodoMutationResponse { 43 | return { 44 | renameTodo: { 45 | todo: { 46 | id: todo.id, 47 | text: text, 48 | }, 49 | }, 50 | }; 51 | } 52 | 53 | function commit(mutate, 54 | text: string, 55 | todo: Todo_todo, 56 | ): Disposable { 57 | const input: RenameTodoInput = { 58 | text, 59 | id: todo.id, 60 | }; 61 | 62 | return mutate({ 63 | variables: { 64 | input, 65 | }, 66 | optimisticResponse: getOptimisticResponse(text, todo), 67 | }); 68 | } 69 | 70 | export default {commit}; 71 | -------------------------------------------------------------------------------- /examples/relay-hook-example/todo/js/query/QueryApp.js: -------------------------------------------------------------------------------- 1 | const QueryApp = graphql` 2 | query QueryAppQuery($userId: String) { 3 | user(id: $userId) { 4 | ...TodoApp_user 5 | } 6 | } 7 | `; 8 | 9 | export default QueryApp; -------------------------------------------------------------------------------- /examples/relay-hook-example/todo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "start": "babel-node ./server.js", 5 | "build": "relay-compiler", 6 | "flow-typed": "flow-typed install --flowVersion=0.94.0", 7 | "update-schema": "babel-node ./scripts/updateSchema.js", 8 | "lint": "eslint ./ --cache" 9 | }, 10 | "dependencies": { 11 | "classnames": "2.2.6", 12 | "express": "^4.16.4", 13 | "express-graphql": "^0.7.1", 14 | "graphql": "^14.2.0", 15 | "graphql-relay": "^0.6.0", 16 | "prop-types": "^15.7.2", 17 | "react": "^16.12.0", 18 | "react-dom": "^16.8.4", 19 | "relay-runtime": "15.0.0", 20 | "relay-hooks": "8.0.1", 21 | "whatwg-fetch": "3.0.0", 22 | "isomorphic-fetch": "2.2.1", 23 | "es6-promise": "4.2.8", 24 | "react-relay": "^12.0.0" 25 | }, 26 | "devDependencies": { 27 | "@babel/core": "^7.3.4", 28 | "@babel/node": "^7.2.2", 29 | "@babel/plugin-proposal-class-properties": "^7.3.4", 30 | "@babel/plugin-transform-runtime": "^7.3.4", 31 | "@babel/preset-env": "^7.3.4", 32 | "@babel/preset-flow": "^7.0.0", 33 | "@babel/preset-react": "^7.0.0", 34 | "@babel/runtime": "^7.3.4", 35 | "babel-eslint": "^10.0.1", 36 | "babel-loader": "^8.0.5", 37 | "babel-plugin-relay": "12.0.0", 38 | "eslint": "^5.13.0", 39 | "eslint-config-fbjs": "^2.1.0", 40 | "eslint-config-prettier": "^4.0.0", 41 | "eslint-plugin-babel": "^5.3.0", 42 | "eslint-plugin-flowtype": "^3.2.1", 43 | "eslint-plugin-jsx-a11y": "^6.2.1", 44 | "eslint-plugin-prettier": "^3.0.1", 45 | "eslint-plugin-react": "^7.12.4", 46 | "eslint-plugin-relay": "^1.0.0", 47 | "flow-bin": "^0.94.0", 48 | "flow-typed": "^2.5.1", 49 | "prettier": "^1.16.4", 50 | "relay-compiler": "15.0.0", 51 | "webpack": "^5.56.1", 52 | "webpack-dev-server": "^3.2.1" 53 | }, 54 | "prettier": { 55 | "singleQuote": true, 56 | "trailingComma": "all", 57 | "bracketSpacing": false, 58 | "jsxBracketSameLine": true 59 | }, 60 | "relay": { 61 | "src": "./js/", 62 | "schema": "./data/schema.graphql", 63 | "artifactDirectory": "./__generated__/relay", 64 | "language": "javascript" 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /examples/relay-hook-example/todo/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Relay • TodoMVC 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /examples/relay-hook-example/todo/scripts/updateSchema.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env babel-node 2 | // @flow 3 | /** 4 | * This file provided by Facebook is for non-commercial testing and evaluation 5 | * purposes only. Facebook reserves all rights not expressly granted. 6 | * 7 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 8 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 9 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 10 | * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 11 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 12 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 13 | */ 14 | 15 | import fs from 'fs'; 16 | import path from 'path'; 17 | import {schema} from '../data/schema'; 18 | import {printSchema} from 'graphql'; 19 | 20 | const schemaPath = path.resolve(__dirname, '../data/schema.graphql'); 21 | 22 | fs.writeFileSync(schemaPath, printSchema(schema)); 23 | 24 | console.log('Wrote ' + schemaPath); 25 | -------------------------------------------------------------------------------- /examples/relay-hook-example/todo/types/relay-runtime_types.js: -------------------------------------------------------------------------------- 1 | declare module 'relay-runtime' { 2 | declare export var ConnectionHandler: any; 3 | declare export var RecordSource: any; 4 | declare export var Store: any; 5 | declare export var Environment: any; 6 | declare export var Network: any; 7 | 8 | declare export type RequestNode = any; 9 | declare export type Variables = any; 10 | declare export type ReaderFragment = any; 11 | declare export type ConcreteRequest = any; 12 | declare export opaque type FragmentReference; 13 | } 14 | -------------------------------------------------------------------------------- /examples/suspense/nextjs-ssr-preload/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["next/babel"], 3 | "plugins": [ 4 | ["relay", {"artifactDirectory": "./__generated__/relay/"}], 5 | "@babel/plugin-transform-runtime", 6 | "@babel/plugin-proposal-class-properties", 7 | ["styled-components", {"ssr": true}] 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /examples/suspense/nextjs-ssr-preload/.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | lib 3 | flow-typed 4 | types 5 | __generated__ 6 | -------------------------------------------------------------------------------- /examples/suspense/nextjs-ssr-preload/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: [ 3 | 'fbjs/strict', 4 | 'plugin:flowtype/recommended', 5 | 'plugin:prettier/recommended', 6 | 'plugin:react/recommended', 7 | 'plugin:relay/recommended', 8 | 'prettier/flowtype', 9 | 'prettier/react', 10 | ], 11 | parser: 'babel-eslint', 12 | plugins: ['babel', 'flowtype', 'prettier', 'react', 'relay'], 13 | rules: { 14 | 'no-console': 'off', 15 | 'one-var': 'off', 16 | 'react/prop-types': 'off', // Replaced by flow types 17 | 18 | // Mutations aren't located in the same file as Components 19 | 'relay/unused-fields': 'off', 20 | 21 | // Strict Flow linting 22 | 'flowtype/array-style-complex-type': 'error', 23 | 'flowtype/array-style-simple-type': 'error', 24 | 'flowtype/newline-after-flow-annotation': ['error', 'never'], 25 | 'flowtype/no-dupe-keys': 'error', 26 | 'flowtype/no-existential-type': 'error', 27 | 'flowtype/no-mutable-array': 'error', 28 | 'flowtype/no-primitive-constructor-types': 'error', 29 | 'flowtype/no-unused-expressions': 'error', 30 | 'flowtype/no-weak-types': 'error', 31 | 'flowtype/require-parameter-type': 'error', 32 | 'flowtype/require-return-type': 'off', // Too strict 33 | 'flowtype/require-types-at-top': 'error', 34 | 'flowtype/require-valid-file-annotation': 'error', 35 | 'flowtype/require-variable-type': 'off', // Too strict 36 | 'flowtype/sort-keys': 'error', 37 | }, 38 | settings: { 39 | react: { 40 | version: '16.8.1', 41 | flowVersion: '0.94.0', 42 | } 43 | }, 44 | }; 45 | -------------------------------------------------------------------------------- /examples/suspense/nextjs-ssr-preload/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .eslintcache 3 | .next 4 | node_modules 5 | __generated__ 6 | -------------------------------------------------------------------------------- /examples/suspense/nextjs-ssr-preload/.watchmanconfig: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/relay-tools/relay-hooks/31534668ef8275e3f4fc288406319a3c5dc8b636/examples/suspense/nextjs-ssr-preload/.watchmanconfig -------------------------------------------------------------------------------- /examples/suspense/nextjs-ssr-preload/README.md: -------------------------------------------------------------------------------- 1 | # Relay Hooks NextJS SSR TodoMVC 2 | 3 | ## Installation 4 | 5 | ``` 6 | yarn 7 | ``` 8 | 9 | ## Running 10 | 11 | Set up generated files: 12 | 13 | ``` 14 | yarn 15 | yarn compile 16 | ``` 17 | 18 | Start a local server: 19 | 20 | ``` 21 | yarn dev 22 | ``` 23 | 24 | ## difference with nextjs-ssr-example no suspense 25 | 26 | ### createRelayEnvironment.ts 27 | 28 | ```ts 29 | // suspense 30 | import {loadLazyQuery} from 'relay-hooks'; 31 | const prefetch = loadLazyQuery(); 32 | 33 | // no suspense 34 | import {loadQuery} from 'relay-hooks'; 35 | const prefetch = loadQuery(); 36 | ``` 37 | -------------------------------------------------------------------------------- /examples/suspense/nextjs-ssr-preload/components/Header.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Link from 'next/link'; 3 | import styled, {css} from 'styled-components'; 4 | import {withRouter} from 'next/router'; 5 | 6 | const StyledButton = styled.button` 7 | margin: auto; 8 | padding: 10px; 9 | cursor: pointer; 10 | display: -webkit-box; 11 | flex: 1; 12 | ${props => 13 | props.selected && 14 | css` 15 | border: 1px solid #999; 16 | box-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2); 17 | box-sizing: border-box; 18 | `} 19 | `; 20 | 21 | const MyButton = React.forwardRef( 22 | ({onClick, href, children, selected}: any, ref) => ( 23 | 24 | {children} 25 | 26 | ), 27 | ); 28 | 29 | const StyledDiv = styled.div` 30 | display: flex; 31 | background: #fff; 32 | box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), 0 25px 50px 0 rgba(0, 0, 0, 0.1); 33 | `; 34 | 35 | const Header = ({userId}) => { 36 | const selectedYou = userId === 'you'; 37 | return ( 38 | 39 | 40 | ME 41 | 42 | 43 | YOU 44 | 45 | 46 | ); 47 | }; 48 | 49 | export default Header; 50 | -------------------------------------------------------------------------------- /examples/suspense/nextjs-ssr-preload/components/Home.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import TodoApp, { QUERY_APP } from './TodoApp'; 3 | import { usePreloadedQuery, useQuery } from 'relay-hooks'; 4 | import { TodoAppQuery } from '../__generated__/relay/TodoAppQuery.graphql'; 5 | 6 | const Home = ({ prefetch }) => { 7 | const { data, retry } = usePreloadedQuery(prefetch); 8 | if (!data) { 9 | return
no data || skip
; 10 | } 11 | 12 | return ; 13 | }; 14 | 15 | export default Home; 16 | -------------------------------------------------------------------------------- /examples/suspense/nextjs-ssr-preload/data/schema/index.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | /** 3 | * This file provided by Facebook is for non-commercial testing and evaluation 4 | * purposes only. Facebook reserves all rights not expressly granted. 5 | * 6 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 7 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 8 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 9 | * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 10 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 11 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 12 | */ 13 | 14 | import {GraphQLObjectType, GraphQLSchema} from 'graphql'; 15 | 16 | import {nodeField} from './nodes.js'; 17 | import {UserQuery} from './queries/UserQuery'; 18 | import {AddTodoMutation} from './mutations/AddTodoMutation'; 19 | import {ChangeTodoStatusMutation} from './mutations/ChangeTodoStatusMutation'; 20 | import {MarkAllTodosMutation} from './mutations/MarkAllTodosMutation'; 21 | import {RemoveCompletedTodosMutation} from './mutations/RemoveCompletedTodosMutation'; 22 | import {RemoveTodoMutation} from './mutations/RemoveTodoMutation'; 23 | import {RenameTodoMutation} from './mutations/RenameTodoMutation'; 24 | 25 | const Query = new GraphQLObjectType({ 26 | name: 'Query', 27 | fields: { 28 | user: UserQuery, 29 | node: nodeField, 30 | }, 31 | }); 32 | 33 | const Mutation = new GraphQLObjectType({ 34 | name: 'Mutation', 35 | fields: { 36 | addTodo: AddTodoMutation, 37 | changeTodoStatus: ChangeTodoStatusMutation, 38 | markAllTodos: MarkAllTodosMutation, 39 | removeCompletedTodos: RemoveCompletedTodosMutation, 40 | removeTodo: RemoveTodoMutation, 41 | renameTodo: RenameTodoMutation, 42 | }, 43 | }); 44 | 45 | export const schema = new GraphQLSchema({ 46 | query: Query, 47 | mutation: Mutation, 48 | }); 49 | -------------------------------------------------------------------------------- /examples/suspense/nextjs-ssr-preload/data/schema/mutations/ChangeTodoStatusMutation.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | /* graphql-relay doesn't export types, and isn't in flow-typed. This gets too messy */ 3 | /* eslint flowtype/require-return-type: 'off' */ 4 | /** 5 | * This file provided by Facebook is for non-commercial testing and evaluation 6 | * purposes only. Facebook reserves all rights not expressly granted. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 9 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 10 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 11 | * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 12 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 13 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 14 | */ 15 | 16 | import {fromGlobalId, mutationWithClientMutationId} from 'graphql-relay'; 17 | import {GraphQLBoolean, GraphQLID, GraphQLNonNull} from 'graphql'; 18 | import {GraphQLTodo, GraphQLUser} from '../nodes'; 19 | import { 20 | changeTodoStatus, 21 | getTodoOrThrow, 22 | getUserOrThrow, 23 | Todo, 24 | User, 25 | } from '../../database'; 26 | 27 | const ChangeTodoStatusMutation = mutationWithClientMutationId({ 28 | name: 'ChangeTodoStatus', 29 | inputFields: { 30 | complete: {type: new GraphQLNonNull(GraphQLBoolean)}, 31 | id: {type: new GraphQLNonNull(GraphQLID)}, 32 | userId: {type: new GraphQLNonNull(GraphQLID)}, 33 | }, 34 | outputFields: { 35 | todo: { 36 | type: new GraphQLNonNull(GraphQLTodo), 37 | resolve: ({id}) => getTodoOrThrow(id), 38 | }, 39 | user: { 40 | type: new GraphQLNonNull(GraphQLUser), 41 | resolve: ({userId}) => getUserOrThrow(userId), 42 | }, 43 | }, 44 | mutateAndGetPayload: ({id, complete, userId}) => { 45 | changeTodoStatus(id, complete); 46 | 47 | return {id, userId}; 48 | }, 49 | }); 50 | 51 | export {ChangeTodoStatusMutation}; 52 | -------------------------------------------------------------------------------- /examples/suspense/nextjs-ssr-preload/data/schema/mutations/MarkAllTodosMutation.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | /* graphql-relay doesn't export types, and isn't in flow-typed. This gets too messy */ 3 | /* eslint flowtype/require-return-type: 'off' */ 4 | /** 5 | * This file provided by Facebook is for non-commercial testing and evaluation 6 | * purposes only. Facebook reserves all rights not expressly granted. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 9 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 10 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 11 | * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 12 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 13 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 14 | */ 15 | 16 | import {mutationWithClientMutationId} from 'graphql-relay'; 17 | import {GraphQLBoolean, GraphQLID, GraphQLList, GraphQLNonNull} from 'graphql'; 18 | import {GraphQLTodo, GraphQLUser} from '../nodes'; 19 | 20 | import { 21 | getTodoOrThrow, 22 | getUserOrThrow, 23 | markAllTodos, 24 | Todo, 25 | User, 26 | } from '../../database'; 27 | 28 | const MarkAllTodosMutation = mutationWithClientMutationId({ 29 | name: 'MarkAllTodos', 30 | inputFields: { 31 | complete: {type: new GraphQLNonNull(GraphQLBoolean)}, 32 | userId: {type: new GraphQLNonNull(GraphQLID)}, 33 | }, 34 | outputFields: { 35 | changedTodos: { 36 | type: new GraphQLList(new GraphQLNonNull(GraphQLTodo)), 37 | resolve: ({changedTodoIds}) => 38 | changedTodoIds.map(todoId => getTodoOrThrow(todoId)), 39 | }, 40 | user: { 41 | type: new GraphQLNonNull(GraphQLUser), 42 | resolve: ({userId}) => getUserOrThrow(userId), 43 | }, 44 | }, 45 | mutateAndGetPayload: ({complete, userId}) => { 46 | const changedTodoIds = markAllTodos(userId, complete); 47 | 48 | return {changedTodoIds, userId}; 49 | }, 50 | }); 51 | 52 | export {MarkAllTodosMutation}; 53 | -------------------------------------------------------------------------------- /examples/suspense/nextjs-ssr-preload/data/schema/mutations/RemoveCompletedTodosMutation.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | /* graphql-relay doesn't export types, and isn't in flow-typed. This gets too messy */ 3 | /* eslint flowtype/require-return-type: 'off' */ 4 | /** 5 | * This file provided by Facebook is for non-commercial testing and evaluation 6 | * purposes only. Facebook reserves all rights not expressly granted. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 9 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 10 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 11 | * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 12 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 13 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 14 | */ 15 | 16 | import {mutationWithClientMutationId, toGlobalId} from 'graphql-relay'; 17 | import {GraphQLID, GraphQLList, GraphQLNonNull, GraphQLString} from 'graphql'; 18 | import {GraphQLUser} from '../nodes'; 19 | import {getUserOrThrow, removeCompletedTodos, User} from '../../database'; 20 | 21 | const RemoveCompletedTodosMutation = mutationWithClientMutationId({ 22 | name: 'RemoveCompletedTodos', 23 | inputFields: { 24 | userId: {type: new GraphQLNonNull(GraphQLID)}, 25 | }, 26 | outputFields: { 27 | deletedTodoIds: { 28 | type: new GraphQLList(new GraphQLNonNull(GraphQLString)), 29 | resolve: ({deletedTodoIds}) => deletedTodoIds, 30 | }, 31 | user: { 32 | type: new GraphQLNonNull(GraphQLUser), 33 | resolve: ({userId}) => getUserOrThrow(userId), 34 | }, 35 | }, 36 | mutateAndGetPayload: ({userId}) => { 37 | const deletedTodoIds = removeCompletedTodos(userId); 38 | 39 | return {deletedTodoIds, userId}; 40 | }, 41 | }); 42 | 43 | export {RemoveCompletedTodosMutation}; 44 | -------------------------------------------------------------------------------- /examples/suspense/nextjs-ssr-preload/data/schema/mutations/RemoveTodoMutation.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | /* graphql-relay doesn't export types, and isn't in flow-typed. This gets too messy */ 3 | /* eslint flowtype/require-return-type: 'off' */ 4 | /** 5 | * This file provided by Facebook is for non-commercial testing and evaluation 6 | * purposes only. Facebook reserves all rights not expressly granted. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 9 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 10 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 11 | * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 12 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 13 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 14 | */ 15 | 16 | import {mutationWithClientMutationId, fromGlobalId} from 'graphql-relay'; 17 | import {GraphQLID, GraphQLNonNull} from 'graphql'; 18 | import {GraphQLUser} from '../nodes'; 19 | import {getUserOrThrow, removeTodo, User} from '../../database'; 20 | 21 | const RemoveTodoMutation = mutationWithClientMutationId({ 22 | name: 'RemoveTodo', 23 | inputFields: { 24 | id: {type: new GraphQLNonNull(GraphQLID)}, 25 | userId: {type: new GraphQLNonNull(GraphQLID)}, 26 | }, 27 | outputFields: { 28 | deletedTodoId: { 29 | type: new GraphQLNonNull(GraphQLID), 30 | resolve: ({id}) => id, 31 | }, 32 | user: { 33 | type: new GraphQLNonNull(GraphQLUser), 34 | resolve: ({userId}) => getUserOrThrow(userId), 35 | }, 36 | }, 37 | mutateAndGetPayload: ({id, userId}) => { 38 | removeTodo(id, userId); 39 | 40 | return {id, userId}; 41 | }, 42 | }); 43 | 44 | export {RemoveTodoMutation}; 45 | -------------------------------------------------------------------------------- /examples/suspense/nextjs-ssr-preload/data/schema/mutations/RenameTodoMutation.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | /* graphql-relay doesn't export types, and isn't in flow-typed. This gets too messy */ 3 | /* eslint flowtype/require-return-type: 'off' */ 4 | /** 5 | * This file provided by Facebook is for non-commercial testing and evaluation 6 | * purposes only. Facebook reserves all rights not expressly granted. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 9 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 10 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 11 | * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 12 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 13 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 14 | */ 15 | 16 | import {mutationWithClientMutationId, fromGlobalId} from 'graphql-relay'; 17 | import {GraphQLID, GraphQLNonNull, GraphQLString} from 'graphql'; 18 | import {GraphQLTodo} from '../nodes'; 19 | import {getTodoOrThrow, renameTodo, Todo} from '../../database'; 20 | 21 | const RenameTodoMutation = mutationWithClientMutationId({ 22 | name: 'RenameTodo', 23 | inputFields: { 24 | id: {type: new GraphQLNonNull(GraphQLID)}, 25 | text: {type: new GraphQLNonNull(GraphQLString)}, 26 | }, 27 | outputFields: { 28 | todo: { 29 | type: new GraphQLNonNull(GraphQLTodo), 30 | resolve: ({id}) => getTodoOrThrow(id), 31 | }, 32 | }, 33 | mutateAndGetPayload: ({id, text}) => { 34 | renameTodo(id, text); 35 | 36 | return {id}; 37 | }, 38 | }); 39 | 40 | export {RenameTodoMutation}; 41 | -------------------------------------------------------------------------------- /examples/suspense/nextjs-ssr-preload/data/schema/queries/UserQuery.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | /* graphql-relay doesn't export types, and isn't in flow-typed. This gets too messy */ 3 | /* eslint flowtype/require-return-type: 'off' */ 4 | /** 5 | * This file provided by Facebook is for non-commercial testing and evaluation 6 | * purposes only. Facebook reserves all rights not expressly granted. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 9 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 10 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 11 | * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 12 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 13 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 14 | */ 15 | 16 | import {GraphQLString} from 'graphql'; 17 | import {GraphQLUser} from '../nodes'; 18 | import {User, getUserOrThrow} from '../../database'; 19 | 20 | const UserQuery = { 21 | type: GraphQLUser, 22 | args: { 23 | id: {type: GraphQLString}, 24 | }, 25 | resolve: (root, {id}) => getUserOrThrow(id), 26 | }; 27 | 28 | export {UserQuery}; 29 | -------------------------------------------------------------------------------- /examples/suspense/nextjs-ssr-preload/html.js: -------------------------------------------------------------------------------- 1 | export default ({ body, title }) => { 2 | return ` 3 | 4 | 5 | 6 | 7 | 8 | ${title} 9 | 10 | 11 | 12 | 13 |
${body}
14 | 15 | 16 | 17 | 18 | `; 19 | }; -------------------------------------------------------------------------------- /examples/suspense/nextjs-ssr-preload/mutations/ChangeTodoStatusMutation.ts: -------------------------------------------------------------------------------- 1 | // @flow 2 | /** 3 | * This file provided by Facebook is for non-commercial testing and evaluation 4 | * purposes only. Facebook reserves all rights not expressly granted. 5 | * 6 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 7 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 8 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 9 | * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 10 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 11 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 12 | */ 13 | 14 | import {commitMutation, graphql} from 'relay-hooks'; 15 | 16 | const mutation = graphql` 17 | mutation ChangeTodoStatusMutation($input: ChangeTodoStatusInput!) { 18 | changeTodoStatus(input: $input) { 19 | todo { 20 | id 21 | complete 22 | } 23 | user { 24 | id 25 | completedCount 26 | } 27 | } 28 | } 29 | `; 30 | 31 | function getOptimisticResponse(complete: boolean, todo: any, user: any): any { 32 | return { 33 | changeTodoStatus: { 34 | todo: { 35 | complete: complete, 36 | id: todo.id, 37 | }, 38 | user: { 39 | id: user.id, 40 | completedCount: complete 41 | ? user.completedCount + 1 42 | : user.completedCount - 1, 43 | }, 44 | }, 45 | }; 46 | } 47 | 48 | function commit(environment: any, complete: boolean, todo: any, user: any) { 49 | const input: any = { 50 | complete, 51 | userId: user.userId, 52 | id: todo.id, 53 | }; 54 | commitMutation(environment, { 55 | mutation, 56 | variables: { 57 | input, 58 | }, 59 | optimisticResponse: getOptimisticResponse(complete, todo, user), 60 | }); 61 | } 62 | 63 | export default {commit}; 64 | -------------------------------------------------------------------------------- /examples/suspense/nextjs-ssr-preload/mutations/RemoveTodoMutation.ts: -------------------------------------------------------------------------------- 1 | // @flow 2 | /** 3 | * This file provided by Facebook is for non-commercial testing and evaluation 4 | * purposes only. Facebook reserves all rights not expressly granted. 5 | * 6 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 7 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 8 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 9 | * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 10 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 11 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 12 | */ 13 | 14 | import {commitMutation, graphql} from 'relay-hooks'; 15 | 16 | const mutation = graphql` 17 | mutation RemoveTodoMutation($input: RemoveTodoInput!) { 18 | removeTodo(input: $input) { 19 | deletedTodoId 20 | user { 21 | completedCount 22 | totalCount 23 | } 24 | } 25 | } 26 | `; 27 | 28 | function commit(environment: any, todo: any, user: any): any { 29 | const input: any = { 30 | id: todo.id, 31 | userId: user.userId, 32 | }; 33 | return commitMutation(environment, { 34 | mutation, 35 | variables: { 36 | input, 37 | }, 38 | configs: [ 39 | { 40 | type: 'NODE_DELETE', 41 | deletedIDFieldName: 'deletedTodoId', 42 | }, 43 | ], 44 | optimisticResponse: { 45 | removeTodo: { 46 | deletedTodoId: todo.id, 47 | user: { 48 | id: user.id, 49 | completedCount: user.completedCount - (todo.complete ? 1 : 0), 50 | totalCount: user.totalCount - 1, 51 | }, 52 | }, 53 | }, 54 | }); 55 | } 56 | 57 | export default {commit}; 58 | -------------------------------------------------------------------------------- /examples/suspense/nextjs-ssr-preload/mutations/RenameTodoMutation.ts: -------------------------------------------------------------------------------- 1 | // @flow 2 | /** 3 | * This file provided by Facebook is for non-commercial testing and evaluation 4 | * purposes only. Facebook reserves all rights not expressly granted. 5 | * 6 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 7 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 8 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 9 | * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 10 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 11 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 12 | */ 13 | 14 | import {commitMutation, graphql} from 'relay-hooks'; 15 | import {Disposable} from 'relay-runtime'; 16 | 17 | const mutation = graphql` 18 | mutation RenameTodoMutation($input: RenameTodoInput!) { 19 | renameTodo(input: $input) { 20 | todo { 21 | id 22 | text 23 | } 24 | } 25 | } 26 | `; 27 | 28 | function getOptimisticResponse(text: string, todo: any): any { 29 | return { 30 | renameTodo: { 31 | todo: { 32 | id: todo.id, 33 | text: text, 34 | }, 35 | }, 36 | }; 37 | } 38 | 39 | function commit(environment: any, text: string, todo: any): Disposable { 40 | const input: any = { 41 | text, 42 | id: todo.id, 43 | }; 44 | 45 | return commitMutation(environment, { 46 | mutation, 47 | variables: { 48 | input, 49 | }, 50 | optimisticResponse: getOptimisticResponse(text, todo), 51 | }); 52 | } 53 | 54 | export default {commit}; 55 | -------------------------------------------------------------------------------- /examples/suspense/nextjs-ssr-preload/next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | -------------------------------------------------------------------------------- /examples/suspense/nextjs-ssr-preload/next.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | webpack(config, options) { 5 | config.resolve.alias['relay'] = path.join( 6 | __dirname, 7 | '__generated__', 8 | 'relay', 9 | ); 10 | return config; 11 | }, 12 | }; 13 | -------------------------------------------------------------------------------- /examples/suspense/nextjs-ssr-preload/now.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 2, 3 | "builds": [ 4 | { 5 | "src": "next.config.js", 6 | "use": "@now/next" 7 | } 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /examples/suspense/nextjs-ssr-preload/pages/_document.tsx: -------------------------------------------------------------------------------- 1 | import Document, {Head, Main, NextScript, DocumentContext} from 'next/document'; 2 | import {createGlobalStyle, ServerStyleSheet} from 'styled-components'; 3 | import * as React from 'react'; 4 | 5 | const GlobalStyle = createGlobalStyle` 6 | 7 | body { 8 | font: 14px 'Helvetica Neue', Helvetica, Arial, sans-serif; 9 | line-height: 1.4em; 10 | background: #f5f5f5; 11 | color: #4d4d4d; 12 | min-width: 230px; 13 | max-width: 550px; 14 | flex-grow: 1; 15 | margin: 0 auto; 16 | display: flex; 17 | flex-direction: column; 18 | -webkit-font-smoothing: antialiased; 19 | -moz-osx-font-smoothing: grayscale; 20 | font-weight: 300; 21 | * { 22 | -webkit-tap-highlight-color: rgba(255, 255, 255, 0); 23 | } 24 | } 25 | 26 | #__next { 27 | display: flex; 28 | flex-direction: column; 29 | } 30 | 31 | 32 | &:focus { 33 | outline: 0; 34 | } 35 | 36 | button { 37 | margin: 0; 38 | padding: 0; 39 | border: 0; 40 | background: none; 41 | font-size: 100%; 42 | vertical-align: baseline; 43 | font-family: inherit; 44 | font-weight: inherit; 45 | color: inherit; 46 | -webkit-appearance: none; 47 | appearance: none; 48 | -webkit-font-smoothing: antialiased; 49 | -moz-osx-font-smoothing: grayscale; 50 | } 51 | 52 | `; 53 | 54 | type Props = { 55 | styleTags: string; 56 | }; 57 | 58 | export default class MyDocument extends Document { 59 | static async getInitialProps(ctx) { 60 | const sheet = new ServerStyleSheet(); 61 | 62 | const page = ctx.renderPage(App => props => { 63 | return sheet.collectStyles(); 64 | }); 65 | const styleTags = sheet.getStyleElement(); 66 | return { 67 | ...page, 68 | styleTags, 69 | }; 70 | } 71 | 72 | render() { 73 | console.log('render document'); 74 | return ( 75 | 76 | 77 | 78 | {this.props.styleTags} 79 | 80 | 81 | 82 |
83 | 84 | 85 | 86 | ); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /examples/suspense/nextjs-ssr-preload/pages/index.tsx: -------------------------------------------------------------------------------- 1 | import Home from '../components/Home'; 2 | 3 | export default Home; 4 | -------------------------------------------------------------------------------- /examples/suspense/nextjs-ssr-preload/pages/you.tsx: -------------------------------------------------------------------------------- 1 | import Home from '../components/Home'; 2 | 3 | export default Home; 4 | -------------------------------------------------------------------------------- /examples/suspense/nextjs-ssr-preload/relay/createRelayEnvironment.ts: -------------------------------------------------------------------------------- 1 | import {Network} from 'relay-runtime'; 2 | import {Store, Environment, RecordSource} from 'relay-runtime'; 3 | 4 | import fetch from 'isomorphic-unfetch'; 5 | import {loadLazyQuery} from 'relay-hooks'; 6 | 7 | let relayEnvironment: Environment; 8 | 9 | function fetchQuery(operation, variables, cacheConfig, uploadables) { 10 | const endpoint = 'http://localhost:3000/graphql'; 11 | return fetch(endpoint, { 12 | method: 'POST', 13 | headers: { 14 | Accept: 'application/json', 15 | 'Content-Type': 'application/json', 16 | }, // Add authentication and other headers here 17 | body: JSON.stringify({ 18 | query: operation.text, // GraphQL text from input 19 | variables, 20 | }), 21 | }).then(response => response.json()); 22 | } 23 | 24 | type InitProps = { 25 | records?: any; 26 | }; 27 | 28 | const network = Network.create(fetchQuery); 29 | 30 | function createEnvironment(records) { 31 | const recordSource = new RecordSource(records); 32 | const store = new Store(recordSource); 33 | const environment = new Environment({ 34 | network, 35 | store, 36 | }); 37 | return environment; 38 | } 39 | 40 | const prefetch = loadLazyQuery(); 41 | 42 | export default function initEnvironment(options: InitProps = {}) { 43 | const {records = {}} = options; 44 | 45 | if (typeof window === 'undefined') { 46 | prefetch.dispose(); 47 | return {environment: createEnvironment(records), prefetch}; 48 | } 49 | 50 | // reuse Relay environment on client-side 51 | if (!relayEnvironment) { 52 | relayEnvironment = createEnvironment(records); 53 | } 54 | return {environment: relayEnvironment, prefetch}; 55 | } 56 | -------------------------------------------------------------------------------- /examples/suspense/nextjs-ssr-preload/scripts/updateSchema.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env babel-node 2 | // @flow 3 | /** 4 | * This file provided by Facebook is for non-commercial testing and evaluation 5 | * purposes only. Facebook reserves all rights not expressly granted. 6 | * 7 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 8 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 9 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 10 | * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 11 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 12 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 13 | */ 14 | 15 | import fs from 'fs'; 16 | import path from 'path'; 17 | import {schema} from '../data/schema'; 18 | import {printSchema} from 'graphql'; 19 | 20 | const schemaPath = path.resolve(__dirname, '../data/schema.graphql'); 21 | 22 | fs.writeFileSync(schemaPath, printSchema(schema)); 23 | 24 | console.log('Wrote ' + schemaPath); 25 | -------------------------------------------------------------------------------- /examples/suspense/nextjs-ssr-preload/server.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | /** 3 | * This file provided by Facebook is for non-commercial testing and evaluation 4 | * purposes only. Facebook reserves all rights not expressly granted. 5 | * 6 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 7 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 8 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 9 | * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 10 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 11 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 12 | */ 13 | require('es6-promise').polyfill(); 14 | require('isomorphic-fetch'); 15 | import express from 'express'; 16 | import graphQLHTTP from 'express-graphql'; 17 | import {schema} from './data/schema'; 18 | import next from 'next'; 19 | 20 | const app = next({dev: process.env.NODE_ENV !== 'production'}); 21 | const handle = app.getRequestHandler(); 22 | 23 | const port = 3000; 24 | 25 | app.prepare().then(() => { 26 | const server = express(); 27 | 28 | server.use( 29 | '/graphql', 30 | graphQLHTTP({ 31 | schema, 32 | graphiql: false, 33 | pretty: true, 34 | }), 35 | ); 36 | 37 | server.get('*', (req, res) => { 38 | return handle(req, res); 39 | }); 40 | 41 | server.listen(port, err => { 42 | if (err) throw err; 43 | console.log(`> Ready on http://localhost:${port}`); 44 | }); 45 | }); 46 | -------------------------------------------------------------------------------- /examples/suspense/nextjs-ssr-preload/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowJs": true, 4 | "allowSyntheticDefaultImports": true, 5 | "esModuleInterop": true, 6 | "isolatedModules": true, 7 | "noImplicitAny": false, 8 | "jsx": "preserve", 9 | "lib": [ 10 | "dom", 11 | "es2016", 12 | "es2017.object" 13 | ], 14 | "moduleResolution": "node", 15 | "noEmit": true, 16 | "strict": true, 17 | "target": "esnext", 18 | "skipLibCheck": true, 19 | "forceConsistentCasingInFileNames": true, 20 | "module": "esnext", 21 | "resolveJsonModule": true 22 | }, 23 | "exclude": [ 24 | "node_modules", 25 | "babel.config.js", 26 | "metro.config.js", 27 | "jest.config.js" 28 | ], 29 | "include": [ 30 | "next-env.d.ts", 31 | "**/*.ts", 32 | "**/*.tsx" 33 | ] 34 | } 35 | -------------------------------------------------------------------------------- /examples/suspense/nextjs-ssr-preload/types/relay-runtime_types.js: -------------------------------------------------------------------------------- 1 | declare module 'relay-runtime' { 2 | declare export var ConnectionHandler: any; 3 | declare export var RecordSource: any; 4 | declare export var Store: any; 5 | declare export var Environment: any; 6 | declare export var Network: any; 7 | 8 | declare export type RequestNode = any; 9 | declare export type Variables = any; 10 | declare export type ReaderFragment = any; 11 | declare export type ConcreteRequest = any; 12 | declare export opaque type FragmentReference; 13 | } 14 | -------------------------------------------------------------------------------- /examples/suspense/nextjs-ssr/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["next/babel"], 3 | "plugins": [ 4 | ["relay", {"artifactDirectory": "./__generated__/relay/"}], 5 | "@babel/plugin-transform-runtime", 6 | "@babel/plugin-proposal-class-properties", 7 | ["styled-components", {"ssr": true}] 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /examples/suspense/nextjs-ssr/.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | lib 3 | flow-typed 4 | types 5 | __generated__ 6 | -------------------------------------------------------------------------------- /examples/suspense/nextjs-ssr/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: [ 3 | 'fbjs/strict', 4 | 'plugin:prettier/recommended', 5 | 'plugin:react/recommended', 6 | 'plugin:relay/recommended', 7 | 'prettier/flowtype', 8 | 'prettier/react', 9 | ], 10 | parser: 'babel-eslint', 11 | plugins: ['babel', 'prettier', 'react', 'relay'], 12 | rules: { 13 | 'no-console': 'off', 14 | 'one-var': 'off', 15 | 'react/prop-types': 'off', // Replaced by flow types 16 | 17 | // Mutations aren't located in the same file as Components 18 | 'relay/unused-fields': 'off', 19 | }, 20 | settings: { 21 | react: { 22 | version: '16.8.1', 23 | flowVersion: '0.94.0', 24 | } 25 | }, 26 | }; 27 | -------------------------------------------------------------------------------- /examples/suspense/nextjs-ssr/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .eslintcache 3 | .next 4 | node_modules 5 | __generated__ 6 | -------------------------------------------------------------------------------- /examples/suspense/nextjs-ssr/.watchmanconfig: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /examples/suspense/nextjs-ssr/README.md: -------------------------------------------------------------------------------- 1 | # Relay Hooks NextJS SSR TodoMVC 2 | 3 | ## Installation 4 | 5 | ``` 6 | yarn 7 | ``` 8 | 9 | ## Running 10 | 11 | Set up generated files: 12 | 13 | ``` 14 | yarn 15 | yarn compile 16 | ``` 17 | 18 | Start a local server: 19 | 20 | ``` 21 | yarn dev 22 | ``` 23 | -------------------------------------------------------------------------------- /examples/suspense/nextjs-ssr/components/Header.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Link from 'next/link'; 3 | import styled, {css} from 'styled-components'; 4 | import {withRouter} from 'next/router'; 5 | 6 | const StyledButton = styled.button` 7 | margin: auto; 8 | padding: 10px; 9 | cursor: pointer; 10 | display: -webkit-box; 11 | flex: 1; 12 | ${props => 13 | props.selected && 14 | css` 15 | border: 1px solid #999; 16 | box-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2); 17 | box-sizing: border-box; 18 | `} 19 | `; 20 | 21 | const MyButton = React.forwardRef( 22 | ({onClick, href, children, selected}: any, ref) => ( 23 | 24 | {children} 25 | 26 | ), 27 | ); 28 | 29 | const StyledDiv = styled.div` 30 | display: flex; 31 | background: #fff; 32 | box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), 0 25px 50px 0 rgba(0, 0, 0, 0.1); 33 | `; 34 | 35 | const Header = ({userId}) => { 36 | const selectedYou = userId === 'you'; 37 | return ( 38 | 39 | 40 | ME 41 | 42 | 43 | YOU 44 | 45 | 46 | ); 47 | }; 48 | 49 | export default Header; 50 | -------------------------------------------------------------------------------- /examples/suspense/nextjs-ssr/data/schema/index.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | /** 3 | * This file provided by Facebook is for non-commercial testing and evaluation 4 | * purposes only. Facebook reserves all rights not expressly granted. 5 | * 6 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 7 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 8 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 9 | * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 10 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 11 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 12 | */ 13 | 14 | import {GraphQLObjectType, GraphQLSchema} from 'graphql'; 15 | 16 | import {nodeField} from './nodes.js'; 17 | import {UserQuery} from './queries/UserQuery'; 18 | import {AddTodoMutation} from './mutations/AddTodoMutation'; 19 | import {ChangeTodoStatusMutation} from './mutations/ChangeTodoStatusMutation'; 20 | import {MarkAllTodosMutation} from './mutations/MarkAllTodosMutation'; 21 | import {RemoveCompletedTodosMutation} from './mutations/RemoveCompletedTodosMutation'; 22 | import {RemoveTodoMutation} from './mutations/RemoveTodoMutation'; 23 | import {RenameTodoMutation} from './mutations/RenameTodoMutation'; 24 | 25 | const Query = new GraphQLObjectType({ 26 | name: 'Query', 27 | fields: { 28 | user: UserQuery, 29 | node: nodeField, 30 | }, 31 | }); 32 | 33 | const Mutation = new GraphQLObjectType({ 34 | name: 'Mutation', 35 | fields: { 36 | addTodo: AddTodoMutation, 37 | changeTodoStatus: ChangeTodoStatusMutation, 38 | markAllTodos: MarkAllTodosMutation, 39 | removeCompletedTodos: RemoveCompletedTodosMutation, 40 | removeTodo: RemoveTodoMutation, 41 | renameTodo: RenameTodoMutation, 42 | }, 43 | }); 44 | 45 | export const schema = new GraphQLSchema({ 46 | query: Query, 47 | mutation: Mutation, 48 | }); 49 | -------------------------------------------------------------------------------- /examples/suspense/nextjs-ssr/data/schema/mutations/ChangeTodoStatusMutation.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | /* graphql-relay doesn't export types, and isn't in flow-typed. This gets too messy */ 3 | /* eslint flowtype/require-return-type: 'off' */ 4 | /** 5 | * This file provided by Facebook is for non-commercial testing and evaluation 6 | * purposes only. Facebook reserves all rights not expressly granted. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 9 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 10 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 11 | * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 12 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 13 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 14 | */ 15 | 16 | import {fromGlobalId, mutationWithClientMutationId} from 'graphql-relay'; 17 | import {GraphQLBoolean, GraphQLID, GraphQLNonNull} from 'graphql'; 18 | import {GraphQLTodo, GraphQLUser} from '../nodes'; 19 | import { 20 | changeTodoStatus, 21 | getTodoOrThrow, 22 | getUserOrThrow, 23 | Todo, 24 | User, 25 | } from '../../database'; 26 | 27 | const ChangeTodoStatusMutation = mutationWithClientMutationId({ 28 | name: 'ChangeTodoStatus', 29 | inputFields: { 30 | complete: {type: new GraphQLNonNull(GraphQLBoolean)}, 31 | id: {type: new GraphQLNonNull(GraphQLID)}, 32 | userId: {type: new GraphQLNonNull(GraphQLID)}, 33 | }, 34 | outputFields: { 35 | todo: { 36 | type: new GraphQLNonNull(GraphQLTodo), 37 | resolve: ({id}) => getTodoOrThrow(id), 38 | }, 39 | user: { 40 | type: new GraphQLNonNull(GraphQLUser), 41 | resolve: ({userId}) => getUserOrThrow(userId), 42 | }, 43 | }, 44 | mutateAndGetPayload: ({id, complete, userId}) => { 45 | changeTodoStatus(id, complete); 46 | 47 | return {id, userId}; 48 | }, 49 | }); 50 | 51 | export {ChangeTodoStatusMutation}; 52 | -------------------------------------------------------------------------------- /examples/suspense/nextjs-ssr/data/schema/mutations/MarkAllTodosMutation.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | /* graphql-relay doesn't export types, and isn't in flow-typed. This gets too messy */ 3 | /* eslint flowtype/require-return-type: 'off' */ 4 | /** 5 | * This file provided by Facebook is for non-commercial testing and evaluation 6 | * purposes only. Facebook reserves all rights not expressly granted. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 9 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 10 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 11 | * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 12 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 13 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 14 | */ 15 | 16 | import {mutationWithClientMutationId} from 'graphql-relay'; 17 | import {GraphQLBoolean, GraphQLID, GraphQLList, GraphQLNonNull} from 'graphql'; 18 | import {GraphQLTodo, GraphQLUser} from '../nodes'; 19 | 20 | import { 21 | getTodoOrThrow, 22 | getUserOrThrow, 23 | markAllTodos, 24 | Todo, 25 | User, 26 | } from '../../database'; 27 | 28 | const MarkAllTodosMutation = mutationWithClientMutationId({ 29 | name: 'MarkAllTodos', 30 | inputFields: { 31 | complete: {type: new GraphQLNonNull(GraphQLBoolean)}, 32 | userId: {type: new GraphQLNonNull(GraphQLID)}, 33 | }, 34 | outputFields: { 35 | changedTodos: { 36 | type: new GraphQLList(new GraphQLNonNull(GraphQLTodo)), 37 | resolve: ({changedTodoIds}) => 38 | changedTodoIds.map(todoId => getTodoOrThrow(todoId)), 39 | }, 40 | user: { 41 | type: new GraphQLNonNull(GraphQLUser), 42 | resolve: ({userId}) => getUserOrThrow(userId), 43 | }, 44 | }, 45 | mutateAndGetPayload: ({complete, userId}) => { 46 | const changedTodoIds = markAllTodos(userId, complete); 47 | 48 | return {changedTodoIds, userId}; 49 | }, 50 | }); 51 | 52 | export {MarkAllTodosMutation}; 53 | -------------------------------------------------------------------------------- /examples/suspense/nextjs-ssr/data/schema/mutations/RemoveCompletedTodosMutation.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | /* graphql-relay doesn't export types, and isn't in flow-typed. This gets too messy */ 3 | /* eslint flowtype/require-return-type: 'off' */ 4 | /** 5 | * This file provided by Facebook is for non-commercial testing and evaluation 6 | * purposes only. Facebook reserves all rights not expressly granted. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 9 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 10 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 11 | * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 12 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 13 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 14 | */ 15 | 16 | import {mutationWithClientMutationId, toGlobalId} from 'graphql-relay'; 17 | import {GraphQLID, GraphQLList, GraphQLNonNull, GraphQLString} from 'graphql'; 18 | import {GraphQLUser} from '../nodes'; 19 | import {getUserOrThrow, removeCompletedTodos, User} from '../../database'; 20 | 21 | const RemoveCompletedTodosMutation = mutationWithClientMutationId({ 22 | name: 'RemoveCompletedTodos', 23 | inputFields: { 24 | userId: {type: new GraphQLNonNull(GraphQLID)}, 25 | }, 26 | outputFields: { 27 | deletedTodoIds: { 28 | type: new GraphQLList(new GraphQLNonNull(GraphQLString)), 29 | resolve: ({deletedTodoIds}) => deletedTodoIds, 30 | }, 31 | user: { 32 | type: new GraphQLNonNull(GraphQLUser), 33 | resolve: ({userId}) => getUserOrThrow(userId), 34 | }, 35 | }, 36 | mutateAndGetPayload: ({userId}) => { 37 | const deletedTodoIds = removeCompletedTodos(userId); 38 | 39 | return {deletedTodoIds, userId}; 40 | }, 41 | }); 42 | 43 | export {RemoveCompletedTodosMutation}; 44 | -------------------------------------------------------------------------------- /examples/suspense/nextjs-ssr/data/schema/mutations/RemoveTodoMutation.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | /* graphql-relay doesn't export types, and isn't in flow-typed. This gets too messy */ 3 | /* eslint flowtype/require-return-type: 'off' */ 4 | /** 5 | * This file provided by Facebook is for non-commercial testing and evaluation 6 | * purposes only. Facebook reserves all rights not expressly granted. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 9 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 10 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 11 | * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 12 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 13 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 14 | */ 15 | 16 | import {mutationWithClientMutationId, fromGlobalId} from 'graphql-relay'; 17 | import {GraphQLID, GraphQLNonNull} from 'graphql'; 18 | import {GraphQLUser} from '../nodes'; 19 | import {getUserOrThrow, removeTodo, User} from '../../database'; 20 | 21 | const RemoveTodoMutation = mutationWithClientMutationId({ 22 | name: 'RemoveTodo', 23 | inputFields: { 24 | id: {type: new GraphQLNonNull(GraphQLID)}, 25 | userId: {type: new GraphQLNonNull(GraphQLID)}, 26 | }, 27 | outputFields: { 28 | deletedTodoId: { 29 | type: new GraphQLNonNull(GraphQLID), 30 | resolve: ({id}) => id, 31 | }, 32 | user: { 33 | type: new GraphQLNonNull(GraphQLUser), 34 | resolve: ({userId}) => getUserOrThrow(userId), 35 | }, 36 | }, 37 | mutateAndGetPayload: ({id, userId}) => { 38 | removeTodo(id, userId); 39 | 40 | return {id, userId}; 41 | }, 42 | }); 43 | 44 | export {RemoveTodoMutation}; 45 | -------------------------------------------------------------------------------- /examples/suspense/nextjs-ssr/data/schema/mutations/RenameTodoMutation.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | /* graphql-relay doesn't export types, and isn't in flow-typed. This gets too messy */ 3 | /* eslint flowtype/require-return-type: 'off' */ 4 | /** 5 | * This file provided by Facebook is for non-commercial testing and evaluation 6 | * purposes only. Facebook reserves all rights not expressly granted. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 9 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 10 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 11 | * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 12 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 13 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 14 | */ 15 | 16 | import {mutationWithClientMutationId, fromGlobalId} from 'graphql-relay'; 17 | import {GraphQLID, GraphQLNonNull, GraphQLString} from 'graphql'; 18 | import {GraphQLTodo} from '../nodes'; 19 | import {getTodoOrThrow, renameTodo, Todo} from '../../database'; 20 | 21 | const RenameTodoMutation = mutationWithClientMutationId({ 22 | name: 'RenameTodo', 23 | inputFields: { 24 | id: {type: new GraphQLNonNull(GraphQLID)}, 25 | text: {type: new GraphQLNonNull(GraphQLString)}, 26 | }, 27 | outputFields: { 28 | todo: { 29 | type: new GraphQLNonNull(GraphQLTodo), 30 | resolve: ({id}) => getTodoOrThrow(id), 31 | }, 32 | }, 33 | mutateAndGetPayload: ({id, text}) => { 34 | renameTodo(id, text); 35 | 36 | return {id}; 37 | }, 38 | }); 39 | 40 | export {RenameTodoMutation}; 41 | -------------------------------------------------------------------------------- /examples/suspense/nextjs-ssr/data/schema/queries/UserQuery.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | /* graphql-relay doesn't export types, and isn't in flow-typed. This gets too messy */ 3 | /* eslint flowtype/require-return-type: 'off' */ 4 | /** 5 | * This file provided by Facebook is for non-commercial testing and evaluation 6 | * purposes only. Facebook reserves all rights not expressly granted. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 9 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 10 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 11 | * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 12 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 13 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 14 | */ 15 | 16 | import {GraphQLString} from 'graphql'; 17 | import {GraphQLUser} from '../nodes'; 18 | import {User, getUserOrThrow} from '../../database'; 19 | 20 | const UserQuery = { 21 | type: GraphQLUser, 22 | args: { 23 | id: {type: GraphQLString}, 24 | }, 25 | resolve: (root, {id}) => getUserOrThrow(id), 26 | }; 27 | 28 | export {UserQuery}; 29 | -------------------------------------------------------------------------------- /examples/suspense/nextjs-ssr/html.js: -------------------------------------------------------------------------------- 1 | export default ({ body, title }) => { 2 | return ` 3 | 4 | 5 | 6 | 7 | 8 | ${title} 9 | 10 | 11 | 12 | 13 |
${body}
14 | 15 | 16 | 17 | 18 | `; 19 | }; -------------------------------------------------------------------------------- /examples/suspense/nextjs-ssr/mutations/ChangeTodoStatusMutation.ts: -------------------------------------------------------------------------------- 1 | // @flow 2 | /** 3 | * This file provided by Facebook is for non-commercial testing and evaluation 4 | * purposes only. Facebook reserves all rights not expressly granted. 5 | * 6 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 7 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 8 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 9 | * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 10 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 11 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 12 | */ 13 | 14 | import {commitMutation, graphql} from 'relay-hooks'; 15 | 16 | const mutation = graphql` 17 | mutation ChangeTodoStatusMutation($input: ChangeTodoStatusInput!) { 18 | changeTodoStatus(input: $input) { 19 | todo { 20 | id 21 | complete 22 | } 23 | user { 24 | id 25 | completedCount 26 | } 27 | } 28 | } 29 | `; 30 | 31 | function getOptimisticResponse(complete: boolean, todo: any, user: any): any { 32 | return { 33 | changeTodoStatus: { 34 | todo: { 35 | complete: complete, 36 | id: todo.id, 37 | }, 38 | user: { 39 | id: user.id, 40 | completedCount: complete 41 | ? user.completedCount + 1 42 | : user.completedCount - 1, 43 | }, 44 | }, 45 | }; 46 | } 47 | 48 | function commit(environment: any, complete: boolean, todo: any, user: any) { 49 | const input: any = { 50 | complete, 51 | userId: user.userId, 52 | id: todo.id, 53 | }; 54 | commitMutation(environment, { 55 | mutation, 56 | variables: { 57 | input, 58 | }, 59 | optimisticResponse: getOptimisticResponse(complete, todo, user), 60 | }); 61 | } 62 | 63 | export default {commit}; 64 | -------------------------------------------------------------------------------- /examples/suspense/nextjs-ssr/mutations/RemoveTodoMutation.ts: -------------------------------------------------------------------------------- 1 | // @flow 2 | /** 3 | * This file provided by Facebook is for non-commercial testing and evaluation 4 | * purposes only. Facebook reserves all rights not expressly granted. 5 | * 6 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 7 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 8 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 9 | * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 10 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 11 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 12 | */ 13 | 14 | import { commitMutation, graphql } from 'relay-hooks'; 15 | import { RemoveTodoMutation } from '../__generated__/relay/RemoveTodoMutation.graphql'; 16 | 17 | const mutation = graphql` 18 | mutation RemoveTodoMutation($input: RemoveTodoInput!) @raw_response_type { 19 | removeTodo(input: $input) { 20 | deletedTodoId 21 | user { 22 | completedCount 23 | totalCount 24 | } 25 | } 26 | } 27 | `; 28 | 29 | function commit(environment: any, todo: any, user: any): any { 30 | const input: any = { 31 | id: todo.id, 32 | userId: user.userId, 33 | }; 34 | return commitMutation(environment, { 35 | mutation, 36 | variables: { 37 | input, 38 | }, 39 | configs: [ 40 | { 41 | type: 'NODE_DELETE', 42 | deletedIDFieldName: 'deletedTodoId', 43 | }, 44 | ], 45 | optimisticResponse: { 46 | removeTodo: { 47 | deletedTodoId: todo.id, 48 | user: { 49 | id: user.id, 50 | completedCount: user.completedCount - (todo.complete ? 1 : 0), 51 | totalCount: user.totalCount - 1, 52 | }, 53 | }, 54 | }, 55 | }); 56 | } 57 | 58 | export default { commit }; 59 | -------------------------------------------------------------------------------- /examples/suspense/nextjs-ssr/mutations/RenameTodoMutation.ts: -------------------------------------------------------------------------------- 1 | // @flow 2 | /** 3 | * This file provided by Facebook is for non-commercial testing and evaluation 4 | * purposes only. Facebook reserves all rights not expressly granted. 5 | * 6 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 7 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 8 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 9 | * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 10 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 11 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 12 | */ 13 | 14 | import {commitMutation, graphql} from 'relay-hooks'; 15 | import {Disposable} from 'relay-runtime'; 16 | 17 | const mutation = graphql` 18 | mutation RenameTodoMutation($input: RenameTodoInput!) { 19 | renameTodo(input: $input) { 20 | todo { 21 | id 22 | text 23 | } 24 | } 25 | } 26 | `; 27 | 28 | function getOptimisticResponse(text: string, todo: any): any { 29 | return { 30 | renameTodo: { 31 | todo: { 32 | id: todo.id, 33 | text: text, 34 | }, 35 | }, 36 | }; 37 | } 38 | 39 | function commit(environment: any, text: string, todo: any): Disposable { 40 | const input: any = { 41 | text, 42 | id: todo.id, 43 | }; 44 | 45 | return commitMutation(environment, { 46 | mutation, 47 | variables: { 48 | input, 49 | }, 50 | optimisticResponse: getOptimisticResponse(text, todo), 51 | }); 52 | } 53 | 54 | export default {commit}; 55 | -------------------------------------------------------------------------------- /examples/suspense/nextjs-ssr/next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | // NOTE: This file should not be edited 5 | // see https://nextjs.org/docs/basic-features/typescript for more information. 6 | -------------------------------------------------------------------------------- /examples/suspense/nextjs-ssr/next.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | webpack(config, options) { 5 | config.resolve.alias['relay'] = path.join( 6 | __dirname, 7 | '__generated__', 8 | 'relay', 9 | ); 10 | return config; 11 | }, 12 | reactStrictMode: true, 13 | }; 14 | -------------------------------------------------------------------------------- /examples/suspense/nextjs-ssr/now.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 2, 3 | "builds": [ 4 | { 5 | "src": "next.config.js", 6 | "use": "@now/next" 7 | } 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /examples/suspense/nextjs-ssr/pages/_document.tsx: -------------------------------------------------------------------------------- 1 | import Document, { Head, Main, NextScript, Html } from 'next/document'; 2 | import { createGlobalStyle, ServerStyleSheet } from 'styled-components'; 3 | import * as React from 'react'; 4 | 5 | type Props = { 6 | styleTags: string; 7 | }; 8 | 9 | export default class MyDocument extends Document { 10 | /*static async getInitialProps(ctx) { 11 | const initialProps = await Document.getInitialProps(ctx); 12 | const sheet = new ServerStyleSheet(); 13 | const page = ctx.renderPage((App) => (props) => { 14 | return sheet.collectStyles(); 15 | }); 16 | const styleTags = sheet.getStyleElement(); 17 | return { ...initialProps, ...page, styleTags }; 18 | }*/ 19 | static async getInitialProps(ctx) { 20 | const sheet = new ServerStyleSheet(); 21 | const originalRenderPage = ctx.renderPage; 22 | 23 | try { 24 | ctx.renderPage = () => 25 | originalRenderPage({ 26 | enhanceApp: (App) => (props) => sheet.collectStyles(), 27 | }); 28 | 29 | const initialProps = await Document.getInitialProps(ctx); 30 | return { 31 | ...initialProps, 32 | styles: ( 33 | <> 34 | {initialProps.styles} 35 | {sheet.getStyleElement()} 36 | 37 | ), 38 | }; 39 | } finally { 40 | sheet.seal(); 41 | } 42 | } 43 | /* 44 | render() { 45 | return ( 46 | 47 | {this.props.styleTags} 48 | 49 | 50 |
51 | 52 | 53 | 54 | ); 55 | }*/ 56 | } 57 | -------------------------------------------------------------------------------- /examples/suspense/nextjs-ssr/pages/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import TodoApp, { QUERY_APP } from '../components/TodoApp'; 3 | import { withData } from '../relay'; 4 | import { useLazyLoadQuery, STORE_OR_NETWORK } from 'relay-hooks'; 5 | import { TodoAppQuery } from '../__generated__/relay/TodoAppQuery.graphql'; 6 | 7 | const query = QUERY_APP; 8 | 9 | const variables = { 10 | // Mock authenticated ID that matches database 11 | userId: 'me', 12 | }; 13 | 14 | // issue 15 | export type $Call any> = Fn extends (arg: any) => infer RT 16 | ? RT 17 | : never; 18 | export type ArrayKeyType = ReadonlyArray<{ readonly ' $data'?: ReadonlyArray } | null>; 19 | export type ArrayKeyReturnType = ( 20 | arg: T, 21 | ) => NonNullable[' $data']>[0]; 22 | 23 | const issue223: ReadonlyArray<$Call>> = []; 24 | console.log('issue 223', issue223); 25 | 26 | const Home = () => { 27 | const { data, retry } = useLazyLoadQuery(query, variables, { 28 | fetchPolicy: STORE_OR_NETWORK, 29 | }); 30 | if (!data) { 31 | return
no data || skip
; 32 | } 33 | 34 | return ; 35 | }; 36 | 37 | //
38 | export default withData(Home, { 39 | query, 40 | variables, 41 | }); 42 | -------------------------------------------------------------------------------- /examples/suspense/nextjs-ssr/pages/you.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import TodoApp, { QUERY_APP } from '../components/TodoApp'; 3 | import { withData } from '../relay'; 4 | import { useLazyLoadQuery, STORE_OR_NETWORK } from 'relay-hooks'; 5 | import { TodoAppQuery } from '../__generated__/relay/TodoAppQuery.graphql'; 6 | 7 | const query = QUERY_APP; 8 | 9 | const variables = { 10 | // Mock authenticated ID that matches database 11 | userId: 'you', 12 | }; 13 | 14 | const Home = () => { 15 | const { data, retry } = useLazyLoadQuery(query, variables, { 16 | fetchPolicy: STORE_OR_NETWORK, 17 | }); 18 | if (!data) { 19 | return
no data || skip
; 20 | } 21 | return ; 22 | }; 23 | 24 | //
25 | export default withData(Home, { 26 | query, 27 | variables, 28 | }); 29 | -------------------------------------------------------------------------------- /examples/suspense/nextjs-ssr/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/relay-tools/relay-hooks/31534668ef8275e3f4fc288406319a3c5dc8b636/examples/suspense/nextjs-ssr/public/favicon.ico -------------------------------------------------------------------------------- /examples/suspense/nextjs-ssr/relay/createRelayEnvironment.ts: -------------------------------------------------------------------------------- 1 | import {Network} from 'relay-runtime'; 2 | import {Store, Environment, RecordSource} from 'relay-runtime'; 3 | 4 | import fetch from 'isomorphic-unfetch'; 5 | 6 | let relayEnvironment: Environment; 7 | 8 | function fetchQuery(operation, variables, cacheConfig, uploadables) { 9 | const endpoint = 'http://localhost:3000/graphql'; 10 | return fetch(endpoint, { 11 | method: 'POST', 12 | headers: { 13 | Accept: 'application/json', 14 | 'Content-Type': 'application/json', 15 | }, // Add authentication and other headers here 16 | body: JSON.stringify({ 17 | query: operation.text, // GraphQL text from input 18 | variables, 19 | }), 20 | }).then(response => response.json()); 21 | } 22 | 23 | type InitProps = { 24 | records?: any; 25 | }; 26 | 27 | const network = Network.create(fetchQuery); 28 | 29 | function createEnvironment(records) { 30 | const recordSource = new RecordSource(records); 31 | const store = new Store(recordSource); 32 | const environment = new Environment({ 33 | network, 34 | store, 35 | }); 36 | return environment; 37 | } 38 | 39 | export default function initEnvironment(options: InitProps = {}) { 40 | const {records = {}} = options; 41 | 42 | if (typeof window === 'undefined') { 43 | return createEnvironment(records); 44 | } 45 | 46 | // reuse Relay environment on client-side 47 | if (!relayEnvironment) { 48 | relayEnvironment = createEnvironment(records); 49 | } 50 | 51 | return relayEnvironment; 52 | } 53 | -------------------------------------------------------------------------------- /examples/suspense/nextjs-ssr/relay/index.js: -------------------------------------------------------------------------------- 1 | export {default as createRelayEnvironment} from './createRelayEnvironment'; 2 | export {default as withData} from './withData'; 3 | -------------------------------------------------------------------------------- /examples/suspense/nextjs-ssr/relay/withData.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import initEnvironment from './createRelayEnvironment'; 3 | import { fetchQuery } from 'relay-hooks'; 4 | import { Variables, GraphQLTaggedNode } from 'relay-runtime'; 5 | import { DocumentContext } from 'next/document'; 6 | import { NextPage } from 'next'; 7 | 8 | type OptionsWithData = { 9 | query: GraphQLTaggedNode; 10 | variables: Variables; 11 | }; 12 | 13 | export default (ComposedComponent: NextPage, options: OptionsWithData) => { 14 | function WithData(dataprops) { 15 | return ; 16 | } 17 | 18 | WithData.getInitialProps = async (ctx: DocumentContext) => { 19 | const isServer = !!ctx.req; 20 | let composedInitialProps = {}; 21 | if (ComposedComponent.getInitialProps) { 22 | composedInitialProps = await ComposedComponent.getInitialProps(ctx); 23 | } 24 | if (!isServer) { 25 | return { 26 | ...composedInitialProps, 27 | environment: null, 28 | }; 29 | } 30 | 31 | let queryRecords = {}; 32 | const environment = initEnvironment(); 33 | 34 | const { query, variables } = options; 35 | if (query) { 36 | await fetchQuery(environment, query, variables); 37 | queryRecords = environment 38 | .getStore() 39 | .getSource() 40 | .toJSON(); 41 | } 42 | return { 43 | ...composedInitialProps, 44 | queryRecords, 45 | environment, 46 | }; 47 | }; 48 | 49 | return WithData; 50 | }; 51 | -------------------------------------------------------------------------------- /examples/suspense/nextjs-ssr/scripts/updateSchema.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env babel-node 2 | // @flow 3 | /** 4 | * This file provided by Facebook is for non-commercial testing and evaluation 5 | * purposes only. Facebook reserves all rights not expressly granted. 6 | * 7 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 8 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 9 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 10 | * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 11 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 12 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 13 | */ 14 | 15 | import fs from 'fs'; 16 | import path from 'path'; 17 | import {schema} from '../data/schema'; 18 | import {printSchema} from 'graphql'; 19 | 20 | const schemaPath = path.resolve(__dirname, '../data/schema.graphql'); 21 | 22 | fs.writeFileSync(schemaPath, printSchema(schema)); 23 | 24 | console.log('Wrote ' + schemaPath); 25 | -------------------------------------------------------------------------------- /examples/suspense/nextjs-ssr/server.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | /** 3 | * This file provided by Facebook is for non-commercial testing and evaluation 4 | * purposes only. Facebook reserves all rights not expressly granted. 5 | * 6 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 7 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 8 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 9 | * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 10 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 11 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 12 | */ 13 | require('es6-promise').polyfill(); 14 | require('isomorphic-fetch'); 15 | import express from 'express'; 16 | import graphQLHTTP from 'express-graphql'; 17 | import {schema} from './data/schema'; 18 | import next from 'next'; 19 | 20 | const app = next({dev: process.env.NODE_ENV !== 'production'}); 21 | const handle = app.getRequestHandler(); 22 | 23 | const port = 3000; 24 | 25 | app.prepare().then(() => { 26 | const server = express(); 27 | 28 | server.use( 29 | '/graphql', 30 | graphQLHTTP({ 31 | schema, 32 | graphiql: false, 33 | pretty: true, 34 | }), 35 | ); 36 | 37 | server.get('*', (req, res) => { 38 | return handle(req, res); 39 | }); 40 | 41 | server.listen(port, err => { 42 | if (err) throw err; 43 | console.log(`> Ready on http://localhost:${port}`); 44 | }); 45 | }); 46 | -------------------------------------------------------------------------------- /examples/suspense/nextjs-ssr/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowJs": true, 4 | "allowSyntheticDefaultImports": true, 5 | "esModuleInterop": true, 6 | "isolatedModules": true, 7 | "noImplicitAny": false, 8 | "jsx": "preserve", 9 | "lib": [ 10 | "dom", 11 | "es2016", 12 | "es2017.object" 13 | ], 14 | "moduleResolution": "node", 15 | "noEmit": true, 16 | "strict": true, 17 | "target": "esnext", 18 | "skipLibCheck": true, 19 | "forceConsistentCasingInFileNames": true, 20 | "module": "esnext", 21 | "resolveJsonModule": true, 22 | "incremental": true 23 | }, 24 | "exclude": [ 25 | "node_modules", 26 | "babel.config.js", 27 | "metro.config.js", 28 | "jest.config.js" 29 | ], 30 | "include": [ 31 | "next-env.d.ts", 32 | "**/*.ts", 33 | "**/*.tsx" 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /examples/suspense/nextjs-ssr/types/relay-runtime_types.js: -------------------------------------------------------------------------------- 1 | declare module 'relay-runtime' { 2 | declare export var ConnectionHandler: any; 3 | declare export var RecordSource: any; 4 | declare export var Store: any; 5 | declare export var Environment: any; 6 | declare export var Network: any; 7 | 8 | declare export type RequestNode = any; 9 | declare export type Variables = any; 10 | declare export type ReaderFragment = any; 11 | declare export type ConcreteRequest = any; 12 | declare export opaque type FragmentReference; 13 | } 14 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | transform: { 3 | '^.+\\.(js|jsx|ts|tsx)$': 'ts-jest', 4 | }, 5 | preset: 'ts-jest', 6 | verbose: true, 7 | 8 | globals: { 9 | __DEV__: true, 10 | 'ts-jest': { 11 | astTransformers: { 12 | before: ['ts-relay-plugin'], 13 | }, 14 | diagnostics: { 15 | warnOnly: true, 16 | }, 17 | isolatedModules: true, 18 | }, 19 | }, 20 | 21 | moduleFileExtensions: ['ts', 'tsx', 'js', 'json'], 22 | testURL: 'http://localhost', 23 | testEnvironment: 'node', 24 | 25 | testMatch: ['/__tests__/*-test.tsx'], 26 | testPathIgnorePatterns: [ 27 | './node_modules/', 28 | '/node_modules/', 29 | '/lib/', 30 | '/lib/', 31 | '/node_modules/', 32 | ], 33 | transformIgnorePatterns: ['./node_modules/(?!(@react-native-community|react-native))'], 34 | coverageThreshold: { 35 | global: { 36 | branches: 0, 37 | functions: 0, 38 | lines: 0, 39 | statements: 0, 40 | }, 41 | }, 42 | setupFiles: ['./scripts/setup.ts'], 43 | timers: 'fake', 44 | }; 45 | -------------------------------------------------------------------------------- /replace.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | files: 'lib/**/*.*', 3 | from: /import \* as /g, 4 | to: 'import ', 5 | }; 6 | -------------------------------------------------------------------------------- /scripts/setup.ts: -------------------------------------------------------------------------------- 1 | import * as Promise from 'promise-polyfill'; 2 | global.Promise = Promise; 3 | -------------------------------------------------------------------------------- /src/ReactRelayContext.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Facebook, Inc. and its affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | * 7 | * @flow strict-local 8 | * @format 9 | */ 10 | 11 | 'use strict'; 12 | 13 | import * as React from 'react'; 14 | import { __internal } from 'relay-runtime'; 15 | 16 | const { createRelayContext } = __internal as any; 17 | 18 | export const ReactRelayContext = createRelayContext(React); 19 | -------------------------------------------------------------------------------- /src/RelayEnvironmentProvider.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { IEnvironment } from 'relay-runtime'; 3 | import { ReactRelayContext } from './ReactRelayContext'; // eslint-disable-line @typescript-eslint/no-unused-vars 4 | 5 | export function RelayEnvironmentProvider(props: { 6 | children: React.ReactNode; 7 | environment: IEnvironment; 8 | }): React.ReactElement { 9 | const context = React.useMemo(() => ({ environment: props.environment }), [props.environment]); 10 | return {props.children}; 11 | } 12 | -------------------------------------------------------------------------------- /src/Utils.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Variables, 3 | ConnectionMetadata, 4 | createOperationDescriptor, 5 | getRequest, 6 | GraphQLTaggedNode, 7 | OperationDescriptor, 8 | CacheConfig, 9 | } from 'relay-runtime'; 10 | import { STORE_OR_NETWORK, STORE_THEN_NETWORK, NETWORK_ONLY, FetchPolicy } from './RelayHooksTypes'; 11 | 12 | export type ReactConnectionMetadata = ConnectionMetadata & { fragmentName: string }; 13 | 14 | export const isNetworkPolicy = (policy: FetchPolicy, full: boolean): boolean => { 15 | return policy === NETWORK_ONLY || policy === STORE_THEN_NETWORK || (policy === STORE_OR_NETWORK && !full); 16 | }; 17 | 18 | export const isStorePolicy = (policy: FetchPolicy): boolean => { 19 | return policy !== NETWORK_ONLY; 20 | }; 21 | 22 | export const forceCache = { force: true }; 23 | 24 | // Fetcher 25 | export function createOperation( 26 | gqlQuery: GraphQLTaggedNode, 27 | variables: Variables, 28 | cacheConfig?: CacheConfig | null, 29 | ): OperationDescriptor { 30 | return createOperationDescriptor(getRequest(gqlQuery), variables, cacheConfig); 31 | } 32 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export { 2 | applyOptimisticMutation, 3 | commitLocalUpdate, 4 | commitMutation, 5 | fetchQuery, 6 | graphql, 7 | requestSubscription, 8 | } from 'relay-runtime'; 9 | export { ReactRelayContext } from './ReactRelayContext'; 10 | export { useQuery, useLazyLoadQuery } from './useQuery'; 11 | export { loadQuery, loadLazyQuery } from './loadQuery'; 12 | export { usePreloadedQuery } from './usePreloadedQuery'; 13 | export { useFragment, useSuspenseFragment, useFragmentSubscription } from './useFragment'; 14 | export { useMutation } from './useMutation'; 15 | export { useSubscription } from './useSubscription'; 16 | export { useOssFragment } from './useOssFragment'; 17 | export { usePagination, usePaginationFragment, usePaginationSubscription } from './usePagination'; 18 | export { useRefetchable, useRefetchableFragment, useRefetchableSubscription } from './useRefetchable'; 19 | export { useRelayEnvironment } from './useRelayEnvironment'; 20 | export { RelayEnvironmentProvider } from './RelayEnvironmentProvider'; 21 | export * from './RelayHooksTypes'; 22 | -------------------------------------------------------------------------------- /src/useForceUpdate.ts: -------------------------------------------------------------------------------- 1 | import { useCallback, useEffect, useRef, useState } from 'react'; 2 | 3 | export function useForceUpdate(): () => void { 4 | const [, forceUpdate] = useState([]); 5 | const mountState = useRef({ mounted: false, pending: false }); 6 | useEffect(() => { 7 | mountState.current.mounted = true; 8 | if (mountState.current.pending) { 9 | mountState.current.pending = false; 10 | forceUpdate([]); 11 | } 12 | return () => { 13 | mountState.current = { mounted: false, pending: false }; 14 | }; 15 | }, []); 16 | const update = useCallback(() => { 17 | if (mountState.current.mounted) { 18 | forceUpdate([]); 19 | } else { 20 | mountState.current.pending = true; 21 | } 22 | }, [forceUpdate]); 23 | return update; 24 | } 25 | -------------------------------------------------------------------------------- /src/usePreloadedQuery.ts: -------------------------------------------------------------------------------- 1 | import { useEffect } from 'react'; 2 | import { OperationType } from 'relay-runtime'; 3 | import { RenderProps, LoadQuery } from './RelayHooksTypes'; 4 | import { useForceUpdate } from './useForceUpdate'; 5 | import { useRelayEnvironment } from './useRelayEnvironment'; 6 | 7 | export const usePreloadedQuery = ( 8 | loadQuery: LoadQuery, 9 | ): RenderProps => { 10 | const forceUpdate = useForceUpdate(); 11 | const environment = useRelayEnvironment(); 12 | 13 | useEffect(() => { 14 | return loadQuery.subscribe(forceUpdate); 15 | // eslint-disable-next-line react-hooks/exhaustive-deps 16 | }, [loadQuery]); 17 | 18 | return loadQuery.getValue(environment) as RenderProps; 19 | }; 20 | -------------------------------------------------------------------------------- /src/useRelayEnvironment.ts: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { IEnvironment } from 'relay-runtime'; 3 | import { ReactRelayContext } from './ReactRelayContext'; 4 | 5 | export function useRelayEnvironment(): TEnvironment { 6 | const { environment } = React.useContext(ReactRelayContext); 7 | return environment; 8 | } 9 | -------------------------------------------------------------------------------- /src/useSubscription.ts: -------------------------------------------------------------------------------- 1 | import { useEffect } from 'react'; 2 | import { GraphQLSubscriptionConfig, requestSubscription, OperationType } from 'relay-runtime'; 3 | import { SkipGraphQLSubscriptionConfig, SkipSubscriptionConfig, SubscriptionConfig } from './RelayHooksTypes'; 4 | import { useRelayEnvironment } from './useRelayEnvironment'; 5 | 6 | export function useSubscription( 7 | config: GraphQLSubscriptionConfig, 8 | opts?: SubscriptionConfig, 9 | ): void; 10 | export function useSubscription( 11 | config: SkipGraphQLSubscriptionConfig, 12 | opts: SkipSubscriptionConfig, 13 | ): void; 14 | export function useSubscription( 15 | config: GraphQLSubscriptionConfig, 16 | opts?: SubscriptionConfig, 17 | ): void { 18 | const environment = useRelayEnvironment(); 19 | const skip = opts && opts.skip; 20 | 21 | useEffect(() => { 22 | if (skip) { 23 | return; 24 | } 25 | const { dispose } = requestSubscription(environment, config); 26 | return dispose; 27 | }, [environment, config, skip]); 28 | } 29 | -------------------------------------------------------------------------------- /tsconfig.esm.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "module": "ESNext", 5 | "outDir": "./lib/es" 6 | }, 7 | } -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "lib", 4 | "rootDir": "src", 5 | "module": "commonjs", 6 | "target": "es5", 7 | "moduleResolution": "node", 8 | "noEmitOnError": true, 9 | "declaration": true, 10 | "lib": ["dom", "es6", "esnext.asynciterable", "es2017.object"], 11 | "jsx": "react", 12 | "skipLibCheck": true 13 | }, 14 | "exclude": ["lib", "__tests__", "examples", "__mocks__", "coverage", "scripts"], 15 | "compileOnSave": true 16 | } 17 | -------------------------------------------------------------------------------- /website/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | lib 3 | *.tgz 4 | lerna-debug.log 5 | .npmrc 6 | build -------------------------------------------------------------------------------- /website/i18n/en.json: -------------------------------------------------------------------------------- 1 | { 2 | "_comment": "This file is auto-generated by write-translations.js", 3 | "localized-strings": { 4 | "next": "Next", 5 | "previous": "Previous", 6 | "tagline": "Collection of libraries usable for the web, react and react-native.", 7 | "docs": { 8 | "relay-hooks": { 9 | "title": "Getting Started" 10 | }, 11 | "use-fragment": { 12 | "title": "useFragment" 13 | }, 14 | "use-mutation": { 15 | "title": "useMutation" 16 | }, 17 | "use-pagination": { 18 | "title": "usePagination" 19 | }, 20 | "use-preloaded-query": { 21 | "title": "usePreloadedQuery" 22 | }, 23 | "use-refetchable": { 24 | "title": "useRefetchable" 25 | }, 26 | "use-subscription": { 27 | "title": "useSubscription" 28 | } 29 | }, 30 | "links": { 31 | "Docs": "Docs", 32 | "GitHub": "GitHub" 33 | }, 34 | "categories": { 35 | "Relay Hooks": "Relay Hooks", 36 | "API Reference": "API Reference" 37 | } 38 | }, 39 | "pages-strings": { 40 | "Help Translate|recruit community translators for your project": "Help Translate", 41 | "Edit this Doc|recruitment message asking to edit the doc source": "Edit", 42 | "Translate this Doc|recruitment message asking to translate the docs": "Translate" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /website/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "repository": "morrys/wora", 3 | "license": "MIT", 4 | "private": true, 5 | "scripts": { 6 | "start": "docusaurus-start", 7 | "build": "docusaurus-build", 8 | "docpub": "docusaurus-publish" 9 | }, 10 | "dependencies": { 11 | "docusaurus": "1.12.0" 12 | } 13 | } -------------------------------------------------------------------------------- /website/sidebars.json: -------------------------------------------------------------------------------- 1 | { 2 | "docs": { 3 | "Relay Hooks": [ 4 | "relay-hooks" 5 | ], 6 | "API Reference": [ 7 | "use-fragment", 8 | "use-refetchable", 9 | "use-pagination", 10 | "use-mutation", 11 | "use-subscription", 12 | "use-preloaded-query" 13 | ] 14 | } 15 | } -------------------------------------------------------------------------------- /website/static/css/custom.css: -------------------------------------------------------------------------------- 1 | /* your custom css */ 2 | 3 | @media only screen and (min-device-width: 360px) and (max-device-width: 736px) { 4 | } 5 | 6 | @media only screen and (min-width: 1024px) { 7 | } 8 | 9 | @media only screen and (max-width: 1023px) { 10 | } 11 | 12 | @media only screen and (min-width: 1400px) { 13 | } 14 | 15 | @media only screen and (min-width: 1500px) { 16 | } -------------------------------------------------------------------------------- /website/static/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/relay-tools/relay-hooks/31534668ef8275e3f4fc288406319a3c5dc8b636/website/static/img/favicon.ico -------------------------------------------------------------------------------- /website/static/img/oss_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/relay-tools/relay-hooks/31534668ef8275e3f4fc288406319a3c5dc8b636/website/static/img/oss_logo.png -------------------------------------------------------------------------------- /website/static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | Your Site Title Here 10 | 11 | 12 | If you are not redirected automatically, follow this link. 13 | 14 | --------------------------------------------------------------------------------