我们都用过 vue 的cli ,或者 react的cli,  亦或是其他的cli 如 vite 等。他们都是提供了一个全局命令,然后在终端执行这个全局命令就可以创建出模板项目。今天我们就自己做一个,给自己用的脚手架项目,帮助自己开发一些项目。

现在我们来造点需求。

背景:我们要在nextjs 项目中,添加页面路由,用过nextjs 的同学应该知道,这个pages 下面的文件就是页面路由。我们一般添加页面都在pages文件夹下新建文件 ,有时候还会在pages同级文件夹下新建components 文件夹,来放我们页面的组件。

要求:

    1. 可以单独创建一个页面路由
    2. 如果页面复杂,那我需要在pages同级目录下创建一个components文件夹下创建同名的组件文件夹
    3. 可以控制是否生成style 文件

    分析:我们回忆下@vue/cli 这样的脚手架是如何使用的

npm install -g @vue/cli

vue create my-project

全局安装之后 开始使用这个全局命名。

那么我们就按照这个思路倒着打。

因此我们也需要搞一个全局命令,然后执行我们全局命令后,就可以执行我们想要的动作,创建文件夹,写文件等。

  1. 创建空项目,next-project-add-page
  2. npm init
  3. 修改 package.json, 添加 bin 字段
  4. 本地调试,先生成软连接 来让我们可以使用 next-add-page 命名, npm  link 
  5. 核心代码编写
{
"name": "next-project-add-page",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"bin": {
"next-add-page": "index.js"
},
"author": "",
"license": "ISC",
"dependencies": {
"commander": "^10.0.0"
}
}

先上一个能走完大概流程的核心代码,里面一些细节的代码逻辑,我们后面再补充。

