11import type { SourceType } from './types'
22import path from 'node:path'
33import 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'
66import { prepareTarget } from './fs-utils'
77import { updateRootPackageJson } from './package-json'
88import { promptTargetDir , promptTemplates } from './prompts'
@@ -11,17 +11,12 @@ import { cloneRepo } from './source-git'
1111import { scaffoldFromNpm } from './source-npm'
1212import { 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
2722function 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+ } )
0 commit comments