├── public ├── tile.png ├── favicon.ico ├── tile-wide.png ├── robots.txt ├── apple-touch-icon.png ├── humans.txt ├── browserconfig.xml └── crossdomain.xml ├── test ├── mocha.opts └── setup.js ├── src ├── components │ ├── Header │ │ ├── logo-small.png │ │ ├── logo-small@2x.png │ │ ├── package.json │ │ ├── Header.css │ │ └── Header.js │ ├── Link │ │ ├── package.json │ │ └── Link.js │ ├── Page │ │ ├── package.json │ │ ├── Page.css │ │ └── Page.js │ ├── Footer │ │ ├── package.json │ │ ├── Footer.css │ │ └── Footer.js │ ├── Layout │ │ ├── package.json │ │ ├── Layout.test.js │ │ ├── Layout.js │ │ ├── __snapshots__ │ │ │ └── Layout.test.js.snap │ │ └── Layout.css │ ├── Feedback │ │ ├── package.json │ │ ├── Feedback.css │ │ └── Feedback.js │ ├── Navigation │ │ ├── package.json │ │ ├── Navigation.css │ │ └── Navigation.js │ ├── variables.css │ ├── App.js │ └── Html.js ├── routes │ ├── about │ │ ├── about.md │ │ └── index.js │ ├── error │ │ ├── index.js │ │ ├── ErrorPage.css │ │ └── ErrorPage.js │ ├── admin │ │ ├── Admin.css │ │ ├── index.js │ │ └── Admin.js │ ├── contact │ │ ├── Contact.css │ │ ├── index.js │ │ └── Contact.js │ ├── not-found │ │ ├── NotFound.css │ │ ├── index.js │ │ └── NotFound.js │ ├── register │ │ ├── Register.css │ │ ├── index.js │ │ └── Register.js │ ├── login │ │ ├── index.js │ │ ├── Login.css │ │ └── Login.js │ ├── privacy │ │ ├── index.js │ │ └── privacy.md │ ├── home │ │ ├── Home.css │ │ ├── index.js │ │ └── Home.js │ └── index.js ├── history.js ├── data │ ├── sequelize.js │ ├── models │ │ ├── UserClaim.js │ │ ├── UserLogin.js │ │ ├── User.js │ │ ├── UserProfile.js │ │ └── index.js │ ├── queries │ │ ├── me.js │ │ └── news.js │ ├── types │ │ ├── UserType.js │ │ └── NewsItemType.js │ └── schema.js ├── router.js ├── DOMUtils.js ├── createFetch.js ├── config.js ├── passport.js ├── client.js └── server.js ├── jest └── fileTransformer.js ├── .flowconfig ├── .nycrc ├── .travis.yml ├── README.md ├── Dockerfile ├── .gitignore ├── .editorconfig ├── tools ├── clean.js ├── lib │ ├── overrideRules.js │ ├── markdown-loader.js │ ├── cp.js │ └── fs.js ├── bundle.js ├── build.js ├── run.js ├── README.md ├── copy.js ├── runServer.js ├── render.js ├── postcss.config.js ├── deploy.js └── start.js ├── .gitattributes ├── docs ├── recipes │ ├── how-to-configure-facebook-login.md │ ├── how-to-integrate-disqus.md │ ├── how-to-use-sass.md │ ├── how-to-integrate-redux.md │ ├── using-npm-and-webpack-as-a-build-tool.md │ ├── how-to-integrate-react-intl.md │ └── how-to-implement-routing.md ├── README.md ├── data-fetching.md ├── testing-your-application.md ├── how-to-configure-text-editors.md ├── react-style-guide.md └── getting-started.md ├── LICENSE.txt ├── .stylelintrc.js ├── .eslintrc.js ├── CONTRIBUTING.md ├── jest.config.js ├── CHANGELOG.md └── package.json /public/tile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Big-Silver/Javascript-Animation/HEAD/public/tile.png -------------------------------------------------------------------------------- /test/mocha.opts: -------------------------------------------------------------------------------- 1 | --compilers js:babel-register 2 | --require ./test/setup 3 | src/**/*.test.js 4 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Big-Silver/Javascript-Animation/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /public/tile-wide.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Big-Silver/Javascript-Animation/HEAD/public/tile-wide.png -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # www.robotstxt.org/ 2 | 3 | # Allow crawling of all content 4 | User-agent: * 5 | Disallow: 6 | -------------------------------------------------------------------------------- /public/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Big-Silver/Javascript-Animation/HEAD/public/apple-touch-icon.png -------------------------------------------------------------------------------- /src/components/Header/logo-small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Big-Silver/Javascript-Animation/HEAD/src/components/Header/logo-small.png -------------------------------------------------------------------------------- /src/components/Link/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Link", 3 | "version": "0.0.0", 4 | "private": true, 5 | "main": "./Link.js" 6 | } 7 | -------------------------------------------------------------------------------- /src/components/Page/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Page", 3 | "version": "0.0.0", 4 | "private": true, 5 | "main": "./Page.js" 6 | } 7 | -------------------------------------------------------------------------------- /src/components/Footer/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Footer", 3 | "version": "0.0.0", 4 | "private": true, 5 | "main": "./Footer.js" 6 | } 7 | -------------------------------------------------------------------------------- /src/components/Header/logo-small@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Big-Silver/Javascript-Animation/HEAD/src/components/Header/logo-small@2x.png -------------------------------------------------------------------------------- /src/components/Header/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Header", 3 | "version": "0.0.0", 4 | "private": true, 5 | "main": "./Header.js" 6 | } 7 | -------------------------------------------------------------------------------- /src/components/Layout/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Layout", 3 | "version": "0.0.0", 4 | "private": true, 5 | "main": "./Layout.js" 6 | } 7 | -------------------------------------------------------------------------------- /src/components/Feedback/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Feedback", 3 | "version": "0.0.0", 4 | "private": true, 5 | "main": "./Feedback.js" 6 | } 7 | -------------------------------------------------------------------------------- /src/components/Navigation/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Navigation", 3 | "version": "0.0.0", 4 | "private": true, 5 | "main": "./Navigation.js" 6 | } 7 | -------------------------------------------------------------------------------- /jest/fileTransformer.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | process(src, filename) { 5 | return `module.exports = ${JSON.stringify(path.basename(filename))};`; 6 | }, 7 | }; 8 | -------------------------------------------------------------------------------- /.flowconfig: -------------------------------------------------------------------------------- 1 | [ignore] 2 | .*/build 3 | .*/docs 4 | .*/node_modules 5 | .*/public 6 | 7 | [include] 8 | 9 | [options] 10 | module.system.node.resolve_dirname=node_modules 11 | module.system.node.resolve_dirname=src 12 | -------------------------------------------------------------------------------- /.nycrc: -------------------------------------------------------------------------------- 1 | { 2 | "require": [ 3 | "babel-register" 4 | ], 5 | "include": [ 6 | "src" 7 | ], 8 | "reporter": [ 9 | "lcov", 10 | "text" 11 | ], 12 | "sourceMap": false, 13 | "instrument": false 14 | } 15 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - '8' 4 | - '7' 5 | - '6' 6 | env: 7 | - CXX=g++-4.8 8 | addons: 9 | apt: 10 | sources: 11 | - ubuntu-toolchain-r-test 12 | packages: 13 | - g++-4.8 14 | script: 15 | - yarn lint 16 | - yarn test 17 | -------------------------------------------------------------------------------- /public/humans.txt: -------------------------------------------------------------------------------- 1 | # humanstxt.org/ 2 | # The humans responsible & technology colophon 3 | 4 | # TEAM 5 | 6 | -- -- 7 | 8 | # THANKS 9 | 10 | 11 | 12 | # TECHNOLOGY COLOPHON 13 | 14 | CSS3, HTML5, JavaScript 15 | React Starter Kit -- https://reactstarter.com/ 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Javascript Animations 2 | 3 | ## Install and Run 4 | ```bash 5 | # Clone the project from repos 6 | $ git clone https://github.com/Big-Silver/Javascript-Animation.git 7 | # Go to the project 8 | $ cd Javascript-Animation 9 | # Install the project 10 | $ npm install 11 | # Run the project 12 | $ npm start 13 | ``` -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:7.9.0-alpine 2 | 3 | # Set a working directory 4 | WORKDIR /usr/src/app 5 | 6 | COPY ./build/package.json . 7 | COPY ./build/yarn.lock . 8 | 9 | # Install Node.js dependencies 10 | RUN yarn install --production --no-progress 11 | 12 | # Copy application files 13 | COPY ./build . 14 | 15 | CMD [ "node", "server.js" ] 16 | -------------------------------------------------------------------------------- /src/routes/about/about.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: About web-animaition.js 3 | component: ContentPage 4 | --- 5 | A new JavaScript API for driving animated content on the web. By unifying the animation features of SVG and CSS, Web Animations unlocks features previously only usable declaratively, and exposes powerful, high-performance animation capabilities to developers. 6 | -------------------------------------------------------------------------------- /public/browserconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # Dependencies 4 | node_modules/ 5 | 6 | # Compiled output 7 | build 8 | 9 | # Runtime data 10 | database.sqlite 11 | 12 | # Test coverage 13 | coverage 14 | .nyc_output 15 | 16 | # Logs 17 | npm-debug.log* 18 | yarn-debug.log* 19 | yarn-error.log* 20 | 21 | # Editors and IDEs 22 | .idea 23 | .vscode/* 24 | !.vscode/settings.json 25 | !.vscode/tasks.json 26 | !.vscode/launch.json 27 | !.vscode/extensions.json 28 | 29 | # Misc 30 | .DS_Store 31 | -------------------------------------------------------------------------------- /src/history.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Starter Kit (https://www.reactstarterkit.com/) 3 | * 4 | * Copyright © 2014-present Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | import createBrowserHistory from 'history/createBrowserHistory'; 11 | 12 | // Navigation manager, e.g. history.push('/home') 13 | // https://github.com/mjackson/history 14 | export default process.env.BROWSER && createBrowserHistory(); 15 | -------------------------------------------------------------------------------- /src/routes/error/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Starter Kit (https://www.reactstarterkit.com/) 3 | * 4 | * Copyright © 2014-present Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | import React from 'react'; 11 | import ErrorPage from './ErrorPage'; 12 | 13 | function action() { 14 | return { 15 | title: 'Demo Error', 16 | component: , 17 | }; 18 | } 19 | 20 | export default action; 21 | -------------------------------------------------------------------------------- /src/components/Page/Page.css: -------------------------------------------------------------------------------- 1 | /** 2 | * React Starter Kit (https://www.reactstarterkit.com/) 3 | * 4 | * Copyright © 2014-present Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | @import '../variables.css'; 11 | 12 | .root { 13 | padding-left: 20px; 14 | padding-right: 20px; 15 | } 16 | 17 | .container { 18 | margin: 0 auto; 19 | padding: 0 0 40px; 20 | max-width: var(--max-content-width); 21 | } 22 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # http://editorconfig.org 4 | 5 | root = true 6 | 7 | [*] 8 | 9 | # Change these settings to your own preference 10 | indent_style = space 11 | indent_size = 2 12 | 13 | # We recommend you to keep these unchanged 14 | end_of_line = lf 15 | charset = utf-8 16 | trim_trailing_whitespace = true 17 | insert_final_newline = true 18 | 19 | # editorconfig-tools is unable to ignore longs strings or urls 20 | max_line_length = null 21 | -------------------------------------------------------------------------------- /src/data/sequelize.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Starter Kit (https://www.reactstarterkit.com/) 3 | * 4 | * Copyright © 2014-present Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | import Sequelize from 'sequelize'; 11 | import config from '../config'; 12 | 13 | const sequelize = new Sequelize(config.databaseUrl, { 14 | define: { 15 | freezeTableName: true, 16 | }, 17 | }); 18 | 19 | export default sequelize; 20 | -------------------------------------------------------------------------------- /src/routes/admin/Admin.css: -------------------------------------------------------------------------------- 1 | /** 2 | * React Starter Kit (https://www.reactstarterkit.com/) 3 | * 4 | * Copyright © 2014-present Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | @import '../../components/variables.css'; 11 | 12 | .root { 13 | padding-left: 20px; 14 | padding-right: 20px; 15 | } 16 | 17 | .container { 18 | margin: 0 auto; 19 | padding: 0 0 40px; 20 | max-width: var(--max-content-width); 21 | } 22 | -------------------------------------------------------------------------------- /src/routes/contact/Contact.css: -------------------------------------------------------------------------------- 1 | /** 2 | * React Starter Kit (https://www.reactstarterkit.com/) 3 | * 4 | * Copyright © 2014-present Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | @import '../../components/variables.css'; 11 | 12 | .root { 13 | padding-left: 20px; 14 | padding-right: 20px; 15 | } 16 | 17 | .container { 18 | margin: 0 auto; 19 | padding: 0 0 40px; 20 | max-width: var(--max-content-width); 21 | } 22 | -------------------------------------------------------------------------------- /src/routes/not-found/NotFound.css: -------------------------------------------------------------------------------- 1 | /** 2 | * React Starter Kit (https://www.reactstarterkit.com/) 3 | * 4 | * Copyright © 2014-present Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | @import '../../components/variables.css'; 11 | 12 | .root { 13 | padding-left: 20px; 14 | padding-right: 20px; 15 | } 16 | 17 | .container { 18 | margin: 0 auto; 19 | padding: 0 0 40px; 20 | max-width: var(--max-content-width); 21 | } 22 | -------------------------------------------------------------------------------- /src/routes/register/Register.css: -------------------------------------------------------------------------------- 1 | /** 2 | * React Starter Kit (https://www.reactstarterkit.com/) 3 | * 4 | * Copyright © 2014-present Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | @import '../../components/variables.css'; 11 | 12 | .root { 13 | padding-left: 20px; 14 | padding-right: 20px; 15 | } 16 | 17 | .container { 18 | margin: 0 auto; 19 | padding: 0 0 40px; 20 | max-width: var(--max-content-width); 21 | } 22 | -------------------------------------------------------------------------------- /src/data/models/UserClaim.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Starter Kit (https://www.reactstarterkit.com/) 3 | * 4 | * Copyright © 2014-present Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | import DataType from 'sequelize'; 11 | import Model from '../sequelize'; 12 | 13 | const UserClaim = Model.define('UserClaim', { 14 | type: { 15 | type: DataType.STRING, 16 | }, 17 | 18 | value: { 19 | type: DataType.STRING, 20 | }, 21 | }); 22 | 23 | export default UserClaim; 24 | -------------------------------------------------------------------------------- /src/data/queries/me.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Starter Kit (https://www.reactstarterkit.com/) 3 | * 4 | * Copyright © 2014-present Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | import UserType from '../types/UserType'; 11 | 12 | const me = { 13 | type: UserType, 14 | resolve({ request }) { 15 | return ( 16 | request.user && { 17 | id: request.user.id, 18 | email: request.user.email, 19 | } 20 | ); 21 | }, 22 | }; 23 | 24 | export default me; 25 | -------------------------------------------------------------------------------- /tools/clean.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Starter Kit (https://www.reactstarterkit.com/) 3 | * 4 | * Copyright © 2014-present Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | import { cleanDir } from './lib/fs'; 11 | 12 | /** 13 | * Cleans up the output (build) directory. 14 | */ 15 | function clean() { 16 | return Promise.all([ 17 | cleanDir('build/*', { 18 | nosort: true, 19 | dot: true, 20 | ignore: ['build/.git'], 21 | }), 22 | ]); 23 | } 24 | 25 | export default clean; 26 | -------------------------------------------------------------------------------- /src/data/models/UserLogin.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Starter Kit (https://www.reactstarterkit.com/) 3 | * 4 | * Copyright © 2014-present Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | import DataType from 'sequelize'; 11 | import Model from '../sequelize'; 12 | 13 | const UserLogin = Model.define('UserLogin', { 14 | name: { 15 | type: DataType.STRING(50), 16 | primaryKey: true, 17 | }, 18 | 19 | key: { 20 | type: DataType.STRING(100), 21 | primaryKey: true, 22 | }, 23 | }); 24 | 25 | export default UserLogin; 26 | -------------------------------------------------------------------------------- /public/crossdomain.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 15 | 16 | -------------------------------------------------------------------------------- /src/data/types/UserType.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Starter Kit (https://www.reactstarterkit.com/) 3 | * 4 | * Copyright © 2014-present Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | import { 11 | GraphQLObjectType as ObjectType, 12 | GraphQLID as ID, 13 | GraphQLString as StringType, 14 | GraphQLNonNull as NonNull, 15 | } from 'graphql'; 16 | 17 | const UserType = new ObjectType({ 18 | name: 'User', 19 | fields: { 20 | id: { type: new NonNull(ID) }, 21 | email: { type: StringType }, 22 | }, 23 | }); 24 | 25 | export default UserType; 26 | -------------------------------------------------------------------------------- /src/data/schema.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Starter Kit (https://www.reactstarterkit.com/) 3 | * 4 | * Copyright © 2014-present Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | import { 11 | GraphQLSchema as Schema, 12 | GraphQLObjectType as ObjectType, 13 | } from 'graphql'; 14 | 15 | import me from './queries/me'; 16 | import news from './queries/news'; 17 | 18 | const schema = new Schema({ 19 | query: new ObjectType({ 20 | name: 'Query', 21 | fields: { 22 | me, 23 | news, 24 | }, 25 | }), 26 | }); 27 | 28 | export default schema; 29 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Automatically normalize line endings for all text-based files 2 | # http://git-scm.com/docs/gitattributes#_end_of_line_conversion 3 | * text=auto 4 | 5 | # For the following file types, normalize line endings to LF on 6 | # checkin and prevent conversion to CRLF when they are checked out 7 | # (this is required in order to prevent newline related issues like, 8 | # for example, after the build script is run) 9 | .* text eol=lf 10 | *.html text eol=lf 11 | *.css text eol=lf 12 | *.less text eol=lf 13 | *.scss text eol=lf 14 | *.sss text eol=lf 15 | *.js text eol=lf 16 | *.json text eol=lf 17 | *.md text eol=lf 18 | *.sh text eol=lf 19 | *.txt text eol=lf 20 | *.xml text eol=lf 21 | -------------------------------------------------------------------------------- /docs/recipes/how-to-configure-facebook-login.md: -------------------------------------------------------------------------------- 1 | How to configure Facebook Login 2 | 3 | 1. Navigate and Login to https://developers.facebook.com/apps 4 | 2. Click "Add New App" 5 | 3. Enter your app name and contact email 6 | 4. In App Dashboard, click `Set Up` in Facebook Login section 7 | 5. Chose "Web" as your Platform 8 | 6. Set Site URL to `http://localhost:3000/` for local testing. (Port of your Node server, not the port of the BrowserSync proxy) 9 | 7. Click Facebook Login on the left panel 10 | 8. Turn on Client OAuth Login and Web OAuth Login. Enter `http://localhost:3000/login/facebook/return` for Valid OAuth redirect URIs. 11 | 9. Get your App ID and Secret and copy it to [`src/config.js`](../src/config.js) 12 | -------------------------------------------------------------------------------- /src/routes/login/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Starter Kit (https://www.reactstarterkit.com/) 3 | * 4 | * Copyright © 2014-present Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | import React from 'react'; 11 | import Layout from '../../components/Layout'; 12 | import Login from './Login'; 13 | 14 | const title = 'Log In'; 15 | 16 | function action() { 17 | return { 18 | chunks: ['login'], 19 | title, 20 | component: ( 21 | 22 | 23 | 24 | ), 25 | }; 26 | } 27 | 28 | export default action; 29 | -------------------------------------------------------------------------------- /src/routes/contact/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Starter Kit (https://www.reactstarterkit.com/) 3 | * 4 | * Copyright © 2014-present Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | import React from 'react'; 11 | import Layout from '../../components/Layout'; 12 | import Contact from './Contact'; 13 | 14 | const title = 'Contact Us'; 15 | 16 | function action() { 17 | return { 18 | chunks: ['contact'], 19 | title, 20 | component: ( 21 | 22 | 23 | 24 | ), 25 | }; 26 | } 27 | 28 | export default action; 29 | -------------------------------------------------------------------------------- /src/routes/error/ErrorPage.css: -------------------------------------------------------------------------------- 1 | /** 2 | * React Starter Kit (https://www.reactstarterkit.com/) 3 | * 4 | * Copyright © 2014-present Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | html { 11 | display: flex; 12 | align-items: center; 13 | justify-content: center; 14 | padding: 0 2rem; 15 | height: 100%; 16 | font-family: sans-serif; 17 | text-align: center; 18 | color: #888; 19 | } 20 | 21 | body { 22 | margin: 0; 23 | } 24 | 25 | h1 { 26 | font-weight: 400; 27 | color: #555; 28 | } 29 | 30 | pre { 31 | white-space: pre-wrap; 32 | text-align: left; 33 | } 34 | -------------------------------------------------------------------------------- /tools/lib/overrideRules.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Starter Kit (https://www.reactstarterkit.com/) 3 | * 4 | * Copyright © 2014-present Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | function overrideRules(rules, patch) { 11 | return rules.map(ruleToPatch => { 12 | let rule = patch(ruleToPatch); 13 | if (rule.rules) { 14 | rule = { ...rule, rules: overrideRules(rule.rules, patch) }; 15 | } 16 | if (rule.oneOf) { 17 | rule = { ...rule, oneOf: overrideRules(rule.oneOf, patch) }; 18 | } 19 | return rule; 20 | }); 21 | } 22 | 23 | export default overrideRules; 24 | -------------------------------------------------------------------------------- /src/routes/about/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Starter Kit (https://www.reactstarterkit.com/) 3 | * 4 | * Copyright © 2014-present Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | import React from 'react'; 11 | import Layout from '../../components/Layout'; 12 | import Page from '../../components/Page'; 13 | import about from './about.md'; 14 | 15 | function action() { 16 | return { 17 | chunks: ['about'], 18 | title: about.title, 19 | component: ( 20 | 21 | 22 | 23 | ), 24 | }; 25 | } 26 | 27 | export default action; 28 | -------------------------------------------------------------------------------- /src/routes/register/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Starter Kit (https://www.reactstarterkit.com/) 3 | * 4 | * Copyright © 2014-present Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | import React from 'react'; 11 | import Layout from '../../components/Layout'; 12 | import Register from './Register'; 13 | 14 | const title = 'New User Registration'; 15 | 16 | function action() { 17 | return { 18 | chunks: ['register'], 19 | title, 20 | component: ( 21 | 22 | 23 | 24 | ), 25 | }; 26 | } 27 | 28 | export default action; 29 | -------------------------------------------------------------------------------- /src/routes/privacy/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Starter Kit (https://www.reactstarterkit.com/) 3 | * 4 | * Copyright © 2014-present Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | import React from 'react'; 11 | import Layout from '../../components/Layout'; 12 | import Page from '../../components/Page'; 13 | import privacy from './privacy.md'; 14 | 15 | function action() { 16 | return { 17 | chunks: ['privacy'], 18 | title: privacy.title, 19 | component: ( 20 | 21 | 22 | 23 | ), 24 | }; 25 | } 26 | 27 | export default action; 28 | -------------------------------------------------------------------------------- /tools/lib/markdown-loader.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Starter Kit (https://www.reactstarterkit.com/) 3 | * 4 | * Copyright © 2014-present Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | const MarkdownIt = require('markdown-it'); 11 | const fm = require('front-matter'); 12 | 13 | module.exports = function markdownLoader(source) { 14 | const md = new MarkdownIt({ 15 | html: true, 16 | linkify: true, 17 | }); 18 | 19 | const frontmatter = fm(source); 20 | frontmatter.attributes.html = md.render(frontmatter.body); 21 | 22 | return `module.exports = ${JSON.stringify(frontmatter.attributes)};`; 23 | }; 24 | -------------------------------------------------------------------------------- /src/routes/not-found/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Starter Kit (https://www.reactstarterkit.com/) 3 | * 4 | * Copyright © 2014-present Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | import React from 'react'; 11 | import Layout from '../../components/Layout'; 12 | import NotFound from './NotFound'; 13 | 14 | const title = 'Page Not Found'; 15 | 16 | function action() { 17 | return { 18 | chunks: ['not-found'], 19 | title, 20 | component: ( 21 | 22 | 23 | 24 | ), 25 | status: 404, 26 | }; 27 | } 28 | 29 | export default action; 30 | -------------------------------------------------------------------------------- /test/setup.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Starter Kit (https://www.reactstarterkit.com/) 3 | * 4 | * Copyright © 2014-present Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | /* Configure Mocha test runner, see package.json/scripts/test */ 11 | 12 | process.env.NODE_ENV = 'test'; 13 | 14 | function noop() { 15 | return null; 16 | } 17 | 18 | require.extensions['.css'] = noop; 19 | require.extensions['.scss'] = noop; 20 | require.extensions['.md'] = noop; 21 | require.extensions['.png'] = noop; 22 | require.extensions['.svg'] = noop; 23 | require.extensions['.jpg'] = noop; 24 | require.extensions['.jpeg'] = noop; 25 | require.extensions['.gif'] = noop; 26 | -------------------------------------------------------------------------------- /src/router.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Starter Kit (https://www.reactstarterkit.com/) 3 | * 4 | * Copyright © 2014-present Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | import Router from 'universal-router'; 11 | import routes from './routes'; 12 | 13 | export default new Router(routes, { 14 | resolveRoute(context, params) { 15 | if (typeof context.route.load === 'function') { 16 | return context.route 17 | .load() 18 | .then(action => action.default(context, params)); 19 | } 20 | if (typeof context.route.action === 'function') { 21 | return context.route.action(context, params); 22 | } 23 | return null; 24 | }, 25 | }); 26 | -------------------------------------------------------------------------------- /src/routes/admin/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Starter Kit (https://www.reactstarterkit.com/) 3 | * 4 | * Copyright © 2014-present Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | import React from 'react'; 11 | import Layout from '../../components/Layout'; 12 | import Admin from './Admin'; 13 | 14 | const title = 'Admin Page'; 15 | const isAdmin = false; 16 | 17 | function action() { 18 | if (!isAdmin) { 19 | return { redirect: '/login' }; 20 | } 21 | 22 | return { 23 | chunks: ['admin'], 24 | title, 25 | component: ( 26 | 27 | 28 | 29 | ), 30 | }; 31 | } 32 | 33 | export default action; 34 | -------------------------------------------------------------------------------- /tools/bundle.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Starter Kit (https://www.reactstarterkit.com/) 3 | * 4 | * Copyright © 2014-present Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | import webpack from 'webpack'; 11 | import webpackConfig from './webpack.config'; 12 | 13 | /** 14 | * Creates application bundles from the source files. 15 | */ 16 | function bundle() { 17 | return new Promise((resolve, reject) => { 18 | webpack(webpackConfig).run((err, stats) => { 19 | if (err) { 20 | return reject(err); 21 | } 22 | 23 | console.info(stats.toString(webpackConfig[0].stats)); 24 | return resolve(); 25 | }); 26 | }); 27 | } 28 | 29 | export default bundle; 30 | -------------------------------------------------------------------------------- /src/data/types/NewsItemType.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Starter Kit (https://www.reactstarterkit.com/) 3 | * 4 | * Copyright © 2014-present Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | import { 11 | GraphQLObjectType as ObjectType, 12 | GraphQLString as StringType, 13 | GraphQLNonNull as NonNull, 14 | } from 'graphql'; 15 | 16 | const NewsItemType = new ObjectType({ 17 | name: 'NewsItem', 18 | fields: { 19 | title: { type: new NonNull(StringType) }, 20 | link: { type: new NonNull(StringType) }, 21 | author: { type: StringType }, 22 | pubDate: { type: new NonNull(StringType) }, 23 | content: { type: StringType }, 24 | }, 25 | }); 26 | 27 | export default NewsItemType; 28 | -------------------------------------------------------------------------------- /src/components/Feedback/Feedback.css: -------------------------------------------------------------------------------- 1 | /** 2 | * React Starter Kit (https://www.reactstarterkit.com/) 3 | * 4 | * Copyright © 2014-present Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | @import '../variables.css'; 11 | 12 | .root { 13 | background: #f5f5f5; 14 | color: #333; 15 | } 16 | 17 | .container { 18 | margin: 0 auto; 19 | padding: 20px 8px; 20 | max-width: var(--max-content-width); 21 | text-align: center; 22 | font-size: 1.5em; /* ~24px */ 23 | } 24 | 25 | .link, 26 | .link:active, 27 | .link:hover, 28 | .link:visited { 29 | color: #333; 30 | text-decoration: none; 31 | } 32 | 33 | .link:hover { 34 | text-decoration: underline; 35 | } 36 | 37 | .spacer { 38 | padding-right: 15px; 39 | padding-left: 15px; 40 | } 41 | -------------------------------------------------------------------------------- /src/data/models/User.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Starter Kit (https://www.reactstarterkit.com/) 3 | * 4 | * Copyright © 2014-present Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | import DataType from 'sequelize'; 11 | import Model from '../sequelize'; 12 | 13 | const User = Model.define( 14 | 'User', 15 | { 16 | id: { 17 | type: DataType.UUID, 18 | defaultValue: DataType.UUIDV1, 19 | primaryKey: true, 20 | }, 21 | 22 | email: { 23 | type: DataType.STRING(255), 24 | validate: { isEmail: true }, 25 | }, 26 | 27 | emailConfirmed: { 28 | type: DataType.BOOLEAN, 29 | defaultValue: false, 30 | }, 31 | }, 32 | { 33 | indexes: [{ fields: ['email'] }], 34 | }, 35 | ); 36 | 37 | export default User; 38 | -------------------------------------------------------------------------------- /src/data/models/UserProfile.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Starter Kit (https://www.reactstarterkit.com/) 3 | * 4 | * Copyright © 2014-present Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | import DataType from 'sequelize'; 11 | import Model from '../sequelize'; 12 | 13 | const UserProfile = Model.define('UserProfile', { 14 | userId: { 15 | type: DataType.UUID, 16 | primaryKey: true, 17 | }, 18 | 19 | displayName: { 20 | type: DataType.STRING(100), 21 | }, 22 | 23 | picture: { 24 | type: DataType.STRING(255), 25 | }, 26 | 27 | gender: { 28 | type: DataType.STRING(50), 29 | }, 30 | 31 | location: { 32 | type: DataType.STRING(100), 33 | }, 34 | 35 | website: { 36 | type: DataType.STRING(255), 37 | }, 38 | }); 39 | 40 | export default UserProfile; 41 | -------------------------------------------------------------------------------- /src/routes/admin/Admin.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Starter Kit (https://www.reactstarterkit.com/) 3 | * 4 | * Copyright © 2014-present Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | import React from 'react'; 11 | import PropTypes from 'prop-types'; 12 | import withStyles from 'isomorphic-style-loader/lib/withStyles'; 13 | import s from './Admin.css'; 14 | 15 | class Admin extends React.Component { 16 | static propTypes = { 17 | title: PropTypes.string.isRequired, 18 | }; 19 | 20 | render() { 21 | return ( 22 |
23 |
24 |