// index.js
// console.log(process.argv)
// [
// 'C:\\Program Files\\nodejs\\node.exe',
// 'C:\\Users\\Administrator\\AppData\\Roaming\\npm\\node_modules\\next-project-add-page\\index.js',
// 'add', // 想要执行的命令
// 'list', // 新增的页面
// '-components', // 是否添加到pages同级的components 文件夹下为 组件
// '-style', // 是否添加样式文件
// ]
const { program } = require('commander');
const child_process = require('child_process');
const { clearInterval } = require('timers');
const fs = require('fs');
const path = require('path');
program
.option('-components')
.option('-style'); program.parse();
console.log(program.opts()) // { Components: true, Style: true } ; function intervalProgress() {
const readline = require('readline');
const unloadChar='-';
const loadedChar='='; let i = 0;
let time = setInterval(()=>{
if(i>10){
clearInterval(time);
console.log(`创建完成,请查收`);
process.exit(0);
}
readline.cursorTo(process.stdout,0,1);
readline.clearScreenDown(process.stdout);
renderProgress('文件创建中',i);
i++;
},200); function renderProgress(text,step){
const PERCENT = Math.round(step*10);
const COUNT = 2;
const unloadStr = new Array(COUNT*(10-step)).fill(unloadChar).join('');
const loadedStr = new Array(COUNT*(step)).fill(loadedChar).join('');
process.stdout.write(`${text}:【${loadedStr}${unloadStr}|${PERCENT}%】`);
} } function main(){
if(process.argv[2] && process.argv[2] == 'add') {
if(process.argv[3]) {
// 页面名称
// 判断pages 文件夹是否存在
if(fs.existsSync(`./pages`) && !fs.existsSync(`./pages/${process.argv[3]}`)) {
let subProcess= child_process.exec("cd ./pages && mkdir " + process.argv[3], function(err,stdout){
if(err)console.log(err);
// 读取 tempalte/index.tsx 的文件
let tempalteStr = fs.readFileSync( path.resolve(__dirname, './template/index.tsx'));
fs.writeFile(`./pages/${process.argv[3]}/index.tsx`, tempalteStr, (err) =>{
console.log(err);
});
// 创建style less
if(program.opts().Style && process.argv[3]) {
fs.writeFile(`./pages/${process.argv[3]}/index.less`,'', (err) =>{
console.log(err);
});
}
subProcess.kill();
});
} }
} if(program.opts().Components && process.argv[3]) {
// 添加component组件
// 页面名称
// 判断components 文件夹是否存在
if(!fs.existsSync(`./components`)) {
fs.mkdirSync('./components');
}
if(!fs.existsSync(`./components/${process.argv[3]}`)){
let subProcess=child_process.exec("cd ./components && mkdir " + process.argv[3], function(err,stdout){
if(err)console.log(err);
subProcess.kill();
});
}
} // 展示进度条
intervalProgress();
// 精确点: 实时查询新建的几个文件(文件夹)是否创建成功,如果创建完毕,应该提前结束进度条 } main();

代码大概解说:

  • 使用nodejs 的fs 模块 api,对文件的读写以及文件存在的判断。
  • commander 对命令行的解析,得到命令行具体的值后 继续做文件的读写
  • 使用 child_process 子进程 对文件的读写
  • process.stdout.write 来写终端的输出内容
  • 用template 文件夹 放 具体的模板文件,具体生成文件的时候,可以将模板的内容给到相应的文件中
 
 
上述代码不是很复杂,一些比较细节的内容我没有做细节处理,比如进度条的展示,这里是用一个 定时器 来模拟进度的一个过程,如果需要精确点可以监听文件的生成进度,从而调优进度条的展示。
 

现在可以nextjs 项目下 执行命令:next-add-page add list -components -style

next-add-page:是全局的一个命令

add: 是添加页面的 一个命令

list: 是添加页面的名称

-components: 在 components 文件夹下创建同名的组件 (可选)

-style:  创建样式文件 (可选)

是否可选,取决与代码对这个命令行参数的解析以及操作。

现在执行这个命令行

就可以看到项目中 生成了对应的文件。

到这里基本的需求已经完成了。

接下里是发布,

具体发布到npm 的步骤就不多说了,没有发布过的可以参考下这里,https://cnodejs.org/topic/5823c4411120be9438b02a31

好了,到这里应该知道怎木去开发自己的一个cli 脚手架了。

参考

https://juejin.cn/post/6844903702453551111

https://juejin.cn/post/6857842033084760071

https://www.runoob.com/nodejs/nodejs-fs.html

搞一个自己用的node-cli的更多相关文章

  1. Node & CLI

    Node & CLI cli 生成文件的原理是什么 https://nodejs.org/api/cli.html http://nodejs.cn/api/cli.html CLI & ...

  2. 新建一个express工程,node app无反应

    1.问题描述 新建一个express工程,node app以后无反应,浏览器输入localhost:3000,显示如下 2.解决方法 在app.js文件中加入如下代码 app.listen(3000, ...

  3. 搭建一个最简单的node服务器

    搭建一个最简单的node服务器 1.创建一个Http服务并监听8888端口 2.使用url模块 获取请求的路由和请求参数 var http = require('http'); var url = r ...

  4. 打算写一个《重学Node.js》系列,希望大家多多支持

    先放上链接吧,项目已经开始2周了:https://github.com/hellozhangran/happy-egg-server 想法 现在是2019年11月24日,还有人要开始学习Node.js ...

  5. 分布式ID生成服务,真的有必要搞一个

    目录 阐述背景 Leaf snowflake 模式介绍 Leaf segment 模式介绍 Leaf 改造支持RPC 阐述背景 不吹嘘,不夸张,项目中用到ID生成的场景确实挺多.比如业务要做幂等的时候 ...

  6. how to read the system information by using the node cli tool?

    how to read the system information by using the node cli tool? node cli & get system info demos ...

  7. node cli & emoji

    node cli & emoji cli $ yarn add node-emoji $ npm i node-emoji https://github.com/omnidan/node-em ...

  8. write a node cli tools, step by step

    write a node cli tools, step by step how to write a node cli tools node cli tools, step by step, nod ...

  9. linux & node & cli & exit(0) & exit(1)

    linux & node & cli & exit(0) & exit(1) exit(0) & exit(1) demo exit(0) === OK exi ...

  10. how to write a node cli tool

    how to write a node cli tool https://www.google.com/search?q=how+to+node+cli+tool&oq=how+to+node ...

随机推荐

  1. 安卓10.0蓝牙HIDL的直通式初始化流程

    本文仅介绍扼要的流程,没有系统化介绍. 首先从system\bt\hci\src\hci_layer_android.cc文件的函数void hci_initialize() 开始初始化: void ...

  2. @Scheduled不执行

    今天发现@Scheduled不执行,注释掉netty的初始化事件就能正常执行了 原因是@PostConstruct是在主线程执行,@PostConstruct不能堵塞,堵塞会导致整个应用挂起不可用

  3. C#中的范围类型(Range Type)

    //语法糖Rangestatic void Main(string[] args) { var myArray = new string[] { "Item1", "It ...

  4. 修改mysql root密码,在workbench中导入.sql文件

    修改mysql root密码: 1.如果没有配置环境变量,在 \Program Files\MySQL\MySQL Server 8.0\bin 文件下 Shit+右键打开 Powershell 窗口 ...

  5. uniapp中使用AntV F6 + table表格插件使用

    首先看页面效果: AntV官网下载F6文件到项目中与uViewUI插件 <template> <view class="page"> <!-- 导航栏 ...

  6. Nacos 之 Distro 协议

    1. 概述 Distro协议是阿里自研的一个最终一致性协议,继承了 Gossip 以及 Eureka 通信(PeerEurekaNodes)的优点并做进一步优化而出来的: 对于原生的Gossip,由于 ...

  7. LOJ数列分块入门九题(中)

    #6281. 数列分块入门 5 - 题目 - LibreOJ (loj.ac) 区间开方,区间求和题. 显然,针对区间维护开方操作很难做到,于是考虑其值的性质,显然,int范围内的值最多开方6次就会变 ...

  8. .netCore Nuget包引用记录

    1.画图  System.Drawing.Common 2.

  9. vue 高级部分

    props的其它内容 props的作用就是用于在子组件中接收传入的数据 props的使用方式 1.数组 props:['name'] 2.对象,指定传入变量的类型 props:{name:Number ...

  10. 自学JavaDay02_class01

    注释 单行注释: //单行注释 多行注释 /** 多行注释* 多行注释* */ 文档注释 /** * 文档注释 * 文档注释 */ 标识符 关键字 标识符 所有的标识符都应该以字母(A-Z 或者 a- ...