# @therobot/codegen-vue-apollo

> Generates vue-apollo operations using composition-api, smart-query, or computed mixins

Latest version **0.1.1** (published 2020-01-25) · MIT license · 0 weekly downloads

## Install

```sh
npm install @therobot/codegen-vue-apollo
pnpm add @therobot/codegen-vue-apollo
yarn add @therobot/codegen-vue-apollo
bun add @therobot/codegen-vue-apollo
```

## Health

**Score 20/100 (F)** — status: abandoned.

Positive: has types; no vulnerabilities.

Warnings: low downloads; no esm support; pre 1.0.

Negative: abandoned; low maintenance score.

## Facts

| | |
|---|---|
| Version | 0.1.1 |
| Published | 2020-01-25 |
| First published | 2019-12-04 |
| Weekly downloads | 0 |
| License | MIT |
| TypeScript types | bundled |
| Module format | CommonJS |
| Dependencies | 8 |
| Unpacked size | 47.7 KB |
| Known vulnerabilities | 0 |
| Install scripts | no |
| Author | gcoda |
| Maintainers | gcoda |

## Links

- npm: https://www.npmjs.com/package/@therobot/codegen-vue-apollo
- npm.io page: https://npm.io/package/@therobot/codegen-vue-apollo

## Dependencies (8)

