/** * 微信小程序框架包单独编译 → dist/build/mp-core/ */ const fs = require('fs') const path = require('path') const { ROOT, PAGES_JSON, BUILD_DIR, MP_OUT_BUILD, MP_CORE_DIR, MP_FRAMEWORK_DIR, MP_LOCK_PATH, makeBuildId, runUniMpBuild, snapshotMpBuild, collectCommonManifest, readJson, writeJson, stripOrphanAddonSubpackages, installMergeScriptToFramework, applyFrameworkAppJsonSubPackages, loadFrameworkNonAddonSubPackages, rmDir } = require('./mp-utils.cjs') const { patchDiyGroupInlineComponents } = require('./mp-diy-group-inline.cjs') const { installNativeDiyBridgeRouter, buildAppDiyRegistryMap } = require('./mp-diy-bridge-merge.cjs') const { scanAllDiyComponents } = require('./diy-component-scan.cjs') const PAGES_BACKUP = path.join(BUILD_DIR, 'pages.full.backup.json') const PAGES_FRAMEWORK = path.join(BUILD_DIR, 'pages.framework.json') function swapPagesJson(useFramework, rawBackup) { if (useFramework) { // 保存原始内容到内存(保留注释),不依赖磁盘备份文件 rawBackup = fs.readFileSync(PAGES_JSON, 'utf-8') fs.copyFileSync(PAGES_FRAMEWORK, PAGES_JSON) console.log('[build-mp-framework] swapped pages.json -> pages.framework.json') return rawBackup } else if (rawBackup) { fs.writeFileSync(PAGES_JSON, rawBackup, 'utf-8') console.log('[build-mp-framework] restored pages.json (with comments)') } } const UV_BARREL_PAGE_PATH = 'app/pages/_uv_barrel/index' /** * 构建期专用:在主包插入一个临时页面,模板里用上全部 uview-plus 组件, * 强制 uni 把组件文件(node-modules/uview-plus/components/*)与各自 props(NN) * 全部打进框架 common/vendor.js;构建完成后该页面会从 app.json 与产物中移除。 * 这样框架自带完整 uview-plus,插件合并时无需再处理 uview-plus 组件。 */ // 判断某个 pages 条目是否为本脚本注入的临时 barrel 页面(兼容历史遗留的不同路径/格式) function isBarrelPageEntry(p) { const entryPath = typeof p === 'string' ? p : p && p.path return typeof entryPath === 'string' && entryPath.includes('_uv_barrel') } function injectUviewBarrelPage() { if (process.env.SKIP_UV_BARREL) { console.log('[build-mp-framework] SKIP_UV_BARREL set, skip barrel page') return false } const compRoot = path.join(ROOT, 'node_modules', 'uview-plus', 'components') if (!fs.existsSync(compRoot)) { console.warn('[build-mp-framework] uview-plus not found, skip barrel page') return false } const names = fs .readdirSync(compRoot) .filter((n) => { const d = path.join(compRoot, n) if (!fs.statSync(d).isDirectory()) return false if (n.includes('--')) return false // u--xxx 为内部别名标签,双连字符在模板中无法解析 return fs.existsSync(path.join(d, `${n}.vue`)) }) .sort() if (!names.length) return false const tags = names.map((n) => ` <${n} />`).join('\n') const pageVue = `\n\n` + `\n` const srcDir = path.join(ROOT, 'src', 'app', 'pages', '_uv_barrel') fs.mkdirSync(srcDir, { recursive: true }) fs.writeFileSync(path.join(srcDir, 'index.vue'), pageVue, 'utf-8') const fw = readJson(PAGES_FRAMEWORK) if (!Array.isArray(fw.pages)) fw.pages = [] // 先清除任何历史残留的 barrel 条目(可能格式/路径不同),避免重复或非法 page fw.pages = fw.pages.filter((p) => !isBarrelPageEntry(p)) fw.pages.push({ path: UV_BARREL_PAGE_PATH, style: {} }) writeJson(PAGES_FRAMEWORK, fw) console.log(`[build-mp-framework] injected uview barrel page (${names.length} components)`) return true } /** 从 mp-core/app.json 移除临时页面入口并删除其编译产物文件 */ function stripUviewBarrelFromCore() { const appPath = path.join(MP_CORE_DIR, 'app.json') if (fs.existsSync(appPath)) { const appJson = readJson(appPath) if (Array.isArray(appJson.pages)) { const len = appJson.pages.length appJson.pages = appJson.pages.filter((p) => !isBarrelPageEntry(p)) if (appJson.pages.length !== len) { writeJson(appPath, appJson) console.log('[build-mp-framework] removed barrel page entry from app.json') } } } const outDirs = [ path.join(MP_CORE_DIR, 'app', 'pages', '_uv_barrel'), path.join(MP_CORE_DIR, 'pages', '_uv_barrel'), path.join(MP_OUT_BUILD, 'app', 'pages', '_uv_barrel'), path.join(MP_OUT_BUILD, 'pages', '_uv_barrel') ] outDirs.forEach((outDir) => { if (fs.existsSync(outDir)) { rmDir(outDir) console.log('[build-mp-framework] removed barrel page files from mp-core:', outDir) } }) } /** 清理构建期临时文件:src 临时页面 + pages.framework.json 中的注入项 */ function removeUviewBarrelTemp() { const srcDirs = [ path.join(ROOT, 'src', 'app', 'pages', '_uv_barrel'), path.join(ROOT, 'src', 'pages', '_uv_barrel') ] srcDirs.forEach((srcDir) => { if (fs.existsSync(srcDir)) rmDir(srcDir) }) if (fs.existsSync(PAGES_FRAMEWORK)) { const fw = readJson(PAGES_FRAMEWORK) if (Array.isArray(fw.pages)) { const len = fw.pages.length fw.pages = fw.pages.filter((p) => !isBarrelPageEntry(p)) if (fw.pages.length !== len) writeJson(PAGES_FRAMEWORK, fw) } } } function main() { const buildId = makeBuildId('mp-framework') require('./generate-mp-framework-pages.cjs') require('./generate-diy-registry.cjs').generateDiyRegistry() const { generateDiyDynamicBridgeMp } = require('./generate-diy-dynamic-bridge-mp.cjs') generateDiyDynamicBridgeMp({ target: 'app' }) generateDiyDynamicBridgeMp({ target: 'router' }) injectUviewBarrelPage() const pagesBackup = swapPagesJson(true) let code = 0 let buildOk = false try { console.log('[build-mp-framework] uni build -p mp-weixin ...') code = runUniMpBuild() if (code !== 0) { console.error('[build-mp-framework] FAILED') process.exit(code) } if (!fs.existsSync(MP_OUT_BUILD)) { console.error('[build-mp-framework] missing output:', MP_OUT_BUILD) process.exit(1) } const stripped = stripOrphanAddonSubpackages(MP_OUT_BUILD) if (stripped) { console.log(`[build-mp-framework] removed ${stripped} orphan addon plugin dir(s)`) } snapshotMpBuild(MP_OUT_BUILD, MP_CORE_DIR, { kind: 'framework', buildId, commonManifest: collectCommonManifest(MP_OUT_BUILD) }) applyFrameworkAppJsonSubPackages(MP_CORE_DIR) stripUviewBarrelFromCore() writeJson( path.join(MP_CORE_DIR, 'mp-framework-subpackages.json'), loadFrameworkNonAddonSubPackages() ) installNativeDiyBridgeRouter(MP_CORE_DIR, buildAppDiyRegistryMap(scanAllDiyComponents)) console.log('[build-mp-framework] installed native diy-dynamic-bridge router (merge-patchable)') const appRegistry = buildAppDiyRegistryMap(scanAllDiyComponents) const inlineResult = patchDiyGroupInlineComponents(MP_CORE_DIR, appRegistry) console.log( `[build-mp-framework] diy-group inlined ${inlineResult.count} app diy component(s) (no bridge hop)` ) const { applyMpWeixinPost } = require('./mp-weixin-post.cjs') applyMpWeixinPost(MP_CORE_DIR) const meta = readJson(path.join(MP_CORE_DIR, 'mp-build-meta.json')) const lock = fs.existsSync(MP_LOCK_PATH) ? readJson(MP_LOCK_PATH) : {} lock.framework = { buildId, gitCommit: meta.gitCommit, time: new Date().toISOString(), dir: 'dist/build/mp-core' } writeJson(MP_LOCK_PATH, lock) installMergeScriptToFramework(MP_CORE_DIR) console.log(`[build-mp-framework] OK -> ${path.relative(ROOT, MP_CORE_DIR)}/ (${buildId})`) console.log('[build-mp-framework] 框架包可直接导入开发者工具(仅主包,无插件分包)') console.log('[build-mp-framework] 添加插件后合并:') console.log(' 1. 将插件包放入 dist/build/mp-core/mp-addons/{key}/') console.log(' 或 npm run build:mp-weixin:addon shop (自动输出到 mp-addons/shop/)') console.log(' 2. cd dist/build/mp-core && node merge-mp-addon.cjs') buildOk = true } finally { swapPagesJson(false, pagesBackup) removeUviewBarrelTemp() if (buildOk) { require('./cleanup-diy-dynamic-bridge-src.cjs').cleanupAddonDiyBridgeSrc() } } } main()