diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 374dbf17958..eec13d075eb 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -1,59 +1,160 @@ -If you are reporting a bug, please fill in below. Otherwise feel free to remove this template entirely. + -### Can you reproduce the problem with latest npm? +### Is this a bug report? -Many errors, especially related to "missing modules", are due to npm bugs. +(write your answer here) -If you're using Windows, [follow these instructions to update npm](https://github.com/npm/npm/wiki/Troubleshooting#upgrading-on-windows). + -Then try to reproduce the issue again. -Can you still reproduce it? +### Can you also reproduce the problem with npm 4.x? -### Description + + +(Write your answer here.) + + +### Which terms did you search for in User Guide? + + + +(Write your answer here if relevant.) -Tell us what actually happens. ### Environment -Run these commands in the project folder and fill in their results: + -1. `npm ls react-scripts` (if you haven’t ejected): -2. `node -v`: -3. `npm -v`: +1. `node -v`: +2. `npm -v`: +4. `yarn --version` (if you use Yarn): +3. `npm ls react-scripts` (if you haven’t ejected): Then, specify: 1. Operating system: -2. Browser and version: +2. Browser and version (if relevant): + + +### Steps to Reproduce + + + +(Write your steps here:) + +1. +2. +3. + + +### Expected Behavior + + + +(Write what you thought would happen.) + + +### Actual Behavior + + + +(Write what happened. Please add screenshots!) + ### Reproducible Demo -Please take the time to create a new app that reproduces the issue. + -(Accidentally, you might get to the root of your problem during that process.) +(Paste the link to an example project and exact instructions to reproduce the issue.) -Push to GitHub and paste the link here. + diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 87acf9c2f90..45268d0b6fc 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,8 +1,7 @@ diff --git a/.travis.yml b/.travis.yml index 2c60bc2674b..b08d2bf6b25 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,7 +2,7 @@ language: node_js node_js: - 6 - - 7 + - 8 cache: directories: - node_modules diff --git a/CHANGELOG.md b/CHANGELOG.md index 9049c88fea5..2a5ef69066d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,178 @@ +## 1.0.8 (June 28, 2017) + +#### :bug: Bug Fix +* `react-scripts` + + * [#2550](https://github.com/facebookincubator/create-react-app/pull/2550) Fix Node 8 compatibility. ([@josephfrazier](https://github.com/josephfrazier)) + * [#2610](https://github.com/facebookincubator/create-react-app/pull/2610) Fix sourcemap directory organization on Windows. ([@plusCubed](https://github.com/plusCubed)) + * [#2596](https://github.com/facebookincubator/create-react-app/pull/2596) Fix an issue with minifying emojis. ([@viankakrisna](https://github.com/viankakrisna)) + * [#2501](https://github.com/facebookincubator/create-react-app/pull/2501) Fix incorrect check if `CI` variable is set to true. ([@varnav](https://github.com/varnav)) + * [#2432](https://github.com/facebookincubator/create-react-app/pull/2432) In new projects, don't register service worker for projects using `PUBLIC_URL` for CDN. ([@jeffposnick](https://github.com/jeffposnick)) + * [#2470](https://github.com/facebookincubator/create-react-app/pull/2470) In new projects, prioritize `index.css` over `App.css`. ([@bryankang](https://github.com/bryankang)) + +* `react-dev-utils` + + * [#2405](https://github.com/facebookincubator/create-react-app/pull/2405) Fix detection of parent directory in `ModuleScopePlugin`. ([@diligiant](https://github.com/diligiant)) + * [#2562](https://github.com/facebookincubator/create-react-app/pull/2562) Fix eject command output. ([@paweljedrzejczyk](https://github.com/paweljedrzejczyk)) + +#### :nail_care: Enhancement + +* `react-scripts` + + * [#2648](https://github.com/facebookincubator/create-react-app/pull/2648) Warn about large bundle sizes. ([@gaearon](https://github.com/gaearon)) + * [#2511](https://github.com/facebookincubator/create-react-app/pull/2511) Support `.web.js` extension for React Native Web. ([@mini-eggs](https://github.com/mini-eggs)) + * [#2645](https://github.com/facebookincubator/create-react-app/pull/2645) Hide confusing "Skipping static resource" message. ([@gaearon](https://github.com/gaearon)) + * [#2389](https://github.com/facebookincubator/create-react-app/pull/2389) Silence unnecessary warning from Babel. ([@gaearon](https://github.com/gaearon)) + * [#2429](https://github.com/facebookincubator/create-react-app/pull/2429) Update `sw-precache-webpack-plugin` to lastest version. ([@goldhand](https://github.com/goldhand)) + * [#2600](https://github.com/facebookincubator/create-react-app/pull/2600) Add empty mock for `dgram` Node module. ([@micopiira](https://github.com/micopiira)) + * [#2458](https://github.com/facebookincubator/create-react-app/pull/2458) Add names to module factories in development. ([@Zaccc123](https://github.com/Zaccc123)) + * [#2551](https://github.com/facebookincubator/create-react-app/pull/2551) In new projects, unregister service worker and force reload if `service-worker.js` is not found. ([@ro-savage](https://github.com/ro-savage)) + +* `babel-preset-react-app`, `react-dev-utils`, `react-scripts` + + * [#2658](https://github.com/facebookincubator/create-react-app/pull/2658) Bump dependencies. ([@gaearon](https://github.com/gaearon)) + +* `create-react-app`, `react-scripts` + + * [#2657](https://github.com/facebookincubator/create-react-app/pull/2657) Put `react-scripts` in `dependencies`, not `devDependencies`. ([@gaearon](https://github.com/gaearon)) + * [#2635](https://github.com/facebookincubator/create-react-app/pull/2635) Silence unhelpful npm warnings. ([@gaearon](https://github.com/gaearon)) + +* `react-dev-utils` + + * [#2637](https://github.com/facebookincubator/create-react-app/pull/2637) Auto-detect Brackets editor from error overlay. ([@petetnt](https://github.com/petetnt)) + * [#2552](https://github.com/facebookincubator/create-react-app/pull/2552) Auto-detect running editor on Windows for error overlay. ([@levrik](https://github.com/levrik)) + * [#2622](https://github.com/facebookincubator/create-react-app/pull/2622) Support opening PhpStorm for error overlay. ([@miraage](https://github.com/miraage)) + * [#2414](https://github.com/facebookincubator/create-react-app/pull/2414) Support opening WebStorm 2017+ from error overlay. ([@wirmar](https://github.com/wirmar)) + * [#2518](https://github.com/facebookincubator/create-react-app/pull/2518) Warn when trying to run on port below 1024 without admin permissions under Linux/macOS. ([@levrik](https://github.com/levrik)) + * [#2385](https://github.com/facebookincubator/create-react-app/pull/2385) Suggest just `yarn build` in output. ([@gaearon](https://github.com/gaearon)) + +* `create-react-app` + + * [#1945](https://github.com/facebookincubator/create-react-app/pull/1945) Fix grammar in CLI output. ([@ColinEberhardt](https://github.com/ColinEberhardt)) + +#### :memo: Documentation + +* User Guide + + * [#2662](https://github.com/facebookincubator/create-react-app/pull/2662) Local testing docker links. ([@EnoahNetzach](https://github.com/EnoahNetzach)) + * [#2660](https://github.com/facebookincubator/create-react-app/pull/2660) Minor code style edits to user guide. ([@gaearon](https://github.com/gaearon)) + * [#2656](https://github.com/facebookincubator/create-react-app/pull/2656) Don't ask to install webpack for using Styleguidist. ([@gaearon](https://github.com/gaearon)) + * [#1641](https://github.com/facebookincubator/create-react-app/pull/1641) Add instructions to use `source-map-explorer`. ([@gr33nfury](https://github.com/gr33nfury)) + * [#2044](https://github.com/facebookincubator/create-react-app/pull/2044) Add React Styleguidist. ([@sapegin](https://github.com/sapegin)) + * [#2006](https://github.com/facebookincubator/create-react-app/pull/2006) Added instruction on how to install Prettier. ([@MrHus](https://github.com/MrHus)) + * [#1813](https://github.com/facebookincubator/create-react-app/pull/1813) Fix grammar. ([@iheng](https://github.com/iheng)) + * [#2060](https://github.com/facebookincubator/create-react-app/pull/2060) Add more info about OOM build failiure [docs]. ([@GAumala](https://github.com/GAumala)) + * [#2305](https://github.com/facebookincubator/create-react-app/pull/2305) Update docs with WebSocket proxy information. ([@jamesblight](https://github.com/jamesblight)) + * [#2445](https://github.com/facebookincubator/create-react-app/pull/2445) Document `REACT_EDITOR` environment variable. ([@wirmar](https://github.com/wirmar)) + * [#2362](https://github.com/facebookincubator/create-react-app/pull/2362) Add yarn example under "Installing a Dependency". ([@BrianDGLS](https://github.com/BrianDGLS)) + * [#2423](https://github.com/facebookincubator/create-react-app/pull/2423) Add docs for setting up CircleCI for CRA. ([@knowbody](https://github.com/knowbody)) + * [#2427](https://github.com/facebookincubator/create-react-app/pull/2427) Added link to tutorial on code splitting. ([@jayair](https://github.com/jayair)) + * [#2447](https://github.com/facebookincubator/create-react-app/pull/2447) Fix wrong comment on Proxy guide. ([@hellowin](https://github.com/hellowin)) + * [#2538](https://github.com/facebookincubator/create-react-app/pull/2538) Fix broken link to a tutorial. ([@romanyanke](https://github.com/romanyanke)) + * [#2522](https://github.com/facebookincubator/create-react-app/pull/2522) Flow init to run as command not flag. ([@khanglu](https://github.com/khanglu)) + * [#2521](https://github.com/facebookincubator/create-react-app/pull/2521) Fix broken link to Storybook docs. ([@shilman](https://github.com/shilman)) + * [#2500](https://github.com/facebookincubator/create-react-app/pull/2500) Fix minor typo. ([@AlexxNica](https://github.com/AlexxNica)) + * [#2331](https://github.com/facebookincubator/create-react-app/pull/2331) Re-add storybook && update the documentation and links. ([@ndelangen](https://github.com/ndelangen)) + * [#2454](https://github.com/facebookincubator/create-react-app/pull/2454) Update Travis CI Node versions in User Guide. ([@ryansully](https://github.com/ryansully)) + * [#2420](https://github.com/facebookincubator/create-react-app/pull/2420) Fix typo. ([@ruskakimov](https://github.com/ruskakimov)) + * [#2392](https://github.com/facebookincubator/create-react-app/pull/2392) Update `jest-enzyme` section. ([@luftywiranda13](https://github.com/luftywiranda13)) + +* README + + * [#2517](https://github.com/facebookincubator/create-react-app/pull/2517) Add Razzle to the alternatives. ([@kireerik](https://github.com/kireerik)) + * [#1931](https://github.com/facebookincubator/create-react-app/pull/1931) Updated README. ([@shaunwallace](https://github.com/shaunwallace)) + * [#2492](https://github.com/facebookincubator/create-react-app/pull/2492) Update webpack links to point to webpack 2. ([@laruiss](https://github.com/laruiss)) + +#### :house: Internal + +* Other + + * [#2465](https://github.com/facebookincubator/create-react-app/pull/2465) Update Prettier to v1. ([@ianschmitz](https://github.com/ianschmitz)) + * [#2489](https://github.com/facebookincubator/create-react-app/pull/2489) chore(templates): Move GitHub templates to hidden .github folder. ([@glennreyes](https://github.com/glennreyes)) + * [#2400](https://github.com/facebookincubator/create-react-app/pull/2400) Added cache clear to e2e scripts. ([@ro-savage](https://github.com/ro-savage)) + * [#2397](https://github.com/facebookincubator/create-react-app/pull/2397) Fix command in e2e-kitchensink.sh cleanup. ([@ro-savage](https://github.com/ro-savage)) + * [#2388](https://github.com/facebookincubator/create-react-app/pull/2388) Fix wrong path expansion in end-to-end test. ([@gaearon](https://github.com/gaearon)) + * [#2387](https://github.com/facebookincubator/create-react-app/pull/2387) Catch "No tests found" during CI. ([@EnoahNetzach](https://github.com/EnoahNetzach)) + +* `react-scripts` + + * [#2408](https://github.com/facebookincubator/create-react-app/pull/2408) E2E testing enhancements. ([@EnoahNetzach](https://github.com/EnoahNetzach)) + * [#2430](https://github.com/facebookincubator/create-react-app/pull/2430) Remove an unnecessary webpack option. ([@andykenward](https://github.com/andykenward)) + +* `react-dev-utils` + + * [#2483](https://github.com/facebookincubator/create-react-app/pull/2483) Remove a scoped package dependency. ([@Timer](https://github.com/Timer)) + +#### Committers: 46 +- Ade Viankakrisna Fadlil ([viankakrisna](https://github.com/viankakrisna)) +- Alexandre Nicastro ([AlexxNica](https://github.com/AlexxNica)) +- Andi N. Dirgantara ([hellowin](https://github.com/hellowin)) +- Andy Kenward ([andykenward](https://github.com/andykenward)) +- Artem Sapegin ([sapegin](https://github.com/sapegin)) +- Ashton ([ashtonsix](https://github.com/ashtonsix)) +- Brian Douglas ([BrianDGLS](https://github.com/BrianDGLS)) +- Colin Eberhardt ([ColinEberhardt](https://github.com/ColinEberhardt)) +- Colin Galindo ([gr33nfury](https://github.com/gr33nfury)) +- Dan Abramov ([gaearon](https://github.com/gaearon)) +- Daniel Ciao ([plusCubed](https://github.com/plusCubed)) +- Erik Engi ([kireerik](https://github.com/kireerik)) +- Evan Jones ([mini-eggs](https://github.com/mini-eggs)) +- Fabrizio Castellarin ([EnoahNetzach](https://github.com/EnoahNetzach)) +- Frédéric Miserey ([diligiant](https://github.com/diligiant)) +- Gabriel Aumala ([GAumala](https://github.com/GAumala)) +- Glenn Reyes ([glennreyes](https://github.com/glennreyes)) +- Heng Li ([iheng](https://github.com/iheng)) +- Ian Schmitz ([ianschmitz](https://github.com/ianschmitz)) +- James Blight ([jamesblight](https://github.com/jamesblight)) +- Jay V ([jayair](https://github.com/jayair)) +- Jeffrey Posnick ([jeffposnick](https://github.com/jeffposnick)) +- Joe Haddad ([Timer](https://github.com/Timer)) +- Joseph Frazier ([josephfrazier](https://github.com/josephfrazier)) +- Khang Lu ([khanglu](https://github.com/khanglu)) +- Levin Rickert ([levrik](https://github.com/levrik)) +- Lufty Wiranda ([luftywiranda13](https://github.com/luftywiranda13)) +- Maarten Hus ([MrHus](https://github.com/MrHus)) +- Marius Wirtherle ([wirmar](https://github.com/wirmar)) +- Mateusz Zatorski ([knowbody](https://github.com/knowbody)) +- Michael Shilman ([shilman](https://github.com/shilman)) +- Mico Piira ([micopiira](https://github.com/micopiira)) +- Mikhail Osher ([miraage](https://github.com/miraage)) +- Norbert de Langen ([ndelangen](https://github.com/ndelangen)) +- Paweł Jędrzejczyk ([paweljedrzejczyk](https://github.com/paweljedrzejczyk)) +- Pete Nykänen ([petetnt](https://github.com/petetnt)) +- Ro Savage ([ro-savage](https://github.com/ro-savage)) +- Roman ([romanyanke](https://github.com/romanyanke)) +- Rustem Kakimov ([ruskakimov](https://github.com/ruskakimov)) +- Ryan Sullivan ([ryansully](https://github.com/ryansully)) +- Stanislas Ormières ([laruiss](https://github.com/laruiss)) +- Will Farley ([goldhand](https://github.com/goldhand)) +- Zac Kwan ([Zaccc123](https://github.com/Zaccc123)) +- [bryankang](https://github.com/bryankang) +- [varnav](https://github.com/varnav) +- shaun wallace ([shaunwallace](https://github.com/shaunwallace)) + +### Migrating from 1.0.7 to 1.0.8 + +Inside any created project that has not been ejected, run: + +``` +npm install --save-dev --save-exact react-scripts@1.0.8 +``` + +or + +``` +yarn add --dev --exact react-scripts@1.0.8 +``` + +**If you previously used `HTTPS=true` environment variable in development**, make sure you aren't affected by a now-fixed vulnerability in Webpack by [visiting this page](http://badcert.mike.works/). You can read more about the vulnerability [here](https://medium.com/@mikenorth/webpack-preact-cli-vulnerability-961572624c54). + +You may optionally then move `react-scripts` from `devDependencies` to `dependencies` since that’s how we’ll structure newly created projects. It is not necessary though. + +If you left the service worker integration enabled and didn’t change how it works, you can replace `src/registerServiceWorker.js` with [this updated version](https://raw.githubusercontent.com/facebookincubator/create-react-app/895c475d3fc218c65dcac9a3ef3f2c0ea746a1ed/packages/react-scripts/template/src/registerServiceWorker.js). + +If you haven't changed the default CSS organization, you may want to apply [this fix](https://github.com/facebookincubator/create-react-app/pull/2470/files) that makes `index.css` take precedence over `App.css` in your project. + ## 1.0.7 (May 27, 2017) #### :bug: Bug Fix diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a4e3414be66..68a9c4e09cc 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -90,6 +90,12 @@ and then run `npm start` or `npm run build`. *Note: if you are using yarn, we suggest that you use `yarn install --no-lockfile` instead of the bare `yarn` or `yarn install` because we [intentionally](https://github.com/facebookincubator/create-react-app/pull/2014#issuecomment-300811661) do not ignore or add yarn.lock to our repo.* +## Contributing to E2E (end to end) tests + +**TL;DR** use the command `yarn e2e:docker` to run unit and e2e tests. + +More detailed information are in the dedicated [README](/packages/react-scripts/fixtures/kitchensink/README.md). + ## Cutting a Release 1. Tag all merged pull requests that go into the release with the relevant milestone. Each merged PR should also be labeled with one of the [labels](https://github.com/facebookincubator/create-react-app/labels) named `tag: ...` to indicate what kind of change it is. diff --git a/README.md b/README.md index 06a2fbf4df9..0c428ef65cf 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ Install it once globally: npm install -g create-react-app ``` -**You’ll need to have Node >= 6 on your machine**. You can use [nvm](https://github.com/creationix/nvm#usage) to easily switch Node versions between different projects. +**You’ll need to have Node >= 6 on your machine**. You can use [nvm](https://github.com/creationix/nvm#installation) to easily switch Node versions between different projects. **This tool doesn’t assume a Node backend**. The Node installation is only required for Create React App itself. @@ -57,23 +57,23 @@ It will create a directory called `my-app` inside the current folder.
Inside that directory, it will generate the initial project structure and install the transitive dependencies: ``` -my-app/ - README.md - node_modules/ - package.json - .gitignore - public/ - favicon.ico - index.html - manifest.json - src/ - App.css - App.js - App.test.js - index.css - index.js - logo.svg - registerServiceWorker.js +my-app +├── README.md +├── node_modules +├── package.json +├── .gitignore +├── public +│ └── favicon.ico +│ └── index.html +│ └── manifest.json +└── src + └── App.css + └── App.js + └── App.test.js + └── index.css + └── index.js + └── logo.svg + └── registerServiceWorker.js ``` No configuration or complicated folder structures, just the files you need to build your app.
@@ -116,6 +116,7 @@ The [User Guide](https://github.com/facebookincubator/create-react-app/blob/mast - [Supported Language Features and Polyfills](https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#supported-language-features-and-polyfills) - [Syntax Highlighting in the Editor](https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#syntax-highlighting-in-the-editor) - [Displaying Lint Output in the Editor](https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#displaying-lint-output-in-the-editor) +- [Formatting Code Automatically](https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#formatting-code-automatically) - [Debugging in the Editor](https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#debugging-in-the-editor) - [Changing the Page ``](https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#changing-the-page-title) - [Installing a Dependency](https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#installing-a-dependency) @@ -137,7 +138,9 @@ The [User Guide](https://github.com/facebookincubator/create-react-app/blob/mast - [Generating Dynamic `<meta>` Tags on the Server](https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#generating-dynamic-meta-tags-on-the-server) - [Pre-Rendering into Static HTML Files](https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#pre-rendering-into-static-html-files) - [Running Tests](https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#running-tests) +- [Developing Components in Isolation](https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#developing-components-in-isolation) - [Making a Progressive Web App](https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#making-a-progressive-web-app) +- [Analyzing the Bundle Size](https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#analyzing-the-bundle-size) - [Deployment](https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#deployment) - [Advanced Configuration](https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#advanced-configuration) - [Troubleshooting](https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#troubleshooting) @@ -232,6 +235,7 @@ Some of the more popular and actively maintained ones are: * [insin/nwb](https://github.com/insin/nwb) * [mozilla-neutrino/neutrino-dev](https://github.com/mozilla-neutrino/neutrino-dev) +* [jaredpalmer/razzle](https://github.com/jaredpalmer/razzle) * [NYTimes/kyt](https://github.com/NYTimes/kyt) * [zeit/next.js](https://github.com/zeit/next.js) * [gatsbyjs/gatsby](https://github.com/gatsbyjs/gatsby) diff --git a/bootstrap.js b/bootstrap.js new file mode 100644 index 00000000000..b54a1ed9f36 --- /dev/null +++ b/bootstrap.js @@ -0,0 +1,67 @@ +'use strict'; + +const { execSync, spawn } = require('child_process'); +const { resolve } = require('path'); +const { existsSync } = require('fs'); +const { platform } = require('os'); + +function shouldUseYarn() { + try { + execSync('yarnpkg --version', { stdio: 'ignore' }); + return true; + } catch (e) { + return false; + } +} + +function shouldUseNpmConcurrently() { + try { + const versionString = execSync('npm --version'); + const m = /^(\d+)[.]/.exec(versionString); + // NPM >= 5 support concurrent installs + return Number(m[1]) >= 5; + } catch (e) { + return false; + } +} + +const yarn = shouldUseYarn(); +const windows = platform() === 'win32'; +const lerna = resolve( + __dirname, + 'node_modules', + '.bin', + windows ? 'lerna.cmd' : 'lerna' +); + +if (!existsSync(lerna)) { + if (yarn) { + console.log('Cannot find lerna. Please run `yarn --check-files`.'); + } else { + console.log( + 'Cannot find lerna. Please remove `node_modules` and run `npm install`.' + ); + } + process.exit(1); +} + +let child; +if (yarn) { + // Yarn does not support concurrency + child = spawn(lerna, ['bootstrap', '--npm-client=yarn', '--concurrency=1'], { + stdio: 'inherit', + }); +} else { + let args = ['bootstrap']; + if ( + // The Windows filesystem does not handle concurrency well + windows || + // Only newer npm versions support concurrency + !shouldUseNpmConcurrently() + ) { + args.push('--concurrency=1'); + } + child = spawn(lerna, args, { stdio: 'inherit' }); +} + +child.on('close', code => process.exit(code)); diff --git a/lerna.json b/lerna.json index e2429777df4..7ca916a8569 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "lerna": "2.0.0-beta.38", + "lerna": "2.0.0-rc.5", "version": "independent", "changelog": { "repo": "facebookincubator/create-react-app", diff --git a/package.json b/package.json index e6bd7b0c3cf..b9abb1ce865 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,8 @@ "changelog": "lerna-changelog", "create-react-app": "tasks/cra.sh", "e2e": "tasks/e2e-simple.sh", - "postinstall": "lerna bootstrap && cd packages/react-error-overlay/ && npm run build:prod", + "e2e:docker": "tasks/local-test.sh", + "postinstall": "node bootstrap.js && cd packages/react-error-overlay/ && npm run build:prod", "publish": "tasks/release.sh", "start": "node packages/react-scripts/scripts/start.js", "test": "node packages/react-scripts/scripts/test.js --env=jsdom", @@ -15,10 +16,10 @@ "devDependencies": { "eslint": "3.19.0", "husky": "^0.13.2", - "lerna": "2.0.0-beta.38", + "lerna": "2.0.0-rc.5", "lerna-changelog": "^0.2.3", "lint-staged": "^3.3.1", - "prettier": "^0.21.0" + "prettier": "^1.5.2" }, "lint-staged": { "*.js": [ diff --git a/packages/babel-preset-react-app/package.json b/packages/babel-preset-react-app/package.json index 175b7b1297f..d10188fda02 100644 --- a/packages/babel-preset-react-app/package.json +++ b/packages/babel-preset-react-app/package.json @@ -1,6 +1,6 @@ { "name": "babel-preset-react-app", - "version": "3.0.0", + "version": "3.0.1", "description": "Babel preset used by Create React App", "repository": "facebookincubator/create-react-app", "license": "BSD-3-Clause", @@ -21,7 +21,7 @@ "babel-plugin-transform-react-jsx-source": "6.22.0", "babel-plugin-transform-regenerator": "6.24.1", "babel-plugin-transform-runtime": "6.23.0", - "babel-preset-env": "1.4.0", + "babel-preset-env": "1.5.2", "babel-preset-react": "6.24.1" }, "peerDependencies": { diff --git a/packages/create-react-app/createReactApp.js b/packages/create-react-app/createReactApp.js index b3d0638e980..9dc7b4ff335 100755 --- a/packages/create-react-app/createReactApp.js +++ b/packages/create-react-app/createReactApp.js @@ -74,10 +74,14 @@ const program = new commander.Command(packageJson.name) ); console.log(` - a specific npm version: ${chalk.green('0.8.2')}`); console.log( - ` - a custom fork published on npm: ${chalk.green('my-react-scripts')}` + ` - a custom fork published on npm: ${chalk.green( + 'my-react-scripts' + )}` ); console.log( - ` - a .tgz archive: ${chalk.green('https://mysite.com/my-react-scripts-0.8.2.tgz')}` + ` - a .tgz archive: ${chalk.green( + 'https://mysite.com/my-react-scripts-0.8.2.tgz' + )}` ); console.log( ` It is not needed unless you specifically want to use a fork.` @@ -87,7 +91,9 @@ const program = new commander.Command(packageJson.name) ` If you have any problems, do not hesitate to file an issue:` ); console.log( - ` ${chalk.cyan('https://github.com/facebookincubator/create-react-app/issues/new')}` + ` ${chalk.cyan( + 'https://github.com/facebookincubator/create-react-app/issues/new' + )}` ); console.log(); }) @@ -218,7 +224,13 @@ function install(useYarn, dependencies, verbose, isOnline) { } } else { command = 'npm'; - args = ['install', '--save', '--save-exact'].concat(dependencies); + args = [ + 'install', + '--save', + '--save-exact', + '--loglevel', + 'error', + ].concat(dependencies); } if (verbose) { @@ -250,17 +262,21 @@ function run( const packageToInstall = getInstallPackage(version); const allDependencies = ['react', 'react-dom', packageToInstall]; - console.log('Installing packages. This might take a couple minutes.'); + console.log('Installing packages. This might take a couple of minutes.'); getPackageName(packageToInstall) - .then(packageName => checkIfOnline(useYarn).then(isOnline => ({ - isOnline: isOnline, - packageName: packageName, - }))) + .then(packageName => + checkIfOnline(useYarn).then(isOnline => ({ + isOnline: isOnline, + packageName: packageName, + })) + ) .then(info => { const isOnline = info.isOnline; const packageName = info.packageName; console.log( - `Installing ${chalk.cyan('react')}, ${chalk.cyan('react-dom')}, and ${chalk.cyan(packageName)}...` + `Installing ${chalk.cyan('react')}, ${chalk.cyan( + 'react-dom' + )}, and ${chalk.cyan(packageName)}...` ); console.log(); @@ -270,11 +286,7 @@ function run( }) .then(packageName => { checkNodeVersion(packageName); - - // Since react-scripts has been installed with --save - // we need to move it into devDependencies and rewrite package.json - // also ensure react dependencies have caret version range - fixDependencies(packageName); + setCaretRangeForRuntimeDeps(packageName); const scriptsPath = path.resolve( process.cwd(), @@ -332,7 +344,9 @@ function run( if (!remainingFiles.length) { // Delete target folder if empty console.log( - `Deleting ${chalk.cyan(`${appName} /`)} from ${chalk.cyan(path.resolve(root, '..'))}` + `Deleting ${chalk.cyan(`${appName} /`)} from ${chalk.cyan( + path.resolve(root, '..') + )}` ); process.chdir(path.resolve(root, '..')); fs.removeSync(path.join(root)); @@ -420,7 +434,9 @@ function getPackageName(installPackage) { /^.+\/(.+?)(?:-\d+.+)?\.tgz$/ )[1]; console.log( - `Based on the filename, assuming it is "${chalk.cyan(assumedProjectName)}"` + `Based on the filename, assuming it is "${chalk.cyan( + assumedProjectName + )}"` ); return Promise.resolve(assumedProjectName); }); @@ -483,7 +499,9 @@ function checkAppName(appName) { const validationResult = validateProjectName(appName); if (!validationResult.validForNewPackages) { console.error( - `Could not create a project called ${chalk.red(`"${appName}"`)} because of npm naming restrictions:` + `Could not create a project called ${chalk.red( + `"${appName}"` + )} because of npm naming restrictions:` ); printValidationResults(validationResult.errors); printValidationResults(validationResult.warnings); @@ -491,16 +509,16 @@ function checkAppName(appName) { } // TODO: there should be a single place that holds the dependencies - const dependencies = ['react', 'react-dom']; - const devDependencies = ['react-scripts']; - const allDependencies = dependencies.concat(devDependencies).sort(); - if (allDependencies.indexOf(appName) >= 0) { + const dependencies = ['react', 'react-dom', 'react-scripts'].sort(); + if (dependencies.indexOf(appName) >= 0) { console.error( chalk.red( - `We cannot create a project called ${chalk.green(appName)} because a dependency with the same name exists.\n` + + `We cannot create a project called ${chalk.green( + appName + )} because a dependency with the same name exists.\n` + `Due to the way npm works, the following names are not allowed:\n\n` ) + - chalk.cyan(allDependencies.map(depName => ` ${depName}`).join('\n')) + + chalk.cyan(dependencies.map(depName => ` ${depName}`).join('\n')) + chalk.red('\n\nPlease choose a different project name.') ); process.exit(1); @@ -519,7 +537,9 @@ function makeCaretRange(dependencies, name) { if (!semver.validRange(patchedVersion)) { console.error( - `Unable to patch ${name} dependency version because version ${chalk.red(version)} will become invalid ${chalk.red(patchedVersion)}` + `Unable to patch ${name} dependency version because version ${chalk.red( + version + )} will become invalid ${chalk.red(patchedVersion)}` ); patchedVersion = version; } @@ -527,7 +547,7 @@ function makeCaretRange(dependencies, name) { dependencies[name] = patchedVersion; } -function fixDependencies(packageName) { +function setCaretRangeForRuntimeDeps(packageName) { const packagePath = path.join(process.cwd(), 'package.json'); const packageJson = require(packagePath); @@ -537,16 +557,11 @@ function fixDependencies(packageName) { } const packageVersion = packageJson.dependencies[packageName]; - if (typeof packageVersion === 'undefined') { console.error(chalk.red(`Unable to find ${packageName} in package.json`)); process.exit(1); } - packageJson.devDependencies = packageJson.devDependencies || {}; - packageJson.devDependencies[packageName] = packageVersion; - delete packageJson.dependencies[packageName]; - makeCaretRange(packageJson.dependencies, 'react'); makeCaretRange(packageJson.dependencies, 'react-dom'); diff --git a/packages/create-react-app/package.json b/packages/create-react-app/package.json index 13a567fa7b4..613007b40b1 100644 --- a/packages/create-react-app/package.json +++ b/packages/create-react-app/package.json @@ -1,6 +1,6 @@ { "name": "create-react-app", - "version": "1.3.1", + "version": "1.3.2", "keywords": [ "react" ], diff --git a/packages/eslint-config-react-app/index.js b/packages/eslint-config-react-app/index.js index 2cbcae04c69..7298a8c3b3d 100644 --- a/packages/eslint-config-react-app/index.js +++ b/packages/eslint-config-react-app/index.js @@ -236,7 +236,8 @@ module.exports = { { object: 'System', property: 'import', - message: 'Please use import() instead. More info: https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#code-splitting', + message: + 'Please use import() instead. More info: https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#code-splitting', }, ], diff --git a/packages/eslint-config-react-app/package.json b/packages/eslint-config-react-app/package.json index 87b63c5b6f8..cd36a70e5cb 100644 --- a/packages/eslint-config-react-app/package.json +++ b/packages/eslint-config-react-app/package.json @@ -1,6 +1,6 @@ { "name": "eslint-config-react-app", - "version": "1.0.4", + "version": "1.0.5", "description": "ESLint configuration used by Create React App", "repository": "facebookincubator/create-react-app", "license": "BSD-3-Clause", diff --git a/packages/react-dev-utils/FileSizeReporter.js b/packages/react-dev-utils/FileSizeReporter.js index 8f822d20133..ab9a2728752 100644 --- a/packages/react-dev-utils/FileSizeReporter.js +++ b/packages/react-dev-utils/FileSizeReporter.js @@ -18,7 +18,13 @@ var stripAnsi = require('strip-ansi'); var gzipSize = require('gzip-size').sync; // Prints a detailed summary of build files. -function printFileSizesAfterBuild(webpackStats, previousSizeMap, buildFolder) { +function printFileSizesAfterBuild( + webpackStats, + previousSizeMap, + buildFolder, + maxBundleGzipSize, + maxChunkGzipSize +) { var root = previousSizeMap.root; var sizes = previousSizeMap.sizes; var assets = webpackStats @@ -41,6 +47,7 @@ function printFileSizesAfterBuild(webpackStats, previousSizeMap, buildFolder) { null, assets.map(a => stripAnsi(a.sizeLabel).length) ); + var suggestBundleSplitting = false; assets.forEach(asset => { var sizeLabel = asset.sizeLabel; var sizeLength = stripAnsi(sizeLabel).length; @@ -48,14 +55,38 @@ function printFileSizesAfterBuild(webpackStats, previousSizeMap, buildFolder) { var rightPadding = ' '.repeat(longestSizeLabelLength - sizeLength); sizeLabel += rightPadding; } + var isMainBundle = asset.name.indexOf('main.') === 0; + var maxRecommendedSize = isMainBundle + ? maxBundleGzipSize + : maxChunkGzipSize; + var isLarge = maxRecommendedSize && asset.size > maxRecommendedSize; + if (isLarge && path.extname(asset.name) === '.js') { + suggestBundleSplitting = true; + } console.log( ' ' + - sizeLabel + + (isLarge ? chalk.yellow(sizeLabel) : sizeLabel) + ' ' + chalk.dim(asset.folder + path.sep) + chalk.cyan(asset.name) ); }); + if (suggestBundleSplitting) { + console.log(); + console.log( + chalk.yellow('The bundle size is significantly larger than recommended.') + ); + console.log( + chalk.yellow( + 'Consider reducing it with code splitting: https://goo.gl/9VhYWB' + ) + ); + console.log( + chalk.yellow( + 'You can also analyze the project dependencies: https://goo.gl/LeUzfb' + ) + ); + } } function removeFileNameHash(buildFolder, fileName) { @@ -88,15 +119,12 @@ function measureFileSizesBeforeBuild(buildFolder) { if (!err && fileNames) { sizes = fileNames .filter(fileName => /\.(js|css)$/.test(fileName)) - .reduce( - (memo, fileName) => { - var contents = fs.readFileSync(fileName); - var key = removeFileNameHash(buildFolder, fileName); - memo[key] = gzipSize(contents); - return memo; - }, - {} - ); + .reduce((memo, fileName) => { + var contents = fs.readFileSync(fileName); + var key = removeFileNameHash(buildFolder, fileName); + memo[key] = gzipSize(contents); + return memo; + }, {}); } resolve({ root: buildFolder, diff --git a/packages/react-dev-utils/ModuleScopePlugin.js b/packages/react-dev-utils/ModuleScopePlugin.js index adc9bdcba93..f630a0279f5 100644 --- a/packages/react-dev-utils/ModuleScopePlugin.js +++ b/packages/react-dev-utils/ModuleScopePlugin.js @@ -37,10 +37,7 @@ class ModuleScopePlugin { // Maybe an indexOf === 0 would be better? const relative = path.relative(appSrc, request.context.issuer); // If it's not in src/ or a subdirectory, not our request! - if ( - relative.startsWith('../') || - relative.startsWith('..\\') - ) { + if (relative.startsWith('../') || relative.startsWith('..\\')) { return callback(); } // Find path from src to the requested file @@ -58,9 +55,19 @@ class ModuleScopePlugin { ) { callback( new Error( - `You attempted to import ${chalk.cyan(request.__innerRequest_request)} which falls outside of the project ${chalk.cyan('src/')} directory. ` + - `Relative imports outside of ${chalk.cyan('src/')} are not supported. ` + - `You can either move it inside ${chalk.cyan('src/')}, or add a symlink to it from project's ${chalk.cyan('node_modules/')}.` + `You attempted to import ${chalk.cyan( + request.__innerRequest_request + )} which falls outside of the project ${chalk.cyan( + 'src/' + )} directory. ` + + `Relative imports outside of ${chalk.cyan( + 'src/' + )} are not supported. ` + + `You can either move it inside ${chalk.cyan( + 'src/' + )}, or add a symlink to it from project's ${chalk.cyan( + 'node_modules/' + )}.` ), request ); diff --git a/packages/react-dev-utils/WebpackDevServerUtils.js b/packages/react-dev-utils/WebpackDevServerUtils.js index 648fef3e1fb..8a66b15ec0c 100644 --- a/packages/react-dev-utils/WebpackDevServerUtils.js +++ b/packages/react-dev-utils/WebpackDevServerUtils.js @@ -37,18 +37,20 @@ if (isSmokeTest) { } function prepareUrls(protocol, host, port) { - const formatUrl = hostname => url.format({ - protocol, - hostname, - port, - pathname: '/', - }); - const prettyPrintUrl = hostname => url.format({ - protocol, - hostname, - port: chalk.bold(port), - pathname: '/', - }); + const formatUrl = hostname => + url.format({ + protocol, + hostname, + port, + pathname: '/', + }); + const prettyPrintUrl = hostname => + url.format({ + protocol, + hostname, + port: chalk.bold(port), + pathname: '/', + }); const isUnspecifiedHost = host === '0.0.0.0' || host === '::'; let prettyHost, lanUrlForConfig, lanUrlForTerminal; @@ -317,9 +319,11 @@ function prepareProxy(proxy, appPublicFolder) { // However API calls like `fetch()` won’t generally accept text/html. // If this heuristic doesn’t work well for you, use a custom `proxy` object. context: function(pathname, req) { - return mayProxy(pathname) && + return ( + mayProxy(pathname) && req.headers.accept && - req.headers.accept.indexOf('text/html') === -1; + req.headers.accept.indexOf('text/html') === -1 + ); }, onProxyReq: proxyReq => { // Browers may send Origin headers even with same-origin @@ -375,39 +379,40 @@ function prepareProxy(proxy, appPublicFolder) { function choosePort(host, defaultPort) { return detect(defaultPort, host).then( - port => new Promise(resolve => { - if (port === defaultPort) { - return resolve(port); - } - const message = process.platform !== 'win32' && - defaultPort < 1024 && - !isRoot() - ? `Admin permissions are required to run a server on a port below 1024.` - : `Something is already running on port ${defaultPort}.`; - if (isInteractive) { - clearConsole(); - const existingProcess = getProcessForPort(defaultPort); - const question = { - type: 'confirm', - name: 'shouldChangePort', - message: chalk.yellow( - message + - `${existingProcess ? ` Probably:\n ${existingProcess}` : ''}` - ) + '\n\nWould you like to run the app on another port instead?', - default: true, - }; - inquirer.prompt(question).then(answer => { - if (answer.shouldChangePort) { - resolve(port); - } else { - resolve(null); - } - }); - } else { - console.log(chalk.red(message)); - resolve(null); - } - }), + port => + new Promise(resolve => { + if (port === defaultPort) { + return resolve(port); + } + const message = + process.platform !== 'win32' && defaultPort < 1024 && !isRoot() + ? `Admin permissions are required to run a server on a port below 1024.` + : `Something is already running on port ${defaultPort}.`; + if (isInteractive) { + clearConsole(); + const existingProcess = getProcessForPort(defaultPort); + const question = { + type: 'confirm', + name: 'shouldChangePort', + message: + chalk.yellow( + message + + `${existingProcess ? ` Probably:\n ${existingProcess}` : ''}` + ) + '\n\nWould you like to run the app on another port instead?', + default: true, + }; + inquirer.prompt(question).then(answer => { + if (answer.shouldChangePort) { + resolve(port); + } else { + resolve(null); + } + }); + } else { + console.log(chalk.red(message)); + resolve(null); + } + }), err => { throw new Error( chalk.red(`Could not find an open port at ${chalk.bold(host)}.`) + diff --git a/packages/react-dev-utils/ansiHTML.js b/packages/react-dev-utils/ansiHTML.js index 5d3e792036c..9f53cea67a3 100644 --- a/packages/react-dev-utils/ansiHTML.js +++ b/packages/react-dev-utils/ansiHTML.js @@ -69,7 +69,8 @@ function ansiHTML(txt) { var open = false; for (var index = 0; index < arr.length; ++index) { var c = arr[index]; - var content = c.content, fg = c.fg; + var content = c.content, + fg = c.fg; var contentParts = content.split('\n'); for (var _index = 0; _index < contentParts.length; ++_index) { diff --git a/packages/react-dev-utils/eslintFormatter.js b/packages/react-dev-utils/eslintFormatter.js index 96b6ce14beb..b7756b7a266 100644 --- a/packages/react-dev-utils/eslintFormatter.js +++ b/packages/react-dev-utils/eslintFormatter.js @@ -82,7 +82,8 @@ function formatter(results) { // it here because we always show at most one error, and // we can only be sure it's an ESLint error before exiting // this function. - output += 'Search for the ' + + output += + 'Search for the ' + chalk.underline(chalk.red('keywords')) + ' to learn more about each error.'; } diff --git a/packages/react-dev-utils/launchEditor.js b/packages/react-dev-utils/launchEditor.js index a1893d13bc8..bcf0a506069 100644 --- a/packages/react-dev-utils/launchEditor.js +++ b/packages/react-dev-utils/launchEditor.js @@ -8,12 +8,12 @@ */ 'use strict'; -var fs = require('fs'); -var path = require('path'); -var child_process = require('child_process'); -var os = require('os'); -var chalk = require('chalk'); -var shellQuote = require('shell-quote'); +const fs = require('fs'); +const path = require('path'); +const child_process = require('child_process'); +const os = require('os'); +const chalk = require('chalk'); +const shellQuote = require('shell-quote'); function isTerminalEditor(editor) { switch (editor) { @@ -28,14 +28,26 @@ function isTerminalEditor(editor) { // Map from full process name to binary that starts the process // We can't just re-use full process name, because it will spawn a new instance // of the app every time -var COMMON_EDITORS = { +const COMMON_EDITORS_OSX = { '/Applications/Atom.app/Contents/MacOS/Atom': 'atom', - '/Applications/Atom Beta.app/Contents/MacOS/Atom Beta': '/Applications/Atom Beta.app/Contents/MacOS/Atom Beta', - '/Applications/Sublime Text.app/Contents/MacOS/Sublime Text': '/Applications/Sublime Text.app/Contents/SharedSupport/bin/subl', - '/Applications/Sublime Text 2.app/Contents/MacOS/Sublime Text 2': '/Applications/Sublime Text 2.app/Contents/SharedSupport/bin/subl', + '/Applications/Atom Beta.app/Contents/MacOS/Atom Beta': + '/Applications/Atom Beta.app/Contents/MacOS/Atom Beta', + '/Applications/Brackets.app/Contents/MacOS/Brackets': 'brackets', + '/Applications/Sublime Text.app/Contents/MacOS/Sublime Text': + '/Applications/Sublime Text.app/Contents/SharedSupport/bin/subl', + '/Applications/Sublime Text 2.app/Contents/MacOS/Sublime Text 2': + '/Applications/Sublime Text 2.app/Contents/SharedSupport/bin/subl', '/Applications/Visual Studio Code.app/Contents/MacOS/Electron': 'code', }; +const COMMON_EDITORS_WIN = [ + 'Brackets.exe', + 'Code.exe', + 'atom.exe', + 'sublime_text.exe', + 'notepad++.exe', +]; + function addWorkspaceToArgumentsIfExists(args, workspace) { if (workspace) { args.unshift(workspace); @@ -44,7 +56,7 @@ function addWorkspaceToArgumentsIfExists(args, workspace) { } function getArgumentsForLineNumber(editor, fileName, lineNumber, workspace) { - var editorBasename = path.basename(editor).replace(/\.(exe|cmd|bat)$/i, ''); + const editorBasename = path.basename(editor).replace(/\.(exe|cmd|bat)$/i, ''); switch (editorBasename) { case 'vim': case 'mvim': @@ -54,11 +66,14 @@ function getArgumentsForLineNumber(editor, fileName, lineNumber, workspace) { case 'Atom Beta': case 'subl': case 'sublime': + case 'sublime_text': case 'wstorm': case 'appcode': case 'charm': case 'idea': return [fileName + ':' + lineNumber]; + case 'notepad++': + return ['-n' + lineNumber, fileName]; case 'joe': case 'emacs': case 'emacsclient': @@ -68,12 +83,15 @@ function getArgumentsForLineNumber(editor, fileName, lineNumber, workspace) { case 'mine': return ['--line', lineNumber, fileName]; case 'code': + case 'Code': return addWorkspaceToArgumentsIfExists( ['-g', fileName + ':' + lineNumber], workspace ); case 'webstorm': case 'webstorm64': + case 'phpstorm': + case 'phpstorm64': return addWorkspaceToArgumentsIfExists( ['--line', lineNumber, fileName], workspace @@ -92,21 +110,41 @@ function guessEditor() { return shellQuote.parse(process.env.REACT_EDITOR); } - // Using `ps x` on OSX we can find out which editor is currently running. - // Potentially we could use similar technique for Windows and Linux - if (process.platform === 'darwin') { - try { - var output = child_process.execSync('ps x').toString(); - var processNames = Object.keys(COMMON_EDITORS); - for (var i = 0; i < processNames.length; i++) { - var processName = processNames[i]; + // Using `ps x` on OSX or `Get-Process` on Windows we can find out which editor is currently running. + // Potentially we could use similar technique for Linux + try { + if (process.platform === 'darwin') { + const output = child_process.execSync('ps x').toString(); + const processNames = Object.keys(COMMON_EDITORS_OSX); + for (let i = 0; i < processNames.length; i++) { + const processName = processNames[i]; if (output.indexOf(processName) !== -1) { - return [COMMON_EDITORS[processName]]; + return [COMMON_EDITORS_OSX[processName]]; + } + } + } else if (process.platform === 'win32') { + const output = child_process + .execSync('powershell -Command "Get-Process | Select-Object Path"', { + stdio: ['pipe', 'pipe', 'ignore'], + }) + .toString(); + const runningProcesses = output.split('\r\n'); + for (let i = 0; i < runningProcesses.length; i++) { + // `Get-Process` sometimes returns empty lines + if (!runningProcesses[i]) { + continue; + } + + const fullProcessPath = runningProcesses[i].trim(); + const shortProcessName = path.basename(fullProcessPath); + + if (COMMON_EDITORS_WIN.indexOf(shortProcessName) !== -1) { + return [fullProcessPath]; } } - } catch (error) { - // Ignore... } + } catch (error) { + // Ignore... } // Last resort, use old skool env vars @@ -139,12 +177,13 @@ function printInstructions(fileName, errorMessage) { ' to the ' + chalk.green('.env.local') + ' file in your project folder ' + - 'and restart the development server.' + 'and restart the development server. Learn more: ' + + chalk.green('https://goo.gl/MMTaZt') ); console.log(); } -var _childProcess = null; +let _childProcess = null; function launchEditor(fileName, lineNumber) { if (!fs.existsSync(fileName)) { return; @@ -176,7 +215,7 @@ function launchEditor(fileName, lineNumber) { fileName = path.relative('', fileName); } - var workspace = null; + let workspace = null; if (lineNumber) { args = args.concat( getArgumentsForLineNumber(editor, fileName, lineNumber, workspace) diff --git a/packages/react-dev-utils/openBrowser.js b/packages/react-dev-utils/openBrowser.js index 9ded7078158..4f5700125b4 100644 --- a/packages/react-dev-utils/openBrowser.js +++ b/packages/react-dev-utils/openBrowser.js @@ -68,7 +68,8 @@ function startBrowserProcess(browser, url) { // requested a different browser, we can try opening // Chrome with AppleScript. This lets us reuse an // existing tab when possible instead of creating a new one. - const shouldTryOpenChromeWithAppleScript = process.platform === 'darwin' && + const shouldTryOpenChromeWithAppleScript = + process.platform === 'darwin' && (typeof browser !== 'string' || browser === OSX_CHROME); if (shouldTryOpenChromeWithAppleScript) { diff --git a/packages/react-dev-utils/package.json b/packages/react-dev-utils/package.json index 2657bbabbbe..d502bf0ac58 100644 --- a/packages/react-dev-utils/package.json +++ b/packages/react-dev-utils/package.json @@ -1,6 +1,6 @@ { "name": "@absolvent/react-dev-utils", - "version": "3.0.0", + "version": "3.0.1", "description": "Webpack utilities used by Create React App", "repository": "facebookincubator/create-react-app", "license": "BSD-3-Clause", @@ -33,8 +33,8 @@ "webpackHotDevClient.js" ], "dependencies": { - "address": "1.0.1", - "anser": "1.3.0", + "address": "1.0.2", + "anser": "1.4.1", "babel-code-frame": "6.22.0", "chalk": "1.1.3", "cross-spawn": "4.0.2", @@ -43,13 +43,13 @@ "filesize": "3.3.0", "gzip-size": "3.0.0", "html-entities": "1.2.1", - "inquirer": "3.0.6", + "inquirer": "3.1.1", "is-root": "1.0.0", - "opn": "5.0.0", + "opn": "5.1.0", "recursive-readdir": "2.2.1", "shell-quote": "1.6.1", "sockjs-client": "1.1.4", - "strip-ansi": "3.0.1", + "strip-ansi": "4.0.0", "text-table": "0.2.0" } } diff --git a/packages/react-dev-utils/printHostingInstructions.js b/packages/react-dev-utils/printHostingInstructions.js index 7b9284d4e59..f9882d44a7c 100644 --- a/packages/react-dev-utils/printHostingInstructions.js +++ b/packages/react-dev-utils/printHostingInstructions.js @@ -23,10 +23,14 @@ function printHostingInstructions( if (publicUrl && publicUrl.indexOf('.github.io/') !== -1) { // "homepage": "http://user.github.io/project" console.log( - `The project was built assuming it is hosted at ${chalk.green(publicPathname)}.` + `The project was built assuming it is hosted at ${chalk.green( + publicPathname + )}.` ); console.log( - `You can control this with the ${chalk.green('homepage')} field in your ${chalk.cyan('package.json')}.` + `You can control this with the ${chalk.green( + 'homepage' + )} field in your ${chalk.cyan('package.json')}.` ); console.log(); console.log(`The ${chalk.cyan('build')} folder is ready to be deployed.`); @@ -48,10 +52,14 @@ function printHostingInstructions( console.log(` ${chalk.yellow('"scripts"')}: {`); console.log(` ${chalk.dim('// ...')}`); console.log( - ` ${chalk.yellow('"predeploy"')}: ${chalk.yellow('"npm run build",')}` + ` ${chalk.yellow('"predeploy"')}: ${chalk.yellow( + '"npm run build",' + )}` ); console.log( - ` ${chalk.yellow('"deploy"')}: ${chalk.yellow('"gh-pages -d build"')}` + ` ${chalk.yellow('"deploy"')}: ${chalk.yellow( + '"gh-pages -d build"' + )}` ); console.log(' }'); console.log(); @@ -63,10 +71,14 @@ function printHostingInstructions( } else if (publicPath !== '/') { // "homepage": "http://mywebsite.com/project" console.log( - `The project was built assuming it is hosted at ${chalk.green(publicPath)}.` + `The project was built assuming it is hosted at ${chalk.green( + publicPath + )}.` ); console.log( - `You can control this with the ${chalk.green('homepage')} field in your ${chalk.cyan('package.json')}.` + `You can control this with the ${chalk.green( + 'homepage' + )} field in your ${chalk.cyan('package.json')}.` ); console.log(); console.log(`The ${chalk.cyan('build')} folder is ready to be deployed.`); @@ -75,10 +87,14 @@ function printHostingInstructions( if (publicUrl) { // "homepage": "http://mywebsite.com" console.log( - `The project was built assuming it is hosted at ${chalk.green(publicUrl)}.` + `The project was built assuming it is hosted at ${chalk.green( + publicUrl + )}.` ); console.log( - `You can control this with the ${chalk.green('homepage')} field in your ${chalk.cyan('package.json')}.` + `You can control this with the ${chalk.green( + 'homepage' + )} field in your ${chalk.cyan('package.json')}.` ); console.log(); } else { @@ -87,12 +103,16 @@ function printHostingInstructions( 'The project was built assuming it is hosted at the server root.' ); console.log( - `To override this, specify the ${chalk.green('homepage')} in your ${chalk.cyan('package.json')}.` + `To override this, specify the ${chalk.green( + 'homepage' + )} in your ${chalk.cyan('package.json')}.` ); console.log('For example, add this to build it for GitHub Pages:'); console.log(); console.log( - ` ${chalk.green('"homepage"')} ${chalk.cyan(':')} ${chalk.green('"http://myname.github.io/myapp"')}${chalk.cyan(',')}` + ` ${chalk.green('"homepage"')} ${chalk.cyan(':')} ${chalk.green( + '"http://myname.github.io/myapp"' + )}${chalk.cyan(',')}` ); console.log(); } diff --git a/packages/react-dev-utils/webpackHotDevClient.js b/packages/react-dev-utils/webpackHotDevClient.js index 86d6fae3286..e54e2b2410d 100644 --- a/packages/react-dev-utils/webpackHotDevClient.js +++ b/packages/react-dev-utils/webpackHotDevClient.js @@ -81,14 +81,16 @@ function addOverlayDivTo(iframe) { } function overlayHeaderStyle() { - return 'font-size: 2em;' + + return ( + 'font-size: 2em;' + 'font-family: sans-serif;' + 'color: rgb(206, 17, 38);' + 'white-space: pre-wrap;' + 'margin: 0 2rem 0.75rem 0px;' + 'flex: 0 0 auto;' + 'max-height: 35%;' + - 'overflow: auto;'; + 'overflow: auto;' + ); } var overlayIframe = null; @@ -127,7 +129,8 @@ function ensureOverlayDivExists(onOverlayDivReady) { function showErrorOverlay(message) { ensureOverlayDivExists(function onOverlayDivReady(overlayDiv) { // TODO: unify this with our runtime overlay - overlayDiv.innerHTML = '<div style="' + + overlayDiv.innerHTML = + '<div style="' + overlayHeaderStyle() + '">Failed to compile</div>' + '<pre style="' + diff --git a/packages/react-error-overlay/package.json b/packages/react-error-overlay/package.json index 88c909e1154..f29aedd0f73 100644 --- a/packages/react-error-overlay/package.json +++ b/packages/react-error-overlay/package.json @@ -1,6 +1,6 @@ { "name": "react-error-overlay", - "version": "1.0.7", + "version": "1.0.8", "description": "An overlay for displaying stack frames.", "main": "lib/index.js", "scripts": { @@ -34,17 +34,17 @@ "anser": "1.2.5", "babel-code-frame": "6.22.0", "babel-runtime": "6.23.0", - "react-dev-utils": "^3.0.0", + "react-dev-utils": "^3.0.1", "settle-promise": "1.0.0", "source-map": "0.5.6" }, "devDependencies": { "babel-cli": "6.24.1", "babel-eslint": "7.2.3", - "babel-preset-react-app": "^3.0.0", + "babel-preset-react-app": "^3.0.1", "cross-env": "5.0.0", "eslint": "3.19.0", - "eslint-config-react-app": "^1.0.4", + "eslint-config-react-app": "^1.0.5", "eslint-plugin-flowtype": "2.33.0", "eslint-plugin-import": "2.2.0", "eslint-plugin-jsx-a11y": "5.0.3", diff --git a/packages/react-error-overlay/src/__tests__/mapper.js b/packages/react-error-overlay/src/__tests__/mapper.js index 64c2cf73018..169bf6c592c 100644 --- a/packages/react-error-overlay/src/__tests__/mapper.js +++ b/packages/react-error-overlay/src/__tests__/mapper.js @@ -14,7 +14,8 @@ import { resolve } from 'path'; test('basic error; 0 context', async () => { expect.assertions(1); - const error = 'TypeError: document.body.missing is not a function\n at App.componentDidMount (http://localhost:3000/static/js/bundle.js:26122:21)\n at http://localhost:3000/static/js/bundle.js:30091:25\n at measureLifeCyclePerf (http://localhost:3000/static/js/bundle.js:29901:12)\n at http://localhost:3000/static/js/bundle.js:30090:11\n at CallbackQueue.notifyAll (http://localhost:3000/static/js/bundle.js:13256:22)\n at ReactReconcileTransaction.close (http://localhost:3000/static/js/bundle.js:35124:26)\n at ReactReconcileTransaction.closeAll (http://localhost:3000/static/js/bundle.js:7390:25)\n at ReactReconcileTransaction.perform (http://localhost:3000/static/js/bundle.js:7337:16)\n at batchedMountComponentIntoNode (http://localhost:3000/static/js/bundle.js:14204:15)\n at ReactDefaultBatchingStrategyTransaction.perform (http://localhost:3000/static/js/bundle.js:7324:20)\n at Object.batchedUpdates (http://localhost:3000/static/js/bundle.js:33900:26)\n at Object.batchedUpdates (http://localhost:3000/static/js/bundle.js:2181:27)\n at Object._renderNewRootComponent (http://localhost:3000/static/js/bundle.js:14398:18)\n at Object._renderSubtreeIntoContainer (http://localhost:3000/static/js/bundle.js:14479:32)\n at Object.render (http://localhost:3000/static/js/bundle.js:14500:23)\n at Object.friendlySyntaxErrorLabel (http://localhost:3000/static/js/bundle.js:17287:20)\n at __webpack_require__ (http://localhost:3000/static/js/bundle.js:660:30)\n at fn (http://localhost:3000/static/js/bundle.js:84:20)\n at Object.<anonymous> (http://localhost:3000/static/js/bundle.js:41219:18)\n at __webpack_require__ (http://localhost:3000/static/js/bundle.js:660:30)\n at validateFormat (http://localhost:3000/static/js/bundle.js:709:39)\n at http://localhost:3000/static/js/bundle.js:712:10'; + const error = + 'TypeError: document.body.missing is not a function\n at App.componentDidMount (http://localhost:3000/static/js/bundle.js:26122:21)\n at http://localhost:3000/static/js/bundle.js:30091:25\n at measureLifeCyclePerf (http://localhost:3000/static/js/bundle.js:29901:12)\n at http://localhost:3000/static/js/bundle.js:30090:11\n at CallbackQueue.notifyAll (http://localhost:3000/static/js/bundle.js:13256:22)\n at ReactReconcileTransaction.close (http://localhost:3000/static/js/bundle.js:35124:26)\n at ReactReconcileTransaction.closeAll (http://localhost:3000/static/js/bundle.js:7390:25)\n at ReactReconcileTransaction.perform (http://localhost:3000/static/js/bundle.js:7337:16)\n at batchedMountComponentIntoNode (http://localhost:3000/static/js/bundle.js:14204:15)\n at ReactDefaultBatchingStrategyTransaction.perform (http://localhost:3000/static/js/bundle.js:7324:20)\n at Object.batchedUpdates (http://localhost:3000/static/js/bundle.js:33900:26)\n at Object.batchedUpdates (http://localhost:3000/static/js/bundle.js:2181:27)\n at Object._renderNewRootComponent (http://localhost:3000/static/js/bundle.js:14398:18)\n at Object._renderSubtreeIntoContainer (http://localhost:3000/static/js/bundle.js:14479:32)\n at Object.render (http://localhost:3000/static/js/bundle.js:14500:23)\n at Object.friendlySyntaxErrorLabel (http://localhost:3000/static/js/bundle.js:17287:20)\n at __webpack_require__ (http://localhost:3000/static/js/bundle.js:660:30)\n at fn (http://localhost:3000/static/js/bundle.js:84:20)\n at Object.<anonymous> (http://localhost:3000/static/js/bundle.js:41219:18)\n at __webpack_require__ (http://localhost:3000/static/js/bundle.js:660:30)\n at validateFormat (http://localhost:3000/static/js/bundle.js:709:39)\n at http://localhost:3000/static/js/bundle.js:712:10'; fetch.mockResponseOnce( fs @@ -38,7 +39,8 @@ test('basic error; 0 context', async () => { test('default context (3)', async () => { expect.assertions(1); - const error = 'TypeError: document.body.missing is not a function\n at App.componentDidMount (http://localhost:3000/static/js/bundle.js:26122:21)'; + const error = + 'TypeError: document.body.missing is not a function\n at App.componentDidMount (http://localhost:3000/static/js/bundle.js:26122:21)'; fetch.mockResponseOnce( fs @@ -62,7 +64,8 @@ test('default context (3)', async () => { test('bad comes back same', async () => { expect.assertions(2); - const error = 'TypeError: document.body.missing is not a function\n at App.componentDidMount (A:1:2)'; + const error = + 'TypeError: document.body.missing is not a function\n at App.componentDidMount (A:1:2)'; const orig = parse(error); expect(orig).toEqual([ { diff --git a/packages/react-error-overlay/src/components/frame.js b/packages/react-error-overlay/src/components/frame.js index 3b27406dd16..43d0d4043a3 100644 --- a/packages/react-error-overlay/src/components/frame.js +++ b/packages/react-error-overlay/src/components/frame.js @@ -242,8 +242,8 @@ function createFrame( let needsHidden = false; const isInternalUrl = isInternalFile(sourceFileName, fileName); const isThrownIntentionally = !isBultinErrorName(errorName); - const shouldCollapse = isInternalUrl && - (isThrownIntentionally || omits.hasReachedAppCode); + const shouldCollapse = + isInternalUrl && (isThrownIntentionally || omits.hasReachedAppCode); if (!isInternalUrl) { omits.hasReachedAppCode = true; @@ -281,9 +281,8 @@ function createFrame( let onSourceClick = null; if (sourceFileName) { // e.g. "/path-to-my-app/webpack/bootstrap eaddeb46b67d75e4dfc1" - const isInternalWebpackBootstrapCode = sourceFileName - .trim() - .indexOf(' ') !== -1; + const isInternalWebpackBootstrapCode = + sourceFileName.trim().indexOf(' ') !== -1; if (!isInternalWebpackBootstrapCode) { onSourceClick = () => { // Keep this in sync with react-error-overlay/middleware.js @@ -312,7 +311,10 @@ function createFrame( let hasSource = false; if (!shouldCollapse) { if ( - compiled && scriptLines && scriptLines.length !== 0 && lineNumber != null + compiled && + scriptLines && + scriptLines.length !== 0 && + lineNumber != null ) { elem.appendChild( createCode( diff --git a/packages/react-error-overlay/src/components/overlay.js b/packages/react-error-overlay/src/components/overlay.js index 573db8def5f..69acf9ad43f 100644 --- a/packages/react-error-overlay/src/components/overlay.js +++ b/packages/react-error-overlay/src/components/overlay.js @@ -60,9 +60,8 @@ function createOverlay( applyStyles(header, headerStyle); // Make message prettier - let finalMessage = message.match(/^\w*:/) || !name - ? message - : name + ': ' + message; + let finalMessage = + message.match(/^\w*:/) || !name ? message : name + ': ' + message; finalMessage = finalMessage // TODO: maybe remove this prefix from fbjs? diff --git a/packages/react-error-overlay/src/utils/isInternalFile.js b/packages/react-error-overlay/src/utils/isInternalFile.js index 71beea7cc57..c78bbe3ed5d 100644 --- a/packages/react-error-overlay/src/utils/isInternalFile.js +++ b/packages/react-error-overlay/src/utils/isInternalFile.js @@ -9,13 +9,15 @@ /* @flow */ function isInternalFile(sourceFileName: ?string, fileName: ?string) { - return sourceFileName == null || + return ( + sourceFileName == null || sourceFileName === '' || sourceFileName.indexOf('/~/') !== -1 || sourceFileName.indexOf('/node_modules/') !== -1 || sourceFileName.trim().indexOf(' ') !== -1 || fileName == null || - fileName === ''; + fileName === '' + ); } export { isInternalFile }; diff --git a/packages/react-error-overlay/src/utils/unmapper.js b/packages/react-error-overlay/src/utils/unmapper.js index 74e38955555..b01736d74aa 100644 --- a/packages/react-error-overlay/src/utils/unmapper.js +++ b/packages/react-error-overlay/src/utils/unmapper.js @@ -15,7 +15,8 @@ import path from 'path'; function count(search: string, string: string): number { // Count starts at -1 becuse a do-while loop always runs at least once - let count = -1, index = -1; + let count = -1, + index = -1; do { // First call or the while case evaluated true, meaning we have to make // count 0 or we found a character diff --git a/packages/react-scripts/config/env.js b/packages/react-scripts/config/env.js index e7d7f9f3206..ebef79ed9e0 100644 --- a/packages/react-scripts/config/env.js +++ b/packages/react-scripts/config/env.js @@ -88,13 +88,10 @@ function getClientEnvironment(publicUrl) { ); // Stringify all values so we can feed into Webpack DefinePlugin const stringified = { - 'process.env': Object.keys(raw).reduce( - (env, key) => { - env[key] = JSON.stringify(raw[key]); - return env; - }, - {} - ), + 'process.env': Object.keys(raw).reduce((env, key) => { + env[key] = JSON.stringify(raw[key]); + return env; + }, {}), }; return { raw, stringified }; diff --git a/packages/react-scripts/config/paths.js b/packages/react-scripts/config/paths.js index 42ec8374a15..94e399f0f4e 100644 --- a/packages/react-scripts/config/paths.js +++ b/packages/react-scripts/config/paths.js @@ -43,8 +43,8 @@ const getPublicUrl = appPackageJson => // like /todos/42/static/js/bundle.7289d.js. We have to know the root. function getServedPath(appPackageJson) { const publicUrl = getPublicUrl(appPackageJson); - const servedUrl = envPublicUrl || - (publicUrl ? url.parse(publicUrl).pathname : '/'); + const servedUrl = + envPublicUrl || (publicUrl ? url.parse(publicUrl).pathname : '/'); return ensureSlash(servedUrl, true); } @@ -89,7 +89,8 @@ module.exports = { const ownPackageJson = require('../package.json'); const reactScriptsPath = resolveApp(`node_modules/${ownPackageJson.name}`); -const reactScriptsLinked = fs.existsSync(reactScriptsPath) && +const reactScriptsLinked = + fs.existsSync(reactScriptsPath) && fs.lstatSync(reactScriptsPath).isSymbolicLink(); // config before publish: we're in ./packages/react-scripts/config/ diff --git a/packages/react-scripts/config/webpack.config.dev.js b/packages/react-scripts/config/webpack.config.dev.js index 34769417aae..7f709e65b71 100644 --- a/packages/react-scripts/config/webpack.config.dev.js +++ b/packages/react-scripts/config/webpack.config.dev.js @@ -78,9 +78,9 @@ module.exports = { chunkFilename: 'static/js/[name].chunk.js', // This is the URL that app is served from. We use "/" in development. publicPath: 'http://localhost:3000/', - // Point sourcemap entries to original disk location + // Point sourcemap entries to original disk location (format as URL on Windows) devtoolModuleFilenameTemplate: info => - path.resolve(info.absoluteResourcePath), + path.resolve(info.absoluteResourcePath).replace(/\\/g, '/'), }, resolve: { // This allows you to set a fallback for where Webpack should look for modules. @@ -95,7 +95,9 @@ module.exports = { // We also include JSX as a common component filename extension to support // some tools, although we do not recommend using it, see: // https://github.com/facebookincubator/create-react-app/issues/290 - extensions: ['.js', '.json', '.jsx'], + // `web` extension prefixes have been added for better support + // for React Native Web. + extensions: ['.web.js', '.js', '.json', '.web.jsx', '.jsx'], alias: { // @remove-on-eject-begin // Resolve Babel runtime relative to react-scripts. @@ -255,7 +257,6 @@ module.exports = { { loader: require.resolve('postcss-loader'), options: { - ident: 'postcss', // https://webpack.js.org/guides/migrating/#complex-options plugins: () => [ require('postcss-flexbugs-fixes'), autoprefixer({ @@ -313,6 +314,7 @@ module.exports = { // Some libraries import Node modules but don't use them in the browser. // Tell Webpack to provide empty mocks for them so importing them works. node: { + dgram: 'empty', fs: 'empty', net: 'empty', tls: 'empty', diff --git a/packages/react-scripts/config/webpack.config.prod.js b/packages/react-scripts/config/webpack.config.prod.js index 972063bd890..5bbc2836e2e 100644 --- a/packages/react-scripts/config/webpack.config.prod.js +++ b/packages/react-scripts/config/webpack.config.prod.js @@ -75,9 +75,11 @@ module.exports = { chunkFilename: 'static/js/[name].[chunkhash:8].chunk.js', // We inferred the "public path" (such as / or /my-project) from homepage. publicPath: publicPath, - // Point sourcemap entries to original disk location + // Point sourcemap entries to original disk location (format as URL on Windows) devtoolModuleFilenameTemplate: info => - path.relative(paths.appSrc, info.absoluteResourcePath), + path + .relative(paths.appSrc, info.absoluteResourcePath) + .replace(/\\/g, '/'), }, resolve: { // This allows you to set a fallback for where Webpack should look for modules. @@ -92,7 +94,9 @@ module.exports = { // We also include JSX as a common component filename extension to support // some tools, although we do not recommend using it, see: // https://github.com/facebookincubator/create-react-app/issues/290 - extensions: ['.js', '.json', '.jsx'], + // `web` extension prefixes have been added for better support + // for React Native Web. + extensions: ['.web.js', '.js', '.json', '.web.jsx', '.jsx'], alias: { // @remove-on-eject-begin // Resolve Babel runtime relative to react-scripts. @@ -215,16 +219,17 @@ module.exports = { test: /\.(js|jsx)$/, include: paths.appSrc, loader: require.resolve('babel-loader'), - // @remove-on-eject-begin options: { + // @remove-on-eject-begin babelrc: false, presets: [require.resolve('babel-preset-react-app')], plugins: [ // this enables decorators require.resolve('babel-plugin-transform-decorators-legacy'), ], + // @remove-on-eject-end + compact: true, }, - // @remove-on-eject-end }, // The notation here is somewhat confusing. // "postcss" loader applies autoprefixer to our CSS. @@ -256,7 +261,6 @@ module.exports = { { loader: require.resolve('postcss-loader'), options: { - ident: 'postcss', // https://webpack.js.org/guides/migrating/#complex-options plugins: () => [ require('postcss-flexbugs-fixes'), autoprefixer({ @@ -323,6 +327,9 @@ module.exports = { }, output: { comments: false, + // Turned on because emoji and regex is not minified properly using default + // https://github.com/facebookincubator/create-react-app/issues/2488 + ascii_only: true, }, sourceMap: true, }), @@ -350,6 +357,11 @@ module.exports = { // This message occurs for every build and is a bit too noisy. return; } + if (message.indexOf('Skipping static resource') === 0) { + // This message obscures real errors so we ignore it. + // https://github.com/facebookincubator/create-react-app/issues/2612 + return; + } console.log(message); }, minify: true, @@ -360,9 +372,6 @@ module.exports = { navigateFallbackWhitelist: [/^(?!\/__).*/], // Don't precache sourcemaps (they're large) and build asset manifest: staticFileGlobsIgnorePatterns: [/\.map$/, /asset-manifest\.json$/], - // Work around Windows path issue in SWPrecacheWebpackPlugin: - // https://github.com/facebookincubator/create-react-app/issues/2235 - stripPrefix: paths.appBuild.replace(/\\/g, '/') + '/', }), // Moment.js is an extremely popular library that bundles large locale files // by default due to how Webpack interprets its code. This is a practical @@ -374,6 +383,7 @@ module.exports = { // Some libraries import Node modules but don't use them in the browser. // Tell Webpack to provide empty mocks for them so importing them works. node: { + dgram: 'empty', fs: 'empty', net: 'empty', tls: 'empty', diff --git a/packages/react-scripts/config/webpackDevServer.config.js b/packages/react-scripts/config/webpackDevServer.config.js index 3cfe6118064..aaabf42b8c1 100644 --- a/packages/react-scripts/config/webpackDevServer.config.js +++ b/packages/react-scripts/config/webpackDevServer.config.js @@ -36,8 +36,8 @@ module.exports = function(proxy, allowedHost) { // So we will disable the host check normally, but enable it if you have // specified the `proxy` setting. Finally, we let you override it if you // really know what you're doing with a special environment variable. - disableHostCheck: !proxy || - process.env.DANGEROUSLY_DISABLE_HOST_CHECK === 'true', + disableHostCheck: + !proxy || process.env.DANGEROUSLY_DISABLE_HOST_CHECK === 'true', // Enable gzip compression of generated files. compress: true, // Silence WebpackDevServer's own logs since they're generally not useful. diff --git a/packages/react-scripts/fixtures/kitchensink/README.md b/packages/react-scripts/fixtures/kitchensink/README.md new file mode 100644 index 00000000000..4e7725ce15f --- /dev/null +++ b/packages/react-scripts/fixtures/kitchensink/README.md @@ -0,0 +1,54 @@ +# Contributing to Create React App's E2E tests + +This is an end to end kitchensink test suite, but has multiple usages in it. + +## Running the test suite + +Tests are automatically run by the CI tools. +In order to run them locally, without having to manually install and configure everything, the `yarn e2e:docker` CLI command can be used. + +This is a simple script that runs a **Docker** container, where the node version, git branch to clone, test suite, and whether to run it with `yarn` or `npm` can be chosen. +Simply run `yarn e2e:docker -- --help` to get additional info. + +If you need guidance installing **Docker**, you should follow their [official docs](https://docs.docker.com/engine/installation/). + +## Writing tests + +Each time a new feature is added, it is advised to add at least one test covering it. + +Features are categorized by their scope: + + - *env*, all those which deal with environment variables (e.g. `NODE_PATH`) + + - *syntax*, all those which showcase a single EcmaScript syntax feature that is expected to be transpiled by **Babel** + + - *webpack*, all those which make use of webpack settings, loaders or plugins + +### Using it as Unit Tests + +In it's most basic for this serve as a collection of unit tests on a single functionality. + +Unit tests are written in a `src/features/**/*.test.js` file located in the same folder as the feature they test, and usually consist of a simple `ReactDOM.render` call. + +These tests are run by **jest** and the environment is `test`, so that it resembles how a **Create React App** application is tested. + +### Using it as Integration Tests + +This suite tests how the single features as before behave while development and in production. +A local HTTP server is started, then every single feature is loaded, one by one, to be tested. + +Test are written in `integration/{env|syntax|webpack}.test.js`, depending on their scope. + +For every test case added there is just a little chore to do: + + - a `case` statement must be added in `src/App.js`, which simply perform a dynamic `import()` of the feature + + - add a test case in the appropriate integration test file, which calls and awaits `initDOM` with the previous `SwitchCase` string + +An usual flow for the test itself is something similar to: + + - add an `id` attribute in a target HTML tag in the feature itself + + - since `initDOM` returns a `Document` element, the previous `id` attribute is used to target the feature's DOM and `expect` accordingly + +These tests are run by **mocha** (why not **jest**? See [this issue](https://github.com/facebook/jest/issues/2288)) and the environments used are both `development` and `production`. diff --git a/packages/react-scripts/fixtures/kitchensink/integration/env.test.js b/packages/react-scripts/fixtures/kitchensink/integration/env.test.js index 89d19fcf4b1..a7d4ff2a2d7 100644 --- a/packages/react-scripts/fixtures/kitchensink/integration/env.test.js +++ b/packages/react-scripts/fixtures/kitchensink/integration/env.test.js @@ -50,9 +50,10 @@ describe('Integration', () => { it('PUBLIC_URL', async () => { const doc = await initDOM('public-url'); - const prefix = process.env.NODE_ENV === 'development' - ? '' - : 'http://www.example.org/spa'; + const prefix = + process.env.NODE_ENV === 'development' + ? '' + : 'http://www.example.org/spa'; expect(doc.getElementById('feature-public-url').textContent).to.equal( `${prefix}.` ); diff --git a/packages/react-scripts/fixtures/kitchensink/integration/initDOM.js b/packages/react-scripts/fixtures/kitchensink/integration/initDOM.js index 4d6f531ea84..b865b5641a3 100644 --- a/packages/react-scripts/fixtures/kitchensink/integration/initDOM.js +++ b/packages/react-scripts/fixtures/kitchensink/integration/initDOM.js @@ -38,13 +38,14 @@ if (process.env.E2E_FILE) { ) ); } else if (process.env.E2E_URL) { - getMarkup = () => new Promise(resolve => { - http.get(process.env.E2E_URL, res => { - let rawData = ''; - res.on('data', chunk => rawData += chunk); - res.on('end', () => resolve(rawData)); + getMarkup = () => + new Promise(resolve => { + http.get(process.env.E2E_URL, res => { + let rawData = ''; + res.on('data', chunk => (rawData += chunk)); + res.on('end', () => resolve(rawData)); + }); }); - }); resourceLoader = (resource, callback) => resource.defaultFetch(callback); } else { @@ -58,21 +59,22 @@ if (process.env.E2E_FILE) { ); } -export default feature => new Promise(async resolve => { - const markup = await getMarkup(); - const host = process.env.E2E_URL || 'http://www.example.org/spa:3000'; - const doc = jsdom.jsdom(markup, { - features: { - FetchExternalResources: ['script', 'css'], - ProcessExternalResources: ['script'], - }, - created: (_, win) => - win.addEventListener('ReactFeatureDidMount', () => resolve(doc), true), - deferClose: true, - resourceLoader, - url: `${host}#${feature}`, - virtualConsole: jsdom.createVirtualConsole().sendTo(console), - }); +export default feature => + new Promise(async resolve => { + const markup = await getMarkup(); + const host = process.env.E2E_URL || 'http://www.example.org/spa:3000'; + const doc = jsdom.jsdom(markup, { + features: { + FetchExternalResources: ['script', 'css'], + ProcessExternalResources: ['script'], + }, + created: (_, win) => + win.addEventListener('ReactFeatureDidMount', () => resolve(doc), true), + deferClose: true, + resourceLoader, + url: `${host}#${feature}`, + virtualConsole: jsdom.createVirtualConsole().sendTo(console), + }); - doc.close(); -}); + doc.close(); + }); diff --git a/packages/react-scripts/fixtures/kitchensink/src/App.js b/packages/react-scripts/fixtures/kitchensink/src/App.js index 3a1981a4000..cb342b06247 100644 --- a/packages/react-scripts/fixtures/kitchensink/src/App.js +++ b/packages/react-scripts/fixtures/kitchensink/src/App.js @@ -30,10 +30,7 @@ class BuiltEmitter extends Component { } render() { - const { - props: { feature }, - handleReady, - } = this; + const { props: { feature }, handleReady } = this; return ( <div> {createElement(feature, { @@ -57,114 +54,132 @@ class App extends Component { const feature = window.location.hash.slice(1); switch (feature) { case 'array-destructuring': - import( - './features/syntax/ArrayDestructuring' - ).then(f => this.setFeature(f.default)); + import('./features/syntax/ArrayDestructuring').then(f => + this.setFeature(f.default) + ); break; case 'array-spread': import('./features/syntax/ArraySpread').then(f => - this.setFeature(f.default)); + this.setFeature(f.default) + ); break; case 'async-await': import('./features/syntax/AsyncAwait').then(f => - this.setFeature(f.default)); + this.setFeature(f.default) + ); break; case 'class-properties': import('./features/syntax/ClassProperties').then(f => - this.setFeature(f.default)); + this.setFeature(f.default) + ); break; case 'computed-properties': - import( - './features/syntax/ComputedProperties' - ).then(f => this.setFeature(f.default)); + import('./features/syntax/ComputedProperties').then(f => + this.setFeature(f.default) + ); break; case 'css-inclusion': import('./features/webpack/CssInclusion').then(f => - this.setFeature(f.default)); + this.setFeature(f.default) + ); break; case 'custom-interpolation': - import( - './features/syntax/CustomInterpolation' - ).then(f => this.setFeature(f.default)); + import('./features/syntax/CustomInterpolation').then(f => + this.setFeature(f.default) + ); break; case 'default-parameters': import('./features/syntax/DefaultParameters').then(f => - this.setFeature(f.default)); + this.setFeature(f.default) + ); break; case 'destructuring-and-await': - import( - './features/syntax/DestructuringAndAwait' - ).then(f => this.setFeature(f.default)); + import('./features/syntax/DestructuringAndAwait').then(f => + this.setFeature(f.default) + ); break; case 'file-env-variables': import('./features/env/FileEnvVariables').then(f => - this.setFeature(f.default)); + this.setFeature(f.default) + ); break; case 'generators': import('./features/syntax/Generators').then(f => - this.setFeature(f.default)); + this.setFeature(f.default) + ); break; case 'image-inclusion': import('./features/webpack/ImageInclusion').then(f => - this.setFeature(f.default)); + this.setFeature(f.default) + ); break; case 'json-inclusion': import('./features/webpack/JsonInclusion').then(f => - this.setFeature(f.default)); + this.setFeature(f.default) + ); break; case 'linked-modules': import('./features/webpack/LinkedModules').then(f => - this.setFeature(f.default)); + this.setFeature(f.default) + ); break; case 'node-path': import('./features/env/NodePath').then(f => this.setFeature(f.default)); break; case 'no-ext-inclusion': import('./features/webpack/NoExtInclusion').then(f => - this.setFeature(f.default)); + this.setFeature(f.default) + ); break; case 'object-destructuring': - import( - './features/syntax/ObjectDestructuring' - ).then(f => this.setFeature(f.default)); + import('./features/syntax/ObjectDestructuring').then(f => + this.setFeature(f.default) + ); break; case 'object-spread': import('./features/syntax/ObjectSpread').then(f => - this.setFeature(f.default)); + this.setFeature(f.default) + ); break; case 'promises': import('./features/syntax/Promises').then(f => - this.setFeature(f.default)); + this.setFeature(f.default) + ); break; case 'public-url': import('./features/env/PublicUrl').then(f => - this.setFeature(f.default)); + this.setFeature(f.default) + ); break; case 'rest-and-default': import('./features/syntax/RestAndDefault').then(f => - this.setFeature(f.default)); + this.setFeature(f.default) + ); break; case 'rest-parameters': import('./features/syntax/RestParameters').then(f => - this.setFeature(f.default)); + this.setFeature(f.default) + ); break; case 'shell-env-variables': import('./features/env/ShellEnvVariables').then(f => - this.setFeature(f.default)); + this.setFeature(f.default) + ); break; case 'svg-inclusion': import('./features/webpack/SvgInclusion').then(f => - this.setFeature(f.default)); + this.setFeature(f.default) + ); break; case 'template-interpolation': - import( - './features/syntax/TemplateInterpolation' - ).then(f => this.setFeature(f.default)); + import('./features/syntax/TemplateInterpolation').then(f => + this.setFeature(f.default) + ); break; case 'unknown-ext-inclusion': - import( - './features/webpack/UnknownExtInclusion' - ).then(f => this.setFeature(f.default)); + import('./features/webpack/UnknownExtInclusion').then(f => + this.setFeature(f.default) + ); break; default: throw new Error(`Missing feature "${feature}"`); diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/env/FileEnvVariables.js b/packages/react-scripts/fixtures/kitchensink/src/features/env/FileEnvVariables.js index 55ae368e437..03d6384719e 100644 --- a/packages/react-scripts/fixtures/kitchensink/src/features/env/FileEnvVariables.js +++ b/packages/react-scripts/fixtures/kitchensink/src/features/env/FileEnvVariables.js @@ -9,7 +9,7 @@ import React from 'react'; -export default () => ( +export default () => <span> <span id="feature-file-env-original-1"> {process.env.REACT_APP_ORIGINAL_1} @@ -18,8 +18,10 @@ export default () => ( {process.env.REACT_APP_ORIGINAL_2} </span> <span id="feature-file-env"> - {process.env.REACT_APP_DEVELOPMENT}{process.env.REACT_APP_PRODUCTION} + {process.env.REACT_APP_DEVELOPMENT} + {process.env.REACT_APP_PRODUCTION} </span> - <span id="feature-file-env-x">{process.env.REACT_APP_X}</span> - </span> -); + <span id="feature-file-env-x"> + {process.env.REACT_APP_X} + </span> + </span>; diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/env/NodePath.js b/packages/react-scripts/fixtures/kitchensink/src/features/env/NodePath.js index a039cefedf3..6d2437a641f 100644 --- a/packages/react-scripts/fixtures/kitchensink/src/features/env/NodePath.js +++ b/packages/react-scripts/fixtures/kitchensink/src/features/env/NodePath.js @@ -33,7 +33,11 @@ export default class extends Component { render() { return ( <div id="feature-node-path"> - {this.state.users.map(user => <div key={user.id}>{user.name}</div>)} + {this.state.users.map(user => + <div key={user.id}> + {user.name} + </div> + )} </div> ); } diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/env/PublicUrl.js b/packages/react-scripts/fixtures/kitchensink/src/features/env/PublicUrl.js index 4ea9b96f8b2..af87748e63e 100644 --- a/packages/react-scripts/fixtures/kitchensink/src/features/env/PublicUrl.js +++ b/packages/react-scripts/fixtures/kitchensink/src/features/env/PublicUrl.js @@ -9,6 +9,7 @@ import React from 'react'; -export default () => ( - <span id="feature-public-url">{process.env.PUBLIC_URL}.</span> -); +export default () => + <span id="feature-public-url"> + {process.env.PUBLIC_URL}. + </span>; diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/env/ShellEnvVariables.js b/packages/react-scripts/fixtures/kitchensink/src/features/env/ShellEnvVariables.js index 8449097d69a..400dfc013be 100644 --- a/packages/react-scripts/fixtures/kitchensink/src/features/env/ShellEnvVariables.js +++ b/packages/react-scripts/fixtures/kitchensink/src/features/env/ShellEnvVariables.js @@ -9,8 +9,7 @@ import React from 'react'; -export default () => ( +export default () => <span id="feature-shell-env-variables"> {process.env.REACT_APP_SHELL_ENV_MESSAGE}. - </span> -); + </span>; diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ArrayDestructuring.js b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ArrayDestructuring.js index be6c39f9007..de0576549c5 100644 --- a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ArrayDestructuring.js +++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ArrayDestructuring.js @@ -38,7 +38,11 @@ export default class extends Component { <div id="feature-array-destructuring"> {this.state.users.map(user => { const [id, name] = user; - return <div key={id}>{name}</div>; + return ( + <div key={id}> + {name} + </div> + ); })} </div> ); diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ArraySpread.js b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ArraySpread.js index eb7886aa47b..ebf90ef6ed8 100644 --- a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ArraySpread.js +++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ArraySpread.js @@ -41,7 +41,11 @@ export default class extends Component { render() { return ( <div id="feature-array-spread"> - {this.state.users.map(user => <div key={user.id}>{user.name}</div>)} + {this.state.users.map(user => + <div key={user.id}> + {user.name} + </div> + )} </div> ); } diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/AsyncAwait.js b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/AsyncAwait.js index a60633460b3..c91da311da6 100644 --- a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/AsyncAwait.js +++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/AsyncAwait.js @@ -41,7 +41,11 @@ export default class extends Component { render() { return ( <div id="feature-async-await"> - {this.state.users.map(user => <div key={user.id}>{user.name}</div>)} + {this.state.users.map(user => + <div key={user.id}> + {user.name} + </div> + )} </div> ); } diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ClassProperties.js b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ClassProperties.js index ed96d4f8c9e..58ae10763dc 100644 --- a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ClassProperties.js +++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ClassProperties.js @@ -29,7 +29,11 @@ export default class extends Component { render() { return ( <div id="feature-class-properties"> - {this.users.map(user => <div key={user.id}>{user.name}</div>)} + {this.users.map(user => + <div key={user.id}> + {user.name} + </div> + )} </div> ); } diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ComputedProperties.js b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ComputedProperties.js index 38dc797a8d5..fcbf721e88b 100644 --- a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ComputedProperties.js +++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ComputedProperties.js @@ -41,9 +41,11 @@ export default class extends Component { render() { return ( <div id="feature-computed-properties"> - {this.state.users.map(user => ( - <div key={user.id}>{user.user_name}</div> - ))} + {this.state.users.map(user => + <div key={user.id}> + {user.user_name} + </div> + )} </div> ); } diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/CustomInterpolation.js b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/CustomInterpolation.js index 1a0123391ce..ab648255a8d 100644 --- a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/CustomInterpolation.js +++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/CustomInterpolation.js @@ -53,9 +53,11 @@ export default class extends Component { return ( <div id="feature-custom-interpolation"> - {this.state.users.map(user => ( - <div key={user.id} style={veryInlineStyle}>{user.name}</div> - ))} + {this.state.users.map(user => + <div key={user.id} style={veryInlineStyle}> + {user.name} + </div> + )} </div> ); } diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/DefaultParameters.js b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/DefaultParameters.js index 0a519eba839..6ebabaec7ea 100644 --- a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/DefaultParameters.js +++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/DefaultParameters.js @@ -41,7 +41,11 @@ export default class extends Component { render() { return ( <div id="feature-default-parameters"> - {this.state.users.map(user => <div key={user.id}>{user.name}</div>)} + {this.state.users.map(user => + <div key={user.id}> + {user.name} + </div> + )} </div> ); } diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/DestructuringAndAwait.js b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/DestructuringAndAwait.js index d44f4cf222c..aa8c9d7db79 100644 --- a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/DestructuringAndAwait.js +++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/DestructuringAndAwait.js @@ -43,7 +43,11 @@ export default class extends Component { render() { return ( <div id="feature-destructuring-and-await"> - {this.state.users.map(user => <div key={user.id}>{user.name}</div>)} + {this.state.users.map(user => + <div key={user.id}> + {user.name} + </div> + )} </div> ); } diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/Generators.js b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/Generators.js index 2fe473d1339..44b2776e1f3 100644 --- a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/Generators.js +++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/Generators.js @@ -43,7 +43,11 @@ export default class extends Component { render() { return ( <div id="feature-generators"> - {this.state.users.map(user => <div key={user.id}>{user.name}</div>)} + {this.state.users.map(user => + <div key={user.id}> + {user.name} + </div> + )} </div> ); } diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ObjectDestructuring.js b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ObjectDestructuring.js index 2994d14af4d..8a7b1095f10 100644 --- a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ObjectDestructuring.js +++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ObjectDestructuring.js @@ -43,7 +43,11 @@ export default class extends Component { <div id="feature-object-destructuring"> {this.state.users.map(user => { const { id, name } = user; - return <div key={id}>{name}</div>; + return ( + <div key={id}> + {name} + </div> + ); })} </div> ); diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ObjectSpread.js b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ObjectSpread.js index 65705f11bbd..5ff4e10ac75 100644 --- a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ObjectSpread.js +++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ObjectSpread.js @@ -41,9 +41,11 @@ export default class extends Component { render() { return ( <div id="feature-object-spread"> - {this.state.users.map(user => ( - <div key={user.id}>{user.name}: {user.age}</div> - ))} + {this.state.users.map(user => + <div key={user.id}> + {user.name}: {user.age} + </div> + )} </div> ); } diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/Promises.js b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/Promises.js index e626de5d818..31ef2c9ebb8 100644 --- a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/Promises.js +++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/Promises.js @@ -42,7 +42,11 @@ export default class extends Component { render() { return ( <div id="feature-promises"> - {this.state.users.map(user => <div key={user.id}>{user.name}</div>)} + {this.state.users.map(user => + <div key={user.id}> + {user.name} + </div> + )} </div> ); } diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/RestAndDefault.js b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/RestAndDefault.js index dc2a1563a41..9e3e3fbabc4 100644 --- a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/RestAndDefault.js +++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/RestAndDefault.js @@ -41,7 +41,11 @@ export default class extends Component { render() { return ( <div id="feature-rest-and-default"> - {this.state.users.map(user => <div key={user.id}>{user.name}</div>)} + {this.state.users.map(user => + <div key={user.id}> + {user.name} + </div> + )} </div> ); } diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/RestParameters.js b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/RestParameters.js index e703a33cc86..98a0e7edc44 100644 --- a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/RestParameters.js +++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/RestParameters.js @@ -41,7 +41,11 @@ export default class extends Component { render() { return ( <div id="feature-rest-parameters"> - {this.state.users.map(user => <div key={user.id}>{user.name}</div>)} + {this.state.users.map(user => + <div key={user.id}> + {user.name} + </div> + )} </div> ); } diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/TemplateInterpolation.js b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/TemplateInterpolation.js index 5aba5da6272..b69f7ede8bd 100644 --- a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/TemplateInterpolation.js +++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/TemplateInterpolation.js @@ -41,7 +41,11 @@ export default class extends Component { render() { return ( <div id="feature-template-interpolation"> - {this.state.users.map(user => <div key={user.id}>{user.name}</div>)} + {this.state.users.map(user => + <div key={user.id}> + {user.name} + </div> + )} </div> ); } diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/webpack/ImageInclusion.js b/packages/react-scripts/fixtures/kitchensink/src/features/webpack/ImageInclusion.js index a6ca7aaa818..8598d8d067d 100644 --- a/packages/react-scripts/fixtures/kitchensink/src/features/webpack/ImageInclusion.js +++ b/packages/react-scripts/fixtures/kitchensink/src/features/webpack/ImageInclusion.js @@ -10,6 +10,5 @@ import React from 'react'; import tiniestCat from './assets/tiniest-cat.jpg'; -export default () => ( - <img id="feature-image-inclusion" src={tiniestCat} alt="tiniest cat" /> -); +export default () => + <img id="feature-image-inclusion" src={tiniestCat} alt="tiniest cat" />; diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/webpack/JsonInclusion.js b/packages/react-scripts/fixtures/kitchensink/src/features/webpack/JsonInclusion.js index e81f7639736..66425c66c74 100644 --- a/packages/react-scripts/fixtures/kitchensink/src/features/webpack/JsonInclusion.js +++ b/packages/react-scripts/fixtures/kitchensink/src/features/webpack/JsonInclusion.js @@ -10,4 +10,7 @@ import React from 'react'; import { abstract } from './assets/abstract.json'; -export default () => <summary id="feature-json-inclusion">{abstract}</summary>; +export default () => + <summary id="feature-json-inclusion"> + {abstract} + </summary>; diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/webpack/LinkedModules.js b/packages/react-scripts/fixtures/kitchensink/src/features/webpack/LinkedModules.js index de8a5e4ab5b..395ebd7ed50 100644 --- a/packages/react-scripts/fixtures/kitchensink/src/features/webpack/LinkedModules.js +++ b/packages/react-scripts/fixtures/kitchensink/src/features/webpack/LinkedModules.js @@ -16,5 +16,9 @@ export default () => { if (!test() || v !== '2.0.0') { throw new Error('Functionality test did not pass.'); } - return <p id="feature-linked-modules">{v}</p>; + return ( + <p id="feature-linked-modules"> + {v} + </p> + ); }; diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/webpack/NoExtInclusion.js b/packages/react-scripts/fixtures/kitchensink/src/features/webpack/NoExtInclusion.js index 7f824c2f292..086885db2d4 100644 --- a/packages/react-scripts/fixtures/kitchensink/src/features/webpack/NoExtInclusion.js +++ b/packages/react-scripts/fixtures/kitchensink/src/features/webpack/NoExtInclusion.js @@ -14,6 +14,7 @@ const text = aFileWithoutExt.includes('base64') ? atob(aFileWithoutExt.split('base64,')[1]).trim() : aFileWithoutExt; -export default () => ( - <a id="feature-no-ext-inclusion" href={text}>aFileWithoutExt</a> -); +export default () => + <a id="feature-no-ext-inclusion" href={text}> + aFileWithoutExt + </a>; diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/webpack/UnknownExtInclusion.js b/packages/react-scripts/fixtures/kitchensink/src/features/webpack/UnknownExtInclusion.js index 70b046e9532..c41a1e0c024 100644 --- a/packages/react-scripts/fixtures/kitchensink/src/features/webpack/UnknownExtInclusion.js +++ b/packages/react-scripts/fixtures/kitchensink/src/features/webpack/UnknownExtInclusion.js @@ -14,6 +14,7 @@ const text = aFileWithExtUnknown.includes('base64') ? atob(aFileWithExtUnknown.split('base64,')[1]).trim() : aFileWithExtUnknown; -export default () => ( - <a id="feature-unknown-ext-inclusion" href={text}>aFileWithExtUnknown</a> -); +export default () => + <a id="feature-unknown-ext-inclusion" href={text}> + aFileWithExtUnknown + </a>; diff --git a/packages/react-scripts/package.json b/packages/react-scripts/package.json index 17d9554dc0b..e8dbeaf29cd 100644 --- a/packages/react-scripts/package.json +++ b/packages/react-scripts/package.json @@ -1,6 +1,6 @@ { "name": "@absolvent/react-scripts", - "version": "1.0.7", + "version": "1.0.8", "description": "Configuration and scripts for Create React App.", "repository": "facebookincubator/create-react-app", "license": "BSD-3-Clause", @@ -21,44 +21,44 @@ "react-scripts": "./bin/react-scripts.js" }, "dependencies": { - "autoprefixer": "7.1.0", - "babel-core": "6.24.1", + "autoprefixer": "7.1.1", + "babel-core": "6.25.0", "babel-eslint": "7.2.3", "babel-jest": "20.0.3", "babel-loader": "7.0.0", "babel-plugin-transform-decorators-legacy": "^1.3.4", - "babel-preset-react-app": "^3.0.0", + "babel-preset-react-app": "^3.0.1", "babel-runtime": "6.23.0", - "case-sensitive-paths-webpack-plugin": "1.1.4", + "case-sensitive-paths-webpack-plugin": "2.1.1", "chalk": "1.1.3", - "css-loader": "0.28.1", + "css-loader": "0.28.4", "dotenv": "4.0.0", "eslint": "3.19.0", "eslint-config-airbnb": "^15.0.1", "eslint-config-prettier": "^2.2.0", "eslint-loader": "1.7.1", - "eslint-plugin-flowtype": "2.33.0", + "eslint-plugin-flowtype": "2.34.0", "eslint-plugin-import": "2.2.0", "eslint-plugin-jsx-a11y": "5.0.3", - "eslint-plugin-react": "7.0.1", - "extract-text-webpack-plugin": "2.1.0", - "file-loader": "0.11.1", + "eslint-plugin-react": "7.1.0", + "extract-text-webpack-plugin": "2.1.2", + "file-loader": "0.11.2", "fs-extra": "3.0.1", - "html-webpack-plugin": "2.28.0", - "jest": "20.0.3", + "html-webpack-plugin": "2.29.0", + "jest": "20.0.4", "object-assign": "4.1.1", "postcss-flexbugs-fixes": "3.0.0", - "postcss-loader": "2.0.5", + "postcss-loader": "2.0.6", "promise": "7.1.1", - "react-dev-utils": "^3.0.0", + "react-dev-utils": "^3.0.1", "@absolvent/react-dev-utils": "^3.0.0", - "react-error-overlay": "^1.0.7", + "react-error-overlay": "^1.0.8", "react-hot-loader": "^3.0.0-beta.7", - "style-loader": "0.17.0", - "sw-precache-webpack-plugin": "0.9.1", - "url-loader": "0.5.8", + "style-loader": "0.18.2", + "sw-precache-webpack-plugin": "0.11.3", + "url-loader": "0.5.9", "webpack": "2.6.1", - "webpack-dev-server": "2.4.5", + "webpack-dev-server": "2.5.0", "webpack-manifest-plugin": "1.1.0", "whatwg-fetch": "2.0.3" }, diff --git a/packages/react-scripts/scripts/build.js b/packages/react-scripts/scripts/build.js index 2e4bc21ee5b..b9b65f5313d 100644 --- a/packages/react-scripts/scripts/build.js +++ b/packages/react-scripts/scripts/build.js @@ -35,10 +35,15 @@ const formatWebpackMessages = require('react-dev-utils/formatWebpackMessages'); const printHostingInstructions = require('react-dev-utils/printHostingInstructions'); const FileSizeReporter = require('react-dev-utils/FileSizeReporter'); -const measureFileSizesBeforeBuild = FileSizeReporter.measureFileSizesBeforeBuild; +const measureFileSizesBeforeBuild = + FileSizeReporter.measureFileSizesBeforeBuild; const printFileSizesAfterBuild = FileSizeReporter.printFileSizesAfterBuild; const useYarn = fs.existsSync(paths.yarnLockFile); +// These sizes are pretty large. We'll warn for bundles exceeding them. +const WARN_AFTER_BUNDLE_GZIP_SIZE = 512 * 1024; +const WARN_AFTER_CHUNK_GZIP_SIZE = 1024 * 1024; + // Warn and crash if required files are missing if (!checkRequiredFiles([paths.appHtml, paths.appIndexJs])) { process.exit(1); @@ -76,7 +81,13 @@ measureFileSizesBeforeBuild(paths.appBuild) } console.log('File sizes after gzip:\n'); - printFileSizesAfterBuild(stats, previousFileSizes, paths.appBuild); + printFileSizesAfterBuild( + stats, + previousFileSizes, + paths.appBuild, + WARN_AFTER_BUNDLE_GZIP_SIZE, + WARN_AFTER_CHUNK_GZIP_SIZE + ); console.log(); const appPackage = require(paths.appPackageJson); diff --git a/packages/react-scripts/scripts/eject.js b/packages/react-scripts/scripts/eject.js index c771e075d35..3d8d258cc67 100644 --- a/packages/react-scripts/scripts/eject.js +++ b/packages/react-scripts/scripts/eject.js @@ -85,19 +85,16 @@ inquirer const folders = ['config', 'config/jest', 'scripts']; // Make shallow array of files paths - const files = folders.reduce( - (files, folder) => { - return files.concat( - fs - .readdirSync(path.join(ownPath, folder)) - // set full path - .map(file => path.join(ownPath, folder, file)) - // omit dirs from file list - .filter(file => fs.lstatSync(file).isFile()) - ); - }, - [] - ); + const files = folders.reduce((files, folder) => { + return files.concat( + fs + .readdirSync(path.join(ownPath, folder)) + // set full path + .map(file => path.join(ownPath, folder, file)) + // omit dirs from file list + .filter(file => fs.lstatSync(file).isFile()) + ); + }, []); // Ensure that the app folder is clean and we won't override any files folders.forEach(verifyAbsent); @@ -124,18 +121,19 @@ inquirer if (content.match(/\/\/ @remove-file-on-eject/)) { return; } - content = content - // Remove dead code from .js files on eject - .replace( - /\/\/ @remove-on-eject-begin([\s\S]*?)\/\/ @remove-on-eject-end/mg, - '' - ) - // Remove dead code from .applescript files on eject - .replace( - /-- @remove-on-eject-begin([\s\S]*?)-- @remove-on-eject-end/mg, - '' - ) - .trim() + '\n'; + content = + content + // Remove dead code from .js files on eject + .replace( + /\/\/ @remove-on-eject-begin([\s\S]*?)\/\/ @remove-on-eject-end/gm, + '' + ) + // Remove dead code from .applescript files on eject + .replace( + /-- @remove-on-eject-begin([\s\S]*?)-- @remove-on-eject-end/gm, + '' + ) + .trim() + '\n'; console.log(` Adding ${cyan(file.replace(ownPath, ''))} to the project`); fs.writeFileSync(file.replace(ownPath, appPath), content); }); @@ -146,35 +144,50 @@ inquirer console.log(cyan('Updating the dependencies')); const ownPackageName = ownPackage.name; - if (appPackage.devDependencies[ownPackageName]) { - console.log(` Removing ${cyan(ownPackageName)} from devDependencies`); - delete appPackage.devDependencies[ownPackageName]; + if (appPackage.devDependencies) { + // We used to put react-scripts in devDependencies + if (appPackage.devDependencies[ownPackageName]) { + console.log(` Removing ${cyan(ownPackageName)} from devDependencies`); + delete appPackage.devDependencies[ownPackageName]; + } } + appPackage.dependencies = appPackage.dependencies || {}; if (appPackage.dependencies[ownPackageName]) { console.log(` Removing ${cyan(ownPackageName)} from dependencies`); delete appPackage.dependencies[ownPackageName]; } - Object.keys(ownPackage.dependencies).forEach(key => { // For some reason optionalDependencies end up in dependencies after install if (ownPackage.optionalDependencies[key]) { return; } - console.log(` Adding ${cyan(key)} to devDependencies`); - appPackage.devDependencies[key] = ownPackage.dependencies[key]; + console.log(` Adding ${cyan(key)} to dependencies`); + appPackage.dependencies[key] = ownPackage.dependencies[key]; + }); + // Sort the deps + const unsortedDependencies = appPackage.dependencies; + appPackage.dependencies = {}; + Object.keys(unsortedDependencies).sort().forEach(key => { + appPackage.dependencies[key] = unsortedDependencies[key]; }); console.log(); + console.log(cyan('Updating the scripts')); delete appPackage.scripts['eject']; Object.keys(appPackage.scripts).forEach(key => { Object.keys(ownPackage.bin).forEach(binKey => { const regex = new RegExp(binKey + ' (\\w+)', 'g'); + if (!regex.test(appPackage.scripts[key])) { + return; + } appPackage.scripts[key] = appPackage.scripts[key].replace( regex, 'node scripts/$1.js' ); console.log( - ` Replacing ${cyan(`"${binKey} ${key}"`)} with ${cyan(`"node scripts/${key}.js"`)}` + ` Replacing ${cyan(`"${binKey} ${key}"`)} with ${cyan( + `"node scripts/${key}.js"` + )}` ); }); }); @@ -217,11 +230,26 @@ inquirer } if (fs.existsSync(paths.yarnLockFile)) { - console.log(cyan('Running yarn...')); - spawnSync('yarnpkg', [], { stdio: 'inherit' }); + // TODO: this is disabled for three reasons. + // + // 1. It produces garbage warnings on Windows on some systems: + // https://github.com/facebookincubator/create-react-app/issues/2030 + // + // 2. For the above reason, it breaks Windows CI: + // https://github.com/facebookincubator/create-react-app/issues/2624 + // + // 3. It is wrong anyway: re-running yarn will respect the lockfile + // rather than package.json we just updated. Instead we should have + // updated the lockfile. So we might as well not do it while it's broken. + // https://github.com/facebookincubator/create-react-app/issues/2627 + // + // console.log(cyan('Running yarn...')); + // spawnSync('yarnpkg', [], { stdio: 'inherit' }); } else { console.log(cyan('Running npm install...')); - spawnSync('npm', ['install'], { stdio: 'inherit' }); + spawnSync('npm', ['install', '--loglevel', 'error'], { + stdio: 'inherit', + }); } console.log(green('Ejected successfully!')); console.log(); diff --git a/packages/react-scripts/scripts/init.js b/packages/react-scripts/scripts/init.js index a50704d1cf7..0a62cc4a976 100644 --- a/packages/react-scripts/scripts/init.js +++ b/packages/react-scripts/scripts/init.js @@ -28,18 +28,14 @@ module.exports = function( originalDirectory, template ) { - const ownPackageName = require(path.join( - __dirname, - '..', - 'package.json' - )).name; + const ownPackageName = require(path.join(__dirname, '..', 'package.json')) + .name; const ownPath = path.join(appPath, 'node_modules', ownPackageName); const appPackage = require(path.join(appPath, 'package.json')); const useYarn = fs.existsSync(path.join(appPath, 'yarn.lock')); // Copy over some of the devDependencies appPackage.dependencies = appPackage.dependencies || {}; - appPackage.devDependencies = appPackage.devDependencies || {}; // Setup the script rules appPackage.scripts = { @@ -193,6 +189,8 @@ module.exports = function( function isReactInstalled(appPackage) { const dependencies = appPackage.dependencies || {}; - return typeof dependencies.react !== 'undefined' && - typeof dependencies['react-dom'] !== 'undefined'; + return ( + typeof dependencies.react !== 'undefined' && + typeof dependencies['react-dom'] !== 'undefined' + ); } diff --git a/packages/react-scripts/scripts/utils/createJestConfig.js b/packages/react-scripts/scripts/utils/createJestConfig.js index 13ae8b30411..de74958efad 100644 --- a/packages/react-scripts/scripts/utils/createJestConfig.js +++ b/packages/react-scripts/scripts/utils/createJestConfig.js @@ -43,6 +43,7 @@ module.exports = (resolve, rootDir, isEjecting) => { moduleNameMapper: { '^react-native$': 'react-native-web', }, + moduleFileExtensions: ['web.js', 'js', 'json', 'web.jsx', 'jsx'], }; if (rootDir) { config.rootDir = rootDir; diff --git a/packages/react-scripts/template/README.md b/packages/react-scripts/template/README.md index c4591c18326..5775028406e 100644 --- a/packages/react-scripts/template/README.md +++ b/packages/react-scripts/template/README.md @@ -17,6 +17,7 @@ You can find the most recent version of this guide [here](https://github.com/fac - [Syntax Highlighting in the Editor](#syntax-highlighting-in-the-editor) - [Displaying Lint Output in the Editor](#displaying-lint-output-in-the-editor) - [Debugging in the Editor](#debugging-in-the-editor) +- [Formatting Code Automatically](#formatting-code-automatically) - [Changing the Page `<title>`](#changing-the-page-title) - [Installing a Dependency](#installing-a-dependency) - [Importing a Component](#importing-a-component) @@ -44,6 +45,7 @@ You can find the most recent version of this guide [here](https://github.com/fac - [Proxying API Requests in Development](#proxying-api-requests-in-development) - ["Invalid Host Header" Errors After Configuring Proxy](#invalid-host-header-errors-after-configuring-proxy) - [Configuring the Proxy Manually](#configuring-the-proxy-manually) + - [Configuring a WebSocket Proxy](#configuring-a-websocket-proxy) - [Using HTTPS in Development](#using-https-in-development) - [Generating Dynamic `<meta>` Tags on the Server](#generating-dynamic-meta-tags-on-the-server) - [Pre-Rendering into Static HTML Files](#pre-rendering-into-static-html-files) @@ -62,9 +64,13 @@ You can find the most recent version of this guide [here](https://github.com/fac - [Disabling jsdom](#disabling-jsdom) - [Snapshot Testing](#snapshot-testing) - [Editor Integration](#editor-integration) +- [Developing Components in Isolation](#developing-components-in-isolation) + - [Getting Started with Storybook](#getting-started-with-storybook) + - [Getting Started with Styleguidist](#getting-started-with-styleguidist) - [Making a Progressive Web App](#making-a-progressive-web-app) - [Offline-First Considerations](#offline-first-considerations) - [Progressive Web App Metadata](#progressive-web-app-metadata) +- [Analyzing the Bundle Size](#analyzing-the-bundle-size) - [Deployment](#deployment) - [Static Server](#static-server) - [Other Solutions](#other-solutions) @@ -83,7 +89,7 @@ You can find the most recent version of this guide [here](https://github.com/fac - [Troubleshooting](#troubleshooting) - [`npm start` doesn’t detect changes](#npm-start-doesnt-detect-changes) - [`npm test` hangs on macOS Sierra](#npm-test-hangs-on-macos-sierra) - - [`npm run build` silently fails](#npm-run-build-silently-fails) + - [`npm run build` exits too early](#npm-run-build-exits-too-early) - [`npm run build` fails on Heroku](#npm-run-build-fails-on-heroku) - [Moment.js locales are missing](#momentjs-locales-are-missing) - [Something Missing?](#something-missing) @@ -138,7 +144,7 @@ For the project to build, **these files must exist with exact filenames**: You can delete or rename the other files. You may create subdirectories inside `src`. For faster rebuilds, only files inside `src` are processed by Webpack.<br> -You need to **put any JS and CSS files inside `src`**, or Webpack won’t see them. +You need to **put any JS and CSS files inside `src`**, otherwise Webpack won’t see them. Only files inside `public` can be used from `public/index.html`.<br> Read instructions below for using assets from JavaScript and HTML. @@ -263,6 +269,56 @@ Then add the block below to your `launch.json` file and put it inside the `.vsco Start your app by running `npm start`, and start debugging in VS Code by pressing `F5` or by clicking the green debug icon. You can now write code, set breakpoints, make changes to the code, and debug your newly modified code—all from your editor. +## Formatting Code Automatically + +Prettier is an opinionated code formatter with support for JavaScript, CSS and JSON. With Prettier you can format the code you write automatically to ensure a code style within your project. See the [Prettier's GitHub page](https://github.com/prettier/prettier) for more information, and look at this [page to see it in action](https://prettier.github.io/prettier/). + +To format our code whenever we make a commit in git, we need to install the following dependencies: + +```sh +npm install --save husky lint-staged prettier +``` + +Alternatively you may use `yarn`: + +```sh +yarn add husky lint-staged prettier +``` + +* `husky` makes it easy to use githooks as if they are npm scripts. +* `lint-staged` allows us to run scripts on staged files in git. See this [blog post about lint-staged to learn more about it](https://medium.com/@okonetchnikov/make-linting-great-again-f3890e1ad6b8). +* `prettier` is the JavaScript formatter we will run before commits. + +Now we can make sure every file is formatted correctly by adding a few lines to the `package.json` in the project root. + +Add the following line to `scripts` section: + +```diff + "scripts": { ++ "precommit": "lint-staged", + "start": "react-scripts start", + "build": "react-scripts build", +``` + +Next we add a 'lint-staged' field to the `package.json`, for example: + +```diff + "dependencies": { + // ... + }, ++ "lint-staged": { ++ "src/**/*.{js,jsx,json,css}": [ ++ "prettier --single-quote --write", ++ "git add" ++ ] ++ }, + "scripts": { +``` + +Now, whenever you make a commit, Prettier will format the changed files automatically. You can also run `./node_modules/.bin/prettier --single-quote --write "src/**/*.{js,jsx}"` to format your entire project for the first time. + +Next you might want to integrate Prettier in your favorite editor. Read the section on [Editor Integration](https://github.com/prettier/prettier#editor-integration) on the Prettier GitHub page. + ## Changing the Page `<title>` You can find the source HTML file in the `public` folder of the generated project. You may edit the `<title>` tag in it to change the title from “React App” to anything else. @@ -277,10 +333,18 @@ If you use a custom server for your app in production and want to modify the tit The generated project includes React and ReactDOM as dependencies. It also includes a set of scripts used by Create React App as a development dependency. You may install other dependencies (for example, React Router) with `npm`: +```sh +npm install --save react-router ``` -npm install --save <library-name> + +Alternatively you may use `yarn`: + +```sh +yarn add react-router ``` +This works for any library, not just `react-router`. + ## Importing a Component This project setup supports ES6 modules thanks to Babel.<br> @@ -377,6 +441,10 @@ This will make `moduleA.js` and all its unique dependencies as a separate chunk You can also use it with `async` / `await` syntax if you prefer it. +### With React Router + +If you are using React Router check out [this tutorial](http://serverless-stack.com/chapters/code-splitting-in-create-react-app.html) on how to use code splitting with it. You can find the companion GitHub repository [here](https://github.com/AnomalyInnovations/serverless-stack-demo-client/tree/code-splitting-in-create-react-app). + ## Adding a Stylesheet This project setup uses [Webpack](https://webpack.js.org/) for handling all assets. Webpack offers a custom way of “extending” the concept of `import` beyond JavaScript. To express that a JavaScript file depends on a CSS file, you need to **import the CSS from the JavaScript file**: @@ -450,9 +518,16 @@ Following this rule often makes CSS preprocessors less useful, as features like First, let’s install the command-line interface for Sass: +```sh +npm install --save node-sass-chokidar ``` -npm install node-sass-chokidar --save-dev + +Alternatively you may use `yarn`: + +```sh +yarn add node-sass-chokidar ``` + Then in `package.json`, add the following lines to `scripts`: ```diff @@ -488,8 +563,14 @@ At this point you might want to remove all CSS files from the source control, an As a final step, you may find it convenient to run `watch-css` automatically with `npm start`, and run `build-css` as a part of `npm run build`. You can use the `&&` operator to execute two scripts sequentially. However, there is no cross-platform way to run two scripts in parallel, so we will install a package for this: +```sh +npm install --save npm-run-all ``` -npm install --save-dev npm-run-all + +Alternatively you may use `yarn`: + +```sh +yarn add npm-run-all ``` Then we can change `start` and `build` scripts to include the CSS preprocessor commands: @@ -647,9 +728,14 @@ You don’t have to use [React Bootstrap](https://react-bootstrap.github.io) tog Install React Bootstrap and Bootstrap from npm. React Bootstrap does not include Bootstrap CSS so this needs to be installed as well: +```sh +npm install --save react-bootstrap bootstrap@3 ``` -npm install react-bootstrap --save -npm install bootstrap@3 --save + +Alternatively you may use `yarn`: + +```sh +yarn add react-bootstrap bootstrap@3 ``` Import Bootstrap CSS and optionally Bootstrap theme CSS in the beginning of your ```src/index.js``` file: @@ -688,7 +774,7 @@ Recent versions of [Flow](http://flowtype.org/) work with Create React App proje To add Flow to a Create React App project, follow these steps: -1. Run `npm install --save-dev flow-bin` (or `yarn add --dev flow-bin`). +1. Run `npm install --save flow-bin` (or `yarn add flow-bin`). 2. Add `"flow": "flow"` to the `scripts` section of your `package.json`. 3. Run `npm run flow init` (or `yarn flow init`) to create a [`.flowconfig` file](https://flowtype.org/docs/advanced-configuration.html) in the root directory. 4. Add `// @flow` to any files you want to type check (for example, to `src/App.js`). @@ -982,6 +1068,36 @@ You may also narrow down matches using `*` and/or `**`, to match the path exactl } ``` +### Configuring a WebSocket Proxy + +When setting up a WebSocket proxy, there are a some extra considerations to be aware of. + +If you’re using a WebSocket engine like [Socket.io](https://socket.io/), you must have a Socket.io server running that you can use as the proxy target. Socket.io will not work with a standard WebSocket server. Specifically, don't expect Socket.io to work with [the websocket.org echo test](http://websocket.org/echo.html). + +There’s some good documentation available for [setting up a Socket.io server](https://socket.io/docs/). + +Standard WebSockets **will** work with a standard WebSocket server as well as the websocket.org echo test. You can use libraries like [ws](https://github.com/websockets/ws) for the server, with [native WebSockets in the browser](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket). + +Either way, you can proxy WebSocket requests manually in `package.json`: + +```js +{ + // ... + "proxy": { + "/socket": { + // Your compatible WebSocket server + "target": "ws://<socket_url>", + // Tell http-proxy-middleware that this is a WebSocket proxy. + // Also allows you to proxy WebSocket requests without an additional HTTP request + // https://github.com/chimurai/http-proxy-middleware#external-websocket-upgrade + "ws": true + // ... + } + } + // ... +} +``` + ## Using HTTPS in Development >Note: this feature is available with `react-scripts@0.4.0` and higher. @@ -1127,12 +1243,20 @@ This test mounts a component and makes sure that it didn’t throw during render When you encounter bugs caused by changing components, you will gain a deeper insight into which parts of them are worth testing in your application. This might be a good time to introduce more specific tests asserting specific expected output or behavior. -If you’d like to test components in isolation from the child components they render, we recommend using [`shallow()` rendering API](http://airbnb.io/enzyme/docs/api/shallow.html) from [Enzyme](http://airbnb.io/enzyme/). You can write a smoke test with it too: +If you’d like to test components in isolation from the child components they render, we recommend using [`shallow()` rendering API](http://airbnb.io/enzyme/docs/api/shallow.html) from [Enzyme](http://airbnb.io/enzyme/). To install it, run: + +```sh +npm install --save enzyme react-test-renderer +``` + +Alternatively you may use `yarn`: ```sh -npm install --save-dev enzyme react-test-renderer +yarn add enzyme react-test-renderer ``` +You can write a smoke test with it too: + ```js import React from 'react'; import { shallow } from 'enzyme'; @@ -1171,18 +1295,24 @@ Additionally, you might find [jest-enzyme](https://github.com/blainekasten/enzym expect(wrapper).toContainReact(welcome) ``` -To setup jest-enzyme with Create React App, follow the instructions for [initializing your test environment](#initializing-test-environment) to import `jest-enzyme`. +To enable this, install `jest-enzyme`: ```sh -npm install --save-dev jest-enzyme +npm install --save jest-enzyme ``` +Alternatively you may use `yarn`: + +```sh +yarn add jest-enzyme +``` + +Import it in [`src/setupTests.js`](#initializing-test-environment) to make its matchers available in every test: + ```js -// src/setupTests.js import 'jest-enzyme'; ``` - ### Using Third Party Assertion Libraries We recommend that you use `expect()` for assertions and `jest.fn()` for spies. If you are having issues with them please [file those against Jest](https://github.com/facebook/jest/issues/new), and we’ll fix them. We intend to keep making them better for React, supporting, for example, [pretty-printing React elements as JSX](https://github.com/facebook/jest/pull/1566). @@ -1255,6 +1385,10 @@ script: 1. Trigger your first build with a git push. 1. [Customize your Travis CI Build](https://docs.travis-ci.com/user/customizing-the-build/) if needed. +#### CircleCI + +Follow [this article](https://medium.com/@knowbody/circleci-and-zeits-now-sh-c9b7eebcd3c1) to set up CircleCI with a Create React App project. + ### On your own environment ##### Windows (cmd.exe) @@ -1289,14 +1423,22 @@ The build command will check for linter warnings and fail if any are found. By default, the `package.json` of the generated project looks like this: ```js - // ... "scripts": { - // ... + "start": "react-scripts start", + "build": "react-scripts build", "test": "react-scripts test --env=jsdom" - } ``` -If you know that none of your tests depend on [jsdom](https://github.com/tmpvar/jsdom), you can safely remove `--env=jsdom`, and your tests will run faster.<br> +If you know that none of your tests depend on [jsdom](https://github.com/tmpvar/jsdom), you can safely remove `--env=jsdom`, and your tests will run faster: + +```diff + "scripts": { + "start": "react-scripts start", + "build": "react-scripts build", +- "test": "react-scripts test --env=jsdom" ++ "test": "react-scripts test" +``` + To help you make up your mind, here is a list of APIs that **need jsdom**: * Any browser globals like `window` and `document` @@ -1332,14 +1474,15 @@ For an example, a simple button component could have following states: Usually, it’s hard to see these states without running a sample app or some examples. -Create React App doesn’t include any tools for this by default, but you can easily add [Storybook for React](https://storybook.js.org) ([source](https://github.com/storybooks/storybook)) to your project. **It is a third-party tool that lets you develop components and see all their states in isolation from your app**. +Create React App doesn’t include any tools for this by default, but you can easily add [Storybook for React](https://storybook.js.org) ([source](https://github.com/storybooks/storybook)) or [React Styleguidist](https://react-styleguidist.js.org/) ([source](https://github.com/styleguidist/react-styleguidist)) to your project. **These are third-party tools that let you develop components and see all their states in isolation from your app**. ![Storybook for React Demo](http://i.imgur.com/7CIAWpB.gif) -A storybook can also be deployed as a static app. -This way, everyone in your team can view and review different states of UI components without starting a backend server or creating an account in your app. +You can also deploy your Storybook or style guide as a static app. This way, everyone in your team can view and review different states of UI components without starting a backend server or creating an account in your app. + +### Getting Started with Storybook -### Setup your app with Storybook +Storybook is a development environment for React UI components. It allows you to browse a component library, view the different states of each component, and interactively develop and test components. First, install the following npm package globally: @@ -1362,6 +1505,44 @@ Learn more about React Storybook: * [Documentation](https://storybook.js.org/basics/introduction/) * [Snapshot Testing UI](https://github.com/storybooks/storybook/tree/master/addons/storyshots) with Storybook + addon/storyshot +### Getting Started with Styleguidist + +Styleguidist combines a style guide, where all your components are presented on a single page with their props documentation and usage examples, with an environment for developing components in isolation, similar to Storybook. In Styleguidist you write examples in Markdown, where each code snippet is rendered as a live editable playground. + +First, install Styleguidist: + +```sh +npm install --save react-styleguidist +``` + +Alternatively you may use `yarn`: + +```sh +yarn add react-styleguidist +``` + +Then, add these scripts to your `package.json`: + +```diff + "scripts": { ++ "styleguide": "styleguidist server", ++ "styleguide:build": "styleguidist build", + "start": "react-scripts start", +``` + +Then, run the following command inside your app’s directory: + +```sh +npm run styleguide +``` + +After that, follow the instructions on the screen. + +Learn more about React Styleguidist: + +* [GitHub Repo](https://github.com/styleguidist/react-styleguidist) +* [Documentation](https://react-styleguidist.js.org/docs/getting-started.html) + ## Making a Progressive Web App By default, the production build is a fully functional, offline-first @@ -1462,6 +1643,52 @@ icons, names, and branding colors to use when the web app is displayed. provides more context about what each field means, and how your customizations will affect your users' experience. +## Analyzing the Bundle Size + +[Source map explorer](https://www.npmjs.com/package/source-map-explorer) analyzes +JavaScript bundles using the source maps. This helps you understand where code +bloat is coming from. + +To add Source map explorer to a Create React App project, follow these steps: + +```sh +npm install --save source-map-explorer +``` + +Alternatively you may use `yarn`: + +```sh +yarn add source-map-explorer +``` + +Then in `package.json`, add the following line to `scripts`: + +```diff + "scripts": { ++ "analyze": "source-map-explorer build/static/js/main.*", + "start": "react-scripts start", + "build": "react-scripts build", + "test": "react-scripts test --env=jsdom", +``` + +>**Note:** +> +>This doesn't quite work on Windows because it doesn't automatically expand `*` in the filepath. For now, the workaround is to look at the full hashed filename in `build/static/js` (e.g. `main.89b7e95a.js`) and copy it into `package.json` when you're running the analyzer. For example: +> +>```diff +>+ "analyze": "source-map-explorer build/static/js/main.89b7e95a.js", +>``` +> +>Unfortunately it will be different after every build. You can express support for fixing this on Windows [in this issue](https://github.com/danvk/source-map-explorer/issues/52). + +Then to analyze the bundle run the production build then run the analyze +script. + +``` +npm run build +npm run analyze +``` + ## Deployment `npm run build` creates a `build` directory with a production build of your app. Set up your favourite HTTP server so that a visitor to your site is served `index.html`, and requests to static paths like `/static/js/main.<hash>.js` are served with the contents of the `/static/js/main.<hash>.js` file. @@ -1524,7 +1751,7 @@ This is because when there is a fresh page load for a `/todos/42`, the server lo }); ``` -If you’re using [Apache](https://httpd.apache.org/), you need to create a `.htaccess` file in the `public` folder that looks like this: +If you’re using [Apache HTTP Server](https://httpd.apache.org/), you need to create a `.htaccess` file in the `public` folder that looks like this: ``` Options -MultiViews @@ -1533,7 +1760,9 @@ If you’re using [Apache](https://httpd.apache.org/), you need to create a `.ht RewriteRule ^ index.html [QSA,L] ``` -It will get copied to the `build` folder when you run `npm run build`. +It will get copied to the `build` folder when you run `npm run build`. + +If you’re using [Apache Tomcat](http://tomcat.apache.org/), you need to follow [this Stack Overflow answer](https://stackoverflow.com/a/41249464/4878474). Now requests to `/todos/42` will be handled correctly both in development and in production. @@ -1658,18 +1887,23 @@ Now, whenever you run `npm run build`, you will see a cheat sheet with instructi To publish it at [https://myusername.github.io/my-app](https://myusername.github.io/my-app), run: ```sh -npm install --save-dev gh-pages +npm install --save gh-pages +``` + +Alternatively you may use `yarn`: + +```sh +yarn add gh-pages ``` Add the following scripts in your `package.json`: -```js - // ... +```diff "scripts": { - // ... - "predeploy": "npm run build", - "deploy": "gh-pages -d build" - } ++ "predeploy": "npm run build", ++ "deploy": "gh-pages -d build", + "start": "react-scripts start", + "build": "react-scripts build", ``` The `predeploy` script will run automatically before `deploy` is run. @@ -1818,6 +2052,7 @@ PORT | :white_check_mark: | :x: | By default, the development web server will at HTTPS | :white_check_mark: | :x: | When set to `true`, Create React App will run the development server in `https` mode. PUBLIC_URL | :x: | :white_check_mark: | Create React App assumes your application is hosted at the serving web server's root or a subpath as specified in [`package.json` (`homepage`)](#building-for-relative-paths). Normally, Create React App ignores the hostname. You may use this variable to force assets to be referenced verbatim to the url you provide (hostname included). This may be particularly useful when using a CDN to host your application. CI | :large_orange_diamond: | :white_check_mark: | When set to `true`, Create React App treats warnings as failures in the build. It also makes the test runner non-watching. Most CIs set this flag by default. +REACT_EDITOR | :white_check_mark: | :x: | When an app crashes in development, you will see an error overlay with clickable stack trace. When you click on it, Create React App will try to determine the editor you are using based on currently running processes, and open the relevant source file. You can [send a pull request to detect your editor of choice](https://github.com/facebookincubator/create-react-app/issues/2636). Setting this environment variable overrides the automatic detection. If you do it, make sure your systems [PATH](https://en.wikipedia.org/wiki/PATH_(variable)) environment variable points to your editor’s bin folder. ## Troubleshooting @@ -1859,9 +2094,13 @@ If this still doesn’t help, try running `launchctl unload -F ~/Library/LaunchA There are also reports that *uninstalling* Watchman fixes the issue. So if nothing else helps, remove it from your system and try again. -### `npm run build` silently fails +### `npm run build` exits too early + +It is reported that `npm run build` can fail on machines with limited memory and no swap space, which is common in cloud environments. Even with small projects this command can increase RAM usage in your system by hundreds of megabytes, so if you have less than 1 GB of available memory your build is likely to fail with the following message: + +> The build failed because the process exited too early. This probably means the system ran out of memory or someone called `kill -9` on the process. -It is reported that `npm run build` can fail on machines with no swap space, which is common in cloud environments. If [the symptoms are matching](https://github.com/facebookincubator/create-react-app/issues/1133#issuecomment-264612171), consider adding some swap space to the machine you’re building on, or build the project locally. +If you are completely sure that you didn't terminate the process, consider [adding some swap space](https://www.digitalocean.com/community/tutorials/how-to-add-swap-on-ubuntu-14-04) to the machine you’re building on, or build the project locally. ### `npm run build` fails on Heroku diff --git a/packages/react-scripts/template/src/registerServiceWorker.js b/packages/react-scripts/template/src/registerServiceWorker.js index 9966897dc28..4a3ccf02124 100644 --- a/packages/react-scripts/template/src/registerServiceWorker.js +++ b/packages/react-scripts/template/src/registerServiceWorker.js @@ -8,40 +8,97 @@ // To learn more about the benefits of this model, read https://goo.gl/KwvDNy. // This link also includes instructions on opting out of this behavior. +const isLocalhost = Boolean( + window.location.hostname === 'localhost' || + // [::1] is the IPv6 localhost address. + window.location.hostname === '[::1]' || + // 127.0.0.1/8 is considered localhost for IPv4. + window.location.hostname.match( + /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ + ) +); + export default function register() { if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { + // The URL constructor is available in all browsers that support SW. + const publicUrl = new URL(process.env.PUBLIC_URL, window.location); + if (publicUrl.origin !== window.location.origin) { + // Our service worker won't work if PUBLIC_URL is on a different origin + // from what our page is served on. This might happen if a CDN is used to + // serve assets; see https://github.com/facebookincubator/create-react-app/issues/2374 + return; + } + window.addEventListener('load', () => { const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; - navigator.serviceWorker - .register(swUrl) - .then(registration => { - registration.onupdatefound = () => { - const installingWorker = registration.installing; - installingWorker.onstatechange = () => { - if (installingWorker.state === 'installed') { - if (navigator.serviceWorker.controller) { - // At this point, the old content will have been purged and - // the fresh content will have been added to the cache. - // It's the perfect time to display a "New content is - // available; please refresh." message in your web app. - console.log('New content is available; please refresh.'); - } else { - // At this point, everything has been precached. - // It's the perfect time to display a - // "Content is cached for offline use." message. - console.log('Content is cached for offline use.'); - } - } - }; - }; - }) - .catch(error => { - console.error('Error during service worker registration:', error); - }); + + if (!isLocalhost) { + // Is not local host. Just register service worker + registerValidSW(swUrl); + } else { + // This is running on localhost. Lets check if a service worker still exists or not. + checkValidServiceWorker(swUrl); + } }); } } +function registerValidSW(swUrl) { + navigator.serviceWorker + .register(swUrl) + .then(registration => { + registration.onupdatefound = () => { + const installingWorker = registration.installing; + installingWorker.onstatechange = () => { + if (installingWorker.state === 'installed') { + if (navigator.serviceWorker.controller) { + // At this point, the old content will have been purged and + // the fresh content will have been added to the cache. + // It's the perfect time to display a "New content is + // available; please refresh." message in your web app. + console.log('New content is available; please refresh.'); + } else { + // At this point, everything has been precached. + // It's the perfect time to display a + // "Content is cached for offline use." message. + console.log('Content is cached for offline use.'); + } + } + }; + }; + }) + .catch(error => { + console.error('Error during service worker registration:', error); + }); +} + +function checkValidServiceWorker(swUrl) { + // Check if the service worker can be found. If it can't reload the page. + fetch(swUrl) + .then(response => { + // Ensure service worker exists, and that we really are getting a JS file. + if ( + response.status === 404 || + response.headers.get('content-type').indexOf('javascript') === -1 + ) { + // No service worker found. Probably a different app. Reload the page. + navigator.serviceWorker.ready.then(registration => { + registration.unregister().then(() => { + window.location.reload(); + }); + }); + } else { + // Service worker found. Proceed as normal. + registerValidSW(swUrl); + } + }) + .catch(() => { + console.log( + 'No internet connection found. App is running in offline mode.' + ); + }); +} + export function unregister() { if ('serviceWorker' in navigator) { navigator.serviceWorker.ready.then(registration => { diff --git a/tasks/e2e-installs.sh b/tasks/e2e-installs.sh index 8f4789ff0c7..58864263247 100755 --- a/tasks/e2e-installs.sh +++ b/tasks/e2e-installs.sh @@ -55,15 +55,6 @@ function checkDependencies { echo "There are extraneous dependencies in package.json" exit 1 fi - - - if ! awk '/"devDependencies": {/{y=1;next}/},/{y=0; next}y' package.json | \ - grep -v -q -E '^\s*"react(-dom|-scripts)?"'; then - echo "Dev Dependencies are correct" - else - echo "There are extraneous devDependencies in package.json" - exit 1 - fi } function create_react_app { @@ -89,7 +80,7 @@ then # AppVeyor uses an old version of yarn. # Once updated to 0.24.3 or above, the workaround can be removed # and replaced with `yarnpkg cache clean` - # Issues: + # Issues: # https://github.com/yarnpkg/yarn/issues/2591 # https://github.com/appveyor/ci/issues/1576 # https://github.com/facebookincubator/create-react-app/pull/2400 @@ -104,12 +95,16 @@ fi if hash npm 2>/dev/null then - npm cache clean + # npm 5 is too buggy right now + if [ $(npm -v | head -c 1) -eq 5 ]; then + npm i -g npm@^4.x + fi; + npm cache clean || npm cache verify fi -# Prevent lerna bootstrap, we only want top-level dependencies +# Prevent bootstrap, we only want top-level dependencies cp package.json package.json.bak -grep -v "lerna bootstrap" package.json > temp && mv temp package.json +grep -v "postinstall" package.json > temp && mv temp package.json npm install mv package.json.bak package.json @@ -121,7 +116,7 @@ then fi # We removed the postinstall, so do it manually -./node_modules/.bin/lerna bootstrap --concurrency=1 +node bootstrap.js cd packages/react-error-overlay/ npm run build:prod diff --git a/tasks/e2e-kitchensink.sh b/tasks/e2e-kitchensink.sh index fcd687e4b0d..ebbac271e6e 100755 --- a/tasks/e2e-kitchensink.sh +++ b/tasks/e2e-kitchensink.sh @@ -72,7 +72,7 @@ then # AppVeyor uses an old version of yarn. # Once updated to 0.24.3 or above, the workaround can be removed # and replaced with `yarnpkg cache clean` - # Issues: + # Issues: # https://github.com/yarnpkg/yarn/issues/2591 # https://github.com/appveyor/ci/issues/1576 # https://github.com/facebookincubator/create-react-app/pull/2400 @@ -87,12 +87,16 @@ fi if hash npm 2>/dev/null then - npm cache clean + # npm 5 is too buggy right now + if [ $(npm -v | head -c 1) -eq 5 ]; then + npm i -g npm@^4.x + fi; + npm cache clean || npm cache verify fi -# Prevent lerna bootstrap, we only want top-level dependencies +# Prevent bootstrap, we only want top-level dependencies cp package.json package.json.bak -grep -v "lerna bootstrap" package.json > temp && mv temp package.json +grep -v "postinstall" package.json > temp && mv temp package.json npm install mv package.json.bak package.json @@ -104,7 +108,7 @@ then fi # We removed the postinstall, so do it manually -./node_modules/.bin/lerna bootstrap --concurrency=1 +node bootstrap.js cd packages/react-error-overlay/ npm run build:prod diff --git a/tasks/e2e-simple.sh b/tasks/e2e-simple.sh index 163bec0818a..48d705eef4c 100755 --- a/tasks/e2e-simple.sh +++ b/tasks/e2e-simple.sh @@ -71,7 +71,7 @@ then # AppVeyor uses an old version of yarn. # Once updated to 0.24.3 or above, the workaround can be removed # and replaced with `yarnpkg cache clean` - # Issues: + # Issues: # https://github.com/yarnpkg/yarn/issues/2591 # https://github.com/appveyor/ci/issues/1576 # https://github.com/facebookincubator/create-react-app/pull/2400 @@ -86,12 +86,16 @@ fi if hash npm 2>/dev/null then - npm cache clean + # npm 5 is too buggy right now + if [ $(npm -v | head -c 1) -eq 5 ]; then + npm i -g npm@^4.x + fi; + npm cache clean || npm cache verify fi -# Prevent lerna bootstrap, we only want top-level dependencies +# Prevent bootstrap, we only want top-level dependencies cp package.json package.json.bak -grep -v "lerna bootstrap" package.json > temp && mv temp package.json +grep -v "postinstall" package.json > temp && mv temp package.json npm install mv package.json.bak package.json @@ -111,9 +115,6 @@ then [[ $err_output =~ You\ are\ running\ Node ]] && exit 0 || exit 1 fi -# We removed the postinstall, so do it manually here -./node_modules/.bin/lerna bootstrap --concurrency=1 - if [ "$USE_YARN" = "yes" ] then # Install Yarn so that the test can use it to install packages. @@ -121,6 +122,9 @@ then yarn cache clean fi +# We removed the postinstall, so do it manually here +node bootstrap.js + # Lint own code ./node_modules/.bin/eslint --max-warnings 0 packages/babel-preset-react-app/ ./node_modules/.bin/eslint --max-warnings 0 packages/create-react-app/ diff --git a/tasks/local-test.sh b/tasks/local-test.sh new file mode 100755 index 00000000000..42d98ffcdcf --- /dev/null +++ b/tasks/local-test.sh @@ -0,0 +1,113 @@ +#!/usr/bin/env bash + +function print_help { + echo "Usage: ${0} [OPTIONS]" + echo "" + echo "OPTIONS:" + echo " --node-version <version> the node version to use while testing [6]" + echo " --git-branch <branch> the git branch to checkout for testing [the current one]" + echo " --test-suite <suite> which test suite to use ('simple', installs', 'kitchensink', 'all') ['all']" + echo " --yarn if present, use yarn as the package manager" + echo " --interactive gain a bash shell after the test run" + echo " --help print this message and exit" + echo "" +} + +cd $(dirname $0) + +node_version=6 +current_git_branch=`git rev-parse --abbrev-ref HEAD` +git_branch=${current_git_branch} +use_yarn=no +test_suite=all +interactive=false + +while [ "$1" != "" ]; do + case $1 in + "--node-version") + shift + node_version=$1 + ;; + "--git-branch") + shift + git_branch=$1 + ;; + "--yarn") + use_yarn=yes + ;; + "--test-suite") + shift + test_suite=$1 + ;; + "--interactive") + interactive=true + ;; + "--help") + print_help + exit 0 + ;; + esac + shift +done + +test_command="./tasks/e2e-simple.sh && ./tasks/e2e-kitchensink.sh && ./tasks/e2e-installs.sh" +case ${test_suite} in + "all") + ;; + "simple") + test_command="./tasks/e2e-simple.sh" + ;; + "kitchensink") + test_command="./tasks/e2e-kitchensink.sh" + ;; + "installs") + test_command="./tasks/e2e-installs.sh" + ;; + *) + ;; +esac + +read -r -d '' apply_changes <<- CMD +cd /var/create-react-app +git config --global user.name "Create React App" +git config --global user.email "cra@email.com" +git stash save -u +git stash show -p > patch +git diff 4b825dc642cb6eb9a060e54bf8d69288fbee4904 stash^3 >> patch +git stash pop +cd - +mv /var/create-react-app/patch . +git apply patch +rm patch +CMD + +if [ ${git_branch} != ${current_git_branch} ]; then + apply_changes='' +fi + +read -r -d '' command <<- CMD +echo "prefix=~/.npm" > ~/.npmrc +mkdir ~/.npm +export PATH=\$PATH:~/.npm/bin +set -x +git clone /var/create-react-app create-react-app --branch ${git_branch} +cd create-react-app +${apply_changes} +node --version +npm --version +set +x +${test_command} && echo -e "\n\e[1;32m✔ Job passed\e[0m" || echo -e "\n\e[1;31m✘ Job failes\e[0m" +$([[ ${interactive} == 'true' ]] && echo 'bash') +CMD + +docker run \ + --env CI=true \ + --env NPM_CONFIG_QUIET=true \ + --env USE_YARN=${use_yarn} \ + --tty \ + --user node \ + --volume ${PWD}/..:/var/create-react-app \ + --workdir /home/node \ + $([[ ${interactive} == 'true' ]] && echo '--interactive') \ + node:${node_version} \ + bash -c "${command}"