通用脚手架命令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 || {});
}
相关推荐
轻口味30 分钟前
【每日学点鸿蒙知识】AVCodec、SmartPerf工具、web组件加载、监听键盘的显示隐藏、Asset Store Kit
前端·华为·harmonyos
alikami33 分钟前
【若依】用 post 请求传 json 格式的数据下载文件
前端·javascript·json
吃杠碰小鸡1 小时前
lodash常用函数
前端·javascript
丰云1 小时前
一个简单封装的的nodejs缓存对象
缓存·node.js
emoji1111111 小时前
前端对页面数据进行缓存
开发语言·前端·javascript
泰伦闲鱼1 小时前
nestjs:GET REQUEST 缓存问题
服务器·前端·缓存·node.js·nestjs
m0_748250031 小时前
Web 第一次作业 初探html 使用VSCode工具开发
前端·html
一个处女座的程序猿O(∩_∩)O1 小时前
vue3 如何使用 mounted
前端·javascript·vue.js
m0_748235952 小时前
web复习(三)
前端
AiFlutter2 小时前
Flutter-底部分享弹窗(showModalBottomSheet)
java·前端·flutter