Skip to content

Commit 16f4819

Browse files
committed
feat: support vite hmr
1 parent dd1f124 commit 16f4819

File tree

9 files changed

+60
-40
lines changed

9 files changed

+60
-40
lines changed

packages/core/index.ts

+33-24
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { createUnplugin } from 'unplugin'
2-
import { NAME } from '@unplugin-vue-cssvars/utils'
2+
import {NAME, setTArray, SUPPORT_FILE_REG} from '@unplugin-vue-cssvars/utils'
33
import { createFilter } from '@rollup/pluginutils'
44
import { parse } from '@vue/compiler-sfc'
55
import chalk from 'chalk'
@@ -15,7 +15,7 @@ import {
1515
import type { ResolvedConfig } from 'vite'
1616
import type { TMatchVariable } from './parser'
1717
import type { Options } from './types'
18-
18+
// TODO: webpack hmr
1919
const unplugin = createUnplugin<Options>(
2020
(options: Options = {}): any => {
2121
const userOptions = initOption(options)
@@ -25,15 +25,14 @@ const unplugin = createUnplugin<Options>(
2525
)
2626
// 预处理 css 文件
2727
const CSSFileModuleMap = preProcessCSS(userOptions, userOptions.alias)
28-
const vbindVariableList = new Map<string, {
29-
TMatchVariable: TMatchVariable
30-
orgTransformCode?: string }>()
28+
const vbindVariableList = new Map<string, TMatchVariable>()
3129
let isScriptSetup = false
3230
if (userOptions.server === undefined) {
3331
console.warn(chalk.yellowBright.bold(`[${NAME}] The server of option is not set, you need to specify whether you are using the development server or building the project`))
3432
console.warn(chalk.yellowBright.bold(`[${NAME}] See: https://github.com/baiwusanyu-c/unplugin-vue-cssvars/blob/master/README.md#option`))
3533
}
3634
let isServer = !!userOptions.server
35+
let isHmring = false
3736
return [
3837
{
3938
name: NAME,
@@ -52,9 +51,7 @@ const unplugin = createUnplugin<Options>(
5251
injectCSSContent,
5352
} = getVBindVariableListByPath(descriptor, id, CSSFileModuleMap, isServer, userOptions.alias)
5453
const variableName = getVariable(descriptor)
55-
vbindVariableList.set(id, {
56-
TMatchVariable: matchVariable(vbindVariableListByPath, variableName),
57-
})
54+
vbindVariableList.set(id, matchVariable(vbindVariableListByPath, variableName))
5855

5956
if (!isServer)
6057
code = injectCssOnBuild(code, injectCSSContent, descriptor)
@@ -73,8 +70,29 @@ const unplugin = createUnplugin<Options>(
7370
isServer = config.command === 'serve'
7471
},
7572
handleHotUpdate(hmr) {
76-
if (hmr.file.endsWith('foo.css'))
77-
return hmr.modules
73+
// TODO refactor
74+
if (SUPPORT_FILE_REG.test(hmr.file)) {
75+
isHmring = true
76+
const sfcModulesPathList = CSSFileModuleMap.get(hmr.file)
77+
if (sfcModulesPathList && sfcModulesPathList.sfcPath) {
78+
const ls = setTArray(sfcModulesPathList.sfcPath)
79+
ls.forEach((sfcp) => {
80+
const modules = hmr.server.moduleGraph.fileToModulesMap.get(sfcp)
81+
// update CSSFileModuleMap
82+
const updatedCSSModules = preProcessCSS(userOptions, userOptions.alias, [hmr.file]).get(hmr.file)
83+
if (updatedCSSModules)
84+
CSSFileModuleMap.set(hmr.file, updatedCSSModules)
85+
86+
// update sfc
87+
const modulesList = setTArray(modules)
88+
for (let i = 0; i < modulesList.length; i++) {
89+
// ⭐TODO: 只支持 .vue ? jsx, tsx, js, ts ?
90+
if (modulesList[i].id.endsWith('.vue'))
91+
hmr.server.reloadModule(modulesList[i])
92+
}
93+
})
94+
}
95+
}
7896
},
7997
},
8098
},
@@ -87,22 +105,13 @@ const unplugin = createUnplugin<Options>(
87105
// transform in dev
88106
if (isServer) {
89107
if (id.endsWith('.vue')) {
90-
const orgCode = code
91-
// console.log('########', id)
92-
code = code.replaceAll('if (!mod)', 'console.log(mod)\n if (!mod)')
93-
// console.log(code)
94-
const injectRes = injectCSSVars(code, vbindVariableList.get(id).TMatchVariable, isScriptSetup)
108+
const injectRes = injectCSSVars(code, vbindVariableList.get(id), isScriptSetup)
95109
code = injectRes.code
96-
injectRes.vbindVariableList && vbindVariableList.set(id, {
97-
TMatchVariable: injectRes.vbindVariableList,
98-
orgTransformCode: orgCode,
99-
})
100-
}
101-
if (id.includes('type=style')) {
102-
console.log('########', id)
103-
code = injectCssOnServer(code, vbindVariableList.get(id.split('?vue')[0]).TMatchVariable)
104-
console.log(code)
110+
injectRes.vbindVariableList && vbindVariableList.set(id, injectRes.vbindVariableList)
111+
isHmring = false
105112
}
113+
if (id.includes('type=style'))
114+
code = injectCssOnServer(code, vbindVariableList.get(id.split('?vue')[0]), isHmring)
106115
}
107116
return code
108117
} catch (err: unknown) {

packages/core/inject/inject-css.ts

+7-1
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,20 @@
1+
import hash from 'hash-sum'
12
import { transformInjectCSS } from '../transform/transform-inject-css'
23
import { parseImports } from '../parser'
34
import type { TInjectCSSContent } from '../runtime/process-css'
45
import type { SFCDescriptor } from '@vue/compiler-sfc'
56
import type { TMatchVariable } from '../parser'
6-
77
export function injectCssOnServer(
88
code: string,
99
vbindVariableList: TMatchVariable | undefined,
10+
isHmring: boolean,
1011
) {
1112
vbindVariableList && vbindVariableList.forEach((vbVar) => {
13+
// 样式文件修改后,热更新会先于 sfc 热更新运行,这里先设置hash
14+
// 详见 packages/core/index.ts的 handleHotUpdate
15+
if (!vbVar.hash && isHmring)
16+
vbVar.hash = hash(vbVar.value + vbVar.has)
17+
1218
code = code.replaceAll(`v-bind-m(${vbVar.value})`, `var(--${vbVar.hash})`)
1319
})
1420
return code

packages/core/inject/inject-cssvars.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,8 @@ export function createUseCssVarsCode(
6060
isScriptSetup: boolean) {
6161
let cssvarsObjectCode = ''
6262
vbindVariableList.forEach((vbVar) => {
63-
const hashVal = hash(vbVar.value + vbVar.has)
63+
// 如果 hash 存在,则说明是由热更新引起的,不需要重新设置 hash
64+
const hashVal = vbVar.hash || hash(vbVar.value + vbVar.has)
6465
vbVar.hash = hashVal
6566
let varStr = ''
6667
// composition api 和 option api 一直帶 _ctx

packages/core/parser/parser-variable.ts

+1-7
Original file line numberDiff line numberDiff line change
@@ -175,13 +175,7 @@ export function getObjectExpressionReturnNode(node: ObjectExpression) {
175175
return res
176176
}
177177

178-
export type TMatchVariable = Array<{
179-
has: boolean
180-
value: string
181-
hash?: string
182-
isRef: boolean
183-
orgTransformCode?: string
184-
}>
178+
export type TMatchVariable = Array<{ has: boolean, value: string, hash?: string, isRef: boolean }>
185179
export function matchVariable(
186180
importCSSModule: Array<string>,
187181
variableName: VariableName,

packages/core/runtime/pre-process-css.ts

+6-2
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,16 @@ import type { ICSSFileMap, SearchGlobOptions } from '../types'
1616
* 预处理css文件
1717
* @param options 选项参数 Options
1818
* @param alias
19+
* @param filesPath
1920
*/
20-
export function preProcessCSS(options: SearchGlobOptions, alias?: Record<string, string>): ICSSFileMap {
21+
export function preProcessCSS(
22+
options: SearchGlobOptions,
23+
alias?: Record<string, string>,
24+
filesPath?: string[]): ICSSFileMap {
2125
const { rootDir, includeCompile } = options
2226

2327
// 获得文件列表
24-
const files = getAllCSSFilePath(includeCompile!, rootDir!)
28+
const files = filesPath || getAllCSSFilePath(includeCompile!, rootDir!)
2529
return createCSSFileModuleMap(files, rootDir!, alias)
2630
}
2731

packages/core/runtime/process-css.ts

+7-2
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ export const getCSSFileRecursion = (
99
key: string,
1010
cssFiles: ICSSFileMap,
1111
cb: (res: ICSSFile) => void,
12+
sfcPath: string,
1213
matchedMark = new Set<string>()) => {
1314
// 添加后缀
1415
// sfc中规则:如果@import 指定了后缀,则根据后缀,否则根据当前 script 标签的 lang 属性(默认css)
@@ -20,11 +21,15 @@ export const getCSSFileRecursion = (
2021
if (matchedMark.has(key)) return
2122
const cssFile = cssFiles.get(key)
2223
if (cssFile) {
24+
if (!cssFile.sfcPath)
25+
cssFile.sfcPath = new Set()
26+
27+
cssFile.sfcPath?.add(sfcPath)
2328
matchedMark.add(key)
2429
cb(cssFile)
2530
if (cssFile.importer.size > 0) {
2631
cssFile.importer.forEach((value) => {
27-
getCSSFileRecursion(lang, value, cssFiles, cb, matchedMark)
32+
getCSSFileRecursion(lang, value, cssFiles, cb, sfcPath, matchedMark)
2833
})
2934
}
3035
} else {
@@ -66,7 +71,7 @@ export const getVBindVariableListByPath = (
6671
vbindVariable.add(vb)
6772
})
6873
}
69-
})
74+
}, id)
7075
} catch (e) {
7176
if ((e as Error).message === 'path') {
7277
const doc = 'https://github.com/baiwusanyu-c/unplugin-vue-cssvars/pull/29'

packages/core/types.ts

+1
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ export interface ICSSFile {
5252
vBindCode: Array<string> | null
5353
content: string
5454
lang: string
55+
sfcPath?: Set<string>
5556
}
5657
export declare type ICSSFileMap = Map<string, ICSSFile>
5758
export declare type VariableName = Record<string, Node | undefined | null>

play/src/assets/css/foo.css

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#foo{
2-
color: v-bind-m(color);
3-
background: #0E53C4;
2+
color: v-bind-m(fooColor);
3+
background: #ffebf8;
44
width: 200px;
55
height: 30px;
66
}

play/src/views/app/App.vue

+1-1
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ export default defineComponent({
7373

7474
<template>
7575
<div id="foo" class="scss" @click="sassColor = 'red'">
76-
app1
76+
app122
7777
<!-- <Comp /> -->
7878
</div>
7979
</template>

0 commit comments

Comments
 (0)