Skip to content

Instantly share code, notes, and snippets.

@hayes
Last active April 25, 2025 04:35
Show Gist options
  • Save hayes/5ddff574971922a13346c07dca234c27 to your computer and use it in GitHub Desktop.
Save hayes/5ddff574971922a13346c07dca234c27 to your computer and use it in GitHub Desktop.
locate pothos from stacktraces
// 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;
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