diff --git a/.changeset/strict-typescript-operations.md b/.changeset/strict-typescript-operations.md new file mode 100644 index 00000000000..1b78b833b67 --- /dev/null +++ b/.changeset/strict-typescript-operations.md @@ -0,0 +1,13 @@ +--- +'@graphql-codegen/typescript-operations': patch +'@graphql-codegen/visitor-plugin-common': patch +'@graphql-codegen/plugin-helpers': patch +--- + +Fix type errors in `typescript-operations` under `strict: true` by widening the shared types it calls into: + +- `@graphql-codegen/plugin-helpers`: `Types.ComplexPluginOutput`'s `prepend` and `append` now accept `null` items. Core already skipped them. +- `@graphql-codegen/visitor-plugin-common`: `DeclarationBlock.withComment` accepts `undefined`, `parseEnumValues`'s `mapOrStr` is optional (it already defaulted to `{}`), `ImportSource.namespace` accepts `null`, and `optimizeOperations`'s `includeFragments` is optional. +- `@graphql-codegen/typescript-operations`: skips document files without a `document`, and no longer throws when called without the plugin info argument. + +Generated output is unchanged. diff --git a/.github/dependabot.yml b/.github/dependabot.yml index dce47990860..76d7a951a58 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -10,6 +10,9 @@ updates: - '/packages/plugins/*/*' - '/packages/presets/*' - '/packages/utils/*' + # Keep ranges like `^1.7.3` when the new version already satisfies them: only the lockfile moves. + # Raising the floor would force a needless release of the published package. + versioning-strategy: increase-if-necessary schedule: interval: weekly day: monday diff --git a/.github/workflows/dependabot-lockfile.yml b/.github/workflows/dependabot-lockfile.yml new file mode 100644 index 00000000000..3a9d18655c2 --- /dev/null +++ b/.github/workflows/dependabot-lockfile.yml @@ -0,0 +1,45 @@ +name: dependabot-lockfile +on: + pull_request: + branches: + - master + paths: + - '**/package.json' + +permissions: {} + +jobs: + # Dependabot jobs that run from a workspace member directory (e.g. /dev-test/*, /examples/*) + # update that package.json but not the root pnpm-lock.yaml. Regenerate it and push it back. + update-lockfile: + name: Update pnpm-lock.yaml + if: ${{ github.event.pull_request.user.login == 'dependabot[bot]' }} + runs-on: ubuntu-latest + permissions: + contents: write # allows pushing the lockfile to the Dependabot branch + steps: + - name: Checkout + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + ref: ${{ github.head_ref }} # the Dependabot branch, not the PR merge commit + - name: Install pnpm and Node + uses: pnpm/setup@fbda4c85fc2e1e08721cd8763afea8f48d60f024 # v3.0.0 + with: + install: false # a frozen install would fail on the stale lockfile + - name: Update lockfile + run: pnpm install --lockfile-only --no-frozen-lockfile --ignore-scripts + - name: Commit and push + env: + HEAD_REF: ${{ github.head_ref }} + run: | + if git diff --quiet -- pnpm-lock.yaml; then + echo "pnpm-lock.yaml is up to date" + exit 0 + fi + git config user.name 'github-actions[bot]' + git config user.email '41898282+github-actions[bot]@users.noreply.github.com' + git add pnpm-lock.yaml + git commit -m 'chore(deps): update pnpm-lock.yaml' + # Other workflows (e.g. changesets-dependencies) may push to the same branch + git pull --rebase origin "$HEAD_REF" + git push origin "HEAD:$HEAD_REF" diff --git a/packages/plugins/other/fragment-matcher/src/index.ts b/packages/plugins/other/fragment-matcher/src/index.ts index f60834e3c3d..e54d466f360 100644 --- a/packages/plugins/other/fragment-matcher/src/index.ts +++ b/packages/plugins/other/fragment-matcher/src/index.ts @@ -1,5 +1,5 @@ import { extname } from 'path'; -import { execute, GraphQLSchema, parse } from 'graphql'; +import { execute, GraphQLSchema, parse, type ExecutionResult } from 'graphql'; import { PluginFunction, PluginValidateFn, @@ -131,7 +131,7 @@ export const plugin: PluginFunction = async ( schema: GraphQLSchema, _documents, pluginConfig: FragmentMatcherConfig, - info, + info = {}, ): Promise => { const config: Required = { module: 'es2015', @@ -161,8 +161,8 @@ export const plugin: PluginFunction = async ( } } `), - })) as any; - const ext = extname(info.outputFile).toLowerCase(); + })) as unknown as ExecutionResult; + const ext = extname(info.outputFile || '').toLowerCase(); if (!introspection.data) { throw new Error(`Plugin "fragment-matcher" couldn't introspect the schema`); @@ -179,15 +179,6 @@ export const plugin: PluginFunction = async ( .filter(type => type.kind === 'UNION' || type.kind === 'INTERFACE') .sort((a, b) => sortStringsLexicographically(a.name, b.name)); - const createPossibleTypesCollection = (acc, type) => { - return { - ...acc, - [type.name]: type.possibleTypes - .map(possibleType => possibleType.name) - .sort(sortStringsLexicographically), - }; - }; - const filteredData: IntrospectionResultData | PossibleTypesResultData = apolloClientVersion === 2 ? { @@ -197,7 +188,16 @@ export const plugin: PluginFunction = async ( }, } : { - possibleTypes: unionAndInterfaceTypes.reduce(createPossibleTypesCollection, {}), + possibleTypes: unionAndInterfaceTypes.reduce((acc, type) => { + return { + ...acc, + // Unions and interfaces always have possibleTypes + // This `|| []` is only to satisfy type safety + [type.name]: (type.possibleTypes || []) + .map(possibleType => possibleType.name) + .sort(sortStringsLexicographically), + }; + }, {}), }; const content = JSON.stringify(filteredData, null, 2); @@ -216,14 +216,14 @@ export const plugin: PluginFunction = async ( } if (extensions.ts.includes(ext)) { - let typename: string; + let typename: string | undefined; if (apolloClientVersion === 2) { typename = `IntrospectionResultData`; } else if (apolloClientVersion === 3) { typename = `PossibleTypesResultData`; } - let type: string; + let type: string | undefined; if (useExplicitTyping) { type = `export type ${typename} = ${content};`; } else if (apolloClientVersion === 2) { diff --git a/packages/plugins/other/visitor-plugin-common/src/enum-values.ts b/packages/plugins/other/visitor-plugin-common/src/enum-values.ts index 6d87a956789..9d1bcb91329 100644 --- a/packages/plugins/other/visitor-plugin-common/src/enum-values.ts +++ b/packages/plugins/other/visitor-plugin-common/src/enum-values.ts @@ -14,7 +14,7 @@ export function parseEnumValues({ naming, }: { schema: GraphQLSchema; - mapOrStr: EnumValuesMap; + mapOrStr?: EnumValuesMap; ignoreEnumValuesFromSchema?: boolean; naming: { convert: ConvertFn; diff --git a/packages/plugins/other/visitor-plugin-common/src/imports.ts b/packages/plugins/other/visitor-plugin-common/src/imports.ts index 2d506d6f423..260c4c35364 100644 --- a/packages/plugins/other/visitor-plugin-common/src/imports.ts +++ b/packages/plugins/other/visitor-plugin-common/src/imports.ts @@ -21,7 +21,7 @@ export type ImportSource = { /** * Namespace to import source as */ - namespace?: string; + namespace?: string | null; /** * Entity names to import */ diff --git a/packages/plugins/other/visitor-plugin-common/src/optimize-operations.ts b/packages/plugins/other/visitor-plugin-common/src/optimize-operations.ts index e4c6f8b0066..1d357490e78 100644 --- a/packages/plugins/other/visitor-plugin-common/src/optimize-operations.ts +++ b/packages/plugins/other/visitor-plugin-common/src/optimize-operations.ts @@ -5,7 +5,7 @@ import { optimizeDocuments } from '@graphql-tools/relay-operation-optimizer'; export function optimizeOperations( schema: GraphQLSchema, documents: Types.DocumentFile[], - options?: { includeFragments: boolean }, + options?: { includeFragments?: boolean }, ): Types.DocumentFile[] { const newDocuments = optimizeDocuments( schema, diff --git a/packages/plugins/other/visitor-plugin-common/src/utils.ts b/packages/plugins/other/visitor-plugin-common/src/utils.ts index 1714d32c6e2..32137bc6604 100644 --- a/packages/plugins/other/visitor-plugin-common/src/utils.ts +++ b/packages/plugins/other/visitor-plugin-common/src/utils.ts @@ -136,7 +136,7 @@ export class DeclarationBlock { _content = null; _block = null; _nameGenerics = null; - _comment = null; + _comment: string | null = null; _ignoreBlockWrapper = false; constructor(private _config: DeclarationBlockConfig) { @@ -168,10 +168,13 @@ export class DeclarationBlock { return this; } - withComment(comment: string | StringValueNode | null, disabled = false): DeclarationBlock { + withComment( + comment: string | StringValueNode | null | undefined, + disabled = false, + ): DeclarationBlock { const nonEmptyComment = !!(isStringValueNode(comment) ? comment.value : comment); - if (nonEmptyComment && !disabled) { + if (comment && nonEmptyComment && !disabled) { this._comment = transformComment(comment, 0); } diff --git a/packages/plugins/typescript/operations/src/index.ts b/packages/plugins/typescript/operations/src/index.ts index ca62af89af9..104af573661 100644 --- a/packages/plugins/typescript/operations/src/index.ts +++ b/packages/plugins/typescript/operations/src/index.ts @@ -19,7 +19,7 @@ import { TypeScriptDocumentsVisitor } from './visitor.js'; export const plugin: PluginFunction< TypeScriptDocumentsPluginConfig, Types.ComplexPluginOutput -> = async (inputSchema, rawDocuments, config, { outputFile }) => { +> = async (inputSchema, rawDocuments, config, { outputFile = '' } = {}) => { const schema = config.nullability?.errorHandlingClient ? await semanticToStrict(inputSchema) : inputSchema; @@ -41,6 +41,10 @@ export const plugin: PluginFunction< }; }>( (prev, document) => { + if (!document.document) { + return prev; + } + prev.all.documentFiles.push(document); prev.all.documentNodes.push(document.document); @@ -83,7 +87,7 @@ export const plugin: PluginFunction< const operationsDefinitions: string[] = operationsResult.definitions as string[]; if (config.addOperationExport) { for (const d of allDocumentsAST.definitions) { - if ('name' in d) { + if ('name' in d && d.name) { operationsDefinitions.push( `export declare const ${d.name.value}: import("graphql").DocumentNode;`, ); diff --git a/packages/utils/plugins-helpers/src/types.ts b/packages/utils/plugins-helpers/src/types.ts index 101638e8eff..99669d0bdb3 100644 --- a/packages/utils/plugins-helpers/src/types.ts +++ b/packages/utils/plugins-helpers/src/types.ts @@ -618,8 +618,8 @@ export namespace Types { export type ComplexPluginOutput> = { content: string; - prepend?: string[]; - append?: string[]; + prepend?: Array; + append?: Array; meta?: M; }; export type PluginOutput = string | ComplexPluginOutput;