Skip to content

Commit

Permalink
change parsing with memoization
Browse files Browse the repository at this point in the history
  • Loading branch information
nksaraf committed Oct 16, 2020
1 parent d4168fc commit d220d01
Show file tree
Hide file tree
Showing 7 changed files with 109 additions and 37 deletions.
11 changes: 10 additions & 1 deletion src/core/graphQLClient.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { QueryCache } from "react-query";

import { createOperation } from "./operation";
import { createQueryCacheStore } from "./store/cacheStore";
import type {
import {
Operation,
QueryKey,
InfiniteQueryKey,
Expand All @@ -25,6 +25,7 @@ import type {
DebugEvent,
GraphQLTaggedNode,
Normalizer,
constants,
} from "./types";
import { createNormalizer } from "./normalizer";
import type { GraphQLSubscriptionClient } from "./subscriptionClient";
Expand Down Expand Up @@ -112,6 +113,14 @@ export class GraphQLClient {
return createOperation(node, options) as Operation<TQuery>;
}

createFragmentRef(record, type, fragmentName) {
return {
[constants.ID_KEY]: this.normalizer.getDataID(record, type),
[constants.FRAGMENTS_KEY]: { [fragmentName]: {} },
[constants.FRAGMENT_OWNER_KEY]: { variables: {} },
};
}

buildSubscription<TQuery extends Query>(
operation: Operation<TQuery>,
{
Expand Down
11 changes: 10 additions & 1 deletion src/core/graphQLTag.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
import { parseGraphQLTag } from "./parser";
import { GraphQLTaggedNode } from "./types";

export const graphql: (
strings: TemplateStringsArray,
...values: (GraphQLTaggedNode | string)[]
) => GraphQLTaggedNode | string = String.raw;
) => GraphQLTaggedNode | string = (strings, ...values) =>
parseGraphQLTag(
String.raw(
strings,
...values
.filter(Boolean)
.map((val) => (typeof val === "string" ? val : (val as any).text))
)
);
1 change: 1 addition & 0 deletions src/core/normalizer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -207,5 +207,6 @@ export function createNormalizer({

return {
normalizeResponse,
getDataID,
};
}
19 changes: 11 additions & 8 deletions src/core/operation.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// https://github.com/facebook/relay/blob/7c67b4750592e469d499128108fe16afe2adaf51/packages/relay-runtime/store/RelayModernSelector.js
import type { ConcreteRequest, ReaderFragment } from "relay-runtime";
import {
getRequest as baseGetRequest,
Expand All @@ -12,27 +13,29 @@ import type {
GraphQLTaggedNode,
FetchOptions,
} from "./types";
import { parseRequest, parseFragment } from "./parser";
import { parseGraphQLTag } from "./parser";

export const getRequest = (
taggedNode: GraphQLTaggedNode | string
): ConcreteRequest => {
if (typeof taggedNode === "string") {
return parseRequest(taggedNode);
return parseGraphQLTag(taggedNode) as ConcreteRequest;
}

// resolves the node from the require call for artifacts from relay-compiler, otherwise returns
const request = baseGetRequest(taggedNode);

// Previously parsed by magiql
if (
typeof request === "object" &&
request.params.metadata?.parser === "graphql"
) {
return request as ConcreteRequest;
} else {
}
// Parsed by relay (require call for artifact from relay-compiler)
else {
(request.params as any).text = (request as any).query;
(request.params as any).metadata = {
...(request.params as any).metadata,
parser: "relay",
};
(request.params as any).metadata.parser = "relay";
return request;
}
};
Expand All @@ -41,7 +44,7 @@ export const getFragment = (
taggedNode: GraphQLTaggedNode | string
): ReaderFragment | null => {
return typeof taggedNode === "string"
? parseFragment(taggedNode)
? (parseGraphQLTag(taggedNode) as ReaderFragment)
: baseGetFragment(taggedNode);
};

Expand Down
100 changes: 75 additions & 25 deletions src/core/parser.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
// Piggybacking off relay-runtime here
// https://github.com/facebook/relay/blob/7c67b4750592e469d499128108fe16afe2adaf51/packages/relay-runtime/store/RelayModernSelector.js
import type {
ReaderFragment,
NormalizationOperation,
NormalizationField,
NormalizationScalarField,
NormalizationSelection,
ConcreteRequest,
} from "relay-runtime";


import {
constants,
} from "./types";
import { constants } from "./types";

import { parse } from "graphql/language/parser";
import { print } from "graphql/language/printer";
Expand All @@ -26,6 +25,7 @@ import {
InlineFragmentNode,
FieldNode,
} from "graphql";
import { stableStringify } from "../utils";

const parseSelections = (selectionSet: SelectionSetNode) => {
const selections: NormalizationSelection[] = selectionSet.selections.map(
Expand Down Expand Up @@ -118,7 +118,7 @@ const parseSelections = (selectionSet: SelectionSetNode) => {
return selections;
};

const parsedOperation = (
const parseOperation = (
op: OperationDefinitionNode
): NormalizationOperation => {
return {
Expand Down Expand Up @@ -291,39 +291,90 @@ const flattenFragments = (node: DocumentNode): DocumentNode => {
return doc;
};

export const parseRequest = (taggedNode: string) => {
const node = parse(taggedNode);
const memoized = <T extends any[], V>(
fn: (...args: T) => V,
serialize: (...args: T) => string
) => {
const cache: { [key: string]: V } = {};
return (...vars: T) => {
const key = serialize(...vars);
if (cache[key]) {
return cache[key];
} else {
const val = fn(...vars);
if (val) {
cache[key] = val;
}
return val;
}
};
};

export const parseGraphQLTag = memoized(
(taggedNode: string): ReaderFragment | ConcreteRequest => {
try {
const node = parse(taggedNode);

const document = node.definitions.find(
(def) =>
def.kind === "OperationDefinition" ||
def.kind === "FragmentDefinition"
) as OperationDefinitionNode | FragmentDefinitionNode;

if (!document) {
throw new Error(
"No GraphQL query/mutation/subscription/fragment found"
);
}
if (document.kind === "FragmentDefinition") {
return parseFragment(node, taggedNode);
} else {
return parseRequest(node, taggedNode);
}
} catch (e) {
console.log("Error parsing GraphQL", e.message, taggedNode);
}
},
(s) => s
);

export const parseRequest = (
node: DocumentNode,
taggedNode: string
): ConcreteRequest => {
const flatNode = flattenFragments(inlineFragments(node));
const flatNodeWithTypename = addTypeName(flatNode);

const document = flatNodeWithTypename.definitions.find(
const queryDocument = flatNodeWithTypename.definitions.find(
(def) => def.kind === "OperationDefinition"
) as OperationDefinitionNode;

const fragmentDocument = node.definitions.find(
(def) => def.kind === "OperationDefinition"
) as OperationDefinitionNode;

return {
kind: "Request",
fragment: parsedOperation(
node.definitions.find(
(def) => def.kind === "OperationDefinition"
) as OperationDefinitionNode
) as ReaderFragment,
operation: parsedOperation(document),
fragment: parseOperation(fragmentDocument) as ReaderFragment,
operation: parseOperation(queryDocument),
text: taggedNode,
params: {
operationKind: document.operation,
name: document.name.value,
id: document.name.value,
operationKind: queryDocument.operation,
name: queryDocument.name.value,
id: queryDocument.name.value,
cacheID: "",
text: print(document),
text: print(queryDocument),
metadata: {
parser: "graphql",
// TODO: parse plural
},
},
};
} as ConcreteRequest;
};

export const parseFragment = (taggedNode: string) => {
const node = parse(taggedNode);

export const parseFragment = (
node: DocumentNode,
taggedNode: string
): ReaderFragment => {
const fragment = node.definitions.find(
(def) => def.kind === "FragmentDefinition"
) as FragmentDefinitionNode;
Expand All @@ -336,7 +387,6 @@ export const parseFragment = (taggedNode: string) => {
selections: parseSelections(fragment.selectionSet),
type: fragment.typeCondition.name.value,
abstractKey: null,
text: taggedNode,
} as ReaderFragment;
};


1 change: 1 addition & 0 deletions src/core/types.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ export interface Normalizer {
data: Response<TQuery>,
operation: Operation<TQuery>
) => { [key: string]: object };
getDataID: GetDataID;
}

export interface Store {
Expand Down
3 changes: 1 addition & 2 deletions src/hooks/useFragment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import {
KeyReturnType,
GraphQLTaggedNode,
} from "../core/types";
import { assertBabelPlugin } from "../utils";
import { useGraphQLStore } from "./useGraphQLStore";

export function useFragment<TKey extends KeyType>(
Expand All @@ -14,6 +13,6 @@ export function useFragment<TKey extends KeyType>(
): $Call<KeyReturnType<TKey>> {
const node = getFragment(fragmentNode);
const store = useGraphQLStore();
const data = store.useFragment(node as any, fragmentRef);
const data = store.useFragment(node, fragmentRef);
return data;
}

1 comment on commit d220d01

@vercel
Copy link

@vercel vercel bot commented on d220d01 Oct 16, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.