diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b707151 --- /dev/null +++ b/.gitignore @@ -0,0 +1,14 @@ +.DS_Store +.AppleDouble +.LSOverride +Icon +/dist +/node_modules + +# Thumbnails +._* + +# Files that might appear on external disk +.Spotlight-V100 +.Trashes + diff --git a/History.md b/History.md index 5c7d9d2..4cd74fc 100644 --- a/History.md +++ b/History.md @@ -1,3 +1,14 @@ +3.0.0 / 2018-04-16 +================== +> 这是一个全新的大版本, 不向下兼容2.x, 使用typescript重构。 + + * 重构API, 使用链式操作, 逻辑更加清晰 + * 支持完整的pool option + * 新增drop方法,支持删除数据库/数据表 + * 新增tableCreate/dbCreate方法, 支持创建数据库/数据表 + * 新增indexCreate/indexRename/indexDrop/indexList, 支持对索引的增删改查 + + 2.2.2 / 2018-03-22 ================== * 增加时区和BIGINT配置 diff --git a/docs/2.x.md b/docs/2.x.md new file mode 100644 index 0000000..33bd64f --- /dev/null +++ b/docs/2.x.md @@ -0,0 +1,224 @@ +![module info](https://nodei.co/npm/mysqli.png?downloads=true&downloadRank=true&stars=true) + +# mysqli + +> 本模块基于 node-mysql 模块二次封装,将SQL语法转为类似MongoDB的API。对常用的增删改查提供了简单的API, 并且进行了SQL注入过滤, 对新手非常友好。 + +## 使用 npm 安装 + +```bash +npm install mysqli +``` + +## 实例化 + +> 实例化可以传入一个数组,或单个object配置。只有一个数据库时,默认 +> 是主库 ; 多于 1 个数据库服务时,自动以第 1 个为主库,其他的从库,故实例化时 +> ,`注意顺序`。 + +```javascript +let Mysqli = require('mysqli') + +//传入json +let conn = new Mysqli({ + host: '', // IP/域名 + post: 3306, //端口, 默认 3306 + user: '', //用户名 + passwd: '', //密码 + charset: '', // 数据库编码,默认 utf8 【可选】 + db: '' // 可指定数据库,也可以不指定 【可选】 +}) + +// 传入数组 +let conn = new Mysqli([ + { + host: 'host1', // IP/域名 + post: 3306, //端口, 默认 3306 + user: '', //用户名 + passwd: '', //密码 + charset: '', // 数据库编码,默认 utf8 【可选】 + db: '' // 可指定数据库,也可以不指定 【可选】 + }, + { + host: 'host2', // IP/域名 + post: 3306, //端口, 默认 3306 + user: '', //用户名 + passwd: '', //密码 + charset: '', // 数据库编码,默认 utf8 【可选】 + db: '' // 可指定数据库,也可以不指定 【可选】 + } +]) +``` + +## API 方法 + +### 1. escape(val) + +> 这是`node-mysql`的内部方法,用于进行 SQL 安全过滤,这里只是做了搬运工,把它暴 +> 露出来给外部调用而已。 **这个是静态方法** + +```javascript +const Mysqli = require('mysqli') + +Mysqli.escape('这是文本') +``` + +### 2. emit(isSlave, dbName) + +* isSlave `` 可选 +* dbName `` 可选。 如果在实例化连接时 , 已经传入 db, 则这里可不传值。 + +> 触发一个数据库实例 , 可接受 2 个参数 , 第 1 个为 " 是否从库 ", 第 2 个为 " 数 +> 据库名称 " +> `这一步是必须要的, ` +```javascript +const Mysqli = require('mysqli') +let conn = new Mysqli({ + /*...*/ +}) +let db = conn.emit(true, 'test') +``` + +### 3. listDb() + +> 顾名思义,该方法即用于列举当前账号权限范围内的所有的数据库名称,返回值是一个数 +> 组 ; +> +> **注:**`方法返回的是一个 Promise 对象` + +```javascript +db.listDb().then(list => { + console.log(list) +}) +``` + +### 4. listTable() + +> 该方法用于列举当前数据库数据表的集合 + +```javascript +db.listTable().then(list => { + console.log(list) +}) +``` + +### 5. query(sql) + +* sql `` + +> 该方法用于当内置的方法满足不了需求时,可以自行编写`sql 语句`执行 ; 但要注意防 +> 止`sql 注入`,因为该方法是最基础的方法,模块不对传入的`sql 语句`进行任何的安全 +> 过滤。 + +```javascript +// 不使用await指令时,返回的是Promise对象 +db.query(`select * from users limit 10`).then(row => { + console.log(row) +}) +``` + +### 6. find(condition, select) + +* condition ``, 查询条件 +* select ``, 要返回字段 , 默认全部返回 + +> 该方法用于查询多条数据。无论结果是多少条,返回的都是`数组格式`; 详细请看下面代 +> 码示例: + +```javascript +db + .find( + { + table: '', // 要查询的表 + where: {foo: 'bar', goo: {$like: 'hello%'}}, //条件, 语法类似mongodb + sort: { + //排序, key是要排序的字段,value是排序方式, 1顺序,-1逆序 【可选】 + a: 1, + b: -1 + }, + limit: [0, 1] // 查询范围,可用于分页 【可选】 + }, + ['id', 'name'] //要select的字段 + ) + .then(row => { + console.log(row) + }) +``` + +### 7. findOne(condition) + +* condition `` + +> 该方法与上面的`filter`方法的使用一致,区别只是该方法只返回一条数据,且为`json +> 格式`。 + +### 8. count(condition) + +* condition `` + +> 该方法与上面的`filter`方法的使用一致,不过返回的是条目总数 (``) + +### 9. insert(condition, doc) + +* condition ``, 插入的条件 +* doc ``, 插入的数据 + +> 该方法与上面的`filter`方法的使用类似,手于插入一条数据,具体请看下面代码 ; +> +> **注:**`该方法一次只能插入一条数据` + +```javascript +// 如果主键是自增ID,则结果返回的是 刚插入的数据的自增ID +db + .insert( + { + table: 'xx' + }, + { xx: 1234 } //要插入的数据 + ) + .then(id => { + console.log(id) + }) +``` + +### 10. update(condition, doc) + +* condition ``, 条件 +* doc ``, 数据 + +> 该方法与上面的`filter`方法的使用类似,用于更新数据,具体请看下面代码 ; `该方法返 +> 回的是被修改的条目数量` + +```javascript +// 如果修改成功,则返回被修改的数量 +db + .update( + { + table: 'xx' + }, + { xx: 1234 } //要插入的数据 + ) + .then(num => { + console.log(num) + }) +``` + +### 11. remove(condition) + +* condition `` + +> 该方法与上面的`update`方法的使用类似,用于删除指定条件的数据 ; 具体请看下面代码 +> ; `该方法返回的是被删除的条目数量` + +```javascript +// 如果修改成功,返回的是被删除的条目数量 +db + .remove( + { + table: 'xx' + } + ) + .then(num => { + console.log(num) + }) +``` diff --git a/docs/3.x.md b/docs/3.x.md new file mode 100644 index 0000000..e69de29 diff --git a/lib/method.js b/lib/method.js deleted file mode 100644 index 5b5ec7e..0000000 --- a/lib/method.js +++ /dev/null @@ -1,255 +0,0 @@ -/** - * - * @authors yutent (yutent@doui.cc) - * @date 2017-12-14 14:01:03 - * @version $Id$ - */ - -const { escape } = require('mysql') -const parser = require('./parser') - -class SqlErr extends Error { - constructor(msg, sql) { - super(msg) - this.sql = sql || '' - } -} - -class Method { - constructor(pool, slave, db) { - this.pool = pool - this.slave = slave - this.db = db - } - - connect() { - const defer = Promise.defer() - this.pool.getConnection(this.slave, (err, conn) => { - if (err) { - return defer.reject(new SqlErr(`MySQL connect ${err}`)) - } - if (this.db) { - conn.query('USE ' + this.db, err => { - if (err) { - return defer.reject(new SqlErr('Select DB ' + err)) - } - defer.resolve(conn) - }) - } else { - defer.resolve(conn) - } - }) - return defer.promise - } - - listDb() { - return this.connect().then(conn => { - const defer = Promise.defer() - - conn.query('SHOW DATABASES', (err, row) => { - conn.release() - if (err) { - return defer.reject(new SqlErr('SHOW DATABASES ' + err)) - } - defer.resolve(row.map(it => it.Database)) - }) - - return defer.promise - }) - } - - //返回数据表 - listTable() { - return this.connect().then(conn => { - const defer = Promise.defer() - - conn.query('SHOW TABLES', (err, row) => { - conn.release() - if (err) { - return defer.reject(new SqlErr('SHOW TABLES ' + err)) - } - defer.resolve(row.map(it => Object.values(it)[0])) - }) - - return defer.promise - }) - } - - /** - * [query sql语句执行] - * @param {[type]} sql [sql语句] - */ - query(sql) { - if (typeof sql !== 'string') { - return Promise.reject( - new SqlErr( - `Query error, argument sql must be string. ${typeof sql} given`, - sql - ) - ) - } - - return this.connect().then(conn => { - const defer = Promise.defer() - - conn.query(sql, (err, result) => { - conn.release() - if (err) { - return defer.reject(new SqlErr(`Query ${err}`, sql)) - } - defer.resolve(result) - }) - return defer.promise - }) - } - - find(condition, select) { - const { table, leftJoin, rightJoin, join, where, sort, limit } = condition - if (!table) { - return Promise.reject(new SqlErr('Find Error: empty table')) - } - - let sql = parser.select(select) - sql += `FROM ${table} ` - if (leftJoin) { - sql += parser.leftJoin(leftJoin) - } - if (rightJoin) { - sql += parser.rightJoin(rightJoin) - } - if (join) { - sql += parser.join(join) - } - - if (where) { - sql += parser.where(where) - } - - if (sort) { - sql += parser.sort(sort) - } - - if (limit) { - sql += parser.limit(limit) - } - - return this.connect().then(conn => { - const defer = Promise.defer() - - conn.query(sql, (err, result) => { - conn.release() - if (err) { - return defer.reject(new SqlErr(`Find ${err}`, sql)) - } - defer.resolve(result) - }) - return defer.promise - }) - } - - findOne(condition, select) { - condition.limit = [1] - return this.find(condition, select).then(row => { - return row[0] || null - }) - } - - count(condition) { - delete condition.limit - return this.find(condition, ['count(*) AS total']).then(row => { - return (row[0] && row[0].total) || 0 - }) - } - - insert({ table }, doc) { - if (!table) { - return Promise.reject(new SqlErr('Insert Error: empty table')) - } - - let sql = `INSERT INTO ${table} ` - let keys = [] - let vals = [] - - for (let i in doc) { - keys.push(i) - vals.push(escape(doc[i])) - } - sql += `(${keys.join(',')}) VALUES (${vals.join(',')})` - - return this.connect().then(conn => { - const defer = Promise.defer() - - conn.query(sql, (err, result) => { - conn.release() - if (err) { - return defer.reject(new SqlErr(`Insert ${err}`, sql)) - } - - defer.resolve(result.insertId) - }) - - return defer.promise - }) - } - - update({ table, where }, doc) { - if (!table) { - return Promise.reject(new SqlErr('Update Error: empty table')) - } - - let sql = `UPDATE ${table} SET ` - - let fields = [] //要更新的字段 - for (let i in doc) { - let val = doc[i] - if (typeof val === 'object' && val.$sql) { - val = `(${val.$sql})` - } else { - val = escape(val) - } - fields.push(i + ' = ' + val) - } - sql += fields.join(',') - sql += parser.where(where) - - return this.connect().then(conn => { - const defer = Promise.defer() - conn.query(sql, (err, res) => { - conn.release() - if (err) { - return defer.reject(new SqlErr(`Update ${err}`, sql)) - } - - defer.resolve(res.affectedRows) - }) - return defer.promise - }) - } - - remove({ table, where }) { - if (!table) { - return Promise.reject(new SqlErr('Remove Error: empty table')) - } - - let sql = `DELETE FROM \`${table}\` ` - - if (where) { - sql += parser.where(where) - } - - return this.connect().then(conn => { - const defer = Promise.defer() - conn.query(sql, (err, res) => { - conn.release() - if (err) { - return defer.reject(new SqlErr(`Remove ${err}`, sql)) - } - - defer.resolve(res.affectedRows) - }) - return defer.promise - }) - } -} - -module.exports = Method diff --git a/package.json b/package.json index cf97fdb..0e28ccd 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,8 @@ { "name": "mysqli", - "version": "2.2.2", + "version": "3.0.0", "description": "MySQL tool", - "main": "index.js", + "main": "dist/index.js", "dependencies": { "es.shim": "^0.0.3", "mysql": "^2.13.0" diff --git a/index.js b/src/index.ts similarity index 70% rename from index.js rename to src/index.ts index ecca1f5..20abe14 100644 --- a/index.js +++ b/src/index.ts @@ -5,25 +5,17 @@ * */ 'use strict' -require('es.shim') +import 'es.shim' const mysql = require('mysql') -const Method = require('./lib/method') +import Api from './lib/api' -if (!Promise.defer) { - Promise.defer = function() { - let obj = {} - obj.promise = new this((yes, no) => { - obj.resolve = yes - obj.reject = no - }) - return obj - } -} class Mysqli { + useSlaveDB: boolean + pool: any /** * [constructor 构造数据库连接池] */ - constructor(config) { + constructor(config: any) { if (!Array.isArray(config)) { config = [config] } @@ -35,7 +27,7 @@ class Mysqli { restoreNodeTimeout: 10000 }) - config.forEach((item, i) => { + config.forEach((item: { [prop: string]: any }, i: number) => { let { host, port, @@ -44,7 +36,8 @@ class Mysqli { passwd: password, db: database, timezone, - supportBigNumbers + supportBigNumbers, + ...others } = item let name = i < 1 ? 'MASTER' : 'SLAVE' + i let collate @@ -65,20 +58,21 @@ class Mysqli { password, database, timezone, - supportBigNumbers + supportBigNumbers, + ...others }) }) return this } //对外的escape方法 - static escape(val) { + static escape(val: any) { return mysql.escape(val) } - emit(fromSlave = false, db) { - const slave = fromSlave && this.useSlaveDB ? 'SLAVE*' : 'MASTER' - return new Method(this.pool, slave, db) + emit(fromSlave = false, db: string = '') { + let slave = fromSlave && this.useSlaveDB ? 'SLAVE*' : 'MASTER' + return new Api(this.pool, slave, db) } } diff --git a/src/lib/api.ts b/src/lib/api.ts new file mode 100644 index 0000000..1cb2d27 --- /dev/null +++ b/src/lib/api.ts @@ -0,0 +1,123 @@ +/** + * + * @authors yutent (yutent@doui.cc) + * @date 2018-04-13 14:30:49 + * @version $Id$ + */ + +import { defer, SqlErr } from './utils' +import Method from './method' +interface Conn { + query(sql: string, cb?: any): void + release(): void +} +class Api { + pool: any + slave: string + db: string + constructor(pool: object, slave: string = 'MASTER', db: string = '') { + this.pool = pool + this.slave = slave + this.db = db + } + + connect() { + let out = defer() + this.pool.getConnection(this.slave, (err: Error, conn: Conn) => { + if (err) { + return out.reject(new SqlErr(`MySQL connect ${err}`)) + } + if (this.db) { + conn.query('USE ' + this.db, (err: Error) => { + if (err) { + return out.reject(new SqlErr('Select DB ' + err)) + } + out.resolve(conn) + }) + } else { + out.resolve(conn) + } + }) + return out.promise + } + + table(name: string) { + if (!name) { + throw new SqlErr('Query Error: empty table') + } + return new Method(this.pool, this.slave, this.db, name) + } + + /** + * [query sql语句执行] + * @param {[type]} sql [sql语句] + */ + query(sql: string) { + if (typeof sql !== 'string') { + return Promise.reject( + new SqlErr( + `Query error, argument sql must be string. ${typeof sql} given`, + sql + ) + ) + } + + return this.connect().then((conn: Conn) => { + let out = defer() + + conn.query(sql, (err: Error, result: any) => { + conn.release() + if (err) { + return out.reject(new SqlErr(`Query ${err}`, sql)) + } + out.resolve(result) + }) + return out.promise + }) + } + + drop() { + if (!this.db) { + return Promise.reject('') + } + + this.connect().then((conn: Conn) => { + conn.query('') + }) + } + + dbList() { + return this.connect().then((conn: Conn) => { + let out = defer() + + conn.query('SHOW DATABASES', (err: Error, row: any) => { + conn.release() + if (err) { + return out.reject(new SqlErr('SHOW DATABASES ' + err)) + } + out.resolve(row.map((it: any) => it.Database)) + }) + + return out.promise + }) + } + + //返回数据表 + tableList() { + return this.connect().then((conn: Conn) => { + const out = defer() + + conn.query('SHOW TABLES', (err: Error, row: any) => { + conn.release() + if (err) { + return out.reject(new SqlErr('SHOW TABLES ' + err)) + } + out.resolve(row.map((it: any) => it[Object.keys(it)[0]])) + }) + + return out.promise + }) + } +} + +export default Api diff --git a/src/lib/method.ts b/src/lib/method.ts new file mode 100644 index 0000000..aec3675 --- /dev/null +++ b/src/lib/method.ts @@ -0,0 +1,312 @@ +/** + * + * @authors yutent (yutent@doui.cc) + * @date 2017-12-14 14:01:03 + * @version $Id$ + */ + +import { defer, SqlErr, parser, escape } from './utils' + +console.log(escape(new Date())) +interface Conn { + query(sql: string, cb?: any): void + release(): void +} +class Method { + pool: any + slave: string + db: string + cache: { [prop: string]: any } = {} + + constructor(pool: object, slave: string, db: string, table: string) { + this.pool = pool + this.slave = slave + this.db = db + this.cache = { table } + } + + private connect() { + let out = defer() + this.pool.getConnection(this.slave, (err: Error, conn: Conn) => { + if (err) { + return out.reject(new SqlErr(`MySQL connect ${err}`)) + } + if (this.db) { + conn.query('USE ' + this.db, (err: Error) => { + if (err) { + return out.reject(new SqlErr('Select DB ' + err)) + } + out.resolve(conn) + }) + } else { + out.resolve(conn) + } + }) + return out.promise + } + + leftJoin(tables: any[]) { + this.cache.leftJoin = tables + return this + } + + rightJoin(tables: any[]) { + this.cache.rightJoin = tables + return this + } + + join(tables: any[]) { + this.cache.join = tables + return this + } + + /** + * [filter 过滤条件] + * @param {any} val [支持多种形式的过滤] + * sql: .filter('name like "foo%" and age > 18') + * func: .filter(function(){return 'name = "xiaoming"'}) + * obj: .filter({ + * name: {$like: 'foo%'} + * age: {$gt: 18} + * }) + * obj形式的过滤, 支持多种多样, 详细请看Readme介绍 + */ + filter(val: any) { + this.cache.filter = val + return this + } + + /** + * [sort 对记录按指定字段排序] + * @param {number }} keys [以对象形式传入值] + * 如: {name: 1, age: -1} 1代表顺序, -1代表逆序 + */ + sort(keys: { [prop: string]: number }) { + this.cache.sort = keys + return this + } + + // 从第几条记录开始返回, 必须搭配limit使用,否则会被忽略 + skip(skip: number) { + this.cache.skip = skip + return this + } + + // 返回指定数量的记录 + limit(size: number) { + this.cache.size = size + return this + } + + // 截取指定范围内的记录 + slice(start: number, end: number) { + this.cache.limit = [start, end - start] + return this + } + + /** + * [withFields 选取指定的字段] + * @param {string[]} fields [以数组形式传入] + */ + withFields(fields: string[]) { + this.cache.fields = fields + return this + } + + // ================================================================ + // ====================== 以下方法,才是sql执行 ======================= + // ================================================================ + + /** + * [getAll 获取所有记录] + * @param {any[]} ids [description] + */ + getAll(ids?: any[]) { + if (!this.cache.filter && ids) { + this.cache.filter = { id: { $in: ids } } + } + + let { + table, + leftJoin, + rightJoin, + join, + filter, + fields, + sort, + skip, + size, + limit + } = this.cache + + // 没有使用 slice方法的前提下, 通过skip/limit补全 + if (!limit) { + if (size && size > 0) { + limit = [size] + if (skip !== undefined) { + limit.unshift(skip) + } + } + } + + let sql: string + sql = parser.select(fields) + sql += `FROM ${table} ` + if (leftJoin) { + sql += parser.leftJoin(leftJoin) + } + if (rightJoin) { + sql += parser.rightJoin(rightJoin) + } + if (join) { + sql += parser.join(join) + } + + if (filter) { + sql += parser.filter(filter) + } + + if (sort) { + sql += parser.sort(sort) + } + + if (limit) { + sql += parser.limit(limit) + } + + return this.connect().then((conn: Conn) => { + let out = defer() + + conn.query(sql, (err: Error, result: any[]) => { + conn.release() + if (err) { + return out.reject(new SqlErr(`Find ${err}`, sql)) + } + out.resolve(result) + }) + return out.promise + }) + } + + /** + * [get 获取单条记录详细] + * @param {any} id [取主键值为id的记录, 当且仅当没设置过滤条件时有效] + */ + get(id?: any) { + return this.getAll(id ? [id] : null).then((list: any[]) => { + return list[0] + }) + } + + /** + * [count 获取记录总数] + * @return {number} [description] + */ + count(): number { + return this.getAll().then((list: any[]) => { + return list.length + }) + } + + /** + * [insert 插入单条文档, 返回当前插入的文档的ID(如果是自增)] + * @param {any }} doc [文档object] + */ + insert(doc: { [prop: string]: any }) { + if (!doc) { + return Promise.reject(new SqlErr('Insert Error: empty document')) + } + let { table } = this.cache + let sql = `INSERT INTO ${table} ` + let keys = [] + let vals = [] + + for (let i in doc) { + keys.push(i) + vals.push(escape(doc[i])) + } + sql += `(${keys.join(',')}) VALUES (${vals.join(',')})` + + return this.connect().then((conn: Conn) => { + const out = defer() + + conn.query(sql, (err: Error, result: any) => { + conn.release() + if (err) { + return out.reject(new SqlErr(`Insert ${err}`, sql)) + } + + out.resolve(result.insertId) + }) + + return out.promise + }) + } + + /** + * [update 更新文档, 返回更新成功的文档数量] + * 可以使用filter过滤条件 + * @param {any }} doc [要更新的字段] + */ + update(doc: { [prop: string]: any }) { + if (!doc) { + return Promise.reject(new SqlErr('Update Error: empty document')) + } + let { table, filter } = this.cache + let sql = `UPDATE ${table} SET ` + let fields = [] //要更新的字段 + for (let i in doc) { + let val = doc[i] + if (typeof val === 'object' && val.$sql) { + val = `(${val.$sql})` + } else { + val = escape(val) + } + fields.push(i + ' = ' + val) + } + sql += fields.join(',') + sql += parser.filter(filter) + + return this.connect().then((conn: Conn) => { + const out = defer() + + conn.query(sql, (err: Error, result: any) => { + conn.release() + if (err) { + return out.reject(new SqlErr(`Update ${err}`, sql)) + } + + out.resolve(result.affectedRows) + }) + + return out.promise + }) + } + + /** + * [remove 删除文档, 返回删除成功的文档数量] + * 可以使用filter过滤条件 + */ + remove() { + let { table, filter } = this.cache + let sql = `DELETE FROM ${table} ` + sql += parser.filter(filter) + + return this.connect().then((conn: Conn) => { + const out = defer() + + conn.query(sql, (err: Error, result: any) => { + conn.release() + if (err) { + return out.reject(new SqlErr(`Remove ${err}`, sql)) + } + + out.resolve(result.affectedRows) + }) + + return out.promise + }) + } +} + +export default Method diff --git a/lib/parser.js b/src/lib/utils.ts similarity index 78% rename from lib/parser.js rename to src/lib/utils.ts index 0d0eac9..d250ce8 100644 --- a/lib/parser.js +++ b/src/lib/utils.ts @@ -6,7 +6,7 @@ */ const { escape } = require('mysql') -function getType(val) { +function getType(val: any): string { if (val === null) { return String(val) } @@ -15,7 +15,7 @@ function getType(val) { .slice(8, -1) .toLowerCase() } -function parse$or(arr) { +function parse$or(arr: any[]) { let sql = '' for (let it of arr) { sql += '(' @@ -29,7 +29,7 @@ function parse$or(arr) { sql = sql.slice(0, -3) return sql } -function parse$and(arr) { +function parse$and(arr: any[]) { let sql = '' for (let it of arr) { sql += '(' @@ -44,7 +44,7 @@ function parse$and(arr) { return sql } -function parse$opt(opt) { +function parse$opt(opt: { [prop: string]: any }) { let sql = '' for (let k in opt) { let tmp = opt[k] @@ -60,7 +60,7 @@ function parse$opt(opt) { } if (tmp.$in) { - let list = tmp.$in.map(it => { + let list = tmp.$in.map((it: any) => { return escape(it) }) sql += ` ${k} IN (${list.join(',')}) ` @@ -70,7 +70,7 @@ function parse$opt(opt) { if (tmp.$between.length < 2) { throw new Error(`Array $between's length must be 2.`) } - let list = tmp.$between.map(it => { + let list = tmp.$between.map((it: any) => { return escape(it) }) sql += ` ${k} BETWEEN ${list[0]} AND ${list[1]} ` @@ -102,8 +102,8 @@ function parse$opt(opt) { return sql } -const Parser = { - leftJoin(tables) { +export const parser = { + leftJoin(tables: any[]) { let sql = '' for (let it of tables) { sql += ` LEFT JOIN ${it.table} ON ${it.on} ` @@ -111,7 +111,7 @@ const Parser = { return sql }, - rightJoin(tables) { + rightJoin(tables: any[]) { let sql = '' for (let it of tables) { sql += ` RIGHT JOIN ${it.table} ON ${it.on} ` @@ -119,7 +119,7 @@ const Parser = { return sql }, - join(tables) { + join(tables: any[]) { let sql = '' for (let it of tables) { sql += ` JOIN ${it[0]} ON ${it.on} ` @@ -127,7 +127,7 @@ const Parser = { return sql }, - where(opt) { + filter(opt: any) { if (typeof opt === 'string') { return ` WHERE ${opt} ` } @@ -146,12 +146,12 @@ const Parser = { return ' ' }, - select(arr = ['*']) { + select(arr: string[] = ['*']) { return `SELECT ${arr.join(',')} ` }, // 排序 ---------------------------------- - sort(obj = {}) { + sort(obj: { [propName: string]: number } = {}) { let sort = '' for (let i in obj) { let c = '' @@ -167,9 +167,28 @@ const Parser = { } }, - limit(...args) { + limit(...args: number[]) { return ` LIMIT ${args.join(',')} ` } } -module.exports = Parser +export class SqlErr extends Error { + sql: string + constructor(msg: string = '', sql: string = '') { + super(msg) + this.sql = sql + } +} + +export const defer = () => { + let obj: { [prop: string]: any } = {} + obj.promise = new Promise((yes, no) => { + obj.resolve = yes + obj.reject = no + }) + return obj +} + +export { escape } + +export default escape diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..6437eab --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "target": "es2015", + "lib": ["es2015"], + "module": "commonjs", + "outDir": "dist/", + "noImplicitAny": true, + "removeComments": false, + "preserveConstEnums": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "pretty": true, + "types" : ["node"], + "noEmitOnError": true, + + }, + "include": ["src/**/*"], + +} \ No newline at end of file