@@ -1,8 +1,10 @@
/**
* 切换 pages.json 在「完整模式」和「商家端专属模式」之间
* 切换 pages.json 到不同端的专属编译模式。
*
* 用法:
* npm run pages:merchant → 仅编译 merchant 相关页面(大幅缩短编译时间)
* npm run pages:consumer → 仅编译 consumer 相关页面
* npm run pages:merchant → 仅编译 merchant 相关页面
* npm run pages:admin → 仅编译 admin 相关页面
* npm run pages:full → 恢复完整 pages.json
*/
const fs = require ( "fs" ) ;
@@ -11,35 +13,415 @@ const path = require("path");
const root = path . resolve ( _ _dirname , ".." ) ;
const pagesJson = path . join ( root , "pages.json" ) ;
const pagesFull = path . join ( root , "pages.full.json" ) ;
const pagesMerchant = path . join ( root , "pages.merchant.json " ) ;
const backupDir = path . join ( root , ". pages-backup " ) ;
const pageExtensions = [ ".uvue" , ".vue" , ".nvue" ] ;
const mode = process . argv [ 2 ] ; // 'merchant' | 'full'
const consumerUserPages = [
"pages/user/boot" ,
"pages/user/login" ,
"pages/user/register" ,
"pages/user/forgot-password" ,
"pages/user/terms" ,
"pages/user/center" ,
"pages/user/profile" ,
"pages/user/change-password" ,
"pages/user/bind-phone" ,
"pages/user/bind-email" ,
] ;
if ( mode === "merchant" ) {
if ( ! fs . existsSync ( pagesMerchant ) ) {
console . error ( "❌ pages.merchant.json 不存在,请先创建该文件。" ) ;
const merchantUserPages = [
"pages/user/login" ,
"pages/user/boot" ,
"pages/user/register" ,
"pages/user/forgot-password" ,
"pages/user/terms" ,
"pages/user/change-password" ,
] ;
const adminUserPages = [
"pages/user/login" ,
"pages/user/boot" ,
"pages/user/forgot-password" ,
"pages/user/terms" ,
"pages/user/change-password" ,
] ;
const targetConfigs = {
consumer : {
fileName : "pages.consumer.json" ,
label : "消费者端" ,
entryPath : "pages/main/index" ,
orderedTopLevelPaths : [
"pages/main/index" ,
... consumerUserPages ,
"pages/main/category" ,
"pages/main/messages" ,
"pages/main/cart" ,
"pages/main/profile" ,
] ,
topLevelMatcher ( pagePath ) {
return (
pagePath . startsWith ( "pages/main/" ) ||
consumerUserPages . includes ( pagePath )
) ;
} ,
subPackageMatcher ( rootPath ) {
return rootPath === "pages/mall/consumer" ;
} ,
tabBarMatcher ( pagePath ) {
return pagePath . startsWith ( "pages/main/" ) ;
} ,
defaultCondition ( ) {
return {
current : 0 ,
list : [
{
name : "consumer端" ,
path : "pages/main/index" ,
query : "role=consumer" ,
} ,
] ,
} ;
} ,
} ,
merchant : {
fileName : "pages.merchant.json" ,
label : "商家端" ,
entryPath : "pages/user/login" ,
orderedTopLevelPaths : [
"pages/user/login" ,
"pages/user/boot" ,
"pages/user/register" ,
"pages/user/forgot-password" ,
"pages/user/terms" ,
"pages/user/change-password" ,
"pages/mall/merchant/index" ,
"pages/mall/merchant/messages" ,
"pages/mall/merchant/orders" ,
"pages/mall/merchant/growth" ,
"pages/mall/merchant/profile" ,
] ,
topLevelMatcher ( pagePath ) {
return (
merchantUserPages . includes ( pagePath ) ||
pagePath . startsWith ( "pages/mall/merchant/" )
) ;
} ,
subPackageMatcher ( ) {
return false ;
} ,
defaultCondition ( ) {
return {
current : 0 ,
list : [
{
name : "merchant端" ,
path : "pages/mall/merchant/index" ,
query : "role=merchant" ,
} ,
] ,
} ;
} ,
} ,
admin : {
fileName : "pages.admin.json" ,
label : "管理后台" ,
entryPath : "pages/user/login" ,
orderedTopLevelPaths : [
"pages/user/login" ,
"pages/user/boot" ,
"pages/user/forgot-password" ,
"pages/user/terms" ,
"pages/user/change-password" ,
"pages/mall/admin/homePage/index" ,
"pages/mall/admin/userCenter/index" ,
] ,
topLevelMatcher ( pagePath ) {
return (
adminUserPages . includes ( pagePath ) ||
pagePath === "pages/mall/admin/homePage/index" ||
pagePath === "pages/mall/admin/userCenter/index"
) ;
} ,
subPackageMatcher ( rootPath ) {
return rootPath . startsWith ( "pages/mall/admin/" ) ;
} ,
defaultCondition ( ) {
return {
current : 0 ,
list : [
{
name : "admin端" ,
path : "pages/mall/admin/homePage/index" ,
query : "role=admin" ,
} ,
] ,
} ;
} ,
} ,
} ;
const mode = process . argv [ 2 ] ; // 'consumer' | 'merchant' | 'admin' | 'full'
function readJson ( filePath ) {
const rawContent = fs . readFileSync ( filePath , "utf8" ) . replace ( /^\uFEFF/ , "" ) ;
return JSON . parse ( rawContent ) ;
}
function writeJson ( filePath , content ) {
fs . writeFileSync ( filePath , ` ${ JSON . stringify ( content , null , 2 ) } \n ` , "utf8" ) ;
}
function ensureFullBackup ( ) {
if ( fs . existsSync ( pagesFull ) ) {
return ;
}
fs . copyFileSync ( pagesJson , pagesFull ) ;
console . log ( "📦 已备份 pages.json → pages.full.json" ) ;
}
function backupCurrentPages ( targetName ) {
if ( ! fs . existsSync ( pagesJson ) ) {
return ;
}
fs . mkdirSync ( backupDir , { recursive : true } ) ;
const timestamp = new Date ( ) . toISOString ( ) . replace ( /[.:]/g , "-" ) ;
const backupPath = path . join (
backupDir ,
` pages. ${ targetName } . ${ timestamp } .json ` ,
) ;
fs . copyFileSync ( pagesJson , backupPath ) ;
console . log ( ` 📦 已备份当前 pages.json → ${ path . relative ( root , backupPath ) } ` ) ;
}
function getPageMap ( fullConfig ) {
return new Map ( fullConfig . pages . map ( ( page ) => [ page . path , page ] ) ) ;
}
function findMatchingPageFile ( routePath ) {
for ( const extension of pageExtensions ) {
const candidate = path . join ( root , ` ${ routePath } ${ extension } ` ) ;
if ( fs . existsSync ( candidate ) ) {
return candidate ;
}
}
return null ;
}
function clonePage ( page ) {
return JSON . parse ( JSON . stringify ( page ) ) ;
}
function flattenSubPackagePages ( fullConfig , rootPath ) {
const targetPackage = fullConfig . subPackages . find (
( item ) => item . root === rootPath ,
) ;
if ( ! targetPackage ) {
return [ ] ;
}
return targetPackage . pages . map ( ( page ) => ( {
path : ` ${ rootPath } / ${ page . path } ` ,
style : page . style ? JSON . parse ( JSON . stringify ( page . style ) ) : { } ,
} ) ) ;
}
function getTopLevelPages ( fullConfig , targetConfig ) {
const pageMap = getPageMap ( fullConfig ) ;
const matchedFromTopLevel = fullConfig . pages
. filter ( ( page ) => targetConfig . topLevelMatcher ( page . path ) )
. map ( ( page ) => clonePage ( page ) ) ;
const flattenedPages = ( targetConfig . flattenRoots || [ ] ) . flatMap ( ( rootPath ) =>
flattenSubPackagePages ( fullConfig , rootPath ) ,
) ;
const mergedMap = new Map ( ) ;
for ( const page of [ ... matchedFromTopLevel , ... flattenedPages ] ) {
mergedMap . set ( page . path , page ) ;
}
const orderedPages = [ ] ;
for ( const pagePath of targetConfig . orderedTopLevelPaths ) {
if ( mergedMap . has ( pagePath ) ) {
orderedPages . push ( mergedMap . get ( pagePath ) ) ;
mergedMap . delete ( pagePath ) ;
} else if ( pageMap . has ( pagePath ) ) {
orderedPages . push ( clonePage ( pageMap . get ( pagePath ) ) ) ;
}
}
for ( const page of [ ... matchedFromTopLevel , ... flattenedPages ] ) {
if ( mergedMap . has ( page . path ) ) {
orderedPages . push ( mergedMap . get ( page . path ) ) ;
mergedMap . delete ( page . path ) ;
}
}
return dedupePages ( orderedPages ) ;
}
function getSubPackages ( fullConfig , targetConfig ) {
return fullConfig . subPackages
. filter ( ( item ) => targetConfig . subPackageMatcher ( item . root ) )
. map ( ( item ) => JSON . parse ( JSON . stringify ( item ) ) ) ;
}
function dedupePages ( pages ) {
const seen = new Set ( ) ;
return pages . filter ( ( page ) => {
if ( seen . has ( page . path ) ) {
return false ;
}
seen . add ( page . path ) ;
return true ;
} ) ;
}
function getCondition ( targetConfig , sourceCondition ) {
if ( sourceCondition && Array . isArray ( sourceCondition . list ) ) {
const filteredList = sourceCondition . list . filter (
( item ) => item . path === targetConfig . entryPath ,
) ;
if ( filteredList . length > 0 ) {
return {
current : 0 ,
list : filteredList ,
} ;
}
}
return targetConfig . defaultCondition ( ) ;
}
function validateGeneratedConfig ( targetName , config ) {
const missingRoutes = [ ] ;
const routeSet = new Set ( ) ;
for ( const page of config . pages || [ ] ) {
routeSet . add ( page . path ) ;
if ( ! findMatchingPageFile ( page . path ) ) {
missingRoutes . push ( page . path ) ;
}
}
for ( const subPackage of config . subPackages || [ ] ) {
for ( const page of subPackage . pages || [ ] ) {
const fullPath = ` ${ subPackage . root } / ${ page . path } ` ;
routeSet . add ( fullPath ) ;
if ( ! findMatchingPageFile ( fullPath ) ) {
missingRoutes . push ( fullPath ) ;
}
}
}
for ( const tabItem of config . tabBar ? . list || [ ] ) {
if ( ! routeSet . has ( tabItem . pagePath ) ) {
missingRoutes . push ( ` tabBar -> ${ tabItem . pagePath } ` ) ;
}
}
for ( const conditionItem of config . condition ? . list || [ ] ) {
if ( ! routeSet . has ( conditionItem . path ) ) {
missingRoutes . push ( ` condition -> ${ conditionItem . path } ` ) ;
}
}
if ( missingRoutes . length > 0 ) {
console . error ( ` ❌ ${ targetName } 配置存在无效页面引用: ` ) ;
for ( const routePath of missingRoutes ) {
console . error ( ` - ${ routePath } ` ) ;
}
process . exit ( 1 ) ;
}
// 备份当前 pages.json( 仅在未备份时执行, 避免覆盖真正的完整版本)
if ( ! fs . existsSync ( pagesFull ) ) {
fs . copyFileSync ( pagesJson , pagesFull ) ;
console . log ( "📦 已备份 pages.json → pages.full.json" ) ;
}
function buildTargetConfig ( fullConfig , targetName , sourceCondition ) {
const targetConfig = targetConfigs [ targetName ] ;
const nextConfig = { } ;
nextConfig . pages = getTopLevelPages ( fullConfig , targetConfig ) ;
nextConfig . subPackages = getSubPackages ( fullConfig , targetConfig ) ;
if ( targetConfig . tabBarMatcher ) {
const filteredTabBarList = ( fullConfig . tabBar ? . list || [ ] ) . filter ( ( item ) =>
targetConfig . tabBarMatcher ( item . pagePath ) ,
) ;
if ( filteredTabBarList . length > 0 && fullConfig . tabBar ) {
nextConfig . tabBar = {
... JSON . parse ( JSON . stringify ( fullConfig . tabBar ) ) ,
list : filteredTabBarList ,
} ;
}
}
fs . copyFileSync ( pagesMerchant , pagesJson ) ;
console . log ( "✅ 已切换为【商家端专属编译模式】" ) ;
console . log ( " 仅包含 merchant 页面,差量编译速度大幅提升。" ) ;
for ( const key of Object . keys ( fullConfig ) ) {
if ( [ "pages" , "subPackages" , "tabBar" , "condition" ] . includes ( key ) ) {
continue ;
}
nextConfig [ key ] = JSON . parse ( JSON . stringify ( fullConfig [ key ] ) ) ;
}
nextConfig . condition = getCondition ( targetConfig , sourceCondition ) ;
validateGeneratedConfig ( targetName , nextConfig ) ;
return nextConfig ;
}
function writeTargetConfig ( targetName , config ) {
const targetPath = path . join ( root , targetConfigs [ targetName ] . fileName ) ;
writeJson ( targetPath , config ) ;
fs . copyFileSync ( targetPath , pagesJson ) ;
const pageCount = config . pages . length ;
const subPackageCount = config . subPackages . length ;
const tabBarCount = config . tabBar ? . list ? . length || 0 ;
console . log ( ` ✅ 已切换为【 ${ targetConfigs [ targetName ] . label } 专属编译模式】 ` ) ;
console . log ( ` 目标配置: ${ targetConfigs [ targetName ] . fileName } ` ) ;
console . log ( ` pages: ${ pageCount } 个 ` ) ;
console . log ( ` subPackages: ${ subPackageCount } 个 ` ) ;
console . log ( ` tabBar: ${ tabBarCount } 个 ` ) ;
console . log ( " 恢复完整版本: npm run pages:full" ) ;
} else if ( mode === "full" ) {
}
function restoreFullPages ( ) {
if ( ! fs . existsSync ( pagesFull ) ) {
console . error ( "❌ pages.full.json 不存在。" ) ;
console . error ( " 可能当前已经是完整模式,或备份文件已被删除。" ) ;
process . exit ( 1 ) ;
}
backupCurrentPages ( "full" ) ;
fs . copyFileSync ( pagesFull , pagesJson ) ;
console . log ( "✅ 已恢复【完整 pages.json】" ) ;
console . log ( " 包含 consumer + merchant + admin 全部页面。" ) ;
}
if ( mode === "full" ) {
restoreFullPages ( ) ;
} else if ( Object . prototype . hasOwnProperty . call ( targetConfigs , mode ) ) {
ensureFullBackup ( ) ;
backupCurrentPages ( mode ) ;
const fullConfig = readJson ( pagesFull ) ;
const sourceCondition = fs . existsSync ( pagesJson )
? ( ( ) => {
try {
return readJson ( pagesJson ) . condition ;
} catch ( error ) {
return null ;
}
} ) ( )
: null ;
const nextConfig = buildTargetConfig ( fullConfig , mode , sourceCondition ) ;
writeTargetConfig ( mode , nextConfig ) ;
} else {
console . log ( "用法:" ) ;
console . log ( " npm run pages:consumer 切换到消费者端专属编译模式" ) ;
console . log ( " npm run pages:merchant 切换到商家端专属编译模式" ) ;
console . log ( " npm run pages:admin 切换到管理后台专属编译模式" ) ;
console . log ( " npm run pages:full 恢复完整编译模式" ) ;
}