From e810f84b5ef4e8f7b23cfcc0c558e2ceeaef7818 Mon Sep 17 00:00:00 2001 From: fengmk2 Date: Sat, 22 Mar 2025 23:35:26 +0800 Subject: [PATCH 1/7] feat: support cjs and esm both by tshy BREAKING CHANGE: drop Node.js < 18.19.0 support and only support egg>=4 part of https://github.com/eggjs/egg/issues/3644 https://github.com/eggjs/egg/issues/5257 --- .eslintignore | 3 - .eslintrc | 3 - .github/workflows/nodejs.yml | 2 +- .oxlintrc.json | 141 ++++++++++++++++++ README.md | 58 ++++--- README.zh-CN.md | 39 ++--- agent.ts | 15 -- app.ts | 15 -- docker-compose.yml | 17 +++ index.d.ts | 41 ----- index.test-d.ts | 9 -- lib/mysql.ts | 24 --- package.json | 109 +++++++++----- src/agent.ts | 3 + src/app.ts | 3 + src/boot.ts | 37 +++++ {config => src/config}/config.default.ts | 31 +++- src/index.ts | 1 + src/types.ts | 16 ++ src/typings/index.d.ts | 4 + test/fixtures/apps/mysqlapp-dynamic/agent.js | 23 +-- test/fixtures/apps/mysqlapp-dynamic/app.js | 12 +- .../apps/mysqlapp-dynamic/app/router.js | 2 +- test/fixtures/apps/mysqlapp-new/agent.js | 21 +-- test/fixtures/apps/mysqlapp-new/app/router.js | 2 +- test/fixtures/apps/mysqlapp/agent.js | 21 +-- test/fixtures/apps/mysqlapp/app/router.js | 2 +- test/mysql.test.ts | 34 +++-- tsconfig.json | 13 +- 29 files changed, 452 insertions(+), 249 deletions(-) delete mode 100644 .eslintignore delete mode 100644 .eslintrc create mode 100644 .oxlintrc.json delete mode 100644 agent.ts delete mode 100644 app.ts create mode 100644 docker-compose.yml delete mode 100644 index.d.ts delete mode 100644 index.test-d.ts delete mode 100644 lib/mysql.ts create mode 100644 src/agent.ts create mode 100644 src/app.ts create mode 100644 src/boot.ts rename {config => src/config}/config.default.ts (55%) create mode 100644 src/index.ts create mode 100644 src/types.ts create mode 100644 src/typings/index.d.ts diff --git a/.eslintignore b/.eslintignore deleted file mode 100644 index c0c2e30..0000000 --- a/.eslintignore +++ /dev/null @@ -1,3 +0,0 @@ -test/fixtures -coverage -*.js diff --git a/.eslintrc b/.eslintrc deleted file mode 100644 index 114c344..0000000 --- a/.eslintrc +++ /dev/null @@ -1,3 +0,0 @@ -{ - "extends": "eslint-config-egg/typescript" -} diff --git a/.github/workflows/nodejs.yml b/.github/workflows/nodejs.yml index 4a9bdf6..449d10d 100644 --- a/.github/workflows/nodejs.yml +++ b/.github/workflows/nodejs.yml @@ -12,6 +12,6 @@ jobs: uses: node-modules/github-actions/.github/workflows/node-test-mysql.yml@master with: os: 'ubuntu-latest' - version: '16, 18, 20, 22' + version: '18, 20, 22' secrets: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} diff --git a/.oxlintrc.json b/.oxlintrc.json new file mode 100644 index 0000000..ad1f751 --- /dev/null +++ b/.oxlintrc.json @@ -0,0 +1,141 @@ +{ + "$schema": "./node_modules/oxlint/configuration_schema.json", + "env": { + "node": true, + "mocha": true + }, + "categories": { + "correctness": "error", + "perf": "error", + "nursery": "error", + "restriction": "error", + "style": "error", + "pedantic": "error", + "suspicious": "error" + }, + "plugins": [ + "import", + "typescript", + "unicorn", + "jsdoc", + "node", + "promise", + "oxc" + ], + "rules": { + // eslint + "constructor-super": "error", + "getter-return": "error", + "no-undef": "error", + "no-unreachable": "error", + "no-var": "error", + "no-eq-null": "error", + "no-await-in-loop": "allow", + "eqeqeq": ["error", "smart"], + "init-declarations": "allow", + "curly": "allow", + "no-ternary": "allow", + "max-params": ["error", 5], + "no-await-expression-member": "error", + "no-continue": "allow", + "guard-for-in": "allow", + "func-style": "allow", + "sort-imports": "allow", + "yoda": "allow", + "sort-keys": "allow", + "no-magic-numbers": "allow", + "no-duplicate-imports": "error", + "no-multi-assign": "error", + "func-names": "error", + "default-param-last": "error", + "prefer-object-spread": "error", + "no-undefined": "allow", + "no-plusplus": "allow", + // maybe warn + "no-console": "warn", + "no-extraneous-class": "allow", + "no-empty-function": "error", + "max-depth": ["error", 6], + "max-lines-per-function": "allow", + "no-lonely-if": "error", + "max-lines": "allow", + "require-await": "allow", + "max-nested-callbacks": ["error", 5], + "max-classes-per-file": "allow", + "radix": "allow", + "no-negated-condition": "error", + "no-else-return": "error", + "no-throw-literal": "error", + + // import + "import/exports-last": "allow", + "import/max-dependencies": "allow", + "import/no-cycle": "error", + "import/no-anonymous-default-export": "allow", + "import/no-namespace": "error", + "import/named": "error", + "import/export": "error", + "import/no-default-export": "allow", + "import/unambiguous": "error", + + // promise + "promise/no-return-wrap": "error", + "promise/param-names": "error", + "promise/prefer-await-to-callbacks": "error", + "promise/prefer-await-to-then": "error", + "promise/prefer-catch": "error", + "promise/no-return-in-finally": "error", + "promise/avoid-new": "error", + + // unicorn + "unicorn/error-message": "error", + "unicorn/no-null": "allow", + "unicorn/filename-case": "allow", + "unicorn/prefer-structured-clone": "error", + "unicorn/prefer-logical-operator-over-ternary": "error", + "unicorn/prefer-number-properties": "error", + "unicorn/prefer-array-some": "error", + "unicorn/prefer-string-slice": "error", + // "unicorn/no-null": "error", + "unicorn/throw-new-error": "error", + "unicorn/catch-error-name": "allow", + "unicorn/prefer-spread": "allow", + "unicorn/numeric-separators-style": "error", + "unicorn/prefer-string-raw": "error", + "unicorn/text-encoding-identifier-case": "error", + "unicorn/no-array-for-each": "error", + "unicorn/explicit-length-check": "error", + "unicorn/no-lonely-if": "error", + "unicorn/no-useless-undefined": "allow", + "unicorn/prefer-date-now": "error", + "unicorn/no-static-only-class": "allow", + "unicorn/no-typeof-undefined": "error", + "unicorn/prefer-negative-index": "error", + + // oxc + "oxc/no-map-spread": "error", + "oxc/no-rest-spread-properties": "allow", + "oxc/no-optional-chaining": "allow", + "oxc/no-async-await": "allow", + + // typescript + "typescript/explicit-function-return-type": "allow", + "typescript/consistent-type-imports": "error", + "typescript/consistent-type-definitions": "error", + "typescript/consistent-indexed-object-style": "allow", + "typescript/no-inferrable-types": "error", + "typescript/array-type": "error", + "typescript/no-non-null-assertion": "error", + "typescript/no-explicit-any": "error", + "typescript/no-import-type-side-effects": "error", + "typescript/no-dynamic-delete": "error", + "typescript/prefer-ts-expect-error": "error", + "typescript/ban-ts-comment": "error", + "typescript/prefer-enum-initializers": "error", + + // jsdoc + "jsdoc/require-returns": "allow", + "jsdoc/require-param": "allow" + }, + "ignorePatterns": ["index.d.ts", "test/fixtures/**"] +} diff --git a/README.md b/README.md index 26e9617..8bf32fe 100644 --- a/README.md +++ b/README.md @@ -1,23 +1,26 @@ -# egg-mysql +# @eggjs/mysql [![NPM version][npm-image]][npm-url] -[![Node.js CI](https://github.com/eggjs/egg-mysql/actions/workflows/nodejs.yml/badge.svg)](https://github.com/eggjs/egg-mysql/actions/workflows/nodejs.yml) +[![CI](https://github.com/eggjs/mysql/actions/workflows/nodejs.yml/badge.svg?branch=master)](https://github.com/eggjs/mysql/actions/workflows/nodejs.yml) [![Test coverage][codecov-image]][codecov-url] [![npm download][download-image]][download-url] +[![Node.js Version](https://img.shields.io/node/v/@eggjs/mysql.svg?style=flat)](https://nodejs.org/en/download/) +[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](https://makeapullrequest.com) +![CodeRabbit Pull Request Reviews](https://img.shields.io/coderabbit/prs/github/eggjs/mysql) -[npm-image]: https://img.shields.io/npm/v/egg-mysql.svg?style=flat-square -[npm-url]: https://npmjs.org/package/egg-mysql -[codecov-image]: https://img.shields.io/codecov/c/github/eggjs/egg-mysql.svg?style=flat-square -[codecov-url]: https://codecov.io/github/eggjs/egg-mysql?branch=master -[download-image]: https://img.shields.io/npm/dm/egg-mysql.svg?style=flat-square -[download-url]: https://npmjs.org/package/egg-mysql +[npm-image]: https://img.shields.io/npm/v/@eggjs/mysql.svg?style=flat-square +[npm-url]: https://npmjs.org/package/@eggjs/mysql +[codecov-image]: https://img.shields.io/codecov/c/github/eggjs/mysql.svg?style=flat-square +[codecov-url]: https://codecov.io/github/eggjs/mysql?branch=master +[download-image]: https://img.shields.io/npm/dm/@eggjs/mysql.svg?style=flat-square +[download-url]: https://npmjs.org/package/@eggjs/mysql -Aliyun rds client(support mysql protocol) for egg framework +MySQL plugin for Egg.js ## Install ```bash -npm i egg-mysql --save +npm i @eggjs/mysql ``` MySQL Plugin for egg, support egg application access to MySQL database. @@ -32,7 +35,7 @@ Change `${app_root}/config/plugin.ts` to enable MySQL plugin: export default { mysql: { enable: true, - package: 'egg-mysql', + package: '@eggjs/mysql', }, } ``` @@ -57,9 +60,9 @@ export default { // database database: 'test', }, - // load into app, default is open + // load into app, default is `true` app: true, - // load into agent, default is close + // load into agent, default is `false` agent: false, }, } @@ -107,10 +110,10 @@ export default { Usage: ```ts -const client1 = app.mysqls.get('db1'); +const client1 = app.mysqls.getSingletonInstance('db1'); await client1.query(sql, values); -const client2 = app.mysqls.get('db2'); +const client2 = app.mysqls.getSingletonInstance('db2'); await client2.query(sql, values); ``` @@ -239,9 +242,30 @@ await app.mysql.insert(table, { // INSERT INTO `$table`(`id`, `fullname`) VALUES(123, CONCAT("James", "Bond")) ``` +## For the local dev + +Run docker compose to start test mysql service + +```bash +docker compose -f docker-compose.yml up -d +# if you run the first time, should wait for ~20s to let mysql service init started +``` + +Run the unit tests + +```bash +npm test +``` + +Stop test mysql service + +```bash +docker compose -f docker-compose.yml down +``` + ## Questions & Suggestions -Please open an issue [here](https://github.com/eggjs/egg/issues). +Please open an issue [here](https://github.com/eggjs/mysql/issues). ## License @@ -249,7 +273,7 @@ Please open an issue [here](https://github.com/eggjs/egg/issues). ## Contributors -[![Contributors](https://contrib.rocks/image?repo=eggjs/core)](https://github.com/eggjs/core/graphs/contributors) +[![Contributors](https://contrib.rocks/image?repo=eggjs/mysql)](https://github.com/eggjs/mysql/graphs/contributors) Made with [contributors-img](https://contrib.rocks). diff --git a/README.zh-CN.md b/README.zh-CN.md index 706bdc0..8c919b4 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -1,16 +1,19 @@ -# egg-mysql +# @eggjs/mysql [![NPM version][npm-image]][npm-url] -[![Node.js CI](https://github.com/eggjs/egg-mysql/actions/workflows/nodejs.yml/badge.svg)](https://github.com/eggjs/egg-mysql/actions/workflows/nodejs.yml) +[![CI](https://github.com/eggjs/mysql/actions/workflows/nodejs.yml/badge.svg?branch=master)](https://github.com/eggjs/mysql/actions/workflows/nodejs.yml) [![Test coverage][codecov-image]][codecov-url] [![npm download][download-image]][download-url] +[![Node.js Version](https://img.shields.io/node/v/@eggjs/mysql.svg?style=flat)](https://nodejs.org/en/download/) +[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](https://makeapullrequest.com) +![CodeRabbit Pull Request Reviews](https://img.shields.io/coderabbit/prs/github/eggjs/mysql) -[npm-image]: https://img.shields.io/npm/v/egg-mysql.svg?style=flat-square -[npm-url]: https://npmjs.org/package/egg-mysql -[codecov-image]: https://img.shields.io/codecov/c/github/eggjs/egg-mysql.svg?style=flat-square -[codecov-url]: https://codecov.io/github/eggjs/egg-mysql?branch=master -[download-image]: https://img.shields.io/npm/dm/egg-mysql.svg?style=flat-square -[download-url]: https://npmjs.org/package/egg-mysql +[npm-image]: https://img.shields.io/npm/v/@eggjs/mysql.svg?style=flat-square +[npm-url]: https://npmjs.org/package/@eggjs/mysql +[codecov-image]: https://img.shields.io/codecov/c/github/eggjs/mysql.svg?style=flat-square +[codecov-url]: https://codecov.io/github/eggjs/mysql?branch=master +[download-image]: https://img.shields.io/npm/dm/@eggjs/mysql.svg?style=flat-square +[download-url]: https://npmjs.org/package/@eggjs/mysql MySQL 插件是为 egg 提供 MySQL 数据库访问的功能 @@ -19,7 +22,7 @@ MySQL 插件是为 egg 提供 MySQL 数据库访问的功能 ## 安装 ```bash -npm i egg-mysql --save +npm i @eggjs/mysql ``` ## 配置 @@ -30,7 +33,7 @@ npm i egg-mysql --save export default { mysql: { enable: true, - package: 'egg-mysql', + package: '@eggjs/mysql', }, } ``` @@ -119,22 +122,22 @@ await client2.query(sql, values); #### app.mysql -如果开启了 `config.mysql.app = true`,则会在 app 上注入 [@eggjs/rds] 客户端 的 [Singleton 单例](https://github.com/eggjs/egg/blob/master/lib/core/singleton.js)。 +如果开启了 `config.mysql.app = true`,则会在 app 上注入 [@eggjs/rds] 客户端 的 [Singleton 单例](https://github.com/eggjs/core/blob/master/src/singleton.ts)。 ```ts await app.mysql.query(sql); -await app.mysqls.get('db1').query(sql); +await app.mysqls.getSingletonInstance('db1').query(sql); ``` ### Agent #### agent.mysql -如果开启了 `config.mysql.agent = true`,则会在 agent 上注入 [@eggjs/rds] 客户端 的 [Singleton 单例](https://github.com/eggjs/egg/blob/master/lib/core/singleton.js)。 +如果开启了 `config.mysql.agent = true`,则会在 agent 上注入 [@eggjs/rds] 客户端 的 [Singleton 单例](https://github.com/eggjs/core/blob/master/src/singleton.ts)。 ```ts await agent.mysql.query(sql); -await agent.mysqls.get('db1').query(sql); +await agent.mysqls.getSingletonInstance('db1').query(sql); ``` ## CRUD 使用指南 @@ -236,7 +239,7 @@ const results = await app.mysql.query('update posts set hits = (hits + ?) where #### 内置表达式 -- NOW(): 数据库当前系统时间,通过`app.mysql.literals.now`获取。 +- `NOW()`: 数据库当前系统时间,通过 `app.mysql.literals.now` 获取。 ```ts await app.mysql.insert(table, { @@ -248,7 +251,7 @@ await app.mysql.insert(table, { #### 自定义表达式 -下例展示了如何调用mysql内置的`CONCAT(s1, ...sn)`函数,做字符串拼接。 +下例展示了如何调用mysql内置的 `CONCAT(s1, ...sn)` 函数,做字符串拼接。 ```ts const Literal = app.mysql.literals.Literal; @@ -264,7 +267,7 @@ await app.mysql.insert(table, { ## Questions & Suggestions -Please open an issue [here](https://github.com/eggjs/egg/issues). +Please open an issue [here](https://github.com/eggjs/mysql/issues). ## License @@ -272,7 +275,7 @@ Please open an issue [here](https://github.com/eggjs/egg/issues). ## Contributors -[![Contributors](https://contrib.rocks/image?repo=eggjs/core)](https://github.com/eggjs/core/graphs/contributors) +[![Contributors](https://contrib.rocks/image?repo=eggjs/mysql)](https://github.com/eggjs/mysql/graphs/contributors) Made with [contributors-img](https://contrib.rocks). diff --git a/agent.ts b/agent.ts deleted file mode 100644 index 9e95cf8..0000000 --- a/agent.ts +++ /dev/null @@ -1,15 +0,0 @@ -import type { Agent, IBoot } from 'egg'; -import { initPlugin } from './lib/mysql'; - -export default class AgentBootHook implements IBoot { - private readonly agent: Agent; - constructor(agent: Agent) { - this.agent = agent; - } - - configDidLoad() { - if (this.agent.config.mysql.agent) { - initPlugin(this.agent); - } - } -} diff --git a/app.ts b/app.ts deleted file mode 100644 index 21f62ed..0000000 --- a/app.ts +++ /dev/null @@ -1,15 +0,0 @@ -import type { Application, IBoot } from 'egg'; -import { initPlugin } from './lib/mysql'; - -export default class AppBootHook implements IBoot { - private readonly app: Application; - constructor(app: Application) { - this.app = app; - } - - configDidLoad() { - if (this.app.config.mysql.app) { - initPlugin(this.app); - } - } -} diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..1e72dd1 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,17 @@ +name: eggjs_mysql_dev_services_mysql + +services: + mysql: + image: mysql:9 + environment: + MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:-} + MYSQL_ALLOW_EMPTY_PASSWORD: yes + MYSQL_DATABASE: ${MYSQL_DATABASE:-test} + ports: + - 3306:3306 + healthcheck: + test: ['CMD', 'mysqladmin', 'ping', '-h', 'localhost'] + interval: 10s + timeout: 5s + retries: 5 + restart: unless-stopped diff --git a/index.d.ts b/index.d.ts deleted file mode 100644 index 7b29051..0000000 --- a/index.d.ts +++ /dev/null @@ -1,41 +0,0 @@ -import type { RDSClient } from '@eggjs/rds'; - -type EggMySQL = RDSClient; - -interface EggMySQLClientOption { - /// host e.g. 'mysql.com' - host: string; - /// port e.g. '3306' - port: string; - /// username e.g. 'test_user' - user: string; - // password e.g. 'test_password' - password: string; - // database e.g. 'test' - database: string; -} - -interface EggMySQLClientsOption { - [clientName: string]: EggMySQLClientOption; -} - -interface EggMySqlConfig { - default?: object; - app?: boolean; - agent?: boolean; - client?: EggMySQLClientOption; - clients?: EggMySQLClientsOption; -} - -declare module 'egg' { - interface Application { - mysql: EggMySQL; - mysqls: { - get(clientId: string): EggMySQL; - }; - } - - interface EggAppConfig { - mysql: EggMySqlConfig; - } -} diff --git a/index.test-d.ts b/index.test-d.ts deleted file mode 100644 index ed48ddd..0000000 --- a/index.test-d.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { expectType } from 'tsd'; -import { Application } from 'egg'; -import { EggMySQL } from '.'; - -const app = new Application(); - -expectType>(app.mysql.get('table', {})); -expectType(app.mysql); -expectType(app.mysqls.get('foo')); diff --git a/lib/mysql.ts b/lib/mysql.ts deleted file mode 100644 index 899cbc0..0000000 --- a/lib/mysql.ts +++ /dev/null @@ -1,24 +0,0 @@ -import type { Application, Agent } from 'egg'; -import { RDSClient } from '@eggjs/rds'; - -let count = 0; -function createOneClient(config: Record, app: Application | Agent) { - app.coreLogger.info('[egg-mysql] connecting %s@%s:%s/%s', - config.user, config.host, config.port, config.database); - const client = new RDSClient(config); - - app.beforeStart(async () => { - const rows = await client.query('select now() as currentTime;'); - const index = count++; - app.coreLogger.info('[egg-mysql] instance[%s] status OK, rds currentTime: %s', - index, rows[0].currentTime); - }); - return client; -} - -export function initPlugin(app: Application | Agent) { - app.addSingleton('mysql', createOneClient); - // alias to app.mysqls - // https://github.com/eggjs/egg/blob/9ad39f59991bd48633b8da4abe1da5eb79a1de62/lib/core/singleton.js#L38 - (app as any).mysqls = (app as any).mysql; -} diff --git a/package.json b/package.json index b9be4e2..98c5d46 100644 --- a/package.json +++ b/package.json @@ -1,17 +1,18 @@ { - "name": "egg-mysql", + "name": "@eggjs/mysql", + "publishConfig": { + "access": "public" + }, "version": "5.0.0", - "description": "MySQL plugin for egg", + "description": "MySQL plugin for Egg.js", "eggPlugin": { - "name": "mysql" + "name": "mysql", + "exports": { + "import": "./dist/esm", + "require": "./dist/commonjs", + "typescript": "./src" + } }, - "files": [ - "config/*.js", - "lib/*.js", - "agent.js", - "app.js", - "index.d.ts" - ], "keywords": [ "egg", "eggPlugin", @@ -19,43 +20,71 @@ "mysql", "database" ], + "repository": { + "type": "git", + "url": "git+https://github.com/eggjs/mysql.git" + }, + "bugs": { + "url": "https://github.com/eggjs/mysql/issues" + }, + "homepage": "https://github.com/eggjs/mysql#readme", + "author": "jtyjty99999", + "license": "MIT", + "engines": { + "node": ">= 18.19.0" + }, "dependencies": { + "@eggjs/core": "^6.4.0", "@eggjs/rds": "^1.2.1" }, "devDependencies": { - "@eggjs/tsconfig": "^1.3.2", - "@types/mocha": "^10.0.1", - "@types/node": "^18.14.6", - "egg": "^3.15.0", - "egg-bin": "^6.1.2", - "egg-mock": "^5.10.6", - "eslint": "^8.16.0", - "eslint-config-egg": "^12.1.0", - "tsd": "^0.26.0", - "typescript": "^4.9.5" - }, - "engines": { - "node": ">=18.0.0" + "@arethetypeswrong/cli": "^0.17.4", + "@eggjs/bin": "7", + "@eggjs/mock": "^6.0.7", + "@eggjs/tsconfig": "2", + "@types/mocha": "10", + "@types/node": "22", + "egg": "^4.0.10", + "oxlint": "^0.16.2", + "rimraf": "6", + "tshy": "3", + "tshy-after": "1", + "typescript": "5" }, "scripts": { - "tsd": "tsd", - "test": "npm run lint -- --fix && npm run test-local", - "test-local": "egg-bin test", - "cov": "egg-bin cov", - "lint": "eslint .", - "ci": "npm run lint && npm run cov && npm run prepublishOnly", - "build": "tsc -p ./tsconfig.json", - "clean": "tsc --build --clean", - "prepublishOnly": "npm run clean && npm run build" + "lint": "oxlint", + "pretest": "npm run clean && npm run lint -- --fix", + "test": "egg-bin test", + "preci": "npm run clean && npm run lint", + "ci": "egg-bin cov", + "postci": "npm run prepublishOnly && npm run clean", + "clean": "rimraf dist", + "prepublishOnly": "tshy && tshy-after && attw --pack" }, - "repository": { - "type": "git", - "url": "git+https://github.com/eggjs/egg-mysql.git" + "type": "module", + "tshy": { + "exports": { + ".": "./src/index.ts", + "./package.json": "./package.json" + } }, - "bugs": { - "url": "https://github.com/eggjs/egg/issues" + "exports": { + ".": { + "import": { + "types": "./dist/esm/index.d.ts", + "default": "./dist/esm/index.js" + }, + "require": { + "types": "./dist/commonjs/index.d.ts", + "default": "./dist/commonjs/index.js" + } + }, + "./package.json": "./package.json" }, - "homepage": "https://github.com/eggjs/egg-mysql#readme", - "author": "jtyjty99999", - "license": "MIT" + "files": [ + "dist", + "src" + ], + "types": "./dist/commonjs/index.d.ts", + "main": "./dist/commonjs/index.js" } diff --git a/src/agent.ts b/src/agent.ts new file mode 100644 index 0000000..ac1e7f9 --- /dev/null +++ b/src/agent.ts @@ -0,0 +1,3 @@ +import { MySQLBootHook } from './boot.js'; + +export default MySQLBootHook; diff --git a/src/app.ts b/src/app.ts new file mode 100644 index 0000000..ac1e7f9 --- /dev/null +++ b/src/app.ts @@ -0,0 +1,3 @@ +import { MySQLBootHook } from './boot.js'; + +export default MySQLBootHook; diff --git a/src/boot.ts b/src/boot.ts new file mode 100644 index 0000000..a2a6159 --- /dev/null +++ b/src/boot.ts @@ -0,0 +1,37 @@ +import { RDSClient, type RDSClientOptions } from '@eggjs/rds'; +import type { EggCore, ILifecycleBoot } from '@eggjs/core'; + +async function createMySQLClient(config: RDSClientOptions, app: EggCore, clientName = 'default') { + app.coreLogger.info('[@eggjs/mysql] clientName[%s] connecting %s@%s:%s/%s', + clientName, config.user, config.host, config.port, config.database); + const client = new RDSClient(config); + + const rows = await client.query('select now() as currentTime;'); + app.coreLogger.info('[@eggjs/mysql] clientName[%s] status OK, MySQL Server currentTime: %j', + clientName, rows[0].currentTime); + return client; +} + +export class MySQLBootHook implements ILifecycleBoot { + private readonly app: EggCore; + constructor(app: EggCore) { + this.app = app; + } + + configDidLoad() { + if (this.app.type === 'application' && !this.app.config.mysql.app) { + return; + } else if (this.app.type === 'agent' && !this.app.config.mysql.agent) { + return; + } + + this.app.addSingleton('mysql', createMySQLClient); + // alias to app.mysqls + // https://github.com/eggjs/core/blob/41fe40ff68432db1f0bd89a88bdc33dd321bffb6/src/singleton.ts#L50 + Reflect.defineProperty(this.app, 'mysqls', { + get() { + return this.mysql; + }, + }); + } +} diff --git a/config/config.default.ts b/src/config/config.default.ts similarity index 55% rename from config/config.default.ts rename to src/config/config.default.ts index a7fb711..2244ff9 100644 --- a/config/config.default.ts +++ b/src/config/config.default.ts @@ -1,7 +1,34 @@ +import type { RDSClientOptions } from '@eggjs/rds'; + +export type MySQLClientOptions = RDSClientOptions; + +export interface MySQLClientsOptions { + [clientName: string]: MySQLClientOptions; +} + +export interface MySQLConfig { + default?: MySQLClientOptions; + /** + * load into app, default is `true` + */ + app?: boolean; + /** + * load into agent, default is `false` + */ + agent?: boolean; + /** + * single database + */ + client?: MySQLClientOptions; + /** + * multi databases + */ + clients?: MySQLClientsOptions; +} + export default { mysql: { default: { - database: null, connectionLimit: 5, }, app: true, @@ -33,5 +60,5 @@ export default { // database: 'database', // }, // }, - }, + } satisfies MySQLConfig, }; diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..ce5fb25 --- /dev/null +++ b/src/index.ts @@ -0,0 +1 @@ +import './types.js'; diff --git a/src/types.ts b/src/types.ts new file mode 100644 index 0000000..a4b3a48 --- /dev/null +++ b/src/types.ts @@ -0,0 +1,16 @@ +import type { Singleton } from '@eggjs/core'; +import type { RDSClient } from '@eggjs/rds'; + +import type { MySQLConfig } from './config/config.default.js'; + +declare module '@eggjs/core' { + // add EggAppConfig overrides types + interface EggAppConfig { + mysql: MySQLConfig; + } + + interface EggCore { + mysql: RDSClient & Singleton; + mysqls: Singleton; + } +} diff --git a/src/typings/index.d.ts b/src/typings/index.d.ts new file mode 100644 index 0000000..53c65c7 --- /dev/null +++ b/src/typings/index.d.ts @@ -0,0 +1,4 @@ +// make sure to import egg typings and let typescript know about it +// @see https://github.com/whxaxes/blog/issues/11 +// and https://www.typescriptlang.org/docs/handbook/declaration-merging.html +import 'egg'; diff --git a/test/fixtures/apps/mysqlapp-dynamic/agent.js b/test/fixtures/apps/mysqlapp-dynamic/agent.js index 54af5a5..44cede0 100644 --- a/test/fixtures/apps/mysqlapp-dynamic/agent.js +++ b/test/fixtures/apps/mysqlapp-dynamic/agent.js @@ -1,14 +1,17 @@ -const fs = require('fs'); -const path = require('path'); +const fs = require('node:fs'); +const path = require('node:path'); -module.exports = function(agent) { - agent.mysql1 = agent.mysql.createInstance(agent.config.mysql1); - const done = agent.readyCallback('agent-mysql'); - const p = path.join(__dirname, 'run/agent_result.json'); - fs.existsSync(p) && fs.unlinkSync(p); +module.exports = class Boot { + constructor(agent) { + this.agent = agent; + } - (async () => { - const result = await agent.mysql1.query('select now() as currentTime;'); + async didReady() { + this.agent.mysql1 = await this.agent.mysql.createInstanceAsync(this.agent.config.mysql1); + const p = path.join(__dirname, 'run/agent_result.json'); + fs.existsSync(p) && fs.unlinkSync(p); + + const result = await this.agent.mysql1.query('select now() as currentTime;'); fs.writeFileSync(p, JSON.stringify(result)); - })().then(done).catch(done); + } }; diff --git a/test/fixtures/apps/mysqlapp-dynamic/app.js b/test/fixtures/apps/mysqlapp-dynamic/app.js index ba20ab0..cca4a78 100644 --- a/test/fixtures/apps/mysqlapp-dynamic/app.js +++ b/test/fixtures/apps/mysqlapp-dynamic/app.js @@ -1,3 +1,9 @@ -module.exports = function(app) { - app.mysql1 = app.mysql.createInstance(app.config.mysql1); -}; +module.exports = class Boot { + constructor(app) { + this.app = app; + } + + async didReady() { + this.app.mysql1 = await this.app.mysql.createInstanceAsync(this.app.config.mysql1); + } +} diff --git a/test/fixtures/apps/mysqlapp-dynamic/app/router.js b/test/fixtures/apps/mysqlapp-dynamic/app/router.js index b6cca73..e59f3f9 100644 --- a/test/fixtures/apps/mysqlapp-dynamic/app/router.js +++ b/test/fixtures/apps/mysqlapp-dynamic/app/router.js @@ -1,5 +1,5 @@ 'use strict'; -module.exports = function(app) { +module.exports = function exports(app) { app.get('/', app.controller.home); }; diff --git a/test/fixtures/apps/mysqlapp-new/agent.js b/test/fixtures/apps/mysqlapp-new/agent.js index 7b74602..b8509d3 100644 --- a/test/fixtures/apps/mysqlapp-new/agent.js +++ b/test/fixtures/apps/mysqlapp-new/agent.js @@ -1,13 +1,16 @@ -const fs = require('fs'); -const path = require('path'); +const fs = require('node:fs'); +const path = require('node:path'); -module.exports = function(agent) { - const done = agent.readyCallback('agent-mysql'); - const p = path.join(__dirname, 'run/agent_result.json'); - fs.existsSync(p) && fs.unlinkSync(p); +module.exports = class Boot { + constructor(agent) { + this.agent = agent; + } - (async () => { - const result = await agent.mysql.query('select now() as currentTime;'); + async didReady() { + const p = path.join(__dirname, 'run/agent_result.json'); + fs.existsSync(p) && fs.unlinkSync(p); + + const result = await this.agent.mysql.query('select now() as currentTime;'); fs.writeFileSync(p, JSON.stringify(result)); - })().then(done).catch(done); + } }; diff --git a/test/fixtures/apps/mysqlapp-new/app/router.js b/test/fixtures/apps/mysqlapp-new/app/router.js index 0791f31..9fc71e0 100644 --- a/test/fixtures/apps/mysqlapp-new/app/router.js +++ b/test/fixtures/apps/mysqlapp-new/app/router.js @@ -1,3 +1,3 @@ -module.exports = function(app) { +module.exports = function exports(app) { app.get('/', app.controller.home); }; diff --git a/test/fixtures/apps/mysqlapp/agent.js b/test/fixtures/apps/mysqlapp/agent.js index 1eb2320..b8509d3 100644 --- a/test/fixtures/apps/mysqlapp/agent.js +++ b/test/fixtures/apps/mysqlapp/agent.js @@ -1,13 +1,16 @@ -const fs = require('fs'); -const path = require('path'); +const fs = require('node:fs'); +const path = require('node:path'); -module.exports = agent => { - const done = agent.readyCallback('agent-mysql'); - const p = path.join(__dirname, 'run/agent_result.json'); - fs.existsSync(p) && fs.unlinkSync(p); +module.exports = class Boot { + constructor(agent) { + this.agent = agent; + } - (async () => { - const result = await agent.mysql.query('select now() as currentTime;'); + async didReady() { + const p = path.join(__dirname, 'run/agent_result.json'); + fs.existsSync(p) && fs.unlinkSync(p); + + const result = await this.agent.mysql.query('select now() as currentTime;'); fs.writeFileSync(p, JSON.stringify(result)); - })().then(done).catch(done); + } }; diff --git a/test/fixtures/apps/mysqlapp/app/router.js b/test/fixtures/apps/mysqlapp/app/router.js index 0791f31..9fc71e0 100644 --- a/test/fixtures/apps/mysqlapp/app/router.js +++ b/test/fixtures/apps/mysqlapp/app/router.js @@ -1,3 +1,3 @@ -module.exports = function(app) { +module.exports = function exports(app) { app.get('/', app.controller.home); }; diff --git a/test/mysql.test.ts b/test/mysql.test.ts index 5169331..94b6d6e 100644 --- a/test/mysql.test.ts +++ b/test/mysql.test.ts @@ -1,10 +1,13 @@ -import { strict as assert } from 'node:assert'; +import assert from 'node:assert/strict'; import { randomUUID } from 'node:crypto'; import path from 'node:path'; import fs from 'node:fs'; -import mm, { MockApplication } from 'egg-mock'; -// import types from index.d.ts -import type {} from '..'; +import { fileURLToPath } from 'node:url'; + +import { mm, type MockApplication } from '@eggjs/mock'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); describe('test/mysql.test.ts', () => { let app: MockApplication; @@ -111,7 +114,7 @@ describe('test/mysql.test.ts', () => { it('should escape value', () => { const val = app.mysql.escape('\'"?><=!@#'); - assert(val === '\'\\\'\\"?><=!@#\''); + assert(val === String.raw`'\'\"?><=!@#'`); }); it('should agent error when password wrong on multi clients', async () => { @@ -120,12 +123,13 @@ describe('test/mysql.test.ts', () => { }); await assert.rejects(async () => { await app.ready(); - }, (err: any) => { + }, (err) => { + assert(err instanceof Error); assert.match(err.message, /Access denied for user/); - assert.equal(err.code, 'ER_ACCESS_DENIED_ERROR'); - assert.equal(err.errno, 1045); - assert.equal(err.sqlState, '28000'); - assert.match(err.sqlMessage, /^Access denied for user 'root'@'[^\']+' \(using password: YES\)$/); + assert.equal(Reflect.get(err, 'code'), 'ER_ACCESS_DENIED_ERROR'); + assert.equal(Reflect.get(err, 'errno'), 1045); + assert.equal(Reflect.get(err, 'sqlState'), '28000'); + assert.match(Reflect.get(err, 'sqlMessage'), /^Access denied for user 'root'@'[^']+' \(using password: YES\)$/); assert.equal(err.name, 'RDSClientGetConnectionError'); return true; }); @@ -142,7 +146,7 @@ describe('test/mysql.test.ts', () => { }); describe('config.mysql.agent = true', () => { - let app; + let app: MockApplication; before(() => { app = mm.cluster({ baseDir: 'apps/mysqlapp', @@ -159,7 +163,7 @@ describe('test/mysql.test.ts', () => { }); describe('config.mysql.app = false', () => { - let app; + let app: MockApplication; before(() => { app = mm.app({ baseDir: 'apps/mysqlapp-disable', @@ -174,7 +178,7 @@ describe('test/mysql.test.ts', () => { }); describe('newConfig', () => { - let app; + let app: MockApplication; before(() => { app = mm.cluster({ baseDir: 'apps/mysqlapp-new', @@ -197,8 +201,8 @@ describe('test/mysql.test.ts', () => { }); }); - describe('createInstance', () => { - let app; + describe('createInstanceAsync', () => { + let app: MockApplication; before(() => { app = mm.cluster({ baseDir: 'apps/mysqlapp-dynamic', diff --git a/tsconfig.json b/tsconfig.json index 13e0b56..376b902 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,14 +1,3 @@ { - "extends": "@eggjs/tsconfig", - "compilerOptions": { - "module": "Node16", - "useUnknownInCatchVariables": false, - "target": "ES2022" - }, - "include": [ - "lib", - "config", - "app.ts", - "agent.ts" - ] + "extends": "@eggjs/tsconfig" } From 4450b237e87d7026bc3df8048570e5ecfd344354 Mon Sep 17 00:00:00 2001 From: fengmk2 Date: Sat, 22 Mar 2025 23:37:33 +0800 Subject: [PATCH 2/7] f --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 8bf32fe..1d3db7d 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,9 @@ MySQL plugin for Egg.js npm i @eggjs/mysql ``` -MySQL Plugin for egg, support egg application access to MySQL database. +MySQL Plugin for `egg@4`, support egg application access to MySQL database. + +> If you're using `egg@3`, please use `egg-mysql@5` instead. This plugin based on [@eggjs/rds], if you want to know specific usage, you should refer to the document of [@eggjs/rds]. From 02c17f5ce51ef0f05fc4f4c745db39da3a6fcc18 Mon Sep 17 00:00:00 2001 From: fengmk2 Date: Sat, 22 Mar 2025 23:43:03 +0800 Subject: [PATCH 3/7] f --- .oxlintrc.json | 2 +- __snapshots__/mysql.test.ts.js | 14 ++++++++++++++ package.json | 1 + test/mysql.test.ts | 5 +++++ 4 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 __snapshots__/mysql.test.ts.js diff --git a/.oxlintrc.json b/.oxlintrc.json index ad1f751..2d788b0 100644 --- a/.oxlintrc.json +++ b/.oxlintrc.json @@ -137,5 +137,5 @@ "jsdoc/require-returns": "allow", "jsdoc/require-param": "allow" }, - "ignorePatterns": ["index.d.ts", "test/fixtures/**"] + "ignorePatterns": ["index.d.ts", "test/fixtures/**", "__snapshots__"] } diff --git a/__snapshots__/mysql.test.ts.js b/__snapshots__/mysql.test.ts.js new file mode 100644 index 0000000..cee2275 --- /dev/null +++ b/__snapshots__/mysql.test.ts.js @@ -0,0 +1,14 @@ +exports['test/mysql.test.ts should make default config stable 1'] = { + "default": { + "connectionLimit": 5 + }, + "app": true, + "agent": true, + "client": { + "host": "127.0.0.1", + "port": 3306, + "user": "root", + "password": "", + "database": "test" + } +} diff --git a/package.json b/package.json index 98c5d46..4193859 100644 --- a/package.json +++ b/package.json @@ -47,6 +47,7 @@ "egg": "^4.0.10", "oxlint": "^0.16.2", "rimraf": "6", + "snap-shot-it": "^7.9.10", "tshy": "3", "tshy-after": "1", "typescript": "5" diff --git a/test/mysql.test.ts b/test/mysql.test.ts index 94b6d6e..8e5bf89 100644 --- a/test/mysql.test.ts +++ b/test/mysql.test.ts @@ -4,6 +4,7 @@ import path from 'node:path'; import fs from 'node:fs'; import { fileURLToPath } from 'node:url'; +import snapshot from 'snap-shot-it'; import { mm, type MockApplication } from '@eggjs/mock'; const __filename = fileURLToPath(import.meta.url); @@ -44,6 +45,10 @@ describe('test/mysql.test.ts', () => { afterEach(mm.restore); + it('should make default config stable', () => { + snapshot(app.config.mysql); + }); + it('should query mysql user table success', () => { return app.httpRequest() .get('/') From c0e7276889cdee48748a004c1444f98e82431b5f Mon Sep 17 00:00:00 2001 From: fengmk2 Date: Sun, 23 Mar 2025 20:17:32 +0800 Subject: [PATCH 4/7] f --- .oxlintrc.json | 1 + test/esm.test.ts | 42 +++++++++++++++++++ test/fixtures/apps/mysqlapp-ts-esm/agent.ts | 19 +++++++++ .../mysqlapp-ts-esm/app/controller/home.ts | 10 +++++ .../apps/mysqlapp-ts-esm/app/router.ts | 5 +++ .../apps/mysqlapp-ts-esm/app/service/user.ts | 7 ++++ .../apps/mysqlapp-ts-esm/config/config.ts | 15 +++++++ .../apps/mysqlapp-ts-esm/package.json | 4 ++ .../apps/mysqlapp-ts-esm/tsconfig.json | 3 ++ .../apps/mysqlapp-ts-esm/typings/index.d.ts | 1 + 10 files changed, 107 insertions(+) create mode 100644 test/esm.test.ts create mode 100644 test/fixtures/apps/mysqlapp-ts-esm/agent.ts create mode 100644 test/fixtures/apps/mysqlapp-ts-esm/app/controller/home.ts create mode 100644 test/fixtures/apps/mysqlapp-ts-esm/app/router.ts create mode 100644 test/fixtures/apps/mysqlapp-ts-esm/app/service/user.ts create mode 100644 test/fixtures/apps/mysqlapp-ts-esm/config/config.ts create mode 100644 test/fixtures/apps/mysqlapp-ts-esm/package.json create mode 100644 test/fixtures/apps/mysqlapp-ts-esm/tsconfig.json create mode 100644 test/fixtures/apps/mysqlapp-ts-esm/typings/index.d.ts diff --git a/.oxlintrc.json b/.oxlintrc.json index 2d788b0..10f5656 100644 --- a/.oxlintrc.json +++ b/.oxlintrc.json @@ -111,6 +111,7 @@ "unicorn/no-static-only-class": "allow", "unicorn/no-typeof-undefined": "error", "unicorn/prefer-negative-index": "error", + "unicorn/no-anonymous-default-export": "allow", // oxc "oxc/no-map-spread": "error", diff --git a/test/esm.test.ts b/test/esm.test.ts new file mode 100644 index 0000000..3910018 --- /dev/null +++ b/test/esm.test.ts @@ -0,0 +1,42 @@ +import assert from 'node:assert/strict'; +import { randomUUID } from 'node:crypto'; + +import { mm, type MockApplication } from '@eggjs/mock'; + +describe('test/esm.test.ts', () => { + let app: MockApplication; + const uid = randomUUID(); + + before(() => { + app = mm.app({ + baseDir: 'apps/mysqlapp-ts-esm', + }); + return app.ready(); + }); + + beforeEach(async () => { + // init test datas + await app.mysql.query(`insert into npm_auth set user_id = 'egg-${uid}-1', password = '1'`); + await app.mysql.query(`insert into npm_auth set user_id = 'egg-${uid}-2', password = '2'`); + await app.mysql.query(`insert into npm_auth set user_id = 'egg-${uid}-3', password = '3'`); + await app.mysql.queryOne(`select * from npm_auth where user_id = 'egg-${uid}-3'`); + }); + + afterEach(async () => { + await app.mysql.query(`delete from npm_auth where user_id like 'egg-${uid}%'`); + }); + + after(async () => { + await app.close(); + }); + + + it('should query mysql user table success', async () => { + const res = await app.httpRequest() + .get('/') + .expect(200); + + assert.equal(res.body.status, 'success'); + assert.equal(res.body.users.length, 3); + }); +}); diff --git a/test/fixtures/apps/mysqlapp-ts-esm/agent.ts b/test/fixtures/apps/mysqlapp-ts-esm/agent.ts new file mode 100644 index 0000000..dd5f747 --- /dev/null +++ b/test/fixtures/apps/mysqlapp-ts-esm/agent.ts @@ -0,0 +1,19 @@ +import fs from 'node:fs/promises'; +import path from 'node:path'; + +import type { Agent } from 'egg'; + +export default class Boot { + private readonly agent: Agent; + constructor(agent: Agent) { + this.agent = agent; + } + + async didReady() { + const p = path.join(this.agent.baseDir, 'run/agent_result.json'); + await fs.rm(p, { force: true }); + + const result = await this.agent.mysql.query('select now() as currentTime;'); + await fs.writeFile(p, JSON.stringify(result)); + } +} diff --git a/test/fixtures/apps/mysqlapp-ts-esm/app/controller/home.ts b/test/fixtures/apps/mysqlapp-ts-esm/app/controller/home.ts new file mode 100644 index 0000000..0effdc2 --- /dev/null +++ b/test/fixtures/apps/mysqlapp-ts-esm/app/controller/home.ts @@ -0,0 +1,10 @@ +import type { Context } from 'egg'; + +export default async (ctx: Context) => { + const users = await ctx.service.user.list(ctx); + + ctx.body = { + status: 'success', + users, + }; +}; diff --git a/test/fixtures/apps/mysqlapp-ts-esm/app/router.ts b/test/fixtures/apps/mysqlapp-ts-esm/app/router.ts new file mode 100644 index 0000000..a99b0c2 --- /dev/null +++ b/test/fixtures/apps/mysqlapp-ts-esm/app/router.ts @@ -0,0 +1,5 @@ +import type { Application } from 'egg'; + +export default (app: Application) => { + app.get('/', app.controller.home); +}; diff --git a/test/fixtures/apps/mysqlapp-ts-esm/app/service/user.ts b/test/fixtures/apps/mysqlapp-ts-esm/app/service/user.ts new file mode 100644 index 0000000..2c25fb8 --- /dev/null +++ b/test/fixtures/apps/mysqlapp-ts-esm/app/service/user.ts @@ -0,0 +1,7 @@ +import type { Context } from 'egg'; + +export default class UserService { + async list(ctx: Context) { + return await ctx.app.mysql.query('select * from npm_auth'); + } +} diff --git a/test/fixtures/apps/mysqlapp-ts-esm/config/config.ts b/test/fixtures/apps/mysqlapp-ts-esm/config/config.ts new file mode 100644 index 0000000..204bf77 --- /dev/null +++ b/test/fixtures/apps/mysqlapp-ts-esm/config/config.ts @@ -0,0 +1,15 @@ +import type { EggAppConfig, PowerPartial } from 'egg'; + +export default { + keys: 'foo', + mysql: { + client: { + host: '127.0.0.1', + port: 3306, + user: 'root', + password: '', + database: 'test', + }, + agent: true, + }, +} satisfies PowerPartial; diff --git a/test/fixtures/apps/mysqlapp-ts-esm/package.json b/test/fixtures/apps/mysqlapp-ts-esm/package.json new file mode 100644 index 0000000..d31f99b --- /dev/null +++ b/test/fixtures/apps/mysqlapp-ts-esm/package.json @@ -0,0 +1,4 @@ +{ + "name": "mysqlapp-ts-esm", + "type": "module" +} diff --git a/test/fixtures/apps/mysqlapp-ts-esm/tsconfig.json b/test/fixtures/apps/mysqlapp-ts-esm/tsconfig.json new file mode 100644 index 0000000..376b902 --- /dev/null +++ b/test/fixtures/apps/mysqlapp-ts-esm/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "@eggjs/tsconfig" +} diff --git a/test/fixtures/apps/mysqlapp-ts-esm/typings/index.d.ts b/test/fixtures/apps/mysqlapp-ts-esm/typings/index.d.ts new file mode 100644 index 0000000..0be8969 --- /dev/null +++ b/test/fixtures/apps/mysqlapp-ts-esm/typings/index.d.ts @@ -0,0 +1 @@ +import '../../../../../src/index.ts'; From 82f21e3959df947a444ee541e3401c33b572f8ac Mon Sep 17 00:00:00 2001 From: fengmk2 Date: Sun, 23 Mar 2025 20:31:59 +0800 Subject: [PATCH 5/7] use prettier --- .github/workflows/nodejs.yml | 4 +- .github/workflows/release.yml | 2 +- .prettierrc | 6 + CHANGELOG.md | 87 ++++++----- README.md | 26 ++-- README.zh-CN.md | 32 ++-- __snapshots__/mysql.test.ts.js | 24 +-- package.json | 9 ++ src/boot.ts | 23 ++- test/esm.test.ts | 25 ++-- .../mysqlapp-disable/config/config.default.js | 2 +- .../mysqlapp-dynamic/config/config.default.js | 2 +- .../app/controller/home.js | 5 +- .../app/service/user.js | 2 +- .../config/config.default.js | 48 +++--- .../apps/mysqlapp-new/app/service/user.js | 2 +- .../mysqlapp-new/config/config.default.js | 12 +- .../apps/mysqlapp/app/controller/home.js | 2 +- .../apps/mysqlapp/app/service/user.js | 2 +- test/fixtures/apps/mysqlapp/config/config.js | 2 +- test/mysql.test.ts | 139 ++++++++++++------ 21 files changed, 275 insertions(+), 181 deletions(-) create mode 100644 .prettierrc diff --git a/.github/workflows/nodejs.yml b/.github/workflows/nodejs.yml index 449d10d..36312e6 100644 --- a/.github/workflows/nodejs.yml +++ b/.github/workflows/nodejs.yml @@ -2,9 +2,9 @@ name: CI on: push: - branches: [ master ] + branches: [master] pull_request: - branches: [ master ] + branches: [master] jobs: Job: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a2bf04a..4240f1c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -2,7 +2,7 @@ name: Release on: push: - branches: [ master ] + branches: [master] jobs: release: diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..43aee1a --- /dev/null +++ b/.prettierrc @@ -0,0 +1,6 @@ +{ + "singleQuote": true, + "trailingComma": "es5", + "tabWidth": 2, + "arrowParens": "avoid" +} diff --git a/CHANGELOG.md b/CHANGELOG.md index 5f740fb..b0644a7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,27 +2,26 @@ ## [5.0.0](https://github.com/eggjs/egg-mysql/compare/v4.1.0...v5.0.0) (2025-03-08) - ### ⚠ BREAKING CHANGES -* drop Node.js < 18 support -* use mysql2 instead of mysql +- drop Node.js < 18 support +- use mysql2 instead of mysql closes https://github.com/eggjs/egg-mysql/issues/31 - + ## Summary by CodeRabbit - **Documentation** - Updated usage instructions and contributor displays with corrected -typographical errors. + typographical errors. - **Chores** - - Removed outdated contributor templates and automation workflows. + - Removed outdated contributor templates and automation workflows. - **Dependencies** - Migrated to a new database client package and raised the minimum -Node.js version requirement. + Node.js version requirement. - **Tests** - Refined error validations in database operations for improved error reporting. @@ -30,88 +29,84 @@ reporting. ### Features -* use @eggjs/rds instead of ali-rds ([#32](https://github.com/eggjs/egg-mysql/issues/32)) ([3093528](https://github.com/eggjs/egg-mysql/commit/30935285268b1c674f9a70b4cb77501f89276467)) +- use @eggjs/rds instead of ali-rds ([#32](https://github.com/eggjs/egg-mysql/issues/32)) ([3093528](https://github.com/eggjs/egg-mysql/commit/30935285268b1c674f9a70b4cb77501f89276467)) ## [4.1.0](https://github.com/eggjs/egg-mysql/compare/v4.0.0...v4.1.0) (2025-03-08) - ### Features -* update ali-rds@6.4.0 ([#30](https://github.com/eggjs/egg-mysql/issues/30)) ([31ed369](https://github.com/eggjs/egg-mysql/commit/31ed369d954ce4bded89469907f81dcbde3060df)) +- update ali-rds@6.4.0 ([#30](https://github.com/eggjs/egg-mysql/issues/30)) ([31ed369](https://github.com/eggjs/egg-mysql/commit/31ed369d954ce4bded89469907f81dcbde3060df)) ## [4.0.0](https://github.com/eggjs/egg-mysql/compare/v3.4.0...v4.0.0) (2023-03-06) - ### ⚠ BREAKING CHANGES -* drop Node.js < 16 support +- drop Node.js < 16 support ### Features -* refactor with TypeScript ([#27](https://github.com/eggjs/egg-mysql/issues/27)) ([4b7311d](https://github.com/eggjs/egg-mysql/commit/4b7311d2beb43a0338337c7128e016690ea04c9e)) +- refactor with TypeScript ([#27](https://github.com/eggjs/egg-mysql/issues/27)) ([4b7311d](https://github.com/eggjs/egg-mysql/commit/4b7311d2beb43a0338337c7128e016690ea04c9e)) ## [3.4.0](https://github.com/eggjs/egg-mysql/compare/v3.3.0...v3.4.0) (2023-02-15) - ### Features -* **type:** add type definition for mysql.count method ([#26](https://github.com/eggjs/egg-mysql/issues/26)) ([7aef13e](https://github.com/eggjs/egg-mysql/commit/7aef13eb861b41c538d3b3d561c92a666a61110b)) +- **type:** add type definition for mysql.count method ([#26](https://github.com/eggjs/egg-mysql/issues/26)) ([7aef13e](https://github.com/eggjs/egg-mysql/commit/7aef13eb861b41c538d3b3d561c92a666a61110b)) ## [3.3.0](https://github.com/eggjs/egg-mysql/compare/v3.2.0...v3.3.0) (2022-12-18) - ### Features -* upgrade ali-rds v4 ([#24](https://github.com/eggjs/egg-mysql/issues/24)) ([1b129e8](https://github.com/eggjs/egg-mysql/commit/1b129e8f94b0739a5515d5704be301df85f97b30)) +- upgrade ali-rds v4 ([#24](https://github.com/eggjs/egg-mysql/issues/24)) ([1b129e8](https://github.com/eggjs/egg-mysql/commit/1b129e8f94b0739a5515d5704be301df85f97b30)) --- -3.2.0 / 2022-12-03 -================== +# 3.2.0 / 2022-12-03 **features** - * [[`4cf93ce`](http://github.com/eggjs/egg-mysql/commit/4cf93ce5fbeeb3fc734a8e7ba708b27994adad88)] - feat: add more type definition for mysql.get method (#20) (Xin(Khalil) Zhang <>) + +- [[`4cf93ce`](http://github.com/eggjs/egg-mysql/commit/4cf93ce5fbeeb3fc734a8e7ba708b27994adad88)] - feat: add more type definition for mysql.get method (#20) (Xin(Khalil) Zhang <>) **fixes** - * [[`d3c8a31`](http://github.com/eggjs/egg-mysql/commit/d3c8a31e02beccc8823820340bda89fe307a34ea)] - fix: remove reckless assertion (#15) (WangJie <>) + +- [[`d3c8a31`](http://github.com/eggjs/egg-mysql/commit/d3c8a31e02beccc8823820340bda89fe307a34ea)] - fix: remove reckless assertion (#15) (WangJie <>) **others** - * [[`ed419d6`](http://github.com/eggjs/egg-mysql/commit/ed419d6c51e25fa3ea2a4b91628375d4d4dcb77d)] - 🤖 TEST: Add tsd test (#22) (fengmk2 <>) - * [[`4137cfc`](http://github.com/eggjs/egg-mysql/commit/4137cfc46e0db04f6122b065516055a99765eb19)] - 📖 DOC: Use async/await isntead of yield (fengmk2 <>) - * [[`b103400`](http://github.com/eggjs/egg-mysql/commit/b103400c153176bd9c38e35d72aa3a791999ec27)] - 📖 DOC: Update contributors (fengmk2 <>) - * [[`a67989c`](http://github.com/eggjs/egg-mysql/commit/a67989c4e6c55604d8d61d1af7af9bc5df35df2e)] - 🤖 TEST: Run test on GitHub Action (#19) (fengmk2 <>) - * [[`3d04360`](http://github.com/eggjs/egg-mysql/commit/3d04360fd7745ef45d32e8e27c5691878d0cd3bf)] - Create codeql-analysis.yml (fengmk2 <>) -3.1.1 / 2022-06-03 -================== +- [[`ed419d6`](http://github.com/eggjs/egg-mysql/commit/ed419d6c51e25fa3ea2a4b91628375d4d4dcb77d)] - 🤖 TEST: Add tsd test (#22) (fengmk2 <>) +- [[`4137cfc`](http://github.com/eggjs/egg-mysql/commit/4137cfc46e0db04f6122b065516055a99765eb19)] - 📖 DOC: Use async/await isntead of yield (fengmk2 <>) +- [[`b103400`](http://github.com/eggjs/egg-mysql/commit/b103400c153176bd9c38e35d72aa3a791999ec27)] - 📖 DOC: Update contributors (fengmk2 <>) +- [[`a67989c`](http://github.com/eggjs/egg-mysql/commit/a67989c4e6c55604d8d61d1af7af9bc5df35df2e)] - 🤖 TEST: Run test on GitHub Action (#19) (fengmk2 <>) +- [[`3d04360`](http://github.com/eggjs/egg-mysql/commit/3d04360fd7745ef45d32e8e27c5691878d0cd3bf)] - Create codeql-analysis.yml (fengmk2 <>) + +# 3.1.1 / 2022-06-03 **fixes** - * [[`bb7856b`](http://github.com/eggjs/egg-mysql/commit/bb7856bbf8e363f2ee0ce9410204fd227c2ccd08)] - fix: mysql.update missing condition define (#18) (shangwenhe <>) -3.1.0 / 2022-02-11 -================== +- [[`bb7856b`](http://github.com/eggjs/egg-mysql/commit/bb7856bbf8e363f2ee0ce9410204fd227c2ccd08)] - fix: mysql.update missing condition define (#18) (shangwenhe <>) + +# 3.1.0 / 2022-02-11 **features** - * [[`aade70b`](http://github.com/eggjs/egg-mysql/commit/aade70bce78afb39e8e9b3201261bbb8bcf26847)] - feat: add complete typescript typings (#17) (AntiMoron <>) + +- [[`aade70b`](http://github.com/eggjs/egg-mysql/commit/aade70bce78afb39e8e9b3201261bbb8bcf26847)] - feat: add complete typescript typings (#17) (AntiMoron <>) **others** - * [[`2e02e40`](http://github.com/eggjs/egg-mysql/commit/2e02e402d6d23740f68ae26c28633303d4d9e206)] - chore: update travis (#16) (TZ | 天猪 <>) - * [[`89910f6`](http://github.com/eggjs/egg-mysql/commit/89910f6ef17be38b59bc066d754793cc65a84624)] - test: add null query test case (#13) (Century Guo <<648772021@qq.com>>) - * [[`b0dd988`](http://github.com/eggjs/egg-mysql/commit/b0dd988d51b95d576c852d54d26014a845ac2f3d)] - deps: autod (#12) (TZ | 天猪 <>) - * [[`18b67fd`](http://github.com/eggjs/egg-mysql/commit/18b67fd3e43627ad420ed3df8e8a6e305f5202f6)] - deps: upgrade dependencies (#10) (Haoliang Gao <>) -3.0.0 / 2017-04-03 -================== +- [[`2e02e40`](http://github.com/eggjs/egg-mysql/commit/2e02e402d6d23740f68ae26c28633303d4d9e206)] - chore: update travis (#16) (TZ | 天猪 <>) +- [[`89910f6`](http://github.com/eggjs/egg-mysql/commit/89910f6ef17be38b59bc066d754793cc65a84624)] - test: add null query test case (#13) (Century Guo <<648772021@qq.com>>) +- [[`b0dd988`](http://github.com/eggjs/egg-mysql/commit/b0dd988d51b95d576c852d54d26014a845ac2f3d)] - deps: autod (#12) (TZ | 天猪 <>) +- [[`18b67fd`](http://github.com/eggjs/egg-mysql/commit/18b67fd3e43627ad420ed3df8e8a6e305f5202f6)] - deps: upgrade dependencies (#10) (Haoliang Gao <>) + +# 3.0.0 / 2017-04-03 - * deps: ali-rds@3 (#9) +- deps: ali-rds@3 (#9) -2.0.0 / 2017-02-09 -================== +# 2.0.0 / 2017-02-09 - * feat: remove app.instrument and fix test (#5) +- feat: remove app.instrument and fix test (#5) -1.0.1 / 2016-12-30 -================== +# 1.0.1 / 2016-12-30 - * docs: add app.js and agent.js extend intro (#3) - * docs: add English translation doc (#2) +- docs: add app.js and agent.js extend intro (#3) +- docs: add English translation doc (#2) diff --git a/README.md b/README.md index 1d3db7d..f947ede 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ export default { enable: true, package: '@eggjs/mysql', }, -} +}; ``` Configure database information in `${app_root}/config/config.default.ts`: @@ -67,7 +67,7 @@ export default { // load into agent, default is `false` agent: false, }, -} +}; ``` Usage: @@ -98,15 +98,13 @@ export default { // ... }, // default configuration for all databases - default: { - - }, + default: {}, // load into app, default is open app: true, // load into agent, default is close agent: false, }, -} +}; ``` Usage: @@ -135,11 +133,14 @@ const insertSuccess = result.affectedRows === 1; // get const post = await app.mysql.get('posts', { id: 12 }); // query -const results = await app.mysql.select('posts',{ +const results = await app.mysql.select('posts', { where: { status: 'draft' }, - orders: [['created_at','desc'], ['id','desc']], + orders: [ + ['created_at', 'desc'], + ['id', 'desc'], + ], limit: 10, - offset: 0 + offset: 0, }); ``` @@ -195,7 +196,7 @@ try { - disadvantage: all transation will be successful or failed, cannot control precisely ```ts -const result = await app.mysql.beginTransactionScope(async (conn) => { +const result = await app.mysql.beginTransactionScope(async conn => { // don't commit or rollback by yourself await conn.insert(table, row1); await conn.update(table, row2); @@ -209,7 +210,10 @@ const result = await app.mysql.beginTransactionScope(async (conn) => { ### Custom SQL splicing ```ts -const results = await app.mysql.query('update posts set hits = (hits + ?) where id = ?', [ 1, postId ]); +const results = await app.mysql.query( + 'update posts set hits = (hits + ?) where id = ?', + [1, postId] +); ``` ### Literal diff --git a/README.zh-CN.md b/README.zh-CN.md index 8c919b4..73539f3 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -35,7 +35,7 @@ export default { enable: true, package: '@eggjs/mysql', }, -} +}; ``` 在 `config/config.${env}.ts` 配置各个环境的数据库连接信息: @@ -56,14 +56,14 @@ export default { // 密码 password: 'test_password', // 数据库名 - database: 'test', + database: 'test', }, // 是否加载到 app 上,默认开启 app: true, // 是否加载到 agent 上,默认关闭 agent: false, }, -} +}; ``` 使用方式: @@ -94,16 +94,14 @@ export default { // ... }, // 所有数据库配置的默认值 - default: { - - }, + default: {}, // 是否加载到 app 上,默认开启 app: true, // 是否加载到 agent 上,默认关闭 agent: false, }, -} +}; ``` 使用方式: @@ -156,11 +154,14 @@ const insertSuccess = result.affectedRows === 1; // 获得一个 const post = await app.mysql.get('posts', { id: 12 }); // 查询 -const results = await app.mysql.select('posts',{ +const results = await app.mysql.select('posts', { where: { status: 'draft' }, - orders: [['created_at','desc'], ['id','desc']], + orders: [ + ['created_at', 'desc'], + ['id', 'desc'], + ], limit: 10, - offset: 0 + offset: 0, }); ``` @@ -182,7 +183,7 @@ const updateSuccess = result.affectedRows === 1; ```ts const result = await app.mysql.delete('table-name', { - name: 'fengmk2' + name: 'fengmk2', }); ``` @@ -216,7 +217,7 @@ try { - 缺点:整个事务要么成功,要么失败,无法做细粒度控制。 ```ts -const result = await app.mysql.beginTransactionScope(async (conn) => { +const result = await app.mysql.beginTransactionScope(async conn => { // don't commit or rollback by yourself await conn.insert(table, row1); await conn.update(table, row2); @@ -230,7 +231,10 @@ const result = await app.mysql.beginTransactionScope(async (conn) => { ### 自定义SQL拼接 ```ts -const results = await app.mysql.query('update posts set hits = (hits + ?) where id = ?', [1, postId]); +const results = await app.mysql.query( + 'update posts set hits = (hits + ?) where id = ?', + [1, postId] +); ``` ### 表达式(Literal) @@ -243,7 +247,7 @@ const results = await app.mysql.query('update posts set hits = (hits + ?) where ```ts await app.mysql.insert(table, { - create_time: app.mysql.literals.now + create_time: app.mysql.literals.now, }); // INSERT INTO `$table`(`create_time`) VALUES(NOW()) diff --git a/__snapshots__/mysql.test.ts.js b/__snapshots__/mysql.test.ts.js index cee2275..6aa475a 100644 --- a/__snapshots__/mysql.test.ts.js +++ b/__snapshots__/mysql.test.ts.js @@ -1,14 +1,14 @@ exports['test/mysql.test.ts should make default config stable 1'] = { - "default": { - "connectionLimit": 5 + default: { + connectionLimit: 5, }, - "app": true, - "agent": true, - "client": { - "host": "127.0.0.1", - "port": 3306, - "user": "root", - "password": "", - "database": "test" - } -} + app: true, + agent: true, + client: { + host: '127.0.0.1', + port: 3306, + user: 'root', + password: '', + database: 'test', + }, +}; diff --git a/package.json b/package.json index 4193859..b9f0a12 100644 --- a/package.json +++ b/package.json @@ -45,7 +45,9 @@ "@types/mocha": "10", "@types/node": "22", "egg": "^4.0.10", + "lint-staged": "^15.5.0", "oxlint": "^0.16.2", + "prettier": "^3.5.3", "rimraf": "6", "snap-shot-it": "^7.9.10", "tshy": "3", @@ -62,6 +64,13 @@ "clean": "rimraf dist", "prepublishOnly": "tshy && tshy-after && attw --pack" }, + "lint-staged": { + "*": "prettier --write --ignore-unknown --cache", + "*.{ts,js}": [ + "prettier --ignore-unknown --write", + "oxlint --fix" + ] + }, "type": "module", "tshy": { "exports": { diff --git a/src/boot.ts b/src/boot.ts index a2a6159..fe2b07e 100644 --- a/src/boot.ts +++ b/src/boot.ts @@ -1,14 +1,27 @@ import { RDSClient, type RDSClientOptions } from '@eggjs/rds'; import type { EggCore, ILifecycleBoot } from '@eggjs/core'; -async function createMySQLClient(config: RDSClientOptions, app: EggCore, clientName = 'default') { - app.coreLogger.info('[@eggjs/mysql] clientName[%s] connecting %s@%s:%s/%s', - clientName, config.user, config.host, config.port, config.database); +async function createMySQLClient( + config: RDSClientOptions, + app: EggCore, + clientName = 'default' +) { + app.coreLogger.info( + '[@eggjs/mysql] clientName[%s] connecting %s@%s:%s/%s', + clientName, + config.user, + config.host, + config.port, + config.database + ); const client = new RDSClient(config); const rows = await client.query('select now() as currentTime;'); - app.coreLogger.info('[@eggjs/mysql] clientName[%s] status OK, MySQL Server currentTime: %j', - clientName, rows[0].currentTime); + app.coreLogger.info( + '[@eggjs/mysql] clientName[%s] status OK, MySQL Server currentTime: %j', + clientName, + rows[0].currentTime + ); return client; } diff --git a/test/esm.test.ts b/test/esm.test.ts index 3910018..3be5c32 100644 --- a/test/esm.test.ts +++ b/test/esm.test.ts @@ -16,25 +16,32 @@ describe('test/esm.test.ts', () => { beforeEach(async () => { // init test datas - await app.mysql.query(`insert into npm_auth set user_id = 'egg-${uid}-1', password = '1'`); - await app.mysql.query(`insert into npm_auth set user_id = 'egg-${uid}-2', password = '2'`); - await app.mysql.query(`insert into npm_auth set user_id = 'egg-${uid}-3', password = '3'`); - await app.mysql.queryOne(`select * from npm_auth where user_id = 'egg-${uid}-3'`); + await app.mysql.query( + `insert into npm_auth set user_id = 'egg-${uid}-1', password = '1'` + ); + await app.mysql.query( + `insert into npm_auth set user_id = 'egg-${uid}-2', password = '2'` + ); + await app.mysql.query( + `insert into npm_auth set user_id = 'egg-${uid}-3', password = '3'` + ); + await app.mysql.queryOne( + `select * from npm_auth where user_id = 'egg-${uid}-3'` + ); }); afterEach(async () => { - await app.mysql.query(`delete from npm_auth where user_id like 'egg-${uid}%'`); + await app.mysql.query( + `delete from npm_auth where user_id like 'egg-${uid}%'` + ); }); after(async () => { await app.close(); }); - it('should query mysql user table success', async () => { - const res = await app.httpRequest() - .get('/') - .expect(200); + const res = await app.httpRequest().get('/').expect(200); assert.equal(res.body.status, 'success'); assert.equal(res.body.users.length, 3); diff --git a/test/fixtures/apps/mysqlapp-disable/config/config.default.js b/test/fixtures/apps/mysqlapp-disable/config/config.default.js index 820f865..5b9e887 100644 --- a/test/fixtures/apps/mysqlapp-disable/config/config.default.js +++ b/test/fixtures/apps/mysqlapp-disable/config/config.default.js @@ -4,7 +4,7 @@ exports.mysql = { user: 'root', password: '', database: 'test', - app:false + app: false, }; exports.keys = 'foo'; diff --git a/test/fixtures/apps/mysqlapp-dynamic/config/config.default.js b/test/fixtures/apps/mysqlapp-dynamic/config/config.default.js index b8a5ea1..9996350 100644 --- a/test/fixtures/apps/mysqlapp-dynamic/config/config.default.js +++ b/test/fixtures/apps/mysqlapp-dynamic/config/config.default.js @@ -7,7 +7,7 @@ exports.mysql1 = { port: 3306, user: 'root', password: '', - database: 'test' + database: 'test', }; exports.keys = 'foo'; diff --git a/test/fixtures/apps/mysqlapp-multi-client-wrong/app/controller/home.js b/test/fixtures/apps/mysqlapp-multi-client-wrong/app/controller/home.js index 6ace8b5..14fc028 100644 --- a/test/fixtures/apps/mysqlapp-multi-client-wrong/app/controller/home.js +++ b/test/fixtures/apps/mysqlapp-multi-client-wrong/app/controller/home.js @@ -1,7 +1,8 @@ -module.exports = async (ctx) => { +module.exports = async ctx => { const dataArr = await ctx.service.user.list(ctx); ctx.body = { - hasRows: dataArr[0].length > 0 && dataArr[1].length > 0 && dataArr[2].length > 0, + hasRows: + dataArr[0].length > 0 && dataArr[1].length > 0 && dataArr[2].length > 0, }; }; diff --git a/test/fixtures/apps/mysqlapp-multi-client-wrong/app/service/user.js b/test/fixtures/apps/mysqlapp-multi-client-wrong/app/service/user.js index ce9a3ab..1bd06f7 100644 --- a/test/fixtures/apps/mysqlapp-multi-client-wrong/app/service/user.js +++ b/test/fixtures/apps/mysqlapp-multi-client-wrong/app/service/user.js @@ -1,4 +1,4 @@ -exports.list = async (ctx) => { +exports.list = async ctx => { return await Promise.all([ ctx.app.mysqls.get('db1').query('select * from npm_auth'), ctx.app.mysqls.get('db2').query('select * from npm_auth'), diff --git a/test/fixtures/apps/mysqlapp-multi-client-wrong/config/config.default.js b/test/fixtures/apps/mysqlapp-multi-client-wrong/config/config.default.js index 055904c..f665589 100644 --- a/test/fixtures/apps/mysqlapp-multi-client-wrong/config/config.default.js +++ b/test/fixtures/apps/mysqlapp-multi-client-wrong/config/config.default.js @@ -1,26 +1,30 @@ exports.mysql = { - clients: [{ - clientId: 'db1', - host: '127.0.0.1', - port: 3306, - user: 'root', - password: '123', - database: 'test', - }, { - clientId: 'db2', - host: '127.0.0.1', - port: 3306, - user: 'root', - password: 'wrong', - database: 'test', - }, { - clientId: 'db3', - host: '127.0.0.1', - port: 3306, - user: 'root', - password: '', - database: 'test', - }], + clients: [ + { + clientId: 'db1', + host: '127.0.0.1', + port: 3306, + user: 'root', + password: '123', + database: 'test', + }, + { + clientId: 'db2', + host: '127.0.0.1', + port: 3306, + user: 'root', + password: 'wrong', + database: 'test', + }, + { + clientId: 'db3', + host: '127.0.0.1', + port: 3306, + user: 'root', + password: '', + database: 'test', + }, + ], agent: true, }; diff --git a/test/fixtures/apps/mysqlapp-new/app/service/user.js b/test/fixtures/apps/mysqlapp-new/app/service/user.js index bea5766..d83d1d6 100644 --- a/test/fixtures/apps/mysqlapp-new/app/service/user.js +++ b/test/fixtures/apps/mysqlapp-new/app/service/user.js @@ -1,3 +1,3 @@ -exports.list = async (ctx) => { +exports.list = async ctx => { return await ctx.app.mysql.query('select * from npm_auth'); }; diff --git a/test/fixtures/apps/mysqlapp-new/config/config.default.js b/test/fixtures/apps/mysqlapp-new/config/config.default.js index 1d79a26..af5641a 100644 --- a/test/fixtures/apps/mysqlapp-new/config/config.default.js +++ b/test/fixtures/apps/mysqlapp-new/config/config.default.js @@ -1,12 +1,12 @@ exports.mysql = { client: { - host: '127.0.0.1', - port: 3306, - user: 'root', - password: '', - database: 'test' + host: '127.0.0.1', + port: 3306, + user: 'root', + password: '', + database: 'test', }, - agent: true + agent: true, }; exports.keys = 'foo'; diff --git a/test/fixtures/apps/mysqlapp/app/controller/home.js b/test/fixtures/apps/mysqlapp/app/controller/home.js index 5d6b137..def83f9 100644 --- a/test/fixtures/apps/mysqlapp/app/controller/home.js +++ b/test/fixtures/apps/mysqlapp/app/controller/home.js @@ -1,4 +1,4 @@ -module.exports = async (ctx) => { +module.exports = async ctx => { const users = await ctx.service.user.list(ctx); ctx.body = { diff --git a/test/fixtures/apps/mysqlapp/app/service/user.js b/test/fixtures/apps/mysqlapp/app/service/user.js index bea5766..d83d1d6 100644 --- a/test/fixtures/apps/mysqlapp/app/service/user.js +++ b/test/fixtures/apps/mysqlapp/app/service/user.js @@ -1,3 +1,3 @@ -exports.list = async (ctx) => { +exports.list = async ctx => { return await ctx.app.mysql.query('select * from npm_auth'); }; diff --git a/test/fixtures/apps/mysqlapp/config/config.js b/test/fixtures/apps/mysqlapp/config/config.js index c08e493..af5641a 100644 --- a/test/fixtures/apps/mysqlapp/config/config.js +++ b/test/fixtures/apps/mysqlapp/config/config.js @@ -6,7 +6,7 @@ exports.mysql = { password: '', database: 'test', }, - agent: true + agent: true, }; exports.keys = 'foo'; diff --git a/test/mysql.test.ts b/test/mysql.test.ts index 8e5bf89..5dbcbf8 100644 --- a/test/mysql.test.ts +++ b/test/mysql.test.ts @@ -24,10 +24,18 @@ describe('test/mysql.test.ts', () => { beforeEach(async () => { // init test datas try { - await app.mysql.query(`insert into npm_auth set user_id = 'egg-${uid}-1', password = '1'`); - await app.mysql.query(`insert into npm_auth set user_id = 'egg-${uid}-2', password = '2'`); - await app.mysql.query(`insert into npm_auth set user_id = 'egg-${uid}-3', password = '3'`); - await app.mysql.queryOne(`select * from npm_auth where user_id = 'egg-${uid}-3'`); + await app.mysql.query( + `insert into npm_auth set user_id = 'egg-${uid}-1', password = '1'` + ); + await app.mysql.query( + `insert into npm_auth set user_id = 'egg-${uid}-2', password = '2'` + ); + await app.mysql.query( + `insert into npm_auth set user_id = 'egg-${uid}-3', password = '3'` + ); + await app.mysql.queryOne( + `select * from npm_auth where user_id = 'egg-${uid}-3'` + ); } catch (err) { console.log('init test datas error: %s', err); } @@ -35,7 +43,9 @@ describe('test/mysql.test.ts', () => { afterEach(async () => { // 清空测试数据 - await app.mysql.query(`delete from npm_auth where user_id like 'egg-${uid}%'`); + await app.mysql.query( + `delete from npm_auth where user_id like 'egg-${uid}%'` + ); }); after(async () => { @@ -50,17 +60,17 @@ describe('test/mysql.test.ts', () => { }); it('should query mysql user table success', () => { - return app.httpRequest() - .get('/') - .expect(200); + return app.httpRequest().get('/').expect(200); }); it('should query limit 2', async () => { - const users = await app.mysql.query('select * from npm_auth order by id desc limit 2'); + const users = await app.mysql.query( + 'select * from npm_auth order by id desc limit 2' + ); assert(users.length === 2); const rows = await app.mysql.select('npm_auth', { - orders: [[ 'id', 'desc' ]], + orders: [['id', 'desc']], limit: 2, }); assert(rows.length === 2); @@ -69,19 +79,28 @@ describe('test/mysql.test.ts', () => { }); it('should update successfully', async () => { - const user = await app.mysql.queryOne('select * from npm_auth order by id desc limit 10'); - const result = await app.mysql.update('npm_auth', { id: user.id, user_id: `79744-${uid}-update` }); + const user = await app.mysql.queryOne( + 'select * from npm_auth order by id desc limit 10' + ); + const result = await app.mysql.update('npm_auth', { + id: user.id, + user_id: `79744-${uid}-update`, + }); assert(result.affectedRows === 1); }); it('should delete successfully', async () => { - const user = await app.mysql.queryOne('select * from npm_auth order by id desc limit 10'); + const user = await app.mysql.queryOne( + 'select * from npm_auth order by id desc limit 10' + ); const result = await app.mysql.delete('npm_auth', { id: user.id }); assert(result.affectedRows === 1); }); it('should query one success', async () => { - const user = await app.mysql.queryOne('select * from npm_auth order by id desc limit 10'); + const user = await app.mysql.queryOne( + 'select * from npm_auth order by id desc limit 10' + ); assert(user); assert(typeof user.user_id === 'string' && user.user_id); @@ -90,7 +109,9 @@ describe('test/mysql.test.ts', () => { }); it('should query one desc is NULL success', async () => { - const user = await app.mysql.queryOne('select * from npm_auth where `desc` is NULL'); + const user = await app.mysql.queryOne( + 'select * from npm_auth where `desc` is NULL' + ); assert(user); assert(typeof user.user_id === 'string' && user.user_id); @@ -99,7 +120,9 @@ describe('test/mysql.test.ts', () => { }); it('should query with literal in where conditions', async () => { - const user = await app.mysql.queryOne('select * from npm_auth where `password` is not NULL'); + const user = await app.mysql.queryOne( + 'select * from npm_auth where `password` is not NULL' + ); assert(user); assert(typeof user.user_id === 'string' && user.user_id); @@ -126,23 +149,31 @@ describe('test/mysql.test.ts', () => { const app = mm.app({ baseDir: 'apps/mysqlapp-multi-client-wrong', }); - await assert.rejects(async () => { - await app.ready(); - }, (err) => { - assert(err instanceof Error); - assert.match(err.message, /Access denied for user/); - assert.equal(Reflect.get(err, 'code'), 'ER_ACCESS_DENIED_ERROR'); - assert.equal(Reflect.get(err, 'errno'), 1045); - assert.equal(Reflect.get(err, 'sqlState'), '28000'); - assert.match(Reflect.get(err, 'sqlMessage'), /^Access denied for user 'root'@'[^']+' \(using password: YES\)$/); - assert.equal(err.name, 'RDSClientGetConnectionError'); - return true; - }); + await assert.rejects( + async () => { + await app.ready(); + }, + err => { + assert(err instanceof Error); + assert.match(err.message, /Access denied for user/); + assert.equal(Reflect.get(err, 'code'), 'ER_ACCESS_DENIED_ERROR'); + assert.equal(Reflect.get(err, 'errno'), 1045); + assert.equal(Reflect.get(err, 'sqlState'), '28000'); + assert.match( + Reflect.get(err, 'sqlMessage'), + /^Access denied for user 'root'@'[^']+' \(using password: YES\)$/ + ); + assert.equal(err.name, 'RDSClientGetConnectionError'); + return true; + } + ); }); it('should queryOne work on transaction', async () => { const result = await app.mysql.beginTransactionScope(async conn => { - const row = await conn.queryOne('select * from npm_auth order by id desc limit 10'); + const row = await conn.queryOne( + 'select * from npm_auth order by id desc limit 10' + ); return { row }; }); assert(result.row); @@ -161,9 +192,15 @@ describe('test/mysql.test.ts', () => { after(() => app.close()); it('should agent.mysql work', () => { - const result = fs.readFileSync(path.join(__dirname, - './fixtures/apps/mysqlapp/run/agent_result.json'), 'utf8'); - assert(/\[\{"currentTime":"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z"\}\]/.test(result)); + const result = fs.readFileSync( + path.join(__dirname, './fixtures/apps/mysqlapp/run/agent_result.json'), + 'utf8' + ); + assert( + /\[\{"currentTime":"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z"\}\]/.test( + result + ) + ); }); }); @@ -194,15 +231,22 @@ describe('test/mysql.test.ts', () => { after(() => app.close()); it('should new config agent.mysql work', () => { - const result = fs.readFileSync(path.join(__dirname, - './fixtures/apps/mysqlapp-new/run/agent_result.json'), 'utf8'); - assert(/\[\{"currentTime":"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z"\}\]/.test(result)); + const result = fs.readFileSync( + path.join( + __dirname, + './fixtures/apps/mysqlapp-new/run/agent_result.json' + ), + 'utf8' + ); + assert( + /\[\{"currentTime":"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z"\}\]/.test( + result + ) + ); }); it('should query mysql user table success', () => { - return app.httpRequest() - .get('/') - .expect(200); + return app.httpRequest().get('/').expect(200); }); }); @@ -218,15 +262,22 @@ describe('test/mysql.test.ts', () => { after(() => app.close()); it('should new config agent.mysql work', () => { - const result = fs.readFileSync(path.join(__dirname, - './fixtures/apps/mysqlapp-dynamic/run/agent_result.json'), 'utf8'); - assert(/\[\{"currentTime":"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z"\}\]/.test(result)); + const result = fs.readFileSync( + path.join( + __dirname, + './fixtures/apps/mysqlapp-dynamic/run/agent_result.json' + ), + 'utf8' + ); + assert( + /\[\{"currentTime":"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z"\}\]/.test( + result + ) + ); }); it('should query mysql user table success', () => { - return app.httpRequest() - .get('/') - .expect(200); + return app.httpRequest().get('/').expect(200); }); }); }); From c80602ee509b9676fe5f9c072294b8410b6c51c5 Mon Sep 17 00:00:00 2001 From: fengmk2 Date: Sun, 23 Mar 2025 20:34:08 +0800 Subject: [PATCH 6/7] add husky --- .husky/pre-commit | 1 + package.json | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 .husky/pre-commit diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100644 index 0000000..2312dc5 --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1 @@ +npx lint-staged diff --git a/package.json b/package.json index b9f0a12..64279e5 100644 --- a/package.json +++ b/package.json @@ -45,6 +45,7 @@ "@types/mocha": "10", "@types/node": "22", "egg": "^4.0.10", + "husky": "^9.1.7", "lint-staged": "^15.5.0", "oxlint": "^0.16.2", "prettier": "^3.5.3", @@ -62,7 +63,8 @@ "ci": "egg-bin cov", "postci": "npm run prepublishOnly && npm run clean", "clean": "rimraf dist", - "prepublishOnly": "tshy && tshy-after && attw --pack" + "prepublishOnly": "tshy && tshy-after && attw --pack", + "prepare": "husky" }, "lint-staged": { "*": "prettier --write --ignore-unknown --cache", From 454816dba14b00154fd025664c4957c2ad077c90 Mon Sep 17 00:00:00 2001 From: fengmk2 Date: Sun, 23 Mar 2025 20:35:17 +0800 Subject: [PATCH 7/7] add more files --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 64279e5..ec9e318 100644 --- a/package.json +++ b/package.json @@ -68,7 +68,7 @@ }, "lint-staged": { "*": "prettier --write --ignore-unknown --cache", - "*.{ts,js}": [ + "*.{ts,js,json,md,yml}": [ "prettier --ignore-unknown --write", "oxlint --fix" ]