diff --git a/CHANGELOG.md b/CHANGELOG.md
index 099498866be..fa28b2aaf14 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -48,9 +48,6 @@
- Removed `apollo-boost` since Apollo Client 3.0 provides a boost like getting started experience out of the box.
[@hwillson](https://github.com/hwillson) in [#5217](https://github.com/apollographql/apollo-client/pull/5217)
-- The `@apollo/client` package is now published without a nested `@apollo/client/lib` directory.
- [@hwillson](https://github.com/hwillson) in [#5357](https://github.com/apollographql/apollo-client/pull/5357)
-
- The `queryManager` property of `ApolloClient` instances is now marked as
`private`, paving the way for a more aggressive redesign of its API.
@@ -68,9 +65,6 @@
- `ApolloClient` is now only available as a named export. The default `ApolloClient` export has been removed.
[@hwillson](https://github.com/hwillson) in [#5425](https://github.com/apollographql/apollo-client/pull/5425)
-- Utilities that were previously externally available through the `apollo-utilities` package, are now internal only.
- [@hwillson](https://github.com/hwillson) in [#TODO](https://github.com/apollographql/apollo-client/pull/TODO)
-
- The `ObservableQuery#getCurrentResult` method no longer falls back to reading from the cache, so calling it immediately after `client.watchQuery` will consistently return a `loading: true` result. When the `fetchPolicy` permits cached results, those results will be delivered via the `next` method of the `ObservableQuery`, and can be obtained by `getCurrentResult` after they have been delivered. This change prevents race conditions where the initial behavior of one query could depend on the timing of cache writes associated with other queries.
[@benjamn](https://github.com/benjamn) in [#5565](https://github.com/apollographql/apollo-client/pull/5565)
@@ -94,6 +88,9 @@
- Custom field `read` functions can read from neighboring fields using the `getFieldValue(fieldName)` helper, and may also read fields from other entities by calling `getFieldValue(fieldName, foreignReference)`.
[@benjamn](https://github.com/benjamn) in [#5651](https://github.com/apollographql/apollo-client/pull/5651)
+- Utilities that were previously externally available through the `apollo-utilities` package are now only available by importing from `@apollo/client/utilities`.
+ [@hwillson](https://github.com/hwillson) in [#5683](https://github.com/apollographql/apollo-client/pull/5683)
+
## Apollo Client (2.6.4)
### Apollo Client (2.6.4)
diff --git a/config/prepareDist.js b/config/prepareDist.js
index 906baaa3b15..0b98eda2391 100644
--- a/config/prepareDist.js
+++ b/config/prepareDist.js
@@ -53,7 +53,7 @@ fs.copyFileSync(`${srcDir}/README.md`, `${destDir}/README.md`);
fs.copyFileSync(`${srcDir}/LICENSE`, `${destDir}/LICENSE`);
-/* @apollo/client/core, @apollo/client/cache */
+/* @apollo/client/core, @apollo/client/cache, @apollo/client/utilities */
function buildPackageJson(bundleName) {
return JSON.stringify({
@@ -91,15 +91,21 @@ function writeCjsIndex(bundleName, exportNames, includeNames = true) {
].join('\n'));
}
-// Create `core` and `cache` bundle package.json files, storing them in their
-// associated dist directory. This helps provide a way for the Apollo Client
-// core to be used without React (via `@apollo/client/core`), and the cache
-// to be used by itself (via `@apollo/client/cache`). Also create
-// `core.cjs.js` and `cache.cjs.js` CommonJS entry point files that only
-// include the exports needed for each bundle.
+// Create `core`, `cache` and `utilities` bundle package.json files, storing
+// them in their associated dist directory. This helps provide a way for the
+// Apollo Client core to be used without React (via `@apollo/client/core`),
+// and AC's cache and utilities to be used by themselves
+// (`@apollo/client/cache` and `@apollo/client/utilities`), via the
+// `core.cjs.js`, `cache.cjs.js` and `utilities.cjs.js` CommonJS entry point
+// files that only include the exports needed for each bundle.
fs.writeFileSync(`${distRoot}/core/package.json`, buildPackageJson('core'));
writeCjsIndex('core', loadExportNames('react'), false);
fs.writeFileSync(`${distRoot}/cache/package.json`, buildPackageJson('cache'));
writeCjsIndex('cache', loadExportNames('cache'));
+
+fs.writeFileSync(
+ `${distRoot}/utilities/package.json`,
+ buildPackageJson('utilities')
+);
diff --git a/config/rollup.config.js b/config/rollup.config.js
index db4419dc9b7..e219f8972eb 100644
--- a/config/rollup.config.js
+++ b/config/rollup.config.js
@@ -108,6 +108,23 @@ function prepareCJSMinified(input) {
};
}
+function prepareUtilities() {
+ const utilsDistDir = `${distDir}/utilities`;
+ return {
+ input: `${utilsDistDir}/index.js`,
+ external,
+ output: {
+ file: `${utilsDistDir}/utilities.cjs.js`,
+ format: 'cjs',
+ sourcemap: true,
+ exports: 'named',
+ },
+ plugins: [
+ nodeResolve(),
+ ],
+ };
+}
+
// 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
@@ -144,6 +161,7 @@ function rollup() {
prepareESM(packageJson.module, distDir),
prepareCJS(packageJson.module, packageJson.main),
prepareCJSMinified(packageJson.main),
+ prepareUtilities(),
prepareTesting(),
];
}
diff --git a/src/cache/inmemory/entityStore.ts b/src/cache/inmemory/entityStore.ts
index df5bc670bb3..60f38a8b98c 100644
--- a/src/cache/inmemory/entityStore.ts
+++ b/src/cache/inmemory/entityStore.ts
@@ -1,11 +1,12 @@
import { dep, OptimisticDependencyFunction, KeyTrie } from 'optimism';
import { invariant } from 'ts-invariant';
+import { equal } from '@wry/equality';
+
import { isReference, StoreValue } from '../../utilities/graphql/storeUtils';
import {
DeepMerger,
ReconcilerFunction,
} from '../../utilities/common/mergeDeep';
-import { isEqual } from '../../utilities/common/isEqual';
import { canUseWeakMap } from '../../utilities/common/canUse';
import { NormalizedCache, NormalizedCacheObject, StoreObject } from './types';
import {
@@ -482,7 +483,7 @@ const storeObjectReconciler: ReconcilerFunction<[EntityStore]> = function (
// returning incoming would be logically correct) because preserving
// the referential identity of existing data can prevent needless
// rereading and rerendering.
- if (isEqual(existing, incoming)) {
+ if (equal(existing, incoming)) {
return existing;
}
}
diff --git a/src/core/LocalState.ts b/src/core/LocalState.ts
index a250a89afa1..7ab0f0df6db 100644
--- a/src/core/LocalState.ts
+++ b/src/core/LocalState.ts
@@ -32,7 +32,6 @@ import {
} from '../utilities/graphql/storeUtils';
import { ApolloClient } from '../ApolloClient';
import { Resolvers, OperationVariables } from './types';
-import { capitalizeFirstLetter } from '../utilities/common/capitalizeFirstLetter';
export type Resolver = (
rootValue?: any,
@@ -278,7 +277,8 @@ export class LocalState {
.operation;
const defaultOperationType = definitionOperation
- ? capitalizeFirstLetter(definitionOperation)
+ ? definitionOperation.charAt(0).toUpperCase() +
+ definitionOperation.slice(1)
: 'Query';
const { cache, client } = this;
diff --git a/src/core/ObservableQuery.ts b/src/core/ObservableQuery.ts
index 994f69518c7..7db43eaeceb 100644
--- a/src/core/ObservableQuery.ts
+++ b/src/core/ObservableQuery.ts
@@ -1,6 +1,6 @@
import { invariant, InvariantError } from 'ts-invariant';
+import { equal } from '@wry/equality';
-import { isEqual } from '../utilities/common/isEqual';
import { tryFunctionOrLogError } from '../utilities/common/errorHandling';
import { cloneDeep } from '../utilities/common/cloneDeep';
import { getOperationDefinition } from '../utilities/graphql/getFromAST';
@@ -207,7 +207,7 @@ export class ObservableQuery<
newResult &&
snapshot.networkStatus === newResult.networkStatus &&
snapshot.stale === newResult.stale &&
- isEqual(snapshot.data, newResult.data)
+ equal(snapshot.data, newResult.data)
);
}
@@ -260,7 +260,7 @@ export class ObservableQuery<
fetchPolicy = 'network-only';
}
- if (!isEqual(this.variables, variables)) {
+ if (!equal(this.variables, variables)) {
// update observable variables
this.variables = {
...this.variables,
@@ -268,7 +268,7 @@ export class ObservableQuery<
};
}
- if (!isEqual(this.options.variables, this.variables)) {
+ if (!equal(this.options.variables, this.variables)) {
// Update the existing options with new variables
this.options.variables = {
...this.options.variables,
@@ -446,7 +446,7 @@ export class ObservableQuery<
variables = variables || this.variables;
- if (!tryFetch && isEqual(variables, this.variables)) {
+ if (!tryFetch && equal(variables, this.variables)) {
// If we have no observers, then we don't actually want to make a network
// request. As soon as someone observes the query, the request will kick
// off. For now, we just store any changes. (See #1077)
@@ -600,7 +600,7 @@ export class ObservableQuery<
previousResult &&
fetchPolicy !== 'cache-only' &&
queryManager.transform(query).serverQuery &&
- !isEqual(previousVariables, variables)
+ !equal(previousVariables, variables)
) {
this.refetch();
} else {
diff --git a/src/data/queries.ts b/src/data/queries.ts
index 5a67d0c9ead..47d07cbc6b5 100644
--- a/src/data/queries.ts
+++ b/src/data/queries.ts
@@ -1,7 +1,7 @@
import { DocumentNode, GraphQLError, ExecutionResult } from 'graphql';
import { invariant } from 'ts-invariant';
-import { isEqual } from '../utilities/common/isEqual';
+import { equal } from '@wry/equality';
import { NetworkStatus } from '../core/networkStatus';
import { isNonEmptyArray } from '../utilities/common/arrays';
@@ -44,7 +44,7 @@ export class QueryStore {
invariant(
!previousQuery ||
previousQuery.document === query.document ||
- isEqual(previousQuery.document, query.document),
+ equal(previousQuery.document, query.document),
'Internal Error: may not update existing query string in store',
);
@@ -57,7 +57,7 @@ export class QueryStore {
previousQuery.networkStatus !== NetworkStatus.loading
// if the previous query was still loading, we don't want to remember it at all.
) {
- if (!isEqual(previousQuery.variables, query.variables)) {
+ if (!equal(previousQuery.variables, query.variables)) {
isSetVariables = true;
previousVariables = previousQuery.variables;
}
diff --git a/src/react/data/MutationData.ts b/src/react/data/MutationData.ts
index 78d20e568a7..6421f1ca6af 100644
--- a/src/react/data/MutationData.ts
+++ b/src/react/data/MutationData.ts
@@ -1,4 +1,4 @@
-import { equal as isEqual } from '@wry/equality';
+import { equal } from '@wry/equality';
import { DocumentType } from '../parser/parser';
import { ApolloError } from '../../errors/ApolloError';
@@ -175,7 +175,7 @@ export class MutationData<
private updateResult(result: MutationResult) {
if (
this.isMounted &&
- (!this.previousResult || !isEqual(this.previousResult, result))
+ (!this.previousResult || !equal(this.previousResult, result))
) {
this.setResult(result);
this.previousResult = result;
diff --git a/src/react/data/OperationData.ts b/src/react/data/OperationData.ts
index c046a45c81a..92e1f6fbe6d 100644
--- a/src/react/data/OperationData.ts
+++ b/src/react/data/OperationData.ts
@@ -1,5 +1,5 @@
import { DocumentNode } from 'graphql';
-import { equal as isEqual } from '@wry/equality';
+import { equal } from '@wry/equality';
import { invariant } from 'ts-invariant';
import { ApolloClient } from '../../ApolloClient';
@@ -29,7 +29,7 @@ export abstract class OperationData {
newOptions: CommonOptions,
storePrevious: boolean = false
) {
- if (storePrevious && !isEqual(this.options, newOptions)) {
+ if (storePrevious && !equal(this.options, newOptions)) {
this.previousOptions = this.options;
}
this.options = newOptions;
diff --git a/src/react/data/QueryData.ts b/src/react/data/QueryData.ts
index 84584b4cfdb..fe56dc4f3ef 100644
--- a/src/react/data/QueryData.ts
+++ b/src/react/data/QueryData.ts
@@ -1,4 +1,4 @@
-import { equal as isEqual } from '@wry/equality';
+import { equal } from '@wry/equality';
import { ApolloError } from '../../errors/ApolloError';
import { NetworkStatus } from '../../core/networkStatus';
@@ -245,7 +245,7 @@ export class QueryData extends OperationData {
};
if (
- !isEqual(
+ !equal(
newObservableQueryOptions,
this.previousData.observableQueryOptions
)
@@ -280,7 +280,7 @@ export class QueryData extends OperationData {
previousResult &&
previousResult.loading === loading &&
previousResult.networkStatus === networkStatus &&
- isEqual(previousResult.data, data)
+ equal(previousResult.data, data)
) {
return;
}
@@ -306,7 +306,7 @@ export class QueryData extends OperationData {
const previousResult = this.previousData.result;
if (
(previousResult && previousResult.loading) ||
- !isEqual(error, this.previousData.error)
+ !equal(error, this.previousData.error)
) {
this.previousData.error = error;
onNewData();
@@ -433,8 +433,8 @@ export class QueryData extends OperationData {
if (
this.previousOptions &&
!this.previousData.loading &&
- isEqual(this.previousOptions.query, query) &&
- isEqual(this.previousOptions.variables, variables)
+ equal(this.previousOptions.query, query) &&
+ equal(this.previousOptions.variables, variables)
) {
return;
}
diff --git a/src/react/data/SubscriptionData.ts b/src/react/data/SubscriptionData.ts
index 9128899de3c..de7b41a4e3e 100644
--- a/src/react/data/SubscriptionData.ts
+++ b/src/react/data/SubscriptionData.ts
@@ -1,4 +1,4 @@
-import { equal as isEqual } from '@wry/equality';
+import { equal } from '@wry/equality';
import { OperationData } from './OperationData';
import {
@@ -54,7 +54,7 @@ export class SubscriptionData<
this.previousOptions &&
Object.keys(this.previousOptions).length > 0 &&
(this.previousOptions.subscription !== this.getOptions().subscription ||
- !isEqual(this.previousOptions.variables, this.getOptions().variables) ||
+ !equal(this.previousOptions.variables, this.getOptions().variables) ||
this.previousOptions.skip !== this.getOptions().skip)
) {
this.cleanup();
diff --git a/src/react/hooks/utils/useDeepMemo.ts b/src/react/hooks/utils/useDeepMemo.ts
index f6090d07cbf..a1f4882952a 100644
--- a/src/react/hooks/utils/useDeepMemo.ts
+++ b/src/react/hooks/utils/useDeepMemo.ts
@@ -1,4 +1,4 @@
-import { equal as isEqual } from '@wry/equality';
+import { equal } from '@wry/equality';
import { requireReactLazily } from '../../react';
@@ -17,7 +17,7 @@ export function useDeepMemo(
const { useRef } = React;
const ref = useRef<{ key: TKey; value: TValue }>();
- if (!ref.current || !isEqual(key, ref.current.key)) {
+ if (!ref.current || !equal(key, ref.current.key)) {
ref.current = { key, value: memoFn() };
}
diff --git a/src/utilities/common/__tests__/isEqual.ts b/src/utilities/common/__tests__/isEqual.ts
deleted file mode 100644
index e621a837082..00000000000
--- a/src/utilities/common/__tests__/isEqual.ts
+++ /dev/null
@@ -1,174 +0,0 @@
-import { isEqual } from '../isEqual';
-
-describe('isEqual', () => {
- it('should return true for equal primitive values', () => {
- expect(isEqual(undefined, undefined)).toBe(true);
- expect(isEqual(null, null)).toBe(true);
- expect(isEqual(true, true)).toBe(true);
- expect(isEqual(false, false)).toBe(true);
- expect(isEqual(-1, -1)).toBe(true);
- expect(isEqual(+1, +1)).toBe(true);
- expect(isEqual(42, 42)).toBe(true);
- expect(isEqual(0, 0)).toBe(true);
- expect(isEqual(0.5, 0.5)).toBe(true);
- expect(isEqual('hello', 'hello')).toBe(true);
- expect(isEqual('world', 'world')).toBe(true);
- });
-
- it('should return false for not equal primitive values', () => {
- expect(!isEqual(undefined, null)).toBe(true);
- expect(!isEqual(null, undefined)).toBe(true);
- expect(!isEqual(true, false)).toBe(true);
- expect(!isEqual(false, true)).toBe(true);
- expect(!isEqual(-1, +1)).toBe(true);
- expect(!isEqual(+1, -1)).toBe(true);
- expect(!isEqual(42, 42.00000000000001)).toBe(true);
- expect(!isEqual(0, 0.5)).toBe(true);
- expect(!isEqual('hello', 'world')).toBe(true);
- expect(!isEqual('world', 'hello')).toBe(true);
- });
-
- it('should return false when comparing primitives with objects', () => {
- expect(!isEqual({}, null)).toBe(true);
- expect(!isEqual(null, {})).toBe(true);
- expect(!isEqual({}, true)).toBe(true);
- expect(!isEqual(true, {})).toBe(true);
- expect(!isEqual({}, 42)).toBe(true);
- expect(!isEqual(42, {})).toBe(true);
- expect(!isEqual({}, 'hello')).toBe(true);
- expect(!isEqual('hello', {})).toBe(true);
- });
-
- it('should correctly compare shallow objects', () => {
- expect(isEqual({}, {})).toBe(true);
- expect(isEqual({ a: 1, b: 2, c: 3 }, { a: 1, b: 2, c: 3 })).toBe(true);
- expect(!isEqual({ a: 1, b: 2, c: 3 }, { a: 3, b: 2, c: 1 })).toBe(true);
- expect(!isEqual({ a: 1, b: 2, c: 3 }, { a: 1, b: 2 })).toBe(true);
- expect(!isEqual({ a: 1, b: 2 }, { a: 1, b: 2, c: 3 })).toBe(true);
- });
-
- it('should correctly compare deep objects', () => {
- expect(isEqual({ x: {} }, { x: {} })).toBe(true);
- expect(
- isEqual({ x: { a: 1, b: 2, c: 3 } }, { x: { a: 1, b: 2, c: 3 } }),
- ).toBe(true);
- expect(
- !isEqual({ x: { a: 1, b: 2, c: 3 } }, { x: { a: 3, b: 2, c: 1 } }),
- ).toBe(true);
- expect(!isEqual({ x: { a: 1, b: 2, c: 3 } }, { x: { a: 1, b: 2 } })).toBe(
- true,
- );
- expect(!isEqual({ x: { a: 1, b: 2 } }, { x: { a: 1, b: 2, c: 3 } })).toBe(
- true,
- );
- });
-
- it('should correctly compare deep objects without object prototype ', () => {
- // Solves https://github.com/apollographql/apollo-client/issues/2132
- const objNoProto = Object.create(null);
- objNoProto.a = { b: 2, c: [3, 4] };
- objNoProto.e = Object.create(null);
- objNoProto.e.f = 5;
- expect(isEqual(objNoProto, { a: { b: 2, c: [3, 4] }, e: { f: 5 } })).toBe(
- true,
- );
- expect(!isEqual(objNoProto, { a: { b: 2, c: [3, 4] }, e: { f: 6 } })).toBe(
- true,
- );
- expect(!isEqual(objNoProto, { a: { b: 2, c: [3, 4] }, e: null })).toBe(
- true,
- );
- expect(!isEqual(objNoProto, { a: { b: 2, c: [3] }, e: { f: 5 } })).toBe(
- true,
- );
- expect(!isEqual(objNoProto, null)).toBe(true);
- });
-
- it('should correctly handle modified prototypes', () => {
- Array.prototype.foo = null;
- expect(isEqual([1, 2, 3], [1, 2, 3])).toBe(true);
- expect(!isEqual([1, 2, 3], [1, 2, 4])).toBe(true);
- delete Array.prototype.foo;
- });
-
- describe('comparing objects with circular refs', () => {
- // copied with slight modification from lodash test suite
- it('should compare objects with circular references', () => {
- const object1 = {},
- object2 = {};
-
- object1.a = object1;
- object2.a = object2;
-
- expect(isEqual(object1, object2)).toBe(true);
-
- object1.b = 0;
- object2.b = Object(0);
-
- expect(isEqual(object1, object2)).toBe(true);
-
- object1.c = Object(1);
- object2.c = Object(2);
-
- expect(isEqual(object1, object2)).toBe(false);
-
- object1 = { a: 1, b: 2, c: 3 };
- object1.b = object1;
- object2 = { a: 1, b: { a: 1, b: 2, c: 3 }, c: 3 };
-
- expect(isEqual(object1, object2)).toBe(false);
- });
-
- it('should have transitive equivalence for circular references of objects', () => {
- const object1 = {},
- object2 = { a: object1 },
- object3 = { a: object2 };
-
- object1.a = object1;
-
- expect(isEqual(object1, object2)).toBe(true);
- expect(isEqual(object2, object3)).toBe(true);
- expect(isEqual(object1, object3)).toBe(true);
- });
-
- it('should compare objects with multiple circular references', () => {
- const array1 = [{}],
- array2 = [{}];
-
- (array1[0].a = array1).push(array1);
- (array2[0].a = array2).push(array2);
-
- expect(isEqual(array1, array2)).toBe(true);
-
- array1[0].b = 0;
- array2[0].b = Object(0);
-
- expect(isEqual(array1, array2)).toBe(true);
-
- array1[0].c = Object(1);
- array2[0].c = Object(2);
-
- expect(isEqual(array1, array2)).toBe(false);
- });
-
- it('should compare objects with complex circular references', () => {
- const object1 = {
- foo: { b: { c: { d: {} } } },
- bar: { a: 2 },
- };
-
- const object2 = {
- foo: { b: { c: { d: {} } } },
- bar: { a: 2 },
- };
-
- object1.foo.b.c.d = object1;
- object1.bar.b = object1.foo.b;
-
- object2.foo.b.c.d = object2;
- object2.bar.b = object2.foo.b;
-
- expect(isEqual(object1, object2)).toBe(true);
- });
- });
-});
diff --git a/src/utilities/common/capitalizeFirstLetter.ts b/src/utilities/common/capitalizeFirstLetter.ts
deleted file mode 100644
index 0eab4968185..00000000000
--- a/src/utilities/common/capitalizeFirstLetter.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-export function capitalizeFirstLetter(str: string) {
- return str.charAt(0).toUpperCase() + str.slice(1);
-}
diff --git a/src/utilities/common/isEqual.ts b/src/utilities/common/isEqual.ts
deleted file mode 100644
index debf7429c5e..00000000000
--- a/src/utilities/common/isEqual.ts
+++ /dev/null
@@ -1 +0,0 @@
-export { equal as isEqual } from '@wry/equality';
diff --git a/src/utilities/graphql/__tests__/getFromAST.ts b/src/utilities/graphql/__tests__/getFromAST.ts
index 328aeb57d4a..575ecfca038 100644
--- a/src/utilities/graphql/__tests__/getFromAST.ts
+++ b/src/utilities/graphql/__tests__/getFromAST.ts
@@ -6,7 +6,6 @@ import {
checkDocument,
getFragmentDefinitions,
getQueryDefinition,
- getMutationDefinition,
getDefaultValues,
getOperationName,
} from '../getFromAST';
@@ -162,32 +161,6 @@ describe('AST utility functions', () => {
}).toThrow();
});
- it('should get the correct mutation definition out of a mutation with multiple fragments', () => {
- const mutationWithFragments = gql`
- mutation {
- createAuthor(firstName: "John", lastName: "Smith") {
- ...authorDetails
- }
- }
-
- fragment authorDetails on Author {
- firstName
- lastName
- }
- `;
- const expectedDoc = gql`
- mutation {
- createAuthor(firstName: "John", lastName: "Smith") {
- ...authorDetails
- }
- }
- `;
- const expectedResult: OperationDefinitionNode = expectedDoc
- .definitions[0] as OperationDefinitionNode;
- const actualResult = getMutationDefinition(mutationWithFragments);
- expect(print(actualResult)).toEqual(print(expectedResult));
- });
-
it('should get the operation name out of a query', () => {
const query = gql`
query nameOfQuery {
@@ -268,24 +241,9 @@ describe('AST utility functions', () => {
}
`;
- const complexMutation = gql`
- mutation complexStuff(
- $test: Input = { key1: ["value", "value2"], key2: { key3: 4 } }
- ) {
- complexStuff(test: $test) {
- people {
- name
- }
- }
- }
- `;
-
expect(getDefaultValues(getQueryDefinition(basicQuery))).toEqual({
first: 1,
});
- expect(getDefaultValues(getMutationDefinition(complexMutation))).toEqual({
- test: { key1: ['value', 'value2'], key2: { key3: 4 } },
- });
});
});
});
diff --git a/src/utilities/graphql/__tests__/transform.ts b/src/utilities/graphql/__tests__/transform.ts
index 26835f4cc4f..01a687dafc3 100644
--- a/src/utilities/graphql/__tests__/transform.ts
+++ b/src/utilities/graphql/__tests__/transform.ts
@@ -8,7 +8,6 @@ disableFragmentWarnings();
import {
addTypenameToDocument,
removeDirectivesFromDocument,
- getDirectivesFromDocument,
removeConnectionDirectiveFromDocument,
removeArgumentsFromDocument,
removeFragmentSpreadFromDocument,
@@ -803,408 +802,6 @@ describe('query transforms', () => {
});
});
-describe('getDirectivesFromDocument', () => {
- it('should get query with fields of storage directive ', () => {
- const query = gql`
- query Simple {
- field @storage(if: true)
- }
- `;
-
- const expected = gql`
- query Simple {
- field @storage(if: true)
- }
- `;
- const doc = getDirectivesFromDocument([{ name: 'storage' }], query);
- expect(print(doc)).toBe(print(expected));
- });
-
- it('should get query with fields of storage directive [test function] ', () => {
- const query = gql`
- query Simple {
- field @storage(if: true)
- }
- `;
-
- const expected = gql`
- query Simple {
- field @storage(if: true)
- }
- `;
- const test = ({ name: { value } }: { name: any }) => value === 'storage';
- const doc = getDirectivesFromDocument([{ test }], query);
- expect(print(doc)).toBe(print(expected));
- });
-
- it('should only get query with fields of storage directive ', () => {
- const query = gql`
- query Simple {
- maybe @skip(if: false)
- field @storage(if: true)
- }
- `;
-
- const expected = gql`
- query Simple {
- field @storage(if: true)
- }
- `;
- const doc = getDirectivesFromDocument([{ name: 'storage' }], query);
- expect(print(doc)).toBe(print(expected));
- });
-
- it('should only get query with multiple fields of storage directive ', () => {
- const query = gql`
- query Simple {
- maybe @skip(if: false)
- field @storage(if: true)
- other @storage
- }
- `;
-
- const expected = gql`
- query Simple {
- field @storage(if: true)
- other @storage
- }
- `;
- const doc = getDirectivesFromDocument([{ name: 'storage' }], query);
- expect(print(doc)).toBe(print(expected));
- });
-
- it('should get query with fields of both storage and client directives ', () => {
- const query = gql`
- query Simple {
- maybe @skip(if: false)
- field @storage(if: true)
- user @client
- }
- `;
-
- const expected = gql`
- query Simple {
- field @storage(if: true)
- user @client
- }
- `;
- const doc = getDirectivesFromDocument(
- [{ name: 'storage' }, { name: 'client' }],
- query,
- );
- expect(print(doc)).toBe(print(expected));
- });
-
- it('should get query with different types of directive matchers ', () => {
- const query = gql`
- query Simple {
- maybe @skip(if: false)
- field @storage(if: true)
- user @client
- }
- `;
-
- const expected = gql`
- query Simple {
- field @storage(if: true)
- user @client
- }
- `;
- const doc = getDirectivesFromDocument(
- [
- { name: 'storage' },
- { test: directive => directive.name.value === 'client' },
- ],
- query,
- );
-
- expect(print(doc)).toBe(print(expected));
- });
-
- it('should get query with nested fields ', () => {
- const query = gql`
- query Simple {
- user {
- firstName @client
- email
- }
- }
- `;
-
- const expected = gql`
- query Simple {
- user {
- firstName @client
- }
- }
- `;
- const doc = getDirectivesFromDocument([{ name: 'client' }], query);
- expect(print(doc)).toBe(print(expected));
- });
-
- it('should include all the nested fields of field that has client directive ', () => {
- const query = gql`
- query Simple {
- user @client {
- firstName
- email
- }
- }
- `;
-
- const expected = gql`
- query Simple {
- user @client {
- firstName
- email
- }
- }
- `;
- const doc = getDirectivesFromDocument([{ name: 'client' }], query);
- expect(print(doc)).toBe(print(expected));
- });
-
- it('should return null if the query is no longer valid', () => {
- const query = gql`
- query Simple {
- field
- }
- `;
- const doc = getDirectivesFromDocument([{ name: 'client' }], query);
- expect(print(doc)).toBe(null);
- });
-
- it('should get query with client fields in fragment', function() {
- const query = gql`
- query Simple {
- ...fragmentSpread
- }
-
- fragment fragmentSpread on Thing {
- field @client
- other
- }
- `;
- const expected = gql`
- query Simple {
- ...fragmentSpread
- }
-
- fragment fragmentSpread on Thing {
- field @client
- }
- `;
- const doc = getDirectivesFromDocument([{ name: 'client' }], query);
- expect(print(doc)).toBe(print(expected));
- });
-
- it('should get query with client fields in fragment with nested fields', function() {
- const query = gql`
- query Simple {
- ...fragmentSpread
- }
-
- fragment fragmentSpread on Thing {
- user {
- firstName @client
- lastName
- }
- }
- `;
- const expected = gql`
- query Simple {
- ...fragmentSpread
- }
-
- fragment fragmentSpread on Thing {
- user {
- firstName @client
- }
- }
- `;
- const doc = getDirectivesFromDocument([{ name: 'client' }], query);
- expect(print(doc)).toBe(print(expected));
- });
-
- it('should get query with client fields in multiple fragments', function() {
- const query = gql`
- query Simple {
- ...fragmentSpread
- ...anotherFragmentSpread
- }
-
- fragment fragmentSpread on Thing {
- field @client
- other
- }
-
- fragment anotherFragmentSpread on AnotherThing {
- user @client
- product
- }
- `;
- const expected = gql`
- query Simple {
- ...fragmentSpread
- ...anotherFragmentSpread
- }
-
- fragment fragmentSpread on Thing {
- field @client
- }
-
- fragment anotherFragmentSpread on AnotherThing {
- user @client
- }
- `;
- const doc = getDirectivesFromDocument([{ name: 'client' }], query);
- expect(print(doc)).toBe(print(expected));
- });
-
- it("should return null if fragment didn't have client fields", function() {
- const query = gql`
- query Simple {
- ...fragmentSpread
- }
-
- fragment fragmentSpread on Thing {
- field
- }
- `;
- const doc = getDirectivesFromDocument([{ name: 'client' }], query);
- expect(print(doc)).toBe(print(null));
- });
-
- it('should get query with client fields when both fields and fragements are mixed', function() {
- const query = gql`
- query Simple {
- user @client
- product @storage
- order
- ...fragmentSpread
- }
-
- fragment fragmentSpread on Thing {
- field @client
- other
- }
- `;
- const expected = gql`
- query Simple {
- user @client
- ...fragmentSpread
- }
-
- fragment fragmentSpread on Thing {
- field @client
- }
- `;
- const doc = getDirectivesFromDocument([{ name: 'client' }], query);
- expect(print(doc)).toBe(print(expected));
- });
-
- it('should get mutation with client fields', () => {
- const query = gql`
- mutation {
- login @client
- }
- `;
-
- const expected = gql`
- mutation {
- login @client
- }
- `;
- const doc = getDirectivesFromDocument([{ name: 'client' }], query);
- expect(print(doc)).toBe(print(expected));
- });
-
- it('should get mutation fields of client only', () => {
- const query = gql`
- mutation {
- login @client
- updateUser
- }
- `;
-
- const expected = gql`
- mutation {
- login @client
- }
- `;
- const doc = getDirectivesFromDocument([{ name: 'client' }], query);
- expect(print(doc)).toBe(print(expected));
- });
-
- describe('includeAllFragments', () => {
- it('= false: should remove the values without a client in fragment', () => {
- const query = gql`
- fragment client on ClientData {
- hi @client
- bye @storage
- bar
- }
-
- query Mixed {
- foo @client {
- ...client
- }
- bar {
- baz
- }
- }
- `;
-
- const expected = gql`
- fragment client on ClientData {
- hi @client
- }
-
- query Mixed {
- foo @client {
- ...client
- }
- }
- `;
- const doc = getDirectivesFromDocument([{ name: 'client' }], query, false);
- expect(print(doc)).toBe(print(expected));
- });
-
- it('= true: should include the values without a client in fragment', () => {
- const query = gql`
- fragment client on ClientData {
- hi @client
- bye @storage
- bar
- }
-
- query Mixed {
- foo @client {
- ...client
- }
- bar {
- baz
- }
- }
- `;
-
- const expected = gql`
- fragment client on ClientData {
- hi @client
- }
-
- query Mixed {
- foo @client {
- ...client
- }
- }
- `;
- const doc = getDirectivesFromDocument([{ name: 'client' }], query, true);
- expect(print(doc)).toBe(print(expected));
- });
- });
-});
-
describe('removeClientSetsFromDocument', () => {
it('should remove @client fields from document', () => {
const query = gql`
diff --git a/src/utilities/graphql/directives.ts b/src/utilities/graphql/directives.ts
index 84c14abaeaa..a7a20a01fb4 100644
--- a/src/utilities/graphql/directives.ts
+++ b/src/utilities/graphql/directives.ts
@@ -1,7 +1,6 @@
// Provides the methods that allow QueryManager to handle the `skip` and
// `include` directives within GraphQL.
import {
- FieldNode,
SelectionNode,
VariableNode,
BooleanValueNode,
@@ -15,29 +14,10 @@ import { visit } from 'graphql/language/visitor';
import { invariant } from 'ts-invariant';
-import { argumentsObjectFromField } from './storeUtils';
-
export type DirectiveInfo = {
[fieldName: string]: { [argName: string]: any };
};
-export function getDirectiveInfoFromField(
- field: FieldNode,
- variables: Object,
-): DirectiveInfo {
- if (field.directives && field.directives.length) {
- const directiveObj: DirectiveInfo = {};
- field.directives.forEach((directive: DirectiveNode) => {
- directiveObj[directive.name.value] = argumentsObjectFromField(
- directive,
- variables,
- );
- });
- return directiveObj;
- }
- return null;
-}
-
export function shouldInclude(
selection: SelectionNode,
variables: { [name: string]: any } = {},
diff --git a/src/utilities/graphql/getFromAST.ts b/src/utilities/graphql/getFromAST.ts
index d5aabba0609..3dad1fe3da7 100644
--- a/src/utilities/graphql/getFromAST.ts
+++ b/src/utilities/graphql/getFromAST.ts
@@ -11,22 +11,6 @@ import { assign } from '../common/assign';
import { valueToObjectRepresentation } from './storeUtils';
-export function getMutationDefinition(
- doc: DocumentNode,
-): OperationDefinitionNode {
- checkDocument(doc);
-
- let mutationDef: OperationDefinitionNode | null = doc.definitions.filter(
- definition =>
- definition.kind === 'OperationDefinition' &&
- definition.operation === 'mutation',
- )[0] as OperationDefinitionNode;
-
- invariant(mutationDef, 'Must contain a mutation definition.');
-
- return mutationDef;
-}
-
// Checks the document for errors and throws an exception if there is an error.
export function checkDocument(doc: DocumentNode) {
invariant(
@@ -65,14 +49,6 @@ export function getOperationDefinition(
)[0] as OperationDefinitionNode;
}
-export function getOperationDefinitionOrDie(
- document: DocumentNode,
-): OperationDefinitionNode {
- const def = getOperationDefinition(document);
- invariant(def, `GraphQL document is missing an operation`);
- return def;
-}
-
export function getOperationName(doc: DocumentNode): string | null {
return (
doc.definitions
@@ -195,19 +171,3 @@ export function getDefaultValues(
return {};
}
-
-/**
- * Returns the names of all variables declared by the operation.
- */
-export function variablesInOperation(
- operation: OperationDefinitionNode,
-): Set {
- const names = new Set();
- if (operation.variableDefinitions) {
- for (const definition of operation.variableDefinitions) {
- names.add(definition.variable.name.value);
- }
- }
-
- return names;
-}
diff --git a/src/utilities/graphql/storeUtils.ts b/src/utilities/graphql/storeUtils.ts
index a4178975702..5bf701f1a2c 100644
--- a/src/utilities/graphql/storeUtils.ts
+++ b/src/utilities/graphql/storeUtils.ts
@@ -44,18 +44,6 @@ export type StoreValue =
| void
| Object;
-export type ScalarValue = StringValueNode | BooleanValueNode | EnumValueNode;
-
-export function isScalarValue(value: ValueNode): value is ScalarValue {
- return ['StringValue', 'BooleanValue', 'EnumValue'].indexOf(value.kind) > -1;
-}
-
-export type NumberValue = IntValueNode | FloatValueNode;
-
-export function isNumberValue(value: ValueNode): value is NumberValue {
- return ['IntValue', 'FloatValue'].indexOf(value.kind) > -1;
-}
-
function isStringValue(value: ValueNode): value is StringValueNode {
return value.kind === 'StringValue';
}
@@ -297,38 +285,5 @@ export function isInlineFragment(
return selection.kind === 'InlineFragment';
}
-function defaultValueFromVariable(node: VariableNode) {
- throw new InvariantError(`Variable nodes are not supported by valueFromNode`);
-}
-
export type VariableValue = (node: VariableNode) => any;
-/**
- * Evaluate a ValueNode and yield its value in its natural JS form.
- */
-export function valueFromNode(
- node: ValueNode,
- onVariable: VariableValue = defaultValueFromVariable,
-): any {
- switch (node.kind) {
- case 'Variable':
- return onVariable(node);
- case 'NullValue':
- return null;
- case 'IntValue':
- return parseInt(node.value, 10);
- case 'FloatValue':
- return parseFloat(node.value);
- case 'ListValue':
- return node.values.map(v => valueFromNode(v, onVariable));
- case 'ObjectValue': {
- const value: { [key: string]: any } = {};
- for (const field of node.fields) {
- value[field.name.value] = valueFromNode(field.value, onVariable);
- }
- return value;
- }
- default:
- return node.value;
- }
-}
diff --git a/src/utilities/graphql/transform.ts b/src/utilities/graphql/transform.ts
index b63ed2680d6..94ff1c04e21 100644
--- a/src/utilities/graphql/transform.ts
+++ b/src/utilities/graphql/transform.ts
@@ -325,48 +325,6 @@ function hasDirectivesInSelection(
);
}
-export function getDirectivesFromDocument(
- directives: GetDirectiveConfig[],
- doc: DocumentNode,
-): DocumentNode {
- checkDocument(doc);
-
- let parentPath: string;
-
- return nullIfDocIsEmpty(
- visit(doc, {
- SelectionSet: {
- enter(node, _key, _parent, path) {
- const currentPath = path.join('-');
-
- if (
- !parentPath ||
- currentPath === parentPath ||
- !currentPath.startsWith(parentPath)
- ) {
- if (node.selections) {
- const selectionsWithDirectives = node.selections.filter(
- selection => hasDirectivesInSelection(directives, selection),
- );
-
- if (hasDirectivesInSelectionSet(directives, node, false)) {
- parentPath = currentPath;
- }
-
- return {
- ...node,
- selections: selectionsWithDirectives,
- };
- } else {
- return null;
- }
- }
- },
- },
- }),
- );
-}
-
function getArgumentMatcher(config: RemoveArgumentsConfig[]) {
return function argumentMatcher(argument: ArgumentNode) {
return config.some(
diff --git a/src/utilities/index.ts b/src/utilities/index.ts
new file mode 100644
index 00000000000..320c73c5ee5
--- /dev/null
+++ b/src/utilities/index.ts
@@ -0,0 +1,63 @@
+export {
+ DirectiveInfo,
+ InclusionDirectives,
+ shouldInclude,
+ hasDirectives,
+ hasClientExports,
+ getDirectiveNames,
+ getInclusionDirectives,
+} from './graphql/directives';
+
+export {
+ FragmentMap,
+ createFragmentMap,
+ getFragmentQueryDocument,
+ getFragmentFromSelection,
+} from './graphql/fragments';
+
+export {
+ checkDocument,
+ getOperationDefinition,
+ getOperationName,
+ getFragmentDefinitions,
+ getQueryDefinition,
+ getFragmentDefinition,
+ getMainDefinition,
+ getDefaultValues,
+} from './graphql/getFromAST';
+
+export {
+ Reference,
+ StoreValue,
+ Directives,
+ VariableValue,
+ makeReference,
+ isReference,
+ isField,
+ isInlineFragment,
+ valueToObjectRepresentation,
+ storeKeyNameFromField,
+ argumentsObjectFromField,
+ resultKeyNameFromField,
+ getStoreKeyName,
+ getTypenameFromResult,
+} from './graphql/storeUtils';
+
+export {
+ RemoveNodeConfig,
+ GetNodeConfig,
+ RemoveDirectiveConfig,
+ GetDirectiveConfig,
+ RemoveArgumentsConfig,
+ GetFragmentSpreadConfig,
+ RemoveFragmentSpreadConfig,
+ RemoveFragmentDefinitionConfig,
+ RemoveVariableDefinitionConfig,
+ addTypenameToDocument,
+ buildQueryFromSelectionSet,
+ removeDirectivesFromDocument,
+ removeConnectionDirectiveFromDocument,
+ removeArgumentsFromDocument,
+ removeFragmentSpreadFromDocument,
+ removeClientSetsFromDocument,
+} from './graphql/transform';
diff --git a/src/utilities/testing/mocking/mockLink.ts b/src/utilities/testing/mocking/mockLink.ts
index 4bc9699617c..a110bca193f 100644
--- a/src/utilities/testing/mocking/mockLink.ts
+++ b/src/utilities/testing/mocking/mockLink.ts
@@ -1,5 +1,6 @@
import { print } from 'graphql/language/printer';
import stringify from 'fast-json-stable-stringify';
+import { equal } from '@wry/equality';
import { Observable } from '../../../utilities/observables/Observable';
import { ApolloLink } from '../../../link/core/ApolloLink';
@@ -14,7 +15,6 @@ import {
removeConnectionDirectiveFromDocument,
} from '../../../utilities/graphql/transform';
import { cloneDeep } from '../../../utilities/common/cloneDeep';
-import { isEqual } from '../../../utilities/common/isEqual';
export type ResultFunction = () => T;
@@ -77,7 +77,7 @@ export class MockLink extends ApolloLink {
const requestVariables = operation.variables || {};
const mockedResponseVariables = res.request.variables || {};
if (
- !isEqual(
+ !equal(
stringify(requestVariables),
stringify(mockedResponseVariables)
)