From 1cd87c5b226f54138c473e1f7e7d691e11136af4 Mon Sep 17 00:00:00 2001 From: Arda TANRIKULU <20847995+ardatan@users.noreply.github.com> Date: Tue, 9 Jul 2019 01:22:57 -0400 Subject: [PATCH] [urql-typescript] Fix generics for operation props (#2132) * Fix generics for operation props * Fix --- dev-test/codegen.yml | 8 + dev-test/githunt/types.urql.tsx | 402 ++++++++++++++++++ package.json | 1 + .../plugins/typescript/urql/src/visitor.ts | 7 +- .../typescript/urql/tests/urql.spec.ts | 35 +- yarn.lock | 19 +- 6 files changed, 466 insertions(+), 6 deletions(-) create mode 100644 dev-test/githunt/types.urql.tsx diff --git a/dev-test/codegen.yml b/dev-test/codegen.yml index 879e622c549..a8b32bd8ae0 100644 --- a/dev-test/codegen.yml +++ b/dev-test/codegen.yml @@ -158,6 +158,14 @@ generates: - typescript - typescript-operations - typescript-stencil-apollo + ./dev-test/githunt/types.urql.tsx: + schema: ./dev-test/githunt/schema.json + documents: ./dev-test/githunt/**/*.graphql + plugins: + - add: // tslint:disable + - typescript + - typescript-operations + - typescript-urql ./dev-test/githunt: schema: ./dev-test/githunt/schema.json documents: ./dev-test/githunt/**/*.graphql diff --git a/dev-test/githunt/types.urql.tsx b/dev-test/githunt/types.urql.tsx new file mode 100644 index 00000000000..d22a58dffdf --- /dev/null +++ b/dev-test/githunt/types.urql.tsx @@ -0,0 +1,402 @@ +// tslint:disable +import gql from 'graphql-tag'; +import * as React from 'react'; +import * as Urql from 'urql'; +export type Maybe = T | null; +export type Omit = Pick>; +/** All built-in and custom scalars, mapped to their actual values */ +export type Scalars = { + ID: string, + String: string, + Boolean: boolean, + Int: number, + Float: number, +}; + +/** A comment about an entry, submitted by a user */ +export type Comment = { + __typename?: 'Comment', + /** The SQL ID of this entry */ + id: Scalars['Int'], + /** The GitHub user who posted the comment */ + postedBy: User, + /** A timestamp of when the comment was posted */ + createdAt: Scalars['Float'], + /** The text of the comment */ + content: Scalars['String'], + /** The repository which this comment is about */ + repoName: Scalars['String'], +}; + +/** Information about a GitHub repository submitted to GitHunt */ +export type Entry = { + __typename?: 'Entry', + /** Information about the repository from GitHub */ + repository: Repository, + /** The GitHub user who submitted this entry */ + postedBy: User, + /** A timestamp of when the entry was submitted */ + createdAt: Scalars['Float'], + /** The score of this repository, upvotes - downvotes */ + score: Scalars['Int'], + /** The hot score of this repository */ + hotScore: Scalars['Float'], + /** Comments posted about this repository */ + comments: Array>, + /** The number of comments posted about this repository */ + commentCount: Scalars['Int'], + /** The SQL ID of this entry */ + id: Scalars['Int'], + /** XXX to be changed */ + vote: Vote, +}; + + +/** Information about a GitHub repository submitted to GitHunt */ +export type EntryCommentsArgs = { + limit?: Maybe, + offset?: Maybe +}; + +/** A list of options for the sort order of the feed */ +export enum FeedType { + /** Sort by a combination of freshness and score, using Reddit's algorithm */ + Hot = 'HOT', + /** Newest entries first */ + New = 'NEW', + /** Highest score entries first */ + Top = 'TOP' +} + +export type Mutation = { + __typename?: 'Mutation', + /** Submit a new repository, returns the new submission */ + submitRepository?: Maybe, + /** Vote on a repository submission, returns the submission that was voted on */ + vote?: Maybe, + /** Comment on a repository, returns the new comment */ + submitComment?: Maybe, +}; + + +export type MutationSubmitRepositoryArgs = { + repoFullName: Scalars['String'] +}; + + +export type MutationVoteArgs = { + repoFullName: Scalars['String'], + type: VoteType +}; + + +export type MutationSubmitCommentArgs = { + repoFullName: Scalars['String'], + commentContent: Scalars['String'] +}; + +export type Query = { + __typename?: 'Query', + /** A feed of repository submissions */ + feed?: Maybe>>, + /** A single entry */ + entry?: Maybe, + /** Return the currently logged in user, or null if nobody is logged in */ + currentUser?: Maybe, +}; + + +export type QueryFeedArgs = { + type: FeedType, + offset?: Maybe, + limit?: Maybe +}; + + +export type QueryEntryArgs = { + repoFullName: Scalars['String'] +}; + +/** A repository object from the GitHub API. This uses the exact field names returned by the + * GitHub API for simplicity, even though the convention for GraphQL is usually to camel case. + */ +export type Repository = { + __typename?: 'Repository', + /** Just the name of the repository, e.g. GitHunt-API */ + name: Scalars['String'], + /** The full name of the repository with the username, e.g. apollostack/GitHunt-API */ + full_name: Scalars['String'], + /** The description of the repository */ + description?: Maybe, + /** The link to the repository on GitHub */ + html_url: Scalars['String'], + /** The number of people who have starred this repository on GitHub */ + stargazers_count: Scalars['Int'], + /** The number of open issues on this repository on GitHub */ + open_issues_count?: Maybe, + /** The owner of this repository on GitHub, e.g. apollostack */ + owner?: Maybe, +}; + +export type Subscription = { + __typename?: 'Subscription', + /** Subscription fires on every comment added */ + commentAdded?: Maybe, +}; + + +export type SubscriptionCommentAddedArgs = { + repoFullName: Scalars['String'] +}; + +/** A user object from the GitHub API. This uses the exact field names returned from the GitHub API. */ +export type User = { + __typename?: 'User', + /** The name of the user, e.g. apollostack */ + login: Scalars['String'], + /** The URL to a directly embeddable image for this user's avatar */ + avatar_url: Scalars['String'], + /** The URL of this user's GitHub page */ + html_url: Scalars['String'], +}; + +/** XXX to be removed */ +export type Vote = { + __typename?: 'Vote', + vote_value: Scalars['Int'], +}; + +/** The type of vote to record, when submitting a vote */ +export enum VoteType { + Up = 'UP', + Down = 'DOWN', + Cancel = 'CANCEL' +} +export type OnCommentAddedSubscriptionVariables = { + repoFullName: Scalars['String'] +}; + + +export type OnCommentAddedSubscription = ({ __typename?: 'Subscription' } & { commentAdded: Maybe<({ __typename?: 'Comment' } & Pick & { postedBy: ({ __typename?: 'User' } & Pick) })> }); + +export type CommentQueryVariables = { + repoFullName: Scalars['String'], + limit?: Maybe, + offset?: Maybe +}; + + +export type CommentQuery = ({ __typename?: 'Query' } & { currentUser: Maybe<({ __typename?: 'User' } & Pick)>, entry: Maybe<({ __typename?: 'Entry' } & Pick & { postedBy: ({ __typename?: 'User' } & Pick), comments: Array>, repository: ({ __typename?: 'Repository' } & Pick & ({ __typename?: 'Repository' } & Pick)) })> }); + +export type CommentsPageCommentFragment = ({ __typename?: 'Comment' } & Pick & { postedBy: ({ __typename?: 'User' } & Pick) }); + +export type CurrentUserForProfileQueryVariables = {}; + + +export type CurrentUserForProfileQuery = ({ __typename?: 'Query' } & { currentUser: Maybe<({ __typename?: 'User' } & Pick)> }); + +export type FeedEntryFragment = ({ __typename?: 'Entry' } & Pick & { repository: ({ __typename?: 'Repository' } & Pick & { owner: Maybe<({ __typename?: 'User' } & Pick)> }) } & (VoteButtonsFragment & RepoInfoFragment)); + +export type FeedQueryVariables = { + type: FeedType, + offset?: Maybe, + limit?: Maybe +}; + + +export type FeedQuery = ({ __typename?: 'Query' } & { currentUser: Maybe<({ __typename?: 'User' } & Pick)>, feed: Maybe>> }); + +export type SubmitRepositoryMutationVariables = { + repoFullName: Scalars['String'] +}; + + +export type SubmitRepositoryMutation = ({ __typename?: 'Mutation' } & { submitRepository: Maybe<({ __typename?: 'Entry' } & Pick)> }); + +export type RepoInfoFragment = ({ __typename?: 'Entry' } & Pick & { repository: ({ __typename?: 'Repository' } & Pick), postedBy: ({ __typename?: 'User' } & Pick) }); + +export type SubmitCommentMutationVariables = { + repoFullName: Scalars['String'], + commentContent: Scalars['String'] +}; + + +export type SubmitCommentMutation = ({ __typename?: 'Mutation' } & { submitComment: Maybe<({ __typename?: 'Comment' } & CommentsPageCommentFragment)> }); + +export type VoteButtonsFragment = ({ __typename?: 'Entry' } & Pick & { vote: ({ __typename?: 'Vote' } & Pick) }); + +export type VoteMutationVariables = { + repoFullName: Scalars['String'], + type: VoteType +}; + + +export type VoteMutation = ({ __typename?: 'Mutation' } & { vote: Maybe<({ __typename?: 'Entry' } & Pick & { vote: ({ __typename?: 'Vote' } & Pick) })> }); +export const CommentsPageCommentFragmentDoc = gql` + fragment CommentsPageComment on Comment { + id + postedBy { + login + html_url + } + createdAt + content +} + `; +export const VoteButtonsFragmentDoc = gql` + fragment VoteButtons on Entry { + score + vote { + vote_value + } +} + `; +export const RepoInfoFragmentDoc = gql` + fragment RepoInfo on Entry { + createdAt + repository { + description + stargazers_count + open_issues_count + } + postedBy { + html_url + login + } +} + `; +export const FeedEntryFragmentDoc = gql` + fragment FeedEntry on Entry { + id + commentCount + repository { + full_name + html_url + owner { + avatar_url + } + } + ...VoteButtons + ...RepoInfo +} + ${VoteButtonsFragmentDoc} +${RepoInfoFragmentDoc}`; +export const OnCommentAddedDocument = gql` + subscription onCommentAdded($repoFullName: String!) { + commentAdded(repoFullName: $repoFullName) { + id + postedBy { + login + html_url + } + createdAt + content + } +} + `; + +export const OnCommentAddedComponent = (props: Omit, 'query'> & { variables?: OnCommentAddedSubscriptionVariables }) => ( + +); + +export const CommentDocument = gql` + query Comment($repoFullName: String!, $limit: Int, $offset: Int) { + currentUser { + login + html_url + } + entry(repoFullName: $repoFullName) { + id + postedBy { + login + html_url + } + createdAt + comments(limit: $limit, offset: $offset) { + ...CommentsPageComment + } + commentCount + repository { + full_name + html_url + ... on Repository { + description + open_issues_count + stargazers_count + } + } + } +} + ${CommentsPageCommentFragmentDoc}`; + +export const CommentComponent = (props: Omit, 'query'> & { variables: CommentQueryVariables }) => ( + +); + +export const CurrentUserForProfileDocument = gql` + query CurrentUserForProfile { + currentUser { + login + avatar_url + } +} + `; + +export const CurrentUserForProfileComponent = (props: Omit, 'query'> & { variables?: CurrentUserForProfileQueryVariables }) => ( + +); + +export const FeedDocument = gql` + query Feed($type: FeedType!, $offset: Int, $limit: Int) { + currentUser { + login + } + feed(type: $type, offset: $offset, limit: $limit) { + ...FeedEntry + } +} + ${FeedEntryFragmentDoc}`; + +export const FeedComponent = (props: Omit, 'query'> & { variables: FeedQueryVariables }) => ( + +); + +export const SubmitRepositoryDocument = gql` + mutation submitRepository($repoFullName: String!) { + submitRepository(repoFullName: $repoFullName) { + createdAt + } +} + `; + +export const SubmitRepositoryComponent = (props: Omit, 'query'> & { variables?: SubmitRepositoryMutationVariables }) => ( + +); + +export const SubmitCommentDocument = gql` + mutation submitComment($repoFullName: String!, $commentContent: String!) { + submitComment(repoFullName: $repoFullName, commentContent: $commentContent) { + ...CommentsPageComment + } +} + ${CommentsPageCommentFragmentDoc}`; + +export const SubmitCommentComponent = (props: Omit, 'query'> & { variables?: SubmitCommentMutationVariables }) => ( + +); + +export const VoteDocument = gql` + mutation vote($repoFullName: String!, $type: VoteType!) { + vote(repoFullName: $repoFullName, type: $type) { + score + id + vote { + vote_value + } + } +} + `; + +export const VoteComponent = (props: Omit, 'query'> & { variables?: VoteMutationVariables }) => ( + +); diff --git a/package.json b/package.json index c662dbd76da..26a287d51b0 100644 --- a/package.json +++ b/package.json @@ -54,6 +54,7 @@ "microbundle": "0.11.0", "react-apollo": "2.5.8", "react-apollo-hooks": "0.4.5", + "urql": "1.1.3", "rimraf": "2.6.3", "ts-jest": "24.0.2", "tslint": "5.18.0", diff --git a/packages/plugins/typescript/urql/src/visitor.ts b/packages/plugins/typescript/urql/src/visitor.ts index 8cf822dc8b0..a38c4f8def9 100644 --- a/packages/plugins/typescript/urql/src/visitor.ts +++ b/packages/plugins/typescript/urql/src/visitor.ts @@ -48,8 +48,13 @@ export class UrqlVisitor extends ClientSideBaseVisitor variableDef.type.kind === Kind.NON_NULL_TYPE); + const generics = [operationResultType, operationVariablesTypes]; + + if (operationType === 'Subscription') { + generics.unshift(operationResultType); + } return ` -export const ${componentName} = (props: Omit & { variables${isVariablesRequired ? '' : '?'}: ${operationVariablesTypes} }) => ( +export const ${componentName} = (props: Omit, 'query'> & { variables${isVariablesRequired ? '' : '?'}: ${operationVariablesTypes} }) => ( ); `; diff --git a/packages/plugins/typescript/urql/tests/urql.spec.ts b/packages/plugins/typescript/urql/tests/urql.spec.ts index dc1b0ad08ae..ded47468824 100644 --- a/packages/plugins/typescript/urql/tests/urql.spec.ts +++ b/packages/plugins/typescript/urql/tests/urql.spec.ts @@ -349,7 +349,7 @@ query MyFeed { )) as Types.ComplexPluginOutput; expect(content.content).toBeSimilarStringTo(` - export const TestComponent = (props: Omit & { variables?: TestQueryVariables }) => + export const TestComponent = (props: Omit, 'query'> & { variables?: TestQueryVariables }) => ( ); @@ -398,7 +398,7 @@ query MyFeed { )) as Types.ComplexPluginOutput; expect(content.content).toBeSimilarStringTo(` - export const TestComponent = (props: Omit & { variables: TestQueryVariables }) => ( + export const TestComponent = (props: Omit, 'query'> & { variables: TestQueryVariables }) => ( );`); await validateTypeScript(content, schema, docs, {}); @@ -430,7 +430,7 @@ query MyFeed { )) as Types.ComplexPluginOutput; expect(content.content).toBeSimilarStringTo(` - export const TestComponent = (props: Omit & { variables?: TestMutationVariables }) => ( + export const TestComponent = (props: Omit, 'query'> & { variables?: TestMutationVariables }) => ( );`); await validateTypeScript(content, schema, docs, {}); @@ -449,6 +449,35 @@ query MyFeed { expect(content.content).not.toContain(`export class ITestComponent`); }); + + it('should add three generics if operation type is subscription', async () => { + const documents = parse(/* GraphQL */ ` + subscription ListenToComments($name: String) { + commentAdded(repoFullName: $name) { + id + } + } + `); + + const docs = [{ filePath: '', content: documents }]; + + const content = (await plugin( + schema, + docs, + { + withComponent: true, + }, + { + outputFile: 'graphql.tsx', + } + )) as Types.ComplexPluginOutput; + + expect(content.content).toBeSimilarStringTo(` + export const ListenToCommentsComponent = (props: Omit, 'query'> & { variables?: ListenToCommentsSubscriptionVariables }) => ( + + );`); + await validateTypeScript(content, schema, docs, {}); + }); }); describe('Hooks', () => { diff --git a/yarn.lock b/yarn.lock index d124269177a..603da52b5ad 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8049,7 +8049,7 @@ graphql-tag-pluck@0.8.3: "@babel/types" "^7.4.4" source-map-support "^0.5.12" -graphql-tag@2.10.1, graphql-tag@^2.9.2: +graphql-tag@2.10.1, graphql-tag@^2.10.1, graphql-tag@^2.9.2: version "2.10.1" resolved "https://registry.yarnpkg.com/graphql-tag/-/graphql-tag-2.10.1.tgz#10aa41f1cd8fae5373eaf11f1f67260a3cad5e02" integrity sha512-jApXqWBzNXQ8jYa/HLkZJaVw9jgwNqZkywa2zfFn16Iv1Zb7ELNHkJaXHR7Quvd5SIGsy6Ny7SUKATgnu05uEg== @@ -13651,7 +13651,7 @@ promzard@^0.3.0: dependencies: read "1" -prop-types@^15.6.2, prop-types@^15.7.2: +prop-types@^15.6.0, prop-types@^15.6.2, prop-types@^15.7.2: version "15.7.2" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5" integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ== @@ -16722,6 +16722,16 @@ url@^0.11.0: punycode "1.3.2" querystring "0.2.0" +urql@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/urql/-/urql-1.1.3.tgz#41fffb0a3b1a3f7ac47be55b7f84ca69875e7358" + integrity sha512-H6xdtT+b3n7OYG1U/ItLHTBfBpM0Kxd6FkQSYP1TvylqUeyz160whRu/XpwDRt2VwbLy5+j729wv4BF1f1zP4Q== + dependencies: + fast-json-stable-stringify "^2.0.0" + graphql-tag "^2.10.1" + prop-types "^15.6.0" + wonka "^3.0.0" + use@^3.1.0: version "3.1.1" resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" @@ -17146,6 +17156,11 @@ windows-release@^3.1.0: dependencies: execa "^1.0.0" +wonka@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/wonka/-/wonka-3.0.0.tgz#4f32a6a30e3229bae5944f002ce32d05b0423ba8" + integrity sha512-eDDzMU1gv1OgZG0fYom/0xi3WO4s2ILt6QvZzjcnSvFM+nzpgX4sux1+Z+LDYWhknJA9agpDADVtATZtajMtLA== + wordwrap@0.0.2: version "0.0.2" resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.2.tgz#b79669bb42ecb409f83d583cad52ca17eaa1643f"