手写cli

惠善一的博客:http://huishanyi.club/

通过命令行工具,初始化团队项目,并生成团队规范代码,一键创建项目,一键生成代码,一键生成功能模块···
解放双手,从 cli 开始, JSer 永不为奴!

创建项目目录

两种方法:

  1. 鼠标右键创建文件夹,文件夹名称即为项目名称;

  2. 打开系统命令面板(powershell、cmd···),输入命令:

    • mkdir 项目名称
      

初始化 node 项目

在项目文件目录运行命令:

npm init

运行命令后会出现如下提示(按照提示完成即可):

根据提示完成操作后,会在项目根目录生成项目描述文件package.json,可以回车跳过以下步骤,后续在package.json文件中完善项目信息。

This utility will walk you through creating a package.json file.
It only covers the most common items, and tries to guess sensible defaults.

See `npm help init` for definitive documentation on these fields
and exactly what they do.

Use `npm install <pkg>` afterwards to install a package and
save it as a dependency in the package.json file.

Press ^C at any time to quit.
package name: 项目名称
version: 版本号
description: 项目描述
entry point: (index.js)入口
test command: 测试相关
git repository: git仓库地址
keywords: 关键词
author: 作者
license: 证书
About to write to E:\***\package.json:

{
  "name": "***",
  "version": "***",
  "description": "***",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "***",
  "license": "ISC"
}

Is this OK? (yes) yes

安装相关依赖

项目后续会使用以下依赖包。

运行命令:

npm i commander download-git-repo ora handlebars figlet clear chalk open

依赖包介绍:

  • commander(完整的 node.js 命令行解决方案)
  • download-git-repo(从节点下载并提取仓库(GitHub, GitLab, Bitbucket))
  • ora(终端进度条)
  • handlebars(轻量的语义化模板)
  • figlet(终端实现图形字体)
  • clear(终端清屏)
  • chalk(终端字符串样式)
  • open(跨平台级的,可打开 url、文件、可执行脚本)

创建 cli 入口文件

项目根目录创建文件夹bin,在bin文件夹中创建mycli.js(自定义)文件。

文件内容如下:

#!/usr/bin/env node
// 第一行为约定脚本解释器以 node 进行解析,必须放在文件第一行!行首不能有任何字符!

定义执行命令

打开package.json文件,添加相关代码(句首有+号的代码为新增代码):

{
  "name": "***",
  "version": "***",
  "description": "***",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  + "bin": {
  +   "mycli": "./bin/mycli.js"
  + },
  "author": "***",
  "license": "ISC",
  "dependencies": {
    "chalk": "^4.1.0",
    "clear": "^0.1.0",
    "commander": "^6.2.0",
    "download-git-repo": "^3.0.2",
    "figlet": "^1.5.0",
    "handlebars": "^4.7.6",
    "open": "^7.3.0",
    "ora": "^5.1.0"
  }
}

映射命令至系统

package.json文件中刚添加的bin命令映射至系统中。

运行命令:

npm link

此时我们可以全局使用bin中的mycli命令了。

npm link机理:由于已经安装好 node 环境,故此我们可以直接全局使用nodenpm命令,而npm link则是将package.jsonbin下的命令通过npm系统环境变量桥接,所以我们可以全局使用bin中的命令。

至此我们实现了 cli 的第一步:可以全局使用我们自定义的命令了~~~
莫要问我为什么在这里用了彩色的字。。。
因为我比较————
骚!

定制命令行界面

在入口文件bin => mycli.js中进行命令行界面定制。

#!/usr/bin/env node
const program = require('commander')// 引入 commander

program.version(require('../package.json').version)// 声明 cli 当前版本,如:输入mycli -V 命令,即可显示当前 cli 版本

program
    .command('init <name>')// 定义使用命令,如:命令 mycli init <name>
    .description('init project')// 命令描述
    .action(name => {// 该命令对应的动作
        console.log('init--------'+name);
    })

program.parse(process.argv)// 将命令参数暴露出去,供使用者使用

定制初始化功能

在根目录中创建lib文件夹,在lib文件夹中创建init.js文件,用于编写初始化功能代码。

基础结构搭建

初始化功能基础代码结构。

lib => init.js文件代码如下:

const { promisify } = require('util')// node 核心模块 util,其中的 promisify 方法可以将异步回调方法改造成返回 Promise 实例的方法
const figlet = promisify(require('figlet'))// 以 promise 形式引入 figlet 模块,figlet 可以式代码以图形出现在终端
const clear = require('clear')// 终端清屏
const chalk = require('chalk');// 终端字符串样式定制
const colorLog = (content, color) => console.log(chalk[color||'green'](content));// 封装日志输出,可定制颜色

module.exports = async name => {
    clear();
    const data = await figlet('Gisuni Welcome');
    colorLog(data,'red')
}

连接功能与命令

bin => mycli.jsinit方法进行引入并使用,代码如下:

#!/usr/bin/env node
const program = require('commander')// 引入 commander

program.version(require('../package.json').version)// 声明 cli 当前版本,如:输入mycli -V 命令,即可显示当前 cli 版本

