pagesJson.ts 8.0 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, ResolvedConfig } from 'vite'
fxy060608's avatar
fxy060608 已提交
5
import { parse } from 'jsonc-parser'
fxy060608's avatar
fxy060608 已提交
6
import { camelize, capitalize } from '@vue/shared'
fxy060608's avatar
fxy060608 已提交
7 8
import {
  H5_FRAMEWORK_STYLE_PATH,
fxy060608's avatar
fxy060608 已提交
9 10
  BASE_COMPONENTS_STYLE_PATH,
  normalizePagesJson,
11
  API_DEPS_CSS,
fxy060608's avatar
fxy060608 已提交
12 13 14
} from '@dcloudio/uni-cli-shared'
import { VitePluginUniResolvedOptions } from '../..'
import { FEATURE_DEFINES } from '../../utils'
15

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

fxy060608's avatar
fxy060608 已提交
18 19 20
const PAGES_JSON_JS = 'pages.json.js'

export function uniPagesJsonPlugin(
fxy060608's avatar
fxy060608 已提交
21
  config: ResolvedConfig,
fxy060608's avatar
fxy060608 已提交
22 23 24 25 26 27 28 29 30 31
  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'
      }
    },
fxy060608's avatar
fxy060608 已提交
32
    transform(code, id, ssr) {
fxy060608's avatar
fxy060608 已提交
33 34 35
      if (id.endsWith(PAGES_JSON_JS)) {
        return {
          code:
fxy060608's avatar
fxy060608 已提交
36 37 38
            (options.command === 'serve' || (options.command === 'build' && ssr)
              ? registerGlobalCode(config, ssr)
              : '') + generatePagesJsonCode(ssr, code, config, options),
fxy060608's avatar
fxy060608 已提交
39 40 41 42 43 44 45 46 47 48
          map: { mappings: '' },
        }
      }
    },
    load(id) {
      if (id.endsWith(PAGES_JSON_JS)) {
        return JSON.stringify(parse(fs.readFileSync(pagesJsonPath, 'utf8')))
      }
    },
  }
49
}
fxy060608's avatar
fxy060608 已提交
50 51 52 53

interface PageRouteOptions {
  name: string
  path: string
fxy060608's avatar
fxy060608 已提交
54
  meta: Partial<UniApp.PageRouteMeta>
fxy060608's avatar
fxy060608 已提交
55 56
}

fxy060608's avatar
fxy060608 已提交
57
function generatePagesJsonCode(
fxy060608's avatar
fxy060608 已提交
58
  ssr: boolean | undefined,
fxy060608's avatar
fxy060608 已提交
59
  jsonStr: string,
fxy060608's avatar
fxy060608 已提交
60
  config: ResolvedConfig,
fxy060608's avatar
fxy060608 已提交
61 62
  options: VitePluginUniResolvedOptions
) {
fxy060608's avatar
fxy060608 已提交
63
  const globalName = getGlobal(ssr)
fxy060608's avatar
fxy060608 已提交
64
  const pagesJson = normalizePagesJson(jsonStr, options.platform)
fxy060608's avatar
fxy060608 已提交
65
  const definePagesCode = generatePagesDefineCode(pagesJson, config)
fxy060608's avatar
fxy060608 已提交
66 67
  const uniRoutesCode = generateRoutes(globalName, pagesJson)
  const uniConfigCode = generateConfig(globalName, pagesJson, options)
fxy060608's avatar
fxy060608 已提交
68 69 70
  const manifestJsonPath = slash(
    path.resolve(options.inputDir, 'manifest.json.js')
  )
fxy060608's avatar
fxy060608 已提交
71
  const cssCode = generateCssCode(config, options)
fxy060608's avatar
fxy060608 已提交
72

73
  return `
fxy060608's avatar
fxy060608 已提交
74 75 76
import { ${
    config.define!.__UNI_FEATURE_PAGES__ ? 'defineAsyncComponent, ' : ''
  }resolveComponent, createVNode, withCtx, openBlock, createBlock } from 'vue'
fxy060608's avatar
fxy060608 已提交
77
import { PageComponent, AsyncLoadingComponent, AsyncErrorComponent } from '@dcloudio/uni-h5'
fxy060608's avatar
fxy060608 已提交
78
import { appid, debug, networkTimeout, router, async, sdkConfigs, qqMapKey, nvue } from '${manifestJsonPath}'
fxy060608's avatar
fxy060608 已提交
79
const extend = Object.assign
fxy060608's avatar
fxy060608 已提交
80
${cssCode}
fxy060608's avatar
fxy060608 已提交
81
${uniConfigCode}
82
${definePagesCode}
fxy060608's avatar
fxy060608 已提交
83
${uniRoutesCode}
fxy060608's avatar
fxy060608 已提交
84
${options.command === 'serve' ? hmrCode : ''}
fxy060608's avatar
fxy060608 已提交
85
export {}
86
`
87 88
}

