diff --git a/.husky/.gitignore b/.husky/.gitignore new file mode 100644 index 0000000..31354ec --- /dev/null +++ b/.husky/.gitignore @@ -0,0 +1 @@ +_ diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100755 index 0000000..36af219 --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1,4 @@ +#!/bin/sh +. "$(dirname "$0")/_/husky.sh" + +npx lint-staged diff --git a/__tests__/index.spec.js b/__tests__/index.spec.js index e30c3ab..c67bb6e 100644 --- a/__tests__/index.spec.js +++ b/__tests__/index.spec.js @@ -15,10 +15,9 @@ const db = knex({ user: process.env.MYSQL_USER, password: process.env.MYSQL_PASSWORD, database: process.env.MYSQL_DATABASE, - } + }, }); - function getById(id) { return db('persons').where({ id }).first(); } @@ -30,37 +29,39 @@ describe('onDuplicateUpdate', () => { describe('validation', () => { it('should throw if applied to none insert query', () => { - expect(() => db('persons').select('id').onDuplicateUpdate('')) - .toThrowError('onDuplicateUpdate error: should be used only with insert query.'); + expect(() => db('persons').select('id').onDuplicateUpdate('')).toThrowError( + 'onDuplicateUpdate error: should be used only with insert query.' + ); }); it('should throw if no columns given', () => { - expect(() => db.insert({ id: 1, name: 'test' }).into('persons').onDuplicateUpdate()) - .toThrowError('onDuplicateUpdate error: please specify at least one column name.'); + expect(() => + db.insert({ id: 1, name: 'test' }).into('persons').onDuplicateUpdate() + ).toThrowError('onDuplicateUpdate error: please specify at least one column name.'); }); it('should throw if columns are not string or object', () => { - expect(() => db.insert({ id: 1, name: 'test' }).into('persons').onDuplicateUpdate(false)) - .toThrowError('onDuplicateUpdate error: expected column name to be string or object.'); + expect(() => + db.insert({ id: 1, name: 'test' }).into('persons').onDuplicateUpdate(false) + ).toThrowError('onDuplicateUpdate error: expected column name to be string or object.'); }); it('should throw if insert is empty array', () => { - expect(() => db.insert([]).into('persons').onDuplicateUpdate('whatever')) - .toThrowError('onDuplicateUpdate error: empty insert statement.'); + expect(() => db.insert([]).into('persons').onDuplicateUpdate('whatever')).toThrowError( + 'onDuplicateUpdate error: empty insert statement.' + ); }); }); describe('behaviour', () => { it('should allow insert new row', async () => { const person = { id: 2, name: 'test' }; - await db.insert(person).into('persons') - .onDuplicateUpdate('name'); + await db.insert(person).into('persons').onDuplicateUpdate('name'); const insertPerson = await getById(2); expect(insertPerson).toEqual(expect.objectContaining(person)); }); it('should update on duplicate', async () => { await db.insert({ id: 3, name: 'test3' }).into('persons'); - await db.insert({ id: 3, name: 'test33' }).into('persons') - .onDuplicateUpdate('name'); + await db.insert({ id: 3, name: 'test33' }).into('persons').onDuplicateUpdate('name'); const person = await getById(3); expect(person.name).toBe('test33'); @@ -69,11 +70,13 @@ describe('onDuplicateUpdate', () => { it('should allow updating multiple columns', async () => { const id = 4; await db.insert({ id, name: 'test4', email: '1@1.com' }).into('persons'); - await db.insert({ - id, - name: 'test5', - email: '5@5.com' - }).into('persons') + await db + .insert({ + id, + name: 'test5', + email: '5@5.com', + }) + .into('persons') .onDuplicateUpdate('name', 'email'); const person = await getById(id); @@ -83,45 +86,48 @@ describe('onDuplicateUpdate', () => { it('should allow specifying a value for a column when updating multiple', async () => { const id = 5; await db.insert({ id, name: 'test6', email: '6@6.com' }).into('persons'); - await db.insert({ - id, - name: 'test6', - email: '6@6.com' - }).into('persons') - .onDuplicateUpdate({ 'email': 'updated-email', 'name': 'update-name' }); + await db + .insert({ + id, + name: 'test6', + email: '6@6.com', + }) + .into('persons') + .onDuplicateUpdate({ email: 'updated-email', name: 'update-name' }); const person = await getById(id); - expect(person).toEqual(expect.objectContaining({ - name: 'update-name', - email: 'updated-email', - })); + expect(person).toEqual( + expect.objectContaining({ + name: 'update-name', + email: 'updated-email', + }) + ); }); it('should allow specifying a value for a column when updating multiple and mix types', async () => { const id = 6; await db.insert({ id, name: 'test6', email: '6@6.com' }).into('persons'); - await db.insert({ - id, - name: 'test6', - email: '6@6.com' - }).into('persons') - .onDuplicateUpdate({ 'email': 'updated-email' }, 'name'); + await db + .insert({ + id, + name: 'test6', + email: '6@6.com', + }) + .into('persons') + .onDuplicateUpdate({ email: 'updated-email' }, 'name'); const person = await getById(id); - expect(person).toEqual(expect.objectContaining({ - name: 'test6', - email: 'updated-email', - })); + expect(person).toEqual( + expect.objectContaining({ + name: 'test6', + email: 'updated-email', + }) + ); }); it('should correctly escape field with ? character', async () => { - await db - .insert({ id: 7, name: 'other value' }) - .into('persons'); - await db - .insert({ id: 7, name: '?' }) - .into('persons') - .onDuplicateUpdate('name'); + await db.insert({ id: 7, name: 'other value' }).into('persons'); + await db.insert({ id: 7, name: '?' }).into('persons').onDuplicateUpdate('name'); const person = await getById(7); expect(person.name).toBe('?'); diff --git a/lib/index.js b/lib/index.js index 12944c3..694eee5 100644 --- a/lib/index.js +++ b/lib/index.js @@ -10,36 +10,36 @@ module.exports.attachOnDuplicateUpdate = function attachOnDuplicateUpdate() { throw new Error('onDuplicateUpdate error: please specify at least one column name.'); } - const { placeholders, bindings } = columns.reduce((result, column) => { - if (typeof column === 'string') { - result.placeholders.push(`??=Values(??)`); - result.bindings.push(column, column); - } else if (column && typeof column === 'object') { - Object.keys(column).forEach((key) => { - result.placeholders.push(`??=?`); - result.bindings.push(key, column[key]); - }); - } else { - throw new Error('onDuplicateUpdate error: expected column name to be string or object.'); - } - - return result; - }, { placeholders: [], bindings: [] }); - - const { - sql: originalSQL, - bindings: originalBindings, - } = this.toSQL(); + const { placeholders, bindings } = columns.reduce( + (result, column) => { + if (typeof column === 'string') { + result.placeholders.push(`??=Values(??)`); + result.bindings.push(column, column); + } else if (column && typeof column === 'object') { + Object.keys(column).forEach((key) => { + result.placeholders.push(`??=?`); + result.bindings.push(key, column[key]); + }); + } else { + throw new Error('onDuplicateUpdate error: expected column name to be string or object.'); + } + + return result; + }, + { placeholders: [], bindings: [] } + ); + + const { sql: originalSQL, bindings: originalBindings } = this.toSQL(); if (!originalBindings.length) { - throw new Error('onDuplicateUpdate error: empty insert statement.') + throw new Error('onDuplicateUpdate error: empty insert statement.'); } const newBindings = [...originalBindings, ...bindings]; return this.client.raw( `${originalSQL} on duplicate key update ${placeholders.join(', ')}`, - newBindings, + newBindings ); }); }; diff --git a/package.json b/package.json index c15846a..4e6859f 100644 --- a/package.json +++ b/package.json @@ -2,6 +2,21 @@ "name": "knex-on-duplicate-update", "version": "2.1.3", "description": "Simple patcher for Knex. It adds the .onDuplicateUpdate() function to knex's query builder.", + "keywords": [ + "knex", + "on duplicate update", + "upsert" + ], + "homepage": "https://github.com/felixmosh/knex-on-duplicate-update#readme", + "bugs": { + "url": "https://github.com/felixmosh/knex-on-duplicate-update/issues" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/felixmosh/knex-on-duplicate-update.git" + }, + "license": "ISC", + "author": "Felixmosh", "main": "lib/index.js", "types": "types.d.ts", "files": [ @@ -9,28 +24,17 @@ "types.d.ts" ], "scripts": { + "release": "release-it", "test": "jest", "test:types": "tsd", - "version": "auto-changelog -p && git add CHANGELOG.md", - "release": "release-it" + "version": "auto-changelog -p && git add CHANGELOG.md" }, - "repository": { - "type": "git", - "url": "git+https://github.com/felixmosh/knex-on-duplicate-update.git" + "lint-staged": { + "*.ts": [ + "prettier --write" ] }, - "keywords": [ - "knex", - "on duplicate update", - "upsert" - ], - "author": "Felixmosh", - "license": "ISC", - "bugs": { - "url": "https://github.com/felixmosh/knex-on-duplicate-update/issues" - }, - "homepage": "https://github.com/felixmosh/knex-on-duplicate-update#readme", - "peerDependencies": { - "knex": ">= 0.95.1" + "jest": { + "testEnvironment": "node" }, "devDependencies": { "@types/jest": "^27.0.1", @@ -40,11 +44,12 @@ "jest": "^27.0.6", "knex": "0.95.9", "mysql": "^2.18.1", + "prettier": "^2.6.2", "release-it": "^14.4.1", "tsd": "^0.17.0" }, - "jest": { - "testEnvironment": "node" + "peerDependencies": { + "knex": ">= 0.95.1" }, "release-it": { "git": { diff --git a/prettier.config.js b/prettier.config.js new file mode 100644 index 0000000..b19b956 --- /dev/null +++ b/prettier.config.js @@ -0,0 +1,5 @@ +module.exports = { + printWidth: 100, + trailingComma: 'es5', + singleQuote: true, +}; diff --git a/yarn.lock b/yarn.lock index 05b7abd..4a6bf1f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3393,6 +3393,11 @@ prepend-http@^2.0.0: resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897" integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc= +prettier@^2.6.2: + version "2.6.2" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.6.2.tgz#e26d71a18a74c3d0f0597f55f01fb6c06c206032" + integrity sha512-PkUpF+qoXTqhOeWL9fu7As8LXsIUZ1WYaJiY/a7McAQzxjk82OF0tibkFXVCDImZtWxbvojFjerkiLb0/q8mew== + pretty-format@^27.0.0, pretty-format@^27.0.6: version "27.0.6" resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.0.6.tgz#ab770c47b2c6f893a21aefc57b75da63ef49a11f"