利用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 相关文章推荐
jQuery 改变CSS样式基础代码
Feb 11 Javascript
利用google提供的API(JavaScript接口)获取网站访问者IP地理位置的代码详解
Jul 24 Javascript
jQuery实现购物车表单自动结算效果实例
Aug 10 Javascript
在AngularJS中如何使用谷歌地图把当前位置显示出来
Jan 25 Javascript
微信小程序 富文本转文本实例详解
Oct 24 Javascript
在node.js中怎么屏蔽掉favicon.ico的请求
Mar 01 Javascript
Vue实现动态创建和删除数据的方法
Mar 17 Javascript
vue同步父子组件和异步父子组件的生命周期顺序问题
Oct 07 Javascript
node.js实现为PDF添加水印的示例代码
Dec 05 Javascript
模块化react-router配置方法详解
Jun 03 Javascript
详解javascript中var与ES6规范中let、const区别与用法
Jan 11 Javascript
vue基于Echarts的拖拽数据可视化功能实现
Dec 04 Vue.js
微信小程序实现可拖动悬浮图标(包括按钮角标的实现)
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
星际初学者游戏中永远要做的事
2020/03/04 星际争霸
php URL编码解码函数代码
2009/03/10 PHP
ThinkPHP的SAE开发相关注意事项详解
2016/10/09 PHP
PHP实现对文件锁进行加锁、解锁操作的方法
2017/07/04 PHP
PHP用PDO如何封装简单易用的DB类详解
2017/07/30 PHP
读jQuery之七 判断点击了鼠标哪个键的代码
2011/06/21 Javascript
JavaScript中的关键字&quot;VAR&quot;使用详解 分享
2013/07/31 Javascript
Javascript脚本实现静态网页加密实例代码
2013/11/05 Javascript
Jquery ajax执行顺序 返回自定义错误信息(实例讲解)
2013/11/06 Javascript
JavaScript中反正弦函数Math.asin()的使用简介
2015/06/14 Javascript
js时钟翻牌效果实现代码分享
2020/07/31 Javascript
javascript中的altKey 和 Event属性大全
2015/11/06 Javascript
跟我学习javascript的call(),apply(),bind()与回调
2015/11/16 Javascript
Bootstrap每天必学之进度条
2015/11/30 Javascript
学习JavaScript设计模式(代理模式)
2015/12/03 Javascript
jQuery easyUI datagrid 增加求和统计行的实现代码
2016/06/01 Javascript
JS Array创建及concat()split()slice()的使用方法
2016/06/03 Javascript
Vue render渲染时间戳转时间,时间转时间戳及渲染进度条效果
2018/07/27 Javascript
JS加密插件CryptoJS实现AES加密操作示例
2018/08/16 Javascript
抖音上用记事本编写爱心小程序教程
2019/04/17 Javascript
vue改变对象或数组时的刷新机制的方法总结
2019/04/24 Javascript
通过GASP让vue实现动态效果实例代码详解
2019/11/24 Javascript
JS实现导航栏楼层特效
2020/01/01 Javascript
[01:41]DOTA2 2015国际邀请赛中国区预选赛第三日战报
2015/05/28 DOTA
windows及linux环境下永久修改pip镜像源的方法
2016/11/28 Python
详解Django中类视图使用装饰器的方式
2018/08/12 Python
django url到views参数传递的实例
2019/07/19 Python
pytorch 常用线性函数详解
2020/01/15 Python
Python内存泄漏和内存溢出的解决方案
2020/09/26 Python
HTML5印章绘制电子签章图片(中文英文椭圆章、中文英文椭圆印章)
2019/06/03 HTML / CSS
阿联酋航空假期:Emirates Holidays
2018/03/20 全球购物
2014年自我评价
2014/01/04 职场文书
信息管理专业自荐书
2014/06/05 职场文书
2014年党委工作总结
2014/11/22 职场文书
入党宣誓大会后的感想
2015/08/10 职场文书
2016教师年度考核评语大全
2015/12/01 职场文书