项目地址
laddish/custom-cli
https://gitee.com/laddish/custom-cli
为什么?
必备模块
"dependencies": {
"axios": "^0.21.1",
"chalk": "^4.1.0",
"commander": "^7.2.0",
"download-git-repo": "^3.0.2",
"fs-extra": "^9.1.0",
"inquirer": "^8.0.0",
"ora": "^5.4.0"
}
axios用于获取仓库repo和tag
chalk用于输入彩色字体
commander命令行输入输出模块
download-git-repo跟俊repo和tag下载
fs-extra增强版的fs模块,可以判断目录是否存在
inquirer用于命令行和用户的交互选择
ora用于创建等待的loading
思路
- 先创建可执行的脚本 #! /usr/bin/env node
- 配置package.json中的bin字段 配置别名链接
- npm link 链接到本地环境 产生全局执行命令 C:\nodejs\npmglobal
- npm unlink 取消软连接
- npm link —force 强制链接
- link相当于将当前本地模块链接到npm目录下 这个npm目录可以直接访问 所以当前包就可以直接访问了
up to date in 1.274s
found 0 vulnerabilities
C:\nodejs\npmglobal\lad -> C:\nodejs\npmglobal\node_modules\lad\bin\lad
C:\nodejs\npmglobal\node_modules\lad -> C:\mycode\custom-cli
- 配置可执行命令
- commander
- 做一个命令行交互的功能
- inquirer
- 将模板下载下来
- download-git-repo
- 根据用户的选择动态生成内容
- mitalsmith
yarn add commander
目录结构
package.json
{
"name": "lad",
"version": "1.0.0",
"main": "index.js",
"bin": {
"lad": "./bin/lad",
"lad-cli": "./bin/lad"
},
"scripts": {},
"keywords": [],
"author": "",
"license": "MIT",
"dependencies": {
"axios": "^0.21.1",
"chalk": "^4.1.0",
"commander": "^7.2.0",
"download-git-repo": "^3.0.2",
"fs-extra": "^9.1.0",
"inquirer": "^8.0.0",
"ora": "^5.4.0"
}
}
bin/lad
创建后需要软连接npm link
#! /usr/bin/env node
console.log("lad-cli");
const program = require("commander");
const chalk = require("chalk");
program
.command("create <app-name>")
.description("Create a new project")
.option("-f, --force", "overwrite target directory if it exists")
.action((name, cmd) => {
//动态调用create模块去创建
// console.log(name, cmd); //需要提取这个cmd中的属性
require("../lib/create")(name,cmd)
});
program
.command("config [value]")
.description("inspect and modify the config")
.option("-g, --get <path>", "get value from option")
.option("-s, --set <path> <value>", "set option from config")
.option("-d, --delete <path>", "delete option from config")
.option("-l, --list", "list option")
.action((value, cmd) => {
console.log(value, cmd);
});
program
.command("ui")
.description("start and open lad-cli ui")
.option("-p, --port <path>", "Port used for the UI server")
.action((cmd) => {
//这里没有参数第一个
console.log(cmd);
});
program
.version(`lad-cli@${require("../package.json").version}`)
.usage(`<command> [option]`);
program.on("--help", function () {
console.log();
console.log(`Run ${chalk.cyan("lad <command> --help")} show details`);
console.log();
});
//解析用户命令执行的参数
program.parse(process.argv);
//核心功能 1.创建项目 2.更改配置文件 3.ui界面 @vue/ui
request
//通过axios来获取结果
const axios = require("axios");
axios.interceptors.response.use((res) => {
return res.data;
});
async function fetchRepoList() {
//可以通过配置文件拉取不同仓库对应的用户下的文件
return axios.get("https://api.github.com/orgs/zhu-cli/repos");
}
async function fetchTagList(repo) {
//可以通过配置文件拉取不同仓库对应的用户下的文件
console.log(`https://api.github.com/orgs/zhu-cli/${repo}/tags`)
return axios.get(`https://api.github.com/repos/zhu-cli/${repo}/tags`);
}
module.exports = {
fetchRepoList,
fetchTagList
};
util
const ora = require("ora");
function sleep(time) {
return new Promise((resolve, reject) => {
setTimeout(resolve, time);
});
}
//失败重新获取
async function wrapLoading(fn, msg, ...args) {
//制作一个等待的loading
const spinner = ora(msg);
spinner.start(); //开启加载
try {
let repos = await fn(...args);
spinner.succeed();
return repos;
} catch (error) {
spinner.fail("request failed , refetch...");
await sleep(1000);
return wrapLoading(fn, msg, ...args);
}
}
module.exports = {
sleep,
wrapLoading,
};
Creator
const inquirer = require("inquirer");
const { fetchRepoList, fetchTagList } = require("./request");
const { wrapLoading } = require("./util");
const downloadGitRepo = require("download-git-repo");
const util = require("util");
const path = require("path");
class Creator {
constructor(projectName, targetDir) {
//new的时候会调用构造函数
this.name = projectName;
this.target = targetDir;
//此时这个方法就是一个promise方法
this.downloadGitRepo = util.promisify(downloadGitRepo);
}
async fetchRepo() {
//失败重新获取
let repos = await wrapLoading(fetchRepoList, "waiting fetch template");
// console.log(repos);s
if (!repos) {
return;
}
repos = repos.map((item) => item.name);
let { repo } = await inquirer.prompt({
name: "repo",
type: "list",
choices: repos,
message: "choose a template to create project",
});
return repo;
}
async fetchTag(repo) {
//根据repo拉取版本号
let tags = await wrapLoading(fetchTagList, "waiting fetch tags", repo);
if (!tags) {
return;
}
// console.log(tags);
tags = tags.map((item) => item.name);
let { tag } = await inquirer.prompt({
name: "tag",
type: "list",
choices: tags,
message: "choose a tag to create project",
});
return tag;
}
async download(repo, tag) {
//需要先拼接出下载路径
let requestUrl = `zhu-cli/${repo}${tag ? "#" + tag : ""}`;
//把资源下载到某个路径上(后续可以增加缓存功能)
//放到系统目录中 模板 和用户的其他选择 生成结果放到当前目录下
await wrapLoading(
this.downloadGitRepo,
`downloading template ${repo}@${tag}`,
requestUrl,
path.resolve(process.cwd(), `${repo}@${tag}`)
);
return this.target;
}
async create() {
//创建
//将模板down下来 download-git-repo
// console.log(this.name);
// console.log(this.target);
//采用远程拉取 github 当前组织下的模板
let repo = await this.fetchRepo();
//再通过模板找到版本号
let tag = await this.fetchTag(repo);
console.log(repo, tag);
//下载
let downloadUrl = await this.download(repo, tag);
//单独写个类去生成模板
//编译模板
}
}
module.exports = Creator;
create
const path = require("path");
const fs = require("fs-extra");
const inquirer = require("inquirer");
const chalk = require("chalk");
const Creator = require("./Creator");
module.exports = async function (projectName, options) {
// console.log(projectName, options);
//创建项目
const cwd = process.cwd(); //获取当前命令执行的工作目录
// console.log(cwd);
const targetDir = path.join(cwd, projectName);
if (fs.existsSync(targetDir)) {
if (options.force) {
//强制创建 删除已有的
await fs.remove(targetDir);
} else {
//提示用户是否确定要覆盖
let { action } = await inquirer.prompt([
{
name: "action",
type: "list", //类型很丰富
message: "Target directory already exists, pick an action",
choices: [
{ name: "overwrite", value: "overwrite" },
{ name: "cancel", value: false },
],
},
]);
console.log(action);
if (!action) {
return;
} else if (action == "overwrite") {
console.log(`\r\n${chalk.yellow("Removing...")}\r\n`);
await fs.remove(targetDir);
}
}
}
//创建项目
const creator = new Creator(projectName, targetDir);
creator.create();
};