diff --git a/packages/gatsby-cli/src/structured-errors/error-map.ts b/packages/gatsby-cli/src/structured-errors/error-map.ts index 986078c9595b3..9a7f35ab53403 100644 --- a/packages/gatsby-cli/src/structured-errors/error-map.ts +++ b/packages/gatsby-cli/src/structured-errors/error-map.ts @@ -262,21 +262,21 @@ const errors = { ${context.sourceMessage} This can happen if you e.g. accidentally added { } to the field "${context.fieldName}". If you didn't expect "${context.fieldName}" to be of type "${context.fieldType}" make sure that your input source and/or plugin is correct. - However, if you expect "value" to exist, the field might be accessible in another subfield. Please try your query in GraphiQL and use the GraphiQL explorer to see which fields you can query and what shape they have. + However, if you expect "value" to exist, the field might be accessible in another subfield. Please try your query in GraphiQL. - It is recommended to explicitly type your GraphQL schema if you want to use optional fields. This way you don't have to add the mentioned - "dummy content". Visit our docs to learn how you can define the schema for "${context.type}": - https://www.gatsbyjs.com/docs/reference/graphql-data-layer/schema-customization#creating-type-definitions`, + It is recommended to explicitly type your GraphQL schema if you want to use optional fields.`, type: Type.GRAPHQL, level: Level.ERROR, category: ErrorCategory.USER, + docsUrl: `https://gatsby.dev/creating-type-definitions`, }, "85923": { text: (context): string => - `There was an error in your GraphQL query:\n\n${context.sourceMessage}\n\nIf you don't expect "${context.field}" to exist on the type "${context.type}" it is most likely a typo.\nHowever, if you expect "${context.field}" to exist there are a couple of solutions to common problems:\n\n- If you added a new data source and/or changed something inside gatsby-node.js/gatsby-config.js, please try a restart of your development server\n- The field might be accessible in another subfield, please try your query in GraphiQL and use the GraphiQL explorer to see which fields you can query and what shape they have\n- You want to optionally use your field "${context.field}" and right now it is not used anywhere. Therefore Gatsby can't infer the type and add it to the GraphQL schema. A quick fix is to add at least one entry with that field ("dummy content")\n\nIt is recommended to explicitly type your GraphQL schema if you want to use optional fields. This way you don't have to add the mentioned "dummy content". Visit our docs to learn how you can define the schema for "${context.type}":\nhttps://www.gatsbyjs.com/docs/reference/graphql-data-layer/schema-customization#creating-type-definitions`, + `There was an error in your GraphQL query:\n\n${context.sourceMessage}\n\nIf you don't expect "${context.field}" to exist on the type "${context.type}" it is most likely a typo. However, if you expect "${context.field}" to exist there are a couple of solutions to common problems:\n\n- If you added a new data source and/or changed something inside gatsby-node/gatsby-config, please try a restart of your development server.\n- You want to optionally use your field "${context.field}" and right now it is not used anywhere.\n\nIt is recommended to explicitly type your GraphQL schema if you want to use optional fields.`, type: Type.GRAPHQL, level: Level.ERROR, category: ErrorCategory.USER, + docsUrl: `https://gatsby.dev/creating-type-definitions`, }, "85924": { text: (context): string => @@ -713,6 +713,16 @@ const errors = { type: Type.COMPILATION, category: ErrorCategory.USER, }, + "12100": { + text: ( + context + ): string => `There was an error while trying to generate TS types from your GraphQL queries: + + ${context.sourceMessage}`, + level: Level.ERROR, + type: Type.GRAPHQL, + category: ErrorCategory.USER, + }, } export type ErrorId = string | keyof typeof errors diff --git a/packages/gatsby-plugin-image/package.json b/packages/gatsby-plugin-image/package.json index ee43734e7ef68..25bbd451d95fc 100644 --- a/packages/gatsby-plugin-image/package.json +++ b/packages/gatsby-plugin-image/package.json @@ -83,6 +83,7 @@ "common-tags": "^1.8.2", "fs-extra": "^10.1.0", "gatsby-core-utils": "^3.15.0-next.1", + "gatsby-plugin-utils": "^3.9.0-next.2", "objectFitPolyfill": "^2.3.5", "prop-types": "^15.8.1" }, diff --git a/packages/gatsby-plugin-image/src/resolver-utils.ts b/packages/gatsby-plugin-image/src/resolver-utils.ts index 76686fd7c8523..513b6daacca9d 100644 --- a/packages/gatsby-plugin-image/src/resolver-utils.ts +++ b/packages/gatsby-plugin-image/src/resolver-utils.ts @@ -5,6 +5,7 @@ import type { ObjectTypeComposerFieldConfigAsObjectDefinition, ObjectTypeComposerArgumentConfigMapDefinition, } from "graphql-compose" +import { hasFeature } from "gatsby-plugin-utils" import type { ISharpGatsbyImageArgs, IImageSizeArgs } from "./image-utils" export const ImageFormatType: EnumTypeComposerAsObjectDefinition = { @@ -74,7 +75,7 @@ export function getGatsbyImageResolver( IGatsbyImageResolverArgs & TArgs > { return { - type: `JSON!`, + type: hasFeature(`graphql-typegen`) ? `GatsbyImageData!` : `JSON!`, args: { layout: { type: ImageLayoutType.name, @@ -166,7 +167,7 @@ export function getGatsbyImageFieldConfig( IGatsbyImageFieldArgs & TArgs > { return { - type: `JSON!`, + type: hasFeature(`graphql-typegen`) ? `GatsbyImageData!` : `JSON!`, args: { layout: { type: ImageLayoutType.name, diff --git a/packages/gatsby-plugin-utils/src/polyfill-remote-file/graphql/gatsby-image-resolver.ts b/packages/gatsby-plugin-utils/src/polyfill-remote-file/graphql/gatsby-image-resolver.ts index 3f99aaf310ccb..ca2b18d3038f0 100644 --- a/packages/gatsby-plugin-utils/src/polyfill-remote-file/graphql/gatsby-image-resolver.ts +++ b/packages/gatsby-plugin-utils/src/polyfill-remote-file/graphql/gatsby-image-resolver.ts @@ -1,3 +1,4 @@ +import { hasFeature } from "../../has-feature" import { generateImageUrl } from "../utils/url-generator" import { getImageFormatFromMimeType } from "../utils/mime-type-helpers" import { stripIndent } from "../utils/strip-indent" @@ -260,7 +261,7 @@ export function generateGatsbyImageFieldConfig( IGatsbyImageDataArgs > { return { - type: `JSON`, + type: hasFeature(`graphql-typegen`) ? `GatsbyImageData!` : `JSON`, description: `Data used in the component. See https://gatsby.dev/img for more info.`, args: { layout: { diff --git a/packages/gatsby-plugin-utils/src/types.ts b/packages/gatsby-plugin-utils/src/types.ts index 0c98ccc5b3e6d..f60d24fa403fe 100644 --- a/packages/gatsby-plugin-utils/src/types.ts +++ b/packages/gatsby-plugin-utils/src/types.ts @@ -3,7 +3,7 @@ * These types are duplicated from packages/gatsby/src/bootstrap/load-plugins/types.ts * If you edit this file, make sure to edit that file too!!! * They are duplicate to avoid a circular dependency between gatsby-plugin-utils <=> gatsby <=> gatsby-plugin-utils - * See gatsbyjs/gatsby#27578 and ping @laurieontech or @mxstbr if you have any questions + * See gatsbyjs/gatsby#27578 */ export interface IRawSiteConfig { diff --git a/packages/gatsby-transformer-sharp/package.json b/packages/gatsby-transformer-sharp/package.json index 0040908048c2d..a09bb507bdf72 100644 --- a/packages/gatsby-transformer-sharp/package.json +++ b/packages/gatsby-transformer-sharp/package.json @@ -12,6 +12,7 @@ "bluebird": "^3.7.2", "common-tags": "^1.8.2", "fs-extra": "^10.1.0", + "gatsby-plugin-utils": "^3.9.0-next.2", "probe-image-size": "^7.2.3", "semver": "^7.3.7", "sharp": "^0.30.3" diff --git a/packages/gatsby-transformer-sharp/src/customize-schema.js b/packages/gatsby-transformer-sharp/src/customize-schema.js index 455ad706a3210..9179a8967b9f3 100644 --- a/packages/gatsby-transformer-sharp/src/customize-schema.js +++ b/packages/gatsby-transformer-sharp/src/customize-schema.js @@ -17,6 +17,7 @@ const { traceSVG, generateImageData, } = require(`gatsby-plugin-sharp`) +const { hasFeature } = require(`gatsby-plugin-utils`) const sharp = require(`./safe-sharp`) const fs = require(`fs-extra`) @@ -407,7 +408,9 @@ const imageNodeType = ({ cache, }) => { return { - type: new GraphQLNonNull(GraphQLJSON), + type: hasFeature(`graphql-typegen`) + ? `GatsbyImageData!` + : new GraphQLNonNull(GraphQLJSON), args: { layout: { type: ImageLayoutType, diff --git a/packages/gatsby/index.d.ts b/packages/gatsby/index.d.ts index a2c4665f40b2d..02e229db21feb 100644 --- a/packages/gatsby/index.d.ts +++ b/packages/gatsby/index.d.ts @@ -17,7 +17,7 @@ import { GraphQLOutputType } from "graphql" import { PluginOptionsSchemaJoi, ObjectSchema } from "gatsby-plugin-utils" import { IncomingMessage, ServerResponse } from "http" -export type AvailableFeatures = "image-cdn" +export type AvailableFeatures = "image-cdn" | "graphql-typegen" export { default as Link, diff --git a/packages/gatsby/scripts/__tests__/api.js b/packages/gatsby/scripts/__tests__/api.js index 95a821dfba780..6640eb77218c4 100644 --- a/packages/gatsby/scripts/__tests__/api.js +++ b/packages/gatsby/scripts/__tests__/api.js @@ -32,6 +32,7 @@ it("generates the expected api output", done => { }, "features": Array [ "image-cdn", + "graphql-typegen", ], "node": Object { "createPages": Object {}, diff --git a/packages/gatsby/scripts/output-api-file.js b/packages/gatsby/scripts/output-api-file.js index 6e09e74727574..f2b8f7638ec7b 100644 --- a/packages/gatsby/scripts/output-api-file.js +++ b/packages/gatsby/scripts/output-api-file.js @@ -41,7 +41,7 @@ async function outputFile() { }, {}) /** @type {Array} */ - output.features = ["image-cdn"]; + output.features = ["image-cdn", "graphql-typegen"]; return fs.writeFile( path.resolve(OUTPUT_FILE_NAME), diff --git a/packages/gatsby/src/schema/__tests__/__snapshots__/print.js.snap b/packages/gatsby/src/schema/__tests__/__snapshots__/print.js.snap index 4bf8925958da5..0d7036a724498 100644 --- a/packages/gatsby/src/schema/__tests__/__snapshots__/print.js.snap +++ b/packages/gatsby/src/schema/__tests__/__snapshots__/print.js.snap @@ -193,7 +193,7 @@ interface RemoteFile { fit: RemoteFileFit = COVER cropFocus: [RemoteFileCropFocus] quality: Int = 75 - ): JSON + ): GatsbyImageData! } type File implements Node @dontInfer { @@ -582,7 +582,7 @@ interface RemoteFile { fit: RemoteFileFit = COVER cropFocus: [RemoteFileCropFocus] quality: Int = 75 - ): JSON + ): GatsbyImageData! } type File implements Node @dontInfer { @@ -981,7 +981,7 @@ interface RemoteFile { fit: RemoteFileFit = COVER cropFocus: [RemoteFileCropFocus] quality: Int = 75 - ): JSON + ): GatsbyImageData! } type File implements Node @dontInfer { diff --git a/packages/gatsby/src/schema/schema-composer.ts b/packages/gatsby/src/schema/schema-composer.ts index db3e36a9d00c9..864ad96ba3be0 100644 --- a/packages/gatsby/src/schema/schema-composer.ts +++ b/packages/gatsby/src/schema/schema-composer.ts @@ -2,6 +2,7 @@ import { SchemaComposer, GraphQLJSON } from "graphql-compose" import { addDirectives, GraphQLFieldExtensionDefinition } from "./extensions" import { GraphQLDate } from "./types/date" import { IGatsbyResolverContext } from "./type-definitions" +import { GatsbyImageDataScalar } from "./types/media" import { getNodeInterface } from "./types/node-interface" import { getOrCreateRemoteFileInterface } from "./types/remote-file-interface" @@ -19,6 +20,7 @@ export const createSchemaComposer = ({ schemaComposer.add(GraphQLDate) schemaComposer.add(GraphQLJSON) + schemaComposer.add(GatsbyImageDataScalar) addDirectives({ schemaComposer, fieldExtensions }) return schemaComposer } diff --git a/packages/gatsby/src/schema/types/built-in-types.ts b/packages/gatsby/src/schema/types/built-in-types.ts index a1261bd4c75eb..2fd4c7a6adafd 100644 --- a/packages/gatsby/src/schema/types/built-in-types.ts +++ b/packages/gatsby/src/schema/types/built-in-types.ts @@ -156,6 +156,7 @@ export const builtInScalarTypeNames = [ `Int`, `JSON`, `String`, + `GatsbyImageData`, ] export const internalTypeNames = [ diff --git a/packages/gatsby/src/schema/types/media.ts b/packages/gatsby/src/schema/types/media.ts new file mode 100644 index 0000000000000..f16eee3330604 --- /dev/null +++ b/packages/gatsby/src/schema/types/media.ts @@ -0,0 +1,5 @@ +import { GraphQLScalarType } from "graphql" + +export const GatsbyImageDataScalar = new GraphQLScalarType({ + name: `GatsbyImageData`, +}) diff --git a/packages/gatsby/src/services/graphql-typegen.ts b/packages/gatsby/src/services/graphql-typegen.ts index 5fd8f44087412..3fa4a0a09876f 100644 --- a/packages/gatsby/src/services/graphql-typegen.ts +++ b/packages/gatsby/src/services/graphql-typegen.ts @@ -30,9 +30,18 @@ export async function graphQLTypegen({ const { schema, definitions } = store.getState() - await writeGraphQLSchema(directory, schema) - await writeGraphQLFragments(directory, definitions) - await writeTypeScriptTypes(directory, schema, definitions) + try { + await writeGraphQLSchema(directory, schema) + await writeGraphQLFragments(directory, definitions) + await writeTypeScriptTypes(directory, schema, definitions) + } catch (err) { + activity.panicOnBuild({ + id: `12100`, + context: { + sourceMessage: err, + }, + }) + } activity.end() } diff --git a/packages/gatsby/src/state-machines/develop/actions.ts b/packages/gatsby/src/state-machines/develop/actions.ts index 075bc6f7f2b3c..7fb2be7b02c58 100644 --- a/packages/gatsby/src/state-machines/develop/actions.ts +++ b/packages/gatsby/src/state-machines/develop/actions.ts @@ -207,7 +207,16 @@ export const schemaTypegen: ActionFunction< context.reporter!.verbose(`Re-Generating schema.graphql`) - await writeGraphQLSchema(directory, schema) + try { + await writeGraphQLSchema(directory, schema) + } catch (err) { + context.reporter!.panicOnBuild({ + id: `12100`, + context: { + sourceMessage: err, + }, + }) + } } export const definitionsTypegen: ActionFunction< @@ -220,8 +229,17 @@ export const definitionsTypegen: ActionFunction< context.reporter!.verbose(`Re-Generating fragments.graphql & TS Types`) - await writeGraphQLFragments(directory, definitions) - await writeTypeScriptTypes(directory, schema, definitions) + try { + await writeGraphQLFragments(directory, definitions) + await writeTypeScriptTypes(directory, schema, definitions) + } catch (err) { + context.reporter!.panicOnBuild({ + id: `12100`, + context: { + sourceMessage: err, + }, + }) + } } export const buildActions: ActionFunctionMap = { diff --git a/packages/gatsby/src/utils/flags.ts b/packages/gatsby/src/utils/flags.ts index fce3ec57b2f81..9a0f6f1aa8aeb 100644 --- a/packages/gatsby/src/utils/flags.ts +++ b/packages/gatsby/src/utils/flags.ts @@ -240,8 +240,8 @@ const activeFlags: Array = [ env: `GATSBY_GRAPHQL_TYPEGEN`, command: `develop`, telemetryId: `GraphQLTypegen`, - description: `A built-in way for automatic TypeScript type generation and better GraphQL IntelliSense. See https://github.com/gatsbyjs/gatsby/discussions/35420 for more details.`, - experimental: true, + description: `More easily incorporate content into your pages through automatic TypeScript type generation and better GraphQL IntelliSense. See https://github.com/gatsbyjs/gatsby/discussions/35420 for more details.`, + experimental: false, noCI: true, testFitness: (): fitnessEnum => true, }, diff --git a/packages/gatsby/src/utils/graphql-typegen/ts-codegen.ts b/packages/gatsby/src/utils/graphql-typegen/ts-codegen.ts index d05c6e1479dbd..f40b5030aefbb 100644 --- a/packages/gatsby/src/utils/graphql-typegen/ts-codegen.ts +++ b/packages/gatsby/src/utils/graphql-typegen/ts-codegen.ts @@ -29,6 +29,7 @@ const DEFAULT_TYPESCRIPT_CONFIG: Readonly = { scalars: { Date: `string`, JSON: `Record`, + GatsbyImageData: `import('gatsby-plugin-image').IGatsbyImageData`, }, // import type {} syntax is nicer useTypeImports: true, @@ -121,8 +122,6 @@ export async function writeTypeScriptTypes( } }) - const isVerbose = process.env.gatsby_log_level === `verbose` - const codegenOptions: Omit = { // @ts-ignore - Incorrect types schema: undefined, @@ -139,7 +138,6 @@ export async function writeTypeScriptTypes( skipTypename: true, flattenGeneratedTypes: true, }, - skipDocumentsValidation: !isVerbose, } const result = await codegen({