项目模板管理脚手架ptm-cli开发

一、ptm-cli 使用说明

  project template manager cli

  一款对项目模板/项目进行管理的脚手架工具,具有添加模板/项目编辑模板/项目删除模板/项目查看模板/项目以及下载项目根据模板初始化项目等功能。

1、特点

  • ptm-cli兼容githubgitee码云

    现在很多自行开发的脚手架都是都只能对github上模板/项目进行下载(底层使用download-git-repo),随着国内码云的发展壮大,国内开发者很多也在码云上进行代码管理,兼容gitee也十分必要;目前现有的脚手架轮子中并没有兼容码云的!

  • 管理功能全

    大多数脚手架只对某一个模板进行初始化下载,只具备指定初始化功能;ptm-cli能够对模板和项目进行管理,不仅可以自由添加删除开源的优秀项目(例如vue),还可以添加删除编辑自己开发的项目模板

2、安装

$ npm install ptm-cli -g

3、使用

1)基础帮助命令

安装完在电脑终端输入相应命令查看和执行相关操作。

# 查看脚手架可执行的相关命令语句
$ ptm 

# 查看当前版本
$ ptm -V

# 查看帮助
$ ptm -h

2)添加模板/项目

输入命令:

$ ptm-add

根据终端提示输入相关信息:

模板名:添加的模板自定义命名(建议使用引文);

url:模板/项目在github或者码云上的https/ssh克隆地址链接;

branch:模板/项目所在github或者码云上的分支名(默认master);

delGitInfo是否删除模板/项目中的原作者git的开发信息(默认true删除),对于模板管理建议默认设置为true,即初始化模板不含有其他让人的git信息,对于项目管理(自己项目)建议保留自己开发相关的git信息;

description:模板/项目的描述信息(默认为空);

例子:

xxx % ptm-add  
? 请输入模板名称 vpblogs
? 请输入模板地址(https/ssh) git@gitee.com:goodloving/vpblogs.git
? 请输入模板分支(默认master) master
? 是否删除模板中.git信息(默认删除) true
? 请输入模板描述(默认为空) 基于vuepress创建个人博客主页的模板工程

添加模板成功!

最终的模板列表为:
   vpblogs
     url:git@gitee.com:goodloving/vpblogs.git
     branch:master
     description:基于vuepress创建个人博客主页的模板工程
     delGitInfo:true
xxx % 

3)编辑模板/项目

输入命令:

$ ptm-edit templateName key content

根据终端提示输入相关信息:

templateName:本人添加过的模板/项目名

key:想要进行编辑的模板/项目的关键信息,包括:urlbranchdelGitInfodescription

content:对关键字对应内容进行编辑替代的内容;

例子(修改模板vpblogs的delGitInfo信息):

xxx % ptm-edit vpblogs delGitInfo false

修改模板成功!

最终的模板列表为:
   vpblogs
     url:git@gitee.com:goodloving/vpblogs.git
     branch:master
     description:基于vuepress创建个人博客主页的模板工程
     delGitInfo:false
xxx % 

4)查看模板/项目

输入命令:

$ ptm-list

例子:

xxx % ptm-list

模板列表为:
   vpblogs
     url:git@gitee.com:goodloving/vpblogs.git
     branch:master
     description:基于vuepress创建个人博客主页的模板工程
     delGitInfo:false
xxx % 

5)删除模板/项目

输入命令:

$ ptm-del

例子:

xxx % ptm-del
? 请输入要删除的模板名称 vpblogs

删除模板成功!

最终的模板列表为:
xxx % 

6)基于模板新建/初始化项目

输入命令:

$ ptm-init vpblogs testPTM

例子(根据模板vpblogs新建项目testPTM):

xxx % ptm-init vpblogs testPTM

开始创建项目~ 

✔ 正在创建中···

项目创建成功~ 

开始你的项目开发!
xxx % 

执行完成后在当前终端所在目录下可以看到名为testPTM的项目文件!

