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 & 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( source: MagicString, block?: T, loc?: SourceLocation | { start: number; end: number }, handler: ProxyHandler = {}, ): MagicBlock { 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 }