Skip to content

Commit 41381be

Browse files
committed
test: added unit test
1 parent 16f4819 commit 41381be

File tree

10 files changed

+184
-28
lines changed

10 files changed

+184
-28
lines changed
+95
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
import { resolve } from 'path'
2+
import { beforeEach, describe, expect, test } from 'vitest'
3+
import { transformSymbol } from '@unplugin-vue-cssvars/utils'
4+
import { triggerSFCUpdate, updatedCSSModules } from '../hmr'
5+
6+
const mockOption = {
7+
rootDir: resolve(),
8+
include: [/.vue/],
9+
includeCompile: ['**/**.scss', '**/**.css'],
10+
server: true,
11+
}
12+
const file = transformSymbol(`${resolve()}/packages/core/hmr/__test__/style/foo.css`)
13+
const mockModuleNode = new Set<any>()
14+
mockModuleNode.add({ id: 'foo.vue' })
15+
16+
const mockFileToModulesMap = new Map()
17+
mockFileToModulesMap.set('../D/test', mockModuleNode)
18+
19+
let hmrModule = null
20+
const mockServer = {
21+
reloadModule: (m) => {
22+
hmrModule = m
23+
},
24+
moduleGraph: {
25+
fileToModulesMap: mockFileToModulesMap,
26+
},
27+
}
28+
beforeEach(() => {
29+
hmrModule = null
30+
})
31+
describe('HMR', () => {
32+
test('HMR: updatedCSSModules', () => {
33+
const CSSFileModuleMap = new Map()
34+
CSSFileModuleMap.set(file, {
35+
importer: new Set(),
36+
vBindCode: ['foo'],
37+
})
38+
updatedCSSModules(CSSFileModuleMap, mockOption, file)
39+
expect(CSSFileModuleMap.get(file).content).toBeTruthy()
40+
expect(CSSFileModuleMap.get(file).vBindCode).toMatchObject(['test'])
41+
})
42+
43+
test('HMR: triggerSFCUpdate basic', () => {
44+
const CSSFileModuleMap = new Map()
45+
CSSFileModuleMap.set(file, {
46+
importer: new Set(),
47+
vBindCode: ['foo'],
48+
sfcPath: new Set(['../D/test']),
49+
})
50+
51+
triggerSFCUpdate(CSSFileModuleMap, mockOption, {
52+
importer: new Set(),
53+
vBindCode: ['foo'],
54+
sfcPath: new Set(['../D/test']),
55+
} as any, file, mockServer as any)
56+
expect(CSSFileModuleMap.get(file).content).toBeTruthy()
57+
expect(CSSFileModuleMap.get(file).vBindCode).toMatchObject(['test'])
58+
expect(hmrModule).toMatchObject({ id: 'foo.vue' })
59+
})
60+
61+
test('HMR: triggerSFCUpdate sfcPath is undefined', () => {
62+
const CSSFileModuleMap = new Map()
63+
CSSFileModuleMap.set(file, {
64+
importer: new Set(),
65+
vBindCode: ['foo'],
66+
sfcPath: new Set(['../D/test']),
67+
})
68+
69+
triggerSFCUpdate(CSSFileModuleMap, mockOption, {
70+
importer: new Set(),
71+
vBindCode: ['foo'],
72+
} as any, file, mockServer as any)
73+
expect(CSSFileModuleMap.get(file).content).not.toBeTruthy()
74+
expect(CSSFileModuleMap.get(file).vBindCode).toMatchObject(['foo'])
75+
expect(hmrModule).not.toBeTruthy()
76+
})
77+
78+
test('HMR: triggerSFCUpdate sfcPath is empty', () => {
79+
const CSSFileModuleMap = new Map()
80+
CSSFileModuleMap.set(file, {
81+
importer: new Set(),
82+
vBindCode: ['foo'],
83+
sfcPath: new Set(['../D/test']),
84+
})
85+
86+
triggerSFCUpdate(CSSFileModuleMap, mockOption, {
87+
importer: new Set(),
88+
vBindCode: ['foo'],
89+
sfcPath: new Set(),
90+
} as any, file, mockServer as any)
91+
expect(CSSFileModuleMap.get(file).content).not.toBeTruthy()
92+
expect(CSSFileModuleMap.get(file).vBindCode).toMatchObject(['foo'])
93+
expect(hmrModule).not.toBeTruthy()
94+
})
95+
})
+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#foo{
2+
color: v-bind-m(test);
3+
background: #ffebf8;
4+
width: 200px;
5+
height: 30px;
6+
}

packages/core/hmr/hmr.ts

