Loading...
Loading...
Compare original and translation side by side
npm-packagenpm-package| Concern | Tool | Why |
|---|---|---|
| Runtime / package manager | Bun | Fast install, run, transpile |
| Bundler | Bunup | Bun-native, dual entry (lib + cli), .d.ts |
| Argument parsing | citty | ~3KB, TypeScript-native, auto-help, |
| Terminal colors | picocolors | ~7KB, CJS+ESM, auto-detect |
| TypeScript | | Maximum correctness |
| Formatting + basic linting | Biome v2 | Fast, single tool |
| Type-aware linting | ESLint + typescript-eslint | Deep type safety |
| Testing | Vitest | Isolation, mocking, coverage |
| Versioning | Changesets | File-based, explicit |
| Publishing | | Trusted Publishing / OIDC |
| 关注点 | 工具 | 原因 |
|---|---|---|
| 运行时 / 包管理器 | Bun | 安装、运行、转译速度快 |
| 打包工具 | Bunup | Bun原生支持,双入口(库 + CLI),生成.d.ts类型声明 |
| 参数解析 | citty | 体积约3KB,TypeScript原生支持,自动生成帮助文档,提供 |
| 终端颜色 | picocolors | 体积约7KB,支持CJS+ESM,自动检测终端环境 |
| TypeScript配置 | | 确保代码最大程度的正确性 |
| 代码格式化 + 基础代码检查 | Biome v2 | 速度快,单一工具完成多项任务 |
| 类型感知代码检查 | ESLint + typescript-eslint | 深度类型安全检查 |
| 测试工具 | Vitest | 支持隔离测试、模拟、覆盖率统计 |
| 版本管理 | Changesets | 基于文件的显式版本管理 |
| 发布工具 | | 可信发布 / OIDC认证 |
bun run <skill-path>/scripts/scaffold.ts ./my-cli \
--name my-cli \
--bin my-cli \
--description "What this CLI does" \
--author "Your Name" \
--license MIT--bin <name>--cli-only--no-eslintcd my-cli
bun install
bun add -d bunup typescript vitest @vitest/coverage-v8 @biomejs/biome @changesets/cli
bun add citty picocolors
bun add -d eslint typescript-eslint # unless --no-eslintbun run <skill-path>/scripts/scaffold.ts ./my-cli \
--name my-cli \
--bin my-cli \
--description "What this CLI does" \
--author "Your Name" \
--license MIT--bin <name>--cli-only--no-eslintcd my-cli
bun install
bun add -d bunup typescript vitest @vitest/coverage-v8 @biomejs/biome @changesets/cli
bun add citty picocolors
bun add -d eslint typescript-eslint # 若使用了--no-eslint则无需执行my-cli/
├── src/
│ ├── index.ts # Library exports (programmatic API)
│ ├── index.test.ts # Unit tests for library
│ ├── cli.ts # CLI entry point (imports from index.ts)
│ └── cli.test.ts # CLI integration tests
├── dist/
│ ├── index.js # Library bundle
│ ├── index.d.ts # Type declarations
│ └── cli.js # CLI binary (with shebang)
├── .changeset/
│ └── config.json
├── package.json
├── tsconfig.json
├── bunup.config.ts
├── biome.json
├── eslint.config.ts
├── vitest.config.ts
├── .gitignore
├── README.md
└── LICENSEmy-cli/
├── src/
│ ├── index.ts # 库导出的公共API
│ ├── index.test.ts # 库的单元测试
│ ├── cli.ts # CLI入口文件(从index.ts导入逻辑)
│ └── cli.test.ts # CLI集成测试
├── dist/
│ ├── index.js # 库打包产物
│ ├── index.d.ts # 类型声明文件
│ └── cli.js # CLI二进制文件(包含shebang)
├── .changeset/
│ └── config.json
├── package.json
├── tsconfig.json
├── bunup.config.ts
├── biome.json
├── eslint.config.ts
├── vitest.config.ts
├── .gitignore
├── README.md
└── LICENSEsrc/index.tssrc/index.test.tsexportsbinsrc/index.tssrc/index.test.tsexportsbincli.tsindex.tscli.ts → imports from → index.ts / core modules
↑
unit testscli.tsindex.tscli.ts → 导入自 → index.ts / 核心模块
↑
单元测试#!/usr/bin/env node#!/usr/bin/env bunbindist/chmod +x dist/cli.js#!/usr/bin/env node#!/usr/bin/env bunbindist/chmod +x dist/cli.js"type": "module"typesfiles: ["dist"]exportsbinbinexports"type": "module"typesfiles: ["dist"]exportsbinbinexportsanyunknownimport typerunMain()process.on('SIGINT', ...)anyunknownimport typerunMain()process.on('SIGINT', ...)exportsfilesexportsfilesimport { defineCommand, runMain } from 'citty';
const main = defineCommand({
meta: { name: 'my-cli', version: '1.0.0', description: '...' },
args: {
input: { type: 'positional', description: 'Input file', required: true },
output: { alias: 'o', type: 'string', description: 'Output path', default: './out' },
verbose: { alias: 'v', type: 'boolean', description: 'Verbose output', default: false },
},
run({ args }) {
// args is fully typed
},
});
void runMain(main);import { defineCommand, runMain } from 'citty';
const main = defineCommand({
meta: { name: 'my-cli', version: '1.0.0', description: '...' },
args: {
input: { type: 'positional', description: 'Input file', required: true },
output: { alias: 'o', type: 'string', description: 'Output path', default: './out' },
verbose: { alias: 'v', type: 'boolean', description: 'Verbose output', default: false },
},
run({ args }) {
// args是完全类型化的
},
});
void runMain(main);import { defineCommand, runMain } from 'citty';
const init = defineCommand({ meta: { name: 'init' }, /* ... */ });
const build = defineCommand({ meta: { name: 'build' }, /* ... */ });
const main = defineCommand({
meta: { name: 'my-cli', version: '1.0.0' },
subCommands: { init, build },
});
void runMain(main);import { defineCommand, runMain } from 'citty';
const init = defineCommand({ meta: { name: 'init' }, /* ... */ });
const build = defineCommand({ meta: { name: 'build' }, /* ... */ });
const main = defineCommand({
meta: { name: 'my-cli', version: '1.0.0' },
subCommands: { init, build },
});
void runMain(main);// src/index.test.ts
import { describe, it, expect } from 'vitest';
import { processInput } from './index.js';
describe('processInput', () => {
it('handles valid input', () => {
expect(processInput('test')).toBe('expected');
});
});// src/index.test.ts
import { describe, it, expect } from 'vitest';
import { processInput } from './index.js';
describe('processInput', () => {
it('handles valid input', () => {
expect(processInput('test')).toBe('expected');
});
});bun run build// src/cli.test.ts
import { describe, it, expect } from 'vitest';
import { execFile } from 'node:child_process';
import { promisify } from 'node:util';
const exec = promisify(execFile);
describe('CLI', () => {
it('prints help', async () => {
const { stdout } = await exec('node', ['./dist/cli.js', '--help']);
expect(stdout).toContain('my-cli');
});
});bun run build// src/cli.test.ts
import { describe, it, expect } from 'vitest';
import { execFile } from 'node:child_process';
import { promisify } from 'node:util';
const exec = promisify(execFile);
describe('CLI', () => {
it('prints help', async () => {
const { stdout } = await exec('node', ['./dist/cli.js', '--help']);
expect(stdout).toContain('my-cli');
});
});undefinedundefinedundefinedundefinedsrc/commands/init.tssrc/commands/build.tsdefineCommand()subCommandssrc/commands/init.tssrc/commands/build.tsdefineCommand()subCommandssrc/index.tsexportsbindts: { entry: ['src/index.ts'] }src/index.tsexportsbindts: { entry: ['src/index.ts'] }bun buildtsc --emitDeclarationOnlybun buildbun publish--provenancenpm publishbun publishNPM_CONFIG_TOKENNODE_AUTH_TOKEN#!/usr/bin/env bunbannerdist/cli.jsbun buildtsc --emitDeclarationOnlybun buildbun publish--provenancenpm publishbun publishNPM_CONFIG_TOKENNODE_AUTH_TOKEN#!/usr/bin/env bunbannerdist/cli.js