diff --git a/workspaces/leetcode-api/.gitattributes b/workspaces/leetcode-api/.gitattributes index 1c69deda..46cad71d 100644 --- a/workspaces/leetcode-api/.gitattributes +++ b/workspaces/leetcode-api/.gitattributes @@ -1 +1,2 @@ schema.graphql linguist-generated +*.generated.ts linguist-generated diff --git a/workspaces/leetcode-api/src/api/active-daily-coding-challenge-question/fetchGraphQL.generated.ts b/workspaces/leetcode-api/src/api/active-daily-coding-challenge-question/fetchGraphQL.generated.ts index e342d03f..4169d440 100644 --- a/workspaces/leetcode-api/src/api/active-daily-coding-challenge-question/fetchGraphQL.generated.ts +++ b/workspaces/leetcode-api/src/api/active-daily-coding-challenge-question/fetchGraphQL.generated.ts @@ -27,7 +27,7 @@ export type QueryVariables = export type Query = Simplify; export const QUERY = - "query ActiveDailyCodingChallengeQuestion{activeDailyCodingChallengeQuestion{date question{difficulty questionFrontendId title titleSlug}}}"; + "query{activeDailyCodingChallengeQuestion{date question{difficulty questionFrontendId title titleSlug}}}"; export function fetchGraphQL(variables: QueryVariables): Promise { return getGraphQLClient().request(QUERY, variables); diff --git a/workspaces/leetcode-api/src/api/question-list/fetchGraphQL.generated.ts b/workspaces/leetcode-api/src/api/question-list/fetchGraphQL.generated.ts index 0726731e..06ca7503 100644 --- a/workspaces/leetcode-api/src/api/question-list/fetchGraphQL.generated.ts +++ b/workspaces/leetcode-api/src/api/question-list/fetchGraphQL.generated.ts @@ -30,7 +30,7 @@ export type QueryVariables = Simplify; export type Query = Simplify; export const QUERY = - "query QuestionList($categorySlug:String!,$limit:Int,$skip:Int,$filters:QuestionListFilterInput!){questionList(categorySlug:$categorySlug limit:$limit skip:$skip filters:$filters){data{difficulty isPaidOnly questionFrontendId title titleSlug}totalNum}}"; + "query($categorySlug:String!,$limit:Int,$skip:Int,$filters:QuestionListFilterInput!){questionList(categorySlug:$categorySlug limit:$limit skip:$skip filters:$filters){data{difficulty isPaidOnly questionFrontendId title titleSlug}totalNum}}"; export function fetchGraphQL(variables: QueryVariables): Promise { return getGraphQLClient().request(QUERY, variables); diff --git a/workspaces/leetcode-api/src/api/recent-ac-submission-list/fetchGraphQL.generated.ts b/workspaces/leetcode-api/src/api/recent-ac-submission-list/fetchGraphQL.generated.ts index 69384754..948ca991 100644 --- a/workspaces/leetcode-api/src/api/recent-ac-submission-list/fetchGraphQL.generated.ts +++ b/workspaces/leetcode-api/src/api/recent-ac-submission-list/fetchGraphQL.generated.ts @@ -24,7 +24,7 @@ export type QueryVariables = Simplify; export type Query = Simplify; export const QUERY = - "query RecentAcSubmissionList($username:String!,$limit:Int!){recentAcSubmissionList(username:$username,limit:$limit){id title titleSlug timestamp}}"; + "query($username:String!,$limit:Int!){recentAcSubmissionList(username:$username,limit:$limit){id title titleSlug timestamp}}"; export function fetchGraphQL(variables: QueryVariables): Promise { return getGraphQLClient().request(QUERY, variables); diff --git a/workspaces/leetcode-api/src/api/topic/fetchGraphQL.generated.ts b/workspaces/leetcode-api/src/api/topic/fetchGraphQL.generated.ts index 5a148214..ed3a04d0 100644 --- a/workspaces/leetcode-api/src/api/topic/fetchGraphQL.generated.ts +++ b/workspaces/leetcode-api/src/api/topic/fetchGraphQL.generated.ts @@ -22,7 +22,7 @@ export type QueryVariables = Simplify; export type Query = Simplify; export const QUERY = - "query Topic($topicId:Int!){topic(id:$topicId){title solutionTags{slug}post{content}}}"; + "query($topicId:Int!){topic(id:$topicId){title solutionTags{slug}post{content}}}"; export function fetchGraphQL(variables: QueryVariables): Promise { return getGraphQLClient().request(QUERY, variables); diff --git a/workspaces/leetcode-api/src/scripts/codegen/graphqlCodegenPlugin.ts b/workspaces/leetcode-api/src/scripts/codegen/graphqlCodegenPlugin.ts index 06d2c3e0..00d86614 100644 --- a/workspaces/leetcode-api/src/scripts/codegen/graphqlCodegenPlugin.ts +++ b/workspaces/leetcode-api/src/scripts/codegen/graphqlCodegenPlugin.ts @@ -6,15 +6,19 @@ import { z } from "zod"; import { isObject } from "@code-chronicles/util/isObject"; import { only } from "@code-chronicles/util/only"; +import { spliceString } from "@code-chronicles/util/spliceString"; -const operationNameZodType = z - .object({ value: z.string() }) - .transform(({ value }) => value); +const nonNegativeIntZodType = z.number().int().nonnegative(); + +const operationNameZodType = z.object({ + value: z.string(), + loc: z.object({ start: nonNegativeIntZodType, end: nonNegativeIntZodType }), +}); export const plugin: PluginFunction<{}> = function plugin(_schema, documents) { // Encode some assumptions as invariants, namely that there is a single query // operation in the file. - const { document, rawSDL: unminifiedGraphql } = only(documents); + const { document, rawSDL: unminifiedGraphQL } = only(documents); const definition = only(nullthrows(document).definitions); invariant( isObject(definition) && @@ -23,10 +27,26 @@ export const plugin: PluginFunction<{}> = function plugin(_schema, documents) { "Expected a query!", ); - const minifiedGraphql = graphqlQueryCompress(nullthrows(unminifiedGraphql)); - const operationName = operationNameZodType.parse(definition.name); + // Extract the operation name from the definition, as well as information + // about its location in the raw, unminified GraphQL. + const { + value: operationName, + loc: { start: operationNameStart, end: operationNameEnd }, + } = operationNameZodType.parse(definition.name); + const operationNameLength = operationNameEnd - operationNameStart; + invariant( + operationName.length === operationNameLength, + "Operation name length mismatch!", + ); - // TODO: strip out the operation name from the minified GraphQL + // Minify the GraphQL we use for the query. + const minifiedGraphQL = graphqlQueryCompress( + spliceString( + nullthrows(unminifiedGraphQL), + operationNameStart, + operationNameLength, + ), + ); return { prepend: [ @@ -42,7 +62,7 @@ export const plugin: PluginFunction<{}> = function plugin(_schema, documents) { export type QueryVariables = Simplify<${operationName}QueryVariables>; export type Query = Simplify<${operationName}Query>; - export const QUERY = ${JSON.stringify(minifiedGraphql)}; + export const QUERY = ${JSON.stringify(minifiedGraphQL)}; export function fetchGraphQL(variables: QueryVariables): Promise { return getGraphQLClient().request(QUERY, variables); diff --git a/workspaces/util/src/spliceString.ts b/workspaces/util/src/spliceString.ts new file mode 100644 index 00000000..e578129c --- /dev/null +++ b/workspaces/util/src/spliceString.ts @@ -0,0 +1,10 @@ +export function spliceString( + s: string, + start: number, + deleteCount: number = Infinity, + ...items: unknown[] +): string { + const chars: unknown[] = [...s]; + chars.splice(start, deleteCount, ...items); + return chars.join(""); +}