利用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 相关文章推荐
JS 各种网页尺寸判断实例方法
Apr 18 Javascript
一些老手都不一定知道的JavaScript技巧
May 06 Javascript
对之前写的jquery分页做下升级
Jun 19 Javascript
第二篇Bootstrap起步
Jun 21 Javascript
angular源码学习第一篇 setupModuleLoader方法
Oct 20 Javascript
ES6新特性八:async函数用法实例详解
Apr 21 Javascript
javascript定时器取消定时器及优化方法
Jul 08 Javascript
老生常谈JavaScript获取CSS样式的方法(兼容各浏览器)
Sep 19 Javascript
4个顶级JavaScript高级文本编辑器
Oct 10 Javascript
Vue keepAlive 数据缓存工具实现返回上一个页面浏览的位置
May 10 Javascript
Vue3不支持Filters过滤器的问题
Sep 24 Javascript
基于javascript实现移动端轮播图效果
Dec 21 Javascript
微信小程序实现可拖动悬浮图标(包括按钮角标的实现)
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
快速开发一个PHP扩展图文教程
2008/12/12 PHP
模板引擎正则表达式调试小技巧
2011/07/20 PHP
php使用CURL伪造IP和来源实例详解
2015/01/15 PHP
php中使用GD库做验证码
2016/03/31 PHP
PHP的全局错误处理详解
2016/04/25 PHP
从父页面读取和操作iframe中内容方法
2009/07/25 Javascript
兼容IE和FF的js脚本代码小结(比较常用)
2010/12/06 Javascript
jQuery拖拽div实现思路
2014/02/19 Javascript
Nodejs使用mysql模块之获得更新和删除影响的行数的方法
2014/03/18 NodeJs
a标签的href与onclick事件的区别详解
2014/11/12 Javascript
详解JavaScript中getFullYear()方法的使用
2015/06/10 Javascript
jQuery控制div实现随滚动条滚动效果
2016/06/07 Javascript
jquery实现ajax加载超时提示的方法
2016/07/23 Javascript
Vue的轮播图组件实现方法
2018/03/03 Javascript
解决iview打包时UglifyJs报错的问题
2018/03/07 Javascript
微信小程序实现文字无限轮播效果
2018/12/28 Javascript
详解JS浏览器事件循环机制
2019/03/27 Javascript
js中Generator函数的深入讲解
2019/04/07 Javascript
详解Node.js异步处理的各种写法
2019/06/09 Javascript
javascript实现贪吃蛇小练习
2020/07/05 Javascript
Python3 模块、包调用&amp;路径详解
2017/10/25 Python
Python实现聊天机器人的示例代码
2018/07/09 Python
python try 异常处理(史上最全)
2019/03/07 Python
Python字符串内置函数功能与用法总结
2019/04/16 Python
Python发展史及网络爬虫
2019/06/19 Python
jupyter notebook 多环境conda kernel配置方式
2020/04/10 Python
python代码实现将列表中重复元素之间的内容全部滤除
2020/05/22 Python
keras 自定义loss model.add_loss的使用详解
2020/06/22 Python
欧洲领先的电子和电信零售商和服务提供商:Currys PC World Business
2017/12/05 全球购物
美国受信赖的教育产品供应商:Nest Learning
2018/06/14 全球购物
加多宝凉茶广告词
2014/03/18 职场文书
保护环境建议书100字
2014/05/13 职场文书
ktv好的活动方案
2014/08/17 职场文书
实习介绍信模板
2015/01/30 职场文书
杭州西湖英语导游词
2015/02/03 职场文书
大学优秀学生主要事迹材料
2015/11/04 职场文书