- [react](https://npm.io/package/react.md) ^16.12.0
- [tslib](https://npm.io/package/tslib.md) 1.10.0
- [graphql](https://npm.io/package/graphql.md) 14.5.8
- [auto-bind](https://npm.io/package/auto-bind.md) ^3.0.0
- [superstruct](https://npm.io/package/superstruct.md) ^0.8.3
- [@types/sharedworker](https://npm.io/package/@types/sharedworker.md) ^0.0.29
- [@graphql-codegen/plugin-helpers](https://npm.io/package/@graphql-codegen/plugin-helpers.md) ^1.9.1
- [@graphql-codegen/visitor-plugin-common](https://npm.io/package/@graphql-codegen/visitor-plugin-common.md) ^1.9.1

## Recent versions

- 0.1.1 (latest) — 2020-01-25
- 0.1.0 — 2020-01-25
- 0.0.7 — 2020-01-17
- 0.0.6 — 2020-01-16
- 0.0.5 — 2020-01-16
- 0.0.4 — 2020-01-02
- 0.0.3 — 2020-01-01
- 0.0.2 — 2019-12-31
- 0.0.1 — 2019-12-16
- 0.0.0 — 2019-12-04

## README

# Vue Apollo Codegen

```graphql
# src/allPosts.gql
query allPosts($id: ID!) {
  allPosts(categoryId: $id) {
    id
    title
  }
}
# src/components/Post/addComment.gql
mutation addComment($postId: ID!, $content: String!) {
  addComment(postId: $postId)
}
```

## Usage

```yaml
config:
  federation: true
  noSchemaStitching: true
hooks:
  afterAllFileWrite:
    - 'npx prettier --write  "./src/generated/**/*" # ignore args'
schema:
  - ../packages/*-server/**/*.gql # server schema
generates:
  ./src/generated/index.ts:
    schema:
      - ./src/apollo/clientSchema.gql
    documents: './src/**/*.gql'
    config:
      mixinComponents: false # default: true
      smartQueryDecorators: false # default: true

      useComposable: true # default: false
      partialVariablesOnComposableMutations: true # default: true

      # default: false - compatibility
      # should use typescript-graphql-files-modules
      noDeclareOperationModules: true

      noFragmentUtils: true # default: false
      noQueryUtils: true # default: false

      # from ClientSideBasePluginConfig
      noGraphQLTag: true # default: false
    plugins:
      - add: "import { DocumentNode } from 'graphql'"
      - time
      - add: '/* eslint-disable */'

      - typescript
      - typescript-operations
      - '@therobot/codegen-vue-apollo'

  ./src/generated/types.d.ts:
    documents: './src/**/*.gql'
    plugins:
      - typescript-graphql-files-modules
```

> in case of `Cannot use GraphQLScalarType "ID" from another module or realm` run `env NODE_ENV=production graphql-codegen`

# Composable Partial

> `apollo-composable@4.0.0-alpha.5`

TypeScript will scream at you when using `useMutation` without proper variables.

Since mutations, not called instantly, it should be possible to pass variables on `mutate` call. And this is where TypeScript should be concerned.

```graphql
mutation learn($questionId: ID!, $answerId: ID) {
  learn(answerId: $answerId, questionId: $questionId) @client {
    id
    score
    previousScore
  }
}
```

```typescript
const { mutate } = useLearnMutation({
  // variables, not required
  variables: {
    questionId, // also not required
  },
})
mutate({
  // '{}' is not assignable
  // 'questionId' required in type 'LearnMutationVariables'.
})
```

# Mixins and Decorators

This was my starting point for this plugin.
As soon as composable got released, i stopped thinking about this.
Was writing while learning typescript, on my first vue-typescript project.

Mixins should be usable in non-ts non-class based components.

```typescript
import { AllPostsSmartQuery, AllPostsQuery, AddCommentMutationMixin } from '@generated'
import { Component, Mixins } from 'vue-property-decorator'

@Component({ name: 'PostList' })
export default class PostList extends Mixins(AddCommentMutationMixin) {
  // if you need proper 'this' inside hooks (result, update, skip)
  // add 'this type' - PostList
  @AllPostsSmartQuery<PostList>({
    variables() {
      return { id: 'DEADBEEF' }
    },
  })
  public allPosts: AllPostsQuery['allPosts']

  public addComment() {
    this.addCommentMutation({
      variables: {
        content: 'Hello World',
      },
    })
  }
}
```

# Templates

Copied from this readme, using comments as markers.

```typescript
// vue-apollo-query:begin
export const $NAME$SmartQuery = <Component = any>(
  options?: Omit<VueApolloQueryDefinitionPatched<Component, $RESULT_TYPE$, $VARIABLES_TYPE$>, 'query'>
) =>
  SmartQuery<Component, $RESULT_TYPE$, $VARIABLES_TYPE$>({
    query: $DOCUMENT$,
    ...options,
  })
// vue-apollo-query:end

// query-utils:begin
export const useCache$NAME$$OPERATION_TYPE$ = (
  options: Omit<ProxyQuery<$RESULT_TYPE$, $VARIABLES_TYPE$>, 'query'>
) => proxyQuery<$RESULT_TYPE$, $VARIABLES_TYPE$>({ query: $DOCUMENT$, ...options })
// query-utils:end
```

```typescript
// vue-apollo-mutation-mixin:begin
@Component({ name: '$NAME$MutationMixin' })
export class $NAME$MutationMixin extends Vue {
  public $OPERATION_NAME$Mutation(
    options: Omit<VueApolloMutationOptions<$RESULT_TYPE$, $VARIABLES_TYPE$>, 'mutation'>
  ): Promise<FetchResult<$RESULT_TYPE$>> {
    return this.$apollo.mutate<$RESULT_TYPE$, $VARIABLES_TYPE$>({
      ...options,
      mutation: $DOCUMENT$,
    })
  }
}
// vue-apollo-mutation-mixin:end

// fragment-utils:begin
export const fragment$NAME$ = (
  options: Omit<FragmentResolver<$TYPE$>, 'type' | 'fragment' | 'fragmentName'>
) =>
  fragmentResolver<$TYPE$>({
    ...options,
    fragment: $DOCUMENT$,
    type: '$ON_TYPE$',
    fragmentName: '$NAME$',
  })
// fragment-utils:end
```

---

```typescript
// vue-apollo-query-mixin:begin

@Component({ name: '$NAME$QueryMixin' })
export class $NAME$QueryMixin extends Vue {
  public $OPERATION_NAME$Query(
    options: Omit<VueQueryOptions<$VARIABLES_TYPE$>, 'query'>
  ): Promise<FetchResult<$RESULT_TYPE$>> {
    return this.$apollo.query<$RESULT_TYPE$, $VARIABLES_TYPE$>({
      ...options,
      query: $DOCUMENT$,
    })
  }
}

// vue-apollo-query-mixin:end
```

---

## Vue-Apollo v4 composable

```typescript
// vue-apollo-query-composable:begin

export const use$NAME$$OPERATION_TYPE$ = (
  variables: $VARIABLES_TYPE$ | Ref<$VARIABLES_TYPE$> | ReactiveFunction<$VARIABLES_TYPE$> = {},
  options:
    | UseQueryOptions<$RESULT_TYPE$, $VARIABLES_TYPE$>
    | Ref<UseQueryOptions<$RESULT_TYPE$, $VARIABLES_TYPE$>>
    | ReactiveFunction<UseQueryOptions<$RESULT_TYPE$, $VARIABLES_TYPE$>> = {}
) => useQuery<$RESULT_TYPE$, $VARIABLES_TYPE$>($DOCUMENT$, variables, options)

// vue-apollo-query-composable:end
```

```typescript
// vue-apollo-query-composable-require-variables:begin

export const use$NAME$$OPERATION_TYPE$ = (
  variables: $VARIABLES_TYPE$ | Ref<$VARIABLES_TYPE$> | ReactiveFunction<$VARIABLES_TYPE$>,
  options:
    | UseQueryOptions<$RESULT_TYPE$, $VARIABLES_TYPE$>
    | Ref<UseQueryOptions<$RESULT_TYPE$, $VARIABLES_TYPE$>>
    | ReactiveFunction<UseQueryOptions<$RESULT_TYPE$, $VARIABLES_TYPE$>> = {}
) => useQuery<$RESULT_TYPE$, $VARIABLES_TYPE$>($DOCUMENT$, variables, options)

// vue-apollo-query-composable-require-variables:end
```

```typescript
// composable-mutation-partial-variables:begin
export const use$NAME$$OPERATION_TYPE$ = (
  options:
    | UseMutationOptions<$RESULT_TYPE$, Partial<$VARIABLES_TYPE$>>
    | ReactiveFunction<UseMutationOptions<$RESULT_TYPE$, Partial<$VARIABLES_TYPE$>>> = { variables: {} }
): UseMutationReturn<$RESULT_TYPE$, $VARIABLES_TYPE$> =>
  useMutation<$RESULT_TYPE$, Partial<$VARIABLES_TYPE$>>($DOCUMENT$, { variables: {}, ...options })
// composable-mutation-partial-variables:end

// vue-apollo-mutation-composable:begin
export const use$NAME$$OPERATION_TYPE$ = (
  options:
    | UseMutationOptions<$RESULT_TYPE$, $VARIABLES_TYPE$>
    | ReactiveFunction<UseMutationOptions<$RESULT_TYPE$, $VARIABLES_TYPE$>> = {}
) => useMutation<$RESULT_TYPE$, $VARIABLES_TYPE$>($DOCUMENT$, options)

// vue-apollo-mutation-composable:end
```

---

### Bunch of common definitions

```typescript
// vue-apollo-common-composable:begin
import { Ref, PropType } from '@vue/composition-api'

// Maybe is generated by graphql-codegen, and can be overridden in codegen.yml
export type MaybeProp<T> = PropType<Maybe<T>>

// prettier-ignore
import { useQuery, useMutation, UseQueryOptions, UseMutationReturn, UseMutationOptions } from '@vue/apollo-composable'
type ReactiveFunction<TParam> = () => TParam

// vue-apollo-common-composable:end
```

```typescript
// vue-apollo-common-class:begin
import Vue from 'vue'
import { MutationOptions, QueryOptions } from 'apollo-client'

import { VueApolloQueryDefinition, VueApolloSubscribeToMoreOptions } from 'vue-apollo/types/options'
import Component, { createDecorator, VueDecorator } from 'vue-class-component'

interface ClientOptions {
  client?: string
}
type VueApolloMutationOptions<R, V> = MutationOptions<R, V> & ClientOptions

function SmartQuery<C = any, R = any, V = any>(
  options: VueApolloQueryDefinitionPatched<C, R, V>
): VueDecorator {
  return createDecorator((componentOptions: any, k: string) => {
    componentOptions.apollo = componentOptions.apollo || {}
    componentOptions.apollo[k] = options
  })
}

type VueQueryOptions<V> = QueryOptions<V> & ClientOptions
type OverrideThis<F, T> = F extends (...args: infer A) => infer B ? (this: T, ...args: A) => B : F
type OverrideAllThis<O, T> = {
  [key in keyof O]: OverrideThis<O[key], T>
}
type VueApolloQueryDefinitionWithoutVariablesAndSubscribeToMore<R = any, V = any> = Omit<
  VueApolloQueryDefinition<R, V>,
  'subscribeToMore' | 'variables'
>
type SubscribeToMoreOptionsPatched<C, R, V> = OverrideAllThis<
  Omit<VueApolloSubscribeToMoreOptions<R, V>, 'updateQuery' | 'variables'>,
  C
> & {
  variables?: (this: C) => any
  updateQuery?: UpdateQueryFn<C, R, any, any>
}
type UpdateQueryFn<C = any, R = any, TSubscriptionVariables = any, TSubscriptionData = any> = (
  this: C,
  previousQueryResult: R,
  options: {
    subscriptionData: { data: TSubscriptionData }
    variables?: TSubscriptionVariables
  }
) => R

export interface VueApolloQueryDefinitionPatched<C = any, R = any, V = any>
  extends OverrideAllThis<VueApolloQueryDefinitionWithoutVariablesAndSubscribeToMore<R, V>, C> {
  variables?: (this: C) => V | V
  subscribeToMore?: SubscribeToMoreOptionsPatched<C, R, V> | Array<SubscribeToMoreOptionsPatched<C, R, V>>
}

// vue-apollo-common-class:end
```

```typescript
// vue-apollo-common:begin
import { DataProxy } from 'apollo-cache'
import { FetchResult } from 'apollo-link'
// vue-apollo-common:end

// query-utils-common:begin

interface ProxyQuery<R = any, V = any> {
  cache: DataProxy
  query: DocumentNode
  variables?: V
  reducer?: (source: R) => R
  patch?: Partial<R>
}
export const proxyQuery = <R, V>(o: ProxyQuery<R, V>): R | null => {
  const old = o.cache.readQuery<R, V>({
    query: o.query,
    variables: o.variables,
  })
  const result = o.patch && old ? { ...old, ...o.patch } : old
  const data = o.reducer && result ? o.reducer(result) : result
  o.cache.writeQuery({
    data,
    query: o.query,
    variables: o.variables,
  })
  return data
}
// query-utils-common:end

// fragment-utils-common:begin
interface FragmentResolver<T> {
  fragment: DocumentNode
  fragmentName?: string

  /** convenience alias for {source.__typename} */
  type?: string

  source: Partial<T>
  patch?: Partial<T>

  reducer?: (source: Partial<T> | T) => T
  context: {
    getCacheKey?: (result: any) => string | null
    cache: DataProxy
  }
  onError?: (error: Error) => void
}

export const fragmentResolver = <T extends { id?: string; _id?: string; __typename?: string }>({
  type,
  source,
  patch,
  fragment,
  fragmentName,
  reducer,
  context: { getCacheKey, cache },
  onError,
}: FragmentResolver<T>): Partial<T> | null => {
  const fragmentSource = { __typename: type, ...source } as Partial<T>
  const id = getCacheKey
    ? getCacheKey(fragmentSource)
    : `${fragmentSource.__typename}:${fragmentSource.id || fragmentSource._id}`
  if (id) {
    let oldData = null
    try {
      oldData = cache.readFragment<T>({ id, fragment, fragmentName })
    } catch (e) {
      if (onError) {
        onError(e)
      }
    }
    const patched =
      oldData && patch
        ? { ...oldData, ...patch }
        : patch
        ? { ...fragmentSource, ...patch }
        : oldData || fragmentSource
    const data = reducer ? reducer(patched) : patched
    if (data !== null) {
      cache.writeFragment<Partial<T>>({ fragment, fragmentName, data, id })
    }
    return data && type ? { __typename: type, ...data } : data
  }
  return null
}
// fragment-utils-common:end
```

### MAYBE: Mutation Decorator

```typescript
export function Mutate<C = any, R = any, V = any>(options: VueApolloMutationOptionsPatched<C, R, V>) {
  // tslint:disable-next-line
  return function(_target: Vue, _key: string, descriptor: any) {
    const original = descriptor.value
    descriptor.value = function apolloMutator(variables?: any) {
      const mutationOptions = !variables
        ? options
        : typeof options.variables === 'function'
        ? {
            ...options,
            variables: options.variables.apply(this, [variables]),
          }
        : { ...options, variables }

      return this.$apollo
        .mutate(mutationOptions)
        .then((result: FetchResult<R>) => original.apply(this, [variables, result]))
        .catch((networkError: Error) => original.apply(this, [variables, { networkError }]))
    }
  }
}

type VueApolloMutationOptionsWithoutVariables<R = any, V = any> = Omit<
  VueApolloMutationOptions<R, V>,
  'variables'
>

interface VueApolloMutationOptionsPatched<C = any, R = any, V = any>
  extends OverrideAllThis<VueApolloMutationOptionsWithoutVariables<R, V>, C> {
  variables?: (this: C, ...args: any) => V | V
}
```

---
_Source: https://npm.io/package/@therobot/codegen-vue-apollo · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
