通用脚手架命令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 || {});
}
相关推荐
栈老师不回家18 分钟前
Vue 计算属性和监听器
前端·javascript·vue.js
前端啊龙24 分钟前
用vue3封装丶高仿element-plus里面的日期联级选择器,日期选择器
前端·javascript·vue.js
一颗松鼠28 分钟前
JavaScript 闭包是什么?简单到看完就理解!
开发语言·前端·javascript·ecmascript
小远yyds1 小时前
前端Web用户 token 持久化
开发语言·前端·javascript·vue.js
吕彬-前端2 小时前
使用vite+react+ts+Ant Design开发后台管理项目(五)
前端·javascript·react.js
学前端的小朱2 小时前
Redux的简介及其在React中的应用
前端·javascript·react.js·redux·store
guai_guai_guai2 小时前
uniapp
前端·javascript·vue.js·uni-app
bysking3 小时前
【前端-组件】定义行分组的表格表单实现-bysking
前端·react.js
王哲晓3 小时前
第三十章 章节练习商品列表组件封装
前端·javascript·vue.js
fg_4113 小时前
无网络安装ionic和运行
前端·npm