pagesJson.ts 7.9 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 29 30 31 32 33 34 35 36 37 38 39 40
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:
            (options.devServer ? registerGlobalCode : '') +
            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}
68
`
69 70 71
}

const registerGlobalCode = `import {uni,getCurrentPages,getApp,UniServiceJSBridge,UniViewJSBridge} from '@dcloudio/uni-h5'
72 73 74 75 76 77
window.getApp = getApp
window.getCurrentPages = getCurrentPages
window.uni = window.__GLOBAL__ = uni
window.UniViewJSBridge = UniViewJSBridge
window.UniServiceJSBridge = UniServiceJSBridge
`
78

fxy060608's avatar
fxy060608 已提交
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
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']
94
  Object.keys(globalStyle).forEach((name) => {
fxy060608's avatar
fxy060608 已提交
95 96 97 98 99 100 101
    if (name.startsWith('mp-') || name.startsWith('quickapp')) {
      delete globalStyle[name]
    }
  })
  return globalStyle
}

fxy060608's avatar
fxy060608 已提交
102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
const navigationBarMaps = {
  navigationBarBackgroundColor: 'backgroundColor',
  navigationBarTextStyle: 'textStyle',
  navigationBarTitleText: 'titleText',
  navigationBarShadow: 'shadow',
  navigationStyle: 'style',
  titleImage: 'titleImage',
  titlePenetrate: 'titlePenetrate',
}

function normalizeNavigationBar(pageStyle: Record<string, any>) {
  const navigationBar = Object.create(null)
  Object.keys(navigationBarMaps).forEach((name) => {
    if (hasOwn(pageStyle, name)) {
      navigationBar[navigationBarMaps[name]] = pageStyle[name]
      delete pageStyle[name]
    }
  })
  const { titleNView } = pageStyle
  if (isPlainObject(titleNView)) {
    Object.assign(navigationBar, titleNView)
  }
  return navigationBar
}

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

fxy060608's avatar
fxy060608 已提交
132 133 134 135 136 137 138 139 140 141
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 已提交
142
    return normalizePageStyle(removePlatformStyle(pageStyle))
fxy060608's avatar
fxy060608 已提交
143
  }
fxy060608's avatar
fxy060608 已提交
144
  return {}
fxy060608's avatar
fxy060608 已提交
145 146
}

fxy060608's avatar
fxy060608 已提交
147 148
function formatSubpackages(subpackages?: UniApp.PagesJsonSubpackagesOptions[]) {
  const pages: UniApp.PagesJsonPageOptions[] = []
fxy060608's avatar
fxy060608 已提交
149 150 151 152 153 154 155 156 157 158 159 160 161 162
  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 已提交
163
  let pagesJson: UniApp.PagesJson = {
164
    pages: [],
fxy060608's avatar
fxy060608 已提交
165
  }
166
  // preprocess
fxy060608's avatar
fxy060608 已提交
167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183
  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 已提交
184
  return capitalize(camelize(path.replace(/\//g, '-')))
fxy060608's avatar
fxy060608 已提交
185 186
}

fxy060608's avatar
fxy060608 已提交
187
function generatePageDefineCode(pageOptions: UniApp.PagesJsonPageOptions) {
fxy060608's avatar
fxy060608 已提交
188 189
  return `const ${formatPageIdentifier(
    pageOptions.path
fxy060608's avatar
fxy060608 已提交
190
  )} = defineAsyncComponent({
fxy060608's avatar
fxy060608 已提交
191
 loader: () => import('./${pageOptions.path}.vue?mpType=page'),
fxy060608's avatar
fxy060608 已提交
192 193 194 195 196 197
 loadingComponent: AsyncLoadingComponent,
 errorComponent: AsyncErrorComponent,
 delay: async.delay,
 timeout: async.timeout,
 suspensible: async.suspensible
})`
fxy060608's avatar
fxy060608 已提交
198 199
}

fxy060608's avatar
fxy060608 已提交
200 201
function generatePagesDefineCode(pagesJson: UniApp.PagesJson) {
  return pagesJson.pages
202
    .map((pageOptions) => generatePageDefineCode(pageOptions))
fxy060608's avatar
fxy060608 已提交
203 204 205
    .join('\n')
}

fxy060608's avatar
fxy060608 已提交
206
function formatPagesRoute(pagesJson: UniApp.PagesJson): PageRouteOptions[] {
fxy060608's avatar
fxy060608 已提交
207 208
  const firstPagePath = pagesJson.pages[0].path
  const tabBarList = (pagesJson.tabBar && pagesJson.tabBar.list) || []
fxy060608's avatar
fxy060608 已提交
209
  return pagesJson.pages.map((pageOptions) => {
fxy060608's avatar
fxy060608 已提交
210 211
    const path = pageOptions.path
    const name = formatPageIdentifier(path)
212
    const isEntry = firstPagePath === path ? true : undefined
fxy060608's avatar
fxy060608 已提交
213 214 215
    const tabBarIndex = tabBarList.findIndex(
      (tabBarPage: { pagePath: string }) => tabBarPage.pagePath === path
    )
216
    const isTabBar = tabBarIndex !== -1 ? true : undefined
fxy060608's avatar
fxy060608 已提交
217 218

    let windowTop = 0
fxy060608's avatar
fxy060608 已提交
219 220 221 222 223 224 225 226 227 228 229
    const meta = Object.assign(
      {
        isQuit: isEntry || isTabBar ? true : undefined,
        isEntry,
        isTabBar,
        tabBarIndex,
        windowTop,
      },
      formatPageStyle(pageOptions.style)
    )

fxy060608's avatar
fxy060608 已提交
230 231 232
    return {
      name,
      path: pageOptions.path,
233
      meta,
fxy060608's avatar
fxy060608 已提交
234 235 236 237
    }
  })
}

fxy060608's avatar
fxy060608 已提交
238
function generatePageRoute({ name, path, meta }: PageRouteOptions) {
fxy060608's avatar
fxy060608 已提交
239
  return `{
240 241 242
  path:'/${meta.isEntry ? '' : path}',
  component:{
    render() {
fxy060608's avatar
fxy060608 已提交
243
      return (openBlock(), createBlock(PageComponent, null, {page: withCtx(() => [createVNode(${name})]), _: 1 /* STABLE */}))
244 245 246 247
    }
  },
  meta: ${JSON.stringify(meta)}
}`
fxy060608's avatar
fxy060608 已提交
248 249 250
}

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

fxy060608's avatar
fxy060608 已提交
254
function generateRoutes(pagesJson: UniApp.PagesJson) {
fxy060608's avatar
fxy060608 已提交
255
  return `window.__uniRoutes=[${[
fxy060608's avatar
fxy060608 已提交
256
    `{ path: '/${pagesJson.pages[0].path}', redirect: '/' }`,
257
    ...generatePagesRoute(formatPagesRoute(pagesJson)),
fxy060608's avatar
fxy060608 已提交
258
  ].join(',')}]`
fxy060608's avatar
fxy060608 已提交
259 260
}

fxy060608's avatar
fxy060608 已提交
261 262 263 264
function generateConfig(
  pagesJson: Record<string, any>,
  options: VitePluginUniResolvedOptions
) {
fxy060608's avatar
fxy060608 已提交
265 266 267
  delete pagesJson.pages
  delete pagesJson.subPackages
  delete pagesJson.subpackages
fxy060608's avatar
fxy060608 已提交
268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285
  pagesJson.compilerVersion = pkg['uni-app'].compilerVersion
  return (
    (options.devServer
      ? ''
      : `window['____'+appid+'____']=true
delete window['____'+appid+'____']
`) +
    `window.__uniConfig=Object.assign(${JSON.stringify(pagesJson)},{
  async,
  debug,
  networkTimeout,
  sdkConfigs,
  qqMapKey,
  nvue,
  router
})
`
  )
fxy060608's avatar
fxy060608 已提交
286
}