UNPKG

uploadthing-codegen

Version:
100 lines (99 loc) 3.39 kB
#!/usr/bin/env node import { exec } from "child_process"; import fs from "fs"; import { writeFile } from "fs/promises"; import { createJiti } from "jiti"; import path from "path"; import process from "process"; import { Project } from "ts-morph"; import { promisify } from "util"; import { z } from "zod"; const codegenConfigZ = z .object({ routers: z.array(z.object({ schema: z.object({ endpoint: z.string(), headers: z.object({ Authorization: z.string(), }), }), generates: z.string(), })), hooks: z.object({ afterAllFileWrite: z.array(z.string()), }), }) .refine((data) => { const endpoints = new Set(data.routers.map((d) => d.schema.endpoint)); const generates = new Set(data.routers.map((d) => d.generates)); return (endpoints.size === data.routers.length && generates.size === data.routers.length); }); const endpointSchemaZ = z.array(z.object({ router: z.string(), endpoints: z.custom(), })); const __dirname = process.cwd(); const configFile = "codegen.ut.ts"; const configPath = path.join(__dirname, configFile); const jiti = createJiti(__dirname); const untypedConfig = await jiti.import(configPath, { default: true }); const { success, data: config, error, } = codegenConfigZ.safeParse(untypedConfig); if (!success) { console.error(error); process.exit(1); } console.log("✔ Parse Configuration"); for (const router of config.routers) { const res = await fetch(router.schema.endpoint, { headers: { Authorization: router.schema.headers.Authorization, }, }); if (!res.ok) { console.error(`Failed to fetch schema from ${router.schema.endpoint}\n${JSON.stringify(await res.json())}`); process.exit(1); } const untypedSchema = await res.json(); const { success, data: routers, error, } = endpointSchemaZ.safeParse(untypedSchema); if (!success) { console.error(error); process.exit(1); } const conjustedRouters = routers.reduce((acc, { endpoints, router }) => ({ ...acc, [router]: endpoints.reduce((acc, { config, slug }) => ({ ...acc, [slug]: config }), {}), }), {}); const generatedPath = path.join(__dirname, router.generates); const project = new Project(); const sourceFile = project.createSourceFile(generatedPath, "", { overwrite: true, }); sourceFile.addTypeAlias({ isExported: true, name: "UploadthingRouter", type: JSON.stringify(conjustedRouters, null, 2), }); sourceFile.addTypeAlias({ isExported: true, name: "Endpoint", type: Object.keys(conjustedRouters) .map((router) => Object.keys(conjustedRouters[router]) .map((endpoint) => `"${router}/${endpoint}"`) .join(" | ")) .join(" | "), }); const dir = path.dirname(generatedPath); if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true }); await writeFile(generatedPath, sourceFile.getFullText()); for (const command of config.hooks.afterAllFileWrite) { const { stderr } = await promisify(exec)(`${command} ${router.generates}`); if (stderr) { console.error(error); process.exit(1); } } } console.log("✔ Generate Outputs"); process.exit(0);