├── .github ├── FUNDING.yml └── ISSUE_TEMPLATE ├── .travis.yml ├── .gitignore ├── img ├── js.png ├── npm.png ├── pm2.png ├── chai.png ├── eslint.png ├── flow.png ├── jest.png ├── mocha.png ├── react.png ├── redux.png ├── yarn.png ├── webpack.png ├── flow-padded.png ├── jest-padded.png ├── js-padded.png ├── pm2-padded.png ├── yarn-padded.png ├── eslint-padded.png ├── pm2-padded-90.png ├── react-padded.png ├── react-router.png ├── redux-padded.png ├── bootstrap-padded.png ├── eslint-padded-90.png ├── flow-padded-90.png ├── jest-padded-90.png ├── react-padded-90.png ├── redux-padded-90.png ├── webpack-padded.png ├── yarn-padded-90.png ├── webpack-padded-90.png ├── bootstrap-padded-90.png ├── react-router-padded.png └── react-router-padded-90.png ├── package.json ├── mdlint.js ├── LICENSE.md ├── CHANGELOG.md ├── how-to-translate.md ├── yarn.lock ├── README.md └── tutorial ├── 09-travis-coveralls-heroku.md ├── 07-socket-io.md ├── 01-node-yarn-package-json.md ├── 03-express-nodemon-pm2.md ├── 04-webpack-react-hmr.md ├── 08-bootstrap-jss.md ├── 02-babel-es6-eslint-flow-jest-husky.md ├── 06-react-router-ssr-helmet.md └── 05-redux-immutable-fetch.md /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: verekia 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: node 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | npm-debug.log 3 | node_modules 4 | 5 | -------------------------------------------------------------------------------- /img/js.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/verekia/js-stack-from-scratch/HEAD/img/js.png -------------------------------------------------------------------------------- /img/npm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/verekia/js-stack-from-scratch/HEAD/img/npm.png -------------------------------------------------------------------------------- /img/pm2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/verekia/js-stack-from-scratch/HEAD/img/pm2.png -------------------------------------------------------------------------------- /img/chai.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/verekia/js-stack-from-scratch/HEAD/img/chai.png -------------------------------------------------------------------------------- /img/eslint.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/verekia/js-stack-from-scratch/HEAD/img/eslint.png -------------------------------------------------------------------------------- /img/flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/verekia/js-stack-from-scratch/HEAD/img/flow.png -------------------------------------------------------------------------------- /img/jest.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/verekia/js-stack-from-scratch/HEAD/img/jest.png -------------------------------------------------------------------------------- /img/mocha.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/verekia/js-stack-from-scratch/HEAD/img/mocha.png -------------------------------------------------------------------------------- /img/react.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/verekia/js-stack-from-scratch/HEAD/img/react.png -------------------------------------------------------------------------------- /img/redux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/verekia/js-stack-from-scratch/HEAD/img/redux.png -------------------------------------------------------------------------------- /img/yarn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/verekia/js-stack-from-scratch/HEAD/img/yarn.png -------------------------------------------------------------------------------- /img/webpack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/verekia/js-stack-from-scratch/HEAD/img/webpack.png -------------------------------------------------------------------------------- /img/flow-padded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/verekia/js-stack-from-scratch/HEAD/img/flow-padded.png -------------------------------------------------------------------------------- /img/jest-padded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/verekia/js-stack-from-scratch/HEAD/img/jest-padded.png -------------------------------------------------------------------------------- /img/js-padded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/verekia/js-stack-from-scratch/HEAD/img/js-padded.png -------------------------------------------------------------------------------- /img/pm2-padded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/verekia/js-stack-from-scratch/HEAD/img/pm2-padded.png -------------------------------------------------------------------------------- /img/yarn-padded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/verekia/js-stack-from-scratch/HEAD/img/yarn-padded.png -------------------------------------------------------------------------------- /img/eslint-padded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/verekia/js-stack-from-scratch/HEAD/img/eslint-padded.png -------------------------------------------------------------------------------- /img/pm2-padded-90.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/verekia/js-stack-from-scratch/HEAD/img/pm2-padded-90.png -------------------------------------------------------------------------------- /img/react-padded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/verekia/js-stack-from-scratch/HEAD/img/react-padded.png -------------------------------------------------------------------------------- /img/react-router.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/verekia/js-stack-from-scratch/HEAD/img/react-router.png -------------------------------------------------------------------------------- /img/redux-padded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/verekia/js-stack-from-scratch/HEAD/img/redux-padded.png -------------------------------------------------------------------------------- /img/bootstrap-padded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/verekia/js-stack-from-scratch/HEAD/img/bootstrap-padded.png -------------------------------------------------------------------------------- /img/eslint-padded-90.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/verekia/js-stack-from-scratch/HEAD/img/eslint-padded-90.png -------------------------------------------------------------------------------- /img/flow-padded-90.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/verekia/js-stack-from-scratch/HEAD/img/flow-padded-90.png -------------------------------------------------------------------------------- /img/jest-padded-90.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/verekia/js-stack-from-scratch/HEAD/img/jest-padded-90.png -------------------------------------------------------------------------------- /img/react-padded-90.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/verekia/js-stack-from-scratch/HEAD/img/react-padded-90.png -------------------------------------------------------------------------------- /img/redux-padded-90.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/verekia/js-stack-from-scratch/HEAD/img/redux-padded-90.png -------------------------------------------------------------------------------- /img/webpack-padded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/verekia/js-stack-from-scratch/HEAD/img/webpack-padded.png -------------------------------------------------------------------------------- /img/yarn-padded-90.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/verekia/js-stack-from-scratch/HEAD/img/yarn-padded-90.png -------------------------------------------------------------------------------- /img/webpack-padded-90.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/verekia/js-stack-from-scratch/HEAD/img/webpack-padded-90.png -------------------------------------------------------------------------------- /img/bootstrap-padded-90.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/verekia/js-stack-from-scratch/HEAD/img/bootstrap-padded-90.png -------------------------------------------------------------------------------- /img/react-router-padded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/verekia/js-stack-from-scratch/HEAD/img/react-router-padded.png -------------------------------------------------------------------------------- /img/react-router-padded-90.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/verekia/js-stack-from-scratch/HEAD/img/react-router-padded-90.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "js-stack-from-scratch", 3 | "version": "2.4.5", 4 | "description": "JavaScript Stack from Scratch - Step-by-step tutorial to build a modern JavaScript stack", 5 | "scripts": { 6 | "test": "node mdlint.js" 7 | }, 8 | "devDependencies": { 9 | "glob": "^7.1.1", 10 | "markdownlint": "^0.4.0" 11 | }, 12 | "repository": "verekia/js-stack-from-scratch", 13 | "author": "Jonathan Verrecchia - @verekia", 14 | "license": "MIT" 15 | } 16 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE: -------------------------------------------------------------------------------- 1 | ### Type of issue: (feature suggestion, bug?) 2 | 3 | ### Chapter: 4 | 5 | ### If it's a bug: 6 | 7 | Please try using the code provided instead of your own to see if that solves the issue. If it does, compare the content of relevant files to see if anything is missing in your version. Every chapter is automatically tested, so issues are likely coming from missing an instruction in the tutorial or a typo. Feel free to open an issue if there is a problem with instructions though. 8 | -------------------------------------------------------------------------------- /mdlint.js: -------------------------------------------------------------------------------- 1 | const glob = require('glob') 2 | const markdownlint = require('markdownlint') 3 | 4 | const config = { 5 | 'default': true, 6 | 'line_length': false, 7 | 'no-emphasis-as-header': false, 8 | } 9 | 10 | const files = glob.sync('**/*.md', { ignore: '**/node_modules/**' }) 11 | 12 | markdownlint({ files, config }, (err, result) => { 13 | if (!err) { 14 | const resultString = result.toString() 15 | console.log('== Linting Markdown Files...') 16 | if (resultString) { 17 | console.log(resultString) 18 | process.exit(1) 19 | } else { 20 | console.log('== OK!') 21 | } 22 | } 23 | }) 24 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # MIT License 2 | 3 | Copyright (c) 2017 Jonathan Verrecchia 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 all 13 | 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 THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | ## v2.4.5 4 | 5 | - Add `babel-plugin-flow-react-proptypes`. 6 | - Add `eslint-plugin-compat`. 7 | - Add JSS `composes` example. 8 | 9 | ## v2.4.4 10 | 11 | - Update Immutable to remove the `import * as Immutable from 'immutable'` syntax. 12 | - Declare Flow types outside of function params for React components. 13 | - Improve Webpack `publicPath`. 14 | 15 | ## V2, up to v2.4.3 16 | 17 | - Gulp is gone, replaced by NPM (Yarn) scripts. 18 | - Express has been added, with template strings for static HTML. Gzip compression enabled. 19 | - Support for development environment with Nodemon and production environment with PM2. 20 | - Minification or sourcemaps depending on the environment via Webpack. 21 | - Add Webpack Dev Server, with Hot Module Replacement and `react-hot-loader`. 22 | - Add an asynchronous call example with `redux-thunk`. 23 | - Linting / type checking / testing is not launched at every file change anymore, but triggered by Git Hooks via Husky. 24 | - Some chapters have been combined to make it easier to maintain the tutorial. 25 | - Replace Chai and Mocha by Jest. 26 | - Add React-Router, Server-Side rendering, `react-helmet`. 27 | - Rename all "dog" things and replaced it by "hello" things. It's a Hello World app after all. 28 | - Add Twitter Bootstrap, JSS, and `react-jss` for styling. 29 | - Add a Websocket example with Socket.IO. 30 | - Add optional Heroku, Travis, and Coveralls integrations. 31 | -------------------------------------------------------------------------------- /how-to-translate.md: -------------------------------------------------------------------------------- 1 | # How to translate this tutorial 2 | 3 | Thank you for your interest in translating my tutorial! Here are a few recommendations to get started. 4 | 5 | This tutorial is in constant evolution to provide the best learning experience to readers. Both the code and `README.md` files will change over time. It is great if you do a one-shot translation that won't evolve, but it would be even better if you could try to keep up with the original English version as it changes! 6 | 7 | Here is what I think is a good workflow: 8 | 9 | - Check if there is already an [ongoing translation](https://github.com/verekia/js-stack-from-scratch/issues/147) for your language. If that's the case, get in touch with the folks who opened it and consider collaborating. All maintainers will be mentioned on the English repo, so team work is encouraged! You can open issues on their translation fork project to offer your help on certain chapters for instance. 10 | 11 | - Join the [Translations Gitter room](https://gitter.im/js-stack-from-scratch/Translations) if you're feeling chatty. 12 | 13 | - Fork the main [English repository](https://github.com/verekia/js-stack-from-scratch). 14 | 15 | - Post in [this issue](https://github.com/verekia/js-stack-from-scratch/issues/147) the language and URL of your forked repo. 16 | 17 | - Translate the `README.md` files. 18 | 19 | - Add a note somewhere explaining on the main `README.md` that this is a translation, with a link to the English repository. If you don't plan to make the translation evolve over time, you can maybe add a little note saying to refer to the English one for an up-to-date version of the tutorial. I'll leave that up to your preference. 20 | 21 | - Submit a Pull Request to the English repo to add a link to your forked repository under the Translations section of the main `README.md`. It could look like this: 22 | 23 | ```md 24 | ## Translations 25 | 26 | - [Language](http://github.com/yourprofile/your-fork) by [You](http://yourwebsite.com) 27 | or 28 | - [Language](http://github.com/yourprofile/your-fork) by [@You](http://twitter.com/yourprofile) 29 | or 30 | - [Language](http://github.com/yourprofile/your-fork) by [@You](http://github.com/yourprofile) 31 | ``` 32 | 33 | Since I want to reward you for your good work as much as possible, you can put any link you like on your name (to your personal website, Twitter profile, or Github profile for instance). 34 | 35 | - After your original one-shot translation, if you want to update your repo with the latest change from the main English repo, [sync your fork](https://help.github.com/articles/syncing-a-fork/) with my repo. To make it easy to see what changed since your initial translation, you can use Github's feature to [compare commits](https://help.github.com/articles/comparing-commits-across-time/#comparing-commits). Set the **base** to the last commit from the English repo you used to translate, and compare it to **master**, like so: 36 | 37 | 38 | https://github.com/verekia/js-stack-from-scratch/compare/c65dfa65d02c21063d94f0955de90947ba5273ad...master 39 | 40 | 41 | That should give you a easy-to-read diff to see exactly what changed in `README.md` files since your translation! 42 | -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | argparse@^1.0.7: 6 | version "1.0.9" 7 | resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.9.tgz#73d83bc263f86e97f8cc4f6bae1b0e90a7d22c86" 8 | dependencies: 9 | sprintf-js "~1.0.2" 10 | 11 | balanced-match@^0.4.1: 12 | version "0.4.2" 13 | resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-0.4.2.tgz#cb3f3e3c732dc0f01ee70b403f302e61d7709838" 14 | 15 | brace-expansion@^1.0.0: 16 | version "1.1.6" 17 | resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.6.tgz#7197d7eaa9b87e648390ea61fc66c84427420df9" 18 | dependencies: 19 | balanced-match "^0.4.1" 20 | concat-map "0.0.1" 21 | 22 | concat-map@0.0.1: 23 | version "0.0.1" 24 | resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" 25 | 26 | entities@~1.1.1: 27 | version "1.1.1" 28 | resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.1.tgz#6e5c2d0a5621b5dadaecef80b90edfb5cd7772f0" 29 | 30 | fs.realpath@^1.0.0: 31 | version "1.0.0" 32 | resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" 33 | 34 | glob@^7.1.1: 35 | version "7.1.1" 36 | resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.1.tgz#805211df04faaf1c63a3600306cdf5ade50b2ec8" 37 | dependencies: 38 | fs.realpath "^1.0.0" 39 | inflight "^1.0.4" 40 | inherits "2" 41 | minimatch "^3.0.2" 42 | once "^1.3.0" 43 | path-is-absolute "^1.0.0" 44 | 45 | inflight@^1.0.4: 46 | version "1.0.6" 47 | resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" 48 | dependencies: 49 | once "^1.3.0" 50 | wrappy "1" 51 | 52 | inherits@2: 53 | version "2.0.3" 54 | resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" 55 | 56 | linkify-it@^2.0.0: 57 | version "2.0.3" 58 | resolved "https://registry.yarnpkg.com/linkify-it/-/linkify-it-2.0.3.tgz#d94a4648f9b1c179d64fa97291268bdb6ce9434f" 59 | dependencies: 60 | uc.micro "^1.0.1" 61 | 62 | markdown-it@^8.3.0: 63 | version "8.3.1" 64 | resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-8.3.1.tgz#2f4b622948ccdc193d66f3ca2d43125ac4ac7323" 65 | dependencies: 66 | argparse "^1.0.7" 67 | entities "~1.1.1" 68 | linkify-it "^2.0.0" 69 | mdurl "^1.0.1" 70 | uc.micro "^1.0.3" 71 | 72 | markdownlint@^0.4.0: 73 | version "0.4.0" 74 | resolved "https://registry.yarnpkg.com/markdownlint/-/markdownlint-0.4.0.tgz#5be8a342c4ab4d5c6cfe8702db30c63bbed01589" 75 | dependencies: 76 | markdown-it "^8.3.0" 77 | 78 | mdurl@^1.0.1: 79 | version "1.0.1" 80 | resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e" 81 | 82 | minimatch@^3.0.2: 83 | version "3.0.3" 84 | resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.3.tgz#2a4e4090b96b2db06a9d7df01055a62a77c9b774" 85 | dependencies: 86 | brace-expansion "^1.0.0" 87 | 88 | once@^1.3.0: 89 | version "1.4.0" 90 | resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" 91 | dependencies: 92 | wrappy "1" 93 | 94 | path-is-absolute@^1.0.0: 95 | version "1.0.1" 96 | resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" 97 | 98 | sprintf-js@~1.0.2: 99 | version "1.0.3" 100 | resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" 101 | 102 | uc.micro@^1.0.1, uc.micro@^1.0.3: 103 | version "1.0.3" 104 | resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.3.tgz#7ed50d5e0f9a9fb0a573379259f2a77458d50192" 105 | 106 | wrappy@1: 107 | version "1.0.2" 108 | resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" 109 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # JavaScript Stack from Scratch 2 | 3 | [](https://travis-ci.org/verekia/js-stack-from-scratch) 4 | [](https://github.com/verekia/js-stack-from-scratch/releases) 5 | [](https://gitter.im/js-stack-from-scratch/) 6 | 7 | [](https://facebook.github.io/react/) 8 | [](http://redux.js.org/) 9 | [](https://github.com/ReactTraining/react-router) 10 | [](https://flowtype.org/) 11 | [](http://eslint.org/) 12 | [](https://facebook.github.io/jest/) 13 | [](https://yarnpkg.com/) 14 | [](https://webpack.github.io/) 15 | 16 | Welcome to my modern JavaScript stack tutorial: **JavaScript Stack from Scratch**. 17 | 18 | > 🎉 **This is the V2 of the tutorial, major changes happened since the 2016 release. Check the [Change Log](/CHANGELOG.md)!** 19 | 20 | This is a straight-to-the-point guide to assembling a JavaScript stack. It requires some general programming knowledge, and JavaScript basics. **It focuses on wiring tools together** and giving you the **simplest possible example** for each tool. You can see this tutorial as *a way to write your own boilerplate from scratch*. Since the goal of this tutorial is to assemble various tools, I do not go into details about how these tools work individually. Refer to their documentation or find other tutorials if you want to acquire deeper knowledge in them. 21 | 22 | You don't need to use this entire stack if you build a simple web page with a few JS interactions of course (a combination of Browserify/Webpack + Babel + jQuery is enough to be able to write ES6 code in different files), but if you want to build a web app that scales, and need help setting things up, this tutorial will work great for you. 23 | 24 | A big chunk of the stack described in this tutorial uses React. If you are beginning and just want to learn React, [create-react-app](https://github.com/facebookincubator/create-react-app) will get you up and running with a React environment very quickly with a pre-made configuration. I would for instance recommend this approach to someone who arrives in a team that's using React and needs to catch up with a learning playground. In this tutorial you won't use a pre-made configuration, because I want you to understand everything that's happening under the hood. 25 | 26 | Code examples are available for each chapter, and you can run them all with `yarn && yarn start`. I recommend writing everything from scratch yourself by following the **step-by-step instructions** though. 27 | 28 | Final code available in the [JS-Stack-Boilerplate repository](https://github.com/verekia/js-stack-boilerplate), and in the [releases](https://github.com/verekia/js-stack-from-scratch/releases). There is a [live demo](https://js-stack.herokuapp.com/) too. 29 | 30 | Works on Linux, macOS, and Windows. 31 | 32 | > **Note**: Since the tutorial was last edited in May 2017, a few libraries have slightly changed their APIs. 95% of the tutorial is still perfectly valid, but if you run into something weird, make sure to check out the [open issues](https://github.com/verekia/js-stack-from-scratch/issues?q=is%3Aopen+is%3Aissue+label%3Abug). 33 | 34 | ## Table of contents 35 | 36 | [01 - Node, Yarn, `package.json`](/tutorial/01-node-yarn-package-json.md#readme) 37 | 38 | [02 - Babel, ES6, ESLint, Flow, Jest, Husky](/tutorial/02-babel-es6-eslint-flow-jest-husky.md#readme) 39 | 40 | [03 - Express, Nodemon, PM2](/tutorial/03-express-nodemon-pm2.md#readme) 41 | 42 | [04 - Webpack, React, HMR](/tutorial/04-webpack-react-hmr.md#readme) 43 | 44 | [05 - Redux, Immutable, Fetch](/tutorial/05-redux-immutable-fetch.md#readme) 45 | 46 | [06 - React Router, Server-Side Rendering, Helmet](/tutorial/06-react-router-ssr-helmet.md#readme) 47 | 48 | [07 - Socket.IO](/tutorial/07-socket-io.md#readme) 49 | 50 | [08 - Bootstrap, JSS](/tutorial/08-bootstrap-jss.md#readme) 51 | 52 | [09 - Travis, Coveralls, Heroku](/tutorial/09-travis-coveralls-heroku.md#readme) 53 | 54 | ## Coming up next 55 | 56 | Setting up your editor (Atom first), MongoDB, Progressive Web App, E2E testing. 57 | 58 | ## Translations 59 | 60 | If you want to add your translation, please read the [translation recommendations](/how-to-translate.md) to get started! 61 | 62 | ### V2 63 | 64 | - [Bulgarian](https://github.com/mihailgaberov/js-stack-from-scratch) by [mihailgaberov](http://github.com/mihailgaberov) 65 | - [Chinese (simplified)](https://github.com/yepbug/js-stack-from-scratch/) by [@yepbug](https://github.com/yepbug) 66 | - [French](https://github.com/naomihauret/js-stack-from-scratch/) by [Naomi Hauret](https://twitter.com/naomihauret) 67 | - [Italian](https://github.com/fbertone/guida-javascript-moderno) by [Fabrizio Bertone](https://github.com/fbertone) - [fbertone.it](http://fbertone.it) 68 | - [Polish](https://github.com/mbiesiad/js-stack-from-scratch) by [@mbiesiad](https://github.com/mbiesiad) 69 | 70 | Check out the [ongoing translations](https://github.com/verekia/js-stack-from-scratch/issues/147). 71 | 72 | ### V1 73 | 74 | - [Chinese (simplified)](https://github.com/pd4d10/js-stack-from-scratch) by [@pd4d10](http://github.com/pd4d10) 75 | - [Italian](https://github.com/fbertone/js-stack-from-scratch) by [Fabrizio Bertone](https://github.com/fbertone) 76 | - [Japanese](https://github.com/takahashim/js-stack-from-scratch) by [@takahashim](https://github.com/takahashim) 77 | - [Russian](https://github.com/UsulPro/js-stack-from-scratch) by [React Theming](https://github.com/sm-react/react-theming) 78 | - [Thai](https://github.com/MicroBenz/js-stack-from-scratch) by [MicroBenz](https://github.com/MicroBenz) 79 | 80 | ## Credits 81 | 82 | Created by [@verekia](https://twitter.com/verekia) – [verekia.com](http://verekia.com/). 83 | 84 | License: MIT 85 | -------------------------------------------------------------------------------- /tutorial/09-travis-coveralls-heroku.md: -------------------------------------------------------------------------------- 1 | # 09 - Travis, Coveralls, and Heroku 2 | 3 | Code for this chapter available in the `master` branch of the [JS-Stack-Boilerplate repository](https://github.com/verekia/js-stack-boilerplate). 4 | 5 | In this chapter, I integrate our app with third-party services. These services offer free and paid plans. It is a bit controversial to use such services in a tutorial that relies exclusively on community-driven and free open source tools, which is why I maintain 2 different branches of the [JS-Stack-Boilerplate repository](https://github.com/verekia/js-stack-boilerplate), `master` and `master-no-services`. 6 | 7 | ## Travis 8 | 9 | > 💡 **[Travis CI](https://travis-ci.org/)** is a popular continuous integration platform, free for open source projects. 10 | 11 | If your project is hosted publicly on Github, integrating Travis is very simple. First, authenticate with your Github account on Travis, and add your repository. 12 | 13 | - Then, create a `.travis.yml` file containing: 14 | 15 | ```yaml 16 | language: node_js 17 | node_js: node 18 | script: yarn test && yarn prod:build 19 | ``` 20 | 21 | Travis will detect automatically that you use Yarn because you have a `yarn.lock` file. Every time you push code to your Github repository, it will run `yarn test && yarn prod:build`. If nothing goes wrong, you should get a green build. 22 | 23 | ## Coveralls 24 | 25 | > 💡 **[Coveralls](https://coveralls.io)** is a service that gives you a history and statistics of your test coverage. 26 | 27 | If your project is open-source on Github and compatible with Coveralls' supported Continuous Integration services, the only thing you need to do is to pipe the coverage file generated by Jest to the `coveralls` binary. 28 | 29 | - Run `yarn add --dev coveralls` 30 | 31 | - Edit your `.travis.yml`'s `script` instruction like so: 32 | 33 | ```yaml 34 | script: yarn test && yarn prod:build && cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js 35 | ``` 36 | 37 | Now, every time Travis will build, it will automatically submit your Jest test coverage data to Coveralls. 38 | 39 | ## Badges 40 | 41 | Is everything green on Travis and Coveralls? Great, show it off to the world with shiny badges! 42 | 43 | You can use the code provided by Travis or Coveralls directly, or use [shields.io](http://shields.io/) to homogenize or customize your badges. Let's use shields.io here. 44 | 45 | - Create or edit your `README.md` like so: 46 | 47 | ```md 48 | [](https://travis-ci.org/GITHUB-USERNAME/GITHUB-REPO) 49 | [](https://coveralls.io/github/GITHUB-USERNAME/GITHUB-REPO?branch=master) 50 | ``` 51 | 52 | Of course, replace `GITHUB-USERNAME/GITHUB-REPO` by your actual Github username and repository name. 53 | 54 | ## Heroku 55 | 56 | > 💡 **[Heroku](https://www.heroku.com/)** is a [PaaS](https://en.wikipedia.org/wiki/Platform_as_a_service) to deploy to. It takes care of infrastructure details, so you can focus on developing your app without worrying about what happens behind the scenes. 57 | 58 | This tutorial is not sponsored in any way by Heroku, but Heroku being one hell of a platform, I am going to show you how to deploy your app to it. Yep, that's the kind of free love you get when you build a great product. 59 | 60 | **Note**: I might add an AWS section in this chapter later, but one thing at a time. 61 | 62 | ### Web setup 63 | 64 | - If that's not done yet, install the [Heroku CLI](https://devcenter.heroku.com/articles/getting-started-with-nodejs) and log in. 65 | 66 | - Go to your [Heroku Dashboard](https://dashboard.heroku.com/) and create 2 apps, one called `your-project` and one called `your-project-staging` for instance. 67 | 68 | We are going to let Heroku take care of transpiling our ES6/Flow code with Babel, and generate client bundles with Webpack. But since these are `devDependencies`, Yarn won't install them in a production environment like Heroku. Let's change this behavior with the `NPM_CONFIG_PRODUCTION` env variable. 69 | 70 | - In both apps, under Settings > Config Variables, add `NPM_CONFIG_PRODUCTION` set to `false`. 71 | 72 | - Create a Pipeline, and grant Heroku access to your Github. 73 | 74 | - Add both apps to the pipeline, make the staging one auto-deploy on changes in `master`, and enable Review Apps. 75 | 76 | Alright, let's prepare our project for a deployment to Heroku. 77 | 78 | ### Running in production mode locally 79 | 80 | - Create a `.env` file containing: 81 | 82 | ```.env 83 | NODE_ENV='production' 84 | PORT='8000' 85 | ``` 86 | 87 | That's in this file that you should put your local-only variables and secret variables. Don't commit it to a public repository if your project is private. 88 | 89 | - Add `/.env` to your `.gitignore` 90 | 91 | - Create a `Procfile` file containing: 92 | 93 | ```Procfile 94 | web: node lib/server 95 | ``` 96 | 97 | That's where we specify the entry point of our server. 98 | 99 | We are not going to use PM2 anymore, we'll use `heroku local` instead to run in production mode locally. 100 | 101 | - Run `yarn remove pm2` 102 | 103 | - Edit your `prod:start` script in `package.json`: 104 | 105 | ```json 106 | "prod:start": "heroku local", 107 | ``` 108 | 109 | - Remove `prod:stop` from `package.json`. We don't need it anymore since `heroku local` is a hanging process that we can kill with Ctrl+C, unlike `pm2 start`. 110 | 111 | 🏁 Run `yarn prod:build` and `yarn prod:start`. It should start your server and show you the logs. 112 | 113 | ### Deploying to production 114 | 115 | - Add the following line to your `scripts` in `package.json`: 116 | 117 | ```json 118 | "heroku-postbuild": "yarn prod:build", 119 | ``` 120 | 121 | `heroku-postbuild` is a task that will be run every time you deploy an app to Heroku. 122 | 123 | You will also probably want to specify a specific version of Node or Yarn for Heroku to use. 124 | 125 | - Add this to your `package.json`: 126 | 127 | ```json 128 | "engines": { 129 | "node": "7.x", 130 | "yarn": "0.20.3" 131 | }, 132 | ``` 133 | 134 | - Create an `app.json` file containing: 135 | 136 | ```json 137 | { 138 | "env": { 139 | "NPM_CONFIG_PRODUCTION": "false" 140 | } 141 | } 142 | ``` 143 | 144 | This is for your Review Apps to use. 145 | 146 | You should now be all set to use Heroku Pipeline deployments. 147 | 148 | 🏁 Create a new git branch, make changes and open a Github Pull Request to instantiate a Review App. Check your changes on the Review App URL, and if everything looks good, merge your Pull Request with `master` on Github. A few minutes later, your staging app should have been automatically deployed. Check your changes on the staging app URL, and if everything still looks good, promote staging to production. 149 | 150 | You are done! Congratulations if you finished this entire tutorial starting from scratch. 151 | 152 | You deserve this emoji medal: 🏅 153 | 154 | Back to the [previous section](08-bootstrap-jss.md#readme) or the [table of contents](https://github.com/verekia/js-stack-from-scratch#table-of-contents). 155 | -------------------------------------------------------------------------------- /tutorial/07-socket-io.md: -------------------------------------------------------------------------------- 1 | # 07 - Socket.IO 2 | 3 | Code for this chapter available [here](https://github.com/verekia/js-stack-walkthrough/tree/master/07-socket-io). 4 | 5 | > 💡 **[Socket.IO](https://github.com/socketio/socket.io)** is a library to easily deal with Websockets. It provides a convenient API and fallback for browsers that don't support Websockets. 6 | 7 | In this chapter, we are going to set up a basic message exchange between the client and the server. In order to not add more pages and components – which would be unrelated to the core feature we're interested in here – we are going to make this exchange happen in the browser console. No UI stuff in this chapter. 8 | 9 | - Run `yarn add socket.io socket.io-client` 10 | 11 | ## Server-side 12 | 13 | - Edit your `src/server/index.js` like so: 14 | 15 | ```js 16 | // @flow 17 | 18 | import compression from 'compression' 19 | import express from 'express' 20 | import { Server } from 'http' 21 | import socketIO from 'socket.io' 22 | 23 | import routing from './routing' 24 | import { WEB_PORT, STATIC_PATH } from '../shared/config' 25 | import { isProd } from '../shared/util' 26 | import setUpSocket from './socket' 27 | 28 | const app = express() 29 | // flow-disable-next-line 30 | const http = Server(app) 31 | const io = socketIO(http) 32 | setUpSocket(io) 33 | 34 | app.use(compression()) 35 | app.use(STATIC_PATH, express.static('dist')) 36 | app.use(STATIC_PATH, express.static('public')) 37 | 38 | routing(app) 39 | 40 | http.listen(WEB_PORT, () => { 41 | // eslint-disable-next-line no-console 42 | console.log(`Server running on port ${WEB_PORT} ${isProd ? '(production)' : 43 | '(development).\nKeep "yarn dev:wds" running in an other terminal'}.`) 44 | }) 45 | ``` 46 | 47 | Note that in order for Socket.IO to work, you need to use `Server` from `http` to `listen` to incoming requests, and not the Express `app`. Fortunately, that doesn't change much of the code. All the Websocket details are externalized in a different file, called with `setUpSocket`. 48 | 49 | - Add the following constants to `src/shared/config.js`: 50 | 51 | ```js 52 | export const IO_CONNECT = 'connect' 53 | export const IO_DISCONNECT = 'disconnect' 54 | export const IO_CLIENT_HELLO = 'IO_CLIENT_HELLO' 55 | export const IO_CLIENT_JOIN_ROOM = 'IO_CLIENT_JOIN_ROOM' 56 | export const IO_SERVER_HELLO = 'IO_SERVER_HELLO' 57 | ``` 58 | 59 | These are the *type of messages* your client and your server will exchange. I suggest prefixing them with either `IO_CLIENT` or `IO_SERVER` to make it clearer *who* is sending the message. Otherwise, things can get pretty confusing when you have a lot of message types. 60 | 61 | As you can see, we have a `IO_CLIENT_JOIN_ROOM`, because for the sake of demonstration, we are going to make clients join a room (like a chatroom). Rooms are useful to broadcast messages to specific groups of users. 62 | 63 | - Create a `src/server/socket.js` file containing: 64 | 65 | ```js 66 | // @flow 67 | 68 | import { 69 | IO_CONNECT, 70 | IO_DISCONNECT, 71 | IO_CLIENT_JOIN_ROOM, 72 | IO_CLIENT_HELLO, 73 | IO_SERVER_HELLO, 74 | } from '../shared/config' 75 | 76 | /* eslint-disable no-console */ 77 | const setUpSocket = (io: Object) => { 78 | io.on(IO_CONNECT, (socket) => { 79 | console.log('[socket.io] A client connected.') 80 | 81 | socket.on(IO_CLIENT_JOIN_ROOM, (room) => { 82 | socket.join(room) 83 | console.log(`[socket.io] A client joined room ${room}.`) 84 | 85 | io.emit(IO_SERVER_HELLO, 'Hello everyone!') 86 | io.to(room).emit(IO_SERVER_HELLO, `Hello clients of room ${room}!`) 87 | socket.emit(IO_SERVER_HELLO, 'Hello you!') 88 | }) 89 | 90 | socket.on(IO_CLIENT_HELLO, (clientMessage) => { 91 | console.log(`[socket.io] Client: ${clientMessage}`) 92 | }) 93 | 94 | socket.on(IO_DISCONNECT, () => { 95 | console.log('[socket.io] A client disconnected.') 96 | }) 97 | }) 98 | } 99 | /* eslint-enable no-console */ 100 | 101 | export default setUpSocket 102 | ``` 103 | 104 | Okay, so in this file, we implement *how our server should react when clients connect and send messages to it*: 105 | 106 | - When the client connects, we log it in the server console, and get access to the `socket` object, which we can use to communicate back with that client. 107 | - When a client sends `IO_CLIENT_JOIN_ROOM`, we make it join the `room` it wants. Once it has joined a room, we send 3 demo messages: 1 message to every user, 1 message to users in that room, 1 message to that client only. 108 | - When the client sends `IO_CLIENT_HELLO`, we log its message in the server console. 109 | - When the client disconnects, we log it as well. 110 | 111 | ## Client-side 112 | 113 | The client-side of things is going to look very similar. 114 | 115 | - Edit `src/client/index.jsx` like so: 116 | 117 | ```js 118 | // [...] 119 | import setUpSocket from './socket' 120 | 121 | // [at the very end of the file] 122 | setUpSocket(store) 123 | ``` 124 | 125 | As you can see, we pass the Redux store to `setUpSocket`. This way whenever a Websocket message coming from the server should alter the client's Redux state, we can `dispatch` actions. We are not going to `dispatch` anything in this example though. 126 | 127 | - Create a `src/client/socket.js` file containing: 128 | 129 | ```js 130 | // @flow 131 | 132 | import socketIOClient from 'socket.io-client' 133 | 134 | import { 135 | IO_CONNECT, 136 | IO_DISCONNECT, 137 | IO_CLIENT_HELLO, 138 | IO_CLIENT_JOIN_ROOM, 139 | IO_SERVER_HELLO, 140 | } from '../shared/config' 141 | 142 | const socket = socketIOClient(window.location.host) 143 | 144 | /* eslint-disable no-console */ 145 | // eslint-disable-next-line no-unused-vars 146 | const setUpSocket = (store: Object) => { 147 | socket.on(IO_CONNECT, () => { 148 | console.log('[socket.io] Connected.') 149 | socket.emit(IO_CLIENT_JOIN_ROOM, 'hello-1234') 150 | socket.emit(IO_CLIENT_HELLO, 'Hello!') 151 | }) 152 | 153 | socket.on(IO_SERVER_HELLO, (serverMessage) => { 154 | console.log(`[socket.io] Server: ${serverMessage}`) 155 | }) 156 | 157 | socket.on(IO_DISCONNECT, () => { 158 | console.log('[socket.io] Disconnected.') 159 | }) 160 | } 161 | /* eslint-enable no-console */ 162 | 163 | export default setUpSocket 164 | ``` 165 | 166 | What happens here should not be surprising if you understood well what we did on the server: 167 | 168 | - As soon as the client is connected, we log it in the browser console and join the room `hello-1234` with a `IO_CLIENT_JOIN_ROOM` message. 169 | - We then send `Hello!` with a `IO_CLIENT_HELLO` message. 170 | - If the server sends us a `IO_SERVER_HELLO` message, we log it in the browser console. 171 | - We also log any disconnection. 172 | 173 | 🏁 Run `yarn start` and `yarn dev:wds`, open `http://localhost:8000`. Then, open your browser console, and also look at the terminal of your Express server. You should see the Websocket communication between your client and server. 174 | 175 | Next section: [08 - Bootstrap, JSS](08-bootstrap-jss.md#readme) 176 | 177 | Back to the [previous section](06-react-router-ssr-helmet.md#readme) or the [table of contents](https://github.com/verekia/js-stack-from-scratch#table-of-contents). 178 | -------------------------------------------------------------------------------- /tutorial/01-node-yarn-package-json.md: -------------------------------------------------------------------------------- 1 | # 01 - Node, Yarn, and `package.json` 2 | 3 | Code for this chapter available [here](https://github.com/verekia/js-stack-walkthrough/tree/master/01-node-yarn-package-json). 4 | 5 | In this section we will set up Node, Yarn, a basic `package.json` file, and try a package. 6 | 7 | ## Node 8 | 9 | > 💡 **[Node.js](https://nodejs.org/)** is a JavaScript runtime environment. It is mostly used for Back-End development, but also for general scripting. In the context of Front-End development, it can be used to perform a whole bunch of tasks like linting, testing, and assembling files. 10 | 11 | We will use Node for basically everything in this tutorial, so you're going to need it. Head to the [download page](https://nodejs.org/en/download/current/) for **macOS** or **Windows** binaries, or the [package manager installations page](https://nodejs.org/en/download/package-manager/) for Linux distributions. 12 | 13 | For instance, on **Ubuntu / Debian**, you would run the following commands to install Node: 14 | 15 | ```sh 16 | curl -sL https://deb.nodesource.com/setup_7.x | sudo -E bash - 17 | sudo apt-get install -y nodejs 18 | ``` 19 | 20 | You want any version of Node > 6.5.0. 21 | 22 | ## Node Version Management Tools 23 | 24 | If you need the flexibility to use multiple versions of Node, check out [NVM](https://github.com/creationix/nvm) or [tj/n](https://github.com/tj/n). 25 | 26 | ## NPM 27 | 28 | NPM is the default package manager for Node. It is automatically installed alongside with Node. Package managers are used to install and manage packages (modules of code that you or someone else wrote). We are going to use a lot of packages in this tutorial, but we'll use Yarn, another package manager. 29 | 30 | ## Yarn 31 | 32 | > 💡 **[Yarn](https://yarnpkg.com/)** is a Node.js package manager which is much faster than NPM, has offline support, and fetches dependencies [more predictably](https://yarnpkg.com/en/docs/yarn-lock). 33 | 34 | Since it [came out](https://code.facebook.com/posts/1840075619545360) in October 2016, it received a very quick adoption and may soon become the package manager of choice of the JavaScript community. If you want to stick to NPM you can simply replace all `yarn add` and `yarn add --dev` commands of this tutorial by `npm install --save` and `npm install --save-dev`. 35 | 36 | Install Yarn by following the [instructions](https://yarnpkg.com/en/docs/install) for your OS. I would recommend using the **Installation Script** from the *Alternatives* tab if you are on macOS or Unix, to [avoid](https://github.com/yarnpkg/yarn/issues/1505) relying on other package managers: 37 | 38 | ```sh 39 | curl -o- -L https://yarnpkg.com/install.sh | bash 40 | ``` 41 | 42 | ## `package.json` 43 | 44 | > 💡 **[package.json](https://yarnpkg.com/en/docs/package-json)** is the file used to describe and configure your JavaScript project. It contains general information (your project name, version, contributors, license, etc), configuration options for tools you use, and even a section to run *tasks*. 45 | 46 | - Create a new folder to work in, and `cd` in it. 47 | - Run `yarn init` and answer the questions (`yarn init -y` to skip all questions), to generate a `package.json` file automatically. 48 | 49 | Here is the basic `package.json` I'll use in this tutorial: 50 | 51 | ```json 52 | { 53 | "name": "your-project", 54 | "version": "1.0.0", 55 | "license": "MIT" 56 | } 57 | ``` 58 | 59 | ## Hello World 60 | 61 | - Create an `index.js` file containing `console.log('Hello world')` 62 | 63 | 🏁 Run `node .` in this folder (`index.js` is the default file Node looks for in a folder). It should print "Hello world". 64 | 65 | **Note**: See that 🏁 racing flag emoji? I will use it every time you reach a **checkpoint**. We are sometimes going to make a lot of changes in a row, and your code may not work until you reach the next checkpoint. 66 | 67 | ## `start` script 68 | 69 | Running `node .` to execute our program is a bit too low-level. We are going to use an NPM/Yarn script to trigger the execution of that code instead. That will give us a nice abstraction to be able to always use `yarn start`, even when our program gets more complicated. 70 | 71 | - In `package.json`, add a `scripts` object like so: 72 | 73 | ```json 74 | { 75 | "name": "your-project", 76 | "version": "1.0.0", 77 | "license": "MIT", 78 | "scripts": { 79 | "start": "node ." 80 | } 81 | } 82 | ``` 83 | 84 | `start` is the name we give to the *task* that will run our program. We are going to create a lot of different tasks in this `scripts` object throughout this tutorial. `start` is typically the name given to the default task of an application. Some other standard task names are `stop` and `test`. 85 | 86 | `package.json` must be a valid JSON file, which means that you cannot have trailing commas. So be careful when editing manually your `package.json` file. 87 | 88 | 🏁 Run `yarn start`. It should print `Hello world`. 89 | 90 | ## Git and `.gitignore` 91 | 92 | - Initialize a Git repository with `git init` 93 | 94 | - Create a `.gitignore` file and add the following to it: 95 | 96 | ```gitignore 97 | .DS_Store 98 | /*.log 99 | ``` 100 | 101 | `.DS_Store` files are auto-generated macOS files that you should never have in your repository. 102 | 103 | `npm-debug.log` and `yarn-error.log` are files that are created when your package manager encounters an error, we don't want them versioned in our repository. 104 | 105 | ## Installing and using a package 106 | 107 | In this section we will install and use a package. A "package" is simply a piece of code that someone else wrote, and that you can use in your own code. It can be anything. Here, we're going to try a package that helps you manipulate colors for instance. 108 | 109 | - Install the community-made package called `color` by running `yarn add color` 110 | 111 | Open `package.json` to see how Yarn automatically added `color` in `dependencies`. 112 | 113 | A `node_modules` folder has been created to store the package. 114 | 115 | - Add `node_modules/` to your `.gitignore` 116 | 117 | You will also notice that a `yarn.lock` file got generated by Yarn. You should commit this file to your repository, as it will ensure that everyone in your team uses the same version of your packages. If you're sticking to NPM instead of Yarn, the equivalent of this file is the *shrinkwrap*. 118 | 119 | - Write the following to your `index.js` file: 120 | 121 | ```js 122 | const color = require('color') 123 | 124 | const redHexa = color({ r: 255, g: 0, b: 0 }).hex() 125 | 126 | console.log(redHexa) 127 | ``` 128 | 129 | 🏁 Run `yarn start`. It should print `#FF0000`. 130 | 131 | Congratulations, you installed and used a package! 132 | 133 | `color` is just used in this section to teach you how to use a simple package. We won't need it anymore, so you can uninstall it: 134 | 135 | - Run `yarn remove color` 136 | 137 | ## Two kinds of dependencies 138 | 139 | There are two kinds of package dependencies, `"dependencies"` and `"devDependencies"`: 140 | 141 | **Dependencies** are libraries you need for your application to function (React, Redux, Lodash, jQuery, etc). You install them with `yarn add [package]`. 142 | 143 | **Dev Dependencies** are libraries used during development or to build your application (Webpack, SASS, linters, testing frameworks, etc). You install those with `yarn add --dev [package]`. 144 | 145 | Next section: [02 - Babel, ES6, ESLint, Flow, Jest, Husky](02-babel-es6-eslint-flow-jest-husky.md#readme) 146 | 147 | Back to the [table of contents](https://github.com/verekia/js-stack-from-scratch#table-of-contents). 148 | -------------------------------------------------------------------------------- /tutorial/03-express-nodemon-pm2.md: -------------------------------------------------------------------------------- 1 | # 03 - Express, Nodemon, and PM2 2 | 3 | Code for this chapter available [here](https://github.com/verekia/js-stack-walkthrough/tree/master/03-express-nodemon-pm2). 4 | 5 | In this section we are going to create the server that will render our web app. We will also set up a development mode and a production mode for this server. 6 | 7 | ## Express 8 | 9 | > 💡 **[Express](http://expressjs.com/)** is by far the most popular web application framework for Node. It provides a very simple and minimal API, and its features can be extended with *middleware*. 10 | 11 | Let's set up a minimal Express server to serve an HTML page with some CSS. 12 | 13 | - Delete everything inside `src` 14 | 15 | Create the following files and folders: 16 | 17 | - Create a `public/css/style.css` file containing: 18 | 19 | ```css 20 | body { 21 | width: 960px; 22 | margin: auto; 23 | font-family: sans-serif; 24 | } 25 | 26 | h1 { 27 | color: limegreen; 28 | } 29 | ``` 30 | 31 | - Create an empty `src/client/` folder. 32 | 33 | - Create an empty `src/shared/` folder. 34 | 35 | This folder is where we put *isomorphic / universal* JavaScript code – files that are used by both the client and the server. A great use case of shared code is *routes*, as you will see later in this tutorial when we'll make an asynchronous call. Here we simply have some configuration constants as an example for now. 36 | 37 | - Create a `src/shared/config.js` file, containing: 38 | 39 | ```js 40 | // @flow 41 | 42 | export const WEB_PORT = process.env.PORT || 8000 43 | export const STATIC_PATH = '/static' 44 | export const APP_NAME = 'Hello App' 45 | ``` 46 | 47 | If the Node process used to run your app has a `process.env.PORT` environment variable set (that's the case when you deploy to Heroku for instance), it will use this for the port. If there is none, we default to `8000`. 48 | 49 | - Create a `src/shared/util.js` file containing: 50 | 51 | ```js 52 | // @flow 53 | 54 | // eslint-disable-next-line import/prefer-default-export 55 | export const isProd = process.env.NODE_ENV === 'production' 56 | ``` 57 | 58 | That's a simple util to test if we are running in production mode or not. The `// eslint-disable-next-line import/prefer-default-export` comment is because we only have one named export here. You can remove it as you add other exports in this file. 59 | 60 | - Run `yarn add express compression` 61 | 62 | `compression` is an Express middleware to activate Gzip compression on the server. 63 | 64 | - Create a `src/server/index.js` file containing: 65 | 66 | ```js 67 | // @flow 68 | 69 | import compression from 'compression' 70 | import express from 'express' 71 | 72 | import { APP_NAME, STATIC_PATH, WEB_PORT } from '../shared/config' 73 | import { isProd } from '../shared/util' 74 | import renderApp from './render-app' 75 | 76 | const app = express() 77 | 78 | app.use(compression()) 79 | app.use(STATIC_PATH, express.static('dist')) 80 | app.use(STATIC_PATH, express.static('public')) 81 | 82 | app.get('/', (req, res) => { 83 | res.send(renderApp(APP_NAME)) 84 | }) 85 | 86 | app.listen(WEB_PORT, () => { 87 | // eslint-disable-next-line no-console 88 | console.log(`Server running on port ${WEB_PORT} ${isProd ? '(production)' : '(development)'}.`) 89 | }) 90 | ``` 91 | 92 | Nothing fancy here, it's almost Express' Hello World tutorial with a few additional imports. We're using 2 different static file directories here. `dist` for generated files, `public` for declarative ones. 93 | 94 | - Create a `src/server/render-app.js` file containing: 95 | 96 | ```js 97 | // @flow 98 | 99 | import { STATIC_PATH } from '../shared/config' 100 | 101 | const renderApp = (title: string) => 102 | ` 103 | 104 |
105 |156 | 157 |
158 |Open your browser console.
165 |Hover me.
441 |Resize the window.
442 | 443 |