使用Enquirer来优化scripts工作流

677 阅读5分钟
WechatIMG495.jpeg

1. 背景

uniapp中采用vue-cli方式开发微信小程序,vscode(就是单纯的不想用HBuilderX开发,用着不顺)中每次启动完项目后都需要手动打开微信开发者工具,首次打开的时候还需导入项目,就想简化一下开发流程,把手动变为自动。并且更灵活的交给开发人员控制是否打开,是否跳过开发者工具的初始化耗时。下面分为两次优化。

2. Enquirer简单介绍

www.npmjs.com/package/enq…

Stylish CLI prompts that are user-friendly, intuitive and easy to create.

用户友好的、简单易懂的、易于创建的交互式询问命令行界面

使用示例:

const { prompt } = require('enquirer');

const response = await prompt({
  type: 'input',
  name: 'username',
  message: 'What is your username?'
});

console.log(response);

运行后,会看到问题,等待用户输入,输入haha后,会得到如下结果:

✔ What is your username? · haha
{ username: 'haha' }

3. 第一次优化:

developers.weixin.qq.com/miniprogram…

3.1 打开微信开发者工具脚本

#!/usr/bin/env node

const path = require("path");
const os = require("os");
const { stat, mkdir } = require("fs");
const automator = require("miniprogram-automator");
const argv = process.argv.slice(2);
const rootDir = process.cwd();
// const pagesJson = require(path.resolve(__dirname, "../src/pages.json"));

console.log("platform:", os.platform());
console.log("miniprogram argv:", argv);

// 项目文件夹
const projectPath = path.resolve(rootDir, `./dist/${argv[0]}/mp-weixin`);
const cliPath =
	os.platform() === "win32"
		? "C:/Program Files (x86)/Tencent/微信web开发者工具/cli.bat"
		: "/Applications/wechatwebdevtools.app/Contents/MacOS/cli";

function openMiniProgram() {
	// developers.weixin.qq.com/miniprogram/dev/devtools/auto/automator.html
	// cliPath 未设置时将会在以下几个位置尝试寻找:
	// Mac:/Applications/wechatwebdevtools.app/Contents/MacOS/cli
	// Win:C:/Program Files (x86)/Tencent/微信web开发者工具/cli.bat
	automator
		.launch({
			// port: 49582,
			cliPath: cliPath, // 工具 cli 位置,如果你没有更改过默认安装位置,可以忽略此项
			// projectPath: `/Users/fangchaoqun/pro/net_hospital_minapp_web/dist/${argv[0]}/mp-weixin`, // 项目文件地址
			projectPath: projectPath, // 项目文件地址
		})
		.then(async (miniProgram) => {
			console.log("启动微信开发者工具中,请耐心等待...");
			const page = await miniProgram.switchTab(`/pages/tabbar/home/home`);
			// await page.waitFor(500);
			// const element = await page.$(".kind-list-item-hd");
			// console.log(await element.attribute("class"));
			// await element.tap();

			// await miniProgram.close();
			console.log("启动微信开发者工具成功");
			process.exit();
		})
		.catch((err) => {
			console.log("微信开发者工具打开异常,请尝试手动打开项目");
			console.error(err);
			process.exit();
		});
}

stat(projectPath, (err, stats) => {
	if (err) {
		console.log("文件夹不存在,即将创建");
		mkdir(projectPath, { recursive: true }, (e) => {
			if (e) throw e;

			openMiniProgram();
		});

		return;
	}

	if (stats.isDirectory()) {
		console.log("文件夹存在,直接打开");
		openMiniProgram();
	}
});

3.2 在package.json中配置命令

{
  "scripts": {
    "predev:mp-weixin": "echo -e '\u001b[32m项目启动后会自动打开微信开发者工具,如果自动打开失败,请手动打开开发者工具\u001b[0m' && node scripts/miniprogram.js dev",
    "dev:mp-weixin": "cross-env NODE_ENV=development UNI_PLATFORM=mp-weixin vue-cli-service uni-build --watch --minimize --BASEURL_ENV=development",
    "predev:mp-weixin:prod": "echo -e '\u001b[32m项目启动后会自动打开微信开发者工具,如果自动打开失败,请手动打开开发者工具\u001b[0m' && node scripts/miniprogram.js dev",
    "dev:mp-weixin:prod": "cross-env NODE_ENV=development UNI_PLATFORM=mp-weixin vue-cli-service uni-build --watch --minimize --BASEURL_ENV=production",

    "build:mp-weixin:dev": "cross-env NODE_ENV=production UNI_PLATFORM=mp-weixin vue-cli-service uni-build --BUILD_ENV=development",
    "postbuild:mp-weixin:dev": "sh scripts/removeRTC.sh && node scripts/miniprogram.js build",
    "build:mp-weixin:prod": "cross-env NODE_ENV=production UNI_PLATFORM=mp-weixin vue-cli-service uni-build --BUILD_ENV=production",
    "postbuild:mp-weixin:prod": "sh scripts/removeRTC.sh && node scripts/miniprogram.js build",
  }
}

