diff --git a/README.md b/README.md
index 45ef104..3d3be1e 100644
--- a/README.md
+++ b/README.md
@@ -143,7 +143,7 @@ import it in your TypeScript code.
```ts
import { IssuesQuery } from './query.graphql.ts'
-````
+```
The `IssuesQuery` variable is a string with the GraphQL query. You can use it
directly in your code, or pass it to a function that accepts a query.
@@ -166,7 +166,7 @@ import type { IssuesQuery } from './query.graphql.ts'
How to get the return type of a query?
-Megaera generates TypeScript types for queries as functions.
+Megaera generates TypeScript types for queries as functions.
```ts
type UserQuery = (vars: { login?: string }) => {
@@ -228,7 +228,7 @@ function query(query: T, variables?: Variables) {
}
// Return type, and types of variables are inferred from the query.
-const {issues} = await query(IssuesQuery, {login: 'webpod'})
+const { issues } = await query(IssuesQuery, { login: 'webpod' })
```
diff --git a/src/cli.ts b/src/cli.ts
index 68451e6..be7cd94 100644
--- a/src/cli.ts
+++ b/src/cli.ts
@@ -99,7 +99,7 @@ void (async function main() {
'%d operation',
'%d operations',
)
- const frg = plural(content.fragments.length, '%d fragment', '%d fragments')
+ const frg = plural(content.fragments.size, '%d fragment', '%d fragments')
console.log(`> ${styleText('green', 'done')} (${ops}, ${frg})`)
const prefix = `// DO NOT EDIT. This is a generated file. Instead of this file, edit "${fileName}".\n\n`
diff --git a/src/generate.test.ts b/src/generate.test.ts
index 0030dee..e96ef2d 100644
--- a/src/generate.test.ts
+++ b/src/generate.test.ts
@@ -29,7 +29,9 @@ function getFieldType(name: string, fieldName: string) {
if (!isObjectType(type)) {
throw new Error(`${name} is not object type.`)
}
- const field = type.astNode?.fields?.find((f) => f.name.value === fieldName)?.type
+ const field = type.astNode?.fields?.find(
+ (f) => f.name.value === fieldName,
+ )?.type
if (!field) {
throw new Error(`Cannot find ${fieldName} field in ${name}.`)
}
diff --git a/src/generate.ts b/src/generate.ts
index ccffdfe..2e393fa 100644
--- a/src/generate.ts
+++ b/src/generate.ts
@@ -11,7 +11,7 @@ import { isObjectType } from 'graphql/type/index.js'
export function generate(content: Content) {
const code: string[] = []
- for (const f of content.fragments) {
+ for (const f of content.fragments.values()) {
code.push(`const ${f.name} = \`#graphql
${f.source}\`
@@ -25,8 +25,9 @@ export type ${f.name} = ${generateSelector(f, 0, true)}
}
let querySource = q.source
- for (const f of content.fragments) {
- querySource = '${' + f.name + '}\n' + querySource
+
+ for (const fName of usedFragments(q, content)) {
+ querySource = '${' + fName + '}\n' + querySource
}
code.push(`export const ${q.name} = \`#graphql
@@ -39,6 +40,25 @@ export type ${q.name} = (${generateVariables(q.variables)}) => ${generateSelecto
return code.join('\n')
}
+function usedFragments(q: Selector, content: Content): string[] {
+ const fragments: string[] = []
+ for (const field of q.fields) {
+ if (field.isFragment) {
+ fragments.push(field.name)
+ const fragment = content.fragments.get(field.name)
+ if (!fragment) {
+ throw new Error(`Fragment ${field.name} is not defined.`)
+ }
+ fragments.push(...usedFragments(fragment, content))
+ }
+ fragments.push(...usedFragments(field, content))
+ }
+ for (const inlineFragment of q.inlineFragments) {
+ fragments.push(...usedFragments(inlineFragment, content))
+ }
+ return fragments
+}
+
function generateVariables(variables?: Variable[]) {
if (!variables || variables.length === 0) {
return ''
diff --git a/src/visitor.ts b/src/visitor.ts
index 298ed17..78ccb15 100644
--- a/src/visitor.ts
+++ b/src/visitor.ts
@@ -1,5 +1,13 @@
-import { GraphQLNonNull, GraphQLOutputType, GraphQLType, isNonNullType } from 'graphql/type/definition.js'
-import { typeFromAST, TypeInfo, visitWithTypeInfo } from 'graphql/utilities/index.js'
+import {
+ GraphQLOutputType,
+ GraphQLType,
+ isNonNullType,
+} from 'graphql/type/definition.js'
+import {
+ typeFromAST,
+ TypeInfo,
+ visitWithTypeInfo,
+} from 'graphql/utilities/index.js'
import { parse, print, Source, visit } from 'graphql/language/index.js'
import { GraphQLSchema } from 'graphql/type/index.js'
import { GraphQLError } from 'graphql/error/index.js'
@@ -23,7 +31,7 @@ export type Selector = {
export type Content = {
operations: Selector[]
- fragments: Selector[]
+ fragments: Map
}
export function traverse(schema: GraphQLSchema, source: Source): Content {
@@ -32,19 +40,19 @@ export function traverse(schema: GraphQLSchema, source: Source): Content {
const content: Content = {
operations: [],
- fragments: []
+ fragments: new Map(),
}
const stack: Selector[] = []
const visitor = visitWithTypeInfo(typeInfo, {
OperationDefinition: {
- enter: function(node) {
+ enter: function (node) {
if (node.name === undefined) {
throw new GraphQLError(
firstLetterUpper(node.operation) + ' name is required',
node,
- source
+ source,
)
}
checkUnique(node.name.value, content)
@@ -55,7 +63,7 @@ export function traverse(schema: GraphQLSchema, source: Source): Content {
variables.push({
name: v.variable.name.value,
type: type,
- required: v.defaultValue === undefined && isNonNullType(type)
+ required: v.defaultValue === undefined && isNonNullType(type),
})
}
@@ -65,7 +73,7 @@ export function traverse(schema: GraphQLSchema, source: Source): Content {
fields: [],
inlineFragments: [],
variables: variables,
- source: print(node)
+ source: print(node),
}
stack.push(s)
@@ -73,7 +81,7 @@ export function traverse(schema: GraphQLSchema, source: Source): Content {
},
leave() {
stack.pop()
- }
+ },
},
FragmentDefinition: {
@@ -85,15 +93,15 @@ export function traverse(schema: GraphQLSchema, source: Source): Content {
type: typeInfo.getType() ?? undefined,
fields: [],
inlineFragments: [],
- source: print(node)
+ source: print(node),
}
stack.push(s)
- content.fragments.push(s)
+ content.fragments.set(s.name, s)
},
leave() {
stack.pop()
- }
+ },
},
Field: {
@@ -109,7 +117,7 @@ export function traverse(schema: GraphQLSchema, source: Source): Content {
},
leave() {
stack.pop()
- }
+ },
},
FragmentSpread: {
@@ -121,13 +129,17 @@ export function traverse(schema: GraphQLSchema, source: Source): Content {
fields: [],
inlineFragments: [],
})
- }
+ },
},
InlineFragment: {
enter(node) {
if (!node.typeCondition) {
- throw new GraphQLError('Inline fragment must have type condition.', node, source)
+ throw new GraphQLError(
+ 'Inline fragment must have type condition.',
+ node,
+ source,
+ )
}
const s: Selector = {
name: node.typeCondition.name.value,
@@ -141,7 +153,7 @@ export function traverse(schema: GraphQLSchema, source: Source): Content {
leave() {
stack.pop()
},
- }
+ },
})
visit(ast, visitor)
@@ -153,7 +165,7 @@ function checkUnique(name: string, content: Content) {
if (content.operations.find((o) => o.name === name)) {
throw new GraphQLError(`Operation with name "${name}" is already defined.`)
}
- if (content.fragments.find((f) => f.name === name)) {
+ if (content.fragments.has(name)) {
throw new GraphQLError(`Fragment with name "${name}" is already defined.`)
}
}
diff --git a/tsconfig.json b/tsconfig.json
index 44a5d3b..78b4ecf 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -7,7 +7,7 @@
"strict": true,
"outDir": "./dist",
"declaration": true,
- "allowJs": true,
+ "allowJs": true
},
"include": ["./src/**/*"]
}