ptm-cli 脚手架开发

1、知识储备

1)commander

完整的node.js 命令行解决方案,用来处理终端命令行中输入命令和编写命令行指令的第三方npm库。

常用API:

  • 声明 program 变量

const { program } = require('commander');

  • 命令行输出版本号

program.version('0.0.1');

  • 命令行输出指令提示

program.usage("<command> [Options]");

  • 命令行输出单一命令输入规范提示

program.command("ptm-add", "新增一个模板库!")

  • 解析命令行参数

program.parse(process.argv);

  • ···

commander详细api

2)inquirer

处理可交互的node.js嵌入式的命令行界面的第三方npm库。

常用API:

  • 声明 inquirer 变量

const inquirer = require('inquirer');

  • 具体使用方式

inquirer
.prompt([
/* 放入可交互式提供的问题 */
])
.then(answers => {
// 命令行中接收到的输入参数
})
.catch(error => {
// 报错获取
});

  • ···

inquirer详细api

3)git-clone

通过shell命令克隆一个git存储库的第三方npm库。

常用API:

  • 声明 git-clone 变量

const clone = require('git-clone');

  • 具体使用方式

clone(repo, targetPath, [options], cb);
克隆一个repo路径的git仓库到targetPath目录下,回调函数cd用来捕捉克隆结果;

  • options可选参数:
  • git: git的二进制路径(可选).
  • shallow: 当为true时克隆深度为1 (可选).
  • checkout: 切换到当前分支(可选).

git-clone详细api

4)chalk

改变终端输出样式的第三方npm库。

常用API:

  • 声明 chalk 变量

const chalk = require('chalk');

  • 具体使用方式

console.log(chalk.blue('Hello world!'));

chalk详细api

5)ora

添加优雅的终端转轮的第三方npm库。

常用API:

  • 声明 ora 变量

const ora = require('ora');

  • 具体使用方式

const spinner = ora('提示内容···');

  • 开始显示转轮

spinner.start()

  • 错误/失败显示转轮

spinner.fail()

  • 成功转轮显示

spinner.succeed()

  • ···

ora详细api

6)rimraf

封装rm -rf命令,用来删除文件和文件夹的第三方npm库。

常用API:

  • 声明 rimraf 变量

const rm = require("rimraf").sync;

  • 具体使用方式,删除指定file文件/文件夹

rm(file, [opts], callback)

  • ···

rimraf详细api

2、初始化项目

  新建项目文件夹PTM_CLI,在项目文件夹下打开终端执行初始化操作npm init,与终端进行交互操作生成含有项目信息的package.json文件,依次安装步骤1中的6个要用到的第三方npm库:npm install xxx -g(也可直接将依赖写入package.json中的dependences中,直接执行npm install);
  打开package.json文件,添加终端命令执行语句(bin区域下):

{
  "name": "ptm-cli",
  ···
  "private": false,
  "author": {
    "name": "wawoweb",
    "wechat(公众号)": "wawoweb  /  哇喔WEB",
    "wechat": "h17179797429",
    "email": "936106161@qq.com"
  },
  "bin": {
    "ptm": "./bin/ptm.js",
    "ptm-init": "./bin/ptm-init.js",
    "ptm-list": "./bin/ptm-list.js",
    "ptm-add": "./bin/ptm-add.js",
    "ptm-del": "./bin/ptm-del.js",
    "ptm-edit": "./bin/ptm-edit.js"
  },
 
  ···
  "dependencies": {
    "chalk": "^4.1.0",
    "commander": "^6.2.1",
    "git-clone": "^0.1.0",
    "inquirer": "^7.3.3",
    "ora": "^5.1.0",
    "rimraf": "^3.0.2"
  }
}

同时,在项目文件夹下新建目录bin,并在bin文件夹下新建package.json中对应的6个文件:

