From 2c03a0f44fd932d47138a3af511325289715b444 Mon Sep 17 00:00:00 2001 From: hwillson Date: Mon, 16 Sep 2019 18:10:20 -0400 Subject: [PATCH 01/28] Remove specific test matching Falling back on the Jest default will work now that we're not using a monorepo, and open things up to test further nested `__tests__` directories (which will be needed to test React changes). --- config/jest.config.js | 1 - 1 file changed, 1 deletion(-) diff --git a/config/jest.config.js b/config/jest.config.js index 7d94e22f76e..3b87591a0ac 100644 --- a/config/jest.config.js +++ b/config/jest.config.js @@ -10,7 +10,6 @@ module.exports = { }, moduleFileExtensions: ['ts', 'tsx', 'js', 'json'], testURL: 'http://localhost', - testMatch: ['/src/**/__tests__/**/*.ts'], testPathIgnorePatterns: [ '/node_modules/', '/lib/' From eca59fecf88df0aece1be54317a80b6811f7d964 Mon Sep 17 00:00:00 2001 From: hwillson Date: Mon, 16 Sep 2019 18:13:26 -0400 Subject: [PATCH 02/28] Dependency updates (peer/dep/devDep) in preparation of RA merge --- package-lock.json | 89 +++++++++++++++++++++++++++++++++++++++++++++++ package.json | 10 ++++-- 2 files changed, 96 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 732fb7aeb87..eeeba0a4b08 100644 --- a/package-lock.json +++ b/package-lock.json @@ -183,6 +183,23 @@ "@babel/helper-plugin-utils": "^7.0.0" } }, + "@babel/runtime": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.6.0.tgz", + "integrity": "sha512-89eSBLJsxNxOERC0Op4vd+0Bqm6wRMqMbFtV3i0/fbaWw/mJ8Q3eBvgX0G4SyrOOLCtbu98HspF8o09MRT+KzQ==", + "dev": true, + "requires": { + "regenerator-runtime": "^0.13.2" + }, + "dependencies": { + "regenerator-runtime": { + "version": "0.13.3", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz", + "integrity": "sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw==", + "dev": true + } + } + }, "@babel/template": { "version": "7.6.0", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.6.0.tgz", @@ -1044,6 +1061,37 @@ "@types/yargs": "^13.0.0" } }, + "@sheerun/mutationobserver-shim": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@sheerun/mutationobserver-shim/-/mutationobserver-shim-0.3.2.tgz", + "integrity": "sha512-vTCdPp/T/Q3oSqwHmZ5Kpa9oI7iLtGl3RQaA/NyLHikvcrPxACkkKVr/XzkSPJWXHRhKGzVvb0urJsbMlRxi1Q==", + "dev": true + }, + "@testing-library/dom": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-6.2.0.tgz", + "integrity": "sha512-YaaoAIDTNV8AfC19XLa6KNeBB5KuSxWYPrgYN1vBu1i+czQlfWJSCS0A3yd2V3BUH9di9C1BD+7OoyVBpZCh2Q==", + "dev": true, + "requires": { + "@babel/runtime": "^7.5.5", + "@sheerun/mutationobserver-shim": "^0.3.2", + "@types/testing-library__dom": "^6.0.0", + "aria-query": "3.0.0", + "pretty-format": "^24.8.0", + "wait-for-expect": "^1.3.0" + } + }, + "@testing-library/react": { + "version": "9.1.4", + "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-9.1.4.tgz", + "integrity": "sha512-fQ/PXZoLcmnS1W5ZiM3P7XBy2x6Hm9cJAT/ZDuZKzJ1fS1rN3j31p7ReAqUe3N1kJ46sNot0n1oiGbz7FPU+FA==", + "dev": true, + "requires": { + "@babel/runtime": "^7.5.5", + "@testing-library/dom": "^6.1.0", + "@types/testing-library__react": "^9.1.0" + } + }, "@types/babel__core": { "version": "7.1.3", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.3.tgz", @@ -1195,6 +1243,25 @@ "integrity": "sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw==", "dev": true }, + "@types/testing-library__dom": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@types/testing-library__dom/-/testing-library__dom-6.0.3.tgz", + "integrity": "sha512-NCjixGZ6iubYpe63YKYJy/bDkPp+HD8fbi+0iXcaYhsYjQhoa7IfFz/WcaCRZJnKSi63c9GSK9pZi/Y8/gTvFA==", + "dev": true, + "requires": { + "pretty-format": "^24.3.0" + } + }, + "@types/testing-library__react": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/@types/testing-library__react/-/testing-library__react-9.1.1.tgz", + "integrity": "sha512-8/toTJaIlS3BC7JrK2ElTnbjH8tmFP7atdL2ZsIa1JDmH9RKSm/7Wp5oMDJzXoWr988Mv7ym/XZ8LRglyoGCGw==", + "dev": true, + "requires": { + "@types/react-dom": "*", + "@types/testing-library__dom": "*" + } + }, "@types/yargs": { "version": "13.0.2", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.2.tgz", @@ -1673,6 +1740,16 @@ "integrity": "sha1-7L0W+JSbFXGDcRsb2jNPN4QBhas=", "dev": true }, + "aria-query": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-3.0.0.tgz", + "integrity": "sha1-ZbP8wcoRVajJrmTW7uKX8V1RM8w=", + "dev": true, + "requires": { + "ast-types-flow": "0.0.7", + "commander": "^2.11.0" + } + }, "arr-flatten": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", @@ -1718,6 +1795,12 @@ "integrity": "sha512-uWMHxJxtfj/1oZClOxDEV1sQ1HCDkA4MG8Gr69KKeBjEVH0R84WlejZ0y2DcwyBlpAEMltmVYkVgqfLFb2oyiA==", "dev": true }, + "ast-types-flow": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz", + "integrity": "sha1-9wtzXGvKGlycItmCw+Oef+ujva0=", + "dev": true + }, "astral-regex": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", @@ -8716,6 +8799,12 @@ "browser-process-hrtime": "^0.1.2" } }, + "wait-for-expect": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/wait-for-expect/-/wait-for-expect-1.3.0.tgz", + "integrity": "sha512-8fJU7jiA96HfGPt+P/UilelSAZfhMBJ52YhKzlmZQvKEZU2EcD1GQ0yqGB6liLdHjYtYAoGVigYwdxr5rktvzA==", + "dev": true + }, "walker": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.7.tgz", diff --git a/package.json b/package.json index f37ff7cf041..815f6d7c91e 100644 --- a/package.json +++ b/package.json @@ -48,6 +48,12 @@ "maxSize": "16.9 kB" } ], + "peerDependencies": { + "@types/react": "^16.8.0", + "graphql": "^14.5.4", + "react": "^16.8.0", + "react-dom": "^16.8.0" + }, "dependencies": { "@types/zen-observable": "^0.8.0", "@wry/equality": "^0.1.9", @@ -59,13 +65,11 @@ "tslib": "^1.10.0", "zen-observable": "^0.8.14" }, - "peerDependencies": { - "graphql": "^14.5.4" - }, "devDependencies": { "@babel/core": "7.6.0", "@babel/plugin-transform-modules-commonjs": "7.6.0", "@babel/plugin-transform-modules-umd": "7.2.0", + "@testing-library/react": "^9.1.4", "@types/fast-json-stable-stringify": "^2.0.0", "@types/isomorphic-fetch": "0.0.35", "@types/jest": "24.0.18", From d0eca9333aea96a6b445c1405b1d9a543de75ae6 Mon Sep 17 00:00:00 2001 From: hwillson Date: Mon, 16 Sep 2019 18:14:55 -0400 Subject: [PATCH 03/28] Modernize `OperationVariables` using `Record` React Apollo uses its own copy of `OperationVariables`; let's update AC's version, so we can configure RA to use it after it has been merged in. --- src/core/types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/types.ts b/src/core/types.ts index 4a1f0ff9891..caafd37cf85 100644 --- a/src/core/types.ts +++ b/src/core/types.ts @@ -11,7 +11,7 @@ export type QueryListener = ( forceResolvers?: boolean, ) => void; -export type OperationVariables = { [key: string]: any }; +export type OperationVariables = Record; export type PureQueryOptions = { query: DocumentNode; From c5d872ba3a64253ca53e01aded34d040ce722320 Mon Sep 17 00:00:00 2001 From: hwillson Date: Mon, 16 Sep 2019 18:17:02 -0400 Subject: [PATCH 04/28] TS config changes in preparation of React Apollo being merged in --- tsconfig.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tsconfig.json b/tsconfig.json index 25f6b0588bc..c5646af20ab 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -14,7 +14,8 @@ "module": "es2015", "esModuleInterop": true, "outDir": "./lib", - "lib": ["es2015", "dom"] + "lib": ["es2015", "esnext.asynciterable", "dom"], + "jsx": "react" }, "include": ["src/**/*.ts"], "exclude": [ From 191065ce765855e4e7bae86b1a82fe2483b91e72 Mon Sep 17 00:00:00 2001 From: hwillson Date: Mon, 16 Sep 2019 18:30:55 -0400 Subject: [PATCH 05/28] Bring in React Apollo source This commit kicks of the initial merge of React Apollo into Apollo Client. It merges in the source of: - @apollo/react-common - @apollo/react-hooks - @apollo/react-testing The HOC (@apollo/react-hoc) and components (@apollo/react-components) packages are not going to be brought into Apollo Client, and the React SSR package (@apollo/react-ssr) will also be kept separate for now. This commit represents the initial merge of the code (with changes required to get all tests passing), but does not represent the final state of Apollo Client's React integration. Now that the RA code is part of AC, work on leveraging the tighter RA/AC integration can proceed. --- src/react/context/ApolloConsumer.tsx | 25 + src/react/context/ApolloContext.ts | 21 + src/react/context/ApolloProvider.tsx | 38 + .../context/__tests__/ApolloConsumer.test.tsx | 67 + .../context/__tests__/ApolloProvider.test.tsx | 146 +++ src/react/data/MutationData.ts | 185 +++ src/react/data/OperationData.ts | 78 ++ src/react/data/QueryData.ts | 504 ++++++++ src/react/data/SubscriptionData.ts | 151 +++ .../hooks/__tests__/useApolloClient.test.tsx | 44 + .../hooks/__tests__/useLazyQuery.test.tsx | 452 +++++++ .../hooks/__tests__/useMutation.test.tsx | 334 +++++ src/react/hooks/__tests__/useQuery.test.tsx | 1152 +++++++++++++++++ .../hooks/__tests__/useSubscription.test.tsx | 293 +++++ src/react/hooks/useApolloClient.ts | 15 + src/react/hooks/useLazyQuery.ts | 15 + src/react/hooks/useMutation.ts | 37 + src/react/hooks/useQuery.ts | 15 + src/react/hooks/useSubscription.ts | 43 + src/react/hooks/utils/useBaseQuery.ts | 73 ++ src/react/hooks/utils/useDeepMemo.ts | 22 + src/react/index.ts | 13 + src/react/parser/__tests__/parser.test.ts | 227 ++++ src/react/parser/parser.ts | 115 ++ src/react/ssr/RenderPromises.ts | 98 ++ src/react/testing/__tests__/README.md | 3 + src/react/testing/index.ts | 12 + src/react/testing/mocks/MockedProvider.tsx | 52 + src/react/testing/mocks/mockLink.ts | 158 +++ .../testing/mocks/mockSubscriptionLink.ts | 46 + src/react/testing/mocks/types.ts | 37 + src/react/testing/utils/createClient.ts | 20 + src/react/testing/utils/stripSymbols.ts | 7 + src/react/testing/utils/wait.ts | 3 + src/react/types/types.ts | 263 ++++ 35 files changed, 4764 insertions(+) create mode 100644 src/react/context/ApolloConsumer.tsx create mode 100644 src/react/context/ApolloContext.ts create mode 100644 src/react/context/ApolloProvider.tsx create mode 100644 src/react/context/__tests__/ApolloConsumer.test.tsx create mode 100644 src/react/context/__tests__/ApolloProvider.test.tsx create mode 100644 src/react/data/MutationData.ts create mode 100644 src/react/data/OperationData.ts create mode 100644 src/react/data/QueryData.ts create mode 100644 src/react/data/SubscriptionData.ts create mode 100644 src/react/hooks/__tests__/useApolloClient.test.tsx create mode 100644 src/react/hooks/__tests__/useLazyQuery.test.tsx create mode 100644 src/react/hooks/__tests__/useMutation.test.tsx create mode 100644 src/react/hooks/__tests__/useQuery.test.tsx create mode 100644 src/react/hooks/__tests__/useSubscription.test.tsx create mode 100644 src/react/hooks/useApolloClient.ts create mode 100644 src/react/hooks/useLazyQuery.ts create mode 100644 src/react/hooks/useMutation.ts create mode 100644 src/react/hooks/useQuery.ts create mode 100644 src/react/hooks/useSubscription.ts create mode 100644 src/react/hooks/utils/useBaseQuery.ts create mode 100644 src/react/hooks/utils/useDeepMemo.ts create mode 100644 src/react/index.ts create mode 100644 src/react/parser/__tests__/parser.test.ts create mode 100644 src/react/parser/parser.ts create mode 100644 src/react/ssr/RenderPromises.ts create mode 100644 src/react/testing/__tests__/README.md create mode 100644 src/react/testing/index.ts create mode 100644 src/react/testing/mocks/MockedProvider.tsx create mode 100644 src/react/testing/mocks/mockLink.ts create mode 100644 src/react/testing/mocks/mockSubscriptionLink.ts create mode 100644 src/react/testing/mocks/types.ts create mode 100644 src/react/testing/utils/createClient.ts create mode 100644 src/react/testing/utils/stripSymbols.ts create mode 100644 src/react/testing/utils/wait.ts create mode 100644 src/react/types/types.ts diff --git a/src/react/context/ApolloConsumer.tsx b/src/react/context/ApolloConsumer.tsx new file mode 100644 index 00000000000..2e8a2ac4efa --- /dev/null +++ b/src/react/context/ApolloConsumer.tsx @@ -0,0 +1,25 @@ +import React from 'react'; +import { invariant } from 'ts-invariant'; + +import ApolloClient from '../../ApolloClient'; +import { getApolloContext } from './ApolloContext'; + +export interface ApolloConsumerProps { + children: (client: ApolloClient) => React.ReactChild | null; +} + +export const ApolloConsumer: React.FC = props => { + const ApolloContext = getApolloContext(); + return ( + + {(context: any) => { + invariant( + context && context.client, + 'Could not find "client" in the context of ApolloConsumer. ' + + 'Wrap the root component in an .' + ); + return props.children(context.client); + }} + + ); +}; diff --git a/src/react/context/ApolloContext.ts b/src/react/context/ApolloContext.ts new file mode 100644 index 00000000000..e80c6299004 --- /dev/null +++ b/src/react/context/ApolloContext.ts @@ -0,0 +1,21 @@ +import React from 'react'; + +import ApolloClient from '../../ApolloClient'; + +export interface ApolloContextValue { + client?: ApolloClient; + renderPromises?: Record; +} + +let apolloContext: React.Context; + +export function getApolloContext() { + if (!apolloContext) { + apolloContext = React.createContext({}); + } + return apolloContext; +} + +export function resetApolloContext() { + apolloContext = React.createContext({}); +} diff --git a/src/react/context/ApolloProvider.tsx b/src/react/context/ApolloProvider.tsx new file mode 100644 index 00000000000..1bb98962e6d --- /dev/null +++ b/src/react/context/ApolloProvider.tsx @@ -0,0 +1,38 @@ +import React from 'react'; +import { invariant } from 'ts-invariant'; + +import ApolloClient from '../../ApolloClient'; +import { getApolloContext } from './ApolloContext'; + +export interface ApolloProviderProps { + client: ApolloClient; + children: React.ReactNode | React.ReactNode[] | null; +} + +export const ApolloProvider: React.FC> = ({ + client, + children +}) => { + const ApolloContext = getApolloContext(); + return ( + + {(context = {}) => { + if (client && context.client !== client) { + context = Object.assign({}, context, { client }); + } + + invariant( + context.client, + 'ApolloProvider was not passed a client instance. Make ' + + 'sure you pass in your client via the "client" prop.' + ); + + return ( + + {children} + + ); + }} + + ); +}; diff --git a/src/react/context/__tests__/ApolloConsumer.test.tsx b/src/react/context/__tests__/ApolloConsumer.test.tsx new file mode 100644 index 00000000000..f3f8ed35474 --- /dev/null +++ b/src/react/context/__tests__/ApolloConsumer.test.tsx @@ -0,0 +1,67 @@ +import React from 'react'; +import { ApolloLink } from 'apollo-link'; +import { render, cleanup } from '@testing-library/react'; + +import ApolloClient from '../../../ApolloClient'; +import { InMemoryCache as Cache } from '../../../cache/inmemory/inMemoryCache'; +import { ApolloProvider } from '../ApolloProvider'; +import { ApolloConsumer } from '../ApolloConsumer'; +import { getApolloContext } from '../ApolloContext'; + +const client = new ApolloClient({ + cache: new Cache(), + link: new ApolloLink((o, f) => (f ? f(o) : null)) +}); + +describe(' component', () => { + afterEach(cleanup); + + it('has a render prop', done => { + render( + + + {clientRender => { + try { + expect(clientRender).toBe(client); + done(); + } catch (e) { + done.fail(e); + } + return null; + }} + + + ); + }); + + it('renders the content in the children prop', () => { + const { getByText } = render( + + {() =>
Test
}
+
+ ); + + expect(getByText('Test')).toBeTruthy(); + }); + + it('errors if there is no client in the context', () => { + // Prevent Error about missing context type from appearing in the console. + const errorLogger = console.error; + console.error = () => {}; + expect(() => { + // We're wrapping the `ApolloConsumer` component in a + // `ApolloContext.Provider` component, to reset the context before + // testing. + const ApolloContext = getApolloContext(); + render( + + {() => null} + + ); + }).toThrowError( + 'Could not find "client" in the context of ApolloConsumer. Wrap the root component in an ' + ); + + console.error = errorLogger; + }); +}); diff --git a/src/react/context/__tests__/ApolloProvider.test.tsx b/src/react/context/__tests__/ApolloProvider.test.tsx new file mode 100644 index 00000000000..2a10fa6bb1f --- /dev/null +++ b/src/react/context/__tests__/ApolloProvider.test.tsx @@ -0,0 +1,146 @@ +import React, { useContext } from 'react'; +import { render, cleanup } from '@testing-library/react'; +import { ApolloLink } from 'apollo-link'; + +import ApolloClient from '../../../ApolloClient'; +import { InMemoryCache as Cache } from '../../../cache/inmemory/inMemoryCache'; +import { ApolloProvider } from '../ApolloProvider'; +import { getApolloContext } from '../ApolloContext'; + +describe(' Component', () => { + afterEach(cleanup); + + const client = new ApolloClient({ + cache: new Cache(), + link: new ApolloLink((o, f) => (f ? f(o) : null)) + }); + + class Child extends React.Component { + static contextType = getApolloContext(); + + componentDidUpdate() { + if (this.props.data) this.props.data.refetch(); + } + + render() { + return null; + } + } + + interface Props { + client: ApolloClient; + } + + class Container extends React.Component { + constructor(props: Props) { + super(props); + this.state = {}; + } + + componentDidMount() { + this.setState({ + client: this.props.client + }); + } + + render() { + return ( + + + + ); + } + } + + it('should render children components', () => { + const { getByText } = render( + +
Test
+
+ ); + + expect(getByText('Test')).toBeTruthy(); + }); + + it('should support the 2.0', () => { + const { getByText } = render( + }> +
Test
+
+ ); + + expect(getByText('Test')).toBeTruthy(); + }); + + it('should require a client', () => { + const originalConsoleError = console.error; + console.error = () => { + /* noop */ + }; + expect(() => { + // Before testing `ApolloProvider`, we first fully reset the + // existing context using `ApolloContext.Provider` directly. + const ApolloContext = getApolloContext(); + render( + + +
+ + + ); + }).toThrowError( + 'ApolloProvider was not passed a client instance. Make ' + + 'sure you pass in your client via the "client" prop.' + ); + console.error = originalConsoleError; + }); + + it('should not require a store', () => { + const { getByText } = render( + +
Test
+
+ ); + expect(getByText('Test')).toBeTruthy(); + }); + + it('should add the client to the children context', () => { + const TestChild = () => { + const context = useContext(getApolloContext()); + expect(context.client).toEqual(client); + return null; + }; + render( + + + + + ); + }); + + it('should update props when the client changes', () => { + let clientToCheck = client; + + const TestChild = () => { + const context = useContext(getApolloContext()); + expect(context.client).toEqual(clientToCheck); + return null; + }; + const { rerender } = render( + + + + ); + + const newClient = new ApolloClient({ + cache: new Cache(), + link: new ApolloLink((o, f) => (f ? f(o) : null)) + }); + clientToCheck = newClient; + rerender( + + + + ); + }); +}); diff --git a/src/react/data/MutationData.ts b/src/react/data/MutationData.ts new file mode 100644 index 00000000000..226dfa809db --- /dev/null +++ b/src/react/data/MutationData.ts @@ -0,0 +1,185 @@ +import { equal as isEqual } from '@wry/equality'; + +import { ApolloContextValue } from '../context/ApolloContext'; +import { DocumentType } from '../parser/parser'; +import { ApolloError } from '../../errors/ApolloError'; +import { + MutationOptions, + MutationTuple, + ExecutionResult, + MutationFunctionOptions, + MutationResult +} from '../types/types'; +import { OperationData } from './OperationData'; +import { OperationVariables } from '../../core/types'; + +export class MutationData< + TData = any, + TVariables = OperationVariables +> extends OperationData { + private mostRecentMutationId: number; + private result: MutationResult; + private previousResult?: MutationResult; + private setResult: (result: MutationResult) => any; + + constructor({ + options, + context, + result, + setResult + }: { + options: MutationOptions; + context: ApolloContextValue; + result: MutationResult; + setResult: (result: MutationResult) => any; + }) { + super(options, context); + this.verifyDocumentType(options.mutation, DocumentType.Mutation); + this.result = result; + this.setResult = setResult; + this.mostRecentMutationId = 0; + } + + public execute(result: MutationResult) { + this.isMounted = true; + this.verifyDocumentType(this.getOptions().mutation, DocumentType.Mutation); + result.client = this.refreshClient().client; + return [this.runMutation, result] as MutationTuple; + } + + public afterExecute() { + this.isMounted = true; + return this.unmount.bind(this); + } + + public cleanup() { + // No cleanup required. + } + + private runMutation = ( + mutationFunctionOptions: MutationFunctionOptions< + TData, + TVariables + > = {} as MutationFunctionOptions + ) => { + this.onMutationStart(); + const mutationId = this.generateNewMutationId(); + + return this.mutate(mutationFunctionOptions) + .then((response: ExecutionResult) => { + this.onMutationCompleted(response, mutationId); + return response; + }) + .catch((error: ApolloError) => { + this.onMutationError(error, mutationId); + if (!this.getOptions().onError) throw error; + }); + }; + + private mutate( + mutationFunctionOptions: MutationFunctionOptions + ) { + const { + mutation, + variables, + optimisticResponse, + update, + context: mutationContext = {}, + awaitRefetchQueries = false, + fetchPolicy + } = this.getOptions(); + const mutateOptions = { ...mutationFunctionOptions }; + + const mutateVariables = Object.assign( + {}, + variables, + mutateOptions.variables + ); + delete mutateOptions.variables; + + return this.refreshClient().client.mutate({ + mutation, + optimisticResponse, + refetchQueries: + mutateOptions.refetchQueries || this.getOptions().refetchQueries, + awaitRefetchQueries, + update, + context: mutationContext, + fetchPolicy, + variables: mutateVariables, + ...mutateOptions + }); + } + + private onMutationStart() { + if (!this.result.loading && !this.getOptions().ignoreResults) { + this.updateResult({ + loading: true, + error: undefined, + data: undefined, + called: true + }); + } + } + + private onMutationCompleted( + response: ExecutionResult, + mutationId: number + ) { + const { onCompleted, ignoreResults } = this.getOptions(); + + const { data, errors } = response; + const error = + errors && errors.length > 0 + ? new ApolloError({ graphQLErrors: errors }) + : undefined; + + const callOncomplete = () => + onCompleted ? onCompleted(data as TData) : null; + + if (this.isMostRecentMutation(mutationId) && !ignoreResults) { + this.updateResult({ + called: true, + loading: false, + data, + error + }); + } + callOncomplete(); + } + + private onMutationError(error: ApolloError, mutationId: number) { + const { onError } = this.getOptions(); + + if (this.isMostRecentMutation(mutationId)) { + this.updateResult({ + loading: false, + error, + data: undefined, + called: true + }); + } + + if (onError) { + onError(error); + } + } + + private generateNewMutationId(): number { + return ++this.mostRecentMutationId; + } + + private isMostRecentMutation(mutationId: number) { + return this.mostRecentMutationId === mutationId; + } + + private updateResult(result: MutationResult) { + if ( + this.isMounted && + (!this.previousResult || !isEqual(this.previousResult, result)) + ) { + this.setResult(result); + this.previousResult = result; + } + } +} diff --git a/src/react/data/OperationData.ts b/src/react/data/OperationData.ts new file mode 100644 index 00000000000..021a939a3d0 --- /dev/null +++ b/src/react/data/OperationData.ts @@ -0,0 +1,78 @@ +import { DocumentNode } from 'graphql'; +import { equal as isEqual } from '@wry/equality'; +import { invariant } from 'ts-invariant'; + +import ApolloClient from '../../ApolloClient'; +import { DocumentType, parser, operationName } from '../parser/parser'; +import { ApolloContextValue } from '../context/ApolloContext'; +import { CommonOptions } from '../types/types'; + +export abstract class OperationData { + public isMounted: boolean = false; + public previousOptions: CommonOptions = {} as CommonOptions< + TOptions + >; + public context: ApolloContextValue = {}; + public client: ApolloClient | undefined; + + private options: CommonOptions = {} as CommonOptions; + + constructor(options?: CommonOptions, context?: ApolloContextValue) { + this.options = options || ({} as CommonOptions); + this.context = context || {}; + } + + public getOptions(): CommonOptions { + return this.options; + } + + public setOptions(newOptions: CommonOptions) { + if (!isEqual(this.options, newOptions)) { + this.previousOptions = this.options; + } + this.options = newOptions; + } + + public abstract execute(...args: any): any; + public abstract afterExecute(...args: any): void | (() => void); + public abstract cleanup(): void; + + protected unmount() { + this.isMounted = false; + } + + protected refreshClient() { + const client = + (this.options && this.options.client) || + (this.context && this.context.client); + + invariant( + !!client, + 'Could not find "client" in the context or passed in as an option. ' + + 'Wrap the root component in an , or pass an ' + + 'ApolloClient instance in via options.' + ); + + let isNew = false; + if (client !== this.client) { + isNew = true; + this.client = client; + this.cleanup(); + } + return { + client: this.client as ApolloClient, + isNew + }; + } + + protected verifyDocumentType(document: DocumentNode, type: DocumentType) { + const operation = parser(document); + const requiredOperationName = operationName(type); + const usedOperationName = operationName(operation.type); + invariant( + operation.type === type, + `Running a ${requiredOperationName} requires a graphql ` + + `${requiredOperationName}, but a ${usedOperationName} was used instead.` + ); + } +} diff --git a/src/react/data/QueryData.ts b/src/react/data/QueryData.ts new file mode 100644 index 00000000000..0064692d17a --- /dev/null +++ b/src/react/data/QueryData.ts @@ -0,0 +1,504 @@ +import { equal as isEqual } from '@wry/equality'; + +import { ApolloQueryResult } from '../../core/types'; +import { ApolloError } from '../../errors/ApolloError'; +import { NetworkStatus } from '../../core/networkStatus'; +import { + FetchMoreQueryOptions, + SubscribeToMoreOptions +} from '../../core/watchQueryOptions'; +import { + FetchMoreOptions, + UpdateQueryOptions +} from '../../core/ObservableQuery'; +import { ApolloContextValue } from '../context/ApolloContext'; +import { DocumentType } from '../parser/parser'; +import { + QueryResult, + QueryPreviousData, + QueryOptions, + QueryCurrentObservable, + QueryTuple, + QueryLazyOptions, + ObservableQueryFields +} from '../types/types'; +import { OperationData } from './OperationData'; + +export class QueryData extends OperationData { + private previousData: QueryPreviousData = {}; + private currentObservable: QueryCurrentObservable = {}; + private forceUpdate: any; + + private runLazy: boolean = false; + private lazyOptions?: QueryLazyOptions; + + constructor({ + options, + context, + forceUpdate + }: { + options: QueryOptions; + context: ApolloContextValue; + forceUpdate: any; + }) { + super(options, context); + this.forceUpdate = forceUpdate; + } + + public execute(): QueryResult { + this.refreshClient(); + + const { skip, query, ssr } = this.getOptions(); + if (skip || query !== this.previousData.query) { + this.removeQuerySubscription(); + this.previousData.query = query; + } + + this.updateObservableQuery(); + + if (this.isMounted) this.startQuerySubscription(); + + const ssrDisabled = ssr === false; + + return this.getExecuteSsrResult(ssrDisabled) || this.getExecuteResult(); + } + + public executeLazy(): QueryTuple { + return !this.runLazy + ? [ + this.runLazyQuery, + { + loading: false, + networkStatus: NetworkStatus.ready, + called: false, + data: undefined + } as QueryResult + ] + : [this.runLazyQuery, this.execute()]; + } + + // For server-side rendering + public fetchData(): Promise> | boolean { + const options = this.getOptions(); + if (options.skip || options.ssr === false) return false; + + // currentObservable.query is already assigned the registered SSR observable in initializeObservableQuery. + const obs = this.currentObservable.query!; + const currentResult = obs.getCurrentResult(); + return currentResult.loading ? obs.result() : false; + } + + public afterExecute({ lazy = false }: { lazy?: boolean } = {}) { + this.isMounted = true; + if (!lazy || this.runLazy) { + this.handleErrorOrCompleted(); + + // When the component is done rendering stored query errors, we'll + // remove those errors from the `ObservableQuery` query store, so they + // aren't re-displayed on subsequent (potentially error free) + // requests/responses. + setTimeout(() => { + this.currentObservable.query && + this.currentObservable.query.resetQueryStoreErrors(); + }); + } + + return this.unmount.bind(this); + } + + public cleanup() { + this.removeQuerySubscription(); + delete this.currentObservable.query; + delete this.previousData.result; + } + + public getOptions() { + const options = super.getOptions(); + const lazyOptions = this.lazyOptions || {}; + const updatedOptions = { + ...options, + variables: { + ...options.variables, + ...lazyOptions.variables + }, + context: { + ...options.context, + ...lazyOptions.context + } + }; + + // skip is not supported when using lazy query execution. + if (this.runLazy) { + delete updatedOptions.skip; + } + + return updatedOptions; + } + + private runLazyQuery = (options?: QueryLazyOptions) => { + this.cleanup(); + + this.runLazy = true; + this.lazyOptions = options; + this.forceUpdate(); + }; + + private getExecuteResult = (): QueryResult => { + const result = this.getQueryResult(); + this.startQuerySubscription(); + return result; + }; + + private getExecuteSsrResult(ssrDisabled: boolean) { + let result; + + if (this.context && this.context.renderPromises) { + const ssrLoading = { + loading: true, + networkStatus: NetworkStatus.loading, + called: true, + data: undefined + }; + + // SSR is disabled, so just return the loading event and leave it in that state. + if (ssrDisabled) { + return ssrLoading; + } + + result = this.context.renderPromises.addQueryPromise( + this, + this.getExecuteResult + ); + if (!result) { + result = ssrLoading as QueryResult; + } + } + + return result; + } + + private prepareObservableQueryOptions() { + const options = this.getOptions(); + this.verifyDocumentType(options.query, DocumentType.Query); + const displayName = options.displayName || 'Query'; + + // Set the fetchPolicy to cache-first for network-only and cache-and-network + // fetches for server side renders. + if ( + this.context && + this.context.renderPromises && + (options.fetchPolicy === 'network-only' || + options.fetchPolicy === 'cache-and-network') + ) { + options.fetchPolicy = 'cache-first'; + } + + return { + ...options, + displayName, + context: options.context || {}, + metadata: { reactComponent: { displayName } } + }; + } + + private initializeObservableQuery() { + // See if there is an existing observable that was used to fetch the same + // data and if so, use it instead since it will contain the proper queryId + // to fetch the result set. This is used during SSR. + if (this.context && this.context.renderPromises) { + this.currentObservable.query = this.context.renderPromises.getSSRObservable( + this.getOptions() + ); + } + + if (!this.currentObservable.query) { + const observableQueryOptions = this.prepareObservableQueryOptions(); + this.previousData.observableQueryOptions = { + ...observableQueryOptions, + children: null + }; + this.currentObservable.query = this.refreshClient().client.watchQuery( + observableQueryOptions + ); + + if (this.context && this.context.renderPromises) { + this.context.renderPromises.registerSSRObservable( + this.currentObservable.query, + observableQueryOptions + ); + } + } + } + + private updateObservableQuery() { + // If we skipped initially, we may not have yet created the observable + if (!this.currentObservable.query) { + this.initializeObservableQuery(); + return; + } + + const newObservableQueryOptions = { + ...this.prepareObservableQueryOptions(), + children: null + }; + + if ( + !isEqual( + newObservableQueryOptions, + this.previousData.observableQueryOptions + ) + ) { + this.previousData.observableQueryOptions = newObservableQueryOptions; + this.currentObservable + .query!.setOptions(newObservableQueryOptions) + // The error will be passed to the child container, so we don't + // need to log it here. We could conceivably log something if + // an option was set. OTOH we don't log errors w/ the original + // query. See https://github.com/apollostack/react-apollo/issues/404 + .catch(() => {}); + } + } + + private startQuerySubscription() { + if (this.currentObservable.subscription || this.getOptions().skip) return; + + const obsQuery = this.currentObservable.query!; + this.currentObservable.subscription = obsQuery.subscribe({ + next: ({ loading, networkStatus, data }) => { + const previousResult = this.previousData.result; + + if (previousResult) { + // Calls to `ObservableQuery.fetchMore` return a result before the + // `updateQuery` function fully finishes. This can lead to an + // extra un-necessary re-render since the initially returned data is + // the same as data that has already been rendered. We'll + // prevent the un-necessary render from happening, making sure + // `fetchMore` results are only rendered when `updateQuery` has + // completed. + if ( + previousResult.loading && + previousResult.networkStatus === NetworkStatus.fetchMore && + isEqual(previousResult.data, data) + ) { + return; + } + + // Make sure we're not attempting to re-render similar results + if ( + previousResult.loading === loading && + previousResult.networkStatus === networkStatus && + isEqual(previousResult.data, data) + ) { + return; + } + } + + this.forceUpdate(); + }, + error: error => { + this.resubscribeToQuery(); + if (!error.hasOwnProperty('graphQLErrors')) throw error; + + const previousResult = this.previousData.result; + if ( + (previousResult && previousResult.loading) || + !isEqual(error, this.previousData.error) + ) { + this.previousData.error = error; + this.forceUpdate(); + } + } + }); + } + + private resubscribeToQuery() { + this.removeQuerySubscription(); + + // Unfortunately, if `lastError` is set in the current + // `observableQuery` when the subscription is re-created, + // the subscription will immediately receive the error, which will + // cause it to terminate again. To avoid this, we first clear + // the last error/result from the `observableQuery` before re-starting + // the subscription, and restore it afterwards (so the subscription + // has a chance to stay open). + const lastError = this.currentObservable.query!.getLastError(); + const lastResult = this.currentObservable.query!.getLastResult(); + this.currentObservable.query!.resetLastResults(); + this.startQuerySubscription(); + Object.assign(this.currentObservable.query!, { + lastError, + lastResult + }); + } + + private getQueryResult(): QueryResult { + let result: any = this.observableQueryFields(); + const options = this.getOptions(); + + // When skipping a query (ie. we're not querying for data but still want + // to render children), make sure the `data` is cleared out and + // `loading` is set to `false` (since we aren't loading anything). + if (options.skip) { + result = { + ...result, + data: undefined, + error: undefined, + loading: false, + called: true + }; + } else { + // Fetch the current result (if any) from the store. + const currentResult = this.currentObservable.query!.getCurrentResult(); + const { loading, partial, networkStatus, errors } = currentResult; + let { error, data } = currentResult; + + // Until a set naming convention for networkError and graphQLErrors is + // decided upon, we map errors (graphQLErrors) to the error options. + if (errors && errors.length > 0) { + error = new ApolloError({ graphQLErrors: errors }); + } + + result = { + ...result, + loading, + networkStatus, + error, + called: true + }; + + if (loading) { + const previousData = + this.previousData.result && this.previousData.result.data; + result.data = + previousData && data + ? { + ...previousData, + ...data + } + : previousData || data; + } else if (error) { + Object.assign(result, { + data: (this.currentObservable.query!.getLastResult() || ({} as any)) + .data + }); + } else { + const { fetchPolicy } = this.currentObservable.query!.options; + const { partialRefetch } = options; + if ( + partialRefetch && + !data && + partial && + fetchPolicy !== 'cache-only' + ) { + // When a `Query` component is mounted, and a mutation is executed + // that returns the same ID as the mounted `Query`, but has less + // fields in its result, Apollo Client's `QueryManager` returns the + // data as `undefined` since a hit can't be found in the cache. + // This can lead to application errors when the UI elements rendered by + // the original `Query` component are expecting certain data values to + // exist, and they're all of a sudden stripped away. To help avoid + // this we'll attempt to refetch the `Query` data. + Object.assign(result, { + loading: true, + networkStatus: NetworkStatus.loading + }); + result.refetch(); + return result; + } + + result.data = data; + } + } + + result.client = this.client; + this.previousData.loading = + (this.previousData.result && this.previousData.result.loading) || false; + this.previousData.result = result; + return result; + } + + private handleErrorOrCompleted() { + const obsQuery = this.currentObservable.query; + if (!obsQuery) return; + + const { data, loading, error } = obsQuery.getCurrentResult(); + + if (!loading) { + const { query, variables, onCompleted, onError } = this.getOptions(); + + // No changes, so we won't call onError/onCompleted. + if ( + this.previousOptions && + !this.previousData.loading && + isEqual(this.previousOptions.query, query) && + isEqual(this.previousOptions.variables, variables) + ) { + return; + } + + if (onCompleted && !error) { + onCompleted(data); + } else if (onError && error) { + onError(error); + } + } + } + + private removeQuerySubscription() { + if (this.currentObservable.subscription) { + this.currentObservable.subscription.unsubscribe(); + delete this.currentObservable.subscription; + } + } + + private obsRefetch = (variables?: TVariables) => + this.currentObservable.query!.refetch(variables); + + private obsFetchMore = ( + fetchMoreOptions: FetchMoreQueryOptions & + FetchMoreOptions + ) => this.currentObservable.query!.fetchMore(fetchMoreOptions); + + private obsUpdateQuery = ( + mapFn: ( + previousQueryResult: TData, + options: UpdateQueryOptions + ) => TData + ) => this.currentObservable.query!.updateQuery(mapFn); + + private obsStartPolling = (pollInterval: number) => { + this.currentObservable && + this.currentObservable.query! && + this.currentObservable.query!.startPolling(pollInterval); + }; + + private obsStopPolling = () => { + this.currentObservable && + this.currentObservable.query! && + this.currentObservable.query!.stopPolling(); + }; + + private obsSubscribeToMore = < + TSubscriptionData = TData, + TSubscriptionVariables = TVariables + >( + options: SubscribeToMoreOptions< + TData, + TSubscriptionVariables, + TSubscriptionData + > + ) => this.currentObservable.query!.subscribeToMore(options); + + private observableQueryFields() { + const observable = this.currentObservable.query!; + return { + variables: observable.variables, + refetch: this.obsRefetch, + fetchMore: this.obsFetchMore, + updateQuery: this.obsUpdateQuery, + startPolling: this.obsStartPolling, + stopPolling: this.obsStopPolling, + subscribeToMore: this.obsSubscribeToMore + } as ObservableQueryFields; + } +} diff --git a/src/react/data/SubscriptionData.ts b/src/react/data/SubscriptionData.ts new file mode 100644 index 00000000000..0863f633803 --- /dev/null +++ b/src/react/data/SubscriptionData.ts @@ -0,0 +1,151 @@ +import { equal as isEqual } from '@wry/equality'; + +import { ApolloContextValue } from '../context/ApolloContext'; +import { OperationData } from './OperationData'; +import { + SubscriptionCurrentObservable, + SubscriptionOptions, + SubscriptionResult +} from '../types/types'; + +export class SubscriptionData< + TData = any, + TVariables = any +> extends OperationData> { + private setResult: any; + private currentObservable: SubscriptionCurrentObservable = {}; + + constructor({ + options, + context, + setResult + }: { + options: SubscriptionOptions; + context: ApolloContextValue; + setResult: any; + }) { + super(options, context); + this.setResult = setResult; + this.initialize(options); + } + + public execute(result: SubscriptionResult) { + if (this.getOptions().skip === true) { + this.cleanup(); + return { + loading: false, + error: undefined, + data: undefined, + variables: this.getOptions().variables + }; + } + + let currentResult = result; + if (this.refreshClient().isNew) { + currentResult = this.getLoadingResult(); + } + + let { shouldResubscribe } = this.getOptions(); + if (typeof shouldResubscribe === 'function') { + shouldResubscribe = !!shouldResubscribe(this.getOptions()); + } + + if ( + shouldResubscribe !== false && + this.previousOptions && + Object.keys(this.previousOptions).length > 0 && + (this.previousOptions.subscription !== this.getOptions().subscription || + !isEqual(this.previousOptions.variables, this.getOptions().variables) || + this.previousOptions.skip !== this.getOptions().skip) + ) { + this.cleanup(); + currentResult = this.getLoadingResult(); + } + + this.initialize(this.getOptions()); + this.startSubscription(); + + this.previousOptions = this.getOptions(); + return { ...currentResult, variables: this.getOptions().variables }; + } + + public afterExecute() { + this.isMounted = true; + } + + public cleanup() { + this.endSubscription(); + delete this.currentObservable.query; + } + + private initialize(options: SubscriptionOptions) { + if (this.currentObservable.query || this.getOptions().skip === true) return; + this.currentObservable.query = this.refreshClient().client.subscribe({ + query: options.subscription, + variables: options.variables, + fetchPolicy: options.fetchPolicy + }); + } + + private startSubscription() { + if (this.currentObservable.subscription) return; + this.currentObservable.subscription = this.currentObservable.query!.subscribe( + { + next: this.updateCurrentData.bind(this), + error: this.updateError.bind(this), + complete: this.completeSubscription.bind(this) + } + ); + } + + private getLoadingResult() { + return { + loading: true, + error: undefined, + data: undefined + }; + } + + private updateResult(result: SubscriptionResult) { + if (this.isMounted) { + this.setResult(result); + } + } + + private updateCurrentData(result: SubscriptionResult) { + const { onSubscriptionData } = this.getOptions(); + + this.updateResult({ + data: result.data, + loading: false, + error: undefined + }); + + if (onSubscriptionData) { + onSubscriptionData({ + client: this.refreshClient().client, + subscriptionData: result + }); + } + } + + private updateError(error: any) { + this.updateResult({ + error, + loading: false + }); + } + + private completeSubscription() { + const { onSubscriptionComplete } = this.getOptions(); + if (onSubscriptionComplete) onSubscriptionComplete(); + this.endSubscription(); + } + + private endSubscription() { + if (this.currentObservable.subscription) { + this.currentObservable.subscription.unsubscribe(); + delete this.currentObservable.subscription; + } + } +} diff --git a/src/react/hooks/__tests__/useApolloClient.test.tsx b/src/react/hooks/__tests__/useApolloClient.test.tsx new file mode 100644 index 00000000000..63b818225ab --- /dev/null +++ b/src/react/hooks/__tests__/useApolloClient.test.tsx @@ -0,0 +1,44 @@ +import React from 'react'; +import { render, cleanup } from '@testing-library/react'; +import { ApolloLink } from 'apollo-link'; +import { InvariantError } from 'ts-invariant'; + +import { ApolloProvider } from '../../context/ApolloProvider'; +import { resetApolloContext } from '../../context/ApolloContext'; +import ApolloClient from '../../../ApolloClient'; +import { InMemoryCache } from '../../../cache/inmemory/inMemoryCache'; +import { useApolloClient } from '../useApolloClient'; + +describe('useApolloClient Hook', () => { + afterEach(() => { + cleanup(); + resetApolloContext(); + }); + + it('should return a client instance from the context if available', () => { + const client = new ApolloClient({ + cache: new InMemoryCache(), + link: ApolloLink.empty() + }); + + function App() { + expect(useApolloClient()).toEqual(client); + return null; + } + + render( + + + + ); + }); + + it("should error if a client instance can't be found in the context", () => { + function App() { + expect(() => useApolloClient()).toThrow(InvariantError); + return null; + } + + render(); + }); +}); diff --git a/src/react/hooks/__tests__/useLazyQuery.test.tsx b/src/react/hooks/__tests__/useLazyQuery.test.tsx new file mode 100644 index 00000000000..52b82355aa9 --- /dev/null +++ b/src/react/hooks/__tests__/useLazyQuery.test.tsx @@ -0,0 +1,452 @@ +import React, { useState } from 'react'; +import { DocumentNode } from 'graphql'; +import gql from 'graphql-tag'; +import { render, wait } from '@testing-library/react'; + +import { MockedProvider } from '../../testing'; +import ApolloClient from '../../../ApolloClient'; +import { InMemoryCache } from '../../../cache/inmemory/inMemoryCache'; +import { ApolloProvider } from '../../context/ApolloProvider'; +import { useLazyQuery } from '../useLazyQuery'; + +describe('useLazyQuery Hook', () => { + const CAR_QUERY: DocumentNode = gql` + query { + cars { + make + model + vin + } + } + `; + + const CAR_RESULT_DATA = { + cars: [ + { + make: 'Audi', + model: 'RS8', + vin: 'DOLLADOLLABILL', + __typename: 'Car' + } + ] + }; + + const CAR_MOCKS = [ + { + request: { + query: CAR_QUERY + }, + result: { data: CAR_RESULT_DATA } + } + ]; + + it('should hold query execution until manually triggered', async () => { + let renderCount = 0; + const Component = () => { + const [execute, { loading, data }] = useLazyQuery(CAR_QUERY); + switch (renderCount) { + case 0: + expect(loading).toEqual(false); + setTimeout(() => { + execute(); + }); + break; + case 1: + expect(loading).toEqual(true); + break; + case 2: + expect(loading).toEqual(false); + expect(data).toEqual(CAR_RESULT_DATA); + break; + default: // Do nothing + } + renderCount += 1; + return null; + }; + + render( + + + + ); + + await wait(() => { + expect(renderCount).toBe(3); + }); + }); + + it('should set `called` to false by default', () => { + const Component = () => { + const [, { loading, called }] = useLazyQuery(CAR_QUERY); + expect(loading).toBeFalsy(); + expect(called).toBeFalsy(); + return null; + }; + + render( + + + + ); + }); + + it('should set `called` to true after calling the lazy execute function', async () => { + let renderCount = 0; + const Component = () => { + const [execute, { loading, called, data }] = useLazyQuery(CAR_QUERY); + switch (renderCount) { + case 0: + expect(loading).toBeFalsy(); + expect(called).toBeFalsy(); + setTimeout(() => { + execute(); + }); + break; + case 1: + expect(loading).toBeTruthy(); + expect(called).toBeTruthy(); + break; + case 2: + expect(loading).toEqual(false); + expect(called).toBeTruthy(); + expect(data).toEqual(CAR_RESULT_DATA); + break; + default: // Do nothing + } + renderCount += 1; + return null; + }; + + render( + + + + ); + + await wait(() => { + expect(renderCount).toBe(3); + }); + }); + + it('should override `skip` if lazy mode execution function is called', async () => { + let renderCount = 0; + const Component = () => { + const [execute, { loading, data }] = useLazyQuery(CAR_QUERY, { + skip: true + } as any); + switch (renderCount) { + case 0: + expect(loading).toBeFalsy(); + setTimeout(() => { + execute(); + }); + break; + case 1: + expect(loading).toBeTruthy(); + break; + case 2: + expect(loading).toEqual(false); + expect(data).toEqual(CAR_RESULT_DATA); + break; + default: // Do nothing + } + renderCount += 1; + return null; + }; + + render( + + + + ); + + await wait(() => { + expect(renderCount).toBe(3); + }); + }); + + it( + 'should use variables defined in hook options (if any), when running ' + + 'the lazy execution function', + async () => { + const CAR_QUERY: DocumentNode = gql` + query AllCars($year: Int!) { + cars(year: $year) @client { + make + year + } + } + `; + + const CAR_RESULT_DATA = [ + { + make: 'Audi', + year: 2000, + __typename: 'Car' + }, + { + make: 'Hyundai', + year: 2001, + __typename: 'Car' + } + ]; + + const client = new ApolloClient({ + cache: new InMemoryCache(), + resolvers: { + Query: { + cars(_root, { year }) { + return CAR_RESULT_DATA.filter(car => car.year === year); + } + } + } + }); + + let renderCount = 0; + const Component = () => { + const [execute, { loading, data }] = useLazyQuery(CAR_QUERY, { + variables: { year: 2001 } + }); + switch (renderCount) { + case 0: + expect(loading).toBeFalsy(); + setTimeout(() => { + execute(); + }); + break; + case 1: + expect(loading).toBeTruthy(); + break; + case 2: + expect(loading).toEqual(false); + expect(data.cars).toEqual([CAR_RESULT_DATA[1]]); + break; + default: // Do nothing + } + renderCount += 1; + return null; + }; + + render( + + + + ); + + await wait(() => { + expect(renderCount).toBe(3); + }); + } + ); + + it( + 'should use variables passed into lazy execution function, ' + + 'overriding similar variables defined in Hook options', + async () => { + const CAR_QUERY: DocumentNode = gql` + query AllCars($year: Int!) { + cars(year: $year) @client { + make + year + } + } + `; + + const CAR_RESULT_DATA = [ + { + make: 'Audi', + year: 2000, + __typename: 'Car' + }, + { + make: 'Hyundai', + year: 2001, + __typename: 'Car' + } + ]; + + const client = new ApolloClient({ + cache: new InMemoryCache(), + resolvers: { + Query: { + cars(_root, { year }) { + return CAR_RESULT_DATA.filter(car => car.year === year); + } + } + } + }); + + let renderCount = 0; + const Component = () => { + const [execute, { loading, data }] = useLazyQuery(CAR_QUERY, { + variables: { year: 2001 } + }); + switch (renderCount) { + case 0: + expect(loading).toBeFalsy(); + setTimeout(() => { + execute({ variables: { year: 2000 } }); + }); + break; + case 1: + expect(loading).toBeTruthy(); + break; + case 2: + expect(loading).toEqual(false); + expect(data.cars).toEqual([CAR_RESULT_DATA[0]]); + break; + default: // Do nothing + } + renderCount += 1; + return null; + }; + + render( + + + + ); + + await wait(() => { + expect(renderCount).toBe(3); + }); + } + ); + + it( + 'should fetch data each time the execution function is called, when ' + + 'using a "network-only" fetch policy', + async () => { + const data1 = CAR_RESULT_DATA; + + const data2 = { + cars: [ + { + make: 'Audi', + model: 'SQ5', + vin: 'POWERANDTRUNKSPACE', + __typename: 'Car' + } + ] + }; + + const mocks = [ + { + request: { + query: CAR_QUERY + }, + result: { data: data1 } + }, + { + request: { + query: CAR_QUERY + }, + result: { data: data2 } + } + ]; + + let renderCount = 0; + const Component = () => { + const [execute, { loading, data }] = useLazyQuery(CAR_QUERY, { + fetchPolicy: 'network-only' + }); + switch (renderCount) { + case 0: + expect(loading).toEqual(false); + setTimeout(() => { + execute(); + }); + break; + case 1: + expect(loading).toEqual(true); + break; + case 2: + expect(loading).toEqual(false); + expect(data).toEqual(data1); + setTimeout(() => { + execute(); + }); + break; + case 3: + expect(loading).toEqual(true); + break; + case 4: + expect(loading).toEqual(false); + expect(data).toEqual(data2); + break; + default: // Do nothing + } + renderCount += 1; + return null; + }; + + render( + + + + ); + + await wait(() => { + expect(renderCount).toBe(5); + }); + } + ); + + it('should only call onCompleted once per query run', async () => { + let renderCount = 0; + let onCompletedCount = 0; + const Component = () => { + const [_, setCounter] = useState(0); + const [execute, { loading, data }] = useLazyQuery(CAR_QUERY, { + onCompleted() { + onCompletedCount += 1; + } + }); + + switch (renderCount) { + case 0: + expect(loading).toEqual(false); + setTimeout(() => { + execute(); + }); + break; + case 1: + expect(loading).toEqual(true); + break; + case 2: + expect(loading).toEqual(false); + expect(data).toEqual(CAR_RESULT_DATA); + setTimeout(() => { + execute(); + }); + break; + case 3: + expect(loading).toEqual(false); + expect(data).toEqual(CAR_RESULT_DATA); + // Force a render to help make sure onCompleted isn't called again + // since the query isn't re-run. + setCounter(1); + break; + case 4: + expect(loading).toEqual(false); + expect(data).toEqual(CAR_RESULT_DATA); + break; + default: // Do nothing + } + + renderCount += 1; + return null; + }; + + render( + + + + ); + + await wait(() => { + expect(onCompletedCount).toBe(2); + expect(renderCount).toBe(5); + }); + }); +}); diff --git a/src/react/hooks/__tests__/useMutation.test.tsx b/src/react/hooks/__tests__/useMutation.test.tsx new file mode 100644 index 00000000000..cc48ff96d97 --- /dev/null +++ b/src/react/hooks/__tests__/useMutation.test.tsx @@ -0,0 +1,334 @@ +import React, { useEffect } from 'react'; +import { DocumentNode } from 'graphql'; +import gql from 'graphql-tag'; +import { render, cleanup, wait } from '@testing-library/react'; + +import { MockedProvider, mockSingleLink } from '../../testing'; +import ApolloClient from '../../../ApolloClient'; +import { InMemoryCache } from '../../../cache/inmemory/inMemoryCache'; +import { ApolloProvider } from '../../context/ApolloProvider'; +import { useMutation } from '../useMutation'; + +describe('useMutation Hook', () => { + interface Todo { + id: number; + description: string; + priority: string; + } + + const CREATE_TODO_MUTATION: DocumentNode = gql` + mutation createTodo($description: String!) { + createTodo(description: $description) { + id + description + priority + } + } + `; + + const CREATE_TODO_RESULT = { + createTodo: { + id: 1, + description: 'Get milk!', + priority: 'High', + __typename: 'Todo' + } + }; + + afterEach(cleanup); + + describe('General use', () => { + it('should handle a simple mutation properly', done => { + const variables = { + description: 'Get milk!' + }; + + const mocks = [ + { + request: { + query: CREATE_TODO_MUTATION, + variables + }, + result: { data: CREATE_TODO_RESULT } + } + ]; + + let renderCount = 0; + const Component = () => { + const [createTodo, { loading, data }] = useMutation( + CREATE_TODO_MUTATION + ); + switch (renderCount) { + case 0: + expect(loading).toBeFalsy(); + expect(data).toBeUndefined(); + createTodo({ variables }); + break; + case 1: + expect(loading).toBeTruthy(); + expect(data).toBeUndefined(); + break; + case 2: + expect(loading).toBeFalsy(); + expect(data).toEqual(CREATE_TODO_RESULT); + done(); + break; + default: + } + renderCount += 1; + return null; + }; + + render( + + + + ); + }); + + it('should be able to call mutations as an effect', done => { + const variables = { + description: 'Get milk!' + }; + + const mocks = [ + { + request: { + query: CREATE_TODO_MUTATION, + variables + }, + result: { data: CREATE_TODO_RESULT } + } + ]; + + let renderCount = 0; + const useCreateTodo = () => { + const [createTodo, { loading, data }] = useMutation( + CREATE_TODO_MUTATION + ); + + useEffect(() => { + createTodo({ variables }); + }, [variables]); + + switch (renderCount) { + case 0: + expect(loading).toBeFalsy(); + expect(data).toBeUndefined(); + break; + case 1: + expect(loading).toBeTruthy(); + expect(data).toBeUndefined(); + break; + case 2: + expect(loading).toBeFalsy(); + expect(data).toEqual(CREATE_TODO_RESULT); + done(); + break; + default: + } + renderCount += 1; + return null; + }; + + const Component = () => { + useCreateTodo(); + return null; + }; + + render( + + + + ); + }); + + it('should ensure the mutation callback function has a stable identity', done => { + const variables = { + description: 'Get milk!' + }; + + const mocks = [ + { + request: { + query: CREATE_TODO_MUTATION, + variables + }, + result: { data: CREATE_TODO_RESULT } + } + ]; + + let mutationFn: any; + let renderCount = 0; + const Component = () => { + const [createTodo, { loading, data }] = useMutation( + CREATE_TODO_MUTATION + ); + switch (renderCount) { + case 0: + mutationFn = createTodo; + expect(loading).toBeFalsy(); + expect(data).toBeUndefined(); + setTimeout(() => { + createTodo({ variables }); + }); + break; + case 1: + expect(mutationFn).toBe(createTodo); + expect(loading).toBeTruthy(); + expect(data).toBeUndefined(); + break; + case 2: + expect(loading).toBeFalsy(); + expect(data).toEqual(CREATE_TODO_RESULT); + done(); + break; + default: + } + renderCount += 1; + return null; + }; + + render( + + + + ); + }); + + it('should resolve mutate function promise with mutation results', done => { + const variables = { + description: 'Get milk!' + }; + + const mocks = [ + { + request: { + query: CREATE_TODO_MUTATION, + variables + }, + result: { data: CREATE_TODO_RESULT } + } + ]; + + const Component = () => { + const [createTodo] = useMutation<{ createTodo: Todo }>( + CREATE_TODO_MUTATION + ); + + async function doIt() { + const { data } = await createTodo({ variables }); + expect(data).toEqual(CREATE_TODO_RESULT); + expect(data!.createTodo.description).toEqual( + CREATE_TODO_RESULT.createTodo.description + ); + done(); + } + + useEffect(() => { + doIt(); + }, []); + + return null; + }; + + render( + + + + ); + }); + + it('should return the current client instance in the result object', async () => { + const Component = () => { + const [, { client }] = useMutation(CREATE_TODO_MUTATION); + expect(client).toBeDefined(); + expect(client instanceof ApolloClient).toBeTruthy(); + return null; + }; + + render( + + + + ); + + await wait(); + }); + }); + + describe('Optimistic response', () => { + it('should support optimistic response handling', done => { + const optimisticResponse = { + __typename: 'Mutation', + createTodo: { + id: 1, + description: 'TEMPORARY', + priority: 'High', + __typename: 'Todo' + } + }; + + const variables = { + description: 'Get milk!' + }; + + const mocks = [ + { + request: { + query: CREATE_TODO_MUTATION, + variables + }, + result: { data: CREATE_TODO_RESULT } + } + ]; + + const link = mockSingleLink(...mocks); + const cache = new InMemoryCache(); + const client = new ApolloClient({ + cache, + link + }); + + let renderCount = 0; + const Component = () => { + const [createTodo, { loading, data }] = useMutation( + CREATE_TODO_MUTATION, + { optimisticResponse } + ); + + switch (renderCount) { + case 0: + expect(loading).toBeFalsy(); + expect(data).toBeUndefined(); + createTodo({ variables }); + + const dataInStore = client.cache.extract(true); + expect(dataInStore['Todo:1']).toEqual( + optimisticResponse.createTodo + ); + + break; + case 1: + expect(loading).toBeTruthy(); + expect(data).toBeUndefined(); + break; + case 2: + expect(loading).toBeFalsy(); + expect(data).toEqual(CREATE_TODO_RESULT); + done(); + break; + default: + } + renderCount += 1; + return null; + }; + + render( + + + + ); + }); + }); +}); diff --git a/src/react/hooks/__tests__/useQuery.test.tsx b/src/react/hooks/__tests__/useQuery.test.tsx new file mode 100644 index 00000000000..56a901ff594 --- /dev/null +++ b/src/react/hooks/__tests__/useQuery.test.tsx @@ -0,0 +1,1152 @@ +import React, { useState, useReducer } from 'react'; +import { DocumentNode, GraphQLError } from 'graphql'; +import gql from 'graphql-tag'; +import { render, cleanup, wait } from '@testing-library/react'; +import { ApolloLink, Observable } from 'apollo-link'; + +import { MockedProvider, MockLink } from '../../testing'; +import ApolloClient from '../../../ApolloClient'; +import { InMemoryCache } from '../../../cache/inmemory/inMemoryCache'; +import { ApolloProvider } from '../../context/ApolloProvider'; +import { useQuery } from '../useQuery'; + +describe('useQuery Hook', () => { + const CAR_QUERY: DocumentNode = gql` + query { + cars { + make + model + vin + } + } + `; + + const CAR_RESULT_DATA = { + cars: [ + { + make: 'Audi', + model: 'RS8', + vin: 'DOLLADOLLABILL', + __typename: 'Car' + } + ] + }; + + const CAR_MOCKS = [ + { + request: { + query: CAR_QUERY + }, + result: { data: CAR_RESULT_DATA } + } + ]; + + afterEach(cleanup); + + describe('General use', () => { + it('should handle a simple query properly', done => { + const Component = () => { + const { data, loading } = useQuery(CAR_QUERY); + if (!loading) { + expect(data).toEqual(CAR_RESULT_DATA); + done(); + } + return null; + }; + + render( + + + + ); + }); + + it('should keep data as undefined until data is actually returned', done => { + const Component = () => { + const { data, loading } = useQuery(CAR_QUERY); + if (loading) { + expect(data).toBeUndefined(); + } else { + expect(data).toEqual(CAR_RESULT_DATA); + done(); + } + return null; + }; + + render( + + + + ); + }); + + it('should ensure ObservableQuery fields have a stable identity', async () => { + let refetchFn: any; + let fetchMoreFn: any; + let updateQueryFn: any; + let startPollingFn: any; + let stopPollingFn: any; + let subscribeToMoreFn: any; + const Component = () => { + const { + loading, + refetch, + fetchMore, + updateQuery, + startPolling, + stopPolling, + subscribeToMore + } = useQuery(CAR_QUERY); + if (loading) { + refetchFn = refetch; + fetchMoreFn = fetchMore; + updateQueryFn = updateQuery; + startPollingFn = startPolling; + stopPollingFn = stopPolling; + subscribeToMoreFn = subscribeToMore; + } else { + expect(refetch).toBe(refetchFn); + expect(fetchMore).toBe(fetchMoreFn); + expect(updateQuery).toBe(updateQueryFn); + expect(startPolling).toBe(startPollingFn); + expect(stopPolling).toBe(stopPollingFn); + expect(subscribeToMore).toBe(subscribeToMoreFn); + } + return null; + }; + + render( + + + + ); + + await wait(); + }); + }); + + describe('Polling', () => { + it('should support polling', done => { + let renderCount = 0; + const Component = () => { + let { data, loading, stopPolling } = useQuery(CAR_QUERY, { + pollInterval: 10 + }); + switch (renderCount) { + case 0: + expect(loading).toBeTruthy(); + break; + case 1: + expect(loading).toBeFalsy(); + expect(data).toEqual(CAR_RESULT_DATA); + break; + case 2: + expect(loading).toBeFalsy(); + expect(data).toEqual(CAR_RESULT_DATA); + stopPolling(); + setTimeout(() => { + done(); + }, 10); + break; + case 3: + done.fail('Uh oh - we should have stopped polling!'); + break; + default: + // Do nothing + } + renderCount += 1; + return null; + }; + + render( + + + + ); + }); + + it('should stop polling when skip is true', done => { + let renderCount = 0; + const Component = () => { + const [shouldSkip, setShouldSkip] = useState(false); + let { data, loading } = useQuery(CAR_QUERY, { + pollInterval: 10, + skip: shouldSkip + }); + + switch (renderCount) { + case 0: + expect(loading).toBeTruthy(); + break; + case 1: + expect(loading).toBeFalsy(); + expect(data).toEqual(CAR_RESULT_DATA); + break; + case 2: + expect(loading).toBeFalsy(); + expect(data).toEqual(CAR_RESULT_DATA); + setShouldSkip(true); + break; + case 3: + expect(loading).toBeFalsy(); + expect(data).toBeUndefined(); + setTimeout(() => { + done(); + }, 10); + break; + case 4: + done.fail('Uh oh - we should have stopped polling!'); + break; + default: + // Do nothing + } + renderCount += 1; + return null; + }; + + render( + + + + ); + }); + + it('should stop polling when the component is unmounted', done => { + const mockLink = new MockLink(CAR_MOCKS); + const linkRequestSpy = jest.spyOn(mockLink, 'request'); + let renderCount = 0; + const QueryComponent = ({ unmount }: { unmount: () => void }) => { + const { data, loading } = useQuery(CAR_QUERY, { pollInterval: 10 }); + switch (renderCount) { + case 0: + expect(loading).toBeTruthy(); + break; + case 1: + expect(loading).toBeFalsy(); + expect(data).toEqual(CAR_RESULT_DATA); + expect(linkRequestSpy).toHaveBeenCalledTimes(1); + break; + case 2: + expect(loading).toBeFalsy(); + expect(data).toEqual(CAR_RESULT_DATA); + expect(linkRequestSpy).toHaveBeenCalledTimes(2); + unmount(); + break; + default: + } + renderCount += 1; + return null; + }; + + const Component = () => { + const [queryMounted, setQueryMounted] = useState(true); + const unmount = () => setTimeout(() => setQueryMounted(false), 0); + if (!queryMounted) + setTimeout(() => { + expect(linkRequestSpy).toHaveBeenCalledTimes(2); + done(); + }, 30); + return <>{queryMounted && }; + }; + + render( + + + + ); + }); + + it( + 'should not throw an error if `stopPolling` is called manually after ' + + 'a component has unmounted (even though polling has already been ' + + 'stopped automatically)', + async () => { + let unmount: any; + let renderCount = 0; + const Component = () => { + const { data, loading, stopPolling } = useQuery(CAR_QUERY, { + pollInterval: 10 + }); + switch (renderCount) { + case 0: + expect(loading).toBeTruthy(); + break; + case 1: + expect(loading).toBeFalsy(); + expect(data).toEqual(CAR_RESULT_DATA); + setTimeout(() => { + unmount(); + stopPolling(); + }); + break; + default: + } + renderCount += 1; + return null; + }; + + unmount = render( + + + + ).unmount; + + await wait(() => { + expect(renderCount).toBe(2); + }); + } + ); + + it('should set called to true by default', () => { + const Component = () => { + const { loading, called } = useQuery(CAR_QUERY); + expect(loading).toBeTruthy(); + expect(called).toBeTruthy(); + return null; + }; + + render( + + + + ); + }); + }); + + describe('Error handling', () => { + it("should render GraphQLError's", done => { + const query = gql` + query TestQuery { + rates(currency: "USD") { + rate + } + } + `; + + const mocks = [ + { + request: { query }, + result: { + errors: [new GraphQLError('forced error')] + } + } + ]; + + const Component = () => { + const { loading, error } = useQuery(query); + if (!loading) { + expect(error).toBeDefined(); + expect(error!.message).toEqual('GraphQL error: forced error'); + done(); + } + return null; + }; + + render( + + + + ); + }); + + it('should only call onError callbacks once', done => { + const query = gql` + query SomeQuery { + stuff { + thing + } + } + `; + + const resultData = { stuff: { thing: 'it!', __typename: 'Stuff' } }; + + let callCount = 0; + const link = new ApolloLink(() => { + if (!callCount) { + callCount += 1; + return new Observable(observer => { + observer.error(new Error('Oh no!')); + }); + } else { + return Observable.of({ data: resultData }); + } + }); + + const client = new ApolloClient({ + link, + cache: new InMemoryCache() + }); + + const onErrorMock = jest.fn(); + + let renderCount = 0; + const Component = () => { + const { loading, error, refetch, data, networkStatus } = useQuery( + query, + { + onError: onErrorMock, + notifyOnNetworkStatusChange: true + } + ); + + switch (renderCount) { + case 0: + expect(loading).toBeTruthy(); + break; + case 1: + expect(loading).toBeFalsy(); + expect(error).toBeDefined(); + expect(error!.message).toEqual('Network error: Oh no!'); + setTimeout(() => { + expect(onErrorMock.mock.calls.length).toBe(1); + refetch(); + }); + break; + case 2: + expect(loading).toBeTruthy(); + break; + case 3: + expect(loading).toBeFalsy(); + expect(data).toEqual(resultData); + done(); + break; + default: // Do nothing + } + + renderCount += 1; + return null; + }; + + render( + + + + ); + }); + + it('should persist errors on re-render if they are still valid', done => { + const query = gql` + query SomeQuery { + stuff { + thing + } + } + `; + + const mocks = [ + { + request: { query }, + result: { + errors: [new GraphQLError('forced error')] + } + } + ]; + + let renderCount = 0; + function App() { + const [_, forceUpdate] = useReducer(x => x + 1, 0); + const { loading, error } = useQuery(query); + + switch (renderCount) { + case 0: + expect(loading).toBeTruthy(); + expect(error).toBeUndefined(); + break; + case 1: + expect(error).toBeDefined(); + expect(error!.message).toEqual('GraphQL error: forced error'); + setTimeout(() => { + forceUpdate(0); + }); + break; + case 2: + expect(error).toBeDefined(); + expect(error!.message).toEqual('GraphQL error: forced error'); + done(); + break; + default: // Do nothing + } + + renderCount += 1; + return null; + } + + render( + + + + ); + }); + + it( + 'should persist errors on re-render when inlining onError and/or ' + + 'onCompleted callbacks', + async () => { + const query = gql` + query SomeQuery { + stuff { + thing + } + } + `; + + const mocks = [ + { + request: { query }, + result: { + errors: [new GraphQLError('forced error')] + } + } + ]; + + let renderCount = 0; + function App() { + const [_, forceUpdate] = useReducer(x => x + 1, 0); + const { loading, error } = useQuery(query, { + onError: () => {}, + onCompleted: () => {} + }); + + switch (renderCount) { + case 0: + expect(loading).toBeTruthy(); + expect(error).toBeUndefined(); + break; + case 1: + expect(error).toBeDefined(); + expect(error!.message).toEqual('GraphQL error: forced error'); + setTimeout(() => { + forceUpdate(0); + }); + break; + case 2: + expect(error).toBeDefined(); + expect(error!.message).toEqual('GraphQL error: forced error'); + break; + default: // Do nothing + } + + renderCount += 1; + return null; + } + + render( + + + + ); + + await wait(() => { + expect(renderCount).toBe(3); + }); + } + ); + + it('should render errors (different error messages) with loading done on refetch', async () => { + const query = gql` + query SomeQuery { + stuff { + thing + } + } + `; + + const mocks = [ + { + request: { query }, + result: { + errors: [new GraphQLError('an error 1')] + } + }, + { + request: { query }, + result: { + errors: [new GraphQLError('an error 2')] + } + } + ]; + + let renderCount = 0; + function App() { + const { loading, error, refetch } = useQuery(query, { + notifyOnNetworkStatusChange: true + }); + + switch (renderCount) { + case 0: + expect(loading).toBeTruthy(); + expect(error).toBeUndefined(); + break; + case 1: + expect(loading).toBeFalsy(); + expect(error).toBeDefined(); + expect(error!.message).toEqual('GraphQL error: an error 1'); + setTimeout(() => { + // catch here to avoid failing due to 'uncaught promise rejection' + refetch().catch(() => {}); + }); + break; + case 2: + expect(loading).toBeTruthy(); + break; + case 3: + expect(loading).toBeFalsy(); + expect(error).toBeDefined(); + expect(error!.message).toEqual('GraphQL error: an error 2'); + break; + default: // Do nothing + } + + renderCount += 1; + return null; + } + + render( + + + + ); + + await wait(() => { + expect(renderCount).toBe(4); + }); + }); + + it('should render errors (same error messages) with loading done on refetch', async () => { + const query = gql` + query SomeQuery { + stuff { + thing + } + } + `; + + const mocks = [ + { + request: { query }, + result: { + errors: [new GraphQLError('same error message')] + } + }, + { + request: { query }, + result: { + errors: [new GraphQLError('same error message')] + } + } + ]; + + let renderCount = 0; + function App() { + const { loading, error, refetch } = useQuery(query, { + notifyOnNetworkStatusChange: true + }); + + switch (renderCount) { + case 0: + expect(loading).toBeTruthy(); + expect(error).toBeUndefined(); + break; + case 1: + expect(loading).toBeFalsy(); + expect(error).toBeDefined(); + expect(error!.message).toEqual('GraphQL error: same error message'); + setTimeout(() => { + // catch here to avoid failing due to 'uncaught promise rejection' + refetch().catch(() => {}); + }); + break; + case 2: + expect(loading).toBeTruthy(); + break; + case 3: + expect(loading).toBeFalsy(); + expect(error).toBeDefined(); + expect(error!.message).toEqual('GraphQL error: same error message'); + break; + default: // Do nothing + } + + renderCount += 1; + return null; + } + + render( + + + + ); + + await wait(() => { + expect(renderCount).toBe(4); + }); + }); + + it('should render both success and errors (same error messages) with loading done on refetch', async () => { + const mocks = [ + { + request: { query: CAR_QUERY }, + result: { + errors: [new GraphQLError('same error message')] + } + }, + { + request: { query: CAR_QUERY }, + result: { + data: CAR_RESULT_DATA + } + }, + { + request: { query: CAR_QUERY }, + result: { + errors: [new GraphQLError('same error message')] + } + } + ]; + + let renderCount = 0; + function App() { + const { loading, data, error, refetch } = useQuery(CAR_QUERY, { + notifyOnNetworkStatusChange: true + }); + + switch (renderCount) { + case 0: + expect(loading).toBeTruthy(); + expect(error).toBeUndefined(); + break; + case 1: + expect(loading).toBeFalsy(); + expect(error).toBeDefined(); + expect(error!.message).toEqual('GraphQL error: same error message'); + setTimeout(() => { + // catch here to avoid failing due to 'uncaught promise rejection' + refetch().catch(() => {}); + }); + break; + case 2: + expect(loading).toBeTruthy(); + break; + case 3: + expect(loading).toBeFalsy(); + expect(error).toBeUndefined(); + expect(data).toEqual(CAR_RESULT_DATA); + setTimeout(() => { + // catch here to avoid failing due to 'uncaught promise rejection' + refetch().catch(() => {}); + }); + break; + case 4: + expect(loading).toBeTruthy(); + break; + case 5: + expect(loading).toBeFalsy(); + expect(error).toBeDefined(); + expect(error!.message).toEqual('GraphQL error: same error message'); + break; + default: // Do nothing + } + + renderCount += 1; + return null; + } + + render( + + + + ); + + await wait(() => { + expect(renderCount).toBe(6); + }); + }); + }); + + describe('Pagination', () => { + it( + 'should render `fetchMore.updateQuery` updated results with proper ' + + 'loading status, when `notifyOnNetworkStatusChange` is true', + async () => { + const carQuery: DocumentNode = gql` + query cars($limit: Int) { + cars(limit: $limit) { + id + make + model + vin + __typename + } + } + `; + + const carResults = { + cars: [ + { + id: 1, + make: 'Audi', + model: 'RS8', + vin: 'DOLLADOLLABILL', + __typename: 'Car' + } + ] + }; + + const moreCarResults = { + cars: [ + { + id: 2, + make: 'Audi', + model: 'eTron', + vin: 'TREESRGOOD', + __typename: 'Car' + } + ] + }; + + const mocks = [ + { + request: { query: carQuery, variables: { limit: 1 } }, + result: { data: carResults } + }, + { + request: { query: carQuery, variables: { limit: 1 } }, + result: { data: moreCarResults } + } + ]; + + let renderCount = 0; + function App() { + const { loading, data, fetchMore } = useQuery(carQuery, { + variables: { limit: 1 }, + notifyOnNetworkStatusChange: true + }); + + switch (renderCount) { + case 0: + expect(loading).toBeTruthy(); + break; + case 1: + expect(loading).toBeFalsy(); + expect(data).toEqual(carResults); + fetchMore({ + variables: { + limit: 1 + }, + updateQuery: (prev, { fetchMoreResult }) => ({ + cars: [...prev.cars, ...fetchMoreResult.cars] + }) + }); + break; + case 2: + expect(loading).toBeTruthy(); + break; + case 3: + expect(loading).toBeFalsy(); + expect(data).toEqual({ + cars: [carResults.cars[0], moreCarResults.cars[0]] + }); + break; + default: + } + + renderCount += 1; + return null; + } + + render( + + + + ); + + await wait(() => { + expect(renderCount).toBe(4); + }); + } + ); + + it( + 'should render `fetchMore.updateQuery` updated results with no ' + + 'loading status, when `notifyOnNetworkStatusChange` is false', + async () => { + const carQuery: DocumentNode = gql` + query cars($limit: Int) { + cars(limit: $limit) { + id + make + model + vin + __typename + } + } + `; + + const carResults = { + cars: [ + { + id: 1, + make: 'Audi', + model: 'RS8', + vin: 'DOLLADOLLABILL', + __typename: 'Car' + } + ] + }; + + const moreCarResults = { + cars: [ + { + id: 2, + make: 'Audi', + model: 'eTron', + vin: 'TREESRGOOD', + __typename: 'Car' + } + ] + }; + + const mocks = [ + { + request: { query: carQuery, variables: { limit: 1 } }, + result: { data: carResults } + }, + { + request: { query: carQuery, variables: { limit: 1 } }, + result: { data: moreCarResults } + } + ]; + + let renderCount = 0; + function App() { + const { loading, data, fetchMore } = useQuery(carQuery, { + variables: { limit: 1 }, + notifyOnNetworkStatusChange: false + }); + + switch (renderCount) { + case 0: + expect(loading).toBeTruthy(); + break; + case 1: + expect(loading).toBeFalsy(); + expect(data).toEqual(carResults); + fetchMore({ + variables: { + limit: 1 + }, + updateQuery: (prev, { fetchMoreResult }) => ({ + cars: [...prev.cars, ...fetchMoreResult.cars] + }) + }); + break; + case 2: + expect(loading).toBeFalsy(); + expect(data).toEqual({ + cars: [carResults.cars[0], moreCarResults.cars[0]] + }); + break; + default: + } + + renderCount += 1; + return null; + } + + render( + + + + ); + + await wait(() => { + expect(renderCount).toBe(3); + }); + } + ); + }); + + describe('Refetching', () => { + it('should properly handle refetching with different variables', async () => { + const carQuery: DocumentNode = gql` + query cars($id: Int) { + cars(id: $id) { + id + make + model + vin + __typename + } + } + `; + + const carData1 = { + cars: [ + { + id: 1, + make: 'Audi', + model: 'RS8', + vin: 'DOLLADOLLABILL', + __typename: 'Car' + } + ] + }; + + const carData2 = { + cars: [ + { + id: 2, + make: 'Audi', + model: 'eTron', + vin: 'TREESRGOOD', + __typename: 'Car' + } + ] + }; + + const mocks = [ + { + request: { query: carQuery, variables: { id: 1 } }, + result: { data: carData1 } + }, + { + request: { query: carQuery, variables: { id: 2 } }, + result: { data: carData2 } + }, + { + request: { query: carQuery, variables: { id: 1 } }, + result: { data: carData1 } + } + ]; + + let renderCount = 0; + function App() { + const { loading, data, refetch } = useQuery(carQuery, { + variables: { id: 1 } + }); + + switch (renderCount) { + case 0: + expect(loading).toBeTruthy(); + break; + case 1: + expect(loading).toBeFalsy(); + expect(data).toEqual(carData1); + refetch({ id: 2 }); + break; + case 2: + expect(loading).toBeTruthy(); + break; + case 3: + expect(loading).toBeFalsy(); + expect(data).toEqual(carData2); + refetch({ id: 1 }); + break; + case 4: + expect(loading).toBeTruthy(); + break; + case 5: + expect(loading).toBeFalsy(); + expect(data).toEqual(carData1); + break; + default: + } + + renderCount += 1; + return null; + } + + render( + + + + ); + + await wait(() => { + expect(renderCount).toBe(6); + }); + }); + }); + + describe('Callbacks', () => { + it( + 'should pass loaded data to onCompleted when using the cache-only ' + + 'fetch policy', + async () => { + const cache = new InMemoryCache(); + const client = new ApolloClient({ + cache, + resolvers: {} + }); + + cache.writeQuery({ + query: CAR_QUERY, + data: CAR_RESULT_DATA + }); + + let onCompletedCalled = false; + const Component = () => { + const { loading, data } = useQuery(CAR_QUERY, { + fetchPolicy: 'cache-only', + onCompleted(data) { + onCompletedCalled = true; + expect(data).toBeDefined(); + console.log(data); + } + }); + if (!loading) { + expect(data).toEqual(CAR_RESULT_DATA); + } + return null; + }; + + render( + + + + ); + + await wait(() => { + expect(onCompletedCalled).toBeTruthy(); + }); + } + ); + + it('should only call onCompleted once per query run', async () => { + const cache = new InMemoryCache(); + const client = new ApolloClient({ + cache, + resolvers: {} + }); + + cache.writeQuery({ + query: CAR_QUERY, + data: CAR_RESULT_DATA + }); + + let onCompletedCount = 0; + const Component = () => { + const { loading, data } = useQuery(CAR_QUERY, { + fetchPolicy: 'cache-only', + onCompleted() { + onCompletedCount += 1; + } + }); + if (!loading) { + expect(data).toEqual(CAR_RESULT_DATA); + } + return null; + }; + + render( + + + + ); + + await wait(() => { + expect(onCompletedCount).toBe(1); + }); + }); + }); +}); diff --git a/src/react/hooks/__tests__/useSubscription.test.tsx b/src/react/hooks/__tests__/useSubscription.test.tsx new file mode 100644 index 00000000000..e0ee56712a4 --- /dev/null +++ b/src/react/hooks/__tests__/useSubscription.test.tsx @@ -0,0 +1,293 @@ +import React from 'react'; +import { render, cleanup } from '@testing-library/react'; +import gql from 'graphql-tag'; + +import { MockSubscriptionLink } from '../../testing'; +import ApolloClient from '../../../ApolloClient'; +import { InMemoryCache as Cache } from '../../../cache/inmemory/inMemoryCache'; +import { ApolloProvider } from '../../context/ApolloProvider'; +import { useSubscription } from '../useSubscription'; + +describe('useSubscription Hook', () => { + afterEach(cleanup); + + it('should handle a simple subscription properly', done => { + const subscription = gql` + subscription { + car { + make + } + } + `; + + const results = ['Audi', 'BMW', 'Mercedes', 'Hyundai'].map(make => ({ + result: { data: { car: { make } } } + })); + + const link = new MockSubscriptionLink(); + const client = new ApolloClient({ + link, + cache: new Cache({ addTypename: false }) + }); + + let renderCount = 0; + const Component = () => { + const { loading, data, error } = useSubscription(subscription); + switch (renderCount) { + case 0: + expect(loading).toBe(true); + expect(error).toBeUndefined(); + expect(data).toBeUndefined(); + break; + case 1: + expect(loading).toBe(false); + expect(data).toEqual(results[0].result.data); + break; + case 2: + expect(loading).toBe(false); + expect(data).toEqual(results[1].result.data); + break; + case 3: + expect(loading).toBe(false); + expect(data).toEqual(results[2].result.data); + break; + case 4: + expect(loading).toBe(false); + expect(data).toEqual(results[3].result.data); + done(); + break; + default: + } + setTimeout(() => { + renderCount <= results.length && + link.simulateResult(results[renderCount - 1]); + }); + renderCount += 1; + return null; + }; + + render( + + + + ); + }); + + it('should cleanup after the subscription component has been unmounted', done => { + const subscription = gql` + subscription { + car { + make + } + } + `; + + const results = [ + { + result: { data: { car: { make: 'Pagani' } } } + } + ]; + + const link = new MockSubscriptionLink(); + const client = new ApolloClient({ + link, + cache: new Cache({ addTypename: false }) + }); + + let renderCount = 0; + let onSubscriptionDataCount = 0; + let unmount: any; + + const Component = () => { + const { loading, data, error } = useSubscription(subscription, { + onSubscriptionData() { + onSubscriptionDataCount += 1; + } + }); + switch (renderCount) { + case 0: + expect(loading).toBe(true); + expect(error).toBeUndefined(); + expect(data).toBeUndefined(); + link.simulateResult(results[0]); + break; + case 1: + expect(loading).toBe(false); + expect(data).toEqual(results[0].result.data); + + setTimeout(() => { + expect(onSubscriptionDataCount).toEqual(1); + + // After the component has been unmounted, the internal + // ObservableQuery should be stopped, meaning it shouldn't + // receive any new data (so the onSubscriptionDataCount should + // stay at 1). + unmount(); + link.simulateResult(results[0]); + setTimeout(() => { + expect(onSubscriptionDataCount).toEqual(1); + done(); + }); + }); + break; + default: + } + renderCount += 1; + return null; + }; + + unmount = render( + + + + ).unmount; + }); + + it('should never execute a subscription with the skip option', done => { + const subscription = gql` + subscription { + car { + make + } + } + `; + + const link = new MockSubscriptionLink(); + const client = new ApolloClient({ + link, + cache: new Cache({ addTypename: false }) + }); + + let renderCount = 0; + let onSubscriptionDataCount = 0; + let unmount: any; + + const Component = () => { + const { loading, data, error } = useSubscription(subscription, { + skip: true, + onSubscriptionData() { + onSubscriptionDataCount += 1; + } + }); + switch (renderCount) { + case 0: + expect(loading).toBe(false); + expect(error).toBeUndefined(); + expect(data).toBeUndefined(); + setTimeout(() => { + unmount(); + setTimeout(() => { + expect(onSubscriptionDataCount).toEqual(0); + expect(renderCount).toEqual(1); + done(); + }); + }); + break; + default: + } + renderCount += 1; + return null; + }; + + unmount = render( + + + + ).unmount; + }); + + it('should create a subscription after skip has changed from true to a falsy value', done => { + const subscription = gql` + subscription { + car { + make + } + } + `; + + const results = [ + { + result: { data: { car: { make: 'Pagani' } } } + }, + { + result: { data: { car: { make: 'Scoop' } } } + } + ]; + + const link = new MockSubscriptionLink(); + const client = new ApolloClient({ + link, + cache: new Cache({ addTypename: false }) + }); + + let renderCount = 0; + let unmount: any; + + const Component = () => { + const [, triggerRerender] = React.useState(0); + const [skip, setSkip] = React.useState(true); + const { loading, data, error } = useSubscription(subscription, { + skip + }); + switch (renderCount) { + case 0: + expect(loading).toBe(false); + expect(error).toBeUndefined(); + expect(data).toBeUndefined(); + setSkip(false); + break; + case 1: + expect(loading).toBe(true); + expect(error).toBeUndefined(); + expect(data).toBeUndefined(); + link.simulateResult(results[0]); + break; + case 2: + expect(loading).toBe(false); + expect(data).toEqual(results[0].result.data); + setSkip(true); + break; + case 3: + expect(loading).toBe(false); + expect(data).toBeUndefined(); + expect(error).toBeUndefined(); + // ensure state persists across rerenders + triggerRerender(i => i + 1); + break; + case 4: + expect(loading).toBe(false); + expect(data).toBeUndefined(); + expect(error).toBeUndefined(); + setSkip(false); + break; + case 5: + expect(loading).toBe(true); + expect(error).toBeUndefined(); + expect(data).toBeUndefined(); + link.simulateResult(results[1]); + break; + case 6: + expect(loading).toBe(false); + expect(error).toBeUndefined(); + expect(data).toEqual(results[1].result.data); + setTimeout(() => { + unmount(); + setTimeout(() => { + expect(renderCount).toEqual(7); + done(); + }); + }); + break; + default: + } + renderCount += 1; + return null; + }; + + unmount = render( + + + + ).unmount; + }); +}); diff --git a/src/react/hooks/useApolloClient.ts b/src/react/hooks/useApolloClient.ts new file mode 100644 index 00000000000..1d8788b5377 --- /dev/null +++ b/src/react/hooks/useApolloClient.ts @@ -0,0 +1,15 @@ +import React from 'react'; +import { invariant } from 'ts-invariant'; + +import ApolloClient from '../../ApolloClient'; +import { getApolloContext } from '../context/ApolloContext'; + +export function useApolloClient(): ApolloClient { + const { client } = React.useContext(getApolloContext()); + invariant( + client, + 'No Apollo Client instance can be found. Please ensure that you ' + + 'have called `ApolloProvider` higher up in your tree.' + ); + return client!; +} diff --git a/src/react/hooks/useLazyQuery.ts b/src/react/hooks/useLazyQuery.ts new file mode 100644 index 00000000000..03bf70c208d --- /dev/null +++ b/src/react/hooks/useLazyQuery.ts @@ -0,0 +1,15 @@ +import { DocumentNode } from 'graphql'; + +import { LazyQueryHookOptions, QueryTuple } from '../types/types'; +import { useBaseQuery } from './utils/useBaseQuery'; +import { OperationVariables } from '../../core/types'; + +export function useLazyQuery( + query: DocumentNode, + options?: LazyQueryHookOptions +) { + return useBaseQuery(query, options, true) as QueryTuple< + TData, + TVariables + >; +} diff --git a/src/react/hooks/useMutation.ts b/src/react/hooks/useMutation.ts new file mode 100644 index 00000000000..d06ba3dce44 --- /dev/null +++ b/src/react/hooks/useMutation.ts @@ -0,0 +1,37 @@ +import { useContext, useState, useRef, useEffect } from 'react'; +import { DocumentNode } from 'graphql'; + +import { getApolloContext } from '../context/ApolloContext'; +import { MutationHookOptions, MutationTuple } from '../types/types'; +import { MutationData } from '../data/MutationData'; +import { OperationVariables } from '../../core/types'; + +export function useMutation( + mutation: DocumentNode, + options?: MutationHookOptions +): MutationTuple { + const context = useContext(getApolloContext()); + const [result, setResult] = useState({ called: false, loading: false }); + const updatedOptions = options ? { ...options, mutation } : { mutation }; + + const mutationDataRef = useRef>(); + function getMutationDataRef() { + if (!mutationDataRef.current) { + mutationDataRef.current = new MutationData({ + options: updatedOptions, + context, + result, + setResult + }); + } + return mutationDataRef.current; + } + + const mutationData = getMutationDataRef(); + mutationData.setOptions(updatedOptions); + mutationData.context = context; + + useEffect(() => mutationData.afterExecute()); + + return mutationData.execute(result); +} diff --git a/src/react/hooks/useQuery.ts b/src/react/hooks/useQuery.ts new file mode 100644 index 00000000000..8823020d214 --- /dev/null +++ b/src/react/hooks/useQuery.ts @@ -0,0 +1,15 @@ +import { DocumentNode } from 'graphql'; + +import { QueryHookOptions, QueryResult } from '../types/types'; +import { useBaseQuery } from './utils/useBaseQuery'; +import { OperationVariables } from '../../core/types'; + +export function useQuery( + query: DocumentNode, + options?: QueryHookOptions +) { + return useBaseQuery(query, options, false) as QueryResult< + TData, + TVariables + >; +} diff --git a/src/react/hooks/useSubscription.ts b/src/react/hooks/useSubscription.ts new file mode 100644 index 00000000000..45583bbd4ff --- /dev/null +++ b/src/react/hooks/useSubscription.ts @@ -0,0 +1,43 @@ +import { useContext, useState, useRef, useEffect } from 'react'; +import { DocumentNode } from 'graphql'; + +import { getApolloContext } from '../context/ApolloContext'; +import { SubscriptionHookOptions } from '../types/types'; +import { SubscriptionData } from '../data/SubscriptionData'; +import { OperationVariables } from '../../core/types'; + +export function useSubscription( + subscription: DocumentNode, + options?: SubscriptionHookOptions +) { + const context = useContext(getApolloContext()); + const updatedOptions = options + ? { ...options, subscription } + : { subscription }; + const [result, setResult] = useState({ + loading: !updatedOptions.skip, + error: undefined, + data: undefined + }); + + const subscriptionDataRef = useRef>(); + function getSubscriptionDataRef() { + if (!subscriptionDataRef.current) { + subscriptionDataRef.current = new SubscriptionData({ + options: updatedOptions, + context, + setResult + }); + } + return subscriptionDataRef.current; + } + + const subscriptionData = getSubscriptionDataRef(); + subscriptionData.setOptions(updatedOptions); + subscriptionData.context = context; + + useEffect(() => subscriptionData.afterExecute()); + useEffect(() => subscriptionData.cleanup.bind(subscriptionData), []); + + return subscriptionData.execute(result); +} diff --git a/src/react/hooks/utils/useBaseQuery.ts b/src/react/hooks/utils/useBaseQuery.ts new file mode 100644 index 00000000000..38e8a458d3a --- /dev/null +++ b/src/react/hooks/utils/useBaseQuery.ts @@ -0,0 +1,73 @@ +import { useContext, useEffect, useReducer, useRef } from 'react'; +import { DocumentNode } from 'graphql'; + +import { getApolloContext } from '../../context/ApolloContext'; +import { + QueryHookOptions, + QueryOptions, + QueryTuple, + QueryResult, +} from '../../types/types'; +import { QueryData } from '../../data/QueryData'; +import { useDeepMemo } from './useDeepMemo'; +import { OperationVariables } from '../../../core/types'; + +export function useBaseQuery( + query: DocumentNode, + options?: QueryHookOptions, + lazy = false +) { + const context = useContext(getApolloContext()); + const [tick, forceUpdate] = useReducer(x => x + 1, 0); + const updatedOptions = options ? { ...options, query } : { query }; + + const queryDataRef = useRef>(); + + if (!queryDataRef.current) { + queryDataRef.current = new QueryData({ + options: updatedOptions as QueryOptions, + context, + forceUpdate + }); + } + + const queryData = queryDataRef.current; + queryData.setOptions(updatedOptions); + queryData.context = context; + + // `onError` and `onCompleted` callback functions will not always have a + // stable identity, so we'll exclude them from the memoization key to + // prevent `afterExecute` from being triggered un-necessarily. + const memo = { + options: { ...updatedOptions, onError: undefined, onCompleted: undefined }, + context, + tick + }; + + const result = useDeepMemo( + () => (lazy ? queryData.executeLazy() : queryData.execute()), + memo + ); + + const queryResult = lazy + ? (result as QueryTuple)[1] + : (result as QueryResult); + + useEffect( + () => queryData.afterExecute({ lazy }), + lazy + ? undefined + : [ + queryResult.loading, + queryResult.networkStatus, + queryResult.error, + queryResult.data + ] + ); + + useEffect(() => { + return () => queryData.cleanup(); + }, []); + + return result; +} diff --git a/src/react/hooks/utils/useDeepMemo.ts b/src/react/hooks/utils/useDeepMemo.ts new file mode 100644 index 00000000000..32b5517af3a --- /dev/null +++ b/src/react/hooks/utils/useDeepMemo.ts @@ -0,0 +1,22 @@ +import { useRef } from 'react'; +import { equal as isEqual } from '@wry/equality'; + +/** + * Memoize a result using deep equality. This hook has two advantages over + * React.useMemo: it uses deep equality to compare memo keys, and it guarantees + * that the memo function will only be called if the keys are unequal. + * React.useMemo cannot be relied on to do this, since it is only a performance + * optimization (see https://reactjs.org/docs/hooks-reference.html#usememo). + */ +export function useDeepMemo( + memoFn: () => TValue, + key: TKey +): TValue { + const ref = useRef<{ key: TKey; value: TValue }>(); + + if (!ref.current || !isEqual(key, ref.current.key)) { + ref.current = { key, value: memoFn() }; + } + + return ref.current.value; +} diff --git a/src/react/index.ts b/src/react/index.ts new file mode 100644 index 00000000000..501468295e3 --- /dev/null +++ b/src/react/index.ts @@ -0,0 +1,13 @@ +export { ApolloProvider } from './context/ApolloProvider'; +export { ApolloConsumer } from './context/ApolloConsumer'; +export { getApolloContext, resetApolloContext } from './context/ApolloContext'; + +export { useQuery } from './hooks/useQuery'; +export { useLazyQuery } from './hooks/useLazyQuery'; +export { useMutation } from './hooks/useMutation'; +export { useSubscription } from './hooks/useSubscription'; +export { useApolloClient } from './hooks/useApolloClient'; + +export { RenderPromises } from './ssr/RenderPromises'; + +export * from './types/types'; diff --git a/src/react/parser/__tests__/parser.test.ts b/src/react/parser/__tests__/parser.test.ts new file mode 100644 index 00000000000..8b50adf438d --- /dev/null +++ b/src/react/parser/__tests__/parser.test.ts @@ -0,0 +1,227 @@ +import gql from 'graphql-tag'; + +import { parser, DocumentType } from '../parser'; + +type OperationDefinition = any; + +describe('parser', () => { + it('should error if both a query and a mutation is present', () => { + const query = gql` + query { + user { + name + } + } + + mutation($t: String) { + addT(t: $t) { + user { + name + } + } + } + `; + + expect(parser.bind(null, query)).toThrowError(/react-apollo only supports/); + }); + + it('should error if multiple operations are present', () => { + const query = gql` + query One { + user { + name + } + } + + query Two { + user { + name + } + } + `; + + expect(parser.bind(null, query)).toThrowError(/react-apollo only supports/); + }); + + it('should error if not a DocumentNode', () => { + const query = ` + query One { user { name } } + `; + expect(parser.bind(null, query as any)).toThrowError( + /not a valid GraphQL DocumentNode/ + ); + }); + + it('should return the name of the operation', () => { + const query = gql` + query One { + user { + name + } + } + `; + expect(parser(query).name).toBe('One'); + + const mutation = gql` + mutation One { + user { + name + } + } + `; + expect(parser(mutation).name).toBe('One'); + + const subscription = gql` + subscription One { + user { + name + } + } + `; + expect(parser(subscription).name).toBe('One'); + }); + + it('should return data as the name of the operation if not named', () => { + const query = gql` + query { + user { + name + } + } + `; + expect(parser(query).name).toBe('data'); + + const unnamedQuery = gql` + { + user { + name + } + } + `; + expect(parser(unnamedQuery).name).toBe('data'); + + const mutation = gql` + mutation { + user { + name + } + } + `; + expect(parser(mutation).name).toBe('data'); + + const subscription = gql` + subscription { + user { + name + } + } + `; + expect(parser(subscription).name).toBe('data'); + }); + + it('should return the type of operation', () => { + const query = gql` + query One { + user { + name + } + } + `; + expect(parser(query).type).toBe(DocumentType.Query); + + const unnamedQuery = gql` + { + user { + name + } + } + `; + expect(parser(unnamedQuery).type).toBe(DocumentType.Query); + + const mutation = gql` + mutation One { + user { + name + } + } + `; + expect(parser(mutation).type).toBe(DocumentType.Mutation); + + const subscription = gql` + subscription One { + user { + name + } + } + `; + expect(parser(subscription).type).toBe(DocumentType.Subscription); + }); + + it('should return the variable definitions of the operation', () => { + const query = gql` + query One($t: String!) { + user(t: $t) { + name + } + } + `; + let definition = query.definitions[0] as OperationDefinition; + expect(parser(query).variables).toEqual(definition.variableDefinitions); + + const mutation = gql` + mutation One($t: String!) { + user(t: $t) { + name + } + } + `; + definition = mutation.definitions[0] as OperationDefinition; + expect(parser(mutation).variables).toEqual(definition.variableDefinitions); + + const subscription = gql` + subscription One($t: String!) { + user(t: $t) { + name + } + } + `; + definition = subscription.definitions[0] as OperationDefinition; + expect(parser(subscription).variables).toEqual( + definition.variableDefinitions + ); + }); + + it('should not error if the operation has no variables', () => { + const query = gql` + query { + user(t: $t) { + name + } + } + `; + let definition = query.definitions[0] as OperationDefinition; + expect(parser(query).variables).toEqual(definition.variableDefinitions); + + const mutation = gql` + mutation { + user(t: $t) { + name + } + } + `; + definition = mutation.definitions[0] as OperationDefinition; + expect(parser(mutation).variables).toEqual(definition.variableDefinitions); + + const subscription = gql` + subscription { + user(t: $t) { + name + } + } + `; + definition = subscription.definitions[0] as OperationDefinition; + expect(parser(subscription).variables).toEqual( + definition.variableDefinitions + ); + }); +}); diff --git a/src/react/parser/parser.ts b/src/react/parser/parser.ts new file mode 100644 index 00000000000..ad0cbf69371 --- /dev/null +++ b/src/react/parser/parser.ts @@ -0,0 +1,115 @@ +import { + DocumentNode, + DefinitionNode, + VariableDefinitionNode, + OperationDefinitionNode +} from 'graphql'; +import { invariant } from 'ts-invariant'; + +export enum DocumentType { + Query, + Mutation, + Subscription +} + +export interface IDocumentDefinition { + type: DocumentType; + name: string; + variables: ReadonlyArray; +} + +const cache = new Map(); + +export function operationName(type: DocumentType) { + let name; + switch (type) { + case DocumentType.Query: + name = 'Query'; + break; + case DocumentType.Mutation: + name = 'Mutation'; + break; + case DocumentType.Subscription: + name = 'Subscription'; + break; + } + return name; +} + +// This parser is mostly used to saftey check incoming documents. +export function parser(document: DocumentNode): IDocumentDefinition { + const cached = cache.get(document); + if (cached) return cached; + + let variables, type, name; + + invariant( + !!document && !!document.kind, + `Argument of ${document} passed to parser was not a valid GraphQL ` + + `DocumentNode. You may need to use 'graphql-tag' or another method ` + + `to convert your operation into a document` + ); + + const fragments = document.definitions.filter( + (x: DefinitionNode) => x.kind === 'FragmentDefinition' + ); + + const queries = document.definitions.filter( + (x: DefinitionNode) => + x.kind === 'OperationDefinition' && x.operation === 'query' + ); + + const mutations = document.definitions.filter( + (x: DefinitionNode) => + x.kind === 'OperationDefinition' && x.operation === 'mutation' + ); + + const subscriptions = document.definitions.filter( + (x: DefinitionNode) => + x.kind === 'OperationDefinition' && x.operation === 'subscription' + ); + + invariant( + !fragments.length || + (queries.length || mutations.length || subscriptions.length), + `Passing only a fragment to 'graphql' is not yet supported. ` + + `You must include a query, subscription or mutation as well` + ); + + invariant( + queries.length + mutations.length + subscriptions.length <= 1, + `react-apollo only supports a query, subscription, or a mutation per HOC. ` + + `${document} had ${queries.length} queries, ${subscriptions.length} ` + + `subscriptions and ${mutations.length} mutations. ` + + `You can use 'compose' to join multiple operation types to a component` + ); + + type = queries.length ? DocumentType.Query : DocumentType.Mutation; + if (!queries.length && !mutations.length) type = DocumentType.Subscription; + + const definitions = queries.length + ? queries + : mutations.length + ? mutations + : subscriptions; + + invariant( + definitions.length === 1, + `react-apollo only supports one definition per HOC. ${document} had ` + + `${definitions.length} definitions. ` + + `You can use 'compose' to join multiple operation types to a component` + ); + + const definition = definitions[0] as OperationDefinitionNode; + variables = definition.variableDefinitions || []; + + if (definition.name && definition.name.kind === 'Name') { + name = definition.name.value; + } else { + name = 'data'; // fallback to using data if no name + } + + const payload = { name, type, variables }; + cache.set(document, payload); + return payload; +} diff --git a/src/react/ssr/RenderPromises.ts b/src/react/ssr/RenderPromises.ts new file mode 100644 index 00000000000..ae2849e6a69 --- /dev/null +++ b/src/react/ssr/RenderPromises.ts @@ -0,0 +1,98 @@ +import { DocumentNode } from 'graphql'; + +import { ObservableQuery } from '../../core/ObservableQuery'; +import { QueryOptions } from '../types/types'; +import { QueryData } from '../data/QueryData'; + +type QueryInfo = { + seen: boolean; + observable: ObservableQuery | null; +}; + +function makeDefaultQueryInfo(): QueryInfo { + return { + seen: false, + observable: null + }; +} + +export class RenderPromises { + // Map from Query component instances to pending fetchData promises. + private queryPromises = new Map, Promise>(); + + // Two-layered map from (query document, stringified variables) to QueryInfo + // objects. These QueryInfo objects are intended to survive through the whole + // getMarkupFromTree process, whereas specific Query instances do not survive + // beyond a single call to renderToStaticMarkup. + private queryInfoTrie = new Map>(); + + // Registers the server side rendered observable. + public registerSSRObservable( + observable: ObservableQuery, + props: QueryOptions + ) { + this.lookupQueryInfo(props).observable = observable; + } + + // Get's the cached observable that matches the SSR Query instances query and variables. + public getSSRObservable( + props: QueryOptions + ) { + return this.lookupQueryInfo(props).observable; + } + + public addQueryPromise( + queryInstance: QueryData, + finish: () => React.ReactNode + ): React.ReactNode { + const info = this.lookupQueryInfo(queryInstance.getOptions()); + if (!info.seen) { + this.queryPromises.set( + queryInstance.getOptions(), + new Promise(resolve => { + resolve(queryInstance.fetchData()); + }) + ); + // Render null to abandon this subtree for this rendering, so that we + // can wait for the data to arrive. + return null; + } + return finish(); + } + + public hasPromises() { + return this.queryPromises.size > 0; + } + + public consumeAndAwaitPromises() { + const promises: Promise[] = []; + this.queryPromises.forEach((promise, queryInstance) => { + // Make sure we never try to call fetchData for this query document and + // these variables again. Since the queryInstance objects change with + // every rendering, deduplicating them by query and variables is the + // best we can do. If a different Query component happens to have the + // same query document and variables, it will be immediately rendered + // by calling finish() in addQueryPromise, which could result in the + // rendering of an unwanted loading state, but that's not nearly as bad + // as getting stuck in an infinite rendering loop because we kept calling + // queryInstance.fetchData for the same Query component indefinitely. + this.lookupQueryInfo(queryInstance).seen = true; + promises.push(promise); + }); + this.queryPromises.clear(); + return Promise.all(promises); + } + + private lookupQueryInfo( + props: QueryOptions + ): QueryInfo { + const { queryInfoTrie } = this; + const { query, variables } = props; + const varMap = queryInfoTrie.get(query) || new Map(); + if (!queryInfoTrie.has(query)) queryInfoTrie.set(query, varMap); + const variablesString = JSON.stringify(variables); + const info = varMap.get(variablesString) || makeDefaultQueryInfo(); + if (!varMap.has(variablesString)) varMap.set(variablesString, info); + return info; + } +} diff --git a/src/react/testing/__tests__/README.md b/src/react/testing/__tests__/README.md new file mode 100644 index 00000000000..12528595e22 --- /dev/null +++ b/src/react/testing/__tests__/README.md @@ -0,0 +1,3 @@ +**Note:** `MockedProvider` tests are currently run from the `@apollo/react-hoc` +project, as they have a hard dependency on the `graphql` HOC. They will be +refactored, and eventually stored here. diff --git a/src/react/testing/index.ts b/src/react/testing/index.ts new file mode 100644 index 00000000000..5c311957c9e --- /dev/null +++ b/src/react/testing/index.ts @@ -0,0 +1,12 @@ +export { MockedProvider } from './mocks/MockedProvider'; +export { MockLink, mockSingleLink } from './mocks/mockLink'; +export { + MockSubscriptionLink, + mockObservableLink +} from './mocks/mockSubscriptionLink'; + +export { createClient } from './utils/createClient'; +export { stripSymbols } from './utils/stripSymbols'; +export { wait } from './utils/wait'; + +export * from './mocks/types'; diff --git a/src/react/testing/mocks/MockedProvider.tsx b/src/react/testing/mocks/MockedProvider.tsx new file mode 100644 index 00000000000..9d585f03806 --- /dev/null +++ b/src/react/testing/mocks/MockedProvider.tsx @@ -0,0 +1,52 @@ +import React from 'react'; + +import ApolloClient from '../../../ApolloClient'; +import { InMemoryCache as Cache } from '../../../cache/inmemory/inMemoryCache'; +import { ApolloProvider } from '../../context/ApolloProvider'; +import { MockLink } from './mockLink'; +import { MockedProviderProps, MockedProviderState } from './types'; + +export class MockedProvider extends React.Component< + MockedProviderProps, + MockedProviderState +> { + public static defaultProps: MockedProviderProps = { + addTypename: true + }; + + constructor(props: MockedProviderProps) { + super(props); + + const { + mocks, + addTypename, + defaultOptions, + cache, + resolvers, + link + } = this.props; + const client = new ApolloClient({ + cache: cache || new Cache({ addTypename }), + defaultOptions, + link: link || new MockLink(mocks || [], addTypename), + resolvers + }); + + this.state = { client }; + } + + public render() { + const { children, childProps } = this.props; + return children ? ( + + {React.cloneElement(React.Children.only(children), { ...childProps })} + + ) : null; + } + + public componentWillUnmount() { + // Since this.state.client was created in the constructor, it's this + // MockedProvider's responsibility to terminate it. + this.state.client.stop(); + } +} diff --git a/src/react/testing/mocks/mockLink.ts b/src/react/testing/mocks/mockLink.ts new file mode 100644 index 00000000000..d18ee65b4ba --- /dev/null +++ b/src/react/testing/mocks/mockLink.ts @@ -0,0 +1,158 @@ +import { + Operation, + GraphQLRequest, + ApolloLink, + FetchResult, + Observable +} from 'apollo-link'; +import { print } from 'graphql/language/printer'; +import stringify from 'fast-json-stable-stringify'; + +import { + addTypenameToDocument, + removeClientSetsFromDocument, + removeConnectionDirectiveFromDocument, + cloneDeep, + isEqual +} from '../../../utilities'; +import { MockedResponse, ResultFunction } from './types'; + +function requestToKey(request: GraphQLRequest, addTypename: Boolean): string { + const queryString = + request.query && + print(addTypename ? addTypenameToDocument(request.query) : request.query); + const requestKey = { query: queryString }; + return JSON.stringify(requestKey); +} + +export class MockLink extends ApolloLink { + public addTypename: Boolean = true; + private mockedResponsesByKey: { [key: string]: MockedResponse[] } = {}; + + constructor( + mockedResponses: ReadonlyArray, + addTypename: Boolean = true + ) { + super(); + this.addTypename = addTypename; + if (mockedResponses) + mockedResponses.forEach(mockedResponse => { + this.addMockedResponse(mockedResponse); + }); + } + + public addMockedResponse(mockedResponse: MockedResponse) { + const normalizedMockedResponse = this.normalizeMockedResponse( + mockedResponse + ); + const key = requestToKey( + normalizedMockedResponse.request, + this.addTypename + ); + let mockedResponses = this.mockedResponsesByKey[key]; + if (!mockedResponses) { + mockedResponses = []; + this.mockedResponsesByKey[key] = mockedResponses; + } + mockedResponses.push(normalizedMockedResponse); + } + + public request(operation: Operation): Observable | null { + const key = requestToKey(operation, this.addTypename); + let responseIndex; + const response = (this.mockedResponsesByKey[key] || []).find( + (res, index) => { + const requestVariables = operation.variables || {}; + const mockedResponseVariables = res.request.variables || {}; + if ( + !isEqual( + stringify(requestVariables), + stringify(mockedResponseVariables) + ) + ) { + return false; + } + responseIndex = index; + return true; + } + ); + + if (!response || typeof responseIndex === 'undefined') { + throw new Error( + `No more mocked responses for the query: ${print( + operation.query + )}, variables: ${JSON.stringify(operation.variables)}` + ); + } + + this.mockedResponsesByKey[key].splice(responseIndex, 1); + + const { result, error, delay, newData } = response; + + if (newData) { + response.result = newData(); + this.mockedResponsesByKey[key].push(response); + } + + if (!result && !error) { + throw new Error( + `Mocked response should contain either result or error: ${key}` + ); + } + + return new Observable(observer => { + let timer = setTimeout( + () => { + if (error) { + observer.error(error); + } else { + if (result) { + observer.next( + typeof result === 'function' + ? (result as ResultFunction)() + : result + ); + } + observer.complete(); + } + }, + delay ? delay : 0 + ); + + return () => { + clearTimeout(timer); + }; + }); + } + + private normalizeMockedResponse( + mockedResponse: MockedResponse + ): MockedResponse { + const newMockedResponse = cloneDeep(mockedResponse); + newMockedResponse.request.query = removeConnectionDirectiveFromDocument( + newMockedResponse.request.query + ); + const query = removeClientSetsFromDocument(newMockedResponse.request.query); + if (query) { + newMockedResponse.request.query = query; + } + return newMockedResponse; + } +} + +// Pass in multiple mocked responses, so that you can test flows that end up +// making multiple queries to the server. +// NOTE: The last arg can optionally be an `addTypename` arg. +export function mockSingleLink(...mockedResponses: Array): ApolloLink { + // To pull off the potential typename. If this isn't a boolean, we'll just + // set it true later. + let maybeTypename = mockedResponses[mockedResponses.length - 1]; + let mocks = mockedResponses.slice(0, mockedResponses.length - 1); + + if (typeof maybeTypename !== 'boolean') { + mocks = mockedResponses; + maybeTypename = true; + } + + return new MockLink(mocks, maybeTypename); +} diff --git a/src/react/testing/mocks/mockSubscriptionLink.ts b/src/react/testing/mocks/mockSubscriptionLink.ts new file mode 100644 index 00000000000..0fc62afdbc4 --- /dev/null +++ b/src/react/testing/mocks/mockSubscriptionLink.ts @@ -0,0 +1,46 @@ +import { ApolloLink, FetchResult, Observable } from 'apollo-link'; + +import { MockedSubscriptionResult } from './types'; + +export class MockSubscriptionLink extends ApolloLink { + public unsubscribers: any[] = []; + public setups: any[] = []; + + private observer: any; + + constructor() { + super(); + } + + public request(_req: any) { + return new Observable(observer => { + this.setups.forEach(x => x()); + this.observer = observer; + return () => { + this.unsubscribers.forEach(x => x()); + }; + }); + } + + public simulateResult(result: MockedSubscriptionResult, complete = false) { + setTimeout(() => { + const { observer } = this; + if (!observer) throw new Error('subscription torn down'); + if (complete && observer.complete) observer.complete(); + if (result.result && observer.next) observer.next(result.result); + if (result.error && observer.error) observer.error(result.error); + }, result.delay || 0); + } + + public onSetup(listener: any): void { + this.setups = this.setups.concat([listener]); + } + + public onUnsubscribe(listener: any): void { + this.unsubscribers = this.unsubscribers.concat([listener]); + } +} + +export function mockObservableLink(): MockSubscriptionLink { + return new MockSubscriptionLink(); +} diff --git a/src/react/testing/mocks/types.ts b/src/react/testing/mocks/types.ts new file mode 100644 index 00000000000..4b95df0acb5 --- /dev/null +++ b/src/react/testing/mocks/types.ts @@ -0,0 +1,37 @@ +import { ApolloLink, GraphQLRequest, FetchResult } from 'apollo-link'; + +import ApolloClient, { DefaultOptions } from '../../../ApolloClient'; +import { Resolvers } from '../../../core/types'; +import { ApolloCache } from '../../../cache/core/cache'; + + +export type ResultFunction = () => T; + +export interface MockedResponse { + request: GraphQLRequest; + result?: FetchResult | ResultFunction; + error?: Error; + delay?: number; + newData?: ResultFunction; +} + +export interface MockedSubscriptionResult { + result?: FetchResult; + error?: Error; + delay?: number; +} + +export interface MockedProviderProps { + mocks?: ReadonlyArray; + addTypename?: boolean; + defaultOptions?: DefaultOptions; + cache?: ApolloCache; + resolvers?: Resolvers; + childProps?: object; + children?: React.ReactElement; + link?: ApolloLink; +} + +export interface MockedProviderState { + client: ApolloClient; +} diff --git a/src/react/testing/utils/createClient.ts b/src/react/testing/utils/createClient.ts new file mode 100644 index 00000000000..045e2b9f35b --- /dev/null +++ b/src/react/testing/utils/createClient.ts @@ -0,0 +1,20 @@ +import { DocumentNode } from 'graphql'; + +import ApolloClient from '../../../ApolloClient'; +import { InMemoryCache } from '../../../cache/inmemory/inMemoryCache'; +import { NormalizedCacheObject } from '../../../cache/inmemory/types'; +import { mockSingleLink } from '../mocks/mockLink'; + +export function createClient( + data: TData, + query: DocumentNode, + variables = {}, +): ApolloClient { + return new ApolloClient({ + link: mockSingleLink({ + request: { query, variables }, + result: { data }, + }), + cache: new InMemoryCache({ addTypename: false }), + }); +} diff --git a/src/react/testing/utils/stripSymbols.ts b/src/react/testing/utils/stripSymbols.ts new file mode 100644 index 00000000000..1d2d6c0a27f --- /dev/null +++ b/src/react/testing/utils/stripSymbols.ts @@ -0,0 +1,7 @@ +/** + * Apollo-client adds Symbols to the data in the store. In order to make + * assertions in our tests easier we strip these Symbols from the data. + */ +export function stripSymbols(data: T): T { + return JSON.parse(JSON.stringify(data)); +} diff --git a/src/react/testing/utils/wait.ts b/src/react/testing/utils/wait.ts new file mode 100644 index 00000000000..e0af6fc61b3 --- /dev/null +++ b/src/react/testing/utils/wait.ts @@ -0,0 +1,3 @@ +export function wait(ms: number): Promise { + return new Promise(resolve => setTimeout(resolve, ms)); +} diff --git a/src/react/types/types.ts b/src/react/types/types.ts new file mode 100644 index 00000000000..c83a609f2ca --- /dev/null +++ b/src/react/types/types.ts @@ -0,0 +1,263 @@ +import { ReactNode } from 'react'; +import { DocumentNode, GraphQLError } from 'graphql'; +import { Observable } from 'apollo-link'; + +import ApolloClient from '../../ApolloClient'; +import { + ApolloQueryResult, + PureQueryOptions, + OperationVariables +} from '../../core/types'; +import { ApolloError } from '../../errors/ApolloError'; +import { + FetchPolicy, + WatchQueryFetchPolicy, + ErrorPolicy, + FetchMoreQueryOptions, + MutationUpdaterFn, +} from '../../core/watchQueryOptions'; +import { FetchMoreOptions, ObservableQuery } from '../../core/ObservableQuery'; +import { NetworkStatus } from '../../core/networkStatus'; + +/* Common types */ + +export type Context = Record; + +export interface ExecutionResult> { + data?: T; + extensions?: Record; + errors?: GraphQLError[]; +} + +export type CommonOptions = TOptions & { + client?: ApolloClient; +}; + +/* Query types */ + +export interface BaseQueryOptions { + ssr?: boolean; + variables?: TVariables; + fetchPolicy?: WatchQueryFetchPolicy; + errorPolicy?: ErrorPolicy; + pollInterval?: number; + client?: ApolloClient; + notifyOnNetworkStatusChange?: boolean; + context?: Context; + partialRefetch?: boolean; + returnPartialData?: boolean; +} + +export interface QueryFunctionOptions< + TData = any, + TVariables = OperationVariables +> extends BaseQueryOptions { + displayName?: string; + skip?: boolean; + onCompleted?: (data: TData) => void; + onError?: (error: ApolloError) => void; +} + +export type ObservableQueryFields = Pick< + ObservableQuery, + | 'startPolling' + | 'stopPolling' + | 'subscribeToMore' + | 'updateQuery' + | 'refetch' + | 'variables' +> & { + fetchMore: (( + fetchMoreOptions: FetchMoreQueryOptions & + FetchMoreOptions + ) => Promise>) & + (( + fetchMoreOptions: { query?: DocumentNode } & FetchMoreQueryOptions< + TVariables2, + K + > & + FetchMoreOptions + ) => Promise>); +}; + +export interface QueryResult + extends ObservableQueryFields { + client: ApolloClient; + data: TData | undefined; + error?: ApolloError; + loading: boolean; + networkStatus: NetworkStatus; + called: boolean; +} + +export interface QueryOptions + extends QueryFunctionOptions { + children?: (result: QueryResult) => ReactNode; + query: DocumentNode; +} + +export interface QueryHookOptions + extends QueryFunctionOptions { + query?: DocumentNode; +} + +export interface LazyQueryHookOptions< + TData = any, + TVariables = OperationVariables +> extends Omit, 'skip'> { + query?: DocumentNode; +} + +export interface QueryPreviousData { + client?: ApolloClient; + query?: DocumentNode; + observableQueryOptions?: {}; + result?: ApolloQueryResult | null; + loading?: boolean; + options?: QueryOptions; + error?: ApolloError; +} + +export interface QueryCurrentObservable { + query?: ObservableQuery | null; + subscription?: ZenObservable.Subscription; +} + +export interface QueryLazyOptions { + variables?: TVariables; + context?: Context; +} + +export type QueryTuple = [ + (options?: QueryLazyOptions) => void, + QueryResult +]; + +/* Mutation types */ + +export type RefetchQueriesFunction = ( + ...args: any[] +) => Array; + +export interface BaseMutationOptions< + TData = any, + TVariables = OperationVariables +> { + variables?: TVariables; + optimisticResponse?: TData | ((vars: TVariables) => TData); + refetchQueries?: Array | RefetchQueriesFunction; + awaitRefetchQueries?: boolean; + errorPolicy?: ErrorPolicy; + update?: MutationUpdaterFn; + client?: ApolloClient; + notifyOnNetworkStatusChange?: boolean; + context?: Context; + onCompleted?: (data: TData) => void; + onError?: (error: ApolloError) => void; + fetchPolicy?: WatchQueryFetchPolicy; + ignoreResults?: boolean; +} + +export interface MutationFunctionOptions< + TData = any, + TVariables = OperationVariables +> { + variables?: TVariables; + optimisticResponse?: TData | ((vars: TVariables | {}) => TData); + refetchQueries?: Array | RefetchQueriesFunction; + awaitRefetchQueries?: boolean; + update?: MutationUpdaterFn; + context?: Context; + fetchPolicy?: WatchQueryFetchPolicy; +} + +export interface MutationResult { + data?: TData; + error?: ApolloError; + loading: boolean; + called: boolean; + client?: ApolloClient; +} + +export declare type MutationFetchResult< + TData = Record, + C = Record, + E = Record +> = ExecutionResult & { + extensions?: E; + context?: C; +}; + +export declare type MutationFunction< + TData = any, + TVariables = OperationVariables +> = ( + options?: MutationFunctionOptions +) => Promise>; + +export interface MutationHookOptions< + TData = any, + TVariables = OperationVariables +> extends BaseMutationOptions { + mutation?: DocumentNode; +} + +export interface MutationOptions + extends BaseMutationOptions { + mutation: DocumentNode; +} + +export type MutationTuple = [ + ( + options?: MutationFunctionOptions + ) => Promise>, + MutationResult +]; + +/* Subscription types */ + +export interface OnSubscriptionDataOptions { + client: ApolloClient; + subscriptionData: SubscriptionResult; +} + +export interface BaseSubscriptionOptions< + TData = any, + TVariables = OperationVariables +> { + variables?: TVariables; + fetchPolicy?: FetchPolicy; + shouldResubscribe?: + | boolean + | ((options: BaseSubscriptionOptions) => boolean); + client?: ApolloClient; + skip?: boolean; + onSubscriptionData?: (options: OnSubscriptionDataOptions) => any; + onSubscriptionComplete?: () => void; +} + +export interface SubscriptionResult { + loading: boolean; + data?: TData; + error?: ApolloError; +} + +export interface SubscriptionHookOptions< + TData = any, + TVariables = OperationVariables +> extends BaseSubscriptionOptions { + subscription?: DocumentNode; +} + +export interface SubscriptionOptions< + TData = any, + TVariables = OperationVariables +> extends BaseSubscriptionOptions { + subscription: DocumentNode; + children?: null | ((result: SubscriptionResult) => JSX.Element | null); +} + +export interface SubscriptionCurrentObservable { + query?: Observable; + subscription?: ZenObservable.Subscription; +} From d6ef8030f38cbb3d3980ddd2fdae99fcb912e7d4 Mon Sep 17 00:00:00 2001 From: hwillson Date: Mon, 16 Sep 2019 18:37:37 -0400 Subject: [PATCH 06/28] Re-export React functionality --- src/index.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/index.ts b/src/index.ts index 6dac574944d..12457b3addc 100644 --- a/src/index.ts +++ b/src/index.ts @@ -39,3 +39,5 @@ export default ApolloClient; export * from './cache/core'; export * from './cache/inmemory'; + +export * from './react'; From 0f962e0c2c6ff75ce6ab08fa53aff8cb3b52a8d7 Mon Sep 17 00:00:00 2001 From: hwillson Date: Mon, 16 Sep 2019 18:57:25 -0400 Subject: [PATCH 07/28] Typing changes to clear implicit any type errors The TS rules are a bit more strict in this project (compared to React Apollo). --- src/react/data/QueryData.ts | 2 +- src/react/data/SubscriptionData.ts | 2 +- src/react/hooks/utils/useBaseQuery.ts | 6 +++++- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/react/data/QueryData.ts b/src/react/data/QueryData.ts index 0064692d17a..3f7b46a98c1 100644 --- a/src/react/data/QueryData.ts +++ b/src/react/data/QueryData.ts @@ -158,7 +158,7 @@ export class QueryData extends OperationData { networkStatus: NetworkStatus.loading, called: true, data: undefined - }; + } as QueryResult; // SSR is disabled, so just return the loading event and leave it in that state. if (ssrDisabled) { diff --git a/src/react/data/SubscriptionData.ts b/src/react/data/SubscriptionData.ts index 0863f633803..c16a5de37fe 100644 --- a/src/react/data/SubscriptionData.ts +++ b/src/react/data/SubscriptionData.ts @@ -103,7 +103,7 @@ export class SubscriptionData< loading: true, error: undefined, data: undefined - }; + } as SubscriptionResult; } private updateResult(result: SubscriptionResult) { diff --git a/src/react/hooks/utils/useBaseQuery.ts b/src/react/hooks/utils/useBaseQuery.ts index 38e8a458d3a..e06b6ebbd60 100644 --- a/src/react/hooks/utils/useBaseQuery.ts +++ b/src/react/hooks/utils/useBaseQuery.ts @@ -39,7 +39,11 @@ export function useBaseQuery( // stable identity, so we'll exclude them from the memoization key to // prevent `afterExecute` from being triggered un-necessarily. const memo = { - options: { ...updatedOptions, onError: undefined, onCompleted: undefined }, + options: { + ...updatedOptions, + onError: undefined, + onCompleted: undefined + } as QueryHookOptions, context, tick }; From 2e2cb0777594991e671909cc0d2859be6aa4a515 Mon Sep 17 00:00:00 2001 From: hwillson Date: Mon, 16 Sep 2019 19:03:44 -0400 Subject: [PATCH 08/28] Add new global dep exemptions for rollup bundling New exemptions required by React Apollo --- config/rollup.config.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/config/rollup.config.js b/config/rollup.config.js index a47cae931f4..9c79e3c9ee4 100644 --- a/config/rollup.config.js +++ b/config/rollup.config.js @@ -27,6 +27,8 @@ const defaultGlobals = { 'graphql/language/visitor': 'visitor', 'fast-json-stable-stringify': 'stringify', '@wry/equality': 'wryEquality', + graphql: 'graphql', + react: 'React' }; function rollup({ From 2418a463fa0efd7bd492350245e39e3b7a563468 Mon Sep 17 00:00:00 2001 From: hwillson Date: Mon, 16 Sep 2019 19:04:28 -0400 Subject: [PATCH 09/28] Bundlesize increase to accommodate React code This is just a preliminary bump; this number will go down as we start refactoring. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 815f6d7c91e..bc5c98945de 100644 --- a/package.json +++ b/package.json @@ -45,7 +45,7 @@ { "name": "apollo-client", "path": "./lib/apollo-client.cjs.min.js", - "maxSize": "16.9 kB" + "maxSize": "21 kB" } ], "peerDependencies": { From 5a102b2bf312479cf1e1270d5d0cd14d514e7980 Mon Sep 17 00:00:00 2001 From: hwillson Date: Tue, 17 Sep 2019 07:22:55 -0400 Subject: [PATCH 10/28] Update all React tests to use async instead of done callbacks This, along with using `@testing-library/react`'s `wait` function, gets rid of all React "you must use act()" warnings. --- .../hooks/__tests__/useMutation.test.tsx | 33 +++++++--- src/react/hooks/__tests__/useQuery.test.tsx | 65 ++++++++++--------- .../hooks/__tests__/useSubscription.test.tsx | 41 ++++++------ 3 files changed, 81 insertions(+), 58 deletions(-) diff --git a/src/react/hooks/__tests__/useMutation.test.tsx b/src/react/hooks/__tests__/useMutation.test.tsx index cc48ff96d97..21b371971bf 100644 --- a/src/react/hooks/__tests__/useMutation.test.tsx +++ b/src/react/hooks/__tests__/useMutation.test.tsx @@ -38,7 +38,7 @@ describe('useMutation Hook', () => { afterEach(cleanup); describe('General use', () => { - it('should handle a simple mutation properly', done => { + it('should handle a simple mutation properly', async () => { const variables = { description: 'Get milk!' }; @@ -71,7 +71,6 @@ describe('useMutation Hook', () => { case 2: expect(loading).toBeFalsy(); expect(data).toEqual(CREATE_TODO_RESULT); - done(); break; default: } @@ -84,9 +83,13 @@ describe('useMutation Hook', () => { ); + + await wait(() => { + expect(renderCount).toBe(3); + }); }); - it('should be able to call mutations as an effect', done => { + it('should be able to call mutations as an effect', async () => { const variables = { description: 'Get milk!' }; @@ -123,7 +126,6 @@ describe('useMutation Hook', () => { case 2: expect(loading).toBeFalsy(); expect(data).toEqual(CREATE_TODO_RESULT); - done(); break; default: } @@ -141,9 +143,13 @@ describe('useMutation Hook', () => { ); + + await wait(() => { + expect(renderCount).toBe(3); + }); }); - it('should ensure the mutation callback function has a stable identity', done => { + it('should ensure the mutation callback function has a stable identity', async () => { const variables = { description: 'Get milk!' }; @@ -181,7 +187,6 @@ describe('useMutation Hook', () => { case 2: expect(loading).toBeFalsy(); expect(data).toEqual(CREATE_TODO_RESULT); - done(); break; default: } @@ -194,9 +199,13 @@ describe('useMutation Hook', () => { ); + + await wait(() => { + expect(renderCount).toBe(3); + }); }); - it('should resolve mutate function promise with mutation results', done => { + it('should resolve mutate function promise with mutation results', async () => { const variables = { description: 'Get milk!' }; @@ -222,7 +231,6 @@ describe('useMutation Hook', () => { expect(data!.createTodo.description).toEqual( CREATE_TODO_RESULT.createTodo.description ); - done(); } useEffect(() => { @@ -237,6 +245,8 @@ describe('useMutation Hook', () => { ); + + await wait(); }); it('should return the current client instance in the result object', async () => { @@ -258,7 +268,7 @@ describe('useMutation Hook', () => { }); describe('Optimistic response', () => { - it('should support optimistic response handling', done => { + it('should support optimistic response handling', async () => { const optimisticResponse = { __typename: 'Mutation', createTodo: { @@ -316,7 +326,6 @@ describe('useMutation Hook', () => { case 2: expect(loading).toBeFalsy(); expect(data).toEqual(CREATE_TODO_RESULT); - done(); break; default: } @@ -329,6 +338,10 @@ describe('useMutation Hook', () => { ); + + await wait(() => { + expect(renderCount).toBe(3); + }); }); }); }); diff --git a/src/react/hooks/__tests__/useQuery.test.tsx b/src/react/hooks/__tests__/useQuery.test.tsx index 56a901ff594..c2f16e6ed6f 100644 --- a/src/react/hooks/__tests__/useQuery.test.tsx +++ b/src/react/hooks/__tests__/useQuery.test.tsx @@ -44,12 +44,11 @@ describe('useQuery Hook', () => { afterEach(cleanup); describe('General use', () => { - it('should handle a simple query properly', done => { + it('should handle a simple query properly', async () => { const Component = () => { const { data, loading } = useQuery(CAR_QUERY); if (!loading) { expect(data).toEqual(CAR_RESULT_DATA); - done(); } return null; }; @@ -59,16 +58,17 @@ describe('useQuery Hook', () => { ); + + await wait(); }); - it('should keep data as undefined until data is actually returned', done => { + it('should keep data as undefined until data is actually returned', async () => { const Component = () => { const { data, loading } = useQuery(CAR_QUERY); if (loading) { expect(data).toBeUndefined(); } else { expect(data).toEqual(CAR_RESULT_DATA); - done(); } return null; }; @@ -78,6 +78,8 @@ describe('useQuery Hook', () => { ); + + await wait(); }); it('should ensure ObservableQuery fields have a stable identity', async () => { @@ -126,7 +128,7 @@ describe('useQuery Hook', () => { }); describe('Polling', () => { - it('should support polling', done => { + it('should support polling', async () => { let renderCount = 0; const Component = () => { let { data, loading, stopPolling } = useQuery(CAR_QUERY, { @@ -144,13 +146,9 @@ describe('useQuery Hook', () => { expect(loading).toBeFalsy(); expect(data).toEqual(CAR_RESULT_DATA); stopPolling(); - setTimeout(() => { - done(); - }, 10); break; case 3: - done.fail('Uh oh - we should have stopped polling!'); - break; + throw new Error('Uh oh - we should have stopped polling!'); default: // Do nothing } @@ -163,9 +161,13 @@ describe('useQuery Hook', () => { ); + + await wait(() => { + expect(renderCount).toBe(3); + }); }); - it('should stop polling when skip is true', done => { + it('should stop polling when skip is true', async () => { let renderCount = 0; const Component = () => { const [shouldSkip, setShouldSkip] = useState(false); @@ -190,13 +192,9 @@ describe('useQuery Hook', () => { case 3: expect(loading).toBeFalsy(); expect(data).toBeUndefined(); - setTimeout(() => { - done(); - }, 10); break; case 4: - done.fail('Uh oh - we should have stopped polling!'); - break; + throw new Error('Uh oh - we should have stopped polling!'); default: // Do nothing } @@ -209,9 +207,13 @@ describe('useQuery Hook', () => { ); + + await wait(() => { + expect(renderCount).toBe(4); + }); }); - it('should stop polling when the component is unmounted', done => { + it('should stop polling when the component is unmounted', async () => { const mockLink = new MockLink(CAR_MOCKS); const linkRequestSpy = jest.spyOn(mockLink, 'request'); let renderCount = 0; @@ -241,11 +243,6 @@ describe('useQuery Hook', () => { const Component = () => { const [queryMounted, setQueryMounted] = useState(true); const unmount = () => setTimeout(() => setQueryMounted(false), 0); - if (!queryMounted) - setTimeout(() => { - expect(linkRequestSpy).toHaveBeenCalledTimes(2); - done(); - }, 30); return <>{queryMounted && }; }; @@ -254,6 +251,10 @@ describe('useQuery Hook', () => { ); + + await wait(() => { + expect(linkRequestSpy).toHaveBeenCalledTimes(2); + }) }); it( @@ -314,7 +315,7 @@ describe('useQuery Hook', () => { }); describe('Error handling', () => { - it("should render GraphQLError's", done => { + it("should render GraphQLError's", async () => { const query = gql` query TestQuery { rates(currency: "USD") { @@ -337,7 +338,6 @@ describe('useQuery Hook', () => { if (!loading) { expect(error).toBeDefined(); expect(error!.message).toEqual('GraphQL error: forced error'); - done(); } return null; }; @@ -347,9 +347,11 @@ describe('useQuery Hook', () => { ); + + await wait(); }); - it('should only call onError callbacks once', done => { + it('should only call onError callbacks once', async () => { const query = gql` query SomeQuery { stuff { @@ -408,7 +410,6 @@ describe('useQuery Hook', () => { case 3: expect(loading).toBeFalsy(); expect(data).toEqual(resultData); - done(); break; default: // Do nothing } @@ -422,9 +423,13 @@ describe('useQuery Hook', () => { ); + + await wait(() => { + expect(renderCount).toBe(4); + }); }); - it('should persist errors on re-render if they are still valid', done => { + it('should persist errors on re-render if they are still valid', async () => { const query = gql` query SomeQuery { stuff { @@ -462,7 +467,6 @@ describe('useQuery Hook', () => { case 2: expect(error).toBeDefined(); expect(error!.message).toEqual('GraphQL error: forced error'); - done(); break; default: // Do nothing } @@ -476,6 +480,10 @@ describe('useQuery Hook', () => { ); + + await wait(() => { + expect(renderCount).toBe(3); + }); }); it( @@ -1091,7 +1099,6 @@ describe('useQuery Hook', () => { onCompleted(data) { onCompletedCalled = true; expect(data).toBeDefined(); - console.log(data); } }); if (!loading) { diff --git a/src/react/hooks/__tests__/useSubscription.test.tsx b/src/react/hooks/__tests__/useSubscription.test.tsx index e0ee56712a4..181b6fbd465 100644 --- a/src/react/hooks/__tests__/useSubscription.test.tsx +++ b/src/react/hooks/__tests__/useSubscription.test.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { render, cleanup } from '@testing-library/react'; +import { render, cleanup, wait } from '@testing-library/react'; import gql from 'graphql-tag'; import { MockSubscriptionLink } from '../../testing'; @@ -11,7 +11,7 @@ import { useSubscription } from '../useSubscription'; describe('useSubscription Hook', () => { afterEach(cleanup); - it('should handle a simple subscription properly', done => { + it('should handle a simple subscription properly', async () => { const subscription = gql` subscription { car { @@ -54,7 +54,6 @@ describe('useSubscription Hook', () => { case 4: expect(loading).toBe(false); expect(data).toEqual(results[3].result.data); - done(); break; default: } @@ -71,9 +70,13 @@ describe('useSubscription Hook', () => { ); + + await wait(() => { + expect(renderCount).toBe(5); + }); }); - it('should cleanup after the subscription component has been unmounted', done => { + it('should cleanup after the subscription component has been unmounted', async () => { const subscription = gql` subscription { car { @@ -124,10 +127,6 @@ describe('useSubscription Hook', () => { // stay at 1). unmount(); link.simulateResult(results[0]); - setTimeout(() => { - expect(onSubscriptionDataCount).toEqual(1); - done(); - }); }); break; default: @@ -141,9 +140,13 @@ describe('useSubscription Hook', () => { ).unmount; + + await wait(() => { + expect(onSubscriptionDataCount).toEqual(1); + }); }); - it('should never execute a subscription with the skip option', done => { + it('should never execute a subscription with the skip option', async () => { const subscription = gql` subscription { car { @@ -176,11 +179,6 @@ describe('useSubscription Hook', () => { expect(data).toBeUndefined(); setTimeout(() => { unmount(); - setTimeout(() => { - expect(onSubscriptionDataCount).toEqual(0); - expect(renderCount).toEqual(1); - done(); - }); }); break; default: @@ -194,9 +192,14 @@ describe('useSubscription Hook', () => { ).unmount; + + await wait(() => { + expect(onSubscriptionDataCount).toEqual(0); + expect(renderCount).toEqual(1); + }); }); - it('should create a subscription after skip has changed from true to a falsy value', done => { + it('should create a subscription after skip has changed from true to a falsy value', async () => { const subscription = gql` subscription { car { @@ -272,10 +275,6 @@ describe('useSubscription Hook', () => { expect(data).toEqual(results[1].result.data); setTimeout(() => { unmount(); - setTimeout(() => { - expect(renderCount).toEqual(7); - done(); - }); }); break; default: @@ -289,5 +288,9 @@ describe('useSubscription Hook', () => { ).unmount; + + await wait(() => { + expect(renderCount).toEqual(7); + }); }); }); From 6ebacef9de3819319bfb1f3e41d044e27bdf324a Mon Sep 17 00:00:00 2001 From: hwillson Date: Wed, 18 Sep 2019 20:47:25 -0400 Subject: [PATCH 11/28] Create a new testing bundle These new rollup config elements will create a separate `testing.js` CJS based bundle, that contains React testing utilities like `MockedProvider`. Applications can access this new bundle like: import { MockedProvider } from '@apollo/client/lib/testing'; --- config/rollup.config.js | 117 +++++++++++++++++++++++++++++----------- package-lock.json | 25 +++++++++ package.json | 7 +-- 3 files changed, 116 insertions(+), 33 deletions(-) diff --git a/config/rollup.config.js b/config/rollup.config.js index 9c79e3c9ee4..aefca088e97 100644 --- a/config/rollup.config.js +++ b/config/rollup.config.js @@ -8,6 +8,10 @@ import cjsModulesTransform from '@babel/plugin-transform-modules-commonjs'; import umdModulesTransform from '@babel/plugin-transform-modules-umd'; import invariantPlugin from 'rollup-plugin-invariant'; import { terser as minify } from 'rollup-plugin-terser'; +import replace from 'rollup-plugin-replace'; +import { main as mainBundle } from '../package.json'; + +const hasOwn = Object.prototype.hasOwnProperty; function onwarn(message) { const suppressed = ['UNRESOLVED_IMPORT', 'THIS_IS_UNDEFINED']; @@ -37,7 +41,6 @@ function rollup({ extraGlobals = {}, } = {}) { const projectDir = path.join(__dirname, '..'); - console.info(`Building project esm ${projectDir}`); const tsconfig = `${projectDir}/tsconfig.json`; const globals = { @@ -46,7 +49,7 @@ function rollup({ }; function external(id) { - return Object.prototype.hasOwnProperty.call(globals, id); + return hasOwn.call(globals, id); } function outputFile(format) { @@ -93,34 +96,36 @@ function rollup({ // Rollup replaces `this` with `undefined`, but this default behavior can // be overridden with the `context` option. context: 'this', - plugins: [{ - transform(source, id) { - const output = transformSync(source, { - inputSourceMap: JSON.parse(fs.readFileSync(id + '.map')), - sourceMaps: true, - plugins: [ - [toFormat === 'umd' ? umdModulesTransform : cjsModulesTransform, { - loose: true, - allowTopLevelThis: true, - }], - ], - }); - - // There doesn't seem to be any way to get Rollup to emit a source map - // that goes all the way back to the source file (rather than just to - // the bundle.esm.js intermediate file), so we pass sourcemap:false in - // the output options above, and manually write the CJS and UMD source - // maps here. - fs.writeFileSync( - outputFile(toFormat) + '.map', - JSON.stringify(output.map), - ); - - return { - code: output.code, - }; + plugins: [ + { + transform(source, id) { + const output = transformSync(source, { + inputSourceMap: JSON.parse(fs.readFileSync(id + '.map')), + sourceMaps: true, + plugins: [ + [toFormat === 'umd' ? umdModulesTransform : cjsModulesTransform, { + loose: true, + allowTopLevelThis: true, + }], + ], + }); + + // There doesn't seem to be any way to get Rollup to emit a source map + // that goes all the way back to the source file (rather than just to + // the bundle.esm.js intermediate file), so we pass sourcemap:false in + // the output options above, and manually write the CJS and UMD source + // maps here. + fs.writeFileSync( + outputFile(toFormat) + '.map', + JSON.stringify(output.map), + ); + + return { + code: output.code, + }; + } } - }], + ] } } @@ -145,9 +150,61 @@ function rollup({ }, }, }), + ] + } + ]; +} + +// Build a separate CJS only `testing.js` bundle, that includes React +// testing utilities like `MockedProvider` (testing utilities are kept out of +// the main `apollo-client` bundle). This bundle can be accessed directly +// like: +// +// import { MockedProvider } from '@apollo/client/lib/testing'; +// +// Note: The `ApolloProvider` reference is marked as being global so it can +// then be replaced with a hard coded path to the `apollo-client.cjs.js` +// bundle. This is done to ensure that when using this bundle `MockedProvider` +// always uses the same `ApolloProvider` instance as the rest of the +// application under test. This means they'll share the exact same React +// context, and be able to share the same Apollo Client instance stored in that +// context. +function rollupTesting() { + const globals = { + ...defaultGlobals, + '../../context/ApolloProvider': 'ApolloProvider' + }; + + const output = { + file: './lib/testing.js', + format: 'cjs', + }; + + return [ + { + input: './src/react/testing/index.ts', + external: (id) => hasOwn.call(globals, id), + output, + plugins: [ + nodeResolve({ + extensions: ['.ts', '.tsx'], + }), + typescriptPlugin({ + typescript, + tsconfig: `${path.join(__dirname, '..')}/tsconfig.json` + }), + ], + }, + { + input: output.file, + output, + plugins: [ + replace({ + 'context/ApolloProvider': mainBundle + }), ], }, ]; } -export default rollup(); +export default rollup().concat(rollupTesting()); diff --git a/package-lock.json b/package-lock.json index eeeba0a4b08..a7f4a3b1e28 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6174,6 +6174,15 @@ "yallist": "^2.1.2" } }, + "magic-string": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.3.tgz", + "integrity": "sha512-6QK0OpF/phMz0Q2AxILkX2mFhi7m+WMwTRg0LQKq/WBB0cDP4rYH3Wp4/d3OTXlrPLVJT/RFqj8tFeAR4nk8AA==", + "dev": true, + "requires": { + "sourcemap-codec": "^1.4.4" + } + }, "make-dir": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", @@ -7299,6 +7308,16 @@ "rollup-pluginutils": "^2.8.1" } }, + "rollup-plugin-replace": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/rollup-plugin-replace/-/rollup-plugin-replace-2.2.0.tgz", + "integrity": "sha512-/5bxtUPkDHyBJAKketb4NfaeZjL5yLZdeUihSfbF2PQMz+rSTEb8ARKoOl3UBT4m7/X+QOXJo3sLTcq+yMMYTA==", + "dev": true, + "requires": { + "magic-string": "^0.25.2", + "rollup-pluginutils": "^2.6.0" + } + }, "rollup-plugin-sourcemaps": { "version": "0.4.2", "resolved": "https://registry.npmjs.org/rollup-plugin-sourcemaps/-/rollup-plugin-sourcemaps-0.4.2.tgz", @@ -8068,6 +8087,12 @@ "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", "dev": true }, + "sourcemap-codec": { + "version": "1.4.6", + "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.6.tgz", + "integrity": "sha512-1ZooVLYFxC448piVLBbtOxFcXwnymH9oUF8nRd3CuYDVvkRBxRl6pB4Mtas5a4drtL+E8LDgFkQNcgIw6tc8Hg==", + "dev": true + }, "spdx-correct": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", diff --git a/package.json b/package.json index bc5c98945de..2d23309fc1e 100644 --- a/package.json +++ b/package.json @@ -12,10 +12,10 @@ ], "author": "opensource@apollographql.com", "license": "MIT", - "main": "./lib/apollo-client.cjs.js", - "module": "./lib/apollo-client.esm.js", + "main": "lib/apollo-client.cjs.js", + "module": "lib/apollo-client.esm.js", "sideEffects": [ - "./lib/cache/inmemory/fixPolyfills.js" + "lib/cache/inmemory/fixPolyfills.js" ], "repository": { "type": "git", @@ -94,6 +94,7 @@ "rollup-plugin-invariant": "0.5.6", "rollup-plugin-local-resolve": "1.0.7", "rollup-plugin-node-resolve": "5.2.0", + "rollup-plugin-replace": "^2.2.0", "rollup-plugin-sourcemaps": "0.4.2", "rollup-plugin-terser": "5.1.1", "rollup-plugin-typescript2": "0.24.0", From a241e09213ad224a07029fb508be5c10348e8a81 Mon Sep 17 00:00:00 2001 From: hwillson Date: Wed, 18 Sep 2019 21:06:51 -0400 Subject: [PATCH 12/28] Remove duplicated `MutationFetchResult` Apollo Client is already pulling a similar type from Apollo Link, so this updates the React code to do the same. --- src/react/types/types.ts | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/src/react/types/types.ts b/src/react/types/types.ts index c83a609f2ca..bb1a8242ebd 100644 --- a/src/react/types/types.ts +++ b/src/react/types/types.ts @@ -1,6 +1,6 @@ import { ReactNode } from 'react'; import { DocumentNode, GraphQLError } from 'graphql'; -import { Observable } from 'apollo-link'; +import { Observable, FetchResult } from 'apollo-link'; import ApolloClient from '../../ApolloClient'; import { @@ -179,21 +179,12 @@ export interface MutationResult { client?: ApolloClient; } -export declare type MutationFetchResult< - TData = Record, - C = Record, - E = Record -> = ExecutionResult & { - extensions?: E; - context?: C; -}; - export declare type MutationFunction< TData = any, TVariables = OperationVariables > = ( options?: MutationFunctionOptions -) => Promise>; +) => Promise>; export interface MutationHookOptions< TData = any, From dcf1602eda4fbc03fafd74119a98b213f3f3522e Mon Sep 17 00:00:00 2001 From: hwillson Date: Fri, 20 Sep 2019 20:07:55 -0400 Subject: [PATCH 13/28] Revert "Create a new testing bundle" This reverts commit 6ebacef9de3819319bfb1f3e41d044e27bdf324a. --- config/rollup.config.js | 117 +++++++++++----------------------------- package-lock.json | 25 --------- package.json | 7 ++- 3 files changed, 33 insertions(+), 116 deletions(-) diff --git a/config/rollup.config.js b/config/rollup.config.js index aefca088e97..9c79e3c9ee4 100644 --- a/config/rollup.config.js +++ b/config/rollup.config.js @@ -8,10 +8,6 @@ import cjsModulesTransform from '@babel/plugin-transform-modules-commonjs'; import umdModulesTransform from '@babel/plugin-transform-modules-umd'; import invariantPlugin from 'rollup-plugin-invariant'; import { terser as minify } from 'rollup-plugin-terser'; -import replace from 'rollup-plugin-replace'; -import { main as mainBundle } from '../package.json'; - -const hasOwn = Object.prototype.hasOwnProperty; function onwarn(message) { const suppressed = ['UNRESOLVED_IMPORT', 'THIS_IS_UNDEFINED']; @@ -41,6 +37,7 @@ function rollup({ extraGlobals = {}, } = {}) { const projectDir = path.join(__dirname, '..'); + console.info(`Building project esm ${projectDir}`); const tsconfig = `${projectDir}/tsconfig.json`; const globals = { @@ -49,7 +46,7 @@ function rollup({ }; function external(id) { - return hasOwn.call(globals, id); + return Object.prototype.hasOwnProperty.call(globals, id); } function outputFile(format) { @@ -96,36 +93,34 @@ function rollup({ // Rollup replaces `this` with `undefined`, but this default behavior can // be overridden with the `context` option. context: 'this', - plugins: [ - { - transform(source, id) { - const output = transformSync(source, { - inputSourceMap: JSON.parse(fs.readFileSync(id + '.map')), - sourceMaps: true, - plugins: [ - [toFormat === 'umd' ? umdModulesTransform : cjsModulesTransform, { - loose: true, - allowTopLevelThis: true, - }], - ], - }); - - // There doesn't seem to be any way to get Rollup to emit a source map - // that goes all the way back to the source file (rather than just to - // the bundle.esm.js intermediate file), so we pass sourcemap:false in - // the output options above, and manually write the CJS and UMD source - // maps here. - fs.writeFileSync( - outputFile(toFormat) + '.map', - JSON.stringify(output.map), - ); - - return { - code: output.code, - }; - } + plugins: [{ + transform(source, id) { + const output = transformSync(source, { + inputSourceMap: JSON.parse(fs.readFileSync(id + '.map')), + sourceMaps: true, + plugins: [ + [toFormat === 'umd' ? umdModulesTransform : cjsModulesTransform, { + loose: true, + allowTopLevelThis: true, + }], + ], + }); + + // There doesn't seem to be any way to get Rollup to emit a source map + // that goes all the way back to the source file (rather than just to + // the bundle.esm.js intermediate file), so we pass sourcemap:false in + // the output options above, and manually write the CJS and UMD source + // maps here. + fs.writeFileSync( + outputFile(toFormat) + '.map', + JSON.stringify(output.map), + ); + + return { + code: output.code, + }; } - ] + }], } } @@ -150,61 +145,9 @@ function rollup({ }, }, }), - ] - } - ]; -} - -// Build a separate CJS only `testing.js` bundle, that includes React -// testing utilities like `MockedProvider` (testing utilities are kept out of -// the main `apollo-client` bundle). This bundle can be accessed directly -// like: -// -// import { MockedProvider } from '@apollo/client/lib/testing'; -// -// Note: The `ApolloProvider` reference is marked as being global so it can -// then be replaced with a hard coded path to the `apollo-client.cjs.js` -// bundle. This is done to ensure that when using this bundle `MockedProvider` -// always uses the same `ApolloProvider` instance as the rest of the -// application under test. This means they'll share the exact same React -// context, and be able to share the same Apollo Client instance stored in that -// context. -function rollupTesting() { - const globals = { - ...defaultGlobals, - '../../context/ApolloProvider': 'ApolloProvider' - }; - - const output = { - file: './lib/testing.js', - format: 'cjs', - }; - - return [ - { - input: './src/react/testing/index.ts', - external: (id) => hasOwn.call(globals, id), - output, - plugins: [ - nodeResolve({ - extensions: ['.ts', '.tsx'], - }), - typescriptPlugin({ - typescript, - tsconfig: `${path.join(__dirname, '..')}/tsconfig.json` - }), - ], - }, - { - input: output.file, - output, - plugins: [ - replace({ - 'context/ApolloProvider': mainBundle - }), ], }, ]; } -export default rollup().concat(rollupTesting()); +export default rollup(); diff --git a/package-lock.json b/package-lock.json index a7f4a3b1e28..eeeba0a4b08 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6174,15 +6174,6 @@ "yallist": "^2.1.2" } }, - "magic-string": { - "version": "0.25.3", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.3.tgz", - "integrity": "sha512-6QK0OpF/phMz0Q2AxILkX2mFhi7m+WMwTRg0LQKq/WBB0cDP4rYH3Wp4/d3OTXlrPLVJT/RFqj8tFeAR4nk8AA==", - "dev": true, - "requires": { - "sourcemap-codec": "^1.4.4" - } - }, "make-dir": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", @@ -7308,16 +7299,6 @@ "rollup-pluginutils": "^2.8.1" } }, - "rollup-plugin-replace": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/rollup-plugin-replace/-/rollup-plugin-replace-2.2.0.tgz", - "integrity": "sha512-/5bxtUPkDHyBJAKketb4NfaeZjL5yLZdeUihSfbF2PQMz+rSTEb8ARKoOl3UBT4m7/X+QOXJo3sLTcq+yMMYTA==", - "dev": true, - "requires": { - "magic-string": "^0.25.2", - "rollup-pluginutils": "^2.6.0" - } - }, "rollup-plugin-sourcemaps": { "version": "0.4.2", "resolved": "https://registry.npmjs.org/rollup-plugin-sourcemaps/-/rollup-plugin-sourcemaps-0.4.2.tgz", @@ -8087,12 +8068,6 @@ "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", "dev": true }, - "sourcemap-codec": { - "version": "1.4.6", - "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.6.tgz", - "integrity": "sha512-1ZooVLYFxC448piVLBbtOxFcXwnymH9oUF8nRd3CuYDVvkRBxRl6pB4Mtas5a4drtL+E8LDgFkQNcgIw6tc8Hg==", - "dev": true - }, "spdx-correct": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", diff --git a/package.json b/package.json index 2d23309fc1e..bc5c98945de 100644 --- a/package.json +++ b/package.json @@ -12,10 +12,10 @@ ], "author": "opensource@apollographql.com", "license": "MIT", - "main": "lib/apollo-client.cjs.js", - "module": "lib/apollo-client.esm.js", + "main": "./lib/apollo-client.cjs.js", + "module": "./lib/apollo-client.esm.js", "sideEffects": [ - "lib/cache/inmemory/fixPolyfills.js" + "./lib/cache/inmemory/fixPolyfills.js" ], "repository": { "type": "git", @@ -94,7 +94,6 @@ "rollup-plugin-invariant": "0.5.6", "rollup-plugin-local-resolve": "1.0.7", "rollup-plugin-node-resolve": "5.2.0", - "rollup-plugin-replace": "^2.2.0", "rollup-plugin-sourcemaps": "0.4.2", "rollup-plugin-terser": "5.1.1", "rollup-plugin-typescript2": "0.24.0", From 0f96dff02aa811fbdd2c6daa4850593c986b3b17 Mon Sep 17 00:00:00 2001 From: hwillson Date: Fri, 20 Sep 2019 20:16:04 -0400 Subject: [PATCH 14/28] Stop building UMD bundles Apollo Client use doesn't really benefit from having a UMD bundle, since the project has external dependencies that aren't bundled into the UMD. --- config/rollup.config.js | 10 ++-------- package-lock.json | 10 ---------- package.json | 1 - 3 files changed, 2 insertions(+), 19 deletions(-) diff --git a/config/rollup.config.js b/config/rollup.config.js index 9c79e3c9ee4..8e12f46f5ce 100644 --- a/config/rollup.config.js +++ b/config/rollup.config.js @@ -5,7 +5,6 @@ import path from 'path'; import fs from 'fs'; import { transformSync } from '@babel/core'; import cjsModulesTransform from '@babel/plugin-transform-modules-commonjs'; -import umdModulesTransform from '@babel/plugin-transform-modules-umd'; import invariantPlugin from 'rollup-plugin-invariant'; import { terser as minify } from 'rollup-plugin-terser'; @@ -89,17 +88,13 @@ function rollup({ format: 'esm', sourcemap: false, }, - // The UMD bundle expects `this` to refer to the global object. By default - // Rollup replaces `this` with `undefined`, but this default behavior can - // be overridden with the `context` option. - context: 'this', plugins: [{ transform(source, id) { const output = transformSync(source, { inputSourceMap: JSON.parse(fs.readFileSync(id + '.map')), sourceMaps: true, plugins: [ - [toFormat === 'umd' ? umdModulesTransform : cjsModulesTransform, { + [cjsModulesTransform, { loose: true, allowTopLevelThis: true, }], @@ -109,7 +104,7 @@ function rollup({ // There doesn't seem to be any way to get Rollup to emit a source map // that goes all the way back to the source file (rather than just to // the bundle.esm.js intermediate file), so we pass sourcemap:false in - // the output options above, and manually write the CJS and UMD source + // the output options above, and manually write the CJS source // maps here. fs.writeFileSync( outputFile(toFormat) + '.map', @@ -127,7 +122,6 @@ function rollup({ return [ fromSource('esm'), fromESM('cjs'), - fromESM('umd'), { input: outputFile('cjs'), output: { diff --git a/package-lock.json b/package-lock.json index eeeba0a4b08..fdff56d562f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -173,16 +173,6 @@ "babel-plugin-dynamic-import-node": "^2.3.0" } }, - "@babel/plugin-transform-modules-umd": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.2.0.tgz", - "integrity": "sha512-BV3bw6MyUH1iIsGhXlOK6sXhmSarZjtJ/vMiD9dNmpY8QXFFQTj+6v92pcfy1iqa8DeAfJFwoxcrS/TUZda6sw==", - "dev": true, - "requires": { - "@babel/helper-module-transforms": "^7.1.0", - "@babel/helper-plugin-utils": "^7.0.0" - } - }, "@babel/runtime": { "version": "7.6.0", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.6.0.tgz", diff --git a/package.json b/package.json index bc5c98945de..2808b733763 100644 --- a/package.json +++ b/package.json @@ -68,7 +68,6 @@ "devDependencies": { "@babel/core": "7.6.0", "@babel/plugin-transform-modules-commonjs": "7.6.0", - "@babel/plugin-transform-modules-umd": "7.2.0", "@testing-library/react": "^9.1.4", "@types/fast-json-stable-stringify": "^2.0.0", "@types/isomorphic-fetch": "0.0.35", From ceedde95e4a93334c8c38709169b38c3a47cebfb Mon Sep 17 00:00:00 2001 From: hwillson Date: Sat, 21 Sep 2019 12:45:46 -0400 Subject: [PATCH 15/28] Rollup bundling changes These changes wire up a new Rollup based bundling approach. We no longer build and provide an ESM bundle. By providing an ESM bundle, and pointing to it via the `package.json` `module` property, we were inadvertently reducing the effectiveness of build tool tree shaking / dead code elimination capabilities. For example, since webpack was getting the ESM bundle from the `module` property, it wasn't able to get to the fully extracted ESM source in `./lib`, to use it for DCE. With the changes in this commit, the `package.json` continues to point `main` to the CJS bundle (with all Apollo Client code in it), and now points `module` to the non-bundled ESM entry point of `./lib/index.js`. This means we're leaving the bundling of ESM code up to Apollo Client consumers and their build tooling, most of which will be able to leverage the expanded ESM source to build more efficient bundles on their side. We'll re-visit this strategy as work on AC 3 progresses, but so far the results have been promising. --- config/rollup.config.js | 182 ++++++++++++--------------------- package-lock.json | 217 +--------------------------------------- package.json | 10 +- 3 files changed, 69 insertions(+), 340 deletions(-) diff --git a/config/rollup.config.js b/config/rollup.config.js index 8e12f46f5ce..e40c44c84fc 100644 --- a/config/rollup.config.js +++ b/config/rollup.config.js @@ -1,22 +1,10 @@ import nodeResolve from 'rollup-plugin-node-resolve'; -import typescriptPlugin from 'rollup-plugin-typescript2'; -import typescript from 'typescript'; -import path from 'path'; -import fs from 'fs'; -import { transformSync } from '@babel/core'; -import cjsModulesTransform from '@babel/plugin-transform-modules-commonjs'; import invariantPlugin from 'rollup-plugin-invariant'; import { terser as minify } from 'rollup-plugin-terser'; -function onwarn(message) { - const suppressed = ['UNRESOLVED_IMPORT', 'THIS_IS_UNDEFINED']; +import packageJson from '../package.json'; - if (!suppressed.find(code => message.code === code)) { - return console.warn(message.message); - } -} - -const defaultGlobals = { +const globals = { 'apollo-link': 'apolloLink.core', 'tslib': 'tslib', 'ts-invariant': 'invariant', @@ -30,117 +18,75 @@ const defaultGlobals = { react: 'React' }; -function rollup({ - input = './src/index.ts', - outputPrefix = 'apollo-client', - extraGlobals = {}, -} = {}) { - const projectDir = path.join(__dirname, '..'); - console.info(`Building project esm ${projectDir}`); - const tsconfig = `${projectDir}/tsconfig.json`; +function external(id) { + return Object.prototype.hasOwnProperty.call(globals, id); +} - const globals = { - ...defaultGlobals, - ...extraGlobals, +function prepareESM() { + return { + input: packageJson.module, + external, + preserveModules: true, + output: { + dir: './lib', + format: 'esm', + sourcemap: true, + }, + plugins: [ + nodeResolve(), + invariantPlugin({ + // Instead of completely stripping InvariantError messages in + // production, this option assigns a numeric code to the + // production version of each error (unique to the call/throw + // location), which makes it much easier to trace production + // errors back to the unminified code where they were thrown, + // where the full error string can be found. See #4519. + errorCodes: true, + }), + ] }; +} - function external(id) { - return Object.prototype.hasOwnProperty.call(globals, id); - } - - function outputFile(format) { - return `./lib/${outputPrefix}.${format}.js`; - } - - function fromSource(format) { - return { - input, - external, - output: { - file: outputFile(format), - format, - sourcemap: true, - }, - plugins: [ - nodeResolve({ - extensions: ['.ts', '.tsx'], - }), - typescriptPlugin({ typescript, tsconfig }), - invariantPlugin({ - // Instead of completely stripping InvariantError messages in - // production, this option assigns a numeric code to the - // production version of each error (unique to the call/throw - // location), which makes it much easier to trace production - // errors back to the unminified code where they were thrown, - // where the full error string can be found. See #4519. - errorCodes: true, - }), - ], - onwarn, - }; - } - - function fromESM(toFormat) { - return { - input: outputFile('esm'), - output: { - file: outputFile(toFormat), - format: 'esm', - sourcemap: false, - }, - plugins: [{ - transform(source, id) { - const output = transformSync(source, { - inputSourceMap: JSON.parse(fs.readFileSync(id + '.map')), - sourceMaps: true, - plugins: [ - [cjsModulesTransform, { - loose: true, - allowTopLevelThis: true, - }], - ], - }); - - // There doesn't seem to be any way to get Rollup to emit a source map - // that goes all the way back to the source file (rather than just to - // the bundle.esm.js intermediate file), so we pass sourcemap:false in - // the output options above, and manually write the CJS source - // maps here. - fs.writeFileSync( - outputFile(toFormat) + '.map', - JSON.stringify(output.map), - ); - - return { - code: output.code, - }; - } - }], +function prepareCJS() { + return { + input: packageJson.module, + external, + output: { + file: packageJson.main, + format: 'cjs', + sourcemap: true, + exports: 'named' } } +} - return [ - fromSource('esm'), - fromESM('cjs'), - { - input: outputFile('cjs'), - output: { - file: outputFile('cjs.min'), - format: 'esm', - }, - plugins: [ - minify({ - mangle: { - toplevel: true, - }, - compress: { - global_defs: { - '@process.env.NODE_ENV': JSON.stringify('production'), - }, - }, - }), - ], +function prepareCJSMinified() { + return { + input: packageJson.main, + output: { + file: packageJson.main.replace('.js', '.min.js'), + format: 'cjs', }, + plugins: [ + minify({ + mangle: { + toplevel: true, + }, + compress: { + global_defs: { + '@process.env.NODE_ENV': JSON.stringify('production'), + }, + }, + }), + ], + }; +} + +function rollup() { + return [ + prepareESM(), + prepareCJS(), + prepareCJSMinified() ]; } diff --git a/package-lock.json b/package-lock.json index fdff56d562f..5d1719aa0c1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -76,45 +76,12 @@ "@babel/types": "^7.0.0" } }, - "@babel/helper-module-imports": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.0.0.tgz", - "integrity": "sha512-aP/hlLq01DWNEiDg4Jn23i+CXxW/owM4WpDLFUbpjxe4NS3BhLVZQ5i7E0ZrxuQ/vwekIeciyamgB1UIYxxM6A==", - "dev": true, - "requires": { - "@babel/types": "^7.0.0" - } - }, - "@babel/helper-module-transforms": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.5.5.tgz", - "integrity": "sha512-jBeCvETKuJqeiaCdyaheF40aXnnU1+wkSiUs/IQg3tB85up1LyL8x77ClY8qJpuRJUcXQo+ZtdNESmZl4j56Pw==", - "dev": true, - "requires": { - "@babel/helper-module-imports": "^7.0.0", - "@babel/helper-simple-access": "^7.1.0", - "@babel/helper-split-export-declaration": "^7.4.4", - "@babel/template": "^7.4.4", - "@babel/types": "^7.5.5", - "lodash": "^4.17.13" - } - }, "@babel/helper-plugin-utils": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.0.0.tgz", "integrity": "sha512-CYAOUCARwExnEixLdB6sDm2dIJ/YgEAKDM1MOeMeZu9Ld/bDgVo8aiWrXwcY7OBh+1Ea2uUcVRcxKk0GJvW7QA==", "dev": true }, - "@babel/helper-simple-access": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.1.0.tgz", - "integrity": "sha512-Vk+78hNjRbsiu49zAPALxTb+JUQCz1aolpd8osOF16BGnLtseD21nbHgLPGUwrXEurZgiCOUmvs3ExTu4F5x6w==", - "dev": true, - "requires": { - "@babel/template": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, "@babel/helper-split-export-declaration": { "version": "7.4.4", "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.4.4.tgz", @@ -161,18 +128,6 @@ "@babel/helper-plugin-utils": "^7.0.0" } }, - "@babel/plugin-transform-modules-commonjs": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.6.0.tgz", - "integrity": "sha512-Ma93Ix95PNSEngqomy5LSBMAQvYKVe3dy+JlVJSHEXZR5ASL9lQBedMiCyVtmTLraIDVRE3ZjTZvmXXD2Ozw3g==", - "dev": true, - "requires": { - "@babel/helper-module-transforms": "^7.4.4", - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/helper-simple-access": "^7.1.0", - "babel-plugin-dynamic-import-node": "^2.3.0" - } - }, "@babel/runtime": { "version": "7.6.0", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.6.0.tgz", @@ -1860,15 +1815,6 @@ "slash": "^2.0.0" } }, - "babel-plugin-dynamic-import-node": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.0.tgz", - "integrity": "sha512-o6qFkpeQEBxcqt0XYlWzAVxNCSCZdUgcR8IRlhD/8DylxjjO4foPcvTW0GGKa/cVt3rvxZ7o5ippJ+/0nvLhlQ==", - "dev": true, - "requires": { - "object.assign": "^4.1.0" - } - }, "babel-plugin-istanbul": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-5.2.0.tgz", @@ -2376,12 +2322,6 @@ "integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==", "dev": true }, - "commondir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", - "dev": true - }, "component-emitter": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", @@ -3030,17 +2970,6 @@ "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", "dev": true }, - "fs-extra": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", - "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", - "dev": true, - "requires": { - "graceful-fs": "^4.2.0", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - } - }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -6063,15 +5992,6 @@ "minimist": "^1.2.0" } }, - "jsonfile": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.6" - } - }, "jsprim": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", @@ -6557,18 +6477,6 @@ } } }, - "object.assign": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", - "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", - "dev": true, - "requires": { - "define-properties": "^1.1.2", - "function-bind": "^1.1.1", - "has-symbols": "^1.0.0", - "object-keys": "^1.0.11" - } - }, "object.getownpropertydescriptors": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz", @@ -7270,12 +7178,6 @@ "tslib": "^1.9.3" } }, - "rollup-plugin-local-resolve": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/rollup-plugin-local-resolve/-/rollup-plugin-local-resolve-1.0.7.tgz", - "integrity": "sha1-xIZwFxbBWt0hJ1ZcLqoQESMyCIc=", - "dev": true - }, "rollup-plugin-node-resolve": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/rollup-plugin-node-resolve/-/rollup-plugin-node-resolve-5.2.0.tgz", @@ -7289,16 +7191,6 @@ "rollup-pluginutils": "^2.8.1" } }, - "rollup-plugin-sourcemaps": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/rollup-plugin-sourcemaps/-/rollup-plugin-sourcemaps-0.4.2.tgz", - "integrity": "sha1-YhJaqUCHqt97g+9N+vYptHMTXoc=", - "dev": true, - "requires": { - "rollup-pluginutils": "^2.0.1", - "source-map-resolve": "^0.5.0" - } - }, "rollup-plugin-terser": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/rollup-plugin-terser/-/rollup-plugin-terser-5.1.1.tgz", @@ -7312,105 +7204,6 @@ "terser": "^4.1.0" } }, - "rollup-plugin-typescript2": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/rollup-plugin-typescript2/-/rollup-plugin-typescript2-0.24.0.tgz", - "integrity": "sha512-yp7z9hZ5kXjOGXKXkfTRJuIPyLvKtUWfAGmreilnYn+qIVhE5CgE7SFvzLKHggsAXPaCAflHRiEj0l71WXWJkA==", - "dev": true, - "requires": { - "find-cache-dir": "^3.0.0", - "fs-extra": "8.1.0", - "resolve": "1.12.0", - "rollup-pluginutils": "2.8.1", - "tslib": "1.10.0" - }, - "dependencies": { - "find-cache-dir": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.0.0.tgz", - "integrity": "sha512-t7ulV1fmbxh5G9l/492O1p5+EBbr3uwpt6odhFTMc+nWyhmbloe+ja9BZ8pIBtqFWhOmCWVjx+pTW4zDkFoclw==", - "dev": true, - "requires": { - "commondir": "^1.0.1", - "make-dir": "^3.0.0", - "pkg-dir": "^4.1.0" - } - }, - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } - }, - "make-dir": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.0.0.tgz", - "integrity": "sha512-grNJDhb8b1Jm1qeqW5R/O63wUo4UXo2v2HMic6YT9i/HBlF93S8jkMgH7yugvY9ABDShH4VZMn8I+U8+fCNegw==", - "dev": true, - "requires": { - "semver": "^6.0.0" - } - }, - "p-limit": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.1.tgz", - "integrity": "sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "requires": { - "p-limit": "^2.2.0" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true - }, - "path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true - }, - "pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "requires": { - "find-up": "^4.0.0" - } - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, "rollup-pluginutils": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.8.1.tgz", @@ -8633,6 +8426,7 @@ "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.6.0.tgz", "integrity": "sha512-W+jrUHJr3DXKhrsS7NUVxn3zqMOFn0hL/Ei6v0anCIMoKC93TjcflTagwIHLW7SfMFfiQuktQyFVCFHGUE0+yg==", "dev": true, + "optional": true, "requires": { "commander": "~2.20.0", "source-map": "~0.6.1" @@ -8642,7 +8436,8 @@ "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true + "dev": true, + "optional": true } } }, @@ -8658,12 +8453,6 @@ "set-value": "^2.0.1" } }, - "universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", - "dev": true - }, "unset-value": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", diff --git a/package.json b/package.json index 2808b733763..8f406ce44c5 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "author": "opensource@apollographql.com", "license": "MIT", "main": "./lib/apollo-client.cjs.js", - "module": "./lib/apollo-client.esm.js", + "module": "./lib/index.js", "sideEffects": [ "./lib/cache/inmemory/fixPolyfills.js" ], @@ -66,8 +66,6 @@ "zen-observable": "^0.8.14" }, "devDependencies": { - "@babel/core": "7.6.0", - "@babel/plugin-transform-modules-commonjs": "7.6.0", "@testing-library/react": "^9.1.4", "@types/fast-json-stable-stringify": "^2.0.0", "@types/isomorphic-fetch": "0.0.35", @@ -91,16 +89,12 @@ "rimraf": "^3.0.0", "rollup": "1.21.2", "rollup-plugin-invariant": "0.5.6", - "rollup-plugin-local-resolve": "1.0.7", "rollup-plugin-node-resolve": "5.2.0", - "rollup-plugin-sourcemaps": "0.4.2", "rollup-plugin-terser": "5.1.1", - "rollup-plugin-typescript2": "0.24.0", "rxjs": "6.5.3", "ts-jest": "24.0.2", "tsc-watch": "3.0.1", - "typescript": "^3.6.2", - "uglify-js": "3.6.0" + "typescript": "^3.6.2" }, "publishConfig": { "access": "public" From 4ecd8e12dd586f98fa63c514cc275c9ffcaae9bd Mon Sep 17 00:00:00 2001 From: hwillson Date: Sat, 21 Sep 2019 16:06:22 -0400 Subject: [PATCH 16/28] Be explicit about root named React exports --- src/react/index.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/react/index.ts b/src/react/index.ts index 501468295e3..7049c322186 100644 --- a/src/react/index.ts +++ b/src/react/index.ts @@ -10,4 +10,12 @@ export { useApolloClient } from './hooks/useApolloClient'; export { RenderPromises } from './ssr/RenderPromises'; -export * from './types/types'; +export { + QueryHookOptions, + QueryResult, + LazyQueryHookOptions, + QueryTuple, + MutationHookOptions, + MutationTuple, + SubscriptionHookOptions, +} from './types/types'; From 87c410be3fc7b03aa2d6c31458b2393c4f9783f1 Mon Sep 17 00:00:00 2001 From: hwillson Date: Mon, 23 Sep 2019 05:40:23 -0400 Subject: [PATCH 17/28] Doc updates to reflect new Apollo Client React API --- docs/gatsby-config.js | 9 ++- docs/source/advanced/subscriptions.mdx | 4 +- .../api/{apollo-client.mdx => core.mdx} | 3 +- docs/source/api/react-common.mdx | 35 ---------- docs/source/api/react-components.mdx | 11 ++- docs/source/api/react-hoc.mdx | 16 ++--- docs/source/api/react-hooks.mdx | 41 +++++++---- docs/source/api/react-ssr.md | 4 +- docs/source/api/react-testing.md | 12 ++-- docs/source/essentials/get-started.mdx | 2 +- docs/source/essentials/local-state.mdx | 8 +-- docs/source/essentials/queries.mdx | 2 +- docs/source/features/caching.md | 20 +++--- docs/source/hooks-migration.md | 68 +++++-------------- docs/source/integrations.md | 4 +- docs/source/recipes/static-typing.md | 2 +- docs/source/recipes/testing.mdx | 14 ++-- docs/source/why-apollo.mdx | 2 +- docs/static/_redirects | 4 ++ 19 files changed, 104 insertions(+), 157 deletions(-) rename docs/source/api/{apollo-client.mdx => core.mdx} (99%) delete mode 100644 docs/source/api/react-common.mdx diff --git a/docs/gatsby-config.js b/docs/gatsby-config.js index 4da69d6cba7..ebf96897d50 100644 --- a/docs/gatsby-config.js +++ b/docs/gatsby-config.js @@ -59,14 +59,13 @@ module.exports = { 'recipes/meteor', 'recipes/recompose', ], - 'API Reference': [ - 'api/apollo-client', + 'Apollo Client API': [ + 'api/core', 'api/react-hooks', - 'api/react-ssr', 'api/react-testing', + 'api/react-ssr', 'api/react-components', - 'api/react-hoc', - 'api/react-common', + 'api/react-hoc' ], }, }, diff --git a/docs/source/advanced/subscriptions.mdx b/docs/source/advanced/subscriptions.mdx index d3e85e244bb..22bd48c64b9 100644 --- a/docs/source/advanced/subscriptions.mdx +++ b/docs/source/advanced/subscriptions.mdx @@ -120,7 +120,7 @@ Now, queries and mutations will go over HTTP as normal, but subscriptions will b ## useSubscription Hook -The easiest way to bring live data to your UI is by using React Apollo's `useSubscription` Hook. This lets you render the stream of data from your service directly within your render function of your component! One thing to note, subscriptions are just listeners, they don't request any data when first connected, but only open up a connection to get new data. Binding live data to your UI is as easy as this: +The easiest way to bring live data to your UI is by using Apollo Client's `useSubscription` Hook. This lets you render the stream of data from your service directly within your render function of your component! One thing to note, subscriptions are just listeners, they don't request any data when first connected, but only open up a connection to get new data. Binding live data to your UI is as easy as this:
@@ -199,7 +199,7 @@ With GraphQL subscriptions your client will be alerted on push from the server a With `subscribeToMore`, you can easily do the latter. -`subscribeToMore` is a function available on every query result in React Apollo. It works just like [`fetchMore`](/advanced/caching/#incremental-loading-fetchmore), except that the update function gets called every time the subscription returns, instead of only once. +`subscribeToMore` is a function available on every query result, made using Apollo Client's React integration. It works just like [`fetchMore`](/advanced/caching/#incremental-loading-fetchmore), except that the update function gets called every time the subscription returns, instead of only once. Here is a regular query: diff --git a/docs/source/api/apollo-client.mdx b/docs/source/api/core.mdx similarity index 99% rename from docs/source/api/apollo-client.mdx rename to docs/source/api/core.mdx index 201ff521a47..f29267a839e 100644 --- a/docs/source/api/apollo-client.mdx +++ b/docs/source/api/core.mdx @@ -1,5 +1,6 @@ --- -title: class ApolloClient +title: Core +description: Apollo Client core API reference order: 11 --- diff --git a/docs/source/api/react-common.mdx b/docs/source/api/react-common.mdx deleted file mode 100644 index 2f9d4d1d2e8..00000000000 --- a/docs/source/api/react-common.mdx +++ /dev/null @@ -1,35 +0,0 @@ ---- -title: '@apollo/react-common' -description: API reference ---- - -import ApolloProvider from '../../shared/apollo-provider.mdx'; - -## Installation - -``` -npm install @apollo/react-common -``` - -## `ApolloProvider` - - - -## `ApolloConsumer` - -One way to access the configured Apollo Client instance directly is to create an `ApolloConsumer` component and provide a render prop function as its child. The render prop function will be called with your `ApolloClient` instance as its only argument. You can think of the `ApolloConsumer` component as similar to the `Consumer` component from the [React Context API](https://reactjs.org/docs/context.html). - -### Example - -```jsx -import React from 'react'; -import { ApolloConsumer } from '@apollo/react-common'; -// or from the hooks, components, hoc packages: -// import { ApolloConsumer } from "@apollo/react-hooks"; - -const WithApolloClient = () => ( - - {client => 'We have access to the client!' /* do stuff here */} - -); -``` diff --git a/docs/source/api/react-components.mdx b/docs/source/api/react-components.mdx index 627b2d43a19..1d761e980f4 100644 --- a/docs/source/api/react-components.mdx +++ b/docs/source/api/react-components.mdx @@ -1,6 +1,6 @@ --- -title: '@apollo/react-components' -description: API reference +title: 'React - Components (DEPRECATED)' +description: Deprecated React Apollo render prop component API --- import QueryOptions from '../../shared/query-options.mdx'; @@ -9,7 +9,8 @@ import MutationOptions from '../../shared/mutation-options.mdx'; import MutationResult from '../../shared/mutation-result.mdx'; import SubscriptionOptions from '../../shared/subscription-options.mdx'; import SubscriptionResult from '../../shared/subscription-result.mdx'; -import ApolloProvider from '../../shared/apollo-provider.mdx'; + +> **NOTE:** React Apollo's render prop components have been deprecated. They will continue to receive bug fixes until March 2020, after which they will no longer be maintained by Apollo. ## Installation @@ -56,7 +57,3 @@ The Subscription component accepts the following props. Only `subscription` is * The render prop function that you pass to the `children` prop of `Subscription` is called with an object that has the following properties. - -## `ApolloProvider` - - diff --git a/docs/source/api/react-hoc.mdx b/docs/source/api/react-hoc.mdx index 1ce5daa6190..141cc3eecf2 100644 --- a/docs/source/api/react-hoc.mdx +++ b/docs/source/api/react-hoc.mdx @@ -1,10 +1,12 @@ --- -title: '@apollo/react-hoc' -description: API reference +title: 'React - HOC (DEPRECATED)' +description: Deprecated React Apollo HOC API --- import ApolloProvider from '../../shared/apollo-provider.mdx'; +> **NOTE:** React Apollo's higher order components have been deprecated. They will continue to receive bug fixes until March 2020, after which they will no longer be maintained by Apollo. + ## Installation ``` @@ -55,7 +57,7 @@ const TodoAppWithData = withTodoAppQuery(TodoApp); export default TodoAppWithData; ``` -The `graphql()` function will only be able to provide access to your GraphQL data if there is a [``](/api/react-common/#apolloprovider) component higher up in your tree to provide an [`ApolloClient`](/api/apollo-client/) instance that will be used to fetch your data. +The `graphql()` function will only be able to provide access to your GraphQL data if there is a [``](/api/react-hooks/#apolloprovider) component higher up in your tree to provide an [`ApolloClient`](/api/core/) instance that will be used to fetch your data. The behavior of your component enhanced with the `graphql()` function will be different depending on if your GraphQL operation is a [query](/essentials/queries/), a [mutation](/essentials/mutations/), or a [subscription](/advanced/subscriptions/). Go to the appropriate API documentation for more information about the functionality and available options for each type. @@ -1068,11 +1070,11 @@ export default graphql( import { withApollo } from '@apollo/react-hoc'; ``` -A simple enhancer which provides direct access to your [`ApolloClient`](/api/apollo-client/) instance. This is useful if you want to do custom logic with Apollo. Such as calling one-off queries. By calling this function with the component you want to enhance, `withApollo()` will create a new component which passes in an instance of `ApolloClient` as a `client` prop. +A simple enhancer which provides direct access to your [`ApolloClient`](/api/core/) instance. This is useful if you want to do custom logic with Apollo. Such as calling one-off queries. By calling this function with the component you want to enhance, `withApollo()` will create a new component which passes in an instance of `ApolloClient` as a `client` prop. If you are wondering when to use `withApollo()` and when to use [`graphql()`](#graphqlquery-configcomponent) the answer is that most of the time you will want to use `graphql()`. `graphql()` provides many of the advanced features you need to work with your GraphQL data. You should only use `withApollo()` if you want the GraphQL client without any of the other features. -This will only be able to provide access to your client if there is an [``](/api/react-common/#apolloprovider) component higher up in your tree to actually provide the client. +This will only be able to provide access to your client if there is an [``](/api/react-hooks/#apolloprovider) component higher up in your tree to actually provide the client. **Example:** @@ -1083,7 +1085,3 @@ function MyComponent({ client }) { export default withApollo(MyComponent); ``` - -### `ApolloProvider` - - diff --git a/docs/source/api/react-hooks.mdx b/docs/source/api/react-hooks.mdx index 9b1bce1b52f..cbca879a293 100644 --- a/docs/source/api/react-hooks.mdx +++ b/docs/source/api/react-hooks.mdx @@ -1,20 +1,39 @@ --- -title: '@apollo/react-hooks' -description: API reference +title: React - Hooks +description: Apollo Client React API reference --- +import ApolloProvider from '../../shared/apollo-provider.mdx'; import QueryOptions from '../../shared/query-options.mdx'; import QueryResult from '../../shared/query-result.mdx'; import MutationOptions from '../../shared/mutation-options.mdx'; import MutationResult from '../../shared/mutation-result.mdx'; import SubscriptionOptions from '../../shared/subscription-options.mdx'; import SubscriptionResult from '../../shared/subscription-result.mdx'; -import ApolloProvider from '../../shared/subscription-result.mdx'; ## Installation -``` -npm install @apollo/react-hooks +Apollo Client >= 3 includes React hooks functionality out of the box. You don't need to install any additional packages. + +## `ApolloProvider` + + + +## `ApolloConsumer` + +One way to access the configured Apollo Client instance directly is to create an `ApolloConsumer` component and provide a render prop function as its child. The render prop function will be called with your `ApolloClient` instance as its only argument. You can think of the `ApolloConsumer` component as similar to the `Consumer` component from the [React Context API](https://reactjs.org/docs/context.html). + +### Example + +```jsx +import React from 'react'; +import { ApolloConsumer } from '@apollo/client'; + +const WithApolloClient = () => ( + + {client => 'We have access to the client!' /* do stuff here */} + +); ``` ## `useQuery` @@ -22,7 +41,7 @@ npm install @apollo/react-hooks ### Example ```jsx -import { useQuery } from '@apollo/react-hooks'; +import { useQuery } from '@apollo/client'; import gql from 'graphql-tag'; const GET_GREETING = gql` @@ -74,7 +93,7 @@ function useQuery( ### Example ```jsx -import { useLazyQuery } from "@apollo/react-hooks"; +import { useLazyQuery } from "@apollo/client"; import gql from "graphql-tag"; const GET_GREETING = gql` @@ -141,7 +160,7 @@ function useLazyQuery( ### Example ```jsx -import { useMutation } from '@apollo/react-hooks'; +import { useMutation } from '@apollo/client'; import gql from 'graphql-tag'; const ADD_TODO = gql` @@ -265,6 +284,8 @@ function useSubscription( ### Example ```jsx +import { useApolloClient } from '@apollo/client'; + function SomeComponent() { const client = useApolloClient(); // `client` is now set to the `ApolloClient` instance being used by the @@ -283,7 +304,3 @@ function useApolloClient(): ApolloClient {} | Param | Type | Description | | ---------------------- | -------------------------- | ---------------------------------------------------------- | | Apollo Client instance | ApolloClient<object> | The `ApolloClient` instance being used by the application. | - -### `ApolloProvider` - - diff --git a/docs/source/api/react-ssr.md b/docs/source/api/react-ssr.md index 11db310dd89..f8f7a42da0c 100644 --- a/docs/source/api/react-ssr.md +++ b/docs/source/api/react-ssr.md @@ -1,6 +1,6 @@ --- -title: "@apollo/react-ssr" -description: API reference +title: React - SSR +description: Apollo Client React server side rendering API --- ## Installation diff --git a/docs/source/api/react-testing.md b/docs/source/api/react-testing.md index 3f48e33aa85..09d278aa19a 100644 --- a/docs/source/api/react-testing.md +++ b/docs/source/api/react-testing.md @@ -1,21 +1,19 @@ --- -title: "@apollo/react-testing" -description: API reference +title: React - Testing +description: Apollo Client React testing API --- ## Installation -``` -npm install @apollo/react-testing -``` +Apollo Client >= 3 includes React testing utilities out of the box. You don't need to install any additional packages. ## `MockedProvider` ```js -import { MockedProvider } from "@apollo/react-testing"; +import { MockedProvider } from "@apollo/client/lib/react/testing"; ``` -The `MockedProvider` is a test-utility that allows you to create a mocked version of the [`ApolloProvider`](/api/react-common/#apolloprovider) that doesn't send out network requests to your API, but rather allows you to specify the exact response payload for a given request. +The `MockedProvider` is a test-utility that allows you to create a mocked version of the [`ApolloProvider`](/api/react-hooks/#apolloprovider) that doesn't send out network requests to your API, but rather allows you to specify the exact response payload for a given request. The `` component takes the following props: diff --git a/docs/source/essentials/get-started.mdx b/docs/source/essentials/get-started.mdx index 02dd68b79fd..b419e7ed670 100644 --- a/docs/source/essentials/get-started.mdx +++ b/docs/source/essentials/get-started.mdx @@ -168,4 +168,4 @@ Now that you've learned how to fetch data with Apollo Client, you're ready to di - [Queries](/essentials/queries/): Learn how to fetch queries with arguments and dive deeper into configuration options. For a full list of options, check out the API reference for `useQuery`. - [Mutations](/essentials/mutations/): Learn how to update data with mutations and when you'll need to update the Apollo cache. For a full list of options, check out the API reference for `useMutation` components. -- [Apollo Client API](/api/apollo-client/): Sometimes, you'll need to access the client directly like we did in our plain JavaScript example above. Visit the API reference for a full list of options. +- [Apollo Client API](/api/core/): Sometimes, you'll need to access the client directly like we did in our plain JavaScript example above. Visit the API reference for a full list of options. diff --git a/docs/source/essentials/local-state.mdx b/docs/source/essentials/local-state.mdx index 41828998164..6a4ecf1c558 100644 --- a/docs/source/essentials/local-state.mdx +++ b/docs/source/essentials/local-state.mdx @@ -351,7 +351,7 @@ cache.writeData({ }); ``` -Sometimes you may need to [reset the store](/api/apollo-client/#ApolloClient.resetStore) in your application, when a user logs out for example. If you call `client.resetStore` anywhere in your application, you will likely want to initialize your cache again. You can do this using the `client.onResetStore` method to register a callback that will call `cache.writeData` again. +Sometimes you may need to [reset the store](/api/core/#ApolloClient.resetStore) in your application, when a user logs out for example. If you call `client.resetStore` anywhere in your application, you will likely want to initialize your cache again. You can do this using the `client.onResetStore` method to register a callback that will call `cache.writeData` again. ```js import { ApolloClient, InMemoryCache } from '@apollo/client'; @@ -717,7 +717,7 @@ ReactDOM.render( -In the above example, we first prep the cache using `cache.writeData` to store a value for the `isLoggedIn` field. We then run the `IS_LOGGED_IN` query via a React Apollo `useQuery` hook, which includes an `@client` directive. When Apollo Client executes the `IS_LOGGED_IN` query, it first looks for a local resolver that can be used to handle the `@client` field. When it can't find one, it falls back on trying to pull the specified field out of the cache. So in this case, the `data` value returned by the `useQuery` hook has a `isLoggedIn` property available, which includes the `isLoggedIn` result (`!!localStorage.getItem('token')`) pulled directly from the cache. +In the above example, we first prep the cache using `cache.writeData` to store a value for the `isLoggedIn` field. We then run the `IS_LOGGED_IN` query via an Apollo Client `useQuery` hook, which includes an `@client` directive. When Apollo Client executes the `IS_LOGGED_IN` query, it first looks for a local resolver that can be used to handle the `@client` field. When it can't find one, it falls back on trying to pull the specified field out of the cache. So in this case, the `data` value returned by the `useQuery` hook has a `isLoggedIn` property available, which includes the `isLoggedIn` result (`!!localStorage.getItem('token')`) pulled directly from the cache. > ⚠️ If you want to use Apollo Client's `@client` support to query the cache without using local resolvers, you must pass an empty object into the `ApolloClient` constructor `resolvers` option. Without this Apollo Client will not enable its integrated `@client` support, which means your `@client` based queries will be passed to the Apollo Client link chain. You can find more details about why this is necessary [here](https://github.com/apollographql/apollo-client/pull/4499). @@ -725,7 +725,7 @@ Pulling `@client` field values directly out of the cache isn't quite as flexible ### Working with fetch policies -Before Apollo Client executes a query, one of the first things it does is check to see which [`fetchPolicy`](/api/apollo-client/#ApolloClient.query) it has been configured to use. It does this so it knows where it should attempt to resolve the query from first, either the cache or the network. When running a query, Apollo Client treats `@client` based local resolvers just like it does remote resolvers, in that it will adhere to its defined `fetchPolicy` to know where to attempt to pull data from first. When working with local resolvers, it's important to understand how fetch policies impact the running of resolver functions, since by default local resolver functions are not run on every request. This is because the result of running a local resolver is cached with the rest of the query result, and pulled from the cache on the next request. Let's look at an example: +Before Apollo Client executes a query, one of the first things it does is check to see which [`fetchPolicy`](/api/core/#ApolloClient.query) it has been configured to use. It does this so it knows where it should attempt to resolve the query from first, either the cache or the network. When running a query, Apollo Client treats `@client` based local resolvers just like it does remote resolvers, in that it will adhere to its defined `fetchPolicy` to know where to attempt to pull data from first. When working with local resolvers, it's important to understand how fetch policies impact the running of resolver functions, since by default local resolver functions are not run on every request. This is because the result of running a local resolver is cached with the rest of the query result, and pulled from the cache on the next request. Let's look at an example:
@@ -818,7 +818,7 @@ export default function Launch({ launchId }) {
-In the above example we're using a React Apollo `useQuery` hook to run the `GET_LAUNCH_DETAILS` query. The `@client` based `isInCart` field is configured to pull its data from the following resolver: +In the above example we're using an Apollo Client `useQuery` hook to run the `GET_LAUNCH_DETAILS` query. The `@client` based `isInCart` field is configured to pull its data from the following resolver: ```js import { GET_CART_ITEMS } from './pages/cart'; diff --git a/docs/source/essentials/queries.mdx b/docs/source/essentials/queries.mdx index fb52e6f2acf..8697b7411ec 100644 --- a/docs/source/essentials/queries.mdx +++ b/docs/source/essentials/queries.mdx @@ -48,7 +48,7 @@ const GET_DOGS = gql` ```tsx:title=index.js import gql from 'graphql-tag'; -import { Query } from 'react-apollo'; +import { Query } from '@apollo/react-components'; const GET_DOGS = gql` { diff --git a/docs/source/features/caching.md b/docs/source/features/caching.md index c4a694fad18..49875315e76 100644 --- a/docs/source/features/caching.md +++ b/docs/source/features/caching.md @@ -20,7 +20,7 @@ const client = new ApolloClient({ ... }); ## `readQuery` -The `readQuery` method is very similar to the [`query` method on `ApolloClient`](/api/apollo-client/#ApolloClient.query) except that `readQuery` will _never_ make a request to your GraphQL server. The `query` method, on the other hand, may send a request to your server if the appropriate data is not in your cache whereas `readQuery` will throw an error if the data is not in your cache. `readQuery` will _always_ read from the cache. You can use `readQuery` by giving it a GraphQL query like so: +The `readQuery` method is very similar to the [`query` method on `ApolloClient`](/api/core/#ApolloClient.query) except that `readQuery` will _never_ make a request to your GraphQL server. The `query` method, on the other hand, may send a request to your server if the appropriate data is not in your cache whereas `readQuery` will throw an error if the data is not in your cache. `readQuery` will _always_ read from the cache. You can use `readQuery` by giving it a GraphQL query like so: ```js const { todo } = client.readQuery({ @@ -59,8 +59,8 @@ const { todo } = client.readQuery({ **Resources:** -- [`ApolloClient#query` API documentation](/api/apollo-client/#ApolloClient.query) -- [`ApolloClient#readQuery` API documentation](/api/apollo-client/#ApolloClient.readQuery) +- [`ApolloClient#query` API documentation](/api/core/#ApolloClient.query) +- [`ApolloClient#readQuery` API documentation](/api/core/#ApolloClient.readQuery) ## `readFragment` @@ -111,7 +111,7 @@ The beauty of `readFragment` is that the todo could have come from anywhere! The **Resources:** -- [`ApolloClient#readFragment` API documentation](/api/apollo-client/#ApolloClient.readFragment) +- [`ApolloClient#readFragment` API documentation](/api/core/#ApolloClient.readFragment) ## `writeQuery` and `writeFragment` @@ -168,11 +168,11 @@ client.writeQuery({ **Resources:** -- [`ApolloClient#watchQuery` API documentation](/api/apollo-client/#ApolloClient.watchQuery) -- [`ApolloClient#writeQuery` API documentation](/api/apollo-client/#ApolloClient.writeQuery) -- [`ApolloClient#writeFragment` API documentation](/api/apollo-client/#ApolloClient.writeFragment) -- [`DataProxy#writeQuery` API documentation](/api/apollo-client/#ApolloClient.writeQuery) -- [`DataProxy#writeFragment` API documentation](/api/apollo-client/#ApolloClient.writeFragment) +- [`ApolloClient#watchQuery` API documentation](/api/core/#ApolloClient.watchQuery) +- [`ApolloClient#writeQuery` API documentation](/api/core/#ApolloClient.writeQuery) +- [`ApolloClient#writeFragment` API documentation](/api/core/#ApolloClient.writeFragment) +- [`DataProxy#writeQuery` API documentation](/api/core/#ApolloClient.writeQuery) +- [`DataProxy#writeFragment` API documentation](/api/core/#ApolloClient.writeFragment) ## Updating the cache after a mutation @@ -259,4 +259,4 @@ The `update` function is not a good place for side-effects as it may be called m **Resources:** -- [`ApolloClient#mutate` API documentation](/api/apollo-client/#ApolloClient.mutate) +- [`ApolloClient#mutate` API documentation](/api/core/#ApolloClient.mutate) diff --git a/docs/source/hooks-migration.md b/docs/source/hooks-migration.md index 2c6200f83ce..80ab64fb8df 100644 --- a/docs/source/hooks-migration.md +++ b/docs/source/hooks-migration.md @@ -5,83 +5,51 @@ description: How to integrate the new hooks API into your existing Apollo app The new hooks API for Apollo Client is a simpler way to fetch data in your React app without the boilerplate of render prop components and higher-order components (HOC). We recommend using hooks for all new Apollo code going forward. -Upgrading to the new hooks API requires a major version bump from the previous version of Apollo's React integration. Before updating to the latest version, it's important to review the following details: +## Core packages -## Breaking changes +React hooks functionality is included in the Apollo Client bundle, whereas the older HOC / render prop component approaches are not. The React team has made it clear that hooks are the future, so we've decided to keep the older approaches available through separate packages. -Please keep in mind that this is a major version release, and there are some breaking changes (we've tried to keep them to a minimum). The full breaking changes list can be found in the [`Changelog.md`](https://github.com/apollographql/react-apollo/blob/master/Changelog.md#breaking-changes). - -## New packages - -To help reduce application bundle sizes, React Apollo 3 introduces a new modular package structure: - -**Individual packages (recommended):** - -- [@apollo/react-hooks](https://www.npmjs.com/package/@apollo/react-hooks) -- [@apollo/react-components](https://www.npmjs.com/package/@apollo/react-components) -- [@apollo/react-hoc](https://www.npmjs.com/package/@apollo/react-hoc) - -**Umbrella package (includes hooks, render props, and HOCs):** - -- [react-apollo](https://www.npmjs.com/package/react-apollo) - -**Supporting packages:** - -- [@apollo/react-common](https://www.npmjs.com/package/@apollo/react-common) -- [@apollo/react-ssr](https://www.npmjs.com/package/@apollo/react-ssr) -- [@apollo/react-testing](https://www.npmjs.com/package/@apollo/react-testing) - -You can use the umbrella `react-apollo` package to get access to the new hooks, along with the legacy `graphql` HOC and `Query` / `Mutation` / `Subscription` components. If you're planning on supporting each of these 3 different paradigms in your application while you incrementally migrate, then the `react-apollo` package should work well. - -If you're only planning on using some of this functionality, like only the new hooks, then you'll want to install the individual packages instead of `react-apollo`. Installing only the `@apollo/react-hooks` package yields a 50% bundle size savings than using all of `react-apollo`. +- [`@apollo/client`](https://www.npmjs.com/package/@apollo/client) - Apollo Client core with React hooks integration +- [`@apollo/react-components`](https://www.npmjs.com/package/@apollo/react-components) - React Apollo render prop components +- [`@apollo/react-hoc`](https://www.npmjs.com/package/@apollo/react-hoc) - React Apollo HOC (`grapqhl`) API ### Installation/upgrade scenarios **I just want to use Apollo hooks:** ``` -npm install @apollo/react-hooks +npm install @apollo/client ``` +(remove the `react-apollo` and `@apollo/react-hooks` packages if they were previously installed) + **I just want to use Apollo render prop components:** ``` -npm install @apollo/react-components +npm install @apollo/client @apollo/react-components ``` -(and remove the `react-apollo` package if it was previously installed) +(remove the `react-apollo` package if it was previously installed) **I just want to use Apollo HOCs:** ``` -npm install @apollo/react-hoc -``` - -(and remove the `react-apollo` package if it was previously installed) - -**I want to use all 3 React Apollo paradigms in my application:** - -``` -npm install react-apollo +npm install @apollo/client @apollo/react-hoc ``` -> **Note:** We're going to be moving away from the `react-apollo` package in the future, so installing the individual paradigm packages instead of `react-apollo` is recommended. - -### Bundle size note +(remove the `react-apollo` package if it was previously installed) -React Apollo 3 uses hooks behind the scenes for everything, including the `graphql` HOC and render prop components. While installing and using only the paradigm package(s) that interest you will save on bundle size, the savings will vary based on the packages you're using. This is because the HOC package depends on the components package, and the components package depends on the hooks package. In other words: +**I want to use all 3 React paradigms in my application:** ``` -@apollo/react-hoc <-- @apollo/react-components <-- @apollo/react-hooks -@apollo/react-components <-- @apollo/react-hooks -@apollo/react-hooks +npm install @apollo/client @apollo/react-components @apollo/react-hoc ``` -This means using only the `@apollo/react-hooks` package will give you the greatest bundle size savings. +(remove the `react-apollo` and `@apollo/react-hooks` packages if they were previously installed) ## Server-side rendering -The `getDataFromTree` and `renderToStringWithData` SSR functions are no longer bundled with any of the React Apollo packages in order to help reduce bundle sizes for those who aren't using SSR. If you want to use these functions, you'll need to add in the `@apollo/react-ssr` package: +The `getDataFromTree` and `renderToStringWithData` React SSR functions are not bundled with Apollo Client, in order to help reduce bundle sizes for those who aren't using SSR. If you want to use these functions, you'll need to add in the `@apollo/react-ssr` package: ``` npm install @apollo/react-ssr @@ -89,8 +57,8 @@ npm install @apollo/react-ssr ## Testing -Testing utilities are no longer bundled with any of the React Apollo packages. Everything has been moved into the `@apollo/react-testing` package: +React testing utilities are now available through the Apollo Client project, but they aren't included in the default bundle. To access the React testing utilities, you can use a cherry-pick import from `@apollo/client/lib/react/testing` like: ``` -npm install --save-dev @apollo/react-testing +import { MockedProvider } from '@apollo/client/lib/react/testing'; ``` diff --git a/docs/source/integrations.md b/docs/source/integrations.md index 0abb96af442..daf0380da2f 100644 --- a/docs/source/integrations.md +++ b/docs/source/integrations.md @@ -6,9 +6,9 @@ description: How to use Apollo Client with the view layer your application is de ## React -React Apollo allows you to fetch data from your GraphQL server and use it in building complex and reactive UIs using the React framework. React Apollo may be used in any context that React may be used. In the browser, in React Native, or in Node.js when you want to server side render. +Apollo Client's built-in React support allows you to fetch data from your GraphQL server and use it in building complex and reactive UIs using the React framework. Apollo Client may be used in any context that React may be used. In the browser, in React Native, or in Node.js when you want to server side render. -React Apollo, unlike some other tools in the React ecosystem, requires _no_ complex build setup to get up and running. As long as you have a GraphQL server you can get started building out your application with React immediately. React Apollo works out of the box with both [`create-react-app`](https://github.com/facebookincubator/create-react-app) and [React Native](http://facebook.github.io/react-native +Apollo Client, unlike some other tools in the React ecosystem, requires _no_ complex build setup to get up and running. As long as you have a GraphQL server you can get started building out your application with React immediately. Apollo Client's React functionality works out of the box with both [`create-react-app`](https://github.com/facebookincubator/create-react-app) and [React Native](http://facebook.github.io/react-native ) with a single install and with no extra hassle configuring Babel or other JavaScript tools. ## Vue diff --git a/docs/source/recipes/static-typing.md b/docs/source/recipes/static-typing.md index 2b79ea5d7ad..cbf188ba637 100644 --- a/docs/source/recipes/static-typing.md +++ b/docs/source/recipes/static-typing.md @@ -11,7 +11,7 @@ The most common need when using type systems with GraphQL is to type the results ## Typing hooks -React Apollo's `useQuery`, `useMutation` and `useSubscription` hooks are fully typed, and Generics can be used to type both incoming operation variables and GraphQL result data. React Apollo Hook options and result types are listed in the [Hooks API](/api/react-hooks/) section of the docs. You can find a typed example of each Hook below. +Apollo Client's `useQuery`, `useMutation` and `useSubscription` React hooks are fully typed, and Generics can be used to type both incoming operation variables and GraphQL result data. React Hook options and result types are listed in the [Hooks API](/api/react-hooks/) section of the docs. You can find a typed example of each Hook below. ### `useQuery` diff --git a/docs/source/recipes/testing.mdx b/docs/source/recipes/testing.mdx index 6fede7d6428..3ac472ccce4 100644 --- a/docs/source/recipes/testing.mdx +++ b/docs/source/recipes/testing.mdx @@ -1,13 +1,13 @@ --- title: Testing React components -description: Have peace of mind when using React Apollo in production +description: Peace of mind when using Apollo Client with React in production --- import { MultiCodeBlock } from 'gatsby-theme-apollo-docs'; -Running tests against code meant for production has long been a best practice. It provides additional security for the code that's already written, and prevents accidental regressions in the future. Components utilizing React Apollo, the React implementation of Apollo Client, are no exception. +Running tests against code meant for production has long been a best practice. It provides additional security for the code that's already written, and prevents accidental regressions in the future. Components utilizing React with Apollo Client are no exception. -Although React Apollo has a lot going on under the hood, the library provides multiple tools for testing that simplify those abstractions, and allows complete focus on the component logic. These testing utilities have long been used to test the React Apollo library itself, so they will be supported long-term. +Although Apollo Client's React integration has a lot going on under the hood, the library provides multiple tools for testing that simplify those abstractions, and allows complete focus on the component logic. These testing utilities have long been used to test React functoinality with Apollo Client itself, so they will be supported long-term. ## An introduction @@ -15,7 +15,7 @@ Apollo Client's React integration relies on [React's context](https://reactjs.or This guide will explain step-by-step how to test Apollo Client React code. The following examples use the [Jest](https://facebook.github.io/jest/docs/en/tutorial-react.html) testing framework, but most concepts should be reusable with other libraries. These examples aim to use as simple of a toolset as possible, so React's [test renderer](https://reactjs.org/docs/test-renderer.html) will be used in place of React-specific tools like [Enzyme](https://github.com/airbnb/enzyme) and [react-testing-library](https://github.com/kentcdodds/react-testing-library). -> **Note:** As of React Apollo 3, all testing utilities can now be found in their own `@apollo/react-testing` package. +> **Note:** As of Apollo Client 3, all testing utilities can now be imported from `@apollo/client/lib/react/testing`. Consider the component below, which makes a basic query, and displays its results: @@ -25,7 +25,7 @@ Consider the component below, which makes a basic query, and displays its result ```jsx import React from 'react'; import gql from 'graphql-tag'; -import { useQuery } from '@apollo/react-hooks'; +import { useQuery } from '@apollo/client'; // Make sure the query is also exported -- not just the component export const GET_DOG_QUERY = gql` @@ -122,7 +122,7 @@ it('renders without error', () => { ## `MockedProvider` -The `@apollo/react-testing` package exports a `MockedProvider` component which simplifies the testing of React components by mocking calls to the GraphQL endpoint. This allows the tests to be run in isolation and provides consistent results on every run by removing the dependence on remote data. +The `@apollo/client` package exports a `MockedProvider` component which simplifies the testing of React components by mocking calls to the GraphQL endpoint. This allows the tests to be run in isolation and provides consistent results on every run by removing the dependence on remote data. By using this `MockedProvider` component, it's possible to specify the exact results that should be returned for a certain query using the `mocks` prop. @@ -131,7 +131,7 @@ Here's an example of a test for the above `Dog` component using `MockedProvider` ```jsx // dog.test.js -import { MockedProvider } from '@apollo/react-testing'; +import { MockedProvider } from '@apollo/client/lib/react/testing'; // The component AND the query need to be exported import { GET_DOG_QUERY, Dog } from './dog'; diff --git a/docs/source/why-apollo.mdx b/docs/source/why-apollo.mdx index 12f888e65a7..7de5d929fc0 100644 --- a/docs/source/why-apollo.mdx +++ b/docs/source/why-apollo.mdx @@ -9,7 +9,7 @@ Data management shouldn't have to be so difficult! If you're wondering how to si ## Declarative data fetching -With Apollo's declarative approach to data fetching, all of the logic for retrieving your data, tracking loading and error states, and updating your UI is encapsulated by the `useQuery` Hook. This encapsulation makes integrating query results into your presentational components a breeze! Let's see what this looks like in practice with React Apollo: +With Apollo's declarative approach to data fetching, all of the logic for retrieving your data, tracking loading and error states, and updating your UI is encapsulated by the `useQuery` Hook. This encapsulation makes integrating query results into your presentational components a breeze! Let's see what this looks like in practice with Apollo Client and React:
diff --git a/docs/static/_redirects b/docs/static/_redirects index a1386478c04..e97e042a75c 100644 --- a/docs/static/_redirects +++ b/docs/static/_redirects @@ -33,3 +33,7 @@ /docs/react/api/apollo-client.html#FetchPolicy docs/react/api/react-apollo.html#graphql-config-options-fetchPolicy /docs/react/api/apollo-client.html#ErrorPolicy docs/react/api/react-apollo.html#graphql-config-options-errorPolicy /docs/react/features/performance.html /docs/react/recipes/performance.html + +# AC 3 changes +/docs/react/api/apollo-client /docs/react/api/core +/docs/react/api/react-common /docs/react/api/react-hooks From 68195a930b51bcbbe9bf7bc79a6826f761ccdb25 Mon Sep 17 00:00:00 2001 From: hwillson Date: Mon, 23 Sep 2019 07:18:15 -0400 Subject: [PATCH 18/28] Pin optimism to 0.11.1 @benjamn updating to 0.11.2 introduced: ``` src/cache/inmemory/entityCache.ts:2:15 - error TS2305: Module '"apollo-client/node_modules/optimism/lib"' has no exported member 'OptimisticDependencyFunction'. ``` --- package-lock.json | 6 +++--- package.json | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 246322a1ffc..e249d5b581c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6506,9 +6506,9 @@ } }, "optimism": { - "version": "0.11.2", - "resolved": "https://registry.npmjs.org/optimism/-/optimism-0.11.2.tgz", - "integrity": "sha512-vXO84TCqi4Q7rHrUJpV81BySp5q0nw/ZBBfwNCCceqBSszRcDsbLMoihfuGVRd2UA1hIYRnxriiYLMlTinDMFg==", + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/optimism/-/optimism-0.11.1.tgz", + "integrity": "sha512-EVUZtx/BKcmZbqE8u7Ai+83FiUS2sLD4ZsmBlS1Pgf+1d5pfLXtX4lA9219OG6cUd77AYxgK9+TBV1myD0QCbQ==", "requires": { "@wry/context": "^0.4.0" } diff --git a/package.json b/package.json index 8d78731a688..169dcb4effe 100644 --- a/package.json +++ b/package.json @@ -60,7 +60,7 @@ "@wry/equality": "^0.1.9", "apollo-link": "^1.2.13", "fast-json-stable-stringify": "^2.0.0", - "optimism": "^0.11.1", + "optimism": "0.11.1", "symbol-observable": "^1.2.0", "ts-invariant": "^0.4.4", "tslib": "^1.10.0", From c3283dcb93e4f1f2a56ad55e49351f55106c2e2b Mon Sep 17 00:00:00 2001 From: hwillson Date: Mon, 23 Sep 2019 09:27:19 -0400 Subject: [PATCH 19/28] Fix TS source links; Exclude API from link checker The source links needed to be updated to match the new non-monorepo project structure. I'm not sure why the API is being excluded from link checking, but it's defintiely needed. Links like `/api/core/#ApolloClient.resetStore` aren't working, when they should be. But since the API was being excluded previosuly, this commit fixes the config to exclude it again. --- docs/gatsby-config.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/docs/gatsby-config.js b/docs/gatsby-config.js index ebf96897d50..5385c39b5c4 100644 --- a/docs/gatsby-config.js +++ b/docs/gatsby-config.js @@ -20,11 +20,17 @@ module.exports = { }, */ checkLinksOptions: { - exceptions: ['/api/apollo-client/', '/v2.4/api/apollo-client/'], + exceptions: [ + '/api/core/', + '/v2.4/api/core/', + '/v2.5/api/core/', + '/v2.6/api/core/', + '/v3.0/api/core/' + ], }, typescriptApiBox: { data: require('./docs.json'), - filepathPrefix: 'packages/apollo-client/src/', + filepathPrefix: 'src/', }, sidebarCategories: { null: ['index', 'why-apollo', 'integrations', 'hooks-migration'], From 378bef07bc97333b210fb8d98beb20f170c50cb9 Mon Sep 17 00:00:00 2001 From: hwillson Date: Mon, 23 Sep 2019 09:29:55 -0400 Subject: [PATCH 20/28] Update all docs deps --- docs/package-lock.json | 1858 +++++++++++----------------------------- docs/package.json | 6 +- 2 files changed, 503 insertions(+), 1361 deletions(-) diff --git a/docs/package-lock.json b/docs/package-lock.json index 517688cbfb8..0c0f5512327 100644 --- a/docs/package-lock.json +++ b/docs/package-lock.json @@ -3,11 +3,16 @@ "lockfileVersion": 1, "dependencies": { "@apollo/space-kit": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/@apollo/space-kit/-/space-kit-0.0.8.tgz", - "integrity": "sha512-uWA+PSXR+NhgvnG3MBNSM4+0EyzXzEyNgrX1FGS0bTEfyEQ546S5ghoSQwJoLqQdZHqw3UKzba5PH4cJrqbI4Q==", + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@apollo/space-kit/-/space-kit-2.9.0.tgz", + "integrity": "sha512-1tZEKH3Uo7ZHWu/JQl0ediozz6ov7arHZqpLOf5vzm5UTjl+SPGO0iDUmgecwKj1Wq8gCzn3+8ITyIPmoPb3wA==", "requires": { - "normalize.css": "^8.0.1" + "@emotion/cache": "^10.0.15", + "@emotion/core": "^10.0.15", + "@types/classnames": "^2.2.9", + "@types/tinycolor2": "^1.4.2", + "classnames": "^2.2.6", + "tinycolor2": "^1.4.1" } }, "@babel/code-frame": { @@ -19,17 +24,17 @@ } }, "@babel/core": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.5.5.tgz", - "integrity": "sha512-i4qoSr2KTtce0DmkuuQBV4AuQgGPUcPXMr9L5MyYAtk06z068lQ10a4O009fe5OB/DfNV+h+qqT7ddNV8UnRjg==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.6.0.tgz", + "integrity": "sha512-FuRhDRtsd6IptKpHXAa+4WPZYY2ZzgowkbLBecEDDSje1X/apG7jQM33or3NdOmjXBKWGOg4JmSiRfUfuTtHXw==", "requires": { "@babel/code-frame": "^7.5.5", - "@babel/generator": "^7.5.5", - "@babel/helpers": "^7.5.5", - "@babel/parser": "^7.5.5", - "@babel/template": "^7.4.4", - "@babel/traverse": "^7.5.5", - "@babel/types": "^7.5.5", + "@babel/generator": "^7.6.0", + "@babel/helpers": "^7.6.0", + "@babel/parser": "^7.6.0", + "@babel/template": "^7.6.0", + "@babel/traverse": "^7.6.0", + "@babel/types": "^7.6.0", "convert-source-map": "^1.1.0", "debug": "^4.1.0", "json5": "^2.1.0", @@ -50,11 +55,11 @@ } }, "@babel/generator": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.5.5.tgz", - "integrity": "sha512-ETI/4vyTSxTzGnU2c49XHv2zhExkv9JHLTwDAFz85kmcwuShvYG2H08FwgIguQf4JC75CBnXAUM5PqeF4fj0nQ==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.6.0.tgz", + "integrity": "sha512-Ms8Mo7YBdMMn1BYuNtKuP/z0TgEIhbcyB8HVR6PPNYp4P61lMsABiS4A3VG1qznjXVCf3r+fVHhm4efTYVsySA==", "requires": { - "@babel/types": "^7.5.5", + "@babel/types": "^7.6.0", "jsesc": "^2.5.1", "lodash": "^4.17.13", "source-map": "^0.5.0", @@ -257,13 +262,13 @@ } }, "@babel/helpers": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.5.5.tgz", - "integrity": "sha512-nRq2BUhxZFnfEn/ciJuhklHvFOqjJUD5wpx+1bxUF2axL9C+v4DE/dmp5sT2dKnpOs4orZWzpAZqlCy8QqE/7g==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.6.0.tgz", + "integrity": "sha512-W9kao7OBleOjfXtFGgArGRX6eCP0UEcA2ZWEWNkJdRZnHhW4eEbeswbG3EwaRsnQUAEGWYgMq1HsIXuNNNy2eQ==", "requires": { - "@babel/template": "^7.4.4", - "@babel/traverse": "^7.5.5", - "@babel/types": "^7.5.5" + "@babel/template": "^7.6.0", + "@babel/traverse": "^7.6.0", + "@babel/types": "^7.6.0" } }, "@babel/highlight": { @@ -277,9 +282,9 @@ } }, "@babel/parser": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.5.5.tgz", - "integrity": "sha512-E5BN68cqR7dhKan1SfqgPGhQ178bkVKpXTPEXnFJBrEt8/DKRZlybmy+IgYLTeN7tp1R5Ccmbm2rBk17sHYU3g==" + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.6.0.tgz", + "integrity": "sha512-+o2q111WEx4srBs7L9eJmcwi655eD8sXniLqMB93TBK9GrNzGrxDWSjiqz2hLU0Ha8MTXFIP0yd9fNdP+m43ZQ==" }, "@babel/plugin-proposal-async-generator-functions": { "version": "7.2.0", @@ -445,9 +450,9 @@ } }, "@babel/plugin-transform-block-scoping": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.5.5.tgz", - "integrity": "sha512-82A3CLRRdYubkG85lKwhZB0WZoHxLGsJdux/cOVaJCJpvYFl1LVzAIFyRsa7CvXqW8rBM4Zf3Bfn8PHt5DP0Sg==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.6.0.tgz", + "integrity": "sha512-tIt4E23+kw6TgL/edACZwP1OUKrjOTyMrFMLoT5IOFrfMRabCgekjqFd5o6PaAMildBu46oFkekIdMuGkkPEpA==", "requires": { "@babel/helper-plugin-utils": "^7.0.0", "lodash": "^4.17.13" @@ -477,9 +482,9 @@ } }, "@babel/plugin-transform-destructuring": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.5.0.tgz", - "integrity": "sha512-YbYgbd3TryYYLGyC7ZR+Tq8H/+bCmwoaxHfJHupom5ECstzbRLTch6gOQbhEY9Z4hiCNHEURgq06ykFv9JZ/QQ==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.6.0.tgz", + "integrity": "sha512-2bGIS5P1v4+sWTCnKNDZDxbGvEqi0ijeqM/YqHtVGrvG2y0ySgnEEhXErvE9dA0bnIzY9bIzdFK0jFA46ASIIQ==", "requires": { "@babel/helper-plugin-utils": "^7.0.0" } @@ -574,9 +579,9 @@ } }, "@babel/plugin-transform-modules-commonjs": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.5.0.tgz", - "integrity": "sha512-xmHq0B+ytyrWJvQTc5OWAC4ii6Dhr0s22STOoydokG51JjWhyYo5mRPXoi+ZmtHQhZZwuXNN+GG5jy5UZZJxIQ==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.6.0.tgz", + "integrity": "sha512-Ma93Ix95PNSEngqomy5LSBMAQvYKVe3dy+JlVJSHEXZR5ASL9lQBedMiCyVtmTLraIDVRE3ZjTZvmXXD2Ozw3g==", "requires": { "@babel/helper-module-transforms": "^7.4.4", "@babel/helper-plugin-utils": "^7.0.0", @@ -624,11 +629,11 @@ } }, "@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.4.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.4.5.tgz", - "integrity": "sha512-z7+2IsWafTBbjNsOxU/Iv5CvTJlr5w4+HGu1HovKYTtgJ362f7kBcQglkfmlspKKZ3bgrbSGvLfNx++ZJgCWsg==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.6.0.tgz", + "integrity": "sha512-jem7uytlmrRl3iCAuQyw8BpB4c4LWvSpvIeXKpMb+7j84lkx4m4mYr5ErAcmN5KM7B6BqrAvRGjBIbbzqCczew==", "requires": { - "regexp-tree": "^0.1.6" + "regexp-tree": "^0.1.13" } }, "@babel/plugin-transform-new-target": { @@ -788,21 +793,6 @@ "@babel/helper-create-class-features-plugin": "^7.6.0", "@babel/helper-plugin-utils": "^7.0.0", "@babel/plugin-syntax-typescript": "^7.2.0" - }, - "dependencies": { - "@babel/helper-create-class-features-plugin": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.6.0.tgz", - "integrity": "sha512-O1QWBko4fzGju6VoVvrZg0RROCVifcLxiApnGP3OWfWzvxRZFCoBD81K5ur5e3bVY2Vf/5rIJm8cqPKn8HUJng==", - "requires": { - "@babel/helper-function-name": "^7.1.0", - "@babel/helper-member-expression-to-functions": "^7.5.5", - "@babel/helper-optimise-call-expression": "^7.0.0", - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/helper-replace-supers": "^7.5.5", - "@babel/helper-split-export-declaration": "^7.4.4" - } - } } }, "@babel/plugin-transform-unicode-regex": { @@ -825,9 +815,9 @@ } }, "@babel/preset-env": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.5.5.tgz", - "integrity": "sha512-GMZQka/+INwsMz1A5UEql8tG015h5j/qjptpKY2gJ7giy8ohzU710YciJB5rcKsWGWHiW3RUnHib0E5/m3Tp3A==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.6.0.tgz", + "integrity": "sha512-1efzxFv/TcPsNXlRhMzRnkBFMeIqBBgzwmZwlFDw5Ubj0AGLeufxugirwZmkkX/ayi3owsSqoQ4fw8LkfK9SYg==", "requires": { "@babel/helper-module-imports": "^7.0.0", "@babel/helper-plugin-utils": "^7.0.0", @@ -845,10 +835,10 @@ "@babel/plugin-transform-arrow-functions": "^7.2.0", "@babel/plugin-transform-async-to-generator": "^7.5.0", "@babel/plugin-transform-block-scoped-functions": "^7.2.0", - "@babel/plugin-transform-block-scoping": "^7.5.5", + "@babel/plugin-transform-block-scoping": "^7.6.0", "@babel/plugin-transform-classes": "^7.5.5", "@babel/plugin-transform-computed-properties": "^7.2.0", - "@babel/plugin-transform-destructuring": "^7.5.0", + "@babel/plugin-transform-destructuring": "^7.6.0", "@babel/plugin-transform-dotall-regex": "^7.4.4", "@babel/plugin-transform-duplicate-keys": "^7.5.0", "@babel/plugin-transform-exponentiation-operator": "^7.2.0", @@ -857,10 +847,10 @@ "@babel/plugin-transform-literals": "^7.2.0", "@babel/plugin-transform-member-expression-literals": "^7.2.0", "@babel/plugin-transform-modules-amd": "^7.5.0", - "@babel/plugin-transform-modules-commonjs": "^7.5.0", + "@babel/plugin-transform-modules-commonjs": "^7.6.0", "@babel/plugin-transform-modules-systemjs": "^7.5.0", "@babel/plugin-transform-modules-umd": "^7.2.0", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.4.5", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.6.0", "@babel/plugin-transform-new-target": "^7.4.4", "@babel/plugin-transform-object-super": "^7.5.5", "@babel/plugin-transform-parameters": "^7.4.4", @@ -873,7 +863,7 @@ "@babel/plugin-transform-template-literals": "^7.4.4", "@babel/plugin-transform-typeof-symbol": "^7.2.0", "@babel/plugin-transform-unicode-regex": "^7.4.4", - "@babel/types": "^7.5.5", + "@babel/types": "^7.6.0", "browserslist": "^4.6.0", "core-js-compat": "^3.1.1", "invariant": "^2.2.2", @@ -882,13 +872,13 @@ }, "dependencies": { "browserslist": { - "version": "4.6.6", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.6.6.tgz", - "integrity": "sha512-D2Nk3W9JL9Fp/gIcWei8LrERCS+eXu9AM5cfXA8WEZ84lFks+ARnZ0q/R69m2SV3Wjma83QDDPxsNKXUwdIsyA==", + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.7.0.tgz", + "integrity": "sha512-9rGNDtnj+HaahxiVV38Gn8n8Lr8REKsel68v1sPFfIGEK6uSXTY3h9acgiT1dZVtOOUtifo/Dn8daDQ5dUgVsA==", "requires": { - "caniuse-lite": "^1.0.30000984", - "electron-to-chromium": "^1.3.191", - "node-releases": "^1.1.25" + "caniuse-lite": "^1.0.30000989", + "electron-to-chromium": "^1.3.247", + "node-releases": "^1.1.29" } } } @@ -915,34 +905,34 @@ } }, "@babel/runtime": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.5.5.tgz", - "integrity": "sha512-28QvEGyQyNkB0/m2B4FU7IEZGK2NUrcMtT6BZEFALTguLk+AUT6ofsHtPk5QyjAdUkpMJ+/Em+quwz4HOt30AQ==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.6.0.tgz", + "integrity": "sha512-89eSBLJsxNxOERC0Op4vd+0Bqm6wRMqMbFtV3i0/fbaWw/mJ8Q3eBvgX0G4SyrOOLCtbu98HspF8o09MRT+KzQ==", "requires": { "regenerator-runtime": "^0.13.2" } }, "@babel/template": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.4.4.tgz", - "integrity": "sha512-CiGzLN9KgAvgZsnivND7rkA+AeJ9JB0ciPOD4U59GKbQP2iQl+olF1l76kJOupqidozfZ32ghwBEJDhnk9MEcw==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.6.0.tgz", + "integrity": "sha512-5AEH2EXD8euCk446b7edmgFdub/qfH1SN6Nii3+fyXP807QRx9Q73A2N5hNwRRslC2H9sNzaFhsPubkS4L8oNQ==", "requires": { "@babel/code-frame": "^7.0.0", - "@babel/parser": "^7.4.4", - "@babel/types": "^7.4.4" + "@babel/parser": "^7.6.0", + "@babel/types": "^7.6.0" } }, "@babel/traverse": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.5.5.tgz", - "integrity": "sha512-MqB0782whsfffYfSjH4TM+LMjrJnhCNEDMDIjeTpl+ASaUvxcjoiVCo/sM1GhS1pHOXYfWVCYneLjMckuUxDaQ==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.6.0.tgz", + "integrity": "sha512-93t52SaOBgml/xY74lsmt7xOR4ufYvhb5c5qiM6lu4J/dWGMAfAh6eKw4PjLes6DI6nQgearoxnFJk60YchpvQ==", "requires": { "@babel/code-frame": "^7.5.5", - "@babel/generator": "^7.5.5", + "@babel/generator": "^7.6.0", "@babel/helper-function-name": "^7.1.0", "@babel/helper-split-export-declaration": "^7.4.4", - "@babel/parser": "^7.5.5", - "@babel/types": "^7.5.5", + "@babel/parser": "^7.6.0", + "@babel/types": "^7.6.0", "debug": "^4.1.0", "globals": "^11.1.0", "lodash": "^4.17.13" @@ -959,9 +949,9 @@ } }, "@babel/types": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.5.5.tgz", - "integrity": "sha512-s63F9nJioLqOlW3UkyMd+BYhXt44YuaFm/VV0VwuteqjYwRrObkU7ra9pY4wAJR3oXi8hJrMcrcJdO/HH33vtw==", + "version": "7.6.1", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.6.1.tgz", + "integrity": "sha512-X7gdiuaCmA0uRjCmRtYJNAVCc/q+5xSgsfKJHqMN4iNLILX39677fJE1O40arPMh0TTtS9ItH67yre6c7k6t0g==", "requires": { "esutils": "^2.0.2", "lodash": "^4.17.13", @@ -988,14 +978,14 @@ } }, "@emotion/cache": { - "version": "10.0.17", - "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-10.0.17.tgz", - "integrity": "sha512-442/miwbuwIDfSzfMqZNxuzxSEbskcz/bZ86QBYzEjFrr/oq9w+y5kJY1BHbGhDtr91GO232PZ5NN9XYMwr/Qg==", + "version": "10.0.19", + "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-10.0.19.tgz", + "integrity": "sha512-BoiLlk4vEsGBg2dAqGSJu0vJl/PgVtCYLBFJaEO8RmQzPugXewQCXZJNXTDFaRlfCs0W+quesayav4fvaif5WQ==", "requires": { "@emotion/sheet": "0.9.3", "@emotion/stylis": "0.8.4", "@emotion/utils": "0.11.2", - "@emotion/weak-memoize": "0.2.3" + "@emotion/weak-memoize": "0.2.4" } }, "@emotion/core": { @@ -1022,30 +1012,30 @@ } }, "@emotion/hash": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.7.2.tgz", - "integrity": "sha512-RMtr1i6E8MXaBWwhXL3yeOU8JXRnz8GNxHvaUfVvwxokvayUY0zoBeWbKw1S9XkufmGEEdQd228pSZXFkAln8Q==" + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.7.3.tgz", + "integrity": "sha512-14ZVlsB9akwvydAdaEnVnvqu6J2P6ySv39hYyl/aoB6w/V+bXX0tay8cF6paqbgZsN2n5Xh15uF4pE+GvE+itw==" }, "@emotion/is-prop-valid": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.8.2.tgz", - "integrity": "sha512-ZQIMAA2kLUWiUeMZNJDTeCwYRx1l8SQL0kHktze4COT22occKpDML1GDUXP5/sxhOMrZO8vZw773ni4H5Snrsg==", + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.8.3.tgz", + "integrity": "sha512-We7VBiltAJ70KQA0dWkdPMXnYoizlxOXpvtjmu5/MBnExd+u0PGgV27WCYanmLAbCwAU30Le/xA0CQs/F/Otig==", "requires": { - "@emotion/memoize": "0.7.2" + "@emotion/memoize": "0.7.3" } }, "@emotion/memoize": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.2.tgz", - "integrity": "sha512-hnHhwQzvPCW1QjBWFyBtsETdllOM92BfrKWbUTmh9aeOlcVOiXvlPsK4104xH8NsaKfg86PTFsWkueQeUfMA/w==" + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.3.tgz", + "integrity": "sha512-2Md9mH6mvo+ygq1trTeVp2uzAKwE2P7In0cRpD/M9Q70aH8L+rxMLbb3JCN2JoSWsV2O+DdFjfbbXoMoLBczow==" }, "@emotion/serialize": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-0.11.10.tgz", - "integrity": "sha512-04AB+wU00vv9jLgkWn13c/GJg2yXp3w7ZR3Q1O6mBSE6mbUmYeNX3OpBhfp//6r47lFyY0hBJJue+bA30iokHQ==", + "version": "0.11.11", + "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-0.11.11.tgz", + "integrity": "sha512-YG8wdCqoWtuoMxhHZCTA+egL0RSGdHEc+YCsmiSBPBEDNuVeMWtjEWtGrhUterSChxzwnWBXvzSxIFQI/3sHLw==", "requires": { - "@emotion/hash": "0.7.2", - "@emotion/memoize": "0.7.2", + "@emotion/hash": "0.7.3", + "@emotion/memoize": "0.7.3", "@emotion/unitless": "0.7.4", "@emotion/utils": "0.11.2", "csstype": "^2.5.7" @@ -1066,13 +1056,13 @@ } }, "@emotion/styled-base": { - "version": "10.0.17", - "resolved": "https://registry.npmjs.org/@emotion/styled-base/-/styled-base-10.0.17.tgz", - "integrity": "sha512-vqQvxluZZKPByAB4zYZys0Qo/kVDP/03hAeg1K+TYpnZRwTi7WteOodc+/5669RPVNcfb93fphQpM5BYJnI1/g==", + "version": "10.0.19", + "resolved": "https://registry.npmjs.org/@emotion/styled-base/-/styled-base-10.0.19.tgz", + "integrity": "sha512-Sz6GBHTbOZoeZQKvkE9gQPzaJ6/qtoQ/OPvyG2Z/6NILlYk60Es1cEcTgTkm26H8y7A0GSgp4UmXl+srvsnFPg==", "requires": { "@babel/runtime": "^7.5.5", - "@emotion/is-prop-valid": "0.8.2", - "@emotion/serialize": "^0.11.10", + "@emotion/is-prop-valid": "0.8.3", + "@emotion/serialize": "^0.11.11", "@emotion/utils": "0.11.2" } }, @@ -1092,9 +1082,9 @@ "integrity": "sha512-UHX2XklLl3sIaP6oiMmlVzT0J+2ATTVpf0dHQVyPJHTkOITvXfaSqnRk6mdDhV9pR8T/tHc3cex78IKXssmzrA==" }, "@emotion/weak-memoize": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.2.3.tgz", - "integrity": "sha512-zVgvPwGK7c1aVdUVc9Qv7SqepOGRDrqCw7KZPSZziWGxSlbII3gmvGLPzLX4d0n0BMbamBacUrN22zOMyFFEkQ==" + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.2.4.tgz", + "integrity": "sha512-6PYY5DVdAY1ifaQW6XYTnOMihmBVT27elqSjEoodchsGjzYlEsTQMcEhSud99kVawatyTZRTiVkJ/c6lwbQ7nA==" }, "@gatsbyjs/relay-compiler": { "version": "2.0.0-printer-fix.4", @@ -1120,9 +1110,9 @@ } }, "@hapi/address": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@hapi/address/-/address-2.1.1.tgz", - "integrity": "sha512-DYuHzu978pP1XW1GD3HGvLnAFjbQTIgc2+V153FGkbS2pgo9haigCdwBnUDrbhaOkgiJlbZvoEqDrcxSLHpiWA==" + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@hapi/address/-/address-2.1.2.tgz", + "integrity": "sha512-O4QDrx+JoGKZc6aN64L04vqa7e41tIiLU+OvKdcYaEMP97UttL0f9GIi9/0A4WAMx0uBd6SidDIhktZhgOcN8Q==" }, "@hapi/bourne": { "version": "1.3.2", @@ -1146,9 +1136,9 @@ } }, "@hapi/topo": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-3.1.3.tgz", - "integrity": "sha512-JmS9/vQK6dcUYn7wc2YZTqzIKubAQcJKu2KCKAru6es482U5RT5fP1EXCPtlXpiK7PR0On/kpQKI4fRKkzpZBQ==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-3.1.4.tgz", + "integrity": "sha512-aVWQTOI9wBD6zawmOr6f+tdEIxQC8JXfQVLTjgGe8YEStAWGn/GNNVTobKJhbWKveQj2RyYF3oYbO9SC8/eOCA==", "requires": { "@hapi/hoek": "8.x.x" } @@ -1194,100 +1184,6 @@ "unified": "8.3.2", "unist-builder": "1.0.4", "unist-util-visit": "2.0.0" - }, - "dependencies": { - "@babel/core": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.6.0.tgz", - "integrity": "sha512-FuRhDRtsd6IptKpHXAa+4WPZYY2ZzgowkbLBecEDDSje1X/apG7jQM33or3NdOmjXBKWGOg4JmSiRfUfuTtHXw==", - "requires": { - "@babel/code-frame": "^7.5.5", - "@babel/generator": "^7.6.0", - "@babel/helpers": "^7.6.0", - "@babel/parser": "^7.6.0", - "@babel/template": "^7.6.0", - "@babel/traverse": "^7.6.0", - "@babel/types": "^7.6.0", - "convert-source-map": "^1.1.0", - "debug": "^4.1.0", - "json5": "^2.1.0", - "lodash": "^4.17.13", - "resolve": "^1.3.2", - "semver": "^5.4.1", - "source-map": "^0.5.0" - } - }, - "@babel/generator": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.6.0.tgz", - "integrity": "sha512-Ms8Mo7YBdMMn1BYuNtKuP/z0TgEIhbcyB8HVR6PPNYp4P61lMsABiS4A3VG1qznjXVCf3r+fVHhm4efTYVsySA==", - "requires": { - "@babel/types": "^7.6.0", - "jsesc": "^2.5.1", - "lodash": "^4.17.13", - "source-map": "^0.5.0", - "trim-right": "^1.0.1" - } - }, - "@babel/helpers": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.6.0.tgz", - "integrity": "sha512-W9kao7OBleOjfXtFGgArGRX6eCP0UEcA2ZWEWNkJdRZnHhW4eEbeswbG3EwaRsnQUAEGWYgMq1HsIXuNNNy2eQ==", - "requires": { - "@babel/template": "^7.6.0", - "@babel/traverse": "^7.6.0", - "@babel/types": "^7.6.0" - } - }, - "@babel/parser": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.6.0.tgz", - "integrity": "sha512-+o2q111WEx4srBs7L9eJmcwi655eD8sXniLqMB93TBK9GrNzGrxDWSjiqz2hLU0Ha8MTXFIP0yd9fNdP+m43ZQ==" - }, - "@babel/template": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.6.0.tgz", - "integrity": "sha512-5AEH2EXD8euCk446b7edmgFdub/qfH1SN6Nii3+fyXP807QRx9Q73A2N5hNwRRslC2H9sNzaFhsPubkS4L8oNQ==", - "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/parser": "^7.6.0", - "@babel/types": "^7.6.0" - } - }, - "@babel/traverse": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.6.0.tgz", - "integrity": "sha512-93t52SaOBgml/xY74lsmt7xOR4ufYvhb5c5qiM6lu4J/dWGMAfAh6eKw4PjLes6DI6nQgearoxnFJk60YchpvQ==", - "requires": { - "@babel/code-frame": "^7.5.5", - "@babel/generator": "^7.6.0", - "@babel/helper-function-name": "^7.1.0", - "@babel/helper-split-export-declaration": "^7.4.4", - "@babel/parser": "^7.6.0", - "@babel/types": "^7.6.0", - "debug": "^4.1.0", - "globals": "^11.1.0", - "lodash": "^4.17.13" - } - }, - "@babel/types": { - "version": "7.6.1", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.6.1.tgz", - "integrity": "sha512-X7gdiuaCmA0uRjCmRtYJNAVCc/q+5xSgsfKJHqMN4iNLILX39677fJE1O40arPMh0TTtS9ItH67yre6c7k6t0g==", - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.13", - "to-fast-properties": "^2.0.0" - } - }, - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "requires": { - "ms": "^2.1.1" - } - } } }, "@mdx-js/react": { @@ -1488,6 +1384,11 @@ "loader-utils": "^1.2.3" } }, + "@types/classnames": { + "version": "2.2.9", + "resolved": "https://registry.npmjs.org/@types/classnames/-/classnames-2.2.9.tgz", + "integrity": "sha512-MNl+rT5UmZeilaPxAVs6YaPC2m6aA8rofviZbhbxpPpl61uKodfdQVsBtgJGTqGizEf02oW3tsVe7FYB8kK14A==" + }, "@types/configstore": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/@types/configstore/-/configstore-2.1.1.tgz", @@ -1544,9 +1445,9 @@ "integrity": "sha1-fyrX7FX5FEgvybHsS7GuYCjUYGY=" }, "@types/node": { - "version": "12.7.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-12.7.2.tgz", - "integrity": "sha512-dyYO+f6ihZEtNPDcWNR1fkoTDf3zAK3lAABDze3mz6POyIercH0lEUawUFXlG8xaQZmm1yEBON/4TsYv/laDYg==" + "version": "12.7.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.7.5.tgz", + "integrity": "sha512-9fq4jZVhPNW8r+UYKnxF1e2HkDWOWKM5bC2/7c9wPV835I0aOrVbS/Hw/pWPk2uKrNXQqg9Z959Kz+IYDd5p3w==" }, "@types/prop-types": { "version": "15.7.2", @@ -1559,9 +1460,9 @@ "integrity": "sha512-ce5d3q03Ex0sy4R14722Rmt6MT07Ua+k4FwDfdcToYJcMKNtRVQvJ6JCAPdAmAnbRb6CsX6aYb9m96NGod9uTw==" }, "@types/reach__router": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@types/reach__router/-/reach__router-1.2.4.tgz", - "integrity": "sha512-a+MFhebeSGi0LwHZ0UhH/ke77rWtNQnt8YmaHnquSaY3HmyEi+BPQi3GhPcUPnC9X5BLw/qORw3BPxGb1mCtEw==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@types/reach__router/-/reach__router-1.2.5.tgz", + "integrity": "sha512-Lna9cD38dN3deqJ6ThZgMKoAzW1LE3u+uUbPGdHUqquoM/fnZitSV1xfJxHjovu4SsNkpN9udkte3wEyrBPawQ==", "requires": { "@types/history": "*", "@types/react": "*" @@ -1576,6 +1477,11 @@ "csstype": "^2.2.0" } }, + "@types/tinycolor2": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@types/tinycolor2/-/tinycolor2-1.4.2.tgz", + "integrity": "sha512-PeHg/AtdW6aaIO2a+98Xj7rWY4KC1E6yOy7AFknJQ7VXUGNrMlyxDFxJo7HqLtjQms/ZhhQX52mLVW/EX3JGOw==" + }, "@types/tmp": { "version": "0.0.32", "resolved": "https://registry.npmjs.org/@types/tmp/-/tmp-0.0.32.tgz", @@ -2197,19 +2103,6 @@ "electron-to-chromium": "^1.3.247", "node-releases": "^1.1.29" } - }, - "electron-to-chromium": { - "version": "1.3.258", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.258.tgz", - "integrity": "sha512-rkPYrgFU7k/8ngjHYvzOZ44OQQ1GeIRIQnhGv00RkSlQXEnJKsGonQppbEEWHuuxZegpMao+WZmYraWQJQJMMg==" - }, - "node-releases": { - "version": "1.1.30", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.30.tgz", - "integrity": "sha512-BHcr1g6NeUH12IL+X3Flvs4IOnl1TL0JczUhEZjDE+FXXPQcVCNr8NEPb01zqGxzhTpdyJL5GXemaCW7aw6Khw==", - "requires": { - "semver": "^5.3.0" - } } } }, @@ -2376,14 +2269,14 @@ } }, "babel-plugin-emotion": { - "version": "10.0.17", - "resolved": "https://registry.npmjs.org/babel-plugin-emotion/-/babel-plugin-emotion-10.0.17.tgz", - "integrity": "sha512-KNuBadotqYWpQexHhHOu7M9EV1j2c+Oh/JJqBfEQDusD6mnORsCZKHkl+xYwK82CPQ/23wRrsBIEYnKjtbMQJw==", + "version": "10.0.19", + "resolved": "https://registry.npmjs.org/babel-plugin-emotion/-/babel-plugin-emotion-10.0.19.tgz", + "integrity": "sha512-1pJb5uKN/gx6bi3gGr588Krj49sxARI9KmxhtMUa+NRJb6lR3OfC51mh3NlWRsOqdjWlT4cSjnZpnFq5K3T5ZA==", "requires": { "@babel/helper-module-imports": "^7.0.0", - "@emotion/hash": "0.7.2", - "@emotion/memoize": "0.7.2", - "@emotion/serialize": "^0.11.10", + "@emotion/hash": "0.7.3", + "@emotion/memoize": "0.7.3", + "@emotion/serialize": "^0.11.11", "babel-plugin-macros": "^2.0.0", "babel-plugin-syntax-jsx": "^6.18.0", "convert-source-map": "^1.5.0", @@ -2411,9 +2304,9 @@ } }, "babel-plugin-remove-graphql-queries": { - "version": "2.7.7", - "resolved": "https://registry.npmjs.org/babel-plugin-remove-graphql-queries/-/babel-plugin-remove-graphql-queries-2.7.7.tgz", - "integrity": "sha512-/bmTuUkkLtim99qe7Lj3VBTLsOX3nxeuwtkqb4q3ybuynpP2rYuLUUQ3Z5Lq+1sT63ImN49+q8EiP+BMXstEAQ==" + "version": "2.7.8", + "resolved": "https://registry.npmjs.org/babel-plugin-remove-graphql-queries/-/babel-plugin-remove-graphql-queries-2.7.8.tgz", + "integrity": "sha512-uMEVCaeDQbQBJBev8cMGHInJRGu/oMPdVbIK5sqRXpAgtdfo1WGriyb7OtIKDycSlvHx95l2NjOtwPADI9mG9w==" }, "babel-plugin-syntax-dynamic-import": { "version": "6.18.0", @@ -2470,9 +2363,9 @@ } }, "babel-preset-gatsby": { - "version": "0.2.13", - "resolved": "https://registry.npmjs.org/babel-preset-gatsby/-/babel-preset-gatsby-0.2.13.tgz", - "integrity": "sha512-iu1jj+Ja5JraFa9gtQ1m2zTSq8KowBXf94qTxAomxf2TWE4sldYPmdMofajYy52EolTRjBHRtnhjZCsaBV2D+A==", + "version": "0.2.14", + "resolved": "https://registry.npmjs.org/babel-preset-gatsby/-/babel-preset-gatsby-0.2.14.tgz", + "integrity": "sha512-D/zj3RNm52j6k2mzmoF2YxTKGaDs0lhp6v1l3WCDne1kmv9PIb/R8GovcsFvqXVF1FJ0ajNZvyhsJK/eqyGfHQ==", "requires": { "@babel/plugin-proposal-class-properties": "^7.5.5", "@babel/plugin-syntax-dynamic-import": "^7.2.0", @@ -2484,157 +2377,6 @@ "babel-plugin-dynamic-import-node": "^1.2.0", "babel-plugin-macros": "^2.6.1", "babel-plugin-transform-react-remove-prop-types": "^0.4.24" - }, - "dependencies": { - "@babel/plugin-transform-block-scoping": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.6.0.tgz", - "integrity": "sha512-tIt4E23+kw6TgL/edACZwP1OUKrjOTyMrFMLoT5IOFrfMRabCgekjqFd5o6PaAMildBu46oFkekIdMuGkkPEpA==", - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "lodash": "^4.17.13" - } - }, - "@babel/plugin-transform-destructuring": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.6.0.tgz", - "integrity": "sha512-2bGIS5P1v4+sWTCnKNDZDxbGvEqi0ijeqM/YqHtVGrvG2y0ySgnEEhXErvE9dA0bnIzY9bIzdFK0jFA46ASIIQ==", - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-transform-modules-commonjs": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.6.0.tgz", - "integrity": "sha512-Ma93Ix95PNSEngqomy5LSBMAQvYKVe3dy+JlVJSHEXZR5ASL9lQBedMiCyVtmTLraIDVRE3ZjTZvmXXD2Ozw3g==", - "requires": { - "@babel/helper-module-transforms": "^7.4.4", - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/helper-simple-access": "^7.1.0", - "babel-plugin-dynamic-import-node": "^2.3.0" - }, - "dependencies": { - "babel-plugin-dynamic-import-node": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.0.tgz", - "integrity": "sha512-o6qFkpeQEBxcqt0XYlWzAVxNCSCZdUgcR8IRlhD/8DylxjjO4foPcvTW0GGKa/cVt3rvxZ7o5ippJ+/0nvLhlQ==", - "requires": { - "object.assign": "^4.1.0" - } - } - } - }, - "@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.6.0.tgz", - "integrity": "sha512-jem7uytlmrRl3iCAuQyw8BpB4c4LWvSpvIeXKpMb+7j84lkx4m4mYr5ErAcmN5KM7B6BqrAvRGjBIbbzqCczew==", - "requires": { - "regexp-tree": "^0.1.13" - } - }, - "@babel/preset-env": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.6.0.tgz", - "integrity": "sha512-1efzxFv/TcPsNXlRhMzRnkBFMeIqBBgzwmZwlFDw5Ubj0AGLeufxugirwZmkkX/ayi3owsSqoQ4fw8LkfK9SYg==", - "requires": { - "@babel/helper-module-imports": "^7.0.0", - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-proposal-async-generator-functions": "^7.2.0", - "@babel/plugin-proposal-dynamic-import": "^7.5.0", - "@babel/plugin-proposal-json-strings": "^7.2.0", - "@babel/plugin-proposal-object-rest-spread": "^7.5.5", - "@babel/plugin-proposal-optional-catch-binding": "^7.2.0", - "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", - "@babel/plugin-syntax-async-generators": "^7.2.0", - "@babel/plugin-syntax-dynamic-import": "^7.2.0", - "@babel/plugin-syntax-json-strings": "^7.2.0", - "@babel/plugin-syntax-object-rest-spread": "^7.2.0", - "@babel/plugin-syntax-optional-catch-binding": "^7.2.0", - "@babel/plugin-transform-arrow-functions": "^7.2.0", - "@babel/plugin-transform-async-to-generator": "^7.5.0", - "@babel/plugin-transform-block-scoped-functions": "^7.2.0", - "@babel/plugin-transform-block-scoping": "^7.6.0", - "@babel/plugin-transform-classes": "^7.5.5", - "@babel/plugin-transform-computed-properties": "^7.2.0", - "@babel/plugin-transform-destructuring": "^7.6.0", - "@babel/plugin-transform-dotall-regex": "^7.4.4", - "@babel/plugin-transform-duplicate-keys": "^7.5.0", - "@babel/plugin-transform-exponentiation-operator": "^7.2.0", - "@babel/plugin-transform-for-of": "^7.4.4", - "@babel/plugin-transform-function-name": "^7.4.4", - "@babel/plugin-transform-literals": "^7.2.0", - "@babel/plugin-transform-member-expression-literals": "^7.2.0", - "@babel/plugin-transform-modules-amd": "^7.5.0", - "@babel/plugin-transform-modules-commonjs": "^7.6.0", - "@babel/plugin-transform-modules-systemjs": "^7.5.0", - "@babel/plugin-transform-modules-umd": "^7.2.0", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.6.0", - "@babel/plugin-transform-new-target": "^7.4.4", - "@babel/plugin-transform-object-super": "^7.5.5", - "@babel/plugin-transform-parameters": "^7.4.4", - "@babel/plugin-transform-property-literals": "^7.2.0", - "@babel/plugin-transform-regenerator": "^7.4.5", - "@babel/plugin-transform-reserved-words": "^7.2.0", - "@babel/plugin-transform-shorthand-properties": "^7.2.0", - "@babel/plugin-transform-spread": "^7.2.0", - "@babel/plugin-transform-sticky-regex": "^7.2.0", - "@babel/plugin-transform-template-literals": "^7.4.4", - "@babel/plugin-transform-typeof-symbol": "^7.2.0", - "@babel/plugin-transform-unicode-regex": "^7.4.4", - "@babel/types": "^7.6.0", - "browserslist": "^4.6.0", - "core-js-compat": "^3.1.1", - "invariant": "^2.2.2", - "js-levenshtein": "^1.1.3", - "semver": "^5.5.0" - } - }, - "@babel/runtime": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.6.0.tgz", - "integrity": "sha512-89eSBLJsxNxOERC0Op4vd+0Bqm6wRMqMbFtV3i0/fbaWw/mJ8Q3eBvgX0G4SyrOOLCtbu98HspF8o09MRT+KzQ==", - "requires": { - "regenerator-runtime": "^0.13.2" - } - }, - "@babel/types": { - "version": "7.6.1", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.6.1.tgz", - "integrity": "sha512-X7gdiuaCmA0uRjCmRtYJNAVCc/q+5xSgsfKJHqMN4iNLILX39677fJE1O40arPMh0TTtS9ItH67yre6c7k6t0g==", - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.13", - "to-fast-properties": "^2.0.0" - } - }, - "browserslist": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.7.0.tgz", - "integrity": "sha512-9rGNDtnj+HaahxiVV38Gn8n8Lr8REKsel68v1sPFfIGEK6uSXTY3h9acgiT1dZVtOOUtifo/Dn8daDQ5dUgVsA==", - "requires": { - "caniuse-lite": "^1.0.30000989", - "electron-to-chromium": "^1.3.247", - "node-releases": "^1.1.29" - } - }, - "electron-to-chromium": { - "version": "1.3.258", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.258.tgz", - "integrity": "sha512-rkPYrgFU7k/8ngjHYvzOZ44OQQ1GeIRIQnhGv00RkSlQXEnJKsGonQppbEEWHuuxZegpMao+WZmYraWQJQJMMg==" - }, - "node-releases": { - "version": "1.1.30", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.30.tgz", - "integrity": "sha512-BHcr1g6NeUH12IL+X3Flvs4IOnl1TL0JczUhEZjDE+FXXPQcVCNr8NEPb01zqGxzhTpdyJL5GXemaCW7aw6Khw==", - "requires": { - "semver": "^5.3.0" - } - }, - "regexp-tree": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.13.tgz", - "integrity": "sha512-hwdV/GQY5F8ReLZWO+W1SRoN5YfpOKY6852+tBFcma72DKBIcHjPRIlIvQN35bCOljuAfP2G2iB0FC/w236mUw==" - } } }, "babel-runtime": { @@ -2738,9 +2480,9 @@ "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==" }, "base64id": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/base64id/-/base64id-1.0.0.tgz", - "integrity": "sha1-R2iMuZu2gE8OBtPnY7HDLlfY5rY=" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", + "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==" }, "batch": { "version": "0.6.1", @@ -3311,19 +3053,6 @@ "electron-to-chromium": "^1.3.247", "node-releases": "^1.1.29" } - }, - "electron-to-chromium": { - "version": "1.3.258", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.258.tgz", - "integrity": "sha512-rkPYrgFU7k/8ngjHYvzOZ44OQQ1GeIRIQnhGv00RkSlQXEnJKsGonQppbEEWHuuxZegpMao+WZmYraWQJQJMMg==" - }, - "node-releases": { - "version": "1.1.30", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.30.tgz", - "integrity": "sha512-BHcr1g6NeUH12IL+X3Flvs4IOnl1TL0JczUhEZjDE+FXXPQcVCNr8NEPb01zqGxzhTpdyJL5GXemaCW7aw6Khw==", - "requires": { - "semver": "^5.3.0" - } } } }, @@ -3457,11 +3186,11 @@ } }, "chokidar": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.0.2.tgz", - "integrity": "sha512-c4PR2egjNjI1um6bamCQ6bUNPDiyofNQruHvKgHQ4gDUP/ITSVSzNsiI5OWtHOsX323i5ha/kk4YmOZ1Ktg7KA==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.1.1.tgz", + "integrity": "sha512-df4o16uZmMHzVQwECZRHwfguOt5ixpuQVaZHjYMvYisgKhE+JXwcj/Tcr3+3bu/XeOJQ9ycYmzu7Mv8XrGxJDQ==", "requires": { - "anymatch": "^3.0.1", + "anymatch": "^3.1.0", "braces": "^3.0.2", "fsevents": "^2.0.6", "glob-parent": "^5.0.0", @@ -3472,9 +3201,9 @@ }, "dependencies": { "anymatch": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.0.3.tgz", - "integrity": "sha512-c6IvoeBECQlMVuYUjSwimnhmztImpErfxJzWZhIQinIvQWoGOnB0dLIgifbPHQt5heS6mNlaZG16f06H3C8t1g==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.0.tgz", + "integrity": "sha512-Ozz7l4ixzI7Oxj2+cw+p0tVUt27BpaJ+1+q1TCeANWxHpvyn2+Un+YamBdfKu0uh8xLodGhoa1v7595NhKDAuA==", "requires": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" @@ -3508,9 +3237,9 @@ "optional": true }, "glob-parent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.0.0.tgz", - "integrity": "sha512-Z2RwiujPRGluePM6j699ktJYxmPpJKCfpGA13jz2hmFZC7gKetzrWvg5KN3+OsIFmydGyZ1AVwERCq1w/ZZwRg==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.0.tgz", + "integrity": "sha512-qjtRgnIVmOfnKUE3NJAQEdk+lKrxfw8t5ke7SXtfMTHcjsBfOfWXCQfdb30zfDoZQ2IRSIiidmjtbHZPZ++Ihw==", "requires": { "is-glob": "^4.0.1" } @@ -3599,6 +3328,11 @@ } } }, + "classnames": { + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.2.6.tgz", + "integrity": "sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q==" + }, "clean-stack": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", @@ -3839,9 +3573,9 @@ } }, "colors": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.3.3.tgz", - "integrity": "sha512-mmGt/1pZqYRjMxB1axhTo16/snVZ5krrKkcmMeVKxzECMMXoCgnvTPp10QgHfcbQZw8Dq2jMNG6je4JlWU0gWg==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", + "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", "optional": true }, "combined-stream": { @@ -3974,9 +3708,9 @@ } }, "confusing-browser-globals": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.8.tgz", - "integrity": "sha512-lI7asCibVJ6Qd3FGU7mu4sfG4try4LX3+GVS+Gv8UlrEf2AeW57piecapnog2UHZSbcX/P/1UDWVaTsblowlZg==" + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.9.tgz", + "integrity": "sha512-KbS1Y0jMtyPgIxjO7ZzMAuUpAKMt1SzCL9fsrKsX6b0zJPTaT0SiSPmewwVZg9UAO83HVIlEhZF84LIjZ0lmAw==" }, "connect-history-api-fallback": { "version": "1.6.0", @@ -4110,13 +3844,13 @@ }, "dependencies": { "browserslist": { - "version": "4.6.6", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.6.6.tgz", - "integrity": "sha512-D2Nk3W9JL9Fp/gIcWei8LrERCS+eXu9AM5cfXA8WEZ84lFks+ARnZ0q/R69m2SV3Wjma83QDDPxsNKXUwdIsyA==", + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.7.0.tgz", + "integrity": "sha512-9rGNDtnj+HaahxiVV38Gn8n8Lr8REKsel68v1sPFfIGEK6uSXTY3h9acgiT1dZVtOOUtifo/Dn8daDQ5dUgVsA==", "requires": { - "caniuse-lite": "^1.0.30000984", - "electron-to-chromium": "^1.3.191", - "node-releases": "^1.1.25" + "caniuse-lite": "^1.0.30000989", + "electron-to-chromium": "^1.3.247", + "node-releases": "^1.1.29" } }, "semver": { @@ -5038,9 +4772,9 @@ "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" }, "electron-to-chromium": { - "version": "1.3.241", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.241.tgz", - "integrity": "sha512-Gb9E6nWZlbgjDDNe5cAvMJixtn79krNJ70EDpq/M10lkGo7PGtBUe7Y0CYVHsBScRwi6ybCS+YetXAN9ysAHDg==" + "version": "1.3.264", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.264.tgz", + "integrity": "sha512-z8E7WkrrquCuGYv+kKyybuZIbdms+4PeHp7Zm2uIgEhAigP0bOwqXILItwj0YO73o+QyHY/7XtEfP5DsHOWQgQ==" }, "elliptic": { "version": "6.5.1", @@ -5088,16 +4822,16 @@ } }, "engine.io": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.3.2.tgz", - "integrity": "sha512-AsaA9KG7cWPXWHp5FvHdDWY3AMWeZ8x+2pUVLcn71qE5AtAzgGbxuclOytygskw8XGmiQafTmnI9Bix3uihu2w==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.4.0.tgz", + "integrity": "sha512-XCyYVWzcHnK5cMz7G4VTu2W7zJS7SM1QkcelghyIk/FmobWBtXE7fwhBusEKvCSqc3bMh8fNFMlUkCKTFRxH2w==", "requires": { "accepts": "~1.3.4", - "base64id": "1.0.0", + "base64id": "2.0.0", "cookie": "0.3.1", - "debug": "~3.1.0", - "engine.io-parser": "~2.1.0", - "ws": "~6.1.0" + "debug": "~4.1.0", + "engine.io-parser": "~2.2.0", + "ws": "^7.1.2" }, "dependencies": { "cookie": { @@ -5106,29 +4840,24 @@ "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" }, "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", "requires": { - "ms": "2.0.0" + "ms": "^2.1.1" } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" } } }, "engine.io-client": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.3.2.tgz", - "integrity": "sha512-y0CPINnhMvPuwtqXfsGuWE8BB66+B6wTtCofQDRecMQPYX3MYUZXFNKDhdrSe3EVjgOu4V3rxdeqN/Tr91IgbQ==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.4.0.tgz", + "integrity": "sha512-a4J5QO2k99CM2a0b12IznnyQndoEvtA4UAldhGzKqnHf42I3Qs2W5SPnDvatZRcMaNZs4IevVicBPayxYt6FwA==", "requires": { "component-emitter": "1.2.1", "component-inherit": "0.0.3", - "debug": "~3.1.0", - "engine.io-parser": "~2.1.1", + "debug": "~4.1.0", + "engine.io-parser": "~2.2.0", "has-cors": "1.1.0", "indexof": "0.0.1", "parseqs": "0.0.5", @@ -5144,24 +4873,27 @@ "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=" }, "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", "requires": { - "ms": "2.0.0" + "ms": "^2.1.1" } }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + "ws": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.1.4.tgz", + "integrity": "sha512-eqZfL+NE/YQc1/ZynhojeV8q+H050oR8AZ2uIev7RU10svA9ZnJUddHcOUZTJLinZ9yEfdA2kSATS2qZK5fhJA==", + "requires": { + "async-limiter": "~1.0.0" + } } } }, "engine.io-parser": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.1.3.tgz", - "integrity": "sha512-6HXPre2O4Houl7c4g7Ic/XzPnHBvaEmN90vtRO9uLmwtRqQmTOw0QMevL1TOfL2Cpu1VzsaTmMotQgMdkzGkVA==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.2.0.tgz", + "integrity": "sha512-6I3qD9iUxotsC5HEMuuGsKA0cXerGz+4uGcXQEkfBidgKf0amsjrrtwcbwK/nzpZBxclXlV7gGl9dgWvu4LF6w==", "requires": { "after": "0.8.2", "arraybuffer.slice": "~0.0.7", @@ -5212,24 +4944,28 @@ } }, "error-stack-parser": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.0.3.tgz", - "integrity": "sha512-vRC4rKv87twMZy92X4+TmUdv3iYMsmePbpG/YguHsfzmZ8bYJZYYep7yrXH09yFUaCEPKgNK5X79+Yq7hwLVOA==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.0.4.tgz", + "integrity": "sha512-fZ0KkoxSjLFmhW5lHbUT3tLwy3nX1qEzMYo8koY1vrsAco53CMT1djnBSeC/wUjTEZRhZl9iRw7PaMaxfJ4wzQ==", "requires": { - "stackframe": "^1.0.4" + "stackframe": "^1.1.0" } }, "es-abstract": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.13.0.tgz", - "integrity": "sha512-vDZfg/ykNxQVwup/8E1BZhVzFfBxs9NqMzGcvIJrqg5k2/5Za2bWo40dK2J1pgLngZ7c+Shh8lwYtLGyrwPutg==", + "version": "1.14.2", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.14.2.tgz", + "integrity": "sha512-DgoQmbpFNOofkjJtKwr87Ma5EW4Dc8fWhD0R+ndq7Oc456ivUfGOOP6oAZTTKl5/CcNMP+EN+e3/iUzgE0veZg==", "requires": { "es-to-primitive": "^1.2.0", "function-bind": "^1.1.1", "has": "^1.0.3", + "has-symbols": "^1.0.0", "is-callable": "^1.1.4", "is-regex": "^1.0.4", - "object-keys": "^1.0.12" + "object-inspect": "^1.6.0", + "object-keys": "^1.1.1", + "string.prototype.trimleft": "^2.0.0", + "string.prototype.trimright": "^2.0.0" } }, "es-to-primitive": { @@ -5449,9 +5185,9 @@ } }, "eslint-plugin-graphql": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/eslint-plugin-graphql/-/eslint-plugin-graphql-3.0.3.tgz", - "integrity": "sha512-hHwLyxSkC5rkakJ/SNTWwOswPdVhvfyMCnEOloevrLQIOHUNVIQBg1ljCaRe9C40HdzgcGUFUdG5BHLCKm8tuw==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-graphql/-/eslint-plugin-graphql-3.1.0.tgz", + "integrity": "sha512-87HGS00aeBqGFiQZQGzSPzk1D59w+124F8CRIDATh3LJqce5RCTuUI4tcIqPeyY95YPBCIKwISksWUuA0nrgNw==", "requires": { "graphql-config": "^2.0.1", "lodash": "^4.11.1" @@ -5628,9 +5364,9 @@ "integrity": "sha512-wC9j5vjH9Xu9s8XhumgBoypdFJswraU1HXykqCCD/b7q+EH4P/avf5fM1e8IiHyHNZOeOiWwrki2775XFTYyeg==" }, "eventemitter3": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.2.tgz", - "integrity": "sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q==" + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.0.tgz", + "integrity": "sha512-qerSRB0p+UDEssxTtm6EDKcE7W4OaoisfIMl4CngyEhjpYglocpNg6UEqCvemdGhosAsg4sO2dXJOdyBifPGCg==" }, "events": { "version": "3.0.0", @@ -6767,13 +6503,13 @@ "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=" }, "gatsby": { - "version": "2.15.15", - "resolved": "https://registry.npmjs.org/gatsby/-/gatsby-2.15.15.tgz", - "integrity": "sha512-mOZJSBoVYd6ObEIB0sHje6BeMd7OQeGAw2XtJCPfetolkwWqYN+fUpYdVlYz5cbztuWmXIQoU6jhrxqsJu/InA==", + "version": "2.15.21", + "resolved": "https://registry.npmjs.org/gatsby/-/gatsby-2.15.21.tgz", + "integrity": "sha512-mSiWjKDugVRswf0jCBpMCO7NQToBXXooZqMXB9eftUjowCJQyBOxZdJzB73brQQOltUoZp4ib6n4t7mpFwFuzA==", "requires": { "@babel/code-frame": "^7.5.5", "@babel/core": "^7.6.0", - "@babel/parser": "^7.5.5", + "@babel/parser": "^7.6.0", "@babel/polyfill": "^7.6.0", "@babel/runtime": "^7.6.0", "@babel/traverse": "^7.6.0", @@ -6792,8 +6528,8 @@ "babel-loader": "^8.0.6", "babel-plugin-add-module-exports": "^0.3.3", "babel-plugin-dynamic-import-node": "^1.2.0", - "babel-plugin-remove-graphql-queries": "^2.7.7", - "babel-preset-gatsby": "^0.2.13", + "babel-plugin-remove-graphql-queries": "^2.7.8", + "babel-preset-gatsby": "^0.2.14", "better-opn": "1.0.0", "better-queue": "^3.8.10", "bluebird": "^3.5.5", @@ -6801,7 +6537,7 @@ "cache-manager": "^2.10.0", "cache-manager-fs-hash": "^0.0.7", "chalk": "^2.4.2", - "chokidar": "3.0.2", + "chokidar": "3.1.1", "common-tags": "^1.8.0", "compression": "^1.7.4", "convert-hrtime": "^2.0.0", @@ -6818,7 +6554,7 @@ "eslint-config-react-app": "^4.0.1", "eslint-loader": "^2.2.1", "eslint-plugin-flowtype": "^3.13.0", - "eslint-plugin-graphql": "^3.0.3", + "eslint-plugin-graphql": "^3.1.0", "eslint-plugin-import": "^2.18.2", "eslint-plugin-jsx-a11y": "^6.2.3", "eslint-plugin-react": "^7.14.3", @@ -6831,22 +6567,22 @@ "flat": "^4.1.0", "fs-exists-cached": "1.0.0", "fs-extra": "^8.1.0", - "gatsby-cli": "^2.7.47", - "gatsby-core-utils": "^1.0.8", - "gatsby-graphiql-explorer": "^0.2.15", - "gatsby-link": "^2.2.13", - "gatsby-plugin-page-creator": "^2.1.17", - "gatsby-react-router-scroll": "^2.1.8", - "gatsby-telemetry": "^1.1.23", + "gatsby-cli": "^2.7.49", + "gatsby-core-utils": "^1.0.10", + "gatsby-graphiql-explorer": "^0.2.18", + "gatsby-link": "^2.2.15", + "gatsby-plugin-page-creator": "^2.1.21", + "gatsby-react-router-scroll": "^2.1.9", + "gatsby-telemetry": "^1.1.25", "glob": "^7.1.4", "got": "8.3.2", - "graphql": "^14.5.4", + "graphql": "^14.5.7", "graphql-compose": "^6.3.5", "graphql-playground-middleware-express": "^1.7.12", "invariant": "^2.2.4", "is-relative": "^1.0.0", "is-relative-url": "^3.0.0", - "is-wsl": "^2.1.0", + "is-wsl": "^2.1.1", "jest-worker": "^24.9.0", "json-loader": "^0.5.7", "json-stringify-safe": "^5.0.1", @@ -6883,7 +6619,7 @@ "sift": "^5.1.0", "signal-exit": "^3.0.2", "slash": "^3.0.0", - "socket.io": "^2.2.0", + "socket.io": "^2.3.0", "stack-trace": "^0.0.10", "string-similarity": "^1.2.2", "style-loader": "^0.23.1", @@ -6894,9 +6630,9 @@ "util.promisify": "^1.0.0", "uuid": "^3.3.3", "v8-compile-cache": "^1.1.2", - "webpack": "~4.40.1", + "webpack": "~4.40.2", "webpack-dev-middleware": "^3.7.1", - "webpack-dev-server": "^3.8.0", + "webpack-dev-server": "^3.8.1", "webpack-hot-middleware": "^2.25.0", "webpack-merge": "^4.2.2", "webpack-stats-plugin": "^0.3.0", @@ -6904,130 +6640,6 @@ "yaml-loader": "^0.5.0" }, "dependencies": { - "@babel/core": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.6.0.tgz", - "integrity": "sha512-FuRhDRtsd6IptKpHXAa+4WPZYY2ZzgowkbLBecEDDSje1X/apG7jQM33or3NdOmjXBKWGOg4JmSiRfUfuTtHXw==", - "requires": { - "@babel/code-frame": "^7.5.5", - "@babel/generator": "^7.6.0", - "@babel/helpers": "^7.6.0", - "@babel/parser": "^7.6.0", - "@babel/template": "^7.6.0", - "@babel/traverse": "^7.6.0", - "@babel/types": "^7.6.0", - "convert-source-map": "^1.1.0", - "debug": "^4.1.0", - "json5": "^2.1.0", - "lodash": "^4.17.13", - "resolve": "^1.3.2", - "semver": "^5.4.1", - "source-map": "^0.5.0" - }, - "dependencies": { - "@babel/parser": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.6.0.tgz", - "integrity": "sha512-+o2q111WEx4srBs7L9eJmcwi655eD8sXniLqMB93TBK9GrNzGrxDWSjiqz2hLU0Ha8MTXFIP0yd9fNdP+m43ZQ==" - }, - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "requires": { - "ms": "^2.1.1" - } - } - } - }, - "@babel/generator": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.6.0.tgz", - "integrity": "sha512-Ms8Mo7YBdMMn1BYuNtKuP/z0TgEIhbcyB8HVR6PPNYp4P61lMsABiS4A3VG1qznjXVCf3r+fVHhm4efTYVsySA==", - "requires": { - "@babel/types": "^7.6.0", - "jsesc": "^2.5.1", - "lodash": "^4.17.13", - "source-map": "^0.5.0", - "trim-right": "^1.0.1" - } - }, - "@babel/helpers": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.6.0.tgz", - "integrity": "sha512-W9kao7OBleOjfXtFGgArGRX6eCP0UEcA2ZWEWNkJdRZnHhW4eEbeswbG3EwaRsnQUAEGWYgMq1HsIXuNNNy2eQ==", - "requires": { - "@babel/template": "^7.6.0", - "@babel/traverse": "^7.6.0", - "@babel/types": "^7.6.0" - } - }, - "@babel/runtime": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.6.0.tgz", - "integrity": "sha512-89eSBLJsxNxOERC0Op4vd+0Bqm6wRMqMbFtV3i0/fbaWw/mJ8Q3eBvgX0G4SyrOOLCtbu98HspF8o09MRT+KzQ==", - "requires": { - "regenerator-runtime": "^0.13.2" - } - }, - "@babel/template": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.6.0.tgz", - "integrity": "sha512-5AEH2EXD8euCk446b7edmgFdub/qfH1SN6Nii3+fyXP807QRx9Q73A2N5hNwRRslC2H9sNzaFhsPubkS4L8oNQ==", - "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/parser": "^7.6.0", - "@babel/types": "^7.6.0" - }, - "dependencies": { - "@babel/parser": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.6.0.tgz", - "integrity": "sha512-+o2q111WEx4srBs7L9eJmcwi655eD8sXniLqMB93TBK9GrNzGrxDWSjiqz2hLU0Ha8MTXFIP0yd9fNdP+m43ZQ==" - } - } - }, - "@babel/traverse": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.6.0.tgz", - "integrity": "sha512-93t52SaOBgml/xY74lsmt7xOR4ufYvhb5c5qiM6lu4J/dWGMAfAh6eKw4PjLes6DI6nQgearoxnFJk60YchpvQ==", - "requires": { - "@babel/code-frame": "^7.5.5", - "@babel/generator": "^7.6.0", - "@babel/helper-function-name": "^7.1.0", - "@babel/helper-split-export-declaration": "^7.4.4", - "@babel/parser": "^7.6.0", - "@babel/types": "^7.6.0", - "debug": "^4.1.0", - "globals": "^11.1.0", - "lodash": "^4.17.13" - }, - "dependencies": { - "@babel/parser": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.6.0.tgz", - "integrity": "sha512-+o2q111WEx4srBs7L9eJmcwi655eD8sXniLqMB93TBK9GrNzGrxDWSjiqz2hLU0Ha8MTXFIP0yd9fNdP+m43ZQ==" - }, - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "requires": { - "ms": "^2.1.1" - } - } - } - }, - "@babel/types": { - "version": "7.6.1", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.6.1.tgz", - "integrity": "sha512-X7gdiuaCmA0uRjCmRtYJNAVCc/q+5xSgsfKJHqMN4iNLILX39677fJE1O40arPMh0TTtS9ItH67yre6c7k6t0g==", - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.13", - "to-fast-properties": "^2.0.0" - } - }, "ansi-regex": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", @@ -7126,9 +6738,9 @@ } }, "gatsby-cli": { - "version": "2.7.47", - "resolved": "https://registry.npmjs.org/gatsby-cli/-/gatsby-cli-2.7.47.tgz", - "integrity": "sha512-dDVrbTjxzUwugOnnI/MDFZruYie5KW+t+XmLrG2L4ZjKBrAT6G2wsiPS+NwHiConqfTx67xoFAp54++C8+iWXg==", + "version": "2.7.49", + "resolved": "https://registry.npmjs.org/gatsby-cli/-/gatsby-cli-2.7.49.tgz", + "integrity": "sha512-Bsko3geZr502/9xl5/jik0wddNrzBDcWCm3dFsFy0FUcXHu8WISIjYPdCVRwqTTAu1yBX30QsJtBbu/9LTjIKA==", "requires": { "@babel/code-frame": "^7.5.5", "@babel/runtime": "^7.6.0", @@ -7146,7 +6758,7 @@ "execa": "^2.0.4", "fs-exists-cached": "^1.0.0", "fs-extra": "^8.1.0", - "gatsby-telemetry": "^1.1.23", + "gatsby-telemetry": "^1.1.25", "hosted-git-info": "^3.0.0", "ink": "^2.3.0", "ink-spinner": "^3.0.1", @@ -7183,11 +6795,6 @@ "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" - }, - "source-map": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", - "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==" } } }, @@ -7383,6 +6990,11 @@ "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" }, + "source-map": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", + "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==" + }, "strip-ansi": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", @@ -7451,133 +7063,73 @@ } }, "gatsby-core-utils": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/gatsby-core-utils/-/gatsby-core-utils-1.0.8.tgz", - "integrity": "sha512-080Jl8NamTbCGliKxXpMjEO1XUYU5FAow+VPR/j6hJk+Kl/gFmpE1mqa5QnHRGLZQhBP/h2T0mUwnSJn9m/Jsw==" + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/gatsby-core-utils/-/gatsby-core-utils-1.0.10.tgz", + "integrity": "sha512-AeOrwjF4LiAHAqvrUkAFtvZHkLpKtF2gnIS/nezHDzIMq0SaZLHbdQiJcesyzZ6CcerjLIDWbD6MqxrTt6KkhA==" }, "gatsby-graphiql-explorer": { - "version": "0.2.15", - "resolved": "https://registry.npmjs.org/gatsby-graphiql-explorer/-/gatsby-graphiql-explorer-0.2.15.tgz", - "integrity": "sha512-T4N+q8VDbivREBDRKLoxlersaYhWhEaTBKBLT0rWAd8PfPiFQPno8JIaqFTB1DLgsL421wJo9JSsLy1xPx79yA==", + "version": "0.2.18", + "resolved": "https://registry.npmjs.org/gatsby-graphiql-explorer/-/gatsby-graphiql-explorer-0.2.18.tgz", + "integrity": "sha512-QQmX1Jg5LBsTG6ZoYte0nHS7+tApea/sVCntHSe4M7JRnM8KrECJJsyDesCBUME6lvzV5wdUV9TvFER3+sK++A==", "requires": { "@babel/runtime": "^7.6.0" - }, - "dependencies": { - "@babel/runtime": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.6.0.tgz", - "integrity": "sha512-89eSBLJsxNxOERC0Op4vd+0Bqm6wRMqMbFtV3i0/fbaWw/mJ8Q3eBvgX0G4SyrOOLCtbu98HspF8o09MRT+KzQ==", - "requires": { - "regenerator-runtime": "^0.13.2" - } - } } }, "gatsby-link": { - "version": "2.2.13", - "resolved": "https://registry.npmjs.org/gatsby-link/-/gatsby-link-2.2.13.tgz", - "integrity": "sha512-CghQ+hM5JQONNuQ5tQ383z9B9ngDPr79I1lLhZ8j8IPOfna4VQsdVYYtS/wHkdiuF2Ji8ZADP1YJcUEEAEfhew==", + "version": "2.2.15", + "resolved": "https://registry.npmjs.org/gatsby-link/-/gatsby-link-2.2.15.tgz", + "integrity": "sha512-EeL5c+iFbXrVJ/xzbkvaiuLcLPt2FFJ5cTiBx1KV2abe1U8vCeQgBjMfc5rABSyOV8nUx5js468LGvaLkmgJdA==", "requires": { "@babel/runtime": "^7.6.0", - "@types/reach__router": "^1.2.4", + "@types/reach__router": "^1.2.5", "prop-types": "^15.7.2" - }, - "dependencies": { - "@babel/runtime": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.6.0.tgz", - "integrity": "sha512-89eSBLJsxNxOERC0Op4vd+0Bqm6wRMqMbFtV3i0/fbaWw/mJ8Q3eBvgX0G4SyrOOLCtbu98HspF8o09MRT+KzQ==", - "requires": { - "regenerator-runtime": "^0.13.2" - } - } } }, "gatsby-page-utils": { - "version": "0.0.17", - "resolved": "https://registry.npmjs.org/gatsby-page-utils/-/gatsby-page-utils-0.0.17.tgz", - "integrity": "sha512-4uH6cfkUk76gmzqEPKWebjOtX36YJhV0oNVDTKJNXHmodfQikpQPtMdtXC99TKbcG8N8Iu+0cvDTE44dUkrL5Q==", + "version": "0.0.21", + "resolved": "https://registry.npmjs.org/gatsby-page-utils/-/gatsby-page-utils-0.0.21.tgz", + "integrity": "sha512-9eKL5M25SK7Nqbbax3zYWH5mY4880uraZEJ0zhgtLwuuhoIfBruHi0hZovbeFhAOj8F+nqXrZiLhPmG8HNZ5NA==", "requires": { "@babel/runtime": "^7.6.0", "bluebird": "^3.5.5", - "chokidar": "3.0.2", + "chokidar": "3.1.1", "fs-exists-cached": "^1.0.0", "glob": "^7.1.4", "lodash": "^4.17.15", "micromatch": "^3.1.10", "slash": "^3.0.0" - }, - "dependencies": { - "@babel/runtime": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.6.0.tgz", - "integrity": "sha512-89eSBLJsxNxOERC0Op4vd+0Bqm6wRMqMbFtV3i0/fbaWw/mJ8Q3eBvgX0G4SyrOOLCtbu98HspF8o09MRT+KzQ==", - "requires": { - "regenerator-runtime": "^0.13.2" - } - } } }, "gatsby-plugin-emotion": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/gatsby-plugin-emotion/-/gatsby-plugin-emotion-4.1.6.tgz", - "integrity": "sha512-3iOPkDG54ByYYyO7/M/3pFuCbt6IAtANznVNHb2GdqCVqgx71u2MD9uQlUrs6FdMAmlsVWLxr/b3kUTHO//jJA==", + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/gatsby-plugin-emotion/-/gatsby-plugin-emotion-4.1.7.tgz", + "integrity": "sha512-py48KP/pVj7NZInHkud21b/0TrAcGq7Y25VMkiFXJMpqpPHSSlYZHWYloTR/R4k88tZO2VHwPtmTPhHSeeJX3g==", "requires": { "@babel/runtime": "^7.6.0", "@emotion/babel-preset-css-prop": "^10.0.17" - }, - "dependencies": { - "@babel/runtime": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.6.0.tgz", - "integrity": "sha512-89eSBLJsxNxOERC0Op4vd+0Bqm6wRMqMbFtV3i0/fbaWw/mJ8Q3eBvgX0G4SyrOOLCtbu98HspF8o09MRT+KzQ==", - "requires": { - "regenerator-runtime": "^0.13.2" - } - } } }, "gatsby-plugin-google-analytics": { - "version": "2.1.16", - "resolved": "https://registry.npmjs.org/gatsby-plugin-google-analytics/-/gatsby-plugin-google-analytics-2.1.16.tgz", - "integrity": "sha512-r56AlYp702uAx1dx584tui7Bk496/YkAJ3rB+vbLehocif4zyhNsFSlE4vKDBqG83c+iGywFdaa6xUTvh14hbw==", - "requires": { - "@babel/runtime": "^7.6.0" - }, - "dependencies": { - "@babel/runtime": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.6.0.tgz", - "integrity": "sha512-89eSBLJsxNxOERC0Op4vd+0Bqm6wRMqMbFtV3i0/fbaWw/mJ8Q3eBvgX0G4SyrOOLCtbu98HspF8o09MRT+KzQ==", - "requires": { - "regenerator-runtime": "^0.13.2" - } - } + "version": "2.1.17", + "resolved": "https://registry.npmjs.org/gatsby-plugin-google-analytics/-/gatsby-plugin-google-analytics-2.1.17.tgz", + "integrity": "sha512-l+sqqHHLmbVx7XQOLTsPNQp3bKOwNcXANVOmFBumR3tBKrwS3oMfn0+/K/uzVESJ3oq2GRvKSMW6NWEoUtuo/Q==", + "requires": { + "@babel/runtime": "^7.6.0" } }, "gatsby-plugin-less": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/gatsby-plugin-less/-/gatsby-plugin-less-3.0.6.tgz", - "integrity": "sha512-+gqi8S3YCmEi7BGPDluFK9AwVVLseu+eCEI281+J7nWGiXEvM++kBSLR3y4jLwVyoQ32RibYIpTiGSl4jqQjOg==", + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/gatsby-plugin-less/-/gatsby-plugin-less-3.0.7.tgz", + "integrity": "sha512-qbnj4KpTWq37FR3ijNH/oAjV4cqXY8C2JVZ+1YPlmqvTUopxWALWEC1eLm6dcgqLy2Wgv9COFKiMlCpGW+a2Yg==", "requires": { "@babel/runtime": "^7.6.0", "less-loader": "^5.0.0" - }, - "dependencies": { - "@babel/runtime": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.6.0.tgz", - "integrity": "sha512-89eSBLJsxNxOERC0Op4vd+0Bqm6wRMqMbFtV3i0/fbaWw/mJ8Q3eBvgX0G4SyrOOLCtbu98HspF8o09MRT+KzQ==", - "requires": { - "regenerator-runtime": "^0.13.2" - } - } } }, "gatsby-plugin-mdx": { - "version": "1.0.41", - "resolved": "https://registry.npmjs.org/gatsby-plugin-mdx/-/gatsby-plugin-mdx-1.0.41.tgz", - "integrity": "sha512-V5VYyBWDyVO+rs71KbVgZmhiZkHgQIcgu1KC3cuJqMDgO5e1Xqhc7yBvfv75+GCN72fotDMeVdOC3FJic4HXQw==", + "version": "1.0.43", + "resolved": "https://registry.npmjs.org/gatsby-plugin-mdx/-/gatsby-plugin-mdx-1.0.43.tgz", + "integrity": "sha512-BKhAExhLvz2gzpENz/af8udAxqmvDiV3TTkQvW2RzJ1UXJh8f1WsJzum2t4TuJza2UYp4+U1GxGSiSSRdQS4Bg==", "requires": { "@babel/core": "^7.6.0", "@babel/generator": "^7.6.0", @@ -7610,207 +7162,12 @@ "static-site-generator-webpack-plugin": "^3.4.2", "style-to-object": "^0.2.3", "underscore.string": "^3.3.5", - "unified": "^8.3.2", + "unified": "^8.4.0", "unist-util-map": "^1.0.5", "unist-util-remove": "^1.0.3", "unist-util-visit": "^1.4.1" }, "dependencies": { - "@babel/core": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.6.0.tgz", - "integrity": "sha512-FuRhDRtsd6IptKpHXAa+4WPZYY2ZzgowkbLBecEDDSje1X/apG7jQM33or3NdOmjXBKWGOg4JmSiRfUfuTtHXw==", - "requires": { - "@babel/code-frame": "^7.5.5", - "@babel/generator": "^7.6.0", - "@babel/helpers": "^7.6.0", - "@babel/parser": "^7.6.0", - "@babel/template": "^7.6.0", - "@babel/traverse": "^7.6.0", - "@babel/types": "^7.6.0", - "convert-source-map": "^1.1.0", - "debug": "^4.1.0", - "json5": "^2.1.0", - "lodash": "^4.17.13", - "resolve": "^1.3.2", - "semver": "^5.4.1", - "source-map": "^0.5.0" - } - }, - "@babel/generator": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.6.0.tgz", - "integrity": "sha512-Ms8Mo7YBdMMn1BYuNtKuP/z0TgEIhbcyB8HVR6PPNYp4P61lMsABiS4A3VG1qznjXVCf3r+fVHhm4efTYVsySA==", - "requires": { - "@babel/types": "^7.6.0", - "jsesc": "^2.5.1", - "lodash": "^4.17.13", - "source-map": "^0.5.0", - "trim-right": "^1.0.1" - } - }, - "@babel/helpers": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.6.0.tgz", - "integrity": "sha512-W9kao7OBleOjfXtFGgArGRX6eCP0UEcA2ZWEWNkJdRZnHhW4eEbeswbG3EwaRsnQUAEGWYgMq1HsIXuNNNy2eQ==", - "requires": { - "@babel/template": "^7.6.0", - "@babel/traverse": "^7.6.0", - "@babel/types": "^7.6.0" - } - }, - "@babel/parser": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.6.0.tgz", - "integrity": "sha512-+o2q111WEx4srBs7L9eJmcwi655eD8sXniLqMB93TBK9GrNzGrxDWSjiqz2hLU0Ha8MTXFIP0yd9fNdP+m43ZQ==" - }, - "@babel/plugin-transform-block-scoping": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.6.0.tgz", - "integrity": "sha512-tIt4E23+kw6TgL/edACZwP1OUKrjOTyMrFMLoT5IOFrfMRabCgekjqFd5o6PaAMildBu46oFkekIdMuGkkPEpA==", - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "lodash": "^4.17.13" - } - }, - "@babel/plugin-transform-destructuring": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.6.0.tgz", - "integrity": "sha512-2bGIS5P1v4+sWTCnKNDZDxbGvEqi0ijeqM/YqHtVGrvG2y0ySgnEEhXErvE9dA0bnIzY9bIzdFK0jFA46ASIIQ==", - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/plugin-transform-modules-commonjs": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.6.0.tgz", - "integrity": "sha512-Ma93Ix95PNSEngqomy5LSBMAQvYKVe3dy+JlVJSHEXZR5ASL9lQBedMiCyVtmTLraIDVRE3ZjTZvmXXD2Ozw3g==", - "requires": { - "@babel/helper-module-transforms": "^7.4.4", - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/helper-simple-access": "^7.1.0", - "babel-plugin-dynamic-import-node": "^2.3.0" - } - }, - "@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.6.0.tgz", - "integrity": "sha512-jem7uytlmrRl3iCAuQyw8BpB4c4LWvSpvIeXKpMb+7j84lkx4m4mYr5ErAcmN5KM7B6BqrAvRGjBIbbzqCczew==", - "requires": { - "regexp-tree": "^0.1.13" - } - }, - "@babel/preset-env": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.6.0.tgz", - "integrity": "sha512-1efzxFv/TcPsNXlRhMzRnkBFMeIqBBgzwmZwlFDw5Ubj0AGLeufxugirwZmkkX/ayi3owsSqoQ4fw8LkfK9SYg==", - "requires": { - "@babel/helper-module-imports": "^7.0.0", - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-proposal-async-generator-functions": "^7.2.0", - "@babel/plugin-proposal-dynamic-import": "^7.5.0", - "@babel/plugin-proposal-json-strings": "^7.2.0", - "@babel/plugin-proposal-object-rest-spread": "^7.5.5", - "@babel/plugin-proposal-optional-catch-binding": "^7.2.0", - "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", - "@babel/plugin-syntax-async-generators": "^7.2.0", - "@babel/plugin-syntax-dynamic-import": "^7.2.0", - "@babel/plugin-syntax-json-strings": "^7.2.0", - "@babel/plugin-syntax-object-rest-spread": "^7.2.0", - "@babel/plugin-syntax-optional-catch-binding": "^7.2.0", - "@babel/plugin-transform-arrow-functions": "^7.2.0", - "@babel/plugin-transform-async-to-generator": "^7.5.0", - "@babel/plugin-transform-block-scoped-functions": "^7.2.0", - "@babel/plugin-transform-block-scoping": "^7.6.0", - "@babel/plugin-transform-classes": "^7.5.5", - "@babel/plugin-transform-computed-properties": "^7.2.0", - "@babel/plugin-transform-destructuring": "^7.6.0", - "@babel/plugin-transform-dotall-regex": "^7.4.4", - "@babel/plugin-transform-duplicate-keys": "^7.5.0", - "@babel/plugin-transform-exponentiation-operator": "^7.2.0", - "@babel/plugin-transform-for-of": "^7.4.4", - "@babel/plugin-transform-function-name": "^7.4.4", - "@babel/plugin-transform-literals": "^7.2.0", - "@babel/plugin-transform-member-expression-literals": "^7.2.0", - "@babel/plugin-transform-modules-amd": "^7.5.0", - "@babel/plugin-transform-modules-commonjs": "^7.6.0", - "@babel/plugin-transform-modules-systemjs": "^7.5.0", - "@babel/plugin-transform-modules-umd": "^7.2.0", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.6.0", - "@babel/plugin-transform-new-target": "^7.4.4", - "@babel/plugin-transform-object-super": "^7.5.5", - "@babel/plugin-transform-parameters": "^7.4.4", - "@babel/plugin-transform-property-literals": "^7.2.0", - "@babel/plugin-transform-regenerator": "^7.4.5", - "@babel/plugin-transform-reserved-words": "^7.2.0", - "@babel/plugin-transform-shorthand-properties": "^7.2.0", - "@babel/plugin-transform-spread": "^7.2.0", - "@babel/plugin-transform-sticky-regex": "^7.2.0", - "@babel/plugin-transform-template-literals": "^7.4.4", - "@babel/plugin-transform-typeof-symbol": "^7.2.0", - "@babel/plugin-transform-unicode-regex": "^7.4.4", - "@babel/types": "^7.6.0", - "browserslist": "^4.6.0", - "core-js-compat": "^3.1.1", - "invariant": "^2.2.2", - "js-levenshtein": "^1.1.3", - "semver": "^5.5.0" - } - }, - "@babel/template": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.6.0.tgz", - "integrity": "sha512-5AEH2EXD8euCk446b7edmgFdub/qfH1SN6Nii3+fyXP807QRx9Q73A2N5hNwRRslC2H9sNzaFhsPubkS4L8oNQ==", - "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/parser": "^7.6.0", - "@babel/types": "^7.6.0" - } - }, - "@babel/traverse": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.6.0.tgz", - "integrity": "sha512-93t52SaOBgml/xY74lsmt7xOR4ufYvhb5c5qiM6lu4J/dWGMAfAh6eKw4PjLes6DI6nQgearoxnFJk60YchpvQ==", - "requires": { - "@babel/code-frame": "^7.5.5", - "@babel/generator": "^7.6.0", - "@babel/helper-function-name": "^7.1.0", - "@babel/helper-split-export-declaration": "^7.4.4", - "@babel/parser": "^7.6.0", - "@babel/types": "^7.6.0", - "debug": "^4.1.0", - "globals": "^11.1.0", - "lodash": "^4.17.13" - } - }, - "@babel/types": { - "version": "7.6.1", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.6.1.tgz", - "integrity": "sha512-X7gdiuaCmA0uRjCmRtYJNAVCc/q+5xSgsfKJHqMN4iNLILX39677fJE1O40arPMh0TTtS9ItH67yre6c7k6t0g==", - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.13", - "to-fast-properties": "^2.0.0" - } - }, - "babel-plugin-dynamic-import-node": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.0.tgz", - "integrity": "sha512-o6qFkpeQEBxcqt0XYlWzAVxNCSCZdUgcR8IRlhD/8DylxjjO4foPcvTW0GGKa/cVt3rvxZ7o5ippJ+/0nvLhlQ==", - "requires": { - "object.assign": "^4.1.0" - } - }, - "browserslist": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.7.0.tgz", - "integrity": "sha512-9rGNDtnj+HaahxiVV38Gn8n8Lr8REKsel68v1sPFfIGEK6uSXTY3h9acgiT1dZVtOOUtifo/Dn8daDQ5dUgVsA==", - "requires": { - "caniuse-lite": "^1.0.30000989", - "electron-to-chromium": "^1.3.247", - "node-releases": "^1.1.29" - } - }, "debug": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", @@ -7819,24 +7176,23 @@ "ms": "^2.1.1" } }, - "electron-to-chromium": { - "version": "1.3.255", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.255.tgz", - "integrity": "sha512-SZ6NlaNw3h4WR5kA1BK8XltdJCax02P+lW+z78RYoLDqmpyYuDQ5bS+/O6MCJ/j761qoZIFox2qYYt+UwqGA5w==" + "is-plain-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.0.0.tgz", + "integrity": "sha512-EYisGhpgSCwspmIuRHGjROWTon2Xp8Z7U03Wubk/bTL5TTRC5R1rGVgyjzBrk9+ULdH6cRD06KRcw/xfqhVYKQ==" }, - "node-releases": { - "version": "1.1.30", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.30.tgz", - "integrity": "sha512-BHcr1g6NeUH12IL+X3Flvs4IOnl1TL0JczUhEZjDE+FXXPQcVCNr8NEPb01zqGxzhTpdyJL5GXemaCW7aw6Khw==", + "unified": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/unified/-/unified-8.4.0.tgz", + "integrity": "sha512-hQqeCrzqqS3vk8WbvbjYgaxe9WqmZF32Y3lz/kY5A8/5RdJbxoa4yOIAYpSEvqii9n2MTI2OL1+ByoVJYLhlUg==", "requires": { - "semver": "^5.3.0" + "bail": "^1.0.0", + "extend": "^3.0.0", + "is-plain-obj": "^2.0.0", + "trough": "^1.0.0", + "vfile": "^4.0.0" } }, - "regexp-tree": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.13.tgz", - "integrity": "sha512-hwdV/GQY5F8ReLZWO+W1SRoN5YfpOKY6852+tBFcma72DKBIcHjPRIlIvQN35bCOljuAfP2G2iB0FC/w236mUw==" - }, "unist-util-visit": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-1.4.1.tgz", @@ -7848,45 +7204,25 @@ } }, "gatsby-plugin-page-creator": { - "version": "2.1.17", - "resolved": "https://registry.npmjs.org/gatsby-plugin-page-creator/-/gatsby-plugin-page-creator-2.1.17.tgz", - "integrity": "sha512-0PxLwZH1INOjQ9QhoL+1lS5/Fad72izmuusiEWktP1njw0CBzNQW9XK+KdT2TrSzlnt9U4NyJzW+VDAbtVBXpg==", + "version": "2.1.21", + "resolved": "https://registry.npmjs.org/gatsby-plugin-page-creator/-/gatsby-plugin-page-creator-2.1.21.tgz", + "integrity": "sha512-OpdvGh36xNYiX0jp+h5U2PXrkRjJGyEvehCiq8mV/PVU/75tWCZ1jGaaBneZEEh/9tx9NPvqdpq0DeHa2Rqs9Q==", "requires": { "@babel/runtime": "^7.6.0", "bluebird": "^3.5.5", "fs-exists-cached": "^1.0.0", - "gatsby-page-utils": "^0.0.17", + "gatsby-page-utils": "^0.0.21", "glob": "^7.1.4", "lodash": "^4.17.15", "micromatch": "^3.1.10" - }, - "dependencies": { - "@babel/runtime": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.6.0.tgz", - "integrity": "sha512-89eSBLJsxNxOERC0Op4vd+0Bqm6wRMqMbFtV3i0/fbaWw/mJ8Q3eBvgX0G4SyrOOLCtbu98HspF8o09MRT+KzQ==", - "requires": { - "regenerator-runtime": "^0.13.2" - } - } } }, "gatsby-plugin-react-helmet": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/gatsby-plugin-react-helmet/-/gatsby-plugin-react-helmet-3.1.7.tgz", - "integrity": "sha512-mW5t8Fu2taX1azf5hnidj565HSFgBPaBzpxyprqhdIPWANh+Er+eA9x9Pz+c3Tw33ia2aghKTzocHa5pYTu0KA==", + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/gatsby-plugin-react-helmet/-/gatsby-plugin-react-helmet-3.1.8.tgz", + "integrity": "sha512-EpBpTwRLOHrxQLL9D3pBikgtW/4TIeUbZc93raerLr14ihht6wdWcGs4pjYAt0cKhRJ2WCvk3pgC3/yM6CEDww==", "requires": { "@babel/runtime": "^7.6.0" - }, - "dependencies": { - "@babel/runtime": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.6.0.tgz", - "integrity": "sha512-89eSBLJsxNxOERC0Op4vd+0Bqm6wRMqMbFtV3i0/fbaWw/mJ8Q3eBvgX0G4SyrOOLCtbu98HspF8o09MRT+KzQ==", - "requires": { - "regenerator-runtime": "^0.13.2" - } - } } }, "gatsby-plugin-svgr": { @@ -7895,29 +7231,19 @@ "integrity": "sha512-54REIMe79qFBAwpcnWHBkvEE9CKoEVkefF9rDXai0k642r91SZ4UeWFuAmsegPG+sPVub7tHfHu/2LVXK1I9kg==" }, "gatsby-react-router-scroll": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/gatsby-react-router-scroll/-/gatsby-react-router-scroll-2.1.8.tgz", - "integrity": "sha512-h6zjADduO8lBvZozn7E7aijoaex75Thk/dwS8256AgKb5SDQ0ilFQHZKsZPvQHwftGpOuvdoYnks/Mb3nR0cJg==", + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/gatsby-react-router-scroll/-/gatsby-react-router-scroll-2.1.9.tgz", + "integrity": "sha512-9od/1l3j2q2pv6Q3ZE4/YNAtCvk1ctbldEu4c9S+J19exflvjydk+qXPaBblEiM3wbsT3bDf+aOB62fRSzFTNg==", "requires": { "@babel/runtime": "^7.6.0", "scroll-behavior": "^0.9.10", "warning": "^3.0.0" - }, - "dependencies": { - "@babel/runtime": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.6.0.tgz", - "integrity": "sha512-89eSBLJsxNxOERC0Op4vd+0Bqm6wRMqMbFtV3i0/fbaWw/mJ8Q3eBvgX0G4SyrOOLCtbu98HspF8o09MRT+KzQ==", - "requires": { - "regenerator-runtime": "^0.13.2" - } - } } }, "gatsby-remark-autolink-headers": { - "version": "2.1.10", - "resolved": "https://registry.npmjs.org/gatsby-remark-autolink-headers/-/gatsby-remark-autolink-headers-2.1.10.tgz", - "integrity": "sha512-MXQuxgTurOXMYi3Rpywz2kMe4Px/H3B2OBy5ZphL9WwOFfaiRK10GkRNMNlHSDNA0K5151PnuSd5mKxLOCxbYw==", + "version": "2.1.11", + "resolved": "https://registry.npmjs.org/gatsby-remark-autolink-headers/-/gatsby-remark-autolink-headers-2.1.11.tgz", + "integrity": "sha512-XrgBMBKevBzIzpJinKB0KZyxq/LsJ/UrBwEn227rF2rC7Ef1n7oQRg4rt+ZpkV9C684GXSOf9wF7TXECv4Al6Q==", "requires": { "@babel/runtime": "^7.6.0", "github-slugger": "^1.2.1", @@ -7926,14 +7252,6 @@ "unist-util-visit": "^1.4.1" }, "dependencies": { - "@babel/runtime": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.6.0.tgz", - "integrity": "sha512-89eSBLJsxNxOERC0Op4vd+0Bqm6wRMqMbFtV3i0/fbaWw/mJ8Q3eBvgX0G4SyrOOLCtbu98HspF8o09MRT+KzQ==", - "requires": { - "regenerator-runtime": "^0.13.2" - } - }, "unist-util-visit": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-1.4.1.tgz", @@ -7963,9 +7281,9 @@ } }, "gatsby-remark-copy-linked-files": { - "version": "2.1.19", - "resolved": "https://registry.npmjs.org/gatsby-remark-copy-linked-files/-/gatsby-remark-copy-linked-files-2.1.19.tgz", - "integrity": "sha512-VKiWxw9x2KZNERoVAtx9oyIjjAtw9vorcNVBND2zlwoo04qyEK5vbnbJmaedCv1XxEpYEJSd7TVw/8saAqz3Vg==", + "version": "2.1.20", + "resolved": "https://registry.npmjs.org/gatsby-remark-copy-linked-files/-/gatsby-remark-copy-linked-files-2.1.20.tgz", + "integrity": "sha512-bw+Lf3u9foV1uEbYxLTkfIXbNrjOn3vGwJ33zXw+uzF5fAsC0IDeSN0/wNsvshDsBVUpjafS0rG7jyWFYxpg6g==", "requires": { "@babel/runtime": "^7.6.0", "cheerio": "^1.0.0-rc.3", @@ -7977,14 +7295,6 @@ "unist-util-visit": "^1.4.1" }, "dependencies": { - "@babel/runtime": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.6.0.tgz", - "integrity": "sha512-89eSBLJsxNxOERC0Op4vd+0Bqm6wRMqMbFtV3i0/fbaWw/mJ8Q3eBvgX0G4SyrOOLCtbu98HspF8o09MRT+KzQ==", - "requires": { - "regenerator-runtime": "^0.13.2" - } - }, "cheerio": { "version": "1.0.0-rc.3", "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.3.tgz", @@ -8031,23 +7341,15 @@ } }, "gatsby-remark-prismjs": { - "version": "3.3.13", - "resolved": "https://registry.npmjs.org/gatsby-remark-prismjs/-/gatsby-remark-prismjs-3.3.13.tgz", - "integrity": "sha512-m5EIH2D1PE6kpqaPbas8cd85rIdXoux5Q3FUo5gkKkiBefdTt3Kk1kA2eW/qzEs8hovHWmRigFzA/HsvK8A1/A==", + "version": "3.3.14", + "resolved": "https://registry.npmjs.org/gatsby-remark-prismjs/-/gatsby-remark-prismjs-3.3.14.tgz", + "integrity": "sha512-KGYjCSpry0DXPhd66wpTEubkcuNrNnW2JWh/eGWRvACLzLpInTNFHTm1wOYsRsfAFVYFT4CWChq33ALaiCH96Q==", "requires": { "@babel/runtime": "^7.6.0", "parse-numeric-range": "^0.0.2", "unist-util-visit": "^1.4.1" }, "dependencies": { - "@babel/runtime": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.6.0.tgz", - "integrity": "sha512-89eSBLJsxNxOERC0Op4vd+0Bqm6wRMqMbFtV3i0/fbaWw/mJ8Q3eBvgX0G4SyrOOLCtbu98HspF8o09MRT+KzQ==", - "requires": { - "regenerator-runtime": "^0.13.2" - } - }, "unist-util-visit": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-1.4.1.tgz", @@ -8106,17 +7408,17 @@ } }, "gatsby-source-filesystem": { - "version": "2.1.22", - "resolved": "https://registry.npmjs.org/gatsby-source-filesystem/-/gatsby-source-filesystem-2.1.22.tgz", - "integrity": "sha512-GUWtBtMEMI2ubr4ql67g+kpDpdrv8Ri/Z2F8KgO2CqNCXKR5ZY/4uf2Yt2mWPNsQgCivH/DkvkfHnw7P++DOTQ==", + "version": "2.1.26", + "resolved": "https://registry.npmjs.org/gatsby-source-filesystem/-/gatsby-source-filesystem-2.1.26.tgz", + "integrity": "sha512-MukMx4CXj+2AYWTsPaBX4flQejrWum/2PJzhOBpO+ytXXlhfaAJt4bBZr2nrgz2QHcFJUZ1kWR+ZSl7gEyZeQA==", "requires": { "@babel/runtime": "^7.6.0", "better-queue": "^3.8.10", "bluebird": "^3.5.5", - "chokidar": "3.0.2", + "chokidar": "3.1.1", "file-type": "^12.3.0", "fs-extra": "^8.1.0", - "gatsby-core-utils": "^1.0.8", + "gatsby-core-utils": "^1.0.10", "got": "^7.1.0", "md5-file": "^3.2.3", "mime": "^2.4.4", @@ -8127,14 +7429,6 @@ "xstate": "^4.6.7" }, "dependencies": { - "@babel/runtime": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.6.0.tgz", - "integrity": "sha512-89eSBLJsxNxOERC0Op4vd+0Bqm6wRMqMbFtV3i0/fbaWw/mJ8Q3eBvgX0G4SyrOOLCtbu98HspF8o09MRT+KzQ==", - "requires": { - "regenerator-runtime": "^0.13.2" - } - }, "got": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/got/-/got-7.1.0.tgz", @@ -8210,9 +7504,9 @@ } }, "gatsby-telemetry": { - "version": "1.1.23", - "resolved": "https://registry.npmjs.org/gatsby-telemetry/-/gatsby-telemetry-1.1.23.tgz", - "integrity": "sha512-tG1NS1BBIKHUXutGKYvoGFmR3sIC+l9GHd52ko2RNxm+rcnKMQAsrhlCfnKw2yGB+wys/k9YOJ5bZ1da1TFe7A==", + "version": "1.1.25", + "resolved": "https://registry.npmjs.org/gatsby-telemetry/-/gatsby-telemetry-1.1.25.tgz", + "integrity": "sha512-vX2nARHJEDiyhhKuvF3wN2cbrluwgMycbiunazG6NU29P+zFlQCz9MMrZ4Q09h5SP4P1sWJtrDv13+5A66GHpg==", "requires": { "@babel/code-frame": "^7.5.5", "@babel/runtime": "^7.6.0", @@ -8233,14 +7527,6 @@ "uuid": "3.3.3" }, "dependencies": { - "@babel/runtime": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.6.0.tgz", - "integrity": "sha512-89eSBLJsxNxOERC0Op4vd+0Bqm6wRMqMbFtV3i0/fbaWw/mJ8Q3eBvgX0G4SyrOOLCtbu98HspF8o09MRT+KzQ==", - "requires": { - "regenerator-runtime": "^0.13.2" - } - }, "configstore": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.0.tgz", @@ -8322,11 +7608,11 @@ } }, "gatsby-theme-apollo-core": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/gatsby-theme-apollo-core/-/gatsby-theme-apollo-core-2.0.1.tgz", - "integrity": "sha512-dylMvlSAhkAKazb06vGnUvq4kO4XHEXmc+WwIkwg3k0eF0LLt3f7HLpRGKHm94ErowhBpBa9QmvGSZKbDHRfwQ==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/gatsby-theme-apollo-core/-/gatsby-theme-apollo-core-2.0.2.tgz", + "integrity": "sha512-IpIc2bcHYiQivTFek4fQE1arLuDMVXhML1/Hcbu+eCFf+jko2jsOa8EdGgV+9C7bdXD13sGrIWPYkT+GwOP+cg==", "requires": { - "@apollo/space-kit": "0.0.8", + "@apollo/space-kit": "^2.7.0", "@emotion/core": "^10.0.7", "@emotion/styled": "^10.0.7", "@svgr/webpack": "^4.2.0", @@ -8339,15 +7625,13 @@ "polished": "^2.3.3", "prop-types": "^15.6.2", "react-helmet": "^5.2.0", - "react-icons": "^3.3.0", - "react-use": "^9.11.0", - "store": "^2.0.12" + "react-use": "^9.11.0" } }, "gatsby-theme-apollo-docs": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/gatsby-theme-apollo-docs/-/gatsby-theme-apollo-docs-2.0.3.tgz", - "integrity": "sha512-3J99pjke2VfvXliF2t+jke/D4jVruM3+CO25rIw7fDExkKyBQstHN9kCsrPDffEcnRH3QkVTWB/G0UqsfL/TiQ==", + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/gatsby-theme-apollo-docs/-/gatsby-theme-apollo-docs-2.1.5.tgz", + "integrity": "sha512-MpJ37Fn8agOdUh6dGBFzPCS1h1bq8MOJ2Pjvh7Eou4vcUQogVPHJKddjGhi5xLcy0paWLLyYnnwt5DYZTQ2ovA==", "requires": { "@mdx-js/mdx": "^1.1.0", "@mdx-js/react": "^1.0.27", @@ -8362,7 +7646,7 @@ "gatsby-remark-typescript": "0.0.9", "gatsby-source-filesystem": "^2.0.29", "gatsby-source-git": "^1.0.1", - "gatsby-theme-apollo-core": "^2.0.0", + "gatsby-theme-apollo-core": "^2.0.2", "gatsby-transformer-remark": "^2.6.7", "js-yaml": "^3.13.1", "prismjs": "^1.15.0", @@ -8374,13 +7658,13 @@ } }, "gatsby-transformer-remark": { - "version": "2.6.22", - "resolved": "https://registry.npmjs.org/gatsby-transformer-remark/-/gatsby-transformer-remark-2.6.22.tgz", - "integrity": "sha512-WONmnxXJ86Ko9y7YgQRN+mVoHgv9nTd+IjgLyiuNDuMFKCAUrKSBpTVyqfv8AEYoovGFuaCx1gCp6aT6MKmyzQ==", + "version": "2.6.24", + "resolved": "https://registry.npmjs.org/gatsby-transformer-remark/-/gatsby-transformer-remark-2.6.24.tgz", + "integrity": "sha512-UeUGZzCL8/I8O0nAW/j0FwJvNDy0mQ3+EoVe3gX5C5wOB0Sjq4RLPs1316OfEom4dNntPATIRpw0ngr00dNaNg==", "requires": { "@babel/runtime": "^7.6.0", "bluebird": "^3.5.5", - "gatsby-core-utils": "^1.0.8", + "gatsby-core-utils": "^1.0.10", "gray-matter": "^4.0.2", "hast-util-raw": "^4.0.0", "hast-util-to-html": "^4.0.1", @@ -8401,14 +7685,6 @@ "unist-util-visit": "^1.4.1" }, "dependencies": { - "@babel/runtime": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.6.0.tgz", - "integrity": "sha512-89eSBLJsxNxOERC0Op4vd+0Bqm6wRMqMbFtV3i0/fbaWw/mJ8Q3eBvgX0G4SyrOOLCtbu98HspF8o09MRT+KzQ==", - "requires": { - "regenerator-runtime": "^0.13.2" - } - }, "hast-to-hyperscript": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/hast-to-hyperscript/-/hast-to-hyperscript-5.0.0.tgz", @@ -8794,9 +8070,9 @@ } }, "glob-parent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.0.0.tgz", - "integrity": "sha512-Z2RwiujPRGluePM6j699ktJYxmPpJKCfpGA13jz2hmFZC7gKetzrWvg5KN3+OsIFmydGyZ1AVwERCq1w/ZZwRg==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.0.tgz", + "integrity": "sha512-qjtRgnIVmOfnKUE3NJAQEdk+lKrxfw8t5ke7SXtfMTHcjsBfOfWXCQfdb30zfDoZQ2IRSIiidmjtbHZPZ++Ihw==", "requires": { "is-glob": "^4.0.1" } @@ -8884,9 +8160,9 @@ "integrity": "sha512-IItsdsea19BoLC7ELy13q1iJFNmd7ofZH5+X/pJr90/nRoPEX0DJo1dHDbgtYWOhJhcCgMDTOw84RZ72q6lB+Q==" }, "graphql": { - "version": "14.5.5", - "resolved": "https://registry.npmjs.org/graphql/-/graphql-14.5.5.tgz", - "integrity": "sha512-Ly+hS+HY+gHN79ee7KTP25ZLUJJTMfpOMoDa3LSRZNFUh7w0CLAAgzuyOpOZQFAPWFwooEuakixh0EbVe+vqAA==", + "version": "14.5.7", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-14.5.7.tgz", + "integrity": "sha512-as410RMJSUFqF8RcH2QWxZ5ioqHzsH9VWnWbaU+UnDXJ/6azMDIYPrtXCBPXd8rlunEVb7W8z6fuUnNHMbFu9A==", "requires": { "iterall": "^1.2.2" } @@ -9393,11 +8669,11 @@ "integrity": "sha1-ksnBN0w1CF912zWexWzCV8u5P6Q=" }, "http-proxy": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.17.0.tgz", - "integrity": "sha512-Taqn+3nNvYRfJ3bGvKfBSRwy1v6eePlm3oc/aWVxZp57DQr5Eq3xhKJi7Z4hZpS8PC3H4qI+Yly5EmFacGuA/g==", + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.0.tgz", + "integrity": "sha512-84I2iJM/n1d4Hdgc6y2+qY5mDaz2PUVjlg9znE9byl+q0uC3DeByqBGReQu5tpLK0TAqTIXScRUV+dg7+bUPpQ==", "requires": { - "eventemitter3": "^3.0.0", + "eventemitter3": "^4.0.0", "follow-redirects": "^1.0.0", "requires-port": "^1.0.0" } @@ -9779,9 +9055,9 @@ "integrity": "sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA==" }, "is-absolute-url": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-3.0.1.tgz", - "integrity": "sha512-c2QjUwuMxLsld90sj3xYzpFYWJtuxkIn1f5ua9RTEYJt/vV2IsM+Py00/6qjV7qExgifUvt7qfyBGBBKm+2iBg==" + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-3.0.2.tgz", + "integrity": "sha512-+5g/wLlcm1AcxSP7014m6GvbPHswDx980vD/3bZaap8aGV9Yfs7Q6y6tfaupgZ5O74Byzc8dGrSCJ+bFXx0KdA==" }, "is-accessor-descriptor": { "version": "0.1.6", @@ -10127,9 +9403,9 @@ "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==" }, "is-retry-allowed": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz", - "integrity": "sha1-EaBgVotnM5REAz0BJaYaINVk+zQ=" + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz", + "integrity": "sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg==" }, "is-root": { "version": "1.0.0", @@ -10210,9 +9486,9 @@ "integrity": "sha512-0wfcrFgOOOBdgRNT9H33xe6Zi6yhX/uoc4U8NBZGeQQB0ctU1dnlNTyL9JM2646bHDTpsDm1Brb3VPoCIMrd/A==" }, "is-wsl": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.1.0.tgz", - "integrity": "sha512-pFTjpv/x5HRj8kbZ/Msxi9VrvtOMRBqaDi3OIcbwPI3OuH+r3lLxVWukLITBaOGJIbA/w2+M1eVmVa4XNQlAmQ==" + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.1.1.tgz", + "integrity": "sha512-umZHcSrwlDHo2TGMXv0DZ8dIUGunZ2Iv68YZnrmCiBPkZ4aaOhtv7pXJKeki9k3qJ3RJr0cDyitcl5wEH3AYog==" }, "isarray": { "version": "1.0.0", @@ -11130,9 +10406,9 @@ "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==" }, "merge2": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.2.4.tgz", - "integrity": "sha512-FYE8xI+6pjFOhokZu0We3S5NKCirLbCzSh2Usf3qEyr4X8U+0jNg9P8RZ4qz+V2UoECLVwSyzU3LxXBaLGtD3A==" + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.3.0.tgz", + "integrity": "sha512-2j4DAdlBOkiSZIsaXk4mTE3sRS02yBHAtfy127xRV3bQUFqXkjHCHLW6Scv7DwNRbIWNHH8zpnz9zMaKXIdvYw==" }, "methods": { "version": "1.1.2", @@ -11554,9 +10830,9 @@ } }, "node-releases": { - "version": "1.1.28", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.28.tgz", - "integrity": "sha512-AQw4emh6iSXnCpDiFe0phYcThiccmkNWMZnFZ+lDJjAP8J0m2fVd59duvUUyuTirQOhIAajTFkzG6FHCLBO59g==", + "version": "1.1.32", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.32.tgz", + "integrity": "sha512-VhVknkitq8dqtWoluagsGPn3dxTvN9fwgR59fV3D7sLBHe0JfDramsMI8n8mY//ccq/Kkrf8ZRHRpsyVZ3qw1A==", "requires": { "semver": "^5.3.0" } @@ -11622,11 +10898,6 @@ "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-3.3.0.tgz", "integrity": "sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg==" }, - "normalize.css": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/normalize.css/-/normalize.css-8.0.1.tgz", - "integrity": "sha512-qizSNPO93t1YUuUhP22btGOo3chcvDFqFaj2TRybP0DMxkHOCTYwp3n34fel4a31ORXy4m1Xq0Gyqpb5m33qIg==" - }, "npm-run-path": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", @@ -11711,6 +10982,11 @@ "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-1.3.1.tgz", "integrity": "sha512-OSuu/pU4ENM9kmREg0BdNrUDIl1heYa4mBZacJc+vVWz4GtAwu7jO8s4AIt2aGRUTqxykpWzI3Oqnsm13tTMDA==" }, + "object-inspect": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.6.0.tgz", + "integrity": "sha512-GJzfBZ6DgDAmnuaM3104jR4s1Myxr3Y3zfIyN4z3UdqN69oSRacNK8UhnobDdC+7J2AHCjGwxQubNJfE70SXXQ==" + }, "object-is": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.0.1.tgz", @@ -11988,6 +11264,13 @@ "integrity": "sha512-6QfeouDf236N+MAxHch0CVIy8o/KBnmhttKjxZoOkUlzqU+u9rZgEyXH3OdckhTgawbqf5rpzmyR+07+Lv0+zg==", "requires": { "eventemitter3": "^3.1.0" + }, + "dependencies": { + "eventemitter3": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.2.tgz", + "integrity": "sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q==" + } } }, "p-retry": { @@ -12081,9 +11364,9 @@ } }, "parse-asn1": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.4.tgz", - "integrity": "sha512-Qs5duJcuvNExRfFZ99HDD3z4mAi3r9Wl/FOjEOijlxwCZs7E7mW2vjTpgQ4J8LpTF8x5v+1Vn5UQFejmWT11aw==", + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.5.tgz", + "integrity": "sha512-jkMYn1dcJqF6d5CpU689bq7w/b5ALS9ROVSpQDPrZsqqesUJii9qutvoT5ltGedNXMO2e16YUWIghG9KxaViTQ==", "requires": { "asn1.js": "^4.0.0", "browserify-aes": "^1.0.0", @@ -12401,9 +11684,9 @@ "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=" }, "postcss": { - "version": "7.0.17", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.17.tgz", - "integrity": "sha512-546ZowA+KZ3OasvQZHsbuEpysvwTZNGJv9EfyCQdsIDltPSWHAeTQ5fQy/Npi2ZDtLI3zs7Ps/p6wThErhm9fQ==", + "version": "7.0.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.18.tgz", + "integrity": "sha512-/7g1QXXgegpF+9GJj4iN7ChGF40sYuGYJ8WZu8DZWnmhQ/G36hfdk3q9LBJmoK+lZ+yzZ5KYpOoxq7LF1BxE8g==", "requires": { "chalk": "^2.4.2", "source-map": "^0.6.1", @@ -12465,19 +11748,6 @@ "node-releases": "^1.1.29" } }, - "electron-to-chromium": { - "version": "1.3.258", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.258.tgz", - "integrity": "sha512-rkPYrgFU7k/8ngjHYvzOZ44OQQ1GeIRIQnhGv00RkSlQXEnJKsGonQppbEEWHuuxZegpMao+WZmYraWQJQJMMg==" - }, - "node-releases": { - "version": "1.1.30", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.30.tgz", - "integrity": "sha512-BHcr1g6NeUH12IL+X3Flvs4IOnl1TL0JczUhEZjDE+FXXPQcVCNr8NEPb01zqGxzhTpdyJL5GXemaCW7aw6Khw==", - "requires": { - "semver": "^5.3.0" - } - }, "postcss-value-parser": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", @@ -12636,19 +11906,6 @@ "node-releases": "^1.1.29" } }, - "electron-to-chromium": { - "version": "1.3.258", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.258.tgz", - "integrity": "sha512-rkPYrgFU7k/8ngjHYvzOZ44OQQ1GeIRIQnhGv00RkSlQXEnJKsGonQppbEEWHuuxZegpMao+WZmYraWQJQJMMg==" - }, - "node-releases": { - "version": "1.1.30", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.30.tgz", - "integrity": "sha512-BHcr1g6NeUH12IL+X3Flvs4IOnl1TL0JczUhEZjDE+FXXPQcVCNr8NEPb01zqGxzhTpdyJL5GXemaCW7aw6Khw==", - "requires": { - "semver": "^5.3.0" - } - }, "postcss-selector-parser": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.1.tgz", @@ -12718,19 +11975,6 @@ "node-releases": "^1.1.29" } }, - "electron-to-chromium": { - "version": "1.3.258", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.258.tgz", - "integrity": "sha512-rkPYrgFU7k/8ngjHYvzOZ44OQQ1GeIRIQnhGv00RkSlQXEnJKsGonQppbEEWHuuxZegpMao+WZmYraWQJQJMMg==" - }, - "node-releases": { - "version": "1.1.30", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.30.tgz", - "integrity": "sha512-BHcr1g6NeUH12IL+X3Flvs4IOnl1TL0JczUhEZjDE+FXXPQcVCNr8NEPb01zqGxzhTpdyJL5GXemaCW7aw6Khw==", - "requires": { - "semver": "^5.3.0" - } - }, "postcss-value-parser": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", @@ -12979,19 +12223,6 @@ "node-releases": "^1.1.29" } }, - "electron-to-chromium": { - "version": "1.3.258", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.258.tgz", - "integrity": "sha512-rkPYrgFU7k/8ngjHYvzOZ44OQQ1GeIRIQnhGv00RkSlQXEnJKsGonQppbEEWHuuxZegpMao+WZmYraWQJQJMMg==" - }, - "node-releases": { - "version": "1.1.30", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.30.tgz", - "integrity": "sha512-BHcr1g6NeUH12IL+X3Flvs4IOnl1TL0JczUhEZjDE+FXXPQcVCNr8NEPb01zqGxzhTpdyJL5GXemaCW7aw6Khw==", - "requires": { - "semver": "^5.3.0" - } - }, "postcss-value-parser": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", @@ -13075,19 +12306,6 @@ "electron-to-chromium": "^1.3.247", "node-releases": "^1.1.29" } - }, - "electron-to-chromium": { - "version": "1.3.258", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.258.tgz", - "integrity": "sha512-rkPYrgFU7k/8ngjHYvzOZ44OQQ1GeIRIQnhGv00RkSlQXEnJKsGonQppbEEWHuuxZegpMao+WZmYraWQJQJMMg==" - }, - "node-releases": { - "version": "1.1.30", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.30.tgz", - "integrity": "sha512-BHcr1g6NeUH12IL+X3Flvs4IOnl1TL0JczUhEZjDE+FXXPQcVCNr8NEPb01zqGxzhTpdyJL5GXemaCW7aw6Khw==", - "requires": { - "semver": "^5.3.0" - } } } }, @@ -13665,9 +12883,9 @@ } }, "react-hot-loader": { - "version": "4.12.13", - "resolved": "https://registry.npmjs.org/react-hot-loader/-/react-hot-loader-4.12.13.tgz", - "integrity": "sha512-4Byk3aVQhcmTnVCBvDHOEOUnMFMj81r2yRKZQSfLOG2yd/4hm/A3oK15AnCZilQExqSFSsHcK64lIIU+dU2zQQ==", + "version": "4.12.14", + "resolved": "https://registry.npmjs.org/react-hot-loader/-/react-hot-loader-4.12.14.tgz", + "integrity": "sha512-ecxH4eBvEaJ9onT8vkEmK1FAAJUh1PqzGqds9S3k+GeihSp7nKAp4fOxytO+Ghr491LiBD38jaKyDXYnnpI9pQ==", "requires": { "fast-levenshtein": "^2.0.6", "global": "^4.3.0", @@ -13686,21 +12904,6 @@ } } }, - "react-icons": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-3.7.0.tgz", - "integrity": "sha512-7MyPwjIhuyW0D2N3s4DEd0hGPGFf0sK+IIRKhc1FvSpZNVmnUoGvHbmAwzGJU+3my+fvihVWgwU5SDtlAri56Q==", - "requires": { - "camelcase": "^5.0.0" - }, - "dependencies": { - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==" - } - } - }, "react-is": { "version": "16.9.0", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.9.0.tgz", @@ -13933,9 +13136,9 @@ } }, "regexp-tree": { - "version": "0.1.12", - "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.12.tgz", - "integrity": "sha512-TsXZ8+cv2uxMEkLfgwO0E068gsNMLfuYwMMhiUxf0Kw2Vcgzq93vgl6wIlIYuPmfMqMjfQ9zAporiozqCnwLuQ==" + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.13.tgz", + "integrity": "sha512-hwdV/GQY5F8ReLZWO+W1SRoN5YfpOKY6852+tBFcma72DKBIcHjPRIlIvQN35bCOljuAfP2G2iB0FC/w236mUw==" }, "regexp.prototype.flags": { "version": "1.2.0", @@ -13951,9 +13154,9 @@ "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==" }, "regexpu-core": { - "version": "4.5.5", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.5.5.tgz", - "integrity": "sha512-FpI67+ky9J+cDizQUJlIlNZFKual/lUkFr1AG6zOCpwZ9cLrg8UUVakyUQJD7fCDIe9Z2nwTQJNPyonatNmDFQ==", + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.6.0.tgz", + "integrity": "sha512-YlVaefl8P5BnFYOITTNzDvan1ulLOiXJzCNZxduTIosN17b87h3bvG9yHMoHaRuo88H4mQ06Aodj5VtYGGGiTg==", "requires": { "regenerate": "^1.4.0", "regenerate-unicode-properties": "^8.1.0", @@ -14138,100 +13341,6 @@ "is-alphabetical": "1.0.3", "remark-parse": "7.0.1", "unified": "8.3.2" - }, - "dependencies": { - "@babel/core": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.6.0.tgz", - "integrity": "sha512-FuRhDRtsd6IptKpHXAa+4WPZYY2ZzgowkbLBecEDDSje1X/apG7jQM33or3NdOmjXBKWGOg4JmSiRfUfuTtHXw==", - "requires": { - "@babel/code-frame": "^7.5.5", - "@babel/generator": "^7.6.0", - "@babel/helpers": "^7.6.0", - "@babel/parser": "^7.6.0", - "@babel/template": "^7.6.0", - "@babel/traverse": "^7.6.0", - "@babel/types": "^7.6.0", - "convert-source-map": "^1.1.0", - "debug": "^4.1.0", - "json5": "^2.1.0", - "lodash": "^4.17.13", - "resolve": "^1.3.2", - "semver": "^5.4.1", - "source-map": "^0.5.0" - } - }, - "@babel/generator": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.6.0.tgz", - "integrity": "sha512-Ms8Mo7YBdMMn1BYuNtKuP/z0TgEIhbcyB8HVR6PPNYp4P61lMsABiS4A3VG1qznjXVCf3r+fVHhm4efTYVsySA==", - "requires": { - "@babel/types": "^7.6.0", - "jsesc": "^2.5.1", - "lodash": "^4.17.13", - "source-map": "^0.5.0", - "trim-right": "^1.0.1" - } - }, - "@babel/helpers": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.6.0.tgz", - "integrity": "sha512-W9kao7OBleOjfXtFGgArGRX6eCP0UEcA2ZWEWNkJdRZnHhW4eEbeswbG3EwaRsnQUAEGWYgMq1HsIXuNNNy2eQ==", - "requires": { - "@babel/template": "^7.6.0", - "@babel/traverse": "^7.6.0", - "@babel/types": "^7.6.0" - } - }, - "@babel/parser": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.6.0.tgz", - "integrity": "sha512-+o2q111WEx4srBs7L9eJmcwi655eD8sXniLqMB93TBK9GrNzGrxDWSjiqz2hLU0Ha8MTXFIP0yd9fNdP+m43ZQ==" - }, - "@babel/template": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.6.0.tgz", - "integrity": "sha512-5AEH2EXD8euCk446b7edmgFdub/qfH1SN6Nii3+fyXP807QRx9Q73A2N5hNwRRslC2H9sNzaFhsPubkS4L8oNQ==", - "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/parser": "^7.6.0", - "@babel/types": "^7.6.0" - } - }, - "@babel/traverse": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.6.0.tgz", - "integrity": "sha512-93t52SaOBgml/xY74lsmt7xOR4ufYvhb5c5qiM6lu4J/dWGMAfAh6eKw4PjLes6DI6nQgearoxnFJk60YchpvQ==", - "requires": { - "@babel/code-frame": "^7.5.5", - "@babel/generator": "^7.6.0", - "@babel/helper-function-name": "^7.1.0", - "@babel/helper-split-export-declaration": "^7.4.4", - "@babel/parser": "^7.6.0", - "@babel/types": "^7.6.0", - "debug": "^4.1.0", - "globals": "^11.1.0", - "lodash": "^4.17.13" - } - }, - "@babel/types": { - "version": "7.6.1", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.6.1.tgz", - "integrity": "sha512-X7gdiuaCmA0uRjCmRtYJNAVCc/q+5xSgsfKJHqMN4iNLILX39677fJE1O40arPMh0TTtS9ItH67yre6c7k6t0g==", - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.13", - "to-fast-properties": "^2.0.0" - } - }, - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "requires": { - "ms": "^2.1.1" - } - } } }, "remark-parse": { @@ -15145,16 +14254,16 @@ } }, "socket.io": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-2.2.0.tgz", - "integrity": "sha512-wxXrIuZ8AILcn+f1B4ez4hJTPG24iNgxBBDaJfT6MsyOhVYiTXWexGoPkd87ktJG8kQEcL/NBvRi64+9k4Kc0w==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-2.3.0.tgz", + "integrity": "sha512-2A892lrj0GcgR/9Qk81EaY2gYhCBxurV0PfmmESO6p27QPrUK1J3zdns+5QPqvUYK2q657nSj0guoIil9+7eFg==", "requires": { "debug": "~4.1.0", - "engine.io": "~3.3.1", + "engine.io": "~3.4.0", "has-binary2": "~1.0.2", "socket.io-adapter": "~1.1.0", - "socket.io-client": "2.2.0", - "socket.io-parser": "~3.3.0" + "socket.io-client": "2.3.0", + "socket.io-parser": "~3.4.0" }, "dependencies": { "debug": { @@ -15173,16 +14282,16 @@ "integrity": "sha1-KoBeihTWNyEk3ZFZrUUC+MsH8Gs=" }, "socket.io-client": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.2.0.tgz", - "integrity": "sha512-56ZrkTDbdTLmBIyfFYesgOxsjcLnwAKoN4CiPyTVkMQj3zTUh0QAx3GbvIvLpFEOvQWu92yyWICxB0u7wkVbYA==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.3.0.tgz", + "integrity": "sha512-cEQQf24gET3rfhxZ2jJ5xzAOo/xhZwK+mOqtGRg5IowZsMgwvHwnf/mCRapAAkadhM26y+iydgwsXGObBB5ZdA==", "requires": { "backo2": "1.0.2", "base64-arraybuffer": "0.1.5", "component-bind": "1.0.0", "component-emitter": "1.2.1", - "debug": "~3.1.0", - "engine.io-client": "~3.3.1", + "debug": "~4.1.0", + "engine.io-client": "~3.4.0", "has-binary2": "~1.0.2", "has-cors": "1.1.0", "indexof": "0.0.1", @@ -15199,27 +14308,52 @@ "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=" }, "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", "requires": { - "ms": "2.0.0" + "ms": "^2.1.1" } }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + "isarray": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", + "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=" + }, + "socket.io-parser": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.3.0.tgz", + "integrity": "sha512-hczmV6bDgdaEbVqhAeVMM/jfUfzuEZHsQg6eOmLgJht6G3mPKMxYm75w2+qhAQZ+4X+1+ATZ+QFKeOZD5riHng==", + "requires": { + "component-emitter": "1.2.1", + "debug": "~3.1.0", + "isarray": "2.0.1" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } } } }, "socket.io-parser": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.3.0.tgz", - "integrity": "sha512-hczmV6bDgdaEbVqhAeVMM/jfUfzuEZHsQg6eOmLgJht6G3mPKMxYm75w2+qhAQZ+4X+1+ATZ+QFKeOZD5riHng==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.4.0.tgz", + "integrity": "sha512-/G/VOI+3DBp0+DJKW4KesGnQkQPFmUCbA/oO2QGT6CWxU7hLGWqU3tyuzeSK/dqcyeHsQg1vTe9jiZI8GU9SCQ==", "requires": { "component-emitter": "1.2.1", - "debug": "~3.1.0", + "debug": "~4.1.0", "isarray": "2.0.1" }, "dependencies": { @@ -15229,22 +14363,17 @@ "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=" }, "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", "requires": { - "ms": "2.0.0" + "ms": "^2.1.1" } }, "isarray": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=" - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" } } }, @@ -15491,11 +14620,11 @@ "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==" }, "stack-generator": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/stack-generator/-/stack-generator-2.0.3.tgz", - "integrity": "sha512-kdzGoqrnqsMxOEuXsXyQTmvWXZmG0f3Ql2GDx5NtmZs59sT2Bt9Vdyq0XdtxUi58q/+nxtbF9KOQ9HkV1QznGg==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/stack-generator/-/stack-generator-2.0.4.tgz", + "integrity": "sha512-ha1gosTNcgxwzo9uKTQ8zZ49aUp5FIUW58YHFxCqaAHtE0XqBg0chGFYA1MfmW//x1KWq3F4G7Ug7bJh4RiRtg==", "requires": { - "stackframe": "^1.0.4" + "stackframe": "^1.1.0" } }, "stack-trace": { @@ -15509,17 +14638,17 @@ "integrity": "sha512-MTX+MeG5U994cazkjd/9KNAapsHnibjMLnfXodlkXw76JEea0UiNzrqidzo1emMwk7w5Qhc9jd4Bn9TBb1MFwA==" }, "stackframe": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.0.4.tgz", - "integrity": "sha512-to7oADIniaYwS3MhtCa/sQhrxidCCQiF/qp4/m5iN3ipf0Y7Xlri0f6eG29r08aL7JYl8n32AF3Q5GYBZ7K8vw==" + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.1.0.tgz", + "integrity": "sha512-Vx6W1Yvy+AM1R/ckVwcHQHV147pTPBKWCRLrXMuPrFVfvBUc3os7PR1QLIWCMhPpRg5eX9ojzbQIMLGBwyLjqg==" }, "stacktrace-gps": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/stacktrace-gps/-/stacktrace-gps-3.0.2.tgz", - "integrity": "sha512-9o+nWhiz5wFnrB3hBHs2PTyYrS60M1vvpSzHxwxnIbtY2q9Nt51hZvhrG1+2AxD374ecwyS+IUwfkHRE/2zuGg==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/stacktrace-gps/-/stacktrace-gps-3.0.3.tgz", + "integrity": "sha512-51Rr7dXkyFUKNmhY/vqZWK+EvdsfFSRiQVtgHTFlAdNIYaDD7bVh21yBHXaNWAvTD+w+QSjxHg7/v6Tz4veExA==", "requires": { "source-map": "0.5.6", - "stackframe": "^1.0.4" + "stackframe": "^1.1.0" }, "dependencies": { "source-map": { @@ -15530,13 +14659,13 @@ } }, "stacktrace-js": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/stacktrace-js/-/stacktrace-js-2.0.0.tgz", - "integrity": "sha1-d2ymRqlbxsayuQd2U2p/xyxt21g=", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/stacktrace-js/-/stacktrace-js-2.0.1.tgz", + "integrity": "sha512-13oDNgBSeWtdGa4/2BycNyKqe+VktCoJ8VLx4pDoJkwGGJVtiHdfMOAj3aW9xTi8oR2v34z9IcvfCvT6XNdNAw==", "requires": { - "error-stack-parser": "^2.0.1", - "stack-generator": "^2.0.1", - "stacktrace-gps": "^3.0.1" + "error-stack-parser": "^2.0.4", + "stack-generator": "^2.0.4", + "stacktrace-gps": "^3.0.3" } }, "state-toggle": { @@ -15596,11 +14725,6 @@ "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" }, - "store": { - "version": "2.0.12", - "resolved": "https://registry.npmjs.org/store/-/store-2.0.12.tgz", - "integrity": "sha1-jFNOKguDH3K3X8XxEZhXxE711ZM=" - }, "stream-browserify": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz", @@ -15732,6 +14856,24 @@ } } }, + "string.prototype.trimleft": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.0.tgz", + "integrity": "sha512-FJ6b7EgdKxxbDxc79cOlok6Afd++TTs5szo+zJTUyow3ycrRfJVE2pq3vcN53XexvKZu/DJMDfeI/qMiZTrjTw==", + "requires": { + "define-properties": "^1.1.3", + "function-bind": "^1.1.1" + } + }, + "string.prototype.trimright": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.0.tgz", + "integrity": "sha512-fXZTSV55dNBwv16uw+hh5jkghxSnc5oHq+5K/gXgizHwAvMetdAJlHqqoFC1FSDVPYWLkAKl2cxpUT41sV7nSg==", + "requires": { + "define-properties": "^1.1.3", + "function-bind": "^1.1.1" + } + }, "string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", @@ -15838,19 +14980,6 @@ "node-releases": "^1.1.29" } }, - "electron-to-chromium": { - "version": "1.3.258", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.258.tgz", - "integrity": "sha512-rkPYrgFU7k/8ngjHYvzOZ44OQQ1GeIRIQnhGv00RkSlQXEnJKsGonQppbEEWHuuxZegpMao+WZmYraWQJQJMMg==" - }, - "node-releases": { - "version": "1.1.30", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.30.tgz", - "integrity": "sha512-BHcr1g6NeUH12IL+X3Flvs4IOnl1TL0JczUhEZjDE+FXXPQcVCNr8NEPb01zqGxzhTpdyJL5GXemaCW7aw6Khw==", - "requires": { - "semver": "^5.3.0" - } - }, "postcss-selector-parser": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.1.tgz", @@ -16099,6 +15228,11 @@ "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==", "optional": true }, + "tinycolor2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.4.1.tgz", + "integrity": "sha1-9PrTM0R7wLB9TcjpIJ2POaisd+g=" + }, "title-case": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/title-case/-/title-case-2.1.1.tgz", @@ -16323,6 +15457,14 @@ "shelljs": "^0.8.3", "typedoc-default-themes": "^0.6.0", "typescript": "3.5.x" + }, + "dependencies": { + "typescript": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.5.3.tgz", + "integrity": "sha512-ACzBtm/PhXBDId6a6sDJfroT2pOWt/oOnk4/dElG5G33ZL776N3Y6/6bKZJBFpd+b05F3Ct9qDjMeJmRWtE2/g==", + "dev": true + } } }, "typedoc-default-themes": { @@ -16338,9 +15480,9 @@ } }, "typescript": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.5.3.tgz", - "integrity": "sha512-ACzBtm/PhXBDId6a6sDJfroT2pOWt/oOnk4/dElG5G33ZL776N3Y6/6bKZJBFpd+b05F3Ct9qDjMeJmRWtE2/g==", + "version": "3.6.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.6.3.tgz", + "integrity": "sha512-N7bceJL1CtRQ2RiG0AQME13ksR7DiuQh/QehubYcghzv20tnh+MQnQIuJddTmsbqYj+dztchykemz0zFzlvdQw==", "dev": true }, "ua-parser-js": { @@ -17066,13 +16208,13 @@ } }, "webpack-dev-server": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.8.0.tgz", - "integrity": "sha512-Hs8K9yI6pyMvGkaPTeTonhD6JXVsigXDApYk9JLW4M7viVBspQvb1WdAcWxqtmttxNW4zf2UFLsLNe0y87pIGQ==", + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.8.1.tgz", + "integrity": "sha512-9F5DnfFA9bsrhpUCAfQic/AXBVHvq+3gQS+x6Zj0yc1fVVE0erKh2MV4IV12TBewuTrYeeTIRwCH9qLMvdNvTw==", "requires": { "ansi-html": "0.0.7", "bonjour": "^3.5.0", - "chokidar": "^2.1.6", + "chokidar": "^2.1.8", "compression": "^1.7.4", "connect-history-api-fallback": "^1.6.0", "debug": "^4.1.1", @@ -17083,23 +16225,23 @@ "import-local": "^2.0.0", "internal-ip": "^4.3.0", "ip": "^1.1.5", - "is-absolute-url": "^3.0.0", + "is-absolute-url": "^3.0.2", "killable": "^1.0.1", - "loglevel": "^1.6.3", + "loglevel": "^1.6.4", "opn": "^5.5.0", "p-retry": "^3.0.1", - "portfinder": "^1.0.21", + "portfinder": "^1.0.24", "schema-utils": "^1.0.0", - "selfsigned": "^1.10.4", + "selfsigned": "^1.10.6", "semver": "^6.3.0", "serve-index": "^1.9.1", "sockjs": "0.3.19", - "sockjs-client": "1.3.0", + "sockjs-client": "1.4.0", "spdy": "^4.0.1", "strip-ansi": "^3.0.1", "supports-color": "^6.1.0", "url": "^0.11.0", - "webpack-dev-middleware": "^3.7.0", + "webpack-dev-middleware": "^3.7.1", "webpack-log": "^2.0.0", "ws": "^6.2.1", "yargs": "12.0.5" @@ -17367,9 +16509,9 @@ "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" }, "sockjs-client": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.3.0.tgz", - "integrity": "sha512-R9jxEzhnnrdxLCNln0xg5uGHqMnkhPSTzUZH2eXcR03S/On9Yvoq2wyUZILRUhZCNVu2PmwWVoyuiPz8th8zbg==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.4.0.tgz", + "integrity": "sha512-5zaLyO8/nri5cua0VtOrFXBPK1jbL4+1cebT/mmKA1E1ZXOvJrII75bPu0l0k843G/+iAbhEqzyKr0w/eCCj7g==", "requires": { "debug": "^3.2.5", "eventsource": "^1.0.7", @@ -17605,11 +16747,11 @@ } }, "ws": { - "version": "6.1.4", - "resolved": "https://registry.npmjs.org/ws/-/ws-6.1.4.tgz", - "integrity": "sha512-eqZfL+NE/YQc1/ZynhojeV8q+H050oR8AZ2uIev7RU10svA9ZnJUddHcOUZTJLinZ9yEfdA2kSATS2qZK5fhJA==", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.1.2.tgz", + "integrity": "sha512-gftXq3XI81cJCgkUiAVixA0raD9IVmXqsylCrjRygw4+UOOGzPoxnQ6r/CnVL9i+mDncJo94tSkyrtuuQVBmrg==", "requires": { - "async-limiter": "~1.0.0" + "async-limiter": "^1.0.0" } }, "x-is-string": { diff --git a/docs/package.json b/docs/package.json index 84ac1d58dcf..d6399ba87e1 100644 --- a/docs/package.json +++ b/docs/package.json @@ -6,13 +6,13 @@ "types": "cd .. && typedoc --json ./docs/docs.json --ignoreCompilerErrors ./src/index.ts" }, "dependencies": { - "gatsby": "2.15.15", - "gatsby-theme-apollo-docs": "2.0.3", + "gatsby": "2.15.21", + "gatsby-theme-apollo-docs": "2.1.5", "react": "16.9.0", "react-dom": "16.9.0" }, "devDependencies": { "typedoc": "0.15.0", - "typescript": "3.5.3" + "typescript": "3.6.3" } } From a36780dc6fb9e86204ab43275b68fe4ccd20ab17 Mon Sep 17 00:00:00 2001 From: hwillson Date: Mon, 23 Sep 2019 09:30:16 -0400 Subject: [PATCH 21/28] Update fetchOptions to point to new location --- docs/source/essentials/local-state.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/essentials/local-state.mdx b/docs/source/essentials/local-state.mdx index 6a4ecf1c558..df04b4e568c 100644 --- a/docs/source/essentials/local-state.mdx +++ b/docs/source/essentials/local-state.mdx @@ -725,7 +725,7 @@ Pulling `@client` field values directly out of the cache isn't quite as flexible ### Working with fetch policies -Before Apollo Client executes a query, one of the first things it does is check to see which [`fetchPolicy`](/api/core/#ApolloClient.query) it has been configured to use. It does this so it knows where it should attempt to resolve the query from first, either the cache or the network. When running a query, Apollo Client treats `@client` based local resolvers just like it does remote resolvers, in that it will adhere to its defined `fetchPolicy` to know where to attempt to pull data from first. When working with local resolvers, it's important to understand how fetch policies impact the running of resolver functions, since by default local resolver functions are not run on every request. This is because the result of running a local resolver is cached with the rest of the query result, and pulled from the cache on the next request. Let's look at an example: +Before Apollo Client executes a query, one of the first things it does is check to see which [`fetchPolicy`](/api/core/#apolloclient-functions) it has been configured to use. It does this so it knows where it should attempt to resolve the query from first, either the cache or the network. When running a query, Apollo Client treats `@client` based local resolvers just like it does remote resolvers, in that it will adhere to its defined `fetchPolicy` to know where to attempt to pull data from first. When working with local resolvers, it's important to understand how fetch policies impact the running of resolver functions, since by default local resolver functions are not run on every request. This is because the result of running a local resolver is cached with the rest of the query result, and pulled from the cache on the next request. Let's look at an example:
From e6e268c3c322f75054f108538fb149c2ed832c8e Mon Sep 17 00:00:00 2001 From: Ben Newman Date: Mon, 23 Sep 2019 10:29:18 -0400 Subject: [PATCH 22/28] Revert "Pin optimism to 0.11.1" This reverts commit 68195a930b51bcbbe9bf7bc79a6826f761ccdb25. https://github.com/benjamn/optimism/pull/52 --- package-lock.json | 138 +++++++++++++++++++++++----------------------- package.json | 2 +- 2 files changed, 70 insertions(+), 70 deletions(-) diff --git a/package-lock.json b/package-lock.json index e249d5b581c..5e1fa227c91 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2981,28 +2981,28 @@ "dependencies": { "abbrev": { "version": "1.1.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", "dev": true, "optional": true }, "ansi-regex": { "version": "2.1.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", "dev": true, "optional": true }, "aproba": { "version": "1.2.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", "dev": true, "optional": true }, "are-we-there-yet": { "version": "1.1.5", - "resolved": false, + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", "dev": true, "optional": true, @@ -3013,14 +3013,14 @@ }, "balanced-match": { "version": "1.0.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", "dev": true, "optional": true }, "brace-expansion": { "version": "1.1.11", - "resolved": false, + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "optional": true, @@ -3031,42 +3031,42 @@ }, "chownr": { "version": "1.1.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.1.tgz", "integrity": "sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g==", "dev": true, "optional": true }, "code-point-at": { "version": "1.1.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", "dev": true, "optional": true }, "concat-map": { "version": "0.0.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "dev": true, "optional": true }, "console-control-strings": { "version": "1.1.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", "dev": true, "optional": true }, "core-util-is": { "version": "1.0.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", "dev": true, "optional": true }, "debug": { "version": "4.1.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", "dev": true, "optional": true, @@ -3076,28 +3076,28 @@ }, "deep-extend": { "version": "0.6.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", "dev": true, "optional": true }, "delegates": { "version": "1.0.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", "dev": true, "optional": true }, "detect-libc": { "version": "1.0.3", - "resolved": false, + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", "dev": true, "optional": true }, "fs-minipass": { "version": "1.2.5", - "resolved": false, + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.5.tgz", "integrity": "sha512-JhBl0skXjUPCFH7x6x61gQxrKyXsxB5gcgePLZCwfyCGGsTISMoIeObbrvVeP6Xmyaudw4TT43qV2Gz+iyd2oQ==", "dev": true, "optional": true, @@ -3107,14 +3107,14 @@ }, "fs.realpath": { "version": "1.0.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "dev": true, "optional": true }, "gauge": { "version": "2.7.4", - "resolved": false, + "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", "dev": true, "optional": true, @@ -3131,7 +3131,7 @@ }, "glob": { "version": "7.1.3", - "resolved": false, + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", "dev": true, "optional": true, @@ -3146,14 +3146,14 @@ }, "has-unicode": { "version": "2.0.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", "dev": true, "optional": true }, "iconv-lite": { "version": "0.4.24", - "resolved": false, + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", "dev": true, "optional": true, @@ -3163,7 +3163,7 @@ }, "ignore-walk": { "version": "3.0.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.1.tgz", "integrity": "sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ==", "dev": true, "optional": true, @@ -3173,7 +3173,7 @@ }, "inflight": { "version": "1.0.6", - "resolved": false, + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", "dev": true, "optional": true, @@ -3184,21 +3184,21 @@ }, "inherits": { "version": "2.0.3", - "resolved": false, + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", "dev": true, "optional": true }, "ini": { "version": "1.3.5", - "resolved": false, + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", "dev": true, "optional": true }, "is-fullwidth-code-point": { "version": "1.0.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", "dev": true, "optional": true, @@ -3208,14 +3208,14 @@ }, "isarray": { "version": "1.0.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", "dev": true, "optional": true }, "minimatch": { "version": "3.0.4", - "resolved": false, + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, "optional": true, @@ -3225,14 +3225,14 @@ }, "minimist": { "version": "0.0.8", - "resolved": false, + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", "dev": true, "optional": true }, "minipass": { "version": "2.3.5", - "resolved": false, + "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.3.5.tgz", "integrity": "sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==", "dev": true, "optional": true, @@ -3243,7 +3243,7 @@ }, "minizlib": { "version": "1.2.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.2.1.tgz", "integrity": "sha512-7+4oTUOWKg7AuL3vloEWekXY2/D20cevzsrNT2kGWm+39J9hGTCBv8VI5Pm5lXZ/o3/mdR4f8rflAPhnQb8mPA==", "dev": true, "optional": true, @@ -3253,7 +3253,7 @@ }, "mkdirp": { "version": "0.5.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", "dev": true, "optional": true, @@ -3263,14 +3263,14 @@ }, "ms": { "version": "2.1.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", "dev": true, "optional": true }, "needle": { "version": "2.3.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/needle/-/needle-2.3.0.tgz", "integrity": "sha512-QBZu7aAFR0522EyaXZM0FZ9GLpq6lvQ3uq8gteiDUp7wKdy0lSd2hPlgFwVuW1CBkfEs9PfDQsQzZghLs/psdg==", "dev": true, "optional": true, @@ -3282,7 +3282,7 @@ }, "node-pre-gyp": { "version": "0.12.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.12.0.tgz", "integrity": "sha512-4KghwV8vH5k+g2ylT+sLTjy5wmUOb9vPhnM8NHvRf9dHmnW/CndrFXy2aRPaPST6dugXSdHXfeaHQm77PIz/1A==", "dev": true, "optional": true, @@ -3301,7 +3301,7 @@ }, "nopt": { "version": "4.0.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz", "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=", "dev": true, "optional": true, @@ -3312,14 +3312,14 @@ }, "npm-bundled": { "version": "1.0.6", - "resolved": false, + "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.0.6.tgz", "integrity": "sha512-8/JCaftHwbd//k6y2rEWp6k1wxVfpFzB6t1p825+cUb7Ym2XQfhwIC5KwhrvzZRJu+LtDE585zVaS32+CGtf0g==", "dev": true, "optional": true }, "npm-packlist": { "version": "1.4.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.1.tgz", "integrity": "sha512-+TcdO7HJJ8peiiYhvPxsEDhF3PJFGUGRcFsGve3vxvxdcpO2Z4Z7rkosRM0kWj6LfbK/P0gu3dzk5RU1ffvFcw==", "dev": true, "optional": true, @@ -3330,7 +3330,7 @@ }, "npmlog": { "version": "4.1.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", "dev": true, "optional": true, @@ -3343,21 +3343,21 @@ }, "number-is-nan": { "version": "1.0.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", "dev": true, "optional": true }, "object-assign": { "version": "4.1.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", "dev": true, "optional": true }, "once": { "version": "1.4.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "dev": true, "optional": true, @@ -3367,21 +3367,21 @@ }, "os-homedir": { "version": "1.0.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", "dev": true, "optional": true }, "os-tmpdir": { "version": "1.0.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", "dev": true, "optional": true }, "osenv": { "version": "0.1.5", - "resolved": false, + "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", "dev": true, "optional": true, @@ -3392,21 +3392,21 @@ }, "path-is-absolute": { "version": "1.0.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "dev": true, "optional": true }, "process-nextick-args": { "version": "2.0.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", "dev": true, "optional": true }, "rc": { "version": "1.2.8", - "resolved": false, + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", "dev": true, "optional": true, @@ -3419,7 +3419,7 @@ "dependencies": { "minimist": { "version": "1.2.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "dev": true, "optional": true @@ -3428,7 +3428,7 @@ }, "readable-stream": { "version": "2.3.6", - "resolved": false, + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "optional": true, @@ -3444,7 +3444,7 @@ }, "rimraf": { "version": "2.6.3", - "resolved": false, + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", "dev": true, "optional": true, @@ -3454,49 +3454,49 @@ }, "safe-buffer": { "version": "5.1.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "dev": true, "optional": true }, "safer-buffer": { "version": "2.1.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "dev": true, "optional": true }, "sax": { "version": "1.2.4", - "resolved": false, + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", "dev": true, "optional": true }, "semver": { "version": "5.7.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", "dev": true, "optional": true }, "set-blocking": { "version": "2.0.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", "dev": true, "optional": true }, "signal-exit": { "version": "3.0.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", "dev": true, "optional": true }, "string-width": { "version": "1.0.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "dev": true, "optional": true, @@ -3508,7 +3508,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "optional": true, @@ -3518,7 +3518,7 @@ }, "strip-ansi": { "version": "3.0.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "dev": true, "optional": true, @@ -3528,14 +3528,14 @@ }, "strip-json-comments": { "version": "2.0.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", "dev": true, "optional": true }, "tar": { "version": "4.4.8", - "resolved": false, + "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.8.tgz", "integrity": "sha512-LzHF64s5chPQQS0IYBn9IN5h3i98c12bo4NCO7e0sGM2llXQ3p2FGC5sdENN4cTW48O915Sh+x+EXx7XW96xYQ==", "dev": true, "optional": true, @@ -3551,14 +3551,14 @@ }, "util-deprecate": { "version": "1.0.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", "dev": true, "optional": true }, "wide-align": { "version": "1.1.3", - "resolved": false, + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", "dev": true, "optional": true, @@ -3568,14 +3568,14 @@ }, "wrappy": { "version": "1.0.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", "dev": true, "optional": true }, "yallist": { "version": "3.0.3", - "resolved": false, + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz", "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==", "dev": true, "optional": true @@ -6506,9 +6506,9 @@ } }, "optimism": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/optimism/-/optimism-0.11.1.tgz", - "integrity": "sha512-EVUZtx/BKcmZbqE8u7Ai+83FiUS2sLD4ZsmBlS1Pgf+1d5pfLXtX4lA9219OG6cUd77AYxgK9+TBV1myD0QCbQ==", + "version": "0.11.3", + "resolved": "https://registry.npmjs.org/optimism/-/optimism-0.11.3.tgz", + "integrity": "sha512-3dbru7TR4GTRxEqkkv+Ql7yDwqZRGnnSoYzoP/fuO20Ftt8eWufOsjXKFTLbbVADXes+BPLzm7D8mGLt3rqm7A==", "requires": { "@wry/context": "^0.4.0" } diff --git a/package.json b/package.json index 169dcb4effe..2dd60d96b91 100644 --- a/package.json +++ b/package.json @@ -60,7 +60,7 @@ "@wry/equality": "^0.1.9", "apollo-link": "^1.2.13", "fast-json-stable-stringify": "^2.0.0", - "optimism": "0.11.1", + "optimism": "^0.11.3", "symbol-observable": "^1.2.0", "ts-invariant": "^0.4.4", "tslib": "^1.10.0", From 7ee82b53e38a8333ab62a43db9803cb88aa3aaae Mon Sep 17 00:00:00 2001 From: hwillson Date: Mon, 23 Sep 2019 14:48:30 -0400 Subject: [PATCH 23/28] `react-dom` no longer needs to be a peer dep On the React Apollo side, it's only required when using `@apollo/react-ssr`. Since we're not merging in the RA SSR code yet, we don't need it as a peer dep. --- package.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/package.json b/package.json index 2dd60d96b91..0fc2e12ee8f 100644 --- a/package.json +++ b/package.json @@ -52,8 +52,7 @@ "peerDependencies": { "@types/react": "^16.8.0", "graphql": "^14.5.4", - "react": "^16.8.0", - "react-dom": "^16.8.0" + "react": "^16.8.0" }, "dependencies": { "@types/zen-observable": "^0.8.0", From b6ab68d8c391c97f9a2740e8a511bcb100226fe2 Mon Sep 17 00:00:00 2001 From: hwillson Date: Mon, 23 Sep 2019 14:56:14 -0400 Subject: [PATCH 24/28] Remove unnecessary `await`s in tests `wait` already returns a promise so we can just return it. --- .../hooks/__tests__/useLazyQuery.test.tsx | 14 +++---- .../hooks/__tests__/useMutation.test.tsx | 12 +++--- src/react/hooks/__tests__/useQuery.test.tsx | 38 +++++++++---------- .../hooks/__tests__/useSubscription.test.tsx | 8 ++-- 4 files changed, 36 insertions(+), 36 deletions(-) diff --git a/src/react/hooks/__tests__/useLazyQuery.test.tsx b/src/react/hooks/__tests__/useLazyQuery.test.tsx index 52b82355aa9..977c1be61c1 100644 --- a/src/react/hooks/__tests__/useLazyQuery.test.tsx +++ b/src/react/hooks/__tests__/useLazyQuery.test.tsx @@ -70,7 +70,7 @@ describe('useLazyQuery Hook', () => { ); - await wait(() => { + return wait(() => { expect(renderCount).toBe(3); }); }); @@ -123,7 +123,7 @@ describe('useLazyQuery Hook', () => { ); - await wait(() => { + return wait(() => { expect(renderCount).toBe(3); }); }); @@ -160,7 +160,7 @@ describe('useLazyQuery Hook', () => { ); - await wait(() => { + return wait(() => { expect(renderCount).toBe(3); }); }); @@ -233,7 +233,7 @@ describe('useLazyQuery Hook', () => { ); - await wait(() => { + return wait(() => { expect(renderCount).toBe(3); }); } @@ -307,7 +307,7 @@ describe('useLazyQuery Hook', () => { ); - await wait(() => { + return wait(() => { expect(renderCount).toBe(3); }); } @@ -386,7 +386,7 @@ describe('useLazyQuery Hook', () => { ); - await wait(() => { + return wait(() => { expect(renderCount).toBe(5); }); } @@ -444,7 +444,7 @@ describe('useLazyQuery Hook', () => { ); - await wait(() => { + return wait(() => { expect(onCompletedCount).toBe(2); expect(renderCount).toBe(5); }); diff --git a/src/react/hooks/__tests__/useMutation.test.tsx b/src/react/hooks/__tests__/useMutation.test.tsx index 21b371971bf..080065346ec 100644 --- a/src/react/hooks/__tests__/useMutation.test.tsx +++ b/src/react/hooks/__tests__/useMutation.test.tsx @@ -84,7 +84,7 @@ describe('useMutation Hook', () => { ); - await wait(() => { + return wait(() => { expect(renderCount).toBe(3); }); }); @@ -144,7 +144,7 @@ describe('useMutation Hook', () => { ); - await wait(() => { + return wait(() => { expect(renderCount).toBe(3); }); }); @@ -200,7 +200,7 @@ describe('useMutation Hook', () => { ); - await wait(() => { + return wait(() => { expect(renderCount).toBe(3); }); }); @@ -246,7 +246,7 @@ describe('useMutation Hook', () => { ); - await wait(); + return wait(); }); it('should return the current client instance in the result object', async () => { @@ -263,7 +263,7 @@ describe('useMutation Hook', () => { ); - await wait(); + return wait(); }); }); @@ -339,7 +339,7 @@ describe('useMutation Hook', () => { ); - await wait(() => { + return wait(() => { expect(renderCount).toBe(3); }); }); diff --git a/src/react/hooks/__tests__/useQuery.test.tsx b/src/react/hooks/__tests__/useQuery.test.tsx index c2f16e6ed6f..0693a0131e8 100644 --- a/src/react/hooks/__tests__/useQuery.test.tsx +++ b/src/react/hooks/__tests__/useQuery.test.tsx @@ -59,7 +59,7 @@ describe('useQuery Hook', () => { ); - await wait(); + return wait(); }); it('should keep data as undefined until data is actually returned', async () => { @@ -79,7 +79,7 @@ describe('useQuery Hook', () => { ); - await wait(); + return wait(); }); it('should ensure ObservableQuery fields have a stable identity', async () => { @@ -123,7 +123,7 @@ describe('useQuery Hook', () => { ); - await wait(); + return wait(); }); }); @@ -162,7 +162,7 @@ describe('useQuery Hook', () => { ); - await wait(() => { + return wait(() => { expect(renderCount).toBe(3); }); }); @@ -208,7 +208,7 @@ describe('useQuery Hook', () => { ); - await wait(() => { + return wait(() => { expect(renderCount).toBe(4); }); }); @@ -252,7 +252,7 @@ describe('useQuery Hook', () => { ); - await wait(() => { + return wait(() => { expect(linkRequestSpy).toHaveBeenCalledTimes(2); }) }); @@ -292,7 +292,7 @@ describe('useQuery Hook', () => { ).unmount; - await wait(() => { + return wait(() => { expect(renderCount).toBe(2); }); } @@ -348,7 +348,7 @@ describe('useQuery Hook', () => { ); - await wait(); + return wait(); }); it('should only call onError callbacks once', async () => { @@ -424,7 +424,7 @@ describe('useQuery Hook', () => { ); - await wait(() => { + return wait(() => { expect(renderCount).toBe(4); }); }); @@ -481,7 +481,7 @@ describe('useQuery Hook', () => { ); - await wait(() => { + return wait(() => { expect(renderCount).toBe(3); }); }); @@ -544,7 +544,7 @@ describe('useQuery Hook', () => { ); - await wait(() => { + return wait(() => { expect(renderCount).toBe(3); }); } @@ -615,7 +615,7 @@ describe('useQuery Hook', () => { ); - await wait(() => { + return wait(() => { expect(renderCount).toBe(4); }); }); @@ -685,7 +685,7 @@ describe('useQuery Hook', () => { ); - await wait(() => { + return wait(() => { expect(renderCount).toBe(4); }); }); @@ -765,7 +765,7 @@ describe('useQuery Hook', () => { ); - await wait(() => { + return wait(() => { expect(renderCount).toBe(6); }); }); @@ -868,7 +868,7 @@ describe('useQuery Hook', () => { ); - await wait(() => { + return wait(() => { expect(renderCount).toBe(4); }); } @@ -967,7 +967,7 @@ describe('useQuery Hook', () => { ); - await wait(() => { + return wait(() => { expect(renderCount).toBe(3); }); } @@ -1070,7 +1070,7 @@ describe('useQuery Hook', () => { ); - await wait(() => { + return wait(() => { expect(renderCount).toBe(6); }); }); @@ -1113,7 +1113,7 @@ describe('useQuery Hook', () => { ); - await wait(() => { + return wait(() => { expect(onCompletedCalled).toBeTruthy(); }); } @@ -1151,7 +1151,7 @@ describe('useQuery Hook', () => { ); - await wait(() => { + return wait(() => { expect(onCompletedCount).toBe(1); }); }); diff --git a/src/react/hooks/__tests__/useSubscription.test.tsx b/src/react/hooks/__tests__/useSubscription.test.tsx index 181b6fbd465..ccb642ed458 100644 --- a/src/react/hooks/__tests__/useSubscription.test.tsx +++ b/src/react/hooks/__tests__/useSubscription.test.tsx @@ -71,7 +71,7 @@ describe('useSubscription Hook', () => { ); - await wait(() => { + return wait(() => { expect(renderCount).toBe(5); }); }); @@ -141,7 +141,7 @@ describe('useSubscription Hook', () => { ).unmount; - await wait(() => { + return wait(() => { expect(onSubscriptionDataCount).toEqual(1); }); }); @@ -193,7 +193,7 @@ describe('useSubscription Hook', () => { ).unmount; - await wait(() => { + return wait(() => { expect(onSubscriptionDataCount).toEqual(0); expect(renderCount).toEqual(1); }); @@ -289,7 +289,7 @@ describe('useSubscription Hook', () => { ).unmount; - await wait(() => { + return wait(() => { expect(renderCount).toEqual(7); }); }); From f9736103cd43d5d95cfe7bec368f4a235828b8e6 Mon Sep 17 00:00:00 2001 From: hwillson Date: Tue, 24 Sep 2019 07:22:22 -0400 Subject: [PATCH 25/28] .npmignore is no longer necessary Since we're publishing from within the "dist" directory, and publishing is going to be blocked from the project root, we don't need to maintain an .npmignore file. --- .npmignore | 14 -------------- 1 file changed, 14 deletions(-) delete mode 100644 .npmignore diff --git a/.npmignore b/.npmignore deleted file mode 100644 index 9a0bb53e265..00000000000 --- a/.npmignore +++ /dev/null @@ -1,14 +0,0 @@ -scripts -test -typings -.gitignore -.travis.yml -.rpt2_cache -ambient.d.ts -CHANGELOG.md -design.md -Gruntfile.js -tsconfig.json -tslint.json -typings.json -config From ef6db1195775b001d7d44c1957a2b6c9a0d4257f Mon Sep 17 00:00:00 2001 From: hwillson Date: Tue, 24 Sep 2019 07:33:03 -0400 Subject: [PATCH 26/28] Change compiled source location from "lib" to "dist" --- config/jest.config.js | 3 ++- config/rollup.config.js | 2 +- docs/source/api/react-testing.md | 2 +- docs/source/hooks-migration.md | 4 ++-- docs/source/recipes/testing.mdx | 4 ++-- tsconfig.json | 2 +- 6 files changed, 9 insertions(+), 8 deletions(-) diff --git a/config/jest.config.js b/config/jest.config.js index 3b87591a0ac..366fb5b1fe1 100644 --- a/config/jest.config.js +++ b/config/jest.config.js @@ -12,7 +12,8 @@ module.exports = { testURL: 'http://localhost', testPathIgnorePatterns: [ '/node_modules/', - '/lib/' + '/dist/' ], + modulePathIgnorePatterns: ['/dist/'], setupFiles: ['/src/config/jest/setup.ts'], }; diff --git a/config/rollup.config.js b/config/rollup.config.js index e40c44c84fc..4c7be3b4200 100644 --- a/config/rollup.config.js +++ b/config/rollup.config.js @@ -28,7 +28,7 @@ function prepareESM() { external, preserveModules: true, output: { - dir: './lib', + dir: './dist', format: 'esm', sourcemap: true, }, diff --git a/docs/source/api/react-testing.md b/docs/source/api/react-testing.md index 09d278aa19a..e9b670c9bef 100644 --- a/docs/source/api/react-testing.md +++ b/docs/source/api/react-testing.md @@ -10,7 +10,7 @@ Apollo Client >= 3 includes React testing utilities out of the box. You don't ne ## `MockedProvider` ```js -import { MockedProvider } from "@apollo/client/lib/react/testing"; +import { MockedProvider } from "@apollo/client/react/testing"; ``` The `MockedProvider` is a test-utility that allows you to create a mocked version of the [`ApolloProvider`](/api/react-hooks/#apolloprovider) that doesn't send out network requests to your API, but rather allows you to specify the exact response payload for a given request. diff --git a/docs/source/hooks-migration.md b/docs/source/hooks-migration.md index 80ab64fb8df..d3a781ece64 100644 --- a/docs/source/hooks-migration.md +++ b/docs/source/hooks-migration.md @@ -57,8 +57,8 @@ npm install @apollo/react-ssr ## Testing -React testing utilities are now available through the Apollo Client project, but they aren't included in the default bundle. To access the React testing utilities, you can use a cherry-pick import from `@apollo/client/lib/react/testing` like: +React testing utilities are now available through the Apollo Client project, but they aren't included in the default bundle. To access the React testing utilities, you can use a cherry-pick import from `@apollo/client/react/testing` like: ``` -import { MockedProvider } from '@apollo/client/lib/react/testing'; +import { MockedProvider } from '@apollo/client/react/testing'; ``` diff --git a/docs/source/recipes/testing.mdx b/docs/source/recipes/testing.mdx index 1bb4e527b24..c8d929854e5 100644 --- a/docs/source/recipes/testing.mdx +++ b/docs/source/recipes/testing.mdx @@ -15,7 +15,7 @@ Apollo Client's React integration relies on [React's context](https://reactjs.or This guide will explain step-by-step how to test Apollo Client React code. The following examples use the [Jest](https://facebook.github.io/jest/docs/en/tutorial-react.html) testing framework, but most concepts should be reusable with other libraries. These examples aim to use as simple of a toolset as possible, so React's [test renderer](https://reactjs.org/docs/test-renderer.html) will be used in place of React-specific tools like [Enzyme](https://github.com/airbnb/enzyme) and [react-testing-library](https://github.com/kentcdodds/react-testing-library). -> **Note:** As of Apollo Client 3, all testing utilities can now be imported from `@apollo/client/lib/react/testing`. +> **Note:** As of Apollo Client 3, all testing utilities can now be imported from `@apollo/client/react/testing`. Consider the component below, which makes a basic query, and displays its results: @@ -131,7 +131,7 @@ Here's an example of a test for the above `Dog` component using `MockedProvider` ```jsx // dog.test.js -import { MockedProvider } from '@apollo/client/lib/react/testing'; +import { MockedProvider } from '@apollo/client/react/testing'; // The component AND the query need to be exported import { GET_DOG_QUERY, Dog } from './dog'; diff --git a/tsconfig.json b/tsconfig.json index c5646af20ab..fd12058e8a5 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -13,7 +13,7 @@ "target": "es5", "module": "es2015", "esModuleInterop": true, - "outDir": "./lib", + "outDir": "./dist", "lib": ["es2015", "esnext.asynciterable", "dom"], "jsx": "react" }, From 4c7af4ea3a2155d8f8f8bb8febda33cc680c943d Mon Sep 17 00:00:00 2001 From: hwillson Date: Tue, 24 Sep 2019 07:36:25 -0400 Subject: [PATCH 27/28] Adjust publish process to use "dist" as the project root These changes include a new script that is used to prepare the "dist" directory for publishing. This includes: - Copying a modified package.json into "dist", after it has been adjusted (remove "./dist" directory prefixes, remove un-needed package.json properties, and remove the private flag). - Copying the README.md into "dist". This script is now called as part of the "predeploy" script. The "deploy" script will now also cd into "dist" before publishing, so only the contents in "dist" will be published to npm (and "dist" will essentially serve as the published package root). --- config/prepareDist.js | 39 +++++++++++++++++++++++++++++++++++++++ package.json | 23 +++++++++++------------ 2 files changed, 50 insertions(+), 12 deletions(-) create mode 100644 config/prepareDist.js diff --git a/config/prepareDist.js b/config/prepareDist.js new file mode 100644 index 00000000000..6daa4608646 --- /dev/null +++ b/config/prepareDist.js @@ -0,0 +1,39 @@ +// The Apollo Client source that is published to npm is located in the +// "dist" directory. This utility script is called just before deploying +// Apollo Client, to make sure the "dist" directory is prepared for publishing. +// +// This script will: +// +// - Copy the current root package.json into "dist" after adjusting it for +// publishing. +// - Copy the root README.md into "dist" + +const packageJson = require('../package.json'); +const fs = require('fs'); + +// The root package.json is marked as private to prevent publishing +// from happening in the root of the project. This sets the package back to +// public so it can be published from the "dist" directory. +packageJson.private = false; + +// Remove package.json items that we don't need to publish +delete packageJson.scripts; +delete packageJson.bundlesize; + +// The root package.json points to the CJS/ESM source in "dist", to support +// on-going package development (e.g. running tests, supporting npm link, etc.). +// When publishing from "dist" however, we need to update the package.json +// to point to the files within the same directory. +const distPackageJson = JSON.stringify( + packageJson, + (_key, value) => ( + typeof value === 'string' ? value.replace(/\.\/dist\//, '') : value + ), + 2 +); + +// Save the modified package.json to "dist" +fs.writeFileSync(`${__dirname}/../dist/package.json`, distPackageJson); + +// Copy the README into "dist" +fs.copyFileSync(`${__dirname}/../README.md`, `${__dirname}/../dist/README.md`); diff --git a/package.json b/package.json index 0fc2e12ee8f..80d87e1b5a5 100644 --- a/package.json +++ b/package.json @@ -2,6 +2,7 @@ "name": "@apollo/client", "version": "3.0.0-alpha.4", "description": "A fully-featured caching GraphQL client.", + "private": true, "keywords": [ "apollo", "graphql", @@ -12,11 +13,11 @@ ], "author": "opensource@apollographql.com", "license": "MIT", - "main": "./lib/apollo-client.cjs.js", - "module": "./lib/index.js", - "types": "./lib/index.d.ts", + "main": "./dist/apollo-client.cjs.js", + "module": "./dist/index.js", + "types": "./dist/index.d.ts", "sideEffects": [ - "./lib/cache/inmemory/fixPolyfills.js" + "./dist/cache/inmemory/fixPolyfills.js" ], "repository": { "type": "git", @@ -31,7 +32,7 @@ "build": "npx tsc", "postbuild": "npm run bundle", "watch": "npx tsc-watch --onSuccess \"npm run postbuild\"", - "clean": "npx rimraf -r lib coverage", + "clean": "npx rimraf -r dist coverage", "test": "jest --config ./config/jest.config.js", "test:ci": "npm run coverage -- --ci --maxWorkers=2 --reporters=default --reporters=jest-junit", "test:watch": "jest --config ./config/jest.config.js --watch", @@ -39,13 +40,14 @@ "coverage": "npx jest --config ./config/jest.config.js --verbose --coverage", "coverage:upload": "npx codecov", "bundlesize": "npm run build && bundlesize", - "predeploy": "npm run build", - "deploy": "npm publish --tag alpha" + "prepdist": "node ./config/prepareDist.js", + "predeploy": "npm run build && npm run prepdist", + "deploy": "cd dist && npm publish --tag alpha" }, "bundlesize": [ { "name": "apollo-client", - "path": "./lib/apollo-client.cjs.min.js", + "path": "./dist/apollo-client.cjs.min.js", "maxSize": "21 kB" } ], @@ -98,8 +100,5 @@ }, "publishConfig": { "access": "public" - }, - "files": [ - "lib" - ] + } } From d7db78e2e6caed4d58582ef3b8408ef606dda035 Mon Sep 17 00:00:00 2001 From: hwillson Date: Tue, 24 Sep 2019 10:28:01 -0400 Subject: [PATCH 28/28] Copy LICENSE into dist during predeploy --- config/prepareDist.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/config/prepareDist.js b/config/prepareDist.js index 6daa4608646..880b870e722 100644 --- a/config/prepareDist.js +++ b/config/prepareDist.js @@ -6,7 +6,8 @@ // // - Copy the current root package.json into "dist" after adjusting it for // publishing. -// - Copy the root README.md into "dist" +// - Copy the supporting files from the root into "dist" (e.g. `README.MD`, +// `LICENSE`, etc.) const packageJson = require('../package.json'); const fs = require('fs'); @@ -35,5 +36,8 @@ const distPackageJson = JSON.stringify( // Save the modified package.json to "dist" fs.writeFileSync(`${__dirname}/../dist/package.json`, distPackageJson); -// Copy the README into "dist" -fs.copyFileSync(`${__dirname}/../README.md`, `${__dirname}/../dist/README.md`); +// Copy supporting files into "dist" +const srcDir = `${__dirname}/..`; +const destDir = `${srcDir}/dist`; +fs.copyFileSync(`${srcDir}/README.md`, `${destDir}/README.md`); +fs.copyFileSync(`${srcDir}/LICENSE`, `${destDir}/LICENSE`);