graphql-request
Version:
Minimal GraphQL client supporting Node and browsers for scripts or simple apps.
74 lines • 3.44 kB
JavaScript
import { tryCatch } from '../../lib/prelude.js';
import { isOperationDefinitionNode } from '../lib/graphql.js';
import { parse } from 'graphql';
import { print } from 'graphql';
/**
* helpers
*/
const extractOperationName = (document) => {
let operationName = undefined;
const defs = document.definitions.filter(isOperationDefinitionNode);
if (defs.length === 1) {
operationName = defs[0].name?.value;
}
return operationName;
};
const extractIsMutation = (document) => {
let isMutation = false;
const defs = document.definitions.filter(isOperationDefinitionNode);
if (defs.length === 1) {
/* eslint-disable-next-line @typescript-eslint/no-unsafe-enum-comparison --
* graphql@15's `OperationTypeNode` is a type, but graphql@16's `OperationTypeNode` is a native TypeScript enum
* Therefore, we cannot use `OperationTypeNode.MUTATION` here because it wouldn't work with graphql@15
**/
isMutation = defs[0].operation === `mutation`;
}
return isMutation;
};
export const analyzeDocument = (document, excludeOperationName) => {
/**
* Normalize boxed String objects to primitive strings for compatibility with TypedDocumentString.
*
* Problem: \@graphql-codegen with `documentMode: 'string'` generates a TypedDocumentString class that
* extends the built-in String class, creating "boxed String" instances:
*
* @see https://github.com/dotansimha/graphql-code-generator/blob/c87b779b9b400813d733fa89dcb16724b30c6d16/packages/plugins/typescript/typed-document-node/src/index.ts#L40-L57
*
* ```
* class TypedDocumentString<TResult, TVariables> extends String
* constructor(value: string)
* super(value) // Creates a boxed String object, not a primitive string
* ```
*
* Boxed Strings are objects, not primitives:
* - `typeof "hello" === "string"` returns true
* - `typeof new String("hello") === "string"` returns false (returns "object")
*
* Without normalization, TypedDocumentString instances would fail the `typeof === 'string'` check
* and incorrectly be passed to `print()`, which expects a DocumentNode AST, causing a crash.
*
* Solution: Detect and convert boxed Strings to primitives using template literal coercion.
* - Primitive strings pass through (typeof === "string")
* - DocumentNode objects pass through (have .kind property)
* - Boxed Strings get converted to primitives (template literal to primitive string)
*
* @see https://github.com/graffle-js/graffle/issues/1453
*/
const normalizedDocument = typeof document === `string` || `kind` in document
? document
: String(document);
const expression = typeof normalizedDocument === `string` ? normalizedDocument : print(normalizedDocument);
let isMutation = false;
let operationName = undefined;
if (excludeOperationName) {
return { expression, isMutation, operationName };
}
const docNode = tryCatch(() => (typeof normalizedDocument === `string` ? parse(normalizedDocument) : normalizedDocument));
if (docNode instanceof Error) {
return { expression, isMutation, operationName };
}
operationName = extractOperationName(docNode);
isMutation = extractIsMutation(docNode);
return { expression, operationName, isMutation };
};
//# sourceMappingURL=analyzeDocument.js.map