README
π» Termost
Get the most of your terminal⨠Features
Termost allows you build command line tools in a minute thanks to its:
- Fluent syntax to express your CLI configurations with instructions such as:
- Subcommand support
- Long and short option support
- Interaction support thanks to its built-in
inquirer
wrapper - Tasks support thanks to its built-in
Listr
wrapper andexec
helper
- Shareable outputs between instruction
- Beautiful CLI message formatting via
message
instruction - Auto-generated help and version information
- TypeScript support to foster a type-safe API
π Quickstart
Install the library:
# NPM
npm install termost
# Yarn
yarn add termost
Once you're done, you can start consuming it in your executable file (binary):
#!/usr/bin/env node
import { termost } from "termost";
const program = termost<{
sharedOutput: string;
option: string;
}>("My super program description");
program
.option({
key: "option",
name: { long: "flag", short: "f" },
description: "A super useful CLI flag",
defaultValue: "Hello from option",
})
.task({
key: "sharedOutput",
label: "Retrieves files",
async handler(_, helpers) {
return await helpers.exec('echo "Hello from task"', {
cwd: process.cwd(),
});
},
})
.message({
handler(context, helpers) {
helpers.print(`Task value: ${context.values.sharedOutput}`);
helpers.print(`Option value: ${context.values.option}`, {
type: "warning",
});
},
});
The output will look like:
βοΈ Usage
Here's an API overview:
command({ name, description })
#!/usr/bin/env node
import { termost } from "termost";
const program = termost("Example to showcase the `command` API");
program
.command({
name: "build",
description: "Transpile and bundle in production mode",
})
.message({
handler(context, helpers) {
helpers.print(
`π Hello, I'm the ${context.currentCommand} command`
);
},
});
program
.command({
name: "watch",
description: "Rebuild your assets on any code change",
})
.message({
handler(context, helpers) {
helpers.print(
`π Hello, I'm the ${context.currentCommand} command`,
{ type: "warning" }
);
},
});
option({ key, name, description, defaultValue, skip })
#!/usr/bin/env node
import { termost } from "termost";
type ProgramContext = {
optionWithAlias: number;
optionWithoutAlias: string;
};
const program = termost<ProgramContext>("Example to showcase the `option` API");
program
.option({
key: "optionWithAlias",
name: { long: "shortOption", short: "s" },
description: "Useful CLI flag",
defaultValue: 0,
})
.option({
key: "optionWithoutAlias",
name: "longOption",
description: "Useful CLI flag",
defaultValue: "defaultValue",
})
.message({
handler(context, helpers) {
helpers.print(JSON.stringify(context, null, 2));
},
});
message({ handler, skip })
#!/usr/bin/env node
import { termost } from "termost";
const program = termost("Example to showcase the `message` API");
program.message({
handler(context, helpers) {
const content =
"A content formatted thanks to the `print` helper presets.";
helpers.print(content);
helpers.print(content, { type: "warning" });
helpers.print(content, { type: "error" });
helpers.print(content, { type: "success" });
helpers.print(content, {
type: "information",
label: "π You can also customize the label",
});
helpers.print(["I support also", "multilines", "with array input"], {
type: "information",
label: "π You can also customize the label",
});
console.log(
helpers.format(
"\nYou can also have a total control on the formatting through the `format` helper.",
{
color: "white",
modifier: ["italic", "strikethrough", "bold"],
}
)
);
},
});
question({ key, label, type, skip, ...typeParameters })
#!/usr/bin/env node
type ProgramContext = {
question1: "singleOption1" | "singleOption2";
question2: Array<"multipleOption1" | "multipleOption2">;
question3: boolean;
question4: string;
};
const program = termost<ProgramContext>(
"Example to showcase the `question` API"
);
program
.question({
type: "select:one",
key: "question1",
label: "What is your single choice?",
choices: ["singleOption1", "singleOption2"],
defaultValue: "singleOption1",
})
.question({
type: "select:many",
key: "question2",
label: "What is your multiple choices?",
choices: ["multipleOption1", "multipleOption2"],
defaultValue: ["multipleOption2"],
})
.question({
type: "confirm",
key: "question3",
label: "Are you sure to skip this question?",
defaultValue: false,
skip(context) {
return Boolean(context.values.question3);
},
})
.question({
type: "text",
key: "question4",
label: (context) =>
`Dynamic question label generated from a contextual value: ${context.values.question1}`,
})
.message({
handler(context, helpers) {
helpers.print(JSON.stringify(context, null, 4));
},
});
task({ key, label, handler, skip })
#!/usr/bin/env node
import { termost } from "termost";
type ProgramContext = {
computedFromOtherTaskValues: "big" | "small";
execOutput: string;
size: number;
};
const program = termost<ProgramContext>("Example to showcase the `task` API");
program
.task({
key: "size",
label: "Task with returned value (persisted)",
async handler() {
return 45;
},
})
.task({
label: "Task with side-effect only (no persisted value)",
handler() {
// @note: side-effect only handler
},
})
.task({
key: "computedFromOtherTaskValues",
label: "Task can also access other persisted task context",
handler(context) {
if (context.values.size > 2000) {
return Promise.resolve("big");
}
return Promise.resolve("small");
},
})
.task({
key: "execOutput",
label: "Or even execute external commands thanks to its provided helpers",
handler(context, helpers) {
return helpers.exec("ls -al");
},
})
.task({
label: "A task can be skipped as well",
async handler() {
await wait(2000);
return Promise.resolve("Super long task");
},
skip(context) {
const needOptimization = context.values.size > 2000;
return !needOptimization;
},
})
.task({
label: (context) =>
`A task can have a dynamic label generated from contextual values: ${context.values.computedFromOtherTaskValues}`,
async handler() {},
})
.message({
handler(context, helpers) {
helpers.print(
`A task with a specified "key" can be retrieved here. Size = ${context.values.size}. If no "key" was specified the task returned value cannot be persisted across program instructions.`
);
console.info(JSON.stringify(context, null, 2));
},
});
const wait = (delay: number) => {
return new Promise((resolve) => setTimeout(resolve, delay));
};
π€© Built with Termost
- Quickbundle The zero-configuration bundler powered by ESBuild