Description
Hello 👋
I'm playing around with .meta
in trpc-cli, and had a thought about how it might be useful to use it when creating CLIs. title
and description
are already used for positional parameter names and helptext. But right now, for defining shortopts users have to rely on trpc's .meta
method on the procedure, which is a little awkward. This is what I'd like to do in future:
declare module '../node_modules/.pnpm/@zod+core@0.5.0/node_modules/@zod/core/dist/commonjs/registries.d.ts' {
interface GlobalMeta {
alias?: string
}
}
test('zod4 meta', async () => {
const router = t.router({
add: t.procedure
.input(
z.tuple([
z.string().meta({
title: 'module',
description: 'The name of the module to addd',
}),
z.object({
saveDev: z.boolean().meta({alias: 'D'}),
saveExact: z.boolean().meta({alias: 'E'}),
}),
]),
)
.mutation(async ({input: [name, opts]}) => {
return JSON.stringify([`installing ${name} with opts:`, opts, '...'])
}),
})
expect(await run(router, ['add', 'left-pad'])).toMatchInlineSnapshot(
`"["installing left-pad with opts:",{"saveDev":false,"saveExact":false},"..."]"`,
)
expect(await run(router, ['add', 'left-pad', '--save-dev', '--save-exact'])).toMatchInlineSnapshot(
`"["installing left-pad with opts:",{"saveDev":true,"saveExact":true},"..."]"`,
)
expect(await run(router, ['add', 'left-pad', '-D', '-E'])).toMatchInlineSnapshot(
`"["installing left-pad with opts:",{"saveDev":true,"saveExact":true},"..."]"`,
)
expect(await run(router, ['add', 'left-pad', '-DE'])).toMatchInlineSnapshot(
`"["installing left-pad with opts:",{"saveDev":true,"saveExact":true},"..."]"`,
)
})
(run(...)
is a helper method that roughly acts like doing node mycli.js add left-pad --save-dev --save-exact
)
The zod-related part is the module augmentation that adds alias?: string
to zod's GlobalMeta. I know it would be possible to use a custom registry, but it seems overkill for this - especially given this is only for type safety. Users can already do .meta({alias: 'D'})
and everything just works.
The problem with the above is the fact that GlobalMeta
is part of @zod/core
, not zod
, so I have to use an installation-specific relative path - not very useful if I'm publishing a package. I think if GlobalMeta
was part of zod
I could just do:
declare module 'zod' {
interface GlobalMeta {
alias?: string
}
}
I'm not sure exactly the best way to do that - is zod
now just a thin wrapper for @zod/core
?