From 3ea048d6cba79db15fcb79a09c9eeb389f6aac68 Mon Sep 17 00:00:00 2001 From: Zachary Golba Date: Sun, 18 Dec 2016 13:58:15 -0500 Subject: [PATCH 01/12] refactor: optimize util performance (#580) --- .eslintrc.json | 1 - src/interfaces.js | 4 ++ .../controller/test/controller.test.js | 33 ++++++++------ .../controller/test/params-to-query.test.js | 30 ++++++------- src/packages/database/test/model.test.js | 1 - src/packages/database/test/query.test.js | 1 - src/packages/luxify/test/luxify.test.js | 22 +++++----- .../router/route/params/test/params.test.js | 11 ++--- src/packages/router/test/namespace.test.js | 13 ++---- .../server/utils/create-server-error.js | 26 +++++------ src/utils/compact.js | 25 ++++++----- src/utils/entries.js | 17 ++++--- src/utils/has-own-property.js | 6 +-- src/utils/map-to-object.js | 17 ++++--- src/utils/merge.js | 30 +++++-------- src/utils/omit.js | 15 ++++--- src/utils/pick.js | 17 ++++--- src/utils/promise-hash.js | 34 +++++++------- src/utils/set-type.js | 23 ---------- src/utils/test/set-type.test.js | 11 ----- src/utils/transform-keys.js | 44 +++++++++---------- 21 files changed, 173 insertions(+), 208 deletions(-) delete mode 100644 src/utils/set-type.js delete mode 100644 src/utils/test/set-type.test.js diff --git a/.eslintrc.json b/.eslintrc.json index 3a33d712..2f5b7e93 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -31,7 +31,6 @@ "max-len": ["error", 80], "arrow-parens": 0, "comma-dangle": [2, "only-multiline"], - "prefer-reflect": 2, "global-require": 0, "class-methods-use-this": 0, "no-restricted-syntax": [ diff --git a/src/interfaces.js b/src/interfaces.js index 44f7d8d8..68b391d3 100644 --- a/src/interfaces.js +++ b/src/interfaces.js @@ -13,3 +13,7 @@ export interface Chain { value(): T; construct>(constructor: V): Chain; } + +export type ObjectMap = { + [key: K]: V; +}; diff --git a/src/packages/controller/test/controller.test.js b/src/packages/controller/test/controller.test.js index 112c4812..588e73af 100644 --- a/src/packages/controller/test/controller.test.js +++ b/src/packages/controller/test/controller.test.js @@ -6,7 +6,6 @@ import Controller from '../index'; import Serializer from '../../serializer'; import { Model } from '../../database'; -import setType from '../../../utils/set-type'; import { getTestApp } from '../../../../test/utils/get-test-app'; import type { Request, Response } from '../../server'; @@ -56,7 +55,8 @@ describe('module "controller"', () => { }); describe('#index()', () => { - const createRequest = (params = {}): Request => setType(() => ({ + // $FlowIgnore + const createRequest = (params = {}): Request => ({ params, route: { controller: subject @@ -72,7 +72,7 @@ describe('module "controller"', () => { number: 1 } } - })); + }); it('returns an array of records', async () => { const request = createRequest(); @@ -157,7 +157,8 @@ describe('module "controller"', () => { }); describe('#show()', () => { - const createRequest = (params = {}): Request => setType(() => ({ + // $FlowIgnore + const createRequest = (params = {}): Request => ({ params, route: { controller: subject @@ -167,7 +168,7 @@ describe('module "controller"', () => { posts: attributes } } - })); + }); it('returns a single record', async () => { const request = createRequest({ id: 1 }); @@ -237,7 +238,8 @@ describe('module "controller"', () => { describe('#create()', () => { let result: Model; - const createRequest = (params = {}): Request => setType(() => ({ + // $FlowIgnore + const createRequest = (params = {}): Request => ({ params, url: { pathname: '/posts' @@ -257,9 +259,10 @@ describe('module "controller"', () => { users: ['id'] } } - })); + }); - const createResponse = (): Response => setType(() => ({ + // $FlowIgnore + const createResponse = (): Response => ({ headers: new Map(), statusCode: 200, @@ -270,7 +273,7 @@ describe('module "controller"', () => { getHeader(key: string): string | void { return this.headers.get(key); } - })); + }); afterEach(async () => { await result.destroy(); @@ -361,7 +364,9 @@ describe('module "controller"', () => { describe('#update()', () => { let record: Model; - const createRequest = (params = {}): Request => setType(() => ({ + + // $FlowIgnore + const createRequest = (params = {}): Request => ({ params, route: { controller: subject @@ -371,7 +376,7 @@ describe('module "controller"', () => { posts: attributes } } - })); + }); beforeEach(async () => { record = await Post.create({ @@ -545,7 +550,9 @@ describe('module "controller"', () => { describe('#destroy()', () => { let record: Model; - const createRequest = (params = {}): Request => setType(() => ({ + + // $FlowIgnore + const createRequest = (params = {}): Request => ({ params, route: { controller: subject @@ -555,7 +562,7 @@ describe('module "controller"', () => { posts: attributes } } - })); + }); before(async () => { record = await Post.create({ diff --git a/src/packages/controller/test/params-to-query.test.js b/src/packages/controller/test/params-to-query.test.js index 7daf9d56..8f7d5d5b 100644 --- a/src/packages/controller/test/params-to-query.test.js +++ b/src/packages/controller/test/params-to-query.test.js @@ -5,32 +5,30 @@ import { describe, it, before } from 'mocha'; import type { Model } from '../../database'; import type { Request$params } from '../../server'; import merge from '../../../utils/merge'; -import setType from '../../../utils/set-type'; import paramsToQuery from '../utils/params-to-query'; import { getTestApp } from '../../../../test/utils/get-test-app'; describe('module "controller"', () => { describe('util paramsToQuery()', () => { let Post: Class; - const createParams = (obj: Object): Request$params => setType(() => { - return merge({ - sort: 'createdAt', - filter: {}, - fields: { - posts: [ - 'body', - 'title', - 'createdAt', - 'updatedAt' - ] - } - }, obj); - }); + const createParams = (obj: Object): Request$params => merge({ + sort: 'createdAt', + filter: {}, + fields: { + posts: [ + 'body', + 'title', + 'createdAt', + 'updatedAt' + ] + } + }, obj); before(async () => { const { models } = await getTestApp(); - Post = setType(() => models.get('post')); + // $FlowIgnore + Post = models.get('post'); }); it('returns the correct params object', () => { diff --git a/src/packages/database/test/model.test.js b/src/packages/database/test/model.test.js index 1be19a8b..39e229bb 100644 --- a/src/packages/database/test/model.test.js +++ b/src/packages/database/test/model.test.js @@ -7,7 +7,6 @@ import Model from '../model'; import Query, { RecordNotFoundError } from '../query'; import { ValidationError } from '../validation'; -import setType from '../../../utils/set-type'; import { getTestApp } from '../../../../test/utils/get-test-app'; describe('module "database/model"', () => { diff --git a/src/packages/database/test/query.test.js b/src/packages/database/test/query.test.js index 3863890c..b8918006 100644 --- a/src/packages/database/test/query.test.js +++ b/src/packages/database/test/query.test.js @@ -5,7 +5,6 @@ import { it, describe, before, beforeEach } from 'mocha'; import Query from '../query'; import Model from '../model'; -import setType from '../../../utils/set-type'; import { getTestApp } from '../../../../test/utils/get-test-app'; describe('module "database/query"', () => { diff --git a/src/packages/luxify/test/luxify.test.js b/src/packages/luxify/test/luxify.test.js index 0d0484d3..5ef89ecf 100644 --- a/src/packages/luxify/test/luxify.test.js +++ b/src/packages/luxify/test/luxify.test.js @@ -2,21 +2,21 @@ import { expect } from 'chai'; import { it, describe } from 'mocha'; -import luxify from '../index'; - import K from '../../../utils/k'; -import setType from '../../../utils/set-type'; +import luxify from '../index'; +import type { Request, Response } from '../../server'; describe('module "luxify"', () => { describe('#luxify()', () => { - const [request, response] = setType(() => [ - {}, - { - getHeader: K, - setHeader: K, - removeHeader: K - } - ]); + // $FlowIgnore + const request: Request = {}; + + // $FlowIgnore + const response: Response = { + getHeader: K, + setHeader: K, + removeHeader: K + }; it('promisifies a callback based middleware function', () => { const subject = luxify((req, res, next) => { diff --git a/src/packages/router/route/params/test/params.test.js b/src/packages/router/route/params/test/params.test.js index 5b0ede3a..c28743c2 100644 --- a/src/packages/router/route/params/test/params.test.js +++ b/src/packages/router/route/params/test/params.test.js @@ -4,7 +4,6 @@ import { it, describe, before } from 'mocha'; import { paramsFor, defaultParamsFor } from '../index'; -import setType from '../../../../../utils/set-type'; import { getTestApp } from '../../../../../../test/utils/get-test-app'; import type Controller from '../../../../controller'; @@ -16,9 +15,8 @@ describe('module "router/route/params"', () => { before(async () => { const { controllers } = await getTestApp(); - getController = (name: string): Controller => setType(() => { - return controllers.get(name); - }); + // $FlowIgnore + getController = (name: string): Controller => controllers.get(name); }); describe('with model-less controller', () => { @@ -45,9 +43,8 @@ describe('module "router/route/params"', () => { before(async () => { const { controllers } = await getTestApp(); - getController = (name: string): Controller => setType(() => { - return controllers.get(name); - }); + // $FlowIgnore + getController = (name: string): Controller => controllers.get(name); }); describe('with collection route', () => { diff --git a/src/packages/router/test/namespace.test.js b/src/packages/router/test/namespace.test.js index fbcd91bc..f352372c 100644 --- a/src/packages/router/test/namespace.test.js +++ b/src/packages/router/test/namespace.test.js @@ -3,10 +3,7 @@ import { expect } from 'chai'; import { it, describe, before, beforeEach } from 'mocha'; import Namespace from '../namespace'; - -import setType from '../../../utils/set-type'; import { getTestApp } from '../../../../test/utils/get-test-app'; - import type Controller from '../../controller'; describe('module "router/namespace"', () => { @@ -30,17 +27,15 @@ describe('module "router/namespace"', () => { controllers = app.controllers; - controller = setType(() => { - return controllers.get('admin/application'); - }); + // $FlowIgnore + controller = controllers.get('admin/application'); createRootNamespace = (): Namespace => new Namespace({ controllers, path: '/', name: 'root', - controller: setType(() => { - return app.controllers.get('application'); - }) + // $FlowIgnore + controller: controllers.get('application') }); }); diff --git a/src/packages/server/utils/create-server-error.js b/src/packages/server/utils/create-server-error.js index dc5c96c4..f461f238 100644 --- a/src/packages/server/utils/create-server-error.js +++ b/src/packages/server/utils/create-server-error.js @@ -1,5 +1,4 @@ // @flow -import setType from '../../../utils/set-type'; import type { Server$Error } from '../interfaces'; /** @@ -9,20 +8,19 @@ export default function createServerError( Target: Class, statusCode: number ): Class & Class { - return setType(() => { - const ServerError = class extends Target { - statusCode: number; + const ServerError = class extends Target { + statusCode: number; - constructor(...args: Array) { - super(...args); - this.statusCode = statusCode; - } - }; + constructor(...args: Array) { + super(...args); + this.statusCode = statusCode; + } + }; - Reflect.defineProperty(ServerError, 'name', { - value: Target.name - }); - - return ServerError; + Reflect.defineProperty(ServerError, 'name', { + value: Target.name }); + + // $FlowIgnore + return ServerError; } diff --git a/src/utils/compact.js b/src/utils/compact.js index 7dfcd364..efbad15e 100644 --- a/src/utils/compact.js +++ b/src/utils/compact.js @@ -1,23 +1,24 @@ // @flow import isNull from './is-null'; import entries from './entries'; -import setType from './set-type'; import isUndefined from './is-undefined'; /** * @private */ export default function compact>(source: T): T { - return setType(() => { - if (Array.isArray(source)) { - return source.filter(value => !isNull(value) && !isUndefined(value)); - } + if (Array.isArray(source)) { + return source.filter(value => !isNull(value) && !isUndefined(value)); + } - return entries(source) - .filter(([, value]) => !isNull(value) && !isUndefined(value)) - .reduce((result, [key, value]) => ({ - ...result, - [key]: value - }), {}); - }); + // $FlowIgnore + return entries(source) + .filter(([, value]) => !isNull(value) && !isUndefined(value)) + .reduce((obj, [key, value]) => { + const result = obj; + + result[key] = value; + + return result; + }, {}); } diff --git a/src/utils/entries.js b/src/utils/entries.js index c62854ed..712171cb 100644 --- a/src/utils/entries.js +++ b/src/utils/entries.js @@ -9,11 +9,18 @@ export default function entries(source: Object): Array<[string, any]> { return Object.entries(source); } - return Object.keys(source).reduce((result, key) => { - const value = Reflect.get(source, key); + const keys = Object.keys(source); - result.push([key, value]); + return keys.reduce((prev, key) => { + const next = prev; + const entry = new Array(2); - return result; - }, []); + entry[0] = key; + entry[1] = source[key]; + + // $FlowIgnore + next[next.length] = entry; + + return next; + }, new Array(keys.length)); } diff --git a/src/utils/has-own-property.js b/src/utils/has-own-property.js index 6444e678..7e229c42 100644 --- a/src/utils/has-own-property.js +++ b/src/utils/has-own-property.js @@ -1,8 +1,4 @@ // @flow export default function hasOwnProperty(target: Object, key: string): boolean { - return Reflect.apply( - Object.prototype.hasOwnProperty, - target, - [key] - ); + return Object.prototype.hasOwnProperty.call(target, key); } diff --git a/src/utils/map-to-object.js b/src/utils/map-to-object.js index 3e6ef431..ca609e6d 100644 --- a/src/utils/map-to-object.js +++ b/src/utils/map-to-object.js @@ -1,11 +1,14 @@ // @flow -export default function mapToObject( - source: Map -): { [key: string]: T } { +import type { ObjectMap } from '../interfaces'; + +export default function mapToObject(source: Map): ObjectMap { return Array .from(source) - .reduce((obj, [key, value]) => ({ - ...obj, - [String(key)]: value - }), {}); + .reduce((obj, [key, value]) => { + const result = obj; + + result[String(key)] = value; + + return result; + }, {}); } diff --git a/src/utils/merge.js b/src/utils/merge.js index 03efde8f..e2b8aa13 100644 --- a/src/utils/merge.js +++ b/src/utils/merge.js @@ -1,33 +1,27 @@ // @flow import entries from './entries'; -import setType from './set-type'; import isObject from './is-object'; - -function hasOwnProperty(target: Object, key: string): boolean { - return Reflect.apply(Object.prototype.hasOwnProperty, target, [key]); -} +import hasOwnProperty from './has-own-property'; /** * @private */ export default function merge(dest: T, source: U): T & U { - return setType(() => entries(source).reduce((result, [key, value]) => { + // $FlowIgnore + return entries(source).reduce((obj, [key, value]) => { + const result = obj; + let mergeValue = value; + if (hasOwnProperty(result, key) && isObject(value)) { - const currentValue = Reflect.get(result, key); + const currentValue = result[key]; if (isObject(currentValue)) { - return { - ...result, - [key]: merge(currentValue, value) - }; + mergeValue = merge(currentValue, value); } } - return { - ...result, - [key]: value - }; - }, { - ...dest - })); + result[key] = mergeValue; + + return result; + }, { ...dest }); } diff --git a/src/utils/omit.js b/src/utils/omit.js index b4ef6070..68a6b2ba 100644 --- a/src/utils/omit.js +++ b/src/utils/omit.js @@ -1,15 +1,18 @@ // @flow import entries from './entries'; -import setType from './set-type'; /** * @private */ export default function omit(src: T, ...omitted: Array): T { - return setType(() => entries(src) + // $FlowIgnore + return entries(src) .filter(([key]) => omitted.indexOf(key) < 0) - .reduce((result, [key, value]: [string, mixed]) => ({ - ...result, - [key]: value - }), {})); + .reduce((obj, [key, value]) => { + const result = obj; + + result[key] = value; + + return result; + }, {}); } diff --git a/src/utils/pick.js b/src/utils/pick.js index 580d80ec..d5a192ff 100644 --- a/src/utils/pick.js +++ b/src/utils/pick.js @@ -1,15 +1,18 @@ // @flow -import setType from './set-type'; /** * @private */ export default function pick(src: T, ...keys: Array): T { - return setType(() => keys - .map((key): [string, mixed] => [key, Reflect.get(src, key)]) + // $FlowIgnore + return keys + .map((key): [string, mixed] => [key, src[key]]) .filter(([, value]) => typeof value !== 'undefined') - .reduce((result, [key, value]) => ({ - ...result, - [key]: value - }), {})); + .reduce((obj, [key, value]) => { + const result = obj; + + result[key] = value; + + return result; + }, {}); } diff --git a/src/utils/promise-hash.js b/src/utils/promise-hash.js index 07aced4b..4c157777 100644 --- a/src/utils/promise-hash.js +++ b/src/utils/promise-hash.js @@ -5,24 +5,24 @@ import entries from './entries'; * @private */ export default function promiseHash(promises: Object): Promise { - if (Object.keys(promises).length) { + const iter = entries(promises); + + if (iter.length) { return Promise.all( - entries(promises) - .map(([key, promise]: [string, Promise]) => ( - new Promise((resolve, reject) => { - if (promise && typeof promise.then === 'function') { - promise - .then((value) => resolve({ [key]: value })) - .catch(reject); - } else { - resolve({ [key]: promise }); - } - }) - )) - ).then((objects) => objects.reduce((hash, object) => ({ - ...hash, - ...object - }), {})); + iter.map(([key, promise]: [string, Promise]) => ( + new Promise((resolve, reject) => { + if (promise && typeof promise.then === 'function') { + promise + .then((value) => resolve({ [key]: value })) + .catch(reject); + } else { + resolve({ [key]: promise }); + } + }) + )) + ).then(objects => ( + Object.assign({}, ...objects) + )); } return Promise.resolve({}); diff --git a/src/utils/set-type.js b/src/utils/set-type.js deleted file mode 100644 index e67a6f5b..00000000 --- a/src/utils/set-type.js +++ /dev/null @@ -1,23 +0,0 @@ -// @flow - -/** - * Use this util as a brute force way of tricking flow into understanding intent - * to extend or combine a type in a polymorphic function. - * - * In essence, this function allows you to declare your types for a high order - * function that wraps the inner logic of this function without flow throwing - * any type errors. This allows you to properly set the return value of the - * high order function to whatever you like so consumers of the high order - * function can still benifit from type inference and safety as long as the - * return value type declaration is 100% accurate. - * - * WARNING: - * This function should rarely be used as it requires a good understanding of - * the flow type system to ensure that the function this util wraps is still - * type safe. - * - * @private - */ -export default function setType(fn: () => any): any { - return fn(); -} diff --git a/src/utils/test/set-type.test.js b/src/utils/test/set-type.test.js deleted file mode 100644 index c58cac83..00000000 --- a/src/utils/test/set-type.test.js +++ /dev/null @@ -1,11 +0,0 @@ -// @flow -import { expect } from 'chai'; -import { it, describe } from 'mocha'; - -import setType from '../set-type'; - -describe('util setType()', () => { - it('returns the function call of the first and only argument', () => { - expect(setType(() => 'Test')).to.equal('Test'); - }); -}); diff --git a/src/utils/transform-keys.js b/src/utils/transform-keys.js index d292b582..060841dc 100644 --- a/src/utils/transform-keys.js +++ b/src/utils/transform-keys.js @@ -2,7 +2,6 @@ import { camelize, dasherize } from 'inflection'; import entries from './entries'; -import setType from './set-type'; import underscore from './underscore'; /** @@ -13,32 +12,29 @@ export function transformKeys>( transformer: (key: string) => string, deep: boolean = false ): T { - return setType(() => { - if (Array.isArray(source)) { - return source.slice(0); - } else if (source && typeof source === 'object') { - return entries(source).reduce((result, [key, value]) => { - const recurse = deep - && value - && typeof value === 'object' - && !Array.isArray(value); + if (Array.isArray(source)) { + return source.slice(0); + } else if (source && typeof source === 'object') { + // $FlowIgnore + return entries(source).reduce((obj, [key, value]) => { + const result = obj; + const recurse = deep + && value + && typeof value === 'object' + && !Array.isArray(value); - if (recurse) { - return { - ...result, - [transformer(key)]: transformKeys(value, transformer, true) - }; - } + if (recurse) { + result[transformer(key)] = transformKeys(value, transformer, true); + } else { + result[transformer(key)] = value; + } - return { - ...result, - [transformer(key)]: value - }; - }, {}); - } + return result; + }, {}); + } - return {}; - }); + // $FlowIgnore + return {}; } /** From e619f23bb28db9b5947a9b29a90bb491a6ee2447 Mon Sep 17 00:00:00 2001 From: Zachary Golba Date: Sun, 18 Dec 2016 14:03:23 -0500 Subject: [PATCH 02/12] refactor: do not fork more processes than number of cpu cores (#582) --- src/packages/cli/commands/serve.js | 9 +++-- src/packages/pm/cluster/index.js | 56 +++++++++++------------------- 2 files changed, 28 insertions(+), 37 deletions(-) diff --git a/src/packages/cli/commands/serve.js b/src/packages/cli/commands/serve.js index 2f3016e0..29329f64 100644 --- a/src/packages/cli/commands/serve.js +++ b/src/packages/cli/commands/serve.js @@ -24,6 +24,7 @@ export async function serve({ const load = createLoader(CWD); const { logging } = load('config'); const logger = new Logger(logging); + let maxWorkers; if (hot) { const watcher = await watch(CWD); @@ -34,11 +35,15 @@ export async function serve({ }); } + if (!cluster) { + maxWorkers = 1; + } + createCluster({ logger, + maxWorkers, path: CWD, - port: PORT, - maxWorkers: cluster ? undefined : 1 + port: PORT }).once('ready', () => { logger.info(`Lux Server listening on port: ${cyan(`${PORT}`)}`); }); diff --git a/src/packages/pm/cluster/index.js b/src/packages/pm/cluster/index.js index 4a12e741..3f215a9a 100644 --- a/src/packages/pm/cluster/index.js +++ b/src/packages/pm/cluster/index.js @@ -9,6 +9,7 @@ import { red, green } from 'chalk'; import { NODE_ENV } from '../../../constants'; import { line } from '../../logger'; +import { freezeProps } from '../../freezeable'; import omit from '../../../utils/omit'; import range from '../../../utils/range'; import { composeAsync } from '../../../utils/compose'; @@ -31,45 +32,30 @@ class Cluster extends EventEmitter { maxWorkers: number; constructor({ path, port, logger, maxWorkers }: Cluster$opts) { + let numCPUs = os.cpus().length; + super(); - Object.defineProperties(this, { - path: { - value: path, - writable: false, - enumerable: true, - configurable: false - }, - - port: { - value: port, - writable: false, - enumerable: true, - configurable: false - }, - - logger: { - value: logger, - writable: false, - enumerable: true, - configurable: false - }, - - workers: { - value: new Set(), - writable: false, - enumerable: true, - configurable: false - }, - - maxWorkers: { - value: maxWorkers || os.cpus().length, - writable: false, - enumerable: true, - configurable: false - } + if (numCPUs > 2 && numCPUs % 2 === 0) { + numCPUs /= 2; + } + + Object.assign(this, { + path, + port, + logger, + workers: new Set(), + maxWorkers: maxWorkers || numCPUs }); + freezeProps(this, true, + 'path', + 'port', + 'logger', + 'workers', + 'maxWorkers' + ); + cluster.setupMaster({ exec: joinPath(path, 'dist', 'boot.js') }); From 723ba191615f764cf4e6983f3195e6175b8ee490 Mon Sep 17 00:00:00 2001 From: Zachary Golba Date: Sun, 18 Dec 2016 14:13:36 -0500 Subject: [PATCH 03/12] refactor: make serializer methods synchronous --- src/packages/jsonapi/interfaces.js | 3 +- src/packages/serializer/index.js | 129 ++++++++++++++--------------- 2 files changed, 63 insertions(+), 69 deletions(-) diff --git a/src/packages/jsonapi/interfaces.js b/src/packages/jsonapi/interfaces.js index c607c29e..1d8badf3 100644 --- a/src/packages/jsonapi/interfaces.js +++ b/src/packages/jsonapi/interfaces.js @@ -6,7 +6,7 @@ type JSONAPI$value = | ?JSONAPI$BaseObject | Array; -interface JSONAPI$BaseObject { +type JSONAPI$BaseObject = { [key: void | string]: JSONAPI$value; meta?: JSONAPI$BaseObject; } @@ -75,7 +75,6 @@ export interface JSONAPI$ErrorObject { } export interface JSONAPI$Document { - // $FlowIgnore data?: Array | JSONAPI$ResourceObject; links?: JSONAPI$DocumentLinks; errors?: Array; diff --git a/src/packages/serializer/index.js b/src/packages/serializer/index.js index 245f44eb..2cb9f3ad 100644 --- a/src/packages/serializer/index.js +++ b/src/packages/serializer/index.js @@ -5,7 +5,6 @@ import { VERSION } from '../jsonapi'; import { freezeProps } from '../freezeable'; import uniq from '../../utils/uniq'; import underscore from '../../utils/underscore'; -import promiseHash from '../../utils/promise-hash'; import { dasherizeKeys } from '../../utils/transform-keys'; import type { Model } from '../database'; // eslint-disable-line no-unused-vars import type { // eslint-disable-line no-duplicate-imports @@ -467,7 +466,7 @@ class Serializer { * * @private */ - async format({ + format({ data, links, domain, @@ -477,24 +476,22 @@ class Serializer { links: JSONAPI$DocumentLinks; domain: string; include: Array; - }): Promise { + }): JSONAPI$Document { let serialized = {}; const included: Array = []; if (Array.isArray(data)) { serialized = { - data: await Promise.all( - data.map(item => this.formatOne({ - item, - domain, - include, - included - })) - ) + data: data.map(item => this.formatOne({ + item, + domain, + include, + included + })) }; } else { serialized = { - data: await this.formatOne({ + data: this.formatOne({ domain, include, included, @@ -514,7 +511,6 @@ class Serializer { return { ...serialized, links, - jsonapi: { version: VERSION } @@ -553,12 +549,11 @@ class Serializer { * relationships should be formatted and included in the returned * [JSON API](http://jsonapi.org) resource object. * - * @return {Promise} Resolves with a [JSON API](http://jsonapi.org) resource - * object. + * @return {Object} A [JSON API](http://jsonapi.org) resource object. * * @private */ - async formatOne({ + formatOne({ item, links, domain, @@ -572,17 +567,22 @@ class Serializer { include: Array; included: Array; formatRelationships?: boolean - }): Promise { + }): JSONAPI$ResourceObject { const { resourceName: type } = item; const id = String(item.getPrimaryKey()); - let relationships = {}; - const attributes = dasherizeKeys( - item.getAttributes( - ...Object - .keys(item.rawColumnData) - .filter(key => this.attributes.includes(key)) - ) + Array + .from(item.currentChangeSet) + .reduce((obj, [key, value]) => { + if (this.attributes.includes(key)) { + return { + ...obj, + [key]: value + }; + } + + return obj; + }, {}) ); const serialized: JSONAPI$ResourceObject = { @@ -591,46 +591,42 @@ class Serializer { attributes }; - if (formatRelationships) { - relationships = await promiseHash( - [...this.hasOne, ...this.hasMany].reduce((hash, name) => ({ - ...hash, - - [dasherize(underscore(name))]: (async () => { - const related = await Reflect.get(item, name); - - if (Array.isArray(related)) { - return { - data: await Promise.all( - related.map(async (relatedItem) => { - const { - data: relatedData - } = await this.formatRelationship({ - domain, - included, - item: relatedItem, - include: include.includes(name) - }); - - return relatedData; - }) - ) - }; - } else if (related && related.id) { - return this.formatRelationship({ - domain, - included, - item: related, - include: include.includes(name) - }); - } + let relationships = {}; - return { - data: null + if (formatRelationships) { + relationships = this.hasOne + .concat(this.hasMany) + .reduce((obj, name) => { + const related = item.currentChangeSet.get(name); + let data = { + data: null + }; + + if (Array.isArray(related)) { + data = { + data: related.map(relatedItem => ( + this.formatRelationship({ + domain, + included, + item: relatedItem, + include: include.includes(name) + }).data + )) }; - })() - }), {}) - ); + } else if (related && related.id) { + data = this.formatRelationship({ + domain, + included, + item: related, + include: include.includes(name) + }); + } + + return { + ...obj, + [dasherize(underscore(name))]: data + }; + }, {}); } if (Object.keys(relationships).length) { @@ -677,12 +673,11 @@ class Serializer { * http://jsonapi.org) resource objects that will be added to the top level * included array of a [JSON API](http://jsonapi.org) document object. * - * @return {Promise} Resolves with a [JSON API](http://jsonapi.org) - * relationship object. + * @return {Object} A [JSON API](http://jsonapi.org) relationship object. * * @private */ - async formatRelationship({ + formatRelationship({ item, domain, include, @@ -692,7 +687,7 @@ class Serializer { domain: string; include: boolean; included: Array; - }): Promise { + }): JSONAPI$RelationshipObject { const { namespace } = this; const { resourceName: type, constructor: { serializer } } = item; const id = String(item.getPrimaryKey()); @@ -710,7 +705,7 @@ class Serializer { if (include) { included.push( - await serializer.formatOne({ + serializer.formatOne({ item, domain, include: [], From 39565477112b3b7a903ba3f885ada4595bbaf553 Mon Sep 17 00:00:00 2001 From: Zachary Golba Date: Sun, 18 Dec 2016 15:59:03 -0500 Subject: [PATCH 04/12] refactor: do not use reflect --- src/packages/application/initialize.js | 4 +- .../application/utils/create-controller.js | 7 +- .../application/utils/create-serializer.js | 7 +- src/packages/cli/commands/repl.js | 5 +- .../cli/generator/utils/generator-for.js | 2 +- src/packages/cli/templates/model.js | 2 +- .../compiler/utils/create-manifest.js | 2 +- src/packages/controller/index.js | 2 +- .../controller/test/controller.test.js | 44 +++-- .../controller/utils/params-to-query.js | 2 +- .../controller/utils/resolve-relationships.js | 21 ++- src/packages/database/initialize.js | 4 +- src/packages/database/model/index.js | 30 +-- .../database/model/initialize-class.js | 15 +- .../database/model/utils/get-columns.js | 18 +- .../database/model/utils/persistence.js | 23 ++- src/packages/database/model/utils/validate.js | 2 +- src/packages/database/query/index.js | 6 +- src/packages/database/query/runner/index.js | 5 +- .../query/runner/utils/build-results.js | 172 +++++++++--------- .../query/runner/utils/get-find-param.js | 3 +- .../database/query/utils/scopes-for.js | 27 +-- .../database/relationship/utils/getters.js | 5 +- .../relationship/utils/inverse-setters.js | 47 +++-- .../database/relationship/utils/setters.js | 13 +- .../relationship/utils/unassociate.js | 9 +- .../relationship/utils/update-relationship.js | 10 +- src/packages/database/test/database.test.js | 10 +- src/packages/database/test/model.test.js | 18 +- .../database/test/relationship.test.js | 24 +-- src/packages/database/utils/connect.js | 5 +- src/packages/freezeable/utils/freeze.js | 43 +++-- src/packages/fs/test/fs.test.js | 20 +- src/packages/loader/test/loader.test.js | 6 +- src/packages/loader/utils/bundle-for.js | 36 ++-- src/packages/logger/index.js | 2 +- .../logger/request-logger/templates.js | 8 +- .../logger/request-logger/utils/log-text.js | 2 +- src/packages/logger/test/logger.test.js | 2 +- src/packages/luxify/index.js | 6 +- src/packages/luxify/test/luxify.test.js | 8 +- .../luxify/utils/create-response-proxy.js | 2 +- .../router/definitions/context/index.js | 8 +- .../context/utils/create-definition-group.js | 49 ++++- .../context/utils/create-definition.js | 3 +- src/packages/router/definitions/index.js | 2 +- src/packages/router/definitions/interfaces.js | 1 + src/packages/router/index.js | 8 +- src/packages/router/resource/index.js | 2 +- .../router/route/action/enhancers/resource.js | 2 +- .../route/action/enhancers/track-perf.js | 2 +- src/packages/router/route/index.js | 3 +- .../route/params/parameter-group/index.js | 2 +- .../utils/has-required-params.js | 2 +- .../serializer/test/serializer.test.js | 40 ++-- .../parser/utils/parse-nested-object.js | 2 +- src/packages/server/responder/index.js | 12 +- src/packages/server/response/index.js | 11 +- src/packages/server/response/interfaces.js | 3 +- .../server/utils/create-server-error.js | 2 +- src/utils/chain.js | 3 +- src/utils/proxy.js | 5 +- src/utils/test/k.test.js | 6 +- 63 files changed, 483 insertions(+), 364 deletions(-) diff --git a/src/packages/application/initialize.js b/src/packages/application/initialize.js index f54662ae..8b23d48c 100644 --- a/src/packages/application/initialize.js +++ b/src/packages/application/initialize.js @@ -48,7 +48,7 @@ export default async function initialize(app: T, { ); models.forEach(model => { - Reflect.defineProperty(model, 'serializer', { + Object.defineProperty(model, 'serializer', { value: closestChild(serializers, model.resourceName), writable: false, enumerable: false, @@ -67,7 +67,7 @@ export default async function initialize(app: T, { ); controllers.forEach(controller => { - Reflect.defineProperty(controller, 'controllers', { + Object.defineProperty(controller, 'controllers', { value: controllers, writable: true, enumerable: false, diff --git a/src/packages/application/utils/create-controller.js b/src/packages/application/utils/create-controller.js index 59cc6e3a..095a5c79 100644 --- a/src/packages/application/utils/create-controller.js +++ b/src/packages/application/utils/create-controller.js @@ -38,11 +38,12 @@ export default function createController( serializer = closestAncestor(serializers, key); } - const instance: T = Reflect.construct(constructor, [{ + const instance: T = new constructor({ + // $FlowIgnore model, namespace, serializer - }]); + }); if (serializer) { if (!instance.filter.length) { @@ -66,7 +67,7 @@ export default function createController( ]; } - Reflect.defineProperty(instance, 'parent', { + Object.defineProperty(instance, 'parent', { value: parent, writable: false, enumerable: true, diff --git a/src/packages/application/utils/create-serializer.js b/src/packages/application/utils/create-serializer.js index 19b6c06d..5eaefce4 100644 --- a/src/packages/application/utils/create-serializer.js +++ b/src/packages/application/utils/create-serializer.js @@ -22,13 +22,14 @@ export default function createSerializer>( parent = null; } - const instance: T = Reflect.construct(constructor, [{ + const instance: T = new constructor({ + // $FlowIgnore model, parent, namespace - }]); + }); - Reflect.defineProperty(instance, 'parent', { + Object.defineProperty(instance, 'parent', { value: parent, writable: false, enumerable: true, diff --git a/src/packages/cli/commands/repl.js b/src/packages/cli/commands/repl.js index b82ea36b..2f593274 100644 --- a/src/packages/cli/commands/repl.js +++ b/src/packages/cli/commands/repl.js @@ -7,9 +7,8 @@ import type Application from '../../application'; export function repl(): Promise { return new Promise(async (resolve) => { - const app: Application = await Reflect.apply(require, null, [ - path.join(CWD, 'dist', 'boot') - ]); + // $FlowIgnore + const app: Application = require(path.join(CWD, 'dist', 'boot')); const instance = startRepl({ prompt: '> ' diff --git a/src/packages/cli/generator/utils/generator-for.js b/src/packages/cli/generator/utils/generator-for.js index 9826ff88..72d80e9d 100644 --- a/src/packages/cli/generator/utils/generator-for.js +++ b/src/packages/cli/generator/utils/generator-for.js @@ -5,7 +5,7 @@ import * as generators from './generate-type'; export default function generatorFor(type: string): Generator { const normalized = type.toLowerCase(); - const generator: void | Generator = Reflect.get(generators, normalized); + const generator: void | Generator = generators[normalized]; if (!generator) { throw new Error(`Could not find a generator for '${type}'.`); diff --git a/src/packages/cli/templates/model.js b/src/packages/cli/templates/model.js index 0d42540e..64fe3d6c 100644 --- a/src/packages/cli/templates/model.js +++ b/src/packages/cli/templates/model.js @@ -35,7 +35,7 @@ export default (name: string, attrs: Array) => { .pipe(str => camelize(str, true)) .value(); - const value = Reflect.get(types, key); + const value = types[key]; if (value) { const inverse = camelize(normalized, true); diff --git a/src/packages/compiler/utils/create-manifest.js b/src/packages/compiler/utils/create-manifest.js index b61939bb..b752f5da 100644 --- a/src/packages/compiler/utils/create-manifest.js +++ b/src/packages/compiler/utils/create-manifest.js @@ -108,7 +108,7 @@ export default async function createManifest( Array .from(assets) .map(([key, value]) => { - const write = Reflect.get(writer, key); + const write = writer[key]; if (write) { return write(value); diff --git a/src/packages/controller/index.js b/src/packages/controller/index.js index 13c81b87..6ef35c8b 100644 --- a/src/packages/controller/index.js +++ b/src/packages/controller/index.js @@ -629,7 +629,7 @@ class Controller { `${getDomain(req) + pathname}/${record.getPrimaryKey()}` ); - Reflect.set(res, 'statusCode', 201); + res.status(201); return record.unwrap(); } diff --git a/src/packages/controller/test/controller.test.js b/src/packages/controller/test/controller.test.js index 588e73af..62a5a81f 100644 --- a/src/packages/controller/test/controller.test.js +++ b/src/packages/controller/test/controller.test.js @@ -108,7 +108,7 @@ describe('module "controller"', () => { result.forEach(item => { assertRecord(item); - expect(Reflect.get(item, 'isPublic')).to.be.false; + expect(item).to.have.property('isPublic', false); }); }); @@ -315,9 +315,12 @@ describe('module "controller"', () => { 'updatedAt' ]); - const user = await Reflect.get(result, 'user'); - const title = Reflect.get(result, 'title'); - const isPublic = Reflect.get(result, 'isPublic'); + // $FlowIgnore + const user = await result.user; + // $FlowIgnore + const title = result.title; + // $FlowIgnore + const isPublic = result.isPublic; expect(user.id).to.equal(1); expect(title).to.equal('#create() Test'); @@ -355,7 +358,7 @@ describe('module "controller"', () => { result = await subject.create(request, response); - const id = Reflect.get(result, 'id'); + const id = result.getPrimaryKey(); const location = response.getHeader('Location'); expect(location).to.equal(`http://${HOST}/posts/${id}`); @@ -390,8 +393,10 @@ describe('module "controller"', () => { it('returns a record if attribute(s) change', async () => { let item = record; - let isPublic = Reflect.get(item, 'isPublic'); - const id = Reflect.get(item, 'id'); + // $FlowIgnore + let isPublic = item.isPublic; + // $FlowIgnore + const id = item.id; expect(isPublic).to.be.false; @@ -410,16 +415,18 @@ describe('module "controller"', () => { assertRecord(result); item = await Post.find(id); - isPublic = Reflect.get(item, 'isPublic'); + isPublic = item.isPublic; expect(isPublic).to.be.true; }); it('returns a record if relationships(s) change', async () => { let item = record; - let user = await Reflect.get(item, 'user'); - let comments = await Reflect.get(item, 'comments'); - const id = Reflect.get(item, 'id'); + // $FlowIgnore + let user = await item.user; + // $FlowIgnore + let comments = await item.comments; + const id = item.getPrimaryKey(); expect(user).to.be.null; expect(comments).to.deep.equal([]); @@ -469,8 +476,8 @@ describe('module "controller"', () => { ]); item = await Post.find(id); - user = await Reflect.get(item, 'user'); - comments = await Reflect.get(item, 'comments'); + user = await item.user; + comments = await item.comments; expect(user.id).to.equal(1); @@ -484,7 +491,7 @@ describe('module "controller"', () => { }); it('returns the number `204` if no changes occur', async () => { - const id = Reflect.get(record, 'id'); + const id = record.getPrimaryKey(); const request = createRequest({ id, @@ -519,8 +526,9 @@ describe('module "controller"', () => { it('supports sparse field sets', async () => { let item = record; - let title = Reflect.get(item, 'title'); - const id = Reflect.get(item, 'id'); + // $FlowIgnore + let title = item.title; + const id = item.getPrimaryKey(); expect(title).to.equal('#destroy() Test'); @@ -542,7 +550,7 @@ describe('module "controller"', () => { assertRecord(result, ['id', 'title']); item = await Post.find(id); - title = Reflect.get(item, 'title'); + title = item.title; expect(title).to.equal('Sparse Field Sets Work With #destroy()!'); }); @@ -571,7 +579,7 @@ describe('module "controller"', () => { }); it('returns the number `204` if the record is destroyed', async () => { - const id = Reflect.get(record, 'id'); + const id = record.getPrimaryKey(); const result = await subject.destroy(createRequest({ id })); expect(result).to.equal(204); diff --git a/src/packages/controller/utils/params-to-query.js b/src/packages/controller/utils/params-to-query.js index 38fb8412..fa41408e 100644 --- a/src/packages/controller/utils/params-to-query.js +++ b/src/packages/controller/utils/params-to-query.js @@ -21,7 +21,7 @@ export default function paramsToQuery(model: Class, { let query = { id, filter, - select: [model.primaryKey, ...Reflect.get(fields, model.resourceName)] + select: [model.primaryKey, ...fields[model.resourceName]] }; if (page) { diff --git a/src/packages/controller/utils/resolve-relationships.js b/src/packages/controller/utils/resolve-relationships.js index 255cfe01..d54efa69 100644 --- a/src/packages/controller/utils/resolve-relationships.js +++ b/src/packages/controller/utils/resolve-relationships.js @@ -11,23 +11,26 @@ export default function resolveRelationships( relationships: Object = {} ): Object { return entries(relationships).reduce((obj, [key, value]) => { - let { data = null } = value || {}; + const result = obj; - if (data) { + if (value && value.data) { const opts = model.relationshipFor(key); if (opts) { - if (Array.isArray(data)) { - data = data.map(item => Reflect.construct(opts.model, [item])); + /* eslint-disable new-cap */ + if (Array.isArray(value.data)) { + result[key] = value.data.map(item => new opts.model(item)); } else { - data = Reflect.construct(opts.model, [data]); + result[key] = new opts.model(value.data); } + /* eslint-enable new-cap */ + + return result; } } - return { - ...obj, - [key]: data - }; + result[key] = null; + + return result; }, {}); } diff --git a/src/packages/database/initialize.js b/src/packages/database/initialize.js index b4a63ebd..8581f8fe 100644 --- a/src/packages/database/initialize.js +++ b/src/packages/database/initialize.js @@ -20,7 +20,7 @@ export default async function initialize( const { path, models, logger, checkMigrations } = opts; let { config } = opts; - config = Reflect.get(config, NODE_ENV); + config = config[NODE_ENV]; if (!config) { throw new ConfigMissingError(NODE_ENV); @@ -29,7 +29,7 @@ export default async function initialize( const { debug = (NODE_ENV === 'development') }: { - debug: boolean + debug?: boolean } = config; Object.defineProperties(instance, { diff --git a/src/packages/database/model/index.js b/src/packages/database/model/index.js index d20685da..c5d843c0 100644 --- a/src/packages/database/model/index.js +++ b/src/packages/database/model/index.js @@ -683,7 +683,7 @@ class Model { Object.assign(this, props); if (initialize) { - Reflect.defineProperty(this, 'initialized', { + Object.defineProperty(this, 'initialized', { value: true, writable: false, enumerable: false, @@ -1150,7 +1150,8 @@ class Model { * @private */ getPrimaryKey(): number { - return Reflect.get(this, this.constructor.primaryKey); + // $FlowIgnore + return this[this.constructor.primaryKey]; } /** @@ -1169,7 +1170,7 @@ class Model { ): Promise> { const run = async (trx: Knex$Transaction) => { const { hooks, logger, primaryKey } = this; - const instance = Reflect.construct(this, [props, false]); + const instance = new this(props, false); let statements = []; const associations = Object @@ -1198,10 +1199,11 @@ class Model { const runner = createRunner(logger, statements); const [[primaryKeyValue]] = await runner(await create(instance, trx)); - Reflect.set(instance, primaryKey, primaryKeyValue); - Reflect.set(instance.rawColumnData, primaryKey, primaryKeyValue); + // $FlowIgnore + instance[primaryKey] = primaryKeyValue; + instance.rawColumnData[primaryKey] = primaryKeyValue; - Reflect.defineProperty(instance, 'initialized', { + Object.defineProperty(instance, 'initialized', { value: true, writable: false, enumerable: false, @@ -1384,7 +1386,7 @@ class Model { * @public */ static hasScope(name: string): boolean { - return Boolean(Reflect.get(this.scopes, name)); + return Boolean(this.scopes[name]); } /** @@ -1422,14 +1424,14 @@ class Model { const getTableName = compose(pluralize, underscore); const tableName = getTableName(this.name); - Reflect.defineProperty(this, 'tableName', { + Object.defineProperty(this, 'tableName', { value: tableName, writable: false, enumerable: true, configurable: false }); - Reflect.defineProperty(this.prototype, 'tableName', { + Object.defineProperty(this.prototype, 'tableName', { value: tableName, writable: false, enumerable: false, @@ -1453,7 +1455,7 @@ class Model { * @private */ static columnFor(key: string): void | Object { - return Reflect.get(this.attributes, key); + return this.attributes[key]; } /** @@ -1467,7 +1469,11 @@ class Model { static columnNameFor(key: string): void | string { const column = this.columnFor(key); - return column ? column.columnName : undefined; + if (column) { + return column.columnName; + } + + return undefined; } /** @@ -1479,7 +1485,7 @@ class Model { * @private */ static relationshipFor(key: string): void | Relationship$opts { - return Reflect.get(this.relationships, key); + return this.relationships[key]; } } diff --git a/src/packages/database/model/initialize-class.js b/src/packages/database/model/initialize-class.js index 42973f97..33382e7f 100644 --- a/src/packages/database/model/initialize-class.js +++ b/src/packages/database/model/initialize-class.js @@ -57,6 +57,8 @@ function initializeProps(prototype, attributes, relationships) { function initializeHooks({ model, hooks, logger }) { return Object.freeze( entries(hooks).reduce((obj, [key, value]) => { + const result = obj; + if (!VALID_HOOKS.has(key)) { logger.warn(line` Invalid hook '${key}' will not be added to Model '${model.name}'. @@ -65,15 +67,14 @@ function initializeHooks({ model, hooks, logger }) { }. `); - return obj; + return result; } - return { - ...obj, - [key]: async (instance, transaction) => { - await Reflect.apply(value, model, [instance, transaction]); - } - }; + result[key] = (instance, transaction) => Promise.resolve( + value.call(model, instance, transaction) + ); + + return result; }, {}) ); } diff --git a/src/packages/database/model/utils/get-columns.js b/src/packages/database/model/utils/get-columns.js index f6bdc3e4..692f708b 100644 --- a/src/packages/database/model/utils/get-columns.js +++ b/src/packages/database/model/utils/get-columns.js @@ -7,14 +7,22 @@ import type Model from '../index'; * @private */ export default function getColumns(record: Model, only?: Array) { - let { constructor: { attributes: columns } } = record; + let { + constructor: { + attributes: columns + } + } = record; if (only) { columns = pick(columns, ...only); } - return entries(columns).reduce((obj, [key, { columnName }]) => ({ - ...obj, - [columnName]: Reflect.get(record, key) - }), {}); + return entries(columns).reduce((obj, [key, { columnName }]) => { + const result = obj; + + // $FlowIgnore + result[columnName] = record[key]; + + return result; + }, {}); } diff --git a/src/packages/database/model/utils/persistence.js b/src/packages/database/model/utils/persistence.js index 2239cd62..1cec6ec2 100644 --- a/src/packages/database/model/utils/persistence.js +++ b/src/packages/database/model/utils/persistence.js @@ -14,24 +14,25 @@ export function create( record: Model, trx: Knex$Transaction ): [Knex$QueryBuilder] { + const target = record; const timestamp = new Date(); - Object.assign(record, { + Object.assign(target, { createdAt: timestamp, updatedAt: timestamp }); - Object.assign(record.rawColumnData, { + Object.assign(target.rawColumnData, { createdAt: timestamp, updatedAt: timestamp }); return [ - record.constructor + target.constructor .table() .transacting(trx) - .returning(record.constructor.primaryKey) - .insert(omit(getColumns(record), record.constructor.primaryKey)) + .returning(target.constructor.primaryKey) + .insert(omit(getColumns(target), target.constructor.primaryKey)) ]; } @@ -42,16 +43,18 @@ export function update( record: Model, trx: Knex$Transaction ): [Knex$QueryBuilder] { - Reflect.set(record, 'updatedAt', new Date()); + const target = record; + + target.updatedAt = new Date(); return [ - record.constructor + target.constructor .table() .transacting(trx) - .where(record.constructor.primaryKey, record.getPrimaryKey()) + .where(target.constructor.primaryKey, target.getPrimaryKey()) .update(getColumns( - record, - Array.from(record.dirtyAttributes.keys()) + target, + Array.from(target.dirtyAttributes.keys()) )) ]; } diff --git a/src/packages/database/model/utils/validate.js b/src/packages/database/model/utils/validate.js index 38a4ae01..a235552a 100644 --- a/src/packages/database/model/utils/validate.js +++ b/src/packages/database/model/utils/validate.js @@ -11,7 +11,7 @@ export default function validate(instance: Model): true { .map(([key, value]) => ({ key, value, - validator: Reflect.get(instance.constructor.validates, key) + validator: instance.constructor.validates[key] })) .filter(({ validator }) => validator) .map(props => new Validation(props)) diff --git a/src/packages/database/query/index.js b/src/packages/database/query/index.js index d686e8d1..25318209 100644 --- a/src/packages/database/query/index.js +++ b/src/packages/database/query/index.js @@ -446,16 +446,12 @@ class Query<+T: any> extends Promise { relationships } = src; - const dest = Reflect.construct(this, [model]); - - Object.assign(dest, { + return Object.assign(new this(model), { snapshots, collection, shouldCount, relationships }); - - return dest; } } diff --git a/src/packages/database/query/runner/index.js b/src/packages/database/query/runner/index.js index 10e5e620..9d3e691f 100644 --- a/src/packages/database/query/runner/index.js +++ b/src/packages/database/query/runner/index.js @@ -49,13 +49,14 @@ export function createRunner(target: Query<*>, opts: { name = 'select'; } - const method = Reflect.get(query, name); + // $FlowIgnore + const method = query[name]; if (!Array.isArray(params)) { params = [params]; } - return Reflect.apply(method, query, params); + return method.apply(query, params); }, model.table()); if (model.store.debug) { diff --git a/src/packages/database/query/runner/utils/build-results.js b/src/packages/database/query/runner/utils/build-results.js index 6db66fd1..4e251779 100644 --- a/src/packages/database/query/runner/utils/build-results.js +++ b/src/packages/database/query/runner/utils/build-results.js @@ -1,6 +1,7 @@ // @flow import { camelize, singularize } from 'inflection'; +// eslint-disable-next-line no-unused-vars import Model from '../../../model'; import entries from '../../../../../utils/entries'; import underscore from '../../../../../utils/underscore'; @@ -27,110 +28,103 @@ export default async function buildResults({ } if (Object.keys(relationships).length) { - related = entries(relationships) - .reduce((obj, entry) => { - const [name, relationship] = entry; - let foreignKey = camelize(relationship.foreignKey, true); - - if (relationship.through) { - const query = relationship.model.select(...relationship.attrs); - - const baseKey = `${relationship.through.tableName}.` + - `${singularize(underscore(name))}_id`; - - foreignKey = `${relationship.through.tableName}.` + - `${relationship.foreignKey}`; - - query.snapshots.push( - ['select', [ - `${baseKey} as ${camelize(baseKey.split('.').pop(), true)}`, - `${foreignKey} as ${camelize(foreignKey.split('.').pop(), true)}` - ]], - - ['innerJoin', [ - relationship.through.tableName, - `${relationship.model.tableName}.` + - `${relationship.model.primaryKey}`, - '=', - baseKey - ]], - - ['whereIn', [ - foreignKey, - results.map(({ id }) => id) - ]] - ); - - return { - ...obj, - [name]: query - }; - } + related = entries(relationships).reduce((obj, entry) => { + const result = obj; + const [name, relationship] = entry; + let foreignKey = camelize(relationship.foreignKey, true); + + if (relationship.through) { + const query = relationship.model.select(...relationship.attrs); + + const baseKey = `${relationship.through.tableName}.` + + `${singularize(underscore(name))}_id`; + + foreignKey = `${relationship.through.tableName}.` + + `${relationship.foreignKey}`; + + query.snapshots.push( + ['select', [ + `${baseKey} as ${camelize(baseKey.split('.').pop(), true)}`, + `${foreignKey} as ${camelize(foreignKey.split('.').pop(), true)}` + ]], + + ['innerJoin', [ + relationship.through.tableName, + `${relationship.model.tableName}.` + + `${relationship.model.primaryKey}`, + '=', + baseKey + ]], + + ['whereIn', [ + foreignKey, + results.map(({ id }) => id) + ]] + ); + + result[name] = query; + + return result; + } + + result[name] = relationship.model + .select(...relationship.attrs) + .where({ + [foreignKey]: results.map(({ id }) => id) + }); - return { - ...obj, - [name]: relationship.model - .select(...relationship.attrs) - .where({ - [foreignKey]: results.map(({ id }) => id) - }) - }; - }, {}); + return result; + }, {}); related = await promiseHash(related); } return results.map(record => { if (related) { - entries(related) - .forEach(([name, relatedResults]: [string, Array]) => { - const relationship = model.relationshipFor(name); + const target = record; - if (relationship) { - let { foreignKey } = relationship; + entries(related).forEach(([name, relatedResults]) => { + const relationship = model.relationshipFor(name); - foreignKey = camelize(foreignKey, true); + if (relationship) { + let { foreignKey } = relationship; - Reflect.set(record, name, relatedResults.filter(({ - rawColumnData - }) => { - const fk = Reflect.get(rawColumnData, foreignKey); - const pk = Reflect.get(record, model.primaryKey); - - return fk === pk; - })); - } - }); + foreignKey = camelize(foreignKey, true); + target[name] = relatedResults.filter(({ rawColumnData }) => ( + rawColumnData[foreignKey] === record[model.primaryKey] + )); + } + }); } - const instance = Reflect.construct(model, [ - entries(record) - .reduce((r, entry) => { - let [key, value] = entry; - - if (!value && pkPattern.test(key)) { - return r; - } else if (key.indexOf('.') >= 0) { - const [a, b] = key.split('.'); - let parent: ?Object = r[a]; - - if (!parent) { - parent = {}; - } - - key = a; - value = { - ...parent, - [b]: value - }; + // eslint-disable-next-line new-cap + const instance = new model( + entries(record).reduce((obj, entry) => { + const result = obj; + let [key, value] = entry; + + if (!value && pkPattern.test(key)) { + return result; + } else if (key.indexOf('.') >= 0) { + const [a, b] = key.split('.'); + let parent: ?Object = result[a]; + + if (!parent) { + parent = {}; } - return { - ...r, - [key]: value + key = a; + value = { + ...parent, + [b]: value }; - }, {}) - ]); + } + + result[key] = value; + + return result; + }, {}) + ); instance.currentChangeSet.persist(); diff --git a/src/packages/database/query/runner/utils/get-find-param.js b/src/packages/database/query/runner/utils/get-find-param.js index 8b912fe5..1ea6fa4f 100644 --- a/src/packages/database/query/runner/utils/get-find-param.js +++ b/src/packages/database/query/runner/utils/get-find-param.js @@ -17,7 +17,8 @@ export default function getFindParam({ const [, params] = snapshot; if (params && isObject(params)) { - return Reflect.get(params, `${tableName}.${primaryKey}`); + // $FlowIgnore + return params[`${tableName}.${primaryKey}`]; } } } diff --git a/src/packages/database/query/utils/scopes-for.js b/src/packages/database/query/utils/scopes-for.js index bb1bef88..755424b9 100644 --- a/src/packages/database/query/utils/scopes-for.js +++ b/src/packages/database/query/utils/scopes-for.js @@ -1,17 +1,20 @@ // @flow +import type { ObjectMap } from '../../../../interfaces'; import type Query from '../index'; -export default function scopesFor(target: Query): { - [key: string]: () => Query -} { - return Object.keys(target.model.scopes).reduce((scopes, name) => ({ - ...scopes, - [name]: { +export default function scopesFor( + target: Query +): ObjectMap) => Query> { + return Object.keys(target.model.scopes).reduce((scopes, name) => { + const result = scopes; + + result[name] = { get() { // eslint-disable-next-line func-names const scope = function (...args: Array) { - const fn = Reflect.get(target.model, name); - const { snapshots } = Reflect.apply(fn, target.model, args); + // $FlowIgnore + const fn = target.model[name]; + const { snapshots } = fn.apply(target.model, args); Object.assign(target, { snapshots: [ @@ -26,7 +29,7 @@ export default function scopesFor(target: Query): { return target; }; - Reflect.defineProperty(scope, 'name', { + Object.defineProperty(scope, 'name', { value: name, writable: false, enumerable: false, @@ -35,6 +38,8 @@ export default function scopesFor(target: Query): { return scope; } - } - }), {}); + }; + + return result; + }, {}); } diff --git a/src/packages/database/relationship/utils/getters.js b/src/packages/database/relationship/utils/getters.js index ccf1b03f..8a1a3a4b 100644 --- a/src/packages/database/relationship/utils/getters.js +++ b/src/packages/database/relationship/utils/getters.js @@ -27,7 +27,7 @@ async function getHasManyThrough(owner: Model, { if (records.length) { value = await model.where({ [model.primaryKey]: records - .map(record => Reflect.get(record, foreignKey)) + .map(record => record[foreignKey]) .filter(Boolean) }); } @@ -66,7 +66,8 @@ export function getBelongsTo(owner: Model, { model, foreignKey }: Relationship$opts) { - const foreignValue = Reflect.get(owner, foreignKey); + // $FlowIgnore + const foreignValue = owner[foreignKey]; return foreignValue ? model.find(foreignValue) : Promise.resolve(null); } diff --git a/src/packages/database/relationship/utils/inverse-setters.js b/src/packages/database/relationship/utils/inverse-setters.js index 0bd3f953..85f60cfa 100644 --- a/src/packages/database/relationship/utils/inverse-setters.js +++ b/src/packages/database/relationship/utils/inverse-setters.js @@ -2,6 +2,15 @@ import type { Model } from '../../index'; import type { Relationship$opts } from '../index'; +type Setter$opts = { + type: $PropertyType; + model: $PropertyType; + inverse: $PropertyType; + through?: $PropertyType; + foreignKey: $PropertyType; + inverseModel: Class; +}; + /** * @private */ @@ -9,11 +18,16 @@ export function setHasManyInverse(owner: Model, value: Array, { inverse, foreignKey, inverseModel -}: Relationship$opts & { - inverseModel: Class; -}) { - const primaryKey = Reflect.get(owner, owner.constructor.primaryKey); - const { type: inverseType } = inverseModel.relationshipFor(inverse); +}: Setter$opts) { + // $FlowIgnore + const primaryKey = owner[owner.constructor.primaryKey]; + const relationship = inverseModel.relationshipFor(inverse); + + if (!relationship) { + return; + } + + const { type: inverseType } = relationship; for (const record of value) { let { currentChangeSet: changeSet } = record; @@ -26,7 +40,8 @@ export function setHasManyInverse(owner: Model, value: Array, { changeSet.set(inverse, owner); if (inverseType === 'belongsTo') { - Reflect.set(record, foreignKey, primaryKey); + // $FlowIgnore + record[foreignKey] = primaryKey; } } } @@ -39,11 +54,16 @@ export function setHasOneInverse(owner: Model, value?: ?Model, { inverse, foreignKey, inverseModel -}: Relationship$opts & { - inverseModel: Class; -}) { +}: Setter$opts) { if (value) { - const { type: inverseType } = inverseModel.relationshipFor(inverse); + const nextValue = value; + const relationship = inverseModel.relationshipFor(inverse); + + if (!relationship) { + return; + } + + const { type: inverseType } = relationship; let inverseValue = value.currentChangeSet.get(inverse); if (inverseType === 'hasMany') { @@ -58,14 +78,15 @@ export function setHasOneInverse(owner: Model, value?: ?Model, { inverseValue = owner; if (inverseType === 'belongsTo') { - Reflect.set(value, foreignKey, inverseValue.getPrimaryKey()); + // $FlowIgnore + nextValue[foreignKey] = inverseValue.getPrimaryKey(); } } - let { currentChangeSet: changeSet } = value; + let { currentChangeSet: changeSet } = nextValue; if (changeSet.isPersisted) { - changeSet = changeSet.applyTo(value); + changeSet = changeSet.applyTo(nextValue); } changeSet.set(inverse, inverseValue || null); diff --git a/src/packages/database/relationship/utils/setters.js b/src/packages/database/relationship/utils/setters.js index 1c415940..2200c91d 100644 --- a/src/packages/database/relationship/utils/setters.js +++ b/src/packages/database/relationship/utils/setters.js @@ -60,7 +60,7 @@ export function setHasOne(owner: Model, key: string, value?: ?Model, { let valueToSet = value; if (value && typeof value === 'object' && !model.isInstance(value)) { - valueToSet = Reflect.construct(model, [valueToSet]); + valueToSet = new model(value); // eslint-disable-line new-cap } let { currentChangeSet: changeSet } = owner; @@ -99,16 +99,15 @@ export function setBelongsTo(owner: Model, key: string, value?: ?Model, { inverse, foreignKey }: Relationship$opts) { - setHasOne(owner, key, value, { + const target = owner; + + setHasOne(target, key, value, { type, model, inverse, foreignKey }); - if (value) { - Reflect.set(owner, foreignKey, Reflect.get(value, model.primaryKey)); - } else { - Reflect.set(owner, foreignKey, null); - } + // $FlowIgnore + target[foreignKey] = value ? value[model.primaryKey] : null; } diff --git a/src/packages/database/relationship/utils/unassociate.js b/src/packages/database/relationship/utils/unassociate.js index ec0231dc..3dd76086 100644 --- a/src/packages/database/relationship/utils/unassociate.js +++ b/src/packages/database/relationship/utils/unassociate.js @@ -5,11 +5,14 @@ import type { Model } from '../../index'; // eslint-disable-line no-unused-vars * @private */ function unassociateOne(value: T, foreignKey: string): T { - if (value) { - Reflect.set(value, foreignKey, null); + const target = value; + + if (target) { + // $FlowIgnore + target[foreignKey] = null; } - return value; + return target; } /** diff --git a/src/packages/database/relationship/utils/update-relationship.js b/src/packages/database/relationship/utils/update-relationship.js index 69a4ee35..b213697d 100644 --- a/src/packages/database/relationship/utils/update-relationship.js +++ b/src/packages/database/relationship/utils/update-relationship.js @@ -110,21 +110,23 @@ function updateBelongsTo({ trx }: Params): Array { if (value instanceof opts.model) { + const target = record; const inverseOpts = opts.model.relationshipFor(opts.inverse); const foreignKeyValue = value.getPrimaryKey(); - Reflect.set(record, opts.foreignKey, foreignKeyValue); + // $FlowIgnore + target[opts.foreignKey] = foreignKeyValue; if (inverseOpts && inverseOpts.type === 'hasOne') { return [ - record.constructor + target.constructor .table() .transacting(trx) .update(opts.foreignKey, null) .where(opts.foreignKey, foreignKeyValue) .whereNot( - `${record.constructor.tableName}.${record.constructor.primaryKey}`, - record.getPrimaryKey() + `${target.constructor.tableName}.${target.constructor.primaryKey}`, + target.getPrimaryKey() ) ]; } diff --git a/src/packages/database/test/database.test.js b/src/packages/database/test/database.test.js index 6a3b8da8..5dea3e63 100644 --- a/src/packages/database/test/database.test.js +++ b/src/packages/database/test/database.test.js @@ -5,9 +5,13 @@ import { it, before, describe } from 'mocha'; import Database from '../index'; import { getTestApp } from '../../../../test/utils/get-test-app'; -const DATABASE_DRIVER: string = Reflect.get(process.env, 'DATABASE_DRIVER'); -const DATABASE_USERNAME: string = Reflect.get(process.env, 'DATABASE_USERNAME'); -const DATABASE_PASSWORD: string = Reflect.get(process.env, 'DATABASE_PASSWORD'); + +// $FlowIgnore +const DATABASE_DRIVER: string = process.env.DATABASE_DRIVER; +// $FlowIgnore +const DATABASE_USERNAME: string = process.env.DATABASE_USERNAME; +// $FlowIgnore +const DATABASE_PASSWORD: string = process.env.DATABASE_PASSWORD; const DEFAULT_CONFIG = { development: { diff --git a/src/packages/database/test/model.test.js b/src/packages/database/test/model.test.js index 39e229bb..36b98872 100644 --- a/src/packages/database/test/model.test.js +++ b/src/packages/database/test/model.test.js @@ -132,7 +132,7 @@ describe('module "database/model"', () => { ]); Object.keys(Subject.attributes).forEach(key => { - const value = Reflect.get(Subject.attributes, key); + const value = Subject.attributes[key]; expect(value).to.have.all.keys([ 'type', @@ -161,7 +161,7 @@ describe('module "database/model"', () => { it('adds attribute accessors on the `prototype`', () => { Object.keys(Subject.attributes).forEach(key => { - const desc = Reflect.getOwnPropertyDescriptor(Subject.prototype, key); + const desc = Object.getOwnPropertyDescriptor(Subject.prototype, key); expect(desc).to.have.property('get').and.be.a('function'); expect(desc).to.have.property('set').and.be.a('function'); @@ -180,7 +180,7 @@ describe('module "database/model"', () => { ]); Object.keys(Subject.hasMany).forEach(key => { - const value = Reflect.get(Subject.hasMany, key); + const value = Subject.hasMany[key]; expect(value).to.be.an('object'); expect(value).to.have.property('type').and.equal('hasMany'); @@ -198,7 +198,7 @@ describe('module "database/model"', () => { expect(Subject.belongsTo).to.have.all.keys(['user']); Object.keys(Subject.belongsTo).forEach(key => { - const value = Reflect.get(Subject.belongsTo, key); + const value = Subject.belongsTo[key]; expect(value).to.be.an('object'); expect(value).to.have.property('type').and.equal('belongsTo'); @@ -220,7 +220,7 @@ describe('module "database/model"', () => { ]); Object.keys(Subject.relationships).forEach(key => { - const value = Reflect.get(Subject.relationships, key); + const value = Subject.relationships[key]; expect(value).to.have.property('type'); @@ -254,7 +254,7 @@ describe('module "database/model"', () => { it('adds relationship accessors to the `prototype`', () => { Object.keys(Subject.relationships).forEach(key => { - const desc = Reflect.getOwnPropertyDescriptor(Subject.prototype, key); + const desc = Object.getOwnPropertyDescriptor(Subject.prototype, key); expect(desc).to.have.property('get').and.be.a('function'); expect(desc).to.have.property('set').and.be.a('function'); @@ -283,7 +283,8 @@ describe('module "database/model"', () => { ]); Object.keys(Subject.scopes).forEach(key => { - const value = Reflect.get(Subject, key); + // $FlowIgnore + const value = Subject[key]; expect(value).to.be.a('function'); }); @@ -836,7 +837,8 @@ describe('module "database/model"', () => { const assertSaveHook = async (instance: Model, hookSpy) => { hookSpy.reset(); - Reflect.set(instance, 'isPublic', true); + // $FlowIgnore + instance.isPublic = true; await instance.save(); expect(hookSpy.calledWith(instance)).to.be.true; diff --git a/src/packages/database/test/relationship.test.js b/src/packages/database/test/relationship.test.js index d7c08031..39d68b1f 100644 --- a/src/packages/database/test/relationship.test.js +++ b/src/packages/database/test/relationship.test.js @@ -189,11 +189,10 @@ describe('module "database/relationship"', () => { let subjectId; const setup = async () => { - subject = await Post.create({ - title: '#set() test' - }); + subject = await Post + .create({ title: '#set() test' }) + .then(post => post.unwrap()); - subject = subject.unwrap(); subjectId = subject.getPrimaryKey(); }; @@ -212,11 +211,9 @@ describe('module "database/relationship"', () => { beforeEach(async () => { await setup(); - image = await Image.create({ - url: 'http://postlight.com' - }); - - image = image.unwrap(); + image = await Image + .create({ url: 'http://postlight.com' }) + .then(img => img.unwrap()); instances.add(image); set(subject, 'image', image); @@ -226,7 +223,8 @@ describe('module "database/relationship"', () => { it('can add a record to the relationship', async () => { expect(image).to.have.property('postId', subjectId); - expect(await Reflect.get(image, 'post')).be.an.instanceof(Post); + // $FlowIgnore + expect(await image.post).be.an.instanceof(Post); }); }); @@ -252,14 +250,16 @@ describe('module "database/relationship"', () => { it('can add a record to the relationship', async () => { expect(subject).to.have.property('userId', user.getPrimaryKey()); - expect(await Reflect.get(subject, 'user')).to.be.an.instanceof(User); + // $FlowIgnore + expect(await subject.user).to.be.an.instanceof(User); }); it('can remove a record from the relationship', async () => { set(subject, 'user', null); expect(subject).to.have.property('userId', null); - expect(await Reflect.get(subject, 'user')).to.be.null; + // $FlowIgnore + expect(await subject.user).to.be.null; }); }); diff --git a/src/packages/database/utils/connect.js b/src/packages/database/utils/connect.js index bf82804a..5d9eb236 100644 --- a/src/packages/database/utils/connect.js +++ b/src/packages/database/utils/connect.js @@ -35,10 +35,7 @@ export default function connect(path: string, config: Object = {}): Knex { }; } - const knex: Class = Reflect.apply(require, null, [ - joinPath(path, 'node_modules', 'knex') - ]); - + const knex: Class = require(joinPath(path, 'node_modules', 'knex')); const usingSQLite = driver === 'sqlite3'; const connection = DATABASE_URL || url || { diff --git a/src/packages/freezeable/utils/freeze.js b/src/packages/freezeable/utils/freeze.js index b27b6779..35828899 100644 --- a/src/packages/freezeable/utils/freeze.js +++ b/src/packages/freezeable/utils/freeze.js @@ -41,15 +41,21 @@ export function freezeProps( makePublic: boolean, ...props: Array ): T { - Object.defineProperties(target, props.reduce((obj, key) => ({ - ...obj, - [key]: { - value: Reflect.get(target, key), + const properties = props.reduce((obj, key) => { + const result = obj; + + result[key] = { + // $FlowIgnore + value: target[key], writable: false, enumerable: makePublic, configurable: false, - } - }), {})); + }; + + return result; + }, {}); + + Object.defineProperties(target, properties); return target; } @@ -62,8 +68,10 @@ export function deepFreezeProps( makePublic: boolean, ...props: Array ): T { - Object.defineProperties(target, props.reduce((obj, key) => { - let value = Reflect.get(target, key); + const properties = props.reduce((obj, key) => { + const result = obj; + // $FlowIgnore + let value = target[key]; if (Array.isArray(value)) { value = freezeArray(value); @@ -71,16 +79,17 @@ export function deepFreezeProps( value = freezeValue(value); } - return { - ...obj, - [key]: { - value, - writable: false, - enumerable: makePublic, - configurable: false, - } + result[key] = { + value, + writable: false, + enumerable: makePublic, + configurable: false, }; - }, {})); + + return result; + }, {}); + + Object.defineProperties(target, properties); return target; } diff --git a/src/packages/fs/test/fs.test.js b/src/packages/fs/test/fs.test.js index e4b19096..96c05263 100644 --- a/src/packages/fs/test/fs.test.js +++ b/src/packages/fs/test/fs.test.js @@ -39,7 +39,7 @@ describe('module "fs"', () => { // unwrap spies of node fs methods spies = spiedMethods.reduce((memo, methodName) => { memo[methodName].restore(); - Reflect.deleteProperty(memo, methodName); + delete memo[methodName]; return memo; }, spies); }); @@ -62,7 +62,7 @@ describe('module "fs"', () => { it('delegates to node fs#mkdir', async () => { const dirPath = join(tmpDirPath, 'test-mkdir'); await fs.mkdir(dirPath); - expect(spies['mkdir'].calledWith(dirPath)).to.be.true; + expect(spies.mkdir.calledWith(dirPath)).to.be.true; }); it('returns a promise', () => { const dirPath = join(tmpDirPath, 'test-mkdir'); @@ -73,7 +73,7 @@ describe('module "fs"', () => { describe('#rmdir()', () => { it('delegates to node fs#rmdir', async () => { await fs.rmdir(tmpDirPath); - expect(spies['rmdir'].calledWith(tmpDirPath)).to.be.true; + expect(spies.rmdir.calledWith(tmpDirPath)).to.be.true; }); it('returns a promise', returnsPromiseSpec('rmdir', tmpDirPath)); }); @@ -81,7 +81,7 @@ describe('module "fs"', () => { describe('#readdir()', () => { it('delegates to node fs#readdir', async () => { await fs.readdir(tmpDirPath); - expect(spies['readdir'].calledWith(tmpDirPath)).to.be.true; + expect(spies.readdir.calledWith(tmpDirPath)).to.be.true; }); it('returns a promise', returnsPromiseSpec('readdir', tmpDirPath)); }); @@ -96,7 +96,7 @@ describe('module "fs"', () => { it('delegates to node fs#readFile', async () => { await fs.readFile(tmpFilePath); - expect(spies['readFile'].calledWith(tmpFilePath)).to.be.true; + expect(spies.readFile.calledWith(tmpFilePath)).to.be.true; }); it('returns a promise', returnsPromiseSpec('readFile', tmpFilePath)); }); @@ -111,7 +111,7 @@ describe('module "fs"', () => { it('delegates to node fs#writeFile', async () => { await fs.writeFile(tmpFilePath, 'test data'); - expect(spies['writeFile'].calledWith(tmpFilePath)).to.be.true; + expect(spies.writeFile.calledWith(tmpFilePath)).to.be.true; }); it('returns a promise', returnsPromiseSpec('writeFile', tmpFilePath)); }); @@ -126,7 +126,7 @@ describe('module "fs"', () => { it('delegates to node fs#appendFile', async () => { await fs.appendFile(tmpFilePath, 'test data'); - expect(spies['appendFile'].calledWith(tmpFilePath)); + expect(spies.appendFile.calledWith(tmpFilePath)); }); it('returns a promise', returnsPromiseSpec('appendFile', tmpFilePath)); }); @@ -141,7 +141,7 @@ describe('module "fs"', () => { it('delegates to node fs#stat', async () => { await fs.stat(tmpFilePath); - expect(spies['stat'].calledWith(tmpFilePath)); + expect(spies.stat.calledWith(tmpFilePath)); }); it('returns a promise', returnsPromiseSpec('stat', tmpFilePath)); }); @@ -156,7 +156,7 @@ describe('module "fs"', () => { it('delegates to node fs#unlink', async () => { await fs.unlink(tmpFilePath); - expect(spies['unlink'].calledWith(tmpFilePath)); + expect(spies.unlink.calledWith(tmpFilePath)); }); it('returns a promise', returnsPromiseSpec('unlink', tmpFilePath)); }); @@ -187,7 +187,7 @@ function returnsPromiseSpec( ...args: Array ): (done?: () => void) => void | Promise { return function () { - const res = Reflect.apply(fs[method], fs, args); + const res = fs[method].apply(fs, args); expect(res).to.be.an.instanceOf(Promise); }; } diff --git a/src/packages/loader/test/loader.test.js b/src/packages/loader/test/loader.test.js index 9a132c68..49a1cc90 100644 --- a/src/packages/loader/test/loader.test.js +++ b/src/packages/loader/test/loader.test.js @@ -43,7 +43,7 @@ describe('module "loader"', () => { result.forEach(value => { expect( - Reflect.getPrototypeOf(value).name.endsWith('Controller') + Object.getPrototypeOf(value).name.endsWith('Controller') ).to.be.true; }); }); @@ -64,7 +64,7 @@ describe('module "loader"', () => { expect(result).to.be.an.instanceof(FreezeableMap); result.forEach(value => { - expect(Reflect.getPrototypeOf(value).name).to.equal('Model'); + expect(Object.getPrototypeOf(value).name).to.equal('Model'); }); }); @@ -83,7 +83,7 @@ describe('module "loader"', () => { result.forEach(value => { expect( - Reflect.getPrototypeOf(value).name.endsWith('Serializer') + Object.getPrototypeOf(value).name.endsWith('Serializer') ).to.be.true; }); }); diff --git a/src/packages/loader/utils/bundle-for.js b/src/packages/loader/utils/bundle-for.js index 7e14d0d3..30d67ea5 100644 --- a/src/packages/loader/utils/bundle-for.js +++ b/src/packages/loader/utils/bundle-for.js @@ -17,25 +17,24 @@ const SUFFIX_PATTERN = /^.+(Controller|Down|Serializer|Up)/; */ function normalize(manifest: Object) { return entries(manifest).reduce((obj, [key, value]) => { + const result = obj; + if (SUFFIX_PATTERN.test(key)) { const suffix = key.replace(SUFFIX_PATTERN, '$1'); const stripSuffix = source => source.replace(suffix, ''); switch (suffix) { case 'Controller': - obj.controllers.set(formatKey(key, stripSuffix), value); + result.controllers.set(formatKey(key, stripSuffix), value); break; case 'Serializer': - obj.serializers.set(formatKey(key, stripSuffix), value); + result.serializers.set(formatKey(key, stripSuffix), value); break; case 'Up': case 'Down': - obj.migrations.set( - formatKey(key), - Reflect.construct(Migration, [value]) - ); + result.migrations.set(formatKey(key), new Migration(value)); break; default: @@ -46,32 +45,30 @@ function normalize(manifest: Object) { case 'Application': case 'routes': case 'seed': - Reflect.set(obj, formatKey(key), value); + result[formatKey(key)] = value; break; case 'config': - Reflect.set(obj, 'config', { - ...merge(createDefaultConfig(), { - ...obj.config, - ...value - }) + result.config = merge(createDefaultConfig(), { + ...result.config, + ...value }); break; case 'database': - Reflect.set(obj, 'config', { - ...obj.config, + result.config = { + ...result.config, database: value - }); + }; break; default: - obj.models.set(formatKey(key), value); + result.models.set(formatKey(key), value); break; } } - return obj; + return result; }, { config: {}, controllers: new FreezeableMap(), @@ -85,9 +82,8 @@ function normalize(manifest: Object) { * @private */ export default function bundleFor(path: string): FreezeableMap { - const manifest: Object = Reflect.apply(require, null, [ - joinPath(path, 'dist', 'bundle') - ]); + // $FlowIgnore + const manifest: Object = require(joinPath(path, 'dist', 'bundle')); return chain(manifest) .pipe(normalize) diff --git a/src/packages/logger/index.js b/src/packages/logger/index.js index 3fb10d6e..5a3d2aa0 100644 --- a/src/packages/logger/index.js +++ b/src/packages/logger/index.js @@ -240,7 +240,7 @@ class Logger { const levelNum = LEVELS.get(level) || 0; LEVELS.forEach((val, key: Logger$level) => { - Reflect.defineProperty(this, key.toLowerCase(), { + Object.defineProperty(this, key.toLowerCase(), { writable: false, enumerable: false, configurable: false, diff --git a/src/packages/logger/request-logger/templates.js b/src/packages/logger/request-logger/templates.js index 831f2943..11e25117 100644 --- a/src/packages/logger/request-logger/templates.js +++ b/src/packages/logger/request-logger/templates.js @@ -41,8 +41,8 @@ export const debugTemplate = ({ }: RequestLogger$templateData) => `\ ${line` Processed ${cyan(`${method}`)} "${path}" from ${remoteAddress} - with ${Reflect.apply(colorStr, null, [`${statusCode}`])} - ${Reflect.apply(colorStr, null, [`${statusMessage}`])} by ${ + with ${colorStr(String(statusCode))} + ${colorStr(String(statusMessage))} by ${ route ? `${yellow(route.controller.constructor.name)}#${blue(route.action)}` : null @@ -91,9 +91,9 @@ export const infoTemplate = ({ Processed ${cyan(`${method}`)} "${path}" ${magenta('Params')} ${ JSON.stringify(params)} from ${remoteAddress } in ${(endTime - startTime).toString()} ms with ${ - Reflect.apply(colorStr, null, [`${statusCode}`]) + colorStr(String(statusCode)) } ${ - Reflect.apply(colorStr, null, [`${statusMessage}`]) + colorStr(String(statusMessage)) } by ${ route ? `${yellow(route.controller.constructor.name)}#${blue(route.action)}` diff --git a/src/packages/logger/request-logger/utils/log-text.js b/src/packages/logger/request-logger/utils/log-text.js index e4718242..65d72dea 100644 --- a/src/packages/logger/request-logger/utils/log-text.js +++ b/src/packages/logger/request-logger/utils/log-text.js @@ -50,7 +50,7 @@ export default function logText(logger: Logger, { statusColor = 'red'; } - let colorStr = Reflect.get(chalk, statusColor); + let colorStr = chalk[statusColor]; if (typeof colorStr === 'undefined') { colorStr = (str: string) => str; diff --git a/src/packages/logger/test/logger.test.js b/src/packages/logger/test/logger.test.js index 83337364..4741b6ba 100644 --- a/src/packages/logger/test/logger.test.js +++ b/src/packages/logger/test/logger.test.js @@ -106,7 +106,7 @@ function hookWrite (cb) { const cbWrapper = (...args) => { if (isLoggerData(...args)) { - Reflect.apply(cb, null, args); + cb(...args); } }; diff --git a/src/packages/luxify/index.js b/src/packages/luxify/index.js index b874f42e..dbe0ef03 100644 --- a/src/packages/luxify/index.js +++ b/src/packages/luxify/index.js @@ -21,7 +21,7 @@ export default function luxify( ): Action { const result = function (req, res) { // eslint-disable-line func-names return new Promise((resolve, reject) => { - Reflect.apply(middleware, null, [ + middleware( req, createResponseProxy(res, resolve), (err) => { @@ -31,11 +31,11 @@ export default function luxify( resolve(); } } - ]); + ); }); }; - Reflect.defineProperty(result, 'name', { + Object.defineProperty(result, 'name', { value: middleware.name }); diff --git a/src/packages/luxify/test/luxify.test.js b/src/packages/luxify/test/luxify.test.js index 5ef89ecf..6912bd80 100644 --- a/src/packages/luxify/test/luxify.test.js +++ b/src/packages/luxify/test/luxify.test.js @@ -38,7 +38,8 @@ describe('module "luxify"', () => { it('resolves when Response#send is called', () => { const subject = luxify((req, res) => { - Reflect.apply(Reflect.get(res, 'send'), res, ['Hello world!']); + // $FlowIgnore + res.send('Hello world!'); }); return subject(request, response).then(data => { @@ -48,9 +49,10 @@ describe('module "luxify"', () => { it('resolves when Response#json is called', () => { const subject = luxify((req, res) => { - Reflect.apply(Reflect.get(res, 'json'), res, [{ + // $FlowIgnore + res.json({ data: 'Hello world!' - }]); + }); }); return subject(request, response).then(data => { diff --git a/src/packages/luxify/utils/create-response-proxy.js b/src/packages/luxify/utils/create-response-proxy.js index f9bc01e2..99abd25a 100644 --- a/src/packages/luxify/utils/create-response-proxy.js +++ b/src/packages/luxify/utils/create-response-proxy.js @@ -20,7 +20,7 @@ export default function createResponseProxy( return resolve; default: - return Reflect.get(target, key); + return target[key]; } } }); diff --git a/src/packages/router/definitions/context/index.js b/src/packages/router/definitions/context/index.js index 75e04008..fd3a0ce4 100644 --- a/src/packages/router/definitions/context/index.js +++ b/src/packages/router/definitions/context/index.js @@ -29,15 +29,11 @@ export function contextFor(build: Router$DefinitionBuilder<*>) { ...context, member(builder: () => void) { - const childCtx = createDefinitionGroup('member', namespace); - - Reflect.apply(builder, childCtx, []); + builder.call(createDefinitionGroup('member', namespace)); }, collection(builder: () => void) { - const childCtx = createDefinitionGroup('collection', namespace); - - Reflect.apply(builder, childCtx, []); + builder.call(createDefinitionGroup('collection', namespace)); } }; } else { diff --git a/src/packages/router/definitions/context/utils/create-definition-group.js b/src/packages/router/definitions/context/utils/create-definition-group.js index 9c953329..117e2222 100644 --- a/src/packages/router/definitions/context/utils/create-definition-group.js +++ b/src/packages/router/definitions/context/utils/create-definition-group.js @@ -1,22 +1,55 @@ // @flow -import { REQUEST_METHODS } from '../../../../server'; import type { Route$type, Router$Namespace } from '../../../index'; // eslint-disable-line max-len, no-unused-vars +import type { Router$Definition } from '../../interfaces'; import createDefinition from './create-definition'; +type Router$DefinitionGroup = { + get: Router$Definition; + head: Router$Definition; + post: Router$Definition; + patch: Router$Definition; + delete: Router$Definition; + options: Router$Definition; +}; + /** * @private */ export default function createDefinitionGroup( type: Route$type, namespace: T -) { - return REQUEST_METHODS.reduce((methods, method) => ({ - ...methods, - [method.toLowerCase()]: createDefinition({ +): Router$DefinitionGroup { + return { + get: createDefinition({ + type, + namespace, + method: 'GET' + }), + head: createDefinition({ + type, + namespace, + method: 'HEAD' + }), + post: createDefinition({ + type, + namespace, + method: 'POST' + }), + patch: createDefinition({ + type, + namespace, + method: 'PATCH' + }), + delete: createDefinition({ + type, + namespace, + method: 'DELETE' + }), + options: createDefinition({ type, - method, - namespace + namespace, + method: 'OPTIONS' }) - }), {}); + }; } diff --git a/src/packages/router/definitions/context/utils/create-definition.js b/src/packages/router/definitions/context/utils/create-definition.js index d8d24613..e1b52494 100644 --- a/src/packages/router/definitions/context/utils/create-definition.js +++ b/src/packages/router/definitions/context/utils/create-definition.js @@ -3,6 +3,7 @@ import { Route } from '../../../index'; import { normalizeName, normalizePath } from '../../../namespace'; import type { Request$method } from '../../../../server'; import type { Router$Namespace, Route$type } from '../../../index'; // eslint-disable-line max-len, no-duplicate-imports +import type { Router$Definition } from '../../interfaces'; /** * @private @@ -11,7 +12,7 @@ export default function createDefinition({ type, method, namespace }: { type: Route$type; method: Request$method; namespace: Router$Namespace; -}) { +}): Router$Definition { return function define(name: string, action?: string = normalizeName(name)) { const normalized = normalizeName(name); const { controller } = namespace; diff --git a/src/packages/router/definitions/index.js b/src/packages/router/definitions/index.js index 64316f43..0d924a80 100644 --- a/src/packages/router/definitions/index.js +++ b/src/packages/router/definitions/index.js @@ -40,7 +40,7 @@ export function build(builder?: () => void, namespace: T) { } if (builder) { - Reflect.apply(builder, context, []); + builder.call(context); } return namespace; diff --git a/src/packages/router/definitions/interfaces.js b/src/packages/router/definitions/interfaces.js index 332bd6d2..a4012d65 100644 --- a/src/packages/router/definitions/interfaces.js +++ b/src/packages/router/definitions/interfaces.js @@ -1,6 +1,7 @@ // @flow import type { Router$Namespace, Resource$opts } from '../index'; +export type Router$Definition = (name: string, action: string) => void; export type Router$DefinitionBuilder = ( builder?: () => void, namespace: T diff --git a/src/packages/router/index.js b/src/packages/router/index.js index 201ed68d..ccdec014 100644 --- a/src/packages/router/index.js +++ b/src/packages/router/index.js @@ -25,7 +25,7 @@ class Router extends FreezeableMap { super(); define(this, definitions); - Reflect.defineProperty(this, 'replacer', { + Object.defineProperty(this, 'replacer', { value: createReplacer(controllers), writable: false, enumerable: false, @@ -35,14 +35,16 @@ class Router extends FreezeableMap { this.freeze(); } - match({ method, url }: Request): void | Route { + match(req: Request): void | Route { const params = []; + const { url, method } = req; + const staticPath = url.pathname.replace(this.replacer, (str, g1, g2) => { params.push(g2); return `${g1}/:dynamic`; }); - Reflect.set(url, 'params', params); + url.params = params; return this.get(`${method}:${staticPath}`); } diff --git a/src/packages/router/resource/index.js b/src/packages/router/resource/index.js index faca5afc..d9c942a3 100644 --- a/src/packages/router/resource/index.js +++ b/src/packages/router/resource/index.js @@ -15,7 +15,7 @@ class Resource extends Namespace { constructor({ only, ...opts }: Resource$opts) { super(opts); - Reflect.defineProperty(this, 'only', { + Object.defineProperty(this, 'only', { value: new FreezeableSet(normalizeOnly(only)), writable: false, enumerable: false, diff --git a/src/packages/router/route/action/enhancers/resource.js b/src/packages/router/route/action/enhancers/resource.js index 3554887f..a4089347 100644 --- a/src/packages/router/route/action/enhancers/resource.js +++ b/src/packages/router/route/action/enhancers/resource.js @@ -74,7 +74,7 @@ export default function resource(action: Action): Action { return data; }; - Reflect.defineProperty(resourceAction, 'name', { + Object.defineProperty(resourceAction, 'name', { value: action.name }); diff --git a/src/packages/router/route/action/enhancers/track-perf.js b/src/packages/router/route/action/enhancers/track-perf.js index 7b944888..e14fc7f3 100644 --- a/src/packages/router/route/action/enhancers/track-perf.js +++ b/src/packages/router/route/action/enhancers/track-perf.js @@ -33,7 +33,7 @@ export default function trackPerf>(action: U): Action { return result; }; - Reflect.defineProperty(trackedAction, 'name', { + Object.defineProperty(trackedAction, 'name', { value: action.name }); diff --git a/src/packages/router/route/index.js b/src/packages/router/route/index.js index 8745894f..2fbe71a0 100644 --- a/src/packages/router/route/index.js +++ b/src/packages/router/route/index.js @@ -43,7 +43,8 @@ class Route extends FreezeableSet> { const dynamicSegments = getDynamicSegments(path); if (action && controller) { - const handler = Reflect.get(controller, action); + // $FlowIgnore + const handler = controller[action]; if (typeof handler === 'function') { const params = paramsFor({ diff --git a/src/packages/router/route/params/parameter-group/index.js b/src/packages/router/route/params/parameter-group/index.js index e31a8579..23df4174 100644 --- a/src/packages/router/route/params/parameter-group/index.js +++ b/src/packages/router/route/params/parameter-group/index.js @@ -56,7 +56,7 @@ class ParameterGroup extends FreezeableMap { const match = this.get(key); if (match) { - Reflect.set(validated, key, match.validate(value)); + validated[key] = match.validate(value); } else if (!match && !sanitize) { throw new InvalidParameterError(`${path}${key}`); } diff --git a/src/packages/router/route/params/parameter-group/utils/has-required-params.js b/src/packages/router/route/params/parameter-group/utils/has-required-params.js index 38084c07..f315d5ed 100644 --- a/src/packages/router/route/params/parameter-group/utils/has-required-params.js +++ b/src/packages/router/route/params/parameter-group/utils/has-required-params.js @@ -10,7 +10,7 @@ export default function hasRequiredParams( params: Object ): boolean { for (const [key, { path, required }] of group) { - if (required && !Reflect.has(params, key)) { + if (required && !(key in params)) { throw new ParameterRequiredError(path); } } diff --git a/src/packages/serializer/test/serializer.test.js b/src/packages/serializer/test/serializer.test.js index 7465fd1a..c2f4cc0d 100644 --- a/src/packages/serializer/test/serializer.test.js +++ b/src/packages/serializer/test/serializer.test.js @@ -89,11 +89,14 @@ describe('module "serializer"', () => { let include = []; const run = async trx => { // $FlowIgnore - const post = await Post.transacting(trx).create({ - body: faker.lorem.paragraphs(), - title: faker.lorem.sentence(), - isPublic: faker.random.boolean() - }); + const post = await Post + .transacting(trx) + .create({ + body: faker.lorem.paragraphs(), + title: faker.lorem.sentence(), + isPublic: faker.random.boolean() + }) + .then(record => record.unwrap()); const postId = post.getPrimaryKey(); @@ -108,7 +111,8 @@ describe('module "serializer"', () => { instances.add(user); include = [...include, 'user']; - Reflect.set(post, 'user', user); + // $FlowIgnore + post.user = user; } if (includeImage) { @@ -231,10 +235,14 @@ describe('module "serializer"', () => { image, comments ] = await Promise.all([ - Reflect.get(post, 'user'), - Reflect.get(post, 'tags'), - Reflect.get(post, 'image'), - Reflect.get(post, 'comments') + // $FlowIgnore + post.user, + // $FlowIgnore + post.tags, + // $FlowIgnore + post.image, + // $FlowIgnore + post.comments ]); const postId = post.getPrimaryKey(); @@ -464,7 +472,8 @@ describe('module "serializer"', () => { it('supports including a has-one relationship', async () => { const post = await createPost(); - const image = await Reflect.get(post, 'image'); + // $FlowIgnore + const image = await post.image; const result = await subject.format({ data: post, domain: DOMAIN, @@ -495,7 +504,8 @@ describe('module "serializer"', () => { it('supports including belongs-to relationships', async () => { const post = await createPost(); - const user = await Reflect.get(post, 'user'); + // $FlowIgnore + const user = await post.user; const result = await subject.format({ data: post, domain: DOMAIN, @@ -527,7 +537,8 @@ describe('module "serializer"', () => { it('supports including a one-to-many relationship', async () => { const post = await createPost(); - const comments = await Reflect.get(post, 'comments'); + // $FlowIgnore + const comments = await post.comments; const result = await subject.format({ data: post, domain: DOMAIN, @@ -566,7 +577,8 @@ describe('module "serializer"', () => { it('supports including a many-to-many relationship', async () => { const post = await createPost(); - const tags = await Reflect.get(post, 'tags'); + // $FlowIgnore + const tags = await post.tags; const result = await subject.format({ data: post, domain: DOMAIN, diff --git a/src/packages/server/request/parser/utils/parse-nested-object.js b/src/packages/server/request/parser/utils/parse-nested-object.js index 8346cce0..db3979f7 100644 --- a/src/packages/server/request/parser/utils/parse-nested-object.js +++ b/src/packages/server/request/parser/utils/parse-nested-object.js @@ -10,7 +10,7 @@ export default function parseNestedObject(source: Object): Object { return entries(source).reduce((result, [key, value]) => { if (DELIMITER.test(key)) { const parentKey = key.replace(DELIMITER, '$1'); - const parentValue = Reflect.get(result, parentKey); + const parentValue = result[parentKey]; return { ...result, diff --git a/src/packages/server/responder/index.js b/src/packages/server/responder/index.js index c03bc8e5..8a6ed6d6 100644 --- a/src/packages/server/responder/index.js +++ b/src/packages/server/responder/index.js @@ -9,17 +9,17 @@ import hasContentType from './utils/has-content-type'; * @private */ export function createResponder(req: Request, res: Response) { - return function respond(data?: ?mixed) { - const normalized = normalize(data); + return function respond(input: any) { + const { data, statusCode } = normalize(input); - if (normalized.statusCode) { - Reflect.set(res, 'statusCode', normalized.statusCode); + if (statusCode) { + res.status(statusCode); } - if (res.statusCode !== 204 && !hasContentType(res)) { + if (statusCode !== 204 && !hasContentType(res)) { res.setHeader('Content-Type', MIME_TYPE); } - res.end(normalized.data); + res.end(data); }; } diff --git a/src/packages/server/response/index.js b/src/packages/server/response/index.js index 057e9fc7..ac0d233a 100644 --- a/src/packages/server/response/index.js +++ b/src/packages/server/response/index.js @@ -13,7 +13,14 @@ import type { Response, Response$opts } from './interfaces'; * @private */ export function createResponse(res: any, opts: Response$opts): Response { - return Object.assign(res, opts, { - stats: [] + const response = res; + + return Object.assign(response, opts, { + stats: [], + + status(value: number): Response { + response.statusCode = value; + return response; + } }); } diff --git a/src/packages/server/response/interfaces.js b/src/packages/server/response/interfaces.js index 36eb3109..a073a170 100644 --- a/src/packages/server/response/interfaces.js +++ b/src/packages/server/response/interfaces.js @@ -12,7 +12,7 @@ export type Response$opts = { logger: Logger }; -declare export class Response extends stream$Writable { +export interface Response extends stream$Writable { [key: string]: void | ?mixed; stats: Array; @@ -20,6 +20,7 @@ declare export class Response extends stream$Writable { statusCode: number; statusMessage: string; + status(value: number): Response; getHeader(name: string): void | string; setHeader(name: string, value: string): void; removeHeader(name: string): void; diff --git a/src/packages/server/utils/create-server-error.js b/src/packages/server/utils/create-server-error.js index f461f238..b757c068 100644 --- a/src/packages/server/utils/create-server-error.js +++ b/src/packages/server/utils/create-server-error.js @@ -17,7 +17,7 @@ export default function createServerError( } }; - Reflect.defineProperty(ServerError, 'name', { + Object.defineProperty(ServerError, 'name', { value: Target.name }); diff --git a/src/utils/chain.js b/src/utils/chain.js index ed344ab7..774bb387 100644 --- a/src/utils/chain.js +++ b/src/utils/chain.js @@ -15,7 +15,8 @@ export default function chain(source: T): Chain { }, construct>(constructor: V): Chain { - return chain(Reflect.construct(constructor, [source])); + // $FlowIgnore + return chain(new constructor(source)); } }; } diff --git a/src/utils/proxy.js b/src/utils/proxy.js index 462aa3d3..3a075d53 100644 --- a/src/utils/proxy.js +++ b/src/utils/proxy.js @@ -13,7 +13,7 @@ export function trapGet(traps: Object): Proxy$get { } if (hasOwnProperty(traps, key)) { - const value = Reflect.get(traps, key); + const value = traps[key]; if (typeof value === 'function') { return value.bind(receiver, target); @@ -22,6 +22,7 @@ export function trapGet(traps: Object): Proxy$get { return value; } - return Reflect.get(target, key); + // $FlowIgnore + return target[key]; }; } diff --git a/src/utils/test/k.test.js b/src/utils/test/k.test.js index a4e18955..8d14f7bd 100644 --- a/src/utils/test/k.test.js +++ b/src/utils/test/k.test.js @@ -8,8 +8,8 @@ describe('util K()', () => { it('always returns the context it is called with', () => { const obj = {}; - expect(Reflect.apply(K, 1, [])).to.equal(1); - expect(Reflect.apply(K, obj, [])).to.equal(obj); - expect(Reflect.apply(K, 'Test', [])).to.equal('Test'); + expect(K.call(1)).to.equal(1); + expect(K.call(obj)).to.equal(obj); + expect(K.call('Test')).to.equal('Test'); }); }); From 4c8ccdf12e6b84ea5df5ce8801ebf747a0bf31e2 Mon Sep 17 00:00:00 2001 From: Zachary Golba Date: Sun, 18 Dec 2016 17:06:33 -0500 Subject: [PATCH 05/12] refactor: improve request parsing algorithms --- .eslintrc.json | 1 + src/packages/server/constants.js | 3 +- src/packages/server/index.js | 73 +++++++------------ src/packages/server/request/parser/index.js | 7 +- .../server/request/parser/utils/format.js | 33 +++++---- .../parser/utils/parse-nested-object.js | 26 +++---- .../request/parser/utils/parse-write.js | 53 +++++++++----- .../responder/utils/has-content-type.js | 10 +-- 8 files changed, 101 insertions(+), 105 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index 2f5b7e93..ae66f1cd 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -32,6 +32,7 @@ "arrow-parens": 0, "comma-dangle": [2, "only-multiline"], "global-require": 0, + "operator-assignment": 0, "class-methods-use-this": 0, "no-restricted-syntax": [ "error", diff --git a/src/packages/server/constants.js b/src/packages/server/constants.js index a1e8d988..8a7c6f73 100644 --- a/src/packages/server/constants.js +++ b/src/packages/server/constants.js @@ -1,6 +1,5 @@ // @flow -export const HAS_BODY = /^(POST|PATCH)$/; - +export const HAS_BODY = /^(?:POST|PATCH)$/; export const STATUS_CODES = new Map([ [100, 'Continue'], [101, 'Switching Protocols'], diff --git a/src/packages/server/index.js b/src/packages/server/index.js index 7b1b2d06..5a4d933e 100644 --- a/src/packages/server/index.js +++ b/src/packages/server/index.js @@ -3,6 +3,7 @@ import { createServer } from 'http'; import type { Writable } from 'stream'; import type { IncomingMessage, Server as HTTPServer } from 'http'; // eslint-disable-line max-len, no-duplicate-imports +import { freezeProps } from '../freezeable'; import { tryCatchSync } from '../../utils/try-catch'; import type Logger from '../logger'; import type Router from '../router'; @@ -30,36 +31,22 @@ class Server { instance: HTTPServer; - constructor({ logger, router, cors }: Server$opts) { - Object.defineProperties(this, { - router: { - value: router, - writable: false, - enumerable: false, - configurable: false - }, - - logger: { - value: logger, - writable: false, - enumerable: false, - configurable: false - }, - - cors: { - value: cors, - writable: false, - enumerable: false, - configurable: false - }, - - instance: { - value: createServer(this.receiveRequest), - writable: false, - enumerable: false, - configurable: false - } + constructor({ logger, router, cors }: Server$opts): this { + Object.assign(this, { + cors, + router, + logger, + instance: createServer(this.receiveRequest) }); + + freezeProps(this, false, + 'cors', + 'router', + 'logger', + 'instance' + ); + + return this; } listen(port: number): void { @@ -67,22 +54,21 @@ class Server { } initializeRequest(req: IncomingMessage, res: Writable): [Request, Response] { - const { logger, router, cors } = this; - - req.setEncoding('utf8'); - const response = createResponse(res, { - logger + logger: this.logger }); - setCORSHeaders(response, cors); + setCORSHeaders(response, this.cors); const request = createRequest(req, { - logger, - router + logger: this.logger, + router: this.router }); - return [request, response]; + return [ + request, + response + ]; } validateRequest({ method, headers }: Request): true { @@ -96,11 +82,10 @@ class Server { } receiveRequest = (req: IncomingMessage, res: Writable): void => { - const { logger } = this; const [request, response] = this.initializeRequest(req, res); const respond = createResponder(request, response); - logger.request(request, response, { + this.logger.request(request, response, { startTime: Date.now() }); @@ -109,21 +94,19 @@ class Server { if (isValid) { parseRequest(request) .then(params => { - const { route } = request; - Object.assign(request, { params }); - if (route) { - return route.visit(request, response); + if (request.route) { + return request.route.visit(request, response); } return undefined; }) .then(respond) .catch(err => { - logger.error(err); + this.logger.error(err); respond(err); }); } diff --git a/src/packages/server/request/parser/index.js b/src/packages/server/request/parser/index.js index 95c3032c..bb23def3 100644 --- a/src/packages/server/request/parser/index.js +++ b/src/packages/server/request/parser/index.js @@ -11,10 +11,9 @@ export function parseRequest(req: Request): Promise { switch (req.method) { case 'POST': case 'PATCH': - return parseWrite(req).then(params => ({ - ...parseRead(req), - ...params - })); + return parseWrite(req).then(params => ( + Object.assign(params, parseRead(req)) + )); default: return Promise.resolve(parseRead(req)); diff --git a/src/packages/server/request/parser/utils/format.js b/src/packages/server/request/parser/utils/format.js index 3cfc811b..012694b8 100644 --- a/src/packages/server/request/parser/utils/format.js +++ b/src/packages/server/request/parser/utils/format.js @@ -78,10 +78,12 @@ export function formatSort(sort: string): string { * @private */ export function formatFields(fields: Object): Object { - return entries(fields).reduce((result, [key, value]) => ({ - ...result, - [key]: makeArray(value) - }), {}); + return entries(fields).reduce((obj, [key, value]) => { + const result = obj; + + result[key] = makeArray(value); + return result; + }, {}); } /** @@ -96,30 +98,29 @@ export function formatInclude(include: string | Array): Array { */ export default function format(params: Object, method: Request$method): Object { const result = entries(params).reduce((obj, param) => { + const data = obj; const [, value] = param; let [key] = param; + let formatted; key = key.replace(BRACKETS, ''); switch (typeof value) { case 'object': - return { - ...obj, - [key]: isNull(value) ? null : formatObject(value, method, format) - }; + formatted = isNull(value) ? null : formatObject(value, method, format); + break; case 'string': - return { - ...obj, - [key]: formatString(value, key === 'id' ? 'GET' : method) - }; + formatted = formatString(value, key === 'id' ? 'GET' : method); + break; default: - return { - ...obj, - [key]: value - }; + formatted = value; + break; } + + data[key] = formatted; + return data; }, {}); return camelizeKeys(result, true); diff --git a/src/packages/server/request/parser/utils/parse-nested-object.js b/src/packages/server/request/parser/utils/parse-nested-object.js index db3979f7..8bdba7f8 100644 --- a/src/packages/server/request/parser/utils/parse-nested-object.js +++ b/src/packages/server/request/parser/utils/parse-nested-object.js @@ -7,23 +7,23 @@ const DELIMITER = /^(.+)\[(.+)]$/g; * @private */ export default function parseNestedObject(source: Object): Object { - return entries(source).reduce((result, [key, value]) => { + return entries(source).reduce((obj, [key, value]) => { + const result = obj; + if (DELIMITER.test(key)) { const parentKey = key.replace(DELIMITER, '$1'); - const parentValue = result[parentKey]; + let parentValue = result[parentKey]; + + if (!parentValue) { + parentValue = {}; + result[parentKey] = parentValue; + } - return { - ...result, - [parentKey]: { - ...(parentValue || {}), - [key.replace(DELIMITER, '$2')]: value - } - }; + parentValue[key.replace(DELIMITER, '$2')] = value; + } else { + result[key] = value; } - return { - ...result, - [key]: value - }; + return result; }, {}); } diff --git a/src/packages/server/request/parser/utils/parse-write.js b/src/packages/server/request/parser/utils/parse-write.js index 42c76308..ba8770e4 100644 --- a/src/packages/server/request/parser/utils/parse-write.js +++ b/src/packages/server/request/parser/utils/parse-write.js @@ -5,37 +5,56 @@ import type { Request } from '../../interfaces'; import format from './format'; +function getLength(req: Request): number { + const contentLength = req.headers.get('content-length'); + + if (contentLength) { + const length = Number.parseInt(contentLength, 10); + + if (Number.isFinite(length)) { + return length; + } + } + + return 0; +} + /** * @private */ export default function parseWrite(req: Request): Promise { return new Promise((resolve, reject) => { - let body = ''; - const cleanUp = () => { - req.removeAllListeners('end'); - req.removeAllListeners('data'); - req.removeAllListeners('error'); - }; + const body = Buffer.allocUnsafe(getLength(req)); + let offset = 0; - req.on('data', data => { - body += data.toString(); - }); + const handleData = data => { + data.copy(body, offset); + offset = offset + data.length; + }; - req.once('end', () => { - const parsed = tryCatchSync(() => JSON.parse(body)); + const handleEnd = () => { + const parsed = tryCatchSync(() => JSON.parse(body.toString())); - cleanUp(); + // eslint-disable-next-line no-use-before-define + req.removeListener('error', handleError); + req.removeListener('data', handleData); + req.removeListener('end', handleEnd); if (parsed) { resolve(format(parsed, req.method)); } else { reject(new MalformedRequestError()); } - }); + }; + + const handleError = () => { + req.removeListener('error', handleError); + req.removeListener('data', handleData); + req.removeListener('end', handleEnd); + }; - req.once('error', err => { - cleanUp(); - reject(err); - }); + req.on('data', handleData); + req.once('end', handleEnd); + req.once('error', handleError); }); } diff --git a/src/packages/server/responder/utils/has-content-type.js b/src/packages/server/responder/utils/has-content-type.js index 044f7cc0..45e25186 100644 --- a/src/packages/server/responder/utils/has-content-type.js +++ b/src/packages/server/responder/utils/has-content-type.js @@ -1,12 +1,6 @@ // @flow import type { Response } from '../../index'; -export default function hasContentType(res: Response) { - let contentType = res.getHeader('Content-Type'); - - if (!contentType) { - contentType = res.getHeader('content-type'); - } - - return Boolean(contentType); +export default function hasContentType(res: Response): boolean { + return Boolean(res.getHeader('content-type')); } From 32a82ffa635fe44d83689c63a6bb84ae2238ed2d Mon Sep 17 00:00:00 2001 From: Zachary Golba Date: Sun, 18 Dec 2016 17:26:52 -0500 Subject: [PATCH 06/12] refactor: improve route algorithms --- src/constants.js | 1 + src/packages/router/route/action/constants.js | 2 - .../router/route/action/enhancers/resource.js | 109 +++++----- .../route/action/enhancers/track-perf.js | 36 ++-- src/packages/router/route/action/index.js | 25 ++- src/packages/router/route/index.js | 192 ++++++++++-------- 6 files changed, 192 insertions(+), 173 deletions(-) delete mode 100644 src/packages/router/route/action/constants.js diff --git a/src/constants.js b/src/constants.js index a5c06ee7..aa7781ca 100644 --- a/src/constants.js +++ b/src/constants.js @@ -24,3 +24,4 @@ export const PLATFORM = platform(); export const BACKSLASH = /\\/g; export const CIRCLECI = ENV.CIRCLECI; export const APPVEYOR = ENV.APPVEYOR; +export const IS_PRODUCTION = NODE_ENV === 'production'; diff --git a/src/packages/router/route/action/constants.js b/src/packages/router/route/action/constants.js deleted file mode 100644 index 77d0abb2..00000000 --- a/src/packages/router/route/action/constants.js +++ /dev/null @@ -1,2 +0,0 @@ -// @flow -export const FINAL_HANDLER = '__FINAL_HANDLER__'; diff --git a/src/packages/router/route/action/enhancers/resource.js b/src/packages/router/route/action/enhancers/resource.js index a4089347..eff05f3b 100644 --- a/src/packages/router/route/action/enhancers/resource.js +++ b/src/packages/router/route/action/enhancers/resource.js @@ -9,69 +9,68 @@ import type { Action } from '../interfaces'; */ export default function resource(action: Action): Action { // eslint-disable-next-line func-names - const resourceAction = async function (req, res) { - const { route: { action: actionName } } = req; - const result = action(req, res); - let links = {}; - let data; - let total; + const resourceAction = function (req, res) { + const isIndex = req.route.action === 'index'; - if (actionName === 'index') { - [data, total] = await Promise.all([ - result, - Query.from(result).count() - ]); - } else { - data = await result; + const init = [ + action(req, res), + Promise.resolve(0) + ]; + + if (isIndex) { + init[1] = Query.from(init[0]).count(); } - if (Array.isArray(data) || (data && data.isModelInstance)) { - const domain = getDomain(req); + return Promise + .all(init) + .then(([data, total]) => { + if (Array.isArray(data) || (data && data.isModelInstance)) { + const domain = getDomain(req); + let links = {}; - const { - params, - url: { - path, - pathname - }, - route: { - controller: { - namespace, - serializer, - defaultPerPage - } - } - } = req; + const { + params, + url: { + path, + pathname + }, + route: { + controller: { + namespace, + serializer, + defaultPerPage + } + } + } = req; - const include = params.include || []; + if (isIndex) { + links = createPageLinks({ + params, + domain, + pathname, + defaultPerPage, + total: total || 0 + }); + } else if (namespace) { + links = { + self: domain.replace(`/${namespace}`, '') + path + }; + } else { + links = { + self: domain + path + }; + } - if (actionName === 'index') { - links = createPageLinks({ - params, - domain, - pathname, - defaultPerPage, - total: total || 0 - }); - } else if (actionName !== 'index' && namespace) { - links = { - self: domain.replace(`/${namespace}`, '') + path - }; - } else if (actionName !== 'index' && !namespace) { - links = { - self: domain + path - }; - } + return serializer.format({ + data, + links, + domain, + include: params.include || [] + }); + } - return serializer.format({ - data, - links, - domain, - include + return data; }); - } - - return data; }; Object.defineProperty(resourceAction, 'name', { diff --git a/src/packages/router/route/action/enhancers/track-perf.js b/src/packages/router/route/action/enhancers/track-perf.js index e14fc7f3..f72f90df 100644 --- a/src/packages/router/route/action/enhancers/track-perf.js +++ b/src/packages/router/route/action/enhancers/track-perf.js @@ -1,6 +1,4 @@ // @flow -import { FINAL_HANDLER } from '../constants'; -import getActionName from '../utils/get-action-name'; import getControllerName from '../utils/get-controller-name'; import type { Action } from '../interfaces'; @@ -9,28 +7,26 @@ import type { Action } from '../interfaces'; */ export default function trackPerf>(action: U): Action { // eslint-disable-next-line func-names - const trackedAction = async function (...args: Array) { - const [req, res] = args; + const trackedAction = function (req, res, data) { const start = Date.now(); - const result = await action(...args); - let { name } = action; - let type = 'middleware'; - if (name === FINAL_HANDLER) { - type = 'action'; - name = getActionName(req); - } else if (!name) { - name = 'anonymous'; - } + return action(req, res, data).then(result => { + let { name } = action; + const type = 'middleware'; - res.stats.push({ - type, - name, - duration: Date.now() - start, - controller: getControllerName(req) - }); + if (!name) { + name = 'anonymous'; + } + + res.stats.push({ + type, + name, + duration: Date.now() - start, + controller: getControllerName(req) + }); - return result; + return result; + }); }; Object.defineProperty(trackedAction, 'name', { diff --git a/src/packages/router/route/action/index.js b/src/packages/router/route/action/index.js index c6ec4f73..4fa68843 100644 --- a/src/packages/router/route/action/index.js +++ b/src/packages/router/route/action/index.js @@ -1,4 +1,6 @@ // @flow +import { IS_PRODUCTION } from '../../../../constants'; +import insert from '../../../../utils/insert'; import type Controller from '../../../controller'; import resource from './enhancers/resource'; @@ -13,23 +15,24 @@ export function createAction( action: Action, controller: Controller ): Array> { - let fn = action.bind(controller); + let controllerAction = action.bind(controller); if (type !== 'custom' && controller.hasModel && controller.hasSerializer) { - fn = resource(fn); + controllerAction = resource(controllerAction); } - return [ + let handlers = [ ...controller.beforeAction, - // eslint-disable-next-line no-underscore-dangle - function __FINAL_HANDLER__(req, res) { - return fn(req, res); - }, - ...controller.afterAction, - ].map(trackPerf); + controllerAction, + ...controller.afterAction + ]; + + if (!IS_PRODUCTION) { + handlers = handlers.map(trackPerf); + } + + return insert(new Array(handlers.length), handlers); } -export { FINAL_HANDLER } from './constants'; export { default as createPageLinks } from './utils/create-page-links'; - export type { Action } from './interfaces'; diff --git a/src/packages/router/route/index.js b/src/packages/router/route/index.js index 2fbe71a0..71721c68 100644 --- a/src/packages/router/route/index.js +++ b/src/packages/router/route/index.js @@ -1,9 +1,10 @@ // @flow import { FreezeableSet, freezeProps, deepFreezeProps } from '../../freezeable'; +import { tryCatchSync } from '../../../utils/try-catch'; import type Controller from '../../controller'; import type { Request, Response, Request$method } from '../../server'; -import { FINAL_HANDLER, createAction } from './action'; +import { createAction } from './action'; import { paramsFor, defaultParamsFor, validateResourceId } from './params'; import getStaticPath from './utils/get-static-path'; import getDynamicSegments from './utils/get-dynamic-segments'; @@ -25,10 +26,14 @@ class Route extends FreezeableSet> { method: Request$method; + handlers: Array>; + controller: Controller; staticPath: string; + actionIndex: number; + defaultParams: Object; dynamicSegments: Array; @@ -40,75 +45,72 @@ class Route extends FreezeableSet> { method, controller }: Route$opts) { - const dynamicSegments = getDynamicSegments(path); + // $FlowIgnore + const handler = controller[action]; + + if (typeof handler !== 'function') { + const { + constructor: { + name: controllerName + } + } = controller; - if (action && controller) { - // $FlowIgnore - const handler = controller[action]; - - if (typeof handler === 'function') { - const params = paramsFor({ - type, - method, - controller, - dynamicSegments - }); - - const staticPath = getStaticPath(path, dynamicSegments); - - const defaultParams = defaultParamsFor({ - type, - controller - }); - - super(createAction(type, handler, controller)); - - Object.assign(this, { - type, - path, - params, - action, - method, - controller, - staticPath, - defaultParams, - dynamicSegments - }); - - freezeProps(this, true, - 'type', - 'path' - ); - - freezeProps(this, false, - 'action', - 'params', - 'method', - 'controller', - 'staticPath' - ); - - deepFreezeProps(this, false, - 'defaultParams', - 'dynamicSegments' - ); - } else { - const { - constructor: { - name: controllerName - } - } = controller; - - throw new TypeError( - `Handler for ${controllerName}#${action} is not a function.` - ); - } - } else { throw new TypeError( - 'Arguements `controller` and `action` must not be undefined' + `Handler for ${controllerName}#${action} is not a function.` ); } + const dynamicSegments = getDynamicSegments(path); + const staticPath = getStaticPath(path, dynamicSegments); + + const params = paramsFor({ + type, + method, + controller, + dynamicSegments + }); + + const defaultParams = defaultParamsFor({ + type, + controller + }); + + const handlers = createAction(type, handler, controller); + + super(handlers); + + Object.assign(this, { + type, + path, + params, + action, + method, + handlers, + controller, + staticPath, + defaultParams, + dynamicSegments, + actionIndex: controller.beforeAction.length + }); + + freezeProps(this, true, + 'type', + 'path' + ); + + freezeProps(this, false, + 'action', + 'params', + 'method', + 'controller', + 'staticPath' + ); + + deepFreezeProps(this, false, + 'defaultParams', + 'dynamicSegments' + ); + this.freeze(); } @@ -127,39 +129,59 @@ class Route extends FreezeableSet> { }, {}); } - async execHandlers(req: Request, res: Response): Promise { + execHandlers(req: Request, res: Response): Promise { + let result = Promise.resolve(); let calledFinal = false; - let data; + let shouldContinue = true; + const { handlers, actionIndex } = this; - for (const handler of this) { - // eslint-disable-next-line no-await-in-loop - data = await handler(req, res, data); - - if (handler.name === FINAL_HANDLER) { - calledFinal = true; + const step = (prev, nextIdx) => prev.then(data => { + if (!calledFinal && typeof data !== 'undefined') { + shouldContinue = false; + return prev; } - if (!calledFinal && typeof data !== 'undefined') { + return handlers[nextIdx](req, res, data); + }); + + for (let i = 0; i < handlers.length; i = i + 1) { + if (!shouldContinue) { break; } + + result = step(result, i); + + if (i === actionIndex) { + calledFinal = true; + } } - return data; + return result; } - async visit(req: Request, res: Response): Promise { - const { defaultParams } = this; + visit(req: Request, res: Response): Promise { + let error; - Object.assign(req, { - defaultParams, - params: this.params.validate({ - ...req.params, - ...this.parseParams(req.url.params) - }) + tryCatchSync(() => { + const { defaultParams } = this; + + Object.assign(req, { + defaultParams, + params: this.params.validate({ + ...req.params, + ...this.parseParams(req.url.params) + }) + }); + + if (this.type === 'member' && req.method === 'PATCH') { + validateResourceId(req); + } + }, err => { + error = err; }); - if (this.type === 'member' && req.method === 'PATCH') { - validateResourceId(req); + if (error) { + return Promise.reject(error); } return this.execHandlers(req, res); From a4385696f9bfd50dcef323d9e1f4bb1585c38184 Mon Sep 17 00:00:00 2001 From: Zachary Golba Date: Sun, 18 Dec 2016 17:45:04 -0500 Subject: [PATCH 07/12] refactor: optimize compiled output --- scripts/build/config/index.js | 34 +++++----- src/packages/cli/commands/dbcreate.js | 17 +++-- src/packages/cli/commands/dbdrop.js | 17 +++-- src/packages/cli/commands/dbseed.js | 11 ++-- src/packages/compiler/index.js | 94 ++++++++++++++------------- src/packages/compiler/utils/config.js | 32 +++++++++ 6 files changed, 120 insertions(+), 85 deletions(-) create mode 100644 src/packages/compiler/utils/config.js diff --git a/scripts/build/config/index.js b/scripts/build/config/index.js index e51af63f..ed914220 100644 --- a/scripts/build/config/index.js +++ b/scripts/build/config/index.js @@ -1,7 +1,10 @@ + 'use strict'; require('../../../lib/babel-hook'); +const SRC = '../../../src'; + const fs = require('fs'); const path = require('path'); @@ -10,9 +13,11 @@ const json = require('rollup-plugin-json'); const babel = require('rollup-plugin-babel'); const nodeResolve = require('rollup-plugin-node-resolve'); -const template = require('../../../src/packages/template').default; +const compiler = require(`${SRC}/packages/compiler`); +const { default: template } = require(`${SRC}/packages/template`); + +const banner = template` -const BANNER = template` 'use strict'; require('source-map-support').install({ @@ -21,25 +26,20 @@ const BANNER = template` `; module.exports = { - rollup: { + rollup: compiler.createRollupConfig({ + plugins: [ + json(), + babel(), + nodeResolve({ preferBuiltins: true }) + ], external: [ 'knex', 'bundle', path.join(__dirname, '..', '..', '..', 'lib', 'fs', 'index.js'), ...fs.readdirSync(path.join(__dirname, '..', '..', '..', 'node_modules')) - ], - - plugins: [ - json(), - babel(), - nodeResolve({ preferBuiltins: true }) ] - }, - - bundle: { - banner: BANNER, - format: 'cjs', - sourceMap: true, - useStrict: false - } + }), + bundle: compiler.createBundleConfig({ + banner + }) }; diff --git a/src/packages/cli/commands/dbcreate.js b/src/packages/cli/commands/dbcreate.js index 491e7146..e5ac40f8 100644 --- a/src/packages/cli/commands/dbcreate.js +++ b/src/packages/cli/commands/dbcreate.js @@ -11,17 +11,16 @@ import { createLoader } from '../../loader'; */ export async function dbcreate() { const load = createLoader(CWD); + let cfg = load('config'); + + cfg = Reflect.get(cfg.database, NODE_ENV); const { - database: { - [NODE_ENV]: { - driver, - database, - url, - ...config - } - } - } = load('config'); + url, + driver, + database, + ...config + } = cfg; if (driver === 'sqlite3') { await writeFile(`${CWD}/db/${database}_${NODE_ENV}.sqlite`, ''); diff --git a/src/packages/cli/commands/dbdrop.js b/src/packages/cli/commands/dbdrop.js index 6c5e55ab..ffb40331 100644 --- a/src/packages/cli/commands/dbdrop.js +++ b/src/packages/cli/commands/dbdrop.js @@ -11,17 +11,16 @@ import { createLoader } from '../../loader'; */ export async function dbdrop() { const load = createLoader(CWD); + let cfg = load('config'); + + cfg = Reflect.get(cfg.database, NODE_ENV); const { - database: { - [NODE_ENV]: { - driver, - database, - url, - ...config - } - } - } = load('config'); + url, + driver, + database, + ...config + } = cfg; if (driver === 'sqlite3') { await rmrf(`${CWD}/db/${database}_${NODE_ENV}.sqlite`); diff --git a/src/packages/cli/commands/dbseed.js b/src/packages/cli/commands/dbseed.js index 8bba0c96..c3f4dac4 100644 --- a/src/packages/cli/commands/dbseed.js +++ b/src/packages/cli/commands/dbseed.js @@ -6,21 +6,20 @@ import { createLoader } from '../../loader'; /** * @private */ -export async function dbseed() { +export function dbseed() { const load = createLoader(CWD); - const { database: config } = load('config'); const seed = load('seed'); const models = load('models'); - const store = await new Database({ + return new Database({ config, models, path: CWD, logger: new Logger({ enabled: false }) - }); - - await store.connection.transaction(seed); + }).then(store => ( + store.connection.transaction(seed) + )); } diff --git a/src/packages/compiler/index.js b/src/packages/compiler/index.js index e951b7bf..6bfc3796 100644 --- a/src/packages/compiler/index.js +++ b/src/packages/compiler/index.js @@ -8,14 +8,15 @@ import eslint from 'rollup-plugin-eslint'; import nodeResolve from 'rollup-plugin-node-resolve'; import { rollup } from 'rollup'; -import { rmrf, exists, readdir, readdirRec, isJSFile } from '../fs'; +import { IS_PRODUCTION } from '../../constants'; +import { rmrf, exists, readdir, readdirRec, readFile, isJSFile } from '../fs'; import template from '../template'; import uniq from '../../utils/uniq'; -import onwarn from './utils/handle-warning'; import normalizePath from './utils/normalize-path'; import createManifest from './utils/create-manifest'; import createBootScript from './utils/create-boot-script'; +import { createRollupConfig, createBundleConfig } from './utils/config'; /** * @private @@ -74,7 +75,11 @@ export async function compile(dir: string, env: string, { ]); }); - await Promise.all([ + const [babelrc] = await Promise.all([ + readFile( + path.join(dir, '.babelrc'), + 'utf8' + ), createManifest(dir, assets, { useStrict }), @@ -83,39 +88,39 @@ export async function compile(dir: string, env: string, { }) ]); - const bundle = await rollup({ - entry, - onwarn, - external, - plugins: [ - alias({ - resolve: ['.js'], - app: normalizePath(path.join(dir, 'app')), - LUX_LOCAL: normalizePath(local) - }), - - json(), - - nodeResolve({ - preferBuiltins: true - }), - - eslint({ - cwd: dir, - parser: 'babel-eslint', - useEslintrc: false, - include: [ - path.join(dir, 'app', '**'), - ], - exclude: [ - path.join(dir, 'package.json'), - path.join(__dirname, '..', 'src', '**') - ] - }), - - babel() - ] - }); + const bundle = await rollup( + createRollupConfig({ + entry, + external, + plugins: [ + alias({ + resolve: ['.js'], + app: normalizePath(path.join(dir, 'app')), + LUX_LOCAL: normalizePath(local) + }), + json(), + nodeResolve({ preferBuiltins: true }), + eslint({ + cwd: dir, + parser: 'babel-eslint', + useEslintrc: false, + include: [ + path.join(dir, 'app', '**'), + ], + exclude: [ + path.join(dir, 'package.json'), + path.join(__dirname, '..', 'src', '**') + ] + }), + babel({ + babelrc: false, + minified: IS_PRODUCTION, + comments: false, + ...JSON.parse(babelrc.toString()) + }) + ] + }) + ); await rmrf(entry); @@ -126,14 +131,15 @@ export async function compile(dir: string, env: string, { `; if (useStrict) { - banner = `'use strict';\n\n${banner}`; + banner = `\n'use strict';\n\n${banner}`; } - return bundle.write({ - banner, - dest: path.join(dir, 'dist', 'bundle.js'), - format: 'cjs', - sourceMap: true, - useStrict: false - }); + return bundle.write( + createBundleConfig({ + banner, + dest: path.join(dir, 'dist', 'bundle.js') + }) + ); } + +export { createRollupConfig, createBundleConfig } from './utils/config'; diff --git a/src/packages/compiler/utils/config.js b/src/packages/compiler/utils/config.js new file mode 100644 index 00000000..0c54cb57 --- /dev/null +++ b/src/packages/compiler/utils/config.js @@ -0,0 +1,32 @@ +// @flow +import merge from '../../../utils/merge'; + +const IGNORE_WARNING = /^treating '.+' as external dependency$/i; + +export function createRollupConfig(options: T): T { + return merge({ + acorn: { + sourceType: 'module', + ecmaVersion: 2017, + allowReserved: true, + preserveParens: true + }, + entry: '', + onwarn: warning => { + if (IGNORE_WARNING.test(warning)) { + return; + } + + process.stderr.write(`${warning}\n`); + }, + preferConst: true + }, options); +} + +export function createBundleConfig(options: T): T { + return merge({ + format: 'cjs', + sourceMap: true, + useStrict: false + }, options); +} From fc20eceee2d4455a7d06da45846b5dac26b1d75d Mon Sep 17 00:00:00 2001 From: Zachary Golba Date: Sun, 18 Dec 2016 18:04:48 -0500 Subject: [PATCH 08/12] test: update controller + serializer tests --- src/packages/controller/test/controller.test.js | 15 +-------------- src/packages/serializer/test/serializer.test.js | 16 ++++++++-------- test/test-app/config/environments/production.js | 3 +-- test/utils/mocks.js | 5 +++++ 4 files changed, 15 insertions(+), 24 deletions(-) diff --git a/src/packages/controller/test/controller.test.js b/src/packages/controller/test/controller.test.js index 62a5a81f..b0191cec 100644 --- a/src/packages/controller/test/controller.test.js +++ b/src/packages/controller/test/controller.test.js @@ -7,6 +7,7 @@ import Serializer from '../../serializer'; import { Model } from '../../database'; import { getTestApp } from '../../../../test/utils/get-test-app'; +import { createResponse } from '../../../../test/utils/mocks'; import type { Request, Response } from '../../server'; @@ -261,20 +262,6 @@ describe('module "controller"', () => { } }); - // $FlowIgnore - const createResponse = (): Response => ({ - headers: new Map(), - statusCode: 200, - - setHeader(key: string, value: string): void { - this.headers.set(key, value); - }, - - getHeader(key: string): string | void { - return this.headers.get(key); - } - }); - afterEach(async () => { await result.destroy(); }); diff --git a/src/packages/serializer/test/serializer.test.js b/src/packages/serializer/test/serializer.test.js index c2f4cc0d..d469cf4b 100644 --- a/src/packages/serializer/test/serializer.test.js +++ b/src/packages/serializer/test/serializer.test.js @@ -336,7 +336,7 @@ describe('module "serializer"', () => { it('works with a single instance of `Model`', async () => { const post = await createPost(); - const result = await subject.format({ + const result = subject.format({ data: post, domain: DOMAIN, include: [], @@ -376,7 +376,7 @@ describe('module "serializer"', () => { .map(post => post.getPrimaryKey()) .map(String); - const result = await subject.format({ + const result = subject.format({ data: posts, domain: DOMAIN, include: [], @@ -410,7 +410,7 @@ describe('module "serializer"', () => { subject = createSerializer('admin'); const post = await createPost(); - const result = await subject.format({ + const result = subject.format({ data: post, domain: DOMAIN, include: [], @@ -444,7 +444,7 @@ describe('module "serializer"', () => { includeComments: true }); - const result = await subject.format({ + const result = subject.format({ data: post, domain: DOMAIN, include: [], @@ -474,7 +474,7 @@ describe('module "serializer"', () => { const post = await createPost(); // $FlowIgnore const image = await post.image; - const result = await subject.format({ + const result = subject.format({ data: post, domain: DOMAIN, include: ['image'], @@ -506,7 +506,7 @@ describe('module "serializer"', () => { const post = await createPost(); // $FlowIgnore const user = await post.user; - const result = await subject.format({ + const result = subject.format({ data: post, domain: DOMAIN, include: ['user'], @@ -539,7 +539,7 @@ describe('module "serializer"', () => { const post = await createPost(); // $FlowIgnore const comments = await post.comments; - const result = await subject.format({ + const result = subject.format({ data: post, domain: DOMAIN, include: ['comments'], @@ -579,7 +579,7 @@ describe('module "serializer"', () => { const post = await createPost(); // $FlowIgnore const tags = await post.tags; - const result = await subject.format({ + const result = subject.format({ data: post, domain: DOMAIN, include: ['tags'], diff --git a/test/test-app/config/environments/production.js b/test/test-app/config/environments/production.js index 319760b4..597cce59 100644 --- a/test/test-app/config/environments/production.js +++ b/test/test-app/config/environments/production.js @@ -2,8 +2,7 @@ export default { logging: { level: 'INFO', format: 'json', - enabled: true, - + enabled: false, filter: { params: [] } diff --git a/test/utils/mocks.js b/test/utils/mocks.js index ee1117bf..0a41af45 100644 --- a/test/utils/mocks.js +++ b/test/utils/mocks.js @@ -23,6 +23,11 @@ export const createResponse = (): Response => ({ statusCode: 200, statusMessage: 'OK', + status(value: number): Response { + this.statusCode = value; + return this; + }, + getHeader(key: string) { return headersFor(this).get(key); }, From 561a8b65ada47f35e81ed5116652f28d5fca1a8d Mon Sep 17 00:00:00 2001 From: Zachary Golba Date: Fri, 20 Jan 2017 09:02:39 -0500 Subject: [PATCH 09/12] Merge branch 'master' into perf --- .eslintrc.json | 1 + .flowconfig | 19 +- .gitignore | 4 +- CHANGELOG.md | 58 ++++ appveyor.yml | 53 +++- bin/lux | 71 ++--- circle.yml | 74 ++++- codecov.yml | 6 + .../migrate/2016051621371582-create-users.js | 3 +- .../2016051621393305-create-actions.js | 3 +- .../2016051621405358-create-comments.js | 3 +- .../2016051621421145-create-reactions.js | 3 +- .../migrate/2016051621434009-create-posts.js | 3 +- .../2016051621435837-create-friendships.js | 3 +- .../2016051621460053-create-notifications.js | 3 +- .../migrate/2016061214092135-create-tags.js | 3 +- ...2016061214112168-create-categorizations.js | 3 +- examples/social-network/package.json | 2 +- .../migrate/2016051617272695-create-tasks.js | 6 +- .../migrate/2016051617273905-create-lists.js | 6 +- examples/todo/package.json | 2 +- flow-typed/npm/ansi-regex_vx.x.x.js | 4 +- flow-typed/npm/babel-core_vx.x.x.js | 4 +- flow-typed/npm/babel-eslint_vx.x.x.js | 4 +- .../npm/babel-plugin-istanbul_vx.x.x.js | 4 +- ...ransform-es2015-modules-commonjs_vx.x.x.js | 4 +- flow-typed/npm/babel-preset-lux_vx.x.x.js | 4 +- .../npm/eslint-config-airbnb-base_vx.x.x.js | 4 +- .../npm/eslint-plugin-flowtype_vx.x.x.js | 4 +- flow-typed/npm/eslint-plugin-import_vx.x.x.js | 4 +- flow-typed/npm/eslint_vx.x.x.js | 11 +- flow-typed/npm/faker_vx.x.x.js | 4 +- flow-typed/npm/fb-watchman_vx.x.x.js | 4 +- flow-typed/npm/flow-typed_vx.x.x.js | 4 +- flow-typed/npm/inflection_vx.x.x.js | 4 +- flow-typed/npm/knex_vx.x.x.js | 4 +- flow-typed/npm/mocha-junit-reporter_vx.x.x.js | 59 ++++ flow-typed/npm/node-fetch_vx.x.x.js | 4 +- flow-typed/npm/nyc_vx.x.x.js | 4 +- flow-typed/npm/ora_vx.x.x.js | 4 +- flow-typed/npm/remark-cli_vx.x.x.js | 4 +- flow-typed/npm/remark-lint_vx.x.x.js | 4 +- .../remark-preset-lint-recommended_vx.x.x.js | 4 +- flow-typed/npm/rollup-plugin-alias_vx.x.x.js | 4 +- flow-typed/npm/rollup-plugin-babel_vx.x.x.js | 4 +- flow-typed/npm/rollup-plugin-eslint_vx.x.x.js | 4 +- flow-typed/npm/rollup-plugin-json_vx.x.x.js | 4 +- flow-typed/npm/rollup-plugin-lux_vx.x.x.js | 298 ++++++++++++++++++ .../npm/rollup-plugin-multi-entry_vx.x.x.js | 32 ++ .../npm/rollup-plugin-node-resolve_vx.x.x.js | 4 +- flow-typed/npm/rollup_vx.x.x.js | 4 +- flow-typed/npm/shx_vx.x.x.js | 60 ++++ flow-typed/npm/sinon_vx.x.x.js | 4 +- flow-typed/npm/source-map-support_vx.x.x.js | 25 +- mocha.opts | 2 + package.json | 39 +-- rollup.config.js | 38 +++ scripts/appveyor/before-test.ps1 | 19 -- scripts/appveyor/install.ps1 | 8 - scripts/build/cli.js | 48 --- scripts/circle/before-script.sh | 28 -- scripts/circle/install.sh | 32 -- scripts/clean.js | 19 -- src/constants.js | 1 - src/interfaces.js | 16 + src/packages/application/index.js | 2 +- src/packages/application/interfaces.js | 2 +- .../application/utils/create-controller.js | 17 +- .../application/utils/create-serializer.js | 8 +- src/packages/cli/commands/index.js | 13 + .../cli/generator/utils/generate-type.js | 66 ++-- src/packages/cli/templates/model-migration.js | 7 +- src/packages/compiler/index.js | 145 ++++----- src/packages/compiler/test/compiler.test.js | 86 +++++ .../compiler/test/format-name.test.js | 49 +++ .../compiler/test/is-external.test.js | 58 ++++ .../compiler/utils/create-manifest.js | 8 +- src/packages/compiler/utils/format-name.js | 32 +- src/packages/compiler/utils/handle-warning.js | 18 +- src/packages/compiler/utils/is-external.js | 20 ++ src/packages/compiler/utils/normalize-path.js | 9 - src/packages/compiler/utils/strip-ext.js | 9 - src/packages/controller/index.js | 138 +++++--- .../controller/test/controller.test.js | 137 ++++---- src/packages/controller/utils/find-many.js | 10 +- .../database/model/utils/persistence.js | 6 +- src/packages/database/query/index.js | 24 +- src/packages/database/query/runner/index.js | 1 - .../query/runner/utils/get-find-param.js | 1 - src/packages/database/test/query.test.js | 12 +- src/packages/fs/index.js | 17 +- src/packages/fs/test/exists.test.js | 7 +- src/packages/fs/test/fs.test.js | 10 +- src/packages/fs/test/rmrf.test.js | 5 +- src/packages/fs/test/utils/create-tmp-dir.js | 25 +- .../fs/test/utils/create-tmp-files.js | 27 +- src/packages/fs/test/utils/get-tmp-file.js | 9 +- src/packages/fs/test/utils/remove-tmp-dir.js | 56 ++-- src/packages/fs/test/watcher.test.js | 5 +- src/packages/fs/utils/create-path-remover.js | 18 -- src/packages/fs/utils/exists.js | 8 +- src/packages/fs/utils/is-js-file.js | 4 +- src/packages/fs/utils/rmrf.js | 37 ++- .../builder/utils/create-parent-builder.js | 5 +- src/packages/loader/index.js | 7 +- src/packages/loader/resolver/index.js | 2 - .../loader/resolver/utils/closest-ancestor.js | 25 +- .../loader/resolver/utils/closest-child.js | 4 +- .../loader/resolver/utils/get-parent-key.js | 11 - .../loader/resolver/utils/strip-namespaces.js | 9 - .../context/utils/normalize-resource-args.js | 16 +- .../test/normalize-resource-args.test.js | 4 - src/packages/router/interfaces.js | 8 +- src/packages/router/namespace/index.js | 4 +- src/packages/router/namespace/interfaces.js | 4 +- .../router/route/action/enhancers/resource.js | 2 +- src/packages/router/route/action/index.js | 2 +- src/packages/router/route/index.js | 2 +- src/packages/router/route/interfaces.js | 2 +- src/packages/router/route/params/index.js | 2 +- .../router/route/params/interfaces.js | 2 +- .../route/params/parameter-group/index.js | 2 +- .../route/params/test/parameter.test.js | 35 ++ .../route/params/utils/get-data-params.js | 188 ++++++----- .../utils/get-default-collection-params.js | 64 ++-- .../params/utils/get-default-member-params.js | 54 ++-- .../route/params/utils/get-query-params.js | 100 +++--- .../route/params/utils/validate-type.js | 4 + src/packages/router/route/test/route.test.js | 12 +- src/packages/router/test/namespace.test.js | 19 +- src/packages/router/test/router.test.js | 2 +- src/packages/router/utils/create-replacer.js | 2 +- src/packages/serializer/index.js | 12 +- .../serializer/test/serializer.test.js | 7 +- src/packages/server/request/interfaces.js | 2 +- src/utils/github.js | 16 +- src/utils/test/github.test.js | 9 - src/utils/transform-keys.js | 3 +- .../migrate/2016051621371582-create-users.js | 3 +- .../2016051621393305-create-actions.js | 3 +- .../2016051621405358-create-comments.js | 3 +- .../2016051621421145-create-reactions.js | 3 +- .../migrate/2016051621434009-create-posts.js | 3 +- .../2016051621435837-create-friendships.js | 3 +- .../2016051621460053-create-notifications.js | 3 +- .../migrate/2016061214092135-create-tags.js | 3 +- ...2016061214112168-create-categorizations.js | 3 +- .../migrate/2016092015442134-create-images.js | 3 +- 148 files changed, 1926 insertions(+), 1024 deletions(-) create mode 100644 codecov.yml create mode 100644 flow-typed/npm/mocha-junit-reporter_vx.x.x.js create mode 100644 flow-typed/npm/rollup-plugin-lux_vx.x.x.js create mode 100644 flow-typed/npm/rollup-plugin-multi-entry_vx.x.x.js create mode 100644 flow-typed/npm/shx_vx.x.x.js create mode 100644 mocha.opts create mode 100644 rollup.config.js delete mode 100644 scripts/appveyor/before-test.ps1 delete mode 100644 scripts/appveyor/install.ps1 delete mode 100644 scripts/build/cli.js delete mode 100644 scripts/circle/before-script.sh delete mode 100644 scripts/circle/install.sh delete mode 100644 scripts/clean.js create mode 100644 src/packages/cli/commands/index.js create mode 100644 src/packages/compiler/test/compiler.test.js create mode 100644 src/packages/compiler/test/format-name.test.js create mode 100644 src/packages/compiler/test/is-external.test.js create mode 100644 src/packages/compiler/utils/is-external.js delete mode 100644 src/packages/compiler/utils/normalize-path.js delete mode 100644 src/packages/compiler/utils/strip-ext.js delete mode 100644 src/packages/fs/utils/create-path-remover.js delete mode 100644 src/packages/loader/resolver/utils/get-parent-key.js delete mode 100644 src/packages/loader/resolver/utils/strip-namespaces.js diff --git a/.eslintrc.json b/.eslintrc.json index ae66f1cd..a6a9a6c6 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -51,6 +51,7 @@ ], "newlines-between": "always" }], + "import/extensions": 0, "import/no-dynamic-require": 0, "import/prefer-default-export": 0, "flowtype/semi": 2, diff --git a/.flowconfig b/.flowconfig index 2ce17751..9aaf9c53 100644 --- a/.flowconfig +++ b/.flowconfig @@ -1,7 +1,3 @@ -[libs] -flow-typed/ -decl/ - [ignore] .*/lib/.* .*/dist/.* @@ -9,10 +5,23 @@ decl/ .*/examples/.* .*/node_modules/bcryptjs/* +[include] +./src/ +./node_modules/ + +[libs] +node_modules/rollup-plugin-lux/decl/ +node_modules/rollup-plugin-lux/flow-typed/ +decl/ +flow-typed/ + [options] +emoji=true +strip_root=true suppress_comment=\\(.\\|\n\\)*\\$FlowIgnore module.name_mapper='LUX_LOCAL' -> '' +module.name_mapper='rollup-plugin-lux' -> '/node_modules/rollup-plugin-lux/src/index.js' unsafe.enable_getters_and_setters=true @@ -20,4 +29,4 @@ esproposal.class_static_fields=enable esproposal.class_instance_fields=enable [version] -0.37.4 +^0.38.0 diff --git a/.gitignore b/.gitignore index 95efd1bd..4e7cff22 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,10 @@ # See http://help.github.com/ignore-files/ for more about ignoring files. # compiled output -*.lcov +.nyc_output/ coverage/ dist/ -.nyc_output/ +*test-results.xml # dependencies node_modules/ diff --git a/CHANGELOG.md b/CHANGELOG.md index 13ed6db6..0785c87a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,63 @@ # Lux Changelog +### v1.1.4 (Jan 19, 2017) + +##### Commits + +* [[`d1c5e8d00e`](https://github.com/postlight/lux/commit/d1c5e8d00e)] - **fix**: cli and compilation errors (#649) (Zachary Golba) +* [[`06d9fc7f65`](https://github.com/postlight/lux/commit/06d9fc7f65)] - **fix**: make sure result is an instance of query in resource enhancer (#648) (Nick Schot) +* [[`4f4bd16c12`](https://github.com/postlight/lux/commit/4f4bd16c12)] - **deps**: update nyc to version 10.1.2 (#646) (Greenkeeper) +* [[`d6c9ab49fc`](https://github.com/postlight/lux/commit/d6c9ab49fc)] - **release**: v1.1.3 🔧 (#644) (Zachary Golba) + +### v1.1.3 (Jan 17, 2017) + +##### Upgrading + +Due to a change in the way the cli modules are bundled, you may need to uninstall +and reinstall lux globally before upgrading your application. + +```bash +npm uninstall -g lux-framework +npm cache clean +npm install -g lux-framework@latest +``` + +##### Commits + +* [[`89dff6162c`](https://github.com/postlight/lux/commit/89dff6162c)] - **fix**: we should check for lux under the dependency key in package.json not root (#643) (Zachary Golba) +* [[`b7c66cb41f`](https://github.com/postlight/lux/commit/b7c66cb41f)] - **fix**: make sure dates are correctly validated & parsed (#639) (Nick Schot) +* [[`00273c8279`](https://github.com/postlight/lux/commit/00273c8279)] - **deps**: update ora to version 1.0.0 (#641) (Greenkeeper) +* [[`fe2b860abd`](https://github.com/postlight/lux/commit/fe2b860abd)] - **deps**: update source-map-support to version 0.4.10 (#642) (Greenkeeper) +* [[`f41fc048b9`](https://github.com/postlight/lux/commit/f41fc048b9)] - **deps**: integrate rollup-plugin-lux (#632) (Zachary Golba) +* [[`e4ed011b0a`](https://github.com/postlight/lux/commit/e4ed011b0a)] - **deps**: update eslint-plugin-flowtype to version 2.30.0 (#636) (Greenkeeper) +* [[`df95938f3f`](https://github.com/postlight/lux/commit/df95938f3f)] - **deps**: update source-map-support to version 0.4.9 (#634) (Greenkeeper) +* [[`ebbcf577bd`](https://github.com/postlight/lux/commit/ebbcf577bd)] - **fix**: migration generator indexes (#625) (Nick Schot) +* [[`93aa5c47bd`](https://github.com/postlight/lux/commit/93aa5c47bd)] - **fix**: make pages consistent when using sort on non-unique attributes (#633) (Nick Schot) +* [[`d1589ab8f3`](https://github.com/postlight/lux/commit/d1589ab8f3)] - **test**: allow appveyor builds to fail with pg driver (#635) (Zachary Golba) +* [[`682d6fef56`](https://github.com/postlight/lux/commit/682d6fef56)] - **chore**: improve ci config and process (#627) (Zachary Golba) +* [[`d0795c36fa`](https://github.com/postlight/lux/commit/d0795c36fa)] - **deps**: update rollup to version 0.41.4 (#630) (Greenkeeper) +* [[`20d6f0dbe7`](https://github.com/postlight/lux/commit/20d6f0dbe7)] - **deps**: update ansi-regex to version 2.1.1 (#629) (Greenkeeper) +* [[`016938c401`](https://github.com/postlight/lux/commit/016938c401)] - **deps**: update rollup to version 0.41.3 (#628) (Greenkeeper) +* [[`e7d069ffe7`](https://github.com/postlight/lux/commit/e7d069ffe7)] - **deps**: update rollup to version 0.41.1 (#616) (Greenkeeper) +* [[`1626cd882c`](https://github.com/postlight/lux/commit/1626cd882c)] - **deps**: update ora to version 0.4.1 (#623) (Greenkeeper) +* [[`60b33a6a33`](https://github.com/postlight/lux/commit/60b33a6a33)] - **deps**: update eslint to version 3.13.1 (#622) (Greenkeeper) +* [[`b050cfaf03`](https://github.com/postlight/lux/commit/b050cfaf03)] - **deps**: update eslint-config-airbnb-base to version 11.0.1 (#619) (Greenkeeper) +* [[`5a879d467e`](https://github.com/postlight/lux/commit/5a879d467e)] - **deps**: update eslint to version 3.13.0 (#615) (Greenkeeper) +* [[`fbbf198e2b`](https://github.com/postlight/lux/commit/fbbf198e2b)] - **deps**: update babel-plugin-istanbul to version 3.1.2 (#611) (Greenkeeper) +* [[`4c5e326afa`](https://github.com/postlight/lux/commit/4c5e326afa)] - **release**: v1.1.2 🔧 (#609) (Zachary Golba) + +### v1.1.2 (Jan 3, 2017) + +:tada: Happy New Year! + +##### Commits + +* [[`f828e8effa`](https://github.com/postlight/lux/commit/f828e8effa)] - **fix**: database is undefined when using the postgresql driver (#605) (Zachary Golba) +* [[`46ed68b35a`](https://github.com/postlight/lux/commit/46ed68b35a)] - **deps**: update third party type definitions (#604) (Zachary Golba) +* [[`914a4b831d`](https://github.com/postlight/lux/commit/914a4b831d)] - **deps**: update sinon to version 1.17.7 (#602) (Greenkeeper) +* [[`d3ffda9156`](https://github.com/postlight/lux/commit/d3ffda9156)] - **deps**: update eslint-plugin-flowtype to version 2.29.2 (#595) (Greenkeeper) +* [[`f5b002e3dd`](https://github.com/postlight/lux/commit/f5b002e3dd)] - **release**: v1.1.1 🔧 (#594) (Zachary Golba) + ### v1.1.1 (Dec 28, 2016) ##### Commits diff --git a/appveyor.yml b/appveyor.yml index 5e42e51a..a5e60585 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,19 +1,60 @@ -image: Visual Studio 2015 +build: off +version: '{build}' +branches: + only: + - master platform: x64 +services: + - mysql + - postgresql +matrix: + allow_failures: + - DATABASE_DRIVER: pg environment: nodejs_version: 6 NODE_ENV: test + CREATE_DATABASE: CREATE DATABASE lux_test; + DROP_DATABASE: DROP DATABASE IF EXISTS lux_test; DATABASE_PASSWORD: Password12! matrix: + - DATABASE_DRIVER: pg + DATABASE_USERNAME: postgres - DATABASE_DRIVER: mysql2 DATABASE_USERNAME: root - DATABASE_DRIVER: sqlite3 -services: - - mysql install: - - ps: C:\projects\lux\scripts\appveyor\install.ps1 + - ps: Install-Product node $env:nodejs_version $env:platform + - npm install + - npm link + - ps: Set-Location C:\projects\lux\test\test-app + - npm install + - ps: Set-Location C:\projects\lux before_test: - - ps: C:\projects\lux\scripts\appveyor\before-test.ps1 + - | + SET PGUSER=postgres + SET PGPASSWORD=%DATABASE_PASSWORD% + PATH=C:\Program Files\PostgreSQL\9.5\bin\;%PATH% + dropdb --if-exists lux_test + createdb lux_test + - ps: | + $env:MYSQL_PWD="$env:DATABASE_PASSWORD" + $mysql="C:\Program Files\MySql\MySQL Server 5.7\bin\mysql" + + Invoke-Expression "& '$mysql' -e '$env:DROP_DATABASE' -u root" + Invoke-Expression "& '$mysql' -e '$env:CREATE_DATABASE' -u root" + + Remove-Item C:\projects\lux\test\test-app\db\* -Force -Include *.sqlite + Write-Host $null >> C:\projects\lux\test\test-app\db\lux_test_test.sqlite + - npm run clean + - npm run build test_script: + - npm run flow + - npm run lint - npm test -build: off +notifications: +- provider: Webhook + url: https://webhooks.gitter.im/e/070ebbf83dc0f595c4fb + method: POST + on_build_success: true + on_build_failure: true + on_build_status_changed: false diff --git a/bin/lux b/bin/lux index 7b72a8ea..1b9ac969 100755 --- a/bin/lux +++ b/bin/lux @@ -10,49 +10,26 @@ const { red, green } = require('chalk'); const { version: VERSION } = require('../package.json'); -const PWD = process.cwd(); -const PROJECT_REQUIRED = [ - 'test', - 'build', - 'serve', - 'console', - 'generate', - 'destroy', - 'db:seed', - 'db:reset', - 'db:migrate' -]; - -function writeOut(...lines) { - lines.forEach(line => { - process.stdout.write(`${line}\n`); - }); -} - -function writeErr(...lines) { - lines.forEach(line => { - process.stderr.write(`${line}\n`); - }); -} - function inLuxProject() { - if (PWD.indexOf(path.join('lux', 'test', 'test-app')) >= 0) { + const cwd = process.cwd(); + + if (cwd.indexOf(path.join('lux', 'test', 'test-app')) >= 0) { return true; } try { - const { dependencies } = require(path.join(PWD, 'package.json')); - - return Reflect.has(dependencies, 'lux-framework'); + const { dependencies } = require(path.join(cwd, 'package.json')); + return Boolean(dependencies['lux-framework']); } catch (err) { return false; } } function commandNotFound(cmd) { - writeOut( - `${EOL} ${red(cmd)} is not a valid command${EOL}\n`, - ` Use ${green('lux --help')} for a full list of commands${EOL}` + // eslint-disable-next-line no-console + console.log( + `${EOL} ${red(cmd)} is not a valid command.${EOL.repeat(2)}`, + ` Use ${green('lux --help')} for a full list of commands.${EOL}` ); } @@ -65,27 +42,18 @@ function setEnvVar(key, val, def) { } function exec(cmd, ...args) { - let handler; - - if (PROJECT_REQUIRED.indexOf(cmd) >= 0 && !inLuxProject()) { - return Promise.reject( - 'Error: Directory does not contain a valid Lux application.' - ); - } + const handler = require('../dist')[cmd]; + const needsProject = new RegExp( + '^(?:db:.+|test|build|serve|console|generate|destroy)$' + ); - try { - handler = require(path.join( - PWD, - 'node_modules', - 'lux-framework', - 'dist', - cmd - ))[cmd]; - } catch (err) { - handler = require(path.join(__dirname, '..', 'dist', cmd))[cmd]; + if (needsProject.test(cmd) && !inLuxProject()) { + return Promise.reject(new Error( + 'Directory does not contain a valid Lux application.' + )); } - return Reflect.apply(handler, null, args); + return handler(...args); } function exit(code) { @@ -93,7 +61,8 @@ function exit(code) { } function rescue(err) { - writeErr(err.stack); + // eslint-disable-next-line + console.error(err.stack); exit(1); } diff --git a/circle.yml b/circle.yml index 9bb4c046..0e7521b0 100644 --- a/circle.yml +++ b/circle.yml @@ -1,17 +1,77 @@ -test: - override: - - bash -e scripts/circle/before-script.sh: - parallel: true - - npm test && npm run test:codecov: - parallel: true machine: node: version: 6 environment: NODE_ENV: test + DROP_DATABASE: DROP DATABASE IF EXISTS lux_test; + CREATE_DATABASE: CREATE DATABASE lux_test; DATABASE_USERNAME: ubuntu +database: + override: + - psql -c "$DROP_DATABASE" -U postgres + - psql -c "$CREATE_DATABASE" -U postgres + - mysql -e "$DROP_DATABASE" + - mysql -e "$CREATE_DATABASE" dependencies: + pre: + - | + cd ../ + + if [ -d watchman ]; then + cd watchman + sudo make install + else + git clone https://github.com/facebook/watchman.git + cd watchman + git checkout v4.7.0 + + ./autogen.sh + ./configure + make + sudo make install + fi + + cd ../lux override: - - bash -e scripts/circle/install.sh + - npm install + - npm link + post: + - | + cd test/test-app + npm install + cd ../../ cache_directories: - /home/ubuntu/watchman +compile: + pre: + - npm run clean + override: + - npm run build +test: + pre: + - case $CIRCLE_NODE_INDEX in 0) export DATABASE_DRIVER="pg" ;; 1) export DATABASE_DRIVER="mysql2" ;; 2) export DATABASE_DRIVER="sqlite3" ;; esac: + parallel: true + override: + - npm run flow: + parallel: true + - npm run lint: + parallel: true + - npm test -- -R mocha-junit-reporter: + parallel: true + environment: + MOCHA_FILE: $CIRCLE_TEST_REPORTS/junit/test-results.xml + post: + - npm run codecov: + parallel: true +notify: + webhooks: + - url: https://webhooks.gitter.im/e/6d49c9b19c888dba70b8 +deployment: + release: + tag: /^(?:v\d.\d.\d)$/ + commands: + - npm run clean + - npm run build + - rm -rf ~/.npmrc && touch ~/.npmrc + - echo //registry.npmjs.org/:_authToken=$NPM_AUTH_TOKEN >> ~/.npmrc + - npm publish diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 00000000..58b6767c --- /dev/null +++ b/codecov.yml @@ -0,0 +1,6 @@ +coverage: + notify: + gitter: + default: + url: "https://webhooks.gitter.im/e/b50a21b0b2a0271501db" + threshold: 1% diff --git a/examples/social-network/db/migrate/2016051621371582-create-users.js b/examples/social-network/db/migrate/2016051621371582-create-users.js index eca7984a..3b95abfe 100644 --- a/examples/social-network/db/migrate/2016051621371582-create-users.js +++ b/examples/social-network/db/migrate/2016051621371582-create-users.js @@ -15,7 +15,8 @@ export function up(schema) { .notNullable(); table.timestamps(); - table.index(['created_at', 'updated_at']); + table.index('created_at'); + table.index('updated_at'); }); } diff --git a/examples/social-network/db/migrate/2016051621393305-create-actions.js b/examples/social-network/db/migrate/2016051621393305-create-actions.js index 313a6f81..973a8998 100644 --- a/examples/social-network/db/migrate/2016051621393305-create-actions.js +++ b/examples/social-network/db/migrate/2016051621393305-create-actions.js @@ -11,7 +11,8 @@ export function up(schema) { .notNullable(); table.timestamps(); - table.index(['created_at', 'updated_at']); + table.index('created_at'); + table.index('updated_at'); }); } diff --git a/examples/social-network/db/migrate/2016051621405358-create-comments.js b/examples/social-network/db/migrate/2016051621405358-create-comments.js index 0387c47e..4b6e49d4 100644 --- a/examples/social-network/db/migrate/2016051621405358-create-comments.js +++ b/examples/social-network/db/migrate/2016051621405358-create-comments.js @@ -17,7 +17,8 @@ export function up(schema) { .index(); table.timestamps(); - table.index(['created_at', 'updated_at']); + table.index('created_at'); + table.index('updated_at'); }); } diff --git a/examples/social-network/db/migrate/2016051621421145-create-reactions.js b/examples/social-network/db/migrate/2016051621421145-create-reactions.js index c5cb377b..6a6be203 100644 --- a/examples/social-network/db/migrate/2016051621421145-create-reactions.js +++ b/examples/social-network/db/migrate/2016051621421145-create-reactions.js @@ -20,7 +20,8 @@ export function up(schema) { .index(); table.timestamps(); - table.index(['created_at', 'updated_at']); + table.index('created_at'); + table.index('updated_at'); }); } diff --git a/examples/social-network/db/migrate/2016051621434009-create-posts.js b/examples/social-network/db/migrate/2016051621434009-create-posts.js index 87733725..f387760d 100644 --- a/examples/social-network/db/migrate/2016051621434009-create-posts.js +++ b/examples/social-network/db/migrate/2016051621434009-create-posts.js @@ -18,7 +18,8 @@ export function up(schema) { .index(); table.timestamps(); - table.index(['created_at', 'updated_at']); + table.index('created_at'); + table.index('updated_at'); }); } diff --git a/examples/social-network/db/migrate/2016051621435837-create-friendships.js b/examples/social-network/db/migrate/2016051621435837-create-friendships.js index 3e7adfda..41b20fdb 100644 --- a/examples/social-network/db/migrate/2016051621435837-create-friendships.js +++ b/examples/social-network/db/migrate/2016051621435837-create-friendships.js @@ -9,7 +9,8 @@ export function up(schema) { .index(); table.timestamps(); - table.index(['created_at', 'updated_at']); + table.index('created_at'); + table.index('updated_at'); }); } diff --git a/examples/social-network/db/migrate/2016051621460053-create-notifications.js b/examples/social-network/db/migrate/2016051621460053-create-notifications.js index ac82214e..9fb05c14 100644 --- a/examples/social-network/db/migrate/2016051621460053-create-notifications.js +++ b/examples/social-network/db/migrate/2016051621460053-create-notifications.js @@ -14,7 +14,8 @@ export function up(schema) { .index(); table.timestamps(); - table.index(['created_at', 'updated_at']); + table.index('created_at'); + table.index('updated_at'); }); } diff --git a/examples/social-network/db/migrate/2016061214092135-create-tags.js b/examples/social-network/db/migrate/2016061214092135-create-tags.js index 3b019168..e05c7a8b 100644 --- a/examples/social-network/db/migrate/2016061214092135-create-tags.js +++ b/examples/social-network/db/migrate/2016061214092135-create-tags.js @@ -7,7 +7,8 @@ export function up(schema) { .notNullable(); table.timestamps(); - table.index(['created_at', 'updated_at']); + table.index('created_at'); + table.index('updated_at'); }); } diff --git a/examples/social-network/db/migrate/2016061214112168-create-categorizations.js b/examples/social-network/db/migrate/2016061214112168-create-categorizations.js index 0035b451..71de18ba 100644 --- a/examples/social-network/db/migrate/2016061214112168-create-categorizations.js +++ b/examples/social-network/db/migrate/2016061214112168-create-categorizations.js @@ -9,7 +9,8 @@ export function up(schema) { .index(); table.timestamps(); - table.index(['created_at', 'updated_at']); + table.index('created_at'); + table.index('updated_at'); }); } diff --git a/examples/social-network/package.json b/examples/social-network/package.json index 1abc4977..3100a520 100644 --- a/examples/social-network/package.json +++ b/examples/social-network/package.json @@ -14,7 +14,7 @@ "babel-preset-lux": "1.3.0", "bcryptjs": "2.3.0", "knex": "0.12.6", - "lux-framework": "1.1.1", + "lux-framework": "1.1.4", "sqlite3": "3.1.4" }, "devDependencies": { diff --git a/examples/todo/db/migrate/2016051617272695-create-tasks.js b/examples/todo/db/migrate/2016051617272695-create-tasks.js index 18b1ddf6..86d87a1f 100644 --- a/examples/todo/db/migrate/2016051617272695-create-tasks.js +++ b/examples/todo/db/migrate/2016051617272695-create-tasks.js @@ -17,10 +17,8 @@ export function up(schema) { table.integer('list_id').index(); table.timestamps(); - table.index([ - 'created_at', - 'updated_at' - ]); + table.index('created_at'); + table.index('updated_at'); }); } diff --git a/examples/todo/db/migrate/2016051617273905-create-lists.js b/examples/todo/db/migrate/2016051617273905-create-lists.js index a8079722..6e5873c5 100644 --- a/examples/todo/db/migrate/2016051617273905-create-lists.js +++ b/examples/todo/db/migrate/2016051617273905-create-lists.js @@ -8,10 +8,8 @@ export function up(schema) { .notNullable(); table.timestamps(); - table.index([ - 'created_at', - 'updated_at' - ]); + table.index('created_at'); + table.index('updated_at'); }); } diff --git a/examples/todo/package.json b/examples/todo/package.json index 10cfca55..dd26abcd 100644 --- a/examples/todo/package.json +++ b/examples/todo/package.json @@ -13,7 +13,7 @@ "babel-core": "6.14.0", "babel-preset-lux": "1.3.0", "knex": "0.12.6", - "lux-framework": "1.1.1", + "lux-framework": "1.1.4", "sqlite3": "3.1.4" }, "devDependencies": { diff --git a/flow-typed/npm/ansi-regex_vx.x.x.js b/flow-typed/npm/ansi-regex_vx.x.x.js index f8eeb73c..4d38f036 100644 --- a/flow-typed/npm/ansi-regex_vx.x.x.js +++ b/flow-typed/npm/ansi-regex_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: 51d72f02b42fb5856f5027bc57785f68 -// flow-typed version: <>/ansi-regex_v2.0.0/flow_v0.37.4 +// flow-typed signature: 452607e6dbda94270a8b97dbdfb66685 +// flow-typed version: <>/ansi-regex_v2.1.1/flow_v0.38.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/babel-core_vx.x.x.js b/flow-typed/npm/babel-core_vx.x.x.js index 066c0a4e..04a4dc21 100644 --- a/flow-typed/npm/babel-core_vx.x.x.js +++ b/flow-typed/npm/babel-core_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: 5408c66b269ff83d3d212b0de75abf73 -// flow-typed version: <>/babel-core_v6.21.0/flow_v0.37.4 +// flow-typed signature: d692dba6c579c5b69dea0df77cde52c0 +// flow-typed version: <>/babel-core_v6.21.0/flow_v0.38.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/babel-eslint_vx.x.x.js b/flow-typed/npm/babel-eslint_vx.x.x.js index 374fd87c..1eaace51 100644 --- a/flow-typed/npm/babel-eslint_vx.x.x.js +++ b/flow-typed/npm/babel-eslint_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: 19bae530d943112a4c3a8c24dc4ee26b -// flow-typed version: <>/babel-eslint_v7.1.1/flow_v0.37.4 +// flow-typed signature: 55742293112dd22f8984209529124482 +// flow-typed version: <>/babel-eslint_v7.1.1/flow_v0.38.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/babel-plugin-istanbul_vx.x.x.js b/flow-typed/npm/babel-plugin-istanbul_vx.x.x.js index ffaaecaa..8d0dcd64 100644 --- a/flow-typed/npm/babel-plugin-istanbul_vx.x.x.js +++ b/flow-typed/npm/babel-plugin-istanbul_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: 5cc218c4d830cb34beb520dcd4da50ef -// flow-typed version: <>/babel-plugin-istanbul_v3.0.0/flow_v0.37.4 +// flow-typed signature: d80a7b586bbec974855825e2b0f4b8f6 +// flow-typed version: <>/babel-plugin-istanbul_v3.1.2/flow_v0.38.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/babel-plugin-transform-es2015-modules-commonjs_vx.x.x.js b/flow-typed/npm/babel-plugin-transform-es2015-modules-commonjs_vx.x.x.js index bb77f373..26bcdd9b 100644 --- a/flow-typed/npm/babel-plugin-transform-es2015-modules-commonjs_vx.x.x.js +++ b/flow-typed/npm/babel-plugin-transform-es2015-modules-commonjs_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: 7ca39869ede6768f3a3f4f63cc2d0f19 -// flow-typed version: <>/babel-plugin-transform-es2015-modules-commonjs_v6.18.0/flow_v0.37.4 +// flow-typed signature: 1d90db81b4452cd3f99ec09d578fc314 +// flow-typed version: <>/babel-plugin-transform-es2015-modules-commonjs_v6.18.0/flow_v0.38.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/babel-preset-lux_vx.x.x.js b/flow-typed/npm/babel-preset-lux_vx.x.x.js index 5127e5e2..80cf6b4b 100644 --- a/flow-typed/npm/babel-preset-lux_vx.x.x.js +++ b/flow-typed/npm/babel-preset-lux_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: 155bc92269e2113855adc0aa5fb615e3 -// flow-typed version: <>/babel-preset-lux_v2.0.1/flow_v0.37.4 +// flow-typed signature: afb738bb6c1e550f0a7e656014d1abbe +// flow-typed version: <>/babel-preset-lux_v2.0.1/flow_v0.38.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/eslint-config-airbnb-base_vx.x.x.js b/flow-typed/npm/eslint-config-airbnb-base_vx.x.x.js index e10d6d9c..5951777e 100644 --- a/flow-typed/npm/eslint-config-airbnb-base_vx.x.x.js +++ b/flow-typed/npm/eslint-config-airbnb-base_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: 59ad9a8f24a0677df47d8e8a86aa5b1e -// flow-typed version: <>/eslint-config-airbnb-base_v11.0.0/flow_v0.37.4 +// flow-typed signature: f37a26d405e7a350c52b574de4074168 +// flow-typed version: <>/eslint-config-airbnb-base_v11.0.1/flow_v0.38.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/eslint-plugin-flowtype_vx.x.x.js b/flow-typed/npm/eslint-plugin-flowtype_vx.x.x.js index cdeae4c8..51ca05ee 100644 --- a/flow-typed/npm/eslint-plugin-flowtype_vx.x.x.js +++ b/flow-typed/npm/eslint-plugin-flowtype_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: 1c3788470c355b0d3f690514a6114826 -// flow-typed version: <>/eslint-plugin-flowtype_v2.29.2/flow_v0.37.4 +// flow-typed signature: d06b43c0f6e48b0ab0c1cbf0379b6fcf +// flow-typed version: <>/eslint-plugin-flowtype_v2.30.0/flow_v0.38.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/eslint-plugin-import_vx.x.x.js b/flow-typed/npm/eslint-plugin-import_vx.x.x.js index b022b056..9921e819 100644 --- a/flow-typed/npm/eslint-plugin-import_vx.x.x.js +++ b/flow-typed/npm/eslint-plugin-import_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: 974a529062530f35fa4a84bc2c6bd7f3 -// flow-typed version: <>/eslint-plugin-import_v2.2.0/flow_v0.37.4 +// flow-typed signature: 36e62fb84e0abea792aa052ba3eb8b0c +// flow-typed version: <>/eslint-plugin-import_v2.2.0/flow_v0.38.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/eslint_vx.x.x.js b/flow-typed/npm/eslint_vx.x.x.js index 9b330036..b8e12078 100644 --- a/flow-typed/npm/eslint_vx.x.x.js +++ b/flow-typed/npm/eslint_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: 502a8fda1ab0385e20354b5cbe96c473 -// flow-typed version: <>/eslint_v3.12.1/flow_v0.37.4 +// flow-typed signature: 39c719d3a8ef136e1ba183a7d663f2b4 +// flow-typed version: <>/eslint_v3.13.1/flow_v0.38.0 /** * This is an autogenerated libdef stub for: @@ -1018,6 +1018,10 @@ declare module 'eslint/lib/rules/prefer-const' { declare module.exports: any; } +declare module 'eslint/lib/rules/prefer-destructuring' { + declare module.exports: any; +} + declare module 'eslint/lib/rules/prefer-numeric-literals' { declare module.exports: any; } @@ -1986,6 +1990,9 @@ declare module 'eslint/lib/rules/prefer-arrow-callback.js' { declare module 'eslint/lib/rules/prefer-const.js' { declare module.exports: $Exports<'eslint/lib/rules/prefer-const'>; } +declare module 'eslint/lib/rules/prefer-destructuring.js' { + declare module.exports: $Exports<'eslint/lib/rules/prefer-destructuring'>; +} declare module 'eslint/lib/rules/prefer-numeric-literals.js' { declare module.exports: $Exports<'eslint/lib/rules/prefer-numeric-literals'>; } diff --git a/flow-typed/npm/faker_vx.x.x.js b/flow-typed/npm/faker_vx.x.x.js index 1b8a2dd9..c9438baa 100644 --- a/flow-typed/npm/faker_vx.x.x.js +++ b/flow-typed/npm/faker_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: d26f8ca54ec05cae0455702549167b55 -// flow-typed version: <>/faker_v3.1.0/flow_v0.37.4 +// flow-typed signature: 6f749627aaa80fc489129ba2f69c0e74 +// flow-typed version: <>/faker_v3.1.0/flow_v0.38.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/fb-watchman_vx.x.x.js b/flow-typed/npm/fb-watchman_vx.x.x.js index e0420185..3abed929 100644 --- a/flow-typed/npm/fb-watchman_vx.x.x.js +++ b/flow-typed/npm/fb-watchman_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: 15d3999c696529679ce12e1e7dde5f82 -// flow-typed version: <>/fb-watchman_v1.9.0/flow_v0.37.4 +// flow-typed signature: 1872b556b019ed1e1c4cb5b36d663dfc +// flow-typed version: <>/fb-watchman_v1.9.0/flow_v0.38.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/flow-typed_vx.x.x.js b/flow-typed/npm/flow-typed_vx.x.x.js index 96724875..79a0928e 100644 --- a/flow-typed/npm/flow-typed_vx.x.x.js +++ b/flow-typed/npm/flow-typed_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: ee823e933d64c9273ee186e4f07741b4 -// flow-typed version: <>/flow-typed_v2.0.0/flow_v0.37.4 +// flow-typed signature: 3dba6be297539bc738d6117475bc2cb3 +// flow-typed version: <>/flow-typed_v2.0.0/flow_v0.38.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/inflection_vx.x.x.js b/flow-typed/npm/inflection_vx.x.x.js index 38faf9b8..6f217fd6 100644 --- a/flow-typed/npm/inflection_vx.x.x.js +++ b/flow-typed/npm/inflection_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: b857fc16e0a4f8e2c97563df5b6e3dbd -// flow-typed version: <>/inflection_v1.10.0/flow_v0.37.4 +// flow-typed signature: 4b00291a429acb5009c0e635c612b7da +// flow-typed version: <>/inflection_v1.10.0/flow_v0.38.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/knex_vx.x.x.js b/flow-typed/npm/knex_vx.x.x.js index 60ac3dfb..90060d90 100644 --- a/flow-typed/npm/knex_vx.x.x.js +++ b/flow-typed/npm/knex_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: bcacf85678bfb29b01a6eb3b465ae8a2 -// flow-typed version: <>/knex_v0.12.6/flow_v0.37.4 +// flow-typed signature: 2a3732dd3a8e69fb1f9fa91a5dc8adda +// flow-typed version: <>/knex_v0.12.6/flow_v0.38.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/mocha-junit-reporter_vx.x.x.js b/flow-typed/npm/mocha-junit-reporter_vx.x.x.js new file mode 100644 index 00000000..0e0e76a2 --- /dev/null +++ b/flow-typed/npm/mocha-junit-reporter_vx.x.x.js @@ -0,0 +1,59 @@ +// flow-typed signature: 23db411854e376cc41c30904b2a7dcb7 +// flow-typed version: <>/mocha-junit-reporter_v1.13.0/flow_v0.38.0 + +/** + * This is an autogenerated libdef stub for: + * + * 'mocha-junit-reporter' + * + * Fill this stub out by replacing all the `any` types. + * + * Once filled out, we encourage you to share your work with the + * community by sending a pull request to: + * https://github.com/flowtype/flow-typed + */ + +declare module 'mocha-junit-reporter' { + declare module.exports: any; +} + +/** + * We include stubs for each file inside this npm package in case you need to + * require those files directly. Feel free to delete any files that aren't + * needed. + */ +declare module 'mocha-junit-reporter/test/helpers/mock-runner' { + declare module.exports: any; +} + +declare module 'mocha-junit-reporter/test/helpers/mock-test' { + declare module.exports: any; +} + +declare module 'mocha-junit-reporter/test/mocha-junit-reporter-spec' { + declare module.exports: any; +} + +declare module 'mocha-junit-reporter/test/mock-results' { + declare module.exports: any; +} + +// Filename aliases +declare module 'mocha-junit-reporter/index' { + declare module.exports: $Exports<'mocha-junit-reporter'>; +} +declare module 'mocha-junit-reporter/index.js' { + declare module.exports: $Exports<'mocha-junit-reporter'>; +} +declare module 'mocha-junit-reporter/test/helpers/mock-runner.js' { + declare module.exports: $Exports<'mocha-junit-reporter/test/helpers/mock-runner'>; +} +declare module 'mocha-junit-reporter/test/helpers/mock-test.js' { + declare module.exports: $Exports<'mocha-junit-reporter/test/helpers/mock-test'>; +} +declare module 'mocha-junit-reporter/test/mocha-junit-reporter-spec.js' { + declare module.exports: $Exports<'mocha-junit-reporter/test/mocha-junit-reporter-spec'>; +} +declare module 'mocha-junit-reporter/test/mock-results.js' { + declare module.exports: $Exports<'mocha-junit-reporter/test/mock-results'>; +} diff --git a/flow-typed/npm/node-fetch_vx.x.x.js b/flow-typed/npm/node-fetch_vx.x.x.js index 051fe4a6..cbb5c3dc 100644 --- a/flow-typed/npm/node-fetch_vx.x.x.js +++ b/flow-typed/npm/node-fetch_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: 3093d0b369a1f8053b937e67782ef7fa -// flow-typed version: <>/node-fetch_v1.6.3/flow_v0.37.4 +// flow-typed signature: 6ef7b2d8d69a8c94b0bca2c9c92071b4 +// flow-typed version: <>/node-fetch_v1.6.3/flow_v0.38.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/nyc_vx.x.x.js b/flow-typed/npm/nyc_vx.x.x.js index 8013735f..c0a45a16 100644 --- a/flow-typed/npm/nyc_vx.x.x.js +++ b/flow-typed/npm/nyc_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: a64deef67989ede615b081912aa88e4a -// flow-typed version: <>/nyc_v10.0.0/flow_v0.37.4 +// flow-typed signature: 60025bde4e74f11c19497dd2b22b80b4 +// flow-typed version: <>/nyc_v10.1.2/flow_v0.38.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/ora_vx.x.x.js b/flow-typed/npm/ora_vx.x.x.js index ec1756d1..ca49161c 100644 --- a/flow-typed/npm/ora_vx.x.x.js +++ b/flow-typed/npm/ora_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: d84ef127f35065e0ef87b961704566b7 -// flow-typed version: <>/ora_v0.4.0/flow_v0.37.4 +// flow-typed signature: 5247f23d1d5e04431d931fb72cde82c7 +// flow-typed version: <>/ora_v1.0.0/flow_v0.38.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/remark-cli_vx.x.x.js b/flow-typed/npm/remark-cli_vx.x.x.js index d95feef3..452a4a2d 100644 --- a/flow-typed/npm/remark-cli_vx.x.x.js +++ b/flow-typed/npm/remark-cli_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: 220b9a83bd415c1757a1a814c918d740 -// flow-typed version: <>/remark-cli_v2.1.0/flow_v0.37.4 +// flow-typed signature: 874f86bb1fd3b256fd22b101cc7c49d9 +// flow-typed version: <>/remark-cli_v2.1.0/flow_v0.38.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/remark-lint_vx.x.x.js b/flow-typed/npm/remark-lint_vx.x.x.js index 9688c3fa..00aee0e8 100644 --- a/flow-typed/npm/remark-lint_vx.x.x.js +++ b/flow-typed/npm/remark-lint_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: 472f73896e15dd81e8cba88d4866d89b -// flow-typed version: <>/remark-lint_v5.4.0/flow_v0.37.4 +// flow-typed signature: e63505d366e4ccbf4d93a1a8a324c940 +// flow-typed version: <>/remark-lint_v5.4.0/flow_v0.38.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/remark-preset-lint-recommended_vx.x.x.js b/flow-typed/npm/remark-preset-lint-recommended_vx.x.x.js index 395612d6..5aa24c85 100644 --- a/flow-typed/npm/remark-preset-lint-recommended_vx.x.x.js +++ b/flow-typed/npm/remark-preset-lint-recommended_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: 3ee7e3ab11db69843340916ea720bbcc -// flow-typed version: <>/remark-preset-lint-recommended_v1.0.0/flow_v0.37.4 +// flow-typed signature: 9795e498c7d5012466199f76d25118f0 +// flow-typed version: <>/remark-preset-lint-recommended_v1.0.0/flow_v0.38.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/rollup-plugin-alias_vx.x.x.js b/flow-typed/npm/rollup-plugin-alias_vx.x.x.js index ed6ef61e..63a3b151 100644 --- a/flow-typed/npm/rollup-plugin-alias_vx.x.x.js +++ b/flow-typed/npm/rollup-plugin-alias_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: 0842c9f6bc666344788cf346fab52cbe -// flow-typed version: <>/rollup-plugin-alias_v1.2.0/flow_v0.37.4 +// flow-typed signature: f690b64455dd559925e1167c4ae35753 +// flow-typed version: <>/rollup-plugin-alias_v1.2.0/flow_v0.38.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/rollup-plugin-babel_vx.x.x.js b/flow-typed/npm/rollup-plugin-babel_vx.x.x.js index bf75237f..6b3bb8c2 100644 --- a/flow-typed/npm/rollup-plugin-babel_vx.x.x.js +++ b/flow-typed/npm/rollup-plugin-babel_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: abb5699c4bc060d6191b9a78612664a3 -// flow-typed version: <>/rollup-plugin-babel_v2.7.1/flow_v0.37.4 +// flow-typed signature: 8db89a282c24932f2ea9956386edc8e2 +// flow-typed version: <>/rollup-plugin-babel_v2.7.1/flow_v0.38.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/rollup-plugin-eslint_vx.x.x.js b/flow-typed/npm/rollup-plugin-eslint_vx.x.x.js index 967aa54f..6bfd9321 100644 --- a/flow-typed/npm/rollup-plugin-eslint_vx.x.x.js +++ b/flow-typed/npm/rollup-plugin-eslint_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: c1a79170120b5b11bb6b8aad967ae61d -// flow-typed version: <>/rollup-plugin-eslint_v3.0.0/flow_v0.37.4 +// flow-typed signature: 14540a968670ab30e50e04fe48322cf3 +// flow-typed version: <>/rollup-plugin-eslint_v3.0.0/flow_v0.38.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/rollup-plugin-json_vx.x.x.js b/flow-typed/npm/rollup-plugin-json_vx.x.x.js index 90f329f6..d3d2969c 100644 --- a/flow-typed/npm/rollup-plugin-json_vx.x.x.js +++ b/flow-typed/npm/rollup-plugin-json_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: 3961843fca066a4b4d9a1a99751fa6e5 -// flow-typed version: <>/rollup-plugin-json_v2.0.2/flow_v0.37.4 +// flow-typed signature: 9732b5b9ddc7803620d47ddb16d2a108 +// flow-typed version: <>/rollup-plugin-json_v2.0.2/flow_v0.38.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/rollup-plugin-lux_vx.x.x.js b/flow-typed/npm/rollup-plugin-lux_vx.x.x.js new file mode 100644 index 00000000..f43526a2 --- /dev/null +++ b/flow-typed/npm/rollup-plugin-lux_vx.x.x.js @@ -0,0 +1,298 @@ +// flow-typed signature: 909e8534269a646264f04853a7b2397f +// flow-typed version: <>/rollup-plugin-lux_v3.0.0/flow_v0.38.0 + +/** + * This is an autogenerated libdef stub for: + * + * 'rollup-plugin-lux' + * + * Fill this stub out by replacing all the `any` types. + * + * Once filled out, we encourage you to share your work with the + * community by sending a pull request to: + * https://github.com/flowtype/flow-typed + */ + +declare module 'rollup-plugin-lux' { + declare module.exports: any; +} + +/** + * We include stubs for each file inside this npm package in case you need to + * require those files directly. Feel free to delete any files that aren't + * needed. + */ +declare module 'rollup-plugin-lux/decl/acorn' { + declare module.exports: any; +} + +declare module 'rollup-plugin-lux/decl/eol' { + declare module.exports: any; +} + +declare module 'rollup-plugin-lux/decl/magic-string' { + declare module.exports: any; +} + +declare module 'rollup-plugin-lux/dist/index.es' { + declare module.exports: any; +} + +declare module 'rollup-plugin-lux/dist/index' { + declare module.exports: any; +} + +declare module 'rollup-plugin-lux/flow-typed/npm/acorn_vx.x.x' { + declare module.exports: any; +} + +declare module 'rollup-plugin-lux/flow-typed/npm/babel-core_vx.x.x' { + declare module.exports: any; +} + +declare module 'rollup-plugin-lux/flow-typed/npm/babel-eslint_vx.x.x' { + declare module.exports: any; +} + +declare module 'rollup-plugin-lux/flow-typed/npm/babel-jest_vx.x.x' { + declare module.exports: any; +} + +declare module 'rollup-plugin-lux/flow-typed/npm/babel-plugin-transform-es2015-modules-commonjs_vx.x.x' { + declare module.exports: any; +} + +declare module 'rollup-plugin-lux/flow-typed/npm/babel-plugin-transform-flow-strip-types_vx.x.x' { + declare module.exports: any; +} + +declare module 'rollup-plugin-lux/flow-typed/npm/eol_vx.x.x' { + declare module.exports: any; +} + +declare module 'rollup-plugin-lux/flow-typed/npm/eslint_vx.x.x' { + declare module.exports: any; +} + +declare module 'rollup-plugin-lux/flow-typed/npm/eslint-config-airbnb-base_vx.x.x' { + declare module.exports: any; +} + +declare module 'rollup-plugin-lux/flow-typed/npm/eslint-plugin-flowtype_vx.x.x' { + declare module.exports: any; +} + +declare module 'rollup-plugin-lux/flow-typed/npm/eslint-plugin-import_vx.x.x' { + declare module.exports: any; +} + +declare module 'rollup-plugin-lux/flow-typed/npm/flow-bin_v0.x.x' { + declare module.exports: any; +} + +declare module 'rollup-plugin-lux/flow-typed/npm/flow-typed_vx.x.x' { + declare module.exports: any; +} + +declare module 'rollup-plugin-lux/flow-typed/npm/fs-promise_vx.x.x' { + declare module.exports: any; +} + +declare module 'rollup-plugin-lux/flow-typed/npm/jest_v18.x.x' { + declare module.exports: any; +} + +declare module 'rollup-plugin-lux/flow-typed/npm/jest-junit_vx.x.x' { + declare module.exports: any; +} + +declare module 'rollup-plugin-lux/flow-typed/npm/magic-string_vx.x.x' { + declare module.exports: any; +} + +declare module 'rollup-plugin-lux/flow-typed/npm/remark-cli_vx.x.x' { + declare module.exports: any; +} + +declare module 'rollup-plugin-lux/flow-typed/npm/remark-preset-lint-recommended_vx.x.x' { + declare module.exports: any; +} + +declare module 'rollup-plugin-lux/flow-typed/npm/rollup_vx.x.x' { + declare module.exports: any; +} + +declare module 'rollup-plugin-lux/flow-typed/npm/rollup-plugin-babel_vx.x.x' { + declare module.exports: any; +} + +declare module 'rollup-plugin-lux/flow-typed/npm/rollup-plugin-node-resolve_vx.x.x' { + declare module.exports: any; +} + +declare module 'rollup-plugin-lux/flow-typed/npm/shx_vx.x.x' { + declare module.exports: any; +} + +declare module 'rollup-plugin-lux/rollup.config' { + declare module.exports: any; +} + +declare module 'rollup-plugin-lux/src/constants' { + declare module.exports: any; +} + +declare module 'rollup-plugin-lux/src/index' { + declare module.exports: any; +} + +declare module 'rollup-plugin-lux/src/interfaces' { + declare module.exports: any; +} + +declare module 'rollup-plugin-lux/src/transform/index' { + declare module.exports: any; +} + +declare module 'rollup-plugin-lux/src/transform/interfaces' { + declare module.exports: any; +} + +declare module 'rollup-plugin-lux/src/transform/parse-source' { + declare module.exports: any; +} + +declare module 'rollup-plugin-lux/src/transform/render' { + declare module.exports: any; +} + +declare module 'rollup-plugin-lux/src/transform/static-name' { + declare module.exports: any; +} + +declare module 'rollup-plugin-lux/src/utils/compose' { + declare module.exports: any; +} + +declare module 'rollup-plugin-lux/src/utils/test/compose.test' { + declare module.exports: any; +} + +// Filename aliases +declare module 'rollup-plugin-lux/decl/acorn.js' { + declare module.exports: $Exports<'rollup-plugin-lux/decl/acorn'>; +} +declare module 'rollup-plugin-lux/decl/eol.js' { + declare module.exports: $Exports<'rollup-plugin-lux/decl/eol'>; +} +declare module 'rollup-plugin-lux/decl/magic-string.js' { + declare module.exports: $Exports<'rollup-plugin-lux/decl/magic-string'>; +} +declare module 'rollup-plugin-lux/dist/index.es.js' { + declare module.exports: $Exports<'rollup-plugin-lux/dist/index.es'>; +} +declare module 'rollup-plugin-lux/dist/index.js' { + declare module.exports: $Exports<'rollup-plugin-lux/dist/index'>; +} +declare module 'rollup-plugin-lux/flow-typed/npm/acorn_vx.x.x.js' { + declare module.exports: $Exports<'rollup-plugin-lux/flow-typed/npm/acorn_vx.x.x'>; +} +declare module 'rollup-plugin-lux/flow-typed/npm/babel-core_vx.x.x.js' { + declare module.exports: $Exports<'rollup-plugin-lux/flow-typed/npm/babel-core_vx.x.x'>; +} +declare module 'rollup-plugin-lux/flow-typed/npm/babel-eslint_vx.x.x.js' { + declare module.exports: $Exports<'rollup-plugin-lux/flow-typed/npm/babel-eslint_vx.x.x'>; +} +declare module 'rollup-plugin-lux/flow-typed/npm/babel-jest_vx.x.x.js' { + declare module.exports: $Exports<'rollup-plugin-lux/flow-typed/npm/babel-jest_vx.x.x'>; +} +declare module 'rollup-plugin-lux/flow-typed/npm/babel-plugin-transform-es2015-modules-commonjs_vx.x.x.js' { + declare module.exports: $Exports<'rollup-plugin-lux/flow-typed/npm/babel-plugin-transform-es2015-modules-commonjs_vx.x.x'>; +} +declare module 'rollup-plugin-lux/flow-typed/npm/babel-plugin-transform-flow-strip-types_vx.x.x.js' { + declare module.exports: $Exports<'rollup-plugin-lux/flow-typed/npm/babel-plugin-transform-flow-strip-types_vx.x.x'>; +} +declare module 'rollup-plugin-lux/flow-typed/npm/eol_vx.x.x.js' { + declare module.exports: $Exports<'rollup-plugin-lux/flow-typed/npm/eol_vx.x.x'>; +} +declare module 'rollup-plugin-lux/flow-typed/npm/eslint_vx.x.x.js' { + declare module.exports: $Exports<'rollup-plugin-lux/flow-typed/npm/eslint_vx.x.x'>; +} +declare module 'rollup-plugin-lux/flow-typed/npm/eslint-config-airbnb-base_vx.x.x.js' { + declare module.exports: $Exports<'rollup-plugin-lux/flow-typed/npm/eslint-config-airbnb-base_vx.x.x'>; +} +declare module 'rollup-plugin-lux/flow-typed/npm/eslint-plugin-flowtype_vx.x.x.js' { + declare module.exports: $Exports<'rollup-plugin-lux/flow-typed/npm/eslint-plugin-flowtype_vx.x.x'>; +} +declare module 'rollup-plugin-lux/flow-typed/npm/eslint-plugin-import_vx.x.x.js' { + declare module.exports: $Exports<'rollup-plugin-lux/flow-typed/npm/eslint-plugin-import_vx.x.x'>; +} +declare module 'rollup-plugin-lux/flow-typed/npm/flow-bin_v0.x.x.js' { + declare module.exports: $Exports<'rollup-plugin-lux/flow-typed/npm/flow-bin_v0.x.x'>; +} +declare module 'rollup-plugin-lux/flow-typed/npm/flow-typed_vx.x.x.js' { + declare module.exports: $Exports<'rollup-plugin-lux/flow-typed/npm/flow-typed_vx.x.x'>; +} +declare module 'rollup-plugin-lux/flow-typed/npm/fs-promise_vx.x.x.js' { + declare module.exports: $Exports<'rollup-plugin-lux/flow-typed/npm/fs-promise_vx.x.x'>; +} +declare module 'rollup-plugin-lux/flow-typed/npm/jest_v18.x.x.js' { + declare module.exports: $Exports<'rollup-plugin-lux/flow-typed/npm/jest_v18.x.x'>; +} +declare module 'rollup-plugin-lux/flow-typed/npm/jest-junit_vx.x.x.js' { + declare module.exports: $Exports<'rollup-plugin-lux/flow-typed/npm/jest-junit_vx.x.x'>; +} +declare module 'rollup-plugin-lux/flow-typed/npm/magic-string_vx.x.x.js' { + declare module.exports: $Exports<'rollup-plugin-lux/flow-typed/npm/magic-string_vx.x.x'>; +} +declare module 'rollup-plugin-lux/flow-typed/npm/remark-cli_vx.x.x.js' { + declare module.exports: $Exports<'rollup-plugin-lux/flow-typed/npm/remark-cli_vx.x.x'>; +} +declare module 'rollup-plugin-lux/flow-typed/npm/remark-preset-lint-recommended_vx.x.x.js' { + declare module.exports: $Exports<'rollup-plugin-lux/flow-typed/npm/remark-preset-lint-recommended_vx.x.x'>; +} +declare module 'rollup-plugin-lux/flow-typed/npm/rollup_vx.x.x.js' { + declare module.exports: $Exports<'rollup-plugin-lux/flow-typed/npm/rollup_vx.x.x'>; +} +declare module 'rollup-plugin-lux/flow-typed/npm/rollup-plugin-babel_vx.x.x.js' { + declare module.exports: $Exports<'rollup-plugin-lux/flow-typed/npm/rollup-plugin-babel_vx.x.x'>; +} +declare module 'rollup-plugin-lux/flow-typed/npm/rollup-plugin-node-resolve_vx.x.x.js' { + declare module.exports: $Exports<'rollup-plugin-lux/flow-typed/npm/rollup-plugin-node-resolve_vx.x.x'>; +} +declare module 'rollup-plugin-lux/flow-typed/npm/shx_vx.x.x.js' { + declare module.exports: $Exports<'rollup-plugin-lux/flow-typed/npm/shx_vx.x.x'>; +} +declare module 'rollup-plugin-lux/rollup.config.js' { + declare module.exports: $Exports<'rollup-plugin-lux/rollup.config'>; +} +declare module 'rollup-plugin-lux/src/constants.js' { + declare module.exports: $Exports<'rollup-plugin-lux/src/constants'>; +} +declare module 'rollup-plugin-lux/src/index.js' { + declare module.exports: $Exports<'rollup-plugin-lux/src/index'>; +} +declare module 'rollup-plugin-lux/src/interfaces.js' { + declare module.exports: $Exports<'rollup-plugin-lux/src/interfaces'>; +} +declare module 'rollup-plugin-lux/src/transform/index.js' { + declare module.exports: $Exports<'rollup-plugin-lux/src/transform/index'>; +} +declare module 'rollup-plugin-lux/src/transform/interfaces.js' { + declare module.exports: $Exports<'rollup-plugin-lux/src/transform/interfaces'>; +} +declare module 'rollup-plugin-lux/src/transform/parse-source.js' { + declare module.exports: $Exports<'rollup-plugin-lux/src/transform/parse-source'>; +} +declare module 'rollup-plugin-lux/src/transform/render.js' { + declare module.exports: $Exports<'rollup-plugin-lux/src/transform/render'>; +} +declare module 'rollup-plugin-lux/src/transform/static-name.js' { + declare module.exports: $Exports<'rollup-plugin-lux/src/transform/static-name'>; +} +declare module 'rollup-plugin-lux/src/utils/compose.js' { + declare module.exports: $Exports<'rollup-plugin-lux/src/utils/compose'>; +} +declare module 'rollup-plugin-lux/src/utils/test/compose.test.js' { + declare module.exports: $Exports<'rollup-plugin-lux/src/utils/test/compose.test'>; +} diff --git a/flow-typed/npm/rollup-plugin-multi-entry_vx.x.x.js b/flow-typed/npm/rollup-plugin-multi-entry_vx.x.x.js new file mode 100644 index 00000000..12910425 --- /dev/null +++ b/flow-typed/npm/rollup-plugin-multi-entry_vx.x.x.js @@ -0,0 +1,32 @@ +// flow-typed signature: 2d9ca705a1b02b5e8d967102e4060690 +// flow-typed version: <>/rollup-plugin-multi-entry_v2.0.1/flow_v0.38.0 + +/** + * This is an autogenerated libdef stub for: + * + * 'rollup-plugin-multi-entry' + * + * Fill this stub out by replacing all the `any` types. + * + * Once filled out, we encourage you to share your work with the + * community by sending a pull request to: + * https://github.com/flowtype/flow-typed + */ + +declare module 'rollup-plugin-multi-entry' { + declare module.exports: any; +} + +/** + * We include stubs for each file inside this npm package in case you need to + * require those files directly. Feel free to delete any files that aren't + * needed. + */ +declare module 'rollup-plugin-multi-entry/dist/rollup-plugin-multi-entry' { + declare module.exports: any; +} + +// Filename aliases +declare module 'rollup-plugin-multi-entry/dist/rollup-plugin-multi-entry.js' { + declare module.exports: $Exports<'rollup-plugin-multi-entry/dist/rollup-plugin-multi-entry'>; +} diff --git a/flow-typed/npm/rollup-plugin-node-resolve_vx.x.x.js b/flow-typed/npm/rollup-plugin-node-resolve_vx.x.x.js index 2611c21d..50aec0d9 100644 --- a/flow-typed/npm/rollup-plugin-node-resolve_vx.x.x.js +++ b/flow-typed/npm/rollup-plugin-node-resolve_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: 32f69a82227c079d39861205abff3d6a -// flow-typed version: <>/rollup-plugin-node-resolve_v2.0.0/flow_v0.37.4 +// flow-typed signature: d82331e3b74f5830e7cf49a570eaf8c9 +// flow-typed version: <>/rollup-plugin-node-resolve_v2.0.0/flow_v0.38.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/rollup_vx.x.x.js b/flow-typed/npm/rollup_vx.x.x.js index 5ad57701..b04b759e 100644 --- a/flow-typed/npm/rollup_vx.x.x.js +++ b/flow-typed/npm/rollup_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: d3611bf94e963633e0a9014f71c7174d -// flow-typed version: <>/rollup_v0.37.0/flow_v0.37.4 +// flow-typed signature: 6e70a13a5078052df9c10e4501267cfb +// flow-typed version: <>/rollup_v0.41.4/flow_v0.38.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/shx_vx.x.x.js b/flow-typed/npm/shx_vx.x.x.js new file mode 100644 index 00000000..bdf8a75a --- /dev/null +++ b/flow-typed/npm/shx_vx.x.x.js @@ -0,0 +1,60 @@ +// flow-typed signature: 4d1872b9258c322405383245fcd9b511 +// flow-typed version: <>/shx_v0.2.2/flow_v0.38.0 + +/** + * This is an autogenerated libdef stub for: + * + * 'shx' + * + * Fill this stub out by replacing all the `any` types. + * + * Once filled out, we encourage you to share your work with the + * community by sending a pull request to: + * https://github.com/flowtype/flow-typed + */ + +declare module 'shx' { + declare module.exports: any; +} + +/** + * We include stubs for each file inside this npm package in case you need to + * require those files directly. Feel free to delete any files that aren't + * needed. + */ +declare module 'shx/lib/cli' { + declare module.exports: any; +} + +declare module 'shx/lib/config' { + declare module.exports: any; +} + +declare module 'shx/lib/help' { + declare module.exports: any; +} + +declare module 'shx/lib/printCmdRet' { + declare module.exports: any; +} + +declare module 'shx/lib/shx' { + declare module.exports: any; +} + +// Filename aliases +declare module 'shx/lib/cli.js' { + declare module.exports: $Exports<'shx/lib/cli'>; +} +declare module 'shx/lib/config.js' { + declare module.exports: $Exports<'shx/lib/config'>; +} +declare module 'shx/lib/help.js' { + declare module.exports: $Exports<'shx/lib/help'>; +} +declare module 'shx/lib/printCmdRet.js' { + declare module.exports: $Exports<'shx/lib/printCmdRet'>; +} +declare module 'shx/lib/shx.js' { + declare module.exports: $Exports<'shx/lib/shx'>; +} diff --git a/flow-typed/npm/sinon_vx.x.x.js b/flow-typed/npm/sinon_vx.x.x.js index 69ca3382..03be9713 100644 --- a/flow-typed/npm/sinon_vx.x.x.js +++ b/flow-typed/npm/sinon_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: 0e1bf5241d587cdd95459c832262a1f1 -// flow-typed version: <>/sinon_v1.17.7/flow_v0.37.4 +// flow-typed signature: d6244e61bc4d3f66af2d9fe508b5d4f5 +// flow-typed version: <>/sinon_v1.17.7/flow_v0.38.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/source-map-support_vx.x.x.js b/flow-typed/npm/source-map-support_vx.x.x.js index 62facf51..7e2058c6 100644 --- a/flow-typed/npm/source-map-support_vx.x.x.js +++ b/flow-typed/npm/source-map-support_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: a01a481d420d1895b8df891b0928b247 -// flow-typed version: <>/source-map-support_v0.4.8/flow_v0.37.4 +// flow-typed signature: 484270ae3534b05cdf5cb108c6a904be +// flow-typed version: <>/source-map-support_v0.4.10/flow_v0.38.0 /** * This is an autogenerated libdef stub for: @@ -42,6 +42,18 @@ declare module 'source-map-support/test' { declare module.exports: any; } +declare module 'source-map-support/webpack-test/compiled' { + declare module.exports: any; +} + +declare module 'source-map-support/webpack-test/script' { + declare module.exports: any; +} + +declare module 'source-map-support/webpack-test/webpack.config' { + declare module.exports: any; +} + // Filename aliases declare module 'source-map-support/browser-source-map-support.js' { declare module.exports: $Exports<'source-map-support/browser-source-map-support'>; @@ -58,3 +70,12 @@ declare module 'source-map-support/source-map-support.js' { declare module 'source-map-support/test.js' { declare module.exports: $Exports<'source-map-support/test'>; } +declare module 'source-map-support/webpack-test/compiled.js' { + declare module.exports: $Exports<'source-map-support/webpack-test/compiled'>; +} +declare module 'source-map-support/webpack-test/script.js' { + declare module.exports: $Exports<'source-map-support/webpack-test/script'>; +} +declare module 'source-map-support/webpack-test/webpack.config.js' { + declare module.exports: $Exports<'source-map-support/webpack-test/webpack.config'>; +} diff --git a/mocha.opts b/mocha.opts new file mode 100644 index 00000000..947b72a8 --- /dev/null +++ b/mocha.opts @@ -0,0 +1,2 @@ +--require ./lib/babel-hook.js +test/index.js src/**/*.test.js diff --git a/package.json b/package.json index 88ee4251..1c7011fb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "lux-framework", - "version": "1.1.1", + "version": "1.1.4", "description": "Build scalable, Node.js-powered REST APIs with almost no code.", "repository": "github:postlight/lux", "keywords": [ @@ -17,16 +17,13 @@ "lux": "bin/lux" }, "scripts": { - "build": "npm run clean && npm run flow && npm run lint && npm run build:cli", - "build:cli": "node scripts/build/cli.js", - "build:test": "node scripts/build/test.js", - "clean": "node scripts/clean.js", + "build": "rollup -c", + "clean": "shx rm -rf .nyc_output coverage dist test/test-app/dist test-results.xml", + "codecov": "nyc report --reporter=lcov > coverage.lcov && curl -s https://codecov.io/bash | bash", "flow": "flow check", - "install:types": "flow-typed install --overwrite", + "install:types": "shx rm -rf flow-typed && flow-typed install", "lint": "remark . && eslint .", - "start": "lux serve", - "test": "npm run build && nyc -i ./lib/babel-hook.js --instrument false --source-map false mocha -r ./lib/babel-hook.js test/index.js src/**/*.test.js", - "test:codecov": "nyc report --reporter=lcov > coverage.lcov && curl -s https://codecov.io/bash | bash" + "test": "nyc -i ./lib/babel-hook.js --instrument false --source-map false mocha --opts mocha.opts" }, "author": "Zachary Golba", "license": "MIT", @@ -38,41 +35,45 @@ "node": ">= 6.0" }, "dependencies": { - "ansi-regex": "2.0.0", + "ansi-regex": "2.1.1", "babel-eslint": "7.1.1", "chalk": "1.1.3", "commander": "2.9.0", - "eslint": "3.12.1", + "eslint": "3.13.1", "fb-watchman": "1.9.0", "inflection": "1.10.0", "knex": "0.12.6", - "ora": "0.4.0", - "rollup": "0.37.0", + "ora": "1.0.0", + "rollup": "0.41.4", "rollup-plugin-alias": "1.2.0", "rollup-plugin-babel": "2.7.1", "rollup-plugin-eslint": "3.0.0", "rollup-plugin-json": "2.0.2", + "rollup-plugin-lux": "3.0.0", "rollup-plugin-node-resolve": "2.0.0", - "source-map-support": "0.4.8" + "source-map-support": "0.4.10" }, "devDependencies": { "babel-core": "6.21.0", - "babel-plugin-istanbul": "3.0.0", + "babel-plugin-istanbul": "3.1.2", "babel-plugin-transform-es2015-modules-commonjs": "6.18.0", "babel-preset-lux": "2.0.1", "chai": "3.5.0", - "eslint-config-airbnb-base": "11.0.0", - "eslint-plugin-flowtype": "2.29.2", + "eslint-config-airbnb-base": "11.0.1", + "eslint-plugin-flowtype": "2.30.0", "eslint-plugin-import": "2.2.0", "faker": "3.1.0", - "flow-bin": "0.37.4", + "flow-bin": "0.38.0", "flow-typed": "2.0.0", "mocha": "3.2.0", + "mocha-junit-reporter": "1.13.0", "node-fetch": "1.6.3", - "nyc": "10.0.0", + "nyc": "10.1.2", "remark-cli": "2.1.0", "remark-lint": "5.4.0", "remark-preset-lint-recommended": "1.0.0", + "rollup-plugin-multi-entry": "2.0.1", + "shx": "0.2.2", "sinon": "1.17.7" } } diff --git a/rollup.config.js b/rollup.config.js new file mode 100644 index 00000000..83340972 --- /dev/null +++ b/rollup.config.js @@ -0,0 +1,38 @@ +import path from 'path'; + +import json from 'rollup-plugin-json'; +import babel from 'rollup-plugin-babel'; +import resolve from 'rollup-plugin-node-resolve'; + +export default { + dest: 'dist/index.js', + entry: 'src/packages/cli/commands/index.js', + format: 'cjs', + banner: ( + 'require(\'source-map-support\').install({\n' + + ' environment: \'node\'\n' + + '});\n' + ), + onwarn: ({ code, message }) => { + if (code === 'UNUSED_EXTERNAL_IMPORT') { + return; + } + // eslint-disable-next-line no-console + console.warn(message); + }, + plugins: [ + json(), + babel(), + resolve() + ], + external: id => !( + id.startsWith('.') + || id.startsWith('/') // Absolute path on Unix + || /^[A-Z]:[\\/]/.test(id) // Absolute path on Windows + || id.startsWith('src') + || id.startsWith(path.join(__dirname, 'src')) + || id === 'babelHelpers' + || id === '\u0000babelHelpers' + ), + sourceMap: true +}; diff --git a/scripts/appveyor/before-test.ps1 b/scripts/appveyor/before-test.ps1 deleted file mode 100644 index 38406ce8..00000000 --- a/scripts/appveyor/before-test.ps1 +++ /dev/null @@ -1,19 +0,0 @@ -$DROP_DATABASE="DROP DATABASE IF EXISTS lux_test;" -$CREATE_DATABASE="CREATE DATABASE lux_test;" - -Switch ($env:DATABASE_DRIVER) { - "mysql2" { - $env:MYSQL_PWD="Password12!" - $mysql="C:\Program Files\MySql\MySQL Server 5.7\bin\mysql" - - Invoke-Expression "& '$mysql' -e '$DROP_DATABASE' -u root" - Invoke-Expression "& '$mysql' -e '$CREATE_DATABASE' -u root" - } - - "sqlite3" { - Remove-Item C:\projects\lux\test\test-app\db\* -Force -Include *.sqlite - Write-Host $null >> C:\projects\lux\test\test-app\db\lux_test_test.sqlite - } -} - -New-Item -ItemType directory C:\tmp diff --git a/scripts/appveyor/install.ps1 b/scripts/appveyor/install.ps1 deleted file mode 100644 index b9f7e6dc..00000000 --- a/scripts/appveyor/install.ps1 +++ /dev/null @@ -1,8 +0,0 @@ -Install-Product node $env:nodejs_version x64 - -npm install -npm link - -Set-Location C:\projects\lux\test\test-app -npm install -Set-Location C:\projects\lux diff --git a/scripts/build/cli.js b/scripts/build/cli.js deleted file mode 100644 index f95ceff7..00000000 --- a/scripts/build/cli.js +++ /dev/null @@ -1,48 +0,0 @@ -'use strict'; - -process.env.NODE_ENV = 'production'; - -require('../../lib/babel-hook'); - -const { EOL } = require('os'); -const path = require('path'); - -const rollup = require('rollup').rollup; - -const fs = require('../../src/packages/fs'); - -const dist = path.join(__dirname, '..', '..', 'dist'); -const config = require('./config'); - -const commands = path.join( - __dirname, - '..', - '..', - 'src', - 'packages', - 'cli', - 'commands' -); - -fs.readdir(commands) - .then(files => Promise.all( - files.map(file => { - const cmdConfig = { - rollup: Object.assign({}, config.rollup, { - entry: path.join(commands, file), - }), - - bundle: Object.assign({}, config.bundle, { - dest: path.join(dist, file) - }) - }; - - return rollup(cmdConfig.rollup) - .then(bundle => bundle.write(cmdConfig.bundle)); - }) - )) - .catch(err => { - process.stderr.write(err.stack); - process.stderr.write(EOL); - process.exit(1); - }); diff --git a/scripts/circle/before-script.sh b/scripts/circle/before-script.sh deleted file mode 100644 index e191e200..00000000 --- a/scripts/circle/before-script.sh +++ /dev/null @@ -1,28 +0,0 @@ -#!/bin/bash -e -DROP_DATABASE="DROP DATABASE IF EXISTS lux_test;" -CREATE_DATABASE="CREATE DATABASE lux_test;" - -case $CIRCLE_NODE_INDEX in - 0) - export DATABASE_DRIVER="pg" - - psql -c "$DROP_DATABASE" -U postgres - psql -c "$CREATE_DATABASE" -U postgres - ;; - - 1) - export DATABASE_DRIVER="mysql2" - - mysql -e "$DROP_DATABASE" - mysql -e "$CREATE_DATABASE" - ;; - - 2) - export DATABASE_DRIVER="sqlite3" - - rm -rf test/test-app/db/lux_test_test.sqlite - touch test/test-app/db/lux_test_test.sqlite - ;; -esac - -echo "ENV: DATABASE_DRIVER=$DATABASE_DRIVER" diff --git a/scripts/circle/install.sh b/scripts/circle/install.sh deleted file mode 100644 index 82885edc..00000000 --- a/scripts/circle/install.sh +++ /dev/null @@ -1,32 +0,0 @@ -#!/bin/bash -e -set -x -set -e - -cd ../ - -# Install Watchman -if [ -d watchman ]; then - cd watchman - sudo make install -else - git clone https://github.com/facebook/watchman.git - cd watchman - git checkout v4.7.0 - - ./autogen.sh - ./configure - make - sudo make install -fi - -cd ../lux - -# Install Node Modules -rm -rf node_modules -npm install -npm link - -cd test/test-app -rm -rf node_modules -npm install -cd ../../ diff --git a/scripts/clean.js b/scripts/clean.js deleted file mode 100644 index 107dc65d..00000000 --- a/scripts/clean.js +++ /dev/null @@ -1,19 +0,0 @@ -'use strict'; - -require('../lib/babel-hook'); - -const path = require('path'); - -const rmrf = require('../src/packages/fs').rmrf; - -Promise.all([ - rmrf(path.join(__dirname, '..', '.nyc_output')), - rmrf(path.join(__dirname, '..', 'coverage')), - rmrf(path.join(__dirname, '..', 'coverage.lcov')), - rmrf(path.join(__dirname, '..', 'dist')), - rmrf(path.join(__dirname, '..', 'test', 'test-app', 'dist')) -]).then(() => { - process.exit(0); -}).catch(() => { - process.exit(1); -}); diff --git a/src/constants.js b/src/constants.js index aa7781ca..0ff4b3b5 100644 --- a/src/constants.js +++ b/src/constants.js @@ -21,7 +21,6 @@ export const NODE_ENV = ENV.NODE_ENV || 'development'; export const DATABASE_URL = ENV.DATABASE_URL; export const LUX_CONSOLE = ENV.LUX_CONSOLE || false; export const PLATFORM = platform(); -export const BACKSLASH = /\\/g; export const CIRCLECI = ENV.CIRCLECI; export const APPVEYOR = ENV.APPVEYOR; export const IS_PRODUCTION = NODE_ENV === 'production'; diff --git a/src/interfaces.js b/src/interfaces.js index 68b391d3..0985329e 100644 --- a/src/interfaces.js +++ b/src/interfaces.js @@ -17,3 +17,19 @@ export interface Chain { export type ObjectMap = { [key: K]: V; }; + +export interface Thenable<+R> { + constructor(callback: ( + resolve: (result: Promise | R) => void, + reject: (error: any) => void + ) => mixed): void; + + then( + onFulfill?: (value: R) => Promise | U, + onReject?: (error: any) => Promise | U + ): Promise; + + catch( + onReject?: (error: any) => ?Promise | U + ): Promise; +} diff --git a/src/packages/application/index.js b/src/packages/application/index.js index 182f445f..cf52d738 100644 --- a/src/packages/application/index.js +++ b/src/packages/application/index.js @@ -88,7 +88,7 @@ class Application { * @type {Map} * @private */ - controllers: FreezeableMap; + controllers: FreezeableMap>; /** * A map containing each `Serializer` instance. diff --git a/src/packages/application/interfaces.js b/src/packages/application/interfaces.js index d1d9feba..384d2ca5 100644 --- a/src/packages/application/interfaces.js +++ b/src/packages/application/interfaces.js @@ -10,7 +10,7 @@ export type Application$opts = Config & { database: Database$config; }; -export type Application$factoryOpts> = { +export type Application$factoryOpts | Serializer<*>> = { key: string; store: Database; parent: ?T; diff --git a/src/packages/application/utils/create-controller.js b/src/packages/application/utils/create-controller.js index 095a5c79..eea986a6 100644 --- a/src/packages/application/utils/create-controller.js +++ b/src/packages/application/utils/create-controller.js @@ -1,29 +1,27 @@ // @flow +import { posix } from 'path'; + import { deepFreezeProps } from '../../freezeable'; -import { - getNamespaceKey, - stripNamespaces, - closestAncestor -} from '../../loader'; +import { closestAncestor } from '../../loader'; import { tryCatchSync } from '../../../utils/try-catch'; import type Database from '../../database'; import type Controller from '../../controller'; import type Serializer from '../../serializer'; import type { Bundle$Namespace } from '../../loader'; // eslint-disable-line max-len, no-duplicate-imports -export default function createController( +export default function createController>( constructor: Class, opts: { key: string; store: Database; - parent: ?Controller; + parent: ?Controller<*>; serializers: Bundle$Namespace>; } ): T { const { key, store, serializers } = opts; - const namespace = getNamespaceKey(key).replace('root', ''); + const namespace = posix.dirname(key).replace('.', ''); let { parent } = opts; - let model = tryCatchSync(() => store.modelFor(stripNamespaces(key))); + let model = tryCatchSync(() => store.modelFor(posix.basename(key))); let serializer = serializers.get(key); if (!model) { @@ -39,7 +37,6 @@ export default function createController( } const instance: T = new constructor({ - // $FlowIgnore model, namespace, serializer diff --git a/src/packages/application/utils/create-serializer.js b/src/packages/application/utils/create-serializer.js index 5eaefce4..09f32722 100644 --- a/src/packages/application/utils/create-serializer.js +++ b/src/packages/application/utils/create-serializer.js @@ -1,6 +1,7 @@ // @flow +import { posix } from 'path'; + import { deepFreezeProps } from '../../freezeable'; -import { getNamespaceKey, stripNamespaces } from '../../loader'; import { tryCatchSync } from '../../../utils/try-catch'; import type Serializer from '../../serializer'; // eslint-disable-line max-len, no-unused-vars import type { Application$factoryOpts } from '../index'; @@ -10,9 +11,9 @@ export default function createSerializer>( opts: Application$factoryOpts ): T { const { key, store } = opts; - const namespace = getNamespaceKey(key).replace('root', ''); + const namespace = posix.dirname(key).replace('.', ''); let { parent } = opts; - let model = tryCatchSync(() => store.modelFor(stripNamespaces(key))); + let model = tryCatchSync(() => store.modelFor(posix.basename(key))); if (!model) { model = null; @@ -23,7 +24,6 @@ export default function createSerializer>( } const instance: T = new constructor({ - // $FlowIgnore model, parent, namespace diff --git a/src/packages/cli/commands/index.js b/src/packages/cli/commands/index.js new file mode 100644 index 00000000..d3c55a87 --- /dev/null +++ b/src/packages/cli/commands/index.js @@ -0,0 +1,13 @@ +// @flow +export { build } from './build.js'; +export { create } from './create.js'; +export { dbcreate } from './dbcreate.js'; +export { dbdrop } from './dbdrop.js'; +export { dbmigrate } from './dbmigrate.js'; +export { dbrollback } from './dbrollback.js'; +export { dbseed } from './dbseed.js'; +export { destroy } from './destroy.js'; +export { generate } from './generate.js'; +export { repl } from './repl.js'; +export { serve } from './serve.js'; +export { test } from './test.js'; diff --git a/src/packages/cli/generator/utils/generate-type.js b/src/packages/cli/generator/utils/generate-type.js index 5159a1d5..d3bdd67e 100644 --- a/src/packages/cli/generator/utils/generate-type.js +++ b/src/packages/cli/generator/utils/generate-type.js @@ -1,11 +1,10 @@ // @flow -import { join as joinPath } from 'path'; +import { posix, join as joinPath } from 'path'; import { green } from 'chalk'; import { pluralize, singularize } from 'inflection'; import { NAMESPACED_RESOURCE_MESSAGE } from '../constants'; -import { stripNamespaces, getNamespaceKey } from '../../../loader'; import { generateTimestamp } from '../../../database'; import { exists, readFile, writeFile } from '../../../fs'; import modelTemplate from '../../templates/model'; @@ -45,9 +44,9 @@ export async function controller(opts: Generator$opts): Promise { name }); - const namespace = getNamespaceKey(name); + const namespace = posix.dirname(name); - if (namespace !== 'root') { + if (namespace !== '.') { const hasParent = await exists( joinPath(cwd, dir, ...[...namespace.split('/'), 'application.js']) ); @@ -86,9 +85,9 @@ export async function serializer(opts: Generator$opts): Promise { name }); - const namespace = getNamespaceKey(name); + const namespace = posix.dirname(name); - if (namespace !== 'root') { + if (namespace !== '.') { const hasParent = await exists( joinPath(cwd, dir, ...[...namespace.split('/'), 'application.js']) ); @@ -119,7 +118,7 @@ export function migration(opts: Generator$opts) { }); name = chain(name) - .pipe(stripNamespaces) + .pipe(posix.basename) .pipe(str => `${generateTimestamp()}-${str}`) .value(); @@ -149,7 +148,7 @@ export function modelMigration(opts: Generator$opts) { }); name = chain(name) - .pipe(stripNamespaces) + .pipe(posix.basename) .pipe(pluralize) .pipe(str => `${generateTimestamp()}-create-${str}`) .value(); @@ -178,7 +177,7 @@ export async function model(opts: Generator$opts): Promise { await modelMigration({ name, ...opts }); name = chain(name) - .pipe(stripNamespaces) + .pipe(posix.basename) .pipe(singularize) .value(); @@ -236,30 +235,31 @@ export async function resource(opts: Generator$opts) { await controller(opts); await serializer(opts); - if (getNamespaceKey(opts.name) !== 'root') { + if (posix.dirname(opts.name) !== '.') { log(NAMESPACED_RESOURCE_MESSAGE); - } else { - const path = joinPath(opts.cwd, 'app', 'routes.js'); - const routes = chain(await readFile(path)) - .pipe(buf => buf.toString('utf8')) - .pipe(str => str.split('\n')) - .pipe(lines => lines.reduce((result, line, index, arr) => { - const closeIndex = arr.lastIndexOf('}'); - let str = result; - - if (line && index <= closeIndex) { - str += `${line}\n`; - } - - if (index + 1 === closeIndex) { - str += ` this.resource('${pluralize(opts.name)}');\n`; - } - - return str; - }, '')) - .value(); - - await writeFile(path, routes); - log(`${green('update')} app/routes.js`); + return; } + + const path = joinPath(opts.cwd, 'app', 'routes.js'); + const routes = chain(await readFile(path)) + .pipe(buf => buf.toString('utf8')) + .pipe(str => str.split('\n')) + .pipe(lines => lines.reduce((result, line, index, arr) => { + const closeIndex = arr.lastIndexOf('}'); + let str = result; + + if (line && index <= closeIndex) { + str += `${line}\n`; + } + + if (index + 1 === closeIndex) { + str += ` this.resource('${pluralize(opts.name)}');\n`; + } + + return str; + }, '')) + .value(); + + await writeFile(path, routes); + log(`${green('update')} app/routes.js`); } diff --git a/src/packages/cli/templates/model-migration.js b/src/packages/cli/templates/model-migration.js index 17d85ca1..f98b1c72 100644 --- a/src/packages/cli/templates/model-migration.js +++ b/src/packages/cli/templates/model-migration.js @@ -58,11 +58,8 @@ export default (name: string, attrs: Array | string): string => { ${body} table.timestamps(); - table.index([ - 'id', - 'created_at', - 'updated_at' - ]); + table.index('created_at'); + table.index('updated_at'); }); } diff --git a/src/packages/compiler/index.js b/src/packages/compiler/index.js index 6bfc3796..3b56447b 100644 --- a/src/packages/compiler/index.js +++ b/src/packages/compiler/index.js @@ -1,48 +1,43 @@ // @flow -import path from 'path'; +import os from 'os'; +import path, { posix } from 'path'; +import lux from 'rollup-plugin-lux'; import json from 'rollup-plugin-json'; import alias from 'rollup-plugin-alias'; import babel from 'rollup-plugin-babel'; import eslint from 'rollup-plugin-eslint'; -import nodeResolve from 'rollup-plugin-node-resolve'; +import resolve from 'rollup-plugin-node-resolve'; import { rollup } from 'rollup'; -import { IS_PRODUCTION } from '../../constants'; -import { rmrf, exists, readdir, readdirRec, readFile, isJSFile } from '../fs'; +import { rmrf, readdir, readdirRec, isJSFile } from '../fs'; import template from '../template'; -import uniq from '../../utils/uniq'; -import normalizePath from './utils/normalize-path'; +import onwarn from './utils/handle-warning'; +import isExternal from './utils/is-external'; import createManifest from './utils/create-manifest'; import createBootScript from './utils/create-boot-script'; -import { createRollupConfig, createBundleConfig } from './utils/config'; /** * @private */ -export async function compile(dir: string, env: string, { - useStrict = false -}: { - useStrict: boolean -} = {}): Promise { - let banner; +type CompileOptions = { + useStrict?: boolean; +}; +/** + * @private + */ +export async function compile( + dir: string, + env: string, + opts: CompileOptions = {} +): Promise { + const { useStrict = false } = opts; const local = path.join(__dirname, '..', 'src', 'index.js'); const entry = path.join(dir, 'dist', 'index.js'); - - const nodeModules = path.join(dir, 'node_modules'); - const luxNodeModules = path.join(__dirname, '..', 'node_modules'); - let external = await readdir(nodeModules).then(files => ( - files.filter(name => name !== 'lux-framework') - )); - - if (await exists(luxNodeModules)) { - external = uniq([ - ...external, - ...(await readdir(luxNodeModules)) - ]); - } + const external = isExternal(dir); + let banner; const assets = await Promise.all([ readdir(path.join(dir, 'app', 'models')), @@ -75,11 +70,7 @@ export async function compile(dir: string, env: string, { ]); }); - const [babelrc] = await Promise.all([ - readFile( - path.join(dir, '.babelrc'), - 'utf8' - ), + await Promise.all([ createManifest(dir, assets, { useStrict }), @@ -88,39 +79,48 @@ export async function compile(dir: string, env: string, { }) ]); - const bundle = await rollup( - createRollupConfig({ - entry, - external, - plugins: [ - alias({ - resolve: ['.js'], - app: normalizePath(path.join(dir, 'app')), - LUX_LOCAL: normalizePath(local) - }), - json(), - nodeResolve({ preferBuiltins: true }), - eslint({ - cwd: dir, - parser: 'babel-eslint', - useEslintrc: false, - include: [ - path.join(dir, 'app', '**'), - ], - exclude: [ - path.join(dir, 'package.json'), - path.join(__dirname, '..', 'src', '**') - ] - }), - babel({ - babelrc: false, - minified: IS_PRODUCTION, - comments: false, - ...JSON.parse(babelrc.toString()) - }) - ] - }) - ); + const aliases = { + app: posix.join('/', ...dir.split(path.sep), 'app'), + LUX_LOCAL: posix.join('/', ...local.split(path.sep)) + }; + + if (os.platform() === 'win32') { + const [volume] = dir; + const prefix = `${volume}:/`; + + Object.assign(aliases, { + app: aliases.app.replace(prefix, ''), + LUX_LOCAL: aliases.LUX_LOCAL.replace(prefix, '') + }); + } + + const bundle = await rollup({ + entry, + onwarn, + external, + plugins: [ + alias({ + resolve: ['.js'], + ...aliases + }), + json(), + resolve(), + eslint({ + cwd: dir, + parser: 'babel-eslint', + useEslintrc: false, + include: [ + path.join(dir, 'app', '**'), + ], + exclude: [ + path.join(dir, 'package.json'), + path.join(__dirname, '..', 'src', '**') + ] + }), + babel(), + lux(path.resolve(path.sep, dir, 'app')) + ] + }); await rmrf(entry); @@ -131,15 +131,16 @@ export async function compile(dir: string, env: string, { `; if (useStrict) { - banner = `\n'use strict';\n\n${banner}`; + banner = `'use strict';\n\n${banner}`; } - return bundle.write( - createBundleConfig({ - banner, - dest: path.join(dir, 'dist', 'bundle.js') - }) - ); + return bundle.write({ + banner, + dest: path.join(dir, 'dist', 'bundle.js'), + format: 'cjs', + sourceMap: true, + useStrict: false + }); } -export { createRollupConfig, createBundleConfig } from './utils/config'; +export { default as onwarn } from './utils/handle-warning'; diff --git a/src/packages/compiler/test/compiler.test.js b/src/packages/compiler/test/compiler.test.js new file mode 100644 index 00000000..8beb264b --- /dev/null +++ b/src/packages/compiler/test/compiler.test.js @@ -0,0 +1,86 @@ +// @flow +import path from 'path'; + +import * as Rollup from 'rollup'; +import { spy, stub } from 'sinon'; +import { expect } from 'chai'; +import { it, describe, afterEach, beforeEach } from 'mocha'; + +import { getTestApp } from '../../../../test/utils/get-test-app'; +import { compile, onwarn } from '../index'; + +describe('module "compiler"', () => { + describe('#compile()', () => { + let rollupStub; + + beforeEach(() => { + rollupStub = stub(Rollup, 'rollup', () => ({ + write: () => Promise.resolve() + })); + }); + + afterEach(() => { + rollupStub.restore(); + }); + + ['use strict', 'use weak'].forEach(opt => { + describe(`- ${opt}`, () => { + it('creates an instance of rollup with the correct config', async () => { + const { path: dir } = await getTestApp(); + const entry = path.join(dir, 'dist', 'index.js') + + await compile(dir, 'test', { + useStrict: opt === 'use strict' + }); + + const { args: [rollupConfig] } = rollupStub.getCall(0); + + expect(rollupConfig).to.have.property('entry', entry); + expect(rollupConfig) + .to.have.property('plugins') + .and.be.an('array') + .with.lengthOf(6); + }); + }); + }); + }); + + describe('#onwarn()', () => { + let warnSpy; + const warnings = { + EMPTY_BUNDLE: { + code: 'EMPTY_BUNDLE', + message: 'Generated an empty bundle' + }, + UNUSED_EXTERNAL_IMPORT: { + code: 'UNUSED_EXTERNAL_IMPORT', + message: ( + `'unused', 'notused' and 'neverused' are imported from external` + + `module 'external' but never used` + ) + } + }; + + beforeEach(() => { + warnSpy = spy(console, 'warn'); + }); + + afterEach(() => { + warnSpy.restore(); + }); + + it('outputs valid warning types to stderr', () => { + onwarn(warnings.EMPTY_BUNDLE); + expect( + warnSpy.calledWithExactly(warnings.EMPTY_BUNDLE.message) + ).to.be.true; + }); + + it('ignores invalid warning types', () => { + onwarn(warnings.UNUSED_EXTERNAL_IMPORT); + expect( + warnSpy.neverCalledWith(warnings.UNUSED_EXTERNAL_IMPORT.message) + ).to.be.true; + }); + }); +}); diff --git a/src/packages/compiler/test/format-name.test.js b/src/packages/compiler/test/format-name.test.js new file mode 100644 index 00000000..19e4ae00 --- /dev/null +++ b/src/packages/compiler/test/format-name.test.js @@ -0,0 +1,49 @@ +// @flow +import path from 'path'; + +import { expect } from 'chai'; +import { it, describe, beforeEach } from 'mocha'; + +import { getTestApp } from '../../../../test/utils/get-test-app'; +import formatName from '../utils/format-name'; + +describe('module "compiler"', () => { + describe('util formatName()', () => { + let keys: Array; + + beforeEach(async () => { + const { controllers } = await getTestApp(); + + keys = Array.from(controllers.keys()); + }); + + it('transforms an array of keys into identifiers', () => { + expect(keys.map(formatName).sort()).to.deep.equal([ + 'Actions', + 'Admin$Actions', + 'Admin$Application', + 'Admin$Categorizations', + 'Admin$Comments', + 'Admin$Friendships', + 'Admin$Images', + 'Admin$Notifications', + 'Admin$Posts', + 'Admin$Reactions', + 'Admin$Tags', + 'Admin$Users', + 'Application', + 'Categorizations', + 'Comments', + 'Custom', + 'Friendships', + 'Health', + 'Images', + 'Notifications', + 'Posts', + 'Reactions', + 'Tags', + 'Users' + ]); + }); + }); +}); diff --git a/src/packages/compiler/test/is-external.test.js b/src/packages/compiler/test/is-external.test.js new file mode 100644 index 00000000..c17bda3e --- /dev/null +++ b/src/packages/compiler/test/is-external.test.js @@ -0,0 +1,58 @@ +// @flow +import path from 'path'; + +import { spy } from 'sinon'; +import { expect } from 'chai'; +import { it, describe, beforeEach } from 'mocha'; + +import isExternal from '../utils/is-external'; + +const SRC = path.join(__dirname, '..', '..', '..'); + +describe('module "compiler"', () => { + describe('util isExternal()', () => { + it('returns a function that accepts a single argument', () => { + expect(isExternal(SRC)).to.be.a('function').with.lengthOf(1); + }); + + describe('external()', () => { + let external: (id: string) => boolean; + + beforeEach(() => { + external = isExternal(SRC); + }); + + it('returns `true` for external modules', () => { + expect(external('knex')).to.be.true; + }); + + it('returns `false` for aliased file paths', () => { + expect(external('app/models/user')).to.be.false; + }); + + it('returns `false` for absolute file paths', () => { + expect(external('/absolute/path/to/app/models/user')).to.be.false; + expect(external('C:/absolute/path/to/app/models/user')).to.be.false; + expect(external( + 'C:\\absolute\\path\\to\\app\\models\\user' + )).to.be.false; + }); + + it('returns `false` for relative file paths', () => { + expect(external('./app/models/user')).to.be.false; + }); + + it('returns `false` for "LUX_LOCAL"', () => { + expect(external('LUX_LOCAL')).to.be.false; + }); + + it('returns `false` for "lux-framework"', () => { + expect(external('lux-framework')).to.be.false; + }); + + it('returns `false` for "babelHelpers"', () => { + expect(external('babelHelpers')).to.be.false; + }); + }); + }); +}); diff --git a/src/packages/compiler/utils/create-manifest.js b/src/packages/compiler/utils/create-manifest.js index b752f5da..52a086ee 100644 --- a/src/packages/compiler/utils/create-manifest.js +++ b/src/packages/compiler/utils/create-manifest.js @@ -1,5 +1,5 @@ // @flow -import { join as joinPath } from 'path'; +import { sep, posix, basename, join as joinPath } from 'path'; import { camelize, capitalize, pluralize } from 'inflection'; @@ -9,9 +9,7 @@ import tryCatch from '../../../utils/try-catch'; import underscore from '../../../utils/underscore'; import { compose } from '../../../utils/compose'; -import stripExt from './strip-ext'; import formatName from './format-name'; -import normalizePath from './normalize-path'; /** * @private @@ -21,7 +19,7 @@ function createExportStatement( path: string, isDefault: boolean = true ): string { - const normalized = normalizePath(path); + const normalized = posix.join(...path.split(sep)); if (isDefault) { return `export {\n default as ${name}\n} from '../${normalized}';\n\n`; @@ -68,7 +66,7 @@ function createWriter(file: string) { migrations: writerFor('migration', async (item) => { const path = joinPath('db', 'migrate', item); const name = chain(item) - .pipe(stripExt) + .pipe(str => basename(str, '.js')) .pipe(underscore) .pipe(str => str.substr(17)) .pipe(str => camelize(str, true)) diff --git a/src/packages/compiler/utils/format-name.js b/src/packages/compiler/utils/format-name.js index 04793a8f..c48ddd80 100644 --- a/src/packages/compiler/utils/format-name.js +++ b/src/packages/compiler/utils/format-name.js @@ -1,30 +1,24 @@ // @flow +import { posix, dirname, basename } from 'path'; + import { camelize } from 'inflection'; -import chain from '../../../utils/chain'; import underscore from '../../../utils/underscore'; - -import stripExt from './strip-ext'; -import normalizePath from './normalize-path'; +import { compose } from '../../../utils/compose'; const DOUBLE_COLON = /::/g; /** * @private */ -function applyNamespace(source: string) { - return source.replace(DOUBLE_COLON, '$'); -} +const formatName: (source: string) => string = compose( + (name: string) => name.replace(DOUBLE_COLON, '$'), + camelize, + underscore, + (name: string) => posix.join( + dirname(name), + basename(name, '.js') + ) +); -/** - * @private - */ -export default function formatName(source: string) { - return chain(source) - .pipe(normalizePath) - .pipe(stripExt) - .pipe(underscore) - .pipe(camelize) - .pipe(applyNamespace) - .value(); -} +export default formatName; diff --git a/src/packages/compiler/utils/handle-warning.js b/src/packages/compiler/utils/handle-warning.js index d582fea9..46409777 100644 --- a/src/packages/compiler/utils/handle-warning.js +++ b/src/packages/compiler/utils/handle-warning.js @@ -3,8 +3,18 @@ /** * @private */ -export default function handleWarnings(...warnings: Array): void { - warnings - .filter(warning => warning.indexOf('external dependency') < 0) - .forEach(warning => process.stderr.write(`${warning}\n`)); +type CompilerWarning = { + code: string; + message: string; +}; + +/** + * @private + */ +export default function handleWarning(warning: CompilerWarning): void { + if (warning.code === 'UNUSED_EXTERNAL_IMPORT') { + return; + } + // eslint-disable-next-line no-console + console.warn(warning.message); } diff --git a/src/packages/compiler/utils/is-external.js b/src/packages/compiler/utils/is-external.js new file mode 100644 index 00000000..6af6b043 --- /dev/null +++ b/src/packages/compiler/utils/is-external.js @@ -0,0 +1,20 @@ +// @flow +import path from 'path'; + +/** + * @private + */ +export default function isExternal(dir: string): (id: string) => boolean { + return (id: string): boolean => !( + id.startsWith('.') + || id.endsWith('lux-framework') + || id.startsWith('/') // Absolute path on Unix + || /^[A-Z]:[\\/]/.test(id) // Absolute path on Windows + || id.startsWith('app') + || id.startsWith(path.join(dir, 'app')) + || id.startsWith(path.join(dir, 'dist')) + || id === 'LUX_LOCAL' + || id === 'babelHelpers' + || id === '\u0000babelHelpers' + ); +} diff --git a/src/packages/compiler/utils/normalize-path.js b/src/packages/compiler/utils/normalize-path.js deleted file mode 100644 index a4a0621c..00000000 --- a/src/packages/compiler/utils/normalize-path.js +++ /dev/null @@ -1,9 +0,0 @@ -// @flow -import { BACKSLASH } from '../../../constants'; - -/** - * @private - */ -export default function normalizePath(source: string): string { - return source.replace(BACKSLASH, '/'); -} diff --git a/src/packages/compiler/utils/strip-ext.js b/src/packages/compiler/utils/strip-ext.js deleted file mode 100644 index beba362f..00000000 --- a/src/packages/compiler/utils/strip-ext.js +++ /dev/null @@ -1,9 +0,0 @@ -// @flow -const EXT_PATTERN = /\.js$/; - -/** - * @private - */ -export default function stripExt(source: string): string { - return source.replace(EXT_PATTERN, ''); -} diff --git a/src/packages/controller/index.js b/src/packages/controller/index.js index 6ef35c8b..fc0cc0f0 100644 --- a/src/packages/controller/index.js +++ b/src/packages/controller/index.js @@ -1,19 +1,43 @@ // @flow -import { Model } from '../database'; +// eslint-disable-next-line no-unused-vars +import { Model, Query } from '../database'; +import { line } from '../logger'; import { getDomain } from '../server'; import { freezeProps } from '../freezeable'; import type Serializer from '../serializer'; -import type { Query } from '../database'; // eslint-disable-line max-len, no-duplicate-imports -import type { Request, Response } from '../server'; // eslint-disable-line max-len, no-duplicate-imports +// eslint-disable-next-line no-duplicate-imports +import type { Request, Response } from '../server'; +import type { Thenable } from '../../interfaces'; import findOne from './utils/find-one'; import findMany from './utils/find-many'; import resolveRelationships from './utils/resolve-relationships'; -import type { - Controller$opts, - Controller$beforeAction, - Controller$afterAction -} from './interfaces'; + +/** + * @private + */ +export type BeforeAction = ( + request: Request, + response: Response +) => Promise; + +/** + * @private + */ +export type AfterAction = ( + request: Request, + response: Response, + responseData: any +) => Promise; + +/** + * @private + */ +export type Options = { + model?: ?Class; + namespace?: string; + serializer?: Serializer; +}; /** * ## Overview @@ -274,7 +298,7 @@ import type { * @class Controller * @public */ -class Controller { +class Controller { /** * An array of custom query parameter keys that are allowed to reach a * Controller instance from an incoming `HTTP` request. @@ -397,7 +421,7 @@ class Controller { * @default [] * @public */ - beforeAction: Array = []; + beforeAction: Array = []; /** * Functions to execute on each request handled by a `Controller` after the @@ -448,7 +472,7 @@ class Controller { * @default [] * @public */ - afterAction: Array = []; + afterAction: Array = []; /** * The default amount of items to include per each response of the index @@ -468,7 +492,7 @@ class Controller { * @type {Model} * @private */ - model: Class; + model: ?Class; /** * A reference to the root Controller for the namespace that a Controller @@ -478,7 +502,7 @@ class Controller { * @type {?Controller} * @private */ - parent: ?Controller; + parent: ?Controller<*>; /** * The namespace that a Controller instance is a member of. @@ -506,7 +530,7 @@ class Controller { * @type {Map} * @private */ - controllers: Map; + controllers: Map>; /** * A boolean value representing whether or not a Controller instance has a @@ -538,7 +562,7 @@ class Controller { */ hasSerializer: boolean; - constructor({ model, namespace, serializer }: Controller$opts) { + constructor({ model, namespace, serializer }: Options) { Object.assign(this, { model, namespace, @@ -573,8 +597,14 @@ class Controller { * @return {Promise} Resolves with an array of Model instances. * @public */ - index(req: Request): Query> { - return findMany(this.model, req); + index(request: Request): Thenable> { + const { model } = this; + + if (model) { + return findMany(model, request); + } + + return Query.resolve([]); } /** @@ -589,8 +619,14 @@ class Controller { * id url parameter. * @public */ - show(req: Request): Query { - return findOne(this.model, req); + show(request: Request): Thenable { + const { model } = this; + + if (model) { + return findOne(model, request); + } + + return Promise.resolve(null); } /** @@ -604,9 +640,15 @@ class Controller { * @return {Promise} Resolves with the newly created Model instance. * @public */ - async create(req: Request, res: Response): Promise { + async create(req: Request, res: Response): Promise { const { model } = this; + if (!model) { + throw new Error(line` + Controllers without a Model must override the built in "create" action. + `); + } + const { url: { pathname @@ -646,32 +688,36 @@ class Controller { * Resolves with the number `204` if no changes occur. * @public */ - update(req: Request): Promise { + update(request: Request): Promise { const { model } = this; - return findOne(model, req) - .then(record => { - const { - params: { - data: { - attributes, - relationships + if (model) { + return findOne(model, request) + .then(record => { + const { + params: { + data: { + attributes, + relationships + } } + } = request; + + return record.update({ + ...attributes, + ...resolveRelationships(model, relationships) + }); + }) + .then(record => { + if (record.didPersist) { + return record.unwrap(); } - } = req; - return record.update({ - ...attributes, - ...resolveRelationships(model, relationships) + return 204; }); - }) - .then(record => { - if (record.didPersist) { - return record.unwrap(); - } + } - return 204; - }); + return Promise.resolve(null); } /** @@ -685,10 +731,16 @@ class Controller { * @return {Promise} Resolves with the number `204`. * @public */ - destroy(req: Request): Promise { - return findOne(this.model, req) - .then(record => record.destroy()) - .then(() => 204); + destroy(request: Request): Promise { + const { model } = this; + + if (model) { + return findOne(model, request) + .then(record => record.destroy()) + .then(() => 204); + } + + return Promise.resolve(null); } /** diff --git a/src/packages/controller/test/controller.test.js b/src/packages/controller/test/controller.test.js index b0191cec..a8ef38eb 100644 --- a/src/packages/controller/test/controller.test.js +++ b/src/packages/controller/test/controller.test.js @@ -16,7 +16,7 @@ const HOST = 'localhost:4000'; describe('module "controller"', () => { describe('class Controller', () => { let Post: Class; - let subject: Controller; + let subject: Controller<*>; const attributes = [ 'id', @@ -37,15 +37,16 @@ describe('module "controller"', () => { before(async () => { const app = await getTestApp(); + const model = app.models.get('post'); - // $FlowIgnore - Post = app.models.get('post'); + if (model) { + Post = model; + } subject = new Controller({ model: Post, namespace: '', serializer: new Serializer({ - // $FlowIgnore model: Post, parent: null, namespace: '' @@ -75,58 +76,65 @@ describe('module "controller"', () => { } }); - it('returns an array of records', async () => { - const request = createRequest(); - const result = await subject.index(request); - - expect(result).to.be.an('array').with.lengthOf(25); - result.forEach(item => assertRecord(item)); + it('returns an array of records', () => { + return subject + .index(createRequest()) + .then(result => { + expect(result).to.be.an('array').with.lengthOf(25); + result.forEach(item => assertRecord(item)); + }); }); - it('supports specifying page size', async () => { + it('supports specifying page size', () => { const request = createRequest({ page: { size: 10 } }); - const result = await subject.index(request); - - expect(result).to.be.an('array').with.lengthOf(10); - result.forEach(item => assertRecord(item)); + return subject + .index(request) + .then(result => { + expect(result).to.be.an('array').with.lengthOf(10); + result.forEach(item => assertRecord(item)); + }); }); - it('supports filter parameters', async () => { + it('supports filter parameters', () => { const request = createRequest({ filter: { isPublic: false } }); - const result = await subject.index(request); - - expect(result).to.be.an('array').with.length.above(0); + return subject + .index(request) + .then(result => { + expect(result).to.be.an('array').with.length.above(0); - result.forEach(item => { - assertRecord(item); - expect(item).to.have.property('isPublic', false); - }); + result.forEach(item => { + assertRecord(item); + expect(item).to.have.property('isPublic', false); + }); + }); }); - it('supports sparse field sets', async () => { + it('supports sparse field sets', () => { const request = createRequest({ fields: { posts: ['id', 'title'] } }); - const result = await subject.index(request); - - expect(result).to.be.an('array').with.lengthOf(25); - result.forEach(item => assertRecord(item, ['id', 'title'])); + return subject + .index(request) + .then(result => { + expect(result).to.be.an('array').with.lengthOf(25); + result.forEach(item => assertRecord(item, ['id', 'title'])); + }); }); - it('supports eager loading relationships', async () => { + it('supports eager loading relationships', () => { const request = createRequest({ include: ['user'], fields: { @@ -138,21 +146,23 @@ describe('module "controller"', () => { } }); - const result = await subject.index(request); - - expect(result).to.be.an('array').with.lengthOf(25); - - result.forEach(item => { - assertRecord(item, [ - ...attributes, - 'user' - ]); - - expect(item.rawColumnData.user).to.have.all.keys([ - 'id', - 'name', - 'email' - ]); + return subject + .index(request) + .then(result => { + expect(result).to.be.an('array').with.lengthOf(25); + + result.forEach(item => { + assertRecord(item, [ + ...attributes, + 'user' + ]); + + expect(item.rawColumnData.user).to.have.all.keys([ + 'id', + 'name', + 'email' + ]); + }); }); }); }); @@ -204,7 +214,7 @@ describe('module "controller"', () => { assertRecord(result, ['id', 'title']); }); - it('supports eager loading relationships', async () => { + it('supports eager loading relationships', () => { const request = createRequest({ id: 1, include: ['user'], @@ -217,22 +227,24 @@ describe('module "controller"', () => { } }); - const result = await subject.show(request); - - expect(result).to.be.ok; - - if (result) { - assertRecord(result, [ - ...attributes, - 'user' - ]); - - expect(result.rawColumnData.user).to.have.all.keys([ - 'id', - 'name', - 'email' - ]); - } + return subject + .show(request) + .then(result => { + expect(result).to.be.ok; + + if (result) { + assertRecord(result, [ + ...attributes, + 'user' + ]); + + expect(result.rawColumnData.user).to.have.all.keys([ + 'id', + 'name', + 'email' + ]); + } + }); }); }); @@ -302,11 +314,8 @@ describe('module "controller"', () => { 'updatedAt' ]); - // $FlowIgnore const user = await result.user; - // $FlowIgnore const title = result.title; - // $FlowIgnore const isPublic = result.isPublic; expect(user.id).to.equal(1); diff --git a/src/packages/controller/utils/find-many.js b/src/packages/controller/utils/find-many.js index 9e06da2f..7c2a1dd0 100644 --- a/src/packages/controller/utils/find-many.js +++ b/src/packages/controller/utils/find-many.js @@ -1,7 +1,9 @@ // @flow import merge from '../../../utils/merge'; -import type { Model, Query } from '../../database'; +// eslint-disable-next-line no-unused-vars +import type { Model } from '../../database'; import type { Request } from '../../server'; +import type { Thenable } from '../../../interfaces'; import paramsToQuery from './params-to-query'; @@ -10,9 +12,9 @@ import paramsToQuery from './params-to-query'; */ export default function findMany( model: Class, - req: Request -): Query> { - const params = merge(req.defaultParams, req.params); + request: Request +): Thenable> { + const params = merge(request.defaultParams, request.params); const { sort, page, diff --git a/src/packages/database/model/utils/persistence.js b/src/packages/database/model/utils/persistence.js index 39bdda28..be032238 100644 --- a/src/packages/database/model/utils/persistence.js +++ b/src/packages/database/model/utils/persistence.js @@ -10,7 +10,7 @@ import getColumns from './get-columns'; /** * @private */ -export function create(record: Model, trx: Object): [Object] { +export function create(record: Model, trx: Object): Array { const target = record; const timestamp = new Date(); @@ -36,7 +36,7 @@ export function create(record: Model, trx: Object): [Object] { /** * @private */ -export function update(record: Model, trx: Object): [Object] { +export function update(record: Model, trx: Object): Array { const target = record; target.updatedAt = new Date(); @@ -56,7 +56,7 @@ export function update(record: Model, trx: Object): [Object] { /** * @private */ -export function destroy(record: Model, trx: Object): [Object] { +export function destroy(record: Model, trx: Object): Array { return [ record.constructor .table() diff --git a/src/packages/database/query/index.js b/src/packages/database/query/index.js index a14934b5..b2dc6d4b 100644 --- a/src/packages/database/query/index.js +++ b/src/packages/database/query/index.js @@ -2,6 +2,7 @@ import { camelize } from 'inflection'; import entries from '../../../utils/entries'; +import uniq from '../../../utils/uniq'; import type Model from '../model'; import scopesFor from './utils/scopes-for'; @@ -27,7 +28,7 @@ class Query<+T: any> extends Promise { /** * @private */ - snapshots: Array<[string, mixed]>; + snapshots: Array>; /** * @private @@ -162,12 +163,13 @@ class Query<+T: any> extends Promise { if (columnName) { this.snapshots = this.snapshots - .filter(([method]) => method !== 'orderBy') + .filter(([method]) => method !== 'orderByRaw') .concat([ - ['orderBy', [ - `${this.model.tableName}.${columnName}`, - direction - ]] + // eslint-disable-next-line prefer-template + ['orderByRaw', uniq([columnName, this.model.primaryKey]) + .map(key => `${this.model.tableName}.${key}`) + .join(', ') + ` ${direction}` + ] ]); } } @@ -222,7 +224,9 @@ class Query<+T: any> extends Promise { first(): this { if (!this.shouldCount) { - const willSort = this.snapshots.some(([method]) => method === 'orderBy'); + const willSort = this.snapshots.some( + ([method]) => method === 'orderByRaw' + ); this.collection = false; @@ -238,7 +242,9 @@ class Query<+T: any> extends Promise { last(): this { if (!this.shouldCount) { - const willSort = this.snapshots.some(([method]) => method === 'orderBy'); + const willSort = this.snapshots.some( + ([method]) => method === 'orderByRaw' + ); this.collection = false; @@ -399,7 +405,7 @@ class Query<+T: any> extends Promise { if (scopes.length) { const keys = scopes.map(scope => { if (scope === 'order') { - return 'orderBy'; + return 'orderByRaw'; } return scope; diff --git a/src/packages/database/query/runner/index.js b/src/packages/database/query/runner/index.js index 9d3e691f..515b0c87 100644 --- a/src/packages/database/query/runner/index.js +++ b/src/packages/database/query/runner/index.js @@ -49,7 +49,6 @@ export function createRunner(target: Query<*>, opts: { name = 'select'; } - // $FlowIgnore const method = query[name]; if (!Array.isArray(params)) { diff --git a/src/packages/database/query/runner/utils/get-find-param.js b/src/packages/database/query/runner/utils/get-find-param.js index 1ea6fa4f..97644b64 100644 --- a/src/packages/database/query/runner/utils/get-find-param.js +++ b/src/packages/database/query/runner/utils/get-find-param.js @@ -17,7 +17,6 @@ export default function getFindParam({ const [, params] = snapshot; if (params && isObject(params)) { - // $FlowIgnore return params[`${tableName}.${primaryKey}`]; } } diff --git a/src/packages/database/test/query.test.js b/src/packages/database/test/query.test.js index b8918006..da6cbcb0 100644 --- a/src/packages/database/test/query.test.js +++ b/src/packages/database/test/query.test.js @@ -334,7 +334,7 @@ describe('module "database/query"', () => { const result = subject.order('id', 'DESC'); expect(result.snapshots).to.deep.equal([ - ['orderBy', ['posts.id', 'DESC']] + ['orderByRaw', 'posts.id DESC'] ]); }); @@ -342,7 +342,7 @@ describe('module "database/query"', () => { const result = subject.order('id'); expect(result.snapshots).to.deep.equal([ - ['orderBy', ['posts.id', 'ASC']] + ['orderByRaw', 'posts.id ASC'] ]); }); @@ -439,7 +439,7 @@ describe('module "database/query"', () => { const result = subject.first(); expect(result.snapshots).to.deep.equal([ - ['orderBy', ['posts.id', 'ASC']], + ['orderByRaw', 'posts.id ASC'], ['limit', 1] ]); }); @@ -454,7 +454,7 @@ describe('module "database/query"', () => { const result = subject.order('createdAt', 'DESC').first(); expect(result.snapshots).to.deep.equal([ - ['orderBy', ['posts.created_at', 'DESC']], + ['orderByRaw', 'posts.created_at, posts.id DESC'], ['limit', 1] ]); }); @@ -493,7 +493,7 @@ describe('module "database/query"', () => { const result = subject.last(); expect(result.snapshots).to.deep.equal([ - ['orderBy', ['posts.id', 'DESC']], + ['orderByRaw', 'posts.id DESC'], ['limit', 1] ]); }); @@ -508,7 +508,7 @@ describe('module "database/query"', () => { const result = subject.order('createdAt', 'DESC').last(); expect(result.snapshots).to.deep.equal([ - ['orderBy', ['posts.created_at', 'DESC']], + ['orderByRaw', 'posts.created_at, posts.id DESC'], ['limit', 1] ]); }); diff --git a/src/packages/fs/index.js b/src/packages/fs/index.js index 34d4825d..030f8cf9 100644 --- a/src/packages/fs/index.js +++ b/src/packages/fs/index.js @@ -1,11 +1,10 @@ // @flow import fs from 'fs'; -import { join as joinPath, resolve as resolvePath } from 'path'; +import { basename, join as joinPath, resolve as resolvePath } from 'path'; import type { Stats } from 'fs'; // eslint-disable-line no-duplicate-imports import Watcher from './watcher'; import createResolver from './utils/create-resolver'; -import createPathRemover from './utils/create-path-remover'; import type { fs$readOpts, fs$writeOpts } from './interfaces'; export { default as rmrf } from './utils/rmrf'; @@ -89,8 +88,6 @@ export function readdirRec( path: string, opts?: fs$readOpts ): Promise> { - const stripPath = createPathRemover(path); - return readdir(path, opts) .then(files => Promise.all( files.map(file => { @@ -106,13 +103,13 @@ export function readdirRec( ])) )) .then(files => files.reduce((arr, [file, children]) => { - const basename = stripPath(file); + const name = basename(file); - return [ - ...arr, - basename, - ...children.map(child => joinPath(basename, stripPath(child))) - ]; + // eslint-disable-next-line no-param-reassign + arr[arr.length] = name; + return arr.concat( + children.map(child => joinPath(name, basename(child))) + ); }, [])); } diff --git a/src/packages/fs/test/exists.test.js b/src/packages/fs/test/exists.test.js index 98a1a798..75bd3c64 100644 --- a/src/packages/fs/test/exists.test.js +++ b/src/packages/fs/test/exists.test.js @@ -1,9 +1,9 @@ // @flow - +import { tmpdir } from 'os'; import { expect } from 'chai'; import { it, describe, before, after } from 'mocha'; -import { sep, basename, dirname, join } from 'path'; +import { basename, dirname, join } from 'path'; import { createTmpDir, @@ -13,11 +13,10 @@ import { import { exists } from '../index'; -const TMP_PATH = join(sep, 'tmp', `lux-${Date.now()}`); +const TMP_PATH = join(tmpdir(), `lux-${Date.now()}`); describe('module "fs"', () => { describe('#exists()', () => { - before(async () => { await createTmpDir(TMP_PATH); await createTmpFiles(TMP_PATH, 5); diff --git a/src/packages/fs/test/fs.test.js b/src/packages/fs/test/fs.test.js index 96c05263..9f1abba1 100644 --- a/src/packages/fs/test/fs.test.js +++ b/src/packages/fs/test/fs.test.js @@ -1,5 +1,6 @@ // @flow -import { sep, join } from 'path'; +import { tmpdir } from 'os'; +import { join } from 'path'; import { expect } from 'chai'; import { it, describe, before, after, beforeEach, afterEach } from 'mocha'; @@ -12,9 +13,8 @@ import * as fs from '../index'; import { createTmpDir, getTmpFile, createTmpFiles } from './utils'; describe('module "fs"', () => { - - let spies: {[ module: string ]: Spy } = {}; let tmpDirPath: string; + let spies: { [module: string]: Spy } = {}; const spiedMethods = [ 'mkdir', 'rmdir', @@ -45,7 +45,7 @@ describe('module "fs"', () => { }); beforeEach(async () => { - tmpDirPath = `/tmp/lux-${Date.now()}`; + tmpDirPath = join(tmpdir(), `lux-${Date.now()}`); await createTmpDir(tmpDirPath); }); @@ -162,7 +162,7 @@ describe('module "fs"', () => { }); describe('#watch()', () => { - const watchPath = join(sep, 'tmp', `lux-${Date.now()}`); + const watchPath = join(tmpdir(), `lux-${Date.now()}`); let result; before(async () => { diff --git a/src/packages/fs/test/rmrf.test.js b/src/packages/fs/test/rmrf.test.js index 7416622e..8ce201ff 100644 --- a/src/packages/fs/test/rmrf.test.js +++ b/src/packages/fs/test/rmrf.test.js @@ -1,9 +1,10 @@ // @flow +import { tmpdir } from 'os'; import { expect } from 'chai'; import { it, describe, beforeEach, afterEach } from 'mocha'; -import { sep, join } from 'path'; +import { join } from 'path'; import { rmrf, exists } from '../index'; import { @@ -18,7 +19,7 @@ describe('module "fs"', () => { let tmpDirPath: string; beforeEach(async () => { - tmpDirPath = join(sep, 'tmp', `lux-${Date.now()}`); + tmpDirPath = join(tmpdir(), `lux-${Date.now()}`); await createTmpDir(tmpDirPath); await createTmpFiles(tmpDirPath, 5); diff --git a/src/packages/fs/test/utils/create-tmp-dir.js b/src/packages/fs/test/utils/create-tmp-dir.js index c18a4b53..e3c81bfb 100644 --- a/src/packages/fs/test/utils/create-tmp-dir.js +++ b/src/packages/fs/test/utils/create-tmp-dir.js @@ -1,22 +1,15 @@ // @flow import { mkdir } from 'fs'; -import { sep, join, dirname } from 'path'; export default function createTmpDir(path: string) { - return createRootTmpDir(path) - .then(() => new Promise((resolve, reject) => { - mkdir(path, undefined, (err) => { - if (err) return reject(err); - resolve(); - }); - })); -} - - -function createRootTmpDir() { - return new Promise(resolve => { - const path = join(sep, 'tmp'); - - mkdir(dirname(path), undefined, () => resolve(path)); + return new Promise((resolve, reject) => { + mkdir(path, undefined, (err) => { + if (err) { + reject(err); + return; + } + + resolve(); + }); }); } diff --git a/src/packages/fs/test/utils/create-tmp-files.js b/src/packages/fs/test/utils/create-tmp-files.js index 141b455e..3d4bc1bb 100644 --- a/src/packages/fs/test/utils/create-tmp-files.js +++ b/src/packages/fs/test/utils/create-tmp-files.js @@ -1,22 +1,27 @@ // @flow - -import { writeFile } from 'fs'; import { join } from 'path'; +import { writeFile } from 'fs'; import range from '../../../../utils/range'; export default function createTmpFiles( dir: string, numberToCreate: number -) { - const filePaths = Array.from(range(1, numberToCreate)) +): Promise> { + const filePaths = Array + .from(range(1, numberToCreate)) .map(() => join(dir, `${Date.now()}.tmp`)); - return Promise.all(filePaths.map((filePath) => { - return new Promise((resolve, reject) => { - writeFile(filePath, '', (error) => { - if (error) return reject(error); - resolve(); + + return Promise.all(filePaths.map(filePath => ( + new Promise((resolve, reject) => { + writeFile(filePath, '', err => { + if (err) { + reject(err); + return; + } + + resolve(filePath); }); - }); - })); + }) + ))); } diff --git a/src/packages/fs/test/utils/get-tmp-file.js b/src/packages/fs/test/utils/get-tmp-file.js index 1aa0f519..a4aa03b0 100644 --- a/src/packages/fs/test/utils/get-tmp-file.js +++ b/src/packages/fs/test/utils/get-tmp-file.js @@ -1,12 +1,15 @@ // @flow - import { readdir } from 'fs'; import { join } from 'path'; -export default function getTmpFile (path: string) { +export default function getTmpFile(path: string) { return new Promise((resolve, reject) => { readdir(path, (err, files) => { - if (err) return reject(err); + if (err) { + reject(err); + return; + } + resolve(join(path, files[0])); }); }); diff --git a/src/packages/fs/test/utils/remove-tmp-dir.js b/src/packages/fs/test/utils/remove-tmp-dir.js index bc7d843d..308abf64 100644 --- a/src/packages/fs/test/utils/remove-tmp-dir.js +++ b/src/packages/fs/test/utils/remove-tmp-dir.js @@ -1,32 +1,44 @@ // @flow - -import { rmdir, readdir, unlink } from 'fs'; import { join } from 'path'; +import { rmdir, readdir, unlink } from 'fs'; -export default function removeTmpDir(path: string) { +export default function removeTmpDir(path: string): Promise { return new Promise((resolve, reject) => { readdir(path, (err, files) => { - if (err) return reject(err); - const filePaths = files.map(fileName => join(path, fileName)); - removeTmpFiles(filePaths) - .then(() => { - rmdir(path, (error) => { - if (error) reject(error); - resolve(); - }); - }) - .catch((error) => reject(error)); + if (err) { + reject(err); + return; + } + + resolve(files); }); - }); -} + }).then(files => ( + removeTmpFiles(files.map(name => join(path, name))) + )).then(() => ( + new Promise((resolve, reject) => { + rmdir(path, err => { + if (err) { + reject(err); + return; + } -function removeTmpFiles(filePaths: Array) { - return Promise.all(filePaths.map((filePath) => { - return new Promise((resolve, reject) => { - unlink(filePath, (err) => { - if (err) return reject(err); resolve(); }); - }); - })); + }) + )); +} + +function removeTmpFiles(paths: Array): Promise> { + return Promise.all(paths.map(path => ( + new Promise((resolve, reject) => { + unlink(path, err => { + if (err) { + reject(err); + return; + } + + resolve(path); + }); + }) + ))); } diff --git a/src/packages/fs/test/watcher.test.js b/src/packages/fs/test/watcher.test.js index 710920a2..ea4f56b6 100644 --- a/src/packages/fs/test/watcher.test.js +++ b/src/packages/fs/test/watcher.test.js @@ -1,5 +1,6 @@ // @flow -import { sep, join as joinPath } from 'path'; +import { tmpdir } from 'os'; +import { join as joinPath } from 'path'; import { expect } from 'chai'; import { it, describe, after, before } from 'mocha'; @@ -10,7 +11,7 @@ import { APPVEYOR } from '../../../constants'; import { rmrf, mkdirRec, writeFile } from '../index'; describe('module "fs"', () => { - const tmpDirPath = joinPath(sep, 'tmp', `lux-${Date.now()}`); + const tmpDirPath = joinPath(tmpdir(), `lux-${Date.now()}`); const tmpAppPath = joinPath(tmpDirPath, 'app'); before(async () => { diff --git a/src/packages/fs/utils/create-path-remover.js b/src/packages/fs/utils/create-path-remover.js deleted file mode 100644 index 34401956..00000000 --- a/src/packages/fs/utils/create-path-remover.js +++ /dev/null @@ -1,18 +0,0 @@ -// @flow -import { BACKSLASH, PLATFORM } from '../../../constants'; -import type { fs$PathRemover } from '../interfaces'; - -/** - * @private - */ -export default function createPathRemover(path: string): fs$PathRemover { - let pattern = new RegExp(`${path}(/)?(.+)`); - - if (PLATFORM.startsWith('win')) { - const sep = '\\\\'; - - pattern = new RegExp(`${path.replace(BACKSLASH, sep)}(${sep})?(.+)`); - } - - return source => source.replace(pattern, '$2'); -} diff --git a/src/packages/fs/utils/exists.js b/src/packages/fs/utils/exists.js index bc1e4bb2..e031227b 100644 --- a/src/packages/fs/utils/exists.js +++ b/src/packages/fs/utils/exists.js @@ -1,6 +1,5 @@ // @flow import { stat, readdir } from '../index'; -import tryCatch from '../../../utils/try-catch'; /** * @private @@ -20,7 +19,8 @@ export default async function exists( return files.some(file => pattern.test(file)); } - const str = path; - - return Boolean(await tryCatch(() => stat(str))); + return stat(path).then( + () => true, + () => false + ); } diff --git a/src/packages/fs/utils/is-js-file.js b/src/packages/fs/utils/is-js-file.js index 8612def0..99565f06 100644 --- a/src/packages/fs/utils/is-js-file.js +++ b/src/packages/fs/utils/is-js-file.js @@ -1,9 +1,9 @@ // @flow -const REGEXP = /^(?!\.).+\.js$/; +import { extname } from 'path'; /** * @private */ export default function isJSFile(target: string): boolean { - return REGEXP.test(target); + return extname(target) === '.js'; } diff --git a/src/packages/fs/utils/rmrf.js b/src/packages/fs/utils/rmrf.js index a0e52645..e0919afc 100644 --- a/src/packages/fs/utils/rmrf.js +++ b/src/packages/fs/utils/rmrf.js @@ -2,28 +2,33 @@ import path from 'path'; import { stat, rmdir, readdir, unlink } from '../index'; -import tryCatch from '../../../utils/try-catch'; /** * @private */ -async function rmrf(target: string): Promise { - const stats = await tryCatch(() => stat(target)); +function rmrf(target: string): Promise { + return stat(target) + .then(stats => { + if (stats && stats.isDirectory()) { + return readdir(target); + } else if (stats && stats.isFile()) { + return unlink(target).then(() => []); + } - if (stats && stats.isDirectory()) { - let files = await tryCatch(() => readdir(target)); + return []; + }) + .then(files => ( + Promise.all(files.map(file => rmrf(path.join(target, file)))) + )) + .then(() => rmdir(target)) + .catch(err => { + if (err.code === 'ENOENT') { + return Promise.resolve(); + } - if (files) { - files = files.map(file => rmrf(path.join(target, file))); - - await Promise.all(files); - await rmdir(target); - } - } else if (stats && stats.isFile()) { - await tryCatch(() => unlink(target)); - } - - return true; + return Promise.reject(err); + }) + .then(() => true); } export default rmrf; diff --git a/src/packages/loader/builder/utils/create-parent-builder.js b/src/packages/loader/builder/utils/create-parent-builder.js index e11c095e..6b9bf46f 100644 --- a/src/packages/loader/builder/utils/create-parent-builder.js +++ b/src/packages/loader/builder/utils/create-parent-builder.js @@ -1,5 +1,6 @@ // @flow -import { getParentKey } from '../../resolver'; +import { posix } from 'path'; + import type { Builder$Construct, Builder$ParentBuilder } from '../interfaces'; import sortByNamespace from './sort-by-namespace'; @@ -18,7 +19,7 @@ export default function createParentBuilder( if (key !== 'root') { grandparent = result.find(namespace => ( - namespace.key === getParentKey(key) + namespace.key === posix.dirname(key) )); if (grandparent) { diff --git a/src/packages/loader/index.js b/src/packages/loader/index.js index 9d0ddaba..c05dfcd0 100644 --- a/src/packages/loader/index.js +++ b/src/packages/loader/index.js @@ -18,12 +18,7 @@ export function createLoader(path: string): Loader { } export { build } from './builder'; -export { - stripNamespaces, - closestAncestor, - closestChild, - getParentKey as getNamespaceKey -} from './resolver'; +export { closestAncestor, closestChild } from './resolver'; export type { Loader, diff --git a/src/packages/loader/resolver/index.js b/src/packages/loader/resolver/index.js index b711aff5..f50fff43 100644 --- a/src/packages/loader/resolver/index.js +++ b/src/packages/loader/resolver/index.js @@ -42,7 +42,5 @@ export function resolve( }, new FreezeableMap()); } -export { default as getParentKey } from './utils/get-parent-key'; -export { default as stripNamespaces } from './utils/strip-namespaces'; export { default as closestAncestor } from './utils/closest-ancestor'; export { default as closestChild } from './utils/closest-child'; diff --git a/src/packages/loader/resolver/utils/closest-ancestor.js b/src/packages/loader/resolver/utils/closest-ancestor.js index d31d7fe3..d391d085 100644 --- a/src/packages/loader/resolver/utils/closest-ancestor.js +++ b/src/packages/loader/resolver/utils/closest-ancestor.js @@ -1,20 +1,29 @@ // @flow +import { posix } from 'path'; + import type { Bundle$Namespace } from '../../index'; export default function closestAncestor( source: Bundle$Namespace, key: string ): void | T { - const parts = key.split('/'); + const name = posix.basename(key); + let namespace = posix.dirname(key); + + if (namespace === '.') { + return source.get(name); + } + + namespace = posix.dirname(namespace); - if (parts.length > 2) { - const name = parts.pop(); - const part = `${parts.slice(0, parts.length - 1).join('/')}/${name}`; + const ancestor = source.get(posix.join(namespace, name)); - return source.get(part) || closestAncestor(source, part); - } else if (parts.length === 2) { - return source.get(parts.pop()); + if (ancestor) { + return ancestor; } - return undefined; + return closestAncestor( + source, + posix.join(posix.dirname(namespace), name) + ); } diff --git a/src/packages/loader/resolver/utils/closest-child.js b/src/packages/loader/resolver/utils/closest-child.js index 390bb75b..6eb43555 100644 --- a/src/packages/loader/resolver/utils/closest-child.js +++ b/src/packages/loader/resolver/utils/closest-child.js @@ -1,4 +1,6 @@ // @flow +import { posix } from 'path'; + import type { Bundle$Namespace } from '../../index'; export default function closestChild( @@ -7,7 +9,7 @@ export default function closestChild( ): void | T { const [[, result] = []] = Array .from(source) - .map(([path, value]) => [path.split('/').pop(), value]) + .map(([path, value]) => [posix.basename(path), value]) .filter(([resource]) => key === resource); return result; diff --git a/src/packages/loader/resolver/utils/get-parent-key.js b/src/packages/loader/resolver/utils/get-parent-key.js deleted file mode 100644 index ad1b768f..00000000 --- a/src/packages/loader/resolver/utils/get-parent-key.js +++ /dev/null @@ -1,11 +0,0 @@ -// @flow - -/** - * @private - */ -export default function getParentKey(source: string): string { - const parts = source.split('/'); - const parent = parts.slice(0, Math.max(parts.length - 1, 0)).join('/'); - - return parent || 'root'; -} diff --git a/src/packages/loader/resolver/utils/strip-namespaces.js b/src/packages/loader/resolver/utils/strip-namespaces.js deleted file mode 100644 index b6d7d1f8..00000000 --- a/src/packages/loader/resolver/utils/strip-namespaces.js +++ /dev/null @@ -1,9 +0,0 @@ -// @flow -import getParentKey from './get-parent-key'; - -/** - * @private - */ -export default function stripNamespaces(source: string): string { - return source.replace(`${getParentKey(source)}/`, ''); -} diff --git a/src/packages/router/definitions/context/utils/normalize-resource-args.js b/src/packages/router/definitions/context/utils/normalize-resource-args.js index 9228bc29..3f95d1bf 100644 --- a/src/packages/router/definitions/context/utils/normalize-resource-args.js +++ b/src/packages/router/definitions/context/utils/normalize-resource-args.js @@ -5,11 +5,7 @@ import type { Controller$builtIn } from '../../../../controller'; // eslint-disa /** * @private */ -export default function normalizeResourceArgs(args: [ - string, - { path: string, only: Array }, - Function -]): [{ +export default function normalizeResourceArgs(args: Array): [{ name: string, path: string, only: Array @@ -18,12 +14,18 @@ export default function normalizeResourceArgs(args: [ let [, opts, builder] = args; if (!opts) { - opts = {}; + opts = { + path: '', + only: undefined + }; } if (typeof opts === 'function') { builder = opts; - opts = {}; + opts = { + path: '', + only: undefined + }; } if (typeof builder !== 'function') { diff --git a/src/packages/router/definitions/test/normalize-resource-args.test.js b/src/packages/router/definitions/test/normalize-resource-args.test.js index 72056ecf..742100d8 100644 --- a/src/packages/router/definitions/test/normalize-resource-args.test.js +++ b/src/packages/router/definitions/test/normalize-resource-args.test.js @@ -9,7 +9,6 @@ import normalizeResourceArgs from '../context/utils/normalize-resource-args'; describe('module "router/definitions/context"', () => { describe('util normalizeResourceArgs()', () => { it('normalizes arguments with a name only', () => { - // $FlowIgnore const result = normalizeResourceArgs(['posts']); expect(result).to.be.an('array'); @@ -28,7 +27,6 @@ describe('module "router/definitions/context"', () => { }); it('normalizes arguments with a name and options', () => { - // $FlowIgnore const result = normalizeResourceArgs(['posts', { only: [ 'show', @@ -55,7 +53,6 @@ describe('module "router/definitions/context"', () => { }); it('normalizes arguments with a name and builder', () => { - // $FlowIgnore const result = normalizeResourceArgs(['posts', function () { return undefined; }]); @@ -76,7 +73,6 @@ describe('module "router/definitions/context"', () => { }); it('normalizes arguments with a name, options, and builder', () => { - // $FlowIgnore const result = normalizeResourceArgs(['posts', { only: [ 'show', diff --git a/src/packages/router/interfaces.js b/src/packages/router/interfaces.js index e9713193..9255ddc2 100644 --- a/src/packages/router/interfaces.js +++ b/src/packages/router/interfaces.js @@ -4,8 +4,8 @@ import type Controller from '../controller'; import type { FreezeableSet } from '../freezeable'; export type Router$opts = { - controller: Controller; - controllers: Map; + controller: Controller<*>; + controllers: Map>; routes(): void; }; @@ -19,6 +19,6 @@ export interface Router$Namespace extends FreezeableSet { path: string; isRoot: boolean; namespace: Router$Namespace; - controller: Controller; - controllers: Map; + controller: Controller<*>; + controllers: Map>; } diff --git a/src/packages/router/namespace/index.js b/src/packages/router/namespace/index.js index 1121b3b3..5bb5c421 100644 --- a/src/packages/router/namespace/index.js +++ b/src/packages/router/namespace/index.js @@ -19,9 +19,9 @@ class Namespace extends FreezeableSet { namespace: Router$Namespace; - controller: Controller; + controller: Controller<*>; - controllers: Map; + controllers: Map>; constructor({ name, diff --git a/src/packages/router/namespace/interfaces.js b/src/packages/router/namespace/interfaces.js index f5b23069..b58da2a2 100644 --- a/src/packages/router/namespace/interfaces.js +++ b/src/packages/router/namespace/interfaces.js @@ -6,6 +6,6 @@ export type Namespace$opts = { name: string; path: string; namespace?: Router$Namespace; - controller: Controller; - controllers: Map; + controller: Controller<*>; + controllers: Map>; }; diff --git a/src/packages/router/route/action/enhancers/resource.js b/src/packages/router/route/action/enhancers/resource.js index eff05f3b..8ab9683d 100644 --- a/src/packages/router/route/action/enhancers/resource.js +++ b/src/packages/router/route/action/enhancers/resource.js @@ -17,7 +17,7 @@ export default function resource(action: Action): Action { Promise.resolve(0) ]; - if (isIndex) { + if (isIndex && init[0] instanceof Query) { init[1] = Query.from(init[0]).count(); } diff --git a/src/packages/router/route/action/index.js b/src/packages/router/route/action/index.js index 4fa68843..458438dd 100644 --- a/src/packages/router/route/action/index.js +++ b/src/packages/router/route/action/index.js @@ -13,7 +13,7 @@ import type { Action } from './interfaces'; export function createAction( type: string, action: Action, - controller: Controller + controller: Controller<*> ): Array> { let controllerAction = action.bind(controller); diff --git a/src/packages/router/route/index.js b/src/packages/router/route/index.js index 6f1a5bba..66e08b08 100644 --- a/src/packages/router/route/index.js +++ b/src/packages/router/route/index.js @@ -28,7 +28,7 @@ class Route extends FreezeableSet> { handlers: Array>; - controller: Controller; + controller: Controller<*>; staticPath: string; diff --git a/src/packages/router/route/interfaces.js b/src/packages/router/route/interfaces.js index 5d48193e..a1d79dbd 100644 --- a/src/packages/router/route/interfaces.js +++ b/src/packages/router/route/interfaces.js @@ -12,5 +12,5 @@ export type Route$opts = { path: string; action: string; method: Request$method; - controller: Controller; + controller: Controller<*>; }; diff --git a/src/packages/router/route/params/index.js b/src/packages/router/route/params/index.js index f384ba3a..9914046e 100644 --- a/src/packages/router/route/params/index.js +++ b/src/packages/router/route/params/index.js @@ -69,7 +69,7 @@ export function defaultParamsFor({ controller }: { type: string; - controller: Controller + controller: Controller<*> }): Object { const { hasModel } = controller; diff --git a/src/packages/router/route/params/interfaces.js b/src/packages/router/route/params/interfaces.js index 49e5832e..7d3aaabd 100644 --- a/src/packages/router/route/params/interfaces.js +++ b/src/packages/router/route/params/interfaces.js @@ -7,7 +7,7 @@ import type { Lux$Collection } from '../../../../interfaces'; export type Params$opts = { type: Route$type; method: Request$method; - controller: Controller; + controller: Controller<*>; dynamicSegments: Array; }; diff --git a/src/packages/router/route/params/parameter-group/index.js b/src/packages/router/route/params/parameter-group/index.js index 23df4174..1f971ad0 100644 --- a/src/packages/router/route/params/parameter-group/index.js +++ b/src/packages/router/route/params/parameter-group/index.js @@ -20,7 +20,7 @@ class ParameterGroup extends FreezeableMap { sanitize: boolean; - constructor(contents: Array<[string, ParameterLike]>, { + constructor(contents: Array, { path, required, sanitize diff --git a/src/packages/router/route/params/test/parameter.test.js b/src/packages/router/route/params/test/parameter.test.js index 1b9875dd..422e45b8 100644 --- a/src/packages/router/route/params/test/parameter.test.js +++ b/src/packages/router/route/params/test/parameter.test.js @@ -139,6 +139,41 @@ describe('module "router/route/params"', () => { }); }); + describe('- type "date"', () => { + [true, false].forEach(required => { + describe(`- ${required ? 'required' : 'optional'}`, () => { + let value; + + beforeEach(() => { + value = new Date(); + subject = new Parameter({ + required, + type: 'date', + path: 'meta.test' + }); + }); + + if (required) { + it('fails when the value is null', () => { + expect(() => subject.validate(null)).to.throw(TypeError); + }); + } else { + it('passes when the value is null', () => { + expect(subject.validate(null)).to.be.null; + }); + } + + it('fails when there is a type mismatch', () => { + expect(() => subject.validate('test')).to.throw(TypeError); + }); + + it('returns the value when the type and value match', () => { + expect(subject.validate(value)).to.equal(value); + }); + }); + }); + }); + primitives.forEach(({ type, valid, falsy, invalid }) => { describe(`- type "${type}"`, () => { [true, false].forEach(required => { diff --git a/src/packages/router/route/params/utils/get-data-params.js b/src/packages/router/route/params/utils/get-data-params.js index 797a41bc..2a7bf69a 100644 --- a/src/packages/router/route/params/utils/get-data-params.js +++ b/src/packages/router/route/params/utils/get-data-params.js @@ -6,15 +6,20 @@ import { typeForColumn } from '../../../../database'; import type Controller from '../../../../controller'; import type { ParameterLike } from '../interfaces'; +type ParamTuple = [string, ParameterLike]; + /** * @private */ -function getIDParam({ model }: Controller): [string, ParameterLike] { - const primaryKeyColumn = model.columnFor(model.primaryKey); +function getIDParam({ model }: Controller<*>): ParamTuple { let primaryKeyType = 'number'; - if (primaryKeyColumn) { - primaryKeyType = typeForColumn(primaryKeyColumn); + if (model) { + const primaryKeyColumn = model.columnFor(model.primaryKey); + + if (primaryKeyColumn) { + primaryKeyType = typeForColumn(primaryKeyColumn); + } } return ['id', new Parameter({ @@ -27,13 +32,19 @@ function getIDParam({ model }: Controller): [string, ParameterLike] { /** * @private */ -function getTypeParam({ - model -}: Controller): [string, ParameterLike] { +function getTypeParam(controller: Controller<*>): ParamTuple { + if (controller.model) { + return ['type', new Parameter({ + type: 'string', + path: 'data.type', + values: [controller.model.resourceName], + required: true + })]; + } + return ['type', new Parameter({ type: 'string', path: 'data.type', - values: [model.resourceName], required: true })]; } @@ -44,22 +55,33 @@ function getTypeParam({ function getAttributesParam({ model, params -}: Controller): [string, ParameterLike] { +}: Controller<*>): ParamTuple { return ['attributes', new ParameterGroup(params.reduce((group, param) => { - const col = model.columnFor(param); + const path = `data.attributes.${param}`; - if (col) { - const type = typeForColumn(col); - const path = `data.attributes.${param}`; - const required = !col.nullable && isNull(col.defaultValue); + if (model) { + const col = model.columnFor(param); - return [ - ...group, - [param, new Parameter({ type, path, required })] - ]; + if (col) { + const type = typeForColumn(col); + const required = !col.nullable && isNull(col.defaultValue); + + return [ + ...group, + [param, new Parameter({ type, path, required })] + ]; + } + + return group; } - return group; + return [ + ...group, + [param, new Parameter({ + path, + required: false + })] + ]; }, []), { path: 'data.attributes', sanitize: true @@ -69,68 +91,76 @@ function getAttributesParam({ /** * @private */ -function getRelationshipsParam({ - model, - params -}: Controller): [string, ParameterLike] { - return ['relationships', new ParameterGroup(params.reduce((group, param) => { - const path = `data.relationships.${param}`; - const opts = model.relationshipFor(param); - - if (!opts) { - return group; - } - - if (opts.type === 'hasMany') { - return [ - ...group, - - [param, new ParameterGroup([ - ['data', new Parameter({ - type: 'array', - path: `${path}.data`, - required: true - })] - ], { - path - })] - ]; - } - - const primaryKeyColumn = opts.model.columnFor(opts.model.primaryKey); - let primaryKeyType = 'number'; - - if (primaryKeyColumn) { - primaryKeyType = typeForColumn(primaryKeyColumn); - } +function getRelationshipsParam(controller: Controller<*>): ParamTuple { + if (controller.model) { + const { model, params } = controller; return [ - ...group, - - [param, new ParameterGroup([ - ['data', new ParameterGroup([ - ['id', new Parameter({ - type: primaryKeyType, - path: `${path}.data.id`, - required: true - })], - - ['type', new Parameter({ - type: 'string', - path: `${path}.data.type`, - values: [opts.model.resourceName], - required: true + 'relationships', + new ParameterGroup(params.reduce((group, param) => { + const path = `data.relationships.${param}`; + const opts = model.relationshipFor(param); + + if (!opts) { + return group; + } + + if (opts.type === 'hasMany') { + return [ + ...group, + + [param, new ParameterGroup([ + ['data', new Parameter({ + type: 'array', + path: `${path}.data`, + required: true + })] + ], { + path + })] + ]; + } + + const primaryKeyColumn = opts.model.columnFor(opts.model.primaryKey); + let primaryKeyType = 'number'; + + if (primaryKeyColumn) { + primaryKeyType = typeForColumn(primaryKeyColumn); + } + + return [ + ...group, + + [param, new ParameterGroup([ + ['data', new ParameterGroup([ + ['id', new Parameter({ + type: primaryKeyType, + path: `${path}.data.id`, + required: true + })], + + ['type', new Parameter({ + type: 'string', + path: `${path}.data.type`, + values: [opts.model.resourceName], + required: true + })] + ], { + type: 'array', + path: `${path}.data`, + required: true + })] + ], { + path })] - ], { - type: 'array', - path: `${path}.data`, - required: true - })] - ], { - path - })] + ]; + }, []), { + path: 'data.relationships' + }) ]; - }, []), { + } + + return ['relationships', new ParameterGroup([], { path: 'data.relationships' })]; } @@ -139,9 +169,9 @@ function getRelationshipsParam({ * @private */ export default function getDataParams( - controller: Controller, + controller: Controller<*>, includeID: boolean -): [string, ParameterLike] { +): ParamTuple { let params = [getTypeParam(controller)]; if (controller.hasModel) { diff --git a/src/packages/router/route/params/utils/get-default-collection-params.js b/src/packages/router/route/params/utils/get-default-collection-params.js index b6fa6ab4..376013b0 100644 --- a/src/packages/router/route/params/utils/get-default-collection-params.js +++ b/src/packages/router/route/params/utils/get-default-collection-params.js @@ -4,36 +4,52 @@ import type Controller from '../../../../controller'; /** * @private */ -export default function getDefaultCollectionParams({ - model, - defaultPerPage, - serializer: { - hasOne, - hasMany, - attributes +function getDefaultCollectionParams(controller: Controller<*>): Object { + if (controller.model) { + const { + model, + defaultPerPage, + serializer: { + hasOne, + hasMany, + attributes + } + } = controller; + + return { + sort: 'createdAt', + filter: {}, + fields: { + [model.resourceName]: attributes, + ...[...hasOne, ...hasMany].reduce((include, key) => { + const opts = model.relationshipFor(key); + + if (!opts) { + return include; + } + + return { + ...include, + [opts.model.resourceName]: [opts.model.primaryKey] + }; + }, {}) + }, + page: { + size: defaultPerPage, + number: 1 + } + }; } -}: Controller): Object { + return { sort: 'createdAt', filter: {}, - fields: { - [model.resourceName]: attributes, - ...[...hasOne, ...hasMany].reduce((include, key) => { - const opts = model.relationshipFor(key); - - if (!opts) { - return include; - } - - return { - ...include, - [opts.model.resourceName]: [opts.model.primaryKey] - }; - }, {}) - }, + fields: {}, page: { - size: defaultPerPage, + size: 25, number: 1 } }; } + +export default getDefaultCollectionParams; diff --git a/src/packages/router/route/params/utils/get-default-member-params.js b/src/packages/router/route/params/utils/get-default-member-params.js index af493d4a..46f381d7 100644 --- a/src/packages/router/route/params/utils/get-default-member-params.js +++ b/src/packages/router/route/params/utils/get-default-member-params.js @@ -4,29 +4,39 @@ import type Controller from '../../../../controller'; /** * @private */ -export default function getDefaultMemberParams({ - model, - serializer: { - hasOne, - hasMany, - attributes - } -}: Controller): Object { - return { - fields: { - [model.resourceName]: attributes, - ...[...hasOne, ...hasMany].reduce((include, key) => { - const opts = model.relationshipFor(key); +function getDefaultMemberParams(controller: Controller<*>): Object { + if (controller.model) { + const { + model, + serializer: { + hasOne, + hasMany, + attributes + } + } = controller; - if (!opts) { - return include; - } + return { + fields: { + [model.resourceName]: attributes, + ...[...hasOne, ...hasMany].reduce((include, key) => { + const opts = model.relationshipFor(key); - return { - ...include, - [opts.model.resourceName]: [opts.model.primaryKey] - }; - }, {}) - } + if (!opts) { + return include; + } + + return { + ...include, + [opts.model.resourceName]: [opts.model.primaryKey] + }; + }, {}) + } + }; + } + + return { + fields: {} }; } + +export default getDefaultMemberParams; diff --git a/src/packages/router/route/params/utils/get-query-params.js b/src/packages/router/route/params/utils/get-query-params.js index 5f97bbb1..be0d8020 100644 --- a/src/packages/router/route/params/utils/get-query-params.js +++ b/src/packages/router/route/params/utils/get-query-params.js @@ -21,7 +21,7 @@ function getPageParam(): [string, ParameterLike] { */ function getSortParam({ sort -}: Controller): [string, ParameterLike] { +}: Controller<*>): [string, ParameterLike] { return ['sort', new Parameter({ path: 'sort', type: 'string', @@ -38,7 +38,7 @@ function getSortParam({ */ function getFilterParam({ filter -}: Controller): [string, ParameterLike] { +}: Controller<*>): [string, ParameterLike] { return ['filter', new ParameterGroup(filter.map(param => [ param, new Parameter({ @@ -52,48 +52,56 @@ function getFilterParam({ /** * @private */ -function getFieldsParam({ - model, - serializer: { - hasOne, - hasMany, - attributes - } -}: Controller): [string, ParameterLike] { - const relationships = [...hasOne, ...hasMany]; - - return ['fields', new ParameterGroup([ - [model.resourceName, new Parameter({ - path: `fields.${model.resourceName}`, - type: 'array', - values: attributes, - sanitize: true - })], - ...relationships.reduce((result, relationship) => { - const opts = model.relationshipFor(relationship); - - if (opts) { - return [ - ...result, - - [opts.model.resourceName, new Parameter({ - path: `fields.${opts.model.resourceName}`, - type: 'array', - sanitize: true, - - values: [ - opts.model.primaryKey, - ...opts.model.serializer.attributes - ] - })] - ]; +function getFieldsParam(controller: Controller<*>): [string, ParameterLike] { + if (controller.model) { + const { + model, + serializer: { + hasOne, + hasMany, + attributes } + } = controller; + + const relationships = [...hasOne, ...hasMany]; + + return ['fields', new ParameterGroup([ + [model.resourceName, new Parameter({ + path: `fields.${model.resourceName}`, + type: 'array', + values: attributes, + sanitize: true + })], + ...relationships.reduce((result, relationship) => { + const opts = model.relationshipFor(relationship); + + if (opts) { + return [ + ...result, + + [opts.model.resourceName, new Parameter({ + path: `fields.${opts.model.resourceName}`, + type: 'array', + sanitize: true, + + values: [ + opts.model.primaryKey, + ...opts.model.serializer.attributes + ] + })] + ]; + } + + return result; + }, []) + ], { + path: 'fields', + sanitize: true + })]; + } - return result; - }, []) - ], { - path: 'fields', - sanitize: true + return ['fields', new ParameterGroup([], { + path: 'fields' })]; } @@ -105,7 +113,7 @@ function getIncludeParam({ hasOne, hasMany } -}: Controller): [string, ParameterLike] { +}: Controller<*>): [string, ParameterLike] { const relationships = [...hasOne, ...hasMany]; return ['include', new Parameter({ @@ -120,7 +128,7 @@ function getIncludeParam({ */ export function getCustomParams({ query -}: Controller): Array<[string, ParameterLike]> { +}: Controller<*>): Array<[string, ParameterLike]> { return query.map(param => [param, new Parameter({ path: param })]); @@ -130,7 +138,7 @@ export function getCustomParams({ * @private */ export function getMemberQueryParams( - controller: Controller + controller: Controller<*> ): Array<[string, ParameterLike]> { if (controller.hasModel) { return [ @@ -147,7 +155,7 @@ export function getMemberQueryParams( * @private */ export function getCollectionQueryParams( - controller: Controller + controller: Controller<*> ): Array<[string, ParameterLike]> { if (controller.hasModel) { return [ diff --git a/src/packages/router/route/params/utils/validate-type.js b/src/packages/router/route/params/utils/validate-type.js index 3227ca7c..796da867 100644 --- a/src/packages/router/route/params/utils/validate-type.js +++ b/src/packages/router/route/params/utils/validate-type.js @@ -38,6 +38,10 @@ export default function validateType( isValid = isObject(value) || isNull(value); break; + case 'date': + isValid = value instanceof Date; + break; + default: isValid = type === valueType; } diff --git a/src/packages/router/route/test/route.test.js b/src/packages/router/route/test/route.test.js index 2b8169d4..b2bae1a5 100644 --- a/src/packages/router/route/test/route.test.js +++ b/src/packages/router/route/test/route.test.js @@ -16,13 +16,17 @@ import Route from '../index'; describe('module "router/route"', () => { describe('class Route', () => { describe('#constructor()', () => { - let controller: Controller; + let controller: Controller<*>; before(async () => { const { controllers } = await getTestApp(); + const postsController = controllers.get('posts'); - // $FlowIgnore - controller = controllers.get('posts'); + if (!postsController) { + throw new Error('Could not find controller "posts".'); + } + + controller = postsController; }); it('creates an instance of route', () => { @@ -327,7 +331,7 @@ describe('module "router/route"', () => { }); describe('#visit', () => { - let controller: Controller; + let controller: Controller<*>; before(async () => { const { controllers } = await getTestApp(); diff --git a/src/packages/router/test/namespace.test.js b/src/packages/router/test/namespace.test.js index f352372c..7e4bf8ef 100644 --- a/src/packages/router/test/namespace.test.js +++ b/src/packages/router/test/namespace.test.js @@ -10,7 +10,7 @@ describe('module "router/namespace"', () => { describe('class Namespace', () => { describe('#constructor()', () => { let root; - let controller: Controller; + let controller: Controller<*>; let controllers; let createRootNamespace; const expectNamspaceToBeValid = (subject: Namespace, name, path) => { @@ -24,18 +24,25 @@ describe('module "router/namespace"', () => { before(async () => { const app = await getTestApp(); + const rootController = app.controllers.get('application'); + const namespaceController = controllers.get('admin/application'); - controllers = app.controllers; + if (!rootController) { + throw new Error('Could not find controller "application".'); + } + + if (!namespaceController) { + throw new Error('Could not find controller "admin/application".'); + } - // $FlowIgnore - controller = controllers.get('admin/application'); + controllers = app.controllers; + controller = namespaceController; createRootNamespace = (): Namespace => new Namespace({ controllers, path: '/', name: 'root', - // $FlowIgnore - controller: controllers.get('application') + controller: rootController }); }); diff --git a/src/packages/router/test/router.test.js b/src/packages/router/test/router.test.js index 53ba59ec..31f806e0 100644 --- a/src/packages/router/test/router.test.js +++ b/src/packages/router/test/router.test.js @@ -14,7 +14,7 @@ const CONTROLLER_MISSING_MESSAGE = /Could not resolve controller by name '.+'/; describe('module "router"', () => { describe('class Router', () => { - let controller: Controller; + let controller: Controller<*>; let controllers; before(async () => { diff --git a/src/packages/router/utils/create-replacer.js b/src/packages/router/utils/create-replacer.js index 4640d584..818f2a7f 100644 --- a/src/packages/router/utils/create-replacer.js +++ b/src/packages/router/utils/create-replacer.js @@ -5,7 +5,7 @@ import type Controller from '../../controller'; * @private */ export default function createReplacer( - controllers: Map + controllers: Map> ): RegExp { const names = Array .from(controllers) diff --git a/src/packages/serializer/index.js b/src/packages/serializer/index.js index 2cb9f3ad..4c44e3c1 100644 --- a/src/packages/serializer/index.js +++ b/src/packages/serializer/index.js @@ -6,7 +6,7 @@ import { freezeProps } from '../freezeable'; import uniq from '../../utils/uniq'; import underscore from '../../utils/underscore'; import { dasherizeKeys } from '../../utils/transform-keys'; -import type { Model } from '../database'; // eslint-disable-line no-unused-vars +import { Model } from '../database'; // eslint-disable-line no-unused-vars import type { // eslint-disable-line no-duplicate-imports JSONAPI$Document, JSONAPI$DocumentLinks, @@ -14,7 +14,11 @@ import type { // eslint-disable-line no-duplicate-imports JSONAPI$RelationshipObject } from '../jsonapi'; -import type { Serializer$opts } from './interfaces'; +export type Options = { + model?: ?Class; + parent: ?Serializer<*>; + namespace: string; +}; /** * ## Overview @@ -401,7 +405,7 @@ class Serializer { * @type {Model} * @private */ - model: Class; + model: ?Class; /** * A reference to the root Serializer for the namespace that a Serializer @@ -422,7 +426,7 @@ class Serializer { */ namespace: string; - constructor({ model, parent, namespace }: Serializer$opts) { + constructor({ model, parent, namespace }: Options) { Object.assign(this, { model, parent, diff --git a/src/packages/serializer/test/serializer.test.js b/src/packages/serializer/test/serializer.test.js index d469cf4b..549692ae 100644 --- a/src/packages/serializer/test/serializer.test.js +++ b/src/packages/serializer/test/serializer.test.js @@ -53,6 +53,10 @@ describe('module "serializer"', () => { const Comment = models.get('comment'); const Categorization = models.get('categorization'); + if (!Post) { + throw new Error('Could not find model "Post".'); + } + class TestSerializer extends Serializer { attributes = [ 'body', @@ -75,7 +79,6 @@ describe('module "serializer"', () => { createSerializer = (namespace = '') => new TestSerializer({ namespace, - // $FlowIgnore model: Post, parent: null }); @@ -88,7 +91,6 @@ describe('module "serializer"', () => { } = {}, transaction) => { let include = []; const run = async trx => { - // $FlowIgnore const post = await Post .transacting(trx) .create({ @@ -198,7 +200,6 @@ describe('module "serializer"', () => { return await run(transaction); } - // $FlowIgnore return await Post.transaction(run); }; }); diff --git a/src/packages/server/request/interfaces.js b/src/packages/server/request/interfaces.js index 85b7190d..99ec0189 100644 --- a/src/packages/server/request/interfaces.js +++ b/src/packages/server/request/interfaces.js @@ -63,7 +63,7 @@ declare export class Request extends stream$Readable { defaultParams: Request$params; route: Route; action: string; - controller: Controller; + controller: Controller<*>; url: Request$url; connection: { diff --git a/src/utils/github.js b/src/utils/github.js index 0f820444..40976931 100644 --- a/src/utils/github.js +++ b/src/utils/github.js @@ -1,19 +1,19 @@ // @flow const GITHUB_URL = 'https://github.com/postlight/lux'; +type Options = { + line?: number; + branch?: string; +}; + /** * @private */ -export function fileLink(path: string, { - line, - branch = 'master' -}: { - line?: number; - branch?: string; -} = {}): string { +export function fileLink(path: string, opts: Options = {}): string { + const { line, branch = 'master' } = opts; let link = `${GITHUB_URL}/blob/${branch}/${path}`; - if (typeof line === 'number' && line >= 0) { + if (line && line >= 0) { link += `#${line}`; } diff --git a/src/utils/test/github.test.js b/src/utils/test/github.test.js index 1bd158c4..5d0e8233 100644 --- a/src/utils/test/github.test.js +++ b/src/utils/test/github.test.js @@ -35,15 +35,6 @@ describe('util github', () => { expect(result).to.equal(`${baseURL}/master/src/index.js#2`); }); - it('ignores line if it is not a number', () => { - const result = github.fileLink('src/index.js', { - // $FlowIgnore - line: [1, 2, 3] - }); - - expect(result).to.equal(`${baseURL}/master/src/index.js`); - }); - it('ignores line if it is > 0', () => { const result = github.fileLink('src/index.js', { line: -10 diff --git a/src/utils/transform-keys.js b/src/utils/transform-keys.js index 060841dc..175d6e64 100644 --- a/src/utils/transform-keys.js +++ b/src/utils/transform-keys.js @@ -21,7 +21,8 @@ export function transformKeys>( const recurse = deep && value && typeof value === 'object' - && !Array.isArray(value); + && !Array.isArray(value) + && !(value instanceof Date); if (recurse) { result[transformer(key)] = transformKeys(value, transformer, true); diff --git a/test/test-app/db/migrate/2016051621371582-create-users.js b/test/test-app/db/migrate/2016051621371582-create-users.js index eca7984a..3b95abfe 100644 --- a/test/test-app/db/migrate/2016051621371582-create-users.js +++ b/test/test-app/db/migrate/2016051621371582-create-users.js @@ -15,7 +15,8 @@ export function up(schema) { .notNullable(); table.timestamps(); - table.index(['created_at', 'updated_at']); + table.index('created_at'); + table.index('updated_at'); }); } diff --git a/test/test-app/db/migrate/2016051621393305-create-actions.js b/test/test-app/db/migrate/2016051621393305-create-actions.js index 313a6f81..973a8998 100644 --- a/test/test-app/db/migrate/2016051621393305-create-actions.js +++ b/test/test-app/db/migrate/2016051621393305-create-actions.js @@ -11,7 +11,8 @@ export function up(schema) { .notNullable(); table.timestamps(); - table.index(['created_at', 'updated_at']); + table.index('created_at'); + table.index('updated_at'); }); } diff --git a/test/test-app/db/migrate/2016051621405358-create-comments.js b/test/test-app/db/migrate/2016051621405358-create-comments.js index 0387c47e..4b6e49d4 100644 --- a/test/test-app/db/migrate/2016051621405358-create-comments.js +++ b/test/test-app/db/migrate/2016051621405358-create-comments.js @@ -17,7 +17,8 @@ export function up(schema) { .index(); table.timestamps(); - table.index(['created_at', 'updated_at']); + table.index('created_at'); + table.index('updated_at'); }); } diff --git a/test/test-app/db/migrate/2016051621421145-create-reactions.js b/test/test-app/db/migrate/2016051621421145-create-reactions.js index 516b8797..d1a80433 100644 --- a/test/test-app/db/migrate/2016051621421145-create-reactions.js +++ b/test/test-app/db/migrate/2016051621421145-create-reactions.js @@ -22,7 +22,8 @@ export function up(schema) { .index(); table.timestamps(); - table.index(['created_at', 'updated_at']); + table.index('created_at'); + table.index('updated_at'); }); } diff --git a/test/test-app/db/migrate/2016051621434009-create-posts.js b/test/test-app/db/migrate/2016051621434009-create-posts.js index 87733725..f387760d 100644 --- a/test/test-app/db/migrate/2016051621434009-create-posts.js +++ b/test/test-app/db/migrate/2016051621434009-create-posts.js @@ -18,7 +18,8 @@ export function up(schema) { .index(); table.timestamps(); - table.index(['created_at', 'updated_at']); + table.index('created_at'); + table.index('updated_at'); }); } diff --git a/test/test-app/db/migrate/2016051621435837-create-friendships.js b/test/test-app/db/migrate/2016051621435837-create-friendships.js index 3e7adfda..41b20fdb 100644 --- a/test/test-app/db/migrate/2016051621435837-create-friendships.js +++ b/test/test-app/db/migrate/2016051621435837-create-friendships.js @@ -9,7 +9,8 @@ export function up(schema) { .index(); table.timestamps(); - table.index(['created_at', 'updated_at']); + table.index('created_at'); + table.index('updated_at'); }); } diff --git a/test/test-app/db/migrate/2016051621460053-create-notifications.js b/test/test-app/db/migrate/2016051621460053-create-notifications.js index ac82214e..9fb05c14 100644 --- a/test/test-app/db/migrate/2016051621460053-create-notifications.js +++ b/test/test-app/db/migrate/2016051621460053-create-notifications.js @@ -14,7 +14,8 @@ export function up(schema) { .index(); table.timestamps(); - table.index(['created_at', 'updated_at']); + table.index('created_at'); + table.index('updated_at'); }); } diff --git a/test/test-app/db/migrate/2016061214092135-create-tags.js b/test/test-app/db/migrate/2016061214092135-create-tags.js index 3b019168..e05c7a8b 100644 --- a/test/test-app/db/migrate/2016061214092135-create-tags.js +++ b/test/test-app/db/migrate/2016061214092135-create-tags.js @@ -7,7 +7,8 @@ export function up(schema) { .notNullable(); table.timestamps(); - table.index(['created_at', 'updated_at']); + table.index('created_at'); + table.index('updated_at'); }); } diff --git a/test/test-app/db/migrate/2016061214112168-create-categorizations.js b/test/test-app/db/migrate/2016061214112168-create-categorizations.js index 0035b451..71de18ba 100644 --- a/test/test-app/db/migrate/2016061214112168-create-categorizations.js +++ b/test/test-app/db/migrate/2016061214112168-create-categorizations.js @@ -9,7 +9,8 @@ export function up(schema) { .index(); table.timestamps(); - table.index(['created_at', 'updated_at']); + table.index('created_at'); + table.index('updated_at'); }); } diff --git a/test/test-app/db/migrate/2016092015442134-create-images.js b/test/test-app/db/migrate/2016092015442134-create-images.js index cf03696a..a87f4014 100644 --- a/test/test-app/db/migrate/2016092015442134-create-images.js +++ b/test/test-app/db/migrate/2016092015442134-create-images.js @@ -12,7 +12,8 @@ export function up(schema) { .index(); table.timestamps(); - table.index(['created_at', 'updated_at']); + table.index('created_at'); + table.index('updated_at'); }); } From 0e4998f166b0d4480b3e634fff07b97584b0ac38 Mon Sep 17 00:00:00 2001 From: Zachary Golba Date: Fri, 20 Jan 2017 09:03:17 -0500 Subject: [PATCH 10/12] fix: remove scripts directory --- scripts/build/config/index.js | 45 ----------------------------------- 1 file changed, 45 deletions(-) delete mode 100644 scripts/build/config/index.js diff --git a/scripts/build/config/index.js b/scripts/build/config/index.js deleted file mode 100644 index ed914220..00000000 --- a/scripts/build/config/index.js +++ /dev/null @@ -1,45 +0,0 @@ - -'use strict'; - -require('../../../lib/babel-hook'); - -const SRC = '../../../src'; - -const fs = require('fs'); -const path = require('path'); - -// Plugins -const json = require('rollup-plugin-json'); -const babel = require('rollup-plugin-babel'); -const nodeResolve = require('rollup-plugin-node-resolve'); - -const compiler = require(`${SRC}/packages/compiler`); -const { default: template } = require(`${SRC}/packages/template`); - -const banner = template` - - 'use strict'; - - require('source-map-support').install({ - environment: 'node' - }); -`; - -module.exports = { - rollup: compiler.createRollupConfig({ - plugins: [ - json(), - babel(), - nodeResolve({ preferBuiltins: true }) - ], - external: [ - 'knex', - 'bundle', - path.join(__dirname, '..', '..', '..', 'lib', 'fs', 'index.js'), - ...fs.readdirSync(path.join(__dirname, '..', '..', '..', 'node_modules')) - ] - }), - bundle: compiler.createBundleConfig({ - banner - }) -}; From 3c62b0da271cfd9d4f02c9d52a8ef6a871e87a91 Mon Sep 17 00:00:00 2001 From: Zachary Golba Date: Wed, 25 Jan 2017 09:53:42 -0500 Subject: [PATCH 11/12] fix: flow errors --- .../loader/builder/test/create-parent-builder.test.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/packages/loader/builder/test/create-parent-builder.test.js b/src/packages/loader/builder/test/create-parent-builder.test.js index 530f6919..8937a5ee 100644 --- a/src/packages/loader/builder/test/create-parent-builder.test.js +++ b/src/packages/loader/builder/test/create-parent-builder.test.js @@ -18,22 +18,21 @@ describe('module "loader/builder"', () => { class ApiV1ApplicationController extends Controller {} beforeEach(() => { - subject = createParentBuilder((key, target, parent) => { + subject = createParentBuilder((key, Target, parent) => { const namespace = posix.dirname(key).replace('.', ''); - // $FlowIgnore const serializer = new Serializer({ namespace, model: null, parent: null }); - return Reflect.construct(target, [{ + return new Target({ parent, namespace, serializer, model: null - }]); + }); }); }); From 0384e1b2d44633b2316510e1628d941846e7a6c1 Mon Sep 17 00:00:00 2001 From: Zachary Golba Date: Sun, 29 Jan 2017 17:16:00 -0500 Subject: [PATCH 12/12] fix: holes in array return from entries util fix: holes in array return from entries util --- bin/lux | 6 +++--- src/packages/cli/commands/dbcreate.js | 2 +- src/packages/cli/commands/dbdrop.js | 2 +- src/utils/entries.js | 8 ++++---- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/bin/lux b/bin/lux index d1bc1ccb..e4859a46 100755 --- a/bin/lux +++ b/bin/lux @@ -35,9 +35,9 @@ function commandNotFound(cmd) { function setEnvVar(key, val, def) { if (val) { - Reflect.set(process.env, key, val); - } else if (!Reflect.has(process.env, key)) { - Reflect.set(process.env, key, def); + process.env[key] = val; + } else if (!process.env[key]) { + process.env[key] = def; } } diff --git a/src/packages/cli/commands/dbcreate.js b/src/packages/cli/commands/dbcreate.js index 173ec9bc..7ba6d462 100644 --- a/src/packages/cli/commands/dbcreate.js +++ b/src/packages/cli/commands/dbcreate.js @@ -13,7 +13,7 @@ import { createLoader } from '../../loader'; */ export function dbcreate() { const load = createLoader(CWD); - const config = Reflect.get(load('config').database, NODE_ENV); + const config = load('config').database[NODE_ENV]; if (!config) { throw new DatabaseConfigMissingError(NODE_ENV); diff --git a/src/packages/cli/commands/dbdrop.js b/src/packages/cli/commands/dbdrop.js index 7b73e7c7..551793bb 100644 --- a/src/packages/cli/commands/dbdrop.js +++ b/src/packages/cli/commands/dbdrop.js @@ -13,7 +13,7 @@ import { createLoader } from '../../loader'; */ export function dbdrop() { const load = createLoader(CWD); - const config = Reflect.get(load('config').database, NODE_ENV); + const config = load('config').database[NODE_ENV]; if (!config) { throw new DatabaseConfigMissingError(NODE_ENV); diff --git a/src/utils/entries.js b/src/utils/entries.js index 712171cb..0c29c605 100644 --- a/src/utils/entries.js +++ b/src/utils/entries.js @@ -10,17 +10,17 @@ export default function entries(source: Object): Array<[string, any]> { } const keys = Object.keys(source); + const result = new Array(keys.length); - return keys.reduce((prev, key) => { + return keys.reduce((prev, key, idx) => { const next = prev; const entry = new Array(2); entry[0] = key; entry[1] = source[key]; - // $FlowIgnore - next[next.length] = entry; + next[idx] = entry; return next; - }, new Array(keys.length)); + }, result); }