diff --git a/.gitignore b/.gitignore index d00c2d9752..35660fefec 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ database.sqlite node_modules ncp-debug.log npm-debug.log +stats.json diff --git a/Dockerfile b/Dockerfile index 8c7c90c7e7..707ebe8240 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,10 +1,11 @@ -FROM node:7.2.1-alpine +FROM node:6.9.5-alpine # Copy application files COPY ./build /usr/src/app WORKDIR /usr/src/app -# Install Node.js dependencies -RUN npm install --production --silent +# Install Yarn and Node.js dependencies +RUN npm install yarn --global --no-progress --silent --depth 0 && \ + yarn install --production --no-progress CMD [ "node", "server.js" ] diff --git a/LICENSE.txt b/LICENSE.txt index 16f8127007..3e95f03c67 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,6 +1,6 @@ The MIT License -Copyright (c) 2014-2016 Konstantin Tarkus, KriaSoft LLC. +Copyright (c) 2014-present Konstantin Tarkus, KriaSoft LLC. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index e377181272..9d1ef8748e 100644 --- a/README.md +++ b/README.md @@ -22,9 +22,7 @@ and newcomers to the industry. ### Getting Started * Follow the [getting started guide](./docs/getting-started.md) to download and run the project - ([node](https://nodejs.org/) >= 6, - **[node-gyp](https://github.com/nodejs/node-gyp#readme)** - and **[prerequisites](https://github.com/nodejs/node-gyp#installation)**) + ([Node.js](https://nodejs.org/) >= 6.5) * Check the [code recipes](./docs/recipes) used in this boilerplate, or share yours @@ -217,7 +215,7 @@ requests](CONTRIBUTING.md#pull-requests). ### License -Copyright © 2014-2016 Kriasoft, LLC. This source code is licensed under the MIT +Copyright © 2014-present Kriasoft, LLC. This source code is licensed under the MIT license found in the [LICENSE.txt](https://github.com/kriasoft/react-starter-kit/blob/master/LICENSE.txt) file. The documentation to the project is licensed under the [CC BY-SA 4.0](http://creativecommons.org/licenses/by-sa/4.0/) license. diff --git a/docs/getting-started.md b/docs/getting-started.md index 13d6215dc8..171db69122 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -4,7 +4,6 @@ * Mac OS X, Windows, or Linux * [Yarn](https://yarnpkg.com/) package + [Node.js](https://nodejs.org/) v6.5 or newer - * `node-gyp` prerequisites mentioned [here](https://github.com/nodejs/node-gyp) * Text editor or IDE pre-configured with React/JSX/Flow/ESlint ([learn more](./how-to-configure-text-editors.md)) ### Directory Layout @@ -33,11 +32,14 @@ Before you start, take a moment to see how the project structure looks like: │ ├── /clean.js # Cleans up the output (build) folder │ ├── /copy.js # Copies static files to output (build) folder │ ├── /deploy.js # Deploys your web application +│ ├── /postcss.config.js # Configuration for transforming styles with PostCSS plugins │ ├── /run.js # Helper function for running build automation tasks │ ├── /runServer.js # Launches (or restarts) Node.js server │ ├── /start.js # Launches the development web server with "live reload" │ └── /webpack.config.js # Configurations for client-side and server-side bundles -└── package.json # The list of 3rd party libraries and utilities +├── Dockerfile # Commands for building a Docker image for production +├── package.json # The list of 3rd party libraries and utilities +└── yarn.lock # Fixed versions of all the dependencies ``` **Note**: The current version of RSK does not contain a Flux implementation. diff --git a/docs/recipes/how-to-integrate-react-intl.md b/docs/recipes/how-to-integrate-react-intl.md index 18358b4804..997177dbc9 100644 --- a/docs/recipes/how-to-integrate-react-intl.md +++ b/docs/recipes/how-to-integrate-react-intl.md @@ -109,11 +109,11 @@ export default injectIntl(Example); When running the development server, every source file is watched and parsed for changed messages. Messages files are updated on the fly. -If a new definition is found, this definition is added to the end of every used `src/messages/xx-XX.json` file so when commiting, new translations will be at the tail of file. +If a new definition is found, this definition is added to the end of every used `src/messages/xx-XX.json` file so when committing, new translations will be at the tail of file. When an untranslated message is removed and its `message` field is empty as well, the message will be deleted from all translation files. This is why the `files` array is present. -When editiong a translation file, it should be copied to `build/messages/` directory. +When editing a translation file, it should be copied to `build/messages/` directory. ### Other References diff --git a/docs/recipes/how-to-use-sass.md b/docs/recipes/how-to-use-sass.md index edb856da6b..54d0fdabf0 100644 --- a/docs/recipes/how-to-use-sass.md +++ b/docs/recipes/how-to-use-sass.md @@ -8,7 +8,9 @@ than `node-sass`. ### Step 1 -Install [`node-sass`](https://github.com/sass/node-sass) and +Install [`node-sass`](https://github.com/sass/node-sass) +(includes [node-gyp](https://github.com/nodejs/node-gyp#readme) +and [prerequisites](https://github.com/nodejs/node-gyp#installation)) and [`sass-loader`](https://github.com/jtangelder/sass-loader) modules as dev dependencies: ```sh diff --git a/package.json b/package.json index 7052abe7fd..f0282b2a9b 100644 --- a/package.json +++ b/package.json @@ -6,19 +6,24 @@ "node": ">=6.5", "npm": ">=3.10" }, + "browserslist": [ + ">1%", + "last 4 versions", + "Firefox ESR", + "not ie < 9" + ], "dependencies": { "babel-polyfill": "^6.22.0", - "babel-runtime": "^6.22.0", "bluebird": "^3.4.7", "body-parser": "^1.16.0", "classnames": "^2.2.5", "cookie-parser": "^1.4.3", "core-js": "^2.4.1", "express": "^4.14.1", - "express-graphql": "^0.6.2", + "express-graphql": "^0.6.3", "express-jwt": "^5.1.0", "fastclick": "^1.0.6", - "fbjs": "^0.8.8", + "fbjs": "^0.8.9", "graphql": "^0.9.1", "history": "^4.5.1", "isomorphic-style-loader": "^1.1.0", @@ -45,11 +50,10 @@ "babel-eslint": "^7.1.1", "babel-loader": "^6.2.10", "babel-plugin-rewire": "^1.0.0", - "babel-plugin-transform-runtime": "^6.22.0", - "babel-preset-latest": "^6.22.0", + "babel-preset-env": "^1.1.8", "babel-preset-react": "^6.22.0", "babel-preset-react-optimize": "^1.0.1", - "babel-preset-stage-0": "^6.22.0", + "babel-preset-stage-2": "^6.22.0", "babel-register": "^6.22.0", "babel-template": "^6.22.0", "babel-types": "^6.22.0", @@ -59,17 +63,15 @@ "css-loader": "^0.26.1", "editorconfig-tools": "^0.1.1", "enzyme": "^2.7.1", - "eslint": "^3.14.1", - "eslint-config-airbnb": "^14.0.0", + "eslint": "^3.15.0", + "eslint-config-airbnb": "^14.1.0", "eslint-loader": "^1.6.1", - "eslint-plugin-css-modules": "^2.1.0", + "eslint-plugin-css-modules": "^2.2.0", "eslint-plugin-import": "^2.2.0", - "eslint-plugin-jsx-a11y": "^3.0.2", + "eslint-plugin-jsx-a11y": "^4.0.0", "eslint-plugin-react": "^6.9.0", - "extend": "^3.0.0", "file-loader": "^0.10.0", "front-matter": "^2.1.2", - "git-repository": "^0.1.4", "glob": "^7.1.1", "json-loader": "^0.5.4", "lint-staged": "^3.3.0", @@ -78,11 +80,11 @@ "mocha": "^3.2.0", "pixrem": "^3.0.2", "pleeease-filters": "^3.0.0", - "postcss": "^5.2.11", + "postcss": "^5.2.12", "postcss-calc": "^5.3.1", - "postcss-color-function": "^2.0.1", + "postcss-color-function": "^3.0.0", "postcss-custom-media": "^5.0.1", - "postcss-custom-properties": "^5.0.1", + "postcss-custom-properties": "^5.0.2", "postcss-custom-selectors": "^3.0.0", "postcss-flexbugs-fixes": "^2.1.0", "postcss-loader": "^1.2.2", @@ -103,28 +105,26 @@ "rimraf": "^2.5.4", "sinon": "^2.0.0-pre.5", "stylefmt": "^5.1.1", - "stylelint": "^7.7.1", - "stylelint-config-standard": "^15.0.1", + "stylelint": "^7.8.0", + "stylelint-config-standard": "^16.0.0", "url-loader": "^0.5.7", "webpack": "^2.2.1", - "webpack-dev-middleware": "^1.9.0", + "webpack-dev-middleware": "^1.10.0", "webpack-hot-middleware": "^2.16.1", "write-file-webpack-plugin": "^3.4.2" }, "babel": { "presets": [ - "react", - "stage-0" - ], - "plugins": [ - "syntax-trailing-function-commas", - "transform-async-to-generator", - "transform-es2015-destructuring", - "transform-es2015-parameters", - "transform-es2015-duplicate-keys", - "transform-es2015-modules-commonjs", - "transform-exponentiation-operator", - "transform-runtime" + [ + "env", + { + "targets": { + "node": "current" + } + } + ], + "stage-2", + "react" ], "env": { "test": { @@ -150,17 +150,10 @@ "browser": true }, "rules": { - "arrow-parens": "off", - "generator-star-spacing": "off", "import/extensions": "off", "import/no-extraneous-dependencies": "off", - "react/forbid-prop-types": "off", "react/jsx-filename-extension": "off", - "react/no-array-index-key": "off", - "react/no-danger": "off", - "react/no-unused-prop-types": "off", - "react/prefer-stateless-function": "off", - "react/require-default-props": "off" + "react/prefer-stateless-function": "off" } }, "stylelint": { @@ -187,7 +180,7 @@ }, "pre-commit": "lint:staged", "lint-staged": { - "*.{md,sh,txt,xml,html,json}": [ + "*.{cmd,html,json,md,sh,txt,xml,yml}": [ "editorconfig-tools fix", "git add" ], @@ -212,9 +205,10 @@ "copy": "babel-node tools/run copy", "bundle": "babel-node tools/run bundle", "build": "babel-node tools/run build", - "deploy": "babel-node tools/run deployToAzureWebApps", + "deploy": "babel-node tools/run deploy", "render": "babel-node tools/run render", "serve": "babel-node tools/run runServer", - "start": "babel-node tools/run start" + "start": "babel-node tools/run start", + "stats": "babel-node tools/run stats" } } diff --git a/src/client.js b/src/client.js index b549b101b0..63172448b6 100644 --- a/src/client.js +++ b/src/client.js @@ -1,13 +1,12 @@ /** * React Starter Kit (https://www.reactstarterkit.com/) * - * Copyright © 2014-2016 Kriasoft, LLC. All rights reserved. + * Copyright © 2014-present Kriasoft, LLC. All rights reserved. * * This source code is licensed under the MIT license found in the * LICENSE.txt file in the root directory of this source tree. */ -import 'babel-polyfill'; import React from 'react'; import ReactDOM from 'react-dom'; import FastClick from 'fastclick'; @@ -16,6 +15,7 @@ import queryString from 'query-string'; import { createPath } from 'history/PathUtils'; import history from './core/history'; import App from './components/App'; +import { updateMeta } from './core/DOMUtils'; import { ErrorReporter, deepForceUpdate } from './core/devUtils'; // Global (context) variables that can be easily accessed from any React component @@ -30,31 +30,6 @@ const context = { }, }; -function updateTag(tagName, keyName, keyValue, attrName, attrValue) { - const node = document.head.querySelector(`${tagName}[${keyName}="${keyValue}"]`); - if (node && node.getAttribute(attrName) === attrValue) return; - - // Remove and create a new tag in order to make it work with bookmarks in Safari - if (node) { - node.parentNode.removeChild(node); - } - if (typeof attrValue === 'string') { - const nextNode = document.createElement(tagName); - nextNode.setAttribute(keyName, keyValue); - nextNode.setAttribute(attrName, attrValue); - document.head.appendChild(nextNode); - } -} -function updateMeta(name, content) { - updateTag('meta', 'name', name, 'content', content); -} -function updateCustomMeta(property, content) { // eslint-disable-line no-unused-vars - updateTag('meta', 'property', property, 'content', content); -} -function updateLink(rel, href) { // eslint-disable-line no-unused-vars - updateTag('link', 'rel', rel, 'href', href); -} - // Switch off the native scroll restoration behavior and handle it manually // https://developers.google.com/web/updates/2015/09/history-api-scroll-restoration const scrollPositionsHistory = {}; @@ -114,14 +89,14 @@ let currentLocation = history.location; let routes = require('./routes').default; // Re-render the app when window.location changes -async function onLocationChange(location) { +async function onLocationChange(location, action) { // Remember the latest scroll position for the previous location scrollPositionsHistory[currentLocation.key] = { scrollX: window.pageXOffset, scrollY: window.pageYOffset, }; // Delete stored scroll position for next page if any - if (history.action === 'PUSH') { + if (action === 'PUSH') { delete scrollPositionsHistory[location.key]; } currentLocation = location; @@ -151,23 +126,20 @@ async function onLocationChange(location) { () => onRenderComplete(route, location), ); } catch (error) { - console.error(error); // eslint-disable-line no-console - - // Current url has been changed during navigation process, do nothing - if (currentLocation.key !== location.key) { - return; - } - // Display the error in full-screen for development mode if (__DEV__) { appInstance = null; document.title = `Error: ${error.message}`; ReactDOM.render(, container); - return; + throw error; } - // Avoid broken navigation in production mode by a full page reload on error - window.location.reload(); + console.error(error); // eslint-disable-line no-console + + // Do a full page reload if error occurs during client-side navigation + if (action && currentLocation.key === location.key) { + window.location.reload(); + } } } @@ -176,6 +148,16 @@ async function onLocationChange(location) { history.listen(onLocationChange); onLocationChange(currentLocation); +// Handle errors that might happen after rendering +// Display the error in full-screen for development mode +if (process.env.NODE_ENV !== 'production') { + window.addEventListener('error', (event) => { + appInstance = null; + document.title = `Runtime Error: ${event.error.message}`; + ReactDOM.render(, container); + }); +} + // Enable Hot Module Replacement (HMR) if (module.hot) { module.hot.accept('./routes', () => { diff --git a/src/components/App.js b/src/components/App.js index 86a1aa61a5..f76d948843 100644 --- a/src/components/App.js +++ b/src/components/App.js @@ -1,7 +1,7 @@ /** * React Starter Kit (https://www.reactstarterkit.com/) * - * Copyright © 2014-2016 Kriasoft, LLC. All rights reserved. + * Copyright © 2014-present Kriasoft, LLC. All rights reserved. * * This source code is licensed under the MIT license found in the * LICENSE.txt file in the root directory of this source tree. diff --git a/src/components/Feedback/Feedback.css b/src/components/Feedback/Feedback.css index b73144e5f3..e67a9eb3e5 100644 --- a/src/components/Feedback/Feedback.css +++ b/src/components/Feedback/Feedback.css @@ -1,7 +1,7 @@ /** * React Starter Kit (https://www.reactstarterkit.com/) * - * Copyright © 2014-2016 Kriasoft, LLC. All rights reserved. + * Copyright © 2014-present Kriasoft, LLC. All rights reserved. * * This source code is licensed under the MIT license found in the * LICENSE.txt file in the root directory of this source tree. diff --git a/src/components/Feedback/Feedback.js b/src/components/Feedback/Feedback.js index 31d0c06068..3216ffd6e6 100644 --- a/src/components/Feedback/Feedback.js +++ b/src/components/Feedback/Feedback.js @@ -1,7 +1,7 @@ /** * React Starter Kit (https://www.reactstarterkit.com/) * - * Copyright © 2014-2016 Kriasoft, LLC. All rights reserved. + * Copyright © 2014-present Kriasoft, LLC. All rights reserved. * * This source code is licensed under the MIT license found in the * LICENSE.txt file in the root directory of this source tree. diff --git a/src/components/Footer/Footer.css b/src/components/Footer/Footer.css index a34e74e76a..3297798db9 100644 --- a/src/components/Footer/Footer.css +++ b/src/components/Footer/Footer.css @@ -1,7 +1,7 @@ /** * React Starter Kit (https://www.reactstarterkit.com/) * - * Copyright © 2014-2016 Kriasoft, LLC. All rights reserved. + * Copyright © 2014-present Kriasoft, LLC. All rights reserved. * * This source code is licensed under the MIT license found in the * LICENSE.txt file in the root directory of this source tree. diff --git a/src/components/Footer/Footer.js b/src/components/Footer/Footer.js index 5cc600c255..6e1bbca309 100644 --- a/src/components/Footer/Footer.js +++ b/src/components/Footer/Footer.js @@ -1,7 +1,7 @@ /** * React Starter Kit (https://www.reactstarterkit.com/) * - * Copyright © 2014-2016 Kriasoft, LLC. All rights reserved. + * Copyright © 2014-present Kriasoft, LLC. All rights reserved. * * This source code is licensed under the MIT license found in the * LICENSE.txt file in the root directory of this source tree. diff --git a/src/components/Header/Header.css b/src/components/Header/Header.css index 59b8785a5d..b6c7f6fb58 100644 --- a/src/components/Header/Header.css +++ b/src/components/Header/Header.css @@ -1,7 +1,7 @@ /** * React Starter Kit (https://www.reactstarterkit.com/) * - * Copyright © 2014-2016 Kriasoft, LLC. All rights reserved. + * Copyright © 2014-present Kriasoft, LLC. All rights reserved. * * This source code is licensed under the MIT license found in the * LICENSE.txt file in the root directory of this source tree. diff --git a/src/components/Header/Header.js b/src/components/Header/Header.js index fc596da1fe..85a5fe720c 100644 --- a/src/components/Header/Header.js +++ b/src/components/Header/Header.js @@ -1,7 +1,7 @@ /** * React Starter Kit (https://www.reactstarterkit.com/) * - * Copyright © 2014-2016 Kriasoft, LLC. All rights reserved. + * Copyright © 2014-present Kriasoft, LLC. All rights reserved. * * This source code is licensed under the MIT license found in the * LICENSE.txt file in the root directory of this source tree. @@ -20,7 +20,7 @@ class Header extends React.Component { return (
- + React Your Company diff --git a/src/components/Html.js b/src/components/Html.js index 64968b9a9b..ce5b8d2306 100644 --- a/src/components/Html.js +++ b/src/components/Html.js @@ -1,7 +1,7 @@ /** * React Starter Kit (https://www.reactstarterkit.com/) * - * Copyright © 2014-2016 Kriasoft, LLC. All rights reserved. + * Copyright © 2014-present Kriasoft, LLC. All rights reserved. * * This source code is licensed under the MIT license found in the * LICENSE.txt file in the root directory of this source tree. @@ -14,13 +14,21 @@ class Html extends React.Component { static propTypes = { title: PropTypes.string.isRequired, description: PropTypes.string.isRequired, - style: PropTypes.string, + styles: PropTypes.arrayOf(PropTypes.shape({ + id: PropTypes.string.isRequired, + cssText: PropTypes.string.isRequired, + }).isRequired), scripts: PropTypes.arrayOf(PropTypes.string.isRequired), - children: PropTypes.string, + children: PropTypes.string.isRequired, + }; + + static defaultProps = { + styles: [], + scripts: [], }; render() { - const { title, description, style, scripts, children } = this.props; + const { title, description, styles, scripts, children } = this.props; return ( @@ -30,13 +38,25 @@ class Html extends React.Component { - {style &&