通用脚手架命令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 || {});
}
相关推荐
@大迁世界4 分钟前
TypeScript 的本质并非类型,而是信任
开发语言·前端·javascript·typescript·ecmascript
GIS之路12 分钟前
GDAL 实现矢量裁剪
前端·python·信息可视化
是一个Bug16 分钟前
后端开发者视角的前端开发面试题清单(50道)
前端
Amumu1213818 分钟前
React面向组件编程
开发语言·前端·javascript
持续升级打怪中40 分钟前
Vue3 中虚拟滚动与分页加载的实现原理与实践
前端·性能优化
GIS之路43 分钟前
GDAL 实现矢量合并
前端
hxjhnct1 小时前
React useContext的缺陷
前端·react.js·前端框架
前端 贾公子1 小时前
从入门到实践:前端 Monorepo 工程化实战(4)
前端
菩提小狗1 小时前
Sqlmap双击运行脚本,双击直接打开。
前端·笔记·安全·web安全
前端工作日常1 小时前
我学习到的AG-UI的概念
前端