Skip to content

Commit e85a1df

Browse files
committed
chore: update
1 parent 33133d7 commit e85a1df

23 files changed

+206
-248
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
"@icebreakers/monorepo-templates": patch
3+
"@icebreakers/monorepo": patch
4+
"create-icebreaker": patch
5+
---
6+
7+
将 commander 与 inquirer 提示组件统一放入 monorepo-templates,并用更规范的 Commander 风格重写 create-icebreaker 的 CLI 解析。

packages/create-icebreaker/src/args.ts

Lines changed: 0 additions & 113 deletions
This file was deleted.
Lines changed: 56 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import type { SourceType } from './types'
22
import path from 'node:path'
33
import process from 'node:process'
4-
import { parseArgs } from './args'
5-
import { DEFAULT_SOURCE } from './constants'
4+
import { Command } from '@icebreakers/monorepo-templates'
5+
import { DEFAULT_BRANCH, DEFAULT_REPO, DEFAULT_SOURCE, DEFAULT_TARGET } from './constants'
66
import { prepareTarget } from './fs-utils'
77
import { updateRootPackageJson } from './package-json'
88
import { promptTargetDir, promptTemplates } from './prompts'
@@ -11,17 +11,12 @@ import { cloneRepo } from './source-git'
1111
import { scaffoldFromNpm } from './source-npm'
1212
import { resolveTemplateSelections } from './templates'
1313

14-
function printHelp() {
15-
process.stdout.write(`${[
16-
'Usage: create-icebreaker [dir] [--source npm|git] [--repo <repo>] [--branch <branch>]',
17-
'Options:',
18-
' --source <npm|git> Source for templates (default npm)',
19-
' --repo <repo> GitHub repo or git url to clone (default sonofmagic/monorepo-template)',
20-
' --branch <branch> Branch or tag to checkout (default main)',
21-
' --templates <list> Comma-separated template keys or indexes to keep',
22-
' --force Remove existing target directory before cloning',
23-
' -h, --help Show this help message',
24-
].join('\n')}\n`)
14+
interface CreateOptions {
15+
source?: string
16+
repo?: string
17+
branch?: string
18+
templates?: string
19+
force?: boolean
2520
}
2621

2722
function printNextSteps(targetDir: string) {
@@ -36,28 +31,28 @@ function printNextSteps(targetDir: string) {
3631
].join('\n'))
3732
}
3833

