Skip to content

Commit

Permalink
update parser
Browse files Browse the repository at this point in the history
  • Loading branch information
danstarns committed Sep 1, 2024
1 parent 8579d1d commit 8cb3a1c
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 23 deletions.
6 changes: 4 additions & 2 deletions packages/cli/src/commands/generate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,12 @@ generate

console.log("Parsing files for GQLPT usage...");
const queries = await parseFiles(files);
console.log(`Found ${queries.length} unique GQLPT queries`);

console.log("Generating type definitions...");
const typesContent = await generateTypes({ queries, client });
const typesContent = await generateTypes({
queries: queries.map((x) => x.query),
client,
});

console.log(`Writing type definitions to ${outputPath}`);
await fs.writeFile(outputPath, typesContent);
Expand Down
101 changes: 83 additions & 18 deletions packages/cli/src/parser.ts
Original file line number Diff line number Diff line change
@@ -1,37 +1,102 @@
import { parse } from "@typescript-eslint/typescript-estree";
import {
AST_NODE_TYPES,
TSESTree,
parse,
} from "@typescript-eslint/typescript-estree";
import fs from "fs/promises";

export async function parseFiles(files: string[]): Promise<string[]> {
const queries: string[] = [];
interface QueryInfo {
query: string;
location: { line: number; column: number };
}

export async function parseFiles(files: string[]): Promise<QueryInfo[]> {
const queries: QueryInfo[] = [];

for (const file of files) {
console.log(`Parsing file: ${file}`);
const content = await fs.readFile(file, "utf-8");
const ast = parse(content, {
jsx: true,
range: true,
});
const ast = parse(content, { jsx: true, loc: true });

const variableMap = new Map<string, string>();

traverseAST(ast, (node) => {
if (
node.type === "CallExpression" &&
node.callee.type === "MemberExpression" &&
node.callee.property.type === "Identifier" &&
node.callee.property.name === "generateAndSend" &&
node.arguments[0]?.type === "Literal"
node.type === AST_NODE_TYPES.VariableDeclarator &&
node.id.type === AST_NODE_TYPES.Identifier &&
node.init?.type === AST_NODE_TYPES.Literal &&
typeof node.init.value === "string"
) {
queries.push(node.arguments[0].value as string);
variableMap.set(node.id.name, node.init.value);
}

if (isGenerateAndSendCall(node)) {
const arg = node.arguments[0];
if (
arg.type === AST_NODE_TYPES.Literal &&
typeof arg.value === "string"
) {
queries.push({
query: arg.value,
location: arg.loc!.start,
});
} else if (arg.type === AST_NODE_TYPES.Identifier) {
const variableValue = variableMap.get(arg.name);
if (variableValue) {
queries.push({
query: variableValue,
location: arg.loc!.start,
});
}
}
}
});
}

console.log(`Total queries found: ${queries.length}`);
queries.forEach((q) =>
console.log(
`Query: "${q.query}" at line ${q.location.line}, column ${q.location.column}`,
),
);
return queries;
}

function traverseAST(node: any, callback: (node: any) => void) {
function isGenerateAndSendCall(
node: TSESTree.Node,
): node is TSESTree.CallExpression {
return (
node.type === AST_NODE_TYPES.CallExpression &&
node.callee.type === AST_NODE_TYPES.MemberExpression &&
node.callee.property.type === AST_NODE_TYPES.Identifier &&
node.callee.property.name === "generateAndSend"
);
}

function traverseAST(
node: TSESTree.Node,
callback: (node: TSESTree.Node) => void,
) {
callback(node);
for (const key in node) {
if (typeof node[key] === "object" && node[key] !== null) {
traverseAST(node[key], callback);
}

if (node.type === AST_NODE_TYPES.Program) {
node.body.forEach((child) => traverseAST(child, callback));
} else if ("body" in node && Array.isArray(node.body)) {
node.body.forEach((child) => traverseAST(child, callback));
} else {
Object.keys(node).forEach((key) => {
const child = (node as any)[key];
if (typeof child === "object" && child !== null) {
if (Array.isArray(child)) {
child.forEach((item) => {
if (typeof item === "object" && item !== null) {
traverseAST(item as TSESTree.Node, callback);
}
});
} else {
traverseAST(child as TSESTree.Node, callback);
}
}
});
}
}
6 changes: 3 additions & 3 deletions packages/gqlpt/tests/remote-schema.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,17 +65,17 @@ adapters.forEach(({ name, adapter }) => {
expect(print(parse(generatedTypeDefs))).toEqual(print(parse(typeDefs)));
});

test("should generateAndSend", async () => {
test("should generateAndSend with inline", async () => {
const gqlpt = new GQLPTClient({
adapter,
url: "http://localhost:4000/graphql",
});

await gqlpt.connect();

const resonse = await gqlpt.generateAndSend("Find users by id 1");
const response = await gqlpt.generateAndSend("Find users by id 1");

expect(resonse).toEqual({
expect(response).toEqual({
errors: undefined,
data: {
user: resolvers.Query.user(undefined, { id: "1" }),
Expand Down

0 comments on commit 8cb3a1c

Please sign in to comment.