建立Node腳手架工具(中)

簡介

本次時《建立Node腳手架》的第二節. 第一節中介紹了建立Node腳手架時經常使用的工具庫. 詳情請查看(上)。本節將一步一步開發一個Node工具, 實現的功能是: 相似create-react-app, 經過這個工具, 能夠建立基本的react編程結構.javascript

項目準備

clone一份前一節的項目代碼cli-demo. 新項目取名爲create-react-redux-app-cli. 文件結構以下:java

- bin
    - cli
- lib
- package.json
複製代碼

package.json的配置.

在bin節點下, 添加一個crra命令, 引用指向bin/cli. 這樣就能夠執行crra命令node

"bin": {
    "crra": "./bin/cli"
  },
複製代碼

安裝命令到node_modules.

進入項目的根目錄. 執行react

npm link
複製代碼

安裝完成後, crra命令就能夠在任意地方執行了.webpack

到此, 準備工做已經完成了.git

這個腳手架工具要實現的功能是

  • 工具名稱是crra, 建立項目的命令是create, 執行是要傳入新的項目名稱
  • 過程當中須要支持package.json的配置. 好比git repository, author等.
  • 下載git的基礎項目做爲咱們的模板.
  • 下載模板過程當中, 須要有進度提示
  • 下載完成後, 使用用戶輸入的信息更新新建立項目的package.json
  • 對新建立的項目, 自動安裝依賴.

接下來咱們開幹吧...github

建立一個create命令.

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);
複製代碼

在lib目錄下, 新增一個download.js文件

用來下載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
};
複製代碼

lib目錄下, 新增一個exec.js

封裝一個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
};
複製代碼

lib目錄下, 新增一個install.js

用來安裝新項目的依賴.

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
};

複製代碼

lib目錄下, 新增一個file.js

用來定義更新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
};
複製代碼

在cli中. 將上面定義的公共方法組裝一下.

#!/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工具就作好了.

項目代碼

代碼

接下來, 如何發佈到npm上.

請看: 《建立Node腳手架工具(下)》

相關文章
相關標籤/搜索