Last active
April 25, 2025 04:35
-
-
Save hayes/5ddff574971922a13346c07dca234c27 to your computer and use it in GitHub Desktop.
locate pothos from stacktraces
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Copyright (c) Mysten Labs, Inc. | |
// SPDX-License-Identifier: Apache-2.0 | |
import type { IGraphQLConfig } from 'graphql-config'; | |
// @ts-ignore | |
import { spawn, execSync } from 'child_process'; | |
const locateServer = spawn('bun', ['--watch', './packages/graphql/src/locate.ts'], { | |
stdio: 'inherit', | |
}); | |
// @ts-ignore | |
process.on('exit', () => { | |
locateServer.kill(); | |
}); | |
const config: IGraphQLConfig = { | |
projects: { | |
portal: { | |
schema: './schema.graphql', | |
documents: ['...'], | |
include: ['...'], | |
extensions: { | |
languageService: { | |
cacheSchemaForLookup: false, | |
locateCommand: (_projectName: string, typeName: string) => { | |
try { | |
const stdout = execSync( | |
`curl -s -X POST -H "Content-Type: application/json" -d '{"query":"{locate(typeName:\\"${typeName}\\")}"}' http://localhost:${0x10c8}/graphql`, | |
); | |
const location = JSON.parse(stdout.toString())?.data?.locate; | |
const [uri, line, character] = location.split(':'); | |
return { | |
uri, | |
range: { | |
start: { | |
line: Number.parseInt(line, 10) - 1, | |
character: Number.parseInt(character, 10) - 1, | |
}, | |
end: { | |
line: Number.parseInt(line, 10) - 1, | |
character: Number.parseInt(character, 10) - 1, | |
}, | |
}, | |
}; | |
} catch (error) { | |
console.error(error); | |
return null; | |
} | |
}, | |
}, | |
}, | |
}, | |
}, | |
}; | |
export default config; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { createYoga } from 'graphql-yoga'; | |
import SchemaBuilder, { type BaseTypeRef, type SchemaTypes, RootFieldBuilder } from '@pothos/core'; | |
import { isInterfaceType, isObjectType } from 'graphql'; | |
// @ts-ignore some plugin options are expected here | |
const builder = new SchemaBuilder({}); | |
const ConfigStoreProto = Object.getPrototypeOf(builder.configStore); | |
const originalAddTypeRef = ConfigStoreProto.addTypeRef; | |
ConfigStoreProto.addTypeRef = function addTypeRef( | |
this: typeof ConfigStoreProto, | |
ref: BaseTypeRef< | |
SchemaTypes, | |
{ | |
extensions?: Record<string, unknown>; | |
} | |
>, | |
) { | |
const result = originalAddTypeRef.call(this, ref as never); | |
const sourceLocationError = new Error('Source location error'); | |
ref.onConfig((config) => { | |
config.extensions = { | |
...config.extensions, | |
sourceLocationError, | |
}; | |
}); | |
return result; | |
} as never; | |
const BaseFieldUtilProto = Object.getPrototypeOf(RootFieldBuilder).prototype as unknown as { | |
createField: (options: PothosSchemaTypes.FieldOptions<SchemaTypes, unknown, {}>) => unknown; | |
}; | |
const originalCreateField = BaseFieldUtilProto.createField; | |
BaseFieldUtilProto.createField = function createField( | |
options: PothosSchemaTypes.FieldOptions<SchemaTypes, unknown>, | |
) { | |
return originalCreateField.call(this, { | |
...options, | |
extensions: { | |
...options.extensions, | |
sourceLocationError: new Error('Source location error'), | |
}, | |
} as never); | |
}; | |
const { schema } = await import('./schema'); | |
builder.queryType({ | |
fields: (t) => ({ | |
locate: t.string({ | |
args: { | |
typeName: t.arg.string({ required: true }), | |
}, | |
resolve: (_, { typeName }) => { | |
const [type, field] = typeName.split('.'); | |
const fieldName = field?.split('(')[0]; | |
const gqlType = schema.getType(type); | |
if (!gqlType) { | |
return null; | |
} | |
let error: Error | undefined; | |
if (field) { | |
if (!(isObjectType(gqlType) || isInterfaceType(gqlType))) { | |
return null; | |
} | |
const gqlField = gqlType.getFields()[fieldName]; | |
error = gqlField.extensions.sourceLocationError as Error | undefined; | |
} | |
// Fallback to type if the field is not found | |
if (!error) { | |
error = gqlType.extensions.sourceLocationError as Error | undefined; | |
} | |
if (!error) { | |
return null; | |
} | |
return extractLocation(error); | |
}, | |
}), | |
}), | |
}); | |
const yoga = createYoga({ | |
schema: builder.toSchema(), | |
}); | |
// @ts-ignore bun types are not configured for this package | |
const server = Bun.serve({ | |
port: 0x10c8, | |
fetch: yoga, | |
}); | |
console.log(`locate server is running on ${server.url}graphql`); | |
function extractLocation(error: Error) { | |
console.log(error.stack); | |
const lineWithPath = /at .*\(((?=\/).*)\)/; | |
console.log(error); | |
return error | |
.stack!.split('\n') | |
.map((line) => lineWithPath.exec(line)?.[1]) | |
.filter( | |
(line) => | |
line && | |
!line.includes('@pothos/') && | |
!line.includes('locate.ts') && | |
!line.includes('node_modules'), | |
)[0]; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment