@databases/sql
Advanced tools
Comparing version 3.0.0 to 3.1.0
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const __1 = require("../"); | ||
test('correctly renders sql', () => { | ||
const query = __1.default` | ||
const query = __1.default ` | ||
SELECT * | ||
@@ -16,11 +11,8 @@ FROM foo | ||
`; | ||
expect(query.format({ | ||
escapeIdentifier: () => { | ||
throw new Error('not implemented'); | ||
}, | ||
formatValue: value => ({ | ||
placeholder: '?', | ||
value | ||
}) | ||
})).toMatchInlineSnapshot(` | ||
expect(query.format({ | ||
escapeIdentifier: () => { | ||
throw new Error('not implemented'); | ||
}, | ||
formatValue: (value) => ({ placeholder: '?', value }), | ||
})).toMatchInlineSnapshot(` | ||
Object { | ||
@@ -39,17 +31,17 @@ "text": "SELECT * | ||
test('can join parts of query', () => { | ||
const conditions = [__1.default`id = ${10}`, __1.default`created_at > ${new Date(1545238400939)}`]; | ||
const query = __1.default` | ||
const conditions = [ | ||
__1.default `id = ${10}`, | ||
__1.default `created_at > ${new Date(1545238400939)}`, | ||
]; | ||
const query = __1.default ` | ||
SELECT * | ||
FROM foo | ||
WHERE ${__1.default.join(conditions, __1.default` AND `)}; | ||
WHERE ${__1.default.join(conditions, __1.default ` AND `)}; | ||
`; | ||
expect(query.format({ | ||
escapeIdentifier: () => { | ||
throw new Error('not implemented'); | ||
}, | ||
formatValue: value => ({ | ||
placeholder: '?', | ||
value | ||
}) | ||
})).toMatchInlineSnapshot(` | ||
expect(query.format({ | ||
escapeIdentifier: () => { | ||
throw new Error('not implemented'); | ||
}, | ||
formatValue: (value) => ({ placeholder: '?', value }), | ||
})).toMatchInlineSnapshot(` | ||
Object { | ||
@@ -67,13 +59,9 @@ "text": "SELECT * | ||
test('can read in a file', () => { | ||
const query = __1.default.file(`${__dirname}/fixture.sql`); | ||
expect(query.format({ | ||
escapeIdentifier: () => { | ||
throw new Error('not implemented'); | ||
}, | ||
formatValue: value => ({ | ||
placeholder: '?', | ||
value | ||
}) | ||
})).toMatchInlineSnapshot(` | ||
const query = __1.default.file(`${__dirname}/fixture.sql`); | ||
expect(query.format({ | ||
escapeIdentifier: () => { | ||
throw new Error('not implemented'); | ||
}, | ||
formatValue: (value) => ({ placeholder: '?', value }), | ||
})).toMatchInlineSnapshot(` | ||
Object { | ||
@@ -84,2 +72,3 @@ "text": "SELECT * FROM my_table;", | ||
`); | ||
}); | ||
}); | ||
//# sourceMappingURL=index.test.js.map |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.SQLItemType = exports.isSqlQuery = void 0; | ||
const fs_1 = require("fs"); | ||
const web_1 = require("./web"); | ||
exports.SQLItemType = web_1.SQLItemType; | ||
exports.isSqlQuery = web_1.isSqlQuery; // Create the SQL interface we export. | ||
Object.defineProperty(exports, "SQLItemType", { enumerable: true, get: function () { return web_1.SQLItemType; } }); | ||
Object.defineProperty(exports, "isSqlQuery", { enumerable: true, get: function () { return web_1.isSqlQuery; } }); | ||
// Create the SQL interface we export. | ||
const sql = Object.assign(web_1.default, { | ||
file: filename => web_1.default.__dangerous__rawValue(fs_1.readFileSync(filename, 'utf8')) | ||
file: (filename) => web_1.default.__dangerous__rawValue(fs_1.readFileSync(filename, 'utf8')), | ||
}); | ||
@@ -21,2 +16,3 @@ exports.default = sql; | ||
module.exports.isSqlQuery = web_1.isSqlQuery; | ||
module.exports.SQLItemType = web_1.SQLItemType; | ||
module.exports.SQLItemType = web_1.SQLItemType; | ||
//# sourceMappingURL=index.js.map |
409
lib/web.js
@@ -1,16 +0,21 @@ | ||
"use strict"; // @public | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.isSqlQuery = exports.SQLItemType = void 0; | ||
var SQLItemType; | ||
(function (SQLItemType) { | ||
SQLItemType[SQLItemType["RAW"] = 0] = "RAW"; | ||
SQLItemType[SQLItemType["VALUE"] = 1] = "VALUE"; | ||
SQLItemType[SQLItemType["IDENTIFIER"] = 2] = "IDENTIFIER"; | ||
SQLItemType[SQLItemType["RAW"] = 0] = "RAW"; | ||
SQLItemType[SQLItemType["VALUE"] = 1] = "VALUE"; | ||
SQLItemType[SQLItemType["IDENTIFIER"] = 2] = "IDENTIFIER"; | ||
})(SQLItemType = exports.SQLItemType || (exports.SQLItemType = {})); | ||
const formatter = Symbol('SQL Query Formatter'); | ||
const literalSeparators = new Set(['', ',', ', ', ' AND ', ' OR ', ') AND (', ') OR (', ';']); | ||
const literalSeparators = new Set([ | ||
'', | ||
',', | ||
', ', | ||
' AND ', | ||
' OR ', | ||
') AND (', | ||
') OR (', | ||
';', | ||
]); | ||
/** | ||
@@ -25,231 +30,184 @@ * The representation of a SQL query. Call `compile` to turn it into a SQL | ||
*/ | ||
class SQLQuery { | ||
// The constructor is private. Users should use the static `create` method to | ||
// make a new `SQLQuery`. | ||
constructor(items) { | ||
this._cache = new Map(); | ||
this._items = items; | ||
} | ||
static registerFormatter(constructor, format) { | ||
constructor.prototype[formatter] = format; | ||
} | ||
/** | ||
* A template string tag that interpolates literal SQL with placeholder SQL | ||
* values. | ||
*/ | ||
static query(strings, ...values) { | ||
const items = []; // Add all of the strings as raw items and values as placeholder values. | ||
for (let i = 0; i < strings.length; i++) { | ||
if (strings[i]) { | ||
items.push({ | ||
type: SQLItemType.RAW, | ||
text: strings[i] | ||
}); | ||
} | ||
if (i < values.length) { | ||
const value = values[i]; // If the value is a `SQLQuery`, add all of its items. | ||
if (value instanceof SQLQuery) { | ||
for (const item of value._items) items.push(item); | ||
} else { | ||
if (value && typeof value === 'object' && formatter in value) { | ||
const formatted = value[formatter](value); | ||
if (!(formatted instanceof SQLQuery)) { | ||
throw new Error('Formatters should always return SQLQuery objects'); | ||
// The constructor is private. Users should use the static `create` method to | ||
// make a new `SQLQuery`. | ||
constructor(items) { | ||
this._cache = new Map(); | ||
this._items = items; | ||
} | ||
static registerFormatter(constructor, format) { | ||
constructor.prototype[formatter] = format; | ||
} | ||
/** | ||
* A template string tag that interpolates literal SQL with placeholder SQL | ||
* values. | ||
*/ | ||
static query(strings, ...values) { | ||
const items = []; | ||
// Add all of the strings as raw items and values as placeholder values. | ||
for (let i = 0; i < strings.length; i++) { | ||
if (strings[i]) { | ||
items.push({ type: SQLItemType.RAW, text: strings[i] }); | ||
} | ||
for (const item of formatted._items) items.push(item); | ||
} else if (typeof value === 'bigint') { | ||
items.push({ | ||
type: SQLItemType.VALUE, | ||
value: value.toString(10) | ||
}); | ||
} else { | ||
if (strings[i + 1] && strings[i + 1].startsWith("'") && strings[i].endsWith("'")) { | ||
throw new Error(`You do not need to wrap values in 'quotes' when using @databases. Any JavaScript string passed via \${...} syntax is already treated as a string. Please remove the quotes around this value.`); | ||
if (i < values.length) { | ||
const value = values[i]; | ||
// If the value is a `SQLQuery`, add all of its items. | ||
if (value instanceof SQLQuery) { | ||
for (const item of value._items) | ||
items.push(item); | ||
} | ||
else { | ||
if (value && typeof value === 'object' && formatter in value) { | ||
const formatted = value[formatter](value); | ||
if (!(formatted instanceof SQLQuery)) { | ||
throw new Error('Formatters should always return SQLQuery objects'); | ||
} | ||
for (const item of formatted._items) | ||
items.push(item); | ||
} | ||
else if (typeof value === 'bigint') { | ||
items.push({ type: SQLItemType.VALUE, value: value.toString(10) }); | ||
} | ||
else { | ||
if (strings[i + 1] && | ||
strings[i + 1].startsWith("'") && | ||
strings[i].endsWith("'")) { | ||
throw new Error(`You do not need to wrap values in 'quotes' when using @databases. Any JavaScript string passed via \${...} syntax is already treated as a string. Please remove the quotes around this value.`); | ||
} | ||
items.push({ type: SQLItemType.VALUE, value }); | ||
} | ||
} | ||
} | ||
items.push({ | ||
type: SQLItemType.VALUE, | ||
value | ||
}); | ||
} | ||
} | ||
} | ||
return new SQLQuery(items); | ||
} | ||
return new SQLQuery(items); | ||
} | ||
/** | ||
* Joins multiple queries together and puts a separator in between if a | ||
* separator was defined. | ||
*/ | ||
static join(queries, separator) { | ||
if (typeof separator === 'string' && !literalSeparators.has(separator)) { | ||
throw new Error(`Please tag your string as an SQL query via "sql.join(..., sql\`${separator.includes('`') ? 'your_separator' : separator}\`)" or use one of the standard speparators: ${[...literalSeparators].map(s => `"${s}"`).join(', ')}`); | ||
/** | ||
* Joins multiple queries together and puts a separator in between if a | ||
* separator was defined. | ||
*/ | ||
static join(queries, separator) { | ||
if (typeof separator === 'string' && !literalSeparators.has(separator)) { | ||
throw new Error(`Please tag your string as an SQL query via "sql.join(..., sql\`${separator.includes('`') ? 'your_separator' : separator}\`)" or use one of the standard speparators: ${[...literalSeparators] | ||
.map((s) => `"${s}"`) | ||
.join(', ')}`); | ||
} | ||
const items = []; | ||
const separatorItems = separator | ||
? typeof separator === 'string' | ||
? [{ type: SQLItemType.RAW, text: separator }] | ||
: separator._items | ||
: undefined; | ||
let addedFirst = false; | ||
// Add the items of all our queries into the `items` array, adding text | ||
// separator items as necessary. | ||
for (const query of queries) { | ||
if (!addedFirst) { | ||
addedFirst = true; | ||
} | ||
else if (separatorItems) { | ||
items.push(...separatorItems); | ||
} | ||
items.push(...query._items); | ||
} | ||
return new SQLQuery(items); | ||
} | ||
const items = []; | ||
const separatorItems = separator ? typeof separator === 'string' ? [{ | ||
type: SQLItemType.RAW, | ||
text: separator | ||
}] : separator._items : undefined; | ||
let addedFirst = false; // Add the items of all our queries into the `items` array, adding text | ||
// separator items as necessary. | ||
for (const query of queries) { | ||
if (!addedFirst) { | ||
addedFirst = true; | ||
} else if (separatorItems) { | ||
items.push(...separatorItems); | ||
} | ||
items.push(...query._items); | ||
/** | ||
* Creates a new query with the raw text. | ||
*/ | ||
static __dangerous__rawValue(text) { | ||
return new SQLQuery([{ type: SQLItemType.RAW, text }]); | ||
} | ||
return new SQLQuery(items); | ||
} | ||
/** | ||
* Creates a new query with the raw text. | ||
*/ | ||
static __dangerous__rawValue(text) { | ||
return new SQLQuery([{ | ||
type: SQLItemType.RAW, | ||
text | ||
}]); | ||
} | ||
/** | ||
* Creates a new query from the array of `SQLItem` parts | ||
*/ | ||
static __dangerous__constructFromParts(items) { | ||
return new SQLQuery(items); | ||
} | ||
/** | ||
* Creates a new query with the value. This value will be turned into a | ||
* placeholder when the query gets compiled. | ||
*/ | ||
static value(value) { | ||
return new SQLQuery([{ | ||
type: SQLItemType.VALUE, | ||
value | ||
}]); | ||
} | ||
/** | ||
* Creates an identifier query. Each name will be escaped, and the | ||
* names will be concatenated with a period (`.`). | ||
*/ | ||
static ident(...names) { | ||
return new SQLQuery([{ | ||
type: SQLItemType.IDENTIFIER, | ||
names | ||
}]); | ||
} | ||
format(formatter) { | ||
const cached = this._cache.get(formatter); | ||
if (cached) return cached; | ||
const fresh = typeof formatter === 'function' ? formatter(this._items) : formatStandard(this._items, formatter); | ||
this._cache.set(formatter, fresh); | ||
return fresh; | ||
} | ||
/** | ||
* Creates a new query from the array of `SQLItem` parts | ||
*/ | ||
static __dangerous__constructFromParts(items) { | ||
return new SQLQuery(items); | ||
} | ||
/** | ||
* Creates a new query with the value. This value will be turned into a | ||
* placeholder when the query gets compiled. | ||
*/ | ||
static value(value) { | ||
return new SQLQuery([{ type: SQLItemType.VALUE, value }]); | ||
} | ||
/** | ||
* Creates an identifier query. Each name will be escaped, and the | ||
* names will be concatenated with a period (`.`). | ||
*/ | ||
static ident(...names) { | ||
return new SQLQuery([{ type: SQLItemType.IDENTIFIER, names }]); | ||
} | ||
format(formatter) { | ||
const cached = this._cache.get(formatter); | ||
if (cached) | ||
return cached; | ||
const fresh = typeof formatter === 'function' | ||
? formatter(this._items) | ||
: formatStandard(this._items, formatter); | ||
this._cache.set(formatter, fresh); | ||
return fresh; | ||
} | ||
} | ||
function formatStandard(items, { | ||
escapeIdentifier, | ||
formatValue | ||
}) { | ||
// Create an empty query object. | ||
let text = ''; | ||
const values = []; | ||
const localIdentifiers = new Map(); | ||
for (const item of items) { | ||
switch (item.type) { | ||
// If this is just raw text, we add it directly to the query text. | ||
case SQLItemType.RAW: | ||
{ | ||
text += item.text; | ||
break; | ||
function formatStandard(items, { escapeIdentifier, formatValue }) { | ||
// Create an empty query object. | ||
let text = ''; | ||
const values = []; | ||
const localIdentifiers = new Map(); | ||
for (const item of items) { | ||
switch (item.type) { | ||
// If this is just raw text, we add it directly to the query text. | ||
case SQLItemType.RAW: { | ||
text += item.text; | ||
break; | ||
} | ||
// If we got a value SQL item, add a placeholder and add the value to our | ||
// placeholder values array. | ||
case SQLItemType.VALUE: { | ||
const { placeholder, value } = formatValue(item.value, values.length); | ||
text += placeholder; | ||
values.push(value); | ||
break; | ||
} | ||
// If we got an identifier type, escape the strings and get a local | ||
// identifier for non-string identifiers. | ||
case SQLItemType.IDENTIFIER: { | ||
text += item.names | ||
.map((name) => { | ||
if (typeof name === 'string') | ||
return escapeIdentifier(name); | ||
if (!localIdentifiers.has(name)) | ||
localIdentifiers.set(name, `__local_${localIdentifiers.size}__`); | ||
return escapeIdentifier(localIdentifiers.get(name)); | ||
}) | ||
.join('.'); | ||
break; | ||
} | ||
} | ||
// If we got a value SQL item, add a placeholder and add the value to our | ||
// placeholder values array. | ||
case SQLItemType.VALUE: | ||
{ | ||
const { | ||
placeholder, | ||
value | ||
} = formatValue(item.value, values.length); | ||
text += placeholder; | ||
values.push(value); | ||
break; | ||
} | ||
if (text.trim()) { | ||
const lines = text.split('\n'); | ||
const min = Math.min(...lines | ||
.filter((l) => l.trim() !== '') | ||
.map((l) => /^\s*/.exec(l)[0].length)); | ||
if (min) { | ||
text = lines.map((line) => line.substr(min)).join('\n'); | ||
} | ||
// If we got an identifier type, escape the strings and get a local | ||
// identifier for non-string identifiers. | ||
case SQLItemType.IDENTIFIER: | ||
{ | ||
text += item.names.map(name => { | ||
if (typeof name === 'string') return escapeIdentifier(name); | ||
if (!localIdentifiers.has(name)) localIdentifiers.set(name, `__local_${localIdentifiers.size}__`); | ||
return escapeIdentifier(localIdentifiers.get(name)); | ||
}).join('.'); | ||
break; | ||
} | ||
} | ||
} | ||
if (text.trim()) { | ||
const lines = text.split('\n'); | ||
const min = Math.min(...lines.filter(l => l.trim() !== '').map(l => /^\s*/.exec(l)[0].length)); | ||
if (min) { | ||
text = lines.map(line => line.substr(min)).join('\n'); | ||
} | ||
} | ||
return { | ||
text: text.trim(), | ||
values | ||
}; | ||
} // tslint:disable:no-unbound-method | ||
return { text: text.trim(), values }; | ||
} | ||
// tslint:disable:no-unbound-method | ||
// Create the SQL interface we export. | ||
const sql = Object.assign(SQLQuery.query, { | ||
join: SQLQuery.join, | ||
__dangerous__rawValue: SQLQuery.__dangerous__rawValue, | ||
__dangerous__constructFromParts: SQLQuery.__dangerous__constructFromParts, | ||
value: SQLQuery.value, | ||
ident: SQLQuery.ident, | ||
registerFormatter: SQLQuery.registerFormatter | ||
}); // tslint:enable:no-unbound-method | ||
join: SQLQuery.join, | ||
__dangerous__rawValue: SQLQuery.__dangerous__rawValue, | ||
__dangerous__constructFromParts: SQLQuery.__dangerous__constructFromParts, | ||
value: SQLQuery.value, | ||
ident: SQLQuery.ident, | ||
registerFormatter: SQLQuery.registerFormatter, | ||
}); | ||
// tslint:enable:no-unbound-method | ||
exports.default = sql; | ||
function isSqlQuery(query) { | ||
return query instanceof SQLQuery; | ||
return query instanceof SQLQuery; | ||
} | ||
exports.isSqlQuery = isSqlQuery; | ||
@@ -259,2 +217,3 @@ module.exports = sql; | ||
module.exports.isSqlQuery = isSqlQuery; | ||
module.exports.SQLItemType = SQLItemType; | ||
module.exports.SQLItemType = SQLItemType; | ||
//# sourceMappingURL=web.js.map |
{ | ||
"name": "@databases/sql", | ||
"version": "3.0.0", | ||
"version": "3.1.0", | ||
"description": "", | ||
@@ -8,5 +8,3 @@ "main": "./lib/index.js", | ||
"dependencies": {}, | ||
"devDependencies": { | ||
"@types/node": "^13.13.4" | ||
}, | ||
"devDependencies": {}, | ||
"scripts": {}, | ||
@@ -22,5 +20,4 @@ "repository": "https://github.com/ForbesLindesay/atdatabases/tree/master/packages/sql", | ||
"lib/", | ||
"web.js", | ||
"web.d.ts" | ||
"web/" | ||
] | ||
} |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
212228
0
0
404