From bb178d3336ae1e07376c479a9fef1a5d9b129c1f Mon Sep 17 00:00:00 2001 From: Vladimir Kutepov Date: Sat, 12 Mar 2016 23:10:30 +0300 Subject: [PATCH 001/357] Move .eslintrc and .jscsrc in package.json to simplify directory layout - .eslintrc -> package.json/eslintConfig ([ref](http://eslint.org/docs/user-guide/configuring#configuration-file-formats)) - .jscsrc -> package.json/jscsConfig ([ref](http://jscs.info/overview#-config-c)) - .babelrc -> package.json/babel ([ref](https://babeljs.io/docs/usage/babelrc/#use-via-package-json)) --- .eslintrc | 16 ---------------- .jscsrc | 6 ------ package.json | 21 +++++++++++++++++++++ 3 files changed, 21 insertions(+), 22 deletions(-) delete mode 100644 .eslintrc delete mode 100644 .jscsrc diff --git a/.eslintrc b/.eslintrc deleted file mode 100644 index a828fbb28..000000000 --- a/.eslintrc +++ /dev/null @@ -1,16 +0,0 @@ -{ - "parser": "babel-eslint", - "extends": "airbnb", - "globals": { - "__DEV__": true - }, - "env": { - "browser": true, - "jest": true - }, - "rules": { - "no-confusing-arrow": 0, - "react/jsx-quotes": 0, - "jsx-quotes": [2, "prefer-double"] - } -} diff --git a/.jscsrc b/.jscsrc deleted file mode 100644 index bad58619f..000000000 --- a/.jscsrc +++ /dev/null @@ -1,6 +0,0 @@ -{ - "preset": "airbnb", - "excludeFiles": ["build/**", "node_modules/**"], - "validateQuoteMarks": null, - "disallowSpacesInsideTemplateStringPlaceholders": null -} diff --git a/package.json b/package.json index 7bd027fc4..c6c88c237 100644 --- a/package.json +++ b/package.json @@ -95,6 +95,27 @@ "stage-0" ] }, + "eslintConfig": { + "parser": "babel-eslint", + "extends": "airbnb", + "globals": { + "__DEV__": true + }, + "env": { + "browser": true, + "jest": true + }, + "rules": { + "no-confusing-arrow": 0, + "react/jsx-quotes": 0, + "jsx-quotes": [2, "prefer-double"] + } + }, + "jscsConfig": { + "preset": "airbnb", + "excludeFiles": ["build/**", "node_modules/**"], + "disallowSpacesInsideTemplateStringPlaceholders": null + }, "jest": { "rootDir": "./src", "testPathDirs": [ From eb7cc4751c0cd197d4e1099c1bcbd3f37ce871ee Mon Sep 17 00:00:00 2001 From: manu Date: Mon, 21 Mar 2016 13:55:46 +1100 Subject: [PATCH 002/357] Combining NotFoundPage into ErrorPage component, minor error CSS change for smaller media query --- src/components/ErrorPage/ErrorPage.js | 12 ++++- src/components/ErrorPage/ErrorPage.scss | 29 +++++----- src/components/NotFoundPage/NotFoundPage.js | 39 -------------- src/components/NotFoundPage/NotFoundPage.scss | 53 ------------------- src/components/NotFoundPage/package.json | 6 --- src/routes.js | 6 +-- src/views/error.jade | 34 +++++------- 7 files changed, 38 insertions(+), 141 deletions(-) delete mode 100644 src/components/NotFoundPage/NotFoundPage.js delete mode 100644 src/components/NotFoundPage/NotFoundPage.scss delete mode 100644 src/components/NotFoundPage/package.json diff --git a/src/components/ErrorPage/ErrorPage.js b/src/components/ErrorPage/ErrorPage.js index ffb07a5ed..bafe76299 100644 --- a/src/components/ErrorPage/ErrorPage.js +++ b/src/components/ErrorPage/ErrorPage.js @@ -11,7 +11,8 @@ import React, { Component, PropTypes } from 'react'; import withStyles from 'isomorphic-style-loader/lib/withStyles'; import s from './ErrorPage.scss'; -const title = 'Error'; +let title = 'Error'; +let content = 'Sorry, a critical error occurred on this page.'; class ErrorPage extends Component { @@ -25,10 +26,17 @@ class ErrorPage extends Component { } render() { + console.log(this.props); + if (this.props.statusCode !== undefined) + if (this.props.statusCode === 404) { + title = 'Page Not Found'; + content = 'Sorry, the page you were trying to view does not exist.'; + } + return (

{title}

-

Sorry, an critical error occurred on this page.

+

{content}

); } diff --git a/src/components/ErrorPage/ErrorPage.scss b/src/components/ErrorPage/ErrorPage.scss index 9764560c9..2e4282b43 100644 --- a/src/components/ErrorPage/ErrorPage.scss +++ b/src/components/ErrorPage/ErrorPage.scss @@ -8,8 +8,9 @@ */ * { + padding: 0; margin: 0; - line-height: 1.2; + line-height: 1.4; } html { @@ -17,38 +18,34 @@ html { width: 100%; height: 100%; color: #888; + font-size: 16px; text-align: center; font-family: sans-serif; } - body { display: table-cell; - margin: 2em auto; + padding: 0.625rem; vertical-align: middle; } h1 { color: #555; font-weight: 400; - font-size: 2em; + font-size: 2rem; + line-height: 1.2; + margin-bottom: 1rem; } - p { margin: 0 auto; - width: 280px; + max-width: 280px; +} +pre { + text-align: left; + margin-top: 2rem; } @media only screen and (max-width: 280px) { - - body, - p { - width: 95%; - } - h1 { - font-size: 1.5em; - margin: 0 0 0.3em; - + font-size: 1.5rem; } - } diff --git a/src/components/NotFoundPage/NotFoundPage.js b/src/components/NotFoundPage/NotFoundPage.js deleted file mode 100644 index 71234823f..000000000 --- a/src/components/NotFoundPage/NotFoundPage.js +++ /dev/null @@ -1,39 +0,0 @@ -/** - * React Starter Kit (https://www.reactstarterkit.com/) - * - * Copyright © 2014-2016 Kriasoft, LLC. All rights reserved. - * - * This source code is licensed under the MIT license found in the - * LICENSE.txt file in the root directory of this source tree. - */ - -import React, { Component, PropTypes } from 'react'; -import withStyles from 'isomorphic-style-loader/lib/withStyles'; -import s from './NotFoundPage.scss'; - -const title = 'Page Not Found'; - -class NotFoundPage extends Component { - - static contextTypes = { - onSetTitle: PropTypes.func.isRequired, - onPageNotFound: PropTypes.func.isRequired, - }; - - componentWillMount() { - this.context.onSetTitle(title); - this.context.onPageNotFound(); - } - - render() { - return ( -
-

{title}

-

Sorry, but the page you were trying to view does not exist.

-
- ); - } - -} - -export default withStyles(NotFoundPage, s); diff --git a/src/components/NotFoundPage/NotFoundPage.scss b/src/components/NotFoundPage/NotFoundPage.scss deleted file mode 100644 index f1f922776..000000000 --- a/src/components/NotFoundPage/NotFoundPage.scss +++ /dev/null @@ -1,53 +0,0 @@ -/** - * React Starter Kit (https://www.reactstarterkit.com/) - * - * Copyright © 2014-2016 Kriasoft, LLC. All rights reserved. - * - * This source code is licensed under the MIT license found in the - * LICENSE.txt file in the root directory of this source tree. - */ - -* { - margin: 0; - line-height: 1.2; -} - -html { - display: table; - width: 100%; - height: 100%; - color: #888; - text-align: center; - font-family: sans-serif; -} - -body { - display: table-cell; - margin: 2em auto; - vertical-align: middle; -} - -h1 { - color: #555; - font-weight: 400; - font-size: 2em; -} - -p { - margin: 0 auto; - width: 280px; -} - -@media only screen and (max-width: 280px) { - - body, - p { - width: 95%; - } - - h1 { - font-size: 1.5em; - margin: 0 0 0.3em; - } - -} diff --git a/src/components/NotFoundPage/package.json b/src/components/NotFoundPage/package.json deleted file mode 100644 index f3adaaf4c..000000000 --- a/src/components/NotFoundPage/package.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "name": "NotFoundPage", - "version": "0.0.0", - "private": true, - "main": "./NotFoundPage.js" -} diff --git a/src/routes.js b/src/routes.js index 132787d65..e82c5fd2d 100644 --- a/src/routes.js +++ b/src/routes.js @@ -12,7 +12,6 @@ import Router from 'react-routing/src/Router'; import fetch from './core/fetch'; import App from './components/App'; import ContentPage from './components/ContentPage'; -import NotFoundPage from './components/NotFoundPage'; import ErrorPage from './components/ErrorPage'; const routes = [ @@ -39,9 +38,8 @@ const router = new Router(on => { return data && data.content && ; }); - on('error', (state, error) => state.statusCode === 404 ? - : - + on('error', (state, error) => + ); }); diff --git a/src/views/error.jade b/src/views/error.jade index a44f6bdcd..4582f3c10 100644 --- a/src/views/error.jade +++ b/src/views/error.jade @@ -5,55 +5,47 @@ html(lang="en") title Internal Server Error meta(name="viewport", content="width=device-width, initial-scale=1") style. - * { - line-height: 1.2; + padding: 0; margin: 0; + line-height: 1.4; } html { - color: #888; display: table; - font-family: sans-serif; + width: 100%; height: 100%; + color: #888; + font-size: 16px; text-align: center; - width: 100%; + font-family: sans-serif; } - body { display: table-cell; + padding: 0.625rem; vertical-align: middle; - margin: 2em auto; } h1 { color: #555; - font-size: 2em; font-weight: 400; + font-size: 2rem; + line-height: 1.2; + margin-bottom: 1rem; } - p { margin: 0 auto; - width: 280px; + max-width: 280px; } - pre { text-align: left; - max-width: 1000px; - margin: 0 auto; + margin-top: 2rem; } @media only screen and (max-width: 280px) { - - body, p { - width: 95%; - } - h1 { - font-size: 1.5em; - margin: 0 0 0.3em; + font-size: 1.5rem; } - } body From 878315a69f3809462747fce8257296a86be816e6 Mon Sep 17 00:00:00 2001 From: m4recek Date: Tue, 22 Mar 2016 23:03:07 +0100 Subject: [PATCH 003/357] Adds a hint for a problem with autoreloading when Webstorm "safe write" feature is enabled Autoreload functionality is not picking up file changes when Webstorm "safe write" feature is enabled --- docs/how-to-configure-text-editors.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/how-to-configure-text-editors.md b/docs/how-to-configure-text-editors.md index 38750cf43..a3573fe86 100644 --- a/docs/how-to-configure-text-editors.md +++ b/docs/how-to-configure-text-editors.md @@ -25,6 +25,8 @@ Enable **CSSComb** by installing CSSReorder plug-in ![csscomb-in-webstorm](https://dl.dropboxusercontent.com/u/16006521/react-starter-kit/webstorm-csscomb.png) +**If you have trouble with autoreloading** try to disable "safe write" in `File > Settings > System Settings > Use "safe write" (save chnages to a temporary file first)` + ### Atom Install atom packages From bbdff351d5a0c1dfba2ceead49e5111b53211610 Mon Sep 17 00:00:00 2001 From: whynot123 Date: Sun, 27 Mar 2016 15:56:15 +0300 Subject: [PATCH 004/357] Update tools/run.js to print options if present --- tools/run.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tools/run.js b/tools/run.js index 25584d297..4f62b175b 100644 --- a/tools/run.js +++ b/tools/run.js @@ -14,11 +14,15 @@ function format(time) { function run(fn, options) { const task = typeof fn.default === 'undefined' ? fn : fn.default; const start = new Date(); - console.log(`[${format(start)}] Starting '${task.name}'...`); + console.log( + `[${format(start)}] Starting '${task.name}${options ? `(${options})` : ''}'...` + ); return task(options).then(() => { const end = new Date(); const time = end.getTime() - start.getTime(); - console.log(`[${format(end)}] Finished '${task.name}' after ${time} ms`); + console.log( + `[${format(end)}] Finished '${task.name}${options ? `(${options})` : ''}' after ${time} ms` + ); }); } From e550786dbf40d1f968b5fc9055fdf7760367782e Mon Sep 17 00:00:00 2001 From: Rafael Arquero Gimeno Date: Sun, 27 Mar 2016 20:00:16 +0200 Subject: [PATCH 005/357] Introduce Sequelize as ORM. Facebook login works as espected. --- package.json | 2 + src/config.js | 2 +- src/core/db.js | 70 ++--------------- src/core/passport.js | 139 ++++++++++++++++++--------------- src/data/models/User.js | 16 ++++ src/data/models/UserClaim.js | 10 +++ src/data/models/UserLogin.js | 10 +++ src/data/models/UserProfile.js | 11 +++ src/data/models/index.js | 22 ++++++ 9 files changed, 154 insertions(+), 128 deletions(-) create mode 100644 src/data/models/User.js create mode 100644 src/data/models/UserClaim.js create mode 100644 src/data/models/UserLogin.js create mode 100644 src/data/models/UserProfile.js create mode 100644 src/data/models/index.js diff --git a/package.json b/package.json index d33eb873b..5f9827df2 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ "react": "15.0.0-rc.2", "react-dom": "15.0.0-rc.2", "react-routing": "0.0.7", + "sequelize": "^3.20.0", "source-map-support": "0.4.0", "whatwg-fetch": "0.11.0" }, @@ -83,6 +84,7 @@ "redbox-react": "^1.2.2", "stylelint": "^5.2.1", "stylelint-config-standard": "^4.0.1", + "sqlite3": "^3.1.2", "url-loader": "^0.5.7", "webpack": "^1.12.14", "webpack-hot-middleware": "^2.10.0", diff --git a/src/config.js b/src/config.js index fe6ebc021..7ead528bf 100644 --- a/src/config.js +++ b/src/config.js @@ -13,7 +13,7 @@ export const port = process.env.PORT || 3000; export const host = process.env.WEBSITE_HOSTNAME || `localhost:${port}`; -export const databaseUrl = process.env.DATABASE_URL || 'postgresql://demo:Lqk62xg6TBm5UhfR@demo.ctbl5itzitm4.us-east-1.rds.amazonaws.com:5432/membership01'; +export const databaseUrl = process.env.DATABASE_URL || 'sqlite:database.sqlite'; export const analytics = { diff --git a/src/core/db.js b/src/core/db.js index ea311112d..9b0c4309a 100644 --- a/src/core/db.js +++ b/src/core/db.js @@ -7,75 +7,17 @@ * LICENSE.txt file in the root directory of this source tree. */ -import db from 'pg'; -import Promise from 'bluebird'; +import Sequelize from 'sequelize'; import { databaseUrl } from '../config'; // TODO: Customize database connection settings /* jscs:disable requireCamelCaseOrUpperCaseIdentifiers */ -db.defaults.ssl = true; -db.defaults.poolSize = 2; -db.defaults.application_name = 'RSK'; -/* jscs:enable requireCamelCaseOrUpperCaseIdentifiers */ - -/** - * Promise-based wrapper for pg.Client - * https://github.com/brianc/node-postgres/wiki/Client - */ -function AsyncClient(client) { - this.client = client; - this.query = this.query.bind(this); - this.end = this.end.bind(this); -} - -AsyncClient.prototype.query = function query(sql, ...args) { - return new Promise((resolve, reject) => { - if (args.length) { - this.client.query(sql, args, (err, result) => { - if (err) { - reject(err); - } else { - resolve(result); - } - }); - } else { - this.client.query(sql, (err, result) => { - if (err) { - reject(err); - } else { - resolve(result); - } - }); - } - }); -}; -AsyncClient.prototype.end = function end() { - this.client.end(); -}; - -/** - * Promise-based wrapper for pg.connect() - * https://github.com/brianc/node-postgres/wiki/pg - */ -db.connect = (connect => callback => new Promise((resolve, reject) => { - connect.call(db, databaseUrl, (err, client, done) => { - if (err) { - if (client) { - done(client); - } +// db.defaults.ssl = true; +// db.defaults.poolSize = 2; +// db.defaults.application_name = 'RSK'; +/* jscs:enable requireCamelCaseOrUpperCaseIdentifiers */ - reject(err); - } else { - callback(new AsyncClient(client)).then(() => { - done(); - resolve(); - }).catch(error => { - done(client); - reject(error); - }); - } - }); -}))(db.connect); +const db = new Sequelize(databaseUrl); export default db; diff --git a/src/core/passport.js b/src/core/passport.js index 88fe572c8..4d25245b4 100644 --- a/src/core/passport.js +++ b/src/core/passport.js @@ -15,7 +15,7 @@ import passport from 'passport'; import { Strategy as FacebookStrategy } from 'passport-facebook'; -import db from './db'; +import { User, UserLogin, UserClaim, UserProfile } from '../data/models'; import { auth as config } from '../config'; /** @@ -29,83 +29,96 @@ passport.use(new FacebookStrategy({ passReqToCallback: true, }, (req, accessToken, refreshToken, profile, done) => { const loginName = 'facebook'; - db.connect(async ({ query }) => { + const claimType = 'urn:facebook:access_token'; + const fooBar = async () => { if (req.user) { - let result = await query( - 'SELECT 1 FROM user_login WHERE name = $1 AND key = $2', - loginName, profile.id - ); - if (result.rowCount) { + const userLogin = await UserLogin.findOne({ + attributes: ['id'], + where: { name: loginName, key: profile.id }, + }); + if (userLogin) { // There is already a Facebook account that belongs to you. // Sign in with that account or delete it, then link it with your current account. done(); } else { - await query(` - INSERT INTO user_account (id, email) SELECT $1, $2::character - WHERE NOT EXISTS (SELECT 1 FROM user_account WHERE id = $1);`, - req.user.id, profile._json.email); - await query(` - INSERT INTO user_login (user_id, name, key) VALUES ($1, 'facebook', $2);`, - req.user.id, profile.id); - await query(` - INSERT INTO user_claim (user_id, type, value) VALUES - ($1, 'urn:facebook:access_token', $3);`, - req.user.id, profile.id); - await query(` - INSERT INTO user_profile (user_id) SELECT $1 - WHERE NOT EXISTS (SELECT 1 FROM user_profile WHERE user_id = $1);`, - req.user.id); - await query(` - UPDATE user_profile SET - display_name = COALESCE(NULLIF(display_name, ''), $2), - gender = COALESCE(NULLIF(gender, ''), $3), - picture = COALESCE(NULLIF(picture, ''), $4), - WHERE user_id = $1;`, - req.user.id, profile.displayName, profile._json.gender, - `https://graph.facebook.com/${profile.id}/picture?type=large`); - result = await query(` - SELECT id, email FROM user_account WHERE id = $1;`, - req.user.id); - done(null, result.rows[0]); + const user = await User.create({ + id: req.user.id, + email: profile._json.email, + logins: [ + { name: loginName, key: profile.id }, + ], + claims: [ + { type: claimType, value: profile.id }, + ], + profile: { + displayName: profile.displayName, + gender: profile._json.gender, + picture: `https://graph.facebook.com/${profile.id}/picture?type=large`, + }, + }, { + include: [ + { model: UserLogin, as: 'logins' }, + { model: UserClaim, as: 'claims' }, + { model: UserProfile, as: 'profile' }, + ], + }); + done(null, { + id: user.id, + email: user.email, + }); } } else { - let result = await query(` - SELECT u.id, u.email FROM user_account AS u - LEFT JOIN user_login AS l ON l.user_id = u.id - WHERE l.name = $1 AND l.key = $2`, loginName, profile.id); - if (result.rowCount) { - done(null, result.rows[0]); + const users = await User.findAll({ + attributes: ['id', 'email'], + where: { '$logins.name$': loginName, '$logins.key$': profile.id }, + include: [ + { + attributes: ['name', 'key'], + model: UserLogin, + as: 'logins', + required: true, + }, + ], + }); + if (users.length) { + done(null, users[0]); } else { - result = await query('SELECT 1 FROM user_account WHERE email = $1', profile._json.email); - if (result.rowCount) { + let user = await User.findOne({ where: { email: profile._json.email } }); + if (user) { // There is already an account using this email address. Sign in to // that account and link it with Facebook manually from Account Settings. done(null); } else { - result = await query(` - INSERT INTO user_account (email) VALUES ($1) RETURNING (id)`, - profile._json.email - ); - const userId = result.rows[0].id; - await query(` - INSERT INTO user_login (user_id, name, key) VALUES ($1, 'facebook', $2)`, - userId, profile.id); - await query(` - INSERT INTO user_claim (user_id, type, value) VALUES - ($1, 'urn:facebook:access_token', $2);`, - userId, accessToken); - await query(` - INSERT INTO user_profile (user_id, display_name, gender, picture) - VALUES ($1, $2, $3, $4);`, - userId, profile.displayName, profile._json.gender, - `https://graph.facebook.com/${profile.id}/picture?type=large` - ); - result = await query('SELECT id, email FROM user_account WHERE id = $1;', userId); - done(null, result.rows[0]); + user = await User.create({ + email: profile._json.email, + logins: [ + { name: loginName, key: profile.id }, + ], + claims: [ + { type: claimType, value: accessToken }, + ], + profile: { + displaynName: profile.displayName, + gender: profile._json.gender, + picture: `https://graph.facebook.com/${profile.id}/picture?type=large`, + }, + }, { + include: [ + { model: UserLogin, as: 'logins' }, + { model: UserClaim, as: 'claims' }, + { model: UserProfile, as: 'profile' }, + ], + }); + done(null, { + id: user.id, + email: user.email, + }); } } } - }).catch(done); + }; + + fooBar().catch(done); })); export default passport; diff --git a/src/data/models/User.js b/src/data/models/User.js new file mode 100644 index 000000000..c7978e7dc --- /dev/null +++ b/src/data/models/User.js @@ -0,0 +1,16 @@ +export default (sequelize, DataTypes) => sequelize.define('User', { + email: { + type: DataTypes.STRING, + validate: { + isEmail: true, + }, + }, +}, { + classMethods: { + associate({ User, UserLogin, UserClaim, UserProfile }) { + User.hasMany(UserLogin, { as: 'logins' }); + User.hasMany(UserClaim, { as: 'claims' }); + User.hasOne(UserProfile, { as: 'profile' }); + }, + }, +}); diff --git a/src/data/models/UserClaim.js b/src/data/models/UserClaim.js new file mode 100644 index 000000000..d51fdeb4c --- /dev/null +++ b/src/data/models/UserClaim.js @@ -0,0 +1,10 @@ +export default (sequelize, DataTypes) => sequelize.define('UserClaim', { + type: DataTypes.STRING, // 'urn:facebook:access_token' + value: DataTypes.INTEGER, // profile.id +}, { + classMethods: { + associate({ User, UserClaim }) { + UserClaim.belongsTo(User); + }, + }, +}); diff --git a/src/data/models/UserLogin.js b/src/data/models/UserLogin.js new file mode 100644 index 000000000..251a0f287 --- /dev/null +++ b/src/data/models/UserLogin.js @@ -0,0 +1,10 @@ +export default (sequelize, DataTypes) => sequelize.define('UserLogin', { + name: DataTypes.STRING, // loginName + key: DataTypes.INTEGER, // profile.id +}, { + classMethods: { + associate({ User, UserLogin }) { + UserLogin.belongsTo(User); + }, + }, +}); diff --git a/src/data/models/UserProfile.js b/src/data/models/UserProfile.js new file mode 100644 index 000000000..0926d4409 --- /dev/null +++ b/src/data/models/UserProfile.js @@ -0,0 +1,11 @@ +export default (sequelize, DataTypes) => sequelize.define('UserProfile', { + displayName: DataTypes.INTEGER, // profile.displayName + gender: DataTypes.INTEGER, // profile._json.gender + picture: DataTypes.INTEGER, // `https://graph.facebook.com/${profile.id}/picture?type=large` +}, { + classMethods: { + associate({ User, UserProfile }) { + UserProfile.belongsTo(User); + }, + }, +}); diff --git a/src/data/models/index.js b/src/data/models/index.js new file mode 100644 index 000000000..24f0bb565 --- /dev/null +++ b/src/data/models/index.js @@ -0,0 +1,22 @@ +import Sequelize from 'sequelize'; +import db from '../../core/db'; + +const models = { + User: require('./User').default(db, Sequelize), + UserLogin: require('./UserLogin').default(db, Sequelize), + UserClaim: require('./UserClaim').default(db, Sequelize), + UserProfile: require('./UserProfile').default(db, Sequelize), +}; + +Object.keys(models).forEach((modelName) => { + if (models[modelName].associate) { + models[modelName].associate(models); + } +}); + +db.sync(); + +export const User = models.User; +export const UserLogin = models.UserLogin; +export const UserClaim = models.UserClaim; +export const UserProfile = models.UserProfile; From 314e6a3cb06161d998b360927fa1ede56eb3786e Mon Sep 17 00:00:00 2001 From: Hieu Tran <658655@gmail.com> Date: Tue, 29 Mar 2016 00:44:16 +0700 Subject: [PATCH 006/357] Remove redundant commas in SQL statement --- src/core/passport.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/passport.js b/src/core/passport.js index 88fe572c8..5fff062c7 100644 --- a/src/core/passport.js +++ b/src/core/passport.js @@ -59,7 +59,7 @@ passport.use(new FacebookStrategy({ UPDATE user_profile SET display_name = COALESCE(NULLIF(display_name, ''), $2), gender = COALESCE(NULLIF(gender, ''), $3), - picture = COALESCE(NULLIF(picture, ''), $4), + picture = COALESCE(NULLIF(picture, ''), $4) WHERE user_id = $1;`, req.user.id, profile.displayName, profile._json.gender, `https://graph.facebook.com/${profile.id}/picture?type=large`); From 258741a6911c1fe4cb9d12fbe828aa8cddfae280 Mon Sep 17 00:00:00 2001 From: Konstantin Tarkus Date: Sat, 2 Apr 2016 17:10:07 +0300 Subject: [PATCH 007/357] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c71c6d3a4..76c2626a4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ All notable changes to this project will be documented in this file. ### [Unreleased][unreleased] +- Move Babel, ESLint and JSCS configurations to `pacakge.json` [#497](https://github.com/kriasoft/react-starter-kit/pull/497) - Convert `Feedback`, `Footer`, `Header`, and `Navigation` to functional stateless components - Move page / screen components into the `src/routes` folder along with the routing information for them [BREAKING CHANGE]. [6553936](https://github.com/kriasoft/react-starter-kit/commit/6553936e693e24a8ac6178f4962af15e0ea87dfd) From 540db65aadce80b32fa05421e52923741fccb3c1 Mon Sep 17 00:00:00 2001 From: Olivier Tassinari Date: Mon, 4 Apr 2016 22:04:50 +0200 Subject: [PATCH 008/357] [Babel] Improve the production build --- package.json | 3 +++ tools/webpack.config.js | 17 ++++++++++++++--- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 87900b8e5..a09e32cab 100644 --- a/package.json +++ b/package.json @@ -46,6 +46,9 @@ "babel-jest": "^9.0.3", "babel-loader": "^6.2.4", "babel-plugin-react-transform": "^2.0.2", + "babel-plugin-transform-react-constant-elements": "^6.5.0", + "babel-plugin-transform-react-inline-elements": "^6.6.5", + "babel-plugin-transform-react-remove-prop-types": "^0.2.4", "babel-plugin-transform-runtime": "^6.6.0", "babel-preset-es2015": "^6.6.0", "babel-preset-node5": "^11.0.1", diff --git a/tools/webpack.config.js b/tools/webpack.config.js index f13a21907..3bc92acde 100644 --- a/tools/webpack.config.js +++ b/tools/webpack.config.js @@ -34,6 +34,19 @@ const GLOBALS = { // client-side (client.js) and server-side (server.js) bundles // ----------------------------------------------------------------------------- +let babelLoaderPlugins = [ + 'transform-runtime', +]; + +if (!DEBUG) { + babelLoaderPlugins = [ + ...babelLoaderPlugins, + 'transform-react-remove-prop-types', + 'transform-react-constant-elements', + 'transform-react-inline-elements', + ]; +} + const config = { context: path.resolve(__dirname, '../src'), @@ -63,9 +76,7 @@ const config = { 'es2015', 'stage-0', ], - plugins: [ - 'transform-runtime', - ], + plugins: babelLoaderPlugins, }, }, { From 00346331a7152d561febfe5a64638f55e41d39c6 Mon Sep 17 00:00:00 2001 From: Zane Hitchcox Date: Tue, 5 Apr 2016 23:40:30 -0500 Subject: [PATCH 009/357] clean config.entry This accomplishes the same thing, with fewer lines of code --- tools/start.js | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/tools/start.js b/tools/start.js index 173ad69ed..754ff386c 100644 --- a/tools/start.js +++ b/tools/start.js @@ -31,12 +31,7 @@ async function start() { // to enable Hot Module Replacement (HMR) and React Transform webpackConfig.filter(x => x.target !== 'node').forEach(config => { /* eslint-disable no-param-reassign */ - if (Array.isArray(config.entry)) { - config.entry.unshift('webpack-hot-middleware/client'); - } else { - config.entry = ['webpack-hot-middleware/client', config.entry]; - } - + config.entry = ['webpack-hot-middleware/client'].concat(config.entry); config.output.filename = config.output.filename.replace('[chunkhash]', '[hash]'); config.output.chunkFilename = config.output.chunkFilename.replace('[chunkhash]', '[hash]'); config.plugins.push(new webpack.HotModuleReplacementPlugin()); From cfe08ae8c54a12c8fb865fe657cebdc3b8d2c558 Mon Sep 17 00:00:00 2001 From: Ashley Street Date: Wed, 6 Apr 2016 18:53:01 +0100 Subject: [PATCH 010/357] Correcting Typo Found a small typo, corrected for you. --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 76c2626a4..4ce2f8b86 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ All notable changes to this project will be documented in this file. ### [Unreleased][unreleased] -- Move Babel, ESLint and JSCS configurations to `pacakge.json` [#497](https://github.com/kriasoft/react-starter-kit/pull/497) +- Move Babel, ESLint and JSCS configurations to `package.json` [#497](https://github.com/kriasoft/react-starter-kit/pull/497) - Convert `Feedback`, `Footer`, `Header`, and `Navigation` to functional stateless components - Move page / screen components into the `src/routes` folder along with the routing information for them [BREAKING CHANGE]. [6553936](https://github.com/kriasoft/react-starter-kit/commit/6553936e693e24a8ac6178f4962af15e0ea87dfd) From ff5016fd2f52ae9d852d7ebf640ce108f05342b7 Mon Sep 17 00:00:00 2001 From: Konstantin Tarkus Date: Fri, 8 Apr 2016 23:16:08 +0300 Subject: [PATCH 011/357] Update routing library and the list of routes --- CHANGELOG.md | 5 ++ package.json | 58 ++++++++------- src/client.js | 74 +++++++++++-------- src/components/App/App.js | 15 ++-- src/components/ContentPage/package.json | 6 -- src/components/ErrorPage/ErrorPage.js | 48 ------------ src/components/ErrorPage/package.json | 6 -- src/routes.js | 46 ------------ src/routes/contact/Contact.js | 7 +- src/routes/contact/index.js | 13 ++-- .../content/Content.js} | 16 ++-- .../content/Content.scss} | 2 +- src/routes/content/index.js | 36 +++++++++ src/routes/error/ErrorPage.js | 40 ++++++++++ .../ErrorPage => routes/error}/ErrorPage.scss | 5 +- src/routes/error/index.js | 27 +++++++ src/routes/home/Home.js | 6 +- src/routes/home/index.js | 27 +++++-- src/routes/index.js | 42 +++++++++++ src/routes/login/Login.js | 7 +- src/routes/login/index.js | 13 ++-- src/routes/register/Register.js | 7 +- src/routes/register/index.js | 13 ++-- src/server.js | 30 +++++--- 24 files changed, 327 insertions(+), 222 deletions(-) delete mode 100644 src/components/ContentPage/package.json delete mode 100644 src/components/ErrorPage/ErrorPage.js delete mode 100644 src/components/ErrorPage/package.json delete mode 100644 src/routes.js rename src/{components/ContentPage/ContentPage.js => routes/content/Content.js} (80%) rename src/{components/ContentPage/ContentPage.scss => routes/content/Content.scss} (90%) create mode 100644 src/routes/content/index.js create mode 100644 src/routes/error/ErrorPage.js rename src/{components/ErrorPage => routes/error}/ErrorPage.scss (92%) create mode 100644 src/routes/error/index.js create mode 100644 src/routes/index.js diff --git a/CHANGELOG.md b/CHANGELOG.md index 4ce2f8b86..aaaf26af6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,11 @@ All notable changes to this project will be documented in this file. ### [Unreleased][unreleased] +- Rename `onSetTitle`, `onSetMeta` context variables to `setTitle`, `setMeta` +- Move `Content` component to `src/routes/content` +- Move `ErrorPage` component to `src/routes/error` +- Move the list of top-level routes to `src/routes/index` +- Update routing to use `universal-router` library - Move Babel, ESLint and JSCS configurations to `package.json` [#497](https://github.com/kriasoft/react-starter-kit/pull/497) - Convert `Feedback`, `Footer`, `Header`, and `Navigation` to functional stateless components - Move page / screen components into the `src/routes` folder along with the routing information for them [BREAKING CHANGE]. [6553936](https://github.com/kriasoft/react-starter-kit/commit/6553936e693e24a8ac6178f4962af15e0ea87dfd) diff --git a/package.json b/package.json index a09e32cab..3318fa63a 100644 --- a/package.json +++ b/package.json @@ -11,45 +11,45 @@ "body-parser": "1.15.0", "classnames": "2.2.3", "cookie-parser": "1.4.1", - "core-js": "2.2.1", + "core-js": "2.2.2", "eventemitter3": "1.2.0", "express": "4.13.4", "express-graphql": "0.4.13", "express-jwt": "3.3.0", "fastclick": "1.0.6", - "fbjs": "0.8.0-alpha.3", - "front-matter": "2.0.6", - "graphiql": "0.6.6", - "graphql": "0.4.18", + "fbjs": "0.8.0", + "front-matter": "2.0.7", + "graphiql": "0.7.0", + "graphql": "0.5.0", "history": "2.0.1", "isomorphic-style-loader": "0.0.12", "jade": "1.11.0", "jsonwebtoken": "5.7.0", - "markdown-it": "6.0.0", - "node-fetch": "1.4.1", + "markdown-it": "6.0.1", + "node-fetch": "1.5.0", "normalize.css": "4.0.0", "passport": "0.3.2", "passport-facebook": "2.1.0", - "pg": "4.5.1", + "pg": "4.5.2", "pretty-error": "2.0.0", - "react": "15.0.0-rc.2", - "react-dom": "15.0.0-rc.2", - "react-routing": "0.0.7", + "react": "15.0.1", + "react-dom": "15.0.1", "source-map-support": "0.4.0", + "universal-router": "1.1.0-beta.3", "whatwg-fetch": "0.11.0" }, "devDependencies": { "assets-webpack-plugin": "^3.4.0", - "autoprefixer": "^6.3.4", - "babel-cli": "^6.6.5", - "babel-eslint": "^6.0.0", - "babel-jest": "^9.0.3", + "autoprefixer": "^6.3.6", + "babel-cli": "^6.7.5", + "babel-eslint": "^6.0.2", + "babel-jest": "^10.0.1", "babel-loader": "^6.2.4", "babel-plugin-react-transform": "^2.0.2", "babel-plugin-transform-react-constant-elements": "^6.5.0", "babel-plugin-transform-react-inline-elements": "^6.6.5", "babel-plugin-transform-react-remove-prop-types": "^0.2.4", - "babel-plugin-transform-runtime": "^6.6.0", + "babel-plugin-transform-runtime": "^6.7.5", "babel-preset-es2015": "^6.6.0", "babel-preset-node5": "^11.0.1", "babel-preset-react": "^6.5.0", @@ -58,10 +58,10 @@ "css-loader": "^0.23.1", "del": "^2.2.0", "enzyme": "^2.2.0", - "eslint": "^2.5.1", + "eslint": "^2.7.0", "eslint-config-airbnb": "^6.2.0", "eslint-loader": "^1.3.0", - "eslint-plugin-react": "^4.2.3", + "eslint-plugin-react": "^4.3.0", "estraverse-fb": "^1.3.1", "extend": "^3.0.0", "file-loader": "^0.8.5", @@ -69,23 +69,23 @@ "git-repository": "^0.1.4", "glob": "^7.0.3", "jade-loader": "^0.8.0", - "jest-cli": "^0.9.2", + "jest-cli": "^0.10.0", "jscs": "^2.11.0", "json-loader": "^0.5.4", "mkdirp": "^0.5.1", "ncp": "^2.0.0", "postcss": "^5.0.19", - "postcss-import": "^8.0.2", + "postcss-import": "^8.1.0", "postcss-loader": "^0.8.2", "postcss-scss": "^0.1.7", "precss": "^1.4.0", "raw-loader": "^0.5.1", - "react-addons-test-utils": "^15.0.0-rc.2", + "react-addons-test-utils": "^15.0.1", "react-transform-catch-errors": "^1.0.2", "react-transform-hmr": "^1.0.4", - "redbox-react": "^1.2.2", - "stylelint": "^5.2.1", - "stylelint-config-standard": "^4.0.1", + "redbox-react": "^1.2.3", + "stylelint": "^5.3.0", + "stylelint-config-standard": "^5.0.0", "url-loader": "^0.5.7", "webpack": "^1.12.14", "webpack-hot-middleware": "^2.10.0", @@ -111,12 +111,18 @@ "rules": { "no-confusing-arrow": 0, "react/jsx-quotes": 0, - "jsx-quotes": [2, "prefer-double"] + "jsx-quotes": [ + 2, + "prefer-double" + ] } }, "jscsConfig": { "preset": "airbnb", - "excludeFiles": ["build/**", "node_modules/**"], + "excludeFiles": [ + "build/**", + "node_modules/**" + ], "disallowSpacesInsideTemplateStringPlaceholders": null }, "jest": { diff --git a/src/client.js b/src/client.js index aaaefb6a1..90a102b23 100644 --- a/src/client.js +++ b/src/client.js @@ -10,16 +10,16 @@ import 'babel-polyfill'; import ReactDOM from 'react-dom'; import FastClick from 'fastclick'; -import Router from './routes'; +import { match } from 'universal-router'; +import routes from './routes'; import Location from './core/Location'; import { addEventListener, removeEventListener } from './core/DOMUtils'; -let cssContainer = document.getElementById('css'); -const appContainer = document.getElementById('app'); +const container = document.getElementById('app'); const context = { insertCss: styles => styles._insertCss(), - onSetTitle: value => (document.title = value), - onSetMeta: (name, content) => { + setTitle: value => (document.title = value), + setMeta: (name, content) => { // Remove and create a new tag in order to make it work // with bookmarks in Safari const elements = document.getElementsByTagName('meta'); @@ -37,49 +37,61 @@ const context = { }, }; -// Google Analytics tracking. Don't send 'pageview' event after the first -// rendering, as it was already sent by the Html component. -let trackPageview = () => (trackPageview = () => window.ga('send', 'pageview')); +// Restore the scroll position if it was saved into the state +function restoreScrollPosition(state) { + if (state && state.scrollY !== undefined) { + window.scrollTo(state.scrollX, state.scrollY); + } else { + window.scrollTo(0, 0); + } +} -function render(state) { - Router.dispatch(state, (newState, component) => { - ReactDOM.render(component, appContainer, () => { - // Restore the scroll position if it was saved into the state - if (state.scrollY !== undefined) { - window.scrollTo(state.scrollX, state.scrollY); - } else { - window.scrollTo(0, 0); - } +let renderComplete = (state, callback) => { + restoreScrollPosition(state); + const elem = document.getElementById('css'); + if (elem) elem.parentNode.removeChild(elem); + callback(true); + renderComplete = (s) => { + restoreScrollPosition(s); - trackPageview(); + // Google Analytics tracking. Don't send 'pageview' event after + // the initial rendering, as it was already sent + window.ga('send', 'pageview'); - // Remove the pre-rendered CSS because it's no longer used - // after the React app is launched - if (cssContainer) { - cssContainer.parentNode.removeChild(cssContainer); - cssContainer = null; - } - }); + callback(true); + }; +}; + +function render(state, component) { + return new Promise((resolve, reject) => { + try { + ReactDOM.render( + component, + container, + renderComplete.bind(undefined, state, resolve) + ); + } catch (err) { + reject(err); + } }); } function run() { let currentLocation = null; - let currentState = null; // Make taps on links and buttons work fast on mobiles FastClick.attach(document.body); // Re-render the app when window.location changes - const unlisten = Location.listen(location => { + const removeLocationListener = Location.listen(location => { currentLocation = location; - currentState = Object.assign({}, location.state, { + match(routes, { path: location.pathname, query: location.query, state: location.state, context, - }); - render(currentState); + render: render.bind(undefined, location.state), + }).catch(err => console.error(err)); // eslint-disable-line no-console }); // Save the page scroll position into the current location's state @@ -101,7 +113,7 @@ function run() { addEventListener(window, 'scroll', setPageOffset); addEventListener(window, 'pagehide', () => { removeEventListener(window, 'scroll', setPageOffset); - unlisten(); + removeLocationListener(); }); } diff --git a/src/components/App/App.js b/src/components/App/App.js index 82fb058ba..a8483dc3a 100644 --- a/src/components/App/App.js +++ b/src/components/App/App.js @@ -19,9 +19,8 @@ class App extends Component { static propTypes = { context: PropTypes.shape({ insertCss: PropTypes.func, - onSetTitle: PropTypes.func, - onSetMeta: PropTypes.func, - onPageNotFound: PropTypes.func, + setTitle: PropTypes.func, + setMeta: PropTypes.func, }), children: PropTypes.element.isRequired, error: PropTypes.object, @@ -29,18 +28,16 @@ class App extends Component { static childContextTypes = { insertCss: PropTypes.func.isRequired, - onSetTitle: PropTypes.func.isRequired, - onSetMeta: PropTypes.func.isRequired, - onPageNotFound: PropTypes.func.isRequired, + setTitle: PropTypes.func.isRequired, + setMeta: PropTypes.func.isRequired, }; getChildContext() { const context = this.props.context; return { insertCss: context.insertCss || emptyFunction, - onSetTitle: context.onSetTitle || emptyFunction, - onSetMeta: context.onSetMeta || emptyFunction, - onPageNotFound: context.onPageNotFound || emptyFunction, + setTitle: context.setTitle || emptyFunction, + setMeta: context.setMeta || emptyFunction, }; } diff --git a/src/components/ContentPage/package.json b/src/components/ContentPage/package.json deleted file mode 100644 index 26aa4558a..000000000 --- a/src/components/ContentPage/package.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "name": "ContentPage", - "version": "0.0.0", - "private": true, - "main": "./ContentPage.js" -} diff --git a/src/components/ErrorPage/ErrorPage.js b/src/components/ErrorPage/ErrorPage.js deleted file mode 100644 index 547a1f7e0..000000000 --- a/src/components/ErrorPage/ErrorPage.js +++ /dev/null @@ -1,48 +0,0 @@ -/** - * React Starter Kit (https://www.reactstarterkit.com/) - * - * Copyright © 2014-2016 Kriasoft, LLC. All rights reserved. - * - * This source code is licensed under the MIT license found in the - * LICENSE.txt file in the root directory of this source tree. - */ - -import React, { Component, PropTypes } from 'react'; -import withStyles from 'isomorphic-style-loader/lib/withStyles'; -import s from './ErrorPage.scss'; - -let title = 'Error'; -let content = 'Sorry, a critical error occurred on this page.'; - -class ErrorPage extends Component { - - static contextTypes = { - onSetTitle: PropTypes.func.isRequired, - onPageNotFound: PropTypes.func.isRequired, - }; - - static propTypes = { - statusCode: PropTypes.number, - }; - - componentWillMount() { - this.context.onSetTitle(title); - } - - render() { - if (this.props.statusCode === 404) { - title = 'Page Not Found'; - content = 'Sorry, the page you were trying to view does not exist.'; - } - - return ( -
-

{title}

-

{content}

-
- ); - } - -} - -export default withStyles(ErrorPage, s); diff --git a/src/components/ErrorPage/package.json b/src/components/ErrorPage/package.json deleted file mode 100644 index 41d6d9a4b..000000000 --- a/src/components/ErrorPage/package.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "name": "ErrorPage", - "version": "0.0.0", - "private": true, - "main": "./ErrorPage.js" -} diff --git a/src/routes.js b/src/routes.js deleted file mode 100644 index e82c5fd2d..000000000 --- a/src/routes.js +++ /dev/null @@ -1,46 +0,0 @@ -/** - * React Starter Kit (https://www.reactstarterkit.com/) - * - * Copyright © 2014-2016 Kriasoft, LLC. All rights reserved. - * - * This source code is licensed under the MIT license found in the - * LICENSE.txt file in the root directory of this source tree. - */ - -import React from 'react'; -import Router from 'react-routing/src/Router'; -import fetch from './core/fetch'; -import App from './components/App'; -import ContentPage from './components/ContentPage'; -import ErrorPage from './components/ErrorPage'; - -const routes = [ - require('./routes/home'), - require('./routes/contact'), - require('./routes/login'), - require('./routes/register'), -]; - -const router = new Router(on => { - on('*', async (state, next) => { - const component = await next(); - return component && {component}; - }); - - routes.forEach(route => { - on(route.path, route.action); - }); - - on('*', async (state) => { - const query = `/graphql?query={content(path:"${state.path}"){path,title,content,component}}`; - const response = await fetch(query); - const { data } = await response.json(); - return data && data.content && ; - }); - - on('error', (state, error) => - - ); -}); - -export default router; diff --git a/src/routes/contact/Contact.js b/src/routes/contact/Contact.js index 311b1cd6f..b06518714 100644 --- a/src/routes/contact/Contact.js +++ b/src/routes/contact/Contact.js @@ -11,7 +11,10 @@ import React, { PropTypes } from 'react'; import withStyles from 'isomorphic-style-loader/lib/withStyles'; import s from './Contact.scss'; -function Contact({ title }) { +const title = 'Contact Us'; + +function Contact(props, context) { + context.setTitle(title); return (
@@ -22,6 +25,6 @@ function Contact({ title }) { ); } -Contact.propTypes = { title: PropTypes.string.isRequired }; +Contact.contextTypes = { setTitle: PropTypes.func.isRequired }; export default withStyles(Contact, s); diff --git a/src/routes/contact/index.js b/src/routes/contact/index.js index 540588ddf..4956b8df5 100644 --- a/src/routes/contact/index.js +++ b/src/routes/contact/index.js @@ -10,9 +10,12 @@ import React from 'react'; import Contact from './Contact'; -export const path = '/contact'; -export const action = async (state) => { - const title = 'Contact Us'; - state.context.onSetTitle(title); - return ; +export default { + + path: '/contact', + + action() { + return ; + }, + }; diff --git a/src/components/ContentPage/ContentPage.js b/src/routes/content/Content.js similarity index 80% rename from src/components/ContentPage/ContentPage.js rename to src/routes/content/Content.js index a32d6a771..2c36802d9 100644 --- a/src/components/ContentPage/ContentPage.js +++ b/src/routes/content/Content.js @@ -9,9 +9,13 @@ import React, { Component, PropTypes } from 'react'; import withStyles from 'isomorphic-style-loader/lib/withStyles'; -import s from './ContentPage.scss'; +import s from './Content.scss'; -class ContentPage extends Component { +class Content extends Component { + + static contextTypes = { + setTitle: PropTypes.func.isRequired, + }; static propTypes = { path: PropTypes.string.isRequired, @@ -19,12 +23,8 @@ class ContentPage extends Component { title: PropTypes.string, }; - static contextTypes = { - onSetTitle: PropTypes.func.isRequired, - }; - componentWillMount() { - this.context.onSetTitle(this.props.title); + this.context.setTitle(this.props.title); } render() { @@ -40,4 +40,4 @@ class ContentPage extends Component { } -export default withStyles(ContentPage, s); +export default withStyles(Content, s); diff --git a/src/components/ContentPage/ContentPage.scss b/src/routes/content/Content.scss similarity index 90% rename from src/components/ContentPage/ContentPage.scss rename to src/routes/content/Content.scss index dc01da929..d98463bf0 100644 --- a/src/components/ContentPage/ContentPage.scss +++ b/src/routes/content/Content.scss @@ -7,7 +7,7 @@ * LICENSE.txt file in the root directory of this source tree. */ -@import 'http://github.com/variables.scss'; +@import 'http://github.com/components/variables.scss'; .root { padding-left: 20px; diff --git a/src/routes/content/index.js b/src/routes/content/index.js new file mode 100644 index 000000000..ccc51e7e8 --- /dev/null +++ b/src/routes/content/index.js @@ -0,0 +1,36 @@ +/** + * React Starter Kit (https://www.reactstarterkit.com/) + * + * Copyright © 2014-2016 Kriasoft, LLC. All rights reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE.txt file in the root directory of this source tree. + */ + +import React from 'react'; +import Content from './Content'; +import fetch from '../../core/fetch'; + +export default { + + path: '*', + + async action({ path }) { // eslint-disable-line react/prop-types + const resp = await fetch('http://github.com/graphql', { + method: 'post', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + query: `{content(path:"${path}"){path,title,content,component}}`, + }), + credentials: 'include', + }); + if (resp.status !== 200) throw new Error(resp.statusText); + const { data } = await resp.json(); + if (!data || !data.content) return undefined; + return ; + }, + +}; diff --git a/src/routes/error/ErrorPage.js b/src/routes/error/ErrorPage.js new file mode 100644 index 000000000..21d3bbfe7 --- /dev/null +++ b/src/routes/error/ErrorPage.js @@ -0,0 +1,40 @@ +/** + * React Starter Kit (https://www.reactstarterkit.com/) + * + * Copyright © 2014-2016 Kriasoft, LLC. All rights reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE.txt file in the root directory of this source tree. + */ + +import React, { PropTypes } from 'react'; +import withStyles from 'isomorphic-style-loader/lib/withStyles'; +import s from './ErrorPage.scss'; + +function ErrorPage({ error }, context) { + let title = 'Error'; + let content = 'Sorry, a critical error occurred on this page.'; + let errorMessage = null; + + if (error.status === 404) { + title = 'Page Not Found'; + content = 'Sorry, the page you were trying to view does not exist.'; + } else if (process.env.NODE_ENV !== 'production') { + errorMessage =
{error.stack}
; + } + + context.setTitle(title); + + return ( +
+

{title}

+

{content}

+ {errorMessage} +
+ ); +} + +ErrorPage.propTypes = { error: PropTypes.object.isRequired }; +ErrorPage.contextTypes = { setTitle: PropTypes.func.isRequired }; + +export default withStyles(ErrorPage, s); diff --git a/src/components/ErrorPage/ErrorPage.scss b/src/routes/error/ErrorPage.scss similarity index 92% rename from src/components/ErrorPage/ErrorPage.scss rename to src/routes/error/ErrorPage.scss index 7ecebf8d5..7c05efd71 100644 --- a/src/components/ErrorPage/ErrorPage.scss +++ b/src/routes/error/ErrorPage.scss @@ -24,7 +24,9 @@ html { body { display: table-cell; vertical-align: middle; + /* stylelint-disable */ margin: 2em auto; + /* stylelint-enable */ } h1 { @@ -45,7 +47,8 @@ pre { @media only screen and (max-width: 280px) { - body, p { + body, + p { width: 95%; } diff --git a/src/routes/error/index.js b/src/routes/error/index.js new file mode 100644 index 000000000..b6873440a --- /dev/null +++ b/src/routes/error/index.js @@ -0,0 +1,27 @@ +/** + * React Starter Kit (https://www.reactstarterkit.com/) + * + * Copyright © 2014-2016 Kriasoft, LLC. All rights reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE.txt file in the root directory of this source tree. + */ + +import React from 'react'; +import App from '../../components/App'; +import ErrorPage from './ErrorPage'; + +export default { + + path: '/error', + + action({ render, context, error }) { + return render( + + + , + error.status || 500 + ); + }, + +}; diff --git a/src/routes/home/Home.js b/src/routes/home/Home.js index 378c46b99..c3165f767 100644 --- a/src/routes/home/Home.js +++ b/src/routes/home/Home.js @@ -11,7 +11,10 @@ import React, { PropTypes } from 'react'; import withStyles from 'isomorphic-style-loader/lib/withStyles'; import s from './Home.scss'; -function Home({ news }) { +const title = 'React Starter Kit'; + +function Home({ news }, context) { + context.setTitle(title); return (
@@ -39,5 +42,6 @@ Home.propTypes = { contentSnippet: PropTypes.string, })).isRequired, }; +Home.contextTypes = { setTitle: PropTypes.func.isRequired }; export default withStyles(Home, s); diff --git a/src/routes/home/index.js b/src/routes/home/index.js index 2c0805d7b..e135f464a 100644 --- a/src/routes/home/index.js +++ b/src/routes/home/index.js @@ -11,10 +11,25 @@ import React from 'react'; import Home from './Home'; import fetch from '../../core/fetch'; -export const path = '/'; -export const action = async (state) => { - const response = await fetch('http://github.com/graphql?query={news{title,link,contentSnippet}}'); - const { data } = await response.json(); - state.context.onSetTitle('React.js Starter Kit'); - return ; +export default { + + path: '/', + + async action() { + const resp = await fetch('http://github.com/graphql', { + method: 'post', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + query: '{news{title,link,contentSnippet}}', + }), + credentials: 'include', + }); + const { data } = await resp.json(); + if (!data || !data.news) throw new Error('Failed to load the news feed.'); + return ; + }, + }; diff --git a/src/routes/index.js b/src/routes/index.js new file mode 100644 index 000000000..b092faad6 --- /dev/null +++ b/src/routes/index.js @@ -0,0 +1,42 @@ +/** + * React Starter Kit (https://www.reactstarterkit.com/) + * + * Copyright © 2014-2016 Kriasoft, LLC. All rights reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE.txt file in the root directory of this source tree. + */ + +import React from 'react'; +import App from '../components/App'; + +// Child routes +import home from './home'; +import contact from './contact'; +import login from './login'; +import register from './register'; +import content from './content'; +import error from './error'; + +export default { + + path: '/', + + children: [ + home, + contact, + login, + register, + content, + error, + ], + + async action({ next, render, context }) { + const component = await next(); + if (component === undefined) return component; + return render( + {component} + ); + }, + +}; diff --git a/src/routes/login/Login.js b/src/routes/login/Login.js index 4466818c6..4cbecc3f1 100644 --- a/src/routes/login/Login.js +++ b/src/routes/login/Login.js @@ -11,7 +11,10 @@ import React, { PropTypes } from 'react'; import withStyles from 'isomorphic-style-loader/lib/withStyles'; import s from './Login.scss'; -function Login({ title }) { +const title = 'Log In'; + +function Login(props, context) { + context.setTitle(title); return (
@@ -112,6 +115,6 @@ function Login({ title }) { ); } -Login.propTypes = { title: PropTypes.string.isRequired }; +Login.contextTypes = { setTitle: PropTypes.func.isRequired }; export default withStyles(Login, s); diff --git a/src/routes/login/index.js b/src/routes/login/index.js index 9f3c62144..c16b14272 100644 --- a/src/routes/login/index.js +++ b/src/routes/login/index.js @@ -10,9 +10,12 @@ import React from 'react'; import Login from './Login'; -export const path = '/login'; -export const action = async (state) => { - const title = 'Log In'; - state.context.onSetTitle(title); - return ; +export default { + + path: '/login', + + action() { + return ; + }, + }; diff --git a/src/routes/register/Register.js b/src/routes/register/Register.js index 0d04d360a..cec021b63 100644 --- a/src/routes/register/Register.js +++ b/src/routes/register/Register.js @@ -11,7 +11,10 @@ import React, { PropTypes } from 'react'; import withStyles from 'isomorphic-style-loader/lib/withStyles'; import s from './Register.scss'; -function Register({ title }) { +const title = 'New User Registration'; + +function Register(props, context) { + context.setTitle(title); return (
@@ -22,6 +25,6 @@ function Register({ title }) { ); } -Register.propTypes = { title: PropTypes.string.isRequired }; +Register.contextTypes = { setTitle: PropTypes.func.isRequired }; export default withStyles(Register, s); diff --git a/src/routes/register/index.js b/src/routes/register/index.js index 015b7f848..afcdceccb 100644 --- a/src/routes/register/index.js +++ b/src/routes/register/index.js @@ -10,9 +10,12 @@ import React from 'react'; import Register from './Register'; -export const path = '/register'; -export const action = async (state) => { - const title = 'New User Registration'; - state.context.onSetTitle(title); - return ; +export default { + + path: '/register', + + action() { + return ; + }, + }; diff --git a/src/server.js b/src/server.js index cf530d356..db33a638a 100644 --- a/src/server.js +++ b/src/server.js @@ -16,10 +16,11 @@ import expressJwt from 'express-jwt'; import expressGraphQL from 'express-graphql'; import jwt from 'jsonwebtoken'; import ReactDOM from 'react-dom/server'; +import { match } from 'universal-router'; import PrettyError from 'pretty-error'; import passport from './core/passport'; import schema from './data/schema'; -import Router from './routes'; +import routes from './routes'; import assets from './assets'; import { port, auth, analytics } from './config'; @@ -80,6 +81,7 @@ server.use('/graphql', expressGraphQL(req => ({ // ----------------------------------------------------------------------------- server.get('*', async (req, res, next) => { try { + let css = []; let statusCode = 200; const template = require('./views/index.jade'); const data = { title: '', description: '', css: '', body: '', entry: assets.main.js }; @@ -88,17 +90,21 @@ server.get('*', async (req, res, next) => { data.trackingId = analytics.google.trackingId; } - const css = []; - const context = { - insertCss: styles => css.push(styles._getCss()), - onSetTitle: value => (data.title = value), - onSetMeta: (key, value) => (data[key] = value), - onPageNotFound: () => (statusCode = 404), - }; - - await Router.dispatch({ path: req.path, query: req.query, context }, (state, component) => { - data.body = ReactDOM.renderToString(component); - data.css = css.join(''); + await match(routes, { + path: req.path, + query: req.query, + context: { + insertCss: styles => css.push(styles._getCss()), + setTitle: value => (data.title = value), + setMeta: (key, value) => (data[key] = value), + }, + render(component, status = 200) { + css = []; + statusCode = status; + data.body = ReactDOM.renderToString(component); + data.css = css.join(''); + return true; + }, }); res.status(statusCode); From 94a8c390e955a716d7ba32865b3bb16f5d7afaf8 Mon Sep 17 00:00:00 2001 From: Konstantin Tarkus Date: Sat, 9 Apr 2016 11:03:02 +0300 Subject: [PATCH 012/357] Update client.js, remove scrollTo behavior after the initial rendering --- src/client.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/client.js b/src/client.js index 90a102b23..565dddc3d 100644 --- a/src/client.js +++ b/src/client.js @@ -15,7 +15,6 @@ import routes from './routes'; import Location from './core/Location'; import { addEventListener, removeEventListener } from './core/DOMUtils'; -const container = document.getElementById('app'); const context = { insertCss: styles => styles._insertCss(), setTitle: value => (document.title = value), @@ -47,7 +46,6 @@ function restoreScrollPosition(state) { } let renderComplete = (state, callback) => { - restoreScrollPosition(state); const elem = document.getElementById('css'); if (elem) elem.parentNode.removeChild(elem); callback(true); @@ -62,7 +60,7 @@ let renderComplete = (state, callback) => { }; }; -function render(state, component) { +function render(container, state, component) { return new Promise((resolve, reject) => { try { ReactDOM.render( @@ -78,6 +76,7 @@ function render(state, component) { function run() { let currentLocation = null; + const container = document.getElementById('app'); // Make taps on links and buttons work fast on mobiles FastClick.attach(document.body); @@ -90,7 +89,7 @@ function run() { query: location.query, state: location.state, context, - render: render.bind(undefined, location.state), + render: render.bind(undefined, container, location.state), }).catch(err => console.error(err)); // eslint-disable-line no-console }); From ae3fd247cbbc852417ce54c638bcc7b5ded101f2 Mon Sep 17 00:00:00 2001 From: Konstantin Tarkus Date: Sat, 9 Apr 2016 11:14:50 +0300 Subject: [PATCH 013/357] Update npm modules: graphql, pg, styleint --- package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 3318fa63a..25b40742f 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ "core-js": "2.2.2", "eventemitter3": "1.2.0", "express": "4.13.4", - "express-graphql": "0.4.13", + "express-graphql": "0.5.0", "express-jwt": "3.3.0", "fastclick": "1.0.6", "fbjs": "0.8.0", @@ -30,7 +30,7 @@ "normalize.css": "4.0.0", "passport": "0.3.2", "passport-facebook": "2.1.0", - "pg": "4.5.2", + "pg": "4.5.3", "pretty-error": "2.0.0", "react": "15.0.1", "react-dom": "15.0.1", @@ -84,7 +84,7 @@ "react-transform-catch-errors": "^1.0.2", "react-transform-hmr": "^1.0.4", "redbox-react": "^1.2.3", - "stylelint": "^5.3.0", + "stylelint": "^5.4.0", "stylelint-config-standard": "^5.0.0", "url-loader": "^0.5.7", "webpack": "^1.12.14", From 0d01779c3df27d887e05cb50b5335c08e0de90b8 Mon Sep 17 00:00:00 2001 From: Konstantin Tarkus Date: Mon, 11 Apr 2016 14:18:46 +0300 Subject: [PATCH 014/357] Rename the "server" variable to "app" in "src/server.js" --- CHANGELOG.md | 2 ++ src/server.js | 31 ++++++++++++++++--------------- 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index aaaf26af6..51f395af1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ All notable changes to this project will be documented in this file. ### [Unreleased][unreleased] +- Rename `server` variable in `server.js` to `app` +- Integrate [Sequelize](http://docs.sequelizejs.com/) to make the project compatible with different types of databases - Rename `onSetTitle`, `onSetMeta` context variables to `setTitle`, `setMeta` - Move `Content` component to `src/routes/content` - Move `ErrorPage` component to `src/routes/error` diff --git a/src/server.js b/src/server.js index 2a57d4486..b5671415a 100644 --- a/src/server.js +++ b/src/server.js @@ -25,7 +25,7 @@ import routes from './routes'; import assets from './assets'; import { port, auth, analytics } from './config'; -const server = global.server = express(); +const app = express(); // // Tell any CSS tooling (such as Material UI) to use all vendor prefixes if the @@ -37,27 +37,27 @@ global.navigator.userAgent = global.navigator.userAgent || 'all'; // // Register Node.js middleware // ----------------------------------------------------------------------------- -server.use(express.static(path.join(__dirname, 'public'))); -server.use(cookieParser()); -server.use(bodyParser.urlencoded({ extended: true })); -server.use(bodyParser.json()); +app.use(express.static(path.join(__dirname, 'public'))); +app.use(cookieParser()); +app.use(bodyParser.urlencoded({ extended: true })); +app.use(bodyParser.json()); // // Authentication // ----------------------------------------------------------------------------- -server.use(expressJwt({ +app.use(expressJwt({ secret: auth.jwt.secret, credentialsRequired: false, /* jscs:disable requireCamelCaseOrUpperCaseIdentifiers */ getToken: req => req.cookies.id_token, /* jscs:enable requireCamelCaseOrUpperCaseIdentifiers */ })); -server.use(passport.initialize()); +app.use(passport.initialize()); -server.get('/login/facebook', +app.get('/login/facebook', passport.authenticate('facebook', { scope: ['email', 'user_location'], session: false }) ); -server.get('/login/facebook/return', +app.get('/login/facebook/return', passport.authenticate('facebook', { failureRedirect: '/login', session: false }), (req, res) => { const expiresIn = 60 * 60 * 24 * 180; // 180 days @@ -70,7 +70,7 @@ server.get('/login/facebook/return', // // Register API middleware // ----------------------------------------------------------------------------- -server.use('/graphql', expressGraphQL(req => ({ +app.use('/graphql', expressGraphQL(req => ({ schema, graphiql: true, rootValue: { request: req }, @@ -80,7 +80,7 @@ server.use('/graphql', expressGraphQL(req => ({ // // Register server-side rendering middleware // ----------------------------------------------------------------------------- -server.get('*', async (req, res, next) => { +app.get('*', async (req, res, next) => { try { let css = []; let statusCode = 200; @@ -122,7 +122,7 @@ const pe = new PrettyError(); pe.skipNodeFiles(); pe.skipPackage('express'); -server.use((err, req, res, next) => { // eslint-disable-line no-unused-vars +app.use((err, req, res, next) => { // eslint-disable-line no-unused-vars console.log(pe.render(err)); // eslint-disable-line no-console const template = require('./views/error.jade'); const statusCode = err.status || 500; @@ -136,9 +136,10 @@ server.use((err, req, res, next) => { // eslint-disable-line no-unused-vars // // Launch the server // ----------------------------------------------------------------------------- -models.sync().then(() => { - server.listen(port, () => { - /* eslint-disable no-console */ +/* eslint-disable no-console */ +models.sync().catch(err => console.error(err.stack)).then(() => { + app.listen(port, () => { console.log(`The server is running at http://localhost:${port}/`); }); }); +/* eslint-enable no-console */ From c2e7c68b337856fb954887bffc0f6703d5e56c53 Mon Sep 17 00:00:00 2001 From: Konstantin Tarkus Date: Mon, 11 Apr 2016 17:57:21 +0300 Subject: [PATCH 015/357] Add Backers section to README.md --- README.md | 87 ++++++++++++++++++----------------------- docs/getting-started.md | 37 ++++++++++++++++++ 2 files changed, 74 insertions(+), 50 deletions(-) diff --git a/README.md b/README.md index 8372aca17..8bb2b21e8 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,20 @@ ## React Starter Kit — "isomorphic" web app boilerplate -[![Support us on Bountysource](https://dl.dropboxusercontent.com/u/16006521/react-starter-kit/banner.png)](https://salt.bountysource.com/teams/react-starter-kit)
- > [React Starter Kit](https://www.reactstarterkit.com) is an opinionated -> boilerplate for web development built on top of Facebook's -> [React](https://facebook.github.io/react/) library, -> [Node.js](https://nodejs.org/) / [Express](http://expressjs.com/) server -> and [Flux](http://facebook.github.io/flux/) architecture. Containing -> modern web development tools such as [Webpack](http://webpack.github.io/), -> [Babel](http://babeljs.io/) and [BrowserSync](http://www.browsersync.io/). -> Helping you to stay productive following the best practices. A solid starting -> point for both professionals and newcomers to the industry. - -See [demo](http://demo.reactstarterkit.com)  |  +> boilerplate for web development built on top of [Node.js](https://nodejs.org/), +> [Express](http://expressjs.com/), [GraphQL](http://graphql.org/) and +> [React](https://facebook.github.io/react/). Containing modern web development +> tools such as [Webpack](http://webpack.github.io/), [Babel](http://babeljs.io/) +> and [BrowserSync](http://www.browsersync.io/). Helping you to stay productive +> following the best practices. A solid starting point for both professionals +> and newcomers to the industry. + +See [homepage](https://www.reactstarterkit.com)  |  +[demo](http://demo.reactstarterkit.com)  |  [docs](https://github.com/kriasoft/react-starter-kit/tree/master/docs)  |  [to-do list](https://waffle.io/kriasoft/react-starter-kit)  |  -join [#react-starter-kit](https://gitter.im/kriasoft/react-starter-kit) chatroom to stay up to date  |  -visit our sponsors: +join [#react-starter-kit](https://gitter.im/kriasoft/react-starter-kit) chat room to stay up to date  |  +visit our sponsors:

[![Rollbar - Full-stack error tracking for all apps in any language](https://dl.dropboxusercontent.com/u/16006521/react-starter-kit/rollbar.png)](https://rollbar.com/?utm_source=reactstartkit(github)&utm_medium=link&utm_campaign=reactstartkit(github))    [![Localize - Translate your web app in minutes](https://dl.dropboxusercontent.com/u/16006521/react-starter-kit/localize.png)](https://localizejs.com/?cid=802&utm_source=rsk) @@ -26,42 +24,31 @@ visit our sponsors: * Follow the [getting started guide](./docs/getting-started.md) to download and run the project * Check the [code recipes](./docs/recipes) used in this boilerplate, or share yours -### Directory Layout - -``` -. -├── /build/ # The folder for compiled output -├── /docs/ # Documentation files for the project -├── /node_modules/ # 3rd-party libraries and utilities -├── /src/ # The source code of the application -│ ├── /actions/ # Action creators that allow to trigger a dispatch to stores -│ ├── /components/ # React components -│ ├── /constants/ # Constants (action types etc.) -│ ├── /content/ # Static content (plain HTML or Markdown, Jade, you name it) -│ ├── /core/ # Core framework and utility functions -│ ├── /data/ # GraphQL server schema -│ ├── /decorators/ # Higher-order React components -│ ├── /public/ # Static files which are copied into the /build/public folder -│ ├── /routes/ # Page/screen components along with the routing information -│ ├── /stores/ # Stores contain the application state and logic -│ ├── /views/ # Express.js views for index and error pages -│ ├── /client.js # Client-side startup script -│ ├── /config.js # Global application settings -│ ├── /routes.js # Universal (isomorphic) application routes -│ └── /server.js # Server-side startup script -├── /tools/ # Build automation scripts and utilities -│ ├── /lib/ # Library for utility snippets -│ ├── /build.js # Builds the project from source to output (build) folder -│ ├── /bundle.js # Bundles the web resources into package(s) through Webpack -│ ├── /clean.js # Cleans up the output (build) folder -│ ├── /copy.js # Copies static files to output (build) folder -│ ├── /deploy.js # Deploys your web application -│ ├── /run.js # Helper function for running build automation tasks -│ ├── /runServer.js # Launches (or restarts) Node.js server -│ ├── /start.js # Launches the development web server with "live reload" -│ └── /webpack.config.js # Configurations for client-side and server-side bundles -└── package.json # The list of 3rd party libraries and utilities -``` +### Backers + +♥ Universal Router? Help us keep it alive by [donating funds](https://salt.bountysource.com/teams/react-starter-kit) to cover project expenses! + + + lehneres + + + Tarkan Anlar + + + Morten Olsen + + + Adam + + + David Ernst + + + Zane Hitchcox + + + + ### Related Projects diff --git a/docs/getting-started.md b/docs/getting-started.md index cdecea31c..e3d13ddd8 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -1,5 +1,42 @@ ## Getting Started +### Directory Layout + +``` +. +├── /build/ # The folder for compiled output +├── /docs/ # Documentation files for the project +├── /node_modules/ # 3rd-party libraries and utilities +├── /src/ # The source code of the application +│ ├── /actions/ # Action creators that allow to trigger a dispatch to stores +│ ├── /components/ # React components +│ ├── /constants/ # Constants (action types etc.) +│ ├── /content/ # Static content (plain HTML or Markdown, Jade, you name it) +│ ├── /core/ # Core framework and utility functions +│ ├── /data/ # GraphQL server schema +│ ├── /decorators/ # Higher-order React components +│ ├── /public/ # Static files which are copied into the /build/public folder +│ ├── /routes/ # Page/screen components along with the routing information +│ ├── /stores/ # Stores contain the application state and logic +│ ├── /views/ # Express.js views for index and error pages +│ ├── /client.js # Client-side startup script +│ ├── /config.js # Global application settings +│ ├── /routes.js # Universal (isomorphic) application routes +│ └── /server.js # Server-side startup script +├── /tools/ # Build automation scripts and utilities +│ ├── /lib/ # Library for utility snippets +│ ├── /build.js # Builds the project from source to output (build) folder +│ ├── /bundle.js # Bundles the web resources into package(s) through Webpack +│ ├── /clean.js # Cleans up the output (build) folder +│ ├── /copy.js # Copies static files to output (build) folder +│ ├── /deploy.js # Deploys your web application +│ ├── /run.js # Helper function for running build automation tasks +│ ├── /runServer.js # Launches (or restarts) Node.js server +│ ├── /start.js # Launches the development web server with "live reload" +│ └── /webpack.config.js # Configurations for client-side and server-side bundles +└── package.json # The list of 3rd party libraries and utilities +``` + ### Requirements * Mac OS X, Windows, or Linux From c000544a6babaa10b320eeedfe9bf6e0befcbd98 Mon Sep 17 00:00:00 2001 From: Konstantin Tarkus Date: Mon, 11 Apr 2016 18:46:39 +0300 Subject: [PATCH 016/357] Update Backers section in README.md --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 8bb2b21e8..3b9b0dc8a 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ > [Express](http://expressjs.com/), [GraphQL](http://graphql.org/) and > [React](https://facebook.github.io/react/). Containing modern web development > tools such as [Webpack](http://webpack.github.io/), [Babel](http://babeljs.io/) -> and [BrowserSync](http://www.browsersync.io/). Helping you to stay productive +> and [Browsersync](http://www.browsersync.io/). Helping you to stay productive > following the best practices. A solid starting point for both professionals > and newcomers to the industry. @@ -26,7 +26,9 @@ visit our sponsors:

### Backers -♥ Universal Router? Help us keep it alive by [donating funds](https://salt.bountysource.com/teams/react-starter-kit) to cover project expenses! +♥ React Starter Kit? Help us keep it alive by donating funds to cover project +expenses via [Bountysource](https://salt.bountysource.com/teams/react-starter-kit) +or [PayPal](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=RETHAJHV3T972)! lehneres From 8bf073c9daeb887650901f1e834ddba82bec9463 Mon Sep 17 00:00:00 2001 From: greenkeeperio-bot Date: Tue, 12 Apr 2016 20:20:47 +0300 Subject: [PATCH 017/357] chore(package): update express-graphql to version 0.5.1 http://greenkeeper.io/ --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 8fc65285e..67d19b6d8 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ "core-js": "2.2.2", "eventemitter3": "1.2.0", "express": "4.13.4", - "express-graphql": "0.5.0", + "express-graphql": "0.5.1", "express-jwt": "3.3.0", "fastclick": "1.0.6", "fbjs": "0.8.0", From 2994426a90884baa8f055c38946d6858530463e0 Mon Sep 17 00:00:00 2001 From: greenkeeperio-bot Date: Tue, 12 Apr 2016 23:20:23 +0300 Subject: [PATCH 018/357] chore(package): update bluebird to version 3.3.5 http://greenkeeper.io/ --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 67d19b6d8..d221755f1 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "dependencies": { "babel-polyfill": "6.7.4", "babel-runtime": "6.6.1", - "bluebird": "3.3.4", + "bluebird": "3.3.5", "body-parser": "1.15.0", "classnames": "2.2.3", "cookie-parser": "1.4.1", From 877daaf8f677b7740653b59209772be323f3a2aa Mon Sep 17 00:00:00 2001 From: Evgeny Murashkin Date: Fri, 15 Apr 2016 13:28:52 +0300 Subject: [PATCH 019/357] Renamed 'core/Location' to 'core/history' npm history module v2.0.2 --- package.json | 2 +- src/client.js | 4 ++-- src/components/Link/Link.js | 8 ++++---- src/core/{Location.js => history.js} | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) rename src/core/{Location.js => history.js} (79%) diff --git a/package.json b/package.json index d221755f1..4a3e1262f 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "front-matter": "2.0.7", "graphiql": "0.7.0", "graphql": "0.5.0", - "history": "2.0.1", + "history": "2.0.2", "isomorphic-style-loader": "0.0.12", "jade": "1.11.0", "jsonwebtoken": "5.7.0", diff --git a/src/client.js b/src/client.js index 565dddc3d..d1df93d0d 100644 --- a/src/client.js +++ b/src/client.js @@ -12,7 +12,7 @@ import ReactDOM from 'react-dom'; import FastClick from 'fastclick'; import { match } from 'universal-router'; import routes from './routes'; -import Location from './core/Location'; +import history from './core/history'; import { addEventListener, removeEventListener } from './core/DOMUtils'; const context = { @@ -82,7 +82,7 @@ function run() { FastClick.attach(document.body); // Re-render the app when window.location changes - const removeLocationListener = Location.listen(location => { + const removeLocationListener = history.listen(location => { currentLocation = location; match(routes, { path: location.pathname, diff --git a/src/components/Link/Link.js b/src/components/Link/Link.js index 333df7d46..a60ee19ab 100644 --- a/src/components/Link/Link.js +++ b/src/components/Link/Link.js @@ -8,7 +8,7 @@ */ import React, { Component, PropTypes } from 'react'; -import Location from '../../core/Location'; +import history from '../../core/history'; function isLeftClickEvent(event) { return event.button === 0; @@ -44,9 +44,9 @@ class Link extends Component { // eslint-disable-line react/prefer-stateless-fun if (allowTransition) { if (this.props.to) { - Location.push(this.props.to); + history.push(this.props.to); } else { - Location.push({ + history.push({ pathname: event.currentTarget.pathname, search: event.currentTarget.search, }); @@ -56,7 +56,7 @@ class Link extends Component { // eslint-disable-line react/prefer-stateless-fun render() { const { to, ...props } = this.props; // eslint-disable-line no-use-before-define - return ; + return ; } } diff --git a/src/core/Location.js b/src/core/history.js similarity index 79% rename from src/core/Location.js rename to src/core/history.js index 53ad8fb1f..c7458a8ce 100644 --- a/src/core/Location.js +++ b/src/core/history.js @@ -11,6 +11,6 @@ import createHistory from 'history/lib/createBrowserHistory'; import createMemoryHistory from 'history/lib/createMemoryHistory'; import useQueries from 'history/lib/useQueries'; -const location = useQueries(process.env.BROWSER ? createHistory : createMemoryHistory)(); +const history = useQueries(process.env.BROWSER ? createHistory : createMemoryHistory)(); -export default location; +export default history; From cd92ff727b4a69cd7912f9a1464991021d16ac48 Mon Sep 17 00:00:00 2001 From: Evgeny Murashkin Date: Fri, 15 Apr 2016 14:23:47 +0300 Subject: [PATCH 020/357] removeLocationListener -> removeHistoryListener --- src/client.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/client.js b/src/client.js index d1df93d0d..4f9de5b16 100644 --- a/src/client.js +++ b/src/client.js @@ -82,7 +82,7 @@ function run() { FastClick.attach(document.body); // Re-render the app when window.location changes - const removeLocationListener = history.listen(location => { + const removeHistoryListener = history.listen(location => { currentLocation = location; match(routes, { path: location.pathname, @@ -112,7 +112,7 @@ function run() { addEventListener(window, 'scroll', setPageOffset); addEventListener(window, 'pagehide', () => { removeEventListener(window, 'scroll', setPageOffset); - removeLocationListener(); + removeHistoryListener(); }); } From 79e70b3c803dd15ac4af0f68de173d1ff28017e4 Mon Sep 17 00:00:00 2001 From: Konstantin Tarkus Date: Sat, 16 Apr 2016 15:31:54 +0300 Subject: [PATCH 021/357] Update npm modules; replace Jest with Mocha --- CHANGELOG.md | 19 ++++++--- README.md | 24 ++++++------ docs/getting-started.md | 42 +++++++++++--------- package.json | 50 +++++++++++------------- src/actions/.gitignore | 0 src/components/App/App.test.js | 30 ++++++++++++++ src/components/App/__tests__/App-test.js | 16 -------- src/components/Feedback/Feedback.js | 2 +- src/components/Footer/Footer.js | 2 +- src/components/Header/Header.js | 2 +- src/components/Navigation/Navigation.js | 2 +- src/constants/ActionTypes.js | 14 ------- src/routes/contact/Contact.js | 2 +- src/routes/content/Content.js | 2 +- src/routes/error/ErrorPage.js | 2 +- src/routes/home/Home.js | 2 +- src/routes/login/Login.js | 2 +- src/routes/register/Register.js | 2 +- src/stores/.gitignore | 0 test/.eslintrc | 9 +++++ test/setup.js | 25 ++++++++++++ test/stubs/SCSSStub.js | 5 --- 22 files changed, 145 insertions(+), 109 deletions(-) delete mode 100644 src/actions/.gitignore create mode 100644 src/components/App/App.test.js delete mode 100644 src/components/App/__tests__/App-test.js delete mode 100644 src/constants/ActionTypes.js delete mode 100644 src/stores/.gitignore create mode 100644 test/.eslintrc create mode 100644 test/setup.js delete mode 100644 test/stubs/SCSSStub.js diff --git a/CHANGELOG.md b/CHANGELOG.md index 51f395af1..6ff9ac7f9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file. ### [Unreleased][unreleased] +- Update `isomorphic-style-loader` to `v1.0.0`, it adds comparability with ES2015+ decorators. + Code such as `export default withStyles(MyComponent, style1, style2)` must be replaced with + `export default withStyles(style1, style2)(MyComponent)` (BREAKING CHANGE). +- Replace Jest with Mocha, Chai, Sinon. Unit test files must be renamed from + `MyComponent/__test__/MyComponent-test.js` to `MyComponent/MyComponent.test.js` (BREAKING CHANGE). +- Remove `actions`, `stores` folders since there is no Flux library included into the kit - Rename `server` variable in `server.js` to `app` - Integrate [Sequelize](http://docs.sequelizejs.com/) to make the project compatible with different types of databases - Rename `onSetTitle`, `onSetMeta` context variables to `setTitle`, `setMeta` @@ -13,15 +19,17 @@ All notable changes to this project will be documented in this file. - Update routing to use `universal-router` library - Move Babel, ESLint and JSCS configurations to `package.json` [#497](https://github.com/kriasoft/react-starter-kit/pull/497) - Convert `Feedback`, `Footer`, `Header`, and `Navigation` to functional stateless components -- Move page / screen components into the `src/routes` folder along with the routing information for them [BREAKING CHANGE]. [6553936](https://github.com/kriasoft/react-starter-kit/commit/6553936e693e24a8ac6178f4962af15e0ea87dfd) +- Move page / screen components into the `src/routes` folder along with the routing information for them (BREAKING CHANGE). [6553936](https://github.com/kriasoft/react-starter-kit/commit/6553936e693e24a8ac6178f4962af15e0ea87dfd) -### [v0.5.1] - 2016-03-02 +### [v0.5.1] +> 2016-03-02 -- Remove `Html` React component in favor of compiled Jade templates (`src/views`) [BREAKING CHANGE]. [e188388](https://github.com/kriasoft/react-starter-kit/commit/e188388f87069cdc7d501b385d6b0e46c98fed60) +- Remove `Html` React component in favor of compiled Jade templates (`src/views`) (BREAKING CHANGE). [e188388](https://github.com/kriasoft/react-starter-kit/commit/e188388f87069cdc7d501b385d6b0e46c98fed60) - Add global error handling in Node.js/Express app. [e188388](https://github.com/kriasoft/react-starter-kit/commit/e188388f87069cdc7d501b385d6b0e46c98fed60) - Add support for Markdown and HTML for static pages. [#469](https://github.com/kriasoft/react-starter-kit/pull/469), [#477](https://github.com/kriasoft/react-starter-kit/pull/477) -### [v0.5.0] - 2016-02-27 +### [v0.5.0] +> 2016-02-27 - Replace RESTful API endpoint (`src/api`) with GraphQL (`src/data`) - Add a sample GraphQL endpoint [localhost:3000/graphql](https://localhost:3000/graphql) @@ -40,7 +48,8 @@ All notable changes to this project will be documented in this file. - Add support of `--release` and `--verbose` flags to build scripts - Add `CHANGELOG.md` file with a list of notable changes to this project -### [v0.4.1] - 2015-10-04 +### [v0.4.1] +> 2015-10-04 - Replace React Hot Loader (depricated) with React Transform - Replace `index.html` template with `Html` (shell) React component diff --git a/README.md b/README.md index 3b9b0dc8a..9f0be03ec 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ > following the best practices. A solid starting point for both professionals > and newcomers to the industry. -See [homepage](https://www.reactstarterkit.com)  |  +See [getting started](./docs/getting-started.md)  |  [demo](http://demo.reactstarterkit.com)  |  [docs](https://github.com/kriasoft/react-starter-kit/tree/master/docs)  |  [to-do list](https://waffle.io/kriasoft/react-starter-kit)  |  @@ -27,8 +27,8 @@ visit our sponsors:

### Backers ♥ React Starter Kit? Help us keep it alive by donating funds to cover project -expenses via [Bountysource](https://salt.bountysource.com/teams/react-starter-kit) -or [PayPal](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=RETHAJHV3T972)! +expenses via [OpenCollective](https://opencollective.com/react-starter-kit) or +[PayPal](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=RETHAJHV3T972)!
lehneres @@ -48,16 +48,10 @@ or [PayPal](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id Zane Hitchcox - + -### Related Projects - - * [Membership Database](https://github.com/membership/membership.db) — SQL schema boilerplate for user accounts, profiles, roles, and auth claims - * [React Static Boilerplate](https://github.com/koistya/react-static-boilerplate) — Generates static websites from React components - * [Babel Starter Kit](https://github.com/kriasoft/babel-starter-kit) — Boilerplate for authoring JavaScript/React.js libraries - ### Learn More * [Getting Started with React.js](http://facebook.github.io/react/) @@ -65,11 +59,17 @@ or [PayPal](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id * [React.js Questions on StackOverflow](http://stackoverflow.com/questions/tagged/reactjs) * [React.js Discussion Board](https://discuss.reactjs.org/) * [Flux Architecture for Building User Interfaces](http://facebook.github.io/flux/) - * [Jest - Painless Unit Testing](http://facebook.github.io/jest/) - * [Flow - A static type checker for JavaScript](http://flowtype.org/) + * [Enzyme — JavaScript Testing utilities for React](http://airbnb.io/enzyme/) + * [Flow — A static type checker for JavaScript](http://flowtype.org/) * [The Future of React](https://github.com/reactjs/react-future) * [Learn ES6](https://babeljs.io/docs/learn-es6/), [ES6 Features](https://github.com/lukehoban/es6features#readme) +### Related Projects + + * [Membership Database](https://github.com/membership/membership.db) — SQL schema boilerplate for user accounts, profiles, roles, and auth claims + * [React Static Boilerplate](https://github.com/koistya/react-static-boilerplate) — Generates static websites from React components + * [Babel Starter Kit](https://github.com/kriasoft/babel-starter-kit) — Boilerplate for authoring JavaScript/React.js libraries + ### Support * [#react-starter-kit](http://stackoverflow.com/questions/tagged/react-starter-kit) on Stack Overflow — Questions and answers diff --git a/docs/getting-started.md b/docs/getting-started.md index e3d13ddd8..ff9bca9f3 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -1,28 +1,34 @@ ## Getting Started +### Requirements + + * Mac OS X, Windows, or Linux + * [Node.js](https://nodejs.org/) v5.0 or newer + * `npm` v3.3 or newer (new to [npm](https://docs.npmjs.com/)?) + * `node-gyp` prerequisites mentioned [here](https://github.com/nodejs/node-gyp) + * Text editor or IDE pre-configured with React/JSX/Flow/ESlint ([learn more](./how-to-configure-text-editors.md)) + ### Directory Layout +Before you start, take a moment to see how the project structure looks like: + ``` . ├── /build/ # The folder for compiled output ├── /docs/ # Documentation files for the project ├── /node_modules/ # 3rd-party libraries and utilities ├── /src/ # The source code of the application -│ ├── /actions/ # Action creators that allow to trigger a dispatch to stores │ ├── /components/ # React components -│ ├── /constants/ # Constants (action types etc.) -│ ├── /content/ # Static content (plain HTML or Markdown, Jade, you name it) +│ ├── /content/ # Static pages like About Us, Privacy Policy etc. │ ├── /core/ # Core framework and utility functions -│ ├── /data/ # GraphQL server schema -│ ├── /decorators/ # Higher-order React components +│ ├── /data/ # GraphQL server schema and data models │ ├── /public/ # Static files which are copied into the /build/public folder │ ├── /routes/ # Page/screen components along with the routing information -│ ├── /stores/ # Stores contain the application state and logic -│ ├── /views/ # Express.js views for index and error pages +│ ├── /views/ # Express.js views (templates) for index and error pages │ ├── /client.js # Client-side startup script │ ├── /config.js # Global application settings -│ ├── /routes.js # Universal (isomorphic) application routes │ └── /server.js # Server-side startup script +├── /test/ # Unit and end-to-end tests ├── /tools/ # Build automation scripts and utilities │ ├── /lib/ # Library for utility snippets │ ├── /build.js # Builds the project from source to output (build) folder @@ -37,13 +43,10 @@ └── package.json # The list of 3rd party libraries and utilities ``` -### Requirements - - * Mac OS X, Windows, or Linux - * [Node.js](https://nodejs.org/) v5.0 or newer - * `npm` v3.3 or newer (new to [npm](https://docs.npmjs.com/)?) - * `node-gyp` prerequisites mentioned [here](https://github.com/nodejs/node-gyp) - * Text editor or IDE pre-configured with React/JSX/Flow/ESlint ([learn more](./how-to-configure-text-editors.md)) +**Note**: The current version of RSK does not contain a Flux implementation. +It can be easily integrated with any Flux library of your choice. The most +commonly used Flux libraries are [Flux](http://facebook.github.io/flux/), +[Redux](http://redux.js.org/), and [Relay](http://facebook.github.io/relay/). ### Quick Start @@ -122,12 +125,13 @@ $ npm run lint To launch unit tests: ```shell -$ npm test +$ npm test # Run unit tests with Mocha +$ npm run test:watch # Launch unit test runner and start watching for changes ``` -Test any javascript module by creating a `__tests__/` directory where -the file is. Append `-test.js` to the filename and -[Jest](https://facebook.github.io/jest/) will do the rest. +By default, [Mocha](https://mochajs.org/) test runner is looking for test files +matching the `src/**/*.test.js` pattern. Take a look at `src/components/App/App.test.js` +as an example. To deploy the app, run: diff --git a/package.json b/package.json index 4a3e1262f..3ea04e1cb 100644 --- a/package.json +++ b/package.json @@ -22,12 +22,12 @@ "graphiql": "0.7.0", "graphql": "0.5.0", "history": "2.0.2", - "isomorphic-style-loader": "0.0.12", + "isomorphic-style-loader": "1.0.0", "jade": "1.11.0", "jsonwebtoken": "5.7.0", "markdown-it": "6.0.1", - "node-fetch": "1.5.0", - "normalize.css": "4.0.0", + "node-fetch": "1.5.1", + "normalize.css": "4.1.1", "passport": "0.3.2", "passport-facebook": "2.1.0", "pretty-error": "2.0.0", @@ -44,9 +44,9 @@ "autoprefixer": "^6.3.6", "babel-cli": "^6.7.5", "babel-eslint": "^6.0.2", - "babel-jest": "^10.0.1", "babel-loader": "^6.2.4", "babel-plugin-react-transform": "^2.0.2", + "babel-plugin-rewire": "^1.0.0-rc-2", "babel-plugin-transform-react-constant-elements": "^6.5.0", "babel-plugin-transform-react-inline-elements": "^6.6.5", "babel-plugin-transform-react-remove-prop-types": "^0.2.5", @@ -55,13 +55,16 @@ "babel-preset-node5": "^11.0.1", "babel-preset-react": "^6.5.0", "babel-preset-stage-0": "^6.5.0", - "browser-sync": "^2.11.2", + "babel-register": "^6.7.2", + "browser-sync": "^2.12.3", + "chai": "^3.5.0", "css-loader": "^0.23.1", "del": "^2.2.0", "enzyme": "^2.2.0", - "eslint": "^2.7.0", - "eslint-config-airbnb": "^6.2.0", + "eslint": "^2.8.0", + "eslint-config-airbnb": "^7.0.0", "eslint-loader": "^1.3.0", + "eslint-plugin-jsx-a11y": "^0.6.2", "eslint-plugin-react": "^4.3.0", "estraverse-fb": "^1.3.1", "extend": "^3.0.0", @@ -70,7 +73,6 @@ "git-repository": "^0.1.4", "glob": "^7.0.3", "jade-loader": "^0.8.0", - "jest-cli": "^0.10.0", "jscs": "^2.11.0", "json-loader": "^0.5.4", "mkdirp": "^0.5.1", @@ -88,7 +90,7 @@ "stylelint": "^5.4.0", "stylelint-config-standard": "^5.0.0", "url-loader": "^0.5.7", - "webpack": "^1.12.14", + "webpack": "^1.13.0", "webpack-hot-middleware": "^2.10.0", "webpack-middleware": "^1.5.1" }, @@ -97,7 +99,14 @@ "react", "node5", "stage-0" - ] + ], + "env": { + "test": { + "plugins": [ + "rewire" + ] + } + } }, "eslintConfig": { "parser": "babel-eslint", @@ -106,8 +115,7 @@ "__DEV__": true }, "env": { - "browser": true, - "jest": true + "browser": true }, "rules": { "no-confusing-arrow": 0, @@ -126,21 +134,6 @@ ], "disallowSpacesInsideTemplateStringPlaceholders": null }, - "jest": { - "rootDir": "./src", - "testPathDirs": [ - "", - "/../test/" - ], - "moduleNameMapper": { - "\\.scss$": "SCSSStub" - }, - "unmockedModulePathPatterns": [ - "react", - "enzyme", - "core-js" - ] - }, "stylelint": { "extends": "stylelint-config-standard", "rules": { @@ -152,7 +145,8 @@ "jscs": "jscs src tools --verbose", "stylelint": "stylelint \"src/**/*.scss\" --syntax scss", "lint": "npm run eslint && npm run jscs && npm run stylelint", - "test": "npm run lint && jest", + "test": "mocha src/**/*.test.js --require test/setup.js --compilers js:babel-register", + "test:watch": "mocha src/**/*.test.js --require test/setup.js --compilers js:babel-register --reporter min --watch", "clean": "babel-node tools/run clean", "copy": "babel-node tools/run copy", "bundle": "babel-node tools/run bundle", diff --git a/src/actions/.gitignore b/src/actions/.gitignore deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/components/App/App.test.js b/src/components/App/App.test.js new file mode 100644 index 000000000..2e392637e --- /dev/null +++ b/src/components/App/App.test.js @@ -0,0 +1,30 @@ +/** + * React Starter Kit (https://www.reactstarterkit.com/) + * + * Copyright © 2014-2016 Kriasoft, LLC. All rights reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE.txt file in the root directory of this source tree. + */ + +/* eslint-env mocha */ +/* eslint-disable padded-blocks, no-unused-expressions */ + +import React from 'react'; +import { expect } from 'chai'; +import { shallow } from 'enzyme'; +import App from './App'; + +describe('App', () => { + + it('renders children correctly', () => { + const wrapper = shallow( + {} }}> +
+ + ); + + expect(wrapper.contains(
)).to.be.true; + }); + +}); diff --git a/src/components/App/__tests__/App-test.js b/src/components/App/__tests__/App-test.js deleted file mode 100644 index 9bf856893..000000000 --- a/src/components/App/__tests__/App-test.js +++ /dev/null @@ -1,16 +0,0 @@ -jest.unmock('../App'); - -import App from '../App'; -import React from 'react'; -import { shallow } from 'enzyme'; - -describe('App', () => { - it('renders children correctly', () => { - const wrapper = shallow( - {} }}> -
- - ); - expect(wrapper.contains(
)).toBe(true); - }); -}); diff --git a/src/components/Feedback/Feedback.js b/src/components/Feedback/Feedback.js index 3688c506e..1ed0ec6e7 100644 --- a/src/components/Feedback/Feedback.js +++ b/src/components/Feedback/Feedback.js @@ -29,4 +29,4 @@ function Feedback() { ); } -export default withStyles(Feedback, s); +export default withStyles(s)(Feedback); diff --git a/src/components/Footer/Footer.js b/src/components/Footer/Footer.js index b1a680d5e..23b62b3a7 100644 --- a/src/components/Footer/Footer.js +++ b/src/components/Footer/Footer.js @@ -28,4 +28,4 @@ function Footer() { ); } -export default withStyles(Footer, s); +export default withStyles(s)(Footer); diff --git a/src/components/Header/Header.js b/src/components/Header/Header.js index 7b337e52b..125268b58 100644 --- a/src/components/Header/Header.js +++ b/src/components/Header/Header.js @@ -31,4 +31,4 @@ function Header() { ); } -export default withStyles(Header, s); +export default withStyles(s)(Header); diff --git a/src/components/Navigation/Navigation.js b/src/components/Navigation/Navigation.js index 72cac0217..8e75b17f4 100644 --- a/src/components/Navigation/Navigation.js +++ b/src/components/Navigation/Navigation.js @@ -30,4 +30,4 @@ Navigation.propTypes = { className: PropTypes.string, }; -export default withStyles(Navigation, s); +export default withStyles(s)(Navigation); diff --git a/src/constants/ActionTypes.js b/src/constants/ActionTypes.js deleted file mode 100644 index 8298a2344..000000000 --- a/src/constants/ActionTypes.js +++ /dev/null @@ -1,14 +0,0 @@ -/** - * React Starter Kit (https://www.reactstarterkit.com/) - * - * Copyright © 2014-2016 Kriasoft, LLC. All rights reserved. - * - * This source code is licensed under the MIT license found in the - * LICENSE.txt file in the root directory of this source tree. - */ - -import keyMirror from 'fbjs/lib/keyMirror'; - -export default keyMirror({ - -}); diff --git a/src/routes/contact/Contact.js b/src/routes/contact/Contact.js index b06518714..0f28b5cec 100644 --- a/src/routes/contact/Contact.js +++ b/src/routes/contact/Contact.js @@ -27,4 +27,4 @@ function Contact(props, context) { Contact.contextTypes = { setTitle: PropTypes.func.isRequired }; -export default withStyles(Contact, s); +export default withStyles(s)(Contact); diff --git a/src/routes/content/Content.js b/src/routes/content/Content.js index 2c36802d9..958a47d1f 100644 --- a/src/routes/content/Content.js +++ b/src/routes/content/Content.js @@ -40,4 +40,4 @@ class Content extends Component { } -export default withStyles(Content, s); +export default withStyles(s)(Content); diff --git a/src/routes/error/ErrorPage.js b/src/routes/error/ErrorPage.js index 21d3bbfe7..6b2b03587 100644 --- a/src/routes/error/ErrorPage.js +++ b/src/routes/error/ErrorPage.js @@ -37,4 +37,4 @@ function ErrorPage({ error }, context) { ErrorPage.propTypes = { error: PropTypes.object.isRequired }; ErrorPage.contextTypes = { setTitle: PropTypes.func.isRequired }; -export default withStyles(ErrorPage, s); +export default withStyles(s)(ErrorPage); diff --git a/src/routes/home/Home.js b/src/routes/home/Home.js index c3165f767..7bcecfb2e 100644 --- a/src/routes/home/Home.js +++ b/src/routes/home/Home.js @@ -44,4 +44,4 @@ Home.propTypes = { }; Home.contextTypes = { setTitle: PropTypes.func.isRequired }; -export default withStyles(Home, s); +export default withStyles(s)(Home); diff --git a/src/routes/login/Login.js b/src/routes/login/Login.js index 4cbecc3f1..985c0297e 100644 --- a/src/routes/login/Login.js +++ b/src/routes/login/Login.js @@ -117,4 +117,4 @@ function Login(props, context) { Login.contextTypes = { setTitle: PropTypes.func.isRequired }; -export default withStyles(Login, s); +export default withStyles(s)(Login); diff --git a/src/routes/register/Register.js b/src/routes/register/Register.js index cec021b63..4c1af503f 100644 --- a/src/routes/register/Register.js +++ b/src/routes/register/Register.js @@ -27,4 +27,4 @@ function Register(props, context) { Register.contextTypes = { setTitle: PropTypes.func.isRequired }; -export default withStyles(Register, s); +export default withStyles(s)(Register); diff --git a/src/stores/.gitignore b/src/stores/.gitignore deleted file mode 100644 index e69de29bb..000000000 diff --git a/test/.eslintrc b/test/.eslintrc new file mode 100644 index 000000000..499c2d6d5 --- /dev/null +++ b/test/.eslintrc @@ -0,0 +1,9 @@ +{ + "env": { + "mocha": true + }, + "rules": { + "no-unused-expressions": 0, + "padded-blocks": 0 + } +} diff --git a/test/setup.js b/test/setup.js new file mode 100644 index 000000000..2aa607f65 --- /dev/null +++ b/test/setup.js @@ -0,0 +1,25 @@ +/** + * React Starter Kit (https://www.reactstarterkit.com/) + * + * Copyright © 2014-2016 Kriasoft, LLC. All rights reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE.txt file in the root directory of this source tree. + */ + +/* Configure Mocha test runner, see package.json/scripts/test */ + +process.env.NODE_ENV = 'test'; + +function noop() { + return null; +} + +require.extensions['.css'] = noop; +require.extensions['.scss'] = noop; +require.extensions['.md'] = noop; +require.extensions['.png'] = noop; +require.extensions['.svg'] = noop; +require.extensions['.jpg'] = noop; +require.extensions['.jpeg'] = noop; +require.extensions['.gif'] = noop; diff --git a/test/stubs/SCSSStub.js b/test/stubs/SCSSStub.js deleted file mode 100644 index 95c47ee81..000000000 --- a/test/stubs/SCSSStub.js +++ /dev/null @@ -1,5 +0,0 @@ -/** - * @providesModule SCSSStub - */ - -module.exports = {}; From c299205ac1dc09170320d846f09989f2437c942d Mon Sep 17 00:00:00 2001 From: Konstantin Tarkus Date: Sat, 16 Apr 2016 15:45:23 +0300 Subject: [PATCH 022/357] Add missing dependencies - mocha and sinon --- package.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/package.json b/package.json index 3ea04e1cb..5cd5150b2 100644 --- a/package.json +++ b/package.json @@ -76,6 +76,7 @@ "jscs": "^2.11.0", "json-loader": "^0.5.4", "mkdirp": "^0.5.1", + "mocha": "^2.4.5", "ncp": "^2.0.0", "postcss": "^5.0.19", "postcss-import": "^8.1.0", @@ -87,6 +88,7 @@ "react-transform-catch-errors": "^1.0.2", "react-transform-hmr": "^1.0.4", "redbox-react": "^1.2.3", + "sinon": "^2.0.0-pre", "stylelint": "^5.4.0", "stylelint-config-standard": "^5.0.0", "url-loader": "^0.5.7", From d3ba59bc2b406e0422967b6e4a79ab1e863dbe82 Mon Sep 17 00:00:00 2001 From: Konstantin Tarkus Date: Sat, 16 Apr 2016 16:02:06 +0300 Subject: [PATCH 023/357] Update .travis.yml (#577) --- .travis.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 09cbb6d21..5de4afb57 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,3 @@ -sudo: false language: node_js node_js: - '5' @@ -10,3 +9,6 @@ addons: - ubuntu-toolchain-r-test packages: - g++-4.8 +script: + - npm run lint + - npm run test From b24d0146b871eaa7e03cd7d4725dc8a3a29b1d9a Mon Sep 17 00:00:00 2001 From: Konstantin Tarkus Date: Sun, 17 Apr 2016 22:50:56 +0300 Subject: [PATCH 024/357] Add missing peer dependencies - babe-core... Add babel-core, babel-template and babel-types required by babel-plugin-rewire; remove estraverse-fb which is no longer in use Fixes #581 --- package.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 5cd5150b2..f7567d538 100644 --- a/package.json +++ b/package.json @@ -43,6 +43,7 @@ "assets-webpack-plugin": "^3.4.0", "autoprefixer": "^6.3.6", "babel-cli": "^6.7.5", + "babel-core": "^6.7.6", "babel-eslint": "^6.0.2", "babel-loader": "^6.2.4", "babel-plugin-react-transform": "^2.0.2", @@ -56,6 +57,8 @@ "babel-preset-react": "^6.5.0", "babel-preset-stage-0": "^6.5.0", "babel-register": "^6.7.2", + "babel-template": "^6.7.0", + "babel-types": "^6.7.2", "browser-sync": "^2.12.3", "chai": "^3.5.0", "css-loader": "^0.23.1", @@ -66,7 +69,6 @@ "eslint-loader": "^1.3.0", "eslint-plugin-jsx-a11y": "^0.6.2", "eslint-plugin-react": "^4.3.0", - "estraverse-fb": "^1.3.1", "extend": "^3.0.0", "file-loader": "^0.8.5", "gaze": "^1.0.0", From ed2b9c173ae43e434afedb3a6629023570bb030c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20M=C3=BChl?= Date: Mon, 18 Apr 2016 21:46:38 +0700 Subject: [PATCH 025/357] Fix ESLint: Local variable 'smth' is redundant (#589) --- src/data/queries/content.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/data/queries/content.js b/src/data/queries/content.js index 305e89052..ffd9e84f2 100644 --- a/src/data/queries/content.js +++ b/src/data/queries/content.js @@ -43,8 +43,7 @@ const parseContent = (path, fileContent, extension) => { default: return null; } - const smth = Object.assign({ path, content: htmlContent }, fmContent.attributes); - return smth; + return Object.assign({ path, content: htmlContent }, fmContent.attributes); }; const readFile = Promise.promisify(fs.readFile); From b505a7e193ef953f9ae48901b978543cd303a191 Mon Sep 17 00:00:00 2001 From: Sergiu Falcusan Date: Tue, 19 Apr 2016 19:29:45 +0300 Subject: [PATCH 026/357] Fix: AccessToken from facebook saved as String (#590) --- src/data/models/UserClaim.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/data/models/UserClaim.js b/src/data/models/UserClaim.js index 132de13ee..8eb90bbae 100644 --- a/src/data/models/UserClaim.js +++ b/src/data/models/UserClaim.js @@ -17,7 +17,7 @@ const UserClaim = Model.define('UserClaim', { }, value: { - type: DataType.INTEGER, + type: DataType.STRING, }, }); From 5e4bacbe07084396eda978b2b7ca246cc72506b3 Mon Sep 17 00:00:00 2001 From: Konstantin Tarkus Date: Thu, 21 Apr 2016 06:24:31 +0300 Subject: [PATCH 027/357] Update npm modules --- package.json | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/package.json b/package.json index f7567d538..b18438df8 100644 --- a/package.json +++ b/package.json @@ -17,11 +17,11 @@ "express-graphql": "0.5.1", "express-jwt": "3.3.0", "fastclick": "1.0.6", - "fbjs": "0.8.0", + "fbjs": "0.8.1", "front-matter": "2.0.7", "graphiql": "0.7.0", "graphql": "0.5.0", - "history": "2.0.2", + "history": "2.1.0", "isomorphic-style-loader": "1.0.0", "jade": "1.11.0", "jsonwebtoken": "5.7.0", @@ -42,9 +42,9 @@ "devDependencies": { "assets-webpack-plugin": "^3.4.0", "autoprefixer": "^6.3.6", - "babel-cli": "^6.7.5", - "babel-core": "^6.7.6", - "babel-eslint": "^6.0.2", + "babel-cli": "^6.7.7", + "babel-core": "^6.7.7", + "babel-eslint": "^6.0.3", "babel-loader": "^6.2.4", "babel-plugin-react-transform": "^2.0.2", "babel-plugin-rewire": "^1.0.0-rc-2", @@ -58,7 +58,7 @@ "babel-preset-stage-0": "^6.5.0", "babel-register": "^6.7.2", "babel-template": "^6.7.0", - "babel-types": "^6.7.2", + "babel-types": "^6.7.7", "browser-sync": "^2.12.3", "chai": "^3.5.0", "css-loader": "^0.23.1", @@ -91,8 +91,8 @@ "react-transform-hmr": "^1.0.4", "redbox-react": "^1.2.3", "sinon": "^2.0.0-pre", - "stylelint": "^5.4.0", - "stylelint-config-standard": "^5.0.0", + "stylelint": "^6.0.3", + "stylelint-config-standard": "^6.0.0", "url-loader": "^0.5.7", "webpack": "^1.13.0", "webpack-hot-middleware": "^2.10.0", From 18593c5c1fc0b380e68ba8a4656643091926ea6e Mon Sep 17 00:00:00 2001 From: Zane Hitchcox Date: Fri, 22 Apr 2016 16:47:06 -0500 Subject: [PATCH 028/357] Fixes #614 --- tools/copy.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tools/copy.js b/tools/copy.js index 46107342b..4088c0337 100644 --- a/tools/copy.js +++ b/tools/copy.js @@ -37,10 +37,12 @@ async function copy({ watch } = {}) { const watcher = await new Promise((resolve, reject) => { gaze('src/content/**/*.*', (err, val) => err ? reject(err) : resolve(val)); }); - watcher.on('changed', async (file) => { + const cp = async (file) => { const relPath = file.substr(path.join(__dirname, '../src/content/').length); await ncp(`src/content/${relPath}`, `build/content/${relPath}`); - }); + } + watcher.on('changed', cp); + watcher.on('added', cp); } } From 5e13920257c22262f82a56b2de389ecc3751dbc4 Mon Sep 17 00:00:00 2001 From: Pavel Lang Date: Thu, 28 Apr 2016 14:55:56 +0200 Subject: [PATCH 029/357] [fix] linter error --- tools/copy.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tools/copy.js b/tools/copy.js index 4088c0337..4144ade90 100644 --- a/tools/copy.js +++ b/tools/copy.js @@ -37,12 +37,14 @@ async function copy({ watch } = {}) { const watcher = await new Promise((resolve, reject) => { gaze('src/content/**/*.*', (err, val) => err ? reject(err) : resolve(val)); }); + const cp = async (file) => { const relPath = file.substr(path.join(__dirname, '../src/content/').length); await ncp(`src/content/${relPath}`, `build/content/${relPath}`); - } + }; + watcher.on('changed', cp); - watcher.on('added', cp); + watcher.on('added', cp); } } From ba597afa956dcc5785db4a70bc803581ff2d7bfb Mon Sep 17 00:00:00 2001 From: Pavel Lang Date: Thu, 28 Apr 2016 17:57:12 +0200 Subject: [PATCH 030/357] Updated docs Closes #623 Closes #612 Closes #610 Closes #595 Closes #173 Many thanks to @tobiasmuehl and @tomByrer and @bravo_kernel for reviewing. --- CONTRIBUTING.md | 1 + README.md | 19 +++++ docs/README.md | 2 + docs/recipes/feature-react-intl.md | 122 +++++++++++++++++++++++++++++ docs/recipes/feature-redux.md | 56 +++++++++++++ 5 files changed, 200 insertions(+) create mode 100644 docs/recipes/feature-react-intl.md create mode 100644 docs/recipes/feature-redux.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 701723713..875486870 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -49,6 +49,7 @@ forked repo, check that it meets these guidelines: * [Squash](http://stackoverflow.com/questions/5189560/squash-my-last-x-commits-together-using-git) your commits into one for each PR. * Run `npm test` to make sure that your code style is OK and there are no any regression bugs. +* When contributing to an opt-in feature, apply the `[feature/...]` tag as a prefix to your PR title #### Style Guide diff --git a/README.md b/README.md index 9f0be03ec..bdd8441dd 100644 --- a/README.md +++ b/README.md @@ -52,6 +52,25 @@ expenses via [OpenCollective](https://opencollective.com/react-starter-kit) or +### Feature branches + +Some features aren't provided by default, but you can optionally add them. +To do so, simply merge the corresponding feature branch. +These branches should be in sync with master and all other features branches +so there should not be any merging conflicts. +If conflicts occur, please [report to us](https://github.com/kriasoft/react-starter-kit/issues). + + * [feature/redux](https://github.com/kriasoft/react-starter-kit/tree/feature/redux) – isomorphic redux support. + Use [`connect()`](https://github.com/reactjs/react-redux/blob/master/docs/api.md#connectmapstatetoprops-mapdispatchtoprops-mergeprops-options) + higher order component to access state in redux store and to get actions mapped on dispatch. + You can see [LanguageSwitcher](https://github.com/kriasoft/react-starter-kit/blob/86eadfd3d11d804cf858aa21f657022fcc098752/src/components/LanguageSwitcher/LanguageSwitcher.js) component how to use `connect()` +
+ [Read full recipe](./docs/recipes/feature-redux.md) + + * [feature/react-intl](https://github.com/kriasoft/react-starter-kit/tree/feature/react-intl) – [`react-intl`](https://github.com/yahoo/react-intl#react-intl) integration based on `feature/redux` +
+ [Read full recipe](./docs/recipes/feature-react-intl.md) + ### Learn More * [Getting Started with React.js](http://facebook.github.io/react/) diff --git a/docs/README.md b/docs/README.md index a3c529cf7..c6ede3cc6 100644 --- a/docs/README.md +++ b/docs/README.md @@ -15,4 +15,6 @@ ### Recipes * [How to Implement Routing and Navigation](./recipes/how-to-implement-routing.md) +* [How to Integrate Redux](./recipes/feature-redux.md) +* [How to Integrate React-Intl](./recipes/feature-react-intl.md) * [How to Integrate Disqus](./recipes/how-to-integrate-disqus.md) diff --git a/docs/recipes/feature-react-intl.md b/docs/recipes/feature-react-intl.md new file mode 100644 index 000000000..023bc3ea0 --- /dev/null +++ b/docs/recipes/feature-react-intl.md @@ -0,0 +1,122 @@ +## Integrating [React-Intl](https://github.com/yahoo/react-intl#react-intl) + + 1. Merge `feature/react-intl` branch with git. + Because react-intl integration is built on top of `feature/redux`, you'll also get all the features. + + 2. Adjust `INTL_REQUIRE_DESCRIPTIONS` constant in `tools/webpack.config.js` around line 17: + ```js + const INTL_REQUIRE_DESCRIPTIONS = true; + ``` + When this boolean is set to true, the build will only succeed if a `description` is set for every message descriptor. + + 3. Adjust `locales` settings in `src/config.js`: + ```js + // default locale is the first one + export const locales = ['en-GB', 'cs-CZ']; + ``` + Note that you should follow + [BCP 47](https://tools.ietf.org/html/bcp47) + ([RFC 5646](https://tools.ietf.org/html/rfc5646)). + + 4. Add locale support in `src/client.js`: + ```js + import en from 'react-intl/locale-data/en'; + import cs from 'react-intl/locale-data/cs'; + ... + + [en, cs].forEach(addLocaleData); + ``` + + 5. Execute `npm run extractMessages` or `npm start` to strip out messages. + Message files are created in `src/messages` directory. + + 6. Edit `src/messages/*.json` files, change only `message` property. + + 7. Execute `npm run build`, + your translations should be copied to `build/messages/` directory. + + +## How to write localizable components + +Just import the appropriate [component](https://github.com/yahoo/react-intl/wiki#the-react-intl-module) from `react-intl` + +- For localizable text use +[``](https://github.com/yahoo/react-intl/wiki/Components#formattedmessage). +- You can also use it with +the [`defineMessages()`](https://github.com/yahoo/react-intl/wiki/API#definemessages) helper. + +- For date and time: +[``](https://github.com/yahoo/react-intl/wiki/Components#formatteddate) +[``](https://github.com/yahoo/react-intl/wiki/Components#formattedtime) +[``](https://github.com/yahoo/react-intl/wiki/Components#formattedrelative) + +- For numbers and currencies: +[``](https://github.com/yahoo/react-intl/wiki/Components#formattednumber) +[``](https://github.com/yahoo/react-intl/wiki/Components#formattedplural) + +- If possible, do not use ``, see how to use *Rich Text Formatting* with +[``](https://github.com/yahoo/react-intl/wiki/Components#formattedmessage) + +- When you need an imperative formatting API, use the [`injectIntl`](https://github.com/yahoo/react-intl/wiki/API#injectintl) High-Order Component. + +### Example + +```jsx +import { defineMessages, FormattedMessage, injectIntl, intlShape } from 'react-intl'; + +const messages = defineMessages({ + text: { + id: 'example.text', + defaultMessage: 'Example text', + description: 'Hi Pavel', + }, + textTemplate: { + id: 'example.text.template', + defaultMessage: 'Example text template', + description: 'Hi {name}', + }, +}); + +function Example(props) { + const text = props.intl.formatMessage(messages.textTemplate, { name: 'Pavel'}); + return ( +
+ + + Pavel + }} + /> +
+ ); +} + +Example.propTypes = { + intl: intlShape, +} + +export default injectIntl(Example); +``` + +## Updating translations + +When running the development server, every source file is watched and parsed for changed messages. + +Messages files are updated on the fly. +If a new definition is found, this definition is added to the end of every used `src/messages/xx-XX.json` file so when commiting, new translations will be at the tail of file. + +When an untranslated message is removed and its `message` field is empty as well, the message will be deleted from all translation files. This is why the `files` array is present. + +When editiong a translation file, it should be copied to `build/messages/` directory. + +## Other References + + * [`Intl documentation on MDN`](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Intl) + * [express-request-language](https://github.com/tinganho/express-request-language#readme) + – for more details how initial language negotiation works. diff --git a/docs/recipes/feature-redux.md b/docs/recipes/feature-redux.md new file mode 100644 index 000000000..dd3bfb9e6 --- /dev/null +++ b/docs/recipes/feature-redux.md @@ -0,0 +1,56 @@ +## Integrating [Redux](http://redux.js.org/index.html) + +Merge `feature/redux` branch with git. +If you are interested in `feature/react-intl`, +merge that branch instead as it also includes Redux. + +**If you don't know redux well, you should [read about it first](http://redux.js.org/docs/basics/index.html).** + + +## Creating Actions + + 1. Go to `src/constants/index.js` and define action name there. + + 2. Go to `src/actions/` and create file with appropriate name. + You can copy `src/actions/runtime.js` as a template. + + 3. If you need async actions, use [`redux-thunk`](https://github.com/gaearon/redux-thunk#readme). + For inspiration on how to create async actions you can look at + [`setLocale`](https://github.com/kriasoft/react-starter-kit/blob/feature/react-intl/src/actions/intl.js) + action from `feature/react-intl`. + See [Async Flow](http://redux.js.org/docs/advanced/AsyncFlow.html) for more information on this topic. + + +## Creating Reducer (aka Store) + + 1. Go to [`src/reducers/`](https://github.com/kriasoft/react-starter-kit/tree/feature/redux/src/reducers) and create new file there. + + You can copy [`src/reducers/runtime.js`](https://github.com/kriasoft/react-starter-kit/tree/feature/redux/src/reducers/runtime.js) as a template. + + - Do not forget to always return `state`. + - Never mutate provided `state`. + If you mutate state, rendering of connected component will not be triggered because of `===` equality. + Always return new value if you perform state update. + You can use this construct: `{ ...state, updatedKey: action.payload.value, }` + - Keep in mind that store state *must* be repeatable by replaying actions on it. + For example, when you store timestamp, pass it into *action payload*. + If you call REST API, do it in action. *Never do this in reducer!* + + 2. Edit [`src/reducers/index.js`](https://github.com/kriasoft/react-starter-kit/tree/feature/redux/src/reducers/index.js), import your reducer and add it to root reducer created by + [`combineReducers`](http://redux.js.org/docs/api/combineReducers.html) + + +## Connecting Components + +You can use [`connect()`](https://github.com/reactjs/react-redux/blob/master/docs/api.md#connectmapstatetoprops-mapdispatchtoprops-mergeprops-options) High-Order Component from [`react-redux`](https://github.com/reactjs/react-redux#readme) package. + +See [Usage With React](http://redux.js.org/docs/basics/UsageWithReact.html) on redux.js.org. + +For an example you can look at +[``](https://github.com/kriasoft/react-starter-kit/blob/feature/react-intl/src/components/LanguageSwitcher/LanguageSwitcher.js) +component from `feature/react-intl` branch. It demonstrates both subscribing to store and dispatching actions. + + +## Dispatching Actions On Server + +See source of `src/server.js` From 200c229e4eb8995dc8d92065d5a223a300299ce3 Mon Sep 17 00:00:00 2001 From: Vladimir Kutepov Date: Fri, 13 May 2016 12:15:54 +0300 Subject: [PATCH 031/357] Add --static flag to build script which allows to generate static html for specific routes (#642) --- package.json | 1 + tools/README.md | 1 + tools/build.js | 5 +++++ tools/render.js | 45 +++++++++++++++++++++++++++++++++++++++++++++ tools/runServer.js | 1 + 5 files changed, 53 insertions(+) create mode 100644 tools/render.js diff --git a/package.json b/package.json index b18438df8..219df4d16 100644 --- a/package.json +++ b/package.json @@ -156,6 +156,7 @@ "bundle": "babel-node tools/run bundle", "build": "babel-node tools/run build", "deploy": "babel-node tools/run deploy", + "render": "babel-node tools/run render", "start": "babel-node tools/run start" } } diff --git a/tools/README.md b/tools/README.md index 85881717d..6b4fefa42 100644 --- a/tools/README.md +++ b/tools/README.md @@ -27,6 +27,7 @@ Flag | Description ----------- | -------------------------------------------------- `--release` | Minimizes and optimizes the compiled output `--verbose` | Prints detailed information to the console +`--static` | Renders [specified routes](./render.js#L15) as static html files For example: diff --git a/tools/build.js b/tools/build.js index 482de1bda..9ce2632a6 100644 --- a/tools/build.js +++ b/tools/build.js @@ -11,6 +11,7 @@ import run from './run'; import clean from './clean'; import copy from './copy'; import bundle from './bundle'; +import render from './render'; /** * Compiles the project from source files into a distributable @@ -20,6 +21,10 @@ async function build() { await run(clean); await run(copy); await run(bundle); + + if (process.argv.includes('--static')) { + await run(render); + } } export default build; diff --git a/tools/render.js b/tools/render.js new file mode 100644 index 000000000..5512f8e17 --- /dev/null +++ b/tools/render.js @@ -0,0 +1,45 @@ +/** + * React Starter Kit (https://www.reactstarterkit.com/) + * + * Copyright © 2014-2016 Kriasoft, LLC. All rights reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE.txt file in the root directory of this source tree. + */ + +import runServer from './runServer'; +import fs from './lib/fs'; +import fetch from 'node-fetch'; +import { host } from '../src/config'; + +// Enter your paths here which you want to render as static +const routes = [ + '/', + '/contact', + '/login', + '/register', + '/about', + '/privacy', + '/404', // https://help.github.com/articles/creating-a-custom-404-page-for-your-github-pages-site/ +]; + +async function render() { + let server; + await new Promise(resolve => (server = runServer(resolve))); + + await routes.reduce((promise, route) => promise.then(async () => { + const url = `http://${host}${route}`; + const dir = `build/public${route.replace(/[^\/]*$/, '')}`; + const name = route.endsWith('/') ? 'index.html' : `${route.match(/[^/]+$/)[0]}.html`; + const dist = `${dir}${name}`; + const res = await fetch(url); + const text = await res.text(); + await fs.makeDir(dir); + await fs.writeFile(dist, text); + console.log(`${dist} => ${res.status} ${res.statusText}`); + }), Promise.resolve()); + + server.kill('SIGTERM'); +} + +export default render; diff --git a/tools/runServer.js b/tools/runServer.js index 4846be6fc..f2bb51316 100644 --- a/tools/runServer.js +++ b/tools/runServer.js @@ -47,6 +47,7 @@ function runServer(cb) { server.stdout.on('data', onStdOut); server.stderr.on('data', x => process.stderr.write(x)); + return server; } process.on('exit', () => { From 03153a3b6df9d0a14523fc2f751b356f01a0b363 Mon Sep 17 00:00:00 2001 From: Pepijn Senders Date: Tue, 17 May 2016 19:45:34 +0200 Subject: [PATCH 032/357] Update test scripts to fix an issue with glob arguments (#656) - Add quotes to mocha glob - Change watcher to extend from the regular test call --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 219df4d16..84171a5db 100644 --- a/package.json +++ b/package.json @@ -149,8 +149,8 @@ "jscs": "jscs src tools --verbose", "stylelint": "stylelint \"src/**/*.scss\" --syntax scss", "lint": "npm run eslint && npm run jscs && npm run stylelint", - "test": "mocha src/**/*.test.js --require test/setup.js --compilers js:babel-register", - "test:watch": "mocha src/**/*.test.js --require test/setup.js --compilers js:babel-register --reporter min --watch", + "test": "mocha 'src/**/*.test.js' --require test/setup.js --compilers js:babel-register", + "test:watch": "npm run test -- --reporter min --watch", "clean": "babel-node tools/run clean", "copy": "babel-node tools/run copy", "bundle": "babel-node tools/run bundle", From 7b03f0b94381932002dd5642ef26644d159f7de5 Mon Sep 17 00:00:00 2001 From: Konstantin Tarkus Date: Tue, 17 May 2016 21:13:38 +0300 Subject: [PATCH 033/357] Update npm modules; fix ESLint warnings; remove JSCS --- package.json | 103 +++++++++++++++----------------- src/client.js | 2 +- src/components/Header/Header.js | 3 +- src/core/passport.js | 1 + src/routes/login/Login.js | 32 +++++----- src/server.js | 8 +-- tools/.eslintrc | 3 +- tools/run.js | 2 +- 8 files changed, 75 insertions(+), 79 deletions(-) diff --git a/package.json b/package.json index 84171a5db..783cd632d 100644 --- a/package.json +++ b/package.json @@ -5,94 +5,94 @@ "npm": ">=3.3 <4" }, "dependencies": { - "babel-polyfill": "6.7.4", + "babel-polyfill": "6.8.0", "babel-runtime": "6.6.1", "bluebird": "3.3.5", - "body-parser": "1.15.0", - "classnames": "2.2.3", + "body-parser": "1.15.1", + "classnames": "2.2.5", "cookie-parser": "1.4.1", - "core-js": "2.2.2", + "core-js": "2.4.0", "eventemitter3": "1.2.0", "express": "4.13.4", "express-graphql": "0.5.1", - "express-jwt": "3.3.0", + "express-jwt": "3.4.0", "fastclick": "1.0.6", - "fbjs": "0.8.1", + "fbjs": "0.8.2", "front-matter": "2.0.7", - "graphiql": "0.7.0", - "graphql": "0.5.0", - "history": "2.1.0", + "graphiql": "0.7.1", + "graphql": "0.6.0", + "history": "2.1.1", "isomorphic-style-loader": "1.0.0", "jade": "1.11.0", - "jsonwebtoken": "5.7.0", - "markdown-it": "6.0.1", - "node-fetch": "1.5.1", + "jsonwebtoken": "6.2.0", + "markdown-it": "6.0.2", + "node-fetch": "1.5.2", "normalize.css": "4.1.1", "passport": "0.3.2", "passport-facebook": "2.1.0", "pretty-error": "2.0.0", - "react": "15.0.1", - "react-dom": "15.0.1", - "sequelize": "^3.21.0", + "react": "15.0.2", + "react-dom": "15.0.2", + "sequelize": "^3.23.2", "source-map-support": "0.4.0", - "sqlite3": "^3.1.3", - "universal-router": "1.1.0-beta.3", - "whatwg-fetch": "0.11.0" + "sqlite3": "^3.1.4", + "universal-router": "1.2.1", + "whatwg-fetch": "1.0.0" }, "devDependencies": { "assets-webpack-plugin": "^3.4.0", "autoprefixer": "^6.3.6", - "babel-cli": "^6.7.7", - "babel-core": "^6.7.7", - "babel-eslint": "^6.0.3", + "babel-cli": "^6.8.0", + "babel-core": "^6.8.0", + "babel-eslint": "^6.0.4", "babel-loader": "^6.2.4", "babel-plugin-react-transform": "^2.0.2", - "babel-plugin-rewire": "^1.0.0-rc-2", - "babel-plugin-transform-react-constant-elements": "^6.5.0", - "babel-plugin-transform-react-inline-elements": "^6.6.5", - "babel-plugin-transform-react-remove-prop-types": "^0.2.5", - "babel-plugin-transform-runtime": "^6.7.5", + "babel-plugin-rewire": "^1.0.0-rc-3", + "babel-plugin-transform-react-constant-elements": "^6.8.0", + "babel-plugin-transform-react-inline-elements": "^6.8.0", + "babel-plugin-transform-react-remove-prop-types": "^0.2.6", + "babel-plugin-transform-runtime": "^6.8.0", "babel-preset-es2015": "^6.6.0", - "babel-preset-node5": "^11.0.1", + "babel-preset-node5": "^11.1.0", "babel-preset-react": "^6.5.0", "babel-preset-stage-0": "^6.5.0", - "babel-register": "^6.7.2", - "babel-template": "^6.7.0", - "babel-types": "^6.7.7", - "browser-sync": "^2.12.3", + "babel-register": "^6.8.0", + "babel-template": "^6.8.0", + "babel-types": "^6.8.1", + "browser-sync": "^2.12.8", "chai": "^3.5.0", "css-loader": "^0.23.1", "del": "^2.2.0", - "enzyme": "^2.2.0", - "eslint": "^2.8.0", - "eslint-config-airbnb": "^7.0.0", + "enzyme": "^2.3.0", + "eslint": "^2.10.2", + "eslint-config-airbnb": "^9.0.1", "eslint-loader": "^1.3.0", - "eslint-plugin-jsx-a11y": "^0.6.2", - "eslint-plugin-react": "^4.3.0", + "eslint-plugin-import": "^1.8.0", + "eslint-plugin-jsx-a11y": "^1.2.0", + "eslint-plugin-react": "^5.1.1", "extend": "^3.0.0", "file-loader": "^0.8.5", "gaze": "^1.0.0", "git-repository": "^0.1.4", "glob": "^7.0.3", "jade-loader": "^0.8.0", - "jscs": "^2.11.0", "json-loader": "^0.5.4", "mkdirp": "^0.5.1", "mocha": "^2.4.5", "ncp": "^2.0.0", - "postcss": "^5.0.19", - "postcss-import": "^8.1.0", - "postcss-loader": "^0.8.2", - "postcss-scss": "^0.1.7", + "postcss": "^5.0.21", + "postcss-import": "^8.1.2", + "postcss-loader": "^0.9.1", + "postcss-scss": "^0.1.8", "precss": "^1.4.0", "raw-loader": "^0.5.1", - "react-addons-test-utils": "^15.0.1", + "react-addons-test-utils": "^15.0.2", "react-transform-catch-errors": "^1.0.2", "react-transform-hmr": "^1.0.4", - "redbox-react": "^1.2.3", + "redbox-react": "^1.2.4", "sinon": "^2.0.0-pre", - "stylelint": "^6.0.3", - "stylelint-config-standard": "^6.0.0", + "stylelint": "^6.3.3", + "stylelint-config-standard": "^7.0.0", "url-loader": "^0.5.7", "webpack": "^1.13.0", "webpack-hot-middleware": "^2.10.0", @@ -130,14 +130,6 @@ ] } }, - "jscsConfig": { - "preset": "airbnb", - "excludeFiles": [ - "build/**", - "node_modules/**" - ], - "disallowSpacesInsideTemplateStringPlaceholders": null - }, "stylelint": { "extends": "stylelint-config-standard", "rules": { @@ -146,10 +138,9 @@ }, "scripts": { "eslint": "eslint src tools", - "jscs": "jscs src tools --verbose", "stylelint": "stylelint \"src/**/*.scss\" --syntax scss", - "lint": "npm run eslint && npm run jscs && npm run stylelint", - "test": "mocha 'src/**/*.test.js' --require test/setup.js --compilers js:babel-register", + "lint": "npm run eslint && npm run stylelint", + "test": "mocha \"src/**/*.test.js\" --require test/setup.js --compilers js:babel-register", "test:watch": "npm run test -- --reporter min --watch", "clean": "babel-node tools/run clean", "copy": "babel-node tools/run copy", diff --git a/src/client.js b/src/client.js index 4f9de5b16..2fe659615 100644 --- a/src/client.js +++ b/src/client.js @@ -16,7 +16,7 @@ import history from './core/history'; import { addEventListener, removeEventListener } from './core/DOMUtils'; const context = { - insertCss: styles => styles._insertCss(), + insertCss: styles => styles._insertCss(), // eslint-disable-line no-underscore-dangle setTitle: value => (document.title = value), setMeta: (name, content) => { // Remove and create a new tag in order to make it work diff --git a/src/components/Header/Header.js b/src/components/Header/Header.js index 125268b58..95bae8b6a 100644 --- a/src/components/Header/Header.js +++ b/src/components/Header/Header.js @@ -12,6 +12,7 @@ import withStyles from 'isomorphic-style-loader/lib/withStyles'; import s from './Header.scss'; import Link from '../Link'; import Navigation from '../Navigation'; +import logoUrl from './logo-small.png'; function Header() { return ( @@ -19,7 +20,7 @@ function Header() {
- React + React Your Company
diff --git a/src/core/passport.js b/src/core/passport.js index a8984e0af..ddf4b945e 100644 --- a/src/core/passport.js +++ b/src/core/passport.js @@ -28,6 +28,7 @@ passport.use(new FacebookStrategy({ profileFields: ['name', 'email', 'link', 'locale', 'timezone'], passReqToCallback: true, }, (req, accessToken, refreshToken, profile, done) => { + /* eslint-disable no-underscore-dangle */ const loginName = 'facebook'; const claimType = 'urn:facebook:access_token'; const fooBar = async () => { diff --git a/src/routes/login/Login.js b/src/routes/login/Login.js index 985c0297e..4fe857f67 100644 --- a/src/routes/login/Login.js +++ b/src/routes/login/Login.js @@ -45,14 +45,15 @@ function Login(props, context) { viewBox="0 0 30 30" xmlns="http://www.w3.org/2000/svg" > - Log in with Google @@ -67,13 +68,14 @@ function Login(props, context) { viewBox="0 0 30 30" xmlns="http://www.w3.org/2000/svg" > - Log in with Twitter diff --git a/src/server.js b/src/server.js index b5671415a..2600b3a23 100644 --- a/src/server.js +++ b/src/server.js @@ -22,7 +22,7 @@ import passport from './core/passport'; import models from './data/models'; import schema from './data/schema'; import routes from './routes'; -import assets from './assets'; +import assets from './assets'; // eslint-disable-line import/no-unresolved import { port, auth, analytics } from './config'; const app = express(); @@ -84,7 +84,7 @@ app.get('*', async (req, res, next) => { try { let css = []; let statusCode = 200; - const template = require('./views/index.jade'); + const template = require('./views/index.jade'); // eslint-disable-line global-require const data = { title: '', description: '', css: '', body: '', entry: assets.main.js }; if (process.env.NODE_ENV === 'production') { @@ -95,7 +95,7 @@ app.get('*', async (req, res, next) => { path: req.path, query: req.query, context: { - insertCss: styles => css.push(styles._getCss()), + insertCss: styles => css.push(styles._getCss()), // eslint-disable-line no-underscore-dangle setTitle: value => (data.title = value), setMeta: (key, value) => (data[key] = value), }, @@ -124,7 +124,7 @@ pe.skipPackage('express'); app.use((err, req, res, next) => { // eslint-disable-line no-unused-vars console.log(pe.render(err)); // eslint-disable-line no-console - const template = require('./views/error.jade'); + const template = require('./views/error.jade'); // eslint-disable-line global-require const statusCode = err.status || 500; res.status(statusCode); res.send(template({ diff --git a/tools/.eslintrc b/tools/.eslintrc index 7753f32e7..a5ff0c841 100644 --- a/tools/.eslintrc +++ b/tools/.eslintrc @@ -1,5 +1,6 @@ { "rules": { - "no-console": 0 + "no-console": 0, + "global-require": 0 } } diff --git a/tools/run.js b/tools/run.js index 4f62b175b..a42f0a5eb 100644 --- a/tools/run.js +++ b/tools/run.js @@ -27,7 +27,7 @@ function run(fn, options) { } if (process.mainModule.children.length === 0 && process.argv.length > 2) { - delete require.cache[__filename]; + delete require.cache[__filename]; // eslint-disable-line no-underscore-dangle const module = require(`./${process.argv[2]}.js`).default; run(module).catch(err => console.error(err.stack)); } From 8fcb0fa3aac032eb9e7d85e07a50703e54e0e8af Mon Sep 17 00:00:00 2001 From: Konstantin Tarkus Date: Wed, 18 May 2016 18:39:46 +0300 Subject: [PATCH 034/357] Add support for plain CSS (.css) files (#662) --- .travis.yml | 1 + package.json | 46 ++++++---- src/components/App/{App.scss => App.css} | 4 +- src/components/App/App.js | 2 +- .../Feedback/{Feedback.scss => Feedback.css} | 4 +- src/components/Feedback/Feedback.js | 2 +- .../Footer/{Footer.scss => Footer.css} | 4 +- src/components/Footer/Footer.js | 2 +- .../Header/{Header.scss => Header.css} | 10 ++- src/components/Header/Header.js | 2 +- .../{Navigation.scss => Navigation.css} | 0 src/components/Navigation/Navigation.js | 2 +- src/components/variables.css | 31 +++++++ src/components/variables.scss | 31 ------- .../contact/{Contact.scss => Contact.css} | 4 +- src/routes/contact/Contact.js | 2 +- .../Register.scss => content/Content.css} | 4 +- src/routes/content/Content.js | 2 +- .../error/{ErrorPage.scss => ErrorPage.css} | 0 src/routes/error/ErrorPage.js | 2 +- src/routes/home/{Home.scss => Home.css} | 4 +- src/routes/home/Home.js | 2 +- src/routes/login/{Login.scss => Login.css} | 86 +++++++++---------- src/routes/login/Login.js | 2 +- .../Content.scss => register/Register.css} | 4 +- src/routes/register/Register.js | 2 +- tools/webpack.config.js | 66 ++++++++++++-- 27 files changed, 191 insertions(+), 130 deletions(-) rename src/components/App/{App.scss => App.css} (97%) rename src/components/Feedback/{Feedback.scss => Feedback.css} (90%) rename src/components/Footer/{Footer.scss => Footer.css} (91%) rename src/components/Header/{Header.scss => Header.css} (83%) rename src/components/Navigation/{Navigation.scss => Navigation.css} (100%) create mode 100644 src/components/variables.css delete mode 100644 src/components/variables.scss rename src/routes/contact/{Contact.scss => Contact.css} (82%) rename src/routes/{register/Register.scss => content/Content.css} (82%) rename src/routes/error/{ErrorPage.scss => ErrorPage.css} (100%) rename src/routes/home/{Home.scss => Home.css} (87%) rename src/routes/login/{Login.scss => Login.css} (69%) rename src/routes/{content/Content.scss => register/Register.css} (82%) diff --git a/.travis.yml b/.travis.yml index 5de4afb57..c11f35ad3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,6 @@ language: node_js node_js: + - '6' - '5' env: - CXX=g++-4.8 diff --git a/package.json b/package.json index 783cd632d..5928d38b2 100644 --- a/package.json +++ b/package.json @@ -1,13 +1,13 @@ { "private": true, "engines": { - "node": ">=5.0 <6", + "node": ">=5.0 <7", "npm": ">=3.3 <4" }, "dependencies": { - "babel-polyfill": "6.8.0", - "babel-runtime": "6.6.1", - "bluebird": "3.3.5", + "babel-polyfill": "6.9.0", + "babel-runtime": "6.9.0", + "bluebird": "3.4.0", "body-parser": "1.15.1", "classnames": "2.2.5", "cookie-parser": "1.4.1", @@ -29,21 +29,21 @@ "node-fetch": "1.5.2", "normalize.css": "4.1.1", "passport": "0.3.2", - "passport-facebook": "2.1.0", + "passport-facebook": "2.1.1", "pretty-error": "2.0.0", "react": "15.0.2", "react-dom": "15.0.2", - "sequelize": "^3.23.2", + "sequelize": "3.23.2", "source-map-support": "0.4.0", - "sqlite3": "^3.1.4", + "sqlite3": "3.1.4", "universal-router": "1.2.1", "whatwg-fetch": "1.0.0" }, "devDependencies": { "assets-webpack-plugin": "^3.4.0", "autoprefixer": "^6.3.6", - "babel-cli": "^6.8.0", - "babel-core": "^6.8.0", + "babel-cli": "^6.9.0", + "babel-core": "^6.9.0", "babel-eslint": "^6.0.4", "babel-loader": "^6.2.4", "babel-plugin-react-transform": "^2.0.2", @@ -51,14 +51,14 @@ "babel-plugin-transform-react-constant-elements": "^6.8.0", "babel-plugin-transform-react-inline-elements": "^6.8.0", "babel-plugin-transform-react-remove-prop-types": "^0.2.6", - "babel-plugin-transform-runtime": "^6.8.0", - "babel-preset-es2015": "^6.6.0", + "babel-plugin-transform-runtime": "^6.9.0", + "babel-preset-es2015": "^6.9.0", "babel-preset-node5": "^11.1.0", "babel-preset-react": "^6.5.0", "babel-preset-stage-0": "^6.5.0", - "babel-register": "^6.8.0", - "babel-template": "^6.8.0", - "babel-types": "^6.8.1", + "babel-register": "^6.9.0", + "babel-template": "^6.9.0", + "babel-types": "^6.9.0", "browser-sync": "^2.12.8", "chai": "^3.5.0", "css-loader": "^0.23.1", @@ -80,18 +80,28 @@ "mkdirp": "^0.5.1", "mocha": "^2.4.5", "ncp": "^2.0.0", + "pixrem": "^3.0.0", + "pleeease-filters": "^3.0.0", "postcss": "^5.0.21", + "postcss-calc": "^5.2.1", + "postcss-color-function": "^2.0.1", + "postcss-custom-media": "^5.0.1", + "postcss-custom-properties": "^5.0.1", + "postcss-custom-selectors": "^3.0.0", "postcss-import": "^8.1.2", "postcss-loader": "^0.9.1", - "postcss-scss": "^0.1.8", - "precss": "^1.4.0", + "postcss-media-minmax": "^2.1.2", + "postcss-nesting": "^2.3.1", + "postcss-pseudoelements": "^3.0.0", + "postcss-selector-matches": "^2.0.1", + "postcss-selector-not": "^2.0.0", "raw-loader": "^0.5.1", "react-addons-test-utils": "^15.0.2", "react-transform-catch-errors": "^1.0.2", "react-transform-hmr": "^1.0.4", "redbox-react": "^1.2.4", "sinon": "^2.0.0-pre", - "stylelint": "^6.3.3", + "stylelint": "^6.4.1", "stylelint-config-standard": "^7.0.0", "url-loader": "^0.5.7", "webpack": "^1.13.0", @@ -138,7 +148,7 @@ }, "scripts": { "eslint": "eslint src tools", - "stylelint": "stylelint \"src/**/*.scss\" --syntax scss", + "stylelint": "stylelint \"src/**/*.css\"", "lint": "npm run eslint && npm run stylelint", "test": "mocha \"src/**/*.test.js\" --require test/setup.js --compilers js:babel-register", "test:watch": "npm run test -- --reporter min --watch", diff --git a/src/components/App/App.scss b/src/components/App/App.css similarity index 97% rename from src/components/App/App.scss rename to src/components/App/App.css index 71f174123..9d5aa691c 100644 --- a/src/components/App/App.scss +++ b/src/components/App/App.css @@ -11,7 +11,7 @@ /*! React Starter Kit | MIT License | https://www.reactstarterkit.com/ */ -@import 'http://github.com/variables.scss'; +@import 'http://github.com/variables.css'; /* * Base styles @@ -21,7 +21,7 @@ html { color: #222; font-weight: 100; font-size: 1em; /* ~16px; */ - font-family: $font-family-base; + font-family: var(--font-family-base); line-height: 1.375; /* ~22px */ } diff --git a/src/components/App/App.js b/src/components/App/App.js index a8483dc3a..3fad77bd2 100644 --- a/src/components/App/App.js +++ b/src/components/App/App.js @@ -9,7 +9,7 @@ import React, { Component, PropTypes } from 'react'; import emptyFunction from 'fbjs/lib/emptyFunction'; -import s from './App.scss'; +import s from './App.css'; import Header from '../Header'; import Feedback from '../Feedback'; import Footer from '../Footer'; diff --git a/src/components/Feedback/Feedback.scss b/src/components/Feedback/Feedback.css similarity index 90% rename from src/components/Feedback/Feedback.scss rename to src/components/Feedback/Feedback.css index 9b9ffcb0c..b73144e5f 100644 --- a/src/components/Feedback/Feedback.scss +++ b/src/components/Feedback/Feedback.css @@ -7,7 +7,7 @@ * LICENSE.txt file in the root directory of this source tree. */ -@import 'http://github.com/variables.scss'; +@import 'http://github.com/variables.css'; .root { background: #f5f5f5; @@ -17,7 +17,7 @@ .container { margin: 0 auto; padding: 20px 8px; - max-width: $max-content-width; + max-width: var(--max-content-width); text-align: center; font-size: 1.5em; /* ~24px */ } diff --git a/src/components/Feedback/Feedback.js b/src/components/Feedback/Feedback.js index 1ed0ec6e7..596a52c8c 100644 --- a/src/components/Feedback/Feedback.js +++ b/src/components/Feedback/Feedback.js @@ -9,7 +9,7 @@ import React from 'react'; import withStyles from 'isomorphic-style-loader/lib/withStyles'; -import s from './Feedback.scss'; +import s from './Feedback.css'; function Feedback() { return ( diff --git a/src/components/Footer/Footer.scss b/src/components/Footer/Footer.css similarity index 91% rename from src/components/Footer/Footer.scss rename to src/components/Footer/Footer.css index d6fe535ee..cc4270a85 100644 --- a/src/components/Footer/Footer.scss +++ b/src/components/Footer/Footer.css @@ -7,7 +7,7 @@ * LICENSE.txt file in the root directory of this source tree. */ -@import 'http://github.com/variables.scss'; +@import 'http://github.com/variables.css'; .root { background: #333; @@ -17,7 +17,7 @@ .container { margin: 0 auto; padding: 20px 15px; - max-width: $max-content-width; + max-width: var(--max-content-width); text-align: center; } diff --git a/src/components/Footer/Footer.js b/src/components/Footer/Footer.js index 23b62b3a7..a393dff0c 100644 --- a/src/components/Footer/Footer.js +++ b/src/components/Footer/Footer.js @@ -9,7 +9,7 @@ import React from 'react'; import withStyles from 'isomorphic-style-loader/lib/withStyles'; -import s from './Footer.scss'; +import s from './Footer.css'; import Link from '../Link'; function Footer() { diff --git a/src/components/Header/Header.scss b/src/components/Header/Header.css similarity index 83% rename from src/components/Header/Header.scss rename to src/components/Header/Header.css index 34dce84b0..59b8785a5 100644 --- a/src/components/Header/Header.scss +++ b/src/components/Header/Header.css @@ -7,9 +7,11 @@ * LICENSE.txt file in the root directory of this source tree. */ -@import 'http://github.com/variables.scss'; +@import 'http://github.com/variables.css'; -$brand-color: #61dafb; +:root { + --brand-color: #61dafb; +} .root { background: #373277; @@ -19,11 +21,11 @@ $brand-color: #61dafb; .container { margin: 0 auto; padding: 20px 0; - max-width: $max-content-width; + max-width: var(--max-content-width); } .brand { - color: color($brand-color lightness(+10%)); + color: color(var(--brand-color) lightness(+10%)); text-decoration: none; font-size: 1.75em; /* ~28px */ } diff --git a/src/components/Header/Header.js b/src/components/Header/Header.js index 95bae8b6a..c3b7357b9 100644 --- a/src/components/Header/Header.js +++ b/src/components/Header/Header.js @@ -9,7 +9,7 @@ import React from 'react'; import withStyles from 'isomorphic-style-loader/lib/withStyles'; -import s from './Header.scss'; +import s from './Header.css'; import Link from '../Link'; import Navigation from '../Navigation'; import logoUrl from './logo-small.png'; diff --git a/src/components/Navigation/Navigation.scss b/src/components/Navigation/Navigation.css similarity index 100% rename from src/components/Navigation/Navigation.scss rename to src/components/Navigation/Navigation.css diff --git a/src/components/Navigation/Navigation.js b/src/components/Navigation/Navigation.js index 8e75b17f4..938c0e768 100644 --- a/src/components/Navigation/Navigation.js +++ b/src/components/Navigation/Navigation.js @@ -10,7 +10,7 @@ import React, { PropTypes } from 'react'; import cx from 'classnames'; import withStyles from 'isomorphic-style-loader/lib/withStyles'; -import s from './Navigation.scss'; +import s from './Navigation.css'; import Link from '../Link'; function Navigation({ className }) { diff --git a/src/components/variables.css b/src/components/variables.css new file mode 100644 index 000000000..cee58513d --- /dev/null +++ b/src/components/variables.css @@ -0,0 +1,31 @@ +/** + * React Starter Kit (https://www.reactstarterkit.com/) + * + * Copyright © 2014-2016 Kriasoft, LLC. All rights reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE.txt file in the root directory of this source tree. + */ + +:root { + /* + * Typography + * ======================================================================== */ + + --font-family-base: 'Segoe UI', 'HelveticaNeue-Light', sans-serif; + + /* + * Layout + * ======================================================================== */ + + --max-content-width: 1000px; + + /* + * Media queries breakpoints + * ======================================================================== */ + + --screen-xs-min: 480px; /* Extra small screen / phone */ + --screen-sm-min: 768px; /* Small screen / tablet */ + --screen-md-min: 992px; /* Medium screen / desktop */ + --screen-lg-min: 1200px; /* Large screen / wide desktop */ +} diff --git a/src/components/variables.scss b/src/components/variables.scss deleted file mode 100644 index 55218a85f..000000000 --- a/src/components/variables.scss +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Colors - * ========================================================================== */ - -$white-base: hsl(255, 255, 255); -$gray-darker: color(black lightness(+13.5%)); /* #222 */ -$gray-dark: color(black lightness(+25%)); /* #404040 */ -$gray: color(black lightness(+33.5%)); /* #555 */ -$gray-light: color(black lightness(+46.7%)); /* #777 */ -$gray-lighter: color(black lightness(+93.5%)); /* #eee */ - -/* - * Typography - * ========================================================================== */ - -$font-family-base: 'Segoe UI', 'HelveticaNeue-Light', sans-serif; - -/* - * Layout - * ========================================================================== */ - -$max-content-width: 1000px; - -/* - * Media queries breakpoints - * ========================================================================== */ - -$screen-xs-min: 480px; /* Extra small screen / phone */ -$screen-sm-min: 768px; /* Small screen / tablet */ -$screen-md-min: 992px; /* Medium screen / desktop */ -$screen-lg-min: 1200px; /* Large screen / wide desktop */ diff --git a/src/routes/contact/Contact.scss b/src/routes/contact/Contact.css similarity index 82% rename from src/routes/contact/Contact.scss rename to src/routes/contact/Contact.css index d98463bf0..765dda927 100644 --- a/src/routes/contact/Contact.scss +++ b/src/routes/contact/Contact.css @@ -7,7 +7,7 @@ * LICENSE.txt file in the root directory of this source tree. */ -@import 'http://github.com/components/variables.scss'; +@import 'http://github.com/components/variables.css'; .root { padding-left: 20px; @@ -17,5 +17,5 @@ .container { margin: 0 auto; padding: 0 0 40px; - max-width: $max-content-width; + max-width: var(--max-content-width); } diff --git a/src/routes/contact/Contact.js b/src/routes/contact/Contact.js index 0f28b5cec..ecac261e6 100644 --- a/src/routes/contact/Contact.js +++ b/src/routes/contact/Contact.js @@ -9,7 +9,7 @@ import React, { PropTypes } from 'react'; import withStyles from 'isomorphic-style-loader/lib/withStyles'; -import s from './Contact.scss'; +import s from './Contact.css'; const title = 'Contact Us'; diff --git a/src/routes/register/Register.scss b/src/routes/content/Content.css similarity index 82% rename from src/routes/register/Register.scss rename to src/routes/content/Content.css index d98463bf0..765dda927 100644 --- a/src/routes/register/Register.scss +++ b/src/routes/content/Content.css @@ -7,7 +7,7 @@ * LICENSE.txt file in the root directory of this source tree. */ -@import 'http://github.com/components/variables.scss'; +@import 'http://github.com/components/variables.css'; .root { padding-left: 20px; @@ -17,5 +17,5 @@ .container { margin: 0 auto; padding: 0 0 40px; - max-width: $max-content-width; + max-width: var(--max-content-width); } diff --git a/src/routes/content/Content.js b/src/routes/content/Content.js index 958a47d1f..d6418825e 100644 --- a/src/routes/content/Content.js +++ b/src/routes/content/Content.js @@ -9,7 +9,7 @@ import React, { Component, PropTypes } from 'react'; import withStyles from 'isomorphic-style-loader/lib/withStyles'; -import s from './Content.scss'; +import s from './Content.css'; class Content extends Component { diff --git a/src/routes/error/ErrorPage.scss b/src/routes/error/ErrorPage.css similarity index 100% rename from src/routes/error/ErrorPage.scss rename to src/routes/error/ErrorPage.css diff --git a/src/routes/error/ErrorPage.js b/src/routes/error/ErrorPage.js index 6b2b03587..c1830865b 100644 --- a/src/routes/error/ErrorPage.js +++ b/src/routes/error/ErrorPage.js @@ -9,7 +9,7 @@ import React, { PropTypes } from 'react'; import withStyles from 'isomorphic-style-loader/lib/withStyles'; -import s from './ErrorPage.scss'; +import s from './ErrorPage.css'; function ErrorPage({ error }, context) { let title = 'Error'; diff --git a/src/routes/home/Home.scss b/src/routes/home/Home.css similarity index 87% rename from src/routes/home/Home.scss rename to src/routes/home/Home.css index 49e6b15d7..68428f7a5 100644 --- a/src/routes/home/Home.scss +++ b/src/routes/home/Home.css @@ -7,7 +7,7 @@ * LICENSE.txt file in the root directory of this source tree. */ -@import 'http://github.com/components/variables.scss'; +@import 'http://github.com/components/variables.css'; .root { padding-left: 20px; @@ -17,7 +17,7 @@ .container { margin: 0 auto; padding: 0 0 40px; - max-width: $max-content-width; + max-width: var(--max-content-width); } .news { diff --git a/src/routes/home/Home.js b/src/routes/home/Home.js index 7bcecfb2e..c2f0eac17 100644 --- a/src/routes/home/Home.js +++ b/src/routes/home/Home.js @@ -9,7 +9,7 @@ import React, { PropTypes } from 'react'; import withStyles from 'isomorphic-style-loader/lib/withStyles'; -import s from './Home.scss'; +import s from './Home.css'; const title = 'React Starter Kit'; diff --git a/src/routes/login/Login.scss b/src/routes/login/Login.css similarity index 69% rename from src/routes/login/Login.scss rename to src/routes/login/Login.css index a83809029..653371a80 100644 --- a/src/routes/login/Login.scss +++ b/src/routes/login/Login.css @@ -6,7 +6,7 @@ * This source code is licensed under the MIT license found in the * LICENSE.txt file in the root directory of this source tree. */ -@import 'http://github.com/components/variables.scss'; +@import 'http://github.com/components/variables.css'; .root { padding-left: 20px; @@ -49,11 +49,11 @@ font-size: 18px; line-height: 1.3333333; transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s; +} - &:focus { - border-color: #0074c2; - box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(0, 116, 194, 0.6); - } +.input:focus { + border-color: #0074c2; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(0, 116, 194, 0.6); } .button { @@ -72,45 +72,45 @@ font-size: 18px; line-height: 1.3333333; cursor: pointer; +} - &:hover { - background: rgba(54, 50, 119, 0.8); - } +.button:hover { + background: rgba(54, 50, 119, 0.8); +} - &:focus { - border-color: #0074c2; - box-shadow: 0 0 8px rgba(0, 116, 194, 0.6); - } +.button:focus { + border-color: #0074c2; + box-shadow: 0 0 8px rgba(0, 116, 194, 0.6); } .facebook { border-color: #3b5998; background: #3b5998; composes: button; +} - &:hover { - background: #2d4373; - } +.facebook:hover { + background: #2d4373; } .google { border-color: #dd4b39; background: #dd4b39; composes: button; +} - &:hover { - background: #c23321; - } +.google:hover { + background: #c23321; } .twitter { border-color: #55acee; background: #55acee; composes: button; +} - &:hover { - background: #2795e9; - } +.twitter:hover { + background: #2795e9; } .icon { @@ -131,27 +131,27 @@ color: #757575; text-align: center; font-size: 80%; +} + +.lineThrough::before { + position: absolute; + top: 50%; + left: 50%; + z-index: -1; + margin-top: -5px; + margin-left: -20px; + width: 40px; + height: 10px; + background-color: #fff; + content: ''; +} - &::before { - position: absolute; - top: 50%; - left: 50%; - z-index: -1; - margin-top: -5px; - margin-left: -20px; - width: 40px; - height: 10px; - background-color: #fff; - content: ''; - } - - &::after { - position: absolute; - top: 49%; - z-index: -2; - display: block; - width: 100%; - border-bottom: 1px solid #ddd; - content: ''; - } +.lineThrough::after { + position: absolute; + top: 49%; + z-index: -2; + display: block; + width: 100%; + border-bottom: 1px solid #ddd; + content: ''; } diff --git a/src/routes/login/Login.js b/src/routes/login/Login.js index 4fe857f67..c170316ac 100644 --- a/src/routes/login/Login.js +++ b/src/routes/login/Login.js @@ -9,7 +9,7 @@ import React, { PropTypes } from 'react'; import withStyles from 'isomorphic-style-loader/lib/withStyles'; -import s from './Login.scss'; +import s from './Login.css'; const title = 'Log In'; diff --git a/src/routes/content/Content.scss b/src/routes/register/Register.css similarity index 82% rename from src/routes/content/Content.scss rename to src/routes/register/Register.css index d98463bf0..765dda927 100644 --- a/src/routes/content/Content.scss +++ b/src/routes/register/Register.css @@ -7,7 +7,7 @@ * LICENSE.txt file in the root directory of this source tree. */ -@import 'http://github.com/components/variables.scss'; +@import 'http://github.com/components/variables.css'; .root { padding-left: 20px; @@ -17,5 +17,5 @@ .container { margin: 0 auto; padding: 0 0 40px; - max-width: $max-content-width; + max-width: var(--max-content-width); } diff --git a/src/routes/register/Register.js b/src/routes/register/Register.js index 4c1af503f..3ab3f83ca 100644 --- a/src/routes/register/Register.js +++ b/src/routes/register/Register.js @@ -9,7 +9,7 @@ import React, { PropTypes } from 'react'; import withStyles from 'isomorphic-style-loader/lib/withStyles'; -import s from './Register.scss'; +import s from './Register.css'; const title = 'New User Registration'; diff --git a/tools/webpack.config.js b/tools/webpack.config.js index ed249f940..4af4d52c1 100644 --- a/tools/webpack.config.js +++ b/tools/webpack.config.js @@ -74,20 +74,27 @@ const config = { }, }, { - test: /\.scss$/, + test: /\.css/, loaders: [ 'isomorphic-style-loader', `css-loader?${JSON.stringify({ sourceMap: DEBUG, - // CSS Modules https://github.com/css-modules/css-modules modules: true, localIdentName: DEBUG ? '[name]_[local]_[hash:base64:3]' : '[hash:base64:4]', - // CSS Nano http://cssnano.co/options/ minimize: !DEBUG, })}`, - 'postcss-loader?parser=postcss-scss', + 'postcss-loader?pack=default', + ], + }, + { + test: /\.scss$/, + loaders: [ + 'isomorphic-style-loader', + `css-loader?${JSON.stringify({ sourceMap: DEBUG, minimize: !DEBUG })}`, + 'postcss-loader?pack=sass', + 'sass-loader', ], }, { @@ -142,11 +149,52 @@ const config = { }, postcss(bundler) { - return [ - require('postcss-import')({ addDependencyTo: bundler }), - require('precss')(), - require('autoprefixer')({ browsers: AUTOPREFIXER_BROWSERS }), - ]; + return { + default: [ + // Transfer @import rule by inlining content, e.g. @import 'http://github.com/normalize.css' + // https://github.com/postcss/postcss-import + require('postcss-import')({ addDependencyTo: bundler }), + // W3C variables, e.g. :root { --color: red; } div { background: var(--color); } + // https://github.com/postcss/postcss-custom-properties + require('postcss-custom-properties')(), + // W3C CSS Custom Media Queries, e.g. @custom-media --small-viewport (max-width: 30em); + // https://github.com/postcss/postcss-custom-media + require('postcss-custom-media')(), + // CSS4 Media Queries, e.g. @media screen and (width >= 500px) and (width <= 1200px) { } + // https://github.com/postcss/postcss-media-minmax + require('postcss-media-minmax')(), + // W3C CSS Custom Selectors, e.g. @custom-selector :--heading h1, h2, h3, h4, h5, h6; + // https://github.com/postcss/postcss-custom-selectors + require('postcss-custom-selectors')(), + // W3C calc() function, e.g. div { height: calc(100px - 2em); } + // https://github.com/postcss/postcss-calc + require('postcss-calc')(), + // Allows you to nest one style rule inside another + // https://github.com/jonathantneal/postcss-nesting + require('postcss-nesting')(), + // W3C color() function, e.g. div { background: color(red alpha(90%)); } + // https://github.com/postcss/postcss-color-function + require('postcss-color-function')(), + // Convert CSS shorthand filters to SVG equivalent, e.g. .blur { filter: blur(4px); } + // https://github.com/iamvdo/pleeease-filters + require('pleeease-filters')(), + // Generate pixel fallback for "rem" units, e.g. div { margin: 2.5rem 2px 3em 100%; } + // https://github.com/robwierzbowski/node-pixrem + require('pixrem')(), + // W3C CSS Level4 :matches() pseudo class, e.g. p:matches(:first-child, .special) { } + // https://github.com/postcss/postcss-selector-matches + require('postcss-selector-matches')(), + // Transforms :not() W3C CSS Level 4 pseudo class to :not() CSS Level 3 selectors + // https://github.com/postcss/postcss-selector-not + require('postcss-selector-not')(), + // Add vendor prefixes to CSS rules using values from caniuse.com + // https://github.com/postcss/autoprefixer + require('autoprefixer')({ browsers: AUTOPREFIXER_BROWSERS }), + ], + sass: [ + require('autoprefixer')({ browsers: AUTOPREFIXER_BROWSERS }), + ], + }; }, }; From b22b1810461cec9c53eedffe632a3ce70a6b29a3 Mon Sep 17 00:00:00 2001 From: Konstantin Tarkus Date: Wed, 25 May 2016 14:23:35 +0300 Subject: [PATCH 035/357] Update npm modules; tweak Stylelint settings --- package.json | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/package.json b/package.json index 5928d38b2..1d9e86489 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,7 @@ "bluebird": "3.4.0", "body-parser": "1.15.1", "classnames": "2.2.5", - "cookie-parser": "1.4.1", + "cookie-parser": "1.4.2", "core-js": "2.4.0", "eventemitter3": "1.2.0", "express": "4.13.4", @@ -18,21 +18,21 @@ "express-jwt": "3.4.0", "fastclick": "1.0.6", "fbjs": "0.8.2", - "front-matter": "2.0.7", + "front-matter": "2.0.8", "graphiql": "0.7.1", "graphql": "0.6.0", "history": "2.1.1", "isomorphic-style-loader": "1.0.0", "jade": "1.11.0", - "jsonwebtoken": "6.2.0", + "jsonwebtoken": "7.0.0", "markdown-it": "6.0.2", "node-fetch": "1.5.2", "normalize.css": "4.1.1", "passport": "0.3.2", "passport-facebook": "2.1.1", "pretty-error": "2.0.0", - "react": "15.0.2", - "react-dom": "15.0.2", + "react": "15.1.0", + "react-dom": "15.1.0", "sequelize": "3.23.2", "source-map-support": "0.4.0", "sqlite3": "3.1.4", @@ -50,7 +50,7 @@ "babel-plugin-rewire": "^1.0.0-rc-3", "babel-plugin-transform-react-constant-elements": "^6.8.0", "babel-plugin-transform-react-inline-elements": "^6.8.0", - "babel-plugin-transform-react-remove-prop-types": "^0.2.6", + "babel-plugin-transform-react-remove-prop-types": "^0.2.7", "babel-plugin-transform-runtime": "^6.9.0", "babel-preset-es2015": "^6.9.0", "babel-preset-node5": "^11.1.0", @@ -68,7 +68,7 @@ "eslint-config-airbnb": "^9.0.1", "eslint-loader": "^1.3.0", "eslint-plugin-import": "^1.8.0", - "eslint-plugin-jsx-a11y": "^1.2.0", + "eslint-plugin-jsx-a11y": "^1.2.2", "eslint-plugin-react": "^5.1.1", "extend": "^3.0.0", "file-loader": "^0.8.5", @@ -78,9 +78,9 @@ "jade-loader": "^0.8.0", "json-loader": "^0.5.4", "mkdirp": "^0.5.1", - "mocha": "^2.4.5", + "mocha": "^2.5.3", "ncp": "^2.0.0", - "pixrem": "^3.0.0", + "pixrem": "^3.0.1", "pleeease-filters": "^3.0.0", "postcss": "^5.0.21", "postcss-calc": "^5.2.1", @@ -96,15 +96,15 @@ "postcss-selector-matches": "^2.0.1", "postcss-selector-not": "^2.0.0", "raw-loader": "^0.5.1", - "react-addons-test-utils": "^15.0.2", + "react-addons-test-utils": "^15.1.0", "react-transform-catch-errors": "^1.0.2", "react-transform-hmr": "^1.0.4", - "redbox-react": "^1.2.4", + "redbox-react": "^1.2.6", "sinon": "^2.0.0-pre", - "stylelint": "^6.4.1", - "stylelint-config-standard": "^7.0.0", + "stylelint": "^6.5.1", + "stylelint-config-standard": "^8.0.0", "url-loader": "^0.5.7", - "webpack": "^1.13.0", + "webpack": "^1.13.1", "webpack-hot-middleware": "^2.10.0", "webpack-middleware": "^1.5.1" }, @@ -143,7 +143,8 @@ "stylelint": { "extends": "stylelint-config-standard", "rules": { - "string-quotes": "single" + "string-quotes": "single", + "selector-pseudo-class-no-unknown": [true, { "ignorePseudoClasses": ["global", "local"] }] } }, "scripts": { From 19077c4e6ada068eb8dae0d3094b2c845c4d0f4b Mon Sep 17 00:00:00 2001 From: Zihao Zhang Date: Thu, 9 Jun 2016 11:36:11 -0700 Subject: [PATCH 036/357] Change db string length (#691) --- src/data/models/User.js | 2 +- src/data/models/UserProfile.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/data/models/User.js b/src/data/models/User.js index 9d394256e..16525c719 100644 --- a/src/data/models/User.js +++ b/src/data/models/User.js @@ -19,7 +19,7 @@ const User = Model.define('User', { }, email: { - type: DataType.STRING(256), + type: DataType.STRING(255), validate: { isEmail: true }, }, diff --git a/src/data/models/UserProfile.js b/src/data/models/UserProfile.js index 779d7d736..56e20674b 100644 --- a/src/data/models/UserProfile.js +++ b/src/data/models/UserProfile.js @@ -13,7 +13,7 @@ const UserProfile = Model.define('UserProfile', { }, picture: { - type: DataType.STRING(256), + type: DataType.STRING(255), }, gender: { @@ -25,7 +25,7 @@ const UserProfile = Model.define('UserProfile', { }, website: { - type: DataType.STRING(256), + type: DataType.STRING(255), }, }); From c556508e24ee7a089b54c6389f3846c81198e8e2 Mon Sep 17 00:00:00 2001 From: Vladimir Kutepov Date: Thu, 9 Jun 2016 22:35:14 +0300 Subject: [PATCH 037/357] Update history module to v3 (#692) - Update `history` dependency to v3.0.0 ([changelog](https://github.com/ReactJSTraining/history/blob/master/CHANGES.md)) - Add `windowScrollX` and `windowScrollY` helpers to `core/DOMUtils` - Rename `match()` to `UniversalRouter.resolve()` - Fix scroll issues ([see article](https://developers.google.com/web/updates/2015/09/history-api-scroll-restoration)) --- package.json | 12 +++++++-- src/client.js | 62 +++++++++++++++++++++++++++----------------- src/core/DOMUtils.js | 10 +++++++ src/server.js | 4 +-- 4 files changed, 60 insertions(+), 28 deletions(-) diff --git a/package.json b/package.json index 1d9e86489..521cf8486 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "front-matter": "2.0.8", "graphiql": "0.7.1", "graphql": "0.6.0", - "history": "2.1.1", + "history": "3.0.0", "isomorphic-style-loader": "1.0.0", "jade": "1.11.0", "jsonwebtoken": "7.0.0", @@ -144,7 +144,15 @@ "extends": "stylelint-config-standard", "rules": { "string-quotes": "single", - "selector-pseudo-class-no-unknown": [true, { "ignorePseudoClasses": ["global", "local"] }] + "selector-pseudo-class-no-unknown": [ + true, + { + "ignorePseudoClasses": [ + "global", + "local" + ] + } + ] } }, "scripts": { diff --git a/src/client.js b/src/client.js index 2fe659615..3ddbdee9c 100644 --- a/src/client.js +++ b/src/client.js @@ -10,10 +10,16 @@ import 'babel-polyfill'; import ReactDOM from 'react-dom'; import FastClick from 'fastclick'; -import { match } from 'universal-router'; +import UniversalRouter from 'universal-router'; import routes from './routes'; import history from './core/history'; -import { addEventListener, removeEventListener } from './core/DOMUtils'; +import { readState, saveState } from 'history/lib/DOMStateStorage'; +import { + addEventListener, + removeEventListener, + windowScrollX, + windowScrollY, +} from './core/DOMUtils'; const context = { insertCss: styles => styles._insertCss(), // eslint-disable-line no-underscore-dangle @@ -75,44 +81,52 @@ function render(container, state, component) { } function run() { - let currentLocation = null; const container = document.getElementById('app'); + let currentLocation = history.getCurrentLocation(); // Make taps on links and buttons work fast on mobiles FastClick.attach(document.body); // Re-render the app when window.location changes - const removeHistoryListener = history.listen(location => { + function onLocationChange(location) { + // Save the page scroll position into the current location's state + if (currentLocation.key) { + saveState(currentLocation.key, { + ...readState(currentLocation.key), + scrollX: windowScrollX(), + scrollY: windowScrollY(), + }); + } currentLocation = location; - match(routes, { + + UniversalRouter.resolve(routes, { path: location.pathname, query: location.query, state: location.state, context, render: render.bind(undefined, container, location.state), }).catch(err => console.error(err)); // eslint-disable-line no-console - }); + } - // Save the page scroll position into the current location's state - const supportPageOffset = window.pageXOffset !== undefined; - const isCSS1Compat = ((document.compatMode || '') === 'CSS1Compat'); - const setPageOffset = () => { - currentLocation.state = currentLocation.state || Object.create(null); - if (supportPageOffset) { - currentLocation.state.scrollX = window.pageXOffset; - currentLocation.state.scrollY = window.pageYOffset; - } else { - currentLocation.state.scrollX = isCSS1Compat ? - document.documentElement.scrollLeft : document.body.scrollLeft; - currentLocation.state.scrollY = isCSS1Compat ? - document.documentElement.scrollTop : document.body.scrollTop; - } - }; + // Add History API listener and trigger initial change + const removeHistoryListener = history.listen(onLocationChange); + history.replace(currentLocation); + + // https://developers.google.com/web/updates/2015/09/history-api-scroll-restoration + let originalScrollRestoration; + if (window.history && 'scrollRestoration' in window.history) { + originalScrollRestoration = window.history.scrollRestoration; + window.history.scrollRestoration = 'manual'; + } - addEventListener(window, 'scroll', setPageOffset); - addEventListener(window, 'pagehide', () => { - removeEventListener(window, 'scroll', setPageOffset); + // Prevent listeners collisions during history navigation + addEventListener(window, 'pagehide', function onPageHide() { + removeEventListener(window, 'pagehide', onPageHide); removeHistoryListener(); + if (originalScrollRestoration) { + window.history.scrollRestoration = originalScrollRestoration; + originalScrollRestoration = undefined; + } }); } diff --git a/src/core/DOMUtils.js b/src/core/DOMUtils.js index e00492376..859e6341f 100644 --- a/src/core/DOMUtils.js +++ b/src/core/DOMUtils.js @@ -22,3 +22,13 @@ export function removeEventListener(node, event, listener) { node.detachEvent(`on${event}`, listener); } } + +export function windowScrollX() { + return (window.pageXOffset !== undefined) ? window.pageXOffset : + (document.documentElement || document.body.parentNode || document.body).scrollLeft; +} + +export function windowScrollY() { + return (window.pageYOffset !== undefined) ? window.pageYOffset : + (document.documentElement || document.body.parentNode || document.body).scrollTop; +} diff --git a/src/server.js b/src/server.js index 2600b3a23..01fb63c58 100644 --- a/src/server.js +++ b/src/server.js @@ -16,7 +16,7 @@ import expressJwt from 'express-jwt'; import expressGraphQL from 'express-graphql'; import jwt from 'jsonwebtoken'; import ReactDOM from 'react-dom/server'; -import { match } from 'universal-router'; +import UniversalRouter from 'universal-router'; import PrettyError from 'pretty-error'; import passport from './core/passport'; import models from './data/models'; @@ -91,7 +91,7 @@ app.get('*', async (req, res, next) => { data.trackingId = analytics.google.trackingId; } - await match(routes, { + await UniversalRouter.resolve(routes, { path: req.path, query: req.query, context: { From daf10cf83807379d6c81eb59d71887472838dd04 Mon Sep 17 00:00:00 2001 From: Dhyego Fernando Date: Thu, 9 Jun 2016 16:27:32 -0400 Subject: [PATCH 038/357] fix(Isomorphic Style Loader): Add support to load multiple styles (#678) * fix(Isomorphic Style Loader): Add support to load multiple styles * fix(Isomorphic Style Loader): Add remove feature back --- src/client.js | 7 ++++++- src/server.js | 4 +++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/client.js b/src/client.js index 3ddbdee9c..47346fac7 100644 --- a/src/client.js +++ b/src/client.js @@ -22,7 +22,12 @@ import { } from './core/DOMUtils'; const context = { - insertCss: styles => styles._insertCss(), // eslint-disable-line no-underscore-dangle + insertCss: (...styles) => { + const removeCss = styles.map(style => style._insertCss()); // eslint-disable-line no-underscore-dangle, max-len + return () => { + removeCss.forEach(f => f()); + }; + }, setTitle: value => (document.title = value), setMeta: (name, content) => { // Remove and create a new tag in order to make it work diff --git a/src/server.js b/src/server.js index 01fb63c58..976518c78 100644 --- a/src/server.js +++ b/src/server.js @@ -95,7 +95,9 @@ app.get('*', async (req, res, next) => { path: req.path, query: req.query, context: { - insertCss: styles => css.push(styles._getCss()), // eslint-disable-line no-underscore-dangle + insertCss: (...styles) => { + styles.forEach(style => css.push(style._getCss())); // eslint-disable-line no-underscore-dangle, max-len + }, setTitle: value => (data.title = value), setMeta: (key, value) => (data[key] = value), }, From 8c3bd6c7a0ba5fefa6cf67091c46381d9b65839c Mon Sep 17 00:00:00 2001 From: bravo-kernel Date: Thu, 9 Jun 2016 22:56:54 +0200 Subject: [PATCH 039/357] Adds testing section (#687) Integrates comments by @langpavel --- docs/README.md | 1 + docs/testing-your-application.md | 97 ++++++++++++++++++++++++++++++++ 2 files changed, 98 insertions(+) create mode 100644 docs/testing-your-application.md diff --git a/docs/README.md b/docs/README.md index b314fe324..522107b96 100644 --- a/docs/README.md +++ b/docs/README.md @@ -6,6 +6,7 @@ * [React Style Guide](./react-style-guide.md) * [How to configure text editors and IDEs](./how-to-configure-text-editors.md) * [Data fetching with WHATWG Fetch](./data-fetching.md) +* [Testing your application](./testing-your-application.md) ### Questions diff --git a/docs/testing-your-application.md b/docs/testing-your-application.md new file mode 100644 index 000000000..2cf77c74c --- /dev/null +++ b/docs/testing-your-application.md @@ -0,0 +1,97 @@ +## Testing your application + +### Used libraries + +RSK comes with the following libraries for testing purposes: + +- [Mocha](https://mochajs.org/) - Node.js and browser test runner +- [Chai](http://chaijs.com/) - Assertion library +- [Enzyme](https://github.com/airbnb/enzyme) - Testing utilities for React + +You may also want to take a look at the following related packages: + +- [jsdom](https://github.com/tmpvar/jsdom) +- [react-addons-test-utils](https://www.npmjs.com/package/react-addons-test-utils) + +### Running tests + +To test your application simply run the +[`npm test`](https://github.com/kriasoft/react-starter-kit/blob/b22b1810461cec9c53eedffe632a3ce70a6b29a3/package.json#L154) +command which will: +- recursively find all files ending with `.test.js` in your `src/` directory +- mocha execute found files + +```bash +npm test +``` + +### Conventions + +- test filenames MUST end with `test.js` or `npm test` will not be able to detect them +- test filenames SHOULD be named after the related component (e.g. create `Login.test.js` for +`Login.js` component) + +### Basic example + +To help you on your way RSK comes with the following +[basic test case](https://github.com/kriasoft/react-starter-kit/blob/master/src/components/App/App.test.js) +you can use as a starting point: + +```js +import React from 'react'; +import { expect } from 'chai'; +import { shallow } from 'enzyme'; +import App from './App'; + +describe('App', () => { + + it('renders children correctly', () => { + const wrapper = shallow( + {} }}> +
+ + ); + + expect(wrapper.contains(
)).to.be.true; + }); + +}); +``` + +### React-intl example + +React-intl users MUST render/wrap components inside an IntlProvider like the example below: + +The example below example is a drop-in test for the RSK `Header` component: + +```js +import React from 'react'; +import Header from './Header'; +import IntlProvider from 'react-intl'; +import Navigation from '../../components/Navigation'; + +describe('A test suite for
', () => { + + it('should contain a component', () => { + it('rendering', () => { + const wrapper = renderIntoDocument(
); + expect(wrapper.find(Navigation)).to.have.length(1); + }); + }); + +}); +``` + +Please note that NOT using IntlProvider will produce the following error: + +> Invariant Violation: [React Intl] Could not find required `intl` object. +> needs to exist in the component ancestry. + +### Linting + +Running RSK eslint will also scan your test files: + +```bash +npm run eslint +``` + From de4d26eef0fcccf90d4412dfea734cc84cf7124c Mon Sep 17 00:00:00 2001 From: Vladimir Kutepov Date: Thu, 9 Jun 2016 23:57:20 +0300 Subject: [PATCH 040/357] Fix npm warnings about graphql dependencies (#693) fixes #661 --- package.json | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/package.json b/package.json index 521cf8486..c960cc3a4 100644 --- a/package.json +++ b/package.json @@ -5,38 +5,38 @@ "npm": ">=3.3 <4" }, "dependencies": { - "babel-polyfill": "6.9.0", - "babel-runtime": "6.9.0", + "babel-polyfill": "6.9.1", + "babel-runtime": "6.9.2", "bluebird": "3.4.0", "body-parser": "1.15.1", "classnames": "2.2.5", - "cookie-parser": "1.4.2", + "cookie-parser": "1.4.3", "core-js": "2.4.0", "eventemitter3": "1.2.0", "express": "4.13.4", - "express-graphql": "0.5.1", + "express-graphql": "0.5.3", "express-jwt": "3.4.0", "fastclick": "1.0.6", - "fbjs": "0.8.2", - "front-matter": "2.0.8", - "graphiql": "0.7.1", + "fbjs": "0.8.3", + "front-matter": "2.1.0", + "graphiql": "0.7.2", "graphql": "0.6.0", "history": "3.0.0", "isomorphic-style-loader": "1.0.0", "jade": "1.11.0", "jsonwebtoken": "7.0.0", - "markdown-it": "6.0.2", - "node-fetch": "1.5.2", + "markdown-it": "6.0.5", + "node-fetch": "1.5.3", "normalize.css": "4.1.1", "passport": "0.3.2", "passport-facebook": "2.1.1", "pretty-error": "2.0.0", "react": "15.1.0", "react-dom": "15.1.0", - "sequelize": "3.23.2", + "sequelize": "3.23.3", "source-map-support": "0.4.0", "sqlite3": "3.1.4", - "universal-router": "1.2.1", + "universal-router": "1.2.2", "whatwg-fetch": "1.0.0" }, "devDependencies": { @@ -67,7 +67,7 @@ "eslint": "^2.10.2", "eslint-config-airbnb": "^9.0.1", "eslint-loader": "^1.3.0", - "eslint-plugin-import": "^1.8.0", + "eslint-plugin-import": "^1.8.1", "eslint-plugin-jsx-a11y": "^1.2.2", "eslint-plugin-react": "^5.1.1", "extend": "^3.0.0", From bf44958c69674151c018a7bd5ac65ea02685916d Mon Sep 17 00:00:00 2001 From: Adam Date: Fri, 10 Jun 2016 11:03:57 -0400 Subject: [PATCH 041/357] Fix OccurrenceOrderPlugin spelling (#683) --- tools/webpack.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/webpack.config.js b/tools/webpack.config.js index 4af4d52c1..0ec56876b 100644 --- a/tools/webpack.config.js +++ b/tools/webpack.config.js @@ -229,7 +229,7 @@ const clientConfig = extend(true, {}, config, { // Assign the module and chunk ids by occurrence count // Consistent ordering of modules required if using any hashing ([hash] or [chunkhash]) // https://webpack.github.io/docs/list-of-plugins.html#occurrenceorderplugin - new webpack.optimize.OccurenceOrderPlugin(true), + new webpack.optimize.OccurrenceOrderPlugin(true), ...DEBUG ? [] : [ From 68b0fd4ac8b177bdcdeab42cd4f074316efa2b01 Mon Sep 17 00:00:00 2001 From: Kenji Miwa Date: Sun, 12 Jun 2016 14:54:02 -0700 Subject: [PATCH 042/357] fixed typo in passport.js (#696) --- src/core/passport.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/passport.js b/src/core/passport.js index ddf4b945e..f83fdb565 100644 --- a/src/core/passport.js +++ b/src/core/passport.js @@ -100,7 +100,7 @@ passport.use(new FacebookStrategy({ { type: claimType, value: accessToken }, ], profile: { - displaynName: profile.displayName, + displayName: profile.displayName, gender: profile._json.gender, picture: `https://graph.facebook.com/${profile.id}/picture?type=large`, }, From 47c7156e92e9f42a71b8ddcc521961833122ceb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20M=C3=BChl?= Date: Tue, 14 Jun 2016 15:05:41 +0700 Subject: [PATCH 043/357] Docs: use more expressive language (#701) --- docs/react-style-guide.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/react-style-guide.md b/docs/react-style-guide.md index a54d2da8d..ad2f023b7 100644 --- a/docs/react-style-guide.md +++ b/docs/react-style-guide.md @@ -21,7 +21,7 @@ * Add `package.json` file into each component's folder.
This will allow to easily reference such components from other places in your code.
- `import Nav from '../Nav'` vs `import Nav from '../Nav/Nav.js'` + Use `import Nav from '../Nav'` instead of `import Nav from '../Nav/Nav.js'` ``` /components/Navigation/icon.svg From bb655fa1777dfc0b8b9105de6b8a673e9ef53009 Mon Sep 17 00:00:00 2001 From: DominikeCruz Date: Thu, 16 Jun 2016 10:30:45 -0300 Subject: [PATCH 044/357] Update stylelint-config-standard (#707) * Update stylelint-config-standard When I run 'npm run lint', this warning show up: Deprecation Warning: 'number-zero-length-no-unit' has been deprecated, and will be removed in '7.0'. Use 'length-zero-no-unit' instead. See: http://stylelint.io/user-guide/rules/length-zero-no-unit/ update package to remove it. * Update stylelint to use length-zero-no-unit instead number-zero-length-no-unit. --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index c960cc3a4..afef077d2 100644 --- a/package.json +++ b/package.json @@ -101,8 +101,8 @@ "react-transform-hmr": "^1.0.4", "redbox-react": "^1.2.6", "sinon": "^2.0.0-pre", - "stylelint": "^6.5.1", - "stylelint-config-standard": "^8.0.0", + "stylelint": "^6.6.0", + "stylelint-config-standard": "^9.0.0", "url-loader": "^0.5.7", "webpack": "^1.13.1", "webpack-hot-middleware": "^2.10.0", From 0f2d46b1475aa233da6b71c439fa46dac15f7204 Mon Sep 17 00:00:00 2001 From: Aron Griffis Date: Fri, 17 Jun 2016 12:50:29 -0400 Subject: [PATCH 045/357] Fix spelling of "vice versa" (#710) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 40037b8ed..63fc7d824 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ branches that you can use either as a reference or merge into your project: isomorphic Redux and React Intl by [Pavel Lang](https://github.com/langpavel) (see [how to integrate React Intl](./docs/recipes/how-to-integrate-react-intl.md)) -If you think that any of these features should be on `master`, or vise versa, some features should +If you think that any of these features should be on `master`, or vice versa, some features should removed from the `master` branch, please [let us know](https://gitter.im/kriasoft/react-starter-kit). We love your feedback! From 7456f4723e4d4b4fa85af91e13353640d15ccd95 Mon Sep 17 00:00:00 2001 From: Vladimir Kutepov Date: Sat, 18 Jun 2016 00:01:13 +0300 Subject: [PATCH 046/357] Remove jade dependency --- package.json | 2 -- src/client.js | 6 ++-- src/components/Html.js | 42 ++++++++++++++++++++++++ src/config.js | 5 +-- src/content/about.jade | 47 --------------------------- src/content/about.md | 46 ++++++++++++++++++++++++++ src/content/index.jade | 22 ------------- src/content/index.md | 19 +++++++++++ src/content/privacy.jade | 52 ----------------------------- src/content/privacy.md | 46 ++++++++++++++++++++++++++ src/data/queries/content.js | 8 ++--- src/routes/error/ErrorPage.js | 6 ++-- src/server.js | 38 ++++++++++++---------- src/views/error.jade | 61 ----------------------------------- src/views/index.jade | 18 ----------- tools/webpack.config.js | 4 --- 16 files changed, 187 insertions(+), 235 deletions(-) create mode 100644 src/components/Html.js delete mode 100644 src/content/about.jade create mode 100644 src/content/about.md delete mode 100644 src/content/index.jade create mode 100644 src/content/index.md delete mode 100644 src/content/privacy.jade create mode 100644 src/content/privacy.md delete mode 100644 src/views/error.jade delete mode 100644 src/views/index.jade diff --git a/package.json b/package.json index afef077d2..570de2480 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,6 @@ "graphql": "0.6.0", "history": "3.0.0", "isomorphic-style-loader": "1.0.0", - "jade": "1.11.0", "jsonwebtoken": "7.0.0", "markdown-it": "6.0.5", "node-fetch": "1.5.3", @@ -75,7 +74,6 @@ "gaze": "^1.0.0", "git-repository": "^0.1.4", "glob": "^7.0.3", - "jade-loader": "^0.8.0", "json-loader": "^0.5.4", "mkdirp": "^0.5.1", "mocha": "^2.5.3", diff --git a/src/client.js b/src/client.js index 47346fac7..f91bea1f0 100644 --- a/src/client.js +++ b/src/client.js @@ -65,7 +65,9 @@ let renderComplete = (state, callback) => { // Google Analytics tracking. Don't send 'pageview' event after // the initial rendering, as it was already sent - window.ga('send', 'pageview'); + if (window.ga) { + window.ga('send', 'pageview'); + } callback(true); }; @@ -109,7 +111,7 @@ function run() { query: location.query, state: location.state, context, - render: render.bind(undefined, container, location.state), + render: render.bind(undefined, container, location.state), // eslint-disable-line react/jsx-no-bind, max-len }).catch(err => console.error(err)); // eslint-disable-line no-console } diff --git a/src/components/Html.js b/src/components/Html.js new file mode 100644 index 000000000..be22c96b1 --- /dev/null +++ b/src/components/Html.js @@ -0,0 +1,42 @@ +import React, { PropTypes } from 'react'; +import { analytics } from '../config'; + +function Html({ title, description, style, script, children }) { + return ( + + + + + {title} + + + +