脚手架功能模块图
需要注意的是不支持 windows 系统
因为内部是使用#!/usr/bin/env node 来执行命令的
脚手架流程图
安装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); });
测试执行
xi-cli init projectName
效果:
代码地址