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)

authored by kitten.sh and committed by

GitHub c8b0623d 0525948e

+83 -97
+1 -1
exchanges/graphcache/default-storage/package.json
··· 16 16 "./package.json": "./package.json" 17 17 }, 18 18 "dependencies": { 19 - "@urql/core": ">=3.1.1", 19 + "@urql/core": ">=3.2.2", 20 20 "wonka": "^6.0.0" 21 21 } 22 22 }
+5 -3
exchanges/graphcache/src/cacheExchange.ts
··· 21 21 } from 'wonka'; 22 22 23 23 import { query, write, writeOptimistic } from './operations'; 24 - import { addCacheOutcome, toRequestPolicy } from './helpers/operation'; 24 + import { addMetadata, toRequestPolicy } from './helpers/operation'; 25 25 import { filterVariables, getMainOperation } from './ast'; 26 26 import { Store, noopDataState, hydrateData, reserveLayer } from './store'; 27 27 import { Data, Dependencies, CacheExchangeOpts } from './types'; ··· 295 295 message: 'The result could not be retrieved from the cache', 296 296 operation: res.operation, 297 297 }); 298 - return addCacheOutcome(res.operation, 'miss'); 298 + return addMetadata(res.operation, { cacheOutcome: 'miss' }); 299 299 }) 300 300 ); 301 301 ··· 327 327 !reexecutingOperations.has(res.operation.key)); 328 328 329 329 const result: OperationResult = { 330 - operation: addCacheOutcome(res.operation, res.outcome), 330 + operation: addMetadata(res.operation, { 331 + cacheOutcome: res.outcome, 332 + }), 331 333 data: res.data, 332 334 error: res.error, 333 335 extensions: res.extensions,
+4 -4
exchanges/graphcache/src/helpers/operation.ts
··· 1 1 import { 2 2 Operation, 3 3 RequestPolicy, 4 - CacheOutcome, 4 + OperationDebugMeta, 5 5 makeOperation, 6 6 } from '@urql/core'; 7 7 8 8 // Returns the given operation result with added cacheOutcome meta field 9 - export const addCacheOutcome = ( 9 + export const addMetadata = ( 10 10 operation: Operation, 11 - outcome: CacheOutcome 11 + meta: OperationDebugMeta 12 12 ): Operation => 13 13 makeOperation(operation.kind, operation, { 14 14 ...operation.context, 15 15 meta: { 16 16 ...operation.context.meta, 17 - cacheOutcome: outcome, 17 + ...meta, 18 18 }, 19 19 }); 20 20
+31 -41
packages/core/src/gql.ts
··· 3 3 import { AnyVariables, TypedDocumentNode } from './types'; 4 4 import { keyDocument, stringifyDocument } from './utils'; 5 5 6 - const applyDefinitions = ( 7 - fragmentNames: Map<string, string>, 8 - target: DefinitionNode[], 9 - source: Array<DefinitionNode> | ReadonlyArray<DefinitionNode> 10 - ) => { 11 - for (const definition of source) { 12 - if (definition.kind === Kind.FRAGMENT_DEFINITION) { 13 - const name = definition.name.value; 14 - const value = stringifyDocument(definition); 15 - // Fragments will be deduplicated according to this Map 16 - if (!fragmentNames.has(name)) { 17 - fragmentNames.set(name, value); 18 - target.push(definition); 19 - } else if ( 20 - process.env.NODE_ENV !== 'production' && 21 - fragmentNames.get(name) !== value 22 - ) { 23 - // Fragments with the same names is expected to have the same contents 24 - console.warn( 25 - '[WARNING: Duplicate Fragment] A fragment with name `' + 26 - name + 27 - '` already exists in this document.\n' + 28 - 'While fragment names may not be unique across your source, each name must be unique per document.' 29 - ); 30 - } 31 - } else { 32 - target.push(definition); 33 - } 34 - } 35 - }; 36 - 37 6 /** A GraphQL parse function, which may be called as a tagged template literal, returning a parsed {@link DocumentNode}. 38 7 * 39 8 * @remarks ··· 89 58 string: string 90 59 ): TypedDocumentNode<Data, Variables>; 91 60 92 - function gql(/* arguments */) { 61 + function gql(parts: string | TemplateStringsArray /* arguments */) { 93 62 const fragmentNames = new Map<string, string>(); 94 63 const definitions: DefinitionNode[] = []; 95 - const interpolations: DefinitionNode[] = []; 64 + const source: DocumentNode[] = []; 96 65 97 66 // Apply the entire tagged template body's definitions 98 - let body: string = Array.isArray(arguments[0]) 99 - ? arguments[0][0] 100 - : arguments[0] || ''; 67 + let body: string = Array.isArray(parts) ? parts[0] : parts || ''; 101 68 for (let i = 1; i < arguments.length; i++) { 102 69 const value = arguments[i]; 103 70 if (value && value.definitions) { 104 - interpolations.push(...value.definitions); 71 + source.push(value); 105 72 } else { 106 73 body += value; 107 74 } ··· 109 76 body += arguments[0][i]; 110 77 } 111 78 112 - // Apply the tag's body definitions 113 - applyDefinitions(fragmentNames, definitions, keyDocument(body).definitions); 114 - // Copy over each interpolated document's definitions 115 - applyDefinitions(fragmentNames, definitions, interpolations); 79 + source.unshift(keyDocument(body)); 80 + for (const document of source) { 81 + for (const definition of document.definitions) { 82 + if (definition.kind === Kind.FRAGMENT_DEFINITION) { 83 + const name = definition.name.value; 84 + const value = stringifyDocument(definition); 85 + // Fragments will be deduplicated according to this Map 86 + if (!fragmentNames.has(name)) { 87 + fragmentNames.set(name, value); 88 + definitions.push(definition); 89 + } else if ( 90 + process.env.NODE_ENV !== 'production' && 91 + fragmentNames.get(name) !== value 92 + ) { 93 + // Fragments with the same names is expected to have the same contents 94 + console.warn( 95 + '[WARNING: Duplicate Fragment] A fragment with name `' + 96 + name + 97 + '` already exists in this document.\n' + 98 + 'While fragment names may not be unique across your source, each name must be unique per document.' 99 + ); 100 + } 101 + } else { 102 + definitions.push(definition); 103 + } 104 + } 105 + } 116 106 117 107 return keyDocument({ 118 108 kind: Kind.DOCUMENT,
+1
packages/core/src/types.ts
··· 433 433 * `OperationResult`s and is filled in by exchanges across the codebase in development. 434 434 * 435 435 * This data is not for production use and hence shouldn't be used or relied upon directly. 436 + * In production, this may not be set by default exchanges. 436 437 */ 437 438 meta?: OperationDebugMeta; 438 439 /** Instructs fetch exchanges to use a GET request.
+1 -1
packages/core/src/utils/hash.ts
··· 30 30 * @see {@link http://www.cse.yorku.ca/~oz/hash.html#djb2} for a further description of djb2. 31 31 */ 32 32 export const phash = (x: string, seed?: HashValue): HashValue => { 33 - let h = typeof seed === 'number' ? seed | 0 : 5381; 33 + let h = (seed || 5381) | 0; 34 34 for (let i = 0, l = x.length | 0; i < l; i++) 35 35 h = (h << 5) + h + x.charCodeAt(i); 36 36 return h as HashValue;
+7 -7
packages/core/src/utils/request.ts
··· 24 24 25 25 const SOURCE_NAME = 'gql'; 26 26 const GRAPHQL_STRING_RE = /("{3}[\s\S]*"{3}|"(?:\\.|[^"])*")/g; 27 - const REPLACE_CHAR_RE = /(#[^\n\r]+)?(?:\n|\r\n?|$)+/g; 27 + const REPLACE_CHAR_RE = /(?:#[^\n\r]+)?(?:[\r\n]+|$)/g; 28 28 29 29 const replaceOutsideStrings = (str: string, idx: number) => 30 30 idx % 2 === 0 ? str.replace(REPLACE_CHAR_RE, '\n') : str; ··· 95 95 ): HashValue => { 96 96 let key = phash(stringifyDocument(node)); 97 97 // Add the operation name to the produced hash 98 - if (typeof node === 'object' && 'definitions' in node) { 99 - const operationName = getOperationName(node); 98 + if ((node as DocumentNode).definitions) { 99 + const operationName = getOperationName(node as DocumentNode); 100 100 if (operationName) key = phash(`\n# ${operationName}`, key); 101 101 } 102 102 return key; ··· 151 151 Data = any, 152 152 Variables extends AnyVariables = AnyVariables 153 153 >( 154 - q: string | DocumentNode | TypedDocumentNode<Data, Variables>, 155 - variables: Variables 154 + _query: string | DocumentNode | TypedDocumentNode<Data, Variables>, 155 + _variables: Variables 156 156 ): GraphQLRequest<Data, Variables> => { 157 - if (!variables) variables = {} as Variables; 158 - const query = keyDocument(q); 157 + const variables = _variables || ({} as Variables); 158 + const query = keyDocument(_query); 159 159 const printedVars = stringifyVariables(variables); 160 160 let key = query.__key; 161 161 if (printedVars !== '{}') key = phash(printedVars, key);
+3 -12
packages/core/src/utils/result.ts
··· 28 28 result: ExecutionResult, 29 29 response?: any 30 30 ): OperationResult => { 31 - if ( 32 - (!('data' in result) && !('errors' in result)) || 33 - 'incremental' in result 34 - ) { 31 + if (!('data' in result) && !('errors' in result)) { 35 32 throw new Error('No Content'); 36 33 } 37 34 ··· 45 42 response, 46 43 }) 47 44 : undefined, 48 - extensions: 49 - (typeof result.extensions === 'object' && result.extensions) || undefined, 45 + extensions: result.extensions ? { ...result.extensions } : undefined, 50 46 hasNext: result.hasNext == null ? defaultHasNext : result.hasNext, 51 47 }; 52 48 }; ··· 83 79 84 80 // NOTE: We handle the old version of the incremental delivery payloads as well 85 81 if ('path' in nextResult) { 86 - incremental = [ 87 - { 88 - data: nextResult.data, 89 - path: nextResult.path, 90 - } as IncrementalPayload, 91 - ]; 82 + incremental = [nextResult as IncrementalPayload]; 92 83 } 93 84 94 85 if (incremental) {
+8 -16
packages/core/src/utils/streamUtils.ts
··· 1 - import { Source, subscribe, pipe } from 'wonka'; 1 + import { Source, take, filter, toPromise, pipe } from 'wonka'; 2 2 import { OperationResult, PromisifiedSource } from '../types'; 3 3 4 4 /** Patches a `toPromise` method onto the `Source` passed to it. ··· 9 9 export function withPromise<T extends OperationResult>( 10 10 source$: Source<T> 11 11 ): PromisifiedSource<T> { 12 - (source$ as PromisifiedSource<T>).toPromise = () => { 13 - return new Promise(resolve => { 14 - const subscription = pipe( 15 - source$, 16 - subscribe(result => { 17 - if (!result.stale && !result.hasNext) { 18 - Promise.resolve().then(() => { 19 - subscription.unsubscribe(); 20 - resolve(result); 21 - }); 22 - } 23 - }) 24 - ); 25 - }); 26 - }; 12 + (source$ as PromisifiedSource<T>).toPromise = () => 13 + pipe( 14 + source$, 15 + filter(result => !result.stale && !result.hasNext), 16 + take(1), 17 + toPromise 18 + ); 27 19 28 20 return source$ as PromisifiedSource<T>; 29 21 }
+4 -6
packages/core/src/utils/stringifyVariables.ts
··· 10 10 return stringify(x.toJSON()); 11 11 } else if (Array.isArray(x)) { 12 12 let out = '['; 13 - for (let value of x) { 14 - if (out !== '[') out += ','; 15 - value = stringify(value); 16 - out += value.length > 0 ? value : 'null'; 13 + for (const value of x) { 14 + if (out.length > 1) out += ','; 15 + out += stringify(value) || 'null'; 17 16 } 18 - 19 17 out += ']'; 20 18 return out; 21 19 } ··· 24 22 if (!keys.length && x.constructor && x.constructor !== Object) { 25 23 const key = cache.get(x) || Math.random().toString(36).slice(2); 26 24 cache.set(x, key); 27 - return `{"__key":"${key}"}`; 25 + return stringify({ __key: key }); 28 26 } 29 27 30 28 seen.add(x);
+18 -6
scripts/babel/transform-debug-target.mjs
··· 1 - const dispatchProperty = 'dispatchDebug'; 2 1 const visited = 'visitedByDebugTargetTransformer'; 3 2 4 3 const warningDevCheckTemplate = ` 5 4 process.env.NODE_ENV !== 'production' ? NODE : undefined 6 5 `.trim(); 7 6 7 + const noopTransformTemplate = ` 8 + process.env.NODE_ENV !== 'production' ? NODE : FALLBACK 9 + `.trim(); 10 + 8 11 const plugin = ({ template, types: t }) => { 9 12 const wrapWithDevCheck = template.expression( 10 13 warningDevCheckTemplate, 11 14 { placeholderPattern: /^NODE$/ } 12 15 ); 13 16 17 + const wrapWithNoopTransform = template.expression( 18 + noopTransformTemplate, 19 + { placeholderPattern: /^(NODE|FALLBACK)$/ } 20 + ); 21 + 14 22 let name = 'unknownExchange'; 15 23 16 24 return { ··· 22 30 } 23 31 }, 24 32 CallExpression(path, meta) { 25 - if ( 26 - !path.node[visited] && 27 - path.node.callee && 28 - path.node.callee.name === dispatchProperty 29 - ) { 33 + if (path.node[visited] || !path.node.callee) return; 34 + 35 + if (path.node.callee.name === 'dispatchDebug') { 30 36 path.node[visited] = true; 31 37 if (t.isObjectExpression(path.node.arguments[0]) && !meta.filename.endsWith('compose.ts')) { 32 38 path.node.arguments[0].properties.push( ··· 38 44 } 39 45 40 46 path.replaceWith(wrapWithDevCheck({ NODE: path.node })); 47 + } else if (path.node.callee.name === 'addMetadata') { 48 + path.node[visited] = true; 49 + path.replaceWith(wrapWithNoopTransform({ 50 + NODE: path.node, 51 + FALLBACK: path.node.arguments[0], 52 + })); 41 53 } 42 54 } 43 55 }