eslint-plugin-slonik 1.9.0 → 1.10.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -79,10 +79,10 @@ export default [
79
79
  | `sql.interval({...})` | ✅ Full | Extracts type → `$1::interval` |
80
80
  | `sql.json(value)` | ✅ Full | Extracts type → `$1::json` |
81
81
  | `sql.jsonb(value)` | ✅ Full | Extracts type → `$1::jsonb` |
82
- | `sql.literalValue(value)` | ✅ Full | Extracts type`$1` |
82
+ | `sql.literalValue(value)` | ✅ Full | Embeds as literal → `''` |
83
83
  | `sql.uuid(str)` | ✅ Full | Extracts type → `$1::uuid` |
84
+ | `sql.binary(buffer)` | ✅ Full | Extracts type → `$1::bytea` |
84
85
  | `sql.join([...], glue)` | ✅ Skip | Skipped (runtime content) |
85
- | `sql.binary(buffer)` | ✅ Skip | Skipped |
86
86
 
87
87
  ### How It Works
88
88
 
@@ -136,13 +136,19 @@ sql.type(z.object({ id: z.number() }))`
136
136
  sql.type(z.object({ result: z.string() }))`
137
137
  SELECT ${sql.literalValue('hello')} AS result
138
138
  `;
139
- // → Validates: SELECT $1 AS result
139
+ // → Validates: SELECT '' AS result
140
140
 
141
141
  // sql.uuid for UUID values
142
142
  sql.type(z.object({ id: z.number() }))`
143
143
  SELECT id FROM users WHERE external_id = ${sql.uuid(externalId)}
144
144
  `;
145
145
  // → Validates: SELECT id FROM users WHERE external_id = $1::uuid
146
+
147
+ // sql.binary for binary data
148
+ sql.type(z.object({ id: z.number() }))`
149
+ UPDATE files SET content = ${sql.binary(buffer)} WHERE id = ${id}
150
+ `;
151
+ // → Validates: UPDATE files SET content = $1::bytea WHERE id = $2
146
152
  ```
147
153
 
148
154
  **Graceful Skip** means the plugin recognizes Slonik tokens and skips validation for those expressions, preventing false positives:
package/dist/index.cjs CHANGED
@@ -477,6 +477,20 @@ function isSlonikUuidCall(expression) {
477
477
  const objectName = getMemberExpressionObjectName(callee.object);
478
478
  return objectName === "sql";
479
479
  }
480
+ function isSlonikBinaryCall(expression) {
481
+ if (expression.type !== "CallExpression") {
482
+ return false;
483
+ }
484
+ const callee = expression.callee;
485
+ if (callee.type !== "MemberExpression") {
486
+ return false;
487
+ }
488
+ if (callee.property.type !== "Identifier" || callee.property.name !== "binary") {
489
+ return false;
490
+ }
491
+ const objectName = getMemberExpressionObjectName(callee.object);
492
+ return objectName === "sql";
493
+ }
480
494
  function extractSlonikFragment(expression) {
481
495
  if (expression.type !== "TaggedTemplateExpression") {
482
496
  return null;
@@ -689,7 +703,7 @@ function mapTemplateLiteralToQueryText(quasi, parser, checker, options, sourceCo
689
703
  continue;
690
704
  }
691
705
  if (isSlonikLiteralValueCall(expression)) {
692
- const placeholder2 = `$${++$idx}`;
706
+ const placeholder2 = `''`;
693
707
  $queryText += placeholder2;
694
708
  sourcemaps.push({
695
709
  original: {
@@ -724,6 +738,24 @@ function mapTemplateLiteralToQueryText(quasi, parser, checker, options, sourceCo
724
738
  });
725
739
  continue;
726
740
  }
741
+ if (isSlonikBinaryCall(expression)) {
742
+ const placeholder2 = `$${++$idx}::bytea`;
743
+ $queryText += placeholder2;
744
+ sourcemaps.push({
745
+ original: {
746
+ start: expression.range[0] - quasi.range[0] - 2,
747
+ end: expression.range[1] - quasi.range[0],
748
+ text: sourceCode.text.slice(expression.range[0] - 2, expression.range[1] + 1)
749
+ },
750
+ generated: {
751
+ start: position,
752
+ end: position + placeholder2.length,
753
+ text: placeholder2
754
+ },
755
+ offset: 0
756
+ });
757
+ continue;
758
+ }
727
759
  const slonikUnnestTypes = extractSlonikUnnestTypes(expression);
728
760
  if (slonikUnnestTypes !== null) {
729
761
  const placeholders = slonikUnnestTypes.map((type) => `$${++$idx}::${type}`);