├── .travis.yml ├── .gitignore ├── img ├── chai.png ├── flow.png ├── jest.png ├── js.png ├── npm.png ├── pm2.png ├── yarn.png ├── eslint.png ├── mocha.png ├── react.png ├── redux.png ├── js-padded.png ├── webpack.png ├── flow-padded.png ├── jest-padded.png ├── pm2-padded.png ├── yarn-padded.png ├── eslint-padded.png ├── flow-padded-90.png ├── jest-padded-90.png ├── pm2-padded-90.png ├── react-padded.png ├── react-router.png ├── redux-padded.png ├── webpack-padded.png ├── yarn-padded-90.png ├── bootstrap-padded.png ├── eslint-padded-90.png ├── react-padded-90.png ├── redux-padded-90.png ├── bootstrap-padded-90.png ├── react-router-padded.png ├── webpack-padded-90.png └── react-router-padded-90.png ├── package.json ├── .github └── ISSUE_TEMPLATE ├── mdlint.js ├── LICENSE.md ├── CHANGELOG.md ├── how-to-translate.md ├── yarn.lock ├── README.md └── tutorial ├── 07-socket-io.md ├── 09-travis-coveralls-heroku.md ├── 01-node-yarn-package-json.md ├── 03-express-nodemon-pm2.md ├── 04-webpack-react-hmr.md ├── 08-bootstrap-jss.md ├── 06-react-router-ssr-helmet.md ├── 02-babel-es6-eslint-flow-jest-husky.md └── 05-redux-immutable-fetch.md /.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/chai.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fbertone/guida-javascript-moderno/HEAD/img/chai.png -------------------------------------------------------------------------------- /img/flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fbertone/guida-javascript-moderno/HEAD/img/flow.png -------------------------------------------------------------------------------- /img/jest.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fbertone/guida-javascript-moderno/HEAD/img/jest.png -------------------------------------------------------------------------------- /img/js.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fbertone/guida-javascript-moderno/HEAD/img/js.png -------------------------------------------------------------------------------- /img/npm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fbertone/guida-javascript-moderno/HEAD/img/npm.png -------------------------------------------------------------------------------- /img/pm2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fbertone/guida-javascript-moderno/HEAD/img/pm2.png -------------------------------------------------------------------------------- /img/yarn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fbertone/guida-javascript-moderno/HEAD/img/yarn.png -------------------------------------------------------------------------------- /img/eslint.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fbertone/guida-javascript-moderno/HEAD/img/eslint.png -------------------------------------------------------------------------------- /img/mocha.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fbertone/guida-javascript-moderno/HEAD/img/mocha.png -------------------------------------------------------------------------------- /img/react.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fbertone/guida-javascript-moderno/HEAD/img/react.png -------------------------------------------------------------------------------- /img/redux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fbertone/guida-javascript-moderno/HEAD/img/redux.png -------------------------------------------------------------------------------- /img/js-padded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fbertone/guida-javascript-moderno/HEAD/img/js-padded.png -------------------------------------------------------------------------------- /img/webpack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fbertone/guida-javascript-moderno/HEAD/img/webpack.png -------------------------------------------------------------------------------- /img/flow-padded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fbertone/guida-javascript-moderno/HEAD/img/flow-padded.png -------------------------------------------------------------------------------- /img/jest-padded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fbertone/guida-javascript-moderno/HEAD/img/jest-padded.png -------------------------------------------------------------------------------- /img/pm2-padded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fbertone/guida-javascript-moderno/HEAD/img/pm2-padded.png -------------------------------------------------------------------------------- /img/yarn-padded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fbertone/guida-javascript-moderno/HEAD/img/yarn-padded.png -------------------------------------------------------------------------------- /img/eslint-padded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fbertone/guida-javascript-moderno/HEAD/img/eslint-padded.png -------------------------------------------------------------------------------- /img/flow-padded-90.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fbertone/guida-javascript-moderno/HEAD/img/flow-padded-90.png -------------------------------------------------------------------------------- /img/jest-padded-90.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fbertone/guida-javascript-moderno/HEAD/img/jest-padded-90.png -------------------------------------------------------------------------------- /img/pm2-padded-90.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fbertone/guida-javascript-moderno/HEAD/img/pm2-padded-90.png -------------------------------------------------------------------------------- /img/react-padded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fbertone/guida-javascript-moderno/HEAD/img/react-padded.png -------------------------------------------------------------------------------- /img/react-router.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fbertone/guida-javascript-moderno/HEAD/img/react-router.png -------------------------------------------------------------------------------- /img/redux-padded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fbertone/guida-javascript-moderno/HEAD/img/redux-padded.png -------------------------------------------------------------------------------- /img/webpack-padded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fbertone/guida-javascript-moderno/HEAD/img/webpack-padded.png -------------------------------------------------------------------------------- /img/yarn-padded-90.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fbertone/guida-javascript-moderno/HEAD/img/yarn-padded-90.png -------------------------------------------------------------------------------- /img/bootstrap-padded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fbertone/guida-javascript-moderno/HEAD/img/bootstrap-padded.png -------------------------------------------------------------------------------- /img/eslint-padded-90.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fbertone/guida-javascript-moderno/HEAD/img/eslint-padded-90.png -------------------------------------------------------------------------------- /img/react-padded-90.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fbertone/guida-javascript-moderno/HEAD/img/react-padded-90.png -------------------------------------------------------------------------------- /img/redux-padded-90.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fbertone/guida-javascript-moderno/HEAD/img/redux-padded-90.png -------------------------------------------------------------------------------- /img/bootstrap-padded-90.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fbertone/guida-javascript-moderno/HEAD/img/bootstrap-padded-90.png -------------------------------------------------------------------------------- /img/react-router-padded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fbertone/guida-javascript-moderno/HEAD/img/react-router-padded.png -------------------------------------------------------------------------------- /img/webpack-padded-90.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fbertone/guida-javascript-moderno/HEAD/img/webpack-padded-90.png -------------------------------------------------------------------------------- /img/react-router-padded-90.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fbertone/guida-javascript-moderno/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 | # Guida alla creazione di un progetto Javascript Moderno 2 | 3 | [![Build Status](https://travis-ci.org/verekia/js-stack-from-scratch.svg?branch=master)](https://travis-ci.org/verekia/js-stack-from-scratch) 4 | [![Release](https://img.shields.io/github/release/verekia/js-stack-from-scratch.svg?style=flat-square)](https://github.com/verekia/js-stack-from-scratch/releases) 5 | [![Dependencies](https://img.shields.io/david/verekia/js-stack-boilerplate.svg?style=flat-square)](https://david-dm.org/verekia/js-stack-boilerplate) 6 | [![Dev Dependencies](https://img.shields.io/david/dev/verekia/js-stack-boilerplate.svg?style=flat-square)](https://david-dm.org/verekia/js-stack-boilerplate?type=dev) 7 | [![Gitter](https://img.shields.io/gitter/room/js-stack-from-scratch/Lobby.svg?style=flat-square)](https://gitter.im/js-stack-from-scratch/) 8 | 9 | [![React](/img/react-padded-90.png)](https://facebook.github.io/react/) 10 | [![Redux](/img/redux-padded-90.png)](http://redux.js.org/) 11 | [![React Router](/img/react-router-padded-90.png)](https://github.com/ReactTraining/react-router) 12 | [![Flow](/img/flow-padded-90.png)](https://flowtype.org/) 13 | [![ESLint](/img/eslint-padded-90.png)](http://eslint.org/) 14 | [![Jest](/img/jest-padded-90.png)](https://facebook.github.io/jest/) 15 | [![Yarn](/img/yarn-padded-90.png)](https://yarnpkg.com/) 16 | [![Webpack](/img/webpack-padded-90.png)](https://webpack.github.io/) 17 | [![Bootstrap](/img/bootstrap-padded-90.png)](http://getbootstrap.com/) 18 | 19 | Benvenuto a questa guida: **Guida alla creazione di un progetto Javascript Moderno**. 20 | 21 | > 🎉 **Questa è la seconda versione della guida, grosse modifiche sono state fatte rispetto alla prima. Controlla il [Change Log](/CHANGELOG.md)!** 22 | 23 | Questa è una guida essenziale per la costruzione di uno stack Javascript utilizzando gli strumenti più attuali. Richiede qualche conoscienza generica di programmazione ed alcune basi di Javascript. **È focalizzata sull'utilizzo in sequenza di alcuni tool** e ti presenta **l'esempio più semplice possibile** per ogni tool. Puoi vedere questa guida come *un modo per scrivere il tuo boilerplate personale partendo da zero*. Siccome l'obiettivo di questa guida è l'utilizzo combinato di una serie di tool, non ti spiegherò nel dettaglio come funziona ciascun tool da solo. Fai riferimento alla loro documentazione o cerca altri tutorial se ti interessa approfondire maggiormente. 24 | 25 | Chiaramente non avrai bisogno di utilizzare tutto lo stack se devi solo creare una semplice pagina web con poche interazioni in JS (una combinazione di Browserify/Webpack + Babel + jQuery è sufficiente per poter scrivere codice ES6 in file separati), ma se vuoi creare una web app scalabile, ed hai bisogno di un aiuto per configurare il tutto, questo tutorial farà proprio al tuo caso. 26 | 27 | Una buona parte dello stack descritto in questa guida utilizza React. Se sei un principiante e vuoi solo imparare React, il progetto [create-react-app](https://github.com/facebookincubator/create-react-app) ti permetterà di avere un ambiente React funzionante molto rapidamente, utilizzando una configurazione già pronta. Consiglierei questa strada a chi ad esempio entra in un team che utilizza già React e deve mettersi al passo utilizzando un ambiente semplice per fare delle prove. In questa guida non ti fornirò una configurazione già pronta perchè voglio che tu comprenda tutto il procedimento che c'è dietro. 28 | 29 | Esempi di codice sono inclusi in ogni capitolo, puoi avviarli utilizzando i comandi `yarn && yarn start`. Ti consiglio comunque di riscrivere tutto per conto tuo seguendo le **istruzioni passo a passo**. 30 | 31 | Il codice completo è disponibile in questo repository: [JS-Stack-Boilerplate](https://github.com/verekia/js-stack-boilerplate) e nelle [release](https://github.com/verekia/js-stack-from-scratch/releases). C'è anche una [demo live](https://js-stack.herokuapp.com/) 32 | 33 | Funziona su Linux, macOS, e Windows. 34 | 35 | > **Nota**: Da quando questo tutorial è stato aggiornato a maggio 2017, alcune librerie hanno leggermente modificato le loro API. 95% del tutorial è ancora perfettamente valido, ma se riscontri dei problemi, assicurati di controllare gli [open issues](https://github.com/verekia/js-stack-from-scratch/issues?q=is%3Aopen+is%3Aissue+label%3Abug). 36 | 37 | [01 - Node, Yarn, `package.json`](/tutorial/01-node-yarn-package-json.md#readme) 38 | 39 | [02 - Babel, ES6, ESLint, Flow, Jest, Husky](/tutorial/02-babel-es6-eslint-flow-jest-husky.md#readme) 40 | 41 | [03 - Express, Nodemon, PM2](/tutorial/03-express-nodemon-pm2.md#readme) 42 | 43 | [04 - Webpack, React, HMR](/tutorial/04-webpack-react-hmr.md#readme) 44 | 45 | [05 - Redux, Immutable, Fetch](/tutorial/05-redux-immutable-fetch.md#readme) 46 | 47 | [06 - React Router, Server-Side Rendering, Helmet](/tutorial/06-react-router-ssr-helmet.md#readme) 48 | 49 | [07 - Socket.IO](/tutorial/07-socket-io.md#readme) 50 | 51 | [08 - Bootstrap, JSS](/tutorial/08-bootstrap-jss.md#readme) 52 | 53 | [09 - Travis, Coveralls, Heroku](/tutorial/09-travis-coveralls-heroku.md#readme) 54 | 55 | ## Prossimamente 56 | 57 | Impostare il tuo editor (Atom inizialmente), MongoDB, Progressive Web App, E2E testing. 58 | 59 | ## Traduzioni 60 | 61 | Se vuoi aggiungere la tua traduzione, leggi le [raccomandazioni](/how-to-translate.md) per iniziare! 62 | 63 | ### V2 64 | 65 | Presto nuove traduzioni ;) 66 | 67 | - [Bulgarian](https://github.com/mihailgaberov/js-stack-from-scratch) by [mihailgaberov](http://github.com/mihailgaberov) 68 | - [Chinese (simplified)](https://github.com/yepbug/js-stack-from-scratch/) by [@yepbug](https://github.com/yepbug) 69 | - [French](https://github.com/naomihauret/js-stack-from-scratch/) by [Naomi Hauret](https://twitter.com/naomihauret) 70 | - [Italian](https://github.com/fbertone/guida-javascript-moderno) by [Fabrizio Bertone](https://github.com/fbertone) - [fbertone.it](http://fbertone.it) 71 | 72 | Controlla le [traduzioni in corso](https://github.com/verekia/js-stack-from-scratch/issues/147). 73 | 74 | ### V1 75 | 76 | - [Chinese (simplified)](https://github.com/pd4d10/js-stack-from-scratch) by [@pd4d10](http://github.com/pd4d10) 77 | - [Italian](https://github.com/fbertone/js-stack-from-scratch) by [Fabrizio Bertone](https://github.com/fbertone) 78 | - [Japanese](https://github.com/takahashim/js-stack-from-scratch) by [@takahashim](https://github.com/takahashim) 79 | - [Russian](https://github.com/UsulPro/js-stack-from-scratch) by [React Theming](https://github.com/sm-react/react-theming) 80 | - [Thai](https://github.com/MicroBenz/js-stack-from-scratch) by [MicroBenz](https://github.com/MicroBenz) 81 | 82 | ## Credits 83 | 84 | Versione originale creata da [@verekia](https://twitter.com/verekia) – [verekia.com](http://verekia.com/). 85 | Traduzione di [Fabrizio Bertone](https://github.com/fbertone) – [fbertone.it](http://fbertone.it/). 86 | 87 | Licenza: MIT 88 | -------------------------------------------------------------------------------- /tutorial/07-socket-io.md: -------------------------------------------------------------------------------- 1 | # 07 - Socket.IO 2 | 3 | Il codice di questo capitolo è disponibile [qua](https://github.com/verekia/js-stack-walkthrough/tree/master/07-socket-io). 4 | 5 | > 💡 **[Socket.IO](https://github.com/socketio/socket.io)** è una libreria per utilizzare in modo semplice i Websocket. Fornisce delle comode API e supporta delle soluzioni alternative per i browser che non implementano i nativamente Websocket. 6 | 7 | In questo capitolo configureremo un semplice scambio di messaggi tra il client ed il server. Per non aggiungere ulteriori pagine e component – che sarebbero scollegati dalle funzionalità che ci interessano – implementeremo questo scambio di messaggi direttamente nella console del browser. Niente UI per questo capitolo. 8 | 9 | - Esegui `yarn add socket.io socket.io-client` 10 | 11 | ## Server-side 12 | 13 | - Modifica `src/server/index.js` in questo modo: 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 | Nota che per permettere a Socket.IO di funzionare, devi usare `Server` di `http` in modalità `listen` per le richieste in arrivo, e non `app` di Express. Per fortuna non cambia molto nel codice. Tutti i dettagli dei Websocket sono configurati in un file esterno, richiamato da `setUpSocket`. 48 | 49 | - Aggiungi le seguenti costanti in `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 | Questi sono i *tipi di messaggi* che il client ed il server si scambieranno. Ti consiglio di usare come prefisso `IO_CLIENT` o `IO_SERVER` per rendere chiaro *chi* sta inviando il messaggio. Altrimenti le cose possono farsi parecchio confuse quando hai molti tipi di messaggio. 60 | 61 | Come puoi vedere, abbiamo un `IO_CLIENT_JOIN_ROOM`, perchè come dimostrazione faremo in modo che i client si colleghino ad una room (come una chatroom). Le room sono utili per inviare i messaggi in broadcast ad una serie di utenti. 62 | 63 | - Crea `src/server/socket.js` contenente: 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, in questo file abbiamo implementato *come il server dovrebbe reagire quando dei client si collegano o inviano dei messaggi*: 105 | 106 | - Quando il client si connette, lo logghiamo nella console del server, ed utilizziamo l'oggetto `socket`, attraverso il quale possiamo comunicare con il client. 107 | - Quando un client invia `IO_CLIENT_JOIN_ROOM`, lo facciamo accedere alla `room` che desidera. Quando si è collegato ad una room, inviamo 3 messaggi: 1 un messaggio ad ogni utente, 1 messaggio agli utenti della room, 1 messaggio solo a quel client. 108 | - Quando il client invia `IO_CLIENT_HELLO`, logghiamo il messaggio nella console del server. 109 | - Quando il client si disconnette, logghiamo anche questo evento. 110 | 111 | ## Client-side 112 | 113 | Il lato client sarà molto simile. 114 | 115 | - Modifica `src/client/index.jsx` così: 116 | 117 | ```js 118 | // [...] 119 | import setUpSocket from './socket' 120 | 121 | // [at the very end of the file] 122 | setUpSocket(store) 123 | ``` 124 | 125 | Come puoi vedere, passiamo lo store di Redux a `setUpSocket`. In questo modo quando un messaggio Websocket che arriva dal server deve modificare lo stato Redux del client, possiamo fare il `dispatch` delle azioni. Tuttavia in questo esempio non eseguiremo il `dispatch` di niente. 126 | 127 | - Crea `src/client/socket.js` contenente: 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 | Quello che succede qua non dovrebbe sorprenderti se hai capido quello che abbiamo fatto nel server: 167 | 168 | - Appena il client si collega, lo logghiamo nella console del browser ed entriamo in `hello-1234` tramite un messaggio `IO_CLIENT_JOIN_ROOM`. 169 | - Successivamente inviamo `Hello!` tramite un messaggio `IO_CLIENT_HELLO`. 170 | - Se il server ci invia un messaggio `IO_SERVER_HELLO`, lo logghiamo nella console del browser. 171 | - Logghiamo anche ogni eventuale disconnessione. 172 | 173 | 🏁 Esegui `yarn start` e `yarn dev:wds`, poi apri `http://localhost:8000`. Apri poi la console del browser, e controlla la console del server Express. Dovresti vedere la comunicazione tramite Websocket tra il client ed il server. 174 | 175 | Prossimo capitolo: [08 - Bootstrap, JSS](08-bootstrap-jss.md#readme) 176 | 177 | Torna al [capitolo precedente](06-react-router-ssr-helmet.md#readme) o all'[indice dei contenuti](https://github.com/fbertone/guida-javascript-moderno#indice-dei-contenuti). 178 | -------------------------------------------------------------------------------- /tutorial/09-travis-coveralls-heroku.md: -------------------------------------------------------------------------------- 1 | # 09 - Travis, Coveralls, ed Heroku 2 | 3 | Il codice di questo capitolo è disponibile nel branch `master` del repository [JS-Stack-Boilerplate](https://github.com/verekia/js-stack-boilerplate). 4 | 5 | In questo capitolo integreremo l'app con servizi di terze parti. Questi servizi offrono piani gratuiti e a pagamento. È un po' controverso utilizzare servizi di questo tipo in un tutorial che fa leva unicamente su tool opensource sviluppati dalla comunità, per questo motivo verranno mantenuti 2 branch separati del repository [JS-Stack-Boilerplate](https://github.com/verekia/js-stack-boilerplate), `master` e `master-no-services`. 6 | 7 | ## Travis 8 | 9 | > 💡 **[Travis CI](https://travis-ci.org/)** è una piattaforma popolare di continuous integration, gratuita per progetti open source. 10 | 11 | Se il tuo progetto è disponibile apertamente su Github, l'integrazione con Travis è molto semplice. Innanzitutto autenticati su Travis utilizzando il tuo account Github, e aggiungi il tuo repository. 12 | 13 | - Poi crea il file `.travis.yml` contenente: 14 | 15 | ```yaml 16 | language: node_js 17 | node_js: node 18 | script: yarn test && yarn prod:build 19 | ``` 20 | 21 | Travis si accorgerà automaticamente che utilizzi Yarn perchè è presente il file `yarn.lock`. Ogni volta che fai un push del codice sul tuo repository in Github, eseguirà `yarn test && yarn prod:build`. Se niente va storto, dovresti vedere una build verde. 22 | 23 | ## Coveralls 24 | 25 | > 💡 **[Coveralls](https://coveralls.io)** è un servizio che mantiene una cronologia e delle statistiche sulla copertura dei test. 26 | 27 | Se il tuo progetto è open-source su Github e compatibile con i servizi di Continuous Integration supportati da Coveralls, l'unica cosa che devi fare è inviare in pipe il file di copertura generato da Jest all'eseguibile di `coveralls`. 28 | 29 | - Esegui `yarn add --dev coveralls` 30 | 31 | - Modifica la sezione `script` di `.travis.yml` in questo modo: 32 | 33 | ```yaml 34 | script: yarn test && yarn prod:build && cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js 35 | ``` 36 | 37 | Adesso, ogni volta che Travis farà una build, sottometterà automaticamente le informazioni di copertura dei test di Jest a Coveralls. 38 | 39 | ## Badges 40 | 41 | È tutto verde su Travis e Coveralls? Ottimo, mostralo al mondo con dei badge scintillanti! 42 | 43 | Puoi usare direttamente il codice fornito da Travis o Coveralls, oppure utilizzare [shields.io](http://shields.io/) per rendere omogenei o personalizzare i badge. Usiamo shields.io. 44 | 45 | - Crea o modifica il tuo `README.md` in questo modo: 46 | 47 | ```md 48 | [![Build Status](https://img.shields.io/travis/GITHUB-USERNAME/GITHUB-REPO.svg?style=flat-square)](https://travis-ci.org/GITHUB-USERNAME/GITHUB-REPO) 49 | [![Coverage Status](https://img.shields.io/coveralls/GITHUB-USERNAME/GITHUB-REPO.svg?style=flat-square)](https://coveralls.io/github/GITHUB-USERNAME/GITHUB-REPO?branch=master) 50 | ``` 51 | 52 | Ovviamente, sostituisci `GITHUB-USERNAME/GITHUB-REPO` con il tuo username di Github ed il nome del repository. 53 | 54 | ## Heroku 55 | 56 | > 💡 **[Heroku](https://www.heroku.com/)** è una [PaaS](https://en.wikipedia.org/wiki/Platform_as_a_service) su cui puoi effettuare il deploy. Si occuperà dei dettagli dell'infrastruttura, in modo che tu possa focalizzarti sullo sviluppo dell'app senza preoccuparti di quello che succede al di sotto. 57 | 58 | Questo tutorial non è sponsorizzato in alcun modo da Heroku, ma essendo Heroku una piattaforma che sa il fatto suo, ti spegherò come puoi farci il deploy dell'app. Questo è il tipo di affetto gratuito che ricevi quando costruisci un ottimo prodotto. 59 | 60 | **Nota**: Una sezione su AWS potrebbe essere aggiunta in questo capitolo successivamente, ma una cosa alla volta. 61 | 62 | ### Web setup 63 | 64 | - Se non l'hai ancora fatto, installa la [CLI di Heroku](https://devcenter.heroku.com/articles/getting-started-with-nodejs) ed effettua il log in. 65 | 66 | - Vai nella tua [Dashboard di Heroku](https://dashboard.heroku.com/) e crea 2 app, una chiamata `your-project` e un'altra chiamata ad esempio `your-project-staging`. 67 | 68 | Lasceremo che Heroku si occupi di effettuare il transpiling del nostro codice ES6/Flow tramite Babel, e generi dei bundle per i client tramite Webpack. Ma siccome quelle sono delle `devDependencies`, Yarn non le installerà in un ambiente di produzione come Heroku. Cambiamo questo comportamente utilizzando la variabile d'ambiente `NPM_CONFIG_PRODUCTION`. 69 | 70 | - In entrambe le app, sotto Settings > Config Variables, aggiunge `NPM_CONFIG_PRODUCTION` impostata a `false`. 71 | 72 | - Crea una Pipeline, e permetti ad Heroku l'accesso al tuo Github. 73 | 74 | - Aggiungi le 2 app alla pipeline, imposta quella di staging in modo che venga effettuato un auto-deploy in seguito ai cambiamenti in `master`, e abilita Review Apps. 75 | 76 | Ok, prepariamo il nostro progetto per il deploy su Heroku. 77 | 78 | ### Esecuzione in modalità produzione in locale 79 | 80 | - Crea il file `.env` contenente: 81 | 82 | ```.env 83 | NODE_ENV='production' 84 | PORT='8000' 85 | ``` 86 | 87 | È all'interno di questo file che dovresti inserire le variabili locali e segrete. Non inserirle nei commit in un repository pubblico se il tuo progetto è privato. 88 | 89 | - Aggiungi `/.env` al tuo `.gitignore` 90 | 91 | - Crea il file `Procfile` contenente: 92 | 93 | ```Procfile 94 | web: node lib/server 95 | ``` 96 | 97 | Qua è dove specifichiamo il punto d'ingresso del nostro server. 98 | 99 | Non useremo più PM2, useremo al suo posto `heroku local` per lanciare in modalità produzione in locale. 100 | 101 | - Esegui `yarn remove pm2` 102 | 103 | - Modilica lo script `prod:start` in `package.json`: 104 | 105 | ```json 106 | "prod:start": "heroku local", 107 | ``` 108 | 109 | - Rimuovi `prod:stop` da `package.json`. Non ci servirà più siccome `heroku local` è un processo che rimane appeso e possiamo killare con Ctrl+C, a differenza di `pm2 start`. 110 | 111 | 🏁 Esegui `yarn prod:build` e `yarn prod:start`. Dovrebbe far partire il server e visualizzare i log. 112 | 113 | ### Deploy in produzione 114 | 115 | - Aggiungi la seguente linea a `scripts` in `package.json`: 116 | 117 | ```json 118 | "heroku-postbuild": "yarn prod:build", 119 | ``` 120 | 121 | `heroku-postbuild` è un task che verrà eseguito ogni volta che effettuerai il deploy di un'app su Heroku. 122 | 123 | Probabilmente vorrai anche specificare una versione precisa di Node o Yarn da utilizzare su Heroku. 124 | 125 | - Aggiungi quanto segue a `package.json`: 126 | 127 | ```json 128 | "engines": { 129 | "node": "7.x", 130 | "yarn": "0.20.3" 131 | }, 132 | ``` 133 | 134 | - Crea `app.json` contenente: 135 | 136 | ```json 137 | { 138 | "env": { 139 | "NPM_CONFIG_PRODUCTION": "false" 140 | } 141 | } 142 | ``` 143 | 144 | Questo è da utilizzare in Review App. 145 | 146 | Adesso dovresti essere pronto per effettuare i deploy tramite la Pipeline di Heroku. 147 | 148 | 🏁 Crea un nuovo branch di git, fai delle modifiche e apri Github Pull Request per istanziare una Review App. Verifica le tue modifiche in Review App URL e se ti sembra tutto corretto, fai il merge della tua Pull Request sul `master` in Github. Dopo alcuni minuti, la tua app di staging dovrebbe essere stata inviata in deploy automaticamente. Verifica le modifiche nello staging app URL, e se ti sembra che vada ancora tutto bene, promuovi lo staging in produzione. 149 | 150 | Hai finito! Congratulazioni se hai terminato questo tutorial partendo da zero. 151 | 152 | Ti meriti questa medaglia emoji: 🏅 153 | 154 | Torna al [capitolo precedente](08-bootstrap-jss.md#readme) o all'[indice dei contenuti](https://github.com/fbertone/guida-javascript-moderno#indice-dei-contenuti). 155 | -------------------------------------------------------------------------------- /tutorial/01-node-yarn-package-json.md: -------------------------------------------------------------------------------- 1 | # 01 - Node, Yarn, e `package.json` 2 | 3 | Il codice per questo capitolo è disponibile [qua](https://github.com/verekia/js-stack-walkthrough/tree/master/01-node-yarn-package-json). 4 | 5 | In questa sezione configureremo Node, Yarn, un file `package.json` di base, e proveremo un package. 6 | 7 | ## Node 8 | 9 | > 💡 **[Node.js](https://nodejs.org/)** è un ambiente di runtime per JavaScript. Viene utilizzato principalmente per lo sviluppo di Back-End, ma anche come ambiente di scripting in generale. Nel contesto dello sviluppo di Front-End, può essere utilizzato per l'esecuzione di tutta una serie di task, come il linting, esecuzione di test, e manipolazione dei file. 10 | 11 | Utilizzeremo Node praticamente per tutto il tutorial, quindi ne avrai bisogno. Vai alla pagina di [download](https://nodejs.org/en/download/current/) per l'installazione in **macOS** o **Windows**, o alla pagina di [installazione tramite package manager](https://nodejs.org/en/download/package-manager/) per le distribuzioni Linux. 12 | 13 | Ad esempio, in **Ubuntu / Debian**, eseguiresti questi comandi per installare 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 | Ti serve una versione di Node > 6.5.0. 21 | 22 | ## Node Version Management Tools 23 | 24 | Se ti serve la flessibilità di poter utilizzare versioni multiple di Node, installa [NVM](https://github.com/creationix/nvm)) oppure [tj/n](https://github.com/tj/n). 25 | 26 | ## NPM 27 | 28 | NPM è il package manager di default per Node. Viene automaticamente installato insieme a Node. I package manager sono utilizzati per installare e gestire i pacchetti (moduli di codice scritti da te o da altri). Utilizzeremo molti pacchetti in questo tutorial, ma ci serviremo di Yarn, un package manager differente. 29 | 30 | ## Yarn 31 | 32 | > 💡 **[Yarn](https://yarnpkg.com/)** è un package manager per Node.js che è molto più veloce di NPM, ha il supporto offline, e gestisce le dipendenza in modo più [predicibile](https://yarnpkg.com/en/docs/yarn-lock). 33 | 34 | Da quando è stato [rilasciato](https://code.facebook.com/posts/1840075619545360) ad ottobre 2016, è stato adottato molto rapidamente e potrebbe diventare presto il package manager di riferimento per la comunità JavaScript. Se preferisci rimanere con NPM puoi semplicemente sostituire tutti i comandi `yarn add` e `yarn add --dev` con `npm install --save` e `npm install --save-dev`. 35 | 36 | Installa Yarn seguendo le [istruzioni](https://yarnpkg.com/en/docs/install) per il tuo OS. Se utilizzi macOS o Unix, ti consiglio di usare lo script **Installation Script** che trovi nella scheda *Alternatives* per [evitare](https://github.com/yarnpkg/yarn/issues/1505) di essere dipendente da altri package manager: 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)** è il file che serve a descrivere e configurare i progetti JavaScript. Contiene delle informazioni generali (il nome del progetto, la versione, i contributor, la licenza, etc), opzioni di configurazione dei tool che usi, e anche una sezione per l'esecuzione di *task*. 45 | 46 | - Crea una cartella di progetto ed entraci da console (`cd`). 47 | - Esegui `yarn init` e rispondi alle domande (`yarn init -y` per saltare tutte le domande), per generare automaticamente il file `package.json`. 48 | 49 | Ecco il contenuto del file `package.json` che useremo per questo 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 | - Crea un file `index.js` contenente `console.log('Hello world')` 62 | 63 | 🏁 Esegui `node .` in questa cartella (`index.js` è il file che Node cerca di default per l'esecuzione). Dovrebbe scrivere "Hello world". 64 | 65 | **Nota**: Vedi quella bandierina 🏁 ? La inserirò ogni volta che raggiungi un **checkpoint**. A volte faremo molte modifiche di seguito, e il codice potrebbe non funzionare fino al raggiungimento del checkpoint successivo. 66 | 67 | ## `start` script 68 | 69 | Scrivere `node .` per eseguire il nostro programma è un po' troppo di basso livello. Utilizzeremo uno script NPM/Yarn per far partire l'esecuzione del codice. Questo ci permetterà di avere un buon livello di astrazione e poter sempre eseguire `yarn start`, anche quando il nostro programma diventerà più complesso. 70 | 71 | - All'interno di `package.json`, aggiungi un oggetto `scripts` in questo modo: 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` è il nome che diamo al *task* che farà partire il nostro programma. Creeremo molti task differenti in questo oggetto `scripts` nel corso di questo tutorial. `start` tipicamente è il nome che viene dato al task di default. Altri nomi di task standard sono `stop` e `test`. 85 | 86 | `package.json` deve essere un file JSON valido, il che significa che non può avere delle virgole finali. Devi quindi fare attenzione quando modifichi a mano il file `package.json`. 87 | 88 | 🏁 Lancia `yarn start`. Dovrebbe venire scritto `Hello world`. 89 | 90 | ## Git e `.gitignore` 91 | 92 | - Inizializza un repository Git con `git init` 93 | 94 | - Crea un file `.gitignore` ed inserisci al suo interno quanto segue: 95 | 96 | ```gitignore 97 | .DS_Store 98 | /*.log 99 | ``` 100 | 101 | I file `.DS_Store` sono dei file di macOS che vengono generati automaticamente e non dovresti mai avere inclusi nel tuo repository. 102 | 103 | `npm-debug.log` e `yarn-error.log` sono dei file che vengono creati quando il package manager riscontra un errore, non li vogliamo includere nel repository. 104 | 105 | ## Installare ed utilizzare un package 106 | 107 | In questa sezione vedremo come installare ed utilizzare un package. Un "package" è semplicemente del codice scritto da altri, che puoi riutilizzare nel tuo codice. Può essere qualunque cosa. Adesso ad esempio utilizzeremo un package che può aiutarti ad utilizzare i codici dei colori. 108 | 109 | - Installa il package chiamato `color` attraverso il comando `yarn add color` 110 | 111 | Apri `package.json` per vedere come Yarn ha automaticamente aggiunto `color` nelle `dependencies`. 112 | 113 | Una cartella `node_modules` è stata creata per contenere il package. 114 | 115 | - Aggiungi `node_modules/` al file `.gitignore` 116 | 117 | Noterai anche che un file `yarn.lock` è stato generato da Yarn. Dovresti includere questo file nel repository, perchè farà in modo da assicurare che ogni persona del tuo team utilizzi le stesse versioni dei package. Se stai utilizzando NPM invece di Yarn, l'equivalente di questo file si chiama *shrinkwrap*. 118 | 119 | - Scrivi quanto segue nel file `index.js`: 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 | 🏁 Esegui `yarn start`. Dovrebbe scrivere `#FF0000`. 130 | 131 | Congratulazioni, hai installato ed utilizzato un package! 132 | 133 | `color` è stato utilizzato in questa sezione unicamente per farti vedere come puoi utilizzare un semplice package. Non lo utilizzeremo più in seguito, puoi quindi disintallarlo: 134 | 135 | - Esegui `yarn remove color` 136 | 137 | ## Due tipi di dipendenze 138 | 139 | Ci sono due tipi di dipendenze di package, `"dependencies"` e `"devDependencies"`: 140 | 141 | **Dependencies** sono librerie che sono necessarie per il funzionamento dell'applicazione (React, Redux, Lodash, jQuery, etc). Le installi con il comando `yarn add [package]`. 142 | 143 | **Dev Dependencies** sono librerie utilizzate durante lo sviluppo o per fare il build dell'applicazione (Webpack, SASS, linter, framework di testing, etc). Queste vanno installate utilizzando il comando `yarn add --dev [package]`. 144 | 145 | Prossimo capitolo: [02 - Babel, ES6, ESLint, Flow, Jest, Husky](02-babel-es6-eslint-flow-jest-husky.md#readme) 146 | 147 | Torna all'[indice dei contenuti](https://github.com/fbertone/guida-javascript-moderno#indice-dei-contenuti). 148 | -------------------------------------------------------------------------------- /tutorial/03-express-nodemon-pm2.md: -------------------------------------------------------------------------------- 1 | # 03 - Express, Nodemon, e PM2 2 | 3 | Il codice per questo capitolo è disponibile [qua](https://github.com/verekia/js-stack-walkthrough/tree/master/03-express-nodemon-pm2). 4 | 5 | In questa sezione creeremo il server che si occuperà del render della nostra web app. Configureremo inoltre per il server una modalità di sviluppo ed una di produzione. 6 | 7 | ## Express 8 | 9 | > 💡 **[Express](http://expressjs.com/)** è di gran lunga il framework web più famoso per Node. Dispone di un'API molto semplice e minimale, e le sue funzionalità possono essere estese tramite *middleware*. 10 | 11 | Configuriamo un server Express per servire una pagina HTML page con del CSS. 12 | 13 | - Cancella tutto in `src` 14 | 15 | Crea i seguenti file e cartelle: 16 | 17 | - Crea un file `public/css/style.css` con questo contenuto: 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 | - Crea una cartella vuota `src/client/`. 32 | 33 | - Crea una cartella vuota `src/shared/`. 34 | 35 | Questa cartella è dove inseriremo il codice Javascript *isomorfico / universale* – file che verranno utilizzati sia dal client che dal server. Un buon esempio di codice condiviso sono le *routes*, come vedrai più avanti quando faremo delle chiamate asincrone. Per il momento impostiamo solo alcune constanti di configurazione come esempio. 36 | 37 | - Crea un file `src/shared/config.js` contenente: 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 | Se il processo di Node utilizzato per eseguire la tua applicazione ha una variabile d'ambiente `process.env.PORT` impostata (ad esempio quando fai il deploy su Heroku), utilizzarà il valore di questa variabile come porta. Se non è configurata, utilizziamo come default la `8000`. 48 | 49 | - Crea il file `src/shared/util.js` contenente: 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 | Questo è un modo semplice per verificare se siamo in produzione o no. Il commento `// eslint-disable-next-line import/prefer-default-export` ci serve perchè abbiamo un solo export in questo caso. Potrai rimuoverlo quando aggiungerai nuove export in questo file. 59 | 60 | - Esegui `yarn add express compression` 61 | 62 | `compression` è un middleware di Express per attivare la compressione Gzip sul server. 63 | 64 | - Crea il file `src/server/index.js` contenente: 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 | Niente di particolare qua, è quasi il tutorial "Hello World" di Express con alcune import aggiuntive. Stiamo utilizzando 2 directory differenti per i file statici. `dist` per i file generati, `public` per quelli fissi. 93 | 94 | - Crea il file `src/server/render-app.js` contenente: 95 | 96 | ```js 97 | // @flow 98 | 99 | import { STATIC_PATH } from '../shared/config' 100 | 101 | const renderApp = (title: string) => 102 | ` 103 | 104 | 105 | ${title} 106 | 107 | 108 | 109 |

