- Clone repository.
- Run
npm installoryarn install. - Run
npm run devoryarn run dev. - Run
npm testoryarn test. - Serve
index.htmlusinglive-serveror similar.
- Learn to set up a React project from scratch.
- Learn about Eslint, Jest, Enzyme, Babel and Webpack.
- Appreciate the things Create React App does for us.
- Feel warm and fuzzy that we don't need CRA to start a project.
We need node & npm, as well as packages live-server and eslint installed globally. VSCode's eslint extension is recommended. Command line commands are run inside the project folder. This guide uses npm but should work the same using yarn.
- Create
index.htmlandindex.jsfiles at the root of the project. - Inside
index.html, scaffold a basic html document and create a script tag pointing to a non-existing (for now!) bundle file:<script src="bundle/bundle.js"></script>
- Inside
index.html, create a div element with anidoftarget, which we'll use to attach our React application to the DOM. - Initialize the project by running
npm init -y.
- Talking points: the importance of linting. The eslint webpage.
- Initialize using the command:
eslint --init. You'll be presented with choices. - Choose
Use a popular style guideoption. - Choose
Airbnb, withReact, andJSONformat. - Allow required dependencies to be installed with npm.
- Edit the automatically created
.eslintrc.jsonfile to look like this (intellisense helps):{ "extends": "airbnb", "env": { "es6": true, "browser": true, "node": true, "jest": true }, "parserOptions": { "ecmaVersion": 2018, "sourceType": "module", "ecmaFeatures": { "jsx": true, "spread": true } }, "rules": { "semi": 2 } }
- Talking points: the importance of testing. Untested code is legacy code.
- Install
jestusing the commandnpm i --save-dev jest. - Edit
package.jsonfile so thetestscript looks like this:"test": "jest"
- Run
npm test -- --initto get an automatically generated jest configuration file. You will be asked some questions. - When asked, choose environment to use:
jsdom(browser). - After finishing answering questions you can change the script in
package.jsonto be:"test": "jest --watchAll"
- Create a top-level folder named
setup_tests. - Inside
setup_tests, create a__mocks__folder. - Inside
__mocks__folder, createfileMock.jsandstyleMock.jsfiles. - Inside
fileMock.jsplace the following code:module.exports = 'test-file-stub';
- Inside
styleMock.jsplace the following code:module.exports = {};
- Find the
jest.config.jsfile at the root of the project. This was generated automatically. - Uncomment
moduleNameMapperand add the following value:moduleNameMapper: { "\\.(css|less|sass|scss)$": "<rootDir>/setup_tests/__mocks__/styleMock.js", "\\.(gif|ttf|eot|svg)$": "<rootDir>/setup_tests/__mocks__/fileMock.js" },
- We need the following dev-dependencies:
- enzyme
- enzyme-adapter-react-16
- Run
npm i --save-dev enzyme enzyme-adapter-react-16. - Create a
setupTests.jsfile inside thesetup_teststop-level folder. - Add the following code to
setupTests.js:import { configure } from 'enzyme'; import Adapter from 'enzyme-adapter-react-16'; configure({ adapter: new Adapter() });
- Find the
jest.config.jsfile at the root of the project. - Uncomment
setupFilesAfterEnvand add the following value:setupFilesAfterEnv: [ '<rootDir>setup_tests/setupTests.js', ],
- Talking points: transcompiling, the babel website.
- We need the following dev-dependencies:
- @babel/core
- @babel/preset-env
- @babel/preset-react
- Run
npm i --save-dev @babel/core @babel/preset-env @babel/preset-react. - Create a
babel.config.jsfile at the root of the project. - Put this configuration inside
babel.config.js:module.exports = { presets: [ ["@babel/preset-env", { "targets": { "chrome": "60" } }], "@babel/preset-react" ] }
@babel/preset-envwill configure itself according to the desired targets. Edit browser name and version to get more or less aggressive transpiling of the code. Without this{ "targets": etc etc }configuration object, all the JavaScript code will get transpiled down to ES5, which might be overkill depending on project requirements.
- Talking points: concatenation, minification, uglification, the problem of fetching many assets over http.
- We need the following dev-dependencies:
- webpack
- webpack-cli
- babel-loader
- Run
npm i --save-dev webpack webpack-cli babel-loader. - Edit
package.jsonand add a new script:"dev": "webpack --watch".
- Create a
webpack.config.jsfile at the root of the project. - Edit
webpack.config.jsfile to add a mode of operation, entry and output points, and extensions:var path = require('path'); module.exports = { // 'production' mode would minify and uglify the code, and use React's production code mode: 'development', // entry is the starting point for the web made by our files through imports and exports entry: path.resolve(__dirname, 'index.js'), // all code will get concatenated into a single bundle.js inside a bundle folder output: { path: path.resolve(__dirname, 'bundle'), filename: 'bundle.js', }, // types of files we want Webpack to bundle resolve: { extensions: ['.js', '.jsx'], }, };
- Add a new
modulekey towebpack.config.jsfile:var path = require('path'); module.exports = { // etc // mode, entry, output, resolve // etc module: { rules: [ { test: /\.(js|jsx)$/, exclude: /node_modules/, use: { loader: 'babel-loader' } }, ] } }
- Talking points: difference between React and React DOM.
- Install React and React DOM:
npm i --save react react-dom - Edit index.js as follows:
import React from 'react'; import ReactDOM from 'react-dom'; function App() { return ( <div>Hello World</div> ); } ReactDOM.render( <App />, document.querySelector('#target'), );
- Run Webpack:
npm run dev - Run Live Server in a separate terminal:
live-server - Run tests in a separate terminal:
npm test
- We need the following dev-dependencies:
- less
- less-loader
- css-loader
- style-loader
- Run
npm i --save-dev less less-loader css-loader style-loader. - Edit the
extensionspart insidewebpack.config.jsto look for.lessfiles:{ // etc resolve: { extensions: ['.js', '.jsx', '.less'] }, // etc }
- Add the following object to the
rulesarray insidewebpack.config.js:{ test: /\.less$/, exclude: /node_modules/, use: [ { loader: 'style-loader' }, { loader: 'css-loader' }, { loader: 'less-loader' } ] }
- Create an
index.lessfile at the root, add some styles and import it intoindex.jsusing the following syntax:import './index.less';
- Remember to restart webpack in order to get the configuration changes.
- Run
npm i --save-dev @babel/plugin-proposal-class-properties. - Add a
pluginskey tobabel.config.js:module.exports = { presets: [ ["@babel/preset-env", { "targets": { "chrome": "60" } }], "@babel/preset-react" ], plugins: [ '@babel/plugin-proposal-class-properties' ] }
- With arrow functions we don't need to bind
thisin the constructor. - Run
npm i --save-dev babel-eslint. - Inside
.eslintrcadd aparserkey:{ "extends": "airbnb", "parser": "babel-eslint", // etc }
- Add the following field to the configuration in
webpack.config.js:devtool: 'source-map',
- This allows to visually inspect the weight of our npm dependencies.
- Install Webpack Bundle Analyzer by running
npm i --save-dev webpack-bundle-analyzer. - Pull the analyzer in at the top of
webpack.config.js.var BundleAnalyzer = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
- Create a plugins property on the module.exports object in
webpack.config.js.plugins: [ new BundleAnalyzer() ],
- Initialize git repository.
- Add
node_modulesandbundlefolders to.gitignore. - Prevent configuration files from being linted by creating a top-level
.eslintignorefile. - Add annoying Airbnb rules to the
eslintrc.jsonfile with a value of0(1 means warning, 2 means error):{ // etc "rules": { "semi": 2, "annoying-rule-we-want-disabled": 0, // etc } }