pagesJson.ts 8.3 KB
Newer Older
1
import fs from 'fs'
fxy060608's avatar
fxy060608 已提交
2 3
import path from 'path'
import slash from 'slash'
fxy060608's avatar
fxy060608 已提交
4
import { Plugin } from 'vite'
fxy060608's avatar
fxy060608 已提交
5 6
import { parse } from 'jsonc-parser'
import { hasOwn, camelize, capitalize, isPlainObject } from '@vue/shared'
fxy060608's avatar
fxy060608 已提交
7
import { parseJson } from '@dcloudio/uni-cli-shared'
fxy060608's avatar
fxy060608 已提交
8
import { VitePluginUniResolvedOptions } from '../..'
9

fxy060608's avatar
fxy060608 已提交
10
const pkg = require('@dcloudio/vite-plugin-uni/package.json')
fxy060608's avatar
fxy060608 已提交
11

fxy060608's avatar
fxy060608 已提交
12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
const PAGES_JSON_JS = 'pages.json.js'

export function uniPagesJsonPlugin(
  options: VitePluginUniResolvedOptions
): Plugin {
  const pagesJsonPath = slash(path.join(options.inputDir, 'pages.json'))
  return {
    name: 'vite:uni-pages-json',
    resolveId(id) {
      if (id.endsWith(PAGES_JSON_JS)) {
        return pagesJsonPath + '.js'
      }
    },
    transform(code, id) {
      if (id.endsWith(PAGES_JSON_JS)) {
        return {
          code:
fxy060608's avatar
fxy060608 已提交
29
            (options.command === 'serve' ? registerGlobalCode : '') +
fxy060608's avatar
fxy060608 已提交
30 31 32 33 34 35 36 37 38 39 40
            parsePagesJson(code, options),
          map: { mappings: '' },
        }
      }
    },
    load(id) {
      if (id.endsWith(PAGES_JSON_JS)) {
        return JSON.stringify(parse(fs.readFileSync(pagesJsonPath, 'utf8')))
      }
    },
  }
41
}
fxy060608's avatar
fxy060608 已提交
42 43 44 45

interface PageRouteOptions {
  name: string
  path: string
fxy060608's avatar
fxy060608 已提交
46
  meta: Partial<UniApp.PageRouteMeta>
fxy060608's avatar
fxy060608 已提交
47 48
}

fxy060608's avatar
fxy060608 已提交
49 50 51 52
function parsePagesJson(
  jsonStr: string,
  options: VitePluginUniResolvedOptions
) {
53 54
  const pagesJson = formatPagesJson(jsonStr)
  const definePagesCode = generatePagesDefineCode(pagesJson)
fxy060608's avatar
fxy060608 已提交
55 56 57 58 59
  const uniRoutesCode = generateRoutes(pagesJson)
  const uniConfigCode = generateConfig(pagesJson, options)
  const manifestJsonPath = slash(
    path.resolve(options.inputDir, 'manifest.json.js')
  )
fxy060608's avatar
fxy060608 已提交
60

61
  return `
62
import { defineAsyncComponent, resolveComponent, createVNode, withCtx, openBlock, createBlock } from 'vue'
fxy060608's avatar
fxy060608 已提交
63 64 65
import { PageComponent, AsyncLoadingComponent, AsyncErrorComponent } from '@dcloudio/uni-h5'
import { appid, debug, networkTimeout, router, async, sdkConfigs, qqMapKey, nvue } from '${manifestJsonPath}'
${uniConfigCode}
66
${definePagesCode}
fxy060608's avatar
fxy060608 已提交
67
${uniRoutesCode}
fxy060608's avatar
fxy060608 已提交
68
${options.command === 'serve' ? hmrCode : ''}
69
`
70 71
}

fxy060608's avatar
fxy060608 已提交
72 73 74 75 76 77
const hmrCode = `if(import.meta.hot){
  import.meta.hot.on('invalidate', (data) => {
      import.meta.hot.invalidate()
  })
}`

78
const registerGlobalCode = `import {uni,getCurrentPages,getApp,UniServiceJSBridge,UniViewJSBridge} from '@dcloudio/uni-h5'
79 80 81 82 83 84
window.getApp = getApp
window.getCurrentPages = getCurrentPages
window.uni = window.__GLOBAL__ = uni
window.UniViewJSBridge = UniViewJSBridge
window.UniServiceJSBridge = UniServiceJSBridge
`
85

fxy060608's avatar
fxy060608 已提交
86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
function formatPages(pagesJson: Record<string, any>, jsonStr: string) {
  if (!Array.isArray(pagesJson.pages)) {
    pagesJson.pages = []
    console.error(`[vite] Error: pages.json->pages parse failed.\n`, jsonStr)
  } else if (!pagesJson.pages.length) {
    console.error(
      `[vite] Error: pages.json->pages must contain at least 1 page.\n`,
      jsonStr
    )
  }
}