./bin/ptm.js         终端命令ptm执行文件
./bin/ptm-init.js    终端命令ptm-init执行文件(根据模板初始化项目)
./bin/ptm-list.js    终端命令ptm-list执行文件(查看当前模板列表)
./bin/ptm-add.js     终端命令ptm-add执行文件(添加新的模板)
./bin/ptm-del.js     终端命令ptm-del执行文件(删除指定模板)
./bin/ptm-edit.js    终端命令ptm-edit执行文件(编辑指定模板指定信息内容)

最后在根目录下新建模板存储文件template.json,项目目录结构如下:

image.png

3、功能开发

  在上述新建bin目录下的6个功能文件最上方添加#!/usr/bin/env node:配置#!/usr/bin/env node, 就是解决了不同系统node路径不同,让系统动态的去查找node来执行你的脚本文件。

1)ptm(脚手架命令提示)

分析:

  当用户不了解ptm-cli时,输入ptm可以向用户展示可用的所用命令语句和含义(commander);

代码(ptm.js):

#!/usr/bin/env node
const program = require("commander");
const package = require("../package.json");

// 定义当前版本
// 定义使用方法
// 定义五个指令
program
  .version(package.version)
  .usage("<command> [Options]")
  .command("ptm-add", "新增一个模板库!")
  .command("ptm-del", "删除一个模板库!")
  .command("ptm-list", "查看模板库列表!")
  .command("ptm-edit templatename key content", "修改模板库信息!")
  .command("ptm-init templatename projectName", "基于模板库创建一个新的工程!");

// 解析命令行参数
program.parse(process.argv);

2)ptm-add(添加模板)

分析:

  将用户指定模板添加到template.json中存储起来,需要与用户进行交互(inquirer),涉及到文件的读写(fs),将执行结果向用户展示(chalk);

代码(ptm-add.js):

#!/usr/bin/env node
//交互式命令行库
const inquirer = require("inquirer");
//控制台样式库
const chalk = require("chalk");
//node内置文件模块库
const fs = require("fs");
//读取模板配置文件
const tpConfig = require(`${__dirname}/../template.json`);
// 打印模板列表的公共函数
const printPtmList = require("../utils").printPtmList;

//自定义交互式命令行的问答
let questions = [
  {
    name: "name",
    type: "input",
    message: "请输入模板名称",
    validate(val) {
      if (val === "") {
        return "模板名不能为空!";
      } else if (tpConfig[val]) {
        return "模板名已经存在!";
      } else {
        return true;
      }
    },
  },
  {
    name: "url",
    type: "input",
    message: "请输入模板地址(https/ssh)",
    validate(val) {
      if (val === "") return "模板地址不能为空!";
      return true;
    },
  },
  {
    name: "branch",
    type: "input",
    message: "请输入模板分支(默认master)",
    default: "master",
  },
  {
    name: "delGitInfo",
    type: "input",
    message: "是否删除模板中.git信息(默认删除)",
    default: true,
  },
  {
    name: "description",
    type: "input",
    message: "请输入模板描述(默认为空)",
    default: "",
  },
];

inquirer.prompt(questions).then((answers) => {
  // 获取用户输入的内容
  let { name, url, branch, description, delGitInfo } = answers;
  //过滤Unicode的字符
  tpConfig[name] = {
    url,
    branch,
    description,
    delGitInfo,
  };
  // 将模板信息写入template.json文件中s
  fs.writeFile(
    `${__dirname}/../template.json`,
    JSON.stringify(tpConfig),
    "utf-8",
    (err) => {
      if (err) {
        console.log(chalk.red(`\n添加模板失败:${err}\n`));
      } else {
        console.log(chalk.green("\n添加模板成功!\n"));
        console.log("最终的模板列表为:");
        printPtmList(tpConfig);
      }
    }
  );
});

模板列表打印函数封装(utils.js):

