Skip to content

Commit

Permalink
more better docs
Browse files Browse the repository at this point in the history
  • Loading branch information
mmkal committed May 24, 2024
1 parent 251c43b commit 6f3c413
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 18 deletions.
28 changes: 26 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ Turn a [tRPC](https://trpc.io) router into a type-safe, fully-functional, docume
- [Positional parameters](#positional-parameters)
- [Flags](#flags)
- [Both](#both)
- [API docs](#api-docs)
- [trpcCli](#trpccli)
- [Params](#params)
- [Returns](#returns)
- [Calculator example](#calculator-example)
- [Output and lifecycle](#output-and-lifecycle)
- [Features and Limitations](#features-and-limitations)
Expand Down Expand Up @@ -146,6 +150,26 @@ path/to/cli copy a.txt b.txt --mkdirp
Procedures with incompatible inputs will be returned in the `ignoredProcedures` property.

### API docs

<!-- codegen:start {preset: markdownFromJsdoc, source: src/index.ts, export: trpcCli} -->
#### [trpcCli](./src/index.ts#L27)

Run a trpc router as a CLI.

##### Params

|name |description |
|-------|-----------------------------------------------------------------------------------------|
|router |A trpc router |
|context|The context to use when calling the procedures - needed if your router requires a context|
|alias |A function that can be used to provide aliases for flags. |

##### Returns

A CLI object with a `run` method that can be called to run the CLI. The `run` method will parse the command line arguments, call the appropriate trpc procedure, log the result and exit the process. On error, it will log the error and exit with a non-zero exit code.
<!-- codegen:end -->

### Calculator example

Here's a more involved example, along with what it outputs:
Expand Down Expand Up @@ -356,7 +380,7 @@ You could also override `process.exit` to avoid killing the process at all - see
Given a migrations router looking like this:

<!-- codegen:start {preset: custom, require: tsx/cjs, source: ./readme-codegen.ts, export: dump, file: test/fixtures/migrations.ts} -->
<!-- hash:99a33a561c51e15dddf7c57da43d3b72 -->
<!-- hash:8635f80f9309a63813b659a227270b73 -->
```ts
import * as trpcServer from '@trpc/server'
import {trpcCli, type TrpcCliMeta} from 'trpc-cli'
Expand Down Expand Up @@ -421,7 +445,7 @@ const router = trpc.router({
create: trpc.procedure
.meta({description: 'Create a new migration'})
.input(
z.object({name: z.string(), content: z.string(), bb: z.boolean()}), //
z.object({name: z.string(), content: z.string()}), //
)
.mutation(async ({input}) => {
migrations.push({...input, status: 'pending'})
Expand Down
39 changes: 24 additions & 15 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,23 +12,32 @@ import {parseProcedureInputs} from './zod-procedure'
export * from './types'

// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const trpcCli = <R extends Router<any>>({router, context, alias}: TrpcCliParams<R>) => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const procedures = Object.entries<Procedure<any, any>>(router._def.procedures as {}).map(
([commandName, procedure]) => {
const procedureResult = parseProcedureInputs(procedure)
if (!procedureResult.success) {
return [commandName, procedureResult.error] as const
}
type AnyRouter = Router<any>
// eslint-disable-next-line @typescript-eslint/no-explicit-any
type AnyProcedure = Procedure<any, any>

/**
* Run a trpc router as a CLI.
*
* @param router A trpc router
* @param context The context to use when calling the procedures - needed if your router requires a context
* @param alias A function that can be used to provide aliases for flags.
* @returns A CLI object with a `run` method that can be called to run the CLI. The `run` method will parse the command line arguments, call the appropriate trpc procedure, log the result and exit the process. On error, it will log the error and exit with a non-zero exit code.
*/
export const trpcCli = <R extends AnyRouter>({router, context, alias}: TrpcCliParams<R>) => {
const procedures = Object.entries<AnyProcedure>(router._def.procedures as {}).map(([commandName, procedure]) => {
const procedureResult = parseProcedureInputs(procedure)
if (!procedureResult.success) {
return [commandName, procedureResult.error] as const
}

const jsonSchema = procedureResult.value
const properties = flattenedProperties(jsonSchema.flagsSchema)
const incompatiblePairs = incompatiblePropertyPairs(jsonSchema.flagsSchema)
const type = router._def.procedures[commandName]._def.mutation ? 'mutation' : 'query'
const jsonSchema = procedureResult.value
const properties = flattenedProperties(jsonSchema.flagsSchema)
const incompatiblePairs = incompatiblePropertyPairs(jsonSchema.flagsSchema)
const type = router._def.procedures[commandName]._def.mutation ? 'mutation' : 'query'

return [commandName, {procedure, jsonSchema, properties, incompatiblePairs, type}] as const
},
)
return [commandName, {procedure, jsonSchema, properties, incompatiblePairs, type}] as const
})

const procedureEntries = procedures.flatMap(([k, v]) => {
return typeof v === 'string' ? [] : [[k, v] as const]
Expand Down
9 changes: 9 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,17 @@ import {type JsonSchema7Type} from 'zod-to-json-schema'

// eslint-disable-next-line @typescript-eslint/no-explicit-any
export type TrpcCliParams<R extends Router<any>> = {
/** A tRPC router. Procedures will become CLI commands. */
router: R
/** Context to be supplied when invoking the router. */
context?: inferRouterContext<R>
/**
* A function that will be called for every flag, for every command. Used to provide single-character aliases for flags.
* Return a single-character string to alias a flag to that character.
* @param fullName The full-length name of the flag
* @param meta Metadata about the command and flags. Includes the command name and all the other flags for the command (so you can avoid clashes you might get with `return fullName[0]`).
* @returns A single-letter string to alias the flag to that character, or `void`/`undefined` to not alias the flag.
*/
alias?: (fullName: string, meta: {command: string; flags: Record<string, unknown>}) => string | undefined
}
/**
Expand Down
2 changes: 1 addition & 1 deletion test/fixtures/migrations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ const router = trpc.router({
create: trpc.procedure
.meta({description: 'Create a new migration'})
.input(
z.object({name: z.string(), content: z.string(), bb: z.boolean()}), //
z.object({name: z.string(), content: z.string()}), //
)
.mutation(async ({input}) => {
migrations.push({...input, status: 'pending'})
Expand Down

0 comments on commit 6f3c413

Please sign in to comment.