25 | {this.props.title} 26 |

27 |

...

28 |
29 |
30 | ); 31 | } 32 | } 33 | 34 | export default withStyles(s)(Admin); 35 | -------------------------------------------------------------------------------- /src/routes/home/Home.css: -------------------------------------------------------------------------------- 1 | /** 2 | * React Starter Kit (https://www.reactstarterkit.com/) 3 | * 4 | * Copyright © 2014-present Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | @import '../../components/variables.css'; 11 | 12 | .root { 13 | padding-left: 20px; 14 | padding-right: 20px; 15 | } 16 | 17 | .container { 18 | margin: 0 auto; 19 | padding: 0 0 40px; 20 | max-width: var(--max-content-width); 21 | } 22 | 23 | /* .newsItem { 24 | margin: 0 0 2rem; 25 | } 26 | 27 | .newsTitle { 28 | font-size: 1.5rem; 29 | } 30 | 31 | .newsDesc { 32 | h1, 33 | h2, 34 | h3, 35 | h4, 36 | h5, 37 | h6 { 38 | font-size: 1.125rem; 39 | } 40 | 41 | pre { 42 | white-space: pre-wrap; 43 | font-size: 0.875rem; 44 | } 45 | 46 | img { 47 | max-width: 100%; 48 | } 49 | } */ 50 | -------------------------------------------------------------------------------- /src/routes/contact/Contact.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Starter Kit (https://www.reactstarterkit.com/) 3 | * 4 | * Copyright © 2014-present Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | import React from 'react'; 11 | import PropTypes from 'prop-types'; 12 | import withStyles from 'isomorphic-style-loader/lib/withStyles'; 13 | import s from './Contact.css'; 14 | 15 | class Contact extends React.Component { 16 | static propTypes = { 17 | title: PropTypes.string.isRequired, 18 | }; 19 | 20 | render() { 21 | return ( 22 |
23 |
24 |

25 | {this.props.title} 26 |

27 |

...

28 |
29 |
30 | ); 31 | } 32 | } 33 | 34 | export default withStyles(s)(Contact); 35 | -------------------------------------------------------------------------------- /src/routes/register/Register.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Starter Kit (https://www.reactstarterkit.com/) 3 | * 4 | * Copyright © 2014-present Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | import React from 'react'; 11 | import PropTypes from 'prop-types'; 12 | import withStyles from 'isomorphic-style-loader/lib/withStyles'; 13 | import s from './Register.css'; 14 | 15 | class Register extends React.Component { 16 | static propTypes = { 17 | title: PropTypes.string.isRequired, 18 | }; 19 | 20 | render() { 21 | return ( 22 |
23 |
24 |

25 | {this.props.title} 26 |

27 |

...

28 |
29 |
30 | ); 31 | } 32 | } 33 | 34 | export default withStyles(s)(Register); 35 | -------------------------------------------------------------------------------- /src/components/Layout/Layout.test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Starter Kit (https://www.reactstarterkit.com/) 3 | * 4 | * Copyright © 2014-present Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | /* eslint-env jest */ 11 | /* eslint-disable padded-blocks, no-unused-expressions */ 12 | 13 | import React from 'react'; 14 | import renderer from 'react-test-renderer'; 15 | import App from '../App'; 16 | import Layout from './Layout'; 17 | 18 | describe('Layout', () => { 19 | it('renders children correctly', () => { 20 | const wrapper = renderer 21 | .create( 22 | {} }}> 23 | 24 |
25 | 26 | , 27 | ) 28 | .toJSON(); 29 | 30 | expect(wrapper).toMatchSnapshot(); 31 | }); 32 | }); 33 | -------------------------------------------------------------------------------- /src/routes/home/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Starter Kit (https://www.reactstarterkit.com/) 3 | * 4 | * Copyright © 2014-present Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | import React from 'react'; 11 | import Home from './Home'; 12 | import Layout from '../../components/Layout'; 13 | 14 | async function action({ fetch }) { 15 | const resp = await fetch('/graphql', { 16 | body: JSON.stringify({ 17 | query: '{news{title,link,content}}', 18 | }), 19 | }); 20 | const { data } = await resp.json(); 21 | if (!data || !data.news) throw new Error('Failed to load the news feed.'); 22 | return { 23 | chunks: ['home'], 24 | title: 'React Starter Kit', 25 | component: ( 26 | 27 | 28 | 29 | ), 30 | }; 31 | } 32 | 33 | export default action; 34 | -------------------------------------------------------------------------------- /src/components/Footer/Footer.css: -------------------------------------------------------------------------------- 1 | /** 2 | * React Starter Kit (https://www.reactstarterkit.com/) 3 | * 4 | * Copyright © 2014-present Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | @import '../variables.css'; 11 | 12 | .root { 13 | background: #333; 14 | color: #fff; 15 | } 16 | 17 | .container { 18 | margin: 0 auto; 19 | padding: 20px 15px; 20 | max-width: var(--max-content-width); 21 | text-align: center; 22 | } 23 | 24 | .text { 25 | color: rgba(255, 255, 255, 0.5); 26 | } 27 | 28 | .spacer { 29 | color: rgba(255, 255, 255, 0.3); 30 | } 31 | 32 | .text, 33 | .link { 34 | padding: 2px 5px; 35 | font-size: 1em; 36 | } 37 | 38 | .link, 39 | .link:active, 40 | .link:visited { 41 | color: rgba(255, 255, 255, 0.6); 42 | text-decoration: none; 43 | } 44 | 45 | .link:hover { 46 | color: rgba(255, 255, 255, 1); 47 | } 48 | -------------------------------------------------------------------------------- /src/routes/not-found/NotFound.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Starter Kit (https://www.reactstarterkit.com/) 3 | * 4 | * Copyright © 2014-present Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | import React from 'react'; 11 | import PropTypes from 'prop-types'; 12 | import withStyles from 'isomorphic-style-loader/lib/withStyles'; 13 | import s from './NotFound.css'; 14 | 15 | class NotFound extends React.Component { 16 | static propTypes = { 17 | title: PropTypes.string.isRequired, 18 | }; 19 | 20 | render() { 21 | return ( 22 |
23 |
24 |

25 | {this.props.title} 26 |

27 |

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

28 |
29 |
30 | ); 31 | } 32 | } 33 | 34 | export default withStyles(s)(NotFound); 35 | -------------------------------------------------------------------------------- /src/components/Navigation/Navigation.css: -------------------------------------------------------------------------------- 1 | /** 2 | * React Starter Kit (https://www.reactstarterkit.com/) 3 | * 4 | * Copyright © 2014-present Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | .root { 11 | float: right; 12 | margin: 6px 0 0; 13 | } 14 | 15 | .link { 16 | display: inline-block; 17 | padding: 3px 8px; 18 | text-decoration: none; 19 | font-size: 1.125em; /* ~18px */ 20 | } 21 | 22 | .link, 23 | .link:active, 24 | .link:visited { 25 | color: rgba(255, 255, 255, 0.6); 26 | } 27 | 28 | .link:hover { 29 | color: rgba(255, 255, 255, 1); 30 | } 31 | 32 | /* .highlight { 33 | margin-right: 8px; 34 | margin-left: 8px; 35 | border-radius: 3px; 36 | background: rgba(0, 0, 0, 0.15); 37 | color: #fff; 38 | } 39 | 40 | .highlight:hover { 41 | background: rgba(0, 0, 0, 0.3); 42 | } 43 | 44 | .spacer { 45 | color: rgba(255, 255, 255, 0.3); 46 | } */ 47 | -------------------------------------------------------------------------------- /tools/lib/cp.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Starter Kit (https://www.reactstarterkit.com/) 3 | * 4 | * Copyright © 2014-present Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | import cp from 'child_process'; 11 | 12 | export const spawn = (command, args, options) => 13 | new Promise((resolve, reject) => { 14 | cp.spawn(command, args, options).on('close', code => { 15 | if (code === 0) { 16 | resolve(); 17 | } else { 18 | reject(new Error(`${command} ${args.join(' ')} => ${code} (error)`)); 19 | } 20 | }); 21 | }); 22 | 23 | export const exec = (command, options) => 24 | new Promise((resolve, reject) => { 25 | cp.exec(command, options, (err, stdout, stderr) => { 26 | if (err) { 27 | reject(err); 28 | return; 29 | } 30 | 31 | resolve({ stdout, stderr }); 32 | }); 33 | }); 34 | 35 | export default { spawn, exec }; 36 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | ## Documentation 2 | 3 | ### General 4 | 5 | * [Getting Started](./getting-started.md) 6 | * [React Style Guide](./react-style-guide.md) 7 | * [How to configure text editors and IDEs](./how-to-configure-text-editors.md) 8 | * [Data fetching with WHATWG Fetch](./data-fetching.md) 9 | * [Testing your application](./testing-your-application.md) 10 | 11 | ### Questions 12 | 13 | * [Which module bundler should I use?](https://github.com/kriasoft/react-starter-kit/issues/3) 14 | * [Which Flux implementation should I use?](https://github.com/kriasoft/react-starter-kit/issues/22) 15 | 16 | ### Recipes 17 | 18 | * [How to Implement Routing and Navigation](./recipes/how-to-implement-routing.md) 19 | * [How to Integrate Redux](./recipes/how-to-integrate-redux.md) 20 | * [How to Integrate React Intl](./recipes/how-to-integrate-react-intl.md) 21 | * [How to Integrate Disqus](./recipes/how-to-integrate-disqus.md) 22 | * [How to Use Sass/SCSS](./recipes/how-to-use-sass.md) 23 | * [How to Configure Facebook Login](./recipes/how-to-configure-facebook-login.md) 24 | -------------------------------------------------------------------------------- /src/components/variables.css: -------------------------------------------------------------------------------- 1 | /** 2 | * React Starter Kit (https://www.reactstarterkit.com/) 3 | * 4 | * Copyright © 2014-present Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | :root { 11 | /* 12 | * Typography 13 | * ======================================================================== */ 14 | 15 | --font-family-base: 'Segoe UI', 'HelveticaNeue-Light', sans-serif; 16 | 17 | /* 18 | * Layout 19 | * ======================================================================== */ 20 | 21 | --max-content-width: 1000px; 22 | 23 | /* 24 | * Media queries breakpoints 25 | * ======================================================================== */ 26 | 27 | --screen-xs-min: 480px; /* Extra small screen / phone */ 28 | --screen-sm-min: 768px; /* Small screen / tablet */ 29 | --screen-md-min: 992px; /* Medium screen / desktop */ 30 | --screen-lg-min: 1200px; /* Large screen / wide desktop */ 31 | } 32 | -------------------------------------------------------------------------------- /tools/build.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Starter Kit (https://www.reactstarterkit.com/) 3 | * 4 | * Copyright © 2014-present Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | import cp from 'child_process'; 11 | import run from './run'; 12 | import clean from './clean'; 13 | import copy from './copy'; 14 | import bundle from './bundle'; 15 | import render from './render'; 16 | import pkg from '../package.json'; 17 | 18 | /** 19 | * Compiles the project from source files into a distributable 20 | * format and copies it to the output (build) folder. 21 | */ 22 | async function build() { 23 | await run(clean); 24 | await run(copy); 25 | await run(bundle); 26 | 27 | if (process.argv.includes('--static')) { 28 | await run(render); 29 | } 30 | 31 | if (process.argv.includes('--docker')) { 32 | cp.spawnSync('docker', ['build', '-t', pkg.name, '.'], { 33 | stdio: 'inherit', 34 | }); 35 | } 36 | } 37 | 38 | export default build; 39 | -------------------------------------------------------------------------------- /src/routes/home/Home.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Starter Kit (https://www.reactstarterkit.com/) 3 | * 4 | * Copyright © 2014-present Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | import React from 'react'; 11 | // import PropTypes from 'prop-types'; 12 | import withStyles from 'isomorphic-style-loader/lib/withStyles'; 13 | import s from './Home.css'; 14 | 15 | class Home extends React.Component { 16 | // static propTypes = { 17 | // news: PropTypes.arrayOf( 18 | // PropTypes.shape({ 19 | // title: PropTypes.string.isRequired, 20 | // link: PropTypes.string.isRequired, 21 | // content: PropTypes.string, 22 | // }), 23 | // ).isRequired, 24 | // }; 25 | 26 | render() { 27 | return ( 28 |
29 |
30 |

React.js News

31 |
32 |
33 | ); 34 | } 35 | } 36 | 37 | export default withStyles(s)(Home); 38 | -------------------------------------------------------------------------------- /src/components/Layout/Layout.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Starter Kit (https://www.reactstarterkit.com/) 3 | * 4 | * Copyright © 2014-present Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | import React from 'react'; 11 | import PropTypes from 'prop-types'; 12 | import withStyles from 'isomorphic-style-loader/lib/withStyles'; 13 | 14 | // external-global styles must be imported in your JS. 15 | import normalizeCss from 'normalize.css'; 16 | import s from './Layout.css'; 17 | import Header from '../Header'; 18 | import Feedback from '../Feedback'; 19 | import Footer from '../Footer'; 20 | 21 | class Layout extends React.Component { 22 | static propTypes = { 23 | children: PropTypes.node.isRequired, 24 | }; 25 | 26 | render() { 27 | return ( 28 |
29 |
30 | {this.props.children} 31 | 32 |
33 |
34 | ); 35 | } 36 | } 37 | 38 | export default withStyles(normalizeCss, s)(Layout); 39 | -------------------------------------------------------------------------------- /src/data/models/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Starter Kit (https://www.reactstarterkit.com/) 3 | * 4 | * Copyright © 2014-present Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | import sequelize from '../sequelize'; 11 | import User from './User'; 12 | import UserLogin from './UserLogin'; 13 | import UserClaim from './UserClaim'; 14 | import UserProfile from './UserProfile'; 15 | 16 | User.hasMany(UserLogin, { 17 | foreignKey: 'userId', 18 | as: 'logins', 19 | onUpdate: 'cascade', 20 | onDelete: 'cascade', 21 | }); 22 | 23 | User.hasMany(UserClaim, { 24 | foreignKey: 'userId', 25 | as: 'claims', 26 | onUpdate: 'cascade', 27 | onDelete: 'cascade', 28 | }); 29 | 30 | User.hasOne(UserProfile, { 31 | foreignKey: 'userId', 32 | as: 'profile', 33 | onUpdate: 'cascade', 34 | onDelete: 'cascade', 35 | }); 36 | 37 | function sync(...args) { 38 | return sequelize.sync(...args); 39 | } 40 | 41 | export default { sync }; 42 | export { User, UserLogin, UserClaim, UserProfile }; 43 | -------------------------------------------------------------------------------- /src/components/Header/Header.css: -------------------------------------------------------------------------------- 1 | /** 2 | * React Starter Kit (https://www.reactstarterkit.com/) 3 | * 4 | * Copyright © 2014-present Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | @import '../variables.css'; 11 | 12 | :root { 13 | --brand-color: #61dafb; 14 | } 15 | 16 | .root { 17 | background: #373277; 18 | color: #fff; 19 | } 20 | 21 | .container { 22 | margin: 0 auto; 23 | padding: 20px 0; 24 | max-width: var(--max-content-width); 25 | } 26 | 27 | .brand { 28 | color: color(var(--brand-color) lightness(+10%)); 29 | text-decoration: none; 30 | font-size: 1.75em; /* ~28px */ 31 | } 32 | 33 | .brandTxt { 34 | margin-left: 10px; 35 | } 36 | 37 | .banner { 38 | text-align: center; 39 | } 40 | 41 | .bannerTitle { 42 | margin: 0; 43 | padding: 10px; 44 | font-weight: normal; 45 | font-size: 4em; 46 | line-height: 1em; 47 | } 48 | 49 | .bannerDesc { 50 | padding: 0; 51 | color: rgba(255, 255, 255, 0.5); 52 | font-size: 1.25em; 53 | margin: 0; 54 | } 55 | -------------------------------------------------------------------------------- /src/components/Page/Page.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Starter Kit (https://www.reactstarterkit.com/) 3 | * 4 | * Copyright © 2014-present Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | import React from 'react'; 11 | import PropTypes from 'prop-types'; 12 | import withStyles from 'isomorphic-style-loader/lib/withStyles'; 13 | import s from './Page.css'; 14 | 15 | class Page extends React.Component { 16 | static propTypes = { 17 | title: PropTypes.string.isRequired, 18 | html: PropTypes.string.isRequired, 19 | }; 20 | 21 | render() { 22 | const { title, html } = this.props; 23 | return ( 24 |
25 |
26 |

27 | {title} 28 |

29 |
33 |
34 |
35 | ); 36 | } 37 | } 38 | 39 | export default withStyles(s)(Page); 40 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2014-present Konstantin Tarkus, Kriasoft LLC. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/components/Feedback/Feedback.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Starter Kit (https://www.reactstarterkit.com/) 3 | * 4 | * Copyright © 2014-present Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | import React from 'react'; 11 | import withStyles from 'isomorphic-style-loader/lib/withStyles'; 12 | import s from './Feedback.css'; 13 | 14 | class Feedback extends React.Component { 15 | render() { 16 | return ( 17 | 34 | ); 35 | } 36 | } 37 | 38 | export default withStyles(s)(Feedback); 39 | -------------------------------------------------------------------------------- /src/components/Navigation/Navigation.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Starter Kit (https://www.reactstarterkit.com/) 3 | * 4 | * Copyright © 2014-present Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | import React from 'react'; 11 | // import cx from 'classnames'; 12 | import withStyles from 'isomorphic-style-loader/lib/withStyles'; 13 | import s from './Navigation.css'; 14 | import Link from '../Link'; 15 | 16 | class Navigation extends React.Component { 17 | render() { 18 | return ( 19 |
20 | 21 | About 22 | 23 | {/* 24 | Contact 25 | 26 | | 27 | 28 | Log in 29 | 30 | or 31 | 32 | Sign up 33 | */} 34 |
35 | ); 36 | } 37 | } 38 | 39 | export default withStyles(s)(Navigation); 40 | -------------------------------------------------------------------------------- /src/DOMUtils.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Starter Kit (https://www.reactstarterkit.com/) 3 | * 4 | * Copyright © 2014-present Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | export function updateTag(tagName, keyName, keyValue, attrName, attrValue) { 11 | const node = document.head.querySelector( 12 | `${tagName}[${keyName}="${keyValue}"]`, 13 | ); 14 | if (node && node.getAttribute(attrName) === attrValue) return; 15 | 16 | // Remove and create a new tag in order to make it work with bookmarks in Safari 17 | if (node) { 18 | node.parentNode.removeChild(node); 19 | } 20 | if (typeof attrValue === 'string') { 21 | const nextNode = document.createElement(tagName); 22 | nextNode.setAttribute(keyName, keyValue); 23 | nextNode.setAttribute(attrName, attrValue); 24 | document.head.appendChild(nextNode); 25 | } 26 | } 27 | 28 | export function updateMeta(name, content) { 29 | updateTag('meta', 'name', name, 'content', content); 30 | } 31 | 32 | export function updateCustomMeta(property, content) { 33 | updateTag('meta', 'property', property, 'content', content); 34 | } 35 | 36 | export function updateLink(rel, href) { 37 | updateTag('link', 'rel', rel, 'href', href); 38 | } 39 | -------------------------------------------------------------------------------- /src/components/Footer/Footer.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Starter Kit (https://www.reactstarterkit.com/) 3 | * 4 | * Copyright © 2014-present Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | import React from 'react'; 11 | import withStyles from 'isomorphic-style-loader/lib/withStyles'; 12 | import s from './Footer.css'; 13 | import Link from '../Link'; 14 | 15 | class Footer extends React.Component { 16 | render() { 17 | return ( 18 |
19 |
20 | © Big Silver 21 | · 22 | 23 | Home 24 | 25 | {/* · 26 | 27 | Admin 28 | 29 | · 30 | 31 | Privacy 32 | 33 | · 34 | 35 | Not Found 36 | */} 37 |
38 |
39 | ); 40 | } 41 | } 42 | 43 | export default withStyles(s)(Footer); 44 | -------------------------------------------------------------------------------- /src/routes/error/ErrorPage.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Starter Kit (https://www.reactstarterkit.com/) 3 | * 4 | * Copyright © 2014-present Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | import React from 'react'; 11 | import PropTypes from 'prop-types'; 12 | import withStyles from 'isomorphic-style-loader/lib/withStyles'; 13 | import s from './ErrorPage.css'; 14 | 15 | class ErrorPage extends React.Component { 16 | static propTypes = { 17 | error: PropTypes.shape({ 18 | name: PropTypes.string.isRequired, 19 | message: PropTypes.string.isRequired, 20 | stack: PropTypes.string.isRequired, 21 | }), 22 | }; 23 | 24 | static defaultProps = { 25 | error: null, 26 | }; 27 | 28 | render() { 29 | if (__DEV__ && this.props.error) { 30 | return ( 31 |
32 |

33 | {this.props.error.name} 34 |

35 |
36 |             {this.props.error.stack}
37 |           
38 |
39 | ); 40 | } 41 | 42 | return ( 43 |
44 |

Error

45 |

Sorry, a critical error occurred on this page.

46 |
47 | ); 48 | } 49 | } 50 | 51 | export { ErrorPage as ErrorPageWithoutStyle }; 52 | export default withStyles(s)(ErrorPage); 53 | -------------------------------------------------------------------------------- /src/components/Header/Header.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Starter Kit (https://www.reactstarterkit.com/) 3 | * 4 | * Copyright © 2014-present Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | import React from 'react'; 11 | import withStyles from 'isomorphic-style-loader/lib/withStyles'; 12 | import s from './Header.css'; 13 | import Link from '../Link'; 14 | import Navigation from '../Navigation'; 15 | import logoUrl from './logo-small.png'; 16 | import logoUrl2x from './logo-small@2x.png'; 17 | 18 | class Header extends React.Component { 19 | render() { 20 | return ( 21 |
22 |
23 | 24 | 25 | React 32 | Big Silver 33 | 34 |
35 |

JS Animation

36 |

web animation.js

37 |
38 |
39 |
40 | ); 41 | } 42 | } 43 | 44 | export default withStyles(s)(Header); 45 | -------------------------------------------------------------------------------- /tools/run.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Starter Kit (https://www.reactstarterkit.com/) 3 | * 4 | * Copyright © 2014-present Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | export function format(time) { 11 | return time.toTimeString().replace(/.*(\d{2}:\d{2}:\d{2}).*/, '$1'); 12 | } 13 | 14 | function run(fn, options) { 15 | const task = typeof fn.default === 'undefined' ? fn : fn.default; 16 | const start = new Date(); 17 | console.info( 18 | `[${format(start)}] Starting '${task.name}${options 19 | ? ` (${options})` 20 | : ''}'...`, 21 | ); 22 | return task(options).then(resolution => { 23 | const end = new Date(); 24 | const time = end.getTime() - start.getTime(); 25 | console.info( 26 | `[${format(end)}] Finished '${task.name}${options 27 | ? ` (${options})` 28 | : ''}' after ${time} ms`, 29 | ); 30 | return resolution; 31 | }); 32 | } 33 | 34 | if (require.main === module && process.argv.length > 2) { 35 | // eslint-disable-next-line no-underscore-dangle 36 | delete require.cache[__filename]; 37 | 38 | // eslint-disable-next-line global-require, import/no-dynamic-require 39 | const module = require(`./${process.argv[2]}.js`).default; 40 | 41 | run(module).catch(err => { 42 | console.error(err.stack); 43 | process.exit(1); 44 | }); 45 | } 46 | 47 | export default run; 48 | -------------------------------------------------------------------------------- /src/components/Link/Link.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Starter Kit (https://www.reactstarterkit.com/) 3 | * 4 | * Copyright © 2014-present Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | import React from 'react'; 11 | import PropTypes from 'prop-types'; 12 | import history from '../../history'; 13 | 14 | function isLeftClickEvent(event) { 15 | return event.button === 0; 16 | } 17 | 18 | function isModifiedEvent(event) { 19 | return !!(event.metaKey || event.altKey || event.ctrlKey || event.shiftKey); 20 | } 21 | 22 | class Link extends React.Component { 23 | static propTypes = { 24 | to: PropTypes.string.isRequired, 25 | children: PropTypes.node.isRequired, 26 | onClick: PropTypes.func, 27 | }; 28 | 29 | static defaultProps = { 30 | onClick: null, 31 | }; 32 | 33 | handleClick = event => { 34 | if (this.props.onClick) { 35 | this.props.onClick(event); 36 | } 37 | 38 | if (isModifiedEvent(event) || !isLeftClickEvent(event)) { 39 | return; 40 | } 41 | 42 | if (event.defaultPrevented === true) { 43 | return; 44 | } 45 | 46 | event.preventDefault(); 47 | history.push(this.props.to); 48 | }; 49 | 50 | render() { 51 | const { to, children, ...props } = this.props; 52 | return ( 53 | 54 | {children} 55 | 56 | ); 57 | } 58 | } 59 | 60 | export default Link; 61 | -------------------------------------------------------------------------------- /src/data/queries/news.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Starter Kit (https://www.reactstarterkit.com/) 3 | * 4 | * Copyright © 2014-present Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | import { GraphQLList as List } from 'graphql'; 11 | import fetch from 'isomorphic-fetch'; 12 | import NewsItemType from '../types/NewsItemType'; 13 | 14 | // React.js News Feed (RSS) 15 | const url = 16 | 'https://api.rss2json.com/v1/api.json' + 17 | '?rss_url=https%3A%2F%2Freactjsnews.com%2Ffeed.xml'; 18 | 19 | let items = []; 20 | let lastFetchTask; 21 | let lastFetchTime = new Date(1970, 0, 1); 22 | 23 | const news = { 24 | type: new List(NewsItemType), 25 | resolve() { 26 | if (lastFetchTask) { 27 | return lastFetchTask; 28 | } 29 | 30 | if (new Date() - lastFetchTime > 1000 * 60 * 10 /* 10 mins */) { 31 | lastFetchTime = new Date(); 32 | lastFetchTask = fetch(url) 33 | .then(response => response.json()) 34 | .then(data => { 35 | if (data.status === 'ok') { 36 | items = data.items; 37 | } 38 | 39 | lastFetchTask = null; 40 | return items; 41 | }) 42 | .catch(err => { 43 | lastFetchTask = null; 44 | throw err; 45 | }); 46 | 47 | if (items.length) { 48 | return items; 49 | } 50 | 51 | return lastFetchTask; 52 | } 53 | 54 | return items; 55 | }, 56 | }; 57 | 58 | export default news; 59 | -------------------------------------------------------------------------------- /src/createFetch.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Starter Kit (https://www.reactstarterkit.com/) 3 | * 4 | * Copyright © 2014-present Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | /* @flow */ 11 | 12 | type Fetch = (url: string, options: ?any) => Promise; 13 | 14 | type Options = { 15 | baseUrl: string, 16 | cookie?: string, 17 | }; 18 | 19 | /** 20 | * Creates a wrapper function around the HTML5 Fetch API that provides 21 | * default arguments to fetch(...) and is intended to reduce the amount 22 | * of boilerplate code in the application. 23 | * https://developer.mozilla.org/docs/Web/API/Fetch_API/Using_Fetch 24 | */ 25 | function createFetch(fetch: Fetch, { baseUrl, cookie }: Options) { 26 | // NOTE: Tweak the default options to suite your application needs 27 | const defaults = { 28 | method: 'POST', // handy with GraphQL backends 29 | mode: baseUrl ? 'cors' : 'same-origin', 30 | credentials: baseUrl ? 'include' : 'same-origin', 31 | headers: { 32 | Accept: 'application/json', 33 | 'Content-Type': 'application/json', 34 | ...(cookie ? { Cookie: cookie } : null), 35 | }, 36 | }; 37 | 38 | return (url: string, options: any) => 39 | url.startsWith('/graphql') || url.startsWith('/api') 40 | ? fetch(`${baseUrl}${url}`, { 41 | ...defaults, 42 | ...options, 43 | headers: { 44 | ...defaults.headers, 45 | ...(options && options.headers), 46 | }, 47 | }) 48 | : fetch(url, options); 49 | } 50 | 51 | export default createFetch; 52 | -------------------------------------------------------------------------------- /src/components/App.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Starter Kit (https://www.reactstarterkit.com/) 3 | * 4 | * Copyright © 2014-present Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | import React from 'react'; 11 | import PropTypes from 'prop-types'; 12 | 13 | const ContextType = { 14 | // Enables critical path CSS rendering 15 | // https://github.com/kriasoft/isomorphic-style-loader 16 | insertCss: PropTypes.func.isRequired, 17 | // Universal HTTP client 18 | fetch: PropTypes.func.isRequired, 19 | }; 20 | 21 | /** 22 | * The top-level React component setting context (global) variables 23 | * that can be accessed from all the child components. 24 | * 25 | * https://facebook.github.io/react/docs/context.html 26 | * 27 | * Usage example: 28 | * 29 | * const context = { 30 | * history: createBrowserHistory(), 31 | * store: createStore(), 32 | * }; 33 | * 34 | * ReactDOM.render( 35 | * 36 | * 37 | * 38 | * 39 | * , 40 | * container, 41 | * ); 42 | */ 43 | class App extends React.PureComponent { 44 | static propTypes = { 45 | context: PropTypes.shape(ContextType).isRequired, 46 | children: PropTypes.element.isRequired, 47 | }; 48 | 49 | static childContextTypes = ContextType; 50 | 51 | getChildContext() { 52 | return this.props.context; 53 | } 54 | 55 | render() { 56 | // NOTE: If you need to add or modify header, footer etc. of the app, 57 | // please do that inside the Layout component. 58 | return React.Children.only(this.props.children); 59 | } 60 | } 61 | 62 | export default App; 63 | -------------------------------------------------------------------------------- /.stylelintrc.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Starter Kit (https://www.reactstarterkit.com/) 3 | * 4 | * Copyright © 2014-present Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | // stylelint configuration 11 | // https://stylelint.io/user-guide/configuration/ 12 | module.exports = { 13 | // The standard config based on a handful of CSS style guides 14 | // https://github.com/stylelint/stylelint-config-standard 15 | extends: 'stylelint-config-standard', 16 | 17 | plugins: [ 18 | // stylelint plugin to sort CSS rules content with specified order 19 | // https://github.com/hudochenkov/stylelint-order 20 | 'stylelint-order', 21 | ], 22 | 23 | rules: { 24 | 'property-no-unknown': [ 25 | true, 26 | { 27 | ignoreProperties: [ 28 | // CSS Modules composition 29 | // https://github.com/css-modules/css-modules#composition 30 | 'composes', 31 | ], 32 | }, 33 | ], 34 | 35 | 'selector-pseudo-class-no-unknown': [ 36 | true, 37 | { 38 | ignorePseudoClasses: [ 39 | // CSS Modules :global scope 40 | // https://github.com/css-modules/css-modules#exceptions 41 | 'global', 42 | ], 43 | }, 44 | ], 45 | 46 | // Opinionated rule, you can disable it if you want 47 | 'string-quotes': 'single', 48 | 49 | // https://github.com/hudochenkov/stylelint-order/blob/master/rules/order/README.md 50 | 'order/order': [ 51 | 'custom-properties', 52 | 'dollar-variables', 53 | 'declarations', 54 | 'at-rules', 55 | 'rules', 56 | ], 57 | 58 | // https://github.com/hudochenkov/stylelint-order/blob/master/rules/properties-order/README.md 59 | 'order/properties-order': [], 60 | }, 61 | }; 62 | -------------------------------------------------------------------------------- /src/config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Starter Kit (https://www.reactstarterkit.com/) 3 | * 4 | * Copyright © 2014-present Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | /* eslint-disable max-len */ 11 | 12 | if (process.env.BROWSER) { 13 | throw new Error( 14 | 'Do not import `config.js` from inside the client-side code.', 15 | ); 16 | } 17 | 18 | module.exports = { 19 | // Node.js app 20 | port: process.env.PORT || 3000, 21 | 22 | // API Gateway 23 | api: { 24 | // API URL to be used in the client-side code 25 | clientUrl: process.env.API_CLIENT_URL || '', 26 | // API URL to be used in the server-side code 27 | serverUrl: 28 | process.env.API_SERVER_URL || 29 | `http://localhost:${process.env.PORT || 3000}`, 30 | }, 31 | 32 | // Database 33 | databaseUrl: process.env.DATABASE_URL || 'sqlite:database.sqlite', 34 | 35 | // Web analytics 36 | analytics: { 37 | // https://analytics.google.com/ 38 | googleTrackingId: process.env.GOOGLE_TRACKING_ID, // UA-XXXXX-X 39 | }, 40 | 41 | // Authentication 42 | auth: { 43 | jwt: { secret: process.env.JWT_SECRET || 'React Starter Kit' }, 44 | 45 | // https://developers.facebook.com/ 46 | facebook: { 47 | id: process.env.FACEBOOK_APP_ID || '186244551745631', 48 | secret: 49 | process.env.FACEBOOK_APP_SECRET || 'a970ae3240ab4b9b8aae0f9f0661c6fc', 50 | }, 51 | 52 | // https://cloud.google.com/console/project 53 | google: { 54 | id: 55 | process.env.GOOGLE_CLIENT_ID || 56 | '251410730550-ahcg0ou5mgfhl8hlui1urru7jn5s12km.apps.googleusercontent.com', 57 | secret: process.env.GOOGLE_CLIENT_SECRET || 'Y8yR9yZAhm9jQ8FKAL8QIEcd', 58 | }, 59 | 60 | // https://apps.twitter.com/ 61 | twitter: { 62 | key: process.env.TWITTER_CONSUMER_KEY || 'Ie20AZvLJI2lQD5Dsgxgjauns', 63 | secret: 64 | process.env.TWITTER_CONSUMER_SECRET || 65 | 'KTZ6cxoKnEakQCeSpZlaUCJWGAlTEBJj0y2EMkUBujA7zWSvaQ', 66 | }, 67 | }, 68 | }; 69 | -------------------------------------------------------------------------------- /src/routes/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Starter Kit (https://www.reactstarterkit.com/) 3 | * 4 | * Copyright © 2014-present Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | /* eslint-disable global-require */ 11 | 12 | // The top-level (parent) route 13 | const routes = { 14 | path: '/', 15 | 16 | // Keep in mind, routes are evaluated in order 17 | children: [ 18 | { 19 | path: '/', 20 | load: () => import(/* webpackChunkName: 'home' */ './home'), 21 | }, 22 | { 23 | path: '/contact', 24 | load: () => import(/* webpackChunkName: 'contact' */ './contact'), 25 | }, 26 | { 27 | path: '/login', 28 | load: () => import(/* webpackChunkName: 'login' */ './login'), 29 | }, 30 | { 31 | path: '/register', 32 | load: () => import(/* webpackChunkName: 'register' */ './register'), 33 | }, 34 | { 35 | path: '/about', 36 | load: () => import(/* webpackChunkName: 'about' */ './about'), 37 | }, 38 | { 39 | path: '/privacy', 40 | load: () => import(/* webpackChunkName: 'privacy' */ './privacy'), 41 | }, 42 | { 43 | path: '/admin', 44 | load: () => import(/* webpackChunkName: 'admin' */ './admin'), 45 | }, 46 | 47 | // Wildcard routes, e.g. { path: '*', ... } (must go last) 48 | { 49 | path: '*', 50 | load: () => import(/* webpackChunkName: 'not-found' */ './not-found'), 51 | }, 52 | ], 53 | 54 | async action({ next }) { 55 | // Execute each child route until one of them return the result 56 | const route = await next(); 57 | 58 | // Provide default values for title, description etc. 59 | route.title = `${route.title || 'Untitled Page'} - www.reactstarterkit.com`; 60 | route.description = route.description || ''; 61 | 62 | return route; 63 | }, 64 | }; 65 | 66 | // The error page is available by permanent url for development mode 67 | if (__DEV__) { 68 | routes.children.unshift({ 69 | path: '/error', 70 | action: require('./error').default, 71 | }); 72 | } 73 | 74 | export default routes; 75 | -------------------------------------------------------------------------------- /tools/README.md: -------------------------------------------------------------------------------- 1 | # Build Automation Tools 2 | 3 | ### `yarn start` (`start.js`) 4 | 5 | * Cleans up the output `/build` directory (`clean.js`) 6 | * Copies static files to the output folder (`copy.js`) 7 | * Launches [Webpack](https://webpack.github.io/) compiler in a watch mode (via [webpack-middleware](https://github.com/kriasoft/webpack-middleware)) 8 | * Launches Node.js server from the compiled output folder (`runServer.js`) 9 | * Launches [Browsersync](https://browsersync.io/), 10 | [Hot Module Replacement](https://webpack.github.io/docs/hot-module-replacement), and 11 | [React Hot Loader](https://github.com/gaearon/react-hot-loader) 12 | 13 | ### `yarn run build` (`build.js`) 14 | 15 | * Cleans up the output `/build` folder (`clean.js`) 16 | * Copies static files to the output folder (`copy.js`) 17 | * Creates application bundles with Webpack (`bundle.js`, `webpack.config.js`) 18 | 19 | ### `yarn run deploy` (`deploy.js`) 20 | 21 | * Builds the project from source files (`build.js`) 22 | * Pushes the contents of the `/build` folder to a remote server with Git 23 | 24 | ## Options 25 | 26 | Flag | Description 27 | ----------- | -------------------------------------------------- 28 | `--release` | Minimizes and optimizes the compiled output 29 | `--verbose` | Prints detailed information to the console 30 | `--analyze` | Launches [Webpack Bundle Analyzer](https://github.com/th0r/webpack-bundle-analyzer) 31 | `--static` | Renders [specified routes](./render.js#L15) as static html files 32 | `--docker` | Build an image from a Dockerfile 33 | `--silent` | Do not open the default browser 34 | 35 | For example: 36 | 37 | ```sh 38 | $ yarn run build -- --release --verbose # Build the app in production mode 39 | ``` 40 | 41 | or 42 | 43 | ```sh 44 | $ yarn start -- --release # Launch dev server in production mode 45 | ``` 46 | 47 | ## Misc 48 | 49 | * `webpack.config.js` - Webpack configuration for both client-side and server-side bundles 50 | * `postcss.config.js` - PostCSS configuration for transforming styles with JS plugins 51 | * `run.js` - Helps to launch other scripts with `babel-node` (e.g. `babel-node tools/run build`) 52 | * `.eslintrc` - ESLint overrides for built automation scripts 53 | -------------------------------------------------------------------------------- /docs/recipes/how-to-integrate-disqus.md: -------------------------------------------------------------------------------- 1 | ## How to Integrate [Disqus](https://disqus.com) 2 | 3 | https://disqus.com/admin/create/ 4 | 5 | #### `DisqusThread.js` 6 | 7 | ```js 8 | import React from 'react'; 9 | import PropTypes from 'prop-types'; 10 | 11 | const SHORTNAME = 'example'; 12 | const WEBSITE_URL = 'http://www.example.com'; 13 | 14 | function renderDisqus() { 15 | if (window.DISQUS === undefined) { 16 | var script = document.createElement('script'); 17 | script.async = true; 18 | script.src = 'https://' + SHORTNAME + '.disqus.com/embed.js'; 19 | document.getElementsByTagName('head')[0].appendChild(script); 20 | } else { 21 | window.DISQUS.reset({reload: true}); 22 | } 23 | } 24 | 25 | class DisqusThread extends React.Component{ 26 | 27 | static propTypes = { 28 | id: PropTypes.string.isRequired, 29 | title: PropTypes.string.isRequired, 30 | path: PropTypes.string.isRequired 31 | }; 32 | 33 | shouldComponentUpdate(nextProps) { 34 | return this.props.id !== nextProps.id || 35 | this.props.title !== nextProps.title || 36 | this.props.path !== nextProps.path; 37 | } 38 | 39 | componentDidMount() { 40 | renderDisqus(); 41 | } 42 | 43 | componentDidUpdate() { 44 | renderDisqus(); 45 | } 46 | 47 | render() { 48 | let { id, title, path, ...other} = this.props; 49 | 50 | if (process.env.BROWSER) { 51 | window.disqus_shortname = SHORTNAME; 52 | window.disqus_identifier = id; 53 | window.disqus_title = title; 54 | window.disqus_url = WEBSITE_URL + path; 55 | } 56 | 57 | return
; 58 | } 59 | 60 | } 61 | 62 | export default DisqusThread; 63 | ``` 64 | 65 | #### `MyComponent.js` 66 | 67 | ```js 68 | import React from 'react'; 69 | import DisqusThread from './DisqusThread.js'; 70 | 71 | class MyComponent extends React.Component{ 72 | 73 | render() { 74 | return ( 75 |
76 | 79 |
80 | ); 81 | } 82 | 83 | } 84 | 85 | export default MyComponent; 86 | ``` 87 | -------------------------------------------------------------------------------- /tools/copy.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Starter Kit (https://www.reactstarterkit.com/) 3 | * 4 | * Copyright © 2014-present Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | import path from 'path'; 11 | import chokidar from 'chokidar'; 12 | import { writeFile, copyFile, makeDir, copyDir, cleanDir } from './lib/fs'; 13 | import pkg from '../package.json'; 14 | import { format } from './run'; 15 | 16 | /** 17 | * Copies static files such as robots.txt, favicon.ico to the 18 | * output (build) folder. 19 | */ 20 | async function copy() { 21 | await makeDir('build'); 22 | await Promise.all([ 23 | writeFile( 24 | 'build/package.json', 25 | JSON.stringify( 26 | { 27 | private: true, 28 | engines: pkg.engines, 29 | dependencies: pkg.dependencies, 30 | scripts: { 31 | start: 'node server.js', 32 | }, 33 | }, 34 | null, 35 | 2, 36 | ), 37 | ), 38 | copyFile('LICENSE.txt', 'build/LICENSE.txt'), 39 | copyFile('yarn.lock', 'build/yarn.lock'), 40 | copyDir('public', 'build/public'), 41 | ]); 42 | 43 | if (process.argv.includes('--watch')) { 44 | const watcher = chokidar.watch(['public/**/*'], { ignoreInitial: true }); 45 | 46 | watcher.on('all', async (event, filePath) => { 47 | const start = new Date(); 48 | const src = path.relative('./', filePath); 49 | const dist = path.join( 50 | 'build/', 51 | src.startsWith('src') ? path.relative('src', src) : src, 52 | ); 53 | switch (event) { 54 | case 'add': 55 | case 'change': 56 | await makeDir(path.dirname(dist)); 57 | await copyFile(filePath, dist); 58 | break; 59 | case 'unlink': 60 | case 'unlinkDir': 61 | cleanDir(dist, { nosort: true, dot: true }); 62 | break; 63 | default: 64 | return; 65 | } 66 | const end = new Date(); 67 | const time = end.getTime() - start.getTime(); 68 | console.info(`[${format(end)}] ${event} '${dist}' after ${time} ms`); 69 | }); 70 | } 71 | } 72 | 73 | export default copy; 74 | -------------------------------------------------------------------------------- /tools/runServer.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Starter Kit (https://www.reactstarterkit.com/) 3 | * 4 | * Copyright © 2014-present Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | import path from 'path'; 11 | import cp from 'child_process'; 12 | import webpackConfig from './webpack.config'; 13 | 14 | // Should match the text string used in `src/server.js/server.listen(...)` 15 | const RUNNING_REGEXP = /The server is running at http:\/\/(.*?)\//; 16 | 17 | let server; 18 | let pending = true; 19 | const [, serverConfig] = webpackConfig; 20 | const serverPath = path.join( 21 | serverConfig.output.path, 22 | serverConfig.output.filename.replace('[name]', 'server'), 23 | ); 24 | 25 | // Launch or restart the Node.js server 26 | function runServer() { 27 | return new Promise(resolve => { 28 | function onStdOut(data) { 29 | const time = new Date().toTimeString(); 30 | const match = data.toString('utf8').match(RUNNING_REGEXP); 31 | 32 | process.stdout.write(time.replace(/.*(\d{2}:\d{2}:\d{2}).*/, '[$1] ')); 33 | process.stdout.write(data); 34 | 35 | if (match) { 36 | server.host = match[1]; 37 | server.stdout.removeListener('data', onStdOut); 38 | server.stdout.on('data', x => process.stdout.write(x)); 39 | pending = false; 40 | resolve(server); 41 | } 42 | } 43 | 44 | if (server) { 45 | server.kill('SIGTERM'); 46 | } 47 | 48 | server = cp.spawn('node', [serverPath], { 49 | env: Object.assign({ NODE_ENV: 'development' }, process.env), 50 | silent: false, 51 | }); 52 | 53 | if (pending) { 54 | server.once('exit', (code, signal) => { 55 | if (pending) { 56 | throw new Error( 57 | `Server terminated unexpectedly with code: ${code} signal: ${signal}`, 58 | ); 59 | } 60 | }); 61 | } 62 | 63 | server.stdout.on('data', onStdOut); 64 | server.stderr.on('data', x => process.stderr.write(x)); 65 | 66 | return server; 67 | }); 68 | } 69 | 70 | process.on('exit', () => { 71 | if (server) { 72 | server.kill('SIGTERM'); 73 | } 74 | }); 75 | 76 | export default runServer; 77 | -------------------------------------------------------------------------------- /docs/recipes/how-to-use-sass.md: -------------------------------------------------------------------------------- 1 | ## How to Use Sass/SCSS 2 | 3 | > **Note**: Using plain CSS via [PostCSS](http://postcss.org/) is recommended approach because it 4 | reduces the size of the tech stack used in the project, enforces you to learn vanilla CSS syntax 5 | with modern CSS Level 3+ features that allow you doing everything you would normally do with 6 | Sass/SCSS. Also compilation of plain `.css` files should work faster with `postcss` pre-processor 7 | than `node-sass`. 8 | 9 | ### Step 1 10 | 11 | Install [`node-sass`](https://github.com/sass/node-sass) 12 | (includes [node-gyp](https://github.com/nodejs/node-gyp#readme) 13 | and [prerequisites](https://github.com/nodejs/node-gyp#installation)) and 14 | [`sass-loader`](https://github.com/jtangelder/sass-loader) modules as dev dependencies: 15 | 16 | ```sh 17 | $ yarn add node-sass --dev 18 | $ yarn add sass-loader --dev 19 | ``` 20 | 21 | ### Step 2 22 | 23 | Update [`webpack.config.js`](../../tools/webpack.config.js) file to use `sass-loader` for `.scss` files: 24 | 25 | ```js 26 | const config = { 27 | ... 28 | module: { 29 | rules: [ 30 | ... 31 | { 32 | test: /\.scss$/, 33 | use: [ 34 | { 35 | loader: 'isomorphic-style-loader', 36 | }, 37 | { 38 | loader: 'css-loader', 39 | options: { 40 | sourceMap: isDebug, 41 | minimize: !isDebug, 42 | }, 43 | }, 44 | { 45 | loader: 'postcss-loader', 46 | options: { 47 | config: { 48 | path: './tools/postcss.sass.js', 49 | }, 50 | }, 51 | }, 52 | { 53 | loader: 'sass-loader', 54 | }, 55 | ], 56 | }, 57 | ... 58 | ] 59 | } 60 | ... 61 | } 62 | ``` 63 | 64 | ### Step 3 65 | 66 | Add one more configuration (`tools/postcss.sass.js`) for [PostCSS](https://github.com/postcss/postcss) to 67 | enable [Autoprefixer](https://github.com/postcss/autoprefixer) for your `.scss` files: 68 | 69 | ```js 70 | module.exports = () => ({ 71 | plugins: [ 72 | require('autoprefixer')(), 73 | ], 74 | }); 75 | ``` 76 | 77 | For more information visit https://github.com/jtangelder/sass-loader and https://github.com/sass/node-sass 78 | -------------------------------------------------------------------------------- /tools/render.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Starter Kit (https://www.reactstarterkit.com/) 3 | * 4 | * Copyright © 2014-present Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | import path from 'path'; 11 | import fetch from 'node-fetch'; 12 | import { writeFile, makeDir } from './lib/fs'; 13 | import runServer from './runServer'; 14 | 15 | // Enter your paths here which you want to render as static 16 | // Example: 17 | // const routes = [ 18 | // '/', // => build/public/index.html 19 | // '/page', // => build/public/page.html 20 | // '/page/', // => build/public/page/index.html 21 | // '/page/name', // => build/public/page/name.html 22 | // '/page/name/', // => build/public/page/name/index.html 23 | // ]; 24 | const routes = [ 25 | '/', 26 | '/contact', 27 | '/login', 28 | '/register', 29 | '/about', 30 | '/privacy', 31 | '/404', // https://help.github.com/articles/creating-a-custom-404-page-for-your-github-pages-site/ 32 | ]; 33 | 34 | async function render() { 35 | const server = await runServer(); 36 | 37 | // add dynamic routes 38 | // const products = await fetch(`http://${server.host}/api/products`).then(res => res.json()); 39 | // products.forEach(product => routes.push( 40 | // `/product/${product.uri}`, 41 | // `/product/${product.uri}/specs` 42 | // )); 43 | 44 | await Promise.all( 45 | routes.map(async (route, index) => { 46 | const url = `http://${server.host}${route}`; 47 | const fileName = route.endsWith('/') 48 | ? 'index.html' 49 | : `${path.basename(route, '.html')}.html`; 50 | const dirName = path.join( 51 | 'build/public', 52 | route.endsWith('/') ? route : path.dirname(route), 53 | ); 54 | const dist = path.join(dirName, fileName); 55 | const timeStart = new Date(); 56 | const response = await fetch(url); 57 | const timeEnd = new Date(); 58 | const text = await response.text(); 59 | await makeDir(dirName); 60 | await writeFile(dist, text); 61 | const time = timeEnd.getTime() - timeStart.getTime(); 62 | console.info( 63 | `#${index + 64 | 1} ${dist} => ${response.status} ${response.statusText} (${time} ms)`, 65 | ); 66 | }), 67 | ); 68 | 69 | server.kill('SIGTERM'); 70 | } 71 | 72 | export default render; 73 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Starter Kit (https://www.reactstarterkit.com/) 3 | * 4 | * Copyright © 2014-present Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | // ESLint configuration 11 | // http://eslint.org/docs/user-guide/configuring 12 | module.exports = { 13 | parser: 'babel-eslint', 14 | 15 | extends: [ 16 | 'airbnb', 17 | 'plugin:flowtype/recommended', 18 | 'plugin:css-modules/recommended', 19 | 'prettier', 20 | 'prettier/flowtype', 21 | 'prettier/react', 22 | ], 23 | 24 | plugins: ['flowtype', 'css-modules', 'prettier'], 25 | 26 | globals: { 27 | __DEV__: true, 28 | }, 29 | 30 | env: { 31 | browser: true, 32 | }, 33 | 34 | rules: { 35 | // `js` and `jsx` are common extensions 36 | // `mjs` is for `universal-router` only, for now 37 | 'import/extensions': [ 38 | 'error', 39 | 'always', 40 | { 41 | js: 'never', 42 | jsx: 'never', 43 | mjs: 'never', 44 | }, 45 | ], 46 | 47 | // Not supporting nested package.json yet 48 | // https://github.com/benmosher/eslint-plugin-import/issues/458 49 | 'import/no-extraneous-dependencies': 'off', 50 | 51 | // Recommend not to leave any console.log in your code 52 | // Use console.error, console.warn and console.info instead 53 | 'no-console': [ 54 | 'error', 55 | { 56 | allow: ['warn', 'error', 'info'], 57 | }, 58 | ], 59 | 60 | // Allow js files to use jsx syntax, too 61 | 'react/jsx-filename-extension': ['error', { extensions: ['.js', '.jsx'] }], 62 | 63 | // Automatically convert pure class to function by 64 | // babel-plugin-transform-react-pure-class-to-function 65 | // https://github.com/kriasoft/react-starter-kit/pull/961 66 | 'react/prefer-stateless-function': 'off', 67 | 68 | // ESLint plugin for prettier formatting 69 | // https://github.com/prettier/eslint-plugin-prettier 70 | 'prettier/prettier': [ 71 | 'error', 72 | { 73 | // https://github.com/prettier/prettier#options 74 | singleQuote: true, 75 | trailingComma: 'all', 76 | }, 77 | ], 78 | }, 79 | 80 | settings: { 81 | // Allow absolute paths in imports, e.g. import Button from 'components/Button' 82 | // https://github.com/benmosher/eslint-plugin-import/tree/master/resolvers 83 | 'import/resolver': { 84 | node: { 85 | moduleDirectory: ['node_modules', 'src'], 86 | }, 87 | }, 88 | }, 89 | }; 90 | -------------------------------------------------------------------------------- /docs/data-fetching.md: -------------------------------------------------------------------------------- 1 | ## Data Fetching 2 | 3 | At a bare minimum you may want to use [HTML5 Fetch API][fetch] as an HTTP client utility for 4 | making Ajax request to the [data API server][nodeapi]. This API is supported natively in all the 5 | major browsers except for IE (note, that Edge browser does support Fetch). 6 | 7 | **React Starter Kit** is pre-configured with [`whatwg-fetch`][wfetch] polyfill for the browser 8 | environment and [`node-fetch`][nfetch] module for the server-side environment (see 9 | [`src/createFetch.js`](../src/createFetch.js)), allowing you to use the `fetch(url, options)` 10 | method universally in both the client-side and server-side code bases. 11 | 12 | In order to avoid the amount of boilerplate code needed when using the raw `fetch(..)` 13 | function, a simple wrapper was created that provides a base URL of the data API server, credentials 14 | (cookies), CORS etc. For example, in a browser environment the base URL of the data API server 15 | might be an empty string, so when you make an Ajax request to the `/graphql` endpoint it's being 16 | sent to the same origin, and when the same code is executed on the server, during server-side 17 | rendering, it fetches data from the `http://api:8080/graphql` endpoint (`node-fetch` doesn't 18 | support relative URLs for obvious reasons). 19 | 20 | Because of these subtle differences of how the `fetch` method works internally, it makes total 21 | sense to pass it as a `context` variable to your React application, so it can be used from either 22 | routing level or from inside your React components as follows: 23 | 24 | #### Route Example 25 | 26 | ```js 27 | { 28 | path: '/posts/:id', 29 | async action({ params, fetch }) { 30 | const resp = await fetch(`/api/posts/${params.id}`, { method: 'GET' }); 31 | const data = await resp.json(); 32 | return { title: data.title, component: }; 33 | } 34 | } 35 | ``` 36 | 37 | #### React Component 38 | 39 | ```js 40 | class Post extends React.Component { 41 | static contextTypes = { fetch: PropTypes.func.isRequired }; 42 | handleDelete = (event) => { 43 | event.preventDefault(); 44 | const id = event.target.dataset['id']; 45 | this.context.fetch(`/api/posts/${id}`, { method: 'DELETE' }).then(...); 46 | }; 47 | render() { ... } 48 | } 49 | ``` 50 | 51 | #### Related articles 52 | 53 | * [That's so fetch!](https://jakearchibald.com/2015/thats-so-fetch/) by [Jake Archibald](https://twitter.com/jaffathecake) 54 | 55 | 56 | [fetch]: https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch 57 | [wfetch]: https://github.com/github/fetchno 58 | [nfetch]: https://github.com/bitinn/node-fetch 59 | [nodeapi]: https://github.com/kriasoft/nodejs-api-starter 60 | 61 | -------------------------------------------------------------------------------- /src/routes/privacy/privacy.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Privacy Policy 3 | --- 4 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean consequat 5 | tortor fermentum mi fermentum dignissim. Nullam vel ipsum ut ligula elementum 6 | lobortis. Maecenas aliquam, massa laoreet lacinia pretium, nisi urna venenatis 7 | tortor, nec imperdiet tellus libero efficitur metus. Fusce semper posuere 8 | ligula, et facilisis metus bibendum interdum. Mauris at mauris sit amet sem 9 | pharetra commodo a eu leo. Nam at est non risus cursus maximus. Nam feugiat 10 | augue libero, id consectetur tortor bibendum non. Quisque nec fringilla lorem. 11 | Nullam efficitur vulputate mauris, nec maximus leo dignissim id. 12 | 13 | In hac habitasse platea dictumst. Duis sagittis dui ac ex suscipit maximus. 14 | Morbi pellentesque venenatis felis sed convallis. Nulla varius, nibh vitae 15 | placerat tempus, mauris sem elementum ipsum, eget sollicitudin nisl est vel 16 | purus. Fusce malesuada odio velit, non cursus leo fermentum id. Cras pharetra 17 | sodales fringilla. Etiam quis est a dolor egestas pellentesque. Maecenas non 18 | scelerisque purus, congue cursus arcu. Donec vel dapibus mi. Mauris maximus 19 | posuere placerat. Sed et libero eu nibh tristique mollis a eget lectus. Donec 20 | interdum augue sollicitudin vehicula hendrerit. Vivamus justo orci, molestie 21 | ac sollicitudin ac, lobortis at tellus. Etiam rhoncus ullamcorper risus eu 22 | tempor. Sed porttitor, neque ac efficitur gravida, arcu lacus pharetra dui, in 23 | consequat elit tellus auctor nulla. Donec placerat elementum diam, vitae 24 | imperdiet lectus luctus at. 25 | 26 | Nullam eu feugiat mi. Quisque nec tristique nisl, dignissim dictum leo. Nam 27 | non quam nisi. Donec rutrum turpis ac diam blandit, id pulvinar mauris 28 | suscipit. Pellentesque tincidunt libero ultricies risus iaculis, sit amet 29 | consequat velit blandit. Fusce quis varius nulla. Nullam nisi nisi, suscipit 30 | ut magna quis, feugiat porta nibh. Sed id enim lectus. Suspendisse elementum 31 | justo sapien, sit amet consequat orci accumsan et. Aliquam ornare ullamcorper 32 | sem sed finibus. Nullam ac lacus pulvinar, egestas felis ut, accumsan est. 33 | 34 | Pellentesque sagittis vehicula sem quis luctus. Proin sodales magna in lorem 35 | hendrerit aliquam. Integer eu varius orci. Vestibulum ante ipsum primis in 36 | faucibus orci luctus et ultrices posuere cubilia Curae; Vestibulum ante ipsum 37 | primis in faucibus orci luctus et ultrices posuere cubilia Curae; Ut at mauris 38 | nibh. Suspendisse maximus ac eros at vestibulum. 39 | 40 | Interdum et malesuada fames ac ante ipsum primis in faucibus. Quisque egestas 41 | tortor et dui consequat faucibus. Nunc vitae odio ornare, venenatis ligula a, 42 | vulputate nisl. Aenean congue varius ex, sit amet bibendum odio posuere at. 43 | Nulla facilisi. In finibus, nulla vitae tincidunt ornare, sapien nulla 44 | fermentum mauris, sed consectetur tortor arcu eget arcu. Vestibulum vel quam 45 | enim. 46 | -------------------------------------------------------------------------------- /docs/recipes/how-to-integrate-redux.md: -------------------------------------------------------------------------------- 1 | ## How to Integrate [Redux](http://redux.js.org/index.html) 2 | 3 | Merge `feature/redux` branch with Git. If you are interested in `feature/react-intl`, merge that 4 | branch instead as it also includes Redux. 5 | 6 | **If you don't know Redux well, you should [read about it first](http://redux.js.org/docs/basics/index.html).** 7 | 8 | 9 | ### Creating Actions 10 | 11 | 1. Go to `src/constants/index.js` and define action name there. 12 | 13 | 2. Go to `src/actions/` and create file with appropriate name. You can copy 14 | `src/actions/runtime.js` as a template. 15 | 16 | 3. If you need async actions, use [`redux-thunk`](https://github.com/gaearon/redux-thunk#readme). 17 | For inspiration on how to create async actions you can look at 18 | [`setLocale`](https://github.com/kriasoft/react-starter-kit/blob/feature/react-intl/src/actions/intl.js) 19 | action from `feature/react-intl`. 20 | See [Async Flow](http://redux.js.org/docs/advanced/AsyncFlow.html) for more information on this 21 | topic. 22 | 23 | 24 | ### Creating Reducer (aka Store) 25 | 26 | 1. Go to [`src/reducers/`](https://github.com/kriasoft/react-starter-kit/tree/feature/redux/src/reducers) and create new file there. 27 | 28 | You can copy [`src/reducers/runtime.js`](https://github.com/kriasoft/react-starter-kit/tree/feature/redux/src/reducers/runtime.js) as a template. 29 | 30 | - Do not forget to always return `state`. 31 | - Never mutate provided `state`. 32 | If you mutate state, rendering of connected component will not be triggered because of `===` equality. 33 | Always return new value if you perform state update. 34 | You can use this construct: `{ ...state, updatedKey: action.payload.value, }` 35 | - Keep in mind that store state *must* be repeatable by replaying actions on it. 36 | For example, when you store timestamp, pass it into *action payload*. 37 | If you call REST API, do it in action. *Never do this in reducer!* 38 | 39 | 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 40 | [`combineReducers`](http://redux.js.org/docs/api/combineReducers.html) 41 | 42 | 43 | ### Connecting Components 44 | 45 | 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. 46 | 47 | See [Usage With React](http://redux.js.org/docs/basics/UsageWithReact.html) on redux.js.org. 48 | 49 | For an example you can look at 50 | [``](https://github.com/kriasoft/react-starter-kit/blob/feature/react-intl/src/components/LanguageSwitcher/LanguageSwitcher.js) 51 | component from `feature/react-intl` branch. It demonstrates both subscribing to store and dispatching actions. 52 | 53 | 54 | ### Dispatching Actions On Server 55 | 56 | See source of `src/server.js` 57 | -------------------------------------------------------------------------------- /docs/recipes/using-npm-and-webpack-as-a-build-tool.md: -------------------------------------------------------------------------------- 1 | ## Using Yarn and Webpack as a Build Tool 2 | 3 | The [Yarn](https://yarnpkg.com/) command line utility that comes with Node.js 4 | allows you to run arbitrary scripts and [Node.js modules](https://www.npmjs.com/) 5 | without them being globally installed. This is very convenient, because other 6 | developers in your team don't need to worry about having some set of tools 7 | installed globally before they can execute build automation scripts in your 8 | project. 9 | 10 | For example, if you need to lint your JavaScript code with [ESLint](http://eslint.org/) 11 | and [JSCS](http://jscs.info/), you just install them as project's dependencies: 12 | 13 | ```shell 14 | $ yarn add eslint jscs --dev 15 | ``` 16 | 17 | Add a new command line to `package.json/scripts`: 18 | 19 | ```json 20 | { 21 | "devDependencies": { 22 | "eslint": "^1.10.0", 23 | "jscs": "^2.7.0" 24 | }, 25 | "scripts": { 26 | "lint": "eslint src && jscs src" 27 | } 28 | } 29 | ``` 30 | 31 | And execute it by running: 32 | 33 | ```shell 34 | $ yarn run lint # yarn run 35 | ``` 36 | 37 | Which will be the same as running `./node_modules/bin/eslint src && ./node_modules/bin/jscs src`, 38 | except that the former has a shorter syntax and works the the same way on all 39 | platforms (Mac OS X, Windows, Linux). 40 | 41 | The same way you can run [Webpack](http://webpack.github.io/) module bundler 42 | to compile the source code of your app into a distributable format. Since 43 | Webpack has numerous [configuration options](http://webpack.github.io/docs/configuration), 44 | it's a good idea to have all of them in a separate configuration file, as 45 | opposed to feeding them to Webpack's CLI as command line arguments. As a rule 46 | of thumb, you want to keep the "scripts" section in your `package.json` file 47 | short enough and easy to read. 48 | 49 | For example, you may have `src/client.js` and `src/server.js` files that used 50 | as entry points to the client-side and server-side code of your app. The 51 | following Webpack configuration file (`webpack.config.js`) can be used 52 | to bundle them into client-side and server-side application bundles - 53 | `build/public/client.js` and `build/server.js` respectively: 54 | 55 | ```js 56 | module.exports = [{ 57 | context: __dirname + '/src' 58 | entry: './client.js', 59 | output: { 60 | path: __dirname + '/build/public', 61 | filename: 'client.js' 62 | } 63 | }, { 64 | context: __dirname + '/src', 65 | entry: './server.js', 66 | output: { 67 | path: __dirname + '/build', 68 | filename: 'server.js', 69 | libraryTarget: 'commonjs2' 70 | }, 71 | target: 'node', 72 | externals: /node_modules/, 73 | }]; 74 | ``` 75 | 76 | The `npm` script for it may look like this: 77 | 78 | ```json 79 | { 80 | "devDependencies": { 81 | "webpack": "^1.12.0" 82 | }, 83 | "scripts": { 84 | "build": "webpack --config webpack.config.js" 85 | } 86 | } 87 | ``` 88 | 89 | You can run it as follows: 90 | 91 | ```shell 92 | $ yarn run build 93 | ``` 94 | -------------------------------------------------------------------------------- /src/components/Html.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React Starter Kit (https://www.reactstarterkit.com/) 3 | * 4 | * Copyright © 2014-present Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | import React from 'react'; 11 | import PropTypes from 'prop-types'; 12 | import serialize from 'serialize-javascript'; 13 | import config from '../config'; 14 | 15 | /* eslint-disable react/no-danger */ 16 | 17 | class Html extends React.Component { 18 | static propTypes = { 19 | title: PropTypes.string.isRequired, 20 | description: PropTypes.string.isRequired, 21 | styles: PropTypes.arrayOf( 22 | PropTypes.shape({ 23 | id: PropTypes.string.isRequired, 24 | cssText: PropTypes.string.isRequired, 25 | }).isRequired, 26 | ), 27 | scripts: PropTypes.arrayOf(PropTypes.string.isRequired), 28 | app: PropTypes.object, // eslint-disable-line 29 | children: PropTypes.string.isRequired, 30 | }; 31 | 32 | static defaultProps = { 33 | styles: [], 34 | scripts: [], 35 | }; 36 | 37 | render() { 38 | const { title, description, styles, scripts, app, children } = this.props; 39 | return ( 40 | 41 | 42 | 43 | 44 | 45 | {title} 46 | 47 | 48 | 49 | {scripts.map(script => 50 | , 51 | )} 52 | 53 | {styles.map(style => 54 |