Command

Yes, Milkio can be used not only to build servers behind the client side, but also to develop small tools that can be used in the terminal.
Writing
Any API located in the /apps/$/
directory is treated as a command. When using commands, it feels like using a regular API, with the difference being the format of the parameters. Create a /apps/$/say.ts
file and try saying hello to the world…?
/* eslint-disable no-console */import { defineApi, defineApiTest } from "milkio"
export const api = defineApi({ meta: {}, async action( params: { commands: Array<string>; options: Record<string, string | true>; }, context ) { console.log(`hello world!`) console.log(`Your Params:`, params) },})
You may notice that you now have two parameters: commands
and options
. These parameters are automatically parsed from argv
by Milkio. Take a look at the following example, which shows the executed command and the obtained params.
./app say hello world --foo=bar --enable
params: { commands: ["hello", "world"], options: { foo: "bar", enable: true, },}
Usage
Before bundling, we can use our command with bun command
.
bun command say hello world --foo=bar --enable
We can also bundle our application into a binary file. In the scripts
section of our /package.json
, modify the build
script to bundle our command instead of the HTTP server.
"build:command": "bun build ./run-command.ts --splitting --sourcemap=inline --outdir=./dist --target=bun --minify",
Run the build command, and you will have a new file named app
in your directory.
npm run build
Try running it!
./app say hello world --foo=bar --enable
Default Command
When executed directly, Milkio will try to find a command named default
.
For example, when running ./app
or bun command
, it will actually execute your /src/apps/$/default.ts
file.
Command Not Found
When a command is not found, Milkio will execute the notFoundHandler
method defined in your /run-command.ts
file. The event object contains the executed command name and any accompanying params.
async function command() { const commandHandler = defineCommandHandler(await milkio, { notFoundHandler(event) { console.log("command not found", event) } }) await commandHandler(process.argv)}
Interactive CLI
Bun provides built-in methods for creating an interactive CLI.
console.log(prompt("What's your sign?"))console.log(alert("Get ready to move on!"))console.log(confirm("Do you really want to leave?"))
$ Shell
Bun’s $ Shell feature is very powerful.
import { $ } from "bun";
await echo "Hello World!"`; // Hello World!
Get the output of a shell command as text:
import { $ } from "bun";
const welcome = await echo "Hello World!"`.text();
console.log(welcome); // Hello World!\n
Pass parameters:
import { $ } from "bun";
const name = "World";
await echo "Hello ${name}!"`; // Hello World!
To avoid potential security issues, Bun’s $ Shell parameterizes template string interpolation. This means that the following approach will not work as expected:
import { $ } from "bun";
const path = "-a /";
await ls ${path}`; // Will not work
You can use raw
to disable this protection:
import { $ } from "bun";
const path = "-a /";
await ls ${{raw: path }}`; // Works as expected
For more usage, please refer to the $ Shell documentation.
Starting an HTTP Server with a Command
Sometimes, we may need to develop an application that includes both commands and an HTTP server, and start the HTTP server with a specific command.
Thanks to Milkio’s excellent modularity, it’s easy to combine the two. You just need to copy the code from /run-serve.ts
into your command.
import { defineApi, defineHttpHandler, envToNumber } from "milkio"import { env } from "node:process"import { milkio } from "../../../milkio"
export const api = defineApi({ meta: {}, async action(params: { commands: Array<string>; options: Record<string, string | true>; }, context) { const httpHandler = defineHttpHandler(await milkio) // if you are using Bun Bun.serve({ port: envToNumber(env.PORT, 9000), fetch(request) { return httpHandler({ request }) } }) },})