import { writeFile } from "node:fs/promises"; import { Client } from "@olenbetong/appframe-data"; import { buildYamlConfig, type CLIOptions, fetchAndGenerate, formatWithBiome, } from "@olenbetong/appframe-vite/resources"; import { config } from "dotenv"; import inquirer from "inquirer"; import { Command } from "./lib/Command.js"; import { importJson } from "./lib/importJson.js"; import { Server } from "./lib/Server.js"; config({ path: `${process.cwd()}/.env`, quiet: true }); const appPkg = await importJson("../package.json"); async function getResourceDefinition(resourceName: string, options: CLIOptions & { server: string }) { if (options.fields === true) { // Need to fetch the resource to show the interactive field picker let server = new Server(options.server); let resource = await server.getResourceArgument(resourceName); let definition = await server.getResourceDefinition(resource); let response = await inquirer.prompt([ { type: "checkbox", name: "fields", message: "Select fields", choices: definition.Parameters.map((p: any) => ({ value: p.Name, })), }, ]); options.fields = response.fields.join(","); } let server = new Server(options.server); let resource = await server.getResourceArgument(resourceName); let { APPFRAME_LOGIN: username = "", APPFRAME_PWD: password = "" } = process.env; let client = new Client(options.server); await client.login(username, password); let content = await fetchAndGenerate(resource, options, client); if (options.output) { let header = buildYamlConfig(resource, options); await writeFile(options.output, `${header}\n${content}`, "utf-8"); await formatWithBiome(options.output); console.log(`Written to ${options.output}`); } else { let command: string[] = [`af resources generate ${resource}`]; if (options.id) command.push(`--id ${options.id}`); if (options.unique) command.push(`--unique ${options.unique}`); if (options.global) command.push("--global"); if (options.types) command.push("--types"); if (options.maxRecords) command.push(`--max-records ${options.maxRecords}`); if (options.sortOrder) command.push(`--sort-order ${options.sortOrder}`); if (options.permissions) command.push(`--permissions ${options.permissions}`); if (options.master) command.push(`--master ${options.master}`); if (options.linkFields) command.push(`--link-fields ${options.linkFields}`); if (options.expose) command.push(`--expose${typeof options.expose === "string" ? ` ${options.expose}` : ""}`); if (options.dynamic) command.push("--dynamic"); if (options.overrides) command.push(`--overrides "${options.overrides}"`); if (options.distinct) command.push("--distinct"); if (options.aggregates) command.push(`--aggregates ${options.aggregates}`); if (options.groupBy) command.push(`--group-by ${options.groupBy}`); if (options.where) command.push(`--where "${options.where}"`); if (options.fields) command.push(`--fields ${options.fields}`); console.log(`/*\nauto-generated by CLI:\n${command.join(" \\\n ")}\n*/`); console.log(content); } } const program = new Command(); program .version(appPkg.version) .addResourceArgument() .option("-i, --id ", "ID to use as name for the data object", "dsDataObject") .option("-g, --global", "Use af.data globals to generate the data object") .option("-t, --types", "Include data object types") .option("-f, --fields [fields]", "Comma-separated list of fields to include") .option( "-p, --permissions ", "Permission to set on the data object (I = insert, U = update and D = delete; can be combined, for example IUD for all permissions)", ) .option("-m, --max-records ", "Max records to fetch", "50") .option("-b, --master ", "Name of data object to set as master data object") .option( "-l, --link-fields ", "Comma separated list of fields to bind to master data object. Either name of field that is in both data objects, or : (e.g. MainGroup_ID:ID)", ) .option( "-s, --sort-order ", "Comma separated list of sort orders with format :, e.g. Created:Desc, or just field name for ascending sort", ) .option("-d, --dynamic", "Flag to make the data object dynamic loading") .option( "-e, --expose [id]", "Expose the data object on global af.article.dataObjects. If id is not given, the data object ID is used", ) .option("-u, --unique ", "Unique table name to use when performing updates/delete") .option( "-o, --overrides ", "Comma separated list of field/parameter type overrides with format :. E.g. BuildSites:[string][]", ) .option("--group-by ", "Comma separated list of fields to group by") .option( "--aggregates ", "Comma separated list of aggregate binding for non-grouped fields, e.g. Quantity:SUM,Created:MAX", ) .option("--distinct", "Data object should fetch distinct data") .option("--where ", "Initial where clause to set on the data object") .option( "-O, --output ", "Write output to this file instead of stdout; also sets the correct relative import path for custom.d.ts", ) .action(getResourceDefinition); await program.parseAsync(process.argv);