2.1 kB · 90 lines · Created 8mo ago
TypeScript
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091export class LocalIdent { readonly name: string | undefined;
constructor(name: string | undefined) { this.name = name; }}
export class CodeFragment { readonly strings: readonly string[]; readonly values: readonly unknown[];
constructor(strings: readonly string[], values: readonly unknown[]) { this.strings = strings; this.values = values; }
/** evaluate the fragment via `new Function`, returning the result */ eval(): unknown { const seen = new Map<unknown, string>(); const interpolations: unknown[] = [];
let uid = 0; let params = '';
const resolve = ({ strings, values }: CodeFragment): string => { let source = strings[0]!;
for (let idx = 0, len = values.length; idx < len; idx++) { const val = values[idx];
if (val instanceof CodeFragment) { source += resolve(val); } else if (val instanceof LocalIdent) { let name = seen.get(val); if (name === undefined) { if (val.name !== undefined) { name = `_$${uid++}_${val.name}`; } else { name = `_$${uid++}`; }
seen.set(val, name); }
source += name; } else { let name = seen.get(val); if (name === undefined) { name = `_$${uid++}`; params = params ? params + ',' + name : name;
seen.set(val, name); interpolations.push(val); }
source += name; }
source += strings[idx + 1]!; }
return source; };
const source = resolve(this);
const fn = new Function(`[${params}]`, source);
return fn(interpolations); }}
const EMPTY: readonly unknown[] = [];const NONIDENT_RE = /[^A-Za-z0-9_$]/g;
// #__NO_SIDE_EFFECTS__export const raw = (source: string): CodeFragment => { return new CodeFragment([source], EMPTY);};
// #__NO_SIDE_EFFECTS__export const x = (strings: TemplateStringsArray, ...values: unknown[]): CodeFragment => { return new CodeFragment(strings, values);};
// #__NO_SIDE_EFFECTS__export const local = (name?: string): LocalIdent => { return new LocalIdent(name?.replace(NONIDENT_RE, '_'));};