@graphql-codegen/gql-tag-operations
Version: 
GraphQL Code Generator plugin for generating a typed gql tag function
128 lines (127 loc) • 6.64 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.plugin = void 0;
const visitor_plugin_common_1 = require("@graphql-codegen/visitor-plugin-common");
const documentTypePartial = `
export type DocumentType<TDocumentNode extends DocumentNode<any, any>> = TDocumentNode extends DocumentNode<
  infer TType,
  any
>
  ? TType
  : never;
`.split(`\n`);
const plugin = (_, __, { sourcesWithOperations, useTypeImports, augmentedModuleName, gqlTagName = 'gql', emitLegacyCommonJSImports, documentMode, }, _info) => {
    if (documentMode === visitor_plugin_common_1.DocumentMode.string) {
        const code = [`import * as types from './graphql${emitLegacyCommonJSImports ? '' : '.js'}';\n`, `\n`];
        // We need the mapping from source as written to full document source to
        // handle fragments. An identity function would not suffice.
        if (sourcesWithOperations.length > 0) {
            code.push([...getDocumentRegistryChunk(sourcesWithOperations)].join(''));
        }
        else {
            code.push('const documents = {}');
        }
        if (sourcesWithOperations.length > 0) {
            code.push([...getGqlOverloadChunk(sourcesWithOperations, gqlTagName, 'augmented', emitLegacyCommonJSImports), `\n`].join(''));
        }
        code.push([`export function ${gqlTagName}(source: string) {\n`, `  return (documents as any)[source] ?? {};\n`, `}\n`].join(''));
        return code.join('\n');
    }
    if (augmentedModuleName == null) {
        const code = [
            `import * as types from './graphql${emitLegacyCommonJSImports ? '' : '.js'}';\n`,
            `${useTypeImports ? 'import type' : 'import'} { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core';\n`,
            `\n`,
        ];
        if (sourcesWithOperations.length > 0) {
            code.push([...getDocumentRegistryChunk(sourcesWithOperations)].join(''));
        }
        else {
            code.push('const documents = [];');
        }
        code.push([
            `\n`,
            `/**\n * The ${gqlTagName} function is used to parse GraphQL queries into a document that can be used by GraphQL clients.\n *\n`,
            ` *\n * @example\n`,
            ' * ```ts\n',
            ` * const query = ${gqlTagName}` + '(`query GetUser($id: ID!) { user(id: $id) { name } }`);\n',
            ' * ```\n *\n',
            ` * The query argument is unknown!\n`,
            ` * Please regenerate the types.\n`,
            ` */\n`,
            `export function ${gqlTagName}(source: string): unknown;\n`,
            `\n`,
        ].join(''));
        if (sourcesWithOperations.length > 0) {
            code.push([...getGqlOverloadChunk(sourcesWithOperations, gqlTagName, 'lookup', emitLegacyCommonJSImports), `\n`].join(''));
        }
        code.push([
            `export function ${gqlTagName}(source: string) {\n`,
            `  return (documents as any)[source] ?? {};\n`,
            `}\n`,
            `\n`,
            ...documentTypePartial,
        ].join(''));
        return code.join('');
    }
    return [
        `import { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core';\n`,
        `declare module "${augmentedModuleName}" {`,
        [
            `\n`,
            ...(sourcesWithOperations.length > 0
                ? getGqlOverloadChunk(sourcesWithOperations, gqlTagName, 'augmented', emitLegacyCommonJSImports)
                : []),
            `export function ${gqlTagName}(source: string): unknown;\n`,
            `\n`,
            ...documentTypePartial,
        ]
            .map(line => (line === `\n` ? line : `  ${line}`))
            .join(``),
        `}`,
    ].join(`\n`);
};
exports.plugin = plugin;
function getDocumentRegistryChunk(sourcesWithOperations = []) {
    const lines = new Array();
    // It's possible for there to be duplicate sourceOperations, this set will ensure we have unique records for our document registry
    const linesDupCheck = new Set();
    lines.push(`/**\n * Map of all GraphQL operations in the project.\n *\n * This map has several performance disadvantages:\n`, ` * 1. It is not tree-shakeable, so it will include all operations in the project.\n`, ` * 2. It is not minifiable, so the string of a GraphQL query will be multiple times inside the bundle.\n`, ` * 3. It does not support dead code elimination, so it will add unused operations.\n *\n`, ` * Therefore it is highly recommended to use the babel or swc plugin for production.\n`, ` * Learn more about it here: https://the-guild.dev/graphql/codegen/plugins/presets/preset-client#reducing-bundle-size\n */\n`, `type Documents = {\n`);
    for (const { operations, ...rest } of sourcesWithOperations) {
        const originalString = rest.source.rawSDL;
        const operation = operations[0];
        const aboutToPushLine = `    ${JSON.stringify(originalString)}: typeof types.${operation.initialName},\n`;
        if (!linesDupCheck.has(aboutToPushLine)) {
            lines.push(aboutToPushLine);
            linesDupCheck.add(aboutToPushLine);
        }
    }
    lines.push(`};\n`, `const documents: Documents = {\n`);
    for (const { operations, ...rest } of sourcesWithOperations) {
        const originalString = rest.source.rawSDL;
        const operation = operations[0];
        const aboutToPushLine = `    ${JSON.stringify(originalString)}: types.${operation.initialName},\n`;
        if (!linesDupCheck.has(aboutToPushLine)) {
            lines.push(aboutToPushLine);
            linesDupCheck.add(aboutToPushLine);
        }
    }
    lines.push(`};\n`);
    return lines;
}
function getGqlOverloadChunk(sourcesWithOperations, gqlTagName, mode, emitLegacyCommonJSImports) {
    const lines = new Set();
    // We intentionally don't use a <T extends keyof typeof documents> generic, because TS
    // would print very long `gql` function signatures (duplicating the source).
    for (const { operations, ...rest } of sourcesWithOperations) {
        const originalString = rest.source.rawSDL;
        const returnType = mode === 'lookup'
            ? `(typeof documents)[${JSON.stringify(originalString)}]`
            : emitLegacyCommonJSImports
                ? `typeof import('./graphql').${operations[0].initialName}`
                : `typeof import('./graphql.js').${operations[0].initialName}`;
        lines.add(`/**\n * The ${gqlTagName} function is used to parse GraphQL queries into a document that can be used by GraphQL clients.\n */\n` +
            `export function ${gqlTagName}(source: ${JSON.stringify(originalString)}): ${returnType};\n`);
    }
    return lines;
}