3.3 总结

  • 需求是满足了,在运行npm run dev:mp-weixin后,是会自动打开微信开发者工具

  • 但是打开微信开发者工具后,会加载很长时间,初始化那个项目,命令行就一直挂载等待着(项目一旦大了,这个过程真是漫长,还有跟你本地的电脑配置有关),等初始化结束了,才能继续执行后面的任务

3.4 需要优化改进的点

  1. 需不需要自动打开微信开发者工具,命令行中交给开发者选择,默认不需要,直接执行后面的任务

  2. 打开后,需不需要等待微信开发者工具的初始化任务完成,默认不需要,直接执行后面的任务

  3. 总体不够灵活,有时候需要自行打开,有时候不需要,由开发者决定

4. 第二次优化

4.1 改进后的打开微信开发者工具脚本

  • 使用Enquirer增加交互提醒,开发人员可以选择是否自动打开微信开发者工具

  • 并且增加了等待时间,在等待时间内如果开发者不执行操作,将执行默认操作

关于enquirer交互式确认选择,基本使用如下:

const { Confirm } = require('enquirer');

const prompt = new Confirm({
  name: 'question',
  message: 'Want to answer?'
});

prompt.run()
  .then(answer => console.log('Answer:', answer))
  .catch(console.error);

存在的难点

  1. 现在存在的问题是,如何在命令行中弹出问题,让用户选择的同时,还能动态的更改文字内容。一番文档查找,文档中有提供自定义交互式提示

解决Custom prompts

  1. 动态文案解决了,现在还存在一个问题,就是给定等待时间结束后,命令不操作,就一直挂载着,没有继续执行后面的任务,需要开发者敲回车按键后才能继续执行后面任务,所以需要解决

解决enquirer.use,写一个plugin,在给定的时间内,prompt.cancel()自动结束,释放命令行

#!/usr/bin/env node

const Enquirer = require("enquirer");
const {Prompt} = require("enquirer");
const chalk = require("chalk");
// ...

const WAIT_TIME = 5;

function openMiniProgram() {
  // ...
}

// 检查目标文件夹是否存在
function handleDirState () {
	// ...
}

// https://www.npmjs.com/package/enquirer#-custom-prompts
class HaiKarate extends Prompt {
  constructor(options = {}) {
    super(options);
    this.count = options.count || 0;
    // this.cursorHide();
		this.timer = null;
		this._resolve = null;

		this.handleTimer();
  }

	handleTimer() {
		this.timer = setInterval(() => {
			if (this.count === 0) {
				clearInterval(this.timer);
				this.clear();
				this.close();
				// process.exit(0);
			} else {
				this.count--;
				this.render();
			}
		}, 1000);
	}

  render() {
    this.clear(); // clear previously rendered prompt from the terminal
		this.write(`${this.state.message}${this.count}${this.state.message2}`);
  }

	close() {
    super.close();
  }
}

const enquirer = new Enquirer();
const prompt1 = new HaiKarate({
  message: "是否自动打开微信开发者工具?",
	message2: "秒后将自动关闭,后续如需请自行手动打开 y/N",
  name: "answer",
  count: WAIT_TIME,
  type: "confirm",
  initial: false,
});

enquirer.register('haikarate', HaiKarate);

// https://github.com/enquirer/enquirer/issues/273
// 关键在于这里,设置一个plugin给enquirer使用,超过时间,执行prompt.cancel()取消命令行挂载
const timeoutPlugin = (timeout = 30) => enquirer => {
  enquirer.on('prompt', prompt => {
    const t = setTimeout(() => {
			prompt.hint = () => {
				return `${WAIT_TIME}内任务未手动选择${prompt.name === "answer2" ? "Y/n" : "y/N"},已默认执行`;
			};
			prompt.cancel();

			if(prompt.name === "answer2") {
				setTimeout(() => process.exit(0), 1000);
			}
		}, timeout*1000);

    prompt.on('submit', _ => clearTimeout(t));
    prompt.on('cancel', _ => clearTimeout(t));
  });
}
enquirer.use(timeoutPlugin(WAIT_TIME)); // timeout after 5s

