Mirror: The highly customizable and versatile GraphQL client with which you add on features like normalized caching as you grow.

chore(core): Reformat some code to reduce @urql/core size (#3042)

Changed files
+83 -97
exchanges
graphcache
default-storage
src
packages
scripts
+1 -1
exchanges/graphcache/default-storage/package.json
···
"./package.json": "./package.json"
},
"dependencies": {
-
"@urql/core": ">=3.1.1",
"wonka": "^6.0.0"
}
}
···
"./package.json": "./package.json"
},
"dependencies": {
+
"@urql/core": ">=3.2.2",
"wonka": "^6.0.0"
}
}
+5 -3
exchanges/graphcache/src/cacheExchange.ts
···
} from 'wonka';
import { query, write, writeOptimistic } from './operations';
-
import { addCacheOutcome, toRequestPolicy } from './helpers/operation';
import { filterVariables, getMainOperation } from './ast';
import { Store, noopDataState, hydrateData, reserveLayer } from './store';
import { Data, Dependencies, CacheExchangeOpts } from './types';
···
message: 'The result could not be retrieved from the cache',
operation: res.operation,
});
-
return addCacheOutcome(res.operation, 'miss');
})
);
···
!reexecutingOperations.has(res.operation.key));
const result: OperationResult = {
-
operation: addCacheOutcome(res.operation, res.outcome),
data: res.data,
error: res.error,
extensions: res.extensions,
···
} from 'wonka';
import { query, write, writeOptimistic } from './operations';
+
import { addMetadata, toRequestPolicy } from './helpers/operation';
import { filterVariables, getMainOperation } from './ast';
import { Store, noopDataState, hydrateData, reserveLayer } from './store';
import { Data, Dependencies, CacheExchangeOpts } from './types';
···
message: 'The result could not be retrieved from the cache',
operation: res.operation,
});
+
return addMetadata(res.operation, { cacheOutcome: 'miss' });
})
);
···
!reexecutingOperations.has(res.operation.key));
const result: OperationResult = {
+
operation: addMetadata(res.operation, {
+
cacheOutcome: res.outcome,
+
}),
data: res.data,
error: res.error,
extensions: res.extensions,
+4 -4
exchanges/graphcache/src/helpers/operation.ts
···
import {
Operation,
RequestPolicy,
-
CacheOutcome,
makeOperation,
} from '@urql/core';
// Returns the given operation result with added cacheOutcome meta field
-
export const addCacheOutcome = (
operation: Operation,
-
outcome: CacheOutcome
): Operation =>
makeOperation(operation.kind, operation, {
...operation.context,
meta: {
...operation.context.meta,
-
cacheOutcome: outcome,
},
});
···
import {
Operation,
RequestPolicy,
+
OperationDebugMeta,
makeOperation,
} from '@urql/core';
// Returns the given operation result with added cacheOutcome meta field
+
export const addMetadata = (
operation: Operation,
+
meta: OperationDebugMeta
): Operation =>
makeOperation(operation.kind, operation, {
...operation.context,
meta: {
...operation.context.meta,
+
...meta,
},
});
+31 -41
packages/core/src/gql.ts
···
import { AnyVariables, TypedDocumentNode } from './types';
import { keyDocument, stringifyDocument } from './utils';
-
const applyDefinitions = (
-
fragmentNames: Map<string, string>,
-
target: DefinitionNode[],
-
source: Array<DefinitionNode> | ReadonlyArray<DefinitionNode>
-
) => {
-
for (const definition of source) {
-
if (definition.kind === Kind.FRAGMENT_DEFINITION) {
-
const name = definition.name.value;
-
const value = stringifyDocument(definition);
-
// Fragments will be deduplicated according to this Map
-
if (!fragmentNames.has(name)) {
-
fragmentNames.set(name, value);
-
target.push(definition);
-
} else if (
-
process.env.NODE_ENV !== 'production' &&
-
fragmentNames.get(name) !== value
-
) {
-
// Fragments with the same names is expected to have the same contents
-
console.warn(
-
'[WARNING: Duplicate Fragment] A fragment with name `' +
-
name +
-
'` already exists in this document.\n' +
-
'While fragment names may not be unique across your source, each name must be unique per document.'
-
);
-
}
-
} else {
-
target.push(definition);
-
}
-
}
-
};
-
/** A GraphQL parse function, which may be called as a tagged template literal, returning a parsed {@link DocumentNode}.
*
* @remarks
···
string: string
): TypedDocumentNode<Data, Variables>;
-
function gql(/* arguments */) {
const fragmentNames = new Map<string, string>();
const definitions: DefinitionNode[] = [];
-
const interpolations: DefinitionNode[] = [];
// Apply the entire tagged template body's definitions
-
let body: string = Array.isArray(arguments[0])
-
? arguments[0][0]
-
: arguments[0] || '';
for (let i = 1; i < arguments.length; i++) {
const value = arguments[i];
if (value && value.definitions) {
-
interpolations.push(...value.definitions);
} else {
body += value;
}
···
body += arguments[0][i];
}
-
// Apply the tag's body definitions
-
applyDefinitions(fragmentNames, definitions, keyDocument(body).definitions);
-
// Copy over each interpolated document's definitions
-
applyDefinitions(fragmentNames, definitions, interpolations);
return keyDocument({
kind: Kind.DOCUMENT,
···
import { AnyVariables, TypedDocumentNode } from './types';
import { keyDocument, stringifyDocument } from './utils';
/** A GraphQL parse function, which may be called as a tagged template literal, returning a parsed {@link DocumentNode}.
*
* @remarks
···
string: string
): TypedDocumentNode<Data, Variables>;
+
function gql(parts: string | TemplateStringsArray /* arguments */) {
const fragmentNames = new Map<string, string>();
const definitions: DefinitionNode[] = [];
+
const source: DocumentNode[] = [];
// Apply the entire tagged template body's definitions
+
let body: string = Array.isArray(parts) ? parts[0] : parts || '';
for (let i = 1; i < arguments.length; i++) {
const value = arguments[i];
if (value && value.definitions) {
+
source.push(value);
} else {
body += value;
}
···
body += arguments[0][i];
}
+
source.unshift(keyDocument(body));
+
for (const document of source) {
+
for (const definition of document.definitions) {
+
if (definition.kind === Kind.FRAGMENT_DEFINITION) {
+
const name = definition.name.value;
+
const value = stringifyDocument(definition);
+
// Fragments will be deduplicated according to this Map
+
if (!fragmentNames.has(name)) {
+
fragmentNames.set(name, value);
+
definitions.push(definition);
+
} else if (
+
process.env.NODE_ENV !== 'production' &&
+
fragmentNames.get(name) !== value
+
) {
+
// Fragments with the same names is expected to have the same contents
+
console.warn(
+
'[WARNING: Duplicate Fragment] A fragment with name `' +
+
name +
+
'` already exists in this document.\n' +
+
'While fragment names may not be unique across your source, each name must be unique per document.'
+
);
+
}
+
} else {
+
definitions.push(definition);
+
}
+
}
+
}
return keyDocument({
kind: Kind.DOCUMENT,
+1
packages/core/src/types.ts
···
* `OperationResult`s and is filled in by exchanges across the codebase in development.
*
* This data is not for production use and hence shouldn't be used or relied upon directly.
*/
meta?: OperationDebugMeta;
/** Instructs fetch exchanges to use a GET request.
···
* `OperationResult`s and is filled in by exchanges across the codebase in development.
*
* This data is not for production use and hence shouldn't be used or relied upon directly.
+
* In production, this may not be set by default exchanges.
*/
meta?: OperationDebugMeta;
/** Instructs fetch exchanges to use a GET request.
+1 -1
packages/core/src/utils/hash.ts
···
* @see {@link http://www.cse.yorku.ca/~oz/hash.html#djb2} for a further description of djb2.
*/
export const phash = (x: string, seed?: HashValue): HashValue => {
-
let h = typeof seed === 'number' ? seed | 0 : 5381;
for (let i = 0, l = x.length | 0; i < l; i++)
h = (h << 5) + h + x.charCodeAt(i);
return h as HashValue;
···
* @see {@link http://www.cse.yorku.ca/~oz/hash.html#djb2} for a further description of djb2.
*/
export const phash = (x: string, seed?: HashValue): HashValue => {
+
let h = (seed || 5381) | 0;
for (let i = 0, l = x.length | 0; i < l; i++)
h = (h << 5) + h + x.charCodeAt(i);
return h as HashValue;
+7 -7
packages/core/src/utils/request.ts
···
const SOURCE_NAME = 'gql';
const GRAPHQL_STRING_RE = /("{3}[\s\S]*"{3}|"(?:\\.|[^"])*")/g;
-
const REPLACE_CHAR_RE = /(#[^\n\r]+)?(?:\n|\r\n?|$)+/g;
const replaceOutsideStrings = (str: string, idx: number) =>
idx % 2 === 0 ? str.replace(REPLACE_CHAR_RE, '\n') : str;
···
): HashValue => {
let key = phash(stringifyDocument(node));
// Add the operation name to the produced hash
-
if (typeof node === 'object' && 'definitions' in node) {
-
const operationName = getOperationName(node);
if (operationName) key = phash(`\n# ${operationName}`, key);
}
return key;
···
Data = any,
Variables extends AnyVariables = AnyVariables
>(
-
q: string | DocumentNode | TypedDocumentNode<Data, Variables>,
-
variables: Variables
): GraphQLRequest<Data, Variables> => {
-
if (!variables) variables = {} as Variables;
-
const query = keyDocument(q);
const printedVars = stringifyVariables(variables);
let key = query.__key;
if (printedVars !== '{}') key = phash(printedVars, key);
···
const SOURCE_NAME = 'gql';
const GRAPHQL_STRING_RE = /("{3}[\s\S]*"{3}|"(?:\\.|[^"])*")/g;
+
const REPLACE_CHAR_RE = /(?:#[^\n\r]+)?(?:[\r\n]+|$)/g;
const replaceOutsideStrings = (str: string, idx: number) =>
idx % 2 === 0 ? str.replace(REPLACE_CHAR_RE, '\n') : str;
···
): HashValue => {
let key = phash(stringifyDocument(node));
// Add the operation name to the produced hash
+
if ((node as DocumentNode).definitions) {
+
const operationName = getOperationName(node as DocumentNode);
if (operationName) key = phash(`\n# ${operationName}`, key);
}
return key;
···
Data = any,
Variables extends AnyVariables = AnyVariables
>(
+
_query: string | DocumentNode | TypedDocumentNode<Data, Variables>,
+
_variables: Variables
): GraphQLRequest<Data, Variables> => {
+
const variables = _variables || ({} as Variables);
+
const query = keyDocument(_query);
const printedVars = stringifyVariables(variables);
let key = query.__key;
if (printedVars !== '{}') key = phash(printedVars, key);
+3 -12
packages/core/src/utils/result.ts
···
result: ExecutionResult,
response?: any
): OperationResult => {
-
if (
-
(!('data' in result) && !('errors' in result)) ||
-
'incremental' in result
-
) {
throw new Error('No Content');
}
···
response,
})
: undefined,
-
extensions:
-
(typeof result.extensions === 'object' && result.extensions) || undefined,
hasNext: result.hasNext == null ? defaultHasNext : result.hasNext,
};
};
···
// NOTE: We handle the old version of the incremental delivery payloads as well
if ('path' in nextResult) {
-
incremental = [
-
{
-
data: nextResult.data,
-
path: nextResult.path,
-
} as IncrementalPayload,
-
];
}
if (incremental) {
···
result: ExecutionResult,
response?: any
): OperationResult => {
+
if (!('data' in result) && !('errors' in result)) {
throw new Error('No Content');
}
···
response,
})
: undefined,
+
extensions: result.extensions ? { ...result.extensions } : undefined,
hasNext: result.hasNext == null ? defaultHasNext : result.hasNext,
};
};
···
// NOTE: We handle the old version of the incremental delivery payloads as well
if ('path' in nextResult) {
+
incremental = [nextResult as IncrementalPayload];
}
if (incremental) {
+8 -16
packages/core/src/utils/streamUtils.ts
···
-
import { Source, subscribe, pipe } from 'wonka';
import { OperationResult, PromisifiedSource } from '../types';
/** Patches a `toPromise` method onto the `Source` passed to it.
···
export function withPromise<T extends OperationResult>(
source$: Source<T>
): PromisifiedSource<T> {
-
(source$ as PromisifiedSource<T>).toPromise = () => {
-
return new Promise(resolve => {
-
const subscription = pipe(
-
source$,
-
subscribe(result => {
-
if (!result.stale && !result.hasNext) {
-
Promise.resolve().then(() => {
-
subscription.unsubscribe();
-
resolve(result);
-
});
-
}
-
})
-
);
-
});
-
};
return source$ as PromisifiedSource<T>;
}
···
+
import { Source, take, filter, toPromise, pipe } from 'wonka';
import { OperationResult, PromisifiedSource } from '../types';
/** Patches a `toPromise` method onto the `Source` passed to it.
···
export function withPromise<T extends OperationResult>(
source$: Source<T>
): PromisifiedSource<T> {
+
(source$ as PromisifiedSource<T>).toPromise = () =>
+
pipe(
+
source$,
+
filter(result => !result.stale && !result.hasNext),
+
take(1),
+
toPromise
+
);
return source$ as PromisifiedSource<T>;
}
+4 -6
packages/core/src/utils/stringifyVariables.ts
···
return stringify(x.toJSON());
} else if (Array.isArray(x)) {
let out = '[';
-
for (let value of x) {
-
if (out !== '[') out += ',';
-
value = stringify(value);
-
out += value.length > 0 ? value : 'null';
}
-
out += ']';
return out;
}
···
if (!keys.length && x.constructor && x.constructor !== Object) {
const key = cache.get(x) || Math.random().toString(36).slice(2);
cache.set(x, key);
-
return `{"__key":"${key}"}`;
}
seen.add(x);
···
return stringify(x.toJSON());
} else if (Array.isArray(x)) {
let out = '[';
+
for (const value of x) {
+
if (out.length > 1) out += ',';
+
out += stringify(value) || 'null';
}
out += ']';
return out;
}
···
if (!keys.length && x.constructor && x.constructor !== Object) {
const key = cache.get(x) || Math.random().toString(36).slice(2);
cache.set(x, key);
+
return stringify({ __key: key });
}
seen.add(x);
+18 -6
scripts/babel/transform-debug-target.mjs
···
-
const dispatchProperty = 'dispatchDebug';
const visited = 'visitedByDebugTargetTransformer';
const warningDevCheckTemplate = `
process.env.NODE_ENV !== 'production' ? NODE : undefined
`.trim();
const plugin = ({ template, types: t }) => {
const wrapWithDevCheck = template.expression(
warningDevCheckTemplate,
{ placeholderPattern: /^NODE$/ }
);
let name = 'unknownExchange';
return {
···
}
},
CallExpression(path, meta) {
-
if (
-
!path.node[visited] &&
-
path.node.callee &&
-
path.node.callee.name === dispatchProperty
-
) {
path.node[visited] = true;
if (t.isObjectExpression(path.node.arguments[0]) && !meta.filename.endsWith('compose.ts')) {
path.node.arguments[0].properties.push(
···
}
path.replaceWith(wrapWithDevCheck({ NODE: path.node }));
}
}
}
···
const visited = 'visitedByDebugTargetTransformer';
const warningDevCheckTemplate = `
process.env.NODE_ENV !== 'production' ? NODE : undefined
`.trim();
+
const noopTransformTemplate = `
+
process.env.NODE_ENV !== 'production' ? NODE : FALLBACK
+
`.trim();
+
const plugin = ({ template, types: t }) => {
const wrapWithDevCheck = template.expression(
warningDevCheckTemplate,
{ placeholderPattern: /^NODE$/ }
);
+
const wrapWithNoopTransform = template.expression(
+
noopTransformTemplate,
+
{ placeholderPattern: /^(NODE|FALLBACK)$/ }
+
);
+
let name = 'unknownExchange';
return {
···
}
},
CallExpression(path, meta) {
+
if (path.node[visited] || !path.node.callee) return;
+
+
if (path.node.callee.name === 'dispatchDebug') {
path.node[visited] = true;
if (t.isObjectExpression(path.node.arguments[0]) && !meta.filename.endsWith('compose.ts')) {
path.node.arguments[0].properties.push(
···
}
path.replaceWith(wrapWithDevCheck({ NODE: path.node }));
+
} else if (path.node.callee.name === 'addMetadata') {
+
path.node[visited] = true;
+
path.replaceWith(wrapWithNoopTransform({
+
NODE: path.node,
+
FALLBACK: path.node.arguments[0],
+
}));
}
}
}