diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 00000000000..4eaf46c6d7d --- /dev/null +++ b/.eslintignore @@ -0,0 +1,5 @@ +node_modules/ +build +my-app* +packages/react-scripts/template +packages/react-scripts/fixtures diff --git a/.eslintrc b/.eslintrc index 5e603ecd193..d4e6d47749e 100644 --- a/.eslintrc +++ b/.eslintrc @@ -1,3 +1,16 @@ { - "extends": "react-app" + "extends": "eslint:recommended", + "env": { + "browser": true, + "commonjs": true, + "node": true, + "es6": true + }, + "parserOptions": { + "ecmaVersion": 6 + }, + "rules": { + "no-console": "off", + "strict": ["error", "global"] + } } diff --git a/.gitignore b/.gitignore index 7444dad4ec2..79ce88915d7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ +.idea/ +.vscode/ node_modules/ build .DS_Store @@ -5,5 +7,7 @@ build my-app* template/src/__tests__/__snapshots__/ lerna-debug.log -npm-debug.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* /.changelog diff --git a/.travis.yml b/.travis.yml index 8f358246614..fa2c7d8b364 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,11 +3,13 @@ language: node_js node_js: - 4 - 6 + - 7 cache: directories: - node_modules - packages/create-react-app/node_modules - packages/react-scripts/node_modules +install: true script: - 'if [ $TEST_SUITE = "simple" ]; then tasks/e2e-simple.sh; fi' - 'if [ $TEST_SUITE = "installs" ]; then tasks/e2e-installs.sh; fi' diff --git a/CHANGELOG.md b/CHANGELOG.md index 113bf641366..b96fd76db7d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,507 @@ +## 0.9.5 (March 9, 2017) + +#### :bug: Bug Fix + +* `react-scripts` + + * [#1783](https://github.com/facebookincubator/create-react-app/pull/1783) **Work around Node 7.7.2 bug that crashes `npm start`.** ([@ryanwalters](https://github.com/ryanwalters)) + +#### :nail_care: Enhancement + +* `eslint-config-react-app` + + * [#1773](https://github.com/facebookincubator/create-react-app/pull/1773) Remove `guard-for-in` lint rule. ([@spicyj](https://github.com/spicyj)) + +* `react-scripts` + * [#1760](https://github.com/facebookincubator/create-react-app/pull/1760) Suggest `serve` for running in production. ([@leo](https://github.com/leo)) + * [#1747](https://github.com/facebookincubator/create-react-app/pull/1747) Display `yarn` instead of `yarnpkg` when creating a new app. ([@lpalmes](https://github.com/lpalmes)) + +#### :memo: Documentation + +* `react-scripts` + + * [#1756](https://github.com/facebookincubator/create-react-app/pull/1756) Add Yarn steps for adding Flow. ([@zertosh](https://github.com/zertosh)) + +#### :house: Internal + +* `babel-preset-react-app` + + * [#1742](https://github.com/facebookincubator/create-react-app/pull/1742) Switch to `babel-preset-env` to remove the deprecation warning. ([@Timer](https://github.com/Timer)) + +#### Committers: 6 +- Andres Suarez ([zertosh](https://github.com/zertosh)) +- Ben Alpert ([spicyj](https://github.com/spicyj)) +- Joe Haddad ([Timer](https://github.com/Timer)) +- Leo Lamprecht ([leo](https://github.com/leo)) +- Lorenzo Palmes ([lpalmes](https://github.com/lpalmes)) +- Ryan Walters ([ryanwalters](https://github.com/ryanwalters)) + +### Migrating from 0.9.4 to 0.9.5 + +Inside any created project that has not been ejected, run: + +``` +npm install --save-dev --save-exact react-scripts@0.9.5 +``` + +## 0.9.4 (March 6, 2017) + +#### :bug: Bug Fix +* `create-react-app` + + * [#1706](https://github.com/facebookincubator/create-react-app/pull/1706) Extract compressed package for package name. ([@Timer](https://github.com/Timer)) + + You may now specify a scoped package for `--scripts-version` and obtain a working installation. + + * [#1695](https://github.com/facebookincubator/create-react-app/pull/1695) Print why installation was aborted. ([@tgig](https://github.com/tgig)) + +* `react-scripts` + + * [#1727](https://github.com/facebookincubator/create-react-app/pull/1727) Fix ejecting from a scoped fork. ([@gaearon](https://github.com/gaearon)) + + Ejecting now works within a scoped fork. + + * [#1721](https://github.com/facebookincubator/create-react-app/pull/1721) Fix hot reloading for WebpackDevServer after eject. ([@gaearon](https://github.com/gaearon)) + +* `react-dev-utils` + + * [#1690](https://github.com/facebookincubator/create-react-app/pull/1690) Fix `openBrowser()` when `BROWSER=open` on macOS. ([@bpierre](https://github.com/bpierre)) + + * [#1696](https://github.com/facebookincubator/create-react-app/pull/1696) Improve reliability of port detection. ([@chrisdrackett](https://github.com/chrisdrackett)) + +#### :nail_care: Enhancement +* `eslint-config-react-app`, `react-scripts` + + * [#1705](https://github.com/facebookincubator/create-react-app/pull/1705) Add support for `ignoreRestSiblings` in `no-unused-vars`. ([@chrisdrackett](https://github.com/chrisdrackett)) + + Linter no longer warns when using rest properties to remove variables from an object. + + * [#1542](https://github.com/facebookincubator/create-react-app/pull/1542) Bump `jsx-a11y` version. ([@bondz](https://github.com/bondz)) + +* `react-dev-utils`, `react-scripts` + + * [#1726](https://github.com/facebookincubator/create-react-app/pull/1726) Extract generic build functions into `react-dev-utils`. ([@viankakrisna](https://github.com/viankakrisna)) + +* Other + + * [#1402](https://github.com/facebookincubator/create-react-app/pull/1402) Stub `package.json` for e2e test. ([@matoilic](https://github.com/matoilic)) + +#### :memo: Documentation +* `react-scripts` + * [#1710](https://github.com/facebookincubator/create-react-app/pull/1710) Update now.sh deployment instructions. ([@replaid](https://github.com/replaid)) + * [#1717](https://github.com/facebookincubator/create-react-app/pull/1717) Add docs for Apache client side routing. ([@viankakrisna](https://github.com/viankakrisna)) + * [#1698](https://github.com/facebookincubator/create-react-app/pull/1698) Suggest to use `.env` for enabling polling mode. ([@gaearon](https://github.com/gaearon)) + * [#1687](https://github.com/facebookincubator/create-react-app/pull/1687) Fixed missing `--recursive` flag in first `npm run watch-css` command. ([@mklemme](https://github.com/mklemme)) + +#### :house: Internal +* `react-scripts` + * [#1736](https://github.com/facebookincubator/create-react-app/pull/1736) Fix eject for linked react-scripts. ([@tuchk4](https://github.com/tuchk4)) + * [#1741](https://github.com/facebookincubator/create-react-app/pull/1741) Fix internal linting setup. ([@gaearon](https://github.com/gaearon)) + * [#1730](https://github.com/facebookincubator/create-react-app/pull/1730) Fix Node 4 e2e tests. ([@Timer](https://github.com/Timer)) +* `eslint-config-react-app` + * [#1740](https://github.com/facebookincubator/create-react-app/pull/1740) Relax ESLint config peerDependency. ([@gaearon](https://github.com/gaearon)) +* `eslint-config-react-app`, `react-dev-utils`, `react-scripts` + * [#1729](https://github.com/facebookincubator/create-react-app/pull/1729) Lint internal scripts with `eslint:recommended`. ([@gaearon](https://github.com/gaearon)) +* `react-dev-utils` + * [#1724](https://github.com/facebookincubator/create-react-app/pull/1724) Don't use ES6 in a file that should run on Node 4. ([@gaearon](https://github.com/gaearon)) +* Other + * [#1723](https://github.com/facebookincubator/create-react-app/pull/1723) Skip AppVeyor CI builds for Markdown changes. ([@gaearon](https://github.com/gaearon)) + * [#1707](https://github.com/facebookincubator/create-react-app/pull/1707) Add double quotes to escape spaces in paths in e2e. ([@viankakrisna](https://github.com/viankakrisna)) + * [#1688](https://github.com/facebookincubator/create-react-app/pull/1688) Upgrade `lerna` version. ([@viankakrisna](https://github.com/viankakrisna)) + +#### Committers: 11 +- Ade Viankakrisna Fadlil ([viankakrisna](https://github.com/viankakrisna)) +- Bond ([bondz](https://github.com/bondz)) +- Chris Drackett ([chrisdrackett](https://github.com/chrisdrackett)) +- Dan Abramov ([gaearon](https://github.com/gaearon)) +- Joe Haddad ([Timer](https://github.com/Timer)) +- Mato Ilic ([matoilic](https://github.com/matoilic)) +- Myk Klemme ([mklemme](https://github.com/mklemme)) +- Pierre Bertet ([bpierre](https://github.com/bpierre)) +- Ryan Platte ([replaid](https://github.com/replaid)) +- Travis Giggy ([tgig](https://github.com/tgig)) +- Valerii Sorokobatko ([tuchk4](https://github.com/tuchk4)) + +### Migrating from 0.9.3 to 0.9.4 + +Inside any created project that has not been ejected, run: + +``` +npm install --save-dev --save-exact react-scripts@0.9.4 +``` + +You may also optionally update the global command-line utility for scoped package support: + +``` +npm install -g create-react-app@1.3.0 +``` + +## 0.9.3 (February 28, 2017) + +#### :rocket: New Feature +* `create-react-app` + * [#1423](https://github.com/facebookincubator/create-react-app/pull/1423) **Fall back to Yarn offline cache when creating a new project.** ([@voxsim](https://github.com/voxsim)) + + If you are using Yarn, and you have created at least one app previously, Create React App now works offline. + + Yarn offline installation demo + +#### :bug: Bug Fix + +* `react-scripts` + + * [#1665](https://github.com/facebookincubator/create-react-app/pull/1665) Temporarily disable ESLint caching because of a bug. ([@gaearon](https://github.com/gaearon)) + +* `create-react-app` + * [#1675](https://github.com/facebookincubator/create-react-app/pull/1675) Delete project folder on failed installation on Windows. ([@johann-sonntagbauer](https://github.com/johann-sonntagbauer)) + * [#1662](https://github.com/facebookincubator/create-react-app/pull/1662) Validate project name before creating a project. ([@johann-sonntagbauer](https://github.com/johann-sonntagbauer)) + * [#1669](https://github.com/facebookincubator/create-react-app/pull/1669) Make sure React dependencies aren’t pinned in new projects. ([@johann-sonntagbauer](https://github.com/johann-sonntagbauer)) + +#### :nail_care: Enhancement +* `react-scripts` + + * [#1677](https://github.com/facebookincubator/create-react-app/pull/1677) Add `X-FORWARDED` headers for proxy requests. ([@johann-sonntagbauer](https://github.com/johann-sonntagbauer)) + +#### :memo: Documentation +* `react-scripts` + + * [#1657](https://github.com/facebookincubator/create-react-app/pull/1657) Tweak the Visual Studio Code debugging guide. ([@ryansully](https://github.com/ryansully)) + +#### :house: Internal +* End-to-end Tests + + * [#1648](https://github.com/facebookincubator/create-react-app/pull/1648) Add Windows CI tests for better stability. ([@Timer](https://github.com/Timer)) + +#### Committers: 5 +- Dan Abramov ([gaearon](https://github.com/gaearon)) +- Joe Haddad ([Timer](https://github.com/Timer)) +- Johann Hubert Sonntagbauer ([johann-sonntagbauer](https://github.com/johann-sonntagbauer)) +- Ryan Sullivan ([ryansully](https://github.com/ryansully)) +- Simon Vocella ([voxsim](https://github.com/voxsim)) + +### Migrating from 0.9.2 to 0.9.3 + +Inside any created project that has not been ejected, run: + +``` +npm install --save-dev --save-exact react-scripts@0.9.3 +``` + +You may also optionally update the global command-line utility for offline Yarn cache support: + +``` +npm install -g create-react-app@1.2.1 +``` + +## 0.9.2 (February 26, 2017) + +#### :nail_care: Enhancement + +* `create-react-app` + * [#1253](https://github.com/facebookincubator/create-react-app/pull/1253) **Install time optimization.** ([@n3tr](https://github.com/n3tr)) + + React, ReactDOM, and `react-scripts` are now installed in the same install instead of two different installs. This reduces app creation time by a noticeable amount. + + * [#1512](https://github.com/facebookincubator/create-react-app/pull/1512) **Graceful error handling.** ([@chitchu](https://github.com/chitchu)) + + If an error occurs while `create-react-app` is running, it will now clean up and not leave a broken project to reduce confusion. + + * [#1193](https://github.com/facebookincubator/create-react-app/pull/1193) Suggest upgrading to NPM >= 3 for faster install times. ([@mobinni](https://github.com/mobinni)) + + * [#1603](https://github.com/facebookincubator/create-react-app/pull/1603) Allow app creation in a WebStorm project. ([@driquelme](https://github.com/driquelme)) + + * [#1570](https://github.com/facebookincubator/create-react-app/pull/1570) Allow git urls in `--scripts-version`. ([@tomconroy](https://github.com/tomconroy)) + +* `react-scripts` + * [#1578](https://github.com/facebookincubator/create-react-app/pull/1578) Enable lint caching in development. ([@viankakrisna](https://github.com/viankakrisna)) + + * [#1478](https://github.com/facebookincubator/create-react-app/pull/1478) Update the build script message to show the correct port. ([@chyipin](https://github.com/chyipin)) + + * [#1567](https://github.com/facebookincubator/create-react-app/pull/1567) Remove .bin files after eject. ([@tuchk4](https://github.com/tuchk4)) + + * [#1560](https://github.com/facebookincubator/create-react-app/pull/1560) Bump `recursive-readdir`. ([@wtgtybhertgeghgtwtg](https://github.com/wtgtybhertgeghgtwtg)) + +#### :bug: Bug Fix +* `react-scripts` + + * [#1635](https://github.com/facebookincubator/create-react-app/pull/1635) **Fix Jest configuration.** ([@Timer](https://github.com/Timer)) + + Fixes ejecting on Windows for macOS and Linux machines. + + * [#1356](https://github.com/facebookincubator/create-react-app/pull/1356) Fix workflow if react-scripts package is linked via npm-link. ([@tuchk4](https://github.com/tuchk4)) + + Advanced users may opt to fork `react-scripts` instead of ejecting so they still receive upstream updates.
+ `react-scripts` will now function as expected when linking to a development version.
+ Previously, you could not test changes with an existing application via linking. + + * [#1585](https://github.com/facebookincubator/create-react-app/pull/1585) Ensure PORT environment variable is an integer. ([@matoilic](https://github.com/matoilic)) + + * [#1628](https://github.com/facebookincubator/create-react-app/pull/1628) Show correct port for pushstate-server URL text. ([@mattccrampton](https://github.com/mattccrampton)) + + * [#1647](https://github.com/facebookincubator/create-react-app/pull/1647) Fix `npm test` on Windows ([@gaearon](https://github.com/gaearon)) + + +#### :memo: Documentation +* User Guides + * [#1391](https://github.com/facebookincubator/create-react-app/pull/1391) Add note how to resolve missing required files for Heroku. ([@sbritoig](https://github.com/sbritoig)) + * [#1577](https://github.com/facebookincubator/create-react-app/pull/1577) Add a how-to on `react-snapshot`. ([@superhighfives](https://github.com/superhighfives)) + * [#1121](https://github.com/facebookincubator/create-react-app/pull/1121) Add documentation for customizing Bootstrap theme. ([@myappincome](https://github.com/myappincome)) + * [#1540](https://github.com/facebookincubator/create-react-app/pull/1540) Document debugging in Visual Studio Code. ([@bondz](https://github.com/bondz)) + * [#1618](https://github.com/facebookincubator/create-react-app/pull/1618) Add note about when to import Bootstrap CSS. ([@joewoodhouse](https://github.com/joewoodhouse)) + * [#1518](https://github.com/facebookincubator/create-react-app/pull/1518) Update flow configuration documentation. ([@SBrown52](https://github.com/SBrown52)) + * [#1625](https://github.com/facebookincubator/create-react-app/pull/1625) Specify that NODE_ENV is set to 'production' during the build step. ([@mderazon](https://github.com/mderazon)) + * [#1573](https://github.com/facebookincubator/create-react-app/pull/1573) Update Jest documentation links. ([@mkermani144](https://github.com/mkermani144)) + * [#1564](https://github.com/facebookincubator/create-react-app/pull/1564) Add --recursive to Sass watch script. ([@aleburato](https://github.com/aleburato)) + * [#1561](https://github.com/facebookincubator/create-react-app/pull/1561) Use https in link in documentation. ([@dariocravero](https://github.com/dariocravero)) + * [#1562](https://github.com/facebookincubator/create-react-app/pull/1562) Update `jest-enzyme` documentation. ([@kiranps](https://github.com/kiranps)) + * [#1543](https://github.com/facebookincubator/create-react-app/pull/1543) Update CSS preprocessor instructions. ([@aleburato](https://github.com/aleburato)) + * [#1338](https://github.com/facebookincubator/create-react-app/pull/1338) Add link to Azure deployment tutorial. ([@tpetrina](https://github.com/tpetrina)) + * [#1320](https://github.com/facebookincubator/create-react-app/pull/1320) Document how to disable autoprefix feature. ([@rrubas](https://github.com/rrubas)) + * [#1313](https://github.com/facebookincubator/create-react-app/pull/1313) List features beyond ES6 supported by create-react-app. ([@jonathanconway](https://github.com/jonathanconway)) + * [#1008](https://github.com/facebookincubator/create-react-app/pull/1008) Add Saas support documentation. ([@tsironis](https://github.com/tsironis)) + * [#994](https://github.com/facebookincubator/create-react-app/pull/994) Suggest `jest-enzyme` for simplifying test matchers. ([@blainekasten](https://github.com/blainekasten)) + * [#1608](https://github.com/facebookincubator/create-react-app/pull/1608) Add note for using CHOKIDAR_USEPOLLING in virtual machines to enable HMR. ([@AJamesPhillips](https://github.com/AJamesPhillips)) + * [#1495](https://github.com/facebookincubator/create-react-app/pull/1495) Add useful link to react-scripts. ([@pd4d10](https://github.com/pd4d10)) +* READMEs + * [#1576](https://github.com/facebookincubator/create-react-app/pull/1576) Switch from Neo to Neutrino. ([@eliperelman](https://github.com/eliperelman)) + * [#1275](https://github.com/facebookincubator/create-react-app/pull/1275) Suggest yarn commands in addition to npm. ([@lifez](https://github.com/lifez)) + +#### :house: Internal +* `babel-preset-react-app` + * [#1598](https://github.com/facebookincubator/create-react-app/pull/1598) Remove redundant babel-plugin-transform-es2015-parameters. ([@christophehurpeau](https://github.com/christophehurpeau)) +* Other + * [#1534](https://github.com/facebookincubator/create-react-app/pull/1534) Use yarn@latest in e2e. ([@gaearon](https://github.com/gaearon)) + * [#1295](https://github.com/facebookincubator/create-react-app/pull/1295) Make node version check more robust in e2e. ([@pugnascotia](https://github.com/pugnascotia)) + * [#1503](https://github.com/facebookincubator/create-react-app/pull/1503) Fix `test -e` in e2e. ([@igetgames](https://github.com/igetgames)) + +#### Committers: 36 +- Ade Viankakrisna Fadlil ([viankakrisna](https://github.com/viankakrisna)) +- Alessandro Burato ([aleburato](https://github.com/aleburato)) +- Alexander James Phillips ([AJamesPhillips](https://github.com/AJamesPhillips)) +- Blaine Kasten ([blainekasten](https://github.com/blainekasten)) +- Bond ([bondz](https://github.com/bondz)) +- Charlie Gleason ([superhighfives](https://github.com/superhighfives)) +- Christophe Hurpeau ([christophehurpeau](https://github.com/christophehurpeau)) +- Dan Abramov ([gaearon](https://github.com/gaearon)) +- Daniel Riquelme ([driquelme](https://github.com/driquelme)) +- Darío Javier Cravero ([dariocravero](https://github.com/dariocravero)) +- Dimitris Tsironis ([tsironis](https://github.com/tsironis)) +- Eli Perelman ([eliperelman](https://github.com/eliperelman)) +- Jirat Ki. ([n3tr](https://github.com/n3tr)) +- Joe Haddad ([Timer](https://github.com/Timer)) +- Joe Woodhouse ([joewoodhouse](https://github.com/joewoodhouse)) +- Jonathan Conway ([jonathanconway](https://github.com/jonathanconway)) +- Marcus R. Brown ([igetgames](https://github.com/igetgames)) +- Mato Ilic ([matoilic](https://github.com/matoilic)) +- Matt Crampton ([mattccrampton](https://github.com/mattccrampton)) +- Michael DeRazon ([mderazon](https://github.com/mderazon)) +- Mo Binni ([mobinni](https://github.com/mobinni)) +- Mohammad Kermani ([mkermani144](https://github.com/mkermani144)) +- Phawin Khongkhasawan ([lifez](https://github.com/lifez)) +- Roman Rubas ([rrubas](https://github.com/rrubas)) +- Rory Hunter ([pugnascotia](https://github.com/pugnascotia)) +- Tom Conroy ([tomconroy](https://github.com/tomconroy)) +- Toni Petrina ([tpetrina](https://github.com/tpetrina)) +- Valerii Sorokobatko ([tuchk4](https://github.com/tuchk4)) +- Vicente Jr Yuchitcho ([chitchu](https://github.com/chitchu)) +- [SBrown52](https://github.com/SBrown52) +- [chyipin](https://github.com/chyipin) +- [myappincome](https://github.com/myappincome) +- [sbritoig](https://github.com/sbritoig) +- [wtgtybhertgeghgtwtg](https://github.com/wtgtybhertgeghgtwtg) +- kiran ps ([kiranps](https://github.com/kiranps)) +- pd4d10 ([pd4d10](https://github.com/pd4d10)) + +### Migrating from 0.9.0 to 0.9.2 + +**Note:** 0.9.1 had known issues so you should skip it. + +Inside any created project that has not been ejected, run: + +``` +npm install --save-dev --save-exact react-scripts@0.9.2 +``` + +You may also optionally update the global command-line utility for more efficient installs (thanks [@n3tr](https://github.com/n3tr)): + +``` +npm install -g create-react-app@1.1.0 +``` + +## 0.9.1 (February 25, 2017) + +This release has known issues and you should skip it. Update directly to 0.9.2 instead. + +## 0.9.0 (February 11, 2017) + +Thanks to [@Timer](https://github.com/timer) for cutting this release. + +#### :rocket: New Feature + +* `react-scripts` + + * [#1489](https://github.com/facebookincubator/create-react-app/pull/1489) Support setting `"homepage"` to `"."` to generate relative asset paths. ([@tibdex](https://github.com/tibdex)) + + Applications that don’t use the HTML5 `pushState` API can now be built to be served from any relative URL. To enable this, specify `"."` as your `homepage` setting in `package.json`. It used to be possible before with a few known bugs, but they should be fixed now. See [Serving the Same Build from Different Paths](https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#serving-the-same-build-from-different-paths). + + * [#937](https://github.com/facebookincubator/create-react-app/pull/1504) Add `PUBLIC_URL` environment variable for advanced use. ([@EnoahNetzach](https://github.com/EnoahNetzach)) + + If you use a CDN to serve the app, you can now specify `PUBLIC_URL` environment variable to override the base URL (including the hostname) for resources referenced from the built code. This new variable is mentioned in the new [Advanced Configuration](https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#advanced-configuration) section. + + * [#1440](https://github.com/facebookincubator/create-react-app/pull/1440) Make all `REACT_APP_*` environment variables accessible in `index.html`. ([@jihchi](https://github.com/jihchi)) + + This makes all environment variables previously available in JS, also available in the HTML file, for example `%REACT_APP_MY_VARIABLE%`. See [Referencing Environment Variables in HTML](https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#referencing-environment-variables-in-the-html). + +* `react-dev-utils` + + * [#1148](https://github.com/facebookincubator/create-react-app/pull/1148) Configure which browser to open with `npm start`. ([@GAumala](https://github.com/GAumala)) + + You can now disable the automatic browser launching by setting the `BROWSER` environment variable to `none`. You can also specify a different browser (or an arbitrary script) to open by default, [as supported by `opn` command](https://github.com/sindresorhus/opn#app) that we use under the hood. See [Advanced Configuration](https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#advanced-configuration). + +#### :boom: Breaking Change + +* `react-scripts` + + * [#1522](https://github.com/facebookincubator/create-react-app/pull/1522) Upgrade dependencies. ([@Timer](https://github.com/Timer)) + * [#1432](https://github.com/facebookincubator/create-react-app/pull/1432) Bump Jest version. ([@gaearon](https://github.com/gaearon)) + * [#1311](https://github.com/facebookincubator/create-react-app/pull/1311) Updated `babel-jest` and `jest` packages to 18.0.0. ([@lopezator](https://github.com/lopezator)) + + Jest has been updated to 18 and has introduced some [breaking changes and new features](https://facebook.github.io/jest/blog/2016/12/15/2016-in-jest.html). + +* `react-scripts`, `react-dev-utils` + + * [#1264](https://github.com/facebookincubator/create-react-app/pull/1264) Remove interactive shell check when opening browser on start. ([@CaryLandholt](https://github.com/CaryLandholt)) + + Non-interactive terminals no longer automatically disable launching of the browser. Instead, you need to [specify `none` as `BROWSER` environment variable](https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#advanced-configuration) if you wish to disable it. + +#### :bug: Bug Fix + +* `react-scripts` + + * [#1441](https://github.com/facebookincubator/create-react-app/pull/1441) Added `babel-runtime` dependency to deduplicate dependencies when using Yarn. ([@jkimbo](https://github.com/jkimbo)) + + This works around a bug in Yarn that caused newly created projects to be over 400MB. Now they are down to 126MB, just like with npm 3. + + * [#1522](https://github.com/facebookincubator/create-react-app/pull/1522) Upgrade dependencies. ([@Timer](https://github.com/Timer)) + * [#1458](https://github.com/facebookincubator/create-react-app/pull/1458) Additionally remove `react-scripts` from dependencies on eject. ([@creynders](https://github.com/creynders)) + * [#1309](https://github.com/facebookincubator/create-react-app/pull/1309) Bump `babel-loader` version (#1009). ([@frontsideair](https://github.com/frontsideair)) + * [#1267](https://github.com/facebookincubator/create-react-app/pull/1267) Only gitignore directories in root, not deep. ([@jayphelps](https://github.com/jayphelps)) + +* `react-dev-utils` + + * [#1377](https://github.com/facebookincubator/create-react-app/pull/1377) webpack-dev-server patch for 'still-ok' success status. ([@TheBlackBolt](https://github.com/TheBlackBolt)) + * [#1274](https://github.com/facebookincubator/create-react-app/pull/1274) Downgrading to compatible version of SockJS-Client. ([@holloway](https://github.com/holloway)) + * [#1247](https://github.com/facebookincubator/create-react-app/pull/1247) Only open Chrome tab if BROWSER is missing or is Chrome. ([@gaearon](https://github.com/gaearon)) + +#### :nail_care: Enhancement + +* `react-scripts` + + * [#1496](https://github.com/facebookincubator/create-react-app/pull/1496) Make build exit with error code when interrupted. ([@brandones](https://github.com/brandones)) + * [#1352](https://github.com/facebookincubator/create-react-app/pull/1352) More descriptive error message for `env.CI = true` warnings causing failures. ([@jayphelps](https://github.com/jayphelps)) + * [#1264](https://github.com/facebookincubator/create-react-app/pull/1264) Remove interactive shell check when opening browser on start. ([@CaryLandholt](https://github.com/CaryLandholt)) + * [#1311](https://github.com/facebookincubator/create-react-app/pull/1311) Updated `babel-jest` and `jest` packages to 18.0.0. ([@lopezator](https://github.com/lopezator)) + * [#1432](https://github.com/facebookincubator/create-react-app/pull/1432) Bump Jest version. ([@gaearon](https://github.com/gaearon)) + * [#1507](https://github.com/facebookincubator/create-react-app/pull/1507) fix: add yarn gitignores. ([@adjohnson916](https://github.com/adjohnson916)) + * [#1510](https://github.com/facebookincubator/create-react-app/pull/1510) Add missing `'\n'` to the end of `package.json` file. ([@pd4d10](https://github.com/pd4d10)) + * [#1324](https://github.com/facebookincubator/create-react-app/pull/1324) Use npm script hooks to avoid `&&` in deploy script. ([@zpao](https://github.com/zpao)) + +* `create-react-app` + + * [#1270](https://github.com/facebookincubator/create-react-app/pull/1270) gh-1269: Enabling nested folder paths for project name. ([@dinukadesilva](https://github.com/dinukadesilva)) + + +#### :memo: Documentation + +* User Guide + + * [#1515](https://github.com/facebookincubator/create-react-app/pull/1515) readme: Advanced Configuration. ([@Timer](https://github.com/Timer)) + * [#1513](https://github.com/facebookincubator/create-react-app/pull/1513) clarifying the use of custom environment variables. ([@calweb](https://github.com/calweb)) + * [#1511](https://github.com/facebookincubator/create-react-app/pull/1511) Change "OS X" references to "macOS". ([@RodrigoHahn](https://github.com/RodrigoHahn)) + * [#1482](https://github.com/facebookincubator/create-react-app/pull/1482) Edit User Guide: Add ESLint config for VS Code users. ([@vulong23](https://github.com/vulong23)) + * [#1483](https://github.com/facebookincubator/create-react-app/pull/1483) Reflect websocket proxy support on README (#1013). ([@frontsideair](https://github.com/frontsideair)) + * [#1453](https://github.com/facebookincubator/create-react-app/pull/1453) Readme: Removes experimental from Jest snapshot. ([@frehner](https://github.com/frehner)) + * [#1437](https://github.com/facebookincubator/create-react-app/pull/1437) Added links to tutorials for integrating cra with an api backend. ([@alexdriaguine](https://github.com/alexdriaguine)) + * [#1422](https://github.com/facebookincubator/create-react-app/pull/1422) Add causes of dev server not detecting changes. ([@jetpackpony](https://github.com/jetpackpony)) + * [#1260](https://github.com/facebookincubator/create-react-app/pull/1260) Heroku Deployment: Adds a note on how to resolve "File/Module Not Found Errors" . ([@MsUzoAgu](https://github.com/MsUzoAgu)) + * [#1256](https://github.com/facebookincubator/create-react-app/pull/1256) Add "Changing the Page Title" to User Guide. ([@gaearon](https://github.com/gaearon)) + * [#1245](https://github.com/facebookincubator/create-react-app/pull/1245) Replace the Flow documentation section. ([@gaearon](https://github.com/gaearon)) + * [#1514](https://github.com/facebookincubator/create-react-app/pull/1514) corrected minor typo. ([@crowchirp](https://github.com/crowchirp)) + * [#1393](https://github.com/facebookincubator/create-react-app/pull/1393) replace two space syntax with br tag. ([@carlsagan21](https://github.com/carlsagan21)) + * [#1384](https://github.com/facebookincubator/create-react-app/pull/1384) Document Flow support. ([@dschep](https://github.com/dschep)) + +* READMEs + + * [#1375](https://github.com/facebookincubator/create-react-app/pull/1375) Change console.log for errors and warnings. ([@jimmyhmiller](https://github.com/jimmyhmiller)) + * [#1369](https://github.com/facebookincubator/create-react-app/pull/1369) Add missing import in react-dev-utils README.md. ([@pedronauck](https://github.com/pedronauck)) + +#### :house: Internal + +* Internal Test Suite + + * [#1519](https://github.com/facebookincubator/create-react-app/pull/1519) Add test cases for PUBLIC_URL and relative path. ([@Timer](https://github.com/Timer)) + * [#1484](https://github.com/facebookincubator/create-react-app/pull/1484) Improve e2e-kitchensink and Jest coverage. ([@Timer](https://github.com/Timer)) + * [#1463](https://github.com/facebookincubator/create-react-app/pull/1463) Minor code style and wrong expect. ([@tuchk4](https://github.com/tuchk4)) + * [#1470](https://github.com/facebookincubator/create-react-app/pull/1470) E2e jsdom fix. ([@EnoahNetzach](https://github.com/EnoahNetzach)) + * [#1187](https://github.com/facebookincubator/create-react-app/pull/1187) Use a more sophisticated template for end-to-end testing.. ([@EnoahNetzach](https://github.com/EnoahNetzach)) + +* Other + + * [#1289](https://github.com/facebookincubator/create-react-app/pull/1289) Remove path-exists from dependencies and replace it with fs.existsSync. ([@halfzebra](https://github.com/halfzebra)) + +#### Committers: 35 +- Alex Driaguine ([alexdriaguine](https://github.com/alexdriaguine)) +- Anders D. Johnson ([adjohnson916](https://github.com/adjohnson916)) +- Anthony F. ([frehner](https://github.com/frehner)) +- Brandon Istenes ([brandones](https://github.com/brandones)) +- Calvin Webster ([calweb](https://github.com/calweb)) +- Cary Landholt ([CaryLandholt](https://github.com/CaryLandholt)) +- Chandan Rai ([crowchirp](https://github.com/crowchirp)) +- Christian Raidl ([Chris-R3](https://github.com/Chris-R3)) +- Dan Abramov ([gaearon](https://github.com/gaearon)) +- Daniel Schep ([dschep](https://github.com/dschep)) +- David ([lopezator](https://github.com/lopezator)) +- Dinuka De Silva ([dinukadesilva](https://github.com/dinukadesilva)) +- Eduard Kyvenko ([halfzebra](https://github.com/halfzebra)) +- Fabrizio Castellarin ([EnoahNetzach](https://github.com/EnoahNetzach)) +- Fatih ([frontsideair](https://github.com/frontsideair)) +- Gabriel Aumala ([GAumala](https://github.com/GAumala)) +- Jay Phelps ([jayphelps](https://github.com/jayphelps)) +- Jih-Chi Lee ([jihchi](https://github.com/jihchi)) +- Jimmy Miller ([jimmyhmiller](https://github.com/jimmyhmiller)) +- Joe Haddad ([Timer](https://github.com/Timer)) +- Johnny Magrippis ([jmagrippis](https://github.com/jmagrippis)) +- Jonathan Kim ([jkimbo](https://github.com/jkimbo)) +- MUA ([MsUzoAgu](https://github.com/MsUzoAgu)) +- Matthew Holloway ([holloway](https://github.com/holloway)) +- Nguyen Le Vu Long ([vulong23](https://github.com/vulong23)) +- Paul O’Shannessy ([zpao](https://github.com/zpao)) +- Pedro Nauck ([pedronauck](https://github.com/pedronauck)) +- Robbie H ([TheBlackBolt](https://github.com/TheBlackBolt)) +- Thibault Derousseaux ([tibdex](https://github.com/tibdex)) +- Valerii ([tuchk4](https://github.com/tuchk4)) +- Vasiliy Taranov ([jetpackpony](https://github.com/jetpackpony)) +- [RodrigoHahn](https://github.com/RodrigoHahn) +- creynders ([creynders](https://github.com/creynders)) +- pd4d10 ([pd4d10](https://github.com/pd4d10)) +- soo ([carlsagan21](https://github.com/carlsagan21)) + +### Migrating from 0.8.5 to 0.9.0 + +Inside any created project that has not been ejected, run: + +``` +npm install --save-dev --save-exact react-scripts@0.9.0 +``` + +Then, run your tests. If you are affected by breaking changes from Jest 18, consult [blog post](https://facebook.github.io/jest/blog/2016/12/15/2016-in-jest.html), [changelog](https://github.com/facebook/jest/blob/master/CHANGELOG.md#jest-1800), and [documentation](http://facebook.github.io/jest/docs/getting-started.html). You might need to update any snapshots since their format might have changed. + +If you relied on the browser not starting in non-interactive terminals, you now need to explicitly specify `BROWSER=none` as an environment variable to disable it. + ## 0.8.5 (January 9, 2017) +Thanks to [@fson](https://github.com/fson) for cutting this release. + #### :bug: Bug Fix * `create-react-app`, `react-scripts` * [#1365](https://github.com/facebookincubator/create-react-app/pull/1365) Use yarnpkg alias to run Yarn. ([@fson](https://github.com/fson)) @@ -29,60 +531,60 @@ npm install -g create-react-app@1.0.3 * `react-scripts` * [#1233](https://github.com/facebookincubator/create-react-app/pull/1233) Disable subresource integrity temporarily. ([@Timer](https://github.com/Timer)) - + We added [Subresource Integrity](https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity) checks to the build output in 0.8.2 but it turns out that they may fail in browsers using special compression proxies, such as Chrome on Android, when served over HTTP. We disabled the checks until we can find a safe way to add them. - + * `react-dev-utils` * [#1226](https://github.com/facebookincubator/create-react-app/pull/1226) Fix weird lint output. ([@n3tr](https://github.com/n3tr)) - + Fixes strange lint message formatting in some edge cases. - + * [#1215](https://github.com/facebookincubator/create-react-app/pull/1215) Fix - openChrome won't open default browser (using Canary). ([@n3tr](https://github.com/n3tr)) - + Fixes a regression that caused stable Google Chrome to be opened even if you are using Canary as the default browser. - + * `create-react-app` * [#1223](https://github.com/facebookincubator/create-react-app/pull/1223) Clean up Yarn detection and install code. ([@fson](https://github.com/fson)) - + Fixes noisy output on Windows when Yarn is not installed. * [#1224](https://github.com/facebookincubator/create-react-app/pull/1224) Exit with an error code when npm/yarn install fails. ([@fson](https://github.com/fson)) - + #### :nail_care: Enhancement * `react-scripts` * [#1237](https://github.com/facebookincubator/create-react-app/pull/1237) Clear scrollback in test mode. ([@gaearon](https://github.com/gaearon)) - + Ensures test watcher clears the console before running. - + * [#1229](https://github.com/facebookincubator/create-react-app/pull/1229) Disable jest watch mode when --coverage flag is present [#1207]. ([@BenoitAverty](https://github.com/BenoitAverty)) - + Since coverage doesn't work well with watch mode, we don’t run the watcher on `npm test -- --coverage` anymore. - + * [#1212](https://github.com/facebookincubator/create-react-app/pull/1212) Proxy rewrites Origin header to match the target server URL. ([@koles](https://github.com/koles)) - + Makes sure more API endpoints can work with the `proxy` setting. - + * [#1222](https://github.com/facebookincubator/create-react-app/pull/1222) Disable gh-page setup instruction if scripts.deploy has been added. ([@n3tr](https://github.com/n3tr)) - + Suppresses the instructions printed at the end of `npm run build` if `npm run deploy` already exists. * `create-react-app` * [#1236](https://github.com/facebookincubator/create-react-app/pull/1236) Tweak console messages. ([@gaearon](https://github.com/gaearon)) - + Makes error messages more friendly. - + * [#1195](https://github.com/facebookincubator/create-react-app/pull/1195) Use "commander" for cli argv handling. ([@EnoahNetzach](https://github.com/EnoahNetzach)) - + Adds `create-react-app --help` with a list of options. * `react-dev-utils` * [#1211](https://github.com/facebookincubator/create-react-app/pull/1211) Use a better clear console sequence. ([@gaearon](https://github.com/gaearon)) - + Ensures the development server clears the terminal when files are changed. #### :memo: Documentation @@ -132,14 +634,14 @@ npm install -g create-react-app@1.0.2 * [#1204](https://github.com/facebookincubator/create-react-app/pull/1204) Catch synchronous errors from spawning yarn. ([@gaearon](https://github.com/gaearon)) Fixes a crash when running `create-react-app` in some cases. - + * `react-scripts` * [#1203](https://github.com/facebookincubator/create-react-app/pull/1203) Update webpack-subresource-integrity to fix Windows builds. ([@gaearon](https://github.com/gaearon)) - + Fixes a crash when running `npm run build` on Windows. - + * [#1201](https://github.com/facebookincubator/create-react-app/pull/1201) Instruct Jest to load native components from RNW instead of RN. ([@remon-georgy](https://github.com/remon-georgy)) - + Fixes tests for users of React Native Web. #### :memo: Documentation @@ -251,6 +753,8 @@ npm install --save-dev --save-exact react-scripts@0.8.2 ## 0.8.1 (December 4, 2016) +Thanks to [@fson](https://github.com/fson) for cutting this release. + #### :bug: Bug Fix * `react-scripts` * [#1149](https://github.com/facebookincubator/create-react-app/pull/1149) Fix incorrectly stubbing JavaScript files with a dot in the import path in tests. ([@fson](https://github.com/fson)) @@ -265,6 +769,8 @@ npm install --save-dev --save-exact react-scripts@0.8.1 ## 0.8.0 (December 3, 2016) +Thanks to [@fson](https://github.com/fson) for cutting this release. + #### :rocket: New Feature * `react-scripts` * [#944](https://github.com/facebookincubator/create-react-app/pull/944) Crash the build during CI whenever linter warnings are encountered. ([@excitement-engineer](https://github.com/excitement-engineer)) @@ -419,6 +925,8 @@ npm install --save-dev --save-exact react-scripts@0.8.0 ## 0.7.0 (October 22, 2016) +Thanks to [@fson](https://github.com/fson) for cutting this release. + ### Build Dependency (`react-scripts`) * Updates Jest to [version 16.0](http://facebook.github.io/jest/blog/2016/10/03/jest-16.html), with an upgraded CLI, improved snapshot testing, new matchers and more. ([@chase](https://github.com/chase) in [#858](https://github.com/facebookincubator/create-react-app/pull/858)) @@ -439,7 +947,7 @@ npm install --save-dev --save-exact react-scripts@0.8.0 ### Babel Preset (`babel-preset-react-app`) -* The preset now detects the Node.js version in test environment and disables unnecessary ES2015 transforms using using `babel-preset-env`. ([@shubheksha](https://github.com/shubheksha) in [#878](https://github.com/facebookincubator/create-react-app/pull/878), [@JeffreyATW](https://github.com/JeffreyATW) in [#927 +* The preset now detects the Node.js version in test environment and disables unnecessary ES2015 transforms using `babel-preset-env`. ([@shubheksha](https://github.com/shubheksha) in [#878](https://github.com/facebookincubator/create-react-app/pull/878), [@JeffreyATW](https://github.com/JeffreyATW) in [#927 ](https://github.com/facebookincubator/create-react-app/pull/927)) * Fixes a duplicate dependency on `babel-plugin-transform-regenerator`. ([@akofman](https://github.com/akofman) in [#864](https://github.com/facebookincubator/create-react-app/pull/864)) @@ -597,7 +1105,7 @@ This ensures it become a part of the build output, and resolves correctly both w ## 0.4.3 (September 18, 2016) -This is a hotfix release for a broken package. +This is a hotfix release for a broken package.
It contained no changes to the code. ### Build Dependency (`react-scripts`) @@ -739,7 +1247,7 @@ npm install --save-dev --save-exact react-scripts@0.3.0 #### Breaking Change -Now `favicon.ico` is not treated specially anymore. +Now `favicon.ico` is not treated specially anymore.
If you use it, move it to `src` and add the following line to `` in your HTML: ```html diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 74785fd16ce..693ba51bde7 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -10,7 +10,7 @@ Following these guidelines helps to communicate that you respect the time of the As much as possible, we try to avoid adding configuration and flags. The purpose of this tool is to provide the best experience for people getting started with React, and this will always be our first priority. This means that sometimes we [sacrifice additional functionality](https://gettingreal.37signals.com/ch05_Half_Not_Half_Assed.php) (such as server rendering) because it is too hard to solve it in a way that wouldn’t require any configuration. -We prefer **convention, heuristics, or interactivity** over configuration. +We prefer **convention, heuristics, or interactivity** over configuration.
Here’s a few examples of them in action. ### Convention @@ -41,12 +41,44 @@ Please **ask first** if somebody else is already working on this or the core dev Please also provide a **test plan**, i.e. specify how you verified that your addition works. +## Folder Structure of Create React App +`create-react-app` is a monorepo, meaning it is divided into independent sub-packages.
+These packages can be found in the [`packages/`](https://github.com/facebookincubator/create-react-app/tree/master/packages) directory. + +### Overview of directory structure +``` +packages/ + babel-preset-react-app/ + create-react-app/ + eslint-config-react-app/ + react-dev-utils/ + react-scripts/ +``` +### Package Descriptions +#### [babel-preset-react-app](https://github.com/facebookincubator/create-react-app/tree/master/packages/babel-preset-react-app) +This package is a babel preset intended to be used with `react-scripts`.
+It targets platforms that React is designed to support (IE 9+) and enables experimental features used heavily at Facebook.
+This package is enabled by default for all `create-react-app` scaffolded applications. +#### [create-react-app](https://github.com/facebookincubator/create-react-app/tree/master/packages/create-react-app) +The global CLI command code can be found in this directory, and shouldn't often be changed. It should run on Node 0.10+. +#### [eslint-config-react-app](https://github.com/facebookincubator/create-react-app/tree/master/packages/eslint-config-react-app) +This package contains a conservative set of rules focused on making errors apparent and enforces no style rules.
+This package is enabled by default for all `create-react-app` scaffolded applications. +#### [react-dev-utils](https://github.com/facebookincubator/create-react-app/tree/master/packages/react-dev-utils) +This package contains utilities used for `react-scripts` and sister packages.
+Its main purpose is to conceal code which the user shouldn't be burdened with upon ejecting. +#### [react-scripts](https://github.com/facebookincubator/create-react-app/tree/master/packages/react-scripts) +This package is the heart of the project, which contains the scripts for setting up the development server, building production builds, configuring all software used, etc.
+All functionality must be retained (and configuration given to the user) if they choose to eject. + ## Setting Up a Local Copy 1. Clone the repo with `git clone https://github.com/facebookincubator/create-react-app` 2. Run `npm install` in the root `create-react-app` folder. +3. *(Only for macOS Sierra)*: Until [0.10.0](https://github.com/facebookincubator/create-react-app/milestone/23) is released, you may need to install [Watchman](https://facebook.github.io/watchman/docs/install.html) (e.g. `brew install watchman`). + Once it is done, you can modify any file locally and run `npm start`, `npm test` or `npm run build` just like in a generated project. If you want to try out the end-to-end flow with the global CLI, you can do this too: @@ -68,6 +100,7 @@ and then run `npm start` or `npm run build`. * You'll need an [access token for the GitHub API](https://help.github.com/articles/creating-an-access-token-for-command-line-use/). Save it to this environment variable: `export GITHUB_AUTH="..."` * Run `npm run changelog`. The command will find all the labeled pull requests merged since the last release and group them by the label and affected packages, and create a change log entry with all the changes and links to PRs and their authors. Copy and paste it to `CHANGELOG.md`. * Add a four-space indented paragraph after each non-trivial list item, explaining what changed and why. For each breaking change also write who it affects and instructions for migrating existing code. + * Maybe add some newlines here and there. Preview the result on GitHub to get a feel for it. Changelog generator output is a bit too terse for my taste, so try to make it visually pleasing and well grouped. 6. Make sure to include “Migrating from ...” instructions for the previous release. Often you can copy and paste them. 7. After merging the changelog update, create a GitHub Release with the same text. See previous Releases for inspiration. 8. **Do not run `npm publish`. Instead, run `npm run publish`.** diff --git a/README.md b/README.md index 85a23174d2b..d57e589e5bc 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,10 @@ Create React apps with no build configuration. * [Getting Started](#getting-started) – How to create a new app. * [User Guide](https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md) – How to develop apps bootstrapped with Create React App. -## tl;dr +Create React App works on macOS, Windows, and Linux.
+If something doesn’t work please [file an issue](https://github.com/facebookincubator/create-react-app/issues/new). + +## Quick Overview ```sh npm install -g create-react-app @@ -13,7 +16,6 @@ npm install -g create-react-app create-react-app my-app cd my-app/ npm start - ``` Then open [http://localhost:3000/](http://localhost:3000/) to see your app.
@@ -21,6 +23,13 @@ When you’re ready to deploy to production, create a minified bundle with `npm npm start +### Get Started Immediately + +You **don’t** need to install or configure tools like Webpack or Babel.
+They are preconfigured and hidden so that you can focus on the code. + +Just create a project, and you’re good to go. + ## Getting Started ### Installation @@ -35,7 +44,7 @@ npm install -g create-react-app **We strongly recommend to use Node >= 6 and npm >= 3 for faster installation speed and better disk usage.** You can use [nvm](https://github.com/creationix/nvm#usage) to easily switch Node versions between different projects. -**This tool doesn’t assume a Node backend**. The Node installation is only required for the build tools that rely on it locally, such as Webpack and Babel. +**This tool doesn’t assume a Node backend**. The Node installation is only required for Create React App itself. ### Creating an App @@ -70,7 +79,7 @@ my-app/ No configuration or complicated folder structures, just the files you need to build your app.
Once the installation is done, you can run some commands inside the project folder: -### `npm start` +### `npm start` or `yarn start` Runs the app in development mode.
Open [http://localhost:3000](http://localhost:3000) to view it in the browser. @@ -80,14 +89,14 @@ You will see the build errors and lint warnings in the console. Build errors -### `npm test` +### `npm test` or `yarn test` Runs the test watcher in an interactive mode.
By default, runs tests related to files changes since the last commit. [Read more about testing.](https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#running-tests) -### `npm run build` +### `npm run build` or `yarn build` Builds the app for production to the `build` folder.
It correctly bundles React in production mode and optimizes the build for the best performance. @@ -102,13 +111,16 @@ The [User Guide](https://github.com/facebookincubator/create-react-app/blob/mast - [Updating to New Releases](https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#updating-to-new-releases) - [Folder Structure](https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#folder-structure) - [Available Scripts](https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#available-scripts) +- [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) +- [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) - [Importing a Component](https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#importing-a-component) - [Adding a Stylesheet](https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#adding-a-stylesheet) - [Post-Processing CSS](https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#post-processing-css) +- [Adding a CSS Preprocessor (Sass, Less etc.)](https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#adding-a-css-preprocessor-sass-less-etc) - [Adding Images and Fonts](https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#adding-images-and-fonts) - [Using the `public` Folder](https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#using-the-public-folder) - [Using Global Variables](https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#using-global-variables) @@ -120,10 +132,12 @@ The [User Guide](https://github.com/facebookincubator/create-react-app/blob/mast - [Proxying API Requests in Development](https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#proxying-api-requests-in-development) - [Using HTTPS in Development](https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#using-https-in-development) - [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) - [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) A copy of the user guide will be created as `README.md` in your project folder. @@ -172,7 +186,7 @@ Some features are currently **not supported**: * Server rendering. * Some experimental syntax extensions (e.g. decorators). * CSS Modules. -* LESS or Sass. +* Importing LESS or Sass directly ([but you still can use them](https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#adding-a-css-preprocessor-sass-less-etc)). * Hot reloading of components. Some of them might get added in the future if they are stable, are useful to majority of React apps, don’t conflict with existing tools, and don’t introduce additional configuration. @@ -195,6 +209,11 @@ All of them are transitive dependencies of the provided npm package. We'd love to have your helping hand on `create-react-app`! See [CONTRIBUTING.md](CONTRIBUTING.md) for more information on what we're looking for and how to get started. +## React Native + +Looking for something similar, but for React Native?<br> +Check out [Create React Native App](https://github.com/react-community/create-react-native-app/). + ## Acknowledgements We are grateful to the authors of existing related projects for their ideas and collaboration: @@ -209,7 +228,7 @@ If you don’t agree with the choices made in this project, you might want to ex Some of the more popular and actively maintained ones are: * [insin/nwb](https://github.com/insin/nwb) -* [mozilla/neo](https://github.com/mozilla/neo) +* [mozilla-neutrino/neutrino-dev](https://github.com/mozilla-neutrino/neutrino-dev) * [NYTimes/kyt](https://github.com/NYTimes/kyt) * [zeit/next.js](https://github.com/zeit/next.js) * [gatsbyjs/gatsby](https://github.com/gatsbyjs/gatsby) @@ -225,6 +244,7 @@ Notable alternatives also include: * [react-app](https://github.com/kriasoft/react-app) * [dev-toolkit](https://github.com/stoikerty/dev-toolkit) * [tarec](https://github.com/geowarin/tarec) +* [sku](https://github.com/seek-oss/sku) You can also use module bundlers like [webpack](http://webpack.github.io) and [Browserify](http://browserify.org/) directly.<br> React documentation includes [a walkthrough](https://facebook.github.io/react/docs/package-management.html) on this topic. diff --git a/appveyor.cleanup-cache.txt b/appveyor.cleanup-cache.txt new file mode 100644 index 00000000000..19d0b989b62 --- /dev/null +++ b/appveyor.cleanup-cache.txt @@ -0,0 +1,5 @@ +Edit this file to trigger a cache rebuild. +http://help.appveyor.com/discussions/questions/1310-delete-cache + +---- +Just testing if this works. diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 00000000000..8b2b688fc66 --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,50 @@ +image: Visual Studio 2017 + +environment: + matrix: + - nodejs_version: 7 + test_suite: "simple" + - nodejs_version: 7 + test_suite: "installs" + - nodejs_version: 7 + test_suite: "kitchensink" + - nodejs_version: 6 + test_suite: "simple" + - nodejs_version: 6 + test_suite: "installs" + - nodejs_version: 6 + test_suite: "kitchensink" + - nodejs_version: 4 + test_suite: "simple" + - nodejs_version: 4 + test_suite: "installs" + - nodejs_version: 4 + test_suite: "kitchensink" + +cache: + - node_modules -> appveyor.cleanup-cache.txt + - packages\react-scripts\node_modules -> appveyor.cleanup-cache.txt + +clone_depth: 50 + +matrix: + fast_finish: true + +platform: + - x64 + +install: + # TODO: Remove after https://github.com/appveyor/ci/issues/1426 is fixed + - set PATH=C:\Program Files\Git\mingw64\bin;%PATH% + - ps: Install-Product node $env:nodejs_version $env:platform + +build: off + +skip_commits: + files: + - '**/*.md' + +test_script: + - node --version + - npm --version + - sh tasks/e2e-%test_suite%.sh diff --git a/lerna.json b/lerna.json index 00b2a8875a0..e2429777df4 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "lerna": "2.0.0-beta.30", + "lerna": "2.0.0-beta.38", "version": "independent", "changelog": { "repo": "facebookincubator/create-react-app", @@ -12,5 +12,8 @@ "tag: internal": ":house: Internal" }, "cacheDir": ".changelog" - } + }, + "packages": [ + "packages/*" + ] } diff --git a/package.json b/package.json index 6705e0413f9..46bc55b0245 100644 --- a/package.json +++ b/package.json @@ -11,14 +11,8 @@ "test": "node packages/react-scripts/scripts/test.js --env=jsdom" }, "devDependencies": { - "babel-eslint": "6.1.2", - "eslint": "3.5.0", - "eslint-config-react-app": "0.2.1", - "eslint-plugin-flowtype": "2.18.1", - "eslint-plugin-import": "1.12.0", - "eslint-plugin-jsx-a11y": "2.2.2", - "eslint-plugin-react": "6.3.0", - "lerna": "2.0.0-beta.30", + "eslint": "3.16.1", + "lerna": "2.0.0-beta.38", "lerna-changelog": "^0.2.3" } } diff --git a/packages/babel-preset-react-app/README.md b/packages/babel-preset-react-app/README.md index 5221c41af59..4dc9fb9b168 100644 --- a/packages/babel-preset-react-app/README.md +++ b/packages/babel-preset-react-app/README.md @@ -1,6 +1,6 @@ # babel-preset-react-app -This package includes the Babel preset used by [Create React App](https://github.com/facebookincubator/create-react-app). +This package includes the Babel preset used by [Create React App](https://github.com/facebookincubator/create-react-app).<br> Please refer to its documentation: * [Getting Started](https://github.com/facebookincubator/create-react-app/blob/master/README.md#getting-started) – How to create a new app. diff --git a/packages/babel-preset-react-app/index.js b/packages/babel-preset-react-app/index.js index a028babc06c..3df26bf5774 100644 --- a/packages/babel-preset-react-app/index.js +++ b/packages/babel-preset-react-app/index.js @@ -64,13 +64,6 @@ if (env === 'development' || env === 'test') { } if (env === 'test') { - plugins.push.apply(plugins, [ - // We always include this plugin regardless of environment - // because of a Babel bug that breaks object rest/spread without it: - // https://github.com/babel/babel/issues/4851 - require.resolve('babel-plugin-transform-es2015-parameters') - ]); - module.exports = { presets: [ // ES features necessary for user's Node version @@ -88,14 +81,24 @@ if (env === 'test') { module.exports = { presets: [ // Latest stable ECMAScript features - require.resolve('babel-preset-latest'), + [require.resolve('babel-preset-env'), { + targets: { + // React parses on ie 9, so we should too + ie: 9, + // We currently minify with uglify + // Remove after https://github.com/mishoo/UglifyJS2/issues/448 + uglify: true + }, + // Disable polyfill transforms + useBuiltIns: false + }], // JSX, Flow require.resolve('babel-preset-react') ], plugins: plugins.concat([ // function* () { yield 42; yield 43; } [require.resolve('babel-plugin-transform-regenerator'), { - // Async functions are converted to generators by babel-preset-latest + // Async functions are converted to generators by babel-preset-env async: false }], ]) diff --git a/packages/babel-preset-react-app/package.json b/packages/babel-preset-react-app/package.json index c9513ae340a..e8b6fb4dbf3 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": "2.0.1", + "version": "2.2.0", "description": "Babel preset used by Create React App", "repository": "facebookincubator/create-react-app", "license": "BSD-3-Clause", @@ -11,18 +11,16 @@ "index.js" ], "dependencies": { - "babel-plugin-transform-class-properties": "6.16.0", - "babel-plugin-transform-es2015-parameters": "6.18.0", - "babel-plugin-transform-object-rest-spread": "6.19.0", - "babel-plugin-transform-react-constant-elements": "6.9.1", - "babel-plugin-transform-react-jsx": "6.8.0", - "babel-plugin-transform-react-jsx-self": "6.11.0", - "babel-plugin-transform-react-jsx-source": "6.9.0", - "babel-plugin-transform-regenerator": "6.16.1", - "babel-plugin-transform-runtime": "6.15.0", - "babel-preset-env": "0.0.8", - "babel-preset-latest": "6.16.0", - "babel-preset-react": "6.16.0", - "babel-runtime": "6.11.6" + "babel-plugin-transform-class-properties": "6.22.0", + "babel-plugin-transform-object-rest-spread": "6.22.0", + "babel-plugin-transform-react-constant-elements": "6.22.0", + "babel-plugin-transform-react-jsx": "6.22.0", + "babel-plugin-transform-react-jsx-self": "6.22.0", + "babel-plugin-transform-react-jsx-source": "6.22.0", + "babel-plugin-transform-regenerator": "6.22.0", + "babel-plugin-transform-runtime": "6.22.0", + "babel-preset-env": "1.2.1", + "babel-preset-react": "6.22.0", + "babel-runtime": "6.22.0" } } diff --git a/packages/create-react-app/README.md b/packages/create-react-app/README.md index 062a320250d..c8fed06489f 100644 --- a/packages/create-react-app/README.md +++ b/packages/create-react-app/README.md @@ -1,6 +1,6 @@ # create-react-app -This package includes the global command for [Create React App](https://github.com/facebookincubator/create-react-app). +This package includes the global command for [Create React App](https://github.com/facebookincubator/create-react-app).<br> Please refer to its documentation: * [Getting Started](https://github.com/facebookincubator/create-react-app/blob/master/README.md#getting-started) – How to create a new app. diff --git a/packages/create-react-app/index.js b/packages/create-react-app/index.js old mode 100644 new mode 100755 index 4dbb37f4969..f2f17142149 --- a/packages/create-react-app/index.js +++ b/packages/create-react-app/index.js @@ -39,8 +39,9 @@ 'use strict'; var chalk = require('chalk'); +var validateProjectName = require("validate-npm-package-name"); -var currentNodeVersion = process.versions.node +var currentNodeVersion = process.versions.node; if (currentNodeVersion.split('.')[0] < 4) { console.error( chalk.red( @@ -58,6 +59,10 @@ var path = require('path'); var execSync = require('child_process').execSync; var spawn = require('cross-spawn'); var semver = require('semver'); +var dns = require('dns'); +var tmp = require('tmp'); +var unpack = require('tar-pack').unpack; +var hyperquest = require('hyperquest'); var projectName; @@ -97,6 +102,14 @@ if (typeof projectName === 'undefined') { process.exit(1); } +function printValidationResults(results) { + if (typeof results !== 'undefined') { + results.forEach(function (error) { + console.error(chalk.red(' * ' + error)); + }); + } +} + var hiddenProgram = new commander.Command() .option('--internal-testing-template <path-to-template>', '(internal usage only, DO NOT RELY ON THIS) ' + 'use a non-standard application template') @@ -124,7 +137,7 @@ function createApp(name, verbose, version, template) { var packageJson = { name: appName, version: '0.1.0', - private: true, + private: true }; fs.writeFileSync( path.join(root, 'package.json'), @@ -133,10 +146,6 @@ function createApp(name, verbose, version, template) { var originalDirectory = process.cwd(); process.chdir(root); - console.log('Installing packages. This might take a couple minutes.'); - console.log('Installing ' + chalk.cyan('react-scripts') + '...'); - console.log(); - run(root, appName, version, verbose, originalDirectory, template); } @@ -149,49 +158,133 @@ function shouldUseYarn() { } } -function install(packageToInstall, verbose, callback) { - var command; - var args; - if (shouldUseYarn()) { - command = 'yarnpkg'; - args = [ 'add', '--dev', '--exact', packageToInstall]; - } else { - command = 'npm'; - args = ['install', '--save-dev', '--save-exact', packageToInstall]; - } +function install(useYarn, dependencies, verbose, isOnline) { + return new Promise(function(resolve, reject) { + var command; + var args; + if (useYarn) { + command = 'yarnpkg'; + args = [ + 'add', + '--exact', + ]; + if (!isOnline) { + args.push('--offline'); + } + [].push.apply(args, dependencies); + + if (!isOnline) { + console.log(chalk.yellow('You appear to be offline.')); + console.log(chalk.yellow('Falling back to the local Yarn cache.')); + console.log(); + } + + } else { + checkNpmVersion(); + command = 'npm'; + args = ['install', '--save', '--save-exact'].concat(dependencies); + } - if (verbose) { - args.push('--verbose'); - } + if (verbose) { + args.push('--verbose'); + } - var child = spawn(command, args, {stdio: 'inherit'}); - child.on('close', function(code) { - callback(code, command, args); + var child = spawn(command, args, {stdio: 'inherit'}); + child.on('close', function(code) { + if (code !== 0) { + reject({ + command: command + ' ' + args.join(' ') + }); + return; + } + resolve(); + }); }); } function run(root, appName, version, verbose, originalDirectory, template) { var packageToInstall = getInstallPackage(version); - var packageName = getPackageName(packageToInstall); - - install(packageToInstall, verbose, function(code, command, args) { - if (code !== 0) { - console.error(chalk.cyan(command + ' ' + args.join(' ')) + ' failed'); - process.exit(1); - } + var allDependencies = ['react', 'react-dom', packageToInstall]; - checkNodeVersion(packageName); + console.log('Installing packages. This might take a couple minutes.'); - var scriptsPath = path.resolve( - process.cwd(), - 'node_modules', - packageName, - 'scripts', - 'init.js' - ); - var init = require(scriptsPath); - init(root, appName, verbose, originalDirectory, template); - }); + var useYarn = shouldUseYarn(); + getPackageName(packageToInstall) + .then(function(packageName) { + return checkIfOnline(useYarn).then(function(isOnline) { + return { + isOnline: isOnline, + packageName: packageName, + }; + }); + }) + .then(function(info) { + var isOnline = info.isOnline; + var packageName = info.packageName; + console.log( + 'Installing ' + chalk.cyan('react') + ', ' + chalk.cyan('react-dom') + + ', and ' + chalk.cyan(packageName) + '...' + ); + console.log(); + + return install(useYarn, allDependencies, verbose, isOnline).then(function() { + return packageName; + }); + }) + .then(function(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); + + var scriptsPath = path.resolve( + process.cwd(), + 'node_modules', + packageName, + 'scripts', + 'init.js' + ); + var init = require(scriptsPath); + init(root, appName, verbose, originalDirectory, template); + }) + .catch(function(reason) { + console.log(); + console.log('Aborting installation.'); + if (reason.command) { + console.log(' ' + chalk.cyan(reason.command), 'has failed.') + } else { + console.log(chalk.red('Unexpected error. Please report it as a bug:')); + console.log(reason); + } + console.log(); + + // On 'exit' we will delete these files from target directory. + var knownGeneratedFiles = [ + 'package.json', 'npm-debug.log', 'yarn-error.log', 'yarn-debug.log', 'node_modules' + ]; + var currentFiles = fs.readdirSync(path.join(root)); + currentFiles.forEach(function (file) { + knownGeneratedFiles.forEach(function (fileToMatch) { + // This will catch `(npm-debug|yarn-error|yarn-debug).log*` files + // and the rest of knownGeneratedFiles. + if ((fileToMatch.match(/.log/g) && file.indexOf(fileToMatch) === 0) || file === fileToMatch) { + console.log('Deleting generated file...', chalk.cyan(file)); + fs.removeSync(path.join(root, file)); + } + }); + }); + var remainingFiles = fs.readdirSync(path.join(root)); + if (!remainingFiles.length) { + // Delete target folder if empty + console.log('Deleting', chalk.cyan(appName + '/'), 'from', chalk.cyan(path.resolve(root, '..'))); + process.chdir(path.resolve(root, '..')); + fs.removeSync(path.join(root)); + } + console.log('Done.'); + process.exit(1); + }); } function getInstallPackage(version) { @@ -206,17 +299,96 @@ function getInstallPackage(version) { return packageToInstall; } +function getTemporaryDirectory() { + return new Promise(function(resolve, reject) { + // Unsafe cleanup lets us recursively delete the directory if it contains + // contents; by default it only allows removal if it's empty + tmp.dir({ unsafeCleanup: true }, function(err, tmpdir, callback) { + if (err) { + reject(err); + } else { + resolve({ + tmpdir: tmpdir, + cleanup: function() { + try { + callback(); + } catch (ignored) { + // Callback might throw and fail, since it's a temp directory the + // OS will clean it up eventually... + } + } + }); + } + }); + }); +} + +function extractStream(stream, dest) { + return new Promise(function(resolve, reject) { + stream.pipe(unpack(dest, function(err) { + if (err) { + reject(err); + } else { + resolve(dest); + } + })); + }); +} + // Extract package name from tarball url or path. function getPackageName(installPackage) { if (installPackage.indexOf('.tgz') > -1) { - // The package name could be with or without semver version, e.g. react-scripts-0.2.0-alpha.1.tgz - // However, this function returns package name only without semver version. - return installPackage.match(/^.+\/(.+?)(?:-\d+.+)?\.tgz$/)[1]; + return getTemporaryDirectory().then(function(obj) { + var stream; + if (/^http/.test(installPackage)) { + stream = hyperquest(installPackage); + } else { + stream = fs.createReadStream(installPackage); + } + return extractStream(stream, obj.tmpdir).then(function() { + return obj; + }); + }).then(function(obj) { + var packageName = require(path.join(obj.tmpdir, 'package.json')).name; + obj.cleanup(); + return packageName; + }).catch(function(err) { + // The package name could be with or without semver version, e.g. react-scripts-0.2.0-alpha.1.tgz + // However, this function returns package name only without semver version. + console.log('Could not extract the package name from the archive: ' + err.message); + var assumedProjectName = installPackage.match(/^.+\/(.+?)(?:-\d+.+)?\.tgz$/)[1]; + console.log('Based on the filename, assuming it is "' + chalk.cyan(assumedProjectName) + '"'); + return Promise.resolve(assumedProjectName); + }); + } else if (installPackage.indexOf('git+') === 0) { + // Pull package name out of git urls e.g: + // git+https://github.com/mycompany/react-scripts.git + // git+ssh://github.com/mycompany/react-scripts.git#v1.2.3 + return Promise.resolve(installPackage.match(/([^\/]+)\.git(#.*)?$/)[1]); } else if (installPackage.indexOf('@') > 0) { // Do not match @scope/ when stripping off @version or @tag - return installPackage.charAt(0) + installPackage.substr(1).split('@')[0]; + return Promise.resolve(installPackage.charAt(0) + installPackage.substr(1).split('@')[0]); } - return installPackage; + return Promise.resolve(installPackage); +} + +function checkNpmVersion() { + var isNpm2 = false; + try { + var npmVersion = execSync('npm --version').toString(); + isNpm2 = semver.lt(npmVersion, '3.0.0'); + } catch (err) { + return; + } + if (!isNpm2) { + return; + } + console.log(chalk.yellow('It looks like you are using npm 2.')); + console.log(chalk.yellow( + 'We suggest using npm 3 or Yarn for faster install times ' + + 'and less disk space usage.' + )); + console.log(); } function checkNodeVersion(packageName) { @@ -246,11 +418,18 @@ function checkNodeVersion(packageName) { } function checkAppName(appName) { + var validationResult = validateProjectName(appName); + if (!validationResult.validForNewPackages) { + console.error('Could not create a project called ' + chalk.red('"' + appName + '"') + ' because of npm naming restrictions:'); + printValidationResults(validationResult.errors); + printValidationResults(validationResult.warnings); + process.exit(1); + } + // TODO: there should be a single place that holds the dependencies var dependencies = ['react', 'react-dom']; var devDependencies = ['react-scripts']; var allDependencies = dependencies.concat(devDependencies).sort(); - if (allDependencies.indexOf(appName) >= 0) { console.error( chalk.red( @@ -268,15 +447,81 @@ function checkAppName(appName) { } } +function makeCaretRange(dependencies, name) { + var version = dependencies[name]; + + if (typeof version === 'undefined') { + console.error( + chalk.red('Missing ' + name + ' dependency in package.json') + ); + process.exit(1); + } + + var patchedVersion = '^' + version; + + if (!semver.validRange(patchedVersion)) { + console.error( + 'Unable to patch ' + name + ' dependency version because version ' + chalk.red(version) + ' will become invalid ' + chalk.red(patchedVersion) + ); + patchedVersion = version; + } + + dependencies[name] = patchedVersion; +} + +function fixDependencies(packageName) { + var packagePath = path.join(process.cwd(), 'package.json'); + var packageJson = require(packagePath); + + if (typeof packageJson.dependencies === 'undefined') { + console.error( + chalk.red('Missing dependencies in package.json') + ); + process.exit(1); + } + + var 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'); + + fs.writeFileSync(packagePath, JSON.stringify(packageJson, null, 2)); +} + // If project only contains files generated by GH, it’s safe. // We also special case IJ-based products .idea because it integrates with CRA: // https://github.com/facebookincubator/create-react-app/pull/368#issuecomment-243446094 function isSafeToCreateProjectIn(root) { var validFiles = [ - '.DS_Store', 'Thumbs.db', '.git', '.gitignore', '.idea', 'README.md', 'LICENSE' + '.DS_Store', 'Thumbs.db', '.git', '.gitignore', '.idea', 'README.md', 'LICENSE', 'web.iml', '.hg', '.hgignore', '.hgcheck' ]; return fs.readdirSync(root) .every(function(file) { return validFiles.indexOf(file) >= 0; }); } + +function checkIfOnline(useYarn) { + if (!useYarn) { + // Don't ping the Yarn registry. + // We'll just assume the best case. + return Promise.resolve(true); + } + + return new Promise(function(resolve) { + dns.lookup('registry.yarnpkg.com', function(err) { + resolve(err === null); + }); + }); +} diff --git a/packages/create-react-app/package.json b/packages/create-react-app/package.json index 0ccb85e786b..4450add254c 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.0.3", + "version": "1.3.0", "keywords": [ "react" ], @@ -24,6 +24,10 @@ "commander": "^2.9.0", "cross-spawn": "^4.0.0", "fs-extra": "^1.0.0", - "semver": "^5.0.3" + "hyperquest": "^2.1.2", + "semver": "^5.0.3", + "tar-pack": "^3.4.0", + "tmp": "0.0.31", + "validate-npm-package-name": "^3.0.0" } } diff --git a/packages/eslint-config-react-app/README.md b/packages/eslint-config-react-app/README.md index 5c20f50ca2e..8eace6efffc 100644 --- a/packages/eslint-config-react-app/README.md +++ b/packages/eslint-config-react-app/README.md @@ -1,6 +1,6 @@ # eslint-config-react-app -This package includes the shareable ESLint configuration used by [Create React App](https://github.com/facebookincubator/create-react-app). +This package includes the shareable ESLint configuration used by [Create React App](https://github.com/facebookincubator/create-react-app).<br> Please refer to its documentation: * [Getting Started](https://github.com/facebookincubator/create-react-app/blob/master/README.md#getting-started) – How to create a new app. @@ -17,7 +17,7 @@ If you want to use this ESLint configuration in a project not built with Create First, install this package, ESLint and the necessary plugins. ```sh - npm install --save-dev eslint-config-react-app babel-eslint@7.0.0 eslint@3.8.1 eslint-plugin-flowtype@2.21.0 eslint-plugin-import@2.0.1 eslint-plugin-jsx-a11y@2.2.3 eslint-plugin-react@6.4.1 + npm install --save-dev eslint-config-react-app babel-eslint@7.1.1 eslint@3.16.1 eslint-plugin-flowtype@2.21.0 eslint-plugin-import@2.0.1 eslint-plugin-jsx-a11y@4.0.0 eslint-plugin-react@6.4.1 ``` Then create a file named `.eslintrc` with following contents in the root folder of your project: diff --git a/packages/eslint-config-react-app/index.js b/packages/eslint-config-react-app/index.js index d93478bc8c9..b69e6d88c64 100644 --- a/packages/eslint-config-react-app/index.js +++ b/packages/eslint-config-react-app/index.js @@ -7,6 +7,8 @@ * of patent rights can be found in the PATENTS file in the same directory. */ +'use strict'; + // Inspired by https://github.com/airbnb/javascript but less opinionated. // We use eslint-loader so even warnings are very visible. @@ -59,7 +61,6 @@ module.exports = { 'default-case': ['warn', { commentPattern: '^no default$' }], 'dot-location': ['warn', 'property'], eqeqeq: ['warn', 'allow-null'], - 'guard-for-in': 'warn', 'new-parens': 'warn', 'no-array-constructor': 'warn', 'no-caller': 'warn', @@ -84,7 +85,7 @@ module.exports = { 'no-invalid-regexp': 'warn', 'no-iterator': 'warn', 'no-label-var': 'warn', - 'no-labels': ['warn', { allowLoop: false, allowSwitch: false }], + 'no-labels': ['warn', { allowLoop: true, allowSwitch: false }], 'no-lone-blocks': 'warn', 'no-loop-func': 'warn', 'no-mixed-operators': ['warn', { @@ -110,7 +111,6 @@ module.exports = { 'no-regex-spaces': 'warn', 'no-restricted-syntax': [ 'warn', - 'LabeledStatement', 'WithStatement', ], 'no-script-url': 'warn', @@ -133,7 +133,8 @@ module.exports = { 'no-unused-vars': ['warn', { vars: 'local', varsIgnorePattern: '^_', - args: 'none' + args: 'none', + ignoreRestSiblings: true, }], 'no-use-before-define': ['warn', 'nofunc'], 'no-useless-computed-key': 'warn', diff --git a/packages/eslint-config-react-app/package.json b/packages/eslint-config-react-app/package.json index 66e690e90f2..c43a970efb7 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": "0.5.0", + "version": "0.6.2", "description": "ESLint configuration used by Create React App", "repository": "facebookincubator/create-react-app", "license": "BSD-3-Clause", @@ -12,10 +12,10 @@ ], "peerDependencies": { "babel-eslint": "^7.0.0", - "eslint": "^3.8.1", + "eslint": "^3.16.1", "eslint-plugin-flowtype": "^2.21.0", "eslint-plugin-import": "^2.0.1", - "eslint-plugin-jsx-a11y": "^2.2.3", + "eslint-plugin-jsx-a11y": "^2.0.0 || ^3.0.0 || ^4.0.0", "eslint-plugin-react": "^6.4.1" } } diff --git a/packages/react-dev-utils/FileSizeReporter.js b/packages/react-dev-utils/FileSizeReporter.js new file mode 100644 index 00000000000..08273673f92 --- /dev/null +++ b/packages/react-dev-utils/FileSizeReporter.js @@ -0,0 +1,109 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +'use strict'; + +var fs = require('fs'); +var path = require('path'); +var chalk = require('chalk'); +var filesize = require('filesize'); +var recursive = require('recursive-readdir'); +var stripAnsi = require('strip-ansi'); +var gzipSize = require('gzip-size').sync; + +// Prints a detailed summary of build files. +function printFileSizesAfterBuild(webpackStats, previousSizeMap) { + var root = previousSizeMap.root; + var sizes = previousSizeMap.sizes; + var assets = webpackStats + .toJson() + .assets.filter(asset => /\.(js|css)$/.test(asset.name)) + .map(asset => { + var fileContents = fs.readFileSync(path.join(root, asset.name)); + var size = gzipSize(fileContents); + var previousSize = sizes[removeFileNameHash(root, asset.name)]; + var difference = getDifferenceLabel(size, previousSize); + return { + folder: path.join('build', path.dirname(asset.name)), + name: path.basename(asset.name), + size: size, + sizeLabel: filesize(size) + (difference ? ' (' + difference + ')' : '') + }; + }); + assets.sort((a, b) => b.size - a.size); + var longestSizeLabelLength = Math.max.apply( + null, + assets.map(a => stripAnsi(a.sizeLabel).length) + ); + assets.forEach(asset => { + var sizeLabel = asset.sizeLabel; + var sizeLength = stripAnsi(sizeLabel).length; + if (sizeLength < longestSizeLabelLength) { + var rightPadding = ' '.repeat(longestSizeLabelLength - sizeLength); + sizeLabel += rightPadding; + } + console.log( + ' ' + + sizeLabel + + ' ' + + chalk.dim(asset.folder + path.sep) + + chalk.cyan(asset.name) + ); + }); +} + +function removeFileNameHash(buildFolder, fileName) { + return fileName + .replace(buildFolder, '') + .replace(/\/?(.*)(\.\w+)(\.js|\.css)/, (match, p1, p2, p3) => p1 + p3); +} + +// Input: 1024, 2048 +// Output: "(+1 KB)" +function getDifferenceLabel(currentSize, previousSize) { + var FIFTY_KILOBYTES = 1024 * 50; + var difference = currentSize - previousSize; + var fileSize = !Number.isNaN(difference) ? filesize(difference) : 0; + if (difference >= FIFTY_KILOBYTES) { + return chalk.red('+' + fileSize); + } else if (difference < FIFTY_KILOBYTES && difference > 0) { + return chalk.yellow('+' + fileSize); + } else if (difference < 0) { + return chalk.green(fileSize); + } else { + return ''; + } +} + +function measureFileSizesBeforeBuild(buildFolder) { + return new Promise(resolve => { + recursive(buildFolder, (err, fileNames) => { + var sizes; + 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; + }, {}); + } + resolve({ + root: buildFolder, + sizes: sizes || {}, + }); + }); + }); +} + +module.exports = { + measureFileSizesBeforeBuild: measureFileSizesBeforeBuild, + printFileSizesAfterBuild: printFileSizesAfterBuild, +}; diff --git a/packages/react-dev-utils/README.md b/packages/react-dev-utils/README.md index 455996fcc59..26d762c3597 100644 --- a/packages/react-dev-utils/README.md +++ b/packages/react-dev-utils/README.md @@ -1,6 +1,6 @@ # react-dev-utils -This package includes some utilities used by [Create React App](https://github.com/facebookincubator/create-react-app). +This package includes some utilities used by [Create React App](https://github.com/facebookincubator/create-react-app).<br> Please refer to its documentation: * [Getting Started](https://github.com/facebookincubator/create-react-app/blob/master/README.md#getting-started) – How to create a new app. @@ -20,7 +20,7 @@ There is no single entry point. You can only import individual top-level modules #### `new InterpolateHtmlPlugin(replacements: {[key:string]: string})` -This Webpack plugin lets us interpolate custom variables into `index.html`. +This Webpack plugin lets us interpolate custom variables into `index.html`.<br> It works in tandem with [HtmlWebpackPlugin](https://github.com/ampedandwired/html-webpack-plugin) 2.x via its [events](https://github.com/ampedandwired/html-webpack-plugin#events). ```js @@ -58,8 +58,8 @@ module.exports = { #### `new WatchMissingNodeModulesPlugin(nodeModulesPath: string)` -This Webpack plugin ensures `npm install <library>` forces a project rebuild. -We’re not sure why this isn't Webpack's default behavior. +This Webpack plugin ensures `npm install <library>` forces a project rebuild.<br> +We’re not sure why this isn't Webpack's default behavior.<br> See [#186](https://github.com/facebookincubator/create-react-app/issues/186) for details. ```js @@ -83,8 +83,8 @@ module.exports = { #### `checkRequiredFiles(files: Array<string>): boolean` -Makes sure that all passed files exist. -Filenames are expected to be absolute. +Makes sure that all passed files exist.<br> +Filenames are expected to be absolute.<br> If a file is not found, prints a warning message and returns `false`. ```js @@ -110,6 +110,29 @@ clearConsole(); console.log('Just cleared the screen!'); ``` +#### `FileSizeReporter` + +##### `measureFileSizesBeforeBuild(buildFolder: string): Promise<OpaqueFileSizes>` + +Captures JS and CSS asset sizes inside the passed `buildFolder`. Save the result value to compare it after the build. + +##### `printFileSizesAfterBuild(webpackStats: WebpackStats, previousFileSizes: OpaqueFileSizes)` + +Prints the JS and CSS asset sizes after the build, and includes a size comparison with `previousFileSizes` that were captured earlier using `measureFileSizesBeforeBuild()`. + +```js +var { + measureFileSizesBeforeBuild, + printFileSizesAfterBuild, +} = require('react-dev-utils/FileSizeReporter'); + +measureFileSizesBeforeBuild(buildFolder).then(previousFileSizes => { + return cleanAndRebuild().then(webpackStats => { + printFileSizesAfterBuild(webpackStats, previousFileSizes); + }); +}); +``` + #### `formatWebpackMessages({errors: Array<string>, warnings: Array<string>}): {errors: Array<string>, warnings: Array<string>}` Extracts and prettifies warning and error messages from webpack [stats](https://github.com/webpack/docs/wiki/node.js-api#stats) object. @@ -161,8 +184,8 @@ getProcessForPort(3000); #### `openBrowser(url: string): boolean` -Attempts to open the browser with a given URL. -On Mac OS X, attempts to reuse an existing Chrome tab via AppleScript. +Attempts to open the browser with a given URL.<br> +On Mac OS X, attempts to reuse an existing Chrome tab via AppleScript.<br> Otherwise, falls back to [opn](https://github.com/sindresorhus/opn) behavior. @@ -179,8 +202,8 @@ if (openBrowser('http://localhost:3000')) { This function displays a console prompt to the user. -By convention, "no" should be the conservative choice. -If you mistype the answer, we'll always take it as a "no". +By convention, "no" should be the conservative choice.<br> +If you mistype the answer, we'll always take it as a "no".<br> You can control the behavior on `<Enter>` with `isYesDefault`. ```js diff --git a/packages/react-dev-utils/checkRequiredFiles.js b/packages/react-dev-utils/checkRequiredFiles.js index 69233db4837..55139b0b870 100644 --- a/packages/react-dev-utils/checkRequiredFiles.js +++ b/packages/react-dev-utils/checkRequiredFiles.js @@ -7,6 +7,8 @@ * of patent rights can be found in the PATENTS file in the same directory. */ +'use strict'; + var fs = require('fs'); var path = require('path'); var chalk = require('chalk'); diff --git a/packages/react-dev-utils/clearConsole.js b/packages/react-dev-utils/clearConsole.js index cfd10155167..74336284723 100644 --- a/packages/react-dev-utils/clearConsole.js +++ b/packages/react-dev-utils/clearConsole.js @@ -7,6 +7,8 @@ * of patent rights can be found in the PATENTS file in the same directory. */ +'use strict'; + function clearConsole() { process.stdout.write(process.platform === 'win32' ? '\x1Bc' : '\x1B[2J\x1B[3J\x1B[H'); } diff --git a/packages/react-dev-utils/formatWebpackMessages.js b/packages/react-dev-utils/formatWebpackMessages.js index 88834a9fc62..bfee3ec5b65 100644 --- a/packages/react-dev-utils/formatWebpackMessages.js +++ b/packages/react-dev-utils/formatWebpackMessages.js @@ -7,6 +7,8 @@ * of patent rights can be found in the PATENTS file in the same directory. */ +'use strict'; + // WARNING: this code is untranspiled and is used in browser too. // Please make sure any changes are in ES5 or contribute a Babel compile step. diff --git a/packages/react-dev-utils/getProcessForPort.js b/packages/react-dev-utils/getProcessForPort.js index 5540fbad47a..24b89823eb6 100644 --- a/packages/react-dev-utils/getProcessForPort.js +++ b/packages/react-dev-utils/getProcessForPort.js @@ -1,3 +1,14 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +'use strict'; + var chalk = require('chalk'); var execSync = require('child_process').execSync; var path = require('path'); @@ -43,7 +54,7 @@ function getProcessCommand(processId, processDirectory) { } function getDirectoryOfProcessById(processId) { - return execSync('lsof -p '+ processId + ' | grep cwd | awk \'{print $9}\'', execOptions).trim(); + return execSync('lsof -p '+ processId + ' | awk \'$4=="cwd" {print $9}\'', execOptions).trim(); } function getProcessForPort(port) { diff --git a/packages/react-dev-utils/openBrowser.js b/packages/react-dev-utils/openBrowser.js index a3623515e0a..912c1819bcd 100644 --- a/packages/react-dev-utils/openBrowser.js +++ b/packages/react-dev-utils/openBrowser.js @@ -7,6 +7,8 @@ * of patent rights can be found in the PATENTS file in the same directory. */ +'use strict'; + var execSync = require('child_process').execSync; var opn = require('opn'); @@ -17,7 +19,7 @@ function openBrowser(url) { // Attempt to honor this environment variable. // It is specific to the operating system. // See https://github.com/sindresorhus/opn#app for documentation. - const browser = process.env.BROWSER; + var browser = process.env.BROWSER; // Special case: BROWSER="none" will prevent opening completely. if (browser === 'none') { @@ -50,6 +52,14 @@ function openBrowser(url) { } } + // Another special case: on OS X, check if BROWSER has been set to "open". + // In this case, instead of passing `open` to `opn` (which won't work), + // just ignore it (thus ensuring the intended behavior, i.e. opening the system browser): + // https://github.com/facebookincubator/create-react-app/pull/1690#issuecomment-283518768 + if (process.platform === 'darwin' && browser === 'open') { + browser = undefined; + } + // Fallback to opn // (It will always open new tab) try { diff --git a/packages/react-dev-utils/package.json b/packages/react-dev-utils/package.json index f3062c56d19..d6aa878d28c 100644 --- a/packages/react-dev-utils/package.json +++ b/packages/react-dev-utils/package.json @@ -1,6 +1,6 @@ { "name": "react-dev-utils", - "version": "0.4.2", + "version": "0.5.2", "description": "Webpack utilities used by Create React App", "repository": "facebookincubator/create-react-app", "license": "BSD-3-Clause", @@ -11,23 +11,27 @@ "node": ">=4" }, "files": [ - "clearConsole.js", "checkRequiredFiles.js", + "clearConsole.js", + "FileSizeReporter.js", "formatWebpackMessages.js", "getProcessForPort.js", "InterpolateHtmlPlugin.js", - "openChrome.applescript", "openBrowser.js", + "openChrome.applescript", "prompt.js", "WatchMissingNodeModulesPlugin.js", "webpackHotDevClient.js" ], "dependencies": { - "ansi-html": "0.0.5", + "ansi-html": "0.0.7", "chalk": "1.1.3", "escape-string-regexp": "1.0.5", + "filesize": "3.3.0", + "gzip-size": "3.0.0", "html-entities": "1.2.0", "opn": "4.0.2", + "recursive-readdir": "2.1.1", "sockjs-client": "1.0.1", "strip-ansi": "3.0.1" } diff --git a/packages/react-dev-utils/prompt.js b/packages/react-dev-utils/prompt.js index 4b1faf9bac8..e6e19cf39eb 100644 --- a/packages/react-dev-utils/prompt.js +++ b/packages/react-dev-utils/prompt.js @@ -7,6 +7,8 @@ * of patent rights can be found in the PATENTS file in the same directory. */ +'use strict'; + var rl = require('readline'); // Convention: "no" should be the conservative choice. @@ -37,6 +39,6 @@ function prompt(question, isYesDefault) { return resolve(isYes); }); }); -}; +} module.exports = prompt; diff --git a/packages/react-dev-utils/webpackHotDevClient.js b/packages/react-dev-utils/webpackHotDevClient.js index 7b1768d8fa0..b5ed34669b5 100644 --- a/packages/react-dev-utils/webpackHotDevClient.js +++ b/packages/react-dev-utils/webpackHotDevClient.js @@ -7,6 +7,8 @@ * of patent rights can be found in the PATENTS file in the same directory. */ +'use strict'; + // This alternative WebpackDevServer combines the functionality of: // https://github.com/webpack/webpack-dev-server/blob/webpack-1/client/index.js // https://github.com/webpack/webpack/blob/webpack-1/hot/dev-server.js @@ -319,4 +321,4 @@ function tryApplyUpdates(onHotUpdateSuccess) { } ); } -}; +} diff --git a/packages/react-scripts/README.md b/packages/react-scripts/README.md index 845e546c67c..8004b887004 100644 --- a/packages/react-scripts/README.md +++ b/packages/react-scripts/README.md @@ -1,6 +1,6 @@ # react-scripts -This package includes scripts and configuration used by [Create React App](https://github.com/facebookincubator/create-react-app). +This package includes scripts and configuration used by [Create React App](https://github.com/facebookincubator/create-react-app).<br> Please refer to its documentation: * [Getting Started](https://github.com/facebookincubator/create-react-app/blob/master/README.md#getting-started) – How to create a new app. diff --git a/packages/react-scripts/.babelrc b/packages/react-scripts/babelrc similarity index 100% rename from packages/react-scripts/.babelrc rename to packages/react-scripts/babelrc diff --git a/packages/react-scripts/bin/react-scripts.js b/packages/react-scripts/bin/react-scripts.js index 58381833957..94a799fd89d 100755 --- a/packages/react-scripts/bin/react-scripts.js +++ b/packages/react-scripts/bin/react-scripts.js @@ -1,4 +1,15 @@ #!/usr/bin/env node +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +'use strict'; + var spawn = require('cross-spawn'); var script = process.argv[2]; var args = process.argv.slice(3); @@ -13,10 +24,27 @@ case 'test': [require.resolve('../scripts/' + script)].concat(args), {stdio: 'inherit'} ); + if (result.signal) { + if (result.signal === 'SIGKILL') { + console.log( + '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.' + ); + } else if (result.signal === 'SIGTERM') { + console.log( + 'The build failed because the process exited too early. ' + + 'Someone might have called `kill` or `killall`, or the system could ' + + 'be shutting down.' + ); + } + process.exit(1); + } process.exit(result.status); break; default: console.log('Unknown script "' + script + '".'); console.log('Perhaps you need to update react-scripts?'); + console.log('See: https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#updating-to-new-releases'); break; } diff --git a/packages/react-scripts/config/env.js b/packages/react-scripts/config/env.js index 66ba341b358..5f49ffaa17a 100644 --- a/packages/react-scripts/config/env.js +++ b/packages/react-scripts/config/env.js @@ -8,6 +8,7 @@ * of patent rights can be found in the PATENTS file in the same directory. */ // @remove-on-eject-end +'use strict'; // Grab NODE_ENV and REACT_APP_* environment variables and prepare them to be // injected into the application via DefinePlugin in Webpack configuration. @@ -15,25 +16,33 @@ var REACT_APP = /^REACT_APP_/i; function getClientEnvironment(publicUrl) { - var processEnv = Object + var raw = Object .keys(process.env) .filter(key => REACT_APP.test(key)) .reduce((env, key) => { - env[key] = JSON.stringify(process.env[key]); + env[key] = process.env[key]; return env; }, { // Useful for determining whether we’re running in production mode. // Most importantly, it switches React into the correct mode. - 'NODE_ENV': JSON.stringify( - process.env.NODE_ENV || 'development' - ), + 'NODE_ENV': process.env.NODE_ENV || 'development', // Useful for resolving the correct path to static assets in `public`. // For example, <img src={process.env.PUBLIC_URL + '/img/logo.png'} />. // This should only be used as an escape hatch. Normally you would put // images into the `src` and `import` them in code to get their paths. - 'PUBLIC_URL': JSON.stringify(publicUrl) + 'PUBLIC_URL': publicUrl }); - return {'process.env': processEnv}; + // Stringify all values so we can feed into Webpack DefinePlugin + var stringified = { + 'process.env': Object + .keys(raw) + .reduce((env, key) => { + env[key] = JSON.stringify(raw[key]); + return env; + }, {}) + }; + + return { raw, stringified }; } module.exports = getClientEnvironment; diff --git a/packages/react-scripts/config/jest/babelTransform.js b/packages/react-scripts/config/jest/babelTransform.js index 145bd86cc9a..0eb6656221e 100644 --- a/packages/react-scripts/config/jest/babelTransform.js +++ b/packages/react-scripts/config/jest/babelTransform.js @@ -6,6 +6,8 @@ * of patent rights can be found in the PATENTS file in the same directory. */ +'use strict'; + const babelJest = require('babel-jest'); module.exports = babelJest.createTransformer({ diff --git a/packages/react-scripts/config/jest/cssTransform.js b/packages/react-scripts/config/jest/cssTransform.js index eead954406b..0a9849f1f54 100644 --- a/packages/react-scripts/config/jest/cssTransform.js +++ b/packages/react-scripts/config/jest/cssTransform.js @@ -7,6 +7,7 @@ * of patent rights can be found in the PATENTS file in the same directory. */ // @remove-on-eject-end +'use strict'; // This is a custom Jest transformer turning style imports into empty objects. // http://facebook.github.io/jest/docs/tutorial-webpack.html @@ -15,7 +16,7 @@ module.exports = { process() { return 'module.exports = {};'; }, - getCacheKey(fileData, filename) { + getCacheKey() { // The output is always the same. return 'cssTransform'; }, diff --git a/packages/react-scripts/config/jest/fileTransform.js b/packages/react-scripts/config/jest/fileTransform.js index 82c672bebd3..060ee259de2 100644 --- a/packages/react-scripts/config/jest/fileTransform.js +++ b/packages/react-scripts/config/jest/fileTransform.js @@ -7,6 +7,7 @@ * of patent rights can be found in the PATENTS file in the same directory. */ // @remove-on-eject-end +'use strict'; const path = require('path'); diff --git a/packages/react-scripts/config/paths.js b/packages/react-scripts/config/paths.js index 2f10ea2fb8a..83f234d6c62 100644 --- a/packages/react-scripts/config/paths.js +++ b/packages/react-scripts/config/paths.js @@ -8,9 +8,11 @@ * of patent rights can be found in the PATENTS file in the same directory. */ // @remove-on-eject-end +'use strict'; var path = require('path'); var fs = require('fs'); +var url = require('url'); // Make sure any symlinks in the project folder are resolved: // https://github.com/facebookincubator/create-react-app/issues/637 @@ -40,6 +42,37 @@ var nodePaths = (process.env.NODE_PATH || '') .filter(folder => !path.isAbsolute(folder)) .map(resolveApp); +var envPublicUrl = process.env.PUBLIC_URL; + +function ensureSlash(path, needsSlash) { + var hasSlash = path.endsWith('/'); + if (hasSlash && !needsSlash) { + return path.substr(path, path.length - 1); + } else if (!hasSlash && needsSlash) { + return path + '/'; + } else { + return path; + } +} + +function getPublicUrl(appPackageJson) { + return envPublicUrl || require(appPackageJson).homepage; +} + +// We use `PUBLIC_URL` environment variable or "homepage" field to infer +// "public path" at which the app is served. +// Webpack needs to know it to put the right <script> hrefs into HTML even in +// single-page apps that may serve index.html for nested URLs like /todos/42. +// We can't use a relative path in HTML because we don't want to load something +// like /todos/42/static/js/bundle.7289d.js. We have to know the root. +function getServedPath(appPackageJson) { + var publicUrl = getPublicUrl(appPackageJson); + var servedUrl = envPublicUrl || ( + publicUrl ? url.parse(publicUrl).pathname : '/' + ); + return ensureSlash(servedUrl, true); +} + // config after eject: we're in ./config/ module.exports = { appBuild: resolveApp('build'), @@ -51,17 +84,19 @@ module.exports = { yarnLockFile: resolveApp('yarn.lock'), testsSetup: resolveApp('src/setupTests.js'), appNodeModules: resolveApp('node_modules'), - ownNodeModules: resolveApp('node_modules'), - nodePaths: nodePaths + nodePaths: nodePaths, + publicUrl: getPublicUrl(resolveApp('package.json')), + servedPath: getServedPath(resolveApp('package.json')) }; // @remove-on-eject-begin function resolveOwn(relativePath) { - return path.resolve(__dirname, relativePath); + return path.resolve(__dirname, '..', relativePath); } // config before eject: we're in ./node_modules/react-scripts/config/ module.exports = { + appPath: resolveApp('.'), appBuild: resolveApp('build'), appPublic: resolveApp('public'), appHtml: resolveApp('public/index.html'), @@ -71,25 +106,37 @@ module.exports = { yarnLockFile: resolveApp('yarn.lock'), testsSetup: resolveApp('src/setupTests.js'), appNodeModules: resolveApp('node_modules'), - // this is empty with npm3 but node resolution searches higher anyway: - ownNodeModules: resolveOwn('../node_modules'), - nodePaths: nodePaths + nodePaths: nodePaths, + publicUrl: getPublicUrl(resolveApp('package.json')), + servedPath: getServedPath(resolveApp('package.json')), + // These properties only exist before ejecting: + ownPath: resolveOwn('.'), + ownNodeModules: resolveOwn('node_modules'), // This is empty on npm 3 }; +var ownPackageJson = require('../package.json'); +var reactScriptsPath = resolveApp(`node_modules/${ownPackageJson.name}`); +var reactScriptsLinked = fs.existsSync(reactScriptsPath) && fs.lstatSync(reactScriptsPath).isSymbolicLink(); + // config before publish: we're in ./packages/react-scripts/config/ -if (__dirname.indexOf(path.join('packages', 'react-scripts', 'config')) !== -1) { +if (!reactScriptsLinked && __dirname.indexOf(path.join('packages', 'react-scripts', 'config')) !== -1) { module.exports = { - appBuild: resolveOwn('../../../build'), - appPublic: resolveOwn('../template/public'), - appHtml: resolveOwn('../template/public/index.html'), - appIndexJs: resolveOwn('../template/src/index.js'), - appPackageJson: resolveOwn('../package.json'), - appSrc: resolveOwn('../template/src'), - yarnLockFile: resolveOwn('../template/yarn.lock'), - testsSetup: resolveOwn('../template/src/setupTests.js'), - appNodeModules: resolveOwn('../node_modules'), - ownNodeModules: resolveOwn('../node_modules'), - nodePaths: nodePaths + appPath: resolveApp('.'), + appBuild: resolveOwn('../../build'), + appPublic: resolveOwn('template/public'), + appHtml: resolveOwn('template/public/index.html'), + appIndexJs: resolveOwn('template/src/index.js'), + appPackageJson: resolveOwn('package.json'), + appSrc: resolveOwn('template/src'), + yarnLockFile: resolveOwn('template/yarn.lock'), + testsSetup: resolveOwn('template/src/setupTests.js'), + appNodeModules: resolveOwn('node_modules'), + nodePaths: nodePaths, + publicUrl: getPublicUrl(resolveOwn('package.json')), + servedPath: getServedPath(resolveOwn('package.json')), + // These properties only exist before ejecting: + ownPath: resolveOwn('.'), + ownNodeModules: resolveOwn('node_modules'), }; } // @remove-on-eject-end diff --git a/packages/react-scripts/config/polyfills.js b/packages/react-scripts/config/polyfills.js index a1353ca3d2d..14031a1688d 100644 --- a/packages/react-scripts/config/polyfills.js +++ b/packages/react-scripts/config/polyfills.js @@ -8,6 +8,7 @@ * of patent rights can be found in the PATENTS file in the same directory. */ // @remove-on-eject-end +'use strict'; if (typeof Promise === 'undefined') { // Rejection tracking prevents a common issue where React gets into an diff --git a/packages/react-scripts/config/webpack.config.dev.js b/packages/react-scripts/config/webpack.config.dev.js index 96fd632b795..83bbf5f996e 100644 --- a/packages/react-scripts/config/webpack.config.dev.js +++ b/packages/react-scripts/config/webpack.config.dev.js @@ -8,6 +8,7 @@ * of patent rights can be found in the PATENTS file in the same directory. */ // @remove-on-eject-end +'use strict'; var autoprefixer = require('autoprefixer'); var webpack = require('webpack'); @@ -81,7 +82,9 @@ module.exports = { // We use `fallback` instead of `root` because we want `node_modules` to "win" // if there any conflicts. This matches Node resolution mechanism. // https://github.com/facebookincubator/create-react-app/issues/253 - fallback: paths.nodePaths, + // We also fallback to the app's node_modules to support hoisted modules in a + // linked package workflow. + fallback: [paths.appNodeModules].concat(paths.nodePaths), // These are the reasonable defaults supported by the Node ecosystem. // We also include JSX as a common component filename extension to support // some tools, although we do not recommend using it, see: @@ -93,14 +96,16 @@ module.exports = { 'react-native': 'react-native-web' } }, - // @remove-on-eject-begin - // Resolve loaders (webpack plugins for CSS, images, transpilation) from the - // directory of `react-scripts` itself rather than the project directory. resolveLoader: { + // @remove-on-eject-begin + // Resolve loaders (webpack plugins for CSS, images, transpilation) from the + // directory of `react-scripts` itself rather than the project directory. root: paths.ownNodeModules, - moduleTemplates: ['*-loader'] + moduleTemplates: ['*-loader'], + // @remove-on-eject-end + // Fallback to any hoisted modules when dealing with linked libraries + fallback: paths.appNodeModules }, - // @remove-on-eject-end module: { // First, run the linter. // It's important to do this before Babel processes the JS. @@ -112,24 +117,24 @@ module.exports = { } ], loaders: [ - // Default loader: load all assets that are not handled - // by other loaders with the url loader. - // Note: This list needs to be updated with every change of extensions - // the other loaders match. - // E.g., when adding a loader for a new supported file extension, - // we need to add the supported extension to this loader too. - // Add one new line in `exclude` for each loader. - // - // "file" loader makes sure those assets get served by WebpackDevServer. - // When you `import` an asset, you get its (virtual) filename. - // In production, they would get copied to the `build` folder. - // "url" loader works like "file" loader except that it embeds assets - // smaller than specified limit in bytes as data URLs to avoid requests. - // A missing `test` is equivalent to a match. + // ** ADDING/UPDATING LOADERS ** + // The "url" loader handles all assets unless explicitly excluded. + // The `exclude` list *must* be updated with every change to loader extensions. + // When adding a new loader, you must add its `test` + // as a new entry in the `exclude` list for "url" loader. + + // "url" loader embeds assets smaller than specified size as data URLs to avoid requests. + // Otherwise, it acts like the "file" loader. { exclude: [ /\.html$/, - /\.(js|jsx)$/, + // We have to write /\.(js|jsx)(\?.*)?$/ rather than just /\.(js|jsx)$/ + // because you might change the hot reloading server from the custom one + // to Webpack's built-in webpack-dev-server/client?/, which would not + // get properly excluded by /\.(js|jsx)$/ because of the query string. + // Webpack 2 fixes this, but for now we include this hack. + // https://github.com/facebookincubator/create-react-app/issues/1713 + /\.(js|jsx)(\?.*)?$/, /\.css$/, /\.json$/, /\.svg$/ @@ -179,13 +184,15 @@ module.exports = { name: 'static/media/[name].[hash:8].[ext]' } } + // ** STOP ** Are you adding a new loader? + // Remember to add the new extension(s) to the "url" loader exclusion list. ] }, // @remove-on-eject-begin // Point ESLint to our predefined config. eslint: { - configFile: path.join(__dirname, '../.eslintrc'), - useEslintrc: false + configFile: path.join(__dirname, '../eslintrc'), + useEslintrc: false, }, // @remove-on-eject-end // We use PostCSS for autoprefixing only. @@ -202,12 +209,11 @@ module.exports = { ]; }, plugins: [ - // Makes the public URL available as %PUBLIC_URL% in index.html, e.g.: + // Makes some environment variables available in index.html. + // The public URL is available as %PUBLIC_URL% in index.html, e.g.: // <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico"> // In development, this will be an empty string. - new InterpolateHtmlPlugin({ - PUBLIC_URL: publicUrl - }), + new InterpolateHtmlPlugin(env.raw), // Generates an `index.html` file with the <script> injected. new HtmlWebpackPlugin({ inject: true, @@ -215,7 +221,7 @@ module.exports = { }), // Makes some environment variables available to the JS code, for example: // if (process.env.NODE_ENV === 'development') { ... }. See `./env.js`. - new webpack.DefinePlugin(env), + new webpack.DefinePlugin(env.stringified), // This is necessary to emit hot updates (currently CSS only): new webpack.HotModuleReplacementPlugin(), // Watcher doesn't work well if you mistype casing in a path so we use diff --git a/packages/react-scripts/config/webpack.config.prod.js b/packages/react-scripts/config/webpack.config.prod.js index 058db0d7921..b840f93a8e5 100644 --- a/packages/react-scripts/config/webpack.config.prod.js +++ b/packages/react-scripts/config/webpack.config.prod.js @@ -8,6 +8,7 @@ * of patent rights can be found in the PATENTS file in the same directory. */ // @remove-on-eject-end +'use strict'; var autoprefixer = require('autoprefixer'); var webpack = require('webpack'); @@ -15,7 +16,6 @@ var HtmlWebpackPlugin = require('html-webpack-plugin'); var ExtractTextPlugin = require('extract-text-webpack-plugin'); var ManifestPlugin = require('webpack-manifest-plugin'); var InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin'); -var url = require('url'); var paths = require('./paths'); var getClientEnvironment = require('./env'); @@ -24,40 +24,37 @@ var getClientEnvironment = require('./env'); var path = require('path'); // @remove-on-eject-end -function ensureSlash(path, needsSlash) { - var hasSlash = path.endsWith('/'); - if (hasSlash && !needsSlash) { - return path.substr(path, path.length - 1); - } else if (!hasSlash && needsSlash) { - return path + '/'; - } else { - return path; - } -} - -// We use "homepage" field to infer "public path" at which the app is served. -// Webpack needs to know it to put the right <script> hrefs into HTML even in -// single-page apps that may serve index.html for nested URLs like /todos/42. -// We can't use a relative path in HTML because we don't want to load something -// like /todos/42/static/js/bundle.7289d.js. We have to know the root. -var homepagePath = require(paths.appPackageJson).homepage; -var homepagePathname = homepagePath ? url.parse(homepagePath).pathname : '/'; // Webpack uses `publicPath` to determine where the app is being served from. // It requires a trailing slash, or the file assets will get an incorrect path. -var publicPath = ensureSlash(homepagePathname, true); +var publicPath = paths.servedPath; +// Some apps do not use client-side routing with pushState. +// For these, "homepage" can be set to "." to enable relative asset paths. +var shouldUseRelativeAssetPaths = publicPath === './'; // `publicUrl` is just like `publicPath`, but we will provide it to our app // as %PUBLIC_URL% in `index.html` and `process.env.PUBLIC_URL` in JavaScript. -// Omit trailing slash as %PUBLIC_PATH%/xyz looks better than %PUBLIC_PATH%xyz. -var publicUrl = ensureSlash(homepagePathname, false); +// Omit trailing slash as %PUBLIC_URL%/xyz looks better than %PUBLIC_URL%xyz. +var publicUrl = publicPath.slice(0, -1); // Get environment variables to inject into our app. var env = getClientEnvironment(publicUrl); // Assert this just to be safe. // Development builds of React are slow and not intended for production. -if (env['process.env'].NODE_ENV !== '"production"') { +if (env.stringified['process.env'].NODE_ENV !== '"production"') { throw new Error('Production builds must have NODE_ENV=production.'); } +// Note: defined here because it will be used more than once. +const cssFilename = 'static/css/[name].[contenthash:8].css'; + +// ExtractTextPlugin expects the build output to be flat. +// (See https://github.com/webpack-contrib/extract-text-webpack-plugin/issues/27) +// However, our output is structured with css, js and media folders. +// To have this structure working with relative paths, we have to use custom options. +const extractTextPluginOptions = shouldUseRelativeAssetPaths + // Making sure that the publicPath goes back to to build folder. + ? { publicPath: Array(cssFilename.split('/').length).join('../') } + : undefined; + // This is the production configuration. // It compiles slowly and is focused on producing a fast and minimal bundle. // The development configuration is different and lives in a separate file. @@ -89,7 +86,9 @@ module.exports = { // We use `fallback` instead of `root` because we want `node_modules` to "win" // if there any conflicts. This matches Node resolution mechanism. // https://github.com/facebookincubator/create-react-app/issues/253 - fallback: paths.nodePaths, + // We also fallback to the app's node_modules to support hoisted modules in a + // linked package workflow. + fallback: [paths.appNodeModules].concat(paths.nodePaths), // These are the reasonable defaults supported by the Node ecosystem. // We also include JSX as a common component filename extension to support // some tools, although we do not recommend using it, see: @@ -101,14 +100,16 @@ module.exports = { 'react-native': 'react-native-web' } }, - // @remove-on-eject-begin - // Resolve loaders (webpack plugins for CSS, images, transpilation) from the - // directory of `react-scripts` itself rather than the project directory. resolveLoader: { + // @remove-on-eject-begin + // Resolve loaders (webpack plugins for CSS, images, transpilation) from the + // directory of `react-scripts` itself rather than the project directory. root: paths.ownNodeModules, - moduleTemplates: ['*-loader'] + moduleTemplates: ['*-loader'], + // @remove-on-eject-end + // Fallback to any hoisted modules when dealing with linked libraries + fallback: paths.appNodeModules }, - // @remove-on-eject-end module: { // First, run the linter. // It's important to do this before Babel processes the JS. @@ -120,18 +121,14 @@ module.exports = { } ], loaders: [ - // Default loader: load all assets that are not handled - // by other loaders with the url loader. - // Note: This list needs to be updated with every change of extensions - // the other loaders match. - // E.g., when adding a loader for a new supported file extension, - // we need to add the supported extension to this loader too. - // Add one new line in `exclude` for each loader. - // - // "file" loader makes sure those assets end up in the `build` folder. - // When you `import` an asset, you get its filename. - // "url" loader works just like "file" loader but it also embeds - // assets smaller than specified size as data URLs to avoid requests. + // ** ADDING/UPDATING LOADERS ** + // The "url" loader handles all assets unless explicitly excluded. + // The `exclude` list *must* be updated with every change to loader extensions. + // When adding a new loader, you must add its `test` + // as a new entry in the `exclude` list in the "url" loader. + + // "url" loader embeds assets smaller than specified size as data URLs to avoid requests. + // Otherwise, it acts like the "file" loader. { exclude: [ /\.html$/, @@ -172,7 +169,11 @@ module.exports = { // in the main CSS file. { test: /\.css$/, - loader: ExtractTextPlugin.extract('style', 'css?importLoaders=1!postcss') + loader: ExtractTextPlugin.extract( + 'style', + 'css?importLoaders=1!postcss', + extractTextPluginOptions + ) // Note: this won't work without `new ExtractTextPlugin()` in `plugins`. }, // JSON is not enabled by default in Webpack but both Node and Browserify @@ -189,6 +190,8 @@ module.exports = { name: 'static/media/[name].[hash:8].[ext]' } } + // ** STOP ** Are you adding a new loader? + // Remember to add the new extension(s) to the "url" loader exclusion list. ] }, // @remove-on-eject-begin @@ -196,7 +199,7 @@ module.exports = { eslint: { // TODO: consider separate config for production, // e.g. to enable no-console and no-debugger only in production. - configFile: path.join(__dirname, '../.eslintrc'), + configFile: path.join(__dirname, '../eslintrc'), useEslintrc: false }, // @remove-on-eject-end @@ -214,13 +217,12 @@ module.exports = { ]; }, plugins: [ - // Makes the public URL available as %PUBLIC_URL% in index.html, e.g.: + // Makes some environment variables available in index.html. + // The public URL is available as %PUBLIC_URL% in index.html, e.g.: // <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico"> // In production, it will be an empty string unless you specify "homepage" // in `package.json`, in which case it will be the pathname of that URL. - new InterpolateHtmlPlugin({ - PUBLIC_URL: publicUrl - }), + new InterpolateHtmlPlugin(env.raw), // Generates an `index.html` file with the <script> injected. new HtmlWebpackPlugin({ inject: true, @@ -242,7 +244,7 @@ module.exports = { // if (process.env.NODE_ENV === 'production') { ... }. See `./env.js`. // It is absolutely essential that NODE_ENV was set to production here. // Otherwise React will be compiled in the very slow development mode. - new webpack.DefinePlugin(env), + new webpack.DefinePlugin(env.stringified), // This helps ensure the builds are consistent if source hasn't changed: new webpack.optimize.OccurrenceOrderPlugin(), // Try to dedupe duplicated modules, if any: @@ -262,7 +264,7 @@ module.exports = { } }), // Note: this won't work without ExtractTextPlugin.extract(..) in `loaders`. - new ExtractTextPlugin('static/css/[name].[contenthash:8].css'), + new ExtractTextPlugin(cssFilename), // Generate a manifest file which contains a mapping of all asset filenames // to their corresponding output file so that tools can pick it up without // having to parse `index.html`. diff --git a/packages/react-scripts/.eslintrc b/packages/react-scripts/eslintrc similarity index 100% rename from packages/react-scripts/.eslintrc rename to packages/react-scripts/eslintrc diff --git a/packages/react-scripts/fixtures/kitchensink/.babelrc b/packages/react-scripts/fixtures/kitchensink/.babelrc index 5686105b953..c14b2828d16 100644 --- a/packages/react-scripts/fixtures/kitchensink/.babelrc +++ b/packages/react-scripts/fixtures/kitchensink/.babelrc @@ -1,3 +1,3 @@ { - "presets": ["latest"] + "presets": ["react-app"] } diff --git a/packages/react-scripts/fixtures/kitchensink/.template.dependencies.json b/packages/react-scripts/fixtures/kitchensink/.template.dependencies.json index 62e0d34a35c..8a54954cf0f 100644 --- a/packages/react-scripts/fixtures/kitchensink/.template.dependencies.json +++ b/packages/react-scripts/fixtures/kitchensink/.template.dependencies.json @@ -1,10 +1,10 @@ { "dependencies": { - "babel-preset-latest": "6.16.0", "babel-register": "6.22.0", "babel-polyfill": "6.20.0", "chai": "3.5.0", "jsdom": "9.8.3", - "mocha": "3.2.0" + "mocha": "3.2.0", + "test-integrity": "1.0.0" } } diff --git a/packages/react-scripts/fixtures/kitchensink/integration/env.test.js b/packages/react-scripts/fixtures/kitchensink/integration/env.test.js index a179aa7cbb2..c3169c987a9 100644 --- a/packages/react-scripts/fixtures/kitchensink/integration/env.test.js +++ b/packages/react-scripts/fixtures/kitchensink/integration/env.test.js @@ -1,24 +1,42 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + import { expect } from 'chai' import initDOM from './initDOM' describe('Integration', () => { describe('Environment variables', () => { + it('file env variables', async () => { + const doc = await initDOM('file-env-variables') + + expect(doc.getElementById('feature-file-env-variables').textContent).to.equal('fromtheenvfile.') + }) + it('NODE_PATH', async () => { const doc = await initDOM('node-path') expect(doc.getElementById('feature-node-path').childElementCount).to.equal(4) }) - it('shell env variables', async () => { - const doc = await initDOM('shell-env-variables') + it('PUBLIC_URL', async () => { + const doc = await initDOM('public-url') - expect(doc.getElementById('feature-shell-env-variables').textContent).to.equal('fromtheshell.') + const prefix = process.env.NODE_ENV === 'development' ? '' : 'http://www.example.org/spa'; + expect(doc.getElementById('feature-public-url').textContent).to.equal(`${prefix}.`) + expect(doc.querySelector('head link[rel="shortcut icon"]').getAttribute('href')) + .to.equal(`${prefix}/favicon.ico`) }) - it('file env variables', async () => { - const doc = await initDOM('file-env-variables') + it('shell env variables', async () => { + const doc = await initDOM('shell-env-variables') - expect(doc.getElementById('feature-file-env-variables').textContent).to.equal('fromtheenvfile.') + expect(doc.getElementById('feature-shell-env-variables').textContent).to.equal('fromtheshell.') }) }) }) diff --git a/packages/react-scripts/fixtures/kitchensink/integration/initDOM.js b/packages/react-scripts/fixtures/kitchensink/integration/initDOM.js index cec02227493..3cfb182148a 100644 --- a/packages/react-scripts/fixtures/kitchensink/integration/initDOM.js +++ b/packages/react-scripts/fixtures/kitchensink/integration/initDOM.js @@ -1,3 +1,12 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + const fs = require('fs') const http = require('http') const jsdom = require('jsdom') @@ -15,9 +24,11 @@ if (process.env.E2E_FILE) { const markup = fs.readFileSync(file, 'utf8') getMarkup = () => markup + const pathPrefix = process.env.PUBLIC_URL.replace(/^https?:\/\/[^/]+\/?/, '') + resourceLoader = (resource, callback) => callback( null, - fs.readFileSync(path.join(path.dirname(file), resource.url.pathname), 'utf8') + fs.readFileSync(path.join(path.dirname(file), resource.url.pathname.replace(pathPrefix, '')), 'utf8') ) } else if (process.env.E2E_URL) { getMarkup = () => new Promise(resolve => { @@ -37,7 +48,7 @@ if (process.env.E2E_FILE) { export default feature => new Promise(async resolve => { const markup = await getMarkup() - const host = process.env.E2E_URL || 'http://localhost:3000' + const host = process.env.E2E_URL || 'http://www.example.org/spa:3000' const doc = jsdom.jsdom(markup, { features: { FetchExternalResources: ['script', 'css'], diff --git a/packages/react-scripts/fixtures/kitchensink/integration/syntax.test.js b/packages/react-scripts/fixtures/kitchensink/integration/syntax.test.js index c8b7fbbc508..8fe1990f20a 100644 --- a/packages/react-scripts/fixtures/kitchensink/integration/syntax.test.js +++ b/packages/react-scripts/fixtures/kitchensink/integration/syntax.test.js @@ -1,3 +1,12 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + import { expect } from 'chai' import initDOM from './initDOM' diff --git a/packages/react-scripts/fixtures/kitchensink/integration/webpack.test.js b/packages/react-scripts/fixtures/kitchensink/integration/webpack.test.js index 9e2826b8fc6..8cf4312c210 100644 --- a/packages/react-scripts/fixtures/kitchensink/integration/webpack.test.js +++ b/packages/react-scripts/fixtures/kitchensink/integration/webpack.test.js @@ -1,3 +1,12 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + import { expect } from 'chai' import initDOM from './initDOM' @@ -29,6 +38,12 @@ describe('Integration', () => { expect(doc.getElementById('feature-json-inclusion').textContent).to.equal('This is an abstract.') }) + it('linked modules', async () => { + const doc = await initDOM('linked-modules') + + expect(doc.getElementById('feature-linked-modules').textContent).to.equal('2.0.0') + }) + it('svg inclusion', async () => { const doc = await initDOM('svg-inclusion') diff --git a/packages/react-scripts/fixtures/kitchensink/src/App.js b/packages/react-scripts/fixtures/kitchensink/src/App.js index cf93b4c132b..dd1b1388c5a 100644 --- a/packages/react-scripts/fixtures/kitchensink/src/App.js +++ b/packages/react-scripts/fixtures/kitchensink/src/App.js @@ -1,28 +1,49 @@ -import React from 'react'; +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ -class BuiltEmitter extends React.Component { - constructor(props) { - super(props) +import React, { Component, PropTypes, createElement } from 'react'; - this.callWhenDone = done => done(); +class BuiltEmitter extends Component { + static propTypes = { + feature: PropTypes.func.isRequired } componentDidMount() { - this.callWhenDone(() => document.dispatchEvent(new Event('ReactFeatureDidMount'))); + const { feature } = this.props; + + // Class components must call this.props.onReady when they're ready for the test. + // We will assume functional components are ready immediately after mounting. + if (!Component.isPrototypeOf(feature)) { + this.handleReady(); + } } - render() { - const feature = React.cloneElement(React.Children.only(this.props.children), { - setCallWhenDone: done => { - this.callWhenDone = done; - } - }); + handleReady() { + document.dispatchEvent(new Event('ReactFeatureDidMount')); + } - return <div>{feature}</div>; + render() { + const { + props: { feature }, + handleReady + } = this; + return ( + <div> + {createElement(feature, { + onReady: handleReady + })} + </div> + ); } } -class App extends React.Component { +class App extends Component { constructor(props) { super(props); @@ -32,7 +53,8 @@ class App extends React.Component { } componentDidMount() { - switch (location.hash.slice(1)) { + const feature = location.hash.slice(1); + switch (feature) { case 'array-destructuring': require.ensure([], () => this.setFeature(require('./features/syntax/ArrayDestructuring').default)); break; @@ -72,6 +94,9 @@ class App extends React.Component { case 'json-inclusion': require.ensure([], () => this.setFeature(require('./features/webpack/JsonInclusion').default)); break; + case 'linked-modules': + require.ensure([], () => this.setFeature(require('./features/webpack/LinkedModules').default)); + break; case 'node-path': require.ensure([], () => this.setFeature(require('./features/env/NodePath').default)); break; @@ -87,6 +112,9 @@ class App extends React.Component { case 'promises': require.ensure([], () => this.setFeature(require('./features/syntax/Promises').default)); break; + case 'public-url': + require.ensure([], () => this.setFeature(require('./features/env/PublicUrl').default)); + break; case 'rest-and-default': require.ensure([], () => this.setFeature(require('./features/syntax/RestAndDefault').default)); break; @@ -105,9 +133,7 @@ class App extends React.Component { case 'unknown-ext-inclusion': require.ensure([], () => this.setFeature(require('./features/webpack/UnknownExtInclusion').default)); break; - default: - this.setFeature(null); - break; + default: throw new Error(`Missing feature "${feature}"`); } } @@ -116,8 +142,11 @@ class App extends React.Component { } render() { - const Feature = this.state.feature; - return Feature ? <BuiltEmitter><Feature /></BuiltEmitter> : null; + const { feature } = this.state; + if (feature !== null) { + return <BuiltEmitter feature={feature} />; + } + return null; } } diff --git a/packages/react-scripts/fixtures/kitchensink/src/App.test.js b/packages/react-scripts/fixtures/kitchensink/src/App.test.js deleted file mode 100644 index b84af98d720..00000000000 --- a/packages/react-scripts/fixtures/kitchensink/src/App.test.js +++ /dev/null @@ -1,8 +0,0 @@ -import React from 'react'; -import ReactDOM from 'react-dom'; -import App from './App'; - -it('renders without crashing', () => { - const div = document.createElement('div'); - ReactDOM.render(<App />, div); -}); diff --git a/packages/react-scripts/fixtures/kitchensink/src/absoluteLoad.js b/packages/react-scripts/fixtures/kitchensink/src/absoluteLoad.js index dc769fe1e53..a1fe21e48ff 100644 --- a/packages/react-scripts/fixtures/kitchensink/src/absoluteLoad.js +++ b/packages/react-scripts/fixtures/kitchensink/src/absoluteLoad.js @@ -1,3 +1,12 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + export default () => [ { id: 1, name: '1' }, { id: 2, name: '2' }, 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 c7b7c5d537d..ff874b62812 100644 --- a/packages/react-scripts/fixtures/kitchensink/src/features/env/FileEnvVariables.js +++ b/packages/react-scripts/fixtures/kitchensink/src/features/env/FileEnvVariables.js @@ -1,3 +1,12 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + import React from 'react' export default () => ( diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/env/FileEnvVariables.test.js b/packages/react-scripts/fixtures/kitchensink/src/features/env/FileEnvVariables.test.js index c9d802be975..5a0fed4c649 100644 --- a/packages/react-scripts/fixtures/kitchensink/src/features/env/FileEnvVariables.test.js +++ b/packages/react-scripts/fixtures/kitchensink/src/features/env/FileEnvVariables.test.js @@ -1,3 +1,12 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + import React from 'react'; import ReactDOM from 'react-dom'; import FileEnvVariables from './FileEnvVariables'; 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 1644b49ca04..e4b868e47b6 100644 --- a/packages/react-scripts/fixtures/kitchensink/src/features/env/NodePath.js +++ b/packages/react-scripts/fixtures/kitchensink/src/features/env/NodePath.js @@ -1,21 +1,32 @@ -import React from 'react' +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +import React, { Component, PropTypes } from 'react' import load from 'absoluteLoad' -export default class extends React.Component { +export default class extends Component { + static propTypes = { + onReady: PropTypes.func.isRequired + } + constructor(props) { super(props); - - this.done = () => {}; - this.props.setCallWhenDone && this.props.setCallWhenDone((done) => { - this.done = done; - }); - this.state = { users: [] }; } async componentDidMount() { const users = load(); - this.setState({ users }, () => this.done()); + this.setState({ users }); + } + + componentDidUpdate() { + this.props.onReady(); } render() { diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/env/NodePath.test.js b/packages/react-scripts/fixtures/kitchensink/src/features/env/NodePath.test.js index 05b981853b9..7e076dade75 100644 --- a/packages/react-scripts/fixtures/kitchensink/src/features/env/NodePath.test.js +++ b/packages/react-scripts/fixtures/kitchensink/src/features/env/NodePath.test.js @@ -1,3 +1,12 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + import React from 'react'; import ReactDOM from 'react-dom'; import NodePath from './NodePath'; @@ -5,6 +14,8 @@ import NodePath from './NodePath'; describe('NODE_PATH', () => { it('renders without crashing', () => { const div = document.createElement('div'); - ReactDOM.render(<NodePath />, div); + return new Promise(resolve => { + ReactDOM.render(<NodePath onReady={resolve} />, 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 new file mode 100644 index 00000000000..4c61be73956 --- /dev/null +++ b/packages/react-scripts/fixtures/kitchensink/src/features/env/PublicUrl.js @@ -0,0 +1,14 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +import React from 'react' + +export default () => ( + <span id="feature-public-url">{process.env.PUBLIC_URL}.</span> +) diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/env/PublicUrl.test.js b/packages/react-scripts/fixtures/kitchensink/src/features/env/PublicUrl.test.js new file mode 100644 index 00000000000..c9e1be0e94a --- /dev/null +++ b/packages/react-scripts/fixtures/kitchensink/src/features/env/PublicUrl.test.js @@ -0,0 +1,19 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +import React from 'react'; +import ReactDOM from 'react-dom'; +import PublicUrl from './PublicUrl'; + +describe('PUBLIC_URL', () => { + it('renders without crashing', () => { + const div = document.createElement('div'); + ReactDOM.render(<PublicUrl />, div); + }); +}); 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 37b80ec6097..243bfc00935 100644 --- a/packages/react-scripts/fixtures/kitchensink/src/features/env/ShellEnvVariables.js +++ b/packages/react-scripts/fixtures/kitchensink/src/features/env/ShellEnvVariables.js @@ -1,3 +1,12 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + import React from 'react' export default () => ( diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/env/ShellEnvVariables.test.js b/packages/react-scripts/fixtures/kitchensink/src/features/env/ShellEnvVariables.test.js index 981bee67c26..13707697591 100644 --- a/packages/react-scripts/fixtures/kitchensink/src/features/env/ShellEnvVariables.test.js +++ b/packages/react-scripts/fixtures/kitchensink/src/features/env/ShellEnvVariables.test.js @@ -1,3 +1,12 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + import React from 'react'; import ReactDOM from 'react-dom'; import ShellEnvVariables from './ShellEnvVariables'; 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 1ee751af71f..48516d43fd3 100644 --- a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ArrayDestructuring.js +++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ArrayDestructuring.js @@ -1,4 +1,13 @@ -import React from 'react' +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +import React, { Component, PropTypes } from 'react' function load() { return [ @@ -9,21 +18,23 @@ function load() { ]; } -export default class extends React.Component { +export default class extends Component { + static propTypes = { + onReady: PropTypes.func.isRequired + } + constructor(props) { super(props); - - this.done = () => {}; - this.props.setCallWhenDone && this.props.setCallWhenDone((done) => { - this.done = done; - }); - this.state = { users: [] }; } async componentDidMount() { const users = load(); - this.setState({ users }, () => this.done()); + this.setState({ users }); + } + + componentDidUpdate() { + this.props.onReady(); } render() { diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ArrayDestructuring.test.js b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ArrayDestructuring.test.js index 617df2a6c6c..42dde0f1c35 100644 --- a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ArrayDestructuring.test.js +++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ArrayDestructuring.test.js @@ -1,3 +1,12 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + import React from 'react'; import ReactDOM from 'react-dom'; import ArrayDestructuring from './ArrayDestructuring'; @@ -5,6 +14,8 @@ import ArrayDestructuring from './ArrayDestructuring'; describe('array destructuring', () => { it('renders without crashing', () => { const div = document.createElement('div'); - ReactDOM.render(<ArrayDestructuring />, div); + return new Promise(resolve => { + ReactDOM.render(<ArrayDestructuring onReady={resolve} />, 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 be6311980d7..9998589a138 100644 --- a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ArraySpread.js +++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ArraySpread.js @@ -1,4 +1,13 @@ -import React from 'react' +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +import React, { Component, PropTypes } from 'react' function load(users) { return [ @@ -9,21 +18,23 @@ function load(users) { ]; } -export default class extends React.Component { +export default class extends Component { + static propTypes = { + onReady: PropTypes.func.isRequired + } + constructor(props) { super(props); - - this.done = () => {}; - this.props.setCallWhenDone && this.props.setCallWhenDone((done) => { - this.done = done; - }); - this.state = { users: [] }; } async componentDidMount() { const users = load([{ id: 42, name: '42' }]); - this.setState({ users }, () => this.done()); + this.setState({ users }); + } + + componentDidUpdate() { + this.props.onReady(); } render() { diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ArraySpread.test.js b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ArraySpread.test.js index 85fade6e3a9..9ed7633ea35 100644 --- a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ArraySpread.test.js +++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ArraySpread.test.js @@ -1,3 +1,12 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + import React from 'react'; import ReactDOM from 'react-dom'; import ArraySpread from './ArraySpread'; @@ -5,6 +14,8 @@ import ArraySpread from './ArraySpread'; describe('array spread', () => { it('renders without crashing', () => { const div = document.createElement('div'); - ReactDOM.render(<ArraySpread />, div); + return new Promise(resolve => { + ReactDOM.render(<ArraySpread onReady={resolve} />, 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 84dd42e0a9b..b5b6013f93c 100644 --- a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/AsyncAwait.js +++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/AsyncAwait.js @@ -1,4 +1,13 @@ -import React from 'react' +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +import React, { Component, PropTypes } from 'react' async function load() { return [ @@ -9,21 +18,23 @@ async function load() { ]; } -export default class extends React.Component { +export default class extends Component { + static propTypes = { + onReady: PropTypes.func.isRequired + } + constructor(props) { super(props); - - this.done = () => {}; - this.props.setCallWhenDone && this.props.setCallWhenDone((done) => { - this.done = done; - }); - this.state = { users: [] }; } async componentDidMount() { const users = await load(); - this.setState({ users }, () => this.done()); + this.setState({ users }); + } + + componentDidUpdate() { + this.props.onReady(); } render() { diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/AsyncAwait.test.js b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/AsyncAwait.test.js index 072f16fffea..02713981799 100644 --- a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/AsyncAwait.test.js +++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/AsyncAwait.test.js @@ -1,3 +1,12 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + import React from 'react'; import ReactDOM from 'react-dom'; import AsyncAwait from './AsyncAwait'; @@ -5,6 +14,8 @@ import AsyncAwait from './AsyncAwait'; describe('async/await', () => { it('renders without crashing', () => { const div = document.createElement('div'); - ReactDOM.render(<AsyncAwait />, div); + return new Promise(resolve => { + ReactDOM.render(<AsyncAwait onReady={resolve} />, 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 65e500d645b..63bd7ab0512 100644 --- a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ClassProperties.js +++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ClassProperties.js @@ -1,6 +1,19 @@ -import React from 'react' +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +import React, { Component, PropTypes } from 'react' + +export default class extends Component { + static propTypes = { + onReady: PropTypes.func.isRequired + } -export default class extends React.Component { users = [ { id: 1, name: '1' }, { id: 2, name: '2' }, @@ -8,6 +21,10 @@ export default class extends React.Component { { id: 4, name: '4' } ]; + componentDidMount() { + this.props.onReady() + } + render() { return ( <div id="feature-class-properties"> diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ClassProperties.test.js b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ClassProperties.test.js index 71d851dd2fc..5b038e76d90 100644 --- a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ClassProperties.test.js +++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ClassProperties.test.js @@ -1,3 +1,12 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + import React from 'react'; import ReactDOM from 'react-dom'; import ClassProperties from './ClassProperties'; @@ -5,6 +14,8 @@ import ClassProperties from './ClassProperties'; describe('class properties', () => { it('renders without crashing', () => { const div = document.createElement('div'); - ReactDOM.render(<ClassProperties />, div); + return new Promise(resolve => { + ReactDOM.render(<ClassProperties onReady={resolve} />, 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 b6111a149d9..e345283eaaa 100644 --- a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ComputedProperties.js +++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ComputedProperties.js @@ -1,4 +1,13 @@ -import React from 'react' +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +import React, { Component, PropTypes } from 'react' function load(prefix) { return [ @@ -9,21 +18,23 @@ function load(prefix) { ]; } -export default class extends React.Component { +export default class extends Component { + static propTypes = { + onReady: PropTypes.func.isRequired + } + constructor(props) { super(props); - - this.done = () => {}; - this.props.setCallWhenDone && this.props.setCallWhenDone((done) => { - this.done = done; - }); - this.state = { users: [] }; } async componentDidMount() { const users = load('user_'); - this.setState({ users }, () => this.done()); + this.setState({ users }); + } + + componentDidUpdate() { + this.props.onReady(); } render() { diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ComputedProperties.test.js b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ComputedProperties.test.js index 4e9aaf17a1d..91b697fe0a0 100644 --- a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ComputedProperties.test.js +++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ComputedProperties.test.js @@ -1,3 +1,12 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + import React from 'react'; import ReactDOM from 'react-dom'; import ComputedProperties from './ComputedProperties'; @@ -5,6 +14,8 @@ import ComputedProperties from './ComputedProperties'; describe('computed properties', () => { it('renders without crashing', () => { const div = document.createElement('div'); - ReactDOM.render(<ComputedProperties />, div); + return new Promise(resolve => { + ReactDOM.render(<ComputedProperties onReady={resolve} />, 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 8184d3bd4ba..399280d0190 100644 --- a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/CustomInterpolation.js +++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/CustomInterpolation.js @@ -1,4 +1,13 @@ -import React from 'react' +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +import React, { Component, PropTypes } from 'react' const styled = ([style]) => style.trim() .split(/\s*;\s*/) @@ -14,21 +23,23 @@ function load() { ]; } -export default class extends React.Component { +export default class extends Component { + static propTypes = { + onReady: PropTypes.func.isRequired + } + constructor(props) { super(props); - - this.done = () => {}; - this.props.setCallWhenDone && this.props.setCallWhenDone((done) => { - this.done = done; - }); - this.state = { users: [] }; } async componentDidMount() { const users = load(); - this.setState({ users }, () => this.done()); + this.setState({ users }); + } + + componentDidUpdate() { + this.props.onReady(); } render() { diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/CustomInterpolation.test.js b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/CustomInterpolation.test.js index 10b1df278c1..ac468db76a8 100644 --- a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/CustomInterpolation.test.js +++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/CustomInterpolation.test.js @@ -1,3 +1,12 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + import React from 'react'; import ReactDOM from 'react-dom'; import CustomInterpolation from './CustomInterpolation'; @@ -5,6 +14,8 @@ import CustomInterpolation from './CustomInterpolation'; describe('custom interpolation', () => { it('renders without crashing', () => { const div = document.createElement('div'); - ReactDOM.render(<CustomInterpolation />, div); + return new Promise(resolve => { + ReactDOM.render(<CustomInterpolation onReady={resolve} />, 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 637a239d7a4..fd4054e56cb 100644 --- a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/DefaultParameters.js +++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/DefaultParameters.js @@ -1,4 +1,13 @@ -import React from 'react' +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +import React, { Component, PropTypes } from 'react' function load(id = 0) { return [ @@ -9,21 +18,23 @@ function load(id = 0) { ]; } -export default class extends React.Component { +export default class extends Component { + static propTypes = { + onReady: PropTypes.func.isRequired + } + constructor(props) { super(props); - - this.done = () => {}; - this.props.setCallWhenDone && this.props.setCallWhenDone((done) => { - this.done = done; - }); - this.state = { users: [] }; } async componentDidMount() { const users = load(); - this.setState({ users }, () => this.done()); + this.setState({ users }); + } + + componentDidUpdate() { + this.props.onReady(); } render() { diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/DefaultParameters.test.js b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/DefaultParameters.test.js index b5ece244681..26d5184eb11 100644 --- a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/DefaultParameters.test.js +++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/DefaultParameters.test.js @@ -1,3 +1,12 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + import React from 'react'; import ReactDOM from 'react-dom'; import DefaultParameters from './DefaultParameters'; @@ -5,6 +14,8 @@ import DefaultParameters from './DefaultParameters'; describe('default parameters', () => { it('renders without crashing', () => { const div = document.createElement('div'); - ReactDOM.render(<DefaultParameters />, div); + return new Promise(resolve => { + ReactDOM.render(<DefaultParameters onReady={resolve} />, 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 e28e0bb3643..c2345f5ce54 100644 --- a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/DestructuringAndAwait.js +++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/DestructuringAndAwait.js @@ -1,4 +1,13 @@ -import React from 'react' +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +import React, { Component, PropTypes } from 'react' async function load() { return { users: [ @@ -9,21 +18,23 @@ async function load() { ] }; } -export default class extends React.Component { +export default class extends Component { + static propTypes = { + onReady: PropTypes.func.isRequired + } + constructor(props) { super(props); - - this.done = () => {}; - this.props.setCallWhenDone && this.props.setCallWhenDone((done) => { - this.done = done; - }); - this.state = { users: [] }; } async componentDidMount() { const { users } = await load(); - this.setState({ users }, () => this.done()); + this.setState({ users }); + } + + componentDidUpdate() { + this.props.onReady(); } render() { diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/DestructuringAndAwait.test.js b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/DestructuringAndAwait.test.js index 14521e30798..c7d74d8012b 100644 --- a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/DestructuringAndAwait.test.js +++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/DestructuringAndAwait.test.js @@ -1,3 +1,12 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + import React from 'react'; import ReactDOM from 'react-dom'; import DestructuringAndAwait from './DestructuringAndAwait'; @@ -5,6 +14,8 @@ import DestructuringAndAwait from './DestructuringAndAwait'; describe('destructuring and await', () => { it('renders without crashing', () => { const div = document.createElement('div'); - ReactDOM.render(<DestructuringAndAwait />, div); + return new Promise(resolve => { + ReactDOM.render(<DestructuringAndAwait onReady={resolve} />, 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 a20fc19b75f..170f00a7f0d 100644 --- a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/Generators.js +++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/Generators.js @@ -1,4 +1,13 @@ -import React from 'react' +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +import React, { Component, PropTypes } from 'react' function * load(limit) { let i = 1; @@ -8,15 +17,13 @@ function * load(limit) { } } -export default class extends React.Component { +export default class extends Component { + static propTypes = { + onReady: PropTypes.func.isRequired + } + constructor(props) { super(props); - - this.done = () => {}; - this.props.setCallWhenDone && this.props.setCallWhenDone((done) => { - this.done = done; - }); - this.state = { users: [] }; } @@ -25,7 +32,11 @@ export default class extends React.Component { for (let user of load(4)) { users.push(user); } - this.setState({ users }, () => this.done()); + this.setState({ users }); + } + + componentDidUpdate() { + this.props.onReady(); } render() { diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/Generators.test.js b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/Generators.test.js index 1fd36cdbed3..3bbe2d4f703 100644 --- a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/Generators.test.js +++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/Generators.test.js @@ -1,3 +1,12 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + import React from 'react'; import ReactDOM from 'react-dom'; import Generators from './Generators'; @@ -5,6 +14,8 @@ import Generators from './Generators'; describe('generators', () => { it('renders without crashing', () => { const div = document.createElement('div'); - ReactDOM.render(<Generators />, div); + return new Promise(resolve => { + ReactDOM.render(<Generators onReady={resolve} />, 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 db377cee5e6..025920b7f31 100644 --- a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ObjectDestructuring.js +++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ObjectDestructuring.js @@ -1,4 +1,13 @@ -import React from 'react' +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +import React, { Component, PropTypes } from 'react' function load() { return [ @@ -9,21 +18,23 @@ function load() { ]; } -export default class extends React.Component { +export default class extends Component { + static propTypes = { + onReady: PropTypes.func.isRequired + } + constructor(props) { super(props); - - this.done = () => {}; - this.props.setCallWhenDone && this.props.setCallWhenDone((done) => { - this.done = done; - }); - this.state = { users: [] }; } async componentDidMount() { const users = load(); - this.setState({ users }, () => this.done()); + this.setState({ users }); + } + + componentDidUpdate() { + this.props.onReady(); } render() { diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ObjectDestructuring.test.js b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ObjectDestructuring.test.js index 7ed28147dec..503b2cb11b6 100644 --- a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ObjectDestructuring.test.js +++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ObjectDestructuring.test.js @@ -1,3 +1,12 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + import React from 'react'; import ReactDOM from 'react-dom'; import ObjectDestructuring from './ObjectDestructuring'; @@ -5,6 +14,8 @@ import ObjectDestructuring from './ObjectDestructuring'; describe('object destructuring', () => { it('renders without crashing', () => { const div = document.createElement('div'); - ReactDOM.render(<ObjectDestructuring />, div); + return new Promise(resolve => { + ReactDOM.render(<ObjectDestructuring onReady={resolve} />, 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 72356fb9404..d5de0aef31a 100644 --- a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ObjectSpread.js +++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ObjectSpread.js @@ -1,4 +1,13 @@ -import React from 'react' +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +import React, { Component, PropTypes } from 'react' function load(baseUser) { return [ @@ -9,21 +18,23 @@ function load(baseUser) { ]; } -export default class extends React.Component { +export default class extends Component { + static propTypes = { + onReady: PropTypes.func.isRequired + } + constructor(props) { super(props); - - this.done = () => {}; - this.props.setCallWhenDone && this.props.setCallWhenDone((done) => { - this.done = done; - }); - this.state = { users: [] }; } async componentDidMount() { const users = load({ age: 42 }); - this.setState({ users }, () => this.done()); + this.setState({ users }); + } + + componentDidUpdate() { + this.props.onReady(); } render() { diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ObjectSpread.test.js b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ObjectSpread.test.js index 9de96c26418..09a92ce05a2 100644 --- a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ObjectSpread.test.js +++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/ObjectSpread.test.js @@ -1,3 +1,12 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + import React from 'react'; import ReactDOM from 'react-dom'; import ObjectSpread from './ObjectSpread'; @@ -5,6 +14,8 @@ import ObjectSpread from './ObjectSpread'; describe('object spread', () => { it('renders without crashing', () => { const div = document.createElement('div'); - ReactDOM.render(<ObjectSpread />, div); + return new Promise(resolve => { + ReactDOM.render(<ObjectSpread onReady={resolve} />, 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 9eb8c20f790..8b362fccf2c 100644 --- a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/Promises.js +++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/Promises.js @@ -1,4 +1,13 @@ -import React from 'react' +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +import React, { Component, PropTypes } from 'react' function load() { return Promise.resolve([ @@ -9,24 +18,26 @@ function load() { ]); } -export default class extends React.Component { +export default class extends Component { + static propTypes = { + onReady: PropTypes.func.isRequired + } + constructor(props) { super(props); - - this.done = () => {}; - this.props.setCallWhenDone && this.props.setCallWhenDone((done) => { - this.done = done; - }); - this.state = { users: [] }; } componentDidMount() { load().then(users => { - this.setState({ users }, () => this.done()); + this.setState({ users }); }); } + componentDidUpdate() { + this.props.onReady(); + } + render() { return ( <div id="feature-promises"> diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/Promises.test.js b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/Promises.test.js index 96b4d298d7c..6f27e70d3e7 100644 --- a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/Promises.test.js +++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/Promises.test.js @@ -1,3 +1,12 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + import React from 'react'; import ReactDOM from 'react-dom'; import Promises from './Promises'; @@ -5,6 +14,8 @@ import Promises from './Promises'; describe('promises', () => { it('renders without crashing', () => { const div = document.createElement('div'); - ReactDOM.render(<Promises />, div); + return new Promise(resolve => { + ReactDOM.render(<Promises onReady={resolve} />, 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 94b75980835..47d011f2eae 100644 --- a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/RestAndDefault.js +++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/RestAndDefault.js @@ -1,4 +1,13 @@ -import React from 'react' +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +import React, { Component, PropTypes } from 'react' function load({ id, ...rest } = { id: 0, user: { id: 42, name: '42' } }) { return [ @@ -9,21 +18,23 @@ function load({ id, ...rest } = { id: 0, user: { id: 42, name: '42' } }) { ]; } -export default class extends React.Component { +export default class extends Component { + static propTypes = { + onReady: PropTypes.func.isRequired + } + constructor(props) { super(props); - - this.done = () => {}; - this.props.setCallWhenDone && this.props.setCallWhenDone((done) => { - this.done = done; - }); - this.state = { users: [] }; } async componentDidMount() { const users = load(); - this.setState({ users }, () => this.done()); + this.setState({ users }); + } + + componentDidUpdate() { + this.props.onReady(); } render() { diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/RestAndDefault.test.js b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/RestAndDefault.test.js index 95f4a19fa0b..e48b7dc59b9 100644 --- a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/RestAndDefault.test.js +++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/RestAndDefault.test.js @@ -1,3 +1,12 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + import React from 'react'; import ReactDOM from 'react-dom'; import RestAndDefault from './RestAndDefault'; @@ -5,6 +14,8 @@ import RestAndDefault from './RestAndDefault'; describe('rest + default', () => { it('renders without crashing', () => { const div = document.createElement('div'); - ReactDOM.render(<RestAndDefault />, div); + return new Promise(resolve => { + ReactDOM.render(<RestAndDefault onReady={resolve} />, 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 c1cd63e887e..1a6cf7c1687 100644 --- a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/RestParameters.js +++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/RestParameters.js @@ -1,4 +1,13 @@ -import React from 'react' +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +import React, { Component, PropTypes } from 'react' function load({ id = 0, ...rest }) { return [ @@ -9,21 +18,23 @@ function load({ id = 0, ...rest }) { ]; } -export default class extends React.Component { +export default class extends Component { + static propTypes = { + onReady: PropTypes.func.isRequired + } + constructor(props) { super(props); - - this.done = () => {}; - this.props.setCallWhenDone && this.props.setCallWhenDone((done) => { - this.done = done; - }); - this.state = { users: [] }; } async componentDidMount() { const users = load({ id: 0, user: { id: 42, name: '42' } }); - this.setState({ users }, () => this.done()); + this.setState({ users }); + } + + componentDidUpdate() { + this.props.onReady(); } render() { diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/RestParameters.test.js b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/RestParameters.test.js index 8e097713318..c1eb90e0dbd 100644 --- a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/RestParameters.test.js +++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/RestParameters.test.js @@ -1,3 +1,12 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + import React from 'react'; import ReactDOM from 'react-dom'; import RestParameters from './RestParameters'; @@ -5,6 +14,8 @@ import RestParameters from './RestParameters'; describe('rest parameters', () => { it('renders without crashing', () => { const div = document.createElement('div'); - ReactDOM.render(<RestParameters />, div); + return new Promise(resolve => { + ReactDOM.render(<RestParameters onReady={resolve} />, 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 33b004722d8..ff46e1c72f8 100644 --- a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/TemplateInterpolation.js +++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/TemplateInterpolation.js @@ -1,4 +1,13 @@ -import React from 'react' +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +import React, { Component, PropTypes } from 'react' function load(name) { return [ @@ -9,21 +18,23 @@ function load(name) { ]; } -export default class extends React.Component { +export default class extends Component { + static propTypes = { + onReady: PropTypes.func.isRequired + } + constructor(props) { super(props); - - this.done = () => {}; - this.props.setCallWhenDone && this.props.setCallWhenDone((done) => { - this.done = done; - }); - this.state = { users: [] }; } async componentDidMount() { const users = load('user_'); - this.setState({ users }, () => this.done()); + this.setState({ users }); + } + + componentDidUpdate() { + this.props.onReady(); } render() { diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/TemplateInterpolation.test.js b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/TemplateInterpolation.test.js index b49af029a44..ff32baaedd0 100644 --- a/packages/react-scripts/fixtures/kitchensink/src/features/syntax/TemplateInterpolation.test.js +++ b/packages/react-scripts/fixtures/kitchensink/src/features/syntax/TemplateInterpolation.test.js @@ -1,3 +1,12 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + import React from 'react'; import ReactDOM from 'react-dom'; import TemplateInterpolation from './TemplateInterpolation'; @@ -5,6 +14,8 @@ import TemplateInterpolation from './TemplateInterpolation'; describe('template interpolation', () => { it('renders without crashing', () => { const div = document.createElement('div'); - ReactDOM.render(<TemplateInterpolation />, div); + return new Promise(resolve => { + ReactDOM.render(<TemplateInterpolation onReady={resolve} />, div); + }); }); }); diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/webpack/CssInclusion.js b/packages/react-scripts/fixtures/kitchensink/src/features/webpack/CssInclusion.js index f35d11e0a82..7c46c7208b7 100644 --- a/packages/react-scripts/fixtures/kitchensink/src/features/webpack/CssInclusion.js +++ b/packages/react-scripts/fixtures/kitchensink/src/features/webpack/CssInclusion.js @@ -1,3 +1,12 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + import React from 'react' import './assets/style.css' diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/webpack/CssInclusion.test.js b/packages/react-scripts/fixtures/kitchensink/src/features/webpack/CssInclusion.test.js index f3e10a441a8..e315ee33b38 100644 --- a/packages/react-scripts/fixtures/kitchensink/src/features/webpack/CssInclusion.test.js +++ b/packages/react-scripts/fixtures/kitchensink/src/features/webpack/CssInclusion.test.js @@ -1,3 +1,12 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + import React from 'react'; import ReactDOM from 'react-dom'; import CssInclusion from './CssInclusion'; 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 d793408a5c7..c4560ff9dae 100644 --- a/packages/react-scripts/fixtures/kitchensink/src/features/webpack/ImageInclusion.js +++ b/packages/react-scripts/fixtures/kitchensink/src/features/webpack/ImageInclusion.js @@ -1,3 +1,12 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + import React from 'react' import tiniestCat from './assets/tiniest-cat.jpg' diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/webpack/ImageInclusion.test.js b/packages/react-scripts/fixtures/kitchensink/src/features/webpack/ImageInclusion.test.js index 85e68d51e85..042d2da3088 100644 --- a/packages/react-scripts/fixtures/kitchensink/src/features/webpack/ImageInclusion.test.js +++ b/packages/react-scripts/fixtures/kitchensink/src/features/webpack/ImageInclusion.test.js @@ -1,3 +1,12 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + import React from 'react'; import ReactDOM from 'react-dom'; import ImageInclusion from './ImageInclusion'; 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 8050b9cfd6c..552a8be7b1a 100644 --- a/packages/react-scripts/fixtures/kitchensink/src/features/webpack/JsonInclusion.js +++ b/packages/react-scripts/fixtures/kitchensink/src/features/webpack/JsonInclusion.js @@ -1,3 +1,12 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + import React from 'react' import { abstract } from './assets/abstract.json' diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/webpack/JsonInclusion.test.js b/packages/react-scripts/fixtures/kitchensink/src/features/webpack/JsonInclusion.test.js index 70043bd0503..ee150f629a6 100644 --- a/packages/react-scripts/fixtures/kitchensink/src/features/webpack/JsonInclusion.test.js +++ b/packages/react-scripts/fixtures/kitchensink/src/features/webpack/JsonInclusion.test.js @@ -1,3 +1,12 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + import React from 'react'; import ReactDOM from 'react-dom'; import JsonInclusion from './JsonInclusion'; diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/webpack/LinkedModules.js b/packages/react-scripts/fixtures/kitchensink/src/features/webpack/LinkedModules.js new file mode 100644 index 00000000000..de8a5e4ab5b --- /dev/null +++ b/packages/react-scripts/fixtures/kitchensink/src/features/webpack/LinkedModules.js @@ -0,0 +1,20 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +import React from 'react'; +import './assets/style.css'; +import { test, version } from 'test-integrity'; + +export default () => { + const v = version(); + if (!test() || v !== '2.0.0') { + throw new Error('Functionality test did not pass.'); + } + return <p id="feature-linked-modules">{v}</p>; +}; diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/webpack/LinkedModules.test.js b/packages/react-scripts/fixtures/kitchensink/src/features/webpack/LinkedModules.test.js new file mode 100644 index 00000000000..aa1e911ae45 --- /dev/null +++ b/packages/react-scripts/fixtures/kitchensink/src/features/webpack/LinkedModules.test.js @@ -0,0 +1,25 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +import React from 'react'; +import ReactDOM from 'react-dom'; +import { test, version } from 'test-integrity'; +import LinkedModules from './LinkedModules'; + +describe('linked modules', () => { + it('has integrity', () => { + expect(test()); + expect(version() === '2.0.0'); + }); + + it('renders without crashing', () => { + const div = document.createElement('div'); + ReactDOM.render(<LinkedModules />, div); + }); +}); 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 70b2d2b2155..fb07359fdc9 100644 --- a/packages/react-scripts/fixtures/kitchensink/src/features/webpack/NoExtInclusion.js +++ b/packages/react-scripts/fixtures/kitchensink/src/features/webpack/NoExtInclusion.js @@ -1,3 +1,12 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + import React from 'react' import aFileWithoutExt from './assets/aFileWithoutExt' diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/webpack/NoExtInclusion.test.js b/packages/react-scripts/fixtures/kitchensink/src/features/webpack/NoExtInclusion.test.js index 666f94f384c..a08b5d21b0c 100644 --- a/packages/react-scripts/fixtures/kitchensink/src/features/webpack/NoExtInclusion.test.js +++ b/packages/react-scripts/fixtures/kitchensink/src/features/webpack/NoExtInclusion.test.js @@ -1,3 +1,12 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + import React from 'react'; import ReactDOM from 'react-dom'; import NoExtInclusion from './NoExtInclusion'; diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/webpack/SvgInclusion.js b/packages/react-scripts/fixtures/kitchensink/src/features/webpack/SvgInclusion.js index 7c2fdb2e44e..f82e5f2911c 100644 --- a/packages/react-scripts/fixtures/kitchensink/src/features/webpack/SvgInclusion.js +++ b/packages/react-scripts/fixtures/kitchensink/src/features/webpack/SvgInclusion.js @@ -1,3 +1,12 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + import React from 'react' import logo from './assets/logo.svg' diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/webpack/SvgInclusion.test.js b/packages/react-scripts/fixtures/kitchensink/src/features/webpack/SvgInclusion.test.js index 507da01403c..585c6c19d8a 100644 --- a/packages/react-scripts/fixtures/kitchensink/src/features/webpack/SvgInclusion.test.js +++ b/packages/react-scripts/fixtures/kitchensink/src/features/webpack/SvgInclusion.test.js @@ -1,3 +1,12 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + import React from 'react'; import ReactDOM from 'react-dom'; import SvgInclusion from './SvgInclusion'; 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 30aeccd6fb7..8734064c882 100644 --- a/packages/react-scripts/fixtures/kitchensink/src/features/webpack/UnknownExtInclusion.js +++ b/packages/react-scripts/fixtures/kitchensink/src/features/webpack/UnknownExtInclusion.js @@ -1,3 +1,12 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + import React from 'react' import aFileWithExtUnknown from './assets/aFileWithExt.unknown' diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/webpack/UnknownExtInclusion.test.js b/packages/react-scripts/fixtures/kitchensink/src/features/webpack/UnknownExtInclusion.test.js index 46724e0a27b..73a8016e17b 100644 --- a/packages/react-scripts/fixtures/kitchensink/src/features/webpack/UnknownExtInclusion.test.js +++ b/packages/react-scripts/fixtures/kitchensink/src/features/webpack/UnknownExtInclusion.test.js @@ -1,3 +1,12 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + import React from 'react'; import ReactDOM from 'react-dom'; import UnknownExtInclusion from './UnknownExtInclusion'; diff --git a/packages/react-scripts/fixtures/kitchensink/src/index.js b/packages/react-scripts/fixtures/kitchensink/src/index.js index 0ea36197ba1..dc1143691f2 100644 --- a/packages/react-scripts/fixtures/kitchensink/src/index.js +++ b/packages/react-scripts/fixtures/kitchensink/src/index.js @@ -1,3 +1,12 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + import React from 'react'; import ReactDOM from 'react-dom'; import App from './App'; diff --git a/packages/react-scripts/fixtures/kitchensink/src/subfolder/lol.js b/packages/react-scripts/fixtures/kitchensink/src/subfolder/lol.js index 6c7375d5479..c844f831d31 100644 --- a/packages/react-scripts/fixtures/kitchensink/src/subfolder/lol.js +++ b/packages/react-scripts/fixtures/kitchensink/src/subfolder/lol.js @@ -1 +1,10 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + module.exports = function() { return `haha` } diff --git a/packages/react-scripts/package.json b/packages/react-scripts/package.json index c3f7d2bddb4..ea55505b60c 100644 --- a/packages/react-scripts/package.json +++ b/packages/react-scripts/package.json @@ -1,6 +1,6 @@ { "name": "react-scripts", - "version": "0.8.5", + "version": "0.9.5", "description": "Configuration and scripts for Create React App.", "repository": "facebookincubator/create-react-app", "license": "BSD-3-Clause", @@ -11,8 +11,8 @@ "url": "https://github.com/facebookincubator/create-react-app/issues" }, "files": [ - ".babelrc", - ".eslintrc", + "babelrc", + "eslintrc", "bin", "config", "scripts", @@ -23,54 +23,50 @@ "react-scripts": "./bin/react-scripts.js" }, "dependencies": { - "autoprefixer": "6.5.1", - "babel-core": "6.17.0", + "autoprefixer": "6.7.2", + "babel-core": "6.22.1", "babel-eslint": "7.1.1", "babel-jest": "18.0.0", "babel-loader": "6.2.10", + "babel-preset-react-app": "^2.2.0", "babel-runtime": "^6.20.0", - "babel-preset-react-app": "^2.0.1", "case-sensitive-paths-webpack-plugin": "1.1.4", "chalk": "1.1.3", "connect-history-api-fallback": "1.3.0", "cross-spawn": "4.0.2", - "css-loader": "0.26.0", - "detect-port": "1.0.1", + "css-loader": "0.26.1", + "detect-port": "1.1.1", "dotenv": "2.0.0", - "eslint": "3.8.1", - "eslint-config-react-app": "^0.5.0", + "eslint": "3.16.1", + "eslint-config-react-app": "^0.6.2", "eslint-loader": "1.6.0", "eslint-plugin-flowtype": "2.21.0", "eslint-plugin-import": "2.0.1", - "eslint-plugin-jsx-a11y": "2.2.3", + "eslint-plugin-jsx-a11y": "4.0.0", "eslint-plugin-react": "6.4.1", "extract-text-webpack-plugin": "1.0.1", - "file-loader": "0.9.0", - "filesize": "3.3.0", + "file-loader": "0.10.0", "fs-extra": "0.30.0", - "gzip-size": "3.0.0", "html-webpack-plugin": "2.24.0", - "http-proxy-middleware": "0.17.2", + "http-proxy-middleware": "0.17.3", "jest": "18.1.0", "json-loader": "0.5.4", - "object-assign": "4.1.0", - "postcss-loader": "1.0.0", + "object-assign": "4.1.1", + "postcss-loader": "1.2.2", "promise": "7.1.1", - "react-dev-utils": "^0.4.2", - "recursive-readdir": "2.1.0", - "strip-ansi": "3.0.1", + "react-dev-utils": "^0.5.2", "style-loader": "0.13.1", "url-loader": "0.5.7", "webpack": "1.14.0", "webpack-dev-server": "1.16.2", "webpack-manifest-plugin": "1.1.0", - "whatwg-fetch": "1.0.0" + "whatwg-fetch": "2.0.2" }, "devDependencies": { "react": "^15.3.0", "react-dom": "^15.3.0" }, "optionalDependencies": { - "fsevents": "1.0.14" + "fsevents": "1.0.17" } } diff --git a/packages/react-scripts/scripts/build.js b/packages/react-scripts/scripts/build.js index 42be50d43a8..63c88120af8 100644 --- a/packages/react-scripts/scripts/build.js +++ b/packages/react-scripts/scripts/build.js @@ -8,6 +8,7 @@ * of patent rights can be found in the PATENTS file in the same directory. */ // @remove-on-eject-end +'use strict'; // Do this as the first thing so that any code reading it knows the right env. process.env.NODE_ENV = 'production'; @@ -21,14 +22,14 @@ require('dotenv').config({silent: true}); var chalk = require('chalk'); var fs = require('fs-extra'); var path = require('path'); -var filesize = require('filesize'); -var gzipSize = require('gzip-size').sync; +var url = require('url'); var webpack = require('webpack'); var config = require('../config/webpack.config.prod'); var paths = require('../config/paths'); var checkRequiredFiles = require('react-dev-utils/checkRequiredFiles'); -var recursive = require('recursive-readdir'); -var stripAnsi = require('strip-ansi'); +var FileSizeReporter = require('react-dev-utils/FileSizeReporter'); +var measureFileSizesBeforeBuild = FileSizeReporter.measureFileSizesBeforeBuild; +var printFileSizesAfterBuild = FileSizeReporter.printFileSizesAfterBuild; var useYarn = fs.existsSync(paths.yarnLockFile); @@ -37,88 +38,20 @@ if (!checkRequiredFiles([paths.appHtml, paths.appIndexJs])) { process.exit(1); } -// Input: /User/dan/app/build/static/js/main.82be8.js -// Output: /static/js/main.js -function removeFileNameHash(fileName) { - return fileName - .replace(paths.appBuild, '') - .replace(/\/?(.*)(\.\w+)(\.js|\.css)/, (match, p1, p2, p3) => p1 + p3); -} - -// Input: 1024, 2048 -// Output: "(+1 KB)" -function getDifferenceLabel(currentSize, previousSize) { - var FIFTY_KILOBYTES = 1024 * 50; - var difference = currentSize - previousSize; - var fileSize = !Number.isNaN(difference) ? filesize(difference) : 0; - if (difference >= FIFTY_KILOBYTES) { - return chalk.red('+' + fileSize); - } else if (difference < FIFTY_KILOBYTES && difference > 0) { - return chalk.yellow('+' + fileSize); - } else if (difference < 0) { - return chalk.green(fileSize); - } else { - return ''; - } -} - // First, read the current file sizes in build directory. // This lets us display how much they changed later. -recursive(paths.appBuild, (err, fileNames) => { - var previousSizeMap = (fileNames || []) - .filter(fileName => /\.(js|css)$/.test(fileName)) - .reduce((memo, fileName) => { - var contents = fs.readFileSync(fileName); - var key = removeFileNameHash(fileName); - memo[key] = gzipSize(contents); - return memo; - }, {}); - +measureFileSizesBeforeBuild(paths.appBuild).then(previousFileSizes => { // Remove all content but keep the directory so that // if you're in it, you don't end up in Trash fs.emptyDirSync(paths.appBuild); // Start the webpack build - build(previousSizeMap); + build(previousFileSizes); // Merge with the public folder copyPublicFolder(); }); -// Print a detailed summary of build files. -function printFileSizes(stats, previousSizeMap) { - var assets = stats.toJson().assets - .filter(asset => /\.(js|css)$/.test(asset.name)) - .map(asset => { - var fileContents = fs.readFileSync(paths.appBuild + '/' + asset.name); - var size = gzipSize(fileContents); - var previousSize = previousSizeMap[removeFileNameHash(asset.name)]; - var difference = getDifferenceLabel(size, previousSize); - return { - folder: path.join('build', path.dirname(asset.name)), - name: path.basename(asset.name), - size: size, - sizeLabel: filesize(size) + (difference ? ' (' + difference + ')' : '') - }; - }); - assets.sort((a, b) => b.size - a.size); - var longestSizeLabelLength = Math.max.apply(null, - assets.map(a => stripAnsi(a.sizeLabel).length) - ); - assets.forEach(asset => { - var sizeLabel = asset.sizeLabel; - var sizeLength = stripAnsi(sizeLabel).length; - if (sizeLength < longestSizeLabelLength) { - var rightPadding = ' '.repeat(longestSizeLabelLength - sizeLength); - sizeLabel += rightPadding; - } - console.log( - ' ' + sizeLabel + - ' ' + chalk.dim(asset.folder + path.sep) + chalk.cyan(asset.name) - ); - }); -} - // Print out errors function printErrors(summary, errors) { console.log(chalk.red(summary)); @@ -130,7 +63,7 @@ function printErrors(summary, errors) { } // Create the production build and print the deployment instructions. -function build(previousSizeMap) { +function build(previousFileSizes) { console.log('Creating an optimized production build...'); webpack(config).run((err, stats) => { if (err) { @@ -144,7 +77,7 @@ function build(previousSizeMap) { } if (process.env.CI && stats.compilation.warnings.length) { - printErrors('Failed to compile.', stats.compilation.warnings); + printErrors('Failed to compile. When process.env.CI = true, warnings are treated as failures. Most CI servers set this automatically.', stats.compilation.warnings); process.exit(1); } @@ -153,20 +86,20 @@ function build(previousSizeMap) { console.log('File sizes after gzip:'); console.log(); - printFileSizes(stats, previousSizeMap); + printFileSizesAfterBuild(stats, previousFileSizes); console.log(); - var openCommand = process.platform === 'win32' ? 'start' : 'open'; var appPackage = require(paths.appPackageJson); - var homepagePath = appPackage.homepage; + var publicUrl = paths.publicUrl; var publicPath = config.output.publicPath; - if (homepagePath && homepagePath.indexOf('.github.io/') !== -1) { + var publicPathname = url.parse(publicPath).pathname; + 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(publicPath) + '.'); + console.log('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') + '.'); console.log(); console.log('The ' + chalk.cyan('build') + ' folder is ready to be deployed.'); - console.log('To publish it at ' + chalk.green(homepagePath) + ', run:'); + console.log('To publish it at ' + chalk.green(publicUrl) + ', run:'); // If script deploy has been added to package.json, skip the instructions if (typeof appPackage.scripts.deploy === 'undefined') { console.log(); @@ -198,30 +131,30 @@ function build(previousSizeMap) { console.log('The ' + chalk.cyan('build') + ' folder is ready to be deployed.'); console.log(); } else { - // no homepage or "homepage": "http://mywebsite.com" - console.log('The project was built assuming it is hosted at the server root.'); - if (homepagePath) { + if (publicUrl) { // "homepage": "http://mywebsite.com" + console.log('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') + '.'); console.log(); } else { // no homepage + console.log('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') + '.'); 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(',')); console.log(); } - console.log('The ' + chalk.cyan('build') + ' folder is ready to be deployed.'); - console.log('You may also serve it locally with a static server:') + var build = path.relative(process.cwd(), paths.appBuild); + console.log('The ' + chalk.cyan(build) + ' folder is ready to be deployed.'); + console.log('You may serve it with a static server:'); console.log(); if (useYarn) { - console.log(' ' + chalk.cyan('yarn') + ' global add pushstate-server'); + console.log(` ${chalk.cyan('yarn')} global add serve`); } else { - console.log(' ' + chalk.cyan('npm') + ' install -g pushstate-server'); + console.log(` ${chalk.cyan('npm')} install -g serve`); } - console.log(' ' + chalk.cyan('pushstate-server') + ' build'); - console.log(' ' + chalk.cyan(openCommand) + ' http://localhost:9000'); + console.log(` ${chalk.cyan('serve')} -s build`); console.log(); } }); diff --git a/packages/react-scripts/scripts/eject.js b/packages/react-scripts/scripts/eject.js index b8f9d313143..6647a3bcc2e 100644 --- a/packages/react-scripts/scripts/eject.js +++ b/packages/react-scripts/scripts/eject.js @@ -6,6 +6,7 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. */ +'use strict'; var createJestConfig = require('../utils/createJestConfig'); var fs = require('fs-extra'); @@ -28,8 +29,8 @@ prompt( console.log('Ejecting...'); - var ownPath = path.join(__dirname, '..'); - var appPath = path.join(ownPath, '..', '..'); + var ownPath = paths.ownPath; + var appPath = paths.appPath; function verifyAbsent(file) { if (fs.existsSync(path.join(appPath, file))) { @@ -88,8 +89,8 @@ prompt( var ownPackage = require(path.join(ownPath, 'package.json')); var appPackage = require(path.join(appPath, 'package.json')); - var babelConfig = JSON.parse(fs.readFileSync(path.join(ownPath, '.babelrc'), 'utf8')); - var eslintConfig = JSON.parse(fs.readFileSync(path.join(ownPath, '.eslintrc'), 'utf8')); + var babelConfig = JSON.parse(fs.readFileSync(path.join(ownPath, 'babelrc'), 'utf8')); + var eslintConfig = JSON.parse(fs.readFileSync(path.join(ownPath, 'eslintrc'), 'utf8')); console.log(cyan('Updating the dependencies')); var ownPackageName = ownPackage.name; @@ -114,14 +115,17 @@ prompt( console.log(cyan('Updating the scripts')); delete appPackage.scripts['eject']; Object.keys(appPackage.scripts).forEach(function (key) { - appPackage.scripts[key] = appPackage.scripts[key] - .replace(/react-scripts (\w+)/g, 'node scripts/$1.js'); - console.log( - ' Replacing ' + - cyan('"react-scripts ' + key + '"') + - ' with ' + - cyan('"node scripts/' + key + '.js"') - ); + Object.keys(ownPackage.bin).forEach(function (binKey) { + var regex = new RegExp(binKey + ' (\\w+)', 'g'); + appPackage.scripts[key] = appPackage.scripts[key] + .replace(regex, 'node scripts/$1.js'); + console.log( + ' Replacing ' + + cyan('"' + binKey + ' ' + key + '"') + + ' with ' + + cyan('"node scripts/' + key + '.js"') + ); + }); }); console.log(); @@ -129,13 +133,12 @@ prompt( // Add Jest config console.log(' Adding ' + cyan('Jest') + ' configuration'); appPackage.jest = createJestConfig( - filePath => path.join('<rootDir>', filePath), + filePath => path.posix.join('<rootDir>', filePath), null, true ); // Add Babel config - console.log(' Adding ' + cyan('Babel') + ' preset'); appPackage.babel = babelConfig; @@ -145,17 +148,28 @@ prompt( fs.writeFileSync( path.join(appPath, 'package.json'), - JSON.stringify(appPackage, null, 2) + JSON.stringify(appPackage, null, 2) + '\n' ); console.log(); + // "Don't destroy what isn't ours" + if (ownPath.indexOf(appPath) === 0) { + try { + // remove react-scripts and react-scripts binaries from app node_modules + Object.keys(ownPackage.bin).forEach(function(binKey) { + fs.removeSync(path.join(appPath, 'node_modules', '.bin', binKey)); + }); + fs.removeSync(ownPath); + } catch(e) { + // It's not essential that this succeeds + } + } + if (fs.existsSync(paths.yarnLockFile)) { console.log(cyan('Running yarn...')); - fs.removeSync(ownPath); spawnSync('yarnpkg', [], {stdio: 'inherit'}); } else { console.log(cyan('Running npm install...')); - fs.removeSync(ownPath); spawnSync('npm', ['install'], {stdio: 'inherit'}); } console.log(green('Ejected successfully!')); diff --git a/packages/react-scripts/scripts/init.js b/packages/react-scripts/scripts/init.js index 864005ecac2..b4d40f53153 100644 --- a/packages/react-scripts/scripts/init.js +++ b/packages/react-scripts/scripts/init.js @@ -7,6 +7,8 @@ * of patent rights can be found in the PATENTS file in the same directory. */ +'use strict'; + var fs = require('fs-extra'); var path = require('path'); var spawn = require('cross-spawn'); @@ -64,8 +66,6 @@ module.exports = function(appPath, appName, verbose, originalDirectory, template } }); - // Run yarn or npm for react and react-dom - // TODO: having to do two npm/yarn installs is bad, can we avoid it? var command; var args; @@ -92,53 +92,68 @@ module.exports = function(appPath, appName, verbose, originalDirectory, template fs.unlinkSync(templateDependenciesPath); } - console.log('Installing react and react-dom using ' + command + '...'); - console.log(); + // Install react and react-dom for backward compatibility with old CRA cli + // which doesn't install react and react-dom along with react-scripts + // or template is presetend (via --internal-testing-template) + if (!isReactInstalled(appPackage) || template) { + console.log('Installing react and react-dom using ' + command + '...'); + console.log(); - var proc = spawn(command, args, {stdio: 'inherit'}); - proc.on('close', function (code) { - if (code !== 0) { + var proc = spawn.sync(command, args, {stdio: 'inherit'}); + if (proc.status !== 0) { console.error('`' + command + ' ' + args.join(' ') + '` failed'); return; } + } - // Display the most elegant way to cd. - // This needs to handle an undefined originalDirectory for - // backward compatibility with old global-cli's. - var cdpath; - if (originalDirectory && - path.join(originalDirectory, appName) === appPath) { - cdpath = appName; - } else { - cdpath = appPath; - } + // Display the most elegant way to cd. + // This needs to handle an undefined originalDirectory for + // backward compatibility with old global-cli's. + var cdpath; + if (originalDirectory && + path.join(originalDirectory, appName) === appPath) { + cdpath = appName; + } else { + cdpath = appPath; + } + // Change displayed command to yarn instead of yarnpkg + var displayedCommand = useYarn ? 'yarn' : 'npm'; + + console.log(); + console.log('Success! Created ' + appName + ' at ' + appPath); + console.log('Inside that directory, you can run several commands:'); + console.log(); + console.log(chalk.cyan(' ' + displayedCommand + ' start')); + console.log(' Starts the development server.'); + console.log(); + console.log(chalk.cyan(` ${displayedCommand} ${useYarn ? '' : 'run '}build`)); + console.log(' Bundles the app into static files for production.'); + console.log(); + console.log(chalk.cyan(' ' + displayedCommand + ' test')); + console.log(' Starts the test runner.'); + console.log(); + console.log(chalk.cyan(` ${displayedCommand} ${useYarn ? '' : 'run '}eject`)); + console.log(' Removes this tool and copies build dependencies, configuration files'); + console.log(' and scripts into the app directory. If you do this, you can’t go back!'); + console.log(); + console.log('We suggest that you begin by typing:'); + console.log(); + console.log(chalk.cyan(' cd'), cdpath); + console.log(' ' + chalk.cyan(displayedCommand + ' start')); + if (readmeExists) { console.log(); - console.log('Success! Created ' + appName + ' at ' + appPath); - console.log('Inside that directory, you can run several commands:'); - console.log(); - console.log(chalk.cyan(' ' + command + ' start')); - console.log(' Starts the development server.'); - console.log(); - console.log(chalk.cyan(' ' + command + ' run build')); - console.log(' Bundles the app into static files for production.'); - console.log(); - console.log(chalk.cyan(' ' + command + ' test')); - console.log(' Starts the test runner.'); - console.log(); - console.log(chalk.cyan(' ' + command + ' run eject')); - console.log(' Removes this tool and copies build dependencies, configuration files'); - console.log(' and scripts into the app directory. If you do this, you can’t go back!'); - console.log(); - console.log('We suggest that you begin by typing:'); - console.log(); - console.log(chalk.cyan(' cd'), cdpath); - console.log(' ' + chalk.cyan(command + ' start')); - if (readmeExists) { - console.log(); - console.log(chalk.yellow('You had a `README.md` file, we renamed it to `README.old.md`')); - } - console.log(); - console.log('Happy hacking!'); - }); + console.log(chalk.yellow('You had a `README.md` file, we renamed it to `README.old.md`')); + } + console.log(); + console.log('Happy hacking!'); }; + +function isReactInstalled(appPackage) { + var dependencies = appPackage.dependencies || {}; + + return ( + typeof dependencies.react !== 'undefined' && + typeof dependencies['react-dom'] !== 'undefined' + ) +} diff --git a/packages/react-scripts/scripts/start.js b/packages/react-scripts/scripts/start.js index 8615fb074a9..b24cd9a27b4 100644 --- a/packages/react-scripts/scripts/start.js +++ b/packages/react-scripts/scripts/start.js @@ -9,6 +9,8 @@ */ // @remove-on-eject-end +'use strict'; + process.env.NODE_ENV = 'development'; // Load environment variables from .env file. Suppress warnings using silent @@ -43,7 +45,7 @@ if (!checkRequiredFiles([paths.appHtml, paths.appIndexJs])) { } // Tools like Cloud9 rely on this. -var DEFAULT_PORT = process.env.PORT || 3000; +var DEFAULT_PORT = parseInt(process.env.PORT, 10) || 3000; var compiler; var handleCompile; @@ -186,6 +188,14 @@ function addMiddleware(devServer) { console.log(chalk.red('Instead, the type of "proxy" was "' + typeof proxy + '".')); console.log(chalk.red('Either remove "proxy" from package.json, or make it a string.')); process.exit(1); + // Test that proxy url specified starts with http:// or https:// + } else if (!/^http(s)?:\/\//.test(proxy)) { + console.log( + chalk.red( + 'When "proxy" is specified in package.json it must start with either http:// or https://' + ) + ); + process.exit(1); } // Otherwise, if proxy is specified, we will let it handle any request. @@ -201,7 +211,7 @@ function addMiddleware(devServer) { var hpm = httpProxyMiddleware(pathname => mayProxy.test(pathname), { target: proxy, logLevel: 'silent', - onProxyReq: function(proxyReq, req, res) { + onProxyReq: function(proxyReq) { // Browers may send Origin headers even with same-origin // requests. To prevent CORS issues, we have to change // the Origin to match the target URL. @@ -212,7 +222,8 @@ function addMiddleware(devServer) { onError: onProxyError(proxy), secure: false, changeOrigin: true, - ws: true + ws: true, + xfwd: true }); devServer.use(mayProxy, hpm); @@ -241,7 +252,7 @@ function runDevServer(host, port, protocol) { // project directory is dangerous because we may expose sensitive files. // Instead, we establish a convention that only files in `public` directory // get served. Our build script will copy `public` into the `build` folder. - // In `index.html`, you can get URL of `public` folder with %PUBLIC_PATH%: + // In `index.html`, you can get URL of `public` folder with %PUBLIC_URL%: // <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico"> // In JavaScript code, you can access it with `process.env.PUBLIC_URL`. // Note that we only recommend to use `public` folder as an escape hatch @@ -275,7 +286,7 @@ function runDevServer(host, port, protocol) { addMiddleware(devServer); // Launch WebpackDevServer. - devServer.listen(port, (err, result) => { + devServer.listen(port, host, err => { if (err) { return console.log(err); } @@ -292,7 +303,7 @@ function runDevServer(host, port, protocol) { function run(port) { var protocol = process.env.HTTPS === 'true' ? "https" : "http"; - var host = process.env.HOST || 'localhost'; + var host = process.env.HOST || '0.0.0.0'; setupCompiler(host, port, protocol); runDevServer(host, port, protocol); } diff --git a/packages/react-scripts/scripts/test.js b/packages/react-scripts/scripts/test.js index 9de5181d739..c4511f839dc 100644 --- a/packages/react-scripts/scripts/test.js +++ b/packages/react-scripts/scripts/test.js @@ -9,6 +9,8 @@ */ // @remove-on-eject-end +'use strict'; + process.env.NODE_ENV = 'test'; process.env.PUBLIC_URL = ''; diff --git a/packages/react-scripts/template/README.md b/packages/react-scripts/template/README.md index 0eb5cda3840..cdd41234a75 100644 --- a/packages/react-scripts/template/README.md +++ b/packages/react-scripts/template/README.md @@ -13,13 +13,16 @@ You can find the most recent version of this guide [here](https://github.com/fac - [npm test](#npm-test) - [npm run build](#npm-run-build) - [npm run eject](#npm-run-eject) +- [Supported Language Features and Polyfills](#supported-language-features-and-polyfills) - [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) - [Changing the Page `<title>`](#changing-the-page-title) - [Installing a Dependency](#installing-a-dependency) - [Importing a Component](#importing-a-component) - [Adding a Stylesheet](#adding-a-stylesheet) - [Post-Processing CSS](#post-processing-css) +- [Adding a CSS Preprocessor (Sass, Less etc.)](#adding-a-css-preprocessor-sass-less-etc) - [Adding Images and Fonts](#adding-images-and-fonts) - [Using the `public` Folder](#using-the-public-folder) - [Changing the HTML](#changing-the-html) @@ -27,8 +30,12 @@ You can find the most recent version of this guide [here](https://github.com/fac - [When to Use the `public` Folder](#when-to-use-the-public-folder) - [Using Global Variables](#using-global-variables) - [Adding Bootstrap](#adding-bootstrap) + - [Using a Custom Theme](#using-a-custom-theme) - [Adding Flow](#adding-flow) - [Adding Custom Environment Variables](#adding-custom-environment-variables) + - [Referencing Environment Variables in the HTML](#referencing-environment-variables-in-the-html) + - [Adding Temporary Environment Variables In Your Shell](#adding-temporary-environment-variables-in-your-shell) + - [Adding Development Environment Variables In `.env`](#adding-development-environment-variables-in-env) - [Can I Use Decorators?](#can-i-use-decorators) - [Integrating with an API Backend](#integrating-with-an-api-backend) - [Node](#node) @@ -36,6 +43,8 @@ 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) - [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) +- [Injecting Data from the Server into the Page](#injecting-data-from-the-server-into-the-page) - [Running Tests](#running-tests) - [Filename Conventions](#filename-conventions) - [Command Line Interface](#command-line-interface) @@ -53,8 +62,11 @@ You can find the most recent version of this guide [here](https://github.com/fac - [Developing Components in Isolation](#developing-components-in-isolation) - [Making a Progressive Web App](#making-a-progressive-web-app) - [Deployment](#deployment) + - [Static Server](#static-server) + - [Other Solutions](#other-solutions) - [Serving Apps with Client-Side Routing](#serving-apps-with-client-side-routing) - [Building for Relative Paths](#building-for-relative-paths) + - [Azure](#azure) - [Firebase](#firebase) - [GitHub Pages](#github-pages) - [Heroku](#heroku) @@ -63,6 +75,7 @@ You can find the most recent version of this guide [here](https://github.com/fac - [Now](#now) - [S3 and CloudFront](#s3-and-cloudfront) - [Surge](#surge) +- [Advanced Configuration](#advanced-configuration) - [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) @@ -165,6 +178,29 @@ Instead, it will copy all the configuration files and the transitive dependencie You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it. +## Supported Language Features and Polyfills + +This project supports a superset of the latest JavaScript standard.<br> +In addition to [ES6](https://github.com/lukehoban/es6features) syntax features, it also supports: + +* [Exponentiation Operator](https://github.com/rwaldron/exponentiation-operator) (ES2016). +* [Async/await](https://github.com/tc39/ecmascript-asyncawait) (ES2017). +* [Object Rest/Spread Properties](https://github.com/sebmarkbage/ecmascript-rest-spread) (stage 3 proposal). +* [Class Fields and Static Properties](https://github.com/tc39/proposal-class-public-fields) (stage 2 proposal). +* [JSX](https://facebook.github.io/react/docs/introducing-jsx.html) and [Flow](https://flowtype.org/) syntax. + +Learn more about [different proposal stages](https://babeljs.io/docs/plugins/#presets-stage-x-experimental-presets-). + +While we recommend to use experimental proposals with some caution, Facebook heavily uses these features in the product code, so we intend to provide [codemods](https://medium.com/@cpojer/effective-javascript-codemods-5a6686bb46fb) if any of these proposals change in the future. + +Note that **the project only includes a few ES6 [polyfills](https://en.wikipedia.org/wiki/Polyfill)**: + +* [`Object.assign()`](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/assign) via [`object-assign`](https://github.com/sindresorhus/object-assign). +* [`Promise`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) via [`promise`](https://github.com/then/promise). +* [`fetch()`](https://developer.mozilla.org/en/docs/Web/API/Fetch_API) via [`whatwg-fetch`](https://github.com/github/fetch). + +If you use any other ES6+ features that need **runtime support** (such as `Array.from()` or `Symbol`), make sure you are including the appropriate polyfills manually, or that the browsers you are targeting already support them. + ## Syntax Highlighting in the Editor To configure the syntax highlighting in your favorite text editor, head to the [relevant Babel documentation page](https://babeljs.io/docs/editors) and follow the instructions. Some of the most popular editors are covered. @@ -185,6 +221,18 @@ You would need to install an ESLint plugin for your editor first. ><img src="http://i.imgur.com/yVNNHJM.png" width="300"> + +>**For Visual Studio Code users** + +>VS Code ESLint plugin automatically detects Create React App's configuration file. So you do not need to create `eslintrc.json` at the root directory, except when you want to add your own rules. In that case, you should include CRA's config by adding this line: + +>```js +>{ +> // ... +> "extends": "react-app" +>} +>``` + Then add this block to the `package.json` file of your project: ```js @@ -199,20 +247,49 @@ Then add this block to the `package.json` file of your project: Finally, you will need to install some packages *globally*: ```sh -npm install -g eslint-config-react-app@0.3.0 eslint@3.8.1 babel-eslint@7.0.0 eslint-plugin-react@6.4.1 eslint-plugin-import@2.0.1 eslint-plugin-jsx-a11y@2.2.3 eslint-plugin-flowtype@2.21.0 +npm install -g eslint-config-react-app@0.3.0 eslint@3.8.1 babel-eslint@7.0.0 eslint-plugin-react@6.4.1 eslint-plugin-import@2.0.1 eslint-plugin-jsx-a11y@4.0.0 eslint-plugin-flowtype@2.21.0 ``` We recognize that this is suboptimal, but it is currently required due to the way we hide the ESLint dependency. The ESLint team is already [working on a solution to this](https://github.com/eslint/eslint/issues/3458) so this may become unnecessary in a couple of months. +## Debugging in the Editor + +**This feature is currently only supported by [Visual Studio Code](https://code.visualstudio.com) editor.** + +Visual Studio Code supports live-editing and debugging out of the box with Create React App. This enables you as a developer to write and debug your React code without leaving the editor, and most importantly it enables you to have a continuous development workflow, where context switching is minimal, as you don’t have to switch between tools. + +You would need to have the latest version of [VS Code](https://code.visualstudio.com) and VS Code [Chrome Debugger Extension](https://marketplace.visualstudio.com/items?itemName=msjsdiag.debugger-for-chrome) installed. + +Then add the block below to your `launch.json` file and put it inside the `.vscode` folder in your app’s root directory. + +```json +{ + "version": "0.2.0", + "configurations": [{ + "name": "Chrome", + "type": "chrome", + "request": "launch", + "url": "http://localhost:3000", + "webRoot": "${workspaceRoot}/src", + "userDataDir": "${workspaceRoot}/.vscode/chrome", + "sourceMapPathOverrides": { + "webpack:///src/*": "${webRoot}/*" + } + }] +} +``` + +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. + ## 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. -Note that normally you wouldn't edit files in the `public` folder very often. For example, [adding a stylesheet](#adding-a-stylesheet) is done without touching the HTML. +Note that normally you wouldn’t edit files in the `public` folder very often. For example, [adding a stylesheet](#adding-a-stylesheet) is done without touching the HTML. If you need to dynamically update the page title based on the content, you can use the browser [`document.title`](https://developer.mozilla.org/en-US/docs/Web/API/Document/title) API. For more complex scenarios when you want to change the title from React components, you can use [React Helmet](https://github.com/nfl/react-helmet), a third party library. -Finally, if you use a custom server for your app in production and want to modify the title before it gets sent to the browser, you can follow advice in [this section](#generating-dynamic-meta-tags-on-the-server). +If you use a custom server for your app in production and want to modify the title before it gets sent to the browser, you can follow advice in [this section](#generating-dynamic-meta-tags-on-the-server). Alternatively, you can pre-build each page as a static HTML file which then loads the JavaScript bundle, which is covered [here](#pre-rendering-into-static-html-files). ## Installing a Dependency @@ -334,7 +411,62 @@ becomes this: } ``` -There is currently no support for preprocessors such as Less, or for sharing variables across CSS files. +If you need to disable autoprefixing for some reason, [follow this section](https://github.com/postcss/autoprefixer#disabling). + +## Adding a CSS Preprocessor (Sass, Less etc.) + +Generally, we recommend that you don’t reuse the same CSS classes across different components. For example, instead of using a `.Button` CSS class in `<AcceptButton>` and `<RejectButton>` components, we recommend creating a `<Button>` component with its own `.Button` styles, that both `<AcceptButton>` and `<RejectButton>` can render (but [not inherit](https://facebook.github.io/react/docs/composition-vs-inheritance.html)). + +Following this rule often makes CSS preprocessors less useful, as features like mixins and nesting are replaced by component composition. You can, however, integrate a CSS preprocessor if you find it valuable. In this walkthrough, we will be using Sass, but you can also use Less, or another alternative. + +First, let’s install the command-line interface for Sass: + +``` +npm install node-sass --save-dev +``` + +Then in `package.json`, add the following lines to `scripts`: + +```diff + "scripts": { ++ "build-css": "node-sass src/ -o src/", ++ "watch-css": "npm run build-css && node-sass src/ -o src/ --watch --recursive", + "start": "react-scripts start", + "build": "react-scripts build", + "test": "react-scripts test --env=jsdom", +``` + +>Note: To use a different preprocessor, replace `build-css` and `watch-css` commands according to your preprocessor’s documentation. + +Now you can rename `src/App.css` to `src/App.scss` and run `npm run watch-css`. The watcher will find every Sass file in `src` subdirectories, and create a corresponding CSS file next to it, in our case overwriting `src/App.css`. Since `src/App.js` still imports `src/App.css`, the styles become a part of your application. You can now edit `src/App.scss`, and `src/App.css` will be regenerated. + +To share variables between Sass files, you can use Sass imports. For example, `src/App.scss` and other component style files could include `@import "./shared.scss";` with variable definitions. + +At this point you might want to remove all CSS files from the source control, and add `src/**/*.css` to your `.gitignore` file. It is generally a good practice to keep the build products outside of the source control. + +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: + +``` +npm install --save-dev npm-run-all +``` + +Then we can change `start` and `build` scripts to include the CSS preprocessor commands: + +```diff + "scripts": { + "build-css": "node-sass src/ -o src/", + "watch-css": "npm run build-css && node-sass src/ -o src/ --watch --recursive", +- "start": "react-scripts start", +- "build": "react-scripts build", ++ "start-js": "react-scripts start", ++ "start": "npm-run-all -p watch-css start-js", ++ "build": "npm run build-css && react-scripts build", + "test": "react-scripts test --env=jsdom", + "eject": "react-scripts eject" + } +``` + +Now running `npm start` and `npm run build` also builds Sass files. Note that `node-sass` seems to have an [issue recognizing newly created files on some systems](https://github.com/sass/node-sass/issues/1891) so you might need to restart the watcher when you create a file until it’s resolved. ## Adding Images and Fonts @@ -457,18 +589,20 @@ Alternatively, you can force the linter to ignore any line by adding `// eslint- You don’t have to use [React Bootstrap](https://react-bootstrap.github.io) together with React but it is a popular library for integrating Bootstrap with React apps. If you need it, you can integrate it with Create React App by following these steps: -Install React Bootstrap and Bootstrap from NPM. React Bootstrap does not include Bootstrap CSS so this needs to be installed as well: +Install React Bootstrap and Bootstrap from npm. React Bootstrap does not include Bootstrap CSS so this needs to be installed as well: ``` npm install react-bootstrap --save npm install bootstrap@3 --save ``` -Import Bootstrap CSS and optionally Bootstrap theme CSS in the ```src/index.js``` file: +Import Bootstrap CSS and optionally Bootstrap theme CSS in the beginning of your ```src/index.js``` file: ```js import 'bootstrap/dist/css/bootstrap.css'; import 'bootstrap/dist/css/bootstrap-theme.css'; +// Put any other imports below so that CSS from your +// components takes precedence over default styles. ``` Import required React Bootstrap components within ```src/App.js``` file or your custom component files: @@ -479,19 +613,31 @@ import { Navbar, Jumbotron, Button } from 'react-bootstrap'; Now you are ready to use the imported React Bootstrap components within your component hierarchy defined in the render method. Here is an example [`App.js`](https://gist.githubusercontent.com/gaearon/85d8c067f6af1e56277c82d19fd4da7b/raw/6158dd991b67284e9fc8d70b9d973efe87659d72/App.js) redone using React Bootstrap. +### Using a Custom Theme + +Sometimes you might need to tweak the visual styles of Bootstrap (or equivalent package).<br> +We suggest the following approach: + +* Create a new package that depends on the package you wish to customize, e.g. Bootstrap. +* Add the necessary build steps to tweak the theme, and publish your package on npm. +* Install your own theme npm package as a dependency of your app. + +Here is an example of adding a [customized Bootstrap](https://medium.com/@tacomanator/customizing-create-react-app-aa9ffb88165) that follows these steps. + ## Adding Flow Flow is a static type checker that helps you write code with fewer bugs. Check out this [introduction to using static types in JavaScript](https://medium.com/@preethikasireddy/why-use-static-types-in-javascript-part-1-8382da1e0adb) if you are new to this concept. -Recent versions of [Flow](http://flowtype.org/) work with Create React App projects out of the box. +Recent versions of [Flow](http://flowtype.org/) work with Create React App projects out of the box. To add Flow to a Create React App project, follow these steps: -1. Run `npm install --save-dev flow-bin`. +1. Run `npm install --save-dev flow-bin` (or `yarn add --dev flow-bin`). 2. Add `"flow": "flow"` to the `scripts` section of your `package.json`. -3. Add `// @flow` to any files you want to type check (for example, to `src/App.js`). +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`). -Now you can run `npm run flow` to check the files for type errors. +Now you can run `npm run flow` (or `yarn flow`) to check the files for type errors. You can optionally use an IDE like [Nuclide](https://nuclide.io/docs/languages/flow/) for a better integrated experience. In the future we plan to integrate it into Create React App even more closely. @@ -503,11 +649,16 @@ To learn more about Flow, check out [its documentation](https://flowtype.org/). Your project can consume variables declared in your environment as if they were declared locally in your JS files. By default you will have `NODE_ENV` defined for you, and any other environment variables starting with -`REACT_APP_`. These environment variables will be defined for you on `process.env`. For example, having an environment -variable named `REACT_APP_SECRET_CODE` will be exposed in your JS as `process.env.REACT_APP_SECRET_CODE`, in addition -to `process.env.NODE_ENV`. +`REACT_APP_`. + +**The environment variables are embedded during the build time**. Since Create React App produces a static HTML/CSS/JS bundle, it can’t possibly read them at runtime. To read them at runtime, you would need to load HTML into memory on the server and replace placeholders in runtime, just like [described here](#injecting-data-from-the-server-into-the-page). Alternatively you can rebuild the app on the server anytime you change them. + +>Note: You must create custom environment variables beginning with `REACT_APP_`. Any other variables except `NODE_ENV` will be ignored to avoid accidentally [exposing a private key on the machine that could have the same name](https://github.com/facebookincubator/create-react-app/issues/865#issuecomment-252199527). Changing any environment variables will require you to restart the development server if it is running. ->Note: Changing any environment variables will require you to restart the development server if it is running. +These environment variables will be defined for you on `process.env`. For example, having an environment +variable named `REACT_APP_SECRET_CODE` will be exposed in your JS as `process.env.REACT_APP_SECRET_CODE`. + +There is also a special built-in environment variable called `NODE_ENV`. You can read it from `process.env.NODE_ENV`. When you run `npm start`, it is always equal to `'development'`, when you run `npm test` it is always equal to `'test'`, and when you run `npm run build` to make a production bundle, it is always equal to `'production'`. **You cannot override `NODE_ENV` manually.** This prevents developers from accidentally deploying a slow development build to production. These environment variables can be useful for displaying information conditionally based on where the project is deployed or consuming sensitive data that lives outside of version control. @@ -541,6 +692,10 @@ When you load the app in the browser and inspect the `<input>`, you will see its </div> ``` +The above form is looking for a variable called `REACT_APP_SECRET_CODE` from the environment. In order to consume this +value, we need to have it defined in the environment. This can be done using two ways: either in your shell or in +a `.env` file. Both of these ways are described in the next few sections. + Having access to the `NODE_ENV` is also useful for performing actions conditionally: ```js @@ -549,13 +704,26 @@ if (process.env.NODE_ENV !== 'production') { } ``` -The above form is looking for a variable called `REACT_APP_SECRET_CODE` from the environment. In order to consume this -value, we need to have it defined in the environment. This can be done using two ways: either in your shell or in -a `.env` file. +When you compile the app with `npm run build`, the minification step will strip out this condition, and the resulting bundle will be smaller. + +### Referencing Environment Variables in the HTML + +>Note: this feature is available with `react-scripts@0.9.0` and higher. + +You can also access the environment variables starting with `REACT_APP_` in the `public/index.html`. For example: + +```html +<title>%REACT_APP_WEBSITE_NAME% +``` + +Note that the caveats from the above section apply: + +* Apart from a few built-in variables (`NODE_ENV` and `PUBLIC_URL`), variable names must start with `REACT_APP_` to work. +* The environment variables are injected at build time. If you need to inject them at runtime, [follow this approach instead](#generating-dynamic-meta-tags-on-the-server). ### Adding Temporary Environment Variables In Your Shell -Defining environment variables can vary between OSes. It's also important to know that this manner is temporary for the +Defining environment variables can vary between OSes. It’s also important to know that this manner is temporary for the life of the shell session. #### Windows (cmd.exe) @@ -566,7 +734,7 @@ set REACT_APP_SECRET_CODE=abcdef&&npm start (Note: the lack of whitespace is intentional.) -#### Linux, OS X (Bash) +#### Linux, macOS (Bash) ```bash REACT_APP_SECRET_CODE=abcdef npm start @@ -611,12 +779,12 @@ These tutorials will help you to integrate your app with an API backend running using `fetch()` to access it. ### Node -Check out [this tutorial](https://www.fullstackreact.com/articles/using-create-react-app-with-a-server/). +Check out [this tutorial](https://www.fullstackreact.com/articles/using-create-react-app-with-a-server/). You can find the companion GitHub repository [here](https://github.com/fullstackreact/food-lookup-demo). ### Ruby on Rails -Check out [this tutorial](https://www.fullstackreact.com/articles/how-to-get-create-react-app-to-work-with-your-rails-api/). +Check out [this tutorial](https://www.fullstackreact.com/articles/how-to-get-create-react-app-to-work-with-your-rails-api/). You can find the companion GitHub repository [here](https://github.com/fullstackreact/food-lookup-demo-rails). ## Proxying API Requests in Development @@ -650,7 +818,7 @@ Fetch API cannot load http://localhost:4000/api/todos. No 'Access-Control-Allow- Keep in mind that `proxy` only has effect in development (with `npm start`), and it is up to you to ensure that URLs like `/api/todos` point to the right thing in production. You don’t have to use the `/api` prefix. Any unrecognized request without a `text/html` accept header will be redirected to the specified `proxy`. -Currently the `proxy` option only handles HTTP requests, and it won’t proxy WebSocket connections.
+The `proxy` option supports HTTP, HTTPS and WebSocket connections.
If the `proxy` option is **not** flexible enough for you, alternatively you can: * Enable CORS on your server ([here’s how to do it for Express](http://enable-cors.org/server_expressjs.html)). @@ -672,7 +840,7 @@ set HTTPS=true&&npm start (Note: the lack of whitespace is intentional.) -#### Linux, OS X (Bash) +#### Linux, macOS (Bash) ```bash HTTPS=true npm start @@ -688,14 +856,39 @@ Since Create React App doesn’t support server rendering, you might be wonderin - - + + ``` -Then, on the server, regardless of the backend you use, you can read `index.html` into memory and replace `%OG_TITLE%`, `%OG_DESCRIPTION%`, and any other placeholders with values depending on the current URL. Just make sure to sanitize and escape the interpolated values so that they are safe to embed into HTML! +Then, on the server, regardless of the backend you use, you can read `index.html` into memory and replace `__OG_TITLE__`, `__OG_DESCRIPTION__`, and any other placeholders with values depending on the current URL. Just make sure to sanitize and escape the interpolated values so that they are safe to embed into HTML! If you use a Node server, you can even share the route matching logic between the client and the server. However duplicating it also works fine in simple cases. +## Pre-Rendering into Static HTML Files + +If you’re hosting your `build` with a static hosting provider you can use [react-snapshot](https://www.npmjs.com/package/react-snapshot) to generate HTML pages for each route, or relative link, in your application. These pages will then seamlessly become active, or “hydrated”, when the JavaScript bundle has loaded. + +There are also opportunities to use this outside of static hosting, to take the pressure off the server when generating and caching routes. + +The primary benefit of pre-rendering is that you get the core content of each page _with_ the HTML payload—regardless of whether or not your JavaScript bundle successfully downloads. It also increases the likelihood that each route of your application will be picked up by search engines. + +You can read more about [zero-configuration pre-rendering (also called snapshotting) here](https://medium.com/superhighfives/an-almost-static-stack-6df0a2791319). + +## Injecting Data from the Server into the Page + +Similarly to the previous section, you can leave some placeholders in the HTML that inject global variables, for example: + +```js + + + + +``` + +Then, on the server, you can replace `__SERVER_DATA__` with a JSON of real data right before sending the response. The client code can then read `window.SERVER_DATA` to use it. **Make sure to [sanitize the JSON before sending it to the client](https://medium.com/node-security/the-most-common-xss-vulnerability-in-react-js-applications-2bdffbcc1fa0) as it makes your app vulnerable to XSS attacks.** + ## Running Tests >Note: this feature is available with `react-scripts@0.3.0` and higher.
@@ -752,8 +945,8 @@ it('sums numbers', () => { }); ``` -All `expect()` matchers supported by Jest are [extensively documented here](http://facebook.github.io/jest/docs/api.html#expect-value).
-You can also use [`jest.fn()` and `expect(fn).toBeCalled()`](http://facebook.github.io/jest/docs/api.html#tobecalled) to create “spies” or mock functions. +All `expect()` matchers supported by Jest are [extensively documented here](http://facebook.github.io/jest/docs/expect.html).
+You can also use [`jest.fn()` and `expect(fn).toBeCalled()`](http://facebook.github.io/jest/docs/expect.html#tohavebeencalled) to create “spies” or mock functions. ### Testing Components @@ -779,7 +972,7 @@ When you encounter bugs caused by changing components, you will gain a deeper in 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: ```sh -npm install --save-dev enzyme react-addons-test-utils +npm install --save-dev enzyme react-test-renderer ``` ```js @@ -811,9 +1004,27 @@ it('renders welcome message', () => { }); ``` -All Jest matchers are [extensively documented here](http://facebook.github.io/jest/docs/api.html#expect-value).
+All Jest matchers are [extensively documented here](http://facebook.github.io/jest/docs/expect.html).
Nevertheless you can use a third-party assertion library like [Chai](http://chaijs.com/) if you want to, as described below. +Additionally, you might find [jest-enzyme](https://github.com/blainekasten/enzyme-matchers) helpful to simplify your tests with readable matchers. The above `contains` code can be written simpler with jest-enzyme. + +```js +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`. **Note that currently only version 2.x is compatible with Create React App.** + +```sh +npm install --save-dev jest-enzyme@2.x +``` + +```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). @@ -900,7 +1111,7 @@ set CI=true&&npm run build (Note: the lack of whitespace is intentional.) -##### Linux, OS X (Bash) +##### Linux, macOS (Bash) ```bash CI=true npm test @@ -949,7 +1160,7 @@ Snapshot testing is a feature of Jest that automatically generates text snapshot ### Editor Integration -If you use [Visual Studio Code](https://code.visualstudio.com), there is a [Jest extension](https://github.com/orta/vscode-jest) which works with Create React App out of the box. This provides a lot of IDE-like features while using a text editor: showing the status of a test run with potential fail messages inline, starting and stopping the watcher automatically, and offering one-click snapshot updates. +If you use [Visual Studio Code](https://code.visualstudio.com), there is a [Jest extension](https://github.com/orta/vscode-jest) which works with Create React App out of the box. This provides a lot of IDE-like features while using a text editor: showing the status of a test run with potential fail messages inline, starting and stopping the watcher automatically, and offering one-click snapshot updates. ![VS Code Jest Preview](https://cloud.githubusercontent.com/assets/49038/20795349/a032308a-b7c8-11e6-9b34-7eeac781003f.png) @@ -964,7 +1175,7 @@ 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 [React Storybook](https://github.com/kadirahq/react-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 [React Storybook](https://github.com/kadirahq/react-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**. ![React Storybook Demo](http://i.imgur.com/7CIAWpB.gif) @@ -999,30 +1210,48 @@ You can turn your React app into a [Progressive Web App](https://developers.goog ## 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..js` are served with the contents of the `/static/js/main..js` file. For example, Python contains a built-in HTTP server that can serve static files: +`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..js` are served with the contents of the `/static/js/main..js` file. + +### Static Server + +For environments using [Node](https://nodejs.org/), the easiest way to handle this would be to install [serve](https://github.com/zeit/serve) and let it handle the rest: ```sh -cd build -python -m SimpleHTTPServer 9000 +npm install -g serve +serve -s build ``` -If you're using [Node](https://nodejs.org/) and [Express](http://expressjs.com/) as a server, it might look like this: +The last command shown above will serve your static site on the port **5000**. Like many of [serve](https://github.com/zeit/serve)’s internal settings, the port can be adjusted using the `-p` or `--port` flags. + +Run this command to get a full list of the options available: + +```sh +serve -h +``` + +### Other Solutions + +You don’t necessarily need a static server in order to run a Create React App project in production. It works just as fine integrated into an existing dynamic one. + +Here’s a programmatic example using [Node](https://nodejs.org/) and [Express](http://expressjs.com/): ```javascript const express = require('express'); const path = require('path'); const app = express(); -app.use(express.static('./build')); +app.use(express.static(path.join(__dirname, 'build'))); app.get('/', function (req, res) { - res.sendFile(path.join(__dirname, './build', 'index.html')); + res.sendFile(path.join(__dirname, 'build', 'index.html')); }); app.listen(9000); ``` -Create React App is not opinionated about your choice of web server. Any static file server will do. The `build` folder with static assets is the only output produced by Create React App. +The choice of your server software isn’t important either. Since Create React App is completely platform-agnostic, there’s no need to explicitly use Node. + +The `build` folder with static assets is the only output produced by Create React App. However this is not quite enough if you use client-side routing. Read the next section if you want to support URLs like `/todos/42` in your single-page app. @@ -1033,14 +1262,25 @@ If you use routers that use the HTML5 [`pushState` history API](https://develope This is because when there is a fresh page load for a `/todos/42`, the server looks for the file `build/todos/42` and does not find it. The server needs to be configured to respond to a request to `/todos/42` by serving `index.html`. For example, we can amend our Express example above to serve `index.html` for any unknown paths: ```diff - app.use(express.static('./build')); + app.use(express.static(path.join(__dirname, 'build'))); -app.get('/', function (req, res) { +app.get('/*', function (req, res) { - res.sendFile(path.join(__dirname, './build', 'index.html')); + res.sendFile(path.join(__dirname, 'build', 'index.html')); }); ``` +If you’re using [Apache](https://httpd.apache.org/), you need to create a `.htaccess` file in the `public` folder that looks like this: + +``` + Options -MultiViews + RewriteEngine On + RewriteCond %{REQUEST_FILENAME} !-f + RewriteRule ^ index.html [QSA,L] +``` + +It will get copied to the `build` folder when you run `npm run build`. + Now requests to `/todos/42` will be handled correctly both in development and in production. ### Building for Relative Paths @@ -1054,12 +1294,27 @@ To override this, specify the `homepage` in your `package.json`, for example: This will let Create React App correctly infer the root path to use in the generated HTML file. +#### Serving the Same Build from Different Paths + +>Note: this feature is available with `react-scripts@0.9.0` and higher. + +If you are not using the HTML5 `pushState` history API or not using client-side routing at all, it is unnecessary to specify the URL from which your app will be served. Instead, you can put this in your `package.json`: + +```js + "homepage": ".", +``` + +This will make sure that all the asset paths are relative to `index.html`. You will then be able to move your app from `http://mywebsite.com` to `http://mywebsite.com/relativepath` or even `http://mywebsite.com/relative/path` without having to rebuild it. + +### Azure + +See [this](https://medium.com/@to_pe/deploying-create-react-app-on-microsoft-azure-c0f6686a4321) blog post on how to deploy your React app to [Microsoft Azure](https://azure.microsoft.com/). ### Firebase -Install the Firebase CLI if you haven't already by running `npm install -g firebase-tools`. Sign up for a [Firebase account](https://console.firebase.google.com/) and create a new project. Run `firebase login` and login with your previous created Firebase account. +Install the Firebase CLI if you haven’t already by running `npm install -g firebase-tools`. Sign up for a [Firebase account](https://console.firebase.google.com/) and create a new project. Run `firebase login` and login with your previous created Firebase account. -Then run the `firebase init` command from your project's root. You need to choose the **Hosting: Configure and deploy Firebase Hosting sites** and choose the Firebase project you created in the previous step. You will need to agree with `database.rules.json` being created, choose `build` as the public directory, and also agree to **Configure as a single-page app** by replying with `y`. +Then run the `firebase init` command from your project’s root. You need to choose the **Hosting: Configure and deploy Firebase Hosting sites** and choose the Firebase project you created in the previous step. You will need to agree with `database.rules.json` being created, choose `build` as the public directory, and also agree to **Configure as a single-page app** by replying with `y`. ```sh === Project Setup @@ -1164,7 +1419,7 @@ Then run: npm run deploy ``` -#### Step 4: Ensure your project's settings use `gh-pages` +#### Step 4: Ensure your project’s settings use `gh-pages` Finally, make sure **GitHub Pages** option in your GitHub project settings is set to use the `gh-pages` branch: @@ -1176,7 +1431,7 @@ You can configure a custom domain with GitHub Pages by adding a `CNAME` file to #### Notes on client-side routing -GitHub Pages doesn't support routers that use the HTML5 `pushState` history API under the hood (for example, React Router using `browserHistory`). This is because when there is a fresh page load for a url like `http://user.github.io/todomvc/todos/42`, where `/todos/42` is a frontend route, the GitHub Pages server returns 404 because it knows nothing of `/todos/42`. If you want to add a router to a project hosted on GitHub Pages, here are a couple of solutions: +GitHub Pages doesn’t support routers that use the HTML5 `pushState` history API under the hood (for example, React Router using `browserHistory`). This is because when there is a fresh page load for a url like `http://user.github.io/todomvc/todos/42`, where `/todos/42` is a frontend route, the GitHub Pages server returns 404 because it knows nothing of `/todos/42`. If you want to add a router to a project hosted on GitHub Pages, here are a couple of solutions: * You could switch from using HTML5 history API to routing with hashes. If you use React Router, you can switch to `hashHistory` for this effect, but the URL will be longer and more verbose (for example, `http://user.github.io/todomvc/#/todos/42?_k=yknaj`). [Read more](https://github.com/reactjs/react-router/blob/master/docs/guides/Histories.md#histories) about different history implementations in React Router. * Alternatively, you can use a trick to teach GitHub Pages to handle 404 by redirecting to your `index.html` page with a special redirect parameter. You would need to add a `404.html` file with the redirection code to the `build` folder before deploying your project, and you’ll need to add code handling the redirect parameter to `index.html`. You can find a detailed explanation of this technique [in this guide](https://github.com/rafrex/spa-github-pages). @@ -1184,29 +1439,48 @@ GitHub Pages doesn't support routers that use the HTML5 `pushState` history API ### Heroku Use the [Heroku Buildpack for Create React App](https://github.com/mars/create-react-app-buildpack).
-You can find instructions in [Deploying React with Zero Configuration](https://blog.heroku.com/deploying-react-with-zero-configuration). +You can find instructions in [Deploying React with Zero Configuration](https://blog.heroku.com/deploying-react-with-zero-configuration). + +#### Resolving Heroku Deployment Errors -#### Resolving "Module not found: Error: Cannot resolve 'file' or 'directory'" +Sometimes `npm run build` works locally but fails during deploy via Heroku. Following are the most common cases. -Sometimes `npm run build` works locally but fails during deploy via Heroku with an error like this: +##### "Module not found: Error: Cannot resolve 'file' or 'directory'" -``` +If you get something like this: + +``` remote: Failed to create a production build. Reason: -remote: Module not found: Error: Cannot resolve 'file' or 'directory' -MyDirectory in /tmp/build_1234/src +remote: Module not found: Error: Cannot resolve 'file' or 'directory' +MyDirectory in /tmp/build_1234/src ``` -This means you need to ensure that the lettercase of the file or directory you `import` matches the one you see on your filesystem or on GitHub. +It means you need to ensure that the lettercase of the file or directory you `import` matches the one you see on your filesystem or on GitHub. This is important because Linux (the operating system used by Heroku) is case sensitive. So `MyDirectory` and `mydirectory` are two distinct directories and thus, even though the project builds locally, the difference in case breaks the `import` statements on Heroku remotes. +##### "Could not find a required file." + +If you exclude or ignore necessary files from the package you will see a error similar this one: + +``` +remote: Could not find a required file. +remote: Name: `index.html` +remote: Searched in: /tmp/build_a2875fc163b209225122d68916f1d4df/public +remote: +remote: npm ERR! Linux 3.13.0-105-generic +remote: npm ERR! argv "/tmp/build_a2875fc163b209225122d68916f1d4df/.heroku/node/bin/node" "/tmp/build_a2875fc163b209225122d68916f1d4df/.heroku/node/bin/npm" "run" "build" +``` + +In this case, ensure that the file is there with the proper lettercase and that’s not ignored on your local `.gitignore` or `~/.gitignore_global`. + ### Modulus See the [Modulus blog post](http://blog.modulus.io/deploying-react-apps-on-modulus) on how to deploy your react app to Modulus. ## Netlify -**To do a manual deploy to Netlify's CDN:** +**To do a manual deploy to Netlify’s CDN:** ```sh npm install netlify-cli @@ -1235,7 +1509,27 @@ When you build the project, Create React App will place the `public` folder cont ### Now -See [this example](https://github.com/xkawi/create-react-app-now) for a zero-configuration single-command deployment with [now](https://zeit.co/now). +[now](https://zeit.co/now) offers a zero-configuration single-command deployment. + +1. Install the `now` command-line tool either via the recommended [desktop tool](https://zeit.co/download) or via node with `npm install -g now`. + +2. Install `serve` by running `npm install --save serve`. + +3. Add this line to `scripts` in `package.json`: + + ``` + "now-start": "serve build/", + ``` + +4. Run `now` from your project directory. You will see a **now.sh** URL in your output like this: + + ``` + > Ready! https://your-project-dirname-tpspyhtdtk.now.sh (copied to clipboard) + ``` + + Paste that URL into your browser when the build is complete, and you will see your deployed app. + +Details are available in [this article.](https://zeit.co/blog/now-static) ### S3 and CloudFront @@ -1243,25 +1537,29 @@ See this [blog post](https://medium.com/@omgwtfmarc/deploying-create-react-app-t ### Surge -Install the Surge CLI if you haven't already by running `npm install -g surge`. Run the `surge` command and log in you or create a new account. You just need to specify the *build* folder and your custom domain, and you are done. +Install the Surge CLI if you haven’t already by running `npm install -g surge`. Run the `surge` command and log in you or create a new account. + +When asked about the project path, make sure to specify the `build` folder, for example: ```sh - email: email@domain.com - password: ******** project path: /path/to/project/build - size: 7 files, 1.8 MB - domain: create-react-app.surge.sh - upload: [====================] 100%, eta: 0.0s - propagate on CDN: [====================] 100% - plan: Free - users: email@domain.com - IP Address: X.X.X.X - - Success! Project is published and running at create-react-app.surge.sh ``` Note that in order to support routers that use HTML5 `pushState` API, you may want to rename the `index.html` in your build folder to `200.html` before deploying to Surge. This [ensures that every URL falls back to that file](https://surge.sh/help/adding-a-200-page-for-client-side-routing). +## Advanced Configuration + +You can adjust various development and production settings by setting environment variables in your shell or with [.env](#adding-development-environment-variables-in-env). + +Variable | Development | Production | Usage +:--- | :---: | :---: | :--- +BROWSER | :white_check_mark: | :x: | By default, Create React App will open the default system browser, favoring Chrome on macOS. Specify a [browser](https://github.com/sindresorhus/opn#app) to override this behavior, or set it to `none` to disable it completely. +HOST | :white_check_mark: | :x: | By default, the development web server binds to `localhost`. You may use this variable to specify a different host. +PORT | :white_check_mark: | :x: | By default, the development web server will attempt to listen on port 3000 or prompt you to attempt the next available port. You may use this variable to specify a different port. +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. + ## Troubleshooting ### `npm start` doesn’t detect changes @@ -1273,7 +1571,8 @@ If this doesn’t happen, try one of the following workarounds: * If the watcher doesn’t see a file called `index.js` and you’re referencing it by the folder name, you [need to restart the watcher](https://github.com/facebookincubator/create-react-app/issues/1164) due to a Webpack bug. * Some editors like Vim and IntelliJ have a “safe write” feature that currently breaks the watcher. You will need to disable it. Follow the instructions in [“Working with editors supporting safe write”](https://webpack.github.io/docs/webpack-dev-server.html#working-with-editors-ides-supporting-safe-write). * If your project path contains parentheses, try moving the project to a path without them. This is caused by a [Webpack watcher bug](https://github.com/webpack/watchpack/issues/42). -* On Linux and OS X, you might need to [tweak system settings](https://webpack.github.io/docs/troubleshooting.html#not-enough-watchers) to allow more watchers. +* On Linux and macOS, you might need to [tweak system settings](https://webpack.github.io/docs/troubleshooting.html#not-enough-watchers) to allow more watchers. +* If the project runs inside a virtual machine such as (a Vagrant provisioned) VirtualBox, create an `.env` file in your project directory if it doesn’t exist, and add `CHOKIDAR_USEPOLLING=true` to it. This ensures that the next time you run `npm start`, the watcher uses the polling mode, as necessary inside a VM. If none of these solutions help please leave a comment [in this thread](https://github.com/facebookincubator/create-react-app/issues/659). @@ -1297,7 +1596,7 @@ brew reinstall watchman You can find [other installation methods](https://facebook.github.io/watchman/docs/install.html#build-install) on the Watchman documentation page. -If this still doesn't help, try running `launchctl unload -F ~/Library/LaunchAgents/com.github.facebook.watchman.plist`. +If this still doesn’t help, try running `launchctl unload -F ~/Library/LaunchAgents/com.github.facebook.watchman.plist`. There are also reports that *uninstalling* Watchman fixes the issue. So if nothing else helps, remove it from your system and try again. @@ -1308,7 +1607,7 @@ It is reported that `npm run build` can fail on machines with no swap space, whi ### `npm run build` fails on Heroku This may be a problem with case sensitive filenames. -Please refer to [this section](#resolving-module-not-found-error-cannot-resolve-file-or-directory). +Please refer to [this section](#resolving-heroku-deployment-errors). ## Something Missing? diff --git a/packages/react-scripts/template/gitignore b/packages/react-scripts/template/gitignore index 45edb3c7d80..927d17bb9c5 100644 --- a/packages/react-scripts/template/gitignore +++ b/packages/react-scripts/template/gitignore @@ -1,4 +1,4 @@ -# See http://help.github.com/ignore-files/ for more about ignoring files. +# See https://help.github.com/ignore-files/ for more about ignoring files. # dependencies /node_modules @@ -12,4 +12,7 @@ # misc .DS_Store .env -npm-debug.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* + diff --git a/packages/react-scripts/template/public/index.html b/packages/react-scripts/template/public/index.html index aab5e3b00ce..7f3e83f4e43 100644 --- a/packages/react-scripts/template/public/index.html +++ b/packages/react-scripts/template/public/index.html @@ -2,7 +2,7 @@ - + diff --git a/packages/react-scripts/utils/createJestConfig.js b/packages/react-scripts/utils/createJestConfig.js index f1c67c018f1..fb4fc1276a3 100644 --- a/packages/react-scripts/utils/createJestConfig.js +++ b/packages/react-scripts/utils/createJestConfig.js @@ -7,6 +7,8 @@ * of patent rights can be found in the PATENTS file in the same directory. */ +'use strict'; + // Note: this file does not exist after ejecting. const fs = require('fs'); diff --git a/tasks/cra.sh b/tasks/cra.sh index f63fd464b0c..c94559406d2 100755 --- a/tasks/cra.sh +++ b/tasks/cra.sh @@ -52,7 +52,7 @@ root_path=$PWD # ****************************************************************************** # Install all our packages -$root_path/node_modules/.bin/lerna bootstrap +"$root_path"/node_modules/.bin/lerna bootstrap cd packages/react-scripts @@ -61,10 +61,10 @@ cp package.json package.json.orig # Replace own dependencies (those in the `packages` dir) with the local paths # of those packages. -node $root_path/tasks/replace-own-deps.js +node "$root_path"/tasks/replace-own-deps.js # Finally, pack react-scripts -scripts_path=$root_path/packages/react-scripts/`npm pack` +scripts_path="$root_path"/packages/react-scripts/`npm pack` # Restore package.json rm package.json @@ -79,8 +79,8 @@ mv package.json.orig package.json yarn cache clean || true # Go back to the root directory and run the command from here -cd $root_path -node packages/create-react-app/index.js --scripts-version=$scripts_path "$@" +cd "$root_path" +node packages/create-react-app/index.js --scripts-version="$scripts_path" "$@" # Cleanup cleanup diff --git a/tasks/e2e-installs.sh b/tasks/e2e-installs.sh index 7bb4c0392f2..cacf4f96e1f 100755 --- a/tasks/e2e-installs.sh +++ b/tasks/e2e-installs.sh @@ -21,8 +21,8 @@ temp_app_path=`mktemp -d 2>/dev/null || mktemp -d -t 'temp_app_path'` function cleanup { echo 'Cleaning up.' - cd $root_path - rm -rf $temp_cli_path $temp_app_path + cd "$root_path" + rm -rf "$temp_cli_path" "$temp_app_path" } # Error messages are redirected to stderr @@ -39,6 +39,13 @@ function handle_exit { exit } +# Check for the existence of one or more files. +function exists { + for f in $*; do + test -e "$f" + done +} + function create_react_app { node "$temp_cli_path"/node_modules/create-react-app/index.js $* } @@ -61,7 +68,7 @@ npm install if [ "$USE_YARN" = "yes" ] then # Install Yarn so that the test can use it to install packages. - npm install -g yarn@0.17.10 # TODO: remove version when https://github.com/yarnpkg/yarn/issues/2142 is fixed. + npm install -g yarn yarn cache clean fi @@ -70,54 +77,92 @@ fi # ****************************************************************************** # Pack CLI -cd $root_path/packages/create-react-app +cd "$root_path"/packages/create-react-app cli_path=$PWD/`npm pack` # Install the CLI in a temporary location -cd $temp_cli_path -npm install $cli_path +cd "$temp_cli_path" +npm install "$cli_path" # ****************************************************************************** # Test --scripts-version with a version number # ****************************************************************************** -cd $temp_app_path +cd "$temp_app_path" create_react_app --scripts-version=0.4.0 test-app-version-number cd test-app-version-number # Check corresponding scripts version is installed. -test -e node_modules/react-scripts +exists node_modules/react-scripts grep '"version": "0.4.0"' node_modules/react-scripts/package.json # ****************************************************************************** # Test --scripts-version with a tarball url # ****************************************************************************** -cd $temp_app_path +cd "$temp_app_path" create_react_app --scripts-version=https://registry.npmjs.org/react-scripts/-/react-scripts-0.4.0.tgz test-app-tarball-url cd test-app-tarball-url # Check corresponding scripts version is installed. -test -e node_modules/react-scripts +exists node_modules/react-scripts grep '"version": "0.4.0"' node_modules/react-scripts/package.json # ****************************************************************************** # Test --scripts-version with a custom fork of react-scripts # ****************************************************************************** -cd $temp_app_path +cd "$temp_app_path" create_react_app --scripts-version=react-scripts-fork test-app-fork cd test-app-fork # Check corresponding scripts version is installed. -test -e node_modules/react-scripts-fork +exists node_modules/react-scripts-fork + +# ****************************************************************************** +# Test project folder is deleted on failing package installation +# ****************************************************************************** + +cd "$temp_app_path" +# we will install a non-existing package to simulate a failed installataion. +create_react_app --scripts-version=`date +%s` test-app-should-not-exist || true +# confirm that the project folder was deleted +test ! -d test-app-should-not-exist + +# ****************************************************************************** +# Test project folder is not deleted when creating app over existing folder +# ****************************************************************************** + +cd "$temp_app_path" +mkdir test-app-should-remain +echo '## Hello' > ./test-app-should-remain/README.md +# we will install a non-existing package to simulate a failed installataion. +create_react_app --scripts-version=`date +%s` test-app-should-remain || true +# confirm the file exist +test -e test-app-should-remain/README.md +# confirm only README.md is the only file in the directory +if [ "$(ls -1 ./test-app-should-remain | wc -l | tr -d '[:space:]')" != "1" ]; then + false +fi + +# ****************************************************************************** +# Test --scripts-version with a scoped fork tgz of react-scripts +# ****************************************************************************** + +cd $temp_app_path +curl "https://registry.npmjs.org/@enoah_netzach/react-scripts/-/react-scripts-0.9.0.tgz" -o enoah-scripts-0.9.0.tgz +create_react_app --scripts-version=$temp_app_path/enoah-scripts-0.9.0.tgz test-app-scoped-fork-tgz +cd test-app-scoped-fork-tgz + +# Check corresponding scripts version is installed. +exists node_modules/@enoah_netzach/react-scripts # ****************************************************************************** # Test nested folder path as the project name # ****************************************************************************** #Testing a path that exists -cd $temp_app_path +cd "$temp_app_path" mkdir test-app-nested-paths-t1 cd test-app-nested-paths-t1 mkdir -p test-app-nested-paths-t1/aa/bb/cc/dd @@ -126,13 +171,13 @@ cd test-app-nested-paths-t1/aa/bb/cc/dd npm start -- --smoke-test #Testing a path that does not exist -cd $temp_app_path +cd "$temp_app_path" create_react_app test-app-nested-paths-t2/aa/bb/cc/dd cd test-app-nested-paths-t2/aa/bb/cc/dd npm start -- --smoke-test #Testing a path that is half exists -cd $temp_app_path +cd "$temp_app_path" mkdir -p test-app-nested-paths-t3/aa create_react_app test-app-nested-paths-t3/aa/bb/cc/dd cd test-app-nested-paths-t3/aa/bb/cc/dd diff --git a/tasks/e2e-kitchensink.sh b/tasks/e2e-kitchensink.sh index 892230ab747..0d9c8372bcb 100755 --- a/tasks/e2e-kitchensink.sh +++ b/tasks/e2e-kitchensink.sh @@ -14,15 +14,18 @@ # Start in tasks/ even if run from root directory cd "$(dirname "$0")" -# CLI and app temporary locations +# CLI, app, and test module temporary locations # http://unix.stackexchange.com/a/84980 temp_cli_path=`mktemp -d 2>/dev/null || mktemp -d -t 'temp_cli_path'` temp_app_path=`mktemp -d 2>/dev/null || mktemp -d -t 'temp_app_path'` +temp_module_path=`mktemp -d 2>/dev/null || mktemp -d -t 'temp_module_path'` function cleanup { echo 'Cleaning up.' - cd $root_path - rm -rf $temp_cli_path $temp_app_path + ps -ef | grep 'react-scripts' | grep -v grep | awk '{print $2}' | xargs kill -s 9 + cd "$root_path" + # TODO: fix "Device or resource busy" and remove ``|| $CI` + rm -rf "$temp_cli_path" "$temp_app_path" "$temp_module_path" || $CI } # Error messages are redirected to stderr @@ -40,7 +43,14 @@ function handle_exit { } function create_react_app { - node "$temp_cli_path"/node_modules/create-react-app/index.js $* + node "$temp_cli_path"/node_modules/create-react-app/index.js "$@" +} + +# Check for the existence of one or more files. +function exists { + for f in $*; do + test -e "$f" + done } # Exit the script with a helpful error message when any error is encountered @@ -61,7 +71,7 @@ npm install if [ "$USE_YARN" = "yes" ] then # Install Yarn so that the test can use it to install packages. - npm install -g yarn@0.17.10 # TODO: remove version when https://github.com/yarnpkg/yarn/issues/2142 is fixed. + npm install -g yarn yarn cache clean fi @@ -70,21 +80,21 @@ fi # ****************************************************************************** # Pack CLI -cd $root_path/packages/create-react-app +cd "$root_path"/packages/create-react-app cli_path=$PWD/`npm pack` # Go to react-scripts -cd $root_path/packages/react-scripts +cd "$root_path"/packages/react-scripts # Save package.json because we're going to touch it cp package.json package.json.orig # Replace own dependencies (those in the `packages` dir) with the local paths # of those packages. -node $root_path/tasks/replace-own-deps.js +node "$root_path"/tasks/replace-own-deps.js # Finally, pack react-scripts -scripts_path=$root_path/packages/react-scripts/`npm pack` +scripts_path="$root_path"/packages/react-scripts/`npm pack` # Restore package.json rm package.json @@ -95,12 +105,16 @@ mv package.json.orig package.json # ****************************************************************************** # Install the CLI in a temporary location -cd $temp_cli_path -npm install $cli_path +cd "$temp_cli_path" +npm install "$cli_path" # Install the app in a temporary location cd $temp_app_path -create_react_app --scripts-version=$scripts_path --internal-testing-template=$root_path/packages/react-scripts/fixtures/kitchensink test-kitchensink +create_react_app --scripts-version="$scripts_path" --internal-testing-template="$root_path"/packages/react-scripts/fixtures/kitchensink test-kitchensink + +# Install the test module +cd "$temp_module_path" +npm install test-integrity@^2.0.1 # ****************************************************************************** # Now that we used create-react-app to create an app depending on react-scripts, @@ -108,18 +122,29 @@ create_react_app --scripts-version=$scripts_path --internal-testing-template=$ro # ****************************************************************************** # Enter the app directory -cd test-kitchensink +cd "$temp_app_path/test-kitchensink" + +# Link to our preset +npm link "$root_path"/packages/babel-preset-react-app + +# Link to test module +npm link "$temp_module_path/node_modules/test-integrity" # Test the build -NODE_PATH=src REACT_APP_SHELL_ENV_MESSAGE=fromtheshell npm run build +REACT_APP_SHELL_ENV_MESSAGE=fromtheshell \ + NODE_PATH=src \ + PUBLIC_URL=http://www.example.org/spa/ \ + npm run build + # Check for expected output -test -e build/*.html -test -e build/static/js/main.*.js +exists build/*.html +exists build/static/js/main.*.js # Unit tests REACT_APP_SHELL_ENV_MESSAGE=fromtheshell \ CI=true \ NODE_PATH=src \ + NODE_ENV=test \ npm test -- --no-cache --testPathPattern="/src/" # Test "development" environment @@ -128,45 +153,65 @@ PORT=3001 \ REACT_APP_SHELL_ENV_MESSAGE=fromtheshell \ NODE_PATH=src \ nohup npm start &>$tmp_server_log & -grep -q 'The app is running at:' <(tail -f $tmp_server_log) +while true +do + if grep -q 'The app is running at:' $tmp_server_log; then + break + else + sleep 1 + fi +done E2E_URL="http://localhost:3001" \ REACT_APP_SHELL_ENV_MESSAGE=fromtheshell \ CI=true NODE_PATH=src \ - node node_modules/.bin/mocha --require babel-register --require babel-polyfill integration/*.test.js + NODE_ENV=development \ + node_modules/.bin/mocha --require babel-register --require babel-polyfill integration/*.test.js # Test "production" environment E2E_FILE=./build/index.html \ CI=true \ NODE_PATH=src \ + NODE_ENV=production \ + PUBLIC_URL=http://www.example.org/spa/ \ node_modules/.bin/mocha --require babel-register --require babel-polyfill integration/*.test.js # ****************************************************************************** # Finally, let's check that everything still works after ejecting. # ****************************************************************************** +# Unlink our preset +npm unlink "$root_path"/packages/babel-preset-react-app + # Eject... echo yes | npm run eject # ...but still link to the local packages -npm link $root_path/packages/babel-preset-react-app -npm link $root_path/packages/eslint-config-react-app -npm link $root_path/packages/react-dev-utils -npm link $root_path/packages/react-scripts +npm link "$root_path"/packages/babel-preset-react-app +npm link "$root_path"/packages/eslint-config-react-app +npm link "$root_path"/packages/react-dev-utils +npm link "$root_path"/packages/react-scripts -# ...and we need to remove template's .babelrc +# ...and we need to remove template's .babelrc rm .babelrc +# Link to test module +npm link "$temp_module_path/node_modules/test-integrity" # Test the build -NODE_PATH=src REACT_APP_SHELL_ENV_MESSAGE=fromtheshell npm run build +REACT_APP_SHELL_ENV_MESSAGE=fromtheshell \ + NODE_PATH=src \ + PUBLIC_URL=http://www.example.org/spa/ \ + npm run build + # Check for expected output -test -e build/*.html -test -e build/static/js/main.*.js +exists build/*.html +exists build/static/js/main.*.js # Unit tests REACT_APP_SHELL_ENV_MESSAGE=fromtheshell \ CI=true \ NODE_PATH=src \ - npm test -- --no-cache --testPathPattern="/src/" + NODE_ENV=test \ + npm test -- --no-cache --testPathPattern='/src/' # Test "development" environment tmp_server_log=`mktemp` @@ -174,11 +219,18 @@ PORT=3002 \ REACT_APP_SHELL_ENV_MESSAGE=fromtheshell \ NODE_PATH=src \ nohup npm start &>$tmp_server_log & -grep -q 'The app is running at:' <(tail -f $tmp_server_log) +while true +do + if grep -q 'The app is running at:' $tmp_server_log; then + break + else + sleep 1 + fi +done E2E_URL="http://localhost:3002" \ REACT_APP_SHELL_ENV_MESSAGE=fromtheshell \ CI=true NODE_PATH=src \ - NODE_ENV=production \ + NODE_ENV=development \ node_modules/.bin/mocha --require babel-register --require babel-polyfill integration/*.test.js # Test "production" environment @@ -186,6 +238,7 @@ E2E_FILE=./build/index.html \ CI=true \ NODE_ENV=production \ NODE_PATH=src \ + PUBLIC_URL=http://www.example.org/spa/ \ node_modules/.bin/mocha --require babel-register --require babel-polyfill integration/*.test.js # Cleanup diff --git a/tasks/e2e-simple.sh b/tasks/e2e-simple.sh index f8baf743de7..d733656d866 100755 --- a/tasks/e2e-simple.sh +++ b/tasks/e2e-simple.sh @@ -21,10 +21,10 @@ temp_app_path=`mktemp -d 2>/dev/null || mktemp -d -t 'temp_app_path'` function cleanup { echo 'Cleaning up.' - cd $root_path + cd "$root_path" # Uncomment when snapshot testing is enabled by default: # rm ./packages/react-scripts/template/src/__snapshots__/App.test.js.snap - rm -rf $temp_cli_path $temp_app_path + rm -rf "$temp_cli_path" $temp_app_path } # Error messages are redirected to stderr @@ -42,7 +42,14 @@ function handle_exit { } function create_react_app { - node "$temp_cli_path"/node_modules/create-react-app/index.js $* + node "$temp_cli_path"/node_modules/create-react-app/index.js "$@" +} + +# Check for the existence of one or more files. +function exists { + for f in $*; do + test -e "$f" + done } # Exit the script with a helpful error message when any error is encountered @@ -58,25 +65,39 @@ set -x cd .. root_path=$PWD +# Prevent lerna bootstrap, we only want top-level dependencies +cp package.json package.json.bak +grep -v "lerna bootstrap" package.json > temp && mv temp package.json +npm install +mv package.json.bak package.json + +# We need to install create-react-app deps to test it +cd "$root_path"/packages/create-react-app npm install +cd "$root_path" # If the node version is < 4, the script should just give an error. -if [ `node --version | sed -e 's/^v//' -e 's/\..\+//g'` -lt 4 ] +if [[ `node --version | sed -e 's/^v//' -e 's/\..*//g'` -lt 4 ]] then cd $temp_app_path err_output=`node "$root_path"/packages/create-react-app/index.js test-node-version 2>&1 > /dev/null || echo ''` [[ $err_output =~ You\ are\ running\ Node ]] && exit 0 || exit 1 fi +# Still use npm install instead of directly calling lerna bootstrap to test +# postinstall script functionality (one npm install should result in a working +# project) +npm install + if [ "$USE_YARN" = "yes" ] then # Install Yarn so that the test can use it to install packages. - npm install -g yarn@0.17.10 # TODO: remove version when https://github.com/yarnpkg/yarn/issues/2142 is fixed. + npm install -g yarn yarn cache clean fi # Lint own code -./node_modules/.bin/eslint --ignore-path .gitignore ./ +./node_modules/.bin/eslint --max-warnings 0 . # ****************************************************************************** # First, test the create-react-app development environment. @@ -86,16 +107,16 @@ fi # Test local build command npm run build # Check for expected output -test -e build/*.html -test -e build/static/js/*.js -test -e build/static/css/*.css -test -e build/static/media/*.svg -test -e build/favicon.ico +exists build/*.html +exists build/static/js/*.js +exists build/static/css/*.css +exists build/static/media/*.svg +exists build/favicon.ico # Run tests with CI flag CI=true npm test # Uncomment when snapshot testing is enabled by default: -# test -e template/src/__snapshots__/App.test.js.snap +# exists template/src/__snapshots__/App.test.js.snap # Test local start command npm start -- --smoke-test @@ -105,21 +126,21 @@ npm start -- --smoke-test # ****************************************************************************** # Pack CLI -cd $root_path/packages/create-react-app +cd "$root_path"/packages/create-react-app cli_path=$PWD/`npm pack` # Go to react-scripts -cd $root_path/packages/react-scripts +cd "$root_path"/packages/react-scripts # Save package.json because we're going to touch it cp package.json package.json.orig # Replace own dependencies (those in the `packages` dir) with the local paths # of those packages. -node $root_path/tasks/replace-own-deps.js +node "$root_path"/tasks/replace-own-deps.js # Finally, pack react-scripts -scripts_path=$root_path/packages/react-scripts/`npm pack` +scripts_path="$root_path"/packages/react-scripts/`npm pack` # Restore package.json rm package.json @@ -130,38 +151,96 @@ mv package.json.orig package.json # ****************************************************************************** # Install the CLI in a temporary location -cd $temp_cli_path -npm install $cli_path +cd "$temp_cli_path" + +# Initialize package.json before installing the CLI because npm will not install +# the CLI properly in the temporary location if it is missing. +npm init --yes + +# Now we can install the CLI from the local package. +npm install "$cli_path" # Install the app in a temporary location cd $temp_app_path -create_react_app --scripts-version=$scripts_path test-app +create_react_app --scripts-version="$scripts_path" test-app # ****************************************************************************** # Now that we used create-react-app to create an app depending on react-scripts, # let's make sure all npm scripts are in the working state. # ****************************************************************************** +function verify_env_url { + # Backup package.json because we're going to make it dirty + cp package.json package.json.orig + + # Test default behavior + grep -F -R --exclude=*.map "\"/static/" build/ -q; test $? -eq 0 || exit 1 + + # Test relative path build + awk -v n=2 -v s=" \"homepage\": \".\"," 'NR == n {print s} {print}' package.json > tmp && mv tmp package.json + + npm run build + # Disabled until this can be tested + # grep -F -R --exclude=*.map "../../static/" build/ -q; test $? -eq 0 || exit 1 + grep -F -R --exclude=*.map "\"./static/" build/ -q; test $? -eq 0 || exit 1 + grep -F -R --exclude=*.map "\"/static/" build/ -q; test $? -eq 1 || exit 1 + + PUBLIC_URL="/anabsolute" npm run build + grep -F -R --exclude=*.map "/anabsolute/static/" build/ -q; test $? -eq 0 || exit 1 + grep -F -R --exclude=*.map "\"/static/" build/ -q; test $? -eq 1 || exit 1 + + # Test absolute path build + sed "2s/.*/ \"homepage\": \"\/testingpath\",/" package.json > tmp && mv tmp package.json + + npm run build + grep -F -R --exclude=*.map "/testingpath/static/" build/ -q; test $? -eq 0 || exit 1 + grep -F -R --exclude=*.map "\"/static/" build/ -q; test $? -eq 1 || exit 1 + + PUBLIC_URL="https://www.example.net/overridetest" npm run build + grep -F -R --exclude=*.map "https://www.example.net/overridetest/static/" build/ -q; test $? -eq 0 || exit 1 + grep -F -R --exclude=*.map "\"/static/" build/ -q; test $? -eq 1 || exit 1 + grep -F -R --exclude=*.map "testingpath/static" build/ -q; test $? -eq 1 || exit 1 + + # Test absolute url build + sed "2s/.*/ \"homepage\": \"https:\/\/www.example.net\/testingpath\",/" package.json > tmp && mv tmp package.json + + npm run build + grep -F -R --exclude=*.map "/testingpath/static/" build/ -q; test $? -eq 0 || exit 1 + grep -F -R --exclude=*.map "\"/static/" build/ -q; test $? -eq 1 || exit 1 + + PUBLIC_URL="https://www.example.net/overridetest" npm run build + grep -F -R --exclude=*.map "https://www.example.net/overridetest/static/" build/ -q; test $? -eq 0 || exit 1 + grep -F -R --exclude=*.map "\"/static/" build/ -q; test $? -eq 1 || exit 1 + grep -F -R --exclude=*.map "testingpath/static" build/ -q; test $? -eq 1 || exit 1 + + # Restore package.json + rm package.json + mv package.json.orig package.json +} + # Enter the app directory cd test-app # Test the build npm run build # Check for expected output -test -e build/*.html -test -e build/static/js/*.js -test -e build/static/css/*.css -test -e build/static/media/*.svg -test -e build/favicon.ico +exists build/*.html +exists build/static/js/*.js +exists build/static/css/*.css +exists build/static/media/*.svg +exists build/favicon.ico # Run tests with CI flag CI=true npm test # Uncomment when snapshot testing is enabled by default: -# test -e src/__snapshots__/App.test.js.snap +# exists src/__snapshots__/App.test.js.snap # Test the server npm start -- --smoke-test +# Test environment handling +verify_env_url + # ****************************************************************************** # Finally, let's check that everything still works after ejecting. # ****************************************************************************** @@ -170,19 +249,19 @@ npm start -- --smoke-test echo yes | npm run eject # ...but still link to the local packages -npm link $root_path/packages/babel-preset-react-app -npm link $root_path/packages/eslint-config-react-app -npm link $root_path/packages/react-dev-utils -npm link $root_path/packages/react-scripts +npm link "$root_path"/packages/babel-preset-react-app +npm link "$root_path"/packages/eslint-config-react-app +npm link "$root_path"/packages/react-dev-utils +npm link "$root_path"/packages/react-scripts # Test the build npm run build # Check for expected output -test -e build/*.html -test -e build/static/js/*.js -test -e build/static/css/*.css -test -e build/static/media/*.svg -test -e build/favicon.ico +exists build/*.html +exists build/static/js/*.js +exists build/static/css/*.css +exists build/static/media/*.svg +exists build/favicon.ico # Run tests, overring the watch option to disable it. # `CI=true npm test` won't work here because `npm test` becomes just `jest`. @@ -190,11 +269,13 @@ test -e build/favicon.ico # `scripts/test.js` survive ejection (right now it doesn't). npm test -- --watch=no # Uncomment when snapshot testing is enabled by default: -# test -e src/__snapshots__/App.test.js.snap +# exists src/__snapshots__/App.test.js.snap # Test the server npm start -- --smoke-test +# Test environment handling +verify_env_url # Cleanup cleanup diff --git a/tasks/release.sh b/tasks/release.sh index 1520a5f0785..49a7302852f 100755 --- a/tasks/release.sh +++ b/tasks/release.sh @@ -39,6 +39,6 @@ if [ -n "$(git status --porcelain)" ]; then exit 1; fi -cd $root_path +cd "$root_path" # Go! ./node_modules/.bin/lerna publish --independent "$@" diff --git a/template/README.md b/template/README.md index 8f918c1979f..32efd00ff82 100644 --- a/template/README.md +++ b/template/README.md @@ -1,4 +1,4 @@ -This page has moved! +This page has moved!
Please update your link to point [here](https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md) instead. Sorry for the inconvenience!