${title}

110 | 111 | 112 | ` 113 | 114 | export default renderApp 115 | ``` 116 | 117 | Hai presente quando si utilizzavano i *template* sul back-end? Beh, adesso sono diventati abbastanza obsoleti con l'introduzione delle stringhe template in Javascript. Abbiamo una funzione che prende `title` come parametro e lo inietta nei tag `title` e `h1` della pagina, ritornando l'HTML completo. Usiamo anche la costante `STATIC_PATH` come percorso di base per tutte le risorse statiche. 118 | 119 | ### Sintax highlighting delle stringhe template HTML in Atom (opzionale) 120 | 121 | Potrebbe essere possibile attivare la syntax highlighting per il codice HTML presente all'interno delle stringhe template in base al tuo editor. In Atom, se inserisci in tag `html` come prefisso in una stringa template (o un qualsiasi tag che *termina* con `html`, come `ilovehtml`), verrà automaticamente attivato l'highlight del contenuto di quella stringa. A volte uso il tag `html` della libreria `common-tags` per utilizzare questa funzione: 122 | 123 | ```js 124 | import { html } from `common-tags` 125 | 126 | const template = html` 127 |
Wow, colors!
128 | ` 129 | ``` 130 | 131 | Non ho inserito questa tecnica nel boilerplate di questo tutorial siccome funziona solo in Atom, e non è proprio l'ideale. Alcuni utenti di Atom potrebbero comunque trovarlo utile. 132 | 133 | Comunque, torniamo al lavoro! 134 | 135 | - In `package.json` modifica il script `start` in questo modo: `"start": "babel-node src/server",` 136 | 137 | 🏁 Esegui `yarn start`, e apri `localhost:8000` nel browser. Se tutto funziona correttamente dovresti vedere una pagina bianca con scritto "Hello App" sia come titolo della scheda del browser che in verde come titolo della pagina. 138 | 139 | **Nota**: Alcuni processi – tipicamente processi che attendono l'avvenimento di eventi, com un'istanza di un server – ti impediscono di inserire comandi nel terminale fino a quando non hanno terminato l'esecusione. Per chiuderli e tornare alla linea di comando, premi **Ctrl+C**. Alternativamente puoi aprire un altro terminale, in questo modo potrai lanciare comandi mentre l'altro processo è ancora in esecuzione. Puoi anche mandare i processi in background, ma questo è fuori dal contesto del tutorial. 140 | 141 | ## Nodemon 142 | 143 | > 💡 **[Nodemon](https://nodemon.io/)** è un'utility che permette di riavviare automaricamente il server Node quando vengono modificati dei file all'interno della cartella. 144 | 145 | Useremo Nodemon quando siamo in modalità **sviluppo**. 146 | 147 | - Lancia `yarn add --dev nodemon` 148 | 149 | - Modifica `scripts` in questo modo: 150 | 151 | ```json 152 | "start": "yarn dev:start", 153 | "dev:start": "nodemon --ignore lib --exec babel-node src/server", 154 | ``` 155 | 156 | `start` è adesso semplicemente un puntatore ad un altro task, `dev:start`. Questo ci permette di avere un livello di astrazione rispetto al task di default da eseguire con il comando start. 157 | 158 | In `dev:start`, il parametro `--ignore lib` serve a *non* riavviare il server quando avvengono delle modifiche all'interno della cartella `lib`. Per il momento questa cartella non esiste ancora, ma la creeremo nella prossima sezione di questo capitolo, avrà quindi presto significato. Nodemon tipicamente utilizza l'eseguibile `node`. Nel nostro caso, siccome utilizziamo Babel, possiamo dire a Nodemon di utilizzare invece il comando `babel-node`. In questo modo continuerà ad interpretare tutto il codice ES6/Flow. 159 | 160 | 🏁 Lancia `yarn start` e apri `localhost:8000`. Modifica la costante `APP_NAME` in `src/shared/config.js`, che dovrebbe far riavviare il processo del server. Aggiorna la pagina per vedere il nuovo titolo. Nota che questo riavvio automatico del server è differente dalla funzionalità di *Hot Module Replacement* (sostituzione dei moduli a caldo), che è quando i componenti della pagina vengono aggiornati in tempo reale. In questo caso dobbiamo continuare ad aggiornare la pagina a mano, ma almeno non dobbiamo preoccuparci di terminare il server e rilanciarlo. L'Hot Module Replacement sarà introdotto del prossimo capitolo. 161 | 162 | ## PM2 163 | 164 | > 💡 **[PM2](http://pm2.keymetrics.io/)** è un Process Manager (gestore di processi) per Node. Mantiene i tuoi processi attivi in produzione, e offre molte funzionalità per gestirli e monitorarli. 165 | 166 | Useremo PM2 in modalità **produzione**. 167 | 168 | - Esegui `yarn add --dev pm2` 169 | 170 | In produzione, vuoi che il server sia il più performante possibile. `babel-node` lancia l'intero processo di transpilazione di Babel transpilation ad ogni esecuzione, il che non è qualcosa desiderabile in produzione. Abbiamo bisogno che Babel faccia tutto il lavoro in anticipo, e il nostro server utilizzi direttamente i file codificati in ES5. 171 | 172 | Una delle funzionalità principali di Babel è di prendere una intera cartella di codice ES6 (tipicamente chiamata `src`) e transpilarla in una cartella contenente codice ES5 (tipicamente chiamata `lib`). 173 | 174 | Essendo questa cartella `lib` generata automaticamente, è una buona pratica ripulirla automaticamente prima di una nuova compilazione, siccome potrebbe contenere dei vecchi file non più necessari. Un semplice package per cancellare file con supporto multipiattaforma è `rimraf`. 175 | 176 | - Esegui `yarn add --dev rimraf` 177 | 178 | Aggiungiamo il seguente task `prod:build` ai nostri `scripts`: 179 | 180 | ```json 181 | "prod:build": "rimraf lib && babel src -d lib --ignore .test.js", 182 | ``` 183 | 184 | - Esegui `yarn prod:build`, dovrebbe generare una cartella `lib` contenente il codice transpilato, esclusi i file che terminano per `.test.js` (nota che anche i file `.test.jsx` vengono ignorati quando si utilizza questo parametro). 185 | 186 | - Aggiungi `/lib/` al tuo `.gitignore` 187 | 188 | Un'ultima cosa: passeremo una variabile d'ambiente `NODE_ENV` all'eseguibile di PM2. In Unix, faresti questo utilizzando la sintassi `NODE_ENV=production pm2`, ma in Windows è differente. Utilizzeremo un piccolo package chiamato `cross-env` per fare in modo che questa sintassi funzioni anche in Windows. 189 | 190 | - Lancia `yarn add --dev cross-env` 191 | 192 | Aggiorniamo `package.json` in questo modo: 193 | 194 | ```json 195 | "scripts": { 196 | "start": "yarn dev:start", 197 | "dev:start": "nodemon --ignore lib --exec babel-node src/server", 198 | "prod:build": "rimraf lib && babel src -d lib --ignore .test.js", 199 | "prod:start": "cross-env NODE_ENV=production pm2 start lib/server && pm2 logs", 200 | "prod:stop": "pm2 delete server", 201 | "test": "eslint src && flow && jest --coverage", 202 | "precommit": "yarn test", 203 | "prepush": "yarn test" 204 | }, 205 | ``` 206 | 207 | 🏁 Lancia `yarn prod:build`, poi esegui `yarn prod:start`. PM2 dovrebbe mostrare un processo attivo. Vai su `http://localhost:8000/` nel tuo browser e dovresti vedere la tua app. Il terminale dovrebbe mostrare il log, che dovrebbe essere "Server running on port 8000 (production).". Nota che con PM2, i tuoi processi vengono eseguiti in background. Se premi Ctrl+C, terminerai il comando `pm2 logs`, che era il nostro ultimo comando nella catena di `prod:start`, ma il server dovrebbe continuare a funzionare. Se vuoi chiudere il server, esegui `yarn prod:stop` 208 | 209 | Adesso che abbiamo un task `prod:build`, sarebbe ottimale fare in modo che venga verificato il corretto funzionamento del codice prima di eseguire la push nel repository. Siccome è probabilmente inutile eseguire questa verifica ad ogni commit, suggerisco di aggiungerla nel task `prepush`: 210 | 211 | ```json 212 | "prepush": "yarn test && yarn prod:build" 213 | ``` 214 | 215 | 🏁 Esegui `yarn prepush` o semplicemente lancia la push per far partire il processo di test. 216 | 217 | **Nota**: Non abbiamo nessun test qua, quindi Jest si lamenterà un po'. Ignoralo per il momento. 218 | 219 | Prossimo capitolo: [04 - Webpack, React, HMR](04-webpack-react-hmr.md#readme) 220 | 221 | Torna al [capitolo precedente](02-babel-es6-eslint-flow-jest-husky.md#readme) o all'[indice dei contenuti](https://github.com/fbertone/guida-javascript-moderno#indice-dei-contenuti). 222 | -------------------------------------------------------------------------------- /tutorial/04-webpack-react-hmr.md: -------------------------------------------------------------------------------- 1 | # 04 - Webpack, React, e Hot Module Replacement 2 | 3 | Il codice per questo capitolo si trova [qua](https://github.com/verekia/js-stack-walkthrough/tree/master/04-webpack-react-hmr). 4 | 5 | ## Webpack 6 | 7 | > 💡 **[Webpack](https://webpack.js.org/)** è un *module bundler*. Si occupa di prendere un insieme di vari file sorgenti, processarli, e assemblarli in un unico (solitamente) file JavaScript chiamato bundle, che è l'unico file che il client eseguirà. 8 | 9 | Creiamo un semplice *hello world* e facciamone il bundle con Webpack. 10 | 11 | - In `src/shared/config.js`, aggiungi queste costanti: 12 | 13 | ```js 14 | export const WDS_PORT = 7000 15 | 16 | export const APP_CONTAINER_CLASS = 'js-app' 17 | export const APP_CONTAINER_SELECTOR = `.${APP_CONTAINER_CLASS}` 18 | ``` 19 | 20 | - Crea il file `src/client/index.js` contenente: 21 | 22 | ```js 23 | import 'babel-polyfill' 24 | 25 | import { APP_CONTAINER_SELECTOR } from '../shared/config' 26 | 27 | document.querySelector(APP_CONTAINER_SELECTOR).innerHTML = '

