建立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脚手架工具(下)》

相关文章
相关标签/搜索