diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000000..dce47990860 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,72 @@ +version: 2 + +updates: + # Tier 1: published packages + root tooling. Weekly, one grouped PR per package. + - package-ecosystem: npm + directories: + - '/' + - '/packages/graphql-codegen-cli' + - '/packages/graphql-codegen-core' + - '/packages/plugins/*/*' + - '/packages/presets/*' + - '/packages/utils/*' + schedule: + interval: weekly + day: monday + cooldown: + default-days: 1 # mirrors pnpm-workspace.yaml minimumReleaseAge (1 day) + open-pull-requests-limit: 15 + labels: [dependencies] + groups: + minor-patch: + update-types: [minor, patch] + exclude-patterns: # these still get their own PRs + - '@changesets/*' + - 'typescript' + - '@theguild/*' + - '@whatwg-node/*' + - 'graphql-jit' + - 'husky' + - '@types/lz-string' + - 'swc*' + - '@swc/*' + security: + applies-to: security-updates + patterns: ['*'] + ignore: + # Pinned via pnpm-workspace.yaml `overrides` and must move together with them + - dependency-name: 'graphql' + - dependency-name: '@typescript-eslint/*' + - dependency-name: '@rushstack/eslint-patch' + - dependency-name: 'jiti' + # Version is fixed by the bob-the-bundler@7.0.1 patch + - dependency-name: 'bob-the-bundler' + + # Tier 2: non-published code. Monthly, one PR per directory with everything in it. + - package-ecosystem: npm + directories: + - '/website' + - '/dev-test/*' + - '/examples/*' + - '/examples/*/*' + schedule: + interval: monthly + cooldown: + default-days: 1 + open-pull-requests-limit: 10 + labels: [dependencies] + groups: + all: + patterns: ['*'] + ignore: + - dependency-name: 'graphql' + - dependency-name: '@graphql-codegen/*' # workspace packages + + # CI actions (pinned by SHA; Dependabot keeps the SHA and the "# vX" comment in sync) + - package-ecosystem: github-actions + directory: '/' + schedule: + interval: monthly + groups: + actions: + patterns: ['*'] diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 5d9d2fb66ef..2a511dc6022 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -20,7 +20,7 @@ env: jobs: lint: name: Linting Check - uses: the-guild-org/shared-config/.github/workflows/lint.yml@f4eea983237a44bb0ca19c3348dacbfdfcdbec23 # main + uses: the-guild-org/shared-config/.github/workflows/lint.yml@1c4632ccc198d4c9b34d9e6b3d556c6f245b4e69 # main with: script: pnpm lint:ci packageManager: pnpm @@ -32,14 +32,14 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v6 - name: Install pnpm - uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093 # v6.0.8 + uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0 with: standalone: true cache: true - name: Install Node - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 + uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: 24 - name: Install packages @@ -60,14 +60,14 @@ jobs: - 'esm' steps: - name: Checkout - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v6 - name: Install pnpm - uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093 # v6.0.8 + uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0 with: standalone: true cache: true - name: Install Node - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 + uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: 24 - name: Install packages @@ -93,14 +93,14 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v6 - name: Install pnpm - uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093 # v6.0.8 + uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0 with: standalone: true cache: true - name: Install Node - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 + uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: 24 - name: Install packages @@ -162,14 +162,14 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v6 - name: Install pnpm - uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093 # v6.0.8 + uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0 with: standalone: true cache: true - name: Install Node - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 + uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: 24 - name: Install packages @@ -202,14 +202,14 @@ jobs: graphql_version: 16 steps: - name: Checkout - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v6 - name: Install pnpm - uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093 # v6.0.8 + uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0 with: standalone: true cache: true - name: Install Node - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 + uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: ${{matrix.node_version}} - name: Install Dependencies to match graphql version @@ -247,14 +247,14 @@ jobs: graphql_version: 16 steps: - name: Checkout - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v6 - name: Install pnpm - uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093 # v6.0.8 + uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0 with: standalone: true cache: true - name: Install Node - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 + uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: ${{matrix.node_version}} - name: Install Dependencies to match graphql version diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 5eedac21249..af4e78aae8c 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -6,7 +6,7 @@ on: jobs: dependencies: - uses: the-guild-org/shared-config/.github/workflows/changesets-dependencies.yaml@f4eea983237a44bb0ca19c3348dacbfdfcdbec23 # main + uses: the-guild-org/shared-config/.github/workflows/changesets-dependencies.yaml@1c4632ccc198d4c9b34d9e6b3d556c6f245b4e69 # main if: ${{ github.event.pull_request.title != 'Upcoming Release Changes' }} permissions: contents: write # allows modifying changeset files diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9c9eea99207..1763c06e6e4 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -31,7 +31,7 @@ jobs: release-stable: if: github.event_name == 'push' - uses: the-guild-org/shared-config/.github/workflows/release-stable.yml@f4eea983237a44bb0ca19c3348dacbfdfcdbec23 # main + uses: the-guild-org/shared-config/.github/workflows/release-stable.yml@1c4632ccc198d4c9b34d9e6b3d556c6f245b4e69 # main permissions: id-token: write # allows OIDC publishing pull-requests: write # allows creating Version Packages PR diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index cfd5537f359..2b1db23b768 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -10,9 +10,9 @@ jobs: publish-rust-swc-plugin: runs-on: ubuntu-latest steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v6 # Setup .npmrc file to publish to npm - - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6 + - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v6 with: node-version: '24.x' registry-url: 'https://registry.npmjs.org' diff --git a/packages/plugins/other/visitor-plugin-common/CHANGELOG.md b/packages/plugins/other/visitor-plugin-common/CHANGELOG.md index 65963f36fb6..d3be702d22c 100644 --- a/packages/plugins/other/visitor-plugin-common/CHANGELOG.md +++ b/packages/plugins/other/visitor-plugin-common/CHANGELOG.md @@ -1,5 +1,20 @@ # @graphql-codegen/visitor-plugin-common +## 7.2.7 + +### Patch Changes + +- [#11001](https://github.com/dotansimha/graphql-code-generator/pull/11001) + [`65e5599`](https://github.com/dotansimha/graphql-code-generator/commit/65e55991740a23f05886fa07770f8bc7be4bf8a5) + Thanks [@eddeee888](https://github.com/eddeee888)! - Fix `typescript-operations` generating + invalid `interface` declarations for `@oneOf` inputs when `declarationKind` is `interface` + ([#10996](https://github.com/dotansimha/graphql-code-generator/issues/10996)). + + A `@oneOf` input with multiple fields is a union, so it is always generated as a `type` alias; a + single-field `@oneOf` input keeps the configured `declarationKind.input`. `typescript` and + `typescript-operations` share this rule through the new `getOneOfInputDeclarationKind` export from + `@graphql-codegen/visitor-plugin-common`. + ## 7.2.6 ### Patch Changes diff --git a/packages/plugins/other/visitor-plugin-common/package.json b/packages/plugins/other/visitor-plugin-common/package.json index a2961193eb7..5d961c4bd31 100644 --- a/packages/plugins/other/visitor-plugin-common/package.json +++ b/packages/plugins/other/visitor-plugin-common/package.json @@ -1,6 +1,6 @@ { "name": "@graphql-codegen/visitor-plugin-common", - "version": "7.2.6", + "version": "7.2.7", "type": "module", "repository": { "type": "git", diff --git a/packages/plugins/other/visitor-plugin-common/src/base-types-visitor.ts b/packages/plugins/other/visitor-plugin-common/src/base-types-visitor.ts index 69b7319ccec..6cc8e7204f2 100644 --- a/packages/plugins/other/visitor-plugin-common/src/base-types-visitor.ts +++ b/packages/plugins/other/visitor-plugin-common/src/base-types-visitor.ts @@ -38,6 +38,7 @@ import { DeclarationBlockConfig, getConfigValue, getNodeComment, + getOneOfInputDeclarationKind, indent, isOneOfInputObjectType, transformComment, @@ -693,13 +694,14 @@ export class BaseTypesVisitor< } getInputObjectOneOfDeclarationBlock(node: InputObjectTypeDefinitionNode): DeclarationBlock { - // As multiple fields always result in a union, we have - // to force a declaration kind of `type` in this case - const declarationKind = - node.fields.length === 1 ? this._parsedConfig.declarationKind.input : 'type'; return new DeclarationBlock(this._declarationBlockConfig) .export() - .asKind(declarationKind) + .asKind( + getOneOfInputDeclarationKind({ + fieldCount: node.fields.length, + inputDeclarationKind: this._parsedConfig.declarationKind.input, + }), + ) .withName(this.convertName(node)) .withComment(node.description?.value) .withContent(`\n` + node.fields.join('\n |')); diff --git a/packages/plugins/other/visitor-plugin-common/src/utils.ts b/packages/plugins/other/visitor-plugin-common/src/utils.ts index e08a4b15ed1..1714d32c6e2 100644 --- a/packages/plugins/other/visitor-plugin-common/src/utils.ts +++ b/packages/plugins/other/visitor-plugin-common/src/utils.ts @@ -31,7 +31,12 @@ import type { RawConfig } from './base-visitor.js'; import { parseMapper } from './mappers.js'; import { DEFAULT_SCALARS } from './scalars.js'; import type { EnrichedFieldNode } from './selection-set-to-object.js'; -import type { NormalizedScalarsMap, ParsedScalarsMap, ScalarsMap } from './types.js'; +import type { + DeclarationKind, + NormalizedScalarsMap, + ParsedScalarsMap, + ScalarsMap, +} from './types.js'; export const getConfigValue = (value: T | null | undefined, defaultValue: T): T => { if (value === null || value === undefined) { @@ -603,6 +608,20 @@ function stripTrailingSpaces(str: string): string { return str.replace(/ +\n/g, '\n'); } +/** + * A @oneOf input with multiple fields is emitted as a union, which only a `type` alias can declare, + * so the configured declaration kind applies only when there is a single field. + */ +export function getOneOfInputDeclarationKind({ + fieldCount, + inputDeclarationKind, +}: { + fieldCount: number; + inputDeclarationKind: DeclarationKind; +}): DeclarationKind { + return fieldCount === 1 ? inputDeclarationKind : 'type'; +} + const isOneOfTypeCache = new WeakMap(); export function isOneOfInputObjectType( namedType: GraphQLNamedType | null | undefined, diff --git a/packages/plugins/typescript/operations/CHANGELOG.md b/packages/plugins/typescript/operations/CHANGELOG.md index 09988e6de6b..c96104c32b7 100644 --- a/packages/plugins/typescript/operations/CHANGELOG.md +++ b/packages/plugins/typescript/operations/CHANGELOG.md @@ -1,5 +1,24 @@ # @graphql-codegen/typescript-operations +## 6.1.9 + +### Patch Changes + +- [#11001](https://github.com/dotansimha/graphql-code-generator/pull/11001) + [`65e5599`](https://github.com/dotansimha/graphql-code-generator/commit/65e55991740a23f05886fa07770f8bc7be4bf8a5) + Thanks [@eddeee888](https://github.com/eddeee888)! - Fix `typescript-operations` generating + invalid `interface` declarations for `@oneOf` inputs when `declarationKind` is `interface` + ([#10996](https://github.com/dotansimha/graphql-code-generator/issues/10996)). + + A `@oneOf` input with multiple fields is a union, so it is always generated as a `type` alias; a + single-field `@oneOf` input keeps the configured `declarationKind.input`. `typescript` and + `typescript-operations` share this rule through the new `getOneOfInputDeclarationKind` export from + `@graphql-codegen/visitor-plugin-common`. + +- Updated dependencies + [[`65e5599`](https://github.com/dotansimha/graphql-code-generator/commit/65e55991740a23f05886fa07770f8bc7be4bf8a5)]: + - @graphql-codegen/visitor-plugin-common@7.2.7 + ## 6.1.8 ### Patch Changes diff --git a/packages/plugins/typescript/operations/package.json b/packages/plugins/typescript/operations/package.json index 3334698e1be..8aa09e2d877 100644 --- a/packages/plugins/typescript/operations/package.json +++ b/packages/plugins/typescript/operations/package.json @@ -1,6 +1,6 @@ { "name": "@graphql-codegen/typescript-operations", - "version": "6.1.8", + "version": "6.1.9", "type": "module", "description": "GraphQL Code Generator plugin for generating TypeScript types for GraphQL queries, mutations, subscriptions and fragments", "repository": { diff --git a/packages/plugins/typescript/operations/src/visitor.ts b/packages/plugins/typescript/operations/src/visitor.ts index 487ff150ae9..9659776628a 100644 --- a/packages/plugins/typescript/operations/src/visitor.ts +++ b/packages/plugins/typescript/operations/src/visitor.ts @@ -33,6 +33,7 @@ import { getConfigValue, getEnumsImports, getNodeComment, + getOneOfInputDeclarationKind, indent, isNativeNamedType, isOneOfInputObjectType, @@ -313,7 +314,12 @@ export class TypeScriptDocumentsVisitor extends BaseDocumentsVisitor< if (isOneOfInputObjectType(this._schema.getType(inputTypeName))) { return new DeclarationBlock(this._declarationBlockConfig) .export() - .asKind(this.config.declarationKind.input) + .asKind( + getOneOfInputDeclarationKind({ + fieldCount: (node.fields || []).length, + inputDeclarationKind: this.config.declarationKind.input, + }), + ) .withName(this.convertName(node)) .withComment(node.description?.value) .withContent(`\n` + (node.fields || []).join('\n |')).string; diff --git a/packages/plugins/typescript/operations/tests/ts-documents.standalone.input.spec.ts b/packages/plugins/typescript/operations/tests/ts-documents.standalone.input.spec.ts index b4ef422bd22..c5230a0b612 100644 --- a/packages/plugins/typescript/operations/tests/ts-documents.standalone.input.spec.ts +++ b/packages/plugins/typescript/operations/tests/ts-documents.standalone.input.spec.ts @@ -374,6 +374,50 @@ describe('TypeScript Operations Plugin - Input', () => { validateTs(result, undefined, undefined, undefined, undefined, true); }); + it('generates @oneOf input as a type alias when declarationKind is interface', async () => { + const schema = buildSchema(/* GraphQL */ ` + directive @oneOf on INPUT_OBJECT + + type Query { + foo(input: FooInput!): Boolean + } + + input FooInput @oneOf { + a: String + b: Int + } + `); + const document = parse(/* GraphQL */ ` + query Foo($input: FooInput!) { + foo(input: $input) + } + `); + + const result = mergeOutputs([ + await plugin(schema, [{ document }], { declarationKind: 'interface' }, { outputFile: '' }), + ]); + + expect(result).toMatchInlineSnapshot(` + "/** Internal type. DO NOT USE DIRECTLY. */ + type Exact = { [K in keyof T]: T[K] }; + /** Internal type. DO NOT USE DIRECTLY. */ + export type Incremental = T | { [P in keyof T]?: P extends ' $fragmentName' | '__typename' ? T[P] : never }; + export type FooInput = + { a: string; b?: never; } + | { a?: never; b: number; }; + + export type FooQueryVariables = Exact<{ + input: FooInput; + }>; + + + export interface FooQuery { foo: boolean | null } + " + `); + + validateTs(result, undefined, undefined, undefined, undefined, true); + }); + it('generates with custom inputMaybeValue', async () => { const schema = buildSchema(/* GraphQL */ ` type Query { diff --git a/renovate.json b/renovate.json index 87d533b432e..5492c9e42ff 100644 --- a/renovate.json +++ b/renovate.json @@ -1,4 +1,5 @@ { + "enabled": false, "extends": ["github>the-guild-org/shared-config:renovate"], "automerge": true, "major": {