diff --git a/e2e-tests/development-runtime/cypress/integration/graphql-typegen/files-existence.js b/e2e-tests/development-runtime/cypress/integration/graphql-typegen/files-existence.js new file mode 100644 index 0000000000000..8174f3946193c --- /dev/null +++ b/e2e-tests/development-runtime/cypress/integration/graphql-typegen/files-existence.js @@ -0,0 +1,76 @@ +// https://docs.cypress.io/api/commands/readfile#Existence +// By default, cy.readFile() asserts that the file exists and will fail if it does not exist. + +const FRAGMENT = `fragment TestingFragment on Site { + siteMetadata { + author + } +}` + +const GRAPHQL_TYPE = `type CheckMePlease { + hello: String! +}` + +const TS_TYPE = `type CheckMePlease = { + readonly hello: Scalars['String']; +};` + +describe(`typecheck`, () => { + it(`passes without an error`, () => { + cy.exec(`npm run typecheck`) + }) +}) + +describe('fragments.graphql', () => { + it('exists in .cache folder', () => { + cy.readFile('.cache/typegen/fragments.graphql') + }) + it('contains test fragment', () => { + cy.readFile('.cache/typegen/fragments.graphql').then((file) => { + expect(file).to.include(FRAGMENT) + }) + }) +}) + +describe('graphql-config', () => { + it('exists in .cache folder with correct data', () => { + cy.readFile('.cache/typegen/graphql.config.json', 'utf-8').then((json) => { + expect(json).to.deep.equal({ + "schema": ".cache/typegen/schema.graphql", + "documents": [ + "src/**/**.{ts,js,tsx,jsx}", + ".cache/typegen/fragments.graphql" + ], + "extensions": { + "endpoints": { + "default": { + "url": "http://localhost:8000/___graphql" + } + } + } + }) + }) + }) +}) + +describe('schema.graphql', () => { + it('exists in .cache folder', () => { + cy.readFile('.cache/typegen/schema.graphql') + }) + it('contains test type', () => { + cy.readFile('.cache/typegen/schema.graphql').then((file) => { + expect(file).to.include(GRAPHQL_TYPE) + }) + }) +}) + +describe('gatsby-types.d.ts', () => { + it('exists in src folder', () => { + cy.readFile('src/gatsby-types.d.ts') + }) + it('contains test type', () => { + cy.readFile('src/gatsby-types.d.ts').then((file) => { + expect(file).to.include(TS_TYPE) + }) + }) +}) \ No newline at end of file diff --git a/e2e-tests/development-runtime/cypress/integration/graphql-typegen/fragments-file.js b/e2e-tests/development-runtime/cypress/integration/graphql-typegen/fragments-file.js deleted file mode 100644 index cb2cb03e3fa2c..0000000000000 --- a/e2e-tests/development-runtime/cypress/integration/graphql-typegen/fragments-file.js +++ /dev/null @@ -1,5 +0,0 @@ -describe('fragments.graphql', () => { - it('exists in .cache folder', () => { - cy.readFile('.cache/typegen/fragments.graphql') - }) -}) \ No newline at end of file diff --git a/e2e-tests/development-runtime/cypress/integration/graphql-typegen/graphql-config.js b/e2e-tests/development-runtime/cypress/integration/graphql-typegen/graphql-config.js deleted file mode 100644 index d292bca5b0762..0000000000000 --- a/e2e-tests/development-runtime/cypress/integration/graphql-typegen/graphql-config.js +++ /dev/null @@ -1,20 +0,0 @@ -describe('graphql-config', () => { - it('exists in .cache folder with correct data', () => { - cy.readFile('.cache/typegen/graphql.config.json', 'utf-8').then((json) => { - expect(json).to.deep.equal({ - "schema": ".cache/typegen/schema.graphql", - "documents": [ - "src/**/**.{ts,js,tsx,jsx}", - ".cache/typegen/fragments.graphql" - ], - "extensions": { - "endpoints": { - "default": { - "url": "http://localhost:8000/___graphql" - } - } - } - }) - }) - }) -}) \ No newline at end of file diff --git a/e2e-tests/development-runtime/cypress/integration/graphql-typegen/schema-file.js b/e2e-tests/development-runtime/cypress/integration/graphql-typegen/schema-file.js deleted file mode 100644 index 3f5bcab12b1d1..0000000000000 --- a/e2e-tests/development-runtime/cypress/integration/graphql-typegen/schema-file.js +++ /dev/null @@ -1,5 +0,0 @@ -describe('schema.graphql', () => { - it('exists in .cache folder', () => { - cy.readFile('.cache/typegen/schema.graphql') - }) -}) \ No newline at end of file diff --git a/e2e-tests/development-runtime/cypress/integration/graphql-typegen/ts-types.js b/e2e-tests/development-runtime/cypress/integration/graphql-typegen/ts-types.js deleted file mode 100644 index 6e9bd4c629808..0000000000000 --- a/e2e-tests/development-runtime/cypress/integration/graphql-typegen/ts-types.js +++ /dev/null @@ -1,5 +0,0 @@ -describe('gatsby-types.d.ts', () => { - it('exists in src folder', () => { - cy.readFile('src/gatsby-types.d.ts') - }) -}) \ No newline at end of file diff --git a/e2e-tests/development-runtime/cypress/integration/graphql-typegen/updates.js b/e2e-tests/development-runtime/cypress/integration/graphql-typegen/updates.js new file mode 100644 index 0000000000000..ca88323ded3b4 --- /dev/null +++ b/e2e-tests/development-runtime/cypress/integration/graphql-typegen/updates.js @@ -0,0 +1,56 @@ +const QUERY_BEFORE = `type GraphQLTypegenQuery = { readonly site: { readonly siteMetadata: { readonly title: string | null } | null } | null };` +const QUERY_AFTER = `type GraphQLTypegenQuery = { readonly site: { readonly siteMetadata: { readonly author: string | null, readonly title: string | null } | null } | null };` +const FRAGMENT_BEFORE = `fragment SiteInformation on Site { + buildTime +}` +const FRAGMENT_AFTER = `fragment SiteInformation on Site { + buildTime + trailingSlash +}` + +beforeEach(() => { + cy.visit(`/graphql-typegen/`).waitForRouteChange() +}) + +after(() => { + cy.exec(`npm run reset`) +}) + +describe(`hot-reloading changes on GraphQL Typegen files`, () => { + it(`contains initial contents in files`, () => { + cy.readFile('src/gatsby-types.d.ts').then((file) => { + expect(file).to.include(QUERY_BEFORE) + }) + cy.readFile('.cache/typegen/fragments.graphql').then((file) => { + expect(file).to.include(FRAGMENT_BEFORE) + }) + }) + + it(`can edit a page query`, () => { + cy.exec( + `npm run update -- --file src/pages/graphql-typegen.tsx --replacements "# %AUTHOR%:author" --exact` + ) + + cy.waitForHmr() + + cy.readFile('src/gatsby-types.d.ts').then((file) => { + expect(file).to.include(QUERY_AFTER) + }) + }) + + it(`can edit a fragment`, () => { + cy.exec( + `npm run update -- --file src/pages/graphql-typegen.tsx --replacements "# %TRAILING_SLASH%:trailingSlash" --exact` + ) + + cy.waitForHmr() + + cy.readFile('.cache/typegen/fragments.graphql').then((file) => { + expect(file).to.include(FRAGMENT_AFTER) + }) + }) + + it(`successfully runs typecheck`, () => { + cy.exec(`npm run typecheck`) + }) +}) diff --git a/e2e-tests/development-runtime/gatsby-config.js b/e2e-tests/development-runtime/gatsby-config.js index 9cc6dc6df1249..9ccc5806d1583 100644 --- a/e2e-tests/development-runtime/gatsby-config.js +++ b/e2e-tests/development-runtime/gatsby-config.js @@ -30,6 +30,7 @@ module.exports = { `gatsby-source-pinc-data`, `gatsby-source-query-on-demand-data`, `gatsby-browser-tsx`, + `gatsby-node-typegen`, `gatsby-transformer-sharp`, `gatsby-transformer-json`, { diff --git a/e2e-tests/development-runtime/package.json b/e2e-tests/development-runtime/package.json index 58c694775e411..3a917d6370960 100644 --- a/e2e-tests/development-runtime/package.json +++ b/e2e-tests/development-runtime/package.json @@ -38,6 +38,7 @@ "develop": "cross-env CYPRESS_SUPPORT=y ENABLE_GATSBY_REFRESH_ENDPOINT=true GATSBY_EXPERIMENTAL_QUERY_ON_DEMAND=y GATSBY_GRAPHQL_TYPEGEN=y gatsby develop", "serve": "gatsby serve", "clean": "gatsby clean", + "typecheck": "tsc --noEmit", "start": "npm run develop", "format": "prettier --write \"src/**/*.js\"", "test": "npm run start-server-and-test || (npm run reset && exit 1)", @@ -64,6 +65,7 @@ "is-ci": "^2.0.0", "prettier": "2.0.4", "start-server-and-test": "^1.7.11", + "typescript": "^4.6.4", "yargs": "^12.0.5" }, "repository": { diff --git a/e2e-tests/development-runtime/plugins/gatsby-node-typegen/gatsby-node.ts b/e2e-tests/development-runtime/plugins/gatsby-node-typegen/gatsby-node.ts new file mode 100644 index 0000000000000..053527f63e331 --- /dev/null +++ b/e2e-tests/development-runtime/plugins/gatsby-node-typegen/gatsby-node.ts @@ -0,0 +1,23 @@ +import { GatsbyNode } from "gatsby" + +export const createSchemaCustomization: GatsbyNode["createSchemaCustomization"] = ({ actions }) => { + const { createTypes } = actions + + createTypes(` + type CheckMePlease { + hello: String! + } + `) +} + +export const createPages: GatsbyNode["createPages"] = async ({ graphql }) => { + await graphql(`#graphql + query GatsbyNode { + site { + siteMetadata { + title + } + } + } + `) +} \ No newline at end of file diff --git a/e2e-tests/development-runtime/plugins/gatsby-node-typegen/package.json b/e2e-tests/development-runtime/plugins/gatsby-node-typegen/package.json new file mode 100644 index 0000000000000..9e26dfeeb6e64 --- /dev/null +++ b/e2e-tests/development-runtime/plugins/gatsby-node-typegen/package.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/e2e-tests/development-runtime/src/pages/graphql-typegen.tsx b/e2e-tests/development-runtime/src/pages/graphql-typegen.tsx new file mode 100644 index 0000000000000..a5b3d8bda42db --- /dev/null +++ b/e2e-tests/development-runtime/src/pages/graphql-typegen.tsx @@ -0,0 +1,27 @@ +import React from "react" +import { graphql, PageProps } from "gatsby" + +function GraphQLTypegen({ data }: PageProps) { + return ( +

