dotansimha / graphql-code-generator

A tool for generating code based on a GraphQL schema and GraphQL operations (query/mutation/subscription), with flexible support for custom plugins.
https://the-guild.dev/graphql/codegen/
MIT License
10.79k stars 1.32k forks source link

Add plugin doesn’t respect order #3465

Closed lensbart closed 4 years ago

lensbart commented 4 years ago

Hello guys,

I encountered the following bug in the category “above my paygrade”: some lines added by @graphql-codegen/add appear in the wrong order. This happens on one machine (iMac) but not on another (MacBook Pro). Even more puzzlingly, we generate multiple files with a single codegen.yml config file, and the bug appears only in one file, but not the other:

codegen.yml:

# See https://graphql-code-generator.com
documents:
  - './app/**/*.graphql'
  - './website/**/*.graphql'
generates:
  # Generate typed `useMutation`, `useQuery` and `useSubscription` instances
  app/hooks/graphql.ts:
    plugins:
      - typescript
      - typescript-operations
      - typescript-react-apollo
    config:
      # avoid `avoidOptionals: true` — otherwise mutation input types require all fields
      # exportFragmentSpreadSubTypes: true
      immutableTypes: true
      nonOptionalTypename: true
      reactApolloVersion: 3
      scalars:
        Date: Date
        DateTime: Date
        Delta: Delta
        UUID: UUID
        Time: Date
      withComponent: false
      withHOC: false
      withHooks: true

  # By default, Apollo Client doesn't require any knowledge of the GraphQL schema.
  # However, some schema information is needed when using fragments on interfaces
  # or unions. Hence `fragmentTypes.json`.
  # See https://www.apollographql.com/docs/react/data/fragments/#fragments-on-unions-and-interfaces
  app/graphql/fragmentTypes.ts:
    plugins:
      - add:
          content:
            - '/**'
            - ' * @file autogenerated by [GraphQL code generator](https://graphql-code-generator.com).'
            - ' * via a prebuild script in `package.json`. Do not edit this file, as it gets'
            - ' * overwritten before every build.'
            - ' * @see `codegen.yml` in the project root.'
            - ' */'
      - fragment-matcher

  # Generate a `schema.graphql` which can subsequently be uploaded to Apollo
  # Engine, for real-time platform analytics.
  build/graphql-codegen/schema.graphql:
    plugins:
      - schema-ast

  # Generate TypeScript definitions for GraphQL types. These can be imported
  # anywhere in the project via the `Types/graphql` alias. Includes types for
  # GraphQL enums, object types, queries, mutations & subscriptions, and the
  # resolvers on both the Apollo server and client (local queries).
  shared/types/graphql.ts:
    plugins:
      - add:
          content:
            - '/**'
            - ' * @file autogenerated by [GraphQL code generator](https://graphql-code-generator.com).'
            - ' * via a prebuild script in `package.json`. Do not edit this file, as it gets'
            - ' * overwritten before every build.'
            - ' * @see `codegen.yml` in the project root.'
            - ' */'
            - "import { DataProxy } from 'apollo-cache'"
            - 'export type ClientContext = { cache: DataProxy }'
      - typescript
      - typescript-operations
      - typescript-resolvers
    config:
      # avoid `avoidOptionals: true` — otherwise mutation input types require all fields
      contextType: ClientContext
      exportFragmentSpreadSubTypes: true
      immutableTypes: true
      nonOptionalTypename: true
      scalars:
        Date: Date
        DateTime: Date
        Delta: Delta
        UUID: UUID
        Time: Date

hooks:
  afterAllFileWrite:
    - prettier --write
  afterOneFileWrite:
    - prettier --write
overwrite: true
schema:
  - ./app/schema/**/typeDefs.graphql
  - ./server/schema/private/**/*.graphql
# watch: true

In app/graphql/fragmentTypes.ts, the following appears, as expected, at the top of the file:

/**
 * @file autogenerated by [GraphQL code generator](https://graphql-code-generator.com).
 * via a prebuild script in `package.json`. Do not edit this file, as it gets
 * overwritten before every build.
 * @see `codegen.yml` in the project root.
 */

In shared/types/graphql.ts however, we are faced with the following:

 */
/**
 * via a prebuild script in `package.json`. Do not edit this file, as it gets
 * overwritten before every build.
 * @see `codegen.yml` in the project root.
 * @file autogenerated by [GraphQL code generator](https://graphql-code-generator.com).
import { DataProxy } from 'apollo-cache'
import { GraphQLResolveInfo, GraphQLScalarType, GraphQLScalarTypeConfig } from 'graphql';
export type ClientContext = { cache: DataProxy }

Note that the second-to-last line in the snippet above was not added by the add plugin.

If I remove the two following lines, (so that we end up with the same settings for the add plugin for both files), all works well:

 - "import { DataProxy } from 'apollo-cache'"
 - 'export type ClientContext = { cache: DataProxy }'

Both computers run macOS and all project dependencies are up-to-date.

Any help would be greatly appreciated!

Thanks

lensbart commented 4 years ago

Also, I tried both of the following in codegen.yml:

    plugins:
      - add:
          content:
            - '/**'
            - ' * @file autogenerated by [GraphQL code generator](https://graphql-code-generator.com).'
            - ' * via a prebuild script in `package.json`. Do not edit this file, as it gets'
            - ' * overwritten before every build.'
            - ' * @see `codegen.yml` in the project root.'
            - ' */'
            - "import { DataProxy } from 'apollo-cache'"
            - 'export type ClientContext = { cache: DataProxy }'
      - typescript
      - typescript-operations
      - typescript-resolvers
    plugins:
      - add: '/**'
      - add: ' * @file autogenerated by [GraphQL code generator](https://graphql-code-generator.com).'
      - add: ' * via a prebuild script in `package.json`. Do not edit this file, as it gets'
      - add: ' * overwritten before every build.'
      - add: ' * @see `codegen.yml` in the project root.'
      - add: ' */'
      - add: "import { DataProxy } from 'apollo-cache'"
      - add: 'export type ClientContext = { cache: DataProxy }'
      - typescript
      - typescript-operations
      - typescript-resolvers
lensbart commented 4 years ago

Probably due to export being reordered incorrectly if the file itself contains import statements. This does work:

      - add:
          content:
            - '/**'
            - ' * @file autogenerated by [GraphQL code generator](https://graphql-code-generator.com).'
            - ' * via a prebuild script in `package.json`. Do not edit this file, as it gets'
            - ' * overwritten before every build.'
            - ' * @see `codegen.yml` in the project root.'
            - ' */'
            - "import { DataProxy } from 'apollo-cache'"
          placement: 'prepend'
      - add:
          content: 'export type ClientContext = { cache: DataProxy }'
          placement: 'append'
dotansimha commented 4 years ago

@lensbart Codegen is using plugins in the order specified, but also manages a different ordering based on plugins execution. So plugins can choose to add content or append or prepend - this is done mostly because we would like to allow plugins some way to customize the output, and manage imports.

So the prepend section of each plugin is being added by the order of plugins, but, it will always add prepend first. So content section of add plugin will always go after prepend section if other plugins are adding it. You can workaround that by using add plugin as first plugin, and specify the strings you wish to add in content section of, and also add placement configuration and tell the codegen to put it as part of the prepended strings:

generates:
  server-types.ts:
    - add:
        placement: prepend
        content: "// test"
    - typescript
lensbart commented 4 years ago

@dotansimha Yes, I think I fully understand the mechanism. The issue is that the order is not respected for lines specified under add (as explained above). So this is a bug, or at least it should be documented that import and export must not be used together without append/prepend.