-
-
Notifications
You must be signed in to change notification settings - Fork 153
/
farm-vue-hmr.ts
146 lines (135 loc) · 3.6 KB
/
farm-vue-hmr.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
import {
SFCDescriptor,
SFCScriptBlock,
SFCTemplateBlock,
SFCStyleBlock,
} from '@vue/compiler-sfc';
import {
CacheDescriptor,
QueryObj,
StylesCodeCache,
} from './farm-vue-types.js';
import { genMainCode } from './generatorCode.js';
export const cacheScript = new WeakMap();
export function handleHmr(
cacheDescriptor: CacheDescriptor,
descriptor: SFCDescriptor,
stylesCodeCache: StylesCodeCache,
query: QueryObj,
resolvedPath: string
) {
const beforeDescriptor = cacheDescriptor[resolvedPath];
//set descriptors cache to hmr
if (!beforeDescriptor) {
if (Object.keys(query).length === 0)
cacheDescriptor[resolvedPath] = descriptor;
return null;
}
//diff beforeDescriptor and currentDescriptor
else {
return diffDescriptor(
beforeDescriptor,
descriptor,
stylesCodeCache,
resolvedPath
);
}
}
function diffDescriptor(
prevDescriptor: SFCDescriptor,
descriptor: SFCDescriptor,
stylesCodeCache: StylesCodeCache,
resolvedPath: string
) {
let _rerender_only = false;
//If script changed, rerender from root.
const scriptChanged = hasScriptChanged(prevDescriptor, descriptor);
//If only template changed,rerender from current node.
const templateChanged = hasTemplateChanged(
prevDescriptor.template!,
descriptor.template!
);
//If style changed,insert new style.
const [deleteStyles, addStyles] = hasStyleChanged(
prevDescriptor.styles || [],
descriptor.styles || []
);
if (!scriptChanged && templateChanged) {
_rerender_only = true;
}
const { source, moduleType, map } = genMainCode(
descriptor,
stylesCodeCache,
resolvedPath,
true,
_rerender_only,
deleteStyles,
addStyles
);
return { source, moduleType, map };
}
function hasStyleChanged(prev: SFCStyleBlock[], next: SFCStyleBlock[]) {
let p = 0,
q = 0;
const deleteStyles: SFCStyleBlock[] = [];
const addStyles: SFCStyleBlock[] = [];
while (prev[p] && next[q]) {
const prevStyle = prev[p++];
const nextStyle = next[q++];
if (isEqualBlock(prevStyle, nextStyle)) {
continue;
} else {
deleteStyles.push(prevStyle);
addStyles.push(nextStyle);
}
}
//prev should be delete
if (prev[p] && !next[q]) {
while (prev[p]) {
deleteStyles.push(prev[p++]);
}
}
//next has more new styles
else if (!prev[p] && next[q]) {
while (next[q]) {
addStyles.push(next[q++]);
}
}
return [deleteStyles, addStyles];
}
function hasTemplateChanged(prev: SFCTemplateBlock, next: SFCTemplateBlock) {
return isEqualBlock(prev, next);
}
function hasScriptChanged(prev: SFCDescriptor, next: SFCDescriptor) {
if (!isEqualBlock(prev.script!, next.script!)) {
return true;
}
if (!isEqualBlock(prev.scriptSetup!, next.scriptSetup!)) {
return true;
}
//If cssVars changed,it means that script changed
if (prev.cssVars.join('') !== next.cssVars.join('')) {
return true;
}
const prevResolvedScript = cacheScript.get(prev);
const prevImports = prevResolvedScript?.imports;
if (prevImports) {
return !next.template || next.shouldForceReload(prevImports);
}
return false;
}
function isEqualBlock(
a: SFCScriptBlock | SFCTemplateBlock | SFCStyleBlock,
b: SFCScriptBlock | SFCTemplateBlock | SFCStyleBlock
) {
if (!a && !b) return true;
if (!a || !b) return false;
if (a.src && b.src && a.src === b.src) return true;
if (a.content !== b.content) return false;
const keysA = Object.keys(a.attrs);
const keysB = Object.keys(b.attrs);
if (keysA.length !== keysB.length) {
return false;
}
return keysA.every((key) => a.attrs[key] === b.attrs[key]);
}