function removePlatformStyle(globalStyle: Record<string, any>) {
  delete globalStyle['app-plus']
  delete globalStyle['h5']
101
  Object.keys(globalStyle).forEach((name) => {
fxy060608's avatar
fxy060608 已提交
102 103 104 105 106 107 108
    if (name.startsWith('mp-') || name.startsWith('quickapp')) {
      delete globalStyle[name]
    }
  })
  return globalStyle
}

fxy060608's avatar
fxy060608 已提交
109 110 111 112 113 114 115 116 117
const navigationBarMaps = {
  navigationBarBackgroundColor: 'backgroundColor',
  navigationBarTextStyle: 'textStyle',
  navigationBarTitleText: 'titleText',
  navigationStyle: 'style',
  titleImage: 'titleImage',
  titlePenetrate: 'titlePenetrate',
}

fxy060608's avatar
fxy060608 已提交
118 119 120 121
function normalizeNavigationBar(
  pageStyle: Record<string, any>
): UniApp.PageNavigationBar {
  const navigationBar = Object.create(null) as UniApp.PageNavigationBar
fxy060608's avatar
fxy060608 已提交
122 123
  Object.keys(navigationBarMaps).forEach((name) => {
    if (hasOwn(pageStyle, name)) {
fxy060608's avatar
fxy060608 已提交
124
      // @ts-ignore
fxy060608's avatar
fxy060608 已提交
125 126 127 128
      navigationBar[navigationBarMaps[name]] = pageStyle[name]
      delete pageStyle[name]
    }
  })
fxy060608's avatar
fxy060608 已提交
129 130 131 132 133 134 135
  if (
    pageStyle.navigationBarShadow &&
    pageStyle.navigationBarShadow.colorType
  ) {
    navigationBar.shadowColorType = pageStyle.navigationBarShadow.colorType
    delete pageStyle.navigationBarShadow
  }
fxy060608's avatar
fxy060608 已提交
136 137 138 139
  const { titleNView } = pageStyle
  if (isPlainObject(titleNView)) {
    Object.assign(navigationBar, titleNView)
  }
fxy060608's avatar
fxy060608 已提交
140

fxy060608's avatar
fxy060608 已提交
141 142 143 144 145 146 147 148
  return navigationBar
}

function normalizePageStyle(pageStyle: Record<string, any>) {
  pageStyle.navigationBar = normalizeNavigationBar(pageStyle)
  return pageStyle
}

fxy060608's avatar
fxy060608 已提交
149 150 151 152 153 154 155 156 157 158
function formatPageStyle(pageStyle?: Record<string, any>) {
  if (pageStyle) {
    const appGlobalStyle = pageStyle['app-plus']
    if (appGlobalStyle) {
      Object.assign(pageStyle, appGlobalStyle)
    }
    const h5GlobalStyle = pageStyle['h5']
    if (h5GlobalStyle) {
      Object.assign(pageStyle, h5GlobalStyle)
    }
fxy060608's avatar
fxy060608 已提交
159
    return normalizePageStyle(removePlatformStyle(pageStyle))
fxy060608's avatar
fxy060608 已提交
160
  }
fxy060608's avatar
fxy060608 已提交
161
  return {}
fxy060608's avatar
fxy060608 已提交
162 163
}

fxy060608's avatar
fxy060608 已提交
164 165
function formatSubpackages(subpackages?: UniApp.PagesJsonSubpackagesOptions[]) {
  const pages: UniApp.PagesJsonPageOptions[] = []
fxy060608's avatar
fxy060608 已提交
166 167 168 169 170 171 172 173 174 175 176 177 178 179
  if (Array.isArray(subpackages)) {
    subpackages.forEach(({ root, pages: subPages }) => {
      if (root && subPages.length) {
        subPages.forEach((subPage: { path: string }) => {
          subPage.path = slash(path.join(root, subPage.path))
          pages.push(subPage)
        })
      }
    })
  }
  return pages
}

function formatPagesJson(jsonStr: string) {
fxy060608's avatar
fxy060608 已提交
180
  let pagesJson: UniApp.PagesJson = {
181
    pages: [],
fxy060608's avatar
fxy060608 已提交
182
  }
183
  // preprocess
fxy060608's avatar
fxy060608 已提交
184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200
  try {
    pagesJson = parseJson(jsonStr, true)
  } catch (e) {
    console.error(`[vite] Error: pages.json parse failed.\n`, jsonStr, e)
  }
  // pages
  formatPages(pagesJson, jsonStr)
  // subpackages
  pagesJson.pages.push(
    ...formatSubpackages(pagesJson.subPackages || pagesJson.subpackages)
  )
  // globalStyle
  formatPageStyle(pagesJson.globalStyle)
  return pagesJson
}

