脚手架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 || {});
}