+ {data?.site?.siteMetadata?.title} +

+ ) +} + +export const query = graphql` + query GraphQLTypegen{ + site { + siteMetadata { + # %AUTHOR% + title + } + } + } + fragment SiteInformation on Site { + buildTime + # %TRAILING_SLASH% + } +` + +export default GraphQLTypegen diff --git a/e2e-tests/development-runtime/tsconfig.json b/e2e-tests/development-runtime/tsconfig.json new file mode 100644 index 0000000000000..5e20a545d3374 --- /dev/null +++ b/e2e-tests/development-runtime/tsconfig.json @@ -0,0 +1,14 @@ +{ + "compilerOptions": { + "target": "esnext", + "lib": ["dom", "esnext"], + "jsx": "react", + "module": "esnext", + "moduleResolution": "node", + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "strict": true, + "skipLibCheck": true + }, + "include": ["./src/**/*", "./plugins/**/*"] +} diff --git a/packages/gatsby/src/services/graphql-typegen.ts b/packages/gatsby/src/services/graphql-typegen.ts index 61ffc157b0317..5fd8f44087412 100644 --- a/packages/gatsby/src/services/graphql-typegen.ts +++ b/packages/gatsby/src/services/graphql-typegen.ts @@ -1,34 +1,21 @@ -import { EventObject } from "xstate" import { IBuildContext } from "../internal" -import { IDataLayerContext, IQueryRunningContext } from "../state-machines" import { writeGraphQLFragments, writeGraphQLSchema, } from "../utils/graphql-typegen/file-writes" import { writeTypeScriptTypes } from "../utils/graphql-typegen/ts-codegen" -export async function graphQLTypegen( - { - program, - store, - parentSpan, - reporter, - }: IBuildContext | IQueryRunningContext | IDataLayerContext, - _: EventObject, - { - src: { compile }, - }: { - src: { - type: string - compile?: "all" | "schema" | "definitions" - } - } -): Promise { +export async function graphQLTypegen({ + program, + store, + parentSpan, + reporter, +}: IBuildContext): Promise { // TypeScript requires null/undefined checks for these // But this should never happen unless e.g. the state machine doesn't receive this information from a parent state machine - if (!program || !store || !compile || !reporter) { + if (!program || !store || !reporter) { throw new Error( - `Missing required params in graphQLTypegen. program: ${!!program}. store: ${!!store}. compile: ${!!compile}` + `Missing required params in graphQLTypegen. program: ${!!program}. store: ${!!store}.` ) } const directory = program.directory @@ -41,19 +28,11 @@ export async function graphQLTypegen( ) activity.start() - if (compile === `all` || compile === `schema`) { - await writeGraphQLSchema(directory, store) - if (compile === `schema`) { - reporter.verbose(`Re-Generate GraphQL Schema types`) - } - } - if (compile === `all` || compile === `definitions`) { - await writeGraphQLFragments(directory, store) - await writeTypeScriptTypes(directory, store) - if (compile === `definitions`) { - reporter.verbose(`Re-Generate TypeScript types & GraphQL fragments`) - } - } + const { schema, definitions } = store.getState() + + await writeGraphQLSchema(directory, schema) + await writeGraphQLFragments(directory, definitions) + await writeTypeScriptTypes(directory, schema, definitions) activity.end() } diff --git a/packages/gatsby/src/services/listen-for-mutations.ts b/packages/gatsby/src/services/listen-for-mutations.ts index d8fcafb1ab275..cbe0e6fdbda9a 100644 --- a/packages/gatsby/src/services/listen-for-mutations.ts +++ b/packages/gatsby/src/services/listen-for-mutations.ts @@ -18,15 +18,27 @@ export const listenForMutations: InvokeCallback = (callback: Sender) => { callback({ type: `QUERY_RUN_REQUESTED`, payload: event }) } + const emitSetSchema = (event: unknown): void => { + callback({ type: `SET_SCHEMA`, payload: event }) + } + + const emitGraphQLDefinitions = (event: unknown): void => { + callback({ type: `SET_GRAPHQL_DEFINITIONS`, payload: event }) + } + emitter.on(`ENQUEUE_NODE_MUTATION`, emitMutation) emitter.on(`WEBHOOK_RECEIVED`, emitWebhook) emitter.on(`SOURCE_FILE_CHANGED`, emitSourceChange) emitter.on(`QUERY_RUN_REQUESTED`, emitQueryRunRequest) + emitter.on(`SET_SCHEMA`, emitSetSchema) + emitter.on(`SET_GRAPHQL_DEFINITIONS`, emitGraphQLDefinitions) return function unsubscribeFromMutationListening(): void { emitter.off(`ENQUEUE_NODE_MUTATION`, emitMutation) emitter.off(`WEBHOOK_RECEIVED`, emitWebhook) emitter.off(`SOURCE_FILE_CHANGED`, emitSourceChange) emitter.off(`QUERY_RUN_REQUESTED`, emitQueryRunRequest) + emitter.off(`SET_SCHEMA`, emitSetSchema) + emitter.off(`SET_GRAPHQL_DEFINITIONS`, emitGraphQLDefinitions) } } diff --git a/packages/gatsby/src/state-machines/data-layer/index.ts b/packages/gatsby/src/state-machines/data-layer/index.ts index 55180215613b5..121a50b3769dd 100644 --- a/packages/gatsby/src/state-machines/data-layer/index.ts +++ b/packages/gatsby/src/state-machines/data-layer/index.ts @@ -69,26 +69,9 @@ const recreatePagesStates: StatesConfig = { invoke: { id: `building-schema`, src: `buildSchema`, - onDone: [ - { - target: `graphQLTypegen`, - cond: (): boolean => !!process.env.GATSBY_GRAPHQL_TYPEGEN, - }, - { - target: `creatingPages`, - }, - ], - }, - exit: `assignGraphQLRunners`, - }, - graphQLTypegen: { - invoke: { - src: { - type: `graphQLTypegen`, - compile: `schema`, - }, onDone: { target: `creatingPages`, + actions: `assignGraphQLRunners`, }, }, }, diff --git a/packages/gatsby/src/state-machines/data-layer/services.ts b/packages/gatsby/src/state-machines/data-layer/services.ts index e1ca6009f2032..3dd8e34ebf8b5 100644 --- a/packages/gatsby/src/state-machines/data-layer/services.ts +++ b/packages/gatsby/src/state-machines/data-layer/services.ts @@ -5,7 +5,6 @@ import { buildSchema, sourceNodes, writeOutRedirects as writeOutRedirectsAndWatch, - graphQLTypegen, } from "../../services" import { IDataLayerContext } from "./types" @@ -18,5 +17,4 @@ export const dataLayerServices: Record< createPages, buildSchema, writeOutRedirectsAndWatch, - graphQLTypegen, } diff --git a/packages/gatsby/src/state-machines/develop/actions.ts b/packages/gatsby/src/state-machines/develop/actions.ts index ee86bd5132f75..075bc6f7f2b3c 100644 --- a/packages/gatsby/src/state-machines/develop/actions.ts +++ b/packages/gatsby/src/state-machines/develop/actions.ts @@ -16,6 +16,11 @@ import { store } from "../../redux" import { ProgramStatus } from "../../redux/types" import { createWebpackWatcher } from "../../services/listen-to-webpack" import { callRealApi } from "../../utils/call-deferred-api" +import { + writeGraphQLFragments, + writeGraphQLSchema, +} from "../../utils/graphql-typegen/file-writes" +import { writeTypeScriptTypes } from "../../utils/graphql-typegen/ts-codegen" /** * Handler for when we're inside handlers that should be able to mutate nodes * Instead of queueing, we call it right away @@ -193,6 +198,32 @@ export const clearPendingQueryRuns = assign(() => { } }) +export const schemaTypegen: ActionFunction< + IBuildContext, + AnyEventObject +> = async (context, event) => { + const schema = event.payload.payload + const directory = context.program.directory + + context.reporter!.verbose(`Re-Generating schema.graphql`) + + await writeGraphQLSchema(directory, schema) +} + +export const definitionsTypegen: ActionFunction< + IBuildContext, + AnyEventObject +> = async (context, event) => { + const definitions = event.payload.payload + const { schema } = context.store!.getState() + const directory = context.program.directory + + context.reporter!.verbose(`Re-Generating fragments.graphql & TS Types`) + + await writeGraphQLFragments(directory, definitions) + await writeTypeScriptTypes(directory, schema, definitions) +} + export const buildActions: ActionFunctionMap = { callApi, markNodesDirty, @@ -219,4 +250,6 @@ export const buildActions: ActionFunctionMap = { logError, trackRequestedQueryRun, clearPendingQueryRuns, + schemaTypegen, + definitionsTypegen, } diff --git a/packages/gatsby/src/state-machines/develop/index.ts b/packages/gatsby/src/state-machines/develop/index.ts index b9cb47dbe93a5..53b04cfe2249c 100644 --- a/packages/gatsby/src/state-machines/develop/index.ts +++ b/packages/gatsby/src/state-machines/develop/index.ts @@ -40,6 +40,16 @@ const developConfig: MachineConfig = { QUERY_RUN_REQUESTED: { actions: `trackRequestedQueryRun`, }, + SET_SCHEMA: { + actions: `schemaTypegen`, + cond: (ctx: IBuildContext): boolean => + !!process.env.GATSBY_GRAPHQL_TYPEGEN && !ctx.shouldRunInitialTypegen, + }, + SET_GRAPHQL_DEFINITIONS: { + actions: `definitionsTypegen`, + cond: (ctx: IBuildContext): boolean => + !!process.env.GATSBY_GRAPHQL_TYPEGEN && !ctx.shouldRunInitialTypegen, + }, }, states: { // Here we handle the initial bootstrap @@ -136,7 +146,6 @@ const developConfig: MachineConfig = { graphqlRunner, websocketManager, pendingQueryRuns, - shouldRunInitialTypegen, reporter, }: IBuildContext): IQueryRunningContext => { return { @@ -147,7 +156,6 @@ const developConfig: MachineConfig = { graphqlRunner, websocketManager, pendingQueryRuns, - shouldRunInitialTypegen, reporter, } }, @@ -224,7 +232,7 @@ const developConfig: MachineConfig = { src: `startWebpackServer`, onDone: [ { - target: `graphQLTypegen`, + target: `initialGraphQLTypegen`, cond: (): boolean => !!process.env.GATSBY_GRAPHQL_TYPEGEN, }, { @@ -238,12 +246,9 @@ const developConfig: MachineConfig = { }, exit: [`assignServers`, `spawnWebpackListener`, `markSourceFilesClean`], }, - graphQLTypegen: { + initialGraphQLTypegen: { invoke: { - src: { - type: `graphQLTypegen`, - compile: `all`, - }, + src: `graphQLTypegen`, onDone: { target: `waiting`, }, diff --git a/packages/gatsby/src/state-machines/query-running/index.ts b/packages/gatsby/src/state-machines/query-running/index.ts index 513fcdcbaf0f2..d39409a5841d8 100644 --- a/packages/gatsby/src/state-machines/query-running/index.ts +++ b/packages/gatsby/src/state-machines/query-running/index.ts @@ -26,25 +26,6 @@ export const queryStates: MachineConfig = { invoke: { id: `extracting-queries`, src: `extractQueries`, - onDone: [ - { - target: `graphQLTypegen`, - cond: (ctx): boolean => - !!process.env.GATSBY_GRAPHQL_TYPEGEN && - !ctx.shouldRunInitialTypegen, - }, - { - target: `waitingPendingQueries`, - }, - ], - }, - }, - graphQLTypegen: { - invoke: { - src: { - type: `graphQLTypegen`, - compile: `definitions`, - }, onDone: { target: `waitingPendingQueries`, }, diff --git a/packages/gatsby/src/state-machines/query-running/services.ts b/packages/gatsby/src/state-machines/query-running/services.ts index 7a5230abae330..499e6898aa143 100644 --- a/packages/gatsby/src/state-machines/query-running/services.ts +++ b/packages/gatsby/src/state-machines/query-running/services.ts @@ -7,7 +7,6 @@ import { runPageQueries, waitUntilAllJobsComplete, writeOutRedirects, - graphQLTypegen, } from "../../services" import { IQueryRunningContext } from "./types" @@ -22,5 +21,4 @@ export const queryRunningServices: Record< runPageQueries, waitUntilAllJobsComplete, writeOutRedirects, - graphQLTypegen, } diff --git a/packages/gatsby/src/state-machines/query-running/types.ts b/packages/gatsby/src/state-machines/query-running/types.ts index f79114084b756..e7ee8420ed7bd 100644 --- a/packages/gatsby/src/state-machines/query-running/types.ts +++ b/packages/gatsby/src/state-machines/query-running/types.ts @@ -12,7 +12,6 @@ type Reporter = typeof reporter export interface IQueryRunningContext { reporter?: Reporter - shouldRunInitialTypegen?: boolean program?: IProgram store?: Store parentSpan?: Span diff --git a/packages/gatsby/src/utils/graphql-typegen/file-writes.ts b/packages/gatsby/src/utils/graphql-typegen/file-writes.ts index fa6455f468140..459552d07b2d3 100644 --- a/packages/gatsby/src/utils/graphql-typegen/file-writes.ts +++ b/packages/gatsby/src/utils/graphql-typegen/file-writes.ts @@ -1,9 +1,8 @@ import * as fs from "fs-extra" import { join } from "path" -import { printSchema } from "graphql" -import { Store, AnyAction } from "redux" +import { GraphQLSchema, printSchema } from "graphql" import reporter from "gatsby-cli/lib/reporter" -import type { IGatsbyState, IStateProgram } from "../../redux/types" +import type { IDefinitionMeta, IStateProgram } from "../../redux/types" import { stabilizeSchema } from "./utils" const OUTPUT_PATHS = { @@ -17,6 +16,13 @@ export async function writeGraphQLConfig( ): Promise { try { const base = program.directory + const outputPath = join(base, OUTPUT_PATHS.config) + + if (fs.existsSync(outputPath)) { + reporter.verbose(`graphql.config.json already exists. Skipping...`) + return + } + const configJSONString = JSON.stringify( { schema: OUTPUT_PATHS.schema, @@ -35,8 +41,6 @@ export async function writeGraphQLConfig( 2 ) - const outputPath = join(base, OUTPUT_PATHS.config) - await fs.outputFile(outputPath, configJSONString) reporter.verbose(`Successfully created graphql.config.json`) } catch (err) { @@ -46,12 +50,10 @@ export async function writeGraphQLConfig( export async function writeGraphQLFragments( directory: IStateProgram["directory"], - store: Store + definitions: Map ): Promise { try { - const currentDefinitions = store.getState().definitions - - const fragmentString = Array.from(currentDefinitions.entries()) + const fragmentString = Array.from(definitions.entries()) .filter(([_, def]) => def.isFragment) .map(([_, def]) => `# ${def.filePath}\n${def.printedAst}`) .join(`\n`) @@ -65,10 +67,9 @@ export async function writeGraphQLFragments( export async function writeGraphQLSchema( directory: IStateProgram["directory"], - store: Store + schema: GraphQLSchema ): Promise { try { - const { schema } = store.getState() const schemaSDLString = printSchema(stabilizeSchema(schema), { commentDescriptions: true, }) diff --git a/packages/gatsby/src/utils/graphql-typegen/ts-codegen.ts b/packages/gatsby/src/utils/graphql-typegen/ts-codegen.ts index 0800a4d3960a2..d05c6e1479dbd 100644 --- a/packages/gatsby/src/utils/graphql-typegen/ts-codegen.ts +++ b/packages/gatsby/src/utils/graphql-typegen/ts-codegen.ts @@ -1,14 +1,13 @@ import * as fs from "fs-extra" import { join } from "path" import { codegen } from "@graphql-codegen/core" -import { Kind } from "graphql" +import { GraphQLSchema, Kind } from "graphql" import type { Types } from "@graphql-codegen/plugin-helpers" import type { TypeScriptPluginConfig } from "@graphql-codegen/typescript/config" import type { TypeScriptDocumentsPluginConfig } from "@graphql-codegen/typescript-operations/config" -import { AnyAction, Store } from "redux" import { CodeFileLoader } from "@graphql-tools/code-file-loader" import { loadDocuments } from "@graphql-tools/load" -import { IGatsbyState, IStateProgram } from "../../redux/types" +import { IDefinitionMeta, IStateProgram } from "../../redux/types" import { filterTargetDefinitions, stabilizeSchema } from "./utils" const OUTPUT_PATH = `src/gatsby-types.d.ts` @@ -22,7 +21,7 @@ const DEFAULT_TYPESCRIPT_CONFIG: Readonly = { // Types come from the data layer so they can't be modified immutableTypes: true, // TODO: Better maybeValue - maybeValue: `T | undefined`, + maybeValue: `T | null`, // We'll want to re-export ourselves noExport: true, // Recommended for .d.ts files @@ -43,7 +42,8 @@ const DEFAULT_TYPESCRIPT_OPERATIONS_CONFIG: Readonly + schema: GraphQLSchema, + definitions: Map ): Promise { const pluginConfig: Pick = { pluginMap: { @@ -85,18 +85,24 @@ export async function writeTypeScriptTypes( ], } - const { schema, definitions } = store.getState() const filename = join(directory, OUTPUT_PATH) let gatsbyNodeDocuments: Array = [] // The loadDocuments + CodeFileLoader looks for graphql(``) functions inside the gatsby-node.ts files // And then extracts the queries into documents - // The behavior can be modified: https://www.graphql-tools.com/docs/graphql-tag-pluck + // TODO: This codepath can be made obsolete if Gatsby itself already places the queries inside gatsby-node into the `definitions` try { gatsbyNodeDocuments = await loadDocuments( [`./gatsby-node.ts`, `./plugins/**/gatsby-node.ts`], { - loaders: [new CodeFileLoader()], + loaders: [ + new CodeFileLoader({ + // Configures https://www.graphql-tools.com/docs/graphql-tag-pluck to only check graphql function from Gatsby + pluckConfig: { + modules: [{ name: `gatsby`, identifier: `graphql` }], + }, + }), + ], } ) } catch (e) {