feat: add graphql
This commit is contained in:
119
scripts/endpoint-generate.mjs
Normal file
119
scripts/endpoint-generate.mjs
Normal file
@@ -0,0 +1,119 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Usage: npm run endpoint:generate -- [slug]
|
||||
* Example: npm run endpoint:generate -- home-page
|
||||
* Expects src/.../slug.gql.ts with export const name = gql`...`
|
||||
* Runs graphql-codegen, then writes slug.gql.generated.ts beside it.
|
||||
*/
|
||||
|
||||
import { execSync } from "node:child_process";
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
const root = path.resolve(__dirname, "..");
|
||||
|
||||
const slug = process.argv[2];
|
||||
if (!slug || slug.startsWith("-")) {
|
||||
console.error("Usage: npm run endpoint:generate -- <slug>");
|
||||
console.error('Example: npm run endpoint:generate -- home-page');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
function findGqlFile() {
|
||||
const exact = path.join(root, "src", `${slug}.gql.ts`);
|
||||
if (fs.existsSync(exact)) return path.relative(root, exact);
|
||||
|
||||
const stack = [path.join(root, "src")];
|
||||
while (stack.length) {
|
||||
const dir = stack.pop();
|
||||
if (!fs.existsSync(dir)) continue;
|
||||
for (const name of fs.readdirSync(dir, { withFileTypes: true })) {
|
||||
const p = path.join(dir, name.name);
|
||||
if (name.isDirectory()) stack.push(p);
|
||||
else if (name.name === `${slug}.gql.ts`) return path.relative(root, p);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
const gqlPath = findGqlFile();
|
||||
if (!gqlPath) {
|
||||
console.error(`No file named "${slug}.gql.ts" found under src/`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const absGql = path.join(root, gqlPath);
|
||||
const source = fs.readFileSync(absGql, "utf8");
|
||||
|
||||
const exportMatch = source.match(/export\s+const\s+(\w+)\s*=\s*gql\s*`/);
|
||||
if (!exportMatch) {
|
||||
console.error(`${gqlPath}: expected: export const SomeName = gql\`...\``);
|
||||
process.exit(1);
|
||||
}
|
||||
const documentExport = exportMatch[1];
|
||||
|
||||
const opMatch = source.match(/\b(query|mutation|subscription)\s+(\w+)\s*[\({]/);
|
||||
if (!opMatch) {
|
||||
console.error(`${gqlPath}: could not find operation name (query|mutation|subscription Name)`);
|
||||
process.exit(1);
|
||||
}
|
||||
const operationKind = opMatch[1];
|
||||
const operationName = opMatch[2];
|
||||
|
||||
const resultType =
|
||||
operationKind === "mutation"
|
||||
? `${operationName}Mutation`
|
||||
: operationKind === "subscription"
|
||||
? `${operationName}Subscription`
|
||||
: `${operationName}Query`;
|
||||
const variablesType = `${resultType}Variables`;
|
||||
|
||||
const endpointName =
|
||||
operationKind === "mutation"
|
||||
? operationName[0].toLowerCase() + operationName.slice(1) + "Mutation"
|
||||
: operationKind === "subscription"
|
||||
? operationName[0].toLowerCase() + operationName.slice(1) + "Subscription"
|
||||
: operationName[0].toLowerCase() + operationName.slice(1) + "Query";
|
||||
|
||||
const outPath = absGql.replace(/\.gql\.ts$/, ".gql.generated.ts");
|
||||
const outRel = path.relative(root, outPath);
|
||||
|
||||
const gqlDir = path.dirname(absGql);
|
||||
const executeRel = path
|
||||
.relative(gqlDir, path.join(root, "src", "graphql", "execute.ts"))
|
||||
.replace(/\\/g, "/")
|
||||
.replace(/\.ts$/, "");
|
||||
const typesRel = path
|
||||
.relative(gqlDir, path.join(root, "src", "graphql", "graphql.ts"))
|
||||
.replace(/\\/g, "/")
|
||||
.replace(/\.ts$/, "");
|
||||
const gqlBase = path.basename(gqlPath);
|
||||
const gqlImportStem = gqlBase.replace(/\.ts$/, "");
|
||||
|
||||
console.log("Running graphql-codegen…");
|
||||
execSync("npx graphql-codegen", { cwd: root, stdio: "inherit" });
|
||||
|
||||
function quoteImport(rel) {
|
||||
if (rel.startsWith(".")) return rel;
|
||||
return `./${rel}`;
|
||||
}
|
||||
|
||||
const body = `/* eslint-disable */
|
||||
/* Generated by scripts/endpoint-generate.mjs — do not edit by hand */
|
||||
import type { ExecutionResult } from "graphql";
|
||||
|
||||
import { execute } from "${quoteImport(executeRel)}";
|
||||
import type { ${resultType}, ${variablesType} } from "${quoteImport(typesRel)}";
|
||||
import { ${documentExport} } from "./${gqlImportStem}";
|
||||
|
||||
export const ${endpointName} = {
|
||||
async execute(variables?: ${variablesType}): Promise<ExecutionResult<${resultType}>> {
|
||||
return execute<${resultType}, ${variablesType}>(${documentExport}, variables);
|
||||
},
|
||||
};
|
||||
`;
|
||||
|
||||
fs.writeFileSync(outPath, body, "utf8");
|
||||
console.log(`Wrote ${outRel}`);
|
||||
Reference in New Issue
Block a user