chore: Move graphl to a single place (#9303)

* chore: Move graphl to a single place

* add new line
This commit is contained in:
Adrien de Peretti
2024-09-25 12:04:25 +02:00
committed by GitHub
parent 1d3a60023a
commit 358435d715
30 changed files with 78 additions and 62 deletions
@@ -0,0 +1,111 @@
import { cleanGraphQLSchema } from "../clean-graphql"
describe("Clean Graphql Schema", function () {
it("Should keep the schema intact if all entities are available", function () {
const schemaStr = `
type Product {
id: ID!
title: String!
variants: [Variant]!
}
type Variant {
id: ID!
title: String!
product_id: ID!
product: Product!
}
`
const { schema, notFound } = cleanGraphQLSchema(schemaStr)
expect(schema.replace(/\s/g, "")).toEqual(schemaStr.replace(/\s/g, ""))
expect(notFound).toEqual({})
})
it("Should remove fields where the relation doesn't exist", function () {
const schemaStr = `
type Product {
id: ID!
title: String!
variants: [Variant!]!
profile: ShippingProfile!
}
`
const expectedStr = `
type Product {
id: ID!
title: String!
}
`
const { schema, notFound } = cleanGraphQLSchema(schemaStr)
expect(schema.replace(/\s/g, "")).toEqual(expectedStr.replace(/\s/g, ""))
expect(notFound).toEqual({
Product: { variants: "Variant", profile: "ShippingProfile" },
})
})
it("Should remove fields where the relation doesn't exist and flag extended entity where the main entity doesn't exist", function () {
const schemaStr = `
scalar JSON
type Product {
id: ID!
title: String!
variants: [Variant!]!
profile: ShippingProfile!
}
extend type Variant {
metadata: JSON
}
`
const expectedStr = `
scalar JSON
type Product {
id: ID!
title: String!
}
`
const { schema, notFound } = cleanGraphQLSchema(schemaStr)
expect(schema.replace(/\s/g, "")).toEqual(expectedStr.replace(/\s/g, ""))
expect(notFound).toEqual({
Product: { variants: "Variant", profile: "ShippingProfile" },
Variant: { __extended: "" },
})
})
it("Should remove fields from extend where the relation doesn't exist", function () {
const schemaStr = `
scalar JSON
type Product {
id: ID!
title: String!
variants: [Variant!]!
profile: ShippingProfile!
}
extend type Product {
variants: [Variant!]!
profile: ShippingProfile!
metadata: JSON
}
`
const expectedStr = `
scalar JSON
type Product {
id: ID!
title: String!
}
extend type Product {
metadata: JSON
}
`
const { schema, notFound } = cleanGraphQLSchema(schemaStr)
expect(schema.replace(/\s/g, "")).toEqual(expectedStr.replace(/\s/g, ""))
expect(notFound).toEqual({
Product: { variants: "Variant", profile: "ShippingProfile" },
})
})
})
@@ -0,0 +1,358 @@
import { GraphQLParser } from "../graphql-parser"
describe("RemoteJoiner.parseQuery", () => {
beforeEach(() => {
jest.clearAllMocks()
})
it("Simple query with fields", async () => {
const graphqlQuery = `
query {
order {
id
number
date
}
}
`
const parser = new GraphQLParser(graphqlQuery)
const rjQuery = parser.parseQuery()
expect(rjQuery).toEqual({
alias: "order",
fields: ["id", "number", "date"],
expands: [],
})
})
it("Simple query with fields and arguments", async () => {
const graphqlQuery = `
query {
order(
id: "ord_123",
another_arg: 987,
complexArg: {
id: "123",
name: "test",
nestedArg: {
nest_id: "abc",
num: 123
}
}
) {
id
number
date
}
}
`
const parser = new GraphQLParser(graphqlQuery)
const rjQuery = parser.parseQuery()
expect(rjQuery).toEqual({
alias: "order",
fields: ["id", "number", "date"],
expands: [],
args: [
{
name: "id",
value: "ord_123",
},
{
name: "another_arg",
value: 987,
},
{
name: "complexArg",
value: {
id: "123",
name: "test",
nestedArg: {
nest_id: "abc",
num: 123,
},
},
},
],
})
})
it("Simple query with mapping fields to services", async () => {
const graphqlQuery = `
query {
order {
id
number
date
products {
product_id
variant_id
order
variant {
name
sku
}
}
}
}
`
const parser = new GraphQLParser(graphqlQuery, {})
const rjQuery = parser.parseQuery()
expect(rjQuery).toEqual({
alias: "order",
fields: ["id", "number", "date", "products"],
expands: [
{
property: "products",
fields: ["product_id", "variant_id", "order", "variant"],
},
{
property: "products.variant",
fields: ["name", "sku"],
},
],
})
})
it("Nested query with fields", async () => {
const graphqlQuery = `
query {
order {
id
number
date
products {
product_id
variant_id
order
variant {
name
sku
}
}
}
}
`
const parser = new GraphQLParser(graphqlQuery)
const rjQuery = parser.parseQuery()
expect(rjQuery).toEqual({
alias: "order",
fields: ["id", "number", "date", "products"],
expands: [
{
property: "products",
fields: ["product_id", "variant_id", "order", "variant"],
},
{
property: "products.variant",
fields: ["name", "sku"],
},
],
})
})
it("Nested query with fields and arguments", async () => {
const graphqlQuery = `
query {
order (order_id: "ord_123") {
id
number
date
products (limit: 10) {
product_id
variant_id
order
variant (complexArg: { id: "123", name: "test", nestedArg: { nest_id: "abc", num: 123 } }, region_id: "reg_123") {
name
sku
}
}
}
}
`
const parser = new GraphQLParser(graphqlQuery)
const rjQuery = parser.parseQuery()
expect(rjQuery).toEqual({
alias: "order",
fields: ["id", "number", "date", "products"],
expands: [
{
property: "products",
fields: ["product_id", "variant_id", "order", "variant"],
args: [
{
name: "limit",
value: 10,
},
],
},
{
property: "products.variant",
fields: ["name", "sku"],
args: [
{
name: "complexArg",
value: {
id: "123",
name: "test",
nestedArg: {
nest_id: "abc",
num: 123,
},
},
},
{
name: "region_id",
value: "reg_123",
},
],
},
],
args: [
{
name: "order_id",
value: "ord_123",
},
],
})
})
it("Nested query with fields and arguments using variables", async () => {
const graphqlQuery = `
query($orderId: ID, $anotherArg: String, $randomVariable: nonValidatedType) {
order (order_id: $orderId, anotherArg: $anotherArg) {
id
number
date
products (randomValue: $randomVariable) {
product_id
variant_id
order
}
}
}
`
const parser = new GraphQLParser(graphqlQuery, {
orderId: 123,
randomVariable: { complex: { num: 12343, str: "str_123" } },
anotherArg: "any string",
})
const rjQuery = parser.parseQuery()
expect(rjQuery).toEqual({
alias: "order",
fields: ["id", "number", "date", "products"],
expands: [
{
property: "products",
fields: ["product_id", "variant_id", "order"],
args: [
{
name: "randomValue",
value: {
complex: {
num: 12343,
str: "str_123",
},
},
},
],
},
],
args: [
{
name: "order_id",
value: 123,
},
{
name: "anotherArg",
value: "any string",
},
],
})
})
it("Nested query with fields and directives", async () => {
const graphqlQuery = `
query {
order(regularArgs: 123) {
id
number @include(if: "date > '2020-01-01'")
date
products {
product_id
variant_id
variant @count {
name @lowerCase
sku @include(if: "name == 'test'")
}
}
}
}
`
const parser = new GraphQLParser(graphqlQuery)
const rjQuery = parser.parseQuery()
expect(rjQuery).toEqual({
alias: "order",
fields: ["id", "number", "date", "products"],
expands: [
{
property: "products",
fields: ["product_id", "variant_id", "variant"],
directives: {
variant: [
{
name: "count",
},
],
},
},
{
property: "products.variant",
fields: ["name", "sku"],
directives: {
name: [
{
name: "lowerCase",
},
],
sku: [
{
name: "include",
args: [
{
name: "if",
value: "name == 'test'",
},
],
},
],
},
},
],
args: [
{
name: "regularArgs",
value: 123,
},
],
directives: {
number: [
{
name: "include",
args: [
{
name: "if",
value: "date > '2020-01-01'",
},
],
},
],
},
})
})
})
@@ -0,0 +1,94 @@
import { mergeTypeDefs } from "@graphql-tools/merge"
import { makeExecutableSchema } from "@graphql-tools/schema"
import { gqlGetFieldsAndRelations } from "../../get-fields-and-relations"
const userModule = `
type User {
id: ID!
name: String!
blabla: WHATEVER
}
type Post {
author: User!
}
`
const postModule = `
type Post {
id: ID!
title: String!
date: String
}
type User {
posts: [Post!]!
}
type WHATEVER {
random_field: String
post: Post
}
`
const mergedSchema = mergeTypeDefs([userModule, postModule])
const schema = makeExecutableSchema({
typeDefs: mergedSchema,
})
const types = schema.getTypeMap()
describe("gqlGetFieldsAndRelations", function () {
it("Should get all fields of a given entity", async function () {
const fields = gqlGetFieldsAndRelations(types, "User")
expect(fields).toEqual(expect.arrayContaining(["id", "name"]))
})
it("Should get all fields of a given entity and a relation", async function () {
const fields = gqlGetFieldsAndRelations(types, "User", ["posts"])
expect(fields).toEqual(
expect.arrayContaining([
"id",
"name",
"posts.id",
"posts.title",
"posts.date",
])
)
})
it("Should get all fields of a given entity and many relations", async function () {
const fields = gqlGetFieldsAndRelations(types, "User", [
"posts",
"blabla",
"blabla.post",
])
expect(fields).toEqual(
expect.arrayContaining([
"id",
"name",
"posts.id",
"posts.title",
"posts.date",
"blabla.random_field",
"blabla.post.id",
"blabla.post.title",
"blabla.post.date",
])
)
})
it("Should get all fields of a given entity and many relations limited to the relations given", async function () {
const fields = gqlGetFieldsAndRelations(types, "User", ["posts", "blabla"])
expect(fields).toEqual(
expect.arrayContaining([
"id",
"name",
"posts.id",
"posts.title",
"posts.date",
"blabla.random_field",
])
)
})
})
@@ -0,0 +1,110 @@
import { mergeTypeDefs } from "@graphql-tools/merge"
import { makeExecutableSchema } from "@graphql-tools/schema"
import { graphqlSchemaToFields } from "../../graphql-to-fields"
const userModule = `
type User {
id: ID!
name: String!
blabla: WHATEVER
}
type Post {
author: User!
}
`
const postModule = `
type Post {
id: ID!
title: String!
date: String
}
type User {
posts: [Post!]!
}
type WHATEVER {
random_field: String
post: Post
}
`
const mergedSchema = mergeTypeDefs([userModule, postModule])
const schema = makeExecutableSchema({
typeDefs: mergedSchema,
})
const types = schema.getTypeMap()
describe("graphqlSchemaToFields", function () {
it("Should get all fields of a given entity", async function () {
const fields = graphqlSchemaToFields(types, "User")
expect(fields).toEqual(expect.arrayContaining(["id", "name"]))
})
it("Should get all fields of a given entity and a relation", async function () {
const fields = graphqlSchemaToFields(types, "User", ["posts"])
expect(fields).toEqual(
expect.arrayContaining([
"id",
"name",
"posts.id",
"posts.title",
"posts.date",
])
)
})
it("Should get all fields of a given entity and many relations", async function () {
const fields = graphqlSchemaToFields(types, "User", [
"posts",
"blabla",
"blabla.post",
])
expect(fields).toEqual(
expect.arrayContaining([
"id",
"name",
"posts.id",
"posts.title",
"posts.date",
"blabla.random_field",
"blabla.post.id",
"blabla.post.title",
"blabla.post.date",
])
)
})
it("Should get all fields of a given entity and many relations limited to the relations given", async function () {
const fields = graphqlSchemaToFields(types, "User", ["posts", "blabla"])
expect(fields).toEqual(
expect.arrayContaining([
"id",
"name",
"posts.id",
"posts.title",
"posts.date",
"blabla.random_field",
])
)
})
it("Should get all fields of a given entity and many relations limited to the relations given if they exists", async function () {
const fields = graphqlSchemaToFields(types, "User", [
"posts",
"doNotExists",
])
expect(fields).toEqual(
expect.arrayContaining([
"id",
"name",
"posts.id",
"posts.title",
"posts.date",
])
)
})
})
@@ -0,0 +1,91 @@
import { Kind, parse, print, visit } from "graphql"
export function cleanGraphQLSchema(schema: string): {
schema: string
notFound: Record<string, Record<string, string>>
} {
const extractTypeNameAndKind = (type) => {
if (type.kind === Kind.NAMED_TYPE) {
return [type.name.value, type.kind]
}
if (type.kind === Kind.NON_NULL_TYPE || type.kind === Kind.LIST_TYPE) {
return extractTypeNameAndKind(type.type)
}
return [null, null]
}
const ast = parse(schema)
const typeNames = new Set(["String", "Int", "Float", "Boolean", "ID"])
const extendedTypes = new Set()
const kinds = [
Kind.OBJECT_TYPE_DEFINITION,
Kind.INTERFACE_TYPE_DEFINITION,
Kind.ENUM_TYPE_DEFINITION,
Kind.SCALAR_TYPE_DEFINITION,
Kind.INPUT_OBJECT_TYPE_DEFINITION,
Kind.UNION_TYPE_DEFINITION,
]
ast.definitions.forEach((def: any) => {
if (kinds.includes(def.kind)) {
typeNames.add(def.name.value)
} else if (def.kind === Kind.OBJECT_TYPE_EXTENSION) {
extendedTypes.add(def.name.value)
}
})
const nonExistingMap: Record<string, Record<string, string>> = {}
const parentStack: string[] = []
/*
Traverse the graph mapping all the entities + fields and removing the ones that don't exist.
Extensions are not removed, but marked with a "__extended" key if the main entity doesn't exist. (example: Link modules injecting fields into another module)
*/
const cleanedAst = visit(ast, {
ObjectTypeExtension: {
enter(node) {
const typeName = node.name.value
parentStack.push(typeName)
if (!typeNames.has(typeName)) {
nonExistingMap[typeName] ??= {}
nonExistingMap[typeName]["__extended"] = ""
return null
}
return
},
leave() {
parentStack.pop()
},
},
ObjectTypeDefinition: {
enter(node) {
parentStack.push(node.name.value)
},
leave() {
parentStack.pop()
},
},
FieldDefinition: {
leave(node) {
const [typeName, kind] = extractTypeNameAndKind(node.type)
if (!typeNames.has(typeName) && kind === Kind.NAMED_TYPE) {
const currentParent = parentStack[parentStack.length - 1]
nonExistingMap[currentParent] ??= {}
nonExistingMap[currentParent][node.name.value] = typeName
return null
}
return
},
},
})
// Return the schema and the map of non existing entities and fields
return {
schema: print(cleanedAst),
notFound: nonExistingMap,
}
}
@@ -0,0 +1,41 @@
import { isListType, isNonNullType, isObjectType } from "graphql"
/**
* Extracts only the relation fields from the GraphQL type map.
* @param {Map<string, any>} typeMap - The GraphQL schema TypeMap.
* @returns {Map<string, Map<string, string>>} A map where each key is an entity name, and the values are a map of relation fields and their corresponding entity type.
*/
export function extractRelationsFromGQL(
typeMap: Map<string, any>
): Map<string, Map<string, string>> {
const relationMap = new Map()
// Extract the actual type
const getBaseType = (type) => {
if (isNonNullType(type) || isListType(type)) {
return getBaseType(type.ofType)
}
return type
}
for (const [typeName, graphqlType] of Object.entries(typeMap)) {
if (!isObjectType(graphqlType)) {
continue
}
const fields = graphqlType.getFields()
const entityRelations = new Map()
for (const [fieldName, fieldConfig] of Object.entries(fields)) {
const fieldType = getBaseType((fieldConfig as any).type)
// only add relation fields
if (isObjectType(fieldType)) {
entityRelations.set(fieldName, fieldType.name)
}
}
relationMap.set(typeName, entityRelations)
}
return relationMap
}
@@ -0,0 +1,43 @@
import { GraphQLNamedType, GraphQLObjectType, isObjectType } from "graphql"
/**
* Generate a list of fields and fields relations for a given type with the requested relations
* @param schemaTypeMap
* @param typeName
* @param relations
*/
export function gqlGetFieldsAndRelations(
schemaTypeMap: { [key: string]: GraphQLNamedType },
typeName: string,
relations: string[] = []
) {
const result: string[] = []
function traverseFields(typeName, prefix = "") {
const type = schemaTypeMap[typeName]
if (!(type instanceof GraphQLObjectType)) {
return
}
const fields = type.getFields()
for (const fieldName in fields) {
const field = fields[fieldName]
let fieldType = field.type as any
while (fieldType.ofType) {
fieldType = fieldType.ofType
}
if (!isObjectType(fieldType)) {
result.push(`${prefix}${fieldName}`)
} else if (relations.includes(prefix + fieldName)) {
traverseFields(fieldType.name, `${prefix}${fieldName}.`)
}
}
}
traverseFields(typeName)
return result
}
@@ -0,0 +1,195 @@
import { RemoteJoinerQuery } from "@medusajs/types"
import {
ArgumentNode,
DirectiveNode,
DocumentNode,
FieldNode,
Kind,
OperationDefinitionNode,
SelectionSetNode,
ValueNode,
parse,
} from "graphql"
interface Argument {
name: string
value?: unknown
}
interface Directive {
name: string
args?: Argument[]
}
interface Entity {
property: string
fields: string[]
args?: Argument[]
directives?: { [field: string]: Directive[] }
}
/**
* Remote joiner graphql parser
*/
export class GraphQLParser {
private ast: DocumentNode
constructor(input: string, private variables: Record<string, unknown> = {}) {
this.ast = parse(input)
this.variables = variables
}
private parseValueNode(valueNode: ValueNode): unknown {
const obj = {}
switch (valueNode.kind) {
case Kind.VARIABLE:
return this.variables ? this.variables[valueNode.name.value] : undefined
case Kind.INT:
return parseInt(valueNode.value, 10)
case Kind.FLOAT:
return parseFloat(valueNode.value)
case Kind.BOOLEAN:
return Boolean(valueNode.value)
case Kind.STRING:
case Kind.ENUM:
return valueNode.value
case Kind.NULL:
return null
case Kind.LIST:
return valueNode.values.map((v) => this.parseValueNode(v))
case Kind.OBJECT:
for (const field of valueNode.fields) {
obj[field.name.value] = this.parseValueNode(field.value)
}
return obj
default:
return undefined
}
}
private parseArguments(
args: readonly ArgumentNode[]
): Argument[] | undefined {
if (!args.length) {
return
}
return args.map((arg) => {
const value = this.parseValueNode(arg.value)
return {
name: arg.name.value,
value: value,
}
})
}
private parseDirectives(directives: readonly DirectiveNode[]): Directive[] {
return directives.map((directive) => ({
name: directive.name.value,
args: this.parseArguments(directive.arguments || []),
}))
}
private createDirectivesMap(selectionSet: SelectionSetNode):
| {
[field: string]: Directive[]
}
| undefined {
const directivesMap: { [field: string]: Directive[] } = {}
let hasDirectives = false
selectionSet.selections.forEach((field) => {
const fieldName = (field as FieldNode).name.value
const fieldDirectives = this.parseDirectives(
(field as FieldNode).directives || []
)
if (fieldDirectives.length > 0) {
hasDirectives = true
directivesMap[fieldName] = fieldDirectives
}
})
return hasDirectives ? directivesMap : undefined
}
private extractEntities(
node: SelectionSetNode,
parentName = "",
mainService = ""
): Entity[] {
const entities: Entity[] = []
node.selections.forEach((selection) => {
if (selection.kind === "Field") {
const fieldNode = selection as FieldNode
if (!fieldNode.selectionSet) {
return
}
const propName = fieldNode.name.value
const entityName = parentName ? `${parentName}.${propName}` : propName
const nestedEntity: Entity = {
property: entityName.replace(`${mainService}.`, ""),
fields: fieldNode.selectionSet.selections.map(
(field) => (field as FieldNode).name.value
),
args: this.parseArguments(fieldNode.arguments || []),
directives: this.createDirectivesMap(fieldNode.selectionSet),
}
entities.push(nestedEntity)
entities.push(
...this.extractEntities(
fieldNode.selectionSet,
entityName,
mainService
)
)
}
})
return entities
}
public parseQuery(): RemoteJoinerQuery {
const queryDefinition = this.ast.definitions.find(
(definition) => definition.kind === "OperationDefinition"
) as OperationDefinitionNode
if (!queryDefinition) {
throw new Error("No query found")
}
const rootFieldNode = queryDefinition.selectionSet
.selections[0] as FieldNode
const propName = rootFieldNode.name.value
const remoteJoinConfig: RemoteJoinerQuery = {
alias: propName,
fields: [],
expands: [],
}
if (rootFieldNode.arguments) {
remoteJoinConfig.args = this.parseArguments(rootFieldNode.arguments)
}
if (rootFieldNode.selectionSet) {
remoteJoinConfig.fields = rootFieldNode.selectionSet.selections.map(
(field) => (field as FieldNode).name.value
)
remoteJoinConfig.directives = this.createDirectivesMap(
rootFieldNode.selectionSet
)
remoteJoinConfig.expands = this.extractEntities(
rootFieldNode.selectionSet,
propName,
propName
)
}
return remoteJoinConfig
}
}
@@ -0,0 +1,93 @@
import { GraphQLNamedType, GraphQLObjectType, isObjectType } from "graphql"
/**
* From graphql schema get all the fields for the requested type and relations
*
* @param schemaTypeMap
* @param typeName
* @param relations
*
* @example
*
* const userModule = `
* type User {
* id: ID!
* name: String!
* blabla: WHATEVER
* }
*
* type Post {
* author: User!
* }
* `
*
* const postModule = `
* type Post {
* id: ID!
* title: String!
* date: String
* }
*
* type User {
* posts: [Post!]!
* }
*
* type WHATEVER {
* random_field: String
* post: Post
* }
* `
*
* const mergedSchema = mergeTypeDefs([userModule, postModule])
* const schema = makeExecutableSchema({
* typeDefs: mergedSchema,
* })
*
* const fields = graphqlSchemaToFields(types, "User", ["posts"])
*
* console.log(fields)
*
* // [
* // "id",
* // "name",
* // "posts.id",
* // "posts.title",
* // "posts.date",
* // ]
*/
export function graphqlSchemaToFields(
schemaTypeMap: { [key: string]: GraphQLNamedType },
typeName: string,
relations: string[] = []
) {
const result: string[] = []
function traverseFields(typeName, parent = "") {
const type = schemaTypeMap[typeName]
if (!(type instanceof GraphQLObjectType)) {
return
}
const fields = type.getFields()
for (const fieldName in fields) {
const field = fields[fieldName]
let fieldType = field.type as any
while (fieldType.ofType) {
fieldType = fieldType.ofType
}
const composedField = parent ? `${parent}.${fieldName}` : fieldName
if (!isObjectType(fieldType)) {
result.push(composedField)
} else if (relations.includes(composedField)) {
traverseFields(fieldType.name, composedField)
}
}
}
traverseFields(typeName)
return result
}
@@ -0,0 +1,126 @@
import { FileSystem, toCamelCase } from "@medusajs/utils"
import { GraphQLSchema } from "graphql/type"
import { parse, printSchema } from "graphql"
import { codegen } from "@graphql-codegen/core"
import * as typescriptPlugin from "@graphql-codegen/typescript"
import { ModuleJoinerConfig } from "@medusajs/types"
function buildEntryPointsTypeMap({
schema,
joinerConfigs,
}: {
schema: string
joinerConfigs: ModuleJoinerConfig[]
}): { entryPoint: string; entityType: any }[] {
// build map entry point to there type to be merged and used by the remote query
return joinerConfigs
.flatMap((config) => {
const aliases = Array.isArray(config.alias)
? config.alias
: config.alias
? [config.alias]
: []
return aliases.flatMap((alias) => {
const names = Array.isArray(alias.name) ? alias.name : [alias.name]
const entity = alias?.["entity"]
return names.map((aliasItem) => {
return {
entryPoint: aliasItem,
entityType: entity
? schema.includes(`export type ${entity} `)
? alias?.["entity"]
: "any"
: "any",
}
})
})
})
.filter(Boolean)
}
async function generateTypes({
outputDir,
filename,
config,
joinerConfigs,
}: {
outputDir: string
filename: string
config: Parameters<typeof codegen>[0]
joinerConfigs: ModuleJoinerConfig[]
}) {
const fileSystem = new FileSystem(outputDir)
let output = await codegen(config)
const entryPoints = buildEntryPointsTypeMap({ schema: output, joinerConfigs })
const interfaceName = toCamelCase(filename)
const remoteQueryEntryPoints = `
declare module '@medusajs/types' {
interface ${interfaceName} {
${entryPoints
.map((entry) => ` ${entry.entryPoint}: ${entry.entityType}`)
.join("\n")}
}
}`
output += remoteQueryEntryPoints
await fileSystem.create(filename + ".d.ts", output)
const doesBarrelExists = await fileSystem.exists("index.d.ts")
if (!doesBarrelExists) {
await fileSystem.create(
"index.d.ts",
`export * as ${interfaceName}Types from './${filename}'`
)
} else {
const content = await fileSystem.contents("index.d.ts")
if (!content.includes(`${interfaceName}Types`)) {
const newContent = `export * as ${interfaceName}Types from './${filename}'\n${content}`
await fileSystem.create("index.d.ts", newContent)
}
}
}
// TODO: rename from gqlSchemaToTypes to grapthqlToTsTypes
export async function gqlSchemaToTypes({
schema,
outputDir,
filename,
joinerConfigs,
}: {
schema: GraphQLSchema
outputDir: string
filename: string
joinerConfigs: ModuleJoinerConfig[]
}) {
const config = {
documents: [],
config: {
scalars: {
DateTime: { input: "Date | string", output: "Date | string" },
JSON: {
input: "Record<string, unknown>",
output: "Record<string, unknown>",
},
},
},
filename: "",
schema: parse(printSchema(schema as any)),
plugins: [
// Each plugin should be an object
{
typescript: {}, // Here you can pass configuration to the plugin
},
],
pluginMap: {
typescript: typescriptPlugin,
},
}
await generateTypes({ outputDir, filename, config, joinerConfigs })
}
+9
View File
@@ -0,0 +1,9 @@
export * from "./graphql-parser"
export * from "./graphql-to-fields"
export * from "./extract-relations-from-graphql"
export * from "./clean-graphql"
export * from "./graphql-to-ts-types"
export * from "./get-fields-and-relations"
export * from "graphql"
export * from "graphql/type"