program
    .command('init <name>')// 定义使用命令,如:命令 mycli init <name>
    .description('init project')// 命令描述
-.action(name => {// 该命令对应的动作
-console.log('init--------'+name);
-})
+.action(require('../lib/init'))

program.parse(process.argv)// 将命令参数暴露出去,供使用者使用

下拉仓库功能

将仓库中的代码下拉至本地。

lib文件夹中创建download.js文件,其代码如下:

const { promisify } = require('util')// node 核心模块 util,其中的 promisify 方法可以将异步回调方法改造成返回 Promise 实例的方法

module.exports.clone = async (repo, desc) => {
    const download = promisify(require('download-git-repo'))// 以 promise 形式引入 download-git-repo 模块,download-git-repo 可以下拉仓库代码
    const ora = require('ora')// 引入终端进度条代码
    const process = ora(`下载中··· ${repo}`)// 进度条提示

    process.start()// 进度条开始
    await download(repo, desc)// 下拉仓库代码
    process.succeed()// 进度条结束
}

引入下拉功能

将下拉功能引入至初始化模块中。

lib => init.js文件代码如下:

const { promisify } = require('util')// node 核心模块 util,其中的 promisify 方法可以将异步回调方法改造成返回 Promise 实例的方法
const figlet = promisify(require('figlet'))// 以 promise 形式引入 figlet 模块,figlet 可以式代码以图形出现在终端
const clear = require('clear')// 终端清屏
const chalk = require('chalk')// 终端字符串样式定制
const colorLog = (content, color) => console.log(chalk[color||'green'](content)) // 封装日志输出,可定制颜色
+const { clone } = require('./download')// 引入下拉功能模块

module.exports = async name => {
    clear();
    const data = await figlet('Gisuni Welcome');
    colorLog(data,'red');
+colorLog(`创建项目:${name}`);
+await clone('github:用户名/项目名称', name);// 如果项目克隆地址为:https://github.com/ShanYi-Hui/react-template.git,则此处应改造为:github:ShanYi-Hui/react-template
}

自动安装依赖

下拉的项目将自动安装依赖包。

lib => init.js文件代码如下:

const { promisify } = require('util')// node 核心模块 util,其中的 promisify 方法可以将异步回调方法改造成返回 Promise 实例的方法
const figlet = promisify(require('figlet'))// 以 promise 形式引入 figlet 模块,figlet 可以式代码以图形出现在终端
const clear = require('clear')// 终端清屏
const chalk = require('chalk')// 终端字符串样式定制
const colorLog = (content, color) => console.log(chalk[color||'green'](content)) // 封装日志输出,可定制颜色
const { clone } = require('./download')// 引入下拉功能模块

+const spawn = async (...args) => {
+const { spawn } = require('child_process')// node 核心模块 child_process,spawn 方法会异步地衍生子进程,且不阻塞 Node.js 事件循环
+return new Promise(resolve => {
+const proc = spawn(...args);
+proc.stdout.pipe(process.stdout);
+proc.stderr.pipe(process.stderr);
+proc.on('close', () => {
+resolve();
+})
+})
+}

module.exports = async name => {
    clear();
    const data = await figlet('Gisuni Welcome');
    colorLog(data,'red');
colorLog(`创建项目:${name}`);
await clone('github:用户名/项目名称', name);// 如果项目克隆地址为:https://github.com/ShanYi-Hui/react-template.git,则此处应改造为:github:ShanYi-Hui/react-template
+colorLog('安装依赖中···');
+await spawn(process.platform === 'win32'?'npm.cmd':'npm', ['install'], { cwd:`./${name}` });// 安装依赖
+colorLog(`
+安装完成!
+
+项目将自动启动,若未启动请执行如下命令:
+==============================
+cd ${name}
+npm run serve
+==============================
+`);
+}

项目自启动

依赖安装完成后将自动打开浏览器,并在指定端口(默认 8080 端口)运行服务。

lib => init.js文件代码如下:

const { promisify } = require('util')// node 核心模块 util,其中的 promisify 方法可以将异步回调方法改造成返回 Promise 实例的方法
const figlet = promisify(require('figlet'))// 以 promise 形式引入 figlet 模块,figlet 可以式代码以图形出现在终端
const clear = require('clear')// 终端清屏
const chalk = require('chalk')// 终端字符串样式定制
const colorLog = (content, color) => console.log(chalk[color||'green'](content)) // 封装日志输出,可定制颜色
const { clone } = require('./download')// 引入下拉功能模块
+const open = require('open')// 打开链接

const spawn = async (...args) => {
const { spawn } = require('child_process')// node 核心模块 child_process,spawn 方法会异步地衍生子进程,且不阻塞 Node.js 事件循环
return new Promise(resolve => {
const proc = spawn(...args);
proc.stdout.pipe(process.stdout);// 子进程中的日志通过管道传输至主进程
proc.stderr.pipe(process.stderr);// 子进程中的错误捕获通过管道输出至主进程
proc.on('close', () => {// 关闭子进程
resolve();// promise 状态切换至执行完成状态
})
})
}