39-
function normalizeSource(value: SourceType | undefined) {
40-
return value ?? DEFAULT_SOURCE
41-
}
42-
43-
async function main() {
44-
const parsed = parseArgs(process.argv.slice(2))
45-
if (parsed.help) {
46-
printHelp()
47-
return
34+
function normalizeSource(value?: string): SourceType {
35+
const normalized = value?.toLowerCase()
36+
if (normalized === 'npm' || normalized === 'git') {
37+
return normalized
4838
}
39+
return DEFAULT_SOURCE
40+
}
4941

42+
async function runCreate(targetDirInput: string, options: CreateOptions) {
5043
const isInteractive = Boolean(process.stdin.isTTY && process.stdout.isTTY)
51-
const source = normalizeSource(parsed.source)
52-
let targetDirInput = parsed.targetDir
44+
const source = normalizeSource(options.source)
45+
const repo = options.repo ?? DEFAULT_REPO
46+
const branch = options.branch ?? DEFAULT_BRANCH
47+
let targetInput = targetDirInput || DEFAULT_TARGET
5348

5449
if (isInteractive) {
55-
targetDirInput = await promptTargetDir(targetDirInput)
50+
targetInput = await promptTargetDir(targetInput)
5651
}
5752

5853
let selectedTemplates: string[] = []
59-
if (parsed.templates) {
60-
const { selections, unknown } = resolveTemplateSelections(parsed.templates)
54+
if (options.templates) {
55+
const { selections, unknown } = resolveTemplateSelections(options.templates)
6156
if (unknown.length) {
6257
process.stderr.write(`忽略未知模板:${unknown.join(', ')}\n`)
6358
}
@@ -67,25 +62,42 @@ async function main() {
6762
selectedTemplates = await promptTemplates()
6863
}
6964

70-
const targetDir = path.resolve(process.cwd(), targetDirInput)
71-
const projectName = path.basename(targetDir) || targetDirInput
65+
const targetDir = path.resolve(process.cwd(), targetInput)
66+
const projectName = path.basename(targetDir) || targetInput
7267

73-
try {
74-
await prepareTarget(targetDir, parsed.force)
75-
if (source === 'git') {
76-
await cloneRepo(parsed.repo, parsed.branch, targetDir)
77-
await scaffoldFromRepo(targetDir, selectedTemplates)
78-
}
79-
else {
80-
await scaffoldFromNpm(targetDir, selectedTemplates)
81-
}
82-
await updateRootPackageJson(targetDir, projectName)
83-
printNextSteps(targetDir)
68+
await prepareTarget(targetDir, Boolean(options.force))
69+
if (source === 'git') {
70+
await cloneRepo(repo, branch, targetDir)
71+
await scaffoldFromRepo(targetDir, selectedTemplates)
8472
}
85-
catch (error) {
86-
process.stderr.write(`[create-icebreaker] ${error instanceof Error ? error.message : error}\n`)
87-
process.exitCode = 1
73+
else {
74+
await scaffoldFromNpm(targetDir, selectedTemplates)
8875
}
76+
await updateRootPackageJson(targetDir, projectName)
77+
printNextSteps(targetDir)
8978
}
9079

91-
main()
80+
const program = new Command()
81+
program
82+
.name('create-icebreaker')
83+
.description('Bootstrap the icebreaker monorepo template')
84+
.argument('[dir]', 'Target directory', DEFAULT_TARGET)
85+
.option('-s, --source <source>', 'Source for templates (npm|git)', DEFAULT_SOURCE)
86+
.option('-r, --repo <repo>', 'GitHub repo or git url to clone', DEFAULT_REPO)
87+
.option('-b, --branch <branch>', 'Branch or tag to checkout', DEFAULT_BRANCH)
88+
.option('-t, --templates <list>', 'Comma-separated template keys or indexes to keep')
89+
.option('-f, --force', 'Remove existing target directory before cloning', false)
90+
.action(async (dir: string, options: CreateOptions) => {
91+
try {
92+
await runCreate(dir, options)
93+
}
94+
catch (error) {
95+
process.stderr.write(`[create-icebreaker] ${error instanceof Error ? error.message : error}\n`)
96+
process.exitCode = 1
97+
}
98+
})
99+
100+
program.parseAsync(process.argv).catch((error: unknown) => {
101+
process.stderr.write(`[create-icebreaker] ${error instanceof Error ? error.message : error}\n`)
102+
process.exitCode = 1
103+
})
Lines changed: 15 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,21 @@
1-
import process from 'node:process'
2-
import { createInterface } from 'node:readline/promises'
3-
import { formatTemplateList, parseTemplateInput } from './templates'
1+
import { checkbox, input } from '@icebreakers/monorepo-templates'
2+
import { templateChoices } from './templates'
43

54
export async function promptTargetDir(defaultDir: string) {
6-
const rl = createInterface({ input: process.stdin, output: process.stdout })
7-
try {
8-
const answer = await rl.question(`项目创建目录(默认 ${defaultDir}):`)
9-
return answer.trim() || defaultDir
10-
}
11-
finally {
12-
rl.close()
13-
}
5+
const answer = await input({
6+
message: `项目创建目录(默认 ${defaultDir}):`,
7+
default: defaultDir,
8+
})
9+
return (answer?.trim?.() ?? defaultDir) || defaultDir
1410
}
1511

1612
export async function promptTemplates() {
17-
const rl = createInterface({ input: process.stdin, output: process.stdout })
18-
try {
19-
process.stdout.write(`\n可选模板(默认不选择):\n${formatTemplateList()}\n`)
20-
const answer = await rl.question('请选择要保留的模板(逗号分隔,直接回车跳过):')
21-
const { selections, unknown } = parseTemplateInput(answer)
22-
if (unknown.length) {
23-
process.stderr.write(`忽略未知模板:${unknown.join(', ')}\n`)
24-
}
25-
return selections
26-
}
27-
finally {
28-
rl.close()
29-
}
13+
const selections = await checkbox({
14+
message: '请选择要保留的模板(默认不选择):',
15+
choices: templateChoices.map(item => ({
16+
name: `${item.key} - ${item.label}`,
17+
value: item.key,
18+
})),
19+
})
20+
return selections
3021
}
Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1 @@
11
export type SourceType = 'npm' | 'git'
2-
3-
export interface CliOptions {
4-
targetDir: string
5-
repo: string
6-
branch: string
7-
source: SourceType
8-
force: boolean
9-
templates?: string
10-
help?: boolean
11-
}

packages/monorepo-templates/package.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,12 @@
4747
"prepack": "node scripts/sync-assets.mjs",
4848
"lint": "eslint ."
4949
},
50+
"dependencies": {
51+
"@inquirer/checkbox": "^5.0.4",
52+
"@inquirer/input": "^5.0.4",
53+
"@inquirer/select": "^5.0.4",
54+
"commander": "^14.0.2"
55+
},
5056
"publishConfig": {
5157
"access": "public"
5258
}

packages/monorepo-templates/src/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,7 @@ export {
3636
toPublishGitignorePath,
3737
toWorkspaceGitignorePath,
3838
}
39+
export { default as checkbox } from '@inquirer/checkbox'
40+
export { default as input } from '@inquirer/input'
41+
export { default as select } from '@inquirer/select'
42+
export { Command, program } from 'commander'

packages/monorepo/package.json

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -71,17 +71,13 @@
7171
},
7272
"dependencies": {
7373
"@icebreakers/monorepo-templates": "workspace:*",
74-
"@inquirer/checkbox": "^5.0.4",
75-
"@inquirer/input": "^5.0.4",
76-
"@inquirer/select": "^5.0.4",
7774
"@pnpm/find-workspace-dir": "^1000.1.3",
7875
"@pnpm/logger": "^1001.0.1",
7976
"@pnpm/types": "^1001.2.0",
8077
"@pnpm/worker": "^1000.6.1",
8178
"@pnpm/workspace.find-packages": "^1000.0.54",
8279
"@pnpm/workspace.read-manifest": "^1000.2.9",
8380
"c12": "^3.3.3",
84-
"commander": "^14.0.2",
8581
"comment-json": "^4.5.1",
8682
"consola": "^3.4.2",
8783
"execa": "^9.6.1",

packages/monorepo/src/cli/program.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
import type { AgenticTemplateFormat, GenerateAgenticTemplateOptions } from '../commands/ai'
22
import type { CleanCommandConfig, CliOpts } from '../types'
33
import process from 'node:process'
4-
import input from '@inquirer/input'
5-
import select from '@inquirer/select'
6-
import { program } from 'commander'
4+
import { input, program, select } from '@icebreakers/monorepo-templates'
75
import { cleanProjects, createNewProject, createTimestampFolderName, defaultAgenticBaseDir, generateAgenticTemplate, generateAgenticTemplates, getCreateChoices, init, loadAgenticTasks, setVscodeBinaryMirror, skillTargets, syncNpmMirror, syncSkills, upgradeMonorepo } from '../commands'
86
import { defaultTemplate } from '../commands/create'
97
import { name as cliName, version } from '../constants'

packages/monorepo/src/commands/clean.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type { CleanCommandConfig } from '../types'
2-
import checkbox from '@inquirer/checkbox'
2+
import { checkbox } from '@icebreakers/monorepo-templates'
33
import fs from 'fs-extra'
44
import path from 'pathe'
55
import set from 'set-value'

0 commit comments

Comments
 (0)