chore: Move graphl to a single place (#9303)
* chore: Move graphl to a single place * add new line
This commit is contained in:
@@ -1,91 +0,0 @@
|
||||
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,
|
||||
}
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
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
|
||||
}
|
||||
@@ -1,118 +0,0 @@
|
||||
import { MedusaModule } from "../medusa-module"
|
||||
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"
|
||||
|
||||
function buildEntryPointsTypeMap(
|
||||
schema: string
|
||||
): { entryPoint: string; entityType: any }[] {
|
||||
// build map entry point to there type to be merged and used by the remote query
|
||||
|
||||
const joinerConfigs = MedusaModule.getAllJoinerConfigs()
|
||||
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,
|
||||
}: {
|
||||
outputDir: string
|
||||
filename: string
|
||||
config: Parameters<typeof codegen>[0]
|
||||
}) {
|
||||
const fileSystem = new FileSystem(outputDir)
|
||||
|
||||
let output = await codegen(config)
|
||||
const entryPoints = buildEntryPointsTypeMap(output)
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export async function gqlSchemaToTypes({
|
||||
schema,
|
||||
outputDir,
|
||||
filename,
|
||||
}: {
|
||||
schema: GraphQLSchema
|
||||
outputDir: string
|
||||
filename: string
|
||||
}) {
|
||||
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 })
|
||||
}
|
||||
@@ -1,93 +0,0 @@
|
||||
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
|
||||
}
|
||||
@@ -1,4 +1,2 @@
|
||||
export * from "./clean-graphql-schema"
|
||||
export * from "./graphql-schema-to-fields"
|
||||
export * from "./gql-schema-to-types"
|
||||
export * from "./gql-get-fields-and-relations"
|
||||
export * from "./linking-error"
|
||||
export * from "./convert-data-to-link-definition"
|
||||
|
||||
Reference in New Issue
Block a user