#!/usr/bin/env node /** * fix-wechat-miniapp.js * * 该脚本用于修复微信小程序的一些问题: * 1. 将项目根目录下的 site.js 文件拷贝到 unpackage/dist/build/mp-weixin 目录下,如果文件存在则覆盖 * 2. 将 project.config.json 及 project.private.config.json 拷贝到目标目录 * 3. 生成 version.json(包含 Git 提交 ID 和构建时间)到目标目录 * 4. 将 `const site = require("../site");` 和 `const __app_version = require("../version.json");` * 追加到 unpackage/dist/build/mp-weixin/common/vendor.js 文件内容开头部分, * 如果文件开头已有则跳过 * * 使用: * node fix-wechat-miniapp.js # 打补丁并创建 ZIP 文件(默认 mode=production) * node fix-wechat-miniapp.js --no-zip # 只打补丁,不创建 ZIP 文件 * node fix-wechat-miniapp.js --mode development # 使用 development 模式打补丁 * node fix-wechat-miniapp.js --mode production # 使用 production 模式打补丁(默认) * * 注意: * - 在 Windows 上路径使用反斜杠也是可以的;脚本使用 path.join 来兼容不同平台。 * - 运行前请确认在项目根目录运行(site.js 在当前工作目录下)。 */ const fs = require('fs'); const fsp = fs.promises; const path = require('path'); const { execSync } = require('child_process'); const { openFileDirectory } = require('./open-directory'); const { createZipWithSystemCommand } = require('./create-zip'); async function commonPatch(mode = 'production') { try { // 根据当前脚本所在目录(scripts),定位到项目根目录 const cwd = path.join(__dirname, '..'); const srcSitePath = path.join(cwd, 'site.js'); const destDir = path.join(cwd, 'unpackage', 'dist', mode === 'production' ? 'build' : 'dev', 'mp-weixin'); const destSitePath = path.join(destDir, 'site.js'); const vendorPath = path.join(destDir, 'common', 'vendor.js'); // 1) 检查源文件是否存在 const srcExists = await exists(srcSitePath); if (!srcExists) { console.error(`源文件不存在: ${srcSitePath}`); process.exitCode = 2; return; } // 确保目标目录存在 await ensureDir(destDir); // 复制 project.config.json 及 project.private.config.json 文件到 destDir 下面 const configFiles = ['project.config.json', 'project.private.config.json']; for (const fileName of configFiles) { const srcPath = path.join(cwd, fileName); const destPath = path.join(destDir, fileName); // 检查源文件是否存在 const fileExists = await exists(srcPath); if (fileExists) { await fsp.copyFile(srcPath, destPath); console.log(`已拷贝: ${srcPath} -> ${destPath}`); } else { console.warn(`源文件不存在,跳过复制: ${srcPath}`); } } // 复制 site.js 到目标目录(覆盖) await fsp.copyFile(srcSitePath, destSitePath); console.log(`已拷贝: ${srcSitePath} -> ${destSitePath}`); // 3) 生成 version.json(包含 Git 提交 ID 和构建时间) let commitId = 'unknown'; try { commitId = execSync('git rev-parse --short HEAD', { cwd }).toString().trim(); } catch (e) { console.warn('无法获取 Git 提交 ID,使用 unknown 代替'); } const versionJson = { commitId, buildTime: new Date().toISOString() }; const versionPath = path.join(destDir, 'version.json'); await fsp.writeFile(versionPath, JSON.stringify(versionJson, null, 2)); console.log(`已生成: ${versionPath} (commitId: ${commitId})`); // 4) 处理 vendor.js:在开头追加 require 语句(如果还没有的话) const vendorExists = await exists(vendorPath); if (!vendorExists) { console.warn(`⚠ 目标 vendor.js 不存在,跳过追加 require。路径: ${vendorPath}`); console.warn(' → 请先执行 HBuilder 构建(uniapp 构建),产生 vendor.js 后再运行此脚本。'); console.warn(' → version.json 已生成,可在首页连击 5 次标题查看版本信息。'); return; } let vendorContent = await fsp.readFile(vendorPath, 'utf8'); const requireLine = 'const site = require("../site");'; const versionLine = 'const __app_version = require("../version.json");'; // 检查文件开头(例如前 1000 字符)是否已经包含这些 require 语句 const inspectPrefix = vendorContent.slice(0, 1000); const hasSiteRequire = new RegExp( '^\\s*const\\s+site\\s*=\\s*require\\s*\\([\'"]\\.\\./site[\'"]\\);?', 'm' ).test(inspectPrefix); const hasVersionRequire = new RegExp( '^\\s*const\\s+__app_version\\s*=\\s*require\\s*\\([\'"]\\.\\./version\\.json[\'"]\\);?', 'm' ).test(inspectPrefix); if (hasSiteRequire && hasVersionRequire) { console.log('vendor.js 开头已包含 require 语句,跳过追加。'); return; } // 把 require 语句 + newline 插入文件最前面。 // 保留原始换行风格(若可能) const eol = vendorContent.includes('\r\n') ? '\r\n' : '\n'; let prefix = ''; // 保留可能的 BOM if (vendorContent.charCodeAt(0) === 0xfeff) { prefix = '\uFEFF'; vendorContent = vendorContent.slice(1); } const linesToInject = [requireLine]; if (!hasVersionRequire) { linesToInject.push(versionLine); } const newContent = prefix + linesToInject.join(eol) + eol + vendorContent; await fsp.writeFile(vendorPath, newContent, 'utf8'); console.log(`已在 vendor.js 开头追加: ${linesToInject.join(', ')}`); } catch (err) { console.error('发生错误:', err); process.exitCode = 1; } } async function main() { // 解析命令行参数 const argv = process.argv.slice(2); const options = { noZip: argv.includes('--no-zip'), mode: 'production' // 默认值 }; // 解析 --mode 参数 const modeIndex = argv.indexOf('--mode'); if (modeIndex !== -1 && modeIndex + 1 < argv.length) { options.mode = argv[modeIndex + 1]; } // 1) 打补丁 await commonPatch(options.mode); // await commonPatch('development'); // 2) 创建 ZIP 文件(如果未指定 --no-zip) if (!options.noZip) { const cwd = path.join(__dirname, '..'); const buildDir = options.mode === 'production' ? 'build' : 'dev'; const sourceDir = path.join(cwd, 'unpackage', 'dist', buildDir, 'mp-weixin'); const destDir = path.join(cwd, 'unpackage', 'dist', buildDir); const zipFilePath = await createZipWithSystemCommand(sourceDir, destDir); console.log(`ZIP 文件路径: ${zipFilePath}`); // 3) 自动打开zip所在的目录 await openFileDirectory(zipFilePath); } else { console.log('跳过创建 ZIP 文件和打开目录'); } } async function exists(p) { try { await fsp.access(p, fs.constants.F_OK); return true; } catch (e) { return false; } } async function ensureDir(dirPath) { try { await fsp.mkdir(dirPath, { recursive: true }); } catch (e) { // ignore if exists if (e.code !== 'EEXIST') throw e; } } main();