当前位置: 首页 > news >正文

TS语言自定义脚手架

初始化

  • 新建文件夹
  • 初始化命令
  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=https://mirrors.huaweicloud.com/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 build

build后bin文件夹下自动新增index.js文件
验证修改是否生效都需要build

在这里插入图片描述

cnpm link
gaogao

在这里插入图片描述

安装相关工具

安装固定版本,有些版本会有bug

commander

https://www.npmjs.com/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.command(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

https://www.npmjs.com/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 -命令行交互工具

https://www.npmjs.com/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

https://www.npmjs.com/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
  .command("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);
  })

在这里插入图片描述

fs

https://url.nodejs.cn/api/fs.html

  • 该模块用于对文件系统进行更强大的操作。
cnpm i fs-extra.0.1  /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:https://gitee.com/账号/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
.command("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();
  }
}

在这里插入图片描述

  • 创建项目
    在这里插入图片描述
  • 选择模版
    在这里插入图片描述

相关文章:

  • 神经网络新手入门(1)目录
  • 责任链模式解析FilterChain
  • 2000-2020年年汇率平均价数据
  • Ubuntu 22.04.5 LTS 安装企业微信,(2025-02-17安装可行)
  • 二十多年前的苹果电源Power Mac G4 Mdd 电源接口
  • 宝塔docker 安装oracle11G
  • 【097】基于51单片机排队叫号系统【Keil程序+报告+原理图】
  • 4.【线性代数】——矩阵的LU分解
  • STC 51单片机63——关于STC8H的ADC通道切换问题
  • 软硬链接?
  • 附录2:组维接口信息大全
  • 过于依赖chatgpt编程会有哪些弊端?
  • IOT-CVE-2018-17066(D-Link命令注入漏洞)
  • ubuntu22.04安装kvm、virt-manage并配置SR-IOV操作
  • Spring Boot 启动优化✨
  • TCP协议(Transmission Control Protocol)
  • Kubernetes控制平面组件:Kubernetes如何使用etcd
  • Python基础--计算机基础
  • 用于可靠工业通信的5G-TSN集成原型:基于帧复制与消除可靠性的研究
  • Pytorch使用手册-使用 PyTorch 和 TIAToolbox 进行全视野切片图像分类(专题十七)
  • 有人做网站花了10几万/整合营销传播的方法包括
  • 哪里的网站可以做围棋死活题/手机app免费制作平台
  • wordpress 显示代码/广州中小企业seo推广运营
  • 外贸网站流量/网站免费网站免费
  • wap网站 开发/房产网站建设
  • 家里做网站买什么服务器好/做网络推广需要多少钱