如何使用 NodeJS 构建 CLI
先决条件
设置项目
构建 CLI
安装和使用 Inquirer
添加逻辑
用粉笔美化
将其发布到 npm
结束
如何使用 NodeJS 构建 CLI
CLI(命令行界面)是人类迄今为止创建的最基础、最强大的应用程序之一。我们每天都在使用 CLI,无论是 npm、git 还是其他任何 CLI。你的日常工作流程中是否有一些需要反复执行的操作?🤔。很有可能,这些操作都可以使用 CLI 实现自动化✨
那么让我们开始吧🏄
今天我们将构建一个 CLI,它将生成预装了 TailwindCSS、ESLint 和 Prettier 的入门模板。
先决条件
以下是您在学习本教程时需要用到的一些工具:
- 安装了NodeJS的 LTS(长期支持)版本。
- 文本编辑器。
设置项目
让我们初始化一个 NodeJS 项目
- 打开你的终端
- 为您的项目创建一个文件夹
mkdir tailwindcli
- 导航至
cd tailwindcli
- 初始化 NodeJS 项目
npm init
构建 CLI
现在我们已经准备好 NodeJS 设置了。让我们开始构建 CLI
bin
在项目文件夹的根目录中创建一个名为的文件夹。index.js
在文件夹中创建一个名为 的文件bin
。这将是 CLI 的主文件。- 现在打开
package.json
文件并将键的值更改main
为./bin/index.js
。 package.json
现在在文件中添加一个条目bin
,并将其键设置为,tcli
并将其值设置为./bin/index.js
该词
tcli
是我们用来调用 CLI 的关键字。
进行更改后,package.json
文件应如下所示:
{
"name": "tailwindcli",
"version": "1.0.0",
"description": "A CLI for generating starter files with TailwindCSS pre-installed",
"main": "./bin/index.js",
"bin": {
"tcli": "./bin/index.js"
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": ["cli", "tailwindcss", "nodejs"],
"author": "Your name",
"license": "MIT"
}
- 打开
bin/index.js
文件并在文件顶部添加此行
#! /usr/bin/env node
以 开头的行
#!
称为 Shebang 行。Shebang 行用于向将运行以下代码的解释器提供绝对路径。
让我们添加一些 JS 代码,以便我们可以测试 CLI 🚀。
- 添加一些 JS 代码
console.log('The CLI is working 🚀');
- 安装并测试 CLI
CLI 可以在系统的任何位置调用,因此我们使用以下命令进行全局安装
npm install -g .
让我们通过运行tcli
命令来测试我们的 CLI。
🎉 Tada,我们的 CLI 正在运行
安装和使用 Inquirer
Inquirer 是一个用于创建交互式 CLI 界面的软件包。例如:
要安装,请运行以下命令
npm install inquirer
添加查询者的样板
这是查询者的样板
#! /usr/bin/env node
const inquirer = require('inquirer');
inquirer
.prompt([
/* Pass your questions in here */
])
.then((answers) => {
// Use user feedback for... whatever!!
});
添加问题
我们必须将问题作为对象传递。让我们添加第一个关于 JS 框架的问题。
#! /usr/bin/env node
const inquirer = require('inquirer');
inquirer
.prompt([
{
type: 'list',
name: 'framework',
message: 'Choose the JS framework which you are using:',
choices: ['React', 'NextJS', 'Angular', 'Svelte', 'VueJS'],
},
])
.then((answers) => {});
让我们分解一下,了解每个部分的含义
所以这里name
是key
对象的
message
:这是向用户显示的问题choices
:这些是提供给用户的选项
清理代码库[可选]
bin
我们可以在名为 的文件夹中创建一个文件夹,并在名为 的文件夹utils
中创建一个文件。我们可以在其中存储问题并将其导入到文件中utils
questions.js
questions.js
index.js
utils/questions.js
// This question would be shown at the starting
const questions = [
{
type: 'list',
name: 'framework',
message: 'Choose the JS framework which you are using:',
choices: ['React', 'NextJS', 'Angular', 'Svelte', 'VueJS'],
},
];
// This question would be shown only when the user choose either React or NextJS
const questionsTs = [
{
type: 'list',
name: 'typescript',
message: 'Does your project use TypeScript?',
choices: ['Yes', 'No'],
},
];
module.exports.questions = questions;
module.exports.questionsTs = questionsTs;
index.js
#! /usr/bin/env node
const inquirer = require('inquirer');
const { questions, questionsTs } = require('./utils/questions.js');
inquirer.prompt(questions).then((answers) => {
// Use user feedback for... whatever!!
});
添加逻辑
在我们创建问题时,是时候添加一些逻辑了。
访问问题的答案类似于从对象中访问键的值。特定问题的答案的值是answers.<name-of-the-question>
在创建启动文件时,让我们使用ShellJS运行以下命令git clone
:mkdir
...
安装 ShellJS
要安装 ShellJS,请运行以下命令
npm install shelljs
使用 ShellJS
让我们添加一些 if 和 else 块来实现逻辑
#! /usr/bin/env node
const inquirer = require('inquirer');
const shell = require('shelljs');
const { questions, questionsTs } = require('./utils/questions.js');
inquirer.prompt(questions).then((answers) => {
if (answers.framework === 'React') {
inquirer.prompt(questionsTs).then((answersTs) => {
if (answersTs.typescript === 'Yes') {
// If the user has choosen React and want to use TypeScript
} else {
// If the user has choosen React but doesn't want to use TypeScript
}
});
} else if (answers.framework === 'NextJS') {
inquirer.prompt(questionsTs).then((answersTs) => {
if (answersTs.typescript === 'Yes') {
// If the user has choosen NextJS and want to use TypeScript
} else {
// If the user has choosen NextJS but doesn't want to use TypeScript
}
});
else if (answers.framework === 'Svelte') {
// If the user has choosen Svelte
} else {
// If the user has choosen VueJS
}
});
让我们找到一些与 TailwindCSS 集成的 JS 框架的模板
- YashKumarVerma的React + TailwindCSS
- Neeraj1005的NextJS + TailwindCSS
- React + TailwindCSS + TypeScript(作者:GKaszewski)
- NextJS + TailwindCSS + TypeScript(作者:avneesh0612)
- jhanca-vm的Svelte + TailwindCSS
- web2023的VueJS + TailwindCSS
非常感谢那些为社区制作这些精彩模板的优秀人士✨
要运行git clone
命令,请使用 ShellJS 我们刚刚使用的exec
方法
shell.exec('git clone <repo-link>');
现在让我们填写 if 和 else 块
#! /usr/bin/env node
const inquirer = require('inquirer');
const shell = require('shelljs');
const path = process.cwd();
const { questions, questionsTs } = require('./utils/questions.js');
inquirer.prompt(questions).then((answers) => {
if (answers.framework === 'React') {
inquirer.prompt(questionsTs).then((answersTs) => {
if (answersTs.typescript === 'Yes') {
shell.exec(`mkdir ${answers.projectName}`);
shell.exec(
`git clone https://github.com/GKaszewski/react-tailwind-typescript-template ${answers.projectName}`
);
shell.cd(`${path}/${answers.projectName}`);
shell.exec(`npm i`);
console.log(
'👨💻 Successfully installed all the required dependencies\nHappy hacking 🚀'
);
} else {
shell.exec(`mkdir ${answers.projectName}`);
shell.exec(
`git clone https://github.com/YashKumarVerma/react-tailwind-template ${answers.projectName}`
);
console.log('🛠️ Successfully build the required files');
shell.cd(`${path}/${answers.projectName}`);
shell.exec(`npm i`);
console.log(
'👨💻 Successfully installed all the required dependencies\nHappy hacking 🚀'
);
}
});
} else if (answers.framework === 'NextJS') {
inquirer.prompt(questionsTs).then((answersTs) => {
if (answersTs.typescript === 'Yes') {
shell.exec(`mkdir ${answers.projectName}`);
shell.exec(
`git clone https://github.com/avneesh0612/next-starter ${answers.projectName}`
);
shell.cd(`${path}/${answers.projectName}`);
shell.exec(`npm i`);
console.log(
'👨💻 Successfully installed all the required dependencies\nHappy hacking 🚀'
);
} else {
shell.exec(`mkdir ${answers.projectName}`);
shell.exec(
`git clone https://github.com/Neeraj1005/Nextjs-tailwind-template ${answers.projectName}`
);
shell.cd(`${path}/${answers.projectName}`);
shell.exec(`npm i`);
console.log(
'👨💻 Successfully installed all the required dependencies\nHappy hacking 🚀'
);
}
});
} else if (answers.framework === 'Svelte') {
shell.exec(`mkdir ${answers.projectName}`);
shell.exec(
`git clone https://github.com/jhanca-vm/Svelte-Tailwind ${answers.projectName}`
);
shell.cd(`${path}/${answers.projectName}`);
shell.exec(`npm i`);
console.log(
'👨💻 Successfully installed all the required dependencies\nHappy hacking 🚀'
);
} else {
shell.exec(`mkdir ${answers.projectName}`);
shell.exec(
`git clone https://github.com/web2033/vite-vue3-tailwind-starter ${answers.projectName}`
);
shell.cd(`${path}/${answers.projectName}`);
shell.exec(`npm i`);
console.log(
'👨💻 Successfully installed all the required dependencies\nHappy hacking 🚀'
);
}
});
清理代码库[可选]
让我们在utils
名为 的文件夹中创建一个名为 的新文件links.js
。让我们创建一个哈希表,用于存储模板存储库的 GitHub 存储库链接。
let links = new Map([
['React', 'https://github.com/YashKumarVerma/react-tailwind-template'],
[
'React-TS',
'https://github.com/GKaszewski/react-tailwind-typescript-template',
],
['NextJS', 'https://github.com/Neeraj1005/Nextjs-tailwind-template'],
['NextJS-TS', 'https://github.com/avneesh0612/next-starter'],
['Svelte', 'https://github.com/jhanca-vm/Svelte-Tailwind'],
['Vue', 'https://github.com/web2033/vite-vue3-tailwind-starter'],
]);
module.exports = links;
让我们导入utils/index.js
并替换 GitHub 模板存储库链接。
#! /usr/bin/env node
const inquirer = require('inquirer');
const shell = require('shelljs');
const path = process.cwd();
const { questions, questionsTs } = require('./utils/questions.js');
const links = require('./utils/links.js');
inquirer.prompt(questions).then((answers) => {
if (answers.framework === 'React') {
inquirer.prompt(questionsTs).then((answersTs) => {
if (answersTs.typescript === 'Yes') {
shell.exec(`mkdir ${answers.projectName}`);
console.log('📁 Created a folder for the project');
shell.exec(`git clone ${links.get('React-TS')} ${answers.projectName}`);
console.log(`🖨️ Cloned started files into ${answers.projectName}`);
shell.cd(`${path}/${answers.projectName}`);
shell.exec(`npm i`);
console.log(
'👨💻 Successfully installed all the required dependencies\nHappy hacking 🚀'
);
} else {
shell.exec(`mkdir ${answers.projectName}`);
console.log('📁 Created a folder for the project');
shell.exec(`git clone ${links.get('React')} ${answers.projectName}`);
console.log(`🖨️ Cloned started files into ${answers.projectName}`);
shell.cd(`${path}/${answers.projectName}`);
shell.exec(`npm i`);
console.log(
'👨💻 Successfully installed all the required dependencies\nHappy hacking 🚀'
);
}
});
} else if (answers.framework === 'NextJS') {
inquirer.prompt(questionsTs).then((answersTs) => {
if (answersTs.typescript === 'Yes') {
shell.exec(`mkdir ${answers.projectName}`);
console.log('📁 Created a folder for the project');
shell.exec(
`git clone ${links.get('NextJS-TS')} ${answers.projectName}`
);
console.log(`🖨️ Cloned started files into ${answers.projectName}`);
shell.cd(`${path}/${answers.projectName}`);
shell.exec(`npm i`);
console.log(
'👨💻 Successfully installed all the required dependencies\nHappy hacking 🚀'
);
} else {
shell.exec(`mkdir ${answers.projectName}`);
console.log('📁 Created a folder for the project');
shell.exec(`git clone ${links.get('NextJS')} ${answers.projectName}`);
console.log(`🖨️ Cloned started files into ${answers.projectName}`);
shell.cd(`${path}/${answers.projectName}`);
shell.exec(`npm i`);
console.log(
'👨💻 Successfully installed all the required dependencies\nHappy hacking 🚀'
);
}
});
} else if (answers.framework === 'Svelte') {
shell.exec(`mkdir ${answers.projectName}`);
console.log('📁 Created a folder for the project');
shell.exec(`git clone ${links.get('Svelte')} ${answers.projectName}`);
console.log(`🖨️ Cloned started files into ${answers.projectName}`);
shell.cd(`${path}/${answers.projectName}`);
shell.exec(`npm i`);
console.log(
'👨💻 Successfully installed all the required dependencies\nHappy hacking 🚀'
);
} else {
shell.exec(`mkdir ${answers.projectName}`);
console.log('📁 Created a folder for the project');
shell.exec(`git clone ${links.get('Vue')} ${answers.projectName}`);
console.log(`🖨️ Cloned started files into ${answers.projectName}`);
shell.cd(`${path}/${answers.projectName}`);
shell.exec(`npm i`);
console.log(
'👨💻 Successfully installed all the required dependencies\nHappy hacking 🚀'
);
}
});
用粉笔美化
我们用粉笔给文本添加颜色
要安装 chalk,请使用以下命令:
npm install chalk
现在让我们将粉笔导入到我们的index.js
文件中
const chalk = require('chalk');
粉笔有几种预先建立的颜色方法
Chalk 还提供了一种hex
方法,通过它你可以使用任何颜色
让我们为成功输出添加绿色
console.log(chalk.green('Hey 👀, I am a green colored text')); // This is how we can add colors by using chalk
#! /usr/bin/env node
const inquirer = require('inquirer');
const shell = require('shelljs');
const chalk = require('chalk');
const path = process.cwd();
const { questions, questionsTs } = require('./utils/questions.js');
const links = require('./utils/links.js');
inquirer.prompt(questions).then((answers) => {
if (answers.framework === 'React') {
inquirer.prompt(questionsTs).then((answersTs) => {
if (answersTs.typescript === 'Yes') {
shell.exec(`mkdir ${answers.projectName}`);
console.log(chalk.green('📁 Created a folder for the project'));
shell.exec(`git clone ${links.get('React-TS')} ${answers.projectName}`);
console.log(
chalk.green(`🖨️ Cloned started files into ${answers.projectName}`)
);
shell.cd(`${path}/${answers.projectName}`);
shell.exec(`npm i`);
console.log(
chalk.green(
'👨💻 Successfully installed all the required dependencies\nHappy hacking 🚀'
)
);
} else {
shell.exec(`mkdir ${answers.projectName}`);
console.log(chalk.green('📁 Created a folder for the project'));
shell.exec(`git clone ${links.get('React')} ${answers.projectName}`);
console.log(
chalk.green(`🖨️ Cloned started files into ${answers.projectName}`)
);
shell.cd(`${path}/${answers.projectName}`);
shell.exec(`npm i`);
console.log(
chalk.green(
'👨💻 Successfully installed all the required dependencies\nHappy hacking 🚀'
)
);
}
});
} else if (answers.framework === 'NextJS') {
inquirer.prompt(questionsTs).then((answersTs) => {
if (answersTs.typescript === 'Yes') {
shell.exec(`mkdir ${answers.projectName}`);
console.log(chalk.green('📁 Created a folder for the project'));
shell.exec(
`git clone ${links.get('NextJS-TS')} ${answers.projectName}`
);
console.log(
chalk.green(`🖨️ Cloned started files into ${answers.projectName}`)
);
shell.cd(`${path}/${answers.projectName}`);
shell.exec(`npm i`);
console.log(
chalk.green(
'👨💻 Successfully installed all the required dependencies\nHappy hacking 🚀'
)
);
} else {
shell.exec(`mkdir ${answers.projectName}`);
console.log(chalk.green('📁 Created a folder for the project'));
shell.exec(`git clone ${links.get('NextJS')} ${answers.projectName}`);
console.log(
chalk.green(`🖨️ Cloned started files into ${answers.projectName}`)
);
shell.cd(`${path}/${answers.projectName}`);
shell.exec(`npm i`);
console.log(
chalk.green(
'👨💻 Successfully installed all the required dependencies\nHappy hacking 🚀'
)
);
}
});
} else if (answers.framework === 'Svelte') {
shell.exec(`mkdir ${answers.projectName}`);
console.log(chalk.green('📁 Created a folder for the project'));
shell.exec(`git clone ${links.get('Svelte')} ${answers.projectName}`);
console.log(
chalk.green(`🖨️ Cloned started files into ${answers.projectName}`)
);
shell.cd(`${path}/${answers.projectName}`);
shell.exec(`npm i`);
console.log(
chalk.green(
'👨💻 Successfully installed all the required dependencies\nHappy hacking 🚀'
)
);
} else {
shell.exec(`mkdir ${answers.projectName}`);
console.log(chalk.green('📁 Created a folder for the project'));
shell.exec(`git clone ${links.get('Vue')} ${answers.projectName}`);
console.log(
chalk.green(`🖨️ Cloned started files into ${answers.projectName}`)
);
shell.cd(`${path}/${answers.projectName}`);
shell.exec(`npm i`);
console.log(
chalk.green(
'👨💻 Successfully installed all the required dependencies\nHappy hacking 🚀'
)
);
}
});
将其发布到 npm
我们已经成功构建了 CLI 🥳。现在让我们将它部署到 npm,以便其他开发者可以使用我们的 CLI。
创建 npm 帐户
前往npmjs.org创建一个帐户,并确保你也验证了它
唯一的包名称
npm 软件包的名称都是独一无二的。npm 不允许发布名称已被占用的软件包。请访问npmjs.org并检查您的软件包名称是否已被占用。
tailwindcli
已被此包占用。因此我必须将其名称更改为tailwindcsscli
更改包的名称
如果您的包裹是唯一的并且未被占用,请跳过此步骤,如果不是,则请按照此步骤操作。
- 打开
package.json
文件 - 将键的值更改
name
为唯一名称,在本例中,我将其更改为tailwindcsscli
添加关键字
让我们添加一些与我们的包相关的关键字。由于我们在本教程中构建了一个 CLI,因此我们将以下内容作为关键字:
- 命令行
- 尾风CSS
- nodejs
添加许可证
请查看license-templates GitHub 仓库,获取可在项目中使用的许可证模板。我使用的是 MIT 许可证
添加存储库链接
如果你在任何 Git 提供商(例如 GitHub、GitLab)上有一个仓库,你可以在一个新条目中链接到该仓库,repository
该条目的键名为type
和url
,值分别为git
和git+<your-git-repo-link>.git
。它看起来像这样
"repository": {
"type": "git",
"url": "git+<your-git-repo-link>.git"
}
就我而言,repo 链接是https://github.com/Kira272921/tailwindcsscli。因此它看起来像这样
"repository": {
"type": "git",
"url": "git+https://github.com/Kira272921/tailwindcsscli.git"
}
添加错误报告链接
让我们添加一个指向用户报告我们软件包 bug 的站点/地方的链接。通常,它会是 GitHub 仓库中问题页面的链接。
"bugs": {
"url": "https://github.com/Kira272921/tailwindcsscli/issues"
}
添加主页链接
让我们添加指向 npm 包主页的链接。通常,它会指向 GitHub 仓库的 README 链接。
"homepage": "https://github.com/Kira272921/tailwindcsscli/issues#readme"
通过 npm CLI 登录你的 npm 帐户
现在让我们通过 npm CLI 登录我们的 npm 帐户,以便将包发布到 npm。要登录您的 npm 帐户,请运行以下命令并输入正确的凭据。
npm login
发布你的 npm 包
现在让我们使用以下命令发布我们的 npm 包
npm publish
😱 哦不!我收到错误信息
让我们相应地更改包的名称,然后使用推荐的命令发布。我的包package.json
现在看起来像这样
{
"name": "@kira272921/tailwindcsscli",
"version": "1.0.0",
"description": "A CLI for generating starter files for different JS frameworks with tailwindCSS pre-installed",
"main": "./bin/index.js",
"bin": {
"tcli": "./bin/index.js"
},
"scripts": {
"start": "node ./bin/index.js"
},
"keywords": ["cli", "tailwindcss", "nodejs"],
"author": "Kira272921",
"license": "MIT",
"dependencies": {
"inquirer": "^8.2.0",
"shelljs": "^0.8.4"
},
"repository": {
"type": "git",
"url": "git+https://github.com/Kira272921/tailwindcsscli.git"
},
"bugs": {
"url": "https://github.com/Kira272921/tailwindcsscli/issues"
},
"homepage": "https://github.com/Kira272921/tailwindcsscli/issues#readme"
}
现在让我们尝试使用以下命令再次发布它
npm publish --access=public
祈祷一切顺利🤞。耶!我们的 CLI 成功发布到 npm 了🥳
结束
本教程的代码可在 Github 上找到
https://github.com/Kira272921/tailwindcsscli
这篇博文就到这里啦,希望大家能从这篇博文中学到一些新东西。下篇博文再见👋。
鏂囩珷鏉ユ簮锛�https://dev.to/byteslash/how-to-build-a-cli-using-nodejs-4bl2