提交 2972d0c2 编写于 作者: fxy060608's avatar fxy060608

feat(easycom): support hmr

上级 22bc2dfa
......@@ -173,6 +173,12 @@ declare namespace UniApp {
topWindow?: PagesJsonWindowOptions
leftWindow?: PagesJsonWindowOptions
rightWindow?: PagesJsonWindowOptions
easycom?: {
autoscan?: boolean
custom?: {
[name: string]: string
}
}
}
interface TabBarItemBaseOptions {
......
......@@ -3,4 +3,4 @@ export * from './url'
export * from './json'
export * from './utils'
export * from './constants'
export * from './preprocess/index'
export * from './preprocess'
export * from './json'
export * from './pages'
export * from './manifest'
import { parse } from 'jsonc-parser'
import { preJson } from './preprocess'
import { preJson } from '../preprocess'
export function parseJson(jsonStr: string, shouldPre: boolean = false) {
return parse(shouldPre ? preJson(jsonStr) : jsonStr)
......
import fs from 'fs'
import path from 'path'
import { once } from '@dcloudio/uni-shared'
import { parseJson } from './json'
export const parseManifestJson = (inputDir: string) => {
return parseJson(
fs.readFileSync(path.join(inputDir, 'manifest.json'), 'utf8')
)
}
export const parseManifestJsonOnce = once(parseManifestJson)
import fs from 'fs'
import path from 'path'
import slash from 'slash'
import { extend, hasOwn, isArray, isPlainObject } from '@vue/shared'
import { parseJson } from '@dcloudio/uni-cli-shared'
import { TABBAR_HEIGHT } from '@dcloudio/uni-shared'
import { once, TABBAR_HEIGHT } from '@dcloudio/uni-shared'
import { parseJson } from './json'
export const parsePagesJson = (
inputDir: string,
platform: UniApp.PLATFORM,
normalize: boolean = true
) => {
const jsonStr = fs.readFileSync(path.join(inputDir, 'pages.json'), 'utf8')
if (normalize) {
return normalizePagesJson(jsonStr, platform)
}
return parseJson(jsonStr, true) as UniApp.PagesJson
}
export const parsePagesJsonOnce = once(parsePagesJson)
export function normalizePagesJson(jsonStr: string, platform: UniApp.PLATFORM) {
let pagesJson: UniApp.PagesJson = {
......
......@@ -3,5 +3,9 @@
"compilerOptions": {
"outDir": "dist"
},
"include": ["src"]
"include": [
"src",
"../shims-node.d.ts",
"../shims-uni-app.d.ts"
]
}
......@@ -185,13 +185,13 @@ function updateElementStyle(element, styles) {
}
function once(fn, ctx = null) {
let res;
return (...args) => {
return ((...args) => {
if (fn) {
res = fn.apply(ctx, args);
fn = null;
}
return res;
};
});
}
const encode = encodeURIComponent;
......
......@@ -49,7 +49,7 @@ export declare function normalizeTarget(el: HTMLElement): {
export declare const ON_REACH_BOTTOM_DISTANCE = 50;
export declare function once(fn: (...args: any[]) => any, ctx?: unknown): (...args: any[]) => any;
export declare function once<T extends (...args: any[]) => any>(fn: T, ctx?: unknown): T;
/**
* https://github.com/vuejs/vue-router-next/blob/master/src/query.ts
......
......@@ -181,13 +181,13 @@ function updateElementStyle(element, styles) {
}
function once(fn, ctx = null) {
let res;
return (...args) => {
return ((...args) => {
if (fn) {
res = fn.apply(ctx, args);
fn = null;
}
return res;
};
});
}
const encode = encodeURIComponent;
......
......@@ -23,13 +23,16 @@ export function updateElementStyle(
}
}
export function once(fn: (...args: any[]) => any, ctx: unknown = null) {
export function once<T extends (...args: any[]) => any>(
fn: T,
ctx: unknown = null
): T {
let res: any
return (...args: any[]) => {
return ((...args: any[]) => {
if (fn) {
res = fn.apply(ctx, args)
fn = null as any
}
return res
}
}) as T
}
......@@ -2,13 +2,13 @@ import path from 'path'
import slash from 'slash'
import { UserConfig } from 'vite'
import { VitePluginUniResolvedOptions } from '..'
import { FEATURE_DEFINES, initEasycoms } from '../utils'
import { FEATURE_DEFINES, initEasycomsOnce } from '../utils'
export function createBuild(
options: VitePluginUniResolvedOptions,
features: FEATURE_DEFINES
): UserConfig['build'] {
initEasycoms(options.inputDir)
initEasycomsOnce(options.inputDir, options.platform)
return {
polyfillDynamicImport: features.__UNI_FEATURE_PAGES__,
rollupOptions: {
......
import { ConfigEnv, UserConfig } from 'vite'
import { VitePluginUniResolvedOptions } from '..'
import { getFeatures } from '../utils'
import {
parsePagesJsonOnce,
parseManifestJsonOnce,
} from '@dcloudio/uni-cli-shared'
import { initFeatures } from '../utils'
export function createDefine(
options: VitePluginUniResolvedOptions,
env: ConfigEnv
{ inputDir, platform }: VitePluginUniResolvedOptions,
{ command }: ConfigEnv
): UserConfig['define'] {
return getFeatures(options, env.command)
return initFeatures({
inputDir,
command,
platform,
pagesJson: parsePagesJsonOnce(inputDir, platform),
manifestJson: parseManifestJsonOnce(inputDir),
})
}
......@@ -4,13 +4,13 @@ import slash from 'slash'
import { Plugin, ResolvedConfig } from 'vite'
import { parse } from 'jsonc-parser'
import { camelize, capitalize } from '@vue/shared'
import { normalizePagesJson } from '@dcloudio/uni-cli-shared'
import { VitePluginUniResolvedOptions } from '../..'
import {
BASE_COMPONENTS_STYLE_PATH,
FEATURE_DEFINES,
H5_API_STYLE_PATH,
H5_FRAMEWORK_STYLE_PATH,
normalizePagesJson,
} from '../../utils'
const pkg = require('@dcloudio/vite-plugin-uni/package.json')
......@@ -35,7 +35,7 @@ export function uniPagesJsonPlugin(
code:
(config.define!.__UNI_FEATURE_RPX__ ? registerGlobalCode : '') +
(options.command === 'serve' ? registerDevServerGlobalCode : '') +
parsePagesJson(code, config, options),
generatePagesJsonCode(code, config, options),
map: { mappings: '' },
}
}
......@@ -54,7 +54,7 @@ interface PageRouteOptions {
meta: Partial<UniApp.PageRouteMeta>
}
function parsePagesJson(
function generatePagesJsonCode(
jsonStr: string,
config: ResolvedConfig,
options: VitePluginUniResolvedOptions
......
import { ViteDevServer } from 'vite'
import { debounce } from '@dcloudio/uni-shared'
import { VitePluginUniResolvedOptions } from '..'
import { debugEasycom, initEasycoms } from '../utils'
import { debugEasycom, initEasycomsOnce } from '../utils'
export const serveEasycom = (
server: ViteDevServer,
options: VitePluginUniResolvedOptions
) => {
const { filter, refresh } = initEasycoms(options.inputDir)
const { filter, refresh } = initEasycomsOnce(
options.inputDir,
options.platform
)
const refreshEasycom = debounce(refresh, 100)
server.watcher.on('all', (eventName, path) => {
if (!['add', 'unlink'].includes(eventName)) {
......
......@@ -2,8 +2,11 @@ import path from 'path'
import debug from 'debug'
import slash from 'slash'
import { ModuleGraph, Plugin } from 'vite'
import { parseManifestJson, parsePagesJson } from '@dcloudio/uni-cli-shared'
import { VitePluginUniResolvedOptions } from '..'
import { getFeatures } from '../utils'
import { initEasycomsOnce, initFeatures } from '../utils'
const debugHmr = debug('uni:hmr')
......@@ -16,14 +19,12 @@ async function invalidate(file: string, moduleGraph: ModuleGraph) {
})
}
}
let invalidateFiles: string[]
export function createHandleHotUpdate(
options: VitePluginUniResolvedOptions
): Plugin['handleHotUpdate'] {
return async function ({ file, server }) {
if (!invalidateFiles) {
// options.inputDir 的赋值时机是在config中,不能直接使用在上边声明使用
invalidateFiles = [
path.resolve(options.inputDir, 'pages.json.js'),
path.resolve(options.inputDir, 'manifest.json.js'),
......@@ -34,24 +35,49 @@ export function createHandleHotUpdate(
// TODO 目前简单处理,当pages.json,manifest.json发生变化,就直接刷新,理想情况下,应该区分变化的内容,仅必要时做整页面刷新
const isPagesJson = file.endsWith('pages.json')
const isManifestJson = file.endsWith('manifest.json')
if (isPagesJson || isManifestJson) {
debugHmr(file)
server.ws.send({
type: 'custom',
event: 'invalidate',
data: {},
if (!isPagesJson && !isManifestJson) {
return
}
debugHmr(file)
server.ws.send({
type: 'custom',
event: 'invalidate',
data: {},
})
const { inputDir, command, platform } = options
const pagesJson = parsePagesJson(inputDir, platform)
// 更新define
Object.assign(
server.config.define!,
initFeatures({
inputDir,
command,
platform,
pagesJson,
manifestJson: parseManifestJson(inputDir),
})
// 更新define
Object.assign(
server.config.define!,
getFeatures(options, server.config.command)
)
debugHmr('define', server.config.define)
// 当pages.json,manifest.json发生变化时,作废pages.json.js缓存
for (const file of invalidateFiles) {
await invalidate(file, server.moduleGraph)
)
debugHmr('define', server.config.define)
if (isPagesJson) {
const easycom = pagesJson.easycom || {}
const { options, refresh } = initEasycomsOnce(inputDir, platform)
if (
!equal(
{ autoscan: easycom.autoscan, custom: easycom.custom },
{ autoscan: options.autoscan, custom: options.custom }
)
) {
refresh()
}
return []
}
// 当pages.json,manifest.json发生变化时,作废pages.json.js缓存
for (const file of invalidateFiles) {
await invalidate(file, server.moduleGraph)
}
return []
}
}
function equal(obj1: Record<string, any>, obj2: Record<string, any>) {
return JSON.stringify(obj1) === JSON.stringify(obj2)
}
......@@ -5,12 +5,14 @@ import debug from 'debug'
import { createFilter } from '@rollup/pluginutils'
import { once } from '@dcloudio/uni-shared'
import { parsePagesJson, parsePagesJsonOnce } from '@dcloudio/uni-cli-shared'
interface EasycomOption {
dirs?: string[]
rootDir?: string
custom?: EasycomCustom
extensions?: string[]
autoscan?: boolean
custom?: EasycomCustom
}
interface EasycomMatcher {
pattern: RegExp
......@@ -36,31 +38,44 @@ function clearEasycom() {
easycomsInvalidCache.clear()
}
export const initEasycoms = once((inputDir: string) => {
const componentsDir = path.resolve(inputDir, 'components')
const uniModulesDir = path.resolve(inputDir, 'uni_modules')
const initEasycomOptions = () => {
const easycomOptions = {
dirs: [componentsDir, ...initUniModulesEasycomDirs(uniModulesDir)],
rootDir: inputDir,
}
debugEasycom(easycomOptions)
return easycomOptions
}
initEasycom(initEasycomOptions())
return {
filter: createFilter(
['components/*/*.vue', 'uni_modules/*/components/*/*.vue'],
[],
{
resolve: inputDir,
export const initEasycomsOnce = once(
(inputDir: string, platform: UniApp.PLATFORM) => {
const componentsDir = path.resolve(inputDir, 'components')
const uniModulesDir = path.resolve(inputDir, 'uni_modules')
const initEasycomOptions = (pagesJson?: UniApp.PagesJson) => {
// 初始化时,从once中读取缓存,refresh时,实时读取
const { easycom } = pagesJson || parsePagesJson(inputDir, platform, false)
const easycomOptions: EasycomOption = {
dirs:
easycom && easycom.autoscan === false
? [] // 禁止自动扫描
: [componentsDir, ...initUniModulesEasycomDirs(uniModulesDir)],
rootDir: inputDir,
autoscan: !!(easycom && easycom.autoscan),
custom: (easycom && easycom.custom) || {},
}
),
refresh() {
initEasycom(initEasycomOptions())
},
debugEasycom(easycomOptions)
return easycomOptions
}
const options = initEasycomOptions(parsePagesJsonOnce(inputDir, platform))
initEasycom(options)
const res = {
options,
filter: createFilter(
['components/*/*.vue', 'uni_modules/*/components/*/*.vue'],
[],
{
resolve: inputDir,
}
),
refresh() {
res.options = initEasycomOptions()
initEasycom(res.options)
},
}
return res
}
})
)
function initUniModulesEasycomDirs(uniModulesDir: string) {
if (!fs.existsSync(uniModulesDir)) {
......@@ -89,7 +104,7 @@ function initEasycom({
}: EasycomOption) {
clearEasycom()
const easycomsObj = Object.create(null)
if (dirs && rootDir) {
if (dirs && dirs.length && rootDir) {
Object.assign(easycomsObj, initAutoScanEasycoms(dirs, rootDir, extensions))
}
if (custom) {
......
import fs from 'fs'
import path from 'path'
import { ConfigEnv } from 'vite'
import { parse } from 'jsonc-parser'
import { isArray } from '@vue/shared'
import { VitePluginUniResolvedOptions } from '..'
import { normalizePagesJson } from './pagesJson'
interface ProjectFeatures {}
interface PagesFeatures {
......@@ -35,20 +32,18 @@ interface ManifestFeatures {
i18nZhHant: boolean
}
function resolveProjectFeature(
options: VitePluginUniResolvedOptions,
command: ConfigEnv['command']
) {
function initProjectFeature({ command }: InitFeaturesOptions) {
const features: ProjectFeatures = {}
if (command === 'build') {
}
return features
}
function resolvePagesFeature(
options: VitePluginUniResolvedOptions,
command: ConfigEnv['command']
): PagesFeatures {
function initPagesFeature({
pagesJson,
command,
inputDir,
}: InitFeaturesOptions): PagesFeatures {
const features: PagesFeatures = {
nvue: true,
pages: true,
......@@ -71,10 +66,7 @@ function resolvePagesFeature(
leftWindow,
rightWindow,
globalStyle,
} = normalizePagesJson(
fs.readFileSync(path.join(options.inputDir, 'pages.json'), 'utf8'),
options.platform
)
} = pagesJson
if (pages && pages.length === 1) {
features.pages = false
}
......@@ -104,7 +96,7 @@ function resolvePagesFeature(
if (command === 'build') {
if (
!pages.find((page) =>
fs.existsSync(path.resolve(options.inputDir, page.path, '.nvue'))
fs.existsSync(path.resolve(inputDir, page.path, '.nvue'))
)
) {
features.nvue = false
......@@ -153,9 +145,11 @@ function resolvePagesFeature(
return features
}
function resolveManifestFeature(
options: VitePluginUniResolvedOptions
): ManifestFeatures {
function initManifestFeature({
manifestJson,
command,
platform,
}: InitFeaturesOptions): ManifestFeatures {
const features: ManifestFeatures = {
wx: false,
wxs: true,
......@@ -169,23 +163,21 @@ function resolveManifestFeature(
i18nZhHans: true,
i18nZhHant: true,
}
const manifest = parse(
fs.readFileSync(path.join(options.inputDir, 'manifest.json'), 'utf8')
)
if (options.command === 'build') {
if (command === 'build') {
// TODO 需要预编译一遍?
features.wxs = false
features.longpress = false
}
if (
manifest.h5 &&
manifest.h5.router &&
manifest.h5.router.mode === 'history'
manifestJson.h5 &&
manifestJson.h5.router &&
manifestJson.h5.router.mode === 'history'
) {
features.routerMode = '"history"'
}
const platform = manifest[options.platform] || {}
const manifestFeatures = platform.features
const platformJson = manifestJson[platform] || {}
const manifestFeatures = platformJson.features
if (manifestFeatures) {
const { i18n } = manifestFeatures
if (isArray(i18n)) {
......@@ -210,12 +202,17 @@ function resolveManifestFeature(
return features
}
export type FEATURE_DEFINES = ReturnType<typeof getFeatures>
export type FEATURE_DEFINES = ReturnType<typeof initFeatures>
export function getFeatures(
options: VitePluginUniResolvedOptions,
interface InitFeaturesOptions {
pagesJson: UniApp.PagesJson
manifestJson: any
inputDir: string
platform: UniApp.PLATFORM
command: ConfigEnv['command']
) {
}
export function initFeatures(options: InitFeaturesOptions) {
const {
wx,
wxs,
......@@ -241,9 +238,9 @@ export function getFeatures(
navigationBarSearchInput,
navigationBarTransparent,
} = Object.assign(
resolveManifestFeature(options),
resolvePagesFeature(options, command),
resolveProjectFeature(options, command)
initManifestFeature(options),
initPagesFeature(options),
initProjectFeature(options)
)
return {
__UNI_FEATURE_WX__: wx, // 是否启用小程序的组件实例 API,如:selectComponent 等(uni-core/src/service/plugin/appConfig)
......
export * from './filter'
export * from './define'
export * from './features'
export * from './easycom'
export * from './postcss'
export * from './pagesJson'
export * from './constants'
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册