generated from unjs/template
-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathproxy.ts
119 lines (108 loc) Β· 3.81 KB
/
proxy.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
import MagicString from 'magic-string'
import type { SourceLocation } from './loc'
import { createSourceLocation, createSourceLocationFromOffsets } from './loc'
export interface MagicBlockBase {
loc?: SourceLocation | { start: number; end: number }
_source?: string
_loc?: SourceLocation
[key: string]: any
}
export type MagicBlock<T extends MagicBlockBase = MagicBlockBase> = T & MagicString
function resolveOffsets(
loc: SourceLocation | { start: number; end: number },
): { start: number; end: number } {
let { start, end } = loc
// Set offset
if (typeof start !== 'number') { start = start?.offset }
if (typeof end !== 'number') { end = end?.offset }
return {
start: start as number,
end: end as number,
}
}
export function proxyBlock<T extends MagicBlockBase = MagicBlockBase>(
source: MagicString,
block?: T,
loc?: SourceLocation | { start: number; end: number },
handler: ProxyHandler<object> = {},
): MagicBlock<T> {
const { start: blockStart, end: blockEnd } = resolveOffsets(loc || block?.loc || createSourceLocation(source.toString()))
// Grab content from source
const content = source.toString().substring(blockStart, blockEnd)
// Recreate a local Magic String from the block content.
const snip: MagicString = new MagicString(content)
const proxified: { [K in keyof MagicString]?: MagicString[K] } = {
append: (content: string) => {
source.appendRight(blockEnd, content)
return snip.append(content)
},
appendLeft: (index: number, content: string) => {
source.appendLeft(blockStart + index, content)
return snip.appendLeft(index, content)
},
appendRight: (index: number, content: string) => {
source.appendRight(blockStart + index, content)
return snip.appendRight(index, content)
},
prepend: (content: string) => {
source.prependRight(blockStart, content)
return snip.prepend(content)
},
prependLeft: (index: number, content: string) => {
source.prependLeft(blockStart + index, content)
return snip.prependLeft(index, content)
},
prependRight: (index: number, content: string) => {
source.prependRight(blockStart + index, content)
return snip.prependRight(index, content)
},
overwrite: (start: number, end: number, replacement: string, options?: { storeName?: boolean; contentOnly?: boolean }) => {
source.overwrite(blockStart + start, blockStart + end, replacement, options)
return snip.overwrite(start, end, replacement, options)
},
remove: (start: number, end: number) => {
source.remove(blockStart + start, blockStart + end)
return snip.remove(start, end)
},
}
// Set source if block exists
block = block || {} as T
block._source = content
block._loc = createSourceLocationFromOffsets(source.toString(), blockStart, blockEnd)
return new Proxy(
block,
{
...handler,
get(target: T, key: string | symbol, receiver: any) {
if (key === 'source') {
return source
}
if (key in snip) {
if (Object.hasOwn(proxified, key)) { return (proxified as any)[key] }
return snip[key as unknown as keyof MagicString]
}
if (block && key in block && !handler.get) {
return block[key as any]
}
if (handler.get) {
return handler.get(target, key, receiver)
}
},
set(target: T, key: string | symbol, value: any, receiver: any) {
if (key in proxified) {
(proxified as any)[key] = value
return true
}
if (block && !handler.set) {
(block as any)[key] = value
return true
}
if (handler.set) {
return handler.set(target, key, value, receiver)
}
/* c8 ignore next */
return false
},
},
) as MagicBlock<T>
}