利用node.js开发cli的完整步骤


Posted in Javascript onDecember 29, 2020

CLI介绍

命令行界面(英语:command-line interface,缩写:CLI),是在图形用户界面得到普及之前使用最为广泛的用户界面,它通常不支持鼠标,用户通过键盘输入指令,计算机接收到指令后,予以执行。

目前前端开发中,CLI是常用的工具。前端三大框架Vue、React、Angular都有对应的CLI,包括现在最流行的前端工程化的打包工具Webpack,也有对应的webpack-cli。

在现代的前端开发中,CLI提高了开发的效率。让相应的前端开发者免去了大量的重复性操作,节省了大量的时间。CLI可以完成的功能,包括但不限于初始化生成对应的项目模板、执行特定的脚本文件、在项目中创建新的模块 。下面来介绍一下前端工程师如何使用node.js来完成一个CLI。

创建项目

打开终端,创建moka-cli的目录

mkdir moka-cli

进入目录,初始化项目

cd moka-cli
npm init -y

根目录下新建bin目录,并新建index.js文件,此时目录结构如下

|-- moka-cli
 |-- package.json
 |-- bin
  |-- index.js

开发cli需要借助commander、inquirer、chalk三个库

npm install commander inquirer chalk --save

修改package.json文件,使用es moudle,并新增moka命令

package.json

{
 "name": "moka-cli",
 "version": "1.0.0",
 "description": "",
 "main": "index.js",
+ "type": "module",
+ "bin": {
+ "moka": "./bin/index.js"
+ },
 "scripts": {
 "test": "echo \"Error: no test specified\" && exit 1"
 },
 "keywords": [],
 "author": "",
 "license": "ISC",
 "dependencies": {
 "chalk": "^4.1.0",
 "commander": "^6.2.1",
 "inquirer": "^7.3.3"
 }
}

接下来需要在/bin/index.js中编程,在第一行添加如下代码,这行代码是告诉系统要用node来执行这个文件。

#!/usr/bin/env node

再添加下面代码

#!/usr/bin/env node

console.log('moka-cli run!')

然后在项目根目录下全局安装项目

npm install -g

到这里,一个简单的CLI就完成了,可以打开终端,在任意目录下输入moka,控制台就会打印出对应的消息。

$ moka
moka-cli run!

第三方库介绍

moka-cli希望实现可以创建前端开发模板的功能,可以使用moka命令来创建一个vue或react项目。

先来介绍一下使用到到第三方的库

commander

文档地址

用来实现命令的库,在moka-cli中会用来实现create命令,实现用下面的命令行来创建一个vue-demo的项目

moka create vue vue-demo

inquirer

文档地址

实现用户和终端交互的库,在moka-cli中使用create指令的时候会询问是否覆盖已有项目

$ moka create vue moka-demo
? Template named moka-demo is already existed, are you sure to overwrite? (Y/n)

chalk

文档地址

可以在终端界面显示出多种颜色的文本和背景

核心功能

在根目录创建actions和templates目录,目录结构如下

|-- moka-cli
 |-- package-lock.json
 |-- package.json
+ |-- actions
+ | |-- create.js
 |-- bin
 | |-- index.js
+ |-- templates
+  |-- react
+  | |-- app.js
+  | |-- index.js
+  | |-- src
+  |  |-- index.js
+  |-- vue
+   |-- app.js
+   |-- index.js
+   |-- src
+    |-- index.js

templats下面用来存放通过CLI要生成的项目的模板,在这里先随便创建几个文件展示一下。

修改/bin/index.js文件

#!/usr/bin/env node
import create from '../actions/create.js';
import commander from 'commander';

const { program } = commander;

program.version('0.0.1');

program
 .command('create <template> [name]')
 .description('新建项目')
 .action(create);

program.parse(process.argv);

修改./actions/create.js文件

import fs from 'fs';
import path from 'path';
import chalk from 'chalk';
import inquirer from 'inquirer';

import { fileURLToPath } from 'url';

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const TEMPLATES = ['vue', 'react'];

const targetPath = process.cwd();