+65
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import { setTArray } from '@unplugin-vue-cssvars/utils'
2+
import { preProcessCSS } from '../runtime/pre-process-css'
3+
import type { ICSSFile, ICSSFileMap, Options } from '../types'
4+
import type { ViteDevServer } from 'vite'
5+
6+
export function viteHMR(
7+
CSSFileModuleMap: ICSSFileMap,
8+
userOptions: Options,
9+
file: string,
10+
server: ViteDevServer,
11+
) {
12+
// 获取变化的样式文件的 CSSFileMap上有使用它的
13+
const sfcModulesPathList = CSSFileModuleMap.get(file)
14+
triggerSFCUpdate(CSSFileModuleMap, userOptions, sfcModulesPathList, file, server)
15+
}
16+
17+
/**
18+
* update CSSModules
19+
* @param CSSFileModuleMap
20+
* @param userOptions
21+
* @param file
22+
*/
23+
24+
export function updatedCSSModules(
25+
CSSFileModuleMap: ICSSFileMap,
26+
userOptions: Options,
27+
file: string) {
28+
const updatedCSSMS = preProcessCSS(userOptions, userOptions.alias, [file]).get(file)
29+
CSSFileModuleMap.set(file, updatedCSSMS)
30+
}
31+
32+
// TODO: unit test
33+
/**
34+
* triggerSFCUpdate
35+
* @param CSSFileModuleMap
36+
* @param userOptions
37+
* @param sfcModulesPathList
38+
* @param file
39+
* @param server
40+
*/
41+
export function triggerSFCUpdate(
42+
CSSFileModuleMap: ICSSFileMap,
43+
userOptions: Options,
44+
sfcModulesPathList: ICSSFile,
45+
file: string,
46+
server: ViteDevServer) {
47+
if (sfcModulesPathList && sfcModulesPathList.sfcPath) {
48+
// 变化的样式文件的 CSSFileMap上有使用它的 sfc 的信息
49+
const ls = setTArray(sfcModulesPathList.sfcPath)
50+
ls.forEach((sfcp: string) => {
51+
const modules = server.moduleGraph.fileToModulesMap.get(sfcp) || new Set()
52+
53+
// updatedCSSModules
54+
updatedCSSModules(CSSFileModuleMap, userOptions, file)
55+
56+
// update sfc
57+
const modulesList = setTArray(modules)
58+
for (let i = 0; i < modulesList.length; i++) {
59+
// ⭐TODO: 只支持 .vue ? jsx, tsx, js, ts ?
60+
if (modulesList[i].id && (modulesList[i].id as string).endsWith('.vue'))
61+
server.reloadModule(modulesList[i])
62+
}
63+
})
64+
}
65+
}

packages/core/index.ts

+11-23
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { createUnplugin } from 'unplugin'
2-
import {NAME, setTArray, SUPPORT_FILE_REG} from '@unplugin-vue-cssvars/utils'
2+
import { NAME, 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'
@@ -12,7 +12,9 @@ import {
1212
injectCssOnBuild,
1313
injectCssOnServer,
1414
} from './inject'
15-
import type { ResolvedConfig } from 'vite'
15+
import { viteHMR } from './hmr/hmr'
16+
import type { HmrContext, ResolvedConfig } from 'vite'
17+
1618
import type { TMatchVariable } from './parser'
1719
import type { Options } from './types'
1820
// TODO: webpack hmr
@@ -69,29 +71,15 @@ const unplugin = createUnplugin<Options>(
6971
else
7072
isServer = config.command === 'serve'
7173
},
72-
handleHotUpdate(hmr) {
73-
// TODO refactor
74+
handleHotUpdate(hmr: HmrContext) {
7475
if (SUPPORT_FILE_REG.test(hmr.file)) {
7576
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-
}
77+
viteHMR(
78+
CSSFileModuleMap,
79+
userOptions,
80+
hmr.file,
81+
hmr.server,
82+
)
9583
}
9684
},
9785
},

packages/core/runtime/__test__/__snapshots__/pre-process-css.spec.ts.snap

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ exports[`pre process css > preProcessCSS: basic 2`] = `
1414

1515
exports[`pre process css > preProcessCSS: basic 3`] = `
1616
[
17+
"core/hmr/__test__/style/foo.css",
1718
"core/runtime/__test__/style/test.css",
1819
"core/runtime/__test__/style/test2.css",
1920
]

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

+1
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@ describe('pre process css', () => {
135135
test('preProcessCSS: basic', () => {
136136
const files = getAllCSSFilePath(['**/**.css'], resolve('packages'))
137137
expect(files).toMatchObject([
138+
'core/hmr/__test__/style/foo.css',
138139
'core/runtime/__test__/style/test.css',
139140
'core/runtime/__test__/style/test2.css',
140141
])

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,9 @@ export function preProcessCSS(
2323
alias?: Record<string, string>,
2424
filesPath?: string[]): ICSSFileMap {
2525
const { rootDir, includeCompile } = options
26-
2726
// 获得文件列表
2827
const files = filesPath || getAllCSSFilePath(includeCompile!, rootDir!)
28+
2929
return createCSSFileModuleMap(files, rootDir!, alias)
3030
}
3131

@@ -56,7 +56,7 @@ export function createCSSFileModuleMap(files: string[], rootDir: string, alias?:
5656
const fileDirParse = parse(file)
5757
const fileSuffix = fileDirParse.ext
5858

59-
const code = fs.readFileSync(resolve(rootDir!, file), { encoding: 'utf-8' })
59+
const code = fs.readFileSync(transformSymbol(resolve(rootDir!, file)), { encoding: 'utf-8' })
6060
const { imports } = parseImports(code, [transformQuotes])
6161

6262
const absoluteFilePath = transformSymbol(resolve(fileDirParse.dir, fileDirParse.base))

packages/core/runtime/process-css.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ export const getCSSFileRecursion = (
99
key: string,
1010
cssFiles: ICSSFileMap,
1111
cb: (res: ICSSFile) => void,
12-
sfcPath: string,
12+
sfcPath?: string,
1313
matchedMark = new Set<string>()) => {
1414
// 添加后缀
1515
// sfc中规则:如果@import 指定了后缀,则根据后缀,否则根据当前 script 标签的 lang 属性(默认css)

play/src/assets/css/foo.css

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

play/src/views/app/App.vue

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

0 commit comments

Comments
 (0)