diff --git a/.eslintrc b/.eslintrc index c943753581..e435aa4148 100644 --- a/.eslintrc +++ b/.eslintrc @@ -13,8 +13,19 @@ }, "parser": "babel-eslint", "plugins": [ - "react" + "react", + "import" ], + "settings": { + "import/ignore": [ + "node_modules", + "\\.json$" + ], + "import/extensions": [ + ".js", + ".jsx" + ] + }, "globals": { "Promise": true, "window": true, @@ -223,9 +234,9 @@ "no-bitwise": 2, "no-plusplus": 0, + "jsx-quotes": [2, "prefer-single"], "react/display-name": 2, "react/jsx-boolean-value": [2, "always"], - "jsx-quotes": [2, "prefer-single"], "react/jsx-no-undef": 2, "react/jsx-sort-props": [2, { "ignoreCase": true }], "react/jsx-uses-react": 2, @@ -237,6 +248,15 @@ "react/react-in-jsx-scope": 2, "react/self-closing-comp": 2, "react/wrap-multilines": 2, - "react/jsx-closing-bracket-location": [ 2, { "selfClosing": "line-aligned", "nonEmpty": "props-aligned" } ] + "react/jsx-closing-bracket-location": [ 2, { "selfClosing": "line-aligned", "nonEmpty": "props-aligned" } ], + + "import/no-unresolved": 2, + "import/named": 2, + "import/namespace": 2, + "import/default": 2, + "import/export": 2, + "import/imports-first": 2, + "import/no-duplicates": 2, + "import/newline-after-import": 2 } } diff --git a/client/rechallenge/transformers.js b/client/rechallenge/transformers.js index 932d4c1eb3..e7993c5f6d 100644 --- a/client/rechallenge/transformers.js +++ b/client/rechallenge/transformers.js @@ -1,5 +1,7 @@ import { Observable } from 'rx'; +/* eslint-disable import/no-unresolved */ import loopProtect from 'loop-protect'; +/* eslint-enable import/no-unresolved */ import { updateContents } from '../../common/utils/polyvinyl'; diff --git a/client/sagas/frame-saga.js b/client/sagas/frame-saga.js index e35d28b15b..c9acf2f0ae 100644 --- a/client/sagas/frame-saga.js +++ b/client/sagas/frame-saga.js @@ -1,5 +1,7 @@ import Rx, { Observable, Subject } from 'rx'; +/* eslint-disable import/no-unresolved */ import loopProtect from 'loop-protect'; +/* eslint-enable import/no-unresolved */ import types from '../../common/app/routes/challenges/redux/types'; import { updateOutput, diff --git a/client/sagas/hard-go-to-saga.js b/client/sagas/hard-go-to-saga.js index b3c9a8a3a7..1d967a265d 100644 --- a/client/sagas/hard-go-to-saga.js +++ b/client/sagas/hard-go-to-saga.js @@ -1,5 +1,6 @@ -import { hardGoTo } from '../../common/app/redux/types'; +import types from '../../common/app/redux/types'; +const { hardGoTo } = types; export default function hardGoToSaga(action$, getState, { history }) { return action$ .filter(({ type }) => type === hardGoTo) diff --git a/client/sagas/window-saga.js b/client/sagas/window-saga.js index add0d4102c..0e3302186d 100644 --- a/client/sagas/window-saga.js +++ b/client/sagas/window-saga.js @@ -1,7 +1,8 @@ import { Observable } from 'rx'; -import { initWindowHeight } from '../../common/app/redux/types'; +import types from '../../common/app/redux/types'; import { updateWindowHeight } from '../../common/app/redux/actions'; +const { initWindowHeight } = types; function getWindowSize(document, window) { const body = document.getElementsByTagName('body')[0]; return window.innerHeight || diff --git a/common/app/redux/actions.js b/common/app/redux/actions.js index 734f568f99..4be119253c 100644 --- a/common/app/redux/actions.js +++ b/common/app/redux/actions.js @@ -32,6 +32,7 @@ export const addUser = createAction( entities => ({ entities }) ); export const updateThisUser = createAction(types.updateThisUser); +export const showSignIn = createAction(types.showSignIn); // updateUserPoints(username: String, points: Number) => Action export const updateUserPoints = createAction( diff --git a/common/app/redux/entities-reducer.js b/common/app/redux/entities-reducer.js index 8bb0a508a0..979fb7ca98 100644 --- a/common/app/redux/entities-reducer.js +++ b/common/app/redux/entities-reducer.js @@ -1,8 +1,6 @@ -import { - updateUserPoints, - updateCompletedChallenges -} from './types'; +import types from './types'; +const { updateUserPoints, updateCompletedChallenges } = types; const initialState = { superBlock: {}, block: {}, diff --git a/common/app/redux/fetch-user-saga.js b/common/app/redux/fetch-user-saga.js index 3bcaf28f43..ba2f6cbc4e 100644 --- a/common/app/redux/fetch-user-saga.js +++ b/common/app/redux/fetch-user-saga.js @@ -1,5 +1,5 @@ import { Observable } from 'rx'; -import { fetchUser } from './types'; +import types from './types'; import { addUser, updateThisUser, @@ -8,6 +8,8 @@ import { showSignIn } from './actions'; +const { fetchUser } = types; + export default function getUserSaga(action$, getState, { services }) { return action$ .filter(action => action.type === fetchUser) diff --git a/common/app/redux/index.js b/common/app/redux/index.js index d0e8381658..90d8e67847 100644 --- a/common/app/redux/index.js +++ b/common/app/redux/index.js @@ -1,6 +1,6 @@ -export { default as reducer } from './reducer'; -export { default as actions } from './actions'; -export { default as types } from './types'; - import fetchUserSaga from './fetch-user-saga'; + +export { default as reducer } from './reducer'; +export * as actions from './actions'; +export { default as types } from './types'; export const sagas = [ fetchUserSaga ]; diff --git a/common/app/routes/challenges/components/project/Forms.jsx b/common/app/routes/challenges/components/project/Forms.jsx index 3e0bde0b49..84edbc6eea 100644 --- a/common/app/routes/challenges/components/project/Forms.jsx +++ b/common/app/routes/challenges/components/project/Forms.jsx @@ -1,5 +1,5 @@ import React, { PropTypes } from 'react'; -import { resetForm, reduxForm } from 'redux-form'; +import { reduxForm } from 'redux-form'; import { Button, FormGroup, @@ -25,7 +25,6 @@ const propTypes = { }; const bindableActions = { - resetForm, submitChallenge, showProjectSubmit }; diff --git a/common/app/routes/challenges/redux/fetch-challenges-saga.js b/common/app/routes/challenges/redux/fetch-challenges-saga.js index 51e8c89911..30ae750610 100644 --- a/common/app/routes/challenges/redux/fetch-challenges-saga.js +++ b/common/app/routes/challenges/redux/fetch-challenges-saga.js @@ -1,10 +1,6 @@ import { Observable } from 'rx'; import { challengeSelector } from './selectors'; -import { - fetchChallenge, - fetchChallenges, - replaceChallenge -} from './types'; +import types from './types'; import { fetchChallengeCompleted, fetchChallengesCompleted, @@ -13,9 +9,11 @@ import { } from './actions'; import { delayedRedirect, - createErrorObserable + createErrorObservable } from '../../../redux/actions'; +const { fetchChallenge, fetchChallenges, replaceChallenge } = types; + function createNameIdMap(entities) { const { challenge } = entities; return { @@ -77,6 +75,6 @@ export default function fetchChallengesSaga(action$, getState, { services }) { initMap(entities, result), ); }) - .catch(createErrorObserable); + .catch(createErrorObservable); }); } diff --git a/common/app/routes/challenges/redux/index.js b/common/app/routes/challenges/redux/index.js index 5d0fc7cc67..ffb98e4e47 100644 --- a/common/app/routes/challenges/redux/index.js +++ b/common/app/routes/challenges/redux/index.js @@ -1,12 +1,12 @@ -export actions from './actions'; -export reducer from './reducer'; -export types from './types'; - import fetchChallengesSaga from './fetch-challenges-saga'; import completionSaga from './completion-saga'; import nextChallengeSaga from './next-challenge-saga'; import answerSaga from './answer-saga'; +export * as actions from './actions'; +export reducer from './reducer'; +export types from './types'; + export projectNormalizer from './project-normalizer'; export const sagas = [ diff --git a/common/app/routes/challenges/redux/next-challenge-saga.js b/common/app/routes/challenges/redux/next-challenge-saga.js index bb2daacd9b..d8008aaa9d 100644 --- a/common/app/routes/challenges/redux/next-challenge-saga.js +++ b/common/app/routes/challenges/redux/next-challenge-saga.js @@ -1,6 +1,6 @@ import { Observable } from 'rx'; import { push } from 'react-router-redux'; -import { moveToNextChallenge } from './types'; +import types from './types'; import { resetUi, updateCurrentChallenge } from './actions'; import { createErrorObservable, makeToast } from '../../../redux/actions'; import { @@ -10,6 +10,8 @@ import { } from '../utils'; import { randomVerb } from '../../../utils/get-words'; +const { moveToNextChallenge } = types; + export default function nextChallengeSaga(actions$, getState) { return actions$ .filter(({ type }) => type === moveToNextChallenge) diff --git a/common/models/User-Identity.js b/common/models/User-Identity.js index 5293dfbc48..962fe0dc16 100644 --- a/common/models/User-Identity.js +++ b/common/models/User-Identity.js @@ -7,8 +7,8 @@ import { getUsernameFromProvider, getSocialProvider } from '../../server/utils/auth'; +import { defaultProfileImage } from '../utils/constantStrings.json'; -const { defaultProfileImage } = require('../utils/constantStrings.json'); const githubRegex = (/github/i); const debug = debugFactory('fcc:models:userIdent'); diff --git a/package.json b/package.json index 3b44b07a15..25728f9d70 100644 --- a/package.json +++ b/package.json @@ -127,6 +127,7 @@ "chunk-manifest-webpack-plugin": "0.1.0", "del": "^2.2.0", "eslint": "^3.1.0", + "eslint-plugin-import": "^1.9.2", "eslint-plugin-react": "^5.1.1", "gulp": "^3.9.0", "gulp-babel": "^6.1.1", diff --git a/server/boot/randomAPIs.js b/server/boot/randomAPIs.js index 1423ca9d5c..3f516a2fb9 100644 --- a/server/boot/randomAPIs.js +++ b/server/boot/randomAPIs.js @@ -1,7 +1,9 @@ import request from 'request'; import constantStrings from '../utils/constantStrings.json'; import testimonials from '../resources/testimonials.json'; -import secrets from '../../config/secrets'; + +const githubClient = process.env.GITHUB_ID; +const githubSecret = process.env.GITHUB_SECRET; module.exports = function(app) { const router = app.loopback.Router(); @@ -258,9 +260,9 @@ module.exports = function(app) { [ 'https://api.github.com/repos/freecodecamp/', 'freecodecamp/pulls?client_id=', - secrets.github.clientID, + githubClient, '&client_secret=', - secrets.github.clientSecret + githubSecret ].join(''), githubHeaders, function(err, status1, pulls) { @@ -273,9 +275,9 @@ module.exports = function(app) { [ 'https://api.github.com/repos/freecodecamp/', 'freecodecamp/issues?client_id=', - secrets.github.clientID, + githubClient, '&client_secret=', - secrets.github.clientSecret + githubSecret ].join(''), githubHeaders, function(err, status2, issues) { diff --git a/server/boot/user.js b/server/boot/user.js index 18c13119a1..19c7d8cc76 100644 --- a/server/boot/user.js +++ b/server/boot/user.js @@ -12,7 +12,11 @@ import { import certTypes from '../utils/certTypes.json'; -import { ifNoUser401, ifNoUserRedirectTo } from '../utils/middleware'; +import { + ifNoUser401, + ifNoUserRedirectTo, + flashIfNotVerified +} from '../utils/middleware'; import { observeQuery } from '../utils/rx'; import { prepUniqueDays, @@ -20,8 +24,6 @@ import { calcLongestStreak } from '../utils/user-stats'; -import { flashIfNotVerified } from '../utils/middleware'; - const debug = debugFactory('fcc:boot:user'); const sendNonUserToMap = ifNoUserRedirectTo('/map'); const certIds = { diff --git a/server/debug-entry.js b/server/debug-entry.js index fba89b5512..f0e969a841 100644 --- a/server/debug-entry.js +++ b/server/debug-entry.js @@ -2,4 +2,5 @@ // or mocha. require('babel-register'); var app = require('./server'); + app.start(); diff --git a/server/middlewares/cookie-parser.js b/server/middlewares/cookie-parser.js index a991b5dc7e..4bcb0cde79 100644 --- a/server/middlewares/cookie-parser.js +++ b/server/middlewares/cookie-parser.js @@ -1,4 +1,4 @@ import cookieParser from 'cookie-parser'; -import secrets from '../../config/secrets'; -export default cookieParser.bind(cookieParser, secrets.cookieSecret); +const cookieSecret = process.env.COOKIE_SECRET; +export default cookieParser.bind(cookieParser, cookieSecret); diff --git a/server/middlewares/jade-helpers.js b/server/middlewares/jade-helpers.js index d1331a35fa..f7672ef9c6 100644 --- a/server/middlewares/jade-helpers.js +++ b/server/middlewares/jade-helpers.js @@ -1,5 +1,7 @@ import manifest from '../rev-manifest'; +/* eslint-disable import/default */ import config from '../../webpack.config'; +/* eslint-enable import/default */ let chunkManifest; try { diff --git a/server/middlewares/sessions.js b/server/middlewares/sessions.js index afd8516720..313f06d608 100644 --- a/server/middlewares/sessions.js +++ b/server/middlewares/sessions.js @@ -1,8 +1,9 @@ import session from 'express-session'; import MongoStoreFactory from 'connect-mongo'; -import secrets from '../../config/secrets'; const MongoStore = MongoStoreFactory(session); +const sessionSecret = process.env.SESSION_SECRET; +const url = process.env.MONGODB || process.env.MONGOHQ_URL; export default function sessionsMiddleware() { return session({ @@ -10,7 +11,7 @@ export default function sessionsMiddleware() { cookie: { maxAge: 900 * 24 * 60 * 60 * 1000 }, resave: true, saveUninitialized: true, - secret: secrets.sessionSecret, - store: new MongoStore({ url: secrets.db }) + secret: sessionSecret, + store: new MongoStore({ url }) }); } diff --git a/server/passport-providers.js b/server/passport-providers.js index 6103a56eaa..f5d1a28dda 100644 --- a/server/passport-providers.js +++ b/server/passport-providers.js @@ -1,7 +1,8 @@ var successRedirect = '/'; var failureRedirect = '/signin'; var linkFailureRedirect = '/account'; -module.exports = { + +export default { local: { provider: 'local', module: 'passport-local', diff --git a/server/production-start.js b/server/production-start.js index 1de0abcc29..9c6d393b97 100644 --- a/server/production-start.js +++ b/server/production-start.js @@ -5,6 +5,7 @@ var startTime = Date.now(); var timeoutHandler; // this is where server starts booting up var app = require('./server'); + console.log('waiting for db to connect'); diff --git a/server/server.js b/server/server.js index 33ea75461a..56afcfabb5 100755 --- a/server/server.js +++ b/server/server.js @@ -1,5 +1,6 @@ require('dotenv').load(); var pmx = require('pmx'); + pmx.init(); var _ = require('lodash'), diff --git a/server/utils/commit.js b/server/utils/commit.js index 0a9474c40a..a7f5d2ba7b 100644 --- a/server/utils/commit.js +++ b/server/utils/commit.js @@ -1,8 +1,8 @@ import dedent from 'dedent'; import debugFactory from 'debug'; import { Observable } from 'rx'; - import commitGoals from './commit-goals.json'; + const debug = debugFactory('fcc:utils/commit'); export { commitGoals }; diff --git a/server/utils/constants.js b/server/utils/constants.js index 49055b3477..37c1f6fc5f 100644 --- a/server/utils/constants.js +++ b/server/utils/constants.js @@ -1,4 +1,4 @@ -exports.blacklistedUsernames = [ +export const blacklistedUsernames = [ 'bonfire', 'account', 'user', diff --git a/server/utils/index.js b/server/utils/index.js index 8f3a80c66d..4b10716908 100644 --- a/server/utils/index.js +++ b/server/utils/index.js @@ -1,101 +1,22 @@ -var cheerio = require('cheerio'), - request = require('request'), - MDNlinks = require('../../seed/bonfireMDNlinks'), - resources = require('./resources.json'); +export function dasherize(name) { + return ('' + name) + .toLowerCase() + .replace(/\s/g, '-') + .replace(/[^a-z0-9\-\.]/gi, '') + .replace(/\:/g, ''); +} -/** - * Cached values - */ +export function nameify(str) { + return ('' + str) + .replace(/[^a-zA-Z0-9\s]/g, '') + .replace(/\:/g, ''); +} -module.exports = { - dasherize: function dasherize(name) { - return ('' + name) - .toLowerCase() - .replace(/\s/g, '-') - .replace(/[^a-z0-9\-\.]/gi, '') - .replace(/\:/g, ''); - }, - - nameify: function nameify(str) { - return ('' + str) - .replace(/[^a-zA-Z0-9\s]/g, '') - .replace(/\:/g, ''); - }, - - unDasherize: function unDasherize(name) { - return ('' + name) - // replace dash with space - .replace(/\-/g, ' ') - // strip nonalphanumarics chars except whitespace - .replace(/[^a-zA-Z\d\s]/g, '') - .trim(); - }, - - randomPhrase: function() { - return resources.phrases[ - Math.floor(Math.random() * resources.phrases.length) - ]; - }, - - randomVerb: function() { - return resources.verbs[ - Math.floor(Math.random() * resources.verbs.length) - ]; - }, - - randomCompliment: function() { - return resources.compliments[ - Math.floor(Math.random() * resources.compliments.length) - ]; - }, - - whichEnvironment: function() { - return process.env.NODE_ENV; - }, - - getURLTitle: function(url, callback) { - var result = { - title: '', - image: '', - url: '', - description: '' - }; - request(url, function(err, response, body) { - if (err || response.statusCode !== 200) { - return callback(new Error('failed')); - } - var $ = cheerio.load(body); - var metaDescription = $("meta[name='description']"); - var metaImage = $("meta[property='og:image']"); - var urlImage = metaImage.attr('content') ? - metaImage.attr('content') : - ''; - - var metaTitle = $('title'); - var description = metaDescription.attr('content') ? - metaDescription.attr('content') : - ''; - - result.title = metaTitle.text().length < 90 ? - metaTitle.text() : - metaTitle.text().slice(0, 87) + '...'; - - result.image = urlImage; - result.description = description; - return callback(null, result); - }); - }, - - getMDNLinks: function(links) { - if (!links) { - return []; - } - // takes in an array of links, which are strings - - // for each key value, push the corresponding link - // from the MDNlinks object into a new array - return links.map(function(value) { - return MDNlinks[value]; - }); - } -}; +export function unDasherize(name) { + return ('' + name) + // replace dash with space + .replace(/\-/g, ' ') + // strip nonalphanumarics chars except whitespace + .replace(/[^a-zA-Z\d\s]/g, '') + .trim(); +}