Skip to content

Commit 2bd3008

Browse files
authored
feat: support webpack hmr (#54)
* feat: support webpack hmr * update: update unit test
1 parent 1a2f23c commit 2bd3008

File tree

13 files changed

+88
-78
lines changed

13 files changed

+88
-78
lines changed

.idea/inspectionProfiles/Project_Default.xml

-6
This file was deleted.

packages/core/hmr/__test__/hmr.spec.ts

+7-9
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { resolve } from 'path'
22
import { beforeEach, describe, expect, test } from 'vitest'
33
import { transformSymbol } from '@unplugin-vue-cssvars/utils'
4-
import { triggerSFCUpdate, updatedCSSModules, viteHMR } from '../hmr'
4+
import { reloadSFCModules, updatedCSSModules, viteHMR } from '../hmr'
55

66
const mockOption = {
77
rootDir: resolve(),
@@ -54,33 +54,31 @@ describe('HMR', () => {
5454
expect(hmrModule).toMatchObject({ id: 'foo.vue' })
5555
})
5656

57-
test('HMR: triggerSFCUpdate basic', () => {
57+
test('HMR: reloadSFCModules basic', () => {
5858
const CSSFileModuleMap = new Map()
5959
CSSFileModuleMap.set(file, {
6060
importer: new Set(),
6161
vBindCode: ['foo'],
6262
sfcPath: new Set(['../D/test']),
6363
})
6464

65-
triggerSFCUpdate(CSSFileModuleMap, mockOption, {
65+
reloadSFCModules(CSSFileModuleMap, mockOption, {
6666
importer: new Set(),
6767
vBindCode: ['foo'],
6868
sfcPath: new Set(['../D/test']),
6969
} as any, file, mockServer as any)
70-
expect(CSSFileModuleMap.get(file).content).toBeTruthy()
71-
expect(CSSFileModuleMap.get(file).vBindCode).toMatchObject(['test'])
7270
expect(hmrModule).toMatchObject({ id: 'foo.vue' })
7371
})
7472

75-
test('HMR: triggerSFCUpdate sfcPath is undefined', () => {
73+
test('HMR: reloadSFCModules sfcPath is undefined', () => {
7674
const CSSFileModuleMap = new Map()
7775
CSSFileModuleMap.set(file, {
7876
importer: new Set(),
7977
vBindCode: ['foo'],
8078
sfcPath: new Set(['../D/test']),
8179
})
8280

83-
triggerSFCUpdate(CSSFileModuleMap, mockOption, {
81+
reloadSFCModules(CSSFileModuleMap, mockOption, {
8482
importer: new Set(),
8583
vBindCode: ['foo'],
8684
} as any, file, mockServer as any)
@@ -89,15 +87,15 @@ describe('HMR', () => {
8987
expect(hmrModule).not.toBeTruthy()
9088
})
9189

92-
test('HMR: triggerSFCUpdate sfcPath is empty', () => {
90+
test('HMR: reloadSFCModules sfcPath is empty', () => {
9391
const CSSFileModuleMap = new Map()
9492
CSSFileModuleMap.set(file, {
9593
importer: new Set(),
9694
vBindCode: ['foo'],
9795
sfcPath: new Set(['../D/test']),
9896
})
9997

100-
triggerSFCUpdate(CSSFileModuleMap, mockOption, {
98+
reloadSFCModules(CSSFileModuleMap, mockOption, {
10199
importer: new Set(),
102100
vBindCode: ['foo'],
103101
sfcPath: new Set(),

packages/core/hmr/hmr.ts

+8-14
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,11 @@ export function viteHMR(
1111
) {
1212
// 获取变化的样式文件的 CSSFileMap上有使用它的
1313
const sfcModulesPathList = CSSFileModuleMap.get(file)
14-
triggerSFCUpdate(CSSFileModuleMap, userOptions, sfcModulesPathList!, file, server)
14+
if (!(sfcModulesPathList && sfcModulesPathList.sfcPath)) return
15+
// update CSSModules
16+
updatedCSSModules(CSSFileModuleMap, userOptions, file)
17+
// reload sfc Module
18+
reloadSFCModules(CSSFileModuleMap, userOptions, sfcModulesPathList!, file, server)
1519
}
1620

1721
// TODO: unit test
@@ -20,15 +24,7 @@ export function webpackHMR(
2024
userOptions: Options,
2125
file: string,
2226
) {
23-
// 获取变化的样式文件的 CSSFileMap上有使用它的
24-
const sfcModulesPathList = CSSFileModuleMap.get(file)
25-
if (sfcModulesPathList && sfcModulesPathList.sfcPath) {
26-
const ls = setTArray(sfcModulesPathList.sfcPath)
27-
ls.forEach(() => {
28-
// updated CSSModules
29-
updatedCSSModules(CSSFileModuleMap, userOptions, file)
30-
})
31-
}
27+
updatedCSSModules(CSSFileModuleMap, userOptions, file)
3228
}
3329

3430
/**
@@ -51,14 +47,14 @@ export function updatedCSSModules(
5147
}
5248

5349
/**
54-
* triggerSFCUpdate
50+
* reloadSFCModules
5551
* @param CSSFileModuleMap
5652
* @param userOptions
5753
* @param sfcModulesPathList
5854
* @param file
5955
* @param server
6056
*/
61-
export function triggerSFCUpdate(
57+
export function reloadSFCModules(
6258
CSSFileModuleMap: ICSSFileMap,
6359
userOptions: Options,
6460
sfcModulesPathList: ICSSFile,
@@ -68,8 +64,6 @@ export function triggerSFCUpdate(
6864
// 变化的样式文件的 CSSFileMap上有使用它的 sfc 的信息
6965
const ls = setTArray(sfcModulesPathList.sfcPath)
7066
ls.forEach((sfcp: string) => {
71-
// updatedCSSModules
72-
updatedCSSModules(CSSFileModuleMap, userOptions, file)
7367
// update sfc
7468
const modules = server.moduleGraph.fileToModulesMap.get(sfcp) || new Set()
7569
const modulesList = setTArray(modules)

packages/core/index.ts

+15-8
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import {
33
JSX_TSX_REG, NAME,
44
SUPPORT_FILE_REG,
55
log,
6-
runAsyncTaskList,
76
setTArray,
87
transformSymbol,
98
} from '@unplugin-vue-cssvars/utils'
@@ -25,7 +24,7 @@ import type { MagicStringBase } from 'magic-string-ast'
2524
import type { HmrContext, ResolvedConfig } from 'vite'
2625
import type { TMatchVariable } from './parser'
2726
import type { Options } from './types'
28-
// TODO: webpack hmr
27+
// TODO refactor
2928
const unplugin = createUnplugin<Options>(
3029
(options: Options = {}, meta): any => {
3130
const framework = meta.framework
@@ -39,12 +38,13 @@ const unplugin = createUnplugin<Options>(
3938
const vbindVariableList = new Map<string, TMatchVariable>()
4039
let isScriptSetup = false
4140
if (userOptions.server === undefined) {
41+
log('warning', 'The server of option is not set, you need to specify whether you are using the development server or building the project')
42+
log('warning', 'The server of option is not set, you need to specify whether you are using the development server or building the project')
4243
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`))
4344
console.warn(chalk.yellowBright.bold(`[${NAME}] See: https://github.com/baiwusanyu-c/unplugin-vue-cssvars/blob/master/README.md#option`))
4445
}
4546
let isServer = !!userOptions.server
4647
let isHMR = false
47-
const cacheWebpackModule = new Map<string, any>()
4848

4949
function handleVBindVariable(
5050
code: string,
@@ -93,8 +93,9 @@ const unplugin = createUnplugin<Options>(
9393
mgcStr = res
9494
}
9595

96-
if ((transId.includes('?vue&type=style') && isHMR && framework === 'webpack')) {
97-
transId = transId.split('?vue&type=style')[0]
96+
if ((transId.includes('?vue&type=style') || transId.includes('?vue&type=script'))
97+
&& isHMR && framework === 'webpack') {
98+
transId = transId.split('?vue')[0]
9899
const res = handleVBindVariable(code, transId, mgcStr)
99100
if (res)
100101
mgcStr = res
@@ -134,10 +135,12 @@ const unplugin = createUnplugin<Options>(
134135
}
135136
},
136137
},
138+
139+
// TODO unit test
137140
webpack(compiler) {
138141
// mark webpack hmr
139142
let modifiedFile = ''
140-
compiler.hooks.watchRun.tap(NAME, (compilation1) => {
143+
compiler.hooks.watchRun.tapAsync(NAME, (compilation1, watchRunCallBack) => {
141144
if (compilation1.modifiedFiles) {
142145
modifiedFile = transformSymbol(setTArray(compilation1.modifiedFiles)[0] as string)
143146
if (SUPPORT_FILE_REG.test(modifiedFile)) {
@@ -149,6 +152,7 @@ const unplugin = createUnplugin<Options>(
149152
)
150153
}
151154
}
155+
watchRunCallBack()
152156
})
153157

154158
compiler.hooks.compilation.tap(NAME, (compilation) => {
@@ -180,6 +184,8 @@ const unplugin = createUnplugin<Options>(
180184
Promise.all(promises)
181185
.then(() => {
182186
callback()
187+
// hmr end
188+
isHMR = false
183189
})
184190
.catch((e) => {
185191
log('error', e)
@@ -202,6 +208,7 @@ const unplugin = createUnplugin<Options>(
202208
return filter(id)
203209
},
204210
async transform(code: string, id: string) {
211+
console.log(id)
205212
let transId = transformSymbol(id)
206213
let mgcStr = new MagicString(code)
207214
// ⭐TODO: 只支持 .vue ? jsx, tsx, js, ts ?
@@ -211,7 +218,7 @@ const unplugin = createUnplugin<Options>(
211218
const injectRes = injectCSSVars(vbindVariableList.get(idKey), isScriptSetup, parseRes, mgcStr)
212219
mgcStr = injectRes.mgcStr
213220
injectRes.vbindVariableList && vbindVariableList.set(transId, injectRes.vbindVariableList)
214-
isHMR = false
221+
// TODO vite hmr close ? isHMR -> false
215222
}
216223

217224
// transform in dev
@@ -237,7 +244,7 @@ const unplugin = createUnplugin<Options>(
237244
// webpack dev 和 build 都回进入这里
238245
if (framework === 'webpack') {
239246
if (transId.includes('?vue&type=script')) {
240-
transId = transId.split('?vue&type=script')[0]
247+
transId = transId.split('?vue')[0]
241248
injectCSSVarsFn(transId)
242249
}
243250

packages/core/inject/inject-css.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import hash from 'hash-sum'
2-
import { type MagicStringBase } from 'magic-string-ast'
32
import { transformInjectCSS } from '../transform/transform-inject-css'
43
import { parseImports } from '../parser'
4+
import type { MagicStringBase } from 'magic-string-ast'
55
import type { TInjectCSSContent } from '../runtime/process-css'
66
import type { SFCDescriptor } from '@vue/compiler-sfc'
77
import type { TMatchVariable } from '../parser'
@@ -11,7 +11,7 @@ export function injectCSSOnServer(
1111
isHMR: boolean,
1212
) {
1313
vbindVariableList && vbindVariableList.forEach((vbVar) => {
14-
// 样式文件修改后,热更新会先于 sfc 热更新运行,这里先设置hash
14+
// 样式文件修改后,style热更新可能会先于 sfc 热更新运行,这里先设置hash
1515
// 详见 packages/core/index.ts的 handleHotUpdate
1616
if (!vbVar.hash && isHMR)
1717
vbVar.hash = hash(vbVar.value + vbVar.has)

play/vite/src/assets/css/foo.css

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
#foo{
2-
color: v-bind-m(color);
2+
color: v-bind-m(sassColor);
33
background: #ffebf8;
44
width: 200px;
55
height: 30px;
66
}
7+
p {
8+
color: v-bind-m(color);
9+
}

play/vite/src/comp.vue

+6-30
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,12 @@
1-
<script lang="tsx">
2-
/* import { reactive, ref } from 'vue'
3-
const compAsd = () => 'red'
4-
const color = 'red'
5-
const compTheme1 = compAsd()
6-
const compTheme2 = 'red'
7-
const sassColor = 'pink'
8-
const compTheme3 = ref('red')
9-
const compTheme4 = reactive({ color: 'red' })
10-
const compTheme5 = { color: 'red' }
11-
const compTheme6 = () => 'red' */
12-
import { defineComponent } from 'vue'
13-
export default defineComponent({
14-
setup() {
15-
return () => (<div>test</div>)
16-
},
17-
})
1+
<script setup lang="ts">
2+
import { ref } from 'vue'
3+
const color = ref('red')
184
</script>
195

206
<template>
21-
<div class="test">
22-
comp
23-
</div>
7+
<p>comp</p>
248
</template>
259

26-
<style lang="scss">
27-
/*@import "./assets/test.css";*/
28-
div {
29-
color: v-bind(color)
30-
}
31-
// @import './assets/scss/bar.scss';
32-
.blue-btn {
33-
34-
color: v-bind(sassColor);
35-
}
10+
<style scoped>
11+
@import "./assets/css/foo.css";
3612
</style>

play/vite/src/comp2.vue

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<script setup lang="ts">
2+
import { ref } from 'vue'
3+
const color = ref('red')
4+
</script>
5+
6+
<template>
7+
<p>comp2</p>
8+
</template>
9+
10+
<style scoped>
11+
@import "./assets/css/foo.css";
12+
</style>

play/vite/src/views/app/App.vue

+7-5
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
<script setup lang="ts">
22
import { reactive, ref } from 'vue'
33
import Comp from '../../comp.vue'
4-
const color = ref('blue')
4+
import Comp2 from '../../comp2.vue'
5+
/* const color = ref('blue')
56
const appAsd = () => 'red'
67
const fooColor = appAsd()
78
const appTheme2 = 'blue'
@@ -11,7 +12,7 @@ const stylColor = '#fd1d7c'
1112
const appTheme3 = ref('red')
1213
const appTheme4 = reactive({ color: 'red' })
1314
const appTheme5 = { color: 'red' }
14-
const appTheme6 = () => 'red'
15+
const appTheme6 = () => 'red' */
1516
</script>
1617
<!--
1718
<script lang="ts">
@@ -74,10 +75,11 @@ export default defineComponent({
7475
<template>
7576
<div id="foo" class="scss" @click="sassColor = 'red'">
7677
app122
77-
<!-- <Comp /> -->
78+
<Comp />
79+
<Comp2 />
7880
</div>
7981
</template>
8082

81-
<style lang="scss" scoped>
83+
<!-- <style lang="scss" scoped>
8284
@import '@/assets/css/foo.css';
83-
</style>
85+
</style> -->

play/webpack/src/App.vue

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<script setup lang="ts">
22
import { ref } from 'vue'
3-
import Comp from '@/comp.vue'
4-
import Comp2 from '@/comp2.vue'
3+
import Comp from './comp.vue'
4+
import Comp2 from './comp2.vue'
55
const color = ref('red')
66
const appAsd = () => 'green'
77
const fooColor = appAsd()

play/webpack/src/assets/css/foo.css

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
p{
2-
color: v-bind-m(wfwfw);
2+
color: v-bind-m(color);
33
width: 200px;
44
height: 30px;
55
}

play/webpack/src/comp.vue

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<script setup lang="ts">
2+
import { ref } from 'vue'
3+
const color = ref('red')
4+
</script>
5+
6+
<template>
7+
<p>comp</p>
8+
</template>
9+
10+
<style scoped>
11+
@import "./assets/css/foo.css";
12+
</style>

play/webpack/src/comp2.vue

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<script setup lang="ts">
2+
import { ref } from 'vue'
3+
const color = ref('red')
4+
</script>
5+
6+
<template>
7+
<p>comp2</p>
8+
</template>
9+
10+
<style scoped>
11+
@import "./assets/css/foo.css";
12+
</style>

0 commit comments

Comments
 (0)