命令行界面(英语:command-line interface,缩写:CLI)是在图形用户界面得到普及之前使用最为广泛的用户界面,它通常不支持鼠标,用户通过键盘输入指令,计算机接收到指令后,予以执行。也有人称之为字符用户界面(character user interface, CUI)。
我们在项目开发时,经常会用到一些cli工具,比如vue-cli、npm init等等,这些CLI工具我们常常用做项目初始化、代码检查、模板创建等交互相对简单,且重复性较多的工作。
实现CLI工具开发的方式和语言有很多,本文只介绍基于node的实现方案。
依照惯例,我们第一步还是从Hello World开始:
首先,进入工作区,创建并进入项目目录hello-cli,执行npm初始化命令:
mkdir hello-clicd hello-clinpm init
输入或选择一系列项目配置
package name: (hello-cli) version: (1.0.0) description: hello worldentry point: (index.js) test command: testgit repository: keywords: cliauthor: mulianjulicense: (ISC)
npm会自动创建好项目配置文件package.json
{ "name": "hello-cli", "version": "1.0.0", "description": "hello world", "main": "index.js", "scripts": { "test": "test" }, "keywords": [ "cli" ], "author": "mulianju", "license": "ISC"}
在项目根目录,创建bin文件夹,并在bin文件夹内创建hello-cli.js文件,文件中写入:
#!/usr/bin/env nodeconsole.log('Hello World!')
注意文件第一行的“注释”,这行“注释”并不是普通的“注释”,他是用来声明此CLI工具的开发语言,所以千万不要删掉。
在package.json里添加bin字段,用来创建一个命令,并声明命令指向的执行文件即可:
"bin": { "hello-cli": "bin/hello-cli.js"},
执行本地安装:
npm link
至此,我们的第一个CLI工具就开发完成了。我们新建个终端窗口,执行我们自定义的命令,即可看到效果:
hello-cli
输出结果:
Hello World!
CLI工具最关键的一个点,就是用户交互,简单的交互可以极大扩展我们的CLI的能力,比如以上我们用到的npm init,一些项目信息都需要在我们初始化项目过程中,通过CLI输入或选择。
用来实现CLI交互的,主要依赖以下两个包:
注意:inquirer在9.0.0版本开始,模块化方式改为native esm modules,言下之意,如果你的项目中使用的是CMD模块化方式,则需要限制inquirer的版本低于9.0.0,否则将会抛出以下错误:
internal/modules/cjs/loader.js:1102 throw new ERR_REQUIRE_ESM(filename, parentPath, packageJsonPath); ^Error [ERR_REQUIRE_ESM]: Must use import to load ES Module: /mnt/d/work/work/2022/08/hello-cli/node_modules/inquirer/lib/inquirer.jsrequire() of ES modules is not supported.require() of /mnt/d/work/work/2022/08/hello-cli/node_modules/inquirer/lib/inquirer.js from /mnt/d/work/work/2022/08/hello-cli/bin/hello-cli.js is an ES module file as it is a .js file whose nearest parent package.json contains "type": "module" which defines all .js files in that package scope as ES modules.Instead rename inquirer.js to end in .cjs, change the requiring code to use import(), or remove "type": "module" from /mnt/d/work/work/2022/08/hello-cli/node_modules/inquirer/package.json. at new NodeError (internal/errors.js:322:7) at Object.Module._extensions..js (internal/modules/cjs/loader.js:1102:13) at Module.load (internal/modules/cjs/loader.js:950:32) at Function.Module._load (internal/modules/cjs/loader.js:790:12) at Module.require (internal/modules/cjs/loader.js:974:19) at require (internal/modules/cjs/helpers.js:101:18) at Object. (/mnt/d/work/work/2022/08/hello-cli/bin/hello-cli.js:3:18) at Module._compile (internal/modules/cjs/loader.js:1085:14) at Object.Module._extensions..js (internal/modules/cjs/loader.js:1114:10) at Module.load (internal/modules/cjs/loader.js:950:32) { code: 'ERR_REQUIRE_ESM'}
关于以上两个包的更多介绍,基于篇幅原因,我们这里就不详细展开来说了,我们这里只讲一个最简单的应用,其他的功能,待您亲自去尝试~
以下应用完成了,通过用户输入的项目信息和一些默认信息,来初始化一个package.json项目配置文件的功能:
#!/usr/bin/env nodeconst { program } = require("commander")const inquire = require("inquirer")const fs = require("fs")const projectInfo = [ { type: "input", message: "请输入项目名称", name: "name", default: "project", }, { type: "input", message: "请输入项目描述", name: "description", }, { type: "input", message: "请输入项目作者", name: "author", }, { type: "input", message: "请输入项目git仓库", name: "git", }, { type: "list", message: "请选择开源协议", name: "license", choices: ["ISC", "BSD", "GPL", "Apache Licence 2.0", "LGPL", "MIT"], default: "GPL", },]const defaultInfo = { version: "1.0.0", scripts: {},}const initAction = () => { inquire.prompt(projectInfo).then((answers) => { const info = Object.assign({}, defaultInfo, answers) fs.writeFile("package.json", JSON.stringify(info), function (err) { if (err) { res.status(500).send("写入错误") } else { console.log("项目初始化成功,您的项目信息为:
", info) } }) })}switch (process.argv[2]) { case "init": program .command("init") .description("初始化项目") .action(initAction) .parse(process.argv) break default: program .usage("") .command("init", "初始化项目") .parse(process.argv) break}
以上例子,我们实现了自动创建package.json文件,CLI当然不只这点能耐,利用一些npm包,我们还可以实现更丰富的功能。
首先,安装依赖工具:
npm install shelljs --save
编写功能:
const inquire = require("inquirer")const shell = require("shelljs")const projectInfo = [ { type: "input", message: "请输入项目名称", name: "name", default: "project", }]const gitRepository = 'https://github.com/mulianju/hello-cli.git'const initWithGit = () => { inquire.prompt(projectInfo).then((answers) => { console.log('项目正在创建...') const { name = 'project' } = answers shell.exec(` rm -rf ./hello-cli git clone ${gitRepository} rm -rf ./hello-cli/.git mv hello-cli ${name} cd ${name}; `) })}module.exports = { initWithGit}
运行:
hello-cli initWithGit## CLI交互及输出? 请输入项目名称 project项目正在创建...Cloning into 'hello-cli'...
首先,安装依赖工具:
npm install art-template chalk --save
注意:chalk在5.0.0版本开始,模块化方式也变更了,和inquirer相似,参考
编写功能:
const inquirer = require("inquirer")const fs = require("fs")const template = require("art-template")const chalk = require("chalk")const path = require('path')const { capitalize, camelize, mkdirsSync} = require('./utils')const rootDir = '../../../..'const choices = [ { title: "页面(page)", value: "page", }, { title: "组件(component)", value: "component" },]const promptInfo = [ { type: "list", name: "type", message: "请选择需要创建的类型?", prefix: "[?]", choices: choices.map((item) => item.title), filter(val) { return choices.find((item) => item.title == val).value }, }, { type: "input", name: "name", message: `请输入名称(支持多级路径, 如:xxx/xxx)?`, prefix: "[?]", default: "index", },]const checkTemplatesExistsSync = async () => { console.log(__dirname) const results = [ fs.existsSync(path.resolve(__dirname, rootDir, './templates/component.vue.art')), fs.existsSync(path.resolve(__dirname, rootDir, './templates/page.vue.art')), ] .filter((isExist) => !isExist) .map((_, index) => choices[index].title) if (results.length) { console.log( `${chalk.green(results.join(","))}${chalk.red( "模板不存在,请先创建模板" )}` ) } else { return true }}const add = async () => { if (await checkTemplatesExistsSync()) { inquirer .prompt(promptInfo) .then(async (answers) => { const { type, name: inputName } = answers const nameMap = inputName.split('/') const name = capitalize(camelize(nameMap.pop())) const dirname = path.resolve(__dirname, rootDir, `./${type}s/${nameMap.join('/')}`) const templateDir = path.resolve(__dirname, rootDir, `./templates/${type}.vue.art`) if (!fs.existsSync(path.resolve(dirname, `./${name}.vue`))) { mkdirsSync(dirname) fs.writeFileSync(path.resolve(dirname, `./${name}.vue`), template(templateDir, { name }), 'utf8') } else { const role = choices.find(item => item.value == type) console.log(`${chalk.red(role.title)}: ${chalk.green(name)} ${chalk.red('已经存在,换个名字再试试吧')}`) } console.log(answers) }) }}module.exports = { add}
运行:
## 注意:若使用此功能,请将hello-cli项目放置到项目node_modules文件夹,并执行npm link本地安装## 并且项目根目录需创建templates文件夹## 来存放component.vue.art和page.vue.art两个art-template模板文件hello-cli add## CLI交互[?] 请选择需要创建的类型? 组件(component)[?] 请输入名称(支持多级路径, 如:xxx/xxx)? index/test_component
运行后,会在项目根目录自动创建components/index/TestComponent.vue文件
以上,简单做两个例子,更多功能期待你们探索
留言与评论(共有 0 条评论) “” |