因为开发环境上线比较多,顾虑手动修改代码过程中错误修改,导致上线后使用错误的环境,因此使用此方式
运行命令
编译文件目录 build/build.js
设定编辑器所在目录、运行环境
"build": "node build/build.js"
设定编辑器所在目录、运行环境,小程序代码上传
"build:upload": "node build/build.js upload"
设定编辑器所在目录、运行环境,小程序代码上传,版本号写入
"build:upload:version": "node build/build.js upload version"
这里也可以仅仅有第一条命令,就需要一段可以选择编译功能的代码,推荐使用 inquirer
代码分解
文件说明
包含两个文件,build.js buildConfig.js。
build.js:编译主文件
buildConfig.js:编译配置文件,包含开发者工具安装目录和当前环境两个变量。因为在小程序内部没有找到读取json文件的方法,所以配置文件使用.js文件
获取开发者工具目录
因为每个人的安装目录都是不一样的,默认的安装目录为空。第一次运行需要指定目录,并把指定的目录写入 buildConfig.js 文件,假如目录不正确就需要手动删掉,重新运行命令;配置成功后再次进入就不需要再次指定。
buildConfig.js 文件默认是不存在的,需要 try catch 包裹起来,在执行 writeFileSync 的时候没有文件会自动创建文件
async function initBuildConfig () {
try{
const _buildConfig = require('./buildConfig.js') || {}
buildConfig.toolsPath = _buildConfig.toolsPath || ''
buildConfig.env = _buildConfig.env || ''
} catch (e) {
}
}
function getToolsPath () {
return new Promise((resolve, reject) => {
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
rl.question('当前未设置开发者工具安装路径,请设置:\n\t -> ', (path) => {
rl.close();
fs.writeFileSync(
`${__dirname}/buildConfig.js`,
`module.exports = ${JSON.stringify(Object.assign(buildConfig, {toolsPath: path}), false, 2)}`,
)
console.log('\n')
resolve(path)
});
})
}
选择编译环境
使用 inquirer完成环境的选择,并写入 buildConfig.js 文件,下次进入会默认指定上一次选择的环境
当其他文件需要使用环境变量是只需要引用buildConfig.js,并使用env变量即可
async function selectEnv() {
const{env} = await inquirer.prompt({
type: 'list',
name: 'env',
message: '当前编译环境:\n\t ->',
default: buildConfig.env,
choices: ['local', 'qa', 'test', 'v4', 'v8'],
})
fs.writeFileSync(
`${__dirname}/buildConfig.js`,
`module.exports = ${JSON.stringify(Object.assign(buildConfig, {env}), false, 2)}`,
)
return env
}
小程序代码上传
使用命令模式的初衷是为了不打开微信开发者工具,事实证明我想的太理想了,还是需要打开的。
执行命令的返回值 stdout 在执行成功的时候才有值,stderr无论成功与否都有值,所以这里使用 stdout 判断命令执行失败。
function commitCode (env) {
return new Promise((resolve, reject) => {
const version = packageConfig.version
const projectPath = path.resolve(__dirname, '../')
console.log("\n项目路径:\n\t ->", projectPath, '\n')
const toolsPath = buildConfig.toolsPath
const command = `cli ${[
'upload',
'--project',
projectPath,
'-v',
version,
'-d',
`${env}-${version}`
].join(' ')}`
console.log("小程序上传代码命令:\n\t ->", command, '\n')
exec(command, {cwd: toolsPath}, (error, stdout, stderr) => {
if (error) {
console.error(`执行的错误: ${error}`)
reject(error)
return;
}
if (!stdout) {
console.error(`执行的错误: ${stderr}`)
reject(stderr)
return;
}
console.log(`stdout: ${stdout}`);
resolve()
})
})
}
获取上传版本号
若果没有版本号则指定默认版本号1.0.0
有版本号则根据 版本升级、特性更新、修订补丁 的选择修改对应位置的数字,这里没有对版本号做过多的校验,以为错误的版本号是会报错的,没报错就是系统允许的。
async function getVersion () {
if (!packageConfig.version) {
const version = '1.0.0'
console.log("\n当前无版本号,使用默认版本号:\n\t ->", version)
packageConfig.version = version
return version
}
console.log("\n当前版本号:\n\t ->", packageConfig.version, '\n')
const versionTypeOpts = ['版本升级', '特性更新', '修订补丁']
const {versionType} = await inquirer.prompt({
type: 'list',
name: 'versionType',
message: '选择需要更新的版本类型:\n\t ->',
default: '修订补丁',
choices: versionTypeOpts,
})
const index = versionTypeOpts.findIndex(item => item === versionType)
const version = packageConfig.version.replace(/(.+)\.(.+)\.(.+)/, (...args) => {
const _version = args.slice(1,4)
_version[index] = Number(_version[index] || 0) + 1
return _version.join('.')
})
packageConfig.version = version
return version
}
未来版本是选择版本号+自定义版本号,比如当前是1.0.0给出如下几种方案进行选择:
- 1.0.0
- 1.0.1
- 1.1.0
- 2.0.0
- 自定义
选择自定义需要开发者自己手动输入,需要分三段输入,每次输入都要第一段校验正整数,后面两段检验是非负整数,并写入package.json
这种傻瓜式配置更能让人理解,而且扩展能力也比较好
本地写入版本号
写入 package.json 的 version。
function setVersion () {
if (process.argv[3] !== 'version') return
console.log("版本号写入:\n\t ->", packageConfig.version, '\n')
fs.writeFileSync(
path.resolve(__dirname, '../package.json'),
JSON.stringify(packageConfig, false, 2),
)
}
代码合集
const readline = require('readline');
const buildConfig = require('./buildConfig.js')
const packageConfig = require('../package.json')
const { exec } = require('child_process')
const fs = require('fs')
const path = require('path')
var inquirer = require('inquirer')
init()
async function init () {
// console.log(Object.prototype.toString.call(buildConfig))
console.log("当前编译配置:\n\t ->", buildConfig, '\n')
// console.log('__dirname : ' + __dirname)
if (!buildConfig.toolsPath) {
await getToolsPath()
}
console.log("开发者工具安装路径:\n\t ->", buildConfig.toolsPath, '\n')
const env = await selectEnv()
if (process.argv[2] !== 'upload') return
await getVersion()
await commitCode(env)
await setVersion()
console.log("上传成功:\n\t ->", env, '\n')
}
/**
* 获取开发者工具目录
*/
function getToolsPath () {
return new Promise((resolve, reject) => {
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
rl.question('当前未设置开发者工具安装路径,请设置:\n\t -> ', (path) => {
rl.close();
fs.writeFileSync(
`${__dirname}/buildConfig.js`,
`module.exports = ${JSON.stringify(Object.assign(buildConfig, {toolsPath: path}), false, 2)}`,
)
console.log('\n')
resolve(path)
});
})
}
/**
* 选择编译环境
*/
async function selectEnv() {
const{env} = await inquirer.prompt({
type: 'list',
name: 'env',
message: '当前编译环境:\n\t ->',
default: buildConfig.env,
choices: ['local', 'qa', 'test', 'v4', 'v8'],
})
fs.writeFileSync(
`${__dirname}/buildConfig.js`,
`module.exports = ${JSON.stringify(Object.assign(buildConfig, {env}), false, 2)}`,
)
return env
}
/**
* 小程序代码上传
*
* @param {String} env 环境
*/
function commitCode (env) {
return new Promise((resolve, reject) => {
const version = packageConfig.version
const projectPath = path.resolve(__dirname, '../')
console.log("\n项目路径:\n\t ->", projectPath, '\n')
const toolsPath = buildConfig.toolsPath
const command = `cli ${[
'upload',
'--project',
projectPath,
'-v',
version,
'-d',
`${env}-${version}`
].join(' ')}`
console.log("小程序上传代码命令:\n\t ->", command, '\n')
// const ls = execSync(command, {
// cwd: toolsPath,
// })
// console.log(ls, ls.toString())
// console.log(Object.prototype.toString.call(ls))
// console.log(ls.pid, ls.output, ls.stdout)
exec(command, {cwd: toolsPath}, (error, stdout, stderr) => {
// console.log(`error: ${error}`);
// console.log(`stdout: ${stdout}`);
// console.error(`stderr: ${stderr}`);
if (error) {
console.error(`执行的错误: ${error}`)
reject(error)
return;
}
if (!stdout) {
console.error(`执行的错误: ${stderr}`)
reject(stderr)
return;
}
console.log(`stdout: ${stdout}`);
resolve()
})
})
}
/**
* 获取上传版本号
*
* 开发者选择需要更新的版本号
*
* 未来版本是选择版本号+自定义版本号,比如当前是1.0.0给出如下几种方案进行选择:
* 1.0.0
* 1.0.1
* 1.1.0
* 2.0.0
* 自定义
*
* 选择自定义需要开发者自己手动输入,需要分三段输入,每次输入都要第一段校验正整数,后面两段检验是非负整数,并写入package.json
*
* 这种傻瓜式配置更能让人理解,而且扩展能力也比较好
*/
async function getVersion () {
if (!packageConfig.version) {
const version = '1.0.0'
console.log("\n当前无版本号,使用默认版本号:\n\t ->", version)
packageConfig.version = version
return version
}
console.log("\n当前版本号:\n\t ->", packageConfig.version, '\n')
const versionTypeOpts = ['版本升级', '特性更新', '修订补丁']
const {versionType} = await inquirer.prompt({
type: 'list',
name: 'versionType',
message: '选择需要更新的版本类型:\n\t ->',
default: '修订补丁',
choices: versionTypeOpts,
})
const index = versionTypeOpts.findIndex(item => item === versionType)
const version = packageConfig.version.replace(/(.+)\.(.+)\.(.+)/, (...args) => {
const _version = args.slice(1,4)
_version[index] = Number(_version[index] || 0) + 1
return _version.join('.')
})
packageConfig.version = version
return version
}
/**
* 本地写入版本号
*
* 这里没有考虑使用版本号来区别是为了更好的灵活性
*/
function setVersion () {
if (process.argv[3] !== 'version') return
console.log("版本号写入:\n\t ->", packageConfig.version, '\n')
fs.writeFileSync(
path.resolve(__dirname, '../package.json'),
JSON.stringify(packageConfig, false, 2),
)
}
注意
各个项目的环境名称各不相同,需要根据需求自己修改。
buildConfig.js 默认配置成安装目录为空,默认环境为线上环境
如果担心文件bei修改就加入.gitignore禁止上传
参考
https://developers.weixin.qq.com/miniprogram/dev/devtools/cli.html
https://developers.weixin.qq.com/miniprogram/dev/devtools/http.html
http://nodejs.cn/api/readline.html#readline_readline_createinterface_options
http://nodejs.cn/api/child_process.html#child_process_child_process_exec_command_options_callback
http://nodejs.cn/api/fs.html#fs_fs_writefilesync_file_data_options
http://nodejs.cn/api/path.html#path_path_relative_from_to
https://github.com/SBoudrias/Inquirer.js