fxy060608's avatar
fxy060608 已提交
89 90 91 92 93 94
const hmrCode = `if(import.meta.hot){
  import.meta.hot.on('invalidate', (data) => {
      import.meta.hot.invalidate()
  })
}`

fxy060608's avatar
fxy060608 已提交
95 96 97
function getGlobal(ssr?: boolean) {
  return ssr ? 'global' : 'window'
}
fxy060608's avatar
fxy060608 已提交
98

fxy060608's avatar
fxy060608 已提交
99
function registerGlobalCode(config: ResolvedConfig, ssr?: boolean) {
fxy060608's avatar
fxy060608 已提交
100
  const name = getGlobal(ssr)
101 102 103
  const rpx2pxCode =
    !ssr && config.define!.__UNI_FEATURE_RPX__
      ? `import {upx2px} from '@dcloudio/uni-h5'
fxy060608's avatar
fxy060608 已提交
104
  ${name}.rpx2px = upx2px
105
`
106
      : ''
fxy060608's avatar
fxy060608 已提交
107 108
  return `${rpx2pxCode}
import {uni,getCurrentPages,getApp,UniServiceJSBridge,UniViewJSBridge} from '@dcloudio/uni-h5'
fxy060608's avatar
fxy060608 已提交
109 110 111 112 113 114 115
${name}.getApp = getApp
${name}.getCurrentPages = getCurrentPages
${name}.uni = uni
${name}.UniViewJSBridge = UniViewJSBridge
${name}.UniServiceJSBridge = UniServiceJSBridge
`
}
116

