unjs / citty Goto Github PK
View Code? Open in Web Editor NEW๐ Elegant CLI Builder
License: Other
๐ Elegant CLI Builder
License: Other
fig.io provides some additional cli's that they generate a spec for but I really do think the citty DX will be brought to a new level in combination.
Helpful links: https://fig.io/docs/guides/integrating/getting-started
Commander.js
Spec Generator: https://www.npmjs.com/package/@fig/complete-commander?activeTab=code
Node: v19.8.1
citty: 0.1.0
Run the following command: pnpm play build --dir="directory" entryArg dstArg
The expected behavior is that the entryArg and dstArg arguments are parsed and returned by order of their position.
The actual behavior is an error message: EARG Missing required positional argument: ENTRY.
No response
No response
This issue lists Renovate updates and detected dependencies. Read the Dependency Dashboard docs to learn more.
These updates have all been created already. Click a checkbox below to force a retry/rebase of any.
@types/node
, @vitest/coverage-v8
, automd
, eslint-config-unjs
, jiti
, pnpm
, prettier
, typescript
, vitest
)@vitest/coverage-v8
, vitest
).github/workflows/autofix.yml
actions/checkout v4
actions/setup-node v4
autofix-ci/action ff86a557419858bb967097bfc916833f5647fa8c
.github/workflows/ci.yml
actions/checkout v4
actions/setup-node v4
codecov/codecov-action v4
package.json
consola ^3.2.3
@types/node ^20.12.7
@vitest/coverage-v8 ^1.5.2
automd ^0.3.7
changelogen ^0.5.5
eslint ^8.57.0
eslint-config-unjs ^0.2.1
jiti ^1.21.0
prettier ^3.2.5
scule ^1.3.0
typescript ^5.4.5
unbuild ^2.0.0
vitest ^1.5.2
pnpm 9.0.6
OS: Windows 11 Home latest
terminal: Windows Terminal latest
citty: version latest
node: v20.11.1
electron: v28.2.6
I have an electron app and I try to use citty
for some simple commands but when I run the .exe
file all works perfect but on in the end the process dont exit. I ask because maybe is my fault. Add process.exit
in the run
(maybe unnecessary), tell me if is wrong.
Code I use:
const main = defineCommand({
meta: {
name: "hello",
version: "1.0.0",
description: "My Awesome CLI App",
},
args: {
name: {
type: "positional",
description: "Your name",
required: true,
},
friendly: {
type: "boolean",
description: "Use friendly greeting",
},
},
run({ args }) {
console.log(`${args.friendly ? "Hi" : "Greetings"} ${args.name}!`)
process.exit(0)
}
})
runMain(main)
Edit: The weird thing... check how the terminal behaves...
C:\{path_to_the_project}>YourAppName.exe hello MyName
C:\{path_to_the_project}>
Greetings MyName!
(now terminal stuck)
(I press `enter` and then normal)
C:\{path_to_the_project}>
What behavior I expect:
C:\{path_to_the_project}>YourAppName.exe hello MyName
Greetings MyName!
C:\{path_to_the_project}> (no stuck just exit)
export default defineCommand({
meta: {
name: 'test',
description: 'Test command',
},
async setup(context) {
const data = someComputed(context)
return { data }
},
async run({ args }, { data }) {
console.log(data)
}
})
The return value of setup
could be useful, in addition to some initialization behavior, it could potentially produce different values depending on the context
, which could be used in setup
and passed to all subcommands in the run method arguments!
Of course, I may not have thought this through completely, does anyone else have a better suggestion?
I think we can use a proxy + scule for this to reduce overhead
Currently only speficic named positional args can be defined and multiple is not possible (workaround is to use ctx.args._
to access them all!). For this, we might introduce a new type multiPositional
nodejs v16.14.0
--flag
with a default value of true
does not match the description because of the auto-generated --no-flag
options usage
I am migrating the unjs/mkdist
CLI to unjs/citty
, here is the link
here is the Reproduction link
--flag
with a default value of true
does not match the description because of the auto-generated --no-flag
options usage
I am migrating the unjs/mkdist
CLI to unjs/citty
, here is the link
here is the Reproduction link
Or describe an opposite meaning?๐
No response
related to #46
Hello,
This issue will be used to track documentation update that we need to provide.
PR welcomes to help! (and do not hesitate to comment if you have more idea)
"citty": "^0.1.2",
node: 19.9.0
bun: 0.7.3
os: ubuntu 22.04 LTS
https://github.com/emdahlstrom/citty-formatWithOptions
Trying to run the citty readme example code with bun leads to an error:
SyntaxError: Import named 'formatWithOptions' not found in module 'node:util'.
em@silverbook:~/code/reproductions/citty-formatWithOptions$ bun start
$ bun run index.ts
SyntaxError: Import named 'formatWithOptions' not found in module 'node:util'.
error: script "start" exited with code 1 (SIGHUP)
import { createMain, defineCommand } from 'citty'
const command = defineCommand({
//...
async run() {
return Promise.resolve("hello")
}
})
const apiCommand = createMain(_rDefault(command))
const value = await apiCommand({
rawArgs: []
})
console.log(value) //=> "hello"
Would be really cool if you could set an input as type number and have it cast as such automatically.
Being able to ship and expose internal commands but not showing it to users.
We might export utils (as subpath) for formatting output. Also things like banners.
For general logging, consola is probably best but minor output formatting could be more integrated from CLI builder.
This might eventually be extracted to a standalone package.
node: v21.6.2
citty: ^0.1.6
https://github.com/malezjaa/elmen/blob/main/src/index.ts#L71
I have a string argument called name:
It works fine when running locally:
But when i upload the package to npm and download it, it says Cannot access properties of null (reading name)
even though args object contains the name:
No response
No response
18.16.0
I have added a new subcommand named undefined in playground/cli.ts. Still, when I execute pnpm play without extra parameters, the console does not prompt that the command was not found but instead executes the undefined subcommand I defined.
I have checked the source code and am not sure why the "&& !cmd.run" condition is required here
I have added a new subcommand named undefined in playground/cli.ts. Still, when I execute pnpm play without extra parameters, the console does not prompt that the command was not found but instead executes the undefined subcommand I defined.
I have checked the source code and am not sure why the "&& !cmd.run" condition is required here
No response
No response
Feature to solve: #80
export default defineCommand({
meta: {
name: "lint",
description: "A test command",
},
args: {
files: {
type: "multiple",
description: "Files need be lint",
},
},
run({ args }) {
// lint fileA.ts fileB.ts
console.log(args.files) //=> ['fileA.ts', 'fileB.ts]
},
});
Defining commands is one part of building a good CLI.
Bundling it properly is another part! An unbuild preset can be specifically useful to build a (main cli) and pluggable CLI.
inspired from #98 (comment) by @jgoux
With plugins, we can allow them to intercept to different hooks but also preserve their context. We can extend to more hooks if needed.
Example:
const telemetryPlugin = defineCittyPlugin(() => {
const telemetry = new TelemetryClient();
return {
name: 'telemetry',
async setup() {
telemetry.init();
await telemetry.captureEvent(`$command:${ctx.cmd.meta.name}:start`);
},
async cleanup() {
await telemetry.captureEvent(`$command:${ctx.cmd.meta.name}:end`);
telemetry.flush();
}
}
})
defineCommand({
meta: {
name: "hello",
version: "1.0.0",
description: "My Awesome CLI App",
},
plugins: [telemetryPlugin],
run({ args }) {
console.log(`${args.friendly ? "Hi" : "Greetings"} ${args.name}!`);
},
})
I just wanted to give you kudos. citty is absolutely fantastic. Probably the best CLI builder DX I've had in my 30 years of code!
// A.ts
export default defineCommand({
meta: {
name: 'A',
description: 'Test command',
},
async run({ args }) {
console.log("A execute")
}
})
// B.ts
export default defineCommand({
meta: {
name: 'B',
description: 'Test command',
},
async run({ args }) {
console.log("B execute")
}
})
// main.ts
export default defineCommand({
meta: {
name: 'main',
description: 'Test command',
},
subCommands: {
A: () => import('./A').then(r => r.default),
B: () => import('./B').then(r => r.default),
},
async run({ args }) {
console.log("Main execute")
}
})
$ main A
A execute
$ main B
B execute
$ main
Main execute
$ main A
A execute
$ main B
B execute
$ main
ERROR No command specified. # can's support direct exec
We already have the feature of overriding the showUsage
function on runMain
, but the only way you cen compute the usage is as a string using the renderUsage
function.
That means that if you wanted to enrich the usage output you have to recreate the renderUsage
function locally and apply changes.
https://github.com/unjs/citty/blob/main/src/usage.ts#L18-L124
The downsides to this, is that it's fragile to new changes that may occur in commands and it's structure in the future.
I suggest that it should be considered to rethink the renderUsage
method.
It could be great to either create some utils functions or class implementation for getting the usage as data that you can then manipulate.
The renderUsage
method could then receive a implementation of a "render template" from which it can generate the string.
Use cases could be to group commands like:
โก๏ธ My Awesome CLI Experience (foo v1.0.0) 7:51:33 AM
USAGE awesome init|dev|test|build|info|deploy|start
COMMANDS
Development
init Create a new awesome file.
dev Run awsome dev server
test Run tests
Production
build Build a awesome file
info Get information about your awesome bundle
Deployment
deploy Deploy your awesome file to your server
start Launches your awesome file
Use nuxi <command> --help for more information about a command.
Or what ever makes a good documented cli for different use case.
The main topic here is to add the opt-it feature of defining your own render usage "template".
What do you guys think? ๐
export default defineCommand({
meta: {
name: "Test",
description: "A test command",
},
args: {
len: {
type: "string",
description: "Limit len",
validator(value) {
return Number(value) > 24 & Number(value) < 999 ? true : "Len must more than 24 and less than 999 ";
}
},
},
run({ args }) {
},
});
Hi there! Just in the process of using Citty to build a cli and I'm really liking it so far.
I've hit a bit of a snag though regarding glob patterns in string arguments.
Is there a way to disable glob pattern parsing of arguments? I'm noticing for string args, if I pass in a glob pattern, the args
and rawArgs
passed into the run function are processed into file paths. I need to disable this as I'm processing the glob patterns separately.
Thanks!
Brett
I found that Citty has no test at the moment.
It's better to have at least unit tests so that we can accept new features and bug fixes from the community as well as unjs members.
I created this issue just for making the situation clear and trackable.
Perhaps, are you postponing test implementation based one some thoughts?
If not, I will start working on this issue from adding unit tests.
Apart from run, we usually have common setup parts for cli and cli-sub commands handling global things like registering consola or start update checker in background.
citty 0.1.11
nodejs 16
pnpm 8
https://stackblitz.com/edit/stackblitz-starters-kvn1gq?file=src%2Findex.ts
Type 'ParsedArgs<ArgsDef>' is not assignable to type 'Record<"blah", string>'
{
"compilerOptions": {
"target": "ESNext",
"module": "ESNext",
"moduleResolution": "Node",
"esModuleInterop": true,
"strict": true
},
"include": ["src"]
}
The ts error is gone when i set strict
to false
in tsconfig.json
No response
Hello,
Since Citty automatically support -h
or --help
, we could natively support -v
--version
.
Hi ๐๐ป
I'm fan of creating CLI application. I have typer library that does really good job in python. In JS, I plan to use citty but there's lack of documentation for me & my colleagues to get started and fully leverage it.
Can we have a dedicated documentation site for citty like unstorage & others?
P.S. Some of the unjs projects don't have enough docs to understand the value it provides ๐
node.js 21.0.0 LTS
just try the code
usage/output:
$ node test_citty.js format
Formatting!
undefined
This is the main command. Use 'format' or 'help'.
import { defineCommand, runMain } from "citty";
const formatCommand = defineCommand({
meta: {
name: "format",
description: "Format something",
},
run() {
console.log("Formatting!");
},
});
const helpCommand = defineCommand({
meta: {
name: "help",
description: "Display help information",
},
run() {
console.log("Look at this!");
},
});
const mainCommand = defineCommand({
meta: {
name: "main.js",
version: "1.0.0",
description: "CLI with 'format' and 'help' commands",
},
subCommands: {
format: formatCommand,
help: helpCommand,
},
run({subCommand}) {
console.log(subCommand)
console.log("This is the main command. Use 'format' or 'help'.");
},
});
runMain(mainCommand);
I expect it to have all properties like meta, args, and stuff of subcommand currently runned
No response
No response
Add defs in defineCommand
function like interceptors or middlewares
example:
const customInterceptor1 = (ctx: CommandContext) => false
const customInterceptor2 = (ctx: CommandContext) => console.log('before run.')
defineCommand({
meta: {
name: "hello",
version: "1.0.0",
description: "My Awesome CLI App",
},
interceptors: [customInterceptor1, customInterceptor2]
run({ args }) {
// output: before run.
// and this code will not be execute
console.log(`${args.friendly ? "Hi" : "Greetings"} ${args.name}!`);
},
})
v16.14.2
https://stackblitz.com/edit/stackblitz-starters-qggkrd?file=index.js
when the main command's first positional arg's value is the same as a subCommand name
the subCommand runs and the main command runs with the positional arg's value
No response
No response
// build.ts
export default defineCommand({
meta: {
name: 'build',
description: 'build command',
},
args: {
minify: {
type: 'boolean',
description: 'minify file size',
},
mode: {
type: 'string',
description: 'environment variable'
}
}
})
// api/index.ts
import build from './build'
import { transformCommandToApi } from 'citty'
export const apiBuild = transformCommandToApi(build)
// test.ts
import { apiBuild } from './api'
apiBuild({ // directly invoke with api
minify: true,
mode: false
})
Because defineCommand
already defines all the information related to a command, it is very easy to convert it into a direct call to API.
When using lint-staged, the default behaviour is to pass filenames as positional arguments to linting CLIs, e.g.: your-cmd file1.ext file2.ext
AFAIK there's no way to support positional arguments in this way in citty.
So, if I want to be able to support multiple positional arguments in my Citty CLI commands, I can't do it right now (or, at least, it's unclear if there is a way to do so).
Currently, the generated usage is helpful but limited.
In our situation we would like to be able to display something like a prompt select to allow the user to run a command directly.
https://github.com/natemoo-re/clack/ provides nice utils for supporting prompts. I think we can reexport utilities as a sub-path from citty for elegant usage.
Hello,
Having enum
could help user for completing args!
CLIs often need utils for forking or exec.
We might reexport execa
async utils (with dynamic import for CJS compact)
For on/off execa is perfect. Still not sure about forking. Execa supports execaNode
too that supports IPC channel. However I'm not sure if gives enough flexibility for things like dev server (#7) or not. Still worth to try.
Making a dev server (like for nuxt, nitro, ipx, etc) often has many shared requirements. I'm thinking to expose a node-native util that makes it possible and instructions to opt-in to listhen
for more elegancy.
Expose a wrapper for runMain
to create a runMain with bonded main command.
Caching the results after calling the resolveValue
function
I am using citty
in my project, And I think it's maybe a good idea to support it. ๐
This is my source code:
export default defineCommand({
meta: {
name: 'create',
description: 'Generate new project from template'
},
args: {
projectPath: {
type: 'string',
description: 'Project path to create',
valueHint: "PWD",
default: process.cwd()
},
},
setup({ args }) {
args.projectPath = path.resolve(args.projectPath)
},
run() {}
}
And support formatter
attribute in args definition, then we can do like this:
export default defineCommand({
meta: {
name: 'create',
description: 'Generate new project from template'
},
args: {
projectPath: {
type: 'string',
description: 'Project path to create',
valueHint: "PWD",
default: process.cwd(),
+ formatter: (inputPath) => path.resolve(inputPath)
},
},
- setup({ args }) {
- args.projectPath = path.resolve(inputPath)
- },
run() {}
}
When using subcommands, it would be nice to have error handling bubble up, much like setup and cleanup
Hello,
Imagine having an argument named 'joinGlue', I would love that the CLI support both joinGlue
and join-glue
.
The descriptions of each command should be shown next to their label. So instead of this:
ving v1.0.0
ving CLI
USAGE: ving cache|drizzle|record|schema|user
COMMANDS:
cache
drizzle
record
schema
user
Use `ving <command> --help` for more information about a command.
You'd get this:
ving v1.0.0
ving CLI
USAGE: ving cache|drizzle|record|schema|user
COMMANDS:
cache Redis cache entries
drizzle Drizzle ORM code generation and migrations
record Ving Record code generation
schema Ving Schema code generation
user Manage users
Use `ving <command> --help` for more information about a command.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.