通用脚手架命令Command类封装

脚手架Command类进行优化;

model目录下创建一个新的模块command

js 复制代码
lerna create @mj-cli-dev/command

init模块改成class,并继承于class command;

js 复制代码
"use strict";
const Command = require("@mj-cli-dev/command");

class InitCommand extends Command {}

function init(argv) {
  console.log("init---执行--->", argv);
  return new InitCommand(argv);
}
module.exports = init;
module.exports.InitCommand = InitCommand;

command模块class command的实现;

js 复制代码
"use strict";
class Command {
  constructor(argv) {
    // console.log("Command constructor", argv);
  }

  init() {}

  exec() {}
}
module.exports = Command;

exec模块修改,将类数组转成数组传入init方法:

js 复制代码
require(rootFile).call(null, Array.from(arguments));

终端输入如下命令,即可跑通流程:

js 复制代码
mj-cli-dev init --targetPath /Users/Minjie/Documents/vue3/mj-cli-dev/commands/init --debug

检查Node版本

将core模块中检查Node版本的方法checkNodeVersion移到command模块中;

js 复制代码
class Command {
  constructor(argv) {
    console.log("Command constructor");
    this._argv = argv;
    let runner = new Promise((resolve, reject) => {
      let chain = Promise.resolve();
      chain = chain.then(() => this.checkNodeVersion());
      chain.catch((err) => {
        console.log("err--->", err.message);
      });
    });
  }

  // 检查Node版本放到这里执行
  checkNodeVersion() {
    // 1.获取当前Node版本号
    const currentVersion = process.version;
    // 2.比对最低版本号
    const lowestVersion = constant.LOWEST_NODE_VERSION;
    // 当前版本号 < 最低版本号
    if (!semver.gte(currentVersion, lowestVersion)) {
      throw new Error(
        log.error(`mj-cli-dev需要安装 v${lowestVersion}以上版本的Node.js`)
      );
    }
  }
}

参数初始化操作

全局try catch,但是exec模块里抛出的错误没有捕获,因为是异步所以需要额外的捕获;所以在exec模块中加入try catch进行捕获;

js 复制代码
try {
      require(rootFile).call(null, Array.from(arguments));
    } catch (e) {
      log.error(e.message);
}

在command模块class Command中实现init方法;

command模块的完整代码:

js 复制代码
"use strict";
const log = require("@mj-cli-dev/log");
const semver = require("semver");
const constant = require("./const");
class Command {
  constructor(argv) {
    console.log("Command constructor");
    if (!argv) {
      throw new Error("参数不能为空!");
    }
    if (!Array.isArray(argv)) {
      throw new Error("参数必须为数组!");
    }
    if (argv.length < 1) {
      throw new Error("参数长度必须大于1!");
    }
    this._argv = argv;
    let runner = new Promise((resolve, reject) => {
      let chain = Promise.resolve();
      chain = chain.then(() => this.checkNodeVersion());
      chain = chain.then(() => this.initArgs());
      chain = chain.then(() => this.init());
      chain = chain.then(() => this.exec());
      chain.catch((err) => {
        console.log("err--->", err.message);
      });
    });
  }

  // 检查Node版本放到这里执行
  checkNodeVersion() {
    // 1.获取当前Node版本号
    const currentVersion = process.version;
    // 2.比对最低版本号
    const lowestVersion = constant.LOWEST_NODE_VERSION;
    // 当前版本号 < 最低版本号
    if (!semver.gte(currentVersion, lowestVersion)) {
      throw new Error(
        log.error(`mj-cli-dev需要安装 v${lowestVersion}以上版本的Node.js`)
      );
    }
  }
  // 参数初始化
  initArgs() {
    this._cmd = this._argv[this._argv.length - 1];
    this._argv = this._argv.slice(0, this._argv.length - 1);
  }
  // 命令的准备阶段
  init() {
    throw new Error("init 必须实现");
  }

  exec() {
    throw new Error("exec 必须实现");
  }
}
module.exports = Command;

init模块的完整代码:

js 复制代码
"use strict";
const Command = require("@mj-cli-dev/command");
const log = require("@mj-cli-dev/log");

class InitCommand extends Command {
  init() {
    this.projectName = this._argv[0] || "";
    this.force = !!this._cmd.force;
    log.info("projectName", this.projectName, this.force);
  }
  exec() {
    console.log("int exec--->");
  }
}

function init(argv) {
  console.log("init---执行--->", argv);
  return new InitCommand(argv);
}
module.exports = init;
module.exports.InitCommand = InitCommand;

利用Node多进程动态执行命令

第一种实现方式是用spawn方法;

js 复制代码
const code = "";
const child = cp.spawn("node", ["-e", code], {
cwd: process.cwd(),
});
child.stdout.on("data", (chunk) => {});
child.stderr.on("data", (chunk) => {});

第二种实现方式用inherit,将process.stdin、process.stdout、process.stderr直接和父进程进行绑定,:

js 复制代码
const code = "console.log(1)";
const child = cp.spawn("node", ["-e", code], {
    cwd: process.cwd(),
    stdio: "inherit",
});
child.on("err", (e) => {
    log.error(e.message);
    process.exit(1);
});
child.on("exit", (e) => {
    log.info("命令执行完成", e);
    process.exit(e);
});

再次优化代码:

js 复制代码
  const args = Array.from(arguments);
  const cmd = args[args.length - 1];
  // 去除cmd中无用的属性
  const obj = Object.create(null);
  Object.keys(cmd).forEach((key) => {
    if (
      cmd.hasOwnProperty(key) &&
      !key.startsWith("_") &&
      key !== "parent"
    ) {
      obj[key] = cmd[key];
    }
  });
 
  args[args.length - 1] = obj;
  const code = `require('${rootFile}').call(null, ${JSON.stringify(args)})`;

  const child = cp.spawn("node", ["-e", code], {
    cwd: process.cwd(),
    stdio: "inherit",
  });
  child.on("error", (e) => {
    log.error(e.message);
    process.exit(1);
  });
  child.on("exit", (e) => {
    log.info("命令执行完成", e);
    process.exit(e);
  });

兼容windows系统,封装成通用的方法:

js 复制代码
function spawn(command, args, options) {
  // 判断是不是win32
  // win32的命令:cp.spawn("cmd", ["/c", "node", "-d", code])
  const win32 = process.platform === "win32";

  const cmd = win32 ? "cmd" : command;
  const cmdArgs = win32 ? ["/c"].concat(command, args) : args;

  return cp.spawn(cmd, cmdArgs, options || {});
}
相关推荐
y先森4 小时前
CSS3中的伸缩盒模型(弹性盒子、弹性布局)之伸缩容器、伸缩项目、主轴方向、主轴换行方式、复合属性flex-flow
前端·css·css3
前端Hardy4 小时前
纯HTML&CSS实现3D旋转地球
前端·javascript·css·3d·html
susu10830189114 小时前
vue3中父div设置display flex,2个子div重叠
前端·javascript·vue.js
IT女孩儿5 小时前
CSS查缺补漏(补充上一条)
前端·css
吃杠碰小鸡6 小时前
commitlint校验git提交信息
前端
虾球xz6 小时前
游戏引擎学习第20天
前端·学习·游戏引擎
我爱李星璇7 小时前
HTML常用表格与标签
前端·html
疯狂的沙粒7 小时前
如何在Vue项目中应用TypeScript?应该注意那些点?
前端·vue.js·typescript
小镇程序员7 小时前
vue2 src_Todolist全局总线事件版本
前端·javascript·vue.js
野槐7 小时前
前端图像处理(一)
前端