以前、unjs/unbuildに入門したけど、
cittyもつかってオレオレCLIツールを作ってみたときの備忘録(*´ω`*)
テンプレートとしても使える、サンプルリポジトリもここにつくっておいた
cittyとunbuild
どちらもUnJsのパッケージで、
- citty
- CLIコマンドを作るのに便利
- 引数とかをいい感じに処理してくれる
- unbuild
- ライブラリ向けのビルドシステム
- 少ない設定でESM/commonjs+型定義を生成できる
- 前の記事: unjs/unbuildに入門してみた - くらげになりたい。
この2つを使えば、ちょっとしたライブラリやCLIを簡単に作れる(*´ω`*)
cittyの使い方
使い方はシンプルで、こんな感じ。
// main.ts import { runMain, defineCommand } from "citty"; import { description, name, version } from "../package.json"; // コマンドの定義 const main = defineCommand({ // コマンドの名前、バージョンなどの情報 meta: { name, version, description }, // 引数に関する定義 args: { text: { type: "positional", description: "base text", required: true, }, prefix: { type: "string", alias: "p", description: "preffix text", default: "Hello, ", }, quote: { type: "boolean", alias: "q", description: "append quotation", default: false, }, }, // 実際に実行する処理 run: async ({ args }) => { // argsには、コマンドの引数が // 上のargs{}の形式で渡されてくる }, }); // 定義したCLIコマンドを実行 runMain(main);
とりあえず、-h
をつけてjitiで実行してみると、
$ npx jiti src/main.ts -h Template for CLI and npm package powered by citty and unbuild. (template-citty-unbuild v0.0.0) USAGE template-citty-unbuild [OPTIONS] <TEXT> ARGUMENTS TEXT base text OPTIONS -p, --prefix="Hello, " preffix text -q, --quote append quotation
defineCommand
で指定した内容からヘルプを出してくれる
引数の定義
args
まわりは、こんな感じっぽい
// 引数に関する定義 args: { text: { // positionalは、フラグなしの引数 type: "positional", description: "base text", // requiredをつけると必須になる required: true, }, prefix: { // stringは、--<名前>=<文字列>の引数 type: "string", // -pのように、ショート版も指定できるただし、1文字のみ alias: "p", description: "preffix text", // 省略された場合の、デフォルト値も指定できる default: "Hello, ", }, quote: { // booleanは、--<名前>=<真偽値>の引数 type: "boolean", alias: "q", description: "append quotation", default: false, }, },
defuを使ってデフォルト値とマージする
default
にデフォルト値を書く以外にも、
defuを使ってマージするとかもできる
import { defineCommand } from "citty"; import defu from "defu"; import { description, name, version } from "../package.json"; // オプションの型定義 interface Options { prefix: string; suffix: string; quote: boolean; } // オプションのデフォルト値 const DEFAULT_OPTION: Options = { prefix: "Hello, ", suffix: "", quote: false, }; const main = defineCommand({ // ... run: async ({ args }) => { // defuを使って、undefinedの引数に、デフォルト値を設定する const options: Options = defu(args, DEFAULT_OPTION); // ... }, });
CLIを含めてunbuildする
npm packageとして公開できるように、
いろいろ設定していく
ディレクトリ構成
. ├── bin/ │ └── cli.mjs ... CLIコマンド ├── src/ │ ├── lib/ ... ライブラリのコード │ ├── cli.ts ... CLIコマンドの定義 │ ├── index.ts │ └── types.ts ├── build.config.ts ... "unbuild"の設定ファイル └── package.json
build.config.ts
unbuild用の設定ファイルを用意
// build.config.ts import { defineBuildConfig } from "unbuild"; export default defineBuildConfig([ { // エントリーポイントの指定。index.tsを起点にビルドする entries: [ 'src/index.ts' ], // 型定義も出力する declaration: true, rollup: { // デフォルトはmjsのみなので、cjsも出力する emitCJS: true, // msj/cjsをminifyする esbuild: { minify: true } }, }, ]);
src/cli.ts
CLIコマンドの定義はこんな感じで用意
// src/cli.ts import { runMain as _runMain, defineCommand } from "citty"; import { description, name, version } from "../package.json"; const main = defineCommand({ meta: { name, version, description, }, args: { // 略 }, run: async ({ args }) => { // 略 }, }); // CLIの実行を関数化してexportしておく export const runMain = () => _runMain(main);
src/index.ts
exportするものをまとめたindex.tsはこんな感じ。
unbuild.config.ts
のentries
に指定したファイル
// src/index.ts // CLIコマンド export { runMain } from "./cli"; // ライブラリの関数とか export { getMessage } from "./lib/message"; // 型定義もあれば export type { Options } from "./types";
bin/cli.mjs
npx
などで実行するファイルを用意。
dist/index.mjs
とかだとシバン(shebang/1行目)がないので、うまく動かない。。
#!/usr/bin/env node import { runMain } from "../dist/index.mjs"; runMain();
package.json
name
、version
、description
以外で、
設定が必要なのはこのあたり
{ "exports": { ".": { // ES Module向けのexports "import": { "types": "./dist/index.d.mts", "default": "./dist/index.mjs" }, // CommonJS向けのexports "require": { "types": "./dist/index.d.cts", "default": "./dist/index.cjs" } } }, // binのcliファイルを設定 "main": "./bin/cli.mjs", // binには、コマンド名と対応するファイルを設定 "bin": { "template-citty-unbuild": "./bin/cli.mjs" }, // 型定義ファイルも設定 "types": "./dist/index.d.ts", // ビルドしたファイル(dist)とbinを公開するように設定 "files": [ "dist", "bin" ], // このパッケージ自体はCommonJSにしておく "type": "commonjs", // 略 }
ビルドしてみる
これでnpx unbuild
を実行すると、
./dist
配下に以下のファイルが生成される
./dist/ ├── index.cjs ├── index.d.cts ├── index.d.mts ├── index.d.ts └── index.mjs
ビルドしたあとに、./bin/cli.mjs
で実行できればOK
node ./dist/index.mjs
とかでも確認できる
あとは、パッケージをnpmjsやGitHub Packagesとかで公開すれば使えるように(*´ω`*)
昔書いた記事はこちらに〜
- pnpm x Turborepo x lerna-lite x GitHub Packagesでmonorepoなオレオレ非公開ライブラリをつくってみる - くらげになりたい。
- GitHub Packagesでプライベートnpmパッケージを公開する - くらげになりたい。
以上!! テンプレートも作ったので、いろいろ捗るぞ...(*´ω`*)!!