提交 137f4572 编写于 作者: fxy060608's avatar fxy060608

feat: hmr

上级 ba4ca932
declare var tt: any declare var tt: any
declare var qa: any declare var qa: any
declare var swan: any declare var swan: any
declare var __PLATFORM__: declare var __PLATFORM__: UniApp.PLATFORM
| 'h5'
| 'app-plus'
| 'mp-alipay'
| 'mp-baidu'
| 'mp-qq'
| 'mp-toutiao'
| 'mp-weixin'
| 'quickapp-webview'
declare var __PLATFORM_PREFIX__: 'wx' | 'qq' | 'my' | 'swan' | 'tt' | 'qa' declare var __PLATFORM_PREFIX__: 'wx' | 'qq' | 'my' | 'swan' | 'tt' | 'qa'
declare var __GLOBAL__: Record<string, any> declare var __GLOBAL__: Record<string, any>
...@@ -22,6 +14,7 @@ declare var __VUE_OPTIONS_API__: boolean ...@@ -22,6 +14,7 @@ declare var __VUE_OPTIONS_API__: boolean
declare var __UNI_FEATURE_WX__: boolean declare var __UNI_FEATURE_WX__: boolean
declare var __UNI_FEATURE_WXS__: boolean declare var __UNI_FEATURE_WXS__: boolean
declare var __UNI_FEATURE_NVUE__: boolean
declare var __UNI_FEATURE_PROMISE__: boolean declare var __UNI_FEATURE_PROMISE__: boolean
declare var __UNI_FEATURE_LONGPRESS__: boolean declare var __UNI_FEATURE_LONGPRESS__: boolean
declare var __UNI_FEATURE_ROUTER_MODE__: 'hash' | 'history' declare var __UNI_FEATURE_ROUTER_MODE__: 'hash' | 'history'
......
declare namespace UniApp { declare namespace UniApp {
type PLATFORM =
| 'h5'
| 'app-plus'
| 'mp-alipay'
| 'mp-baidu'
| 'mp-qq'
| 'mp-toutiao'
| 'mp-weixin'
| 'quickapp-webview'
interface LayoutWindowOptions { interface LayoutWindowOptions {
matchMedia?: { matchMedia?: {
minWidth?: number minWidth?: number
...@@ -53,22 +62,35 @@ declare namespace UniApp { ...@@ -53,22 +62,35 @@ declare namespace UniApp {
refreshOptions?: PageRefreshOptions refreshOptions?: PageRefreshOptions
} }
interface PagesJsonPageStyle {
enablePullDownRefresh?: boolean
}
interface PagesJsonPageOptions { interface PagesJsonPageOptions {
path: string path: string
style?: Record<string, any> style?: PagesJsonPageStyle
} }
interface PagesJsonSubpackagesOptions { interface PagesJsonSubpackagesOptions {
root: string root: string
pages: PagesJsonPageOptions[] pages: PagesJsonPageOptions[]
} }
interface PagesJsonWindowOptions extends PagesJsonPageOptions {
matchMedia: {
minWidth: number
}
}
interface PagesJson { interface PagesJson {
pages: PagesJsonPageOptions[] pages: PagesJsonPageOptions[]
subpackages?: PagesJsonSubpackagesOptions[] subpackages?: PagesJsonSubpackagesOptions[]
subPackages?: PagesJsonSubpackagesOptions[] subPackages?: PagesJsonSubpackagesOptions[]
globalStyle?: {} globalStyle?: PagesJsonPageStyle
tabBar?: { tabBar?: {
list: [] list: []
} }
topWindow?: PagesJsonWindowOptions
leftWindow?: PagesJsonWindowOptions
rightWindow?: PagesJsonWindowOptions
} }
} }
...@@ -987,12 +987,12 @@ var AppComponent = defineComponent({ ...@@ -987,12 +987,12 @@ var AppComponent = defineComponent({
setup() { setup() {
useCssVar(); useCssVar();
useAppLifecycle(); useAppLifecycle();
const {appClass, onLayoutChange} = useAppClass(); const {clazz, onChange: onChange2} = useAppClass();
return () => (openBlock(), createBlock("uni-app", { return () => (openBlock(), createBlock("uni-app", {
class: appClass.value class: clazz.value
}, [ }, [
createVNode(Layout, { createVNode(Layout, {
onChange: onLayoutChange onChange: onChange2
}, null, 8, ["onChange"]) }, null, 8, ["onChange"])
], 2)); ], 2));
} }
...@@ -1014,44 +1014,27 @@ function useAppLifecycle() { ...@@ -1014,44 +1014,27 @@ function useAppLifecycle() {
function useAppClass() { function useAppClass() {
const showTabBar = ref(false); const showTabBar = ref(false);
const showMaxWidth = ref(false); const showMaxWidth = ref(false);
function onLayoutChange(type, value) { function onChange2(type, value) {
if (type === "showTabBar") { if (type === "showTabBar") {
showTabBar.value = value; showTabBar.value = value;
} else if (type === "showMaxWidth") { } else if (type === "showMaxWidth") {
showMaxWidth.value = value; showMaxWidth.value = value;
} }
} }
const appClass = computed(() => { const clazz = computed(() => {
return { return {
"uni-app--showtabbar": showTabBar.value, "uni-app--showtabbar": showTabBar.value,
"uni-app--maxwidth": showMaxWidth.value "uni-app--maxwidth": showMaxWidth.value
}; };
}); });
return { return {
appClass, clazz,
onLayoutChange onChange: onChange2
}; };
} }
const _sfc_main$r = {};
const _hoisted_1$d = /* @__PURE__ */ createVNode("uni-top-window", null, null, -1);
function _sfc_render$p(_ctx, _cache) {
const _component_router_view = resolveComponent("router-view");
return openBlock(), createBlock("uni-layout", null, [
_hoisted_1$d,
createVNode("uni-content", null, [
createVNode("uni-main", null, [
(openBlock(), createBlock(KeepAlive, null, [
createVNode(_component_router_view)
], 1024))
])
])
]);
}
_sfc_main$r.render = _sfc_render$p;
function initSystemComponents(app) { function initSystemComponents(app) {
AppComponent.name = COMPONENT_NAME_PREFIX + AppComponent.name; AppComponent.name = COMPONENT_NAME_PREFIX + AppComponent.name;
app.component(AppComponent.name, AppComponent); app.component(AppComponent.name, AppComponent);
app.component(_sfc_main$r.name, _sfc_main$r);
} }
function initMixin(app) { function initMixin(app) {
app.mixin({ app.mixin({
......
...@@ -25,20 +25,20 @@ export default defineComponent({ ...@@ -25,20 +25,20 @@ export default defineComponent({
setup() { setup() {
useCssVar() useCssVar()
useAppLifecycle() useAppLifecycle()
const { appClass, onLayoutChange } = useAppClass() const { clazz, onChange } = useAppClass()
return () => ( return () => (
openBlock(), openBlock(),
createBlock( createBlock(
'uni-app', 'uni-app',
{ {
class: appClass.value, class: clazz.value,
}, },
[ [
createVNode( createVNode(
Layout, Layout,
{ {
onChange: onLayoutChange, onChange,
}, },
null, null,
8 /* PROPS */, 8 /* PROPS */,
...@@ -70,21 +70,21 @@ function useAppLifecycle() { ...@@ -70,21 +70,21 @@ function useAppLifecycle() {
function useAppClass() { function useAppClass() {
const showTabBar = ref(false) const showTabBar = ref(false)
const showMaxWidth = ref(false) const showMaxWidth = ref(false)
function onLayoutChange(type: string, value: boolean) { function onChange(type: string, value: boolean) {
if (type === 'showTabBar') { if (type === 'showTabBar') {
showTabBar.value = value showTabBar.value = value
} else if (type === 'showMaxWidth') { } else if (type === 'showMaxWidth') {
showMaxWidth.value = value showMaxWidth.value = value
} }
} }
const appClass = computed(() => { const clazz = computed(() => {
return { return {
'uni-app--showtabbar': showTabBar.value, 'uni-app--showtabbar': showTabBar.value,
'uni-app--maxwidth': showMaxWidth.value, 'uni-app--maxwidth': showMaxWidth.value,
} }
}) })
return { return {
appClass, clazz,
onLayoutChange, onChange,
} }
} }
...@@ -3,27 +3,9 @@ import { App } from 'vue' ...@@ -3,27 +3,9 @@ import { App } from 'vue'
import { COMPONENT_NAME_PREFIX } from '@dcloudio/uni-shared' import { COMPONENT_NAME_PREFIX } from '@dcloudio/uni-shared'
import AppComponent from '../components/app/index' import AppComponent from '../components/app/index'
// import PageComponent from '../components/page/index.vue'
// import AsyncErrorComponent from '../components/async-error/index.vue'
// import AsyncLoadingComponent from '../components/async-loading/index.vue'
import LayoutComponent from '../components/app/test.vue'
export function initSystemComponents(app: App) { export function initSystemComponents(app: App) {
// @ts-ignore // @ts-ignore
AppComponent.name = COMPONENT_NAME_PREFIX + AppComponent.name AppComponent.name = COMPONENT_NAME_PREFIX + AppComponent.name
// @ts-ignore
// PageComponent.name = COMPONENT_NAME_PREFIX + PageComponent.name
// @ts-ignore
// AsyncErrorComponent.name = COMPONENT_NAME_PREFIX + AsyncErrorComponent.name
// @ts-ignore
// AsyncLoadingComponent.name =
// COMPONENT_NAME_PREFIX + AsyncLoadingComponent.name
app.component(AppComponent.name, AppComponent) app.component(AppComponent.name, AppComponent)
// app.component(PageComponent.name, PageComponent)
// app.component(AsyncErrorComponent.name, AsyncErrorComponent)
// app.component(AsyncLoadingComponent.name, AsyncLoadingComponent)
app.component(LayoutComponent.name, LayoutComponent)
} }
import fs from 'fs'
import path from 'path'
import { parse } from 'jsonc-parser'
import { ConfigEnv, UserConfig } from 'vite' import { ConfigEnv, UserConfig } from 'vite'
import { VitePluginUniResolvedOptions } from '..' import { VitePluginUniResolvedOptions } from '..'
import { getFeatures } from '../utils'
interface ProjectFeatures {}
interface PagesFeatures {
pages: boolean
tabBar: boolean
topWindow: boolean
leftWindow: boolean
rightWindow: boolean
pullDownRefresh: boolean
}
interface ManifestFeatures {
wx: boolean
wxs: boolean
promise: boolean
longpress: boolean
routerMode: '"hash"' | '"history"'
}
function resolveProjectFeature(inputDir: string, { command }: ConfigEnv) {
const features: ProjectFeatures = {}
if (command === 'build') {
}
return features
}
function resolvePagesFeature(
inputDir: string,
{ command }: ConfigEnv
): PagesFeatures {
const features: PagesFeatures = {
pages: true, // 是否多页面
tabBar: true, // 是否启用tabBar
topWindow: false, // 是否启用topWindow
leftWindow: false, // 是否启用leftWindow
rightWindow: false, // 是否启用rightWindow
pullDownRefresh: false, // 是否启用下拉刷新
}
const {
tabBar,
pages,
topWindow,
leftWindow,
rightWindow,
globalStyle,
} = parse(fs.readFileSync(path.join(inputDir, 'pages.json'), 'utf8'))
if (pages && pages.length === 1) {
features.pages = false
}
if (!(tabBar && tabBar.list && tabBar.list.length)) {
features.tabBar = false
}
if (topWindow && topWindow.path) {
features.topWindow = true
}
if (leftWindow && leftWindow.path) {
features.leftWindow = true
}
if (rightWindow && rightWindow.path) {
features.rightWindow = true
}
if (globalStyle && globalStyle.enablePullDownRefresh) {
features.pullDownRefresh = true
} else {
if (
pages.find(
(page: { style: { enablePullDownRefresh: any } }) =>
page.style && page.style.enablePullDownRefresh
)
) {
features.pullDownRefresh = true
}
}
return features
}
function resolveManifestFeature(inputDir: string): ManifestFeatures {
const features: ManifestFeatures = {
wx: true, // 是否启用小程序的组件实例 API,如:selectComponent 等(uni-core/src/service/plugin/appConfig)
wxs: true, // 是否启用 wxs 支持,如:getComponentDescriptor 等(uni-core/src/view/plugin/appConfig)
promise: false, // 是否启用旧版本的 promise 支持(即返回[err,res]的格式)
longpress: true, // 是否启用longpress
routerMode: '"hash"', // 启用的 router 类型(uni-h5/src/framework/plugin/router)
}
const manifest = parse(
fs.readFileSync(path.join(inputDir, 'manifest.json'), 'utf8')
)
if (
manifest.h5 &&
manifest.h5.router &&
manifest.h5.router.mode === 'history'
) {
features.routerMode = '"history"'
}
// TODO manifest.json features
return features
}
export function createDefine( export function createDefine(
{ inputDir }: VitePluginUniResolvedOptions, options: VitePluginUniResolvedOptions,
env: ConfigEnv env: ConfigEnv
): UserConfig['define'] { ): UserConfig['define'] {
const { return getFeatures(options, env.command)
wx,
wxs,
pages,
tabBar,
promise,
longpress,
routerMode,
topWindow,
leftWindow,
rightWindow,
pullDownRefresh,
} = Object.assign(
resolveManifestFeature(inputDir),
resolvePagesFeature(inputDir, env),
resolveProjectFeature(inputDir, env)
)
return {
__UNI_FEATURE_WX__: wx,
__UNI_FEATURE_WXS__: wxs,
__UNI_FEATURE_PROMISE__: promise,
__UNI_FEATURE_LONGPRESS__: longpress,
__UNI_FEATURE_ROUTER_MODE__: routerMode,
__UNI_FEATURE_PAGES__: pages,
__UNI_FEATURE_TABBAR__: tabBar,
__UNI_FEATURE_TOPWINDOW__: topWindow,
__UNI_FEATURE_LEFTWINDOW__: leftWindow,
__UNI_FEATURE_RIGHTWINDOW__: rightWindow,
__UNI_FEATURE_RESPONSIVE__: topWindow || leftWindow || rightWindow,
__UNI_FEATURE_PULL_DOWN_REFRESH__: pullDownRefresh,
}
} }
...@@ -10,6 +10,6 @@ export function createConfigResolved(options: VitePluginUniResolvedOptions) { ...@@ -10,6 +10,6 @@ export function createConfigResolved(options: VitePluginUniResolvedOptions) {
options.inputDir = path.resolve(config.root, 'src') options.inputDir = path.resolve(config.root, 'src')
options.assetsDir = config.build.assetsDir options.assetsDir = config.build.assetsDir
resolvePlugins(config.command, config.plugins as Plugin[], options) resolvePlugins(config, options)
}) as Plugin['configResolved'] }) as Plugin['configResolved']
} }
...@@ -13,7 +13,7 @@ export function uniAppVuePlugin(options: VitePluginUniResolvedOptions): Plugin { ...@@ -13,7 +13,7 @@ export function uniAppVuePlugin(options: VitePluginUniResolvedOptions): Plugin {
//App.vue main request //App.vue main request
if (filename === appVuePath && !query.vue) { if (filename === appVuePath && !query.vue) {
return { return {
code: `<template><VUniApp ref="app"/></template><style src="@dcloudio/uni-h5/style/base.css"/>${code}`, code: `<template><VUniApp/></template>${code}`,
map: this.getCombinedSourcemap(), map: this.getCombinedSourcemap(),
} }
} }
......
...@@ -57,10 +57,11 @@ const uniInjectPluginOptions: Partial<InjectOptions> = { ...@@ -57,10 +57,11 @@ const uniInjectPluginOptions: Partial<InjectOptions> = {
} }
export function resolvePlugins( export function resolvePlugins(
command: ResolvedConfig['command'], config: ResolvedConfig,
plugins: Plugin[],
options: VitePluginUniResolvedOptions options: VitePluginUniResolvedOptions
) { ) {
const command = config.command
const plugins = config.plugins as Plugin[]
addPlugin( addPlugin(
plugins, plugins,
uniPrePlugin(Object.assign(uniPrePluginOptions, options)), uniPrePlugin(Object.assign(uniPrePluginOptions, options)),
...@@ -69,7 +70,7 @@ export function resolvePlugins( ...@@ -69,7 +70,7 @@ export function resolvePlugins(
) )
addPlugin(plugins, uniAppVuePlugin(options), 1, 'pre') addPlugin(plugins, uniAppVuePlugin(options), 1, 'pre')
addPlugin(plugins, uniMainJsPlugin(options), 1, 'pre') addPlugin(plugins, uniMainJsPlugin(options), 1, 'pre')
addPlugin(plugins, uniPagesJsonPlugin(options), 1, 'pre') addPlugin(plugins, uniPagesJsonPlugin(config, options), 1, 'pre')
addPlugin(plugins, uniManifestJsonPlugin(options), 1, 'pre') addPlugin(plugins, uniManifestJsonPlugin(options), 1, 'pre')
addPlugin( addPlugin(
......
import fs from 'fs' import fs from 'fs'
import path from 'path' import path from 'path'
import slash from 'slash' import slash from 'slash'
import { Plugin } from 'vite' import { Plugin, ResolvedConfig } from 'vite'
import { parse } from 'jsonc-parser' import { parse } from 'jsonc-parser'
import { hasOwn, camelize, capitalize, isPlainObject } from '@vue/shared' import { camelize, capitalize } from '@vue/shared'
import { parseJson } from '@dcloudio/uni-cli-shared'
import { VitePluginUniResolvedOptions } from '../..' import { VitePluginUniResolvedOptions } from '../..'
import { FEATURE_DEFINES, normalizePagesJson } from '../../utils'
const pkg = require('@dcloudio/vite-plugin-uni/package.json') const pkg = require('@dcloudio/vite-plugin-uni/package.json')
const PAGES_JSON_JS = 'pages.json.js' const PAGES_JSON_JS = 'pages.json.js'
export function uniPagesJsonPlugin( export function uniPagesJsonPlugin(
config: ResolvedConfig,
options: VitePluginUniResolvedOptions options: VitePluginUniResolvedOptions
): Plugin { ): Plugin {
const pagesJsonPath = slash(path.join(options.inputDir, 'pages.json')) const pagesJsonPath = slash(path.join(options.inputDir, 'pages.json'))
...@@ -27,7 +28,7 @@ export function uniPagesJsonPlugin( ...@@ -27,7 +28,7 @@ export function uniPagesJsonPlugin(
return { return {
code: code:
(options.command === 'serve' ? registerGlobalCode : '') + (options.command === 'serve' ? registerGlobalCode : '') +
parsePagesJson(code, options), parsePagesJson(code, config, options),
map: { mappings: '' }, map: { mappings: '' },
} }
} }
...@@ -48,20 +49,23 @@ interface PageRouteOptions { ...@@ -48,20 +49,23 @@ interface PageRouteOptions {
function parsePagesJson( function parsePagesJson(
jsonStr: string, jsonStr: string,
config: ResolvedConfig,
options: VitePluginUniResolvedOptions options: VitePluginUniResolvedOptions
) { ) {
const pagesJson = formatPagesJson(jsonStr) const pagesJson = normalizePagesJson(jsonStr, options.platform)
const definePagesCode = generatePagesDefineCode(pagesJson) const definePagesCode = generatePagesDefineCode(pagesJson)
const uniRoutesCode = generateRoutes(pagesJson) const uniRoutesCode = generateRoutes(pagesJson)
const uniConfigCode = generateConfig(pagesJson, options) const uniConfigCode = generateConfig(pagesJson, options)
const manifestJsonPath = slash( const manifestJsonPath = slash(
path.resolve(options.inputDir, 'manifest.json.js') path.resolve(options.inputDir, 'manifest.json.js')
) )
const cssCode = generateCssCode(config)
return ` return `
import { defineAsyncComponent, resolveComponent, createVNode, withCtx, openBlock, createBlock } from 'vue' import { defineAsyncComponent, resolveComponent, createVNode, withCtx, openBlock, createBlock } from 'vue'
import { PageComponent, AsyncLoadingComponent, AsyncErrorComponent } from '@dcloudio/uni-h5' import { PageComponent, AsyncLoadingComponent, AsyncErrorComponent } from '@dcloudio/uni-h5'
import { appid, debug, networkTimeout, router, async, sdkConfigs, qqMapKey, nvue } from '${manifestJsonPath}' import { appid, debug, networkTimeout, router, async, sdkConfigs, qqMapKey, nvue } from '${manifestJsonPath}'
${cssCode}
${uniConfigCode} ${uniConfigCode}
${definePagesCode} ${definePagesCode}
${uniRoutesCode} ${uniRoutesCode}
...@@ -83,126 +87,24 @@ window.UniViewJSBridge = UniViewJSBridge ...@@ -83,126 +87,24 @@ window.UniViewJSBridge = UniViewJSBridge
window.UniServiceJSBridge = UniServiceJSBridge window.UniServiceJSBridge = UniServiceJSBridge
` `
function formatPages(pagesJson: Record<string, any>, jsonStr: string) { function normalizePageIdentifier(path: string) {
if (!Array.isArray(pagesJson.pages)) { return capitalize(camelize(path.replace(/\//g, '-')))
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']
Object.keys(globalStyle).forEach((name) => {
if (name.startsWith('mp-') || name.startsWith('quickapp')) {
delete globalStyle[name]
}
})
return globalStyle
}
const navigationBarMaps = {
navigationBarBackgroundColor: 'backgroundColor',
navigationBarTextStyle: 'textStyle',
navigationBarTitleText: 'titleText',
navigationStyle: 'style',
titleImage: 'titleImage',
titlePenetrate: 'titlePenetrate',
}
function normalizeNavigationBar(
pageStyle: Record<string, any>
): UniApp.PageNavigationBar {
const navigationBar = Object.create(null) as UniApp.PageNavigationBar
Object.keys(navigationBarMaps).forEach((name) => {
if (hasOwn(pageStyle, name)) {
// @ts-ignore
navigationBar[navigationBarMaps[name]] = pageStyle[name]
delete pageStyle[name]
}
})
if (
pageStyle.navigationBarShadow &&
pageStyle.navigationBarShadow.colorType
) {
navigationBar.shadowColorType = pageStyle.navigationBarShadow.colorType
delete pageStyle.navigationBarShadow
}
const { titleNView } = pageStyle
if (isPlainObject(titleNView)) {
Object.assign(navigationBar, titleNView)
}
return navigationBar
}
function normalizePageStyle(pageStyle: Record<string, any>) {
pageStyle.navigationBar = normalizeNavigationBar(pageStyle)
return pageStyle
}
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)
}
return normalizePageStyle(removePlatformStyle(pageStyle))
}
return {}
}
function formatSubpackages(subpackages?: UniApp.PagesJsonSubpackagesOptions[]) {
const pages: UniApp.PagesJsonPageOptions[] = []
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) { function generateCssCode(config: ResolvedConfig) {
let pagesJson: UniApp.PagesJson = { const define = config.define! as FEATURE_DEFINES
pages: [], const cssFiles = ['@dcloudio/uni-h5/style/base.css']
if (define.__UNI_FEATURE_PAGES__) {
cssFiles.push('@dcloudio/uni-h5/style/layout.css')
} }
// preprocess if (define.__UNI_FEATURE_NVUE__) {
try { cssFiles.push('@dcloudio/uni-h5/style/nvue.css')
pagesJson = parseJson(jsonStr, true)
} catch (e) {
console.error(`[vite] Error: pages.json parse failed.\n`, jsonStr, e)
} }
// pages return cssFiles.map((file) => `import '${file}'`).join('\n')
formatPages(pagesJson, jsonStr)
// subpackages
pagesJson.pages.push(
...formatSubpackages(pagesJson.subPackages || pagesJson.subpackages)
)
// globalStyle
formatPageStyle(pagesJson.globalStyle)
return pagesJson
}
function formatPageIdentifier(path: string) {
return capitalize(camelize(path.replace(/\//g, '-')))
} }
function generatePageDefineCode(pageOptions: UniApp.PagesJsonPageOptions) { function generatePageDefineCode(pageOptions: UniApp.PagesJsonPageOptions) {
return `const ${formatPageIdentifier( return `const ${normalizePageIdentifier(
pageOptions.path pageOptions.path
)} = defineAsyncComponent({ )} = defineAsyncComponent({
loader: () => import('./${pageOptions.path}.vue?mpType=page'), loader: () => import('./${pageOptions.path}.vue?mpType=page'),
...@@ -220,12 +122,12 @@ function generatePagesDefineCode(pagesJson: UniApp.PagesJson) { ...@@ -220,12 +122,12 @@ function generatePagesDefineCode(pagesJson: UniApp.PagesJson) {
.join('\n') .join('\n')
} }
function formatPagesRoute(pagesJson: UniApp.PagesJson): PageRouteOptions[] { function normalizePagesRoute(pagesJson: UniApp.PagesJson): PageRouteOptions[] {
const firstPagePath = pagesJson.pages[0].path const firstPagePath = pagesJson.pages[0].path
const tabBarList = (pagesJson.tabBar && pagesJson.tabBar.list) || [] const tabBarList = (pagesJson.tabBar && pagesJson.tabBar.list) || []
return pagesJson.pages.map((pageOptions) => { return pagesJson.pages.map((pageOptions) => {
const path = pageOptions.path const path = pageOptions.path
const name = formatPageIdentifier(path) const name = normalizePageIdentifier(path)
const isEntry = firstPagePath === path ? true : undefined const isEntry = firstPagePath === path ? true : undefined
const tabBarIndex = tabBarList.findIndex( const tabBarIndex = tabBarList.findIndex(
(tabBarPage: { pagePath: string }) => tabBarPage.pagePath === path (tabBarPage: { pagePath: string }) => tabBarPage.pagePath === path
...@@ -241,7 +143,7 @@ function formatPagesRoute(pagesJson: UniApp.PagesJson): PageRouteOptions[] { ...@@ -241,7 +143,7 @@ function formatPagesRoute(pagesJson: UniApp.PagesJson): PageRouteOptions[] {
tabBarIndex, tabBarIndex,
windowTop, windowTop,
}, },
formatPageStyle(pageOptions.style) pageOptions.style
) )
return { return {
...@@ -271,7 +173,7 @@ function generatePagesRoute(pagesRouteOptions: PageRouteOptions[]) { ...@@ -271,7 +173,7 @@ function generatePagesRoute(pagesRouteOptions: PageRouteOptions[]) {
function generateRoutes(pagesJson: UniApp.PagesJson) { function generateRoutes(pagesJson: UniApp.PagesJson) {
return `window.__uniRoutes=[${[ return `window.__uniRoutes=[${[
`{ path: '/${pagesJson.pages[0].path}', redirect: '/' }`, `{ path: '/${pagesJson.pages[0].path}', redirect: '/' }`,
...generatePagesRoute(formatPagesRoute(pagesJson)), ...generatePagesRoute(normalizePagesRoute(pagesJson)),
].join(',')}]` ].join(',')}]`
} }
......
import path from 'path'
import debug from 'debug' import debug from 'debug'
import { Plugin } from 'vite' import slash from 'slash'
import { ModuleGraph, Plugin } from 'vite'
import { VitePluginUniResolvedOptions } from '..' import { VitePluginUniResolvedOptions } from '..'
import { getFeatures } from '../utils'
const debugHmr = debug('uni:hmr') const debugHmr = debug('uni:hmr')
async function invalidate(file: string, moduleGraph: ModuleGraph) {
const mod = await moduleGraph.getModuleById(slash(file))
if (mod) {
debugHmr('invalidate', mod.id)
moduleGraph.invalidateModule(mod)
}
}
export function createHandleHotUpdate( export function createHandleHotUpdate(
_options: VitePluginUniResolvedOptions options: VitePluginUniResolvedOptions
): Plugin['handleHotUpdate'] { ): Plugin['handleHotUpdate'] {
return function ({ file, server }) { return async function ({ file, server }) {
// TODO 目前简单处理,当pages.json,manifest.json发生变化,就直接刷新,理想情况下,应该区分变化的内容,仅必要时做整页面刷新 // TODO 目前简单处理,当pages.json,manifest.json发生变化,就直接刷新,理想情况下,应该区分变化的内容,仅必要时做整页面刷新
if (file.endsWith('pages.json') || file.endsWith('manifest.json')) { const isPagesJson = file.endsWith('pages.json')
const isManifestJson = file.endsWith('manifest.json')
if (isPagesJson || isManifestJson) {
debugHmr(file) debugHmr(file)
server.ws.send({ server.ws.send({
type: 'custom', type: 'custom',
event: 'invalidate', event: 'invalidate',
data: {}, data: {},
}) })
// 更新define
Object.assign(
server.config.define!,
getFeatures(options, server.config.command)
)
debugHmr('define', server.config.define)
// 当pages.json,manifest.json发生变化时,作废pages.json.js缓存
await invalidate(
path.resolve(options.inputDir, 'pages.json.js'),
server.moduleGraph
)
if (isManifestJson) {
await invalidate(
path.resolve(options.inputDir, 'manifest.json.js'),
server.moduleGraph
)
}
return [] return []
} }
} }
......
...@@ -17,6 +17,7 @@ export interface VitePluginUniResolvedOptions extends VitePluginUniOptions { ...@@ -17,6 +17,7 @@ export interface VitePluginUniResolvedOptions extends VitePluginUniOptions {
root: string root: string
base: string base: string
command: ResolvedConfig['command'] command: ResolvedConfig['command']
platform: UniApp.PLATFORM
inputDir: string inputDir: string
assetsDir: string assetsDir: string
devServer?: ViteDevServer devServer?: ViteDevServer
...@@ -42,6 +43,7 @@ export default function uniPlugin( ...@@ -42,6 +43,7 @@ export default function uniPlugin(
assetsDir: 'assets', assetsDir: 'assets',
inputDir, inputDir,
command: 'serve', command: 'serve',
platform: 'h5',
} }
initEnv(options) initEnv(options)
return { return {
......
import fs from 'fs'
import path from 'path'
import { ConfigEnv } from 'vite'
import { parse } from 'jsonc-parser'
import { VitePluginUniResolvedOptions } from '..'
import { normalizePagesJson } from './pagesJson'
interface ProjectFeatures {}
interface PagesFeatures {
nvue: boolean
pages: boolean
tabBar: boolean
topWindow: boolean
leftWindow: boolean
rightWindow: boolean
pullDownRefresh: boolean
}
interface ManifestFeatures {
wx: boolean
wxs: boolean
promise: boolean
longpress: boolean
routerMode: '"hash"' | '"history"'
}
function resolveProjectFeature(
options: VitePluginUniResolvedOptions,
command: ConfigEnv['command']
) {
const features: ProjectFeatures = {}
if (command === 'build') {
}
return features
}
function resolvePagesFeature(
options: VitePluginUniResolvedOptions,
command: ConfigEnv['command']
): PagesFeatures {
const features: PagesFeatures = {
nvue: true, // 是否启用nvue
pages: true, // 是否多页面
tabBar: true, // 是否启用tabBar
topWindow: false, // 是否启用topWindow
leftWindow: false, // 是否启用leftWindow
rightWindow: false, // 是否启用rightWindow
pullDownRefresh: false, // 是否启用下拉刷新
}
const {
tabBar,
pages,
topWindow,
leftWindow,
rightWindow,
globalStyle,
} = normalizePagesJson(
fs.readFileSync(path.join(options.inputDir, 'pages.json'), 'utf8'),
options.platform
)
if (pages && pages.length === 1) {
features.pages = false
}
if (!(tabBar && tabBar.list && tabBar.list.length)) {
features.tabBar = false
}
if (topWindow && topWindow.path) {
features.topWindow = true
}
if (leftWindow && leftWindow.path) {
features.leftWindow = true
}
if (rightWindow && rightWindow.path) {
features.rightWindow = true
}
if (globalStyle && globalStyle.enablePullDownRefresh) {
features.pullDownRefresh = true
} else {
if (pages.find((page) => page.style && page.style.enablePullDownRefresh)) {
features.pullDownRefresh = true
}
}
if (command === 'build') {
if (
!pages.find((page) =>
fs.existsSync(path.resolve(options.inputDir, page.path, '.nvue'))
)
) {
features.nvue = false
}
}
return features
}
function resolveManifestFeature(
options: VitePluginUniResolvedOptions
): ManifestFeatures {
const features: ManifestFeatures = {
wx: true, // 是否启用小程序的组件实例 API,如:selectComponent 等(uni-core/src/service/plugin/appConfig)
wxs: true, // 是否启用 wxs 支持,如:getComponentDescriptor 等(uni-core/src/view/plugin/appConfig)
promise: false, // 是否启用旧版本的 promise 支持(即返回[err,res]的格式)
longpress: true, // 是否启用longpress
routerMode: '"hash"', // 启用的 router 类型(uni-h5/src/framework/plugin/router)
}
const manifest = parse(
fs.readFileSync(path.join(options.inputDir, 'manifest.json'), 'utf8')
)
if (
manifest.h5 &&
manifest.h5.router &&
manifest.h5.router.mode === 'history'
) {
features.routerMode = '"history"'
}
// TODO manifest.json features
return features
}
export type FEATURE_DEFINES = ReturnType<typeof getFeatures>
export function getFeatures(
options: VitePluginUniResolvedOptions,
command: ConfigEnv['command']
) {
const {
wx,
wxs,
nvue,
pages,
tabBar,
promise,
longpress,
routerMode,
topWindow,
leftWindow,
rightWindow,
pullDownRefresh,
} = Object.assign(
resolveManifestFeature(options),
resolvePagesFeature(options, command),
resolveProjectFeature(options, command)
)
return {
__UNI_FEATURE_WX__: wx,
__UNI_FEATURE_WXS__: wxs,
__UNI_FEATURE_NVUE__: nvue,
__UNI_FEATURE_PROMISE__: promise,
__UNI_FEATURE_LONGPRESS__: longpress,
__UNI_FEATURE_ROUTER_MODE__: routerMode,
__UNI_FEATURE_PAGES__: pages,
__UNI_FEATURE_TABBAR__: tabBar,
__UNI_FEATURE_TOPWINDOW__: topWindow,
__UNI_FEATURE_LEFTWINDOW__: leftWindow,
__UNI_FEATURE_RIGHTWINDOW__: rightWindow,
__UNI_FEATURE_RESPONSIVE__: topWindow || leftWindow || rightWindow,
__UNI_FEATURE_PULL_DOWN_REFRESH__: pullDownRefresh,
}
}
export * from './define'
export * from './pagesJson'
import path from 'path'
import slash from 'slash'
import { hasOwn, isPlainObject } from '@vue/shared'
import { parseJson } from '@dcloudio/uni-cli-shared'
export function normalizePagesJson(jsonStr: string, platform: UniApp.PLATFORM) {
let pagesJson: UniApp.PagesJson = {
pages: [],
}
// preprocess
try {
pagesJson = parseJson(jsonStr, true)
} catch (e) {
console.error(`[vite] Error: pages.json parse failed.\n`, jsonStr, e)
}
// pages
validatePages(pagesJson, jsonStr)
// subpackages
pagesJson.pages.push(
...normalizeSubpackages(pagesJson.subPackages || pagesJson.subpackages)
)
// pageStyle
normalizePages(pagesJson.pages, platform)
// globalStyle
pagesJson.globalStyle = normalizePageStyle(pagesJson.globalStyle!, platform)
return pagesJson
}
function validatePages(pagesJson: Record<string, any>, jsonStr: string) {
if (!Array.isArray(pagesJson.pages)) {
pagesJson.pages = []
console.error(`[uni-app] Error: pages.json->pages parse failed.\n`, jsonStr)
} else if (!pagesJson.pages.length) {
console.error(
`[uni-app] Error: pages.json->pages must contain at least 1 page.\n`,
jsonStr
)
}
}
function normalizePages(
pages: UniApp.PagesJsonPageOptions[],
platform: UniApp.PLATFORM
) {
pages.forEach((page) => {
page.style = normalizePageStyle(page.style!, platform)
})
return pages
}
function normalizeSubpackages(
subpackages?: UniApp.PagesJsonSubpackagesOptions[]
) {
const pages: UniApp.PagesJsonPageOptions[] = []
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 normalizePageStyle(
pageStyle: Record<string, any>,
platform: UniApp.PLATFORM
) {
if (pageStyle) {
if (platform === 'h5') {
Object.assign(pageStyle, pageStyle['app-plus'] || {})
}
Object.assign(pageStyle, pageStyle[platform] || {})
if (['h5', 'app-plus'].includes(platform)) {
pageStyle.navigationBar = normalizeNavigationBar(pageStyle)
}
return removePlatformStyle(pageStyle)
}
return { navigationBar: {} }
}
const navigationBarMaps = {
navigationBarBackgroundColor: 'backgroundColor',
navigationBarTextStyle: 'textStyle',
navigationBarTitleText: 'titleText',
navigationStyle: 'style',
titleImage: 'titleImage',
titlePenetrate: 'titlePenetrate',
}
function normalizeNavigationBar(
pageStyle: Record<string, any>
): UniApp.PageNavigationBar {
const navigationBar = Object.create(null) as UniApp.PageNavigationBar
Object.keys(navigationBarMaps).forEach((name) => {
if (hasOwn(pageStyle, name)) {
// @ts-ignore
navigationBar[navigationBarMaps[name]] = pageStyle[name]
delete pageStyle[name]
}
})
if (
pageStyle.navigationBarShadow &&
pageStyle.navigationBarShadow.colorType
) {
navigationBar.shadowColorType = pageStyle.navigationBarShadow.colorType
delete pageStyle.navigationBarShadow
}
const { titleNView } = pageStyle
if (isPlainObject(titleNView)) {
Object.assign(navigationBar, titleNView)
}
return navigationBar
}
const platforms = ['h5', 'app-plus', 'mp-', 'quickapp']
function removePlatformStyle(pageStyle: Record<string, any>) {
Object.keys(pageStyle).forEach((name) => {
if (platforms.find((prefix) => name.startsWith(prefix))) {
delete pageStyle[name]
}
})
return pageStyle
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册