├── .babelrc ├── .eslintrc ├── .gitignore ├── README.md ├── __mocks__ ├── fileMock.js └── styleMock.js ├── docs ├── deploying.md ├── parcel.md └── setup.md ├── package-lock.json ├── package.json └── src ├── components └── button │ ├── button.css │ ├── button.js │ └── button.test.js ├── index.html └── index.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["@babel/preset-env", "@babel/preset-react"], 3 | "plugins": ["@babel/plugin-proposal-class-properties"] 4 | } 5 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "eslint:recommended", 4 | "react-minimal" 5 | ] 6 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .cache/ 3 | dist/ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Minimal React Boilerplate 2 | 3 | This is a very minimal set-up to get started writing a React app. It bundles your code using [Parcel](https://parceljs.org) and has [Jest](https://jestjs.io/) configured to write tests with [react-testing-library](https://github.com/kentcdodds/react-testing-library). 4 | 5 | This was mostly designed as a learning aid to help understand how bundlers are set up. 6 | **You should probably use [Create React App](https://facebook.github.io/create-react-app/docs/getting-started) for anything really important** 7 | 8 | ## Getting started 9 | 10 | ### Quick start 11 | 12 | 1. Clone this repo 13 | 1. `npm i` 14 | 1. `npm run dev` to start the development server 15 | 16 | ### Slow start 17 | 18 | If you want a bit more info about what exactly is needed for this project to work go to the [setup guide](./docs/setup.md) and follow the manual installation steps in a fresh repo of your own. 19 | -------------------------------------------------------------------------------- /__mocks__/fileMock.js: -------------------------------------------------------------------------------- 1 | export default 'test-file-stub'; 2 | -------------------------------------------------------------------------------- /__mocks__/styleMock.js: -------------------------------------------------------------------------------- 1 | export default {}; 2 | -------------------------------------------------------------------------------- /docs/deploying.md: -------------------------------------------------------------------------------- 1 | # Deploying Your React App 2 | 3 | This app is frontend only, so you can deploy on any static host (like [Netlify](https://netlify.com)). 4 | 5 | If you're making an SPA with client-side routing you need to set up your hosting correctly. This will ensure that all routes point to your `index.html`, allowing your JS to render the correct view. 6 | 7 | Without this your host may return a 404 when trying to directly access a different route e.g. `www.mysite/endpoint` (since `endpoint.html` won't exist). 8 | 9 | ## Building for production 10 | 11 | Add the following build script to your `package.json`: 12 | `"build": parcel build index.html --public-url ./` 13 | 14 | More info in the [Parcel docs](https://parceljs.org/production.html) 15 | 16 | ## Netlify Instructions 17 | 18 | * [Netlify steps for continuous deployment](https://www.netlify.com/docs/continuous-deployment/) 19 | * [Netlify setup for client side routing](https://www.netlify.com/docs/redirects/#history-pushstate-and-single-page-apps) (don't worry about this if you only have a single route) 20 | * If you get build errors using Parcel with Netlify it may be because they build using an older Node version by default (v6!). You can tell them to use whatever version you're using by setting an environment variable called `NODE_ENV` in the 'Deploy Settings' (scroll down to 'Build environment variables'). 21 | -------------------------------------------------------------------------------- /docs/parcel.md: -------------------------------------------------------------------------------- 1 | # What Parcel does 2 | 3 | We want to be able to write modular code using the [ES Modules](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import) `import`/`export` syntax. We also want to be able to write modern ES6+ and custom React syntax (JSX). 4 | 5 | Since browsers don't understand these things we need something to process our source files, transpile them with Babel and then bundle them together into files the browser understands. 6 | 7 | Parcel automatically uses the [`@babel/preset-env`](https://babeljs.io/env/) and [`@babel/preset-react`](https://babeljs.io/docs/plugins/preset-react/) presets to transpile ES6+ and JSX. We've also added the `babel-plugin-transform-class-properties`(https://babeljs.io/docs/plugins/transform-class-properties/) plugin so we can write our class state without needing a constructor function. 8 | 9 | Although Parcel defaults to using `@babel/preset-env` and `@babel/preset-react` Jest doesn't, which is why we had to install and specify them. 10 | 11 | Your dev script (`parcel src/index.html`) will tell Parcel to use the `index.html` file as an 'entrypoint'. 12 | 13 | This means Parcel will start there, find your JS file linked in a script tag, then follow the trail of `import`s until it has built up a 'tree' of your entire app. 14 | 15 | Then it smushes all the files together in a smart way until it's left with a single JS file. 16 | 17 | It copies your `index.html` file into a `dist` folder, along with the newly bundled JS file (which it also links in the new `index.html` for you). 18 | 19 | If you import CSS or SVGs (or other resources Parcel understands) they'll get picked up too. The CSS will all be smushed together into one file and copied into the `dist` folder. Any SVG files will be copied across as they are (although they will get a hashed filename for caching purposes). 20 | 21 | Try adding `--no-minify` to your build script before you run it. You can then open up the `dist` folder and look around. The JS Parcel produces will be weird (it has to use lots of IIFEs and closures to ensure modules are isolated correctly inside one big file) but it should be readable. 22 | -------------------------------------------------------------------------------- /docs/setup.md: -------------------------------------------------------------------------------- 1 | # Parcel Setup for React 2 | 3 | This is a guide for setting up a React app to be bundled with [Parcel](https://parceljs.org/) 📦. We'll also configure ESLint and Jest. 4 | 5 | If you want a more detailed explanation of what Parcel does then have a look at the [Parcel document](./parcel.md). 6 | 7 | ### Install 8 | 9 | 1. `npm init -y` to initialise your repo 10 | 2. `npm i -D parcel-bundler @babel/preset-env @babel/preset-react @babel/plugin-proposal-class-properties` to install dev dependencies 11 | 3. `npm i react react-dom` to install dependencies 12 | 4. Create `.babelrc` file containing: 13 | 14 | ```json 15 | { 16 | "presets": ["@babel/preset-env", "@babel/preset-react"], 17 | "plugins": ["@babel/plugin-proposal-class-properties"] 18 | } 19 | ``` 20 | 21 | Parcel will automatically use Babel to transpile all ES6 and React syntax out of the box, but we want to use a new feature (class properties), so we need to tell Parcel to use an extra plugin. 22 | 23 | 5. Create an `index.html` file containing: 24 | 25 | ```html 26 | 27 | 28 | 29 | 30 | My App 31 | 32 | 33 |
34 | 35 | 36 | 37 | ``` 38 | 39 | Parcel will use this as an 'entrypoint' to your app. It'll find the `index.js` script in there and follow all the `import`s to bundle everything. Then it'll replace the `index.js` with the final JS bundle when it outputs the `dist` folder. 40 | 41 | 6. Create `index.js` with your react setup: 42 | 43 | ```js 44 | import React from "react"; 45 | import { render } from "react-dom"; 46 | 47 | const App = () =>

Hello World

; 48 | 49 | render(, document.getElementById("root")); 50 | ``` 51 | 52 | 7. Setup your build scripts in `package.json`: 53 | 54 | ```json 55 | { 56 | "scripts": { 57 | "dev": "parcel index.html", 58 | "build": "parcel build index.html" 59 | } 60 | } 61 | ``` 62 | 63 | 8. Run `npm run dev` to start the development server. 64 | 65 | ### Test Setup 66 | 67 | We need a few extra things to get Jest working with our ES6+ and React code. 68 | 69 | 1. `npm i -D jest` to install testing dependencies 70 | 2. Add `"test": "jest --watch"` to your npm scripts in `package.json` 71 | 72 | Jest will read the `.babelrc` file to know what presets/plugins it should use. 73 | 74 | #### File imports 75 | 76 | Parcel allows us to [import non-JS files](https://parceljs.org/assets.html) like CSS or images. Unfortunately Jest doesn't use Parcel, so we need to help it understand these imports. 77 | 78 | Create a directory called `__mocks__`, with two files inside: 79 | 80 | `fileMock.js`: 81 | 82 | ```js 83 | export default "whatever-string"; 84 | ``` 85 | 86 | `styleMock.js`: 87 | 88 | ```js 89 | export default {}; 90 | ``` 91 | 92 | Finally we need to tell Jest to use these mocks for the file types we are importing. Add this to the top level of your `package.json`: 93 | 94 | ```json 95 | "jest": { 96 | "moduleNameMapper": { 97 | "\\.(css|less|sass|scss)$": "/__mocks__/styleMock.js", 98 | "\\.(gif|ttf|eot|svg)$": "/__mocks__/fileMock.js" 99 | } 100 | } 101 | ``` 102 | 103 | ### Linting Setup 104 | 105 | **Note:** make sure you don't have ESLint installed globally (`npm rm -g eslint`), otherwise you can get really weird errors. If you still get errors after uninstalling try restarting your editor. 106 | 107 | 1. `npm install -D eslint babel-eslint eslint-plugin-import eslint-plugin-react eslint-config-react-minimal` to install dev dependencies 108 | 2. create `.eslintrc` file with: 109 | 110 | ```json 111 | { 112 | "extends": ["eslint:recommended", "react-minimal"] 113 | } 114 | ``` 115 | 116 | I've packaged up all the necessary config into [eslint-config-react-minimal](https://github.com/oliverjam/eslint-config-react-minimal). Check out the readme if you want more information. 117 | 118 | ### .gitignore 119 | 120 | Run `npx gitignore node` to generate a JS-focused `.gitignore` file. You may want to add the `dist/` directory to it, as Parcel will re-generate this on every build from your source files. 121 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fac-project-setup", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "jest --watch", 8 | "dev": "parcel src/index.html", 9 | "build": "parcel build src/index.html", 10 | "lint": "eslint src/" 11 | }, 12 | "keywords": [], 13 | "author": "", 14 | "license": "ISC", 15 | "jest": { 16 | "moduleNameMapper": { 17 | "\\.(css|less|sass|scss)$": "/__mocks__/styleMock.js", 18 | "\\.(gif|ttf|eot|svg)$": "/__mocks__/fileMock.js" 19 | } 20 | }, 21 | "dependencies": { 22 | "react": "^16.8.6", 23 | "react-dom": "^16.8.6" 24 | }, 25 | "devDependencies": { 26 | "@babel/core": "^7.4.5", 27 | "@babel/plugin-proposal-class-properties": "^7.4.4", 28 | "@babel/preset-env": "^7.4.5", 29 | "@babel/preset-react": "^7.0.0", 30 | "@testing-library/react": "^8.0.1", 31 | "babel-eslint": "^10.0.1", 32 | "eslint": "^5.16.0", 33 | "eslint-config-react-minimal": "^1.0.0", 34 | "eslint-plugin-import": "^2.15.0", 35 | "eslint-plugin-react": "^7.12.4", 36 | "jest": "^24.8.0", 37 | "parcel-bundler": "^1.12.3" 38 | } 39 | } -------------------------------------------------------------------------------- /src/components/button/button.css: -------------------------------------------------------------------------------- 1 | button { 2 | color: red; 3 | } 4 | -------------------------------------------------------------------------------- /src/components/button/button.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import "./button.css"; 3 | 4 | function Button(props) { 5 | const [toggled, setToggled] = React.useState(false); 6 | return ( 7 | 10 | ); 11 | } 12 | 13 | export default Button; 14 | -------------------------------------------------------------------------------- /src/components/button/button.test.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { render, fireEvent, cleanup } from "@testing-library/react"; 3 | import Button from "./button"; 4 | 5 | afterEach(cleanup); 6 | 7 | test("Button works", () => { 8 | const { getByText } = render(); 9 | const buttonNode = getByText("Test"); 10 | fireEvent.click(buttonNode); 11 | expect(buttonNode.textContent).toBe("Toggled"); 12 | }); 13 | -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | React stuff 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom"; 3 | 4 | ReactDOM.render(
hello world
, document.getElementById("🤡")); 5 | --------------------------------------------------------------------------------