diff --git a/changelogs/drizzle-orm/0.29.1.md b/changelogs/drizzle-orm/0.29.1.md new file mode 100644 index 000000000..f1f526ed9 --- /dev/null +++ b/changelogs/drizzle-orm/0.29.1.md @@ -0,0 +1,272 @@ +# Fixes + +- Forward args correctly when using withReplica feature #1536. Thanks @Angelelz +- Fix selectDistinctOn not working with multiple columns #1466. Thanks @L-Mario564 + +# New Features/Helpers + +## New helpers for aggregate functions in SQL - Thanks @L-Mario564 + +> Remember, aggregation functions are often used with the GROUP BY clause of the SELECT statement. So if you are selecting using aggregating functions and other columns in one query, +be sure to use the `.groupBy` clause + +Here is a list of functions and equivalent using `sql` template + +**count** +```ts +await db.select({ value: count() }).from(users); +await db.select({ value: count(users.id) }).from(users); + +// It's equivalent to writing +await db.select({ + value: sql`count('*'))`.mapWith(Number) +}).from(users); +await db.select({ + value: sql`count(${users.id})`.mapWith(Number) +}).from(users); +``` + +**countDistinct** +```ts +await db.select({ value: countDistinct(users.id) }).from(users); + +// It's equivalent to writing +await db.select({ + value: sql`count(${users.id})`.mapWith(Number) +}).from(users); +``` + +**avg** +```ts +await db.select({ value: avg(users.id) }).from(users); + +// It's equivalent to writing +await db.select({ + value: sql`avg(${users.id})`.mapWith(String) +}).from(users); +``` + +**avgDistinct** +```ts +await db.select({ value: avgDistinct(users.id) }).from(users); + +// It's equivalent to writing +await db.select({ + value: sql`avg(distinct ${users.id})`.mapWith(String) +}).from(users); +``` + +**sum** +```ts +await db.select({ value: sum(users.id) }).from(users); + +// It's equivalent to writing +await db.select({ + value: sql`sum(${users.id})`.mapWith(String) +}).from(users); +``` + +**sumDistinct** +```ts +await db.select({ value: sumDistinct(users.id) }).from(users); + +// It's equivalent to writing +await db.select({ + value: sql`sum(distinct ${users.id})`.mapWith(String) +}).from(users); +``` + +**max** +```ts +await db.select({ value: max(users.id) }).from(users); + +// It's equivalent to writing +await db.select({ + value: sql`max(${expression})`.mapWith(users.id) +}).from(users); +``` + +**min** +```ts +await db.select({ value: min(users.id) }).from(users); + +// It's equivalent to writing +await db.select({ + value: sql`min(${users.id})`.mapWith(users.id) +}).from(users); +``` + +# New Packages +## ESLint Drizzle Plugin + +For cases where it's impossible to perform type checks for specific scenarios, or where it's possible but error messages would be challenging to understand, we've decided to create an ESLint package with recommended rules. This package aims to assist developers in handling crucial scenarios during development + +> Big thanks to @Angelelz for initiating the development of this package and transferring it to the Drizzle Team's npm + +## Install + +```sh +[ npm | yarn | pnpm | bun ] install eslint eslint-plugin-drizzle +``` +You can install those packages for typescript support in your IDE +```sh +[ npm | yarn | pnpm | bun ] install @typescript-eslint/eslint-plugin @typescript-eslint/parser +``` + +## Usage + +Create a `.eslintrc.yml` file, add `drizzle` to the `plugins`, and specify the rules you want to use. You can find a list of all existing rules below + +```yml +root: true +parser: '@typescript-eslint/parser' +parserOptions: + project: './tsconfig.json' +plugins: + - drizzle +rules: + 'drizzle/enforce-delete-with-where': "error" + 'drizzle/enforce-update-with-where': "error" +``` + +### All config + +This plugin exports an [`all` config](src/configs/all.js) that makes use of all rules (except for deprecated ones). + +```yml +root: true +extends: + - "plugin:drizzle/all" +parser: '@typescript-eslint/parser' +parserOptions: + project: './tsconfig.json' +plugins: + - drizzle +``` + +At the moment, `all` is equivalent to `recommended` + +```yml +root: true +extends: + - "plugin:drizzle/recommended" +parser: '@typescript-eslint/parser' +parserOptions: + project: './tsconfig.json' +plugins: + - drizzle +``` + +## Rules + +**enforce-delete-with-where**: Enforce using `delete` with the`.where()` clause in the `.delete()` statement. Most of the time, you don't need to delete all rows in the table and require some kind of `WHERE` statements. + +**Error Message**: +``` +Without `.where(...)` you will delete all the rows in a table. If you didn't want to do it, please use `db.delete(...).where(...)` instead. Otherwise you can ignore this rule here +``` + +Optionally, you can define a `drizzleObjectName` in the plugin options that accept a `string` or `string[]`. This is useful when you have objects or classes with a delete method that's not from Drizzle. Such a `delete` method will trigger the ESLint rule. To avoid that, you can define the name of the Drizzle object that you use in your codebase (like db) so that the rule would only trigger if the delete method comes from this object: + +Example, config 1: +```json +"rules": { + "drizzle/enforce-delete-with-where": ["error"] +} +``` + +```ts +class MyClass { + public delete() { + return {} + } +} + +const myClassObj = new MyClass(); + +// ---> Will be triggered by ESLint Rule +myClassObj.delete() + +const db = drizzle(...) +// ---> Will be triggered by ESLint Rule +db.delete() +``` + +Example, config 2: +```json +"rules": { + "drizzle/enforce-delete-with-where": ["error", { "drizzleObjectName": ["db"] }], +} +``` +```ts +class MyClass { + public delete() { + return {} + } +} + +const myClassObj = new MyClass(); + +// ---> Will NOT be triggered by ESLint Rule +myClassObj.delete() + +const db = drizzle(...) +// ---> Will be triggered by ESLint Rule +db.delete() +``` + +**enforce-update-with-where**: Enforce using `update` with the`.where()` clause in the `.update()` statement. Most of the time, you don't need to update all rows in the table and require some kind of `WHERE` statements. + +**Error Message**: +``` +Without `.where(...)` you will update all the rows in a table. If you didn't want to do it, please use `db.update(...).set(...).where(...)` instead. Otherwise you can ignore this rule here +``` + +Optionally, you can define a `drizzleObjectName` in the plugin options that accept a `string` or `string[]`. This is useful when you have objects or classes with a delete method that's not from Drizzle. Such as `update` method will trigger the ESLint rule. To avoid that, you can define the name of the Drizzle object that you use in your codebase (like db) so that the rule would only trigger if the delete method comes from this object: + +Example, config 1: +```json +"rules": { + "drizzle/enforce-update-with-where": ["error"] +} +``` + +```ts +class MyClass { + public update() { + return {} + } +} + +const myClassObj = new MyClass(); + +// ---> Will be triggered by ESLint Rule +myClassObj.update() + +const db = drizzle(...) +// ---> Will be triggered by ESLint Rule +db.update() +``` + +Example, config 2: +```json +"rules": { + "drizzle/enforce-update-with-where": ["error", { "drizzleObjectName": ["db"] }], +} +``` +```ts +class MyClass { + public update() { + return {} + } +} + +const myClassObj = new MyClass(); + +// ---> Will NOT be triggered by ESLint Rule +myClassObj.update() + +const db = drizzle(...) +// ---> Will be triggered by ESLint Rule +db.update() +``` \ No newline at end of file diff --git a/changelogs/eslint-plugin-drizzle/0.2.1.md b/changelogs/eslint-plugin-drizzle/0.2.1.md new file mode 100644 index 000000000..b29f78c3a --- /dev/null +++ b/changelogs/eslint-plugin-drizzle/0.2.1.md @@ -0,0 +1,4 @@ +# eslint-plugin-drizzle 0.2.1 + +- Update README.md +- Change error text message \ No newline at end of file diff --git a/drizzle-orm/package.json b/drizzle-orm/package.json index 6d2613d75..2a1658773 100644 --- a/drizzle-orm/package.json +++ b/drizzle-orm/package.json @@ -1,6 +1,6 @@ { "name": "drizzle-orm", - "version": "0.29.0", + "version": "0.29.1", "description": "Drizzle ORM package for SQL databases", "type": "module", "scripts": { diff --git a/eslint-plugin-drizzle/package.json b/eslint-plugin-drizzle/package.json index b9c46b991..f2a51feb9 100644 --- a/eslint-plugin-drizzle/package.json +++ b/eslint-plugin-drizzle/package.json @@ -1,6 +1,6 @@ { "name": "eslint-plugin-drizzle", - "version": "0.2.0", + "version": "0.2.1", "description": "Eslint plugin for drizzle users to avoid common pitfalls", "main": "src/index.js", "scripts": { @@ -15,10 +15,10 @@ "eslint-plugin", "drizzle" ], - "author": "Angelelz", + "author": "Drizzle Team", "repository": { "type": "git", - "url": "git+https://github.com/drizzle-team/drizzle-orm.git" + "url": "git+https://github.com/drizzle-team/drizzle-orm/tree/main/eslint-plugin-drizzle.git" }, "license": "Apache-2.0", "devDependencies": { diff --git a/eslint-plugin-drizzle/readme.md b/eslint-plugin-drizzle/readme.md index fb45efc03..1d45b2345 100644 --- a/eslint-plugin-drizzle/readme.md +++ b/eslint-plugin-drizzle/readme.md @@ -1,85 +1,163 @@ # eslint-plugin-drizzle -eslint plugin for drizzle users to avoid common pitfalls +For cases where it's impossible to perform type checks for specific scenarios, or where it's possible but error messages would be challenging to understand, we've decided to create an ESLint package with recommended rules. This package aims to assist developers in handling crucial scenarios during development + +> Big thanks to @Angelelz for initiating the development of this package and transferring it to the Drizzle Team's npm ## Install ```sh [ npm | yarn | pnpm | bun ] install eslint eslint-plugin-drizzle ``` +You can install those packages for typescript support in your IDE +```sh +[ npm | yarn | pnpm | bun ] install @typescript-eslint/eslint-plugin @typescript-eslint/parser +``` ## Usage -Use a [preset config](#preset-configs) or configure each rule in `package.json` or `eslint.config.js`. +Create a `.eslintrc.yml` file, add `drizzle` to the `plugins`, and specify the rules you want to use. You can find a list of all existing rules below + +```yml +root: true +parser: '@typescript-eslint/parser' +parserOptions: + project: './tsconfig.json' +plugins: + - drizzle +rules: + 'drizzle/enforce-delete-with-where': "error" + 'drizzle/enforce-update-with-where': "error" +``` -If you don't use the preset, ensure you use the same `env` and `parserOptions` config as below. +### All config -```json -{ - "name": "my-awesome-project", - "eslintConfig": { - "env": { - "es2024": true - }, - "parserOptions": { - "ecmaVersion": "latest", - "sourceType": "module" - }, - "plugins": ["drizzle"], - "rules": { - "drizzle/enforce-delete-with-where": "error", - "drizzle/enforce-update-with-where": "error" - } - } -} +This plugin exports an [`all` config](src/configs/all.js) that makes use of all rules (except for deprecated ones). + +```yml +root: true +extends: + - "plugin:drizzle/all" +parser: '@typescript-eslint/parser' +parserOptions: + project: './tsconfig.json' +plugins: + - drizzle +``` + +At the moment, `all` is equivalent to `recommended` + +```yml +root: true +extends: + - "plugin:drizzle/recommended" +parser: '@typescript-eslint/parser' +parserOptions: + project: './tsconfig.json' +plugins: + - drizzle ``` ## Rules -**enforce-delete-with-where**: Enforce using `delete` with `where` in `DELETE` statement -Optionally, you can defined a `drizzleObjectName` in the plugin options that accepts a string or an array of strings. -This is useful when you have object or classes with a delete method that's not from drizzle. Such delete method will trigger the eslint rule. -To avoid that, you can define the name of the drizzle object that you use in your codebase (like `db`) so that the rule would only trigger if the delete method comes from this object: +**enforce-delete-with-where**: Enforce using `delete` with the`.where()` clause in the `.delete()` statement. Most of the time, you don't need to delete all rows in the table and require some kind of `WHERE` statements. + +Optionally, you can define a `drizzleObjectName` in the plugin options that accept a `string` or `string[]`. This is useful when you have objects or classes with a delete method that's not from Drizzle. Such a `delete` method will trigger the ESLint rule. To avoid that, you can define the name of the Drizzle object that you use in your codebase (like db) so that the rule would only trigger if the delete method comes from this object: + +Example, config 1: ```json "rules": { - "drizzle/enforce-delete-with-where": ["error", { "drizzleObjectName": ["db", "dataSource", "database"] }], + "drizzle/enforce-delete-with-where": ["error"] +} +``` + +```ts +class MyClass { + public delete() { + return {} + } } + +const myClassObj = new MyClass(); + +// ---> Will be triggered by ESLint Rule +myClassObj.delete() + +const db = drizzle(...) +// ---> Will be triggered by ESLint Rule +db.delete() ``` -**enforce-update-with-where**: Enforce using `update` with `where` in `UPDATE` statement -Similar to the delete rule, you can define the name of the drizzle object that you use in your codebase (like `db`) so that the rule would only trigger if the update method comes from this object: +Example, config 2: ```json "rules": { - "drizzle/enforce-update-with-where": ["error", { "drizzleObjectName": "db" }], + "drizzle/enforce-delete-with-where": ["error", { "drizzleObjectName": ["db"] }], } ``` +```ts +class MyClass { + public delete() { + return {} + } +} + +const myClassObj = new MyClass(); -## Preset configs +// ---> Will NOT be triggered by ESLint Rule +myClassObj.delete() -### Recommended config +const db = drizzle(...) +// ---> Will be triggered by ESLint Rule +db.delete() +``` + +**enforce-update-with-where**: Enforce using `update` with the`.where()` clause in the `.update()` statement. Most of the time, you don't need to update all rows in the table and require some kind of `WHERE` statements. -This plugin exports a [`recommended` config](src/configs/recommended.js) that enforces good practices. +Optionally, you can define a `drizzleObjectName` in the plugin options that accept a `string` or `string[]`. This is useful when you have objects or classes with a delete method that's not from Drizzle. Such as `update` method will trigger the ESLint rule. To avoid that, you can define the name of the Drizzle object that you use in your codebase (like db) so that the rule would only trigger if the delete method comes from this object: +Example, config 1: ```json -{ - "name": "my-awesome-project", - "eslintConfig": { - "extends": "plugin:drizzle/recommended" - } +"rules": { + "drizzle/enforce-update-with-where": ["error"] } ``` -### All config +```ts +class MyClass { + public update() { + return {} + } +} -This plugin exports an [`all` config](src/configs/all.js) that makes use of all rules (except for deprecated ones). +const myClassObj = new MyClass(); +// ---> Will be triggered by ESLint Rule +myClassObj.update() + +const db = drizzle(...) +// ---> Will be triggered by ESLint Rule +db.update() +``` + +Example, config 2: ```json -{ - "name": "my-awesome-project", - "eslintConfig": { - "extends": "plugin:drizzle/all" - } +"rules": { + "drizzle/enforce-update-with-where": ["error", { "drizzleObjectName": ["db"] }], } ``` +```ts +class MyClass { + public update() { + return {} + } +} + +const myClassObj = new MyClass(); -At the moment, `all` is equivalent to `recommended`. +// ---> Will NOT be triggered by ESLint Rule +myClassObj.update() + +const db = drizzle(...) +// ---> Will be triggered by ESLint Rule +db.update() +``` diff --git a/eslint-plugin-drizzle/src/enforce-delete-with-where.ts b/eslint-plugin-drizzle/src/enforce-delete-with-where.ts index 21b4824a9..8b1b2d569 100644 --- a/eslint-plugin-drizzle/src/enforce-delete-with-where.ts +++ b/eslint-plugin-drizzle/src/enforce-delete-with-where.ts @@ -17,7 +17,7 @@ const deleteRule = createRule({ }, fixable: 'code', messages: { - enforceDeleteWithWhere: 'Avoid deleting all the rows in a table. Use `db.delete(...).where(...)` instead.', + enforceDeleteWithWhere: 'Without `.where(...)` you will delete all the rows in a table. If you didn\'t want to do it, please use `db.delete(...).where(...)` instead. Otherwise you can ignore this rule here', }, schema: [{ type: 'object', diff --git a/eslint-plugin-drizzle/src/enforce-update-with-where.ts b/eslint-plugin-drizzle/src/enforce-update-with-where.ts index 8c524b1ee..bbe822d43 100644 --- a/eslint-plugin-drizzle/src/enforce-update-with-where.ts +++ b/eslint-plugin-drizzle/src/enforce-update-with-where.ts @@ -17,7 +17,7 @@ const updateRule = createRule({ fixable: 'code', messages: { enforceUpdateWithWhere: - 'Avoid updating all the rows in a table. Use `db.update(...).set(...).where(...)` instead.', + 'Without `.where(...)` you will update all the rows in a table. If you didn\'t want to do it, please use `db.update(...).set(...).where(...)` instead. Otherwise you can ignore this rule here' }, schema: [{ type: 'object',