-
-
Notifications
You must be signed in to change notification settings - Fork 138
/
descriptorCache.ts
124 lines (113 loc) · 3.3 KB
/
descriptorCache.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
import fs from 'node:fs'
import path from 'node:path'
import { createHash } from 'node:crypto'
import slash from 'slash'
import type { CompilerError, SFCDescriptor } from 'vue/compiler-sfc'
import type { ResolvedOptions, VueQuery } from '..'
// compiler-sfc should be exported so it can be re-used
export interface SFCParseResult {
descriptor: SFCDescriptor
errors: (CompilerError | SyntaxError)[]
}
export const cache = new Map<string, SFCDescriptor>()
// we use a separate descriptor cache for HMR purposes.
// The main cached descriptors are parsed from SFCs that may have been
// transformed by other plugins, e.g. vue-macros;
// The HMR cached descriptors are based on the raw, pre-transform SFCs.
export const hmrCache = new Map<string, SFCDescriptor>()
const prevCache = new Map<string, SFCDescriptor | undefined>()
export function createDescriptor(
filename: string,
source: string,
{ root, isProduction, sourceMap, compiler }: ResolvedOptions,
hmr = false,
): SFCParseResult {
const { descriptor, errors } = compiler.parse(source, {
filename,
sourceMap,
})
// ensure the path is normalized in a way that is consistent inside
// project (relative to root) and on different systems.
const normalizedPath = slash(path.normalize(path.relative(root, filename)))
descriptor.id = getHash(normalizedPath + (isProduction ? source : ''))
;(hmr ? hmrCache : cache).set(filename, descriptor)
return { descriptor, errors }
}
export function getPrevDescriptor(filename: string): SFCDescriptor | undefined {
return prevCache.get(filename)
}
export function invalidateDescriptor(filename: string, hmr = false): void {
const _cache = hmr ? hmrCache : cache
const prev = _cache.get(filename)
_cache.delete(filename)
if (prev) {
prevCache.set(filename, prev)
}
}
export function getDescriptor(
filename: string,
options: ResolvedOptions,
createIfNotFound = true,
hmr = false,
code?: string,
): SFCDescriptor | undefined {
const _cache = hmr ? hmrCache : cache
if (_cache.has(filename)) {
return _cache.get(filename)!
}
if (createIfNotFound) {
const { descriptor, errors } = createDescriptor(
filename,
code ?? fs.readFileSync(filename, 'utf-8'),
options,
hmr,
)
if (errors.length && !hmr) {
throw errors[0]
}
return descriptor
}
}
export function getSrcDescriptor(
filename: string,
query: VueQuery,
): SFCDescriptor {
if (query.scoped) {
return cache.get(`${filename}?src=${query.src}`)!
}
return cache.get(filename)!
}
export function getTempSrcDescriptor(
filename: string,
query: VueQuery,
): SFCDescriptor {
// this is only used for pre-compiled <style src> with scoped flag
return {
filename,
id: query.id || '',
styles: [
{
scoped: query.scoped,
loc: {
start: { line: 0, column: 0 },
},
},
],
} as SFCDescriptor
}
export function setSrcDescriptor(
filename: string,
entry: SFCDescriptor,
scoped?: boolean,
): void {
if (scoped) {
// if multiple Vue files use the same src file, they will be overwritten
// should use other key
cache.set(`${filename}?src=${entry.id}`, entry)
return
}
cache.set(filename, entry)
}
function getHash(text: string): string {
return createHash('sha256').update(text).digest('hex').substring(0, 8)
}