Skip to content

Commit 15890ee

Browse files
committed
feat: support any string value in map and join
1 parent 1ea15c5 commit 15890ee

File tree

2 files changed

+47
-22
lines changed

2 files changed

+47
-22
lines changed

src/utils.ts

Lines changed: 36 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,27 @@ export function getQueryValue(
3535
}
3636

3737
if (arg.type === TSESTree.AST_NODE_TYPES.TemplateLiteral) {
38-
if (arg.expressions.every((expr) => isVariableParameterExpression(expr))) {
39-
return {
40-
value: arg.quasis.map((quasi) => quasi.value.cooked).join("?"),
41-
};
38+
let query = "";
39+
40+
for (let i = 0; i < arg.quasis.length; i++) {
41+
const quasi = arg.quasis[i];
42+
if (quasi) {
43+
query += quasi.value.cooked;
44+
45+
const expr = arg.expressions[i];
46+
if (expr) {
47+
const value = getParameterExpressionValue(expr);
48+
if (value == null) {
49+
return null;
50+
}
51+
query += value;
52+
}
53+
}
4254
}
4355

44-
return null;
56+
return {
57+
value: query,
58+
};
4559
}
4660

4761
if (arg.type === TSESTree.AST_NODE_TYPES.Identifier) {
@@ -63,61 +77,62 @@ export function getQueryValue(
6377
}
6478

6579
/**
66-
* Checks if the expression looks like `foo.map(() => '?').join(',')`
80+
* If the expression looks like `foo.map(() => '<value>').join('<separator>')`
81+
* then the value of the map callback is returned.
6782
*/
68-
function isVariableParameterExpression(expr: TSESTree.Expression) {
83+
function getParameterExpressionValue(expr: TSESTree.Expression) {
6984
if (expr.type !== TSESTree.AST_NODE_TYPES.CallExpression || expr.optional) {
70-
return false;
85+
return null;
7186
}
7287

7388
if (expr.callee.type !== TSESTree.AST_NODE_TYPES.MemberExpression) {
74-
return false;
89+
return null;
7590
}
7691

7792
if (!expr.arguments[0]) {
78-
return false;
93+
return null;
7994
}
8095

8196
if (ASTUtils.getPropertyName(expr.callee) !== "join") {
82-
return false;
97+
return null;
8398
}
8499

85100
const joinValue = ASTUtils.getStaticValue(expr.arguments[0]);
86-
if (typeof joinValue?.value !== "string" || joinValue.value.trim() !== ",") {
87-
return false;
101+
if (typeof joinValue?.value !== "string") {
102+
return null;
88103
}
89104

90105
if (
91106
expr.callee.object.type !== TSESTree.AST_NODE_TYPES.CallExpression ||
92107
expr.callee.object.optional
93108
) {
94-
return false;
109+
return null;
95110
}
96111

97112
const maybeMapExpr = expr.callee.object;
98113

99114
if (maybeMapExpr.callee.type !== TSESTree.AST_NODE_TYPES.MemberExpression) {
100-
return false;
115+
return null;
101116
}
102117

103118
if (ASTUtils.getPropertyName(maybeMapExpr.callee) !== "map") {
104-
return false;
119+
return null;
105120
}
106121

107122
if (!maybeMapExpr.arguments[0]) {
108-
return false;
123+
return null;
109124
}
110125

111126
const maybeCallback = maybeMapExpr.arguments[0];
112127

113128
if (maybeCallback.type !== TSESTree.AST_NODE_TYPES.ArrowFunctionExpression) {
114-
return false;
129+
return null;
115130
}
116131

117132
const mapValue = ASTUtils.getStaticValue(maybeCallback.body);
118-
if (typeof mapValue?.value !== "string" || mapValue.value.trim() !== "?") {
119-
return false;
133+
if (typeof mapValue?.value !== "string") {
134+
return null;
120135
}
121136

122-
return true;
137+
return mapValue.value;
123138
}

tests/rules/valid-query.test.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ db.exec(`
1717

1818
const db_users = new SQLite(":memory:");
1919
db_users.exec(`
20-
CREATE TABLE users (id int);
20+
CREATE TABLE users (id int, name text);
2121
`);
2222

2323
const rule = createValidQueryRule({
@@ -36,6 +36,8 @@ ruleTester.run("valid-query", rule, {
3636
"db.prepare('DELETE FROM foo')",
3737
"db_users.prepare(`SELECT * FROM users WHERE id IN (${ids.map(() => '?').join(',')})`);",
3838
"const query = `SELECT * FROM users WHERE id IN (${ids.map(() => '?').join(',')})`;db_users.prepare(query);",
39+
"db_users.prepare(`SELECT * FROM users WHERE ${ids.map(() => 'NAME LIKE ? || \\'%\\'').join(' OR ')}`);",
40+
"const query = `SELECT * FROM users WHERE ${ids.map(() => 'NAME LIKE ? || \\'%\\'').join(' OR ')}`;db_users.prepare(query);",
3941
],
4042
invalid: [
4143
{
@@ -46,6 +48,14 @@ ruleTester.run("valid-query", rule, {
4648
},
4749
],
4850
},
51+
{
52+
code: "db_users.prepare(`SELECT * FROM users WHERE ${ids.map(() => unknownValue).join(' OR ')}`);",
53+
errors: [
54+
{
55+
messageId: "nonStaticQuery",
56+
},
57+
],
58+
},
4959
{
5060
code: "db.prepare(42)",
5161
errors: [

0 commit comments

Comments
 (0)