diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000..4d36cb9 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,52 @@ +/* + * Configuration values for eslint + */ +module.exports = { + env: { + node: true, + es6: true, + }, + + // Each rule has an error level (0-2), and some have extra parameters + // 0 turns a rule off + // 1 makes a rule give a warning + // 2 makes a rule fail linting + rules: { + 'linebreak-style': [2, 'unix'], // Only unix line endings + 'arrow-parens': [2, 'always'], // No parens on arrow functions with one param + 'no-console': [1], // Avoid using console methods + 'no-constant-condition': [1], + 'no-extra-semi': [1], // Enliminate extra semicolons + 'no-func-assign': [1], + 'no-obj-calls': [2], + 'no-unexpected-multiline': [2], // Catch syntax errors due to automatic semicolon insertion + 'no-unneeded-ternary': [2], // Avoid redundant ternary expressions + radix: [2], // Require radix parameter on parseInt + 'no-with': [2], // No use of with construct + 'no-eval': [2], // No use of eval + 'no-unreachable': [1], // Avoid code that is not reachable + 'no-irregular-whitespace': [1], // Highlight whitespace that isn't a tab or space + 'no-new-wrappers': [2], // Avoid using primitive constructors + 'no-new-func': [2], // Avoid Function constructor eval + curly: [2, 'multi-line'], // Require braces for if,for,while,do contructs that are not on the same line + 'no-implied-eval': [2], // Avoid camoflauged eval + 'no-invalid-this': [2], + 'constructor-super': [2], + 'no-dupe-args': [2], // Disallow functions to have more than one parameter with the same name + 'no-dupe-keys': [2], // Disaalow objects to have more than one property with the same name + 'no-dupe-class-members': [2], // Disallow classes to have more than one method/memeber with the same name + 'no-this-before-super': [2], + 'prefer-arrow-callback': [1], // Prefer arrow functions for callbacks + 'no-var': [2], // Use let or const instead of var + 'valid-jsdoc': [1], + semi: [2, 'always'], // Require use of semicolons + strict: [2, 'global'], // have a global 'use strict' in every code file + 'callback-return': [1], // return when invoking a callback + 'object-shorthand': [1, 'methods'], // Prefer shorthand for functions in object literals/classes, but avoid property shorthand + 'prefer-template': [1], // Prefer template strings eg. `Hello ${name}`, to string concatenation + 'no-case-declarations': [2], // Don't define variables in switch labels + 'no-const-assign': [2], // Highlight instances where assigning to const declaration + 'no-new-symbol': [2], // Symbol is not a constructor, don't use the new keyword + 'no-unused-labels': [2], // Error on labels in code that aren't used + }, +}; \ No newline at end of file diff --git a/.jscsrc b/.jscsrc index f8f9f5e..3712c8e 100644 --- a/.jscsrc +++ b/.jscsrc @@ -44,7 +44,6 @@ }, "disallowTrailingWhitespace": true, "disallowYodaConditions": true, - "esnext": true, "requireBlocksOnNewline": 1, "requireCamelCaseOrUpperCaseIdentifiers": true, "requireCapitalizedConstructors": true, diff --git a/app/config/database.js b/app/config/database.js new file mode 100644 index 0000000..26f1ddf --- /dev/null +++ b/app/config/database.js @@ -0,0 +1,14 @@ +'use strict'; + +const knexfile = require('../../knexfile'); +const knexConfig = knexfile[process.env.NODE_ENV]; +const knexConn = knexConfig.connection; + +const params = { + driver: knexConfig.client, + connection: (knexConfig.client == "sqlite3") ? knexConn.filename : knexConn, +}; + +const nodeQuery = require('ci-node-query')(params); + +module.exports = nodeQuery.getQuery(); \ No newline at end of file diff --git a/app/migration_helpers.js b/app/migration_helpers.js index b3357c7..45f158c 100644 --- a/app/migration_helpers.js +++ b/app/migration_helpers.js @@ -6,8 +6,8 @@ */ module.exports = { - get_db_type(knex) { - switch(knex.client.config.client) { + getDbType(knex) { + switch (knex.client.config.client) { case 'postgresql': case 'postgres': case 'pg': @@ -18,28 +18,28 @@ module.exports = { } }, - pg_timestamp_update_function () { + pgTimestampUpdateFunction() { return `CREATE OR REPLACE FUNCTION progblog_update_modified_column() RETURNS TRIGGER AS $$ BEGIN - NEW.updated_at = now(); - RETURN NEW; + NEW.updated_at = now(); + RETURN NEW; END; $$ language 'plpgsql';`; }, - pg_create_timestamp_update_trigger(table) { + pgCreateTimestampUpdateTrigger(table) { return `CREATE TRIGGER update_${table}_modtime BEFORE UPDATE ON "${table}" FOR EACH ROW EXECUTE PROCEDURE progblog_update_modified_column();`; }, - pg_delete_timestamp_update_trigger(table) { + pgDeleteTimestampUpdateTrigger(table) { return `DROP TRIGGER IF EXISTS update_${table}_modtime ON "${table}";`; }, - sqlite3_create_timestamp_update_trigger(table) { + sqlite3CreateTimestampUpdateTrigger(table) { return `CREATE TRIGGER IF NOT EXISTS [UpdateModified] BEFORE UPDATE ON "${table}" FOR EACH ROW @@ -48,7 +48,7 @@ module.exports = { END`; }, - sqlite3_delete_timestamp_update_trigger() { + sqlite3DeleteTimestampUpdateTrigger() { return `DROP TRIGGER IF EXISTS [UpdateModified]`; }, }; \ No newline at end of file diff --git a/gulpfile.js b/gulpfile.js deleted file mode 100644 index 4752381..0000000 --- a/gulpfile.js +++ /dev/null @@ -1,246 +0,0 @@ -'use strict'; - -const apidoc = require('gulp-apidoc'), - documentation = require('gulp-documentation'), - eslint = require('gulp-eslint'), - gulp = require('gulp'), - istanbul = require('gulp-istanbul'), - jscs = require('gulp-jscs'), - mocha = require('gulp-mocha'), - pipe = require('gulp-pipe'), - nsp = require('gulp-nsp'); - -/* - * Path(s) to all source files - */ -const SRC_FILES = [ - 'app/base/**/*.js', - 'app/config/**/*.js', - 'app/controllers/**/*.js', - 'app/helpers/**/*.js', - 'app/models/**/*.js', - 'app/*.js', -]; - -/* - * Path to unit test files - */ -const UNIT_TEST_FILES = ['test/unit/**/*_test.js']; - -/* - * Path to integration test files - */ -const INTEGRATION_TEST_FILES = ['test/integration/**/*_test.js']; - -/* - * Path(s) to all test files - */ -const TEST_FILES = ['test/**/*_test.js']; - -/* - * Configuration values for eslint - */ -const ESLINT_SETTINGS = { - env: { - node: true, - es6: true, - }, - - // Each rule has an error level (0-2), and some have extra parameters - // 0 turns a rule off - // 1 makes a rule give a warning - // 2 makes a rule fail linting - rules: { - 'linebreak-style': [2, 'unix'], // Only unix line endings - 'arrow-parens': [2, 'always'], // No parens on arrow functions with one param - 'no-console': [1], // Avoid using console methods - 'no-constant-condition': [1], - 'no-extra-semi': [1], // Enliminate extra semicolons - 'no-func-assign': [1], - 'no-obj-calls': [2], - 'no-unexpected-multiline': [2], // Catch syntax errors due to automatic semicolon insertion - 'no-unneeded-ternary': [2], // Avoid redundant ternary expressions - radix: [2], // Require radix parameter on parseInt - 'no-with': [2], // No use of with construct - 'no-eval': [2], // No use of eval - 'no-unreachable': [1], // Avoid code that is not reachable - 'no-irregular-whitespace': [1], // Highlight whitespace that isn't a tab or space - 'no-new-wrappers': [2], // Avoid using primitive constructors - 'no-new-func': [2], // Avoid Function constructor eval - curly: [2, 'multi-line'], // Require braces for if,for,while,do contructs that are not on the same line - 'no-implied-eval': [2], // Avoid camoflauged eval - 'no-invalid-this': [2], - 'constructor-super': [2], - 'no-dupe-args': [2], // Disallow functions to have more than one parameter with the same name - 'no-dupe-keys': [2], // Disaalow objects to have more than one property with the same name - 'no-dupe-class-members': [2], // Disallow classes to have more than one method/memeber with the same name - 'no-this-before-super': [2], - 'prefer-arrow-callback': [1], // Prefer arrow functions for callbacks - 'no-var': [2], // Use let or const instead of var - 'valid-jsdoc': [1], - semi: [2, 'always'], // Require use of semicolons - strict: [2, 'global'], // have a global 'use strict' in every code file - 'callback-return': [1], // return when invoking a callback - 'object-shorthand': [1, 'methods'], // Prefer shorthand for functions in object literals/classes, but avoid property shorthand - 'prefer-template': [1], // Prefer template strings eg. `Hello ${name}`, to string concatenation - 'no-case-declarations': [2], // Don't define variables in switch labels - 'no-const-assign': [2], // Highlight instances where assigning to const declaration - 'no-new-symbol': [2], // Symbol is not a constructor, don't use the new keyword - 'no-unused-labels': [2], // Error on labels in code that aren't used - }, -}; - -/* - * Configuration values for mocha - */ -const MOCHA_SETTINGS = { - ui: 'tdd', - bail: true, - slow: 1000, - timeout: 5000, -}; - -/* - * Check syntax and style of test/miscellaneous files - */ -gulp.task('lint-tests', () => { - const LINT_TESTS_FILES = TEST_FILES.concat([ - 'gulpfile.js', - 'server.js', - 'migrations/*.js', - ]); - - // eslint - pipe(gulp.src(LINT_TESTS_FILES), [ - eslint(ESLINT_SETTINGS), - eslint.format(), - eslint.failAfterError(), - ]); - - // JSCS rules are defined in /.jscsrc - pipe(gulp.src(['test/**/*.js', 'gulpfile.js']), [ - jscs(), - jscs.reporter(), - ]); -}); - -/* - * Check syntax and style of source files - */ -gulp.task('lint-src', () => { - // eslint - pipe(gulp.src(SRC_FILES), [ - eslint(ESLINT_SETTINGS), - eslint.format(), - eslint.failAfterError(), - ]); - - // JSCS - // JSCS rules are defined in /.jscsrc - pipe(gulp.src(SRC_FILES), [ - jscs(), - jscs.reporter(), - ]); -}); - -/* - * Run all lint tasks - */ -gulp.task('lint', ['lint-src', 'lint-tests']); - -/* - * Create internal method documentation of source files - */ -gulp.task('src-docs', () => { - pipe(gulp.src(SRC_FILES), [ - documentation({ - format: 'html', - }), - gulp.dest('public/generated/docs'), - ]); -}); - -/* - * Create api documentation from source files - */ -gulp.task('api-docs', (done) => { - apidoc({ - src: 'app/', - dest: 'public/generated/api-docs/', - }, done); -}); - -/* - * Run all documentation generation tasks - */ -gulp.task('docs', ['src-docs', 'api-docs']); - -/* - * Run integration tests - */ -gulp.task('integration-test', ['lint'], () => { - return gulp.src(INTEGRATION_TEST_FILES) - .pipe(mocha(MOCHA_SETTINGS)); -}); - -/* - * Run unit tests - */ -gulp.task('unit-test', ['lint'], () => { - return gulp.src(UNIT_TEST_FILES) - .pipe(mocha(MOCHA_SETTINGS)); -}); - -/* - * Run all tests - */ -gulp.task('test', ['lint'], () => { - return gulp.src(TEST_FILES) - .pipe(mocha(MOCHA_SETTINGS)); -}); - -/* - * Run hooks for istanbul coverage generation - */ -gulp.task('pre-coverage', () => { - return pipe(gulp.src(SRC_FILES), [ - istanbul(), - istanbul.hookRequire(), - ]); -}); - -/* - * Run unit tests and generate code coverage - * - * Does not run integration tests - */ -gulp.task('coverage', ['lint', 'pre-coverage'], () => { - return pipe(gulp.src(UNIT_TEST_FILES), [ - mocha(MOCHA_SETTINGS), - istanbul.writeReports({ - dir: 'public/generated/coverage', - reporters:['clover', 'lcov', 'lcovonly', 'html', 'text'], - }), - ]); -}); - -/* - * Check dependencies for known security vulnerabilites - */ -gulp.task('audit', (cb) => { - nsp({ - package: `${__dirname}/package.json`, - }, cb); -}); - -/* - * Run all tasks - */ -gulp.task('default', [ - 'audit', - 'lint', - 'docs', - 'coverage', -]); - -// End of gulpfile.js \ No newline at end of file diff --git a/migrations/20160224105517_initial.js b/migrations/20160224105517_initial.js index 279e411..f5f2815 100644 --- a/migrations/20160224105517_initial.js +++ b/migrations/20160224105517_initial.js @@ -3,7 +3,7 @@ const helpers = require('../app/migration_helpers'); exports.up = function(knex, Promise) { - const dbType = helpers.get_db_type(knex); + const dbType = helpers.getDbType(knex); // Defer has the promise resulting from creating the initial tables let defer = knex.schema.createTableIfNotExists('users', (table) => { @@ -40,33 +40,33 @@ exports.up = function(knex, Promise) { if ('pg' === dbType) { // Create the function to update - return knex.schema.raw(helpers.pg_timestamp_update_function()) + return knex.schema.raw(helpers.pgTimestampUpdateFunction()) // Create the tables .then(() => defer) // Add trigger for tables - .then(() => knex.schema.raw(helpers.pg_create_timestamp_update_trigger('users'))) - .then(() => knex.schema.raw(helpers.pg_create_timestamp_update_trigger('posts'))); + .then(() => knex.schema.raw(helpers.pgCreateTimestampUpdateTrigger('users'))) + .then(() => knex.schema.raw(helpers.pgCreateTimestampUpdateTrigger('posts'))); } else if ('sqlite3' === dbType) { - return defer.then(() => knex.schema.raw(helpers.sqlite3_create_timestamp_update_trigger('users'))) - .then(() => knex.schema.raw(helpers.sqlite3_create_timestamp_update_trigger('posts'))); + return defer.then(() => knex.schema.raw(helpers.sqlite3CreateTimestampUpdateTrigger('users'))) + .then(() => knex.schema.raw(helpers.sqlite3CreateTimestampUpdateTrigger('posts'))); } else { return defer; } }; exports.down = function(knex, Promise) { - const dbType = helpers.get_db_type(knex); + const dbType = helpers.getDbType(knex); let defer = knex.schema.dropTableIfExists('posts') .then(() => knex.schema.dropTableIfExists('users')); if ('pg' === dbType) { - return knex.schema.raw(helpers.pg_delete_timestamp_update_trigger('users')) - .then(() => knex.schema.raw(helpers.pg_delete_timestamp_update_trigger('posts'))) + return knex.schema.raw(helpers.pgDeleteTimestampUpdateTrigger('users')) + .then(() => knex.schema.raw(helpers.pgDeleteTimestampUpdateTrigger('posts'))) .then(() => defer); } else if ('sqlite3' === dbType) { - return knex.schema.raw(helpers.sqlite3_delete_timestamp_update_trigger('users')) - .then(() => knex.schema.raw(helpers.sqlite3_delete_timestamp_update_trigger('posts'))) + return knex.schema.raw(helpers.sqlite3DeleteTimestampUpdateTrigger('users')) + .then(() => knex.schema.raw(helpers.sqlite3DeleteTimestampUpdateTrigger('posts'))) .then(() => defer); } else { return defer; diff --git a/package.json b/package.json index 03c01f4..37f1734 100644 --- a/package.json +++ b/package.json @@ -3,12 +3,12 @@ "dependencies": { "axios": "^0.9.1", "body-parser": "~1.13.2", - "ci-node-query": "^3.1.0", + "ci-node-query": "^4.0.0", "cookie-parser": "~1.3.5", "debug": "~2.2.0", "dotenv": "^2.0.0", "errors": "^0.3.0", - "eslint": "^1.10.3", + "eslint": "^2.9.0", "express": "4.*", "express-handlebars": "^3.0.0", "express-negotiate": "0.0.5", @@ -17,32 +17,32 @@ "glob": "^6.0.4", "helmet": "^1.1.0", "highlight.js": "^9.1.0", + "jscs": "^2.11.0", "knex": "^0.10.0", "lodash": "^4.5.0", "markdown-it": "^6.0.0", "moment": "^2.11.2", "morgan": "~1.6.1", "nodemon": "^1.9.0", + "nsp": "^2.4.0", "scrypt": "^6.0.1", "sqlite3": "^3.1.1", "winston": "^2.1.1" }, "description": "A simple blog with built-in code snippet functionality", "devDependencies": { - "chai": "^3.4.1", - "chai-as-promised": "^5.2.0", - "eslint": "1.10.*", - "gulp": "3.9.*", - "gulp-apidoc": "0.2.*", - "gulp-documentation": "2.1.*", - "gulp-eslint": "^2.0.0", - "gulp-istanbul": "^0.10.3", - "gulp-jscs": "3.0.*", - "gulp-mocha": "2.2.*", - "gulp-nsp": "^2.3.0", - "gulp-pipe": "1.0.*", + "apidoc": "^0.15.1", + "chai": "^3.5.0", + "chai-as-promised": "^5.3.0", + "documentation": "^4.0.0-beta1", + "eslint": "^3.0.0", + "globstar": "^1.0.0", "istanbul": "0.4.*", - "mocha": "2.3.*", + "jscs": "^3.0.0", + "mocha": "^2.4.5", + "npm-run-all": "^1.6.0", + "nsp": "^2.2.1", + "parallelshell": "^2.0.0", "pre-commit": "^1.1.2", "sinon": "^1.17.3", "sinon-chai": "^2.8.0", @@ -57,11 +57,11 @@ ], "license": "MIT", "main": "./server.js", - "name": "crispy-train", + "name": "ProgBlog", "pre-commit": { "silent": false, "run": [ - "gulp" + "default" ] }, "repository": { @@ -69,9 +69,18 @@ "url": "https://git.timshomepage.net/timw4mail/ProgBlog.git" }, "scripts": { - "start": "nodemon server.js", - "gulp": "gulp default", - "test": "gulp test" + "audit": "nsp check", + "build": "npm-run-all --parallel lint:src lint:tests docs:src docs:api coverage", + "coverage": "istanbul cover ./node_modules/mocha/bin/_mocha", + "default": "npm-run-all --parallel audit lint:src lint:tests test", + "docs": "npm-run-all --parallel docs:src docs:api", + "docs:api": "apidoc -i ./app/ -o ./public/api-docs/", + "docs:src": "globstar -- documentation build -f html -o public/docs \"app/**/*.js\"", + "lint": "npm-run-all lint:tests lint:src", + "lint:src": "jscs server.js ./app && eslint server.js ./app", + "lint:tests": "jscs ./test && eslint ./test", + "start": "nodemon --debug server.js", + "test": "mocha" }, "version": "0.0.1" }