File tree Expand file tree Collapse file tree 6 files changed +152
-0
lines changed Expand file tree Collapse file tree 6 files changed +152
-0
lines changed Original file line number Diff line number Diff line change @@ -133,6 +133,21 @@ const user = db
133
133
.all ();
134
134
` ` `
135
135
136
+ ### parameter-prefix
137
+
138
+ Enforce that all queries use the same prefix for named parameters.
139
+ Can be configured to one of ` : ` (default), ` @` , or ` $` .
140
+
141
+ ` ` ` ts
142
+ // Bad
143
+ /* eslint "sqlite/parameter-prefix": ["error", ":"] */
144
+ db .prepare (" SELECT * FROM users WHERE id = @id" );
145
+
146
+ // Good
147
+ /* eslint "sqlite/parameter-prefix": ["error", ":"] */
148
+ db .prepare (" SELECT * FROM users WHERE id = :id" );
149
+ ` ` `
150
+
136
151
## License
137
152
138
153
eslint-plugin-sqlite is licensed under the [MIT License](LICENSE) and
Original file line number Diff line number Diff line change 1
1
pub mod nullable;
2
2
pub mod parameters;
3
+ pub mod prefix;
3
4
pub mod query;
Original file line number Diff line number Diff line change
1
+ use fallible_iterator:: FallibleIterator ;
2
+ use sqlite3_parser:: {
3
+ ast:: { fmt:: ToTokens , ParameterInfo } ,
4
+ lexer:: sql:: Parser ,
5
+ } ;
6
+ use wasm_bindgen:: prelude:: * ;
7
+
8
+ #[ wasm_bindgen]
9
+ pub fn does_all_named_parameters_start_with_prefix ( query : & str , prefix : char ) -> Option < bool > {
10
+ let mut parser = Parser :: new ( query. as_bytes ( ) ) ;
11
+ let cmd = parser. next ( ) . ok ( ) ??;
12
+
13
+ let mut parameters = ParameterInfo :: default ( ) ;
14
+
15
+ cmd. to_tokens ( & mut parameters) . ok ( ) ?;
16
+
17
+ Some ( parameters. names . iter ( ) . all ( |n| n. starts_with ( prefix) ) )
18
+ }
Original file line number Diff line number Diff line change @@ -4,6 +4,7 @@ import type { TSESLint } from "@typescript-eslint/utils";
4
4
import SQLite from "better-sqlite3" ;
5
5
6
6
import { GetDatabaseOptions , RuleOptions } from "./ruleOptions.js" ;
7
+ import { parameterPrefixRule } from "./rules/parameter-prefix.js" ;
7
8
import { typedInputRule } from "./rules/typed-input.js" ;
8
9
import { createTypedResultRule } from "./rules/typed-result.js" ;
9
10
import { createValidQueryRule } from "./rules/valid-query.js" ;
@@ -67,6 +68,7 @@ export function createSqlitePlugin(options: CreatePluginOptions) {
67
68
"valid-query" : createValidQueryRule ( ruleOptions ) ,
68
69
"typed-result" : createTypedResultRule ( ruleOptions ) ,
69
70
"typed-input" : typedInputRule ,
71
+ "parameter-prefix" : parameterPrefixRule ,
70
72
} ,
71
73
} satisfies TSESLint . FlatConfig . Plugin ;
72
74
Original file line number Diff line number Diff line change
1
+ import { ESLintUtils , TSESTree } from "@typescript-eslint/utils" ;
2
+
3
+ import { does_all_named_parameters_start_with_prefix } from "../parser/parser.js" ;
4
+ import { getQueryValue } from "../utils.js" ;
5
+
6
+ export const parameterPrefixRule = ESLintUtils . RuleCreator . withoutDocs ( {
7
+ create ( context , options ) {
8
+ const expectedPrefix = options [ 0 ] ;
9
+
10
+ return {
11
+ 'CallExpression[callee.type=MemberExpression][callee.property.name="prepare"][arguments.length=1]' (
12
+ node : Omit < TSESTree . CallExpression , "arguments" | "callee" > & {
13
+ arguments : [ TSESTree . CallExpression [ "arguments" ] [ 0 ] ] ;
14
+ callee : TSESTree . MemberExpression ;
15
+ } ,
16
+ ) {
17
+ const arg = node . arguments [ 0 ] ;
18
+
19
+ const val = getQueryValue ( arg , context . sourceCode . getScope ( arg ) ) ;
20
+
21
+ if ( typeof val ?. value !== "string" ) {
22
+ return ;
23
+ }
24
+
25
+ const result = does_all_named_parameters_start_with_prefix (
26
+ val . value ,
27
+ expectedPrefix ,
28
+ ) ;
29
+
30
+ if ( result === false ) {
31
+ context . report ( {
32
+ node : arg ,
33
+ messageId : "incorrectPrefixUsed" ,
34
+ data : {
35
+ prefix : expectedPrefix ,
36
+ } ,
37
+ } ) ;
38
+ }
39
+ } ,
40
+ } ;
41
+ } ,
42
+ meta : {
43
+ messages : {
44
+ incorrectPrefixUsed :
45
+ "Query uses a named parameter prefix that isn't permitted, use '{{prefix}}' instead." ,
46
+ } ,
47
+ schema : [
48
+ {
49
+ type : "string" ,
50
+ enum : [ ":" , "@" , "$" ] ,
51
+ } ,
52
+ ] ,
53
+ type : "problem" ,
54
+ } ,
55
+ defaultOptions : [ ":" ] ,
56
+ } ) ;
Original file line number Diff line number Diff line change
1
+ import { parameterPrefixRule } from "../../src/rules/parameter-prefix.js" ;
2
+ import { ruleTester } from "./rule-tester.js" ;
3
+
4
+ ruleTester . run ( "parameter-prefix" , parameterPrefixRule , {
5
+ valid : [
6
+ {
7
+ code : "db.prepare('SELECT * FROM foo WHERE id = :id')" ,
8
+ } ,
9
+ {
10
+ code : "db.prepare('SELECT * FROM foo WHERE id = :id')" ,
11
+ options : [ ":" ] ,
12
+ } ,
13
+ {
14
+ code : "db.prepare('SELECT * FROM foo WHERE id = @id')" ,
15
+ options : [ "@" ] ,
16
+ } ,
17
+ {
18
+ code : "db.prepare('SELECT * FROM foo WHERE id = $id')" ,
19
+ options : [ "$" ] ,
20
+ } ,
21
+ ] ,
22
+ invalid : [
23
+ {
24
+ code : 'db.prepare("SELECT * FROM foo WHERE id = :id")' ,
25
+ options : [ "@" ] ,
26
+ errors : [
27
+ {
28
+ messageId : "incorrectPrefixUsed" ,
29
+ data : {
30
+ prefix : "@" ,
31
+ } ,
32
+ } ,
33
+ ] ,
34
+ } ,
35
+ {
36
+ code : 'db.prepare("SELECT * FROM foo WHERE id = @id")' ,
37
+ options : [ "$" ] ,
38
+ errors : [
39
+ {
40
+ messageId : "incorrectPrefixUsed" ,
41
+ data : {
42
+ prefix : "$" ,
43
+ } ,
44
+ } ,
45
+ ] ,
46
+ } ,
47
+ {
48
+ code : 'db.prepare("SELECT * FROM foo WHERE id = $id")' ,
49
+ options : [ ":" ] ,
50
+ errors : [
51
+ {
52
+ messageId : "incorrectPrefixUsed" ,
53
+ data : {
54
+ prefix : ":" ,
55
+ } ,
56
+ } ,
57
+ ] ,
58
+ } ,
59
+ ] ,
60
+ } ) ;
You can’t perform that action at this time.
0 commit comments