From 2512e857b2c50e2c78a3ca3ae4ef1d60520cd215 Mon Sep 17 00:00:00 2001 From: Zachary Golba Date: Sat, 4 Jun 2016 11:18:37 -0400 Subject: [PATCH] refactor: use streams for Logger deps: update outdated packages fix: do not check if log file exists before boot chore: update cli templates to reflect api changes --- build/config.js | 1 + build/config.test.js | 3 + package.json | 2 +- src/packages/cli/commands/create.js | 38 ++++--- src/packages/cli/commands/db-create.js | 3 + src/packages/cli/commands/db-drop.js | 3 + src/packages/cli/commands/db-migrate.js | 3 + src/packages/cli/commands/db-rollback.js | 3 + src/packages/cli/commands/db-seed.js | 3 + src/packages/cli/commands/destroy.js | 6 + src/packages/cli/commands/generate.js | 20 +++- src/packages/cli/commands/serve.js | 4 +- src/packages/cli/commands/test.js | 7 +- src/packages/cli/constants.js | 3 + src/packages/cli/index.js | 3 + src/packages/cli/templates/application.js | 20 ++-- src/packages/cli/templates/babel-rc.js | 43 +++---- src/packages/cli/templates/config.js | 20 ++-- src/packages/cli/templates/controller.js | 30 +++-- src/packages/cli/templates/database.js | 12 +- src/packages/cli/templates/empty-migration.js | 19 ++-- src/packages/cli/templates/gitignore.js | 28 +++-- src/packages/cli/templates/license.js | 46 ++++---- src/packages/cli/templates/migration.js | 11 -- src/packages/cli/templates/model-migration.js | 106 ++++++++++-------- src/packages/cli/templates/model.js | 48 ++++---- src/packages/cli/templates/package-json.js | 64 ++++++----- src/packages/cli/templates/readme.js | 36 +++--- src/packages/cli/templates/routes.js | 16 ++- src/packages/cli/templates/seed.js | 16 ++- src/packages/cli/templates/serializer.js | 40 +++++-- src/packages/cli/utils/indent.js | 3 +- src/packages/compiler/index.js | 7 +- src/packages/database/model/index.js | 10 +- .../database/model/utils/fetch-has-many.js | 2 +- src/packages/logger/ansi-remover/index.js | 30 +++++ src/packages/logger/index.js | 71 +++++------- src/packages/logger/initialize.js | 67 +++++++++-- src/packages/logger/utils/write.js | 18 --- src/packages/pm/cluster/index.js | 6 +- src/packages/server/index.js | 2 +- src/packages/template/index.js | 24 +++- src/packages/template/utils/insert-values.js | 6 +- test/test-app/package.json | 10 +- 44 files changed, 562 insertions(+), 351 deletions(-) delete mode 100644 src/packages/cli/templates/migration.js create mode 100644 src/packages/logger/ansi-remover/index.js delete mode 100644 src/packages/logger/utils/write.js diff --git a/build/config.js b/build/config.js index 04d06e8f..042bbe3e 100644 --- a/build/config.js +++ b/build/config.js @@ -8,6 +8,7 @@ import { join as joinPath } from 'path'; export default { external: readdirSync(joinPath(__dirname, '../node_modules')), + sourceMap: true, banner: 'require(\'source-map-support\').install();\n' + diff --git a/build/config.test.js b/build/config.test.js index d8693cdc..dceab1f7 100644 --- a/build/config.test.js +++ b/build/config.test.js @@ -6,6 +6,9 @@ import { readdirSync } from 'fs'; import { join as joinPath } from 'path'; export default { + banner: 'require(\'source-map-support\').install();\n', + sourceMap: true, + entry: [ joinPath(__dirname, '../test/index.js'), joinPath(__dirname, '../test/unit/**/*.js'), diff --git a/package.json b/package.json index 07d25822..0eeb8f1d 100644 --- a/package.json +++ b/package.json @@ -72,7 +72,7 @@ "babel-plugin-transform-flow-strip-types": "6.8.0", "babel-plugin-transform-object-rest-spread": "6.8.0", "chai": "3.5.0", - "documentation": "4.0.0-beta4", + "documentation": "4.0.0-beta5", "flow-bin": "0.26.0", "isomorphic-fetch": "2.2.1", "mocha": "2.5.3", diff --git a/src/packages/cli/commands/create.js b/src/packages/cli/commands/create.js index 7e02c1b4..ccdb147c 100644 --- a/src/packages/cli/commands/create.js +++ b/src/packages/cli/commands/create.js @@ -2,6 +2,7 @@ import Ora from 'ora'; import { green } from 'chalk'; import fs from '../../fs'; +import template from '../../template'; import exec from '../../../utils/exec'; import driverFor from '../utils/driver-for'; @@ -19,6 +20,9 @@ import readmeTemplate from '../templates/readme'; import licenseTemplate from '../templates/license'; import gitignoreTemplate from '../templates/gitignore'; +/** + * @private + */ export default async function create(name, database) { const driver = driverFor(database); const project = `${process.env.PWD}/${name}`; @@ -113,22 +117,24 @@ export default async function create(name, database) { ) ]); - console.log(` -${green('create')} app/index.js -${green('create')} app/routes.js -${green('create')} bin/app.js -${green('create')} config/environments/development.js -${green('create')} config/environments/test.js -${green('create')} config/environments/production.js -${green('create')} config/database.js -${green('create')} db/migrate -${green('create')} db/seed.js -${green('create')} README.md -${green('create')} LICENSE -${green('create')} package.json -${green('create')} .babelrc -${green('create')} .gitignore - `.substr(1).trim()); + const logOutput = template` + ${green('create')} app/index.js + ${green('create')} app/routes.js + ${green('create')} bin/app.js + ${green('create')} config/environments/development.js + ${green('create')} config/environments/test.js + ${green('create')} config/environments/production.js + ${green('create')} config/database.js + ${green('create')} db/migrate + ${green('create')} db/seed.js + ${green('create')} README.md + ${green('create')} LICENSE + ${green('create')} package.json + ${green('create')} .babelrc + ${green('create')} .gitignore + `; + + console.log(logOutput.substr(0, logOutput.length - 1)); await Promise.all([ generate('serializer', 'application', project), diff --git a/src/packages/cli/commands/db-create.js b/src/packages/cli/commands/db-create.js index 4c635e77..210abbb4 100644 --- a/src/packages/cli/commands/db-create.js +++ b/src/packages/cli/commands/db-create.js @@ -4,6 +4,9 @@ import { connect } from '../../database'; const { env: { PWD, NODE_ENV = 'development' } } = process; +/** + * @private + */ export default async function dbCreate() { const { database: { diff --git a/src/packages/cli/commands/db-drop.js b/src/packages/cli/commands/db-drop.js index c7b2028c..b9f15b26 100644 --- a/src/packages/cli/commands/db-drop.js +++ b/src/packages/cli/commands/db-drop.js @@ -4,6 +4,9 @@ import loader from '../../loader'; const { env: { PWD, NODE_ENV = 'development' } } = process; +/** + * @private + */ export default async function dbDrop() { const { database: { diff --git a/src/packages/cli/commands/db-migrate.js b/src/packages/cli/commands/db-migrate.js index 40647e9e..5c7a0fa1 100644 --- a/src/packages/cli/commands/db-migrate.js +++ b/src/packages/cli/commands/db-migrate.js @@ -4,6 +4,9 @@ import loader from '../../loader'; const { env: { PWD } } = process; +/** + * @private + */ export default async function dbMigrate() { const { database: config } = loader(PWD, 'config'); const migrations = loader(PWD, 'migrations'); diff --git a/src/packages/cli/commands/db-rollback.js b/src/packages/cli/commands/db-rollback.js index 4ba93dc1..2b993b54 100644 --- a/src/packages/cli/commands/db-rollback.js +++ b/src/packages/cli/commands/db-rollback.js @@ -5,6 +5,9 @@ import loader from '../../loader'; const { env: { PWD } } = process; +/** + * @private + */ export default async function dbRollback() { const { database: config } = loader(PWD, 'config'); const migrations = loader(PWD, 'migrations'); diff --git a/src/packages/cli/commands/db-seed.js b/src/packages/cli/commands/db-seed.js index a10838f1..f5e1d5c4 100644 --- a/src/packages/cli/commands/db-seed.js +++ b/src/packages/cli/commands/db-seed.js @@ -4,6 +4,9 @@ import loader from '../../loader'; const { env: { PWD } } = process; +/** + * @private + */ export default async function dbSeed() { const { database: config } = loader(PWD, 'config'); const seed = loader(PWD, 'seed'); diff --git a/src/packages/cli/commands/destroy.js b/src/packages/cli/commands/destroy.js index f56e1321..33013300 100644 --- a/src/packages/cli/commands/destroy.js +++ b/src/packages/cli/commands/destroy.js @@ -5,6 +5,9 @@ import fs, { rmrf, exists } from '../../fs'; const { env: { PWD } } = process; +/** + * @private + */ export async function destroyType(type, name) { let path; @@ -35,6 +38,9 @@ export async function destroyType(type, name) { } } +/** + * @private + */ export default async function destroy(type, name) { if (type === 'resource') { const routes = (await fs.readFileAsync(`${PWD}/app/routes.js`, 'utf8')) diff --git a/src/packages/cli/commands/generate.js b/src/packages/cli/commands/generate.js index be0c64ca..8c55b478 100644 --- a/src/packages/cli/commands/generate.js +++ b/src/packages/cli/commands/generate.js @@ -15,6 +15,9 @@ import indent from '../utils/indent'; const { env: { PWD } } = process; +/** + * @private + */ export async function generateType(type, name, pwd, attrs = []) { const rl = createInterface({ input: process.stdin, @@ -101,33 +104,38 @@ export async function generateType(type, name, pwd, attrs = []) { console.log(`${red('remove')} ${oldPath}`); } - await fs.writeFileAsync(`${pwd}/${path}`, `${data}\n`, 'utf8'); + await fs.writeFileAsync(`${pwd}/${path}`, data, 'utf8'); console.log(`${green('create')} ${path}`); } else { - await fs.writeFileAsync(`${pwd}/${path}`, `${data}\n`, 'utf8'); + await fs.writeFileAsync(`${pwd}/${path}`, data, 'utf8'); console.log(`${yellow('overwrite')} ${path}`); } } else { console.log(`${yellow('skip')} ${path}`); } } else { - await fs.writeFileAsync(`${pwd}/${path}`, `${data}\n`, 'utf8'); + await fs.writeFileAsync(`${pwd}/${path}`, data, 'utf8'); console.log(`${green('create')} ${path}`); } rl.close(); } +/** + * @private + */ export default async function generate(type, name, pwd = PWD, attrs = []) { if (type === 'resource') { const routes = (await fs.readFileAsync(`${pwd}/app/routes.js`, 'utf8')) .split('\n') .reduce((str, line, index, array) => { - const closeIndex = array.lastIndexOf('};'); + const closeIndex = array.lastIndexOf('}'); - if (index <= closeIndex) { + if (line.length && index <= closeIndex) { str += `${line}\n`; - } if (index + 1 === closeIndex) { + } + + if (index + 1 === closeIndex) { str += `${indent(2)}resource('${pluralize(name)}');\n`; } diff --git a/src/packages/cli/commands/serve.js b/src/packages/cli/commands/serve.js index 6fd6ef83..2eeb6b8f 100644 --- a/src/packages/cli/commands/serve.js +++ b/src/packages/cli/commands/serve.js @@ -35,9 +35,9 @@ export default async function serve( const { maxWorkers: count } = cluster; - logger.log(`Starting Lux Server with ${cyan(`${count}`)} worker processes`); + logger.info(`Starting Lux Server with ${cyan(`${count}`)} worker processes`); cluster.once('ready', () => { - logger.log(`Lux Server listening on port: ${cyan(`${port}`)}`); + logger.info(`Lux Server listening on port: ${cyan(`${port}`)}`); }); } diff --git a/src/packages/cli/commands/test.js b/src/packages/cli/commands/test.js index 6e6f1ae1..c6c3ac43 100644 --- a/src/packages/cli/commands/test.js +++ b/src/packages/cli/commands/test.js @@ -1,3 +1,8 @@ -export default async function test() { +// @flow + +/** + * @private + */ +export default async function test(): Promise { console.log('Coming Soon!'); } diff --git a/src/packages/cli/constants.js b/src/packages/cli/constants.js index a1c43ef9..6f4e5365 100644 --- a/src/packages/cli/constants.js +++ b/src/packages/cli/constants.js @@ -1,3 +1,6 @@ +/** + * @private + */ export const VALID_DATABASES = [ 'postgres', 'sqlite', diff --git a/src/packages/cli/index.js b/src/packages/cli/index.js index 21a341e8..d1359bec 100644 --- a/src/packages/cli/index.js +++ b/src/packages/cli/index.js @@ -21,6 +21,9 @@ import { dbRollback } from './commands/index'; +/** + * @private + */ export default function CLI() { const { argv, diff --git a/src/packages/cli/templates/application.js b/src/packages/cli/templates/application.js index 06e3b40a..694cf3e5 100644 --- a/src/packages/cli/templates/application.js +++ b/src/packages/cli/templates/application.js @@ -1,15 +1,21 @@ +// @flow import { classify } from 'inflection'; -export default (name) => { +import template from '../../template'; + +/** + * @private + */ +export default (name: string): string => { name = classify(name.replace('-', '_')); - return ` -import Lux from 'lux-framework'; + return template` + import { Application } from 'lux-framework'; -class ${name} extends Lux { + class ${name} extends Application { -} + } -export default ${name}; - `.substr(1).trim(); + export default ${name}; + `; }; diff --git a/src/packages/cli/templates/babel-rc.js b/src/packages/cli/templates/babel-rc.js index 9211b650..6903f6b6 100644 --- a/src/packages/cli/templates/babel-rc.js +++ b/src/packages/cli/templates/babel-rc.js @@ -1,20 +1,23 @@ -export default (name) => { - return ` -{ - "plugins": [ - "transform-es2015-modules-commonjs", - "transform-decorators-legacy", - "transform-class-properties", - "transform-es2015-classes", - "transform-es2015-destructuring", - "transform-es2015-parameters", - "transform-es2015-spread", - "transform-decorators", - "syntax-trailing-function-commas", - "transform-object-rest-spread", - "transform-async-to-generator", - "transform-exponentiation-operator" - ] -} - `.substr(1).trim(); -}; +// @flow +import template from '../../template'; + +/** + * @private + */ +export default (): string => template` + { + "plugins": [ + "external-helpers-2", + "syntax-trailing-function-commas", + "transform-decorators-legacy", + "transform-class-properties", + "transform-decorators", + "transform-es2015-destructuring", + "transform-es2015-parameters", + "transform-es2015-spread", + "transform-object-rest-spread", + "transform-async-to-generator", + "transform-exponentiation-operator" + ] + } +`; diff --git a/src/packages/cli/templates/config.js b/src/packages/cli/templates/config.js index c90f9b13..220d63e1 100644 --- a/src/packages/cli/templates/config.js +++ b/src/packages/cli/templates/config.js @@ -1,4 +1,10 @@ -export default (name, env) => { +// @flow +import template from '../../template'; + +/** + * @private + */ +export default (name: string, env: string): string => { const isProdENV = env === 'production'; let keyPrefix = `${name}`; @@ -6,10 +12,10 @@ export default (name, env) => { keyPrefix += `::${env}`; } - return ` -export default { - log: ${!isProdENV}, - domain: 'http://localhost:4000' -}; - `.substr(1).trim(); + return template` + export default { + log: ${!isProdENV}, + domain: 'http://localhost:4000' + }; + `; }; diff --git a/src/packages/cli/templates/controller.js b/src/packages/cli/templates/controller.js index 82f72aef..98f8abbc 100644 --- a/src/packages/cli/templates/controller.js +++ b/src/packages/cli/templates/controller.js @@ -1,11 +1,21 @@ +// @flow import { classify, camelize, pluralize } from 'inflection'; +import template from '../../template'; + import indent from '../utils/indent'; import underscore from '../../../utils/underscore'; -export default (name, attrs = []) => { +/** + * @private + */ +export default (name: string, attrs: Array): string => { name = classify(underscore(name)); + if (!attrs) { + attrs = []; + } + if (name !== 'Application') { name = pluralize(name); } @@ -19,10 +29,10 @@ export default (name, attrs = []) => { str += (indent(2) + `params = [\n`); } - str += (indent(4) + `'${camelize(underscore(attr), true)}'`); + str += (indent(8) + `'${camelize(underscore(attr), true)}'`); if (index === array.length - 1) { - str += `\n${indent(2)}];`; + str += `\n${indent(6)}];`; } else { str += ',\n'; } @@ -30,13 +40,13 @@ export default (name, attrs = []) => { return str; }, ''); - return ` -import { Controller } from 'lux-framework'; + return template` + import { Controller } from 'lux-framework'; -class ${name}Controller extends Controller { -${body} -} + class ${name}Controller extends Controller { + ${body} + } -export default ${name}Controller; - `.substr(1).trim(); + export default ${name}Controller; + `; }; diff --git a/src/packages/cli/templates/database.js b/src/packages/cli/templates/database.js index 65281f3e..328d91c5 100644 --- a/src/packages/cli/templates/database.js +++ b/src/packages/cli/templates/database.js @@ -1,11 +1,19 @@ +// @flow import indent from '../utils/indent'; -export default (name, driver = 'sqlite3') => { +/** + * @private + */ +export default (name: string, driver: string): string => { let username; let template = 'export default {\n'; name = name.replace('-', '_'); + if (!driver) { + driver = 'sqlite3'; + } + if (driver === 'pg') { username = 'postgres'; } else if (driver !== 'pg' && driver !== 'sqlite3') { @@ -46,7 +54,7 @@ export default (name, driver = 'sqlite3') => { } }); - template += '\n};'; + template += '\n};\n'; return template; }; diff --git a/src/packages/cli/templates/empty-migration.js b/src/packages/cli/templates/empty-migration.js index 369e23ab..74e45932 100644 --- a/src/packages/cli/templates/empty-migration.js +++ b/src/packages/cli/templates/empty-migration.js @@ -1,12 +1,15 @@ -export default () => { - return ` -export function up(schema) { +// @flow +import template from '../../template'; -} +/** + * @private + */ +export default (): string => template` + export function up(schema) { -export function down(schema) { + } -} + export function down(schema) { - `.substr(1).trim(); -}; + } +`; diff --git a/src/packages/cli/templates/gitignore.js b/src/packages/cli/templates/gitignore.js index abb5155a..dc674ece 100644 --- a/src/packages/cli/templates/gitignore.js +++ b/src/packages/cli/templates/gitignore.js @@ -1,15 +1,19 @@ -export default () => { - return ` -# See http://help.github.com/ignore-files/ for more about ignoring files. +// @flow +import template from '../../template'; -# dependencies -/node_modules +/** + * @private + */ +export default (): string => template` + # See http://help.github.com/ignore-files/ for more about ignoring files. -# logs -/log -npm-debug.log + # dependencies + /node_modules -# misc -*.DS_Store - `.substr(1).trim(); -}; + # logs + /log + npm-debug.log + + # misc + *.DS_Store +`; diff --git a/src/packages/cli/templates/license.js b/src/packages/cli/templates/license.js index 8ddf4f85..e9d708ab 100644 --- a/src/packages/cli/templates/license.js +++ b/src/packages/cli/templates/license.js @@ -1,25 +1,29 @@ -export default () => { - return ` -The MIT License (MIT) +// @flow +import template from '../../template'; -Copyright (c) 2016 +/** + * @private + */ +export default (): string => template` + The MIT License (MIT) -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: + Copyright (c) 2016 -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - `.substr(1).trim(); -}; + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +`; diff --git a/src/packages/cli/templates/migration.js b/src/packages/cli/templates/migration.js deleted file mode 100644 index 39d5aeb8..00000000 --- a/src/packages/cli/templates/migration.js +++ /dev/null @@ -1,11 +0,0 @@ -export default (table, attrs = []) => { - return ` -export function up(schema) { - -} - -export function down(schema) { - -} - `.substr(1).trim(); -}; diff --git a/src/packages/cli/templates/model-migration.js b/src/packages/cli/templates/model-migration.js index ba5dc5f0..2510b1df 100644 --- a/src/packages/cli/templates/model-migration.js +++ b/src/packages/cli/templates/model-migration.js @@ -1,52 +1,68 @@ +// @flow import { pluralize } from 'inflection'; +import template from '../../template'; + import indent from '../utils/indent'; import underscore from '../../../utils/underscore'; -export default (name, attrs = []) => { +/** + * @private + */ +export default (name: string, attrs: Array | string): string => { const table = pluralize(underscore(name)); - let indices = ['id']; - - attrs = attrs - .filter(attr => /^(\w|-)+:(\w|-)+$/g.test(attr)) - .map(attr => attr.split(':')) - .filter(([, type]) => !/^has-(one|many)$/g.test(type)) - .map(([column, type]) => { - column = underscore(column); - - if (type === 'belongs-to') { - type = 'integer'; - column = `${column}_id`; - - indices.push(column); - } - - return [column, type]; - }) - .map(([column, type], index) => { - return (index ? '' : '\n') + indent(4) + `table.${type}('${column}');`; - }) - .join('\n'); - - indices.push('created_at', 'updated_at'); - - indices = '\n' + indices - .map(column => indent(6) + `'${column}'`) - .join(',\n') + '\n' + indent(4); - - return ` -export function up(schema) { - return schema.createTable('${table}', table => { - table.increments('id');${attrs} - table.timestamps(); - - table.index([${indices}]); - }); -} - -export function down(schema) { - return schema.dropTable('${table}'); -} - - `.substr(1).trim(); + let indices: Array | string = ['id']; + + if (!attrs) { + attrs = []; + } + + if (Array.isArray(attrs)) { + attrs = attrs + .filter(attr => /^(\w|-)+:(\w|-)+$/g.test(attr)) + .map(attr => attr.split(':')) + .filter(([, type]) => !/^has-(one|many)$/g.test(type)) + .map(([column, type]) => { + column = underscore(column); + + if (type === 'belongs-to') { + type = 'integer'; + column = `${column}_id`; + + if (Array.isArray(indices)) { + indices.push(column); + } + } + + return [column, type]; + }) + .map(([column, type], index) => { + return `${indent(index > 0 ? 8 : 0)}table.${type}('${column}');`; + }) + .join('\n'); + } + + if (Array.isArray(indices)) { + indices.push('created_at', 'updated_at'); + + indices = '\n' + indices + .map(column => indent(10) + `'${column}'`) + .join(',\n') + '\n' + indent(8); + } + + return template` + export function up(schema) { + return schema.createTable('${table}', table => { + table.increments('id'); + ${attrs} + table.timestamps(); + + table.index([${indices}]); + }); + } + + export function down(schema) { + return schema.dropTable('${table}'); + } + `; }; diff --git a/src/packages/cli/templates/model.js b/src/packages/cli/templates/model.js index 27e875e2..98e80fa6 100644 --- a/src/packages/cli/templates/model.js +++ b/src/packages/cli/templates/model.js @@ -1,12 +1,22 @@ +// @flow import { classify, camelize } from 'inflection'; +import template from '../../template'; + import indent from '../utils/indent'; import entries from '../../../utils/entries'; import underscore from '../../../utils/underscore'; -export default (name, attrs = []) => { +/** + * @private + */ +export default (name: string, attrs: Array): string => { name = classify(underscore(name)); + if (!attrs) { + attrs = []; + } + const body = entries( attrs .filter(attr => /^(\w|-)+:(\w|-)+$/g.test(attr)) @@ -22,9 +32,9 @@ export default (name, attrs = []) => { belongsTo = [ ...belongsTo, - indent(4) + `${related}: {\n` + - indent(6) + `inverse: '${inverse}'\n` + - indent(4) + '}' + indent(8) + `${related}: {\n` + + indent(10) + `inverse: '${inverse}'\n` + + indent(8) + '}' ]; break; @@ -32,9 +42,9 @@ export default (name, attrs = []) => { hasOne = [ ...hasOne, - indent(4) + `${related}: {\n` + - indent(6) + `inverse: '${inverse}'\n` + - indent(4) + '}' + indent(8) + `${related}: {\n` + + indent(10) + `inverse: '${inverse}'\n` + + indent(8) + '}' ]; break; @@ -42,9 +52,9 @@ export default (name, attrs = []) => { hasMany = [ ...hasMany, - indent(4) + `${related}: {\n` + - indent(6) + `inverse: '${inverse}'\n` + - indent(4) + '}' + indent(8) + `${related}: {\n` + + indent(10) + `inverse: '${inverse}'\n` + + indent(8) + '}' ]; break; } @@ -63,20 +73,20 @@ export default (name, attrs = []) => { str += '\n\n'; } - str += `${indent(2)}static ${key} = {\n${value}\n${indent(2)}};`; + str += `${indent(index === 0 ? 2 : 6)}static ${key} = ` + + `{\n${value}\n${indent(6)}};`; } return str; }, ''); - return ` -import { Model } from 'lux-framework'; + return template` + import { Model } from 'lux-framework'; -class ${name} extends Model { -${body} -} - -export default ${name}; + class ${name} extends Model { + ${body} + } - `.substr(1).trim(); + export default ${name}; + `; }; diff --git a/src/packages/cli/templates/package-json.js b/src/packages/cli/templates/package-json.js index bdefc902..fe9ed545 100644 --- a/src/packages/cli/templates/package-json.js +++ b/src/packages/cli/templates/package-json.js @@ -1,29 +1,39 @@ -import { version as VERSION } from '../../../../package.json'; +// @flow +import { version } from '../../../../package.json'; -export default (name) => { - return ` -{ - "name": "${name}", - "version": "0.0.1", - "description": "", - "main": "bin/app.js", - "scripts": { - "start": "lux serve", - "test": "lux test" - }, - "author": "", - "license": "MIT", - "dependencies": { - "babel-core": "6.9.0", - "babel-eslint": "6.0.4", - "babel-plugin-transform-decorators-legacy": "1.3.4", - "babel-plugin-transform-runtime": "6.9.0", - "babel-preset-es2015": "6.9.0", - "babel-preset-stage-1": "6.5.0", - "babel-runtime": "6.9.0", - "knex": "0.11.4", - "lux-framework": "${VERSION}" +import template from '../../template'; + +const VERSION: string = version; + +/** + * @private + */ +export default (name: string): string => template` + { + "name": "${name}", + "version": "0.0.1", + "description": "", + "scripts": { + "start": "lux serve", + "test": "lux test" + }, + "author": "", + "license": "MIT", + "dependencies": { + "babel-core": "6.9.1", + "babel-plugin-external-helpers-2": "6.3.13", + "babel-plugin-syntax-trailing-function-commas": "6.8.0", + "babel-plugin-transform-async-to-generator": "6.8.0", + "babel-plugin-transform-class-properties": "6.9.1", + "babel-plugin-transform-decorators": "6.8.0", + "babel-plugin-transform-decorators-legacy": "1.3.4", + "babel-plugin-transform-es2015-destructuring": "6.9.0", + "babel-plugin-transform-es2015-parameters": "6.9.0", + "babel-plugin-transform-es2015-spread": "6.8.0", + "babel-plugin-transform-exponentiation-operator": "6.8.0", + "babel-plugin-transform-object-rest-spread": "6.8.0", + "knex": "0.11.5", + "lux-framework": "${VERSION}" + } } -} - `.substr(1).trim(); -}; +`; diff --git a/src/packages/cli/templates/readme.js b/src/packages/cli/templates/readme.js index 0f4b0c60..c11b6108 100644 --- a/src/packages/cli/templates/readme.js +++ b/src/packages/cli/templates/readme.js @@ -1,23 +1,27 @@ -export default (name) => { - return ` -# ${name} +// @flow +import template from '../../template'; -## Installation +/** + * @private + */ +export default (name: string): string => template` + # ${name} -* \`git clone https://github.com/\` -* \`cd ${name}\` -* \`npm install\` + ## Installation -## Running / Development + * \`git clone https://github.com/\` + * \`cd ${name}\` + * \`npm install\` -* \`lux serve\` + ## Running / Development -## Testing + * \`lux serve\` -* \`lux test\` + ## Testing -## Further Reading / Useful Links -* [Lux](https://github.com/postlight/lux/) -* [Chai](http://chaijs.com/) / [Mocha](http://mochajs.org/) - `.substr(1).trim(); -}; + * \`lux test\` + + ## Further Reading / Useful Links + * [Lux](https://github.com/postlight/lux/) + * [Chai](http://chaijs.com/) / [Mocha](http://mochajs.org/) +`; diff --git a/src/packages/cli/templates/routes.js b/src/packages/cli/templates/routes.js index 5999e7bf..2184f5ab 100644 --- a/src/packages/cli/templates/routes.js +++ b/src/packages/cli/templates/routes.js @@ -1,7 +1,11 @@ -export default () => { - return ` -export default (route, resource) => { +// @flow +import template from '../../template'; -}; - `.substr(1).trim(); -}; +/** + * @private + */ +export default (): string => template` + export default function routes(route, resource) { + + } +`; diff --git a/src/packages/cli/templates/seed.js b/src/packages/cli/templates/seed.js index bc7344ac..e4c55b2e 100644 --- a/src/packages/cli/templates/seed.js +++ b/src/packages/cli/templates/seed.js @@ -1,7 +1,11 @@ -export default () => { - return ` -export default async () => { +// @flow +import template from '../../template'; -}; - `.substr(1).trim(); -}; +/** + * @private + */ +export default (): string => template` + export default async function seed() { + + } +`; diff --git a/src/packages/cli/templates/serializer.js b/src/packages/cli/templates/serializer.js index de2a9b5e..e5fd13ac 100644 --- a/src/packages/cli/templates/serializer.js +++ b/src/packages/cli/templates/serializer.js @@ -1,22 +1,32 @@ +// @flow import { classify, camelize, pluralize } from 'inflection'; +import template from '../../template'; + import indent from '../utils/indent'; import entries from '../../../utils/entries'; import underscore from '../../../utils/underscore'; -export default (name, attrs = []) => { +/** + * @private + */ +export default (name: string, attrs: Array): string => { name = classify(underscore(name)); + if (!attrs) { + attrs = []; + } + if (name !== 'Application') { name = pluralize(name); } const body = entries( attrs - .filter(attr => /^(\w|-)+:(\w|-)+$/g.test(attr)) + .filter(attr => /^(\w|-)+:(\w|-)+$/g) .map(attr => attr.split(':')) .reduce(({ attributes, hasOne, hasMany }, [attr, type]) => { - attr = `${indent(4)}'${camelize(underscore(attr), true)}'`; + attr = `${indent(8)}'${camelize(underscore(attr), true)}'`; switch (type) { case 'belongs-to': @@ -37,7 +47,12 @@ export default (name, attrs = []) => { hasOne, hasMany }; - }, { attributes: [], belongsTo: [], hasOne: [], hasMany: [] }) + }, { + attributes: [], + belongsTo: [], + hasOne: [], + hasMany: [] + }) ).reduce((str, [key, value], index) => { if (value.length) { value = value.join(',\n'); @@ -46,19 +61,20 @@ export default (name, attrs = []) => { str += '\n\n'; } - str += `${indent(2)}${key} = [\n${value}\n${indent(2)}];`; + str += `${indent(index === 0 ? 2 : 6)}${key} = ` + + `[\n${value}\n${indent(6)}];`; } return str; }, ''); - return ` -import { Serializer } from 'lux-framework'; + return template` + import { Serializer } from 'lux-framework'; -class ${name}Serializer extends Serializer { -${body} -} + class ${name}Serializer extends Serializer { + ${body} + } -export default ${name}Serializer; - `.substr(1).trim(); + export default ${name}Serializer; + `; }; diff --git a/src/packages/cli/utils/indent.js b/src/packages/cli/utils/indent.js index 36d9685a..d54a4f58 100644 --- a/src/packages/cli/utils/indent.js +++ b/src/packages/cli/utils/indent.js @@ -1,3 +1,4 @@ -export default function indent(amount = 1) { +// @flow +export default function indent(amount: number = 1): string { return ' '.repeat(amount); } diff --git a/src/packages/compiler/index.js b/src/packages/compiler/index.js index 23c87b39..4fada6e9 100644 --- a/src/packages/compiler/index.js +++ b/src/packages/compiler/index.js @@ -112,6 +112,11 @@ export async function compile( return await bundle.write({ dest: path.join(dir, 'dist/bundle.js'), format: 'cjs', - useStrict: false + sourceMap: true, + useStrict: false, + + banner: + `require('${path.join(__dirname, '../node_modules/source-map-support')}` + + `').install();\n` }); } diff --git a/src/packages/database/model/index.js b/src/packages/database/model/index.js index 39476db5..36536b86 100644 --- a/src/packages/database/model/index.js +++ b/src/packages/database/model/index.js @@ -152,7 +152,7 @@ class Model { const { constructor: { logger } } = this; query.on('query', () => { - setImmediate(() => logger.log(sql`${query.toString()}`)); + setImmediate(() => logger.info(sql`${query.toString()}`)); }); } @@ -198,7 +198,7 @@ class Model { } = this; query.on('query', () => { - setImmediate(() => logger.log(sql`${query.toString()}`)); + setImmediate(() => logger.info(sql`${query.toString()}`)); }); } @@ -279,7 +279,7 @@ class Model { const { logger } = this; query.on('query', () => { - setImmediate(() => logger.log(sql`${query.toString()}`)); + setImmediate(() => logger.info(sql`${query.toString()}`)); }); } @@ -303,7 +303,7 @@ class Model { const { logger } = this; query.on('query', () => { - setImmediate(() => logger.log(sql`${query.toString()}`)); + setImmediate(() => logger.info(sql`${query.toString()}`)); }); } @@ -433,7 +433,7 @@ class Model { const { logger } = this; records.on('query', () => { - setImmediate(() => logger.log(sql`${records.toString()}`)); + setImmediate(() => logger.info(sql`${records.toString()}`)); }); } diff --git a/src/packages/database/model/utils/fetch-has-many.js b/src/packages/database/model/utils/fetch-has-many.js index 7a78e207..e345fcad 100644 --- a/src/packages/database/model/utils/fetch-has-many.js +++ b/src/packages/database/model/utils/fetch-has-many.js @@ -41,7 +41,7 @@ export default async function fetchHasMany(model, related, records) { const { logger } = model; query.on('query', () => { - setImmediate(() => logger.log(sql`${query.toString()}`)); + setImmediate(() => logger.info(sql`${query.toString()}`)); }); } diff --git a/src/packages/logger/ansi-remover/index.js b/src/packages/logger/ansi-remover/index.js new file mode 100644 index 00000000..1ce2bcbd --- /dev/null +++ b/src/packages/logger/ansi-remover/index.js @@ -0,0 +1,30 @@ +// @flow +import ansiregex from 'ansi-regex'; +import { Transform } from 'stream'; + +const pattern = ansiregex(); + +/** + * @private + */ +class AnsiRemover extends Transform { + constructor(options: {} = {}): AnsiRemover { + super(options); + return this; + } + + _transform( + data: ?Buffer | ?string, + encoding: string, + done: () => void + ): void { + if (data instanceof Buffer) { + data = new Buffer(data.toString().replace(pattern, ''), 'utf8'); + this.push(data); + } + + done(null); + } +} + +export default AnsiRemover; diff --git a/src/packages/logger/index.js b/src/packages/logger/index.js index 06fb7af7..d58f9381 100644 --- a/src/packages/logger/index.js +++ b/src/packages/logger/index.js @@ -3,20 +3,11 @@ import moment from 'moment'; import { dim, red, yellow } from 'chalk'; import { isMaster, isWorker } from 'cluster'; -import write from './utils/write'; import initialize from './initialize'; -import bound from '../../decorators/bound'; -import memoize from '../../decorators/memoize'; +import type { PassThrough } from 'stream'; -const { - stderr, - stdout, - - env: { - NODE_ENV = 'development' - } -} = process; +const { env: { NODE_ENV = 'development' } } = process; /** * @@ -27,6 +18,16 @@ class Logger { */ path: string; + /** + * + */ + stdout: PassThrough; + + /** + * + */ + stderr: PassThrough; + /** * */ @@ -39,26 +40,12 @@ class Logger { path: string, enabled: boolean, } = {}): Promise { - Object.defineProperties(this, { - path: { - value: path, - writable: false, - enumerable: false, - configurable: false - }, - - enabled: { - value: Boolean(enabled), - writable: false, - enumerable: true, - configurable: false - } + return initialize(this, isMaster, { + path, + enabled }); - - return initialize(this, isMaster); } - @memoize get file(): string { return `${this.path}/log/${NODE_ENV}.log`; } @@ -67,8 +54,7 @@ class Logger { return moment().format('M/D/YY h:m:ss A'); } - @bound - log(msg: string): void { + info(msg: string): void { if (this.enabled) { if (isWorker && typeof process.send === 'function') { process.send({ @@ -76,16 +62,14 @@ class Logger { type: 'info', message: 'log' }); - } else if (isMaster) { - msg = `${dim(`[${this.timestamp}]`)} ${msg}\n\n`; + } else if (isMaster && this.stdout) { + const chunk = new Buffer(`${dim(`[${this.timestamp}]`)} ${msg}\n\n`); - stdout.write(msg); - setImmediate(write, this.file, msg); + this.stdout.push(chunk); } } } - @bound error(msg: string): void { if (this.enabled) { if (isWorker && typeof process.send === 'function') { @@ -94,16 +78,14 @@ class Logger { type: 'error', message: 'log' }); - } else if (isMaster) { - msg = `${red(`[${this.timestamp}]`)} ${msg}\n\n`; + } else if (isMaster && this.stderr) { + const chunk = new Buffer(`${red(`[${this.timestamp}]`)} ${msg}\n\n`); - stderr.write(msg); - setImmediate(write, this.file, msg); + this.stderr.push(chunk); } } } - @bound warn(msg: string): void { if (this.enabled) { if (isWorker && typeof process.send === 'function') { @@ -112,11 +94,10 @@ class Logger { type: 'warn', message: 'log' }); - } else if (isMaster) { - msg = `${yellow(`\n\n[${this.timestamp}] Warning:`)} ${msg}\n\n`; + } else if (isMaster && this.stderr) { + const chunk = new Buffer(`${yellow(`[${this.timestamp}]`)} ${msg}\n\n`); - stderr.write(msg); - setImmediate(write, this.file, msg); + this.stderr.push(chunk); } } } @@ -134,7 +115,7 @@ class Logger { return this.error(data); case 'info': - return this.log(data); + return this.info(data); case 'warn': return this.warn(data); diff --git a/src/packages/logger/initialize.js b/src/packages/logger/initialize.js index 9e71c17e..d5852874 100644 --- a/src/packages/logger/initialize.js +++ b/src/packages/logger/initialize.js @@ -1,6 +1,9 @@ // @flow +import { PassThrough } from 'stream'; +import { createWriteStream } from 'fs'; import { join as joinPath } from 'path'; +import AnsiRemover from './ansi-remover'; import fs, { exists } from '../fs'; import type Logger from './index'; @@ -10,25 +13,71 @@ const { env: { NODE_ENV = 'development' } } = process; /** * @private */ -export default async function initialize( - instance: Logger, - isMaster: boolean -): Promise { +export default async function initialize(instance: Logger, isMaster: boolean, { + path, + enabled +}: { + path: string, + enabled: boolean +}): Promise { + const stdout = new PassThrough(); + const stderr = new PassThrough(); + + Object.defineProperties(instance, { + path: { + value: path, + writable: false, + enumerable: false, + configurable: false + }, + + stdout: { + value: stdout, + writable: false, + enumerable: false, + configurable: false + }, + + stderr: { + value: stderr, + writable: false, + enumerable: false, + configurable: false + }, + + enabled: { + value: Boolean(enabled), + writable: false, + enumerable: true, + configurable: false + } + }); + if (isMaster) { - const { path } = instance; const logsDir = joinPath(path, 'log'); const logPath = joinPath(logsDir, `${NODE_ENV}.log`); + const ansiRemover = new AnsiRemover(); let doesExist = await exists(logsDir); if (!doesExist) { await fs.mkdirAsync(logsDir); } - doesExist = await exists(logPath); + const writeStream = createWriteStream(logPath); - if (!doesExist) { - await fs.writeFileAsync(logPath, '', 'utf8'); - } + stdout + .pipe(process.stdout, { end: false }); + + stdout + .pipe(ansiRemover, { end: false }) + .pipe(writeStream, { end: false }); + + stderr + .pipe(process.stdout, { end: false }); + + stderr + .pipe(ansiRemover, { end: false }) + .pipe(writeStream, { end: false }); } return instance; diff --git a/src/packages/logger/utils/write.js b/src/packages/logger/utils/write.js deleted file mode 100644 index d86483dc..00000000 --- a/src/packages/logger/utils/write.js +++ /dev/null @@ -1,18 +0,0 @@ -// @flow -import ansiRegex from 'ansi-regex'; - -import fs from '../../fs'; - -import tryCatch from '../../../utils/try-catch'; - -/** - * @private - */ -export default function write(path: string, message: string): void { - tryCatch(async () => { - message = message.replace(ansiRegex(), ''); - await fs.appendFileAsync(path, message, 'utf8'); - }, err => { - console.error(err); - }); -} diff --git a/src/packages/pm/cluster/index.js b/src/packages/pm/cluster/index.js index 635c9ec0..2dc0bec9 100644 --- a/src/packages/pm/cluster/index.js +++ b/src/packages/pm/cluster/index.js @@ -95,7 +95,7 @@ class Cluster extends EventEmitter { const handleExit = (code: ?number) => { worker.removeListener('message', handleMessage); - this.logger.log( + this.logger.info( `Removing worker process: ${red(`${worker.process.pid}`)}` ); @@ -104,7 +104,7 @@ class Cluster extends EventEmitter { }; const handleError = () => { - this.logger.log( + this.logger.info( `Removing worker process: ${red(`${worker.process.pid}`)}` ); @@ -126,7 +126,7 @@ class Cluster extends EventEmitter { break; case 'ready': - this.logger.log( + this.logger.info( `Adding worker process: ${green(`${worker.process.pid}`)}` ); diff --git a/src/packages/server/index.js b/src/packages/server/index.js index 246f4848..b67a2c00 100644 --- a/src/packages/server/index.js +++ b/src/packages/server/index.js @@ -102,7 +102,7 @@ class Server { statusColor = 'red'; } - this.logger.log(line` + this.logger.info(line` ${cyan(`${method}`)} ${url.pathname} -> Finished after ${new Date().getTime() - startTime.getTime()} ms with ${chalk[statusColor].call(null, `${statusCode}`)} diff --git a/src/packages/template/index.js b/src/packages/template/index.js index d98de38f..6a80989d 100644 --- a/src/packages/template/index.js +++ b/src/packages/template/index.js @@ -2,6 +2,7 @@ import insertValues from './utils/insert-values'; const bodyPattern = /^\n([\s\S]+)\s{2}$/gm; +const trailingWhitespace = /\s+$/; /** * @private @@ -10,12 +11,27 @@ export default function template( strings: Array, ...values: Array ): string { - const [body] = insertValues(strings, ...values).match(bodyPattern) || []; + const compiled = insertValues(strings, ...values); + let [body] = compiled.match(bodyPattern) || []; + let indentLevel = /^\s{0,4}(.+)$/g; - return body ? body.split('\n') + if (!body) { + body = compiled; + indentLevel = /^\s{0,2}(.+)$/g; + } + + return body.split('\n') .slice(1) - .map(line => line.substr(4)) - .join('\n') : ''; + .map(line => { + line = line.replace(indentLevel, '$1'); + + if (trailingWhitespace.test(line)) { + line = line.replace(trailingWhitespace, ''); + } + + return line; + }) + .join('\n'); } export { default as insertValues } from './utils/insert-values'; diff --git a/src/packages/template/utils/insert-values.js b/src/packages/template/utils/insert-values.js index 574b7ed9..12ee2188 100644 --- a/src/packages/template/utils/insert-values.js +++ b/src/packages/template/utils/insert-values.js @@ -7,9 +7,9 @@ export default function insertValues( strings: Array, ...values: Array ): string { - return strings.reduce((result, part, idx) => { + return values.length ? strings.reduce((result, part, idx) => { const value = values[idx]; - return result + part + (value ? value : ''); - }, ''); + return result + part + (typeof value !== 'undefined' ? value : ''); + }, '') : strings.join(''); } diff --git a/test/test-app/package.json b/test/test-app/package.json index 7f71cfbd..e1aa3da2 100644 --- a/test/test-app/package.json +++ b/test/test-app/package.json @@ -10,12 +10,12 @@ "author": "", "license": "MIT", "dependencies": { - "babel-core": "6.9.0", + "babel-core": "6.9.1", "babel-eslint": "6.0.4", "babel-plugin-external-helpers-2": "6.3.13", "babel-plugin-syntax-trailing-function-commas": "6.8.0", "babel-plugin-transform-async-to-generator": "6.8.0", - "babel-plugin-transform-class-properties": "6.9.0", + "babel-plugin-transform-class-properties": "6.9.1", "babel-plugin-transform-decorators": "6.8.0", "babel-plugin-transform-decorators-legacy": "1.3.4", "babel-plugin-transform-es2015-destructuring": "6.9.0", @@ -23,9 +23,9 @@ "babel-plugin-transform-es2015-spread": "6.8.0", "babel-plugin-transform-exponentiation-operator": "6.8.0", "babel-plugin-transform-object-rest-spread": "6.8.0", - "knex": "0.11.4", - "mysql2": "1.0.0-rc.1", - "pg": "4.5.5", + "knex": "0.11.5", + "mysql2": "1.0.0-rc.2", + "pg": "4.5.6", "sqlite3": "3.1.4" } }