使用 TypeScript
因为云函数是 node,所以一般是不支持 TypeScript 的。但是 TypeScript 的类型是真的不错,所以决定使用 TypeScript 进行云开发,而且使用 ts 之后可以直接跟前端使用 interface 进行接口定义,不需要另外写文档。
初期:每个函数有个 src 目录,里面存放 ts 文件,每次进行函数发布之前先使用 tsc 进行编译,然后发布到云环境。这个方法有个缺点就是多个云函数直接相同的代码不能通用,只能拷贝多份分布在每个云函数,这样的话每次这些通用函数的 bug 修复需要更新很多云函数。
后期:把所有的云函数提取到另外的目录,使用 Rollup 进行编译到云函数目录并根据依赖动态生成 package.json 文件。这样的话不同的云函数之间可以使用相同的代码。
编译云函数
首先根据云函数名称确定源代码的入口和输出的文件夹路径
module.exports = async function build(functionName, version) {
// 输出路径
const distPath = path.join(__dirname, `../cloudfunctions/${functionName}`);
// 源文件入口文件
const entryPath = path.join(__dirname, `../cloudfunctions-original/functions/${functionName}/index.ts`);
}
然后使用 rollup 的 typescript 和 commjs 插件把源文件编译成 node输出到指定的云函数目录。使用 alias 插件的原因是因为有部分数据同步的工作需要本地运行,然后我写了个 bridge,所以上传云函数的时候替换成 wx-server-sdk,通过编译的时候把 process.env.run 替换为 cloud 来对部分本地运行代码进行删除。
const bundle = await rollup.rollup({
input: entryPath,
plugins: [
alias({
entries: [
{ find: /^[\s|\S]*bridge\/index$/, replacement: 'wx-server-sdk' },
]
}),
replace({
'process.env.run': JSON.stringify('cloud'),
}),
typescript({
tsconfig: './tsconfig.json',
sourceMap: false,
outDir: distPath,
include: [
"../cloudfunctions-original/**/*.ts"
],
tslib: require.resolve(path.join(__dirname, `../cloudfunctions-original/third-lib/tslib.es6.js`),),
}),
commonjs({
extensions: ['.ts'],
sourceMap: false,
}),
],
onwarn(warning) {
if (warning.code !== 'PLUGIN_WARNING' && warning.code !== 'CANNOT_CALL_NAMESPACE') {
console.warn(warning.message);
}
},
external: [...Object.keys(packageJson.dependencies), 'lodash/fp', 'https', 'fs', 'path'],
treeshake: {
moduleSideEffects: false,
}
});
然后输出到硬盘
const outputOptions = {
format: 'cjs',
interop: false,
dir: distPath,
sourcemap: false,
preserveModules: true,
preserveModulesRoot: entryPath.replace('/index.ts', ''),
exports: 'auto',
banner: `/* 此文件自动生成,请勿手动修改,源文件位于 cloudfunctions-original/functions/${functionName} */`,
}
// generate code and a sourcemap
const result = await bundle.generate(outputOptions);
// write the bundle to disk
await bundle.write(outputOptions);
然后根据依赖输出 package.json
const functionDeps = flow(
flatMap(prop('imports')),
map(v => v === 'lodash/fp' ? 'lodash' : v),
reject(v => includes(`/${functionName}/`)(v)),
)(result.output);
const functionPackageJson = {
name: functionName,
version: version || '1.0.0',
...pick([
'description', 'main', 'author', 'license',
])(packageJson),
dependencies: pick(functionDeps)(packageJson.dependencies),
}
// write the package.json to disk
fs.writeFileSync(`${distPath}/package.json`, JSON.stringify(functionPackageJson, "", "\t"));
创建/更新云函数
上面完成了对单个云函数的打包,接下来我们需要进行批量的 build 和创建/更新云函数。
使用 miniprogram-ci 进行云函数的更新,因为 miniprogram-ci 不支持创建云函数,所以需要开通腾讯云开发,然后使用 @cloudbase/manager-node 进行云函数创建。
首先获取当前环境的所有云函数列表
const manager = new CloudBase({
secretId: process.env.secretId,
secretKey: process.env.secretKey,
envId: process.env.cloudEnv
})
// 因为目前云函数最多 150 个,所以直接指定 150 个
manager.functions.getFunctionList(150)
.then((res) => {
functionList = res.Functions.map(v => v.FunctionName);
console.log(JSON.stringify(functionList));
resolve();
})
.catch((err) => {
console.log('err');
reject(err);
});
然后判断云函数是否存在,如果不存在,则进行创建。创建的时候需要注意,如果有 trigger ,可以添加 trigger 字段
try {
config = require(`${functionDirName}/${filePath}/config.json`);
} catch (error) {}
console.log(`开始创建云函数: ${filePath}`)
await manager.functions.createFunction({
func: {
timeout: 3,
name: filePath,
installDependency: true,
ignore: ['node_modules/'],
triggers: config ? config.triggers : [],
runtime: 'Nodejs10.15',
},
functionPath: `${functionDirName}/${filePath}`,
})
否则,更新云函数
await ci.cloud.uploadFunction({
project: new ci.Project({
appid,
type: 'miniProgram',
projectPath: functionDirName,
privateKeyPath,
ignores: ['node_modules/**/*'],
}),
env: process.env.cloudEnv,
name: filePath,
path: `${functionDirName}/${filePath}`,
remoteNpmInstall: true,
});
批量编译+创建/更新云函数
前面完成了命令式的编译和上传云函数,接下来我们把他们组合起来进行批量处理。
获取所有的云函数名称,因为我们把所有的云函数放到了一个目录,直接读取当前的所有文件夹就可以,然后可以自定义过滤规则。
const dirs = fs.readdirSync(dirName, { withFileTypes: true })
然后对每个函数调用编译+上传。如果函数比较多,可以考虑使用 node 的 cluster 模块进行多进程处理。
使用 jenkins 进行自动上传云函数
前面已经把批量上传云函数写成了 node 可执行文件,接下来就可以使用 jenkins 进行云函数的发布控制。根据参数进行环境配置和上传云函数进行规则过滤。