14.5 企业脚手架--丰富命令系统

53 阅读3分钟

丰富命令系统

1.修改commands/base目录下文件,同级添加index.ts,registerCommand.ts, src目录下修改cli.ts。index.ts

// packages\cli\src\commands\base\build.ts 其他同理
import { Command } from 'commander'
import { logger } from '../../utils/logger'
export const build = (program: Command) => {
 return program
        .createCommand('build')
        .description('构建项目').action(()=>{
            logger.info('构建项目');
        });
};

// packages\cli\src\commands\registerCommand.ts
import type { Command } from 'commander'
import { program } from 'commander'


type Fn = (p: Command) => Command

/**
 * register new command
 * @param program
 * @param command
 */
// 负责插件的注册逻辑
export const registerCommand = (fn: Fn) => {
    program.addCommand(fn(program))
}


// packages\cli\src\cli.ts
import { program } from 'commander'

import './commands'

export const defineConfig = () => {
    //
}

export const runCLI = () => {
    program.parse(process.argv)
}


//packages\cli\src\index.ts

export * from './cli'

2.commands/base/create.ts

解析参数 匹配技术栈 匹配模板,区分是本地还是远程 对模板进行加工(例如修改package.json name等) 完成创建

安装依赖fs-extra(文件处理) pnpm i fs-extra -D packages\cli\src\utils\loadTemplate.ts


// packages\cli\src\utils\loadTemplate.ts

// 本地模板,fs操作
// import {readFile} from 'fs-extra'
import {copy} from 'fs-extra'
import { join } from 'node:path'

// readFile(join(__dirname,'../../cli/package.json')).then(data=>{
//     console.log('加载本地脚手架',data.toString())
// })

export const loadTemplate = async (templateName:string) => {
    // 1. 获取模板路径
    const templatePath = join(__dirname,`../templates/${templateName}`)
    console.log('templatePath',templatePath)
    // 2. 拷贝模板到目标路径
    await copy(templatePath,`${process.cwd()}/wzm-demo`)
    console.log('模板加载完成')
}


// packages\cli\src\commands\base\create.ts
import { Command } from 'commander'
import { logger } from '../../utils/logger'
import { loadTemplate } from '../../utils/loadTemplate'
import pc from 'picocolors'

export const create = (program:Command) => {
 return program
        .createCommand('create')
        .description('初始化创建项目').action(()=>{
            logger.info(pc.bgCyan('初始化创建项目'));
            loadTemplate('template-ssr-vue')
        });
};

增加packages\cli\templates文件夹,目录下放对应的模板文件

packages\cli\src\cli.ts引入loadTemplate.ts 执行命令wzm-cli-demo create

3.根据传参选择对应的脚手架

// \packages\cli\src\utils\loadTemplate.ts

// 本地模板,fs操作
// import {readFile} from 'fs-extra'
import {copy, emptyDir } from 'fs-extra'
import { join } from 'node:path'

// readFile(join(__dirname,'../../cli/package.json')).then(data=>{
//     console.log('加载本地脚手架',data.toString())
// })

export const loadTemplate = async (templateName:string) => {
    // 1. 获取模板路径
    const templatePath = join(__dirname,`../templates/${templateName}`)
    // console.log('templatePath',templatePath)
    const targetPath = `${process.cwd()}/wzm-demo`
    // 2. 清空目标路径
    await emptyDir(targetPath)
    // 3. 拷贝模板到目标路径
    await copy(templatePath,`${process.cwd()}/wzm-demo`)
    console.log('模板加载完成')
}
// packages\cli\src\commands\base\create.ts

import { Command } from 'commander'
import { logger } from '../../utils/logger'
import { loadTemplate } from '../../utils/loadTemplate'
import pc from 'picocolors'

export const create = (program:Command) => {
 return program
        .createCommand('create')
        .arguments('<project-name>')
        .option('-f, --template <template>', 'template name')
        .description('初始化创建项目').action((projectName, options)=>{
            logger.info(pc.bgCyan('初始化创建项目'));
            logger.info(pc.bgCyan(`name: ${projectName}, options: ${JSON.stringify(options)}`));
            loadTemplate('template-ssr-vue')
        });
};

执行命令 wzm-cli-demo2 create wzm-demo --template vue3-ts wzm-demo 是定义的文件夹名称 vue3-ts 是自定义指定的templates文件夹下的某一个脚手架项目

name: wzm-demo, options: {"template":"vue3-ts"}

// packages\cli\src\commands\base\create.ts
import { Command } from 'commander'
import { logger } from '../../utils/logger'
import { loadTemplate } from '../../utils/loadTemplate'
import pc from 'picocolors'
import prompts from 'prompts'


// wzm-cli-new>wzm-cli-demo2 create wzm-demo --template template-ssr-vue
export const create = (program:Command) => {
 return program
        .createCommand('create')
        .arguments('<project-name>')
        .option('-f, --template <template>', 'template name')
        .description('初始化创建项目').action(async (projectName, options)=>{
            console.log('已经进到执行环境中',projectName)
            let { template } = options
            // 如果未指定模板,则提示用户选择
            if(!template){
            const response = await prompts({
                    type: 'select',
                    choices: [
                        { title: 'template-ssr-vue-模板', value: 'template-ssr-vue' },
                        { title: 'template-ssr-vue-ts-模板', value: 'template-ssr-vue-ts' }
                    ],
                    name: 'selectTemplate',
                    message: 'What is your framework?'
                })
              template = response.selectTemplate
              logger.info(pc.bgCyan(`手动选择了模板: ${template}`));
            }
            logger.info(pc.bgCyan('初始化创建项目'));
            logger.info(pc.bgCyan(`name: ${projectName}, options: ${JSON.stringify(options)}`));
            // loadTemplate('template-ssr-vue')
            loadTemplate(projectName, template)
        });
};