function formatPageIdentifier(path: string) {
fxy060608's avatar
fxy060608 已提交
201
  return capitalize(camelize(path.replace(/\//g, '-')))
fxy060608's avatar
fxy060608 已提交
202 203
}

fxy060608's avatar
fxy060608 已提交
204
function generatePageDefineCode(pageOptions: UniApp.PagesJsonPageOptions) {
fxy060608's avatar
fxy060608 已提交
205 206
  return `const ${formatPageIdentifier(
    pageOptions.path
fxy060608's avatar
fxy060608 已提交
207
  )} = defineAsyncComponent({
fxy060608's avatar
fxy060608 已提交
208
 loader: () => import('./${pageOptions.path}.vue?mpType=page'),
fxy060608's avatar
fxy060608 已提交
209 210 211 212 213 214
 loadingComponent: AsyncLoadingComponent,
 errorComponent: AsyncErrorComponent,
 delay: async.delay,
 timeout: async.timeout,
 suspensible: async.suspensible
})`
fxy060608's avatar
fxy060608 已提交
215 216
}

fxy060608's avatar
fxy060608 已提交
217 218
function generatePagesDefineCode(pagesJson: UniApp.PagesJson) {
  return pagesJson.pages
219
    .map((pageOptions) => generatePageDefineCode(pageOptions))
fxy060608's avatar
fxy060608 已提交
220 221 222
    .join('\n')
}

fxy060608's avatar
fxy060608 已提交
223
function formatPagesRoute(pagesJson: UniApp.PagesJson): PageRouteOptions[] {
fxy060608's avatar
fxy060608 已提交
224 225
  const firstPagePath = pagesJson.pages[0].path
  const tabBarList = (pagesJson.tabBar && pagesJson.tabBar.list) || []
fxy060608's avatar
fxy060608 已提交
226
  return pagesJson.pages.map((pageOptions) => {
fxy060608's avatar
fxy060608 已提交
227 228
    const path = pageOptions.path
    const name = formatPageIdentifier(path)
229
    const isEntry = firstPagePath === path ? true : undefined
fxy060608's avatar
fxy060608 已提交
230 231 232
    const tabBarIndex = tabBarList.findIndex(
      (tabBarPage: { pagePath: string }) => tabBarPage.pagePath === path
    )
233
    const isTabBar = tabBarIndex !== -1 ? true : undefined
fxy060608's avatar
fxy060608 已提交
234 235

    let windowTop = 0
fxy060608's avatar
fxy060608 已提交
236 237 238 239 240 241 242 243 244 245 246
    const meta = Object.assign(
      {
        isQuit: isEntry || isTabBar ? true : undefined,
        isEntry,
        isTabBar,
        tabBarIndex,
        windowTop,
      },
      formatPageStyle(pageOptions.style)
    )

fxy060608's avatar
fxy060608 已提交
247 248 249
    return {
      name,
      path: pageOptions.path,
250
      meta,
fxy060608's avatar
fxy060608 已提交
251 252 253 254
    }
  })
}

fxy060608's avatar
fxy060608 已提交
255
function generatePageRoute({ name, path, meta }: PageRouteOptions) {
fxy060608's avatar
fxy060608 已提交
256
  return `{
257 258 259
  path:'/${meta.isEntry ? '' : path}',
  component:{
    render() {
fxy060608's avatar
fxy060608 已提交
260
      return (openBlock(), createBlock(PageComponent, null, {page: withCtx(() => [createVNode(${name})]), _: 1 /* STABLE */}))
261 262 263 264
    }
  },
  meta: ${JSON.stringify(meta)}
}`
fxy060608's avatar
fxy060608 已提交
265 266 267
}

function generatePagesRoute(pagesRouteOptions: PageRouteOptions[]) {
268
  return pagesRouteOptions.map((pageOptions) => generatePageRoute(pageOptions))
fxy060608's avatar
fxy060608 已提交
269 270
}

fxy060608's avatar
fxy060608 已提交
271
function generateRoutes(pagesJson: UniApp.PagesJson) {
fxy060608's avatar
fxy060608 已提交
272
  return `window.__uniRoutes=[${[
fxy060608's avatar
fxy060608 已提交
273
    `{ path: '/${pagesJson.pages[0].path}', redirect: '/' }`,
274
    ...generatePagesRoute(formatPagesRoute(pagesJson)),
fxy060608's avatar
fxy060608 已提交
275
  ].join(',')}]`
fxy060608's avatar
fxy060608 已提交
276 277
}

fxy060608's avatar
fxy060608 已提交
278 279 280 281
function generateConfig(
  pagesJson: Record<string, any>,
  options: VitePluginUniResolvedOptions
) {
fxy060608's avatar
fxy060608 已提交
282 283 284
  delete pagesJson.pages
  delete pagesJson.subPackages
  delete pagesJson.subpackages
fxy060608's avatar
fxy060608 已提交
285 286
  pagesJson.compilerVersion = pkg['uni-app'].compilerVersion
  return (
fxy060608's avatar
fxy060608 已提交
287
    (options.command === 'serve'
fxy060608's avatar
fxy060608 已提交
288 289 290 291 292 293 294 295 296 297 298 299 300 301 302
      ? ''
      : `window['____'+appid+'____']=true
delete window['____'+appid+'____']
`) +
    `window.__uniConfig=Object.assign(${JSON.stringify(pagesJson)},{
  async,
  debug,
  networkTimeout,
  sdkConfigs,
  qqMapKey,
  nvue,
  router
})
`
  )
fxy060608's avatar
fxy060608 已提交
303
}