利用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.The.Good.Parts阅读笔记(一)假值与===运算符
Nov 16 Javascript
汉化英文版的Dreamweaver CS5并自动提示jquery
Nov 25 Javascript
javascript getElementsByTagName
Jan 31 Javascript
JS基于FileSystemObject创建一个指定路径的TXT文本文件
Aug 05 Javascript
js滑动提示效果代码分享
Mar 10 Javascript
JavaScript 消息框效果【实现代码】
Apr 27 Javascript
vue.js入门教程之计算属性
Sep 01 Javascript
用jQuery的AJax实现异步访问、异步加载
Nov 02 Javascript
详解vue-router 2.0 常用基础知识点之导航钩子
May 10 Javascript
Vue2.0 axios前后端登陆拦截器(实例讲解)
Oct 27 Javascript
简单了解JavaScript中的执行上下文和堆栈
Jun 24 Javascript
详解Vue的异步更新实现原理
Dec 22 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
使用PHP socke 向指定页面提交数据
2008/07/23 PHP
PHP实现的汉字拼音转换和公历农历转换类及使用示例
2014/07/01 PHP
PHP中strtr字符串替换用法详解
2014/11/26 PHP
php实现mysql数据库连接操作及用户管理
2015/11/08 PHP
PHP使用星号隐藏用户名,手机和邮箱的实现方法
2016/09/22 PHP
利用php + Laravel如何实现部署自动化详解
2017/10/11 PHP
JS动态添加与删除select中的Option对象(示例代码)
2013/12/20 Javascript
addEventListener 的用法示例介绍
2014/05/07 Javascript
分享一款基于jQuery的视频播放插件
2014/10/09 Javascript
原生js制作简单的数字键盘
2015/04/24 Javascript
Javascript中字符串和数字的操作方法整理
2017/01/22 Javascript
Angular 4依赖注入学习教程之ValueProvider的使用(七)
2017/06/04 Javascript
Angular2整合其他插件的方法
2018/01/20 Javascript
关于jquery layui弹出层的使用方法
2018/04/21 jQuery
Node.js爬虫如何获取天气和每日问候详解
2019/08/26 Javascript
Python中除法使用的注意事项
2014/08/21 Python
Python压缩和解压缩zip文件
2015/02/14 Python
PyTorch CNN实战之MNIST手写数字识别示例
2018/05/29 Python
python复制列表时[:]和[::]之间有什么区别
2018/10/16 Python
python脚本当作Linux中的服务启动实现方法
2019/06/28 Python
Django MEDIA的配置及用法详解
2019/07/25 Python
Python解决pip install时出现的Could not fetch URL问题
2019/08/01 Python
Python3和PyCharm安装与环境配置【图文教程】
2020/02/14 Python
django实现HttpResponse返回json数据为中文
2020/03/27 Python
使用Python爬取Json数据的示例代码
2020/12/07 Python
html5使用canvas压缩图片的示例代码
2018/09/11 HTML / CSS
中专自我鉴定范文
2013/10/16 职场文书
庆八一活动方案
2014/01/25 职场文书
运动会入场解说词
2014/02/07 职场文书
医学生就业推荐表自我鉴定
2014/03/26 职场文书
任命书模板
2014/06/04 职场文书
2014年度培训工作总结
2014/11/27 职场文书
行政助理岗位职责
2015/02/10 职场文书
2015年综治维稳工作总结
2015/04/07 职场文书
情侣之间的道歉短信
2015/05/12 职场文书
初中信息技术教学反思
2016/02/16 职场文书