const chalk = require("chalk");
const printPtmList = (tpConfig) => {
  //遍历模板展示出来
  for (const key in tpConfig) {
    if (tpConfig.hasOwnProperty(key)) {
      const item = tpConfig[key];
      console.log(chalk.blue(`   ${key}`));
      for (const i in item) {
        if (item.hasOwnProperty.call(item, i)) {
          const el = item[i];
          console.log(chalk.blue(`     ${i}:${el}`));
        }
      }
    }
  }
};
module.exports = {
  printPtmList
};

3)ptm-list、ptm-del、ptm-edit

分析:

  • ptm-list:读取template.json文件(fs),对json格式数据输出打印(chalk);
  • ptm-del:读取template.json文件(fs),与用户交互(inquirer),删除指定模板信息,输出删除后结果(chalk)
  • ptm-edit:读取template.json文件(fs),与用户交互(inquirer),指定模板的信息进行修改(commander),输出最终修改结果(chalk);

代码简单(省略)

4)ptm-init(根据模板初始化项目)

分析:

  提示用户ptm-init命令必须的参数设置,对输入参数进行判断,读取template.json信息提取新建项目基于的模板信息,从github或者gitee(码云)clone git库文件,根据配置中要求判断是否删除原作者的git开发信息,输出init结果

代码

ptm-init.js

#!/usr/bin/env node
const program = require("commander");
const chalk = require("chalk");
const ora = require("ora");
const gitclone = require("git-clone");
const tpConfig = require(`${__dirname}/../template`);
const rm = require("rimraf").sync;

program.usage("templatename projectName").parse(process.argv);

//判断输入情况
if (program.args.length < 1) {
  return program.help();
}
//输入参数提取
let templateName = program.args[0];
let projectName = program.args[1];
//参数校验
if (!tpConfig[templateName]) {
  console.log(chalk.red("当前模板不存在!\n"));
  return;
}
if (!projectName) {
  console.log(chalk.red("新建项目名不能为空! \n"));
  return;
}

let temp = tpConfig[templateName];
//提取模板的url
let url = temp.url;
//提取分支
let branch = temp.branch;

console.log(chalk.greenBright("\n开始创建项目~ \n"));
//显示加载图标
const spinner = ora("正在创建中···");
spinner.start();

//下载所需额外参数
let cloneOptions = {
  checkout: branch,
  shallow: branch === "master",
};

// 下载git上模板代码
gitclone(url, projectName, cloneOptions, (err) => {
  if (err) {
    spinner.fail();
    console.log(chalk.red(`\n创建项目失败:${err}\n`));
  } else {
    if (temp.deldelGitInfo) {
      // 删除.git相关文件
      rm(projectName + "/.git");
    }

    //成功
    spinner.succeed();
    console.log(chalk.green("\n项目创建成功~ \n"));
    console.log(chalk.green("开始你的项目开发!"));
  }
});

4、npm发布

  • 1)确认package.json中npm发布内容是否完善正确

    • "name": "ptm-cli", //发布到npm上的库的名字
    • "version": "1.0.0", //当前发布版本号(x.y.z格式规定:x大的版本号,大版本号不同代表不向下兼容;y小版本号,当前版本较大改动,向下兼容;z代码较小改动或者bug修改)
    • "description": "", //npm库的简要描述
    • "private": false, //是否公开设置
    • "keywords": [], //npm上搜索的关键词设置
    • ···
  • 2)npm登录和发布

    • 去npm网站注册账号;
    • 终端输入npm login,根据提示输入用户名、密码、邮箱;
    • 终端输入npm publish 发布,查看结果是否发布成功;
    • 强制撤回24小时内发布的npm库,输入npm unpublish --force
  • 3)npm发布验证

    • 在npm网站搜索中输入发布的npm库名,能够搜索到说明已经发布成功!
    image
    • 本地下载验证
      打开本地终端输入npm install ptm-cli -g下载安装发布的npm包,安装完执行自定义命令有效!
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,311评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,339评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,671评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,252评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,253评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,031评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,340评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,973评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,466评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,937评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,039评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,701评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,254评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,259评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,485评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,497评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,786评论 2 345

推荐阅读更多精彩内容