Last active
March 26, 2024 07:54
-
-
Save pjlsergeant/99a1f387695e2da90c76598c244a7148 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
# WHAT | |
Do a pre-pass of tagged template literals, interpolating strings | |
# WHY | |
Consider Prisma's `$queryRaw()`; it accepts a tagged template literal, and | |
replaces all the templated values with SQL placeholders (`$1`, `$2`) etc, before | |
actually running the query. That's great, unless you want to additionally add | |
some values to the SQL that Prisma can't do that to, like an interval -- the | |
below code doesn't work, for this reason: | |
``` | |
const result = await prisma.$queryRaw` | |
SELECT * FROM foo | |
WHERE foo.id = ${id} | |
AND foo.when >= CURRENT_DATE - INTERVAL '${interval}'`; | |
``` | |
This package allows you to mark values that should be interpolated first, before | |
passing to the parent function, eg: | |
``` | |
const result = await prisma.$queryRaw(...pp` | |
SELECT * FROM foo | |
WHERE foo.id = ${id} | |
AND foo.when >= CURRENT_DATE - INTERVAL '${p('1 week')}'`); | |
``` | |
# FUNCTIONS | |
## pp\`\` | |
Accepts a tagged template literal, and returns one, with only values that have | |
been wrapped in `p()` interpolated. | |
# p(string) | |
Marks that a value should be interpolated first | |
*/ | |
class WrappedValue { constructor( public v: string ) {} } | |
export function p(v: string) { return new WrappedValue(v) } | |
function _createTemplateStringsArray(strings: string[]): TemplateStringsArray { | |
const templateStringsArray = [...strings] as unknown as TemplateStringsArray; | |
Object.defineProperty(templateStringsArray, 'raw', { | |
value: Object.freeze([...strings]), | |
writable: false, | |
}); | |
return Object.freeze(templateStringsArray); | |
} | |
export function pp<T = any>(strings: TemplateStringsArray, ...values: (T | WrappedValue)[]) : [TemplateStringsArray, ...T[]] { | |
const newStrings : string[] = []; | |
const newValues : any[] = []; | |
let mergeString : string | null = null; | |
for ( let i = 0; i < (strings.length - 1); i++ ) { | |
let leftString : string; | |
if ( mergeString !== null ) { | |
leftString = mergeString; | |
mergeString = null; | |
} else { | |
leftString = strings[i] | |
} | |
const value = values[i]; | |
if ( typeof value === 'object' && value instanceof WrappedValue ) { | |
// We should always have another value left, because a value right at the | |
// end still gets an empty string appended | |
const rightString = strings[i+1]; | |
mergeString = leftString + value.v + rightString; | |
} else { | |
newStrings.push( leftString ); | |
newValues.push( value ); | |
} | |
} | |
newStrings.push( mergeString === null ? strings.at(-1)! : mergeString ); | |
return [_createTemplateStringsArray(newStrings), ...newValues]; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment