diff --git a/.changeset/serious-ladybugs-pull.md b/.changeset/serious-ladybugs-pull.md new file mode 100644 index 00000000000..45faa9c344b --- /dev/null +++ b/.changeset/serious-ladybugs-pull.md @@ -0,0 +1,5 @@ +--- +'@graphql-codegen/visitor-plugin-common': major +--- + +path starts with "#" diff --git a/dev-test/codegen.ts b/dev-test/codegen.ts index ba8afd46511..7c0b9838288 100644 --- a/dev-test/codegen.ts +++ b/dev-test/codegen.ts @@ -232,6 +232,17 @@ const config: CodegenConfig = { mergeFragmentTypes: true, }, }, + './dev-test/subpath-import/result.d.ts': { + schema: './dev-test/subpath-import/schema.graphql', + plugins: ['typescript', 'typescript-resolvers'], + config: { + contextType: '\\#test-null-value/context#TestContext', + fieldContextTypes: ['mutation.createUser#\\#test/root#FiedContextType'], + enumValues: { + RoleStatus: '\\#changeName/server/drizzle/schema#RoleStatus', + }, + }, + }, }, }; diff --git a/dev-test/subpath-import/result.d.ts b/dev-test/subpath-import/result.d.ts new file mode 100644 index 00000000000..f066002aea9 --- /dev/null +++ b/dev-test/subpath-import/result.d.ts @@ -0,0 +1,167 @@ +import { RoleStatus } from '#changeName/server/drizzle/schema'; +import { GraphQLResolveInfo } from 'graphql'; +import { TestContext } from '#test-null-value/context'; +import { FiedContextType } from '#test/root'; +export type Maybe = T | null; +export type InputMaybe = Maybe; +export type Exact = { [K in keyof T]: T[K] }; +export type MakeOptional = Omit & { [SubKey in K]?: Maybe }; +export type MakeMaybe = Omit & { [SubKey in K]: Maybe }; +export type MakeEmpty = { [_ in K]?: never }; +export type Incremental = T | { [P in keyof T]?: P extends ' $fragmentName' | '__typename' ? T[P] : never }; +export type EnumResolverSignature = { [key in keyof T]?: AllowedValues }; +export type RequireFields = Omit & { [P in K]-?: NonNullable }; +/** All built-in and custom scalars, mapped to their actual values */ +export type Scalars = { + ID: { input: string; output: string }; + String: { input: string; output: string }; + Boolean: { input: boolean; output: boolean }; + Int: { input: number; output: number }; + Float: { input: number; output: number }; +}; + +export { RoleStatus }; + +export type User = { + __typename?: 'User'; + createdAt: Scalars['String']['output']; + email: Scalars['String']['output']; + id: Scalars['ID']['output']; + name: Scalars['String']['output']; + password: Scalars['String']['output']; + updatedAt: Scalars['String']['output']; +}; + +export type Mutation = { + __typename?: 'mutation'; + createUser: User; +}; + +export type MutationCreateUserArgs = { + email: Scalars['String']['input']; + name: Scalars['String']['input']; + password: Scalars['String']['input']; +}; + +export type ResolverTypeWrapper = Promise | T; + +export type ResolverWithResolve = { + resolve: ResolverFn; +}; +export type Resolver = + | ResolverFn + | ResolverWithResolve; + +export type ResolverFn = ( + parent: TParent, + args: TArgs, + context: TContext, + info: GraphQLResolveInfo +) => Promise | TResult; + +export type SubscriptionSubscribeFn = ( + parent: TParent, + args: TArgs, + context: TContext, + info: GraphQLResolveInfo +) => AsyncIterable | Promise>; + +export type SubscriptionResolveFn = ( + parent: TParent, + args: TArgs, + context: TContext, + info: GraphQLResolveInfo +) => TResult | Promise; + +export interface SubscriptionSubscriberObject { + subscribe: SubscriptionSubscribeFn<{ [key in TKey]: TResult }, TParent, TContext, TArgs>; + resolve?: SubscriptionResolveFn; +} + +export interface SubscriptionResolverObject { + subscribe: SubscriptionSubscribeFn; + resolve: SubscriptionResolveFn; +} + +export type SubscriptionObject = + | SubscriptionSubscriberObject + | SubscriptionResolverObject; + +export type SubscriptionResolver = + | ((...args: any[]) => SubscriptionObject) + | SubscriptionObject; + +export type TypeResolveFn = ( + parent: TParent, + context: TContext, + info: GraphQLResolveInfo +) => Maybe | Promise>; + +export type IsTypeOfResolverFn = ( + obj: T, + context: TContext, + info: GraphQLResolveInfo +) => boolean | Promise; + +export type NextResolverFn = () => Promise; + +export type DirectiveResolverFn = ( + next: NextResolverFn, + parent: TParent, + args: TArgs, + context: TContext, + info: GraphQLResolveInfo +) => TResult | Promise; + +/** Mapping between all available schema types and the resolvers types */ +export type ResolversTypes = { + Boolean: ResolverTypeWrapper; + ID: ResolverTypeWrapper; + RoleStatus: RoleStatus; + String: ResolverTypeWrapper; + User: ResolverTypeWrapper; + mutation: ResolverTypeWrapper; +}; + +/** Mapping between all available schema types and the resolvers parents */ +export type ResolversParentTypes = { + Boolean: Scalars['Boolean']['output']; + ID: Scalars['ID']['output']; + String: Scalars['String']['output']; + User: User; + mutation: Mutation; +}; + +export type RoleStatusResolvers = EnumResolverSignature<{ ADMIN?: any; USER?: any }, ResolversTypes['RoleStatus']>; + +export type UserResolvers< + ContextType = TestContext, + ParentType extends ResolversParentTypes['User'] = ResolversParentTypes['User'] +> = { + createdAt?: Resolver; + email?: Resolver; + id?: Resolver; + name?: Resolver; + password?: Resolver; + updatedAt?: Resolver; + __isTypeOf?: IsTypeOfResolverFn; +}; + +export type MutationResolvers< + ContextType = TestContext, + ParentType extends ResolversParentTypes['mutation'] = ResolversParentTypes['mutation'] +> = { + createUser?: Resolver< + ResolversTypes['User'], + ParentType, + FiedContextType, + RequireFields + >; + __isTypeOf?: IsTypeOfResolverFn; +}; + +export type Resolvers = { + RoleStatus?: RoleStatusResolvers; + User?: UserResolvers; + mutation?: MutationResolvers; +}; diff --git a/dev-test/subpath-import/schema.graphql b/dev-test/subpath-import/schema.graphql new file mode 100644 index 00000000000..3010f2f4bc4 --- /dev/null +++ b/dev-test/subpath-import/schema.graphql @@ -0,0 +1,17 @@ +type User { + id: ID! + name: String! + email: String! + password: String! + createdAt: String! + updatedAt: String! +} + +enum RoleStatus { + ADMIN + USER +} + +type mutation { + createUser(name: String!, email: String!, password: String!): User! +} diff --git a/packages/plugins/other/visitor-plugin-common/src/base-resolvers-visitor.ts b/packages/plugins/other/visitor-plugin-common/src/base-resolvers-visitor.ts index ac43a2328a7..2d20fe39a56 100644 --- a/packages/plugins/other/visitor-plugin-common/src/base-resolvers-visitor.ts +++ b/packages/plugins/other/visitor-plugin-common/src/base-resolvers-visitor.ts @@ -1075,10 +1075,15 @@ export class BaseResolversVisitor< protected createFieldContextTypeMap(): FieldContextTypeMap { return this.config.fieldContextTypes.reduce((prev, fieldContextType) => { + const isScoped = fieldContextType.includes('\\#'); + if (fieldContextType.includes('\\#')) { + fieldContextType = fieldContextType.replace('\\#', ''); + } const items = fieldContextType.split('#'); if (items.length === 3) { const [path, source, contextTypeName] = items; - return { ...prev, [path]: parseMapper(`${source}#${contextTypeName}`) }; + const sourceStr = isScoped ? `\\#${source}` : source; + return { ...prev, [path]: parseMapper(`${sourceStr}#${contextTypeName}`) }; } const [path, contextType] = items; return { ...prev, [path]: parseMapper(contextType) }; @@ -1086,10 +1091,15 @@ export class BaseResolversVisitor< } protected createDirectivedContextType(): FieldContextTypeMap { return this.config.directiveContextTypes.reduce((prev, fieldContextType) => { + const isScoped = fieldContextType.includes('\\#'); + if (fieldContextType.includes('\\#')) { + fieldContextType = fieldContextType.replace('\\#', ''); + } const items = fieldContextType.split('#'); if (items.length === 3) { const [path, source, contextTypeName] = items; - return { ...prev, [path]: parseMapper(`${source}#${contextTypeName}`) }; + const sourceStr = isScoped ? `\\#${source}` : source; + return { ...prev, [path]: parseMapper(`${sourceStr}#${contextTypeName}`) }; } const [path, contextType] = items; return { ...prev, [path]: parseMapper(contextType) }; diff --git a/packages/plugins/other/visitor-plugin-common/src/mappers.ts b/packages/plugins/other/visitor-plugin-common/src/mappers.ts index 2559caa6544..8eb1048b119 100644 --- a/packages/plugins/other/visitor-plugin-common/src/mappers.ts +++ b/packages/plugins/other/visitor-plugin-common/src/mappers.ts @@ -33,10 +33,16 @@ interface Helpers { } function prepareLegacy(mapper: string): Helpers { + const isScoped = mapper.includes('\\#'); + if (mapper.includes('\\#')) { + mapper = mapper.replace('\\#', ''); + } const items = mapper.split('#'); const isNamespace = items.length === 3; const isDefault = items[1].trim() === 'default' || items[1].startsWith('default '); const hasAlias = items[1].includes(' as '); + const source = isScoped ? `#${items[0]}` : items[0]; + items[0] = source; return { items, @@ -47,10 +53,15 @@ function prepareLegacy(mapper: string): Helpers { } function prepare(mapper: string): Helpers { - const [source, path] = mapper.split('#'); + const isScoped = mapper.includes('\\#'); + if (mapper.includes('\\#')) { + mapper = mapper.replace('\\#', ''); + } + let [source, path] = mapper.split('#'); const isNamespace = path.includes('.'); const isDefault = path.trim() === 'default' || path.startsWith('default '); const hasAlias = path.includes(' as '); + source = isScoped ? `#${source}` : source; return { items: isNamespace ? [source, ...path.split('.')] : [source, path], @@ -61,6 +72,9 @@ function prepare(mapper: string): Helpers { } function isLegacyMode(mapper: string) { + if (mapper.includes('\\#')) { + mapper = mapper.replace('\\#', ''); + } return mapper.split('#').length === 3; }