mirror of
https://gitee.com/niucloud-team/niucloud.git
synced 2026-07-19 02:37:43 +00:00
296 lines
10 KiB
JavaScript
296 lines
10 KiB
JavaScript
/**
|
||
* addon 构建产物中的 uni-h5 副本使用独立路由 / App 运行时,
|
||
* getApp()、getCurrentPages()、navigateTo 等与 Core 不一致。
|
||
* 将关键 API 委托给 Core 已挂载的 globalThis.*。
|
||
*/
|
||
const fs = require('fs')
|
||
const path = require('path')
|
||
const { ROOT } = require('./addon-utils.cjs')
|
||
|
||
const DELEGATE_PREFIX = `function __wapDelegateUniApi(name){
|
||
return function(arg){
|
||
var u=globalThis.uni;
|
||
if(u&&typeof u[name]==="function")return u[name](arg);
|
||
return Promise.reject({errMsg:name+":fail page not found"});
|
||
};
|
||
}
|
||
function __wapGetCurrentPages(){
|
||
var fn=globalThis.getCurrentPages;
|
||
if(typeof fn==="function"){try{return fn()}catch(e){}}
|
||
return [];
|
||
}
|
||
var __wapNavigateTo=__wapDelegateUniApi("navigateTo");
|
||
var __wapRedirectTo=__wapDelegateUniApi("redirectTo");
|
||
var __wapSwitchTab=__wapDelegateUniApi("switchTab");
|
||
var __wapReLaunch=__wapDelegateUniApi("reLaunch");
|
||
var __wapNavigateBack=__wapDelegateUniApi("navigateBack");
|
||
var __wapSetNavigationBarTitle=__wapDelegateUniApi("setNavigationBarTitle");
|
||
var __wapPageScrollTo=__wapDelegateUniApi("pageScrollTo");
|
||
var __wapSetLocale=__wapDelegateUniApi("setLocale");
|
||
`
|
||
|
||
const NAV_EXPORT_APIS = [
|
||
['navigateTo', '__wapNavigateTo'],
|
||
['redirectTo', '__wapRedirectTo'],
|
||
['switchTab', '__wapSwitchTab'],
|
||
['reLaunch', '__wapReLaunch'],
|
||
['navigateBack', '__wapNavigateBack'],
|
||
['setNavigationBarTitle', '__wapSetNavigationBarTitle'],
|
||
['pageScrollTo', '__wapPageScrollTo'],
|
||
['getCurrentPages$1', '__wapGetCurrentPages']
|
||
]
|
||
|
||
const PATCHED_MARK = '/* __wap-addon-uni-h5-patched */'
|
||
|
||
const GET_CURRENT_PAGES_DELEGATE = `function getCurrentPages$1() {
|
||
var ext = globalThis.getCurrentPages;
|
||
if (typeof ext === "function") {
|
||
try {
|
||
var extPages = ext();
|
||
if (extPages) return extPages;
|
||
} catch (e) {
|
||
}
|
||
}
|
||
const curPages = [];`
|
||
|
||
function fixBrokenDelegateOrder(code) {
|
||
if (!code.startsWith('function __wapDelegateUniApi')) return code
|
||
const importStart = code.indexOf('import {')
|
||
if (importStart === -1) return code
|
||
const importEnd = code.indexOf('\n', importStart) + 1
|
||
const delegateBlock = code.slice(0, importStart)
|
||
return code.slice(importStart, importEnd) + delegateBlock + code.slice(importEnd)
|
||
}
|
||
|
||
function insertDelegateAfterImports(code) {
|
||
if (code.includes(PATCHED_MARK)) return code
|
||
let end = 0
|
||
while (true) {
|
||
const slice = code.slice(end)
|
||
if (!slice.startsWith('import ')) break
|
||
const lineEnd = code.indexOf('\n', end)
|
||
if (lineEnd === -1) break
|
||
end = lineEnd + 1
|
||
}
|
||
return code.slice(0, end) + PATCHED_MARK + '\n' + DELEGATE_PREFIX + code.slice(end)
|
||
}
|
||
|
||
function patchGetAppFunction(code) {
|
||
if (code.includes('globalThis.getApp')) return code
|
||
return code.replace(
|
||
/function getApp\$1\(\)\s*\{\s*return appVm;\s*\}/,
|
||
`function getApp$1() {
|
||
var fn = globalThis.getApp;
|
||
if (typeof fn === "function") {
|
||
try {
|
||
var app = fn();
|
||
if (app) return app;
|
||
} catch (e) {
|
||
}
|
||
}
|
||
return appVm;
|
||
}`
|
||
)
|
||
}
|
||
|
||
/** 修复已错误注入 var pages 与下方 const pages 冲突 */
|
||
function fixDuplicatePagesVar(code) {
|
||
return code.replace(
|
||
/var pages = ext\(\);\s*\n\s*if \(pages\) return pages;/g,
|
||
'var extPages = ext();\n if (extPages) return extPages;'
|
||
)
|
||
}
|
||
|
||
function patchGetCurrentPagesFunction(code) {
|
||
code = fixDuplicatePagesVar(code)
|
||
// DELEGATE_PREFIX 已含 globalThis.getCurrentPages,不能据此跳过 getCurrentPages$1 本体补丁
|
||
if (code.includes('var extPages = ext()')) return code
|
||
return code.replace(
|
||
/function getCurrentPages\$1\(\)\s*\{\s*const curPages = \[\];/,
|
||
GET_CURRENT_PAGES_DELEGATE
|
||
)
|
||
}
|
||
|
||
function coreRouterResolve(getAppFn) {
|
||
return `(function(){var g=globalThis;if(typeof g.getApp==="function"){try{var ga=g.getApp();if(ga&&ga.$router)return ga.$router}catch(e){}}var a=${getAppFn}();return a&&a.$router})()`
|
||
}
|
||
|
||
/** uni-h5 代码拆分到 resize-sensor 等 chunk 时,须统一走 Core 的 getApp().$router */
|
||
function patchCoreRouterAccess(code) {
|
||
if (!code.includes('$router')) return code
|
||
|
||
let next = code
|
||
next = next.replace(
|
||
/const router\s*=\s*(getApp(?:\$\d+)?)\(\)\.\$router;/g,
|
||
(_, fn) => `const router = ${coreRouterResolve(fn)};`
|
||
)
|
||
next = next.replace(
|
||
/(getApp(?:\$\d+)?)\(\)\.\$router\.go\(/g,
|
||
(_, fn) => `${coreRouterResolve(fn)}.go(`
|
||
)
|
||
next = next.replace(
|
||
/\(function\(\)\{var a=(getApp(?:\$\d+)?)\(\);return a&&a\.\$router\}\)\(\)/g,
|
||
(_, fn) => coreRouterResolve(fn)
|
||
)
|
||
return next
|
||
}
|
||
|
||
function patchUnsafeGetAppAccess(code) {
|
||
return code
|
||
.replace(/const appVm2 = getApp\$1\(\)\.\$vm;/g, 'const _wapApp = getApp$1(); const appVm2 = _wapApp && _wapApp.$vm;')
|
||
.replace(/(getApp(?:\$\d+)?)\(\)\.\$router/g, (_, fn) => coreRouterResolve(fn))
|
||
}
|
||
|
||
function patchNavExports(code) {
|
||
let next = code
|
||
for (const [from, to] of NAV_EXPORT_APIS) {
|
||
const re = new RegExp(`\\b${from.replace(/\$/g, '\\$')} as ([A-Za-z0-9_$]+)`, 'g')
|
||
next = next.replace(re, `${to} as $1`)
|
||
}
|
||
return next
|
||
}
|
||
|
||
function patchUniH5File(filePath) {
|
||
let code = fs.readFileSync(filePath, 'utf-8')
|
||
if (!code.includes('function getApp$1()') && !code.includes('export {')) return false
|
||
|
||
code = fixBrokenDelegateOrder(code)
|
||
const before = code
|
||
|
||
if (!code.includes('function __wapDelegateUniApi')) {
|
||
code = insertDelegateAfterImports(code)
|
||
}
|
||
code = patchGetAppFunction(code)
|
||
code = patchGetCurrentPagesFunction(code)
|
||
code = patchCoreRouterAccess(code)
|
||
code = patchUnsafeGetAppAccess(code)
|
||
if (code.includes('export {')) {
|
||
code = patchNavExports(code)
|
||
}
|
||
|
||
if (code === before) return false
|
||
fs.writeFileSync(filePath, code, 'utf-8')
|
||
return true
|
||
}
|
||
|
||
function patchAddonAssetChunk(filePath) {
|
||
let code = fs.readFileSync(filePath, 'utf-8')
|
||
if (!code.includes('$router') && !code.includes('function getApp$1()')) return false
|
||
|
||
const before = code
|
||
code = patchCoreRouterAccess(code)
|
||
code = patchGetAppFunction(code)
|
||
code = patchUnsafeGetAppAccess(code)
|
||
|
||
if (code === before) return false
|
||
fs.writeFileSync(filePath, code, 'utf-8')
|
||
return true
|
||
}
|
||
|
||
function patchExportHelper(filePath) {
|
||
let code = fs.readFileSync(filePath, 'utf-8')
|
||
if (code.includes('__wapExportHelperPatched')) return false
|
||
|
||
// 直接覆盖 navigateTo/redirectTo/reLaunch 局部变量(不能用 exports,ESM 不存在)
|
||
const navDelegate = [
|
||
'',
|
||
'/* __wapExportHelperPatched */',
|
||
'(function(){',
|
||
'var re=new RegExp("^\\\\/?addon\\\\/([^/]+)\\\\/pages\\\\/(.+)");',
|
||
'var _wapOrigNavTo=navigateTo,_wapOrigRedir=redirectTo,_wapOrigReLaunch=reLaunch;',
|
||
'navigateTo=function(args){',
|
||
'var p=args&&args.url||"";var m=p.match(re);',
|
||
'if(m&&typeof getApp==="function"){',
|
||
'try{var r=getApp().$router;if(r){',
|
||
'var q=m[0].startsWith("/")?p:"/"+p;var rs=r.resolve(q);',
|
||
'if(rs&&rs.matched&&rs.matched.length===0){',
|
||
'args=Object.assign({},args,{url:"/app/pages/addon-universal/index?addon="+m[1]+"&page=pages/"+m[2]})',
|
||
'}}}catch(e){}}',
|
||
'return _wapOrigNavTo.call(this,args)};',
|
||
'redirectTo=function(args){',
|
||
'var p=args&&args.url||"";var m=p.match(re);',
|
||
'if(m){args=Object.assign({},args,{url:"/app/pages/addon-universal/index?addon="+m[1]+"&page=pages/"+m[2]})}',
|
||
'return _wapOrigRedir.call(this,args)};',
|
||
'reLaunch=function(args){',
|
||
'var p=args&&args.url||"";var m=p.match(re);',
|
||
'if(m){args=Object.assign({},args,{url:"/app/pages/addon-universal/index?addon="+m[1]+"&page=pages/"+m[2]})}',
|
||
'return _wapOrigReLaunch.call(this,args)};',
|
||
'})();',
|
||
''
|
||
].join('\n');
|
||
|
||
fs.writeFileSync(filePath, code + navDelegate, 'utf-8')
|
||
return true
|
||
}
|
||
|
||
function patchAddonDir(addonDir) {
|
||
const assetsDir = path.join(addonDir, 'assets')
|
||
if (!fs.existsSync(assetsDir)) return 0
|
||
|
||
let count = 0
|
||
for (const name of fs.readdirSync(assetsDir)) {
|
||
if (!name.endsWith('.js')) continue
|
||
const filePath = path.join(assetsDir, name)
|
||
let patched = false
|
||
if (/^uni-h5\.es\.[^.]+\.js$/.test(name)) {
|
||
patched = patchUniH5File(filePath)
|
||
} else {
|
||
patched = patchAddonAssetChunk(filePath)
|
||
}
|
||
if (patched) {
|
||
console.log(`[patch-addon-uni-h5] patched ${path.relative(ROOT, filePath)}`)
|
||
count++
|
||
}
|
||
}
|
||
return count
|
||
}
|
||
|
||
function patchAddonUniH5(key) {
|
||
const staging = path.join(ROOT, 'dist', '.addons', key)
|
||
const assembled = path.join(ROOT, 'dist', 'build', 'h5', 'assets', 'addons', key)
|
||
let count = 0
|
||
if (fs.existsSync(staging)) count += patchAddonDir(staging)
|
||
if (fs.existsSync(assembled)) count += patchAddonDir(assembled)
|
||
return count
|
||
}
|
||
|
||
function patchAllAddonUniH5() {
|
||
const addonsRoot = path.join(ROOT, 'dist', 'build', 'h5', 'assets', 'addons')
|
||
if (!fs.existsSync(addonsRoot)) return 0
|
||
let count = 0
|
||
for (const name of fs.readdirSync(addonsRoot)) {
|
||
const dir = path.join(addonsRoot, name)
|
||
if (fs.statSync(dir).isDirectory()) count += patchAddonDir(dir)
|
||
}
|
||
const stagingRoot = path.join(ROOT, 'dist', '.addons')
|
||
if (fs.existsSync(stagingRoot)) {
|
||
for (const name of fs.readdirSync(stagingRoot)) {
|
||
const dir = path.join(stagingRoot, name)
|
||
if (fs.statSync(dir).isDirectory()) count += patchAddonDir(dir)
|
||
}
|
||
}
|
||
return count
|
||
}
|
||
|
||
if (require.main === module) {
|
||
const key = process.argv[2] || process.env.ADDON_KEY
|
||
if (key) {
|
||
const n = patchAddonUniH5(key)
|
||
console.log(`[patch-addon-uni-h5] done (${n} file(s))`)
|
||
} else {
|
||
const n = patchAllAddonUniH5()
|
||
console.log(`[patch-addon-uni-h5] patched all (${n} file(s))`)
|
||
}
|
||
}
|
||
|
||
module.exports = {
|
||
patchAddonUniH5,
|
||
patchAddonDir,
|
||
patchExportHelper,
|
||
patchUniH5File,
|
||
patchAddonAssetChunk,
|
||
patchCoreRouterAccess,
|
||
patchAllAddonUniH5,
|
||
fixDuplicatePagesVar
|
||
}
|