qqtag
v1.2.1
Published
quasi quote library for javascript template literals
Downloads
3
Maintainers
Readme
qqtag
Quasi quote library for javascript template literals.
Motivation
Some database libraries like mssql supports embedding query parameters using tagged literals:
const sql = require('mssql')
sql.connect(config).then(() => {
return sql.query`select * from mytable where id = ${value}`
}).then(result => {
console.dir(result)
}).catch(err => {
// ... error checks
})
But what to do if you want to embed a table name variable, or if you want to add optional WHERE conditions based on values of variables ?
Usage
quasiquote
expresses a template literal that is not yet evaluated and can be passed to other tag functions.import { quasiquote as qq } from 'qqtag' const sql = require('mssql') sql.connect(config).then(() => { const query = qq`select * from mytable where id = ${value}` return query.sendTo(sql.query.bind(sql)) }) // ...
quote
can embed a string as a part of literals instead of passing it to a template literal place holder.import { quasiquote as qq, quote as qt } from 'qqtag' const tableName = 'tab1' const query1 = qq`select col1 from ${tableName}` const query2 = qq`select col1 from ${qt(tableName)}` query1.sendTo((ss, ...vs) => { console.log([ss, vs]) // -> [ [ 'select col1 from ', '' ], [ 'tab1' ] ] // This is not what is wanted. }) query2.sendTo((ss, ...vs) => { console.log([ss, vs]) // -> [ [ 'select col1 from tab1' ], [] ] })
unquote
can embed anotherquasiquote
.import { quasiquote as qq, unquote as uq } from 'qqtag' let colvalue = '2' const query = qq`select col1 from tab1 ${uq(() => { if (colvalue != null) { return qq`where col2 = ${colvalue}` } })} ` query.sendTo((ss, ...vs) => { console.log([ss, vs]) // -> [ [ 'select col1 from tab1\n where col2 = ', '\n' ], [ '2' ] ] }) colvalue = null query.sendTo((ss, ...vs) => { console.log([ss, vs]) // -> [ [ 'select col1 from tab1\n \n' ], [] ] })
Example
(These examples may be stale and may not work, but the concept will be apply.)
mysql
const newQuery = (conn) =>
(ss, ...vs) => new Promise((resolve, reject) => {
conn.query(newQueryOpts(ss, ...vs), (error, results, fields) => {
if (error) return reject(error)
return resolve({ results, fields })
})
})
const newQueryOpts = (ss, ...vs) => {
const values = vs
const sql = ss.slice(1).reduce((a, s, i) => `${a} ? ${s}`, ss[0])
return { sql, values }
}
const newSql = (conn) =>
(ss, ...vs) => vs.reduce((a, c, i) => `${a}${conn.escape(c)}${ss[i + 1]}`, ss[0])
const listUsers = async (conn, opts) => {
return await qq`\
select * from users \
${uq(() => opts.userId != null ? qq`where user_id=${opts.userId}` : undefined)} ;\
`.sendTo(newQuery(conn))
}
const getLiteralSqlForListUsers = async (conn, opts) => {
return await qq`\
select * from users \
${uq(() => opts.userId != null ? qq`where user_id=${opts.userId}` : undefined)} ;\
`.sendTo(newSql(conn))
}
@azure/cosmos (v2)
const newQuery = (ss, ...vs) => {
const parameters = vs.reduce((a, v, i) => (a.push({ name: `@p_${i}`, value: vs[i] }), a), [])
const query = ss.slice(1).reduce((a, s, i) => `${a} ${parameters[i].name} ${s}`, ss[0])
return { query, parameters }
}
const querySpec = qq`select * from ${qt(collectionName)} t where t.UserId = ${userId}`.sendTo(newQuery)
const feedOptios = {
// ...
}
const queryIterator = collection.container.items.query(querySpec, feedOptios)
mssql: pass typed query parameters
class BaseCustomSqlType {
constructor(value) {
this.value = value
}
valueOf() {
return this.value
}
}
class VarChar extends BaseCustomSqlType {}
class Numeric extends BaseCustomSqlType {}
sql.map.register(VarChar, sql.VarChar)
sql.map.register(Numeric, sql.Numeric)
const value = 123
const query = qq`select * from tab1 where col1 = ${new Numeric(value)}`
Public API
quasiquote: (ss: TemplateStringsArray, ...vs: any[]) => QuasiQuote
- Construct a quasisquote.
quote: (value: any) => Quote
- Construct a quote that can be embeded in a quasiquote.
unquote: (qq: Quote | QuasiQuote | (() => any) | Promise<Quote> | Promise<QuasiQuote> | (() => Promise<any>)) => UnQuote
- Construct an unquote that can be embeded in a quasiquote.
concatQ: (sep: string, qs: QuasiQuote[]) => QuasiQuote
- Concatenate quasiquotes.
stringify: (ss: TemplateStringsArray, ...vs: any[]) => any
- A tag function that stringify a string literal to the same string as that string literal.
class QuasiQuote
- (constructor is not intended to be used to create a instance directly)
sendTo<T>(f: ((ss: TemplateStringsArray, ...vs: any[]) => T) | ((ss: readonly string[], ...vs: any[]) => T)): T
- Pass a quasiquote to a tag function.
sendToAsync<T>(f: ((ss: TemplateStringsArray, ...vs: any[]) => (T | Promise<T>)) | ((ss: readonly string[], ...vs: any[]) => (T | Promise<T>))): Promise<T>
- The async version of
sendTo
.- If Promises are embedded in unquotes, the async version should be used.
- If
f
returns a promise, it is also awaited.
- The async version of
intoTag(): [TemplateStringsArray, ...any[]]
- Convert a quasiquote to a captured tagged literal that can be passed to a tag function.
intoTagAsync(): Promise<[TemplateStringsArray, ...any[]]>
- The async version of
intoTag
.- If Promises are embedded in unquotes, the async version should be used.
- The async version of
evaluated(): QuasiQuote
- Evaluate a captured quasiquote.
- Before evaluated,
sendTo
orintoTag
of a quasiquote evaluates embeded functions in unquotes lazily.
- Before evaluated,
- Evaluate a captured quasiquote.
evaluatedAsync(): Promise<QuasiQuote>
- The async version of
evaluated
.
- The async version of
get isEmpty(): boolean
- Return if a quasiquote is empty (that is, just `` .)
static empty(): QuasiQuote
- Construct an empty quasiquote.
static joinQ(sep: string, q1: QuasiQuote, q2: QuasiQuote): QuasiQuote
- Join two quasiquotes.
class Quote
- (constructor is not intended to be used to create a instance directly)
class UnQuote
- (constructor is not intended to be used to create a instance directly)
References
- nest-literal seems to provide similar functionalities.
License
This library is licensed under the MIT License.