import { spawn } from 'child_process' import dotenv from 'dotenv' import { existsSync, readFileSync, writeFileSync } from 'fs' import { applyEdits, modify } from 'jsonc-parser' import path from 'path' type TargetEnvironment = 'luoxin' | 'pre' | 'prod' interface EnvironmentConfig { appid: string baseApi: string envFile: string label: string viteMode: string viteModeValue: string } const ROOT_DIR = path.resolve(__dirname, '..') const MANIFEST_PATH = path.resolve(ROOT_DIR, 'src/manifest.json') const UNI_CLI_PATH = path.resolve(ROOT_DIR, 'node_modules/@dcloudio/vite-plugin-uni/bin/uni.js') const BUILD_OUTPUT_DIR = path.resolve(ROOT_DIR, 'dist/build/mp-weixin') const ORIGINAL_APPID = 'wxd03398e1bff2b241' const LUOXIN_APPID = 'wx2a5a7f417981f250' const ENVIRONMENTS: Record = { luoxin: { appid: LUOXIN_APPID, baseApi: 'https://luoxin.yaoyi.net', envFile: '.env.production_LUOXIN', label: '罗欣', viteMode: 'production_LUOXIN', viteModeValue: 'production_luoxin', }, pre: { appid: ORIGINAL_APPID, baseApi: 'https://preapi.yaoyi.net', envFile: '.env.pre', label: '预发布', viteMode: 'pre', viteModeValue: 'production_pre', }, prod: { appid: ORIGINAL_APPID, baseApi: 'https://api.yaoyi.net', envFile: '.env.production', label: '正式', viteMode: 'production', viteModeValue: 'production', }, } const TARGETS: TargetEnvironment[] = ['luoxin', 'pre', 'prod'] const fail = (message: string): never => { throw new Error(message) } const parseTarget = (value: string | undefined): TargetEnvironment => { const target = TARGETS.find((item) => item === value) if (!target) { throw new Error(`环境必须是:${TARGETS.join(' / ')}`) } return target } const loadEnvironment = (target: TargetEnvironment): NodeJS.ProcessEnv => { const config = ENVIRONMENTS[target] const envPath = path.resolve(ROOT_DIR, config.envFile) if (!existsSync(envPath)) { fail(`环境文件不存在:${config.envFile}`) } const parsedEnv = dotenv.parse(readFileSync(envPath, 'utf8')) const expectedValues: Record = { VITE_BASE_API: config.baseApi, VITE_MODE: config.viteModeValue, VITE_WX_APPID: config.appid, } for (const [key, expectedValue] of Object.entries(expectedValues)) { if (parsedEnv[key] !== expectedValue) { fail( `${config.envFile} 中的 ${key} 配置错误:期望 ${expectedValue},实际 ${parsedEnv[key] || '未配置'}` ) } } return { ...process.env, ...parsedEnv, } } const injectManifestAppid = (manifestSource: string, appid: string): string => { const edits = modify(manifestSource, ['mp-weixin', 'appid'], appid, { formattingOptions: { insertSpaces: true, tabSize: 2, }, }) return applyEdits(manifestSource, edits) } const runUniBuild = async ( target: TargetEnvironment, processEnv: NodeJS.ProcessEnv ): Promise => { const config = ENVIRONMENTS[target] await new Promise((resolve, reject) => { const childProcess = spawn( process.execPath, [UNI_CLI_PATH, 'build', '-p', 'mp-weixin', '--mode', config.viteMode], { cwd: ROOT_DIR, env: processEnv, stdio: 'inherit', } ) childProcess.once('error', reject) childProcess.once('exit', (code, signal) => { if (code === 0) { resolve() return } reject( new Error( signal ? `uni-app 进程被信号 ${signal} 终止` : `uni-app 进程退出,错误码:${code}` ) ) }) }) } const validateBuildOutput = (expectedAppid: string): void => { const projectConfigPath = path.resolve(BUILD_OUTPUT_DIR, 'project.config.json') if (!existsSync(projectConfigPath)) { fail(`构建产物不存在:${projectConfigPath}`) } const projectConfig = JSON.parse(readFileSync(projectConfigPath, 'utf8')) as { appid?: string } if (projectConfig.appid !== expectedAppid) { fail(`构建产物 AppID 校验失败:期望 ${expectedAppid},实际 ${projectConfig.appid || '未配置'}`) } } const main = async (): Promise => { const target = parseTarget(process.argv[2]) const config = ENVIRONMENTS[target] const processEnv = loadEnvironment(target) const originalManifest = readFileSync(MANIFEST_PATH, 'utf8') console.log(`\n[环境] ${config.label} (${target})`) console.log(`[接口] ${config.baseApi}`) console.log(`[AppID] ${config.appid}`) writeFileSync(MANIFEST_PATH, injectManifestAppid(originalManifest, config.appid), 'utf8') try { await runUniBuild(target, processEnv) validateBuildOutput(config.appid) console.log(`[构建] 已生成 ${BUILD_OUTPUT_DIR}`) } finally { writeFileSync(MANIFEST_PATH, originalManifest, 'utf8') } } void main().catch((error: unknown) => { const message = error instanceof Error ? error.message : String(error) console.error(`\n[失败] ${message}`) process.exitCode = 1 })