@graphql-tools/jest-transform@3.1.0
Minor Changes
-
#8605
ea2603aThanks @ardatan! -@graphql-tools/webpack-loader(and@graphql-tools/jest-transform, which calls it) now accepts more than one GraphQL operation in the same file and exports each named operation, matchinggraphql-tag/loader.A file with two mutations used to throw
GraphQL Webpack Loader allows only for one GraphQL Operation per file, so this import failed in webpack and Jest:# menuMutations.graphql mutation deleteMenuMutation { deleteMenu { ...MenuFields } } mutation upsertMenuMutation { upsertMenu { id } } fragment MenuFields on Menu { id name }
import { deleteMenuMutation, upsertMenuMutation } from './menuMutations.graphql'
The default export is still the whole document. Each named export is that operation plus the fragments it uses.
deleteMenuMutationincludesMenuFields;upsertMenuMutationdoes not.import menuMutations, { deleteMenuMutation, upsertMenuMutation, } from './menuMutations.graphql'
A single named operation is also exported under its name. Unnamed operations are still allowed when the file has only one operation. If the file has more than one operation, each operation must be named.
jest-transformnow passes the Jest source path and filesystem into the loader, soesModule: truecan emit named exports for operations and fragments pulled in with#import. CommonJS named exports use an own property so a GraphQL name of__proto__does not replace the export object's prototype.
Patch Changes
- Updated dependencies [
ea2603a]:- @graphql-tools/webpack-loader@8.1.0
@graphql-tools/load@9.1.0
Minor Changes
-
#8603
62ef681Thanks @ardatan! - Custom loaders onloadDocuments/loadDocumentsSync/loadSchemacan now return an array. Each item is added as its own source.A pointer such as
src/**/*.{ts,tsx}calls the custom loader once. Returning a single combined document kept every operation on that one pointer. Returning an array was ignored, so the load failed withCouldn't find any GraphQL type definitions.// custom-loader.js const { parse } = require('graphql'); module.exports = function () { return [ { document: parse(/* GraphQL */ ` query Users { users { id } } `), location: 'users.graphql', }, { document: parse(/* GraphQL */ ` query Orders { orders { id } } `), location: 'orders.graphql', }, ]; };
loadDocuments( { 'src/**/*.{ts,tsx}': { loader: './custom-loader.js', }, }, { loaders: [] }, );
The result is two sources:
Usersatusers.graphqlandOrdersatorders.graphql. A singleDocumentNodeor{ document }return value still becomes one source. An array ofDocumentNodes uses the pointer as each item's location. -
#8606
6da2904Thanks @ardatan! -loadDocuments/loadTypedefsnow accepterrorHandlingso a single invalid file does not have to hide the rest of the load.A glob that mixes a valid operation with broken SDL used to throw the first
Syntax Errorand drop every other file:# valid.query.graphql query ValidQuery { a }
# broken-a.query.graphql query { a
await loadDocuments('**/*.query.graphql', { loaders: [new GraphQLFileLoader()], }); // throws Syntax Error
errorHandlingchooses what happens next. The default is unchanged (throwthe first error).aggregatewaits until every source is attempted, then throwsAggregateErrorwith each parse/load failure:await loadDocuments('**/*.query.graphql', { loaders: [new GraphQLFileLoader()], errorHandling: 'aggregate', });
returndoes not throw. Failed sources stay in the result as{ location, error }without adocument, next to the sources that parsed:const sources = await loadDocuments('**/*.query.graphql', { loaders: [new GraphQLFileLoader(), new CodeFileLoader()], errorHandling: 'return', }); sources[0].document; // ValidQuery sources[1].document; // undefined sources[1].error; // GraphQLError: Syntax Error
Sourcenow has an optionalerrorfield.@graphql-tools/graphql-file-loaderand@graphql-tools/code-file-loaderhonor the same option.CodeFileLoader's existingnoSilentErrorsdefault is unchanged unlesserrorHandlingis set.Custom loader failures and incomplete inline documents (
query Foo {) follow the same option.errorHandling: 'throw'raises the first failure, notAggregateError. -
#8602
46039a0Thanks @ardatan! -loadSchemaandloadSchemaSyncalready acceptsort: true. That always runs GraphQL'slexicographicSortSchema, which reorders the type list and also reorders fields, arguments, and enum values.A schema that was written with a meaningful enum or field order cannot keep that order after load:
# loaded schema enum DayOfWeek { SUNDAY MONDAY TUESDAY WEDNESDAY THURSDAY FRIDAY SATURDAY } type Query { d: String z: String a: String day: DayOfWeek } type User { aa: String a: String } type A { s: String b: String }
# sort: true (current default) type A { b: String s: String } enum DayOfWeek { FRIDAY MONDAY SATURDAY SUNDAY THURSDAY TUESDAY WEDNESDAY } type Query { a: String d: String day: DayOfWeek z: String } type User { a: String aa: String }
This release adds
sortModeonLoadSchemaOptions.sort: truewith nosortMode, orsortMode: 'lexicographic', keeps the current behavior.sort: true, sortMode: 'typesOnly'sorts only the type list. Field and enum value order stay as they were loaded. If the same type is split across files, those members keep merge order instead of being sorted.sort: false(or omitted) ignoressortMode.
loadSchema('schema.graphql', { loaders: [new GraphQLFileLoader()], sort: true, sortMode: 'typesOnly', });
# sort: true, sortMode: 'typesOnly' type A { s: String b: String } enum DayOfWeek { SUNDAY MONDAY TUESDAY WEDNESDAY THURSDAY FRIDAY SATURDAY } type Query { d: String z: String a: String day: DayOfWeek } type User { aa: String a: String }
Patch Changes
-
#8604
9df67b4Thanks @ardatan! -useCustomLoaderanduseCustomLoaderSyncnow include the original load failure in the thrown error message.A missing, broken, or non-function custom loader used to throw only:
Failed to load custom loader: ./codegen-loader.jsThe underlying
SyntaxError,Cannot find module, or "did not export a function" was swallowed, so codegen andloadDocumentscould not show why the file failed.// broken-loader.js module.exports = function ( {
await useCustomLoader('./broken-loader.js', cwd);
Failed to load custom loader: ./broken-loader.js SyntaxError: Unexpected token '{'getCustomLoaderByPathstill returnsnullwhen the file cannot be loaded. -
Updated dependencies [
6da2904,902d870,7770067,f956bfe]:- @graphql-tools/utils@13.2.0
- @graphql-tools/schema@11.0.7
@graphql-tools/code-file-loader@9.1.0
Minor Changes
-
#8606
6da2904Thanks @ardatan! -loadDocuments/loadTypedefsnow accepterrorHandlingso a single invalid file does not have to hide the rest of the load.A glob that mixes a valid operation with broken SDL used to throw the first
Syntax Errorand drop every other file:# valid.query.graphql query ValidQuery { a }
# broken-a.query.graphql query { a
await loadDocuments('**/*.query.graphql', { loaders: [new GraphQLFileLoader()], }); // throws Syntax Error
errorHandlingchooses what happens next. The default is unchanged (throwthe first error).aggregatewaits until every source is attempted, then throwsAggregateErrorwith each parse/load failure:await loadDocuments('**/*.query.graphql', { loaders: [new GraphQLFileLoader()], errorHandling: 'aggregate', });
returndoes not throw. Failed sources stay in the result as{ location, error }without adocument, next to the sources that parsed:const sources = await loadDocuments('**/*.query.graphql', { loaders: [new GraphQLFileLoader(), new CodeFileLoader()], errorHandling: 'return', }); sources[0].document; // ValidQuery sources[1].document; // undefined sources[1].error; // GraphQLError: Syntax Error
Sourcenow has an optionalerrorfield.@graphql-tools/graphql-file-loaderand@graphql-tools/code-file-loaderhonor the same option.CodeFileLoader's existingnoSilentErrorsdefault is unchanged unlesserrorHandlingis set.Custom loader failures and incomplete inline documents (
query Foo {) follow the same option.errorHandling: 'throw'raises the first failure, notAggregateError.
Patch Changes
- Updated dependencies [
6da2904,7770067,f956bfe]:- @graphql-tools/utils@13.2.0
- @graphql-tools/graphql-tag-pluck@9.0.8
- @graphql-tools/load-files@8.0.7
@graphql-tools/graphql-file-loader@9.1.0
Minor Changes
-
#8606
6da2904Thanks @ardatan! -loadDocuments/loadTypedefsnow accepterrorHandlingso a single invalid file does not have to hide the rest of the load.A glob that mixes a valid operation with broken SDL used to throw the first
Syntax Errorand drop every other file:# valid.query.graphql query ValidQuery { a }
# broken-a.query.graphql query { a
await loadDocuments('**/*.query.graphql', { loaders: [new GraphQLFileLoader()], }); // throws Syntax Error
errorHandlingchooses what happens next. The default is unchanged (throwthe first error).aggregatewaits until every source is attempted, then throwsAggregateErrorwith each parse/load failure:await loadDocuments('**/*.query.graphql', { loaders: [new GraphQLFileLoader()], errorHandling: 'aggregate', });
returndoes not throw. Failed sources stay in the result as{ location, error }without adocument, next to the sources that parsed:const sources = await loadDocuments('**/*.query.graphql', { loaders: [new GraphQLFileLoader(), new CodeFileLoader()], errorHandling: 'return', }); sources[0].document; // ValidQuery sources[1].document; // undefined sources[1].error; // GraphQLError: Syntax Error
Sourcenow has an optionalerrorfield.@graphql-tools/graphql-file-loaderand@graphql-tools/code-file-loaderhonor the same option.CodeFileLoader's existingnoSilentErrorsdefault is unchanged unlesserrorHandlingis set.Custom loader failures and incomplete inline documents (
query Foo {) follow the same option.errorHandling: 'throw'raises the first failure, notAggregateError.
Patch Changes
- Updated dependencies [
6da2904,7770067,f956bfe]:- @graphql-tools/utils@13.2.0
- @graphql-tools/import@8.0.7
- @graphql-tools/load-files@8.0.7
@graphql-tools/utils@13.2.0
Minor Changes
-
#8606
6da2904Thanks @ardatan! -loadDocuments/loadTypedefsnow accepterrorHandlingso a single invalid file does not have to hide the rest of the load.A glob that mixes a valid operation with broken SDL used to throw the first
Syntax Errorand drop every other file:# valid.query.graphql query ValidQuery { a }
# broken-a.query.graphql query { a
await loadDocuments('**/*.query.graphql', { loaders: [new GraphQLFileLoader()], }); // throws Syntax Error
errorHandlingchooses what happens next. The default is unchanged (throwthe first error).aggregatewaits until every source is attempted, then throwsAggregateErrorwith each parse/load failure:await loadDocuments('**/*.query.graphql', { loaders: [new GraphQLFileLoader()], errorHandling: 'aggregate', });
returndoes not throw. Failed sources stay in the result as{ location, error }without adocument, next to the sources that parsed:const sources = await loadDocuments('**/*.query.graphql', { loaders: [new GraphQLFileLoader(), new CodeFileLoader()], errorHandling: 'return', }); sources[0].document; // ValidQuery sources[1].document; // undefined sources[1].error; // GraphQLError: Syntax Error
Sourcenow has an optionalerrorfield.@graphql-tools/graphql-file-loaderand@graphql-tools/code-file-loaderhonor the same option.CodeFileLoader's existingnoSilentErrorsdefault is unchanged unlesserrorHandlingis set.Custom loader failures and incomplete inline documents (
query Foo {) follow the same option.errorHandling: 'throw'raises the first failure, notAggregateError.
Patch Changes
-
#8588
7770067Thanks @ardatan! -rewireTypes(used bymapSchemaandaddTypes) now recreates only the types that actually reference a replaced type.mapSchemaalready changes just the mapped types.rewireTypesthen walked every type and built a newGraphQLObjectType/ enum / scalar so field pointers stayed consistent. That is correct, and it is overkill when a pipeline maps one type on a large schema: unrelated types, unused enums, and unused scalars were cloned on every pass.The crawl now finds types whose field, argument, interface, or union members point at a different object than the type map (including wrapper types created during mapping), then rebuilds only that set. Specified directives stay as-is. Types that do not reach a replacement keep the same JavaScript object.
createStubreturns the graphql-js specified scalars forString/ID/Int/Float/Booleaninstead of building an object or input with that name. StitchtypeFromASTusescreateStubfor every field type.const schema = buildSchema(/* GraphQL */ ` scalar Unused enum Color { RED } type Query { foo: Foo } type Foo { id: ID } `) const mapped = mapSchema(schema, { [MapperKind.OBJECT_TYPE]: type => type.name === 'Foo' ? new GraphQLObjectType({ ...type.toConfig(), description: 'mapped' }) : undefined, }) mapped.getType('Unused') === schema.getType('Unused') mapped.getType('Color') === schema.getType('Color')
-
#8608
f956bfeThanks @ardatan! -transformInputValuenow keeps falsy list items when GraphQL coerces a singleton into a list.A
[String]/[Int]/[Boolean]field can receive"",0, orfalseinstead of a one-element array. The previousasArrayhelper treated those as empty and produced[], so rename and other input transforms dropped a valid value.nullis still returned as-is. A real list is still walked item by item.
@graphql-tools/webpack-loader@8.1.0
Minor Changes
-
#8605
ea2603aThanks @ardatan! -@graphql-tools/webpack-loader(and@graphql-tools/jest-transform, which calls it) now accepts more than one GraphQL operation in the same file and exports each named operation, matchinggraphql-tag/loader.A file with two mutations used to throw
GraphQL Webpack Loader allows only for one GraphQL Operation per file, so this import failed in webpack and Jest:# menuMutations.graphql mutation deleteMenuMutation { deleteMenu { ...MenuFields } } mutation upsertMenuMutation { upsertMenu { id } } fragment MenuFields on Menu { id name }
import { deleteMenuMutation, upsertMenuMutation } from './menuMutations.graphql'
The default export is still the whole document. Each named export is that operation plus the fragments it uses.
deleteMenuMutationincludesMenuFields;upsertMenuMutationdoes not.import menuMutations, { deleteMenuMutation, upsertMenuMutation, } from './menuMutations.graphql'
A single named operation is also exported under its name. Unnamed operations are still allowed when the file has only one operation. If the file has more than one operation, each operation must be named.
jest-transformnow passes the Jest source path and filesystem into the loader, soesModule: truecan emit named exports for operations and fragments pulled in with#import. CommonJS named exports use an own property so a GraphQL name of__proto__does not replace the export object's prototype.
@graphql-tools/executor@4.1.6
Patch Changes
-
#8610
7832344Thanks @ardatan! - Use compact null-prototype maps for per-field execution results.executeFieldsandpromiseForObjectnow allocate result objects with a shared empty constructor thenObject.setPrototypeOf(..., null), instead ofObject.create(null). Dense responses create many of these maps; the named constructor gives V8 a reusable shape while keeping the same null-prototype semantics. -
Updated dependencies [
6da2904,7770067,f956bfe]:- @graphql-tools/utils@13.2.0
@graphql-tools/executor-apollo-link@3.0.7
Patch Changes
@graphql-tools/executor-envelop@5.0.8
Patch Changes
@graphql-tools/executor-legacy-ws@2.0.7
Patch Changes
@graphql-tools/executor-urql-exchange@2.0.7
Patch Changes
@graphql-tools/executor-yoga@5.0.1
Patch Changes
- Updated dependencies [
6da2904,7770067,f956bfe]:- @graphql-tools/utils@13.2.0
- @graphql-tools/executor-envelop@5.0.8
@graphql-tools/graphql-tag-pluck@9.0.8
Patch Changes
graphql-tools@10.0.7
Patch Changes
- Updated dependencies [
902d870]:- @graphql-tools/schema@11.0.7
@graphql-tools/import@8.0.7
Patch Changes
@graphql-tools/links@11.0.7
Patch Changes
@graphql-tools/load-files@8.0.7
Patch Changes
@graphql-tools/apollo-engine-loader@9.0.7
Patch Changes
-
#8598
38e7800Thanks @ardatan! -ApolloEngineLoadernow sendsoperationName: "GetSchemaByTag"in the GraphQL request body.The query was already named
GetSchemaByTag, but the POST body only hadqueryandvariables. Servers and Apollo Server plugins that require an operation name on the request (and read it from the JSON body, not the document) rejected this introspection call. The GitHub loader already sendsoperationNamethe same way. -
Updated dependencies [
6da2904,7770067,f956bfe]:- @graphql-tools/utils@13.2.0
@graphql-tools/external-fragment-loader@1.0.8
Patch Changes
- Updated dependencies [
6da2904,7770067,f956bfe]:- @graphql-tools/utils@13.2.0
- @graphql-tools/graphql-tag-pluck@9.0.8
- @graphql-tools/load-files@8.0.7
@graphql-tools/git-loader@9.0.8
Patch Changes
- Updated dependencies [
6da2904,7770067,f956bfe]:- @graphql-tools/utils@13.2.0
- @graphql-tools/graphql-tag-pluck@9.0.8
@graphql-tools/github-loader@10.0.8
Patch Changes
- Updated dependencies [
6da2904,7770067,f956bfe]:- @graphql-tools/utils@13.2.0
- @graphql-tools/graphql-tag-pluck@9.0.8
@graphql-tools/json-file-loader@9.0.7
Patch Changes
- Updated dependencies [
6da2904,7770067,f956bfe]:- @graphql-tools/utils@13.2.0
- @graphql-tools/load-files@8.0.7
@graphql-tools/module-loader@9.0.7
Patch Changes
@graphql-tools/url-loader@10.0.7
Patch Changes
- Updated dependencies [
6da2904,7770067,f956bfe]:- @graphql-tools/utils@13.2.0
- @graphql-tools/executor-legacy-ws@2.0.7
@graphql-tools/merge@10.0.7
Patch Changes
@graphql-tools/mock@10.1.2
Patch Changes
- Updated dependencies [
6da2904,902d870,7770067,f956bfe]:- @graphql-tools/utils@13.2.0
- @graphql-tools/schema@11.0.7
@graphql-tools/node-require@8.0.7
Patch Changes
- Updated dependencies [
62ef681,9df67b4,6da2904,46039a0,7770067,f956bfe]:- @graphql-tools/load@9.1.0
- @graphql-tools/utils@13.2.0
- @graphql-tools/graphql-file-loader@9.1.0
@graphql-tools/relay-operation-optimizer@8.0.7
Patch Changes
@graphql-tools/resolvers-composition@8.0.7
Patch Changes
@graphql-tools/schema@11.0.7
Patch Changes
-
#8601
902d870Thanks @ardatan! -mergeSchemasandmakeExecutableSchemanow acceptonFieldTypeConflict, the same callbackmergeTypeDefsalready had.When two schemas define the same field with different types, default merge throws. When only nullability differs, it keeps the earlier type.
# first schema type Post { id: ID! title: String! content: String! published: Boolean! } # second schema type Post { id: ID! title: String content: Int fieldA: Int fieldB: String }
content: String!vscontent: Intthrows.title: String!vstitle: StringkeepsString!.mergeSchemas({ schemas: [firstSchema, secondSchema], onFieldTypeConflict: (_existing, incoming) => incoming, });
The merged
Posttakestitle: Stringandcontent: Intfrom the second schema, keepspublishedfrom the first, and addsfieldA/fieldB. Types that exist on only one schema, such asUseron the first, stay.The same callback applies to field arguments. Returning the incoming field uses the second schema's argument types and still keeps arguments that exist on only one side. Returning the existing field with
reverseArguments: truekeeps the first schema's argument types and still appends new arguments. -
Updated dependencies [
6da2904,7770067,f956bfe]:- @graphql-tools/utils@13.2.0
- @graphql-tools/merge@10.0.7