function createTemplate(template, name) {
 const templatePath = path.join(__dirname, '../', `templates/${template}`);

 function readAndCopyFile(parentPath, tempPath) {
 let files = fs.readdirSync(parentPath);

 files.forEach((file) => {
  let curPath = `${parentPath}/${file}`;
  let stat = fs.statSync(curPath);
  let filePath = `${targetPath}/${tempPath}/${file}`;
  if (stat.isDirectory()) {
  fs.mkdirSync(filePath);
  readAndCopyFile(`${parentPath}/${file}`, `${tempPath}/${file}`);
  } else {
  const contents = fs.readFileSync(curPath, 'utf8');
  fs.writeFileSync(filePath, contents, 'utf8');
  }
 });
 }

 readAndCopyFile(templatePath, name);
}

function deleteTemplate(path) {
 if (fs.existsSync(path)) {
 fs.readdirSync(path).forEach(function (file, index) {
  var curPath = path + '/' + file;
  if (fs.lstatSync(curPath).isDirectory()) {
  // recurse
  deleteTemplate(curPath);
  } else {
  // delete file
  fs.unlinkSync(curPath);
  }
 });
 fs.rmdirSync(path);
 }
}

export default function create(template, name) {
 if (!TEMPLATES.includes(template)) {
 console.log(chalk.red(`No ${template} Template`));
 return;
 }

 const projectPath = path.resolve(targetPath, name);

 if (fs.existsSync(projectPath)) {
 inquirer
  .prompt([
  {
   name: 'template-overwrite',
   type: 'confirm',
   message: `Template named ${name} is already existed, are you sure to overwrite?`,
   validate: function (input) {
   if (input.lowerCase !== 'y' && input.lowerCase !== 'n') {
    return 'Please input y/n !';
   } else {
    return true;
   }
   },
  },
  ])
  .then((answers) => {
  // 如果确定覆盖
  if (answers['template-overwrite']) {
   // 删除文件夹
   deleteTemplate(projectPath);
   console.log(chalk.yellow(`Template already existed , removing!`));
   //创建新模块文件夹
   fs.mkdirSync(projectPath);
   createTemplate(template, name);
   console.log(
   chalk.green(`${template} template has been created successfully!`)
   );
  }
  })
  .catch((err) => {
  console.log(chalk.red(err));
  });
 } else {
 fs.mkdirSync(projectPath);
 createTemplate(template, name);
 console.log(
  chalk.green(`${template} template has been created successfully!`)
 );
 }
}

最后在项目根目录运行以下命令,重新安装一下moka-cli

npm install -g

随便找一个路径,运行moka create <template> [name]命令来生成项目

moka create react react-demo

效果如下,会在该目录下生成一个react-demo的文件夹,里面存放的就是moka-cli项目中/templates/react下的所有

$ moka create react react-demo
react template has been created successfully!

如果在该目录下继续创建一个同名的项目,就会提示是否覆盖,输入y后继续执行

$ moka create react react-demo
? Template named react-demo is already existed, are you sure to overwrite? Yes
Template already existed , removing!
react template has been created successfully!

create命令的核心逻辑是通过node的fs模块来复制/templates下的文件,然后将其放到指定的路径下,具体实现直接看代码就可以来。

总结

CLI是提示前端开发效率的重要工具,如果有长期维护的项目,开发一个CLI来完成一些重复性的工作,是一个不错的选择。

moka-cli还没有上传github,等过段时间完善一些/templates下的项目模板,我会在文章里补充项目地址。