// packages\cli\src\utils\loadTemplate.ts

// 本地模板,fs操作
// import {readFile} from 'fs-extra'
import {copy, emptyDir } from 'fs-extra'
import { join } from 'node:path'

// readFile(join(__dirname,'../../cli/package.json')).then(data=>{
//     console.log('加载本地脚手架',data.toString())
// })
// wzm-cli-new>wzm-cli-demo2 create wzm-demo --template template-ssr-vue
export const loadTemplate = async (projectName:string,templateName:string) => {
    // 1. 获取模板路径
    const templatePath = join(__dirname,`../templates/${templateName}`)
    // console.log('templatePath',templatePath)
    const targetPath = `${process.cwd()}/wzm-demo`
    // 2. 清空目标路径
    await emptyDir(targetPath)
    // 3. 拷贝模板到目标路径
    await copy(templatePath,`${process.cwd()}/${projectName}`)
    console.log('模板加载完成')
}

4. 加载本地脚手架和远程脚手架模板

1. package.json

packages\cli\package.json

{
  "name": "@wzm/cli",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "dev": "tsup --watch",
    "build": "tsup"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "packageManager": "pnpm@10.14.0",
  "files": [
    "bin",
    "index.js"
  ],
  "bin": {
    "wzm-cli-demo2": "bin/wzmtest"
  },
  "dependencies": {
    "@wzm/core": "workspace:*",
    "@wzm/shared": "workspace:*",
    "giget": "^2.0.0"
  },
  "devDependencies": {
    "@types/node": "^24.3.0",
    "commander": "^14.0.0",
    "consola": "^3.4.2",
    "fs-extra": "^11.3.1",
    "picocolors": "^1.1.1",
    "prompts": "^2.4.2",
    "tsup": "^8.5.0",
    "typescript": "^5.9.2"
  }
}

2.packages\cli\src\utils\loadTemplate.ts



// 本地模板,fs操作
// import {readFile} from 'fs-extra'
import {copy, emptyDir } from 'fs-extra'
import { downloadTemplate } from 'giget'
import { join } from 'node:path'

interface loadTemplate {
    projectName: string
    templateName: string
    local?: boolean
}

// readFile(join(__dirname,'../../cli/package.json')).then(data=>{
//     console.log('加载本地脚手架',data.toString())
// })
// wzm-cli-new>wzm-cli-demo2 create wzm-demo --template template-ssr-vue
export const loadLoacalTemplate = async (params:Omit<loadTemplate,'local'>) => {
    const { projectName, templateName } = params;
    // 1. 获取模板路径
    const templatePath = join(__dirname,`../templates/${templateName}`)
    // console.log('templatePath',templatePath)
    const targetPath = `${process.cwd()}/wzm-demo`
    // 2. 清空目标路径
    await emptyDir(targetPath)
    // 3. 拷贝模板到目标路径
    await copy(templatePath,`${process.cwd()}/${projectName}`)
    console.log('模板加载完成')
}


export const loadRemoteTemplate = async (params:Omit<loadTemplate,'local'>) => {
const { projectName } = params
const { dir } = await downloadTemplate("https://codeload.github.com/design-sparx/antd-multipurpose-dashboard/tar.gz/refs/heads/main", {
    dir: join(process.cwd(), ".temp"),
    force: true
})
await emptyDir(`${process.cwd()}/projectName`)
await copy(dir, join(process.cwd(), projectName))
console.log('远程模板加载完成')
}
export const loadTemplate = async (params:loadTemplate) => {
const { local, ...resetParams } = params;
    console.log('恭喜发财', resetParams)
if(local){
    console.log('加载本地模板')
   loadLoacalTemplate(resetParams)
} else {
    console.log('加载远程模板')
    loadRemoteTemplate(resetParams)
}
}

3. packages\cli\src\commands\base\create.ts

import { Command } from 'commander'
import { logger } from '../../utils/logger'
import { loadTemplate } from '../../utils/loadTemplate'
import pc from 'picocolors'
import prompts from 'prompts'


// wzm-cli-new>wzm-cli-demo2 create wzm-demo --template template-ssr-vue
export const create = (program:Command) => {
 return program
        .createCommand('create')
        .arguments('<project-name>')
        .option('-f, --template <template>', 'template name')
        .description('初始化创建项目').action(async (projectName, options)=>{
            console.log('已经进到执行环境中',projectName)
            let { template } = options
            // 如果未指定模板,则提示用户选择
            if(!template){
            const response = await prompts({
                    type: 'select',
                    choices: [
                        { title: 'template-ssr-vue-模板', value: 'template-ssr-vue' },
                        { title: 'template-ssr-vue-ts-模板', value: 'template-ssr-vue-ts' }
                    ],
                    name: 'selectTemplate',
                    message: 'What is your framework?'
                })
              template = response.selectTemplate
              logger.info(pc.bgCyan(`手动选择了模板: ${template}`));
            }
            logger.info(pc.bgCyan('初始化创建项目'));
            logger.info(pc.bgCyan(`name: ${projectName}, options: ${JSON.stringify(options)}`));
            // loadTemplate('template-ssr-vue')
            // loadTemplate(projectName, template)
            loadTemplate({projectName, templateName:template, local:false})

        });
};

4. packages\cli\src\index.ts

// \packages\cli\tsup.config.ts
import { defineConfig } from "tsup"

export default  defineConfig ({
    "dts": true, // 在dist目录下生成dts文件
    "entry": ["src/index.ts"],
    "format": ["cjs"],
    "outDir": "dist",
    "platform": "node",
    "target": "node18",
    // 不要将 ESM-only 依赖打包进来,避免 import.meta 丢失
    "external": ["giget", "nypm", "tinyexec"]
})