Repository navigation
[visitor-plugin-common] fix: satisfy strict mode in utils and the remaining files (#4683) #11039
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
db5ae88
ffb7bf6
19eff01
a02bbdd
6f952a0
ad3ef43
f8e2f2a
f6a1521
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| --- | ||
| '@graphql-codegen/visitor-plugin-common': patch | ||
| --- | ||
|
|
||
| Fix type errors in `visitor-plugin-common` (`utils`, `imports`, `naming`, `mappers`, `types`, `variables-to-object`, `convert-schema-enum-to-declaration-block-string`, `selection-set-processor/base` and `optimize-operations`) under `strict: true`: | ||
|
|
||
| - `transformComment` accepts `undefined` and `null`, which it already handles by returning an empty string. | ||
| - `block` declares its param as `string[] | null | undefined`, which it already handles by returning an empty string. | ||
| - `DeclarationBlock`'s fields are typed as `string | null` (`_name` as `string | NameNode | null`), and `withBlock` accepts `null`, which renders the same as no block. | ||
| - `DeclarationBlock` falls back to returning the block unchanged when `blockTransformer` is passed as `undefined`, instead of throwing. | ||
| - `buildScalars` accepts an `undefined` scalars mapping, which it already handles by using only the defaults and the schema. | ||
| - `buildTypeImport` accepts a nullish `identifier` and `source`, and throws a descriptive error when either is missing. | ||
| - `ParsedEnumValuesMap`'s `mappedValues`, `sourceIdentifier`, `sourceFile` and `importIdentifier` accept `null`, which `parseEnumValues` already emits. | ||
| - `transformMappers` defaults `rawMappers` to `{}`. | ||
| - `OperationVariablesToObject.getName`, `OperationVariablesToObject.transform` and `BaseSelectionSetProcessor.buildSelectionSetFromStrings` may return `null`, which they already return. | ||
| - `buildEnumValuesBlock` throws a descriptive error when an enum value is not defined on the schema's enum. | ||
| - `optimizeOperations` throws a descriptive error when a document file has no parsed document. | ||
| - `unique`'s default key uses `String(item)`. | ||
| - `convertSchemaEnumToDeclarationBlockString` treats an enum node without `values` as empty instead of throwing. | ||
| - `generateFragmentImportStatement` treats a missing `identifiers` as empty. | ||
| - `getDeprecationReason` returns the default reason for a `@deprecated` directive without arguments. | ||
| - `isOneOfInputObjectType` returns `false` instead of `undefined`. | ||
| - `buildEnumValuesBlock` treats a non-enum schema type with the enum's name as having no schema enum. | ||
| - `OperationVariablesToObject` emits `''` instead of the text `undefined`/`null` when a variable has no name or no type identifier. | ||
|
|
||
| Generated output is unchanged, except in the cases above. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,8 +1,8 @@ | ||
| import type { | ||
| EnumTypeDefinitionNode, | ||
| EnumValueDefinitionNode, | ||
| GraphQLEnumType, | ||
| GraphQLSchema, | ||
| import { | ||
| isEnumType, | ||
| type EnumTypeDefinitionNode, | ||
| type EnumValueDefinitionNode, | ||
| type GraphQLSchema, | ||
| } from 'graphql'; | ||
| import { convertName } from './naming.js'; | ||
| import type { ConvertFn, ParsedEnumValuesMap } from './types.js'; | ||
|
|
@@ -75,7 +75,7 @@ export const convertSchemaEnumToDeclarationBlockString = ({ | |
| .withName(enumTypeName) | ||
| .withContent( | ||
| '\n' + | ||
| node.values | ||
| (node.values || []) | ||
| .map(enumOption => { | ||
| const name = enumOption.name.value; | ||
| const enumValue: string | number = getValueFromConfig(name) ?? name; | ||
|
|
@@ -95,7 +95,7 @@ export const convertSchemaEnumToDeclarationBlockString = ({ | |
| .withName(enumTypeName) | ||
| .asKind('enum') | ||
| .withBlock( | ||
| node.values | ||
| (node.values || []) | ||
| .map((enumOption, i) => { | ||
| const valueFromConfig = getValueFromConfig(enumOption.name.value); | ||
| const enumValue: string | number = valueFromConfig ?? i; | ||
|
|
@@ -130,7 +130,7 @@ export const convertSchemaEnumToDeclarationBlockString = ({ | |
| .withName(enumTypeName) | ||
| .withComment(node.description?.value) | ||
| .withBlock( | ||
| node.values | ||
| (node.values || []) | ||
| .map(enumOption => { | ||
| const optionName = makeValidEnumIdentifier( | ||
| convertName({ | ||
|
|
@@ -164,7 +164,7 @@ export const convertSchemaEnumToDeclarationBlockString = ({ | |
| .withBlock( | ||
| buildEnumValuesBlock({ | ||
| typeName: enumName, | ||
| values: node.values, | ||
| values: node.values || [], | ||
| schema, | ||
| naming, | ||
| ignoreEnumValuesFromSchema, | ||
|
|
@@ -189,9 +189,8 @@ export const buildEnumValuesBlock = ({ | |
| typeName: string; | ||
| values: ReadonlyArray<EnumValueDefinitionNode>; | ||
| }): string => { | ||
| const schemaEnumType: GraphQLEnumType | undefined = schema | ||
| ? (schema.getType(typeName) as GraphQLEnumType) | ||
| : undefined; | ||
| const schemaType = schema?.getType(typeName); | ||
| const schemaEnumType = isEnumType(schemaType) ? schemaType : undefined; | ||
|
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note: This uses an Generated by Claude Code |
||
|
|
||
| return values | ||
| .map(enumOption => { | ||
|
|
@@ -212,10 +211,16 @@ export const buildEnumValuesBlock = ({ | |
| }), | ||
| ); | ||
| const comment = getNodeComment(enumOption); | ||
| const schemaEnumValue = | ||
| schemaEnumType && !ignoreEnumValuesFromSchema | ||
| ? schemaEnumType.getValue(enumOption.name.value).value | ||
| : undefined; | ||
| let schemaEnumValue; | ||
|
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note: The ternary became a Generated by Claude Code |
||
| if (schemaEnumType && !ignoreEnumValuesFromSchema) { | ||
| const schemaEnumValueDefinition = schemaEnumType.getValue(enumOption.name.value); | ||
| if (!schemaEnumValueDefinition) { | ||
| throw new Error( | ||
| `Enum value "${enumOption.name.value}" is not defined on enum "${typeName}" in the schema`, | ||
| ); | ||
| } | ||
| schemaEnumValue = schemaEnumValueDefinition.value; | ||
| } | ||
| let enumValue: string | number = | ||
| typeof schemaEnumValue === 'undefined' ? enumOption.name.value : schemaEnumValue; | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -40,7 +40,7 @@ export function generateFragmentImportStatement( | |
| const { importSource: fragmentImportSource, ...rest } = statement; | ||
| const { identifiers, path, namespace } = fragmentImportSource; | ||
| const importSource: ImportSource<string> = { | ||
| identifiers: identifiers | ||
| identifiers: (identifiers || []) | ||
| .filter(fragmentImport => kind === 'both' || kind === fragmentImport.kind) | ||
| .map(({ name }) => name), | ||
| path, | ||
|
|
@@ -126,9 +126,9 @@ export function getEnumsImports({ | |
| useTypeImports, | ||
| }: { | ||
| typeIdentifierConverted: string; | ||
| importIdentifier: string | null; | ||
| sourceIdentifier: string | null; | ||
| sourceFile: string | null; | ||
| importIdentifier?: string | null; | ||
| sourceIdentifier?: string | null; | ||
| sourceFile?: string | null; | ||
|
Comment on lines
+129
to
+131
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note: These are the params of the nested Generated by Claude Code |
||
| useTypeImports: boolean; | ||
| }): string[] { | ||
| if (importIdentifier !== sourceIdentifier) { | ||
|
|
@@ -196,11 +196,17 @@ export function buildTypeImport({ | |
| useTypeImports, | ||
| asDefault = false, | ||
| }: { | ||
| identifier: string; | ||
| source: string; | ||
| identifier: string | null | undefined; | ||
| source: string | null | undefined; | ||
| useTypeImports: boolean; | ||
| asDefault?: boolean; | ||
| }): string { | ||
| // `== null` (not a falsy check) so only a missing identifier or source throws; an empty string is still allowed | ||
| if (identifier == null || source == null) { | ||
|
eddeee888 marked this conversation as resolved.
|
||
| throw new Error( | ||
| `Cannot build a type import without an identifier and a source (identifier: ${identifier}, source: ${source})`, | ||
| ); | ||
| } | ||
| if (asDefault) { | ||
| if (useTypeImports) { | ||
| return `import type { default as ${identifier} } from '${source}';`; | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -194,7 +194,7 @@ export function isExternalMapper(value: string): boolean { | |
| } | ||
|
|
||
| export function transformMappers( | ||
| rawMappers: RawResolversConfig['mappers'], | ||
| rawMappers: RawResolversConfig['mappers'] = {}, | ||
|
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note: I gave the param a default instead of adding Generated by Claude Code |
||
| mapperTypeSuffix?: string, | ||
| ): ParsedResolversConfig['mappers'] { | ||
| const result: ParsedResolversConfig['mappers'] = {}; | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -57,7 +57,7 @@ export function quoteIfNeeded(array: string[], joinWith = ' & '): string { | |
| return `(${array.join(joinWith)})`; | ||
| } | ||
|
|
||
| export function block(array) { | ||
| export function block(array: string[] | null | undefined): string { | ||
| return array && array.length !== 0 ? '{\n' + array.join('\n') + '\n}' : ''; | ||
| } | ||
|
|
||
|
|
@@ -107,7 +107,7 @@ export interface DeclarationBlockConfig { | |
| } | ||
|
|
||
| export function transformComment( | ||
| comment: string | StringValueNode, | ||
| comment: string | StringValueNode | undefined | null, | ||
| indentLevel = 0, | ||
| disabled = false, | ||
| ): string { | ||
|
|
@@ -129,14 +129,14 @@ export function transformComment( | |
| } | ||
|
|
||
| export class DeclarationBlock { | ||
| _decorator = null; | ||
| _decorator: string | null = null; | ||
| _export = false; | ||
| _name = null; | ||
| _kind = null; | ||
| _methodName = null; | ||
| _content = null; | ||
| _block = null; | ||
| _nameGenerics = null; | ||
| _name: string | NameNode | null = null; | ||
| _kind: string | null = null; | ||
| _methodName: string | null = null; | ||
| _content: string | null = null; | ||
| _block: string | null = null; | ||
| _nameGenerics: string | null = null; | ||
| _comment: string | null = null; | ||
| _ignoreBlockWrapper = false; | ||
|
|
||
|
|
@@ -189,7 +189,7 @@ export class DeclarationBlock { | |
| return this; | ||
| } | ||
|
|
||
| withBlock(block: string): DeclarationBlock { | ||
| withBlock(block: string | null): DeclarationBlock { | ||
| this._block = block; | ||
|
|
||
| return this; | ||
|
|
@@ -210,6 +210,7 @@ export class DeclarationBlock { | |
|
|
||
| public get string(): string { | ||
| let result = ''; | ||
| const blockTransformer = this._config.blockTransformer || (block => block); | ||
|
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note: The fallback is in the getter rather than the constructor's defaults. The constructor spreads the caller's config over the defaults, so an explicit Generated by Claude Code |
||
|
|
||
| if (this._decorator) { | ||
| result += this._decorator + '\n'; | ||
|
|
@@ -245,14 +246,14 @@ export class DeclarationBlock { | |
| const block = [before, this._block, after].filter(val => !!val).join('\n'); | ||
|
|
||
| if (this._methodName) { | ||
| result += `${this._methodName}(${this._config.blockTransformer(block)})`; | ||
| result += `${this._methodName}(${blockTransformer(block)})`; | ||
| } else { | ||
| result += this._config.blockTransformer(block); | ||
| result += blockTransformer(block); | ||
| } | ||
| } else if (this._content) { | ||
| result += this._content; | ||
| } else if (this._kind) { | ||
| result += this._config.blockTransformer('{}'); | ||
| result += blockTransformer('{}'); | ||
| } | ||
|
|
||
| return stripTrailingSpaces( | ||
|
|
@@ -293,7 +294,7 @@ export function buildScalarsFromConfig( | |
|
|
||
| export function buildScalars( | ||
| schema: GraphQLSchema | undefined, | ||
| scalarsMapping: ScalarsMap, | ||
| scalarsMapping: ScalarsMap | undefined, | ||
| defaultScalarsMapping: NormalizedScalarsMap = DEFAULT_SCALARS, | ||
| defaultScalarType: string | null = 'unknown', | ||
| ): ParsedScalarsMap { | ||
|
|
@@ -641,8 +642,11 @@ export function isOneOfInputObjectType( | |
|
|
||
| isOneOfType = | ||
| isInputObjectType(namedType) && | ||
| ((namedType as unknown as Record<'isOneOf', boolean | undefined>).isOneOf || | ||
| namedType.astNode?.directives?.some(d => d.name.value === 'oneOf')); | ||
| // `isOneOf` only exists on GraphQLInputObjectType from graphql 16.9; removing this cast needs | ||
| // dropping support for older graphql versions | ||
| (((namedType as unknown as Record<'isOneOf', boolean | undefined>).isOneOf || | ||
| namedType.astNode?.directives?.some(d => d.name.value === 'oneOf')) ?? | ||
| false); | ||
|
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note: The Generated by Claude Code |
||
|
|
||
| isOneOfTypeCache.set(namedType, isOneOfType); | ||
|
|
||
|
|
@@ -666,7 +670,7 @@ export function flatten<T>(array: Array<Array<T>>): Array<T> { | |
|
|
||
| export function unique<T>( | ||
| array: Array<T>, | ||
| key: (item: T) => string | number = item => item.toString(), | ||
| key: (item: T) => string | number = item => String(item), | ||
|
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note: Generated by Claude Code |
||
| ): Array<T> { | ||
| return Object.values(array.reduce((acc, item) => ({ [key(item)]: item, ...acc }), {})); | ||
| } | ||
|
|
@@ -752,7 +756,7 @@ export const getNodeComment = ( | |
| const getDeprecationReason = (directive: DirectiveNode): string | void => { | ||
| if (directive.name.value === 'deprecated') { | ||
| let reason = 'Field no longer supported'; | ||
| const deprecatedReason = directive.arguments[0]; | ||
| const deprecatedReason = directive.arguments?.[0]; | ||
| if (deprecatedReason && deprecatedReason.value.kind === Kind.STRING) { | ||
| reason = deprecatedReason.value.value; | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note:
valuesis optional onEnumTypeDefinitionNode, and this function is exported, so a caller can pass in a node that has none. That makes|| []real handling, which is why it has no comment. Eachifbranch returns early and readsnode.valuesonly once, so the fallback stays inline in each branch (same at L98, L133 and L167) instead of going in a local at the top.Generated by Claude Code