diff --git a/.eslintrc.json b/.eslintrc.json index d187d17c..4d4b2f52 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,4 +1,15 @@ { "root": true, - "extends": "hexo" -} + "extends": "hexo/ts.js", + "parserOptions": { + "sourceType": "module", + "ecmaVersion": 2020 + }, + "rules": { + "@typescript-eslint/no-unused-vars": 0, + "@typescript-eslint/no-explicit-any": 0, + "@typescript-eslint/no-var-requires": 0, + "@typescript-eslint/ban-ts-comment": 0, + "no-use-before-define": 0 + } +} \ No newline at end of file diff --git a/.gitignore b/.gitignore index 62df31e9..476e8d29 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,5 @@ node_modules/ .nyc_output/ *.log docs/ -coverage/ \ No newline at end of file +coverage/ +dist \ No newline at end of file diff --git a/lib/types/index.js b/lib/types/index.js deleted file mode 100644 index d2bb02f6..00000000 --- a/lib/types/index.js +++ /dev/null @@ -1,14 +0,0 @@ -'use strict'; - -exports.Mixed = require('../schematype'); -exports.String = require('./string'); -exports.Number = require('./number'); -exports.Boolean = require('./boolean'); -exports.Array = require('./array'); -exports.Object = require('./object'); -exports.Date = require('./date'); -exports.Virtual = require('./virtual'); -exports.CUID = require('./cuid'); -exports.Enum = require('./enum'); -exports.Integer = require('./integer'); -exports.Buffer = require('./buffer'); diff --git a/package.json b/package.json index 1da22e1f..e8a298df 100644 --- a/package.json +++ b/package.json @@ -2,18 +2,21 @@ "name": "warehouse", "version": "4.0.1", "description": "Simple JSON-based database", - "main": "lib/database", + "main": "dist/database", "directories": { - "lib": "./lib" + "lib": "./dist" }, "files": [ - "lib/" + "dist/" ], "scripts": { - "eslint": "eslint lib test", - "test": "mocha \"test/scripts/**/*.js\"", + "pretest": "npm run typescript", + "typescript": "tsc --build tsconfig.json", + "eslint": "eslint src test", + "test": "mocha -r ts-node/register 'test/scripts/**/*.ts'", "test-cov": "nyc --reporter=lcovonly --reporter=text-summary npm test", - "jsdoc": "jsdoc --configure .jsdoc.json" + "jsdoc": "jsdoc --configure .jsdoc.json", + "prepublish": "npm run typescript" }, "repository": "hexojs/warehouse", "keywords": [ @@ -24,26 +27,38 @@ "author": "Tommy Chen (http://zespia.tw)", "license": "MIT", "dependencies": { - "bluebird": "^3.2.2", - "cuid": "^2.1.4", - "graceful-fs": "^4.1.3", + "@types/through2": "^2.0.36", + "bluebird": "^3.7.2", + "cuid": "^2.1.8", + "graceful-fs": "^4.2.10", "hexo-log": "^3.0.0", "is-plain-object": "^5.0.0", "jsonparse": "^1.3.1", - "rfdc": "^1.1.4", + "rfdc": "^1.3.0", "through2": "^4.0.2" }, "devDependencies": { - "chai": "^4.2.0", + "@types/bluebird": "^3.5.36", + "@types/chai": "^4.3.1", + "@types/graceful-fs": "^4.1.5", + "@types/jsonstream": "^0.8.30", + "@types/mocha": "^9.1.1", + "@types/node": "^18.0.0", + "@types/sinon": "^10.0.11", + "@typescript-eslint/eslint-plugin": "^5.29.0", + "@typescript-eslint/parser": "^5.29.0", + "chai": "^4.3.6", "chai-as-promised": "^7.1.1", - "eslint": "^8.1.0", + "eslint": "^8.18.0", "eslint-config-hexo": "^5.0.0", - "jsdoc": "^3.4.0", - "lodash": "^4.17.15", - "minami": "^1.1.1", + "jsdoc": "^3.6.10", + "lodash": "^4.17.21", + "minami": "^1.2.3", "mocha": "^10.0.0", "nyc": "^15.0.0", - "sinon": "^14.0.0" + "sinon": "^14.0.0", + "ts-node": "^10.8.1", + "typescript": "^4.7.4" }, "engines": { "node": ">=14" diff --git a/lib/database.js b/src/database.ts similarity index 70% rename from lib/database.js rename to src/database.ts index 98a873d1..6e4edf80 100644 --- a/lib/database.js +++ b/src/database.ts @@ -1,20 +1,20 @@ -'use strict'; - -const { parse: createJsonParseStream } = require('./jsonstream'); -const Promise = require('bluebird'); -const fs = require('graceful-fs'); -const Model = require('./model'); -const Schema = require('./schema'); -const SchemaType = require('./schematype'); -const WarehouseError = require('./error'); +import { parse as createJsonParseStream } from './lib/jsonstream'; +import Bluebird from 'bluebird'; +import { writev, promises as fsPromises, createReadStream } from 'graceful-fs'; +import { pipeline, Stream } from 'stream'; +import Model from './model'; +import Schema from './schema'; +import SchemaType from './schematype'; +import WarehouseError from './error'; +import log from 'hexo-log'; + const pkg = require('../package.json'); -const { open } = fs.promises; -const pipeline = Promise.promisify(require('stream').pipeline); -const log = require('hexo-log')(); +const { open } = fsPromises; +const pipelineAsync = Bluebird.promisify(pipeline) as (...args: Stream[]) => Bluebird; -let _writev; +let _writev: (handle: fsPromises.FileHandle, buffers: Buffer[]) => Promise; -if (typeof fs.writev === 'function') { +if (typeof writev === 'function') { _writev = (handle, buffers) => handle.writev(buffers); } else { _writev = async (handle, buffers) => { @@ -22,7 +22,7 @@ if (typeof fs.writev === 'function') { }; } -async function exportAsync(database, path) { +async function exportAsync(database: Database, path: string) { const handle = await open(path, 'w'); try { @@ -69,7 +69,17 @@ async function exportAsync(database, path) { } } +interface DatabaseOptions { + version: number, + path: string, + onUpgrade: (...args: any[]) => any, + onDowngrade: (...args: any[]) => any +} + class Database { + options: DatabaseOptions; + _models: any; + Model: typeof Model; /** * Database constructor. @@ -80,13 +90,15 @@ class Database { * @param {function} [options.onUpgrade] Triggered when the database is upgraded * @param {function} [options.onDowngrade] Triggered when the database is downgraded */ - constructor(options) { - this.options = Object.assign({ + constructor(options?: { path: string } & Partial) { + this.options = { version: 0, + // eslint-disable-next-line @typescript-eslint/no-empty-function onUpgrade() {}, - - onDowngrade() {} - }, options); + // eslint-disable-next-line @typescript-eslint/no-empty-function + onDowngrade() {}, + ...options + }; this._models = {}; @@ -104,7 +116,7 @@ class Database { * @param {Schema|object} [schema] * @return {Model} */ - model(name, schema) { + model(name: string, schema?: any) { if (this._models[name]) { return this._models[name]; } @@ -120,7 +132,7 @@ class Database { * @param {function} [callback] * @return {Promise} */ - load(callback) { + load(callback?) { const { path, onUpgrade, onDowngrade, version: newVersion } = this.options; if (!path) throw new WarehouseError('options.path is required'); @@ -143,9 +155,9 @@ class Database { this.model(data.key)._import(data.value); }); - const rs = fs.createReadStream(path, 'utf8'); + const rs = createReadStream(path, 'utf8'); - return pipeline(rs, parseStream).then(() => { + return pipelineAsync(rs, parseStream).then(() => { if (newVersion > oldVersion) { return onUpgrade(oldVersion, newVersion); } else if (newVersion < oldVersion) { @@ -160,11 +172,11 @@ class Database { * @param {function} [callback] * @return {Promise} */ - save(callback) { + save(callback?) { const { path } = this.options; if (!path) throw new WarehouseError('options.path is required'); - return Promise.resolve(exportAsync(this, path)).asCallback(callback); + return Bluebird.resolve(exportAsync(this, path)).asCallback(callback); } toJSON() { @@ -182,12 +194,15 @@ class Database { }, models }; } + static Schema = Schema; + Schema: typeof Schema; + static SchemaType = SchemaType; + SchemaType: typeof SchemaType; + static version: number; } Database.prototype.Schema = Schema; -Database.Schema = Database.prototype.Schema; Database.prototype.SchemaType = SchemaType; -Database.SchemaType = Database.prototype.SchemaType; Database.version = pkg.version; -module.exports = Database; +export default Database; diff --git a/lib/document.js b/src/document.ts similarity index 92% rename from lib/document.js rename to src/document.ts index fd8f672b..2c23b2e5 100644 --- a/lib/document.js +++ b/src/document.ts @@ -1,8 +1,10 @@ -'use strict'; +import rfdc from 'rfdc'; +const cloneDeep = rfdc(); -const cloneDeep = require('rfdc')(); - -class Document { +abstract class Document { + abstract _model; + _id!: any; + abstract _schema; /** * Document constructor. @@ -101,4 +103,4 @@ function isGetter(obj, key) { return Object.getOwnPropertyDescriptor(obj, key).get; } -module.exports = Document; +export default Document; diff --git a/lib/error.js b/src/error.ts similarity index 56% rename from lib/error.js rename to src/error.ts index 1e097423..23d4f41e 100644 --- a/lib/error.js +++ b/src/error.ts @@ -1,6 +1,5 @@ -'use strict'; - class WarehouseError extends Error { + code?: string; /** * WarehouseError constructor @@ -8,18 +7,18 @@ class WarehouseError extends Error { * @param {string} msg * @param {string} code */ - constructor(msg, code) { + constructor(msg: string, code?: string) { super(msg); Error.captureStackTrace(this); this.code = code; } + static ID_EXIST = 'ID_EXIST'; + static ID_NOT_EXIST = 'ID_NOT_EXIST'; + static ID_UNDEFINED = 'ID_UNDEFINED'; } WarehouseError.prototype.name = 'WarehouseError'; -WarehouseError.ID_EXIST = 'ID_EXIST'; -WarehouseError.ID_NOT_EXIST = 'ID_NOT_EXIST'; -WarehouseError.ID_UNDEFINED = 'ID_UNDEFINED'; -module.exports = WarehouseError; +export default WarehouseError; diff --git a/lib/error/population.js b/src/error/population.ts similarity index 52% rename from lib/error/population.js rename to src/error/population.ts index 6e72e928..66e864a0 100644 --- a/lib/error/population.js +++ b/src/error/population.ts @@ -1,9 +1,7 @@ -'use strict'; - -const WarehouseError = require('../error'); +import WarehouseError from '../error'; class PopulationError extends WarehouseError {} PopulationError.prototype.name = 'PopulationError'; -module.exports = PopulationError; +export default PopulationError; diff --git a/lib/error/validation.js b/src/error/validation.ts similarity index 52% rename from lib/error/validation.js rename to src/error/validation.ts index f3f02efe..f76e1fc1 100644 --- a/lib/error/validation.js +++ b/src/error/validation.ts @@ -1,9 +1,7 @@ -'use strict'; - -const WarehouseError = require('../error'); +import WarehouseError from '../error'; class ValidationError extends WarehouseError {} ValidationError.prototype.name = 'ValidationError'; -module.exports = ValidationError; +export default ValidationError; diff --git a/lib/jsonstream/index.js b/src/lib/jsonstream/index.ts similarity index 95% rename from lib/jsonstream/index.js rename to src/lib/jsonstream/index.ts index 7a6d26bd..e028660b 100644 --- a/lib/jsonstream/index.js +++ b/src/lib/jsonstream/index.ts @@ -1,7 +1,5 @@ -'use strict'; - -const through2 = require('through2'); -const Parser = require('jsonparse'); +import through2 from 'through2'; +import Parser from 'jsonparse'; /** * Check whether a x and y are equal, or x matches y, or x(y) is truthy. @@ -29,7 +27,7 @@ const check = (x, y) => { return false; }; -module.exports.parse = function(path, map) { +export function parse(path, map = null) { let header, footer; const parser = new Parser(); @@ -50,6 +48,7 @@ module.exports.parse = function(path, map) { } if (parser.tState !== Parser.C.START || parser.stack.length > 0) { + // @ts-ignore cb(new Error('Incomplete JSON')); return; } @@ -80,6 +79,7 @@ module.exports.parse = function(path, map) { } parser.onValue = function(value) { + // @ts-ignore if (!this.root) { stream.root = value; } if (!path) return; @@ -169,9 +169,12 @@ module.exports.parse = function(path, map) { parser.onToken = function(token, value) { parser._onToken(token, value); if (this.stack.length === 0) { + // @ts-ignore if (stream.root) { + // @ts-ignore if (!path) { stream.push(stream.root); } + // @ts-ignore stream.root = null; } } @@ -199,4 +202,4 @@ module.exports.parse = function(path, map) { footer[key] = value; } } -}; +} diff --git a/lib/model.js b/src/model.ts similarity index 92% rename from lib/model.js rename to src/model.ts index 0e19c4af..ea3e4ffa 100644 --- a/lib/model.js +++ b/src/model.ts @@ -1,18 +1,24 @@ -'use strict'; - -const { EventEmitter } = require('events'); -const cloneDeep = require('rfdc')(); -const Promise = require('bluebird'); -const { parseArgs, getProp, setGetter, shuffle } = require('./util'); -const Document = require('./document'); -const Query = require('./query'); -const Schema = require('./schema'); -const Types = require('./types'); -const WarehouseError = require('./error'); -const PopulationError = require('./error/population'); -const Mutex = require('./mutex'); +import { EventEmitter } from 'events'; +import rfdc from 'rfdc'; +const cloneDeep = rfdc(); +import Promise from 'bluebird'; +import { parseArgs, getProp, setGetter, shuffle } from './util'; +import Document from './document'; +import Query from './query'; +import Schema from './schema'; +import * as Types from './types/index'; +import WarehouseError from './error'; +import PopulationError from './error/population'; +import Mutex from './mutex'; class Model extends EventEmitter { + _mutex = new Mutex(); + data: Record = {}; + schema; + length = 0; + Document; + Query; + _database; /** * Model constructor. @@ -20,7 +26,7 @@ class Model extends EventEmitter { * @param {string} name Model name * @param {Schema|object} [schema] Schema */ - constructor(name, schema_) { + constructor(public name: string, schema_) { super(); let schema; @@ -39,13 +45,11 @@ class Model extends EventEmitter { schema.path('_id', {type: Types.CUID, required: true}); } - this.name = name; - this.data = {}; - this._mutex = new Mutex(); this.schema = schema; - this.length = 0; class _Document extends Document { + _model!: Model; + _schema!: Schema; constructor(data) { super(data); @@ -59,7 +63,10 @@ class Model extends EventEmitter { _Document.prototype._model = this; _Document.prototype._schema = schema; - class _Query extends Query {} + class _Query extends Query { + _model!: Model; + _schema!: Schema; + } this.Query = _Query; @@ -91,7 +98,7 @@ class Model extends EventEmitter { * @param {boolean} [options.lean=false] Returns a plain JavaScript object * @return {Document|object} */ - findById(id, options_) { + findById(id, options_?) { const raw = this.data[id]; if (!raw) return; @@ -116,11 +123,10 @@ class Model extends EventEmitter { /** * Acquires write lock. * - * @param {*} id * @return {Promise} * @private */ - _acquireWriteLock(id) { + _acquireWriteLock(): Promise.Disposer { const mutex = this._mutex; return new Promise((resolve, reject) => { @@ -175,7 +181,7 @@ class Model extends EventEmitter { * @param {function} [callback] * @return {Promise} */ - insertOne(data, callback) { + insertOne(data, callback?) { return Promise.using(this._acquireWriteLock(), () => this._insertOne(data)).asCallback(callback); } @@ -223,7 +229,7 @@ class Model extends EventEmitter { * @return {Promise} * @private */ - _updateWithStack(id, stack) { + _updateWithStack(id, stack): Promise { const schema = this.schema; const data = this.data[id]; @@ -265,7 +271,7 @@ class Model extends EventEmitter { * @param {function} [callback] * @return {Promise} */ - updateById(id, update, callback) { + updateById(id, update, callback?) { return Promise.using(this._acquireWriteLock(), () => { const stack = this.schema._parseUpdate(update); return this._updateWithStack(id, stack); @@ -280,7 +286,7 @@ class Model extends EventEmitter { * @param {function} [callback] * @return {Promise} */ - update(query, data, callback) { + update(query, data, callback?) { return this.find(query).update(data, callback); } @@ -326,7 +332,7 @@ class Model extends EventEmitter { * @param {function} [callback] * @return {Promise} */ - replaceById(id, data, callback) { + replaceById(id, data, callback?) { return Promise.using(this._acquireWriteLock(), () => this._replaceById(id, data)).asCallback(callback); } @@ -338,7 +344,7 @@ class Model extends EventEmitter { * @param {function} [callback] * @return {Promise} */ - replace(query, data, callback) { + replace(query, data, callback?) { return this.find(query).replace(data, callback); } @@ -414,7 +420,7 @@ class Model extends EventEmitter { * @param {function} iterator * @param {object} [options] See {@link Model#findById}. */ - forEach(iterator, options) { + forEach(iterator, options?) { const keys = Object.keys(this.data); let num = 0; @@ -430,7 +436,7 @@ class Model extends EventEmitter { * @param {Object} [options] See {@link Model#findById}. * @return {Array} */ - toArray(options) { + toArray(options?) { const result = new Array(this.length); this.forEach((item, i) => { @@ -450,8 +456,7 @@ class Model extends EventEmitter { * @param {Boolean} [options.lean=false] Returns a plain JavaScript object. * @return {Query|Array} */ - find(query, options_) { - const options = options_ || {}; + find(query, options: { limit?: number; skip?: number; lean?: boolean; } = {}) { const filter = this.schema._execQuery(query); const keys = Object.keys(this.data); const len = keys.length; @@ -486,9 +491,8 @@ class Model extends EventEmitter { * @param {Boolean} [options.lean=false] Returns a plain JavaScript object. * @return {Document|Object} */ - findOne(query, options_) { - const options = options_ || {}; - options.limit = 1; + findOne(query, options_ : { skip?: number; lean?: boolean; } = {}) { + const options = Object.assign(options_, { limit: 1 }); const result = this.find(query, options); return options.lean ? result[0] : result.data[0]; @@ -516,7 +520,7 @@ class Model extends EventEmitter { * @param {Object} [options] See {@link Model#findById}. * @return {Document|Object} */ - eq(i_, options) { + eq(i_, options?) { let index = i_ < 0 ? this.length + i_ : i_; const data = this.data; const keys = Object.keys(data); @@ -562,7 +566,7 @@ class Model extends EventEmitter { * @param {Number} [end] * @return {Query} */ - slice(start_, end_) { + slice(start_: number, end_?: number) { const total = this.length; let start = start_ | 0; @@ -900,7 +904,7 @@ class Model extends EventEmitter { arr[i] = this._populate(item, stack); }); - return new Query(arr); + return new this.Query(arr); } /** @@ -946,12 +950,16 @@ class Model extends EventEmitter { } return result; } + get: Model['findById']; + size: Model['count']; + each: Model['forEach']; + random: Model['shuffle']; } Model.prototype.get = Model.prototype.findById; function execHooks(schema, type, event, data) { - const hooks = schema.hooks[type][event]; + const hooks = schema.hooks[type][event] as ((data: any) => Promise | void)[]; if (!hooks.length) return Promise.resolve(data); return Promise.each(hooks, hook => hook(data)).thenReturn(data); @@ -963,4 +971,4 @@ Model.prototype.each = Model.prototype.forEach; Model.prototype.random = Model.prototype.shuffle; -module.exports = Model; +export default Model; diff --git a/lib/mutex.js b/src/mutex.ts similarity index 75% rename from lib/mutex.js rename to src/mutex.ts index 87725b34..6eaa0ab4 100644 --- a/lib/mutex.js +++ b/src/mutex.ts @@ -1,12 +1,12 @@ -'use strict'; - class Mutex { + private _locked: boolean; + private readonly _queue: (() => void)[]; constructor() { this._locked = false; this._queue = []; } - lock(fn) { + lock(fn: () => void) { if (this._locked) { this._queue.push(fn); return; @@ -29,4 +29,4 @@ class Mutex { } } -module.exports = Mutex; +export default Mutex; diff --git a/lib/query.js b/src/query.ts similarity index 78% rename from lib/query.js rename to src/query.ts index 53b94881..0eb8fae4 100644 --- a/lib/query.js +++ b/src/query.ts @@ -1,17 +1,17 @@ -'use strict'; +import Promise from 'bluebird'; +import { parseArgs, shuffle } from './util'; -const Promise = require('bluebird'); -const { parseArgs, shuffle } = require('./util'); - -class Query { +abstract class Query { + length: number; + abstract _model; + abstract _schema; /** * Query constructor. * * @param {Array} data */ - constructor(data) { - this.data = data; + constructor(private data: any[]) { this.length = data.length; } @@ -20,7 +20,7 @@ class Query { * * @return Number */ - count() { + count(): number { return this.length; } @@ -29,7 +29,7 @@ class Query { * * @param {Function} iterator */ - forEach(iterator) { + forEach(iterator: (item: any, index: number) => void): void { const { data, length } = this; for (let i = 0; i < length; i++) { @@ -42,7 +42,7 @@ class Query { * * @return {Array} */ - toArray() { + toArray(): any[] { return this.data; } @@ -53,7 +53,7 @@ class Query { * @param {Number} i * @return {Document|Object} */ - eq(i) { + eq(i: number) { const index = i < 0 ? this.length + i : i; return this.data[index]; } @@ -83,8 +83,8 @@ class Query { * @param {Number} [end] * @return {Query} */ - slice(start, end) { - return new this.constructor(this.data.slice(start, end)); + slice(start: number, end?: number): Query { + return Reflect.construct(this.constructor, [this.data.slice(start, end)]); } /** @@ -93,7 +93,7 @@ class Query { * @param {Number} i * @return {Query} */ - limit(i) { + limit(i: number): Query { return this.slice(0, i); } @@ -103,7 +103,7 @@ class Query { * @param {Number} i * @return {Query} */ - skip(i) { + skip(i: number): Query { return this.slice(i); } @@ -112,8 +112,8 @@ class Query { * * @return {Query} */ - reverse() { - return new this.constructor(this.data.slice().reverse()); + reverse(): Query { + return Reflect.construct(this.constructor, [this.data.slice().reverse()]); } /** @@ -121,8 +121,8 @@ class Query { * * @return {Query} */ - shuffle() { - return new this.constructor(shuffle(this.data)); + shuffle(): Query { + return Reflect.construct(this.constructor, [shuffle(this.data)]); } /** @@ -135,7 +135,7 @@ class Query { * @param {Boolean} [options.lean=false] Returns a plain JavaScript object. * @return {Query|Array} */ - find(query, options = {}) { + find(query: any, options: { limit?: number; skip?: number; lean?: boolean; } = {}): any[] | Query { const filter = this._schema._execQuery(query); const { data, length } = this; const { lean = false } = options; @@ -155,7 +155,7 @@ class Query { } } - return lean ? arr : new this.constructor(arr); + return lean ? arr : Reflect.construct(this.constructor, [arr]); } /** @@ -167,11 +167,11 @@ class Query { * @param {Boolean} [options.lean=false] Returns a plain JavaScript object. * @return {Document|Object} */ - findOne(query, options = {}) { - options.limit = 1; + findOne(query: any, options: { skip?: number; lean?: boolean; } = {}): any { + const _options = Object.assign(options, { limit: 1 }); - const result = this.find(query, options); - return options.lean ? result[0] : result.data[0]; + const result = this.find(query, _options); + return Array.isArray(result) ? result[0] : result.data[0]; } /** @@ -192,11 +192,11 @@ class Query { * @param {String|Number} [order] * @return {Query} */ - sort(orderby, order) { + sort(orderby, order): Query { const sort = parseArgs(orderby, order); const fn = this._schema._execSort(sort); - return new this.constructor(this.data.slice().sort(fn)); + return Reflect.construct(this.constructor, [this.data.slice().sort(fn)]); } /** @@ -205,9 +205,9 @@ class Query { * @param {Function} iterator * @return {Array} */ - map(iterator) { + map(iterator: (item: any, index: number) => T): T[] { const { data, length } = this; - const result = new Array(length); + const result: T[] = new Array(length); for (let i = 0; i < length; i++) { result[i] = iterator(data[i], i); @@ -224,7 +224,7 @@ class Query { * @param {*} [initial] By default, the initial value is the first document. * @return {*} */ - reduce(iterator, initial) { + reduce(iterator, initial?) { const { data, length } = this; let result, i; @@ -251,7 +251,7 @@ class Query { * @param {*} [initial] By default, the initial value is the last document. * @return {*} */ - reduceRight(iterator, initial) { + reduceRight(iterator, initial?) { const { data, length } = this; let result, i; @@ -277,7 +277,7 @@ class Query { * @param {Function} iterator * @return {Query} */ - filter(iterator) { + filter(iterator: (item: any, index: number) => boolean): Query { const { data, length } = this; const arr = []; @@ -286,7 +286,7 @@ class Query { if (iterator(item, i)) arr.push(item); } - return new this.constructor(arr); + return Reflect.construct(this.constructor, [arr]); } /** @@ -296,7 +296,7 @@ class Query { * @param {Function} iterator * @return {Boolean} */ - every(iterator) { + every(iterator: (item: any, index: number) => boolean): boolean { const { data, length } = this; for (let i = 0; i < length; i++) { @@ -313,7 +313,7 @@ class Query { * @param {Function} iterator * @return {Boolean} */ - some(iterator) { + some(iterator: (item: any, index: number) => boolean): boolean { const { data, length } = this; for (let i = 0; i < length; i++) { @@ -330,7 +330,7 @@ class Query { * @param {Function} [callback] * @return {Promise} */ - update(data, callback) { + update(data: any, callback?: (err?: any) => void): Promise { const model = this._model; const stack = this._schema._parseUpdate(data); @@ -344,7 +344,7 @@ class Query { * @param {Function} [callback] * @return {Promise} */ - replace(data, callback) { + replace(data: any, callback?: (err?: any) => void): Promise { const model = this._model; return Promise.map(this.data, item => model.replaceById(item._id, data)).asCallback(callback); @@ -368,7 +368,7 @@ class Query { * @param {String|Object} expr * @return {Query} */ - populate(expr) { + populate(expr: any): Query { const stack = this._schema._parsePopulate(expr); const { data, length } = this; const model = this._model; @@ -379,6 +379,10 @@ class Query { return this; } + + size: Query['count']; + each: Query['forEach']; + random: Query['shuffle']; } Query.prototype.size = Query.prototype.count; @@ -387,4 +391,4 @@ Query.prototype.each = Query.prototype.forEach; Query.prototype.random = Query.prototype.shuffle; -module.exports = Query; +export default Query; diff --git a/lib/schema.js b/src/schema.ts similarity index 90% rename from lib/schema.js rename to src/schema.ts index a683d8d5..f9f81590 100644 --- a/lib/schema.js +++ b/src/schema.ts @@ -1,11 +1,10 @@ -'use strict'; - -const SchemaType = require('./schematype'); -const Types = require('./types'); -const Promise = require('bluebird'); -const { getProp, setProp, delProp } = require('./util'); -const PopulationError = require('./error/population'); -const { isPlainObject } = require('is-plain-object'); +import SchemaType from './schematype'; +import * as Types from './types/index'; +import Promise from 'bluebird'; +import { getProp, setProp, delProp } from './util'; +import PopulationError from './error/population'; +import SchemaTypeVirtual from './types/virtual'; +import { isPlainObject } from 'is-plain-object'; /** * @callback queryFilterCallback @@ -101,9 +100,8 @@ class UpdateParser { }; } - constructor(paths) { - this.paths = paths; - } + // eslint-disable-next-line no-useless-constructor + constructor(private paths) {} /** * Parses updating expressions and returns a stack. @@ -159,9 +157,8 @@ class UpdateParser { * @private */ class QueryParser { - constructor(paths) { - this.paths = paths; - } + // eslint-disable-next-line no-useless-constructor + constructor(private paths) {} /** * @@ -384,17 +381,18 @@ class QueryParser { } class Schema { + paths: Record> = {}; + statics: Record = {}; + methods: Record = {}; + hooks; + stacks; /** * Schema constructor. * - * @param {Object} schema + * @param {Object} [schema] */ - constructor(schema) { - this.paths = {}; - this.statics = {}; - this.methods = {}; - + constructor(schema?) { this.hooks = { pre: { save: [], @@ -424,7 +422,7 @@ class Schema { * @param {Object} schema * @param {String} prefix */ - add(schema, prefix = '') { + add(schema: Record, prefix = ''): void { const keys = Object.keys(schema); const len = keys.length; @@ -445,7 +443,9 @@ class Schema { * @param {*} obj * @return {SchemaType | undefined} */ - path(name, obj) { + path(name: string): SchemaType; + path(name: string, obj: SchemaType | ((...args: any[]) => any) | { type: any; } | Record | any[]): void; + path(name: string, obj?: SchemaType | ((...args: any[]) => any) | { type: any; } | Record | any[]): SchemaType | void { if (obj == null) { return this.paths[name]; } @@ -462,12 +462,12 @@ class Schema { break; case 'object': - if (obj.type) { - type = getSchemaType(name, obj); - } else if (Array.isArray(obj)) { + if (Array.isArray(obj)) { type = new Types.Array(name, { child: obj.length ? getSchemaType(name, obj[0]) : new SchemaType(name) }); + } else if (obj.type) { + type = getSchemaType(name, obj); } else { type = new Types.Object(); nested = Object.keys(obj).length > 0; @@ -493,7 +493,7 @@ class Schema { * @param {SchemaType} type * @private */ - _updateStack(name, type) { + _updateStack(name: string, type: SchemaType) { const { stacks } = this; stacks.getter.push(data => { @@ -518,7 +518,7 @@ class Schema { stacks.import.push(data => { const value = getProp(data, name); - const result = type.parse(value, data); + const result = type.parse(value); if (result !== undefined) { setProp(data, name, result); @@ -544,7 +544,7 @@ class Schema { * @param {Function} [getter] * @return {SchemaType.Virtual} */ - virtual(name, getter) { + virtual(name: string, getter?: () => any): SchemaTypeVirtual { const virtual = new Types.Virtual(name, {}); if (getter) virtual.get(getter); @@ -601,7 +601,7 @@ class Schema { * @param {String} name * @param {Function} fn */ - static(name, fn) { + static(name: string, fn) { if (!name) throw new TypeError('Method name is required!'); if (typeof fn !== 'function') { @@ -745,6 +745,7 @@ class Schema { * @return {PopulateResult[]} * @private */ + // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types _parsePopulate(expr) { const { paths } = this; const arr = []; @@ -775,6 +776,8 @@ class Schema { if (!item.model) { const path = paths[key]; + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore const ref = path.child ? path.child.options.ref : path.options.ref; if (!ref) { @@ -787,9 +790,10 @@ class Schema { return arr; } + Types: typeof Types; + static Types = Types; } Schema.prototype.Types = Types; -Schema.Types = Schema.prototype.Types; -module.exports = Schema; +export default Schema; diff --git a/lib/schematype.js b/src/schematype.ts similarity index 79% rename from lib/schematype.js rename to src/schematype.ts index 06192a30..22dea735 100644 --- a/lib/schematype.js +++ b/src/schematype.ts @@ -1,7 +1,5 @@ -'use strict'; - -const { setProp } = require('./util'); -const ValidationError = require('./error/validation'); +import { setProp } from './util'; +import ValidationError from './error/validation'; /** * This is the basic schema type. @@ -46,7 +44,9 @@ const ValidationError = require('./error/validation'); * * The return value will replace the original data. */ -class SchemaType { +class SchemaType { + options: { required: boolean; default?: (() => T) | T; }; + default: () => T; /** * SchemaType constructor. @@ -56,9 +56,7 @@ class SchemaType { * @param {Boolean} [options.required=false] * @param {*} [options.default] */ - constructor(name, options) { - this.name = name || ''; - + constructor(public name = '', options?: { required?: boolean; default?: (() => T) | T; }) { this.options = Object.assign({ required: false }, options); @@ -66,7 +64,7 @@ class SchemaType { const default_ = this.options.default; if (typeof default_ === 'function') { - this.default = default_; + this.default = default_ as unknown as () => T; } else { this.default = () => default_; } @@ -80,7 +78,7 @@ class SchemaType { * @param {Object} data * @return {*} */ - cast(value, data) { + cast(value?: unknown, data?: unknown): unknown { if (value == null) { return this.default(); } @@ -95,7 +93,7 @@ class SchemaType { * @param {Object} data * @return {*|Error} */ - validate(value, data) { + validate(value: unknown, data?: unknown): unknown { if (this.options.required && value == null) { throw new ValidationError(`\`${this.name}\` is required!`); } @@ -110,7 +108,7 @@ class SchemaType { * @param {*} b * @return {Number} */ - compare(a, b) { + compare(a: unknown, b: unknown): number { if (a > b) { return 1; } else if (a < b) { @@ -127,7 +125,7 @@ class SchemaType { * @param {Object} data * @return {*} */ - parse(value, data) { + parse(value: unknown): any { return value; } @@ -138,7 +136,7 @@ class SchemaType { * @param {Object} data * @return {*} */ - value(value, data) { + value(value: unknown, data?: unknown): any { return value; } @@ -150,7 +148,7 @@ class SchemaType { * @param {Object} data * @return {Boolean} */ - match(value, query, data) { + match(value: T, query: unknown, data?: unknown): boolean { return value === query; } @@ -162,7 +160,7 @@ class SchemaType { * @param {Object} data * @return {Boolean} */ - q$exist(value, query, data) { + q$exist(value: unknown, query: unknown, data?: unknown): boolean { return (value != null) === query; } @@ -174,7 +172,7 @@ class SchemaType { * @param {Object} data * @return {boolean} */ - q$ne(value, query, data) { + q$ne(value: T, query: unknown, data?: unknown): boolean { return !this.match(value, query, data); } @@ -186,7 +184,7 @@ class SchemaType { * @param {Object} data * @return {Boolean} */ - q$lt(value, query, data) { + q$lt(value: unknown, query: unknown, data?: unknown): boolean { return value < query; } @@ -198,7 +196,7 @@ class SchemaType { * @param {Object} data * @return {Boolean} */ - q$lte(value, query, data) { + q$lte(value: unknown, query: unknown, data?: unknown): boolean { return value <= query; } @@ -210,7 +208,7 @@ class SchemaType { * @param {Object} data * @return {Boolean} */ - q$gt(value, query, data) { + q$gt(value: unknown, query: unknown, data?: unknown): boolean { return value > query; } @@ -222,7 +220,7 @@ class SchemaType { * @param {Object} data * @return {Boolean} */ - q$gte(value, query, data) { + q$gte(value: unknown, query: unknown, data?: unknown): boolean { return value >= query; } @@ -234,7 +232,7 @@ class SchemaType { * @param {Object} data * @return {Boolean} */ - q$in(value, query, data) { + q$in(value: unknown, query: unknown[], data?: unknown): boolean { return query.includes(value); } @@ -246,7 +244,7 @@ class SchemaType { * @param {Object} data * @return {Boolean} */ - q$nin(value, query, data) { + q$nin(value, query, data?) { return !query.includes(value); } @@ -258,7 +256,7 @@ class SchemaType { * @param {Object} data * @return {*} */ - u$set(value, update, data) { + u$set(value, update, data?) { return update; } @@ -270,7 +268,7 @@ class SchemaType { * @param {Object} data * @return {*} */ - u$unset(value, update, data) { return update ? undefined : value; } + u$unset(value, update, data?) { return update ? undefined : value; } /** * Renames a field. @@ -280,10 +278,14 @@ class SchemaType { * @param {Object} data * @return {*} */ - u$rename(value, update, data) { + u$rename(value, update, data): void { if (value !== undefined) setProp(data, update, value); return undefined; } + + q$exists: SchemaType['q$exist']; + q$max: SchemaType['q$lte']; + q$min: SchemaType['q$gte']; } SchemaType.prototype.q$exists = SchemaType.prototype.q$exist; @@ -292,4 +294,4 @@ SchemaType.prototype.q$max = SchemaType.prototype.q$lte; SchemaType.prototype.q$min = SchemaType.prototype.q$gte; -module.exports = SchemaType; +export default SchemaType; diff --git a/lib/types/array.js b/src/types/array.ts similarity index 79% rename from lib/types/array.js rename to src/types/array.ts index 9c7b08ab..3c51f36f 100644 --- a/lib/types/array.js +++ b/src/types/array.ts @@ -1,14 +1,14 @@ -'use strict'; - -const SchemaType = require('../schematype'); -const ValidationError = require('../error/validation'); +import SchemaType from '../schematype'; +import ValidationError from '../error/validation'; const { isArray } = Array; /** * Array schema type. */ -class SchemaTypeArray extends SchemaType { +class SchemaTypeArray> extends SchemaType { + options: SchemaType['options'] & { child?: T; }; + child: T; /** * @@ -18,12 +18,12 @@ class SchemaTypeArray extends SchemaType { * @param {Array|Function} [options.default=[]] * @param {SchemaType} [options.child] */ - constructor(name, options) { + constructor(name: string, options?: Partial['options']> & { child?: T; }) { super(name, Object.assign({ default: [] }, options)); - this.child = this.options.child || new SchemaType(name); + this.child = this.options.child || new SchemaType(name) as T; } /** @@ -33,11 +33,11 @@ class SchemaTypeArray extends SchemaType { * @param {Object} data * @return {Array} */ - cast(value_, data) { - let value = super.cast(value_, data); - if (value == null) return value; + cast(value_?: unknown, data?: unknown): I[] | null | undefined { + value_ = super.cast(value_, data); + if (value_ == null) return value_ as null | undefined; - if (!isArray(value)) value = [value]; + const value = isArray(value_) ? value_ : value_ = [value_]; if (!value.length) return value; const child = this.child; @@ -56,7 +56,7 @@ class SchemaTypeArray extends SchemaType { * @param {Object} data * @return {Array|Error} */ - validate(value_, data) { + validate(value_?: unknown, data?: unknown): I[] { const value = super.validate(value_, data); if (!isArray(value)) { @@ -68,7 +68,7 @@ class SchemaTypeArray extends SchemaType { const child = this.child; for (let i = 0, len = value.length; i < len; i++) { - value[i] = child.validate(value[i], data); + value[i] = child.validate(value[i], data) as I; } return value; @@ -81,7 +81,7 @@ class SchemaTypeArray extends SchemaType { * @param {Array} b * @return {Number} */ - compare(a, b) { + compare(a?: I[], b?: I[]): number { if (a) { if (!b) return 1; } else { @@ -105,10 +105,9 @@ class SchemaTypeArray extends SchemaType { * Parses data. * * @param {Array} value - * @param {Object} data * @return {Array} */ - parse(value, data) { + parse(value?: unknown[]) { if (!value) return value; const len = value.length; @@ -118,7 +117,7 @@ class SchemaTypeArray extends SchemaType { const child = this.child; for (let i = 0; i < len; i++) { - result[i] = child.parse(value[i], data); + result[i] = child.parse(value[i]); } return result; @@ -131,7 +130,7 @@ class SchemaTypeArray extends SchemaType { * @param {Object} data * @return {Array} */ - value(value, data) { + value(value?: unknown[], data?: unknown): any[] { if (!value) return value; const len = value.length; @@ -155,7 +154,7 @@ class SchemaTypeArray extends SchemaType { * @param {Object} data * @return {Boolean} */ - match(value, query, data) { + match(value?: I[], query?: unknown[], data?: unknown): boolean { if (!value || !query) { return value === query; } @@ -182,7 +181,7 @@ class SchemaTypeArray extends SchemaType { * @param {Object} data * @return {Boolean} */ - q$size(value, query, data) { + q$size(value?: unknown[], query?: unknown, data?: unknown): boolean { return (value ? value.length : 0) === query; } @@ -194,7 +193,7 @@ class SchemaTypeArray extends SchemaType { * @param {Object} data * @return {Boolean} */ - q$in(value, query, data) { + q$in(value?: unknown[], query?: unknown[], data?: unknown): boolean { if (!value) return false; for (let i = 0, len = query.length; i < len; i++) { @@ -212,7 +211,7 @@ class SchemaTypeArray extends SchemaType { * @param {Object} data * @return {Boolean} */ - q$nin(value, query, data) { + q$nin(value?: unknown[], query?: unknown[], data?: unknown): boolean { if (!value) return true; for (let i = 0, len = query.length; i < len; i++) { @@ -230,7 +229,7 @@ class SchemaTypeArray extends SchemaType { * @param {Object} data * @return {Boolean} */ - q$all(value, query, data) { + q$all(value?: unknown[], query?: unknown[], data?: unknown): boolean { if (!value) return false; for (let i = 0, len = query.length; i < len; i++) { @@ -248,7 +247,7 @@ class SchemaTypeArray extends SchemaType { * @param {Object} data * @return {Array} */ - u$push(value, update, data) { + u$push(value?: T[], update?: T | T[], data?: unknown): T[] { if (isArray(update)) { return value ? value.concat(update) : update; } @@ -269,7 +268,7 @@ class SchemaTypeArray extends SchemaType { * @param {Object} data * @return {Array} */ - u$unshift(value, update, data) { + u$unshift(value?: unknown[], update?: unknown, data?: unknown): any[] { if (isArray(update)) { return value ? update.concat(value) : update; } @@ -290,7 +289,7 @@ class SchemaTypeArray extends SchemaType { * @param {Object} data * @return {Array} */ - u$pull(value, update, data) { + u$pull(value?: unknown[], update?: unknown, data?: unknown): any[] { if (!value) return value; if (isArray(update)) { @@ -308,7 +307,7 @@ class SchemaTypeArray extends SchemaType { * @param {Object} data * @return {Array} */ - u$shift(value, update, data) { + u$shift(value?, update?, data?) { if (!value || !update) return value; if (update === true) { @@ -328,7 +327,7 @@ class SchemaTypeArray extends SchemaType { * @param {Object} data * @return {Array} */ - u$pop(value, update, data) { + u$pop(value?, update?, data?) { if (!value || !update) return value; const length = value.length; @@ -350,7 +349,7 @@ class SchemaTypeArray extends SchemaType { * @param {Object} data * @return {Array} */ - u$addToSet(value, update, data) { + u$addToSet(value?: any[], update?, data?) { if (isArray(update)) { if (!value) return update; @@ -370,6 +369,9 @@ class SchemaTypeArray extends SchemaType { return value; } + q$length: SchemaTypeArray['q$size']; + u$append: SchemaTypeArray['u$push']; + u$prepend: SchemaTypeArray['u$unshift']; } SchemaTypeArray.prototype.q$length = SchemaTypeArray.prototype.q$size; @@ -378,4 +380,4 @@ SchemaTypeArray.prototype.u$append = SchemaTypeArray.prototype.u$push; SchemaTypeArray.prototype.u$prepend = SchemaTypeArray.prototype.u$unshift; -module.exports = SchemaTypeArray; +export default SchemaTypeArray; diff --git a/lib/types/boolean.js b/src/types/boolean.ts similarity index 70% rename from lib/types/boolean.js rename to src/types/boolean.ts index 16148b1c..55330fde 100644 --- a/lib/types/boolean.js +++ b/src/types/boolean.ts @@ -1,12 +1,10 @@ -'use strict'; - -const SchemaType = require('../schematype'); -const ValidationError = require('../error/validation'); +import SchemaType from '../schematype'; +import ValidationError from '../error/validation'; /** * Boolean schema type. */ -class SchemaTypeBoolean extends SchemaType { +class SchemaTypeBoolean extends SchemaType { /** * Casts a boolean. @@ -15,7 +13,7 @@ class SchemaTypeBoolean extends SchemaType { * @param {Object} data * @return {Boolean} */ - cast(value_, data) { + cast(value_?: unknown, data?: unknown): boolean { const value = super.cast(value_, data); if (value === 'false' || value === '0') return false; @@ -30,24 +28,23 @@ class SchemaTypeBoolean extends SchemaType { * @param {Object} data * @return {Boolean|Error} */ - validate(value_, data) { + validate(value_: unknown, data?: unknown): boolean { const value = super.validate(value_, data); if (value != null && typeof value !== 'boolean') { throw new ValidationError(`\`${value}\` is not a boolean!`); } - return value; + return value as boolean; } /** * Parses data and transform them into boolean values. * * @param {*} value - * @param {Object} data * @return {Boolean} */ - parse(value, data) { + parse(value: unknown): boolean { return Boolean(value); } @@ -55,12 +52,11 @@ class SchemaTypeBoolean extends SchemaType { * Transforms data into number to compress the size of database files. * * @param {Boolean} value - * @param {Object} data * @return {Number} */ - value(value, data) { + value(value: unknown): number { return +value; } } -module.exports = SchemaTypeBoolean; +export default SchemaTypeBoolean; diff --git a/lib/types/buffer.js b/src/types/buffer.ts similarity index 74% rename from lib/types/buffer.js rename to src/types/buffer.ts index 20df46b5..73cc157e 100644 --- a/lib/types/buffer.js +++ b/src/types/buffer.ts @@ -1,12 +1,11 @@ -'use strict'; - -const SchemaType = require('../schematype'); -const ValidationError = require('../error/validation'); +import SchemaType from '../schematype'; +import ValidationError from '../error/validation'; /** * Boolean schema type. */ -class SchemaTypeBuffer extends SchemaType { +class SchemaTypeBuffer extends SchemaType { + options: SchemaType['options'] & { encoding: BufferEncoding; }; /** * @param {string} name @@ -15,7 +14,7 @@ class SchemaTypeBuffer extends SchemaType { * @param {boolean|Function} [options.default] * @param {string} [options.encoding=hex] */ - constructor(name, options) { + constructor(name: string, options?) { super(name, Object.assign({ encoding: 'hex' }, options)); @@ -28,10 +27,10 @@ class SchemaTypeBuffer extends SchemaType { * @param {Object} data * @return {Buffer} */ - cast(value_, data) { + cast(value_?: unknown, data?): Buffer | null | undefined { const value = super.cast(value_, data); - if (value == null || Buffer.isBuffer(value)) return value; + if (value == null || Buffer.isBuffer(value)) return value as Buffer | null | undefined; if (typeof value === 'string') return Buffer.from(value, this.options.encoding); if (Array.isArray(value)) return Buffer.from(value); } @@ -43,7 +42,7 @@ class SchemaTypeBuffer extends SchemaType { * @param {Object} data * @return {Buffer} */ - validate(value_, data) { + validate(value_: unknown, data?): Buffer { const value = super.validate(value_, data); if (!Buffer.isBuffer(value)) { @@ -60,7 +59,7 @@ class SchemaTypeBuffer extends SchemaType { * @param {Buffer} b * @return {Number} */ - compare(a, b) { + compare(a?: Buffer, b?: Buffer): number { if (Buffer.isBuffer(a)) { return Buffer.isBuffer(b) ? a.compare(b) : 1; } @@ -72,10 +71,9 @@ class SchemaTypeBuffer extends SchemaType { * Parses data and transform them into buffer values. * * @param {*} value - * @param {Object} data * @return {Boolean} */ - parse(value, data) { + parse(value?) { return value ? Buffer.from(value, this.options.encoding) : value; } @@ -83,10 +81,9 @@ class SchemaTypeBuffer extends SchemaType { * Transforms data into number to compress the size of database files. * * @param {Buffer} value - * @param {Object} data * @return {Number} */ - value(value, data) { + value(value?: Buffer) { return Buffer.isBuffer(value) ? value.toString(this.options.encoding) : value; } @@ -95,10 +92,9 @@ class SchemaTypeBuffer extends SchemaType { * * @param {Buffer} value * @param {Buffer} query - * @param {Object} data * @return {Boolean} */ - match(value, query, data) { + match(value: Buffer, query: Buffer): boolean { if (Buffer.isBuffer(value) && Buffer.isBuffer(query)) { return value.equals(query); } @@ -107,4 +103,4 @@ class SchemaTypeBuffer extends SchemaType { } } -module.exports = SchemaTypeBuffer; +export default SchemaTypeBuffer; diff --git a/lib/types/cuid.js b/src/types/cuid.ts similarity index 66% rename from lib/types/cuid.js rename to src/types/cuid.ts index 4e723e89..49b057c6 100644 --- a/lib/types/cuid.js +++ b/src/types/cuid.ts @@ -1,23 +1,20 @@ -'use strict'; - -const SchemaType = require('../schematype'); -const cuid = require('cuid'); -const ValidationError = require('../error/validation'); +import SchemaType from '../schematype'; +import cuid from 'cuid'; +import ValidationError from '../error/validation'; /** * [CUID](https://github.com/ericelliott/cuid) schema type. */ -class SchemaTypeCUID extends SchemaType { +class SchemaTypeCUID extends SchemaType { /** * Casts data. Returns a new CUID only if value is null and the field is * required. * * @param {String} value - * @param {Object} data * @return {String} */ - cast(value, data) { + cast(value?) { if (value == null && this.options.required) { return cuid(); } @@ -29,10 +26,9 @@ class SchemaTypeCUID extends SchemaType { * Validates data. A valid CUID must be started with `c` and 25 in length. * * @param {*} value - * @param {Object} data * @return {String|Error} */ - validate(value, data) { + validate(value?) { if (value && (value[0] !== 'c' || value.length !== 25)) { throw new ValidationError(`\`${value}\` is not a valid CUID`); } @@ -41,4 +37,4 @@ class SchemaTypeCUID extends SchemaType { } } -module.exports = SchemaTypeCUID; +export default SchemaTypeCUID; diff --git a/lib/types/date.js b/src/types/date.ts similarity index 70% rename from lib/types/date.js rename to src/types/date.ts index a17e7c05..40727b87 100644 --- a/lib/types/date.js +++ b/src/types/date.ts @@ -1,26 +1,27 @@ -'use strict'; - -const SchemaType = require('../schematype'); -const ValidationError = require('../error/validation'); +import SchemaType from '../schematype'; +import ValidationError from '../error/validation'; /** * Date schema type. */ -class SchemaTypeDate extends SchemaType { +class SchemaTypeDate extends SchemaType { /** * Casts data. * * @param {*} value - * @param {Object} data - * @return {Date} + * @return {Date | null | undefined} */ - cast(value_, data) { - const value = super.cast(value_, data); + cast(value_?): Date | null | undefined; + cast(value_: Date): Date; + cast(value_: null): Date | null; + cast(value_: undefined): Date | undefined; + cast(value_: unknown): Date | null | undefined { + const value = super.cast(value_, null); - if (value == null || value instanceof Date) return value; + if (value == null || value instanceof Date) return value as Date | null | undefined; - return new Date(value); + return new Date(value as any); } /** @@ -30,7 +31,7 @@ class SchemaTypeDate extends SchemaType { * @param {Object} data * @return {Date|Error} */ - validate(value_, data) { + validate(value_, data?) { const value = super.validate(value_, data); if (value != null && (!(value instanceof Date) || isNaN(value.getTime()))) { @@ -45,10 +46,9 @@ class SchemaTypeDate extends SchemaType { * * @param {Date} value * @param {Date} query - * @param {Object} data * @return {Boolean} */ - match(value, query, data) { + match(value: Date, query: Date): boolean { if (!value || !query) { return value === query; } @@ -63,7 +63,7 @@ class SchemaTypeDate extends SchemaType { * @param {Date} b * @return {Number} */ - compare(a, b) { + compare(a?, b?) { if (a) { return b ? a - b : 1; } @@ -75,10 +75,9 @@ class SchemaTypeDate extends SchemaType { * Parses data and transforms it into a date object. * * @param {*} value - * @param {Object} data * @return {Date} */ - parse(value, data) { + parse(value?) { if (value) return new Date(value); } @@ -86,10 +85,9 @@ class SchemaTypeDate extends SchemaType { * Transforms a date object to a string. * * @param {Date} value - * @param {Object} data * @return {String} */ - value(value, data) { + value(value?) { return value ? value.toISOString() : value; } @@ -98,10 +96,9 @@ class SchemaTypeDate extends SchemaType { * * @param {Date} value * @param {Number} query - * @param {Object} data * @return {Boolean} */ - q$day(value, query, data) { + q$day(value, query) { return value ? value.getDate() === query : false; } @@ -110,10 +107,9 @@ class SchemaTypeDate extends SchemaType { * * @param {Date} value * @param {Number} query - * @param {Object} data * @return {Boolean} */ - q$month(value, query, data) { + q$month(value, query) { return value ? value.getMonth() === query : false; } @@ -122,10 +118,9 @@ class SchemaTypeDate extends SchemaType { * * @param {Date} value * @param {Number} query - * @param {Object} data * @return {Boolean} */ - q$year(value, query, data) { + q$year(value, query) { return value ? value.getFullYear() === query : false; } @@ -134,10 +129,9 @@ class SchemaTypeDate extends SchemaType { * * @param {Date} value * @param {Number} update - * @param {Object} data * @return {Date} */ - u$inc(value, update, data) { + u$inc(value, update) { if (value) return new Date(value.getTime() + update); } @@ -146,12 +140,11 @@ class SchemaTypeDate extends SchemaType { * * @param {Date} value * @param {Number} update - * @param {Object} data * @return {Date} */ - u$dec(value, update, data) { + u$dec(value, update) { if (value) return new Date(value.getTime() - update); } } -module.exports = SchemaTypeDate; +export default SchemaTypeDate; diff --git a/lib/types/enum.js b/src/types/enum.ts similarity index 73% rename from lib/types/enum.js rename to src/types/enum.ts index e720507b..1a96d88c 100644 --- a/lib/types/enum.js +++ b/src/types/enum.ts @@ -1,12 +1,11 @@ -'use strict'; - -const SchemaType = require('../schematype'); -const ValidationError = require('../error/validation'); +import SchemaType from '../schematype'; +import ValidationError from '../error/validation'; /** * Enum schema type. */ -class SchemaTypeEnum extends SchemaType { +class SchemaTypeEnum extends SchemaType { + options: SchemaType['options'] & { elements: any[] }; /** * @@ -29,7 +28,7 @@ class SchemaTypeEnum extends SchemaType { * @param {Object} data * @return {*} */ - validate(value_, data) { + validate(value_, data?) { const value = super.validate(value_, data); const elements = this.options.elements; @@ -41,4 +40,4 @@ class SchemaTypeEnum extends SchemaType { } } -module.exports = SchemaTypeEnum; +export default SchemaTypeEnum; diff --git a/src/types/index.ts b/src/types/index.ts new file mode 100644 index 00000000..69174d03 --- /dev/null +++ b/src/types/index.ts @@ -0,0 +1,27 @@ +import SchemaType from '../schematype'; +import SchemaTypeString from './string'; +import SchemaTypeNumber from './number'; +import SchemaTypeBoolean from './boolean'; +import SchemaTypeArray from './array'; +import SchemaTypeObject from './object'; +import SchemaTypeDate from './date'; +import SchemaTypeVirtual from './virtual'; +import SchemaTypeCUID from './cuid'; +import SchemaTypeEnum from './enum'; +import SchemaTypeInteger from './integer'; +import SchemaTypeBuffer from './buffer'; + +export { + SchemaType as Mixed, + SchemaTypeString as String, + SchemaTypeNumber as Number, + SchemaTypeBoolean as Boolean, + SchemaTypeArray as Array, + SchemaTypeObject as Object, + SchemaTypeDate as Date, + SchemaTypeVirtual as Virtual, + SchemaTypeCUID as CUID, + SchemaTypeEnum as Enum, + SchemaTypeInteger as Integer, + SchemaTypeBuffer as Buffer +}; diff --git a/lib/types/integer.js b/src/types/integer.ts similarity index 69% rename from lib/types/integer.js rename to src/types/integer.ts index 2c855042..5eaea23e 100644 --- a/lib/types/integer.js +++ b/src/types/integer.ts @@ -1,7 +1,5 @@ -'use strict'; - -const SchemaTypeNumber = require('./number'); -const ValidationError = require('../error/validation'); +import SchemaTypeNumber from './number'; +import ValidationError from '../error/validation'; /** * Integer schema type. @@ -15,10 +13,10 @@ class SchemaTypeInteger extends SchemaTypeNumber { * @param {Object} data * @return {Number} */ - cast(value_, data) { + cast(value_?, data?): number { const value = super.cast(value_, data); - return parseInt(value, 10); + return parseInt(value as any, 10); } /** @@ -28,7 +26,7 @@ class SchemaTypeInteger extends SchemaTypeNumber { * @param {Object} data * @return {Number|Error} */ - validate(value_, data) { + validate(value_?, data?): number { const value = super.validate(value_, data); if (value % 1 !== 0) { @@ -39,4 +37,4 @@ class SchemaTypeInteger extends SchemaTypeNumber { } } -module.exports = SchemaTypeInteger; +export default SchemaTypeInteger; diff --git a/lib/types/number.js b/src/types/number.ts similarity index 74% rename from lib/types/number.js rename to src/types/number.ts index 90dbffa5..83dcbe2c 100644 --- a/lib/types/number.js +++ b/src/types/number.ts @@ -1,12 +1,10 @@ -'use strict'; - -const SchemaType = require('../schematype'); -const ValidationError = require('../error/validation'); +import SchemaType from '../schematype'; +import ValidationError from '../error/validation'; /** * Number schema type. */ -class SchemaTypeNumber extends SchemaType { +class SchemaTypeNumber extends SchemaType { /** * Casts a number. @@ -15,10 +13,10 @@ class SchemaTypeNumber extends SchemaType { * @param {Object} data * @return {Number} */ - cast(value_, data) { + cast(value_?, data?): number | null | undefined { const value = super.cast(value_, data); - if (value == null || typeof value === 'number') return value; + if (value == null || typeof value === 'number') return value as number | null | undefined; return +value; } @@ -30,7 +28,7 @@ class SchemaTypeNumber extends SchemaType { * @param {Object} data * @return {Number|Error} */ - validate(value_, data) { + validate(value_?, data?) { const value = super.validate(value_, data); if (value !== undefined && (typeof value !== 'number' || isNaN(value))) { @@ -45,10 +43,9 @@ class SchemaTypeNumber extends SchemaType { * * @param {Number} value * @param {Number} update - * @param {Object} data * @return {Number} */ - u$inc(value, update, data) { + u$inc(value, update) { return value ? value + update : update; } @@ -57,10 +54,9 @@ class SchemaTypeNumber extends SchemaType { * * @param {Number} value * @param {Number} update - * @param {Object} data * @return {Number} */ - u$dec(value, update, data) { + u$dec(value, update) { return value ? value - update : -update; } @@ -69,10 +65,9 @@ class SchemaTypeNumber extends SchemaType { * * @param {Number} value * @param {Number} update - * @param {Object} data * @return {Number} */ - u$mul(value, update, data) { + u$mul(value, update) { return value ? value * update : 0; } @@ -81,10 +76,9 @@ class SchemaTypeNumber extends SchemaType { * * @param {Number} value * @param {Number} update - * @param {Object} data * @return {Number} */ - u$div(value, update, data) { + u$div(value, update) { return value ? value / update : 0; } @@ -93,10 +87,9 @@ class SchemaTypeNumber extends SchemaType { * * @param {Number} value * @param {Number} update - * @param {Object} data * @return {Number} */ - u$mod(value, update, data) { + u$mod(value, update) { return value ? value % update : 0; } @@ -105,10 +98,9 @@ class SchemaTypeNumber extends SchemaType { * * @param {Number} value * @param {Number} update - * @param {Object} data * @return {Number} */ - u$max(value, update, data) { + u$max(value, update) { return update > value ? update : value; } @@ -117,12 +109,11 @@ class SchemaTypeNumber extends SchemaType { * * @param {Number} value * @param {Number} update - * @param {Object} data * @return {Number} */ - u$min(value, update, data) { + u$min(value, update) { return update < value ? update : value; } } -module.exports = SchemaTypeNumber; +export default SchemaTypeNumber; diff --git a/lib/types/object.js b/src/types/object.ts similarity index 55% rename from lib/types/object.js rename to src/types/object.ts index ff2eb391..840fa20c 100644 --- a/lib/types/object.js +++ b/src/types/object.ts @@ -1,22 +1,20 @@ -'use strict'; - -const SchemaType = require('../schematype'); +import SchemaType from '../schematype'; /** * Object schema type. */ -class SchemaTypeObject extends SchemaType { +class SchemaTypeObject extends SchemaType> { /** * - * @param {String} name + * @param {String} [name] * @param {Object} [options] * @param {Boolean} [options.required=false] * @param {Object|Function} [options.default={}] */ - constructor(name, options) { + constructor(name?, options?) { super(name, Object.assign({ default: {} }, options)); } } -module.exports = SchemaTypeObject; +export default SchemaTypeObject; diff --git a/lib/types/string.js b/src/types/string.ts similarity index 81% rename from lib/types/string.js rename to src/types/string.ts index ac42f089..589ee085 100644 --- a/lib/types/string.js +++ b/src/types/string.ts @@ -1,12 +1,10 @@ -'use strict'; - -const SchemaType = require('../schematype'); -const ValidationError = require('../error/validation'); +import SchemaType from '../schematype'; +import ValidationError from '../error/validation'; /** * String schema type. */ -class SchemaTypeString extends SchemaType { +class SchemaTypeString extends SchemaType { /** * Casts a string. @@ -15,7 +13,7 @@ class SchemaTypeString extends SchemaType { * @param {Object} data * @return {String} */ - cast(value_, data) { + cast(value_?, data?) { const value = super.cast(value_, data); if (value == null || typeof value === 'string') return value; @@ -29,7 +27,7 @@ class SchemaTypeString extends SchemaType { * @param {Object} data * @return {String|Error} */ - validate(value_, data) { + validate(value_?, data?) { const value = super.validate(value_, data); if (value !== undefined && typeof value !== 'string') { @@ -47,7 +45,7 @@ class SchemaTypeString extends SchemaType { * @param {Object} data * @return {Boolean} */ - match(value, query, data) { + match(value, query, data?) { if (!value || !query) { return value === query; } @@ -67,7 +65,7 @@ class SchemaTypeString extends SchemaType { * @param {Object} data * @return {Boolean} */ - q$in(value, query, data) { + q$in(value, query, data?) { for (let i = 0, len = query.length; i < len; i++) { if (this.match(value, query[i], data)) return true; } @@ -83,7 +81,7 @@ class SchemaTypeString extends SchemaType { * @param {Object} data * @return {Boolean} */ - q$nin(value, query, data) { + q$nin(value, query, data?) { return !this.q$in(value, query, data); } @@ -92,12 +90,11 @@ class SchemaTypeString extends SchemaType { * * @param {String} value * @param {Number} query - * @param {Object} data * @return {Boolean} */ - q$length(value, query, data) { + q$length(value, query) { return (value ? value.length : 0) === query; } } -module.exports = SchemaTypeString; +export default SchemaTypeString; diff --git a/lib/types/virtual.js b/src/types/virtual.ts similarity index 82% rename from lib/types/virtual.js rename to src/types/virtual.ts index adf8748f..aa333a5a 100644 --- a/lib/types/virtual.js +++ b/src/types/virtual.ts @@ -1,12 +1,12 @@ -'use strict'; - -const SchemaType = require('../schematype'); -const { setGetter } = require('../util'); +import SchemaType from '../schematype'; +import { setGetter } from '../util'; /** * Virtual schema type. */ -class SchemaTypeVirtual extends SchemaType { +class SchemaTypeVirtual extends SchemaType { + getter: (() => any) | undefined; + setter: ((value: any) => void) | undefined; /** * Add a getter. @@ -77,4 +77,4 @@ class SchemaTypeVirtual extends SchemaType { } } -module.exports = SchemaTypeVirtual; +export default SchemaTypeVirtual; diff --git a/lib/util.js b/src/util.ts similarity index 90% rename from lib/util.js rename to src/util.ts index 596e2acd..62cef092 100644 --- a/lib/util.js +++ b/src/util.ts @@ -1,6 +1,35 @@ -'use strict'; +function extractPropKey(key) { + return key.split('.'); +} + +function _parseArgs(args) { + if (typeof args !== 'string') return args; + + const arr = args.split(' '); + const result = {}; + + for (let i = 0, len = arr.length; i < len; i++) { + const key = arr[i]; + + switch (key[0]) { + case '+': + result[key.slice(1)] = 1; + break; -exports.shuffle = array => { + case '-': + result[key.slice(1)] = -1; + break; + + default: + result[key] = 1; + } + } + + return result; +} + + +export function shuffle(array) { if (!Array.isArray(array)) throw new TypeError('array must be an Array!'); const $array = array.slice(); const { length } = $array; @@ -17,13 +46,9 @@ exports.shuffle = array => { } return $array; -}; - -function extractPropKey(key) { - return key.split('.'); } -exports.getProp = (obj, key) => { +export function getProp(obj, key) { if (typeof obj !== 'object') throw new TypeError('obj must be an object!'); if (!key) throw new TypeError('key is required!'); @@ -40,9 +65,9 @@ exports.getProp = (obj, key) => { } return result; -}; +} -exports.setProp = (obj, key, value) => { +export function setProp(obj, key, value) { if (typeof obj !== 'object') throw new TypeError('obj must be an object!'); if (!key) throw new TypeError('key is required!'); @@ -64,9 +89,9 @@ exports.setProp = (obj, key, value) => { } cursor[lastKey] = value; -}; +} -exports.delProp = (obj, key) => { +export function delProp(obj, key) { if (typeof obj !== 'object') throw new TypeError('obj must be an object!'); if (!key) throw new TypeError('key is required!'); @@ -92,9 +117,9 @@ exports.delProp = (obj, key) => { } delete cursor[lastKey]; -}; +} -exports.setGetter = (obj, key, fn) => { +export function setGetter(obj, key, fn) { if (typeof obj !== 'object') throw new TypeError('obj must be an object!'); if (!key) throw new TypeError('key is required!'); if (typeof fn !== 'function') throw new TypeError('fn must be a function!'); @@ -117,9 +142,9 @@ exports.setGetter = (obj, key, fn) => { } cursor.__defineGetter__(lastKey, fn); -}; +} -exports.arr2obj = (arr, value) => { +export function arr2obj(arr, value) { if (!Array.isArray(arr)) throw new TypeError('arr must be an array!'); const obj = {}; @@ -130,9 +155,9 @@ exports.arr2obj = (arr, value) => { } return obj; -}; +} -exports.reverse = arr => { +export function reverse(arr) { if (!Array.isArray(arr)) throw new TypeError('arr must be an array!'); const len = arr.length; @@ -146,45 +171,19 @@ exports.reverse = arr => { } return arr; -}; - -function parseArgs(args) { - if (typeof args !== 'string') return args; - - const arr = args.split(' '); - const result = {}; - - for (let i = 0, len = arr.length; i < len; i++) { - const key = arr[i]; - - switch (key[0]) { - case '+': - result[key.slice(1)] = 1; - break; - - case '-': - result[key.slice(1)] = -1; - break; - - default: - result[key] = 1; - } - } - - return result; } -exports.parseArgs = (orderby, order) => { +export function parseArgs(orderby, order?) { let result; if (order) { result = {}; result[orderby] = order; } else if (typeof orderby === 'string') { - result = parseArgs(orderby); + result = _parseArgs(orderby); } else { result = orderby; } return result; -}; +} diff --git a/test/.eslintrc.json b/test/.eslintrc.json index d7729e79..28b8a26d 100644 --- a/test/.eslintrc.json +++ b/test/.eslintrc.json @@ -1,4 +1,10 @@ { - "root": true, - "extends": "hexo/test" + "extends": "hexo/test", + "rules": { + "strict": 0, + "node/no-unsupported-features/es-syntax": 0, + "@typescript-eslint/no-var-requires": 0, + "@typescript-eslint/ban-ts-comment": 0, + "@typescript-eslint/no-empty-function": 0 + } } diff --git a/test/fixtures/db.json b/test/fixtures/db.json index 2bd0a6d0..55754ba9 100644 --- a/test/fixtures/db.json +++ b/test/fixtures/db.json @@ -1 +1 @@ -{"meta":{"version":1,"warehouse":"4.0.1"},"models":{"Test":[{"_id":"A"},{"_id":"B"},{"_id":"C"}]}} \ No newline at end of file +{"meta":{"version":1,"warehouse":"4.0.1"},"models":{"Test":[{"_id":"A"},{"_id":"B"},{"_id":"C"}]}} diff --git a/test/scripts/database.js b/test/scripts/database.ts similarity index 81% rename from test/scripts/database.js rename to test/scripts/database.ts index 30001e87..16be4655 100644 --- a/test/scripts/database.js +++ b/test/scripts/database.ts @@ -1,17 +1,17 @@ -'use strict'; - -const should = require('chai').should(); // eslint-disable-line -const path = require('path'); -const Promise = require('bluebird'); -const sinon = require('sinon'); -const fs = Promise.promisifyAll(require('fs')); +import chai from 'chai'; +const should = chai.should(); // eslint-disable-line +import path from 'path'; +import Promise from 'bluebird'; +import sinon from 'sinon'; +import Database from '../../dist/database'; +import Model from '../../dist/model'; +import fs from 'fs'; +const promisifyFs = Promise.promisifyAll(fs); const DB_PATH = path.join(path.dirname(__dirname), 'fixtures', 'db.json'); const DB_VERSION = 1; describe('Database', () => { - const Database = require('../..'); - const Model = require('../../lib/model'); const Schema = Database.Schema; const db = new Database({path: DB_PATH, version: DB_VERSION}); @@ -85,7 +85,9 @@ describe('Database', () => { }); }); - it('save()', () => db.save().then(() => fs.readFileAsync(DB_PATH, 'utf8')).then(data => { + it('save()', () => db.save().then(() => promisifyFs.readFileAsync(DB_PATH)).then(data => { + // TODO: fix + // @ts-ignore const json = JSON.parse(data); json.meta.should.eql({ diff --git a/test/scripts/document.js b/test/scripts/document.ts similarity index 94% rename from test/scripts/document.js rename to test/scripts/document.ts index ba9d359c..abac13a4 100644 --- a/test/scripts/document.js +++ b/test/scripts/document.ts @@ -1,10 +1,9 @@ -'use strict'; - -const should = require('chai').should(); // eslint-disable-line +import chai from 'chai'; +const should = chai.should(); // eslint-disable-line +import Database from '../../dist/database'; +import Document from '../../dist/document'; describe('Document', () => { - const Database = require('../..'); - const Document = require('../../lib/document'); const db = new Database(); const Schema = Database.Schema; diff --git a/test/scripts/model.js b/test/scripts/model.ts similarity index 98% rename from test/scripts/model.js rename to test/scripts/model.ts index 075dacf3..87cfead4 100644 --- a/test/scripts/model.js +++ b/test/scripts/model.ts @@ -1,13 +1,17 @@ -'use strict'; - -const should = require('chai').use(require('chai-as-promised')).should(); -const sortBy = require('lodash/sortBy'); -const Promise = require('bluebird'); -const sinon = require('sinon'); -const cuid = require('cuid'); +import chai from 'chai'; +const should = chai.should(); // eslint-disable-line +import chaiAsPromised from 'chai-as-promised'; +chai.use(chaiAsPromised ); // eslint-disable-line + +import lodash from 'lodash'; +const { sortBy } = lodash; +import Promise from 'bluebird'; +import sinon from 'sinon'; +import cuid from 'cuid'; +import Database from '../../dist/database'; describe('Model', () => { - const Database = require('../..'); + const Schema = Database.Schema; const SchemaType = Database.SchemaType; @@ -163,6 +167,7 @@ describe('Model', () => { return Promise.all([ Test.insert({id: 1}), Test.insert({id: 1}) + // @ts-ignore ]).should.eventually.be.rejected; }); @@ -1249,6 +1254,7 @@ describe('Model', () => { }); it('_export() - should not save undefined value', () => { + // @ts-ignore class CacheType extends SchemaType { value() {} } diff --git a/test/scripts/mutex.js b/test/scripts/mutex.ts similarity index 77% rename from test/scripts/mutex.js rename to test/scripts/mutex.ts index 4df3c5af..7157285a 100644 --- a/test/scripts/mutex.js +++ b/test/scripts/mutex.ts @@ -1,8 +1,7 @@ -'use strict'; - -const should = require('chai').should(); // eslint-disable-line -const Mutex = require('../../lib/mutex'); -const sinon = require('sinon'); +import chai from 'chai'; +const should = chai.should(); // eslint-disable-line +import Mutex from '../../dist/mutex'; +import sinon from 'sinon'; describe('Mutex', () => { it('mutex test', callback => { diff --git a/test/scripts/query.js b/test/scripts/query.ts similarity index 98% rename from test/scripts/query.js rename to test/scripts/query.ts index 06a99af6..b448882d 100644 --- a/test/scripts/query.js +++ b/test/scripts/query.ts @@ -1,12 +1,13 @@ -'use strict'; - -const should = require('chai').should(); // eslint-disable-line -const sortBy = require('lodash/sortBy'); -const Promise = require('bluebird'); -const Document = require('../../lib/document'); +import chai from 'chai'; +const should = chai.should(); // eslint-disable-line +import lodash from 'lodash'; +const { sortBy } = lodash; +import Promise from 'bluebird'; +import Document from '../../dist/document'; +import Database from '../../dist/database'; describe('Query', () => { - const Database = require('../..'); + const db = new Database(); const Schema = Database.Schema; diff --git a/test/scripts/schema.js b/test/scripts/schema.ts similarity index 87% rename from test/scripts/schema.js rename to test/scripts/schema.ts index 6d3081b5..2a4f9a70 100644 --- a/test/scripts/schema.js +++ b/test/scripts/schema.ts @@ -1,9 +1,8 @@ -'use strict'; - -require('chai').should(); +import chai from 'chai'; +const should = chai.should(); // eslint-disable-line +import Database from '../../dist/database'; describe('Schema', () => { - const Database = require('../..'); const Schema = Database.Schema; it('add()', () => { @@ -49,10 +48,13 @@ describe('Schema', () => { // array schema.paths.arr1.should.be.an.instanceOf(Schema.Types.Array); + // @ts-ignore schema.paths.arr1.child.should.be.an.instanceOf(Schema.Types.Mixed); schema.paths.arr2.should.be.an.instanceOf(Schema.Types.Array); + // @ts-ignore schema.paths.arr2.child.should.be.an.instanceOf(Schema.Types.String); schema.paths.arr3.should.be.an.instanceOf(Schema.Types.Array); + // @ts-ignore schema.paths.arr3.child.should.be.an.instanceOf(Schema.Types.String); // object @@ -101,7 +103,7 @@ describe('Schema', () => { (() => schema.pre('wtf', () => {})).should.to.throw(TypeError, 'Hook type must be `save` or `remove`!'); // hook is not a function - (() => schema.pre('save')).should.to.throw(TypeError, 'Hook must be a function!'); + (() => schema.pre('save', {})).should.to.throw(TypeError, 'Hook must be a function!'); }); it('post()', () => { @@ -121,7 +123,7 @@ describe('Schema', () => { (() => schema.post('wtf', () => {})).should.throw(TypeError, 'Hook type must be `save` or `remove`!'); // hook is not a function - (() => schema.post('save')).should.to.throw(TypeError, 'Hook must be a function!'); + (() => schema.post('save', {})).should.to.throw(TypeError, 'Hook must be a function!'); }); it('method()', () => { @@ -135,7 +137,7 @@ describe('Schema', () => { schema.method.should.to.throw(TypeError, 'Method name is required!'); // without function - (() => schema.method('wtf')).should.to.throw(TypeError, 'Instance method must be a function!'); + (() => schema.method('wtf', {})).should.to.throw(TypeError, 'Instance method must be a function!'); }); it('static()', () => { @@ -149,6 +151,6 @@ describe('Schema', () => { schema.static.should.to.throw(TypeError, 'Method name is required!'); // without function - (() => schema.static('wtf')).should.to.throw(TypeError, 'Static method must be a function!'); + (() => schema.static('wtf', {})).should.to.throw(TypeError, 'Static method must be a function!'); }); }); diff --git a/test/scripts/schematype.js b/test/scripts/schematype.ts similarity index 94% rename from test/scripts/schematype.js rename to test/scripts/schematype.ts index 6ed1bf25..a935779f 100644 --- a/test/scripts/schematype.js +++ b/test/scripts/schematype.ts @@ -1,10 +1,9 @@ -'use strict'; - -const should = require('chai').should(); // eslint-disable-line -const ValidationError = require('../../lib/error/validation'); +import chai from 'chai'; +const should = chai.should(); // eslint-disable-line +import ValidationError from '../../dist/error/validation'; +import SchemaType from '../../dist/schematype'; describe('SchemaType', () => { - const SchemaType = require('../../lib/schematype'); const type = new SchemaType('test'); it('cast()', () => { @@ -134,6 +133,7 @@ describe('SchemaType', () => { it('u$rename', () => { const obj = {a: 1}; should.not.exist(type.u$rename(1, 'b', obj)); + // @ts-ignore obj.b.should.eql(1); }); }); diff --git a/test/scripts/types/array.js b/test/scripts/types/array.ts similarity index 93% rename from test/scripts/types/array.js rename to test/scripts/types/array.ts index 7228343d..19ba5313 100644 --- a/test/scripts/types/array.js +++ b/test/scripts/types/array.ts @@ -1,13 +1,12 @@ -'use strict'; - -const should = require('chai').should(); // eslint-disable-line -const ValidationError = require('../../../lib/error/validation'); +import chai from 'chai'; +const should = chai.should(); // eslint-disable-line +import ValidationError from '../../../dist/error/validation'; +import SchemaTypeArray from '../../../dist/types/array'; +import SchemaTypeString from '../../../dist/types/string'; +import SchemaTypeDate from '../../../dist/types/date'; +import SchemaTypeBoolean from '../../../dist/types/boolean'; describe('SchemaTypeArray', () => { - const SchemaTypeArray = require('../../../lib/types/array'); - const SchemaTypeString = require('../../../lib/types/string'); - const SchemaTypeDate = require('../../../lib/types/date'); - const SchemaTypeBoolean = require('../../../lib/types/boolean'); const type = new SchemaTypeArray('test'); it('cast()', () => { diff --git a/test/scripts/types/boolean.js b/test/scripts/types/boolean.ts similarity index 87% rename from test/scripts/types/boolean.js rename to test/scripts/types/boolean.ts index 07be269f..bf84920c 100644 --- a/test/scripts/types/boolean.js +++ b/test/scripts/types/boolean.ts @@ -1,10 +1,9 @@ -'use strict'; - -require('chai').should(); -const ValidationError = require('../../../lib/error/validation'); +import chai from 'chai'; +const should = chai.should(); // eslint-disable-line +import ValidationError from '../../../dist/error/validation'; +import SchemaTypeBoolean from '../../../dist/types/boolean'; describe('SchemaTypeBoolean', () => { - const SchemaTypeBoolean = require('../../../lib/types/boolean'); const type = new SchemaTypeBoolean('test'); it('cast()', () => { diff --git a/test/scripts/types/buffer.js b/test/scripts/types/buffer.ts similarity index 93% rename from test/scripts/types/buffer.js rename to test/scripts/types/buffer.ts index 3c6c62d9..9bae4b19 100644 --- a/test/scripts/types/buffer.js +++ b/test/scripts/types/buffer.ts @@ -1,10 +1,9 @@ -'use strict'; - -const should = require('chai').should(); -const ValidationError = require('../../../lib/error/validation'); +import chai from 'chai'; +const should = chai.should(); // eslint-disable-line +import ValidationError from '../../../dist/error/validation'; +import SchemaTypeBuffer from '../../../dist/types/buffer'; describe('SchemaTypeBuffer', () => { - const SchemaTypeBuffer = require('../../../lib/types/buffer'); const type = new SchemaTypeBuffer('test'); it('cast()', () => { diff --git a/test/scripts/types/cuid.js b/test/scripts/types/cuid.ts similarity index 72% rename from test/scripts/types/cuid.js rename to test/scripts/types/cuid.ts index f17cd89c..9023eb8a 100644 --- a/test/scripts/types/cuid.js +++ b/test/scripts/types/cuid.ts @@ -1,10 +1,9 @@ -'use strict'; - -const should = require('chai').should(); // eslint-disable-line -const ValidationError = require('../../../lib/error/validation'); +import chai from 'chai'; +const should = chai.should(); // eslint-disable-line +import ValidationError from '../../../dist/error/validation'; +import SchemaTypeCUID from '../../../dist/types/cuid'; describe('SchemaTypeCUID', () => { - const SchemaTypeCUID = require('../../../lib/types/cuid'); const type = new SchemaTypeCUID('test'); it('cast()', () => { diff --git a/test/scripts/types/date.js b/test/scripts/types/date.ts similarity index 91% rename from test/scripts/types/date.js rename to test/scripts/types/date.ts index 912bec84..da80826b 100644 --- a/test/scripts/types/date.js +++ b/test/scripts/types/date.ts @@ -1,10 +1,9 @@ -'use strict'; - -const should = require('chai').should(); // eslint-disable-line -const ValidationError = require('../../../lib/error/validation'); +import chai from 'chai'; +const should = chai.should(); // eslint-disable-line +import ValidationError from '../../../dist/error/validation'; +import SchemaTypeDate from '../../../dist/types/date'; describe('SchemaTypeDate', () => { - const SchemaTypeDate = require('../../../lib/types/date'); const type = new SchemaTypeDate('test'); it('cast()', () => { @@ -86,11 +85,11 @@ describe('SchemaTypeDate', () => { it('u$inc()', () => { type.u$inc(new Date(1e8), 1).should.eql(new Date(1e8 + 1)); - should.not.exist(undefined, 1); + should.not.exist(undefined, '1'); }); it('u$dec()', () => { type.u$dec(new Date(1e8), 1).should.eql(new Date(1e8 - 1)); - should.not.exist(undefined, 1); + should.not.exist(undefined, '1'); }); }); diff --git a/test/scripts/types/enum.js b/test/scripts/types/enum.ts similarity index 72% rename from test/scripts/types/enum.js rename to test/scripts/types/enum.ts index 166c3888..ded35ace 100644 --- a/test/scripts/types/enum.js +++ b/test/scripts/types/enum.ts @@ -1,11 +1,9 @@ -'use strict'; - -require('chai').should(); -const ValidationError = require('../../../lib/error/validation'); +import chai from 'chai'; +const should = chai.should(); // eslint-disable-line +import ValidationError from '../../../dist/error/validation'; +import SchemaTypeEnum from '../../../dist/types/enum'; describe('SchemaTypeEnum', () => { - const SchemaTypeEnum = require('../../../lib/types/enum'); - it('validate()', () => { const type = new SchemaTypeEnum('test', {elements: ['foo', 'bar', 'baz']}); diff --git a/test/scripts/types/integer.js b/test/scripts/types/integer.ts similarity index 85% rename from test/scripts/types/integer.js rename to test/scripts/types/integer.ts index 9589fad1..ffe341f2 100644 --- a/test/scripts/types/integer.js +++ b/test/scripts/types/integer.ts @@ -1,10 +1,9 @@ -'use strict'; - -require('chai').should(); -const ValidationError = require('../../../lib/error/validation'); +import chai from 'chai'; +const should = chai.should(); // eslint-disable-line +import ValidationError from '../../../dist/error/validation'; +import SchemaTypeInteger from '../../../dist/types/integer'; describe('SchemaTypeInteger', () => { - const SchemaTypeInteger = require('../../../lib/types/integer'); const type = new SchemaTypeInteger('test'); it('cast()', () => { diff --git a/test/scripts/types/number.js b/test/scripts/types/number.ts similarity index 90% rename from test/scripts/types/number.js rename to test/scripts/types/number.ts index 6442196e..ae56afbf 100644 --- a/test/scripts/types/number.js +++ b/test/scripts/types/number.ts @@ -1,10 +1,9 @@ -'use strict'; - -require('chai').should(); -const ValidationError = require('../../../lib/error/validation'); +import chai from 'chai'; +const should = chai.should(); // eslint-disable-line +import ValidationError from '../../../dist/error/validation'; +import SchemaTypeNumber from '../../../dist/types/number'; describe('SchemaTypeNumber', () => { - const SchemaTypeNumber = require('../../../lib/types/number'); const type = new SchemaTypeNumber('type'); it('cast()', () => { diff --git a/test/scripts/types/object.js b/test/scripts/types/object.ts similarity index 52% rename from test/scripts/types/object.js rename to test/scripts/types/object.ts index a5cd9c74..e29b6473 100644 --- a/test/scripts/types/object.js +++ b/test/scripts/types/object.ts @@ -1,9 +1,8 @@ -'use strict'; - -const should = require('chai').should(); // eslint-disable-line +import chai from 'chai'; +const should = chai.should(); // eslint-disable-line +import SchemaTypeObject from '../../../dist/types/object'; describe('SchemaTypeObject', () => { - const SchemaTypeObject = require('../../../lib/types/object'); const type = new SchemaTypeObject('test'); it('cast() - default', () => { diff --git a/test/scripts/types/string.js b/test/scripts/types/string.ts similarity index 92% rename from test/scripts/types/string.js rename to test/scripts/types/string.ts index 2425d012..b2572c8c 100644 --- a/test/scripts/types/string.js +++ b/test/scripts/types/string.ts @@ -1,10 +1,9 @@ -'use strict'; - -require('chai').should(); -const ValidationError = require('../../../lib/error/validation'); +import chai from 'chai'; +const should = chai.should(); // eslint-disable-line +import ValidationError from '../../../dist/error/validation'; +import SchemaTypeString from '../../../dist/types/string'; describe('SchemaTypeString', () => { - const SchemaTypeString = require('../../../lib/types/string'); const type = new SchemaTypeString('test'); it('cast()', () => { diff --git a/test/scripts/types/virtual.js b/test/scripts/types/virtual.ts similarity index 85% rename from test/scripts/types/virtual.js rename to test/scripts/types/virtual.ts index 515cbbcb..ddbc83c5 100644 --- a/test/scripts/types/virtual.js +++ b/test/scripts/types/virtual.ts @@ -1,9 +1,8 @@ -'use strict'; - -const should = require('chai').should(); // eslint-disable-line +import chai from 'chai'; +const should = chai.should(); // eslint-disable-line +import SchemaTypeVirtual from '../../../dist/types/virtual'; describe('SchemaTypeVirtual', () => { - const SchemaTypeVirtual = require('../../../lib/types/virtual'); const type = new SchemaTypeVirtual('test'); it('get()', () => { @@ -38,6 +37,7 @@ describe('SchemaTypeVirtual', () => { }); type.cast(undefined, obj); + // @ts-ignore obj.test.should.eql('FOO'); }); @@ -49,6 +49,7 @@ describe('SchemaTypeVirtual', () => { }); type.validate('FOO', obj); + // @ts-ignore obj.name.should.eql('foo'); }); }); diff --git a/test/scripts/util.js b/test/scripts/util.ts similarity index 73% rename from test/scripts/util.js rename to test/scripts/util.ts index 5a23df2a..f823a0a8 100644 --- a/test/scripts/util.js +++ b/test/scripts/util.ts @@ -1,10 +1,8 @@ -'use strict'; - -const should = require('chai').should(); // eslint-disable-line +import chai from 'chai'; +const should = chai.should(); // eslint-disable-line +import * as util from '../../dist/util'; describe('util', () => { - const util = require('../../lib/util'); - it('shuffle()', () => { const src = Array(100).fill(0).map((_, i) => i); const result = util.shuffle(src); @@ -17,7 +15,7 @@ describe('util', () => { }); it('shuffle() - array must be an array', () => { - (() => util.shuffle()).should.to.throw('array must be an Array!'); + (() => util.shuffle({})).should.to.throw('array must be an Array!'); }); it('getProp()', () => { @@ -39,11 +37,11 @@ describe('util', () => { }); it('getProp() - obj must be an object', () => { - (() => util.getProp()).should.to.throw('obj must be an object!'); + (() => util.getProp('', null)).should.to.throw('obj must be an object!'); }); it('getProp() - key is required', () => { - (() => util.getProp({})).should.to.throw('key is required!'); + (() => util.getProp({}, null)).should.to.throw('key is required!'); }); it('setProp()', () => { @@ -70,11 +68,11 @@ describe('util', () => { }); it('setProp() - obj must be an object', () => { - (() => util.setProp()).should.to.throw('obj must be an object!'); + (() => util.setProp('', null, null)).should.to.throw('obj must be an object!'); }); it('setProp() - key is required', () => { - (() => util.setProp({})).should.to.throw('key is required!'); + (() => util.setProp({}, null, null)).should.to.throw('key is required!'); }); it('delProp()', () => { @@ -101,11 +99,11 @@ describe('util', () => { }); it('delProp() - obj must be an object', () => { - (() => util.delProp()).should.to.throw('obj must be an object!'); + (() => util.delProp('', null)).should.to.throw('obj must be an object!'); }); it('delProp() - key is required', () => { - (() => util.delProp({})).should.to.throw('key is required!'); + (() => util.delProp({}, null)).should.to.throw('key is required!'); }); it('setGetter()', () => { @@ -135,19 +133,20 @@ describe('util', () => { util.setGetter(obj, 'a.c.h', () => 'ach'); + // @ts-ignore obj.a.c.h.should.eql('ach'); }); it('setGetter() - obj must be an object', () => { - (() => util.setGetter()).should.to.throw('obj must be an object!'); + (() => util.setGetter('', null, null)).should.to.throw('obj must be an object!'); }); it('setGetter() - key is required', () => { - (() => util.setGetter({})).should.to.throw('key is required!'); + (() => util.setGetter({}, null, null)).should.to.throw('key is required!'); }); it('setGetter() - fn must be a function', () => { - (() => util.setGetter({}, 'test')).should.to.throw('fn must be a function!'); + (() => util.setGetter({}, 'test', {})).should.to.throw('fn must be a function!'); }); it('arr2obj()', () => { @@ -155,7 +154,7 @@ describe('util', () => { }); it('arr2obj() - arr must be an array', () => { - (() => util.arr2obj()).should.to.throw('arr must be an array!'); + (() => util.arr2obj({}, null)).should.to.throw('arr must be an array!'); }); it('reverse()', () => { @@ -165,7 +164,7 @@ describe('util', () => { }); it('reverse() - arr must be an array', () => { - (() => util.reverse()).should.to.throw('arr must be an array!'); + (() => util.reverse({})).should.to.throw('arr must be an array!'); }); it('parseArgs()', () => { diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 00000000..117e4a71 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,20 @@ +{ + "compilerOptions": { + "outDir": "./dist", + "module": "commonjs", + "target": "es6", + "sourceMap": true, + "declaration": true, + "esModuleInterop": true, + "types": [ + "node", + "mocha" + ] + }, + "include": [ + "./src/**/*" + ], + "exclude": [ + "node_modules" + ] +} \ No newline at end of file