1. 准备项目
首先,确保您的项目结构和package.json文件已经准备好。
- 项目结构示例:
my-cli
├── bin
│ └── my-cli.js
├── lib
│ └── commands
│ └── create.js
├── templates
│ └── my-vue-template
│ ├── public
│ ├── src
│ ├── .gitignore
│ ├── package.json
│ └── README.md
├── package.json
└── README.md
各个文件和目录的作用
bin目录: 存放CLI的入口文件。
bin/my-cli.js: CLI的主入口文件,处理命令行输入和执行相应的操作。
lib目录: 存放CLI的业务逻辑代码。
lib/commands/create.js: 处理create命令的业务逻辑代码。
templates目录: 存放项目模板文件。
package.json: npm包的配置文件,包含项目的依赖、命令和其他元数据。
README.md: 项目的说明文档,解释如何使用您的CLI工具。
.gitignore: 指定要忽略的文件和目录,通常用于排除不需要上传到git仓库的文件,如node_modules。
- package.json示例:
{
"name": "my-cli",
"version": "1.0.0",
"description": "My custom CLI tool for Vue projects",
"main": "lib/index.js",
"bin": {
"my-cli": "./bin/my-cli.js"
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "git+https://github.com/username/my-cli.git"
},
"keywords": [
"cli",
"tool",
"vue"
],
"author": "Your Name",
"license": "MIT",
"bugs": {
"url": "https://github.com/username/my-cli/issues"
},
"homepage": "https://github.com/username/my-cli#readme",
"dependencies": {
"commander": "^8.0.0",
"ejs": "^3.1.6",
"fs-extra": "^10.0.0"
}
}
2. 创建命令行入口文件
创建一个bin/my-cli.js文件,用于处理CLI命令。
- bin/my-cli.js示例:
#!/usr/bin/env node
const { program } = require('commander');
const create = require('../lib/commands/create');
program
.version('1.0.0')
.command('create <project-name>')
.description('create a new Vue project')
.action((projectName) => {
create(projectName);
});
program.parse(process.argv);
3. 创建命令处理文件
创建一个处理命令的文件,例如lib/commands/create.js。
- lib/commands/create.js示例:
const fs = require('fs-extra');
const path = require('path');
module.exports = async (projectName) => {
const projectPath = path.join(process.cwd(), projectName);
if (fs.existsSync(projectPath)) {
console.error(`Project ${projectName} already exists`);
process.exit(1);
}
const templatePath = path.join(__dirname, '../../templates/my-vue-template');
try {
await fs.copy(templatePath, projectPath);
console.log(`Project ${projectName} created at ${projectPath}`);
console.log('Run the following commands to get started:');
console.log(`cd ${projectName}`);
console.log('npm install');
console.log('npm run serve');
} catch (err) {
console.error(err);
}
};
4. 添加执行权限
chmod +x bin/my-cli.js
5. 登录npm
在终端中登录到npm:
npm login
6. 发布包到npm
将包发布到npm:
npm publish
7. 全局安装和测试
npm install -g my-cli
my-cli create my-project
8.create项目自动执行install和server
修改lib/commands/create.js
更新create.js文件,添加自动执行命令的逻辑:
const fs = require('fs-extra');
const path = require('path');
const { exec } = require('child_process');
module.exports = async (projectName) => {
const projectPath = path.join(process.cwd(), projectName);
if (fs.existsSync(projectPath)) {
console.error(`Project ${projectName} already exists`);
process.exit(1);
}
const templatePath = path.join(__dirname, '../../templates/my-vue-template');
try {
await fs.copy(templatePath, projectPath);
console.log(`Project ${projectName} created at ${projectPath}`);
// 自动安装依赖
console.log('Installing dependencies...');
exec('npm install', { cwd: projectPath }, (error, stdout, stderr) => {
if (error) {
console.error(`Error installing dependencies: ${error.message}`);
return;
}
if (stderr) {
console.error(`npm install stderr: ${stderr}`);
return;
}
console.log(stdout);
// 自动运行服务器
console.log('Starting the development server...');
exec('npm run serve', { cwd: projectPath }, (error, stdout, stderr) => {
if (error) {
console.error(`Error starting the server: ${error.message}`);
return;
}
if (stderr) {
console.error(`npm run serve stderr: ${stderr}`);
return;
}
console.log(stdout);
});
});
} catch (err) {
console.error(err);
}
};
更新bin/my-cli.js
确保您的bin/my-cli.js文件是正确的,没有需要修改的地方,但确保其完整性:
#!/usr/bin/env node
const { program } = require('commander');
const create = require('../lib/commands/create');
program
.version('1.0.0')
.command('create <project-name>')
.description('create a new Vue project')
.action((projectName) => {
create(projectName);
});
program.parse(process.argv);
发布和测试
9.创建时询问用户是否需要install和server
- 安装inquirer
npm install inquirer
- 更新lib/commands/create.js
const fs = require('fs-extra');
const path = require('path');
const { exec } = require('child_process');
const inquirer = require('inquirer');
module.exports = async (projectName) => {
const projectPath = path.join(process.cwd(), projectName);
if (fs.existsSync(projectPath)) {
console.error(`Project ${projectName} already exists`);
process.exit(1);
}
const templatePath = path.join(__dirname, '../../templates/my-vue-template');
try {
await fs.copy(templatePath, projectPath);
console.log(`Project ${projectName} created at ${projectPath}`);
// 询问用户是否需要安装依赖和启动服务器
const answers = await inquirer.prompt([
{
type: 'confirm',
name: 'install',
message: 'Do you want to install dependencies?',
default: true
},
{
type: 'confirm',
name: 'serve',
message: 'Do you want to start the development server?',
default: true,
when: (answers) => answers.install
}
]);
if (answers.install) {
console.log('Installing dependencies...');
exec('npm install', { cwd: projectPath }, (error, stdout, stderr) => {
if (error) {
console.error(`Error installing dependencies: ${error.message}`);
return;
}
if (stderr) {
console.error(`npm install stderr: ${stderr}`);
return;
}
console.log(stdout);
if (answers.serve) {
console.log('Starting the development server...');
exec('npm run serve', { cwd: projectPath }, (error, stdout, stderr) => {
if (error) {
console.error(`Error starting the server: ${error.message}`);
return;
}
if (stderr) {
console.error(`npm run serve stderr: ${stderr}`);
return;
}
console.log(stdout);
});
}
});
} else {
console.log('You can manually install dependencies by running "npm install" and start the server by running "npm run serve".');
}
} catch (err) {
console.error(err);
}
};
- 确保CLI入口文件正确
#!/usr/bin/env node
const { program } = require('commander');
const create = require('../lib/commands/create');
program
.version('1.0.0')
.command('create <project-name>')
.description('create a new Vue project')
.action((projectName) => {
create(projectName);
});
program.parse(process.argv);
- 发布和测试
10.create时发现并未询问或者变化
先uninstall卸载my-cli再重新安装
确认是否sodu
11.多个template,通过询问用户来安装哪一个
- 准备模板
假设您有两个模板,template1和template2,它们存放在templates目录下:
my-cli
├── bin
│ └── my-cli.js
├── lib
│ └── commands
│ └── create.js
├── templates
│ ├── template1
│ │ ├── public
│ │ │ └── index.html
│ │ ├── src
│ │ │ ├── assets
│ │ │ ├── components
│ │ │ ├── App.vue
│ │ │ └── main.js
│ │ ├── .gitignore
│ │ ├── package.json
│ │ └── README.md
│ └── template2
│ ├── public
│ │ └── index.html
│ ├── src
│ │ ├── assets
│ │ ├── components
│ │ ├── App.vue
│ │ └── main.js
│ ├── .gitignore
│ ├── package.json
│ └── README.md
├── package.json
└── README.md
- 更新lib/commands/create.js
修改create.js文件,以便在创建项目时询问用户选择使用哪个模板:
const fs = require('fs-extra');
const path = require('path');
const { exec } = require('child_process');
const inquirer = require('inquirer');
module.exports = async (projectName) => {
const projectPath = path.join(process.cwd(), projectName);
if (fs.existsSync(projectPath)) {
console.error(`Project ${projectName} already exists`);
process.exit(1);
}
// 询问用户选择模板
const { template } = await inquirer.prompt([
{
type: 'list',
name: 'template',
message: 'Which template would you like to use?',
choices: ['template1', 'template2'],
}
]);
const templatePath = path.join(__dirname, '../../templates', template);
try {
await fs.copy(templatePath, projectPath);
console.log(`Project ${projectName} created using ${template} at ${projectPath}`);
// 询问用户是否需要安装依赖和启动服务器
const answers = await inquirer.prompt([
{
type: 'confirm',
name: 'install',
message: 'Do you want to install dependencies?',
default: true
},
{
type: 'confirm',
name: 'serve',
message: 'Do you want to start the development server?',
default: true,
when: (answers) => answers.install
}
]);
if (answers.install) {
console.log('Installing dependencies...');
exec('npm install', { cwd: projectPath }, (error, stdout, stderr) => {
if (error) {
console.error(`Error installing dependencies: ${error.message}`);
return;
}
if (stderr) {
console.error(`npm install stderr: ${stderr}`);
return;
}
console.log(stdout);
if (answers.serve) {
console.log('Starting the development server...');
exec('npm run serve', { cwd: projectPath }, (error, stdout, stderr) => {
if (error) {
console.error(`Error starting the server: ${error.message}`);
return;
}
if (stderr) {
console.error(`npm run serve stderr: ${stderr}`);
return;
}
console.log(stdout);
});
}
});
} else {
console.log('You can manually install dependencies by running "npm install" and start the server by running "npm run serve".');
}
} catch (err) {
console.error(err);
}
};
- 确保CLI入口文件正确
确保bin/my-cli.js文件内容正确,无需修改,但检查其完整性:
#!/usr/bin/env node
const { program } = require('commander');
const create = require('../lib/commands/create');
program
.version('1.0.0')
.command('create <project-name>')
.description('create a new Vue project')
.action((projectName) => {
create(projectName);
});
program.parse(process.argv);
- 发布和测试
11.inquirer 报错
inquirer 降版本至8.2.0 重新发包测试
12.my-cli update
修改 CLI 入口文件
#!/usr/bin/env node
const { program } = require('commander');
const path = require('path');
const fs = require('fs');
const create = require('../lib/commands/create');
const update = require('../lib/commands/update');
// 读取 package.json 中的版本号
const packageJsonPath = path.join(__dirname, '../package.json');
const pkg = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
program
.version(pkg.version) // 设置版本号
.command('create <project-name>')
.description('create a new Vue project')
.action((projectName) => {
create(projectName);
});
program
.command('update')
.description('update the CLI tool')
.action(() => {
update();
});
program.parse(process.argv);
更新 CLI 入口文件
首先,确保您的 CLI 入口文件 bin/my-cli.js 能够处理 update 命令。更新您的 bin/my-cli.js 文件如下:
#!/usr/bin/env node
const { program } = require('commander');
const create = require('../lib/commands/create');
const update = require('../lib/commands/update');
program
.version('1.0.0')
.command('create <project-name>')
.description('create a new Vue project')
.action((projectName) => {
create(projectName);
});
program
.command('update')
.description('update the CLI tool')
.action(() => {
update();
});
program.parse(process.argv);
发布新版本
卸载重装