TS语言自定义脚手架
- 人工智能
- 2025-09-04 16:21:04

初始化 新建文件夹初始化命令 npm init -y tsc --init npm i @types/node npm i typescript # 处理别名 npm i -D tsc-alias
-y 表示选项都为yes 安装ts相关依赖
新建相关文件bin 文件夹
src文件夹
commands 文件夹 (命令
utils 文件夹 (封装方法)
index.ts文件export * from "./chalk"index.ts 文件
#! /usr/bin/env node console.log('hello gaogao')#! /usr/bin/env node 前后不可有空格 #!用于指定脚本的解释程序,开发npm包这个指令一定要加
.gitignore 文件
#basic node_module package-lock.json #build bin .npmrc 文件 registry= mirrors.huaweicloud /repository/npm TS配置 建【tsconfig.json】 { "compilerOptions": { "target": "ES6", "module": "commonjs", "outDir": "./bin", // 输出地址 相对路径 "baseUrl": "./", "strict": true, "moduleResolution": "node", "esModuleInterop": true, "skipLibCheck": true, "forceConsistentCasingInFileNames": true, "resolveJsonModule": true, "paths":{ "@":["src"], "@utils":["utils"], } }, "include": [ "./src", "src/**/*.ts", "src/**/*.d.ts" ] } 修改【package.json】bin:执行的文件或命令 scripts-build 处理ts文件
{ "name": "gaogao-cli", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "build": "tsc && tsc-alias" }, "bin": { "gaogao": "/bin/src/index.js" }, "keywords": [], "author": "", "license": "ISC", "dependencies": { "@types/figlet": "^1.7.0", "@types/fs-extra": "^11.0.4", "@types/inquirer": "^9.0.7", "@types/log-symbols": "^3.0.0", "@types/node": "^22.13.2", "@types/shelljs": "^0.8.15", "chalk": "^4.0.0", "commander": "^9.0.0", "download-git-repo": "^3.0.2", "figlet": "^1.8.0", "fs-extra": "^10.0.1", "inquirer": "^8.2.1", "loading-cli": "^1.1.2", "log-symbols": "^4.1.0", "ora": "^5.4.1", "shelljs": "^0.8.5", "table": "^6.9.0", "typescript": "^5.7.3" }, "devDependencies": { "tsc-alias": "^1.8.10" } } 测试ts语言需要先build
npm run buildbuild后bin文件夹下自动新增index.js文件 验证修改是否生效都需要build
cnpm link gaogao 安装相关工具安装固定版本,有些版本会有bug
commander.npmjs /package/commander
处理控制台命令工具解析用户输入时一些参数 例如 create 就是利用此工具做解析辅助 cnpm i commander@9.0.0 import {program} from 'commander' import Pack from "../package.json" program.version(Pack.version, "-v, --version"); program.parse(process.argv)//nodejs提供的属性 封装常用命令 commands文件夹下新建create文件夹 文件 import commandCreate from'./create' // create见 create命令目录 const commands:any = { 'create <project-name>':{ description:'create a project', option:[ { cmd:'-f,--force', msg:'overwrite target directory if it exists' } ], action:commandCreate } } export default commands import { Command } from "commander"; import Commands from "@commands"; //index.ts Object.keys(Commands).forEach((command:any) => { const current:any = program mand(command); if (Commands[command].option && Commands[command].option.length) { let options = current.option Commands[command].option.forEach((item: { cmd: string; msg: any }) => { current.option(item.cmd, item.msg || ""); }); } current.action(Commands[command].action); }); chalk 美化工具 该模块用于添加颜色和样式到控制台输出效果见【figlet】
import chalk from 'chalk' console.log("\r\n" + chalk.greenBright.bold('hello gaogao-cli')) 封装字体处理 import chalk from 'chalk'; export const Cblue= (text:string) =>chalk.blue(text) export const Cred= (text:string) =>chalk.red(text) export const Cgreen= (text:string) =>chalk.green(text) figlet.npmjs /package/figlet
该模块用于生成ASCII艺术字具体字体可以去figlet官网查看
cnpm i figlet@1.5.2 @types/figlet import chalk from 'chalk' import figlet from 'figlet' program .name("gaogao") .description("gaogao-cli") .usage("<command> [options]") // 用在内置的帮助信息之后输出自定义的额外信息 .on("--help", () => { console.log("\r\n" + chalk.greenBright.bold(figlet.textSync("gaogao-cli", { font: "Standard", horizontalLayout: "default", verticalLayout: "default", width: 100, whitespaceBreak: true, }))) console.log(`\r\n Run ${chalk.cyanBright(`gaogao-cli <command> --help`)} for detailed usage of given command.`) }); inquirer -命令行交互工具.npmjs /package/inquirer
该模块用于实现交互式命令行界面 例如vue询问是否单元测试 cnpm i inquirer@8.2.1 @types/inquirer 封装inquirer - lib文件夹下`新建interactive.ts `文件 import inquirer from 'inquirer'; /** * @param {string} message 询问提示语句 * @returns {Object} 根据name属性获取用户输入的值{confirm: y/n} */ export const inquirerConfirm = async (message:string): Promise<object> => { const answer = await inquirer.prompt({ type: "confirm", name: "confirm", message, }); return answer; } /** * * @param {string} name 询问事项 * @param {string} message 询问提示语句 * @param {Array} choices 选择模板列表,默认读取对象的name属性 * @returns {Object} 根据name属性获取用户输入的值{请选择项目模板: xxxxxx} */ export const inquirerChoose = async (name:string,message:string, choices:Array<any>): Promise<any> => { const answer = await inquirer.prompt({ type: 'list', name, message, choices, }); return answer; } /** * @param {Array} messages 询问提示语句数组 * @returns {Object} 结果对象 */ export const inquirerInputs = async (messages: Array<any>): Promise<object> => { const questions = messages.map(msg => { return { name: msg.name, type: "input", message: msg.message, } }) const answers = await inquirer.prompt(questions); return answers } loading-cli.npmjs /package/loading-cli
utils 下新建loading文件在这里插入代码片 //loading.ts import loading, { Options, Loading } from "loading-cli"; class Load { load: null | Loading; constructor() { this.load = null; } /** * @Descripttion: 开始loading状态 * @msg: * @param {Options} options * @return {*} */ start (options: Options | string) { if(!this.load){ typeof options==='object' &&!options.frames&&(options.frames=['<','<','^','>','>','_','_']) this.load = loading(options).start() }else{ this.load.start(options as string) } }; stop () { this.load && this.load.stop(); }; succeed(text='success') { this.load && this.load.succeed(text); }; warn(text: string) { this.load && this.load.warn(text); }; info (text: string){ this.load && this.load.info(text); }; } export default new Load(); // index.ts program mand("loading") .description("View all available templates") .action(() => { loading.start({ color: "red", text: "begin", }); setTimeout(() => { loading.warn("警告"); setTimeout(() => { loading.info("提示"); setTimeout(() => { loading.stop(); }, 2000); }, 2000); }, 2000); }) fsurl.nodejs /api/fs.html
该模块用于对文件系统进行更强大的操作。 cnpm i fs-extra@10.0.1 @types/fs-extra 封装文件处理方法 import fs from "fs"; import { Cred } from "./chalk"; export const readDir = (path: string): Promise<any> => new Promise((res, rej) => { fs.readdir(path, (err) => { if (!err) res(true); res(false) }); }); export const mkdir = (path: string): Promise<any> => new Promise((res, rej) => { fs.mkdir(path, (err) => { if (!err) res(""); rej( `${Cred("Can not mak dir")} ${ typeof err === "string" ? err : JSON.stringify(err) }` ); }); }); export const rm = (path: string): Promise<any> => new Promise((res, rej) => { fs.rm(path,{ recursive: true}, (err) => { if (!err) res(""); rej( `${Cred("Can not remove dir:"+ path)} ${ typeof err === "string" ? err : JSON.stringify(err) }` ); }); }); 其他常用工具 # 安装ora模块,该模块用于显示动画加载效果。 cnpm i ora@5.4.1 # 安装download-git-repo模块,该模块用于下载并提取Github/Git(template本地)仓库中的文件。 cnpm i download-git-repo@3.0.2 # 安装handlebars模块,该模块用于处理模板文件。 cnpm i handlebars@4.7.6 # 安装log-symbols模块,该模块用于在控制台输出不同类型的日志符号(√或×)。 cnpm i log-symbols@4.1.0 # 安装axios模块,该模块用于发起HTTP请求。 cnpm i axios@0.26.1 # 安装gitee-repo模块,该模块用于从Gitee仓库中下载模板文件。 cnpm i gitee-repo@0.0.2 # 命令行界面表格内容显示 cnpm i table # 基于nodejs的shell命令工具 cnpm i shelljs @types/shelljs # 在控制台输出不同类型的日志符号(√或×) cnpm i log-symbols@4.1.0 @types/log-symbols 配置模版文件 lib文件夹下新建constants.ts 文件 //constants.ts /** * 项目模板列表 */ export const templates = [ { name: "vue-template", value: "direct: gitee /账号/vue-template.git", desc: "基于vite的自定义vue项目模板", }, ]; /** * 项目信息 */ export const messages = [ { name: "name", message: "请输入项目名称:", }, { name: "description", message: "请输入项目描述:", }, ]; //index.ts import { table } from 'table'; import { templates } from '../lib/constants' // 查看模板列表 program mand("ls") .description("View all available templates") .action(() => { const data = templates.map(item => [chalk.greenBright(item.name), chalk.white(item.value), chalk.white(item.desc)]); data.unshift([chalk.white("Template name"), chalk.white("Template address"), chalk.white("Template description")]); console.log(table(data)); }) gaogao ls create命令 create commands文件夹下新建create文件夹 文件 import fs from "fs-extra"; import ora from "ora"; import inquirer from "inquirer"; import path from "path"; import { exec } from "child_process"; import { readDir, mkdir, rm, Cred, readFile, copyFolder, loading, inquirerChoose } from "@utils"; import { TempLatesRepo, TempLatesName, templates } from "../../constant/repo"; export default async function (projectName: any, options: any) { const cwd = process.cwd(); const targetDirectory = path.join(cwd, projectName); if (fs.existsSync(targetDirectory)) { if (options.force) { // 存在force配置项,直接覆盖 await fs.remove(targetDirectory); } else { // 不存在force配置 项,询问是否覆盖 let { isOverwrite } = await inquirerChoose( "isOverwrite", "Target directory exists,Please choose an action.", [ { name: "Overwrite", value: true, }, { name: "Cancel", value: false, }, ] ); if (!isOverwrite) { console.log("Cancel"); return; } else { loading.start(`Removing ${projectName},please wait a minute`); await fs.remove(targetDirectory); loading.stop(); } } } const spinner = ora("Creating a project......").start(); try { await mkdir(targetDirectory); const TemplatePath = `${process.cwd()}/${TempLatesName}`; if (await readDir(TemplatePath)) { spinner.fail(Cred(`${TempLatesName} is existed!Please remove it`)); process.abort(); } spinner.stop() //下载模版git //TempLatesRepo 模版git地址 exec(`git clone ${TempLatesRepo}`, async (err) => { if(err){ spinner.fail(Cred("can not clone this repo,please try again later!")); spinner.fail(Cred(typeof err === "string" ? err : JSON.stringify(err))); process.abort(); } const project =await readFile( `${TemplatePath}/project.json`) //读取模版工程中project.json文件。存放模版list const question = [ { type: "list", message: "Please select a project template:", name: "template", choices: JSON.parse(project),// string[] }, ]; const { template } = await inquirer.prompt(question); const newPath = targetDirectory; const oldPath = TemplatePath + "/" + template; loading.start("Template generation..."); await copyFolder(oldPath, newPath); // 删除克隆模板 await rm(TemplatePath); loading.stop() }); } catch (err) { console.log(Cred("Project creation failure")); spinner.fail(Cred(typeof err === "string" ? err : JSON.stringify(err))); process.abort(); } } 创建项目 选择模版TS语言自定义脚手架由讯客互联人工智能栏目发布,感谢您对讯客互联的认可,以及对我们原创作品以及文章的青睐,非常欢迎各位朋友分享到个人网站或者朋友圈,但转载请说明文章出处“TS语言自定义脚手架”