module.exports = async name => {
    clear();
    const data = await figlet('Gisuni Welcome');
    colorLog(data,'red');
colorLog(`创建项目:${name}`);
await clone('github:用户名/项目名称', name);// 如果项目克隆地址为:https://github.com/ShanYi-Hui/react-template.git,则此处应改造为:github:ShanYi-Hui/react-template
colorLog('安装依赖中···');
await spawn(process.platform === 'win32'?'npm.cmd':'npm', ['install'], { cwd:`./${name}` });// 安装依赖
colorLog(`
安装完成!

项目将自动启动,若未启动请执行如下命令:
==============================
cd ${name}
npm run serve
==============================
`);
+open('http://localhost:8080');// 打开指定端口
+await spawn(process.platform === 'win32'?'npm.cmd':'npm', ['run','serve'], { cwd:`./${name}` });  // 启动项目
}

其实到这里,一个最基本的 cli 已经制作完成了~~~
那么接下来将开始丰富它的功能!

更新中···

(0)

相关推荐

  • Electron+React 快速搭建一个桌面应用

    一.项目技术栈:Electron+react+react-router+antd1.Electron:electron是一个使用js,html和css等的web技术创建原生桌面应用的框架,他基于chr ...

  • webpack的入门实践,看这篇就够了

    我会将所有的读者概括为初学者,即使你可能有基础,学习本节之前我希望你具有一定的JavaScript和node基础 文中的 ... ...代表省略掉部分代码,和上面的代码相同 文中的文件夹如果没有说创建 ...

  • 用后台开发的逻辑理念学习VUE

    前言 近些年前端开发快速发展,现在学习前端已经不像以前那样仅仅学习一个语法就可以了,它已经是一门编程技术了,它们有自己独立的类似Main函数的入口,有像MVC一样规范好的层次结构,有自己的开发工具可以 ...

  • Vue-Cli脚手架搭建详细步骤

    vue-cli这个构建工具大大降低了webpack的使用难度,支持热更新,有webpack-dev-server的支持,相当于启动了一个请求服务器,给你搭建了一个测试环境,只关注开发就OK. 1.安装 ...

  • 熬了七天,我把小学1-6 年级英语考点提炼成配图手写笔记|生动形象

    是否还有家长对孩子的英语而发愁?尤其是小学孩子的家长们,经常来找老师问孩子的英语学习情况,但大部分也是很不理想,问题就出在很多同学没有找到学习英语的方法,还有一个问题就是,孩子们有的贪玩心重,不愿意花 ...

  • 中考物理手写笔记,电学部分,35

    中考物理手写笔记,电学部分,35

  • 手机wps手写签名功能在哪里

    办公时有很多文件需要签名,如果外出遇到比较紧急的情况,就会很麻烦.那手机wps手写签名功能在哪里,下面一起来看看吧. 1.打开wps office,找到需要签名的文件并打开. 2.点击[编辑]进入编辑 ...

  • 原来华为手机自带会议记录黑科技,开会再也不用手写,涨知识了

    经常要参加各种会议,难免要做会议笔记,最简单的方法就是携带纸笔,手写记录.但是有时候领导还要求整理成电子文件,分享到群里面,打字输入太慢. 那么不妨使用华为手机自带的会议记录神器,开会不用手写,会议内 ...

  • 六年级下册古诗词,看资深教师手写笔记,带学生预习这十首古诗词

    六年级语文下册,在单元学习的最后有一部分内容就是古诗词诵读,在里面出示了十首古诗词供同学们学习. 这十首古诗词,可以合并在一起进行学习,下面杜du老师给大家整理10首古诗词的学习内容. 一.学习< ...

  • 初中化学学霸手写完整版笔记,中考也就考这些了!

    初中化学 2篇原创内容 公众号 今天化学姐给大家整理了初中化学学霸笔记,涵盖化学上下册全部知识点+学习技巧.如需下载打印版,请直接拉到文末查看. 受篇幅限制,本文仅展示部分笔记 (点击图片,查看大图) ...

  • #中考化学##必考# 《酸碱盐》手写#笔...

    #中考化学##必考# <酸碱盐>手写#笔记# #学霸# [给力]领悟:把最重要的事放在你的第一位: [给力]自省:善于排除你的次要事务: [给力]养成:培养要事第一的习惯.

  • 我手写我心,我诗诉我情!古风句子

    南桥泪,一念人海,多少注定,只是无缘,岁月无痕,江湖懂得,一人沉默,万山千河,只是相望咫尺,岁月无常,梦断天涯,古风泪,三尺天,奈何雨,情眼看海,花落无奈,懂得一分,失落一分,从此情断江湖. 无人冷, ...

  • 苏轼半夜被雨惊醒,抬手写下一首词给苏辙,开头7字就是千古名句

    苏轼已死,他的名字只是一个记忆,但是他留给我们的,是他那心灵的喜悦.思想的快乐,这才是万古不朽的. --林语堂 说苏轼大家都不陌生,宋代著名的文学家,名列"唐宋八大家"之一,和辛弃 ...