|
| 1 | +import { FnClient } from '@constructive-io/fn-client'; |
| 2 | +import type { ParsedArgs } from 'minimist'; |
| 3 | + |
| 4 | +export type CommandFn = (args: ParsedArgs) => number | Promise<number>; |
| 5 | + |
| 6 | +const buildClient = (args: ParsedArgs): FnClient => |
| 7 | + new FnClient({ |
| 8 | + rootDir: typeof args.root === 'string' ? args.root : undefined, |
| 9 | + config: typeof args.config === 'string' ? args.config : undefined, |
| 10 | + }); |
| 11 | + |
| 12 | +const cmdGenerate: CommandFn = (args) => { |
| 13 | + const client = buildClient(args); |
| 14 | + const result = client.generate({ |
| 15 | + only: typeof args.only === 'string' ? args.only : undefined, |
| 16 | + packagesOnly: Boolean(args['packages-only']), |
| 17 | + }); |
| 18 | + process.stdout.write( |
| 19 | + `Generated ${result.functions.length} function(s); wrote ${result.filesWritten.length} file(s), ${result.symlinksCreated.length} symlink(s).\n` |
| 20 | + ); |
| 21 | + for (const f of result.filesWritten) process.stdout.write(` + ${f}\n`); |
| 22 | + for (const s of result.symlinksCreated) process.stdout.write(` ~ ${s}\n`); |
| 23 | + return 0; |
| 24 | +}; |
| 25 | + |
| 26 | +const cmdBuild: CommandFn = async (args) => { |
| 27 | + const client = buildClient(args); |
| 28 | + await client.build({ only: typeof args.only === 'string' ? args.only : undefined }); |
| 29 | + return 0; |
| 30 | +}; |
| 31 | + |
| 32 | +const cmdDev: CommandFn = (args) => { |
| 33 | + const client = buildClient(args); |
| 34 | + const handle = client.dev({ only: typeof args.only === 'string' ? args.only : undefined }); |
| 35 | + const stop = (): void => { |
| 36 | + handle.stop().finally(() => process.exit(0)); |
| 37 | + }; |
| 38 | + process.on('SIGINT', stop); |
| 39 | + process.on('SIGTERM', stop); |
| 40 | + process.stdout.write(`Started: ${Object.keys(handle.pids).join(', ')}\n`); |
| 41 | + // Hold the event loop until a signal arrives. |
| 42 | + return new Promise<number>(() => {}); |
| 43 | +}; |
| 44 | + |
| 45 | +const cmdManifest: CommandFn = (args) => { |
| 46 | + const client = buildClient(args); |
| 47 | + const m = client.loadManifest(); |
| 48 | + if (!m) { |
| 49 | + process.stderr.write('functions-manifest.json not found. Run `fn generate` first.\n'); |
| 50 | + return 1; |
| 51 | + } |
| 52 | + process.stdout.write(JSON.stringify(m, null, 2) + '\n'); |
| 53 | + return 0; |
| 54 | +}; |
| 55 | + |
| 56 | +const cmdVerify: CommandFn = (args) => { |
| 57 | + const client = buildClient(args); |
| 58 | + const fns = client.discover(); |
| 59 | + const manifest = client.loadManifest(); |
| 60 | + if (!manifest) { |
| 61 | + process.stderr.write('functions-manifest.json not found. Run `fn generate` first.\n'); |
| 62 | + return 1; |
| 63 | + } |
| 64 | + const expected = new Set(fns.map((f) => f.name)); |
| 65 | + const actual = new Set(manifest.functions.map((f) => f.name)); |
| 66 | + const missing = [...expected].filter((n) => !actual.has(n)); |
| 67 | + const extra = [...actual].filter((n) => !expected.has(n)); |
| 68 | + if (missing.length === 0 && extra.length === 0) { |
| 69 | + process.stdout.write(`OK: ${fns.length} function(s) in sync.\n`); |
| 70 | + return 0; |
| 71 | + } |
| 72 | + if (missing.length) process.stderr.write(`Missing in manifest: ${missing.join(', ')}\n`); |
| 73 | + if (extra.length) process.stderr.write(`Stale in manifest: ${extra.join(', ')}\n`); |
| 74 | + return 2; |
| 75 | +}; |
| 76 | + |
| 77 | +export const commands: Record<string, CommandFn> = { |
| 78 | + generate: cmdGenerate, |
| 79 | + build: cmdBuild, |
| 80 | + dev: cmdDev, |
| 81 | + manifest: cmdManifest, |
| 82 | + verify: cmdVerify, |
| 83 | +}; |
0 commit comments