async function run() {
	try {
		const r1 = await enquirer.prompt([
			{type: 'haiKarate', ...prompt1.options}
		]);

		if(r1 && r1.answer) {
			prompt1.timer && clearInterval(prompt1.timer);
			prompt1.timer = null;

			console.log(chalk.green("微信开发者工具打开中..."));
			handleDirState();
			const prompt2 = new HaiKarate({
				message: '是否跳过等待微信开发者工具初始化?',
				message2: "秒后将跳过 Y/n",
				name: "answer2",
				count: WAIT_TIME,
				type: 'confirm',
				initial: true,
			});

			const r2 = await enquirer.prompt([
				{type: 'haiKarate', ...prompt2.options}
			]);

			if(r2.answer2) {
				console.log(chalk.greenBright("已跳过等待微信开发者工具初始化,继续执行后面任务"));
				process.exit(0);
			} else {
				prompt2.timer && clearInterval(prompt2.timer);
				prompt2.timer = null;
				console.log(chalk.bgBlue("请耐心等待微信开发者工具初始化完成"));
			}

		} else {
			console.log(chalk.red("已取消,后续请自行手动打开微信开发者工具"));
			process.exit(0);
		}
	} catch (error) {
		console.error(error);
	}
}

run();

4.2 改进后的scripts脚本命令

{
  "scripts": {
    "predev:mp-weixin": "node scripts/miniprogram.js dev",
    "dev:mp-weixin": "cross-env NODE_ENV=development UNI_PLATFORM=mp-weixin vue-cli-service uni-build --watch --minimize --BASEURL_ENV=development",
    "predev:mp-weixin:prod": "node scripts/miniprogram.js dev",
    "dev:mp-weixin:prod": "cross-env NODE_ENV=development UNI_PLATFORM=mp-weixin vue-cli-service uni-build --watch --minimize --BASEURL_ENV=production",
  }
}

4.3 实际测试

4.3.1 第1个交互问题:“是否自动打开微信开发者工具?”

  • 输入y
npm run dev:mp-weixin
> predev:mp-weixin
> node scripts/miniprogram.js dev

platform==> darwin
miniprogram argv==> [ 'dev' ]
✔ 是否自动打开微信开发者工具? (y/N) · true
微信开发者工具打开中...

1y.gif

  • 输入n
npm run dev:mp-weixin
> predev:mp-weixin
> node scripts/miniprogram.js dev

platform==> darwin
miniprogram argv==> [ 'dev' ]
✔ 是否自动打开微信开发者工具? (y/N) · false
已取消,后续请自行手动打开微信开发者工具

1n.gif

  • 默认不选择,让其超时
npm run dev:mp-weixin
> predev:mp-weixin
> node scripts/miniprogram.js dev

platform==> darwin
miniprogram argv==> [ 'dev' ]
✖ 是否自动打开微信开发者工具? (y/N) · false 5内任务未手动选择y/N,已默认执行

1t.gif

4.3.2 第2个交互问题:“是否跳过等待微信开发者工具初始化?”

第1个问题输入了y后,才能进入第2个问题

  • 输入y
npm run dev:mp-weixin

> predev:mp-weixin
> node scripts/miniprogram.js dev

platform==> darwin
miniprogram argv==> [ 'dev' ]
✔ 是否自动打开微信开发者工具? (y/N) · true
微信开发者工具打开中...
? 是否跳过等待微信开发者工具初始化? (Y/n) › 文件夹存在,直接打开
✔ 是否跳过等待微信开发者工具初始化? (Y/n) · true
已跳过等待微信开发者工具初始化,继续执行后面任务

1y-2y.gif

  • 输入n
npm run dev:mp-weixin

> predev:mp-weixin
> node scripts/miniprogram.js dev

platform==> darwin
miniprogram argv==> [ 'dev' ]
✔ 是否自动打开微信开发者工具? (y/N) · true
微信开发者工具打开中...
? 是否跳过等待微信开发者工具初始化? (Y/n) › 文件夹存在,直接打开
✔ 是否跳过等待微信开发者工具初始化? (Y/n) · false
请耐心等待微信开发者工具初始化完成

1y-2n.gif

  • 默认不选择,让其超时
npm run dev:mp-weixin

> predev:mp-weixin
> node scripts/miniprogram.js dev

platform==> darwin
miniprogram argv==> [ 'dev' ]
✔ 是否自动打开微信开发者工具? (y/N) · true
微信开发者工具打开中...
? 是否跳过等待微信开发者工具初始化? (Y/n) › 文件夹存在,直接打开
✖ 是否跳过等待微信开发者工具初始化? (Y/n) · true 5内任务未手动选择Y/n,已默认执行

是否跳过等待微信开发者工具初始化?0秒后将跳过 Y/n

1y-2t.gif

4.3.3 总结

这样就很灵活的解决了上述痛点,再也不需要长时间等待微信开发者初始化卡在那了

5. 参考资料

  1. enquirer官方文档
  2. 小程序自动化文档

6. 最后

如果文章对您有帮助,可以关注我的个人公众号半个柠檬2020,偶尔也会在公众号上面更新一些自己的学习笔记。