本次時《建立Node腳手架》的第二節. 第一節中介紹了建立Node腳手架時經常使用的工具庫. 詳情請查看(上)。本節將一步一步開發一個Node工具, 實現的功能是: 相似create-react-app, 經過這個工具, 能夠建立基本的react編程結構.javascript
clone一份前一節的項目代碼cli-demo. 新項目取名爲create-react-redux-app-cli. 文件結構以下:java
- bin
- cli
- lib
- package.json
複製代碼
在bin節點下, 添加一個crra命令, 引用指向bin/cli. 這樣就能夠執行crra命令node
"bin": {
"crra": "./bin/cli"
},
複製代碼
進入項目的根目錄. 執行react
npm link
複製代碼
安裝完成後, crra命令就能夠在任意地方執行了.webpack
到此, 準備工做已經完成了.git
接下來咱們開幹吧...github
bin/cli文件web
#!/usr/bin/env node
const path = require('path');
const program = require('commander');
program
.version(require('../package.json').version, '-v, --version');
program
.command('create <ProjectName>')
.description('Create a new Project')
.action(async (projectName) => {
console.log(projectName);
})
program.parse(process.argv);
複製代碼
用來下載git上的基礎項目做爲模板.npm
咱們先安裝一個新的工具庫.編程
npm i -D download-git-repo
複製代碼
lib/download.js文件.
const { promisify } = require('util');
const ora = require('ora');
/** * 從git倉庫上下載項目到本地 * @param {String} repo git倉庫地址 * @param {String} desc 本地的路徑 */
const clone = async function (repo, desc) {
// 包裝成一個promise方法.
const download = promisify(require('download-git-repo'));
// 顯示下載進度.
const process = ora(`建立中...`);
process.start();
try {
await download(repo, desc);
process.succeed();
} catch (error) {
process.fail(error.message);
}
};
module.exports = {
clone
};
複製代碼
封裝一個exec方法, 用來執行命令行命令.
const childProcess = require('child_process');
const which = require('which');
const chalk = require('chalk');
/** * 查找系統中用於安裝依賴包的命令 */
function findNpm() {
const npms = ['tnpm', 'cnpm', 'npm'];
for (let i = 0; i < npms.length; i++) {
try {
// 查找環境變量下指定的可執行文件的第一個實例
which.sync(npms[i]);
return npms[i]
} catch (e) {
}
}
throw new Error(chalk.red('請安裝npm'));
}
/** * 開啓子進程來執行命令 * @param {String} cmd 待執行的命令 * @param {Array} args 命令執行時的參數. * @param {Function} fn 執行完成時的回調. */
function exec(cmd, args, fn) {
args = args || [];
const runner = childProcess.spawn(cmd, args, {
stdio: 'inherit'
});
runner.on('close', function (code) {
if (fn) {
fn(code);
}
})
}
module.exports = {
exec,
findNpm
};
複製代碼
用來安裝新項目的依賴.
const which = require('which');
const { exec, findNpm } = require('./exec');
/** * 執行npm install命令, 安裝項目依賴. */
const install = () => {
const npm = findNpm();
exec(which.sync(npm), ['install'], function () {
console.log(npm + '安裝完成');
});
};
module.exports = {
install
};
複製代碼
用來定義更新package.json的方法.
const chalk = require('chalk');
const fs = require('fs-extra');
/** * 更新文件內容. * @param {String} filePath * @param {Object} contents */
const updateFile = (filePath, contents) => {
if(fs.existsSync(filePath)){
const fileContent = Object.assign({}, require(filePath), contents);
fs.writeJSONSync(filePath, fileContent, { spaces: '\t' });
} else{
throw new Error(chalk.red(`${filePath}不存在`));
}
};
module.exports = {
updateFile
};
複製代碼
#!/usr/bin/env node
const path = require('path');
const program = require('commander');
const inquirer = require('inquirer');
const { updateFile } = require('../lib/file');
const { install } = require('../lib/install');
// 定義和用戶交互時的questions
const prompt = () => {
return inquirer.prompt([
{
type: 'input',
name: 'author',
message: '請輸入做者的名稱'
},
{
type: 'input',
name: 'repository',
message: '請輸入GitHub的項目地址'
},
{
type: 'confirm',
name: 'isOk',
message: '請確認輸入是否ok?'
}
])
};
program
.version(require('../package.json').version, '-v, --version');
program
.command('create <ProjectName>')
.description('建立一個新項目')
.action((projectName) => {
prompt().then(async (results) => {
const { author, repository, isOk } = results;
if (!isOk) {
return;
}
// 1. clone項目
const { clone } = require('../lib/download');
console.log('🚀建立項目: ' + projectName);
await clone('github.com:ichenzhifan/react-redux-base', projectName);
console.log(`項目${projectName}建立成功`);
// 2. 更新package.json的配置.
const packageJson = path.join(path.resolve(projectName), 'package.json');
const repositoryObj = repository ? {
type: "git",
url: repository
} : {};
updateFile(packageJson, {
name: projectName,
author,
repository: repositoryObj
});
// 3. 安裝依賴
console.log('安裝依賴...')
// 將node工做目錄更改爲構建的項目根目錄下
const projectPath = path.resolve(projectName);
process.chdir(projectPath);
// 執行安裝命令
install();
});
})
program.parse(process.argv);
複製代碼
到此, 一個功能完整的cli工具就作好了.
請看: 《建立Node腳手架工具(下)》