fxy060608's avatar
fxy060608 已提交
117 118
function normalizePageIdentifier(path: string) {
  return capitalize(camelize(path.replace(/\//g, '-')))
fxy060608's avatar
fxy060608 已提交
119 120
}

fxy060608's avatar
fxy060608 已提交
121 122 123 124
function generateCssCode(
  config: ResolvedConfig,
  options: VitePluginUniResolvedOptions
) {
fxy060608's avatar
fxy060608 已提交
125
  const define = config.define! as FEATURE_DEFINES
fxy060608's avatar
fxy060608 已提交
126
  const cssFiles = [H5_FRAMEWORK_STYLE_PATH + 'base.css']
fxy060608's avatar
fxy060608 已提交
127
  if (define.__UNI_FEATURE_PAGES__) {
fxy060608's avatar
fxy060608 已提交
128 129 130
    cssFiles.push(H5_FRAMEWORK_STYLE_PATH + 'async.css')
  }
  if (define.__UNI_FEATURE_RESPONSIVE__) {
fxy060608's avatar
fxy060608 已提交
131
    cssFiles.push(H5_FRAMEWORK_STYLE_PATH + 'layout.css')
fxy060608's avatar
fxy060608 已提交
132
  }
fxy060608's avatar
fxy060608 已提交
133
  if (define.__UNI_FEATURE_NAVIGATIONBAR__) {
fxy060608's avatar
fxy060608 已提交
134
    cssFiles.push(H5_FRAMEWORK_STYLE_PATH + 'pageHead.css')
fxy060608's avatar
fxy060608 已提交
135
  }
fxy060608's avatar
fxy060608 已提交
136
  if (define.__UNI_FEATURE_TABBAR__) {
fxy060608's avatar
fxy060608 已提交
137
    cssFiles.push(H5_FRAMEWORK_STYLE_PATH + 'tabBar.css')
fxy060608's avatar
fxy060608 已提交
138
  }
fxy060608's avatar
fxy060608 已提交
139
  if (define.__UNI_FEATURE_NVUE__) {
fxy060608's avatar
fxy060608 已提交
140
    cssFiles.push(H5_FRAMEWORK_STYLE_PATH + 'nvue.css')
fxy060608's avatar
fxy060608 已提交
141
  }
fxy060608's avatar
fxy060608 已提交
142
  if (define.__UNI_FEATURE_PULL_DOWN_REFRESH__) {
fxy060608's avatar
fxy060608 已提交
143
    cssFiles.push(H5_FRAMEWORK_STYLE_PATH + 'pageRefresh.css')
fxy060608's avatar
fxy060608 已提交
144
  }
fxy060608's avatar
fxy060608 已提交
145
  if (define.__UNI_FEATURE_NAVIGATIONBAR_SEARCHINPUT__) {
fxy060608's avatar
fxy060608 已提交
146 147 148
    cssFiles.push(BASE_COMPONENTS_STYLE_PATH + 'input.css')
  }
  if (options.command === 'serve') {
fxy060608's avatar
fxy060608 已提交
149
    // 开发模式,自动添加所有API相关css
150 151
    Object.keys(API_DEPS_CSS).forEach((name) => {
      const styles = API_DEPS_CSS[name as keyof typeof API_DEPS_CSS]
fxy060608's avatar
fxy060608 已提交
152 153 154 155 156 157
      styles.forEach((style) => {
        if (!cssFiles.includes(style)) {
          cssFiles.push(style)
        }
      })
    })
fxy060608's avatar
fxy060608 已提交
158
  }
fxy060608's avatar
fxy060608 已提交
159
  return cssFiles.map((file) => `import '${file}'`).join('\n')
fxy060608's avatar
fxy060608 已提交
160 161
}

fxy060608's avatar
fxy060608 已提交
162
function generatePageDefineCode(pageOptions: UniApp.PagesJsonPageOptions) {
fxy060608's avatar
fxy060608 已提交
163
  return `const ${normalizePageIdentifier(
fxy060608's avatar
fxy060608 已提交
164
    pageOptions.path
fxy060608's avatar
fxy060608 已提交
165 166
  )} = defineAsyncComponent(extend({loader:()=>import('./${
    pageOptions.path
fxy060608's avatar
fxy060608 已提交
167
  }?mpType=page')},AsyncComponentOptions))`
fxy060608's avatar
fxy060608 已提交
168 169
}

fxy060608's avatar
fxy060608 已提交
170 171 172 173 174 175 176 177 178 179 180 181
function generatePagesDefineCode(
  pagesJson: UniApp.PagesJson,
  config: ResolvedConfig
) {
  const define = config.define! as FEATURE_DEFINES
  if (!define.__UNI_FEATURE_PAGES__) {
    // single page
    const pagePath = pagesJson.pages[0].path
    return `import {default as ${normalizePageIdentifier(
      pagePath
    )}} from './${pagePath}.vue?mpType=page'`
  }
fxy060608's avatar
fxy060608 已提交
182 183 184 185 186 187 188 189 190 191 192
  const { pages } = pagesJson
  return (
    `const AsyncComponentOptions = {
  loadingComponent: AsyncLoadingComponent,
  errorComponent: AsyncErrorComponent,
  delay: async.delay,
  timeout: async.timeout,
  suspensible: async.suspensible
}
` + pages.map((pageOptions) => generatePageDefineCode(pageOptions)).join('\n')
  )
fxy060608's avatar
fxy060608 已提交
193 194
}

fxy060608's avatar
fxy060608 已提交
195
function normalizePagesRoute(pagesJson: UniApp.PagesJson): PageRouteOptions[] {
fxy060608's avatar
fxy060608 已提交
196 197
  const firstPagePath = pagesJson.pages[0].path
  const tabBarList = (pagesJson.tabBar && pagesJson.tabBar.list) || []
fxy060608's avatar
fxy060608 已提交
198
  return pagesJson.pages.map((pageOptions) => {
fxy060608's avatar
fxy060608 已提交
199
    const path = pageOptions.path
fxy060608's avatar
fxy060608 已提交
200
    const name = normalizePageIdentifier(path)
201
    const isEntry = firstPagePath === path ? true : undefined
fxy060608's avatar
fxy060608 已提交
202 203 204
    const tabBarIndex = tabBarList.findIndex(
      (tabBarPage: { pagePath: string }) => tabBarPage.pagePath === path
    )
205
    const isTabBar = tabBarIndex !== -1 ? true : undefined
fxy060608's avatar
fxy060608 已提交
206 207

    let windowTop = 0
fxy060608's avatar
fxy060608 已提交
208 209
    const meta = Object.assign(
      {
fxy060608's avatar
fxy060608 已提交
210
        route: pageOptions.path,
fxy060608's avatar
fxy060608 已提交
211 212 213 214 215 216
        isQuit: isEntry || isTabBar ? true : undefined,
        isEntry,
        isTabBar,
        tabBarIndex,
        windowTop,
      },
fxy060608's avatar
fxy060608 已提交
217
      pageOptions.style
fxy060608's avatar
fxy060608 已提交
218
    )
fxy060608's avatar
fxy060608 已提交
219 220 221
    return {
      name,
      path: pageOptions.path,
222
      meta,
fxy060608's avatar
fxy060608 已提交
223 224 225 226
    }
  })
}

fxy060608's avatar
fxy060608 已提交
227
function generatePageRoute({ name, path, meta }: PageRouteOptions) {
fxy060608's avatar
fxy060608 已提交
228 229
  const { isEntry } = meta
  const alias = isEntry ? `\n  alias:'/${path}',` : ''
fxy060608's avatar
fxy060608 已提交
230
  return `{
fxy060608's avatar
fxy060608 已提交
231
  path:'/${isEntry ? '' : path}',${alias}
fxy060608's avatar
fxy060608 已提交
232
  component:{render(){return renderPage(${name})}},
233 234
  meta: ${JSON.stringify(meta)}
}`
fxy060608's avatar
fxy060608 已提交
235 236 237
}

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

fxy060608's avatar
fxy060608 已提交
241
function generateRoutes(globalName: string, pagesJson: UniApp.PagesJson) {
fxy060608's avatar
fxy060608 已提交
242 243 244 245
  return `
function renderPage(component){
  return (openBlock(), createBlock(PageComponent, null, {page: withCtx(() => [createVNode(component, { ref: "page" }, null, 512 /* NEED_PATCH */)]), _: 1 /* STABLE */}))
}
fxy060608's avatar
fxy060608 已提交
246
${globalName}.__uniRoutes=[${[
fxy060608's avatar
fxy060608 已提交
247
    ...generatePagesRoute(normalizePagesRoute(pagesJson)),
fxy060608's avatar
fxy060608 已提交
248
  ].join(',')}]`
fxy060608's avatar
fxy060608 已提交
249 250
}

fxy060608's avatar
fxy060608 已提交
251
function generateConfig(
fxy060608's avatar
fxy060608 已提交
252
  globalName: string,
fxy060608's avatar
fxy060608 已提交
253 254 255
  pagesJson: Record<string, any>,
  options: VitePluginUniResolvedOptions
) {
fxy060608's avatar
fxy060608 已提交
256 257 258
  delete pagesJson.pages
  delete pagesJson.subPackages
  delete pagesJson.subpackages
fxy060608's avatar
fxy060608 已提交
259 260
  pagesJson.compilerVersion = pkg['uni-app'].compilerVersion
  return (
fxy060608's avatar
fxy060608 已提交
261
    (options.command === 'serve'
fxy060608's avatar
fxy060608 已提交
262
      ? ''
fxy060608's avatar
fxy060608 已提交
263 264
      : `${globalName}['____'+appid+'____']=true
delete ${globalName}['____'+appid+'____']
fxy060608's avatar
fxy060608 已提交
265
`) +
fxy060608's avatar
fxy060608 已提交
266
    `${globalName}.__uniConfig=Object.assign(${JSON.stringify(pagesJson)},{
fxy060608's avatar
fxy060608 已提交
267 268 269 270 271 272 273 274 275 276
  async,
  debug,
  networkTimeout,
  sdkConfigs,
  qqMapKey,
  nvue,
  router
})
`
  )
fxy060608's avatar
fxy060608 已提交
277
}