到此这篇关于利用node.js开发cli的文章就介绍到这了,更多相关node.js开发cli内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Javascript 相关文章推荐
用户注册常用javascript代码
Aug 29 Javascript
jQuery 连续列表实现代码
Dec 21 Javascript
修改jQuery Validation里默认的验证方法
Feb 14 Javascript
基于jQuery的获取标签名的代码
Jul 16 Javascript
jquery实现表格奇数偶数行不同样式(有图为证及实现代码)
Jan 23 Javascript
jquery缓动swing liner控制动画过程不同时刻的速度
May 29 Javascript
JavaScript实现从数组中选出和等于固定值的n个数
Sep 03 Javascript
jQuery往返城市和日期查询实例讲解
Oct 09 Javascript
jQuery实现右侧显示可向左滑动展示的深色QQ客服效果代码
Oct 23 Javascript
微信小程序自定义多选事件的实现代码
May 17 Javascript
对layui中表单元素的使用详解
Aug 15 Javascript
如何基于jQuery实现五角星评分
Sep 02 jQuery
微信小程序实现可拖动悬浮图标(包括按钮角标的实现)
Dec 29 #Javascript
vue中配置scss全局变量的步骤
Dec 28 #Vue.js
为什么推荐使用JSX开发Vue3
Dec 28 #Vue.js
Vue仿百度搜索功能
Dec 28 #Vue.js
node.js通过Sequelize 连接MySQL的方法
Dec 28 #Javascript
解决elementui表格操作列自适应列宽
Dec 28 #Javascript
微信小程序视频弹幕发送功能的实现
Dec 28 #Javascript
You might like
在PHP3中实现SESSION的功能(二)
2006/10/09 PHP
提高PHP编程效率的方法
2013/11/07 PHP
ThinkPHP通过AJAX返回JSON的两种实现方法
2014/12/18 PHP
thinkPHP实现多字段模糊匹配查询的方法
2016/12/01 PHP
PHP脚本自动识别验证码查询汽车违章
2016/12/20 PHP
php使用imagecopymerge()函数创建半透明水印
2018/01/25 PHP
PHP商品秒杀问题解决方案实例详解【mysql与redis】
2019/07/22 PHP
jquery键盘事件介绍
2011/01/31 Javascript
说说JSON和JSONP 也许你会豁然开朗
2012/09/02 Javascript
浅谈关于JavaScript的语言特性分析
2013/04/11 Javascript
JavaScript数组函数unshift、shift、pop、push使用实例
2014/08/27 Javascript
AngularJs 弹出模态框(model)
2016/04/07 Javascript
canvas绘制表盘时钟
2017/01/23 Javascript
angular中的cookie读写方法
2017/08/02 Javascript
jQuery实现腾讯信用界面(自制刻度尺)样式
2017/08/15 jQuery
微信小程序搜索功能(附:小程序前端+PHP后端)
2019/02/28 Javascript
富文本编辑器vue2-editor实现全屏功能
2019/05/26 Javascript
JS实现骰子3D旋转效果
2019/10/24 Javascript
Node.js API详解之 console模块用法详解
2020/05/12 Javascript
[35:26]DOTA2上海特级锦标赛B组小组赛#2 VG VS Fnatic第三局
2016/02/26 DOTA
[00:59]DOTA2背景故事第二期之四大基本法则
2020/07/07 DOTA
[01:01:01]完美世界DOTA2联赛循环赛 GXR vs FTD BO2第一场 10.29
2020/10/29 DOTA
python中利用Future对象异步返回结果示例代码
2017/09/07 Python
Python中logging.NullHandler 的使用教程
2018/11/29 Python
python的继承知识点总结
2018/12/10 Python
python datetime时间格式的相互转换问题
2020/06/11 Python
python pygame 愤怒的小鸟游戏示例代码
2021/02/25 Python
基于HTML5实现类似微信手机摇一摇功能(计算摇动次数)
2017/07/24 HTML / CSS
Myprotein蛋白粉美国官网:欧洲畅销运动营养品牌
2016/11/15 全球购物
美国新娘礼品店:The Paisley Box
2020/09/08 全球购物
个人总结与自我评价
2014/09/18 职场文书
二十年同学聚会致辞
2015/07/28 职场文书
python 经纬度求两点距离、三点面积操作
2021/06/03 Python
Python使用DFA算法过滤内容敏感词
2022/04/22 Python
Win10系统搭建ftp文件服务器详细教程
2022/08/05 Servers
win11电脑关机鼠标灯还亮怎么解决? win11关机后鼠标灯还亮解决方法
2023/01/09 数码科技