项目脚手架搭建 cli js
脚手架功能模块图

脚手架流程图

安装lerna
lerna是一个可以在管理多个npm包,所以对于多个npm包管理很方便。
npm install lerna -g
建立项目文件夹
mkdir xi-cli
cd xi-cli
mkdir command
mkdir core
mkdir utils
lerna初始化
lerna init
npm包主要是分功能放在command、core、utils三个文件下面,所以需要删除packages,修改lerna.json
 "packages": ["command/*", "core/*", "utils/*"]

实现个模块功能
添加core模块
lerna create @xi-cli/core

给core/index.js 添加#!usr/bin/env node,让系统执行xi-cl的时候,去环境变量里面去找node来执行
// core/index.js
#!usr/bin/env node
//core/package.json
"bin": {
    "xi-cli": "lib/index.js"
  },
执行npm link
npm link
测试命令
xi-cli
添加日志模块
//添加日志
lerna create @xi-cli/log
//安装npmlog
lerna add npmlog --scope=@xi-cli/log
在log/index.js里面,初始化npmlog的基本配置
"use strict";
const npmlog = require("npmlog");
//定义npmlog的level
npmlog.level = process.env.LOG_LEVEL ? process.env.LOG_LEVEL : "info";
npmlog.heading = "xi-cli";
module.exports = npmlog;
core模块功能开发
core模块主要实现init项目命令
//给core模块添加 log模块
lerna add @xi-cli/log --scope=@xi-cli/core
安装commander
lerna add commander --scope=@xi-cli/core
注册init命令
program
    .name("xi-cli")
    .command("init [name]")
    .option("-f,--force", "是否强制初始化项目")
    .action((name) => {
     console.log("name")
    });
 //重点 不能忘记
  program.parse(process.argv);
测试命令
xi-cli init projectname

实现init项目command的逻辑
这里主要实现的功能就是下载模板,render模板,安装依赖,启动项目这几项功能
"use strict";
const fs = require("fs");
const inquier = require("inquirer");
const fse = require("fs-extra");
const {execSync} = require("child_process");
const path = require("path");
const glob = require("glob");
const ejs = require("ejs");
const log = require("@xi-cli/log");
const {resolve} = require("path");
//__dirname 执行文件的目录  这里是init/lib/index.js
//需要的程序执行的当前目录
const localPath = process.cwd();
class Init {
  constructor(name) {
    this.propjectName = name;
    this.force = true;
    this.configInfo = {
      projectName: name,
    };
    this.exec();
  }
  async exec() {
    try {
      // 准备阶段
      await this.prepare();
      //下载模块
      this.downloadTemplate();
      //匹配文件
      let files = await this.getGlobFile();
      //ejsrender模板
      let res = await this.ejsRender(files);
      let tempalteDir = await this.moveFile();
      //删除模板文件夹
      fse.removeSync(path.join(localPath, tempalteDir));
      //安装依赖 并启动
      this.installDepend();
      this.initStartProject();
    } catch (e) {
      log.error(e);
    }
  }
  async prepare() {
    //判断当前目录是否为空
    if (this.isCewEmpty()) {
      //询问是否创建
      const answer = await inquier.prompt([
        {
          type: "confrim",
          name: "isContinue",
          message: "当前文件不为空,是否继续?",
          default: "xi-project",
        },
      ]);
      if (answer.isContinue) {
        //是否启动强制更新
        fse.emptyDirSync(localPath);
      }
    }
  }
  isCewEmpty() {
    let fileList = fs.readdirSync(localPath);
    //文件过滤
    fileList = fileList.filter((file) => {
      return !file.startsWith(".") && ["node_modules"].indexOf(file) < 0;
    });
    return fileList.length > 0 && fileList ? true : false;
  }
  downloadTemplate() {
    log.info("开始下载模板");
    //git模块的地址
    execSync("git clone https://gitee.com/rainbowChenhong/xi-template.git", {
      stdio: [0, 1, 2], // we need this so node will print the command output
      cwd: localPath, // path to where you want to save the file
    });
  }
  async getGlobFile() {
    const dir = process.cwd();
    return new Promise((resolve, reject) => {
      glob(
        "**",
        {
          cwd: dir,
          //忽略文件
          ignore: ["**/**.html"],
          //匹配文件夹
          nodir: true,
        },
        (err, files) => {
          if (err) reject(err);
          resolve(files);
        }
      );
    });
  }
  ejsRender(files) {
    let filesPromise = [];
    files.map((file) => {
      const filePath = path.join(localPath, file);
      filesPromise.push(
        new Promise((resolve, reject) => {
          ejs.renderFile(filePath, this.configInfo, (err, result) => {
            if (err) {
              reject(err);
            }
            if (result) {
              //写入
              fse.writeFileSync(filePath, result);
            }
            resolve(result);
          });
        })
      );
    });
    return Promise.all(filesPromise);
  }
  moveFile() {
    return new Promise((resolve, reject) => {
      glob(
        "**",
        {
          cwd: localPath,
          //忽略文件
          ignore: [],
          //匹配文件夹
          nodir: true,
        },
        (err, files) => {
          let curentDir = "";
          files.map((file) => {
            if (err) reject(err);
            const filePath = path.join(localPath, file);
            let fileAr = file.split("/");
            curentDir = fileAr.shift();
            let newPath = path.join(localPath, fileAr.join("/"));
            //移动文件
            fse.moveSync(filePath, newPath);
          });
          resolve(curentDir);
        }
      );
    });
  }
  installDepend() {
    execSync("npm install ", {
      stdio: [0, 1, 2],
      cwd: localPath,
    });
  }
  initStartProject() {
    execSync("npm run start ", {
      stdio: [0, 1, 2],
      cwd: localPath,
    });
  }
}
module.exports = Init;
修改core/lib/index.js
//安装init模块
lerna add @xi-cli/init --scope=@xi-cli/core
//core/lib/index.js 修改命令
const Init = require("@xi-cli/init");
 program
    .command("init [name]")
    .option("-f,--force", "是否强制初始化项目")
    .action((name) => {
      new Init(name);
    });
测试执行
//需要注意的是不支持 windows 系统
//因为内部是使用#!/usr/bin/env node 来执行命令的
xi-cli init projectName
//windows系统
node core/lib/index.js init projectname
效果:

 
                    
                     
                    
                 
                    
                
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号