Skip to content

[cli] fix: satisfy strict mode in cli (#4683) - #11020

Merged
eddeee888 merged 1 commit into
masterfrom
claude/fervent-allen-xihu9n
Oct 3, 2026
Merged

eddeee888 merged 1 commit into
masterfrom
claude/fervent-allen-xihu9n

Conversation

@eddeee888

Copy link
Copy Markdown
Collaborator

Description

Part of the strict: true tracker #10985. This fixes the 32 errors graphql-codegen-cli hits under strict mode, corrects the cli's exported types that didn't match runtime, and widens two plugin-helpers helpers so cli call sites need no fallbacks.

Missing type packages

  • Adds @types/yargs, @types/babel__generator and @types/babel__template as cli devDeps (TS7016 under noImplicitAny). The two Babel ones are already in the lockfile at these versions through other packages.

Shared helpers widened

  • normalizeInstanceOrArray and normalizeConfig in plugin-helpers accept null/undefined and return []. normalizeInstanceOrArray's body already did this. normalizeConfig now checks config && before Object.keys, which threw on null before (TS2345 at 9 call sites in codegen.ts).
  • Because of this, patterns.ts drops its ! assertions, a ?? [] fallback and the explicit generic arguments on these calls.

Exported types corrected

  • loadCodegenConfig returns Promise<LoadCodegenConfigResult | null>. It already resolved to null when no config file is found, and loadContext already handled that (TS2322). The now-redundant result.config as Types.Config cast is removed.
  • CodegenContext.filepath is optional. A context created programmatically, like generate() without a config file, has none. Watch mode used to call relative(process.cwd(), undefined) on it and threw ERR_INVALID_ARG_TYPE. It now only watches the config file when there is one.
  • YamlCliFlags includes the kebab-case ignore-no-documents, emit-legacy-common-js-imports and import-extension keys that updateContextWithCliFlags reads. yargs sets them (TS2551).
  • CodegenContext.getConfig() has overloads: no argument returns Types.Config, and getConfig(extraConfig) returns T & Types.Config (TS2322).
  • CodegenContext.checkModeStaleFiles is string[] instead of an inferred never[] (TS2345 in generate-and-save.ts).

Local fixes

  • codegen.ts: the preset ternary tests outputConfig.preset directly, so TypeScript narrows it. The unused hasPreset is removed.
  • config.ts: the private _config/config fields are optional. A context without either config or a GraphQL Config gets a commented generates: {} default, so it still fails with codegen's missing "generates" error. getCustomConfigPath returns undefined instead of null.
  • graphql-config.ts: isGraphQLConfig accepts undefined. It already checked for it.
  • init/questions.ts: plugins starts as [], so client-preset answers get plugins: [] instead of undefined. Both consumers treat these the same.
  • utils/debugging.ts: meta is required, since debugLog always sets it.
  • utils/file-system.ts: unlinkFile's callback is required and typed as fs.unlink's (err: Error | null) => void. Without one, fs.unlink throws. The only caller always passes one.

No new as casts or ! assertions. Generated output is unchanged. The strict typecheck goes from 205 to 173 errors: 0 in graphql-codegen-cli, and no other package's count changes.

This is the middle of three options discussed. The others were "local fixes only", which left loadCodegenConfig and filepath mistyped, and "also derive YamlCliFlags from the yargs options and split CodegenContext construction", which could be breaking. We picked this one so the exported types match runtime without a breaking change.

pnpm-lock.yaml: besides the new @types packages, pnpm dedupe (the repo's pre-commit step) collapses duplicate postcss and tinyglobby copies onto 8.5.28 and 0.2.17. Both versions were already in the lockfile.

Related #4683

Type of change

  • Bug fix (non-breaking change which fixes an issue)

How Has This Been Tested?

  • Ran tsc --noEmit -p tsconfig.json with the tracker's strict tsconfig.json: 0 errors under packages/graphql-codegen-cli, and no other package's count changes.
  • pnpm types:check (the current non-strict config) is clean.
  • vitest for cli and plugin-helpers: 114 pass. The same 72 cli tests about plugin/preset resolution fail in this environment on a clean master checkout too.
  • eslint, prettier and pnpm dedupe --check were run by hand on the changed files and are clean. The commit skipped the pre-commit hook, because in this container its pnpm dedupe recreates node_modules while eslint and prettier are running alongside it.

🤖 Generated with Claude Code

https://claude.ai/code/session_01JcaAHEYKgZnuJ894vm8nRe


Generated by Claude Code

@changeset-bot

changeset-bot Bot commented Oct 1, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: ab81a7a

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 2 packages
Name Type
@graphql-codegen/cli Patch
@graphql-codegen/plugin-helpers Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-actions

github-actions Bot commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

🚀 Snapshot Release (alpha)

The latest changes of this PR are available as alpha on npm (based on the declared changesets):

Package Version Info
@graphql-codegen/cli 7.4.4-alpha-20261003115436-ab81a7a7be052e891d8b91b917f9289469c34d81 npm ↗︎ unpkg ↗︎
@graphql-codegen/time 7.1.1-alpha-20261003115436-ab81a7a7be052e891d8b91b917f9289469c34d81 npm ↗︎ unpkg ↗︎
@graphql-codegen/visitor-plugin-common 7.2.8-alpha-20261003115436-ab81a7a7be052e891d8b91b917f9289469c34d81 npm ↗︎ unpkg ↗︎
@graphql-codegen/typescript-operations 6.1.10-alpha-20261003115436-ab81a7a7be052e891d8b91b917f9289469c34d81 npm ↗︎ unpkg ↗︎
@graphql-codegen/typescript 6.1.1-alpha-20261003115436-ab81a7a7be052e891d8b91b917f9289469c34d81 npm ↗︎ unpkg ↗︎
@graphql-codegen/client-preset 6.2.1-alpha-20261003115436-ab81a7a7be052e891d8b91b917f9289469c34d81 npm ↗︎ unpkg ↗︎
@graphql-codegen/plugin-helpers 7.4.1-alpha-20261003115436-ab81a7a7be052e891d8b91b917f9289469c34d81 npm ↗︎ unpkg ↗︎

"@types/js-yaml": "4.0.9",
"@types/micromatch": "^4.0.2",
"@types/shell-quote": "1.7.5",
"@types/yargs": "^17.0.35",

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: @types/yargs 17 is used with yargs 18 because there is no 18 line of the types, and yargs 18 doesn't ship types for its root entry. The cli only calls yargs(argv).version().options().parse(), and that result is still as any, so the 17 typings cover what's used.


Generated by Claude Code

Comment on lines +117 to +118
"@types/babel__generator": "7.27.0",
"@types/babel__template": "7.4.4",

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: pinned to the exact versions the lockfile already resolves for the other packages using them, so adding them brings in no new Babel type versions.


Generated by Claude Code

Comment on lines +50 to +52
'ignore-no-documents'?: boolean;
'emit-legacy-common-js-imports'?: boolean;
'import-extension'?: '' | `.${string}`;

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: added the kebab-case keys to the type instead of switching updateContextWithCliFlags to read the camelCase ones. Those reads have their own handling for the 'false' string value, and changing which key they read is a runtime change this typing-only fix shouldn't make.


Generated by Claude Code

Comment on lines +412 to +414
getConfig(): Types.Config;
getConfig<T>(extraConfig: T): T & Types.Config;
getConfig<T>(extraConfig?: T): Types.Config | (T & Types.Config) {

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: overloads rather than a cast on the return. The no-argument calls (updateConfig, makeGlobalPatternSet) get plain Types.Config, while getConfig(defaultSchemaLoadOptions) keeps the T & Types.Config intersection, and neither call site changes.


Generated by Claude Code

let queue: Array<{
message: string;
meta?: any[];
meta: any[];

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why need this change?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Under strict mode, printLogs spreads ...log.meta (debugging.ts#L17). With `meta?: any[]` that's `any[] | undefined`, and spreading it fails with TS2488. `meta` is never actually missing: `debugLog` always pushes its rest param `...meta: any[]` ([L8-L12](https://github.com/dotansimha/graphql-code-generator/blob/ab81a7a7be052e891d8b91b917f9289469c34d81/packages/graphql-codegen-cli/src/utils/debugging.ts#L8-L12)),`` which is [] when no extra args are passed. Making it required matches what's stored, needs no || [] fallback, and doesn't change runtime behaviour.


Generated by Claude Code

Comment on lines -133 to -136
...(typeof config.watch === 'boolean'
? []
: normalizeInstanceOrArray<string>(config.watch ?? [])),
relative(process.cwd(), initialContext.filepath),

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems to change runtime behaviour?
Explain why needed

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it changes runtime behaviour, but only when filepath is undefined. If there is a filepath, the pattern list is the same as before.

filepath is undefined when generate() is called programmatically with a config object instead of a config file. In that case:

  1. ensureContext builds new CodegenContext({ config: input }) with no filepath (config.ts#L507).
  2. With watch set, generate() calls createWatcher(context, …) (generate-and-save.ts#L188-L189), and that calls `makeGlobalPatternSet(initialContext)` ([watcher.ts#L55](https://github.com/dotansimha/graphql-code-generator/blob/ab81a7a7be052e891d8b91b917f9289469c34d81/packages/graphql-codegen-cli/src/utils/watcher.ts#L55)).``
  3. Before this PR, that ran relative(process.cwd(), undefined), which throws TypeError [ERR_INVALID_ARG_TYPE]: The "to" argument must be of type string. Received undefined (Node path.relative docs: "A TypeError is thrown if either from or to is not a string").

So the only behaviour change is that programmatic watch mode without a config file now watches the schema and document patterns instead of crashing. There's no config file to watch in that case.

It's needed because filepath is now typed string | undefined, and relative() won't accept that under strict mode. The other ways to satisfy the type are:

If you'd rather this PR not change behaviour at all, I can switch to the descriptive throw.


Generated by Claude Code

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JcaAHEYKgZnuJ894vm8nRe

eddeee888:oss:issue-fix
@eddeee888
eddeee888 force-pushed the claude/fervent-allen-xihu9n branch from e566a64 to ab81a7a Compare October 3, 2026 11:53
@eddeee888
eddeee888 marked this pull request as ready for review October 3, 2026 12:03
@eddeee888
eddeee888 merged commit 82ebbb5 into master Oct 3, 2026
23 checks passed
@eddeee888
eddeee888 deleted the claude/fervent-allen-xihu9n branch October 3, 2026 12:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants