在开发过程中,我们会通过官方的脚手架搭建项目,这没问题。在我们把路由,工具函数,layout,网络封装以及页面都写好时,这个时候你要开发一个新项目,难道又重新来一次吗?
粘贴复制是麻烦的,那可以开发一款符合公司的脚手架,基于脚手架一键生成基本demo,将上述的layout、utils、page、router等集成进去,其他的时间就可以摸鱼了🐠
需求分析
脚手架三要素分别是创建项目命令,版本号和选项,以下的工作都将围绕着这三要素进行
bin
例如vue/cli 和 create-react-app创建vue项目和react项目,而创建一个脚手架的第一步就是package.json中的bin字段。
当在package.json中添加这个字段时,会创建一个脚本到/user/local/bin/cli-create的符号连接,将运行该脚本。
本地开发时先执行npm link将该包链接到npm目录下 ,这个目录可以直接访问,完成之后会生成cli-create命令
{
"bin": {
"cli-create": "index.js"
},
}
然后在index.js文件中首行添加 \#!/usr/bin/env node表示在node环境下执行
至此,第一步脚手架全局命令就成功了,接着就是利用Commander添加具体的命令和选项,如cli-create create vue-app
Commander
nodejs命令行的解决方案
安装
npm install commander
脚手架版本
const { Command } = require("commander");
const program = new Command();
program.version("cli-create@" + require("./package.json").version); // 定义脚手架版本
脚手架选项
Commander 使用.opton()方法来定义选项,同时可以附加选项的简介。
解析后的选项可以通过Command对象上的.opts()方法获取,同时会被传递给命令处理函数。
#!/usr/bin/env node
const { Command } = require("commander");
const program = new Command();
program.version("cli-create@" + require("./package.json").version); // 定义脚手架版本
/**
* 定义选项
* 需要注意两个点
* 1是不能将--name和<value>写在一起,如--name<value>,必须有一个空格,--name <value>
* 2是option要单独写,不能将其写在program.command后面,不然读不出来
*/
program.option("-n, --name <value>", "this is cli-create's name","cli-create"); // 定义脚手架选项
const options = program.opts(); // 获取脚手架所有的选项
console.log(options);
program.parse(process.argv);
脚手架命令
通过.command()配置命令。
.command()的第一个参数为命令名称。命令参数可以跟在名称后面,也可以用.argument()单独指定。参数可为必选的(尖括号表示)、可选的(方括号表示)或变长参数(点号表示,如果使用,只能是最后一个参数)。
#!/usr/bin/env node
const { Command } = require("commander");
const program = new Command();
program
.command("create <project-name> [second]")
.description("create a new project by cli-create")
.action((name, second) => {
console.log(name, second); // 打印 create命令后面的参数
});
program.parse(process.argv);
到这里,我们已经创建了最基础的命令,接下来就是在 action 这个方法中处理
action使用
通过命令create 创建项目,需要一个代码仓库模版通过类似于git clone url方式拉取下来,再进入到文件夹中通过 npm install 下载依赖,最后执行 npm run serve 。到此一个最简单的脚手架就创建完成了。
download-git-repo
一个 Node.js 模块,它允许你直接从 Git 仓库下载项目,支持github,gitlab,不支持码云
npm install download-git-repo
参数详情如下:
- 第一个是模版仓库地址,默认
master分支,通过在末尾添加#main切换到main分支- 建议通过direct和clone的方式,不然需要将完整的url传递给zip文件
- 第二个是要下载的文件路径
- 第三个是选项,
alone:true表示通过git clone的方式下载
#!/usr/bin/env node
const { Command } = require("commander");
const download = require("download-git-repo");
const program = new Command();
program
.command("create <project-name> [repositoryUrl]")
.description("create a new project by cli-create")
.action((name, second) => {
console.log("开始下载脚手架模版~");
download(
"direct:https://github.com/wczy-ao/project-vue2-template.git#main",
name,
{
clone: true,
},
function (err) {
if (!err) {
console.log("下载成功~");
}
}
);
});
program.parse(process.argv);
安装模版依赖
上面已经把模版代码下载下来了,但是没有通过npm install / yarn / pnpm i添加依赖,这一步可做可不做,因为不知道当前用户习惯用哪个,如果用的不适合则有可能会出现混乱。
在这里我们先使用pnpm安装,后面优化通过命令来实现。
pnpm需要nodejs版本至少 18.12,所以我们需要针对这个判断一下,我们改一下代码
const pnpmVersion = 18.12;
program
.command("create <project-name> [repositoryUrl]")
.description("create a new project by cli-create")
.action((name, second) => {
console.log("开始下载脚手架模版~");
const nodeVersionArr = process.version.substring(1).split(".");
const nodeVersion = nodeVersionArr[0] + "." + nodeVersionArr[1];
download(
"direct:https://github.com/wczy-ao/project-vue2-template.git#main",
name,
{
clone: true,
},
function (err) {
if (!err) {
console.log(
"code repository dowload success, start download node_modules by pnpm, please wait"
);
if (pnpmVersion - Number(nodeVersion) > 0) {
console.log(
"pnpm need nodejs version at least V18.12,please upgrade node version"
);
} else {
// 开始安装 node_modules
}
}
}
);
});
安装node_modules通过child_process的spawn来实现
- 第一个参数是要执行的命令(字符串)
- 第二个参数是一个数组,包含了传递给命令的所有参数。
- 事件监听
stdout:当子进程产生标准输出时触发。stderr:当子进程产生错误输出时触发。close:当子进程退出时触发,提供退出代码。
spawn默认不缓冲I/O数据,而是以流的形式实时读取子进程的标准输出和错误输出,因此更适合处理大量数据或长时间运行的任务
const { spawn } = require("child_process");
program. .action((name, second) => {
console.log("start download project template~");
download(
"direct:https://github.com/wczy-ao/project-vue2-template.git#main",
name,
{
clone: true,
},
function (err) {
if (!err) {
console.log(
"code repository dowload success, start download node_modules by pnpm, please wait"
);
if (pnpmVersion - Number(nodeVersion) > 0) {
console.log(
"pnpm need nodejs version at least V18.12,please upgrade node version"
);
} else {
// 开始安装 node_modules
const result = spawn("pnpm", ["i"], {
cwd: `./${name}`,
});
}
}
}
);
});
spawn是异步的,我们这里通过promise优化一下
// lib/handleSpawn.js
const { spawn } = require("child_process");
function handleSpawn(...args) {
return new Promise((resolve, reject) => {
const result = spawn(...args);
result.stdout.pipe(process.stdout); // 成功后将子进程的标准输出通过管道传输到当前进程的标准输出
result.stderr.pipe(process.stderr); // 同样可以将子进程的标准错误也重定向到当前进程的标准错误
result.on("close", () => {
resolve(); // 结束之后再resolve出去
});
});
}
module.exports = {
handleSpawn,
};
// index.js
program
.command("create <project-name> [repositoryUrl]")
.description("create a new project by cli-create")
.action((name, second) => {
console.log("start download project template~");
download(
"direct:https://github.com/wczy-ao/project-vue2-template.git#main",
name,
{
clone: true,
},
async function (err) {
if (!err) {
console.log(
"code repository dowload success, start download node_modules by pnpm, please wait"
);
if (pnpmVersion - Number(nodeVersion) > 0) {
console.log(
"pnpm need nodejs version at least V18.12,please upgrade node version"
);
} else {
// 开始安装 node_modules
await handleSpawn("pnpm", ["i"], {
cwd: `./${name}`,
});
await handleSpawn("pnpm", ["run", "start"], {
cwd: `./${name}`,
});
}
}
}
);
});
至此,一个简单的脚手架就开发完成了。但在pnpm i时候会有很长的间隔,可以针对这些进行一定的优化
优化
代码优化
上面代码回调过多,这里整改一下
// index.js
#!/usr/bin/env node
const { Command } = require("commander");
const { createCommander } = require("./lib/createCommanders");
const program = new Command();
program.version("cli-create@" + require("./package.json").version); // 定义脚手架版本
program.option("-n, --name <value>", "this is cli-create's name", "cli-create");
createCommander(program);
program.parse(process.argv);
// lib/handleLoading.js
const loadResult = () => {
return new Promise((resolve) => {
import("ora").then((res) => {
const spinner = res.default("loading").start();
spinner.color = "yellow";
spinner.text = "Loading...";
resolve(spinner);
});
});
};
module.exports = {
loadResult,
};
// lib/handleChalk.js
const chalkResult = () => {
return new Promise((resolve) => {
import("chalk").then((res) => {
resolve(res.default);
});
});
};
module.exports = {
chalkResult,
};
// handleSpawn.js
const { spawn } = require("child_process");
function handleSpawn(...args) {
return new Promise((resolve, reject) => {
const result = spawn(...args);
result.stdout.pipe(process.stdout);
result.stderr.pipe(process.stderr);
result.on("close", () => {
resolve();
});
});
}
module.exports = {
handleSpawn,
};
// createCommanders.js
const download = require("download-git-repo");
const { loadResult } = require("./handleLoading");
const { chalkResult } = require("./handleChalk");
const { handleSpawn } = require("./handleSpawn");
const createCommander = (program) => {
const pnpmVersion = 18.12;
const nodeVersionArr = process.version.substring(1).split(".");
const nodeVersion = nodeVersionArr[0] + "." + nodeVersionArr[1];
program
.command("create <project-name> [repositoryUrl]")
.description("create a new project by cli-create")
.action(async (name, second) => {
const chalk = await chalkResult();
const spinner = await loadResult();
download(
"direct:https://github.com/wczy-ao/project-vue2-template.git#main",
name,
{
clone: true,
},
async function (err) {
if (err) {
spinner.fail("download error");
}
if (!err) {
console.log(
"code repository dowload success, start download node_modules by pnpm, please wait"
);
if (pnpmVersion - Number(nodeVersion) > 0) {
console.log(
"pnpm need nodejs version at least V18.12,please upgrade node version"
);
} else {
// 开始安装 node_modules
await handleSpawn("pnpm", ["i"], {
cwd: `./${name}`,
});
spinner.succeed("download success");
// await handleSpawn("pnpm", ["run", "start"], {
// cwd: `./${name}`,
// });
}
}
}
);
});
};
module.exports = {
createCommander,
};
依赖下载增加 loading
ora一个用于终端环境的美观、简洁的加载动画和进度反馈工具。开发者经常使用 ora 来增强命令行应用的用户体验,比如显示加载状态、任务完成情况或是错误提示等。
pnpm add ora
ora默认是esmodule,所以这里需要动态导入一下
const download = require("download-git-repo");
const { loadResult } = require("./handleLoading");
const { handleSpawn } = require("./handleSpawn");
const createCommander = (program) => {
const pnpmVersion = 18.12;
const nodeVersionArr = process.version.substring(1).split(".");
const nodeVersion = nodeVersionArr[0] + "." + nodeVersionArr[1];
program
.command("create <project-name> [repositoryUrl]")
.description("create a new project by cli-create")
.action(async (name, second) => {
const spinner = await loadResult();
download(
"direct:https://github.com/wczy-ao/project-vue2-template.git#main",
name,
{
clone: true,
},
async function (err) {
if (err) {
spinner.fail("download error");
}
if (!err) {
console.log(
"code repository dowload success, start download node_modules by pnpm, please wait"
)
if (pnpmVersion - Number(nodeVersion) > 0) {
spinner.fail("pnpm need nodejs version at least V18.12,please upgrade node version");
} else {
// 开始安装 node_modules
await handleSpawn("pnpm", ["i"], {
cwd: `./${name}`,
});
spinner.succeed("download success");
// await handleSpawn("pnpm", ["run", "start"], {
// cwd: `./${name}`,
// });
}
}
}
);
});
};
module.exports = {
createCommander,
};
打印提供状态颜色
我们针对不同的信息也需要用颜色标注出来,比如错误红色,成功绿色等
chalk 允许你在终端(命令行界面)中使用颜色和样式来输出文本。
pnpm add chalk
const download = require("download-git-repo");
const { loadResult } = require("./handleLoading");
const { chalkResult } = require("./handleChalk");
const { handleSpawn } = require("./handleSpawn");
const createCommander = (program) => {
const pnpmVersion = 18.12;
const nodeVersionArr = process.version.substring(1).split(".");
const nodeVersion = nodeVersionArr[0] + "." + nodeVersionArr[1];
program
.command("create <project-name> [repositoryUrl]")
.description("create a new project by cli-create")
.action(async (name, second) => {
const chalk = await chalkResult();
const spinner = await loadResult();
download(
"direct:https://github.com/wczy-ao/project-vue2-template.git#main",
name,
{
clone: true,
},
async function (err) {
if (err) {
spinner.fail("download error");
}
if (!err) {
console.log(chalk.green(
"code repository dowload success, start download node_modules by pnpm, please wait"
))
if (pnpmVersion - Number(nodeVersion) > 0) {
spinner.fail("pnpm need nodejs version at least V18.12,please upgrade node version");
} else {
// 开始安装 node_modules
await handleSpawn("pnpm", ["i"], {
cwd: `./${name}`,
});
spinner.succeed("download success");
// await handleSpawn("pnpm", ["run", "start"], {
// cwd: `./${name}`,
// });
}
}
}
);
});
};
module.exports = {
createCommander,
};
增加代码仓库地址选项
上面的代码模版是我的,对于开发者来说,肯定是不试用的,那么我这里提供第二选项,传入相应的代码仓库地址,同样可以下载下来。
cli-create create myApp 'https://github@1sss:xxxxx#main.git'
program
.command("create <project-name> [repositoryUrl]")
.description("create a new project by cli-create")
.action(async (name, projectUrl) => {
const chalk = await chalkResult();
const spinner = await loadResult();
const gitUrl =
projectUrl ||
"https://github.com/wczy-ao/project-vue2-template.git#main";
download(
`direct:${gitUrl}`,
name,
{
clone: true,
},
async function (err) {
}
);
});
最后
如果此文对你有帮助欢迎大家
点赞收藏加关注!如有不对之处,望各位大佬不吝赐教。三连加关注,更新不迷路!