Hello Webpack!

' 28 | ``` 29 | 30 | Se vuoi utilizzare alcune delle funzionalità ES più recenti nel codice del client, ad esempio le `Promise`, devi includere il [Polyfill di Babel](https://babeljs.io/docs/usage/polyfill/) prima di qualunque altra cosa all'interno del bundle. 31 | 32 | - Esegui `yarn add babel-polyfill` 33 | 34 | Se lanci ESLint su questo file, segnalerà che `document` è undefined. 35 | 36 | - Aggiungi il campo `env` nel file `.eslintrc.json` per permettere l'utilizzo di `window` e `document`: 37 | 38 | ```json 39 | "env": { 40 | "browser": true, 41 | "jest": true 42 | } 43 | ``` 44 | 45 | Bene, adesso dobbiamo fare il bundle di questa applicazione client ES6 in un bundle ES5. 46 | 47 | - Crea il file `webpack.config.babel.js` contenente: 48 | 49 | ```js 50 | // @flow 51 | 52 | import path from 'path' 53 | 54 | import { WDS_PORT } from './src/shared/config' 55 | import { isProd } from './src/shared/util' 56 | 57 | export default { 58 | entry: [ 59 | './src/client', 60 | ], 61 | output: { 62 | filename: 'js/bundle.js', 63 | path: path.resolve(__dirname, 'dist'), 64 | publicPath: isProd ? '/static/' : `http://localhost:${WDS_PORT}/dist/`, 65 | }, 66 | module: { 67 | rules: [ 68 | { test: /\.(js|jsx)$/, use: 'babel-loader', exclude: /node_modules/ }, 69 | ], 70 | }, 71 | devtool: isProd ? false : 'source-map', 72 | resolve: { 73 | extensions: ['.js', '.jsx'], 74 | }, 75 | devServer: { 76 | port: WDS_PORT, 77 | }, 78 | } 79 | ``` 80 | 81 | Questo file serve a descrivere in che modo assemblare il nostro bundle: `entry` è il punto di accesso della nostra app, `output.filename` è il nome del file di bundle da generare, `output.path` e `output.publicPath` descrivono la cartella di destinazione e l'URL. Mettiamo il bundle nella cartella `dist`, che conterrà i file generati automaticamente (a differenza del CSS che abbiamo creato in precedenza e che si trova in `public`). `module.rules` è dove spieghi a Webpack di applicare alcune operazioni a certi tipi di file. Qua diciamo che vogliamo che tutti i file `.js` e `.jsx` (per React) ad esclusione di quelli in `node_modules` passino attraverso un modulo chiamato `babel-loader`. Vogliamo inoltre che queste due estensioni vengano utilizzate per la risoluzione dei moduli (`resolve`) quando ne facciamo un `import`. Infine definiamo una porta per il Webpack Dev Server. 82 | 83 | **Nota**: L'estensione `.babel.js` è una funzionalità di Webpack che permette di applicare le trasformazioni di Babel a questo file di configurazione. 84 | 85 | `babel-loader` è un plugin per Webpack che transpila il codice esattamente come abbiamo fatto dall'inizio di questo tutorial. L'unica differenza è che adesso il codice verrà eseguito all'interno del browser invece che nel server. 86 | 87 | - Esegui `yarn add --dev webpack webpack-dev-server babel-core babel-loader` 88 | 89 | `babel-core` è una dipendenza di `babel-loader`, quindi dobbiamo installarlo. 90 | 91 | - Aggiungi `/dist/` al file `.gitignore` 92 | 93 | ### Update dei Task 94 | 95 | In modalità di sviluppo utilizzeremo `webpack-dev-server` per sfruttare le funzionalità di Hot Module Reloading (più avanti in questo capitolo), mentre in produzione utilizzeremo semplicemente `webpack` per generare i bundle. In entrambe i casi, il parametro `--progress` viene utilizzato per mostrare informazioni aggiuntive quando Webpack sta compilando i file. In produzione passeremo a `webpack` anche il parametro `-p` per minificare il codice, e la variabile `NODE_ENV` impostata a `production`. 96 | 97 | Aggiorniamo gli `scripts` per implementare il tutto, e migliorare anche altri task: 98 | 99 | ```json 100 | "scripts": { 101 | "start": "yarn dev:start", 102 | "dev:start": "nodemon -e js,jsx --ignore lib --ignore dist --exec babel-node src/server", 103 | "dev:wds": "webpack-dev-server --progress", 104 | "prod:build": "rimraf lib dist && babel src -d lib --ignore .test.js && cross-env NODE_ENV=production webpack -p --progress", 105 | "prod:start": "cross-env NODE_ENV=production pm2 start lib/server && pm2 logs", 106 | "prod:stop": "pm2 delete server", 107 | "lint": "eslint src webpack.config.babel.js --ext .js,.jsx", 108 | "test": "yarn lint && flow && jest --coverage", 109 | "precommit": "yarn test", 110 | "prepush": "yarn test && yarn prod:build" 111 | }, 112 | ``` 113 | 114 | In `dev:start` dichiariamo esplicitamente le estensioni da monitorare, `.js` e `.jsx`, e aggiungiamo `dist` alle cartelle da ignorare. 115 | 116 | Creiamo un task `lint` separato e aggiungiamo `webpack.config.babel.js` ai file su cui effettuare il lint. 117 | 118 | - Adesso creiamo il contenitore per la nostra app in `src/server/render-app.js`, ed includiamo il bundle che verrà generato: 119 | 120 | ```js 121 | // @flow 122 | 123 | import { APP_CONTAINER_CLASS, STATIC_PATH, WDS_PORT } from '../shared/config' 124 | import { isProd } from '../shared/util' 125 | 126 | const renderApp = (title: string) => 127 | ` 128 | 129 | 130 | ${title} 131 | 132 | 133 | 134 |
135 | 136 | 137 | 138 | ` 139 | 140 | export default renderApp 141 | ``` 142 | 143 | In base all'ambiente in cui ci troviamo, includeremo o il bundle del Webpack Dev Server, oppure quello di produzione. Nota che il percorso per il bundle del Webpack Dev Server è *virtuale*, `dist/js/bundle.js` non viene di fatto letto dall'hard disk in modalità sviluppo. È inoltre necessario configurare il Webpack Dev Server in modo da utilizzare una porta differente da quella del tuo server web principale. 144 | 145 | - Infine, in `src/server/index.js`, modifica il messaggio della `console.log` in questo modo: 146 | 147 | ```js 148 | console.log(`Server running on port ${WEB_PORT} ${isProd ? '(production)' : 149 | '(development).\nKeep "yarn dev:wds" running in an other terminal'}.`) 150 | ``` 151 | 152 | Questo suggerirà agli altri sviluppatori cosa fare nel caso eseguissero `yarn start` senza il Webpack Dev Server. 153 | 154 | OK, abbiamo modificato molte cose, vediamo se tutto continua a funzionare come dovrebbe: 155 | 156 | 🏁 Lancia `yarn start` in un terminale. Apri un altro terminale, ed esegui `yarn dev:wds`. Quando il Webpack Dev Server ha terminato di generare il bundle ed i sourcemap (che dovrebbero essere entrambi file di ~600kB) ed entrambe i processi sono fermi nei rispettivi terminal, apri `http://localhost:8000/` e dovresti vedere "Hello Webpack!". Apri la console di Chrome e nella scheda Sources controlla quali file sono inclusi. Dovresti vedere semplicemente `static/css/style.css` sotto `localhost:8000/`, e tutti i file ES6 sotto `webpack://./src`. Questo significa che i sourcemap stanno funzionando. Nell'editor, in `src/client/index.js`, prova a cambiare `Hello Webpack!` in qualcos'altro. Dopo aver salvato il file, Webpack Dev Server nel terminale dovrebbe generare un nuovo bundle e la scheda di Chrome dovrebbe aggiornarsi automaticamente. 157 | 158 | - Killa i processi precedenti nei terminali premento Ctrl+C, poi esegui `yarn prod:build`, e successivamente `yarn prod:start`. Apri `http://localhost:8000/` e dovresti vedere "Hello Webpack!". Nella scheda Sources della console di Chrome, questa volta dovresti trovare `static/js/bundle.js` sotto a `localhost:8000/`, ma nessun sorgente sotto `webpack://`. Clicca su `bundle.js` e assicurati che sia minificato. Esegui `yarn prod:stop`. 159 | 160 | Ottimo lavoro, lo so che è stato un po' intenso. Ti meriti una pausa! La prossima sezione è più semplice. 161 | 162 | **Nota**: Suggerirei di avere almeno 3 terminali aperti, uno per il server Express, uno per il Webpack Dev Server, e uno per Git, i test, e comandi generali come installare pacchetti con `yarn`. Idealmente, dovresti dividere lo schermo dei terminali in più pannelli in modo da poterli vedere tutti contemporaneamente. 163 | 164 | ## React 165 | 166 | > 💡 **[React](https://facebook.github.io/react/)** è una libreria per costruire interfacce utente sviluppata da Facebook. Utilizza la sintassi **[JSX](https://facebook.github.io/react/docs/jsx-in-depth.html)** per rappresentare elementi HTML e componenti sfruttando la potenza di JavaScript. 167 | 168 | In questa sezione faremo il rendering di un po' di testo con React e JSX. 169 | 170 | Prima di tutto installiamo React e ReactDOM: 171 | 172 | - Esegui `yarn add react react-dom` 173 | 174 | Rinomina `src/client/index.js` in `src/client/index.jsx` e scrivici del codice per React: 175 | 176 | ```js 177 | // @flow 178 | 179 | import 'babel-polyfill' 180 | 181 | import React from 'react' 182 | import ReactDOM from 'react-dom' 183 | 184 | import App from './app' 185 | import { APP_CONTAINER_SELECTOR } from '../shared/config' 186 | 187 | ReactDOM.render(, document.querySelector(APP_CONTAINER_SELECTOR)) 188 | ``` 189 | 190 | - Crea il file `src/client/app.jsx` contenente: 191 | 192 | ```js 193 | // @flow 194 | 195 | import React from 'react' 196 | 197 | const App = () =>

Hello React!

198 | 199 | export default App 200 | ``` 201 | 202 | Siccome stiamo usando la sintassi JSX, dobbiamo dire a Babel che deve trasformare anche questo tramite il preset `babel-preset-react`. Mentre ci siamo aggiungiamo anche un plugin di Babel chiamato `flow-react-proptypes` che genera automaticamente i PropType per i component React a partire dalle annotazioni di Flow. 203 | 204 | - Esegui `yarn add --dev babel-preset-react babel-plugin-flow-react-proptypes` modifica il file `.babelrc` in questo modo: 205 | 206 | ```json 207 | { 208 | "presets": [ 209 | "env", 210 | "flow", 211 | "react" 212 | ], 213 | "plugins": [ 214 | "flow-react-proptypes" 215 | ] 216 | } 217 | ``` 218 | 219 | 🏁 Lancia `yarn start` e `yarn dev:wds` e apri `http://localhost:8000`. Dovresti vedere "Hello React!". 220 | 221 | Adesso prova a cambiare il testo in `src/client/app.jsx` in qualcos'altro. Webpack Dev Server dovrebbe ricaricare la pagina automaticamente, che è bello, ma lo renderemo ancora migliore. 222 | 223 | ## Hot Module Replacement 224 | 225 | > 💡 **[Hot Module Replacement](https://webpack.js.org/concepts/hot-module-replacement/)** (*HMR*) è una funzionalità di Webpack per rimpiazzare un modulo al volo senza dover ricaricare tutta la pagina. 226 | 227 | Per fare in modo che HMR funzioni con React, dovremo modificare alcune cose. 228 | 229 | - Esegui `yarn add react-hot-loader@next` 230 | 231 | - Modifica `webpack.config.babel.js` così: 232 | 233 | ```js 234 | import webpack from 'webpack' 235 | // [...] 236 | entry: [ 237 | 'react-hot-loader/patch', 238 | './src/client', 239 | ], 240 | // [...] 241 | devServer: { 242 | port: WDS_PORT, 243 | hot: true, 244 | headers: { 245 | 'Access-Control-Allow-Origin': '*', 246 | }, 247 | }, 248 | plugins: [ 249 | new webpack.optimize.OccurrenceOrderPlugin(), 250 | new webpack.HotModuleReplacementPlugin(), 251 | new webpack.NamedModulesPlugin(), 252 | new webpack.NoEmitOnErrorsPlugin(), 253 | ], 254 | ``` 255 | 256 | La configurazione `headers` serve a consentire Cross-Origin Resource Sharing, che è necessario per HMR. 257 | 258 | - Modifica `src/client/index.jsx`: 259 | 260 | ```js 261 | // @flow 262 | 263 | import 'babel-polyfill' 264 | 265 | import React from 'react' 266 | import ReactDOM from 'react-dom' 267 | import { AppContainer } from 'react-hot-loader' 268 | 269 | import App from './app' 270 | import { APP_CONTAINER_SELECTOR } from '../shared/config' 271 | 272 | const rootEl = document.querySelector(APP_CONTAINER_SELECTOR) 273 | 274 | const wrapApp = AppComponent => 275 | 276 | 277 | 278 | 279 | ReactDOM.render(wrapApp(App), rootEl) 280 | 281 | if (module.hot) { 282 | // flow-disable-next-line 283 | module.hot.accept('./app', () => { 284 | // eslint-disable-next-line global-require 285 | const NextApp = require('./app').default 286 | ReactDOM.render(wrapApp(NextApp), rootEl) 287 | }) 288 | } 289 | ``` 290 | 291 | Dobbiamo rendere `App` un figlio dell'`AppContainer` di `react-hot-loader`, e dobbiamo fare una `require` della versione successiva di `App` quando facciamo l'hot-reloading. Per rendere questo processo pulito ed efficiente, creiamo una funzione `wrapApp` che utilizziamo in entrambe i punti in cui occorre fare il render di `App`. Sentiti libero di spostare `eslint-disable global-require` in cima al file per renderlo più leggibile. 292 | 293 | 🏁 Riavvia il processo `yarn dev:wds` se era ancora in esecuzione. Apri `localhost:8000`. Nella scheda Console, dovresti vedere alcuni log relativi a HMR. Prosegui a modificare qualcosa in `src/client/app.jsx` e le modifiche dovrebbero comparire nel browser dopo qualche secondo, senza dover ricaricare l'intera pagina! 294 | 295 | Prossimo capitolo: [05 - Redux, Immutable, Fetch](05-redux-immutable-fetch.md#readme) 296 | 297 | Torna al [capitolo precedente](03-express-nodemon-pm2.md#readme) o all'[indice dei contenuti](https://github.com/fbertone/guida-javascript-moderno#indice-dei-contenuti). 298 | -------------------------------------------------------------------------------- /tutorial/08-bootstrap-jss.md: -------------------------------------------------------------------------------- 1 | # 08 - Bootstrap e JSS 2 | 3 | Il codice di questo capitolo è disponibile nel branch [`master-no-services`](https://github.com/verekia/js-stack-boilerplate/tree/master-no-services) del repository [JS-Stack-Boilerplate](https://github.com/verekia/js-stack-boilerplate). 4 | 5 | Ok! È arrivata l'ora di fare qualche ritocco estetico alla nostra app bruttina. Utilizzeremo Twitter Bootstrap per dargli qualche stile di base. Successivamente tramite una libreria CSS-in-JS aggiungeremo qualche stile personalizzato. 6 | 7 | ## Twitter Bootstrap 8 | 9 | > 💡 **[Twitter Bootstrap](http://getbootstrap.com/)** è una libreria di componenti per UI. 10 | 11 | Ci sono 2 possibilità per integrare Bootstrap in un'app React. Entrambe hanno dei pro e dei contro: 12 | 13 | - Utilizzando la versione ufficiale, **che usa jQuery e Tether** per il comportamento dei suoi componenti. 14 | - Utilizzando una libreria di terze parti ch re-implementa tutti i componenti di Bootstrap in React, come [React-Bootstrap](https://react-bootstrap.github.io/) oppure [Reactstrap](https://reactstrap.github.io/). 15 | 16 | Le librerie terze forniscono dei component React molto comodi che riducono notevolmente il codice inutile rispetto ai componenti HTML originali, e si integrano in modo ottimale con il tuo codebase React. Detto ciò, devo dire che io sono abbastanza restio ad utilizzarle, perchè saranno sempre *indietro* rispetto alle versioni ufficiali (a volte potenzialmente molto indietro). Inoltre non funzionano con i temi Bootstrap che implementano le loro funzionalità JS. Questo è uno svantaggio abbastanza pesante considerando che uno dei maggiori punti di fornza di Bootstrap è la sua grande comunità di designer che costruiscono degli ottimi temi. 17 | 18 | Per questa ragione, utilizzerò il compromesso di integrare la versione ufficiale, insieme a jQuery e Tether. Una delle preoccupazioni di questo approccio riguarda ovviamente la dimensione dei file risultanti. Per tua informazione, il bundle occupa circa 200KB (Gzip) con jQuery, Tether, ed i JS di Bootstrap inclusi. Penso che sia ragionevole, ma se per te è troppo, ti conviene considerare le altre opzioni per integrare Bootstrap, o addirittura non utilizzare proprio Bootstrap. 19 | 20 | ### I CSS di Bootstrap 21 | 22 | - Cancella `public/css/style.css` 23 | 24 | - Esegui `yarn add bootstrap@4.0.0-alpha.6` 25 | 26 | - Copia `bootstrap.min.css` e `bootstrap.min.css.map` da `node_modules/bootstrap/dist/css` nella tua cartella `public/css`. 27 | 28 | - Modifica `src/server/render-app.jsx` in questo modo: 29 | 30 | ```html 31 | 32 | ``` 33 | 34 | ### I JS di Bootstrap con jQuery e Tether 35 | 36 | Adesso che abbiamo gli stili di Bootstrap caricati nella nostra pagina, abbiamo bisogno dei JavaScript per il comportamento dei componenti. 37 | 38 | - Esegui `yarn add jquery tether` 39 | 40 | - Modifica `src/client/index.jsx` in questo modo: 41 | 42 | ```js 43 | import $ from 'jquery' 44 | import Tether from 'tether' 45 | 46 | // [right after all your imports] 47 | 48 | window.jQuery = $ 49 | window.Tether = Tether 50 | require('bootstrap') 51 | ``` 52 | 53 | Questo caricherà il codice Javascript di Bootstrap. 54 | 55 | ### I componenti di Bootstrap 56 | 57 | Bene, è arrivato il momento di fare il copia-incolla di un sacco di file. 58 | 59 | - Modifica `src/shared/component/page/hello-async.jsx` in questo modo: 60 | 61 | ```js 62 | // @flow 63 | 64 | import React from 'react' 65 | import Helmet from 'react-helmet' 66 | 67 | import MessageAsync from '../../container/message-async' 68 | import HelloAsyncButton from '../../container/hello-async-button' 69 | 70 | const title = 'Async Hello Page' 71 | 72 | const HelloAsyncPage = () => 73 |
74 | 81 |
82 |
83 |

{title}

84 | 85 | 86 |
87 |
88 |
89 | 90 | export default HelloAsyncPage 91 | ``` 92 | 93 | - Edit `src/shared/component/page/hello.jsx` like so: 94 | 95 | ```js 96 | // @flow 97 | 98 | import React from 'react' 99 | import Helmet from 'react-helmet' 100 | 101 | import Message from '../../container/message' 102 | import HelloButton from '../../container/hello-button' 103 | 104 | const title = 'Hello Page' 105 | 106 | const HelloPage = () => 107 |
108 | 115 |
116 |
117 |

{title}

118 | 119 | 120 |
121 |
122 |
123 | 124 | export default HelloPage 125 | ``` 126 | 127 | - Modifica `src/shared/component/page/home.jsx` in questo modo: 128 | 129 | ```js 130 | // @flow 131 | 132 | import React from 'react' 133 | import Helmet from 'react-helmet' 134 | 135 | import ModalExample from '../modal-example' 136 | import { APP_NAME } from '../../config' 137 | 138 | const HomePage = () => 139 |
140 | 146 |
147 |
148 |

{APP_NAME}

149 |
150 |
151 |
152 |
153 |
154 |

Bootstrap

155 |

156 | 157 |

158 |
159 |
160 |

JSS (soon)

161 |
162 |
163 |

Websockets

164 |

Open your browser console.

165 |
166 |
167 |
168 | 169 |
170 | 171 | export default HomePage 172 | ``` 173 | 174 | - Modifica `src/shared/component/page/not-found.jsx` in questo modo: 175 | 176 | ```js 177 | // @flow 178 | 179 | import React from 'react' 180 | import Helmet from 'react-helmet' 181 | import { Link } from 'react-router-dom' 182 | import { HOME_PAGE_ROUTE } from '../../routes' 183 | 184 | const title = 'Page Not Found!' 185 | 186 | const NotFoundPage = () => 187 |
188 | 189 |
190 |
191 |

{title}

192 |
Go to the homepage.
193 |
194 |
195 |
196 | 197 | export default NotFoundPage 198 | ``` 199 | 200 | - Modifica `src/shared/component/button.jsx` in questo modo: 201 | 202 | ```js 203 | // [...] 204 | 210 | // [...] 211 | ``` 212 | 213 | - Crea il file `src/shared/component/footer.jsx` contenente: 214 | 215 | ```js 216 | // @flow 217 | 218 | import React from 'react' 219 | import { APP_NAME } from '../config' 220 | 221 | const Footer = () => 222 |
223 |
224 |
225 |

© {APP_NAME} 2017

226 |
227 |
228 | 229 | export default Footer 230 | ``` 231 | 232 | - Crea il file `src/shared/component/modal-example.jsx` contenente: 233 | 234 | ```js 235 | // @flow 236 | 237 | import React from 'react' 238 | 239 | const ModalExample = () => 240 |
241 |
242 |
243 |
244 |
Modal title
245 | 246 |
247 |
248 | This is a Bootstrap modal. It uses jQuery. 249 |
250 |
251 | 252 |
253 |
254 |
255 |
256 | 257 | export default ModalExample 258 | ``` 259 | 260 | - Modifica `src/shared/app.jsx` in questo modo: 261 | 262 | ```js 263 | const App = () => 264 |
265 | ``` 266 | 267 | Questo è un esempio di *stile React inline*. 268 | 269 | Questo verrà convertito in: `
` nel DOM. Abbiamo bisogno di questo stile per spingere il contenuto sotto la barra di navigazione. Gli [stili React inline](https://speakerdeck.com/vjeux/react-css-in-js) sono un ottimo modo per isolare gli stili dei tuoi component dal namspace CSS globale, ma ha un costo: non puoi utilizzare alcune funzionalità native del CSS come `:hover`, le Media Queries, le animazioni, o `font-face`. Questa è [una delle ragioni](https://github.com/cssinjs/jss/blob/master/docs/benefits.md#compared-to-inline-styles) per cui integreremo la libreria CSS-in-JS, JSS, più avanti in questo capitolo. 270 | 271 | - Modifica `src/shared/component/nav.jsx` in questo modo: 272 | 273 | ```js 274 | // @flow 275 | 276 | import $ from 'jquery' 277 | import React from 'react' 278 | import { Link, NavLink } from 'react-router-dom' 279 | import { APP_NAME } from '../config' 280 | import { 281 | HOME_PAGE_ROUTE, 282 | HELLO_PAGE_ROUTE, 283 | HELLO_ASYNC_PAGE_ROUTE, 284 | NOT_FOUND_DEMO_PAGE_ROUTE, 285 | } from '../routes' 286 | 287 | const handleNavLinkClick = () => { 288 | $('body').scrollTop(0) 289 | $('.js-navbar-collapse').collapse('hide') 290 | } 291 | 292 | const Nav = () => 293 | 313 | 314 | export default Nav 315 | ``` 316 | 317 | Qua abbiamo qualcosa di nuovo, `handleNavLinkClick`. Un problema che ho riscontrato utilizzando la `navbar` di Bootstrap's in una SPA è che cliccando su un link via mobile non richiude il menu, e non ritorna in cima alla pagina. Questa è una buona opportunità per fare un esempio di come puoi integrare del codice jQuery / specifico per Bootstrap nella tua app: 318 | 319 | ```js 320 | import $ from 'jquery' 321 | // [...] 322 | 323 | const handleNavLinkClick = () => { 324 | $('body').scrollTop(0) 325 | $('.js-navbar-collapse').collapse('hide') 326 | } 327 | 328 | 329 | ``` 330 | 331 | **Note**: ho rimosso gli attributi di accessibilità (come gli attributi `aria`) per rendere il codice più leggibile *inel contesto di questo tutorial*. **Dovresti assolutamente reintegrarli**. Fai riferimento alla documentazione di Bootstrap ed agli esempi di codice per impararne il funzionamento. 332 | 333 | 🏁 La tua app dovrebbe adesso essere completamente stilizzata tramite Bootstrap. 334 | 335 | ## Lo stato attuale di CSS 336 | 337 | Nel 2016, il tipico stack JavaScript si è stabilizzato. Le librerie ed i tool utilizzati in questo tutorial sono di fatto *gli standard più avanzati* (*cough – anche se questo potrebbe diventare superato da qui ad un anno – cough*). Si, è uno stack complesso da configurare, ma la maggiorparte degli sviluppatori front-end concordano che React-Redux-Webpack sia la strada da percorrere. Riguardo al CSS, ho alcune brutte notizie. Non c'è niente di definitivo, nessuno stack standard. 338 | 339 | SASS, BEM, SMACSS, SUIT, Bass CSS, React Inline Styles, LESS, Styled Components, CSSX, JSS, Radium, Web Components, CSS Modules, OOCSS, Tachyons, Stylus, Atomic CSS, PostCSS, Aphrodite, React Native for Web, e molti altri che non ho citato sono tutti approcci differenti o tool per fare lo stesso lavoro. Tutti lo fanno bene, e questo è il problema, non esiste un vincitore univoco, è tutto molto confuso. 340 | 341 | Gli sviluppatori React "alla moda", tendono a favorire gli stili React inline, CSS-in-JS, o approcci CSS modulari, siccome si integrano molto bene con React e risolvono molti [problemi](https://speakerdeck.com/vjeux/react-css-in-js) che vengono riscontrati con i CSS regolari. 342 | 343 | I moduli CSS Modules funzionano bene, ma non sfruttano il potere di JavaScript e tutte le sue funzionalità riguardo CSS. Forniscono solo l'incapsulamento, che va bene, ma gli stili React inline e CSS-in-JS portano l'utilizzo degli stili ad un altro livello secondo me. Il mio suggerimento personale è di utilizzare gli stili React inline per gli stili comuni (devi usarli anche per React Native), e usare una libreria CSS-in-JS per cose come `:hover` e le media queries. 344 | 345 | Esistono [numerose librerie CSS-in-JS](https://github.com/MicheleBertoli/css-in-js). JSS è una di quelle più complete e [performanti](https://github.com/cssinjs/jss/blob/master/docs/performance.md). 346 | 347 | ## JSS 348 | 349 | > 💡 **[JSS](http://cssinjs.org/)** è una libreria di tipo CSS-in-JS per scrivere i tuoi stili in JavaScript ed iniettarli nella tua app. 350 | 351 | Adesso che abbiamo qualche template di base con Bootstrap, scriviamo qualche CSS personalizzato. Ho affermato in precedenza che gli stili React inline non supportano `:hover` e le media queries, vediamo un semplice esempio utilizzando JSS. JSS può essere utilizzato tramite `react-jss`, una libreria comoda da utilizzare con i component di React. 352 | 353 | - Esegui `yarn add react-jss` 354 | 355 | Aggiungi quanto segue al file `.flowconfig` perchè attualmente c'è un [problema](https://github.com/cssinjs/jss/issues/411) con Flow e JSS: 356 | 357 | ```flowconfig 358 | [ignore] 359 | .*/node_modules/jss/.* 360 | ``` 361 | 362 | ### Server-side 363 | 364 | JSS può effettuare il render degli stili lato server per il rendering iniziale. 365 | 366 | - Aggiungi le seguenti costanti in `src/shared/config.js`: 367 | 368 | ```js 369 | export const JSS_SSR_CLASS = 'jss-ssr' 370 | export const JSS_SSR_SELECTOR = `.${JSS_SSR_CLASS}` 371 | ``` 372 | 373 | - Modifica `src/server/render-app.jsx` in questo modo: 374 | 375 | ```js 376 | import { SheetsRegistry, SheetsRegistryProvider } from 'react-jss' 377 | // [...] 378 | import { APP_CONTAINER_CLASS, JSS_SSR_CLASS, STATIC_PATH, WDS_PORT } from '../shared/config' 379 | // [...] 380 | const renderApp = (location: string, plainPartialState: ?Object, routerContext: ?Object = {}) => { 381 | const store = initStore(plainPartialState) 382 | const sheets = new SheetsRegistry() 383 | const appHtml = ReactDOMServer.renderToString( 384 | 385 | 386 | 387 | 388 | 389 | 390 | ) 391 | // [...] 392 | 393 | 394 | // [...] 395 | ``` 396 | 397 | ## Client-side 398 | 399 | La prima cosa che il client dovrebbe fare dopo il rendering dell'app lato client, è di rimuovere gli stili JSS generati dal server. 400 | 401 | - Aggiungi quanto segue a `src/client/index.jsx` dopo le chiamate `ReactDOM.render` (ad esempio prima di `setUpSocket(store)`): 402 | 403 | ```js 404 | import { APP_CONTAINER_SELECTOR, JSS_SSR_SELECTOR } from '../shared/config' 405 | // [...] 406 | 407 | const jssServerSide = document.querySelector(JSS_SSR_SELECTOR) 408 | // flow-disable-next-line 409 | jssServerSide.parentNode.removeChild(jssServerSide) 410 | 411 | setUpSocket(store) 412 | ``` 413 | 414 | Modifica `src/shared/component/page/home.jsx` in questo modo: 415 | 416 | ```js 417 | import injectSheet from 'react-jss' 418 | // [...] 419 | const styles = { 420 | hoverMe: { 421 | '&:hover': { 422 | color: 'red', 423 | }, 424 | }, 425 | '@media (max-width: 800px)': { 426 | resizeMe: { 427 | color: 'red', 428 | }, 429 | }, 430 | specialButton: { 431 | composes: ['btn', 'btn-primary'], 432 | backgroundColor: 'limegreen', 433 | }, 434 | } 435 | 436 | const HomePage = ({ classes }: { classes: Object }) => 437 | // [...] 438 |
439 |

JSS

440 |

Hover me.

441 |

Resize the window.

442 | 443 |
444 | // [...] 445 | 446 | export default injectSheet(styles)(HomePage) 447 | ``` 448 | 449 | A differenza degli stili React inline, JSS utilizza le classi. Devi passare gli stili a `injectSheet` e le classi CSS finiscono nelle props del tuo component. 450 | 451 | 🏁 Esegui `yarn start` e `yarn dev:wds`. Apri la homepage. Visualizza il sorgente della pagina (non nell'inspector) per vedere che gli stili JSS sono presenti nel DOM nel render iniziale all'interno dell'elemento `